Update recast

This commit is contained in:
KimLS 2016-01-25 14:33:40 -08:00
parent 913fb6c22e
commit ec58d92e42
25 changed files with 629 additions and 474 deletions

View File

@ -210,6 +210,10 @@ public:
virtual void end(); virtual void end();
void clear(); void clear();
void draw(struct duDebugDraw* dd); void draw(struct duDebugDraw* dd);
private:
// Explicitly disabled copy constructor and copy assignment operator.
duDisplayList(const duDisplayList&);
duDisplayList& operator=(const duDisplayList&);
}; };

View File

@ -31,7 +31,6 @@ enum DrawNavMeshFlags
}; };
void duDebugDrawNavMesh(struct duDebugDraw* dd, const dtNavMesh& mesh, unsigned char flags); 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 duDebugDrawNavMeshWithClosedList(struct duDebugDraw* dd, const dtNavMesh& mesh, const dtNavMeshQuery& query, unsigned char flags);
void duDebugDrawNavMeshNodes(struct duDebugDraw* dd, const dtNavMeshQuery& query); void duDebugDrawNavMeshNodes(struct duDebugDraw* dd, const dtNavMeshQuery& query);
void duDebugDrawNavMeshBVTree(struct duDebugDraw* dd, const dtNavMesh& mesh); void duDebugDrawNavMeshBVTree(struct duDebugDraw* dd, const dtNavMesh& mesh);

View File

@ -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) void duDebugDrawNavMeshWithClosedList(struct duDebugDraw* dd, const dtNavMesh& mesh, const dtNavMeshQuery& query, unsigned char flags)
{ {
if (!dd) return; if (!dd) return;

View File

@ -19,6 +19,8 @@
#ifndef DETOURALLOCATOR_H #ifndef DETOURALLOCATOR_H
#define DETOURALLOCATOR_H #define DETOURALLOCATOR_H
#include <stddef.h>
/// Provides hint values to the memory allocator on how long the /// Provides hint values to the memory allocator on how long the
/// memory is expected to be used. /// memory is expected to be used.
enum dtAllocHint 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. // @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. // @return A pointer to the beginning of the allocated memory block, or null if the allocation failed.
/// @see dtAllocSetCustom /// @see dtAllocSetCustom
typedef void* (dtAllocFunc)(int size, dtAllocHint hint); typedef void* (dtAllocFunc)(size_t size, dtAllocHint hint);
/// A memory deallocation function. /// A memory deallocation function.
/// @param[in] ptr A pointer to a memory block previously allocated using #dtAllocFunc. /// @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. /// @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. /// @return A pointer to the beginning of the allocated memory block, or null if the allocation failed.
/// @see dtFree /// @see dtFree
void* dtAlloc(int size, dtAllocHint hint); void* dtAlloc(size_t size, dtAllocHint hint);
/// Deallocates a memory block. /// Deallocates a memory block.
/// @param[in] ptr A pointer to a memory block previously allocated using #dtAlloc. /// @param[in] ptr A pointer to a memory block previously allocated using #dtAlloc.

View File

@ -118,10 +118,9 @@ enum dtStraightPathOptions
}; };
/// Options for dtNavMeshQuery::findPath /// Options for dtNavMeshQuery::initSlicedFindPath and updateSlicedFindPath
enum dtFindPathOptions 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)
}; };
@ -146,7 +145,7 @@ enum dtPolyTypes
}; };
/// Defines a polyogn within a dtMeshTile object. /// Defines a polygon within a dtMeshTile object.
/// @ingroup detour /// @ingroup detour
struct dtPoly struct dtPoly
{ {
@ -301,6 +300,9 @@ struct dtMeshTile
int dataSize; ///< Size of the tile data. int dataSize; ///< Size of the tile data.
int flags; ///< Tile flags. (See: #dtTileFlags) int flags; ///< Tile flags. (See: #dtTileFlags)
dtMeshTile* next; ///< The next free tile, or the next tile in the spatial grid. 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. /// Configuration parameters used to define multi-tile navigation meshes.
@ -592,6 +594,9 @@ public:
/// @} /// @}
private: private:
// Explicitly disabled copy constructor and copy assignment operator.
dtNavMesh(const dtNavMesh&);
dtNavMesh& operator=(const dtNavMesh&);
/// Returns pointer to tile in the tile array. /// Returns pointer to tile in the tile array.
dtMeshTile* getTile(int i); dtMeshTile* getTile(int i);
@ -620,7 +625,7 @@ private:
void connectExtOffMeshLinks(dtMeshTile* tile, dtMeshTile* target, int side); void connectExtOffMeshLinks(dtMeshTile* tile, dtMeshTile* target, int side);
/// Removes external links at specified 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. // TODO: These methods are duplicates from dtNavMeshQuery, but are needed for off-mesh connection finding.

View File

@ -132,6 +132,9 @@ struct dtRaycastHit
/// hitNormal The normal of the nearest wall hit. [(x, y, z)] /// hitNormal The normal of the nearest wall hit. [(x, y, z)]
float hitNormal[3]; 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] /// Pointer to an array of reference ids of the visited polygons. [opt]
dtPolyRef* path; dtPolyRef* path;
@ -472,6 +475,9 @@ public:
/// @} /// @}
private: private:
// Explicitly disabled copy constructor and copy assignment operator
dtNavMeshQuery(const dtNavMeshQuery&);
dtNavMeshQuery& operator=(const dtNavMeshQuery&);
/// Returns neighbour tile based on side. /// Returns neighbour tile based on side.
dtMeshTile* getNeighbourTileAt(int x, int y, int side) const; dtMeshTile* getNeighbourTileAt(int x, int y, int side) const;

View File

@ -42,17 +42,13 @@ struct dtNode
dtPolyRef id; ///< Polygon ref the node corresponds to. 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 static const int DT_MAX_STATES_PER_NODE = 4; // number of extra states per node. See dtNode::state
class dtNodePool class dtNodePool
{ {
public: public:
dtNodePool(int maxNodes, int hashSize); dtNodePool(int maxNodes, int hashSize);
~dtNodePool(); ~dtNodePool();
inline void operator=(const dtNodePool&) {}
void clear(); void clear();
// Get a dtNode by ref and extra state information. If there is none then - allocate // 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 inline unsigned int getNodeIdx(const dtNode* node) const
{ {
if (!node) return 0; if (!node) return 0;
return (unsigned int)(node - m_nodes)+1; return (unsigned int)(node - m_nodes) + 1;
} }
inline dtNode* getNodeAtIdx(unsigned int idx) inline dtNode* getNodeAtIdx(unsigned int idx)
{ {
if (!idx) return 0; if (!idx) return 0;
return &m_nodes[idx-1]; return &m_nodes[idx - 1];
} }
inline const dtNode* getNodeAtIdx(unsigned int idx) const inline const dtNode* getNodeAtIdx(unsigned int idx) const
{ {
if (!idx) return 0; if (!idx) return 0;
return &m_nodes[idx-1]; return &m_nodes[idx - 1];
} }
inline int getMemUsed() const inline int getMemUsed() const
@ -95,6 +91,9 @@ public:
inline int getNodeCount() const { return m_nodeCount; } inline int getNodeCount() const { return m_nodeCount; }
private: private:
// Explicitly disabled copy constructor and copy assignment operator.
dtNodePool(const dtNodePool&);
dtNodePool& operator=(const dtNodePool&);
dtNode* m_nodes; dtNode* m_nodes;
dtNodeIndex* m_first; dtNodeIndex* m_first;
@ -109,17 +108,10 @@ class dtNodeQueue
public: public:
dtNodeQueue(int n); dtNodeQueue(int n);
~dtNodeQueue(); ~dtNodeQueue();
inline void operator=(dtNodeQueue&) {}
inline void clear() inline void clear() { m_size = 0; }
{
m_size = 0;
}
inline dtNode* top() inline dtNode* top() { return m_heap[0]; }
{
return m_heap[0];
}
inline dtNode* pop() inline dtNode* pop()
{ {
@ -152,12 +144,16 @@ public:
inline int getMemUsed() const inline int getMemUsed() const
{ {
return sizeof(*this) + return sizeof(*this) +
sizeof(dtNode*)*(m_capacity+1); sizeof(dtNode*) * (m_capacity + 1);
} }
inline int getCapacity() const { return m_capacity; } inline int getCapacity() const { return m_capacity; }
private: private:
// Explicitly disabled copy constructor and copy assignment operator.
dtNodeQueue(const dtNodeQueue&);
dtNodeQueue& operator=(const dtNodeQueue&);
void bubbleUp(int i, dtNode* node); void bubbleUp(int i, dtNode* node);
void trickleDown(int i, dtNode* node); void trickleDown(int i, dtNode* node);

View File

@ -19,7 +19,7 @@
#include <stdlib.h> #include <stdlib.h>
#include "DetourAlloc.h" #include "DetourAlloc.h"
static void *dtAllocDefault(int size, dtAllocHint) static void *dtAllocDefault(size_t size, dtAllocHint)
{ {
return malloc(size); return malloc(size);
} }
@ -38,7 +38,7 @@ void dtAllocSetCustom(dtAllocFunc *allocFunc, dtFreeFunc *freeFunc)
sFreeFunc = freeFunc ? freeFunc : dtFreeDefault; sFreeFunc = freeFunc ? freeFunc : dtFreeDefault;
} }
void* dtAlloc(int size, dtAllocHint hint) void* dtAlloc(size_t size, dtAllocHint hint)
{ {
return sAllocFunc(size, hint); return sAllocFunc(size, hint);
} }

View File

@ -350,7 +350,7 @@ int dtNavMesh::findConnectingPolys(const float* va, const float* vb,
return n; return n;
} }
void dtNavMesh::unconnectExtLinks(dtMeshTile* tile, dtMeshTile* target) void dtNavMesh::unconnectLinks(dtMeshTile* tile, dtMeshTile* target)
{ {
if (!tile || !target) return; if (!tile || !target) return;
@ -363,10 +363,9 @@ void dtNavMesh::unconnectExtLinks(dtMeshTile* tile, dtMeshTile* target)
unsigned int pj = DT_NULL_LINK; unsigned int pj = DT_NULL_LINK;
while (j != DT_NULL_LINK) while (j != DT_NULL_LINK)
{ {
if (tile->links[j].side != 0xff && if (decodePolyIdTile(tile->links[j].ref) == targetNum)
decodePolyIdTile(tile->links[j].ref) == targetNum)
{ {
// Revove link. // Remove link.
unsigned int nj = tile->links[j].next; unsigned int nj = tile->links[j].next;
if (pj == DT_NULL_LINK) if (pj == DT_NULL_LINK)
poly->firstLink = nj; 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 /// tile will be restored to the same values they were before the tile was
/// removed. /// 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 /// @see dtCreateNavMeshData, #removeTile
dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags, dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags,
dtTileRef lastRef, dtTileRef* result) dtTileRef lastRef, dtTileRef* result)
@ -1192,25 +1196,24 @@ dtStatus dtNavMesh::removeTile(dtTileRef ref, unsigned char** data, int* dataSiz
} }
// Remove connections to neighbour tiles. // Remove connections to neighbour tiles.
// Create connections with neighbour tiles.
static const int MAX_NEIS = 32; static const int MAX_NEIS = 32;
dtMeshTile* neis[MAX_NEIS]; dtMeshTile* neis[MAX_NEIS];
int nneis; 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); nneis = getTilesAt(tile->header->x, tile->header->y, neis, MAX_NEIS);
for (int j = 0; j < nneis; ++j) for (int j = 0; j < nneis; ++j)
{ {
if (neis[j] == tile) continue; 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) for (int i = 0; i < 8; ++i)
{ {
nneis = getNeighbourTilesAt(tile->header->x, tile->header->y, i, neis, MAX_NEIS); nneis = getNeighbourTilesAt(tile->header->x, tile->header->y, i, neis, MAX_NEIS);
for (int j = 0; j < nneis; ++j) for (int j = 0; j < nneis; ++j)
unconnectExtLinks(neis[j], tile); unconnectLinks(neis[j], tile);
} }
// Reset tile. // Reset tile.

View File

@ -712,7 +712,8 @@ dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* exten
{ {
dtAssert(m_nav); dtAssert(m_nav);
*nearestRef = 0; if (!nearestRef)
return DT_FAILURE | DT_INVALID_PARAM;
// Get nearby polygons from proximity grid. // Get nearby polygons from proximity grid.
const int MAX_SEARCH = 128; 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))) if (dtStatusFailed(queryPolygons(center, extents, filter, polys, &polyCount, MAX_SEARCH)))
return DT_FAILURE | DT_INVALID_PARAM; return DT_FAILURE | DT_INVALID_PARAM;
*nearestRef = 0;
if (polyCount == 0)
return DT_SUCCESS;
// Find nearest polygon amongst the nearby polygons. // Find nearest polygon amongst the nearby polygons.
dtPolyRef nearest = 0; dtPolyRef nearest = 0;
float nearestPoint[3];
float nearestDistanceSqr = FLT_MAX; float nearestDistanceSqr = FLT_MAX;
for (int i = 0; i < polyCount; ++i) for (int i = 0; i < polyCount; ++i)
{ {
@ -751,16 +759,18 @@ dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* exten
if (d < nearestDistanceSqr) if (d < nearestDistanceSqr)
{ {
if (nearestPt) dtVcopy(nearestPoint, closestPtPoly);
dtVcopy(nearestPt, closestPtPoly);
nearestDistanceSqr = d; nearestDistanceSqr = d;
nearest = ref; nearest = ref;
} }
} }
if (nearestRef)
*nearestRef = nearest; *nearestRef = nearest;
if (nearestPt)
dtVcopy(nearestPt, nearestPoint);
return DT_SUCCESS; return DT_SUCCESS;
} }
@ -2420,6 +2430,9 @@ dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, cons
hit->pathCount = n; hit->pathCount = n;
return status; return status;
} }
hit->hitEdgeIndex = segMax;
// Keep track of furthest t so far. // Keep track of furthest t so far.
if (tmax > hit->t) if (tmax > hit->t)
hit->t = tmax; hit->t = tmax;

