Index: zone/mob.h =================================================================== --- zone/mob.h (revision 55) +++ zone/mob.h (revision 75) @@ -39,6 +39,10 @@ #define MOUNT_FAST_WALK 0.93333334 #define MOUNT_FAST_RUN 1.75 +#define HEAD_POSITION 0.9f //ratio of GetSize() where NPCs see from +#define SEE_POSITION 0.5f //ratio of GetSize() where NPCs try to see for LOS +#define CHECK_LOS_STEP 1.0f + //Father Nitwit: DoAnim constants from forums: /* 1 - Kick @@ -354,6 +349,7 @@ inline bool SeeInvisible() { return see_invis; } // Mongrel: Now using the flags inline bool SeeInvisibleUndead() { return see_invis_undead; } // Mongrel: Now using the flags bool CheckLos(Mob* other); + bool CheckLosFN(Mob* other); inline bool GetQglobal() {return qglobal;} // SCORPIOUS2K - return quest global flag bool IsInvisible(Mob* other = 0); Index: zone/mob.cpp =================================================================== --- zone/mob.cpp (revision 55) +++ zone/mob.cpp (revision 75) @@ -2948,7 +2948,10 @@ { return true; } - PNODE pnode = zone->map->SeekNode( zone->map->GetRoot(), tmp_x, tmp_y ); + +//I believe this is contributing to breaking mob spawns when a map is loaded +// NodeRef pnode = zone->map->SeekNode( zone->map->GetRoot(), tmp_x, tmp_y ); + NodeRef pnode = NULL; if (pnode != 0) { int *iface = zone->map->SeekFace( pnode, tmp_x, tmp_y ); @@ -2981,6 +2984,97 @@ } +//Father Nitwit's LOS code +bool Mob::CheckLosFN(Mob* other) { + if(other == NULL || zone->map == NULL) { + return(false); //not sure what the best return is on error + } + + VERTEX myloc; + VERTEX oloc; + +#define LOS_DEFAULT_HEIGHT 6.0f + + myloc.x = GetX(); + myloc.y = GetY(); + myloc.z = GetZ() + (GetSize()==0.0?LOS_DEFAULT_HEIGHT:GetSize())/2 * HEAD_POSITION; + + oloc.x = other->GetX(); + oloc.y = other->GetY(); + oloc.z = other->GetZ() + (other->GetSize()==0.0?LOS_DEFAULT_HEIGHT:other->GetSize())/2 * SEE_POSITION; + +#if EQDEBUG>=5 + FACE *onhit; + LogFile->write(EQEMuLog::Debug, "LOS from (%.2f, %.2f, %.2f) to (%.2f, %.2f, %.2f) sizes: (%.2f, %.2f)", myloc.x, myloc.y, myloc.z, oloc.x, oloc.y, oloc.z, GetSize(), other->GetSize()); +#endif + + NodeRef mynode; + NodeRef onode; + + VERTEX hit; + //see if anything in our node is in the way + mynode = zone->map->SeekNode( zone->map->GetRoot(), myloc.x, myloc.y); + if(mynode != NODE_NONE) { + if(zone->map->LineIntersectsNode(mynode, myloc, oloc, &hit, &onhit)) { +#if EQDEBUG>=5 + LogFile->write(EQEMuLog::Debug, "Check LOS for %s target %s, cannot see.", GetName(), other->GetName() ); + LogFile->write(EQEMuLog::Debug, "\tPoly: (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f)\n", + onhit->a.x, onhit->a.y, onhit->a.z, + onhit->b.x, onhit->b.y, onhit->b.z, + onhit->c.x, onhit->c.y, onhit->c.z); +#endif + return(false); + } + } +#if EQDEBUG>=5 + else { + LogFile->write(EQEMuLog::Debug, "WTF, I have no node, what am I standing on??? (%.2f, %.2f).", myloc.x, myloc.y); + } +#endif + + //see if they are in a different node. + //if so, see if anything in their node is blocking me. + if(! zone->map->LocWithinNode(mynode, oloc.x, oloc.y)) { + onode = zone->map->SeekNode( zone->map->GetRoot(), oloc.x, oloc.y); + if(onode != NODE_NONE && onode != mynode) { + if(zone->map->LineIntersectsNode(onode, myloc, oloc, &hit, &onhit)) { +#if EQDEBUG>=5 + LogFile->write(EQEMuLog::Debug, "Check LOS for %s target %s, cannot see (2).", GetName(), other->GetName()); + LogFile->write(EQEMuLog::Debug, "\tPoly: (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f)\n", + onhit->a.x, onhit->a.y, onhit->a.z, + onhit->b.x, onhit->b.y, onhit->b.z, + onhit->c.x, onhit->c.y, onhit->c.z); +#endif + return(false); + } + } +#if EQDEBUG>=5 + else if(onode == NULL) { + LogFile->write(EQEMuLog::Debug, "WTF, They have no node, what are they standing on??? (%.2f, %.2f).", myloc.x, myloc.y); + } +#endif + } + + /* + if(zone->map->LineIntersectsZone(myloc, oloc, CHECK_LOS_STEP, &onhit)) { +#if EQDEBUG>=5 + LogFile->write(EQEMuLog::Debug, "Check LOS for %s target %s, cannot see.", GetName(), other->GetName() ); + LogFile->write(EQEMuLog::Debug, "\tPoly: (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f)\n", + onhit->a.x, onhit->a.y, onhit->a.z, + onhit->b.x, onhit->b.y, onhit->b.z, + onhit->c.x, onhit->c.y, onhit->c.z); +#endif + return(false); + }*/ + +#if EQDEBUG>=5 + LogFile->write(EQEMuLog::Debug, "Check LOS for %s target %s, CAN SEE.", GetName(), other->GetName()); +#endif + + return(true); +} + + sint32 Mob::GetEquipment(int8 material_slot) { if(material_slot > 8) Index: zone/command.cpp =================================================================== --- zone/command.cpp (revision 55) +++ zone/command.cpp (revision 75) @@ -5335,7 +5344,8 @@ { if(c->GetTarget()) { - if(c->CheckLos(c->GetTarget())) +// if(c->CheckLos(c->GetTarget())) + if(c->CheckLosFN(c->GetTarget())) c->Message(0, "You have LOS to %s", c->GetTarget()->GetName()); else c->Message(0, "You do not have LOS to %s", c->GetTarget()->GetName()); Index: zone/MobAI.cpp =================================================================== --- zone/MobAI.cpp (revision 55) +++ zone/MobAI.cpp (revision 75) @@ -318,10 +321,15 @@ // Had an if statement to check if it wasn't a GM but theres no reason, we check that above // Also had an interactive npc check but I believe these are no longer used, if required can be put above // Assist friend + + //FatherNiwtit: make sure we can see them. last since it is very expensive + if(sender->CheckLosFN(mobTarget)) { + #if EQDEBUG>=5 - LogFile->write(EQEMuLog::Debug, "Check aggro for %s assisting %s, target %s.", sender->GetName(), mob->GetName(), mobTarget->GetName()); + LogFile->write(EQEMuLog::Debug, "Check aggro for %s assisting %s, target %s.", sender->GetName(), mob->GetName(), mobTarget->GetName()); #endif - return mobTarget; + return mobTarget; + } } // Make sure they're still in the zone // Are they in range? @@ -352,11 +360,15 @@ ) ) { + //FatherNiwtit: make sure we can see them. last since it is very expensive + if(sender->CheckLosFN(mob)) { + // Aggro #if EQDEBUG>=5 - LogFile->write(EQEMuLog::Debug, "Check aggro for %s target %s.", sender->GetName(), mob->GetName()); + LogFile->write(EQEMuLog::Debug, "Check aggro for %s target %s.", sender->GetName(), mob->GetName()); #endif - return mob; + return mob; + } } #if EQDEBUG >= 6 cout<<"In zone:"<InZone()<write(EQEMuLog::Debug, "AIYellForHelp(\"%s\",\"%s\") %s attacking %s Dist %f Z %f", sender->GetName(), attacker->GetName(), mob->GetName(), attacker->GetName(), mobDistance, fabs(sender->GetZ()+mob->GetZ())); #endif - mob->AddToHateList(attacker, 1, 0, false); + //Father Nitwit: make sure we can see them. + if(mob->CheckLosFN(attacker)) { + mob->AddToHateList(attacker, 1, 0, false); + } } } iterator.Advance(); @@ -1702,9 +1742,11 @@ x_pos = new_x; y_pos = new_y; - PNODE pnode = zone->map->SeekNode( zone->map->GetRoot(), y_pos, x_pos ); +// PNODE pnode = zone->map->SeekNode( zone->map->GetRoot(), y_pos, x_pos ); +//I believe this is contributing to breaking mob spawns when a map is loaded + NodeRef pnode = NODE_NONE; // Quagmire - Not sure if this is the right thing to do, but it stops the crashing - if (pnode == 0) return; + if (pnode == NODE_NONE) return; int *iface = zone->map->SeekFace( pnode, y_pos, x_pos ); float tmp_z = 0; Index: zone/Map.cpp =================================================================== --- zone/Map.cpp (revision 55) +++ zone/Map.cpp (revision 75) @@ -16,10 +16,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "../common/debug.h" +#include #include #include -#include -using namespace std; #include "../common/files.h" #include "map.h" @@ -27,6 +27,29 @@ #define snprintf _snprintf #endif +//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 DEBUG_SEEK 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 + */ + +#include "gpoint.h" + +//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)) + + Map* Map::LoadMapfile(const char* in_zonename) { FILE *fp; char cWork[256]; @@ -35,69 +58,246 @@ snprintf(cWork, 250, MAP_DIR "/%s.map", in_zonename); if ((fp = fopen( cWork, "rb" ))) { - ret = new Map(fp); + ret = new Map(); + if(ret != NULL) { + ret->loadMap(fp); + printf("Map %s loaded.\n", cWork); + } else { + printf("Map %s loading failed.\n", cWork); + } fclose(fp); - cout << "Map: " << cWork << " loaded." << endl; } else { - // cout << "Map: " << cWork << " not found." << endl; + printf("Map %s not found.\n", cWork); } return ret; } -Map::Map(FILE* fp) -{ - fread(&m_Vertex, 4, 1, fp); - fread(&m_Faces , 4, 1, fp); - mFinalVertex = new VERTEX[m_Vertex]; - mFinalFaces = new FACE [m_Faces]; +Map::Map() { +#ifdef MAP_COUNT_THINGS + _branches = 0; + _finals = 0; + _minx = 999999; + _miny = 999999; + _minz = 999999; + _maxx = -999999; + _maxy = -999999; + _maxz = -999999; +#endif + m_Faces = 0; + m_Nodes = 0; + m_FaceLists = 0; + mFinalFaces = NULL; + mNodes = NULL; + mFaceLists = NULL; +} + +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%x\n", head.version); + return(false); + } - fread(mFinalVertex, m_Vertex, sizeof(VERTEX), fp); - fread(mFinalFaces, m_Faces , sizeof(FACE), fp); - mRoot = new NODE; - RecLoadNode(mRoot, fp ); + printf("Map header: %lu faces, %u nodes, %lu facelists\n", head.face_count, head.node_count, 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 unsigned long[m_FaceLists]; + +// fread(mFinalVertex, m_Vertex, sizeof(VERTEX), fp); + unsigned long count; + if((count=fread(mFinalFaces, sizeof(FACE), m_Faces , fp)) != m_Faces) { + printf("Unable to read %lu faces from map file, got %lu.\n", m_Faces, count); + return(false); + } + if(fread(mNodes, sizeof(NODE), m_Nodes, fp) != m_Nodes) { + printf("Unable to read %lu faces from map file.\n", m_Nodes); + return(false); + } + if(fread(mFaceLists, sizeof(unsigned long), m_FaceLists, fp) != m_FaceLists) { + printf("Unable to read %lu faces from map file.\n", m_FaceLists); + return(false); + } + + +/* mRoot = new NODE(); + RecLoadNode(mRoot, fp );*/ + +#ifdef MAP_COUNT_THINGS + unsigned long 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, %lu branch nodes, %lu final nodes\n", m_Faces*3, m_Faces, _branches, _finals); + printf("Map BB: (%.2f -> %.2f, %.2f -> %.2f, %.2f -> %.2f)\n", _minx, _maxx, _miny, _maxy, _minz, _maxz); +#endif + return(true); } Map::~Map() { - safe_delete(mFinalVertex); - safe_delete(mFinalFaces); - RecFreeNode( mRoot ); +// safe_delete_array(mFinalVertex); + safe_delete_array(mFinalFaces); + safe_delete_array(mNodes); + safe_delete_array(mFaceLists); +// RecFreeNode( mRoot ); } -PNODE Map::SeekNode( PNODE _node, float x, float y ) -{ - if( !_node ) return NULL; - if( x>= _node->minx && x<= _node->maxx && y>= _node->miny && y<= _node->maxy ) - { - if( _node->mask ) - { - PNODE tmp; - tmp = SeekNode( _node->node1, x, y ); - if( tmp ) return tmp; - tmp = SeekNode( _node->node2, x, y ); - if( tmp ) return tmp; - tmp = SeekNode( _node->node3, x, y ); - if( tmp ) return tmp; - tmp = SeekNode( _node->node4, x, y ); - if( tmp ) return tmp; + +NodeRef Map::SeekNode( NodeRef node_r, float x, float y ) { + 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; } - else - return _node; +#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 != NULL) { + printf("\tNode: (%.2f -> %.2f, %.2f -> %.2f)\n", + _node->node1->minx, _node->node1->maxx, _node->node1->miny, _node->node1->maxy); +} +if(_node->node2 != NULL) { + printf("\tNode: (%.2f -> %.2f, %.2f -> %.2f)\n", + _node->node2->minx, _node->node2->maxx, _node->node2->miny, _node->node2->maxy); +} +if(_node->node3 != NULL) { + printf("\tNode: (%.2f -> %.2f, %.2f -> %.2f)\n", + _node->node3->minx, _node->node3->maxx, _node->node3->miny, _node->node3->maxy); +} +if(_node->node4 != NULL) { + 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 = NULL; +#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) + tmp = SeekNode( _node->nodes[2], x, y ); + } else { //quad 2 + if(_node->nodes[2] != NODE_NONE) + tmp = SeekNode( _node->nodes[1], x, y ); + } + } else { + if(y < midy) { //quad 4 + if(_node->nodes[2] != NODE_NONE) + tmp = SeekNode( _node->nodes[3], x, y ); + } else { //quad 1 + if(_node->nodes[2] != NODE_NONE) + tmp = SeekNode( _node->nodes[0], x, y ); + } + } + if( tmp != NODE_NONE ) return tmp; +#else + tmp = SeekNode( _node->nodes[0], x, y ); + if( tmp != NODE_NONE ) return tmp; + tmp = SeekNode( _node->nodes[1], x, y ); + if( tmp != NODE_NONE ) return tmp; + tmp = SeekNode( _node->nodes[2], x, y ); + if( tmp != NODE_NONE ) return tmp; + tmp = SeekNode( _node->nodes[3], x, y ); + if( tmp != NODE_NONE ) return tmp; +#endif + } - return NULL; +#ifdef DEBUG_SEEK +printf(" No node found.\n"); +#endif + return(NODE_NONE); } // maybe precalc edges. -int* Map::SeekFace( PNODE _node, float x, float y ) -{ +int* Map::SeekFace( NodeRef node_r, float x, float y ) { + if( node_r == NODE_NONE || node_r >= m_Nodes) { + return(NULL); + } + PNODE _node = &mNodes[node_r]; + if(!(_node->flags & nodeFinal)) { + return(NULL); //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; - for( int i=0;i<_node->nfaces;i++ ) - { - VERTEX v1 = mFinalVertex[ mFinalFaces[ _node->pfaces[ i ] ].a ]; - VERTEX v2 = mFinalVertex[ mFinalFaces[ _node->pfaces[ i ] ].b ]; - VERTEX v3 = mFinalVertex[ mFinalFaces[ _node->pfaces[ i ] ].c ]; + 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; @@ -111,67 +311,271 @@ nx = x - v3.x; ny = y - v3.y; if( dx*ny - dy*nx >0.0f ) continue; - *face++ = _node->pfaces[ i ]; + *face++ = mFaceLists[_node->faces.offset + i]; } *face = -1; return mCandFaces; } // can be op? -float Map::GetFaceHeight( int _idx, float x, float y ) -{ +float Map::GetFaceHeight( int _idx, float x, float y ) { PFACE pface = &mFinalFaces[ _idx ]; return ( pface->nd - x * pface->nx - y * pface->ny ) / pface->nz; } -// Load Time Dont OP!!! -void Map::RecLoadNode( PNODE _node, void *l_f ) -{ - fread( &_node->minx, 4, 1, (FILE*)l_f ); - fread( &_node->miny, 4, 1, (FILE*)l_f ); - fread( &_node->maxx, 4, 1, (FILE*)l_f ); - fread( &_node->maxy, 4, 1, (FILE*)l_f ); - fread( &_node->nfaces, 4, 1, (FILE*)l_f ); - _node->mask = 0; - _node->node1 = _node->node2 = _node->node3 = _node->node4 = NULL; - if( _node->nfaces ) - { - _node->pfaces = new unsigned int[_node->nfaces]; - fread( _node->pfaces, 4, _node->nfaces, (FILE*)l_f ); - } - else - { - _node->pfaces = NULL; - fread( &_node->mask, 1, 1, (FILE*)l_f ); - if( _node->mask&0x01 ) _node->node1 = new NODE; - if( _node->mask&0x02 ) _node->node2 = new NODE; - if( _node->mask&0x04 ) _node->node3 = new NODE; - if( _node->mask&0x08 ) _node->node4 = new NODE; +//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) { + VERTEX step; + VERTEX cur = start; + + 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; + + NodeRef cnode, lnode; + lnode = NULL; + //while we are not past end + //always do this once, even if start == end. + do { + //look at current location + cnode = SeekNode(GetRoot(), cur.x, cur.y); + if(cnode != NODE_NONE && cnode != lnode) { + if(LineIntersectsNode(cnode, start, end, result, on)) + return(true); + lnode = cnode; + } - if( _node->node1 ) RecLoadNode( _node->node1, l_f ); - if( _node->node2 ) RecLoadNode( _node->node2, l_f ); - if( _node->node3 ) RecLoadNode( _node->node3, l_f ); - if( _node->node4 ) RecLoadNode( _node->node4, l_f ); + //move 1 step + cur.x += step.x; + cur.y += step.y; + 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) ) { + cur.x = end.x; + } + if ( (cur.y > end.y && end.y > start.y) || (cur.y < end.y && end.y < start.y) ) { + cur.y = end.y; + } + if ( (cur.z > end.z && end.z > start.z) || (cur.z < end.z && end.z < start.z) ) { + cur.z = end.z; + } + } while(cur.x != end.x || cur.y != end.y || cur.z != end.z); + + //walked entire line and didnt run into anything... + return(false); +} + +bool Map::LocWithinNode( NodeRef node_r, float x, float y ) { + if( node_r == NODE_NONE || node_r >= m_Nodes) { + return(NULL); } + 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 ); } -// Free Time Dont OP!!! -void Map::RecFreeNode( PNODE _node ) -{ - if( _node ) - { - if( _node->node1 ) RecFreeNode( _node->node1 ); - if( _node->node2 ) RecFreeNode( _node->node2 ); - if( _node->node3 ) RecFreeNode( _node->node3 ); - if( _node->node4 ) RecFreeNode( _node->node4 ); - // Borra el nodo - if( _node->pfaces ) - { - delete _node->pfaces; - _node->pfaces = NULL; +bool Map::LineIntersectsNode( NodeRef node_r, VERTEX p1, VERTEX p2, VERTEX *result, FACE **on) { + if( node_r == NODE_NONE || node_r >= m_Nodes) { + return(true); //can see through empty nodes, just allow LOS on error... + } + PNODE _node = &mNodes[node_r]; + if(!(_node->flags & nodeFinal)) { + return(true); //not a final node... not sure best action + } + + unsigned long i; + + PFACE cur; + unsigned long *cfl = mFaceLists + _node->faces.offset; + + for(i = 0; i < _node->faces.count; i++) { + cur = &mFinalFaces[ *cfl ]; + if(LineIntersectsFace(cur,p1, p2, result)) { + *on = cur; + return(true); } - delete _node; - _node = NULL; + 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) { + if( node_r == NODE_NONE || node_r >= m_Nodes) { + return(NULL); + } + PNODE _node = &mNodes[node_r]; + if(!(_node->flags & nodeFinal)) { + return(-999999); //not a final node... could find the proper node... + } + + p1.z -= 1; + + VERTEX p2(p1); + p2.z = -999999; + + float best_z = -999999; + + unsigned long i; + + VERTEX r_tmp; + if(result == NULL) + result = &r_tmp; + + PFACE cur; + unsigned long *cfl = mFaceLists + _node->faces.offset; + +printf("Start finding best Z...\n"); + for(i = 0; i < _node->faces.count; i++) { + cur = &mFinalFaces[ *cfl ]; +//printf("Intersecting with face %lu\n", *cfl); + if(LineIntersectsFace(cur, p1, p2, result)) { +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); + if (result->z > best_z) { + *on = cur; + best_z = result->z; + } + } + cfl++; + } +fflush(stdout); +printf("Best Z found: %.2f\n", best_z); + + return best_z; +} + + +bool Map::LineIntersectsFace( PFACE cface, VERTEX p1, VERTEX p2, VERTEX *result) { + if( cface == NULL ) { + return(false); //cant intersect a face we dont have... i guess + } + +#define ABS(x) ((x)<0?-(x):(x)) +#define EPS 0.002f //acceptable error + + 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 ABS(x) ((x)<0?-(x):(x)) +//#define RTOD 57.2957795 //radians to degrees constant. + + float d; + float a1,a2,a3; + float total,denom,mu; + VERTEX n,pa1,pa2,pa3, intersect; + +// FACE *thisface = &mFinalFaces[ _node->pfaces[ i ] ]; + + VERTEX *p = &intersect; + if(result != NULL) + 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 = thisface->nx; + n.y = thisface->ny; + n.z = thisface->nz; + d = thisface->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; + 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); + if (mu < 0 || mu > 1) // Intersection not along line segment + return(false); + + // Determine whether or not the intersection point is bounded by pa,pb,pc + 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; + +// 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); +} + +void Map::Normalize(VERTEX *p) { + float len = sqrt(p->x*p->x + p->y*p->y + p->z*p->z); + p->x /= len; + p->y /= len; + p->z /= len; +} + Index: zone/map.h =================================================================== --- zone/map.h (revision 55) +++ zone/map.h (revision 75) @@ -18,50 +18,150 @@ #ifndef MAP_H #define MAP_H + +#define MAP_COUNT_THINGS //count things and print results + +//this is the current version number to expect from the map header +#define MAP_VERSION 0x01000000 + +#pragma pack(1) + typedef struct _vertex{ - unsigned int order; - float x, y, z; +// unsigned long order; + float x; + float y; + float z; + }VERTEX, *PVERTEX; typedef struct _face{ - unsigned int a, b, c; +// unsigned long a, b, c; //vertexs + VERTEX a; + VERTEX b; + VERTEX c; float nx, ny, nz, nd; }FACE, *PFACE; -typedef struct _node{ - float minx, miny, maxx, maxy; - int nfaces; +typedef struct _mapHeader { + unsigned long version; +// unsigned long vertex_count; + unsigned long face_count; + unsigned short node_count; + unsigned long 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; + + unsigned char flags; + union { + unsigned short nodes[4]; //index 0 means NULL, not root + struct { + unsigned long count; + unsigned long offset; + } faces; + }; +} nodeHeader, NODE, *PNODE; + +#pragma pack() + +//special value returned as 'not found' +#define NODE_NONE 65534 +#define MAP_ROOT_NODE 0 + +typedef unsigned short NodeRef; + +/*typedef struct _node { + nodeHeader head; unsigned int * pfaces; char mask; struct _node * node1, *node2, *node3, *node4; -}NODE, *PNODE; +}NODE, *PNODE;*/ -class Map; - class Map { public: static Map* Map::LoadMapfile(const char* in_zonename); - Map(FILE* fp); + Map(); ~Map(); - - PNODE SeekNode( PNODE _node, float x, float y ); - int *SeekFace( PNODE _node, float x, float y ); - float GetFaceHeight( int _idx, float x, float y ); - inline unsigned int GetVertexNumber( ) {return m_Vertex; } + + bool loadMap(FILE *fp); + + //the result is always final, except special NODE_NONE + NodeRef SeekNode( NodeRef _node, float x, float y ); + + //these are untested since rewrite: + int *SeekFace( NodeRef _node, float x, float y ); + float GetFaceHeight( int _idx, float x, float y ); + + bool LocWithinNode( NodeRef _node, float x, float y ); + + bool LineIntersectsZone(VERTEX start, VERTEX end, float step, VERTEX *result, FACE **on = NULL); + + //nodes to these functions must be final + bool LineIntersectsNode( NodeRef _node, VERTEX start, VERTEX end, VERTEX *result, FACE **on = NULL); + bool LineIntersectsFace( PFACE cface, VERTEX start, VERTEX end, VERTEX *result); + float FindBestZ( NodeRef _node, VERTEX start, VERTEX *result, FACE **on = NULL); + +// inline unsigned int GetVertexNumber( ) {return m_Vertex; } inline unsigned int GetFacesNumber( ) { return m_Faces; } - inline PVERTEX GetVertex( int _idx ) {return mFinalVertex + _idx; } +// inline PVERTEX GetVertex( int _idx ) {return mFinalVertex + _idx; } inline PFACE GetFace( int _idx) {return mFinalFaces + _idx; } - inline PNODE GetRoot( ) { return mRoot; } + inline PFACE GetFaceFromlist( int _idx) {return &mFinalFaces[ mFaceLists[_idx] ]; } + inline NodeRef GetRoot( ) { return MAP_ROOT_NODE; } + inline PNODE GetNode( NodeRef r ) { return( mNodes + r ); } private: - unsigned int m_Vertex; - unsigned int m_Faces; - PVERTEX mFinalVertex; - PFACE mFinalFaces; - PNODE mRoot; - int mCandFaces[100]; +// unsigned long m_Vertex; + unsigned long m_Faces; + unsigned long m_Nodes; + unsigned long m_FaceLists; +// PVERTEX mFinalVertex; + PFACE mFinalFaces; + PNODE mNodes; + unsigned long *mFaceLists; + + + int mCandFaces[100]; + +#ifdef MAP_COUNT_THINGS + unsigned long _branches; + unsigned long _finals; + float _minx, _miny, _maxx, _maxy; + float _minz, _maxz; +#endif + + static void Normalize(VERTEX *p); - void RecLoadNode( PNODE _node, void *l_f ); - void RecFreeNode( PNODE _node ); +// void RecLoadNode( PNODE _node, FILE *l_f ); +// void RecFreeNode( PNODE _node ); }; #endif