diff --git a/Detour/Source/DetourTiledNavMesh.cpp b/Detour/Source/DetourTileNavMesh.cpp similarity index 61% rename from Detour/Source/DetourTiledNavMesh.cpp rename to Detour/Source/DetourTileNavMesh.cpp index 2fed134..3e7d58b 100644 --- a/Detour/Source/DetourTiledNavMesh.cpp +++ b/Detour/Source/DetourTileNavMesh.cpp @@ -1,280 +1,40 @@ -#include "DetourTiledNavMesh.h" +// +// Copyright (c) 2009 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// #include #include #include #include +#include "DetourTileNavMesh.h" +#include "DetourNode.h" +#include "DetourCommon.h" -////////////////////////////////////////////////////////////////////////////////////////// -template inline void swap(T& a, T& b) { T t = a; a = b; b = t; } -template inline T min(T a, T b) { return a < b ? a : b; } -template inline T max(T a, T b) { return a > b ? a : b; } -template inline T abs(T a) { return a < 0 ? -a : a; } -template inline T sqr(T a) { return a*a; } -template inline T clamp(T v, T mn, T mx) { return v < mn ? mn : (v > mx ? mx : v); } +inline int opposite(int side) { return (side+2) & 0x3; } -// Some vector utils -inline void vcross(float* dest, const float* v1, const float* v2) +inline bool overlapBoxes(const float* amin, const float* amax, + const float* bmin, const float* bmax) { - dest[0] = v1[1]*v2[2] - v1[2]*v2[1]; - dest[1] = v1[2]*v2[0] - v1[0]*v2[2]; - dest[2] = v1[0]*v2[1] - v1[1]*v2[0]; -} - -inline float vdot(const float* v1, const float* v2) -{ - return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2]; -} - -inline void vsub(float* dest, const float* v1, const float* v2) -{ - dest[0] = v1[0]-v2[0]; - dest[1] = v1[1]-v2[1]; - dest[2] = v1[2]-v2[2]; -} - -inline void vmin(float* mn, const float* v) -{ - mn[0] = min(mn[0], v[0]); - mn[1] = min(mn[1], v[1]); - mn[2] = min(mn[2], v[2]); -} - -inline void vmax(float* mx, const float* v) -{ - mx[0] = max(mx[0], v[0]); - mx[1] = max(mx[1], v[1]); - mx[2] = max(mx[2], v[2]); -} - -inline void vcopy(float* dest, const float* a) -{ - dest[0] = a[0]; - dest[1] = a[1]; - dest[2] = a[2]; -} - -inline float vdistSqr(const float* v1, const float* v2) -{ - float dx = v2[0] - v1[0]; - float dy = v2[1] - v1[1]; - float dz = v2[2] - v1[2]; - return dx*dx + dy*dy + dz*dz; -} - -inline void vnormalize(float* v) -{ - float d = 1.0f / sqrtf(sqr(v[0]) + sqr(v[1]) + sqr(v[2])); - v[0] *= d; - v[1] *= d; - v[2] *= d; -} - -inline bool vequal(const float* p0, const float* p1) -{ - static const float thr = sqr(1.0f/16384.0f); - const float d = vdistSqr(p0, p1); - return d < thr; -} - -inline float vdot2D(const float* u, const float* v) -{ - return u[0]*v[0] + u[2]*v[2]; -} -inline float vperp2D(const float* u, const float* v) -{ - return u[2]*v[0] - u[0]*v[2]; -} - -inline float triArea2D(const float* a, const float* b, const float* c) -{ - return ((b[0]*a[2] - a[0]*b[2]) + (c[0]*b[2] - b[0]*c[2]) + (a[0]*c[2] - c[0]*a[2])) * 0.5f; -} - -static float distancePtSegSqr2D(const float* pt, const float* p, const float* q, float& t) -{ - float pqx = q[0] - p[0]; - float pqz = q[2] - p[2]; - float dx = pt[0] - p[0]; - float dz = pt[2] - p[2]; - float d = pqx*pqx + pqz*pqz; - t = pqx*dx + pqz*dz; - if (d > 0) - t /= d; - if (t < 0) - t = 0; - else if (t > 1) - t = 1; - - dx = p[0] + t*pqx - pt[0]; - dz = p[2] + t*pqz - pt[2]; - - return dx*dx + dz*dz; -} - -static bool intersectSegmentPoly2D(const float* p0, const float* p1, - const float* verts, int nverts, - float& tmin, float& tmax, - int& segMin, int& segMax) -{ - static const float EPS = 0.00000001f; - - tmin = 0; - tmax = 1; - segMin = -1; - segMax = -1; - - float dir[3]; - vsub(dir, p1, p0); - - for (int i = 0, j = nverts-1; i < nverts; j=i++) - { - float edge[3], diff[3]; - vsub(edge, &verts[i*3], &verts[j*3]); - vsub(diff, p0, &verts[j*3]); - float n = vperp2D(edge, diff); - float d = -vperp2D(edge, dir); - if (fabs(d) < EPS) - { - // S is nearly parallel to this edge - if (n < 0) - return false; - else - continue; - } - float t = n / d; - if (d < 0) - { - // segment S is entering across this edge - if (t > tmin) - { - tmin = t; - segMin = j; - // S enters after leaving polygon - if (tmin > tmax) - return false; - } - } - else - { - // segment S is leaving across this edge - if (t < tmax) - { - tmax = t; - segMax = j; - // S leaves before entering polygon - if (tmax < tmin) - return false; - } - } - } - - return true; -} - -static void calcPolyCenter(float* tc, const dtTilePoly* p, const float* verts) -{ - tc[0] = 0.0f; - tc[1] = 0.0f; - tc[2] = 0.0f; - for (int j = 0; j < (int)p->nv; ++j) - { - const float* v = &verts[p->v[j]*3]; - tc[0] += v[0]; - tc[1] += v[1]; - tc[2] += v[2]; - } - const float s = 1.0f / p->nv; - tc[0] *= s; - tc[1] *= s; - tc[2] *= s; -} - -static void closestPtPointTriangle(float* closest, const float* p, - const float* a, const float* b, const float* c) -{ - // Check if P in vertex region outside A - float ab[3], ac[3], ap[3]; - vsub(ab, b, a); - vsub(ac, c, a); - vsub(ap, p, a); - float d1 = vdot(ab, ap); - float d2 = vdot(ac, ap); - if (d1 <= 0.0f && d2 <= 0.0f) - { - // barycentric coordinates (1,0,0) - vcopy(closest, a); - return; - } - - // Check if P in vertex region outside B - float bp[3]; - vsub(bp, p, b); - float d3 = vdot(ab, bp); - float d4 = vdot(ac, bp); - if (d3 >= 0.0f && d4 <= d3) - { - // barycentric coordinates (0,1,0) - vcopy(closest, b); - return; - } - - // Check if P in edge region of AB, if so return projection of P onto AB - float vc = d1*d4 - d3*d2; - if (vc <= 0.0f && d1 >= 0.0f && d3 <= 0.0f) - { - // barycentric coordinates (1-v,v,0) - float v = d1 / (d1 - d3); - closest[0] = a[0] + v * ab[0]; - closest[1] = a[1] + v * ab[1]; - closest[2] = a[2] + v * ab[2]; - return; - } - - // Check if P in vertex region outside C - float cp[3]; - vsub(cp, p, c); - float d5 = vdot(ab, cp); - float d6 = vdot(ac, cp); - if (d6 >= 0.0f && d5 <= d6) - { - // barycentric coordinates (0,0,1) - vcopy(closest, c); - return; - } - - // Check if P in edge region of AC, if so return projection of P onto AC - float vb = d5*d2 - d1*d6; - if (vb <= 0.0f && d2 >= 0.0f && d6 <= 0.0f) - { - // barycentric coordinates (1-w,0,w) - float w = d2 / (d2 - d6); - closest[0] = a[0] + w * ac[0]; - closest[1] = a[1] + w * ac[1]; - closest[2] = a[2] + w * ac[2]; - return; - } - - // Check if P in edge region of BC, if so return projection of P onto BC - float va = d3*d6 - d5*d4; - if (va <= 0.0f && (d4 - d3) >= 0.0f && (d5 - d6) >= 0.0f) - { - // barycentric coordinates (0,1-w,w) - float w = (d4 - d3) / ((d4 - d3) + (d5 - d6)); - closest[0] = b[0] + w * (c[0] - b[0]); - closest[1] = b[1] + w * (c[1] - b[1]); - closest[2] = b[2] + w * (c[2] - b[2]); - return; - } - - // P inside face region. Compute Q through its barycentric coordinates (u,v,w) - float denom = 1.0f / (va + vb + vc); - float v = vb * denom; - float w = vc * denom; - closest[0] = a[0] + ab[0] * v + ac[0] * w; - closest[1] = a[1] + ab[1] * v + ac[1] * w; - closest[2] = a[2] + ab[2] * v + ac[2] * w; + bool overlap = true; + overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap; + overlap = (amin[1] > bmax[1] || amax[1] < bmin[1]) ? false : overlap; + overlap = (amin[2] > bmax[2] || amax[2] < bmin[2]) ? false : overlap; + return overlap; } inline bool overlapRects(const float* amin, const float* amax, @@ -286,21 +46,9 @@ inline bool overlapRects(const float* amin, const float* amax, return overlap; } -inline bool overlapBounds(const float* amin, const float* amax, - const float* bmin, const float* bmax) -{ - bool overlap = true; - overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap; - overlap = (amin[1] > bmax[1] || amax[1] < bmin[1]) ? false : overlap; - overlap = (amin[2] > bmax[2] || amax[2] < bmin[2]) ? false : overlap; - return overlap; -} - -inline int opposite(int side) { return (side+2) & 0x3; } - -static void calcBounds(const float* va, const float* vb, - float* bmin, float* bmax, - int side, float padx, float pady) +static void calcRect(const float* va, const float* vb, + float* bmin, float* bmax, + int side, float padx, float pady) { if ((side&1) == 0) { @@ -318,239 +66,14 @@ static void calcBounds(const float* va, const float* vb, } } - -////////////////////////////////////////////////////////////////////////////////////////// -struct dtTileNode +inline int computeTileHash(int x, int y) { - enum dtTileNodeFlags - { - OPEN = 0x01, - CLOSED = 0x02, - }; - dtTileNode* parent; - float cost; - float total; - unsigned int id; - unsigned char flags; // TODO: merge to id or parent? -}; - -class dtTileNodePool -{ -public: - dtTileNodePool(int maxNodes, int hashSize); - ~dtTileNodePool(); - inline void operator=(const dtTileNodePool&) {} - void clear(); - dtTileNode* getNode(unsigned short id); - const dtTileNode* findNode(unsigned short id) const; - - inline int getMemUsed() const - { - return sizeof(*this) + - sizeof(dtTileNode)*m_maxNodes + - sizeof(unsigned short)*m_maxNodes + - sizeof(unsigned short)*m_hashSize; - } - -private: - inline unsigned int hashint(unsigned int a) const - { - a += ~(a<<15); - a ^= (a>>10); - a += (a<<3); - a ^= (a>>6); - a += ~(a<<11); - a ^= (a>>16); - return a; - } - - dtTileNode* m_nodes; - unsigned short* m_first; - unsigned short* m_next; - const int m_maxNodes; - const int m_hashSize; - int m_nodeCount; -}; - -dtTileNodePool::dtTileNodePool(int maxNodes, int hashSize) : - m_maxNodes(maxNodes), - m_hashSize(hashSize), - m_nodes(0), - m_first(0), - m_next(0) -{ - m_nodes = new dtTileNode[m_maxNodes]; - m_next = new unsigned short[m_maxNodes]; - m_first = new unsigned short[hashSize]; - memset(m_first, 0xff, sizeof(unsigned short)*m_hashSize); - memset(m_next, 0xff, sizeof(unsigned short)*m_maxNodes); + const unsigned int h1 = 0x8da6b343; // Large multiplicative constants; + const unsigned int h2 = 0xd8163841; // here arbitrarily chosen primes + unsigned int n = h1 * x + h2 * y; + return (int)(n & (DT_TILE_LOOKUP_SIZE-1)); } -dtTileNodePool::~dtTileNodePool() -{ - delete [] m_nodes; - delete [] m_next; - delete [] m_first; -} - -void dtTileNodePool::clear() -{ - memset(m_first, 0xff, sizeof(unsigned short)*m_hashSize); - m_nodeCount = 0; -} - -const dtTileNode* dtTileNodePool::findNode(unsigned short id) const -{ - unsigned int bucket = hashint((unsigned int)id) & (m_hashSize-1); - unsigned short i = m_first[bucket]; - while (i != 0xffff) - { - if (m_nodes[i].id == id) - return &m_nodes[i]; - i = m_next[i]; - } - return 0; -} - -dtTileNode* dtTileNodePool::getNode(unsigned short id) -{ - unsigned int bucket = hashint((unsigned int)id) & (m_hashSize-1); - unsigned short i = m_first[bucket]; - dtTileNode* node = 0; - while (i != 0xffff) - { - if (m_nodes[i].id == id) - return &m_nodes[i]; - i = m_next[i]; - } - - if (m_nodeCount >= m_maxNodes) - return 0; - - i = (unsigned short)m_nodeCount; - m_nodeCount++; - - // Init node - node = &m_nodes[i]; - node->parent = 0; - node->cost = 0; - node->total = 0; - node->id = id; - node->flags = 0; - - m_next[i] = m_first[bucket]; - m_first[bucket] = i; - - return node; -} - - -////////////////////////////////////////////////////////////////////////////////////////// -class dtTileNodeQueue -{ -public: - dtTileNodeQueue(int n); - ~dtTileNodeQueue(); - inline void operator=(dtTileNodeQueue&) {} - - inline void clear() - { - m_size = 0; - } - - inline dtTileNode* top() - { - return m_heap[0]; - } - - inline dtTileNode* pop() - { - dtTileNode* result = m_heap[0]; - m_size--; - trickleDown(0, m_heap[m_size]); - return result; - } - - inline void push(dtTileNode* node) - { - m_size++; - bubbleUp(m_size-1, node); - } - - inline void modify(dtTileNode* node) - { - for (int i = 0; i < m_size; ++i) - { - if (m_heap[i] == node) - { - bubbleUp(i, node); - return; - } - } - } - - inline bool empty() const { return m_size == 0; } - - inline int getMemUsed() const - { - return sizeof(*this) + - sizeof(dtTileNode*)*(m_capacity+1); - } - - -private: - void bubbleUp(int i, dtTileNode* node); - void trickleDown(int i, dtTileNode* node); - - dtTileNode** m_heap; - const int m_capacity; - int m_size; -}; - -dtTileNodeQueue::dtTileNodeQueue(int n) : - m_capacity(n), - m_size(0), - m_heap(0) -{ - m_heap = new dtTileNode*[m_capacity+1]; -} - -dtTileNodeQueue::~dtTileNodeQueue() -{ - delete [] m_heap; -} - -void dtTileNodeQueue::bubbleUp(int i, dtTileNode* node) -{ - int parent = (i-1)/2; - // note: (index > 0) means there is a parent - while ((i > 0) && (m_heap[parent]->total > node->total)) - { - m_heap[i] = m_heap[parent]; - i = parent; - parent = (i-1)/2; - } - m_heap[i] = node; -} - -void dtTileNodeQueue::trickleDown(int i, dtTileNode* node) -{ - int child = (i*2)+1; - while (child < m_size) - { - if (((child+1) < m_size) && - (m_heap[child]->total > m_heap[child+1]->total)) - { - child++; - } - m_heap[i] = m_heap[child]; - i = child; - child = (i*2)+1; - } - bubbleUp(i, node); -} - - ////////////////////////////////////////////////////////////////////////////////////////// dtTiledNavMesh::dtTiledNavMesh() : m_tileSize(0), @@ -565,18 +88,16 @@ dtTiledNavMesh::dtTiledNavMesh() : dtTiledNavMesh::~dtTiledNavMesh() { - // TODO! the mesh should not handle the tile memory! for (int i = 0; i < DT_MAX_TILES; ++i) { - if (m_tiles[i].header) + if (m_tiles[i].data && m_tiles[i].dataSize < 0) { - delete [] (unsigned char*)m_tiles[i].header; - m_tiles[i].header = 0; + delete [] m_tiles[i].data; + m_tiles[i].data = 0; + m_tiles[i].dataSize = 0; } } - delete [] m_tmpLinks; - delete m_nodePool; delete m_openList; } @@ -597,35 +118,24 @@ bool dtTiledNavMesh::init(const float* orig, float tileSize, float portalHeight) m_nextFree = &m_tiles[i]; } - m_nodePool = new dtTileNodePool(2048, 256); if (!m_nodePool) - return false; + { + m_nodePool = new dtNodePool(2048, 256); + if (!m_nodePool) + return false; + } - m_openList = new dtTileNodeQueue(2048); if (!m_openList) - return false; + { + m_openList = new dtNodeQueue(2048); + if (!m_openList) + return false; + } return true; } -int dtTiledNavMesh::getPolyNeighbours(dtTilePolyRef ref, dtTilePolyRef* nei, int maxNei) const -{ - int salt, it, ip; - decodeId(ref, salt, it, ip); - if (it >= DT_MAX_TILES) return 0; - if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return 0; - const dtTileHeader* h = m_tiles[it].header; - - if (ip >= h->npolys) return 0; - const dtTilePoly* poly = &h->polys[ip]; - - int n = 0; - for (int i = 0; i < poly->nlinks; ++i) - if (n < maxNei) nei[n++] = h->links[poly->links+i].ref; - - return n; -} - +////////////////////////////////////////////////////////////////////////////////////////// int dtTiledNavMesh::findConnectingPolys(const float* va, const float* vb, dtTile* tile, int side, dtTilePolyRef* con, float* conarea, int maxcon) @@ -634,7 +144,7 @@ int dtTiledNavMesh::findConnectingPolys(const float* va, const float* vb, dtTileHeader* h = tile->header; float amin[2], amax[2]; - calcBounds(va,vb, amin,amax, side, 0.01f, m_portalHeight); + calcRect(va,vb, amin,amax, side, 0.01f, m_portalHeight); // Remove links pointing to 'side' and compact the links array. float bmin[2], bmax[2]; @@ -653,7 +163,7 @@ int dtTiledNavMesh::findConnectingPolys(const float* va, const float* vb, // Check if the segments touch. const float* vc = &h->verts[poly->v[j]*3]; const float* vd = &h->verts[poly->v[(j+1) % (int)poly->nv]*3]; - calcBounds(vc,vd, bmin,bmax, side, 0.01f, m_portalHeight); + calcRect(vc,vd, bmin,bmax, side, 0.01f, m_portalHeight); if (!overlapRects(amin,amax, bmin,bmax)) continue; // Add return value. if (n < maxcon) @@ -752,8 +262,22 @@ void dtTiledNavMesh::buildExtLinks(dtTile* tile, dtTile* target, int side) link->p = (unsigned short)i; link->e = (unsigned char)j; link->side = (unsigned char)side; - link->bmin = neia[k*2+0]; - link->bmax = neia[k*2+1]; + + // Compress portal limits to a byte value. + if (side == 0 || side == 2) + { + const float lmin = min(va[2], vb[2]); + const float lmax = max(va[2], vb[2]); + link->bmin = (unsigned char)(clamp((neia[k*2+0]-lmin)/(lmax-lmin), 0.0f, 1.0f)*255.0f); + link->bmax = (unsigned char)(clamp((neia[k*2+1]-lmin)/(lmax-lmin), 0.0f, 1.0f)*255.0f); + } + else + { + const float lmin = min(va[0], vb[0]); + const float lmax = max(va[0], vb[0]); + link->bmin = (unsigned char)(clamp((neia[k*2+0]-lmin)/(lmax-lmin), 0.0f, 1.0f)*255.0f); + link->bmax = (unsigned char)(clamp((neia[k*2+1]-lmin)/(lmax-lmin), 0.0f, 1.0f)*255.0f); + } nplinks++; } } @@ -800,18 +324,10 @@ void dtTiledNavMesh::buildIntLinks(dtTile* tile) h->nlinks = nlinks; } -inline int computeTileHash(int x, int y) +bool dtTiledNavMesh::addTileAt(int x, int y, unsigned char* data, int dataSize, bool ownsData) { - const unsigned int h1 = 0x8da6b343; // Large multiplicative constants; - const unsigned int h2 = 0xd8163841; // here arbitrarily chosen primes - unsigned int n = h1 * x + h2 * y; - return (int)(n & (DT_TILE_LOOKUP_SIZE-1)); -} - -bool dtTiledNavMesh::addTile(int x, int y, unsigned char* data, int dataSize) -{ - // Remove any old tile at this location. - removeTile(x,y); + if (getTileAt(x,y)) + return false; // Make sure there is enough space for new tile. if (!m_nextFree) return false; @@ -855,17 +371,17 @@ bool dtTiledNavMesh::addTile(int x, int y, unsigned char* data, int dataSize) tile->header = header; tile->x = x; tile->y = y; + tile->data = data; + tile->dataSize = ownsData ? -dataSize : dataSize; buildIntLinks(tile); // Create connections connections. for (int i = 0; i < 4; ++i) { - dtTile* nei = getNeighbourTile(x,y,i); - tile->header->nei[i] = nei; - if (tile->header->nei[i]) + dtTile* nei = getNeighbourTileAt(x,y,i); + if (nei) { - nei->header->nei[opposite(i)] = tile; buildExtLinks(tile, nei, i); buildExtLinks(nei, tile, opposite(i)); } @@ -874,7 +390,7 @@ bool dtTiledNavMesh::addTile(int x, int y, unsigned char* data, int dataSize) return true; } -dtTile* dtTiledNavMesh::getTile(int x, int y) +dtTile* dtTiledNavMesh::getTileAt(int x, int y) { // Find tile based on hash. int h = computeTileHash(x,y); @@ -888,7 +404,17 @@ dtTile* dtTiledNavMesh::getTile(int x, int y) return 0; } -dtTile* dtTiledNavMesh::getNeighbourTile(int x, int y, int side) +dtTile* dtTiledNavMesh::getTile(int i) +{ + return &m_tiles[i]; +} + +const dtTile* dtTiledNavMesh::getTile(int i) const +{ + return &m_tiles[i]; +} + +dtTile* dtTiledNavMesh::getNeighbourTileAt(int x, int y, int side) { switch (side) { @@ -897,45 +423,72 @@ dtTile* dtTiledNavMesh::getNeighbourTile(int x, int y, int side) case 2: x--; break; case 3: y--; break; }; - return getTile(x,y); + return getTileAt(x,y); } -bool dtTiledNavMesh::removeTile(int x, int y) +bool dtTiledNavMesh::removeTileAt(int x, int y, unsigned char** data, int* dataSize) { - dtTile* tile = getTile(x, y); + // Remove tile from hash lookup. + int h = computeTileHash(x,y); + dtTile* prev = 0; + dtTile* tile = m_posLookup[h]; + while (tile) + { + if (tile->x == x && tile->y == y) + { + if (prev) + prev->next = tile->next; + else + m_posLookup[h] = tile->next; + break; + } + prev = tile; + tile = tile->next; + } if (!tile) return false; - + // Remove connections to neighbour tiles. for (int i = 0; i < 4; ++i) { - dtTile* nei = getNeighbourTile(x,y,i); + dtTile* nei = getNeighbourTileAt(x,y,i); if (!nei) continue; - nei->header->nei[opposite(i)] = 0; removeExtLinks(nei, opposite(i)); } - + + // Reset tile. - unsigned char* data = (unsigned char*)tile->header; + if (tile->dataSize < 0) + { + // Owns data + delete [] tile->data; + tile->data = 0; + tile->dataSize = 0; + if (data) *data = 0; + if (dataSize) *dataSize = 0; + } + else + { + if (data) *data = tile->data; + if (dataSize) *dataSize = tile->dataSize; + } tile->header = 0; - tile->x = -1; - tile->y = -1; + tile->x = tile->y = 0; tile->salt++; - // TODO! the mesh should not handle the tile memory! - delete [] data; - + // Add to free list. + tile->next = m_nextFree; + m_nextFree = tile; + return true; } - - bool dtTiledNavMesh::closestPointToPoly(dtTilePolyRef ref, const float* pos, float* closest) const { - int salt, it, ip; - decodeId(ref, salt, it, ip); + unsigned int salt, it, ip; + dtDecodeTileId(ref, salt, it, ip); if (it >= DT_MAX_TILES) return false; if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return false; const dtTileHeader* h = m_tiles[it].header; @@ -994,7 +547,7 @@ dtTilePolyRef dtTiledNavMesh::getTileId(dtTile* tile) { if (!tile) return 0; const unsigned int it = tile - m_tiles; - return encodeId(tile->salt, it, 0); + return dtEncodeTileId(tile->salt, it, 0); } int dtTiledNavMesh::queryTilePolygons(dtTile* tile, @@ -1018,7 +571,7 @@ int dtTiledNavMesh::queryTilePolygons(dtTile* tile, vmin(bmin, v); vmax(bmax, v); } - if (overlapBounds(qmin,qmax, bmin,bmax)) + if (overlapBoxes(qmin,qmax, bmin,bmax)) { if (n < maxPolys) polys[n++] = base | (dtTilePolyRef)i; @@ -1051,7 +604,7 @@ int dtTiledNavMesh::queryPolygons(const float* center, const float* extents, { for (int x = minx; x < maxx; ++x) { - dtTile* tile = getTile(x,y); + dtTile* tile = getTileAt(x,y); if (!tile) continue; n += queryTilePolygons(tile, bmin, bmax, polys+n, maxPolys-n); if (n >= maxPolys) return n; @@ -1063,19 +616,19 @@ int dtTiledNavMesh::queryPolygons(const float* center, const float* extents, float dtTiledNavMesh::getCost(dtTilePolyRef prev, dtTilePolyRef from, dtTilePolyRef to) const { - int salt, it, ip; + unsigned int salt, it, ip; if (prev) from = prev; // The API input has been cheked already, skip checking internal data. - decodeId(from, salt, it, ip); + dtDecodeTileId(from, salt, it, ip); const dtTileHeader* fromHeader = m_tiles[it].header; const dtTilePoly* fromPoly = &fromHeader->polys[ip]; - decodeId(to, salt, it, ip); + dtDecodeTileId(to, salt, it, ip); const dtTileHeader* toHeader = m_tiles[it].header; const dtTilePoly* toPoly = &toHeader->polys[ip]; float fromPc[3], toPc[3]; - calcPolyCenter(fromPc, fromPoly, fromHeader->verts); - calcPolyCenter(toPc, toPoly, toHeader->verts); + calcPolyCenter(fromPc, fromPoly->v, fromPoly->nv, fromHeader->verts); + calcPolyCenter(toPc, toPoly->v, toPoly->nv, toHeader->verts); float dx = fromPc[0]-toPc[0]; float dy = fromPc[1]-toPc[1]; @@ -1086,18 +639,18 @@ float dtTiledNavMesh::getCost(dtTilePolyRef prev, dtTilePolyRef from, dtTilePoly float dtTiledNavMesh::getHeuristic(dtTilePolyRef from, dtTilePolyRef to) const { - int salt, it, ip; + unsigned int salt, it, ip; // The API input has been cheked already, skip checking internal data. - decodeId(from, salt, it, ip); + dtDecodeTileId(from, salt, it, ip); const dtTileHeader* fromHeader = m_tiles[it].header; const dtTilePoly* fromPoly = &fromHeader->polys[ip]; - decodeId(to, salt, it, ip); + dtDecodeTileId(to, salt, it, ip); const dtTileHeader* toHeader = m_tiles[it].header; const dtTilePoly* toPoly = &toHeader->polys[ip]; float fromPc[3], toPc[3]; - calcPolyCenter(fromPc, fromPoly, fromHeader->verts); - calcPolyCenter(toPc, toPoly, toHeader->verts); + calcPolyCenter(fromPc, fromPoly->v, fromPoly->nv, fromHeader->verts); + calcPolyCenter(toPc, toPoly->v, toPoly->nv, toHeader->verts); float dx = fromPc[0]-toPc[0]; float dy = fromPc[1]-toPc[1]; @@ -1130,19 +683,19 @@ int dtTiledNavMesh::findPath(dtTilePolyRef startRef, dtTilePolyRef endRef, m_nodePool->clear(); m_openList->clear(); - dtTileNode* startNode = m_nodePool->getNode(startRef); - startNode->parent = 0; + dtNode* startNode = m_nodePool->getNode(startRef); + startNode->pidx = 0; startNode->cost = 0; startNode->total = getHeuristic(startRef, endRef); startNode->id = startRef; - startNode->flags = dtTileNode::OPEN; + startNode->flags = DT_NODE_OPEN; m_openList->push(startNode); - dtTileNode* lastBestNode = startNode; - float lastBestNodeCost = startNode->total; + dtNode* lastBestNode = startNode; + unsigned short lastBestNodeCost = startNode->total; while (!m_openList->empty()) { - dtTileNode* bestNode = m_openList->pop(); + dtNode* bestNode = m_openList->pop(); if (bestNode->id == endRef) { @@ -1151,8 +704,8 @@ int dtTiledNavMesh::findPath(dtTilePolyRef startRef, dtTilePolyRef endRef, } // Get poly and tile. - int salt, it, ip; - decodeId(bestNode->id, salt, it, ip); + unsigned int salt, it, ip; + dtDecodeTileId(bestNode->id, salt, it, ip); // The API input has been cheked already, skip checking internal data. const dtTileHeader* h = m_tiles[it].header; const dtTilePoly* poly = &h->polys[ip]; @@ -1163,25 +716,28 @@ int dtTiledNavMesh::findPath(dtTilePolyRef startRef, dtTilePolyRef endRef, if (neighbour) { // Skip parent node. - if (bestNode->parent && bestNode->parent->id == neighbour) + if (bestNode->pidx && m_nodePool->getNodeAtIdx(bestNode->pidx)->id == neighbour) continue; - - dtTileNode newNode; - newNode.parent = bestNode; + + dtNode* parent = bestNode; + dtNode newNode; + newNode.pidx = m_nodePool->getNodeIdx(parent); newNode.id = neighbour; - newNode.cost = bestNode->cost + getCost(newNode.parent->parent ? newNode.parent->parent->id : 0, newNode.parent->id, newNode.id); + + dtTilePolyRef prevRef = parent->pidx ? m_nodePool->getNodeAtIdx(parent->pidx)->id : 0; float h = getHeuristic(newNode.id, endRef); + newNode.cost = bestNode->cost + getCost(prevRef, parent->id, newNode.id); newNode.total = newNode.cost + h; - dtTileNode* actualNode = m_nodePool->getNode(newNode.id); + dtNode* actualNode = m_nodePool->getNode(newNode.id); if (!actualNode) continue; - if (!((actualNode->flags & dtTileNode::OPEN) && newNode.total > actualNode->total) && - !((actualNode->flags & dtTileNode::CLOSED) && newNode.total > actualNode->total)) + if (!((actualNode->flags & DT_NODE_OPEN) && newNode.total > actualNode->total) && + !((actualNode->flags & DT_NODE_CLOSED) && newNode.total > actualNode->total)) { - actualNode->flags &= dtTileNode::CLOSED; - actualNode->parent = newNode.parent; + actualNode->flags &= DT_NODE_CLOSED; + actualNode->pidx = newNode.pidx; actualNode->cost = newNode.cost; actualNode->total = newNode.total; @@ -1191,13 +747,13 @@ int dtTiledNavMesh::findPath(dtTilePolyRef startRef, dtTilePolyRef endRef, lastBestNode = actualNode; } - if (actualNode->flags & dtTileNode::OPEN) + if (actualNode->flags & DT_NODE_OPEN) { m_openList->modify(actualNode); } else { - actualNode->flags = dtTileNode::OPEN; + actualNode->flags = DT_NODE_OPEN; m_openList->push(actualNode); } } @@ -1206,12 +762,12 @@ int dtTiledNavMesh::findPath(dtTilePolyRef startRef, dtTilePolyRef endRef, } // Reverse the path. - dtTileNode* prev = 0; - dtTileNode* node = lastBestNode; + dtNode* prev = 0; + dtNode* node = lastBestNode; do { - dtTileNode* next = node->parent; - node->parent = prev; + dtNode* next = m_nodePool->getNodeAtIdx(node->pidx); + node->pidx = m_nodePool->getNodeIdx(prev); prev = node; node = next; } @@ -1223,7 +779,7 @@ int dtTiledNavMesh::findPath(dtTilePolyRef startRef, dtTilePolyRef endRef, do { path[n++] = node->id; - node = node->parent; + node = m_nodePool->getNodeAtIdx(node->pidx); } while (node && n < maxPathSize); @@ -1383,8 +939,8 @@ int dtTiledNavMesh::findStraightPath(const float* startPos, const float* endPos, // Returns portal points between two polygons. bool dtTiledNavMesh::getPortalPoints(dtTilePolyRef from, dtTilePolyRef to, float* left, float* right) const { - int salt, it, ip; - decodeId(from, salt, it, ip); + unsigned int salt, it, ip; + dtDecodeTileId(from, salt, it, ip); if (it >= DT_MAX_TILES) return false; if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return false; if (ip >= m_tiles[it].header->npolys) return false; @@ -1405,17 +961,29 @@ bool dtTiledNavMesh::getPortalPoints(dtTilePolyRef from, dtTilePolyRef to, float // the link width. if (link->side == 0 || link->side == 2) { - left[2] = max(left[2],link->bmin); - left[2] = min(left[2],link->bmax); - right[2] = max(right[2],link->bmin); - right[2] = min(right[2],link->bmax); + // Unpack portal limits. + const float smin = min(left[2],right[2]); + const float smax = max(left[2],right[2]); + const float s = (smax-smin) / 255.0f; + const float lmin = smin + link->bmin*s; + const float lmax = smin + link->bmax*s; + left[2] = max(left[2],lmin); + left[2] = min(left[2],lmax); + right[2] = max(right[2],lmin); + right[2] = min(right[2],lmax); } else if (link->side == 1 || link->side == 3) { - left[0] = max(left[0],link->bmin); - left[0] = min(left[0],link->bmax); - right[0] = max(right[0],link->bmin); - right[0] = min(right[0],link->bmax); + // Unpack portal limits. + const float smin = min(left[0],right[0]); + const float smax = max(left[0],right[0]); + const float s = (smax-smin) / 255.0f; + const float lmin = smin + link->bmin*s; + const float lmax = smin + link->bmax*s; + left[0] = max(left[0],lmin); + left[0] = min(left[0],lmax); + right[0] = max(right[0],lmin); + right[0] = min(right[0],lmax); } return true; } @@ -1440,8 +1008,8 @@ int dtTiledNavMesh::raycast(dtTilePolyRef centerRef, const float* startPos, cons // Cast ray against current polygon. // The API input has been cheked already, skip checking internal data. - int salt, it, ip; - decodeId(curRef, salt, it, ip); + unsigned int salt, it, ip; + dtDecodeTileId(curRef, salt, it, ip); const dtTileHeader* h = m_tiles[it].header; const dtTilePoly* poly = &h->polys[ip]; @@ -1524,8 +1092,7 @@ int dtTiledNavMesh::raycast(dtTilePolyRef centerRef, const float* startPos, cons } int dtTiledNavMesh::findPolysAround(dtTilePolyRef centerRef, const float* centerPos, float radius, - dtTilePolyRef* resultRef, dtTilePolyRef* resultParent, - float* resultCost, unsigned short* resultDepth, + dtTilePolyRef* resultRef, dtTilePolyRef* resultParent, float* resultCost, const int maxResult) { if (!centerRef) return 0; @@ -1535,12 +1102,12 @@ int dtTiledNavMesh::findPolysAround(dtTilePolyRef centerRef, const float* center m_nodePool->clear(); m_openList->clear(); - dtTileNode* startNode = m_nodePool->getNode(centerRef); - startNode->parent = 0; + dtNode* startNode = m_nodePool->getNode(centerRef); + startNode->pidx = 0; startNode->cost = 0; startNode->total = 0; startNode->id = centerRef; - startNode->flags = dtTileNode::OPEN; + startNode->flags = DT_NODE_OPEN; m_openList->push(startNode); int n = 0; @@ -1552,8 +1119,6 @@ int dtTiledNavMesh::findPolysAround(dtTilePolyRef centerRef, const float* center resultParent[n] = 0; if (resultCost) resultCost[n] = 0; - if (resultDepth) - resultDepth[n] = 0; ++n; } @@ -1561,11 +1126,11 @@ int dtTiledNavMesh::findPolysAround(dtTilePolyRef centerRef, const float* center while (!m_openList->empty()) { - dtTileNode* bestNode = m_openList->pop(); + dtNode* bestNode = m_openList->pop(); // Get poly and tile. - int salt, it, ip; - decodeId(bestNode->id, salt, it, ip); + unsigned int salt, it, ip; + dtDecodeTileId(bestNode->id, salt, it, ip); // The API input has been cheked already, skip checking internal data. const dtTileHeader* h = m_tiles[it].header; const dtTilePoly* poly = &h->polys[ip]; @@ -1577,7 +1142,7 @@ int dtTiledNavMesh::findPolysAround(dtTilePolyRef centerRef, const float* center if (neighbour) { // Skip parent node. - if (bestNode->parent && bestNode->parent->id == neighbour) + if (bestNode->pidx && m_nodePool->getNodeAtIdx(bestNode->pidx)->id == neighbour) continue; // Calc distance to the edge. @@ -1590,25 +1155,27 @@ int dtTiledNavMesh::findPolysAround(dtTilePolyRef centerRef, const float* center if (distSqr > radiusSqr) continue; - dtTileNode newNode; - newNode.parent = bestNode; + dtNode* parent = bestNode; + dtNode newNode; + newNode.pidx = m_nodePool->getNodeIdx(parent); newNode.id = neighbour; - newNode.cost = bestNode->cost + 1; // Depth - newNode.total = bestNode->total + getCost(newNode.parent->parent ? newNode.parent->parent->id : 0, newNode.parent->id, newNode.id); - dtTileNode* actualNode = m_nodePool->getNode(newNode.id); + dtTilePolyRef prevRef = parent->pidx ? m_nodePool->getNodeAtIdx(parent->pidx)->id : 0; + newNode.total = parent->total + getCost(prevRef, parent->id, newNode.id); + + dtNode* actualNode = m_nodePool->getNode(newNode.id); if (!actualNode) continue; - if (!((actualNode->flags & dtTileNode::OPEN) && newNode.total > actualNode->total) && - !((actualNode->flags & dtTileNode::CLOSED) && newNode.total > actualNode->total)) + if (!((actualNode->flags & DT_NODE_OPEN) && newNode.total > actualNode->total) && + !((actualNode->flags & DT_NODE_CLOSED) && newNode.total > actualNode->total)) { - actualNode->flags &= ~dtTileNode::CLOSED; - actualNode->parent = newNode.parent; + actualNode->flags &= ~DT_NODE_CLOSED; + actualNode->pidx = newNode.pidx; actualNode->cost = newNode.cost; actualNode->total = newNode.total; - if (actualNode->flags & dtTileNode::OPEN) + if (actualNode->flags & DT_NODE_OPEN) { m_openList->modify(actualNode); } @@ -1619,14 +1186,12 @@ int dtTiledNavMesh::findPolysAround(dtTilePolyRef centerRef, const float* center if (resultRef) resultRef[n] = actualNode->id; if (resultParent) - resultParent[n] = actualNode->parent->id; + resultParent[n] = m_nodePool->getNodeAtIdx(actualNode->pidx)->id; if (resultCost) resultCost[n] = actualNode->total; - if (resultDepth) - resultDepth[n] = (unsigned short)actualNode->cost; ++n; } - actualNode->flags = dtTileNode::OPEN; + actualNode->flags = DT_NODE_OPEN; m_openList->push(actualNode); } } @@ -1647,23 +1212,23 @@ float dtTiledNavMesh::findDistanceToWall(dtTilePolyRef centerRef, const float* c m_nodePool->clear(); m_openList->clear(); - dtTileNode* startNode = m_nodePool->getNode(centerRef); - startNode->parent = 0; + dtNode* startNode = m_nodePool->getNode(centerRef); + startNode->pidx = 0; startNode->cost = 0; startNode->total = 0; startNode->id = centerRef; - startNode->flags = dtTileNode::OPEN; + startNode->flags = DT_NODE_OPEN; m_openList->push(startNode); float radiusSqr = sqr(maxRadius); while (!m_openList->empty()) { - dtTileNode* bestNode = m_openList->pop(); + dtNode* bestNode = m_openList->pop(); // Get poly and tile. - int salt, it, ip; - decodeId(bestNode->id, salt, it, ip); + unsigned int salt, it, ip; + dtDecodeTileId(bestNode->id, salt, it, ip); // The API input has been cheked already, skip checking internal data. const dtTileHeader* h = m_tiles[it].header; const dtTilePoly* poly = &h->polys[ip]; @@ -1718,7 +1283,7 @@ float dtTiledNavMesh::findDistanceToWall(dtTilePolyRef centerRef, const float* c if (neighbour) { // Skip parent node. - if (bestNode->parent && bestNode->parent->id == neighbour) + if (bestNode->pidx && m_nodePool->getNodeAtIdx(bestNode->pidx)->id == neighbour) continue; // Calc distance to the edge. @@ -1731,31 +1296,33 @@ float dtTiledNavMesh::findDistanceToWall(dtTilePolyRef centerRef, const float* c if (distSqr > radiusSqr) continue; - dtTileNode newNode; - newNode.parent = bestNode; + dtNode* parent = bestNode; + dtNode newNode; + newNode.pidx = m_nodePool->getNodeIdx(parent); newNode.id = neighbour; - newNode.cost = bestNode->cost + 1; // Depth - newNode.total = bestNode->total + getCost(newNode.parent->parent ? newNode.parent->parent->id : 0, newNode.parent->id, newNode.id); - dtTileNode* actualNode = m_nodePool->getNode(newNode.id); + dtTilePolyRef prevRef = parent->pidx ? m_nodePool->getNodeAtIdx(parent->pidx)->id : 0; + newNode.total = parent->total + getCost(prevRef, parent->id, newNode.id); + + dtNode* actualNode = m_nodePool->getNode(newNode.id); if (!actualNode) continue; - if (!((actualNode->flags & dtTileNode::OPEN) && newNode.total > actualNode->total) && - !((actualNode->flags & dtTileNode::CLOSED) && newNode.total > actualNode->total)) + if (!((actualNode->flags & DT_NODE_OPEN) && newNode.total > actualNode->total) && + !((actualNode->flags & DT_NODE_CLOSED) && newNode.total > actualNode->total)) { - actualNode->flags &= ~dtTileNode::CLOSED; - actualNode->parent = newNode.parent; + actualNode->flags &= ~DT_NODE_CLOSED; + actualNode->pidx = newNode.pidx; actualNode->cost = newNode.cost; actualNode->total = newNode.total; - if (actualNode->flags & dtTileNode::OPEN) + if (actualNode->flags & DT_NODE_OPEN) { m_openList->modify(actualNode); } else { - actualNode->flags = dtTileNode::OPEN; + actualNode->flags = DT_NODE_OPEN; m_openList->push(actualNode); } } @@ -1770,3 +1337,32 @@ float dtTiledNavMesh::findDistanceToWall(dtTilePolyRef centerRef, const float* c return sqrtf(radiusSqr); } +const dtTilePoly* dtTiledNavMesh::getPolyByRef(dtTilePolyRef ref) const +{ + unsigned int salt, it, ip; + dtDecodeTileId(ref, salt, it, ip); + if (it >= DT_MAX_TILES) return 0; + if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return 0; + if (ip >= m_tiles[it].header->npolys) return 0; + return &m_tiles[it].header->polys[ip]; +} + +const float* dtTiledNavMesh::getPolyVertsByRef(dtTilePolyRef ref) const +{ + unsigned int salt, it, ip; + dtDecodeTileId(ref, salt, it, ip); + if (it >= DT_MAX_TILES) return 0; + if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return 0; + if (ip >= m_tiles[it].header->npolys) return 0; + return m_tiles[it].header->verts; +} + +const dtTileLink* dtTiledNavMesh::getPolyLinksByRef(dtTilePolyRef ref) const +{ + unsigned int salt, it, ip; + dtDecodeTileId(ref, salt, it, ip); + if (it >= DT_MAX_TILES) return 0; + if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return 0; + if (ip >= m_tiles[it].header->npolys) return 0; + return m_tiles[it].header->links; +} diff --git a/Detour/Source/DetourTiledNavMeshBuilder.cpp b/Detour/Source/DetourTileNavMeshBuilder.cpp similarity index 74% rename from Detour/Source/DetourTiledNavMeshBuilder.cpp rename to Detour/Source/DetourTileNavMeshBuilder.cpp index 7298991..273ec90 100644 --- a/Detour/Source/DetourTiledNavMeshBuilder.cpp +++ b/Detour/Source/DetourTileNavMeshBuilder.cpp @@ -20,11 +20,8 @@ #include #include #include -#include "DetourTiledNavMesh.h" - -template inline T abs(T a) { return a < 0 ? -a : a; } -template inline T min(T a, T b) { return a < b ? a : b; } -template inline T max(T a, T b) { return a > b ? a : b; } +#include "DetourTileNavMesh.h" +#include "DetourCommon.h" bool dtCreateNavMeshTileData(const unsigned short* verts, const int nverts, const unsigned short* polys, const int npolys, const int nvp, @@ -41,33 +38,7 @@ bool dtCreateNavMeshTileData(const unsigned short* verts, const int nverts, if (!npolys) return false; -/* int nportals[4] = {0,0,0,0}; - - // Find portals. - for (int i = 0; i < npolys; ++i) - { - const unsigned short* p = &polys[i*2*nvp]; - for (int j = 0; j < nvp; ++j) - { - if (p[j] == 0xffff) break; - int nj = j+1; - if (nj >= nvp || p[nj] == 0xffff) nj = 0; - const unsigned short* va = &verts[p[j]*3]; - const unsigned short* vb = &verts[p[nj]*3]; - - if (va[0] == tileSize && vb[0] == tileSize) - nportals[0]++; // x+ - else if (va[2] == tileSize && vb[2] == tileSize) - nportals[1]++; // z+ - else if (va[0] == 0 && vb[0] == 0) - nportals[2]++; // x- - else if (va[2] == 0 && vb[2] == 0) - nportals[3]++; // z- - } - } -*/ - - // Find portals. + // Find portal edges which are at tile borders. int nedges = 0; int nportals = 0; for (int i = 0; i < npolys; ++i) @@ -98,8 +69,6 @@ bool dtCreateNavMeshTileData(const unsigned short* verts, const int nverts, const int headerSize = sizeof(dtTileHeader); const int vertsSize = sizeof(float)*3*nverts; const int polysSize = sizeof(dtTilePoly)*npolys; -// const int portalsSize = sizeof(dtTilePortal)*(nportals[0]+nportals[1]+nportals[2]+nportals[3]); -// const int dataSize = headerSize + vertsSize + polysSize + portalsSize; const int linksSize = sizeof(dtTileLink)*(nedges + nportals*2); const int dataSize = headerSize + vertsSize + polysSize + linksSize; unsigned char* data = new unsigned char[dataSize]; @@ -110,11 +79,6 @@ bool dtCreateNavMeshTileData(const unsigned short* verts, const int nverts, dtTileHeader* header = (dtTileHeader*)(data); float* navVerts = (float*)(data + headerSize); dtTilePoly* navPolys = (dtTilePoly*)(data + headerSize + vertsSize); -/* dtTilePortal* portals[4]; - portals[0] = (dtTilePortal*)(data + headerSize + vertsSize + polysSize); - portals[1] = portals[0] + nportals[0]; - portals[2] = portals[1] + nportals[1]; - portals[3] = portals[2] + nportals[2];*/ // Store header header->magic = DT_TILE_NAVMESH_MAGIC; @@ -122,10 +86,6 @@ bool dtCreateNavMeshTileData(const unsigned short* verts, const int nverts, header->npolys = npolys; header->nverts = nverts; header->maxlinks = nedges + nportals*2; -/* header->nportals[0] = nportals[0]; - header->nportals[1] = nportals[1]; - header->nportals[2] = nportals[2]; - header->nportals[3] = nportals[3];*/ header->cs = cs; header->bmin[0] = bmin[0]; header->bmin[1] = bmin[1]; @@ -160,7 +120,7 @@ bool dtCreateNavMeshTileData(const unsigned short* verts, const int nverts, src += nvp*2; } - // Store portals. + // Store portal edges. for (int i = 0; i < npolys; ++i) { dtTilePoly* poly = &navPolys[i];