View File

@ -164,6 +164,9 @@ public:
private: private:
// Explicitly disabled copy constructor and copy assignment operator.
dtTileCache(const dtTileCache&);
dtTileCache& operator=(const dtTileCache&);
enum ObstacleRequestAction enum ObstacleRequestAction
{ {
@ -203,7 +206,6 @@ private:
static const int MAX_UPDATE = 64; static const int MAX_UPDATE = 64;
dtCompressedTileRef m_update[MAX_UPDATE]; dtCompressedTileRef m_update[MAX_UPDATE];
int m_nupdate; int m_nupdate;
}; };
dtTileCache* dtAllocTileCache(); dtTileCache* dtAllocTileCache();

View File

@ -78,13 +78,11 @@ struct dtTileCachePolyMesh
struct dtTileCacheAlloc 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); return dtAlloc(size, DT_ALLOC_TEMP);
} }

View File

@ -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 NavMeshTileBuildContext(struct dtTileCacheAlloc* a) : layer(0), lcset(0), lmesh(0), alloc(a) {}
inline ~BuildContext() { purge(); } inline ~NavMeshTileBuildContext() { purge(); }
void purge() void purge()
{ {
dtFreeTileCacheLayer(alloc, layer); 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) if (m_nreqs >= MAX_REQUESTS)
return DT_FAILURE | DT_BUFFER_TOO_SMALL; return DT_FAILURE | DT_BUFFER_TOO_SMALL;
@ -384,7 +384,7 @@ dtObstacleRef dtTileCache::addObstacle(const float* pos, const float radius, con
return DT_SUCCESS; return DT_SUCCESS;
} }
dtObstacleRef dtTileCache::removeObstacle(const dtObstacleRef ref) dtStatus dtTileCache::removeObstacle(const dtObstacleRef ref)
{ {
if (!ref) if (!ref)
return DT_SUCCESS; return DT_SUCCESS;
@ -587,7 +587,7 @@ dtStatus dtTileCache::buildNavMeshTile(const dtCompressedTileRef ref, dtNavMesh*
m_talloc->reset(); m_talloc->reset();
BuildContext bc(m_talloc); NavMeshTileBuildContext bc(m_talloc);
const int walkableClimbVx = (int)(m_params.walkableClimb / m_params.ch); const int walkableClimbVx = (int)(m_params.walkableClimb / m_params.ch);
dtStatus status; dtStatus status;
@ -631,7 +631,11 @@ dtStatus dtTileCache::buildNavMeshTile(const dtCompressedTileRef ref, dtNavMesh*
// Early out if the mesh tile is empty. // Early out if the mesh tile is empty.
if (!bc.lmesh->npolys) 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; return DT_SUCCESS;
}
dtNavMeshCreateParams params; dtNavMeshCreateParams params;
memset(&params, 0, sizeof(params)); memset(&params, 0, sizeof(params));

View File

@ -127,7 +127,7 @@ public:
inline void resetTimers() { if (m_timerEnabled) doResetTimers(); } inline void resetTimers() { if (m_timerEnabled) doResetTimers(); }
/// Starts the specified performance timer. /// 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); } inline void startTimer(const rcTimerLabel label) { if (m_timerEnabled) doStartTimer(label); }
/// Stops the specified performance timer. /// Stops the specified performance timer.
@ -173,6 +173,26 @@ protected:
bool m_timerEnabled; 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. /// Specifies a configuration to use when performing Recast builds.
/// @ingroup recast /// @ingroup recast
struct rcConfig struct rcConfig
@ -245,7 +265,7 @@ struct rcConfig
/// Defines the number of bits allocated to rcSpan::smin and rcSpan::smax. /// Defines the number of bits allocated to rcSpan::smin and rcSpan::smax.
static const int RC_SPAN_HEIGHT_BITS = 13; static const int RC_SPAN_HEIGHT_BITS = 13;
/// Defines the maximum value for rcSpan::smin and rcSpan::smax. /// Defines the maximum value for rcSpan::smin and rcSpan::smax.
static const int RC_SPAN_MAX_HEIGHT = (1<<RC_SPAN_HEIGHT_BITS)-1; static const int RC_SPAN_MAX_HEIGHT = (1 << RC_SPAN_HEIGHT_BITS) - 1;
/// The number of spans allocated per span spool. /// The number of spans allocated per span spool.
/// @see rcSpanPool /// @see rcSpanPool
@ -255,8 +275,8 @@ static const int RC_SPANS_PER_POOL = 2048;
/// @see rcHeightfield /// @see rcHeightfield
struct rcSpan struct rcSpan
{ {
unsigned int smin : 13; ///< The lower limit of the span. [Limit: < #smax] unsigned int smin : RC_SPAN_HEIGHT_BITS; ///< The lower limit of the span. [Limit: < #smax]
unsigned int smax : 13; ///< The upper limit of the span. [Limit: <= #RC_SPAN_MAX_HEIGHT] unsigned int smax : RC_SPAN_HEIGHT_BITS; ///< The upper limit of the span. [Limit: <= #RC_SPAN_MAX_HEIGHT]
unsigned int area : 6; ///< The area id assigned to the span. unsigned int area : 6; ///< The area id assigned to the span.
rcSpan* next; ///< The next span higher up in column. rcSpan* next; ///< The next span higher up in column.
}; };
@ -376,6 +396,7 @@ struct rcContourSet
int width; ///< The width of the set. (Along the x-axis in cell units.) int width; ///< The width of the set. (Along the x-axis in cell units.)
int height; ///< The height of the set. (Along the z-axis in cell units.) int height; ///< The height of the set. (Along the z-axis in cell units.)
int borderSize; ///< The AABB border size used to generate the source data from which the contours were derived. int borderSize; ///< The AABB border size used to generate the source data from which the contours were derived.
float maxError; ///< The max edge error that this contour set was simplified with.
}; };
/// Represents a polygon mesh suitable for use in building a navigation mesh. /// Represents a polygon mesh suitable for use in building a navigation mesh.
@ -396,6 +417,7 @@ struct rcPolyMesh
float cs; ///< The size of each cell. (On the xz-plane.) float cs; ///< The size of each cell. (On the xz-plane.)
float ch; ///< The height of each cell. (The minimum increment along the y-axis.) float ch; ///< The height of each cell. (The minimum increment along the y-axis.)
int borderSize; ///< The AABB border size used to generate the source data from which the mesh was derived. int borderSize; ///< The AABB border size used to generate the source data from which the mesh was derived.
float maxEdgeError; ///< The max error of the polygon edges in the mesh.
}; };
/// Contains triangle meshes that represent detailed height data associated /// Contains triangle meshes that represent detailed height data associated
@ -497,6 +519,14 @@ void rcFreePolyMeshDetail(rcPolyMeshDetail* dmesh);
/// @see rcCompactSpan::reg /// @see rcCompactSpan::reg
static const unsigned short RC_BORDER_REG = 0x8000; static const unsigned short RC_BORDER_REG = 0x8000;
/// Polygon touches multiple regions.
/// If a polygon has this region ID it was merged with or created
/// from polygons of different regions during the polymesh
/// build step that removes redundant border vertices.
/// (Used during the polymesh and detail polymesh build processes)
/// @see rcPolyMesh::regs
static const unsigned short RC_MULTIPLE_REGS = 0;
/// Border vertex flag. /// Border vertex flag.
/// If a region ID has this bit set, then the associated element lies on /// If a region ID has this bit set, then the associated element lies on
/// a tile border. If a contour vertex's region ID has this bit set, the /// a tile border. If a contour vertex's region ID has this bit set, the
@ -747,6 +777,7 @@ void rcCalcGridSize(const float* bmin, const float* bmax, float cs, int* w, int*
/// @param[in] bmax The maximum bounds of the field's AABB. [(x, y, z)] [Units: wu] /// @param[in] bmax The maximum bounds of the field's AABB. [(x, y, z)] [Units: wu]
/// @param[in] cs The xz-plane cell size to use for the field. [Limit: > 0] [Units: wu] /// @param[in] cs The xz-plane cell size to use for the field. [Limit: > 0] [Units: wu]
/// @param[in] ch The y-axis cell size to use for field. [Limit: > 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, bool rcCreateHeightfield(rcContext* ctx, rcHeightfield& hf, int width, int height,
const float* bmin, const float* bmax, const float* bmin, const float* bmax,
float cs, float ch); 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] 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] area The area id of the span. [Limit: <= #RC_WALKABLE_AREA)
/// @param[in] flagMergeThr The merge theshold. [Limit: >= 0] [Units: vx] /// @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 short smin, const unsigned short smax,
const unsigned char area, const int flagMergeThr); 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,out] solid An initialized heightfield.
/// @param[in] flagMergeThr The distance where the walkable flag is favored over the non-walkable flag. /// @param[in] flagMergeThr The distance where the walkable flag is favored over the non-walkable flag.
/// [Limit: >= 0] [Units: vx] /// [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 unsigned char area, rcHeightfield& solid,
const int flagMergeThr = 1); 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,out] solid An initialized heightfield.
/// @param[in] flagMergeThr The distance where the walkable flag is favored over the non-walkable flag. /// @param[in] flagMergeThr The distance where the walkable flag is favored over the non-walkable flag.
/// [Limit: >= 0] [Units: vx] /// [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, const int* tris, const unsigned char* areas, const int nt,
rcHeightfield& solid, const int flagMergeThr = 1); 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,out] solid An initialized heightfield.
/// @param[in] flagMergeThr The distance where the walkable flag is favored over the non-walkable flag. /// @param[in] flagMergeThr The distance where the walkable flag is favored over the non-walkable flag.
/// [Limit: >= 0] [Units: vx] /// [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, const unsigned short* tris, const unsigned char* areas, const int nt,
rcHeightfield& solid, const int flagMergeThr = 1); 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,out] solid An initialized heightfield.
/// @param[in] flagMergeThr The distance where the walkable flag is favored over the non-walkable flag. /// @param[in] flagMergeThr The distance where the walkable flag is favored over the non-walkable flag.
/// [Limit: >= 0] [Units: vx] /// [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); rcHeightfield& solid, const int flagMergeThr = 1);
/// Marks non-walkable spans as walkable if their maximum is within @p walkableClimp of a walkable neihbor. /// 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. /// in the direction.
inline int rcGetDirOffsetX(int dir) 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]; return offset[dir&0x03];
} }
@ -1047,10 +1083,20 @@ inline int rcGetDirOffsetX(int dir)
/// in the direction. /// in the direction.
inline int rcGetDirOffsetY(int dir) 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]; 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 /// @name Layer, Contour, Polymesh, and Detail Mesh Functions
/// @see rcHeightfieldLayer, rcContourSet, rcPolyMesh, rcPolyMeshDetail /// @see rcHeightfieldLayer, rcContourSet, rcPolyMesh, rcPolyMeshDetail

