Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/extern
diff options
context:
space:
mode:
authorBenoit Bolsee <benoit.bolsee@online.be>2011-09-30 01:38:57 +0400
committerBenoit Bolsee <benoit.bolsee@online.be>2011-09-30 01:38:57 +0400
commite6a9b68c798457430698afbfc7dfcdd8b71ca596 (patch)
tree64cc1ff496d8b3df6a04dfac078905a312952d8e /extern
parente21e7895071eaa7484679639f39bb7f413bb4dcc (diff)
Recast: upgrade library.
- Upgrade Recast library to latest portable version - Implement recast_qsort based on FreeBSD qsort.c to have portable thread safe quick sort for use in conversion routine. - Better default value for the Build Navigation Mesh operator
Diffstat (limited to 'extern')
-rw-r--r--extern/recastnavigation/CMakeLists.txt11
-rw-r--r--extern/recastnavigation/Recast/Include/Recast.h1297
-rw-r--r--extern/recastnavigation/Recast/Include/RecastAlloc.h122
-rw-r--r--extern/recastnavigation/Recast/Include/RecastAssert.h33
-rw-r--r--extern/recastnavigation/Recast/Source/Recast.cpp335
-rw-r--r--extern/recastnavigation/Recast/Source/RecastAlloc.cpp88
-rw-r--r--extern/recastnavigation/Recast/Source/RecastArea.cpp524
-rw-r--r--extern/recastnavigation/Recast/Source/RecastContour.cpp399
-rw-r--r--extern/recastnavigation/Recast/Source/RecastFilter.cpp250
-rw-r--r--extern/recastnavigation/Recast/Source/RecastLayers.cpp620
-rw-r--r--extern/recastnavigation/Recast/Source/RecastMesh.cpp679
-rw-r--r--extern/recastnavigation/Recast/Source/RecastMeshDetail.cpp1068
-rw-r--r--extern/recastnavigation/Recast/Source/RecastRasterization.cpp167
-rw-r--r--extern/recastnavigation/Recast/Source/RecastRegion.cpp730
-rw-r--r--extern/recastnavigation/recast-capi.cpp211
-rw-r--r--extern/recastnavigation/recast-capi.h18
16 files changed, 4884 insertions, 1668 deletions
diff --git a/extern/recastnavigation/CMakeLists.txt b/extern/recastnavigation/CMakeLists.txt
index 660b881dd07..7477044c2b3 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/RecastContour.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(&regions[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
}