Browse Source

Update Recast & Detour lib.

1vanK 7 years ago
parent
commit
cc25bef60e
55 changed files with 3644 additions and 1410 deletions
  1. 1 1
      Docs/Urho3D.dox
  2. 1 1
      README.md
  3. 3 3
      Source/ThirdParty/Detour/CMakeLists.txt
  4. 4 2
      Source/ThirdParty/Detour/Include/DetourAlloc.h
  5. 56 0
      Source/ThirdParty/Detour/Include/DetourAssert.h
  6. 24 9
      Source/ThirdParty/Detour/Include/DetourCommon.h
  7. 20 0
      Source/ThirdParty/Detour/Include/DetourMath.h
  8. 27 3
      Source/ThirdParty/Detour/Include/DetourNavMesh.h
  9. 2 1
      Source/ThirdParty/Detour/Include/DetourNavMeshBuilder.h
  10. 103 12
      Source/ThirdParty/Detour/Include/DetourNavMeshQuery.h
  11. 31 22
      Source/ThirdParty/Detour/Include/DetourNode.h
  12. 1 0
      Source/ThirdParty/Detour/Include/DetourStatus.h
  13. 2 2
      Source/ThirdParty/Detour/Source/DetourAlloc.cpp
  14. 14 12
      Source/ThirdParty/Detour/Source/DetourAssert.cpp
  15. 3 8
      Source/ThirdParty/Detour/Source/DetourCommon.cpp
  16. 46 41
      Source/ThirdParty/Detour/Source/DetourNavMesh.cpp
  17. 77 50
      Source/ThirdParty/Detour/Source/DetourNavMeshBuilder.cpp
  18. 490 191
      Source/ThirdParty/Detour/Source/DetourNavMeshQuery.cpp
  19. 26 4
      Source/ThirdParty/Detour/Source/DetourNode.cpp
  20. 0 21
      Source/ThirdParty/Detour/include/DetourMath.h
  21. 3 3
      Source/ThirdParty/DetourCrowd/CMakeLists.txt
  22. 21 11
      Source/ThirdParty/DetourCrowd/Include/DetourCrowd.h
  23. 6 1
      Source/ThirdParty/DetourCrowd/Include/DetourLocalBoundary.h
  24. 8 3
      Source/ThirdParty/DetourCrowd/Include/DetourObstacleAvoidance.h
  25. 6 1
      Source/ThirdParty/DetourCrowd/Include/DetourPathCorridor.h
  26. 4 0
      Source/ThirdParty/DetourCrowd/Include/DetourPathQueue.h
  27. 6 2
      Source/ThirdParty/DetourCrowd/Include/DetourProximityGrid.h
  28. 15 14
      Source/ThirdParty/DetourCrowd/Source/DetourCrowd.cpp
  29. 1 1
      Source/ThirdParty/DetourCrowd/Source/DetourLocalBoundary.cpp
  30. 96 24
      Source/ThirdParty/DetourCrowd/Source/DetourObstacleAvoidance.cpp
  31. 2 2
      Source/ThirdParty/DetourCrowd/Source/DetourPathCorridor.cpp
  32. 0 0
      Source/ThirdParty/DetourCrowd/Source/DetourPathQueue.cpp
  33. 3 5
      Source/ThirdParty/DetourCrowd/Source/DetourProximityGrid.cpp
  34. 3 3
      Source/ThirdParty/DetourTileCache/CMakeLists.txt
  35. 57 7
      Source/ThirdParty/DetourTileCache/Include/DetourTileCache.h
  36. 9 5
      Source/ThirdParty/DetourTileCache/Include/DetourTileCacheBuilder.h
  37. 143 28
      Source/ThirdParty/DetourTileCache/Source/DetourTileCache.cpp
  38. 102 3
      Source/ThirdParty/DetourTileCache/Source/DetourTileCacheBuilder.cpp
  39. 3 3
      Source/ThirdParty/Recast/CMakeLists.txt
  40. 91 18
      Source/ThirdParty/Recast/Include/Recast.h
  41. 341 0
      Source/ThirdParty/Recast/Include/RecastAlloc.h
  42. 56 0
      Source/ThirdParty/Recast/Include/RecastAssert.h
  43. 146 60
      Source/ThirdParty/Recast/Source/Recast.cpp
  44. 3 31
      Source/ThirdParty/Recast/Source/RecastAlloc.cpp
  45. 5 16
      Source/ThirdParty/Recast/Source/RecastArea.cpp
  46. 14 12
      Source/ThirdParty/Recast/Source/RecastAssert.cpp
  47. 398 144
      Source/ThirdParty/Recast/Source/RecastContour.cpp
  48. 7 12
      Source/ThirdParty/Recast/Source/RecastFilter.cpp
  49. 59 35
      Source/ThirdParty/Recast/Source/RecastLayers.cpp
  50. 124 41
      Source/ThirdParty/Recast/Source/RecastMesh.cpp
  51. 388 241
      Source/ThirdParty/Recast/Source/RecastMeshDetail.cpp
  52. 58 28
      Source/ThirdParty/Recast/Source/RecastRasterization.cpp
  53. 534 148
      Source/ThirdParty/Recast/Source/RecastRegion.cpp
  54. 0 124
      Source/ThirdParty/Recast/include/RecastAlloc.h
  55. 1 1
      Source/Urho3D/Navigation/DynamicNavigationMesh.cpp

+ 1 - 1
Docs/Urho3D.dox