View File

@ -19,6 +19,8 @@
#ifndef RECASTALLOC_H #ifndef RECASTALLOC_H
#define RECASTALLOC_H #define RECASTALLOC_H
#include <stddef.h>
/// Provides hint values to the memory allocator on how long the /// Provides hint values to the memory allocator on how long the
/// memory is expected to be used. /// memory is expected to be used.
enum rcAllocHint 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. // @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. // @return A pointer to the beginning of the allocated memory block, or null if the allocation failed.
/// @see rcAllocSetCustom /// @see rcAllocSetCustom
typedef void* (rcAllocFunc)(int size, rcAllocHint hint); typedef void* (rcAllocFunc)(size_t size, rcAllocHint hint);
/// A memory deallocation function. /// A memory deallocation function.
/// @param[in] ptr A pointer to a memory block previously allocated using #rcAllocFunc. /// @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. /// @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. /// @return A pointer to the beginning of the allocated memory block, or null if the allocation failed.
/// @see rcFree /// @see rcFree
void* rcAlloc(int size, rcAllocHint hint); void* rcAlloc(size_t size, rcAllocHint hint);
/// Deallocates a memory block. /// Deallocates a memory block.
/// @param[in] ptr A pointer to a memory block previously allocated using #rcAlloc. /// @param[in] ptr A pointer to a memory block previously allocated using #rcAlloc.
@ -62,42 +64,58 @@ class rcIntArray
{ {
int* m_data; int* m_data;
int m_size, m_cap; 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. /// 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. /// Constructs an instance initialized to the specified size.
/// @param[in] n The initial size of the integer array. /// @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); } rcIntArray(int n) : m_data(0), m_size(0), m_cap(0) { resize(n); }
inline ~rcIntArray() { rcFree(m_data); } ~rcIntArray() { rcFree(m_data); }
/// Specifies the new size of the integer array. /// Specifies the new size of the integer array.
/// @param[in] n 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. /// Push the specified integer onto the end of the array and increases the size by one.
/// @param[in] item The new value. /// @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. /// Returns the value at the end of the array and reduces the size by one.
/// @return The value at the end of the array. /// @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. /// The value at the specified array index.
/// @warning Does not provide overflow protection. /// @warning Does not provide overflow protection.
/// @param[in] i The index of the value. /// @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. /// The value at the specified array index.
/// @warning Does not provide overflow protection. /// @warning Does not provide overflow protection.
/// @param[in] i The index of the value. /// @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. /// 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. /// A simple helper class used to delete an array when it goes out of scope.
@ -119,6 +137,11 @@ public:
/// The root array pointer. /// The root array pointer.
/// @return The root array pointer. /// @return The root array pointer.
inline operator T*() { return ptr; } inline operator T*() { return ptr; }
private:
// Explicitly disabled copy constructor and copy assignment operator.
rcScopedDelete(const rcScopedDelete&);
rcScopedDelete& operator=(const rcScopedDelete&);
}; };
#endif #endif

View File

@ -329,7 +329,7 @@ bool rcBuildCompactHeightfield(rcContext* ctx, const int walkableHeight, const i
{ {
rcAssert(ctx); rcAssert(ctx);
ctx->startTimer(RC_TIMER_BUILD_COMPACTHEIGHTFIELD); rcScopedTimer timer(ctx, RC_TIMER_BUILD_COMPACTHEIGHTFIELD);
const int w = hf.width; const int w = hf.width;
const int h = hf.height; const int h = hf.height;
@ -457,8 +457,6 @@ bool rcBuildCompactHeightfield(rcContext* ctx, const int walkableHeight, const i
tooHighNeighbour, MAX_LAYERS); tooHighNeighbour, MAX_LAYERS);
} }
ctx->stopTimer(RC_TIMER_BUILD_COMPACTHEIGHTFIELD);
return true; return true;
} }

View File

@ -20,7 +20,7 @@
#include <string.h> #include <string.h>
#include "RecastAlloc.h" #include "RecastAlloc.h"
static void *rcAllocDefault(int size, rcAllocHint) static void *rcAllocDefault(size_t size, rcAllocHint)
{ {
return malloc(size); return malloc(size);
} }
@ -41,7 +41,7 @@ void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc)
} }
/// @see rcAllocSetCustom /// @see rcAllocSetCustom
void* rcAlloc(int size, rcAllocHint hint) void* rcAlloc(size_t size, rcAllocHint hint)
{ {
return sRecastAllocFunc(size, 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 /// Using this method ensures the array is at least large enough to hold
/// the specified number of elements. This can improve performance by /// the specified number of elements. This can improve performance by
/// avoiding auto-resizing during use. /// 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; if (!m_cap) m_cap = n;
while (m_cap < n) m_cap *= 2; while (m_cap < n) m_cap *= 2;
int* newData = (int*)rcAlloc(m_cap*sizeof(int), RC_ALLOC_TEMP); int* newData = (int*)rcAlloc(m_cap*sizeof(int), RC_ALLOC_TEMP);
if (m_size && newData) memcpy(newData, m_data, m_size*sizeof(int)); if (m_size && newData) memcpy(newData, m_data, m_size*sizeof(int));
rcFree(m_data); rcFree(m_data);
m_data = newData; m_data = newData;
}
m_size = n;
} }

View File

@ -41,7 +41,7 @@ bool rcErodeWalkableArea(rcContext* ctx, int radius, rcCompactHeightfield& chf)
const int w = chf.width; const int w = chf.width;
const int h = chf.height; 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); unsigned char* dist = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP);
if (!dist) if (!dist)
@ -215,8 +215,6 @@ bool rcErodeWalkableArea(rcContext* ctx, int radius, rcCompactHeightfield& chf)
rcFree(dist); rcFree(dist);
ctx->stopTimer(RC_TIMER_ERODE_AREA);
return true; return true;
} }
@ -245,7 +243,7 @@ bool rcMedianFilterWalkableArea(rcContext* ctx, rcCompactHeightfield& chf)
const int w = chf.width; const int w = chf.width;
const int h = chf.height; 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); unsigned char* areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP);
if (!areas) if (!areas)
@ -307,8 +305,6 @@ bool rcMedianFilterWalkableArea(rcContext* ctx, rcCompactHeightfield& chf)
rcFree(areas); rcFree(areas);
ctx->stopTimer(RC_TIMER_MEDIAN_AREA);
return true; return true;
} }
@ -322,7 +318,7 @@ void rcMarkBoxArea(rcContext* ctx, const float* bmin, const float* bmax, unsigne
{ {
rcAssert(ctx); 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 minx = (int)((bmin[0]-chf.bmin[0])/chf.cs);
int miny = (int)((bmin[1]-chf.bmin[1])/chf.ch); 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); rcAssert(ctx);
ctx->startTimer(RC_TIMER_MARK_CONVEXPOLY_AREA); rcScopedTimer timer(ctx, RC_TIMER_MARK_CONVEXPOLY_AREA);
float bmin[3], bmax[3]; float bmin[3], bmax[3];
rcVcopy(bmin, verts); 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, int rcOffsetPoly(const float* verts, const int nverts, const float offset,
@ -541,7 +532,7 @@ void rcMarkCylinderArea(rcContext* ctx, const float* pos,
{ {
rcAssert(ctx); rcAssert(ctx);
ctx->startTimer(RC_TIMER_MARK_CYLINDER_AREA); rcScopedTimer timer(ctx, RC_TIMER_MARK_CYLINDER_AREA);
float bmin[3], bmax[3]; float bmin[3], bmax[3];
bmin[0] = pos[0] - r; bmin[0] = pos[0] - r;
@ -597,6 +588,4 @@ void rcMarkCylinderArea(rcContext* ctx, const float* pos,
} }
} }
} }
ctx->stopTimer(RC_TIMER_MARK_CYLINDER_AREA);
} }

