diff options
author | Sergey Sharybin <sergey.vfx@gmail.com> | 2011-10-07 22:57:26 +0400 |
---|---|---|
committer | Sergey Sharybin <sergey.vfx@gmail.com> | 2011-10-07 22:57:26 +0400 |
commit | f8b081153bec2268197551a23016768c0dc45cd0 (patch) | |
tree | bf53516fe86913769e74b81f1c351c6a0d3af9f9 /extern | |
parent | 424f6463c1bb8a632d7c875123b16cf6d5a4660a (diff) | |
parent | cc43a5bbde5fd74d848009d0dc5d35779230b12c (diff) |
Merging r40653 through r40847 from trunk into soc-2011-tomato
Diffstat (limited to 'extern')
16 files changed, 4883 insertions, 1667 deletions
diff --git a/extern/recastnavigation/CMakeLists.txt b/extern/recastnavigation/CMakeLists.txt index 660b881dd07..3d8b7ab1513 100644 --- a/extern/recastnavigation/CMakeLists.txt +++ b/extern/recastnavigation/CMakeLists.txt @@ -53,18 +53,19 @@ set(SRC Detour/Include/DetourTileNavMeshBuilder.h Recast/Source/Recast.cpp + Recast/Source/RecastAlloc.cpp + Recast/Source/RecastArea.cpp Recast/Source/RecastContour.cpp Recast/Source/RecastFilter.cpp - Recast/Source/RecastLog.cpp + Recast/Source/RecastLayers.cpp Recast/Source/RecastMesh.cpp Recast/Source/RecastMeshDetail.cpp Recast/Source/RecastRasterization.cpp Recast/Source/RecastRegion.cpp - Recast/Source/RecastTimer.cpp Recast/Include/Recast.h - Recast/Include/RecastLog.h - Recast/Include/RecastTimer.h + Recast/Include/RecastAlloc.h + Recast/Include/RecastAssert.h ) blender_add_lib(extern_recastnavigation "${SRC}" "${INC}" "${INC_SYS}") diff --git a/extern/recastnavigation/Recast/Include/Recast.h b/extern/recastnavigation/Recast/Include/Recast.h index f25ab47f8fa..4e20b0f0fff 100644 --- a/extern/recastnavigation/Recast/Include/Recast.h +++ b/extern/recastnavigation/Recast/Include/Recast.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2009 Mikko Mononen memon@inside.org +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -19,298 +19,685 @@ #ifndef RECAST_H #define RECAST_H -// The units of the parameters are specified in parenthesis as follows: -// (vx) voxels, (wu) world units -struct rcConfig +/// The value of PI used by Recast. +static const float RC_PI = 3.14159265f; + +/// Recast log categories. +/// @see rcContext +enum rcLogCategory { - int width, height; // Dimensions of the rasterized heighfield (vx) - int tileSize; // Width and Height of a tile (vx) - int borderSize; // Non-navigable Border around the heightfield (vx) - float cs, ch; // Grid cell size and height (wu) - float bmin[3], bmax[3]; // Grid bounds (wu) - float walkableSlopeAngle; // Maximum walkble slope angle in degrees. - int walkableHeight; // Minimum height where the agent can still walk (vx) - int walkableClimb; // Maximum height between grid cells the agent can climb (vx) - int walkableRadius; // Radius of the agent in cells (vx) - int maxEdgeLen; // Maximum contour edge length (vx) - float maxSimplificationError; // Maximum distance error from contour to cells (vx) - int minRegionSize; // Minimum regions size. Smaller regions will be deleted (vx) - int mergeRegionSize; // Minimum regions size. Smaller regions will be merged (vx) - int maxVertsPerPoly; // Max number of vertices per polygon - float detailSampleDist; // Detail mesh sample spacing. - float detailSampleMaxError; // Detail mesh simplification max sample error. + RC_LOG_PROGRESS = 1, ///< A progress log entry. + RC_LOG_WARNING, ///< A warning log entry. + RC_LOG_ERROR, ///< An error log entry. }; -// Heightfield span. -struct rcSpan +/// Recast performance timer categories. +/// @see rcContext +enum rcTimerLabel +{ + /// The user defined total time of the build. + RC_TIMER_TOTAL, + /// A user defined build time. + RC_TIMER_TEMP, + /// The time to rasterize the triangles. (See: #rcRasterizeTriangle) + RC_TIMER_RASTERIZE_TRIANGLES, + /// The time to build the compact heightfield. (See: #rcBuildCompactHeightfield) + RC_TIMER_BUILD_COMPACTHEIGHTFIELD, + /// The total time to build the contours. (See: #rcBuildContours) + RC_TIMER_BUILD_CONTOURS, + /// The time to trace the boundaries of the contours. (See: #rcBuildContours) + RC_TIMER_BUILD_CONTOURS_TRACE, + /// The time to simplify the contours. (See: #rcBuildContours) + RC_TIMER_BUILD_CONTOURS_SIMPLIFY, + /// The time to filter ledge spans. (See: #rcFilterLedgeSpans) + RC_TIMER_FILTER_BORDER, + /// The time to filter low height spans. (See: #rcFilterWalkableLowHeightSpans) + RC_TIMER_FILTER_WALKABLE, + /// The time to apply the median filter. (See: #rcMedianFilterWalkableArea) + RC_TIMER_MEDIAN_AREA, + /// The time to filter low obstacles. (See: #rcFilterLowHangingWalkableObstacles) + RC_TIMER_FILTER_LOW_OBSTACLES, + /// The time to build the polygon mesh. (See: #rcBuildPolyMesh) + RC_TIMER_BUILD_POLYMESH, + /// The time to merge polygon meshes. (See: #rcMergePolyMeshes) + RC_TIMER_MERGE_POLYMESH, + /// The time to erode the walkable area. (See: #rcErodeWalkableArea) + RC_TIMER_ERODE_AREA, + /// The time to mark a box area. (See: #rcMarkBoxArea) + RC_TIMER_MARK_BOX_AREA, + /// The time to mark a cylinder area. (See: #rcMarkCylinderArea) + RC_TIMER_MARK_CYLINDER_AREA, + /// The time to mark a convex polygon area. (See: #rcMarkConvexPolyArea) + RC_TIMER_MARK_CONVEXPOLY_AREA, + /// The total time to build the distance field. (See: #rcBuildDistanceField) + RC_TIMER_BUILD_DISTANCEFIELD, + /// The time to build the distances of the distance field. (See: #rcBuildDistanceField) + RC_TIMER_BUILD_DISTANCEFIELD_DIST, + /// The time to blur the distance field. (See: #rcBuildDistanceField) + RC_TIMER_BUILD_DISTANCEFIELD_BLUR, + /// The total time to build the regions. (See: #rcBuildRegions, #rcBuildRegionsMonotone) + RC_TIMER_BUILD_REGIONS, + /// The total time to apply the watershed algorithm. (See: #rcBuildRegions) + RC_TIMER_BUILD_REGIONS_WATERSHED, + /// The time to expand regions while applying the watershed algorithm. (See: #rcBuildRegions) + RC_TIMER_BUILD_REGIONS_EXPAND, + /// The time to flood regions while applying the watershed algorithm. (See: #rcBuildRegions) + RC_TIMER_BUILD_REGIONS_FLOOD, + /// The time to filter out small regions. (See: #rcBuildRegions, #rcBuildRegionsMonotone) + RC_TIMER_BUILD_REGIONS_FILTER, + /// The time to build heightfield layers. (See: #rcBuildHeightfieldLayers) + RC_TIMER_BUILD_LAYERS, + /// The time to build the polygon mesh detail. (See: #rcBuildPolyMeshDetail) + RC_TIMER_BUILD_POLYMESHDETAIL, + /// The time to merge polygon mesh details. (See: #rcMergePolyMeshDetails) + RC_TIMER_MERGE_POLYMESHDETAIL, + /// The maximum number of timers. (Used for iterating timers.) + RC_MAX_TIMERS +}; + +/// Provides an interface for optional logging and performance tracking of the Recast +/// build process. +/// @ingroup recast +class rcContext +{ +public: + + /// Contructor. + /// @param[in] state TRUE if the logging and performance timers should be enabled. [Default: true] + inline rcContext(bool state = true) : m_logEnabled(state), m_timerEnabled(state) {} + virtual ~rcContext() {} + + /// Enables or disables logging. + /// @param[in] state TRUE if logging should be enabled. + inline void enableLog(bool state) { m_logEnabled = state; } + + /// Clears all log entries. + inline void resetLog() { if (m_logEnabled) doResetLog(); } + + /// Logs a message. + /// @param[in] category The category of the message. + /// @param[in] format The message. + void log(const rcLogCategory category, const char* format, ...); + + /// Enables or disables the performance timers. + /// @param[in] state TRUE if timers should be enabled. + inline void enableTimer(bool state) { m_timerEnabled = state; } + + /// Clears all peformance timers. (Resets all to unused.) + inline void resetTimers() { if (m_timerEnabled) doResetTimers(); } + + /// Starts the specified performance timer. + /// @param label The category of timer. + inline void startTimer(const rcTimerLabel label) { if (m_timerEnabled) doStartTimer(label); } + + /// Stops the specified performance timer. + /// @param label The category of the timer. + inline void stopTimer(const rcTimerLabel label) { if (m_timerEnabled) doStopTimer(label); } + + /// Returns the total accumulated time of the specified performance timer. + /// @param label The category of the timer. + /// @return The accumulated time of the timer, or -1 if timers are disabled or the timer has never been started. + inline int getAccumulatedTime(const rcTimerLabel label) const { return m_timerEnabled ? doGetAccumulatedTime(label) : -1; } + +protected: + + /// Clears all log entries. + virtual void doResetLog() {} + + /// Logs a message. + /// @param[in] category The category of the message. + /// @param[in] msg The formatted message. + /// @param[in] len The length of the formatted message. + virtual void doLog(const rcLogCategory /*category*/, const char* /*msg*/, const int /*len*/) {} + + /// Clears all timers. (Resets all to unused.) + virtual void doResetTimers() {} + + /// Starts the specified performance timer. + /// @param[in] label The category of timer. + virtual void doStartTimer(const rcTimerLabel /*label*/) {} + + /// Stops the specified performance timer. + /// @param[in] label The category of the timer. + virtual void doStopTimer(const rcTimerLabel /*label*/) {} + + /// Returns the total accumulated time of the specified performance timer. + /// @param[in] label The category of the timer. + /// @return The accumulated time of the timer, or -1 if timers are disabled or the timer has never been started. + virtual int doGetAccumulatedTime(const rcTimerLabel /*label*/) const { return -1; } + + /// True if logging is enabled. + bool m_logEnabled; + + /// True if the performance timers are enabled. + bool m_timerEnabled; +}; + +/// Specifies a configuration to use when performing Recast builds. +/// @ingroup recast +struct rcConfig { - unsigned int smin : 15; // Span min height. - unsigned int smax : 15; // Span max height. - unsigned int flags : 2; // Span flags. - rcSpan* next; // Next span in column. + /// The width of the field along the x-axis. [Limit: >= 0] [Units: vx] + int width; + + /// The height of the field along the z-axis. [Limit: >= 0] [Units: vx] + int height; + + /// The width/height size of tile's on the xz-plane. [Limit: >= 0] [Units: vx] + int tileSize; + + /// The size of the non-navigable border around the heightfield. [Limit: >=0] [Units: vx] + int borderSize; + + /// The xz-plane cell size to use for fields. [Limit: > 0] [Units: wu] + float cs; + + /// The y-axis cell size to use for fields. [Limit: > 0] [Units: wu] + float ch; + + /// The minimum bounds of the field's AABB. [(x, y, z)] [Units: wu] + float bmin[3]; + + /// The maximum bounds of the field's AABB. [(x, y, z)] [Units: wu] + float bmax[3]; + + /// The maximum slope that is considered walkable. [Limits: 0 <= value < 90] [Units: Degrees] + float walkableSlopeAngle; + + /// Minimum floor to 'ceiling' height that will still allow the floor area to + /// be considered walkable. [Limit: >= 3] [Units: vx] + int walkableHeight; + + /// Maximum ledge height that is considered to still be traversable. [Limit: >=0] [Units: vx] + int walkableClimb; + + /// The distance to erode/shrink the walkable area of the heightfield away from + /// obstructions. [Limit: >=0] [Units: vx] + int walkableRadius; + + /// The maximum allowed length for contour edges along the border of the mesh. [Limit: >=0] [Units: vx] + int maxEdgeLen; + + /// The maximum distance a simplfied contour's border edges should deviate + /// the original raw contour. [Limit: >=0] [Units: wu] + float maxSimplificationError; + + /// The minimum number of cells allowed to form isolated island areas. [Limit: >=0] [Units: vx] + int minRegionArea; + + /// Any regions with a span count smaller than this value will, if possible, + /// be merged with larger regions. [Limit: >=0] [Units: vx] + int mergeRegionArea; + + /// The maximum number of vertices allowed for polygons generated during the + /// contour to polygon conversion process. [Limit: >= 3] + int maxVertsPerPoly; + + /// Sets the sampling distance to use when generating the detail mesh. + /// (For height detail only.) [Limits: 0 or >= 0.9] [Units: wu] + float detailSampleDist; + + /// The maximum distance the detail mesh surface should deviate from heightfield + /// data. (For height detail only.) [Limit: >=0] [Units: wu] + float detailSampleMaxError; }; +/// Defines the number of bits allocated to rcSpan::smin and rcSpan::smax. +static const int RC_SPAN_HEIGHT_BITS = 13; +/// Defines the maximum value for rcSpan::smin and rcSpan::smax. +static const int RC_SPAN_MAX_HEIGHT = (1<<RC_SPAN_HEIGHT_BITS)-1; + +/// The number of spans allocated per span spool. +/// @see rcSpanPool static const int RC_SPANS_PER_POOL = 2048; -// Memory pool used for quick span allocation. +/// Represents a span in a heightfield. +/// @see rcHeightfield +struct rcSpan +{ + unsigned int smin : 13; ///< 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 area : 6; ///< The area id assigned to the span. + rcSpan* next; ///< The next span higher up in column. +}; + +/// A memory pool used for quick allocation of spans within a heightfield. +/// @see rcHeightfield struct rcSpanPool { - rcSpanPool* next; // Pointer to next pool. - rcSpan items[1]; // Array of spans (size RC_SPANS_PER_POOL). + rcSpanPool* next; ///< The next span pool. + rcSpan items[RC_SPANS_PER_POOL]; ///< Array of spans in the pool. }; -// Dynamic span-heightfield. +/// A dynamic heightfield representing obstructed space. +/// @ingroup recast struct rcHeightfield { - inline rcHeightfield() : width(0), height(0), spans(0), pools(0), freelist(0) {} - inline ~rcHeightfield() - { - // Delete span array. - delete [] spans; - // Delete span pools. - while (pools) - { - rcSpanPool* next = pools->next; - delete [] reinterpret_cast<unsigned char*>(pools); - pools = next; - } - } - int width, height; // Dimension of the heightfield. - float bmin[3], bmax[3]; // Bounding box of the heightfield - float cs, ch; // Cell size and height. - rcSpan** spans; // Heightfield of spans (width*height). - rcSpanPool* pools; // Linked list of span pools. - rcSpan* freelist; // Pointer to next free span. + int width; ///< The width of the heightfield. (Along the x-axis in cell units.) + int height; ///< The height of the heightfield. (Along the z-axis in cell units.) + float bmin[3]; ///< The minimum bounds in world space. [(x, y, z)] + float bmax[3]; ///< The maximum bounds in world space. [(x, y, z)] + 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.) + rcSpan** spans; ///< Heightfield of spans (width*height). + rcSpanPool* pools; ///< Linked list of span pools. + rcSpan* freelist; ///< The next free span. }; +/// Provides information on the content of a cell column in a compact heightfield. struct rcCompactCell { - unsigned int index : 24; // Index to first span in column. - unsigned int count : 8; // Number of spans in this column. + unsigned int index : 24; ///< Index to the first span in the column. + unsigned int count : 8; ///< Number of spans in the column. }; +/// Represents a span of unobstructed space within a compact heightfield. struct rcCompactSpan { - unsigned short y; // Bottom coordinate of the span. - unsigned short reg; // Region ID - unsigned short dist; // Distance to border - unsigned short con; // Connections to neighbour cells. - unsigned char h; // Height of the span. - unsigned char flags; // Flags. + unsigned short y; ///< The lower extent of the span. (Measured from the heightfield's base.) + unsigned short reg; ///< The id of the region the span belongs to. (Or zero if not in a region.) + unsigned int con : 24; ///< Packed neighbor connection data. + unsigned int h : 8; ///< The height of the span. (Measured from #y.) }; -// Compact static heightfield. +/// A compact, static heightfield representing unobstructed space. +/// @ingroup recast struct rcCompactHeightfield { - inline rcCompactHeightfield() : maxDistance(0), maxRegions(0), cells(0), spans(0) {} - inline ~rcCompactHeightfield() { delete [] cells; delete [] spans; } - int width, height; // Width and height of the heighfield. - int spanCount; // Number of spans in the heightfield. - int walkableHeight, walkableClimb; // Agent properties. - unsigned short maxDistance; // Maximum distance value stored in heightfield. - unsigned short maxRegions; // Maximum Region Id stored in heightfield. - float bmin[3], bmax[3]; // Bounding box of the heightfield. - float cs, ch; // Cell size and height. - rcCompactCell* cells; // Pointer to width*height cells. - rcCompactSpan* spans; // Pointer to spans. + int width; ///< The width of the heightfield. (Along the x-axis in cell units.) + int height; ///< The height of the heightfield. (Along the z-axis in cell units.) + int spanCount; ///< The number of spans in the heightfield. + int walkableHeight; ///< The walkable height used during the build of the field. (See: rcConfig::walkableHeight) + int walkableClimb; ///< The walkable climb used during the build of the field. (See: rcConfig::walkableClimb) + int borderSize; ///< The AABB border size used during the build of the field. (See: rcConfig::borderSize) + unsigned short maxDistance; ///< The maximum distance value of any span within the field. + unsigned short maxRegions; ///< The maximum region id of any span within the field. + float bmin[3]; ///< The minimum bounds in world space. [(x, y, z)] + float bmax[3]; ///< The maximum bounds in world space. [(x, y, z)] + 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.) + rcCompactCell* cells; ///< Array of cells. [Size: #width*#height] + rcCompactSpan* spans; ///< Array of spans. [Size: #spanCount] + unsigned short* dist; ///< Array containing border distance data. [Size: #spanCount] + unsigned char* areas; ///< Array containing area id data. [Size: #spanCount] +}; + +/// Represents a heightfield layer within a layer set. +/// @see rcHeightfieldLayerSet +struct rcHeightfieldLayer +{ + float bmin[3]; ///< The minimum bounds in world space. [(x, y, z)] + float bmax[3]; ///< The maximum bounds in world space. [(x, y, z)] + 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.) + int width; ///< The width of the heightfield. (Along the x-axis in cell units.) + int height; ///< The height of the heightfield. (Along the z-axis in cell units.) + int minx; ///< The minimum x-bounds of usable data. + int maxx; ///< The maximum x-bounds of usable data. + int miny; ///< The minimum y-bounds of usable data. (Along the z-axis.) + int maxy; ///< The maximum y-bounds of usable data. (Along the z-axis.) + int hmin; ///< The minimum height bounds of usable data. (Along the y-axis.) + int hmax; ///< The maximum height bounds of usable data. (Along the y-axis.) + unsigned char* heights; ///< The heightfield. [Size: (width - borderSize*2) * (h - borderSize*2)] + unsigned char* areas; ///< Area ids. [Size: Same as #heights] + unsigned char* cons; ///< Packed neighbor connection information. [Size: Same as #heights] +}; + +/// Represents a set of heightfield layers. +/// @ingroup recast +/// @see rcAllocHeightfieldLayerSet, rcFreeHeightfieldLayerSet +struct rcHeightfieldLayerSet +{ + rcHeightfieldLayer* layers; ///< The layers in the set. [Size: #nlayers] + int nlayers; ///< The number of layers in the set. }; +/// Represents a simple, non-overlapping contour in field space. struct rcContour { - inline rcContour() : verts(0), nverts(0), rverts(0), nrverts(0) { } - inline ~rcContour() { delete [] verts; delete [] rverts; } - int* verts; // Vertex coordinates, each vertex contains 4 components. - int nverts; // Number of vertices. - int* rverts; // Raw vertex coordinates, each vertex contains 4 components. - int nrverts; // Number of raw vertices. - unsigned short reg; // Region ID of the contour. + int* verts; ///< Simplified contour vertex and connection data. [Size: 4 * #nverts] + int nverts; ///< The number of vertices in the simplified contour. + int* rverts; ///< Raw contour vertex and connection data. [Size: 4 * #nrverts] + int nrverts; ///< The number of vertices in the raw contour. + unsigned short reg; ///< The region id of the contour. + unsigned char area; ///< The area id of the contour. }; +/// Represents a group of related contours. +/// @ingroup recast struct rcContourSet { - inline rcContourSet() : conts(0), nconts(0) {} - inline ~rcContourSet() { delete [] conts; } - rcContour* conts; // Pointer to all contours. - int nconts; // Number of contours. - float bmin[3], bmax[3]; // Bounding box of the heightfield. - float cs, ch; // Cell size and height. + rcContour* conts; ///< An array of the contours in the set. [Size: #nconts] + int nconts; ///< The number of contours in the set. + float bmin[3]; ///< The minimum bounds in world space. [(x, y, z)] + float bmax[3]; ///< The maximum bounds in world space. [(x, y, z)] + 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.) + 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 borderSize; ///< The AABB border size used to generate the source data from which the contours were derived. }; -// Polymesh store a connected mesh of polygons. -// The polygons are store in an array where each polygons takes -// 'nvp*2' elements. The first 'nvp' elements are indices to vertices -// and the second 'nvp' elements are indices to neighbour polygons. -// If a polygona has less than 'bvp' vertices, the remaining indices -// are set os 0xffff. If an polygon edge does not have a neighbour -// the neighbour index is set to 0xffff. -// Vertices can be transformed into world space as follows: -// x = bmin[0] + verts[i*3+0]*cs; -// y = bmin[1] + verts[i*3+1]*ch; -// z = bmin[2] + verts[i*3+2]*cs; +/// Represents a polygon mesh suitable for use in building a navigation mesh. +/// @ingroup recast struct rcPolyMesh -{ - inline rcPolyMesh() : verts(0), polys(0), regs(0), nverts(0), npolys(0), nvp(3) {} - inline ~rcPolyMesh() { delete [] verts; delete [] polys; delete [] regs; } - unsigned short* verts; // Vertices of the mesh, 3 elements per vertex. - unsigned short* polys; // Polygons of the mesh, nvp*2 elements per polygon. - unsigned short* regs; // Regions of the polygons. - int nverts; // Number of vertices. - int npolys; // Number of polygons. - int nvp; // Max number of vertices per polygon. - float bmin[3], bmax[3]; // Bounding box of the mesh. - float cs, ch; // Cell size and height. +{ + unsigned short* verts; ///< The mesh vertices. [Form: (x, y, z) * #nverts] + unsigned short* polys; ///< Polygon and neighbor data. [Length: #maxpolys * 2 * #nvp] + unsigned short* regs; ///< The region id assigned to each polygon. [Length: #maxpolys] + unsigned short* flags; ///< The user defined flags for each polygon. [Length: #maxpolys] + unsigned char* areas; ///< The area id assigned to each polygon. [Length: #maxpolys] + int nverts; ///< The number of vertices. + int npolys; ///< The number of polygons. + int maxpolys; ///< The number of allocated polygons. + int nvp; ///< The maximum number of vertices per polygon. + float bmin[3]; ///< The minimum bounds in world space. [(x, y, z)] + float bmax[3]; ///< The maximum bounds in world space. [(x, y, z)] + 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.) + int borderSize; ///< The AABB border size used to generate the source data from which the mesh was derived. }; -// Detail mesh generated from a rcPolyMesh. -// Each submesh represents a polygon in the polymesh and they are stored in -// excatly same order. Each submesh is described as 4 values: -// base vertex, vertex count, base triangle, triangle count. That is, -// const unsigned char* t = &dtl.tris[(tbase+i)*3]; and -// const float* v = &dtl.verts[(vbase+t[j])*3]; -// If the input polygon has 'n' vertices, those vertices are first in the -// submesh vertex list. This allows to compres the mesh by not storing the -// first vertices and using the polymesh vertices instead. - +/// Contains triangle meshes that represent detailed height data associated +/// with the polygons in its associated polygon mesh object. +/// @ingroup recast struct rcPolyMeshDetail { - inline rcPolyMeshDetail() : - meshes(0), verts(0), tris(0), - nmeshes(0), nverts(0), ntris(0) {} - inline ~rcPolyMeshDetail() - { - delete [] meshes; delete [] verts; delete [] tris; - } - - unsigned short* meshes; // Pointer to all mesh data. - float* verts; // Pointer to all vertex data. - unsigned char* tris; // Pointer to all triangle data. - int nmeshes; // Number of meshes. - int nverts; // Number of total vertices. - int ntris; // Number of triangles. + unsigned int* meshes; ///< The sub-mesh data. [Size: 4*#nmeshes] + float* verts; ///< The mesh vertices. [Size: 3*#nverts] + unsigned char* tris; ///< The mesh triangles. [Size: 4*#ntris] + int nmeshes; ///< The number of sub-meshes defined by #meshes. + int nverts; ///< The number of vertices in #verts. + int ntris; ///< The number of triangles in #tris. }; +/// @name Allocation Functions +/// Functions used to allocate and de-allocate Recast objects. +/// @see rcAllocSetCustom +/// @{ -// Simple dynamic array ints. -class rcIntArray -{ - int* m_data; - int m_size, m_cap; -public: - inline rcIntArray() : m_data(0), m_size(0), m_cap(0) {} - inline rcIntArray(int n) : m_data(0), m_size(0), m_cap(n) { m_data = new int[n]; } - inline ~rcIntArray() { delete [] m_data; } - void resize(int n); - inline void push(int item) { resize(m_size+1); m_data[m_size-1] = item; } - inline int pop() { if (m_size > 0) m_size--; return m_data[m_size]; } - inline const int& operator[](int i) const { return m_data[i]; } - inline int& operator[](int i) { return m_data[i]; } - inline int size() const { return m_size; } -}; +/// Allocates a heightfield object using the Recast allocator. +/// @return A heightfield that is ready for initialization, or null on failure. +/// @ingroup recast +/// @see rcCreateHeightfield, rcFreeHeightField +rcHeightfield* rcAllocHeightfield(); -enum rcSpanFlags -{ - RC_WALKABLE = 0x01, - RC_REACHABLE = 0x02, -}; +/// Frees the specified heightfield object using the Recast allocator. +/// @param[in] hf A heightfield allocated using #rcAllocHeightfield +/// @ingroup recast +/// @see rcAllocHeightfield +void rcFreeHeightField(rcHeightfield* hf); + +/// Allocates a compact heightfield object using the Recast allocator. +/// @return A compact heightfield that is ready for initialization, or null on failure. +/// @ingroup recast +/// @see rcBuildCompactHeightfield, rcFreeCompactHeightfield +rcCompactHeightfield* rcAllocCompactHeightfield(); + +/// Frees the specified compact heightfield object using the Recast allocator. +/// @param[in] chf A compact heightfield allocated using #rcAllocCompactHeightfield +/// @ingroup recast +/// @see rcAllocCompactHeightfield +void rcFreeCompactHeightfield(rcCompactHeightfield* chf); + +/// Allocates a heightfield layer set using the Recast allocator. +/// @return A heightfield layer set that is ready for initialization, or null on failure. +/// @ingroup recast +/// @see rcBuildHeightfieldLayers, rcFreeHeightfieldLayerSet +rcHeightfieldLayerSet* rcAllocHeightfieldLayerSet(); + +/// Frees the specified heightfield layer set using the Recast allocator. +/// @param[in] lset A heightfield layer set allocated using #rcAllocHeightfieldLayerSet +/// @ingroup recast +/// @see rcAllocHeightfieldLayerSet +void rcFreeHeightfieldLayerSet(rcHeightfieldLayerSet* lset); + +/// Allocates a contour set object using the Recast allocator. +/// @return A contour set that is ready for initialization, or null on failure. +/// @ingroup recast +/// @see rcBuildContours, rcFreeContourSet +rcContourSet* rcAllocContourSet(); + +/// Frees the specified contour set using the Recast allocator. +/// @param[in] cset A contour set allocated using #rcAllocContourSet +/// @ingroup recast +/// @see rcAllocContourSet +void rcFreeContourSet(rcContourSet* cset); + +/// Allocates a polygon mesh object using the Recast allocator. +/// @return A polygon mesh that is ready for initialization, or null on failure. +/// @ingroup recast +/// @see rcBuildPolyMesh, rcFreePolyMesh +rcPolyMesh* rcAllocPolyMesh(); + +/// Frees the specified polygon mesh using the Recast allocator. +/// @param[in] pmesh A polygon mesh allocated using #rcAllocPolyMesh +/// @ingroup recast +/// @see rcAllocPolyMesh +void rcFreePolyMesh(rcPolyMesh* pmesh); + +/// Allocates a detail mesh object using the Recast allocator. +/// @return A detail mesh that is ready for initialization, or null on failure. +/// @ingroup recast +/// @see rcBuildPolyMeshDetail, rcFreePolyMeshDetail +rcPolyMeshDetail* rcAllocPolyMeshDetail(); + +/// Frees the specified detail mesh using the Recast allocator. +/// @param[in] dmesh A detail mesh allocated using #rcAllocPolyMeshDetail +/// @ingroup recast +/// @see rcAllocPolyMeshDetail +void rcFreePolyMeshDetail(rcPolyMeshDetail* dmesh); -// If heightfield region ID has the following bit set, the region is on border area -// and excluded from many calculations. +/// @} + +/// Heighfield border flag. +/// If a heightfield region ID has this bit set, then the region is a border +/// region and its spans are considered unwalkable. +/// (Used during the region and contour build process.) +/// @see rcCompactSpan::reg static const unsigned short RC_BORDER_REG = 0x8000; -// If contour region ID has the following bit set, the vertex will be later -// removed in order to match the segments and vertices at tile boundaries. +/// Border vertex flag. +/// 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 +/// vertex will later be removed in order to match the segments and vertices +/// at tile boundaries. +/// (Used during the build process.) +/// @see rcCompactSpan::reg, #rcContour::verts, #rcContour::rverts static const int RC_BORDER_VERTEX = 0x10000; -// Compact span neighbour helpers. -inline int rcGetCon(const rcCompactSpan& s, int dir) -{ - return (s.con >> (dir*4)) & 0xf; -} +/// Area border flag. +/// If a region ID has this bit set, then the associated element lies on +/// the border of an area. +/// (Used during the region and contour build process.) +/// @see rcCompactSpan::reg, #rcContour::verts, #rcContour::rverts +static const int RC_AREA_BORDER = 0x20000; -inline int rcGetDirOffsetX(int dir) +/// Contour build flags. +/// @see rcBuildContours +enum rcBuildContoursFlags { - const int offset[4] = { -1, 0, 1, 0, }; - return offset[dir&0x03]; -} + RC_CONTOUR_TESS_WALL_EDGES = 0x01, ///< Tessellate solid (impassable) edges during contour simplification. + RC_CONTOUR_TESS_AREA_EDGES = 0x02, ///< Tessellate edges between areas during contour simplification. +}; -inline int rcGetDirOffsetY(int dir) -{ - const int offset[4] = { 0, 1, 0, -1 }; - return offset[dir&0x03]; -} +/// Applied to the region id field of contour vertices in order to extract the region id. +/// The region id field of a vertex may have several flags applied to it. So the +/// fields value can't be used directly. +/// @see rcContour::verts, rcContour::rverts +static const int RC_CONTOUR_REG_MASK = 0xffff; + +/// An value which indicates an invalid index within a mesh. +/// @note This does not necessarily indicate an error. +/// @see rcPolyMesh::polys +static const unsigned short RC_MESH_NULL_IDX = 0xffff; + +/// Represents the null area. +/// When a data element is given this value it is considered to no longer be +/// assigned to a usable area. (E.g. It is unwalkable.) +static const unsigned char RC_NULL_AREA = 0; + +/// The default area id used to indicate a walkable polygon. +/// This is also the maximum allowed area id, and the only non-null area id +/// recognized by some steps in the build process. +static const unsigned char RC_WALKABLE_AREA = 63; + +/// The value returned by #rcGetCon if the specified direction is not connected +/// to another span. (Has no neighbor.) +static const int RC_NOT_CONNECTED = 0x3f; + +/// @name General helper functions +/// @{ -// Common helper functions +/// Swaps the values of the two parameters. +/// @param[in,out] a Value A +/// @param[in,out] b Value B template<class T> inline void rcSwap(T& a, T& b) { T t = a; a = b; b = t; } + +/// Returns the minimum of two values. +/// @param[in] a Value A +/// @param[in] b Value B +/// @return The minimum of the two values. template<class T> inline T rcMin(T a, T b) { return a < b ? a : b; } + +/// Returns the maximum of two values. +/// @param[in] a Value A +/// @param[in] b Value B +/// @return The maximum of the two values. template<class T> inline T rcMax(T a, T b) { return a > b ? a : b; } + +/// Returns the absolute value. +/// @param[in] a The value. +/// @return The absolute value of the specified value. template<class T> inline T rcAbs(T a) { return a < 0 ? -a : a; } + +/// Return the square of a value. +/// @param[in] a The value. +/// @return The square of the value. template<class T> inline T rcSqr(T a) { return a*a; } + +/// Clamps the value to the specified range. +/// @param[in] v The value to clamp. +/// @param[in] mn The minimum permitted return value. +/// @param[in] mx The maximum permitted return value. +/// @return The value, clamped to the specified range. template<class T> inline T rcClamp(T v, T mn, T mx) { return v < mn ? mn : (v > mx ? mx : v); } -// Common vector helper functions. -inline void vcross(float* dest, const float* v1, const float* v2) +/// Returns the square root of the value. +/// @param[in] x The value. +/// @return The square root of the vlaue. +float rcSqrt(float x); + +/// Not documented. Internal use only. +/// @param[in] x Not documented. +/// @return Not documented. +inline int rcAlign4(int x) { return (x+3) & ~3; } + +/// @} +/// @name Vector helper functions. +/// @{ + +/// Derives the cross product of two vectors. (v1 x v2) +/// @param[out] dest The cross product. [(x, y, z)] +/// @param[in] v1 A Vector [(x, y, z)] +/// @param[in] v2 A vector [(x, y, z)] +inline void rcVcross(float* dest, const float* v1, const float* v2) { dest[0] = v1[1]*v2[2] - v1[2]*v2[1]; dest[1] = v1[2]*v2[0] - v1[0]*v2[2]; - dest[2] = v1[0]*v2[1] - v1[1]*v2[0]; + dest[2] = v1[0]*v2[1] - v1[1]*v2[0]; } -inline float vdot(const float* v1, const float* v2) +/// Derives the dot product of two vectors. (v1 . v2) +/// @param[in] v1 A Vector [(x, y, z)] +/// @param[in] v2 A vector [(x, y, z)] +/// @return The dot product. +inline float rcVdot(const float* v1, const float* v2) { return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2]; } -inline void vmad(float* dest, const float* v1, const float* v2, const float s) +/// Performs a scaled vector addition. (v1 + (v2 * s)) +/// @param[out] dest The result vector. [(x, y, z)] +/// @param[in] v1 The base vector [(x, y, z)] +/// @param[in] v2 The vector to scale and add to @p v1. [(x, y, z)] +/// @param[in] s The amount to scale @p v2 by before adding to @p v1. +inline void rcVmad(float* dest, const float* v1, const float* v2, const float s) { dest[0] = v1[0]+v2[0]*s; dest[1] = v1[1]+v2[1]*s; dest[2] = v1[2]+v2[2]*s; } -inline void vadd(float* dest, const float* v1, const float* v2) +/// Performs a vector addition. (@p v1 + @p v2) +/// @param[out] dest The result vector. [(x, y, z)] +/// @param[in] v1 The base vector [(x, y, z)] +/// @param[in] v2 The vector to add to @p v1. [(x, y, z)] +inline void rcVadd(float* dest, const float* v1, const float* v2) { dest[0] = v1[0]+v2[0]; dest[1] = v1[1]+v2[1]; dest[2] = v1[2]+v2[2]; } -inline void vsub(float* dest, const float* v1, const float* v2) +/// Performs a vector subtraction. (@p v1 - @p v2) +/// @param[out] dest The result vector. [(x, y, z)] +/// @param[in] v1 The base vector [(x, y, z)] +/// @param[in] v2 The vector to subtract from @p v1. [(x, y, z)] +inline void rcVsub(float* dest, const float* v1, const float* v2) { dest[0] = v1[0]-v2[0]; dest[1] = v1[1]-v2[1]; dest[2] = v1[2]-v2[2]; } -inline void vmin(float* mn, const float* v) +/// Selects the minimum value of each element from the specified vectors. +/// @param[in, out] mn A vector. (Will be updated with the result.) [(x, y, z)] +/// @param[in] v A vector. [(x, y, z)] +inline void rcVmin(float* mn, const float* v) { mn[0] = rcMin(mn[0], v[0]); mn[1] = rcMin(mn[1], v[1]); mn[2] = rcMin(mn[2], v[2]); } -inline void vmax(float* mx, const float* v) +/// Selects the maximum value of each element from the specified vectors. +/// @param[in, out] mx A vector. (Will be updated with the result.) [(x, y, z)] +/// @param[in] v A vector. [(x, y, z)] +inline void rcVmax(float* mx, const float* v) { mx[0] = rcMax(mx[0], v[0]); mx[1] = rcMax(mx[1], v[1]); mx[2] = rcMax(mx[2], v[2]); } -inline void vcopy(float* dest, const float* v) +/// Performs a vector copy. +/// @param[out] dest The result. [(x, y, z)] +/// @param[in] v The vector to copy [(x, y, z)] +inline void rcVcopy(float* dest, const float* v) { dest[0] = v[0]; dest[1] = v[1]; dest[2] = v[2]; } -inline float vdist(const float* v1, const float* v2) +/// Returns the distance between two points. +/// @param[in] v1 A point. [(x, y, z)] +/// @param[in] v2 A point. [(x, y, z)] +/// @return The distance between the two points. +inline float rcVdist(const float* v1, const float* v2) { float dx = v2[0] - v1[0]; float dy = v2[1] - v1[1]; float dz = v2[2] - v1[2]; - return sqrtf(dx*dx + dy*dy + dz*dz); + return rcSqrt(dx*dx + dy*dy + dz*dz); } -inline float vdistSqr(const float* v1, const float* v2) +/// Returns the square of the distance between two points. +/// @param[in] v1 A point. [(x, y, z)] +/// @param[in] v2 A point. [(x, y, z)] +/// @return The square of the distance between the two points. +inline float rcVdistSqr(const float* v1, const float* v2) { float dx = v2[0] - v1[0]; float dy = v2[1] - v1[1]; @@ -318,184 +705,424 @@ inline float vdistSqr(const float* v1, const float* v2) return dx*dx + dy*dy + dz*dz; } -inline void vnormalize(float* v) +/// Normalizes the vector. +/// @param[in,out] v The vector to normalize. [(x, y, z)] +inline void rcVnormalize(float* v) { - float d = 1.0f / sqrtf(rcSqr(v[0]) + rcSqr(v[1]) + rcSqr(v[2])); + float d = 1.0f / rcSqrt(rcSqr(v[0]) + rcSqr(v[1]) + rcSqr(v[2])); v[0] *= d; v[1] *= d; v[2] *= d; } -inline bool vequal(const float* p0, const float* p1) +/// Not documented. Internal use only. +/// @param[in] p0 Not documented. +/// @param[in] p1 Not documented. +/// @return Not documented. +inline bool rcVequal(const float* p0, const float* p1) { static const float thr = rcSqr(1.0f/16384.0f); - const float d = vdistSqr(p0, p1); + const float d = rcVdistSqr(p0, p1); return d < thr; } +/// @} +/// @name Heightfield Functions +/// @see rcHeightfield +/// @{ -// Calculated bounding box of array of vertices. -// Params: -// verts - (in) array of vertices -// nv - (in) vertex count -// bmin, bmax - (out) bounding box +/// Calculates the bounding box of an array of vertices. +/// @ingroup recast +/// @param[in] verts An array of vertices. [(x, y, z) * @p nv] +/// @param[in] nv The number of vertices in the @p verts array. +/// @param[out] bmin The minimum bounds of the AABB. [(x, y, z)] [Units: wu] +/// @param[out] bmax The maximum bounds of the AABB. [(x, y, z)] [Units: wu] void rcCalcBounds(const float* verts, int nv, float* bmin, float* bmax); -// Calculates grid size based on bounding box and grid cell size. -// Params: -// bmin, bmax - (in) bounding box -// cs - (in) grid cell size -// w - (out) grid width -// h - (out) grid height +/// Calculates the grid size based on the bounding box and grid cell size. +/// @ingroup recast +/// @param[in] bmin The minimum bounds of the AABB. [(x, y, z)] [Units: wu] +/// @param[in] bmax The maximum bounds of the AABB. [(x, y, z)] [Units: wu] +/// @param[in] cs The xz-plane cell size. [Limit: > 0] [Units: wu] +/// @param[out] w The width along the x-axis. [Limit: >= 0] [Units: vx] +/// @param[out] h The height along the z-axis. [Limit: >= 0] [Units: vx] void rcCalcGridSize(const float* bmin, const float* bmax, float cs, int* w, int* h); -// Creates and initializes new heightfield. -// Params: -// hf - (in/out) heightfield to initialize. -// width - (in) width of the heightfield. -// height - (in) height of the heightfield. -// bmin, bmax - (in) bounding box of the heightfield -// cs - (in) grid cell size -// ch - (in) grid cell height -bool rcCreateHeightfield(rcHeightfield& hf, int width, int height, +/// Initializes a new heightfield. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in,out] hf The allocated heightfield to initialize. +/// @param[in] width The width of the field along the x-axis. [Limit: >= 0] [Units: vx] +/// @param[in] height The height of the field along the z-axis. [Limit: >= 0] [Units: vx] +/// @param[in] bmin The minimum 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] ch The y-axis cell size to use for field. [Limit: > 0] [Units: wu] +bool rcCreateHeightfield(rcContext* ctx, rcHeightfield& hf, int width, int height, const float* bmin, const float* bmax, float cs, float ch); -// Sets the WALKABLE flag for every triangle whose slope is below -// the maximun walkable slope angle. -// Params: -// walkableSlopeAngle - (in) maximun slope angle in degrees. -// verts - (in) array of vertices -// nv - (in) vertex count -// tris - (in) array of triangle vertex indices -// nt - (in) triangle count -// flags - (out) array of triangle flags -void rcMarkWalkableTriangles(const float walkableSlopeAngle, - const float* verts, int nv, - const int* tris, int nt, - unsigned char* flags); - -// Rasterizes a triangle into heightfield spans. -// Params: -// v0,v1,v2 - (in) the vertices of the triangle. -// flags - (in) triangle flags (uses WALKABLE) -// solid - (in) heighfield where the triangle is rasterized -void rcRasterizeTriangle(const float* v0, const float* v1, const float* v2, - unsigned char flags, rcHeightfield& solid); - -// Rasterizes the triangles into heightfield spans. -// Params: -// verts - (in) array of vertices -// nv - (in) vertex count -// tris - (in) array of triangle vertex indices -// norms - (in) array of triangle normals -// flags - (in) array of triangle flags (uses WALKABLE) -// nt - (in) triangle count -// solid - (in) heighfield where the triangles are rasterized -void rcRasterizeTriangles(const float* verts, int nv, - const int* tris, const unsigned char* flags, int nt, - rcHeightfield& solid); - -// Removes WALKABLE flag from all spans that are at ledges. This filtering -// removes possible overestimation of the conservative voxelization so that -// the resulting mesh will not have regions hanging in air over ledges. -// Params: -// walkableHeight - (in) minimum height where the agent can still walk -// walkableClimb - (in) maximum height between grid cells the agent can climb -// solid - (in/out) heightfield describing the solid space -void rcFilterLedgeSpans(const int walkableHeight, - const int walkableClimb, - rcHeightfield& solid); - -// Removes WALKABLE flag from all spans which have smaller than -// 'walkableHeight' clearane above them. -// Params: -// walkableHeight - (in) minimum height where the agent can still walk -// solid - (in/out) heightfield describing the solid space -void rcFilterWalkableLowHeightSpans(int walkableHeight, - rcHeightfield& solid); - -// Marks spans which are reachable from any of the topmost spans. -// Params: -// walkableHeight - (in) minimum height where the agent can still walk -// walkableClimb - (in) maximum height between grid cells the agent can climb -// solid - (in/out) heightfield describing the solid space -// Returns false if operation ran out of memory. -bool rcMarkReachableSpans(const int walkableHeight, - const int walkableClimb, - rcHeightfield& solid); - -// Builds compact representation of the heightfield. -// Params: -// walkableHeight - (in) minimum height where the agent can still walk -// walkableClimb - (in) maximum height between grid cells the agent can climb -// hf - (in) heightfield to be compacted -// chf - (out) compact heightfield representing the open space. -// Returns false if operation ran out of memory. -bool rcBuildCompactHeightfield(const int walkableHeight, const int walkableClimb, - unsigned char flags, - rcHeightfield& hf, - rcCompactHeightfield& chf); - -// Builds distance field and stores it into the combat heightfield. -// Params: -// chf - (in/out) compact heightfield representing the open space. -// Returns false if operation ran out of memory. -bool rcBuildDistanceField(rcCompactHeightfield& chf); - -// Divides the walkable heighfied into simple regions. -// Each region has only one contour and no overlaps. -// The regions are stored in the compact heightfield 'reg' field. -// The regions will be shrinked by the radius of the agent. -// The process sometimes creates small regions. The parameter -// 'minRegionSize' specifies the smallest allowed regions size. -// If the area of a regions is smaller than allowed, the regions is -// removed or merged to neighbour region. -// Params: -// chf - (in/out) compact heightfield representing the open space. -// walkableRadius - (in) the radius of the agent. -// minRegionSize - (in) the smallest allowed regions size. -// maxMergeRegionSize - (in) the largest allowed regions size which can be merged. -// Returns false if operation ran out of memory. -bool rcBuildRegions(rcCompactHeightfield& chf, - int walkableRadius, int borderSize, - int minRegionSize, int mergeRegionSize); - -// Builds simplified contours from the regions outlines. -// Params: -// chf - (in) compact heightfield which has regions set. -// maxError - (in) maximum allowed distance between simplified countour and cells. -// maxEdgeLen - (in) maximum allowed contour edge length in cells. -// cset - (out) Resulting contour set. -// Returns false if operation ran out of memory. -bool rcBuildContours(rcCompactHeightfield& chf, +/// Sets the area id of all triangles with a slope below the specified value +/// to #RC_WALKABLE_AREA. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in] walkableSlopeAngle The maximum slope that is considered walkable. [Limits: 0 <= value < 90] +/// [Units: Degrees] +/// @param[in] verts The vertices. [(x, y, z) * @p nv] +/// @param[in] nv The number of vertices. +/// @param[in] tris The triangle vertex indices. [(vertA, vertB, vertC) * @p nt] +/// @param[in] nt The number of triangles. +/// @param[out] areas The triangle area ids. [Length: >= @p nt] +void rcMarkWalkableTriangles(rcContext* ctx, const float walkableSlopeAngle, const float* verts, int nv, + const int* tris, int nt, unsigned char* areas); + +/// Sets the area id of all triangles with a slope greater than or equal to the specified value to #RC_NULL_AREA. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in] walkableSlopeAngle The maximum slope that is considered walkable. [Limits: 0 <= value < 90] +/// [Units: Degrees] +/// @param[in] verts The vertices. [(x, y, z) * @p nv] +/// @param[in] nv The number of vertices. +/// @param[in] tris The triangle vertex indices. [(vertA, vertB, vertC) * @p nt] +/// @param[in] nt The number of triangles. +/// @param[out] areas The triangle area ids. [Length: >= @p nt] +void rcClearUnwalkableTriangles(rcContext* ctx, const float walkableSlopeAngle, const float* verts, int nv, + const int* tris, int nt, unsigned char* areas); + +/// Adds a span to the specified heightfield. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in,out] hf An initialized heightfield. +/// @param[in] x The width index where the span is to be added. +/// [Limits: 0 <= value < rcHeightfield::width] +/// @param[in] y The height index where the span is to be added. +/// [Limits: 0 <= value < rcHeightfield::height] +/// @param[in] smin The minimum height of the span. [Limit: < @p smax] [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] flagMergeThr The merge theshold. [Limit: >= 0] [Units: vx] +void rcAddSpan(rcContext* ctx, rcHeightfield& hf, const int x, const int y, + const unsigned short smin, const unsigned short smax, + const unsigned char area, const int flagMergeThr); + +/// Rasterizes a triangle into the specified heightfield. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in] v0 Triangle vertex 0 [(x, y, z)] +/// @param[in] v1 Triangle vertex 1 [(x, y, z)] +/// @param[in] v2 Triangle vertex 2 [(x, y, z)] +/// @param[in] area The area id of the triangle. [Limit: <= #RC_WALKABLE_AREA] +/// @param[in, out] solid An initialized heightfield. +/// @param[in] flagMergeThr The distance where the walkable flag is favored over the non-walkable flag. +/// [Limit: >= 0] [Units: vx] +void rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const float* v2, + const unsigned char area, rcHeightfield& solid, + const int flagMergeThr = 1); + +/// Rasterizes an indexed triangle mesh into the specified heightfield. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in] verts The vertices. [(x, y, z) * @p nv] +/// @param[in] nv The number of vertices. +/// @param[in] tris The triangle indices. [(vertA, vertB, vertC) * @p nt] +/// @param[in] areas The area id's of the triangles. [Limit: <= #RC_WALKABLE_AREA] [Size: @p nt] +/// @param[in] nt The number of triangles. +/// @param[in, out] solid An initialized heightfield. +/// @param[in] flagMergeThr The distance where the walkable flag is favored over the non-walkable flag. +/// [Limit: >= 0] [Units: vx] +void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int nv, + const int* tris, const unsigned char* areas, const int nt, + rcHeightfield& solid, const int flagMergeThr = 1); + +/// Rasterizes an indexed triangle mesh into the specified heightfield. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in] verts The vertices. [(x, y, z) * @p nv] +/// @param[in] nv The number of vertices. +/// @param[in] tris The triangle indices. [(vertA, vertB, vertC) * @p nt] +/// @param[in] areas The area id's of the triangles. [Limit: <= #RC_WALKABLE_AREA] [Size: @p nt] +/// @param[in] nt The number of triangles. +/// @param[in, out] solid An initialized heightfield. +/// @param[in] flagMergeThr The distance where the walkable flag is favored over the non-walkable flag. +/// [Limit: >= 0] [Units: vx] +void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int nv, + const unsigned short* tris, const unsigned char* areas, const int nt, + rcHeightfield& solid, const int flagMergeThr = 1); + +/// Rasterizes triangles into the specified heightfield. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in] verts The triangle vertices. [(ax, ay, az, bx, by, bz, cx, by, cx) * @p nt] +/// @param[in] areas The area id's of the triangles. [Limit: <= #RC_WALKABLE_AREA] [Size: @p nt] +/// @param[in] nt The number of triangles. +/// @param[in, out] solid An initialized heightfield. +/// @param[in] flagMergeThr The distance where the walkable flag is favored over the non-walkable flag. +/// [Limit: >= 0] [Units: vx] +void rcRasterizeTriangles(rcContext* ctx, const float* verts, const unsigned char* areas, const int nt, + rcHeightfield& solid, const int flagMergeThr = 1); + +/// Marks non-walkable spans as walkable if their maximum is within @p walkableClimp of a walkable neihbor. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in] walkableClimb Maximum ledge height that is considered to still be traversable. +/// [Limit: >=0] [Units: vx] +/// @param[in,out] solid A fully built heightfield. (All spans have been added.) +void rcFilterLowHangingWalkableObstacles(rcContext* ctx, const int walkableClimb, rcHeightfield& solid); + +/// Marks spans that are ledges as not-walkable. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in] walkableHeight Minimum floor to 'ceiling' height that will still allow the floor area to +/// be considered walkable. [Limit: >= 3] [Units: vx] +/// @param[in] walkableClimb Maximum ledge height that is considered to still be traversable. +/// [Limit: >=0] [Units: vx] +/// @param[in,out] solid A fully built heightfield. (All spans have been added.) +void rcFilterLedgeSpans(rcContext* ctx, const int walkableHeight, + const int walkableClimb, rcHeightfield& solid); + +/// Marks walkable spans as not walkable if the clearence above the span is less than the specified height. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in] walkableHeight Minimum floor to 'ceiling' height that will still allow the floor area to +/// be considered walkable. [Limit: >= 3] [Units: vx] +/// @param[in,out] solid A fully built heightfield. (All spans have been added.) +void rcFilterWalkableLowHeightSpans(rcContext* ctx, int walkableHeight, rcHeightfield& solid); + +/// Returns the number of spans contained in the specified heightfield. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in] hf An initialized heightfield. +/// @returns The number of spans in the heightfield. +int rcGetHeightFieldSpanCount(rcContext* ctx, rcHeightfield& hf); + +/// @} +/// @name Compact Heightfield Functions +/// @see rcCompactHeightfield +/// @{ + +/// Builds a compact heightfield representing open space, from a heightfield representing solid space. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in] walkableHeight Minimum floor to 'ceiling' height that will still allow the floor area +/// to be considered walkable. [Limit: >= 3] [Units: vx] +/// @param[in] walkableClimb Maximum ledge height that is considered to still be traversable. +/// [Limit: >=0] [Units: vx] +/// @param[in] hf The heightfield to be compacted. +/// @param[out] chf The resulting compact heightfield. (Must be pre-allocated.) +/// @returns True if the operation completed successfully. +bool rcBuildCompactHeightfield(rcContext* ctx, const int walkableHeight, const int walkableClimb, + rcHeightfield& hf, rcCompactHeightfield& chf); + +/// Erodes the walkable area within the heightfield by the specified radius. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in] radius The radius of erosion. [Limits: 0 < value < 255] [Units: vx] +/// @param[in,out] chf The populated compact heightfield to erode. +/// @returns True if the operation completed successfully. +bool rcErodeWalkableArea(rcContext* ctx, int radius, rcCompactHeightfield& chf); + +/// Applies a median filter to walkable area types (based on area id), removing noise. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in,out] chf A populated compact heightfield. +/// @returns True if the operation completed successfully. +bool rcMedianFilterWalkableArea(rcContext* ctx, rcCompactHeightfield& chf); + +/// Applies an area id to all spans within the specified bounding box. (AABB) +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in] bmin The minimum of the bounding box. [(x, y, z)] +/// @param[in] bmax The maximum of the bounding box. [(x, y, z)] +/// @param[in] areaId The area id to apply. [Limit: <= #RC_WALKABLE_AREA] +/// @param[in,out] chf A populated compact heightfield. +void rcMarkBoxArea(rcContext* ctx, const float* bmin, const float* bmax, unsigned char areaId, + rcCompactHeightfield& chf); + +/// Applies the area id to the all spans within the specified convex polygon. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in] verts The vertices of the polygon [Fomr: (x, y, z) * @p nverts] +/// @param[in] nverts The number of vertices in the polygon. +/// @param[in] hmin The height of the base of the polygon. +/// @param[in] hmax The height of the top of the polygon. +/// @param[in] areaId The area id to apply. [Limit: <= #RC_WALKABLE_AREA] +/// @param[in,out] chf A populated compact heightfield. +void rcMarkConvexPolyArea(rcContext* ctx, const float* verts, const int nverts, + const float hmin, const float hmax, unsigned char areaId, + rcCompactHeightfield& chf); + +/// Applies the area id to all spans within the specified cylinder. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in] pos The center of the base of the cylinder. [Form: (x, y, z)] +/// @param[in] r The radius of the cylinder. +/// @param[in] h The height of the cylinder. +/// @param[in] areaId The area id to apply. [Limit: <= #RC_WALKABLE_AREA] +/// @param[in,out] chf A populated compact heightfield. +void rcMarkCylinderArea(rcContext* ctx, const float* pos, + const float r, const float h, unsigned char areaId, + rcCompactHeightfield& chf); + +/// Builds the distance field for the specified compact heightfield. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in,out] chf A populated compact heightfield. +/// @returns True if the operation completed successfully. +bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf); + +/// Builds region data for the heightfield using watershed partitioning. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in,out] chf A populated compact heightfield. +/// @param[in] borderSize The size of the non-navigable border around the heightfield. [Limit: >=0] [Units: vx] +/// @param[in] minRegionArea The minimum number of cells allowed to form isolated island areas. [Limit: >=0] +/// [Units: vx]. +/// @param[in] mergeRegionArea Any regions with a span count smaller than this value will, if possible, +/// be merged with larger regions. [Limit: >=0] [Units: vx] +/// @returns True if the operation completed successfully. +bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, + const int borderSize, const int minRegionArea, const int mergeRegionArea); + +/// Builds region data for the heightfield using simple monotone partitioning. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in,out] chf A populated compact heightfield. +/// @param[in] borderSize The size of the non-navigable border around the heightfield. [Limit: >=0] [Units: vx] +/// @param[in] minRegionArea The minimum number of cells allowed to form isolated island areas. [Limit: >=0] +/// [Units: vx]. +/// @param[in] mergeRegionArea Any regions with a span count smaller than this value will, if possible, +/// be merged with larger regions. [Limit: >=0] [Units: vx] +/// @returns True if the operation completed successfully. +bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf, + const int borderSize, const int minRegionArea, const int mergeRegionArea); + + +/// Sets the neighbor connection data for the specified direction. +/// @param[in] s The span to update. +/// @param[in] dir The direction to set. [Limits: 0 <= value < 4] +/// @param[in] i The index of the neighbor span. +inline void rcSetCon(rcCompactSpan& s, int dir, int i) +{ + const unsigned int shift = (unsigned int)dir*6; + unsigned int con = s.con; + s.con = (con & ~(0x3f << shift)) | (((unsigned int)i & 0x3f) << shift); +} + +/// Gets neighbor connection data for the specified direction. +/// @param[in] s The span to check. +/// @param[in] dir The direction to check. [Limits: 0 <= value < 4] +/// @return The neighbor connection data for the specified direction, +/// or #RC_NOT_CONNECTED if there is no connection. +inline int rcGetCon(const rcCompactSpan& s, int dir) +{ + const unsigned int shift = (unsigned int)dir*6; + return (s.con >> shift) & 0x3f; +} + +/// Gets the standard width (x-axis) offset for the specified direction. +/// @param[in] dir The direction. [Limits: 0 <= value < 4] +/// @return The width offset to apply to the current cell position to move +/// in the direction. +inline int rcGetDirOffsetX(int dir) +{ + const int offset[4] = { -1, 0, 1, 0, }; + return offset[dir&0x03]; +} + +/// Gets the standard height (z-axis) offset for the specified direction. +/// @param[in] dir The direction. [Limits: 0 <= value < 4] +/// @return The height offset to apply to the current cell position to move +/// in the direction. +inline int rcGetDirOffsetY(int dir) +{ + const int offset[4] = { 0, 1, 0, -1 }; + return offset[dir&0x03]; +} + +/// @} +/// @name Layer, Contour, Polymesh, and Detail Mesh Functions +/// @see rcHeightfieldLayer, rcContourSet, rcPolyMesh, rcPolyMeshDetail +/// @{ + +/// Builds a layer set from the specified compact heightfield. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in] chf A fully built compact heightfield. +/// @param[in] borderSize The size of the non-navigable border around the heightfield. [Limit: >=0] +/// [Units: vx] +/// @param[in] walkableHeight Minimum floor to 'ceiling' height that will still allow the floor area +/// to be considered walkable. [Limit: >= 3] [Units: vx] +/// @param[out] lset The resulting layer set. (Must be pre-allocated.) +/// @returns True if the operation completed successfully. +bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf, + const int borderSize, const int walkableHeight, + rcHeightfieldLayerSet& lset); + +/// Builds a contour set from the region outlines in the provided compact heightfield. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in] chf A fully built compact heightfield. +/// @param[in] maxError The maximum distance a simplfied contour's border edges should deviate +/// the original raw contour. [Limit: >=0] [Units: wu] +/// @param[in] maxEdgeLen The maximum allowed length for contour edges along the border of the mesh. +/// [Limit: >=0] [Units: vx] +/// @param[out] cset The resulting contour set. (Must be pre-allocated.) +/// @param[in] buildFlags The build flags. (See: #rcBuildContoursFlags) +/// @returns True if the operation completed successfully. +bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf, const float maxError, const int maxEdgeLen, - rcContourSet& cset); - -// Builds connected convex polygon mesh from contour polygons. -// Params: -// cset - (in) contour set. -// nvp - (in) maximum number of vertices per polygon. -// mesh - (out) poly mesh. -// Returns false if operation ran out of memory. -bool rcBuildPolyMesh(rcContourSet& cset, int nvp, rcPolyMesh& mesh); - -bool rcMergePolyMeshes(rcPolyMesh** meshes, const int nmeshes, rcPolyMesh& mesh); - -// Builds detail triangle mesh for each polygon in the poly mesh. -// Params: -// mesh - (in) poly mesh to detail. -// chf - (in) compacy height field, used to query height for new vertices. -// sampleDist - (in) spacing between height samples used to generate more detail into mesh. -// sampleMaxError - (in) maximum allowed distance between simplified detail mesh and height sample. -// pmdtl - (out) detail mesh. -// Returns false if operation ran out of memory. -bool rcBuildPolyMeshDetail(const rcPolyMesh& mesh, const rcCompactHeightfield& chf, + rcContourSet& cset, const int flags = RC_CONTOUR_TESS_WALL_EDGES); + +/// Builds a polygon mesh from the provided contours. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in] cset A fully built contour set. +/// @param[in] nvp The maximum number of vertices allowed for polygons generated during the +/// contour to polygon conversion process. [Limit: >= 3] +/// @param[out] mesh The resulting polygon mesh. (Must be re-allocated.) +/// @returns True if the operation completed successfully. +bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMesh& mesh); + +/// Merges multiple polygon meshes into a single mesh. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in] meshes An array of polygon meshes to merge. [Size: @p nmeshes] +/// @param[in] nmeshes The number of polygon meshes in the meshes array. +/// @param[in] mesh The resulting polygon mesh. (Must be pre-allocated.) +/// @returns True if the operation completed successfully. +bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, rcPolyMesh& mesh); + +/// Builds a detail mesh from the provided polygon mesh. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in] mesh A fully built polygon mesh. +/// @param[in] chf The compact heightfield used to build the polygon mesh. +/// @param[in] sampleDist Sets the distance to use when samping the heightfield. [Limit: >=0] [Units: wu] +/// @param[in] sampleMaxError The maximum distance the detail mesh surface should deviate from +/// heightfield data. [Limit: >=0] [Units: wu] +/// @param[out] dmesh The resulting detail mesh. (Must be pre-allocated.) +/// @returns True if the operation completed successfully. +bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompactHeightfield& chf, const float sampleDist, const float sampleMaxError, rcPolyMeshDetail& dmesh); -bool rcMergePolyMeshDetails(rcPolyMeshDetail** meshes, const int nmeshes, rcPolyMeshDetail& mesh); +/// Merges multiple detail meshes into a single detail mesh. +/// @ingroup recast +/// @param[in,out] ctx The build context to use during the operation. +/// @param[in] meshes An array of detail meshes to merge. [Size: @p nmeshes] +/// @param[in] nmeshes The number of detail meshes in the meshes array. +/// @param[out] mesh The resulting detail mesh. (Must be pre-allocated.) +/// @returns True if the operation completed successfully. +bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int nmeshes, rcPolyMeshDetail& mesh); bool buildMeshAdjacency(unsigned short* polys, const int npolys, const int nverts, const int vertsPerPoly); +/// @} + #endif // RECAST_H + +/////////////////////////////////////////////////////////////////////////// + +// Due to the large amount of detail documentation for this file, +// the content normally located at the end of the header file has been separated +// out to a file in /Docs/Extern. diff --git a/extern/recastnavigation/Recast/Include/RecastAlloc.h b/extern/recastnavigation/Recast/Include/RecastAlloc.h new file mode 100644 index 00000000000..0038c1a5c54 --- /dev/null +++ b/extern/recastnavigation/Recast/Include/RecastAlloc.h @@ -0,0 +1,122 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#ifndef RECASTALLOC_H +#define RECASTALLOC_H + +/// Provides hint values to the memory allocator on how long the +/// memory is expected to be used. +enum rcAllocHint +{ + RC_ALLOC_PERM, ///< Memory will persist after a function call. + RC_ALLOC_TEMP ///< Memory used temporarily within a function. +}; + +/// A memory allocation function. +// @param[in] size The size, in bytes of memory, to allocate. +// @param[in] rcAllocHint A hint to the allocator on how long the memory is expected to be in use. +// @return A pointer to the beginning of the allocated memory block, or null if the allocation failed. +/// @see rcAllocSetCustom +typedef void* (rcAllocFunc)(int size, rcAllocHint hint); + +/// A memory deallocation function. +/// @see rcAllocSetCustom +// @param[in] ptr +typedef void (rcFreeFunc)(void* ptr); + +/// Sets the base custom allocation functions to be used by Recast. +/// @param[in] allocFunc The memory allocation function to be used by #rcAlloc +/// @param[in] freeFunc The memory de-allocation function to be used by #rcFree +void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc); + +/// Allocates a memory block. +/// @param[in] size The size, in bytes of memory, to allocate. +/// @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. +void* rcAlloc(int size, rcAllocHint hint); + +/// Deallocates a memory block. +/// @param[in] ptr A pointer to a memory block previously allocated using #rcAlloc. +void rcFree(void* ptr); + + +/// A simple dynamic array of integers. +class rcIntArray +{ + int* m_data; + int m_size, m_cap; + inline rcIntArray(const rcIntArray&); + inline rcIntArray& operator=(const rcIntArray&); +public: + + /// Constructs an instance with an initial array size of zero. + inline rcIntArray() : m_data(0), m_size(0), m_cap(0) {} + + /// Constructs an instance initialized to the specified size. + /// @param[in] n The initial size of the integer array. + inline rcIntArray(int n) : m_data(0), m_size(0), m_cap(0) { resize(n); } + inline ~rcIntArray() { rcFree(m_data); } + + /// Specifies the new size of the integer array. + /// @param[in] n The new size of the integer array. + void resize(int n); + + /// Push the specified integer onto the end of the array and increases the size by one. + /// @param[in] item The new value. + inline void push(int item) { resize(m_size+1); m_data[m_size-1] = item; } + + /// Returns the value at the end of the array and reduces the size by one. + /// @return The value at the end of the array. + inline int pop() { if (m_size > 0) m_size--; return m_data[m_size]; } + + /// The value at the specified array index. + /// @warning Does not provide overflow protection. + /// @param[in] i The index of the value. + inline const int& operator[](int i) const { return m_data[i]; } + + /// The value at the specified array index. + /// @warning Does not provide overflow protection. + /// @param[in] i The index of the value. + inline int& operator[](int i) { return m_data[i]; } + + /// The current size of the integer array. + inline int size() const { return m_size; } +}; + +/// A simple helper class used to delete an array when it goes out of scope. +/// @note This class is rarely if ever used by the end user. +template<class T> class rcScopedDelete +{ + T* ptr; + inline T* operator=(T* p); +public: + + /// Constructs an instance with a null pointer. + inline rcScopedDelete() : ptr(0) {} + + /// Constructs an instance with the specified pointer. + /// @param[in] p An pointer to an allocated array. + inline rcScopedDelete(T* p) : ptr(p) {} + inline ~rcScopedDelete() { rcFree(ptr); } + + /// The root array pointer. + /// @return The root array pointer. + inline operator T*() { return ptr; } +}; + +#endif diff --git a/extern/recastnavigation/Recast/Include/RecastAssert.h b/extern/recastnavigation/Recast/Include/RecastAssert.h new file mode 100644 index 00000000000..b58b8fcd286 --- /dev/null +++ b/extern/recastnavigation/Recast/Include/RecastAssert.h @@ -0,0 +1,33 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#ifndef RECASTASSERT_H +#define RECASTASSERT_H + +// Note: This header file's only purpose is to include define assert. +// Feel free to change the file and include your own implementation instead. + +#ifdef NDEBUG +// From http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/ +# define rcAssert(x) do { (void)sizeof(x); } while(__LINE__==-1,false) +#else +# include <assert.h> +# define rcAssert assert +#endif + +#endif // RECASTASSERT_H diff --git a/extern/recastnavigation/Recast/Source/Recast.cpp b/extern/recastnavigation/Recast/Source/Recast.cpp index 0db26c2c1cd..283cf0c128b 100644 --- a/extern/recastnavigation/Recast/Source/Recast.cpp +++ b/extern/recastnavigation/Recast/Source/Recast.cpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2009 Mikko Mononen memon@inside.org +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -22,35 +22,178 @@ #include <string.h> #include <stdlib.h> #include <stdio.h> +#include <stdarg.h> #include "Recast.h" -#include "RecastLog.h" -#include "RecastTimer.h" +#include "RecastAlloc.h" +#include "RecastAssert.h" +float rcSqrt(float x) +{ + return sqrtf(x); +} + +/// @class rcContext +/// @par +/// +/// This class does not provide logging or timer functionality on its +/// own. Both must be provided by a concrete implementation +/// by overriding the protected member functions. Also, this class does not +/// provide an interface for extracting log messages. (Only adding them.) +/// So concrete implementations must provide one. +/// +/// If no logging or timers are required, just pass an instance of this +/// class through the Recast build process. +/// -void rcIntArray::resize(int n) +/// @par +/// +/// Example: +/// @code +/// // Where ctx is an instance of rcContext and filepath is a char array. +/// ctx->log(RC_LOG_ERROR, "buildTiledNavigation: Could not load '%s'", filepath); +/// @endcode +void rcContext::log(const rcLogCategory category, const char* format, ...) { - if (n > m_cap) + if (!m_logEnabled) + return; + static const int MSG_SIZE = 512; + char msg[MSG_SIZE]; + va_list ap; + va_start(ap, format); + int len = vsnprintf(msg, MSG_SIZE, format, ap); + if (len >= MSG_SIZE) { - if (!m_cap) m_cap = 8; - while (m_cap < n) m_cap *= 2; - int* newData = new int[m_cap]; - if (m_size && newData) memcpy(newData, m_data, m_size*sizeof(int)); - delete [] m_data; - m_data = newData; + len = MSG_SIZE-1; + msg[MSG_SIZE-1] = '\0'; } - m_size = n; + va_end(ap); + doLog(category, msg, len); +} + +rcHeightfield* rcAllocHeightfield() +{ + rcHeightfield* hf = (rcHeightfield*)rcAlloc(sizeof(rcHeightfield), RC_ALLOC_PERM); + memset(hf, 0, sizeof(rcHeightfield)); + return hf; +} + +void rcFreeHeightField(rcHeightfield* hf) +{ + if (!hf) return; + // Delete span array. + rcFree(hf->spans); + // Delete span pools. + while (hf->pools) + { + rcSpanPool* next = hf->pools->next; + rcFree(hf->pools); + hf->pools = next; + } + rcFree(hf); +} + +rcCompactHeightfield* rcAllocCompactHeightfield() +{ + rcCompactHeightfield* chf = (rcCompactHeightfield*)rcAlloc(sizeof(rcCompactHeightfield), RC_ALLOC_PERM); + memset(chf, 0, sizeof(rcCompactHeightfield)); + return chf; +} + +void rcFreeCompactHeightfield(rcCompactHeightfield* chf) +{ + if (!chf) return; + rcFree(chf->cells); + rcFree(chf->spans); + rcFree(chf->dist); + rcFree(chf->areas); + rcFree(chf); +} + + +rcHeightfieldLayerSet* rcAllocHeightfieldLayerSet() +{ + rcHeightfieldLayerSet* lset = (rcHeightfieldLayerSet*)rcAlloc(sizeof(rcHeightfieldLayerSet), RC_ALLOC_PERM); + memset(lset, 0, sizeof(rcHeightfieldLayerSet)); + return lset; +} + +void rcFreeHeightfieldLayerSet(rcHeightfieldLayerSet* lset) +{ + if (!lset) return; + for (int i = 0; i < lset->nlayers; ++i) + { + rcFree(lset->layers[i].heights); + rcFree(lset->layers[i].areas); + rcFree(lset->layers[i].cons); + } + rcFree(lset->layers); + rcFree(lset); +} + + +rcContourSet* rcAllocContourSet() +{ + rcContourSet* cset = (rcContourSet*)rcAlloc(sizeof(rcContourSet), RC_ALLOC_PERM); + memset(cset, 0, sizeof(rcContourSet)); + return cset; +} + +void rcFreeContourSet(rcContourSet* cset) +{ + if (!cset) return; + for (int i = 0; i < cset->nconts; ++i) + { + rcFree(cset->conts[i].verts); + rcFree(cset->conts[i].rverts); + } + rcFree(cset->conts); + rcFree(cset); +} + +rcPolyMesh* rcAllocPolyMesh() +{ + rcPolyMesh* pmesh = (rcPolyMesh*)rcAlloc(sizeof(rcPolyMesh), RC_ALLOC_PERM); + memset(pmesh, 0, sizeof(rcPolyMesh)); + return pmesh; +} + +void rcFreePolyMesh(rcPolyMesh* pmesh) +{ + if (!pmesh) return; + rcFree(pmesh->verts); + rcFree(pmesh->polys); + rcFree(pmesh->regs); + rcFree(pmesh->flags); + rcFree(pmesh->areas); + rcFree(pmesh); +} + +rcPolyMeshDetail* rcAllocPolyMeshDetail() +{ + rcPolyMeshDetail* dmesh = (rcPolyMeshDetail*)rcAlloc(sizeof(rcPolyMeshDetail), RC_ALLOC_PERM); + memset(dmesh, 0, sizeof(rcPolyMeshDetail)); + return dmesh; +} + +void rcFreePolyMeshDetail(rcPolyMeshDetail* dmesh) +{ + if (!dmesh) return; + rcFree(dmesh->meshes); + rcFree(dmesh->verts); + rcFree(dmesh->tris); + rcFree(dmesh); } void rcCalcBounds(const float* verts, int nv, float* bmin, float* bmax) { // Calculate bounding box. - vcopy(bmin, verts); - vcopy(bmax, verts); + rcVcopy(bmin, verts); + rcVcopy(bmax, verts); for (int i = 1; i < nv; ++i) { const float* v = &verts[i*3]; - vmin(bmin, v); - vmax(bmax, v); + rcVmin(bmin, v); + rcVmax(bmax, v); } } @@ -60,17 +203,25 @@ void rcCalcGridSize(const float* bmin, const float* bmax, float cs, int* w, int* *h = (int)((bmax[2] - bmin[2])/cs+0.5f); } -bool rcCreateHeightfield(rcHeightfield& hf, int width, int height, +/// @par +/// +/// See the #rcConfig documentation for more information on the configuration parameters. +/// +/// @see rcAllocHeightfield, rcHeightfield +bool rcCreateHeightfield(rcContext* /*ctx*/, rcHeightfield& hf, int width, int height, const float* bmin, const float* bmax, float cs, float ch) { + // TODO: VC complains about unref formal variable, figure out a way to handle this better. +// rcAssert(ctx); + hf.width = width; hf.height = height; - hf.spans = new rcSpan*[hf.width*hf.height]; - vcopy(hf.bmin, bmin); - vcopy(hf.bmax, bmax); + rcVcopy(hf.bmin, bmin); + rcVcopy(hf.bmax, bmax); hf.cs = cs; hf.ch = ch; + hf.spans = (rcSpan**)rcAlloc(sizeof(rcSpan*)*hf.width*hf.height, RC_ALLOC_PERM); if (!hf.spans) return false; memset(hf.spans, 0, sizeof(rcSpan*)*hf.width*hf.height); @@ -80,18 +231,29 @@ bool rcCreateHeightfield(rcHeightfield& hf, int width, int height, static void calcTriNormal(const float* v0, const float* v1, const float* v2, float* norm) { float e0[3], e1[3]; - vsub(e0, v1, v0); - vsub(e1, v2, v0); - vcross(norm, e0, e1); - vnormalize(norm); + rcVsub(e0, v1, v0); + rcVsub(e1, v2, v0); + rcVcross(norm, e0, e1); + rcVnormalize(norm); } -void rcMarkWalkableTriangles(const float walkableSlopeAngle, - const float* verts, int nv, +/// @par +/// +/// Only sets the aread id's for the walkable triangles. Does not alter the +/// area id's for unwalkable triangles. +/// +/// See the #rcConfig documentation for more information on the configuration parameters. +/// +/// @see rcHeightfield, rcClearUnwalkableTriangles, rcRasterizeTriangles +void rcMarkWalkableTriangles(rcContext* /*ctx*/, const float walkableSlopeAngle, + const float* verts, int /*nv*/, const int* tris, int nt, - unsigned char* flags) + unsigned char* areas) { - const float walkableThr = cosf(walkableSlopeAngle/180.0f*(float)M_PI); + // TODO: VC complains about unref formal variable, figure out a way to handle this better. +// rcAssert(ctx); + + const float walkableThr = cosf(walkableSlopeAngle/180.0f*RC_PI); float norm[3]; @@ -101,12 +263,45 @@ void rcMarkWalkableTriangles(const float walkableSlopeAngle, calcTriNormal(&verts[tri[0]*3], &verts[tri[1]*3], &verts[tri[2]*3], norm); // Check if the face is walkable. if (norm[1] > walkableThr) - flags[i] |= RC_WALKABLE; + areas[i] = RC_WALKABLE_AREA; + } +} + +/// @par +/// +/// Only sets the aread id's for the unwalkable triangles. Does not alter the +/// area id's for walkable triangles. +/// +/// See the #rcConfig documentation for more information on the configuration parameters. +/// +/// @see rcHeightfield, rcClearUnwalkableTriangles, rcRasterizeTriangles +void rcClearUnwalkableTriangles(rcContext* /*ctx*/, const float walkableSlopeAngle, + const float* verts, int /*nv*/, + const int* tris, int nt, + unsigned char* areas) +{ + // TODO: VC complains about unref formal variable, figure out a way to handle this better. +// rcAssert(ctx); + + const float walkableThr = cosf(walkableSlopeAngle/180.0f*RC_PI); + + float norm[3]; + + for (int i = 0; i < nt; ++i) + { + const int* tri = &tris[i*3]; + calcTriNormal(&verts[tri[0]*3], &verts[tri[1]*3], &verts[tri[2]*3], norm); + // Check if the face is walkable. + if (norm[1] <= walkableThr) + areas[i] = RC_NULL_AREA; } } -static int getSpanCount(unsigned char flags, rcHeightfield& hf) +int rcGetHeightFieldSpanCount(rcContext* /*ctx*/, rcHeightfield& hf) { + // TODO: VC complains about unref formal variable, figure out a way to handle this better. +// rcAssert(ctx); + const int w = hf.width; const int h = hf.height; int spanCount = 0; @@ -116,7 +311,7 @@ static int getSpanCount(unsigned char flags, rcHeightfield& hf) { for (rcSpan* s = hf.spans[x + y*w]; s; s = s->next) { - if (s->flags == flags) + if (s->area != RC_NULL_AREA) spanCount++; } } @@ -124,21 +319,25 @@ static int getSpanCount(unsigned char flags, rcHeightfield& hf) return spanCount; } -inline void setCon(rcCompactSpan& s, int dir, int i) -{ - s.con &= ~(0xf << (dir*4)); - s.con |= (i&0xf) << (dir*4); -} - -bool rcBuildCompactHeightfield(const int walkableHeight, const int walkableClimb, - unsigned char flags, rcHeightfield& hf, - rcCompactHeightfield& chf) +/// @par +/// +/// This is just the beginning of the process of fully building a compact heightfield. +/// Various filters may be applied applied, then the distance field and regions built. +/// E.g: #rcBuildDistanceField and #rcBuildRegions +/// +/// See the #rcConfig documentation for more information on the configuration parameters. +/// +/// @see rcAllocCompactHeightfield, rcHeightfield, rcCompactHeightfield, rcConfig +bool rcBuildCompactHeightfield(rcContext* ctx, const int walkableHeight, const int walkableClimb, + rcHeightfield& hf, rcCompactHeightfield& chf) { - rcTimeVal startTime = rcGetPerformanceTimer(); + rcAssert(ctx); + + ctx->startTimer(RC_TIMER_BUILD_COMPACTHEIGHTFIELD); const int w = hf.width; const int h = hf.height; - const int spanCount = getSpanCount(flags, hf); + const int spanCount = rcGetHeightFieldSpanCount(ctx, hf); // Fill in header. chf.width = w; @@ -147,27 +346,32 @@ bool rcBuildCompactHeightfield(const int walkableHeight, const int walkableClimb chf.walkableHeight = walkableHeight; chf.walkableClimb = walkableClimb; chf.maxRegions = 0; - vcopy(chf.bmin, hf.bmin); - vcopy(chf.bmax, hf.bmax); + rcVcopy(chf.bmin, hf.bmin); + rcVcopy(chf.bmax, hf.bmax); chf.bmax[1] += walkableHeight*hf.ch; chf.cs = hf.cs; chf.ch = hf.ch; - chf.cells = new rcCompactCell[w*h]; + chf.cells = (rcCompactCell*)rcAlloc(sizeof(rcCompactCell)*w*h, RC_ALLOC_PERM); if (!chf.cells) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.cells' (%d)", w*h); + ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.cells' (%d)", w*h); return false; } memset(chf.cells, 0, sizeof(rcCompactCell)*w*h); - chf.spans = new rcCompactSpan[spanCount]; + chf.spans = (rcCompactSpan*)rcAlloc(sizeof(rcCompactSpan)*spanCount, RC_ALLOC_PERM); if (!chf.spans) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.spans' (%d)", spanCount); + ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.spans' (%d)", spanCount); return false; } memset(chf.spans, 0, sizeof(rcCompactSpan)*spanCount); + chf.areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*spanCount, RC_ALLOC_PERM); + if (!chf.areas) + { + ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.areas' (%d)", spanCount); + return false; + } + memset(chf.areas, RC_NULL_AREA, sizeof(unsigned char)*spanCount); const int MAX_HEIGHT = 0xffff; @@ -185,12 +389,13 @@ bool rcBuildCompactHeightfield(const int walkableHeight, const int walkableClimb c.count = 0; while (s) { - if (s->flags == flags) + if (s->area != RC_NULL_AREA) { const int bot = (int)s->smax; const int top = s->next ? (int)s->next->smin : MAX_HEIGHT; chf.spans[idx].y = (unsigned short)rcClamp(bot, 0, 0xffff); chf.spans[idx].h = (unsigned char)rcClamp(top - bot, 0, 0xff); + chf.areas[idx] = s->area; idx++; c.count++; } @@ -200,6 +405,8 @@ bool rcBuildCompactHeightfield(const int walkableHeight, const int walkableClimb } // Find neighbour connections. + const int MAX_LAYERS = RC_NOT_CONNECTED-1; + int tooHighNeighbour = 0; for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) @@ -208,14 +415,16 @@ bool rcBuildCompactHeightfield(const int walkableHeight, const int walkableClimb for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) { rcCompactSpan& s = chf.spans[i]; + for (int dir = 0; dir < 4; ++dir) { - setCon(s, dir, 0xf); + rcSetCon(s, dir, RC_NOT_CONNECTED); const int nx = x + rcGetDirOffsetX(dir); const int ny = y + rcGetDirOffsetY(dir); // First check that the neighbour cell is in bounds. if (nx < 0 || ny < 0 || nx >= w || ny >= h) continue; + // Iterate over all neighbour spans and check if any of the is // accessible from current cell. const rcCompactCell& nc = chf.cells[nx+ny*w]; @@ -230,23 +439,34 @@ bool rcBuildCompactHeightfield(const int walkableHeight, const int walkableClimb if ((top - bot) >= walkableHeight && rcAbs((int)ns.y - (int)s.y) <= walkableClimb) { // Mark direction as walkable. - setCon(s, dir, k - (int)nc.index); + const int idx = k - (int)nc.index; + if (idx < 0 || idx > MAX_LAYERS) + { + tooHighNeighbour = rcMax(tooHighNeighbour, idx); + continue; + } + rcSetCon(s, dir, idx); break; } } + } } } } - rcTimeVal endTime = rcGetPerformanceTimer(); - - if (rcGetBuildTimes()) - rcGetBuildTimes()->buildCompact += rcGetDeltaTimeUsec(startTime, endTime); + if (tooHighNeighbour > MAX_LAYERS) + { + ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Heightfield has too many layers %d (max: %d)", + tooHighNeighbour, MAX_LAYERS); + } + + ctx->stopTimer(RC_TIMER_BUILD_COMPACTHEIGHTFIELD); return true; } +/* static int getHeightfieldMemoryUsage(const rcHeightfield& hf) { int size = 0; @@ -270,3 +490,4 @@ static int getCompactHeightFieldMemoryusage(const rcCompactHeightfield& chf) size += sizeof(rcCompactCell) * chf.width * chf.height; return size; } +*/
\ No newline at end of file diff --git a/extern/recastnavigation/Recast/Source/RecastAlloc.cpp b/extern/recastnavigation/Recast/Source/RecastAlloc.cpp new file mode 100644 index 00000000000..b5ec1516146 --- /dev/null +++ b/extern/recastnavigation/Recast/Source/RecastAlloc.cpp @@ -0,0 +1,88 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#include <stdlib.h> +#include <string.h> +#include "RecastAlloc.h" + +static void *rcAllocDefault(int size, rcAllocHint) +{ + return malloc(size); +} + +static void rcFreeDefault(void *ptr) +{ + free(ptr); +} + +static rcAllocFunc* sRecastAllocFunc = rcAllocDefault; +static rcFreeFunc* sRecastFreeFunc = rcFreeDefault; + +/// @see rcAlloc, rcFree +void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc) +{ + sRecastAllocFunc = allocFunc ? allocFunc : rcAllocDefault; + sRecastFreeFunc = freeFunc ? freeFunc : rcFreeDefault; +} + +/// @see rcAllocSetCustom +void* rcAlloc(int size, rcAllocHint hint) +{ + return sRecastAllocFunc(size, hint); +} + +/// @par +/// +/// @warning This function leaves the value of @p ptr unchanged. So it still +/// points to the same (now invalid) location, and not to null. +/// +/// @see rcAllocSetCustom +void rcFree(void* ptr) +{ + if (ptr) + sRecastFreeFunc(ptr); +} + +/// @class rcIntArray +/// +/// While it is possible to pre-allocate a specific array size during +/// construction or by using the #resize method, certain methods will +/// automatically resize the array as needed. +/// +/// @warning The array memory is not initialized to zero when the size is +/// manually set during construction or when using #resize. + +/// @par +/// +/// Using this method ensures the array is at least large enough to hold +/// the specified number of elements. This can improve performance by +/// avoiding auto-resizing during use. +void rcIntArray::resize(int n) +{ + if (n > m_cap) + { + if (!m_cap) m_cap = n; + while (m_cap < n) m_cap *= 2; + int* newData = (int*)rcAlloc(m_cap*sizeof(int), RC_ALLOC_TEMP); + if (m_size && newData) memcpy(newData, m_data, m_size*sizeof(int)); + rcFree(m_data); + m_data = newData; + } + m_size = n; +} + diff --git a/extern/recastnavigation/Recast/Source/RecastArea.cpp b/extern/recastnavigation/Recast/Source/RecastArea.cpp new file mode 100644 index 00000000000..a59acc53eb6 --- /dev/null +++ b/extern/recastnavigation/Recast/Source/RecastArea.cpp @@ -0,0 +1,524 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#include <float.h> +#define _USE_MATH_DEFINES +#include <math.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include "Recast.h" +#include "RecastAlloc.h" +#include "RecastAssert.h" + +/// @par +/// +/// Basically, any spans that are closer to a boundary or obstruction than the specified radius +/// are marked as unwalkable. +/// +/// This method is usually called immediately after the heightfield has been built. +/// +/// @see rcCompactHeightfield, rcBuildCompactHeightfield, rcConfig::walkableRadius +bool rcErodeWalkableArea(rcContext* ctx, int radius, rcCompactHeightfield& chf) +{ + rcAssert(ctx); + + const int w = chf.width; + const int h = chf.height; + + ctx->startTimer(RC_TIMER_ERODE_AREA); + + unsigned char* dist = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP); + if (!dist) + { + ctx->log(RC_LOG_ERROR, "erodeWalkableArea: Out of memory 'dist' (%d).", chf.spanCount); + return false; + } + + // Init distance. + memset(dist, 0xff, sizeof(unsigned char)*chf.spanCount); + + // Mark boundary cells. + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + if (chf.areas[i] == RC_NULL_AREA) + { + dist[i] = 0; + } + else + { + const rcCompactSpan& s = chf.spans[i]; + int nc = 0; + for (int dir = 0; dir < 4; ++dir) + { + if (rcGetCon(s, dir) != RC_NOT_CONNECTED) + { + const int nx = x + rcGetDirOffsetX(dir); + const int ny = y + rcGetDirOffsetY(dir); + const int ni = (int)chf.cells[nx+ny*w].index + rcGetCon(s, dir); + if (chf.areas[ni] != RC_NULL_AREA) + { + nc++; + } + } + } + // At least one missing neighbour. + if (nc != 4) + dist[i] = 0; + } + } + } + } + + unsigned char nd; + + // Pass 1 + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + const rcCompactSpan& s = chf.spans[i]; + + if (rcGetCon(s, 0) != RC_NOT_CONNECTED) + { + // (-1,0) + const int ax = x + rcGetDirOffsetX(0); + const int ay = y + rcGetDirOffsetY(0); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0); + const rcCompactSpan& as = chf.spans[ai]; + nd = (unsigned char)rcMin((int)dist[ai]+2, 255); + if (nd < dist[i]) + dist[i] = nd; + + // (-1,-1) + if (rcGetCon(as, 3) != RC_NOT_CONNECTED) + { + const int aax = ax + rcGetDirOffsetX(3); + const int aay = ay + rcGetDirOffsetY(3); + const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 3); + nd = (unsigned char)rcMin((int)dist[aai]+3, 255); + if (nd < dist[i]) + dist[i] = nd; + } + } + if (rcGetCon(s, 3) != RC_NOT_CONNECTED) + { + // (0,-1) + const int ax = x + rcGetDirOffsetX(3); + const int ay = y + rcGetDirOffsetY(3); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3); + const rcCompactSpan& as = chf.spans[ai]; + nd = (unsigned char)rcMin((int)dist[ai]+2, 255); + if (nd < dist[i]) + dist[i] = nd; + + // (1,-1) + if (rcGetCon(as, 2) != RC_NOT_CONNECTED) + { + const int aax = ax + rcGetDirOffsetX(2); + const int aay = ay + rcGetDirOffsetY(2); + const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 2); + nd = (unsigned char)rcMin((int)dist[aai]+3, 255); + if (nd < dist[i]) + dist[i] = nd; + } + } + } + } + } + + // Pass 2 + for (int y = h-1; y >= 0; --y) + { + for (int x = w-1; x >= 0; --x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + const rcCompactSpan& s = chf.spans[i]; + + if (rcGetCon(s, 2) != RC_NOT_CONNECTED) + { + // (1,0) + const int ax = x + rcGetDirOffsetX(2); + const int ay = y + rcGetDirOffsetY(2); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 2); + const rcCompactSpan& as = chf.spans[ai]; + nd = (unsigned char)rcMin((int)dist[ai]+2, 255); + if (nd < dist[i]) + dist[i] = nd; + + // (1,1) + if (rcGetCon(as, 1) != RC_NOT_CONNECTED) + { + const int aax = ax + rcGetDirOffsetX(1); + const int aay = ay + rcGetDirOffsetY(1); + const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 1); + nd = (unsigned char)rcMin((int)dist[aai]+3, 255); + if (nd < dist[i]) + dist[i] = nd; + } + } + if (rcGetCon(s, 1) != RC_NOT_CONNECTED) + { + // (0,1) + const int ax = x + rcGetDirOffsetX(1); + const int ay = y + rcGetDirOffsetY(1); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 1); + const rcCompactSpan& as = chf.spans[ai]; + nd = (unsigned char)rcMin((int)dist[ai]+2, 255); + if (nd < dist[i]) + dist[i] = nd; + + // (-1,1) + if (rcGetCon(as, 0) != RC_NOT_CONNECTED) + { + const int aax = ax + rcGetDirOffsetX(0); + const int aay = ay + rcGetDirOffsetY(0); + const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 0); + nd = (unsigned char)rcMin((int)dist[aai]+3, 255); + if (nd < dist[i]) + dist[i] = nd; + } + } + } + } + } + + const unsigned char thr = (unsigned char)(radius*2); + for (int i = 0; i < chf.spanCount; ++i) + if (dist[i] < thr) + chf.areas[i] = RC_NULL_AREA; + + rcFree(dist); + + ctx->stopTimer(RC_TIMER_ERODE_AREA); + + return true; +} + +static void insertSort(unsigned char* a, const int n) +{ + int i, j; + for (i = 1; i < n; i++) + { + const unsigned char value = a[i]; + for (j = i - 1; j >= 0 && a[j] > value; j--) + a[j+1] = a[j]; + a[j+1] = value; + } +} + +/// @par +/// +/// This filter is usually applied after applying area id's using functions +/// such as #rcMarkBoxArea, #rcMarkConvexPolyArea, and #rcMarkCylinderArea. +/// +/// @see rcCompactHeightfield +bool rcMedianFilterWalkableArea(rcContext* ctx, rcCompactHeightfield& chf) +{ + rcAssert(ctx); + + const int w = chf.width; + const int h = chf.height; + + ctx->startTimer(RC_TIMER_MEDIAN_AREA); + + unsigned char* areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP); + if (!areas) + { + ctx->log(RC_LOG_ERROR, "medianFilterWalkableArea: Out of memory 'areas' (%d).", chf.spanCount); + return false; + } + + // Init distance. + memset(areas, 0xff, sizeof(unsigned char)*chf.spanCount); + + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + const rcCompactSpan& s = chf.spans[i]; + if (chf.areas[i] == RC_NULL_AREA) + { + areas[i] = chf.areas[i]; + continue; + } + + unsigned char nei[9]; + for (int j = 0; j < 9; ++j) + nei[j] = chf.areas[i]; + + for (int dir = 0; dir < 4; ++dir) + { + if (rcGetCon(s, dir) != RC_NOT_CONNECTED) + { + const int ax = x + rcGetDirOffsetX(dir); + const int ay = y + rcGetDirOffsetY(dir); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir); + if (chf.areas[ai] != RC_NULL_AREA) + nei[dir*2+0] = chf.areas[ai]; + + const rcCompactSpan& as = chf.spans[ai]; + const int dir2 = (dir+1) & 0x3; + if (rcGetCon(as, dir2) != RC_NOT_CONNECTED) + { + const int ax2 = ax + rcGetDirOffsetX(dir2); + const int ay2 = ay + rcGetDirOffsetY(dir2); + const int ai2 = (int)chf.cells[ax2+ay2*w].index + rcGetCon(as, dir2); + if (chf.areas[ai2] != RC_NULL_AREA) + nei[dir*2+1] = chf.areas[ai2]; + } + } + } + insertSort(nei, 9); + areas[i] = nei[4]; + } + } + } + + memcpy(chf.areas, areas, sizeof(unsigned char)*chf.spanCount); + + rcFree(areas); + + ctx->stopTimer(RC_TIMER_MEDIAN_AREA); + + return true; +} + +/// @par +/// +/// The value of spacial parameters are in world units. +/// +/// @see rcCompactHeightfield, rcMedianFilterWalkableArea +void rcMarkBoxArea(rcContext* ctx, const float* bmin, const float* bmax, unsigned char areaId, + rcCompactHeightfield& chf) +{ + rcAssert(ctx); + + ctx->startTimer(RC_TIMER_MARK_BOX_AREA); + + int minx = (int)((bmin[0]-chf.bmin[0])/chf.cs); + int miny = (int)((bmin[1]-chf.bmin[1])/chf.ch); + int minz = (int)((bmin[2]-chf.bmin[2])/chf.cs); + int maxx = (int)((bmax[0]-chf.bmin[0])/chf.cs); + int maxy = (int)((bmax[1]-chf.bmin[1])/chf.ch); + int maxz = (int)((bmax[2]-chf.bmin[2])/chf.cs); + + if (maxx < 0) return; + if (minx >= chf.width) return; + if (maxz < 0) return; + if (minz >= chf.height) return; + + if (minx < 0) minx = 0; + if (maxx >= chf.width) maxx = chf.width-1; + if (minz < 0) minz = 0; + if (maxz >= chf.height) maxz = chf.height-1; + + for (int z = minz; z <= maxz; ++z) + { + for (int x = minx; x <= maxx; ++x) + { + const rcCompactCell& c = chf.cells[x+z*chf.width]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + rcCompactSpan& s = chf.spans[i]; + if ((int)s.y >= miny && (int)s.y <= maxy) + { + if (chf.areas[i] != RC_NULL_AREA) + chf.areas[i] = areaId; + } + } + } + } + + ctx->stopTimer(RC_TIMER_MARK_BOX_AREA); + +} + + +static int pointInPoly(int nvert, const float* verts, const float* p) +{ + int i, j, c = 0; + for (i = 0, j = nvert-1; i < nvert; j = i++) + { + const float* vi = &verts[i*3]; + const float* vj = &verts[j*3]; + if (((vi[2] > p[2]) != (vj[2] > p[2])) && + (p[0] < (vj[0]-vi[0]) * (p[2]-vi[2]) / (vj[2]-vi[2]) + vi[0]) ) + c = !c; + } + return c; +} + +/// @par +/// +/// The value of spacial parameters are in world units. +/// +/// The y-values of the polygon vertices are ignored. So the polygon is effectively +/// projected onto the xz-plane at @p hmin, then extruded to @p hmax. +/// +/// @see rcCompactHeightfield, rcMedianFilterWalkableArea +void rcMarkConvexPolyArea(rcContext* ctx, const float* verts, const int nverts, + const float hmin, const float hmax, unsigned char areaId, + rcCompactHeightfield& chf) +{ + rcAssert(ctx); + + ctx->startTimer(RC_TIMER_MARK_CONVEXPOLY_AREA); + + float bmin[3], bmax[3]; + rcVcopy(bmin, verts); + rcVcopy(bmax, verts); + for (int i = 1; i < nverts; ++i) + { + rcVmin(bmin, &verts[i*3]); + rcVmax(bmax, &verts[i*3]); + } + bmin[1] = hmin; + bmax[1] = hmax; + + int minx = (int)((bmin[0]-chf.bmin[0])/chf.cs); + int miny = (int)((bmin[1]-chf.bmin[1])/chf.ch); + int minz = (int)((bmin[2]-chf.bmin[2])/chf.cs); + int maxx = (int)((bmax[0]-chf.bmin[0])/chf.cs); + int maxy = (int)((bmax[1]-chf.bmin[1])/chf.ch); + int maxz = (int)((bmax[2]-chf.bmin[2])/chf.cs); + + if (maxx < 0) return; + if (minx >= chf.width) return; + if (maxz < 0) return; + if (minz >= chf.height) return; + + if (minx < 0) minx = 0; + if (maxx >= chf.width) maxx = chf.width-1; + if (minz < 0) minz = 0; + if (maxz >= chf.height) maxz = chf.height-1; + + + // TODO: Optimize. + for (int z = minz; z <= maxz; ++z) + { + for (int x = minx; x <= maxx; ++x) + { + const rcCompactCell& c = chf.cells[x+z*chf.width]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + rcCompactSpan& s = chf.spans[i]; + if (chf.areas[i] == RC_NULL_AREA) + continue; + if ((int)s.y >= miny && (int)s.y <= maxy) + { + float p[3]; + p[0] = chf.bmin[0] + (x+0.5f)*chf.cs; + p[1] = 0; + p[2] = chf.bmin[2] + (z+0.5f)*chf.cs; + + if (pointInPoly(nverts, verts, p)) + { + chf.areas[i] = areaId; + } + } + } + } + } + + ctx->stopTimer(RC_TIMER_MARK_CONVEXPOLY_AREA); +} + +/// @par +/// +/// The value of spacial parameters are in world units. +/// +/// @see rcCompactHeightfield, rcMedianFilterWalkableArea +void rcMarkCylinderArea(rcContext* ctx, const float* pos, + const float r, const float h, unsigned char areaId, + rcCompactHeightfield& chf) +{ + rcAssert(ctx); + + ctx->startTimer(RC_TIMER_MARK_CYLINDER_AREA); + + float bmin[3], bmax[3]; + bmin[0] = pos[0] - r; + bmin[1] = pos[1]; + bmin[2] = pos[2] - r; + bmax[0] = pos[0] + r; + bmax[1] = pos[1] + h; + bmax[2] = pos[2] + r; + const float r2 = r*r; + + int minx = (int)((bmin[0]-chf.bmin[0])/chf.cs); + int miny = (int)((bmin[1]-chf.bmin[1])/chf.ch); + int minz = (int)((bmin[2]-chf.bmin[2])/chf.cs); + int maxx = (int)((bmax[0]-chf.bmin[0])/chf.cs); + int maxy = (int)((bmax[1]-chf.bmin[1])/chf.ch); + int maxz = (int)((bmax[2]-chf.bmin[2])/chf.cs); + + if (maxx < 0) return; + if (minx >= chf.width) return; + if (maxz < 0) return; + if (minz >= chf.height) return; + + if (minx < 0) minx = 0; + if (maxx >= chf.width) maxx = chf.width-1; + if (minz < 0) minz = 0; + if (maxz >= chf.height) maxz = chf.height-1; + + + for (int z = minz; z <= maxz; ++z) + { + for (int x = minx; x <= maxx; ++x) + { + const rcCompactCell& c = chf.cells[x+z*chf.width]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + rcCompactSpan& s = chf.spans[i]; + + if (chf.areas[i] == RC_NULL_AREA) + continue; + + if ((int)s.y >= miny && (int)s.y <= maxy) + { + const float sx = chf.bmin[0] + (x+0.5f)*chf.cs; + const float sz = chf.bmin[2] + (z+0.5f)*chf.cs; + const float dx = sx - pos[0]; + const float dz = sz - pos[2]; + + if (dx*dx + dz*dz < r2) + { + chf.areas[i] = areaId; + } + } + } + } + } + + ctx->stopTimer(RC_TIMER_MARK_CYLINDER_AREA); +} diff --git a/extern/recastnavigation/Recast/Source/RecastContour.cpp b/extern/recastnavigation/Recast/Source/RecastContour.cpp index 96f763a18f3..078c464e5f4 100644 --- a/extern/recastnavigation/Recast/Source/RecastContour.cpp +++ b/extern/recastnavigation/Recast/Source/RecastContour.cpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2009 Mikko Mononen memon@inside.org +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -21,8 +21,8 @@ #include <string.h> #include <stdio.h> #include "Recast.h" -#include "RecastLog.h" -#include "RecastTimer.h" +#include "RecastAlloc.h" +#include "RecastAssert.h" static int getCornerHeight(int x, int y, int i, int dir, @@ -33,44 +33,46 @@ static int getCornerHeight(int x, int y, int i, int dir, int ch = (int)s.y; int dirp = (dir+1) & 0x3; - unsigned short regs[4] = {0,0,0,0}; + unsigned int regs[4] = {0,0,0,0}; - regs[0] = s.reg; + // Combine region and area codes in order to prevent + // border vertices which are in between two areas to be removed. + regs[0] = chf.spans[i].reg | (chf.areas[i] << 16); - if (rcGetCon(s, dir) != 0xf) + if (rcGetCon(s, dir) != RC_NOT_CONNECTED) { const int ax = x + rcGetDirOffsetX(dir); const int ay = y + rcGetDirOffsetY(dir); const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir); const rcCompactSpan& as = chf.spans[ai]; ch = rcMax(ch, (int)as.y); - regs[1] = as.reg; - if (rcGetCon(as, dirp) != 0xf) + regs[1] = chf.spans[ai].reg | (chf.areas[ai] << 16); + if (rcGetCon(as, dirp) != RC_NOT_CONNECTED) { const int ax2 = ax + rcGetDirOffsetX(dirp); const int ay2 = ay + rcGetDirOffsetY(dirp); const int ai2 = (int)chf.cells[ax2+ay2*chf.width].index + rcGetCon(as, dirp); const rcCompactSpan& as2 = chf.spans[ai2]; ch = rcMax(ch, (int)as2.y); - regs[2] = as2.reg; + regs[2] = chf.spans[ai2].reg | (chf.areas[ai2] << 16); } } - if (rcGetCon(s, dirp) != 0xf) + if (rcGetCon(s, dirp) != RC_NOT_CONNECTED) { const int ax = x + rcGetDirOffsetX(dirp); const int ay = y + rcGetDirOffsetY(dirp); const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dirp); const rcCompactSpan& as = chf.spans[ai]; ch = rcMax(ch, (int)as.y); - regs[3] = as.reg; - if (rcGetCon(as, dir) != 0xf) + regs[3] = chf.spans[ai].reg | (chf.areas[ai] << 16); + if (rcGetCon(as, dir) != RC_NOT_CONNECTED) { const int ax2 = ax + rcGetDirOffsetX(dir); const int ay2 = ay + rcGetDirOffsetY(dir); const int ai2 = (int)chf.cells[ax2+ay2*chf.width].index + rcGetCon(as, dir); const rcCompactSpan& as2 = chf.spans[ai2]; ch = rcMax(ch, (int)as2.y); - regs[2] = as2.reg; + regs[2] = chf.spans[ai2].reg | (chf.areas[ai2] << 16); } } @@ -86,8 +88,9 @@ static int getCornerHeight(int x, int y, int i, int dir, // followed by two interior cells and none of the regions are out of bounds. const bool twoSameExts = (regs[a] & regs[b] & RC_BORDER_REG) != 0 && regs[a] == regs[b]; const bool twoInts = ((regs[c] | regs[d]) & RC_BORDER_REG) == 0; + const bool intsSameArea = (regs[c]>>16) == (regs[d]>>16); const bool noZeros = regs[a] != 0 && regs[b] != 0 && regs[c] != 0 && regs[d] != 0; - if (twoSameExts && twoInts && noZeros) + if (twoSameExts && twoInts && intsSameArea && noZeros) { isBorderVertex = true; break; @@ -109,6 +112,8 @@ static void walkContour(int x, int y, int i, unsigned char startDir = dir; int starti = i; + const unsigned char area = chf.areas[i]; + int iter = 0; while (++iter < 40000) { @@ -116,6 +121,7 @@ static void walkContour(int x, int y, int i, { // Choose the edge corner bool isBorderVertex = false; + bool isAreaBorder = false; int px = x; int py = getCornerHeight(x, y, i, dir, chf, isBorderVertex); int pz = y; @@ -127,16 +133,19 @@ static void walkContour(int x, int y, int i, } int r = 0; const rcCompactSpan& s = chf.spans[i]; - if (rcGetCon(s, dir) != 0xf) + if (rcGetCon(s, dir) != RC_NOT_CONNECTED) { const int ax = x + rcGetDirOffsetX(dir); const int ay = y + rcGetDirOffsetY(dir); const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir); - const rcCompactSpan& as = chf.spans[ai]; - r = (int)as.reg; + r = (int)chf.spans[ai].reg; + if (area != chf.areas[ai]) + isAreaBorder = true; } if (isBorderVertex) r |= RC_BORDER_VERTEX; + if (isAreaBorder) + r |= RC_AREA_BORDER; points.push(px); points.push(py); points.push(pz); @@ -151,7 +160,7 @@ static void walkContour(int x, int y, int i, const int nx = x + rcGetDirOffsetX(dir); const int ny = y + rcGetDirOffsetY(dir); const rcCompactSpan& s = chf.spans[i]; - if (rcGetCon(s, dir) != 0xf) + if (rcGetCon(s, dir) != RC_NOT_CONNECTED) { const rcCompactCell& nc = chf.cells[nx+ny*chf.width]; ni = (int)nc.index + rcGetCon(s, dir); @@ -174,9 +183,9 @@ static void walkContour(int x, int y, int i, } } -static float distancePtSeg(int x, int y, int z, - int px, int py, int pz, - int qx, int qy, int qz) +static float distancePtSeg(const int x, const int z, + const int px, const int pz, + const int qx, const int qz) { /* float pqx = (float)(qx - px); float pqy = (float)(qy - py); @@ -218,20 +227,40 @@ static float distancePtSeg(int x, int y, int z, return dx*dx + dz*dz; } -static void simplifyContour(rcIntArray& points, rcIntArray& simplified, float maxError, int maxEdgeLen) +static void simplifyContour(rcIntArray& points, rcIntArray& simplified, + const float maxError, const int maxEdgeLen, const int buildFlags) { // Add initial points. - bool noConnections = true; + bool hasConnections = false; for (int i = 0; i < points.size(); i += 4) { - if ((points[i+3] & 0xffff) != 0) + if ((points[i+3] & RC_CONTOUR_REG_MASK) != 0) { - noConnections = false; + hasConnections = true; break; } } - if (noConnections) + if (hasConnections) + { + // The contour has some portals to other regions. + // Add a new point to every location where the region changes. + for (int i = 0, ni = points.size()/4; i < ni; ++i) + { + int ii = (i+1) % ni; + const bool differentRegs = (points[i*4+3] & RC_CONTOUR_REG_MASK) != (points[ii*4+3] & RC_CONTOUR_REG_MASK); + const bool areaBorders = (points[i*4+3] & RC_AREA_BORDER) != (points[ii*4+3] & RC_AREA_BORDER); + if (differentRegs || areaBorders) + { + simplified.push(points[i*4+0]); + simplified.push(points[i*4+1]); + simplified.push(points[i*4+2]); + simplified.push(i); + } + } + } + + if (simplified.size() == 0) { // If there is no connections at all, // create some initial points for the simplification process. @@ -256,7 +285,7 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified, float ma llz = z; lli = i/4; } - if (x >= urx || (x == urx && z > urz)) + if (x > urx || (x == urx && z > urz)) { urx = x; ury = y; @@ -274,22 +303,6 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified, float ma simplified.push(urz); simplified.push(uri); } - else - { - // The contour has some portals to other regions. - // Add a new point to every location where the region changes. - for (int i = 0, ni = points.size()/4; i < ni; ++i) - { - int ii = (i+1) % ni; - if ((points[i*4+3] & 0xffff) != (points[ii*4+3] & 0xffff)) - { - simplified.push(points[i*4+0]); - simplified.push(points[i*4+1]); - simplified.push(points[i*4+2]); - simplified.push(i); - } - } - } // Add points until all raw points are within // error tolerance to the simplified shape. @@ -298,34 +311,48 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified, float ma { int ii = (i+1) % (simplified.size()/4); - int ax = simplified[i*4+0]; - int ay = simplified[i*4+1]; - int az = simplified[i*4+2]; - int ai = simplified[i*4+3]; - - int bx = simplified[ii*4+0]; - int by = simplified[ii*4+1]; - int bz = simplified[ii*4+2]; - int bi = simplified[ii*4+3]; + const int ax = simplified[i*4+0]; + const int az = simplified[i*4+2]; + const int ai = simplified[i*4+3]; + const int bx = simplified[ii*4+0]; + const int bz = simplified[ii*4+2]; + const int bi = simplified[ii*4+3]; + // Find maximum deviation from the segment. float maxd = 0; int maxi = -1; - int ci = (ai+1) % pn; + int ci, cinc, endi; - // Tesselate only outer edges. - if ((points[ci*4+3] & 0xffff) == 0) + // Traverse the segment in lexilogical order so that the + // max deviation is calculated similarly when traversing + // opposite segments. + if (bx > ax || (bx == ax && bz > az)) { - while (ci != bi) + cinc = 1; + ci = (ai+cinc) % pn; + endi = bi; + } + else + { + cinc = pn-1; + ci = (bi+cinc) % pn; + endi = ai; + } + + // Tessellate only outer edges or edges between areas. + if ((points[ci*4+3] & RC_CONTOUR_REG_MASK) == 0 || + (points[ci*4+3] & RC_AREA_BORDER)) + { + while (ci != endi) { - float d = distancePtSeg(points[ci*4+0], points[ci*4+1]/4, points[ci*4+2], - ax, ay/4, az, bx, by/4, bz); + float d = distancePtSeg(points[ci*4+0], points[ci*4+2], ax, az, bx, bz); if (d > maxd) { maxd = d; maxi = ci; } - ci = (ci+1) % pn; + ci = (ci+cinc) % pn; } } @@ -336,7 +363,7 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified, float ma { // Add space for the new point. simplified.resize(simplified.size()+4); - int n = simplified.size()/4; + const int n = simplified.size()/4; for (int j = n-1; j > i; --j) { simplified[j*4+0] = simplified[(j-1)*4+0]; @@ -357,33 +384,52 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified, float ma } // Split too long edges. - if (maxEdgeLen > 0) + if (maxEdgeLen > 0 && (buildFlags & (RC_CONTOUR_TESS_WALL_EDGES|RC_CONTOUR_TESS_AREA_EDGES)) != 0) { for (int i = 0; i < simplified.size()/4; ) { - int ii = (i+1) % (simplified.size()/4); - - int ax = simplified[i*4+0]; - int az = simplified[i*4+2]; - int ai = simplified[i*4+3]; + const int ii = (i+1) % (simplified.size()/4); - int bx = simplified[ii*4+0]; - int bz = simplified[ii*4+2]; - int bi = simplified[ii*4+3]; + const int ax = simplified[i*4+0]; + const int az = simplified[i*4+2]; + const int ai = simplified[i*4+3]; + const int bx = simplified[ii*4+0]; + const int bz = simplified[ii*4+2]; + const int bi = simplified[ii*4+3]; + // Find maximum deviation from the segment. int maxi = -1; int ci = (ai+1) % pn; + + // Tessellate only outer edges or edges between areas. + bool tess = false; + // Wall edges. + if ((buildFlags & RC_CONTOUR_TESS_WALL_EDGES) && (points[ci*4+3] & RC_CONTOUR_REG_MASK) == 0) + tess = true; + // Edges between areas. + if ((buildFlags & RC_CONTOUR_TESS_AREA_EDGES) && (points[ci*4+3] & RC_AREA_BORDER)) + tess = true; - // Tesselate only outer edges. - if ((points[ci*4+3] & 0xffff) == 0) + if (tess) { int dx = bx - ax; int dz = bz - az; if (dx*dx + dz*dz > maxEdgeLen*maxEdgeLen) { - int n = bi < ai ? (bi+pn - ai) : (bi - ai); - maxi = (ai + n/2) % pn; + // Round based on the segments in lexilogical order so that the + // max tesselation is consistent regardles in which direction + // segments are traversed. + if (bx > ax || (bx == ax && bz > az)) + { + const int n = bi < ai ? (bi+pn - ai) : (bi - ai); + maxi = (ai + n/2) % pn; + } + else + { + const int n = bi < ai ? (bi+pn - ai) : (bi - ai); + maxi = (ai + (n+1)/2) % pn; + } } } @@ -393,7 +439,7 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified, float ma { // Add space for the new point. simplified.resize(simplified.size()+4); - int n = simplified.size()/4; + const int n = simplified.size()/4; for (int j = n-1; j > i; --j) { simplified[j*4+0] = simplified[(j-1)*4+0]; @@ -420,7 +466,7 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified, float ma // and the neighbour region is take from the next raw point. const int ai = (simplified[i*4+3]+1) % pn; const int bi = simplified[i*4+3]; - simplified[i*4+3] = (points[ai*4+3] & 0xffff) | (points[bi*4+3] & RC_BORDER_VERTEX); + simplified[i*4+3] = (points[ai*4+3] & RC_CONTOUR_REG_MASK) | (points[bi*4+3] & RC_BORDER_VERTEX); } } @@ -446,7 +492,7 @@ static void removeDegenerateSegments(rcIntArray& simplified) simplified[j*4+2] = simplified[(j+1)*4+2]; simplified[j*4+3] = simplified[(j+1)*4+3]; } - simplified.pop(); + simplified.resize(simplified.size()-4); } } } @@ -463,25 +509,40 @@ static int calcAreaOfPolygon2D(const int* verts, const int nverts) return (area+1) / 2; } +inline bool ileft(const int* a, const int* b, const int* c) +{ + return (b[0] - a[0]) * (c[2] - a[2]) - (c[0] - a[0]) * (b[2] - a[2]) <= 0; +} + static void getClosestIndices(const int* vertsa, const int nvertsa, const int* vertsb, const int nvertsb, int& ia, int& ib) { int closestDist = 0xfffffff; + ia = -1, ib = -1; for (int i = 0; i < nvertsa; ++i) { + const int in = (i+1) % nvertsa; + const int ip = (i+nvertsa-1) % nvertsa; const int* va = &vertsa[i*4]; + const int* van = &vertsa[in*4]; + const int* vap = &vertsa[ip*4]; + for (int j = 0; j < nvertsb; ++j) { const int* vb = &vertsb[j*4]; - const int dx = vb[0] - va[0]; - const int dz = vb[2] - va[2]; - const int d = dx*dx + dz*dz; - if (d < closestDist) + // vb must be "infront" of va. + if (ileft(vap,va,vb) && ileft(va,van,vb)) { - ia = i; - ib = j; - closestDist = d; + const int dx = vb[0] - va[0]; + const int dz = vb[2] - va[2]; + const int d = dx*dx + dz*dz; + if (d < closestDist) + { + ia = i; + ib = j; + closestDist = d; + } } } } @@ -490,7 +551,7 @@ static void getClosestIndices(const int* vertsa, const int nvertsa, static bool mergeContours(rcContour& ca, rcContour& cb, int ia, int ib) { const int maxVerts = ca.nverts + cb.nverts + 2; - int* verts = new int[maxVerts*4]; + int* verts = (int*)rcAlloc(sizeof(int)*maxVerts*4, RC_ALLOC_PERM); if (!verts) return false; @@ -520,47 +581,73 @@ static bool mergeContours(rcContour& ca, rcContour& cb, int ia, int ib) nv++; } - delete [] ca.verts; + rcFree(ca.verts); ca.verts = verts; ca.nverts = nv; - delete [] cb.verts; + rcFree(cb.verts); cb.verts = 0; cb.nverts = 0; return true; } -bool rcBuildContours(rcCompactHeightfield& chf, +/// @par +/// +/// The raw contours will match the region outlines exactly. The @p maxError and @p maxEdgeLen +/// parameters control how closely the simplified contours will match the raw contours. +/// +/// Simplified contours are generated such that the vertices for portals between areas match up. +/// (They are considered mandatory vertices.) +/// +/// Setting @p maxEdgeLength to zero will disabled the edge length feature. +/// +/// See the #rcConfig documentation for more information on the configuration parameters. +/// +/// @see rcAllocContourSet, rcCompactHeightfield, rcContourSet, rcConfig +bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf, const float maxError, const int maxEdgeLen, - rcContourSet& cset) + rcContourSet& cset, const int buildFlags) { + rcAssert(ctx); + const int w = chf.width; const int h = chf.height; + const int borderSize = chf.borderSize; - rcTimeVal startTime = rcGetPerformanceTimer(); + ctx->startTimer(RC_TIMER_BUILD_CONTOURS); - vcopy(cset.bmin, chf.bmin); - vcopy(cset.bmax, chf.bmax); + rcVcopy(cset.bmin, chf.bmin); + rcVcopy(cset.bmax, chf.bmax); + if (borderSize > 0) + { + // If the heightfield was build with bordersize, remove the offset. + const float pad = borderSize*chf.cs; + cset.bmin[0] += pad; + cset.bmin[2] += pad; + cset.bmax[0] -= pad; + cset.bmax[2] -= pad; + } cset.cs = chf.cs; cset.ch = chf.ch; + cset.width = chf.width - chf.borderSize*2; + cset.height = chf.height - chf.borderSize*2; + cset.borderSize = chf.borderSize; - const int maxContours = chf.maxRegions*2; - cset.conts = new rcContour[maxContours]; + int maxContours = rcMax((int)chf.maxRegions, 8); + cset.conts = (rcContour*)rcAlloc(sizeof(rcContour)*maxContours, RC_ALLOC_PERM); if (!cset.conts) return false; cset.nconts = 0; - unsigned char* flags = new unsigned char[chf.spanCount]; + rcScopedDelete<unsigned char> flags = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP); if (!flags) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'flags'."); + ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'flags' (%d).", chf.spanCount); return false; } - rcTimeVal traceStartTime = rcGetPerformanceTimer(); - + ctx->startTimer(RC_TIMER_BUILD_CONTOURS_TRACE); // Mark boundaries. for (int y = 0; y < h; ++y) @@ -572,7 +659,7 @@ bool rcBuildContours(rcCompactHeightfield& chf, { unsigned char res = 0; const rcCompactSpan& s = chf.spans[i]; - if (!s.reg || (s.reg & RC_BORDER_REG)) + if (!chf.spans[i].reg || (chf.spans[i].reg & RC_BORDER_REG)) { flags[i] = 0; continue; @@ -580,15 +667,14 @@ bool rcBuildContours(rcCompactHeightfield& chf, for (int dir = 0; dir < 4; ++dir) { unsigned short r = 0; - if (rcGetCon(s, dir) != 0xf) + if (rcGetCon(s, dir) != RC_NOT_CONNECTED) { const int ax = x + rcGetDirOffsetX(dir); const int ay = y + rcGetDirOffsetY(dir); const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir); - const rcCompactSpan& as = chf.spans[ai]; - r = as.reg; + r = chf.spans[ai].reg; } - if (r == s.reg) + if (r == chf.spans[i].reg) res |= (1 << dir); } flags[i] = res ^ 0xf; // Inverse, mark non connected edges. @@ -596,9 +682,7 @@ bool rcBuildContours(rcCompactHeightfield& chf, } } - rcTimeVal traceEndTime = rcGetPerformanceTimer(); - - rcTimeVal simplifyStartTime = rcGetPerformanceTimer(); + ctx->stopTimer(RC_TIMER_BUILD_CONTOURS_TRACE); rcIntArray verts(256); rcIntArray simplified(64); @@ -615,36 +699,87 @@ bool rcBuildContours(rcCompactHeightfield& chf, flags[i] = 0; continue; } - unsigned short reg = chf.spans[i].reg; + const unsigned short reg = chf.spans[i].reg; if (!reg || (reg & RC_BORDER_REG)) continue; + const unsigned char area = chf.areas[i]; verts.resize(0); simplified.resize(0); + + ctx->startTimer(RC_TIMER_BUILD_CONTOURS_TRACE); walkContour(x, y, i, chf, flags, verts); - simplifyContour(verts, simplified, maxError, maxEdgeLen); + ctx->stopTimer(RC_TIMER_BUILD_CONTOURS_TRACE); + + ctx->startTimer(RC_TIMER_BUILD_CONTOURS_SIMPLIFY); + simplifyContour(verts, simplified, maxError, maxEdgeLen, buildFlags); removeDegenerateSegments(simplified); + ctx->stopTimer(RC_TIMER_BUILD_CONTOURS_SIMPLIFY); + // Store region->contour remap info. // Create contour. if (simplified.size()/4 >= 3) { if (cset.nconts >= maxContours) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcBuildContours: Too many contours %d, max %d.", cset.nconts, maxContours); - return false; + // Allocate more contours. + // This can happen when there are tiny holes in the heightfield. + const int oldMax = maxContours; + maxContours *= 2; + rcContour* newConts = (rcContour*)rcAlloc(sizeof(rcContour)*maxContours, RC_ALLOC_PERM); + for (int j = 0; j < cset.nconts; ++j) + { + newConts[j] = cset.conts[j]; + // Reset source pointers to prevent data deletion. + cset.conts[j].verts = 0; + cset.conts[j].rverts = 0; + } + rcFree(cset.conts); + cset.conts = newConts; + + ctx->log(RC_LOG_WARNING, "rcBuildContours: Expanding max contours from %d to %d.", oldMax, maxContours); } rcContour* cont = &cset.conts[cset.nconts++]; cont->nverts = simplified.size()/4; - cont->verts = new int[cont->nverts*4]; + cont->verts = (int*)rcAlloc(sizeof(int)*cont->nverts*4, RC_ALLOC_PERM); + if (!cont->verts) + { + ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'verts' (%d).", cont->nverts); + return false; + } memcpy(cont->verts, &simplified[0], sizeof(int)*cont->nverts*4); + if (borderSize > 0) + { + // If the heightfield was build with bordersize, remove the offset. + for (int i = 0; i < cont->nverts; ++i) + { + int* v = &cont->verts[i*4]; + v[0] -= borderSize; + v[2] -= borderSize; + } + } cont->nrverts = verts.size()/4; - cont->rverts = new int[cont->nrverts*4]; + cont->rverts = (int*)rcAlloc(sizeof(int)*cont->nrverts*4, RC_ALLOC_PERM); + if (!cont->rverts) + { + ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'rverts' (%d).", cont->nrverts); + return false; + } memcpy(cont->rverts, &verts[0], sizeof(int)*cont->nrverts*4); + if (borderSize > 0) + { + // If the heightfield was build with bordersize, remove the offset. + for (int i = 0; i < cont->nrverts; ++i) + { + int* v = &cont->rverts[i*4]; + v[0] -= borderSize; + v[2] -= borderSize; + } + } /* cont->cx = cont->cy = cont->cz = 0; for (int i = 0; i < cont->nverts; ++i) @@ -658,13 +793,14 @@ bool rcBuildContours(rcCompactHeightfield& chf, cont->cz /= cont->nverts;*/ cont->reg = reg; + cont->area = area; } } } } // Check and merge droppings. - // Sometimes the previous algorithms can fail and create several countours + // Sometimes the previous algorithms can fail and create several contours // per area. This pass will try to merge the holes into the main region. for (int i = 0; i < cset.nconts; ++i) { @@ -689,44 +825,29 @@ bool rcBuildContours(rcCompactHeightfield& chf, } if (mergeIdx == -1) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_WARNING, "rcBuildContours: Could not find merge target for bad contour %d.", i); + ctx->log(RC_LOG_WARNING, "rcBuildContours: Could not find merge target for bad contour %d.", i); } else { rcContour& mcont = cset.conts[mergeIdx]; // Merge by closest points. - int ia, ib; + int ia = 0, ib = 0; getClosestIndices(mcont.verts, mcont.nverts, cont.verts, cont.nverts, ia, ib); + if (ia == -1 || ib == -1) + { + ctx->log(RC_LOG_WARNING, "rcBuildContours: Failed to find merge points for %d and %d.", i, mergeIdx); + continue; + } if (!mergeContours(mcont, cont, ia, ib)) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_WARNING, "rcBuildContours: Failed to merge contours %d and %d.", i, mergeIdx); + ctx->log(RC_LOG_WARNING, "rcBuildContours: Failed to merge contours %d and %d.", i, mergeIdx); + continue; } } } } - - delete [] flags; - - rcTimeVal simplifyEndTime = rcGetPerformanceTimer(); - - rcTimeVal endTime = rcGetPerformanceTimer(); - -// if (rcGetLog()) -// { -// rcGetLog()->log(RC_LOG_PROGRESS, "Create contours: %.3f ms", rcGetDeltaTimeUsec(startTime, endTime)/1000.0f); -// rcGetLog()->log(RC_LOG_PROGRESS, " - boundary: %.3f ms", rcGetDeltaTimeUsec(boundaryStartTime, boundaryEndTime)/1000.0f); -// rcGetLog()->log(RC_LOG_PROGRESS, " - contour: %.3f ms", rcGetDeltaTimeUsec(contourStartTime, contourEndTime)/1000.0f); -// } - - if (rcGetBuildTimes()) - { - rcGetBuildTimes()->buildContours += rcGetDeltaTimeUsec(startTime, endTime); - rcGetBuildTimes()->buildContoursTrace += rcGetDeltaTimeUsec(traceStartTime, traceEndTime); - rcGetBuildTimes()->buildContoursSimplify += rcGetDeltaTimeUsec(simplifyStartTime, simplifyEndTime); - } + ctx->stopTimer(RC_TIMER_BUILD_CONTOURS); return true; } diff --git a/extern/recastnavigation/Recast/Source/RecastFilter.cpp b/extern/recastnavigation/Recast/Source/RecastFilter.cpp index ebe60714a18..bf985c362c9 100644 --- a/extern/recastnavigation/Recast/Source/RecastFilter.cpp +++ b/extern/recastnavigation/Recast/Source/RecastFilter.cpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2009 Mikko Mononen memon@inside.org +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -20,15 +20,73 @@ #include <math.h> #include <stdio.h> #include "Recast.h" -#include "RecastLog.h" -#include "RecastTimer.h" +#include "RecastAssert.h" +/// @par +/// +/// Allows the formation of walkable regions that will flow over low lying +/// objects such as curbs, and up structures such as stairways. +/// +/// Two neighboring spans are walkable if: <tt>rcAbs(currentSpan.smax - neighborSpan.smax) < waklableClimb</tt> +/// +/// @warning Will override the effect of #rcFilterLedgeSpans. So if both filters are used, call +/// #rcFilterLedgeSpans after calling this filter. +/// +/// @see rcHeightfield, rcConfig +void rcFilterLowHangingWalkableObstacles(rcContext* ctx, const int walkableClimb, rcHeightfield& solid) +{ + rcAssert(ctx); + + ctx->startTimer(RC_TIMER_FILTER_LOW_OBSTACLES); + + const int w = solid.width; + const int h = solid.height; + + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + { + rcSpan* ps = 0; + bool previousWalkable = false; + unsigned char previousArea = RC_NULL_AREA; + + for (rcSpan* s = solid.spans[x + y*w]; s; ps = s, s = s->next) + { + const bool walkable = s->area != RC_NULL_AREA; + // If current span is not walkable, but there is walkable + // span just below it, mark the span above it walkable too. + if (!walkable && previousWalkable) + { + if (rcAbs((int)s->smax - (int)ps->smax) <= walkableClimb) + s->area = previousArea; + } + // Copy walkable flag so that it cannot propagate + // past multiple non-walkable objects. + previousWalkable = walkable; + previousArea = s->area; + } + } + } + + ctx->stopTimer(RC_TIMER_FILTER_LOW_OBSTACLES); +} -void rcFilterLedgeSpans(const int walkableHeight, - const int walkableClimb, +/// @par +/// +/// A ledge is a span with one or more neighbors whose maximum is further away than @p walkableClimb +/// from the current span's maximum. +/// This method removes the impact of the overestimation of conservative voxelization +/// so the resulting mesh will not have regions hanging in the air over ledges. +/// +/// A span is a ledge if: <tt>rcAbs(currentSpan.smax - neighborSpan.smax) > walkableClimb</tt> +/// +/// @see rcHeightfield, rcConfig +void rcFilterLedgeSpans(rcContext* ctx, const int walkableHeight, const int walkableClimb, rcHeightfield& solid) { - rcTimeVal startTime = rcGetPerformanceTimer(); + rcAssert(ctx); + + ctx->startTimer(RC_TIMER_FILTER_BORDER); const int w = solid.width; const int h = solid.height; @@ -42,15 +100,19 @@ void rcFilterLedgeSpans(const int walkableHeight, for (rcSpan* s = solid.spans[x + y*w]; s; s = s->next) { // Skip non walkable spans. - if ((s->flags & RC_WALKABLE) == 0) + if (s->area == RC_NULL_AREA) continue; - const int bot = (int)s->smax; - const int top = s->next ? (int)s->next->smin : MAX_HEIGHT; + const int bot = (int)(s->smax); + const int top = s->next ? (int)(s->next->smin) : MAX_HEIGHT; // Find neighbours minimum height. int minh = MAX_HEIGHT; + // Min and max height of accessible neighbours. + int asmin = s->smax; + int asmax = s->smax; + for (int dir = 0; dir < 4; ++dir) { int dx = x + rcGetDirOffsetX(dir); @@ -77,30 +139,49 @@ void rcFilterLedgeSpans(const int walkableHeight, ntop = ns->next ? (int)ns->next->smin : MAX_HEIGHT; // Skip neightbour if the gap between the spans is too small. if (rcMin(top,ntop) - rcMax(bot,nbot) > walkableHeight) + { minh = rcMin(minh, nbot - bot); + + // Find min/max accessible neighbour height. + if (rcAbs(nbot - bot) <= walkableClimb) + { + if (nbot < asmin) asmin = nbot; + if (nbot > asmax) asmax = nbot; + } + + } } } // The current span is close to a ledge if the drop to any // neighbour span is less than the walkableClimb. if (minh < -walkableClimb) - s->flags &= ~RC_WALKABLE; - + s->area = RC_NULL_AREA; + + // If the difference between all neighbours is too large, + // we are at steep slope, mark the span as ledge. + if ((asmax - asmin) > walkableClimb) + { + s->area = RC_NULL_AREA; + } } } } - rcTimeVal endTime = rcGetPerformanceTimer(); -// if (rcGetLog()) -// rcGetLog()->log(RC_LOG_PROGRESS, "Filter border: %.3f ms", rcGetDeltaTimeUsec(startTime, endTime)/1000.0f); - if (rcGetBuildTimes()) - rcGetBuildTimes()->filterBorder += rcGetDeltaTimeUsec(startTime, endTime); + ctx->stopTimer(RC_TIMER_FILTER_BORDER); } -void rcFilterWalkableLowHeightSpans(int walkableHeight, - rcHeightfield& solid) +/// @par +/// +/// For this filter, the clearance above the span is the distance from the span's +/// maximum to the next higher span's minimum. (Same grid column.) +/// +/// @see rcHeightfield, rcConfig +void rcFilterWalkableLowHeightSpans(rcContext* ctx, int walkableHeight, rcHeightfield& solid) { - rcTimeVal startTime = rcGetPerformanceTimer(); + rcAssert(ctx); + + ctx->startTimer(RC_TIMER_FILTER_WALKABLE); const int w = solid.width; const int h = solid.height; @@ -114,136 +195,13 @@ void rcFilterWalkableLowHeightSpans(int walkableHeight, { for (rcSpan* s = solid.spans[x + y*w]; s; s = s->next) { - const int bot = (int)s->smax; - const int top = s->next ? (int)s->next->smin : MAX_HEIGHT; + const int bot = (int)(s->smax); + const int top = s->next ? (int)(s->next->smin) : MAX_HEIGHT; if ((top - bot) <= walkableHeight) - s->flags &= ~RC_WALKABLE; + s->area = RC_NULL_AREA; } } } - rcTimeVal endTime = rcGetPerformanceTimer(); - -// if (rcGetLog()) -// rcGetLog()->log(RC_LOG_PROGRESS, "Filter walkable: %.3f ms", rcGetDeltaTimeUsec(startTime, endTime)/1000.0f); - if (rcGetBuildTimes()) - rcGetBuildTimes()->filterWalkable += rcGetDeltaTimeUsec(startTime, endTime); -} - - -struct rcReachableSeed -{ - inline void set(int ix, int iy, rcSpan* is) - { - x = (unsigned short)ix; - y = (unsigned short)iy; - s = is; - } - unsigned short x, y; - rcSpan* s; -}; - -bool rcMarkReachableSpans(const int walkableHeight, - const int walkableClimb, - rcHeightfield& solid) -{ - const int w = solid.width; - const int h = solid.height; - const int MAX_HEIGHT = 0xffff; - - rcTimeVal startTime = rcGetPerformanceTimer(); - - // Build navigable space. - const int MAX_SEEDS = w*h; - rcReachableSeed* stack = new rcReachableSeed[MAX_SEEDS]; - if (!stack) - { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcMarkReachableSpans: Out of memory 'stack' (%d).", MAX_SEEDS); - return false; - } - int stackSize = 0; - - for (int y = 0; y < h; ++y) - { - for (int x = 0; x < w; ++x) - { - rcSpan* topSpan = solid.spans[x + y*w]; - if (!topSpan) - continue; - while (topSpan->next) - topSpan = topSpan->next; - - // If the span is not walkable, skip it. - if ((topSpan->flags & RC_WALKABLE) == 0) - continue; - // If the span has been visited already, skip it. - if (topSpan->flags & RC_REACHABLE) - continue; - - // Start flood fill. - topSpan->flags |= RC_REACHABLE; - stackSize = 0; - stack[stackSize].set(x, y, topSpan); - stackSize++; - - while (stackSize) - { - // Pop a seed from the stack. - stackSize--; - rcReachableSeed cur = stack[stackSize]; - - const int bot = (int)cur.s->smax; - const int top = cur.s->next ? (int)cur.s->next->smin : MAX_HEIGHT; - - // Visit neighbours in all 4 directions. - for (int dir = 0; dir < 4; ++dir) - { - int dx = (int)cur.x + rcGetDirOffsetX(dir); - int dy = (int)cur.y + rcGetDirOffsetY(dir); - // Skip neighbour which are out of bounds. - if (dx < 0 || dy < 0 || dx >= w || dy >= h) - continue; - for (rcSpan* ns = solid.spans[dx + dy*w]; ns; ns = ns->next) - { - // Skip neighbour if it is not walkable. - if ((ns->flags & RC_WALKABLE) == 0) - continue; - // Skip the neighbour if it has been visited already. - if (ns->flags & RC_REACHABLE) - continue; - - const int nbot = (int)ns->smax; - const int ntop = ns->next ? (int)ns->next->smin : MAX_HEIGHT; - // Skip neightbour if the gap between the spans is too small. - if (rcMin(top,ntop) - rcMax(bot,nbot) < walkableHeight) - continue; - // Skip neightbour if the climb height to the neighbour is too high. - if (rcAbs(nbot - bot) >= walkableClimb) - continue; - - // This neighbour has not been visited yet. - // Mark it as reachable and add it to the seed stack. - ns->flags |= RC_REACHABLE; - if (stackSize < MAX_SEEDS) - { - stack[stackSize].set(dx, dy, ns); - stackSize++; - } - } - } - } - } - } - - delete [] stack; - - rcTimeVal endTime = rcGetPerformanceTimer(); - -// if (rcGetLog()) -// rcGetLog()->log(RC_LOG_PROGRESS, "Mark reachable: %.3f ms", rcGetDeltaTimeUsec(startTime, endTime)/1000.0f); - if (rcGetBuildTimes()) - rcGetBuildTimes()->filterMarkReachable += rcGetDeltaTimeUsec(startTime, endTime); - - return true; + ctx->stopTimer(RC_TIMER_FILTER_WALKABLE); } diff --git a/extern/recastnavigation/Recast/Source/RecastLayers.cpp b/extern/recastnavigation/Recast/Source/RecastLayers.cpp new file mode 100644 index 00000000000..617cf45fe66 --- /dev/null +++ b/extern/recastnavigation/Recast/Source/RecastLayers.cpp @@ -0,0 +1,620 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#include <float.h> +#define _USE_MATH_DEFINES +#include <math.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include "Recast.h" +#include "RecastAlloc.h" +#include "RecastAssert.h" + + +static const int RC_MAX_LAYERS = RC_NOT_CONNECTED; +static const int RC_MAX_NEIS = 16; + +struct rcLayerRegion +{ + unsigned char layers[RC_MAX_LAYERS]; + unsigned char neis[RC_MAX_NEIS]; + unsigned short ymin, ymax; + unsigned char layerId; // Layer ID + unsigned char nlayers; // Layer count + unsigned char nneis; // Neighbour count + unsigned char base; // Flag indicating if the region is hte base of merged regions. +}; + + +static void addUnique(unsigned char* a, unsigned char& an, unsigned char v) +{ + const int n = (int)an; + for (int i = 0; i < n; ++i) + if (a[i] == v) + return; + a[an] = v; + an++; +} + +static bool contains(const unsigned char* a, const unsigned char an, const unsigned char v) +{ + const int n = (int)an; + for (int i = 0; i < n; ++i) + if (a[i] == v) + return true; + return false; +} + +inline bool overlapRange(const unsigned short amin, const unsigned short amax, + const unsigned short bmin, const unsigned short bmax) +{ + return (amin > bmax || amax < bmin) ? false : true; +} + + + +struct rcLayerSweepSpan +{ + unsigned short ns; // number samples + unsigned char id; // region id + unsigned char nei; // neighbour id +}; + +/// @par +/// +/// See the #rcConfig documentation for more information on the configuration parameters. +/// +/// @see rcAllocHeightfieldLayerSet, rcCompactHeightfield, rcHeightfieldLayerSet, rcConfig +bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf, + const int borderSize, const int walkableHeight, + rcHeightfieldLayerSet& lset) +{ + rcAssert(ctx); + + ctx->startTimer(RC_TIMER_BUILD_LAYERS); + + const int w = chf.width; + const int h = chf.height; + + rcScopedDelete<unsigned char> srcReg = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP); + if (!srcReg) + { + ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'srcReg' (%d).", chf.spanCount); + return false; + } + memset(srcReg,0xff,sizeof(unsigned char)*chf.spanCount); + + const int nsweeps = chf.width; + rcScopedDelete<rcLayerSweepSpan> sweeps = (rcLayerSweepSpan*)rcAlloc(sizeof(rcLayerSweepSpan)*nsweeps, RC_ALLOC_TEMP); + if (!sweeps) + { + ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'sweeps' (%d).", nsweeps); + return false; + } + + + // Partition walkable area into monotone regions. + int prevCount[256]; + unsigned char regId = 0; + + for (int y = borderSize; y < h-borderSize; ++y) + { + memset(prevCount,0,sizeof(int)*regId); + unsigned char sweepId = 0; + + for (int x = borderSize; x < w-borderSize; ++x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + const rcCompactSpan& s = chf.spans[i]; + if (chf.areas[i] == RC_NULL_AREA) continue; + + unsigned char sid = 0xff; + + // -x + if (rcGetCon(s, 0) != RC_NOT_CONNECTED) + { + const int ax = x + rcGetDirOffsetX(0); + const int ay = y + rcGetDirOffsetY(0); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0); + if (chf.areas[ai] != RC_NULL_AREA && srcReg[ai] != 0xff) + sid = srcReg[ai]; + } + + if (sid == 0xff) + { + sid = sweepId++; + sweeps[sid].nei = 0xff; + sweeps[sid].ns = 0; + } + + // -y + if (rcGetCon(s,3) != RC_NOT_CONNECTED) + { + const int ax = x + rcGetDirOffsetX(3); + const int ay = y + rcGetDirOffsetY(3); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3); + const unsigned char nr = srcReg[ai]; + if (nr != 0xff) + { + // Set neighbour when first valid neighbour is encoutered. + if (sweeps[sid].ns == 0) + sweeps[sid].nei = nr; + + if (sweeps[sid].nei == nr) + { + // Update existing neighbour + sweeps[sid].ns++; + prevCount[nr]++; + } + else + { + // This is hit if there is nore than one neighbour. + // Invalidate the neighbour. + sweeps[sid].nei = 0xff; + } + } + } + + srcReg[i] = sid; + } + } + + // Create unique ID. + for (int i = 0; i < sweepId; ++i) + { + // If the neighbour is set and there is only one continuous connection to it, + // the sweep will be merged with the previous one, else new region is created. + if (sweeps[i].nei != 0xff && prevCount[sweeps[i].nei] == (int)sweeps[i].ns) + { + sweeps[i].id = sweeps[i].nei; + } + else + { + if (regId == 255) + { + ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Region ID overflow."); + return false; + } + sweeps[i].id = regId++; + } + } + + // Remap local sweep ids to region ids. + for (int x = borderSize; x < w-borderSize; ++x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + if (srcReg[i] != 0xff) + srcReg[i] = sweeps[srcReg[i]].id; + } + } + } + + // Allocate and init layer regions. + const int nregs = (int)regId; + rcScopedDelete<rcLayerRegion> regs = (rcLayerRegion*)rcAlloc(sizeof(rcLayerRegion)*nregs, RC_ALLOC_TEMP); + if (!regs) + { + ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'regs' (%d).", nregs); + return false; + } + memset(regs, 0, sizeof(rcLayerRegion)*nregs); + for (int i = 0; i < nregs; ++i) + { + regs[i].layerId = 0xff; + regs[i].ymin = 0xffff; + regs[i].ymax = 0; + } + + // Find region neighbours and overlapping regions. + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + + unsigned char lregs[RC_MAX_LAYERS]; + int nlregs = 0; + + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + const rcCompactSpan& s = chf.spans[i]; + const unsigned char ri = srcReg[i]; + if (ri == 0xff) continue; + + regs[ri].ymin = rcMin(regs[ri].ymin, s.y); + regs[ri].ymax = rcMax(regs[ri].ymax, s.y); + + // Collect all region layers. + if (nlregs < RC_MAX_LAYERS) + lregs[nlregs++] = ri; + + // Update neighbours + for (int dir = 0; dir < 4; ++dir) + { + if (rcGetCon(s, dir) != RC_NOT_CONNECTED) + { + const int ax = x + rcGetDirOffsetX(dir); + const int ay = y + rcGetDirOffsetY(dir); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir); + const unsigned char rai = srcReg[ai]; + if (rai != 0xff && rai != ri) + addUnique(regs[ri].neis, regs[ri].nneis, rai); + } + } + + } + + // Update overlapping regions. + for (int i = 0; i < nlregs-1; ++i) + { + for (int j = i+1; j < nlregs; ++j) + { + if (lregs[i] != lregs[j]) + { + rcLayerRegion& ri = regs[lregs[i]]; + rcLayerRegion& rj = regs[lregs[j]]; + addUnique(ri.layers, ri.nlayers, lregs[j]); + addUnique(rj.layers, rj.nlayers, lregs[i]); + } + } + } + + } + } + + // Create 2D layers from regions. + unsigned char layerId = 0; + + static const int MAX_STACK = 64; + unsigned char stack[MAX_STACK]; + int nstack = 0; + + for (int i = 0; i < nregs; ++i) + { + rcLayerRegion& root = regs[i]; + // Skip alreadu visited. + if (root.layerId != 0xff) + continue; + + // Start search. + root.layerId = layerId; + root.base = 1; + + nstack = 0; + stack[nstack++] = (unsigned char)i; + + while (nstack) + { + // Pop front + rcLayerRegion& reg = regs[stack[0]]; + nstack--; + for (int j = 0; j < nstack; ++j) + stack[j] = stack[j+1]; + + const int nneis = (int)reg.nneis; + for (int j = 0; j < nneis; ++j) + { + const unsigned char nei = reg.neis[j]; + rcLayerRegion& regn = regs[nei]; + // Skip already visited. + if (regn.layerId != 0xff) + continue; + // Skip if the neighbour is overlapping root region. + if (contains(root.layers, root.nlayers, nei)) + continue; + // Skip if the height range would become too large. + const int ymin = rcMin(root.ymin, regn.ymin); + const int ymax = rcMin(root.ymax, regn.ymax); + if ((ymax - ymin) >= 255) + continue; + + if (nstack < MAX_STACK) + { + // Deepen + stack[nstack++] = (unsigned char)nei; + + // Mark layer id + regn.layerId = layerId; + // Merge current layers to root. + for (int k = 0; k < regn.nlayers; ++k) + addUnique(root.layers, root.nlayers, regn.layers[k]); + root.ymin = rcMin(root.ymin, regn.ymin); + root.ymax = rcMax(root.ymax, regn.ymax); + } + } + } + + layerId++; + } + + // Merge non-overlapping regions that are close in height. + const unsigned short mergeHeight = (unsigned short)walkableHeight * 4; + + for (int i = 0; i < nregs; ++i) + { + rcLayerRegion& ri = regs[i]; + if (!ri.base) continue; + + unsigned char newId = ri.layerId; + + for (;;) + { + unsigned char oldId = 0xff; + + for (int j = 0; j < nregs; ++j) + { + if (i == j) continue; + rcLayerRegion& rj = regs[j]; + if (!rj.base) continue; + + // Skip if teh regions are not close to each other. + if (!overlapRange(ri.ymin,ri.ymax+mergeHeight, rj.ymin,rj.ymax+mergeHeight)) + continue; + // Skip if the height range would become too large. + const int ymin = rcMin(ri.ymin, rj.ymin); + const int ymax = rcMin(ri.ymax, rj.ymax); + if ((ymax - ymin) >= 255) + continue; + + // Make sure that there is no overlap when mergin 'ri' and 'rj'. + bool overlap = false; + // Iterate over all regions which have the same layerId as 'rj' + for (int k = 0; k < nregs; ++k) + { + if (regs[k].layerId != rj.layerId) + continue; + // Check if region 'k' is overlapping region 'ri' + // Index to 'regs' is the same as region id. + if (contains(ri.layers,ri.nlayers, (unsigned char)k)) + { + overlap = true; + break; + } + } + // Cannot merge of regions overlap. + if (overlap) + continue; + + // Can merge i and j. + oldId = rj.layerId; + break; + } + + // Could not find anything to merge with, stop. + if (oldId == 0xff) + break; + + // Merge + for (int j = 0; j < nregs; ++j) + { + rcLayerRegion& rj = regs[j]; + if (rj.layerId == oldId) + { + rj.base = 0; + // Remap layerIds. + rj.layerId = newId; + // Add overlaid layers from 'rj' to 'ri'. + for (int k = 0; k < rj.nlayers; ++k) + addUnique(ri.layers, ri.nlayers, rj.layers[k]); + // Update heigh bounds. + ri.ymin = rcMin(ri.ymin, rj.ymin); + ri.ymax = rcMax(ri.ymax, rj.ymax); + } + } + } + } + + // Compact layerIds + unsigned char remap[256]; + memset(remap, 0, 256); + + // Find number of unique layers. + layerId = 0; + for (int i = 0; i < nregs; ++i) + remap[regs[i].layerId] = 1; + for (int i = 0; i < 256; ++i) + { + if (remap[i]) + remap[i] = layerId++; + else + remap[i] = 0xff; + } + // Remap ids. + for (int i = 0; i < nregs; ++i) + regs[i].layerId = remap[regs[i].layerId]; + + // No layers, return empty. + if (layerId == 0) + { + ctx->stopTimer(RC_TIMER_BUILD_LAYERS); + return true; + } + + // Create layers. + rcAssert(lset.layers == 0); + + const int lw = w - borderSize*2; + const int lh = h - borderSize*2; + + // Build contracted bbox for layers. + float bmin[3], bmax[3]; + rcVcopy(bmin, chf.bmin); + rcVcopy(bmax, chf.bmax); + bmin[0] += borderSize*chf.cs; + bmin[2] += borderSize*chf.cs; + bmax[0] -= borderSize*chf.cs; + bmax[2] -= borderSize*chf.cs; + + lset.nlayers = (int)layerId; + + lset.layers = (rcHeightfieldLayer*)rcAlloc(sizeof(rcHeightfieldLayer)*lset.nlayers, RC_ALLOC_PERM); + if (!lset.layers) + { + ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'layers' (%d).", lset.nlayers); + return false; + } + memset(lset.layers, 0, sizeof(rcHeightfieldLayer)*lset.nlayers); + + + // Store layers. + for (int i = 0; i < lset.nlayers; ++i) + { + unsigned char curId = (unsigned char)i; + + // Allocate memory for the current layer. + rcHeightfieldLayer* layer = &lset.layers[i]; + memset(layer, 0, sizeof(rcHeightfieldLayer)); + + const int gridSize = sizeof(unsigned char)*lw*lh; + + layer->heights = (unsigned char*)rcAlloc(gridSize, RC_ALLOC_PERM); + if (!layer->heights) + { + ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'heights' (%d).", gridSize); + return false; + } + memset(layer->heights, 0xff, gridSize); + + layer->areas = (unsigned char*)rcAlloc(gridSize, RC_ALLOC_PERM); + if (!layer->areas) + { + ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'areas' (%d).", gridSize); + return false; + } + memset(layer->areas, 0, gridSize); + + layer->cons = (unsigned char*)rcAlloc(gridSize, RC_ALLOC_PERM); + if (!layer->cons) + { + ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'cons' (%d).", gridSize); + return false; + } + memset(layer->cons, 0, gridSize); + + // Find layer height bounds. + int hmin = 0, hmax = 0; + for (int j = 0; j < nregs; ++j) + { + if (regs[j].base && regs[j].layerId == curId) + { + hmin = (int)regs[j].ymin; + hmax = (int)regs[j].ymax; + } + } + + layer->width = lw; + layer->height = lh; + layer->cs = chf.cs; + layer->ch = chf.ch; + + // Adjust the bbox to fit the heighfield. + rcVcopy(layer->bmin, bmin); + rcVcopy(layer->bmax, bmax); + layer->bmin[1] = bmin[1] + hmin*chf.ch; + layer->bmax[1] = bmin[1] + hmax*chf.ch; + layer->hmin = hmin; + layer->hmax = hmax; + + // Update usable data region. + layer->minx = layer->width; + layer->maxx = 0; + layer->miny = layer->height; + layer->maxy = 0; + + // Copy height and area from compact heighfield. + for (int y = 0; y < lh; ++y) + { + for (int x = 0; x < lw; ++x) + { + const int cx = borderSize+x; + const int cy = borderSize+y; + const rcCompactCell& c = chf.cells[cx+cy*w]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + const rcCompactSpan& s = chf.spans[i]; + // Skip unassigned regions. + if (srcReg[i] == 0xff) + continue; + // Skip of does nto belong to current layer. + unsigned char lid = regs[srcReg[i]].layerId; + if (lid != curId) + continue; + + // Update data bounds. + layer->minx = rcMin(layer->minx, x); + layer->maxx = rcMax(layer->maxx, x); + layer->miny = rcMin(layer->miny, y); + layer->maxy = rcMax(layer->maxy, y); + + // Store height and area type. + const int idx = x+y*lw; + layer->heights[idx] = (unsigned char)(s.y - hmin); + layer->areas[idx] = chf.areas[i]; + + // Check connection. + unsigned char portal = 0; + unsigned char con = 0; + for (int dir = 0; dir < 4; ++dir) + { + if (rcGetCon(s, dir) != RC_NOT_CONNECTED) + { + const int ax = cx + rcGetDirOffsetX(dir); + const int ay = cy + rcGetDirOffsetY(dir); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir); + unsigned char alid = srcReg[ai] != 0xff ? regs[srcReg[ai]].layerId : 0xff; + // Portal mask + if (chf.areas[ai] != RC_NULL_AREA && lid != alid) + { + portal |= (unsigned char)(1<<dir); + // Update height so that it matches on both sides of the portal. + const rcCompactSpan& as = chf.spans[ai]; + if (as.y > hmin) + layer->heights[idx] = rcMax(layer->heights[idx], (unsigned char)(as.y - hmin)); + } + // Valid connection mask + if (chf.areas[ai] != RC_NULL_AREA && lid == alid) + { + const int nx = ax - borderSize; + const int ny = ay - borderSize; + if (nx >= 0 && ny >= 0 && nx < lw && ny < lh) + con |= (unsigned char)(1<<dir); + } + } + } + + layer->cons[idx] = (portal << 4) | con; + } + } + } + + if (layer->minx > layer->maxx) + layer->minx = layer->maxx = 0; + if (layer->miny > layer->maxy) + layer->miny = layer->maxy = 0; + } + + ctx->stopTimer(RC_TIMER_BUILD_LAYERS); + + return true; +} diff --git a/extern/recastnavigation/Recast/Source/RecastMesh.cpp b/extern/recastnavigation/Recast/Source/RecastMesh.cpp index 38d62904213..ef37d569a17 100644 --- a/extern/recastnavigation/Recast/Source/RecastMesh.cpp +++ b/extern/recastnavigation/Recast/Source/RecastMesh.cpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2009 Mikko Mononen memon@inside.org +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -21,9 +21,8 @@ #include <string.h> #include <stdio.h> #include "Recast.h" -#include "RecastLog.h" -#include "RecastTimer.h" - +#include "RecastAlloc.h" +#include "RecastAssert.h" struct rcEdge { @@ -32,36 +31,37 @@ struct rcEdge unsigned short poly[2]; }; -/*static */bool buildMeshAdjacency(unsigned short* polys, const int npolys, +/*static*/ bool buildMeshAdjacency(unsigned short* polys, const int npolys, const int nverts, const int vertsPerPoly) { // Based on code by Eric Lengyel from: // http://www.terathon.com/code/edges.php int maxEdgeCount = npolys*vertsPerPoly; - unsigned short* firstEdge = new unsigned short[nverts + maxEdgeCount]; + unsigned short* firstEdge = (unsigned short*)rcAlloc(sizeof(unsigned short)*(nverts + maxEdgeCount), RC_ALLOC_TEMP); if (!firstEdge) return false; unsigned short* nextEdge = firstEdge + nverts; int edgeCount = 0; - rcEdge* edges = new rcEdge[maxEdgeCount]; + rcEdge* edges = (rcEdge*)rcAlloc(sizeof(rcEdge)*maxEdgeCount, RC_ALLOC_TEMP); if (!edges) + { + rcFree(firstEdge); return false; + } for (int i = 0; i < nverts; i++) - firstEdge[i] = 0xffff; - - // Invalida indices are marked as 0xffff, the following code - // handles them just fine. + firstEdge[i] = RC_MESH_NULL_IDX; for (int i = 0; i < npolys; ++i) { unsigned short* t = &polys[i*vertsPerPoly*2]; for (int j = 0; j < vertsPerPoly; ++j) { + if (t[j] == RC_MESH_NULL_IDX) break; unsigned short v0 = t[j]; - unsigned short v1 = (j+1 >= vertsPerPoly || t[j+1] == 0xffff) ? t[0] : t[j+1]; + unsigned short v1 = (j+1 >= vertsPerPoly || t[j+1] == RC_MESH_NULL_IDX) ? t[0] : t[j+1]; if (v0 < v1) { rcEdge& edge = edges[edgeCount]; @@ -73,7 +73,7 @@ struct rcEdge edge.polyEdge[1] = 0; // Insert edge nextEdge[edgeCount] = firstEdge[v0]; - firstEdge[v0] = edgeCount; + firstEdge[v0] = (unsigned short)edgeCount; edgeCount++; } } @@ -84,11 +84,12 @@ struct rcEdge unsigned short* t = &polys[i*vertsPerPoly*2]; for (int j = 0; j < vertsPerPoly; ++j) { + if (t[j] == RC_MESH_NULL_IDX) break; unsigned short v0 = t[j]; - unsigned short v1 = (j+1 >= vertsPerPoly || t[j+1] == 0xffff) ? t[0] : t[j+1]; + unsigned short v1 = (j+1 >= vertsPerPoly || t[j+1] == RC_MESH_NULL_IDX) ? t[0] : t[j+1]; if (v0 > v1) { - for (unsigned short e = firstEdge[v1]; e != 0xffff; e = nextEdge[e]) + for (unsigned short e = firstEdge[v1]; e != RC_MESH_NULL_IDX; e = nextEdge[e]) { rcEdge& edge = edges[e]; if (edge.vert[1] == v0 && edge.poly[0] == edge.poly[1]) @@ -115,8 +116,8 @@ struct rcEdge } } - delete [] firstEdge; - delete [] edges; + rcFree(firstEdge); + rcFree(edges); return true; } @@ -133,8 +134,8 @@ inline int computeVertexHash(int x, int y, int z) return (int)(n & (VERTEX_BUCKET_COUNT-1)); } -static int addVertex(unsigned short x, unsigned short y, unsigned short z, - unsigned short* verts, int* firstVert, int* nextVert, int& nv) +static unsigned short addVertex(unsigned short x, unsigned short y, unsigned short z, + unsigned short* verts, int* firstVert, int* nextVert, int& nv) { int bucket = computeVertexHash(x, 0, z); int i = firstVert[bucket]; @@ -143,7 +144,7 @@ static int addVertex(unsigned short x, unsigned short y, unsigned short z, { const unsigned short* v = &verts[i*3]; if (v[0] == x && (rcAbs(v[1] - y) <= 2) && v[2] == z) - return i; + return (unsigned short)i; i = nextVert[i]; // next } @@ -156,7 +157,7 @@ static int addVertex(unsigned short x, unsigned short y, unsigned short z, nextVert[i] = firstVert[bucket]; firstVert[bucket] = i; - return i; + return (unsigned short)i; } inline int prev(int i, int n) { return i-1 >= 0 ? i-1 : n-1; } @@ -196,7 +197,7 @@ inline bool collinear(const int* a, const int* b, const int* c) // Returns true iff ab properly intersects cd: they share // a point interior to both segments. The properness of the // intersection is ensured by using strict leftness. -bool intersectProp(const int* a, const int* b, const int* c, const int* d) +static bool intersectProp(const int* a, const int* b, const int* c, const int* d) { // Eliminate improper cases. if (collinear(a,b,c) || collinear(a,b,d) || @@ -287,7 +288,7 @@ static bool diagonal(int i, int j, int n, const int* verts, int* indices) return inCone(i, j, n, verts, indices) && diagonalie(i, j, n, verts, indices); } -int triangulate(int n, const int* verts, int* indices, int* tris) +static int triangulate(int n, const int* verts, int* indices, int* tris) { int ntris = 0; int* dst = tris; @@ -328,8 +329,6 @@ int triangulate(int n, const int* verts, int* indices, int* tris) if (mini == -1) { // Should not happen. - if (rcGetLog()) - rcGetLog()->log(RC_LOG_WARNING, "triangulate: Failed to triangulate polygon."); /* printf("mini == -1 ntris=%d n=%d\n", ntris, n); for (int i = 0; i < n; i++) { @@ -379,7 +378,7 @@ int triangulate(int n, const int* verts, int* indices, int* tris) static int countPolyVerts(const unsigned short* p, const int nvp) { for (int i = 0; i < nvp; ++i) - if (p[i] == 0xffff) + if (p[i] == RC_MESH_NULL_IDX) return i; return nvp; } @@ -454,8 +453,7 @@ static int getPolyMergeValue(unsigned short* pa, unsigned short* pb, return dx*dx + dy*dy; } -static void mergePolys(unsigned short* pa, unsigned short* pb, - const unsigned short* verts, int ea, int eb, +static void mergePolys(unsigned short* pa, unsigned short* pb, int ea, int eb, unsigned short* tmp, const int nvp) { const int na = countPolyVerts(pa, nvp); @@ -474,6 +472,7 @@ static void mergePolys(unsigned short* pa, unsigned short* pb, memcpy(pa, tmp, sizeof(unsigned short)*nvp); } + static void pushFront(int v, int* arr, int& an) { an++; @@ -487,59 +486,157 @@ static void pushBack(int v, int* arr, int& an) an++; } -static bool removeVertex(rcPolyMesh& mesh, const unsigned short rem, const int maxTris) +static bool canRemoveVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short rem) { - unsigned short* tmpPoly; - int ntris; + const int nvp = mesh.nvp; + + // Count number of polygons to remove. + int numRemovedVerts = 0; + int numTouchedVerts = 0; + int numRemainingEdges = 0; + for (int i = 0; i < mesh.npolys; ++i) + { + unsigned short* p = &mesh.polys[i*nvp*2]; + const int nv = countPolyVerts(p, nvp); + int numRemoved = 0; + int numVerts = 0; + for (int j = 0; j < nv; ++j) + { + if (p[j] == rem) + { + numTouchedVerts++; + numRemoved++; + } + numVerts++; + } + if (numRemoved) + { + numRemovedVerts += numRemoved; + numRemainingEdges += numVerts-(numRemoved+1); + } + } + + // There would be too few edges remaining to create a polygon. + // This can happen for example when a tip of a triangle is marked + // as deletion, but there are no other polys that share the vertex. + // In this case, the vertex should not be removed. + if (numRemainingEdges <= 2) + return false; + + // Find edges which share the removed vertex. + const int maxEdges = numTouchedVerts*2; + int nedges = 0; + rcScopedDelete<int> edges = (int*)rcAlloc(sizeof(int)*maxEdges*3, RC_ALLOC_TEMP); + if (!edges) + { + ctx->log(RC_LOG_WARNING, "canRemoveVertex: Out of memory 'edges' (%d).", maxEdges*3); + return false; + } + + for (int i = 0; i < mesh.npolys; ++i) + { + unsigned short* p = &mesh.polys[i*nvp*2]; + const int nv = countPolyVerts(p, nvp); + + // Collect edges which touches the removed vertex. + for (int j = 0, k = nv-1; j < nv; k = j++) + { + if (p[j] == rem || p[k] == rem) + { + // Arrange edge so that a=rem. + int a = p[j], b = p[k]; + if (b == rem) + rcSwap(a,b); + + // Check if the edge exists + bool exists = false; + for (int k = 0; k < nedges; ++k) + { + int* e = &edges[k*3]; + if (e[1] == b) + { + // Exists, increment vertex share count. + e[2]++; + exists = true; + } + } + // Add new edge. + if (!exists) + { + int* e = &edges[nedges*3]; + e[0] = a; + e[1] = b; + e[2] = 1; + nedges++; + } + } + } + } - static const int nvp = mesh.nvp; + // There should be no more than 2 open edges. + // This catches the case that two non-adjacent polygons + // share the removed vertex. In that case, do not remove the vertex. + int numOpenEdges = 0; + for (int i = 0; i < nedges; ++i) + { + if (edges[i*3+2] < 2) + numOpenEdges++; + } + if (numOpenEdges > 2) + return false; + + return true; +} - int* edges = 0; - int nedges = 0; - int* hole = 0; - int nhole = 0; - int* hreg = 0; - int nhreg = 0; - int* tris = 0; - int* tverts = 0; - int* thole = 0; - unsigned short* polys = 0; - unsigned short* pregs = 0; - int npolys = 0; +static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short rem, const int maxTris) +{ + const int nvp = mesh.nvp; // Count number of polygons to remove. - int nrem = 0; + int numRemovedVerts = 0; for (int i = 0; i < mesh.npolys; ++i) { unsigned short* p = &mesh.polys[i*nvp*2]; - for (int j = 0; j < nvp; ++j) - if (p[j] == rem) { nrem++; break; } + const int nv = countPolyVerts(p, nvp); + for (int j = 0; j < nv; ++j) + { + if (p[j] == rem) + numRemovedVerts++; + } } - - edges = new int[nrem*nvp*3]; + + int nedges = 0; + rcScopedDelete<int> edges = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp*4, RC_ALLOC_TEMP); if (!edges) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_WARNING, "removeVertex: Out of memory 'edges' (%d).", nrem*nvp*3); - goto failure; + ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'edges' (%d).", numRemovedVerts*nvp*4); + return false; } - hole = new int[nrem*nvp]; + int nhole = 0; + rcScopedDelete<int> hole = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP); if (!hole) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hole' (%d).", nrem*nvp); - goto failure; + ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hole' (%d).", numRemovedVerts*nvp); + return false; } - hreg = new int[nrem*nvp]; + + int nhreg = 0; + rcScopedDelete<int> hreg = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP); if (!hreg) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hreg' (%d).", nrem*nvp); - goto failure; + ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hreg' (%d).", numRemovedVerts*nvp); + return false; + } + + int nharea = 0; + rcScopedDelete<int> harea = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP); + if (!harea) + { + ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'harea' (%d).", numRemovedVerts*nvp); + return false; } - for (int i = 0; i < mesh.npolys; ++i) { unsigned short* p = &mesh.polys[i*nvp*2]; @@ -554,17 +651,20 @@ static bool removeVertex(rcPolyMesh& mesh, const unsigned short rem, const int m { if (p[j] != rem && p[k] != rem) { - int* e = &edges[nedges*3]; + int* e = &edges[nedges*4]; e[0] = p[k]; e[1] = p[j]; e[2] = mesh.regs[i]; + e[3] = mesh.areas[i]; nedges++; } } // Remove the polygon. unsigned short* p2 = &mesh.polys[(mesh.npolys-1)*nvp*2]; memcpy(p,p2,sizeof(unsigned short)*nvp); + memset(p+nvp,0xff,sizeof(unsigned short)*nvp); mesh.regs[i] = mesh.regs[mesh.npolys-1]; + mesh.areas[i] = mesh.areas[mesh.npolys-1]; mesh.npolys--; --i; } @@ -589,16 +689,18 @@ static bool removeVertex(rcPolyMesh& mesh, const unsigned short rem, const int m } for (int i = 0; i < nedges; ++i) { - if (edges[i*3+0] > rem) edges[i*3+0]--; - if (edges[i*3+1] > rem) edges[i*3+1]--; + if (edges[i*4+0] > rem) edges[i*4+0]--; + if (edges[i*4+1] > rem) edges[i*4+1]--; } if (nedges == 0) return true; - hole[nhole] = edges[0]; - hreg[nhole] = edges[2]; - nhole++; + // Start with one vertex, keep appending connected + // segments to the start and end of the hole. + pushBack(edges[0], hole, nhole); + pushBack(edges[2], hreg, nhreg); + pushBack(edges[3], harea, nharea); while (nedges) { @@ -606,28 +708,34 @@ static bool removeVertex(rcPolyMesh& mesh, const unsigned short rem, const int m for (int i = 0; i < nedges; ++i) { - const int ea = edges[i*3+0]; - const int eb = edges[i*3+1]; - const int r = edges[i*3+2]; + const int ea = edges[i*4+0]; + const int eb = edges[i*4+1]; + const int r = edges[i*4+2]; + const int a = edges[i*4+3]; bool add = false; if (hole[0] == eb) { + // The segment matches the beginning of the hole boundary. pushFront(ea, hole, nhole); pushFront(r, hreg, nhreg); + pushFront(a, harea, nharea); add = true; } else if (hole[nhole-1] == ea) { + // The segment matches the end of the hole boundary. pushBack(eb, hole, nhole); pushBack(r, hreg, nhreg); + pushBack(a, harea, nharea); add = true; } if (add) { - // Remove edge. - edges[i*3+0] = edges[(nedges-1)*3+0]; - edges[i*3+1] = edges[(nedges-1)*3+1]; - edges[i*3+2] = edges[(nedges-1)*3+2]; + // The edge segment was added, remove it. + edges[i*4+0] = edges[(nedges-1)*4+0]; + edges[i*4+1] = edges[(nedges-1)*4+1]; + edges[i*4+2] = edges[(nedges-1)*4+2]; + edges[i*4+3] = edges[(nedges-1)*4+3]; --nedges; match = true; --i; @@ -638,28 +746,25 @@ static bool removeVertex(rcPolyMesh& mesh, const unsigned short rem, const int m break; } - tris = new int[nhole*3]; + rcScopedDelete<int> tris = (int*)rcAlloc(sizeof(int)*nhole*3, RC_ALLOC_TEMP); if (!tris) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tris' (%d).", nhole*3); - goto failure; + ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tris' (%d).", nhole*3); + return false; } - tverts = new int[nhole*4]; + rcScopedDelete<int> tverts = (int*)rcAlloc(sizeof(int)*nhole*4, RC_ALLOC_TEMP); if (!tverts) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tverts' (%d).", nhole*4); - goto failure; + ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tverts' (%d).", nhole*4); + return false; } - thole = new int[nhole]; + rcScopedDelete<int> thole = (int*)rcAlloc(sizeof(int)*nhole, RC_ALLOC_TEMP); if (!tverts) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_WARNING, "removeVertex: Out of memory 'thole' (%d).", nhole); - goto failure; + ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'thole' (%d).", nhole); + return false; } // Generate temp vertex array for triangulation. @@ -674,27 +779,37 @@ static bool removeVertex(rcPolyMesh& mesh, const unsigned short rem, const int m } // Triangulate the hole. - ntris = triangulate(nhole, &tverts[0], &thole[0], tris); - + int ntris = triangulate(nhole, &tverts[0], &thole[0], tris); + if (ntris < 0) + { + ntris = -ntris; + ctx->log(RC_LOG_WARNING, "removeVertex: triangulate() returned bad results."); + } + // Merge the hole triangles back to polygons. - polys = new unsigned short[(ntris+1)*nvp]; + rcScopedDelete<unsigned short> polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*(ntris+1)*nvp, RC_ALLOC_TEMP); if (!polys) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_WARNING, "removeVertex: Out of memory 'polys' (%d).", (ntris+1)*nvp); - goto failure; + ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'polys' (%d).", (ntris+1)*nvp); + return false; } - pregs = new unsigned short[ntris]; + rcScopedDelete<unsigned short> pregs = (unsigned short*)rcAlloc(sizeof(unsigned short)*ntris, RC_ALLOC_TEMP); if (!pregs) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_WARNING, "removeVertex: Out of memory 'pregs' (%d).", ntris); - goto failure; + ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'pregs' (%d).", ntris); + return false; + } + rcScopedDelete<unsigned char> pareas = (unsigned char*)rcAlloc(sizeof(unsigned char)*ntris, RC_ALLOC_TEMP); + if (!pregs) + { + ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'pareas' (%d).", ntris); + return false; } - tmpPoly = &polys[ntris*nvp]; + unsigned short* tmpPoly = &polys[ntris*nvp]; // Build initial polygons. + int npolys = 0; memset(polys, 0xff, ntris*nvp*sizeof(unsigned short)); for (int j = 0; j < ntris; ++j) { @@ -704,7 +819,8 @@ static bool removeVertex(rcPolyMesh& mesh, const unsigned short rem, const int m polys[npolys*nvp+0] = (unsigned short)hole[t[0]]; polys[npolys*nvp+1] = (unsigned short)hole[t[1]]; polys[npolys*nvp+2] = (unsigned short)hole[t[2]]; - pregs[npolys] = hreg[t[0]]; + pregs[npolys] = (unsigned short)hreg[t[0]]; + pareas[npolys] = (unsigned char)harea[t[0]]; npolys++; } } @@ -714,11 +830,11 @@ static bool removeVertex(rcPolyMesh& mesh, const unsigned short rem, const int m // Merge polygons. if (nvp > 3) { - while (true) + for (;;) { // Find best polygons to merge. int bestMergeVal = 0; - int bestPa, bestPb, bestEa, bestEb; + int bestPa = 0, bestPb = 0, bestEa = 0, bestEb = 0; for (int j = 0; j < npolys-1; ++j) { @@ -744,9 +860,10 @@ static bool removeVertex(rcPolyMesh& mesh, const unsigned short rem, const int m // Found best, merge. unsigned short* pa = &polys[bestPa*nvp]; unsigned short* pb = &polys[bestPb*nvp]; - mergePolys(pa, pb, mesh.verts, bestEa, bestEb, tmpPoly, nvp); + mergePolys(pa, pb, bestEa, bestEb, tmpPoly, nvp); memcpy(pb, &polys[(npolys-1)*nvp], sizeof(unsigned short)*nvp); pregs[bestPb] = pregs[npolys-1]; + pareas[bestPb] = pareas[npolys-1]; npolys--; } else @@ -766,50 +883,43 @@ static bool removeVertex(rcPolyMesh& mesh, const unsigned short rem, const int m for (int j = 0; j < nvp; ++j) p[j] = polys[i*nvp+j]; mesh.regs[mesh.npolys] = pregs[i]; + mesh.areas[mesh.npolys] = pareas[i]; mesh.npolys++; + if (mesh.npolys > maxTris) + { + ctx->log(RC_LOG_ERROR, "removeVertex: Too many polygons %d (max:%d).", mesh.npolys, maxTris); + return false; + } } - delete [] edges; - delete [] hole; - delete [] hreg; - delete [] tris; - delete [] thole; - delete [] tverts; - delete [] polys; - delete [] pregs; - return true; - -failure: - delete [] edges; - delete [] hole; - delete [] hreg; - delete [] tris; - delete [] thole; - delete [] tverts; - delete [] polys; - delete [] pregs; - - return false; } - -bool rcBuildPolyMesh(rcContourSet& cset, int nvp, rcPolyMesh& mesh) +/// @par +/// +/// @note If the mesh data is to be used to construct a Detour navigation mesh, then the upper +/// limit must be retricted to <= #DT_VERTS_PER_POLYGON. +/// +/// @see rcAllocPolyMesh, rcContourSet, rcPolyMesh, rcConfig +bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMesh& mesh) { - unsigned short* tmpPoly; - rcTimeVal startTime = rcGetPerformanceTimer(); - rcTimeVal endTime; + rcAssert(ctx); + + ctx->startTimer(RC_TIMER_BUILD_POLYMESH); - vcopy(mesh.bmin, cset.bmin); - vcopy(mesh.bmax, cset.bmax); + rcVcopy(mesh.bmin, cset.bmin); + rcVcopy(mesh.bmax, cset.bmax); mesh.cs = cset.cs; mesh.ch = cset.ch; + mesh.borderSize = cset.borderSize; int maxVertices = 0; int maxTris = 0; int maxVertsPerCont = 0; for (int i = 0; i < cset.nconts; ++i) { + // Skip null contours. + if (cset.conts[i].nverts < 3) continue; maxVertices += cset.conts[i].nverts; maxTris += cset.conts[i].nverts - 2; maxVertsPerCont = rcMax(maxVertsPerCont, cset.conts[i].nverts); @@ -817,103 +927,95 @@ bool rcBuildPolyMesh(rcContourSet& cset, int nvp, rcPolyMesh& mesh) if (maxVertices >= 0xfffe) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Too many vertices %d.", maxVertices); + ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Too many vertices %d.", maxVertices); return false; } - - unsigned char* vflags = 0; - int* nextVert = 0; - int* firstVert = 0; - int* indices = 0; - int* tris = 0; - unsigned short* polys = 0; - - vflags = new unsigned char[maxVertices]; + + rcScopedDelete<unsigned char> vflags = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxVertices, RC_ALLOC_TEMP); if (!vflags) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.verts' (%d).", maxVertices); - goto failure; + ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.verts' (%d).", maxVertices); + return false; } memset(vflags, 0, maxVertices); - mesh.verts = new unsigned short[maxVertices*3]; + mesh.verts = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxVertices*3, RC_ALLOC_PERM); if (!mesh.verts) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.verts' (%d).", maxVertices); - goto failure; + ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.verts' (%d).", maxVertices); + return false; } - mesh.polys = new unsigned short[maxTris*nvp*2]; + mesh.polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxTris*nvp*2, RC_ALLOC_PERM); if (!mesh.polys) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.polys' (%d).", maxTris*nvp*2); - goto failure; + ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.polys' (%d).", maxTris*nvp*2); + return false; } - mesh.regs = new unsigned short[maxTris]; + mesh.regs = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxTris, RC_ALLOC_PERM); if (!mesh.regs) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.regs' (%d).", maxTris); - goto failure; + ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.regs' (%d).", maxTris); + return false; + } + mesh.areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxTris, RC_ALLOC_PERM); + if (!mesh.areas) + { + ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.areas' (%d).", maxTris); + return false; } + mesh.nverts = 0; mesh.npolys = 0; mesh.nvp = nvp; + mesh.maxpolys = maxTris; memset(mesh.verts, 0, sizeof(unsigned short)*maxVertices*3); memset(mesh.polys, 0xff, sizeof(unsigned short)*maxTris*nvp*2); memset(mesh.regs, 0, sizeof(unsigned short)*maxTris); + memset(mesh.areas, 0, sizeof(unsigned char)*maxTris); - nextVert = new int[maxVertices]; + rcScopedDelete<int> nextVert = (int*)rcAlloc(sizeof(int)*maxVertices, RC_ALLOC_TEMP); if (!nextVert) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'nextVert' (%d).", maxVertices); - goto failure; + ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'nextVert' (%d).", maxVertices); + return false; } memset(nextVert, 0, sizeof(int)*maxVertices); - firstVert = new int[VERTEX_BUCKET_COUNT]; + rcScopedDelete<int> firstVert = (int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP); if (!firstVert) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'firstVert' (%d).", VERTEX_BUCKET_COUNT); - goto failure; + ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'firstVert' (%d).", VERTEX_BUCKET_COUNT); + return false; } for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i) firstVert[i] = -1; - indices = new int[maxVertsPerCont]; + rcScopedDelete<int> indices = (int*)rcAlloc(sizeof(int)*maxVertsPerCont, RC_ALLOC_TEMP); if (!indices) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'indices' (%d).", maxVertsPerCont); - goto failure; + ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'indices' (%d).", maxVertsPerCont); + return false; } - tris = new int[maxVertsPerCont*3]; + rcScopedDelete<int> tris = (int*)rcAlloc(sizeof(int)*maxVertsPerCont*3, RC_ALLOC_TEMP); if (!tris) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'tris' (%d).", maxVertsPerCont*3); - goto failure; + ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'tris' (%d).", maxVertsPerCont*3); + return false; } - polys = new unsigned short[(maxVertsPerCont+1)*nvp]; + rcScopedDelete<unsigned short> polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*(maxVertsPerCont+1)*nvp, RC_ALLOC_TEMP); if (!polys) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'polys' (%d).", maxVertsPerCont*nvp); - goto failure; + ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'polys' (%d).", maxVertsPerCont*nvp); + return false; } - tmpPoly = &polys[maxVertsPerCont*nvp]; + unsigned short* tmpPoly = &polys[maxVertsPerCont*nvp]; for (int i = 0; i < cset.nconts; ++i) { rcContour& cont = cset.conts[i]; - // Skip empty contours. + // Skip null contours. if (cont.nverts < 3) continue; @@ -925,20 +1027,20 @@ bool rcBuildPolyMesh(rcContourSet& cset, int nvp, rcPolyMesh& mesh) if (ntris <= 0) { // Bad triangulation, should not happen. -/* for (int k = 0; k < cont.nverts; ++k) +/* printf("\tconst float bmin[3] = {%ff,%ff,%ff};\n", cset.bmin[0], cset.bmin[1], cset.bmin[2]); + printf("\tconst float cs = %ff;\n", cset.cs); + printf("\tconst float ch = %ff;\n", cset.ch); + printf("\tconst int verts[] = {\n"); + for (int k = 0; k < cont.nverts; ++k) { const int* v = &cont.verts[k*4]; printf("\t\t%d,%d,%d,%d,\n", v[0], v[1], v[2], v[3]); - if (nBadPos < 100) - { - badPos[nBadPos*3+0] = v[0]; - badPos[nBadPos*3+1] = v[1]; - badPos[nBadPos*3+2] = v[2]; - nBadPos++; - } - }*/ + } + printf("\t};\n\tconst int nverts = sizeof(verts)/(sizeof(int)*4);\n");*/ + ctx->log(RC_LOG_WARNING, "rcBuildPolyMesh: Bad triangulation Contour %d.", i); ntris = -ntris; } + // Add and merge vertices. for (int j = 0; j < cont.nverts; ++j) { @@ -972,11 +1074,11 @@ bool rcBuildPolyMesh(rcContourSet& cset, int nvp, rcPolyMesh& mesh) // Merge polygons. if (nvp > 3) { - while (true) + for(;;) { // Find best polygons to merge. int bestMergeVal = 0; - int bestPa, bestPb, bestEa, bestEb; + int bestPa = 0, bestPb = 0, bestEa = 0, bestEb = 0; for (int j = 0; j < npolys-1; ++j) { @@ -1002,7 +1104,7 @@ bool rcBuildPolyMesh(rcContourSet& cset, int nvp, rcPolyMesh& mesh) // Found best, merge. unsigned short* pa = &polys[bestPa*nvp]; unsigned short* pb = &polys[bestPb*nvp]; - mergePolys(pa, pb, mesh.verts, bestEa, bestEb, tmpPoly, nvp); + mergePolys(pa, pb, bestEa, bestEb, tmpPoly, nvp); memcpy(pb, &polys[(npolys-1)*nvp], sizeof(unsigned short)*nvp); npolys--; } @@ -1014,7 +1116,6 @@ bool rcBuildPolyMesh(rcContourSet& cset, int nvp, rcPolyMesh& mesh) } } - // Store polygons. for (int j = 0; j < npolys; ++j) { @@ -1023,7 +1124,13 @@ bool rcBuildPolyMesh(rcContourSet& cset, int nvp, rcPolyMesh& mesh) for (int k = 0; k < nvp; ++k) p[k] = q[k]; mesh.regs[mesh.npolys] = cont.reg; + mesh.areas[mesh.npolys] = cont.area; mesh.npolys++; + if (mesh.npolys > maxTris) + { + ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Too many polygons %d (max:%d).", mesh.npolys, maxTris); + return false; + } } } @@ -1033,131 +1140,174 @@ bool rcBuildPolyMesh(rcContourSet& cset, int nvp, rcPolyMesh& mesh) { if (vflags[i]) { - if (!removeVertex(mesh, i, maxTris)) - goto failure; - for (int j = i; j < mesh.nverts-1; ++j) + if (!canRemoveVertex(ctx, mesh, (unsigned short)i)) + continue; + if (!removeVertex(ctx, mesh, (unsigned short)i, maxTris)) + { + // Failed to remove vertex + ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Failed to remove edge vertex %d.", i); + return false; + } + // Remove vertex + // Note: mesh.nverts is already decremented inside removeVertex()! + for (int j = i; j < mesh.nverts; ++j) vflags[j] = vflags[j+1]; --i; } } - - delete [] vflags; - delete [] firstVert; - delete [] nextVert; - delete [] indices; - delete [] tris; // Calculate adjacency. if (!buildMeshAdjacency(mesh.polys, mesh.npolys, mesh.nverts, nvp)) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Adjacency failed."); + ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Adjacency failed."); return false; } - endTime = rcGetPerformanceTimer(); + // Find portal edges + if (mesh.borderSize > 0) + { + const int w = cset.width; + const int h = cset.height; + for (int i = 0; i < mesh.npolys; ++i) + { + unsigned short* p = &mesh.polys[i*2*nvp]; + for (int j = 0; j < nvp; ++j) + { + if (p[j] == RC_MESH_NULL_IDX) break; + // Skip connected edges. + if (p[nvp+j] != RC_MESH_NULL_IDX) + continue; + int nj = j+1; + if (nj >= nvp || p[nj] == RC_MESH_NULL_IDX) nj = 0; + const unsigned short* va = &mesh.verts[p[j]*3]; + const unsigned short* vb = &mesh.verts[p[nj]*3]; + + if ((int)va[0] == 0 && (int)vb[0] == 0) + p[nvp+j] = 0x8000 | 0; + else if ((int)va[2] == h && (int)vb[2] == h) + p[nvp+j] = 0x8000 | 1; + else if ((int)va[0] == w && (int)vb[0] == w) + p[nvp+j] = 0x8000 | 2; + else if ((int)va[2] == 0 && (int)vb[2] == 0) + p[nvp+j] = 0x8000 | 3; + } + } + } + + // Just allocate the mesh flags array. The user is resposible to fill it. + mesh.flags = (unsigned short*)rcAlloc(sizeof(unsigned short)*mesh.npolys, RC_ALLOC_PERM); + if (!mesh.flags) + { + ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.flags' (%d).", mesh.npolys); + return false; + } + memset(mesh.flags, 0, sizeof(unsigned short) * mesh.npolys); + + if (mesh.nverts > 0xffff) + { + ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: The resulting mesh has too many vertices %d (max %d). Data can be corrupted.", mesh.nverts, 0xffff); + } + if (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); + } -// if (rcGetLog()) -// rcGetLog()->log(RC_LOG_PROGRESS, "Build polymesh: %.3f ms", rcGetDeltaTimeUsec(startTime, endTime)/1000.0f); - if (rcGetBuildTimes()) - rcGetBuildTimes()->buildPolymesh += rcGetDeltaTimeUsec(startTime, endTime); + ctx->stopTimer(RC_TIMER_BUILD_POLYMESH); return true; - -failure: - delete [] vflags; - delete [] tmpPoly; - delete [] firstVert; - delete [] nextVert; - delete [] indices; - delete [] tris; - - return false; } -bool rcMergePolyMeshes(rcPolyMesh** meshes, const int nmeshes, rcPolyMesh& mesh) +/// @see rcAllocPolyMesh, rcPolyMesh +bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, rcPolyMesh& mesh) { + rcAssert(ctx); + if (!nmeshes || !meshes) return true; - rcTimeVal startTime = rcGetPerformanceTimer(); - rcTimeVal endTime; - - int* nextVert = 0; - int* firstVert = 0; - unsigned short* vremap = 0; + ctx->startTimer(RC_TIMER_MERGE_POLYMESH); mesh.nvp = meshes[0]->nvp; mesh.cs = meshes[0]->cs; mesh.ch = meshes[0]->ch; - vcopy(mesh.bmin, meshes[0]->bmin); - vcopy(mesh.bmax, meshes[0]->bmax); + rcVcopy(mesh.bmin, meshes[0]->bmin); + rcVcopy(mesh.bmax, meshes[0]->bmax); int maxVerts = 0; int maxPolys = 0; int maxVertsPerMesh = 0; for (int i = 0; i < nmeshes; ++i) { - vmin(mesh.bmin, meshes[i]->bmin); - vmax(mesh.bmax, meshes[i]->bmax); + rcVmin(mesh.bmin, meshes[i]->bmin); + rcVmax(mesh.bmax, meshes[i]->bmax); maxVertsPerMesh = rcMax(maxVertsPerMesh, meshes[i]->nverts); maxVerts += meshes[i]->nverts; maxPolys += meshes[i]->npolys; } mesh.nverts = 0; - mesh.verts = new unsigned short[maxVerts*3]; + mesh.verts = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxVerts*3, RC_ALLOC_PERM); if (!mesh.verts) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.verts' (%d).", maxVerts*3); + ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.verts' (%d).", maxVerts*3); return false; } mesh.npolys = 0; - mesh.polys = new unsigned short[maxPolys*2*mesh.nvp]; + mesh.polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxPolys*2*mesh.nvp, RC_ALLOC_PERM); if (!mesh.polys) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.polys' (%d).", maxPolys*2*mesh.nvp); + ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.polys' (%d).", maxPolys*2*mesh.nvp); return false; } memset(mesh.polys, 0xff, sizeof(unsigned short)*maxPolys*2*mesh.nvp); - mesh.regs = new unsigned short[maxPolys]; + mesh.regs = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxPolys, RC_ALLOC_PERM); if (!mesh.regs) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.regs' (%d).", maxPolys); + ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.regs' (%d).", maxPolys); return false; } memset(mesh.regs, 0, sizeof(unsigned short)*maxPolys); + + mesh.areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxPolys, RC_ALLOC_PERM); + if (!mesh.areas) + { + ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.areas' (%d).", maxPolys); + return false; + } + memset(mesh.areas, 0, sizeof(unsigned char)*maxPolys); + + mesh.flags = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxPolys, RC_ALLOC_PERM); + if (!mesh.flags) + { + ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.flags' (%d).", maxPolys); + return false; + } + memset(mesh.flags, 0, sizeof(unsigned short)*maxPolys); - nextVert = new int[maxVerts]; + rcScopedDelete<int> nextVert = (int*)rcAlloc(sizeof(int)*maxVerts, RC_ALLOC_TEMP); if (!nextVert) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'nextVert' (%d).", maxVerts); - goto failure; + ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'nextVert' (%d).", maxVerts); + return false; } memset(nextVert, 0, sizeof(int)*maxVerts); - firstVert = new int[VERTEX_BUCKET_COUNT]; + rcScopedDelete<int> firstVert = (int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP); if (!firstVert) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'firstVert' (%d).", VERTEX_BUCKET_COUNT); - goto failure; + ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'firstVert' (%d).", VERTEX_BUCKET_COUNT); + return false; } for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i) firstVert[i] = -1; - vremap = new unsigned short[maxVertsPerMesh]; + rcScopedDelete<unsigned short> vremap = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxVertsPerMesh, RC_ALLOC_PERM); if (!vremap) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'vremap' (%d).", maxVertsPerMesh); - goto failure; + ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'vremap' (%d).", maxVertsPerMesh); + return false; } memset(nextVert, 0, sizeof(int)*maxVerts); @@ -1172,7 +1322,7 @@ bool rcMergePolyMeshes(rcPolyMesh** meshes, const int nmeshes, rcPolyMesh& mesh) { unsigned short* v = &pmesh->verts[j*3]; vremap[j] = addVertex(v[0]+ox, v[1], v[2]+oz, - mesh.verts, firstVert, nextVert, mesh.nverts); + mesh.verts, firstVert, nextVert, mesh.nverts); } for (int j = 0; j < pmesh->npolys; ++j) @@ -1180,10 +1330,12 @@ bool rcMergePolyMeshes(rcPolyMesh** meshes, const int nmeshes, rcPolyMesh& mesh) unsigned short* tgt = &mesh.polys[mesh.npolys*2*mesh.nvp]; unsigned short* src = &pmesh->polys[j*2*mesh.nvp]; mesh.regs[mesh.npolys] = pmesh->regs[j]; + mesh.areas[mesh.npolys] = pmesh->areas[j]; + mesh.flags[mesh.npolys] = pmesh->flags[j]; mesh.npolys++; for (int k = 0; k < mesh.nvp; ++k) { - if (src[k] == 0xffff) break; + if (src[k] == RC_MESH_NULL_IDX) break; tgt[k] = vremap[src[k]]; } } @@ -1192,27 +1344,20 @@ bool rcMergePolyMeshes(rcPolyMesh** meshes, const int nmeshes, rcPolyMesh& mesh) // Calculate adjacency. if (!buildMeshAdjacency(mesh.polys, mesh.npolys, mesh.nverts, mesh.nvp)) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcMergePolyMeshes: Adjacency failed."); + ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Adjacency failed."); return false; } - - delete [] firstVert; - delete [] nextVert; - delete [] vremap; - - endTime = rcGetPerformanceTimer(); + if (mesh.nverts > 0xffff) + { + ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: The resulting mesh has too many vertices %d (max %d). Data can be corrupted.", mesh.nverts, 0xffff); + } + if (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); + } - if (rcGetBuildTimes()) - rcGetBuildTimes()->mergePolyMesh += rcGetDeltaTimeUsec(startTime, endTime); + ctx->stopTimer(RC_TIMER_MERGE_POLYMESH); return true; - -failure: - delete [] firstVert; - delete [] nextVert; - delete [] vremap; - - return false; } diff --git a/extern/recastnavigation/Recast/Source/RecastMeshDetail.cpp b/extern/recastnavigation/Recast/Source/RecastMeshDetail.cpp index 55ba28ae7cf..126529e9779 100644 --- a/extern/recastnavigation/Recast/Source/RecastMeshDetail.cpp +++ b/extern/recastnavigation/Recast/Source/RecastMeshDetail.cpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2009 Mikko Mononen memon@inside.org +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -23,248 +23,75 @@ #include <stdlib.h> #include <stdio.h> #include "Recast.h" -#include "RecastLog.h" -#include "RecastTimer.h" +#include "RecastAlloc.h" +#include "RecastAssert.h" +static const unsigned RC_UNSET_HEIGHT = 0xffff; + struct rcHeightPatch { - inline rcHeightPatch() : data(0) {} - inline ~rcHeightPatch() { delete [] data; } + inline rcHeightPatch() : data(0), xmin(0), ymin(0), width(0), height(0) {} + inline ~rcHeightPatch() { rcFree(data); } unsigned short* data; int xmin, ymin, width, height; }; - -static int circumCircle(const float xp, const float yp, - const float x1, const float y1, - const float x2, const float y2, - const float x3, const float y3, - float& xc, float& yc, float& rsqr) +inline float vdot2(const float* a, const float* b) { - static const float EPSILON = 1e-6f; - - const float fabsy1y2 = rcAbs(y1-y2); - const float fabsy2y3 = rcAbs(y2-y3); - - /* Check for coincident points */ - if (fabsy1y2 < EPSILON && fabsy2y3 < EPSILON) - return 0; - - if (fabsy1y2 < EPSILON) - { - const float m2 = - (x3-x2) / (y3-y2); - const float mx2 = (x2 + x3) / 2.0f; - const float my2 = (y2 + y3) / 2.0f; - xc = (x2 + x1) / 2.0f; - yc = m2 * (xc - mx2) + my2; - } - else if (fabsy2y3 < EPSILON) - { - const float m1 = - (x2-x1) / (y2-y1); - const float mx1 = (x1 + x2) / 2.0f; - const float my1 = (y1 + y2) / 2.0f; - xc = (x3 + x2) / 2.0f; - yc = m1 * (xc - mx1) + my1; - } - else - { - const float m1 = - (x2-x1) / (y2-y1); - const float m2 = - (x3-x2) / (y3-y2); - const float mx1 = (x1 + x2) / 2.0f; - const float mx2 = (x2 + x3) / 2.0f; - const float my1 = (y1 + y2) / 2.0f; - const float my2 = (y2 + y3) / 2.0f; - xc = (m1 * mx1 - m2 * mx2 + my2 - my1) / (m1 - m2); - if (fabsy1y2 > fabsy2y3) - yc = m1 * (xc - mx1) + my1; - else - yc = m2 * (xc - mx2) + my2; - } - - float dx,dy; - - dx = x2 - xc; - dy = y2 - yc; - rsqr = dx*dx + dy*dy; - - dx = xp - xc; - dy = yp - yc; - const float drsqr = dx*dx + dy*dy; - - return (drsqr <= rsqr) ? 1 : 0; + return a[0]*b[0] + a[2]*b[2]; } - -static float *_qsort_verts; -static int ptcmp(const void *v1, const void *v2) + +inline float vdistSq2(const float* p, const float* q) { - const float* p1 = &_qsort_verts[(*(const int*)v1)*3]; - const float* p2 = &_qsort_verts[(*(const int*)v2)*3]; - if (p1[0] < p2[0]) - return -1; - else if (p1[0] > p2[0]) - return 1; - else - return 0; + const float dx = q[0] - p[0]; + const float dy = q[2] - p[2]; + return dx*dx + dy*dy; } -// Based on Paul Bourke's triangulate.c -// http://astronomy.swin.edu.au/~pbourke/terrain/triangulate/triangulate.c -static void delaunay(const int nv, float *verts, rcIntArray& idx, rcIntArray& tris, rcIntArray& edges) +inline float vdist2(const float* p, const float* q) { - // Sort vertices - idx.resize(nv); - for (int i = 0; i < nv; ++i) - idx[i] = i; - _qsort_verts = verts; - qsort(&idx[0], idx.size(), sizeof(int), ptcmp); - - // Find the maximum and minimum vertex bounds. - // This is to allow calculation of the bounding triangle - float xmin = verts[0]; - float ymin = verts[2]; - float xmax = xmin; - float ymax = ymin; - for (int i = 1; i < nv; ++i) - { - xmin = rcMin(xmin, verts[i*3+0]); - xmax = rcMax(xmax, verts[i*3+0]); - ymin = rcMin(ymin, verts[i*3+2]); - ymax = rcMax(ymax, verts[i*3+2]); - } - float dx = xmax - xmin; - float dy = ymax - ymin; - float dmax = (dx > dy) ? dx : dy; - float xmid = (xmax + xmin) / 2.0f; - float ymid = (ymax + ymin) / 2.0f; - - // Set up the supertriangle - // This is a triangle which encompasses all the sample points. - // The supertriangle coordinates are added to the end of the - // vertex list. The supertriangle is the first triangle in - // the triangle list. - float sv[3*3]; - - sv[0] = xmid - 20 * dmax; - sv[1] = 0; - sv[2] = ymid - dmax; - - sv[3] = xmid; - sv[4] = 0; - sv[5] = ymid + 20 * dmax; - - sv[6] = xmid + 20 * dmax; - sv[7] = 0; - sv[8] = ymid - dmax; - - tris.push(-3); - tris.push(-2); - tris.push(-1); - tris.push(0); // not completed - - for (int i = 0; i < nv; ++i) - { - const float xp = verts[idx[i]*3+0]; - const float yp = verts[idx[i]*3+2]; - - edges.resize(0); - - // Set up the edge buffer. - // If the point (xp,yp) lies inside the circumcircle then the - // three edges of that triangle are added to the edge buffer - // and that triangle is removed. - for (int j = 0; j < tris.size()/4; ++j) - { - int* t = &tris[j*4]; - if (t[3]) // completed? - continue; - const float* v1 = t[0] < 0 ? &sv[(t[0]+3)*3] : &verts[idx[t[0]]*3]; - const float* v2 = t[1] < 0 ? &sv[(t[1]+3)*3] : &verts[idx[t[1]]*3]; - const float* v3 = t[2] < 0 ? &sv[(t[2]+3)*3] : &verts[idx[t[2]]*3]; - float xc,yc,rsqr; - int inside = circumCircle(xp,yp, v1[0],v1[2], v2[0],v2[2], v3[0],v3[2], xc,yc,rsqr); - if (xc < xp && rcSqr(xp-xc) > rsqr) - t[3] = 1; - if (inside) - { - // Collect triangle edges. - edges.push(t[0]); - edges.push(t[1]); - edges.push(t[1]); - edges.push(t[2]); - edges.push(t[2]); - edges.push(t[0]); - // Remove triangle j. - t[0] = tris[tris.size()-4]; - t[1] = tris[tris.size()-3]; - t[2] = tris[tris.size()-2]; - t[3] = tris[tris.size()-1]; - tris.resize(tris.size()-4); - j--; - } - } - - // Remove duplicate edges. - const int ne = edges.size()/2; - for (int j = 0; j < ne-1; ++j) - { - for (int k = j+1; k < ne; ++k) - { - // Dupe?, make null. - if ((edges[j*2+0] == edges[k*2+1]) && (edges[j*2+1] == edges[k*2+0])) - { - edges[j*2+0] = 0; - edges[j*2+1] = 0; - edges[k*2+0] = 0; - edges[k*2+1] = 0; - } - } - } - - // Form new triangles for the current point - // Skipping over any null. - // All edges are arranged in clockwise order. - for (int j = 0; j < ne; ++j) - { - if (edges[j*2+0] == edges[j*2+1]) continue; - tris.push(edges[j*2+0]); - tris.push(edges[j*2+1]); - tris.push(i); - tris.push(0); // not completed - } - } - - // Remove triangles with supertriangle vertices - // These are triangles which have a vertex number greater than nv - for (int i = 0; i < tris.size()/4; ++i) - { - int* t = &tris[i*4]; - if (t[0] < 0 || t[1] < 0 || t[2] < 0) - { - t[0] = tris[tris.size()-4]; - t[1] = tris[tris.size()-3]; - t[2] = tris[tris.size()-2]; - t[3] = tris[tris.size()-1]; - tris.resize(tris.size()-4); - i--; - } - } - // Triangle vertices are pointing to sorted vertices, remap indices. - for (int i = 0; i < tris.size(); ++i) - tris[i] = idx[tris[i]]; + return sqrtf(vdistSq2(p,q)); } -inline float vdot2(const float* a, const float* b) +inline float vcross2(const float* p1, const float* p2, const float* p3) +{ + const float u1 = p2[0] - p1[0]; + const float v1 = p2[2] - p1[2]; + const float u2 = p3[0] - p1[0]; + const float v2 = p3[2] - p1[2]; + return u1 * v2 - v1 * u2; +} + +static bool circumCircle(const float* p1, const float* p2, const float* p3, + float* c, float& r) { - return a[0]*b[0] + a[2]*b[2]; + static const float EPS = 1e-6f; + + const float cp = vcross2(p1, p2, p3); + if (fabsf(cp) > EPS) + { + const float p1Sq = vdot2(p1,p1); + const float p2Sq = vdot2(p2,p2); + const float p3Sq = vdot2(p3,p3); + c[0] = (p1Sq*(p2[2]-p3[2]) + p2Sq*(p3[2]-p1[2]) + p3Sq*(p1[2]-p2[2])) / (2*cp); + c[2] = (p1Sq*(p3[0]-p2[0]) + p2Sq*(p1[0]-p3[0]) + p3Sq*(p2[0]-p1[0])) / (2*cp); + r = vdist2(c, p1); + return true; + } + + c[0] = p1[0]; + c[2] = p1[2]; + r = 0; + return false; } static float distPtTri(const float* p, const float* a, const float* b, const float* c) { float v0[3], v1[3], v2[3]; - vsub(v0, c,a); - vsub(v1, b,a); - vsub(v2, p,a); + rcVsub(v0, c,a); + rcVsub(v1, b,a); + rcVsub(v2, p,a); const float dot00 = vdot2(v0, v0); const float dot01 = vdot2(v0, v1); @@ -273,15 +100,15 @@ static float distPtTri(const float* p, const float* a, const float* b, const flo const float dot12 = vdot2(v1, v2); // Compute barycentric coordinates - float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01); - float u = (dot11 * dot02 - dot01 * dot12) * invDenom; + const float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01); + const float u = (dot11 * dot02 - dot01 * dot12) * invDenom; float v = (dot00 * dot12 - dot01 * dot02) * invDenom; // If point lies inside the triangle, return interpolated y-coord. static const float EPS = 1e-4f; if (u >= -EPS && v >= -EPS && (u+v) <= 1+EPS) { - float y = a[1] + v0[1]*u + v1[1]*v; + const float y = a[1] + v0[1]*u + v1[1]*v; return fabsf(y-p[1]); } return FLT_MAX; @@ -332,7 +159,7 @@ static float distancePtSeg2d(const float* pt, const float* p, const float* q) return dx*dx + dz*dz; } -static float distToTriMesh(const float* p, const float* verts, int nverts, const int* tris, int ntris) +static float distToTriMesh(const float* p, const float* verts, const int /*nverts*/, const int* tris, const int ntris) { float dmin = FLT_MAX; for (int i = 0; i < ntris; ++i) @@ -366,35 +193,335 @@ static float distToPoly(int nvert, const float* verts, const float* p) } -static unsigned short getHeight(const float* pos, const float* bmin, const float ics, const rcHeightPatch& hp) +static unsigned short getHeight(const float fx, const float fy, const float fz, + const float /*cs*/, const float ics, const float ch, + const rcHeightPatch& hp) { - int ix = (int)floorf((pos[0]-bmin[0])*ics + 0.01f); - int iz = (int)floorf((pos[2]-bmin[2])*ics + 0.01f); + int ix = (int)floorf(fx*ics + 0.01f); + int iz = (int)floorf(fz*ics + 0.01f); ix = rcClamp(ix-hp.xmin, 0, hp.width); iz = rcClamp(iz-hp.ymin, 0, hp.height); unsigned short h = hp.data[ix+iz*hp.width]; + if (h == RC_UNSET_HEIGHT) + { + // Special case when data might be bad. + // Find nearest neighbour pixel which has valid height. + const int off[8*2] = { -1,0, -1,-1, 0,-1, 1,-1, 1,0, 1,1, 0,1, -1,1}; + float dmin = FLT_MAX; + for (int i = 0; i < 8; ++i) + { + const int nx = ix+off[i*2+0]; + const int nz = iz+off[i*2+1]; + if (nx < 0 || nz < 0 || nx >= hp.width || nz >= hp.height) continue; + const unsigned short nh = hp.data[nx+nz*hp.width]; + if (nh == RC_UNSET_HEIGHT) continue; + + const float d = fabsf(nh*ch - fy); + if (d < dmin) + { + h = nh; + dmin = d; + } + +/* const float dx = (nx+0.5f)*cs - fx; + const float dz = (nz+0.5f)*cs - fz; + const float d = dx*dx+dz*dz; + if (d < dmin) + { + h = nh; + dmin = d; + } */ + } + } return h; } -static bool buildPolyDetail(const float* in, const int nin, unsigned short reg, + +enum EdgeValues +{ + UNDEF = -1, + HULL = -2, +}; + +static int findEdge(const int* edges, int nedges, int s, int t) +{ + for (int i = 0; i < nedges; i++) + { + const int* e = &edges[i*4]; + if ((e[0] == s && e[1] == t) || (e[0] == t && e[1] == s)) + return i; + } + return UNDEF; +} + +static int addEdge(rcContext* ctx, int* edges, int& nedges, const int maxEdges, int s, int t, int l, int r) +{ + if (nedges >= maxEdges) + { + ctx->log(RC_LOG_ERROR, "addEdge: Too many edges (%d/%d).", nedges, maxEdges); + return UNDEF; + } + + // Add edge if not already in the triangulation. + int e = findEdge(edges, nedges, s, t); + if (e == UNDEF) + { + int* e = &edges[nedges*4]; + e[0] = s; + e[1] = t; + e[2] = l; + e[3] = r; + return nedges++; + } + else + { + return UNDEF; + } +} + +static void updateLeftFace(int* e, int s, int t, int f) +{ + if (e[0] == s && e[1] == t && e[2] == UNDEF) + e[2] = f; + else if (e[1] == s && e[0] == t && e[3] == UNDEF) + e[3] = f; +} + +static int overlapSegSeg2d(const float* a, const float* b, const float* c, const float* d) +{ + const float a1 = vcross2(a, b, d); + const float a2 = vcross2(a, b, c); + if (a1*a2 < 0.0f) + { + float a3 = vcross2(c, d, a); + float a4 = a3 + a2 - a1; + if (a3 * a4 < 0.0f) + return 1; + } + return 0; +} + +static bool overlapEdges(const float* pts, const int* edges, int nedges, int s1, int t1) +{ + for (int i = 0; i < nedges; ++i) + { + const int s0 = edges[i*4+0]; + const int t0 = edges[i*4+1]; + // Same or connected edges do not overlap. + if (s0 == s1 || s0 == t1 || t0 == s1 || t0 == t1) + continue; + if (overlapSegSeg2d(&pts[s0*3],&pts[t0*3], &pts[s1*3],&pts[t1*3])) + return true; + } + return false; +} + +static void completeFacet(rcContext* ctx, const float* pts, int npts, int* edges, int& nedges, const int maxEdges, int& nfaces, int e) +{ + static const float EPS = 1e-5f; + + int* edge = &edges[e*4]; + + // Cache s and t. + int s,t; + if (edge[2] == UNDEF) + { + s = edge[0]; + t = edge[1]; + } + else if (edge[3] == UNDEF) + { + s = edge[1]; + t = edge[0]; + } + else + { + // Edge already completed. + return; + } + + // Find best point on left of edge. + int pt = npts; + float c[3] = {0,0,0}; + float r = -1; + for (int u = 0; u < npts; ++u) + { + if (u == s || u == t) continue; + if (vcross2(&pts[s*3], &pts[t*3], &pts[u*3]) > EPS) + { + if (r < 0) + { + // The circle is not updated yet, do it now. + pt = u; + circumCircle(&pts[s*3], &pts[t*3], &pts[u*3], c, r); + continue; + } + const float d = vdist2(c, &pts[u*3]); + const float tol = 0.001f; + if (d > r*(1+tol)) + { + // Outside current circumcircle, skip. + continue; + } + else if (d < r*(1-tol)) + { + // Inside safe circumcircle, update circle. + pt = u; + circumCircle(&pts[s*3], &pts[t*3], &pts[u*3], c, r); + } + else + { + // Inside epsilon circum circle, do extra tests to make sure the edge is valid. + // s-u and t-u cannot overlap with s-pt nor t-pt if they exists. + if (overlapEdges(pts, edges, nedges, s,u)) + continue; + if (overlapEdges(pts, edges, nedges, t,u)) + continue; + // Edge is valid. + pt = u; + circumCircle(&pts[s*3], &pts[t*3], &pts[u*3], c, r); + } + } + } + + // Add new triangle or update edge info if s-t is on hull. + if (pt < npts) + { + // Update face information of edge being completed. + updateLeftFace(&edges[e*4], s, t, nfaces); + + // Add new edge or update face info of old edge. + e = findEdge(edges, nedges, pt, s); + if (e == UNDEF) + addEdge(ctx, edges, nedges, maxEdges, pt, s, nfaces, UNDEF); + else + updateLeftFace(&edges[e*4], pt, s, nfaces); + + // Add new edge or update face info of old edge. + e = findEdge(edges, nedges, t, pt); + if (e == UNDEF) + addEdge(ctx, edges, nedges, maxEdges, t, pt, nfaces, UNDEF); + else + updateLeftFace(&edges[e*4], t, pt, nfaces); + + nfaces++; + } + else + { + updateLeftFace(&edges[e*4], s, t, HULL); + } +} + +static void delaunayHull(rcContext* ctx, const int npts, const float* pts, + const int nhull, const int* hull, + rcIntArray& tris, rcIntArray& edges) +{ + int nfaces = 0; + int nedges = 0; + const int maxEdges = npts*10; + edges.resize(maxEdges*4); + + for (int i = 0, j = nhull-1; i < nhull; j=i++) + addEdge(ctx, &edges[0], nedges, maxEdges, hull[j],hull[i], HULL, UNDEF); + + int currentEdge = 0; + while (currentEdge < nedges) + { + if (edges[currentEdge*4+2] == UNDEF) + completeFacet(ctx, pts, npts, &edges[0], nedges, maxEdges, nfaces, currentEdge); + if (edges[currentEdge*4+3] == UNDEF) + completeFacet(ctx, pts, npts, &edges[0], nedges, maxEdges, nfaces, currentEdge); + currentEdge++; + } + + // Create tris + tris.resize(nfaces*4); + for (int i = 0; i < nfaces*4; ++i) + tris[i] = -1; + + for (int i = 0; i < nedges; ++i) + { + const int* e = &edges[i*4]; + if (e[3] >= 0) + { + // Left face + int* t = &tris[e[3]*4]; + if (t[0] == -1) + { + t[0] = e[0]; + t[1] = e[1]; + } + else if (t[0] == e[1]) + t[2] = e[0]; + else if (t[1] == e[0]) + t[2] = e[1]; + } + if (e[2] >= 0) + { + // Right + int* t = &tris[e[2]*4]; + if (t[0] == -1) + { + t[0] = e[1]; + t[1] = e[0]; + } + else if (t[0] == e[0]) + t[2] = e[1]; + else if (t[1] == e[1]) + t[2] = e[0]; + } + } + + for (int i = 0; i < tris.size()/4; ++i) + { + int* t = &tris[i*4]; + if (t[0] == -1 || t[1] == -1 || t[2] == -1) + { + ctx->log(RC_LOG_WARNING, "delaunayHull: Removing dangling face %d [%d,%d,%d].", i, t[0],t[1],t[2]); + t[0] = tris[tris.size()-4]; + t[1] = tris[tris.size()-3]; + t[2] = tris[tris.size()-2]; + t[3] = tris[tris.size()-1]; + tris.resize(tris.size()-4); + --i; + } + } +} + + +inline float getJitterX(const int i) +{ + return (((i * 0x8da6b343) & 0xffff) / 65535.0f * 2.0f) - 1.0f; +} + +inline float getJitterY(const int i) +{ + return (((i * 0xd8163841) & 0xffff) / 65535.0f * 2.0f) - 1.0f; +} + +static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin, const float sampleDist, const float sampleMaxError, const rcCompactHeightfield& chf, const rcHeightPatch& hp, float* verts, int& nverts, rcIntArray& tris, - rcIntArray& edges, rcIntArray& idx, rcIntArray& samples) + rcIntArray& edges, rcIntArray& samples) { - static const int MAX_VERTS = 256; - static const int MAX_EDGE = 64; - float edge[(MAX_EDGE+1)*3]; + 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_VERTS_PER_EDGE = 32; + float edge[(MAX_VERTS_PER_EDGE+1)*3]; + int hull[MAX_VERTS]; + int nhull = 0; nverts = 0; for (int i = 0; i < nin; ++i) - vcopy(&verts[i*3], &in[i*3]); + rcVcopy(&verts[i*3], &in[i*3]); nverts = nin; - const float ics = 1.0f/chf.cs; + const float cs = chf.cs; + const float ics = 1.0f/cs; - // Tesselate outlines. + // Tessellate outlines. // This is done in separate pass in order to ensure // seamless height values across the ply boundaries. if (sampleDist > 0) @@ -403,17 +530,24 @@ static bool buildPolyDetail(const float* in, const int nin, unsigned short reg, { const float* vj = &in[j*3]; const float* vi = &in[i*3]; + bool swapped = false; // Make sure the segments are always handled in same order // using lexological sort or else there will be seams. if (fabsf(vj[0]-vi[0]) < 1e-6f) { if (vj[2] > vi[2]) + { rcSwap(vj,vi); + swapped = true; + } } else { if (vj[0] > vi[0]) + { rcSwap(vj,vi); + swapped = true; + } } // Create samples along the edge. float dx = vi[0] - vj[0]; @@ -421,9 +555,10 @@ static bool buildPolyDetail(const float* in, const int nin, unsigned short reg, float dz = vi[2] - vj[2]; float d = sqrtf(dx*dx + dz*dz); int nn = 1 + (int)floorf(d/sampleDist); - if (nn > MAX_EDGE) nn = MAX_EDGE; + if (nn >= MAX_VERTS_PER_EDGE) nn = MAX_VERTS_PER_EDGE-1; if (nverts+nn >= MAX_VERTS) nn = MAX_VERTS-1-nverts; + for (int k = 0; k <= nn; ++k) { float u = (float)k/(float)nn; @@ -431,10 +566,10 @@ static bool buildPolyDetail(const float* in, const int nin, unsigned short reg, pos[0] = vj[0] + dx*u; pos[1] = vj[1] + dy*u; pos[2] = vj[2] + dz*u; - pos[1] = chf.bmin[1] + getHeight(pos, chf.bmin, ics, hp)*chf.ch; + pos[1] = getHeight(pos[0],pos[1],pos[2], cs, ics, chf.ch, hp)*chf.ch; } // Simplify samples. - int idx[MAX_EDGE] = {0,nn}; + int idx[MAX_VERTS_PER_EDGE] = {0,nn}; int nidx = 2; for (int k = 0; k < nidx-1; ) { @@ -468,31 +603,61 @@ static bool buildPolyDetail(const float* in, const int nin, unsigned short reg, ++k; } } + + hull[nhull++] = j; // Add new vertices. - for (int k = 1; k < nidx-1; ++k) + if (swapped) + { + for (int k = nidx-2; k > 0; --k) + { + rcVcopy(&verts[nverts*3], &edge[idx[k]*3]); + hull[nhull++] = nverts; + nverts++; + } + } + else { - vcopy(&verts[nverts*3], &edge[idx[k]*3]); - nverts++; + for (int k = 1; k < nidx-1; ++k) + { + rcVcopy(&verts[nverts*3], &edge[idx[k]*3]); + hull[nhull++] = nverts; + nverts++; + } } } } - // Tesselate the base mesh. + + // Tessellate the base mesh. edges.resize(0); tris.resize(0); - idx.resize(0); - delaunay(nverts, verts, idx, tris, edges); + + delaunayHull(ctx, nverts, verts, nhull, hull, tris, edges); + + if (tris.size() == 0) + { + // Could not triangulate the poly, make sure there is some valid data there. + ctx->log(RC_LOG_WARNING, "buildPolyDetail: Could not triangulate polygon, adding default data."); + for (int i = 2; i < nverts; ++i) + { + tris.push(0); + tris.push(i-1); + tris.push(i); + tris.push(0); + } + return true; + } if (sampleDist > 0) { // Create sample locations in a grid. float bmin[3], bmax[3]; - vcopy(bmin, in); - vcopy(bmax, in); + rcVcopy(bmin, in); + rcVcopy(bmax, in); for (int i = 1; i < nin; ++i) { - vmin(bmin, &in[i*3]); - vmax(bmax, &in[i*3]); + rcVmin(bmin, &in[i*3]); + rcVmax(bmax, &in[i*3]); } int x0 = (int)floorf(bmin[0]/sampleDist); int x1 = (int)ceilf(bmax[0]/sampleDist); @@ -505,56 +670,71 @@ static bool buildPolyDetail(const float* in, const int nin, unsigned short reg, { float pt[3]; pt[0] = x*sampleDist; + pt[1] = (bmax[1]+bmin[1])*0.5f; pt[2] = z*sampleDist; // Make sure the samples are not too close to the edges. if (distToPoly(nin,in,pt) > -sampleDist/2) continue; samples.push(x); - samples.push(getHeight(pt, chf.bmin, ics, hp)); + samples.push(getHeight(pt[0], pt[1], pt[2], cs, ics, chf.ch, hp)); samples.push(z); + samples.push(0); // Not added } } // Add the samples starting from the one that has the most // error. The procedure stops when all samples are added // or when the max error is within treshold. - const int nsamples = samples.size()/3; + const int nsamples = samples.size()/4; for (int iter = 0; iter < nsamples; ++iter) { + if (nverts >= MAX_VERTS) + break; + // Find sample with most error. - float bestpt[3]; + float bestpt[3] = {0,0,0}; float bestd = 0; + int besti = -1; for (int i = 0; i < nsamples; ++i) { + const int* s = &samples[i*4]; + if (s[3]) continue; // skip added. float pt[3]; - pt[0] = samples[i*3+0]*sampleDist; - pt[1] = chf.bmin[1] + samples[i*3+1]*chf.ch; - pt[2] = samples[i*3+2]*sampleDist; + // The sample location is jittered to get rid of some bad triangulations + // which are cause by symmetrical data from the grid structure. + pt[0] = s[0]*sampleDist + getJitterX(i)*cs*0.1f; + pt[1] = s[1]*chf.ch; + pt[2] = s[2]*sampleDist + getJitterY(i)*cs*0.1f; float d = distToTriMesh(pt, verts, nverts, &tris[0], tris.size()/4); if (d < 0) continue; // did not hit the mesh. if (d > bestd) { bestd = d; - vcopy(bestpt,pt); + besti = i; + rcVcopy(bestpt,pt); } } // If the max error is within accepted threshold, stop tesselating. - if (bestd <= sampleMaxError) + if (bestd <= sampleMaxError || besti == -1) break; - + // Mark sample as added. + samples[besti*4+3] = 1; // Add the new sample point. - vcopy(&verts[nverts*3],bestpt); + rcVcopy(&verts[nverts*3],bestpt); nverts++; // Create new triangulation. // TODO: Incremental add instead of full rebuild. edges.resize(0); tris.resize(0); - idx.resize(0); - delaunay(nverts, verts, idx, tris, edges); + delaunayHull(ctx, nverts, verts, nhull, hull, tris, edges); + } + } - if (nverts >= MAX_VERTS) - break; - } + const int ntris = tris.size()/4; + if (ntris > MAX_TRIS) + { + tris.resize(MAX_TRIS*4); + ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Shrinking triangle count from %d to max %d.", ntris, MAX_TRIS); } return true; @@ -562,82 +742,178 @@ static bool buildPolyDetail(const float* in, const int nin, unsigned short reg, static void getHeightData(const rcCompactHeightfield& chf, const unsigned short* poly, const int npoly, - const unsigned short* verts, + const unsigned short* verts, const int bs, rcHeightPatch& hp, rcIntArray& stack) { // Floodfill the heightfield to get 2D height data, // starting at vertex locations as seeds. - memset(hp.data, 0xff, sizeof(unsigned short)*hp.width*hp.height); - + // Note: Reads to the compact heightfield are offset by border size (bs) + // since border size offset is already removed from the polymesh vertices. + + memset(hp.data, 0, sizeof(unsigned short)*hp.width*hp.height); + stack.resize(0); + static const int offset[9*2] = + { + 0,0, -1,-1, 0,-1, 1,-1, 1,0, 1,1, 0,1, -1,1, -1,0, + }; + // Use poly vertices as seed points for the flood fill. for (int j = 0; j < npoly; ++j) { - const int ax = (int)verts[poly[j]*3+0]; - const int ay = (int)verts[poly[j]*3+1]; - const int az = (int)verts[poly[j]*3+2]; - if (ax < hp.xmin || ax >= hp.xmin+hp.width || - az < hp.ymin || az >= hp.ymin+hp.height) - continue; - - const rcCompactCell& c = chf.cells[ax+az*chf.width]; - int dmin = 0xffff; - int ai = -1; - for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + int cx = 0, cz = 0, ci =-1; + int dmin = RC_UNSET_HEIGHT; + for (int k = 0; k < 9; ++k) { - const rcCompactSpan& s = chf.spans[i]; - int d = rcAbs(ay - (int)s.y); - if (d < dmin) + const int ax = (int)verts[poly[j]*3+0] + offset[k*2+0]; + const int ay = (int)verts[poly[j]*3+1]; + const int az = (int)verts[poly[j]*3+2] + offset[k*2+1]; + if (ax < hp.xmin || ax >= hp.xmin+hp.width || + az < hp.ymin || az >= hp.ymin+hp.height) + continue; + + const rcCompactCell& c = chf.cells[(ax+bs)+(az+bs)*chf.width]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) { - ai = i; - dmin = d; + const rcCompactSpan& s = chf.spans[i]; + int d = rcAbs(ay - (int)s.y); + if (d < dmin) + { + cx = ax; + cz = az; + ci = i; + dmin = d; + } } } - if (ai != -1) + if (ci != -1) { - stack.push(ax); - stack.push(az); - stack.push(ai); + stack.push(cx); + stack.push(cz); + stack.push(ci); } } - + + // Find center of the polygon using flood fill. + int pcx = 0, pcz = 0; + for (int j = 0; j < npoly; ++j) + { + pcx += (int)verts[poly[j]*3+0]; + pcz += (int)verts[poly[j]*3+2]; + } + pcx /= npoly; + pcz /= npoly; + + for (int i = 0; i < stack.size(); i += 3) + { + int cx = stack[i+0]; + int cy = stack[i+1]; + int idx = cx-hp.xmin+(cy-hp.ymin)*hp.width; + hp.data[idx] = 1; + } + while (stack.size() > 0) { int ci = stack.pop(); int cy = stack.pop(); int cx = stack.pop(); - - // Skip already visited locations. - int idx = cx-hp.xmin+(cy-hp.ymin)*hp.width; - if (hp.data[idx] != 0xffff) - continue; + + // Check if close to center of the polygon. + if (rcAbs(cx-pcx) <= 1 && rcAbs(cy-pcz) <= 1) + { + stack.resize(0); + stack.push(cx); + stack.push(cy); + stack.push(ci); + break; + } const rcCompactSpan& cs = chf.spans[ci]; - hp.data[idx] = cs.y; for (int dir = 0; dir < 4; ++dir) { - if (rcGetCon(cs, dir) == 0xf) continue; + if (rcGetCon(cs, dir) == RC_NOT_CONNECTED) continue; const int ax = cx + rcGetDirOffsetX(dir); const int ay = cy + rcGetDirOffsetY(dir); - + if (ax < hp.xmin || ax >= (hp.xmin+hp.width) || ay < hp.ymin || ay >= (hp.ymin+hp.height)) continue; - - if (hp.data[ax-hp.xmin+(ay-hp.ymin)*hp.width] != 0xffff) + + if (hp.data[ax-hp.xmin+(ay-hp.ymin)*hp.width] != 0) continue; + + const int ai = (int)chf.cells[(ax+bs)+(ay+bs)*chf.width].index + rcGetCon(cs, dir); - const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(cs, dir); + int idx = ax-hp.xmin+(ay-hp.ymin)*hp.width; + hp.data[idx] = 1; stack.push(ax); stack.push(ay); stack.push(ai); } - } + } + + memset(hp.data, 0xff, sizeof(unsigned short)*hp.width*hp.height); + + // Mark start locations. + for (int i = 0; i < stack.size(); i += 3) + { + int cx = stack[i+0]; + int cy = stack[i+1]; + int ci = stack[i+2]; + int idx = cx-hp.xmin+(cy-hp.ymin)*hp.width; + const rcCompactSpan& cs = chf.spans[ci]; + hp.data[idx] = cs.y; + } + + static const int RETRACT_SIZE = 256; + int head = 0; + + while (head*3 < stack.size()) + { + int cx = stack[head*3+0]; + int cy = stack[head*3+1]; + int ci = stack[head*3+2]; + head++; + if (head >= RETRACT_SIZE) + { + head = 0; + if (stack.size() > RETRACT_SIZE*3) + memmove(&stack[0], &stack[RETRACT_SIZE*3], sizeof(int)*(stack.size()-RETRACT_SIZE*3)); + stack.resize(stack.size()-RETRACT_SIZE*3); + } + + const rcCompactSpan& cs = chf.spans[ci]; + for (int dir = 0; dir < 4; ++dir) + { + if (rcGetCon(cs, dir) == RC_NOT_CONNECTED) continue; + + const int ax = cx + rcGetDirOffsetX(dir); + const int ay = cy + rcGetDirOffsetY(dir); + + if (ax < hp.xmin || ax >= (hp.xmin+hp.width) || + ay < hp.ymin || ay >= (hp.ymin+hp.height)) + continue; + + if (hp.data[ax-hp.xmin+(ay-hp.ymin)*hp.width] != RC_UNSET_HEIGHT) + continue; + + const int ai = (int)chf.cells[(ax+bs)+(ay+bs)*chf.width].index + rcGetCon(cs, dir); + + const rcCompactSpan& as = chf.spans[ai]; + int idx = ax-hp.xmin+(ay-hp.ymin)*hp.width; + hp.data[idx] = as.y; + + stack.push(ax); + stack.push(ay); + stack.push(ai); + } + } + } static unsigned char getEdgeFlags(const float* va, const float* vb, @@ -664,51 +940,48 @@ static unsigned char getTriFlags(const float* va, const float* vb, const float* return flags; } - - -bool rcBuildPolyMeshDetail(const rcPolyMesh& mesh, const rcCompactHeightfield& chf, +/// @par +/// +/// See the #rcConfig documentation for more information on the configuration parameters. +/// +/// @see rcAllocPolyMeshDetail, rcPolyMesh, rcCompactHeightfield, rcPolyMeshDetail, rcConfig +bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompactHeightfield& chf, const float sampleDist, const float sampleMaxError, rcPolyMeshDetail& dmesh) { + rcAssert(ctx); + + ctx->startTimer(RC_TIMER_BUILD_POLYMESHDETAIL); + if (mesh.nverts == 0 || mesh.npolys == 0) return true; - - rcTimeVal startTime = rcGetPerformanceTimer(); - rcTimeVal endTime; - - int vcap; - int tcap; - + const int nvp = mesh.nvp; const float cs = mesh.cs; const float ch = mesh.ch; const float* orig = mesh.bmin; + const int borderSize = mesh.borderSize; rcIntArray edges(64); rcIntArray tris(512); - rcIntArray idx(512); rcIntArray stack(512); rcIntArray samples(512); float verts[256*3]; - float* poly = 0; - int* bounds = 0; rcHeightPatch hp; int nPolyVerts = 0; int maxhw = 0, maxhh = 0; - bounds = new int[mesh.npolys*4]; + rcScopedDelete<int> bounds = (int*)rcAlloc(sizeof(int)*mesh.npolys*4, RC_ALLOC_TEMP); if (!bounds) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'bounds' (%d).", mesh.npolys*4); - goto failure; + ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'bounds' (%d).", mesh.npolys*4); + return false; } - poly = new float[nvp*3]; - if (!bounds) + rcScopedDelete<float> poly = (float*)rcAlloc(sizeof(float)*nvp*3, RC_ALLOC_TEMP); + if (!poly) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'poly' (%d).", nvp*3); - goto failure; + ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'poly' (%d).", nvp*3); + return false; } // Find max size for a polygon area. @@ -725,7 +998,7 @@ bool rcBuildPolyMeshDetail(const rcPolyMesh& mesh, const rcCompactHeightfield& c ymax = 0; for (int j = 0; j < nvp; ++j) { - if(p[j] == 0xffff) break; + if(p[j] == RC_MESH_NULL_IDX) break; const unsigned short* v = &mesh.verts[p[j]*3]; xmin = rcMin(xmin, (int)v[0]); xmax = rcMax(xmax, (int)v[0]); @@ -742,58 +1015,54 @@ bool rcBuildPolyMeshDetail(const rcPolyMesh& mesh, const rcCompactHeightfield& c maxhh = rcMax(maxhh, ymax-ymin); } - hp.data = new unsigned short[maxhw*maxhh]; + hp.data = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxhw*maxhh, RC_ALLOC_TEMP); if (!hp.data) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'hp.data' (%d).", maxhw*maxhh); - goto failure; + ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'hp.data' (%d).", maxhw*maxhh); + return false; } - + dmesh.nmeshes = mesh.npolys; dmesh.nverts = 0; dmesh.ntris = 0; - dmesh.meshes = new unsigned short[dmesh.nmeshes*4]; + dmesh.meshes = (unsigned int*)rcAlloc(sizeof(unsigned int)*dmesh.nmeshes*4, RC_ALLOC_PERM); if (!dmesh.meshes) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.meshes' (%d).", dmesh.nmeshes*4); - goto failure; + ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.meshes' (%d).", dmesh.nmeshes*4); + return false; } - vcap = nPolyVerts+nPolyVerts/2; - tcap = vcap*2; + int vcap = nPolyVerts+nPolyVerts/2; + int tcap = vcap*2; dmesh.nverts = 0; - dmesh.verts = new float[vcap*3]; + dmesh.verts = (float*)rcAlloc(sizeof(float)*vcap*3, RC_ALLOC_PERM); if (!dmesh.verts) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.verts' (%d).", vcap*3); - goto failure; + ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.verts' (%d).", vcap*3); + return false; } dmesh.ntris = 0; - dmesh.tris = new unsigned char[tcap*4]; + dmesh.tris = (unsigned char*)rcAlloc(sizeof(unsigned char*)*tcap*4, RC_ALLOC_PERM); if (!dmesh.tris) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.tris' (%d).", tcap*4); - goto failure; + ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.tris' (%d).", tcap*4); + return false; } for (int i = 0; i < mesh.npolys; ++i) { const unsigned short* p = &mesh.polys[i*nvp*2]; - // Find polygon bounding box. + // Store polygon vertices for processing. int npoly = 0; for (int j = 0; j < nvp; ++j) { - if(p[j] == 0xffff) break; + if(p[j] == RC_MESH_NULL_IDX) break; const unsigned short* v = &mesh.verts[p[j]*3]; - poly[j*3+0] = orig[0] + v[0]*cs; - poly[j*3+1] = orig[1] + v[1]*ch; - poly[j*3+2] = orig[2] + v[2]*cs; + poly[j*3+0] = v[0]*cs; + poly[j*3+1] = v[1]*ch; + poly[j*3+2] = v[2]*cs; npoly++; } @@ -802,29 +1071,40 @@ bool rcBuildPolyMeshDetail(const rcPolyMesh& mesh, const rcCompactHeightfield& c hp.ymin = bounds[i*4+2]; hp.width = bounds[i*4+1]-bounds[i*4+0]; hp.height = bounds[i*4+3]-bounds[i*4+2]; - getHeightData(chf, p, npoly, mesh.verts, hp, stack); + getHeightData(chf, p, npoly, mesh.verts, borderSize, hp, stack); // Build detail mesh. int nverts = 0; - if (!buildPolyDetail(poly, npoly, mesh.regs[i], + if (!buildPolyDetail(ctx, poly, npoly, sampleDist, sampleMaxError, chf, hp, verts, nverts, tris, - edges, idx, samples)) + edges, samples)) { - goto failure; + return false; } - // Offset detail vertices, unnecassary? + // Move detail verts to world space. for (int j = 0; j < nverts; ++j) - verts[j*3+1] += chf.ch; + { + verts[j*3+0] += orig[0]; + verts[j*3+1] += orig[1] + chf.ch; // Is this offset necessary? + verts[j*3+2] += orig[2]; + } + // Offset poly too, will be used to flag checking. + for (int j = 0; j < npoly; ++j) + { + poly[j*3+0] += orig[0]; + poly[j*3+1] += orig[1]; + poly[j*3+2] += orig[2]; + } // Store detail submesh. const int ntris = tris.size()/4; - - dmesh.meshes[i*4+0] = dmesh.nverts; - dmesh.meshes[i*4+1] = (unsigned short)nverts; - dmesh.meshes[i*4+2] = dmesh.ntris; - dmesh.meshes[i*4+3] = (unsigned short)ntris; + + dmesh.meshes[i*4+0] = (unsigned int)dmesh.nverts; + dmesh.meshes[i*4+1] = (unsigned int)nverts; + dmesh.meshes[i*4+2] = (unsigned int)dmesh.ntris; + dmesh.meshes[i*4+3] = (unsigned int)ntris; // Store vertices, allocate more memory if necessary. if (dmesh.nverts+nverts > vcap) @@ -832,16 +1112,15 @@ bool rcBuildPolyMeshDetail(const rcPolyMesh& mesh, const rcCompactHeightfield& c while (dmesh.nverts+nverts > vcap) vcap += 256; - float* newv = new float[vcap*3]; + float* newv = (float*)rcAlloc(sizeof(float)*vcap*3, RC_ALLOC_PERM); if (!newv) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'newv' (%d).", vcap*3); - goto failure; + ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'newv' (%d).", vcap*3); + return false; } if (dmesh.nverts) memcpy(newv, dmesh.verts, sizeof(float)*3*dmesh.nverts); - delete [] dmesh.verts; + rcFree(dmesh.verts); dmesh.verts = newv; } for (int j = 0; j < nverts; ++j) @@ -857,16 +1136,15 @@ bool rcBuildPolyMeshDetail(const rcPolyMesh& mesh, const rcCompactHeightfield& c { while (dmesh.ntris+ntris > tcap) tcap += 256; - unsigned char* newt = new unsigned char[tcap*4]; + unsigned char* newt = (unsigned char*)rcAlloc(sizeof(unsigned char)*tcap*4, RC_ALLOC_PERM); if (!newt) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'newt' (%d).", tcap*4); - goto failure; + ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'newt' (%d).", tcap*4); + return false; } if (dmesh.ntris) memcpy(newt, dmesh.tris, sizeof(unsigned char)*4*dmesh.ntris); - delete [] dmesh.tris; + rcFree(dmesh.tris); dmesh.tris = newt; } for (int j = 0; j < ntris; ++j) @@ -879,29 +1157,19 @@ bool rcBuildPolyMeshDetail(const rcPolyMesh& mesh, const rcCompactHeightfield& c dmesh.ntris++; } } - - delete [] bounds; - delete [] poly; - - endTime = rcGetPerformanceTimer(); - - if (rcGetBuildTimes()) - rcGetBuildTimes()->buildDetailMesh += rcGetDeltaTimeUsec(startTime, endTime); + + ctx->stopTimer(RC_TIMER_BUILD_POLYMESHDETAIL); return true; - -failure: - - delete [] bounds; - delete [] poly; - - return false; } -bool rcMergePolyMeshDetails(rcPolyMeshDetail** meshes, const int nmeshes, rcPolyMeshDetail& mesh) +/// @see rcAllocPolyMeshDetail, rcPolyMeshDetail +bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int nmeshes, rcPolyMeshDetail& mesh) { - rcTimeVal startTime = rcGetPerformanceTimer(); + rcAssert(ctx); + ctx->startTimer(RC_TIMER_MERGE_POLYMESHDETAIL); + int maxVerts = 0; int maxTris = 0; int maxMeshes = 0; @@ -915,29 +1183,26 @@ bool rcMergePolyMeshDetails(rcPolyMeshDetail** meshes, const int nmeshes, rcPoly } mesh.nmeshes = 0; - mesh.meshes = new unsigned short[maxMeshes*4]; + mesh.meshes = (unsigned int*)rcAlloc(sizeof(unsigned int)*maxMeshes*4, RC_ALLOC_PERM); if (!mesh.meshes) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'pmdtl.meshes' (%d).", maxMeshes*4); + ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'pmdtl.meshes' (%d).", maxMeshes*4); return false; } mesh.ntris = 0; - mesh.tris = new unsigned char[maxTris*4]; + mesh.tris = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxTris*4, RC_ALLOC_PERM); if (!mesh.tris) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.tris' (%d).", maxTris*4); + ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.tris' (%d).", maxTris*4); return false; } mesh.nverts = 0; - mesh.verts = new float[maxVerts*3]; + mesh.verts = (float*)rcAlloc(sizeof(float)*maxVerts*3, RC_ALLOC_PERM); if (!mesh.verts) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.verts' (%d).", maxVerts*3); + ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.verts' (%d).", maxVerts*3); return false; } @@ -948,18 +1213,18 @@ bool rcMergePolyMeshDetails(rcPolyMeshDetail** meshes, const int nmeshes, rcPoly if (!dm) continue; for (int j = 0; j < dm->nmeshes; ++j) { - unsigned short* dst = &mesh.meshes[mesh.nmeshes*4]; - unsigned short* src = &dm->meshes[j*4]; - dst[0] = mesh.nverts+src[0]; + unsigned int* dst = &mesh.meshes[mesh.nmeshes*4]; + unsigned int* src = &dm->meshes[j*4]; + dst[0] = (unsigned int)mesh.nverts+src[0]; dst[1] = src[1]; - dst[2] = mesh.ntris+src[2]; + dst[2] = (unsigned int)mesh.ntris+src[2]; dst[3] = src[3]; mesh.nmeshes++; } for (int k = 0; k < dm->nverts; ++k) { - vcopy(&mesh.verts[mesh.nverts*3], &dm->verts[k*3]); + rcVcopy(&mesh.verts[mesh.nverts*3], &dm->verts[k*3]); mesh.nverts++; } for (int k = 0; k < dm->ntris; ++k) @@ -972,10 +1237,7 @@ bool rcMergePolyMeshDetails(rcPolyMeshDetail** meshes, const int nmeshes, rcPoly } } - rcTimeVal endTime = rcGetPerformanceTimer(); - - if (rcGetBuildTimes()) - rcGetBuildTimes()->mergePolyMeshDetail += rcGetDeltaTimeUsec(startTime, endTime); + ctx->stopTimer(RC_TIMER_MERGE_POLYMESHDETAIL); return true; } diff --git a/extern/recastnavigation/Recast/Source/RecastRasterization.cpp b/extern/recastnavigation/Recast/Source/RecastRasterization.cpp index 658b0e1fb51..d2bb7c98f18 100644 --- a/extern/recastnavigation/Recast/Source/RecastRasterization.cpp +++ b/extern/recastnavigation/Recast/Source/RecastRasterization.cpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2009 Mikko Mononen memon@inside.org +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -20,8 +20,8 @@ #include <math.h> #include <stdio.h> #include "Recast.h" -#include "RecastTimer.h" -#include "RecastLog.h" +#include "RecastAlloc.h" +#include "RecastAssert.h" inline bool overlapBounds(const float* amin, const float* amax, const float* bmin, const float* bmax) { @@ -48,8 +48,7 @@ static rcSpan* allocSpan(rcHeightfield& hf) { // Create new page. // Allocate memory for the new pool. - const int size = (sizeof(rcSpanPool)-sizeof(rcSpan)) + sizeof(rcSpan)*RC_SPANS_PER_POOL; - rcSpanPool* pool = reinterpret_cast<rcSpanPool*>(new unsigned char[size]); + rcSpanPool* pool = (rcSpanPool*)rcAlloc(sizeof(rcSpanPool), RC_ALLOC_PERM); if (!pool) return 0; pool->next = 0; // Add the pool into the list of pools. @@ -83,16 +82,17 @@ static void freeSpan(rcHeightfield& hf, rcSpan* ptr) hf.freelist = ptr; } -static void addSpan(rcHeightfield& hf, int x, int y, - unsigned short smin, unsigned short smax, - unsigned short flags) +static void addSpan(rcHeightfield& hf, const int x, const int y, + const unsigned short smin, const unsigned short smax, + const unsigned char area, const int flagMergeThr) { + int idx = x + y*hf.width; rcSpan* s = allocSpan(hf); s->smin = smin; s->smax = smax; - s->flags = flags; + s->area = area; s->next = 0; // Empty cell, add he first span. @@ -127,9 +127,8 @@ static void addSpan(rcHeightfield& hf, int x, int y, s->smax = cur->smax; // Merge flags. -// if (s->smax == cur->smax) - if (rcAbs((int)s->smax - (int)cur->smax) <= 1) - s->flags |= cur->flags; + if (rcAbs((int)s->smax - (int)cur->smax) <= flagMergeThr) + s->area = rcMax(s->area, cur->area); // Remove current span. rcSpan* next = cur->next; @@ -155,6 +154,21 @@ static void addSpan(rcHeightfield& hf, int x, int y, } } +/// @par +/// +/// The span addition can be set to favor flags. If the span is merged to +/// another span and the new @p smax is within @p flagMergeThr units +/// from the existing span, the span flags are merged. +/// +/// @see rcHeightfield, rcSpan. +void rcAddSpan(rcContext* /*ctx*/, rcHeightfield& hf, const int x, const int y, + const unsigned short smin, const unsigned short smax, + const unsigned char area, const int flagMergeThr) +{ +// rcAssert(ctx); + addSpan(hf, x,y, smin, smax, area, flagMergeThr); +} + static int clipPoly(const float* in, int n, float* out, float pnx, float pnz, float pd) { float d[12]; @@ -186,9 +200,10 @@ static int clipPoly(const float* in, int n, float* out, float pnx, float pnz, fl } static void rasterizeTri(const float* v0, const float* v1, const float* v2, - unsigned char flags, rcHeightfield& hf, + const unsigned char area, rcHeightfield& hf, const float* bmin, const float* bmax, - const float cs, const float ics, const float ich) + const float cs, const float ics, const float ich, + const int flagMergeThr) { const int w = hf.width; const int h = hf.height; @@ -196,12 +211,12 @@ static void rasterizeTri(const float* v0, const float* v1, const float* v2, const float by = bmax[1] - bmin[1]; // Calculate the bounding box of the triangle. - vcopy(tmin, v0); - vcopy(tmax, v0); - vmin(tmin, v1); - vmin(tmin, v2); - vmax(tmax, v1); - vmax(tmax, v2); + rcVcopy(tmin, v0); + rcVcopy(tmax, v0); + rcVmin(tmin, v1); + rcVmin(tmin, v2); + rcVmax(tmax, v1); + rcVmax(tmax, v2); // If the triangle does not touch the bbox of the heightfield, skip the triagle. if (!overlapBounds(bmin, bmax, tmin, tmax)) @@ -223,9 +238,9 @@ static void rasterizeTri(const float* v0, const float* v1, const float* v2, for (int y = y0; y <= y1; ++y) { // Clip polygon to row. - vcopy(&in[0], v0); - vcopy(&in[1*3], v1); - vcopy(&in[2*3], v2); + rcVcopy(&in[0], v0); + rcVcopy(&in[1*3], v1); + rcVcopy(&in[2*3], v2); int nvrow = 3; const float cz = bmin[2] + y*cs; nvrow = clipPoly(in, nvrow, out, 0, 1, -cz); @@ -256,38 +271,78 @@ static void rasterizeTri(const float* v0, const float* v1, const float* v2, if (smax < 0.0f) continue; if (smin > by) continue; // Clamp the span to the heightfield bbox. - if (smin < 0.0f) smin = bmin[1]; - if (smax > by) smax = bmax[1]; + if (smin < 0.0f) smin = 0; + if (smax > by) smax = by; // Snap the span to the heightfield height grid. - unsigned short ismin = (unsigned short)rcClamp((int)floorf(smin * ich), 0, 0x7fff); - unsigned short ismax = (unsigned short)rcClamp((int)ceilf(smax * ich), 0, 0x7fff); + unsigned short ismin = (unsigned short)rcClamp((int)floorf(smin * ich), 0, RC_SPAN_MAX_HEIGHT); + unsigned short ismax = (unsigned short)rcClamp((int)ceilf(smax * ich), (int)ismin+1, RC_SPAN_MAX_HEIGHT); - addSpan(hf, x, y, ismin, ismax, flags); + addSpan(hf, x, y, ismin, ismax, area, flagMergeThr); } } } -void rcRasterizeTriangle(const float* v0, const float* v1, const float* v2, - unsigned char flags, rcHeightfield& solid) +/// @par +/// +/// No spans will be added if the triangle does not overlap the heightfield grid. +/// +/// @see rcHeightfield +void rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const float* v2, + const unsigned char area, rcHeightfield& solid, + const int flagMergeThr) { - rcTimeVal startTime = rcGetPerformanceTimer(); + rcAssert(ctx); + + ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES); const float ics = 1.0f/solid.cs; const float ich = 1.0f/solid.ch; - rasterizeTri(v0, v1, v2, flags, solid, solid.bmin, solid.bmax, solid.cs, ics, ich); + rasterizeTri(v0, v1, v2, area, solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr); + + ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES); +} + +/// @par +/// +/// Spans will only be added for triangles that overlap the heightfield grid. +/// +/// @see rcHeightfield +void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/, + const int* tris, const unsigned char* areas, const int nt, + rcHeightfield& solid, const int flagMergeThr) +{ + rcAssert(ctx); - rcTimeVal endTime = rcGetPerformanceTimer(); + ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES); + + const float ics = 1.0f/solid.cs; + const float ich = 1.0f/solid.ch; + // Rasterize triangles. + for (int i = 0; i < nt; ++i) + { + const float* v0 = &verts[tris[i*3+0]*3]; + const float* v1 = &verts[tris[i*3+1]*3]; + const float* v2 = &verts[tris[i*3+2]*3]; + // Rasterize. + rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr); + } - if (rcGetBuildTimes()) - rcGetBuildTimes()->rasterizeTriangles += rcGetDeltaTimeUsec(startTime, endTime); + ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES); } - -void rcRasterizeTriangles(const float* verts, int nv, - const int* tris, const unsigned char* flags, int nt, - rcHeightfield& solid) + +/// @par +/// +/// Spans will only be added for triangles that overlap the heightfield grid. +/// +/// @see rcHeightfield +void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/, + const unsigned short* tris, const unsigned char* areas, const int nt, + rcHeightfield& solid, const int flagMergeThr) { - rcTimeVal startTime = rcGetPerformanceTimer(); + rcAssert(ctx); + + ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES); const float ics = 1.0f/solid.cs; const float ich = 1.0f/solid.ch; @@ -298,11 +353,35 @@ void rcRasterizeTriangles(const float* verts, int nv, const float* v1 = &verts[tris[i*3+1]*3]; const float* v2 = &verts[tris[i*3+2]*3]; // Rasterize. - rasterizeTri(v0, v1, v2, flags[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich); + rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr); } - rcTimeVal endTime = rcGetPerformanceTimer(); + ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES); +} - if (rcGetBuildTimes()) - rcGetBuildTimes()->rasterizeTriangles += rcGetDeltaTimeUsec(startTime, endTime); +/// @par +/// +/// Spans will only be added for triangles that overlap the heightfield grid. +/// +/// @see rcHeightfield +void rcRasterizeTriangles(rcContext* ctx, const float* verts, const unsigned char* areas, const int nt, + rcHeightfield& solid, const int flagMergeThr) +{ + rcAssert(ctx); + + ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES); + + const float ics = 1.0f/solid.cs; + const float ich = 1.0f/solid.ch; + // Rasterize triangles. + for (int i = 0; i < nt; ++i) + { + const float* v0 = &verts[(i*3+0)*3]; + const float* v1 = &verts[(i*3+1)*3]; + const float* v2 = &verts[(i*3+2)*3]; + // Rasterize. + rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr); + } + + ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES); } diff --git a/extern/recastnavigation/Recast/Source/RecastRegion.cpp b/extern/recastnavigation/Recast/Source/RecastRegion.cpp index 5c557cf0681..4290972ed24 100644 --- a/extern/recastnavigation/Recast/Source/RecastRegion.cpp +++ b/extern/recastnavigation/Recast/Source/RecastRegion.cpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2009 Mikko Mononen memon@inside.org +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages @@ -23,13 +23,12 @@ #include <stdlib.h> #include <stdio.h> #include "Recast.h" -#include "RecastLog.h" -#include "RecastTimer.h" +#include "RecastAlloc.h" +#include "RecastAssert.h" +#include <new> -static unsigned short* calculateDistanceField(rcCompactHeightfield& chf, - unsigned short* src, unsigned short* dst, - unsigned short& maxDist) +static void calculateDistanceField(rcCompactHeightfield& chf, unsigned short* src, unsigned short& maxDist) { const int w = chf.width; const int h = chf.height; @@ -47,11 +46,19 @@ static unsigned short* calculateDistanceField(rcCompactHeightfield& chf, for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) { const rcCompactSpan& s = chf.spans[i]; + const unsigned char area = chf.areas[i]; + int nc = 0; for (int dir = 0; dir < 4; ++dir) { - if (rcGetCon(s, dir) != 0xf) - nc++; + if (rcGetCon(s, dir) != RC_NOT_CONNECTED) + { + const int ax = x + rcGetDirOffsetX(dir); + const int ay = y + rcGetDirOffsetY(dir); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir); + if (area == chf.areas[ai]) + nc++; + } } if (nc != 4) src[i] = 0; @@ -59,6 +66,7 @@ static unsigned short* calculateDistanceField(rcCompactHeightfield& chf, } } + // Pass 1 for (int y = 0; y < h; ++y) { @@ -69,7 +77,7 @@ static unsigned short* calculateDistanceField(rcCompactHeightfield& chf, { const rcCompactSpan& s = chf.spans[i]; - if (rcGetCon(s, 0) != 0xf) + if (rcGetCon(s, 0) != RC_NOT_CONNECTED) { // (-1,0) const int ax = x + rcGetDirOffsetX(0); @@ -80,7 +88,7 @@ static unsigned short* calculateDistanceField(rcCompactHeightfield& chf, src[i] = src[ai]+2; // (-1,-1) - if (rcGetCon(as, 3) != 0xf) + if (rcGetCon(as, 3) != RC_NOT_CONNECTED) { const int aax = ax + rcGetDirOffsetX(3); const int aay = ay + rcGetDirOffsetY(3); @@ -89,7 +97,7 @@ static unsigned short* calculateDistanceField(rcCompactHeightfield& chf, src[i] = src[aai]+3; } } - if (rcGetCon(s, 3) != 0xf) + if (rcGetCon(s, 3) != RC_NOT_CONNECTED) { // (0,-1) const int ax = x + rcGetDirOffsetX(3); @@ -100,7 +108,7 @@ static unsigned short* calculateDistanceField(rcCompactHeightfield& chf, src[i] = src[ai]+2; // (1,-1) - if (rcGetCon(as, 2) != 0xf) + if (rcGetCon(as, 2) != RC_NOT_CONNECTED) { const int aax = ax + rcGetDirOffsetX(2); const int aay = ay + rcGetDirOffsetY(2); @@ -123,7 +131,7 @@ static unsigned short* calculateDistanceField(rcCompactHeightfield& chf, { const rcCompactSpan& s = chf.spans[i]; - if (rcGetCon(s, 2) != 0xf) + if (rcGetCon(s, 2) != RC_NOT_CONNECTED) { // (1,0) const int ax = x + rcGetDirOffsetX(2); @@ -134,7 +142,7 @@ static unsigned short* calculateDistanceField(rcCompactHeightfield& chf, src[i] = src[ai]+2; // (1,1) - if (rcGetCon(as, 1) != 0xf) + if (rcGetCon(as, 1) != RC_NOT_CONNECTED) { const int aax = ax + rcGetDirOffsetX(1); const int aay = ay + rcGetDirOffsetY(1); @@ -143,7 +151,7 @@ static unsigned short* calculateDistanceField(rcCompactHeightfield& chf, src[i] = src[aai]+3; } } - if (rcGetCon(s, 1) != 0xf) + if (rcGetCon(s, 1) != RC_NOT_CONNECTED) { // (0,1) const int ax = x + rcGetDirOffsetX(1); @@ -154,7 +162,7 @@ static unsigned short* calculateDistanceField(rcCompactHeightfield& chf, src[i] = src[ai]+2; // (-1,1) - if (rcGetCon(as, 0) != 0xf) + if (rcGetCon(as, 0) != RC_NOT_CONNECTED) { const int aax = ax + rcGetDirOffsetX(0); const int aay = ay + rcGetDirOffsetY(0); @@ -171,8 +179,6 @@ static unsigned short* calculateDistanceField(rcCompactHeightfield& chf, for (int i = 0; i < chf.spanCount; ++i) maxDist = rcMax(src[i], maxDist); - return src; - } static unsigned short* boxBlur(rcCompactHeightfield& chf, int thr, @@ -191,17 +197,17 @@ static unsigned short* boxBlur(rcCompactHeightfield& chf, int thr, for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) { const rcCompactSpan& s = chf.spans[i]; - int cd = (int)src[i]; + const unsigned short cd = src[i]; if (cd <= thr) { dst[i] = cd; continue; } - int d = cd; + int d = (int)cd; for (int dir = 0; dir < 4; ++dir) { - if (rcGetCon(s, dir) != 0xf) + if (rcGetCon(s, dir) != RC_NOT_CONNECTED) { const int ax = x + rcGetDirOffsetX(dir); const int ay = y + rcGetDirOffsetY(dir); @@ -210,7 +216,7 @@ static unsigned short* boxBlur(rcCompactHeightfield& chf, int thr, const rcCompactSpan& as = chf.spans[ai]; const int dir2 = (dir+1) & 0x3; - if (rcGetCon(as, dir2) != 0xf) + if (rcGetCon(as, dir2) != RC_NOT_CONNECTED) { const int ax2 = ax + rcGetDirOffsetX(dir2); const int ay2 = ay + rcGetDirOffsetY(dir2); @@ -236,22 +242,24 @@ static unsigned short* boxBlur(rcCompactHeightfield& chf, int thr, static bool floodRegion(int x, int y, int i, - unsigned short level, unsigned short minLevel, unsigned short r, + unsigned short level, unsigned short r, rcCompactHeightfield& chf, - unsigned short* src, + unsigned short* srcReg, unsigned short* srcDist, rcIntArray& stack) { const int w = chf.width; + const unsigned char area = chf.areas[i]; + // Flood fill mark region. stack.resize(0); stack.push((int)x); stack.push((int)y); stack.push((int)i); - src[i*2] = r; - src[i*2+1] = 0; + srcReg[i] = r; + srcDist[i] = 0; - unsigned short lev = level >= minLevel+2 ? level-2 : minLevel; + unsigned short lev = level >= 2 ? level-2 : 0; int count = 0; while (stack.size() > 0) @@ -267,25 +275,30 @@ static bool floodRegion(int x, int y, int i, for (int dir = 0; dir < 4; ++dir) { // 8 connected - if (rcGetCon(cs, dir) != 0xf) + if (rcGetCon(cs, dir) != RC_NOT_CONNECTED) { const int ax = cx + rcGetDirOffsetX(dir); const int ay = cy + rcGetDirOffsetY(dir); const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(cs, dir); - unsigned short nr = src[ai*2]; + if (chf.areas[ai] != area) + continue; + unsigned short nr = srcReg[ai]; + if (nr & RC_BORDER_REG) // Do not take borders into account. + continue; if (nr != 0 && nr != r) ar = nr; const rcCompactSpan& as = chf.spans[ai]; const int dir2 = (dir+1) & 0x3; - if (rcGetCon(as, dir2) != 0xf) + if (rcGetCon(as, dir2) != RC_NOT_CONNECTED) { const int ax2 = ax + rcGetDirOffsetX(dir2); const int ay2 = ay + rcGetDirOffsetY(dir2); const int ai2 = (int)chf.cells[ax2+ay2*w].index + rcGetCon(as, dir2); - - unsigned short nr = src[ai2*2]; + if (chf.areas[ai2] != area) + continue; + unsigned short nr = srcReg[ai2]; if (nr != 0 && nr != r) ar = nr; } @@ -293,7 +306,7 @@ static bool floodRegion(int x, int y, int i, } if (ar != 0) { - src[ci*2] = 0; + srcReg[ci] = 0; continue; } count++; @@ -301,21 +314,20 @@ static bool floodRegion(int x, int y, int i, // Expand neighbours. for (int dir = 0; dir < 4; ++dir) { - if (rcGetCon(cs, dir) != 0xf) + if (rcGetCon(cs, dir) != RC_NOT_CONNECTED) { const int ax = cx + rcGetDirOffsetX(dir); const int ay = cy + rcGetDirOffsetY(dir); const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(cs, dir); - if (chf.spans[ai].dist >= lev) + if (chf.areas[ai] != area) + continue; + if (chf.dist[ai] >= lev && srcReg[ai] == 0) { - if (src[ai*2] == 0) - { - src[ai*2] = r; - src[ai*2+1] = 0; - stack.push(ax); - stack.push(ay); - stack.push(ai); - } + srcReg[ai] = r; + srcDist[ai] = 0; + stack.push(ax); + stack.push(ay); + stack.push(ai); } } } @@ -326,8 +338,8 @@ static bool floodRegion(int x, int y, int i, static unsigned short* expandRegions(int maxIter, unsigned short level, rcCompactHeightfield& chf, - unsigned short* src, - unsigned short* dst, + unsigned short* srcReg, unsigned short* srcDist, + unsigned short* dstReg, unsigned short* dstDist, rcIntArray& stack) { const int w = chf.width; @@ -342,7 +354,7 @@ static unsigned short* expandRegions(int maxIter, unsigned short level, const rcCompactCell& c = chf.cells[x+y*w]; for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) { - if (chf.spans[i].dist >= level && src[i*2] == 0) + if (chf.dist[i] >= level && srcReg[i] == 0 && chf.areas[i] != RC_NULL_AREA) { stack.push(x); stack.push(y); @@ -357,7 +369,8 @@ static unsigned short* expandRegions(int maxIter, unsigned short level, { int failed = 0; - memcpy(dst, src, sizeof(unsigned short)*chf.spanCount*2); + memcpy(dstReg, srcReg, sizeof(unsigned short)*chf.spanCount); + memcpy(dstDist, srcDist, sizeof(unsigned short)*chf.spanCount); for (int j = 0; j < stack.size(); j += 3) { @@ -370,29 +383,31 @@ static unsigned short* expandRegions(int maxIter, unsigned short level, continue; } - unsigned short r = src[i*2]; + unsigned short r = srcReg[i]; unsigned short d2 = 0xffff; + const unsigned char area = chf.areas[i]; const rcCompactSpan& s = chf.spans[i]; for (int dir = 0; dir < 4; ++dir) { - if (rcGetCon(s, dir) == 0xf) continue; + if (rcGetCon(s, dir) == RC_NOT_CONNECTED) continue; const int ax = x + rcGetDirOffsetX(dir); const int ay = y + rcGetDirOffsetY(dir); const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir); - if (src[ai*2] > 0 && (src[ai*2] & RC_BORDER_REG) == 0) + if (chf.areas[ai] != area) continue; + if (srcReg[ai] > 0 && (srcReg[ai] & RC_BORDER_REG) == 0) { - if ((int)src[ai*2+1]+2 < (int)d2) + if ((int)srcDist[ai]+2 < (int)d2) { - r = src[ai*2]; - d2 = src[ai*2+1]+2; + r = srcReg[ai]; + d2 = srcDist[ai]+2; } } } if (r) { stack[j+2] = -1; // mark as used - dst[i*2] = r; - dst[i*2+1] = d2; + dstReg[i] = r; + dstDist[i] = d2; } else { @@ -401,7 +416,8 @@ static unsigned short* expandRegions(int maxIter, unsigned short level, } // rcSwap source and dest. - rcSwap(src, dst); + rcSwap(srcReg, dstReg); + rcSwap(srcDist, dstDist); if (failed*3 == stack.size()) break; @@ -414,17 +430,25 @@ static unsigned short* expandRegions(int maxIter, unsigned short level, } } - return src; + return srcReg; } struct rcRegion { - inline rcRegion() : count(0), id(0), remap(false) {} + inline rcRegion(unsigned short i) : + spanCount(0), + id(i), + areaType(0), + remap(false), + visited(false) + {} - int count; - unsigned short id; + int spanCount; // Number of spans belonging to this region + unsigned short id; // ID of the region + unsigned char areaType; // Are type. bool remap; + bool visited; rcIntArray connections; rcIntArray floors; }; @@ -467,25 +491,27 @@ static void replaceNeighbour(rcRegion& reg, unsigned short oldId, unsigned short removeAdjacentNeighbours(reg); } -static bool canMergeWithRegion(rcRegion& reg, unsigned short id) +static bool canMergeWithRegion(const rcRegion& rega, const rcRegion& regb) { + if (rega.areaType != regb.areaType) + return false; int n = 0; - for (int i = 0; i < reg.connections.size(); ++i) + for (int i = 0; i < rega.connections.size(); ++i) { - if (reg.connections[i] == id) + if (rega.connections[i] == regb.id) n++; } if (n > 1) return false; - for (int i = 0; i < reg.floors.size(); ++i) + for (int i = 0; i < rega.floors.size(); ++i) { - if (reg.floors[i] == id) + if (rega.floors[i] == regb.id) return false; } return true; } -static void addUniqueFloorRegion(rcRegion& reg, unsigned short n) +static void addUniqueFloorRegion(rcRegion& reg, int n) { for (int i = 0; i < reg.floors.size(); ++i) if (reg.floors[i] == n) @@ -543,8 +569,8 @@ static bool mergeRegions(rcRegion& rega, rcRegion& regb) for (int j = 0; j < regb.floors.size(); ++j) addUniqueFloorRegion(rega, regb.floors[j]); - rega.count += regb.count; - regb.count = 0; + rega.spanCount += regb.spanCount; + regb.spanCount = 0; regb.connections.resize(0); return true; @@ -562,26 +588,26 @@ static bool isRegionConnectedToBorder(const rcRegion& reg) return false; } -static bool isSolidEdge(rcCompactHeightfield& chf, unsigned short* src, +static bool isSolidEdge(rcCompactHeightfield& chf, unsigned short* srcReg, int x, int y, int i, int dir) { const rcCompactSpan& s = chf.spans[i]; unsigned short r = 0; - if (rcGetCon(s, dir) != 0xf) + if (rcGetCon(s, dir) != RC_NOT_CONNECTED) { const int ax = x + rcGetDirOffsetX(dir); const int ay = y + rcGetDirOffsetY(dir); const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir); - r = src[ai*2]; + r = srcReg[ai]; } - if (r == src[i*2]) + if (r == srcReg[i]) return false; return true; } static void walkContour(int x, int y, int i, int dir, rcCompactHeightfield& chf, - unsigned short* src, + unsigned short* srcReg, rcIntArray& cont) { int startDir = dir; @@ -589,12 +615,12 @@ static void walkContour(int x, int y, int i, int dir, const rcCompactSpan& ss = chf.spans[i]; unsigned short curReg = 0; - if (rcGetCon(ss, dir) != 0xf) + if (rcGetCon(ss, dir) != RC_NOT_CONNECTED) { const int ax = x + rcGetDirOffsetX(dir); const int ay = y + rcGetDirOffsetY(dir); const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(ss, dir); - curReg = src[ai*2]; + curReg = srcReg[ai]; } cont.push(curReg); @@ -603,16 +629,16 @@ static void walkContour(int x, int y, int i, int dir, { const rcCompactSpan& s = chf.spans[i]; - if (isSolidEdge(chf, src, x, y, i, dir)) + if (isSolidEdge(chf, srcReg, x, y, i, dir)) { // Choose the edge corner unsigned short r = 0; - if (rcGetCon(s, dir) != 0xf) + if (rcGetCon(s, dir) != RC_NOT_CONNECTED) { const int ax = x + rcGetDirOffsetX(dir); const int ay = y + rcGetDirOffsetY(dir); const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir); - r = src[ai*2]; + r = srcReg[ai]; } if (r != curReg) { @@ -627,7 +653,7 @@ static void walkContour(int x, int y, int i, int dir, int ni = -1; const int nx = x + rcGetDirOffsetX(dir); const int ny = y + rcGetDirOffsetY(dir); - if (rcGetCon(s, dir) != 0xf) + if (rcGetCon(s, dir) != RC_NOT_CONNECTED) { const rcCompactCell& nc = chf.cells[nx+ny*chf.width]; ni = (int)nc.index + rcGetCon(s, dir); @@ -667,26 +693,26 @@ static void walkContour(int x, int y, int i, int dir, } } -static bool filterSmallRegions(int minRegionSize, int mergeRegionSize, +static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegionSize, unsigned short& maxRegionId, rcCompactHeightfield& chf, - unsigned short* src) + unsigned short* srcReg) { const int w = chf.width; const int h = chf.height; - - int nreg = maxRegionId+1; - rcRegion* regions = new rcRegion[nreg]; + + const int nreg = maxRegionId+1; + rcRegion* regions = (rcRegion*)rcAlloc(sizeof(rcRegion)*nreg, RC_ALLOC_TEMP); if (!regions) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "filterSmallRegions: Out of memory 'regions' (%d).", nreg); + ctx->log(RC_LOG_ERROR, "filterSmallRegions: Out of memory 'regions' (%d).", nreg); return false; } - - for (int i = 0; i < nreg; ++i) - regions[i].id = (unsigned short)i; + // Construct regions + for (int i = 0; i < nreg; ++i) + new(®ions[i]) rcRegion((unsigned short)i); + // Find edge of a region and find connections around the contour. for (int y = 0; y < h; ++y) { @@ -695,19 +721,19 @@ static bool filterSmallRegions(int minRegionSize, int mergeRegionSize, const rcCompactCell& c = chf.cells[x+y*w]; for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) { - unsigned short r = src[i*2]; + unsigned short r = srcReg[i]; if (r == 0 || r >= nreg) continue; rcRegion& reg = regions[r]; - reg.count++; + reg.spanCount++; + - // Update floors. for (int j = (int)c.index; j < ni; ++j) { if (i == j) continue; - unsigned short floorId = src[j*2]; + unsigned short floorId = srcReg[j]; if (floorId == 0 || floorId >= nreg) continue; addUniqueFloorRegion(reg, floorId); @@ -717,11 +743,13 @@ static bool filterSmallRegions(int minRegionSize, int mergeRegionSize, if (reg.connections.size() > 0) continue; + reg.areaType = chf.areas[i]; + // Check if this cell is next to a border. int ndir = -1; for (int dir = 0; dir < 4; ++dir) { - if (isSolidEdge(chf, src, x, y, i, dir)) + if (isSolidEdge(chf, srcReg, x, y, i, dir)) { ndir = dir; break; @@ -732,32 +760,77 @@ static bool filterSmallRegions(int minRegionSize, int mergeRegionSize, { // The cell is at border. // Walk around the contour to find all the neighbours. - walkContour(x, y, i, ndir, chf, src, reg.connections); + walkContour(x, y, i, ndir, chf, srcReg, reg.connections); } } } } - - // Remove too small unconnected regions. + + // Remove too small regions. + rcIntArray stack(32); + rcIntArray trace(32); for (int i = 0; i < nreg; ++i) { rcRegion& reg = regions[i]; if (reg.id == 0 || (reg.id & RC_BORDER_REG)) - continue; - if (reg.count == 0) + continue; + if (reg.spanCount == 0) continue; + if (reg.visited) + continue; + + // Count the total size of all the connected regions. + // Also keep track of the regions connects to a tile border. + bool connectsToBorder = false; + int spanCount = 0; + stack.resize(0); + trace.resize(0); + + reg.visited = true; + stack.push(i); - if (reg.connections.size() == 1 && reg.connections[0] == 0) + while (stack.size()) { - if (reg.count < minRegionSize) + // Pop + int ri = stack.pop(); + + rcRegion& creg = regions[ri]; + + spanCount += creg.spanCount; + trace.push(ri); + + for (int j = 0; j < creg.connections.size(); ++j) { - // Non-connected small region, remove. - reg.count = 0; - reg.id = 0; + if (creg.connections[j] & RC_BORDER_REG) + { + connectsToBorder = true; + continue; + } + rcRegion& nreg = regions[creg.connections[j]]; + if (nreg.visited) + continue; + if (nreg.id == 0 || (nreg.id & RC_BORDER_REG)) + continue; + // Visit + stack.push(nreg.id); + nreg.visited = true; } } - } + // If the accumulated regions size is too small, remove it. + // Do not remove areas which connect to tile borders + // as their size cannot be estimated correctly and removing them + // can potentially remove necessary areas. + if (spanCount < minRegionArea && !connectsToBorder) + { + // Kill all visited regions. + for (int j = 0; j < trace.size(); ++j) + { + regions[trace[j]].spanCount = 0; + regions[trace[j]].id = 0; + } + } + } // Merge too small regions to neighbour regions. int mergeCount = 0 ; @@ -768,14 +841,14 @@ static bool filterSmallRegions(int minRegionSize, int mergeRegionSize, { rcRegion& reg = regions[i]; if (reg.id == 0 || (reg.id & RC_BORDER_REG)) - continue; - if (reg.count == 0) + continue; + if (reg.spanCount == 0) continue; - + // Check to see if the region should be merged. - if (reg.count > mergeRegionSize && isRegionConnectedToBorder(reg)) + if (reg.spanCount > mergeRegionSize && isRegionConnectedToBorder(reg)) continue; - + // Small region with more than 1 connection. // Or region which is not connected to a border at all. // Find smallest neighbour region that connects to this one. @@ -786,11 +859,11 @@ static bool filterSmallRegions(int minRegionSize, int mergeRegionSize, if (reg.connections[j] & RC_BORDER_REG) continue; rcRegion& mreg = regions[reg.connections[j]]; if (mreg.id == 0 || (mreg.id & RC_BORDER_REG)) continue; - if (mreg.count < smallest && - canMergeWithRegion(reg, mreg.id) && - canMergeWithRegion(mreg, reg.id)) + if (mreg.spanCount < smallest && + canMergeWithRegion(reg, mreg) && + canMergeWithRegion(mreg, reg)) { - smallest = mreg.count; + smallest = mreg.spanCount; mergeId = mreg.id; } } @@ -821,16 +894,16 @@ static bool filterSmallRegions(int minRegionSize, int mergeRegionSize, } } while (mergeCount > 0); - + // Compress region Ids. for (int i = 0; i < nreg; ++i) { regions[i].remap = false; - if (regions[i].id == 0) continue; // Skip nil regions. - if (regions[i].id & RC_BORDER_REG) continue; // Skip external regions. + if (regions[i].id == 0) continue; // Skip nil regions. + if (regions[i].id & RC_BORDER_REG) continue; // Skip external regions. regions[i].remap = true; } - + unsigned short regIdGen = 0; for (int i = 0; i < nreg; ++i) { @@ -848,91 +921,88 @@ static bool filterSmallRegions(int minRegionSize, int mergeRegionSize, } } maxRegionId = regIdGen; - + // Remap regions. for (int i = 0; i < chf.spanCount; ++i) { - if ((src[i*2] & RC_BORDER_REG) == 0) - src[i*2] = regions[src[i*2]].id; + if ((srcReg[i] & RC_BORDER_REG) == 0) + srcReg[i] = regions[srcReg[i]].id; } - delete [] regions; + for (int i = 0; i < nreg; ++i) + regions[i].~rcRegion(); + rcFree(regions); return true; } -bool rcBuildDistanceField(rcCompactHeightfield& chf) +/// @par +/// +/// This is usually the second to the last step in creating a fully built +/// compact heightfield. This step is required before regions are built +/// using #rcBuildRegions or #rcBuildRegionsMonotone. +/// +/// After this step, the distance data is available via the rcCompactHeightfield::maxDistance +/// and rcCompactHeightfield::dist fields. +/// +/// @see rcCompactHeightfield, rcBuildRegions, rcBuildRegionsMonotone +bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf) { - rcTimeVal startTime = rcGetPerformanceTimer(); + rcAssert(ctx); + + ctx->startTimer(RC_TIMER_BUILD_DISTANCEFIELD); + + if (chf.dist) + { + rcFree(chf.dist); + chf.dist = 0; + } - unsigned short* dist0 = new unsigned short[chf.spanCount]; - if (!dist0) + unsigned short* src = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP); + if (!src) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'dist0' (%d).", chf.spanCount); + ctx->log(RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'src' (%d).", chf.spanCount); return false; } - unsigned short* dist1 = new unsigned short[chf.spanCount]; - if (!dist1) + unsigned short* dst = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP); + if (!dst) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'dist1' (%d).", chf.spanCount); - delete [] dist0; + ctx->log(RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'dst' (%d).", chf.spanCount); + rcFree(src); return false; } - unsigned short* src = dist0; - unsigned short* dst = dist1; - unsigned short maxDist = 0; - rcTimeVal distStartTime = rcGetPerformanceTimer(); - - if (calculateDistanceField(chf, src, dst, maxDist) != src) - rcSwap(src, dst); + ctx->startTimer(RC_TIMER_BUILD_DISTANCEFIELD_DIST); + calculateDistanceField(chf, src, maxDist); chf.maxDistance = maxDist; - rcTimeVal distEndTime = rcGetPerformanceTimer(); + ctx->stopTimer(RC_TIMER_BUILD_DISTANCEFIELD_DIST); - rcTimeVal blurStartTime = rcGetPerformanceTimer(); + ctx->startTimer(RC_TIMER_BUILD_DISTANCEFIELD_BLUR); // Blur if (boxBlur(chf, 1, src, dst) != src) rcSwap(src, dst); // Store distance. - for (int i = 0; i < chf.spanCount; ++i) - chf.spans[i].dist = src[i]; - - rcTimeVal blurEndTime = rcGetPerformanceTimer(); + chf.dist = src; - delete [] dist0; - delete [] dist1; - - rcTimeVal endTime = rcGetPerformanceTimer(); + ctx->stopTimer(RC_TIMER_BUILD_DISTANCEFIELD_BLUR); + + ctx->stopTimer(RC_TIMER_BUILD_DISTANCEFIELD); -/* if (rcGetLog()) - { - rcGetLog()->log(RC_LOG_PROGRESS, "Build distance field: %.3f ms", rcGetDeltaTimeUsec(startTime, endTime)/1000.0f); - rcGetLog()->log(RC_LOG_PROGRESS, " - dist: %.3f ms", rcGetDeltaTimeUsec(distStartTime, distEndTime)/1000.0f); - rcGetLog()->log(RC_LOG_PROGRESS, " - blur: %.3f ms", rcGetDeltaTimeUsec(blurStartTime, blurEndTime)/1000.0f); - }*/ - if (rcGetBuildTimes()) - { - rcGetBuildTimes()->buildDistanceField += rcGetDeltaTimeUsec(startTime, endTime); - rcGetBuildTimes()->buildDistanceFieldDist += rcGetDeltaTimeUsec(distStartTime, distEndTime); - rcGetBuildTimes()->buildDistanceFieldBlur += rcGetDeltaTimeUsec(blurStartTime, blurEndTime); - } + rcFree(dst); return true; } -static void paintRectRegion(int minx, int maxx, int miny, int maxy, - unsigned short regId, unsigned short minLevel, - rcCompactHeightfield& chf, unsigned short* src) +static void paintRectRegion(int minx, int maxx, int miny, int maxy, unsigned short regId, + rcCompactHeightfield& chf, unsigned short* srcReg) { - const int w = chf.width; + const int w = chf.width; for (int y = miny; y < maxy; ++y) { for (int x = minx; x < maxx; ++x) @@ -940,77 +1010,283 @@ static void paintRectRegion(int minx, int maxx, int miny, int maxy, const rcCompactCell& c = chf.cells[x+y*w]; for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) { - if (chf.spans[i].dist >= minLevel) - src[i*2] = regId; + if (chf.areas[i] != RC_NULL_AREA) + srcReg[i] = regId; } } } } -bool rcBuildRegions(rcCompactHeightfield& chf, - int walkableRadius, int borderSize, - int minRegionSize, int mergeRegionSize) + +static const unsigned short RC_NULL_NEI = 0xffff; + +struct rcSweepSpan +{ + unsigned short rid; // row id + unsigned short id; // region id + unsigned short ns; // number samples + unsigned short nei; // neighbour id +}; + +/// @par +/// +/// Non-null regions will consist of connected, non-overlapping walkable spans that form a single contour. +/// Contours will form simple polygons. +/// +/// If multiple regions form an area that is smaller than @p minRegionArea, then all spans will be +/// re-assigned to the zero (null) region. +/// +/// Partitioning can result in smaller than necessary regions. @p mergeRegionArea helps +/// reduce unecessarily small regions. +/// +/// See the #rcConfig documentation for more information on the configuration parameters. +/// +/// The region data will be available via the rcCompactHeightfield::maxRegions +/// and rcCompactSpan::reg fields. +/// +/// @warning The distance field must be created using #rcBuildDistanceField before attempting to build regions. +/// +/// @see rcCompactHeightfield, rcCompactSpan, rcBuildDistanceField, rcBuildRegionsMonotone, rcConfig +bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf, + const int borderSize, const int minRegionArea, const int mergeRegionArea) { - rcTimeVal startTime = rcGetPerformanceTimer(); + rcAssert(ctx); + + ctx->startTimer(RC_TIMER_BUILD_REGIONS); const int w = chf.width; const int h = chf.height; + unsigned short id = 1; - unsigned short* tmp1 = new unsigned short[chf.spanCount*2]; - if (!tmp1) + rcScopedDelete<unsigned short> srcReg = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP); + if (!srcReg) + { + ctx->log(RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'src' (%d).", chf.spanCount); + return false; + } + memset(srcReg,0,sizeof(unsigned short)*chf.spanCount); + + const int nsweeps = rcMax(chf.width,chf.height); + rcScopedDelete<rcSweepSpan> sweeps = (rcSweepSpan*)rcAlloc(sizeof(rcSweepSpan)*nsweeps, RC_ALLOC_TEMP); + if (!sweeps) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'tmp1' (%d).", chf.spanCount*2); + ctx->log(RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'sweeps' (%d).", nsweeps); return false; } - unsigned short* tmp2 = new unsigned short[chf.spanCount*2]; - if (!tmp2) + + + // Mark border regions. + if (borderSize > 0) + { + // Make sure border will not overflow. + const int bw = rcMin(w, borderSize); + const int bh = rcMin(h, borderSize); + // Paint regions + paintRectRegion(0, bw, 0, h, id|RC_BORDER_REG, chf, srcReg); id++; + paintRectRegion(w-bw, w, 0, h, id|RC_BORDER_REG, chf, srcReg); id++; + paintRectRegion(0, w, 0, bh, id|RC_BORDER_REG, chf, srcReg); id++; + paintRectRegion(0, w, h-bh, h, id|RC_BORDER_REG, chf, srcReg); id++; + + chf.borderSize = borderSize; + } + + rcIntArray prev(256); + + // Sweep one line at a time. + for (int y = borderSize; y < h-borderSize; ++y) { - if (rcGetLog()) - rcGetLog()->log(RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'tmp2' (%d).", chf.spanCount*2); - delete [] tmp1; + // Collect spans from this row. + prev.resize(id+1); + memset(&prev[0],0,sizeof(int)*id); + unsigned short rid = 1; + + for (int x = borderSize; x < w-borderSize; ++x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + const rcCompactSpan& s = chf.spans[i]; + if (chf.areas[i] == RC_NULL_AREA) continue; + + // -x + unsigned short previd = 0; + if (rcGetCon(s, 0) != RC_NOT_CONNECTED) + { + const int ax = x + rcGetDirOffsetX(0); + const int ay = y + rcGetDirOffsetY(0); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0); + if ((srcReg[ai] & RC_BORDER_REG) == 0 && chf.areas[i] == chf.areas[ai]) + previd = srcReg[ai]; + } + + if (!previd) + { + previd = rid++; + sweeps[previd].rid = previd; + sweeps[previd].ns = 0; + sweeps[previd].nei = 0; + } + + // -y + if (rcGetCon(s,3) != RC_NOT_CONNECTED) + { + const int ax = x + rcGetDirOffsetX(3); + const int ay = y + rcGetDirOffsetY(3); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3); + if (srcReg[ai] && (srcReg[ai] & RC_BORDER_REG) == 0 && chf.areas[i] == chf.areas[ai]) + { + unsigned short nr = srcReg[ai]; + if (!sweeps[previd].nei || sweeps[previd].nei == nr) + { + sweeps[previd].nei = nr; + sweeps[previd].ns++; + prev[nr]++; + } + else + { + sweeps[previd].nei = RC_NULL_NEI; + } + } + } + + srcReg[i] = previd; + } + } + + // Create unique ID. + for (int i = 1; i < rid; ++i) + { + if (sweeps[i].nei != RC_NULL_NEI && sweeps[i].nei != 0 && + prev[sweeps[i].nei] == (int)sweeps[i].ns) + { + sweeps[i].id = sweeps[i].nei; + } + else + { + sweeps[i].id = id++; + } + } + + // Remap IDs + for (int x = borderSize; x < w-borderSize; ++x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + if (srcReg[i] > 0 && srcReg[i] < rid) + srcReg[i] = sweeps[srcReg[i]].id; + } + } + } + + ctx->startTimer(RC_TIMER_BUILD_REGIONS_FILTER); + + // Filter out small regions. + chf.maxRegions = id; + if (!filterSmallRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg)) + return false; + + ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER); + + // Store the result out. + for (int i = 0; i < chf.spanCount; ++i) + chf.spans[i].reg = srcReg[i]; + + ctx->stopTimer(RC_TIMER_BUILD_REGIONS); + + return true; +} + +/// @par +/// +/// Non-null regions will consist of connected, non-overlapping walkable spans that form a single contour. +/// Contours will form simple polygons. +/// +/// If multiple regions form an area that is smaller than @p minRegionArea, then all spans will be +/// re-assigned to the zero (null) region. +/// +/// Watershed partitioning can result in smaller than necessary regions, especially in diagonal corridors. +/// @p mergeRegionArea helps reduce unecessarily small regions. +/// +/// See the #rcConfig documentation for more information on the configuration parameters. +/// +/// The region data will be available via the rcCompactHeightfield::maxRegions +/// and rcCompactSpan::reg fields. +/// +/// @warning The distance field must be created using #rcBuildDistanceField before attempting to build regions. +/// +/// @see rcCompactHeightfield, rcCompactSpan, rcBuildDistanceField, rcBuildRegionsMonotone, rcConfig +bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, + const int borderSize, const int minRegionArea, const int mergeRegionArea) +{ + rcAssert(ctx); + + ctx->startTimer(RC_TIMER_BUILD_REGIONS); + + const int w = chf.width; + const int h = chf.height; + + rcScopedDelete<unsigned short> buf = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount*4, RC_ALLOC_TEMP); + if (!buf) + { + ctx->log(RC_LOG_ERROR, "rcBuildRegions: Out of memory 'tmp' (%d).", chf.spanCount*4); return false; } - rcTimeVal regStartTime = rcGetPerformanceTimer(); + ctx->startTimer(RC_TIMER_BUILD_REGIONS_WATERSHED); rcIntArray stack(1024); rcIntArray visited(1024); - unsigned short* src = tmp1; - unsigned short* dst = tmp2; + unsigned short* srcReg = buf; + unsigned short* srcDist = buf+chf.spanCount; + unsigned short* dstReg = buf+chf.spanCount*2; + unsigned short* dstDist = buf+chf.spanCount*3; - memset(src, 0, sizeof(unsigned short) * chf.spanCount*2); + memset(srcReg, 0, sizeof(unsigned short)*chf.spanCount); + memset(srcDist, 0, sizeof(unsigned short)*chf.spanCount); unsigned short regionId = 1; unsigned short level = (chf.maxDistance+1) & ~1; - - unsigned short minLevel = (unsigned short)(walkableRadius*2); - - const int expandIters = 4 + walkableRadius * 2; - // Mark border regions. - paintRectRegion(0, borderSize, 0, h, regionId|RC_BORDER_REG, minLevel, chf, src); regionId++; - paintRectRegion(w-borderSize, w, 0, h, regionId|RC_BORDER_REG, minLevel, chf, src); regionId++; - paintRectRegion(0, w, 0, borderSize, regionId|RC_BORDER_REG, minLevel, chf, src); regionId++; - paintRectRegion(0, w, h-borderSize, h, regionId|RC_BORDER_REG, minLevel, chf, src); regionId++; + // TODO: Figure better formula, expandIters defines how much the + // watershed "overflows" and simplifies the regions. Tying it to + // agent radius was usually good indication how greedy it could be. +// const int expandIters = 4 + walkableRadius * 2; + const int expandIters = 8; + + if (borderSize > 0) + { + // Make sure border will not overflow. + const int bw = rcMin(w, borderSize); + const int bh = rcMin(h, borderSize); + // Paint regions + paintRectRegion(0, bw, 0, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++; + paintRectRegion(w-bw, w, 0, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++; + paintRectRegion(0, w, 0, bh, regionId|RC_BORDER_REG, chf, srcReg); regionId++; + paintRectRegion(0, w, h-bh, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++; - rcTimeVal expTime = 0; - rcTimeVal floodTime = 0; + chf.borderSize = borderSize; + } - while (level > minLevel) + while (level > 0) { level = level >= 2 ? level-2 : 0; - rcTimeVal expStartTime = rcGetPerformanceTimer(); + ctx->startTimer(RC_TIMER_BUILD_REGIONS_EXPAND); // Expand current regions until no empty connected cells found. - if (expandRegions(expandIters, level, chf, src, dst, stack) != src) - rcSwap(src, dst); + if (expandRegions(expandIters, level, chf, srcReg, srcDist, dstReg, dstDist, stack) != srcReg) + { + rcSwap(srcReg, dstReg); + rcSwap(srcDist, dstDist); + } - expTime += rcGetPerformanceTimer() - expStartTime; + ctx->stopTimer(RC_TIMER_BUILD_REGIONS_EXPAND); - rcTimeVal floodStartTime = rcGetPerformanceTimer(); + ctx->startTimer(RC_TIMER_BUILD_REGIONS_FLOOD); // Mark new regions with IDs. for (int y = 0; y < h; ++y) @@ -1020,61 +1296,41 @@ bool rcBuildRegions(rcCompactHeightfield& chf, const rcCompactCell& c = chf.cells[x+y*w]; for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) { - if (chf.spans[i].dist < level || src[i*2] != 0) + if (chf.dist[i] < level || srcReg[i] != 0 || chf.areas[i] == RC_NULL_AREA) continue; - - if (floodRegion(x, y, i, minLevel, level, regionId, chf, src, stack)) + if (floodRegion(x, y, i, level, regionId, chf, srcReg, srcDist, stack)) regionId++; } } } - floodTime += rcGetPerformanceTimer() - floodStartTime; - + ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FLOOD); } // Expand current regions until no empty connected cells found. - if (expandRegions(expandIters*8, minLevel, chf, src, dst, stack) != src) - rcSwap(src, dst); + if (expandRegions(expandIters*8, 0, chf, srcReg, srcDist, dstReg, dstDist, stack) != srcReg) + { + rcSwap(srcReg, dstReg); + rcSwap(srcDist, dstDist); + } - rcTimeVal regEndTime = rcGetPerformanceTimer(); + ctx->stopTimer(RC_TIMER_BUILD_REGIONS_WATERSHED); - rcTimeVal filterStartTime = rcGetPerformanceTimer(); + ctx->startTimer(RC_TIMER_BUILD_REGIONS_FILTER); // Filter out small regions. chf.maxRegions = regionId; - if (!filterSmallRegions(minRegionSize, mergeRegionSize, chf.maxRegions, chf, src)) + if (!filterSmallRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg)) return false; - rcTimeVal filterEndTime = rcGetPerformanceTimer(); - + ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER); + // Write the result out. for (int i = 0; i < chf.spanCount; ++i) - chf.spans[i].reg = src[i*2]; - - delete [] tmp1; - delete [] tmp2; + chf.spans[i].reg = srcReg[i]; - rcTimeVal endTime = rcGetPerformanceTimer(); + ctx->stopTimer(RC_TIMER_BUILD_REGIONS); -/* if (rcGetLog()) - { - rcGetLog()->log(RC_LOG_PROGRESS, "Build regions: %.3f ms", rcGetDeltaTimeUsec(startTime, endTime)/1000.0f); - rcGetLog()->log(RC_LOG_PROGRESS, " - reg: %.3f ms", rcGetDeltaTimeUsec(regStartTime, regEndTime)/1000.0f); - rcGetLog()->log(RC_LOG_PROGRESS, " - exp: %.3f ms", rcGetDeltaTimeUsec(0, expTime)/1000.0f); - rcGetLog()->log(RC_LOG_PROGRESS, " - flood: %.3f ms", rcGetDeltaTimeUsec(0, floodTime)/1000.0f); - rcGetLog()->log(RC_LOG_PROGRESS, " - filter: %.3f ms", rcGetDeltaTimeUsec(filterStartTime, filterEndTime)/1000.0f); - } -*/ - if (rcGetBuildTimes()) - { - rcGetBuildTimes()->buildRegions += rcGetDeltaTimeUsec(startTime, endTime); - rcGetBuildTimes()->buildRegionsReg += rcGetDeltaTimeUsec(regStartTime, regEndTime); - rcGetBuildTimes()->buildRegionsExp += rcGetDeltaTimeUsec(0, expTime); - rcGetBuildTimes()->buildRegionsFlood += rcGetDeltaTimeUsec(0, floodTime); - rcGetBuildTimes()->buildRegionsFilter += rcGetDeltaTimeUsec(filterStartTime, filterEndTime); - } - return true; } diff --git a/extern/recastnavigation/recast-capi.cpp b/extern/recastnavigation/recast-capi.cpp index 2348497b0d7..42a9a4b82c8 100644 --- a/extern/recastnavigation/recast-capi.cpp +++ b/extern/recastnavigation/recast-capi.cpp @@ -30,6 +30,11 @@ #include <math.h> #include "Recast.h" +static rcContext *sctx; + +#define INIT_SCTX() \ + if (sctx == NULL) sctx = new rcContext(false) + int recast_buildMeshAdjacency(unsigned short* polys, const int npolys, const int nverts, const int vertsPerPoly) { @@ -48,109 +53,123 @@ void recast_calcGridSize(const float *bmin, const float *bmax, float cs, int *w, struct recast_heightfield *recast_newHeightfield(void) { - return (struct recast_heightfield *) (new rcHeightfield); + return (struct recast_heightfield *) rcAllocHeightfield(); } void recast_destroyHeightfield(struct recast_heightfield *heightfield) { - delete (rcHeightfield *) heightfield; + rcFreeHeightField((rcHeightfield *) heightfield); } int recast_createHeightfield(struct recast_heightfield *hf, int width, int height, const float *bmin, const float* bmax, float cs, float ch) { - return rcCreateHeightfield(*(rcHeightfield *)hf, width, height, bmin, bmax, cs, ch); + INIT_SCTX(); + return rcCreateHeightfield(sctx, *(rcHeightfield *)hf, width, height, bmin, bmax, cs, ch); } void recast_markWalkableTriangles(const float walkableSlopeAngle,const float *verts, int nv, const int *tris, int nt, unsigned char *flags) { - rcMarkWalkableTriangles(walkableSlopeAngle, verts, nv, tris, nt, flags); + INIT_SCTX(); + rcMarkWalkableTriangles(sctx, walkableSlopeAngle, verts, nv, tris, nt, flags); } void recast_rasterizeTriangles(const float *verts, int nv, const int *tris, const unsigned char *flags, int nt, struct recast_heightfield *solid) { - rcRasterizeTriangles(verts, nv, tris, flags, nt, *(rcHeightfield *) solid); + INIT_SCTX(); + rcRasterizeTriangles(sctx, verts, nv, tris, flags, nt, *(rcHeightfield *) solid); } void recast_filterLedgeSpans(const int walkableHeight, const int walkableClimb, struct recast_heightfield *solid) { - rcFilterLedgeSpans(walkableHeight, walkableClimb, *(rcHeightfield *) solid); + INIT_SCTX(); + rcFilterLedgeSpans(sctx, walkableHeight, walkableClimb, *(rcHeightfield *) solid); } void recast_filterWalkableLowHeightSpans(int walkableHeight, struct recast_heightfield *solid) { - rcFilterWalkableLowHeightSpans(walkableHeight, *(rcHeightfield *) solid); + INIT_SCTX(); + rcFilterWalkableLowHeightSpans(sctx, walkableHeight, *(rcHeightfield *) solid); +} + +void recast_filterLowHangingWalkableObstacles(const int walkableClimb, struct recast_heightfield *solid) +{ + INIT_SCTX(); + rcFilterLowHangingWalkableObstacles(sctx, walkableClimb, *(rcHeightfield *) solid); } struct recast_compactHeightfield *recast_newCompactHeightfield(void) { - return (struct recast_compactHeightfield *) (new rcCompactHeightfield); + return (struct recast_compactHeightfield *) rcAllocCompactHeightfield(); } void recast_destroyCompactHeightfield(struct recast_compactHeightfield *compactHeightfield) { - delete (rcCompactHeightfield *) compactHeightfield; + rcFreeCompactHeightfield( (rcCompactHeightfield *) compactHeightfield); } int recast_buildCompactHeightfield(const int walkableHeight, const int walkableClimb, - unsigned char flags, struct recast_heightfield *hf, struct recast_compactHeightfield *chf) + struct recast_heightfield *hf, struct recast_compactHeightfield *chf) { - int rcFlags = 0; - - if(flags & RECAST_WALKABLE) - rcFlags |= RC_WALKABLE; - - if(flags & RECAST_REACHABLE) - rcFlags |= RC_REACHABLE; + INIT_SCTX(); + return rcBuildCompactHeightfield(sctx, walkableHeight, walkableClimb, + *(rcHeightfield *) hf, *(rcCompactHeightfield *) chf); +} - return rcBuildCompactHeightfield(walkableHeight, walkableClimb, rcFlags, - *(rcHeightfield *) hf, *(rcCompactHeightfield *) chf); +int recast_erodeWalkableArea(int radius, struct recast_compactHeightfield *chf) +{ + INIT_SCTX(); + return rcErodeWalkableArea(sctx, radius, *(rcCompactHeightfield *) chf); } int recast_buildDistanceField(struct recast_compactHeightfield *chf) { - return rcBuildDistanceField(*(rcCompactHeightfield *) chf); + INIT_SCTX(); + return rcBuildDistanceField(sctx, *(rcCompactHeightfield *) chf); } -int recast_buildRegions(struct recast_compactHeightfield *chf, int walkableRadius, int borderSize, +int recast_buildRegions(struct recast_compactHeightfield *chf, int borderSize, int minRegionSize, int mergeRegionSize) { - return rcBuildRegions(*(rcCompactHeightfield *) chf, walkableRadius, borderSize, + INIT_SCTX(); + return rcBuildRegions(sctx, *(rcCompactHeightfield *) chf, borderSize, minRegionSize, mergeRegionSize); } struct recast_contourSet *recast_newContourSet(void) { - return (struct recast_contourSet *) (new rcContourSet); + return (struct recast_contourSet *) rcAllocContourSet(); } void recast_destroyContourSet(struct recast_contourSet *contourSet) { - delete (rcContourSet *) contourSet; + rcFreeContourSet((rcContourSet *) contourSet); } int recast_buildContours(struct recast_compactHeightfield *chf, const float maxError, const int maxEdgeLen, struct recast_contourSet *cset) { - return rcBuildContours(*(rcCompactHeightfield *) chf, maxError, maxEdgeLen, *(rcContourSet *) cset); + INIT_SCTX(); + return rcBuildContours(sctx, *(rcCompactHeightfield *) chf, maxError, maxEdgeLen, *(rcContourSet *) cset); } struct recast_polyMesh *recast_newPolyMesh(void) { - return (recast_polyMesh *) (new rcPolyMesh); + return (recast_polyMesh *) rcAllocPolyMesh(); } void recast_destroyPolyMesh(struct recast_polyMesh *polyMesh) { - delete (rcPolyMesh *) polyMesh; + rcFreePolyMesh((rcPolyMesh *) polyMesh); } int recast_buildPolyMesh(struct recast_contourSet *cset, int nvp, struct recast_polyMesh *mesh) { - return rcBuildPolyMesh(*(rcContourSet *) cset, nvp, * (rcPolyMesh *) mesh); + INIT_SCTX(); + return rcBuildPolyMesh(sctx, *(rcContourSet *) cset, nvp, * (rcPolyMesh *) mesh); } unsigned short *recast_polyMeshGetVerts(struct recast_polyMesh *mesh, int *nverts) @@ -206,18 +225,19 @@ unsigned short *recast_polyMeshGetPolys(struct recast_polyMesh *mesh, int *npoly struct recast_polyMeshDetail *recast_newPolyMeshDetail(void) { - return (struct recast_polyMeshDetail *) (new rcPolyMeshDetail); + return (struct recast_polyMeshDetail *) rcAllocPolyMeshDetail(); } void recast_destroyPolyMeshDetail(struct recast_polyMeshDetail *polyMeshDetail) { - delete (rcPolyMeshDetail *) polyMeshDetail; + rcFreePolyMeshDetail((rcPolyMeshDetail *) polyMeshDetail); } int recast_buildPolyMeshDetail(const struct recast_polyMesh *mesh, const struct recast_compactHeightfield *chf, const float sampleDist, const float sampleMaxError, struct recast_polyMeshDetail *dmesh) { - return rcBuildPolyMeshDetail(*(rcPolyMesh *) mesh, *(rcCompactHeightfield *) chf, + INIT_SCTX(); + return rcBuildPolyMeshDetail(sctx, *(rcPolyMesh *) mesh, *(rcCompactHeightfield *) chf, sampleDist, sampleMaxError, *(rcPolyMeshDetail *) dmesh); } @@ -241,7 +261,7 @@ unsigned char *recast_polyMeshDetailGetTris(struct recast_polyMeshDetail *mesh, return dmesh->tris; } -unsigned short *recast_polyMeshDetailGetMeshes(struct recast_polyMeshDetail *mesh, int *nmeshes) +unsigned int *recast_polyMeshDetailGetMeshes(struct recast_polyMeshDetail *mesh, int *nmeshes) { rcPolyMeshDetail *dmesh = (rcPolyMeshDetail *)mesh; @@ -250,3 +270,130 @@ unsigned short *recast_polyMeshDetailGetMeshes(struct recast_polyMeshDetail *mes return dmesh->meshes; } + +// qsort based on FreeBSD source (libkern\qsort.c) +typedef int cmp_t(void *, const void *, const void *); +static inline char *med3(char *, char *, char *, cmp_t *, void *); +static inline void swapfunc(char *, char *, int, int); + +#define min(a, b) (a) < (b) ? a : b +#define swapcode(TYPE, parmi, parmj, n) \ +{ \ + long i = (n) / sizeof (TYPE); \ + TYPE *pi = (TYPE *) (parmi); \ + TYPE *pj = (TYPE *) (parmj); \ + do { \ + TYPE t = *pi; \ + *pi++ = *pj; \ + *pj++ = t; \ + } while (--i > 0); \ +} +#define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \ + es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1; + +static inline void swapfunc(char* a, char* b, int n, int swaptype) +{ + if(swaptype <= 1) + swapcode(long, a, b, n) + else + swapcode(char, a, b, n) +} + +#define swap(a, b) \ + if (swaptype == 0) { \ + long t = *(long *)(a); \ + *(long *)(a) = *(long *)(b);\ + *(long *)(b) = t; \ + } else \ + swapfunc(a, b, es, swaptype) + +#define vecswap(a, b, n) if ((n) > 0) swapfunc(a, b, n, swaptype) +#define CMP(t, x, y) (cmp((t), (x), (y))) + +static inline char * med3(char *a, char *b, char *c, cmp_t *cmp, void *thunk) +{ + return CMP(thunk, a, b) < 0 ? + (CMP(thunk, b, c) < 0 ? b : (CMP(thunk, a, c) < 0 ? c : a )) + :(CMP(thunk, b, c) > 0 ? b : (CMP(thunk, a, c) < 0 ? a : c )); +} + +void recast_qsort(void *a, size_t n, size_t es, void *thunk, cmp_t *cmp) +{ + char *pa, *pb, *pc, *pd, *pl, *pm, *pn; + int d, r, swaptype, swap_cnt; + +loop: + SWAPINIT(a, es); + swap_cnt = 0; + if (n < 7) { + for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es) + for (pl = pm; + pl > (char *)a && CMP(thunk, pl - es, pl) > 0; + pl -= es) + swap(pl, pl - es); + return; + } + pm = (char *)a + (n / 2) * es; + if (n > 7) { + pl = (char *)a; + pn = (char *)a + (n - 1) * es; + if (n > 40) { + d = (n / 8) * es; + pl = med3(pl, pl + d, pl + 2 * d, cmp, thunk); + pm = med3(pm - d, pm, pm + d, cmp, thunk); + pn = med3(pn - 2 * d, pn - d, pn, cmp, thunk); + } + pm = med3(pl, pm, pn, cmp, thunk); + } + swap((char *)a, pm); + pa = pb = (char *)a + es; + + pc = pd = (char *)a + (n - 1) * es; + for (;;) { + while (pb <= pc && (r = CMP(thunk, pb, a)) <= 0) { + if (r == 0) { + swap_cnt = 1; + swap(pa, pb); + pa += es; + } + pb += es; + } + while (pb <= pc && (r = CMP(thunk, pc, a)) >= 0) { + if (r == 0) { + swap_cnt = 1; + swap(pc, pd); + pd -= es; + } + pc -= es; + } + if (pb > pc) + break; + swap(pb, pc); + swap_cnt = 1; + pb += es; + pc -= es; + } + if (swap_cnt == 0) { /* Switch to insertion sort */ + for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es) + for (pl = pm; + pl > (char *)a && CMP(thunk, pl - es, pl) > 0; + pl -= es) + swap(pl, pl - es); + return; + } + + pn = (char *)a + n * es; + r = min(pa - (char *)a, pb - pa); + vecswap((char *)a, pb - r, r); + r = min(pd - pc, pn - pd - es); + vecswap(pb, pn - r, r); + if ((r = pb - pa) > es) + recast_qsort(a, r / es, es, thunk, cmp); + if ((r = pd - pc) > es) { + /* Iterate rather than recurse to save stack space */ + a = pn - r; + n = r / es; + goto loop; + } +} + diff --git a/extern/recastnavigation/recast-capi.h b/extern/recastnavigation/recast-capi.h index 58fe08e6335..e8831bcdc58 100644 --- a/extern/recastnavigation/recast-capi.h +++ b/extern/recastnavigation/recast-capi.h @@ -28,6 +28,9 @@ #ifndef RECAST_C_API_H #define RECAST_C_API_H +// for size_t +#include <stddef.h> + #ifdef __cplusplus extern "C" { #endif @@ -69,16 +72,20 @@ void recast_filterLedgeSpans(const int walkableHeight, const int walkableClimb, void recast_filterWalkableLowHeightSpans(int walkableHeight, struct recast_heightfield *solid); +void recast_filterLowHangingWalkableObstacles(const int walkableClimb, struct recast_heightfield *solid); + struct recast_compactHeightfield *recast_newCompactHeightfield(void); void recast_destroyCompactHeightfield(struct recast_compactHeightfield *compactHeightfield); int recast_buildCompactHeightfield(const int walkableHeight, const int walkableClimb, - unsigned char flags, struct recast_heightfield *hf, struct recast_compactHeightfield *chf); + struct recast_heightfield *hf, struct recast_compactHeightfield *chf); + +int recast_erodeWalkableArea(int radius, struct recast_compactHeightfield *chf); int recast_buildDistanceField(struct recast_compactHeightfield *chf); -int recast_buildRegions(struct recast_compactHeightfield *chf, int walkableRadius, int borderSize, +int recast_buildRegions(struct recast_compactHeightfield *chf, int borderSize, int minRegionSize, int mergeRegionSize); /* Contour set */ @@ -119,7 +126,12 @@ float *recast_polyMeshDetailGetVerts(struct recast_polyMeshDetail *mesh, int *nv unsigned char *recast_polyMeshDetailGetTris(struct recast_polyMeshDetail *mesh, int *ntris); -unsigned short *recast_polyMeshDetailGetMeshes(struct recast_polyMeshDetail *mesh, int *nmeshes); +unsigned int *recast_polyMeshDetailGetMeshes(struct recast_polyMeshDetail *mesh, int *nmeshes); + +/* utility function: quick sort reentrant */ +typedef int recast_cmp_t(void *ctx, const void *a, const void *b); + +void recast_qsort(void *a, size_t n, size_t es, void *thunk, recast_cmp_t *cmp); #ifdef __cplusplus } |