@@ -236,7 +236,7 @@ Urho3D uses the following third-party libraries:
 - Open Asset Import Library 4.1.0 (http://assimp.sourceforge.net)
 - pugixml 1.7 (http://pugixml.org)
 - rapidjson 1.1.0 (https://github.com/miloyip/rapidjson)
-- Recast/Detour (https://github.com/memononen/recastnavigation)
+- Recast/Detour (https://github.com/recastnavigation/recastnavigation)
 - SDL 2.0.7 (https://www.libsdl.org)
 - SQLite 3.20.1 (https://www.sqlite.org)
 - StanHull (https://codesuppository.blogspot.com/2006/03/john-ratcliffs-code-suppository-blog.html)

+ 1 - 1
README.md

@@ -200,7 +200,7 @@ Urho3D uses the following third-party libraries:
 - Open Asset Import Library 4.1.0 (http://assimp.sourceforge.net)
 - pugixml 1.7 (http://pugixml.org)
 - rapidjson 1.1.0 (https://github.com/miloyip/rapidjson)
-- Recast/Detour (https://github.com/memononen/recastnavigation)
+- Recast/Detour (https://github.com/recastnavigation/recastnavigation)
 - SDL 2.0.7 (https://www.libsdl.org)
 - SQLite 3.20.1 (https://www.sqlite.org)
 - StanHull (https://codesuppository.blogspot.com/2006/03/john-ratcliffs-code-suppository-blog.html)

+ 3 - 3
Source/ThirdParty/Detour/CMakeLists.txt

@@ -24,13 +24,13 @@
 set (TARGET_NAME Detour)
 
 # Define source files
-define_source_files (GLOB_CPP_PATTERNS source/*.cpp GLOB_H_PATTERNS include/*.h)
+define_source_files (GLOB_CPP_PATTERNS Source/*.cpp GLOB_H_PATTERNS Include/*.h)
 
 # Define dependency libs
-set (INCLUDE_DIRS include)
+set (INCLUDE_DIRS Include)
 
 # Setup target
 setup_library ()
 
 # Install headers for building the Urho3D library
-install_header_files (DIRECTORY include/ DESTINATION ${DEST_INCLUDE_DIR}/ThirdParty/Detour FILES_MATCHING PATTERN *.h BUILD_TREE_ONLY)  # Note: the trailing slash is significant
+install_header_files (DIRECTORY Include/ DESTINATION ${DEST_INCLUDE_DIR}/ThirdParty/Detour FILES_MATCHING PATTERN *.h BUILD_TREE_ONLY)  # Note: the trailing slash is significant

+ 4 - 2
Source/ThirdParty/Detour/include/DetourAlloc.h → Source/ThirdParty/Detour/Include/DetourAlloc.h

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

+ 56 - 0
Source/ThirdParty/Detour/Include/DetourAssert.h

@@ -0,0 +1,56 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen [email protected]
+//
+// 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 DETOURASSERT_H
+#define DETOURASSERT_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 dtAssert(x) do { (void)sizeof(x); } while((void)(__LINE__==-1),false)  
+
+#else
+
+/// An assertion failure function.
+//  @param[in]		expression  asserted expression.
+//  @param[in]		file  Filename of the failed assertion.
+//  @param[in]		line  Line number of the failed assertion.
+///  @see dtAssertFailSetCustom
+typedef void (dtAssertFailFunc)(const char* expression, const char* file, int line);
+
+/// Sets the base custom assertion failure function to be used by Detour.
+///  @param[in]		assertFailFunc	The function to be invoked in case of failure of #dtAssert
+void dtAssertFailSetCustom(dtAssertFailFunc *assertFailFunc);
+
+/// Gets the base custom assertion failure function to be used by Detour.
+dtAssertFailFunc* dtAssertFailGetCustom();
+
+#	include <assert.h> 
+#	define dtAssert(expression) \
+		{ \
+			dtAssertFailFunc* failFunc = dtAssertFailGetCustom(); \
+			if(failFunc == NULL) { assert(expression); } \
+			else if(!(expression)) { (*failFunc)(#expression, __FILE__, __LINE__); } \
+		}
+
+#endif
+
+#endif // DETOURASSERT_H

+ 24 - 9
Source/ThirdParty/Detour/include/DetourCommon.h → Source/ThirdParty/Detour/Include/DetourCommon.h

@@ -19,6 +19,9 @@
 #ifndef DETOURCOMMON_H
 #define DETOURCOMMON_H
 
+#include "DetourMath.h"
+#include <stddef.h>
+
 /**
 @defgroup detour Detour
 
@@ -71,11 +74,6 @@ template<class T> inline T dtSqr(T a) { return a*a; }
 ///  @return The value, clamped to the specified range.
 template<class T> inline T dtClamp(T v, T mn, T mx) { return v < mn ? mn : (v > mx ? mx : v); }
 
-/// Returns the square root of the value.
-///  @param[in]		x	The value.
-///  @return The square root of the vlaue.
-float dtSqrt(float x);
-
 /// @}
 /// @name Vector helper functions.
 /// @{
@@ -202,7 +200,7 @@ inline void dtVcopy(float* dest, const float* a)
 /// @return The scalar length of the vector.
 inline float dtVlen(const float* v)
 {
-	return dtSqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
+	return dtMathSqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
 }
 
 /// Derives the square of the scalar length of the vector. (len * len)
@@ -222,7 +220,7 @@ inline float dtVdist(const float* v1, const float* v2)
 	const float dx = v2[0] - v1[0];
 	const float dy = v2[1] - v1[1];
 	const float dz = v2[2] - v1[2];
-	return dtSqrt(dx*dx + dy*dy + dz*dz);
+	return dtMathSqrtf(dx*dx + dy*dy + dz*dz);
 }
 
 /// Returns the square of the distance between two points.
@@ -247,7 +245,7 @@ inline float dtVdist2D(const float* v1, const float* v2)
 {
 	const float dx = v2[0] - v1[0];
 	const float dz = v2[2] - v1[2];
-	return dtSqrt(dx*dx + dz*dz);
+	return dtMathSqrtf(dx*dx + dz*dz);
 }
 
 /// Derives the square of the distance between the specified points on the xz-plane.
@@ -265,7 +263,7 @@ inline float dtVdist2DSqr(const float* v1, const float* v2)
 ///  @param[in,out]	v	The vector to normalize. [(x, y, z)]
 inline void dtVnormalize(float* v)
 {
-	float d = 1.0f / dtSqrt(dtSqr(v[0]) + dtSqr(v[1]) + dtSqr(v[2]));
+	float d = 1.0f / dtMathSqrtf(dtSqr(v[0]) + dtSqr(v[1]) + dtSqr(v[2]));
 	v[0] *= d;
 	v[1] *= d;
 	v[2] *= d;
@@ -485,6 +483,23 @@ inline void dtSwapEndian(float* v)
 void dtRandomPointInConvexPoly(const float* pts, const int npts, float* areas,
 							   const float s, const float t, float* out);
 
+template<typename TypeToRetrieveAs>
+TypeToRetrieveAs* dtGetThenAdvanceBufferPointer(const unsigned char*& buffer, const size_t distanceToAdvance)
+{
+	TypeToRetrieveAs* returnPointer = reinterpret_cast<TypeToRetrieveAs*>(buffer);
+	buffer += distanceToAdvance;
+	return returnPointer;
+}
+
+template<typename TypeToRetrieveAs>
+TypeToRetrieveAs* dtGetThenAdvanceBufferPointer(unsigned char*& buffer, const size_t distanceToAdvance)
+{
+	TypeToRetrieveAs* returnPointer = reinterpret_cast<TypeToRetrieveAs*>(buffer);
+	buffer += distanceToAdvance;
+	return returnPointer;
+}
+
+
 /// @}
 
 #endif // DETOURCOMMON_H

+ 20 - 0
Source/ThirdParty/Detour/Include/DetourMath.h

@@ -0,0 +1,20 @@
+/**
+@defgroup detour Detour
+
+Members in this module are wrappers around the standard math library
+*/
+
+#ifndef DETOURMATH_H
+#define DETOURMATH_H
+
+#include <math.h>
+
+inline float dtMathFabsf(float x) { return fabsf(x); }
+inline float dtMathSqrtf(float x) { return sqrtf(x); }
+inline float dtMathFloorf(float x) { return floorf(x); }
+inline float dtMathCeilf(float x) { return ceilf(x); }
+inline float dtMathCosf(float x) { return cosf(x); }
+inline float dtMathSinf(float x) { return sinf(x); }
+inline float dtMathAtan2f(float y, float x) { return atan2f(y, x); }
+
+#endif

+ 27 - 3
Source/ThirdParty/Detour/include/DetourNavMesh.h → Source/ThirdParty/Detour/Include/DetourNavMesh.h

@@ -117,6 +117,24 @@ enum dtStraightPathOptions
 	DT_STRAIGHTPATH_ALL_CROSSINGS = 0x02,	///< Add a vertex at every polygon edge crossing.
 };
 
+
+/// Options for dtNavMeshQuery::initSlicedFindPath and updateSlicedFindPath
+enum dtFindPathOptions
+{
+	DT_FINDPATH_ANY_ANGLE	= 0x02,		///< use raycasts during pathfind to "shortcut" (raycast still consider costs)
+};
+
+/// Options for dtNavMeshQuery::raycast
+enum dtRaycastOptions
+{
+	DT_RAYCAST_USE_COSTS = 0x01,		///< Raycast should calculate movement cost along the ray and fill RaycastHit::cost
+};
+
+
+/// Limit raycasting during any angle pahfinding
+/// The limit is given as a multiple of the character radius
+static const float DT_RAY_CAST_LIMIT_PROPORTIONS = 50.0f;
+
 /// Flags representing the type of a navigation mesh polygon.
 enum dtPolyTypes
 {
@@ -127,7 +145,7 @@ enum dtPolyTypes
 };
 
 
-/// Defines a polyogn within a dtMeshTile object.
+/// Defines a polygon within a dtMeshTile object.
 /// @ingroup detour
 struct dtPoly
 {
@@ -282,6 +300,9 @@ struct dtMeshTile
 	int dataSize;							///< Size of the tile data.
 	int flags;								///< Tile flags. (See: #dtTileFlags)
 	dtMeshTile* next;						///< The next free tile, or the next tile in the spatial grid.
+private:
+	dtMeshTile(const dtMeshTile&);
+	dtMeshTile& operator=(const dtMeshTile&);
 };
 
 /// Configuration parameters used to define multi-tile navigation meshes.
@@ -573,6 +594,9 @@ public:
 	/// @}
 	
 private:
+	// Explicitly disabled copy constructor and copy assignment operator.
+	dtNavMesh(const dtNavMesh&);
+	dtNavMesh& operator=(const dtNavMesh&);
 
 	/// Returns pointer to tile in the tile array.
 	dtMeshTile* getTile(int i);
@@ -601,7 +625,7 @@ private:
 	void connectExtOffMeshLinks(dtMeshTile* tile, dtMeshTile* target, int side);
 	
 	/// Removes external links at specified side.
-	void unconnectExtLinks(dtMeshTile* tile, dtMeshTile* target);
+	void unconnectLinks(dtMeshTile* tile, dtMeshTile* target);
 	
 
 	// TODO: These methods are duplicates from dtNavMeshQuery, but are needed for off-mesh connection finding.
@@ -611,7 +635,7 @@ private:
 							dtPolyRef* polys, const int maxPolys) const;
 	/// Find nearest polygon within a tile.
 	dtPolyRef findNearestPolyInTile(const dtMeshTile* tile, const float* center,
-									const float* extents, float* nearestPt) const;
+									const float* halfExtents, float* nearestPt) const;
 	/// Returns closest point on polygon.
 	void closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const;
 	

+ 2 - 1
Source/ThirdParty/Detour/include/DetourNavMeshBuilder.h → Source/ThirdParty/Detour/Include/DetourNavMeshBuilder.h

@@ -145,4 +145,5 @@ function.
 
 @see dtCreateNavMeshData
 
-*/
+*/
+

+ 103 - 12
Source/ThirdParty/Detour/include/DetourNavMeshQuery.h → Source/ThirdParty/Detour/Include/DetourNavMeshQuery.h

@@ -43,6 +43,10 @@ class dtQueryFilter
 public:
 	dtQueryFilter();
 	
+#ifdef DT_VIRTUAL_QUERYFILTER
+	virtual ~dtQueryFilter() { }
+#endif
+	
 	/// Returns true if the polygon can be visited.  (I.e. Is traversable.)
 	///  @param[in]		ref		The reference id of the polygon test.
 	///  @param[in]		tile	The tile containing the polygon.
@@ -117,6 +121,48 @@ public:
 
 };
 
+
+
+/// Provides information about raycast hit
+/// filled by dtNavMeshQuery::raycast
+/// @ingroup detour
+struct dtRaycastHit
+{
+	/// The hit parameter. (FLT_MAX if no wall hit.)
+	float t; 
+	
+	/// hitNormal	The normal of the nearest wall hit. [(x, y, z)]
+	float hitNormal[3];
+
+	/// The index of the edge on the final polygon where the wall was hit.
+	int hitEdgeIndex;
+	
+	/// Pointer to an array of reference ids of the visited polygons. [opt]
+	dtPolyRef* path;
+	
+	/// The number of visited polygons. [opt]
+	int pathCount;
+
+	/// The maximum number of polygons the @p path array can hold.
+	int maxPath;
+
+	///  The cost of the path until hit.
+	float pathCost;
+};
+
+/// Provides custom polygon query behavior.
+/// Used by dtNavMeshQuery::queryPolygons.
+/// @ingroup detour
+class dtPolyQuery
+{
+public:
+	virtual ~dtPolyQuery() { }
+
+	/// Called for each batch of unique polygons touched by the search area in dtNavMeshQuery::queryPolygons.
+	/// This can be called multiple times for a single query.
+	virtual void process(const dtMeshTile* tile, dtPoly** polys, dtPolyRef* refs, int count) = 0;
+};
+
 /// Provides the ability to perform pathfinding related queries against
 /// a navigation mesh.
 /// @ingroup detour
@@ -128,7 +174,7 @@ public:
 	
 	/// Initializes the query object.
 	///  @param[in]		nav			Pointer to the dtNavMesh object to use for all queries.
-	///  @param[in]		maxNodes	Maximum number of search nodes. [Limits: 0 < value <= 65536]
+	///  @param[in]		maxNodes	Maximum number of search nodes. [Limits: 0 < value <= 65535]
 	/// @returns The status flags for the query.
 	dtStatus init(const dtNavMesh* nav, const int maxNodes);
 	
@@ -149,7 +195,7 @@ public:
 					  const float* startPos, const float* endPos,
 					  const dtQueryFilter* filter,
 					  dtPolyRef* path, int* pathCount, const int maxPath) const;
-	
+
 	/// Finds the straight path from the start to the end position within the polygon corridor.
 	///  @param[in]		startPos			Path start position. [(x, y, z)]
 	///  @param[in]		endPos				Path end position. [(x, y, z)]
@@ -181,10 +227,11 @@ public:
 	///  @param[in]		startPos	A position within the start polygon. [(x, y, z)]
 	///  @param[in]		endPos		A position within the end polygon. [(x, y, z)]
 	///  @param[in]		filter		The polygon filter to apply to the query.
+	///  @param[in]		options		query options (see: #dtFindPathOptions)
 	/// @returns The status flags for the query.
 	dtStatus initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef,
 								const float* startPos, const float* endPos,
-								const dtQueryFilter* filter);
+								const dtQueryFilter* filter, const unsigned int options = 0);
 
 	/// Updates an in-progress sliced path query.
 	///  @param[in]		maxIter		The maximum number of iterations to perform.
@@ -251,33 +298,55 @@ public:
 								  dtPolyRef* resultRef, dtPolyRef* resultParent, float* resultCost,
 								  int* resultCount, const int maxResult) const;
 	
+	/// Gets a path from the explored nodes in the previous search.
+	///  @param[in]		endRef		The reference id of the end polygon.
+	///  @param[out]	path		An ordered list of polygon references representing the path. (Start to end.)
+	///  							[(polyRef) * @p pathCount]
+	///  @param[out]	pathCount	The number of polygons returned in the @p path array.
+	///  @param[in]		maxPath		The maximum number of polygons the @p path array can hold. [Limit: >= 0]
+	///  @returns		The status flags. Returns DT_FAILURE | DT_INVALID_PARAM if any parameter is wrong, or if
+	///  				@p endRef was not explored in the previous search. Returns DT_SUCCESS | DT_BUFFER_TOO_SMALL
+	///  				if @p path cannot contain the entire path. In this case it is filled to capacity with a partial path.
+	///  				Otherwise returns DT_SUCCESS.
+	///  @remarks		The result of this function depends on the state of the query object. For that reason it should only
+	///  				be used immediately after one of the two Dijkstra searches, findPolysAroundCircle or findPolysAroundShape.
+	dtStatus getPathFromDijkstraSearch(dtPolyRef endRef, dtPolyRef* path, int* pathCount, int maxPath) const;
+
 	/// @}
 	/// @name Local Query Functions
 	///@{
 
 	/// Finds the polygon nearest to the specified center point.
 	///  @param[in]		center		The center of the search box. [(x, y, z)]
-	///  @param[in]		extents		The search distance along each axis. [(x, y, z)]
+	///  @param[in]		halfExtents		The search distance along each axis. [(x, y, z)]
 	///  @param[in]		filter		The polygon filter to apply to the query.
 	///  @param[out]	nearestRef	The reference id of the nearest polygon.
 	///  @param[out]	nearestPt	The nearest point on the polygon. [opt] [(x, y, z)]
 	/// @returns The status flags for the query.
-	dtStatus findNearestPoly(const float* center, const float* extents,
+	dtStatus findNearestPoly(const float* center, const float* halfExtents,
 							 const dtQueryFilter* filter,
 							 dtPolyRef* nearestRef, float* nearestPt) const;
 	
 	/// Finds polygons that overlap the search box.
 	///  @param[in]		center		The center of the search box. [(x, y, z)]
-	///  @param[in]		extents		The search distance along each axis. [(x, y, z)]
+	///  @param[in]		halfExtents		The search distance along each axis. [(x, y, z)]
 	///  @param[in]		filter		The polygon filter to apply to the query.
 	///  @param[out]	polys		The reference ids of the polygons that overlap the query box.
 	///  @param[out]	polyCount	The number of polygons in the search result.
 	///  @param[in]		maxPolys	The maximum number of polygons the search result can hold.
 	/// @returns The status flags for the query.
-	dtStatus queryPolygons(const float* center, const float* extents,
+	dtStatus queryPolygons(const float* center, const float* halfExtents,
 						   const dtQueryFilter* filter,
 						   dtPolyRef* polys, int* polyCount, const int maxPolys) const;
 
+	/// Finds polygons that overlap the search box.
+	///  @param[in]		center		The center of the search box. [(x, y, z)]
+	///  @param[in]		halfExtents		The search distance along each axis. [(x, y, z)]
+	///  @param[in]		filter		The polygon filter to apply to the query.
+	///  @param[in]		query		The query. Polygons found will be batched together and passed to this query.
+	dtStatus queryPolygons(const float* center, const float* halfExtents,
+						   const dtQueryFilter* filter, dtPolyQuery* query) const;
+
 	/// Finds the non-overlapping navigation polygons in the local neighbourhood around the center position.
 	///  @param[in]		startRef		The reference id of the polygon where the search starts.
 	///  @param[in]		centerPos		The center of the query circle. [(x, y, z)]
@@ -310,6 +379,7 @@ public:
 	
 	/// Casts a 'walkability' ray along the surface of the navigation mesh from 
 	/// the start position toward the end position.
+	/// @note A wrapper around raycast(..., RaycastHit*). Retained for backward compatibility.
 	///  @param[in]		startRef	The reference id of the start polygon.
 	///  @param[in]		startPos	A position within the start polygon representing 
 	///  							the start of the ray. [(x, y, z)]
@@ -325,6 +395,22 @@ public:
 					 const dtQueryFilter* filter,
 					 float* t, float* hitNormal, dtPolyRef* path, int* pathCount, const int maxPath) const;
 	
+	/// Casts a 'walkability' ray along the surface of the navigation mesh from 
+	/// the start position toward the end position.
+	///  @param[in]		startRef	The reference id of the start polygon.
+	///  @param[in]		startPos	A position within the start polygon representing 
+	///  							the start of the ray. [(x, y, z)]
+	///  @param[in]		endPos		The position to cast the ray toward. [(x, y, z)]
+	///  @param[in]		filter		The polygon filter to apply to the query.
+	///  @param[in]		flags		govern how the raycast behaves. See dtRaycastOptions
+	///  @param[out]	hit			Pointer to a raycast hit structure which will be filled by the results.
+	///  @param[in]		prevRef		parent of start ref. Used during for cost calculation [opt]
+	/// @returns The status flags for the query.
+	dtStatus raycast(dtPolyRef startRef, const float* startPos, const float* endPos,
+					 const dtQueryFilter* filter, const unsigned int options,
+					 dtRaycastHit* hit, dtPolyRef prevRef = 0) const;
+
+
 	/// Finds the distance from the specified position to the nearest polygon wall.
 	///  @param[in]		startRef		The reference id of the polygon containing @p centerPos.
 	///  @param[in]		centerPos		The center of the search circle. [(x, y, z)]
@@ -424,13 +510,13 @@ public:
 	/// @}
 	
 private:
+	// Explicitly disabled copy constructor and copy assignment operator
+	dtNavMeshQuery(const dtNavMeshQuery&);
+	dtNavMeshQuery& operator=(const dtNavMeshQuery&);
 	
-	/// Returns neighbour tile based on side.
-	dtMeshTile* getNeighbourTileAt(int x, int y, int side) const;
-
 	/// Queries polygons within a tile.
-	int queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax, const dtQueryFilter* filter,
-							dtPolyRef* polys, const int maxPolys) const;
+	void queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax,
+							 const dtQueryFilter* filter, dtPolyQuery* query) const;
 
 	/// Returns portal points between two polygons.
 	dtStatus getPortalPoints(dtPolyRef from, dtPolyRef to, float* left, float* right,
@@ -454,6 +540,9 @@ private:
 	dtStatus appendPortals(const int startIdx, const int endIdx, const float* endPos, const dtPolyRef* path,
 						   float* straightPath, unsigned char* straightPathFlags, dtPolyRef* straightPathRefs,
 						   int* straightPathCount, const int maxStraightPath, const int options) const;
+
+	// Gets the path leading to the specified end node.
+	dtStatus getPathToNode(struct dtNode* endNode, dtPolyRef* path, int* pathCount, int maxPath) const;
 	
 	const dtNavMesh* m_nav;				///< Pointer to navmesh data.
 
@@ -465,6 +554,8 @@ private:
 		dtPolyRef startRef, endRef;
 		float startPos[3], endPos[3];
 		const dtQueryFilter* filter;
+		unsigned int options;
+		float raycastLimitSqr;
 	};
 	dtQueryData m_query;				///< Sliced query state.
 

+ 31 - 22
Source/ThirdParty/Detour/include/DetourNode.h → Source/ThirdParty/Detour/Include/DetourNode.h

@@ -25,48 +25,56 @@ enum dtNodeFlags
 {
 	DT_NODE_OPEN = 0x01,
 	DT_NODE_CLOSED = 0x02,
+	DT_NODE_PARENT_DETACHED = 0x04, // parent of the node is not adjacent. Found using raycast.
 };
 
 typedef unsigned short dtNodeIndex;
 static const dtNodeIndex DT_NULL_IDX = (dtNodeIndex)~0;
 
+static const int DT_NODE_PARENT_BITS = 24;
+static const int DT_NODE_STATE_BITS = 2;
 struct dtNode
 {
-	float pos[3];				///< Position of the node.
-	float cost;					///< Cost from previous node to current node.
-	float total;				///< Cost up to the node.
-	unsigned int pidx : 30;		///< Index to parent node.
-	unsigned int flags : 2;		///< Node flags 0/open/closed.
-	dtPolyRef id;				///< Polygon ref the node corresponds to.
+	float pos[3];								///< Position of the node.
+	float cost;									///< Cost from previous node to current node.
+	float total;								///< Cost up to the node.
+	unsigned int pidx : DT_NODE_PARENT_BITS;	///< Index to parent node.
+	unsigned int state : DT_NODE_STATE_BITS;	///< extra state information. A polyRef can have multiple nodes with different extra info. see DT_MAX_STATES_PER_NODE
+	unsigned int flags : 3;						///< Node flags. A combination of dtNodeFlags.
+	dtPolyRef id;								///< Polygon ref the node corresponds to.
 };
 
+static const int DT_MAX_STATES_PER_NODE = 1 << DT_NODE_STATE_BITS;	// number of extra states per node. See dtNode::state
 
 class dtNodePool
 {
 public:
 	dtNodePool(int maxNodes, int hashSize);
 	~dtNodePool();
-	inline void operator=(const dtNodePool&) {}
 	void clear();
-	dtNode* getNode(dtPolyRef id);
-	dtNode* findNode(dtPolyRef id);
+
+	// Get a dtNode by ref and extra state information. If there is none then - allocate
+	// There can be more than one node for the same polyRef but with different extra state information
+	dtNode* getNode(dtPolyRef id, unsigned char state=0);	
+	dtNode* findNode(dtPolyRef id, unsigned char state);
+	unsigned int findNodes(dtPolyRef id, dtNode** nodes, const int maxNodes);
 
 	inline unsigned int getNodeIdx(const dtNode* node) const
 	{
 		if (!node) return 0;
-		return (unsigned int)(node - m_nodes)+1;
+		return (unsigned int)(node - m_nodes) + 1;
 	}
 
 	inline dtNode* getNodeAtIdx(unsigned int idx)
 	{
 		if (!idx) return 0;
-		return &m_nodes[idx-1];
+		return &m_nodes[idx - 1];
 	}
 
 	inline const dtNode* getNodeAtIdx(unsigned int idx) const
 	{
 		if (!idx) return 0;
-		return &m_nodes[idx-1];
+		return &m_nodes[idx - 1];
 	}
 	
 	inline int getMemUsed() const
@@ -82,8 +90,12 @@ public:
 	inline int getHashSize() const { return m_hashSize; }
 	inline dtNodeIndex getFirst(int bucket) const { return m_first[bucket]; }
 	inline dtNodeIndex getNext(int i) const { return m_next[i]; }
+	inline int getNodeCount() const { return m_nodeCount; }
 	
 private:
+	// Explicitly disabled copy constructor and copy assignment operator.
+	dtNodePool(const dtNodePool&);
+	dtNodePool& operator=(const dtNodePool&);
 	
 	dtNode* m_nodes;
 	dtNodeIndex* m_first;
@@ -98,17 +110,10 @@ class dtNodeQueue
 public:
 	dtNodeQueue(int n);
 	~dtNodeQueue();
-	inline void operator=(dtNodeQueue&) {}
 	
-	inline void clear()
-	{
-		m_size = 0;
-	}
+	inline void clear() { m_size = 0; }
 	
-	inline dtNode* top()
-	{
-		return m_heap[0];
-	}
+	inline dtNode* top() { return m_heap[0]; }
 	
 	inline dtNode* pop()
 	{
@@ -141,12 +146,16 @@ public:
 	inline int getMemUsed() const
 	{
 		return sizeof(*this) +
-		sizeof(dtNode*)*(m_capacity+1);
+		sizeof(dtNode*) * (m_capacity + 1);
 	}
 	
 	inline int getCapacity() const { return m_capacity; }
 	
 private:
+	// Explicitly disabled copy constructor and copy assignment operator.
+	dtNodeQueue(const dtNodeQueue&);
+	dtNodeQueue& operator=(const dtNodeQueue&);
+
 	void bubbleUp(int i, dtNode* node);
 	void trickleDown(int i, dtNode* node);
 	

+ 1 - 0
Source/ThirdParty/Detour/include/DetourStatus.h → Source/ThirdParty/Detour/Include/DetourStatus.h

@@ -35,6 +35,7 @@ static const unsigned int DT_INVALID_PARAM = 1 << 3;	// An input parameter was i
 static const unsigned int DT_BUFFER_TOO_SMALL = 1 << 4;	// Result buffer for the query was too small to store all results.
 static const unsigned int DT_OUT_OF_NODES = 1 << 5;		// Query ran out of nodes during search.
 static const unsigned int DT_PARTIAL_RESULT = 1 << 6;	// Query did not reach the end location, returning best guess. 
+static const unsigned int DT_ALREADY_OCCUPIED = 1 << 7;	// A tile has already been assigned to the given x,y coordinate
 
 
 // Returns true of status is success.

+ 2 - 2
Source/ThirdParty/Detour/source/DetourAlloc.cpp → Source/ThirdParty/Detour/Source/DetourAlloc.cpp

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

+ 14 - 12
Source/ThirdParty/Detour/include/DetourAssert.h → Source/ThirdParty/Detour/Source/DetourAssert.cpp

@@ -16,18 +16,20 @@
 // 3. This notice may not be removed or altered from any source distribution.
 //
 
-#ifndef DETOURASSERT_H
-#define DETOURASSERT_H
+#include "DetourAssert.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.
+#ifndef NDEBUG
 
-#ifdef NDEBUG
-// From http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/
-#	define dtAssert(x) do { (void)sizeof(x); } while((void)(__LINE__==-1),false)  
-#else
-#	include <assert.h> 
-#	define dtAssert assert
-#endif
+static dtAssertFailFunc* sAssertFailFunc = 0;
+
+void dtAssertFailSetCustom(dtAssertFailFunc *assertFailFunc)
+{
+	sAssertFailFunc = assertFailFunc;
+}
 
-#endif // DETOURASSERT_H
+dtAssertFailFunc* dtAssertFailGetCustom()
+{
+	return sAssertFailFunc;
+}
+
+#endif

+ 3 - 8
Source/ThirdParty/Detour/source/DetourCommon.cpp → Source/ThirdParty/Detour/Source/DetourCommon.cpp

@@ -21,11 +21,6 @@
 
 //////////////////////////////////////////////////////////////////////////////////////////
 
-float dtSqrt(float x)
-{
-	return dtMathSqrtf(x);
-}
-
 void dtClosestPtPointTriangle(float* closest, const float* p,
 							  const float* a, const float* b, const float* c)
 {
@@ -347,8 +342,8 @@ void dtRandomPointInConvexPoly(const float* pts, const int npts, float* areas,
 	// Find sub triangle weighted by area.
 	const float thr = s*areasum;
 	float acc = 0.0f;
-	float u = 0.0f;
-	int tri = 0;
+	float u = 1.0f;
+	int tri = npts - 1;
 	for (int i = 2; i < npts; i++) {
 		const float dacc = areas[i];
 		if (thr >= acc && thr < (acc+dacc))
@@ -360,7 +355,7 @@ void dtRandomPointInConvexPoly(const float* pts, const int npts, float* areas,
 		acc += dacc;
 	}
 	
-	float v = dtSqrt(t);
+	float v = dtMathSqrtf(t);
 	
 	const float a = 1 - v;
 	const float b = (1 - u) * v;

+ 46 - 41
Source/ThirdParty/Detour/source/DetourNavMesh.cpp → Source/ThirdParty/Detour/Source/DetourNavMesh.cpp

@@ -223,7 +223,6 @@ dtNavMesh::~dtNavMesh()
 			}
 		}
 	}
-	
 	dtFree(m_posLookup);
 	dtFree(m_tiles);
 }
@@ -311,7 +310,7 @@ int dtNavMesh::findConnectingPolys(const float* va, const float* vb,
 	if (!tile) return 0;
 	
 	float amin[2], amax[2];
-	calcSlabEndPoints(va,vb, amin,amax, side);
+	calcSlabEndPoints(va, vb, amin, amax, side);
 	const float apos = getSlabCoord(va, side);
 
 	// Remove links pointing to 'side' and compact the links array. 
@@ -357,7 +356,7 @@ int dtNavMesh::findConnectingPolys(const float* va, const float* vb,
 	return n;
 }
 
-void dtNavMesh::unconnectExtLinks(dtMeshTile* tile, dtMeshTile* target)
+void dtNavMesh::unconnectLinks(dtMeshTile* tile, dtMeshTile* target)
 {
 	if (!tile || !target) return;
 
@@ -370,10 +369,9 @@ void dtNavMesh::unconnectExtLinks(dtMeshTile* tile, dtMeshTile* target)
 		unsigned int pj = DT_NULL_LINK;
 		while (j != DT_NULL_LINK)
 		{
-			if (tile->links[j].side != 0xff &&
-				decodePolyIdTile(tile->links[j].ref) == targetNum)
+			if (decodePolyIdTile(tile->links[j].ref) == targetNum)
 			{
-				// Revove link.
+				// Remove link.
 				unsigned int nj = tile->links[j].next;
 				if (pj == DT_NULL_LINK)
 					poly->firstLink = nj;
@@ -478,12 +476,12 @@ void dtNavMesh::connectExtOffMeshLinks(dtMeshTile* tile, dtMeshTile* target, int
 		if (targetPoly->firstLink == DT_NULL_LINK)
 			continue;
 		
-		const float ext[3] = { targetCon->rad, target->header->walkableClimb, targetCon->rad };
+		const float halfExtents[3] = { targetCon->rad, target->header->walkableClimb, targetCon->rad };
 		
 		// Find polygon to connect to.
 		const float* p = &targetCon->pos[3];
 		float nearestPt[3];
-		dtPolyRef ref = findNearestPolyInTile(tile, p, ext, nearestPt);
+		dtPolyRef ref = findNearestPolyInTile(tile, p, halfExtents, nearestPt);
 		if (!ref)
 			continue;
 		// findNearestPoly may return too optimistic results, further check to make sure. 
@@ -578,12 +576,12 @@ void dtNavMesh::baseOffMeshLinks(dtMeshTile* tile)
 		dtOffMeshConnection* con = &tile->offMeshCons[i];
 		dtPoly* poly = &tile->polys[con->poly];
 	
-		const float ext[3] = { con->rad, tile->header->walkableClimb, con->rad };
+		const float halfExtents[3] = { con->rad, tile->header->walkableClimb, con->rad };
 		
 		// Find polygon to connect to.
 		const float* p = &con->pos[0]; // First vertex
 		float nearestPt[3];
-		dtPolyRef ref = findNearestPolyInTile(tile, p, ext, nearestPt);
+		dtPolyRef ref = findNearestPolyInTile(tile, p, halfExtents, nearestPt);
 		if (!ref) continue;
 		// findNearestPoly may return too optimistic results, further check to make sure. 
 		if (dtSqr(nearestPt[0]-p[0])+dtSqr(nearestPt[2]-p[2]) > dtSqr(con->rad))
@@ -659,9 +657,9 @@ void dtNavMesh::closestPointOnPoly(dtPolyRef ref, const float* pos, float* close
 	if (!dtDistancePtPolyEdgesSqr(pos, verts, nv, edged, edget))
 	{
 		// Point is outside the polygon, dtClamp to nearest edge.
-		float dmin = FLT_MAX;
-		int imin = -1;
-		for (int i = 0; i < nv; ++i)
+		float dmin = edged[0];
+		int imin = 0;
+		for (int i = 1; i < nv; ++i)
 		{
 			if (edged[i] < dmin)
 			{
@@ -695,7 +693,7 @@ void dtNavMesh::closestPointOnPoly(dtPolyRef ref, const float* pos, float* close
 				v[k] = &tile->detailVerts[(pd->vertBase+(t[k]-poly->vertCount))*3];
 		}
 		float h;
-		if (dtClosestHeightPointTriangle(pos, v[0], v[1], v[2], h))
+		if (dtClosestHeightPointTriangle(closest, v[0], v[1], v[2], h))
 		{
 			closest[1] = h;
 			break;
@@ -704,12 +702,12 @@ void dtNavMesh::closestPointOnPoly(dtPolyRef ref, const float* pos, float* close
 }
 
 dtPolyRef dtNavMesh::findNearestPolyInTile(const dtMeshTile* tile,
-										   const float* center, const float* extents,
+										   const float* center, const float* halfExtents,
 										   float* nearestPt) const
 {
 	float bmin[3], bmax[3];
-	dtVsub(bmin, center, extents);
-	dtVadd(bmax, center, extents);
+	dtVsub(bmin, center, halfExtents);
+	dtVadd(bmax, center, halfExtents);
 	
 	// Get nearby polygons from proximity grid.
 	dtPolyRef polys[128];
@@ -724,7 +722,7 @@ dtPolyRef dtNavMesh::findNearestPolyInTile(const dtMeshTile* tile,
 		float closestPtPoly[3];
 		float diff[3];
 		bool posOverPoly = false;
-		float d = 0;
+		float d;
 		closestPointOnPoly(ref, center, closestPtPoly, &posOverPoly);
 
 		// If a point is directly over a polygon and closer than
@@ -845,6 +843,11 @@ int dtNavMesh::queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, co
 /// tile will be restored to the same values they were before the tile was 
 /// removed.
 ///
+/// The nav mesh assumes exclusive access to the data passed and will make
+/// changes to the dynamic portion of the data. For that reason the data
+/// should not be reused in other nav meshes until the tile has been successfully
+/// removed from this nav mesh.
+///
 /// @see dtCreateNavMeshData, #removeTile
 dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags,
 							dtTileRef lastRef, dtTileRef* result)
@@ -858,7 +861,7 @@ dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags,
 		
 	// Make sure the location is free.
 	if (getTileAt(header->x, header->y, header->layer))
-		return DT_FAILURE;
+		return DT_FAILURE | DT_ALREADY_OCCUPIED;
 		
 	// Allocate a tile.
 	dtMeshTile* tile = 0;
@@ -920,14 +923,14 @@ dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags,
 	const int offMeshLinksSize = dtAlign4(sizeof(dtOffMeshConnection)*header->offMeshConCount);
 	
 	unsigned char* d = data + headerSize;
-	tile->verts = (float*)d; d += vertsSize;
-	tile->polys = (dtPoly*)d; d += polysSize;
-	tile->links = (dtLink*)d; d += linksSize;
-	tile->detailMeshes = (dtPolyDetail*)d; d += detailMeshesSize;
-	tile->detailVerts = (float*)d; d += detailVertsSize;
-	tile->detailTris = (unsigned char*)d; d += detailTrisSize;
-	tile->bvTree = (dtBVNode*)d; d += bvtreeSize;
-	tile->offMeshCons = (dtOffMeshConnection*)d; d += offMeshLinksSize;
+	tile->verts = dtGetThenAdvanceBufferPointer<float>(d, vertsSize);
+	tile->polys = dtGetThenAdvanceBufferPointer<dtPoly>(d, polysSize);
+	tile->links = dtGetThenAdvanceBufferPointer<dtLink>(d, linksSize);
+	tile->detailMeshes = dtGetThenAdvanceBufferPointer<dtPolyDetail>(d, detailMeshesSize);
+	tile->detailVerts = dtGetThenAdvanceBufferPointer<float>(d, detailVertsSize);
+	tile->detailTris = dtGetThenAdvanceBufferPointer<unsigned char>(d, detailTrisSize);
+	tile->bvTree = dtGetThenAdvanceBufferPointer<dtBVNode>(d, bvtreeSize);
+	tile->offMeshCons = dtGetThenAdvanceBufferPointer<dtOffMeshConnection>(d, offMeshLinksSize);
 
 	// If there are no items in the bvtree, reset the tree pointer.
 	if (!bvtreeSize)
@@ -946,7 +949,10 @@ dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags,
 	tile->flags = flags;
 
 	connectIntLinks(tile);
+
+	// Base off-mesh connections to their starting polygons and connect connections inside the tile.
 	baseOffMeshLinks(tile);
+	connectExtOffMeshLinks(tile, tile, -1);
 
 	// Create connections with neighbour tiles.
 	static const int MAX_NEIS = 32;
@@ -957,11 +963,11 @@ dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags,
 	nneis = getTilesAt(header->x, header->y, neis, MAX_NEIS);
 	for (int j = 0; j < nneis; ++j)
 	{
-		if (neis[j] != tile)
-		{
-			connectExtLinks(tile, neis[j], -1);
-			connectExtLinks(neis[j], tile, -1);
-		}
+		if (neis[j] == tile)
+			continue;
+	
+		connectExtLinks(tile, neis[j], -1);
+		connectExtLinks(neis[j], tile, -1);
 		connectExtOffMeshLinks(tile, neis[j], -1);
 		connectExtOffMeshLinks(neis[j], tile, -1);
 	}
@@ -1199,25 +1205,24 @@ dtStatus dtNavMesh::removeTile(dtTileRef ref, unsigned char** data, int* dataSiz
 	}
 	
 	// Remove connections to neighbour tiles.
-	// Create connections with neighbour tiles.
 	static const int MAX_NEIS = 32;
 	dtMeshTile* neis[MAX_NEIS];
 	int nneis;
 	
-	// Connect with layers in current tile.
+	// Disconnect from other layers in current tile.
 	nneis = getTilesAt(tile->header->x, tile->header->y, neis, MAX_NEIS);
 	for (int j = 0; j < nneis; ++j)
 	{
 		if (neis[j] == tile) continue;
-		unconnectExtLinks(neis[j], tile);
+		unconnectLinks(neis[j], tile);
 	}
 	
-	// Connect with neighbour tiles.
+	// Disconnect from neighbour tiles.
 	for (int i = 0; i < 8; ++i)
 	{
 		nneis = getNeighbourTilesAt(tile->header->x, tile->header->y, i, neis, MAX_NEIS);
 		for (int j = 0; j < nneis; ++j)
-			unconnectExtLinks(neis[j], tile);
+			unconnectLinks(neis[j], tile);
 	}
 		
 	// Reset tile.
@@ -1326,8 +1331,8 @@ dtStatus dtNavMesh::storeTileState(const dtMeshTile* tile, unsigned char* data,
 	if (maxDataSize < sizeReq)
 		return DT_FAILURE | DT_BUFFER_TOO_SMALL;
 		
-	dtTileState* tileState = (dtTileState*)data; data += dtAlign4(sizeof(dtTileState));
-	dtPolyState* polyStates = (dtPolyState*)data; data += dtAlign4(sizeof(dtPolyState) * tile->header->polyCount);
+	dtTileState* tileState = dtGetThenAdvanceBufferPointer<dtTileState>(data, dtAlign4(sizeof(dtTileState)));
+	dtPolyState* polyStates = dtGetThenAdvanceBufferPointer<dtPolyState>(data, dtAlign4(sizeof(dtPolyState) * tile->header->polyCount));
 	
 	// Store tile state.
 	tileState->magic = DT_NAVMESH_STATE_MAGIC;
@@ -1358,8 +1363,8 @@ dtStatus dtNavMesh::restoreTileState(dtMeshTile* tile, const unsigned char* data
 	if (maxDataSize < sizeReq)
 		return DT_FAILURE | DT_INVALID_PARAM;
 	
-	const dtTileState* tileState = (const dtTileState*)data; data += dtAlign4(sizeof(dtTileState));
-	const dtPolyState* polyStates = (const dtPolyState*)data; data += dtAlign4(sizeof(dtPolyState) * tile->header->polyCount);
+	const dtTileState* tileState = dtGetThenAdvanceBufferPointer<const dtTileState>(data, dtAlign4(sizeof(dtTileState)));
+	const dtPolyState* polyStates = dtGetThenAdvanceBufferPointer<const dtPolyState>(data, dtAlign4(sizeof(dtPolyState) * tile->header->polyCount));
 	
 	// Check that the restore is possible.
 	if (tileState->magic != DT_NAVMESH_STATE_MAGIC)

+ 77 - 50
Source/ThirdParty/Detour/source/DetourNavMeshBuilder.cpp → Source/ThirdParty/Detour/Source/DetourNavMeshBuilder.cpp

@@ -106,7 +106,6 @@ inline int longestAxis(unsigned short x, unsigned short y, unsigned short z)
 	if (z > maxVal)
 	{
 		axis = 2;
-		maxVal = z;
 	}
 	return axis;
 }
@@ -169,45 +168,72 @@ static void subdivide(BVItem* items, int nitems, int imin, int imax, int& curNod
 	}
 }
 
-static int createBVTree(const unsigned short* verts, const int /*nverts*/,
-						const unsigned short* polys, const int npolys, const int nvp,
-						const float cs, const float ch,
-						const int /*nnodes*/, dtBVNode* nodes)
+static int createBVTree(dtNavMeshCreateParams* params, dtBVNode* nodes, int /*nnodes*/)
 {
 	// Build tree
-	BVItem* items = (BVItem*)dtAlloc(sizeof(BVItem)*npolys, DT_ALLOC_TEMP);
-	for (int i = 0; i < npolys; i++)
+	float quantFactor = 1 / params->cs;
+	BVItem* items = (BVItem*)dtAlloc(sizeof(BVItem)*params->polyCount, DT_ALLOC_TEMP);
+	for (int i = 0; i < params->polyCount; i++)
 	{
 		BVItem& it = items[i];
 		it.i = i;
-		// Calc polygon bounds.
-		const unsigned short* p = &polys[i*nvp*2];
-		it.bmin[0] = it.bmax[0] = verts[p[0]*3+0];
-		it.bmin[1] = it.bmax[1] = verts[p[0]*3+1];
-		it.bmin[2] = it.bmax[2] = verts[p[0]*3+2];
-		
-		for (int j = 1; j < nvp; ++j)
+		// Calc polygon bounds. Use detail meshes if available.
+		if (params->detailMeshes)
 		{
-			if (p[j] == MESH_NULL_IDX) break;
-			unsigned short x = verts[p[j]*3+0];
-			unsigned short y = verts[p[j]*3+1];
-			unsigned short z = verts[p[j]*3+2];
-			
-			if (x < it.bmin[0]) it.bmin[0] = x;
-			if (y < it.bmin[1]) it.bmin[1] = y;
-			if (z < it.bmin[2]) it.bmin[2] = z;
-			
-			if (x > it.bmax[0]) it.bmax[0] = x;
-			if (y > it.bmax[1]) it.bmax[1] = y;
-			if (z > it.bmax[2]) it.bmax[2] = z;
+			int vb = (int)params->detailMeshes[i*4+0];
+			int ndv = (int)params->detailMeshes[i*4+1];
+			float bmin[3];
+			float bmax[3];
+
+			const float* dv = &params->detailVerts[vb*3];
+			dtVcopy(bmin, dv);
+			dtVcopy(bmax, dv);
+
+			for (int j = 1; j < ndv; j++)
+			{
+				dtVmin(bmin, &dv[j * 3]);
+				dtVmax(bmax, &dv[j * 3]);
+			}
+
+			// BV-tree uses cs for all dimensions
+			it.bmin[0] = (unsigned short)dtClamp((int)((bmin[0] - params->bmin[0])*quantFactor), 0, 0xffff);
+			it.bmin[1] = (unsigned short)dtClamp((int)((bmin[1] - params->bmin[1])*quantFactor), 0, 0xffff);
+			it.bmin[2] = (unsigned short)dtClamp((int)((bmin[2] - params->bmin[2])*quantFactor), 0, 0xffff);
+
+			it.bmax[0] = (unsigned short)dtClamp((int)((bmax[0] - params->bmin[0])*quantFactor), 0, 0xffff);
+			it.bmax[1] = (unsigned short)dtClamp((int)((bmax[1] - params->bmin[1])*quantFactor), 0, 0xffff);
+			it.bmax[2] = (unsigned short)dtClamp((int)((bmax[2] - params->bmin[2])*quantFactor), 0, 0xffff);
+		}
+		else
+		{
+			const unsigned short* p = &params->polys[i*params->nvp * 2];
+			it.bmin[0] = it.bmax[0] = params->verts[p[0] * 3 + 0];
+			it.bmin[1] = it.bmax[1] = params->verts[p[0] * 3 + 1];
+			it.bmin[2] = it.bmax[2] = params->verts[p[0] * 3 + 2];
+
+			for (int j = 1; j < params->nvp; ++j)
+			{
+				if (p[j] == MESH_NULL_IDX) break;
+				unsigned short x = params->verts[p[j] * 3 + 0];
+				unsigned short y = params->verts[p[j] * 3 + 1];
+				unsigned short z = params->verts[p[j] * 3 + 2];
+
+				if (x < it.bmin[0]) it.bmin[0] = x;
+				if (y < it.bmin[1]) it.bmin[1] = y;
+				if (z < it.bmin[2]) it.bmin[2] = z;
+
+				if (x > it.bmax[0]) it.bmax[0] = x;
+				if (y > it.bmax[1]) it.bmax[1] = y;
+				if (z > it.bmax[2]) it.bmax[2] = z;
+			}
+			// Remap y
+			it.bmin[1] = (unsigned short)dtMathFloorf((float)it.bmin[1] * params->ch / params->cs);
+			it.bmax[1] = (unsigned short)dtMathCeilf((float)it.bmax[1] * params->ch / params->cs);
 		}
-		// Remap y
-		it.bmin[1] = (unsigned short)dtMathFloorf((float)it.bmin[1]*ch/cs);
-		it.bmax[1] = (unsigned short)dtMathCeilf((float)it.bmax[1]*ch/cs);
 	}
 	
 	int curNode = 0;
-	subdivide(items, npolys, 0, npolys, curNode, nodes);
+	subdivide(items, params->polyCount, 0, params->polyCount, curNode, nodes);
 	
 	dtFree(items);
 	
@@ -421,15 +447,16 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData,
 	memset(data, 0, dataSize);
 	
 	unsigned char* d = data;
-	dtMeshHeader* header = (dtMeshHeader*)d; d += headerSize;
-	float* navVerts = (float*)d; d += vertsSize;
-	dtPoly* navPolys = (dtPoly*)d; d += polysSize;
-	d += linksSize;
-	dtPolyDetail* navDMeshes = (dtPolyDetail*)d; d += detailMeshesSize;
-	float* navDVerts = (float*)d; d += detailVertsSize;
-	unsigned char* navDTris = (unsigned char*)d; d += detailTrisSize;
-	dtBVNode* navBvtree = (dtBVNode*)d; d += bvTreeSize;
-	dtOffMeshConnection* offMeshCons = (dtOffMeshConnection*)d; d += offMeshConsSize;
+
+	dtMeshHeader* header = dtGetThenAdvanceBufferPointer<dtMeshHeader>(d, headerSize);
+	float* navVerts = dtGetThenAdvanceBufferPointer<float>(d, vertsSize);
+	dtPoly* navPolys = dtGetThenAdvanceBufferPointer<dtPoly>(d, polysSize);
+	d += linksSize; // Ignore links; just leave enough space for them. They'll be created on load.
+	dtPolyDetail* navDMeshes = dtGetThenAdvanceBufferPointer<dtPolyDetail>(d, detailMeshesSize);
+	float* navDVerts = dtGetThenAdvanceBufferPointer<float>(d, detailVertsSize);
+	unsigned char* navDTris = dtGetThenAdvanceBufferPointer<unsigned char>(d, detailTrisSize);
+	dtBVNode* navBvtree = dtGetThenAdvanceBufferPointer<dtBVNode>(d, bvTreeSize);
+	dtOffMeshConnection* offMeshCons = dtGetThenAdvanceBufferPointer<dtOffMeshConnection>(d, offMeshConsSize);
 	
 	
 	// Store header
@@ -595,11 +622,9 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData,
 	}
 
 	// Store and create BVtree.
-	// TODO: take detail mesh into account! use byte per bbox extent?
 	if (params->buildBvTree)
 	{
-		createBVTree(params->verts, params->vertCount, params->polys, params->polyCount,
-					 nvp, params->cs, params->ch, params->polyCount*2, navBvtree);
+		createBVTree(params, navBvtree, 2*params->polyCount);
 	}
 	
 	// Store Off-Mesh connections.
@@ -705,14 +730,16 @@ bool dtNavMeshDataSwapEndian(unsigned char* data, const int /*dataSize*/)
 	const int offMeshLinksSize = dtAlign4(sizeof(dtOffMeshConnection)*header->offMeshConCount);
 	
 	unsigned char* d = data + headerSize;
-	float* verts = (float*)d; d += vertsSize;
-	dtPoly* polys = (dtPoly*)d; d += polysSize;
-	/*dtLink* links = (dtLink*)d;*/ d += linksSize;
-	dtPolyDetail* detailMeshes = (dtPolyDetail*)d; d += detailMeshesSize;
-	float* detailVerts = (float*)d; d += detailVertsSize;
-	/*unsigned char* detailTris = (unsigned char*)d;*/ d += detailTrisSize;
-	dtBVNode* bvTree = (dtBVNode*)d; d += bvtreeSize;
-	dtOffMeshConnection* offMeshCons = (dtOffMeshConnection*)d; d += offMeshLinksSize;
+	float* verts = dtGetThenAdvanceBufferPointer<float>(d, vertsSize);
+	dtPoly* polys = dtGetThenAdvanceBufferPointer<dtPoly>(d, polysSize);
+	d += linksSize; // Ignore links; they technically should be endian-swapped but all their data is overwritten on load anyway.
+	//dtLink* links = dtGetThenAdvanceBufferPointer<dtLink>(d, linksSize);
+	dtPolyDetail* detailMeshes = dtGetThenAdvanceBufferPointer<dtPolyDetail>(d, detailMeshesSize);
+	float* detailVerts = dtGetThenAdvanceBufferPointer<float>(d, detailVertsSize);
+	d += detailTrisSize; // Ignore detail tris; single bytes can't be endian-swapped.
+	//unsigned char* detailTris = dtGetThenAdvanceBufferPointer<unsigned char>(d, detailTrisSize);
+	dtBVNode* bvTree = dtGetThenAdvanceBufferPointer<dtBVNode>(d, bvtreeSize);
+	dtOffMeshConnection* offMeshCons = dtGetThenAdvanceBufferPointer<dtOffMeshConnection>(d, offMeshLinksSize);
 	
 	// Vertices
 	for (int i = 0; i < header->vertCount*3; ++i)

File diff suppressed because it is too large
+ 490 - 191
Source/ThirdParty/Detour/Source/DetourNavMeshQuery.cpp


+ 26 - 4
Source/ThirdParty/Detour/source/DetourNode.cpp → Source/ThirdParty/Detour/Source/DetourNode.cpp

@@ -57,7 +57,9 @@ dtNodePool::dtNodePool(int maxNodes, int hashSize) :
 	m_nodeCount(0)
 {
 	dtAssert(dtNextPow2(m_hashSize) == (unsigned int)m_hashSize);
-	dtAssert(m_maxNodes > 0);
+	// pidx is special as 0 means "none" and 1 is the first node. For that reason
+	// we have 1 fewer nodes available than the number of values it can contain.
+	dtAssert(m_maxNodes > 0 && m_maxNodes <= DT_NULL_IDX && m_maxNodes <= (1 << DT_NODE_PARENT_BITS) - 1);
 
 	m_nodes = (dtNode*)dtAlloc(sizeof(dtNode)*m_maxNodes, DT_ALLOC_PERM);
 	m_next = (dtNodeIndex*)dtAlloc(sizeof(dtNodeIndex)*m_maxNodes, DT_ALLOC_PERM);
@@ -84,27 +86,46 @@ void dtNodePool::clear()
 	m_nodeCount = 0;
 }
 
-dtNode* dtNodePool::findNode(dtPolyRef id)
+unsigned int dtNodePool::findNodes(dtPolyRef id, dtNode** nodes, const int maxNodes)
 {
+	int n = 0;
 	unsigned int bucket = dtHashRef(id) & (m_hashSize-1);
 	dtNodeIndex i = m_first[bucket];
 	while (i != DT_NULL_IDX)
 	{
 		if (m_nodes[i].id == id)
+		{
+			if (n >= maxNodes)
+				return n;
+			nodes[n++] = &m_nodes[i];
+		}
+		i = m_next[i];
+	}
+
+	return n;
+}
+
+dtNode* dtNodePool::findNode(dtPolyRef id, unsigned char state)
+{
+	unsigned int bucket = dtHashRef(id) & (m_hashSize-1);
+	dtNodeIndex i = m_first[bucket];
+	while (i != DT_NULL_IDX)
+	{
+		if (m_nodes[i].id == id && m_nodes[i].state == state)
 			return &m_nodes[i];
 		i = m_next[i];
 	}
 	return 0;
 }
 
-dtNode* dtNodePool::getNode(dtPolyRef id)
+dtNode* dtNodePool::getNode(dtPolyRef id, unsigned char state)
 {
 	unsigned int bucket = dtHashRef(id) & (m_hashSize-1);
 	dtNodeIndex i = m_first[bucket];
 	dtNode* node = 0;
 	while (i != DT_NULL_IDX)
 	{
-		if (m_nodes[i].id == id)
+		if (m_nodes[i].id == id && m_nodes[i].state == state)
 			return &m_nodes[i];
 		i = m_next[i];
 	}
@@ -121,6 +142,7 @@ dtNode* dtNodePool::getNode(dtPolyRef id)
 	node->cost = 0;
 	node->total = 0;
 	node->id = id;
+	node->state = state;
 	node->flags = 0;
 	
 	m_next[i] = m_first[bucket];

+ 0 - 21
Source/ThirdParty/Detour/include/DetourMath.h

@@ -1,21 +0,0 @@
-#ifndef DETOURMATH_H
-#define DETOURMATH_H
-
-/**
-@defgroup detour Detour
-
-Members in this module are wrappers around the standard math library
-
-*/
-
-#include <math.h>
-
-#define dtMathFabs(x) fabs(x)
-#define dtMathSqrtf(x) sqrtf(x)
-#define dtMathFloorf(x) floorf(x)
-#define dtMathCeilf(x) ceilf(x)
-#define dtMathCosf(x) cosf(x)
-#define dtMathSinf(x) sinf(x)
-#define dtMathAtan2f(y, x) atan2f(y, x)
-
-#endif

+ 3 - 3
Source/ThirdParty/DetourCrowd/CMakeLists.txt

@@ -24,13 +24,13 @@
 set (TARGET_NAME DetourCrowd)
 
 # Define source files
-define_source_files (GLOB_CPP_PATTERNS source/*.cpp GLOB_H_PATTERNS include/*.h)
+define_source_files (GLOB_CPP_PATTERNS Source/*.cpp GLOB_H_PATTERNS Include/*.h)
 
 # Define dependency libs
-set (INCLUDE_DIRS include ../Detour/include)
+set (INCLUDE_DIRS Include ../Detour/Include)
 
 # Setup target
 setup_library ()
 
 # Install headers for building the Urho3D library
-install_header_files (DIRECTORY include/ DESTINATION ${DEST_INCLUDE_DIR}/ThirdParty/DetourCrowd FILES_MATCHING PATTERN *.h BUILD_TREE_ONLY)  # Note: the trailing slash is significant
+install_header_files (DIRECTORY Include/ DESTINATION ${DEST_INCLUDE_DIR}/ThirdParty/DetourCrowd FILES_MATCHING PATTERN *.h BUILD_TREE_ONLY)  # Note: the trailing slash is significant

+ 21 - 11
Source/ThirdParty/DetourCrowd/include/DetourCrowd.h → Source/ThirdParty/DetourCrowd/Include/DetourCrowd.h

@@ -145,10 +145,10 @@ struct dtCrowdAgent
 	float desiredSpeed;
 
 	float npos[3];		///< The current agent position. [(x, y, z)]
-	float disp[3];
-	float dvel[3];		///< The desired velocity of the agent. [(x, y, z)]
-	float nvel[3];
-	float vel[3];		///< The actual velocity of the agent. [(x, y, z)]
+	float disp[3];		///< A temporary value used to accumulate agent displacement during iterative collision resolution. [(x, y, z)]
+	float dvel[3];		///< The desired velocity of the agent. Based on the current path, calculated from scratch each frame. [(x, y, z)]
+	float nvel[3];		///< The desired velocity adjusted by obstacle avoidance, calculated from scratch each frame. [(x, y, z)]
+	float vel[3];		///< The actual velocity of the agent. The change from nvel -> vel is constrained by max acceleration. [(x, y, z)]
 
 	/// The agent's configuration parameters.
 	dtCrowdAgentParams params;
@@ -208,7 +208,7 @@ typedef void (*dtUpdateCallback)(dtCrowdAgent* ag, float dt);
 /// @ingroup crowd
 class dtCrowd
 {
-	dtUpdateCallback m_updateCallback;
+    dtUpdateCallback m_updateCallback; // Urho3D
 	int m_maxAgents;
 	dtCrowdAgent* m_agents;
 	dtCrowdAgent** m_activeAgents;
@@ -224,7 +224,7 @@ class dtCrowd
 	dtPolyRef* m_pathResult;
 	int m_maxPathResult;
 	
-	float m_ext[3];
+	float m_agentPlacementHalfExtents[3];
 
 	dtQueryFilter m_filters[DT_CROWD_MAX_QUERY_FILTER_TYPE];
 
@@ -243,11 +243,11 @@ class dtCrowd
 	bool requestMoveTargetReplan(const int idx, dtPolyRef ref, const float* pos);
 
 	void purge();
-
+	
 public:
 	dtCrowd();
 	~dtCrowd();
-	
+
 	// Urho3D: Add update callback support
 	/// Initializes the crowd.  
 	///  @param[in]		maxAgents		The maximum number of agents the crowd can manage. [Limit: >= 1]
@@ -339,9 +339,13 @@ public:
 	/// @return The filter used by the crowd.
 	inline dtQueryFilter* getEditableFilter(const int i) { return (i >= 0 && i < DT_CROWD_MAX_QUERY_FILTER_TYPE) ? &m_filters[i] : 0; }
 
-	/// Gets the search extents [(x, y, z)] used by the crowd for query operations. 
-	/// @return The search extents used by the crowd. [(x, y, z)]
-	const float* getQueryExtents() const { return m_ext; }
+	/// Gets the search halfExtents [(x, y, z)] used by the crowd for query operations. 
+	/// @return The search halfExtents used by the crowd. [(x, y, z)]
+	const float* getQueryHalfExtents() const { return m_agentPlacementHalfExtents; }
+
+	/// Same as getQueryHalfExtents. Left to maintain backwards compatibility.
+	/// @return The search halfExtents used by the crowd. [(x, y, z)]
+	const float* getQueryExtents() const { return m_agentPlacementHalfExtents; }
 	
 	/// Gets the velocity sample count.
 	/// @return The velocity sample count.
@@ -357,6 +361,11 @@ public:
 
 	/// Gets the query object used by the crowd.
 	const dtNavMeshQuery* getNavMeshQuery() const { return m_navquery; }
+
+private:
+	// Explicitly disabled copy constructor and copy assignment operator.
+	dtCrowd(const dtCrowd&);
+	dtCrowd& operator=(const dtCrowd&);
 };
 
 /// Allocates a crowd object using the Detour allocator.
@@ -462,3 +471,4 @@ A higher value will result in agents trying to stay farther away from each other
 the cost of more difficult steering in tight spaces.
 
 */
+

+ 6 - 1
Source/ThirdParty/DetourCrowd/include/DetourLocalBoundary.h → Source/ThirdParty/DetourCrowd/Include/DetourLocalBoundary.h

@@ -40,7 +40,7 @@ class dtLocalBoundary
 	dtPolyRef m_polys[MAX_LOCAL_POLYS];
 	int m_npolys;
 
-	void addSegment(const float dist, const float* seg);
+	void addSegment(const float dist, const float* s);
 	
 public:
 	dtLocalBoundary();
@@ -56,6 +56,11 @@ public:
 	inline const float* getCenter() const { return m_center; }
 	inline int getSegmentCount() const { return m_nsegs; }
 	inline const float* getSegment(int i) const { return m_segs[i].s; }
+
+private:
+	// Explicitly disabled copy constructor and copy assignment operator.
+	dtLocalBoundary(const dtLocalBoundary&);
+	dtLocalBoundary& operator=(const dtLocalBoundary&);
 };
 
 #endif // DETOURLOCALBOUNDARY_H

+ 8 - 3
Source/ThirdParty/DetourCrowd/include/DetourObstacleAvoidance.h → Source/ThirdParty/DetourCrowd/Include/DetourObstacleAvoidance.h

@@ -58,6 +58,10 @@ public:
 	inline float getSampleCollisionTimePenalty(const int i) const { return m_tpen[i]; }
 
 private:
+	// Explicitly disabled copy constructor and copy assignment operator.
+	dtObstacleAvoidanceDebugData(const dtObstacleAvoidanceDebugData&);
+	dtObstacleAvoidanceDebugData& operator=(const dtObstacleAvoidanceDebugData&);
+
 	int m_nsamples;
 	int m_maxSamples;
 	float* m_vel;
@@ -122,17 +126,18 @@ public:
 	const dtObstacleSegment* getObstacleSegment(const int i) { return &m_segments[i]; }
 
 private:
+	// Explicitly disabled copy constructor and copy assignment operator.
+	dtObstacleAvoidanceQuery(const dtObstacleAvoidanceQuery&);
+	dtObstacleAvoidanceQuery& operator=(const dtObstacleAvoidanceQuery&);
 
 	void prepare(const float* pos, const float* dvel);
 
 	float processSample(const float* vcand, const float cs,
 						const float* pos, const float rad,
 						const float* vel, const float* dvel,
+						const float minPenalty,
 						dtObstacleAvoidanceDebugData* debug);
 
-	dtObstacleCircle* insertCircle(const float dist);
-	dtObstacleSegment* insertSegment(const float dist);
-
 	dtObstacleAvoidanceParams m_params;
 	float m_invHorizTime;
 	float m_vmax;

+ 6 - 1
Source/ThirdParty/DetourCrowd/include/DetourPathCorridor.h → Source/ThirdParty/DetourCrowd/Include/DetourPathCorridor.h

@@ -131,7 +131,12 @@ public:
 
 	/// The number of polygons in the current corridor path.
 	/// @return The number of polygons in the current corridor path.
-	inline int getPathCount() const { return m_npath; } 	
+	inline int getPathCount() const { return m_npath; }
+
+private:
+	// Explicitly disabled copy constructor and copy assignment operator.
+	dtPathCorridor(const dtPathCorridor&);
+	dtPathCorridor& operator=(const dtPathCorridor&);
 };
 
 int dtMergeCorridorStartMoved(dtPolyRef* path, const int npath, const int maxPath,

+ 4 - 0
Source/ThirdParty/DetourCrowd/include/DetourPathQueue.h → Source/ThirdParty/DetourCrowd/Include/DetourPathQueue.h

@@ -70,6 +70,10 @@ public:
 	
 	inline const dtNavMeshQuery* getNavQuery() const { return m_navquery; }
 
+private:
+	// Explicitly disabled copy constructor and copy assignment operator.
+	dtPathQueue(const dtPathQueue&);
+	dtPathQueue& operator=(const dtPathQueue&);
 };
 
 #endif // DETOURPATHQUEUE_H

+ 6 - 2
Source/ThirdParty/DetourCrowd/include/DetourProximityGrid.h → Source/ThirdParty/DetourCrowd/Include/DetourProximityGrid.h

@@ -21,7 +21,6 @@
 
 class dtProximityGrid
 {
-	int m_maxItems;
 	float m_cellSize;
 	float m_invCellSize;
 	
@@ -44,7 +43,7 @@ public:
 	dtProximityGrid();
 	~dtProximityGrid();
 	
-	bool init(const int maxItems, const float cellSize);
+	bool init(const int poolSize, const float cellSize);
 	
 	void clear();
 	
@@ -60,6 +59,11 @@ public:
 	
 	inline const int* getBounds() const { return m_bounds; }
 	inline float getCellSize() const { return m_cellSize; }
+
+private:
+	// Explicitly disabled copy constructor and copy assignment operator.
+	dtProximityGrid(const dtProximityGrid&);
+	dtProximityGrid& operator=(const dtProximityGrid&);
 };
 
 dtProximityGrid* dtAllocProximityGrid();

+ 15 - 14
Source/ThirdParty/DetourCrowd/source/DetourCrowd.cpp → Source/ThirdParty/DetourCrowd/Source/DetourCrowd.cpp

@@ -208,7 +208,7 @@ static int getNeighbours(const float* pos, const float height, const float range
 		// Check for overlap.
 		float diff[3];
 		dtVsub(diff, pos, ag->npos);
-		if (dtMathFabs(diff[1]) >= (height+ag->params.height)/2.0f)
+		if (dtMathFabsf(diff[1]) >= (height+ag->params.height)/2.0f)
 			continue;
 		diff[1] = 0;
 		const float distSqr = dtVlenSqr(diff);
@@ -332,8 +332,7 @@ Notes:
 */
 
 dtCrowd::dtCrowd() :
-	// Urho3D: Add update callback support
-	m_updateCallback(0),
+	m_updateCallback(0), // Urho3D: Add update callback support
 	m_maxAgents(0),
 	m_agents(0),
 	m_activeAgents(0),
@@ -347,7 +346,7 @@ dtCrowd::dtCrowd() :
 	m_navquery(0)
 {
 	// Urho3D: initialize all class members
-	memset(&m_ext, 0, sizeof(m_ext));
+	memset(&m_agentPlacementHalfExtents, 0, sizeof(m_agentPlacementHalfExtents));
 	memset(&m_obstacleQueryParams, 0, sizeof(m_obstacleQueryParams));
 }
 
@@ -390,12 +389,13 @@ void dtCrowd::purge()
 bool dtCrowd::init(const int maxAgents, const float maxAgentRadius, dtNavMesh* nav, dtUpdateCallback cb)
 {
 	purge();
-
-	m_updateCallback = cb;
+	
+	m_updateCallback = cb; // Urho3D
 	m_maxAgents = maxAgents;
 	m_maxAgentRadius = maxAgentRadius;
 
-	dtVset(m_ext, m_maxAgentRadius*2.0f,m_maxAgentRadius*1.5f,m_maxAgentRadius*2.0f);
+	// Larger than agent radius because it is also used for agent recovery.
+	dtVset(m_agentPlacementHalfExtents, m_maxAgentRadius*2.0f, m_maxAgentRadius*1.5f, m_maxAgentRadius*2.0f);
 	
 	m_grid = dtAllocProximityGrid();
 	if (!m_grid)
@@ -540,7 +540,7 @@ int dtCrowd::addAgent(const float* pos, const dtCrowdAgentParams* params)
 	float nearest[3];
 	dtPolyRef ref = 0;
 	dtVcopy(nearest, pos);
-	dtStatus status = m_navquery->findNearestPoly(pos, m_ext, &m_filters[ag->params.queryFilterType], &ref, nearest);
+	dtStatus status = m_navquery->findNearestPoly(pos, m_agentPlacementHalfExtents, &m_filters[ag->params.queryFilterType], &ref, nearest);
 	if (dtStatusFailed(status))
 	{
 		dtVcopy(nearest, pos);
@@ -570,9 +570,9 @@ int dtCrowd::addAgent(const float* pos, const dtCrowdAgentParams* params)
 	ag->targetState = DT_CROWDAGENT_TARGET_NONE;
 	
 	ag->active = true;
-
-    // Urho3D: added to fix illegal memory access when ncorners is queried before the agent has updated
-    ag->ncorners = 0;
+	
+	// Urho3D: added to fix illegal memory access when ncorners is queried before the agent has updated
+	ag->ncorners = 0;
 
 	return idx;
 }
@@ -665,6 +665,7 @@ bool dtCrowd::resetMoveTarget(const int idx)
 	// Initialize request.
 	ag->targetRef = 0;
 	dtVset(ag->targetPos, 0,0,0);
+	dtVset(ag->dvel, 0,0,0);
 	ag->targetPathqRef = DT_PATHQ_INVALID;
 	ag->targetReplan = false;
 	ag->targetState = DT_CROWDAGENT_TARGET_NONE;
@@ -976,7 +977,7 @@ void dtCrowd::checkPathValidity(dtCrowdAgent** agents, const int nagents, const
 			float nearest[3];
 			dtVcopy(nearest, agentPos);
 			agentRef = 0;
-			m_navquery->findNearestPoly(ag->npos, m_ext, &m_filters[ag->params.queryFilterType], &agentRef, nearest);
+			m_navquery->findNearestPoly(ag->npos, m_agentPlacementHalfExtents, &m_filters[ag->params.queryFilterType], &agentRef, nearest);
 			dtVcopy(agentPos, nearest);
 
 			if (!agentRef)
@@ -1012,7 +1013,7 @@ void dtCrowd::checkPathValidity(dtCrowdAgent** agents, const int nagents, const
 				float nearest[3];
 				dtVcopy(nearest, ag->targetPos);
 				ag->targetRef = 0;
-				m_navquery->findNearestPoly(ag->targetPos, m_ext, &m_filters[ag->params.queryFilterType], &ag->targetRef, nearest);
+				m_navquery->findNearestPoly(ag->targetPos, m_agentPlacementHalfExtents, &m_filters[ag->params.queryFilterType], &ag->targetRef, nearest);
 				dtVcopy(ag->targetPos, nearest);
 				replan = true;
 			}
@@ -1419,7 +1420,7 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
 			ag->corridor.reset(ag->corridor.getFirstPoly(), ag->npos);
 			ag->partial = false;
 		}
-
+		
 		// Urho3D: Add update callback support
 		if (m_updateCallback)
 			(*m_updateCallback)(ag, dt);

+ 1 - 1
Source/ThirdParty/DetourCrowd/source/DetourLocalBoundary.cpp → Source/ThirdParty/DetourCrowd/Source/DetourLocalBoundary.cpp

@@ -31,7 +31,7 @@ dtLocalBoundary::dtLocalBoundary() :
 	m_npolys(0)
 {
 	dtVset(m_center, FLT_MAX,FLT_MAX,FLT_MAX);
-
+	
 	// Urho3D: initialize all class members
 	memset(&m_segs, 0, sizeof(m_segs));
 	memset(&m_polys, 0, sizeof(m_polys));

+ 96 - 24
Source/ThirdParty/DetourCrowd/source/DetourObstacleAvoidance.cpp → Source/ThirdParty/DetourCrowd/Source/DetourObstacleAvoidance.cpp

@@ -46,7 +46,7 @@ static int sweepCircleCircle(const float* c0, const float r0, const float* v,
 	float d = b*b - a*c;
 	if (d < 0.0f) return 0; // no intersection.
 	a = 1.0f / a;
-	const float rd = dtSqrt(d);
+	const float rd = dtMathSqrtf(d);
 	tmin = (b - rd) * a;
 	tmax = (b + rd) * a;
 	return 1;
@@ -60,7 +60,7 @@ static int isectRaySeg(const float* ap, const float* u,
 	dtVsub(v,bq,bp);
 	dtVsub(w,ap,bp);
 	float d = dtVperp2D(u,v);
-	if (dtMathFabs(d) < 1e-6f) return 0;
+	if (dtMathFabsf(d) < 1e-6f) return 0;
 	d = 1.0f/d;
 	t = dtVperp2D(v,w) * d;
 	if (t < 0 || t > 1) return 0;
@@ -207,19 +207,19 @@ void dtFreeObstacleAvoidanceQuery(dtObstacleAvoidanceQuery* ptr)
 	dtFree(ptr);
 }
 
-// Urho3D: initialize all class members
+
 dtObstacleAvoidanceQuery::dtObstacleAvoidanceQuery() :
+	m_invHorizTime(0),
+	m_vmax(0),
+	m_invVmax(0),
 	m_maxCircles(0),
 	m_circles(0),
 	m_ncircles(0),
 	m_maxSegments(0),
 	m_segments(0),
-	m_nsegments(0),
-	m_invHorizTime(0), // Urho3D
-	m_vmax(0), // Urho3D
-	m_invVmax(0) // Urho3D
+	m_nsegments(0)
 {
-	memset(&m_params, 0, sizeof(m_params)); // Urho3D
+    memset(&m_params, 0, sizeof(m_params)); // Urho3D
 }
 
 dtObstacleAvoidanceQuery::~dtObstacleAvoidanceQuery()
@@ -268,7 +268,7 @@ void dtObstacleAvoidanceQuery::addCircle(const float* pos, const float rad,
 
 void dtObstacleAvoidanceQuery::addSegment(const float* p, const float* q)
 {
-	if (m_nsegments > m_maxSegments)
+	if (m_nsegments >= m_maxSegments)
 		return;
 	
 	dtObstacleSegment* seg = &m_segments[m_nsegments++];
@@ -287,7 +287,7 @@ void dtObstacleAvoidanceQuery::prepare(const float* pos, const float* dvel)
 		const float* pa = pos;
 		const float* pb = cir->p;
 		
-		const float orig[3] = {0,0};
+		const float orig[3] = {0,0,0};
 		float dv[3];
 		dtVsub(cir->dp,pb,pa);
 		dtVnormalize(cir->dp);
@@ -317,11 +317,30 @@ void dtObstacleAvoidanceQuery::prepare(const float* pos, const float* dvel)
 	}	
 }
 
+
+/* Calculate the collision penalty for a given velocity vector
+ * 
+ * @param vcand sampled velocity
+ * @param dvel desired velocity
+ * @param minPenalty threshold penalty for early out
+ */
 float dtObstacleAvoidanceQuery::processSample(const float* vcand, const float cs,
 											  const float* pos, const float rad,
 											  const float* vel, const float* dvel,
+											  const float minPenalty,
 											  dtObstacleAvoidanceDebugData* debug)
 {
+	// penalty for straying away from the desired and current velocities
+	const float vpen = m_params.weightDesVel * (dtVdist2D(vcand, dvel) * m_invVmax);
+	const float vcpen = m_params.weightCurVel * (dtVdist2D(vcand, vel) * m_invVmax);
+
+	// find the threshold hit time to bail out based on the early out penalty
+	// (see how the penalty is calculated below to understnad)
+	float minPen = minPenalty - vpen - vcpen;
+	float tThresold = (m_params.weightToi / minPen - 0.1f) * m_params.horizTime;
+	if (tThresold - m_params.horizTime > -FLT_EPSILON)
+		return minPenalty; // already too much
+
 	// Find min time of impact and exit amongst all obstacles.
 	float tmin = m_params.horizTime;
 	float side = 0;
@@ -356,7 +375,11 @@ float dtObstacleAvoidanceQuery::processSample(const float* vcand, const float cs
 		{
 			// The closest obstacle is somewhere ahead of us, keep track of nearest obstacle.
 			if (htmin < tmin)
+			{
 				tmin = htmin;
+				if (tmin < tThresold)
+					return minPenalty;
+			}
 		}
 	}
 
@@ -389,15 +412,17 @@ float dtObstacleAvoidanceQuery::processSample(const float* vcand, const float cs
 		
 		// The closest obstacle is somewhere ahead of us, keep track of nearest obstacle.
 		if (htmin < tmin)
+		{
 			tmin = htmin;
+			if (tmin < tThresold)
+				return minPenalty;
+		}
 	}
 	
 	// Normalize side bias, to prevent it dominating too much.
 	if (nside)
 		side /= nside;
 	
-	const float vpen = m_params.weightDesVel * (dtVdist2D(vcand, dvel) * m_invVmax);
-	const float vcpen = m_params.weightCurVel * (dtVdist2D(vcand, vel) * m_invVmax);
 	const float spen = m_params.weightSide * side;
 	const float tpen = m_params.weightToi * (1.0f/(0.1f+tmin*m_invHorizTime));
 	
@@ -420,7 +445,7 @@ int dtObstacleAvoidanceQuery::sampleVelocityGrid(const float* pos, const float r
 	memcpy(&m_params, params, sizeof(dtObstacleAvoidanceParams));
 	m_invHorizTime = 1.0f / m_params.horizTime;
 	m_vmax = vmax;
-	m_invVmax = 1.0f / vmax;
+	m_invVmax = vmax > 0 ? 1.0f / vmax : FLT_MAX;
 	
 	dtVset(nvel, 0,0,0);
 	
@@ -446,7 +471,7 @@ int dtObstacleAvoidanceQuery::sampleVelocityGrid(const float* pos, const float r
 			
 			if (dtSqr(vcand[0])+dtSqr(vcand[2]) > dtSqr(vmax+cs/2)) continue;
 			
-			const float penalty = processSample(vcand, cs, pos,rad,vel,dvel, debug);
+			const float penalty = processSample(vcand, cs, pos,rad,vel,dvel, minPenalty, debug);
 			ns++;
 			if (penalty < minPenalty)
 			{
@@ -460,6 +485,28 @@ int dtObstacleAvoidanceQuery::sampleVelocityGrid(const float* pos, const float r
 }
 
 
+// vector normalization that ignores the y-component.
+inline void dtNormalize2D(float* v)
+{
+	float d = dtMathSqrtf(v[0] * v[0] + v[2] * v[2]);
+	if (d==0)
+		return;
+	d = 1.0f / d;
+	v[0] *= d;
+	v[2] *= d;
+}
+
+// vector normalization that ignores the y-component.
+inline void dtRorate2D(float* dest, const float* v, float ang)
+{
+	float c = cosf(ang);
+	float s = sinf(ang);
+	dest[0] = v[0]*c - v[2]*s;
+	dest[2] = v[0]*s + v[2]*c;
+	dest[1] = v[1];
+}
+
+
 int dtObstacleAvoidanceQuery::sampleVelocityAdaptive(const float* pos, const float rad, const float vmax,
 													 const float* vel, const float* dvel, float* nvel,
 													 const dtObstacleAvoidanceParams* params,
@@ -470,7 +517,7 @@ int dtObstacleAvoidanceQuery::sampleVelocityAdaptive(const float* pos, const flo
 	memcpy(&m_params, params, sizeof(dtObstacleAvoidanceParams));
 	m_invHorizTime = 1.0f / m_params.horizTime;
 	m_vmax = vmax;
-	m_invVmax = 1.0f / vmax;
+	m_invVmax = vmax > 0 ? 1.0f / vmax : FLT_MAX;
 	
 	dtVset(nvel, 0,0,0);
 	
@@ -488,8 +535,15 @@ int dtObstacleAvoidanceQuery::sampleVelocityAdaptive(const float* pos, const flo
 	const int nd = dtClamp(ndivs, 1, DT_MAX_PATTERN_DIVS);
 	const int nr = dtClamp(nrings, 1, DT_MAX_PATTERN_RINGS);
 	const float da = (1.0f/nd) * DT_PI*2;
-	const float dang = dtMathAtan2f(dvel[2], dvel[0]);
-	
+	const float ca = cosf(da);
+	const float sa = sinf(da);
+
+	// desired direction
+	float ddir[6];
+	dtVcopy(ddir, dvel);
+	dtNormalize2D(ddir);
+	dtRorate2D (ddir+3, ddir, da*0.5f); // rotated by da/2
+
 	// Always add sample at zero
 	pat[npat*2+0] = 0;
 	pat[npat*2+1] = 0;
@@ -498,16 +552,35 @@ int dtObstacleAvoidanceQuery::sampleVelocityAdaptive(const float* pos, const flo
 	for (int j = 0; j < nr; ++j)
 	{
 		const float r = (float)(nr-j)/(float)nr;
-		float a = dang + (j&1)*0.5f*da;
-		for (int i = 0; i < nd; ++i)
+		pat[npat*2+0] = ddir[(j%2)*3] * r;
+		pat[npat*2+1] = ddir[(j%2)*3+2] * r;
+		float* last1 = pat + npat*2;
+		float* last2 = last1;
+		npat++;
+
+		for (int i = 1; i < nd-1; i+=2)
 		{
-			pat[npat*2+0] = cosf(a)*r;
-			pat[npat*2+1] = sinf(a)*r;
+			// get next point on the "right" (rotate CW)
+			pat[npat*2+0] = last1[0]*ca + last1[1]*sa;
+			pat[npat*2+1] = -last1[0]*sa + last1[1]*ca;
+			// get next point on the "left" (rotate CCW)
+			pat[npat*2+2] = last2[0]*ca - last2[1]*sa;
+			pat[npat*2+3] = last2[0]*sa + last2[1]*ca;
+
+			last1 = pat + npat*2;
+			last2 = last1 + 2;
+			npat += 2;
+		}
+
+		if ((nd&1) == 0)
+		{
+			pat[npat*2+2] = last2[0]*ca - last2[1]*sa;
+			pat[npat*2+3] = last2[0]*sa + last2[1]*ca;
 			npat++;
-			a += da;
 		}
 	}
 
+
 	// Start sampling.
 	float cr = vmax * (1.0f - m_params.velBias);
 	float res[3];
@@ -529,7 +602,7 @@ int dtObstacleAvoidanceQuery::sampleVelocityAdaptive(const float* pos, const flo
 			
 			if (dtSqr(vcand[0])+dtSqr(vcand[2]) > dtSqr(vmax+0.001f)) continue;
 			
-			const float penalty = processSample(vcand,cr/10, pos,rad,vel,dvel, debug);
+			const float penalty = processSample(vcand,cr/10, pos,rad,vel,dvel, minPenalty, debug);
 			ns++;
 			if (penalty < minPenalty)
 			{
@@ -547,4 +620,3 @@ int dtObstacleAvoidanceQuery::sampleVelocityAdaptive(const float* pos, const flo
 	
 	return ns;
 }
-

+ 2 - 2
Source/ThirdParty/DetourCrowd/source/DetourPathCorridor.cpp → Source/ThirdParty/DetourCrowd/Source/DetourPathCorridor.cpp

@@ -436,7 +436,7 @@ Behavior:
 - The new position will be located in the adjusted corridor's first polygon.
 
 The expected use case is that the desired position will be 'near' the current corridor. What is considered 'near' 
-depends on local polygon density, query search extents, etc.
+depends on local polygon density, query search half extents, etc.
 
 The resulting position will differ from the desired position if the desired position is not on the navigation mesh, 
 or it can't be reached using a local search.
@@ -475,7 +475,7 @@ Behavior:
 - The corridor is automatically adjusted (shorted or lengthened) in order to remain valid. 
 - The new target will be located in the adjusted corridor's last polygon.
 
-The expected use case is that the desired target will be 'near' the current corridor. What is considered 'near' depends on local polygon density, query search extents, etc.
+The expected use case is that the desired target will be 'near' the current corridor. What is considered 'near' depends on local polygon density, query search half extents, etc.
 
 The resulting target will differ from the desired target if the desired target is not on the navigation mesh, or it can't be reached using a local search.
 */

+ 0 - 0
Source/ThirdParty/DetourCrowd/source/DetourPathQueue.cpp → Source/ThirdParty/DetourCrowd/Source/DetourPathQueue.cpp


+ 3 - 5
Source/ThirdParty/DetourCrowd/source/DetourProximityGrid.cpp → Source/ThirdParty/DetourCrowd/Source/DetourProximityGrid.cpp

@@ -47,18 +47,16 @@ inline int hashPos2(int x, int y, int n)
 	return ((x*73856093) ^ (y*19349663)) & (n-1);
 }
 
-// Urho3D: initialize all class members
 dtProximityGrid::dtProximityGrid() :
-	m_maxItems(0),
 	m_cellSize(0),
+	m_invCellSize(0),
 	m_pool(0),
 	m_poolHead(0),
 	m_poolSize(0),
 	m_buckets(0),
-	m_bucketsSize(0),
-	m_invCellSize(0) // Urho3D
+	m_bucketsSize(0)
 {
-	memset(&m_bounds, 0, sizeof(m_bounds));	// Urho3D
+    memset(&m_bounds, 0, sizeof(m_bounds));	// Urho3D
 }
 
 dtProximityGrid::~dtProximityGrid()

+ 3 - 3
Source/ThirdParty/DetourTileCache/CMakeLists.txt

@@ -24,13 +24,13 @@
 set (TARGET_NAME DetourTileCache)
 
 # Define source files
-define_source_files (GLOB_CPP_PATTERNS source/*.cpp GLOB_H_PATTERNS include/*.h)
+define_source_files (GLOB_CPP_PATTERNS Source/*.cpp GLOB_H_PATTERNS Include/*.h)
 
 # Define dependency libs
-set (INCLUDE_DIRS include ../Detour/include)
+set (INCLUDE_DIRS Include ../Detour/Include)
 
 # Setup target
 setup_library ()
 
 # Install headers for building the Urho3D library
-install_header_files (DIRECTORY include/ DESTINATION ${DEST_INCLUDE_DIR}/ThirdParty/DetourTileCache FILES_MATCHING PATTERN *.h BUILD_TREE_ONLY)  # Note: the trailing slash is significant
+install_header_files (DIRECTORY Include/ DESTINATION ${DEST_INCLUDE_DIR}/ThirdParty/DetourTileCache FILES_MATCHING PATTERN *.h BUILD_TREE_ONLY)  # Note: the trailing slash is significant

+ 57 - 7
Source/ThirdParty/DetourTileCache/include/DetourTileCache.h → Source/ThirdParty/DetourTileCache/Include/DetourTileCache.h

@@ -37,13 +37,47 @@ enum ObstacleState
 	DT_OBSTACLE_REMOVING,
 };
 
+enum ObstacleType
+{
+	DT_OBSTACLE_CYLINDER,
+	DT_OBSTACLE_BOX, // AABB
+	DT_OBSTACLE_ORIENTED_BOX, // OBB
+};
+
+struct dtObstacleCylinder
+{
+	float pos[ 3 ];
+	float radius;
+	float height;
+};
+
+struct dtObstacleBox
+{
+	float bmin[ 3 ];
+	float bmax[ 3 ];
+};
+
+struct dtObstacleOrientedBox
+{
+	float center[ 3 ];
+	float halfExtents[ 3 ];
+	float rotAux[ 2 ]; //{ cos(0.5f*angle)*sin(-0.5f*angle); cos(0.5f*angle)*cos(0.5f*angle) - 0.5 }
+};
+
 static const int DT_MAX_TOUCHED_TILES = 8;
 struct dtTileCacheObstacle
 {
-	float pos[3], radius, height;
+	union
+	{
+		dtObstacleCylinder cylinder;
+		dtObstacleBox box;
+		dtObstacleOrientedBox orientedBox;
+	};
+
 	dtCompressedTileRef touched[DT_MAX_TOUCHED_TILES];
 	dtCompressedTileRef pending[DT_MAX_TOUCHED_TILES];
 	unsigned short salt;
+	unsigned char type;
 	unsigned char state;
 	unsigned char ntouched;
 	unsigned char npending;
@@ -107,13 +141,27 @@ public:
 	
 	dtStatus removeTile(dtCompressedTileRef ref, unsigned char** data, int* dataSize);
 	
+	// Cylinder obstacle.
 	dtStatus addObstacle(const float* pos, const float radius, const float height, dtObstacleRef* result);
+
+	// Aabb obstacle.
+	dtStatus addBoxObstacle(const float* bmin, const float* bmax, dtObstacleRef* result);
+
+	// Box obstacle: can be rotated in Y.
+	dtStatus addBoxObstacle(const float* center, const float* halfExtents, const float yRadians, dtObstacleRef* result);
+	
 	dtStatus removeObstacle(const dtObstacleRef ref);
 	
 	dtStatus queryTiles(const float* bmin, const float* bmax,
 						dtCompressedTileRef* results, int* resultCount, const int maxResults) const;
 	
-	dtStatus update(const float /*dt*/, class dtNavMesh* navmesh);
+	/// Updates the tile cache by rebuilding tiles touched by unfinished obstacle requests.
+	///  @param[in]		dt			The time step size. Currently not used.
+	///  @param[in]		navmesh		The mesh to affect when rebuilding tiles.
+	///  @param[out]	upToDate	Whether the tile cache is fully up to date with obstacle requests and tile rebuilds.
+	///  							If the tile cache is up to date another (immediate) call to update will have no effect;
+	///  							otherwise another call will continue processing obstacle requests and tile rebuilds.
+	dtStatus update(const float dt, class dtNavMesh* navmesh, bool* upToDate = 0);
 	
 	dtStatus buildNavMeshTilesAt(const int tx, const int ty, class dtNavMesh* navmesh);
 	
@@ -122,9 +170,9 @@ public:
 	void calcTightTileBounds(const struct dtTileCacheLayerHeader* header, float* bmin, float* bmax) const;
 	
 	void getObstacleBounds(const struct dtTileCacheObstacle* ob, float* bmin, float* bmax) const;
-	
-    // Urho3D: added function to know when we have too many obstacle requests without update
-    bool isObstacleQueueFull() const { return m_nreqs >= MAX_REQUESTS; }
+
+	// Urho3D: added function to know when we have too many obstacle requests without update
+	bool isObstacleQueueFull() const { return m_nreqs >= MAX_REQUESTS; }
 
 	/// Encodes a tile id.
 	inline dtCompressedTileRef encodeTileId(unsigned int salt, unsigned int it) const
@@ -168,7 +216,10 @@ public:
 	
 	
 private:
-	
+	// Explicitly disabled copy constructor and copy assignment operator.
+	dtTileCache(const dtTileCache&);
+	dtTileCache& operator=(const dtTileCache&);
+
 	enum ObstacleRequestAction
 	{
 		REQUEST_ADD,
@@ -207,7 +258,6 @@ private:
 	static const int MAX_UPDATE = 64;
 	dtCompressedTileRef m_update[MAX_UPDATE];
 	int m_nupdate;
-	
 };
 
 dtTileCache* dtAllocTileCache();

+ 9 - 5
Source/ThirdParty/DetourTileCache/include/DetourTileCacheBuilder.h → Source/ThirdParty/DetourTileCache/Include/DetourTileCacheBuilder.h

@@ -78,13 +78,11 @@ struct dtTileCachePolyMesh
 
 struct dtTileCacheAlloc
 {
-	virtual ~dtTileCacheAlloc() { }
+	virtual ~dtTileCacheAlloc() {}
 
-	virtual void reset()
-	{
-	}
+	virtual void reset() {}
 	
-	virtual void* alloc(const int size)
+	virtual void* alloc(const size_t size)
 	{
 		return dtAlloc(size, DT_ALLOC_TEMP);
 	}
@@ -129,6 +127,12 @@ void dtFreeTileCachePolyMesh(dtTileCacheAlloc* alloc, dtTileCachePolyMesh* lmesh
 dtStatus dtMarkCylinderArea(dtTileCacheLayer& layer, const float* orig, const float cs, const float ch,
 							const float* pos, const float radius, const float height, const unsigned char areaId);
 
+dtStatus dtMarkBoxArea(dtTileCacheLayer& layer, const float* orig, const float cs, const float ch,
+					   const float* bmin, const float* bmax, const unsigned char areaId);
+
+dtStatus dtMarkBoxArea(dtTileCacheLayer& layer, const float* orig, const float cs, const float ch,
+					   const float* center, const float* halfExtents, const float* rotAux, const unsigned char areaId);
+
 dtStatus dtBuildTileCacheRegions(dtTileCacheAlloc* alloc,
 								 dtTileCacheLayer& layer,
 								 const int walkableClimb);

+ 143 - 28
Source/ThirdParty/DetourTileCache/source/DetourTileCache.cpp → Source/ThirdParty/DetourTileCache/Source/DetourTileCache.cpp

@@ -42,10 +42,10 @@ inline int computeTileHash(int x, int y, const int mask)
 }
 
 
-struct BuildContext
+struct NavMeshTileBuildContext
 {
-	inline BuildContext(struct dtTileCacheAlloc* a) : layer(0), lcset(0), lmesh(0), alloc(a) {}
-	inline ~BuildContext() { purge(); }
+	inline NavMeshTileBuildContext(struct dtTileCacheAlloc* a) : layer(0), lcset(0), lmesh(0), alloc(a) {}
+	inline ~NavMeshTileBuildContext() { purge(); }
 	void purge()
 	{
 		dtFreeTileCacheLayer(alloc, layer);
@@ -79,9 +79,9 @@ dtTileCache::dtTileCache() :
 	m_nupdate(0)
 {
 	memset(&m_params, 0, sizeof(m_params));
-
+	memset(m_reqs, 0, sizeof(ObstacleRequest) * MAX_REQUESTS);
+	
 	// Urho3D: initialize all class members
-	memset(&m_reqs, 0, sizeof(m_reqs));
 	memset(&m_update, 0, sizeof(m_update));
 }
 	
@@ -99,7 +99,7 @@ dtTileCache::~dtTileCache()
 			}
 		}
 	}
-	
+
 	dtFree(m_obstacles);
 	m_obstacles = 0;
 	dtFree(m_posLookup);
@@ -361,7 +361,42 @@ dtStatus dtTileCache::removeTile(dtCompressedTileRef ref, unsigned char** data,
 }
 
 
-dtObstacleRef dtTileCache::addObstacle(const float* pos, const float radius, const float height, dtObstacleRef* result)
+dtStatus dtTileCache::addObstacle(const float* pos, const float radius, const float height, dtObstacleRef* result)
+{
+	if (m_nreqs >= MAX_REQUESTS)
+		return DT_FAILURE | DT_BUFFER_TOO_SMALL;
+	
+	dtTileCacheObstacle* ob = 0;
+	if (m_nextFreeObstacle)
+	{
+		ob = m_nextFreeObstacle;
+		m_nextFreeObstacle = ob->next;
+		ob->next = 0;
+	}
+	if (!ob)
+		return DT_FAILURE | DT_OUT_OF_MEMORY;
+	
+	unsigned short salt = ob->salt;
+	memset(ob, 0, sizeof(dtTileCacheObstacle));
+	ob->salt = salt;
+	ob->state = DT_OBSTACLE_PROCESSING;
+	ob->type = DT_OBSTACLE_CYLINDER;
+	dtVcopy(ob->cylinder.pos, pos);
+	ob->cylinder.radius = radius;
+	ob->cylinder.height = height;
+	
+	ObstacleRequest* req = &m_reqs[m_nreqs++];
+	memset(req, 0, sizeof(ObstacleRequest));
+	req->action = REQUEST_ADD;
+	req->ref = getObstacleRef(ob);
+	
+	if (result)
+		*result = req->ref;
+	
+	return DT_SUCCESS;
+}
+
+dtStatus dtTileCache::addBoxObstacle(const float* bmin, const float* bmax, dtObstacleRef* result)
 {
 	if (m_nreqs >= MAX_REQUESTS)
 		return DT_FAILURE | DT_BUFFER_TOO_SMALL;
@@ -380,9 +415,9 @@ dtObstacleRef dtTileCache::addObstacle(const float* pos, const float radius, con
 	memset(ob, 0, sizeof(dtTileCacheObstacle));
 	ob->salt = salt;
 	ob->state = DT_OBSTACLE_PROCESSING;
-	dtVcopy(ob->pos, pos);
-	ob->radius = radius;
-	ob->height = height;
+	ob->type = DT_OBSTACLE_BOX;
+	dtVcopy(ob->box.bmin, bmin);
+	dtVcopy(ob->box.bmax, bmax);
 	
 	ObstacleRequest* req = &m_reqs[m_nreqs++];
 	memset(req, 0, sizeof(ObstacleRequest));
@@ -395,7 +430,46 @@ dtObstacleRef dtTileCache::addObstacle(const float* pos, const float radius, con
 	return DT_SUCCESS;
 }
 
-dtObstacleRef dtTileCache::removeObstacle(const dtObstacleRef ref)
+dtStatus dtTileCache::addBoxObstacle(const float* center, const float* halfExtents, const float yRadians, dtObstacleRef* result)
+{
+	if (m_nreqs >= MAX_REQUESTS)
+		return DT_FAILURE | DT_BUFFER_TOO_SMALL;
+
+	dtTileCacheObstacle* ob = 0;
+	if (m_nextFreeObstacle)
+	{
+		ob = m_nextFreeObstacle;
+		m_nextFreeObstacle = ob->next;
+		ob->next = 0;
+	}
+	if (!ob)
+		return DT_FAILURE | DT_OUT_OF_MEMORY;
+
+	unsigned short salt = ob->salt;
+	memset(ob, 0, sizeof(dtTileCacheObstacle));
+	ob->salt = salt;
+	ob->state = DT_OBSTACLE_PROCESSING;
+	ob->type = DT_OBSTACLE_ORIENTED_BOX;
+	dtVcopy(ob->orientedBox.center, center);
+	dtVcopy(ob->orientedBox.halfExtents, halfExtents);
+
+	float coshalf= cosf(0.5f*yRadians);
+	float sinhalf = sinf(-0.5f*yRadians);
+	ob->orientedBox.rotAux[0] = coshalf*sinhalf;
+	ob->orientedBox.rotAux[1] = coshalf*coshalf - 0.5f;
+
+	ObstacleRequest* req = &m_reqs[m_nreqs++];
+	memset(req, 0, sizeof(ObstacleRequest));
+	req->action = REQUEST_ADD;
+	req->ref = getObstacleRef(ob);
+
+	if (result)
+		*result = req->ref;
+
+	return DT_SUCCESS;
+}
+
+dtStatus dtTileCache::removeObstacle(const dtObstacleRef ref)
 {
 	if (!ref)
 		return DT_SUCCESS;
@@ -451,7 +525,8 @@ dtStatus dtTileCache::queryTiles(const float* bmin, const float* bmax,
 	return DT_SUCCESS;
 }
 
-dtStatus dtTileCache::update(const float /*dt*/, dtNavMesh* navmesh)
+dtStatus dtTileCache::update(const float /*dt*/, dtNavMesh* navmesh,
+							 bool* upToDate)
 {
 	if (m_nupdate == 0)
 	{
@@ -510,12 +585,13 @@ dtStatus dtTileCache::update(const float /*dt*/, dtNavMesh* navmesh)
 		m_nreqs = 0;
 	}
 	
+	dtStatus status = DT_SUCCESS;
 	// Process updates
 	if (m_nupdate)
 	{
 		// Build mesh
 		const dtCompressedTileRef ref = m_update[0];
-		dtStatus status = buildNavMeshTile(ref, navmesh);
+		status = buildNavMeshTile(ref, navmesh);
 		m_nupdate--;
 		if (m_nupdate > 0)
 			memmove(m_update, m_update+1, m_nupdate*sizeof(dtCompressedTileRef));
@@ -558,12 +634,12 @@ dtStatus dtTileCache::update(const float /*dt*/, dtNavMesh* navmesh)
 				}
 			}
 		}
-			
-		if (dtStatusFailed(status))
-			return status;
 	}
 	
-	return DT_SUCCESS;
+	if (upToDate)
+		*upToDate = m_nupdate == 0 && m_nreqs == 0;
+
+	return status;
 }
 
 
@@ -598,7 +674,7 @@ dtStatus dtTileCache::buildNavMeshTile(const dtCompressedTileRef ref, dtNavMesh*
 	
 	m_talloc->reset();
 	
-	BuildContext bc(m_talloc);
+	NavMeshTileBuildContext bc(m_talloc);
 	const int walkableClimbVx = (int)(m_params.walkableClimb / m_params.ch);
 	dtStatus status;
 	
@@ -615,8 +691,21 @@ dtStatus dtTileCache::buildNavMeshTile(const dtCompressedTileRef ref, dtNavMesh*
 			continue;
 		if (contains(ob->touched, ob->ntouched, ref))
 		{
-			dtMarkCylinderArea(*bc.layer, tile->header->bmin, m_params.cs, m_params.ch,
-							   ob->pos, ob->radius, ob->height, 0);
+			if (ob->type == DT_OBSTACLE_CYLINDER)
+			{
+				dtMarkCylinderArea(*bc.layer, tile->header->bmin, m_params.cs, m_params.ch,
+							    ob->cylinder.pos, ob->cylinder.radius, ob->cylinder.height, 0);
+			}
+			else if (ob->type == DT_OBSTACLE_BOX)
+			{
+				dtMarkBoxArea(*bc.layer, tile->header->bmin, m_params.cs, m_params.ch,
+					ob->box.bmin, ob->box.bmax, 0);
+			}
+			else if (ob->type == DT_OBSTACLE_ORIENTED_BOX)
+			{
+				dtMarkBoxArea(*bc.layer, tile->header->bmin, m_params.cs, m_params.ch,
+					ob->orientedBox.center, ob->orientedBox.halfExtents, ob->orientedBox.rotAux, 0);
+			}
 		}
 	}
 	
@@ -627,7 +716,7 @@ dtStatus dtTileCache::buildNavMeshTile(const dtCompressedTileRef ref, dtNavMesh*
 	
 	bc.lcset = dtAllocTileCacheContourSet(m_talloc);
 	if (!bc.lcset)
-		return status;
+		return DT_FAILURE | DT_OUT_OF_MEMORY;
 	status = dtBuildTileCacheContours(m_talloc, *bc.layer, walkableClimbVx,
 									  m_params.maxSimplificationError, *bc.lcset);
 	if (dtStatusFailed(status))
@@ -635,14 +724,18 @@ dtStatus dtTileCache::buildNavMeshTile(const dtCompressedTileRef ref, dtNavMesh*
 	
 	bc.lmesh = dtAllocTileCachePolyMesh(m_talloc);
 	if (!bc.lmesh)
-		return status;
+		return DT_FAILURE | DT_OUT_OF_MEMORY;
 	status = dtBuildTileCachePolyMesh(m_talloc, *bc.lcset, *bc.lmesh);
 	if (dtStatusFailed(status))
 		return status;
 	
 	// Early out if the mesh tile is empty.
 	if (!bc.lmesh->npolys)
+	{
+		// Remove existing tile.
+		navmesh->removeTile(navmesh->getTileRefAt(tile->header->tx,tile->header->ty,tile->header->tlayer),0,0);
 		return DT_SUCCESS;
+	}
 	
 	dtNavMeshCreateParams params;
 	memset(&params, 0, sizeof(params));
@@ -706,10 +799,32 @@ void dtTileCache::calcTightTileBounds(const dtTileCacheLayerHeader* header, floa
 
 void dtTileCache::getObstacleBounds(const struct dtTileCacheObstacle* ob, float* bmin, float* bmax) const
 {
-	bmin[0] = ob->pos[0] - ob->radius;
-	bmin[1] = ob->pos[1];
-	bmin[2] = ob->pos[2] - ob->radius;
-	bmax[0] = ob->pos[0] + ob->radius;
-	bmax[1] = ob->pos[1] + ob->height;
-	bmax[2] = ob->pos[2] + ob->radius;	
+	if (ob->type == DT_OBSTACLE_CYLINDER)
+	{
+		const dtObstacleCylinder &cl = ob->cylinder;
+
+		bmin[0] = cl.pos[0] - cl.radius;
+		bmin[1] = cl.pos[1];
+		bmin[2] = cl.pos[2] - cl.radius;
+		bmax[0] = cl.pos[0] + cl.radius;
+		bmax[1] = cl.pos[1] + cl.height;
+		bmax[2] = cl.pos[2] + cl.radius;
+	}
+	else if (ob->type == DT_OBSTACLE_BOX)
+	{
+		dtVcopy(bmin, ob->box.bmin);
+		dtVcopy(bmax, ob->box.bmax);
+	}
+	else if (ob->type == DT_OBSTACLE_ORIENTED_BOX)
+	{
+		const dtObstacleOrientedBox &orientedBox = ob->orientedBox;
+
+		float maxr = 1.41f*dtMax(orientedBox.halfExtents[0], orientedBox.halfExtents[2]);
+		bmin[0] = orientedBox.center[0] - maxr;
+		bmax[0] = orientedBox.center[0] + maxr;
+		bmin[1] = orientedBox.center[1] - orientedBox.halfExtents[1];
+		bmax[1] = orientedBox.center[1] + orientedBox.halfExtents[1];
+		bmin[2] = orientedBox.center[2] - maxr;
+		bmax[2] = orientedBox.center[2] + maxr;
+	}
 }

+ 102 - 3
Source/ThirdParty/DetourTileCache/source/DetourTileCacheBuilder.cpp → Source/ThirdParty/DetourTileCache/Source/DetourTileCacheBuilder.cpp

@@ -29,9 +29,7 @@ template<class T> class dtFixedArray
 	dtTileCacheAlloc* m_alloc;
 	T* m_ptr;
 	const int m_size;
-	inline T* operator=(T* p);
 	inline void operator=(dtFixedArray<T>& p);
-	inline dtFixedArray();
 public:
 	inline dtFixedArray(dtTileCacheAlloc* a, const int s) : m_alloc(a), m_ptr((T*)a->alloc(sizeof(T)*s)), m_size(s) {}
 	inline ~dtFixedArray() { if (m_alloc) m_alloc->free(m_ptr); }
@@ -1068,6 +1066,7 @@ static bool buildMeshAdjacency(dtTileCacheAlloc* alloc,
 }
 
 
+// Last time I checked the if version got compiled using cmov, which was a lot faster than module (with idiv).
 inline int prev(int i, int n) { return i-1 >= 0 ? i-1 : n-1; }
 inline int next(int i, int n) { return i+1 < n ? i+1 : 0; }
 
@@ -2003,6 +2002,98 @@ dtStatus dtMarkCylinderArea(dtTileCacheLayer& layer, const float* orig, const fl
 	return DT_SUCCESS;
 }
 
+dtStatus dtMarkBoxArea(dtTileCacheLayer& layer, const float* orig, const float cs, const float ch,
+					   const float* bmin, const float* bmax, const unsigned char areaId)
+{
+	const int w = (int)layer.header->width;
+	const int h = (int)layer.header->height;
+	const float ics = 1.0f/cs;
+	const float ich = 1.0f/ch;
+
+	int minx = (int)floorf((bmin[0]-orig[0])*ics);
+	int miny = (int)floorf((bmin[1]-orig[1])*ich);
+	int minz = (int)floorf((bmin[2]-orig[2])*ics);
+	int maxx = (int)floorf((bmax[0]-orig[0])*ics);
+	int maxy = (int)floorf((bmax[1]-orig[1])*ich);
+	int maxz = (int)floorf((bmax[2]-orig[2])*ics);
+	
+	if (maxx < 0) return DT_SUCCESS;
+	if (minx >= w) return DT_SUCCESS;
+	if (maxz < 0) return DT_SUCCESS;
+	if (minz >= h) return DT_SUCCESS;
+
+	if (minx < 0) minx = 0;
+	if (maxx >= w) maxx = w-1;
+	if (minz < 0) minz = 0;
+	if (maxz >= h) maxz = h-1;
+	
+	for (int z = minz; z <= maxz; ++z)
+	{
+		for (int x = minx; x <= maxx; ++x)
+		{
+			const int y = layer.heights[x+z*w];
+			if (y < miny || y > maxy)
+				continue;
+			layer.areas[x+z*w] = areaId;
+		}
+	}
+
+	return DT_SUCCESS;
+}
+
+dtStatus dtMarkBoxArea(dtTileCacheLayer& layer, const float* orig, const float cs, const float ch,
+					   const float* center, const float* halfExtents, const float* rotAux, const unsigned char areaId)
+{
+	const int w = (int)layer.header->width;
+	const int h = (int)layer.header->height;
+	const float ics = 1.0f/cs;
+	const float ich = 1.0f/ch;
+
+	float cx = (center[0] - orig[0])*ics;
+	float cz = (center[2] - orig[2])*ics;
+	
+	float maxr = 1.41f*dtMax(halfExtents[0], halfExtents[2]);
+	int minx = (int)floorf(cx - maxr*ics);
+	int maxx = (int)floorf(cx + maxr*ics);
+	int minz = (int)floorf(cz - maxr*ics);
+	int maxz = (int)floorf(cz + maxr*ics);
+	int miny = (int)floorf((center[1]-halfExtents[1]-orig[1])*ich);
+	int maxy = (int)floorf((center[1]+halfExtents[1]-orig[1])*ich);
+
+	if (maxx < 0) return DT_SUCCESS;
+	if (minx >= w) return DT_SUCCESS;
+	if (maxz < 0) return DT_SUCCESS;
+	if (minz >= h) return DT_SUCCESS;
+
+	if (minx < 0) minx = 0;
+	if (maxx >= w) maxx = w-1;
+	if (minz < 0) minz = 0;
+	if (maxz >= h) maxz = h-1;
+	
+	float xhalf = halfExtents[0]*ics + 0.5f;
+	float zhalf = halfExtents[2]*ics + 0.5f;
+
+	for (int z = minz; z <= maxz; ++z)
+	{
+		for (int x = minx; x <= maxx; ++x)
+		{			
+			float x2 = 2.0f*(float(x) - cx);
+			float z2 = 2.0f*(float(z) - cz);
+			float xrot = rotAux[1]*x2 + rotAux[0]*z2;
+			if (xrot > xhalf || xrot < -xhalf)
+				continue;
+			float zrot = rotAux[1]*z2 - rotAux[0]*x2;
+			if (zrot > zhalf || zrot < -zhalf)
+				continue;
+			const int y = layer.heights[x+z*w];
+			if (y < miny || y > maxy)
+				continue;
+			layer.areas[x+z*w] = areaId;
+		}
+	}
+
+	return DT_SUCCESS;
+}
 
 dtStatus dtBuildTileCacheLayer(dtTileCacheCompressor* comp,
 							   dtTileCacheLayerHeader* header,
@@ -2026,7 +2117,11 @@ dtStatus dtBuildTileCacheLayer(dtTileCacheCompressor* comp,
 	const int bufferSize = gridSize*3;
 	unsigned char* buffer = (unsigned char*)dtAlloc(bufferSize, DT_ALLOC_TEMP);
 	if (!buffer)
+	{
+		dtFree(data);
 		return DT_FAILURE | DT_OUT_OF_MEMORY;
+	}
+
 	memcpy(buffer, heights, gridSize);
 	memcpy(buffer+gridSize, areas, gridSize);
 	memcpy(buffer+gridSize*2, cons, gridSize);
@@ -2037,7 +2132,11 @@ dtStatus dtBuildTileCacheLayer(dtTileCacheCompressor* comp,
 	int compressedSize = 0;
 	dtStatus status = comp->compress(buffer, bufferSize, compressed, maxCompressedSize, &compressedSize);
 	if (dtStatusFailed(status))
+	{
+		dtFree(buffer);
+		dtFree(data);
 		return status;
+	}
 
 	*outData = data;
 	*outDataSize = headerSize + compressedSize;
@@ -2097,7 +2196,7 @@ dtStatus dtDecompressTileCacheLayer(dtTileCacheAlloc* alloc, dtTileCacheCompress
 									   grids, gridsSize, &size);
 	if (dtStatusFailed(status))
 	{
-		dtFree(buffer);
+		alloc->free(buffer);
 		return status;
 	}
 	

+ 3 - 3
Source/ThirdParty/Recast/CMakeLists.txt

@@ -24,13 +24,13 @@
 set (TARGET_NAME Recast)
 
 # Define source files
-define_source_files (GLOB_CPP_PATTERNS source/*.cpp GLOB_H_PATTERNS include/*.h)
+define_source_files (GLOB_CPP_PATTERNS Source/*.cpp GLOB_H_PATTERNS Include/*.h)
 
 # Define dependency libs
-set (INCLUDE_DIRS include)
+set (INCLUDE_DIRS Include)
 
 # Setup target
 setup_library ()
 
 # Install headers for building the Urho3D library
-install_header_files (DIRECTORY include/ DESTINATION ${DEST_INCLUDE_DIR}/ThirdParty/Recast FILES_MATCHING PATTERN *.h BUILD_TREE_ONLY)  # Note: the trailing slash is significant
+install_header_files (DIRECTORY Include/ DESTINATION ${DEST_INCLUDE_DIR}/ThirdParty/Recast FILES_MATCHING PATTERN *.h BUILD_TREE_ONLY)  # Note: the trailing slash is significant

+ 91 - 18
Source/ThirdParty/Recast/include/Recast.h → Source/ThirdParty/Recast/Include/Recast.h

@@ -127,7 +127,7 @@ public:
 	inline void resetTimers() { if (m_timerEnabled) doResetTimers(); }
 
 	/// Starts the specified performance timer.
-	///  @param	label	The category of timer.
+	///  @param	label	The category of the timer.
 	inline void startTimer(const rcTimerLabel label) { if (m_timerEnabled) doStartTimer(label); }
 
 	/// Stops the specified performance timer.
@@ -173,6 +173,26 @@ protected:
 	bool m_timerEnabled;
 };
 
+/// A helper to first start a timer and then stop it when this helper goes out of scope.
+/// @see rcContext
+class rcScopedTimer
+{
+public:
+	/// Constructs an instance and starts the timer.
+	///  @param[in]		ctx		The context to use.
+	///  @param[in]		label	The category of the timer.
+	inline rcScopedTimer(rcContext* ctx, const rcTimerLabel label) : m_ctx(ctx), m_label(label) { m_ctx->startTimer(m_label); }
+	inline ~rcScopedTimer() { m_ctx->stopTimer(m_label); }
+
+private:
+	// Explicitly disabled copy constructor and copy assignment operator.
+	rcScopedTimer(const rcScopedTimer&);
+	rcScopedTimer& operator=(const rcScopedTimer&);
+	
+	rcContext* const m_ctx;
+	const rcTimerLabel m_label;
+};
+
 /// Specifies a configuration to use when performing Recast builds.
 /// @ingroup recast
 struct rcConfig
@@ -245,7 +265,7 @@ struct rcConfig
 /// Defines the number of bits allocated to rcSpan::smin and rcSpan::smax.
 static const int RC_SPAN_HEIGHT_BITS = 13;
 /// Defines the maximum value for rcSpan::smin and rcSpan::smax.
-static const int RC_SPAN_MAX_HEIGHT = (1<<RC_SPAN_HEIGHT_BITS)-1;
+static const int RC_SPAN_MAX_HEIGHT = (1 << RC_SPAN_HEIGHT_BITS) - 1;
 
 /// The number of spans allocated per span spool.
 /// @see rcSpanPool
@@ -255,10 +275,10 @@ static const int RC_SPANS_PER_POOL = 2048;
 /// @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.
+	unsigned int smin : RC_SPAN_HEIGHT_BITS; ///< The lower limit of the span. [Limit: < #smax]
+	unsigned int smax : RC_SPAN_HEIGHT_BITS; ///< The upper limit of the span. [Limit: <= #RC_SPAN_MAX_HEIGHT]
+	unsigned int area : 6;                   ///< The area id assigned to the span.
+	rcSpan* next;                            ///< The next span higher up in column.
 };
 
 /// A memory pool used for quick allocation of spans within a heightfield.
@@ -273,6 +293,9 @@ struct rcSpanPool
 /// @ingroup recast
 struct rcHeightfield
 {
+	rcHeightfield();
+	~rcHeightfield();
+
 	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)]
@@ -282,6 +305,11 @@ struct rcHeightfield
 	rcSpan** spans;		///< Heightfield of spans (width*height).
 	rcSpanPool* pools;	///< Linked list of span pools.
 	rcSpan* freelist;	///< The next free span.
+
+private:
+	// Explicitly-disabled copy constructor and copy assignment operator.
+	rcHeightfield(const rcHeightfield&);
+	rcHeightfield& operator=(const rcHeightfield&);
 };
 
 /// Provides information on the content of a cell column in a compact heightfield. 
@@ -304,6 +332,8 @@ struct rcCompactSpan
 /// @ingroup recast
 struct rcCompactHeightfield
 {
+	rcCompactHeightfield();
+	~rcCompactHeightfield();
 	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.
@@ -338,7 +368,7 @@ struct rcHeightfieldLayer
 	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* heights;		///< The heightfield. [Size: width * height]
 	unsigned char* areas;		///< Area ids. [Size: Same as #heights]
 	unsigned char* cons;		///< Packed neighbor connection information. [Size: Same as #heights]
 };
@@ -348,6 +378,8 @@ struct rcHeightfieldLayer
 /// @see rcAllocHeightfieldLayerSet, rcFreeHeightfieldLayerSet 
 struct rcHeightfieldLayerSet
 {
+	rcHeightfieldLayerSet();
+	~rcHeightfieldLayerSet();
 	rcHeightfieldLayer* layers;			///< The layers in the set. [Size: #nlayers]
 	int nlayers;						///< The number of layers in the set.
 };
@@ -367,6 +399,8 @@ struct rcContour
 /// @ingroup recast
 struct rcContourSet
 {
+	rcContourSet();
+	~rcContourSet();
 	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)]
@@ -376,12 +410,15 @@ struct rcContourSet
 	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.
+	float maxError;		///< The max edge error that this contour set was simplified with.
 };
 
 /// Represents a polygon mesh suitable for use in building a navigation mesh. 
 /// @ingroup recast
 struct rcPolyMesh
 {
+	rcPolyMesh();
+	~rcPolyMesh();
 	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]
@@ -396,6 +433,7 @@ struct rcPolyMesh
 	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.
+	float maxEdgeError;		///< The max error of the polygon edges in the mesh.
 };
 
 /// Contains triangle meshes that represent detailed height data associated 
@@ -497,6 +535,14 @@ void rcFreePolyMeshDetail(rcPolyMeshDetail* dmesh);
 /// @see rcCompactSpan::reg
 static const unsigned short RC_BORDER_REG = 0x8000;
 
+/// Polygon touches multiple regions.
+/// If a polygon has this region ID it was merged with or created
+/// from polygons of different regions during the polymesh
+/// build step that removes redundant border vertices. 
+/// (Used during the polymesh and detail polymesh build processes)
+/// @see rcPolyMesh::regs
+static const unsigned short RC_MULTIPLE_REGS = 0;
+
 /// Border vertex flag.
 /// 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 
@@ -747,6 +793,7 @@ void rcCalcGridSize(const float* bmin, const float* bmax, float cs, int* w, int*
 ///  @param[in]		bmax	The maximum bounds of the field's AABB. [(x, y, z)] [Units: wu]
 ///  @param[in]		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]
+///  @returns True if the operation completed successfully.
 bool rcCreateHeightfield(rcContext* ctx, rcHeightfield& hf, int width, int height,
 						 const float* bmin, const float* bmax,
 						 float cs, float ch);
@@ -790,7 +837,8 @@ void rcClearUnwalkableTriangles(rcContext* ctx, const float walkableSlopeAngle,
 ///  @param[in]		smax			The maximum height of the span. [Limit: <= #RC_SPAN_MAX_HEIGHT] [Units: vx]
 ///  @param[in]		area			The area id of the span. [Limit: <= #RC_WALKABLE_AREA)
 ///  @param[in]		flagMergeThr	The merge theshold. [Limit: >= 0] [Units: vx]
-void rcAddSpan(rcContext* ctx, rcHeightfield& hf, const int x, const int y,
+///  @returns True if the operation completed successfully.
+bool rcAddSpan(rcContext* ctx, rcHeightfield& hf, const int x, const int y,
 			   const unsigned short smin, const unsigned short smax,
 			   const unsigned char area, const int flagMergeThr);
 
@@ -804,7 +852,8 @@ void rcAddSpan(rcContext* ctx, rcHeightfield& hf, const int x, const int y,
 ///  @param[in,out]	solid			An initialized heightfield.
 ///  @param[in]		flagMergeThr	The distance where the walkable flag is favored over the non-walkable flag.
 ///  								[Limit: >= 0] [Units: vx]
-void rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const float* v2,
+///  @returns True if the operation completed successfully.
+bool rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const float* v2,
 						 const unsigned char area, rcHeightfield& solid,
 						 const int flagMergeThr = 1);
 
@@ -819,7 +868,8 @@ void rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const
 ///  @param[in,out]	solid			An initialized heightfield.
 ///  @param[in]		flagMergeThr	The distance where the walkable flag is favored over the non-walkable flag. 
 ///  								[Limit: >= 0] [Units: vx]
-void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int nv,
+///  @returns True if the operation completed successfully.
+bool rcRasterizeTriangles(rcContext* ctx, const float* verts, const int nv,
 						  const int* tris, const unsigned char* areas, const int nt,
 						  rcHeightfield& solid, const int flagMergeThr = 1);
 
@@ -834,7 +884,8 @@ void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int nv,
 ///  @param[in,out]	solid		An initialized heightfield.
 ///  @param[in]		flagMergeThr	The distance where the walkable flag is favored over the non-walkable flag. 
 ///  							[Limit: >= 0] [Units: vx]
-void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int nv,
+///  @returns True if the operation completed successfully.
+bool rcRasterizeTriangles(rcContext* ctx, const float* verts, const int nv,
 						  const unsigned short* tris, const unsigned char* areas, const int nt,
 						  rcHeightfield& solid, const int flagMergeThr = 1);
 
@@ -847,10 +898,11 @@ void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int nv,
 ///  @param[in,out]	solid			An initialized heightfield.
 ///  @param[in]		flagMergeThr	The distance where the walkable flag is favored over the non-walkable flag. 
 ///  								[Limit: >= 0] [Units: vx]
-void rcRasterizeTriangles(rcContext* ctx, const float* verts, const unsigned char* areas, const int nt,
+///  @returns True if the operation completed successfully.
+bool rcRasterizeTriangles(rcContext* ctx, const float* verts, const unsigned char* areas, const int nt,
 						  rcHeightfield& solid, const int flagMergeThr = 1);
 
-/// Marks non-walkable spans as walkable if their maximum is within @p walkableClimp of a walkable neihbor. 
+/// Marks non-walkable spans as walkable if their maximum is within @p walkableClimp of a walkable neighbor. 
 ///  @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. 
@@ -969,7 +1021,7 @@ void rcMarkCylinderArea(rcContext* ctx, const float* pos,
 ///  @returns True if the operation completed successfully.
 bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf);
 
-/// Builds region data for the heightfield using watershed partitioning. 
+/// 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.
@@ -983,6 +1035,18 @@ bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf);
 bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
 					const int borderSize, const int minRegionArea, const int mergeRegionArea);
 
+/// Builds region data for the heightfield by partitioning the heightfield in non-overlapping layers.
+///  @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].
+///  @returns True if the operation completed successfully.
+bool rcBuildLayerRegions(rcContext* ctx, rcCompactHeightfield& chf,
+						 const int borderSize, const int minRegionArea);
+
 /// Builds region data for the heightfield using simple monotone partitioning.
 ///  @ingroup recast 
 ///  @param[in,out]	ctx				The build context to use during the operation.
@@ -997,7 +1061,6 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
 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]
@@ -1026,7 +1089,7 @@ inline int rcGetCon(const rcCompactSpan& s, int dir)
 ///  	in the direction.
 inline int rcGetDirOffsetX(int dir)
 {
-	const int offset[4] = { -1, 0, 1, 0, };
+	static const int offset[4] = { -1, 0, 1, 0, };
 	return offset[dir&0x03];
 }
 
@@ -1036,10 +1099,20 @@ inline int rcGetDirOffsetX(int dir)
 ///  	in the direction.
 inline int rcGetDirOffsetY(int dir)
 {
-	const int offset[4] = { 0, 1, 0, -1 };
+	static const int offset[4] = { 0, 1, 0, -1 };
 	return offset[dir&0x03];
 }
 
+/// Gets the direction for the specified offset. One of x and y should be 0.
+///  @param[in]		x		The x offset. [Limits: -1 <= value <= 1]
+///  @param[in]		y		The y offset. [Limits: -1 <= value <= 1]
+///  @return The direction that represents the offset.
+inline int rcGetDirForOffset(int x, int y)
+{
+	static const int dirs[5] = { 3, 0, -1, 2, 1 };
+	return dirs[((y+1)<<1)+x];
+}
+
 /// @}
 /// @name Layer, Contour, Polymesh, and Detail Mesh Functions
 /// @see rcHeightfieldLayer, rcContourSet, rcPolyMesh, rcPolyMeshDetail
@@ -1072,7 +1145,7 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf,
 ///  @returns True if the operation completed successfully.
 bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
 					 const float maxError, const int maxEdgeLen,
-					 rcContourSet& cset, const int flags = RC_CONTOUR_TESS_WALL_EDGES);
+					 rcContourSet& cset, const int buildFlags = RC_CONTOUR_TESS_WALL_EDGES);
 
 /// Builds a polygon mesh from the provided contours.
 ///  @ingroup recast

+ 341 - 0
Source/ThirdParty/Recast/Include/RecastAlloc.h

@@ -0,0 +1,341 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen [email protected]
+//
+// 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
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <RecastAssert.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)(size_t size, rcAllocHint hint);
+
+/// A memory deallocation function.
+///  @param[in]		ptr		A pointer to a memory block previously allocated using #rcAllocFunc.
+/// @see rcAllocSetCustom
+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.
+/// @see rcFree
+void* rcAlloc(size_t size, rcAllocHint hint);
+
+/// Deallocates a memory block.
+///  @param[in]		ptr		A pointer to a memory block previously allocated using #rcAlloc.
+/// @see rcAlloc
+void rcFree(void* ptr);
+
+/// An implementation of operator new usable for placement new. The default one is part of STL (which we don't use).
+/// rcNewTag is a dummy type used to differentiate our operator from the STL one, in case users import both Recast
+/// and STL.
+struct rcNewTag {};
+inline void* operator new(size_t, const rcNewTag&, void* p) { return p; }
+
+/// Signed to avoid warnnings when comparing to int loop indexes, and common error with comparing to zero.
+/// MSVC2010 has a bug where ssize_t is unsigned (!!!).
+typedef intptr_t rcSizeType;
+#define RC_SIZE_MAX INTPTR_MAX
+
+/// Macros to hint to the compiler about the likeliest branch. Please add a benchmark that demonstrates a performance
+/// improvement before introducing use cases.
+#if defined(__GNUC__) || defined(__clang__)
+#define rcLikely(x) __builtin_expect((x), true)
+#define rcUnlikely(x) __builtin_expect((x), false)
+#else
+#define rcLikely(x) (x)
+#define rcUnlikely(x) (x)
+#endif
+
+/// Variable-sized storage type. Mimics the interface of std::vector<T> with some notable differences:
+///  * Uses rcAlloc()/rcFree() to handle storage.
+///  * No support for a custom allocator.
+///  * Uses signed size instead of size_t to avoid warnings in for loops: "for (int i = 0; i < foo.size(); i++)"
+///  * Omits methods of limited utility: insert/erase, (bad performance), at (we don't use exceptions), operator=.
+///  * assign() and the pre-sizing constructor follow C++11 semantics -- they don't construct a temporary if no value is provided.
+///  * push_back() and resize() support adding values from the current vector. Range-based constructors and assign(begin, end) do not.
+///  * No specialization for bool.
+template <typename T, rcAllocHint H>
+class rcVectorBase {
+	rcSizeType m_size;
+	rcSizeType m_cap;
+	T* m_data;
+	// Constructs a T at the give address with either the copy constructor or the default.
+	static void construct(T* p, const T& v) { ::new(rcNewTag(), (void*)p) T(v); }
+	static void construct(T* p) { ::new(rcNewTag(), (void*)p) T; }
+	static void construct_range(T* begin, T* end);
+	static void construct_range(T* begin, T* end, const T& value);
+	static void copy_range(T* dst, const T* begin, const T* end);
+	void destroy_range(rcSizeType begin, rcSizeType end);
+	// Creates an array of the given size, copies all of this vector's data into it, and returns it.
+	T* allocate_and_copy(rcSizeType size);
+	void resize_impl(rcSizeType size, const T* value);
+ public:
+	typedef rcSizeType size_type;
+	typedef T value_type;
+
+	rcVectorBase() : m_size(0), m_cap(0), m_data(0) {};
+	rcVectorBase(const rcVectorBase<T, H>& other) : m_size(0), m_cap(0), m_data(0) { assign(other.begin(), other.end()); }
+	explicit rcVectorBase(rcSizeType count) : m_size(0), m_cap(0), m_data(0) { resize(count); }
+	rcVectorBase(rcSizeType count, const T& value) : m_size(0), m_cap(0), m_data(0) { resize(count, value); }
+	rcVectorBase(const T* begin, const T* end) : m_size(0), m_cap(0), m_data(0) { assign(begin, end); }
+	~rcVectorBase() { destroy_range(0, m_size); rcFree(m_data); }
+
+	// Unlike in std::vector, we return a bool to indicate whether the alloc was successful.
+	bool reserve(rcSizeType size);
+
+	void assign(rcSizeType count, const T& value) { clear(); resize(count, value); }
+	void assign(const T* begin, const T* end);
+
+	void resize(rcSizeType size) { resize_impl(size, NULL); }
+	void resize(rcSizeType size, const T& value) { resize_impl(size, &value); }
+	// Not implemented as resize(0) because resize requires T to be default-constructible.
+	void clear() { destroy_range(0, m_size); m_size = 0; }
+
+	void push_back(const T& value);
+	void pop_back() { rcAssert(m_size > 0); back().~T(); m_size--; }
+
+	rcSizeType size() const { return m_size; }
+	rcSizeType capacity() const { return m_cap; }
+	bool empty() const { return size() == 0; }
+
+	const T& operator[](rcSizeType i) const { rcAssert(i >= 0 && i < m_size); return m_data[i]; }
+	T& operator[](rcSizeType i) { rcAssert(i >= 0 && i < m_size); return m_data[i]; }
+
+	const T& front() const { rcAssert(m_size); return m_data[0]; }
+	T& front() { rcAssert(m_size); return m_data[0]; }
+	const T& back() const { rcAssert(m_size); return m_data[m_size - 1]; };
+	T& back() { rcAssert(m_size); return m_data[m_size - 1]; };
+	const T* data() const { return m_data; }
+	T* data() { return m_data; }
+
+	T* begin() { return m_data; }
+	T* end() { return m_data + m_size; }
+	const T* begin() const { return m_data; }
+	const T* end() const { return m_data + m_size; }
+
+	void swap(rcVectorBase<T, H>& other);
+
+	// Explicitly deleted.
+	rcVectorBase& operator=(const rcVectorBase<T, H>& other);
+};
+
+template<typename T, rcAllocHint H>
+bool rcVectorBase<T, H>::reserve(rcSizeType count) {
+	if (count <= m_cap) {
+		return true;
+	}
+	T* new_data = allocate_and_copy(count);
+	if (!new_data) {
+	  return false;
+	}
+	destroy_range(0, m_size);
+	rcFree(m_data);
+	m_data = new_data;
+	m_cap = count;
+	return true;
+}
+template <typename T, rcAllocHint H>
+T* rcVectorBase<T, H>::allocate_and_copy(rcSizeType size) {
+	rcAssert(RC_SIZE_MAX / static_cast<rcSizeType>(sizeof(T)) >= size);
+	T* new_data = static_cast<T*>(rcAlloc(sizeof(T) * size, H));
+	if (new_data) {
+		copy_range(new_data, m_data, m_data + m_size);
+	}
+	return new_data;
+}
+template <typename T, rcAllocHint H>
+void rcVectorBase<T, H>::assign(const T* begin, const T* end) {
+	clear();
+	reserve(end - begin);
+	m_size = end - begin;
+	copy_range(m_data, begin, end);
+}
+template <typename T, rcAllocHint H>
+void rcVectorBase<T, H>::push_back(const T& value) {
+	// rcLikely increases performance by ~50% on BM_rcVector_PushPreallocated,
+	// and by ~2-5% on BM_rcVector_Push.
+	if (rcLikely(m_size < m_cap)) {
+		construct(m_data + m_size++, value);
+		return;
+	}
+
+	rcAssert(RC_SIZE_MAX / 2 >= m_size);
+	rcSizeType new_cap = m_size ? 2*m_size : 1;
+	T* data = allocate_and_copy(new_cap);
+	// construct between allocate and destroy+free in case value is
+	// in this vector.
+	construct(data + m_size, value);
+	destroy_range(0, m_size);
+	m_size++;
+	m_cap = new_cap;
+	rcFree(m_data);
+	m_data = data;
+}
+template <typename T, rcAllocHint H>
+void rcVectorBase<T, H>::resize_impl(rcSizeType size, const T* value) {
+	if (size < m_size) {
+		destroy_range(size, m_size);
+		m_size = size;
+	} else if (size > m_size) {
+		T* new_data = allocate_and_copy(size);
+		// We defer deconstructing/freeing old data until after constructing
+		// new elements in case "value" is there.
+		if (value) {
+			construct_range(new_data + m_size, new_data + size, *value);
+		} else {
+			construct_range(new_data + m_size, new_data + size);
+		}
+		destroy_range(0, m_size);
+		rcFree(m_data);
+		m_data = new_data;
+		m_cap = size;
+		m_size = size;
+	}
+}
+template <typename T, rcAllocHint H>
+void rcVectorBase<T, H>::swap(rcVectorBase<T, H>& other) {
+	// TODO: Reorganize headers so we can use rcSwap here.
+	rcSizeType tmp_cap = other.m_cap;
+	rcSizeType tmp_size = other.m_size;
+	T* tmp_data = other.m_data;
+
+	other.m_cap = m_cap;
+	other.m_size = m_size;
+	other.m_data = m_data;
+
+	m_cap = tmp_cap;
+	m_size = tmp_size;
+	m_data = tmp_data;
+}
+// static
+template <typename T, rcAllocHint H>
+void rcVectorBase<T, H>::construct_range(T* begin, T* end) {
+	for (T* p = begin; p < end; p++) {
+		construct(p);
+	}
+}
+// static
+template <typename T, rcAllocHint H>
+void rcVectorBase<T, H>::construct_range(T* begin, T* end, const T& value) {
+	for (T* p = begin; p < end; p++) {
+		construct(p, value);
+	}
+}
+// static
+template <typename T, rcAllocHint H>
+void rcVectorBase<T, H>::copy_range(T* dst, const T* begin, const T* end) {
+	for (rcSizeType i = 0 ; i < end - begin; i++) {
+		construct(dst + i, begin[i]);
+	}
+}
+template <typename T, rcAllocHint H>
+void rcVectorBase<T, H>::destroy_range(rcSizeType begin, rcSizeType end) {
+	for (rcSizeType i = begin; i < end; i++) {
+		m_data[i].~T();
+	}
+}
+
+template <typename T>
+class rcTempVector : public rcVectorBase<T, RC_ALLOC_TEMP> {
+	typedef rcVectorBase<T, RC_ALLOC_TEMP> Base;
+public:
+	rcTempVector() : Base() {}
+	explicit rcTempVector(rcSizeType size) : Base(size) {}
+	rcTempVector(rcSizeType size, const T& value) : Base(size, value) {}
+	rcTempVector(const rcTempVector<T>& other) : Base(other) {}
+	rcTempVector(const T* begin, const T* end) : Base(begin, end) {}
+};
+template <typename T>
+class rcPermVector : public rcVectorBase<T, RC_ALLOC_PERM> {
+	typedef rcVectorBase<T, RC_ALLOC_PERM> Base;
+public:
+	rcPermVector() : Base() {}
+	explicit rcPermVector(rcSizeType size) : Base(size) {}
+	rcPermVector(rcSizeType size, const T& value) : Base(size, value) {}
+	rcPermVector(const rcPermVector<T>& other) : Base(other) {}
+	rcPermVector(const T* begin, const T* end) : Base(begin, end) {}
+};
+
+
+/// Legacy class. Prefer rcVector<int>.
+class rcIntArray
+{
+	rcTempVector<int> m_impl;
+public:
+	rcIntArray() {}
+	rcIntArray(int n) : m_impl(n, 0) {}
+	void push(int item) { m_impl.push_back(item); }
+	void resize(int size) { m_impl.resize(size); }
+	int pop()
+	{
+		int v = m_impl.back();
+		m_impl.pop_back();
+		return v;
+	}
+	int size() const { return m_impl.size(); }
+	int& operator[](int index) { return m_impl[index]; }
+	int operator[](int index) const { return m_impl[index]; }
+};
+
+/// 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;
+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; }
+	
+private:
+	// Explicitly disabled copy constructor and copy assignment operator.
+	rcScopedDelete(const rcScopedDelete&);
+	rcScopedDelete& operator=(const rcScopedDelete&);
+};
+
+#endif

+ 56 - 0
Source/ThirdParty/Recast/Include/RecastAssert.h

@@ -0,0 +1,56 @@
+//
+// Copyright (c) 2009-2010 Mikko Mononen [email protected]
+//
+// 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((void)(__LINE__==-1),false)
+
+#else
+
+/// An assertion failure function.
+//  @param[in]		expression  asserted expression.
+//  @param[in]		file  Filename of the failed assertion.
+//  @param[in]		line  Line number of the failed assertion.
+///  @see rcAssertFailSetCustom
+typedef void (rcAssertFailFunc)(const char* expression, const char* file, int line);
+
+/// Sets the base custom assertion failure function to be used by Recast.
+///  @param[in]		assertFailFunc	The function to be used in case of failure of #dtAssert
+void rcAssertFailSetCustom(rcAssertFailFunc *assertFailFunc);
+
+/// Gets the base custom assertion failure function to be used by Recast.
+rcAssertFailFunc* rcAssertFailGetCustom();
+
+#	include <assert.h> 
+#	define rcAssert(expression) \
+		{ \
+			rcAssertFailFunc* failFunc = rcAssertFailGetCustom(); \
+			if(failFunc == NULL) { assert(expression); } \
+			else if(!(expression)) { (*failFunc)(#expression, __FILE__, __LINE__); } \
+		}
+
+#endif
+
+#endif // RECASTASSERT_H

+ 146 - 60
Source/ThirdParty/Recast/source/Recast.cpp → Source/ThirdParty/Recast/Source/Recast.cpp

@@ -27,6 +27,30 @@
 #include "RecastAlloc.h"
 #include "RecastAssert.h"
 
+namespace
+{
+/// Allocates and constructs an object of the given type, returning a pointer.
+/// TODO: Support constructor args.
+/// @param[in]		hint	Hint to the allocator.
+template <typename T>
+T* rcNew(rcAllocHint hint) {
+	T* ptr = (T*)rcAlloc(sizeof(T), hint);
+	::new(rcNewTag(), (void*)ptr) T();
+	return ptr;
+}
+
+/// Destroys and frees an object allocated with rcNew.
+/// @param[in]     ptr    The object pointer to delete.
+template <typename T>
+void rcDelete(T* ptr) {
+	if (ptr) {
+		ptr->~T();
+		rcFree((void*)ptr);
+	}
+}
+}  // namespace
+
+
 float rcSqrt(float x)
 {
 	return sqrtf(x);
@@ -72,100 +96,163 @@ void rcContext::log(const rcLogCategory category, const char* format, ...)
 
 rcHeightfield* rcAllocHeightfield()
 {
-	rcHeightfield* hf = (rcHeightfield*)rcAlloc(sizeof(rcHeightfield), RC_ALLOC_PERM);
-	memset(hf, 0, sizeof(rcHeightfield));
-	return hf;
+	return rcNew<rcHeightfield>(RC_ALLOC_PERM);
+}
+rcHeightfield::rcHeightfield()
+	: width()
+	, height()
+	, bmin()
+	, bmax()
+	, cs()
+	, ch()
+	, spans()
+	, pools()
+	, freelist()
+{
 }
 
-void rcFreeHeightField(rcHeightfield* hf)
+rcHeightfield::~rcHeightfield()
 {
-	if (!hf) return;
 	// Delete span array.
-	rcFree(hf->spans);
+	rcFree(spans);
 	// Delete span pools.
-	while (hf->pools)
+	while (pools)
 	{
-		rcSpanPool* next = hf->pools->next;
-		rcFree(hf->pools);
-		hf->pools = next;
+		rcSpanPool* next = pools->next;
+		rcFree(pools);
+		pools = next;
 	}
-	rcFree(hf);
+}
+
+void rcFreeHeightField(rcHeightfield* hf)
+{
+	rcDelete(hf);
 }
 
 rcCompactHeightfield* rcAllocCompactHeightfield()
 {
-	rcCompactHeightfield* chf = (rcCompactHeightfield*)rcAlloc(sizeof(rcCompactHeightfield), RC_ALLOC_PERM);
-	memset(chf, 0, sizeof(rcCompactHeightfield));
-	return chf;
+	return rcNew<rcCompactHeightfield>(RC_ALLOC_PERM);
 }
 
 void rcFreeCompactHeightfield(rcCompactHeightfield* chf)
 {
-	if (!chf) return;
-	rcFree(chf->cells);
-	rcFree(chf->spans);
-	rcFree(chf->dist);
-	rcFree(chf->areas);
-	rcFree(chf);
+	rcDelete(chf);
 }
 
+rcCompactHeightfield::rcCompactHeightfield()
+	: width(),
+	height(),
+	spanCount(),
+	walkableHeight(),
+	walkableClimb(),
+	borderSize(),
+	maxDistance(),
+	maxRegions(),
+	bmin(),
+	bmax(),
+	cs(),
+	ch(),
+	cells(),
+	spans(),
+	dist(),
+	areas()
+{
+}
+rcCompactHeightfield::~rcCompactHeightfield()
+{
+	rcFree(cells);
+	rcFree(spans);
+	rcFree(dist);
+	rcFree(areas);
+}
 
 rcHeightfieldLayerSet* rcAllocHeightfieldLayerSet()
 {
-	rcHeightfieldLayerSet* lset = (rcHeightfieldLayerSet*)rcAlloc(sizeof(rcHeightfieldLayerSet), RC_ALLOC_PERM);
-	memset(lset, 0, sizeof(rcHeightfieldLayerSet));
-	return lset;
+	return rcNew<rcHeightfieldLayerSet>(RC_ALLOC_PERM);
 }
-
 void rcFreeHeightfieldLayerSet(rcHeightfieldLayerSet* lset)
 {
-	if (!lset) return;
-	for (int i = 0; i < lset->nlayers; ++i)
+	rcDelete(lset);
+}
+
+rcHeightfieldLayerSet::rcHeightfieldLayerSet()
+	: layers(),	nlayers() {}
+rcHeightfieldLayerSet::~rcHeightfieldLayerSet()
+{
+	for (int i = 0; i < nlayers; ++i)
 	{
-		rcFree(lset->layers[i].heights);
-		rcFree(lset->layers[i].areas);
-		rcFree(lset->layers[i].cons);
+		rcFree(layers[i].heights);
+		rcFree(layers[i].areas);
+		rcFree(layers[i].cons);
 	}
-	rcFree(lset->layers);
-	rcFree(lset);
+	rcFree(layers);
 }
 
 
 rcContourSet* rcAllocContourSet()
 {
-	rcContourSet* cset = (rcContourSet*)rcAlloc(sizeof(rcContourSet), RC_ALLOC_PERM);
-	memset(cset, 0, sizeof(rcContourSet));
-	return cset;
+	return rcNew<rcContourSet>(RC_ALLOC_PERM);
 }
-
 void rcFreeContourSet(rcContourSet* cset)
 {
-	if (!cset) return;
-	for (int i = 0; i < cset->nconts; ++i)
+	rcDelete(cset);
+}
+
+rcContourSet::rcContourSet()
+	: conts(),
+	nconts(),
+	bmin(),
+	bmax(),
+	cs(),
+	ch(),
+	width(),
+	height(),
+	borderSize(),
+	maxError() {}
+rcContourSet::~rcContourSet()
+{
+	for (int i = 0; i < nconts; ++i)
 	{
-		rcFree(cset->conts[i].verts);
-		rcFree(cset->conts[i].rverts);
+		rcFree(conts[i].verts);
+		rcFree(conts[i].rverts);
 	}
-	rcFree(cset->conts);
-	rcFree(cset);
+	rcFree(conts);
 }
 
+
 rcPolyMesh* rcAllocPolyMesh()
 {
-	rcPolyMesh* pmesh = (rcPolyMesh*)rcAlloc(sizeof(rcPolyMesh), RC_ALLOC_PERM);
-	memset(pmesh, 0, sizeof(rcPolyMesh));
-	return pmesh;
+	return rcNew<rcPolyMesh>(RC_ALLOC_PERM);
 }
-
 void rcFreePolyMesh(rcPolyMesh* pmesh)
 {
-	if (!pmesh) return;
-	rcFree(pmesh->verts);
-	rcFree(pmesh->polys);
-	rcFree(pmesh->regs);
-	rcFree(pmesh->flags);
-	rcFree(pmesh->areas);
-	rcFree(pmesh);
+	rcDelete(pmesh);
+}
+
+rcPolyMesh::rcPolyMesh()
+	: verts(),
+	polys(),
+	regs(),
+	flags(),
+	areas(),
+	nverts(),
+	npolys(),
+	maxpolys(),
+	nvp(),
+	bmin(),
+	bmax(),
+	cs(),
+	ch(),
+	borderSize(),
+	maxEdgeError() {}
+
+rcPolyMesh::~rcPolyMesh()
+{
+	rcFree(verts);
+	rcFree(polys);
+	rcFree(regs);
+	rcFree(flags);
+	rcFree(areas);
 }
 
 rcPolyMeshDetail* rcAllocPolyMeshDetail()
@@ -238,18 +325,19 @@ static void calcTriNormal(const float* v0, const float* v1, const float* v2, flo
 
 /// @par
 ///
-/// Only sets the aread id's for the walkable triangles.  Does not alter the
+/// Only sets the area 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 float* verts, int nv,
 							 const int* tris, int nt,
 							 unsigned char* areas)
 {
 	rcIgnoreUnused(ctx);
+	rcIgnoreUnused(nv);
 	
 	const float walkableThr = cosf(walkableSlopeAngle/180.0f*RC_PI);
 
@@ -267,7 +355,7 @@ void rcMarkWalkableTriangles(rcContext* ctx, const float walkableSlopeAngle,
 
 /// @par
 ///
-/// Only sets the aread id's for the unwalkable triangles.  Does not alter the
+/// Only sets the area 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.
@@ -318,7 +406,7 @@ int rcGetHeightFieldSpanCount(rcContext* ctx, rcHeightfield& hf)
 /// @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.
+/// Various filters may be applied, then the distance field and regions built.
 /// E.g: #rcBuildDistanceField and #rcBuildRegions
 ///
 /// See the #rcConfig documentation for more information on the configuration parameters.
@@ -329,7 +417,7 @@ bool rcBuildCompactHeightfield(rcContext* ctx, const int walkableHeight, const i
 {
 	rcAssert(ctx);
 	
-	ctx->startTimer(RC_TIMER_BUILD_COMPACTHEIGHTFIELD);
+	rcScopedTimer timer(ctx, RC_TIMER_BUILD_COMPACTHEIGHTFIELD);
 	
 	const int w = hf.width;
 	const int h = hf.height;
@@ -456,8 +544,6 @@ bool rcBuildCompactHeightfield(rcContext* ctx, const int walkableHeight, const i
 		ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Heightfield has too many layers %d (max: %d)",
 				 tooHighNeighbour, MAX_LAYERS);
 	}
-		
-	ctx->stopTimer(RC_TIMER_BUILD_COMPACTHEIGHTFIELD);
 	
 	return true;
 }
@@ -486,4 +572,4 @@ static int getCompactHeightFieldMemoryusage(const rcCompactHeightfield& chf)
 	size += sizeof(rcCompactCell) * chf.width * chf.height;
 	return size;
 }
-*/
+*/

+ 3 - 31
Source/ThirdParty/Recast/source/RecastAlloc.cpp → Source/ThirdParty/Recast/Source/RecastAlloc.cpp

@@ -19,8 +19,9 @@
 #include <stdlib.h>
 #include <string.h>
 #include "RecastAlloc.h"
+#include "RecastAssert.h"
 
-static void *rcAllocDefault(int size, rcAllocHint)
+static void *rcAllocDefault(size_t size, rcAllocHint)
 {
 	return malloc(size);
 }
@@ -41,7 +42,7 @@ void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc)
 }
 
 /// @see rcAllocSetCustom
-void* rcAlloc(int size, rcAllocHint hint)
+void* rcAlloc(size_t size, rcAllocHint hint)
 {
 	return sRecastAllocFunc(size, hint);
 }
@@ -57,32 +58,3 @@ 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;
-}
-

+ 5 - 16
Source/ThirdParty/Recast/source/RecastArea.cpp → Source/ThirdParty/Recast/Source/RecastArea.cpp

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

+ 14 - 12
Source/ThirdParty/Recast/include/RecastAssert.h → Source/ThirdParty/Recast/Source/RecastAssert.cpp

@@ -16,18 +16,20 @@
 // 3. This notice may not be removed or altered from any source distribution.
 //
 
-#ifndef RECASTASSERT_H
-#define RECASTASSERT_H
+#include "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.
+#ifndef NDEBUG
 
-#ifdef NDEBUG
-// From http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/
-#	define rcAssert(x) do { (void)sizeof(x); } while((void)(__LINE__==-1),false)  
-#else
-#	include <assert.h> 
-#	define rcAssert assert
-#endif
+static rcAssertFailFunc* sRecastAssertFailFunc = 0;
+
+void rcAssertFailSetCustom(rcAssertFailFunc *assertFailFunc)
+{
+	sRecastAssertFailFunc = assertFailFunc;
+}
 
-#endif // RECASTASSERT_H
+rcAssertFailFunc* rcAssertFailGetCustom()
+{
+	return sRecastAssertFailFunc;
+}
+
+#endif

+ 398 - 144
Source/ThirdParty/Recast/source/RecastContour.cpp → Source/ThirdParty/Recast/Source/RecastContour.cpp

@@ -20,6 +20,7 @@
 #include <math.h>
 #include <string.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include "Recast.h"
 #include "RecastAlloc.h"
 #include "RecastAssert.h"
@@ -36,7 +37,7 @@ static int getCornerHeight(int x, int y, int i, int dir,
 	unsigned int regs[4] = {0,0,0,0};
 	
 	// Combine region and area codes in order to prevent
-	// border vertices which are in between two areas to be removed. 
+	// 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) != RC_NOT_CONNECTED)
@@ -187,27 +188,6 @@ 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);
-	float pqz = (float)(qz - pz);
-	float dx = (float)(x - px);
-	float dy = (float)(y - py);
-	float dz = (float)(z - pz);
-	float d = pqx*pqx + pqy*pqy + pqz*pqz;
-	float t = pqx*dx + pqy*dy + pqz*dz;
-	if (d > 0)
-		t /= d;
-	if (t < 0)
-		t = 0;
-	else if (t > 1)
-		t = 1;
-	
-	dx = px + t*pqx - x;
-	dy = py + t*pqy - y;
-	dz = pz + t*pqz - z;
-	
-	return dx*dx + dy*dy + dz*dz;*/
-
 	float pqx = (float)(qx - px);
 	float pqz = (float)(qz - pz);
 	float dx = (float)(x - px);
@@ -257,13 +237,13 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified,
 				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. 
+		// create some initial points for the simplification process.
 		// Find lower-left and upper-right vertices of the contour.
 		int llx = points[0];
 		int lly = points[1];
@@ -311,19 +291,19 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified,
 	{
 		int ii = (i+1) % (simplified.size()/4);
 		
-		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];
+		int ax = simplified[i*4+0];
+		int az = simplified[i*4+2];
+		int ai = simplified[i*4+3];
+
+		int bx = simplified[ii*4+0];
+		int bz = simplified[ii*4+2];
+		int bi = simplified[ii*4+3];
 
 		// Find maximum deviation from the segment.
 		float maxd = 0;
 		int maxi = -1;
 		int ci, cinc, endi;
-		
+
 		// Traverse the segment in lexilogical order so that the
 		// max deviation is calculated similarly when traversing
 		// opposite segments.
@@ -338,6 +318,8 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified,
 			cinc = pn-1;
 			ci = (bi+cinc) % pn;
 			endi = ai;
+			rcSwap(ax, bx);
+			rcSwap(az, bz);
 		}
 		
 		// Tessellate only outer edges or edges between areas.
@@ -397,11 +379,11 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified,
 			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.
@@ -469,32 +451,6 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified,
 	
 }
 
-static void removeDegenerateSegments(rcIntArray& simplified)
-{
-	// Remove adjacent vertices which are equal on xz-plane,
-	// or else the triangulator will get confused.
-	for (int i = 0; i < simplified.size()/4; ++i)
-	{
-		int ni = i+1;
-		if (ni >= (simplified.size()/4))
-			ni = 0;
-			
-		if (simplified[i*4+0] == simplified[ni*4+0] &&
-			simplified[i*4+2] == simplified[ni*4+2])
-		{
-			// Degenerate segment, remove.
-			for (int j = i; j < simplified.size()/4-1; ++j)
-			{
-				simplified[j*4+0] = simplified[(j+1)*4+0];
-				simplified[j*4+1] = simplified[(j+1)*4+1];
-				simplified[j*4+2] = simplified[(j+1)*4+2];
-				simplified[j*4+3] = simplified[(j+1)*4+3];
-			}
-			simplified.resize(simplified.size()-4);
-		}
-	}
-}
-
 static int calcAreaOfPolygon2D(const int* verts, const int nverts)
 {
 	int area = 0;
@@ -507,54 +463,155 @@ 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)
+// TODO: these are the same as in RecastMesh.cpp, consider using the same.
+// Last time I checked the if version got compiled using cmov, which was a lot faster than module (with idiv).
+inline int prev(int i, int n) { return i-1 >= 0 ? i-1 : n-1; }
+inline int next(int i, int n) { return i+1 < n ? i+1 : 0; }
+
+inline int area2(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]);
+}
+
+//	Exclusive or: true iff exactly one argument is true.
+//	The arguments are negated to ensure that they are 0/1
+//	values.  Then the bitwise Xor operator may apply.
+//	(This idea is due to Michael Baldwin.)
+inline bool xorb(bool x, bool y)
+{
+	return !x ^ !y;
+}
+
+// Returns true iff c is strictly to the left of the directed
+// line through a to b.
+inline bool left(const int* a, const int* b, const int* c)
+{
+	return area2(a, b, c) < 0;
+}
+
+inline bool leftOn(const int* a, const int* b, const int* c)
+{
+	return area2(a, b, c) <= 0;
+}
+
+inline bool collinear(const int* a, const int* b, const int* c)
+{
+	return area2(a, b, c) == 0;
+}
+
+//	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.
+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) ||
+		collinear(c,d,a) || collinear(c,d,b))
+		return false;
+	
+	return xorb(left(a,b,c), left(a,b,d)) && xorb(left(c,d,a), left(c,d,b));
+}
+
+// Returns T iff (a,b,c) are collinear and point c lies
+// on the closed segement ab.
+static bool between(const int* a, const int* b, const int* c)
+{
+	if (!collinear(a, b, c))
+		return false;
+	// If ab not vertical, check betweenness on x; else on y.
+	if (a[0] != b[0])
+		return	((a[0] <= c[0]) && (c[0] <= b[0])) || ((a[0] >= c[0]) && (c[0] >= b[0]));
+	else
+		return	((a[2] <= c[2]) && (c[2] <= b[2])) || ((a[2] >= c[2]) && (c[2] >= b[2]));
+}
+
+// Returns true iff segments ab and cd intersect, properly or improperly.
+static bool intersect(const int* a, const int* b, const int* c, const int* d)
+{
+	if (intersectProp(a, b, c, d))
+		return true;
+	else if (between(a, b, c) || between(a, b, d) ||
+			 between(c, d, a) || between(c, d, b))
+		return true;
+	else
+		return false;
+}
+
+static bool vequal(const int* a, const int* b)
+{
+	return a[0] == b[0] && a[2] == b[2];
+}
+
+static bool intersectSegCountour(const int* d0, const int* d1, int i, int n, const int* verts)
 {
-	return (b[0] - a[0]) * (c[2] - a[2]) - (c[0] - a[0]) * (b[2] - a[2]) <= 0;
+	// For each edge (k,k+1) of P
+	for (int k = 0; k < n; k++)
+	{
+		int k1 = next(k, n);
+		// Skip edges incident to i.
+		if (i == k || i == k1)
+			continue;
+		const int* p0 = &verts[k * 4];
+		const int* p1 = &verts[k1 * 4];
+		if (vequal(d0, p0) || vequal(d1, p0) || vequal(d0, p1) || vequal(d1, p1))
+			continue;
+		
+		if (intersect(d0, d1, p0, p1))
+			return true;
+	}
+	return false;
 }
 
-static void getClosestIndices(const int* vertsa, const int nvertsa,
-							  const int* vertsb, const int nvertsb,
-							  int& ia, int& ib)
+static bool	inCone(int i, int n, const int* verts, const int* pj)
 {
-	int closestDist = 0xfffffff;
-	ia = -1, ib = -1;
-	for (int i = 0; i < nvertsa; ++i)
+	const int* pi = &verts[i * 4];
+	const int* pi1 = &verts[next(i, n) * 4];
+	const int* pin1 = &verts[prev(i, n) * 4];
+	
+	// If P[i] is a convex vertex [ i+1 left or on (i-1,i) ].
+	if (leftOn(pin1, pi, pi1))
+		return left(pi, pj, pin1) && left(pj, pi, pi1);
+	// Assume (i-1,i,i+1) not collinear.
+	// else P[i] is reflex.
+	return !(leftOn(pi, pj, pi1) && leftOn(pj, pi, pin1));
+}
+
+
+static void removeDegenerateSegments(rcIntArray& simplified)
+{
+	// Remove adjacent vertices which are equal on xz-plane,
+	// or else the triangulator will get confused.
+	int npts = simplified.size()/4;
+	for (int i = 0; i < npts; ++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];
+		int ni = next(i, npts);
 		
-		for (int j = 0; j < nvertsb; ++j)
+		if (vequal(&simplified[i*4], &simplified[ni*4]))
 		{
-			const int* vb = &vertsb[j*4];
-			// vb must be "infront" of va.
-			if (ileft(vap,va,vb) && ileft(va,van,vb))
+			// Degenerate segment, remove.
+			for (int j = i; j < simplified.size()/4-1; ++j)
 			{
-				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;
-				}
+				simplified[j*4+0] = simplified[(j+1)*4+0];
+				simplified[j*4+1] = simplified[(j+1)*4+1];
+				simplified[j*4+2] = simplified[(j+1)*4+2];
+				simplified[j*4+3] = simplified[(j+1)*4+3];
 			}
+			simplified.resize(simplified.size()-4);
+			npts--;
 		}
 	}
 }
 
+
 static bool mergeContours(rcContour& ca, rcContour& cb, int ia, int ib)
 {
 	const int maxVerts = ca.nverts + cb.nverts + 2;
 	int* verts = (int*)rcAlloc(sizeof(int)*maxVerts*4, RC_ALLOC_PERM);
 	if (!verts)
 		return false;
-
+	
 	int nv = 0;
-
+	
 	// Copy contour A.
 	for (int i = 0; i <= ca.nverts; ++i)
 	{
@@ -582,7 +639,7 @@ static bool mergeContours(rcContour& ca, rcContour& cb, int ia, int ib)
 	rcFree(ca.verts);
 	ca.verts = verts;
 	ca.nverts = nv;
-
+	
 	rcFree(cb.verts);
 	cb.verts = 0;
 	cb.nverts = 0;
@@ -590,18 +647,179 @@ static bool mergeContours(rcContour& ca, rcContour& cb, int ia, int ib)
 	return true;
 }
 
+struct rcContourHole
+{
+	rcContour* contour;
+	int minx, minz, leftmost;
+};
+
+struct rcContourRegion
+{
+	rcContour* outline;
+	rcContourHole* holes;
+	int nholes;
+};
+
+struct rcPotentialDiagonal
+{
+	int vert;
+	int dist;
+};
+
+// Finds the lowest leftmost vertex of a contour.
+static void findLeftMostVertex(rcContour* contour, int* minx, int* minz, int* leftmost)
+{
+	*minx = contour->verts[0];
+	*minz = contour->verts[2];
+	*leftmost = 0;
+	for (int i = 1; i < contour->nverts; i++)
+	{
+		const int x = contour->verts[i*4+0];
+		const int z = contour->verts[i*4+2];
+		if (x < *minx || (x == *minx && z < *minz))
+		{
+			*minx = x;
+			*minz = z;
+			*leftmost = i;
+		}
+	}
+}
+
+static int compareHoles(const void* va, const void* vb)
+{
+	const rcContourHole* a = (const rcContourHole*)va;
+	const rcContourHole* b = (const rcContourHole*)vb;
+	if (a->minx == b->minx)
+	{
+		if (a->minz < b->minz)
+			return -1;
+		if (a->minz > b->minz)
+			return 1;
+	}
+	else
+	{
+		if (a->minx < b->minx)
+			return -1;
+		if (a->minx > b->minx)
+			return 1;
+	}
+	return 0;
+}
+
+
+static int compareDiagDist(const void* va, const void* vb)
+{
+	const rcPotentialDiagonal* a = (const rcPotentialDiagonal*)va;
+	const rcPotentialDiagonal* b = (const rcPotentialDiagonal*)vb;
+	if (a->dist < b->dist)
+		return -1;
+	if (a->dist > b->dist)
+		return 1;
+	return 0;
+}
+
+
+static void mergeRegionHoles(rcContext* ctx, rcContourRegion& region)
+{
+	// Sort holes from left to right.
+	for (int i = 0; i < region.nholes; i++)
+		findLeftMostVertex(region.holes[i].contour, &region.holes[i].minx, &region.holes[i].minz, &region.holes[i].leftmost);
+	
+	qsort(region.holes, region.nholes, sizeof(rcContourHole), compareHoles);
+	
+	int maxVerts = region.outline->nverts;
+	for (int i = 0; i < region.nholes; i++)
+		maxVerts += region.holes[i].contour->nverts;
+	
+	rcScopedDelete<rcPotentialDiagonal> diags((rcPotentialDiagonal*)rcAlloc(sizeof(rcPotentialDiagonal)*maxVerts, RC_ALLOC_TEMP));
+	if (!diags)
+	{
+		ctx->log(RC_LOG_WARNING, "mergeRegionHoles: Failed to allocated diags %d.", maxVerts);
+		return;
+	}
+	
+	rcContour* outline = region.outline;
+	
+	// Merge holes into the outline one by one.
+	for (int i = 0; i < region.nholes; i++)
+	{
+		rcContour* hole = region.holes[i].contour;
+		
+		int index = -1;
+		int bestVertex = region.holes[i].leftmost;
+		for (int iter = 0; iter < hole->nverts; iter++)
+		{
+			// Find potential diagonals.
+			// The 'best' vertex must be in the cone described by 3 cosequtive vertices of the outline.
+			// ..o j-1
+			//   |
+			//   |   * best
+			//   |
+			// j o-----o j+1
+			//         :
+			int ndiags = 0;
+			const int* corner = &hole->verts[bestVertex*4];
+			for (int j = 0; j < outline->nverts; j++)
+			{
+				if (inCone(j, outline->nverts, outline->verts, corner))
+				{
+					int dx = outline->verts[j*4+0] - corner[0];
+					int dz = outline->verts[j*4+2] - corner[2];
+					diags[ndiags].vert = j;
+					diags[ndiags].dist = dx*dx + dz*dz;
+					ndiags++;
+				}
+			}
+			// Sort potential diagonals by distance, we want to make the connection as short as possible.
+			qsort(diags, ndiags, sizeof(rcPotentialDiagonal), compareDiagDist);
+			
+			// Find a diagonal that is not intersecting the outline not the remaining holes.
+			index = -1;
+			for (int j = 0; j < ndiags; j++)
+			{
+				const int* pt = &outline->verts[diags[j].vert*4];
+				bool intersect = intersectSegCountour(pt, corner, diags[i].vert, outline->nverts, outline->verts);
+				for (int k = i; k < region.nholes && !intersect; k++)
+					intersect |= intersectSegCountour(pt, corner, -1, region.holes[k].contour->nverts, region.holes[k].contour->verts);
+				if (!intersect)
+				{
+					index = diags[j].vert;
+					break;
+				}
+			}
+			// If found non-intersecting diagonal, stop looking.
+			if (index != -1)
+				break;
+			// All the potential diagonals for the current vertex were intersecting, try next vertex.
+			bestVertex = (bestVertex + 1) % hole->nverts;
+		}
+		
+		if (index == -1)
+		{
+			ctx->log(RC_LOG_WARNING, "mergeHoles: Failed to find merge points for %p and %p.", region.outline, hole);
+			continue;
+		}
+		if (!mergeContours(*region.outline, *hole, index, bestVertex))
+		{
+			ctx->log(RC_LOG_WARNING, "mergeHoles: Failed to merge contours %p and %p.", region.outline, hole);
+			continue;
+		}
+	}
+}
+
+
 /// @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. 
+/// 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,
@@ -613,7 +831,7 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
 	const int h = chf.height;
 	const int borderSize = chf.borderSize;
 	
-	ctx->startTimer(RC_TIMER_BUILD_CONTOURS);
+	rcScopedTimer timer(ctx, RC_TIMER_BUILD_CONTOURS);
 	
 	rcVcopy(cset.bmin, chf.bmin);
 	rcVcopy(cset.bmax, chf.bmax);
@@ -631,6 +849,7 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
 	cset.width = chf.width - chf.borderSize*2;
 	cset.height = chf.height - chf.borderSize*2;
 	cset.borderSize = chf.borderSize;
+	cset.maxError = maxError;
 	
 	int maxContours = rcMax((int)chf.maxRegions, 8);
 	cset.conts = (rcContour*)rcAlloc(sizeof(rcContour)*maxContours, RC_ALLOC_PERM);
@@ -638,7 +857,7 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
 		return false;
 	cset.nconts = 0;
 	
-	rcScopedDelete<unsigned char> flags = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP);
+	rcScopedDelete<unsigned char> flags((unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP));
 	if (!flags)
 	{
 		ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'flags' (%d).", chf.spanCount);
@@ -704,17 +923,17 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
 				
 				verts.resize(0);
 				simplified.resize(0);
-
+				
 				ctx->startTimer(RC_TIMER_BUILD_CONTOURS_TRACE);
 				walkContour(x, y, i, chf, flags, verts);
 				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)
@@ -722,7 +941,7 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
 					if (cset.nconts >= maxContours)
 					{
 						// Allocate more contours.
-						// This can happen when there are tiny holes in the heightfield.
+						// This happens when a region has holes.
 						const int oldMax = maxContours;
 						maxContours *= 2;
 						rcContour* newConts = (rcContour*)rcAlloc(sizeof(rcContour)*maxContours, RC_ALLOC_PERM);
@@ -735,10 +954,10 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
 						}
 						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;
@@ -779,17 +998,6 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
 						}
 					}
 					
-/*					cont->cx = cont->cy = cont->cz = 0;
-					for (int i = 0; i < cont->nverts; ++i)
-					{
-						cont->cx += cont->verts[i*4+0];
-						cont->cy += cont->verts[i*4+1];
-						cont->cz += cont->verts[i*4+2];
-					}
-					cont->cx /= cont->nverts;
-					cont->cy /= cont->nverts;
-					cont->cz /= cont->nverts;*/
-					
 					cont->reg = reg;
 					cont->area = area;
 				}
@@ -797,55 +1005,101 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
 		}
 	}
 	
-	// Check and merge droppings.
-	// 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)
+	// Merge holes if needed.
+	if (cset.nconts > 0)
 	{
-		rcContour& cont = cset.conts[i];
-		// Check if the contour is would backwards.
-		if (calcAreaOfPolygon2D(cont.verts, cont.nverts) < 0)
+		// Calculate winding of all polygons.
+		rcScopedDelete<char> winding((char*)rcAlloc(sizeof(char)*cset.nconts, RC_ALLOC_TEMP));
+		if (!winding)
+		{
+			ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'hole' (%d).", cset.nconts);
+			return false;
+		}
+		int nholes = 0;
+		for (int i = 0; i < cset.nconts; ++i)
 		{
-			// Find another contour which has the same region ID.
-			int mergeIdx = -1;
-			for (int j = 0; j < cset.nconts; ++j)
+			rcContour& cont = cset.conts[i];
+			// If the contour is wound backwards, it is a hole.
+			winding[i] = calcAreaOfPolygon2D(cont.verts, cont.nverts) < 0 ? -1 : 1;
+			if (winding[i] < 0)
+				nholes++;
+		}
+		
+		if (nholes > 0)
+		{
+			// Collect outline contour and holes contours per region.
+			// We assume that there is one outline and multiple holes.
+			const int nregions = chf.maxRegions+1;
+			rcScopedDelete<rcContourRegion> regions((rcContourRegion*)rcAlloc(sizeof(rcContourRegion)*nregions, RC_ALLOC_TEMP));
+			if (!regions)
+			{
+				ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'regions' (%d).", nregions);
+				return false;
+			}
+			memset(regions, 0, sizeof(rcContourRegion)*nregions);
+			
+			rcScopedDelete<rcContourHole> holes((rcContourHole*)rcAlloc(sizeof(rcContourHole)*cset.nconts, RC_ALLOC_TEMP));
+			if (!holes)
 			{
-				if (i == j) continue;
-				if (cset.conts[j].nverts && cset.conts[j].reg == cont.reg)
+				ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'holes' (%d).", cset.nconts);
+				return false;
+			}
+			memset(holes, 0, sizeof(rcContourHole)*cset.nconts);
+			
+			for (int i = 0; i < cset.nconts; ++i)
+			{
+				rcContour& cont = cset.conts[i];
+				// Positively would contours are outlines, negative holes.
+				if (winding[i] > 0)
 				{
-					// Make sure the polygon is correctly oriented.
-					if (calcAreaOfPolygon2D(cset.conts[j].verts, cset.conts[j].nverts))
-					{
-						mergeIdx = j;
-						break;
-					}
+					if (regions[cont.reg].outline)
+						ctx->log(RC_LOG_ERROR, "rcBuildContours: Multiple outlines for region %d.", cont.reg);
+					regions[cont.reg].outline = &cont;
+				}
+				else
+				{
+					regions[cont.reg].nholes++;
+				}
+			}
+			int index = 0;
+			for (int i = 0; i < nregions; i++)
+			{
+				if (regions[i].nholes > 0)
+				{
+					regions[i].holes = &holes[index];
+					index += regions[i].nholes;
+					regions[i].nholes = 0;
 				}
 			}
-			if (mergeIdx == -1)
+			for (int i = 0; i < cset.nconts; ++i)
 			{
-				ctx->log(RC_LOG_WARNING, "rcBuildContours: Could not find merge target for bad contour %d.", i);
+				rcContour& cont = cset.conts[i];
+				rcContourRegion& reg = regions[cont.reg];
+				if (winding[i] < 0)
+					reg.holes[reg.nholes++].contour = &cont;
 			}
-			else
+			
+			// Finally merge each regions holes into the outline.
+			for (int i = 0; i < nregions; i++)
 			{
-				rcContour& mcont = cset.conts[mergeIdx];
-				// Merge by closest points.
-				int ia = 0, ib = 0;
-				getClosestIndices(mcont.verts, mcont.nverts, cont.verts, cont.nverts, ia, ib);
-				if (ia == -1 || ib == -1)
+				rcContourRegion& reg = regions[i];
+				if (!reg.nholes) continue;
+				
+				if (reg.outline)
 				{
-					ctx->log(RC_LOG_WARNING, "rcBuildContours: Failed to find merge points for %d and %d.", i, mergeIdx);
-					continue;
+					mergeRegionHoles(ctx, reg);
 				}
-				if (!mergeContours(mcont, cont, ia, ib))
+				else
 				{
-					ctx->log(RC_LOG_WARNING, "rcBuildContours: Failed to merge contours %d and %d.", i, mergeIdx);
-					continue;
+					// The region does not have an outline.
+					// This can happen if the contour becaomes selfoverlapping because of
+					// too aggressive simplification settings.
+					ctx->log(RC_LOG_ERROR, "rcBuildContours: Bad outline for region %d, contour simplification is likely too aggressive.", i);
 				}
 			}
 		}
+		
 	}
 	
-	ctx->stopTimer(RC_TIMER_BUILD_CONTOURS);
-	
 	return true;
 }

+ 7 - 12
Source/ThirdParty/Recast/source/RecastFilter.cpp → Source/ThirdParty/Recast/Source/RecastFilter.cpp

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

+ 59 - 35
Source/ThirdParty/Recast/source/RecastLayers.cpp → Source/ThirdParty/Recast/Source/RecastLayers.cpp

@@ -27,7 +27,9 @@
 #include "RecastAssert.h"
 
 
-static const int RC_MAX_LAYERS = RC_NOT_CONNECTED;
+// Must be 255 or smaller (not 256) because layer IDs are stored as
+// a byte where 255 is a special value.
+static const int RC_MAX_LAYERS = 63;
 static const int RC_MAX_NEIS = 16;
 
 struct rcLayerRegion
@@ -38,29 +40,35 @@ struct rcLayerRegion
 	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.
+	unsigned char base;		// Flag indicating if the region is the 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;
 }
 
+static bool addUnique(unsigned char* a, unsigned char& an, int anMax, unsigned char v)
+{
+	if (contains(a, an, v))
+		return true;
+
+	if ((int)an >= anMax)
+		return false;
+
+	a[an] = v;
+	an++;
+	return true;
+}
+
+
 inline bool overlapRange(const unsigned short amin, const unsigned short amax,
 						 const unsigned short bmin, const unsigned short bmax)
 {
@@ -87,12 +95,12 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf,
 {
 	rcAssert(ctx);
 	
-	ctx->startTimer(RC_TIMER_BUILD_LAYERS);
+	rcScopedTimer timer(ctx, RC_TIMER_BUILD_LAYERS);
 	
 	const int w = chf.width;
 	const int h = chf.height;
 	
-	rcScopedDelete<unsigned char> srcReg = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP);
+	rcScopedDelete<unsigned char> srcReg((unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP));
 	if (!srcReg)
 	{
 		ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'srcReg' (%d).", chf.spanCount);
@@ -101,7 +109,7 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf,
 	memset(srcReg,0xff,sizeof(unsigned char)*chf.spanCount);
 	
 	const int nsweeps = chf.width;
-	rcScopedDelete<rcLayerSweepSpan> sweeps = (rcLayerSweepSpan*)rcAlloc(sizeof(rcLayerSweepSpan)*nsweeps, RC_ALLOC_TEMP);
+	rcScopedDelete<rcLayerSweepSpan> sweeps((rcLayerSweepSpan*)rcAlloc(sizeof(rcLayerSweepSpan)*nsweeps, RC_ALLOC_TEMP));
 	if (!sweeps)
 	{
 		ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'sweeps' (%d).", nsweeps);
@@ -212,7 +220,7 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf,
 
 	// Allocate and init layer regions.
 	const int nregs = (int)regId;
-	rcScopedDelete<rcLayerRegion> regs = (rcLayerRegion*)rcAlloc(sizeof(rcLayerRegion)*nregs, RC_ALLOC_TEMP);
+	rcScopedDelete<rcLayerRegion> regs((rcLayerRegion*)rcAlloc(sizeof(rcLayerRegion)*nregs, RC_ALLOC_TEMP));
 	if (!regs)
 	{
 		ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'regs' (%d).", nregs);
@@ -259,7 +267,12 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf,
 						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);
+						{
+							// Don't check return value -- if we cannot add the neighbor
+							// it will just cause a few more regions to be created, which
+							// is fine.
+							addUnique(regs[ri].neis, regs[ri].nneis, RC_MAX_NEIS, rai);
+						}
 					}
 				}
 				
@@ -274,8 +287,13 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf,
 					{
 						rcLayerRegion& ri = regs[lregs[i]];
 						rcLayerRegion& rj = regs[lregs[j]];
-						addUnique(ri.layers, ri.nlayers, lregs[j]);
-						addUnique(rj.layers, rj.nlayers, lregs[i]);
+
+						if (!addUnique(ri.layers, ri.nlayers, RC_MAX_LAYERS, lregs[j]) ||
+							!addUnique(rj.layers, rj.nlayers, RC_MAX_LAYERS, lregs[i]))
+						{
+							ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: layer overflow (too many overlapping walkable platforms). Try increasing RC_MAX_LAYERS.");
+							return false;
+						}
 					}
 				}
 			}
@@ -293,7 +311,7 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf,
 	for (int i = 0; i < nregs; ++i)
 	{
 		rcLayerRegion& root = regs[i];
-		// Skip alreadu visited.
+		// Skip already visited.
 		if (root.layerId != 0xff)
 			continue;
 
@@ -338,7 +356,13 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf,
 					regn.layerId = layerId;
 					// Merge current layers to root.
 					for (int k = 0; k < regn.nlayers; ++k)
-						addUnique(root.layers, root.nlayers, regn.layers[k]);
+					{
+						if (!addUnique(root.layers, root.nlayers, RC_MAX_LAYERS, regn.layers[k]))
+						{
+							ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: layer overflow (too many overlapping walkable platforms). Try increasing RC_MAX_LAYERS.");
+							return false;
+						}
+					}
 					root.ymin = rcMin(root.ymin, regn.ymin);
 					root.ymax = rcMax(root.ymax, regn.ymax);
 				}
@@ -368,7 +392,7 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf,
 				rcLayerRegion& rj = regs[j];
 				if (!rj.base) continue;
 				
-				// Skip if teh regions are not close to each other.
+				// Skip if the 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.
@@ -377,7 +401,7 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf,
 				if ((ymax - ymin) >= 255)
 				  continue;
 						  
-				// Make sure that there is no overlap when mergin 'ri' and 'rj'.
+				// Make sure that there is no overlap when merging 'ri' and 'rj'.
 				bool overlap = false;
 				// Iterate over all regions which have the same layerId as 'rj'
 				for (int k = 0; k < nregs; ++k)
@@ -416,8 +440,15 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf,
 					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.
+					{
+						if (!addUnique(ri.layers, ri.nlayers, RC_MAX_LAYERS, rj.layers[k]))
+						{
+							ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: layer overflow (too many overlapping walkable platforms). Try increasing RC_MAX_LAYERS.");
+							return false;
+						}
+					}
+
+					// Update height bounds.
 					ri.ymin = rcMin(ri.ymin, rj.ymin);
 					ri.ymax = rcMax(ri.ymax, rj.ymax);
 				}
@@ -446,10 +477,7 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf,
 	
 	// No layers, return empty.
 	if (layerId == 0)
-	{
-		ctx->stopTimer(RC_TIMER_BUILD_LAYERS);
 		return true;
-	}
 	
 	// Create layers.
 	rcAssert(lset.layers == 0);
@@ -481,10 +509,8 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf,
 	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;
 
@@ -528,7 +554,7 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf,
 		layer->cs = chf.cs;
 		layer->ch = chf.ch;
 		
-		// Adjust the bbox to fit the heighfield.
+		// Adjust the bbox to fit the heightfield.
 		rcVcopy(layer->bmin, bmin);
 		rcVcopy(layer->bmax, bmax);
 		layer->bmin[1] = bmin[1] + hmin*chf.ch;
@@ -542,7 +568,7 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf,
 		layer->miny = layer->height;
 		layer->maxy = 0;
 		
-		// Copy height and area from compact heighfield. 
+		// Copy height and area from compact heightfield. 
 		for (int y = 0; y < lh; ++y)
 		{
 			for (int x = 0; x < lw; ++x)
@@ -614,7 +640,5 @@ bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf,
 			layer->miny = layer->maxy = 0;
 	}
 	
-	ctx->stopTimer(RC_TIMER_BUILD_LAYERS);
-	
 	return true;
 }

+ 124 - 41
Source/ThirdParty/Recast/source/RecastMesh.cpp → Source/ThirdParty/Recast/Source/RecastMesh.cpp

@@ -160,6 +160,7 @@ static unsigned short addVertex(unsigned short x, unsigned short y, unsigned sho
 	return (unsigned short)i;
 }
 
+// Last time I checked the if version got compiled using cmov, which was a lot faster than module (with idiv).
 inline int prev(int i, int n) { return i-1 >= 0 ? i-1 : n-1; }
 inline int next(int i, int n) { return i+1 < n ? i+1 : 0; }
 
@@ -288,6 +289,53 @@ 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);
 }
 
+
+static bool diagonalieLoose(int i, int j, int n, const int* verts, int* indices)
+{
+	const int* d0 = &verts[(indices[i] & 0x0fffffff) * 4];
+	const int* d1 = &verts[(indices[j] & 0x0fffffff) * 4];
+	
+	// For each edge (k,k+1) of P
+	for (int k = 0; k < n; k++)
+	{
+		int k1 = next(k, n);
+		// Skip edges incident to i or j
+		if (!((k == i) || (k1 == i) || (k == j) || (k1 == j)))
+		{
+			const int* p0 = &verts[(indices[k] & 0x0fffffff) * 4];
+			const int* p1 = &verts[(indices[k1] & 0x0fffffff) * 4];
+			
+			if (vequal(d0, p0) || vequal(d1, p0) || vequal(d0, p1) || vequal(d1, p1))
+				continue;
+			
+			if (intersectProp(d0, d1, p0, p1))
+				return false;
+		}
+	}
+	return true;
+}
+
+static bool	inConeLoose(int i, int j, int n, const int* verts, int* indices)
+{
+	const int* pi = &verts[(indices[i] & 0x0fffffff) * 4];
+	const int* pj = &verts[(indices[j] & 0x0fffffff) * 4];
+	const int* pi1 = &verts[(indices[next(i, n)] & 0x0fffffff) * 4];
+	const int* pin1 = &verts[(indices[prev(i, n)] & 0x0fffffff) * 4];
+	
+	// If P[i] is a convex vertex [ i+1 left or on (i-1,i) ].
+	if (leftOn(pin1, pi, pi1))
+		return leftOn(pi, pj, pin1) && leftOn(pj, pi, pi1);
+	// Assume (i-1,i,i+1) not collinear.
+	// else P[i] is reflex.
+	return !(leftOn(pi, pj, pi1) && leftOn(pj, pi, pin1));
+}
+
+static bool diagonalLoose(int i, int j, int n, const int* verts, int* indices)
+{
+	return inConeLoose(i, j, n, verts, indices) && diagonalieLoose(i, j, n, verts, indices);
+}
+
+
 static int triangulate(int n, const int* verts, int* indices, int* tris)
 {
 	int ntris = 0;
@@ -328,14 +376,41 @@ static int triangulate(int n, const int* verts, int* indices, int* tris)
 		
 		if (mini == -1)
 		{
-			// Should not happen.
-/*			printf("mini == -1 ntris=%d n=%d\n", ntris, n);
+			// We might get here because the contour has overlapping segments, like this:
+			//
+			//  A o-o=====o---o B
+			//   /  |C   D|    \.
+			//  o   o     o     o
+			//  :   :     :     :
+			// We'll try to recover by loosing up the inCone test a bit so that a diagonal
+			// like A-B or C-D can be found and we can continue.
+			minLen = -1;
+			mini = -1;
 			for (int i = 0; i < n; i++)
 			{
-				printf("%d ", indices[i] & 0x0fffffff);
+				int i1 = next(i, n);
+				int i2 = next(i1, n);
+				if (diagonalLoose(i, i2, n, verts, indices))
+				{
+					const int* p0 = &verts[(indices[i] & 0x0fffffff) * 4];
+					const int* p2 = &verts[(indices[next(i2, n)] & 0x0fffffff) * 4];
+					int dx = p2[0] - p0[0];
+					int dy = p2[2] - p0[2];
+					int len = dx*dx + dy*dy;
+					
+					if (minLen < 0 || len < minLen)
+					{
+						minLen = len;
+						mini = i;
+					}
+				}
+			}
+			if (mini == -1)
+			{
+				// The contour is messed up. This sometimes happens
+				// if the contour simplification is too aggressive.
+				return -ntris;
 			}
-			printf("\n");*/
-			return -ntris;
 		}
 		
 		int i = mini;
@@ -453,8 +528,8 @@ static int getPolyMergeValue(unsigned short* pa, unsigned short* pb,
 	return dx*dx + dy*dy;
 }
 
-static void mergePolys(unsigned short* pa, unsigned short* pb, int ea, int eb,
-					   unsigned short* tmp, const int nvp)
+static void mergePolyVerts(unsigned short* pa, unsigned short* pb, int ea, int eb,
+						   unsigned short* tmp, const int nvp)
 {
 	const int na = countPolyVerts(pa, nvp);
 	const int nb = countPolyVerts(pb, nvp);
@@ -526,7 +601,7 @@ static bool canRemoveVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned sho
 	// Find edges which share the removed vertex.
 	const int maxEdges = numTouchedVerts*2;
 	int nedges = 0;
-	rcScopedDelete<int> edges = (int*)rcAlloc(sizeof(int)*maxEdges*3, RC_ALLOC_TEMP);
+	rcScopedDelete<int> edges((int*)rcAlloc(sizeof(int)*maxEdges*3, RC_ALLOC_TEMP));
 	if (!edges)
 	{
 		ctx->log(RC_LOG_WARNING, "canRemoveVertex: Out of memory 'edges' (%d).", maxEdges*3);
@@ -606,7 +681,7 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short
 	}
 	
 	int nedges = 0;
-	rcScopedDelete<int> edges = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp*4, RC_ALLOC_TEMP);
+	rcScopedDelete<int> edges((int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp*4, RC_ALLOC_TEMP));
 	if (!edges)
 	{
 		ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'edges' (%d).", numRemovedVerts*nvp*4);
@@ -614,15 +689,15 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short
 	}
 
 	int nhole = 0;
-	rcScopedDelete<int> hole = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP);
+	rcScopedDelete<int> hole((int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP));
 	if (!hole)
 	{
 		ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hole' (%d).", numRemovedVerts*nvp);
 		return false;
 	}
-	
+
 	int nhreg = 0;
-	rcScopedDelete<int> hreg = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP);
+	rcScopedDelete<int> hreg((int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP));
 	if (!hreg)
 	{
 		ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hreg' (%d).", numRemovedVerts*nvp);
@@ -630,7 +705,7 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short
 	}
 
 	int nharea = 0;
-	rcScopedDelete<int> harea = (int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP);
+	rcScopedDelete<int> harea((int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP));
 	if (!harea)
 	{
 		ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'harea' (%d).", numRemovedVerts*nvp);
@@ -672,7 +747,7 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short
 	}
 	
 	// Remove vertex.
-	for (int i = (int)rem; i < mesh.nverts; ++i)
+	for (int i = (int)rem; i < mesh.nverts - 1; ++i)
 	{
 		mesh.verts[i*3+0] = mesh.verts[(i+1)*3+0];
 		mesh.verts[i*3+1] = mesh.verts[(i+1)*3+1];
@@ -747,22 +822,22 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short
 			break;
 	}
 
-	rcScopedDelete<int> tris = (int*)rcAlloc(sizeof(int)*nhole*3, RC_ALLOC_TEMP);
+	rcScopedDelete<int> tris((int*)rcAlloc(sizeof(int)*nhole*3, RC_ALLOC_TEMP));
 	if (!tris)
 	{
 		ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tris' (%d).", nhole*3);
 		return false;
 	}
 
-	rcScopedDelete<int> tverts = (int*)rcAlloc(sizeof(int)*nhole*4, RC_ALLOC_TEMP);
+	rcScopedDelete<int> tverts((int*)rcAlloc(sizeof(int)*nhole*4, RC_ALLOC_TEMP));
 	if (!tverts)
 	{
 		ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tverts' (%d).", nhole*4);
 		return false;
 	}
 
-	rcScopedDelete<int> thole = (int*)rcAlloc(sizeof(int)*nhole, RC_ALLOC_TEMP);
-	if (!tverts)
+	rcScopedDelete<int> thole((int*)rcAlloc(sizeof(int)*nhole, RC_ALLOC_TEMP));
+	if (!thole)
 	{
 		ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'thole' (%d).", nhole);
 		return false;
@@ -788,20 +863,20 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short
 	}
 	
 	// Merge the hole triangles back to polygons.
-	rcScopedDelete<unsigned short> polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*(ntris+1)*nvp, RC_ALLOC_TEMP);
+	rcScopedDelete<unsigned short> polys((unsigned short*)rcAlloc(sizeof(unsigned short)*(ntris+1)*nvp, RC_ALLOC_TEMP));
 	if (!polys)
 	{
 		ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'polys' (%d).", (ntris+1)*nvp);
 		return false;
 	}
-	rcScopedDelete<unsigned short> pregs = (unsigned short*)rcAlloc(sizeof(unsigned short)*ntris, RC_ALLOC_TEMP);
+	rcScopedDelete<unsigned short> pregs((unsigned short*)rcAlloc(sizeof(unsigned short)*ntris, RC_ALLOC_TEMP));
 	if (!pregs)
 	{
 		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)
+	rcScopedDelete<unsigned char> pareas((unsigned char*)rcAlloc(sizeof(unsigned char)*ntris, RC_ALLOC_TEMP));
+	if (!pareas)
 	{
 		ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'pareas' (%d).", ntris);
 		return false;
@@ -820,7 +895,14 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short
 			polys[npolys*nvp+0] = (unsigned short)hole[t[0]];
 			polys[npolys*nvp+1] = (unsigned short)hole[t[1]];
 			polys[npolys*nvp+2] = (unsigned short)hole[t[2]];
-			pregs[npolys] = (unsigned short)hreg[t[0]];
+
+			// If this polygon covers multiple region types then
+			// mark it as such
+			if (hreg[t[0]] != hreg[t[1]] || hreg[t[1]] != hreg[t[2]])
+				pregs[npolys] = RC_MULTIPLE_REGS;
+			else
+				pregs[npolys] = (unsigned short)hreg[t[0]];
+
 			pareas[npolys] = (unsigned char)harea[t[0]];
 			npolys++;
 		}
@@ -861,7 +943,10 @@ static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short
 				// Found best, merge.
 				unsigned short* pa = &polys[bestPa*nvp];
 				unsigned short* pb = &polys[bestPb*nvp];
-				mergePolys(pa, pb, bestEa, bestEb, tmpPoly, nvp);
+				mergePolyVerts(pa, pb, bestEa, bestEb, tmpPoly, nvp);
+				if (pregs[bestPa] != pregs[bestPb])
+					pregs[bestPa] = RC_MULTIPLE_REGS;
+
 				unsigned short* last = &polys[(npolys-1)*nvp];
 				if (pb != last)
 					memcpy(pb, last, sizeof(unsigned short)*nvp);
@@ -908,13 +993,14 @@ bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMe
 {
 	rcAssert(ctx);
 	
-	ctx->startTimer(RC_TIMER_BUILD_POLYMESH);
+	rcScopedTimer timer(ctx, RC_TIMER_BUILD_POLYMESH);
 
 	rcVcopy(mesh.bmin, cset.bmin);
 	rcVcopy(mesh.bmax, cset.bmax);
 	mesh.cs = cset.cs;
 	mesh.ch = cset.ch;
 	mesh.borderSize = cset.borderSize;
+	mesh.maxEdgeError = cset.maxError;
 	
 	int maxVertices = 0;
 	int maxTris = 0;
@@ -934,7 +1020,7 @@ bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMe
 		return false;
 	}
 		
-	rcScopedDelete<unsigned char> vflags = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxVertices, RC_ALLOC_TEMP);
+	rcScopedDelete<unsigned char> vflags((unsigned char*)rcAlloc(sizeof(unsigned char)*maxVertices, RC_ALLOC_TEMP));
 	if (!vflags)
 	{
 		ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'vflags' (%d).", maxVertices);
@@ -977,7 +1063,7 @@ bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMe
 	memset(mesh.regs, 0, sizeof(unsigned short)*maxTris);
 	memset(mesh.areas, 0, sizeof(unsigned char)*maxTris);
 	
-	rcScopedDelete<int> nextVert = (int*)rcAlloc(sizeof(int)*maxVertices, RC_ALLOC_TEMP);
+	rcScopedDelete<int> nextVert((int*)rcAlloc(sizeof(int)*maxVertices, RC_ALLOC_TEMP));
 	if (!nextVert)
 	{
 		ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'nextVert' (%d).", maxVertices);
@@ -985,7 +1071,7 @@ bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMe
 	}
 	memset(nextVert, 0, sizeof(int)*maxVertices);
 	
-	rcScopedDelete<int> firstVert = (int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP);
+	rcScopedDelete<int> firstVert((int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP));
 	if (!firstVert)
 	{
 		ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'firstVert' (%d).", VERTEX_BUCKET_COUNT);
@@ -994,19 +1080,19 @@ bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMe
 	for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i)
 		firstVert[i] = -1;
 	
-	rcScopedDelete<int> indices = (int*)rcAlloc(sizeof(int)*maxVertsPerCont, RC_ALLOC_TEMP);
+	rcScopedDelete<int> indices((int*)rcAlloc(sizeof(int)*maxVertsPerCont, RC_ALLOC_TEMP));
 	if (!indices)
 	{
 		ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'indices' (%d).", maxVertsPerCont);
 		return false;
 	}
-	rcScopedDelete<int> tris = (int*)rcAlloc(sizeof(int)*maxVertsPerCont*3, RC_ALLOC_TEMP);
+	rcScopedDelete<int> tris((int*)rcAlloc(sizeof(int)*maxVertsPerCont*3, RC_ALLOC_TEMP));
 	if (!tris)
 	{
 		ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'tris' (%d).", maxVertsPerCont*3);
 		return false;
 	}
-	rcScopedDelete<unsigned short> polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*(maxVertsPerCont+1)*nvp, RC_ALLOC_TEMP);
+	rcScopedDelete<unsigned short> polys((unsigned short*)rcAlloc(sizeof(unsigned short)*(maxVertsPerCont+1)*nvp, RC_ALLOC_TEMP));
 	if (!polys)
 	{
 		ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'polys' (%d).", maxVertsPerCont*nvp);
@@ -1107,7 +1193,7 @@ bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMe
 					// Found best, merge.
 					unsigned short* pa = &polys[bestPa*nvp];
 					unsigned short* pb = &polys[bestPb*nvp];
-					mergePolys(pa, pb, bestEa, bestEb, tmpPoly, nvp);
+					mergePolyVerts(pa, pb, bestEa, bestEb, tmpPoly, nvp);
 					unsigned short* lastPoly = &polys[(npolys-1)*nvp];
 					if (pb != lastPoly)
 						memcpy(pb, lastPoly, sizeof(unsigned short)*nvp);
@@ -1218,8 +1304,6 @@ bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMe
 		ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: The resulting mesh has too many polygons %d (max %d). Data can be corrupted.", mesh.npolys, 0xffff);
 	}
 	
-	ctx->stopTimer(RC_TIMER_BUILD_POLYMESH);
-	
 	return true;
 }
 
@@ -1231,7 +1315,7 @@ bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, r
 	if (!nmeshes || !meshes)
 		return true;
 
-	ctx->startTimer(RC_TIMER_MERGE_POLYMESH);
+	rcScopedTimer timer(ctx, RC_TIMER_MERGE_POLYMESH);
 
 	mesh.nvp = meshes[0]->nvp;
 	mesh.cs = meshes[0]->cs;
@@ -1292,7 +1376,7 @@ bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, r
 	}
 	memset(mesh.flags, 0, sizeof(unsigned short)*maxPolys);
 	
-	rcScopedDelete<int> nextVert = (int*)rcAlloc(sizeof(int)*maxVerts, RC_ALLOC_TEMP);
+	rcScopedDelete<int> nextVert((int*)rcAlloc(sizeof(int)*maxVerts, RC_ALLOC_TEMP));
 	if (!nextVert)
 	{
 		ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'nextVert' (%d).", maxVerts);
@@ -1300,7 +1384,7 @@ bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, r
 	}
 	memset(nextVert, 0, sizeof(int)*maxVerts);
 	
-	rcScopedDelete<int> firstVert = (int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP);
+	rcScopedDelete<int> firstVert((int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP));
 	if (!firstVert)
 	{
 		ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'firstVert' (%d).", VERTEX_BUCKET_COUNT);
@@ -1309,7 +1393,7 @@ bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, r
 	for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i)
 		firstVert[i] = -1;
 
-	rcScopedDelete<unsigned short> vremap = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxVertsPerMesh, RC_ALLOC_PERM);
+	rcScopedDelete<unsigned short> vremap((unsigned short*)rcAlloc(sizeof(unsigned short)*maxVertsPerMesh, RC_ALLOC_PERM));
 	if (!vremap)
 	{
 		ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'vremap' (%d).", maxVertsPerMesh);
@@ -1399,8 +1483,6 @@ bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, r
 		ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: The resulting mesh has too many polygons %d (max %d). Data can be corrupted.", mesh.npolys, 0xffff);
 	}
 	
-	ctx->stopTimer(RC_TIMER_MERGE_POLYMESH);
-	
 	return true;
 }
 
@@ -1424,6 +1506,7 @@ bool rcCopyPolyMesh(rcContext* ctx, const rcPolyMesh& src, rcPolyMesh& dst)
 	dst.cs = src.cs;
 	dst.ch = src.ch;
 	dst.borderSize = src.borderSize;
+	dst.maxEdgeError = src.maxEdgeError;
 	
 	dst.verts = (unsigned short*)rcAlloc(sizeof(unsigned short)*src.nverts*3, RC_ALLOC_PERM);
 	if (!dst.verts)
@@ -1463,7 +1546,7 @@ bool rcCopyPolyMesh(rcContext* ctx, const rcPolyMesh& src, rcPolyMesh& dst)
 		ctx->log(RC_LOG_ERROR, "rcCopyPolyMesh: Out of memory 'dst.flags' (%d).", src.npolys);
 		return false;
 	}
-	memcpy(dst.flags, src.flags, sizeof(unsigned char)*src.npolys);
+	memcpy(dst.flags, src.flags, sizeof(unsigned short)*src.npolys);
 	
 	return true;
 }

File diff suppressed because it is too large
+ 388 - 241
Source/ThirdParty/Recast/Source/RecastMeshDetail.cpp


+ 58 - 28
Source/ThirdParty/Recast/source/RecastRasterization.cpp → Source/ThirdParty/Recast/Source/RecastRasterization.cpp

@@ -50,7 +50,7 @@ static rcSpan* allocSpan(rcHeightfield& hf)
 		// Allocate memory for the new pool.
 		rcSpanPool* pool = (rcSpanPool*)rcAlloc(sizeof(rcSpanPool), RC_ALLOC_PERM);
 		if (!pool) return 0;
-		pool->next = 0;
+
 		// Add the pool into the list of pools.
 		pool->next = hf.pools;
 		hf.pools = pool;
@@ -82,7 +82,7 @@ static void freeSpan(rcHeightfield& hf, rcSpan* ptr)
 	hf.freelist = ptr;
 }
 
-static void addSpan(rcHeightfield& hf, const int x, const int y,
+static bool addSpan(rcHeightfield& hf, const int x, const int y,
 					const unsigned short smin, const unsigned short smax,
 					const unsigned char area, const int flagMergeThr)
 {
@@ -90,6 +90,8 @@ static void addSpan(rcHeightfield& hf, const int x, const int y,
 	int idx = x + y*hf.width;
 	
 	rcSpan* s = allocSpan(hf);
+	if (!s)
+		return false;
 	s->smin = smin;
 	s->smax = smax;
 	s->area = area;
@@ -99,7 +101,7 @@ static void addSpan(rcHeightfield& hf, const int x, const int y,
 	if (!hf.spans[idx])
 	{
 		hf.spans[idx] = s;
-		return;
+		return true;
 	}
 	rcSpan* prev = 0;
 	rcSpan* cur = hf.spans[idx];
@@ -152,6 +154,8 @@ static void addSpan(rcHeightfield& hf, const int x, const int y,
 		s->next = hf.spans[idx];
 		hf.spans[idx] = s;
 	}
+
+	return true;
 }
 
 /// @par
@@ -161,12 +165,19 @@ static void addSpan(rcHeightfield& hf, const int x, const int y,
 /// from the existing span, the span flags are merged.
 ///
 /// @see rcHeightfield, rcSpan.
-void rcAddSpan(rcContext* /*ctx*/, rcHeightfield& hf, const int x, const int y,
+bool rcAddSpan(rcContext* ctx, rcHeightfield& hf, const int x, const int y,
 			   const unsigned short smin, const unsigned short smax,
 			   const unsigned char area, const int flagMergeThr)
 {
-//	rcAssert(ctx);
-	addSpan(hf, x,y, smin, smax, area, flagMergeThr);
+	rcAssert(ctx);
+
+	if (!addSpan(hf, x, y, smin, smax, area, flagMergeThr))
+	{
+		ctx->log(RC_LOG_ERROR, "rcAddSpan: Out of memory.");
+		return false;
+	}
+
+	return true;
 }
 
 // divides a convex polygons into two convex polygons on both sides of a line
@@ -227,7 +238,7 @@ static void dividePoly(const float* in, int nin,
 
 
 
-static void rasterizeTri(const float* v0, const float* v1, const float* v2,
+static bool rasterizeTri(const float* v0, const float* v1, const float* v2,
 						 const unsigned char area, rcHeightfield& hf,
 						 const float* bmin, const float* bmax,
 						 const float cs, const float ics, const float ich,
@@ -248,7 +259,7 @@ static void rasterizeTri(const float* v0, const float* v1, const float* v2,
 	
 	// If the triangle does not touch the bbox of the heightfield, skip the triagle.
 	if (!overlapBounds(bmin, bmax, tmin, tmax))
-		return;
+		return true;
 	
 	// Calculate the footprint of the triangle on the grid's y-axis
 	int y0 = (int)((tmin[2] - bmin[2])*ics);
@@ -315,9 +326,12 @@ static void rasterizeTri(const float* v0, const float* v1, const float* v2,
 			unsigned short ismin = (unsigned short)rcClamp((int)floorf(smin * ich), 0, RC_SPAN_MAX_HEIGHT);
 			unsigned short ismax = (unsigned short)rcClamp((int)ceilf(smax * ich), (int)ismin+1, RC_SPAN_MAX_HEIGHT);
 			
-			addSpan(hf, x, y, ismin, ismax, area, flagMergeThr);
+			if (!addSpan(hf, x, y, ismin, ismax, area, flagMergeThr))
+				return false;
 		}
 	}
+
+	return true;
 }
 
 /// @par
@@ -325,19 +339,23 @@ static void rasterizeTri(const float* v0, const float* v1, const float* v2,
 /// No spans will be added if the triangle does not overlap the heightfield grid.
 ///
 /// @see rcHeightfield
-void rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const float* v2,
+bool rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const float* v2,
 						 const unsigned char area, rcHeightfield& solid,
 						 const int flagMergeThr)
 {
 	rcAssert(ctx);
 
-	ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES);
+	rcScopedTimer timer(ctx, RC_TIMER_RASTERIZE_TRIANGLES);
 
 	const float ics = 1.0f/solid.cs;
 	const float ich = 1.0f/solid.ch;
-	rasterizeTri(v0, v1, v2, area, solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
+	if (!rasterizeTri(v0, v1, v2, area, solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr))
+	{
+		ctx->log(RC_LOG_ERROR, "rcRasterizeTriangle: Out of memory.");
+		return false;
+	}
 
-	ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES);
+	return true;
 }
 
 /// @par
@@ -345,13 +363,13 @@ void rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const
 /// Spans will only be added for triangles that overlap the heightfield grid.
 ///
 /// @see rcHeightfield
-void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/,
+bool rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/,
 						  const int* tris, const unsigned char* areas, const int nt,
 						  rcHeightfield& solid, const int flagMergeThr)
 {
 	rcAssert(ctx);
 
-	ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES);
+	rcScopedTimer timer(ctx, RC_TIMER_RASTERIZE_TRIANGLES);
 	
 	const float ics = 1.0f/solid.cs;
 	const float ich = 1.0f/solid.ch;
@@ -362,10 +380,14 @@ void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/,
 		const float* v1 = &verts[tris[i*3+1]*3];
 		const float* v2 = &verts[tris[i*3+2]*3];
 		// Rasterize.
-		rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
+		if (!rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr))
+		{
+			ctx->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory.");
+			return false;
+		}
 	}
-	
-	ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES);
+
+	return true;
 }
 
 /// @par
@@ -373,13 +395,13 @@ void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/,
 /// Spans will only be added for triangles that overlap the heightfield grid.
 ///
 /// @see rcHeightfield
-void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/,
+bool rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/,
 						  const unsigned short* tris, const unsigned char* areas, const int nt,
 						  rcHeightfield& solid, const int flagMergeThr)
 {
 	rcAssert(ctx);
 
-	ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES);
+	rcScopedTimer timer(ctx, RC_TIMER_RASTERIZE_TRIANGLES);
 	
 	const float ics = 1.0f/solid.cs;
 	const float ich = 1.0f/solid.ch;
@@ -390,10 +412,14 @@ void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/,
 		const float* v1 = &verts[tris[i*3+1]*3];
 		const float* v2 = &verts[tris[i*3+2]*3];
 		// Rasterize.
-		rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
+		if (!rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr))
+		{
+			ctx->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory.");
+			return false;
+		}
 	}
-	
-	ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES);
+
+	return true;
 }
 
 /// @par
@@ -401,12 +427,12 @@ void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/,
 /// Spans will only be added for triangles that overlap the heightfield grid.
 ///
 /// @see rcHeightfield
-void rcRasterizeTriangles(rcContext* ctx, const float* verts, const unsigned char* areas, const int nt,
+bool rcRasterizeTriangles(rcContext* ctx, const float* verts, const unsigned char* areas, const int nt,
 						  rcHeightfield& solid, const int flagMergeThr)
 {
 	rcAssert(ctx);
 	
-	ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES);
+	rcScopedTimer timer(ctx, RC_TIMER_RASTERIZE_TRIANGLES);
 	
 	const float ics = 1.0f/solid.cs;
 	const float ich = 1.0f/solid.ch;
@@ -417,8 +443,12 @@ void rcRasterizeTriangles(rcContext* ctx, const float* verts, const unsigned cha
 		const float* v1 = &verts[(i*3+1)*3];
 		const float* v2 = &verts[(i*3+2)*3];
 		// Rasterize.
-		rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
+		if (!rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr))
+		{
+			ctx->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory.");
+			return false;
+		}
 	}
-	
-	ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES);
+
+	return true;
 }

+ 534 - 148
Source/ThirdParty/Recast/source/RecastRegion.cpp → Source/ThirdParty/Recast/Source/RecastRegion.cpp

@@ -25,8 +25,17 @@
 #include "Recast.h"
 #include "RecastAlloc.h"
 #include "RecastAssert.h"
-#include <new>
 
+namespace
+{
+struct LevelStackEntry
+{
+	LevelStackEntry(int x_, int y_, int index_) : x(x_), y(y_), index(index_) {}
+	int x;
+	int y;
+	int index;
+};
+}  // namespace
 
 static void calculateDistanceField(rcCompactHeightfield& chf, unsigned short* src, unsigned short& maxDist)
 {
@@ -245,17 +254,15 @@ static bool floodRegion(int x, int y, int i,
 						unsigned short level, unsigned short r,
 						rcCompactHeightfield& chf,
 						unsigned short* srcReg, unsigned short* srcDist,
-						rcIntArray& stack)
+						rcTempVector<LevelStackEntry>& 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);
+	stack.clear();
+	stack.push_back(LevelStackEntry(x, y, i));
 	srcReg[i] = r;
 	srcDist[i] = 0;
 	
@@ -264,9 +271,11 @@ static bool floodRegion(int x, int y, int i,
 	
 	while (stack.size() > 0)
 	{
-		int ci = stack.pop();
-		int cy = stack.pop();
-		int cx = stack.pop();
+		LevelStackEntry& back = stack.back();
+		int cx = back.x;
+		int cy = back.y;
+		int ci = back.index;
+		stack.pop_back();
 		
 		const rcCompactSpan& cs = chf.spans[ci];
 		
@@ -315,6 +324,7 @@ static bool floodRegion(int x, int y, int i,
 			srcReg[ci] = 0;
 			continue;
 		}
+		
 		count++;
 		
 		// Expand neighbours.
@@ -331,9 +341,7 @@ static bool floodRegion(int x, int y, int i,
 				{
 					srcReg[ai] = r;
 					srcDist[ai] = 0;
-					stack.push(ax);
-					stack.push(ay);
-					stack.push(ai);
+					stack.push_back(LevelStackEntry(ax, ay, ai));
 				}
 			}
 		}
@@ -342,12 +350,20 @@ static bool floodRegion(int x, int y, int i,
 	return count > 0;
 }
 
-static unsigned short* expandRegions(int maxIter, unsigned short level,
-									 rcCompactHeightfield& chf,
-									 unsigned short* srcReg, unsigned short* srcDist,
-									 unsigned short* dstReg, unsigned short* dstDist, 
-									 rcIntArray& stack,
-									 bool fillStack)
+// Struct to keep track of entries in the region table that have been changed.
+struct DirtyEntry
+{
+	DirtyEntry(int index_, unsigned short region_, unsigned short distance2_)
+		: index(index_), region(region_), distance2(distance2_) {}
+	int index;
+	unsigned short region;
+	unsigned short distance2;
+};
+static void expandRegions(int maxIter, unsigned short level,
+					      rcCompactHeightfield& chf,
+					      unsigned short* srcReg, unsigned short* srcDist,
+					      rcTempVector<LevelStackEntry>& stack,
+					      bool fillStack)
 {
 	const int w = chf.width;
 	const int h = chf.height;
@@ -355,7 +371,7 @@ static unsigned short* expandRegions(int maxIter, unsigned short level,
 	if (fillStack)
 	{
 		// Find cells revealed by the raised level.
-		stack.resize(0);
+		stack.clear();
 		for (int y = 0; y < h; ++y)
 		{
 			for (int x = 0; x < w; ++x)
@@ -365,9 +381,7 @@ static unsigned short* expandRegions(int maxIter, unsigned short level,
 				{
 					if (chf.dist[i] >= level && srcReg[i] == 0 && chf.areas[i] != RC_NULL_AREA)
 					{
-						stack.push(x);
-						stack.push(y);
-						stack.push(i);
+						stack.push_back(LevelStackEntry(x, y, i));
 					}
 				}
 			}
@@ -376,27 +390,26 @@ static unsigned short* expandRegions(int maxIter, unsigned short level,
 	else // use cells in the input stack
 	{
 		// mark all cells which already have a region
-		for (int j=0; j<stack.size(); j+=3)
+		for (int j=0; j<stack.size(); j++)
 		{
-			int i = stack[j+2];
+			int i = stack[j].index;
 			if (srcReg[i] != 0)
-				stack[j+2] = -1;
+				stack[j].index = -1;
 		}
 	}
 
+	rcTempVector<DirtyEntry> dirtyEntries;
 	int iter = 0;
 	while (stack.size() > 0)
 	{
 		int failed = 0;
+		dirtyEntries.clear();
 		
-		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)
+		for (int j = 0; j < stack.size(); j++)
 		{
-			int x = stack[j+0];
-			int y = stack[j+1];
-			int i = stack[j+2];
+			int x = stack[j].x;
+			int y = stack[j].y;
+			int i = stack[j].index;
 			if (i < 0)
 			{
 				failed++;
@@ -425,9 +438,8 @@ static unsigned short* expandRegions(int maxIter, unsigned short level,
 			}
 			if (r)
 			{
-				stack[j+2] = -1; // mark as used
-				dstReg[i] = r;
-				dstDist[i] = d2;
+				stack[j].index = -1; // mark as used
+				dirtyEntries.push_back(DirtyEntry(i, r, d2));
 			}
 			else
 			{
@@ -435,11 +447,14 @@ static unsigned short* expandRegions(int maxIter, unsigned short level,
 			}
 		}
 		
-		// rcSwap source and dest.
-		rcSwap(srcReg, dstReg);
-		rcSwap(srcDist, dstDist);
+		// Copy entries that differ between src and dst to keep them in sync.
+		for (int i = 0; i < dirtyEntries.size(); i++) {
+			int idx = dirtyEntries[i].index;
+			srcReg[idx] = dirtyEntries[i].region;
+			srcDist[idx] = dirtyEntries[i].distance2;
+		}
 		
-		if (failed*3 == stack.size())
+		if (failed == stack.size())
 			break;
 		
 		if (level > 0)
@@ -449,16 +464,14 @@ static unsigned short* expandRegions(int maxIter, unsigned short level,
 				break;
 		}
 	}
-	
-	return srcReg;
 }
 
 
 
 static void sortCellsByLevel(unsigned short startLevel,
 							  rcCompactHeightfield& chf,
-							  unsigned short* srcReg,
-							  unsigned int nbStacks, rcIntArray* stacks,
+							  const unsigned short* srcReg,
+							  unsigned int nbStacks, rcTempVector<LevelStackEntry>* stacks,
 							  unsigned short loglevelsPerStack) // the levels per stack (2 in our case) as a bit shift
 {
 	const int w = chf.width;
@@ -466,7 +479,7 @@ static void sortCellsByLevel(unsigned short startLevel,
 	startLevel = startLevel >> loglevelsPerStack;
 
 	for (unsigned int j=0; j<nbStacks; ++j)
-		stacks[j].resize(0);
+		stacks[j].clear();
 
 	// put all cells in the level range into the appropriate stacks
 	for (int y = 0; y < h; ++y)
@@ -486,26 +499,23 @@ static void sortCellsByLevel(unsigned short startLevel,
 				if (sId < 0)
 					sId = 0;
 
-				stacks[sId].push(x);
-				stacks[sId].push(y);
-				stacks[sId].push(i);
+				stacks[sId].push_back(LevelStackEntry(x, y, i));
 			}
 		}
 	}
 }
 
 
-static void appendStacks(rcIntArray& srcStack, rcIntArray& dstStack,
-						 unsigned short* srcReg)
+static void appendStacks(const rcTempVector<LevelStackEntry>& srcStack,
+						 rcTempVector<LevelStackEntry>& dstStack,
+						 const unsigned short* srcReg)
 {
-	for (int j=0; j<srcStack.size(); j+=3)
+	for (int j=0; j<srcStack.size(); j++)
 	{
-		int i = srcStack[j+2];
+		int i = srcStack[j].index;
 		if ((i < 0) || (srcReg[i] != 0))
 			continue;
-		dstStack.push(srcStack[j]);
-		dstStack.push(srcStack[j+1]);
-		dstStack.push(srcStack[j+2]);
+		dstStack.push_back(srcStack[j]);
 	}
 }
 
@@ -516,7 +526,11 @@ struct rcRegion
 		id(i),
 		areaType(0),
 		remap(false),
-		visited(false)
+		visited(false),
+		overlap(false),
+		connectsToBorder(false),
+		ymin(0xffff),
+		ymax(0)
 	{}
 	
 	int spanCount;					// Number of spans belonging to this region
@@ -524,6 +538,9 @@ struct rcRegion
 	unsigned char areaType;			// Are type.
 	bool remap;
 	bool visited;
+	bool overlap;
+	bool connectsToBorder;
+	unsigned short ymin, ymax;
 	rcIntArray connections;
 	rcIntArray floors;
 };
@@ -663,7 +680,7 @@ static bool isRegionConnectedToBorder(const rcRegion& reg)
 	return false;
 }
 
-static bool isSolidEdge(rcCompactHeightfield& chf, unsigned short* srcReg,
+static bool isSolidEdge(rcCompactHeightfield& chf, const unsigned short* srcReg,
 						int x, int y, int i, int dir)
 {
 	const rcCompactSpan& s = chf.spans[i];
@@ -682,7 +699,7 @@ static bool isSolidEdge(rcCompactHeightfield& chf, unsigned short* srcReg,
 
 static void walkContour(int x, int y, int i, int dir,
 						rcCompactHeightfield& chf,
-						unsigned short* srcReg,
+						const unsigned short* srcReg,
 						rcIntArray& cont)
 {
 	int startDir = dir;
@@ -768,25 +785,25 @@ static void walkContour(int x, int y, int i, int dir,
 	}
 }
 
-static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegionSize,
-							   unsigned short& maxRegionId,
-							   rcCompactHeightfield& chf,
-							   unsigned short* srcReg)
+
+static bool mergeAndFilterRegions(rcContext* ctx, int minRegionArea, int mergeRegionSize,
+								  unsigned short& maxRegionId,
+								  rcCompactHeightfield& chf,
+								  unsigned short* srcReg, rcIntArray& overlaps)
 {
 	const int w = chf.width;
 	const int h = chf.height;
 	
 	const int nreg = maxRegionId+1;
-	rcRegion* regions = (rcRegion*)rcAlloc(sizeof(rcRegion)*nreg, RC_ALLOC_TEMP);
-	if (!regions)
-	{
-		ctx->log(RC_LOG_ERROR, "filterSmallRegions: Out of memory 'regions' (%d).", nreg);
+	rcTempVector<rcRegion> regions;
+	if (!regions.reserve(nreg)) {
+		ctx->log(RC_LOG_ERROR, "mergeAndFilterRegions: Out of memory 'regions' (%d).", nreg);
 		return false;
 	}
 
 	// Construct regions
 	for (int i = 0; i < nreg; ++i)
-		new(&regions[i]) rcRegion((unsigned short)i);
+		regions.push_back(rcRegion((unsigned short) i));
 	
 	// Find edge of a region and find connections around the contour.
 	for (int y = 0; y < h; ++y)
@@ -803,7 +820,6 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio
 				rcRegion& reg = regions[r];
 				reg.spanCount++;
 				
-				
 				// Update floors.
 				for (int j = (int)c.index; j < ni; ++j)
 				{
@@ -811,6 +827,8 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio
 					unsigned short floorId = srcReg[j];
 					if (floorId == 0 || floorId >= nreg)
 						continue;
+					if (floorId == r)
+						reg.overlap = true;
 					addUniqueFloorRegion(reg, floorId);
 				}
 				
@@ -906,7 +924,7 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio
 			}
 		}
 	}
-		
+	
 	// Merge too small regions to neighbour regions.
 	int mergeCount = 0 ;
 	do
@@ -916,7 +934,9 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio
 		{
 			rcRegion& reg = regions[i];
 			if (reg.id == 0 || (reg.id & RC_BORDER_REG))
-				continue;                       
+				continue;
+			if (reg.overlap)
+				continue;
 			if (reg.spanCount == 0)
 				continue;
 			
@@ -933,7 +953,7 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio
 			{
 				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.id == 0 || (mreg.id & RC_BORDER_REG) || mreg.overlap) continue;
 				if (mreg.spanCount < smallest &&
 					canMergeWithRegion(reg, mreg) &&
 					canMergeWithRegion(mreg, reg))
@@ -1003,14 +1023,224 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio
 		if ((srcReg[i] & RC_BORDER_REG) == 0)
 			srcReg[i] = regions[srcReg[i]].id;
 	}
+
+	// Return regions that we found to be overlapping.
+	for (int i = 0; i < nreg; ++i)
+		if (regions[i].overlap)
+			overlaps.push(regions[i].id);
+
+	return true;
+}
+
+
+static void addUniqueConnection(rcRegion& reg, int n)
+{
+	for (int i = 0; i < reg.connections.size(); ++i)
+		if (reg.connections[i] == n)
+			return;
+	reg.connections.push(n);
+}
+
+static bool mergeAndFilterLayerRegions(rcContext* ctx, int minRegionArea,
+									   unsigned short& maxRegionId,
+									   rcCompactHeightfield& chf,
+									   unsigned short* srcReg)
+{
+	const int w = chf.width;
+	const int h = chf.height;
 	
+	const int nreg = maxRegionId+1;
+	rcTempVector<rcRegion> regions;
+	
+	// Construct regions
+	if (!regions.reserve(nreg)) {
+		ctx->log(RC_LOG_ERROR, "mergeAndFilterLayerRegions: Out of memory 'regions' (%d).", nreg);
+		return false;
+	}
 	for (int i = 0; i < nreg; ++i)
-		regions[i].~rcRegion();
-	rcFree(regions);
+		regions.push_back(rcRegion((unsigned short) i));
+	
+	// Find region neighbours and overlapping regions.
+	rcIntArray lregs(32);
+	for (int y = 0; y < h; ++y)
+	{
+		for (int x = 0; x < w; ++x)
+		{
+			const rcCompactCell& c = chf.cells[x+y*w];
+
+			lregs.resize(0);
+			
+			for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+			{
+				const rcCompactSpan& s = chf.spans[i];
+				const unsigned short ri = srcReg[i];
+				if (ri == 0 || ri >= nreg) continue;
+				rcRegion& reg = regions[ri];
+				
+				reg.spanCount++;
+				
+				reg.ymin = rcMin(reg.ymin, s.y);
+				reg.ymax = rcMax(reg.ymax, s.y);
+				
+				// Collect all region layers.
+				lregs.push(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 short rai = srcReg[ai];
+						if (rai > 0 && rai < nreg && rai != ri)
+							addUniqueConnection(reg, rai);
+						if (rai & RC_BORDER_REG)
+							reg.connectsToBorder = true;
+					}
+				}
+				
+			}
+			
+			// Update overlapping regions.
+			for (int i = 0; i < lregs.size()-1; ++i)
+			{
+				for (int j = i+1; j < lregs.size(); ++j)
+				{
+					if (lregs[i] != lregs[j])
+					{
+						rcRegion& ri = regions[lregs[i]];
+						rcRegion& rj = regions[lregs[j]];
+						addUniqueFloorRegion(ri, lregs[j]);
+						addUniqueFloorRegion(rj, lregs[i]);
+					}
+				}
+			}
+			
+		}
+	}
+
+	// Create 2D layers from regions.
+	unsigned short layerId = 1;
+
+	for (int i = 0; i < nreg; ++i)
+		regions[i].id = 0;
+
+	// Merge montone regions to create non-overlapping areas.
+	rcIntArray stack(32);
+	for (int i = 1; i < nreg; ++i)
+	{
+		rcRegion& root = regions[i];
+		// Skip already visited.
+		if (root.id != 0)
+			continue;
+		
+		// Start search.
+		root.id = layerId;
+
+		stack.resize(0);
+		stack.push(i);
+		
+		while (stack.size() > 0)
+		{
+			// Pop front
+			rcRegion& reg = regions[stack[0]];
+			for (int j = 0; j < stack.size()-1; ++j)
+				stack[j] = stack[j+1];
+			stack.resize(stack.size()-1);
+			
+			const int ncons = (int)reg.connections.size();
+			for (int j = 0; j < ncons; ++j)
+			{
+				const int nei = reg.connections[j];
+				rcRegion& regn = regions[nei];
+				// Skip already visited.
+				if (regn.id != 0)
+					continue;
+				// Skip if the neighbour is overlapping root region.
+				bool overlap = false;
+				for (int k = 0; k < root.floors.size(); k++)
+				{
+					if (root.floors[k] == nei)
+					{
+						overlap = true;
+						break;
+					}
+				}
+				if (overlap)
+					continue;
+					
+				// Deepen
+				stack.push(nei);
+					
+				// Mark layer id
+				regn.id = layerId;
+				// Merge current layers to root.
+				for (int k = 0; k < regn.floors.size(); ++k)
+					addUniqueFloorRegion(root, regn.floors[k]);
+				root.ymin = rcMin(root.ymin, regn.ymin);
+				root.ymax = rcMax(root.ymax, regn.ymax);
+				root.spanCount += regn.spanCount;
+				regn.spanCount = 0;
+				root.connectsToBorder = root.connectsToBorder || regn.connectsToBorder;
+			}
+		}
+		
+		layerId++;
+	}
+	
+	// Remove small regions
+	for (int i = 0; i < nreg; ++i)
+	{
+		if (regions[i].spanCount > 0 && regions[i].spanCount < minRegionArea && !regions[i].connectsToBorder)
+		{
+			unsigned short reg = regions[i].id;
+			for (int j = 0; j < nreg; ++j)
+				if (regions[j].id == reg)
+					regions[j].id = 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.
+		regions[i].remap = true;
+	}
+	
+	unsigned short regIdGen = 0;
+	for (int i = 0; i < nreg; ++i)
+	{
+		if (!regions[i].remap)
+			continue;
+		unsigned short oldId = regions[i].id;
+		unsigned short newId = ++regIdGen;
+		for (int j = i; j < nreg; ++j)
+		{
+			if (regions[j].id == oldId)
+			{
+				regions[j].id = newId;
+				regions[j].remap = false;
+			}
+		}
+	}
+	maxRegionId = regIdGen;
+	
+	// Remap regions.
+	for (int i = 0; i < chf.spanCount; ++i)
+	{
+		if ((srcReg[i] & RC_BORDER_REG) == 0)
+			srcReg[i] = regions[srcReg[i]].id;
+	}
 	
 	return true;
 }
 
+
+
 /// @par
 /// 
 /// This is usually the second to the last step in creating a fully built
@@ -1025,7 +1255,7 @@ bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf)
 {
 	rcAssert(ctx);
 	
-	ctx->startTimer(RC_TIMER_BUILD_DISTANCEFIELD);
+	rcScopedTimer timer(ctx, RC_TIMER_BUILD_DISTANCEFIELD);
 	
 	if (chf.dist)
 	{
@@ -1049,25 +1279,23 @@ bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf)
 	
 	unsigned short maxDist = 0;
 
-	ctx->startTimer(RC_TIMER_BUILD_DISTANCEFIELD_DIST);
-	
-	calculateDistanceField(chf, src, maxDist);
-	chf.maxDistance = maxDist;
-	
-	ctx->stopTimer(RC_TIMER_BUILD_DISTANCEFIELD_DIST);
-	
-	ctx->startTimer(RC_TIMER_BUILD_DISTANCEFIELD_BLUR);
-	
-	// Blur
-	if (boxBlur(chf, 1, src, dst) != src)
-		rcSwap(src, dst);
-	
-	// Store distance.
-	chf.dist = src;
-	
-	ctx->stopTimer(RC_TIMER_BUILD_DISTANCEFIELD_BLUR);
+	{
+		rcScopedTimer timerDist(ctx, RC_TIMER_BUILD_DISTANCEFIELD_DIST);
+
+		calculateDistanceField(chf, src, maxDist);
+		chf.maxDistance = maxDist;
+	}
+
+	{
+		rcScopedTimer timerBlur(ctx, RC_TIMER_BUILD_DISTANCEFIELD_BLUR);
+
+		// Blur
+		if (boxBlur(chf, 1, src, dst) != src)
+			rcSwap(src, dst);
 
-	ctx->stopTimer(RC_TIMER_BUILD_DISTANCEFIELD);
+		// Store distance.
+		chf.dist = src;
+	}
 	
 	rcFree(dst);
 	
@@ -1127,13 +1355,13 @@ bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf,
 {
 	rcAssert(ctx);
 	
-	ctx->startTimer(RC_TIMER_BUILD_REGIONS);
+	rcScopedTimer timer(ctx, RC_TIMER_BUILD_REGIONS);
 	
 	const int w = chf.width;
 	const int h = chf.height;
 	unsigned short id = 1;
 	
-	rcScopedDelete<unsigned short> srcReg = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP);
+	rcScopedDelete<unsigned short> srcReg((unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP));
 	if (!srcReg)
 	{
 		ctx->log(RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'src' (%d).", chf.spanCount);
@@ -1142,7 +1370,7 @@ bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf,
 	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);
+	rcScopedDelete<rcSweepSpan> sweeps((rcSweepSpan*)rcAlloc(sizeof(rcSweepSpan)*nsweeps, RC_ALLOC_TEMP));
 	if (!sweeps)
 	{
 		ctx->log(RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'sweeps' (%d).", nsweeps);
@@ -1161,9 +1389,9 @@ bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf,
 		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;
 	}
+
+	chf.borderSize = borderSize;
 	
 	rcIntArray prev(256);
 
@@ -1256,20 +1484,22 @@ bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf,
 		}
 	}
 
-	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;
+	{
+		rcScopedTimer timerFilter(ctx, RC_TIMER_BUILD_REGIONS_FILTER);
 
-	ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER);
+		// Merge regions and filter out small regions.
+		rcIntArray overlaps;
+		chf.maxRegions = id;
+		if (!mergeAndFilterRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg, overlaps))
+			return false;
+
+		// Monotone partitioning does not generate overlapping regions.
+	}
 	
 	// Store the result out.
 	for (int i = 0; i < chf.spanCount; ++i)
 		chf.spans[i].reg = srcReg[i];
-	
-	ctx->stopTimer(RC_TIMER_BUILD_REGIONS);
 
 	return true;
 }
@@ -1298,12 +1528,12 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
 {
 	rcAssert(ctx);
 	
-	ctx->startTimer(RC_TIMER_BUILD_REGIONS);
+	rcScopedTimer timer(ctx, RC_TIMER_BUILD_REGIONS);
 	
 	const int w = chf.width;
 	const int h = chf.height;
 	
-	rcScopedDelete<unsigned short> buf = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount*4, RC_ALLOC_TEMP);
+	rcScopedDelete<unsigned short> buf((unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount*2, RC_ALLOC_TEMP));
 	if (!buf)
 	{
 		ctx->log(RC_LOG_ERROR, "rcBuildRegions: Out of memory 'tmp' (%d).", chf.spanCount*4);
@@ -1314,17 +1544,15 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
 
 	const int LOG_NB_STACKS = 3;
 	const int NB_STACKS = 1 << LOG_NB_STACKS;
-	rcIntArray lvlStacks[NB_STACKS];
+	rcTempVector<LevelStackEntry> lvlStacks[NB_STACKS];
 	for (int i=0; i<NB_STACKS; ++i)
-		lvlStacks[i].resize(1024);
+		lvlStacks[i].reserve(256);
 
-	rcIntArray stack(1024);
-	rcIntArray visited(1024);
+	rcTempVector<LevelStackEntry> stack;
+	stack.reserve(256);
 	
 	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(srcReg, 0, sizeof(unsigned short)*chf.spanCount);
 	memset(srcDist, 0, sizeof(unsigned short)*chf.spanCount);
@@ -1343,14 +1571,15 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
 		// 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++;
-
-		chf.borderSize = borderSize;
 	}
+
+	chf.borderSize = borderSize;
 	
 	int sId = -1;
 	while (level > 0)
@@ -1367,60 +1596,217 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
 
 //		ctx->stopTimer(RC_TIMER_DIVIDE_TO_LEVELS);
 
-		ctx->startTimer(RC_TIMER_BUILD_REGIONS_EXPAND);
-		
-		// Expand current regions until no empty connected cells found.
-		if (expandRegions(expandIters, level, chf, srcReg, srcDist, dstReg, dstDist, lvlStacks[sId], false) != srcReg)
 		{
-			rcSwap(srcReg, dstReg);
-			rcSwap(srcDist, dstDist);
+			rcScopedTimer timerExpand(ctx, RC_TIMER_BUILD_REGIONS_EXPAND);
+
+			// Expand current regions until no empty connected cells found.
+			expandRegions(expandIters, level, chf, srcReg, srcDist, lvlStacks[sId], false);
 		}
 		
-		ctx->stopTimer(RC_TIMER_BUILD_REGIONS_EXPAND);
-		
-		ctx->startTimer(RC_TIMER_BUILD_REGIONS_FLOOD);
-		
-		// Mark new regions with IDs.
-		for (int j=0; j<lvlStacks[sId].size(); j+=3)
 		{
-			int x = lvlStacks[sId][j];
-			int y = lvlStacks[sId][j+1];
-			int i = lvlStacks[sId][j+2];
-			if (i >= 0 && srcReg[i] == 0)
+			rcScopedTimer timerFloor(ctx, RC_TIMER_BUILD_REGIONS_FLOOD);
+
+			// Mark new regions with IDs.
+			for (int j = 0; j<lvlStacks[sId].size(); j++)
 			{
-				if (floodRegion(x, y, i, level, regionId, chf, srcReg, srcDist, stack))
-					regionId++;
+				LevelStackEntry current = lvlStacks[sId][j];
+				int x = current.x;
+				int y = current.y;
+				int i = current.index;
+				if (i >= 0 && srcReg[i] == 0)
+				{
+					if (floodRegion(x, y, i, level, regionId, chf, srcReg, srcDist, stack))
+					{
+						if (regionId == 0xFFFF)
+						{
+							ctx->log(RC_LOG_ERROR, "rcBuildRegions: Region ID overflow");
+							return false;
+						}
+						
+						regionId++;
+					}
+				}
 			}
 		}
-		
-		ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FLOOD);
 	}
 	
 	// Expand current regions until no empty connected cells found.