View File

@ -731,7 +731,7 @@ static void mergeRegionHoles(rcContext* ctx, rcContourRegion& region)
for (int i = 0; i < region.nholes; i++) for (int i = 0; i < region.nholes; i++)
maxVerts += region.holes[i].contour->nverts; maxVerts += region.holes[i].contour->nverts;
rcScopedDelete<rcPotentialDiagonal> diags = (rcPotentialDiagonal*)rcAlloc(sizeof(rcPotentialDiagonal)*maxVerts, RC_ALLOC_TEMP); rcScopedDelete<rcPotentialDiagonal> diags((rcPotentialDiagonal*)rcAlloc(sizeof(rcPotentialDiagonal)*maxVerts, RC_ALLOC_TEMP));
if (!diags) if (!diags)
{ {
ctx->log(RC_LOG_WARNING, "mergeRegionHoles: Failed to allocated diags %d.", maxVerts); 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 h = chf.height;
const int borderSize = chf.borderSize; 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.bmin, chf.bmin);
rcVcopy(cset.bmax, chf.bmax); rcVcopy(cset.bmax, chf.bmax);
@ -849,6 +849,7 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
cset.width = chf.width - chf.borderSize*2; cset.width = chf.width - chf.borderSize*2;
cset.height = chf.height - chf.borderSize*2; cset.height = chf.height - chf.borderSize*2;
cset.borderSize = chf.borderSize; cset.borderSize = chf.borderSize;
cset.maxError = maxError;
int maxContours = rcMax((int)chf.maxRegions, 8); int maxContours = rcMax((int)chf.maxRegions, 8);
cset.conts = (rcContour*)rcAlloc(sizeof(rcContour)*maxContours, RC_ALLOC_PERM); cset.conts = (rcContour*)rcAlloc(sizeof(rcContour)*maxContours, RC_ALLOC_PERM);
@ -856,7 +857,7 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
return false; return false;
cset.nconts = 0; cset.nconts = 0;
rcScopedDelete<unsigned char> flags = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP); rcScopedDelete<unsigned char> flags((unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP));
if (!flags) if (!flags)
{ {
ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'flags' (%d).", chf.spanCount); 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) if (cset.nconts > 0)
{ {
// Calculate winding of all polygons. // Calculate winding of all polygons.
rcScopedDelete<char> winding = (char*)rcAlloc(sizeof(char)*cset.nconts, RC_ALLOC_TEMP); rcScopedDelete<char> winding((char*)rcAlloc(sizeof(char)*cset.nconts, RC_ALLOC_TEMP));
if (!winding) if (!winding)
{ {
ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'hole' (%d).", cset.nconts); 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. // Collect outline contour and holes contours per region.
// We assume that there is one outline and multiple holes. // We assume that there is one outline and multiple holes.
const int nregions = chf.maxRegions+1; const int nregions = chf.maxRegions+1;
rcScopedDelete<rcContourRegion> regions = (rcContourRegion*)rcAlloc(sizeof(rcContourRegion)*nregions, RC_ALLOC_TEMP); rcScopedDelete<rcContourRegion> regions((rcContourRegion*)rcAlloc(sizeof(rcContourRegion)*nregions, RC_ALLOC_TEMP));
if (!regions) if (!regions)
{ {
ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'regions' (%d).", nregions); 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); memset(regions, 0, sizeof(rcContourRegion)*nregions);
rcScopedDelete<rcContourHole> holes = (rcContourHole*)rcAlloc(sizeof(rcContourHole)*cset.nconts, RC_ALLOC_TEMP); rcScopedDelete<rcContourHole> holes((rcContourHole*)rcAlloc(sizeof(rcContourHole)*cset.nconts, RC_ALLOC_TEMP));
if (!holes) if (!holes)
{ {
ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'holes' (%d).", cset.nconts); 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; return true;
} }

View File

@ -37,7 +37,7 @@ void rcFilterLowHangingWalkableObstacles(rcContext* ctx, const int walkableClimb
{ {
rcAssert(ctx); rcAssert(ctx);
ctx->startTimer(RC_TIMER_FILTER_LOW_OBSTACLES); rcScopedTimer timer(ctx, RC_TIMER_FILTER_LOW_OBSTACLES);
const int w = solid.width; const int w = solid.width;
const int h = solid.height; const int h = solid.height;
@ -67,8 +67,6 @@ void rcFilterLowHangingWalkableObstacles(rcContext* ctx, const int walkableClimb
} }
} }
} }
ctx->stopTimer(RC_TIMER_FILTER_LOW_OBSTACLES);
} }
/// @par /// @par
@ -86,7 +84,7 @@ void rcFilterLedgeSpans(rcContext* ctx, const int walkableHeight, const int walk
{ {
rcAssert(ctx); rcAssert(ctx);
ctx->startTimer(RC_TIMER_FILTER_BORDER); rcScopedTimer timer(ctx, RC_TIMER_FILTER_BORDER);
const int w = solid.width; const int w = solid.width;
const int h = solid.height; const int h = solid.height;
@ -156,19 +154,18 @@ void rcFilterLedgeSpans(rcContext* ctx, const int walkableHeight, const int walk
// The current span is close to a ledge if the drop to any // The current span is close to a ledge if the drop to any
// neighbour span is less than the walkableClimb. // neighbour span is less than the walkableClimb.
if (minh < -walkableClimb) if (minh < -walkableClimb)
{
s->area = RC_NULL_AREA; s->area = RC_NULL_AREA;
}
// If the difference between all neighbours is too large, // If the difference between all neighbours is too large,
// we are at steep slope, mark the span as ledge. // we are at steep slope, mark the span as ledge.
if ((asmax - asmin) > walkableClimb) else if ((asmax - asmin) > walkableClimb)
{ {
s->area = RC_NULL_AREA; s->area = RC_NULL_AREA;
} }
} }
} }
} }
ctx->stopTimer(RC_TIMER_FILTER_BORDER);
} }
/// @par /// @par
@ -181,7 +178,7 @@ void rcFilterWalkableLowHeightSpans(rcContext* ctx, int walkableHeight, rcHeight
{ {
rcAssert(ctx); rcAssert(ctx);
ctx->startTimer(RC_TIMER_FILTER_WALKABLE); rcScopedTimer timer(ctx, RC_TIMER_FILTER_WALKABLE);
const int w = solid.width; const int w = solid.width;
const int h = solid.height; const int h = solid.height;
@ -202,6 +199,4 @@ void rcFilterWalkableLowHeightSpans(rcContext* ctx, int walkableHeight, rcHeight
} }
} }
} }
ctx->stopTimer(RC_TIMER_FILTER_WALKABLE);
} }

View File

@ -87,12 +87,12 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf,
{ {
rcAssert(ctx); rcAssert(ctx);
ctx->startTimer(RC_TIMER_BUILD_LAYERS); rcScopedTimer timer(ctx, RC_TIMER_BUILD_LAYERS);
const int w = chf.width; const int w = chf.width;
const int h = chf.height; const int h = chf.height;
rcScopedDelete<unsigned char> srcReg = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP); rcScopedDelete<unsigned char> srcReg((unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP));
if (!srcReg) if (!srcReg)
{ {
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'srcReg' (%d).", chf.spanCount); 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); memset(srcReg,0xff,sizeof(unsigned char)*chf.spanCount);
const int nsweeps = chf.width; const int nsweeps = chf.width;
rcScopedDelete<rcLayerSweepSpan> sweeps = (rcLayerSweepSpan*)rcAlloc(sizeof(rcLayerSweepSpan)*nsweeps, RC_ALLOC_TEMP); rcScopedDelete<rcLayerSweepSpan> sweeps((rcLayerSweepSpan*)rcAlloc(sizeof(rcLayerSweepSpan)*nsweeps, RC_ALLOC_TEMP));
if (!sweeps) if (!sweeps)
{ {
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'sweeps' (%d).", nsweeps); 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. // Allocate and init layer regions.
const int nregs = (int)regId; const int nregs = (int)regId;
rcScopedDelete<rcLayerRegion> regs = (rcLayerRegion*)rcAlloc(sizeof(rcLayerRegion)*nregs, RC_ALLOC_TEMP); rcScopedDelete<rcLayerRegion> regs((rcLayerRegion*)rcAlloc(sizeof(rcLayerRegion)*nregs, RC_ALLOC_TEMP));
if (!regs) if (!regs)
{ {
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'regs' (%d).", nregs); 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 ay = y + rcGetDirOffsetY(dir);
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir); const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
const unsigned char rai = srcReg[ai]; 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); addUnique(regs[ri].neis, regs[ri].nneis, rai);
} }
} }
@ -446,10 +446,7 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf,
// No layers, return empty. // No layers, return empty.
if (layerId == 0) if (layerId == 0)
{
ctx->stopTimer(RC_TIMER_BUILD_LAYERS);
return true; return true;
}
// Create layers. // Create layers.
rcAssert(lset.layers == 0); rcAssert(lset.layers == 0);
@ -612,7 +609,5 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf,
layer->miny = layer->maxy = 0; layer->miny = layer->maxy = 0;
} }
ctx->stopTimer(RC_TIMER_BUILD_LAYERS);
return true; return true;
} }

View File

