From eabea6ea16c2db3af2152e4a6260c77210e629c8 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sun, 23 Feb 2014 17:14:50 -0800 Subject: [PATCH] RaycastMesh work der started I'm going to pick up... --- zone/CMakeLists.txt | 2 + zone/Map.cpp | 1025 ------------------------------------------ zone/RaycastMesh.cpp | 938 ++++++++++++++++++++++++++++++++++++++ zone/RaycastMesh.h | 60 +++ zone/map.h | 190 -------- 5 files changed, 1000 insertions(+), 1215 deletions(-) delete mode 100644 zone/Map.cpp create mode 100644 zone/RaycastMesh.cpp create mode 100644 zone/RaycastMesh.h delete mode 100644 zone/map.h diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 49410f0f2..68c4678f3 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -95,6 +95,7 @@ SET(zone_sources questmgr.cpp QuestParserCollection.cpp raids.cpp + RaycastMesh.cpp spawn2.cpp spawn2.h spawngroup.cpp @@ -183,6 +184,7 @@ SET(zone_headers QuestParserCollection.h raid.h raids.h + RaycastMesh.h skills.h spawn2.cpp spawn2.h diff --git a/zone/Map.cpp b/zone/Map.cpp deleted file mode 100644 index 95a6ba1a5..000000000 --- a/zone/Map.cpp +++ /dev/null @@ -1,1025 +0,0 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY except by those people which sell it, which - are required to give you total support for your newly bought product; - without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -#include "../common/debug.h" -#include "../common/MiscFunctions.h" -#include -#include -#include -#include - -#ifndef WIN32 -//comment this out if your worried about zone boot times and your not using valgrind -#define SLOW_AND_CRAPPY_MAKES_VALGRIND_HAPPY -#endif - -#include "map.h" -#include "zone.h" -#ifdef _WINDOWS -#define snprintf _snprintf -#endif - -extern Zone* zone; - -//Do we believe the normals from the map file? -//you want this enabled if it dosent break things. -//#define TRUST_MAPFILE_NORMALS - -//#define OPTIMIZE_QT_LOOKUPS -#define EPS 0.002f //acceptable error - -//#define DEBUG_SEEK 1 -//#define DEBUG_BEST_Z 1 - -/* - of note: - it is possible to get a null node in a valid region if it - does not have any triangles in it. - this will dictate bahaviour on getting a null node - TODO: listen to the above - */ - -//quick functions to clean up vertex code. -#define Vmin3(o, a, b, c) ((a.ob.o)? (a.o>c.o?a.o:c.o) : (b.o>c.o?b.o:c.o)) -#define ABS(x) ((x)<0?-(x):(x)) - -Map* Map::LoadMapfile(const char* in_zonename, const char *directory) { - FILE *fp; - char zBuf[64]; - char cWork[256]; - Map* ret = 0; - - //have to convert to lower because the short names im getting - //are not all lower anymore, copy since strlwr edits the str. - strn0cpy(zBuf, in_zonename, 64); - - if(directory == nullptr) - directory = MAP_DIR; - snprintf(cWork, 250, "%s/%s.map", directory, strlwr(zBuf)); - - if ((fp = fopen( cWork, "rb" ))) { - ret = new Map(); - if(ret != nullptr) { - ret->loadMap(fp); - printf("Map %s loaded.\n", cWork); - } else { - printf("Map %s loading failed.\n", cWork); - } - fclose(fp); - } - else { - printf("Map %s not found.\n", cWork); - } - return ret; -} - -Map::Map() { - _minz = FLT_MAX; - _minx = FLT_MAX; - _miny = FLT_MAX; - _maxz = FLT_MIN; - _maxx = FLT_MIN; - _maxy = FLT_MIN; - - m_Faces = 0; - m_Nodes = 0; - m_FaceLists = 0; - mFinalFaces = nullptr; - mNodes = nullptr; - mFaceLists = nullptr; -} - -bool Map::loadMap(FILE *fp) { -#ifndef INVERSEXY -#warning Map files do not work without inverted XY - return(false); -#endif - - mapHeader head; - if(fread(&head, sizeof(head), 1, fp) != 1) { - //map read error. - return(false); - } - if(head.version != MAP_VERSION) { - //invalid version... if there really are multiple versions, - //a conversion routine could be possible. - printf("Invalid map version 0x%lx\n", (unsigned long)head.version); - return(false); - } - - printf("Map header: %lu faces, %u nodes, %lu facelists\n", (unsigned long)head.face_count, (unsigned int)head.node_count, (unsigned long)head.facelist_count); - - m_Faces = head.face_count; - m_Nodes = head.node_count; - m_FaceLists = head.facelist_count; - - /* fread(&m_Vertex, 4, 1, fp); - fread(&m_Faces , 4, 1, fp);*/ -// mFinalVertex = new VERTEX[m_Vertex]; - mFinalFaces = new FACE [m_Faces]; - mNodes = new NODE[m_Nodes]; - mFaceLists = new uint32[m_FaceLists]; - -// fread(mFinalVertex, m_Vertex, sizeof(VERTEX), fp); - - //this was changed to this loop from the single read because valgrind was - //hanging on this read otherwise... I dont pretend to understand it. -#ifdef SLOW_AND_CRAPPY_MAKES_VALGRIND_HAPPY - uint32 r; - for(r = 0; r < m_Faces; r++) { - if(fread(mFinalFaces+r, sizeof(FACE), 1, fp) != 1) { - printf("Unable to read %lu faces from map file, got %lu.\n", (unsigned long)m_Faces, (unsigned long)r); - return(false); - } - } -#else - uint32 count; - if((count = static_cast(fread(mFinalFaces, sizeof(FACE), m_Faces , fp))) != m_Faces) { - printf("Unable to read %lu face bytes from map file, got %lu.\n", (unsigned long)m_Faces, (unsigned long)count); - return(false); - } -#endif - -#ifdef SLOW_AND_CRAPPY_MAKES_VALGRIND_HAPPY - for(r = 0; r < m_Nodes; r++) { - if(fread(mNodes+r, sizeof(NODE), 1, fp) != 1) { - printf("Unable to read %lu nodes from map file, got %lu.\n", (unsigned long)m_Nodes, (unsigned long)r); - return(false); - } - } -#else - if(fread(mNodes, sizeof(NODE), m_Nodes, fp) != m_Nodes) { - printf("Unable to read %lu nodes from map file.\n", (unsigned long)m_Nodes); - return(false); - } -#endif - -#ifdef SLOW_AND_CRAPPY_MAKES_VALGRIND_HAPPY - for(r = 0; r < m_FaceLists; r++) { - if(fread(mFaceLists+r, sizeof(uint32), 1, fp) != 1) { - printf("Unable to read %lu face lists from map file, got %lu.\n", (unsigned long)m_FaceLists, (unsigned long)r); - return(false); - } - } -#else - if(fread(mFaceLists, sizeof(uint32), m_FaceLists, fp) != m_FaceLists) { - printf("Unable to read %lu face lists from map file.\n", (unsigned long)m_FaceLists); - return(false); - } -#endif - - -/* mRoot = new NODE(); - RecLoadNode(mRoot, fp );*/ - - uint32 i; - float v; - for(i = 0; i < m_Faces; i++) { - v = Vmax3(x, mFinalFaces[i].a, mFinalFaces[i].b, mFinalFaces[i].c); - if(v > _maxx) - _maxx = v; - v = Vmin3(x, mFinalFaces[i].a, mFinalFaces[i].b, mFinalFaces[i].c); - if(v < _minx) - _minx = v; - v = Vmax3(y, mFinalFaces[i].a, mFinalFaces[i].b, mFinalFaces[i].c); - if(v > _maxy) - _maxy = v; - v = Vmin3(y, mFinalFaces[i].a, mFinalFaces[i].b, mFinalFaces[i].c); - if(v < _miny) - _miny = v; - v = Vmax3(z, mFinalFaces[i].a, mFinalFaces[i].b, mFinalFaces[i].c); - if(v > _maxz) - _maxz = v; - v = Vmin3(z, mFinalFaces[i].a, mFinalFaces[i].b, mFinalFaces[i].c); - if(v < _minz) - _minz = v; - } - printf("Loaded map: %lu vertices, %lu faces\n", (unsigned long)m_Faces*3, (unsigned long)m_Faces); - printf("Map BB: (%.2f -> %.2f, %.2f -> %.2f, %.2f -> %.2f)\n", _minx, _maxx, _miny, _maxy, _minz, _maxz); - return(true); -} - -Map::~Map() { -// safe_delete_array(mFinalVertex); - safe_delete_array(mFinalFaces); - safe_delete_array(mNodes); - safe_delete_array(mFaceLists); -// RecFreeNode( mRoot ); -} - - -NodeRef Map::SeekNode( NodeRef node_r, float x, float y ) const { - if(node_r == NODE_NONE || node_r >= m_Nodes) { - return(NODE_NONE); - } - PNODE _node = &mNodes[node_r]; -#ifdef DEBUG_SEEK -printf("Seeking node for %u:(%.2f, %.2f) with root 0x%x.\n", node_r, x, y, _node); - -printf(" Current Box: (%.2f -> %.2f, %.2f -> %.2f)\n", _node->minx, _node->maxx, _node->miny, _node->maxy); -#endif - if( x>= _node->minx && x<= _node->maxx && y>= _node->miny && y<= _node->maxy ) { - if( _node->flags & nodeFinal ) { -#ifdef DEBUG_SEEK -printf("Seeking node for %u:(%.2f, %.2f) with root 0x%x.\n", node_r, x, y, _node); -printf(" Final Node: (%.2f -> %.2f, %.2f -> %.2f)\n", _node->minx, _node->maxx, _node->miny, _node->maxy); -fflush(stdout); -printf(" Final node found with %d faces.\n", _node->faces.count); -/*printf(" Faces:\n"); -unsigned long *cfl = mFaceLists + _node->faces.offset; -unsigned long m; -for(m = 0; m < _node->faces.count; m++) { - FACE *c = &mFinalFaces[ *cfl ]; - printf(" %lu (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f)\n", - *cfl, c->a.x, c->a.y, c->a.z, - c->b.x, c->b.y, c->b.z, - c->c.x, c->c.y, c->c.z); - cfl++; -}*/ -#endif - return node_r; - } -#ifdef DEBUG_SEEK -printf(" Kids: %u, %u, %u, %u\n", _node->nodes[0], _node->nodes[1], _node->nodes[2], _node->nodes[3]); - -printf(" Contained In Box: (%.2f -> %.2f, %.2f -> %.2f)\n", _node->minx, _node->maxx, _node->miny, _node->maxy); - -/*printf(" Node found has children.\n"); -if(_node->node1 != nullptr) { - printf("\tNode: (%.2f -> %.2f, %.2f -> %.2f)\n", - _node->node1->minx, _node->node1->maxx, _node->node1->miny, _node->node1->maxy); -} -if(_node->node2 != nullptr) { - printf("\tNode: (%.2f -> %.2f, %.2f -> %.2f)\n", - _node->node2->minx, _node->node2->maxx, _node->node2->miny, _node->node2->maxy); -} -if(_node->node3 != nullptr) { - printf("\tNode: (%.2f -> %.2f, %.2f -> %.2f)\n", - _node->node3->minx, _node->node3->maxx, _node->node3->miny, _node->node3->maxy); -} -if(_node->node4 != nullptr) { - printf("\tNode: (%.2f -> %.2f, %.2f -> %.2f)\n", - _node->node4->minx, _node->node4->maxx, _node->node4->miny, _node->node4->maxy); -}*/ -#endif - //NOTE: could precalc these and store them in node headers - - NodeRef tmp = NODE_NONE; -#ifdef OPTIMIZE_QT_LOOKUPS - float midx = _node->minx + (_node->maxx - _node->minx) * 0.5; - float midy = _node->miny + (_node->maxy - _node->miny) * 0.5; - //follow ordering rules from map.h... - if(x < midx) { - if(y < midy) { //quad 3 - if(_node->nodes[2] != NODE_NONE && _node->nodes[2] != node_r) - tmp = SeekNode( _node->nodes[2], x, y ); - } else { //quad 2 - if(_node->nodes[2] != NODE_NONE && _node->nodes[1] != node_r) - tmp = SeekNode( _node->nodes[1], x, y ); - } - } else { - if(y < midy) { //quad 4 - if(_node->nodes[2] != NODE_NONE && _node->nodes[3] != node_r) - tmp = SeekNode( _node->nodes[3], x, y ); - } else { //quad 1 - if(_node->nodes[2] != NODE_NONE && _node->nodes[0] != node_r) - tmp = SeekNode( _node->nodes[0], x, y ); - } - } - if( tmp != NODE_NONE ) return tmp; -#else - if(_node->nodes[0] == node_r) return(NODE_NONE); //prevent infinite recursion - tmp = SeekNode( _node->nodes[0], x, y ); - if( tmp != NODE_NONE ) return tmp; - if(_node->nodes[1] == node_r) return(NODE_NONE); //prevent infinite recursion - tmp = SeekNode( _node->nodes[1], x, y ); - if( tmp != NODE_NONE ) return tmp; - if(_node->nodes[2] == node_r) return(NODE_NONE); //prevent infinite recursion - tmp = SeekNode( _node->nodes[2], x, y ); - if( tmp != NODE_NONE ) return tmp; - if(_node->nodes[3] == node_r) return(NODE_NONE); //prevent infinite recursion - tmp = SeekNode( _node->nodes[3], x, y ); - if( tmp != NODE_NONE ) return tmp; -#endif - - } -#ifdef DEBUG_SEEK -printf(" No node found.\n"); -#endif - return(NODE_NONE); -} - -// maybe precalc edges. -int* Map::SeekFace( NodeRef node_r, float x, float y ) { - if( node_r == NODE_NONE || node_r >= m_Nodes) { - return(nullptr); - } - const PNODE _node = &mNodes[node_r]; - if(!(_node->flags & nodeFinal)) { - return(nullptr); //not a final node... could find the proper node... - } - - -//printf("Seeking face for (%.2f, %.2f) with root 0x%x.\n", x, y, _node); - float dx,dy; - float nx,ny; - int *face = mCandFaces; - unsigned long i; - for( i=0;i<_node->faces.count;i++ ) { - const FACE &cf = mFinalFaces[ mFaceLists[_node->faces.offset + i] ]; - const VERTEX &v1 = cf.a; - const VERTEX &v2 = cf.b; - const VERTEX &v3 = cf.c; - - dx = v2.x - v1.x; dy = v2.y - v1.y; - nx = x - v1.x; ny = y - v1.y; - if( dx*ny - dy*nx >0.0f ) continue; - - dx = v3.x - v2.x; dy = v3.y - v2.y; - nx = x - v2.x; ny = y - v2.y; - if( dx*ny - dy*nx >0.0f ) continue; - - dx = v1.x - v3.x; dy = v1.y - v3.y; - nx = x - v3.x; ny = y - v3.y; - if( dx*ny - dy*nx >0.0f ) continue; - - *face++ = mFaceLists[_node->faces.offset + i]; - } - *face = -1; - return mCandFaces; -} - -// can be op? -float Map::GetFaceHeight( int _idx, float x, float y ) const { - const PFACE pface = &mFinalFaces[ _idx ]; - return ( pface->nd - x * pface->nx - y * pface->ny ) / pface->nz; -} - -//FatherNitwit's LOS code... -//Algorithm stolen from internet -//p1=start of segment -//p2=end of segment - -bool Map::LineIntersectsZone(VERTEX start, VERTEX end, float step_mag, VERTEX *result, FACE **on) const -{ - // Cast a ray from start to end, checking for collisions in each node between the two points. - // - float stepx, stepy, stepz, curx, cury, curz; - - curx = start.x; - cury = start.y; - curz = start.z; - - VERTEX cur = start; - - stepx = end.x - start.x; - stepy = end.y - start.y; - stepz = end.z - start.z; - - if((stepx == 0) && (stepy == 0) && (stepz == 0)) - return false; - - float factor = sqrt(stepx*stepx + stepy*stepy + stepz*stepz); - - stepx = (stepx/factor)*step_mag; - stepy = (stepy/factor)*step_mag; - stepz = (stepz/factor)*step_mag; - - NodeRef cnode, lnode, finalnode; - lnode = NODE_NONE; //last node visited - - cnode = SeekNode(GetRoot(), start.x, start.y); - finalnode = SeekNode(GetRoot(), end.x, end.y); - if(cnode == finalnode) - return LineIntersectsNode(cnode, start, end, result, on); - - do { - - stepx = (float)end.x - curx; - stepy = (float)end.y - cury; - stepz = (float)end.z - curz; - - factor = sqrt(stepx*stepx + stepy*stepy + stepz*stepz); - - stepx = (stepx/factor)*step_mag; - stepy = (stepy/factor)*step_mag; - stepz = (stepz/factor)*step_mag; - - cnode = SeekNode(GetRoot(), curx, cury); - if(cnode != lnode) - { - lnode = cnode; - - if(cnode == NODE_NONE) - return false; - - if(LineIntersectsNode(cnode, start, end, result, on)) - return(true); - - if(cnode == finalnode) - return false; - } - curx += stepx; - cury += stepy; - curz += stepz; - - cur.x = curx; - cur.y = cury; - cur.z = curz; - - if(ABS(curx - end.x) < step_mag) cur.x = end.x; - if(ABS(cury - end.y) < step_mag) cur.y = end.y; - if(ABS(curz - end.z) < step_mag) cur.z = end.z; - - } while(cur.x != end.x || cur.y != end.y || cur.z != end.z); - - return false; -} - -bool Map::LocWithinNode( NodeRef node_r, float x, float y ) const { - if( node_r == NODE_NONE || node_r >= m_Nodes) { - return(false); - } - const PNODE _node = &mNodes[node_r]; - //this function exists so nobody outside of MAP needs to know - //how the NODE sturcture works - return( x>= _node->minx && x<= _node->maxx && y>= _node->miny && y<= _node->maxy ); -} - -bool Map::LineIntersectsNode( NodeRef node_r, VERTEX p1, VERTEX p2, VERTEX *result, FACE **on) const { - if( node_r == NODE_NONE || node_r >= m_Nodes) { - return(true); //can see through empty nodes, just allow LOS on error... - } - const PNODE _node = &mNodes[node_r]; - if(!(_node->flags & nodeFinal)) { - return(true); //not a final node... not sure best action - } - - unsigned long i; - - PFACE cur; - const uint32 *cfl = mFaceLists + _node->faces.offset; - - for(i = 0; i < _node->faces.count; i++) { - if(*cfl > m_Faces) - continue; //watch for invalid lists, they seem to happen - cur = &mFinalFaces[ *cfl ]; - if(LineIntersectsFace(cur,p1, p2, result)) { - if(on != nullptr) - *on = cur; - return(true); - } - cfl++; - } - -//printf("Checked %ld faces and found none in the way.\n", i); - - return(false); -} - - -float Map::FindBestZ( NodeRef node_r, VERTEX p1, VERTEX *result, FACE **on) const { - - p1.z += RuleI(Map, FindBestZHeightAdjust); - - if(RuleB(Map, UseClosestZ)) - return FindClosestZ(p1); - - if(node_r == GetRoot()) { - node_r = SeekNode(node_r, p1.x, p1.y); - } - if( node_r == NODE_NONE || node_r >= m_Nodes) { - return(BEST_Z_INVALID); - } - const PNODE _node = &mNodes[node_r]; - if(!(_node->flags & nodeFinal)) { - return(BEST_Z_INVALID); //not a final node... could find the proper node... - } - - VERTEX tmp_result; //dummy placeholder if they do not ask for a result. - if(result == nullptr) - result = &tmp_result; - - VERTEX p2(p1); - p2.z = BEST_Z_INVALID; - - float best_z = BEST_Z_INVALID; - int zAttempt; - - unsigned long i; - - PFACE cur; - - // If we don't find a bestZ on the first attempt, we try again from a position CurrentZ + 10 higher - // This is in case the pathing between waypoints temporarily sends the NPC below ground level. - // - for(zAttempt=1; zAttempt<=2; zAttempt++) { - - const uint32 *cfl = mFaceLists + _node->faces.offset; - -#ifdef DEBUG_BEST_Z -printf("Start finding best Z...\n"); -#endif - for(i = 0; i < _node->faces.count; i++) { - if(*cfl > m_Faces) - continue; //watch for invalid lists, they seem to happen, e.g. in eastwastes.map - - cur = &mFinalFaces[ *cfl ]; -//printf("Intersecting with face %lu\n", *cfl); - if(LineIntersectsFace(cur, p1, p2, result)) { -#ifdef DEBUG_BEST_Z - printf(" %lu (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f)\n", - *cfl, cur->a.x, cur->a.y, cur->a.z, - cur->b.x, cur->b.y, cur->b.z, - cur->c.x, cur->c.y, cur->c.z); - printf("Found a z: %.2f\n", result->z); -#endif - if (result->z > best_z) { - if(on != nullptr) - *on = cur; - best_z = result->z; - } - } - cfl++; - } - - if(best_z != BEST_Z_INVALID) return best_z; - - p1.z = p1.z + 10 ; // If we can't find a best Z, the NPC is probably just under the world. Try again from 10 units higher up. - } - -#ifdef DEBUG_BEST_Z -fflush(stdout); -printf("Best Z found: %.2f\n", best_z); -#endif - return best_z; -} - - -bool Map::LineIntersectsFace( PFACE cface, VERTEX p1, VERTEX p2, VERTEX *result) const { - if( cface == nullptr ) { - return(false); //cant intersect a face we dont have... i guess - } - - const VERTEX &pa = cface->a; - const VERTEX &pb = cface->b; - const VERTEX &pc = cface->c; - - //quick bounding box checks - float tbb; - - tbb = Vmin3(x, pa, pb, pc); - if(p1.x < tbb && p2.x < tbb) - return(false); - tbb = Vmin3(y, pa, pb, pc); - if(p1.y < tbb && p2.y < tbb) - return(false); - tbb = Vmin3(z, pa, pb, pc); - if(p1.z < tbb && p2.z < tbb) - return(false); - - tbb = Vmax3(x, pa, pb, pc); - if(p1.x > tbb && p2.x > tbb) - return(false); - tbb = Vmax3(y, pa, pb, pc); - if(p1.y > tbb && p2.y > tbb) - return(false); - tbb = Vmax3(z, pa, pb, pc); - if(p1.z > tbb && p2.z > tbb) - return(false); - - //begin attempt 2 -//#define RTOD 57.2957795 //radians to degrees constant. - - float d; - float denom,mu; - VERTEX n, intersect; - -// FACE *thisface = &mFinalFaces[ _node->pfaces[ i ] ]; - - VERTEX *p = &intersect; - if(result != nullptr) - p = result; - - // Calculate the parameters for the plane - //recalculate from points -#ifndef TRUST_MAPFILE_NORMALS - n.x = (pb.y - pa.y)*(pc.z - pa.z) - (pb.z - pa.z)*(pc.y - pa.y); - n.y = (pb.z - pa.z)*(pc.x - pa.x) - (pb.x - pa.x)*(pc.z - pa.z); - n.z = (pb.x - pa.x)*(pc.y - pa.y) - (pb.y - pa.y)*(pc.x - pa.x); - Normalize(&n); - d = - n.x * pa.x - n.y * pa.y - n.z * pa.z; -#else - //use precaled data from .map file - n.x = cface->nx; - n.y = cface->ny; - n.z = cface->nz; - d = cface->nd; -#endif - - //try inverting the normals... - n.x = -n.x; - n.y = -n.y; - n.z = -n.z; - d = - n.x * pa.x - n.y * pa.y - n.z * pa.z; //recalc - - - // Calculate the position on the line that intersects the plane - denom = n.x * (p2.x - p1.x) + n.y * (p2.y - p1.y) + n.z * (p2.z - p1.z); - if (ABS(denom) < EPS) // Line and plane don't intersect - return(false); - mu = - (d + n.x * p1.x + n.y * p1.y + n.z * p1.z) / denom; - if (mu < 0 || mu > 1) // Intersection not along line segment - return(false); - p->x = p1.x + mu * (p2.x - p1.x); - p->y = p1.y + mu * (p2.y - p1.y); - p->z = p1.z + mu * (p2.z - p1.z); - - -/* //old method, slow as hell due to acos(), but it works well - - float a1,a2,a3; - float total; - VERTEX pa1,pa2,pa3; - - pa1.x = pa.x - p->x; - pa1.y = pa.y - p->y; - pa1.z = pa.z - p->z; - Normalize(&pa1); - pa2.x = pb.x - p->x; - pa2.y = pb.y - p->y; - pa2.z = pb.z - p->z; - Normalize(&pa2); - pa3.x = pc.x - p->x; - pa3.y = pc.y - p->y; - pa3.z = pc.z - p->z; - Normalize(&pa3); - a1 = pa1.x*pa2.x + pa1.y*pa2.y + pa1.z*pa2.z; - a2 = pa2.x*pa3.x + pa2.y*pa3.y + pa2.z*pa3.z; - a3 = pa3.x*pa1.x + pa3.y*pa1.y + pa3.z*pa1.z; - -//holy hell these 3 acos are slow, we need to rewrite this... -// total = (acos(a1) + acos(a2) + acos(a3)); -// if (ABS(total - 2*M_PI) > EPS) - total = (acos(a1) + acos(a2) + acos(a3)) * 57.2957795; - if (ABS(total - 360) > EPS) - return(false); - - return(true); -*/ - -/* - //yet another failed method, project triangle and point into - //2 space based on largest component of the normal - //and check the triangle there. - float tx, ty, tz; - if(n.x < 0) - tx = -n.x; - else - tx = n.x; - if(n.y < 0) - ty = -n.y; - else - ty = n.y; - if(n.z < 0) - tz = -n.z; - else - tz = n.z; - - VERTEX pa2, pb2, pc2; - if(tx < ty) { - //keep x - if(tz < ty) { - //keep z, drop y - pa2.x = pa.x; pa2.y = pa.z; - pb2.x = pb.x; pb2.y = pb.z; - pc2.x = pc.x; pc2.y = pc.z; - } else { - //keep y, drop z... - pa2.x = pa.x; pa2.y = pa.y; - pb2.x = pb.x; pb2.y = pb.y; - pc2.x = pc.x; pc2.y = pc.y; - } - } else { - //keep y - if(tz < tx) { - //keep z, drop x - pa2.x = pa.x; pa2.y = pa.z; - pb2.x = pb.x; pb2.y = pb.z; - pc2.x = pc.x; pc2.y = pc.z; - } else { - //keep y, drop z... - pa2.x = pa.x; pa2.y = pa.y; - pb2.x = pb.x; pb2.y = pb.y; - pc2.x = pc.x; pc2.y = pc.y; - } - } - - // Determine whether or not the intersection point is bounded by pa,pb,pc -#define Sign(p1, p2, p3) \ - ((p1->x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1->y - p3.y)) - bool b1, b2, b3; - - b1 = Sign(p, pa2, pb2) < 0.0f; - b2 = Sign(p, pb2, pc2) < 0.0f; - b3 = Sign(p, pc2, pa2) < 0.0f; - - return ((b1 == b2) && (b2 == b3)); -*/ - -/* //not working well, seems to block LOS a lot - - //a new check based on barycentric coordinates, stolen from - //http://www.flipcode.com/cgi-bin/fcmsg.cgi?thread_show=7766 - float xcp, ycp, xab, yab, xac, yac; - float divb; - VERTEX barycoords; - - xcp = p->x - pc.x; - ycp = p->z - pc.z; - if( xcp == 0.f && ycp == 0.f ) - return(true); - - xab = pb.x - pa.x; - yab = pb.z - pa.z; - divb = xab * ycp - yab * xcp; - if( divb == 0.f ) - return(false); - - xac = pc.x - pa.x; - yac = pc.z - pa.z; - - barycoords.y = ( -yac * xcp + xac * ycp ) / divb; - if( barycoords.y < -EPS || barycoords.y > (1+EPS) ) - return(false); // small error tolerance - if( barycoords.y < 0.f ) - barycoords.y = 0.f; - if( barycoords.y > 1.f ) - barycoords.y = 1.f; - -// barycoords.x = 1.f - barycoords.y; - - if( xcp != 0.f ) { - float div = -xac + barycoords.y * xab; - if( div == 0.f ) - return(false); // flat triangle - barycoords.z = 1.f - xcp / div ; - } else { - float div = -yac + barycoords.y * yab; - if( div == 0.f ) - return(false); // flat triangle - barycoords.z = 1.f - ycp / div ; - } - - if( barycoords.z < -EPS || barycoords.z > (1+EPS) ) - return(false); -// if( barycoords.z < 0.f ) -// barycoords.z = 0.f; -// if( barycoords.z > 1.f ) -// barycoords.z = 1.f; - -// barycoords.x *= 1.f - barycoords.z; -// barycoords.y *= 1.f - barycoords.z; - - return(true); -*/ - -/* - Yet another method adapted from this code: - Vec3 pa1 = pa - p; - Vec3 pa2 = pb - p; - float d = pa1.cross(pa2).dot(n); - if (d < 0) return false; - Vec3 pa3 = pb - p; - d = pa2.cross(pa3).dot(n); - if (d < 0) return false; - d = pa3.cross(pa1).dot(n); - if (d < 0) return false; - return true; -*/ - - //in practice, this seems to actually take longer - //than the arc cosine method above... - n.x = -n.x; - n.y = -n.y; - n.z = -n.z; - VERTEX pa1,pa2,pa3, tmp; - float t; - - //pa1 = pa - p - pa1.x = pa.x - p->x; - pa1.y = pa.y - p->y; - pa1.z = pa.z - p->z; - - //pa2 = pb - p - pa2.x = pb.x - p->x; - pa2.y = pb.y - p->y; - pa2.z = pb.z - p->z; - - //tmp = pa1 cross pa2 - tmp.x = pa1.y * pa2.z - pa1.z * pa2.y; - tmp.y = pa1.z * pa2.x - pa1.x * pa2.z; - tmp.z = pa1.x * pa2.y - pa1.y * pa2.x; - - //t = tmp dot n - t = tmp.x * n.x + tmp.y * n.y + tmp.z * n.z; - if(t < 0) - return(false); -//printf("t = %f\n", t); - - //pa3 = pb - p - pa3.x = pc.x - p->x; - pa3.y = pc.y - p->y; - pa3.z = pc.z - p->z; - - //tmp = pa2 cross pa3 - tmp.x = pa2.y * pa3.z - pa2.z * pa3.y; - tmp.y = pa2.z * pa3.x - pa2.x * pa3.z; - tmp.z = pa2.x * pa3.y - pa2.y * pa3.x; - - //t = tmp dot n - t = tmp.x * n.x + tmp.y * n.y + tmp.z * n.z; - if(t < 0) - return(false); -//printf("t = %f\n", t); - - //tmp = pa3 cross pa1 - tmp.x = pa3.y * pa1.z - pa3.z * pa1.y; - tmp.y = pa3.z * pa1.x - pa3.x * pa1.z; - tmp.z = pa3.x * pa1.y - pa3.y * pa1.x; - - //t = tmp dot n - t = tmp.x * n.x + tmp.y * n.y + tmp.z * n.z; - if(t < 0) - return(false); -//printf("t = %f\n", t); - - return(true); -} - -void Map::Normalize(VERTEX *p) { - float len = sqrtf(p->x*p->x + p->y*p->y + p->z*p->z); - p->x /= len; - p->y /= len; - p->z /= len; -} - -bool Map::LineIntersectsZoneNoZLeaps(VERTEX start, VERTEX end, float step_mag, VERTEX *result, FACE **on) { - float z = -999999; - VERTEX step; - VERTEX cur; - cur.x = start.x; - cur.y = start.y; - cur.z = start.z; - - step.x = end.x - start.x; - step.y = end.y - start.y; - step.z = end.z - start.z; - float factor = step_mag / sqrt(step.x*step.x + step.y*step.y + step.z*step.z); - - step.x *= factor; - step.y *= factor; - step.z *= factor; - - int steps = 0; - - if (step.x > 0 && step.x < 0.001f) - step.x = 0.001f; - if (step.y > 0 && step.y < 0.001f) - step.y = 0.001f; - if (step.z > 0 && step.z < 0.001f) - step.z = 0.001f; - if (step.x < 0 && step.x > -0.001f) - step.x = -0.001f; - if (step.y < 0 && step.y > -0.001f) - step.y = -0.001f; - if (step.z < 0 && step.z > -0.001f) - step.z = -0.001f; - - NodeRef cnode, lnode; - lnode = 0; - //while we are not past end - //always do this once, even if start == end. - while(cur.x != end.x || cur.y != end.y || cur.z != end.z) - { - steps++; - cnode = SeekNode(GetRoot(), cur.x, cur.y); - if (cnode == NODE_NONE) - { - return(true); - } - VERTEX me; - me.x = cur.x; - me.y = cur.y; - me.z = cur.z; - VERTEX hit; - FACE *onhit; - float best_z = zone->zonemap->FindBestZ(cnode, me, &hit, &onhit); - float diff = ABS(best_z-z); -// diff *= sign(diff); - if (z == -999999 || best_z == -999999 || diff < 12.0) - z = best_z; - else - return(true); - //look at current location - if(cnode != NODE_NONE && cnode != lnode) { - if(LineIntersectsNode(cnode, start, end, result, on)) - { - return(true); - } - lnode = cnode; - } - - //move 1 step - if (cur.x != end.x) - cur.x += step.x; - if (cur.y != end.y) - cur.y += step.y; - if (cur.z != end.z) - cur.z += step.z; - - //watch for end conditions - if ( (cur.x > end.x && end.x >= start.x) || (cur.x < end.x && end.x <= start.x) || (step.x == 0) ) { - cur.x = end.x; - } - if ( (cur.y > end.y && end.y >= start.y) || (cur.y < end.y && end.y <= start.y) || (step.y == 0) ) { - cur.y = end.y; - } - if ( (cur.z > end.z && end.z >= start.z) || (cur.z < end.z && end.z < start.z) || (step.z == 0) ) { - cur.z = end.z; - } - } - - //walked entire line and didnt run into anything... - return(false); -} - -float Map::FindClosestZ(VERTEX p ) const -{ - // Unlike FindBestZ, this method finds the closest Z value above or below the specified point. - // - std::list ZSet; - - NodeRef NodeR = SeekNode(MAP_ROOT_NODE, p.x, p.y); - - if( NodeR == NODE_NONE || NodeR >= m_Nodes) - return 0; - - PNODE CurrentNode = &mNodes[NodeR]; - - if(!(CurrentNode->flags & nodeFinal)) - return 0; - - VERTEX p1(p), p2(p), Result; - - p1.z = 999999; - - p2.z = BEST_Z_INVALID; - - const uint32 *CurrentFaceList = mFaceLists + CurrentNode->faces.offset; - - for(unsigned long i = 0; i < CurrentNode->faces.count; ++i) - { - if(*CurrentFaceList > m_Faces) - continue; - - PFACE CurrentFace = &mFinalFaces[ *CurrentFaceList ]; - - if(CurrentFace->nz > 0 && LineIntersectsFace(CurrentFace, p1, p2, &Result)) - ZSet.push_back(Result.z); - - CurrentFaceList++; - - } - if(ZSet.size() == 0) - return 0; - - if(ZSet.size() == 1) - return ZSet.front(); - - float ClosestZ = -999999; - - for(std::list::iterator Iterator = ZSet.begin(); Iterator != ZSet.end(); ++Iterator) - { - if(ABS(p.z - (*Iterator)) < ABS(p.z - ClosestZ)) - ClosestZ = (*Iterator); - } - - return ClosestZ; -} - diff --git a/zone/RaycastMesh.cpp b/zone/RaycastMesh.cpp new file mode 100644 index 000000000..b6635c7e6 --- /dev/null +++ b/zone/RaycastMesh.cpp @@ -0,0 +1,938 @@ +#include "RaycastMesh.h" +#include +#include +#include + +// This code snippet allows you to create an axis aligned bounding volume tree for a triangle mesh so that you can do +// high-speed raycasting. +// +// There are much better implementations of this available on the internet. In particular I recommend that you use +// OPCODE written by Pierre Terdiman. +// @see: http://www.codercorner.com/Opcode.htm +// +// OPCODE does a whole lot more than just raycasting, and is a rather significant amount of source code. +// +// I am providing this code snippet for the use case where you *only* want to do quick and dirty optimized raycasting. +// I have not done performance testing between this version and OPCODE; so I don't know how much slower it is. However, +// anytime you switch to using a spatial data structure for raycasting, you increase your performance by orders and orders +// of magnitude; so this implementation should work fine for simple tools and utilities. +// +// It also serves as a nice sample for people who are trying to learn the algorithm of how to implement AABB trees. +// AABB = Axis Aligned Bounding Volume trees. +// +// http://www.cgal.org/Manual/3.5/doc_html/cgal_manual/AABB_tree/Chapter_main.html +// +// +// This code snippet was written by John W. Ratcliff on August 18, 2011 and released under the MIT. license. +// +// mailto:jratcliffscarab@gmail.com +// +// The official source can be found at: http://code.google.com/p/raycastmesh/ +// +// + +#pragma warning(disable:4100) + +namespace RAYCAST_MESH +{ + +typedef std::vector< RmUint32 > TriVector; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** +* A method to compute a ray-AABB intersection. +* Original code by Andrew Woo, from "Graphics Gems", Academic Press, 1990 +* Optimized code by Pierre Terdiman, 2000 (~20-30% faster on my Celeron 500) +* Epsilon value added by Klaus Hartmann. (discarding it saves a few cycles only) +* +* Hence this version is faster as well as more robust than the original one. +* +* Should work provided: +* 1) the integer representation of 0.0f is 0x00000000 +* 2) the sign bit of the RmReal is the most significant one +* +* Report bugs: p.terdiman@codercorner.com +* +* \param aabb [in] the axis-aligned bounding box +* \param origin [in] ray origin +* \param dir [in] ray direction +* \param coord [out] impact coordinates +* \return true if ray intersects AABB +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#define RAYAABB_EPSILON 0.00001f +//! Integer representation of a RmRealing-point value. +#define IR(x) ((RmUint32&)x) + +bool intersectRayAABB(const RmReal MinB[3],const RmReal MaxB[3],const RmReal origin[3],const RmReal dir[3],RmReal coord[3]) +{ + bool Inside = true; + RmReal MaxT[3]; + MaxT[0]=MaxT[1]=MaxT[2]=-1.0f; + + // Find candidate planes. + for(RmUint32 i=0;i<3;i++) + { + if(origin[i] < MinB[i]) + { + coord[i] = MinB[i]; + Inside = false; + + // Calculate T distances to candidate planes + if(IR(dir[i])) MaxT[i] = (MinB[i] - origin[i]) / dir[i]; + } + else if(origin[i] > MaxB[i]) + { + coord[i] = MaxB[i]; + Inside = false; + + // Calculate T distances to candidate planes + if(IR(dir[i])) MaxT[i] = (MaxB[i] - origin[i]) / dir[i]; + } + } + + // Ray origin inside bounding box + if(Inside) + { + coord[0] = origin[0]; + coord[1] = origin[1]; + coord[2] = origin[2]; + return true; + } + + // Get largest of the maxT's for final choice of intersection + RmUint32 WhichPlane = 0; + if(MaxT[1] > MaxT[WhichPlane]) WhichPlane = 1; + if(MaxT[2] > MaxT[WhichPlane]) WhichPlane = 2; + + // Check final candidate actually inside box + if(IR(MaxT[WhichPlane])&0x80000000) return false; + + for(RmUint32 i=0;i<3;i++) + { + if(i!=WhichPlane) + { + coord[i] = origin[i] + MaxT[WhichPlane] * dir[i]; + #ifdef RAYAABB_EPSILON + if(coord[i] < MinB[i] - RAYAABB_EPSILON || coord[i] > MaxB[i] + RAYAABB_EPSILON) return false; + #else + if(coord[i] < MinB[i] || coord[i] > MaxB[i]) return false; + #endif + } + } + return true; // ray hits box +} + + + + +bool intersectLineSegmentAABB(const RmReal bmin[3],const RmReal bmax[3],const RmReal p1[3],const RmReal dir[3],RmReal &dist,RmReal intersect[3]) +{ + bool ret = false; + + if ( dist > RAYAABB_EPSILON ) + { + ret = intersectRayAABB(bmin,bmax,p1,dir,intersect); + if ( ret ) + { + RmReal dx = p1[0]-intersect[0]; + RmReal dy = p1[1]-intersect[1]; + RmReal dz = p1[2]-intersect[2]; + RmReal d = dx*dx+dy*dy+dz*dz; + if ( d < dist*dist ) + { + dist = sqrtf(d); + } + else + { + ret = false; + } + } + } + return ret; +} + +/* a = b - c */ +#define vector(a,b,c) \ + (a)[0] = (b)[0] - (c)[0]; \ + (a)[1] = (b)[1] - (c)[1]; \ + (a)[2] = (b)[2] - (c)[2]; + +#define innerProduct(v,q) \ + ((v)[0] * (q)[0] + \ + (v)[1] * (q)[1] + \ + (v)[2] * (q)[2]) + +#define crossProduct(a,b,c) \ + (a)[0] = (b)[1] * (c)[2] - (c)[1] * (b)[2]; \ + (a)[1] = (b)[2] * (c)[0] - (c)[2] * (b)[0]; \ + (a)[2] = (b)[0] * (c)[1] - (c)[0] * (b)[1]; + + +static inline bool rayIntersectsTriangle(const RmReal *p,const RmReal *d,const RmReal *v0,const RmReal *v1,const RmReal *v2,RmReal &t) +{ + RmReal e1[3],e2[3],h[3],s[3],q[3]; + RmReal a,f,u,v; + + vector(e1,v1,v0); + vector(e2,v2,v0); + crossProduct(h,d,e2); + a = innerProduct(e1,h); + + if (a > -0.00001 && a < 0.00001) + return(false); + + f = 1/a; + vector(s,p,v0); + u = f * (innerProduct(s,h)); + + if (u < 0.0 || u > 1.0) + return(false); + + crossProduct(q,s,e1); + v = f * innerProduct(d,q); + if (v < 0.0 || u + v > 1.0) + return(false); + // at this stage we can compute t to find out where + // the intersection point is on the line + t = f * innerProduct(e2,q); + if (t > 0) // ray intersection + return(true); + else // this means that there is a line intersection + // but not a ray intersection + return (false); +} + +static RmReal computePlane(const RmReal *A,const RmReal *B,const RmReal *C,RmReal *n) // returns D +{ + RmReal vx = (B[0] - C[0]); + RmReal vy = (B[1] - C[1]); + RmReal vz = (B[2] - C[2]); + + RmReal wx = (A[0] - B[0]); + RmReal wy = (A[1] - B[1]); + RmReal wz = (A[2] - B[2]); + + RmReal vw_x = vy * wz - vz * wy; + RmReal vw_y = vz * wx - vx * wz; + RmReal vw_z = vx * wy - vy * wx; + + RmReal mag = sqrt((vw_x * vw_x) + (vw_y * vw_y) + (vw_z * vw_z)); + + if ( mag < 0.000001f ) + { + mag = 0; + } + else + { + mag = 1.0f/mag; + } + + RmReal x = vw_x * mag; + RmReal y = vw_y * mag; + RmReal z = vw_z * mag; + + + RmReal D = 0.0f - ((x*A[0])+(y*A[1])+(z*A[2])); + + n[0] = x; + n[1] = y; + n[2] = z; + + return D; +} + + +#define TRI_EOF 0xFFFFFFFF + +enum AxisAABB +{ + AABB_XAXIS, + AABB_YAXIS, + AABB_ZAXIS +}; + +enum ClipCode +{ + CC_MINX = (1<<0), + CC_MAXX = (1<<1), + CC_MINY = (1<<2), + CC_MAXY = (1<<3), + CC_MINZ = (1<<4), + CC_MAXZ = (1<<5), +}; + + +class BoundsAABB +{ +public: + + + void setMin(const RmReal *v) + { + mMin[0] = v[0]; + mMin[1] = v[1]; + mMin[2] = v[2]; + } + + void setMax(const RmReal *v) + { + mMax[0] = v[0]; + mMax[1] = v[1]; + mMax[2] = v[2]; + } + + void setMin(RmReal x,RmReal y,RmReal z) + { + mMin[0] = x; + mMin[1] = y; + mMin[2] = z; + } + + void setMax(RmReal x,RmReal y,RmReal z) + { + mMax[0] = x; + mMax[1] = y; + mMax[2] = z; + } + + void include(const RmReal *v) + { + if ( v[0] < mMin[0] ) mMin[0] = v[0]; + if ( v[1] < mMin[1] ) mMin[1] = v[1]; + if ( v[2] < mMin[2] ) mMin[2] = v[2]; + + if ( v[0] > mMax[0] ) mMax[0] = v[0]; + if ( v[1] > mMax[1] ) mMax[1] = v[1]; + if ( v[2] > mMax[2] ) mMax[2] = v[2]; + } + + void getCenter(RmReal *center) const + { + center[0] = (mMin[0]+mMax[0])*0.5f; + center[1] = (mMin[1]+mMax[1])*0.5f; + center[2] = (mMin[2]+mMax[2])*0.5f; + } + + bool intersects(const BoundsAABB &b) const + { + if ((mMin[0] > b.mMax[0]) || (b.mMin[0] > mMax[0])) return false; + if ((mMin[1] > b.mMax[1]) || (b.mMin[1] > mMax[1])) return false; + if ((mMin[2] > b.mMax[2]) || (b.mMin[2] > mMax[2])) return false; + return true; + } + + bool containsTriangle(const RmReal *p1,const RmReal *p2,const RmReal *p3) const + { + BoundsAABB b; + b.setMin(p1); + b.setMax(p1); + b.include(p2); + b.include(p3); + return intersects(b); + } + + bool containsTriangleExact(const RmReal *p1,const RmReal *p2,const RmReal *p3,RmUint32 &orCode) const + { + bool ret = false; + + RmUint32 andCode; + orCode = getClipCode(p1,p2,p3,andCode); + if ( andCode == 0 ) + { + ret = true; + } + + return ret; + } + + inline RmUint32 getClipCode(const RmReal *p1,const RmReal *p2,const RmReal *p3,RmUint32 &andCode) const + { + andCode = 0xFFFFFFFF; + RmUint32 c1 = getClipCode(p1); + RmUint32 c2 = getClipCode(p2); + RmUint32 c3 = getClipCode(p3); + andCode&=c1; + andCode&=c2; + andCode&=c3; + return c1|c2|c3; + } + + inline RmUint32 getClipCode(const RmReal *p) const + { + RmUint32 ret = 0; + + if ( p[0] < mMin[0] ) + { + ret|=CC_MINX; + } + else if ( p[0] > mMax[0] ) + { + ret|=CC_MAXX; + } + + if ( p[1] < mMin[1] ) + { + ret|=CC_MINY; + } + else if ( p[1] > mMax[1] ) + { + ret|=CC_MAXY; + } + + if ( p[2] < mMin[2] ) + { + ret|=CC_MINZ; + } + else if ( p[2] > mMax[2] ) + { + ret|=CC_MAXZ; + } + + return ret; + } + + inline void clamp(const BoundsAABB &aabb) + { + if ( mMin[0] < aabb.mMin[0] ) mMin[0] = aabb.mMin[0]; + if ( mMin[1] < aabb.mMin[1] ) mMin[1] = aabb.mMin[1]; + if ( mMin[2] < aabb.mMin[2] ) mMin[2] = aabb.mMin[2]; + if ( mMax[0] > aabb.mMax[0] ) mMax[0] = aabb.mMax[0]; + if ( mMax[1] > aabb.mMax[1] ) mMax[1] = aabb.mMax[1]; + if ( mMax[2] > aabb.mMax[2] ) mMax[2] = aabb.mMax[2]; + } + + RmReal mMin[3]; + RmReal mMax[3]; +}; + + +class NodeAABB; + +class NodeInterface +{ +public: + virtual NodeAABB * getNode(void) = 0; + virtual void getFaceNormal(RmUint32 tri,RmReal *faceNormal) = 0; +}; + + + + + + class NodeAABB + { + public: + NodeAABB(void) + { + mLeft = NULL; + mRight = NULL; + mLeafTriangleIndex= TRI_EOF; + } + + NodeAABB(RmUint32 vcount,const RmReal *vertices,RmUint32 tcount,RmUint32 *indices, + RmUint32 maxDepth, // Maximum recursion depth for the triangle mesh. + RmUint32 minLeafSize, // minimum triangles to treat as a 'leaf' node. + RmReal minAxisSize, + NodeInterface *callback, + TriVector &leafTriangles) // once a particular axis is less than this size, stop sub-dividing. + + { + mLeft = NULL; + mRight = NULL; + mLeafTriangleIndex = TRI_EOF; + TriVector triangles; + triangles.reserve(tcount); + for (RmUint32 i=0; i dx ) + { + axis = AABB_YAXIS; + laxis = dy; + } + + if ( dz > dx && dz > dy ) + { + axis = AABB_ZAXIS; + laxis = dz; + } + + RmUint32 count = triangles.size(); + + // if the number of triangles is less than the minimum allowed for a leaf node or... + // we have reached the maximum recursion depth or.. + // the width of the longest axis is less than the minimum axis size then... + // we create the leaf node and copy the triangles into the leaf node triangle array. + if ( count < minLeafSize || depth >= maxDepth || laxis < minAxisSize ) + { + // Copy the triangle indices into the leaf triangles array + mLeafTriangleIndex = leafTriangles.size(); // assign the array start location for these leaf triangles. + leafTriangles.push_back(count); + for (TriVector::const_iterator i=triangles.begin(); i!=triangles.end(); ++i) + { + RmUint32 tri = *i; + leafTriangles.push_back(tri); + } + } + else + { + RmReal center[3]; + mBounds.getCenter(center); + BoundsAABB b1,b2; + splitRect(axis,mBounds,b1,b2,center); + + // Compute two bounding boxes based upon the split of the longest axis + + BoundsAABB leftBounds,rightBounds; + + TriVector leftTriangles; + TriVector rightTriangles; + + + // Create two arrays; one of all triangles which intersect the 'left' half of the bounding volume node + // and another array that includes all triangles which intersect the 'right' half of the bounding volume node. + for (TriVector::const_iterator i=triangles.begin(); i!=triangles.end(); ++i) + { + + RmUint32 tri = (*i); + + { + RmUint32 i1 = indices[tri*3+0]; + RmUint32 i2 = indices[tri*3+1]; + RmUint32 i3 = indices[tri*3+2]; + + const RmReal *p1 = &vertices[i1*3]; + const RmReal *p2 = &vertices[i2*3]; + const RmReal *p3 = &vertices[i3*3]; + + RmUint32 addCount = 0; + RmUint32 orCode=0xFFFFFFFF; + if ( b1.containsTriangleExact(p1,p2,p3,orCode)) + { + addCount++; + if ( leftTriangles.empty() ) + { + leftBounds.setMin(p1); + leftBounds.setMax(p1); + } + leftBounds.include(p1); + leftBounds.include(p2); + leftBounds.include(p3); + leftTriangles.push_back(tri); // Add this triangle to the 'left triangles' array and revise the left triangles bounding volume + } + // if the orCode is zero; meaning the triangle was fully self-contiained int he left bounding box; then we don't need to test against the right + if ( orCode && b2.containsTriangleExact(p1,p2,p3,orCode)) + { + addCount++; + if ( rightTriangles.empty() ) + { + rightBounds.setMin(p1); + rightBounds.setMax(p1); + } + rightBounds.include(p1); + rightBounds.include(p2); + rightBounds.include(p3); + rightTriangles.push_back(tri); // Add this triangle to the 'right triangles' array and revise the right triangles bounding volume. + } + assert( addCount ); + } + } + + if ( !leftTriangles.empty() ) // If there are triangles in the left half then... + { + leftBounds.clamp(b1); // we have to clamp the bounding volume so it stays inside the parent volume. + mLeft = callback->getNode(); // get a new AABB node + new ( mLeft ) NodeAABB(leftBounds); // initialize it to default constructor values. + // Then recursively split this node. + mLeft->split(leftTriangles,vcount,vertices,tcount,indices,depth+1,maxDepth,minLeafSize,minAxisSize,callback,leafTriangles); + } + + if ( !rightTriangles.empty() ) // If there are triangles in the right half then.. + { + rightBounds.clamp(b2); // clamps the bounding volume so it stays restricted to the size of the parent volume. + mRight = callback->getNode(); // allocate and default initialize a new node + new ( mRight ) NodeAABB(rightBounds); + // Recursively split this node. + mRight->split(rightTriangles,vcount,vertices,tcount,indices,depth+1,maxDepth,minLeafSize,minAxisSize,callback,leafTriangles); + } + + } + } + + void splitRect(AxisAABB axis,const BoundsAABB &source,BoundsAABB &b1,BoundsAABB &b2,const RmReal *midpoint) + { + switch ( axis ) + { + case AABB_XAXIS: + { + b1.setMin( source.mMin ); + b1.setMax( midpoint[0], source.mMax[1], source.mMax[2] ); + + b2.setMin( midpoint[0], source.mMin[1], source.mMin[2] ); + b2.setMax(source.mMax); + } + break; + case AABB_YAXIS: + { + b1.setMin(source.mMin); + b1.setMax(source.mMax[0], midpoint[1], source.mMax[2]); + + b2.setMin(source.mMin[0], midpoint[1], source.mMin[2]); + b2.setMax(source.mMax); + } + break; + case AABB_ZAXIS: + { + b1.setMin(source.mMin); + b1.setMax(source.mMax[0], source.mMax[1], midpoint[2]); + + b2.setMin(source.mMin[0], source.mMin[1], midpoint[2]); + b2.setMax(source.mMax); + } + break; + } + } + + + virtual void raycast(bool &hit, + const RmReal *from, + const RmReal *to, + const RmReal *dir, + RmReal *hitLocation, + RmReal *hitNormal, + RmReal *hitDistance, + const RmReal *vertices, + const RmUint32 *indices, + RmReal &nearestDistance, + NodeInterface *callback, + RmUint32 *raycastTriangles, + RmUint32 raycastFrame, + const TriVector &leafTriangles, + RmUint32 &nearestTriIndex) + { + RmReal sect[3]; + RmReal nd = nearestDistance; + if ( !intersectLineSegmentAABB(mBounds.mMin,mBounds.mMax,from,dir,nd,sect) ) + { + return; + } + if ( mLeafTriangleIndex != TRI_EOF ) + { + const RmUint32 *scan = &leafTriangles[mLeafTriangleIndex]; + RmUint32 count = *scan++; + for (RmUint32 i=0; igetFaceNormal(tri,hitNormal); + } + if ( hitDistance ) + { + *hitDistance = t; + } + nearestTriIndex = tri; + hit = true; + } + } + } + } + } + else + { + if ( mLeft ) + { + mLeft->raycast(hit,from,to,dir,hitLocation,hitNormal,hitDistance,vertices,indices,nearestDistance,callback,raycastTriangles,raycastFrame,leafTriangles,nearestTriIndex); + } + if ( mRight ) + { + mRight->raycast(hit,from,to,dir,hitLocation,hitNormal,hitDistance,vertices,indices,nearestDistance,callback,raycastTriangles,raycastFrame,leafTriangles,nearestTriIndex); + } + } + } + + NodeAABB *mLeft; // left node + NodeAABB *mRight; // right node + BoundsAABB mBounds; // bounding volume of node + RmUint32 mLeafTriangleIndex; // if it is a leaf node; then these are the triangle indices. + }; + +class MyRaycastMesh : public RaycastMesh, public NodeInterface +{ +public: + + MyRaycastMesh(RmUint32 vcount,const RmReal *vertices,RmUint32 tcount,const RmUint32 *indices,RmUint32 maxDepth,RmUint32 minLeafSize,RmReal minAxisSize) + { + mRaycastFrame = 0; + if ( maxDepth < 2 ) + { + maxDepth = 2; + } + if ( maxDepth > 15 ) + { + maxDepth = 15; + } + RmUint32 pow2Table[16] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 65536 }; + mMaxNodeCount = 0; + for (RmUint32 i=0; i<=maxDepth; i++) + { + mMaxNodeCount+=pow2Table[i]; + } + mNodes = new NodeAABB[mMaxNodeCount]; + mNodeCount = 0; + mVcount = vcount; + mVertices = (RmReal *)::malloc(sizeof(RmReal)*3*vcount); + memcpy(mVertices,vertices,sizeof(RmReal)*3*vcount); + mTcount = tcount; + mIndices = (RmUint32 *)::malloc(sizeof(RmUint32)*tcount*3); + memcpy(mIndices,indices,sizeof(RmUint32)*tcount*3); + mRaycastTriangles = (RmUint32 *)::malloc(tcount*sizeof(RmUint32)); + memset(mRaycastTriangles,0,tcount*sizeof(RmUint32)); + mRoot = getNode(); + mFaceNormals = NULL; + new ( mRoot ) NodeAABB(mVcount,mVertices,mTcount,mIndices,maxDepth,minLeafSize,minAxisSize,this,mLeafTriangles); + } + + ~MyRaycastMesh(void) + { + delete []mNodes; + ::free(mVertices); + ::free(mIndices); + ::free(mFaceNormals); + ::free(mRaycastTriangles); + } + + virtual bool raycast(const RmReal *from,const RmReal *to,RmReal *hitLocation,RmReal *hitNormal,RmReal *hitDistance) + { + bool ret = false; + + RmReal dir[3]; + dir[0] = to[0] - from[0]; + dir[1] = to[1] - from[1]; + dir[2] = to[2] - from[2]; + RmReal distance = sqrtf( dir[0]*dir[0] + dir[1]*dir[1]+dir[2]*dir[2] ); + if ( distance < 0.0000000001f ) return false; + RmReal recipDistance = 1.0f / distance; + dir[0]*=recipDistance; + dir[1]*=recipDistance; + dir[2]*=recipDistance; + mRaycastFrame++; + RmUint32 nearestTriIndex=TRI_EOF; + mRoot->raycast(ret,from,to,dir,hitLocation,hitNormal,hitDistance,mVertices,mIndices,distance,this,mRaycastTriangles,mRaycastFrame,mLeafTriangles,nearestTriIndex); + return ret; + } + + virtual void release(void) + { + delete this; + } + + virtual const RmReal * getBoundMin(void) const // return the minimum bounding box + { + return mRoot->mBounds.mMin; + } + virtual const RmReal * getBoundMax(void) const // return the maximum bounding box. + { + return mRoot->mBounds.mMax; + } + + virtual NodeAABB * getNode(void) + { + assert( mNodeCount < mMaxNodeCount ); + NodeAABB *ret = &mNodes[mNodeCount]; + mNodeCount++; + return ret; + } + + virtual void getFaceNormal(RmUint32 tri,RmReal *faceNormal) + { + if ( mFaceNormals == NULL ) + { + mFaceNormals = (RmReal *)::malloc(sizeof(RmReal)*3*mTcount); + for (RmUint32 i=0; i(m); +} + + diff --git a/zone/RaycastMesh.h b/zone/RaycastMesh.h new file mode 100644 index 000000000..eec2e0ba4 --- /dev/null +++ b/zone/RaycastMesh.h @@ -0,0 +1,60 @@ +#ifndef RAYCAST_MESH_H + +#define RAYCAST_MESH_H + +// This code snippet allows you to create an axis aligned bounding volume tree for a triangle mesh so that you can do +// high-speed raycasting. +// +// There are much better implementations of this available on the internet. In particular I recommend that you use +// OPCODE written by Pierre Terdiman. +// @see: http://www.codercorner.com/Opcode.htm +// +// OPCODE does a whole lot more than just raycasting, and is a rather significant amount of source code. +// +// I am providing this code snippet for the use case where you *only* want to do quick and dirty optimized raycasting. +// I have not done performance testing between this version and OPCODE; so I don't know how much slower it is. However, +// anytime you switch to using a spatial data structure for raycasting, you increase your performance by orders and orders +// of magnitude; so this implementation should work fine for simple tools and utilities. +// +// It also serves as a nice sample for people who are trying to learn the algorithm of how to implement AABB trees. +// AABB = Axis Aligned Bounding Volume trees. +// +// http://www.cgal.org/Manual/3.5/doc_html/cgal_manual/AABB_tree/Chapter_main.html +// +// +// This code snippet was written by John W. Ratcliff on August 18, 2011 and released under the MIT. license. +// +// mailto:jratcliffscarab@gmail.com +// +// The official source can be found at: http://code.google.com/p/raycastmesh/ +// +// + +typedef float RmReal; +typedef unsigned int RmUint32; + +class RaycastMesh +{ +public: + virtual bool raycast(const RmReal *from,const RmReal *to,RmReal *hitLocation,RmReal *hitNormal,RmReal *hitDistance) = 0; + virtual bool bruteForceRaycast(const RmReal *from,const RmReal *to,RmReal *hitLocation,RmReal *hitNormal,RmReal *hitDistance) = 0; + + virtual const RmReal * getBoundMin(void) const = 0; // return the minimum bounding box + virtual const RmReal * getBoundMax(void) const = 0; // return the maximum bounding box. + virtual void release(void) = 0; +protected: + virtual ~RaycastMesh(void) { }; +}; + + +RaycastMesh * createRaycastMesh(RmUint32 vcount, // The number of vertices in the source triangle mesh + const RmReal *vertices, // The array of vertex positions in the format x1,y1,z1..x2,y2,z2.. etc. + RmUint32 tcount, // The number of triangles in the source triangle mesh + const RmUint32 *indices, // The triangle indices in the format of i1,i2,i3 ... i4,i5,i6, ... + RmUint32 maxDepth=15, // Maximum recursion depth for the triangle mesh. + RmUint32 minLeafSize=4, // minimum triangles to treat as a 'leaf' node. + RmReal minAxisSize=0.01f // once a particular axis is less than this size, stop sub-dividing. + ); + + +#endif \ No newline at end of file diff --git a/zone/map.h b/zone/map.h deleted file mode 100644 index 9ab453ff6..000000000 --- a/zone/map.h +++ /dev/null @@ -1,190 +0,0 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY except by those people which sell it, which - are required to give you total support for your newly bought product; - without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -#ifndef MAP_H -#define MAP_H - -#include - -//this is the current version number to expect from the map header -#define MAP_VERSION 0x01000000 - -#define BEST_Z_INVALID -999999 - -#pragma pack(1) - -typedef struct _vertex{ -// unsigned long order; - float x; - float y; - float z; - - _vertex() - { - x = y = z = 0.0f; - }; - - _vertex(float ix, float iy, float iz) - { - x = ix; - y = iy; - z = iz; - } - bool operator==(const _vertex &v1) const - { - return((v1.x == x) && (v1.y == y) && (v1.z ==z)); - } - -}VERTEX, *PVERTEX; - -typedef struct _face{ -// unsigned long a, b, c; //vertexs - VERTEX a; - VERTEX b; - VERTEX c; - float nx, ny, nz, nd; -}FACE, *PFACE; - -typedef struct _mapHeader { - uint32 version; - uint32 face_count; - uint16 node_count; - uint32 facelist_count; -} mapHeader; - -/* - This is used for the recursive node structure - unsigned shorts are adequate because, worst case - even in a zone that is 6000x6000 with a small node - size of 30x30, there are only 40000 nodes. - - quadrent definitions: - quad 1 (nodes[0]): - x>=0, y>=0 - quad 2 (nodes[1]): - x<0, y>=0 - quad 3 (nodes[2]): - x<0, y<0 - quad 4 (nodes[3]): - x>=0, y<0 - - */ -enum { //node flags - nodeFinal = 0x01 - //7 more bits if theres something to use them for... -}; -typedef struct _nodeHeader { - //bounding box of this node - //there is no reason that these could not be unsigned - //shorts other than we have to compare them to floats - //all over the place, so they stay floats for now. - //changing it could save 8 bytes per node record (~320k for huge maps) - float minx; - float miny; - float maxx; - float maxy; - - uint8 flags; - union { - uint16 nodes[4]; //index 0 means nullptr, not root - struct { - uint32 count; - uint32 offset; - } faces; - }; -} nodeHeader, NODE, *PNODE; - -#pragma pack() - -//special value returned as 'not found' -#define NODE_NONE 65534 -#define MAP_ROOT_NODE 0 - -typedef uint16 NodeRef; - -/*typedef struct _node { - nodeHeader head; - unsigned int * pfaces; - char mask; - struct _node * node1, *node2, *node3, *node4; -}NODE, *PNODE;*/ - -class Map { -public: - static Map* LoadMapfile(const char* in_zonename, const char *directory = nullptr); - - Map(); - ~Map(); - - bool loadMap(FILE *fp); - - //the result is always final, except special NODE_NONE - NodeRef SeekNode( NodeRef _node, float x, float y ) const; - - //these are untested since rewrite: - int *SeekFace( NodeRef _node, float x, float y ); - float GetFaceHeight( int _idx, float x, float y ) const; - - bool LocWithinNode( NodeRef _node, float x, float y ) const; - - //nodes to these functions must be final - bool LineIntersectsNode( NodeRef _node, VERTEX start, VERTEX end, VERTEX *result, FACE **on = nullptr) const; - bool LineIntersectsFace( PFACE cface, VERTEX start, VERTEX end, VERTEX *result) const; - float FindBestZ( NodeRef _node, VERTEX start, VERTEX *result, FACE **on = nullptr) const; - bool LineIntersectsZone(VERTEX start, VERTEX end, float step, VERTEX *result, FACE **on = nullptr) const; - -// inline unsigned int GetVertexNumber( ) {return m_Vertex; } - inline uint32 GetFacesNumber( ) const { return m_Faces; } -// inline PVERTEX GetVertex( int _idx ) {return mFinalVertex + _idx; } - inline PFACE GetFace( int _idx) {return mFinalFaces + _idx; } - inline PFACE GetFaceFromlist( int _idx) {return &mFinalFaces[ mFaceLists[_idx] ]; } - inline NodeRef GetRoot( ) const { return MAP_ROOT_NODE; } - inline PNODE GetNode( NodeRef r ) { return( mNodes + r ); } - - inline float GetMinX() const { return(_minx); } - inline float GetMaxX() const { return(_maxx); } - inline float GetMinY() const { return(_miny); } - inline float GetMaxY() const { return(_maxy); } - inline float GetMinZ() const { return(_minz); } - inline float GetMaxZ() const { return(_maxz); } - bool LineIntersectsZoneNoZLeaps(VERTEX start, VERTEX end, float step_mag, VERTEX *result, FACE **on); - float FindClosestZ(VERTEX p ) const; - -private: -// unsigned long m_Vertex; - uint32 m_Faces; - uint32 m_Nodes; - uint32 m_FaceLists; -// PVERTEX mFinalVertex; - PFACE mFinalFaces; - PNODE mNodes; - uint32 *mFaceLists; - - - int mCandFaces[100]; - - float _minz, _maxz; - float _minx, _miny, _maxx, _maxy; - - static void Normalize(VERTEX *p); - -// void RecLoadNode( PNODE _node, FILE *l_f ); -// void RecFreeNode( PNODE _node ); -}; - -#endif -