-	if (expandRegions(expandIters*8, 0, chf, srcReg, srcDist, dstReg, dstDist, stack, true) != srcReg)
+	expandRegions(expandIters*8, 0, chf, srcReg, srcDist, stack, true);
+	
+	ctx->stopTimer(RC_TIMER_BUILD_REGIONS_WATERSHED);
+	
 	{
-		rcSwap(srcReg, dstReg);
-		rcSwap(srcDist, dstDist);
+		rcScopedTimer timerFilter(ctx, RC_TIMER_BUILD_REGIONS_FILTER);
+
+		// Merge regions and filter out smalle regions.
+		rcIntArray overlaps;
+		chf.maxRegions = regionId;
+		if (!mergeAndFilterRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg, overlaps))
+			return false;
+
+		// If overlapping regions were found during merging, split those regions.
+		if (overlaps.size() > 0)
+		{
+			ctx->log(RC_LOG_ERROR, "rcBuildRegions: %d overlapping regions.", overlaps.size());
+		}
 	}
+		
+	// Write the result out.
+	for (int i = 0; i < chf.spanCount; ++i)
+		chf.spans[i].reg = srcReg[i];
 	
-	ctx->stopTimer(RC_TIMER_BUILD_REGIONS_WATERSHED);
+	return true;
+}
+
+
+bool rcBuildLayerRegions(rcContext* ctx, rcCompactHeightfield& chf,
+						 const int borderSize, const int minRegionArea)
+{
+	rcAssert(ctx);
+	
+	rcScopedTimer timer(ctx, RC_TIMER_BUILD_REGIONS);
 	
-	ctx->startTimer(RC_TIMER_BUILD_REGIONS_FILTER);
+	const int w = chf.width;
+	const int h = chf.height;
+	unsigned short id = 1;
 	
-	// Filter out small regions.
-	chf.maxRegions = regionId;
-	if (!filterSmallRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg))
+	rcScopedDelete<unsigned short> srcReg((unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP));
+	if (!srcReg)
+	{
+		ctx->log(RC_LOG_ERROR, "rcBuildLayerRegions: Out of memory 'src' (%d).", chf.spanCount);
 		return false;
+	}
+	memset(srcReg,0,sizeof(unsigned short)*chf.spanCount);
 	