@ -528,7 +528,7 @@ static int getPolyMergeValue(unsigned short* pa, unsigned short* pb,
return dx*dx + dy*dy; return dx*dx + dy*dy;
} }
static void mergePolys(unsigned short* pa, unsigned short* pb, int ea, int eb, static void mergePolyVerts(unsigned short* pa, unsigned short* pb, int ea, int eb,
unsigned short* tmp, const int nvp) unsigned short* tmp, const int nvp)
{ {
const int na = countPolyVerts(pa, nvp); const int na = countPolyVerts(pa, nvp);
@ -601,7 +601,7 @@ static bool canRemoveVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned sho
// Find edges which share the removed vertex. // Find edges which share the removed vertex.
const int maxEdges = numTouchedVerts*2; const int maxEdges = numTouchedVerts*2;
int nedges = 0; int nedges = 0;
rcScopedDelete<int> edges = (int*)rcAlloc(sizeof(int)*maxEdges*3, RC_ALLOC_TEMP); rcScopedDelete<int> edges((int*)rcAlloc(sizeof(int)*maxEdges*3, RC_ALLOC_TEMP));
if (!edges) if (!edges)
{ {
ctx->log(RC_LOG_WARNING, "canRemoveVertex: Out of memory 'edges' (%d).", maxEdges*3); 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; int nedges = 0;
rcScopedDelete<int> edges = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp*4, RC_ALLOC_TEMP); rcScopedDelete<int> edges((int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp*4, RC_ALLOC_TEMP));
if (!edges) if (!edges)
{ {
ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'edges' (%d).", numRemovedVerts*nvp*4); ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'edges' (%d).", numRemovedVerts*nvp*4);
@ -689,7 +689,7 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short
} }
int nhole = 0; int nhole = 0;
rcScopedDelete<int> hole = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP); rcScopedDelete<int> hole((int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP));
if (!hole) if (!hole)
{ {
ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hole' (%d).", numRemovedVerts*nvp); ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hole' (%d).", numRemovedVerts*nvp);
@ -697,7 +697,7 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short
} }
int nhreg = 0; int nhreg = 0;
rcScopedDelete<int> hreg = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP); rcScopedDelete<int> hreg((int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP));
if (!hreg) if (!hreg)
{ {
ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hreg' (%d).", numRemovedVerts*nvp); 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; int nharea = 0;
rcScopedDelete<int> harea = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP); rcScopedDelete<int> harea((int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP));
if (!harea) if (!harea)
{ {
ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'harea' (%d).", numRemovedVerts*nvp); 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; break;
} }
rcScopedDelete<int> tris = (int*)rcAlloc(sizeof(int)*nhole*3, RC_ALLOC_TEMP); rcScopedDelete<int> tris((int*)rcAlloc(sizeof(int)*nhole*3, RC_ALLOC_TEMP));
if (!tris) if (!tris)
{ {
ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tris' (%d).", nhole*3); ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tris' (%d).", nhole*3);
return false; return false;
} }
rcScopedDelete<int> tverts = (int*)rcAlloc(sizeof(int)*nhole*4, RC_ALLOC_TEMP); rcScopedDelete<int> tverts((int*)rcAlloc(sizeof(int)*nhole*4, RC_ALLOC_TEMP));
if (!tverts) if (!tverts)
{ {
ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tverts' (%d).", nhole*4); ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tverts' (%d).", nhole*4);
return false; return false;
} }
rcScopedDelete<int> thole = (int*)rcAlloc(sizeof(int)*nhole, RC_ALLOC_TEMP); rcScopedDelete<int> thole((int*)rcAlloc(sizeof(int)*nhole, RC_ALLOC_TEMP));
if (!thole) if (!thole)
{ {
ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'thole' (%d).", nhole); 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. // Merge the hole triangles back to polygons.
rcScopedDelete<unsigned short> polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*(ntris+1)*nvp, RC_ALLOC_TEMP); rcScopedDelete<unsigned short> polys((unsigned short*)rcAlloc(sizeof(unsigned short)*(ntris+1)*nvp, RC_ALLOC_TEMP));
if (!polys) if (!polys)
{ {
ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'polys' (%d).", (ntris+1)*nvp); ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'polys' (%d).", (ntris+1)*nvp);
return false; return false;
} }
rcScopedDelete<unsigned short> pregs = (unsigned short*)rcAlloc(sizeof(unsigned short)*ntris, RC_ALLOC_TEMP); rcScopedDelete<unsigned short> pregs((unsigned short*)rcAlloc(sizeof(unsigned short)*ntris, RC_ALLOC_TEMP));
if (!pregs) if (!pregs)
{ {
ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'pregs' (%d).", ntris); ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'pregs' (%d).", ntris);
return false; return false;
} }
rcScopedDelete<unsigned char> pareas = (unsigned char*)rcAlloc(sizeof(unsigned char)*ntris, RC_ALLOC_TEMP); rcScopedDelete<unsigned char> pareas((unsigned char*)rcAlloc(sizeof(unsigned char)*ntris, RC_ALLOC_TEMP));
if (!pareas) if (!pareas)
{ {
ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'pareas' (%d).", ntris); 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+0] = (unsigned short)hole[t[0]];
polys[npolys*nvp+1] = (unsigned short)hole[t[1]]; polys[npolys*nvp+1] = (unsigned short)hole[t[1]];
polys[npolys*nvp+2] = (unsigned short)hole[t[2]]; polys[npolys*nvp+2] = (unsigned short)hole[t[2]];
// 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]]; pregs[npolys] = (unsigned short)hreg[t[0]];
pareas[npolys] = (unsigned char)harea[t[0]]; pareas[npolys] = (unsigned char)harea[t[0]];
npolys++; npolys++;
} }
@ -936,7 +943,10 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short
// Found best, merge. // Found best, merge.
unsigned short* pa = &polys[bestPa*nvp]; unsigned short* pa = &polys[bestPa*nvp];
unsigned short* pb = &polys[bestPb*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]; unsigned short* last = &polys[(npolys-1)*nvp];
if (pb != last) if (pb != last)
memcpy(pb, last, sizeof(unsigned short)*nvp); memcpy(pb, last, sizeof(unsigned short)*nvp);
@ -983,13 +993,14 @@ bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMe
{ {
rcAssert(ctx); rcAssert(ctx);
ctx->startTimer(RC_TIMER_BUILD_POLYMESH); rcScopedTimer timer(ctx, RC_TIMER_BUILD_POLYMESH);
rcVcopy(mesh.bmin, cset.bmin); rcVcopy(mesh.bmin, cset.bmin);
rcVcopy(mesh.bmax, cset.bmax); rcVcopy(mesh.bmax, cset.bmax);
mesh.cs = cset.cs; mesh.cs = cset.cs;
mesh.ch = cset.ch; mesh.ch = cset.ch;
mesh.borderSize = cset.borderSize; mesh.borderSize = cset.borderSize;
mesh.maxEdgeError = cset.maxError;
int maxVertices = 0; int maxVertices = 0;
int maxTris = 0; int maxTris = 0;
@ -1009,7 +1020,7 @@ bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMe
return false; return false;
} }
rcScopedDelete<unsigned char> vflags = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxVertices, RC_ALLOC_TEMP); rcScopedDelete<unsigned char> vflags((unsigned char*)rcAlloc(sizeof(unsigned char)*maxVertices, RC_ALLOC_TEMP));
if (!vflags) if (!vflags)
{ {
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'vflags' (%d).", maxVertices); 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.regs, 0, sizeof(unsigned short)*maxTris);
memset(mesh.areas, 0, sizeof(unsigned char)*maxTris); memset(mesh.areas, 0, sizeof(unsigned char)*maxTris);
rcScopedDelete<int> nextVert = (int*)rcAlloc(sizeof(int)*maxVertices, RC_ALLOC_TEMP); rcScopedDelete<int> nextVert((int*)rcAlloc(sizeof(int)*maxVertices, RC_ALLOC_TEMP));
if (!nextVert) if (!nextVert)
{ {
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'nextVert' (%d).", maxVertices); 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); memset(nextVert, 0, sizeof(int)*maxVertices);
rcScopedDelete<int> firstVert = (int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP); rcScopedDelete<int> firstVert((int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP));
if (!firstVert) if (!firstVert)
{ {
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'firstVert' (%d).", VERTEX_BUCKET_COUNT); 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) for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i)
firstVert[i] = -1; firstVert[i] = -1;
rcScopedDelete<int> indices = (int*)rcAlloc(sizeof(int)*maxVertsPerCont, RC_ALLOC_TEMP); rcScopedDelete<int> indices((int*)rcAlloc(sizeof(int)*maxVertsPerCont, RC_ALLOC_TEMP));
if (!indices) if (!indices)
{ {
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'indices' (%d).", maxVertsPerCont); ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'indices' (%d).", maxVertsPerCont);
return false; return false;
} }
rcScopedDelete<int> tris = (int*)rcAlloc(sizeof(int)*maxVertsPerCont*3, RC_ALLOC_TEMP); rcScopedDelete<int> tris((int*)rcAlloc(sizeof(int)*maxVertsPerCont*3, RC_ALLOC_TEMP));
if (!tris) if (!tris)
{ {
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'tris' (%d).", maxVertsPerCont*3); ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'tris' (%d).", maxVertsPerCont*3);
return false; return false;
} }
rcScopedDelete<unsigned short> polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*(maxVertsPerCont+1)*nvp, RC_ALLOC_TEMP); rcScopedDelete<unsigned short> polys((unsigned short*)rcAlloc(sizeof(unsigned short)*(maxVertsPerCont+1)*nvp, RC_ALLOC_TEMP));
if (!polys) if (!polys)
{ {
ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'polys' (%d).", maxVertsPerCont*nvp); 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. // Found best, merge.
unsigned short* pa = &polys[bestPa*nvp]; unsigned short* pa = &polys[bestPa*nvp];
unsigned short* pb = &polys[bestPb*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]; unsigned short* lastPoly = &polys[(npolys-1)*nvp];
if (pb != lastPoly) if (pb != lastPoly)
memcpy(pb, lastPoly, sizeof(unsigned short)*nvp); 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->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; return true;
} }
@ -1306,7 +1315,7 @@ bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, r
if (!nmeshes || !meshes) if (!nmeshes || !meshes)
return true; return true;
ctx->startTimer(RC_TIMER_MERGE_POLYMESH); rcScopedTimer timer(ctx, RC_TIMER_MERGE_POLYMESH);
mesh.nvp = meshes[0]->nvp; mesh.nvp = meshes[0]->nvp;
mesh.cs = meshes[0]->cs; 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); memset(mesh.flags, 0, sizeof(unsigned short)*maxPolys);
rcScopedDelete<int> nextVert = (int*)rcAlloc(sizeof(int)*maxVerts, RC_ALLOC_TEMP); rcScopedDelete<int> nextVert((int*)rcAlloc(sizeof(int)*maxVerts, RC_ALLOC_TEMP));
if (!nextVert) if (!nextVert)
{ {
ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'nextVert' (%d).", maxVerts); 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); memset(nextVert, 0, sizeof(int)*maxVerts);
rcScopedDelete<int> firstVert = (int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP); rcScopedDelete<int> firstVert((int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP));
if (!firstVert) if (!firstVert)
{ {
ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'firstVert' (%d).", VERTEX_BUCKET_COUNT); 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) for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i)
firstVert[i] = -1; firstVert[i] = -1;
rcScopedDelete<unsigned short> vremap = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxVertsPerMesh, RC_ALLOC_PERM); rcScopedDelete<unsigned short> vremap((unsigned short*)rcAlloc(sizeof(unsigned short)*maxVertsPerMesh, RC_ALLOC_PERM));
if (!vremap) if (!vremap)
{ {
ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'vremap' (%d).", maxVertsPerMesh); 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->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; return true;
} }
@ -1499,6 +1506,7 @@ bool rcCopyPolyMesh(rcContext* ctx, const rcPolyMesh& src, rcPolyMesh& dst)
dst.cs = src.cs; dst.cs = src.cs;
dst.ch = src.ch; dst.ch = src.ch;
dst.borderSize = src.borderSize; dst.borderSize = src.borderSize;
dst.maxEdgeError = src.maxEdgeError;
dst.verts = (unsigned short*)rcAlloc(sizeof(unsigned short)*src.nverts*3, RC_ALLOC_PERM); dst.verts = (unsigned short*)rcAlloc(sizeof(unsigned short)*src.nverts*3, RC_ALLOC_PERM);
if (!dst.verts) if (!dst.verts)

View File

@ -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, static unsigned short getHeight(const float fx, const float fy, const float fz,
const float /*cs*/, const float ics, const float ch, 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 ix = (int)floorf(fx*ics + 0.01f);
int iz = (int)floorf(fz*ics + 0.01f); int iz = (int)floorf(fz*ics + 0.01f);
@ -212,17 +212,26 @@ static unsigned short getHeight(const float fx, const float fy, const float fz,
if (h == RC_UNSET_HEIGHT) if (h == RC_UNSET_HEIGHT)
{ {
// Special case when data might be bad. // Special case when data might be bad.
// Find nearest neighbour pixel which has valid height. // Walk adjacent cells in a spiral up to 'radius', and look
const int off[8*2] = { -1,0, -1,-1, 0,-1, 1,-1, 1,0, 1,1, 0,1, -1,1}; // for a pixel which has a valid height.
float dmin = FLT_MAX; int x = 1, z = 0, dx = 1, dz = 0;
for (int i = 0; i < 8; ++i) int maxSize = radius * 2 + 1;
{ int maxIter = maxSize * maxSize - 1;
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;
int nextRingIterStart = 8;
int nextRingIters = 16;
float dmin = FLT_MAX;
for (int i = 0; i < maxIter; i++)
{
const int nx = ix + x;
const int nz = iz + z;
if (nx >= 0 && nz >= 0 && nx < hp.width && nz < hp.height)
{
const unsigned short nh = hp.data[nx + nz*hp.width];
if (nh != RC_UNSET_HEIGHT)
{
const float d = fabsf(nh*ch - fy); const float d = fabsf(nh*ch - fy);
if (d < dmin) if (d < dmin)
{ {
@ -231,6 +240,43 @@ static unsigned short getHeight(const float fx, const float fy, const float fz,
} }
} }
} }
// 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; 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 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; } 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; 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, static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin,
const float sampleDist, const float sampleMaxError, const float sampleDist, const float sampleMaxError,
const rcCompactHeightfield& chf, const rcHeightPatch& hp, const int heightSearchRadius, const rcCompactHeightfield& chf,
float* verts, int& nverts, rcIntArray& tris, const rcHeightPatch& hp, float* verts, int& nverts,
rcIntArray& edges, rcIntArray& samples) rcIntArray& tris, rcIntArray& edges, rcIntArray& samples)
{ {
static const int MAX_VERTS = 127; 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). 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[0] = vj[0] + dx*u;
pos[1] = vj[1] + dy*u; pos[1] = vj[1] + dy*u;
pos[2] = vj[2] + dz*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. // Simplify samples.
int idx[MAX_VERTS_PER_EDGE] = {0,nn}; 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. // Make sure the samples are not too close to the edges.
if (distToPoly(nin,in,pt) > -sampleDist/2) continue; if (distToPoly(nin,in,pt) > -sampleDist/2) continue;
samples.push(x); 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(z);
samples.push(0); // Not added samples.push(0); // Not added
} }
@ -834,33 +880,25 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin,
return true; return true;
} }
static void seedArrayWithPolyCenter(rcContext* ctx, const rcCompactHeightfield& chf,
static void getHeightDataSeedsFromVertices(const rcCompactHeightfield& chf,
const unsigned short* poly, const int npoly, const unsigned short* poly, const int npoly,
const unsigned short* verts, const int bs, const unsigned short* verts, const int bs,
rcHeightPatch& hp, rcIntArray& stack) 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) // Note: Reads to the compact heightfield are offset by border size (bs)
// since border size offset is already removed from the polymesh vertices. // 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] = static const int offset[9*2] =
{ {
0,0, -1,-1, 0,-1, 1,-1, 1,0, 1,1, 0,1, -1,1, -1,0, 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. // Find cell closest to a poly vertex
for (int j = 0; j < npoly; ++j) int startCellX = 0, startCellY = 0, startSpanIndex = -1;
{
int cx = 0, cz = 0, ci =-1;
int dmin = RC_UNSET_HEIGHT; int dmin = RC_UNSET_HEIGHT;
for (int k = 0; k < 9; ++k) for (int j = 0; j < npoly && dmin > 0; ++j)
{
for (int k = 0; k < 9 && dmin > 0; ++k)
{ {
const int ax = (int)verts[poly[j]*3+0] + offset[k*2+0]; const int ax = (int)verts[poly[j]*3+0] + offset[k*2+0];
const int ay = (int)verts[poly[j]*3+1]; const int ay = (int)verts[poly[j]*3+1];
@ -870,123 +908,139 @@ static void getHeightDataSeedsFromVertices(const rcCompactHeightfield& chf,
continue; continue;
const rcCompactCell& c = chf.cells[(ax+bs)+(az+bs)*chf.width]; 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]; const rcCompactSpan& s = chf.spans[i];
int d = rcAbs(ay - (int)s.y); int d = rcAbs(ay - (int)s.y);
if (d < dmin) if (d < dmin)
{ {
cx = ax; startCellX = ax;
cz = az; startCellY = az;
ci = i; startSpanIndex = i;
dmin = d; dmin = d;
} }
} }
} }
if (ci != -1)
{
stack.push(cx);
stack.push(cz);
stack.push(ci);
}
} }
// Find center of the polygon using flood fill. rcAssert(startSpanIndex != -1);
int pcx = 0, pcz = 0; // Find center of the polygon
int pcx = 0, pcy = 0;
for (int j = 0; j < npoly; ++j) for (int j = 0; j < npoly; ++j)
{ {
pcx += (int)verts[poly[j]*3+0]; pcx += (int)verts[poly[j]*3+0];
pcz += (int)verts[poly[j]*3+2]; pcy += (int)verts[poly[j]*3+2];
} }
pcx /= npoly; 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);
int cx = stack[i+0]; array.push(startCellX);
int cy = stack[i+1]; array.push(startCellY);
int idx = cx-hp.xmin+(cy-hp.ymin)*hp.width; array.push(startSpanIndex);
hp.data[idx] = 1;
}
while (stack.size() > 0) 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 ci = stack.pop(); if (array.size() < 3)
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)
{ {
stack.resize(0); ctx->log(RC_LOG_WARNING, "Walk towards polygon center failed to reach center");
stack.push(cx);
stack.push(cy);
stack.push(ci);
break; 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]; const rcCompactSpan& cs = chf.spans[ci];
for (int i = 0; i < 4; i++)
for (int dir = 0; dir < 4; ++dir)
{ {
if (rcGetCon(cs, dir) == RC_NOT_CONNECTED) continue; int dir = dirs[i];
if (rcGetCon(cs, dir) == RC_NOT_CONNECTED)
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))
continue; 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; continue;
const int ai = (int)chf.cells[(ax+bs)+(ay+bs)*chf.width].index + rcGetCon(cs, dir); if (hp.data[hpx+hpy*hp.width] != 0)
continue;
int idx = ax-hp.xmin+(ay-hp.ymin)*hp.width; hp.data[hpx+hpy*hp.width] = 1;
hp.data[idx] = 1; array.push(newX);
array.push(newY);
array.push((int)chf.cells[(newX+bs)+(newY+bs)*chf.width].index + rcGetCon(cs, dir));
}
stack.push(ax); rcSwap(dirs[directDir], dirs[3]);
stack.push(ay);
stack.push(ai);
}
} }
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); 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]; const rcCompactSpan& cs = chf.spans[ci];
hp.data[idx] = cs.y; hp.data[cx-hp.xmin+(cy-hp.ymin)*hp.width] = cs.y;
// getHeightData seeds are given in coordinates with borders
stack[i+0] += bs;
stack[i+1] += bs;
}
} }
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* poly, const int npoly,
const unsigned short* verts, const int bs, const unsigned short* verts, const int bs,
rcHeightPatch& hp, rcIntArray& stack, rcHeightPatch& hp, rcIntArray& queue,
int region) int region)
{ {
// Note: Reads to the compact heightfield are offset by border size (bs) // Note: Reads to the compact heightfield are offset by border size (bs)
// since border size offset is already removed from the polymesh vertices. // 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); memset(hp.data, 0xff, sizeof(unsigned short)*hp.width*hp.height);
bool empty = true; bool empty = true;
// 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)
{
// Copy the height from the same region, and mark region borders // Copy the height from the same region, and mark region borders
// as seed points to fill the rest. // as seed points to fill the rest.
for (int hy = 0; hy < hp.height; hy++) for (int hy = 0; hy < hp.height; hy++)
@ -995,8 +1049,8 @@ static void getHeightData(const rcCompactHeightfield& chf,
for (int hx = 0; hx < hp.width; hx++) for (int hx = 0; hx < hp.width; hx++)
{ {
int x = hp.xmin + hx + bs; int x = hp.xmin + hx + bs;
const rcCompactCell& c = chf.cells[x+y*chf.width]; const rcCompactCell& c = chf.cells[x + y*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; ++i)
{ {
const rcCompactSpan& s = chf.spans[i]; const rcCompactSpan& s = chf.spans[i];
if (s.reg == region) if (s.reg == region)
@ -1014,7 +1068,7 @@ static void getHeightData(const rcCompactHeightfield& chf,
{ {
const int ax = x + rcGetDirOffsetX(dir); const int ax = x + rcGetDirOffsetX(dir);
const int ay = y + rcGetDirOffsetY(dir); const int ay = y + rcGetDirOffsetY(dir);
const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir); const int ai = (int)chf.cells[ax + ay*chf.width].index + rcGetCon(s, dir);
const rcCompactSpan& as = chf.spans[ai]; const rcCompactSpan& as = chf.spans[ai];
if (as.reg != region) if (as.reg != region)
{ {
@ -1024,37 +1078,38 @@ static void getHeightData(const rcCompactHeightfield& chf,
} }
} }
if (border) if (border)
{ push3(queue, x, y, i);
stack.push(x);
stack.push(y);
stack.push(i);
}
break; break;
} }
} }
} }
} }
}
// if the polygon does not contian any points from the current region (rare, but happens) // if the polygon does not contain 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 // or if it could potentially be overlapping polygons of the same region,
// then use the center as the seed point.
if (empty) 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; static const int RETRACT_SIZE = 256;
int head = 0; 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 cx = queue[head*3+0];
int cy = stack[head*3+1]; int cy = queue[head*3+1];
int ci = stack[head*3+2]; int ci = queue[head*3+2];
head++; head++;
if (head >= RETRACT_SIZE) if (head >= RETRACT_SIZE)
{ {
head = 0; head = 0;
if (stack.size() > RETRACT_SIZE*3) if (queue.size() > RETRACT_SIZE*3)
memmove(&stack[0], &stack[RETRACT_SIZE*3], sizeof(int)*(stack.size()-RETRACT_SIZE*3)); memmove(&queue[0], &queue[RETRACT_SIZE*3], sizeof(int)*(queue.size()-RETRACT_SIZE*3));
stack.resize(stack.size()-RETRACT_SIZE*3); queue.resize(queue.size()-RETRACT_SIZE*3);
} }
const rcCompactSpan& cs = chf.spans[ci]; 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 hx = ax - hp.xmin - bs;
const int hy = ay - hp.ymin - 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; continue;
if (hp.data[hx + hy*hp.width] != RC_UNSET_HEIGHT) 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; hp.data[hx + hy*hp.width] = as.y;
stack.push(ax); push3(queue, ax, ay, ai);
stack.push(ay);
stack.push(ai);
} }
} }
} }
@ -1120,7 +1173,7 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa
{ {
rcAssert(ctx); rcAssert(ctx);
ctx->startTimer(RC_TIMER_BUILD_POLYMESHDETAIL); rcScopedTimer timer(ctx, RC_TIMER_BUILD_POLYMESHDETAIL);
if (mesh.nverts == 0 || mesh.npolys == 0) if (mesh.nverts == 0 || mesh.npolys == 0)
return true; return true;
@ -1130,23 +1183,24 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa
const float ch = mesh.ch; const float ch = mesh.ch;
const float* orig = mesh.bmin; const float* orig = mesh.bmin;
const int borderSize = mesh.borderSize; const int borderSize = mesh.borderSize;
const int heightSearchRadius = rcMax(1, (int)ceilf(mesh.maxEdgeError));
rcIntArray edges(64); rcIntArray edges(64);
rcIntArray tris(512); rcIntArray tris(512);
rcIntArray stack(512); rcIntArray arr(512);
rcIntArray samples(512); rcIntArray samples(512);
float verts[256*3]; float verts[256*3];
rcHeightPatch hp; rcHeightPatch hp;
int nPolyVerts = 0; int nPolyVerts = 0;
int maxhw = 0, maxhh = 0; int maxhw = 0, maxhh = 0;
rcScopedDelete<int> bounds = (int*)rcAlloc(sizeof(int)*mesh.npolys*4, RC_ALLOC_TEMP); rcScopedDelete<int> bounds((int*)rcAlloc(sizeof(int)*mesh.npolys*4, RC_ALLOC_TEMP));
if (!bounds) if (!bounds)
{ {
ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'bounds' (%d).", mesh.npolys*4); ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'bounds' (%d).", mesh.npolys*4);
return false; return false;
} }
rcScopedDelete<float> poly = (float*)rcAlloc(sizeof(float)*nvp*3, RC_ALLOC_TEMP); rcScopedDelete<float> poly((float*)rcAlloc(sizeof(float)*nvp*3, RC_ALLOC_TEMP));
if (!poly) if (!poly)
{ {
ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'poly' (%d).", nvp*3); 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.ymin = bounds[i*4+2];
hp.width = bounds[i*4+1]-bounds[i*4+0]; hp.width = bounds[i*4+1]-bounds[i*4+0];
hp.height = bounds[i*4+3]-bounds[i*4+2]; 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. // Build detail mesh.
int nverts = 0; int nverts = 0;
if (!buildPolyDetail(ctx, poly, npoly, if (!buildPolyDetail(ctx, poly, npoly,
sampleDist, sampleMaxError, sampleDist, sampleMaxError,
chf, hp, verts, nverts, tris, heightSearchRadius, chf, hp,
verts, nverts, tris,
edges, samples)) edges, samples))
{ {
return false; return false;
@ -1327,8 +1382,6 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa
} }
} }
ctx->stopTimer(RC_TIMER_BUILD_POLYMESHDETAIL);
return true; return true;
} }
@ -1337,7 +1390,7 @@ bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int
{ {
rcAssert(ctx); rcAssert(ctx);
ctx->startTimer(RC_TIMER_MERGE_POLYMESHDETAIL); rcScopedTimer timer(ctx, RC_TIMER_MERGE_POLYMESHDETAIL);
int maxVerts = 0; int maxVerts = 0;
int maxTris = 0; int maxTris = 0;
@ -1406,7 +1459,5 @@ bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int
} }
} }
ctx->stopTimer(RC_TIMER_MERGE_POLYMESHDETAIL);
return true; return true;
} }

