diff --git a/recast/debug_utils/include/DebugDraw.h b/recast/debug_utils/include/DebugDraw.h index b24094fb2..0b8c59352 100644 --- a/recast/debug_utils/include/DebugDraw.h +++ b/recast/debug_utils/include/DebugDraw.h @@ -210,6 +210,10 @@ public: virtual void end(); void clear(); void draw(struct duDebugDraw* dd); +private: + // Explicitly disabled copy constructor and copy assignment operator. + duDisplayList(const duDisplayList&); + duDisplayList& operator=(const duDisplayList&); }; diff --git a/recast/debug_utils/include/DetourDebugDraw.h b/recast/debug_utils/include/DetourDebugDraw.h index f997756d6..ff2ca2f9d 100644 --- a/recast/debug_utils/include/DetourDebugDraw.h +++ b/recast/debug_utils/include/DetourDebugDraw.h @@ -31,7 +31,6 @@ enum DrawNavMeshFlags }; void duDebugDrawNavMesh(struct duDebugDraw* dd, const dtNavMesh& mesh, unsigned char flags); -void duDebugDrawNavMeshTile(struct duDebugDraw* dd, const dtNavMesh& mesh, int w, int h, int layer, unsigned char flags); void duDebugDrawNavMeshWithClosedList(struct duDebugDraw* dd, const dtNavMesh& mesh, const dtNavMeshQuery& query, unsigned char flags); void duDebugDrawNavMeshNodes(struct duDebugDraw* dd, const dtNavMeshQuery& query); void duDebugDrawNavMeshBVTree(struct duDebugDraw* dd, const dtNavMesh& mesh); diff --git a/recast/debug_utils/src/DetourDebugDraw.cpp b/recast/debug_utils/src/DetourDebugDraw.cpp index dc9112908..e2db1078f 100644 --- a/recast/debug_utils/src/DetourDebugDraw.cpp +++ b/recast/debug_utils/src/DetourDebugDraw.cpp @@ -250,19 +250,6 @@ void duDebugDrawNavMesh(duDebugDraw* dd, const dtNavMesh& mesh, unsigned char fl } } -void duDebugDrawNavMeshTile(struct duDebugDraw* dd, const dtNavMesh& mesh, int w, int h, int layer, unsigned char flags) { - const dtMeshTile* tile = mesh.getTileAt(w, h, layer); - if (!tile) { - return; - } - - if (!tile->header) { - return; - } - - drawMeshTile(dd, mesh, 0, tile, flags); -} - void duDebugDrawNavMeshWithClosedList(struct duDebugDraw* dd, const dtNavMesh& mesh, const dtNavMeshQuery& query, unsigned char flags) { if (!dd) return; diff --git a/recast/detour/include/DetourAlloc.h b/recast/detour/include/DetourAlloc.h index e814b62a7..f87b454ac 100644 --- a/recast/detour/include/DetourAlloc.h +++ b/recast/detour/include/DetourAlloc.h @@ -19,6 +19,8 @@ #ifndef DETOURALLOCATOR_H #define DETOURALLOCATOR_H +#include + /// Provides hint values to the memory allocator on how long the /// memory is expected to be used. enum dtAllocHint @@ -32,7 +34,7 @@ enum dtAllocHint // @param[in] rcAllocHint A hint to the allocator on how long the memory is expected to be in use. // @return A pointer to the beginning of the allocated memory block, or null if the allocation failed. /// @see dtAllocSetCustom -typedef void* (dtAllocFunc)(int size, dtAllocHint hint); +typedef void* (dtAllocFunc)(size_t size, dtAllocHint hint); /// A memory deallocation function. /// @param[in] ptr A pointer to a memory block previously allocated using #dtAllocFunc. @@ -49,7 +51,7 @@ void dtAllocSetCustom(dtAllocFunc *allocFunc, dtFreeFunc *freeFunc); /// @param[in] hint A hint to the allocator on how long the memory is expected to be in use. /// @return A pointer to the beginning of the allocated memory block, or null if the allocation failed. /// @see dtFree -void* dtAlloc(int size, dtAllocHint hint); +void* dtAlloc(size_t size, dtAllocHint hint); /// Deallocates a memory block. /// @param[in] ptr A pointer to a memory block previously allocated using #dtAlloc. diff --git a/recast/detour/include/DetourNavMesh.h b/recast/detour/include/DetourNavMesh.h index 1060845f1..8ecd57e46 100644 --- a/recast/detour/include/DetourNavMesh.h +++ b/recast/detour/include/DetourNavMesh.h @@ -118,11 +118,10 @@ enum dtStraightPathOptions }; -/// Options for dtNavMeshQuery::findPath +/// Options for dtNavMeshQuery::initSlicedFindPath and updateSlicedFindPath enum dtFindPathOptions { - DT_FINDPATH_LOW_QUALITY_FAR = 0x01, ///< [provisional] trade quality for performance far from the origin. The idea is that by then a new query will be issued - DT_FINDPATH_ANY_ANGLE = 0x02, ///< use raycasts during pathfind to "shortcut" (raycast still consider costs) + DT_FINDPATH_ANY_ANGLE = 0x02, ///< use raycasts during pathfind to "shortcut" (raycast still consider costs) }; /// Options for dtNavMeshQuery::raycast @@ -146,7 +145,7 @@ enum dtPolyTypes }; -/// Defines a polyogn within a dtMeshTile object. +/// Defines a polygon within a dtMeshTile object. /// @ingroup detour struct dtPoly { @@ -301,6 +300,9 @@ struct dtMeshTile int dataSize; ///< Size of the tile data. int flags; ///< Tile flags. (See: #dtTileFlags) dtMeshTile* next; ///< The next free tile, or the next tile in the spatial grid. +private: + dtMeshTile(const dtMeshTile&); + dtMeshTile& operator=(const dtMeshTile&); }; /// Configuration parameters used to define multi-tile navigation meshes. @@ -592,6 +594,9 @@ public: /// @} private: + // Explicitly disabled copy constructor and copy assignment operator. + dtNavMesh(const dtNavMesh&); + dtNavMesh& operator=(const dtNavMesh&); /// Returns pointer to tile in the tile array. dtMeshTile* getTile(int i); @@ -620,7 +625,7 @@ private: void connectExtOffMeshLinks(dtMeshTile* tile, dtMeshTile* target, int side); /// Removes external links at specified side. - void unconnectExtLinks(dtMeshTile* tile, dtMeshTile* target); + void unconnectLinks(dtMeshTile* tile, dtMeshTile* target); // TODO: These methods are duplicates from dtNavMeshQuery, but are needed for off-mesh connection finding. diff --git a/recast/detour/include/DetourNavMeshQuery.h b/recast/detour/include/DetourNavMeshQuery.h index c7b360dcd..d3737a49e 100644 --- a/recast/detour/include/DetourNavMeshQuery.h +++ b/recast/detour/include/DetourNavMeshQuery.h @@ -131,6 +131,9 @@ struct dtRaycastHit /// hitNormal The normal of the nearest wall hit. [(x, y, z)] float hitNormal[3]; + + /// The index of the edge on the final polygon where the wall was hit. + int hitEdgeIndex; /// Pointer to an array of reference ids of the visited polygons. [opt] dtPolyRef* path; @@ -472,6 +475,9 @@ public: /// @} private: + // Explicitly disabled copy constructor and copy assignment operator + dtNavMeshQuery(const dtNavMeshQuery&); + dtNavMeshQuery& operator=(const dtNavMeshQuery&); /// Returns neighbour tile based on side. dtMeshTile* getNeighbourTileAt(int x, int y, int side) const; diff --git a/recast/detour/include/DetourNode.h b/recast/detour/include/DetourNode.h index 6fefdc8e0..1494e1e89 100644 --- a/recast/detour/include/DetourNode.h +++ b/recast/detour/include/DetourNode.h @@ -42,17 +42,13 @@ struct dtNode dtPolyRef id; ///< Polygon ref the node corresponds to. }; - static const int DT_MAX_STATES_PER_NODE = 4; // number of extra states per node. See dtNode::state - - class dtNodePool { public: dtNodePool(int maxNodes, int hashSize); ~dtNodePool(); - inline void operator=(const dtNodePool&) {} void clear(); // Get a dtNode by ref and extra state information. If there is none then - allocate @@ -64,19 +60,19 @@ public: inline unsigned int getNodeIdx(const dtNode* node) const { if (!node) return 0; - return (unsigned int)(node - m_nodes)+1; + return (unsigned int)(node - m_nodes) + 1; } inline dtNode* getNodeAtIdx(unsigned int idx) { if (!idx) return 0; - return &m_nodes[idx-1]; + return &m_nodes[idx - 1]; } inline const dtNode* getNodeAtIdx(unsigned int idx) const { if (!idx) return 0; - return &m_nodes[idx-1]; + return &m_nodes[idx - 1]; } inline int getMemUsed() const @@ -95,6 +91,9 @@ public: inline int getNodeCount() const { return m_nodeCount; } private: + // Explicitly disabled copy constructor and copy assignment operator. + dtNodePool(const dtNodePool&); + dtNodePool& operator=(const dtNodePool&); dtNode* m_nodes; dtNodeIndex* m_first; @@ -109,17 +108,10 @@ class dtNodeQueue public: dtNodeQueue(int n); ~dtNodeQueue(); - inline void operator=(dtNodeQueue&) {} - inline void clear() - { - m_size = 0; - } + inline void clear() { m_size = 0; } - inline dtNode* top() - { - return m_heap[0]; - } + inline dtNode* top() { return m_heap[0]; } inline dtNode* pop() { @@ -152,12 +144,16 @@ public: inline int getMemUsed() const { return sizeof(*this) + - sizeof(dtNode*)*(m_capacity+1); + sizeof(dtNode*) * (m_capacity + 1); } inline int getCapacity() const { return m_capacity; } private: + // Explicitly disabled copy constructor and copy assignment operator. + dtNodeQueue(const dtNodeQueue&); + dtNodeQueue& operator=(const dtNodeQueue&); + void bubbleUp(int i, dtNode* node); void trickleDown(int i, dtNode* node); diff --git a/recast/detour/src/DetourAlloc.cpp b/recast/detour/src/DetourAlloc.cpp index 5f671df5b..d9ad1fc01 100644 --- a/recast/detour/src/DetourAlloc.cpp +++ b/recast/detour/src/DetourAlloc.cpp @@ -19,7 +19,7 @@ #include #include "DetourAlloc.h" -static void *dtAllocDefault(int size, dtAllocHint) +static void *dtAllocDefault(size_t size, dtAllocHint) { return malloc(size); } @@ -38,7 +38,7 @@ void dtAllocSetCustom(dtAllocFunc *allocFunc, dtFreeFunc *freeFunc) sFreeFunc = freeFunc ? freeFunc : dtFreeDefault; } -void* dtAlloc(int size, dtAllocHint hint) +void* dtAlloc(size_t size, dtAllocHint hint) { return sAllocFunc(size, hint); } diff --git a/recast/detour/src/DetourNavMesh.cpp b/recast/detour/src/DetourNavMesh.cpp index d353d08a3..bb6c9e4b7 100644 --- a/recast/detour/src/DetourNavMesh.cpp +++ b/recast/detour/src/DetourNavMesh.cpp @@ -350,7 +350,7 @@ int dtNavMesh::findConnectingPolys(const float* va, const float* vb, return n; } -void dtNavMesh::unconnectExtLinks(dtMeshTile* tile, dtMeshTile* target) +void dtNavMesh::unconnectLinks(dtMeshTile* tile, dtMeshTile* target) { if (!tile || !target) return; @@ -363,10 +363,9 @@ void dtNavMesh::unconnectExtLinks(dtMeshTile* tile, dtMeshTile* target) unsigned int pj = DT_NULL_LINK; while (j != DT_NULL_LINK) { - if (tile->links[j].side != 0xff && - decodePolyIdTile(tile->links[j].ref) == targetNum) + if (decodePolyIdTile(tile->links[j].ref) == targetNum) { - // Revove link. + // Remove link. unsigned int nj = tile->links[j].next; if (pj == DT_NULL_LINK) poly->firstLink = nj; @@ -838,6 +837,11 @@ int dtNavMesh::queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, co /// tile will be restored to the same values they were before the tile was /// removed. /// +/// The nav mesh assumes exclusive access to the data passed and will make +/// changes to the dynamic portion of the data. For that reason the data +/// should not be reused in other nav meshes until the tile has been successfully +/// removed from this nav mesh. +/// /// @see dtCreateNavMeshData, #removeTile dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags, dtTileRef lastRef, dtTileRef* result) @@ -1192,25 +1196,24 @@ dtStatus dtNavMesh::removeTile(dtTileRef ref, unsigned char** data, int* dataSiz } // Remove connections to neighbour tiles. - // Create connections with neighbour tiles. static const int MAX_NEIS = 32; dtMeshTile* neis[MAX_NEIS]; int nneis; - // Connect with layers in current tile. + // Disconnect from other layers in current tile. nneis = getTilesAt(tile->header->x, tile->header->y, neis, MAX_NEIS); for (int j = 0; j < nneis; ++j) { if (neis[j] == tile) continue; - unconnectExtLinks(neis[j], tile); + unconnectLinks(neis[j], tile); } - // Connect with neighbour tiles. + // Disconnect from neighbour tiles. for (int i = 0; i < 8; ++i) { nneis = getNeighbourTilesAt(tile->header->x, tile->header->y, i, neis, MAX_NEIS); for (int j = 0; j < nneis; ++j) - unconnectExtLinks(neis[j], tile); + unconnectLinks(neis[j], tile); } // Reset tile. diff --git a/recast/detour/src/DetourNavMeshQuery.cpp b/recast/detour/src/DetourNavMeshQuery.cpp index 5fbc83eab..48f9c3675 100644 --- a/recast/detour/src/DetourNavMeshQuery.cpp +++ b/recast/detour/src/DetourNavMeshQuery.cpp @@ -712,7 +712,8 @@ dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* exten { dtAssert(m_nav); - *nearestRef = 0; + if (!nearestRef) + return DT_FAILURE | DT_INVALID_PARAM; // Get nearby polygons from proximity grid. const int MAX_SEARCH = 128; @@ -721,8 +722,15 @@ dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* exten if (dtStatusFailed(queryPolygons(center, extents, filter, polys, &polyCount, MAX_SEARCH))) return DT_FAILURE | DT_INVALID_PARAM; + *nearestRef = 0; + + if (polyCount == 0) + return DT_SUCCESS; + // Find nearest polygon amongst the nearby polygons. dtPolyRef nearest = 0; + float nearestPoint[3]; + float nearestDistanceSqr = FLT_MAX; for (int i = 0; i < polyCount; ++i) { @@ -751,15 +759,17 @@ dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* exten if (d < nearestDistanceSqr) { - if (nearestPt) - dtVcopy(nearestPt, closestPtPoly); + dtVcopy(nearestPoint, closestPtPoly); + nearestDistanceSqr = d; nearest = ref; } } - if (nearestRef) - *nearestRef = nearest; + *nearestRef = nearest; + + if (nearestPt) + dtVcopy(nearestPt, nearestPoint); return DT_SUCCESS; } @@ -2420,6 +2430,9 @@ dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, cons hit->pathCount = n; return status; } + + hit->hitEdgeIndex = segMax; + // Keep track of furthest t so far. if (tmax > hit->t) hit->t = tmax; diff --git a/recast/detour_tile_cache/include/DetourTileCache.h b/recast/detour_tile_cache/include/DetourTileCache.h index e2e3f42fc..9c7e01c35 100644 --- a/recast/detour_tile_cache/include/DetourTileCache.h +++ b/recast/detour_tile_cache/include/DetourTileCache.h @@ -164,7 +164,10 @@ public: private: - + // Explicitly disabled copy constructor and copy assignment operator. + dtTileCache(const dtTileCache&); + dtTileCache& operator=(const dtTileCache&); + enum ObstacleRequestAction { REQUEST_ADD, @@ -203,7 +206,6 @@ private: static const int MAX_UPDATE = 64; dtCompressedTileRef m_update[MAX_UPDATE]; int m_nupdate; - }; dtTileCache* dtAllocTileCache(); diff --git a/recast/detour_tile_cache/include/DetourTileCacheBuilder.h b/recast/detour_tile_cache/include/DetourTileCacheBuilder.h index 938d41532..854183c91 100644 --- a/recast/detour_tile_cache/include/DetourTileCacheBuilder.h +++ b/recast/detour_tile_cache/include/DetourTileCacheBuilder.h @@ -78,13 +78,11 @@ struct dtTileCachePolyMesh struct dtTileCacheAlloc { - virtual ~dtTileCacheAlloc() { } + virtual ~dtTileCacheAlloc() {} - virtual void reset() - { - } + virtual void reset() {} - virtual void* alloc(const int size) + virtual void* alloc(const size_t size) { return dtAlloc(size, DT_ALLOC_TEMP); } diff --git a/recast/detour_tile_cache/src/DetourTileCache.cpp b/recast/detour_tile_cache/src/DetourTileCache.cpp index 9d9e7ddd8..9d8fac2ce 100644 --- a/recast/detour_tile_cache/src/DetourTileCache.cpp +++ b/recast/detour_tile_cache/src/DetourTileCache.cpp @@ -40,10 +40,10 @@ inline int computeTileHash(int x, int y, const int mask) } -struct BuildContext +struct NavMeshTileBuildContext { - inline BuildContext(struct dtTileCacheAlloc* a) : layer(0), lcset(0), lmesh(0), alloc(a) {} - inline ~BuildContext() { purge(); } + inline NavMeshTileBuildContext(struct dtTileCacheAlloc* a) : layer(0), lcset(0), lmesh(0), alloc(a) {} + inline ~NavMeshTileBuildContext() { purge(); } void purge() { dtFreeTileCacheLayer(alloc, layer); @@ -350,7 +350,7 @@ dtStatus dtTileCache::removeTile(dtCompressedTileRef ref, unsigned char** data, } -dtObstacleRef dtTileCache::addObstacle(const float* pos, const float radius, const float height, dtObstacleRef* result) +dtStatus dtTileCache::addObstacle(const float* pos, const float radius, const float height, dtObstacleRef* result) { if (m_nreqs >= MAX_REQUESTS) return DT_FAILURE | DT_BUFFER_TOO_SMALL; @@ -384,7 +384,7 @@ dtObstacleRef dtTileCache::addObstacle(const float* pos, const float radius, con return DT_SUCCESS; } -dtObstacleRef dtTileCache::removeObstacle(const dtObstacleRef ref) +dtStatus dtTileCache::removeObstacle(const dtObstacleRef ref) { if (!ref) return DT_SUCCESS; @@ -587,7 +587,7 @@ dtStatus dtTileCache::buildNavMeshTile(const dtCompressedTileRef ref, dtNavMesh* m_talloc->reset(); - BuildContext bc(m_talloc); + NavMeshTileBuildContext bc(m_talloc); const int walkableClimbVx = (int)(m_params.walkableClimb / m_params.ch); dtStatus status; @@ -631,7 +631,11 @@ dtStatus dtTileCache::buildNavMeshTile(const dtCompressedTileRef ref, dtNavMesh* // Early out if the mesh tile is empty. if (!bc.lmesh->npolys) + { + // Remove existing tile. + navmesh->removeTile(navmesh->getTileRefAt(tile->header->tx,tile->header->ty,tile->header->tlayer),0,0); return DT_SUCCESS; + } dtNavMeshCreateParams params; memset(¶ms, 0, sizeof(params)); diff --git a/recast/recast/include/Recast.h b/recast/recast/include/Recast.h index d8bdde2f9..2adcdcb34 100644 --- a/recast/recast/include/Recast.h +++ b/recast/recast/include/Recast.h @@ -127,7 +127,7 @@ public: inline void resetTimers() { if (m_timerEnabled) doResetTimers(); } /// Starts the specified performance timer. - /// @param label The category of timer. + /// @param label The category of the timer. inline void startTimer(const rcTimerLabel label) { if (m_timerEnabled) doStartTimer(label); } /// Stops the specified performance timer. @@ -173,6 +173,26 @@ protected: bool m_timerEnabled; }; +/// A helper to first start a timer and then stop it when this helper goes out of scope. +/// @see rcContext +class rcScopedTimer +{ +public: + /// Constructs an instance and starts the timer. + /// @param[in] ctx The context to use. + /// @param[in] label The category of the timer. + inline rcScopedTimer(rcContext* ctx, const rcTimerLabel label) : m_ctx(ctx), m_label(label) { m_ctx->startTimer(m_label); } + inline ~rcScopedTimer() { m_ctx->stopTimer(m_label); } + +private: + // Explicitly disabled copy constructor and copy assignment operator. + rcScopedTimer(const rcScopedTimer&); + rcScopedTimer& operator=(const rcScopedTimer&); + + rcContext* const m_ctx; + const rcTimerLabel m_label; +}; + /// Specifies a configuration to use when performing Recast builds. /// @ingroup recast struct rcConfig @@ -245,7 +265,7 @@ struct rcConfig /// Defines the number of bits allocated to rcSpan::smin and rcSpan::smax. static const int RC_SPAN_HEIGHT_BITS = 13; /// Defines the maximum value for rcSpan::smin and rcSpan::smax. -static const int RC_SPAN_MAX_HEIGHT = (1< 0] [Units: wu] /// @param[in] ch The y-axis cell size to use for field. [Limit: > 0] [Units: wu] +/// @returns True if the operation completed successfully. bool rcCreateHeightfield(rcContext* ctx, rcHeightfield& hf, int width, int height, const float* bmin, const float* bmax, float cs, float ch); @@ -790,7 +821,8 @@ void rcClearUnwalkableTriangles(rcContext* ctx, const float walkableSlopeAngle, /// @param[in] smax The maximum height of the span. [Limit: <= #RC_SPAN_MAX_HEIGHT] [Units: vx] /// @param[in] area The area id of the span. [Limit: <= #RC_WALKABLE_AREA) /// @param[in] flagMergeThr The merge theshold. [Limit: >= 0] [Units: vx] -void rcAddSpan(rcContext* ctx, rcHeightfield& hf, const int x, const int y, +/// @returns True if the operation completed successfully. +bool rcAddSpan(rcContext* ctx, rcHeightfield& hf, const int x, const int y, const unsigned short smin, const unsigned short smax, const unsigned char area, const int flagMergeThr); @@ -804,7 +836,8 @@ void rcAddSpan(rcContext* ctx, rcHeightfield& hf, const int x, const int y, /// @param[in,out] solid An initialized heightfield. /// @param[in] flagMergeThr The distance where the walkable flag is favored over the non-walkable flag. /// [Limit: >= 0] [Units: vx] -void rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const float* v2, +/// @returns True if the operation completed successfully. +bool rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const float* v2, const unsigned char area, rcHeightfield& solid, const int flagMergeThr = 1); @@ -819,7 +852,8 @@ void rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const /// @param[in,out] solid An initialized heightfield. /// @param[in] flagMergeThr The distance where the walkable flag is favored over the non-walkable flag. /// [Limit: >= 0] [Units: vx] -void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int nv, +/// @returns True if the operation completed successfully. +bool rcRasterizeTriangles(rcContext* ctx, const float* verts, const int nv, const int* tris, const unsigned char* areas, const int nt, rcHeightfield& solid, const int flagMergeThr = 1); @@ -834,7 +868,8 @@ void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int nv, /// @param[in,out] solid An initialized heightfield. /// @param[in] flagMergeThr The distance where the walkable flag is favored over the non-walkable flag. /// [Limit: >= 0] [Units: vx] -void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int nv, +/// @returns True if the operation completed successfully. +bool rcRasterizeTriangles(rcContext* ctx, const float* verts, const int nv, const unsigned short* tris, const unsigned char* areas, const int nt, rcHeightfield& solid, const int flagMergeThr = 1); @@ -847,7 +882,8 @@ void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int nv, /// @param[in,out] solid An initialized heightfield. /// @param[in] flagMergeThr The distance where the walkable flag is favored over the non-walkable flag. /// [Limit: >= 0] [Units: vx] -void rcRasterizeTriangles(rcContext* ctx, const float* verts, const unsigned char* areas, const int nt, +/// @returns True if the operation completed successfully. +bool rcRasterizeTriangles(rcContext* ctx, const float* verts, const unsigned char* areas, const int nt, rcHeightfield& solid, const int flagMergeThr = 1); /// Marks non-walkable spans as walkable if their maximum is within @p walkableClimp of a walkable neihbor. @@ -1037,7 +1073,7 @@ inline int rcGetCon(const rcCompactSpan& s, int dir) /// in the direction. inline int rcGetDirOffsetX(int dir) { - const int offset[4] = { -1, 0, 1, 0, }; + static const int offset[4] = { -1, 0, 1, 0, }; return offset[dir&0x03]; } @@ -1047,10 +1083,20 @@ inline int rcGetDirOffsetX(int dir) /// in the direction. inline int rcGetDirOffsetY(int dir) { - const int offset[4] = { 0, 1, 0, -1 }; + static const int offset[4] = { 0, 1, 0, -1 }; return offset[dir&0x03]; } +/// Gets the direction for the specified offset. One of x and y should be 0. +/// @param[in] x The x offset. [Limits: -1 <= value <= 1] +/// @param[in] y The y offset. [Limits: -1 <= value <= 1] +/// @return The direction that represents the offset. +inline int rcGetDirForOffset(int x, int y) +{ + static const int dirs[5] = { 3, 0, -1, 2, 1 }; + return dirs[((y+1)<<1)+x]; +} + /// @} /// @name Layer, Contour, Polymesh, and Detail Mesh Functions /// @see rcHeightfieldLayer, rcContourSet, rcPolyMesh, rcPolyMeshDetail diff --git a/recast/recast/include/RecastAlloc.h b/recast/recast/include/RecastAlloc.h index 438be9ea5..f1608fb55 100644 --- a/recast/recast/include/RecastAlloc.h +++ b/recast/recast/include/RecastAlloc.h @@ -19,6 +19,8 @@ #ifndef RECASTALLOC_H #define RECASTALLOC_H +#include + /// Provides hint values to the memory allocator on how long the /// memory is expected to be used. enum rcAllocHint @@ -32,7 +34,7 @@ enum rcAllocHint // @param[in] rcAllocHint A hint to the allocator on how long the memory is expected to be in use. // @return A pointer to the beginning of the allocated memory block, or null if the allocation failed. /// @see rcAllocSetCustom -typedef void* (rcAllocFunc)(int size, rcAllocHint hint); +typedef void* (rcAllocFunc)(size_t size, rcAllocHint hint); /// A memory deallocation function. /// @param[in] ptr A pointer to a memory block previously allocated using #rcAllocFunc. @@ -49,7 +51,7 @@ void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc); /// @param[in] hint A hint to the allocator on how long the memory is expected to be in use. /// @return A pointer to the beginning of the allocated memory block, or null if the allocation failed. /// @see rcFree -void* rcAlloc(int size, rcAllocHint hint); +void* rcAlloc(size_t size, rcAllocHint hint); /// Deallocates a memory block. /// @param[in] ptr A pointer to a memory block previously allocated using #rcAlloc. @@ -62,42 +64,58 @@ class rcIntArray { int* m_data; int m_size, m_cap; - inline rcIntArray(const rcIntArray&); - inline rcIntArray& operator=(const rcIntArray&); -public: + void doResize(int n); + + // Explicitly disabled copy constructor and copy assignment operator. + rcIntArray(const rcIntArray&); + rcIntArray& operator=(const rcIntArray&); + +public: /// Constructs an instance with an initial array size of zero. - inline rcIntArray() : m_data(0), m_size(0), m_cap(0) {} + rcIntArray() : m_data(0), m_size(0), m_cap(0) {} /// Constructs an instance initialized to the specified size. /// @param[in] n The initial size of the integer array. - inline rcIntArray(int n) : m_data(0), m_size(0), m_cap(0) { resize(n); } - inline ~rcIntArray() { rcFree(m_data); } + rcIntArray(int n) : m_data(0), m_size(0), m_cap(0) { resize(n); } + ~rcIntArray() { rcFree(m_data); } /// Specifies the new size of the integer array. /// @param[in] n The new size of the integer array. - void resize(int n); + void resize(int n) + { + if (n > m_cap) + doResize(n); + + m_size = n; + } /// Push the specified integer onto the end of the array and increases the size by one. /// @param[in] item The new value. - inline void push(int item) { resize(m_size+1); m_data[m_size-1] = item; } + void push(int item) { resize(m_size+1); m_data[m_size-1] = item; } /// Returns the value at the end of the array and reduces the size by one. /// @return The value at the end of the array. - inline int pop() { if (m_size > 0) m_size--; return m_data[m_size]; } + int pop() + { + if (m_size > 0) + m_size--; + + return m_data[m_size]; + } /// The value at the specified array index. /// @warning Does not provide overflow protection. /// @param[in] i The index of the value. - inline const int& operator[](int i) const { return m_data[i]; } + const int& operator[](int i) const { return m_data[i]; } /// The value at the specified array index. /// @warning Does not provide overflow protection. /// @param[in] i The index of the value. - inline int& operator[](int i) { return m_data[i]; } + int& operator[](int i) { return m_data[i]; } /// The current size of the integer array. - inline int size() const { return m_size; } + int size() const { return m_size; } }; /// A simple helper class used to delete an array when it goes out of scope. @@ -119,6 +137,11 @@ public: /// The root array pointer. /// @return The root array pointer. inline operator T*() { return ptr; } + +private: + // Explicitly disabled copy constructor and copy assignment operator. + rcScopedDelete(const rcScopedDelete&); + rcScopedDelete& operator=(const rcScopedDelete&); }; #endif diff --git a/recast/recast/src/Recast.cpp b/recast/recast/src/Recast.cpp index 4070721da..46bc8b781 100644 --- a/recast/recast/src/Recast.cpp +++ b/recast/recast/src/Recast.cpp @@ -257,7 +257,7 @@ void rcMarkWalkableTriangles(rcContext* ctx, const float walkableSlopeAngle, for (int i = 0; i < nt; ++i) { - const int* tri = &tris[i*3]; + const int* tri = &tris[i*3]; calcTriNormal(&verts[tri[0]*3], &verts[tri[1]*3], &verts[tri[2]*3], norm); // Check if the face is walkable. if (norm[1] > walkableThr) @@ -329,7 +329,7 @@ bool rcBuildCompactHeightfield(rcContext* ctx, const int walkableHeight, const i { rcAssert(ctx); - ctx->startTimer(RC_TIMER_BUILD_COMPACTHEIGHTFIELD); + rcScopedTimer timer(ctx, RC_TIMER_BUILD_COMPACTHEIGHTFIELD); const int w = hf.width; const int h = hf.height; @@ -456,8 +456,6 @@ bool rcBuildCompactHeightfield(rcContext* ctx, const int walkableHeight, const i ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Heightfield has too many layers %d (max: %d)", tooHighNeighbour, MAX_LAYERS); } - - ctx->stopTimer(RC_TIMER_BUILD_COMPACTHEIGHTFIELD); return true; } diff --git a/recast/recast/src/RecastAlloc.cpp b/recast/recast/src/RecastAlloc.cpp index b5ec15161..ee1039f2f 100644 --- a/recast/recast/src/RecastAlloc.cpp +++ b/recast/recast/src/RecastAlloc.cpp @@ -20,7 +20,7 @@ #include #include "RecastAlloc.h" -static void *rcAllocDefault(int size, rcAllocHint) +static void *rcAllocDefault(size_t size, rcAllocHint) { return malloc(size); } @@ -41,7 +41,7 @@ void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc) } /// @see rcAllocSetCustom -void* rcAlloc(int size, rcAllocHint hint) +void* rcAlloc(size_t size, rcAllocHint hint) { return sRecastAllocFunc(size, hint); } @@ -72,17 +72,13 @@ void rcFree(void* ptr) /// Using this method ensures the array is at least large enough to hold /// the specified number of elements. This can improve performance by /// avoiding auto-resizing during use. -void rcIntArray::resize(int n) +void rcIntArray::doResize(int n) { - if (n > m_cap) - { - if (!m_cap) m_cap = n; - while (m_cap < n) m_cap *= 2; - int* newData = (int*)rcAlloc(m_cap*sizeof(int), RC_ALLOC_TEMP); - if (m_size && newData) memcpy(newData, m_data, m_size*sizeof(int)); - rcFree(m_data); - m_data = newData; - } - m_size = n; + if (!m_cap) m_cap = n; + while (m_cap < n) m_cap *= 2; + int* newData = (int*)rcAlloc(m_cap*sizeof(int), RC_ALLOC_TEMP); + if (m_size && newData) memcpy(newData, m_data, m_size*sizeof(int)); + rcFree(m_data); + m_data = newData; } diff --git a/recast/recast/src/RecastArea.cpp b/recast/recast/src/RecastArea.cpp index 1a338cd9b..97139cf99 100644 --- a/recast/recast/src/RecastArea.cpp +++ b/recast/recast/src/RecastArea.cpp @@ -41,7 +41,7 @@ bool rcErodeWalkableArea(rcContext* ctx, int radius, rcCompactHeightfield& chf) const int w = chf.width; const int h = chf.height; - ctx->startTimer(RC_TIMER_ERODE_AREA); + rcScopedTimer timer(ctx, RC_TIMER_ERODE_AREA); unsigned char* dist = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP); if (!dist) @@ -215,8 +215,6 @@ bool rcErodeWalkableArea(rcContext* ctx, int radius, rcCompactHeightfield& chf) rcFree(dist); - ctx->stopTimer(RC_TIMER_ERODE_AREA); - return true; } @@ -245,7 +243,7 @@ bool rcMedianFilterWalkableArea(rcContext* ctx, rcCompactHeightfield& chf) const int w = chf.width; const int h = chf.height; - ctx->startTimer(RC_TIMER_MEDIAN_AREA); + rcScopedTimer timer(ctx, RC_TIMER_MEDIAN_AREA); unsigned char* areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP); if (!areas) @@ -306,8 +304,6 @@ bool rcMedianFilterWalkableArea(rcContext* ctx, rcCompactHeightfield& chf) memcpy(chf.areas, areas, sizeof(unsigned char)*chf.spanCount); rcFree(areas); - - ctx->stopTimer(RC_TIMER_MEDIAN_AREA); return true; } @@ -322,7 +318,7 @@ void rcMarkBoxArea(rcContext* ctx, const float* bmin, const float* bmax, unsigne { rcAssert(ctx); - ctx->startTimer(RC_TIMER_MARK_BOX_AREA); + rcScopedTimer timer(ctx, RC_TIMER_MARK_BOX_AREA); int minx = (int)((bmin[0]-chf.bmin[0])/chf.cs); int miny = (int)((bmin[1]-chf.bmin[1])/chf.ch); @@ -357,9 +353,6 @@ void rcMarkBoxArea(rcContext* ctx, const float* bmin, const float* bmax, unsigne } } } - - ctx->stopTimer(RC_TIMER_MARK_BOX_AREA); - } @@ -391,7 +384,7 @@ void rcMarkConvexPolyArea(rcContext* ctx, const float* verts, const int nverts, { rcAssert(ctx); - ctx->startTimer(RC_TIMER_MARK_CONVEXPOLY_AREA); + rcScopedTimer timer(ctx, RC_TIMER_MARK_CONVEXPOLY_AREA); float bmin[3], bmax[3]; rcVcopy(bmin, verts); @@ -448,8 +441,6 @@ void rcMarkConvexPolyArea(rcContext* ctx, const float* verts, const int nverts, } } } - - ctx->stopTimer(RC_TIMER_MARK_CONVEXPOLY_AREA); } int rcOffsetPoly(const float* verts, const int nverts, const float offset, @@ -541,7 +532,7 @@ void rcMarkCylinderArea(rcContext* ctx, const float* pos, { rcAssert(ctx); - ctx->startTimer(RC_TIMER_MARK_CYLINDER_AREA); + rcScopedTimer timer(ctx, RC_TIMER_MARK_CYLINDER_AREA); float bmin[3], bmax[3]; bmin[0] = pos[0] - r; @@ -597,6 +588,4 @@ void rcMarkCylinderArea(rcContext* ctx, const float* pos, } } } - - ctx->stopTimer(RC_TIMER_MARK_CYLINDER_AREA); } diff --git a/recast/recast/src/RecastContour.cpp b/recast/recast/src/RecastContour.cpp index a7be6691f..277ab0150 100644 --- a/recast/recast/src/RecastContour.cpp +++ b/recast/recast/src/RecastContour.cpp @@ -731,7 +731,7 @@ static void mergeRegionHoles(rcContext* ctx, rcContourRegion& region) for (int i = 0; i < region.nholes; i++) maxVerts += region.holes[i].contour->nverts; - rcScopedDelete diags = (rcPotentialDiagonal*)rcAlloc(sizeof(rcPotentialDiagonal)*maxVerts, RC_ALLOC_TEMP); + rcScopedDelete diags((rcPotentialDiagonal*)rcAlloc(sizeof(rcPotentialDiagonal)*maxVerts, RC_ALLOC_TEMP)); if (!diags) { ctx->log(RC_LOG_WARNING, "mergeRegionHoles: Failed to allocated diags %d.", maxVerts); @@ -831,7 +831,7 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf, const int h = chf.height; const int borderSize = chf.borderSize; - ctx->startTimer(RC_TIMER_BUILD_CONTOURS); + rcScopedTimer timer(ctx, RC_TIMER_BUILD_CONTOURS); rcVcopy(cset.bmin, chf.bmin); rcVcopy(cset.bmax, chf.bmax); @@ -849,6 +849,7 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf, cset.width = chf.width - chf.borderSize*2; cset.height = chf.height - chf.borderSize*2; cset.borderSize = chf.borderSize; + cset.maxError = maxError; int maxContours = rcMax((int)chf.maxRegions, 8); cset.conts = (rcContour*)rcAlloc(sizeof(rcContour)*maxContours, RC_ALLOC_PERM); @@ -856,7 +857,7 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf, return false; cset.nconts = 0; - rcScopedDelete flags = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP); + rcScopedDelete flags((unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP)); if (!flags) { ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'flags' (%d).", chf.spanCount); @@ -1008,7 +1009,7 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf, if (cset.nconts > 0) { // Calculate winding of all polygons. - rcScopedDelete winding = (char*)rcAlloc(sizeof(char)*cset.nconts, RC_ALLOC_TEMP); + rcScopedDelete winding((char*)rcAlloc(sizeof(char)*cset.nconts, RC_ALLOC_TEMP)); if (!winding) { ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'hole' (%d).", cset.nconts); @@ -1029,7 +1030,7 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf, // Collect outline contour and holes contours per region. // We assume that there is one outline and multiple holes. const int nregions = chf.maxRegions+1; - rcScopedDelete regions = (rcContourRegion*)rcAlloc(sizeof(rcContourRegion)*nregions, RC_ALLOC_TEMP); + rcScopedDelete regions((rcContourRegion*)rcAlloc(sizeof(rcContourRegion)*nregions, RC_ALLOC_TEMP)); if (!regions) { ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'regions' (%d).", nregions); @@ -1037,7 +1038,7 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf, } memset(regions, 0, sizeof(rcContourRegion)*nregions); - rcScopedDelete holes = (rcContourHole*)rcAlloc(sizeof(rcContourHole)*cset.nconts, RC_ALLOC_TEMP); + rcScopedDelete holes((rcContourHole*)rcAlloc(sizeof(rcContourHole)*cset.nconts, RC_ALLOC_TEMP)); if (!holes) { ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'holes' (%d).", cset.nconts); @@ -1100,7 +1101,5 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf, } - ctx->stopTimer(RC_TIMER_BUILD_CONTOURS); - return true; } diff --git a/recast/recast/src/RecastFilter.cpp b/recast/recast/src/RecastFilter.cpp index bf985c362..9d3e63c48 100644 --- a/recast/recast/src/RecastFilter.cpp +++ b/recast/recast/src/RecastFilter.cpp @@ -37,7 +37,7 @@ void rcFilterLowHangingWalkableObstacles(rcContext* ctx, const int walkableClimb { rcAssert(ctx); - ctx->startTimer(RC_TIMER_FILTER_LOW_OBSTACLES); + rcScopedTimer timer(ctx, RC_TIMER_FILTER_LOW_OBSTACLES); const int w = solid.width; const int h = solid.height; @@ -67,8 +67,6 @@ void rcFilterLowHangingWalkableObstacles(rcContext* ctx, const int walkableClimb } } } - - ctx->stopTimer(RC_TIMER_FILTER_LOW_OBSTACLES); } /// @par @@ -86,7 +84,7 @@ void rcFilterLedgeSpans(rcContext* ctx, const int walkableHeight, const int walk { rcAssert(ctx); - ctx->startTimer(RC_TIMER_FILTER_BORDER); + rcScopedTimer timer(ctx, RC_TIMER_FILTER_BORDER); const int w = solid.width; const int h = solid.height; @@ -156,20 +154,19 @@ void rcFilterLedgeSpans(rcContext* ctx, const int walkableHeight, const int walk // The current span is close to a ledge if the drop to any // neighbour span is less than the walkableClimb. if (minh < -walkableClimb) + { s->area = RC_NULL_AREA; - + } // If the difference between all neighbours is too large, // we are at steep slope, mark the span as ledge. - if ((asmax - asmin) > walkableClimb) + else if ((asmax - asmin) > walkableClimb) { s->area = RC_NULL_AREA; } } } } - - ctx->stopTimer(RC_TIMER_FILTER_BORDER); -} +} /// @par /// @@ -181,7 +178,7 @@ void rcFilterWalkableLowHeightSpans(rcContext* ctx, int walkableHeight, rcHeight { rcAssert(ctx); - ctx->startTimer(RC_TIMER_FILTER_WALKABLE); + rcScopedTimer timer(ctx, RC_TIMER_FILTER_WALKABLE); const int w = solid.width; const int h = solid.height; @@ -202,6 +199,4 @@ void rcFilterWalkableLowHeightSpans(rcContext* ctx, int walkableHeight, rcHeight } } } - - ctx->stopTimer(RC_TIMER_FILTER_WALKABLE); } diff --git a/recast/recast/src/RecastLayers.cpp b/recast/recast/src/RecastLayers.cpp index 41458c1ea..22a357eff 100644 --- a/recast/recast/src/RecastLayers.cpp +++ b/recast/recast/src/RecastLayers.cpp @@ -87,12 +87,12 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf, { rcAssert(ctx); - ctx->startTimer(RC_TIMER_BUILD_LAYERS); + rcScopedTimer timer(ctx, RC_TIMER_BUILD_LAYERS); const int w = chf.width; const int h = chf.height; - rcScopedDelete srcReg = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP); + rcScopedDelete srcReg((unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP)); if (!srcReg) { ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'srcReg' (%d).", chf.spanCount); @@ -101,7 +101,7 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf, memset(srcReg,0xff,sizeof(unsigned char)*chf.spanCount); const int nsweeps = chf.width; - rcScopedDelete sweeps = (rcLayerSweepSpan*)rcAlloc(sizeof(rcLayerSweepSpan)*nsweeps, RC_ALLOC_TEMP); + rcScopedDelete sweeps((rcLayerSweepSpan*)rcAlloc(sizeof(rcLayerSweepSpan)*nsweeps, RC_ALLOC_TEMP)); if (!sweeps) { ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'sweeps' (%d).", nsweeps); @@ -212,7 +212,7 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf, // Allocate and init layer regions. const int nregs = (int)regId; - rcScopedDelete regs = (rcLayerRegion*)rcAlloc(sizeof(rcLayerRegion)*nregs, RC_ALLOC_TEMP); + rcScopedDelete regs((rcLayerRegion*)rcAlloc(sizeof(rcLayerRegion)*nregs, RC_ALLOC_TEMP)); if (!regs) { ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'regs' (%d).", nregs); @@ -258,7 +258,7 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf, const int ay = y + rcGetDirOffsetY(dir); const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir); const unsigned char rai = srcReg[ai]; - if (rai != 0xff && rai != ri) + if (rai != 0xff && rai != ri && regs[ri].nneis < RC_MAX_NEIS) addUnique(regs[ri].neis, regs[ri].nneis, rai); } } @@ -446,10 +446,7 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf, // No layers, return empty. if (layerId == 0) - { - ctx->stopTimer(RC_TIMER_BUILD_LAYERS); return true; - } // Create layers. rcAssert(lset.layers == 0); @@ -612,7 +609,5 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf, layer->miny = layer->maxy = 0; } - ctx->stopTimer(RC_TIMER_BUILD_LAYERS); - return true; } diff --git a/recast/recast/src/RecastMesh.cpp b/recast/recast/src/RecastMesh.cpp index c88534440..9b6f04e30 100644 --- a/recast/recast/src/RecastMesh.cpp +++ b/recast/recast/src/RecastMesh.cpp @@ -528,8 +528,8 @@ static int getPolyMergeValue(unsigned short* pa, unsigned short* pb, return dx*dx + dy*dy; } -static void mergePolys(unsigned short* pa, unsigned short* pb, int ea, int eb, - unsigned short* tmp, const int nvp) +static void mergePolyVerts(unsigned short* pa, unsigned short* pb, int ea, int eb, + unsigned short* tmp, const int nvp) { const int na = countPolyVerts(pa, nvp); const int nb = countPolyVerts(pb, nvp); @@ -601,7 +601,7 @@ static bool canRemoveVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned sho // Find edges which share the removed vertex. const int maxEdges = numTouchedVerts*2; int nedges = 0; - rcScopedDelete edges = (int*)rcAlloc(sizeof(int)*maxEdges*3, RC_ALLOC_TEMP); + rcScopedDelete edges((int*)rcAlloc(sizeof(int)*maxEdges*3, RC_ALLOC_TEMP)); if (!edges) { ctx->log(RC_LOG_WARNING, "canRemoveVertex: Out of memory 'edges' (%d).", maxEdges*3); @@ -681,7 +681,7 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short } int nedges = 0; - rcScopedDelete edges = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp*4, RC_ALLOC_TEMP); + rcScopedDelete edges((int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp*4, RC_ALLOC_TEMP)); if (!edges) { ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'edges' (%d).", numRemovedVerts*nvp*4); @@ -689,15 +689,15 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short } int nhole = 0; - rcScopedDelete hole = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP); + rcScopedDelete hole((int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP)); if (!hole) { ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hole' (%d).", numRemovedVerts*nvp); return false; } - + int nhreg = 0; - rcScopedDelete hreg = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP); + rcScopedDelete hreg((int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP)); if (!hreg) { ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hreg' (%d).", numRemovedVerts*nvp); @@ -705,7 +705,7 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short } int nharea = 0; - rcScopedDelete harea = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP); + rcScopedDelete harea((int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP)); if (!harea) { ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'harea' (%d).", numRemovedVerts*nvp); @@ -822,21 +822,21 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short break; } - rcScopedDelete tris = (int*)rcAlloc(sizeof(int)*nhole*3, RC_ALLOC_TEMP); + rcScopedDelete tris((int*)rcAlloc(sizeof(int)*nhole*3, RC_ALLOC_TEMP)); if (!tris) { ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tris' (%d).", nhole*3); return false; } - rcScopedDelete tverts = (int*)rcAlloc(sizeof(int)*nhole*4, RC_ALLOC_TEMP); + rcScopedDelete tverts((int*)rcAlloc(sizeof(int)*nhole*4, RC_ALLOC_TEMP)); if (!tverts) { ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tverts' (%d).", nhole*4); return false; } - rcScopedDelete thole = (int*)rcAlloc(sizeof(int)*nhole, RC_ALLOC_TEMP); + rcScopedDelete thole((int*)rcAlloc(sizeof(int)*nhole, RC_ALLOC_TEMP)); if (!thole) { ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'thole' (%d).", nhole); @@ -863,19 +863,19 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short } // Merge the hole triangles back to polygons. - rcScopedDelete polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*(ntris+1)*nvp, RC_ALLOC_TEMP); + rcScopedDelete polys((unsigned short*)rcAlloc(sizeof(unsigned short)*(ntris+1)*nvp, RC_ALLOC_TEMP)); if (!polys) { ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'polys' (%d).", (ntris+1)*nvp); return false; } - rcScopedDelete pregs = (unsigned short*)rcAlloc(sizeof(unsigned short)*ntris, RC_ALLOC_TEMP); + rcScopedDelete pregs((unsigned short*)rcAlloc(sizeof(unsigned short)*ntris, RC_ALLOC_TEMP)); if (!pregs) { ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'pregs' (%d).", ntris); return false; } - rcScopedDelete pareas = (unsigned char*)rcAlloc(sizeof(unsigned char)*ntris, RC_ALLOC_TEMP); + rcScopedDelete pareas((unsigned char*)rcAlloc(sizeof(unsigned char)*ntris, RC_ALLOC_TEMP)); if (!pareas) { ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'pareas' (%d).", ntris); @@ -895,7 +895,14 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short polys[npolys*nvp+0] = (unsigned short)hole[t[0]]; polys[npolys*nvp+1] = (unsigned short)hole[t[1]]; polys[npolys*nvp+2] = (unsigned short)hole[t[2]]; - pregs[npolys] = (unsigned short)hreg[t[0]]; + + // If this polygon covers multiple region types then + // mark it as such + if (hreg[t[0]] != hreg[t[1]] || hreg[t[1]] != hreg[t[2]]) + pregs[npolys] = RC_MULTIPLE_REGS; + else + pregs[npolys] = (unsigned short)hreg[t[0]]; + pareas[npolys] = (unsigned char)harea[t[0]]; npolys++; } @@ -936,7 +943,10 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short // Found best, merge. unsigned short* pa = &polys[bestPa*nvp]; unsigned short* pb = &polys[bestPb*nvp]; - mergePolys(pa, pb, bestEa, bestEb, tmpPoly, nvp); + mergePolyVerts(pa, pb, bestEa, bestEb, tmpPoly, nvp); + if (pregs[bestPa] != pregs[bestPb]) + pregs[bestPa] = RC_MULTIPLE_REGS; + unsigned short* last = &polys[(npolys-1)*nvp]; if (pb != last) memcpy(pb, last, sizeof(unsigned short)*nvp); @@ -983,13 +993,14 @@ bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMe { rcAssert(ctx); - ctx->startTimer(RC_TIMER_BUILD_POLYMESH); + rcScopedTimer timer(ctx, RC_TIMER_BUILD_POLYMESH); rcVcopy(mesh.bmin, cset.bmin); rcVcopy(mesh.bmax, cset.bmax); mesh.cs = cset.cs; mesh.ch = cset.ch; mesh.borderSize = cset.borderSize; + mesh.maxEdgeError = cset.maxError; int maxVertices = 0; int maxTris = 0; @@ -1009,7 +1020,7 @@ bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMe return false; } - rcScopedDelete vflags = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxVertices, RC_ALLOC_TEMP); + rcScopedDelete vflags((unsigned char*)rcAlloc(sizeof(unsigned char)*maxVertices, RC_ALLOC_TEMP)); if (!vflags) { ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'vflags' (%d).", maxVertices); @@ -1052,7 +1063,7 @@ bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMe memset(mesh.regs, 0, sizeof(unsigned short)*maxTris); memset(mesh.areas, 0, sizeof(unsigned char)*maxTris); - rcScopedDelete nextVert = (int*)rcAlloc(sizeof(int)*maxVertices, RC_ALLOC_TEMP); + rcScopedDelete nextVert((int*)rcAlloc(sizeof(int)*maxVertices, RC_ALLOC_TEMP)); if (!nextVert) { ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'nextVert' (%d).", maxVertices); @@ -1060,7 +1071,7 @@ bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMe } memset(nextVert, 0, sizeof(int)*maxVertices); - rcScopedDelete firstVert = (int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP); + rcScopedDelete firstVert((int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP)); if (!firstVert) { ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'firstVert' (%d).", VERTEX_BUCKET_COUNT); @@ -1069,19 +1080,19 @@ bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMe for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i) firstVert[i] = -1; - rcScopedDelete indices = (int*)rcAlloc(sizeof(int)*maxVertsPerCont, RC_ALLOC_TEMP); + rcScopedDelete indices((int*)rcAlloc(sizeof(int)*maxVertsPerCont, RC_ALLOC_TEMP)); if (!indices) { ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'indices' (%d).", maxVertsPerCont); return false; } - rcScopedDelete tris = (int*)rcAlloc(sizeof(int)*maxVertsPerCont*3, RC_ALLOC_TEMP); + rcScopedDelete tris((int*)rcAlloc(sizeof(int)*maxVertsPerCont*3, RC_ALLOC_TEMP)); if (!tris) { ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'tris' (%d).", maxVertsPerCont*3); return false; } - rcScopedDelete polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*(maxVertsPerCont+1)*nvp, RC_ALLOC_TEMP); + rcScopedDelete polys((unsigned short*)rcAlloc(sizeof(unsigned short)*(maxVertsPerCont+1)*nvp, RC_ALLOC_TEMP)); if (!polys) { ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'polys' (%d).", maxVertsPerCont*nvp); @@ -1182,7 +1193,7 @@ bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMe // Found best, merge. unsigned short* pa = &polys[bestPa*nvp]; unsigned short* pb = &polys[bestPb*nvp]; - mergePolys(pa, pb, bestEa, bestEb, tmpPoly, nvp); + mergePolyVerts(pa, pb, bestEa, bestEb, tmpPoly, nvp); unsigned short* lastPoly = &polys[(npolys-1)*nvp]; if (pb != lastPoly) memcpy(pb, lastPoly, sizeof(unsigned short)*nvp); @@ -1293,8 +1304,6 @@ bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMe ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: The resulting mesh has too many polygons %d (max %d). Data can be corrupted.", mesh.npolys, 0xffff); } - ctx->stopTimer(RC_TIMER_BUILD_POLYMESH); - return true; } @@ -1306,7 +1315,7 @@ bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, r if (!nmeshes || !meshes) return true; - ctx->startTimer(RC_TIMER_MERGE_POLYMESH); + rcScopedTimer timer(ctx, RC_TIMER_MERGE_POLYMESH); mesh.nvp = meshes[0]->nvp; mesh.cs = meshes[0]->cs; @@ -1367,7 +1376,7 @@ bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, r } memset(mesh.flags, 0, sizeof(unsigned short)*maxPolys); - rcScopedDelete nextVert = (int*)rcAlloc(sizeof(int)*maxVerts, RC_ALLOC_TEMP); + rcScopedDelete nextVert((int*)rcAlloc(sizeof(int)*maxVerts, RC_ALLOC_TEMP)); if (!nextVert) { ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'nextVert' (%d).", maxVerts); @@ -1375,7 +1384,7 @@ bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, r } memset(nextVert, 0, sizeof(int)*maxVerts); - rcScopedDelete firstVert = (int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP); + rcScopedDelete firstVert((int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP)); if (!firstVert) { ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'firstVert' (%d).", VERTEX_BUCKET_COUNT); @@ -1384,7 +1393,7 @@ bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, r for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i) firstVert[i] = -1; - rcScopedDelete vremap = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxVertsPerMesh, RC_ALLOC_PERM); + rcScopedDelete vremap((unsigned short*)rcAlloc(sizeof(unsigned short)*maxVertsPerMesh, RC_ALLOC_PERM)); if (!vremap) { ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'vremap' (%d).", maxVertsPerMesh); @@ -1474,8 +1483,6 @@ bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, r ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: The resulting mesh has too many polygons %d (max %d). Data can be corrupted.", mesh.npolys, 0xffff); } - ctx->stopTimer(RC_TIMER_MERGE_POLYMESH); - return true; } @@ -1499,6 +1506,7 @@ bool rcCopyPolyMesh(rcContext* ctx, const rcPolyMesh& src, rcPolyMesh& dst) dst.cs = src.cs; dst.ch = src.ch; dst.borderSize = src.borderSize; + dst.maxEdgeError = src.maxEdgeError; dst.verts = (unsigned short*)rcAlloc(sizeof(unsigned short)*src.nverts*3, RC_ALLOC_PERM); if (!dst.verts) diff --git a/recast/recast/src/RecastMeshDetail.cpp b/recast/recast/src/RecastMeshDetail.cpp index c0bba6f07..f1270cf20 100644 --- a/recast/recast/src/RecastMeshDetail.cpp +++ b/recast/recast/src/RecastMeshDetail.cpp @@ -202,7 +202,7 @@ static float distToPoly(int nvert, const float* verts, const float* p) static unsigned short getHeight(const float fx, const float fy, const float fz, const float /*cs*/, const float ics, const float ch, - const rcHeightPatch& hp) + const int radius, const rcHeightPatch& hp) { int ix = (int)floorf(fx*ics + 0.01f); int iz = (int)floorf(fz*ics + 0.01f); @@ -212,23 +212,69 @@ static unsigned short getHeight(const float fx, const float fy, const float fz, if (h == RC_UNSET_HEIGHT) { // Special case when data might be bad. - // Find nearest neighbour pixel which has valid height. - const int off[8*2] = { -1,0, -1,-1, 0,-1, 1,-1, 1,0, 1,1, 0,1, -1,1}; + // Walk adjacent cells in a spiral up to 'radius', and look + // for a pixel which has a valid height. + int x = 1, z = 0, dx = 1, dz = 0; + int maxSize = radius * 2 + 1; + int maxIter = maxSize * maxSize - 1; + + int nextRingIterStart = 8; + int nextRingIters = 16; + float dmin = FLT_MAX; - for (int i = 0; i < 8; ++i) + for (int i = 0; i < maxIter; i++) { - const int nx = ix+off[i*2+0]; - const int nz = iz+off[i*2+1]; - if (nx < 0 || nz < 0 || nx >= hp.width || nz >= hp.height) continue; - const unsigned short nh = hp.data[nx+nz*hp.width]; - if (nh == RC_UNSET_HEIGHT) continue; - - const float d = fabsf(nh*ch - fy); - if (d < dmin) + const int nx = ix + x; + const int nz = iz + z; + + if (nx >= 0 && nz >= 0 && nx < hp.width && nz < hp.height) { - h = nh; - dmin = d; + const unsigned short nh = hp.data[nx + nz*hp.width]; + if (nh != RC_UNSET_HEIGHT) + { + const float d = fabsf(nh*ch - fy); + if (d < dmin) + { + h = nh; + dmin = d; + } + } } + + // We are searching in a grid which looks approximately like this: + // __________ + // |2 ______ 2| + // | |1 __ 1| | + // | | |__| | | + // | |______| | + // |__________| + // We want to find the best height as close to the center cell as possible. This means that + // if we find a height in one of the neighbor cells to the center, we don't want to + // expand further out than the 8 neighbors - we want to limit our search to the closest + // of these "rings", but the best height in the ring. + // For example, the center is just 1 cell. We checked that at the entrance to the function. + // The next "ring" contains 8 cells (marked 1 above). Those are all the neighbors to the center cell. + // The next one again contains 16 cells (marked 2). In general each ring has 8 additional cells, which + // can be thought of as adding 2 cells around the "center" of each side when we expand the ring. + // Here we detect if we are about to enter the next ring, and if we are and we have found + // a height, we abort the search. + if (i + 1 == nextRingIterStart) + { + if (h != RC_UNSET_HEIGHT) + break; + + nextRingIterStart += nextRingIters; + nextRingIters += 8; + } + + if ((x == z) || ((x < 0) && (x == -z)) || ((x > 0) && (x == 1 - z))) + { + int tmp = dx; + dx = -dz; + dz = tmp; + } + x += dx; + z += dz; } } return h; @@ -511,7 +557,7 @@ static float polyMinExtent(const float* verts, const int nverts) inline int prev(int i, int n) { return i-1 >= 0 ? i-1 : n-1; } inline int next(int i, int n) { return i+1 < n ? i+1 : 0; } -static void triangulateHull(const int nverts, const float* verts, const int nhull, const int* hull, rcIntArray& tris) +static void triangulateHull(const int /*nverts*/, const float* verts, const int nhull, const int* hull, rcIntArray& tris) { int start = 0, left = 1, right = nhull-1; @@ -590,9 +636,9 @@ inline float getJitterY(const int i) static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin, const float sampleDist, const float sampleMaxError, - const rcCompactHeightfield& chf, const rcHeightPatch& hp, - float* verts, int& nverts, rcIntArray& tris, - rcIntArray& edges, rcIntArray& samples) + const int heightSearchRadius, const rcCompactHeightfield& chf, + const rcHeightPatch& hp, float* verts, int& nverts, + rcIntArray& tris, rcIntArray& edges, rcIntArray& samples) { static const int MAX_VERTS = 127; static const int MAX_TRIS = 255; // Max tris for delaunay is 2n-2-k (n=num verts, k=num hull verts). @@ -661,7 +707,7 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin, pos[0] = vj[0] + dx*u; pos[1] = vj[1] + dy*u; pos[2] = vj[2] + dz*u; - pos[1] = getHeight(pos[0],pos[1],pos[2], cs, ics, chf.ch, hp)*chf.ch; + pos[1] = getHeight(pos[0],pos[1],pos[2], cs, ics, chf.ch, heightSearchRadius, hp)*chf.ch; } // Simplify samples. int idx[MAX_VERTS_PER_EDGE] = {0,nn}; @@ -769,7 +815,7 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin, // Make sure the samples are not too close to the edges. if (distToPoly(nin,in,pt) > -sampleDist/2) continue; samples.push(x); - samples.push(getHeight(pt[0], pt[1], pt[2], cs, ics, chf.ch, hp)); + samples.push(getHeight(pt[0], pt[1], pt[2], cs, ics, chf.ch, heightSearchRadius, hp)); samples.push(z); samples.push(0); // Not added } @@ -834,33 +880,25 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin, return true; } - -static void getHeightDataSeedsFromVertices(const rcCompactHeightfield& chf, - const unsigned short* poly, const int npoly, - const unsigned short* verts, const int bs, - rcHeightPatch& hp, rcIntArray& stack) +static void seedArrayWithPolyCenter(rcContext* ctx, const rcCompactHeightfield& chf, + const unsigned short* poly, const int npoly, + const unsigned short* verts, const int bs, + rcHeightPatch& hp, rcIntArray& array) { - // Floodfill the heightfield to get 2D height data, - // starting at vertex locations as seeds. - // Note: Reads to the compact heightfield are offset by border size (bs) // since border size offset is already removed from the polymesh vertices. - memset(hp.data, 0, sizeof(unsigned short)*hp.width*hp.height); - - stack.resize(0); - static const int offset[9*2] = { 0,0, -1,-1, 0,-1, 1,-1, 1,0, 1,1, 0,1, -1,1, -1,0, }; - // Use poly vertices as seed points for the flood fill. - for (int j = 0; j < npoly; ++j) + // Find cell closest to a poly vertex + int startCellX = 0, startCellY = 0, startSpanIndex = -1; + int dmin = RC_UNSET_HEIGHT; + for (int j = 0; j < npoly && dmin > 0; ++j) { - int cx = 0, cz = 0, ci =-1; - int dmin = RC_UNSET_HEIGHT; - for (int k = 0; k < 9; ++k) + for (int k = 0; k < 9 && dmin > 0; ++k) { const int ax = (int)verts[poly[j]*3+0] + offset[k*2+0]; const int ay = (int)verts[poly[j]*3+1]; @@ -870,191 +908,208 @@ static void getHeightDataSeedsFromVertices(const rcCompactHeightfield& chf, continue; const rcCompactCell& c = chf.cells[(ax+bs)+(az+bs)*chf.width]; - for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni && dmin > 0; ++i) { const rcCompactSpan& s = chf.spans[i]; int d = rcAbs(ay - (int)s.y); if (d < dmin) { - cx = ax; - cz = az; - ci = i; + startCellX = ax; + startCellY = az; + startSpanIndex = i; dmin = d; } } } - if (ci != -1) - { - stack.push(cx); - stack.push(cz); - stack.push(ci); - } } - // Find center of the polygon using flood fill. - int pcx = 0, pcz = 0; + rcAssert(startSpanIndex != -1); + // Find center of the polygon + int pcx = 0, pcy = 0; for (int j = 0; j < npoly; ++j) { pcx += (int)verts[poly[j]*3+0]; - pcz += (int)verts[poly[j]*3+2]; + pcy += (int)verts[poly[j]*3+2]; } pcx /= npoly; - pcz /= npoly; + pcy /= npoly; - for (int i = 0; i < stack.size(); i += 3) + // Use seeds array as a stack for DFS + array.resize(0); + array.push(startCellX); + array.push(startCellY); + array.push(startSpanIndex); + + int dirs[] = { 0, 1, 2, 3 }; + memset(hp.data, 0, sizeof(unsigned short)*hp.width*hp.height); + // DFS to move to the center. Note that we need a DFS here and can not just move + // directly towards the center without recording intermediate nodes, even though the polygons + // are convex. In very rare we can get stuck due to contour simplification if we do not + // record nodes. + int cx = -1, cy = -1, ci = -1; + while (true) { - int cx = stack[i+0]; - int cy = stack[i+1]; - int idx = cx-hp.xmin+(cy-hp.ymin)*hp.width; - hp.data[idx] = 1; - } - - while (stack.size() > 0) - { - int ci = stack.pop(); - int cy = stack.pop(); - int cx = stack.pop(); - - // Check if close to center of the polygon. - if (rcAbs(cx-pcx) <= 1 && rcAbs(cy-pcz) <= 1) + if (array.size() < 3) { - stack.resize(0); - stack.push(cx); - stack.push(cy); - stack.push(ci); + ctx->log(RC_LOG_WARNING, "Walk towards polygon center failed to reach center"); break; } - + + ci = array.pop(); + cy = array.pop(); + cx = array.pop(); + + if (cx == pcx && cy == pcy) + break; + + // If we are already at the correct X-position, prefer direction + // directly towards the center in the Y-axis; otherwise prefer + // direction in the X-axis + int directDir; + if (cx == pcx) + directDir = rcGetDirForOffset(0, pcy > cy ? 1 : -1); + else + directDir = rcGetDirForOffset(pcx > cx ? 1 : -1, 0); + + // Push the direct dir last so we start with this on next iteration + rcSwap(dirs[directDir], dirs[3]); + const rcCompactSpan& cs = chf.spans[ci]; - - for (int dir = 0; dir < 4; ++dir) + for (int i = 0; i < 4; i++) { - if (rcGetCon(cs, dir) == RC_NOT_CONNECTED) continue; - - const int ax = cx + rcGetDirOffsetX(dir); - const int ay = cy + rcGetDirOffsetY(dir); - - if (ax < hp.xmin || ax >= (hp.xmin+hp.width) || - ay < hp.ymin || ay >= (hp.ymin+hp.height)) + int dir = dirs[i]; + if (rcGetCon(cs, dir) == RC_NOT_CONNECTED) continue; - - if (hp.data[ax-hp.xmin+(ay-hp.ymin)*hp.width] != 0) + + int newX = cx + rcGetDirOffsetX(dir); + int newY = cy + rcGetDirOffsetY(dir); + + int hpx = newX - hp.xmin; + int hpy = newY - hp.ymin; + if (hpx < 0 || hpx >= hp.width || hpy < 0 || hpy >= hp.height) continue; - - const int ai = (int)chf.cells[(ax+bs)+(ay+bs)*chf.width].index + rcGetCon(cs, dir); - - int idx = ax-hp.xmin+(ay-hp.ymin)*hp.width; - hp.data[idx] = 1; - - stack.push(ax); - stack.push(ay); - stack.push(ai); + + if (hp.data[hpx+hpy*hp.width] != 0) + continue; + + hp.data[hpx+hpy*hp.width] = 1; + array.push(newX); + array.push(newY); + array.push((int)chf.cells[(newX+bs)+(newY+bs)*chf.width].index + rcGetCon(cs, dir)); } + + rcSwap(dirs[directDir], dirs[3]); } - + + array.resize(0); + // getHeightData seeds are given in coordinates with borders + array.push(cx+bs); + array.push(cy+bs); + array.push(ci); + memset(hp.data, 0xff, sizeof(unsigned short)*hp.width*hp.height); - - // Mark start locations. - for (int i = 0; i < stack.size(); i += 3) - { - int cx = stack[i+0]; - int cy = stack[i+1]; - int ci = stack[i+2]; - int idx = cx-hp.xmin+(cy-hp.ymin)*hp.width; - const rcCompactSpan& cs = chf.spans[ci]; - hp.data[idx] = cs.y; - - // getHeightData seeds are given in coordinates with borders - stack[i+0] += bs; - stack[i+1] += bs; - } - + const rcCompactSpan& cs = chf.spans[ci]; + hp.data[cx-hp.xmin+(cy-hp.ymin)*hp.width] = cs.y; } +static void push3(rcIntArray& queue, int v1, int v2, int v3) +{ + queue.resize(queue.size() + 3); + queue[queue.size() - 3] = v1; + queue[queue.size() - 2] = v2; + queue[queue.size() - 1] = v3; +} -static void getHeightData(const rcCompactHeightfield& chf, +static void getHeightData(rcContext* ctx, const rcCompactHeightfield& chf, const unsigned short* poly, const int npoly, const unsigned short* verts, const int bs, - rcHeightPatch& hp, rcIntArray& stack, + rcHeightPatch& hp, rcIntArray& queue, int region) { // Note: Reads to the compact heightfield are offset by border size (bs) // since border size offset is already removed from the polymesh vertices. - stack.resize(0); + queue.resize(0); + // Set all heights to RC_UNSET_HEIGHT. memset(hp.data, 0xff, sizeof(unsigned short)*hp.width*hp.height); - + bool empty = true; - // Copy the height from the same region, and mark region borders - // as seed points to fill the rest. - for (int hy = 0; hy < hp.height; hy++) + // We cannot sample from this poly if it was created from polys + // of different regions. If it was then it could potentially be overlapping + // with polys of that region and the heights sampled here could be wrong. + if (region != RC_MULTIPLE_REGS) { - int y = hp.ymin + hy + bs; - for (int hx = 0; hx < hp.width; hx++) + // Copy the height from the same region, and mark region borders + // as seed points to fill the rest. + for (int hy = 0; hy < hp.height; hy++) { - int x = hp.xmin + hx + bs; - const rcCompactCell& c = chf.cells[x+y*chf.width]; - for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + int y = hp.ymin + hy + bs; + for (int hx = 0; hx < hp.width; hx++) { - const rcCompactSpan& s = chf.spans[i]; - if (s.reg == region) + int x = hp.xmin + hx + bs; + const rcCompactCell& c = chf.cells[x + y*chf.width]; + for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; ++i) { - // Store height - hp.data[hx + hy*hp.width] = s.y; - empty = false; - - // If any of the neighbours is not in same region, - // add the current location as flood fill start - bool border = false; - for (int dir = 0; dir < 4; ++dir) + const rcCompactSpan& s = chf.spans[i]; + if (s.reg == region) { - if (rcGetCon(s, dir) != RC_NOT_CONNECTED) + // Store height + hp.data[hx + hy*hp.width] = s.y; + empty = false; + + // If any of the neighbours is not in same region, + // add the current location as flood fill start + bool border = false; + for (int dir = 0; dir < 4; ++dir) { - const int ax = x + rcGetDirOffsetX(dir); - const int ay = y + rcGetDirOffsetY(dir); - const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir); - const rcCompactSpan& as = chf.spans[ai]; - if (as.reg != region) + if (rcGetCon(s, dir) != RC_NOT_CONNECTED) { - border = true; - break; + const int ax = x + rcGetDirOffsetX(dir); + const int ay = y + rcGetDirOffsetY(dir); + const int ai = (int)chf.cells[ax + ay*chf.width].index + rcGetCon(s, dir); + const rcCompactSpan& as = chf.spans[ai]; + if (as.reg != region) + { + border = true; + break; + } } } + if (border) + push3(queue, x, y, i); + break; } - if (border) - { - stack.push(x); - stack.push(y); - stack.push(i); - } - break; } } } } - // if the polygon does not contian any points from the current region (rare, but happens) - // then use the cells closest to the polygon vertices as seeds to fill the height field + // if the polygon does not contain any points from the current region (rare, but happens) + // or if it could potentially be overlapping polygons of the same region, + // then use the center as the seed point. if (empty) - getHeightDataSeedsFromVertices(chf, poly, npoly, verts, bs, hp, stack); + seedArrayWithPolyCenter(ctx, chf, poly, npoly, verts, bs, hp, queue); static const int RETRACT_SIZE = 256; int head = 0; - while (head*3 < stack.size()) + // We assume the seed is centered in the polygon, so a BFS to collect + // height data will ensure we do not move onto overlapping polygons and + // sample wrong heights. + while (head*3 < queue.size()) { - int cx = stack[head*3+0]; - int cy = stack[head*3+1]; - int ci = stack[head*3+2]; + int cx = queue[head*3+0]; + int cy = queue[head*3+1]; + int ci = queue[head*3+2]; head++; if (head >= RETRACT_SIZE) { head = 0; - if (stack.size() > RETRACT_SIZE*3) - memmove(&stack[0], &stack[RETRACT_SIZE*3], sizeof(int)*(stack.size()-RETRACT_SIZE*3)); - stack.resize(stack.size()-RETRACT_SIZE*3); + if (queue.size() > RETRACT_SIZE*3) + memmove(&queue[0], &queue[RETRACT_SIZE*3], sizeof(int)*(queue.size()-RETRACT_SIZE*3)); + queue.resize(queue.size()-RETRACT_SIZE*3); } const rcCompactSpan& cs = chf.spans[ci]; @@ -1067,7 +1122,7 @@ static void getHeightData(const rcCompactHeightfield& chf, const int hx = ax - hp.xmin - bs; const int hy = ay - hp.ymin - bs; - if (hx < 0 || hx >= hp.width || hy < 0 || hy >= hp.height) + if ((unsigned int)hx >= (unsigned int)hp.width || (unsigned int)hy >= (unsigned int)hp.height) continue; if (hp.data[hx + hy*hp.width] != RC_UNSET_HEIGHT) @@ -1078,9 +1133,7 @@ static void getHeightData(const rcCompactHeightfield& chf, hp.data[hx + hy*hp.width] = as.y; - stack.push(ax); - stack.push(ay); - stack.push(ai); + push3(queue, ax, ay, ai); } } } @@ -1120,7 +1173,7 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa { rcAssert(ctx); - ctx->startTimer(RC_TIMER_BUILD_POLYMESHDETAIL); + rcScopedTimer timer(ctx, RC_TIMER_BUILD_POLYMESHDETAIL); if (mesh.nverts == 0 || mesh.npolys == 0) return true; @@ -1130,23 +1183,24 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa const float ch = mesh.ch; const float* orig = mesh.bmin; const int borderSize = mesh.borderSize; + const int heightSearchRadius = rcMax(1, (int)ceilf(mesh.maxEdgeError)); rcIntArray edges(64); rcIntArray tris(512); - rcIntArray stack(512); + rcIntArray arr(512); rcIntArray samples(512); float verts[256*3]; rcHeightPatch hp; int nPolyVerts = 0; int maxhw = 0, maxhh = 0; - rcScopedDelete bounds = (int*)rcAlloc(sizeof(int)*mesh.npolys*4, RC_ALLOC_TEMP); + rcScopedDelete bounds((int*)rcAlloc(sizeof(int)*mesh.npolys*4, RC_ALLOC_TEMP)); if (!bounds) { ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'bounds' (%d).", mesh.npolys*4); return false; } - rcScopedDelete poly = (float*)rcAlloc(sizeof(float)*nvp*3, RC_ALLOC_TEMP); + rcScopedDelete poly((float*)rcAlloc(sizeof(float)*nvp*3, RC_ALLOC_TEMP)); if (!poly) { ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'poly' (%d).", nvp*3); @@ -1240,13 +1294,14 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa hp.ymin = bounds[i*4+2]; hp.width = bounds[i*4+1]-bounds[i*4+0]; hp.height = bounds[i*4+3]-bounds[i*4+2]; - getHeightData(chf, p, npoly, mesh.verts, borderSize, hp, stack, mesh.regs[i]); + getHeightData(ctx, chf, p, npoly, mesh.verts, borderSize, hp, arr, mesh.regs[i]); // Build detail mesh. int nverts = 0; if (!buildPolyDetail(ctx, poly, npoly, sampleDist, sampleMaxError, - chf, hp, verts, nverts, tris, + heightSearchRadius, chf, hp, + verts, nverts, tris, edges, samples)) { return false; @@ -1327,8 +1382,6 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa } } - ctx->stopTimer(RC_TIMER_BUILD_POLYMESHDETAIL); - return true; } @@ -1337,7 +1390,7 @@ bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int { rcAssert(ctx); - ctx->startTimer(RC_TIMER_MERGE_POLYMESHDETAIL); + rcScopedTimer timer(ctx, RC_TIMER_MERGE_POLYMESHDETAIL); int maxVerts = 0; int maxTris = 0; @@ -1406,7 +1459,5 @@ bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int } } - ctx->stopTimer(RC_TIMER_MERGE_POLYMESHDETAIL); - return true; } diff --git a/recast/recast/src/RecastRasterization.cpp b/recast/recast/src/RecastRasterization.cpp index dd3f5f761..a4cef7490 100644 --- a/recast/recast/src/RecastRasterization.cpp +++ b/recast/recast/src/RecastRasterization.cpp @@ -82,7 +82,7 @@ static void freeSpan(rcHeightfield& hf, rcSpan* ptr) hf.freelist = ptr; } -static void addSpan(rcHeightfield& hf, const int x, const int y, +static bool addSpan(rcHeightfield& hf, const int x, const int y, const unsigned short smin, const unsigned short smax, const unsigned char area, const int flagMergeThr) { @@ -90,6 +90,8 @@ static void addSpan(rcHeightfield& hf, const int x, const int y, int idx = x + y*hf.width; rcSpan* s = allocSpan(hf); + if (!s) + return false; s->smin = smin; s->smax = smax; s->area = area; @@ -99,7 +101,7 @@ static void addSpan(rcHeightfield& hf, const int x, const int y, if (!hf.spans[idx]) { hf.spans[idx] = s; - return; + return true; } rcSpan* prev = 0; rcSpan* cur = hf.spans[idx]; @@ -152,6 +154,8 @@ static void addSpan(rcHeightfield& hf, const int x, const int y, s->next = hf.spans[idx]; hf.spans[idx] = s; } + + return true; } /// @par @@ -161,12 +165,19 @@ static void addSpan(rcHeightfield& hf, const int x, const int y, /// from the existing span, the span flags are merged. /// /// @see rcHeightfield, rcSpan. -void rcAddSpan(rcContext* /*ctx*/, rcHeightfield& hf, const int x, const int y, +bool rcAddSpan(rcContext* ctx, rcHeightfield& hf, const int x, const int y, const unsigned short smin, const unsigned short smax, const unsigned char area, const int flagMergeThr) { -// rcAssert(ctx); - addSpan(hf, x,y, smin, smax, area, flagMergeThr); + rcAssert(ctx); + + if (!addSpan(hf, x, y, smin, smax, area, flagMergeThr)) + { + ctx->log(RC_LOG_ERROR, "rcAddSpan: Out of memory."); + return false; + } + + return true; } // divides a convex polygons into two convex polygons on both sides of a line @@ -227,7 +238,7 @@ static void dividePoly(const float* in, int nin, -static void rasterizeTri(const float* v0, const float* v1, const float* v2, +static bool rasterizeTri(const float* v0, const float* v1, const float* v2, const unsigned char area, rcHeightfield& hf, const float* bmin, const float* bmax, const float cs, const float ics, const float ich, @@ -248,7 +259,7 @@ static void rasterizeTri(const float* v0, const float* v1, const float* v2, // If the triangle does not touch the bbox of the heightfield, skip the triagle. if (!overlapBounds(bmin, bmax, tmin, tmax)) - return; + return true; // Calculate the footprint of the triangle on the grid's y-axis int y0 = (int)((tmin[2] - bmin[2])*ics); @@ -315,9 +326,12 @@ static void rasterizeTri(const float* v0, const float* v1, const float* v2, unsigned short ismin = (unsigned short)rcClamp((int)floorf(smin * ich), 0, RC_SPAN_MAX_HEIGHT); unsigned short ismax = (unsigned short)rcClamp((int)ceilf(smax * ich), (int)ismin+1, RC_SPAN_MAX_HEIGHT); - addSpan(hf, x, y, ismin, ismax, area, flagMergeThr); + if (!addSpan(hf, x, y, ismin, ismax, area, flagMergeThr)) + return false; } } + + return true; } /// @par @@ -325,19 +339,23 @@ static void rasterizeTri(const float* v0, const float* v1, const float* v2, /// No spans will be added if the triangle does not overlap the heightfield grid. /// /// @see rcHeightfield -void rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const float* v2, +bool rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const float* v2, const unsigned char area, rcHeightfield& solid, const int flagMergeThr) { rcAssert(ctx); - ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES); + rcScopedTimer timer(ctx, RC_TIMER_RASTERIZE_TRIANGLES); const float ics = 1.0f/solid.cs; const float ich = 1.0f/solid.ch; - rasterizeTri(v0, v1, v2, area, solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr); + if (!rasterizeTri(v0, v1, v2, area, solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr)) + { + ctx->log(RC_LOG_ERROR, "rcRasterizeTriangle: Out of memory."); + return false; + } - ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES); + return true; } /// @par @@ -345,13 +363,13 @@ void rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const /// Spans will only be added for triangles that overlap the heightfield grid. /// /// @see rcHeightfield -void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/, +bool rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/, const int* tris, const unsigned char* areas, const int nt, rcHeightfield& solid, const int flagMergeThr) { rcAssert(ctx); - ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES); + rcScopedTimer timer(ctx, RC_TIMER_RASTERIZE_TRIANGLES); const float ics = 1.0f/solid.cs; const float ich = 1.0f/solid.ch; @@ -361,12 +379,15 @@ void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/, const float* v0 = &verts[tris[i*3+0]*3]; const float* v1 = &verts[tris[i*3+1]*3]; const float* v2 = &verts[tris[i*3+2]*3]; - // Rasterize. - rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr); + if (!rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr)) + { + ctx->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory."); + return false; + } } - - ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES); + + return true; } /// @par @@ -374,13 +395,13 @@ void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/, /// Spans will only be added for triangles that overlap the heightfield grid. /// /// @see rcHeightfield -void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/, +bool rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/, const unsigned short* tris, const unsigned char* areas, const int nt, rcHeightfield& solid, const int flagMergeThr) { rcAssert(ctx); - ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES); + rcScopedTimer timer(ctx, RC_TIMER_RASTERIZE_TRIANGLES); const float ics = 1.0f/solid.cs; const float ich = 1.0f/solid.ch; @@ -391,10 +412,14 @@ void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/, const float* v1 = &verts[tris[i*3+1]*3]; const float* v2 = &verts[tris[i*3+2]*3]; // Rasterize. - rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr); + if (!rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr)) + { + ctx->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory."); + return false; + } } - - ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES); + + return true; } /// @par @@ -402,12 +427,12 @@ void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/, /// Spans will only be added for triangles that overlap the heightfield grid. /// /// @see rcHeightfield -void rcRasterizeTriangles(rcContext* ctx, const float* verts, const unsigned char* areas, const int nt, +bool rcRasterizeTriangles(rcContext* ctx, const float* verts, const unsigned char* areas, const int nt, rcHeightfield& solid, const int flagMergeThr) { rcAssert(ctx); - ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES); + rcScopedTimer timer(ctx, RC_TIMER_RASTERIZE_TRIANGLES); const float ics = 1.0f/solid.cs; const float ich = 1.0f/solid.ch; @@ -418,8 +443,12 @@ void rcRasterizeTriangles(rcContext* ctx, const float* verts, const unsigned cha const float* v1 = &verts[(i*3+1)*3]; const float* v2 = &verts[(i*3+2)*3]; // Rasterize. - rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr); + if (!rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr)) + { + ctx->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory."); + return false; + } } - - ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES); + + return true; } diff --git a/recast/recast/src/RecastRegion.cpp b/recast/recast/src/RecastRegion.cpp index 38bc4ff47..54acf4b73 100644 --- a/recast/recast/src/RecastRegion.cpp +++ b/recast/recast/src/RecastRegion.cpp @@ -1041,7 +1041,7 @@ static void addUniqueConnection(rcRegion& reg, int n) static bool mergeAndFilterLayerRegions(rcContext* ctx, int minRegionArea, unsigned short& maxRegionId, rcCompactHeightfield& chf, - unsigned short* srcReg, rcIntArray& overlaps) + unsigned short* srcReg, rcIntArray& /*overlaps*/) { const int w = chf.width; const int h = chf.height; @@ -1257,7 +1257,7 @@ bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf) { rcAssert(ctx); - ctx->startTimer(RC_TIMER_BUILD_DISTANCEFIELD); + rcScopedTimer timer(ctx, RC_TIMER_BUILD_DISTANCEFIELD); if (chf.dist) { @@ -1281,25 +1281,23 @@ bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf) unsigned short maxDist = 0; - ctx->startTimer(RC_TIMER_BUILD_DISTANCEFIELD_DIST); - - calculateDistanceField(chf, src, maxDist); - chf.maxDistance = maxDist; - - ctx->stopTimer(RC_TIMER_BUILD_DISTANCEFIELD_DIST); - - ctx->startTimer(RC_TIMER_BUILD_DISTANCEFIELD_BLUR); - - // Blur - if (boxBlur(chf, 1, src, dst) != src) - rcSwap(src, dst); - - // Store distance. - chf.dist = src; - - ctx->stopTimer(RC_TIMER_BUILD_DISTANCEFIELD_BLUR); + { + rcScopedTimer timerDist(ctx, RC_TIMER_BUILD_DISTANCEFIELD_DIST); - ctx->stopTimer(RC_TIMER_BUILD_DISTANCEFIELD); + calculateDistanceField(chf, src, maxDist); + chf.maxDistance = maxDist; + } + + { + rcScopedTimer timerBlur(ctx, RC_TIMER_BUILD_DISTANCEFIELD_BLUR); + + // Blur + if (boxBlur(chf, 1, src, dst) != src) + rcSwap(src, dst); + + // Store distance. + chf.dist = src; + } rcFree(dst); @@ -1359,13 +1357,13 @@ bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf, { rcAssert(ctx); - ctx->startTimer(RC_TIMER_BUILD_REGIONS); + rcScopedTimer timer(ctx, RC_TIMER_BUILD_REGIONS); const int w = chf.width; const int h = chf.height; unsigned short id = 1; - rcScopedDelete srcReg = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP); + rcScopedDelete srcReg((unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP)); if (!srcReg) { ctx->log(RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'src' (%d).", chf.spanCount); @@ -1374,7 +1372,7 @@ bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf, memset(srcReg,0,sizeof(unsigned short)*chf.spanCount); const int nsweeps = rcMax(chf.width,chf.height); - rcScopedDelete sweeps = (rcSweepSpan*)rcAlloc(sizeof(rcSweepSpan)*nsweeps, RC_ALLOC_TEMP); + rcScopedDelete sweeps((rcSweepSpan*)rcAlloc(sizeof(rcSweepSpan)*nsweeps, RC_ALLOC_TEMP)); if (!sweeps) { ctx->log(RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'sweeps' (%d).", nsweeps); @@ -1489,23 +1487,21 @@ bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf, } - ctx->startTimer(RC_TIMER_BUILD_REGIONS_FILTER); + { + rcScopedTimer timerFilter(ctx, RC_TIMER_BUILD_REGIONS_FILTER); - // Merge regions and filter out small regions. - rcIntArray overlaps; - chf.maxRegions = id; - if (!mergeAndFilterRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg, overlaps)) - return false; + // Merge regions and filter out small regions. + rcIntArray overlaps; + chf.maxRegions = id; + if (!mergeAndFilterRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg, overlaps)) + return false; - // Monotone partitioning does not generate overlapping regions. - - ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER); + // Monotone partitioning does not generate overlapping regions. + } // Store the result out. for (int i = 0; i < chf.spanCount; ++i) chf.spans[i].reg = srcReg[i]; - - ctx->stopTimer(RC_TIMER_BUILD_REGIONS); return true; } @@ -1534,12 +1530,12 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, { rcAssert(ctx); - ctx->startTimer(RC_TIMER_BUILD_REGIONS); + rcScopedTimer timer(ctx, RC_TIMER_BUILD_REGIONS); const int w = chf.width; const int h = chf.height; - rcScopedDelete buf = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount*4, RC_ALLOC_TEMP); + rcScopedDelete buf((unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount*4, RC_ALLOC_TEMP)); if (!buf) { ctx->log(RC_LOG_ERROR, "rcBuildRegions: Out of memory 'tmp' (%d).", chf.spanCount*4); @@ -1579,6 +1575,13 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, // Make sure border will not overflow. const int bw = rcMin(w, borderSize); const int bh = rcMin(h, borderSize); + + if (regionId > 0xFFFB) + { + ctx->log(RC_LOG_ERROR, "rcBuildRegions: Region ID overflow"); + return false; + } + // Paint regions paintRectRegion(0, bw, 0, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++; paintRectRegion(w-bw, w, 0, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++; @@ -1603,33 +1606,41 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, // ctx->stopTimer(RC_TIMER_DIVIDE_TO_LEVELS); - ctx->startTimer(RC_TIMER_BUILD_REGIONS_EXPAND); - - // Expand current regions until no empty connected cells found. - if (expandRegions(expandIters, level, chf, srcReg, srcDist, dstReg, dstDist, lvlStacks[sId], false) != srcReg) { - rcSwap(srcReg, dstReg); - rcSwap(srcDist, dstDist); - } - - ctx->stopTimer(RC_TIMER_BUILD_REGIONS_EXPAND); - - ctx->startTimer(RC_TIMER_BUILD_REGIONS_FLOOD); - - // Mark new regions with IDs. - for (int j=0; j= 0 && srcReg[i] == 0) + rcScopedTimer timerExpand(ctx, RC_TIMER_BUILD_REGIONS_EXPAND); + + // Expand current regions until no empty connected cells found. + if (expandRegions(expandIters, level, chf, srcReg, srcDist, dstReg, dstDist, lvlStacks[sId], false) != srcReg) { - if (floodRegion(x, y, i, level, regionId, chf, srcReg, srcDist, stack)) - regionId++; + rcSwap(srcReg, dstReg); + rcSwap(srcDist, dstDist); } } - ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FLOOD); + { + rcScopedTimer timerFloor(ctx, RC_TIMER_BUILD_REGIONS_FLOOD); + + // Mark new regions with IDs. + for (int j = 0; j= 0 && srcReg[i] == 0) + { + if (floodRegion(x, y, i, level, regionId, chf, srcReg, srcDist, stack)) + { + if (regionId == 0xFFFF) + { + ctx->log(RC_LOG_ERROR, "rcBuildRegions: Region ID overflow"); + return false; + } + + regionId++; + } + } + } + } } // Expand current regions until no empty connected cells found. @@ -1641,28 +1652,26 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, ctx->stopTimer(RC_TIMER_BUILD_REGIONS_WATERSHED); - ctx->startTimer(RC_TIMER_BUILD_REGIONS_FILTER); - - // Merge regions and filter out smalle regions. - rcIntArray overlaps; - chf.maxRegions = regionId; - if (!mergeAndFilterRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg, overlaps)) - return false; - - // If overlapping regions were found during merging, split those regions. - if (overlaps.size() > 0) { - ctx->log(RC_LOG_ERROR, "rcBuildRegions: %d overlapping regions.", overlaps.size()); - } + rcScopedTimer timerFilter(ctx, RC_TIMER_BUILD_REGIONS_FILTER); - ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER); + // Merge regions and filter out smalle regions. + rcIntArray overlaps; + chf.maxRegions = regionId; + if (!mergeAndFilterRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg, overlaps)) + return false; + + // If overlapping regions were found during merging, split those regions. + if (overlaps.size() > 0) + { + ctx->log(RC_LOG_ERROR, "rcBuildRegions: %d overlapping regions.", overlaps.size()); + } + } // Write the result out. for (int i = 0; i < chf.spanCount; ++i) chf.spans[i].reg = srcReg[i]; - ctx->stopTimer(RC_TIMER_BUILD_REGIONS); - return true; } @@ -1672,13 +1681,13 @@ bool rcBuildLayerRegions(rcContext* ctx, rcCompactHeightfield& chf, { rcAssert(ctx); - ctx->startTimer(RC_TIMER_BUILD_REGIONS); + rcScopedTimer timer(ctx, RC_TIMER_BUILD_REGIONS); const int w = chf.width; const int h = chf.height; unsigned short id = 1; - rcScopedDelete srcReg = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP); + rcScopedDelete srcReg((unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP)); if (!srcReg) { ctx->log(RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'src' (%d).", chf.spanCount); @@ -1687,7 +1696,7 @@ bool rcBuildLayerRegions(rcContext* ctx, rcCompactHeightfield& chf, memset(srcReg,0,sizeof(unsigned short)*chf.spanCount); const int nsweeps = rcMax(chf.width,chf.height); - rcScopedDelete sweeps = (rcSweepSpan*)rcAlloc(sizeof(rcSweepSpan)*nsweeps, RC_ALLOC_TEMP); + rcScopedDelete sweeps((rcSweepSpan*)rcAlloc(sizeof(rcSweepSpan)*nsweeps, RC_ALLOC_TEMP)); if (!sweeps) { ctx->log(RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'sweeps' (%d).", nsweeps); @@ -1802,22 +1811,20 @@ bool rcBuildLayerRegions(rcContext* ctx, rcCompactHeightfield& chf, } - ctx->startTimer(RC_TIMER_BUILD_REGIONS_FILTER); - - // Merge monotone regions to layers and remove small regions. - rcIntArray overlaps; - chf.maxRegions = id; - if (!mergeAndFilterLayerRegions(ctx, minRegionArea, chf.maxRegions, chf, srcReg, overlaps)) - return false; - - ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER); + { + rcScopedTimer timerFilter(ctx, RC_TIMER_BUILD_REGIONS_FILTER); + + // Merge monotone regions to layers and remove small regions. + rcIntArray overlaps; + chf.maxRegions = id; + if (!mergeAndFilterLayerRegions(ctx, minRegionArea, chf.maxRegions, chf, srcReg, overlaps)) + return false; + } // Store the result out. for (int i = 0; i < chf.spanCount; ++i) chf.spans[i].reg = srcReg[i]; - ctx->stopTimer(RC_TIMER_BUILD_REGIONS); - return true; }