-	ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER);
+	const int nsweeps = rcMax(chf.width,chf.height);
+	rcScopedDelete<rcSweepSpan> sweeps((rcSweepSpan*)rcAlloc(sizeof(rcSweepSpan)*nsweeps, RC_ALLOC_TEMP));
+	if (!sweeps)
+	{
+		ctx->log(RC_LOG_ERROR, "rcBuildLayerRegions: Out of memory 'sweeps' (%d).", nsweeps);
+		return false;
+	}
+	
+	
+	// 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)
+	{
+		// Collect spans from this row.
+		prev.resize(id+1);
+		memset(&prev[0],0,sizeof(int)*id);
+		unsigned short rid = 1;
 		
-	// Write the result out.
+		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;
+			}
+		}
+	}
+	
+	
+	{
+		rcScopedTimer timerFilter(ctx, RC_TIMER_BUILD_REGIONS_FILTER);
+
+		// Merge monotone regions to layers and remove small regions.
+		chf.maxRegions = id;
+		if (!mergeAndFilterLayerRegions(ctx, minRegionArea, chf.maxRegions, chf, srcReg))
+			return false;
+	}
+	
+	
+	// Store the result out.
 	for (int i = 0; i < chf.spanCount; ++i)
 		chf.spans[i].reg = srcReg[i];
 	
-	ctx->stopTimer(RC_TIMER_BUILD_REGIONS);
-	
 	return true;
 }
-
-

+ 0 - 124
Source/ThirdParty/Recast/include/RecastAlloc.h

@@ -1,124 +0,0 @@
-//
-// Copyright (c) 2009-2010 Mikko Mononen [email protected]
-//
-// 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.
-///  @param[in]		ptr		A pointer to a memory block previously allocated using #rcAllocFunc.
-/// @see rcAllocSetCustom
-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.
-/// @see rcFree
-void* rcAlloc(int size, rcAllocHint hint);
-
-/// Deallocates a memory block.
-///  @param[in]		ptr		A pointer to a memory block previously allocated using #rcAlloc.
-/// @see 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

+ 1 - 1
Source/Urho3D/Navigation/DynamicNavigationMesh.cpp

@@ -191,7 +191,7 @@ struct LinearAllocator : public dtTileCacheAlloc
         top = 0;
     }
 
-    void* alloc(const int size) override
+    void* alloc(const size_t size) override
     {
         if (!buffer)
             return nullptr;

Some files were not shown because too many files changed in this diff