View File

@ -82,7 +82,7 @@ static void freeSpan(rcHeightfield& hf, rcSpan* ptr)
hf.freelist = 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 short smin, const unsigned short smax,
const unsigned char area, const int flagMergeThr) 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; int idx = x + y*hf.width;
rcSpan* s = allocSpan(hf); rcSpan* s = allocSpan(hf);
if (!s)
return false;
s->smin = smin; s->smin = smin;
s->smax = smax; s->smax = smax;
s->area = area; s->area = area;
@ -99,7 +101,7 @@ static void addSpan(rcHeightfield& hf, const int x, const int y,
if (!hf.spans[idx]) if (!hf.spans[idx])
{ {
hf.spans[idx] = s; hf.spans[idx] = s;
return; return true;
} }
rcSpan* prev = 0; rcSpan* prev = 0;
rcSpan* cur = hf.spans[idx]; 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]; s->next = hf.spans[idx];
hf.spans[idx] = s; hf.spans[idx] = s;
} }
return true;
} }
/// @par /// @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. /// from the existing span, the span flags are merged.
/// ///
/// @see rcHeightfield, rcSpan. /// @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 short smin, const unsigned short smax,
const unsigned char area, const int flagMergeThr) const unsigned char area, const int flagMergeThr)
{ {
// rcAssert(ctx); rcAssert(ctx);
addSpan(hf, x,y, smin, smax, area, flagMergeThr);
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 // 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 unsigned char area, rcHeightfield& hf,
const float* bmin, const float* bmax, const float* bmin, const float* bmax,
const float cs, const float ics, const float ich, 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 the triangle does not touch the bbox of the heightfield, skip the triagle.
if (!overlapBounds(bmin, bmax, tmin, tmax)) if (!overlapBounds(bmin, bmax, tmin, tmax))
return; return true;
// Calculate the footprint of the triangle on the grid's y-axis // Calculate the footprint of the triangle on the grid's y-axis
int y0 = (int)((tmin[2] - bmin[2])*ics); 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 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); 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 /// @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. /// No spans will be added if the triangle does not overlap the heightfield grid.
/// ///
/// @see rcHeightfield /// @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 unsigned char area, rcHeightfield& solid,
const int flagMergeThr) const int flagMergeThr)
{ {
rcAssert(ctx); rcAssert(ctx);
ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES); rcScopedTimer timer(ctx, RC_TIMER_RASTERIZE_TRIANGLES);
const float ics = 1.0f/solid.cs; const float ics = 1.0f/solid.cs;
const float ich = 1.0f/solid.ch; 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 /// @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. /// Spans will only be added for triangles that overlap the heightfield grid.
/// ///
/// @see rcHeightfield /// @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, const int* tris, const unsigned char* areas, const int nt,
rcHeightfield& solid, const int flagMergeThr) rcHeightfield& solid, const int flagMergeThr)
{ {
rcAssert(ctx); rcAssert(ctx);
ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES); rcScopedTimer timer(ctx, RC_TIMER_RASTERIZE_TRIANGLES);
const float ics = 1.0f/solid.cs; const float ics = 1.0f/solid.cs;
const float ich = 1.0f/solid.ch; 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* v0 = &verts[tris[i*3+0]*3];
const float* v1 = &verts[tris[i*3+1]*3]; const float* v1 = &verts[tris[i*3+1]*3];
const float* v2 = &verts[tris[i*3+2]*3]; const float* v2 = &verts[tris[i*3+2]*3];
// Rasterize. // 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 /// @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. /// Spans will only be added for triangles that overlap the heightfield grid.
/// ///
/// @see rcHeightfield /// @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, const unsigned short* tris, const unsigned char* areas, const int nt,
rcHeightfield& solid, const int flagMergeThr) rcHeightfield& solid, const int flagMergeThr)
{ {
rcAssert(ctx); rcAssert(ctx);
ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES); rcScopedTimer timer(ctx, RC_TIMER_RASTERIZE_TRIANGLES);
const float ics = 1.0f/solid.cs; const float ics = 1.0f/solid.cs;
const float ich = 1.0f/solid.ch; 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* v1 = &verts[tris[i*3+1]*3];
const float* v2 = &verts[tris[i*3+2]*3]; const float* v2 = &verts[tris[i*3+2]*3];
// Rasterize. // 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 /// @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. /// Spans will only be added for triangles that overlap the heightfield grid.
/// ///
/// @see rcHeightfield /// @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) rcHeightfield& solid, const int flagMergeThr)
{ {
rcAssert(ctx); rcAssert(ctx);
ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES); rcScopedTimer timer(ctx, RC_TIMER_RASTERIZE_TRIANGLES);
const float ics = 1.0f/solid.cs; const float ics = 1.0f/solid.cs;
const float ich = 1.0f/solid.ch; 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* v1 = &verts[(i*3+1)*3];
const float* v2 = &verts[(i*3+2)*3]; const float* v2 = &verts[(i*3+2)*3];
// Rasterize. // 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;
} }

View File

@ -1041,7 +1041,7 @@ static void addUniqueConnection(rcRegion& reg, int n)
static bool mergeAndFilterLayerRegions(rcContext* ctx, int minRegionArea, static bool mergeAndFilterLayerRegions(rcContext* ctx, int minRegionArea,
unsigned short& maxRegionId, unsigned short& maxRegionId,
rcCompactHeightfield& chf, rcCompactHeightfield& chf,
unsigned short* srcReg, rcIntArray& overlaps) unsigned short* srcReg, rcIntArray& /*overlaps*/)
{ {
const int w = chf.width; const int w = chf.width;
const int h = chf.height; const int h = chf.height;
@ -1257,7 +1257,7 @@ bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf)
{ {
rcAssert(ctx); rcAssert(ctx);
ctx->startTimer(RC_TIMER_BUILD_DISTANCEFIELD); rcScopedTimer timer(ctx, RC_TIMER_BUILD_DISTANCEFIELD);
if (chf.dist) if (chf.dist)
{ {
@ -1281,14 +1281,15 @@ bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf)
unsigned short maxDist = 0; unsigned short maxDist = 0;
ctx->startTimer(RC_TIMER_BUILD_DISTANCEFIELD_DIST); {
rcScopedTimer timerDist(ctx, RC_TIMER_BUILD_DISTANCEFIELD_DIST);
calculateDistanceField(chf, src, maxDist); calculateDistanceField(chf, src, maxDist);
chf.maxDistance = maxDist; chf.maxDistance = maxDist;
}
ctx->stopTimer(RC_TIMER_BUILD_DISTANCEFIELD_DIST); {
rcScopedTimer timerBlur(ctx, RC_TIMER_BUILD_DISTANCEFIELD_BLUR);
ctx->startTimer(RC_TIMER_BUILD_DISTANCEFIELD_BLUR);
// Blur // Blur
if (boxBlur(chf, 1, src, dst) != src) if (boxBlur(chf, 1, src, dst) != src)
@ -1296,10 +1297,7 @@ bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf)
// Store distance. // Store distance.
chf.dist = src; chf.dist = src;
}
ctx->stopTimer(RC_TIMER_BUILD_DISTANCEFIELD_BLUR);
ctx->stopTimer(RC_TIMER_BUILD_DISTANCEFIELD);
rcFree(dst); rcFree(dst);
@ -1359,13 +1357,13 @@ bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf,
{ {
rcAssert(ctx); rcAssert(ctx);
ctx->startTimer(RC_TIMER_BUILD_REGIONS); rcScopedTimer timer(ctx, RC_TIMER_BUILD_REGIONS);
const int w = chf.width; const int w = chf.width;
const int h = chf.height; const int h = chf.height;
unsigned short id = 1; unsigned short id = 1;
rcScopedDelete<unsigned short> srcReg = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP); rcScopedDelete<unsigned short> srcReg((unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP));
if (!srcReg) if (!srcReg)
{ {
ctx->log(RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'src' (%d).", chf.spanCount); 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); memset(srcReg,0,sizeof(unsigned short)*chf.spanCount);
const int nsweeps = rcMax(chf.width,chf.height); const int nsweeps = rcMax(chf.width,chf.height);
rcScopedDelete<rcSweepSpan> sweeps = (rcSweepSpan*)rcAlloc(sizeof(rcSweepSpan)*nsweeps, RC_ALLOC_TEMP); rcScopedDelete<rcSweepSpan> sweeps((rcSweepSpan*)rcAlloc(sizeof(rcSweepSpan)*nsweeps, RC_ALLOC_TEMP));
if (!sweeps) if (!sweeps)
{ {
ctx->log(RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'sweeps' (%d).", nsweeps); ctx->log(RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'sweeps' (%d).", nsweeps);
@ -1489,7 +1487,8 @@ 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. // Merge regions and filter out small regions.
rcIntArray overlaps; rcIntArray overlaps;
@ -1498,15 +1497,12 @@ bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf,
return false; return false;
// Monotone partitioning does not generate overlapping regions. // Monotone partitioning does not generate overlapping regions.
}
ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER);
// Store the result out. // Store the result out.
for (int i = 0; i < chf.spanCount; ++i) for (int i = 0; i < chf.spanCount; ++i)
chf.spans[i].reg = srcReg[i]; chf.spans[i].reg = srcReg[i];
ctx->stopTimer(RC_TIMER_BUILD_REGIONS);
return true; return true;
} }
@ -1534,12 +1530,12 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
{ {
rcAssert(ctx); rcAssert(ctx);
ctx->startTimer(RC_TIMER_BUILD_REGIONS); rcScopedTimer timer(ctx, RC_TIMER_BUILD_REGIONS);
const int w = chf.width; const int w = chf.width;
const int h = chf.height; const int h = chf.height;
rcScopedDelete<unsigned short> buf = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount*4, RC_ALLOC_TEMP); rcScopedDelete<unsigned short> buf((unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount*4, RC_ALLOC_TEMP));
if (!buf) if (!buf)
{ {
ctx->log(RC_LOG_ERROR, "rcBuildRegions: Out of memory 'tmp' (%d).", chf.spanCount*4); 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. // Make sure border will not overflow.
const int bw = rcMin(w, borderSize); const int bw = rcMin(w, borderSize);
const int bh = rcMin(h, borderSize); const int bh = rcMin(h, borderSize);
if (regionId > 0xFFFB)
{
ctx->log(RC_LOG_ERROR, "rcBuildRegions: Region ID overflow");
return false;
}
// Paint regions // Paint regions
paintRectRegion(0, bw, 0, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++; 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++; paintRectRegion(w-bw, w, 0, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++;
@ -1603,7 +1606,8 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
// ctx->stopTimer(RC_TIMER_DIVIDE_TO_LEVELS); // ctx->stopTimer(RC_TIMER_DIVIDE_TO_LEVELS);
ctx->startTimer(RC_TIMER_BUILD_REGIONS_EXPAND); {
rcScopedTimer timerExpand(ctx, RC_TIMER_BUILD_REGIONS_EXPAND);
// Expand current regions until no empty connected cells found. // Expand current regions until no empty connected cells found.
if (expandRegions(expandIters, level, chf, srcReg, srcDist, dstReg, dstDist, lvlStacks[sId], false) != srcReg) if (expandRegions(expandIters, level, chf, srcReg, srcDist, dstReg, dstDist, lvlStacks[sId], false) != srcReg)
@ -1611,13 +1615,13 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
rcSwap(srcReg, dstReg); rcSwap(srcReg, dstReg);
rcSwap(srcDist, dstDist); rcSwap(srcDist, dstDist);
} }
}
ctx->stopTimer(RC_TIMER_BUILD_REGIONS_EXPAND); {
rcScopedTimer timerFloor(ctx, RC_TIMER_BUILD_REGIONS_FLOOD);
ctx->startTimer(RC_TIMER_BUILD_REGIONS_FLOOD);
// Mark new regions with IDs. // Mark new regions with IDs.
for (int j=0; j<lvlStacks[sId].size(); j+=3) for (int j = 0; j<lvlStacks[sId].size(); j += 3)
{ {
int x = lvlStacks[sId][j]; int x = lvlStacks[sId][j];
int y = lvlStacks[sId][j+1]; int y = lvlStacks[sId][j+1];
@ -1625,11 +1629,18 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
if (i >= 0 && srcReg[i] == 0) if (i >= 0 && srcReg[i] == 0)
{ {
if (floodRegion(x, y, i, level, regionId, chf, srcReg, srcDist, stack)) 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++; regionId++;
} }
} }
}
ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FLOOD); }
} }
// Expand current regions until no empty connected cells found. // Expand current regions until no empty connected cells found.
@ -1641,7 +1652,8 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
ctx->stopTimer(RC_TIMER_BUILD_REGIONS_WATERSHED); ctx->stopTimer(RC_TIMER_BUILD_REGIONS_WATERSHED);
ctx->startTimer(RC_TIMER_BUILD_REGIONS_FILTER); {
rcScopedTimer timerFilter(ctx, RC_TIMER_BUILD_REGIONS_FILTER);
// Merge regions and filter out smalle regions. // Merge regions and filter out smalle regions.
rcIntArray overlaps; rcIntArray overlaps;
@ -1654,15 +1666,12 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
{ {
ctx->log(RC_LOG_ERROR, "rcBuildRegions: %d overlapping regions.", overlaps.size()); ctx->log(RC_LOG_ERROR, "rcBuildRegions: %d overlapping regions.", overlaps.size());
} }
}
ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER);
// Write the result out. // Write the result out.
for (int i = 0; i < chf.spanCount; ++i) for (int i = 0; i < chf.spanCount; ++i)
chf.spans[i].reg = srcReg[i]; chf.spans[i].reg = srcReg[i];
ctx->stopTimer(RC_TIMER_BUILD_REGIONS);
return true; return true;
} }
@ -1672,13 +1681,13 @@ bool rcBuildLayerRegions(rcContext* ctx, rcCompactHeightfield& chf,
{ {
rcAssert(ctx); rcAssert(ctx);
ctx->startTimer(RC_TIMER_BUILD_REGIONS); rcScopedTimer timer(ctx, RC_TIMER_BUILD_REGIONS);
const int w = chf.width; const int w = chf.width;
const int h = chf.height; const int h = chf.height;
unsigned short id = 1; unsigned short id = 1;
rcScopedDelete<unsigned short> srcReg = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP); rcScopedDelete<unsigned short> srcReg((unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP));
if (!srcReg) if (!srcReg)
{ {
ctx->log(RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'src' (%d).", chf.spanCount); 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); memset(srcReg,0,sizeof(unsigned short)*chf.spanCount);
const int nsweeps = rcMax(chf.width,chf.height); const int nsweeps = rcMax(chf.width,chf.height);
rcScopedDelete<rcSweepSpan> sweeps = (rcSweepSpan*)rcAlloc(sizeof(rcSweepSpan)*nsweeps, RC_ALLOC_TEMP); rcScopedDelete<rcSweepSpan> sweeps((rcSweepSpan*)rcAlloc(sizeof(rcSweepSpan)*nsweeps, RC_ALLOC_TEMP));
if (!sweeps) if (!sweeps)
{ {
ctx->log(RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'sweeps' (%d).", nsweeps); 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); {
rcScopedTimer timerFilter(ctx, RC_TIMER_BUILD_REGIONS_FILTER);
// Merge monotone regions to layers and remove small regions. // Merge monotone regions to layers and remove small regions.
rcIntArray overlaps; rcIntArray overlaps;
chf.maxRegions = id; chf.maxRegions = id;
if (!mergeAndFilterLayerRegions(ctx, minRegionArea, chf.maxRegions, chf, srcReg, overlaps)) if (!mergeAndFilterLayerRegions(ctx, minRegionArea, chf.maxRegions, chf, srcReg, overlaps))
return false; return false;
}
ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER);
// Store the result out. // Store the result out.
for (int i = 0; i < chf.spanCount; ++i) for (int i = 0; i < chf.spanCount; ++i)
chf.spans[i].reg = srcReg[i]; chf.spans[i].reg = srcReg[i];
ctx->stopTimer(RC_TIMER_BUILD_REGIONS);
return true; return true;
} }