From d2dacaa7248a00c6a1e23dd0e94b6cb40d1e0d46 Mon Sep 17 00:00:00 2001 From: Mikulas Florek Date: Mon, 23 Sep 2024 23:57:35 +0200 Subject: [PATCH] 3rd party refactor --- docs/3rdparty.md | 12 + .../release => win}/freetype.lib | Bin external/recast/include/DebugDraw.h | 223 - external/recast/include/DetourDebugDraw.h | 48 - external/recast/include/RecastDebugDraw.h | 42 - .../lib/win64_vs2017/release/recast.lib | Bin 1445884 -> 0 bytes .../lib/win64_vs2017/release/recast.pdb | Bin 200704 -> 0 bytes .../RecastDump.h => src/DetourAlloc.cpp} | 43 +- external/recast/src/DetourAssert.cpp | 35 + external/recast/src/DetourCommon.cpp | 387 ++ external/recast/src/DetourCrowd.cpp | 1 + external/recast/src/DetourNavMesh.cpp | 1584 +++++++ external/recast/src/DetourNavMeshBuilder.cpp | 802 ++++ external/recast/src/DetourNavMeshQuery.cpp | 3663 +++++++++++++++++ external/recast/src/DetourNode.cpp | 200 + external/recast/src/Recast.cpp | 575 +++ external/recast/src/RecastAlloc.cpp | 60 + external/recast/src/RecastArea.cpp | 591 +++ external/recast/src/RecastAssert.cpp | 35 + external/recast/src/RecastContour.cpp | 1105 +++++ external/recast/src/RecastFilter.cpp | 202 + external/recast/src/RecastLayers.cpp | 644 +++ external/recast/src/RecastMesh.cpp | 1552 +++++++ external/recast/src/RecastMeshDetail.cpp | 1464 +++++++ external/recast/src/RecastRasterization.cpp | 454 ++ external/recast/src/RecastRegion.cpp | 1812 ++++++++ external/recast/src/detour_unity.cpp | 6 - external/recast/src/recast_unity.cpp | 22 + scripts/download_deploy_recast.bat | 34 + scripts/download_freetype.bat | 20 + scripts/genie.lua | 214 +- scripts/main.bat | 192 +- scripts/main.sh | 2 - scripts/recastnavigation.lua | 29 - 34 files changed, 15417 insertions(+), 636 deletions(-) create mode 100644 docs/3rdparty.md rename external/freetype/lib/{win64_vs2017/release => win}/freetype.lib (100%) delete mode 100644 external/recast/include/DebugDraw.h delete mode 100644 external/recast/include/DetourDebugDraw.h delete mode 100644 external/recast/include/RecastDebugDraw.h delete mode 100644 external/recast/lib/win64_vs2017/release/recast.lib delete mode 100644 external/recast/lib/win64_vs2017/release/recast.pdb rename external/recast/{include/RecastDump.h => src/DetourAlloc.cpp} (53%) create mode 100644 external/recast/src/DetourAssert.cpp create mode 100644 external/recast/src/DetourCommon.cpp create mode 100644 external/recast/src/DetourNavMesh.cpp create mode 100644 external/recast/src/DetourNavMeshBuilder.cpp create mode 100644 external/recast/src/DetourNavMeshQuery.cpp create mode 100644 external/recast/src/DetourNode.cpp create mode 100644 external/recast/src/Recast.cpp create mode 100644 external/recast/src/RecastAlloc.cpp create mode 100644 external/recast/src/RecastArea.cpp create mode 100644 external/recast/src/RecastAssert.cpp create mode 100644 external/recast/src/RecastContour.cpp create mode 100644 external/recast/src/RecastFilter.cpp create mode 100644 external/recast/src/RecastLayers.cpp create mode 100644 external/recast/src/RecastMesh.cpp create mode 100644 external/recast/src/RecastMeshDetail.cpp create mode 100644 external/recast/src/RecastRasterization.cpp create mode 100644 external/recast/src/RecastRegion.cpp delete mode 100644 external/recast/src/detour_unity.cpp create mode 100644 external/recast/src/recast_unity.cpp create mode 100644 scripts/download_deploy_recast.bat create mode 100644 scripts/download_freetype.bat delete mode 100644 scripts/recastnavigation.lua diff --git a/docs/3rdparty.md b/docs/3rdparty.md new file mode 100644 index 0000000000..c786d81a8d --- /dev/null +++ b/docs/3rdparty.md @@ -0,0 +1,12 @@ +On Windows all 3rd party libraries are vendored in the LumixEngine repository, either as a binary, or in form of a source code. Everything should work out of the box. + +## Recast + +We utilize [Recast & Detour](https://github.com/recastnavigation/recastnavigation) for navigation purposes. The source code for Recast is included in our repository under [external/recast/src](../external/recast/src). To update Recast, you can run the provided [batch script](../scripts/download_deploy_recast.bat) which will download the latest version and copy it to the appropriate directory. Recast is included as a [unity build](https://en.wikipedia.org/wiki/Unity_build). Recast is build as a part of *navigation* plugin. + +## FreeType2 + +We utilize [FreeType2](https://github.com/nem0/freetype2.git) for text rendering. On Windows, prebuilt library is included in [external/freetype/lib/win](../external/freetype/lib/win/) and is used by default. If you want to build FreeType from source code, use following steps: +1. Run [download_freetype.bat](../scripts/download_freetype.bat) to download FreeType source code. +2. Regenerate project using *GENie*. +3. Build project. \ No newline at end of file diff --git a/external/freetype/lib/win64_vs2017/release/freetype.lib b/external/freetype/lib/win/freetype.lib similarity index 100% rename from external/freetype/lib/win64_vs2017/release/freetype.lib rename to external/freetype/lib/win/freetype.lib diff --git a/external/recast/include/DebugDraw.h b/external/recast/include/DebugDraw.h deleted file mode 100644 index 00b544d1c0..0000000000 --- a/external/recast/include/DebugDraw.h +++ /dev/null @@ -1,223 +0,0 @@ -// -// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// 3. This notice may not be removed or altered from any source distribution. -// - -#ifndef DEBUGDRAW_H -#define DEBUGDRAW_H - -// Some math headers don't have PI defined. -static const float DU_PI = 3.14159265f; - -enum duDebugDrawPrimitives -{ - DU_DRAW_POINTS, - DU_DRAW_LINES, - DU_DRAW_TRIS, - DU_DRAW_QUADS, -}; - -/// Abstract debug draw interface. -struct duDebugDraw -{ - virtual ~duDebugDraw() = 0; - - virtual void depthMask(bool state) = 0; - - virtual void texture(bool state) = 0; - - /// Begin drawing primitives. - /// @param prim [in] primitive type to draw, one of rcDebugDrawPrimitives. - /// @param size [in] size of a primitive, applies to point size and line width only. - virtual void begin(duDebugDrawPrimitives prim, float size = 1.0f) = 0; - - /// Submit a vertex - /// @param pos [in] position of the verts. - /// @param color [in] color of the verts. - virtual void vertex(const float* pos, unsigned int color) = 0; - - /// Submit a vertex - /// @param x,y,z [in] position of the verts. - /// @param color [in] color of the verts. - virtual void vertex(const float x, const float y, const float z, unsigned int color) = 0; - - /// Submit a vertex - /// @param pos [in] position of the verts. - /// @param color [in] color of the verts. - virtual void vertex(const float* pos, unsigned int color, const float* uv) = 0; - - /// Submit a vertex - /// @param x,y,z [in] position of the verts. - /// @param color [in] color of the verts. - virtual void vertex(const float x, const float y, const float z, unsigned int color, const float u, const float v) = 0; - - /// End drawing primitives. - virtual void end() = 0; - - /// Compute a color for given area. - virtual unsigned int areaToCol(unsigned int area); -}; - -inline unsigned int duRGBA(int r, int g, int b, int a) -{ - return ((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16) | ((unsigned int)a << 24); -} - -inline unsigned int duRGBAf(float fr, float fg, float fb, float fa) -{ - unsigned char r = (unsigned char)(fr*255.0f); - unsigned char g = (unsigned char)(fg*255.0f); - unsigned char b = (unsigned char)(fb*255.0f); - unsigned char a = (unsigned char)(fa*255.0f); - return duRGBA(r,g,b,a); -} - -unsigned int duIntToCol(int i, int a); -void duIntToCol(int i, float* col); - -inline unsigned int duMultCol(const unsigned int col, const unsigned int d) -{ - const unsigned int r = col & 0xff; - const unsigned int g = (col >> 8) & 0xff; - const unsigned int b = (col >> 16) & 0xff; - const unsigned int a = (col >> 24) & 0xff; - return duRGBA((r*d) >> 8, (g*d) >> 8, (b*d) >> 8, a); -} - -inline unsigned int duDarkenCol(unsigned int col) -{ - return ((col >> 1) & 0x007f7f7f) | (col & 0xff000000); -} - -inline unsigned int duLerpCol(unsigned int ca, unsigned int cb, unsigned int u) -{ - const unsigned int ra = ca & 0xff; - const unsigned int ga = (ca >> 8) & 0xff; - const unsigned int ba = (ca >> 16) & 0xff; - const unsigned int aa = (ca >> 24) & 0xff; - const unsigned int rb = cb & 0xff; - const unsigned int gb = (cb >> 8) & 0xff; - const unsigned int bb = (cb >> 16) & 0xff; - const unsigned int ab = (cb >> 24) & 0xff; - - unsigned int r = (ra*(255-u) + rb*u)/255; - unsigned int g = (ga*(255-u) + gb*u)/255; - unsigned int b = (ba*(255-u) + bb*u)/255; - unsigned int a = (aa*(255-u) + ab*u)/255; - return duRGBA(r,g,b,a); -} - -inline unsigned int duTransCol(unsigned int c, unsigned int a) -{ - return (a<<24) | (c & 0x00ffffff); -} - - -void duCalcBoxColors(unsigned int* colors, unsigned int colTop, unsigned int colSide); - -void duDebugDrawCylinderWire(struct duDebugDraw* dd, float minx, float miny, float minz, - float maxx, float maxy, float maxz, unsigned int col, const float lineWidth); - -void duDebugDrawBoxWire(struct duDebugDraw* dd, float minx, float miny, float minz, - float maxx, float maxy, float maxz, unsigned int col, const float lineWidth); - -void duDebugDrawArc(struct duDebugDraw* dd, const float x0, const float y0, const float z0, - const float x1, const float y1, const float z1, const float h, - const float as0, const float as1, unsigned int col, const float lineWidth); - -void duDebugDrawArrow(struct duDebugDraw* dd, const float x0, const float y0, const float z0, - const float x1, const float y1, const float z1, - const float as0, const float as1, unsigned int col, const float lineWidth); - -void duDebugDrawCircle(struct duDebugDraw* dd, const float x, const float y, const float z, - const float r, unsigned int col, const float lineWidth); - -void duDebugDrawCross(struct duDebugDraw* dd, const float x, const float y, const float z, - const float size, unsigned int col, const float lineWidth); - -void duDebugDrawBox(struct duDebugDraw* dd, float minx, float miny, float minz, - float maxx, float maxy, float maxz, const unsigned int* fcol); - -void duDebugDrawCylinder(struct duDebugDraw* dd, float minx, float miny, float minz, - float maxx, float maxy, float maxz, unsigned int col); - -void duDebugDrawGridXZ(struct duDebugDraw* dd, const float ox, const float oy, const float oz, - const int w, const int h, const float size, - const unsigned int col, const float lineWidth); - - -// Versions without begin/end, can be used to draw multiple primitives. -void duAppendCylinderWire(struct duDebugDraw* dd, float minx, float miny, float minz, - float maxx, float maxy, float maxz, unsigned int col); - -void duAppendBoxWire(struct duDebugDraw* dd, float minx, float miny, float minz, - float maxx, float maxy, float maxz, unsigned int col); - -void duAppendBoxPoints(struct duDebugDraw* dd, float minx, float miny, float minz, - float maxx, float maxy, float maxz, unsigned int col); - -void duAppendArc(struct duDebugDraw* dd, const float x0, const float y0, const float z0, - const float x1, const float y1, const float z1, const float h, - const float as0, const float as1, unsigned int col); - -void duAppendArrow(struct duDebugDraw* dd, const float x0, const float y0, const float z0, - const float x1, const float y1, const float z1, - const float as0, const float as1, unsigned int col); - -void duAppendCircle(struct duDebugDraw* dd, const float x, const float y, const float z, - const float r, unsigned int col); - -void duAppendCross(struct duDebugDraw* dd, const float x, const float y, const float z, - const float size, unsigned int col); - -void duAppendBox(struct duDebugDraw* dd, float minx, float miny, float minz, - float maxx, float maxy, float maxz, const unsigned int* fcol); - -void duAppendCylinder(struct duDebugDraw* dd, float minx, float miny, float minz, - float maxx, float maxy, float maxz, unsigned int col); - - -class duDisplayList : public duDebugDraw -{ - float* m_pos; - unsigned int* m_color; - int m_size; - int m_cap; - - bool m_depthMask; - duDebugDrawPrimitives m_prim; - float m_primSize; - - void resize(int cap); - -public: - duDisplayList(int cap = 512); - ~duDisplayList(); - virtual void depthMask(bool state); - virtual void begin(duDebugDrawPrimitives prim, float size = 1.0f); - virtual void vertex(const float x, const float y, const float z, unsigned int color); - virtual void vertex(const float* pos, unsigned int color); - virtual void end(); - void clear(); - void draw(struct duDebugDraw* dd); -private: - // Explicitly disabled copy constructor and copy assignment operator. - duDisplayList(const duDisplayList&); - duDisplayList& operator=(const duDisplayList&); -}; - - -#endif // DEBUGDRAW_H diff --git a/external/recast/include/DetourDebugDraw.h b/external/recast/include/DetourDebugDraw.h deleted file mode 100644 index ff2ca2f9d1..0000000000 --- a/external/recast/include/DetourDebugDraw.h +++ /dev/null @@ -1,48 +0,0 @@ -// -// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// 3. This notice may not be removed or altered from any source distribution. -// - -#ifndef DETOURDEBUGDRAW_H -#define DETOURDEBUGDRAW_H - -#include "DetourNavMesh.h" -#include "DetourNavMeshQuery.h" -#include "DetourTileCacheBuilder.h" - -enum DrawNavMeshFlags -{ - DU_DRAWNAVMESH_OFFMESHCONS = 0x01, - DU_DRAWNAVMESH_CLOSEDLIST = 0x02, - DU_DRAWNAVMESH_COLOR_TILES = 0x04, -}; - -void duDebugDrawNavMesh(struct duDebugDraw* dd, const dtNavMesh& mesh, unsigned char flags); -void duDebugDrawNavMeshWithClosedList(struct duDebugDraw* dd, const dtNavMesh& mesh, const dtNavMeshQuery& query, unsigned char flags); -void duDebugDrawNavMeshNodes(struct duDebugDraw* dd, const dtNavMeshQuery& query); -void duDebugDrawNavMeshBVTree(struct duDebugDraw* dd, const dtNavMesh& mesh); -void duDebugDrawNavMeshPortals(struct duDebugDraw* dd, const dtNavMesh& mesh); -void duDebugDrawNavMeshPolysWithFlags(struct duDebugDraw* dd, const dtNavMesh& mesh, const unsigned short polyFlags, const unsigned int col); -void duDebugDrawNavMeshPoly(struct duDebugDraw* dd, const dtNavMesh& mesh, dtPolyRef ref, const unsigned int col); - -void duDebugDrawTileCacheLayerAreas(struct duDebugDraw* dd, const dtTileCacheLayer& layer, const float cs, const float ch); -void duDebugDrawTileCacheLayerRegions(struct duDebugDraw* dd, const dtTileCacheLayer& layer, const float cs, const float ch); -void duDebugDrawTileCacheContours(duDebugDraw* dd, const struct dtTileCacheContourSet& lcset, - const float* orig, const float cs, const float ch); -void duDebugDrawTileCachePolyMesh(duDebugDraw* dd, const struct dtTileCachePolyMesh& lmesh, - const float* orig, const float cs, const float ch); - -#endif // DETOURDEBUGDRAW_H diff --git a/external/recast/include/RecastDebugDraw.h b/external/recast/include/RecastDebugDraw.h deleted file mode 100644 index 6a55fa6472..0000000000 --- a/external/recast/include/RecastDebugDraw.h +++ /dev/null @@ -1,42 +0,0 @@ -// -// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// 3. This notice may not be removed or altered from any source distribution. -// - -#ifndef RECAST_DEBUGDRAW_H -#define RECAST_DEBUGDRAW_H - -void duDebugDrawTriMesh(struct duDebugDraw* dd, const float* verts, int nverts, const int* tris, const float* normals, int ntris, const unsigned char* flags, const float texScale); -void duDebugDrawTriMeshSlope(struct duDebugDraw* dd, const float* verts, int nverts, const int* tris, const float* normals, int ntris, const float walkableSlopeAngle, const float texScale); - -void duDebugDrawHeightfieldSolid(struct duDebugDraw* dd, const struct rcHeightfield& hf); -void duDebugDrawHeightfieldWalkable(struct duDebugDraw* dd, const struct rcHeightfield& hf); - -void duDebugDrawCompactHeightfieldSolid(struct duDebugDraw* dd, const struct rcCompactHeightfield& chf); -void duDebugDrawCompactHeightfieldRegions(struct duDebugDraw* dd, const struct rcCompactHeightfield& chf); -void duDebugDrawCompactHeightfieldDistance(struct duDebugDraw* dd, const struct rcCompactHeightfield& chf); - -void duDebugDrawHeightfieldLayer(duDebugDraw* dd, const struct rcHeightfieldLayer& layer, const int idx); -void duDebugDrawHeightfieldLayers(duDebugDraw* dd, const struct rcHeightfieldLayerSet& lset); -void duDebugDrawHeightfieldLayersRegions(duDebugDraw* dd, const struct rcHeightfieldLayerSet& lset); - -void duDebugDrawRegionConnections(struct duDebugDraw* dd, const struct rcContourSet& cset, const float alpha = 1.0f); -void duDebugDrawRawContours(struct duDebugDraw* dd, const struct rcContourSet& cset, const float alpha = 1.0f); -void duDebugDrawContours(struct duDebugDraw* dd, const struct rcContourSet& cset, const float alpha = 1.0f); -void duDebugDrawPolyMesh(struct duDebugDraw* dd, const struct rcPolyMesh& mesh); -void duDebugDrawPolyMeshDetail(struct duDebugDraw* dd, const struct rcPolyMeshDetail& dmesh); - -#endif // RECAST_DEBUGDRAW_H diff --git a/external/recast/lib/win64_vs2017/release/recast.lib b/external/recast/lib/win64_vs2017/release/recast.lib deleted file mode 100644 index b5413a835070fc36ebf58342f609c339b8908a18..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1445884 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

al=`2=hd6`}2IVzf)*I_zuMlg)PO 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�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 z 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

=ga@K@1 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

j3fbk7#@~IoGt#}LPXVAM;yV*O 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

kJWdNd}885>k{X${JQ!_i@WsIoH@Tn-Cw^ql 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 Qfq_8?fMc;nudL4h4}U6nQvd(} diff --git a/external/recast/lib/win64_vs2017/release/recast.pdb b/external/recast/lib/win64_vs2017/release/recast.pdb deleted file mode 100644 index 39c937915e330844eca362549efcc5bc90548f6c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 200704 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�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 diff --git a/external/recast/include/RecastDump.h b/external/recast/src/DetourAlloc.cpp similarity index 53% rename from external/recast/include/RecastDump.h rename to external/recast/src/DetourAlloc.cpp index 6a722fdaea..d9ad1fc013 100644 --- a/external/recast/include/RecastDump.h +++ b/external/recast/src/DetourAlloc.cpp @@ -16,28 +16,35 @@ // 3. This notice may not be removed or altered from any source distribution. // -#ifndef RECAST_DUMP_H -#define RECAST_DUMP_H +#include +#include "DetourAlloc.h" -struct duFileIO +static void *dtAllocDefault(size_t size, dtAllocHint) { - virtual ~duFileIO() = 0; - virtual bool isWriting() const = 0; - virtual bool isReading() const = 0; - virtual bool write(const void* ptr, const size_t size) = 0; - virtual bool read(void* ptr, const size_t size) = 0; -}; + return malloc(size); +} -bool duDumpPolyMeshToObj(struct rcPolyMesh& pmesh, duFileIO* io); -bool duDumpPolyMeshDetailToObj(struct rcPolyMeshDetail& dmesh, duFileIO* io); - -bool duDumpContourSet(struct rcContourSet& cset, duFileIO* io); -bool duReadContourSet(struct rcContourSet& cset, duFileIO* io); +static void dtFreeDefault(void *ptr) +{ + free(ptr); +} -bool duDumpCompactHeightfield(struct rcCompactHeightfield& chf, duFileIO* io); -bool duReadCompactHeightfield(struct rcCompactHeightfield& chf, duFileIO* io); +static dtAllocFunc* sAllocFunc = dtAllocDefault; +static dtFreeFunc* sFreeFunc = dtFreeDefault; -void duLogBuildTimes(rcContext& ctx, const int totalTileUsec); +void dtAllocSetCustom(dtAllocFunc *allocFunc, dtFreeFunc *freeFunc) +{ + sAllocFunc = allocFunc ? allocFunc : dtAllocDefault; + sFreeFunc = freeFunc ? freeFunc : dtFreeDefault; +} +void* dtAlloc(size_t size, dtAllocHint hint) +{ + return sAllocFunc(size, hint); +} -#endif // RECAST_DUMP_H +void dtFree(void* ptr) +{ + if (ptr) + sFreeFunc(ptr); +} diff --git a/external/recast/src/DetourAssert.cpp b/external/recast/src/DetourAssert.cpp new file mode 100644 index 0000000000..5e019e0cfc --- /dev/null +++ b/external/recast/src/DetourAssert.cpp @@ -0,0 +1,35 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#include "DetourAssert.h" + +#ifndef NDEBUG + +static dtAssertFailFunc* sAssertFailFunc = 0; + +void dtAssertFailSetCustom(dtAssertFailFunc *assertFailFunc) +{ + sAssertFailFunc = assertFailFunc; +} + +dtAssertFailFunc* dtAssertFailGetCustom() +{ + return sAssertFailFunc; +} + +#endif diff --git a/external/recast/src/DetourCommon.cpp b/external/recast/src/DetourCommon.cpp new file mode 100644 index 0000000000..b89d7512c4 --- /dev/null +++ b/external/recast/src/DetourCommon.cpp @@ -0,0 +1,387 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#include "DetourCommon.h" +#include "DetourMath.h" + +////////////////////////////////////////////////////////////////////////////////////////// + +void dtClosestPtPointTriangle(float* closest, const float* p, + const float* a, const float* b, const float* c) +{ + // Check if P in vertex region outside A + float ab[3], ac[3], ap[3]; + dtVsub(ab, b, a); + dtVsub(ac, c, a); + dtVsub(ap, p, a); + float d1 = dtVdot(ab, ap); + float d2 = dtVdot(ac, ap); + if (d1 <= 0.0f && d2 <= 0.0f) + { + // barycentric coordinates (1,0,0) + dtVcopy(closest, a); + return; + } + + // Check if P in vertex region outside B + float bp[3]; + dtVsub(bp, p, b); + float d3 = dtVdot(ab, bp); + float d4 = dtVdot(ac, bp); + if (d3 >= 0.0f && d4 <= d3) + { + // barycentric coordinates (0,1,0) + dtVcopy(closest, b); + return; + } + + // Check if P in edge region of AB, if so return projection of P onto AB + float vc = d1*d4 - d3*d2; + if (vc <= 0.0f && d1 >= 0.0f && d3 <= 0.0f) + { + // barycentric coordinates (1-v,v,0) + float v = d1 / (d1 - d3); + closest[0] = a[0] + v * ab[0]; + closest[1] = a[1] + v * ab[1]; + closest[2] = a[2] + v * ab[2]; + return; + } + + // Check if P in vertex region outside C + float cp[3]; + dtVsub(cp, p, c); + float d5 = dtVdot(ab, cp); + float d6 = dtVdot(ac, cp); + if (d6 >= 0.0f && d5 <= d6) + { + // barycentric coordinates (0,0,1) + dtVcopy(closest, c); + return; + } + + // Check if P in edge region of AC, if so return projection of P onto AC + float vb = d5*d2 - d1*d6; + if (vb <= 0.0f && d2 >= 0.0f && d6 <= 0.0f) + { + // barycentric coordinates (1-w,0,w) + float w = d2 / (d2 - d6); + closest[0] = a[0] + w * ac[0]; + closest[1] = a[1] + w * ac[1]; + closest[2] = a[2] + w * ac[2]; + return; + } + + // Check if P in edge region of BC, if so return projection of P onto BC + float va = d3*d6 - d5*d4; + if (va <= 0.0f && (d4 - d3) >= 0.0f && (d5 - d6) >= 0.0f) + { + // barycentric coordinates (0,1-w,w) + float w = (d4 - d3) / ((d4 - d3) + (d5 - d6)); + closest[0] = b[0] + w * (c[0] - b[0]); + closest[1] = b[1] + w * (c[1] - b[1]); + closest[2] = b[2] + w * (c[2] - b[2]); + return; + } + + // P inside face region. Compute Q through its barycentric coordinates (u,v,w) + float denom = 1.0f / (va + vb + vc); + float v = vb * denom; + float w = vc * denom; + closest[0] = a[0] + ab[0] * v + ac[0] * w; + closest[1] = a[1] + ab[1] * v + ac[1] * w; + closest[2] = a[2] + ab[2] * v + ac[2] * w; +} + +bool dtIntersectSegmentPoly2D(const float* p0, const float* p1, + const float* verts, int nverts, + float& tmin, float& tmax, + int& segMin, int& segMax) +{ + static const float EPS = 0.00000001f; + + tmin = 0; + tmax = 1; + segMin = -1; + segMax = -1; + + float dir[3]; + dtVsub(dir, p1, p0); + + for (int i = 0, j = nverts-1; i < nverts; j=i++) + { + float edge[3], diff[3]; + dtVsub(edge, &verts[i*3], &verts[j*3]); + dtVsub(diff, p0, &verts[j*3]); + const float n = dtVperp2D(edge, diff); + const float d = dtVperp2D(dir, edge); + if (fabsf(d) < EPS) + { + // S is nearly parallel to this edge + if (n < 0) + return false; + else + continue; + } + const float t = n / d; + if (d < 0) + { + // segment S is entering across this edge + if (t > tmin) + { + tmin = t; + segMin = j; + // S enters after leaving polygon + if (tmin > tmax) + return false; + } + } + else + { + // segment S is leaving across this edge + if (t < tmax) + { + tmax = t; + segMax = j; + // S leaves before entering polygon + if (tmax < tmin) + return false; + } + } + } + + return true; +} + +float dtDistancePtSegSqr2D(const float* pt, const float* p, const float* q, float& t) +{ + float pqx = q[0] - p[0]; + float pqz = q[2] - p[2]; + float dx = pt[0] - p[0]; + float dz = pt[2] - p[2]; + float d = pqx*pqx + pqz*pqz; + t = pqx*dx + pqz*dz; + if (d > 0) t /= d; + if (t < 0) t = 0; + else if (t > 1) t = 1; + dx = p[0] + t*pqx - pt[0]; + dz = p[2] + t*pqz - pt[2]; + return dx*dx + dz*dz; +} + +void dtCalcPolyCenter(float* tc, const unsigned short* idx, int nidx, const float* verts) +{ + tc[0] = 0.0f; + tc[1] = 0.0f; + tc[2] = 0.0f; + for (int j = 0; j < nidx; ++j) + { + const float* v = &verts[idx[j]*3]; + tc[0] += v[0]; + tc[1] += v[1]; + tc[2] += v[2]; + } + const float s = 1.0f / nidx; + tc[0] *= s; + tc[1] *= s; + tc[2] *= s; +} + +bool dtClosestHeightPointTriangle(const float* p, const float* a, const float* b, const float* c, float& h) +{ + const float EPS = 1e-6f; + float v0[3], v1[3], v2[3]; + + dtVsub(v0, c, a); + dtVsub(v1, b, a); + dtVsub(v2, p, a); + + // Compute scaled barycentric coordinates + float denom = v0[0] * v1[2] - v0[2] * v1[0]; + if (fabsf(denom) < EPS) + return false; + + float u = v1[2] * v2[0] - v1[0] * v2[2]; + float v = v0[0] * v2[2] - v0[2] * v2[0]; + + if (denom < 0) { + denom = -denom; + u = -u; + v = -v; + } + + // If point lies inside the triangle, return interpolated ycoord. + if (u >= 0.0f && v >= 0.0f && (u + v) <= denom) { + h = a[1] + (v0[1] * u + v1[1] * v) / denom; + return true; + } + return false; +} + +/// @par +/// +/// All points are projected onto the xz-plane, so the y-values are ignored. +bool dtPointInPolygon(const float* pt, const float* verts, const int nverts) +{ + // TODO: Replace pnpoly with triArea2D tests? + int i, j; + bool c = false; + for (i = 0, j = nverts-1; i < nverts; j = i++) + { + const float* vi = &verts[i*3]; + const float* vj = &verts[j*3]; + if (((vi[2] > pt[2]) != (vj[2] > pt[2])) && + (pt[0] < (vj[0]-vi[0]) * (pt[2]-vi[2]) / (vj[2]-vi[2]) + vi[0]) ) + c = !c; + } + return c; +} + +bool dtDistancePtPolyEdgesSqr(const float* pt, const float* verts, const int nverts, + float* ed, float* et) +{ + // TODO: Replace pnpoly with triArea2D tests? + int i, j; + bool c = false; + for (i = 0, j = nverts-1; i < nverts; j = i++) + { + const float* vi = &verts[i*3]; + const float* vj = &verts[j*3]; + if (((vi[2] > pt[2]) != (vj[2] > pt[2])) && + (pt[0] < (vj[0]-vi[0]) * (pt[2]-vi[2]) / (vj[2]-vi[2]) + vi[0]) ) + c = !c; + ed[j] = dtDistancePtSegSqr2D(pt, vj, vi, et[j]); + } + return c; +} + +static void projectPoly(const float* axis, const float* poly, const int npoly, + float& rmin, float& rmax) +{ + rmin = rmax = dtVdot2D(axis, &poly[0]); + for (int i = 1; i < npoly; ++i) + { + const float d = dtVdot2D(axis, &poly[i*3]); + rmin = dtMin(rmin, d); + rmax = dtMax(rmax, d); + } +} + +inline bool overlapRange(const float amin, const float amax, + const float bmin, const float bmax, + const float eps) +{ + return ((amin+eps) > bmax || (amax-eps) < bmin) ? false : true; +} + +/// @par +/// +/// All vertices are projected onto the xz-plane, so the y-values are ignored. +bool dtOverlapPolyPoly2D(const float* polya, const int npolya, + const float* polyb, const int npolyb) +{ + const float eps = 1e-4f; + + for (int i = 0, j = npolya-1; i < npolya; j=i++) + { + const float* va = &polya[j*3]; + const float* vb = &polya[i*3]; + const float n[3] = { vb[2]-va[2], 0, -(vb[0]-va[0]) }; + float amin,amax,bmin,bmax; + projectPoly(n, polya, npolya, amin,amax); + projectPoly(n, polyb, npolyb, bmin,bmax); + if (!overlapRange(amin,amax, bmin,bmax, eps)) + { + // Found separating axis + return false; + } + } + for (int i = 0, j = npolyb-1; i < npolyb; j=i++) + { + const float* va = &polyb[j*3]; + const float* vb = &polyb[i*3]; + const float n[3] = { vb[2]-va[2], 0, -(vb[0]-va[0]) }; + float amin,amax,bmin,bmax; + projectPoly(n, polya, npolya, amin,amax); + projectPoly(n, polyb, npolyb, bmin,bmax); + if (!overlapRange(amin,amax, bmin,bmax, eps)) + { + // Found separating axis + return false; + } + } + return true; +} + +// Returns a random point in a convex polygon. +// Adapted from Graphics Gems article. +void dtRandomPointInConvexPoly(const float* pts, const int npts, float* areas, + const float s, const float t, float* out) +{ + // Calc triangle araes + float areasum = 0.0f; + for (int i = 2; i < npts; i++) { + areas[i] = dtTriArea2D(&pts[0], &pts[(i-1)*3], &pts[i*3]); + areasum += dtMax(0.001f, areas[i]); + } + // Find sub triangle weighted by area. + const float thr = s*areasum; + float acc = 0.0f; + 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)) + { + u = (thr - acc) / dacc; + tri = i; + break; + } + acc += dacc; + } + + float v = dtMathSqrtf(t); + + const float a = 1 - v; + const float b = (1 - u) * v; + const float c = u * v; + const float* pa = &pts[0]; + const float* pb = &pts[(tri-1)*3]; + const float* pc = &pts[tri*3]; + + out[0] = a*pa[0] + b*pb[0] + c*pc[0]; + out[1] = a*pa[1] + b*pb[1] + c*pc[1]; + out[2] = a*pa[2] + b*pb[2] + c*pc[2]; +} + +inline float vperpXZ(const float* a, const float* b) { return a[0]*b[2] - a[2]*b[0]; } + +bool dtIntersectSegSeg2D(const float* ap, const float* aq, + const float* bp, const float* bq, + float& s, float& t) +{ + float u[3], v[3], w[3]; + dtVsub(u,aq,ap); + dtVsub(v,bq,bp); + dtVsub(w,ap,bp); + float d = vperpXZ(u,v); + if (fabsf(d) < 1e-6f) return false; + s = vperpXZ(v,w) / d; + t = vperpXZ(u,w) / d; + return true; +} + diff --git a/external/recast/src/DetourCrowd.cpp b/external/recast/src/DetourCrowd.cpp index 4d2e79f764..25725ba7f3 100644 --- a/external/recast/src/DetourCrowd.cpp +++ b/external/recast/src/DetourCrowd.cpp @@ -1403,6 +1403,7 @@ m_velocitySampleCount += ns; void dtCrowd::doMove(float dt) { int nagents = m_numActiveAgents; + dtCrowdAgent** agents = m_activeAgents; for (int i = 0; i < nagents; ++i) { dtCrowdAgent* ag = m_activeAgents[i]; diff --git a/external/recast/src/DetourNavMesh.cpp b/external/recast/src/DetourNavMesh.cpp new file mode 100644 index 0000000000..a655d1d89a --- /dev/null +++ b/external/recast/src/DetourNavMesh.cpp @@ -0,0 +1,1584 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#include +#include +#include +#include "DetourNavMesh.h" +#include "DetourNode.h" +#include "DetourCommon.h" +#include "DetourMath.h" +#include "DetourAlloc.h" +#include "DetourAssert.h" +#include + + +inline bool overlapSlabs(const float* amin, const float* amax, + const float* bmin, const float* bmax, + const float px, const float py) +{ + // Check for horizontal overlap. + // The segment is shrunken a little so that slabs which touch + // at end points are not connected. + const float minx = dtMax(amin[0]+px,bmin[0]+px); + const float maxx = dtMin(amax[0]-px,bmax[0]-px); + if (minx > maxx) + return false; + + // Check vertical overlap. + const float ad = (amax[1]-amin[1]) / (amax[0]-amin[0]); + const float ak = amin[1] - ad*amin[0]; + const float bd = (bmax[1]-bmin[1]) / (bmax[0]-bmin[0]); + const float bk = bmin[1] - bd*bmin[0]; + const float aminy = ad*minx + ak; + const float amaxy = ad*maxx + ak; + const float bminy = bd*minx + bk; + const float bmaxy = bd*maxx + bk; + const float dmin = bminy - aminy; + const float dmax = bmaxy - amaxy; + + // Crossing segments always overlap. + if (dmin*dmax < 0) + return true; + + // Check for overlap at endpoints. + const float thr = dtSqr(py*2); + if (dmin*dmin <= thr || dmax*dmax <= thr) + return true; + + return false; +} + +static float getSlabCoord(const float* va, const int side) +{ + if (side == 0 || side == 4) + return va[0]; + else if (side == 2 || side == 6) + return va[2]; + return 0; +} + +static void calcSlabEndPoints(const float* va, const float* vb, float* bmin, float* bmax, const int side) +{ + if (side == 0 || side == 4) + { + if (va[2] < vb[2]) + { + bmin[0] = va[2]; + bmin[1] = va[1]; + bmax[0] = vb[2]; + bmax[1] = vb[1]; + } + else + { + bmin[0] = vb[2]; + bmin[1] = vb[1]; + bmax[0] = va[2]; + bmax[1] = va[1]; + } + } + else if (side == 2 || side == 6) + { + if (va[0] < vb[0]) + { + bmin[0] = va[0]; + bmin[1] = va[1]; + bmax[0] = vb[0]; + bmax[1] = vb[1]; + } + else + { + bmin[0] = vb[0]; + bmin[1] = vb[1]; + bmax[0] = va[0]; + bmax[1] = va[1]; + } + } +} + +inline int computeTileHash(int x, int y, const int mask) +{ + const unsigned int h1 = 0x8da6b343; // Large multiplicative constants; + const unsigned int h2 = 0xd8163841; // here arbitrarily chosen primes + unsigned int n = h1 * x + h2 * y; + return (int)(n & mask); +} + +inline unsigned int allocLink(dtMeshTile* tile) +{ + if (tile->linksFreeList == DT_NULL_LINK) + return DT_NULL_LINK; + unsigned int link = tile->linksFreeList; + tile->linksFreeList = tile->links[link].next; + return link; +} + +inline void freeLink(dtMeshTile* tile, unsigned int link) +{ + tile->links[link].next = tile->linksFreeList; + tile->linksFreeList = link; +} + + +dtNavMesh* dtAllocNavMesh() +{ + void* mem = dtAlloc(sizeof(dtNavMesh), DT_ALLOC_PERM); + if (!mem) return 0; + return new(mem) dtNavMesh; +} + +/// @par +/// +/// This function will only free the memory for tiles with the #DT_TILE_FREE_DATA +/// flag set. +void dtFreeNavMesh(dtNavMesh* navmesh) +{ + if (!navmesh) return; + navmesh->~dtNavMesh(); + dtFree(navmesh); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +/** +@class dtNavMesh + +The navigation mesh consists of one or more tiles defining three primary types of structural data: + +A polygon mesh which defines most of the navigation graph. (See rcPolyMesh for its structure.) +A detail mesh used for determining surface height on the polygon mesh. (See rcPolyMeshDetail for its structure.) +Off-mesh connections, which define custom point-to-point edges within the navigation graph. + +The general build process is as follows: + +-# Create rcPolyMesh and rcPolyMeshDetail data using the Recast build pipeline. +-# Optionally, create off-mesh connection data. +-# Combine the source data into a dtNavMeshCreateParams structure. +-# Create a tile data array using dtCreateNavMeshData(). +-# Allocate at dtNavMesh object and initialize it. (For single tile navigation meshes, + the tile data is loaded during this step.) +-# For multi-tile navigation meshes, load the tile data using dtNavMesh::addTile(). + +Notes: + +- This class is usually used in conjunction with the dtNavMeshQuery class for pathfinding. +- Technically, all navigation meshes are tiled. A 'solo' mesh is simply a navigation mesh initialized + to have only a single tile. +- This class does not implement any asynchronous methods. So the ::dtStatus result of all methods will + always contain either a success or failure flag. + +@see dtNavMeshQuery, dtCreateNavMeshData, dtNavMeshCreateParams, #dtAllocNavMesh, #dtFreeNavMesh +*/ + +dtNavMesh::dtNavMesh() : + m_tileWidth(0), + m_tileHeight(0), + m_maxTiles(0), + m_tileLutSize(0), + m_tileLutMask(0), + m_posLookup(0), + m_nextFree(0), + m_tiles(0) +{ +#ifndef DT_POLYREF64 + m_saltBits = 0; + m_tileBits = 0; + m_polyBits = 0; +#endif + memset(&m_params, 0, sizeof(dtNavMeshParams)); + m_orig[0] = 0; + m_orig[1] = 0; + m_orig[2] = 0; +} + +dtNavMesh::~dtNavMesh() +{ + for (int i = 0; i < m_maxTiles; ++i) + { + if (m_tiles[i].flags & DT_TILE_FREE_DATA) + { + dtFree(m_tiles[i].data); + m_tiles[i].data = 0; + m_tiles[i].dataSize = 0; + } + } + dtFree(m_posLookup); + dtFree(m_tiles); +} + +dtStatus dtNavMesh::init(const dtNavMeshParams* params) +{ + memcpy(&m_params, params, sizeof(dtNavMeshParams)); + dtVcopy(m_orig, params->orig); + m_tileWidth = params->tileWidth; + m_tileHeight = params->tileHeight; + + // Init tiles + m_maxTiles = params->maxTiles; + m_tileLutSize = dtNextPow2(params->maxTiles/4); + if (!m_tileLutSize) m_tileLutSize = 1; + m_tileLutMask = m_tileLutSize-1; + + m_tiles = (dtMeshTile*)dtAlloc(sizeof(dtMeshTile)*m_maxTiles, DT_ALLOC_PERM); + if (!m_tiles) + return DT_FAILURE | DT_OUT_OF_MEMORY; + m_posLookup = (dtMeshTile**)dtAlloc(sizeof(dtMeshTile*)*m_tileLutSize, DT_ALLOC_PERM); + if (!m_posLookup) + return DT_FAILURE | DT_OUT_OF_MEMORY; + memset(m_tiles, 0, sizeof(dtMeshTile)*m_maxTiles); + memset(m_posLookup, 0, sizeof(dtMeshTile*)*m_tileLutSize); + m_nextFree = 0; + for (int i = m_maxTiles-1; i >= 0; --i) + { + m_tiles[i].salt = 1; + m_tiles[i].next = m_nextFree; + m_nextFree = &m_tiles[i]; + } + + // Init ID generator values. +#ifndef DT_POLYREF64 + m_tileBits = dtIlog2(dtNextPow2((unsigned int)params->maxTiles)); + m_polyBits = dtIlog2(dtNextPow2((unsigned int)params->maxPolys)); + // Only allow 31 salt bits, since the salt mask is calculated using 32bit uint and it will overflow. + m_saltBits = dtMin((unsigned int)31, 32 - m_tileBits - m_polyBits); + + if (m_saltBits < 10) + return DT_FAILURE | DT_INVALID_PARAM; +#endif + + return DT_SUCCESS; +} + +dtStatus dtNavMesh::init(unsigned char* data, const int dataSize, const int flags) +{ + // Make sure the data is in right format. + dtMeshHeader* header = (dtMeshHeader*)data; + if (header->magic != DT_NAVMESH_MAGIC) + return DT_FAILURE | DT_WRONG_MAGIC; + if (header->version != DT_NAVMESH_VERSION) + return DT_FAILURE | DT_WRONG_VERSION; + + dtNavMeshParams params; + dtVcopy(params.orig, header->bmin); + params.tileWidth = header->bmax[0] - header->bmin[0]; + params.tileHeight = header->bmax[2] - header->bmin[2]; + params.maxTiles = 1; + params.maxPolys = header->polyCount; + + dtStatus status = init(¶ms); + if (dtStatusFailed(status)) + return status; + + return addTile(data, dataSize, flags, 0, 0); +} + +/// @par +/// +/// @note The parameters are created automatically when the single tile +/// initialization is performed. +const dtNavMeshParams* dtNavMesh::getParams() const +{ + return &m_params; +} + +////////////////////////////////////////////////////////////////////////////////////////// +int dtNavMesh::findConnectingPolys(const float* va, const float* vb, + const dtMeshTile* tile, int side, + dtPolyRef* con, float* conarea, int maxcon) const +{ + if (!tile) return 0; + + float amin[2], amax[2]; + calcSlabEndPoints(va, vb, amin, amax, side); + const float apos = getSlabCoord(va, side); + + // Remove links pointing to 'side' and compact the links array. + float bmin[2], bmax[2]; + unsigned short m = DT_EXT_LINK | (unsigned short)side; + int n = 0; + + dtPolyRef base = getPolyRefBase(tile); + + for (int i = 0; i < tile->header->polyCount; ++i) + { + dtPoly* poly = &tile->polys[i]; + const int nv = poly->vertCount; + for (int j = 0; j < nv; ++j) + { + // Skip edges which do not point to the right side. + if (poly->neis[j] != m) continue; + + const float* vc = &tile->verts[poly->verts[j]*3]; + const float* vd = &tile->verts[poly->verts[(j+1) % nv]*3]; + const float bpos = getSlabCoord(vc, side); + + // Segments are not close enough. + if (dtAbs(apos-bpos) > 0.01f) + continue; + + // Check if the segments touch. + calcSlabEndPoints(vc,vd, bmin,bmax, side); + + if (!overlapSlabs(amin,amax, bmin,bmax, 0.01f, tile->header->walkableClimb)) continue; + + // Add return value. + if (n < maxcon) + { + conarea[n*2+0] = dtMax(amin[0], bmin[0]); + conarea[n*2+1] = dtMin(amax[0], bmax[0]); + con[n] = base | (dtPolyRef)i; + n++; + } + break; + } + } + return n; +} + +void dtNavMesh::unconnectLinks(dtMeshTile* tile, dtMeshTile* target) +{ + if (!tile || !target) return; + + const unsigned int targetNum = decodePolyIdTile(getTileRef(target)); + + for (int i = 0; i < tile->header->polyCount; ++i) + { + dtPoly* poly = &tile->polys[i]; + unsigned int j = poly->firstLink; + unsigned int pj = DT_NULL_LINK; + while (j != DT_NULL_LINK) + { + if (decodePolyIdTile(tile->links[j].ref) == targetNum) + { + // Remove link. + unsigned int nj = tile->links[j].next; + if (pj == DT_NULL_LINK) + poly->firstLink = nj; + else + tile->links[pj].next = nj; + freeLink(tile, j); + j = nj; + } + else + { + // Advance + pj = j; + j = tile->links[j].next; + } + } + } +} + +void dtNavMesh::connectExtLinks(dtMeshTile* tile, dtMeshTile* target, int side) +{ + if (!tile) return; + + // Connect border links. + for (int i = 0; i < tile->header->polyCount; ++i) + { + dtPoly* poly = &tile->polys[i]; + + // Create new links. +// unsigned short m = DT_EXT_LINK | (unsigned short)side; + + const int nv = poly->vertCount; + for (int j = 0; j < nv; ++j) + { + // Skip non-portal edges. + if ((poly->neis[j] & DT_EXT_LINK) == 0) + continue; + + const int dir = (int)(poly->neis[j] & 0xff); + if (side != -1 && dir != side) + continue; + + // Create new links + const float* va = &tile->verts[poly->verts[j]*3]; + const float* vb = &tile->verts[poly->verts[(j+1) % nv]*3]; + dtPolyRef nei[4]; + float neia[4*2]; + int nnei = findConnectingPolys(va,vb, target, dtOppositeTile(dir), nei,neia,4); + for (int k = 0; k < nnei; ++k) + { + unsigned int idx = allocLink(tile); + if (idx != DT_NULL_LINK) + { + dtLink* link = &tile->links[idx]; + link->ref = nei[k]; + link->edge = (unsigned char)j; + link->side = (unsigned char)dir; + + link->next = poly->firstLink; + poly->firstLink = idx; + + // Compress portal limits to a byte value. + if (dir == 0 || dir == 4) + { + float tmin = (neia[k*2+0]-va[2]) / (vb[2]-va[2]); + float tmax = (neia[k*2+1]-va[2]) / (vb[2]-va[2]); + if (tmin > tmax) + dtSwap(tmin,tmax); + link->bmin = (unsigned char)(dtClamp(tmin, 0.0f, 1.0f)*255.0f); + link->bmax = (unsigned char)(dtClamp(tmax, 0.0f, 1.0f)*255.0f); + } + else if (dir == 2 || dir == 6) + { + float tmin = (neia[k*2+0]-va[0]) / (vb[0]-va[0]); + float tmax = (neia[k*2+1]-va[0]) / (vb[0]-va[0]); + if (tmin > tmax) + dtSwap(tmin,tmax); + link->bmin = (unsigned char)(dtClamp(tmin, 0.0f, 1.0f)*255.0f); + link->bmax = (unsigned char)(dtClamp(tmax, 0.0f, 1.0f)*255.0f); + } + } + } + } + } +} + +void dtNavMesh::connectExtOffMeshLinks(dtMeshTile* tile, dtMeshTile* target, int side) +{ + if (!tile) return; + + // Connect off-mesh links. + // We are interested on links which land from target tile to this tile. + const unsigned char oppositeSide = (side == -1) ? 0xff : (unsigned char)dtOppositeTile(side); + + for (int i = 0; i < target->header->offMeshConCount; ++i) + { + dtOffMeshConnection* targetCon = &target->offMeshCons[i]; + if (targetCon->side != oppositeSide) + continue; + + dtPoly* targetPoly = &target->polys[targetCon->poly]; + // Skip off-mesh connections which start location could not be connected at all. + if (targetPoly->firstLink == DT_NULL_LINK) + continue; + + 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, 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(targetCon->rad)) + continue; + // Make sure the location is on current mesh. + float* v = &target->verts[targetPoly->verts[1]*3]; + dtVcopy(v, nearestPt); + + // Link off-mesh connection to target poly. + unsigned int idx = allocLink(target); + if (idx != DT_NULL_LINK) + { + dtLink* link = &target->links[idx]; + link->ref = ref; + link->edge = (unsigned char)1; + link->side = oppositeSide; + link->bmin = link->bmax = 0; + // Add to linked list. + link->next = targetPoly->firstLink; + targetPoly->firstLink = idx; + } + + // Link target poly to off-mesh connection. + if (targetCon->flags & DT_OFFMESH_CON_BIDIR) + { + unsigned int tidx = allocLink(tile); + if (tidx != DT_NULL_LINK) + { + const unsigned short landPolyIdx = (unsigned short)decodePolyIdPoly(ref); + dtPoly* landPoly = &tile->polys[landPolyIdx]; + dtLink* link = &tile->links[tidx]; + link->ref = getPolyRefBase(target) | (dtPolyRef)(targetCon->poly); + link->edge = 0xff; + link->side = (unsigned char)(side == -1 ? 0xff : side); + link->bmin = link->bmax = 0; + // Add to linked list. + link->next = landPoly->firstLink; + landPoly->firstLink = tidx; + } + } + } + +} + +void dtNavMesh::connectIntLinks(dtMeshTile* tile) +{ + if (!tile) return; + + dtPolyRef base = getPolyRefBase(tile); + + for (int i = 0; i < tile->header->polyCount; ++i) + { + dtPoly* poly = &tile->polys[i]; + poly->firstLink = DT_NULL_LINK; + + if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) + continue; + + // Build edge links backwards so that the links will be + // in the linked list from lowest index to highest. + for (int j = poly->vertCount-1; j >= 0; --j) + { + // Skip hard and non-internal edges. + if (poly->neis[j] == 0 || (poly->neis[j] & DT_EXT_LINK)) continue; + + unsigned int idx = allocLink(tile); + if (idx != DT_NULL_LINK) + { + dtLink* link = &tile->links[idx]; + link->ref = base | (dtPolyRef)(poly->neis[j]-1); + link->edge = (unsigned char)j; + link->side = 0xff; + link->bmin = link->bmax = 0; + // Add to linked list. + link->next = poly->firstLink; + poly->firstLink = idx; + } + } + } +} + +void dtNavMesh::baseOffMeshLinks(dtMeshTile* tile) +{ + if (!tile) return; + + dtPolyRef base = getPolyRefBase(tile); + + // Base off-mesh connection start points. + for (int i = 0; i < tile->header->offMeshConCount; ++i) + { + dtOffMeshConnection* con = &tile->offMeshCons[i]; + dtPoly* poly = &tile->polys[con->poly]; + + 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, 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)) + continue; + // Make sure the location is on current mesh. + float* v = &tile->verts[poly->verts[0]*3]; + dtVcopy(v, nearestPt); + + // Link off-mesh connection to target poly. + unsigned int idx = allocLink(tile); + if (idx != DT_NULL_LINK) + { + dtLink* link = &tile->links[idx]; + link->ref = ref; + link->edge = (unsigned char)0; + link->side = 0xff; + link->bmin = link->bmax = 0; + // Add to linked list. + link->next = poly->firstLink; + poly->firstLink = idx; + } + + // Start end-point is always connect back to off-mesh connection. + unsigned int tidx = allocLink(tile); + if (tidx != DT_NULL_LINK) + { + const unsigned short landPolyIdx = (unsigned short)decodePolyIdPoly(ref); + dtPoly* landPoly = &tile->polys[landPolyIdx]; + dtLink* link = &tile->links[tidx]; + link->ref = base | (dtPolyRef)(con->poly); + link->edge = 0xff; + link->side = 0xff; + link->bmin = link->bmax = 0; + // Add to linked list. + link->next = landPoly->firstLink; + landPoly->firstLink = tidx; + } + } +} + +namespace +{ + template + void closestPointOnDetailEdges(const dtMeshTile* tile, const dtPoly* poly, const float* pos, float* closest) + { + const unsigned int ip = (unsigned int)(poly - tile->polys); + const dtPolyDetail* pd = &tile->detailMeshes[ip]; + + float dmin = FLT_MAX; + float tmin = 0; + const float* pmin = 0; + const float* pmax = 0; + + for (int i = 0; i < pd->triCount; i++) + { + const unsigned char* tris = &tile->detailTris[(pd->triBase + i) * 4]; + const int ANY_BOUNDARY_EDGE = + (DT_DETAIL_EDGE_BOUNDARY << 0) | + (DT_DETAIL_EDGE_BOUNDARY << 2) | + (DT_DETAIL_EDGE_BOUNDARY << 4); + if (onlyBoundary && (tris[3] & ANY_BOUNDARY_EDGE) == 0) + continue; + + const float* v[3]; + for (int j = 0; j < 3; ++j) + { + if (tris[j] < poly->vertCount) + v[j] = &tile->verts[poly->verts[tris[j]] * 3]; + else + v[j] = &tile->detailVerts[(pd->vertBase + (tris[j] - poly->vertCount)) * 3]; + } + + for (int k = 0, j = 2; k < 3; j = k++) + { + if ((dtGetDetailTriEdgeFlags(tris[3], j) & DT_DETAIL_EDGE_BOUNDARY) == 0 && + (onlyBoundary || tris[j] < tris[k])) + { + // Only looking at boundary edges and this is internal, or + // this is an inner edge that we will see again or have already seen. + continue; + } + + float t; + float d = dtDistancePtSegSqr2D(pos, v[j], v[k], t); + if (d < dmin) + { + dmin = d; + tmin = t; + pmin = v[j]; + pmax = v[k]; + } + } + } + + dtVlerp(closest, pmin, pmax, tmin); + } +} + +bool dtNavMesh::getPolyHeight(const dtMeshTile* tile, const dtPoly* poly, const float* pos, float* height) const +{ + // Off-mesh connections do not have detail polys and getting height + // over them does not make sense. + if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) + return false; + + const unsigned int ip = (unsigned int)(poly - tile->polys); + const dtPolyDetail* pd = &tile->detailMeshes[ip]; + + float verts[DT_VERTS_PER_POLYGON*3]; + const int nv = poly->vertCount; + for (int i = 0; i < nv; ++i) + dtVcopy(&verts[i*3], &tile->verts[poly->verts[i]*3]); + + if (!dtPointInPolygon(pos, verts, nv)) + return false; + + if (!height) + return true; + + // Find height at the location. + for (int j = 0; j < pd->triCount; ++j) + { + const unsigned char* t = &tile->detailTris[(pd->triBase+j)*4]; + const float* v[3]; + for (int k = 0; k < 3; ++k) + { + if (t[k] < poly->vertCount) + v[k] = &tile->verts[poly->verts[t[k]]*3]; + else + v[k] = &tile->detailVerts[(pd->vertBase+(t[k]-poly->vertCount))*3]; + } + float h; + if (dtClosestHeightPointTriangle(pos, v[0], v[1], v[2], h)) + { + *height = h; + return true; + } + } + + // If all triangle checks failed above (can happen with degenerate triangles + // or larger floating point values) the point is on an edge, so just select + // closest. This should almost never happen so the extra iteration here is + // ok. + float closest[3]; + closestPointOnDetailEdges(tile, poly, pos, closest); + *height = closest[1]; + return true; +} + +void dtNavMesh::closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const +{ + const dtMeshTile* tile = 0; + const dtPoly* poly = 0; + getTileAndPolyByRefUnsafe(ref, &tile, &poly); + + dtVcopy(closest, pos); + if (getPolyHeight(tile, poly, pos, &closest[1])) + { + if (posOverPoly) + *posOverPoly = true; + return; + } + + if (posOverPoly) + *posOverPoly = false; + + // Off-mesh connections don't have detail polygons. + if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) + { + const float* v0 = &tile->verts[poly->verts[0]*3]; + const float* v1 = &tile->verts[poly->verts[1]*3]; + float t; + dtDistancePtSegSqr2D(pos, v0, v1, t); + dtVlerp(closest, v0, v1, t); + return; + } + + // Outside poly that is not an offmesh connection. + closestPointOnDetailEdges(tile, poly, pos, closest); +} + +dtPolyRef dtNavMesh::findNearestPolyInTile(const dtMeshTile* tile, + const float* center, const float* halfExtents, + float* nearestPt) const +{ + float bmin[3], bmax[3]; + dtVsub(bmin, center, halfExtents); + dtVadd(bmax, center, halfExtents); + + // Get nearby polygons from proximity grid. + dtPolyRef polys[128]; + int polyCount = queryPolygonsInTile(tile, bmin, bmax, polys, 128); + + // Find nearest polygon amongst the nearby polygons. + dtPolyRef nearest = 0; + float nearestDistanceSqr = FLT_MAX; + for (int i = 0; i < polyCount; ++i) + { + dtPolyRef ref = polys[i]; + float closestPtPoly[3]; + float diff[3]; + bool posOverPoly = false; + float d; + closestPointOnPoly(ref, center, closestPtPoly, &posOverPoly); + + // If a point is directly over a polygon and closer than + // climb height, favor that instead of straight line nearest point. + dtVsub(diff, center, closestPtPoly); + if (posOverPoly) + { + d = dtAbs(diff[1]) - tile->header->walkableClimb; + d = d > 0 ? d*d : 0; + } + else + { + d = dtVlenSqr(diff); + } + + if (d < nearestDistanceSqr) + { + dtVcopy(nearestPt, closestPtPoly); + nearestDistanceSqr = d; + nearest = ref; + } + } + + return nearest; +} + +int dtNavMesh::queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax, + dtPolyRef* polys, const int maxPolys) const +{ + if (tile->bvTree) + { + const dtBVNode* node = &tile->bvTree[0]; + const dtBVNode* end = &tile->bvTree[tile->header->bvNodeCount]; + const float* tbmin = tile->header->bmin; + const float* tbmax = tile->header->bmax; + const float qfac = tile->header->bvQuantFactor; + + // Calculate quantized box + unsigned short bmin[3], bmax[3]; + // dtClamp query box to world box. + float minx = dtClamp(qmin[0], tbmin[0], tbmax[0]) - tbmin[0]; + float miny = dtClamp(qmin[1], tbmin[1], tbmax[1]) - tbmin[1]; + float minz = dtClamp(qmin[2], tbmin[2], tbmax[2]) - tbmin[2]; + float maxx = dtClamp(qmax[0], tbmin[0], tbmax[0]) - tbmin[0]; + float maxy = dtClamp(qmax[1], tbmin[1], tbmax[1]) - tbmin[1]; + float maxz = dtClamp(qmax[2], tbmin[2], tbmax[2]) - tbmin[2]; + // Quantize + bmin[0] = (unsigned short)(qfac * minx) & 0xfffe; + bmin[1] = (unsigned short)(qfac * miny) & 0xfffe; + bmin[2] = (unsigned short)(qfac * minz) & 0xfffe; + bmax[0] = (unsigned short)(qfac * maxx + 1) | 1; + bmax[1] = (unsigned short)(qfac * maxy + 1) | 1; + bmax[2] = (unsigned short)(qfac * maxz + 1) | 1; + + // Traverse tree + dtPolyRef base = getPolyRefBase(tile); + int n = 0; + while (node < end) + { + const bool overlap = dtOverlapQuantBounds(bmin, bmax, node->bmin, node->bmax); + const bool isLeafNode = node->i >= 0; + + if (isLeafNode && overlap) + { + if (n < maxPolys) + polys[n++] = base | (dtPolyRef)node->i; + } + + if (overlap || isLeafNode) + node++; + else + { + const int escapeIndex = -node->i; + node += escapeIndex; + } + } + + return n; + } + else + { + float bmin[3], bmax[3]; + int n = 0; + dtPolyRef base = getPolyRefBase(tile); + for (int i = 0; i < tile->header->polyCount; ++i) + { + dtPoly* p = &tile->polys[i]; + // Do not return off-mesh connection polygons. + if (p->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) + continue; + // Calc polygon bounds. + const float* v = &tile->verts[p->verts[0]*3]; + dtVcopy(bmin, v); + dtVcopy(bmax, v); + for (int j = 1; j < p->vertCount; ++j) + { + v = &tile->verts[p->verts[j]*3]; + dtVmin(bmin, v); + dtVmax(bmax, v); + } + if (dtOverlapBounds(qmin,qmax, bmin,bmax)) + { + if (n < maxPolys) + polys[n++] = base | (dtPolyRef)i; + } + } + return n; + } +} + +/// @par +/// +/// The add operation will fail if the data is in the wrong format, the allocated tile +/// space is full, or there is a tile already at the specified reference. +/// +/// The lastRef parameter is used to restore a tile with the same tile +/// reference it had previously used. In this case the #dtPolyRef's for the +/// 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) +{ + // Make sure the data is in right format. + dtMeshHeader* header = (dtMeshHeader*)data; + if (header->magic != DT_NAVMESH_MAGIC) + return DT_FAILURE | DT_WRONG_MAGIC; + if (header->version != DT_NAVMESH_VERSION) + return DT_FAILURE | DT_WRONG_VERSION; + + // Make sure the location is free. + if (getTileAt(header->x, header->y, header->layer)) + return DT_FAILURE | DT_ALREADY_OCCUPIED; + + // Allocate a tile. + dtMeshTile* tile = 0; + if (!lastRef) + { + if (m_nextFree) + { + tile = m_nextFree; + m_nextFree = tile->next; + tile->next = 0; + } + } + else + { + // Try to relocate the tile to specific index with same salt. + int tileIndex = (int)decodePolyIdTile((dtPolyRef)lastRef); + if (tileIndex >= m_maxTiles) + return DT_FAILURE | DT_OUT_OF_MEMORY; + // Try to find the specific tile id from the free list. + dtMeshTile* target = &m_tiles[tileIndex]; + dtMeshTile* prev = 0; + tile = m_nextFree; + while (tile && tile != target) + { + prev = tile; + tile = tile->next; + } + // Could not find the correct location. + if (tile != target) + return DT_FAILURE | DT_OUT_OF_MEMORY; + // Remove from freelist + if (!prev) + m_nextFree = tile->next; + else + prev->next = tile->next; + + // Restore salt. + tile->salt = decodePolyIdSalt((dtPolyRef)lastRef); + } + + // Make sure we could allocate a tile. + if (!tile) + return DT_FAILURE | DT_OUT_OF_MEMORY; + + // Insert tile into the position lut. + int h = computeTileHash(header->x, header->y, m_tileLutMask); + tile->next = m_posLookup[h]; + m_posLookup[h] = tile; + + // Patch header pointers. + const int headerSize = dtAlign4(sizeof(dtMeshHeader)); + const int vertsSize = dtAlign4(sizeof(float)*3*header->vertCount); + const int polysSize = dtAlign4(sizeof(dtPoly)*header->polyCount); + const int linksSize = dtAlign4(sizeof(dtLink)*(header->maxLinkCount)); + const int detailMeshesSize = dtAlign4(sizeof(dtPolyDetail)*header->detailMeshCount); + const int detailVertsSize = dtAlign4(sizeof(float)*3*header->detailVertCount); + const int detailTrisSize = dtAlign4(sizeof(unsigned char)*4*header->detailTriCount); + const int bvtreeSize = dtAlign4(sizeof(dtBVNode)*header->bvNodeCount); + const int offMeshLinksSize = dtAlign4(sizeof(dtOffMeshConnection)*header->offMeshConCount); + + unsigned char* d = data + headerSize; + tile->verts = dtGetThenAdvanceBufferPointer(d, vertsSize); + tile->polys = dtGetThenAdvanceBufferPointer(d, polysSize); + tile->links = dtGetThenAdvanceBufferPointer(d, linksSize); + tile->detailMeshes = dtGetThenAdvanceBufferPointer(d, detailMeshesSize); + tile->detailVerts = dtGetThenAdvanceBufferPointer(d, detailVertsSize); + tile->detailTris = dtGetThenAdvanceBufferPointer(d, detailTrisSize); + tile->bvTree = dtGetThenAdvanceBufferPointer(d, bvtreeSize); + tile->offMeshCons = dtGetThenAdvanceBufferPointer(d, offMeshLinksSize); + + // If there are no items in the bvtree, reset the tree pointer. + if (!bvtreeSize) + tile->bvTree = 0; + + // Build links freelist + tile->linksFreeList = 0; + tile->links[header->maxLinkCount-1].next = DT_NULL_LINK; + for (int i = 0; i < header->maxLinkCount-1; ++i) + tile->links[i].next = i+1; + + // Init tile. + tile->header = header; + tile->data = data; + tile->dataSize = dataSize; + 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; + dtMeshTile* neis[MAX_NEIS]; + int nneis; + + // Connect with layers in current tile. + nneis = getTilesAt(header->x, header->y, neis, MAX_NEIS); + for (int j = 0; j < nneis; ++j) + { + 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); + } + + // Connect with neighbour tiles. + for (int i = 0; i < 8; ++i) + { + nneis = getNeighbourTilesAt(header->x, header->y, i, neis, MAX_NEIS); + for (int j = 0; j < nneis; ++j) + { + connectExtLinks(tile, neis[j], i); + connectExtLinks(neis[j], tile, dtOppositeTile(i)); + connectExtOffMeshLinks(tile, neis[j], i); + connectExtOffMeshLinks(neis[j], tile, dtOppositeTile(i)); + } + } + + if (result) + *result = getTileRef(tile); + + return DT_SUCCESS; +} + +const dtMeshTile* dtNavMesh::getTileAt(const int x, const int y, const int layer) const +{ + // Find tile based on hash. + int h = computeTileHash(x,y,m_tileLutMask); + dtMeshTile* tile = m_posLookup[h]; + while (tile) + { + if (tile->header && + tile->header->x == x && + tile->header->y == y && + tile->header->layer == layer) + { + return tile; + } + tile = tile->next; + } + return 0; +} + +int dtNavMesh::getNeighbourTilesAt(const int x, const int y, const int side, dtMeshTile** tiles, const int maxTiles) const +{ + int nx = x, ny = y; + switch (side) + { + case 0: nx++; break; + case 1: nx++; ny++; break; + case 2: ny++; break; + case 3: nx--; ny++; break; + case 4: nx--; break; + case 5: nx--; ny--; break; + case 6: ny--; break; + case 7: nx++; ny--; break; + }; + + return getTilesAt(nx, ny, tiles, maxTiles); +} + +int dtNavMesh::getTilesAt(const int x, const int y, dtMeshTile** tiles, const int maxTiles) const +{ + int n = 0; + + // Find tile based on hash. + int h = computeTileHash(x,y,m_tileLutMask); + dtMeshTile* tile = m_posLookup[h]; + while (tile) + { + if (tile->header && + tile->header->x == x && + tile->header->y == y) + { + if (n < maxTiles) + tiles[n++] = tile; + } + tile = tile->next; + } + + return n; +} + +/// @par +/// +/// This function will not fail if the tiles array is too small to hold the +/// entire result set. It will simply fill the array to capacity. +int dtNavMesh::getTilesAt(const int x, const int y, dtMeshTile const** tiles, const int maxTiles) const +{ + int n = 0; + + // Find tile based on hash. + int h = computeTileHash(x,y,m_tileLutMask); + dtMeshTile* tile = m_posLookup[h]; + while (tile) + { + if (tile->header && + tile->header->x == x && + tile->header->y == y) + { + if (n < maxTiles) + tiles[n++] = tile; + } + tile = tile->next; + } + + return n; +} + + +dtTileRef dtNavMesh::getTileRefAt(const int x, const int y, const int layer) const +{ + // Find tile based on hash. + int h = computeTileHash(x,y,m_tileLutMask); + dtMeshTile* tile = m_posLookup[h]; + while (tile) + { + if (tile->header && + tile->header->x == x && + tile->header->y == y && + tile->header->layer == layer) + { + return getTileRef(tile); + } + tile = tile->next; + } + return 0; +} + +const dtMeshTile* dtNavMesh::getTileByRef(dtTileRef ref) const +{ + if (!ref) + return 0; + unsigned int tileIndex = decodePolyIdTile((dtPolyRef)ref); + unsigned int tileSalt = decodePolyIdSalt((dtPolyRef)ref); + if ((int)tileIndex >= m_maxTiles) + return 0; + const dtMeshTile* tile = &m_tiles[tileIndex]; + if (tile->salt != tileSalt) + return 0; + return tile; +} + +int dtNavMesh::getMaxTiles() const +{ + return m_maxTiles; +} + +dtMeshTile* dtNavMesh::getTile(int i) +{ + return &m_tiles[i]; +} + +const dtMeshTile* dtNavMesh::getTile(int i) const +{ + return &m_tiles[i]; +} + +void dtNavMesh::calcTileLoc(const float* pos, int* tx, int* ty) const +{ + *tx = (int)floorf((pos[0]-m_orig[0]) / m_tileWidth); + *ty = (int)floorf((pos[2]-m_orig[2]) / m_tileHeight); +} + +dtStatus dtNavMesh::getTileAndPolyByRef(const dtPolyRef ref, const dtMeshTile** tile, const dtPoly** poly) const +{ + if (!ref) return DT_FAILURE; + unsigned int salt, it, ip; + decodePolyId(ref, salt, it, ip); + if (it >= (unsigned int)m_maxTiles) return DT_FAILURE | DT_INVALID_PARAM; + if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return DT_FAILURE | DT_INVALID_PARAM; + if (ip >= (unsigned int)m_tiles[it].header->polyCount) return DT_FAILURE | DT_INVALID_PARAM; + *tile = &m_tiles[it]; + *poly = &m_tiles[it].polys[ip]; + return DT_SUCCESS; +} + +/// @par +/// +/// @warning Only use this function if it is known that the provided polygon +/// reference is valid. This function is faster than #getTileAndPolyByRef, but +/// it does not validate the reference. +void dtNavMesh::getTileAndPolyByRefUnsafe(const dtPolyRef ref, const dtMeshTile** tile, const dtPoly** poly) const +{ + unsigned int salt, it, ip; + decodePolyId(ref, salt, it, ip); + *tile = &m_tiles[it]; + *poly = &m_tiles[it].polys[ip]; +} + +bool dtNavMesh::isValidPolyRef(dtPolyRef ref) const +{ + if (!ref) return false; + unsigned int salt, it, ip; + decodePolyId(ref, salt, it, ip); + if (it >= (unsigned int)m_maxTiles) return false; + if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return false; + if (ip >= (unsigned int)m_tiles[it].header->polyCount) return false; + return true; +} + +/// @par +/// +/// This function returns the data for the tile so that, if desired, +/// it can be added back to the navigation mesh at a later point. +/// +/// @see #addTile +dtStatus dtNavMesh::removeTile(dtTileRef ref, unsigned char** data, int* dataSize) +{ + if (!ref) + return DT_FAILURE | DT_INVALID_PARAM; + unsigned int tileIndex = decodePolyIdTile((dtPolyRef)ref); + unsigned int tileSalt = decodePolyIdSalt((dtPolyRef)ref); + if ((int)tileIndex >= m_maxTiles) + return DT_FAILURE | DT_INVALID_PARAM; + dtMeshTile* tile = &m_tiles[tileIndex]; + if (tile->salt != tileSalt) + return DT_FAILURE | DT_INVALID_PARAM; + + // Remove tile from hash lookup. + int h = computeTileHash(tile->header->x,tile->header->y,m_tileLutMask); + dtMeshTile* prev = 0; + dtMeshTile* cur = m_posLookup[h]; + while (cur) + { + if (cur == tile) + { + if (prev) + prev->next = cur->next; + else + m_posLookup[h] = cur->next; + break; + } + prev = cur; + cur = cur->next; + } + + // Remove connections to neighbour tiles. + static const int MAX_NEIS = 32; + dtMeshTile* neis[MAX_NEIS]; + int nneis; + + // 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; + unconnectLinks(neis[j], tile); + } + + // 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) + unconnectLinks(neis[j], tile); + } + + // Reset tile. + if (tile->flags & DT_TILE_FREE_DATA) + { + // Owns data + dtFree(tile->data); + tile->data = 0; + tile->dataSize = 0; + if (data) *data = 0; + if (dataSize) *dataSize = 0; + } + else + { + if (data) *data = tile->data; + if (dataSize) *dataSize = tile->dataSize; + } + + tile->header = 0; + tile->flags = 0; + tile->linksFreeList = 0; + tile->polys = 0; + tile->verts = 0; + tile->links = 0; + tile->detailMeshes = 0; + tile->detailVerts = 0; + tile->detailTris = 0; + tile->bvTree = 0; + tile->offMeshCons = 0; + + // Update salt, salt should never be zero. +#ifdef DT_POLYREF64 + tile->salt = (tile->salt+1) & ((1<salt = (tile->salt+1) & ((1<salt == 0) + tile->salt++; + + // Add to free list. + tile->next = m_nextFree; + m_nextFree = tile; + + return DT_SUCCESS; +} + +dtTileRef dtNavMesh::getTileRef(const dtMeshTile* tile) const +{ + if (!tile) return 0; + const unsigned int it = (unsigned int)(tile - m_tiles); + return (dtTileRef)encodePolyId(tile->salt, it, 0); +} + +/// @par +/// +/// Example use case: +/// @code +/// +/// const dtPolyRef base = navmesh->getPolyRefBase(tile); +/// for (int i = 0; i < tile->header->polyCount; ++i) +/// { +/// const dtPoly* p = &tile->polys[i]; +/// const dtPolyRef ref = base | (dtPolyRef)i; +/// +/// // Use the reference to access the polygon data. +/// } +/// @endcode +dtPolyRef dtNavMesh::getPolyRefBase(const dtMeshTile* tile) const +{ + if (!tile) return 0; + const unsigned int it = (unsigned int)(tile - m_tiles); + return encodePolyId(tile->salt, it, 0); +} + +struct dtTileState +{ + int magic; // Magic number, used to identify the data. + int version; // Data version number. + dtTileRef ref; // Tile ref at the time of storing the data. +}; + +struct dtPolyState +{ + unsigned short flags; // Flags (see dtPolyFlags). + unsigned char area; // Area ID of the polygon. +}; + +/// @see #storeTileState +int dtNavMesh::getTileStateSize(const dtMeshTile* tile) const +{ + if (!tile) return 0; + const int headerSize = dtAlign4(sizeof(dtTileState)); + const int polyStateSize = dtAlign4(sizeof(dtPolyState) * tile->header->polyCount); + return headerSize + polyStateSize; +} + +/// @par +/// +/// Tile state includes non-structural data such as polygon flags, area ids, etc. +/// @note The state data is only valid until the tile reference changes. +/// @see #getTileStateSize, #restoreTileState +dtStatus dtNavMesh::storeTileState(const dtMeshTile* tile, unsigned char* data, const int maxDataSize) const +{ + // Make sure there is enough space to store the state. + const int sizeReq = getTileStateSize(tile); + if (maxDataSize < sizeReq) + return DT_FAILURE | DT_BUFFER_TOO_SMALL; + + dtTileState* tileState = dtGetThenAdvanceBufferPointer(data, dtAlign4(sizeof(dtTileState))); + dtPolyState* polyStates = dtGetThenAdvanceBufferPointer(data, dtAlign4(sizeof(dtPolyState) * tile->header->polyCount)); + + // Store tile state. + tileState->magic = DT_NAVMESH_STATE_MAGIC; + tileState->version = DT_NAVMESH_STATE_VERSION; + tileState->ref = getTileRef(tile); + + // Store per poly state. + for (int i = 0; i < tile->header->polyCount; ++i) + { + const dtPoly* p = &tile->polys[i]; + dtPolyState* s = &polyStates[i]; + s->flags = p->flags; + s->area = p->getArea(); + } + + return DT_SUCCESS; +} + +/// @par +/// +/// Tile state includes non-structural data such as polygon flags, area ids, etc. +/// @note This function does not impact the tile's #dtTileRef and #dtPolyRef's. +/// @see #storeTileState +dtStatus dtNavMesh::restoreTileState(dtMeshTile* tile, const unsigned char* data, const int maxDataSize) +{ + // Make sure there is enough space to store the state. + const int sizeReq = getTileStateSize(tile); + if (maxDataSize < sizeReq) + return DT_FAILURE | DT_INVALID_PARAM; + + const dtTileState* tileState = dtGetThenAdvanceBufferPointer(data, dtAlign4(sizeof(dtTileState))); + const dtPolyState* polyStates = dtGetThenAdvanceBufferPointer(data, dtAlign4(sizeof(dtPolyState) * tile->header->polyCount)); + + // Check that the restore is possible. + if (tileState->magic != DT_NAVMESH_STATE_MAGIC) + return DT_FAILURE | DT_WRONG_MAGIC; + if (tileState->version != DT_NAVMESH_STATE_VERSION) + return DT_FAILURE | DT_WRONG_VERSION; + if (tileState->ref != getTileRef(tile)) + return DT_FAILURE | DT_INVALID_PARAM; + + // Restore per poly state. + for (int i = 0; i < tile->header->polyCount; ++i) + { + dtPoly* p = &tile->polys[i]; + const dtPolyState* s = &polyStates[i]; + p->flags = s->flags; + p->setArea(s->area); + } + + return DT_SUCCESS; +} + +/// @par +/// +/// Off-mesh connections are stored in the navigation mesh as special 2-vertex +/// polygons with a single edge. At least one of the vertices is expected to be +/// inside a normal polygon. So an off-mesh connection is "entered" from a +/// normal polygon at one of its endpoints. This is the polygon identified by +/// the prevRef parameter. +dtStatus dtNavMesh::getOffMeshConnectionPolyEndPoints(dtPolyRef prevRef, dtPolyRef polyRef, float* startPos, float* endPos) const +{ + unsigned int salt, it, ip; + + if (!polyRef) + return DT_FAILURE; + + // Get current polygon + decodePolyId(polyRef, salt, it, ip); + if (it >= (unsigned int)m_maxTiles) return DT_FAILURE | DT_INVALID_PARAM; + if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return DT_FAILURE | DT_INVALID_PARAM; + const dtMeshTile* tile = &m_tiles[it]; + if (ip >= (unsigned int)tile->header->polyCount) return DT_FAILURE | DT_INVALID_PARAM; + const dtPoly* poly = &tile->polys[ip]; + + // Make sure that the current poly is indeed off-mesh link. + if (poly->getType() != DT_POLYTYPE_OFFMESH_CONNECTION) + return DT_FAILURE; + + // Figure out which way to hand out the vertices. + int idx0 = 0, idx1 = 1; + + // Find link that points to first vertex. + for (unsigned int i = poly->firstLink; i != DT_NULL_LINK; i = tile->links[i].next) + { + if (tile->links[i].edge == 0) + { + if (tile->links[i].ref != prevRef) + { + idx0 = 1; + idx1 = 0; + } + break; + } + } + + dtVcopy(startPos, &tile->verts[poly->verts[idx0]*3]); + dtVcopy(endPos, &tile->verts[poly->verts[idx1]*3]); + + return DT_SUCCESS; +} + + +const dtOffMeshConnection* dtNavMesh::getOffMeshConnectionByRef(dtPolyRef ref) const +{ + unsigned int salt, it, ip; + + if (!ref) + return 0; + + // Get current polygon + decodePolyId(ref, salt, it, ip); + if (it >= (unsigned int)m_maxTiles) return 0; + if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return 0; + const dtMeshTile* tile = &m_tiles[it]; + if (ip >= (unsigned int)tile->header->polyCount) return 0; + const dtPoly* poly = &tile->polys[ip]; + + // Make sure that the current poly is indeed off-mesh link. + if (poly->getType() != DT_POLYTYPE_OFFMESH_CONNECTION) + return 0; + + const unsigned int idx = ip - tile->header->offMeshBase; + dtAssert(idx < (unsigned int)tile->header->offMeshConCount); + return &tile->offMeshCons[idx]; +} + + +dtStatus dtNavMesh::setPolyFlags(dtPolyRef ref, unsigned short flags) +{ + if (!ref) return DT_FAILURE; + unsigned int salt, it, ip; + decodePolyId(ref, salt, it, ip); + if (it >= (unsigned int)m_maxTiles) return DT_FAILURE | DT_INVALID_PARAM; + if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return DT_FAILURE | DT_INVALID_PARAM; + dtMeshTile* tile = &m_tiles[it]; + if (ip >= (unsigned int)tile->header->polyCount) return DT_FAILURE | DT_INVALID_PARAM; + dtPoly* poly = &tile->polys[ip]; + + // Change flags. + poly->flags = flags; + + return DT_SUCCESS; +} + +dtStatus dtNavMesh::getPolyFlags(dtPolyRef ref, unsigned short* resultFlags) const +{ + if (!ref) return DT_FAILURE; + unsigned int salt, it, ip; + decodePolyId(ref, salt, it, ip); + if (it >= (unsigned int)m_maxTiles) return DT_FAILURE | DT_INVALID_PARAM; + if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return DT_FAILURE | DT_INVALID_PARAM; + const dtMeshTile* tile = &m_tiles[it]; + if (ip >= (unsigned int)tile->header->polyCount) return DT_FAILURE | DT_INVALID_PARAM; + const dtPoly* poly = &tile->polys[ip]; + + *resultFlags = poly->flags; + + return DT_SUCCESS; +} + +dtStatus dtNavMesh::setPolyArea(dtPolyRef ref, unsigned char area) +{ + if (!ref) return DT_FAILURE; + unsigned int salt, it, ip; + decodePolyId(ref, salt, it, ip); + if (it >= (unsigned int)m_maxTiles) return DT_FAILURE | DT_INVALID_PARAM; + if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return DT_FAILURE | DT_INVALID_PARAM; + dtMeshTile* tile = &m_tiles[it]; + if (ip >= (unsigned int)tile->header->polyCount) return DT_FAILURE | DT_INVALID_PARAM; + dtPoly* poly = &tile->polys[ip]; + + poly->setArea(area); + + return DT_SUCCESS; +} + +dtStatus dtNavMesh::getPolyArea(dtPolyRef ref, unsigned char* resultArea) const +{ + if (!ref) return DT_FAILURE; + unsigned int salt, it, ip; + decodePolyId(ref, salt, it, ip); + if (it >= (unsigned int)m_maxTiles) return DT_FAILURE | DT_INVALID_PARAM; + if (m_tiles[it].salt != salt || m_tiles[it].header == 0) return DT_FAILURE | DT_INVALID_PARAM; + const dtMeshTile* tile = &m_tiles[it]; + if (ip >= (unsigned int)tile->header->polyCount) return DT_FAILURE | DT_INVALID_PARAM; + const dtPoly* poly = &tile->polys[ip]; + + *resultArea = poly->getArea(); + + return DT_SUCCESS; +} + diff --git a/external/recast/src/DetourNavMeshBuilder.cpp b/external/recast/src/DetourNavMeshBuilder.cpp new file mode 100644 index 0000000000..e93a97629b --- /dev/null +++ b/external/recast/src/DetourNavMeshBuilder.cpp @@ -0,0 +1,802 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#include +#include +#include +#include +#include "DetourNavMesh.h" +#include "DetourCommon.h" +#include "DetourMath.h" +#include "DetourNavMeshBuilder.h" +#include "DetourAlloc.h" +#include "DetourAssert.h" + +static unsigned short MESH_NULL_IDX = 0xffff; + + +struct BVItem +{ + unsigned short bmin[3]; + unsigned short bmax[3]; + int i; +}; + +static int compareItemX(const void* va, const void* vb) +{ + const BVItem* a = (const BVItem*)va; + const BVItem* b = (const BVItem*)vb; + if (a->bmin[0] < b->bmin[0]) + return -1; + if (a->bmin[0] > b->bmin[0]) + return 1; + return 0; +} + +static int compareItemY(const void* va, const void* vb) +{ + const BVItem* a = (const BVItem*)va; + const BVItem* b = (const BVItem*)vb; + if (a->bmin[1] < b->bmin[1]) + return -1; + if (a->bmin[1] > b->bmin[1]) + return 1; + return 0; +} + +static int compareItemZ(const void* va, const void* vb) +{ + const BVItem* a = (const BVItem*)va; + const BVItem* b = (const BVItem*)vb; + if (a->bmin[2] < b->bmin[2]) + return -1; + if (a->bmin[2] > b->bmin[2]) + return 1; + return 0; +} + +static void calcExtends(BVItem* items, const int /*nitems*/, const int imin, const int imax, + unsigned short* bmin, unsigned short* bmax) +{ + bmin[0] = items[imin].bmin[0]; + bmin[1] = items[imin].bmin[1]; + bmin[2] = items[imin].bmin[2]; + + bmax[0] = items[imin].bmax[0]; + bmax[1] = items[imin].bmax[1]; + bmax[2] = items[imin].bmax[2]; + + for (int i = imin+1; i < imax; ++i) + { + const BVItem& it = items[i]; + if (it.bmin[0] < bmin[0]) bmin[0] = it.bmin[0]; + if (it.bmin[1] < bmin[1]) bmin[1] = it.bmin[1]; + if (it.bmin[2] < bmin[2]) bmin[2] = it.bmin[2]; + + if (it.bmax[0] > bmax[0]) bmax[0] = it.bmax[0]; + if (it.bmax[1] > bmax[1]) bmax[1] = it.bmax[1]; + if (it.bmax[2] > bmax[2]) bmax[2] = it.bmax[2]; + } +} + +inline int longestAxis(unsigned short x, unsigned short y, unsigned short z) +{ + int axis = 0; + unsigned short maxVal = x; + if (y > maxVal) + { + axis = 1; + maxVal = y; + } + if (z > maxVal) + { + axis = 2; + } + return axis; +} + +static void subdivide(BVItem* items, int nitems, int imin, int imax, int& curNode, dtBVNode* nodes) +{ + int inum = imax - imin; + int icur = curNode; + + dtBVNode& node = nodes[curNode++]; + + if (inum == 1) + { + // Leaf + node.bmin[0] = items[imin].bmin[0]; + node.bmin[1] = items[imin].bmin[1]; + node.bmin[2] = items[imin].bmin[2]; + + node.bmax[0] = items[imin].bmax[0]; + node.bmax[1] = items[imin].bmax[1]; + node.bmax[2] = items[imin].bmax[2]; + + node.i = items[imin].i; + } + else + { + // Split + calcExtends(items, nitems, imin, imax, node.bmin, node.bmax); + + int axis = longestAxis(node.bmax[0] - node.bmin[0], + node.bmax[1] - node.bmin[1], + node.bmax[2] - node.bmin[2]); + + if (axis == 0) + { + // Sort along x-axis + qsort(items+imin, inum, sizeof(BVItem), compareItemX); + } + else if (axis == 1) + { + // Sort along y-axis + qsort(items+imin, inum, sizeof(BVItem), compareItemY); + } + else + { + // Sort along z-axis + qsort(items+imin, inum, sizeof(BVItem), compareItemZ); + } + + int isplit = imin+inum/2; + + // Left + subdivide(items, nitems, imin, isplit, curNode, nodes); + // Right + subdivide(items, nitems, isplit, imax, curNode, nodes); + + int iescape = curNode - icur; + // Negative index means escape. + node.i = -iescape; + } +} + +static int createBVTree(dtNavMeshCreateParams* params, dtBVNode* nodes, int /*nnodes*/) +{ + // Build tree + 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. Use detail meshes if available. + if (params->detailMeshes) + { + 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 = ¶ms->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 = ¶ms->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); + } + } + + int curNode = 0; + subdivide(items, params->polyCount, 0, params->polyCount, curNode, nodes); + + dtFree(items); + + return curNode; +} + +static unsigned char classifyOffMeshPoint(const float* pt, const float* bmin, const float* bmax) +{ + static const unsigned char XP = 1<<0; + static const unsigned char ZP = 1<<1; + static const unsigned char XM = 1<<2; + static const unsigned char ZM = 1<<3; + + unsigned char outcode = 0; + outcode |= (pt[0] >= bmax[0]) ? XP : 0; + outcode |= (pt[2] >= bmax[2]) ? ZP : 0; + outcode |= (pt[0] < bmin[0]) ? XM : 0; + outcode |= (pt[2] < bmin[2]) ? ZM : 0; + + switch (outcode) + { + case XP: return 0; + case XP|ZP: return 1; + case ZP: return 2; + case XM|ZP: return 3; + case XM: return 4; + case XM|ZM: return 5; + case ZM: return 6; + case XP|ZM: return 7; + }; + + return 0xff; +} + +// TODO: Better error handling. + +/// @par +/// +/// The output data array is allocated using the detour allocator (dtAlloc()). The method +/// used to free the memory will be determined by how the tile is added to the navigation +/// mesh. +/// +/// @see dtNavMesh, dtNavMesh::addTile() +bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData, int* outDataSize) +{ + if (params->nvp > DT_VERTS_PER_POLYGON) + return false; + if (params->vertCount >= 0xffff) + return false; + if (!params->vertCount || !params->verts) + return false; + if (!params->polyCount || !params->polys) + return false; + + const int nvp = params->nvp; + + // Classify off-mesh connection points. We store only the connections + // whose start point is inside the tile. + unsigned char* offMeshConClass = 0; + int storedOffMeshConCount = 0; + int offMeshConLinkCount = 0; + + if (params->offMeshConCount > 0) + { + offMeshConClass = (unsigned char*)dtAlloc(sizeof(unsigned char)*params->offMeshConCount*2, DT_ALLOC_TEMP); + if (!offMeshConClass) + return false; + + // Find tight heigh bounds, used for culling out off-mesh start locations. + float hmin = FLT_MAX; + float hmax = -FLT_MAX; + + if (params->detailVerts && params->detailVertsCount) + { + for (int i = 0; i < params->detailVertsCount; ++i) + { + const float h = params->detailVerts[i*3+1]; + hmin = dtMin(hmin,h); + hmax = dtMax(hmax,h); + } + } + else + { + for (int i = 0; i < params->vertCount; ++i) + { + const unsigned short* iv = ¶ms->verts[i*3]; + const float h = params->bmin[1] + iv[1] * params->ch; + hmin = dtMin(hmin,h); + hmax = dtMax(hmax,h); + } + } + hmin -= params->walkableClimb; + hmax += params->walkableClimb; + float bmin[3], bmax[3]; + dtVcopy(bmin, params->bmin); + dtVcopy(bmax, params->bmax); + bmin[1] = hmin; + bmax[1] = hmax; + + for (int i = 0; i < params->offMeshConCount; ++i) + { + const float* p0 = ¶ms->offMeshConVerts[(i*2+0)*3]; + const float* p1 = ¶ms->offMeshConVerts[(i*2+1)*3]; + offMeshConClass[i*2+0] = classifyOffMeshPoint(p0, bmin, bmax); + offMeshConClass[i*2+1] = classifyOffMeshPoint(p1, bmin, bmax); + + // Zero out off-mesh start positions which are not even potentially touching the mesh. + if (offMeshConClass[i*2+0] == 0xff) + { + if (p0[1] < bmin[1] || p0[1] > bmax[1]) + offMeshConClass[i*2+0] = 0; + } + + // Cound how many links should be allocated for off-mesh connections. + if (offMeshConClass[i*2+0] == 0xff) + offMeshConLinkCount++; + if (offMeshConClass[i*2+1] == 0xff) + offMeshConLinkCount++; + + if (offMeshConClass[i*2+0] == 0xff) + storedOffMeshConCount++; + } + } + + // Off-mesh connectionss are stored as polygons, adjust values. + const int totPolyCount = params->polyCount + storedOffMeshConCount; + const int totVertCount = params->vertCount + storedOffMeshConCount*2; + + // Find portal edges which are at tile borders. + int edgeCount = 0; + int portalCount = 0; + for (int i = 0; i < params->polyCount; ++i) + { + const unsigned short* p = ¶ms->polys[i*2*nvp]; + for (int j = 0; j < nvp; ++j) + { + if (p[j] == MESH_NULL_IDX) break; + edgeCount++; + + if (p[nvp+j] & 0x8000) + { + unsigned short dir = p[nvp+j] & 0xf; + if (dir != 0xf) + portalCount++; + } + } + } + + const int maxLinkCount = edgeCount + portalCount*2 + offMeshConLinkCount*2; + + // Find unique detail vertices. + int uniqueDetailVertCount = 0; + int detailTriCount = 0; + if (params->detailMeshes) + { + // Has detail mesh, count unique detail vertex count and use input detail tri count. + detailTriCount = params->detailTriCount; + for (int i = 0; i < params->polyCount; ++i) + { + const unsigned short* p = ¶ms->polys[i*nvp*2]; + int ndv = params->detailMeshes[i*4+1]; + int nv = 0; + for (int j = 0; j < nvp; ++j) + { + if (p[j] == MESH_NULL_IDX) break; + nv++; + } + ndv -= nv; + uniqueDetailVertCount += ndv; + } + } + else + { + // No input detail mesh, build detail mesh from nav polys. + uniqueDetailVertCount = 0; // No extra detail verts. + detailTriCount = 0; + for (int i = 0; i < params->polyCount; ++i) + { + const unsigned short* p = ¶ms->polys[i*nvp*2]; + int nv = 0; + for (int j = 0; j < nvp; ++j) + { + if (p[j] == MESH_NULL_IDX) break; + nv++; + } + detailTriCount += nv-2; + } + } + + // Calculate data size + const int headerSize = dtAlign4(sizeof(dtMeshHeader)); + const int vertsSize = dtAlign4(sizeof(float)*3*totVertCount); + const int polysSize = dtAlign4(sizeof(dtPoly)*totPolyCount); + const int linksSize = dtAlign4(sizeof(dtLink)*maxLinkCount); + const int detailMeshesSize = dtAlign4(sizeof(dtPolyDetail)*params->polyCount); + const int detailVertsSize = dtAlign4(sizeof(float)*3*uniqueDetailVertCount); + const int detailTrisSize = dtAlign4(sizeof(unsigned char)*4*detailTriCount); + const int bvTreeSize = params->buildBvTree ? dtAlign4(sizeof(dtBVNode)*params->polyCount*2) : 0; + const int offMeshConsSize = dtAlign4(sizeof(dtOffMeshConnection)*storedOffMeshConCount); + + const int dataSize = headerSize + vertsSize + polysSize + linksSize + + detailMeshesSize + detailVertsSize + detailTrisSize + + bvTreeSize + offMeshConsSize; + + unsigned char* data = (unsigned char*)dtAlloc(sizeof(unsigned char)*dataSize, DT_ALLOC_PERM); + if (!data) + { + dtFree(offMeshConClass); + return false; + } + memset(data, 0, dataSize); + + unsigned char* d = data; + + dtMeshHeader* header = dtGetThenAdvanceBufferPointer(d, headerSize); + float* navVerts = dtGetThenAdvanceBufferPointer(d, vertsSize); + dtPoly* navPolys = dtGetThenAdvanceBufferPointer(d, polysSize); + d += linksSize; // Ignore links; just leave enough space for them. They'll be created on load. + dtPolyDetail* navDMeshes = dtGetThenAdvanceBufferPointer(d, detailMeshesSize); + float* navDVerts = dtGetThenAdvanceBufferPointer(d, detailVertsSize); + unsigned char* navDTris = dtGetThenAdvanceBufferPointer(d, detailTrisSize); + dtBVNode* navBvtree = dtGetThenAdvanceBufferPointer(d, bvTreeSize); + dtOffMeshConnection* offMeshCons = dtGetThenAdvanceBufferPointer(d, offMeshConsSize); + + + // Store header + header->magic = DT_NAVMESH_MAGIC; + header->version = DT_NAVMESH_VERSION; + header->x = params->tileX; + header->y = params->tileY; + header->layer = params->tileLayer; + header->userId = params->userId; + header->polyCount = totPolyCount; + header->vertCount = totVertCount; + header->maxLinkCount = maxLinkCount; + dtVcopy(header->bmin, params->bmin); + dtVcopy(header->bmax, params->bmax); + header->detailMeshCount = params->polyCount; + header->detailVertCount = uniqueDetailVertCount; + header->detailTriCount = detailTriCount; + header->bvQuantFactor = 1.0f / params->cs; + header->offMeshBase = params->polyCount; + header->walkableHeight = params->walkableHeight; + header->walkableRadius = params->walkableRadius; + header->walkableClimb = params->walkableClimb; + header->offMeshConCount = storedOffMeshConCount; + header->bvNodeCount = params->buildBvTree ? params->polyCount*2 : 0; + + const int offMeshVertsBase = params->vertCount; + const int offMeshPolyBase = params->polyCount; + + // Store vertices + // Mesh vertices + for (int i = 0; i < params->vertCount; ++i) + { + const unsigned short* iv = ¶ms->verts[i*3]; + float* v = &navVerts[i*3]; + v[0] = params->bmin[0] + iv[0] * params->cs; + v[1] = params->bmin[1] + iv[1] * params->ch; + v[2] = params->bmin[2] + iv[2] * params->cs; + } + // Off-mesh link vertices. + int n = 0; + for (int i = 0; i < params->offMeshConCount; ++i) + { + // Only store connections which start from this tile. + if (offMeshConClass[i*2+0] == 0xff) + { + const float* linkv = ¶ms->offMeshConVerts[i*2*3]; + float* v = &navVerts[(offMeshVertsBase + n*2)*3]; + dtVcopy(&v[0], &linkv[0]); + dtVcopy(&v[3], &linkv[3]); + n++; + } + } + + // Store polygons + // Mesh polys + const unsigned short* src = params->polys; + for (int i = 0; i < params->polyCount; ++i) + { + dtPoly* p = &navPolys[i]; + p->vertCount = 0; + p->flags = params->polyFlags[i]; + p->setArea(params->polyAreas[i]); + p->setType(DT_POLYTYPE_GROUND); + for (int j = 0; j < nvp; ++j) + { + if (src[j] == MESH_NULL_IDX) break; + p->verts[j] = src[j]; + if (src[nvp+j] & 0x8000) + { + // Border or portal edge. + unsigned short dir = src[nvp+j] & 0xf; + if (dir == 0xf) // Border + p->neis[j] = 0; + else if (dir == 0) // Portal x- + p->neis[j] = DT_EXT_LINK | 4; + else if (dir == 1) // Portal z+ + p->neis[j] = DT_EXT_LINK | 2; + else if (dir == 2) // Portal x+ + p->neis[j] = DT_EXT_LINK | 0; + else if (dir == 3) // Portal z- + p->neis[j] = DT_EXT_LINK | 6; + } + else + { + // Normal connection + p->neis[j] = src[nvp+j]+1; + } + + p->vertCount++; + } + src += nvp*2; + } + // Off-mesh connection vertices. + n = 0; + for (int i = 0; i < params->offMeshConCount; ++i) + { + // Only store connections which start from this tile. + if (offMeshConClass[i*2+0] == 0xff) + { + dtPoly* p = &navPolys[offMeshPolyBase+n]; + p->vertCount = 2; + p->verts[0] = (unsigned short)(offMeshVertsBase + n*2+0); + p->verts[1] = (unsigned short)(offMeshVertsBase + n*2+1); + p->flags = params->offMeshConFlags[i]; + p->setArea(params->offMeshConAreas[i]); + p->setType(DT_POLYTYPE_OFFMESH_CONNECTION); + n++; + } + } + + // Store detail meshes and vertices. + // The nav polygon vertices are stored as the first vertices on each mesh. + // We compress the mesh data by skipping them and using the navmesh coordinates. + if (params->detailMeshes) + { + unsigned short vbase = 0; + for (int i = 0; i < params->polyCount; ++i) + { + dtPolyDetail& dtl = navDMeshes[i]; + const int vb = (int)params->detailMeshes[i*4+0]; + const int ndv = (int)params->detailMeshes[i*4+1]; + const int nv = navPolys[i].vertCount; + dtl.vertBase = (unsigned int)vbase; + dtl.vertCount = (unsigned char)(ndv-nv); + dtl.triBase = (unsigned int)params->detailMeshes[i*4+2]; + dtl.triCount = (unsigned char)params->detailMeshes[i*4+3]; + // Copy vertices except the first 'nv' verts which are equal to nav poly verts. + if (ndv-nv) + { + memcpy(&navDVerts[vbase*3], ¶ms->detailVerts[(vb+nv)*3], sizeof(float)*3*(ndv-nv)); + vbase += (unsigned short)(ndv-nv); + } + } + // Store triangles. + memcpy(navDTris, params->detailTris, sizeof(unsigned char)*4*params->detailTriCount); + } + else + { + // Create dummy detail mesh by triangulating polys. + int tbase = 0; + for (int i = 0; i < params->polyCount; ++i) + { + dtPolyDetail& dtl = navDMeshes[i]; + const int nv = navPolys[i].vertCount; + dtl.vertBase = 0; + dtl.vertCount = 0; + dtl.triBase = (unsigned int)tbase; + dtl.triCount = (unsigned char)(nv-2); + // Triangulate polygon (local indices). + for (int j = 2; j < nv; ++j) + { + unsigned char* t = &navDTris[tbase*4]; + t[0] = 0; + t[1] = (unsigned char)(j-1); + t[2] = (unsigned char)j; + // Bit for each edge that belongs to poly boundary. + t[3] = (1<<2); + if (j == 2) t[3] |= (1<<0); + if (j == nv-1) t[3] |= (1<<4); + tbase++; + } + } + } + + // Store and create BVtree. + if (params->buildBvTree) + { + createBVTree(params, navBvtree, 2*params->polyCount); + } + + // Store Off-Mesh connections. + n = 0; + for (int i = 0; i < params->offMeshConCount; ++i) + { + // Only store connections which start from this tile. + if (offMeshConClass[i*2+0] == 0xff) + { + dtOffMeshConnection* con = &offMeshCons[n]; + con->poly = (unsigned short)(offMeshPolyBase + n); + // Copy connection end-points. + const float* endPts = ¶ms->offMeshConVerts[i*2*3]; + dtVcopy(&con->pos[0], &endPts[0]); + dtVcopy(&con->pos[3], &endPts[3]); + con->rad = params->offMeshConRad[i]; + con->flags = params->offMeshConDir[i] ? DT_OFFMESH_CON_BIDIR : 0; + con->side = offMeshConClass[i*2+1]; + if (params->offMeshConUserID) + con->userId = params->offMeshConUserID[i]; + n++; + } + } + + dtFree(offMeshConClass); + + *outData = data; + *outDataSize = dataSize; + + return true; +} + +bool dtNavMeshHeaderSwapEndian(unsigned char* data, const int /*dataSize*/) +{ + dtMeshHeader* header = (dtMeshHeader*)data; + + int swappedMagic = DT_NAVMESH_MAGIC; + int swappedVersion = DT_NAVMESH_VERSION; + dtSwapEndian(&swappedMagic); + dtSwapEndian(&swappedVersion); + + if ((header->magic != DT_NAVMESH_MAGIC || header->version != DT_NAVMESH_VERSION) && + (header->magic != swappedMagic || header->version != swappedVersion)) + { + return false; + } + + dtSwapEndian(&header->magic); + dtSwapEndian(&header->version); + dtSwapEndian(&header->x); + dtSwapEndian(&header->y); + dtSwapEndian(&header->layer); + dtSwapEndian(&header->userId); + dtSwapEndian(&header->polyCount); + dtSwapEndian(&header->vertCount); + dtSwapEndian(&header->maxLinkCount); + dtSwapEndian(&header->detailMeshCount); + dtSwapEndian(&header->detailVertCount); + dtSwapEndian(&header->detailTriCount); + dtSwapEndian(&header->bvNodeCount); + dtSwapEndian(&header->offMeshConCount); + dtSwapEndian(&header->offMeshBase); + dtSwapEndian(&header->walkableHeight); + dtSwapEndian(&header->walkableRadius); + dtSwapEndian(&header->walkableClimb); + dtSwapEndian(&header->bmin[0]); + dtSwapEndian(&header->bmin[1]); + dtSwapEndian(&header->bmin[2]); + dtSwapEndian(&header->bmax[0]); + dtSwapEndian(&header->bmax[1]); + dtSwapEndian(&header->bmax[2]); + dtSwapEndian(&header->bvQuantFactor); + + // Freelist index and pointers are updated when tile is added, no need to swap. + + return true; +} + +/// @par +/// +/// @warning This function assumes that the header is in the correct endianess already. +/// Call #dtNavMeshHeaderSwapEndian() first on the data if the data is expected to be in wrong endianess +/// to start with. Call #dtNavMeshHeaderSwapEndian() after the data has been swapped if converting from +/// native to foreign endianess. +bool dtNavMeshDataSwapEndian(unsigned char* data, const int /*dataSize*/) +{ + // Make sure the data is in right format. + dtMeshHeader* header = (dtMeshHeader*)data; + if (header->magic != DT_NAVMESH_MAGIC) + return false; + if (header->version != DT_NAVMESH_VERSION) + return false; + + // Patch header pointers. + const int headerSize = dtAlign4(sizeof(dtMeshHeader)); + const int vertsSize = dtAlign4(sizeof(float)*3*header->vertCount); + const int polysSize = dtAlign4(sizeof(dtPoly)*header->polyCount); + const int linksSize = dtAlign4(sizeof(dtLink)*(header->maxLinkCount)); + const int detailMeshesSize = dtAlign4(sizeof(dtPolyDetail)*header->detailMeshCount); + const int detailVertsSize = dtAlign4(sizeof(float)*3*header->detailVertCount); + const int detailTrisSize = dtAlign4(sizeof(unsigned char)*4*header->detailTriCount); + const int bvtreeSize = dtAlign4(sizeof(dtBVNode)*header->bvNodeCount); + const int offMeshLinksSize = dtAlign4(sizeof(dtOffMeshConnection)*header->offMeshConCount); + + unsigned char* d = data + headerSize; + float* verts = dtGetThenAdvanceBufferPointer(d, vertsSize); + dtPoly* polys = dtGetThenAdvanceBufferPointer(d, polysSize); + d += linksSize; // Ignore links; they technically should be endian-swapped but all their data is overwritten on load anyway. + //dtLink* links = dtGetThenAdvanceBufferPointer(d, linksSize); + dtPolyDetail* detailMeshes = dtGetThenAdvanceBufferPointer(d, detailMeshesSize); + float* detailVerts = dtGetThenAdvanceBufferPointer(d, detailVertsSize); + d += detailTrisSize; // Ignore detail tris; single bytes can't be endian-swapped. + //unsigned char* detailTris = dtGetThenAdvanceBufferPointer(d, detailTrisSize); + dtBVNode* bvTree = dtGetThenAdvanceBufferPointer(d, bvtreeSize); + dtOffMeshConnection* offMeshCons = dtGetThenAdvanceBufferPointer(d, offMeshLinksSize); + + // Vertices + for (int i = 0; i < header->vertCount*3; ++i) + { + dtSwapEndian(&verts[i]); + } + + // Polys + for (int i = 0; i < header->polyCount; ++i) + { + dtPoly* p = &polys[i]; + // poly->firstLink is update when tile is added, no need to swap. + for (int j = 0; j < DT_VERTS_PER_POLYGON; ++j) + { + dtSwapEndian(&p->verts[j]); + dtSwapEndian(&p->neis[j]); + } + dtSwapEndian(&p->flags); + } + + // Links are rebuild when tile is added, no need to swap. + + // Detail meshes + for (int i = 0; i < header->detailMeshCount; ++i) + { + dtPolyDetail* pd = &detailMeshes[i]; + dtSwapEndian(&pd->vertBase); + dtSwapEndian(&pd->triBase); + } + + // Detail verts + for (int i = 0; i < header->detailVertCount*3; ++i) + { + dtSwapEndian(&detailVerts[i]); + } + + // BV-tree + for (int i = 0; i < header->bvNodeCount; ++i) + { + dtBVNode* node = &bvTree[i]; + for (int j = 0; j < 3; ++j) + { + dtSwapEndian(&node->bmin[j]); + dtSwapEndian(&node->bmax[j]); + } + dtSwapEndian(&node->i); + } + + // Off-mesh Connections. + for (int i = 0; i < header->offMeshConCount; ++i) + { + dtOffMeshConnection* con = &offMeshCons[i]; + for (int j = 0; j < 6; ++j) + dtSwapEndian(&con->pos[j]); + dtSwapEndian(&con->rad); + dtSwapEndian(&con->poly); + } + + return true; +} diff --git a/external/recast/src/DetourNavMeshQuery.cpp b/external/recast/src/DetourNavMeshQuery.cpp new file mode 100644 index 0000000000..839ee1e815 --- /dev/null +++ b/external/recast/src/DetourNavMeshQuery.cpp @@ -0,0 +1,3663 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#include +#include +#include "DetourNavMeshQuery.h" +#include "DetourNavMesh.h" +#include "DetourNode.h" +#include "DetourCommon.h" +#include "DetourMath.h" +#include "DetourAlloc.h" +#include "DetourAssert.h" +#include + +/// @class dtQueryFilter +/// +/// The Default Implementation +/// +/// At construction: All area costs default to 1.0. All flags are included +/// and none are excluded. +/// +/// If a polygon has both an include and an exclude flag, it will be excluded. +/// +/// The way filtering works, a navigation mesh polygon must have at least one flag +/// set to ever be considered by a query. So a polygon with no flags will never +/// be considered. +/// +/// Setting the include flags to 0 will result in all polygons being excluded. +/// +/// Custom Implementations +/// +/// DT_VIRTUAL_QUERYFILTER must be defined in order to extend this class. +/// +/// Implement a custom query filter by overriding the virtual passFilter() +/// and getCost() functions. If this is done, both functions should be as +/// fast as possible. Use cached local copies of data rather than accessing +/// your own objects where possible. +/// +/// Custom implementations do not need to adhere to the flags or cost logic +/// used by the default implementation. +/// +/// In order for A* searches to work properly, the cost should be proportional to +/// the travel distance. Implementing a cost modifier less than 1.0 is likely +/// to lead to problems during pathfinding. +/// +/// @see dtNavMeshQuery + +dtQueryFilter::dtQueryFilter() : + m_includeFlags(0xffff), + m_excludeFlags(0) +{ + for (int i = 0; i < DT_MAX_AREAS; ++i) + m_areaCost[i] = 1.0f; +} + +#ifdef DT_VIRTUAL_QUERYFILTER +bool dtQueryFilter::passFilter(const dtPolyRef /*ref*/, + const dtMeshTile* /*tile*/, + const dtPoly* poly) const +{ + return (poly->flags & m_includeFlags) != 0 && (poly->flags & m_excludeFlags) == 0; +} + +float dtQueryFilter::getCost(const float* pa, const float* pb, + const dtPolyRef /*prevRef*/, const dtMeshTile* /*prevTile*/, const dtPoly* /*prevPoly*/, + const dtPolyRef /*curRef*/, const dtMeshTile* /*curTile*/, const dtPoly* curPoly, + const dtPolyRef /*nextRef*/, const dtMeshTile* /*nextTile*/, const dtPoly* /*nextPoly*/) const +{ + return dtVdist(pa, pb) * m_areaCost[curPoly->getArea()]; +} +#else +inline bool dtQueryFilter::passFilter(const dtPolyRef /*ref*/, + const dtMeshTile* /*tile*/, + const dtPoly* poly) const +{ + return (poly->flags & m_includeFlags) != 0 && (poly->flags & m_excludeFlags) == 0; +} + +inline float dtQueryFilter::getCost(const float* pa, const float* pb, + const dtPolyRef /*prevRef*/, const dtMeshTile* /*prevTile*/, const dtPoly* /*prevPoly*/, + const dtPolyRef /*curRef*/, const dtMeshTile* /*curTile*/, const dtPoly* curPoly, + const dtPolyRef /*nextRef*/, const dtMeshTile* /*nextTile*/, const dtPoly* /*nextPoly*/) const +{ + return dtVdist(pa, pb) * m_areaCost[curPoly->getArea()]; +} +#endif + +static const float H_SCALE = 0.999f; // Search heuristic scale. + + +dtNavMeshQuery* dtAllocNavMeshQuery() +{ + void* mem = dtAlloc(sizeof(dtNavMeshQuery), DT_ALLOC_PERM); + if (!mem) return 0; + return new(mem) dtNavMeshQuery; +} + +void dtFreeNavMeshQuery(dtNavMeshQuery* navmesh) +{ + if (!navmesh) return; + navmesh->~dtNavMeshQuery(); + dtFree(navmesh); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +/// @class dtNavMeshQuery +/// +/// For methods that support undersized buffers, if the buffer is too small +/// to hold the entire result set the return status of the method will include +/// the #DT_BUFFER_TOO_SMALL flag. +/// +/// Constant member functions can be used by multiple clients without side +/// effects. (E.g. No change to the closed list. No impact on an in-progress +/// sliced path query. Etc.) +/// +/// Walls and portals: A @e wall is a polygon segment that is +/// considered impassable. A @e portal is a passable segment between polygons. +/// A portal may be treated as a wall based on the dtQueryFilter used for a query. +/// +/// @see dtNavMesh, dtQueryFilter, #dtAllocNavMeshQuery(), #dtAllocNavMeshQuery() + +dtNavMeshQuery::dtNavMeshQuery() : + m_nav(0), + m_tinyNodePool(0), + m_nodePool(0), + m_openList(0) +{ + memset(&m_query, 0, sizeof(dtQueryData)); +} + +dtNavMeshQuery::~dtNavMeshQuery() +{ + if (m_tinyNodePool) + m_tinyNodePool->~dtNodePool(); + if (m_nodePool) + m_nodePool->~dtNodePool(); + if (m_openList) + m_openList->~dtNodeQueue(); + dtFree(m_tinyNodePool); + dtFree(m_nodePool); + dtFree(m_openList); +} + +/// @par +/// +/// Must be the first function called after construction, before other +/// functions are used. +/// +/// This function can be used multiple times. +dtStatus dtNavMeshQuery::init(const dtNavMesh* nav, const int maxNodes) +{ + if (maxNodes > DT_NULL_IDX || maxNodes > (1 << DT_NODE_PARENT_BITS) - 1) + return DT_FAILURE | DT_INVALID_PARAM; + + m_nav = nav; + + if (!m_nodePool || m_nodePool->getMaxNodes() < maxNodes) + { + if (m_nodePool) + { + m_nodePool->~dtNodePool(); + dtFree(m_nodePool); + m_nodePool = 0; + } + m_nodePool = new (dtAlloc(sizeof(dtNodePool), DT_ALLOC_PERM)) dtNodePool(maxNodes, dtNextPow2(maxNodes/4)); + if (!m_nodePool) + return DT_FAILURE | DT_OUT_OF_MEMORY; + } + else + { + m_nodePool->clear(); + } + + if (!m_tinyNodePool) + { + m_tinyNodePool = new (dtAlloc(sizeof(dtNodePool), DT_ALLOC_PERM)) dtNodePool(64, 32); + if (!m_tinyNodePool) + return DT_FAILURE | DT_OUT_OF_MEMORY; + } + else + { + m_tinyNodePool->clear(); + } + + if (!m_openList || m_openList->getCapacity() < maxNodes) + { + if (m_openList) + { + m_openList->~dtNodeQueue(); + dtFree(m_openList); + m_openList = 0; + } + m_openList = new (dtAlloc(sizeof(dtNodeQueue), DT_ALLOC_PERM)) dtNodeQueue(maxNodes); + if (!m_openList) + return DT_FAILURE | DT_OUT_OF_MEMORY; + } + else + { + m_openList->clear(); + } + + return DT_SUCCESS; +} + +dtStatus dtNavMeshQuery::findRandomPoint(const dtQueryFilter* filter, float (*frand)(), + dtPolyRef* randomRef, float* randomPt) const +{ + dtAssert(m_nav); + + if (!filter || !frand || !randomRef || !randomPt) + return DT_FAILURE | DT_INVALID_PARAM; + + // Randomly pick one tile. Assume that all tiles cover roughly the same area. + const dtMeshTile* tile = 0; + float tsum = 0.0f; + for (int i = 0; i < m_nav->getMaxTiles(); i++) + { + const dtMeshTile* t = m_nav->getTile(i); + if (!t || !t->header) continue; + + // Choose random tile using reservoi sampling. + const float area = 1.0f; // Could be tile area too. + tsum += area; + const float u = frand(); + if (u*tsum <= area) + tile = t; + } + if (!tile) + return DT_FAILURE; + + // Randomly pick one polygon weighted by polygon area. + const dtPoly* poly = 0; + dtPolyRef polyRef = 0; + const dtPolyRef base = m_nav->getPolyRefBase(tile); + + float areaSum = 0.0f; + for (int i = 0; i < tile->header->polyCount; ++i) + { + const dtPoly* p = &tile->polys[i]; + // Do not return off-mesh connection polygons. + if (p->getType() != DT_POLYTYPE_GROUND) + continue; + // Must pass filter + const dtPolyRef ref = base | (dtPolyRef)i; + if (!filter->passFilter(ref, tile, p)) + continue; + + // Calc area of the polygon. + float polyArea = 0.0f; + for (int j = 2; j < p->vertCount; ++j) + { + const float* va = &tile->verts[p->verts[0]*3]; + const float* vb = &tile->verts[p->verts[j-1]*3]; + const float* vc = &tile->verts[p->verts[j]*3]; + polyArea += dtTriArea2D(va,vb,vc); + } + + // Choose random polygon weighted by area, using reservoi sampling. + areaSum += polyArea; + const float u = frand(); + if (u*areaSum <= polyArea) + { + poly = p; + polyRef = ref; + } + } + + if (!poly) + return DT_FAILURE; + + // Randomly pick point on polygon. + const float* v = &tile->verts[poly->verts[0]*3]; + float verts[3*DT_VERTS_PER_POLYGON]; + float areas[DT_VERTS_PER_POLYGON]; + dtVcopy(&verts[0*3],v); + for (int j = 1; j < poly->vertCount; ++j) + { + v = &tile->verts[poly->verts[j]*3]; + dtVcopy(&verts[j*3],v); + } + + const float s = frand(); + const float t = frand(); + + float pt[3]; + dtRandomPointInConvexPoly(verts, poly->vertCount, areas, s, t, pt); + + float h = 0.0f; + dtStatus status = getPolyHeight(polyRef, pt, &h); + if (dtStatusFailed(status)) + return status; + pt[1] = h; + + dtVcopy(randomPt, pt); + *randomRef = polyRef; + + return DT_SUCCESS; +} + +dtStatus dtNavMeshQuery::findRandomPointAroundCircle(dtPolyRef startRef, const float* centerPos, const float maxRadius, + const dtQueryFilter* filter, float (*frand)(), + dtPolyRef* randomRef, float* randomPt) const +{ + dtAssert(m_nav); + dtAssert(m_nodePool); + dtAssert(m_openList); + + // Validate input + if (!m_nav->isValidPolyRef(startRef) || + !centerPos || !dtVisfinite(centerPos) || + maxRadius < 0 || !dtMathIsfinite(maxRadius) || + !filter || !frand || !randomRef || !randomPt) + { + return DT_FAILURE | DT_INVALID_PARAM; + } + + const dtMeshTile* startTile = 0; + const dtPoly* startPoly = 0; + m_nav->getTileAndPolyByRefUnsafe(startRef, &startTile, &startPoly); + if (!filter->passFilter(startRef, startTile, startPoly)) + return DT_FAILURE | DT_INVALID_PARAM; + + m_nodePool->clear(); + m_openList->clear(); + + dtNode* startNode = m_nodePool->getNode(startRef); + dtVcopy(startNode->pos, centerPos); + startNode->pidx = 0; + startNode->cost = 0; + startNode->total = 0; + startNode->id = startRef; + startNode->flags = DT_NODE_OPEN; + m_openList->push(startNode); + + dtStatus status = DT_SUCCESS; + + const float radiusSqr = dtSqr(maxRadius); + float areaSum = 0.0f; + + const dtMeshTile* randomTile = 0; + const dtPoly* randomPoly = 0; + dtPolyRef randomPolyRef = 0; + + while (!m_openList->empty()) + { + dtNode* bestNode = m_openList->pop(); + bestNode->flags &= ~DT_NODE_OPEN; + bestNode->flags |= DT_NODE_CLOSED; + + // Get poly and tile. + // The API input has been cheked already, skip checking internal data. + const dtPolyRef bestRef = bestNode->id; + const dtMeshTile* bestTile = 0; + const dtPoly* bestPoly = 0; + m_nav->getTileAndPolyByRefUnsafe(bestRef, &bestTile, &bestPoly); + + // Place random locations on on ground. + if (bestPoly->getType() == DT_POLYTYPE_GROUND) + { + // Calc area of the polygon. + float polyArea = 0.0f; + for (int j = 2; j < bestPoly->vertCount; ++j) + { + const float* va = &bestTile->verts[bestPoly->verts[0]*3]; + const float* vb = &bestTile->verts[bestPoly->verts[j-1]*3]; + const float* vc = &bestTile->verts[bestPoly->verts[j]*3]; + polyArea += dtTriArea2D(va,vb,vc); + } + // Choose random polygon weighted by area, using reservoi sampling. + areaSum += polyArea; + const float u = frand(); + if (u*areaSum <= polyArea) + { + randomTile = bestTile; + randomPoly = bestPoly; + randomPolyRef = bestRef; + } + } + + + // Get parent poly and tile. + dtPolyRef parentRef = 0; + const dtMeshTile* parentTile = 0; + const dtPoly* parentPoly = 0; + if (bestNode->pidx) + parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id; + if (parentRef) + m_nav->getTileAndPolyByRefUnsafe(parentRef, &parentTile, &parentPoly); + + for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next) + { + const dtLink* link = &bestTile->links[i]; + dtPolyRef neighbourRef = link->ref; + // Skip invalid neighbours and do not follow back to parent. + if (!neighbourRef || neighbourRef == parentRef) + continue; + + // Expand to neighbour + const dtMeshTile* neighbourTile = 0; + const dtPoly* neighbourPoly = 0; + m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly); + + // Do not advance if the polygon is excluded by the filter. + if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly)) + continue; + + // Find edge and calc distance to the edge. + float va[3], vb[3]; + if (!getPortalPoints(bestRef, bestPoly, bestTile, neighbourRef, neighbourPoly, neighbourTile, va, vb)) + continue; + + // If the circle is not touching the next polygon, skip it. + float tseg; + float distSqr = dtDistancePtSegSqr2D(centerPos, va, vb, tseg); + if (distSqr > radiusSqr) + continue; + + dtNode* neighbourNode = m_nodePool->getNode(neighbourRef); + if (!neighbourNode) + { + status |= DT_OUT_OF_NODES; + continue; + } + + if (neighbourNode->flags & DT_NODE_CLOSED) + continue; + + // Cost + if (neighbourNode->flags == 0) + dtVlerp(neighbourNode->pos, va, vb, 0.5f); + + const float total = bestNode->total + dtVdist(bestNode->pos, neighbourNode->pos); + + // The node is already in open list and the new result is worse, skip. + if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total) + continue; + + neighbourNode->id = neighbourRef; + neighbourNode->flags = (neighbourNode->flags & ~DT_NODE_CLOSED); + neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode); + neighbourNode->total = total; + + if (neighbourNode->flags & DT_NODE_OPEN) + { + m_openList->modify(neighbourNode); + } + else + { + neighbourNode->flags = DT_NODE_OPEN; + m_openList->push(neighbourNode); + } + } + } + + if (!randomPoly) + return DT_FAILURE; + + // Randomly pick point on polygon. + const float* v = &randomTile->verts[randomPoly->verts[0]*3]; + float verts[3*DT_VERTS_PER_POLYGON]; + float areas[DT_VERTS_PER_POLYGON]; + dtVcopy(&verts[0*3],v); + for (int j = 1; j < randomPoly->vertCount; ++j) + { + v = &randomTile->verts[randomPoly->verts[j]*3]; + dtVcopy(&verts[j*3],v); + } + + const float s = frand(); + const float t = frand(); + + float pt[3]; + dtRandomPointInConvexPoly(verts, randomPoly->vertCount, areas, s, t, pt); + + float h = 0.0f; + dtStatus stat = getPolyHeight(randomPolyRef, pt, &h); + if (dtStatusFailed(status)) + return stat; + pt[1] = h; + + dtVcopy(randomPt, pt); + *randomRef = randomPolyRef; + + return DT_SUCCESS; +} + + +////////////////////////////////////////////////////////////////////////////////////////// + +/// @par +/// +/// Uses the detail polygons to find the surface height. (Most accurate.) +/// +/// @p pos does not have to be within the bounds of the polygon or navigation mesh. +/// +/// See closestPointOnPolyBoundary() for a limited but faster option. +/// +dtStatus dtNavMeshQuery::closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const +{ + dtAssert(m_nav); + if (!m_nav->isValidPolyRef(ref) || + !pos || !dtVisfinite(pos) || + !closest) + { + return DT_FAILURE | DT_INVALID_PARAM; + } + + m_nav->closestPointOnPoly(ref, pos, closest, posOverPoly); + return DT_SUCCESS; +} + +/// @par +/// +/// Much faster than closestPointOnPoly(). +/// +/// If the provided position lies within the polygon's xz-bounds (above or below), +/// then @p pos and @p closest will be equal. +/// +/// The height of @p closest will be the polygon boundary. The height detail is not used. +/// +/// @p pos does not have to be within the bounds of the polybon or the navigation mesh. +/// +dtStatus dtNavMeshQuery::closestPointOnPolyBoundary(dtPolyRef ref, const float* pos, float* closest) const +{ + dtAssert(m_nav); + + const dtMeshTile* tile = 0; + const dtPoly* poly = 0; + if (dtStatusFailed(m_nav->getTileAndPolyByRef(ref, &tile, &poly))) + return DT_FAILURE | DT_INVALID_PARAM; + + if (!pos || !dtVisfinite(pos) || !closest) + return DT_FAILURE | DT_INVALID_PARAM; + + // Collect vertices. + float verts[DT_VERTS_PER_POLYGON*3]; + float edged[DT_VERTS_PER_POLYGON]; + float edget[DT_VERTS_PER_POLYGON]; + int nv = 0; + for (int i = 0; i < (int)poly->vertCount; ++i) + { + dtVcopy(&verts[nv*3], &tile->verts[poly->verts[i]*3]); + nv++; + } + + bool inside = dtDistancePtPolyEdgesSqr(pos, verts, nv, edged, edget); + if (inside) + { + // Point is inside the polygon, return the point. + dtVcopy(closest, pos); + } + else + { + // Point is outside the polygon, dtClamp to nearest edge. + float dmin = edged[0]; + int imin = 0; + for (int i = 1; i < nv; ++i) + { + if (edged[i] < dmin) + { + dmin = edged[i]; + imin = i; + } + } + const float* va = &verts[imin*3]; + const float* vb = &verts[((imin+1)%nv)*3]; + dtVlerp(closest, va, vb, edget[imin]); + } + + return DT_SUCCESS; +} + +/// @par +/// +/// Will return #DT_FAILURE | DT_INVALID_PARAM if the provided position is outside the xz-bounds +/// of the polygon. +/// +dtStatus dtNavMeshQuery::getPolyHeight(dtPolyRef ref, const float* pos, float* height) const +{ + dtAssert(m_nav); + + const dtMeshTile* tile = 0; + const dtPoly* poly = 0; + if (dtStatusFailed(m_nav->getTileAndPolyByRef(ref, &tile, &poly))) + return DT_FAILURE | DT_INVALID_PARAM; + + if (!pos || !dtVisfinite2D(pos)) + return DT_FAILURE | DT_INVALID_PARAM; + + // We used to return success for offmesh connections, but the + // getPolyHeight in DetourNavMesh does not do this, so special + // case it here. + if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) + { + const float* v0 = &tile->verts[poly->verts[0]*3]; + const float* v1 = &tile->verts[poly->verts[1]*3]; + float t; + dtDistancePtSegSqr2D(pos, v0, v1, t); + if (height) + *height = v0[1] + (v1[1] - v0[1])*t; + + return DT_SUCCESS; + } + + return m_nav->getPolyHeight(tile, poly, pos, height) + ? DT_SUCCESS + : DT_FAILURE | DT_INVALID_PARAM; +} + +class dtFindNearestPolyQuery : public dtPolyQuery +{ + const dtNavMeshQuery* m_query; + const float* m_center; + float m_nearestDistanceSqr; + dtPolyRef m_nearestRef; + float m_nearestPoint[3]; + +public: + dtFindNearestPolyQuery(const dtNavMeshQuery* query, const float* center) + : m_query(query), m_center(center), m_nearestDistanceSqr(FLT_MAX), m_nearestRef(0), m_nearestPoint() + { + } + + dtPolyRef nearestRef() const { return m_nearestRef; } + const float* nearestPoint() const { return m_nearestPoint; } + + void process(const dtMeshTile* tile, dtPoly** polys, dtPolyRef* refs, int count) + { + dtIgnoreUnused(polys); + + for (int i = 0; i < count; ++i) + { + dtPolyRef ref = refs[i]; + float closestPtPoly[3]; + float diff[3]; + bool posOverPoly = false; + float d; + m_query->closestPointOnPoly(ref, m_center, closestPtPoly, &posOverPoly); + + // If a point is directly over a polygon and closer than + // climb height, favor that instead of straight line nearest point. + dtVsub(diff, m_center, closestPtPoly); + if (posOverPoly) + { + d = dtAbs(diff[1]) - tile->header->walkableClimb; + d = d > 0 ? d*d : 0; + } + else + { + d = dtVlenSqr(diff); + } + + if (d < m_nearestDistanceSqr) + { + dtVcopy(m_nearestPoint, closestPtPoly); + + m_nearestDistanceSqr = d; + m_nearestRef = ref; + } + } + } +}; + +/// @par +/// +/// @note If the search box does not intersect any polygons the search will +/// return #DT_SUCCESS, but @p nearestRef will be zero. So if in doubt, check +/// @p nearestRef before using @p nearestPt. +/// +dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* halfExtents, + const dtQueryFilter* filter, + dtPolyRef* nearestRef, float* nearestPt) const +{ + dtAssert(m_nav); + + if (!nearestRef) + return DT_FAILURE | DT_INVALID_PARAM; + + // queryPolygons below will check rest of params + + dtFindNearestPolyQuery query(this, center); + + dtStatus status = queryPolygons(center, halfExtents, filter, &query); + if (dtStatusFailed(status)) + return status; + + *nearestRef = query.nearestRef(); + // Only override nearestPt if we actually found a poly so the nearest point + // is valid. + if (nearestPt && *nearestRef) + dtVcopy(nearestPt, query.nearestPoint()); + + return DT_SUCCESS; +} + +void dtNavMeshQuery::queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax, + const dtQueryFilter* filter, dtPolyQuery* query) const +{ + dtAssert(m_nav); + static const int batchSize = 32; + dtPolyRef polyRefs[batchSize]; + dtPoly* polys[batchSize]; + int n = 0; + + if (tile->bvTree) + { + const dtBVNode* node = &tile->bvTree[0]; + const dtBVNode* end = &tile->bvTree[tile->header->bvNodeCount]; + const float* tbmin = tile->header->bmin; + const float* tbmax = tile->header->bmax; + const float qfac = tile->header->bvQuantFactor; + + // Calculate quantized box + unsigned short bmin[3], bmax[3]; + // dtClamp query box to world box. + float minx = dtClamp(qmin[0], tbmin[0], tbmax[0]) - tbmin[0]; + float miny = dtClamp(qmin[1], tbmin[1], tbmax[1]) - tbmin[1]; + float minz = dtClamp(qmin[2], tbmin[2], tbmax[2]) - tbmin[2]; + float maxx = dtClamp(qmax[0], tbmin[0], tbmax[0]) - tbmin[0]; + float maxy = dtClamp(qmax[1], tbmin[1], tbmax[1]) - tbmin[1]; + float maxz = dtClamp(qmax[2], tbmin[2], tbmax[2]) - tbmin[2]; + // Quantize + bmin[0] = (unsigned short)(qfac * minx) & 0xfffe; + bmin[1] = (unsigned short)(qfac * miny) & 0xfffe; + bmin[2] = (unsigned short)(qfac * minz) & 0xfffe; + bmax[0] = (unsigned short)(qfac * maxx + 1) | 1; + bmax[1] = (unsigned short)(qfac * maxy + 1) | 1; + bmax[2] = (unsigned short)(qfac * maxz + 1) | 1; + + // Traverse tree + const dtPolyRef base = m_nav->getPolyRefBase(tile); + while (node < end) + { + const bool overlap = dtOverlapQuantBounds(bmin, bmax, node->bmin, node->bmax); + const bool isLeafNode = node->i >= 0; + + if (isLeafNode && overlap) + { + dtPolyRef ref = base | (dtPolyRef)node->i; + if (filter->passFilter(ref, tile, &tile->polys[node->i])) + { + polyRefs[n] = ref; + polys[n] = &tile->polys[node->i]; + + if (n == batchSize - 1) + { + query->process(tile, polys, polyRefs, batchSize); + n = 0; + } + else + { + n++; + } + } + } + + if (overlap || isLeafNode) + node++; + else + { + const int escapeIndex = -node->i; + node += escapeIndex; + } + } + } + else + { + float bmin[3], bmax[3]; + const dtPolyRef base = m_nav->getPolyRefBase(tile); + for (int i = 0; i < tile->header->polyCount; ++i) + { + dtPoly* p = &tile->polys[i]; + // Do not return off-mesh connection polygons. + if (p->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) + continue; + // Must pass filter + const dtPolyRef ref = base | (dtPolyRef)i; + if (!filter->passFilter(ref, tile, p)) + continue; + // Calc polygon bounds. + const float* v = &tile->verts[p->verts[0]*3]; + dtVcopy(bmin, v); + dtVcopy(bmax, v); + for (int j = 1; j < p->vertCount; ++j) + { + v = &tile->verts[p->verts[j]*3]; + dtVmin(bmin, v); + dtVmax(bmax, v); + } + if (dtOverlapBounds(qmin, qmax, bmin, bmax)) + { + polyRefs[n] = ref; + polys[n] = p; + + if (n == batchSize - 1) + { + query->process(tile, polys, polyRefs, batchSize); + n = 0; + } + else + { + n++; + } + } + } + } + + // Process the last polygons that didn't make a full batch. + if (n > 0) + query->process(tile, polys, polyRefs, n); +} + +class dtCollectPolysQuery : public dtPolyQuery +{ + dtPolyRef* m_polys; + const int m_maxPolys; + int m_numCollected; + bool m_overflow; + +public: + dtCollectPolysQuery(dtPolyRef* polys, const int maxPolys) + : m_polys(polys), m_maxPolys(maxPolys), m_numCollected(0), m_overflow(false) + { + } + + int numCollected() const { return m_numCollected; } + bool overflowed() const { return m_overflow; } + + void process(const dtMeshTile* tile, dtPoly** polys, dtPolyRef* refs, int count) + { + dtIgnoreUnused(tile); + dtIgnoreUnused(polys); + + int numLeft = m_maxPolys - m_numCollected; + int toCopy = count; + if (toCopy > numLeft) + { + m_overflow = true; + toCopy = numLeft; + } + + memcpy(m_polys + m_numCollected, refs, (size_t)toCopy * sizeof(dtPolyRef)); + m_numCollected += toCopy; + } +}; + +/// @par +/// +/// If no polygons are found, the function will return #DT_SUCCESS with a +/// @p polyCount of zero. +/// +/// If @p polys is too small to hold the entire result set, then the array will +/// be filled to capacity. The method of choosing which polygons from the +/// full set are included in the partial result set is undefined. +/// +dtStatus dtNavMeshQuery::queryPolygons(const float* center, const float* halfExtents, + const dtQueryFilter* filter, + dtPolyRef* polys, int* polyCount, const int maxPolys) const +{ + if (!polys || !polyCount || maxPolys < 0) + return DT_FAILURE | DT_INVALID_PARAM; + + dtCollectPolysQuery collector(polys, maxPolys); + + dtStatus status = queryPolygons(center, halfExtents, filter, &collector); + if (dtStatusFailed(status)) + return status; + + *polyCount = collector.numCollected(); + return collector.overflowed() ? DT_SUCCESS | DT_BUFFER_TOO_SMALL : DT_SUCCESS; +} + +/// @par +/// +/// The query will be invoked with batches of polygons. Polygons passed +/// to the query have bounding boxes that overlap with the center and halfExtents +/// passed to this function. The dtPolyQuery::process function is invoked multiple +/// times until all overlapping polygons have been processed. +/// +dtStatus dtNavMeshQuery::queryPolygons(const float* center, const float* halfExtents, + const dtQueryFilter* filter, dtPolyQuery* query) const +{ + dtAssert(m_nav); + + if (!center || !dtVisfinite(center) || + !halfExtents || !dtVisfinite(halfExtents) || + !filter || !query) + { + return DT_FAILURE | DT_INVALID_PARAM; + } + + float bmin[3], bmax[3]; + dtVsub(bmin, center, halfExtents); + dtVadd(bmax, center, halfExtents); + + // Find tiles the query touches. + int minx, miny, maxx, maxy; + m_nav->calcTileLoc(bmin, &minx, &miny); + m_nav->calcTileLoc(bmax, &maxx, &maxy); + + static const int MAX_NEIS = 32; + const dtMeshTile* neis[MAX_NEIS]; + + for (int y = miny; y <= maxy; ++y) + { + for (int x = minx; x <= maxx; ++x) + { + const int nneis = m_nav->getTilesAt(x,y,neis,MAX_NEIS); + for (int j = 0; j < nneis; ++j) + { + queryPolygonsInTile(neis[j], bmin, bmax, filter, query); + } + } + } + + return DT_SUCCESS; +} + +/// @par +/// +/// If the end polygon cannot be reached through the navigation graph, +/// the last polygon in the path will be the nearest the end polygon. +/// +/// If the path array is to small to hold the full result, it will be filled as +/// far as possible from the start polygon toward the end polygon. +/// +/// The start and end positions are used to calculate traversal costs. +/// (The y-values impact the result.) +/// +dtStatus dtNavMeshQuery::findPath(dtPolyRef startRef, dtPolyRef endRef, + const float* startPos, const float* endPos, + const dtQueryFilter* filter, + dtPolyRef* path, int* pathCount, const int maxPath) const +{ + dtAssert(m_nav); + dtAssert(m_nodePool); + dtAssert(m_openList); + + if (!pathCount) + return DT_FAILURE | DT_INVALID_PARAM; + + *pathCount = 0; + + // Validate input + if (!m_nav->isValidPolyRef(startRef) || !m_nav->isValidPolyRef(endRef) || + !startPos || !dtVisfinite(startPos) || + !endPos || !dtVisfinite(endPos) || + !filter || !path || maxPath <= 0) + { + return DT_FAILURE | DT_INVALID_PARAM; + } + + if (startRef == endRef) + { + path[0] = startRef; + *pathCount = 1; + return DT_SUCCESS; + } + + m_nodePool->clear(); + m_openList->clear(); + + dtNode* startNode = m_nodePool->getNode(startRef); + dtVcopy(startNode->pos, startPos); + startNode->pidx = 0; + startNode->cost = 0; + startNode->total = dtVdist(startPos, endPos) * H_SCALE; + startNode->id = startRef; + startNode->flags = DT_NODE_OPEN; + m_openList->push(startNode); + + dtNode* lastBestNode = startNode; + float lastBestNodeCost = startNode->total; + + bool outOfNodes = false; + + while (!m_openList->empty()) + { + // Remove node from open list and put it in closed list. + dtNode* bestNode = m_openList->pop(); + bestNode->flags &= ~DT_NODE_OPEN; + bestNode->flags |= DT_NODE_CLOSED; + + // Reached the goal, stop searching. + if (bestNode->id == endRef) + { + lastBestNode = bestNode; + break; + } + + // Get current poly and tile. + // The API input has been cheked already, skip checking internal data. + const dtPolyRef bestRef = bestNode->id; + const dtMeshTile* bestTile = 0; + const dtPoly* bestPoly = 0; + m_nav->getTileAndPolyByRefUnsafe(bestRef, &bestTile, &bestPoly); + + // Get parent poly and tile. + dtPolyRef parentRef = 0; + const dtMeshTile* parentTile = 0; + const dtPoly* parentPoly = 0; + if (bestNode->pidx) + parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id; + if (parentRef) + m_nav->getTileAndPolyByRefUnsafe(parentRef, &parentTile, &parentPoly); + + for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next) + { + dtPolyRef neighbourRef = bestTile->links[i].ref; + + // Skip invalid ids and do not expand back to where we came from. + if (!neighbourRef || neighbourRef == parentRef) + continue; + + // Get neighbour poly and tile. + // The API input has been cheked already, skip checking internal data. + const dtMeshTile* neighbourTile = 0; + const dtPoly* neighbourPoly = 0; + m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly); + + if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly)) + continue; + + // deal explicitly with crossing tile boundaries + unsigned char crossSide = 0; + if (bestTile->links[i].side != 0xff) + crossSide = bestTile->links[i].side >> 1; + + // get the node + dtNode* neighbourNode = m_nodePool->getNode(neighbourRef, crossSide); + if (!neighbourNode) + { + outOfNodes = true; + continue; + } + + // If the node is visited the first time, calculate node position. + if (neighbourNode->flags == 0) + { + getEdgeMidPoint(bestRef, bestPoly, bestTile, + neighbourRef, neighbourPoly, neighbourTile, + neighbourNode->pos); + } + + // Calculate cost and heuristic. + float cost = 0; + float heuristic = 0; + + // Special case for last node. + if (neighbourRef == endRef) + { + // Cost + const float curCost = filter->getCost(bestNode->pos, neighbourNode->pos, + parentRef, parentTile, parentPoly, + bestRef, bestTile, bestPoly, + neighbourRef, neighbourTile, neighbourPoly); + const float endCost = filter->getCost(neighbourNode->pos, endPos, + bestRef, bestTile, bestPoly, + neighbourRef, neighbourTile, neighbourPoly, + 0, 0, 0); + + cost = bestNode->cost + curCost + endCost; + heuristic = 0; + } + else + { + // Cost + const float curCost = filter->getCost(bestNode->pos, neighbourNode->pos, + parentRef, parentTile, parentPoly, + bestRef, bestTile, bestPoly, + neighbourRef, neighbourTile, neighbourPoly); + cost = bestNode->cost + curCost; + heuristic = dtVdist(neighbourNode->pos, endPos)*H_SCALE; + } + + const float total = cost + heuristic; + + // The node is already in open list and the new result is worse, skip. + if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total) + continue; + // The node is already visited and process, and the new result is worse, skip. + if ((neighbourNode->flags & DT_NODE_CLOSED) && total >= neighbourNode->total) + continue; + + // Add or update the node. + neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode); + neighbourNode->id = neighbourRef; + neighbourNode->flags = (neighbourNode->flags & ~DT_NODE_CLOSED); + neighbourNode->cost = cost; + neighbourNode->total = total; + + if (neighbourNode->flags & DT_NODE_OPEN) + { + // Already in open, update node location. + m_openList->modify(neighbourNode); + } + else + { + // Put the node in open list. + neighbourNode->flags |= DT_NODE_OPEN; + m_openList->push(neighbourNode); + } + + // Update nearest node to target so far. + if (heuristic < lastBestNodeCost) + { + lastBestNodeCost = heuristic; + lastBestNode = neighbourNode; + } + } + } + + dtStatus status = getPathToNode(lastBestNode, path, pathCount, maxPath); + + if (lastBestNode->id != endRef) + status |= DT_PARTIAL_RESULT; + + if (outOfNodes) + status |= DT_OUT_OF_NODES; + + return status; +} + +dtStatus dtNavMeshQuery::getPathToNode(dtNode* endNode, dtPolyRef* path, int* pathCount, int maxPath) const +{ + // Find the length of the entire path. + dtNode* curNode = endNode; + int length = 0; + do + { + length++; + curNode = m_nodePool->getNodeAtIdx(curNode->pidx); + } while (curNode); + + // If the path cannot be fully stored then advance to the last node we will be able to store. + curNode = endNode; + int writeCount; + for (writeCount = length; writeCount > maxPath; writeCount--) + { + dtAssert(curNode); + + curNode = m_nodePool->getNodeAtIdx(curNode->pidx); + } + + // Write path + for (int i = writeCount - 1; i >= 0; i--) + { + dtAssert(curNode); + + path[i] = curNode->id; + curNode = m_nodePool->getNodeAtIdx(curNode->pidx); + } + + dtAssert(!curNode); + + *pathCount = dtMin(length, maxPath); + + if (length > maxPath) + return DT_SUCCESS | DT_BUFFER_TOO_SMALL; + + return DT_SUCCESS; +} + + +/// @par +/// +/// @warning Calling any non-slice methods before calling finalizeSlicedFindPath() +/// or finalizeSlicedFindPathPartial() may result in corrupted data! +/// +/// The @p filter pointer is stored and used for the duration of the sliced +/// path query. +/// +dtStatus dtNavMeshQuery::initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef, + const float* startPos, const float* endPos, + const dtQueryFilter* filter, const unsigned int options) +{ + dtAssert(m_nav); + dtAssert(m_nodePool); + dtAssert(m_openList); + + // Init path state. + memset(&m_query, 0, sizeof(dtQueryData)); + m_query.status = DT_FAILURE; + m_query.startRef = startRef; + m_query.endRef = endRef; + if (startPos) + dtVcopy(m_query.startPos, startPos); + if (endPos) + dtVcopy(m_query.endPos, endPos); + m_query.filter = filter; + m_query.options = options; + m_query.raycastLimitSqr = FLT_MAX; + + // Validate input + if (!m_nav->isValidPolyRef(startRef) || !m_nav->isValidPolyRef(endRef) || + !startPos || !dtVisfinite(startPos) || + !endPos || !dtVisfinite(endPos) || !filter) + { + return DT_FAILURE | DT_INVALID_PARAM; + } + + // trade quality with performance? + if (options & DT_FINDPATH_ANY_ANGLE) + { + // limiting to several times the character radius yields nice results. It is not sensitive + // so it is enough to compute it from the first tile. + const dtMeshTile* tile = m_nav->getTileByRef(startRef); + float agentRadius = tile->header->walkableRadius; + m_query.raycastLimitSqr = dtSqr(agentRadius * DT_RAY_CAST_LIMIT_PROPORTIONS); + } + + if (startRef == endRef) + { + m_query.status = DT_SUCCESS; + return DT_SUCCESS; + } + + m_nodePool->clear(); + m_openList->clear(); + + dtNode* startNode = m_nodePool->getNode(startRef); + dtVcopy(startNode->pos, startPos); + startNode->pidx = 0; + startNode->cost = 0; + startNode->total = dtVdist(startPos, endPos) * H_SCALE; + startNode->id = startRef; + startNode->flags = DT_NODE_OPEN; + m_openList->push(startNode); + + m_query.status = DT_IN_PROGRESS; + m_query.lastBestNode = startNode; + m_query.lastBestNodeCost = startNode->total; + + return m_query.status; +} + +dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter, int* doneIters) +{ + if (!dtStatusInProgress(m_query.status)) + return m_query.status; + + // Make sure the request is still valid. + if (!m_nav->isValidPolyRef(m_query.startRef) || !m_nav->isValidPolyRef(m_query.endRef)) + { + m_query.status = DT_FAILURE; + return DT_FAILURE; + } + + dtRaycastHit rayHit; + rayHit.maxPath = 0; + + int iter = 0; + while (iter < maxIter && !m_openList->empty()) + { + iter++; + + // Remove node from open list and put it in closed list. + dtNode* bestNode = m_openList->pop(); + bestNode->flags &= ~DT_NODE_OPEN; + bestNode->flags |= DT_NODE_CLOSED; + + // Reached the goal, stop searching. + if (bestNode->id == m_query.endRef) + { + m_query.lastBestNode = bestNode; + const dtStatus details = m_query.status & DT_STATUS_DETAIL_MASK; + m_query.status = DT_SUCCESS | details; + if (doneIters) + *doneIters = iter; + return m_query.status; + } + + // Get current poly and tile. + // The API input has been cheked already, skip checking internal data. + const dtPolyRef bestRef = bestNode->id; + const dtMeshTile* bestTile = 0; + const dtPoly* bestPoly = 0; + if (dtStatusFailed(m_nav->getTileAndPolyByRef(bestRef, &bestTile, &bestPoly))) + { + // The polygon has disappeared during the sliced query, fail. + m_query.status = DT_FAILURE; + if (doneIters) + *doneIters = iter; + return m_query.status; + } + + // Get parent and grand parent poly and tile. + dtPolyRef parentRef = 0, grandpaRef = 0; + const dtMeshTile* parentTile = 0; + const dtPoly* parentPoly = 0; + dtNode* parentNode = 0; + if (bestNode->pidx) + { + parentNode = m_nodePool->getNodeAtIdx(bestNode->pidx); + parentRef = parentNode->id; + if (parentNode->pidx) + grandpaRef = m_nodePool->getNodeAtIdx(parentNode->pidx)->id; + } + if (parentRef) + { + bool invalidParent = dtStatusFailed(m_nav->getTileAndPolyByRef(parentRef, &parentTile, &parentPoly)); + if (invalidParent || (grandpaRef && !m_nav->isValidPolyRef(grandpaRef)) ) + { + // The polygon has disappeared during the sliced query, fail. + m_query.status = DT_FAILURE; + if (doneIters) + *doneIters = iter; + return m_query.status; + } + } + + // decide whether to test raycast to previous nodes + bool tryLOS = false; + if (m_query.options & DT_FINDPATH_ANY_ANGLE) + { + if ((parentRef != 0) && (dtVdistSqr(parentNode->pos, bestNode->pos) < m_query.raycastLimitSqr)) + tryLOS = true; + } + + for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next) + { + dtPolyRef neighbourRef = bestTile->links[i].ref; + + // Skip invalid ids and do not expand back to where we came from. + if (!neighbourRef || neighbourRef == parentRef) + continue; + + // Get neighbour poly and tile. + // The API input has been cheked already, skip checking internal data. + const dtMeshTile* neighbourTile = 0; + const dtPoly* neighbourPoly = 0; + m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly); + + if (!m_query.filter->passFilter(neighbourRef, neighbourTile, neighbourPoly)) + continue; + + // get the neighbor node + dtNode* neighbourNode = m_nodePool->getNode(neighbourRef, 0); + if (!neighbourNode) + { + m_query.status |= DT_OUT_OF_NODES; + continue; + } + + // do not expand to nodes that were already visited from the same parent + if (neighbourNode->pidx != 0 && neighbourNode->pidx == bestNode->pidx) + continue; + + // If the node is visited the first time, calculate node position. + if (neighbourNode->flags == 0) + { + getEdgeMidPoint(bestRef, bestPoly, bestTile, + neighbourRef, neighbourPoly, neighbourTile, + neighbourNode->pos); + } + + // Calculate cost and heuristic. + float cost = 0; + float heuristic = 0; + + // raycast parent + bool foundShortCut = false; + rayHit.pathCost = rayHit.t = 0; + if (tryLOS) + { + raycast(parentRef, parentNode->pos, neighbourNode->pos, m_query.filter, DT_RAYCAST_USE_COSTS, &rayHit, grandpaRef); + foundShortCut = rayHit.t >= 1.0f; + } + + // update move cost + if (foundShortCut) + { + // shortcut found using raycast. Using shorter cost instead + cost = parentNode->cost + rayHit.pathCost; + } + else + { + // No shortcut found. + const float curCost = m_query.filter->getCost(bestNode->pos, neighbourNode->pos, + parentRef, parentTile, parentPoly, + bestRef, bestTile, bestPoly, + neighbourRef, neighbourTile, neighbourPoly); + cost = bestNode->cost + curCost; + } + + // Special case for last node. + if (neighbourRef == m_query.endRef) + { + const float endCost = m_query.filter->getCost(neighbourNode->pos, m_query.endPos, + bestRef, bestTile, bestPoly, + neighbourRef, neighbourTile, neighbourPoly, + 0, 0, 0); + + cost = cost + endCost; + heuristic = 0; + } + else + { + heuristic = dtVdist(neighbourNode->pos, m_query.endPos)*H_SCALE; + } + + const float total = cost + heuristic; + + // The node is already in open list and the new result is worse, skip. + if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total) + continue; + // The node is already visited and process, and the new result is worse, skip. + if ((neighbourNode->flags & DT_NODE_CLOSED) && total >= neighbourNode->total) + continue; + + // Add or update the node. + neighbourNode->pidx = foundShortCut ? bestNode->pidx : m_nodePool->getNodeIdx(bestNode); + neighbourNode->id = neighbourRef; + neighbourNode->flags = (neighbourNode->flags & ~(DT_NODE_CLOSED | DT_NODE_PARENT_DETACHED)); + neighbourNode->cost = cost; + neighbourNode->total = total; + if (foundShortCut) + neighbourNode->flags = (neighbourNode->flags | DT_NODE_PARENT_DETACHED); + + if (neighbourNode->flags & DT_NODE_OPEN) + { + // Already in open, update node location. + m_openList->modify(neighbourNode); + } + else + { + // Put the node in open list. + neighbourNode->flags |= DT_NODE_OPEN; + m_openList->push(neighbourNode); + } + + // Update nearest node to target so far. + if (heuristic < m_query.lastBestNodeCost) + { + m_query.lastBestNodeCost = heuristic; + m_query.lastBestNode = neighbourNode; + } + } + } + + // Exhausted all nodes, but could not find path. + if (m_openList->empty()) + { + const dtStatus details = m_query.status & DT_STATUS_DETAIL_MASK; + m_query.status = DT_SUCCESS | details; + } + + if (doneIters) + *doneIters = iter; + + return m_query.status; +} + +dtStatus dtNavMeshQuery::finalizeSlicedFindPath(dtPolyRef* path, int* pathCount, const int maxPath) +{ + if (!pathCount) + return DT_FAILURE | DT_INVALID_PARAM; + + *pathCount = 0; + + if (!path || maxPath <= 0) + return DT_FAILURE | DT_INVALID_PARAM; + + if (dtStatusFailed(m_query.status)) + { + // Reset query. + memset(&m_query, 0, sizeof(dtQueryData)); + return DT_FAILURE; + } + + int n = 0; + + if (m_query.startRef == m_query.endRef) + { + // Special case: the search starts and ends at same poly. + path[n++] = m_query.startRef; + } + else + { + // Reverse the path. + dtAssert(m_query.lastBestNode); + + if (m_query.lastBestNode->id != m_query.endRef) + m_query.status |= DT_PARTIAL_RESULT; + + dtNode* prev = 0; + dtNode* node = m_query.lastBestNode; + int prevRay = 0; + do + { + dtNode* next = m_nodePool->getNodeAtIdx(node->pidx); + node->pidx = m_nodePool->getNodeIdx(prev); + prev = node; + int nextRay = node->flags & DT_NODE_PARENT_DETACHED; // keep track of whether parent is not adjacent (i.e. due to raycast shortcut) + node->flags = (node->flags & ~DT_NODE_PARENT_DETACHED) | prevRay; // and store it in the reversed path's node + prevRay = nextRay; + node = next; + } + while (node); + + // Store path + node = prev; + do + { + dtNode* next = m_nodePool->getNodeAtIdx(node->pidx); + dtStatus status = 0; + if (node->flags & DT_NODE_PARENT_DETACHED) + { + float t, normal[3]; + int m; + status = raycast(node->id, node->pos, next->pos, m_query.filter, &t, normal, path+n, &m, maxPath-n); + n += m; + // raycast ends on poly boundary and the path might include the next poly boundary. + if (path[n-1] == next->id) + n--; // remove to avoid duplicates + } + else + { + path[n++] = node->id; + if (n >= maxPath) + status = DT_BUFFER_TOO_SMALL; + } + + if (status & DT_STATUS_DETAIL_MASK) + { + m_query.status |= status & DT_STATUS_DETAIL_MASK; + break; + } + node = next; + } + while (node); + } + + const dtStatus details = m_query.status & DT_STATUS_DETAIL_MASK; + + // Reset query. + memset(&m_query, 0, sizeof(dtQueryData)); + + *pathCount = n; + + return DT_SUCCESS | details; +} + +dtStatus dtNavMeshQuery::finalizeSlicedFindPathPartial(const dtPolyRef* existing, const int existingSize, + dtPolyRef* path, int* pathCount, const int maxPath) +{ + if (!pathCount) + return DT_FAILURE | DT_INVALID_PARAM; + + *pathCount = 0; + + if (!existing || existingSize <= 0 || !path || !pathCount || maxPath <= 0) + return DT_FAILURE | DT_INVALID_PARAM; + + if (dtStatusFailed(m_query.status)) + { + // Reset query. + memset(&m_query, 0, sizeof(dtQueryData)); + return DT_FAILURE; + } + + int n = 0; + + if (m_query.startRef == m_query.endRef) + { + // Special case: the search starts and ends at same poly. + path[n++] = m_query.startRef; + } + else + { + // Find furthest existing node that was visited. + dtNode* prev = 0; + dtNode* node = 0; + for (int i = existingSize-1; i >= 0; --i) + { + m_nodePool->findNodes(existing[i], &node, 1); + if (node) + break; + } + + if (!node) + { + m_query.status |= DT_PARTIAL_RESULT; + dtAssert(m_query.lastBestNode); + node = m_query.lastBestNode; + } + + // Reverse the path. + int prevRay = 0; + do + { + dtNode* next = m_nodePool->getNodeAtIdx(node->pidx); + node->pidx = m_nodePool->getNodeIdx(prev); + prev = node; + int nextRay = node->flags & DT_NODE_PARENT_DETACHED; // keep track of whether parent is not adjacent (i.e. due to raycast shortcut) + node->flags = (node->flags & ~DT_NODE_PARENT_DETACHED) | prevRay; // and store it in the reversed path's node + prevRay = nextRay; + node = next; + } + while (node); + + // Store path + node = prev; + do + { + dtNode* next = m_nodePool->getNodeAtIdx(node->pidx); + dtStatus status = 0; + if (node->flags & DT_NODE_PARENT_DETACHED) + { + float t, normal[3]; + int m; + status = raycast(node->id, node->pos, next->pos, m_query.filter, &t, normal, path+n, &m, maxPath-n); + n += m; + // raycast ends on poly boundary and the path might include the next poly boundary. + if (path[n-1] == next->id) + n--; // remove to avoid duplicates + } + else + { + path[n++] = node->id; + if (n >= maxPath) + status = DT_BUFFER_TOO_SMALL; + } + + if (status & DT_STATUS_DETAIL_MASK) + { + m_query.status |= status & DT_STATUS_DETAIL_MASK; + break; + } + node = next; + } + while (node); + } + + const dtStatus details = m_query.status & DT_STATUS_DETAIL_MASK; + + // Reset query. + memset(&m_query, 0, sizeof(dtQueryData)); + + *pathCount = n; + + return DT_SUCCESS | details; +} + + +dtStatus dtNavMeshQuery::appendVertex(const float* pos, const unsigned char flags, const dtPolyRef ref, + float* straightPath, unsigned char* straightPathFlags, dtPolyRef* straightPathRefs, + int* straightPathCount, const int maxStraightPath) const +{ + if ((*straightPathCount) > 0 && dtVequal(&straightPath[((*straightPathCount)-1)*3], pos)) + { + // The vertices are equal, update flags and poly. + if (straightPathFlags) + straightPathFlags[(*straightPathCount)-1] = flags; + if (straightPathRefs) + straightPathRefs[(*straightPathCount)-1] = ref; + } + else + { + // Append new vertex. + dtVcopy(&straightPath[(*straightPathCount)*3], pos); + if (straightPathFlags) + straightPathFlags[(*straightPathCount)] = flags; + if (straightPathRefs) + straightPathRefs[(*straightPathCount)] = ref; + (*straightPathCount)++; + + // If there is no space to append more vertices, return. + if ((*straightPathCount) >= maxStraightPath) + { + return DT_SUCCESS | DT_BUFFER_TOO_SMALL; + } + + // If reached end of path, return. + if (flags == DT_STRAIGHTPATH_END) + { + return DT_SUCCESS; + } + } + return DT_IN_PROGRESS; +} + +dtStatus dtNavMeshQuery::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 +{ + const float* startPos = &straightPath[(*straightPathCount-1)*3]; + // Append or update last vertex + dtStatus stat = 0; + for (int i = startIdx; i < endIdx; i++) + { + // Calculate portal + const dtPolyRef from = path[i]; + const dtMeshTile* fromTile = 0; + const dtPoly* fromPoly = 0; + if (dtStatusFailed(m_nav->getTileAndPolyByRef(from, &fromTile, &fromPoly))) + return DT_FAILURE | DT_INVALID_PARAM; + + const dtPolyRef to = path[i+1]; + const dtMeshTile* toTile = 0; + const dtPoly* toPoly = 0; + if (dtStatusFailed(m_nav->getTileAndPolyByRef(to, &toTile, &toPoly))) + return DT_FAILURE | DT_INVALID_PARAM; + + float left[3], right[3]; + if (dtStatusFailed(getPortalPoints(from, fromPoly, fromTile, to, toPoly, toTile, left, right))) + break; + + if (options & DT_STRAIGHTPATH_AREA_CROSSINGS) + { + // Skip intersection if only area crossings are requested. + if (fromPoly->getArea() == toPoly->getArea()) + continue; + } + + // Append intersection + float s,t; + if (dtIntersectSegSeg2D(startPos, endPos, left, right, s, t)) + { + float pt[3]; + dtVlerp(pt, left,right, t); + + stat = appendVertex(pt, 0, path[i+1], + straightPath, straightPathFlags, straightPathRefs, + straightPathCount, maxStraightPath); + if (stat != DT_IN_PROGRESS) + return stat; + } + } + return DT_IN_PROGRESS; +} + +/// @par +/// +/// This method peforms what is often called 'string pulling'. +/// +/// The start position is clamped to the first polygon in the path, and the +/// end position is clamped to the last. So the start and end positions should +/// normally be within or very near the first and last polygons respectively. +/// +/// The returned polygon references represent the reference id of the polygon +/// that is entered at the associated path position. The reference id associated +/// with the end point will always be zero. This allows, for example, matching +/// off-mesh link points to their representative polygons. +/// +/// If the provided result buffers are too small for the entire result set, +/// they will be filled as far as possible from the start toward the end +/// position. +/// +dtStatus dtNavMeshQuery::findStraightPath(const float* startPos, const float* endPos, + const dtPolyRef* path, const int pathSize, + float* straightPath, unsigned char* straightPathFlags, dtPolyRef* straightPathRefs, + int* straightPathCount, const int maxStraightPath, const int options) const +{ + dtAssert(m_nav); + + if (!straightPathCount) + return DT_FAILURE | DT_INVALID_PARAM; + + *straightPathCount = 0; + + if (!startPos || !dtVisfinite(startPos) || + !endPos || !dtVisfinite(endPos) || + !path || pathSize <= 0 || !path[0] || + maxStraightPath <= 0) + { + return DT_FAILURE | DT_INVALID_PARAM; + } + + dtStatus stat = 0; + + // TODO: Should this be callers responsibility? + float closestStartPos[3]; + if (dtStatusFailed(closestPointOnPolyBoundary(path[0], startPos, closestStartPos))) + return DT_FAILURE | DT_INVALID_PARAM; + + float closestEndPos[3]; + if (dtStatusFailed(closestPointOnPolyBoundary(path[pathSize-1], endPos, closestEndPos))) + return DT_FAILURE | DT_INVALID_PARAM; + + // Add start point. + stat = appendVertex(closestStartPos, DT_STRAIGHTPATH_START, path[0], + straightPath, straightPathFlags, straightPathRefs, + straightPathCount, maxStraightPath); + if (stat != DT_IN_PROGRESS) + return stat; + + if (pathSize > 1) + { + float portalApex[3], portalLeft[3], portalRight[3]; + dtVcopy(portalApex, closestStartPos); + dtVcopy(portalLeft, portalApex); + dtVcopy(portalRight, portalApex); + int apexIndex = 0; + int leftIndex = 0; + int rightIndex = 0; + + unsigned char leftPolyType = 0; + unsigned char rightPolyType = 0; + + dtPolyRef leftPolyRef = path[0]; + dtPolyRef rightPolyRef = path[0]; + + for (int i = 0; i < pathSize; ++i) + { + float left[3], right[3]; + unsigned char toType; + + if (i+1 < pathSize) + { + unsigned char fromType; // fromType is ignored. + + // Next portal. + if (dtStatusFailed(getPortalPoints(path[i], path[i+1], left, right, fromType, toType))) + { + // Failed to get portal points, in practice this means that path[i+1] is invalid polygon. + // Clamp the end point to path[i], and return the path so far. + + if (dtStatusFailed(closestPointOnPolyBoundary(path[i], endPos, closestEndPos))) + { + // This should only happen when the first polygon is invalid. + return DT_FAILURE | DT_INVALID_PARAM; + } + + // Apeend portals along the current straight path segment. + if (options & (DT_STRAIGHTPATH_AREA_CROSSINGS | DT_STRAIGHTPATH_ALL_CROSSINGS)) + { + // Ignore status return value as we're just about to return anyway. + appendPortals(apexIndex, i, closestEndPos, path, + straightPath, straightPathFlags, straightPathRefs, + straightPathCount, maxStraightPath, options); + } + + // Ignore status return value as we're just about to return anyway. + appendVertex(closestEndPos, 0, path[i], + straightPath, straightPathFlags, straightPathRefs, + straightPathCount, maxStraightPath); + + return DT_SUCCESS | DT_PARTIAL_RESULT | ((*straightPathCount >= maxStraightPath) ? DT_BUFFER_TOO_SMALL : 0); + } + + // If starting really close the portal, advance. + if (i == 0) + { + float t; + if (dtDistancePtSegSqr2D(portalApex, left, right, t) < dtSqr(0.001f)) + continue; + } + } + else + { + // End of the path. + dtVcopy(left, closestEndPos); + dtVcopy(right, closestEndPos); + + toType = DT_POLYTYPE_GROUND; + } + + // Right vertex. + if (dtTriArea2D(portalApex, portalRight, right) <= 0.0f) + { + if (dtVequal(portalApex, portalRight) || dtTriArea2D(portalApex, portalLeft, right) > 0.0f) + { + dtVcopy(portalRight, right); + rightPolyRef = (i+1 < pathSize) ? path[i+1] : 0; + rightPolyType = toType; + rightIndex = i; + } + else + { + // Append portals along the current straight path segment. + if (options & (DT_STRAIGHTPATH_AREA_CROSSINGS | DT_STRAIGHTPATH_ALL_CROSSINGS)) + { + stat = appendPortals(apexIndex, leftIndex, portalLeft, path, + straightPath, straightPathFlags, straightPathRefs, + straightPathCount, maxStraightPath, options); + if (stat != DT_IN_PROGRESS) + return stat; + } + + dtVcopy(portalApex, portalLeft); + apexIndex = leftIndex; + + unsigned char flags = 0; + if (!leftPolyRef) + flags = DT_STRAIGHTPATH_END; + else if (leftPolyType == DT_POLYTYPE_OFFMESH_CONNECTION) + flags = DT_STRAIGHTPATH_OFFMESH_CONNECTION; + dtPolyRef ref = leftPolyRef; + + // Append or update vertex + stat = appendVertex(portalApex, flags, ref, + straightPath, straightPathFlags, straightPathRefs, + straightPathCount, maxStraightPath); + if (stat != DT_IN_PROGRESS) + return stat; + + dtVcopy(portalLeft, portalApex); + dtVcopy(portalRight, portalApex); + leftIndex = apexIndex; + rightIndex = apexIndex; + + // Restart + i = apexIndex; + + continue; + } + } + + // Left vertex. + if (dtTriArea2D(portalApex, portalLeft, left) >= 0.0f) + { + if (dtVequal(portalApex, portalLeft) || dtTriArea2D(portalApex, portalRight, left) < 0.0f) + { + dtVcopy(portalLeft, left); + leftPolyRef = (i+1 < pathSize) ? path[i+1] : 0; + leftPolyType = toType; + leftIndex = i; + } + else + { + // Append portals along the current straight path segment. + if (options & (DT_STRAIGHTPATH_AREA_CROSSINGS | DT_STRAIGHTPATH_ALL_CROSSINGS)) + { + stat = appendPortals(apexIndex, rightIndex, portalRight, path, + straightPath, straightPathFlags, straightPathRefs, + straightPathCount, maxStraightPath, options); + if (stat != DT_IN_PROGRESS) + return stat; + } + + dtVcopy(portalApex, portalRight); + apexIndex = rightIndex; + + unsigned char flags = 0; + if (!rightPolyRef) + flags = DT_STRAIGHTPATH_END; + else if (rightPolyType == DT_POLYTYPE_OFFMESH_CONNECTION) + flags = DT_STRAIGHTPATH_OFFMESH_CONNECTION; + dtPolyRef ref = rightPolyRef; + + // Append or update vertex + stat = appendVertex(portalApex, flags, ref, + straightPath, straightPathFlags, straightPathRefs, + straightPathCount, maxStraightPath); + if (stat != DT_IN_PROGRESS) + return stat; + + dtVcopy(portalLeft, portalApex); + dtVcopy(portalRight, portalApex); + leftIndex = apexIndex; + rightIndex = apexIndex; + + // Restart + i = apexIndex; + + continue; + } + } + } + + // Append portals along the current straight path segment. + if (options & (DT_STRAIGHTPATH_AREA_CROSSINGS | DT_STRAIGHTPATH_ALL_CROSSINGS)) + { + stat = appendPortals(apexIndex, pathSize-1, closestEndPos, path, + straightPath, straightPathFlags, straightPathRefs, + straightPathCount, maxStraightPath, options); + if (stat != DT_IN_PROGRESS) + return stat; + } + } + + // Ignore status return value as we're just about to return anyway. + appendVertex(closestEndPos, DT_STRAIGHTPATH_END, 0, + straightPath, straightPathFlags, straightPathRefs, + straightPathCount, maxStraightPath); + + return DT_SUCCESS | ((*straightPathCount >= maxStraightPath) ? DT_BUFFER_TOO_SMALL : 0); +} + +/// @par +/// +/// This method is optimized for small delta movement and a small number of +/// polygons. If used for too great a distance, the result set will form an +/// incomplete path. +/// +/// @p resultPos will equal the @p endPos if the end is reached. +/// Otherwise the closest reachable position will be returned. +/// +/// @p resultPos is not projected onto the surface of the navigation +/// mesh. Use #getPolyHeight if this is needed. +/// +/// This method treats the end position in the same manner as +/// the #raycast method. (As a 2D point.) See that method's documentation +/// for details. +/// +/// If the @p visited array is too small to hold the entire result set, it will +/// be filled as far as possible from the start position toward the end +/// position. +/// +dtStatus dtNavMeshQuery::moveAlongSurface(dtPolyRef startRef, const float* startPos, const float* endPos, + const dtQueryFilter* filter, + float* resultPos, dtPolyRef* visited, int* visitedCount, const int maxVisitedSize) const +{ + dtAssert(m_nav); + dtAssert(m_tinyNodePool); + + if (!visitedCount) + return DT_FAILURE | DT_INVALID_PARAM; + + *visitedCount = 0; + + if (!m_nav->isValidPolyRef(startRef) || + !startPos || !dtVisfinite(startPos) || + !endPos || !dtVisfinite(endPos) || + !filter || !resultPos || !visited || + maxVisitedSize <= 0) + { + return DT_FAILURE | DT_INVALID_PARAM; + } + + dtStatus status = DT_SUCCESS; + + static const int MAX_STACK = 48; + dtNode* stack[MAX_STACK]; + int nstack = 0; + + m_tinyNodePool->clear(); + + dtNode* startNode = m_tinyNodePool->getNode(startRef); + startNode->pidx = 0; + startNode->cost = 0; + startNode->total = 0; + startNode->id = startRef; + startNode->flags = DT_NODE_CLOSED; + stack[nstack++] = startNode; + + float bestPos[3]; + float bestDist = FLT_MAX; + dtNode* bestNode = 0; + dtVcopy(bestPos, startPos); + + // Search constraints + float searchPos[3], searchRadSqr; + dtVlerp(searchPos, startPos, endPos, 0.5f); + searchRadSqr = dtSqr(dtVdist(startPos, endPos)/2.0f + 0.001f); + + float verts[DT_VERTS_PER_POLYGON*3]; + + while (nstack) + { + // Pop front. + dtNode* curNode = stack[0]; + for (int i = 0; i < nstack-1; ++i) + stack[i] = stack[i+1]; + nstack--; + + // Get poly and tile. + // The API input has been cheked already, skip checking internal data. + const dtPolyRef curRef = curNode->id; + const dtMeshTile* curTile = 0; + const dtPoly* curPoly = 0; + m_nav->getTileAndPolyByRefUnsafe(curRef, &curTile, &curPoly); + + // Collect vertices. + const int nverts = curPoly->vertCount; + for (int i = 0; i < nverts; ++i) + dtVcopy(&verts[i*3], &curTile->verts[curPoly->verts[i]*3]); + + // If target is inside the poly, stop search. + if (dtPointInPolygon(endPos, verts, nverts)) + { + bestNode = curNode; + dtVcopy(bestPos, endPos); + break; + } + + // Find wall edges and find nearest point inside the walls. + for (int i = 0, j = (int)curPoly->vertCount-1; i < (int)curPoly->vertCount; j = i++) + { + // Find links to neighbours. + static const int MAX_NEIS = 8; + int nneis = 0; + dtPolyRef neis[MAX_NEIS]; + + if (curPoly->neis[j] & DT_EXT_LINK) + { + // Tile border. + for (unsigned int k = curPoly->firstLink; k != DT_NULL_LINK; k = curTile->links[k].next) + { + const dtLink* link = &curTile->links[k]; + if (link->edge == j) + { + if (link->ref != 0) + { + const dtMeshTile* neiTile = 0; + const dtPoly* neiPoly = 0; + m_nav->getTileAndPolyByRefUnsafe(link->ref, &neiTile, &neiPoly); + if (filter->passFilter(link->ref, neiTile, neiPoly)) + { + if (nneis < MAX_NEIS) + neis[nneis++] = link->ref; + } + } + } + } + } + else if (curPoly->neis[j]) + { + const unsigned int idx = (unsigned int)(curPoly->neis[j]-1); + const dtPolyRef ref = m_nav->getPolyRefBase(curTile) | idx; + if (filter->passFilter(ref, curTile, &curTile->polys[idx])) + { + // Internal edge, encode id. + neis[nneis++] = ref; + } + } + + if (!nneis) + { + // Wall edge, calc distance. + const float* vj = &verts[j*3]; + const float* vi = &verts[i*3]; + float tseg; + const float distSqr = dtDistancePtSegSqr2D(endPos, vj, vi, tseg); + if (distSqr < bestDist) + { + // Update nearest distance. + dtVlerp(bestPos, vj,vi, tseg); + bestDist = distSqr; + bestNode = curNode; + } + } + else + { + for (int k = 0; k < nneis; ++k) + { + // Skip if no node can be allocated. + dtNode* neighbourNode = m_tinyNodePool->getNode(neis[k]); + if (!neighbourNode) + continue; + // Skip if already visited. + if (neighbourNode->flags & DT_NODE_CLOSED) + continue; + + // Skip the link if it is too far from search constraint. + // TODO: Maybe should use getPortalPoints(), but this one is way faster. + const float* vj = &verts[j*3]; + const float* vi = &verts[i*3]; + float tseg; + float distSqr = dtDistancePtSegSqr2D(searchPos, vj, vi, tseg); + if (distSqr > searchRadSqr) + continue; + + // Mark as the node as visited and push to queue. + if (nstack < MAX_STACK) + { + neighbourNode->pidx = m_tinyNodePool->getNodeIdx(curNode); + neighbourNode->flags |= DT_NODE_CLOSED; + stack[nstack++] = neighbourNode; + } + } + } + } + } + + int n = 0; + if (bestNode) + { + // Reverse the path. + dtNode* prev = 0; + dtNode* node = bestNode; + do + { + dtNode* next = m_tinyNodePool->getNodeAtIdx(node->pidx); + node->pidx = m_tinyNodePool->getNodeIdx(prev); + prev = node; + node = next; + } + while (node); + + // Store result + node = prev; + do + { + visited[n++] = node->id; + if (n >= maxVisitedSize) + { + status |= DT_BUFFER_TOO_SMALL; + break; + } + node = m_tinyNodePool->getNodeAtIdx(node->pidx); + } + while (node); + } + + dtVcopy(resultPos, bestPos); + + *visitedCount = n; + + return status; +} + + +dtStatus dtNavMeshQuery::getPortalPoints(dtPolyRef from, dtPolyRef to, float* left, float* right, + unsigned char& fromType, unsigned char& toType) const +{ + dtAssert(m_nav); + + const dtMeshTile* fromTile = 0; + const dtPoly* fromPoly = 0; + if (dtStatusFailed(m_nav->getTileAndPolyByRef(from, &fromTile, &fromPoly))) + return DT_FAILURE | DT_INVALID_PARAM; + fromType = fromPoly->getType(); + + const dtMeshTile* toTile = 0; + const dtPoly* toPoly = 0; + if (dtStatusFailed(m_nav->getTileAndPolyByRef(to, &toTile, &toPoly))) + return DT_FAILURE | DT_INVALID_PARAM; + toType = toPoly->getType(); + + return getPortalPoints(from, fromPoly, fromTile, to, toPoly, toTile, left, right); +} + +// Returns portal points between two polygons. +dtStatus dtNavMeshQuery::getPortalPoints(dtPolyRef from, const dtPoly* fromPoly, const dtMeshTile* fromTile, + dtPolyRef to, const dtPoly* toPoly, const dtMeshTile* toTile, + float* left, float* right) const +{ + // Find the link that points to the 'to' polygon. + const dtLink* link = 0; + for (unsigned int i = fromPoly->firstLink; i != DT_NULL_LINK; i = fromTile->links[i].next) + { + if (fromTile->links[i].ref == to) + { + link = &fromTile->links[i]; + break; + } + } + if (!link) + return DT_FAILURE | DT_INVALID_PARAM; + + // Handle off-mesh connections. + if (fromPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) + { + // Find link that points to first vertex. + for (unsigned int i = fromPoly->firstLink; i != DT_NULL_LINK; i = fromTile->links[i].next) + { + if (fromTile->links[i].ref == to) + { + const int v = fromTile->links[i].edge; + dtVcopy(left, &fromTile->verts[fromPoly->verts[v]*3]); + dtVcopy(right, &fromTile->verts[fromPoly->verts[v]*3]); + return DT_SUCCESS; + } + } + return DT_FAILURE | DT_INVALID_PARAM; + } + + if (toPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) + { + for (unsigned int i = toPoly->firstLink; i != DT_NULL_LINK; i = toTile->links[i].next) + { + if (toTile->links[i].ref == from) + { + const int v = toTile->links[i].edge; + dtVcopy(left, &toTile->verts[toPoly->verts[v]*3]); + dtVcopy(right, &toTile->verts[toPoly->verts[v]*3]); + return DT_SUCCESS; + } + } + return DT_FAILURE | DT_INVALID_PARAM; + } + + // Find portal vertices. + const int v0 = fromPoly->verts[link->edge]; + const int v1 = fromPoly->verts[(link->edge+1) % (int)fromPoly->vertCount]; + dtVcopy(left, &fromTile->verts[v0*3]); + dtVcopy(right, &fromTile->verts[v1*3]); + + // If the link is at tile boundary, dtClamp the vertices to + // the link width. + if (link->side != 0xff) + { + // Unpack portal limits. + if (link->bmin != 0 || link->bmax != 255) + { + const float s = 1.0f/255.0f; + const float tmin = link->bmin*s; + const float tmax = link->bmax*s; + dtVlerp(left, &fromTile->verts[v0*3], &fromTile->verts[v1*3], tmin); + dtVlerp(right, &fromTile->verts[v0*3], &fromTile->verts[v1*3], tmax); + } + } + + return DT_SUCCESS; +} + +// Returns edge mid point between two polygons. +dtStatus dtNavMeshQuery::getEdgeMidPoint(dtPolyRef from, dtPolyRef to, float* mid) const +{ + float left[3], right[3]; + unsigned char fromType, toType; + if (dtStatusFailed(getPortalPoints(from, to, left,right, fromType, toType))) + return DT_FAILURE | DT_INVALID_PARAM; + mid[0] = (left[0]+right[0])*0.5f; + mid[1] = (left[1]+right[1])*0.5f; + mid[2] = (left[2]+right[2])*0.5f; + return DT_SUCCESS; +} + +dtStatus dtNavMeshQuery::getEdgeMidPoint(dtPolyRef from, const dtPoly* fromPoly, const dtMeshTile* fromTile, + dtPolyRef to, const dtPoly* toPoly, const dtMeshTile* toTile, + float* mid) const +{ + float left[3], right[3]; + if (dtStatusFailed(getPortalPoints(from, fromPoly, fromTile, to, toPoly, toTile, left, right))) + return DT_FAILURE | DT_INVALID_PARAM; + mid[0] = (left[0]+right[0])*0.5f; + mid[1] = (left[1]+right[1])*0.5f; + mid[2] = (left[2]+right[2])*0.5f; + return DT_SUCCESS; +} + + + +/// @par +/// +/// This method is meant to be used for quick, short distance checks. +/// +/// If the path array is too small to hold the result, it will be filled as +/// far as possible from the start postion toward the end position. +/// +/// Using the Hit Parameter (t) +/// +/// If the hit parameter is a very high value (FLT_MAX), then the ray has hit +/// the end position. In this case the path represents a valid corridor to the +/// end position and the value of @p hitNormal is undefined. +/// +/// If the hit parameter is zero, then the start position is on the wall that +/// was hit and the value of @p hitNormal is undefined. +/// +/// If 0 < t < 1.0 then the following applies: +/// +/// @code +/// distanceToHitBorder = distanceToEndPosition * t +/// hitPoint = startPos + (endPos - startPos) * t +/// @endcode +/// +/// Use Case Restriction +/// +/// The raycast ignores the y-value of the end position. (2D check.) This +/// places significant limits on how it can be used. For example: +/// +/// Consider a scene where there is a main floor with a second floor balcony +/// that hangs over the main floor. So the first floor mesh extends below the +/// balcony mesh. The start position is somewhere on the first floor. The end +/// position is on the balcony. +/// +/// The raycast will search toward the end position along the first floor mesh. +/// If it reaches the end position's xz-coordinates it will indicate FLT_MAX +/// (no wall hit), meaning it reached the end position. This is one example of why +/// this method is meant for short distance checks. +/// +dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, const float* endPos, + const dtQueryFilter* filter, + float* t, float* hitNormal, dtPolyRef* path, int* pathCount, const int maxPath) const +{ + dtRaycastHit hit; + hit.path = path; + hit.maxPath = maxPath; + + dtStatus status = raycast(startRef, startPos, endPos, filter, 0, &hit); + + *t = hit.t; + if (hitNormal) + dtVcopy(hitNormal, hit.hitNormal); + if (pathCount) + *pathCount = hit.pathCount; + + return status; +} + + +/// @par +/// +/// This method is meant to be used for quick, short distance checks. +/// +/// If the path array is too small to hold the result, it will be filled as +/// far as possible from the start postion toward the end position. +/// +/// Using the Hit Parameter t of RaycastHit +/// +/// If the hit parameter is a very high value (FLT_MAX), then the ray has hit +/// the end position. In this case the path represents a valid corridor to the +/// end position and the value of @p hitNormal is undefined. +/// +/// If the hit parameter is zero, then the start position is on the wall that +/// was hit and the value of @p hitNormal is undefined. +/// +/// If 0 < t < 1.0 then the following applies: +/// +/// @code +/// distanceToHitBorder = distanceToEndPosition * t +/// hitPoint = startPos + (endPos - startPos) * t +/// @endcode +/// +/// Use Case Restriction +/// +/// The raycast ignores the y-value of the end position. (2D check.) This +/// places significant limits on how it can be used. For example: +/// +/// Consider a scene where there is a main floor with a second floor balcony +/// that hangs over the main floor. So the first floor mesh extends below the +/// balcony mesh. The start position is somewhere on the first floor. The end +/// position is on the balcony. +/// +/// The raycast will search toward the end position along the first floor mesh. +/// If it reaches the end position's xz-coordinates it will indicate FLT_MAX +/// (no wall hit), meaning it reached the end position. This is one example of why +/// this method is meant for short distance checks. +/// +dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, const float* endPos, + const dtQueryFilter* filter, const unsigned int options, + dtRaycastHit* hit, dtPolyRef prevRef) const +{ + dtAssert(m_nav); + + if (!hit) + return DT_FAILURE | DT_INVALID_PARAM; + + hit->t = 0; + hit->pathCount = 0; + hit->pathCost = 0; + + // Validate input + if (!m_nav->isValidPolyRef(startRef) || + !startPos || !dtVisfinite(startPos) || + !endPos || !dtVisfinite(endPos) || + !filter || + (prevRef && !m_nav->isValidPolyRef(prevRef))) + { + return DT_FAILURE | DT_INVALID_PARAM; + } + + float dir[3], curPos[3], lastPos[3]; + float verts[DT_VERTS_PER_POLYGON*3+3]; + int n = 0; + + dtVcopy(curPos, startPos); + dtVsub(dir, endPos, startPos); + dtVset(hit->hitNormal, 0, 0, 0); + + dtStatus status = DT_SUCCESS; + + const dtMeshTile* prevTile, *tile, *nextTile; + const dtPoly* prevPoly, *poly, *nextPoly; + dtPolyRef curRef; + + // The API input has been checked already, skip checking internal data. + curRef = startRef; + tile = 0; + poly = 0; + m_nav->getTileAndPolyByRefUnsafe(curRef, &tile, &poly); + nextTile = prevTile = tile; + nextPoly = prevPoly = poly; + if (prevRef) + m_nav->getTileAndPolyByRefUnsafe(prevRef, &prevTile, &prevPoly); + + while (curRef) + { + // Cast ray against current polygon. + + // Collect vertices. + int nv = 0; + for (int i = 0; i < (int)poly->vertCount; ++i) + { + dtVcopy(&verts[nv*3], &tile->verts[poly->verts[i]*3]); + nv++; + } + + float tmin, tmax; + int segMin, segMax; + if (!dtIntersectSegmentPoly2D(startPos, endPos, verts, nv, tmin, tmax, segMin, segMax)) + { + // Could not hit the polygon, keep the old t and report hit. + hit->pathCount = n; + return status; + } + + hit->hitEdgeIndex = segMax; + + // Keep track of furthest t so far. + if (tmax > hit->t) + hit->t = tmax; + + // Store visited polygons. + if (n < hit->maxPath) + hit->path[n++] = curRef; + else + status |= DT_BUFFER_TOO_SMALL; + + // Ray end is completely inside the polygon. + if (segMax == -1) + { + hit->t = FLT_MAX; + hit->pathCount = n; + + // add the cost + if (options & DT_RAYCAST_USE_COSTS) + hit->pathCost += filter->getCost(curPos, endPos, prevRef, prevTile, prevPoly, curRef, tile, poly, curRef, tile, poly); + return status; + } + + // Follow neighbours. + dtPolyRef nextRef = 0; + + for (unsigned int i = poly->firstLink; i != DT_NULL_LINK; i = tile->links[i].next) + { + const dtLink* link = &tile->links[i]; + + // Find link which contains this edge. + if ((int)link->edge != segMax) + continue; + + // Get pointer to the next polygon. + nextTile = 0; + nextPoly = 0; + m_nav->getTileAndPolyByRefUnsafe(link->ref, &nextTile, &nextPoly); + + // Skip off-mesh connections. + if (nextPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) + continue; + + // Skip links based on filter. + if (!filter->passFilter(link->ref, nextTile, nextPoly)) + continue; + + // If the link is internal, just return the ref. + if (link->side == 0xff) + { + nextRef = link->ref; + break; + } + + // If the link is at tile boundary, + + // Check if the link spans the whole edge, and accept. + if (link->bmin == 0 && link->bmax == 255) + { + nextRef = link->ref; + break; + } + + // Check for partial edge links. + const int v0 = poly->verts[link->edge]; + const int v1 = poly->verts[(link->edge+1) % poly->vertCount]; + const float* left = &tile->verts[v0*3]; + const float* right = &tile->verts[v1*3]; + + // Check that the intersection lies inside the link portal. + if (link->side == 0 || link->side == 4) + { + // Calculate link size. + const float s = 1.0f/255.0f; + float lmin = left[2] + (right[2] - left[2])*(link->bmin*s); + float lmax = left[2] + (right[2] - left[2])*(link->bmax*s); + if (lmin > lmax) dtSwap(lmin, lmax); + + // Find Z intersection. + float z = startPos[2] + (endPos[2]-startPos[2])*tmax; + if (z >= lmin && z <= lmax) + { + nextRef = link->ref; + break; + } + } + else if (link->side == 2 || link->side == 6) + { + // Calculate link size. + const float s = 1.0f/255.0f; + float lmin = left[0] + (right[0] - left[0])*(link->bmin*s); + float lmax = left[0] + (right[0] - left[0])*(link->bmax*s); + if (lmin > lmax) dtSwap(lmin, lmax); + + // Find X intersection. + float x = startPos[0] + (endPos[0]-startPos[0])*tmax; + if (x >= lmin && x <= lmax) + { + nextRef = link->ref; + break; + } + } + } + + // add the cost + if (options & DT_RAYCAST_USE_COSTS) + { + // compute the intersection point at the furthest end of the polygon + // and correct the height (since the raycast moves in 2d) + dtVcopy(lastPos, curPos); + dtVmad(curPos, startPos, dir, hit->t); + float* e1 = &verts[segMax*3]; + float* e2 = &verts[((segMax+1)%nv)*3]; + float eDir[3], diff[3]; + dtVsub(eDir, e2, e1); + dtVsub(diff, curPos, e1); + float s = dtSqr(eDir[0]) > dtSqr(eDir[2]) ? diff[0] / eDir[0] : diff[2] / eDir[2]; + curPos[1] = e1[1] + eDir[1] * s; + + hit->pathCost += filter->getCost(lastPos, curPos, prevRef, prevTile, prevPoly, curRef, tile, poly, nextRef, nextTile, nextPoly); + } + + if (!nextRef) + { + // No neighbour, we hit a wall. + + // Calculate hit normal. + const int a = segMax; + const int b = segMax+1 < nv ? segMax+1 : 0; + const float* va = &verts[a*3]; + const float* vb = &verts[b*3]; + const float dx = vb[0] - va[0]; + const float dz = vb[2] - va[2]; + hit->hitNormal[0] = dz; + hit->hitNormal[1] = 0; + hit->hitNormal[2] = -dx; + dtVnormalize(hit->hitNormal); + + hit->pathCount = n; + return status; + } + + // No hit, advance to neighbour polygon. + prevRef = curRef; + curRef = nextRef; + prevTile = tile; + tile = nextTile; + prevPoly = poly; + poly = nextPoly; + } + + hit->pathCount = n; + + return status; +} + +/// @par +/// +/// At least one result array must be provided. +/// +/// The order of the result set is from least to highest cost to reach the polygon. +/// +/// A common use case for this method is to perform Dijkstra searches. +/// Candidate polygons are found by searching the graph beginning at the start polygon. +/// +/// If a polygon is not found via the graph search, even if it intersects the +/// search circle, it will not be included in the result set. For example: +/// +/// polyA is the start polygon. +/// polyB shares an edge with polyA. (Is adjacent.) +/// polyC shares an edge with polyB, but not with polyA +/// Even if the search circle overlaps polyC, it will not be included in the +/// result set unless polyB is also in the set. +/// +/// The value of the center point is used as the start position for cost +/// calculations. It is not projected onto the surface of the mesh, so its +/// y-value will effect the costs. +/// +/// Intersection tests occur in 2D. All polygons and the search circle are +/// projected onto the xz-plane. So the y-value of the center point does not +/// effect intersection tests. +/// +/// If the result arrays are to small to hold the entire result set, they will be +/// filled to capacity. +/// +dtStatus dtNavMeshQuery::findPolysAroundCircle(dtPolyRef startRef, const float* centerPos, const float radius, + const dtQueryFilter* filter, + dtPolyRef* resultRef, dtPolyRef* resultParent, float* resultCost, + int* resultCount, const int maxResult) const +{ + dtAssert(m_nav); + dtAssert(m_nodePool); + dtAssert(m_openList); + + if (!resultCount) + return DT_FAILURE | DT_INVALID_PARAM; + + *resultCount = 0; + + if (!m_nav->isValidPolyRef(startRef) || + !centerPos || !dtVisfinite(centerPos) || + radius < 0 || !dtMathIsfinite(radius) || + !filter || maxResult < 0) + { + return DT_FAILURE | DT_INVALID_PARAM; + } + + m_nodePool->clear(); + m_openList->clear(); + + dtNode* startNode = m_nodePool->getNode(startRef); + dtVcopy(startNode->pos, centerPos); + startNode->pidx = 0; + startNode->cost = 0; + startNode->total = 0; + startNode->id = startRef; + startNode->flags = DT_NODE_OPEN; + m_openList->push(startNode); + + dtStatus status = DT_SUCCESS; + + int n = 0; + + const float radiusSqr = dtSqr(radius); + + while (!m_openList->empty()) + { + dtNode* bestNode = m_openList->pop(); + bestNode->flags &= ~DT_NODE_OPEN; + bestNode->flags |= DT_NODE_CLOSED; + + // Get poly and tile. + // The API input has been cheked already, skip checking internal data. + const dtPolyRef bestRef = bestNode->id; + const dtMeshTile* bestTile = 0; + const dtPoly* bestPoly = 0; + m_nav->getTileAndPolyByRefUnsafe(bestRef, &bestTile, &bestPoly); + + // Get parent poly and tile. + dtPolyRef parentRef = 0; + const dtMeshTile* parentTile = 0; + const dtPoly* parentPoly = 0; + if (bestNode->pidx) + parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id; + if (parentRef) + m_nav->getTileAndPolyByRefUnsafe(parentRef, &parentTile, &parentPoly); + + if (n < maxResult) + { + if (resultRef) + resultRef[n] = bestRef; + if (resultParent) + resultParent[n] = parentRef; + if (resultCost) + resultCost[n] = bestNode->total; + ++n; + } + else + { + status |= DT_BUFFER_TOO_SMALL; + } + + for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next) + { + const dtLink* link = &bestTile->links[i]; + dtPolyRef neighbourRef = link->ref; + // Skip invalid neighbours and do not follow back to parent. + if (!neighbourRef || neighbourRef == parentRef) + continue; + + // Expand to neighbour + const dtMeshTile* neighbourTile = 0; + const dtPoly* neighbourPoly = 0; + m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly); + + // Do not advance if the polygon is excluded by the filter. + if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly)) + continue; + + // Find edge and calc distance to the edge. + float va[3], vb[3]; + if (!getPortalPoints(bestRef, bestPoly, bestTile, neighbourRef, neighbourPoly, neighbourTile, va, vb)) + continue; + + // If the circle is not touching the next polygon, skip it. + float tseg; + float distSqr = dtDistancePtSegSqr2D(centerPos, va, vb, tseg); + if (distSqr > radiusSqr) + continue; + + dtNode* neighbourNode = m_nodePool->getNode(neighbourRef); + if (!neighbourNode) + { + status |= DT_OUT_OF_NODES; + continue; + } + + if (neighbourNode->flags & DT_NODE_CLOSED) + continue; + + // Cost + if (neighbourNode->flags == 0) + dtVlerp(neighbourNode->pos, va, vb, 0.5f); + + float cost = filter->getCost( + bestNode->pos, neighbourNode->pos, + parentRef, parentTile, parentPoly, + bestRef, bestTile, bestPoly, + neighbourRef, neighbourTile, neighbourPoly); + + const float total = bestNode->total + cost; + + // The node is already in open list and the new result is worse, skip. + if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total) + continue; + + neighbourNode->id = neighbourRef; + neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode); + neighbourNode->total = total; + + if (neighbourNode->flags & DT_NODE_OPEN) + { + m_openList->modify(neighbourNode); + } + else + { + neighbourNode->flags = DT_NODE_OPEN; + m_openList->push(neighbourNode); + } + } + } + + *resultCount = n; + + return status; +} + +/// @par +/// +/// The order of the result set is from least to highest cost. +/// +/// At least one result array must be provided. +/// +/// A common use case for this method is to perform Dijkstra searches. +/// Candidate polygons are found by searching the graph beginning at the start +/// polygon. +/// +/// The same intersection test restrictions that apply to findPolysAroundCircle() +/// method apply to this method. +/// +/// The 3D centroid of the search polygon is used as the start position for cost +/// calculations. +/// +/// Intersection tests occur in 2D. All polygons are projected onto the +/// xz-plane. So the y-values of the vertices do not effect intersection tests. +/// +/// If the result arrays are is too small to hold the entire result set, they will +/// be filled to capacity. +/// +dtStatus dtNavMeshQuery::findPolysAroundShape(dtPolyRef startRef, const float* verts, const int nverts, + const dtQueryFilter* filter, + dtPolyRef* resultRef, dtPolyRef* resultParent, float* resultCost, + int* resultCount, const int maxResult) const +{ + dtAssert(m_nav); + dtAssert(m_nodePool); + dtAssert(m_openList); + + if (!resultCount) + return DT_FAILURE | DT_INVALID_PARAM; + + *resultCount = 0; + + if (!m_nav->isValidPolyRef(startRef) || + !verts || nverts < 3 || + !filter || maxResult < 0) + { + return DT_FAILURE | DT_INVALID_PARAM; + } + + // Validate input + if (!startRef || !m_nav->isValidPolyRef(startRef)) + return DT_FAILURE | DT_INVALID_PARAM; + + m_nodePool->clear(); + m_openList->clear(); + + float centerPos[3] = {0,0,0}; + for (int i = 0; i < nverts; ++i) + dtVadd(centerPos,centerPos,&verts[i*3]); + dtVscale(centerPos,centerPos,1.0f/nverts); + + dtNode* startNode = m_nodePool->getNode(startRef); + dtVcopy(startNode->pos, centerPos); + startNode->pidx = 0; + startNode->cost = 0; + startNode->total = 0; + startNode->id = startRef; + startNode->flags = DT_NODE_OPEN; + m_openList->push(startNode); + + dtStatus status = DT_SUCCESS; + + int n = 0; + + while (!m_openList->empty()) + { + dtNode* bestNode = m_openList->pop(); + bestNode->flags &= ~DT_NODE_OPEN; + bestNode->flags |= DT_NODE_CLOSED; + + // Get poly and tile. + // The API input has been cheked already, skip checking internal data. + const dtPolyRef bestRef = bestNode->id; + const dtMeshTile* bestTile = 0; + const dtPoly* bestPoly = 0; + m_nav->getTileAndPolyByRefUnsafe(bestRef, &bestTile, &bestPoly); + + // Get parent poly and tile. + dtPolyRef parentRef = 0; + const dtMeshTile* parentTile = 0; + const dtPoly* parentPoly = 0; + if (bestNode->pidx) + parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id; + if (parentRef) + m_nav->getTileAndPolyByRefUnsafe(parentRef, &parentTile, &parentPoly); + + if (n < maxResult) + { + if (resultRef) + resultRef[n] = bestRef; + if (resultParent) + resultParent[n] = parentRef; + if (resultCost) + resultCost[n] = bestNode->total; + + ++n; + } + else + { + status |= DT_BUFFER_TOO_SMALL; + } + + for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next) + { + const dtLink* link = &bestTile->links[i]; + dtPolyRef neighbourRef = link->ref; + // Skip invalid neighbours and do not follow back to parent. + if (!neighbourRef || neighbourRef == parentRef) + continue; + + // Expand to neighbour + const dtMeshTile* neighbourTile = 0; + const dtPoly* neighbourPoly = 0; + m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly); + + // Do not advance if the polygon is excluded by the filter. + if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly)) + continue; + + // Find edge and calc distance to the edge. + float va[3], vb[3]; + if (!getPortalPoints(bestRef, bestPoly, bestTile, neighbourRef, neighbourPoly, neighbourTile, va, vb)) + continue; + + // If the poly is not touching the edge to the next polygon, skip the connection it. + float tmin, tmax; + int segMin, segMax; + if (!dtIntersectSegmentPoly2D(va, vb, verts, nverts, tmin, tmax, segMin, segMax)) + continue; + if (tmin > 1.0f || tmax < 0.0f) + continue; + + dtNode* neighbourNode = m_nodePool->getNode(neighbourRef); + if (!neighbourNode) + { + status |= DT_OUT_OF_NODES; + continue; + } + + if (neighbourNode->flags & DT_NODE_CLOSED) + continue; + + // Cost + if (neighbourNode->flags == 0) + dtVlerp(neighbourNode->pos, va, vb, 0.5f); + + float cost = filter->getCost( + bestNode->pos, neighbourNode->pos, + parentRef, parentTile, parentPoly, + bestRef, bestTile, bestPoly, + neighbourRef, neighbourTile, neighbourPoly); + + const float total = bestNode->total + cost; + + // The node is already in open list and the new result is worse, skip. + if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total) + continue; + + neighbourNode->id = neighbourRef; + neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode); + neighbourNode->total = total; + + if (neighbourNode->flags & DT_NODE_OPEN) + { + m_openList->modify(neighbourNode); + } + else + { + neighbourNode->flags = DT_NODE_OPEN; + m_openList->push(neighbourNode); + } + } + } + + *resultCount = n; + + return status; +} + +dtStatus dtNavMeshQuery::getPathFromDijkstraSearch(dtPolyRef endRef, dtPolyRef* path, int* pathCount, int maxPath) const +{ + if (!m_nav->isValidPolyRef(endRef) || !path || !pathCount || maxPath < 0) + return DT_FAILURE | DT_INVALID_PARAM; + + *pathCount = 0; + + dtNode* endNode; + if (m_nodePool->findNodes(endRef, &endNode, 1) != 1 || + (endNode->flags & DT_NODE_CLOSED) == 0) + return DT_FAILURE | DT_INVALID_PARAM; + + return getPathToNode(endNode, path, pathCount, maxPath); +} + +/// @par +/// +/// This method is optimized for a small search radius and small number of result +/// polygons. +/// +/// Candidate polygons are found by searching the navigation graph beginning at +/// the start polygon. +/// +/// The same intersection test restrictions that apply to the findPolysAroundCircle +/// mehtod applies to this method. +/// +/// The value of the center point is used as the start point for cost calculations. +/// It is not projected onto the surface of the mesh, so its y-value will effect +/// the costs. +/// +/// Intersection tests occur in 2D. All polygons and the search circle are +/// projected onto the xz-plane. So the y-value of the center point does not +/// effect intersection tests. +/// +/// If the result arrays are is too small to hold the entire result set, they will +/// be filled to capacity. +/// +dtStatus dtNavMeshQuery::findLocalNeighbourhood(dtPolyRef startRef, const float* centerPos, const float radius, + const dtQueryFilter* filter, + dtPolyRef* resultRef, dtPolyRef* resultParent, + int* resultCount, const int maxResult) const +{ + dtAssert(m_nav); + dtAssert(m_tinyNodePool); + + if (!resultCount) + return DT_FAILURE | DT_INVALID_PARAM; + + *resultCount = 0; + + if (!m_nav->isValidPolyRef(startRef) || + !centerPos || !dtVisfinite(centerPos) || + radius < 0 || !dtMathIsfinite(radius) || + !filter || maxResult < 0) + { + return DT_FAILURE | DT_INVALID_PARAM; + } + + static const int MAX_STACK = 48; + dtNode* stack[MAX_STACK]; + int nstack = 0; + + m_tinyNodePool->clear(); + + dtNode* startNode = m_tinyNodePool->getNode(startRef); + startNode->pidx = 0; + startNode->id = startRef; + startNode->flags = DT_NODE_CLOSED; + stack[nstack++] = startNode; + + const float radiusSqr = dtSqr(radius); + + float pa[DT_VERTS_PER_POLYGON*3]; + float pb[DT_VERTS_PER_POLYGON*3]; + + dtStatus status = DT_SUCCESS; + + int n = 0; + if (n < maxResult) + { + resultRef[n] = startNode->id; + if (resultParent) + resultParent[n] = 0; + ++n; + } + else + { + status |= DT_BUFFER_TOO_SMALL; + } + + while (nstack) + { + // Pop front. + dtNode* curNode = stack[0]; + for (int i = 0; i < nstack-1; ++i) + stack[i] = stack[i+1]; + nstack--; + + // Get poly and tile. + // The API input has been cheked already, skip checking internal data. + const dtPolyRef curRef = curNode->id; + const dtMeshTile* curTile = 0; + const dtPoly* curPoly = 0; + m_nav->getTileAndPolyByRefUnsafe(curRef, &curTile, &curPoly); + + for (unsigned int i = curPoly->firstLink; i != DT_NULL_LINK; i = curTile->links[i].next) + { + const dtLink* link = &curTile->links[i]; + dtPolyRef neighbourRef = link->ref; + // Skip invalid neighbours. + if (!neighbourRef) + continue; + + // Skip if cannot alloca more nodes. + dtNode* neighbourNode = m_tinyNodePool->getNode(neighbourRef); + if (!neighbourNode) + continue; + // Skip visited. + if (neighbourNode->flags & DT_NODE_CLOSED) + continue; + + // Expand to neighbour + const dtMeshTile* neighbourTile = 0; + const dtPoly* neighbourPoly = 0; + m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly); + + // Skip off-mesh connections. + if (neighbourPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) + continue; + + // Do not advance if the polygon is excluded by the filter. + if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly)) + continue; + + // Find edge and calc distance to the edge. + float va[3], vb[3]; + if (!getPortalPoints(curRef, curPoly, curTile, neighbourRef, neighbourPoly, neighbourTile, va, vb)) + continue; + + // If the circle is not touching the next polygon, skip it. + float tseg; + float distSqr = dtDistancePtSegSqr2D(centerPos, va, vb, tseg); + if (distSqr > radiusSqr) + continue; + + // Mark node visited, this is done before the overlap test so that + // we will not visit the poly again if the test fails. + neighbourNode->flags |= DT_NODE_CLOSED; + neighbourNode->pidx = m_tinyNodePool->getNodeIdx(curNode); + + // Check that the polygon does not collide with existing polygons. + + // Collect vertices of the neighbour poly. + const int npa = neighbourPoly->vertCount; + for (int k = 0; k < npa; ++k) + dtVcopy(&pa[k*3], &neighbourTile->verts[neighbourPoly->verts[k]*3]); + + bool overlap = false; + for (int j = 0; j < n; ++j) + { + dtPolyRef pastRef = resultRef[j]; + + // Connected polys do not overlap. + bool connected = false; + for (unsigned int k = curPoly->firstLink; k != DT_NULL_LINK; k = curTile->links[k].next) + { + if (curTile->links[k].ref == pastRef) + { + connected = true; + break; + } + } + if (connected) + continue; + + // Potentially overlapping. + const dtMeshTile* pastTile = 0; + const dtPoly* pastPoly = 0; + m_nav->getTileAndPolyByRefUnsafe(pastRef, &pastTile, &pastPoly); + + // Get vertices and test overlap + const int npb = pastPoly->vertCount; + for (int k = 0; k < npb; ++k) + dtVcopy(&pb[k*3], &pastTile->verts[pastPoly->verts[k]*3]); + + if (dtOverlapPolyPoly2D(pa,npa, pb,npb)) + { + overlap = true; + break; + } + } + if (overlap) + continue; + + // This poly is fine, store and advance to the poly. + if (n < maxResult) + { + resultRef[n] = neighbourRef; + if (resultParent) + resultParent[n] = curRef; + ++n; + } + else + { + status |= DT_BUFFER_TOO_SMALL; + } + + if (nstack < MAX_STACK) + { + stack[nstack++] = neighbourNode; + } + } + } + + *resultCount = n; + + return status; +} + + +struct dtSegInterval +{ + dtPolyRef ref; + short tmin, tmax; +}; + +static void insertInterval(dtSegInterval* ints, int& nints, const int maxInts, + const short tmin, const short tmax, const dtPolyRef ref) +{ + if (nints+1 > maxInts) return; + // Find insertion point. + int idx = 0; + while (idx < nints) + { + if (tmax <= ints[idx].tmin) + break; + idx++; + } + // Move current results. + if (nints-idx) + memmove(ints+idx+1, ints+idx, sizeof(dtSegInterval)*(nints-idx)); + // Store + ints[idx].ref = ref; + ints[idx].tmin = tmin; + ints[idx].tmax = tmax; + nints++; +} + +/// @par +/// +/// If the @p segmentRefs parameter is provided, then all polygon segments will be returned. +/// Otherwise only the wall segments are returned. +/// +/// A segment that is normally a portal will be included in the result set as a +/// wall if the @p filter results in the neighbor polygon becoomming impassable. +/// +/// The @p segmentVerts and @p segmentRefs buffers should normally be sized for the +/// maximum segments per polygon of the source navigation mesh. +/// +dtStatus dtNavMeshQuery::getPolyWallSegments(dtPolyRef ref, const dtQueryFilter* filter, + float* segmentVerts, dtPolyRef* segmentRefs, int* segmentCount, + const int maxSegments) const +{ + dtAssert(m_nav); + + if (!segmentCount) + return DT_FAILURE | DT_INVALID_PARAM; + + *segmentCount = 0; + + const dtMeshTile* tile = 0; + const dtPoly* poly = 0; + if (dtStatusFailed(m_nav->getTileAndPolyByRef(ref, &tile, &poly))) + return DT_FAILURE | DT_INVALID_PARAM; + + if (!filter || !segmentVerts || maxSegments < 0) + return DT_FAILURE | DT_INVALID_PARAM; + + int n = 0; + static const int MAX_INTERVAL = 16; + dtSegInterval ints[MAX_INTERVAL]; + int nints; + + const bool storePortals = segmentRefs != 0; + + dtStatus status = DT_SUCCESS; + + for (int i = 0, j = (int)poly->vertCount-1; i < (int)poly->vertCount; j = i++) + { + // Skip non-solid edges. + nints = 0; + if (poly->neis[j] & DT_EXT_LINK) + { + // Tile border. + for (unsigned int k = poly->firstLink; k != DT_NULL_LINK; k = tile->links[k].next) + { + const dtLink* link = &tile->links[k]; + if (link->edge == j) + { + if (link->ref != 0) + { + const dtMeshTile* neiTile = 0; + const dtPoly* neiPoly = 0; + m_nav->getTileAndPolyByRefUnsafe(link->ref, &neiTile, &neiPoly); + if (filter->passFilter(link->ref, neiTile, neiPoly)) + { + insertInterval(ints, nints, MAX_INTERVAL, link->bmin, link->bmax, link->ref); + } + } + } + } + } + else + { + // Internal edge + dtPolyRef neiRef = 0; + if (poly->neis[j]) + { + const unsigned int idx = (unsigned int)(poly->neis[j]-1); + neiRef = m_nav->getPolyRefBase(tile) | idx; + if (!filter->passFilter(neiRef, tile, &tile->polys[idx])) + neiRef = 0; + } + + // If the edge leads to another polygon and portals are not stored, skip. + if (neiRef != 0 && !storePortals) + continue; + + if (n < maxSegments) + { + const float* vj = &tile->verts[poly->verts[j]*3]; + const float* vi = &tile->verts[poly->verts[i]*3]; + float* seg = &segmentVerts[n*6]; + dtVcopy(seg+0, vj); + dtVcopy(seg+3, vi); + if (segmentRefs) + segmentRefs[n] = neiRef; + n++; + } + else + { + status |= DT_BUFFER_TOO_SMALL; + } + + continue; + } + + // Add sentinels + insertInterval(ints, nints, MAX_INTERVAL, -1, 0, 0); + insertInterval(ints, nints, MAX_INTERVAL, 255, 256, 0); + + // Store segments. + const float* vj = &tile->verts[poly->verts[j]*3]; + const float* vi = &tile->verts[poly->verts[i]*3]; + for (int k = 1; k < nints; ++k) + { + // Portal segment. + if (storePortals && ints[k].ref) + { + const float tmin = ints[k].tmin/255.0f; + const float tmax = ints[k].tmax/255.0f; + if (n < maxSegments) + { + float* seg = &segmentVerts[n*6]; + dtVlerp(seg+0, vj,vi, tmin); + dtVlerp(seg+3, vj,vi, tmax); + if (segmentRefs) + segmentRefs[n] = ints[k].ref; + n++; + } + else + { + status |= DT_BUFFER_TOO_SMALL; + } + } + + // Wall segment. + const int imin = ints[k-1].tmax; + const int imax = ints[k].tmin; + if (imin != imax) + { + const float tmin = imin/255.0f; + const float tmax = imax/255.0f; + if (n < maxSegments) + { + float* seg = &segmentVerts[n*6]; + dtVlerp(seg+0, vj,vi, tmin); + dtVlerp(seg+3, vj,vi, tmax); + if (segmentRefs) + segmentRefs[n] = 0; + n++; + } + else + { + status |= DT_BUFFER_TOO_SMALL; + } + } + } + } + + *segmentCount = n; + + return status; +} + +/// @par +/// +/// @p hitPos is not adjusted using the height detail data. +/// +/// @p hitDist will equal the search radius if there is no wall within the +/// radius. In this case the values of @p hitPos and @p hitNormal are +/// undefined. +/// +/// The normal will become unpredicable if @p hitDist is a very small number. +/// +dtStatus dtNavMeshQuery::findDistanceToWall(dtPolyRef startRef, const float* centerPos, const float maxRadius, + const dtQueryFilter* filter, + float* hitDist, float* hitPos, float* hitNormal) const +{ + dtAssert(m_nav); + dtAssert(m_nodePool); + dtAssert(m_openList); + + // Validate input + if (!m_nav->isValidPolyRef(startRef) || + !centerPos || !dtVisfinite(centerPos) || + maxRadius < 0 || !dtMathIsfinite(maxRadius) || + !filter || !hitDist || !hitPos || !hitNormal) + { + return DT_FAILURE | DT_INVALID_PARAM; + } + + m_nodePool->clear(); + m_openList->clear(); + + dtNode* startNode = m_nodePool->getNode(startRef); + dtVcopy(startNode->pos, centerPos); + startNode->pidx = 0; + startNode->cost = 0; + startNode->total = 0; + startNode->id = startRef; + startNode->flags = DT_NODE_OPEN; + m_openList->push(startNode); + + float radiusSqr = dtSqr(maxRadius); + + dtStatus status = DT_SUCCESS; + + while (!m_openList->empty()) + { + dtNode* bestNode = m_openList->pop(); + bestNode->flags &= ~DT_NODE_OPEN; + bestNode->flags |= DT_NODE_CLOSED; + + // Get poly and tile. + // The API input has been cheked already, skip checking internal data. + const dtPolyRef bestRef = bestNode->id; + const dtMeshTile* bestTile = 0; + const dtPoly* bestPoly = 0; + m_nav->getTileAndPolyByRefUnsafe(bestRef, &bestTile, &bestPoly); + + // Get parent poly and tile. + dtPolyRef parentRef = 0; + const dtMeshTile* parentTile = 0; + const dtPoly* parentPoly = 0; + if (bestNode->pidx) + parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id; + if (parentRef) + m_nav->getTileAndPolyByRefUnsafe(parentRef, &parentTile, &parentPoly); + + // Hit test walls. + for (int i = 0, j = (int)bestPoly->vertCount-1; i < (int)bestPoly->vertCount; j = i++) + { + // Skip non-solid edges. + if (bestPoly->neis[j] & DT_EXT_LINK) + { + // Tile border. + bool solid = true; + for (unsigned int k = bestPoly->firstLink; k != DT_NULL_LINK; k = bestTile->links[k].next) + { + const dtLink* link = &bestTile->links[k]; + if (link->edge == j) + { + if (link->ref != 0) + { + const dtMeshTile* neiTile = 0; + const dtPoly* neiPoly = 0; + m_nav->getTileAndPolyByRefUnsafe(link->ref, &neiTile, &neiPoly); + if (filter->passFilter(link->ref, neiTile, neiPoly)) + solid = false; + } + break; + } + } + if (!solid) continue; + } + else if (bestPoly->neis[j]) + { + // Internal edge + const unsigned int idx = (unsigned int)(bestPoly->neis[j]-1); + const dtPolyRef ref = m_nav->getPolyRefBase(bestTile) | idx; + if (filter->passFilter(ref, bestTile, &bestTile->polys[idx])) + continue; + } + + // Calc distance to the edge. + const float* vj = &bestTile->verts[bestPoly->verts[j]*3]; + const float* vi = &bestTile->verts[bestPoly->verts[i]*3]; + float tseg; + float distSqr = dtDistancePtSegSqr2D(centerPos, vj, vi, tseg); + + // Edge is too far, skip. + if (distSqr > radiusSqr) + continue; + + // Hit wall, update radius. + radiusSqr = distSqr; + // Calculate hit pos. + hitPos[0] = vj[0] + (vi[0] - vj[0])*tseg; + hitPos[1] = vj[1] + (vi[1] - vj[1])*tseg; + hitPos[2] = vj[2] + (vi[2] - vj[2])*tseg; + } + + for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next) + { + const dtLink* link = &bestTile->links[i]; + dtPolyRef neighbourRef = link->ref; + // Skip invalid neighbours and do not follow back to parent. + if (!neighbourRef || neighbourRef == parentRef) + continue; + + // Expand to neighbour. + const dtMeshTile* neighbourTile = 0; + const dtPoly* neighbourPoly = 0; + m_nav->getTileAndPolyByRefUnsafe(neighbourRef, &neighbourTile, &neighbourPoly); + + // Skip off-mesh connections. + if (neighbourPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) + continue; + + // Calc distance to the edge. + const float* va = &bestTile->verts[bestPoly->verts[link->edge]*3]; + const float* vb = &bestTile->verts[bestPoly->verts[(link->edge+1) % bestPoly->vertCount]*3]; + float tseg; + float distSqr = dtDistancePtSegSqr2D(centerPos, va, vb, tseg); + + // If the circle is not touching the next polygon, skip it. + if (distSqr > radiusSqr) + continue; + + if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly)) + continue; + + dtNode* neighbourNode = m_nodePool->getNode(neighbourRef); + if (!neighbourNode) + { + status |= DT_OUT_OF_NODES; + continue; + } + + if (neighbourNode->flags & DT_NODE_CLOSED) + continue; + + // Cost + if (neighbourNode->flags == 0) + { + getEdgeMidPoint(bestRef, bestPoly, bestTile, + neighbourRef, neighbourPoly, neighbourTile, neighbourNode->pos); + } + + const float total = bestNode->total + dtVdist(bestNode->pos, neighbourNode->pos); + + // The node is already in open list and the new result is worse, skip. + if ((neighbourNode->flags & DT_NODE_OPEN) && total >= neighbourNode->total) + continue; + + neighbourNode->id = neighbourRef; + neighbourNode->flags = (neighbourNode->flags & ~DT_NODE_CLOSED); + neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode); + neighbourNode->total = total; + + if (neighbourNode->flags & DT_NODE_OPEN) + { + m_openList->modify(neighbourNode); + } + else + { + neighbourNode->flags |= DT_NODE_OPEN; + m_openList->push(neighbourNode); + } + } + } + + // Calc hit normal. + dtVsub(hitNormal, centerPos, hitPos); + dtVnormalize(hitNormal); + + *hitDist = dtMathSqrtf(radiusSqr); + + return status; +} + +bool dtNavMeshQuery::isValidPolyRef(dtPolyRef ref, const dtQueryFilter* filter) const +{ + const dtMeshTile* tile = 0; + const dtPoly* poly = 0; + dtStatus status = m_nav->getTileAndPolyByRef(ref, &tile, &poly); + // If cannot get polygon, assume it does not exists and boundary is invalid. + if (dtStatusFailed(status)) + return false; + // If cannot pass filter, assume flags has changed and boundary is invalid. + if (!filter->passFilter(ref, tile, poly)) + return false; + return true; +} + +/// @par +/// +/// The closed list is the list of polygons that were fully evaluated during +/// the last navigation graph search. (A* or Dijkstra) +/// +bool dtNavMeshQuery::isInClosedList(dtPolyRef ref) const +{ + if (!m_nodePool) return false; + + dtNode* nodes[DT_MAX_STATES_PER_NODE]; + int n= m_nodePool->findNodes(ref, nodes, DT_MAX_STATES_PER_NODE); + + for (int i=0; iflags & DT_NODE_CLOSED) + return true; + } + + return false; +} diff --git a/external/recast/src/DetourNode.cpp b/external/recast/src/DetourNode.cpp new file mode 100644 index 0000000000..48abbba6b5 --- /dev/null +++ b/external/recast/src/DetourNode.cpp @@ -0,0 +1,200 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#include "DetourNode.h" +#include "DetourAlloc.h" +#include "DetourAssert.h" +#include "DetourCommon.h" +#include + +#ifdef DT_POLYREF64 +// From Thomas Wang, https://gist.github.com/badboy/6267743 +inline unsigned int dtHashRef(dtPolyRef a) +{ + a = (~a) + (a << 18); // a = (a << 18) - a - 1; + a = a ^ (a >> 31); + a = a * 21; // a = (a + (a << 2)) + (a << 4); + a = a ^ (a >> 11); + a = a + (a << 6); + a = a ^ (a >> 22); + return (unsigned int)a; +} +#else +inline unsigned int dtHashRef(dtPolyRef a) +{ + a += ~(a<<15); + a ^= (a>>10); + a += (a<<3); + a ^= (a>>6); + a += ~(a<<11); + a ^= (a>>16); + return (unsigned int)a; +} +#endif + +////////////////////////////////////////////////////////////////////////////////////////// +dtNodePool::dtNodePool(int maxNodes, int hashSize) : + m_nodes(0), + m_first(0), + m_next(0), + m_maxNodes(maxNodes), + m_hashSize(hashSize), + m_nodeCount(0) +{ + dtAssert(dtNextPow2(m_hashSize) == (unsigned int)m_hashSize); + // 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); + m_first = (dtNodeIndex*)dtAlloc(sizeof(dtNodeIndex)*hashSize, DT_ALLOC_PERM); + + dtAssert(m_nodes); + dtAssert(m_next); + dtAssert(m_first); + + memset(m_first, 0xff, sizeof(dtNodeIndex)*m_hashSize); + memset(m_next, 0xff, sizeof(dtNodeIndex)*m_maxNodes); +} + +dtNodePool::~dtNodePool() +{ + dtFree(m_nodes); + dtFree(m_next); + dtFree(m_first); +} + +void dtNodePool::clear() +{ + memset(m_first, 0xff, sizeof(dtNodeIndex)*m_hashSize); + m_nodeCount = 0; +} + +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, 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 && m_nodes[i].state == state) + return &m_nodes[i]; + i = m_next[i]; + } + + if (m_nodeCount >= m_maxNodes) + return 0; + + i = (dtNodeIndex)m_nodeCount; + m_nodeCount++; + + // Init node + node = &m_nodes[i]; + node->pidx = 0; + node->cost = 0; + node->total = 0; + node->id = id; + node->state = state; + node->flags = 0; + + m_next[i] = m_first[bucket]; + m_first[bucket] = i; + + return node; +} + + +////////////////////////////////////////////////////////////////////////////////////////// +dtNodeQueue::dtNodeQueue(int n) : + m_heap(0), + m_capacity(n), + m_size(0) +{ + dtAssert(m_capacity > 0); + + m_heap = (dtNode**)dtAlloc(sizeof(dtNode*)*(m_capacity+1), DT_ALLOC_PERM); + dtAssert(m_heap); +} + +dtNodeQueue::~dtNodeQueue() +{ + dtFree(m_heap); +} + +void dtNodeQueue::bubbleUp(int i, dtNode* node) +{ + int parent = (i-1)/2; + // note: (index > 0) means there is a parent + while ((i > 0) && (m_heap[parent]->total > node->total)) + { + m_heap[i] = m_heap[parent]; + i = parent; + parent = (i-1)/2; + } + m_heap[i] = node; +} + +void dtNodeQueue::trickleDown(int i, dtNode* node) +{ + int child = (i*2)+1; + while (child < m_size) + { + if (((child+1) < m_size) && + (m_heap[child]->total > m_heap[child+1]->total)) + { + child++; + } + m_heap[i] = m_heap[child]; + i = child; + child = (i*2)+1; + } + bubbleUp(i, node); +} diff --git a/external/recast/src/Recast.cpp b/external/recast/src/Recast.cpp new file mode 100644 index 0000000000..1b71710cdc --- /dev/null +++ b/external/recast/src/Recast.cpp @@ -0,0 +1,575 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#include +#define _USE_MATH_DEFINES +#include +#include +#include +#include +#include +#include "Recast.h" +#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 +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 +void rcDelete(T* ptr) { + if (ptr) { + ptr->~T(); + rcFree((void*)ptr); + } +} +} // namespace + + +float rcSqrt(float x) +{ + return sqrtf(x); +} + +/// @class rcContext +/// @par +/// +/// This class does not provide logging or timer functionality on its +/// own. Both must be provided by a concrete implementation +/// by overriding the protected member functions. Also, this class does not +/// provide an interface for extracting log messages. (Only adding them.) +/// So concrete implementations must provide one. +/// +/// If no logging or timers are required, just pass an instance of this +/// class through the Recast build process. +/// + +/// @par +/// +/// Example: +/// @code +/// // Where ctx is an instance of rcContext and filepath is a char array. +/// ctx->log(RC_LOG_ERROR, "buildTiledNavigation: Could not load '%s'", filepath); +/// @endcode +void rcContext::log(const rcLogCategory category, const char* format, ...) +{ + if (!m_logEnabled) + return; + static const int MSG_SIZE = 512; + char msg[MSG_SIZE]; + va_list ap; + va_start(ap, format); + int len = vsnprintf(msg, MSG_SIZE, format, ap); + if (len >= MSG_SIZE) + { + len = MSG_SIZE-1; + msg[MSG_SIZE-1] = '\0'; + } + va_end(ap); + doLog(category, msg, len); +} + +rcHeightfield* rcAllocHeightfield() +{ + return rcNew(RC_ALLOC_PERM); +} +rcHeightfield::rcHeightfield() + : width() + , height() + , bmin() + , bmax() + , cs() + , ch() + , spans() + , pools() + , freelist() +{ +} + +rcHeightfield::~rcHeightfield() +{ + // Delete span array. + rcFree(spans); + // Delete span pools. + while (pools) + { + rcSpanPool* next = pools->next; + rcFree(pools); + pools = next; + } +} + +void rcFreeHeightField(rcHeightfield* hf) +{ + rcDelete(hf); +} + +rcCompactHeightfield* rcAllocCompactHeightfield() +{ + return rcNew(RC_ALLOC_PERM); +} + +void rcFreeCompactHeightfield(rcCompactHeightfield* 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() +{ + return rcNew(RC_ALLOC_PERM); +} +void rcFreeHeightfieldLayerSet(rcHeightfieldLayerSet* lset) +{ + rcDelete(lset); +} + +rcHeightfieldLayerSet::rcHeightfieldLayerSet() + : layers(), nlayers() {} +rcHeightfieldLayerSet::~rcHeightfieldLayerSet() +{ + for (int i = 0; i < nlayers; ++i) + { + rcFree(layers[i].heights); + rcFree(layers[i].areas); + rcFree(layers[i].cons); + } + rcFree(layers); +} + + +rcContourSet* rcAllocContourSet() +{ + return rcNew(RC_ALLOC_PERM); +} +void rcFreeContourSet(rcContourSet* cset) +{ + rcDelete(cset); +} + +rcContourSet::rcContourSet() + : conts(), + nconts(), + bmin(), + bmax(), + cs(), + ch(), + width(), + height(), + borderSize(), + maxError() {} +rcContourSet::~rcContourSet() +{ + for (int i = 0; i < nconts; ++i) + { + rcFree(conts[i].verts); + rcFree(conts[i].rverts); + } + rcFree(conts); +} + + +rcPolyMesh* rcAllocPolyMesh() +{ + return rcNew(RC_ALLOC_PERM); +} +void rcFreePolyMesh(rcPolyMesh* 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() +{ + rcPolyMeshDetail* dmesh = (rcPolyMeshDetail*)rcAlloc(sizeof(rcPolyMeshDetail), RC_ALLOC_PERM); + memset(dmesh, 0, sizeof(rcPolyMeshDetail)); + return dmesh; +} + +void rcFreePolyMeshDetail(rcPolyMeshDetail* dmesh) +{ + if (!dmesh) return; + rcFree(dmesh->meshes); + rcFree(dmesh->verts); + rcFree(dmesh->tris); + rcFree(dmesh); +} + +void rcCalcBounds(const float* verts, int nv, float* bmin, float* bmax) +{ + // Calculate bounding box. + rcVcopy(bmin, verts); + rcVcopy(bmax, verts); + for (int i = 1; i < nv; ++i) + { + const float* v = &verts[i*3]; + rcVmin(bmin, v); + rcVmax(bmax, v); + } +} + +void rcCalcGridSize(const float* bmin, const float* bmax, float cs, int* w, int* h) +{ + *w = (int)((bmax[0] - bmin[0])/cs+0.5f); + *h = (int)((bmax[2] - bmin[2])/cs+0.5f); +} + +/// @par +/// +/// See the #rcConfig documentation for more information on the configuration parameters. +/// +/// @see rcAllocHeightfield, rcHeightfield +bool rcCreateHeightfield(rcContext* ctx, rcHeightfield& hf, int width, int height, + const float* bmin, const float* bmax, + float cs, float ch) +{ + rcIgnoreUnused(ctx); + + hf.width = width; + hf.height = height; + rcVcopy(hf.bmin, bmin); + rcVcopy(hf.bmax, bmax); + hf.cs = cs; + hf.ch = ch; + hf.spans = (rcSpan**)rcAlloc(sizeof(rcSpan*)*hf.width*hf.height, RC_ALLOC_PERM); + if (!hf.spans) + return false; + memset(hf.spans, 0, sizeof(rcSpan*)*hf.width*hf.height); + return true; +} + +static void calcTriNormal(const float* v0, const float* v1, const float* v2, float* norm) +{ + float e0[3], e1[3]; + rcVsub(e0, v1, v0); + rcVsub(e1, v2, v0); + rcVcross(norm, e0, e1); + rcVnormalize(norm); +} + +/// @par +/// +/// 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 int* tris, int nt, + unsigned char* areas) +{ + rcIgnoreUnused(ctx); + rcIgnoreUnused(nv); + + const float walkableThr = cosf(walkableSlopeAngle/180.0f*RC_PI); + + float norm[3]; + + for (int i = 0; i < nt; ++i) + { + const int* tri = &tris[i*3]; + calcTriNormal(&verts[tri[0]*3], &verts[tri[1]*3], &verts[tri[2]*3], norm); + // Check if the face is walkable. + if (norm[1] > walkableThr) + areas[i] = RC_WALKABLE_AREA; + } +} + +/// @par +/// +/// 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. +/// +/// @see rcHeightfield, rcClearUnwalkableTriangles, rcRasterizeTriangles +void rcClearUnwalkableTriangles(rcContext* ctx, const float walkableSlopeAngle, + const float* verts, int /*nv*/, + const int* tris, int nt, + unsigned char* areas) +{ + rcIgnoreUnused(ctx); + + const float walkableThr = cosf(walkableSlopeAngle/180.0f*RC_PI); + + float norm[3]; + + for (int i = 0; i < nt; ++i) + { + const int* tri = &tris[i*3]; + calcTriNormal(&verts[tri[0]*3], &verts[tri[1]*3], &verts[tri[2]*3], norm); + // Check if the face is walkable. + if (norm[1] <= walkableThr) + areas[i] = RC_NULL_AREA; + } +} + +int rcGetHeightFieldSpanCount(rcContext* ctx, rcHeightfield& hf) +{ + rcIgnoreUnused(ctx); + + const int w = hf.width; + const int h = hf.height; + int spanCount = 0; + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + { + for (rcSpan* s = hf.spans[x + y*w]; s; s = s->next) + { + if (s->area != RC_NULL_AREA) + spanCount++; + } + } + } + return spanCount; +} + +/// @par +/// +/// This is just the beginning of the process of fully building a compact heightfield. +/// 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. +/// +/// @see rcAllocCompactHeightfield, rcHeightfield, rcCompactHeightfield, rcConfig +bool rcBuildCompactHeightfield(rcContext* ctx, const int walkableHeight, const int walkableClimb, + rcHeightfield& hf, rcCompactHeightfield& chf) +{ + rcAssert(ctx); + + rcScopedTimer timer(ctx, RC_TIMER_BUILD_COMPACTHEIGHTFIELD); + + const int w = hf.width; + const int h = hf.height; + const int spanCount = rcGetHeightFieldSpanCount(ctx, hf); + + // Fill in header. + chf.width = w; + chf.height = h; + chf.spanCount = spanCount; + chf.walkableHeight = walkableHeight; + chf.walkableClimb = walkableClimb; + chf.maxRegions = 0; + rcVcopy(chf.bmin, hf.bmin); + rcVcopy(chf.bmax, hf.bmax); + chf.bmax[1] += walkableHeight*hf.ch; + chf.cs = hf.cs; + chf.ch = hf.ch; + chf.cells = (rcCompactCell*)rcAlloc(sizeof(rcCompactCell)*w*h, RC_ALLOC_PERM); + if (!chf.cells) + { + ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.cells' (%d)", w*h); + return false; + } + memset(chf.cells, 0, sizeof(rcCompactCell)*w*h); + chf.spans = (rcCompactSpan*)rcAlloc(sizeof(rcCompactSpan)*spanCount, RC_ALLOC_PERM); + if (!chf.spans) + { + ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.spans' (%d)", spanCount); + return false; + } + memset(chf.spans, 0, sizeof(rcCompactSpan)*spanCount); + chf.areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*spanCount, RC_ALLOC_PERM); + if (!chf.areas) + { + ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.areas' (%d)", spanCount); + return false; + } + memset(chf.areas, RC_NULL_AREA, sizeof(unsigned char)*spanCount); + + const int MAX_HEIGHT = 0xffff; + + // Fill in cells and spans. + int idx = 0; + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + { + const rcSpan* s = hf.spans[x + y*w]; + // If there are no spans at this cell, just leave the data to index=0, count=0. + if (!s) continue; + rcCompactCell& c = chf.cells[x+y*w]; + c.index = idx; + c.count = 0; + while (s) + { + if (s->area != RC_NULL_AREA) + { + const int bot = (int)s->smax; + const int top = s->next ? (int)s->next->smin : MAX_HEIGHT; + chf.spans[idx].y = (unsigned short)rcClamp(bot, 0, 0xffff); + chf.spans[idx].h = (unsigned char)rcClamp(top - bot, 0, 0xff); + chf.areas[idx] = s->area; + idx++; + c.count++; + } + s = s->next; + } + } + } + + // Find neighbour connections. + const int MAX_LAYERS = RC_NOT_CONNECTED-1; + int tooHighNeighbour = 0; + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + rcCompactSpan& s = chf.spans[i]; + + for (int dir = 0; dir < 4; ++dir) + { + rcSetCon(s, dir, RC_NOT_CONNECTED); + const int nx = x + rcGetDirOffsetX(dir); + const int ny = y + rcGetDirOffsetY(dir); + // First check that the neighbour cell is in bounds. + if (nx < 0 || ny < 0 || nx >= w || ny >= h) + continue; + + // Iterate over all neighbour spans and check if any of the is + // accessible from current cell. + const rcCompactCell& nc = chf.cells[nx+ny*w]; + for (int k = (int)nc.index, nk = (int)(nc.index+nc.count); k < nk; ++k) + { + const rcCompactSpan& ns = chf.spans[k]; + const int bot = rcMax(s.y, ns.y); + const int top = rcMin(s.y+s.h, ns.y+ns.h); + + // Check that the gap between the spans is walkable, + // and that the climb height between the gaps is not too high. + if ((top - bot) >= walkableHeight && rcAbs((int)ns.y - (int)s.y) <= walkableClimb) + { + // Mark direction as walkable. + const int lidx = k - (int)nc.index; + if (lidx < 0 || lidx > MAX_LAYERS) + { + tooHighNeighbour = rcMax(tooHighNeighbour, lidx); + continue; + } + rcSetCon(s, dir, lidx); + break; + } + } + + } + } + } + } + + if (tooHighNeighbour > MAX_LAYERS) + { + ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Heightfield has too many layers %d (max: %d)", + tooHighNeighbour, MAX_LAYERS); + } + + return true; +} + +/* +static int getHeightfieldMemoryUsage(const rcHeightfield& hf) +{ + int size = 0; + size += sizeof(hf); + size += hf.width * hf.height * sizeof(rcSpan*); + + rcSpanPool* pool = hf.pools; + while (pool) + { + size += (sizeof(rcSpanPool) - sizeof(rcSpan)) + sizeof(rcSpan)*RC_SPANS_PER_POOL; + pool = pool->next; + } + return size; +} + +static int getCompactHeightFieldMemoryusage(const rcCompactHeightfield& chf) +{ + int size = 0; + size += sizeof(rcCompactHeightfield); + size += sizeof(rcCompactSpan) * chf.spanCount; + size += sizeof(rcCompactCell) * chf.width * chf.height; + return size; +} +*/ diff --git a/external/recast/src/RecastAlloc.cpp b/external/recast/src/RecastAlloc.cpp new file mode 100644 index 0000000000..bdc366116e --- /dev/null +++ b/external/recast/src/RecastAlloc.cpp @@ -0,0 +1,60 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#include +#include +#include "RecastAlloc.h" +#include "RecastAssert.h" + +static void *rcAllocDefault(size_t size, rcAllocHint) +{ + return malloc(size); +} + +static void rcFreeDefault(void *ptr) +{ + free(ptr); +} + +static rcAllocFunc* sRecastAllocFunc = rcAllocDefault; +static rcFreeFunc* sRecastFreeFunc = rcFreeDefault; + +/// @see rcAlloc, rcFree +void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc) +{ + sRecastAllocFunc = allocFunc ? allocFunc : rcAllocDefault; + sRecastFreeFunc = freeFunc ? freeFunc : rcFreeDefault; +} + +/// @see rcAllocSetCustom +void* rcAlloc(size_t size, rcAllocHint hint) +{ + return sRecastAllocFunc(size, hint); +} + +/// @par +/// +/// @warning This function leaves the value of @p ptr unchanged. So it still +/// points to the same (now invalid) location, and not to null. +/// +/// @see rcAllocSetCustom +void rcFree(void* ptr) +{ + if (ptr) + sRecastFreeFunc(ptr); +} diff --git a/external/recast/src/RecastArea.cpp b/external/recast/src/RecastArea.cpp new file mode 100644 index 0000000000..97139cf996 --- /dev/null +++ b/external/recast/src/RecastArea.cpp @@ -0,0 +1,591 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#include +#define _USE_MATH_DEFINES +#include +#include +#include +#include +#include "Recast.h" +#include "RecastAlloc.h" +#include "RecastAssert.h" + +/// @par +/// +/// Basically, any spans that are closer to a boundary or obstruction than the specified radius +/// are marked as unwalkable. +/// +/// This method is usually called immediately after the heightfield has been built. +/// +/// @see rcCompactHeightfield, rcBuildCompactHeightfield, rcConfig::walkableRadius +bool rcErodeWalkableArea(rcContext* ctx, int radius, rcCompactHeightfield& chf) +{ + rcAssert(ctx); + + const int w = chf.width; + const int h = chf.height; + + rcScopedTimer timer(ctx, RC_TIMER_ERODE_AREA); + + unsigned char* dist = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP); + if (!dist) + { + ctx->log(RC_LOG_ERROR, "erodeWalkableArea: Out of memory 'dist' (%d).", chf.spanCount); + return false; + } + + // Init distance. + memset(dist, 0xff, sizeof(unsigned char)*chf.spanCount); + + // Mark boundary cells. + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + if (chf.areas[i] == RC_NULL_AREA) + { + dist[i] = 0; + } + else + { + const rcCompactSpan& s = chf.spans[i]; + int nc = 0; + for (int dir = 0; dir < 4; ++dir) + { + if (rcGetCon(s, dir) != RC_NOT_CONNECTED) + { + const int nx = x + rcGetDirOffsetX(dir); + const int ny = y + rcGetDirOffsetY(dir); + const int nidx = (int)chf.cells[nx+ny*w].index + rcGetCon(s, dir); + if (chf.areas[nidx] != RC_NULL_AREA) + { + nc++; + } + } + } + // At least one missing neighbour. + if (nc != 4) + dist[i] = 0; + } + } + } + } + + unsigned char nd; + + // Pass 1 + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + const rcCompactSpan& s = chf.spans[i]; + + if (rcGetCon(s, 0) != RC_NOT_CONNECTED) + { + // (-1,0) + const int ax = x + rcGetDirOffsetX(0); + const int ay = y + rcGetDirOffsetY(0); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0); + const rcCompactSpan& as = chf.spans[ai]; + nd = (unsigned char)rcMin((int)dist[ai]+2, 255); + if (nd < dist[i]) + dist[i] = nd; + + // (-1,-1) + if (rcGetCon(as, 3) != RC_NOT_CONNECTED) + { + const int aax = ax + rcGetDirOffsetX(3); + const int aay = ay + rcGetDirOffsetY(3); + const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 3); + nd = (unsigned char)rcMin((int)dist[aai]+3, 255); + if (nd < dist[i]) + dist[i] = nd; + } + } + if (rcGetCon(s, 3) != RC_NOT_CONNECTED) + { + // (0,-1) + const int ax = x + rcGetDirOffsetX(3); + const int ay = y + rcGetDirOffsetY(3); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3); + const rcCompactSpan& as = chf.spans[ai]; + nd = (unsigned char)rcMin((int)dist[ai]+2, 255); + if (nd < dist[i]) + dist[i] = nd; + + // (1,-1) + if (rcGetCon(as, 2) != RC_NOT_CONNECTED) + { + const int aax = ax + rcGetDirOffsetX(2); + const int aay = ay + rcGetDirOffsetY(2); + const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 2); + nd = (unsigned char)rcMin((int)dist[aai]+3, 255); + if (nd < dist[i]) + dist[i] = nd; + } + } + } + } + } + + // Pass 2 + for (int y = h-1; y >= 0; --y) + { + for (int x = w-1; x >= 0; --x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + const rcCompactSpan& s = chf.spans[i]; + + if (rcGetCon(s, 2) != RC_NOT_CONNECTED) + { + // (1,0) + const int ax = x + rcGetDirOffsetX(2); + const int ay = y + rcGetDirOffsetY(2); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 2); + const rcCompactSpan& as = chf.spans[ai]; + nd = (unsigned char)rcMin((int)dist[ai]+2, 255); + if (nd < dist[i]) + dist[i] = nd; + + // (1,1) + if (rcGetCon(as, 1) != RC_NOT_CONNECTED) + { + const int aax = ax + rcGetDirOffsetX(1); + const int aay = ay + rcGetDirOffsetY(1); + const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 1); + nd = (unsigned char)rcMin((int)dist[aai]+3, 255); + if (nd < dist[i]) + dist[i] = nd; + } + } + if (rcGetCon(s, 1) != RC_NOT_CONNECTED) + { + // (0,1) + const int ax = x + rcGetDirOffsetX(1); + const int ay = y + rcGetDirOffsetY(1); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 1); + const rcCompactSpan& as = chf.spans[ai]; + nd = (unsigned char)rcMin((int)dist[ai]+2, 255); + if (nd < dist[i]) + dist[i] = nd; + + // (-1,1) + if (rcGetCon(as, 0) != RC_NOT_CONNECTED) + { + const int aax = ax + rcGetDirOffsetX(0); + const int aay = ay + rcGetDirOffsetY(0); + const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 0); + nd = (unsigned char)rcMin((int)dist[aai]+3, 255); + if (nd < dist[i]) + dist[i] = nd; + } + } + } + } + } + + const unsigned char thr = (unsigned char)(radius*2); + for (int i = 0; i < chf.spanCount; ++i) + if (dist[i] < thr) + chf.areas[i] = RC_NULL_AREA; + + rcFree(dist); + + return true; +} + +static void insertSort(unsigned char* a, const int n) +{ + int i, j; + for (i = 1; i < n; i++) + { + const unsigned char value = a[i]; + for (j = i - 1; j >= 0 && a[j] > value; j--) + a[j+1] = a[j]; + a[j+1] = value; + } +} + +/// @par +/// +/// This filter is usually applied after applying area id's using functions +/// such as #rcMarkBoxArea, #rcMarkConvexPolyArea, and #rcMarkCylinderArea. +/// +/// @see rcCompactHeightfield +bool rcMedianFilterWalkableArea(rcContext* ctx, rcCompactHeightfield& chf) +{ + rcAssert(ctx); + + const int w = chf.width; + const int h = chf.height; + + rcScopedTimer timer(ctx, RC_TIMER_MEDIAN_AREA); + + unsigned char* areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP); + if (!areas) + { + ctx->log(RC_LOG_ERROR, "medianFilterWalkableArea: Out of memory 'areas' (%d).", chf.spanCount); + return false; + } + + // Init distance. + memset(areas, 0xff, sizeof(unsigned char)*chf.spanCount); + + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + const rcCompactSpan& s = chf.spans[i]; + if (chf.areas[i] == RC_NULL_AREA) + { + areas[i] = chf.areas[i]; + continue; + } + + unsigned char nei[9]; + for (int j = 0; j < 9; ++j) + nei[j] = chf.areas[i]; + + for (int dir = 0; dir < 4; ++dir) + { + if (rcGetCon(s, dir) != RC_NOT_CONNECTED) + { + const int ax = x + rcGetDirOffsetX(dir); + const int ay = y + rcGetDirOffsetY(dir); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir); + if (chf.areas[ai] != RC_NULL_AREA) + nei[dir*2+0] = chf.areas[ai]; + + const rcCompactSpan& as = chf.spans[ai]; + const int dir2 = (dir+1) & 0x3; + if (rcGetCon(as, dir2) != RC_NOT_CONNECTED) + { + const int ax2 = ax + rcGetDirOffsetX(dir2); + const int ay2 = ay + rcGetDirOffsetY(dir2); + const int ai2 = (int)chf.cells[ax2+ay2*w].index + rcGetCon(as, dir2); + if (chf.areas[ai2] != RC_NULL_AREA) + nei[dir*2+1] = chf.areas[ai2]; + } + } + } + insertSort(nei, 9); + areas[i] = nei[4]; + } + } + } + + memcpy(chf.areas, areas, sizeof(unsigned char)*chf.spanCount); + + rcFree(areas); + + return true; +} + +/// @par +/// +/// The value of spacial parameters are in world units. +/// +/// @see rcCompactHeightfield, rcMedianFilterWalkableArea +void rcMarkBoxArea(rcContext* ctx, const float* bmin, const float* bmax, unsigned char areaId, + rcCompactHeightfield& chf) +{ + rcAssert(ctx); + + 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); + int minz = (int)((bmin[2]-chf.bmin[2])/chf.cs); + int maxx = (int)((bmax[0]-chf.bmin[0])/chf.cs); + int maxy = (int)((bmax[1]-chf.bmin[1])/chf.ch); + int maxz = (int)((bmax[2]-chf.bmin[2])/chf.cs); + + if (maxx < 0) return; + if (minx >= chf.width) return; + if (maxz < 0) return; + if (minz >= chf.height) return; + + if (minx < 0) minx = 0; + if (maxx >= chf.width) maxx = chf.width-1; + if (minz < 0) minz = 0; + if (maxz >= chf.height) maxz = chf.height-1; + + for (int z = minz; z <= maxz; ++z) + { + for (int x = minx; x <= maxx; ++x) + { + const rcCompactCell& c = chf.cells[x+z*chf.width]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + rcCompactSpan& s = chf.spans[i]; + if ((int)s.y >= miny && (int)s.y <= maxy) + { + if (chf.areas[i] != RC_NULL_AREA) + chf.areas[i] = areaId; + } + } + } + } +} + + +static int pointInPoly(int nvert, const float* verts, const float* p) +{ + int i, j, c = 0; + for (i = 0, j = nvert-1; i < nvert; j = i++) + { + const float* vi = &verts[i*3]; + const float* vj = &verts[j*3]; + if (((vi[2] > p[2]) != (vj[2] > p[2])) && + (p[0] < (vj[0]-vi[0]) * (p[2]-vi[2]) / (vj[2]-vi[2]) + vi[0]) ) + c = !c; + } + return c; +} + +/// @par +/// +/// The value of spacial parameters are in world units. +/// +/// The y-values of the polygon vertices are ignored. So the polygon is effectively +/// projected onto the xz-plane at @p hmin, then extruded to @p hmax. +/// +/// @see rcCompactHeightfield, rcMedianFilterWalkableArea +void rcMarkConvexPolyArea(rcContext* ctx, const float* verts, const int nverts, + const float hmin, const float hmax, unsigned char areaId, + rcCompactHeightfield& chf) +{ + rcAssert(ctx); + + rcScopedTimer timer(ctx, RC_TIMER_MARK_CONVEXPOLY_AREA); + + float bmin[3], bmax[3]; + rcVcopy(bmin, verts); + rcVcopy(bmax, verts); + for (int i = 1; i < nverts; ++i) + { + rcVmin(bmin, &verts[i*3]); + rcVmax(bmax, &verts[i*3]); + } + bmin[1] = hmin; + bmax[1] = hmax; + + int minx = (int)((bmin[0]-chf.bmin[0])/chf.cs); + int miny = (int)((bmin[1]-chf.bmin[1])/chf.ch); + int minz = (int)((bmin[2]-chf.bmin[2])/chf.cs); + int maxx = (int)((bmax[0]-chf.bmin[0])/chf.cs); + int maxy = (int)((bmax[1]-chf.bmin[1])/chf.ch); + int maxz = (int)((bmax[2]-chf.bmin[2])/chf.cs); + + if (maxx < 0) return; + if (minx >= chf.width) return; + if (maxz < 0) return; + if (minz >= chf.height) return; + + if (minx < 0) minx = 0; + if (maxx >= chf.width) maxx = chf.width-1; + if (minz < 0) minz = 0; + if (maxz >= chf.height) maxz = chf.height-1; + + + // TODO: Optimize. + for (int z = minz; z <= maxz; ++z) + { + for (int x = minx; x <= maxx; ++x) + { + const rcCompactCell& c = chf.cells[x+z*chf.width]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + rcCompactSpan& s = chf.spans[i]; + if (chf.areas[i] == RC_NULL_AREA) + continue; + if ((int)s.y >= miny && (int)s.y <= maxy) + { + float p[3]; + p[0] = chf.bmin[0] + (x+0.5f)*chf.cs; + p[1] = 0; + p[2] = chf.bmin[2] + (z+0.5f)*chf.cs; + + if (pointInPoly(nverts, verts, p)) + { + chf.areas[i] = areaId; + } + } + } + } + } +} + +int rcOffsetPoly(const float* verts, const int nverts, const float offset, + float* outVerts, const int maxOutVerts) +{ + const float MITER_LIMIT = 1.20f; + + int n = 0; + + for (int i = 0; i < nverts; i++) + { + const int a = (i+nverts-1) % nverts; + const int b = i; + const int c = (i+1) % nverts; + const float* va = &verts[a*3]; + const float* vb = &verts[b*3]; + const float* vc = &verts[c*3]; + float dx0 = vb[0] - va[0]; + float dy0 = vb[2] - va[2]; + float d0 = dx0*dx0 + dy0*dy0; + if (d0 > 1e-6f) + { + d0 = 1.0f/rcSqrt(d0); + dx0 *= d0; + dy0 *= d0; + } + float dx1 = vc[0] - vb[0]; + float dy1 = vc[2] - vb[2]; + float d1 = dx1*dx1 + dy1*dy1; + if (d1 > 1e-6f) + { + d1 = 1.0f/rcSqrt(d1); + dx1 *= d1; + dy1 *= d1; + } + const float dlx0 = -dy0; + const float dly0 = dx0; + const float dlx1 = -dy1; + const float dly1 = dx1; + float cross = dx1*dy0 - dx0*dy1; + float dmx = (dlx0 + dlx1) * 0.5f; + float dmy = (dly0 + dly1) * 0.5f; + float dmr2 = dmx*dmx + dmy*dmy; + bool bevel = dmr2 * MITER_LIMIT*MITER_LIMIT < 1.0f; + if (dmr2 > 1e-6f) + { + const float scale = 1.0f / dmr2; + dmx *= scale; + dmy *= scale; + } + + if (bevel && cross < 0.0f) + { + if (n+2 >= maxOutVerts) + return 0; + float d = (1.0f - (dx0*dx1 + dy0*dy1))*0.5f; + outVerts[n*3+0] = vb[0] + (-dlx0+dx0*d)*offset; + outVerts[n*3+1] = vb[1]; + outVerts[n*3+2] = vb[2] + (-dly0+dy0*d)*offset; + n++; + outVerts[n*3+0] = vb[0] + (-dlx1-dx1*d)*offset; + outVerts[n*3+1] = vb[1]; + outVerts[n*3+2] = vb[2] + (-dly1-dy1*d)*offset; + n++; + } + else + { + if (n+1 >= maxOutVerts) + return 0; + outVerts[n*3+0] = vb[0] - dmx*offset; + outVerts[n*3+1] = vb[1]; + outVerts[n*3+2] = vb[2] - dmy*offset; + n++; + } + } + + return n; +} + + +/// @par +/// +/// The value of spacial parameters are in world units. +/// +/// @see rcCompactHeightfield, rcMedianFilterWalkableArea +void rcMarkCylinderArea(rcContext* ctx, const float* pos, + const float r, const float h, unsigned char areaId, + rcCompactHeightfield& chf) +{ + rcAssert(ctx); + + rcScopedTimer timer(ctx, RC_TIMER_MARK_CYLINDER_AREA); + + float bmin[3], bmax[3]; + bmin[0] = pos[0] - r; + bmin[1] = pos[1]; + bmin[2] = pos[2] - r; + bmax[0] = pos[0] + r; + bmax[1] = pos[1] + h; + bmax[2] = pos[2] + r; + const float r2 = r*r; + + int minx = (int)((bmin[0]-chf.bmin[0])/chf.cs); + int miny = (int)((bmin[1]-chf.bmin[1])/chf.ch); + int minz = (int)((bmin[2]-chf.bmin[2])/chf.cs); + int maxx = (int)((bmax[0]-chf.bmin[0])/chf.cs); + int maxy = (int)((bmax[1]-chf.bmin[1])/chf.ch); + int maxz = (int)((bmax[2]-chf.bmin[2])/chf.cs); + + if (maxx < 0) return; + if (minx >= chf.width) return; + if (maxz < 0) return; + if (minz >= chf.height) return; + + if (minx < 0) minx = 0; + if (maxx >= chf.width) maxx = chf.width-1; + if (minz < 0) minz = 0; + if (maxz >= chf.height) maxz = chf.height-1; + + + for (int z = minz; z <= maxz; ++z) + { + for (int x = minx; x <= maxx; ++x) + { + const rcCompactCell& c = chf.cells[x+z*chf.width]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + rcCompactSpan& s = chf.spans[i]; + + if (chf.areas[i] == RC_NULL_AREA) + continue; + + if ((int)s.y >= miny && (int)s.y <= maxy) + { + const float sx = chf.bmin[0] + (x+0.5f)*chf.cs; + const float sz = chf.bmin[2] + (z+0.5f)*chf.cs; + const float dx = sx - pos[0]; + const float dz = sz - pos[2]; + + if (dx*dx + dz*dz < r2) + { + chf.areas[i] = areaId; + } + } + } + } + } +} diff --git a/external/recast/src/RecastAssert.cpp b/external/recast/src/RecastAssert.cpp new file mode 100644 index 0000000000..6297d42023 --- /dev/null +++ b/external/recast/src/RecastAssert.cpp @@ -0,0 +1,35 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#include "RecastAssert.h" + +#ifndef NDEBUG + +static rcAssertFailFunc* sRecastAssertFailFunc = 0; + +void rcAssertFailSetCustom(rcAssertFailFunc *assertFailFunc) +{ + sRecastAssertFailFunc = assertFailFunc; +} + +rcAssertFailFunc* rcAssertFailGetCustom() +{ + return sRecastAssertFailFunc; +} + +#endif diff --git a/external/recast/src/RecastContour.cpp b/external/recast/src/RecastContour.cpp new file mode 100644 index 0000000000..277ab01501 --- /dev/null +++ b/external/recast/src/RecastContour.cpp @@ -0,0 +1,1105 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#define _USE_MATH_DEFINES +#include +#include +#include +#include +#include "Recast.h" +#include "RecastAlloc.h" +#include "RecastAssert.h" + + +static int getCornerHeight(int x, int y, int i, int dir, + const rcCompactHeightfield& chf, + bool& isBorderVertex) +{ + const rcCompactSpan& s = chf.spans[i]; + int ch = (int)s.y; + int dirp = (dir+1) & 0x3; + + 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. + regs[0] = chf.spans[i].reg | (chf.areas[i] << 16); + + if (rcGetCon(s, dir) != RC_NOT_CONNECTED) + { + const int ax = x + rcGetDirOffsetX(dir); + const int ay = y + rcGetDirOffsetY(dir); + const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir); + const rcCompactSpan& as = chf.spans[ai]; + ch = rcMax(ch, (int)as.y); + regs[1] = chf.spans[ai].reg | (chf.areas[ai] << 16); + if (rcGetCon(as, dirp) != RC_NOT_CONNECTED) + { + const int ax2 = ax + rcGetDirOffsetX(dirp); + const int ay2 = ay + rcGetDirOffsetY(dirp); + const int ai2 = (int)chf.cells[ax2+ay2*chf.width].index + rcGetCon(as, dirp); + const rcCompactSpan& as2 = chf.spans[ai2]; + ch = rcMax(ch, (int)as2.y); + regs[2] = chf.spans[ai2].reg | (chf.areas[ai2] << 16); + } + } + if (rcGetCon(s, dirp) != RC_NOT_CONNECTED) + { + const int ax = x + rcGetDirOffsetX(dirp); + const int ay = y + rcGetDirOffsetY(dirp); + const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dirp); + const rcCompactSpan& as = chf.spans[ai]; + ch = rcMax(ch, (int)as.y); + regs[3] = chf.spans[ai].reg | (chf.areas[ai] << 16); + if (rcGetCon(as, dir) != RC_NOT_CONNECTED) + { + const int ax2 = ax + rcGetDirOffsetX(dir); + const int ay2 = ay + rcGetDirOffsetY(dir); + const int ai2 = (int)chf.cells[ax2+ay2*chf.width].index + rcGetCon(as, dir); + const rcCompactSpan& as2 = chf.spans[ai2]; + ch = rcMax(ch, (int)as2.y); + regs[2] = chf.spans[ai2].reg | (chf.areas[ai2] << 16); + } + } + + // Check if the vertex is special edge vertex, these vertices will be removed later. + for (int j = 0; j < 4; ++j) + { + const int a = j; + const int b = (j+1) & 0x3; + const int c = (j+2) & 0x3; + const int d = (j+3) & 0x3; + + // The vertex is a border vertex there are two same exterior cells in a row, + // followed by two interior cells and none of the regions are out of bounds. + const bool twoSameExts = (regs[a] & regs[b] & RC_BORDER_REG) != 0 && regs[a] == regs[b]; + const bool twoInts = ((regs[c] | regs[d]) & RC_BORDER_REG) == 0; + const bool intsSameArea = (regs[c]>>16) == (regs[d]>>16); + const bool noZeros = regs[a] != 0 && regs[b] != 0 && regs[c] != 0 && regs[d] != 0; + if (twoSameExts && twoInts && intsSameArea && noZeros) + { + isBorderVertex = true; + break; + } + } + + return ch; +} + +static void walkContour(int x, int y, int i, + rcCompactHeightfield& chf, + unsigned char* flags, rcIntArray& points) +{ + // Choose the first non-connected edge + unsigned char dir = 0; + while ((flags[i] & (1 << dir)) == 0) + dir++; + + unsigned char startDir = dir; + int starti = i; + + const unsigned char area = chf.areas[i]; + + int iter = 0; + while (++iter < 40000) + { + if (flags[i] & (1 << dir)) + { + // Choose the edge corner + bool isBorderVertex = false; + bool isAreaBorder = false; + int px = x; + int py = getCornerHeight(x, y, i, dir, chf, isBorderVertex); + int pz = y; + switch(dir) + { + case 0: pz++; break; + case 1: px++; pz++; break; + case 2: px++; break; + } + int r = 0; + const rcCompactSpan& s = chf.spans[i]; + if (rcGetCon(s, dir) != RC_NOT_CONNECTED) + { + const int ax = x + rcGetDirOffsetX(dir); + const int ay = y + rcGetDirOffsetY(dir); + const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir); + r = (int)chf.spans[ai].reg; + if (area != chf.areas[ai]) + isAreaBorder = true; + } + if (isBorderVertex) + r |= RC_BORDER_VERTEX; + if (isAreaBorder) + r |= RC_AREA_BORDER; + points.push(px); + points.push(py); + points.push(pz); + points.push(r); + + flags[i] &= ~(1 << dir); // Remove visited edges + dir = (dir+1) & 0x3; // Rotate CW + } + else + { + int ni = -1; + const int nx = x + rcGetDirOffsetX(dir); + const int ny = y + rcGetDirOffsetY(dir); + const rcCompactSpan& s = chf.spans[i]; + if (rcGetCon(s, dir) != RC_NOT_CONNECTED) + { + const rcCompactCell& nc = chf.cells[nx+ny*chf.width]; + ni = (int)nc.index + rcGetCon(s, dir); + } + if (ni == -1) + { + // Should not happen. + return; + } + x = nx; + y = ny; + i = ni; + dir = (dir+3) & 0x3; // Rotate CCW + } + + if (starti == i && startDir == dir) + { + break; + } + } +} + +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 pqz = (float)(qz - pz); + float dx = (float)(x - px); + float dz = (float)(z - pz); + float d = pqx*pqx + pqz*pqz; + float t = pqx*dx + pqz*dz; + if (d > 0) + t /= d; + if (t < 0) + t = 0; + else if (t > 1) + t = 1; + + dx = px + t*pqx - x; + dz = pz + t*pqz - z; + + return dx*dx + dz*dz; +} + +static void simplifyContour(rcIntArray& points, rcIntArray& simplified, + const float maxError, const int maxEdgeLen, const int buildFlags) +{ + // Add initial points. + bool hasConnections = false; + for (int i = 0; i < points.size(); i += 4) + { + if ((points[i+3] & RC_CONTOUR_REG_MASK) != 0) + { + hasConnections = true; + break; + } + } + + if (hasConnections) + { + // The contour has some portals to other regions. + // Add a new point to every location where the region changes. + for (int i = 0, ni = points.size()/4; i < ni; ++i) + { + int ii = (i+1) % ni; + const bool differentRegs = (points[i*4+3] & RC_CONTOUR_REG_MASK) != (points[ii*4+3] & RC_CONTOUR_REG_MASK); + const bool areaBorders = (points[i*4+3] & RC_AREA_BORDER) != (points[ii*4+3] & RC_AREA_BORDER); + if (differentRegs || areaBorders) + { + simplified.push(points[i*4+0]); + simplified.push(points[i*4+1]); + simplified.push(points[i*4+2]); + simplified.push(i); + } + } + } + + if (simplified.size() == 0) + { + // If there is no connections at all, + // create some initial points for the simplification process. + // Find lower-left and upper-right vertices of the contour. + int llx = points[0]; + int lly = points[1]; + int llz = points[2]; + int lli = 0; + int urx = points[0]; + int ury = points[1]; + int urz = points[2]; + int uri = 0; + for (int i = 0; i < points.size(); i += 4) + { + int x = points[i+0]; + int y = points[i+1]; + int z = points[i+2]; + if (x < llx || (x == llx && z < llz)) + { + llx = x; + lly = y; + llz = z; + lli = i/4; + } + if (x > urx || (x == urx && z > urz)) + { + urx = x; + ury = y; + urz = z; + uri = i/4; + } + } + simplified.push(llx); + simplified.push(lly); + simplified.push(llz); + simplified.push(lli); + + simplified.push(urx); + simplified.push(ury); + simplified.push(urz); + simplified.push(uri); + } + + // Add points until all raw points are within + // error tolerance to the simplified shape. + const int pn = points.size()/4; + for (int i = 0; i < simplified.size()/4; ) + { + int ii = (i+1) % (simplified.size()/4); + + int ax = simplified[i*4+0]; + int az = simplified[i*4+2]; + int ai = simplified[i*4+3]; + + 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. + if (bx > ax || (bx == ax && bz > az)) + { + cinc = 1; + ci = (ai+cinc) % pn; + endi = bi; + } + else + { + cinc = pn-1; + ci = (bi+cinc) % pn; + endi = ai; + rcSwap(ax, bx); + rcSwap(az, bz); + } + + // Tessellate only outer edges or edges between areas. + if ((points[ci*4+3] & RC_CONTOUR_REG_MASK) == 0 || + (points[ci*4+3] & RC_AREA_BORDER)) + { + while (ci != endi) + { + float d = distancePtSeg(points[ci*4+0], points[ci*4+2], ax, az, bx, bz); + if (d > maxd) + { + maxd = d; + maxi = ci; + } + ci = (ci+cinc) % pn; + } + } + + + // If the max deviation is larger than accepted error, + // add new point, else continue to next segment. + if (maxi != -1 && maxd > (maxError*maxError)) + { + // Add space for the new point. + simplified.resize(simplified.size()+4); + const int n = simplified.size()/4; + for (int j = n-1; j > i; --j) + { + simplified[j*4+0] = simplified[(j-1)*4+0]; + 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]; + } + // Add the point. + simplified[(i+1)*4+0] = points[maxi*4+0]; + simplified[(i+1)*4+1] = points[maxi*4+1]; + simplified[(i+1)*4+2] = points[maxi*4+2]; + simplified[(i+1)*4+3] = maxi; + } + else + { + ++i; + } + } + + // Split too long edges. + if (maxEdgeLen > 0 && (buildFlags & (RC_CONTOUR_TESS_WALL_EDGES|RC_CONTOUR_TESS_AREA_EDGES)) != 0) + { + for (int i = 0; i < simplified.size()/4; ) + { + const 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]; + + // Find maximum deviation from the segment. + int maxi = -1; + int ci = (ai+1) % pn; + + // Tessellate only outer edges or edges between areas. + bool tess = false; + // Wall edges. + if ((buildFlags & RC_CONTOUR_TESS_WALL_EDGES) && (points[ci*4+3] & RC_CONTOUR_REG_MASK) == 0) + tess = true; + // Edges between areas. + if ((buildFlags & RC_CONTOUR_TESS_AREA_EDGES) && (points[ci*4+3] & RC_AREA_BORDER)) + tess = true; + + if (tess) + { + int dx = bx - ax; + int dz = bz - az; + if (dx*dx + dz*dz > maxEdgeLen*maxEdgeLen) + { + // Round based on the segments in lexilogical order so that the + // max tesselation is consistent regardles in which direction + // segments are traversed. + const int n = bi < ai ? (bi+pn - ai) : (bi - ai); + if (n > 1) + { + if (bx > ax || (bx == ax && bz > az)) + maxi = (ai + n/2) % pn; + else + maxi = (ai + (n+1)/2) % pn; + } + } + } + + // If the max deviation is larger than accepted error, + // add new point, else continue to next segment. + if (maxi != -1) + { + // Add space for the new point. + simplified.resize(simplified.size()+4); + const int n = simplified.size()/4; + for (int j = n-1; j > i; --j) + { + simplified[j*4+0] = simplified[(j-1)*4+0]; + 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]; + } + // Add the point. + simplified[(i+1)*4+0] = points[maxi*4+0]; + simplified[(i+1)*4+1] = points[maxi*4+1]; + simplified[(i+1)*4+2] = points[maxi*4+2]; + simplified[(i+1)*4+3] = maxi; + } + else + { + ++i; + } + } + } + + for (int i = 0; i < simplified.size()/4; ++i) + { + // The edge vertex flag is take from the current raw point, + // and the neighbour region is take from the next raw point. + const int ai = (simplified[i*4+3]+1) % pn; + const int bi = simplified[i*4+3]; + simplified[i*4+3] = (points[ai*4+3] & (RC_CONTOUR_REG_MASK|RC_AREA_BORDER)) | (points[bi*4+3] & RC_BORDER_VERTEX); + } + +} + +static int calcAreaOfPolygon2D(const int* verts, const int nverts) +{ + int area = 0; + for (int i = 0, j = nverts-1; i < nverts; j=i++) + { + const int* vi = &verts[i*4]; + const int* vj = &verts[j*4]; + area += vi[0] * vj[2] - vj[0] * vi[2]; + } + return (area+1) / 2; +} + +// 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) +{ + // 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 bool inCone(int i, int n, const int* verts, const int* pj) +{ + 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) + { + int ni = next(i, npts); + + if (vequal(&simplified[i*4], &simplified[ni*4])) + { + // 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); + 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) + { + int* dst = &verts[nv*4]; + const int* src = &ca.verts[((ia+i)%ca.nverts)*4]; + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + nv++; + } + + // Copy contour B + for (int i = 0; i <= cb.nverts; ++i) + { + int* dst = &verts[nv*4]; + const int* src = &cb.verts[((ib+i)%cb.nverts)*4]; + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + nv++; + } + + rcFree(ca.verts); + ca.verts = verts; + ca.nverts = nv; + + rcFree(cb.verts); + cb.verts = 0; + cb.nverts = 0; + + 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, ®ion.holes[i].minx, ®ion.holes[i].minz, ®ion.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 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. +/// (They are considered mandatory vertices.) +/// +/// Setting @p maxEdgeLength to zero will disabled the edge length feature. +/// +/// See the #rcConfig documentation for more information on the configuration parameters. +/// +/// @see rcAllocContourSet, rcCompactHeightfield, rcContourSet, rcConfig +bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf, + const float maxError, const int maxEdgeLen, + rcContourSet& cset, const int buildFlags) +{ + rcAssert(ctx); + + const int w = chf.width; + const int h = chf.height; + const int borderSize = chf.borderSize; + + rcScopedTimer timer(ctx, RC_TIMER_BUILD_CONTOURS); + + rcVcopy(cset.bmin, chf.bmin); + rcVcopy(cset.bmax, chf.bmax); + if (borderSize > 0) + { + // If the heightfield was build with bordersize, remove the offset. + const float pad = borderSize*chf.cs; + cset.bmin[0] += pad; + cset.bmin[2] += pad; + cset.bmax[0] -= pad; + cset.bmax[2] -= pad; + } + cset.cs = chf.cs; + cset.ch = chf.ch; + cset.width = chf.width - chf.borderSize*2; + cset.height = chf.height - chf.borderSize*2; + cset.borderSize = chf.borderSize; + cset.maxError = maxError; + + int maxContours = rcMax((int)chf.maxRegions, 8); + cset.conts = (rcContour*)rcAlloc(sizeof(rcContour)*maxContours, RC_ALLOC_PERM); + if (!cset.conts) + return false; + cset.nconts = 0; + + rcScopedDelete flags((unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP)); + if (!flags) + { + ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'flags' (%d).", chf.spanCount); + return false; + } + + ctx->startTimer(RC_TIMER_BUILD_CONTOURS_TRACE); + + // Mark boundaries. + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + unsigned char res = 0; + const rcCompactSpan& s = chf.spans[i]; + if (!chf.spans[i].reg || (chf.spans[i].reg & RC_BORDER_REG)) + { + flags[i] = 0; + continue; + } + for (int dir = 0; dir < 4; ++dir) + { + unsigned short r = 0; + 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); + r = chf.spans[ai].reg; + } + if (r == chf.spans[i].reg) + res |= (1 << dir); + } + flags[i] = res ^ 0xf; // Inverse, mark non connected edges. + } + } + } + + ctx->stopTimer(RC_TIMER_BUILD_CONTOURS_TRACE); + + rcIntArray verts(256); + rcIntArray simplified(64); + + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + if (flags[i] == 0 || flags[i] == 0xf) + { + flags[i] = 0; + continue; + } + const unsigned short reg = chf.spans[i].reg; + if (!reg || (reg & RC_BORDER_REG)) + continue; + const unsigned char area = chf.areas[i]; + + verts.resize(0); + simplified.resize(0); + + ctx->startTimer(RC_TIMER_BUILD_CONTOURS_TRACE); + walkContour(x, y, i, chf, flags, verts); + ctx->stopTimer(RC_TIMER_BUILD_CONTOURS_TRACE); + + ctx->startTimer(RC_TIMER_BUILD_CONTOURS_SIMPLIFY); + simplifyContour(verts, simplified, maxError, maxEdgeLen, buildFlags); + removeDegenerateSegments(simplified); + ctx->stopTimer(RC_TIMER_BUILD_CONTOURS_SIMPLIFY); + + + // Store region->contour remap info. + // Create contour. + if (simplified.size()/4 >= 3) + { + if (cset.nconts >= maxContours) + { + // Allocate more contours. + // This happens when a region has holes. + const int oldMax = maxContours; + maxContours *= 2; + rcContour* newConts = (rcContour*)rcAlloc(sizeof(rcContour)*maxContours, RC_ALLOC_PERM); + for (int j = 0; j < cset.nconts; ++j) + { + newConts[j] = cset.conts[j]; + // Reset source pointers to prevent data deletion. + cset.conts[j].verts = 0; + cset.conts[j].rverts = 0; + } + rcFree(cset.conts); + cset.conts = newConts; + + ctx->log(RC_LOG_WARNING, "rcBuildContours: Expanding max contours from %d to %d.", oldMax, maxContours); + } + + rcContour* cont = &cset.conts[cset.nconts++]; + + cont->nverts = simplified.size()/4; + cont->verts = (int*)rcAlloc(sizeof(int)*cont->nverts*4, RC_ALLOC_PERM); + if (!cont->verts) + { + ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'verts' (%d).", cont->nverts); + return false; + } + memcpy(cont->verts, &simplified[0], sizeof(int)*cont->nverts*4); + if (borderSize > 0) + { + // If the heightfield was build with bordersize, remove the offset. + for (int j = 0; j < cont->nverts; ++j) + { + int* v = &cont->verts[j*4]; + v[0] -= borderSize; + v[2] -= borderSize; + } + } + + cont->nrverts = verts.size()/4; + cont->rverts = (int*)rcAlloc(sizeof(int)*cont->nrverts*4, RC_ALLOC_PERM); + if (!cont->rverts) + { + ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'rverts' (%d).", cont->nrverts); + return false; + } + memcpy(cont->rverts, &verts[0], sizeof(int)*cont->nrverts*4); + if (borderSize > 0) + { + // If the heightfield was build with bordersize, remove the offset. + for (int j = 0; j < cont->nrverts; ++j) + { + int* v = &cont->rverts[j*4]; + v[0] -= borderSize; + v[2] -= borderSize; + } + } + + cont->reg = reg; + cont->area = area; + } + } + } + } + + // Merge holes if needed. + if (cset.nconts > 0) + { + // Calculate winding of all polygons. + rcScopedDelete winding((char*)rcAlloc(sizeof(char)*cset.nconts, RC_ALLOC_TEMP)); + if (!winding) + { + ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'hole' (%d).", cset.nconts); + return false; + } + int nholes = 0; + for (int i = 0; i < cset.nconts; ++i) + { + 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 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 holes((rcContourHole*)rcAlloc(sizeof(rcContourHole)*cset.nconts, RC_ALLOC_TEMP)); + if (!holes) + { + ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'holes' (%d).", cset.nconts); + 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) + { + 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; + } + } + for (int i = 0; i < cset.nconts; ++i) + { + rcContour& cont = cset.conts[i]; + rcContourRegion& reg = regions[cont.reg]; + if (winding[i] < 0) + reg.holes[reg.nholes++].contour = &cont; + } + + // Finally merge each regions holes into the outline. + for (int i = 0; i < nregions; i++) + { + rcContourRegion& reg = regions[i]; + if (!reg.nholes) continue; + + if (reg.outline) + { + mergeRegionHoles(ctx, reg); + } + else + { + // 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); + } + } + } + + } + + return true; +} diff --git a/external/recast/src/RecastFilter.cpp b/external/recast/src/RecastFilter.cpp new file mode 100644 index 0000000000..9d3e63c482 --- /dev/null +++ b/external/recast/src/RecastFilter.cpp @@ -0,0 +1,202 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#define _USE_MATH_DEFINES +#include +#include +#include "Recast.h" +#include "RecastAssert.h" + +/// @par +/// +/// Allows the formation of walkable regions that will flow over low lying +/// objects such as curbs, and up structures such as stairways. +/// +/// Two neighboring spans are walkable if: rcAbs(currentSpan.smax - neighborSpan.smax) < waklableClimb +/// +/// @warning Will override the effect of #rcFilterLedgeSpans. So if both filters are used, call +/// #rcFilterLedgeSpans after calling this filter. +/// +/// @see rcHeightfield, rcConfig +void rcFilterLowHangingWalkableObstacles(rcContext* ctx, const int walkableClimb, rcHeightfield& solid) +{ + rcAssert(ctx); + + rcScopedTimer timer(ctx, RC_TIMER_FILTER_LOW_OBSTACLES); + + const int w = solid.width; + const int h = solid.height; + + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + { + rcSpan* ps = 0; + bool previousWalkable = false; + unsigned char previousArea = RC_NULL_AREA; + + for (rcSpan* s = solid.spans[x + y*w]; s; ps = s, s = s->next) + { + const bool walkable = s->area != RC_NULL_AREA; + // If current span is not walkable, but there is walkable + // span just below it, mark the span above it walkable too. + if (!walkable && previousWalkable) + { + if (rcAbs((int)s->smax - (int)ps->smax) <= walkableClimb) + s->area = previousArea; + } + // Copy walkable flag so that it cannot propagate + // past multiple non-walkable objects. + previousWalkable = walkable; + previousArea = s->area; + } + } + } +} + +/// @par +/// +/// A ledge is a span with one or more neighbors whose maximum is further away than @p walkableClimb +/// from the current span's maximum. +/// This method removes the impact of the overestimation of conservative voxelization +/// so the resulting mesh will not have regions hanging in the air over ledges. +/// +/// A span is a ledge if: rcAbs(currentSpan.smax - neighborSpan.smax) > walkableClimb +/// +/// @see rcHeightfield, rcConfig +void rcFilterLedgeSpans(rcContext* ctx, const int walkableHeight, const int walkableClimb, + rcHeightfield& solid) +{ + rcAssert(ctx); + + rcScopedTimer timer(ctx, RC_TIMER_FILTER_BORDER); + + const int w = solid.width; + const int h = solid.height; + const int MAX_HEIGHT = 0xffff; + + // Mark border spans. + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + { + for (rcSpan* s = solid.spans[x + y*w]; s; s = s->next) + { + // Skip non walkable spans. + if (s->area == RC_NULL_AREA) + continue; + + const int bot = (int)(s->smax); + const int top = s->next ? (int)(s->next->smin) : MAX_HEIGHT; + + // Find neighbours minimum height. + int minh = MAX_HEIGHT; + + // Min and max height of accessible neighbours. + int asmin = s->smax; + int asmax = s->smax; + + for (int dir = 0; dir < 4; ++dir) + { + int dx = x + rcGetDirOffsetX(dir); + int dy = y + rcGetDirOffsetY(dir); + // Skip neighbours which are out of bounds. + if (dx < 0 || dy < 0 || dx >= w || dy >= h) + { + minh = rcMin(minh, -walkableClimb - bot); + continue; + } + + // From minus infinity to the first span. + rcSpan* ns = solid.spans[dx + dy*w]; + int nbot = -walkableClimb; + int ntop = ns ? (int)ns->smin : MAX_HEIGHT; + // Skip neightbour if the gap between the spans is too small. + if (rcMin(top,ntop) - rcMax(bot,nbot) > walkableHeight) + minh = rcMin(minh, nbot - bot); + + // Rest of the spans. + for (ns = solid.spans[dx + dy*w]; ns; ns = ns->next) + { + nbot = (int)ns->smax; + ntop = ns->next ? (int)ns->next->smin : MAX_HEIGHT; + // Skip neightbour if the gap between the spans is too small. + if (rcMin(top,ntop) - rcMax(bot,nbot) > walkableHeight) + { + minh = rcMin(minh, nbot - bot); + + // Find min/max accessible neighbour height. + if (rcAbs(nbot - bot) <= walkableClimb) + { + if (nbot < asmin) asmin = nbot; + if (nbot > asmax) asmax = nbot; + } + + } + } + } + + // The current span is close to a ledge if the drop to any + // neighbour span is less than the walkableClimb. + if (minh < -walkableClimb) + { + s->area = RC_NULL_AREA; + } + // If the difference between all neighbours is too large, + // we are at steep slope, mark the span as ledge. + else if ((asmax - asmin) > walkableClimb) + { + s->area = RC_NULL_AREA; + } + } + } + } +} + +/// @par +/// +/// For this filter, the clearance above the span is the distance from the span's +/// maximum to the next higher span's minimum. (Same grid column.) +/// +/// @see rcHeightfield, rcConfig +void rcFilterWalkableLowHeightSpans(rcContext* ctx, int walkableHeight, rcHeightfield& solid) +{ + rcAssert(ctx); + + rcScopedTimer timer(ctx, RC_TIMER_FILTER_WALKABLE); + + const int w = solid.width; + const int h = solid.height; + const int MAX_HEIGHT = 0xffff; + + // Remove walkable flag from spans which do not have enough + // space above them for the agent to stand there. + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + { + for (rcSpan* s = solid.spans[x + y*w]; s; s = s->next) + { + const int bot = (int)(s->smax); + const int top = s->next ? (int)(s->next->smin) : MAX_HEIGHT; + if ((top - bot) <= walkableHeight) + s->area = RC_NULL_AREA; + } + } + } +} diff --git a/external/recast/src/RecastLayers.cpp b/external/recast/src/RecastLayers.cpp new file mode 100644 index 0000000000..acc97e44f0 --- /dev/null +++ b/external/recast/src/RecastLayers.cpp @@ -0,0 +1,644 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#include +#define _USE_MATH_DEFINES +#include +#include +#include +#include +#include "Recast.h" +#include "RecastAlloc.h" +#include "RecastAssert.h" + + +// 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 +{ + unsigned char layers[RC_MAX_LAYERS]; + unsigned char neis[RC_MAX_NEIS]; + unsigned short ymin, ymax; + unsigned char layerId; // Layer ID + unsigned char nlayers; // Layer count + unsigned char nneis; // Neighbour count + unsigned char base; // Flag indicating if the region is the base of merged regions. +}; + + +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) +{ + return (amin > bmax || amax < bmin) ? false : true; +} + + + +struct rcLayerSweepSpan +{ + unsigned short ns; // number samples + unsigned char id; // region id + unsigned char nei; // neighbour id +}; + +/// @par +/// +/// See the #rcConfig documentation for more information on the configuration parameters. +/// +/// @see rcAllocHeightfieldLayerSet, rcCompactHeightfield, rcHeightfieldLayerSet, rcConfig +bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf, + const int borderSize, const int walkableHeight, + rcHeightfieldLayerSet& lset) +{ + rcAssert(ctx); + + rcScopedTimer timer(ctx, RC_TIMER_BUILD_LAYERS); + + const int w = chf.width; + const int h = chf.height; + + rcScopedDelete srcReg((unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP)); + if (!srcReg) + { + ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'srcReg' (%d).", chf.spanCount); + return false; + } + memset(srcReg,0xff,sizeof(unsigned char)*chf.spanCount); + + const int nsweeps = chf.width; + rcScopedDelete sweeps((rcLayerSweepSpan*)rcAlloc(sizeof(rcLayerSweepSpan)*nsweeps, RC_ALLOC_TEMP)); + if (!sweeps) + { + ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'sweeps' (%d).", nsweeps); + return false; + } + + + // Partition walkable area into monotone regions. + int prevCount[256]; + unsigned char regId = 0; + + for (int y = borderSize; y < h-borderSize; ++y) + { + memset(prevCount,0,sizeof(int)*regId); + unsigned char sweepId = 0; + + for (int x = borderSize; x < w-borderSize; ++x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + const rcCompactSpan& s = chf.spans[i]; + if (chf.areas[i] == RC_NULL_AREA) continue; + + unsigned char sid = 0xff; + + // -x + if (rcGetCon(s, 0) != RC_NOT_CONNECTED) + { + const int ax = x + rcGetDirOffsetX(0); + const int ay = y + rcGetDirOffsetY(0); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0); + if (chf.areas[ai] != RC_NULL_AREA && srcReg[ai] != 0xff) + sid = srcReg[ai]; + } + + if (sid == 0xff) + { + sid = sweepId++; + sweeps[sid].nei = 0xff; + sweeps[sid].ns = 0; + } + + // -y + if (rcGetCon(s,3) != RC_NOT_CONNECTED) + { + const int ax = x + rcGetDirOffsetX(3); + const int ay = y + rcGetDirOffsetY(3); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3); + const unsigned char nr = srcReg[ai]; + if (nr != 0xff) + { + // Set neighbour when first valid neighbour is encoutered. + if (sweeps[sid].ns == 0) + sweeps[sid].nei = nr; + + if (sweeps[sid].nei == nr) + { + // Update existing neighbour + sweeps[sid].ns++; + prevCount[nr]++; + } + else + { + // This is hit if there is nore than one neighbour. + // Invalidate the neighbour. + sweeps[sid].nei = 0xff; + } + } + } + + srcReg[i] = sid; + } + } + + // Create unique ID. + for (int i = 0; i < sweepId; ++i) + { + // If the neighbour is set and there is only one continuous connection to it, + // the sweep will be merged with the previous one, else new region is created. + if (sweeps[i].nei != 0xff && prevCount[sweeps[i].nei] == (int)sweeps[i].ns) + { + sweeps[i].id = sweeps[i].nei; + } + else + { + if (regId == 255) + { + ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Region ID overflow."); + return false; + } + sweeps[i].id = regId++; + } + } + + // Remap local sweep ids to region ids. + for (int x = borderSize; x < w-borderSize; ++x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + if (srcReg[i] != 0xff) + srcReg[i] = sweeps[srcReg[i]].id; + } + } + } + + // Allocate and init layer regions. + const int nregs = (int)regId; + rcScopedDelete regs((rcLayerRegion*)rcAlloc(sizeof(rcLayerRegion)*nregs, RC_ALLOC_TEMP)); + if (!regs) + { + ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'regs' (%d).", nregs); + return false; + } + memset(regs, 0, sizeof(rcLayerRegion)*nregs); + for (int i = 0; i < nregs; ++i) + { + regs[i].layerId = 0xff; + regs[i].ymin = 0xffff; + regs[i].ymax = 0; + } + + // Find region neighbours and overlapping regions. + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + + unsigned char lregs[RC_MAX_LAYERS]; + int nlregs = 0; + + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + const rcCompactSpan& s = chf.spans[i]; + const unsigned char ri = srcReg[i]; + if (ri == 0xff) continue; + + regs[ri].ymin = rcMin(regs[ri].ymin, s.y); + regs[ri].ymax = rcMax(regs[ri].ymax, s.y); + + // Collect all region layers. + if (nlregs < RC_MAX_LAYERS) + lregs[nlregs++] = ri; + + // Update neighbours + for (int dir = 0; dir < 4; ++dir) + { + if (rcGetCon(s, dir) != RC_NOT_CONNECTED) + { + const int ax = x + rcGetDirOffsetX(dir); + const int ay = y + rcGetDirOffsetY(dir); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir); + const unsigned char rai = srcReg[ai]; + if (rai != 0xff && rai != ri) + { + // 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); + } + } + } + + } + + // Update overlapping regions. + for (int i = 0; i < nlregs-1; ++i) + { + for (int j = i+1; j < nlregs; ++j) + { + if (lregs[i] != lregs[j]) + { + rcLayerRegion& ri = regs[lregs[i]]; + rcLayerRegion& rj = regs[lregs[j]]; + + 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; + } + } + } + } + + } + } + + // Create 2D layers from regions. + unsigned char layerId = 0; + + static const int MAX_STACK = 64; + unsigned char stack[MAX_STACK]; + int nstack = 0; + + for (int i = 0; i < nregs; ++i) + { + rcLayerRegion& root = regs[i]; + // Skip already visited. + if (root.layerId != 0xff) + continue; + + // Start search. + root.layerId = layerId; + root.base = 1; + + nstack = 0; + stack[nstack++] = (unsigned char)i; + + while (nstack) + { + // Pop front + rcLayerRegion& reg = regs[stack[0]]; + nstack--; + for (int j = 0; j < nstack; ++j) + stack[j] = stack[j+1]; + + const int nneis = (int)reg.nneis; + for (int j = 0; j < nneis; ++j) + { + const unsigned char nei = reg.neis[j]; + rcLayerRegion& regn = regs[nei]; + // Skip already visited. + if (regn.layerId != 0xff) + continue; + // Skip if the neighbour is overlapping root region. + if (contains(root.layers, root.nlayers, nei)) + continue; + // Skip if the height range would become too large. + const int ymin = rcMin(root.ymin, regn.ymin); + const int ymax = rcMax(root.ymax, regn.ymax); + if ((ymax - ymin) >= 255) + continue; + + if (nstack < MAX_STACK) + { + // Deepen + stack[nstack++] = (unsigned char)nei; + + // Mark layer id + regn.layerId = layerId; + // Merge current layers to root. + for (int k = 0; k < regn.nlayers; ++k) + { + 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); + } + } + } + + layerId++; + } + + // Merge non-overlapping regions that are close in height. + const unsigned short mergeHeight = (unsigned short)walkableHeight * 4; + + for (int i = 0; i < nregs; ++i) + { + rcLayerRegion& ri = regs[i]; + if (!ri.base) continue; + + unsigned char newId = ri.layerId; + + for (;;) + { + unsigned char oldId = 0xff; + + for (int j = 0; j < nregs; ++j) + { + if (i == j) continue; + rcLayerRegion& rj = regs[j]; + if (!rj.base) continue; + + // Skip if 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. + const int ymin = rcMin(ri.ymin, rj.ymin); + const int ymax = rcMax(ri.ymax, rj.ymax); + if ((ymax - ymin) >= 255) + continue; + + // 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) + { + if (regs[k].layerId != rj.layerId) + continue; + // Check if region 'k' is overlapping region 'ri' + // Index to 'regs' is the same as region id. + if (contains(ri.layers,ri.nlayers, (unsigned char)k)) + { + overlap = true; + break; + } + } + // Cannot merge of regions overlap. + if (overlap) + continue; + + // Can merge i and j. + oldId = rj.layerId; + break; + } + + // Could not find anything to merge with, stop. + if (oldId == 0xff) + break; + + // Merge + for (int j = 0; j < nregs; ++j) + { + rcLayerRegion& rj = regs[j]; + if (rj.layerId == oldId) + { + rj.base = 0; + // Remap layerIds. + rj.layerId = newId; + // Add overlaid layers from 'rj' to 'ri'. + for (int k = 0; k < rj.nlayers; ++k) + { + 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); + } + } + } + } + + // Compact layerIds + unsigned char remap[256]; + memset(remap, 0, 256); + + // Find number of unique layers. + layerId = 0; + for (int i = 0; i < nregs; ++i) + remap[regs[i].layerId] = 1; + for (int i = 0; i < 256; ++i) + { + if (remap[i]) + remap[i] = layerId++; + else + remap[i] = 0xff; + } + // Remap ids. + for (int i = 0; i < nregs; ++i) + regs[i].layerId = remap[regs[i].layerId]; + + // No layers, return empty. + if (layerId == 0) + return true; + + // Create layers. + rcAssert(lset.layers == 0); + + const int lw = w - borderSize*2; + const int lh = h - borderSize*2; + + // Build contracted bbox for layers. + float bmin[3], bmax[3]; + rcVcopy(bmin, chf.bmin); + rcVcopy(bmax, chf.bmax); + bmin[0] += borderSize*chf.cs; + bmin[2] += borderSize*chf.cs; + bmax[0] -= borderSize*chf.cs; + bmax[2] -= borderSize*chf.cs; + + lset.nlayers = (int)layerId; + + lset.layers = (rcHeightfieldLayer*)rcAlloc(sizeof(rcHeightfieldLayer)*lset.nlayers, RC_ALLOC_PERM); + if (!lset.layers) + { + ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'layers' (%d).", lset.nlayers); + return false; + } + memset(lset.layers, 0, sizeof(rcHeightfieldLayer)*lset.nlayers); + + + // Store layers. + for (int i = 0; i < lset.nlayers; ++i) + { + unsigned char curId = (unsigned char)i; + + rcHeightfieldLayer* layer = &lset.layers[i]; + + const int gridSize = sizeof(unsigned char)*lw*lh; + + layer->heights = (unsigned char*)rcAlloc(gridSize, RC_ALLOC_PERM); + if (!layer->heights) + { + ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'heights' (%d).", gridSize); + return false; + } + memset(layer->heights, 0xff, gridSize); + + layer->areas = (unsigned char*)rcAlloc(gridSize, RC_ALLOC_PERM); + if (!layer->areas) + { + ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'areas' (%d).", gridSize); + return false; + } + memset(layer->areas, 0, gridSize); + + layer->cons = (unsigned char*)rcAlloc(gridSize, RC_ALLOC_PERM); + if (!layer->cons) + { + ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'cons' (%d).", gridSize); + return false; + } + memset(layer->cons, 0, gridSize); + + // Find layer height bounds. + int hmin = 0, hmax = 0; + for (int j = 0; j < nregs; ++j) + { + if (regs[j].base && regs[j].layerId == curId) + { + hmin = (int)regs[j].ymin; + hmax = (int)regs[j].ymax; + } + } + + layer->width = lw; + layer->height = lh; + layer->cs = chf.cs; + layer->ch = chf.ch; + + // Adjust the bbox to fit the heightfield. + rcVcopy(layer->bmin, bmin); + rcVcopy(layer->bmax, bmax); + layer->bmin[1] = bmin[1] + hmin*chf.ch; + layer->bmax[1] = bmin[1] + hmax*chf.ch; + layer->hmin = hmin; + layer->hmax = hmax; + + // Update usable data region. + layer->minx = layer->width; + layer->maxx = 0; + layer->miny = layer->height; + layer->maxy = 0; + + // Copy height and area from compact heightfield. + for (int y = 0; y < lh; ++y) + { + for (int x = 0; x < lw; ++x) + { + const int cx = borderSize+x; + const int cy = borderSize+y; + const rcCompactCell& c = chf.cells[cx+cy*w]; + for (int j = (int)c.index, nj = (int)(c.index+c.count); j < nj; ++j) + { + const rcCompactSpan& s = chf.spans[j]; + // Skip unassigned regions. + if (srcReg[j] == 0xff) + continue; + // Skip of does nto belong to current layer. + unsigned char lid = regs[srcReg[j]].layerId; + if (lid != curId) + continue; + + // Update data bounds. + layer->minx = rcMin(layer->minx, x); + layer->maxx = rcMax(layer->maxx, x); + layer->miny = rcMin(layer->miny, y); + layer->maxy = rcMax(layer->maxy, y); + + // Store height and area type. + const int idx = x+y*lw; + layer->heights[idx] = (unsigned char)(s.y - hmin); + layer->areas[idx] = chf.areas[j]; + + // Check connection. + unsigned char portal = 0; + unsigned char con = 0; + for (int dir = 0; dir < 4; ++dir) + { + if (rcGetCon(s, dir) != RC_NOT_CONNECTED) + { + const int ax = cx + rcGetDirOffsetX(dir); + const int ay = cy + rcGetDirOffsetY(dir); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir); + unsigned char alid = srcReg[ai] != 0xff ? regs[srcReg[ai]].layerId : 0xff; + // Portal mask + if (chf.areas[ai] != RC_NULL_AREA && lid != alid) + { + portal |= (unsigned char)(1< hmin) + layer->heights[idx] = rcMax(layer->heights[idx], (unsigned char)(as.y - hmin)); + } + // Valid connection mask + if (chf.areas[ai] != RC_NULL_AREA && lid == alid) + { + const int nx = ax - borderSize; + const int ny = ay - borderSize; + if (nx >= 0 && ny >= 0 && nx < lw && ny < lh) + con |= (unsigned char)(1<cons[idx] = (portal << 4) | con; + } + } + } + + if (layer->minx > layer->maxx) + layer->minx = layer->maxx = 0; + if (layer->miny > layer->maxy) + layer->miny = layer->maxy = 0; + } + + return true; +} diff --git a/external/recast/src/RecastMesh.cpp b/external/recast/src/RecastMesh.cpp new file mode 100644 index 0000000000..e99eaebb79 --- /dev/null +++ b/external/recast/src/RecastMesh.cpp @@ -0,0 +1,1552 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#define _USE_MATH_DEFINES +#include +#include +#include +#include "Recast.h" +#include "RecastAlloc.h" +#include "RecastAssert.h" + +struct rcEdge +{ + unsigned short vert[2]; + unsigned short polyEdge[2]; + unsigned short poly[2]; +}; + +static bool buildMeshAdjacency(unsigned short* polys, const int npolys, + const int nverts, const int vertsPerPoly) +{ + // Based on code by Eric Lengyel from: + // http://www.terathon.com/code/edges.php + + int maxEdgeCount = npolys*vertsPerPoly; + unsigned short* firstEdge = (unsigned short*)rcAlloc(sizeof(unsigned short)*(nverts + maxEdgeCount), RC_ALLOC_TEMP); + if (!firstEdge) + return false; + unsigned short* nextEdge = firstEdge + nverts; + int edgeCount = 0; + + rcEdge* edges = (rcEdge*)rcAlloc(sizeof(rcEdge)*maxEdgeCount, RC_ALLOC_TEMP); + if (!edges) + { + rcFree(firstEdge); + return false; + } + + for (int i = 0; i < nverts; i++) + firstEdge[i] = RC_MESH_NULL_IDX; + + for (int i = 0; i < npolys; ++i) + { + unsigned short* t = &polys[i*vertsPerPoly*2]; + for (int j = 0; j < vertsPerPoly; ++j) + { + if (t[j] == RC_MESH_NULL_IDX) break; + unsigned short v0 = t[j]; + unsigned short v1 = (j+1 >= vertsPerPoly || t[j+1] == RC_MESH_NULL_IDX) ? t[0] : t[j+1]; + if (v0 < v1) + { + rcEdge& edge = edges[edgeCount]; + edge.vert[0] = v0; + edge.vert[1] = v1; + edge.poly[0] = (unsigned short)i; + edge.polyEdge[0] = (unsigned short)j; + edge.poly[1] = (unsigned short)i; + edge.polyEdge[1] = 0; + // Insert edge + nextEdge[edgeCount] = firstEdge[v0]; + firstEdge[v0] = (unsigned short)edgeCount; + edgeCount++; + } + } + } + + for (int i = 0; i < npolys; ++i) + { + unsigned short* t = &polys[i*vertsPerPoly*2]; + for (int j = 0; j < vertsPerPoly; ++j) + { + if (t[j] == RC_MESH_NULL_IDX) break; + unsigned short v0 = t[j]; + unsigned short v1 = (j+1 >= vertsPerPoly || t[j+1] == RC_MESH_NULL_IDX) ? t[0] : t[j+1]; + if (v0 > v1) + { + for (unsigned short e = firstEdge[v1]; e != RC_MESH_NULL_IDX; e = nextEdge[e]) + { + rcEdge& edge = edges[e]; + if (edge.vert[1] == v0 && edge.poly[0] == edge.poly[1]) + { + edge.poly[1] = (unsigned short)i; + edge.polyEdge[1] = (unsigned short)j; + break; + } + } + } + } + } + + // Store adjacency + for (int i = 0; i < edgeCount; ++i) + { + const rcEdge& e = edges[i]; + if (e.poly[0] != e.poly[1]) + { + unsigned short* p0 = &polys[e.poly[0]*vertsPerPoly*2]; + unsigned short* p1 = &polys[e.poly[1]*vertsPerPoly*2]; + p0[vertsPerPoly + e.polyEdge[0]] = e.poly[1]; + p1[vertsPerPoly + e.polyEdge[1]] = e.poly[0]; + } + } + + rcFree(firstEdge); + rcFree(edges); + + return true; +} + + +static const int VERTEX_BUCKET_COUNT = (1<<12); + +inline int computeVertexHash(int x, int y, int z) +{ + const unsigned int h1 = 0x8da6b343; // Large multiplicative constants; + const unsigned int h2 = 0xd8163841; // here arbitrarily chosen primes + const unsigned int h3 = 0xcb1ab31f; + unsigned int n = h1 * x + h2 * y + h3 * z; + return (int)(n & (VERTEX_BUCKET_COUNT-1)); +} + +static unsigned short addVertex(unsigned short x, unsigned short y, unsigned short z, + unsigned short* verts, int* firstVert, int* nextVert, int& nv) +{ + int bucket = computeVertexHash(x, 0, z); + int i = firstVert[bucket]; + + while (i != -1) + { + const unsigned short* v = &verts[i*3]; + if (v[0] == x && (rcAbs(v[1] - y) <= 2) && v[2] == z) + return (unsigned short)i; + i = nextVert[i]; // next + } + + // Could not find, create new. + i = nv; nv++; + unsigned short* v = &verts[i*3]; + v[0] = x; + v[1] = y; + v[2] = z; + nextVert[i] = firstVert[bucket]; + firstVert[bucket] = i; + + 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; } + +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]; +} + +// Returns T iff (v_i, v_j) is a proper internal *or* external +// diagonal of P, *ignoring edges incident to v_i and v_j*. +static bool diagonalie(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 (intersect(d0, d1, p0, p1)) + return false; + } + } + return true; +} + +// Returns true iff the diagonal (i,j) is strictly internal to the +// polygon P in the neighborhood of the i endpoint. +static bool inCone(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 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)); +} + +// Returns T iff (v_i, v_j) is a proper internal +// diagonal of P. +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; + int* dst = tris; + + // The last bit of the index is used to indicate if the vertex can be removed. + for (int i = 0; i < n; i++) + { + int i1 = next(i, n); + int i2 = next(i1, n); + if (diagonal(i, i2, n, verts, indices)) + indices[i1] |= 0x80000000; + } + + while (n > 3) + { + int minLen = -1; + int mini = -1; + for (int i = 0; i < n; i++) + { + int i1 = next(i, n); + if (indices[i1] & 0x80000000) + { + const int* p0 = &verts[(indices[i] & 0x0fffffff) * 4]; + const int* p2 = &verts[(indices[next(i1, 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) + { + // 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++) + { + 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; + } + } + + int i = mini; + int i1 = next(i, n); + int i2 = next(i1, n); + + *dst++ = indices[i] & 0x0fffffff; + *dst++ = indices[i1] & 0x0fffffff; + *dst++ = indices[i2] & 0x0fffffff; + ntris++; + + // Removes P[i1] by copying P[i+1]...P[n-1] left one index. + n--; + for (int k = i1; k < n; k++) + indices[k] = indices[k+1]; + + if (i1 >= n) i1 = 0; + i = prev(i1,n); + // Update diagonal flags. + if (diagonal(prev(i, n), i1, n, verts, indices)) + indices[i] |= 0x80000000; + else + indices[i] &= 0x0fffffff; + + if (diagonal(i, next(i1, n), n, verts, indices)) + indices[i1] |= 0x80000000; + else + indices[i1] &= 0x0fffffff; + } + + // Append the remaining triangle. + *dst++ = indices[0] & 0x0fffffff; + *dst++ = indices[1] & 0x0fffffff; + *dst++ = indices[2] & 0x0fffffff; + ntris++; + + return ntris; +} + +static int countPolyVerts(const unsigned short* p, const int nvp) +{ + for (int i = 0; i < nvp; ++i) + if (p[i] == RC_MESH_NULL_IDX) + return i; + return nvp; +} + +inline bool uleft(const unsigned short* a, const unsigned short* b, const unsigned short* c) +{ + return ((int)b[0] - (int)a[0]) * ((int)c[2] - (int)a[2]) - + ((int)c[0] - (int)a[0]) * ((int)b[2] - (int)a[2]) < 0; +} + +static int getPolyMergeValue(unsigned short* pa, unsigned short* pb, + const unsigned short* verts, int& ea, int& eb, + const int nvp) +{ + const int na = countPolyVerts(pa, nvp); + const int nb = countPolyVerts(pb, nvp); + + // If the merged polygon would be too big, do not merge. + if (na+nb-2 > nvp) + return -1; + + // Check if the polygons share an edge. + ea = -1; + eb = -1; + + for (int i = 0; i < na; ++i) + { + unsigned short va0 = pa[i]; + unsigned short va1 = pa[(i+1) % na]; + if (va0 > va1) + rcSwap(va0, va1); + for (int j = 0; j < nb; ++j) + { + unsigned short vb0 = pb[j]; + unsigned short vb1 = pb[(j+1) % nb]; + if (vb0 > vb1) + rcSwap(vb0, vb1); + if (va0 == vb0 && va1 == vb1) + { + ea = i; + eb = j; + break; + } + } + } + + // No common edge, cannot merge. + if (ea == -1 || eb == -1) + return -1; + + // Check to see if the merged polygon would be convex. + unsigned short va, vb, vc; + + va = pa[(ea+na-1) % na]; + vb = pa[ea]; + vc = pb[(eb+2) % nb]; + if (!uleft(&verts[va*3], &verts[vb*3], &verts[vc*3])) + return -1; + + va = pb[(eb+nb-1) % nb]; + vb = pb[eb]; + vc = pa[(ea+2) % na]; + if (!uleft(&verts[va*3], &verts[vb*3], &verts[vc*3])) + return -1; + + va = pa[ea]; + vb = pa[(ea+1)%na]; + + int dx = (int)verts[va*3+0] - (int)verts[vb*3+0]; + int dy = (int)verts[va*3+2] - (int)verts[vb*3+2]; + + return dx*dx + dy*dy; +} + +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); + + // Merge polygons. + memset(tmp, 0xff, sizeof(unsigned short)*nvp); + int n = 0; + // Add pa + for (int i = 0; i < na-1; ++i) + tmp[n++] = pa[(ea+1+i) % na]; + // Add pb + for (int i = 0; i < nb-1; ++i) + tmp[n++] = pb[(eb+1+i) % nb]; + + memcpy(pa, tmp, sizeof(unsigned short)*nvp); +} + + +static void pushFront(int v, int* arr, int& an) +{ + an++; + for (int i = an-1; i > 0; --i) arr[i] = arr[i-1]; + arr[0] = v; +} + +static void pushBack(int v, int* arr, int& an) +{ + arr[an] = v; + an++; +} + +static bool canRemoveVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short rem) +{ + const int nvp = mesh.nvp; + + // Count number of polygons to remove. + int numRemovedVerts = 0; + int numTouchedVerts = 0; + int numRemainingEdges = 0; + for (int i = 0; i < mesh.npolys; ++i) + { + unsigned short* p = &mesh.polys[i*nvp*2]; + const int nv = countPolyVerts(p, nvp); + int numRemoved = 0; + int numVerts = 0; + for (int j = 0; j < nv; ++j) + { + if (p[j] == rem) + { + numTouchedVerts++; + numRemoved++; + } + numVerts++; + } + if (numRemoved) + { + numRemovedVerts += numRemoved; + numRemainingEdges += numVerts-(numRemoved+1); + } + } + + // There would be too few edges remaining to create a polygon. + // This can happen for example when a tip of a triangle is marked + // as deletion, but there are no other polys that share the vertex. + // In this case, the vertex should not be removed. + if (numRemainingEdges <= 2) + return false; + + // Find edges which share the removed vertex. + const int maxEdges = numTouchedVerts*2; + int nedges = 0; + rcScopedDelete edges((int*)rcAlloc(sizeof(int)*maxEdges*3, RC_ALLOC_TEMP)); + if (!edges) + { + ctx->log(RC_LOG_WARNING, "canRemoveVertex: Out of memory 'edges' (%d).", maxEdges*3); + return false; + } + + for (int i = 0; i < mesh.npolys; ++i) + { + unsigned short* p = &mesh.polys[i*nvp*2]; + const int nv = countPolyVerts(p, nvp); + + // Collect edges which touches the removed vertex. + for (int j = 0, k = nv-1; j < nv; k = j++) + { + if (p[j] == rem || p[k] == rem) + { + // Arrange edge so that a=rem. + int a = p[j], b = p[k]; + if (b == rem) + rcSwap(a,b); + + // Check if the edge exists + bool exists = false; + for (int m = 0; m < nedges; ++m) + { + int* e = &edges[m*3]; + if (e[1] == b) + { + // Exists, increment vertex share count. + e[2]++; + exists = true; + } + } + // Add new edge. + if (!exists) + { + int* e = &edges[nedges*3]; + e[0] = a; + e[1] = b; + e[2] = 1; + nedges++; + } + } + } + } + + // There should be no more than 2 open edges. + // This catches the case that two non-adjacent polygons + // share the removed vertex. In that case, do not remove the vertex. + int numOpenEdges = 0; + for (int i = 0; i < nedges; ++i) + { + if (edges[i*3+2] < 2) + numOpenEdges++; + } + if (numOpenEdges > 2) + return false; + + return true; +} + +static bool removeVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned short rem, const int maxTris) +{ + const int nvp = mesh.nvp; + + // Count number of polygons to remove. + int numRemovedVerts = 0; + for (int i = 0; i < mesh.npolys; ++i) + { + unsigned short* p = &mesh.polys[i*nvp*2]; + const int nv = countPolyVerts(p, nvp); + for (int j = 0; j < nv; ++j) + { + if (p[j] == rem) + numRemovedVerts++; + } + } + + int nedges = 0; + rcScopedDelete edges((int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp*4, RC_ALLOC_TEMP)); + if (!edges) + { + ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'edges' (%d).", numRemovedVerts*nvp*4); + return false; + } + + int nhole = 0; + rcScopedDelete hole((int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP)); + if (!hole) + { + ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hole' (%d).", numRemovedVerts*nvp); + return false; + } + + int nhreg = 0; + rcScopedDelete hreg((int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP)); + if (!hreg) + { + ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'hreg' (%d).", numRemovedVerts*nvp); + return false; + } + + int nharea = 0; + rcScopedDelete harea((int*)rcAlloc(sizeof(int)*numRemovedVerts*nvp, RC_ALLOC_TEMP)); + if (!harea) + { + ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'harea' (%d).", numRemovedVerts*nvp); + return false; + } + + for (int i = 0; i < mesh.npolys; ++i) + { + unsigned short* p = &mesh.polys[i*nvp*2]; + const int nv = countPolyVerts(p, nvp); + bool hasRem = false; + for (int j = 0; j < nv; ++j) + if (p[j] == rem) hasRem = true; + if (hasRem) + { + // Collect edges which does not touch the removed vertex. + for (int j = 0, k = nv-1; j < nv; k = j++) + { + if (p[j] != rem && p[k] != rem) + { + int* e = &edges[nedges*4]; + e[0] = p[k]; + e[1] = p[j]; + e[2] = mesh.regs[i]; + e[3] = mesh.areas[i]; + nedges++; + } + } + // Remove the polygon. + unsigned short* p2 = &mesh.polys[(mesh.npolys-1)*nvp*2]; + if (p != p2) + memcpy(p,p2,sizeof(unsigned short)*nvp); + memset(p+nvp,0xff,sizeof(unsigned short)*nvp); + mesh.regs[i] = mesh.regs[mesh.npolys-1]; + mesh.areas[i] = mesh.areas[mesh.npolys-1]; + mesh.npolys--; + --i; + } + } + + // Remove vertex. + 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]; + mesh.verts[i*3+2] = mesh.verts[(i+1)*3+2]; + } + mesh.nverts--; + + // Adjust indices to match the removed vertex layout. + for (int i = 0; i < mesh.npolys; ++i) + { + unsigned short* p = &mesh.polys[i*nvp*2]; + const int nv = countPolyVerts(p, nvp); + for (int j = 0; j < nv; ++j) + if (p[j] > rem) p[j]--; + } + for (int i = 0; i < nedges; ++i) + { + if (edges[i*4+0] > rem) edges[i*4+0]--; + if (edges[i*4+1] > rem) edges[i*4+1]--; + } + + if (nedges == 0) + return true; + + // Start with one vertex, keep appending connected + // segments to the start and end of the hole. + pushBack(edges[0], hole, nhole); + pushBack(edges[2], hreg, nhreg); + pushBack(edges[3], harea, nharea); + + while (nedges) + { + bool match = false; + + for (int i = 0; i < nedges; ++i) + { + const int ea = edges[i*4+0]; + const int eb = edges[i*4+1]; + const int r = edges[i*4+2]; + const int a = edges[i*4+3]; + bool add = false; + if (hole[0] == eb) + { + // The segment matches the beginning of the hole boundary. + pushFront(ea, hole, nhole); + pushFront(r, hreg, nhreg); + pushFront(a, harea, nharea); + add = true; + } + else if (hole[nhole-1] == ea) + { + // The segment matches the end of the hole boundary. + pushBack(eb, hole, nhole); + pushBack(r, hreg, nhreg); + pushBack(a, harea, nharea); + add = true; + } + if (add) + { + // The edge segment was added, remove it. + edges[i*4+0] = edges[(nedges-1)*4+0]; + edges[i*4+1] = edges[(nedges-1)*4+1]; + edges[i*4+2] = edges[(nedges-1)*4+2]; + edges[i*4+3] = edges[(nedges-1)*4+3]; + --nedges; + match = true; + --i; + } + } + + if (!match) + break; + } + + rcScopedDelete tris((int*)rcAlloc(sizeof(int)*nhole*3, RC_ALLOC_TEMP)); + if (!tris) + { + ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tris' (%d).", nhole*3); + return false; + } + + rcScopedDelete tverts((int*)rcAlloc(sizeof(int)*nhole*4, RC_ALLOC_TEMP)); + if (!tverts) + { + ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'tverts' (%d).", nhole*4); + return false; + } + + rcScopedDelete thole((int*)rcAlloc(sizeof(int)*nhole, RC_ALLOC_TEMP)); + if (!thole) + { + ctx->log(RC_LOG_WARNING, "removeVertex: Out of memory 'thole' (%d).", nhole); + return false; + } + + // Generate temp vertex array for triangulation. + for (int i = 0; i < nhole; ++i) + { + const int pi = hole[i]; + tverts[i*4+0] = mesh.verts[pi*3+0]; + tverts[i*4+1] = mesh.verts[pi*3+1]; + tverts[i*4+2] = mesh.verts[pi*3+2]; + tverts[i*4+3] = 0; + thole[i] = i; + } + + // Triangulate the hole. + int ntris = triangulate(nhole, &tverts[0], &thole[0], tris); + if (ntris < 0) + { + ntris = -ntris; + ctx->log(RC_LOG_WARNING, "removeVertex: triangulate() returned bad results."); + } + + // Merge the hole triangles back to polygons. + rcScopedDelete polys((unsigned short*)rcAlloc(sizeof(unsigned short)*(ntris+1)*nvp, RC_ALLOC_TEMP)); + if (!polys) + { + ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'polys' (%d).", (ntris+1)*nvp); + return false; + } + rcScopedDelete pregs((unsigned short*)rcAlloc(sizeof(unsigned short)*ntris, RC_ALLOC_TEMP)); + if (!pregs) + { + ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'pregs' (%d).", ntris); + return false; + } + rcScopedDelete pareas((unsigned char*)rcAlloc(sizeof(unsigned char)*ntris, RC_ALLOC_TEMP)); + if (!pareas) + { + ctx->log(RC_LOG_ERROR, "removeVertex: Out of memory 'pareas' (%d).", ntris); + return false; + } + + unsigned short* tmpPoly = &polys[ntris*nvp]; + + // Build initial polygons. + int npolys = 0; + memset(polys, 0xff, ntris*nvp*sizeof(unsigned short)); + for (int j = 0; j < ntris; ++j) + { + int* t = &tris[j*3]; + if (t[0] != t[1] && t[0] != t[2] && t[1] != t[2]) + { + 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]]; + + // 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++; + } + } + if (!npolys) + return true; + + // Merge polygons. + if (nvp > 3) + { + for (;;) + { + // Find best polygons to merge. + int bestMergeVal = 0; + int bestPa = 0, bestPb = 0, bestEa = 0, bestEb = 0; + + for (int j = 0; j < npolys-1; ++j) + { + unsigned short* pj = &polys[j*nvp]; + for (int k = j+1; k < npolys; ++k) + { + unsigned short* pk = &polys[k*nvp]; + int ea, eb; + int v = getPolyMergeValue(pj, pk, mesh.verts, ea, eb, nvp); + if (v > bestMergeVal) + { + bestMergeVal = v; + bestPa = j; + bestPb = k; + bestEa = ea; + bestEb = eb; + } + } + } + + if (bestMergeVal > 0) + { + // Found best, merge. + unsigned short* pa = &polys[bestPa*nvp]; + unsigned short* pb = &polys[bestPb*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); + pregs[bestPb] = pregs[npolys-1]; + pareas[bestPb] = pareas[npolys-1]; + npolys--; + } + else + { + // Could not merge any polygons, stop. + break; + } + } + } + + // Store polygons. + for (int i = 0; i < npolys; ++i) + { + if (mesh.npolys >= maxTris) break; + unsigned short* p = &mesh.polys[mesh.npolys*nvp*2]; + memset(p,0xff,sizeof(unsigned short)*nvp*2); + for (int j = 0; j < nvp; ++j) + p[j] = polys[i*nvp+j]; + mesh.regs[mesh.npolys] = pregs[i]; + mesh.areas[mesh.npolys] = pareas[i]; + mesh.npolys++; + if (mesh.npolys > maxTris) + { + ctx->log(RC_LOG_ERROR, "removeVertex: Too many polygons %d (max:%d).", mesh.npolys, maxTris); + return false; + } + } + + return true; +} + +/// @par +/// +/// @note If the mesh data is to be used to construct a Detour navigation mesh, then the upper +/// limit must be retricted to <= #DT_VERTS_PER_POLYGON. +/// +/// @see rcAllocPolyMesh, rcContourSet, rcPolyMesh, rcConfig +bool rcBuildPolyMesh(rcContext* ctx, rcContourSet& cset, const int nvp, rcPolyMesh& mesh) +{ + rcAssert(ctx); + + 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; + int maxVertsPerCont = 0; + for (int i = 0; i < cset.nconts; ++i) + { + // Skip null contours. + if (cset.conts[i].nverts < 3) continue; + maxVertices += cset.conts[i].nverts; + maxTris += cset.conts[i].nverts - 2; + maxVertsPerCont = rcMax(maxVertsPerCont, cset.conts[i].nverts); + } + + if (maxVertices >= 0xfffe) + { + ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Too many vertices %d.", maxVertices); + return false; + } + + rcScopedDelete vflags((unsigned char*)rcAlloc(sizeof(unsigned char)*maxVertices, RC_ALLOC_TEMP)); + if (!vflags) + { + ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'vflags' (%d).", maxVertices); + return false; + } + memset(vflags, 0, maxVertices); + + mesh.verts = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxVertices*3, RC_ALLOC_PERM); + if (!mesh.verts) + { + ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.verts' (%d).", maxVertices); + return false; + } + mesh.polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxTris*nvp*2, RC_ALLOC_PERM); + if (!mesh.polys) + { + ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.polys' (%d).", maxTris*nvp*2); + return false; + } + mesh.regs = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxTris, RC_ALLOC_PERM); + if (!mesh.regs) + { + ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.regs' (%d).", maxTris); + return false; + } + mesh.areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxTris, RC_ALLOC_PERM); + if (!mesh.areas) + { + ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.areas' (%d).", maxTris); + return false; + } + + mesh.nverts = 0; + mesh.npolys = 0; + mesh.nvp = nvp; + mesh.maxpolys = maxTris; + + memset(mesh.verts, 0, sizeof(unsigned short)*maxVertices*3); + memset(mesh.polys, 0xff, sizeof(unsigned short)*maxTris*nvp*2); + memset(mesh.regs, 0, sizeof(unsigned short)*maxTris); + memset(mesh.areas, 0, sizeof(unsigned char)*maxTris); + + rcScopedDelete nextVert((int*)rcAlloc(sizeof(int)*maxVertices, RC_ALLOC_TEMP)); + if (!nextVert) + { + ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'nextVert' (%d).", maxVertices); + return false; + } + memset(nextVert, 0, sizeof(int)*maxVertices); + + rcScopedDelete firstVert((int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP)); + if (!firstVert) + { + ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'firstVert' (%d).", VERTEX_BUCKET_COUNT); + return false; + } + for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i) + firstVert[i] = -1; + + rcScopedDelete indices((int*)rcAlloc(sizeof(int)*maxVertsPerCont, RC_ALLOC_TEMP)); + if (!indices) + { + ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'indices' (%d).", maxVertsPerCont); + return false; + } + rcScopedDelete tris((int*)rcAlloc(sizeof(int)*maxVertsPerCont*3, RC_ALLOC_TEMP)); + if (!tris) + { + ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'tris' (%d).", maxVertsPerCont*3); + return false; + } + rcScopedDelete polys((unsigned short*)rcAlloc(sizeof(unsigned short)*(maxVertsPerCont+1)*nvp, RC_ALLOC_TEMP)); + if (!polys) + { + ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'polys' (%d).", maxVertsPerCont*nvp); + return false; + } + unsigned short* tmpPoly = &polys[maxVertsPerCont*nvp]; + + for (int i = 0; i < cset.nconts; ++i) + { + rcContour& cont = cset.conts[i]; + + // Skip null contours. + if (cont.nverts < 3) + continue; + + // Triangulate contour + for (int j = 0; j < cont.nverts; ++j) + indices[j] = j; + + int ntris = triangulate(cont.nverts, cont.verts, &indices[0], &tris[0]); + if (ntris <= 0) + { + // Bad triangulation, should not happen. +/* printf("\tconst float bmin[3] = {%ff,%ff,%ff};\n", cset.bmin[0], cset.bmin[1], cset.bmin[2]); + printf("\tconst float cs = %ff;\n", cset.cs); + printf("\tconst float ch = %ff;\n", cset.ch); + printf("\tconst int verts[] = {\n"); + for (int k = 0; k < cont.nverts; ++k) + { + const int* v = &cont.verts[k*4]; + printf("\t\t%d,%d,%d,%d,\n", v[0], v[1], v[2], v[3]); + } + printf("\t};\n\tconst int nverts = sizeof(verts)/(sizeof(int)*4);\n");*/ + ctx->log(RC_LOG_WARNING, "rcBuildPolyMesh: Bad triangulation Contour %d.", i); + ntris = -ntris; + } + + // Add and merge vertices. + for (int j = 0; j < cont.nverts; ++j) + { + const int* v = &cont.verts[j*4]; + indices[j] = addVertex((unsigned short)v[0], (unsigned short)v[1], (unsigned short)v[2], + mesh.verts, firstVert, nextVert, mesh.nverts); + if (v[3] & RC_BORDER_VERTEX) + { + // This vertex should be removed. + vflags[indices[j]] = 1; + } + } + + // Build initial polygons. + int npolys = 0; + memset(polys, 0xff, maxVertsPerCont*nvp*sizeof(unsigned short)); + for (int j = 0; j < ntris; ++j) + { + int* t = &tris[j*3]; + if (t[0] != t[1] && t[0] != t[2] && t[1] != t[2]) + { + polys[npolys*nvp+0] = (unsigned short)indices[t[0]]; + polys[npolys*nvp+1] = (unsigned short)indices[t[1]]; + polys[npolys*nvp+2] = (unsigned short)indices[t[2]]; + npolys++; + } + } + if (!npolys) + continue; + + // Merge polygons. + if (nvp > 3) + { + for(;;) + { + // Find best polygons to merge. + int bestMergeVal = 0; + int bestPa = 0, bestPb = 0, bestEa = 0, bestEb = 0; + + for (int j = 0; j < npolys-1; ++j) + { + unsigned short* pj = &polys[j*nvp]; + for (int k = j+1; k < npolys; ++k) + { + unsigned short* pk = &polys[k*nvp]; + int ea, eb; + int v = getPolyMergeValue(pj, pk, mesh.verts, ea, eb, nvp); + if (v > bestMergeVal) + { + bestMergeVal = v; + bestPa = j; + bestPb = k; + bestEa = ea; + bestEb = eb; + } + } + } + + if (bestMergeVal > 0) + { + // Found best, merge. + unsigned short* pa = &polys[bestPa*nvp]; + unsigned short* pb = &polys[bestPb*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); + npolys--; + } + else + { + // Could not merge any polygons, stop. + break; + } + } + } + + // Store polygons. + for (int j = 0; j < npolys; ++j) + { + unsigned short* p = &mesh.polys[mesh.npolys*nvp*2]; + unsigned short* q = &polys[j*nvp]; + for (int k = 0; k < nvp; ++k) + p[k] = q[k]; + mesh.regs[mesh.npolys] = cont.reg; + mesh.areas[mesh.npolys] = cont.area; + mesh.npolys++; + if (mesh.npolys > maxTris) + { + ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Too many polygons %d (max:%d).", mesh.npolys, maxTris); + return false; + } + } + } + + + // Remove edge vertices. + for (int i = 0; i < mesh.nverts; ++i) + { + if (vflags[i]) + { + if (!canRemoveVertex(ctx, mesh, (unsigned short)i)) + continue; + if (!removeVertex(ctx, mesh, (unsigned short)i, maxTris)) + { + // Failed to remove vertex + ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Failed to remove edge vertex %d.", i); + return false; + } + // Remove vertex + // Note: mesh.nverts is already decremented inside removeVertex()! + // Fixup vertex flags + for (int j = i; j < mesh.nverts; ++j) + vflags[j] = vflags[j+1]; + --i; + } + } + + // Calculate adjacency. + if (!buildMeshAdjacency(mesh.polys, mesh.npolys, mesh.nverts, nvp)) + { + ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Adjacency failed."); + return false; + } + + // Find portal edges + if (mesh.borderSize > 0) + { + const int w = cset.width; + const int h = cset.height; + for (int i = 0; i < mesh.npolys; ++i) + { + unsigned short* p = &mesh.polys[i*2*nvp]; + for (int j = 0; j < nvp; ++j) + { + if (p[j] == RC_MESH_NULL_IDX) break; + // Skip connected edges. + if (p[nvp+j] != RC_MESH_NULL_IDX) + continue; + int nj = j+1; + if (nj >= nvp || p[nj] == RC_MESH_NULL_IDX) nj = 0; + const unsigned short* va = &mesh.verts[p[j]*3]; + const unsigned short* vb = &mesh.verts[p[nj]*3]; + + if ((int)va[0] == 0 && (int)vb[0] == 0) + p[nvp+j] = 0x8000 | 0; + else if ((int)va[2] == h && (int)vb[2] == h) + p[nvp+j] = 0x8000 | 1; + else if ((int)va[0] == w && (int)vb[0] == w) + p[nvp+j] = 0x8000 | 2; + else if ((int)va[2] == 0 && (int)vb[2] == 0) + p[nvp+j] = 0x8000 | 3; + } + } + } + + // Just allocate the mesh flags array. The user is resposible to fill it. + mesh.flags = (unsigned short*)rcAlloc(sizeof(unsigned short)*mesh.npolys, RC_ALLOC_PERM); + if (!mesh.flags) + { + ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.flags' (%d).", mesh.npolys); + return false; + } + memset(mesh.flags, 0, sizeof(unsigned short) * mesh.npolys); + + if (mesh.nverts > 0xffff) + { + ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: The resulting mesh has too many vertices %d (max %d). Data can be corrupted.", mesh.nverts, 0xffff); + } + if (mesh.npolys > 0xffff) + { + ctx->log(RC_LOG_ERROR, "rcBuildPolyMesh: The resulting mesh has too many polygons %d (max %d). Data can be corrupted.", mesh.npolys, 0xffff); + } + + return true; +} + +/// @see rcAllocPolyMesh, rcPolyMesh +bool rcMergePolyMeshes(rcContext* ctx, rcPolyMesh** meshes, const int nmeshes, rcPolyMesh& mesh) +{ + rcAssert(ctx); + + if (!nmeshes || !meshes) + return true; + + rcScopedTimer timer(ctx, RC_TIMER_MERGE_POLYMESH); + + mesh.nvp = meshes[0]->nvp; + mesh.cs = meshes[0]->cs; + mesh.ch = meshes[0]->ch; + rcVcopy(mesh.bmin, meshes[0]->bmin); + rcVcopy(mesh.bmax, meshes[0]->bmax); + + int maxVerts = 0; + int maxPolys = 0; + int maxVertsPerMesh = 0; + for (int i = 0; i < nmeshes; ++i) + { + rcVmin(mesh.bmin, meshes[i]->bmin); + rcVmax(mesh.bmax, meshes[i]->bmax); + maxVertsPerMesh = rcMax(maxVertsPerMesh, meshes[i]->nverts); + maxVerts += meshes[i]->nverts; + maxPolys += meshes[i]->npolys; + } + + mesh.nverts = 0; + mesh.verts = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxVerts*3, RC_ALLOC_PERM); + if (!mesh.verts) + { + ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.verts' (%d).", maxVerts*3); + return false; + } + + mesh.npolys = 0; + mesh.polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxPolys*2*mesh.nvp, RC_ALLOC_PERM); + if (!mesh.polys) + { + ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.polys' (%d).", maxPolys*2*mesh.nvp); + return false; + } + memset(mesh.polys, 0xff, sizeof(unsigned short)*maxPolys*2*mesh.nvp); + + mesh.regs = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxPolys, RC_ALLOC_PERM); + if (!mesh.regs) + { + ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.regs' (%d).", maxPolys); + return false; + } + memset(mesh.regs, 0, sizeof(unsigned short)*maxPolys); + + mesh.areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxPolys, RC_ALLOC_PERM); + if (!mesh.areas) + { + ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.areas' (%d).", maxPolys); + return false; + } + memset(mesh.areas, 0, sizeof(unsigned char)*maxPolys); + + mesh.flags = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxPolys, RC_ALLOC_PERM); + if (!mesh.flags) + { + ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'mesh.flags' (%d).", maxPolys); + return false; + } + memset(mesh.flags, 0, sizeof(unsigned short)*maxPolys); + + rcScopedDelete nextVert((int*)rcAlloc(sizeof(int)*maxVerts, RC_ALLOC_TEMP)); + if (!nextVert) + { + ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'nextVert' (%d).", maxVerts); + return false; + } + memset(nextVert, 0, sizeof(int)*maxVerts); + + rcScopedDelete firstVert((int*)rcAlloc(sizeof(int)*VERTEX_BUCKET_COUNT, RC_ALLOC_TEMP)); + if (!firstVert) + { + ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'firstVert' (%d).", VERTEX_BUCKET_COUNT); + return false; + } + for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i) + firstVert[i] = -1; + + rcScopedDelete vremap((unsigned short*)rcAlloc(sizeof(unsigned short)*maxVertsPerMesh, RC_ALLOC_PERM)); + if (!vremap) + { + ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Out of memory 'vremap' (%d).", maxVertsPerMesh); + return false; + } + memset(vremap, 0, sizeof(unsigned short)*maxVertsPerMesh); + + for (int i = 0; i < nmeshes; ++i) + { + const rcPolyMesh* pmesh = meshes[i]; + + const unsigned short ox = (unsigned short)floorf((pmesh->bmin[0]-mesh.bmin[0])/mesh.cs+0.5f); + const unsigned short oz = (unsigned short)floorf((pmesh->bmin[2]-mesh.bmin[2])/mesh.cs+0.5f); + + bool isMinX = (ox == 0); + bool isMinZ = (oz == 0); + bool isMaxX = ((unsigned short)floorf((mesh.bmax[0] - pmesh->bmax[0]) / mesh.cs + 0.5f)) == 0; + bool isMaxZ = ((unsigned short)floorf((mesh.bmax[2] - pmesh->bmax[2]) / mesh.cs + 0.5f)) == 0; + bool isOnBorder = (isMinX || isMinZ || isMaxX || isMaxZ); + + for (int j = 0; j < pmesh->nverts; ++j) + { + unsigned short* v = &pmesh->verts[j*3]; + vremap[j] = addVertex(v[0]+ox, v[1], v[2]+oz, + mesh.verts, firstVert, nextVert, mesh.nverts); + } + + for (int j = 0; j < pmesh->npolys; ++j) + { + unsigned short* tgt = &mesh.polys[mesh.npolys*2*mesh.nvp]; + unsigned short* src = &pmesh->polys[j*2*mesh.nvp]; + mesh.regs[mesh.npolys] = pmesh->regs[j]; + mesh.areas[mesh.npolys] = pmesh->areas[j]; + mesh.flags[mesh.npolys] = pmesh->flags[j]; + mesh.npolys++; + for (int k = 0; k < mesh.nvp; ++k) + { + if (src[k] == RC_MESH_NULL_IDX) break; + tgt[k] = vremap[src[k]]; + } + + if (isOnBorder) + { + for (int k = mesh.nvp; k < mesh.nvp * 2; ++k) + { + if (src[k] & 0x8000 && src[k] != 0xffff) + { + unsigned short dir = src[k] & 0xf; + switch (dir) + { + case 0: // Portal x- + if (isMinX) + tgt[k] = src[k]; + break; + case 1: // Portal z+ + if (isMaxZ) + tgt[k] = src[k]; + break; + case 2: // Portal x+ + if (isMaxX) + tgt[k] = src[k]; + break; + case 3: // Portal z- + if (isMinZ) + tgt[k] = src[k]; + break; + } + } + } + } + } + } + + // Calculate adjacency. + if (!buildMeshAdjacency(mesh.polys, mesh.npolys, mesh.nverts, mesh.nvp)) + { + ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: Adjacency failed."); + return false; + } + + if (mesh.nverts > 0xffff) + { + ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: The resulting mesh has too many vertices %d (max %d). Data can be corrupted.", mesh.nverts, 0xffff); + } + if (mesh.npolys > 0xffff) + { + ctx->log(RC_LOG_ERROR, "rcMergePolyMeshes: The resulting mesh has too many polygons %d (max %d). Data can be corrupted.", mesh.npolys, 0xffff); + } + + return true; +} + +bool rcCopyPolyMesh(rcContext* ctx, const rcPolyMesh& src, rcPolyMesh& dst) +{ + rcAssert(ctx); + + // Destination must be empty. + rcAssert(dst.verts == 0); + rcAssert(dst.polys == 0); + rcAssert(dst.regs == 0); + rcAssert(dst.areas == 0); + rcAssert(dst.flags == 0); + + dst.nverts = src.nverts; + dst.npolys = src.npolys; + dst.maxpolys = src.npolys; + dst.nvp = src.nvp; + rcVcopy(dst.bmin, src.bmin); + rcVcopy(dst.bmax, src.bmax); + 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) + { + ctx->log(RC_LOG_ERROR, "rcCopyPolyMesh: Out of memory 'dst.verts' (%d).", src.nverts*3); + return false; + } + memcpy(dst.verts, src.verts, sizeof(unsigned short)*src.nverts*3); + + dst.polys = (unsigned short*)rcAlloc(sizeof(unsigned short)*src.npolys*2*src.nvp, RC_ALLOC_PERM); + if (!dst.polys) + { + ctx->log(RC_LOG_ERROR, "rcCopyPolyMesh: Out of memory 'dst.polys' (%d).", src.npolys*2*src.nvp); + return false; + } + memcpy(dst.polys, src.polys, sizeof(unsigned short)*src.npolys*2*src.nvp); + + dst.regs = (unsigned short*)rcAlloc(sizeof(unsigned short)*src.npolys, RC_ALLOC_PERM); + if (!dst.regs) + { + ctx->log(RC_LOG_ERROR, "rcCopyPolyMesh: Out of memory 'dst.regs' (%d).", src.npolys); + return false; + } + memcpy(dst.regs, src.regs, sizeof(unsigned short)*src.npolys); + + dst.areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*src.npolys, RC_ALLOC_PERM); + if (!dst.areas) + { + ctx->log(RC_LOG_ERROR, "rcCopyPolyMesh: Out of memory 'dst.areas' (%d).", src.npolys); + return false; + } + memcpy(dst.areas, src.areas, sizeof(unsigned char)*src.npolys); + + dst.flags = (unsigned short*)rcAlloc(sizeof(unsigned short)*src.npolys, RC_ALLOC_PERM); + if (!dst.flags) + { + ctx->log(RC_LOG_ERROR, "rcCopyPolyMesh: Out of memory 'dst.flags' (%d).", src.npolys); + return false; + } + memcpy(dst.flags, src.flags, sizeof(unsigned short)*src.npolys); + + return true; +} diff --git a/external/recast/src/RecastMeshDetail.cpp b/external/recast/src/RecastMeshDetail.cpp new file mode 100644 index 0000000000..9a423cab8a --- /dev/null +++ b/external/recast/src/RecastMeshDetail.cpp @@ -0,0 +1,1464 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#include +#define _USE_MATH_DEFINES +#include +#include +#include +#include +#include "Recast.h" +#include "RecastAlloc.h" +#include "RecastAssert.h" + + +static const unsigned RC_UNSET_HEIGHT = 0xffff; + +struct rcHeightPatch +{ + inline rcHeightPatch() : data(0), xmin(0), ymin(0), width(0), height(0) {} + inline ~rcHeightPatch() { rcFree(data); } + unsigned short* data; + int xmin, ymin, width, height; +}; + + +inline float vdot2(const float* a, const float* b) +{ + return a[0]*b[0] + a[2]*b[2]; +} + +inline float vdistSq2(const float* p, const float* q) +{ + const float dx = q[0] - p[0]; + const float dy = q[2] - p[2]; + return dx*dx + dy*dy; +} + +inline float vdist2(const float* p, const float* q) +{ + return sqrtf(vdistSq2(p,q)); +} + +inline float vcross2(const float* p1, const float* p2, const float* p3) +{ + const float u1 = p2[0] - p1[0]; + const float v1 = p2[2] - p1[2]; + const float u2 = p3[0] - p1[0]; + const float v2 = p3[2] - p1[2]; + return u1 * v2 - v1 * u2; +} + +static bool circumCircle(const float* p1, const float* p2, const float* p3, + float* c, float& r) +{ + static const float EPS = 1e-6f; + // Calculate the circle relative to p1, to avoid some precision issues. + const float v1[3] = {0,0,0}; + float v2[3], v3[3]; + rcVsub(v2, p2,p1); + rcVsub(v3, p3,p1); + + const float cp = vcross2(v1, v2, v3); + if (fabsf(cp) > EPS) + { + const float v1Sq = vdot2(v1,v1); + const float v2Sq = vdot2(v2,v2); + const float v3Sq = vdot2(v3,v3); + c[0] = (v1Sq*(v2[2]-v3[2]) + v2Sq*(v3[2]-v1[2]) + v3Sq*(v1[2]-v2[2])) / (2*cp); + c[1] = 0; + c[2] = (v1Sq*(v3[0]-v2[0]) + v2Sq*(v1[0]-v3[0]) + v3Sq*(v2[0]-v1[0])) / (2*cp); + r = vdist2(c, v1); + rcVadd(c, c, p1); + return true; + } + + rcVcopy(c, p1); + r = 0; + return false; +} + +static float distPtTri(const float* p, const float* a, const float* b, const float* c) +{ + float v0[3], v1[3], v2[3]; + rcVsub(v0, c,a); + rcVsub(v1, b,a); + rcVsub(v2, p,a); + + const float dot00 = vdot2(v0, v0); + const float dot01 = vdot2(v0, v1); + const float dot02 = vdot2(v0, v2); + const float dot11 = vdot2(v1, v1); + const float dot12 = vdot2(v1, v2); + + // Compute barycentric coordinates + const float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01); + const float u = (dot11 * dot02 - dot01 * dot12) * invDenom; + float v = (dot00 * dot12 - dot01 * dot02) * invDenom; + + // If point lies inside the triangle, return interpolated y-coord. + static const float EPS = 1e-4f; + if (u >= -EPS && v >= -EPS && (u+v) <= 1+EPS) + { + const float y = a[1] + v0[1]*u + v1[1]*v; + return fabsf(y-p[1]); + } + return FLT_MAX; +} + +static float distancePtSeg(const float* pt, const float* p, const float* q) +{ + float pqx = q[0] - p[0]; + float pqy = q[1] - p[1]; + float pqz = q[2] - p[2]; + float dx = pt[0] - p[0]; + float dy = pt[1] - p[1]; + float dz = pt[2] - p[2]; + 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 = p[0] + t*pqx - pt[0]; + dy = p[1] + t*pqy - pt[1]; + dz = p[2] + t*pqz - pt[2]; + + return dx*dx + dy*dy + dz*dz; +} + +static float distancePtSeg2d(const float* pt, const float* p, const float* q) +{ + float pqx = q[0] - p[0]; + float pqz = q[2] - p[2]; + float dx = pt[0] - p[0]; + float dz = pt[2] - p[2]; + float d = pqx*pqx + pqz*pqz; + float t = pqx*dx + pqz*dz; + if (d > 0) + t /= d; + if (t < 0) + t = 0; + else if (t > 1) + t = 1; + + dx = p[0] + t*pqx - pt[0]; + dz = p[2] + t*pqz - pt[2]; + + return dx*dx + dz*dz; +} + +static float distToTriMesh(const float* p, const float* verts, const int /*nverts*/, const int* tris, const int ntris) +{ + float dmin = FLT_MAX; + for (int i = 0; i < ntris; ++i) + { + const float* va = &verts[tris[i*4+0]*3]; + const float* vb = &verts[tris[i*4+1]*3]; + const float* vc = &verts[tris[i*4+2]*3]; + float d = distPtTri(p, va,vb,vc); + if (d < dmin) + dmin = d; + } + if (dmin == FLT_MAX) return -1; + return dmin; +} + +static float distToPoly(int nvert, const float* verts, const float* p) +{ + + float dmin = FLT_MAX; + int i, j, c = 0; + for (i = 0, j = nvert-1; i < nvert; j = i++) + { + const float* vi = &verts[i*3]; + const float* vj = &verts[j*3]; + if (((vi[2] > p[2]) != (vj[2] > p[2])) && + (p[0] < (vj[0]-vi[0]) * (p[2]-vi[2]) / (vj[2]-vi[2]) + vi[0]) ) + c = !c; + dmin = rcMin(dmin, distancePtSeg2d(p, vj, vi)); + } + return c ? -dmin : dmin; +} + + +static unsigned short getHeight(const float fx, const float fy, const float fz, + const float /*cs*/, const float ics, const float ch, + const int radius, const rcHeightPatch& hp) +{ + int ix = (int)floorf(fx*ics + 0.01f); + int iz = (int)floorf(fz*ics + 0.01f); + ix = rcClamp(ix-hp.xmin, 0, hp.width - 1); + iz = rcClamp(iz-hp.ymin, 0, hp.height - 1); + unsigned short h = hp.data[ix+iz*hp.width]; + if (h == RC_UNSET_HEIGHT) + { + // Special case when data might be bad. + // Walk adjacent cells in a spiral up to 'radius', and look + // for a pixel which has a valid height. + int x = 1, z = 0, dx = 1, dz = 0; + int maxSize = radius * 2 + 1; + int maxIter = maxSize * maxSize - 1; + + int nextRingIterStart = 8; + int nextRingIters = 16; + + float dmin = FLT_MAX; + for (int i = 0; i < maxIter; i++) + { + const int nx = ix + x; + const int nz = iz + z; + + if (nx >= 0 && nz >= 0 && nx < hp.width && nz < hp.height) + { + const unsigned short nh = hp.data[nx + nz*hp.width]; + if (nh != RC_UNSET_HEIGHT) + { + const float d = fabsf(nh*ch - fy); + if (d < dmin) + { + h = nh; + dmin = d; + } + } + } + + // We are searching in a grid which looks approximately like this: + // __________ + // |2 ______ 2| + // | |1 __ 1| | + // | | |__| | | + // | |______| | + // |__________| + // We want to find the best height as close to the center cell as possible. This means that + // if we find a height in one of the neighbor cells to the center, we don't want to + // expand further out than the 8 neighbors - we want to limit our search to the closest + // of these "rings", but the best height in the ring. + // For example, the center is just 1 cell. We checked that at the entrance to the function. + // The next "ring" contains 8 cells (marked 1 above). Those are all the neighbors to the center cell. + // The next one again contains 16 cells (marked 2). In general each ring has 8 additional cells, which + // can be thought of as adding 2 cells around the "center" of each side when we expand the ring. + // Here we detect if we are about to enter the next ring, and if we are and we have found + // a height, we abort the search. + if (i + 1 == nextRingIterStart) + { + if (h != RC_UNSET_HEIGHT) + break; + + nextRingIterStart += nextRingIters; + nextRingIters += 8; + } + + if ((x == z) || ((x < 0) && (x == -z)) || ((x > 0) && (x == 1 - z))) + { + int tmp = dx; + dx = -dz; + dz = tmp; + } + x += dx; + z += dz; + } + } + return h; +} + + +enum EdgeValues +{ + EV_UNDEF = -1, + EV_HULL = -2, +}; + +static int findEdge(const int* edges, int nedges, int s, int t) +{ + for (int i = 0; i < nedges; i++) + { + const int* e = &edges[i*4]; + if ((e[0] == s && e[1] == t) || (e[0] == t && e[1] == s)) + return i; + } + return EV_UNDEF; +} + +static int addEdge(rcContext* ctx, int* edges, int& nedges, const int maxEdges, int s, int t, int l, int r) +{ + if (nedges >= maxEdges) + { + ctx->log(RC_LOG_ERROR, "addEdge: Too many edges (%d/%d).", nedges, maxEdges); + return EV_UNDEF; + } + + // Add edge if not already in the triangulation. + int e = findEdge(edges, nedges, s, t); + if (e == EV_UNDEF) + { + int* edge = &edges[nedges*4]; + edge[0] = s; + edge[1] = t; + edge[2] = l; + edge[3] = r; + return nedges++; + } + else + { + return EV_UNDEF; + } +} + +static void updateLeftFace(int* e, int s, int t, int f) +{ + if (e[0] == s && e[1] == t && e[2] == EV_UNDEF) + e[2] = f; + else if (e[1] == s && e[0] == t && e[3] == EV_UNDEF) + e[3] = f; +} + +static int overlapSegSeg2d(const float* a, const float* b, const float* c, const float* d) +{ + const float a1 = vcross2(a, b, d); + const float a2 = vcross2(a, b, c); + if (a1*a2 < 0.0f) + { + float a3 = vcross2(c, d, a); + float a4 = a3 + a2 - a1; + if (a3 * a4 < 0.0f) + return 1; + } + return 0; +} + +static bool overlapEdges(const float* pts, const int* edges, int nedges, int s1, int t1) +{ + for (int i = 0; i < nedges; ++i) + { + const int s0 = edges[i*4+0]; + const int t0 = edges[i*4+1]; + // Same or connected edges do not overlap. + if (s0 == s1 || s0 == t1 || t0 == s1 || t0 == t1) + continue; + if (overlapSegSeg2d(&pts[s0*3],&pts[t0*3], &pts[s1*3],&pts[t1*3])) + return true; + } + return false; +} + +static void completeFacet(rcContext* ctx, const float* pts, int npts, int* edges, int& nedges, const int maxEdges, int& nfaces, int e) +{ + static const float EPS = 1e-5f; + + int* edge = &edges[e*4]; + + // Cache s and t. + int s,t; + if (edge[2] == EV_UNDEF) + { + s = edge[0]; + t = edge[1]; + } + else if (edge[3] == EV_UNDEF) + { + s = edge[1]; + t = edge[0]; + } + else + { + // Edge already completed. + return; + } + + // Find best point on left of edge. + int pt = npts; + float c[3] = {0,0,0}; + float r = -1; + for (int u = 0; u < npts; ++u) + { + if (u == s || u == t) continue; + if (vcross2(&pts[s*3], &pts[t*3], &pts[u*3]) > EPS) + { + if (r < 0) + { + // The circle is not updated yet, do it now. + pt = u; + circumCircle(&pts[s*3], &pts[t*3], &pts[u*3], c, r); + continue; + } + const float d = vdist2(c, &pts[u*3]); + const float tol = 0.001f; + if (d > r*(1+tol)) + { + // Outside current circumcircle, skip. + continue; + } + else if (d < r*(1-tol)) + { + // Inside safe circumcircle, update circle. + pt = u; + circumCircle(&pts[s*3], &pts[t*3], &pts[u*3], c, r); + } + else + { + // Inside epsilon circum circle, do extra tests to make sure the edge is valid. + // s-u and t-u cannot overlap with s-pt nor t-pt if they exists. + if (overlapEdges(pts, edges, nedges, s,u)) + continue; + if (overlapEdges(pts, edges, nedges, t,u)) + continue; + // Edge is valid. + pt = u; + circumCircle(&pts[s*3], &pts[t*3], &pts[u*3], c, r); + } + } + } + + // Add new triangle or update edge info if s-t is on hull. + if (pt < npts) + { + // Update face information of edge being completed. + updateLeftFace(&edges[e*4], s, t, nfaces); + + // Add new edge or update face info of old edge. + e = findEdge(edges, nedges, pt, s); + if (e == EV_UNDEF) + addEdge(ctx, edges, nedges, maxEdges, pt, s, nfaces, EV_UNDEF); + else + updateLeftFace(&edges[e*4], pt, s, nfaces); + + // Add new edge or update face info of old edge. + e = findEdge(edges, nedges, t, pt); + if (e == EV_UNDEF) + addEdge(ctx, edges, nedges, maxEdges, t, pt, nfaces, EV_UNDEF); + else + updateLeftFace(&edges[e*4], t, pt, nfaces); + + nfaces++; + } + else + { + updateLeftFace(&edges[e*4], s, t, EV_HULL); + } +} + +static void delaunayHull(rcContext* ctx, const int npts, const float* pts, + const int nhull, const int* hull, + rcIntArray& tris, rcIntArray& edges) +{ + int nfaces = 0; + int nedges = 0; + const int maxEdges = npts*10; + edges.resize(maxEdges*4); + + for (int i = 0, j = nhull-1; i < nhull; j=i++) + addEdge(ctx, &edges[0], nedges, maxEdges, hull[j],hull[i], EV_HULL, EV_UNDEF); + + int currentEdge = 0; + while (currentEdge < nedges) + { + if (edges[currentEdge*4+2] == EV_UNDEF) + completeFacet(ctx, pts, npts, &edges[0], nedges, maxEdges, nfaces, currentEdge); + if (edges[currentEdge*4+3] == EV_UNDEF) + completeFacet(ctx, pts, npts, &edges[0], nedges, maxEdges, nfaces, currentEdge); + currentEdge++; + } + + // Create tris + tris.resize(nfaces*4); + for (int i = 0; i < nfaces*4; ++i) + tris[i] = -1; + + for (int i = 0; i < nedges; ++i) + { + const int* e = &edges[i*4]; + if (e[3] >= 0) + { + // Left face + int* t = &tris[e[3]*4]; + if (t[0] == -1) + { + t[0] = e[0]; + t[1] = e[1]; + } + else if (t[0] == e[1]) + t[2] = e[0]; + else if (t[1] == e[0]) + t[2] = e[1]; + } + if (e[2] >= 0) + { + // Right + int* t = &tris[e[2]*4]; + if (t[0] == -1) + { + t[0] = e[1]; + t[1] = e[0]; + } + else if (t[0] == e[0]) + t[2] = e[1]; + else if (t[1] == e[1]) + t[2] = e[0]; + } + } + + for (int i = 0; i < tris.size()/4; ++i) + { + int* t = &tris[i*4]; + if (t[0] == -1 || t[1] == -1 || t[2] == -1) + { + ctx->log(RC_LOG_WARNING, "delaunayHull: Removing dangling face %d [%d,%d,%d].", i, t[0],t[1],t[2]); + t[0] = tris[tris.size()-4]; + t[1] = tris[tris.size()-3]; + t[2] = tris[tris.size()-2]; + t[3] = tris[tris.size()-1]; + tris.resize(tris.size()-4); + --i; + } + } +} + +// Calculate minimum extend of the polygon. +static float polyMinExtent(const float* verts, const int nverts) +{ + float minDist = FLT_MAX; + for (int i = 0; i < nverts; i++) + { + const int ni = (i+1) % nverts; + const float* p1 = &verts[i*3]; + const float* p2 = &verts[ni*3]; + float maxEdgeDist = 0; + for (int j = 0; j < nverts; j++) + { + if (j == i || j == ni) continue; + float d = distancePtSeg2d(&verts[j*3], p1,p2); + maxEdgeDist = rcMax(maxEdgeDist, d); + } + minDist = rcMin(minDist, maxEdgeDist); + } + return rcSqrt(minDist); +} + +// 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; } + +static void triangulateHull(const int /*nverts*/, const float* verts, const int nhull, const int* hull, const int nin, rcIntArray& tris) +{ + int start = 0, left = 1, right = nhull-1; + + // Start from an ear with shortest perimeter. + // This tends to favor well formed triangles as starting point. + float dmin = FLT_MAX; + for (int i = 0; i < nhull; i++) + { + if (hull[i] >= nin) continue; // Ears are triangles with original vertices as middle vertex while others are actually line segments on edges + int pi = prev(i, nhull); + int ni = next(i, nhull); + const float* pv = &verts[hull[pi]*3]; + const float* cv = &verts[hull[i]*3]; + const float* nv = &verts[hull[ni]*3]; + const float d = vdist2(pv,cv) + vdist2(cv,nv) + vdist2(nv,pv); + if (d < dmin) + { + start = i; + left = ni; + right = pi; + dmin = d; + } + } + + // Add first triangle + tris.push(hull[start]); + tris.push(hull[left]); + tris.push(hull[right]); + tris.push(0); + + // Triangulate the polygon by moving left or right, + // depending on which triangle has shorter perimeter. + // This heuristic was chose emprically, since it seems + // handle tesselated straight edges well. + while (next(left, nhull) != right) + { + // Check to see if se should advance left or right. + int nleft = next(left, nhull); + int nright = prev(right, nhull); + + const float* cvleft = &verts[hull[left]*3]; + const float* nvleft = &verts[hull[nleft]*3]; + const float* cvright = &verts[hull[right]*3]; + const float* nvright = &verts[hull[nright]*3]; + const float dleft = vdist2(cvleft, nvleft) + vdist2(nvleft, cvright); + const float dright = vdist2(cvright, nvright) + vdist2(cvleft, nvright); + + if (dleft < dright) + { + tris.push(hull[left]); + tris.push(hull[nleft]); + tris.push(hull[right]); + tris.push(0); + left = nleft; + } + else + { + tris.push(hull[left]); + tris.push(hull[nright]); + tris.push(hull[right]); + tris.push(0); + right = nright; + } + } +} + + +inline float getJitterX(const int i) +{ + return (((i * 0x8da6b343) & 0xffff) / 65535.0f * 2.0f) - 1.0f; +} + +inline float getJitterY(const int i) +{ + return (((i * 0xd8163841) & 0xffff) / 65535.0f * 2.0f) - 1.0f; +} + +static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin, + const float sampleDist, const float sampleMaxError, + const int heightSearchRadius, const rcCompactHeightfield& chf, + const rcHeightPatch& hp, float* verts, int& nverts, + rcIntArray& tris, rcIntArray& edges, rcIntArray& samples) +{ + static const int MAX_VERTS = 127; + static const int MAX_TRIS = 255; // Max tris for delaunay is 2n-2-k (n=num verts, k=num hull verts). + static const int MAX_VERTS_PER_EDGE = 32; + float edge[(MAX_VERTS_PER_EDGE+1)*3]; + int hull[MAX_VERTS]; + int nhull = 0; + + nverts = nin; + + for (int i = 0; i < nin; ++i) + rcVcopy(&verts[i*3], &in[i*3]); + + edges.resize(0); + tris.resize(0); + + const float cs = chf.cs; + const float ics = 1.0f/cs; + + // Calculate minimum extents of the polygon based on input data. + float minExtent = polyMinExtent(verts, nverts); + + // Tessellate outlines. + // This is done in separate pass in order to ensure + // seamless height values across the ply boundaries. + if (sampleDist > 0) + { + for (int i = 0, j = nin-1; i < nin; j=i++) + { + const float* vj = &in[j*3]; + const float* vi = &in[i*3]; + bool swapped = false; + // Make sure the segments are always handled in same order + // using lexological sort or else there will be seams. + if (fabsf(vj[0]-vi[0]) < 1e-6f) + { + if (vj[2] > vi[2]) + { + rcSwap(vj,vi); + swapped = true; + } + } + else + { + if (vj[0] > vi[0]) + { + rcSwap(vj,vi); + swapped = true; + } + } + // Create samples along the edge. + float dx = vi[0] - vj[0]; + float dy = vi[1] - vj[1]; + float dz = vi[2] - vj[2]; + float d = sqrtf(dx*dx + dz*dz); + int nn = 1 + (int)floorf(d/sampleDist); + if (nn >= MAX_VERTS_PER_EDGE) nn = MAX_VERTS_PER_EDGE-1; + if (nverts+nn >= MAX_VERTS) + nn = MAX_VERTS-1-nverts; + + for (int k = 0; k <= nn; ++k) + { + float u = (float)k/(float)nn; + float* pos = &edge[k*3]; + pos[0] = vj[0] + dx*u; + pos[1] = vj[1] + dy*u; + pos[2] = vj[2] + dz*u; + pos[1] = getHeight(pos[0],pos[1],pos[2], cs, ics, chf.ch, heightSearchRadius, hp)*chf.ch; + } + // Simplify samples. + int idx[MAX_VERTS_PER_EDGE] = {0,nn}; + int nidx = 2; + for (int k = 0; k < nidx-1; ) + { + const int a = idx[k]; + const int b = idx[k+1]; + const float* va = &edge[a*3]; + const float* vb = &edge[b*3]; + // Find maximum deviation along the segment. + float maxd = 0; + int maxi = -1; + for (int m = a+1; m < b; ++m) + { + float dev = distancePtSeg(&edge[m*3],va,vb); + if (dev > maxd) + { + maxd = dev; + maxi = m; + } + } + // If the max deviation is larger than accepted error, + // add new point, else continue to next segment. + if (maxi != -1 && maxd > rcSqr(sampleMaxError)) + { + for (int m = nidx; m > k; --m) + idx[m] = idx[m-1]; + idx[k+1] = maxi; + nidx++; + } + else + { + ++k; + } + } + + hull[nhull++] = j; + // Add new vertices. + if (swapped) + { + for (int k = nidx-2; k > 0; --k) + { + rcVcopy(&verts[nverts*3], &edge[idx[k]*3]); + hull[nhull++] = nverts; + nverts++; + } + } + else + { + for (int k = 1; k < nidx-1; ++k) + { + rcVcopy(&verts[nverts*3], &edge[idx[k]*3]); + hull[nhull++] = nverts; + nverts++; + } + } + } + } + + // If the polygon minimum extent is small (sliver or small triangle), do not try to add internal points. + if (minExtent < sampleDist*2) + { + triangulateHull(nverts, verts, nhull, hull, nin, tris); + return true; + } + + // Tessellate the base mesh. + // We're using the triangulateHull instead of delaunayHull as it tends to + // create a bit better triangulation for long thin triangles when there + // are no internal points. + triangulateHull(nverts, verts, nhull, hull, nin, tris); + + if (tris.size() == 0) + { + // Could not triangulate the poly, make sure there is some valid data there. + ctx->log(RC_LOG_WARNING, "buildPolyDetail: Could not triangulate polygon (%d verts).", nverts); + return true; + } + + if (sampleDist > 0) + { + // Create sample locations in a grid. + float bmin[3], bmax[3]; + rcVcopy(bmin, in); + rcVcopy(bmax, in); + for (int i = 1; i < nin; ++i) + { + rcVmin(bmin, &in[i*3]); + rcVmax(bmax, &in[i*3]); + } + int x0 = (int)floorf(bmin[0]/sampleDist); + int x1 = (int)ceilf(bmax[0]/sampleDist); + int z0 = (int)floorf(bmin[2]/sampleDist); + int z1 = (int)ceilf(bmax[2]/sampleDist); + samples.resize(0); + for (int z = z0; z < z1; ++z) + { + for (int x = x0; x < x1; ++x) + { + float pt[3]; + pt[0] = x*sampleDist; + pt[1] = (bmax[1]+bmin[1])*0.5f; + pt[2] = z*sampleDist; + // Make sure the samples are not too close to the edges. + if (distToPoly(nin,in,pt) > -sampleDist/2) continue; + samples.push(x); + samples.push(getHeight(pt[0], pt[1], pt[2], cs, ics, chf.ch, heightSearchRadius, hp)); + samples.push(z); + samples.push(0); // Not added + } + } + + // Add the samples starting from the one that has the most + // error. The procedure stops when all samples are added + // or when the max error is within treshold. + const int nsamples = samples.size()/4; + for (int iter = 0; iter < nsamples; ++iter) + { + if (nverts >= MAX_VERTS) + break; + + // Find sample with most error. + float bestpt[3] = {0,0,0}; + float bestd = 0; + int besti = -1; + for (int i = 0; i < nsamples; ++i) + { + const int* s = &samples[i*4]; + if (s[3]) continue; // skip added. + float pt[3]; + // The sample location is jittered to get rid of some bad triangulations + // which are cause by symmetrical data from the grid structure. + pt[0] = s[0]*sampleDist + getJitterX(i)*cs*0.1f; + pt[1] = s[1]*chf.ch; + pt[2] = s[2]*sampleDist + getJitterY(i)*cs*0.1f; + float d = distToTriMesh(pt, verts, nverts, &tris[0], tris.size()/4); + if (d < 0) continue; // did not hit the mesh. + if (d > bestd) + { + bestd = d; + besti = i; + rcVcopy(bestpt,pt); + } + } + // If the max error is within accepted threshold, stop tesselating. + if (bestd <= sampleMaxError || besti == -1) + break; + // Mark sample as added. + samples[besti*4+3] = 1; + // Add the new sample point. + rcVcopy(&verts[nverts*3],bestpt); + nverts++; + + // Create new triangulation. + // TODO: Incremental add instead of full rebuild. + edges.resize(0); + tris.resize(0); + delaunayHull(ctx, nverts, verts, nhull, hull, tris, edges); + } + } + + const int ntris = tris.size()/4; + if (ntris > MAX_TRIS) + { + tris.resize(MAX_TRIS*4); + ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Shrinking triangle count from %d to max %d.", ntris, MAX_TRIS); + } + + return true; +} + +static void seedArrayWithPolyCenter(rcContext* ctx, const rcCompactHeightfield& chf, + const unsigned short* poly, const int npoly, + const unsigned short* verts, const int bs, + rcHeightPatch& hp, rcIntArray& array) +{ + // Note: Reads to the compact heightfield are offset by border size (bs) + // since border size offset is already removed from the polymesh vertices. + + static const int offset[9*2] = + { + 0,0, -1,-1, 0,-1, 1,-1, 1,0, 1,1, 0,1, -1,1, -1,0, + }; + + // Find cell closest to a poly vertex + int startCellX = 0, startCellY = 0, startSpanIndex = -1; + int dmin = RC_UNSET_HEIGHT; + for (int j = 0; j < npoly && dmin > 0; ++j) + { + for (int k = 0; k < 9 && dmin > 0; ++k) + { + const int ax = (int)verts[poly[j]*3+0] + offset[k*2+0]; + const int ay = (int)verts[poly[j]*3+1]; + const int az = (int)verts[poly[j]*3+2] + offset[k*2+1]; + if (ax < hp.xmin || ax >= hp.xmin+hp.width || + az < hp.ymin || az >= hp.ymin+hp.height) + continue; + + const rcCompactCell& c = chf.cells[(ax+bs)+(az+bs)*chf.width]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni && dmin > 0; ++i) + { + const rcCompactSpan& s = chf.spans[i]; + int d = rcAbs(ay - (int)s.y); + if (d < dmin) + { + startCellX = ax; + startCellY = az; + startSpanIndex = i; + dmin = d; + } + } + } + } + + rcAssert(startSpanIndex != -1); + // Find center of the polygon + int pcx = 0, pcy = 0; + for (int j = 0; j < npoly; ++j) + { + pcx += (int)verts[poly[j]*3+0]; + pcy += (int)verts[poly[j]*3+2]; + } + pcx /= npoly; + pcy /= npoly; + + // Use seeds array as a stack for DFS + array.resize(0); + array.push(startCellX); + array.push(startCellY); + array.push(startSpanIndex); + + int dirs[] = { 0, 1, 2, 3 }; + memset(hp.data, 0, sizeof(unsigned short)*hp.width*hp.height); + // DFS to move to the center. Note that we need a DFS here and can not just move + // directly towards the center without recording intermediate nodes, even though the polygons + // are convex. In very rare we can get stuck due to contour simplification if we do not + // record nodes. + int cx = -1, cy = -1, ci = -1; + while (true) + { + if (array.size() < 3) + { + ctx->log(RC_LOG_WARNING, "Walk towards polygon center failed to reach center"); + break; + } + + ci = array.pop(); + cy = array.pop(); + cx = array.pop(); + + if (cx == pcx && cy == pcy) + break; + + // If we are already at the correct X-position, prefer direction + // directly towards the center in the Y-axis; otherwise prefer + // direction in the X-axis + int directDir; + if (cx == pcx) + directDir = rcGetDirForOffset(0, pcy > cy ? 1 : -1); + else + directDir = rcGetDirForOffset(pcx > cx ? 1 : -1, 0); + + // Push the direct dir last so we start with this on next iteration + rcSwap(dirs[directDir], dirs[3]); + + const rcCompactSpan& cs = chf.spans[ci]; + for (int i = 0; i < 4; i++) + { + int dir = dirs[i]; + if (rcGetCon(cs, dir) == RC_NOT_CONNECTED) + continue; + + int newX = cx + rcGetDirOffsetX(dir); + int newY = cy + rcGetDirOffsetY(dir); + + int hpx = newX - hp.xmin; + int hpy = newY - hp.ymin; + if (hpx < 0 || hpx >= hp.width || hpy < 0 || hpy >= hp.height) + continue; + + if (hp.data[hpx+hpy*hp.width] != 0) + continue; + + hp.data[hpx+hpy*hp.width] = 1; + array.push(newX); + array.push(newY); + array.push((int)chf.cells[(newX+bs)+(newY+bs)*chf.width].index + rcGetCon(cs, dir)); + } + + rcSwap(dirs[directDir], dirs[3]); + } + + array.resize(0); + // getHeightData seeds are given in coordinates with borders + array.push(cx+bs); + array.push(cy+bs); + array.push(ci); + + memset(hp.data, 0xff, sizeof(unsigned short)*hp.width*hp.height); + const rcCompactSpan& cs = chf.spans[ci]; + hp.data[cx-hp.xmin+(cy-hp.ymin)*hp.width] = cs.y; +} + + +static void push3(rcIntArray& queue, int v1, int v2, int v3) +{ + queue.resize(queue.size() + 3); + queue[queue.size() - 3] = v1; + queue[queue.size() - 2] = v2; + queue[queue.size() - 1] = v3; +} + +static void getHeightData(rcContext* ctx, const rcCompactHeightfield& chf, + const unsigned short* poly, const int npoly, + const unsigned short* verts, const int bs, + rcHeightPatch& hp, rcIntArray& queue, + int region) +{ + // Note: Reads to the compact heightfield are offset by border size (bs) + // since border size offset is already removed from the polymesh vertices. + + queue.resize(0); + // Set all heights to RC_UNSET_HEIGHT. + memset(hp.data, 0xff, sizeof(unsigned short)*hp.width*hp.height); + + bool empty = true; + + // We cannot sample from this poly if it was created from polys + // of different regions. If it was then it could potentially be overlapping + // with polys of that region and the heights sampled here could be wrong. + if (region != RC_MULTIPLE_REGS) + { + // Copy the height from the same region, and mark region borders + // as seed points to fill the rest. + for (int hy = 0; hy < hp.height; hy++) + { + int y = hp.ymin + hy + bs; + for (int hx = 0; hx < hp.width; hx++) + { + int x = hp.xmin + hx + bs; + const rcCompactCell& c = chf.cells[x + y*chf.width]; + for (int i = (int)c.index, ni = (int)(c.index + c.count); i < ni; ++i) + { + const rcCompactSpan& s = chf.spans[i]; + if (s.reg == region) + { + // Store height + hp.data[hx + hy*hp.width] = s.y; + empty = false; + + // If any of the neighbours is not in same region, + // add the current location as flood fill start + bool border = false; + for (int dir = 0; dir < 4; ++dir) + { + if (rcGetCon(s, dir) != RC_NOT_CONNECTED) + { + const int ax = x + rcGetDirOffsetX(dir); + const int ay = y + rcGetDirOffsetY(dir); + const int ai = (int)chf.cells[ax + ay*chf.width].index + rcGetCon(s, dir); + const rcCompactSpan& as = chf.spans[ai]; + if (as.reg != region) + { + border = true; + break; + } + } + } + if (border) + push3(queue, x, y, i); + break; + } + } + } + } + } + + // if the polygon does not contain any points from the current region (rare, but happens) + // or if it could potentially be overlapping polygons of the same region, + // then use the center as the seed point. + if (empty) + seedArrayWithPolyCenter(ctx, chf, poly, npoly, verts, bs, hp, queue); + + static const int RETRACT_SIZE = 256; + int head = 0; + + // We assume the seed is centered in the polygon, so a BFS to collect + // height data will ensure we do not move onto overlapping polygons and + // sample wrong heights. + while (head*3 < queue.size()) + { + int cx = queue[head*3+0]; + int cy = queue[head*3+1]; + int ci = queue[head*3+2]; + head++; + if (head >= RETRACT_SIZE) + { + head = 0; + if (queue.size() > RETRACT_SIZE*3) + memmove(&queue[0], &queue[RETRACT_SIZE*3], sizeof(int)*(queue.size()-RETRACT_SIZE*3)); + queue.resize(queue.size()-RETRACT_SIZE*3); + } + + const rcCompactSpan& cs = chf.spans[ci]; + for (int dir = 0; dir < 4; ++dir) + { + if (rcGetCon(cs, dir) == RC_NOT_CONNECTED) continue; + + const int ax = cx + rcGetDirOffsetX(dir); + const int ay = cy + rcGetDirOffsetY(dir); + const int hx = ax - hp.xmin - bs; + const int hy = ay - hp.ymin - bs; + + if ((unsigned int)hx >= (unsigned int)hp.width || (unsigned int)hy >= (unsigned int)hp.height) + continue; + + if (hp.data[hx + hy*hp.width] != RC_UNSET_HEIGHT) + continue; + + const int ai = (int)chf.cells[ax + ay*chf.width].index + rcGetCon(cs, dir); + const rcCompactSpan& as = chf.spans[ai]; + + hp.data[hx + hy*hp.width] = as.y; + + push3(queue, ax, ay, ai); + } + } +} + +static unsigned char getEdgeFlags(const float* va, const float* vb, + const float* vpoly, const int npoly) +{ + // The flag returned by this function matches dtDetailTriEdgeFlags in Detour. + // Figure out if edge (va,vb) is part of the polygon boundary. + static const float thrSqr = rcSqr(0.001f); + for (int i = 0, j = npoly-1; i < npoly; j=i++) + { + if (distancePtSeg2d(va, &vpoly[j*3], &vpoly[i*3]) < thrSqr && + distancePtSeg2d(vb, &vpoly[j*3], &vpoly[i*3]) < thrSqr) + return 1; + } + return 0; +} + +static unsigned char getTriFlags(const float* va, const float* vb, const float* vc, + const float* vpoly, const int npoly) +{ + unsigned char flags = 0; + flags |= getEdgeFlags(va,vb,vpoly,npoly) << 0; + flags |= getEdgeFlags(vb,vc,vpoly,npoly) << 2; + flags |= getEdgeFlags(vc,va,vpoly,npoly) << 4; + return flags; +} + +/// @par +/// +/// See the #rcConfig documentation for more information on the configuration parameters. +/// +/// @see rcAllocPolyMeshDetail, rcPolyMesh, rcCompactHeightfield, rcPolyMeshDetail, rcConfig +bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompactHeightfield& chf, + const float sampleDist, const float sampleMaxError, + rcPolyMeshDetail& dmesh) +{ + rcAssert(ctx); + + rcScopedTimer timer(ctx, RC_TIMER_BUILD_POLYMESHDETAIL); + + if (mesh.nverts == 0 || mesh.npolys == 0) + return true; + + const int nvp = mesh.nvp; + const float cs = mesh.cs; + const float ch = mesh.ch; + const float* orig = mesh.bmin; + const int borderSize = mesh.borderSize; + const int heightSearchRadius = rcMax(1, (int)ceilf(mesh.maxEdgeError)); + + rcIntArray edges(64); + rcIntArray tris(512); + rcIntArray arr(512); + rcIntArray samples(512); + float verts[256*3]; + rcHeightPatch hp; + int nPolyVerts = 0; + int maxhw = 0, maxhh = 0; + + rcScopedDelete bounds((int*)rcAlloc(sizeof(int)*mesh.npolys*4, RC_ALLOC_TEMP)); + if (!bounds) + { + ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'bounds' (%d).", mesh.npolys*4); + return false; + } + rcScopedDelete poly((float*)rcAlloc(sizeof(float)*nvp*3, RC_ALLOC_TEMP)); + if (!poly) + { + ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'poly' (%d).", nvp*3); + return false; + } + + // Find max size for a polygon area. + for (int i = 0; i < mesh.npolys; ++i) + { + const unsigned short* p = &mesh.polys[i*nvp*2]; + int& xmin = bounds[i*4+0]; + int& xmax = bounds[i*4+1]; + int& ymin = bounds[i*4+2]; + int& ymax = bounds[i*4+3]; + xmin = chf.width; + xmax = 0; + ymin = chf.height; + ymax = 0; + for (int j = 0; j < nvp; ++j) + { + if(p[j] == RC_MESH_NULL_IDX) break; + const unsigned short* v = &mesh.verts[p[j]*3]; + xmin = rcMin(xmin, (int)v[0]); + xmax = rcMax(xmax, (int)v[0]); + ymin = rcMin(ymin, (int)v[2]); + ymax = rcMax(ymax, (int)v[2]); + nPolyVerts++; + } + xmin = rcMax(0,xmin-1); + xmax = rcMin(chf.width,xmax+1); + ymin = rcMax(0,ymin-1); + ymax = rcMin(chf.height,ymax+1); + if (xmin >= xmax || ymin >= ymax) continue; + maxhw = rcMax(maxhw, xmax-xmin); + maxhh = rcMax(maxhh, ymax-ymin); + } + + hp.data = (unsigned short*)rcAlloc(sizeof(unsigned short)*maxhw*maxhh, RC_ALLOC_TEMP); + if (!hp.data) + { + ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'hp.data' (%d).", maxhw*maxhh); + return false; + } + + dmesh.nmeshes = mesh.npolys; + dmesh.nverts = 0; + dmesh.ntris = 0; + dmesh.meshes = (unsigned int*)rcAlloc(sizeof(unsigned int)*dmesh.nmeshes*4, RC_ALLOC_PERM); + if (!dmesh.meshes) + { + ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.meshes' (%d).", dmesh.nmeshes*4); + return false; + } + + int vcap = nPolyVerts+nPolyVerts/2; + int tcap = vcap*2; + + dmesh.nverts = 0; + dmesh.verts = (float*)rcAlloc(sizeof(float)*vcap*3, RC_ALLOC_PERM); + if (!dmesh.verts) + { + ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.verts' (%d).", vcap*3); + return false; + } + dmesh.ntris = 0; + dmesh.tris = (unsigned char*)rcAlloc(sizeof(unsigned char)*tcap*4, RC_ALLOC_PERM); + if (!dmesh.tris) + { + ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.tris' (%d).", tcap*4); + return false; + } + + for (int i = 0; i < mesh.npolys; ++i) + { + const unsigned short* p = &mesh.polys[i*nvp*2]; + + // Store polygon vertices for processing. + int npoly = 0; + for (int j = 0; j < nvp; ++j) + { + if(p[j] == RC_MESH_NULL_IDX) break; + const unsigned short* v = &mesh.verts[p[j]*3]; + poly[j*3+0] = v[0]*cs; + poly[j*3+1] = v[1]*ch; + poly[j*3+2] = v[2]*cs; + npoly++; + } + + // Get the height data from the area of the polygon. + hp.xmin = bounds[i*4+0]; + hp.ymin = bounds[i*4+2]; + hp.width = bounds[i*4+1]-bounds[i*4+0]; + hp.height = bounds[i*4+3]-bounds[i*4+2]; + getHeightData(ctx, chf, p, npoly, mesh.verts, borderSize, hp, arr, mesh.regs[i]); + + // Build detail mesh. + int nverts = 0; + if (!buildPolyDetail(ctx, poly, npoly, + sampleDist, sampleMaxError, + heightSearchRadius, chf, hp, + verts, nverts, tris, + edges, samples)) + { + return false; + } + + // Move detail verts to world space. + for (int j = 0; j < nverts; ++j) + { + verts[j*3+0] += orig[0]; + verts[j*3+1] += orig[1] + chf.ch; // Is this offset necessary? + verts[j*3+2] += orig[2]; + } + // Offset poly too, will be used to flag checking. + for (int j = 0; j < npoly; ++j) + { + poly[j*3+0] += orig[0]; + poly[j*3+1] += orig[1]; + poly[j*3+2] += orig[2]; + } + + // Store detail submesh. + const int ntris = tris.size()/4; + + dmesh.meshes[i*4+0] = (unsigned int)dmesh.nverts; + dmesh.meshes[i*4+1] = (unsigned int)nverts; + dmesh.meshes[i*4+2] = (unsigned int)dmesh.ntris; + dmesh.meshes[i*4+3] = (unsigned int)ntris; + + // Store vertices, allocate more memory if necessary. + if (dmesh.nverts+nverts > vcap) + { + while (dmesh.nverts+nverts > vcap) + vcap += 256; + + float* newv = (float*)rcAlloc(sizeof(float)*vcap*3, RC_ALLOC_PERM); + if (!newv) + { + ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'newv' (%d).", vcap*3); + return false; + } + if (dmesh.nverts) + memcpy(newv, dmesh.verts, sizeof(float)*3*dmesh.nverts); + rcFree(dmesh.verts); + dmesh.verts = newv; + } + for (int j = 0; j < nverts; ++j) + { + dmesh.verts[dmesh.nverts*3+0] = verts[j*3+0]; + dmesh.verts[dmesh.nverts*3+1] = verts[j*3+1]; + dmesh.verts[dmesh.nverts*3+2] = verts[j*3+2]; + dmesh.nverts++; + } + + // Store triangles, allocate more memory if necessary. + if (dmesh.ntris+ntris > tcap) + { + while (dmesh.ntris+ntris > tcap) + tcap += 256; + unsigned char* newt = (unsigned char*)rcAlloc(sizeof(unsigned char)*tcap*4, RC_ALLOC_PERM); + if (!newt) + { + ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'newt' (%d).", tcap*4); + return false; + } + if (dmesh.ntris) + memcpy(newt, dmesh.tris, sizeof(unsigned char)*4*dmesh.ntris); + rcFree(dmesh.tris); + dmesh.tris = newt; + } + for (int j = 0; j < ntris; ++j) + { + const int* t = &tris[j*4]; + dmesh.tris[dmesh.ntris*4+0] = (unsigned char)t[0]; + dmesh.tris[dmesh.ntris*4+1] = (unsigned char)t[1]; + dmesh.tris[dmesh.ntris*4+2] = (unsigned char)t[2]; + dmesh.tris[dmesh.ntris*4+3] = getTriFlags(&verts[t[0]*3], &verts[t[1]*3], &verts[t[2]*3], poly, npoly); + dmesh.ntris++; + } + } + + return true; +} + +/// @see rcAllocPolyMeshDetail, rcPolyMeshDetail +bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int nmeshes, rcPolyMeshDetail& mesh) +{ + rcAssert(ctx); + + rcScopedTimer timer(ctx, RC_TIMER_MERGE_POLYMESHDETAIL); + + int maxVerts = 0; + int maxTris = 0; + int maxMeshes = 0; + + for (int i = 0; i < nmeshes; ++i) + { + if (!meshes[i]) continue; + maxVerts += meshes[i]->nverts; + maxTris += meshes[i]->ntris; + maxMeshes += meshes[i]->nmeshes; + } + + mesh.nmeshes = 0; + mesh.meshes = (unsigned int*)rcAlloc(sizeof(unsigned int)*maxMeshes*4, RC_ALLOC_PERM); + if (!mesh.meshes) + { + ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'pmdtl.meshes' (%d).", maxMeshes*4); + return false; + } + + mesh.ntris = 0; + mesh.tris = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxTris*4, RC_ALLOC_PERM); + if (!mesh.tris) + { + ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.tris' (%d).", maxTris*4); + return false; + } + + mesh.nverts = 0; + mesh.verts = (float*)rcAlloc(sizeof(float)*maxVerts*3, RC_ALLOC_PERM); + if (!mesh.verts) + { + ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.verts' (%d).", maxVerts*3); + return false; + } + + // Merge datas. + for (int i = 0; i < nmeshes; ++i) + { + rcPolyMeshDetail* dm = meshes[i]; + if (!dm) continue; + for (int j = 0; j < dm->nmeshes; ++j) + { + unsigned int* dst = &mesh.meshes[mesh.nmeshes*4]; + unsigned int* src = &dm->meshes[j*4]; + dst[0] = (unsigned int)mesh.nverts+src[0]; + dst[1] = src[1]; + dst[2] = (unsigned int)mesh.ntris+src[2]; + dst[3] = src[3]; + mesh.nmeshes++; + } + + for (int k = 0; k < dm->nverts; ++k) + { + rcVcopy(&mesh.verts[mesh.nverts*3], &dm->verts[k*3]); + mesh.nverts++; + } + for (int k = 0; k < dm->ntris; ++k) + { + mesh.tris[mesh.ntris*4+0] = dm->tris[k*4+0]; + mesh.tris[mesh.ntris*4+1] = dm->tris[k*4+1]; + mesh.tris[mesh.ntris*4+2] = dm->tris[k*4+2]; + mesh.tris[mesh.ntris*4+3] = dm->tris[k*4+3]; + mesh.ntris++; + } + } + + return true; +} diff --git a/external/recast/src/RecastRasterization.cpp b/external/recast/src/RecastRasterization.cpp new file mode 100644 index 0000000000..a4cef74909 --- /dev/null +++ b/external/recast/src/RecastRasterization.cpp @@ -0,0 +1,454 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#define _USE_MATH_DEFINES +#include +#include +#include "Recast.h" +#include "RecastAlloc.h" +#include "RecastAssert.h" + +inline bool overlapBounds(const float* amin, const float* amax, const float* bmin, const float* bmax) +{ + bool overlap = true; + overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap; + overlap = (amin[1] > bmax[1] || amax[1] < bmin[1]) ? false : overlap; + overlap = (amin[2] > bmax[2] || amax[2] < bmin[2]) ? false : overlap; + return overlap; +} + +inline bool overlapInterval(unsigned short amin, unsigned short amax, + unsigned short bmin, unsigned short bmax) +{ + if (amax < bmin) return false; + if (amin > bmax) return false; + return true; +} + + +static rcSpan* allocSpan(rcHeightfield& hf) +{ + // If running out of memory, allocate new page and update the freelist. + if (!hf.freelist || !hf.freelist->next) + { + // Create new page. + // Allocate memory for the new pool. + rcSpanPool* pool = (rcSpanPool*)rcAlloc(sizeof(rcSpanPool), RC_ALLOC_PERM); + if (!pool) return 0; + + // Add the pool into the list of pools. + pool->next = hf.pools; + hf.pools = pool; + // Add new items to the free list. + rcSpan* freelist = hf.freelist; + rcSpan* head = &pool->items[0]; + rcSpan* it = &pool->items[RC_SPANS_PER_POOL]; + do + { + --it; + it->next = freelist; + freelist = it; + } + while (it != head); + hf.freelist = it; + } + + // Pop item from in front of the free list. + rcSpan* it = hf.freelist; + hf.freelist = hf.freelist->next; + return it; +} + +static void freeSpan(rcHeightfield& hf, rcSpan* ptr) +{ + if (!ptr) return; + // Add the node in front of the free list. + ptr->next = hf.freelist; + hf.freelist = ptr; +} + +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) +{ + + int idx = x + y*hf.width; + + rcSpan* s = allocSpan(hf); + if (!s) + return false; + s->smin = smin; + s->smax = smax; + s->area = area; + s->next = 0; + + // Empty cell, add the first span. + if (!hf.spans[idx]) + { + hf.spans[idx] = s; + return true; + } + rcSpan* prev = 0; + rcSpan* cur = hf.spans[idx]; + + // Insert and merge spans. + while (cur) + { + if (cur->smin > s->smax) + { + // Current span is further than the new span, break. + break; + } + else if (cur->smax < s->smin) + { + // Current span is before the new span advance. + prev = cur; + cur = cur->next; + } + else + { + // Merge spans. + if (cur->smin < s->smin) + s->smin = cur->smin; + if (cur->smax > s->smax) + s->smax = cur->smax; + + // Merge flags. + if (rcAbs((int)s->smax - (int)cur->smax) <= flagMergeThr) + s->area = rcMax(s->area, cur->area); + + // Remove current span. + rcSpan* next = cur->next; + freeSpan(hf, cur); + if (prev) + prev->next = next; + else + hf.spans[idx] = next; + cur = next; + } + } + + // Insert new span. + if (prev) + { + s->next = prev->next; + prev->next = s; + } + else + { + s->next = hf.spans[idx]; + hf.spans[idx] = s; + } + + return true; +} + +/// @par +/// +/// The span addition can be set to favor flags. If the span is merged to +/// another span and the new @p smax is within @p flagMergeThr units +/// from the existing span, the span flags are merged. +/// +/// @see rcHeightfield, rcSpan. +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); + + 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 +static void dividePoly(const float* in, int nin, + float* out1, int* nout1, + float* out2, int* nout2, + float x, int axis) +{ + float d[12]; + for (int i = 0; i < nin; ++i) + d[i] = x - in[i*3+axis]; + + int m = 0, n = 0; + for (int i = 0, j = nin-1; i < nin; j=i, ++i) + { + bool ina = d[j] >= 0; + bool inb = d[i] >= 0; + if (ina != inb) + { + float s = d[j] / (d[j] - d[i]); + out1[m*3+0] = in[j*3+0] + (in[i*3+0] - in[j*3+0])*s; + out1[m*3+1] = in[j*3+1] + (in[i*3+1] - in[j*3+1])*s; + out1[m*3+2] = in[j*3+2] + (in[i*3+2] - in[j*3+2])*s; + rcVcopy(out2 + n*3, out1 + m*3); + m++; + n++; + // add the i'th point to the right polygon. Do NOT add points that are on the dividing line + // since these were already added above + if (d[i] > 0) + { + rcVcopy(out1 + m*3, in + i*3); + m++; + } + else if (d[i] < 0) + { + rcVcopy(out2 + n*3, in + i*3); + n++; + } + } + else // same side + { + // add the i'th point to the right polygon. Addition is done even for points on the dividing line + if (d[i] >= 0) + { + rcVcopy(out1 + m*3, in + i*3); + m++; + if (d[i] != 0) + continue; + } + rcVcopy(out2 + n*3, in + i*3); + n++; + } + } + + *nout1 = m; + *nout2 = n; +} + + + +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, + const int flagMergeThr) +{ + const int w = hf.width; + const int h = hf.height; + float tmin[3], tmax[3]; + const float by = bmax[1] - bmin[1]; + + // Calculate the bounding box of the triangle. + rcVcopy(tmin, v0); + rcVcopy(tmax, v0); + rcVmin(tmin, v1); + rcVmin(tmin, v2); + rcVmax(tmax, v1); + rcVmax(tmax, v2); + + // If the triangle does not touch the bbox of the heightfield, skip the triagle. + if (!overlapBounds(bmin, bmax, tmin, tmax)) + return true; + + // Calculate the footprint of the triangle on the grid's y-axis + int y0 = (int)((tmin[2] - bmin[2])*ics); + int y1 = (int)((tmax[2] - bmin[2])*ics); + y0 = rcClamp(y0, 0, h-1); + y1 = rcClamp(y1, 0, h-1); + + // Clip the triangle into all grid cells it touches. + float buf[7*3*4]; + float *in = buf, *inrow = buf+7*3, *p1 = inrow+7*3, *p2 = p1+7*3; + + rcVcopy(&in[0], v0); + rcVcopy(&in[1*3], v1); + rcVcopy(&in[2*3], v2); + int nvrow, nvIn = 3; + + for (int y = y0; y <= y1; ++y) + { + // Clip polygon to row. Store the remaining polygon as well + const float cz = bmin[2] + y*cs; + dividePoly(in, nvIn, inrow, &nvrow, p1, &nvIn, cz+cs, 2); + rcSwap(in, p1); + if (nvrow < 3) continue; + + // find the horizontal bounds in the row + float minX = inrow[0], maxX = inrow[0]; + for (int i=1; i inrow[i*3]) minX = inrow[i*3]; + if (maxX < inrow[i*3]) maxX = inrow[i*3]; + } + int x0 = (int)((minX - bmin[0])*ics); + int x1 = (int)((maxX - bmin[0])*ics); + x0 = rcClamp(x0, 0, w-1); + x1 = rcClamp(x1, 0, w-1); + + int nv, nv2 = nvrow; + + for (int x = x0; x <= x1; ++x) + { + // Clip polygon to column. store the remaining polygon as well + const float cx = bmin[0] + x*cs; + dividePoly(inrow, nv2, p1, &nv, p2, &nv2, cx+cs, 0); + rcSwap(inrow, p2); + if (nv < 3) continue; + + // Calculate min and max of the span. + float smin = p1[1], smax = p1[1]; + for (int i = 1; i < nv; ++i) + { + smin = rcMin(smin, p1[i*3+1]); + smax = rcMax(smax, p1[i*3+1]); + } + smin -= bmin[1]; + smax -= bmin[1]; + // Skip the span if it is outside the heightfield bbox + if (smax < 0.0f) continue; + if (smin > by) continue; + // Clamp the span to the heightfield bbox. + if (smin < 0.0f) smin = 0; + if (smax > by) smax = by; + + // Snap the span to the heightfield height grid. + unsigned short ismin = (unsigned short)rcClamp((int)floorf(smin * ich), 0, RC_SPAN_MAX_HEIGHT); + unsigned short ismax = (unsigned short)rcClamp((int)ceilf(smax * ich), (int)ismin+1, RC_SPAN_MAX_HEIGHT); + + if (!addSpan(hf, x, y, ismin, ismax, area, flagMergeThr)) + return false; + } + } + + return true; +} + +/// @par +/// +/// No spans will be added if the triangle does not overlap the heightfield grid. +/// +/// @see rcHeightfield +bool rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const float* v2, + const unsigned char area, rcHeightfield& solid, + const int flagMergeThr) +{ + rcAssert(ctx); + + rcScopedTimer timer(ctx, RC_TIMER_RASTERIZE_TRIANGLES); + + const float ics = 1.0f/solid.cs; + const float ich = 1.0f/solid.ch; + 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; + } + + return true; +} + +/// @par +/// +/// Spans will only be added for triangles that overlap the heightfield grid. +/// +/// @see rcHeightfield +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); + + rcScopedTimer timer(ctx, RC_TIMER_RASTERIZE_TRIANGLES); + + const float ics = 1.0f/solid.cs; + const float ich = 1.0f/solid.ch; + // Rasterize triangles. + for (int i = 0; i < nt; ++i) + { + const float* v0 = &verts[tris[i*3+0]*3]; + const float* v1 = &verts[tris[i*3+1]*3]; + const float* v2 = &verts[tris[i*3+2]*3]; + // Rasterize. + 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; + } + } + + return true; +} + +/// @par +/// +/// Spans will only be added for triangles that overlap the heightfield grid. +/// +/// @see rcHeightfield +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); + + rcScopedTimer timer(ctx, RC_TIMER_RASTERIZE_TRIANGLES); + + const float ics = 1.0f/solid.cs; + const float ich = 1.0f/solid.ch; + // Rasterize triangles. + for (int i = 0; i < nt; ++i) + { + const float* v0 = &verts[tris[i*3+0]*3]; + const float* v1 = &verts[tris[i*3+1]*3]; + const float* v2 = &verts[tris[i*3+2]*3]; + // Rasterize. + 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; + } + } + + return true; +} + +/// @par +/// +/// Spans will only be added for triangles that overlap the heightfield grid. +/// +/// @see rcHeightfield +bool rcRasterizeTriangles(rcContext* ctx, const float* verts, const unsigned char* areas, const int nt, + rcHeightfield& solid, const int flagMergeThr) +{ + rcAssert(ctx); + + rcScopedTimer timer(ctx, RC_TIMER_RASTERIZE_TRIANGLES); + + const float ics = 1.0f/solid.cs; + const float ich = 1.0f/solid.ch; + // Rasterize triangles. + for (int i = 0; i < nt; ++i) + { + const float* v0 = &verts[(i*3+0)*3]; + const float* v1 = &verts[(i*3+1)*3]; + const float* v2 = &verts[(i*3+2)*3]; + // Rasterize. + 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; + } + } + + return true; +} diff --git a/external/recast/src/RecastRegion.cpp b/external/recast/src/RecastRegion.cpp new file mode 100644 index 0000000000..e1fc0ee788 --- /dev/null +++ b/external/recast/src/RecastRegion.cpp @@ -0,0 +1,1812 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#include +#define _USE_MATH_DEFINES +#include +#include +#include +#include +#include "Recast.h" +#include "RecastAlloc.h" +#include "RecastAssert.h" + +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) +{ + const int w = chf.width; + const int h = chf.height; + + // Init distance and points. + for (int i = 0; i < chf.spanCount; ++i) + src[i] = 0xffff; + + // Mark boundary cells. + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + const rcCompactSpan& s = chf.spans[i]; + const unsigned char area = chf.areas[i]; + + int nc = 0; + for (int dir = 0; dir < 4; ++dir) + { + if (rcGetCon(s, dir) != RC_NOT_CONNECTED) + { + const int ax = x + rcGetDirOffsetX(dir); + const int ay = y + rcGetDirOffsetY(dir); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir); + if (area == chf.areas[ai]) + nc++; + } + } + if (nc != 4) + src[i] = 0; + } + } + } + + + // Pass 1 + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + const rcCompactSpan& s = chf.spans[i]; + + if (rcGetCon(s, 0) != RC_NOT_CONNECTED) + { + // (-1,0) + const int ax = x + rcGetDirOffsetX(0); + const int ay = y + rcGetDirOffsetY(0); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0); + const rcCompactSpan& as = chf.spans[ai]; + if (src[ai]+2 < src[i]) + src[i] = src[ai]+2; + + // (-1,-1) + if (rcGetCon(as, 3) != RC_NOT_CONNECTED) + { + const int aax = ax + rcGetDirOffsetX(3); + const int aay = ay + rcGetDirOffsetY(3); + const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 3); + if (src[aai]+3 < src[i]) + src[i] = src[aai]+3; + } + } + if (rcGetCon(s, 3) != RC_NOT_CONNECTED) + { + // (0,-1) + const int ax = x + rcGetDirOffsetX(3); + const int ay = y + rcGetDirOffsetY(3); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3); + const rcCompactSpan& as = chf.spans[ai]; + if (src[ai]+2 < src[i]) + src[i] = src[ai]+2; + + // (1,-1) + if (rcGetCon(as, 2) != RC_NOT_CONNECTED) + { + const int aax = ax + rcGetDirOffsetX(2); + const int aay = ay + rcGetDirOffsetY(2); + const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 2); + if (src[aai]+3 < src[i]) + src[i] = src[aai]+3; + } + } + } + } + } + + // Pass 2 + for (int y = h-1; y >= 0; --y) + { + for (int x = w-1; x >= 0; --x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + const rcCompactSpan& s = chf.spans[i]; + + if (rcGetCon(s, 2) != RC_NOT_CONNECTED) + { + // (1,0) + const int ax = x + rcGetDirOffsetX(2); + const int ay = y + rcGetDirOffsetY(2); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 2); + const rcCompactSpan& as = chf.spans[ai]; + if (src[ai]+2 < src[i]) + src[i] = src[ai]+2; + + // (1,1) + if (rcGetCon(as, 1) != RC_NOT_CONNECTED) + { + const int aax = ax + rcGetDirOffsetX(1); + const int aay = ay + rcGetDirOffsetY(1); + const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 1); + if (src[aai]+3 < src[i]) + src[i] = src[aai]+3; + } + } + if (rcGetCon(s, 1) != RC_NOT_CONNECTED) + { + // (0,1) + const int ax = x + rcGetDirOffsetX(1); + const int ay = y + rcGetDirOffsetY(1); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 1); + const rcCompactSpan& as = chf.spans[ai]; + if (src[ai]+2 < src[i]) + src[i] = src[ai]+2; + + // (-1,1) + if (rcGetCon(as, 0) != RC_NOT_CONNECTED) + { + const int aax = ax + rcGetDirOffsetX(0); + const int aay = ay + rcGetDirOffsetY(0); + const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 0); + if (src[aai]+3 < src[i]) + src[i] = src[aai]+3; + } + } + } + } + } + + maxDist = 0; + for (int i = 0; i < chf.spanCount; ++i) + maxDist = rcMax(src[i], maxDist); + +} + +static unsigned short* boxBlur(rcCompactHeightfield& chf, int thr, + unsigned short* src, unsigned short* dst) +{ + const int w = chf.width; + const int h = chf.height; + + thr *= 2; + + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + const rcCompactSpan& s = chf.spans[i]; + const unsigned short cd = src[i]; + if (cd <= thr) + { + dst[i] = cd; + continue; + } + + int d = (int)cd; + 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); + d += (int)src[ai]; + + const rcCompactSpan& as = chf.spans[ai]; + const int dir2 = (dir+1) & 0x3; + if (rcGetCon(as, dir2) != RC_NOT_CONNECTED) + { + const int ax2 = ax + rcGetDirOffsetX(dir2); + const int ay2 = ay + rcGetDirOffsetY(dir2); + const int ai2 = (int)chf.cells[ax2+ay2*w].index + rcGetCon(as, dir2); + d += (int)src[ai2]; + } + else + { + d += cd; + } + } + else + { + d += cd*2; + } + } + dst[i] = (unsigned short)((d+5)/9); + } + } + } + return dst; +} + + +static bool floodRegion(int x, int y, int i, + unsigned short level, unsigned short r, + rcCompactHeightfield& chf, + unsigned short* srcReg, unsigned short* srcDist, + rcTempVector& stack) +{ + const int w = chf.width; + + const unsigned char area = chf.areas[i]; + + // Flood fill mark region. + stack.clear(); + stack.push_back(LevelStackEntry(x, y, i)); + srcReg[i] = r; + srcDist[i] = 0; + + unsigned short lev = level >= 2 ? level-2 : 0; + int count = 0; + + while (stack.size() > 0) + { + 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]; + + // Check if any of the neighbours already have a valid region set. + unsigned short ar = 0; + for (int dir = 0; dir < 4; ++dir) + { + // 8 connected + if (rcGetCon(cs, dir) != RC_NOT_CONNECTED) + { + const int ax = cx + rcGetDirOffsetX(dir); + const int ay = cy + rcGetDirOffsetY(dir); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(cs, dir); + if (chf.areas[ai] != area) + continue; + unsigned short nr = srcReg[ai]; + if (nr & RC_BORDER_REG) // Do not take borders into account. + continue; + if (nr != 0 && nr != r) + { + ar = nr; + break; + } + + const rcCompactSpan& as = chf.spans[ai]; + + const int dir2 = (dir+1) & 0x3; + if (rcGetCon(as, dir2) != RC_NOT_CONNECTED) + { + const int ax2 = ax + rcGetDirOffsetX(dir2); + const int ay2 = ay + rcGetDirOffsetY(dir2); + const int ai2 = (int)chf.cells[ax2+ay2*w].index + rcGetCon(as, dir2); + if (chf.areas[ai2] != area) + continue; + unsigned short nr2 = srcReg[ai2]; + if (nr2 != 0 && nr2 != r) + { + ar = nr2; + break; + } + } + } + } + if (ar != 0) + { + srcReg[ci] = 0; + continue; + } + + count++; + + // Expand neighbours. + for (int dir = 0; dir < 4; ++dir) + { + if (rcGetCon(cs, dir) != RC_NOT_CONNECTED) + { + const int ax = cx + rcGetDirOffsetX(dir); + const int ay = cy + rcGetDirOffsetY(dir); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(cs, dir); + if (chf.areas[ai] != area) + continue; + if (chf.dist[ai] >= lev && srcReg[ai] == 0) + { + srcReg[ai] = r; + srcDist[ai] = 0; + stack.push_back(LevelStackEntry(ax, ay, ai)); + } + } + } + } + + return count > 0; +} + +// 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& stack, + bool fillStack) +{ + const int w = chf.width; + const int h = chf.height; + + if (fillStack) + { + // Find cells revealed by the raised level. + stack.clear(); + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + if (chf.dist[i] >= level && srcReg[i] == 0 && chf.areas[i] != RC_NULL_AREA) + { + stack.push_back(LevelStackEntry(x, y, i)); + } + } + } + } + } + else // use cells in the input stack + { + // mark all cells which already have a region + for (int j=0; j dirtyEntries; + int iter = 0; + while (stack.size() > 0) + { + int failed = 0; + dirtyEntries.clear(); + + for (int j = 0; j < stack.size(); j++) + { + int x = stack[j].x; + int y = stack[j].y; + int i = stack[j].index; + if (i < 0) + { + failed++; + continue; + } + + unsigned short r = srcReg[i]; + unsigned short d2 = 0xffff; + const unsigned char area = chf.areas[i]; + const rcCompactSpan& s = chf.spans[i]; + for (int dir = 0; dir < 4; ++dir) + { + if (rcGetCon(s, dir) == RC_NOT_CONNECTED) continue; + const int ax = x + rcGetDirOffsetX(dir); + const int ay = y + rcGetDirOffsetY(dir); + const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir); + if (chf.areas[ai] != area) continue; + if (srcReg[ai] > 0 && (srcReg[ai] & RC_BORDER_REG) == 0) + { + if ((int)srcDist[ai]+2 < (int)d2) + { + r = srcReg[ai]; + d2 = srcDist[ai]+2; + } + } + } + if (r) + { + stack[j].index = -1; // mark as used + dirtyEntries.push_back(DirtyEntry(i, r, d2)); + } + else + { + failed++; + } + } + + // 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 == stack.size()) + break; + + if (level > 0) + { + ++iter; + if (iter >= maxIter) + break; + } + } +} + + + +static void sortCellsByLevel(unsigned short startLevel, + rcCompactHeightfield& chf, + const unsigned short* srcReg, + unsigned int nbStacks, rcTempVector* stacks, + unsigned short loglevelsPerStack) // the levels per stack (2 in our case) as a bit shift +{ + const int w = chf.width; + const int h = chf.height; + startLevel = startLevel >> loglevelsPerStack; + + for (unsigned int j=0; j> loglevelsPerStack; + int sId = startLevel - level; + if (sId >= (int)nbStacks) + continue; + if (sId < 0) + sId = 0; + + stacks[sId].push_back(LevelStackEntry(x, y, i)); + } + } + } +} + + +static void appendStacks(const rcTempVector& srcStack, + rcTempVector& dstStack, + const unsigned short* srcReg) +{ + for (int j=0; j 1; ) + { + int ni = (i+1) % reg.connections.size(); + if (reg.connections[i] == reg.connections[ni]) + { + // Remove duplicate + for (int j = i; j < reg.connections.size()-1; ++j) + reg.connections[j] = reg.connections[j+1]; + reg.connections.pop(); + } + else + ++i; + } +} + +static void replaceNeighbour(rcRegion& reg, unsigned short oldId, unsigned short newId) +{ + bool neiChanged = false; + for (int i = 0; i < reg.connections.size(); ++i) + { + if (reg.connections[i] == oldId) + { + reg.connections[i] = newId; + neiChanged = true; + } + } + for (int i = 0; i < reg.floors.size(); ++i) + { + if (reg.floors[i] == oldId) + reg.floors[i] = newId; + } + if (neiChanged) + removeAdjacentNeighbours(reg); +} + +static bool canMergeWithRegion(const rcRegion& rega, const rcRegion& regb) +{ + if (rega.areaType != regb.areaType) + return false; + int n = 0; + for (int i = 0; i < rega.connections.size(); ++i) + { + if (rega.connections[i] == regb.id) + n++; + } + if (n > 1) + return false; + for (int i = 0; i < rega.floors.size(); ++i) + { + if (rega.floors[i] == regb.id) + return false; + } + return true; +} + +static void addUniqueFloorRegion(rcRegion& reg, int n) +{ + for (int i = 0; i < reg.floors.size(); ++i) + if (reg.floors[i] == n) + return; + reg.floors.push(n); +} + +static bool mergeRegions(rcRegion& rega, rcRegion& regb) +{ + unsigned short aid = rega.id; + unsigned short bid = regb.id; + + // Duplicate current neighbourhood. + rcIntArray acon; + acon.resize(rega.connections.size()); + for (int i = 0; i < rega.connections.size(); ++i) + acon[i] = rega.connections[i]; + rcIntArray& bcon = regb.connections; + + // Find insertion point on A. + int insa = -1; + for (int i = 0; i < acon.size(); ++i) + { + if (acon[i] == bid) + { + insa = i; + break; + } + } + if (insa == -1) + return false; + + // Find insertion point on B. + int insb = -1; + for (int i = 0; i < bcon.size(); ++i) + { + if (bcon[i] == aid) + { + insb = i; + break; + } + } + if (insb == -1) + return false; + + // Merge neighbours. + rega.connections.resize(0); + for (int i = 0, ni = acon.size(); i < ni-1; ++i) + rega.connections.push(acon[(insa+1+i) % ni]); + + for (int i = 0, ni = bcon.size(); i < ni-1; ++i) + rega.connections.push(bcon[(insb+1+i) % ni]); + + removeAdjacentNeighbours(rega); + + for (int j = 0; j < regb.floors.size(); ++j) + addUniqueFloorRegion(rega, regb.floors[j]); + rega.spanCount += regb.spanCount; + regb.spanCount = 0; + regb.connections.resize(0); + + return true; +} + +static bool isRegionConnectedToBorder(const rcRegion& reg) +{ + // Region is connected to border if + // one of the neighbours is null id. + for (int i = 0; i < reg.connections.size(); ++i) + { + if (reg.connections[i] == 0) + return true; + } + return false; +} + +static bool isSolidEdge(rcCompactHeightfield& chf, const unsigned short* srcReg, + int x, int y, int i, int dir) +{ + const rcCompactSpan& s = chf.spans[i]; + unsigned short r = 0; + if (rcGetCon(s, dir) != RC_NOT_CONNECTED) + { + const int ax = x + rcGetDirOffsetX(dir); + const int ay = y + rcGetDirOffsetY(dir); + const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir); + r = srcReg[ai]; + } + if (r == srcReg[i]) + return false; + return true; +} + +static void walkContour(int x, int y, int i, int dir, + rcCompactHeightfield& chf, + const unsigned short* srcReg, + rcIntArray& cont) +{ + int startDir = dir; + int starti = i; + + const rcCompactSpan& ss = chf.spans[i]; + unsigned short curReg = 0; + if (rcGetCon(ss, dir) != RC_NOT_CONNECTED) + { + const int ax = x + rcGetDirOffsetX(dir); + const int ay = y + rcGetDirOffsetY(dir); + const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(ss, dir); + curReg = srcReg[ai]; + } + cont.push(curReg); + + int iter = 0; + while (++iter < 40000) + { + const rcCompactSpan& s = chf.spans[i]; + + if (isSolidEdge(chf, srcReg, x, y, i, dir)) + { + // Choose the edge corner + unsigned short r = 0; + if (rcGetCon(s, dir) != RC_NOT_CONNECTED) + { + const int ax = x + rcGetDirOffsetX(dir); + const int ay = y + rcGetDirOffsetY(dir); + const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir); + r = srcReg[ai]; + } + if (r != curReg) + { + curReg = r; + cont.push(curReg); + } + + dir = (dir+1) & 0x3; // Rotate CW + } + else + { + int ni = -1; + const int nx = x + rcGetDirOffsetX(dir); + const int ny = y + rcGetDirOffsetY(dir); + if (rcGetCon(s, dir) != RC_NOT_CONNECTED) + { + const rcCompactCell& nc = chf.cells[nx+ny*chf.width]; + ni = (int)nc.index + rcGetCon(s, dir); + } + if (ni == -1) + { + // Should not happen. + return; + } + x = nx; + y = ny; + i = ni; + dir = (dir+3) & 0x3; // Rotate CCW + } + + if (starti == i && startDir == dir) + { + break; + } + } + + // Remove adjacent duplicates. + if (cont.size() > 1) + { + for (int j = 0; j < cont.size(); ) + { + int nj = (j+1) % cont.size(); + if (cont[j] == cont[nj]) + { + for (int k = j; k < cont.size()-1; ++k) + cont[k] = cont[k+1]; + cont.pop(); + } + else + ++j; + } + } +} + + +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; + rcTempVector 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) + 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) + { + for (int x = 0; x < w; ++x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + unsigned short r = srcReg[i]; + if (r == 0 || r >= nreg) + continue; + + rcRegion& reg = regions[r]; + reg.spanCount++; + + // Update floors. + for (int j = (int)c.index; j < ni; ++j) + { + if (i == j) continue; + unsigned short floorId = srcReg[j]; + if (floorId == 0 || floorId >= nreg) + continue; + if (floorId == r) + reg.overlap = true; + addUniqueFloorRegion(reg, floorId); + } + + // Have found contour + if (reg.connections.size() > 0) + continue; + + reg.areaType = chf.areas[i]; + + // Check if this cell is next to a border. + int ndir = -1; + for (int dir = 0; dir < 4; ++dir) + { + if (isSolidEdge(chf, srcReg, x, y, i, dir)) + { + ndir = dir; + break; + } + } + + if (ndir != -1) + { + // The cell is at border. + // Walk around the contour to find all the neighbours. + walkContour(x, y, i, ndir, chf, srcReg, reg.connections); + } + } + } + } + + // Remove too small regions. + rcIntArray stack(32); + rcIntArray trace(32); + for (int i = 0; i < nreg; ++i) + { + rcRegion& reg = regions[i]; + if (reg.id == 0 || (reg.id & RC_BORDER_REG)) + continue; + if (reg.spanCount == 0) + continue; + if (reg.visited) + continue; + + // Count the total size of all the connected regions. + // Also keep track of the regions connects to a tile border. + bool connectsToBorder = false; + int spanCount = 0; + stack.resize(0); + trace.resize(0); + + reg.visited = true; + stack.push(i); + + while (stack.size()) + { + // Pop + int ri = stack.pop(); + + rcRegion& creg = regions[ri]; + + spanCount += creg.spanCount; + trace.push(ri); + + for (int j = 0; j < creg.connections.size(); ++j) + { + if (creg.connections[j] & RC_BORDER_REG) + { + connectsToBorder = true; + continue; + } + rcRegion& neireg = regions[creg.connections[j]]; + if (neireg.visited) + continue; + if (neireg.id == 0 || (neireg.id & RC_BORDER_REG)) + continue; + // Visit + stack.push(neireg.id); + neireg.visited = true; + } + } + + // If the accumulated regions size is too small, remove it. + // Do not remove areas which connect to tile borders + // as their size cannot be estimated correctly and removing them + // can potentially remove necessary areas. + if (spanCount < minRegionArea && !connectsToBorder) + { + // Kill all visited regions. + for (int j = 0; j < trace.size(); ++j) + { + regions[trace[j]].spanCount = 0; + regions[trace[j]].id = 0; + } + } + } + + // Merge too small regions to neighbour regions. + int mergeCount = 0 ; + do + { + mergeCount = 0; + for (int i = 0; i < nreg; ++i) + { + rcRegion& reg = regions[i]; + if (reg.id == 0 || (reg.id & RC_BORDER_REG)) + continue; + if (reg.overlap) + continue; + if (reg.spanCount == 0) + continue; + + // Check to see if the region should be merged. + if (reg.spanCount > mergeRegionSize && isRegionConnectedToBorder(reg)) + continue; + + // Small region with more than 1 connection. + // Or region which is not connected to a border at all. + // Find smallest neighbour region that connects to this one. + int smallest = 0xfffffff; + unsigned short mergeId = reg.id; + for (int j = 0; j < reg.connections.size(); ++j) + { + if (reg.connections[j] & RC_BORDER_REG) continue; + rcRegion& mreg = regions[reg.connections[j]]; + if (mreg.id == 0 || (mreg.id & RC_BORDER_REG) || mreg.overlap) continue; + if (mreg.spanCount < smallest && + canMergeWithRegion(reg, mreg) && + canMergeWithRegion(mreg, reg)) + { + smallest = mreg.spanCount; + mergeId = mreg.id; + } + } + // Found new id. + if (mergeId != reg.id) + { + unsigned short oldId = reg.id; + rcRegion& target = regions[mergeId]; + + // Merge neighbours. + if (mergeRegions(target, reg)) + { + // Fixup regions pointing to current region. + for (int j = 0; j < nreg; ++j) + { + if (regions[j].id == 0 || (regions[j].id & RC_BORDER_REG)) continue; + // If another region was already merged into current region + // change the nid of the previous region too. + if (regions[j].id == oldId) + regions[j].id = mergeId; + // Replace the current region with the new one if the + // current regions is neighbour. + replaceNeighbour(regions[j], oldId, mergeId); + } + mergeCount++; + } + } + } + } + while (mergeCount > 0); + + // Compress region Ids. + for (int i = 0; i < nreg; ++i) + { + regions[i].remap = false; + if (regions[i].id == 0) continue; // Skip nil regions. + if (regions[i].id & RC_BORDER_REG) continue; // Skip external regions. + 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 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 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.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 +/// compact heightfield. This step is required before regions are built +/// using #rcBuildRegions or #rcBuildRegionsMonotone. +/// +/// After this step, the distance data is available via the rcCompactHeightfield::maxDistance +/// and rcCompactHeightfield::dist fields. +/// +/// @see rcCompactHeightfield, rcBuildRegions, rcBuildRegionsMonotone +bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf) +{ + rcAssert(ctx); + + rcScopedTimer timer(ctx, RC_TIMER_BUILD_DISTANCEFIELD); + + if (chf.dist) + { + rcFree(chf.dist); + chf.dist = 0; + } + + unsigned short* src = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP); + if (!src) + { + ctx->log(RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'src' (%d).", chf.spanCount); + return false; + } + unsigned short* dst = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP); + if (!dst) + { + ctx->log(RC_LOG_ERROR, "rcBuildDistanceField: Out of memory 'dst' (%d).", chf.spanCount); + rcFree(src); + return false; + } + + unsigned short maxDist = 0; + + { + 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); + + // Store distance. + chf.dist = src; + } + + rcFree(dst); + + return true; +} + +static void paintRectRegion(int minx, int maxx, int miny, int maxy, unsigned short regId, + rcCompactHeightfield& chf, unsigned short* srcReg) +{ + const int w = chf.width; + for (int y = miny; y < maxy; ++y) + { + for (int x = minx; x < maxx; ++x) + { + const rcCompactCell& c = chf.cells[x+y*w]; + for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i) + { + if (chf.areas[i] != RC_NULL_AREA) + srcReg[i] = regId; + } + } + } +} + + +static const unsigned short RC_NULL_NEI = 0xffff; + +struct rcSweepSpan +{ + unsigned short rid; // row id + unsigned short id; // region id + unsigned short ns; // number samples + unsigned short nei; // neighbour id +}; + +/// @par +/// +/// Non-null regions will consist of connected, non-overlapping walkable spans that form a single contour. +/// Contours will form simple polygons. +/// +/// If multiple regions form an area that is smaller than @p minRegionArea, then all spans will be +/// re-assigned to the zero (null) region. +/// +/// Partitioning can result in smaller than necessary regions. @p mergeRegionArea helps +/// reduce unecessarily small regions. +/// +/// See the #rcConfig documentation for more information on the configuration parameters. +/// +/// The region data will be available via the rcCompactHeightfield::maxRegions +/// and rcCompactSpan::reg fields. +/// +/// @warning The distance field must be created using #rcBuildDistanceField before attempting to build regions. +/// +/// @see rcCompactHeightfield, rcCompactSpan, rcBuildDistanceField, rcBuildRegionsMonotone, rcConfig +bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf, + const int borderSize, const int minRegionArea, const int mergeRegionArea) +{ + rcAssert(ctx); + + rcScopedTimer timer(ctx, RC_TIMER_BUILD_REGIONS); + + const int w = chf.width; + const int h = chf.height; + unsigned short id = 1; + + rcScopedDelete srcReg((unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP)); + if (!srcReg) + { + ctx->log(RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'src' (%d).", chf.spanCount); + return false; + } + memset(srcReg,0,sizeof(unsigned short)*chf.spanCount); + + const int nsweeps = rcMax(chf.width,chf.height); + rcScopedDelete sweeps((rcSweepSpan*)rcAlloc(sizeof(rcSweepSpan)*nsweeps, RC_ALLOC_TEMP)); + if (!sweeps) + { + ctx->log(RC_LOG_ERROR, "rcBuildRegionsMonotone: 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; + + 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 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]; + + return true; +} + +/// @par +/// +/// Non-null regions will consist of connected, non-overlapping walkable spans that form a single contour. +/// Contours will form simple polygons. +/// +/// If multiple regions form an area that is smaller than @p minRegionArea, then all spans will be +/// re-assigned to the zero (null) region. +/// +/// Watershed partitioning can result in smaller than necessary regions, especially in diagonal corridors. +/// @p mergeRegionArea helps reduce unecessarily small regions. +/// +/// See the #rcConfig documentation for more information on the configuration parameters. +/// +/// The region data will be available via the rcCompactHeightfield::maxRegions +/// and rcCompactSpan::reg fields. +/// +/// @warning The distance field must be created using #rcBuildDistanceField before attempting to build regions. +/// +/// @see rcCompactHeightfield, rcCompactSpan, rcBuildDistanceField, rcBuildRegionsMonotone, rcConfig +bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf, + const int borderSize, const int minRegionArea, const int mergeRegionArea) +{ + rcAssert(ctx); + + rcScopedTimer timer(ctx, RC_TIMER_BUILD_REGIONS); + + const int w = chf.width; + const int h = chf.height; + + rcScopedDelete buf((unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount*2, RC_ALLOC_TEMP)); + if (!buf) + { + ctx->log(RC_LOG_ERROR, "rcBuildRegions: Out of memory 'tmp' (%d).", chf.spanCount*4); + return false; + } + + ctx->startTimer(RC_TIMER_BUILD_REGIONS_WATERSHED); + + const int LOG_NB_STACKS = 3; + const int NB_STACKS = 1 << LOG_NB_STACKS; + rcTempVector lvlStacks[NB_STACKS]; + for (int i=0; i stack; + stack.reserve(256); + + unsigned short* srcReg = buf; + unsigned short* srcDist = buf+chf.spanCount; + + memset(srcReg, 0, sizeof(unsigned short)*chf.spanCount); + memset(srcDist, 0, sizeof(unsigned short)*chf.spanCount); + + unsigned short regionId = 1; + unsigned short level = (chf.maxDistance+1) & ~1; + + // TODO: Figure better formula, expandIters defines how much the + // watershed "overflows" and simplifies the regions. Tying it to + // agent radius was usually good indication how greedy it could be. +// const int expandIters = 4 + walkableRadius * 2; + const int expandIters = 8; + + if (borderSize > 0) + { + // Make sure border will not overflow. + const int bw = rcMin(w, borderSize); + const int bh = rcMin(h, borderSize); + + // Paint regions + paintRectRegion(0, bw, 0, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++; + paintRectRegion(w-bw, w, 0, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++; + paintRectRegion(0, w, 0, bh, regionId|RC_BORDER_REG, chf, srcReg); regionId++; + paintRectRegion(0, w, h-bh, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++; + } + + chf.borderSize = borderSize; + + int sId = -1; + while (level > 0) + { + level = level >= 2 ? level-2 : 0; + sId = (sId+1) & (NB_STACKS-1); + +// ctx->startTimer(RC_TIMER_DIVIDE_TO_LEVELS); + + if (sId == 0) + sortCellsByLevel(level, chf, srcReg, NB_STACKS, lvlStacks, 1); + else + appendStacks(lvlStacks[sId-1], lvlStacks[sId], srcReg); // copy left overs from last level + +// ctx->stopTimer(RC_TIMER_DIVIDE_TO_LEVELS); + + { + 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); + } + + { + rcScopedTimer timerFloor(ctx, RC_TIMER_BUILD_REGIONS_FLOOD); + + // Mark new regions with IDs. + for (int j = 0; j= 0 && srcReg[i] == 0) + { + if (floodRegion(x, y, i, level, regionId, chf, srcReg, srcDist, stack)) + { + if (regionId == 0xFFFF) + { + ctx->log(RC_LOG_ERROR, "rcBuildRegions: Region ID overflow"); + return false; + } + + regionId++; + } + } + } + } + } + + // Expand current regions until no empty connected cells found. + expandRegions(expandIters*8, 0, chf, srcReg, srcDist, stack, true); + + ctx->stopTimer(RC_TIMER_BUILD_REGIONS_WATERSHED); + + { + 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]; + + return true; +} + + +bool rcBuildLayerRegions(rcContext* ctx, rcCompactHeightfield& chf, + const int borderSize, const int minRegionArea) +{ + rcAssert(ctx); + + rcScopedTimer timer(ctx, RC_TIMER_BUILD_REGIONS); + + const int w = chf.width; + const int h = chf.height; + unsigned short id = 1; + + rcScopedDelete srcReg((unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP)); + 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); + + const int nsweeps = rcMax(chf.width,chf.height); + rcScopedDelete 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; + + 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]; + + return true; +} diff --git a/external/recast/src/detour_unity.cpp b/external/recast/src/detour_unity.cpp deleted file mode 100644 index f5ded97d8d..0000000000 --- a/external/recast/src/detour_unity.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "DetourCrowd.cpp" -#include "DetourLocalBoundary.cpp" -#include "DetourObstacleAvoidance.cpp" -#include "DetourPathCorridor.cpp" -#include "DetourPathQueue.cpp" -#include "DetourProximityGrid.cpp" diff --git a/external/recast/src/recast_unity.cpp b/external/recast/src/recast_unity.cpp new file mode 100644 index 0000000000..03a90ce481 --- /dev/null +++ b/external/recast/src/recast_unity.cpp @@ -0,0 +1,22 @@ +#include "DetourAlloc.cpp" +#include "DetourAssert.cpp" +#include "DetourCommon.cpp" +#include "DetourCrowd.cpp" +#include "DetourLocalBoundary.cpp" +#include "DetourNavMesh.cpp" +#include "DetourNavMeshBuilder.cpp" +#include "DetourNavMeshQuery.cpp" +#include "DetourNode.cpp" +#include "DetourObstacleAvoidance.cpp" +#include "DetourPathCorridor.cpp" +#include "DetourPathQueue.cpp" +#include "DetourProximityGrid.cpp" +#include "Recast.cpp" +#include "RecastAlloc.cpp" +#include "RecastArea.cpp" +#include "RecastAssert.cpp" +#include "RecastFilter.cpp" +#include "RecastLayers.cpp" +#include "RecastMesh.cpp" +#include "RecastRasterization.cpp" +#include "RecastRegion.cpp" \ No newline at end of file diff --git a/scripts/download_deploy_recast.bat b/scripts/download_deploy_recast.bat new file mode 100644 index 0000000000..023f020a95 --- /dev/null +++ b/scripts/download_deploy_recast.bat @@ -0,0 +1,34 @@ +@echo off + +REM we vendor recast in external/recast/src/* +REM download/pull latest recast +REM copy src and include files from recast repo to the project +REM call this to update recast in the project + +setlocal + set dir_3rdparty_src="..\external\_repos\" + set dst_src_dir="%~dp0..\external\recast\src" + set dst_inc_dir="%~dp0..\external\recast\include" + +pushd %~dp0 + if not exist %dir_3rdparty_src% mkdir %dir_3rdparty_src% + pushd %dir_3rdparty_src% + if not exist recast ( + git.exe clone --depth=1 https://github.com/nem0/recastnavigation.git recast + cd recast + ) else ( + cd recast + git pull + ) + + xcopy "Detour\Source\DetourProximityGrid.cpp" %dst_src_dir% /y + + xcopy "Detour\Source\*" %dst_src_dir% /y + xcopy "DetourCrowd\Source\*" %dst_src_dir% /y + xcopy "Recast\Source\*" %dst_src_dir% /y + + xcopy "Detour\Include\*" %dst_inc_dir% /y + xcopy "DetourCrowd\Include\*" %dst_inc_dir% /y + xcopy "Recast\Include\*" %dst_inc_dir% /y + popd +popd \ No newline at end of file diff --git a/scripts/download_freetype.bat b/scripts/download_freetype.bat new file mode 100644 index 0000000000..01ec6639e7 --- /dev/null +++ b/scripts/download_freetype.bat @@ -0,0 +1,20 @@ +@echo off + +REM This script downloads the FreeType2 source code from the official repository + +setlocal + +setlocal + set dir_3rdparty_src="..\external\_repos\" + +pushd %~dp0 + if not exist %dir_3rdparty_src% mkdir %dir_3rdparty_src% + pushd %dir_3rdparty_src% + if not exist freetype ( + git.exe clone --depth=1 https://github.com/nem0/freetype2.git freetype + ) else ( + cd freetype + git pull + ) + popd +popd \ No newline at end of file diff --git a/scripts/genie.lua b/scripts/genie.lua index 1384bb752d..2025f8fbfe 100644 --- a/scripts/genie.lua +++ b/scripts/genie.lua @@ -88,16 +88,6 @@ newoption { description = "Add Luau project to solution. Do not use the prebuilt library." } -newoption { - trigger = "force-build-freetype", - description = "Add FreeType project to solution. Do not use the prebuilt library." -} - -newoption { - trigger = "force-build-recast", - description = "Add Recast to solution. Do not use the prebuilt library." -} - newoption { trigger = "force-build-luau-dynamic", description = "Add Luau project to solution. Do not use the prebuilt library. Build luau as dynamic library" @@ -127,9 +117,7 @@ local embed_resources = _OPTIONS["embed-resources"] local working_dir = _OPTIONS["working-dir"] local debug_args = _OPTIONS["debug-args"] local release_args = _OPTIONS["release-args"] -local force_build_recast = _OPTIONS["force-build-recast"] local force_build_luau = _OPTIONS["force-build-luau"] -local force_build_freetype = _OPTIONS["force-build-freetype"] local force_build_luau_dynamic = _OPTIONS["force-build-luau-dynamic"] local force_build_physx = _OPTIONS["force-build-physx"] local use_basisu = _OPTIONS["with-basis-universal"] @@ -209,14 +197,6 @@ function linkEditor() end end -function linkOpenGL() - configuration { "windows" } - links { "opengl32" } - configuration { "not windows" } - links { "GL" } - configuration {} -end - function has_plugin(plugin) for _, v in ipairs(plugins) do if v == plugin then @@ -297,18 +277,15 @@ end function linkLib(lib) links {lib} - --if lib == "freetype" then - --libdirs { "external/freetype/lib/win" } - --else - for conf,conf_dir in pairs({Debug="release", RelWithDebInfo="release"}) do - for platform,target_platform in pairs({win="windows", linux="linux", }) do - configuration { "x64", conf, target_platform } - libdirs { path.join(ROOT_DIR, "./external/" .. lib .. "/lib/" .. platform .. "64" .. "_" .. binary_api_dir .. "/" .. conf_dir) } - libdirs { path.join(ROOT_DIR, "./external/" .. lib .. "/dll/" .. platform .. "64" .. "_" .. binary_api_dir .. "/" .. conf_dir) } - end + local use_prebuilt = not os.isdir("../external/_repos/" .. lib) + if use_prebuilt then + for platform,target_platform in pairs({win="windows", linux="linux", }) do + configuration { "x64", target_platform } + libdirs { path.join(ROOT_DIR, "./external/" .. lib .. "/lib/" .. platform) } + libdirs { path.join(ROOT_DIR, "./external/" .. lib .. "/dll/" .. platform) } end - --end - configuration {} + configuration {} + end end function useLua() @@ -595,7 +572,6 @@ if plugin "renderer" then end linkLib "freetype" - linkOpenGL() useLua() configuration { "linux" } @@ -632,27 +608,42 @@ if plugin "audio" then end if plugin "navigation" then - excludes { + files { + "../src/navigation/**.h", + "../src/navigation/**.cpp", + "../external/recast/src/**.cpp", + "../external/recast/include/**.h" + } + + -- use unity build for recast + excludes { + "../external/recast/src/DetourAlloc.cpp", + "../external/recast/src/DetourAssert.cpp", + "../external/recast/src/DetourCommon.cpp", "../external/recast/src/DetourCrowd.cpp", "../external/recast/src/DetourLocalBoundary.cpp", + "../external/recast/src/DetourNavMesh.cpp", + "../external/recast/src/DetourNavMeshBuilder.cpp", + "../external/recast/src/DetourNavMeshQuery.cpp", + "../external/recast/src/DetourNode.cpp", "../external/recast/src/DetourObstacleAvoidance.cpp", "../external/recast/src/DetourPathCorridor.cpp", "../external/recast/src/DetourPathQueue.cpp", "../external/recast/src/DetourProximityGrid.cpp", + "../external/recast/src/Recast.cpp", + "../external/recast/src/RecastAlloc.cpp", + "../external/recast/src/RecastArea.cpp", + "../external/recast/src/RecastAssert.cpp", + "../external/recast/src/RecastFilter.cpp", + "../external/recast/src/RecastLayers.cpp", + "../external/recast/src/RecastMesh.cpp", + "../external/recast/src/RecastRasterization.cpp", + "../external/recast/src/RecastRegion.cpp", + -- don't include following files in unity build because they have conflicting symbols with RecastMesh.cpp and each other + --"../external/recast/src/RecastContour.cpp", + --"../external/recast/src/RecastMeshDetail.cpp", } - if force_build_recast then - files { "3rdparty/recast/Recast/Source/**.cpp" - , "3rdparty/recast/Recast/Include/**.h" - ,"3rdparty/recast/Detour/Source/**.cpp" - , "3rdparty/recast/Detour/Include/**.h" - } - includedirs { "3rdparty/recast/Recast/Include" } - else - linkLib "recast" - end - - files { "../src/navigation/**.h", "../src/navigation/**.cpp", "../external/recast/src/**.cpp" } includedirs { "../src", "../src/navigation", "../external/recast/include" } links { "core", "engine", "renderer" } linkEditor() @@ -734,9 +725,6 @@ if build_app then end if not dynamic_plugins then - if has_plugin("renderer") then - linkOpenGL() - end if has_plugin("physics") then linkPhysX() end @@ -750,9 +738,6 @@ if build_app then linkLib "basisu" end linkLib "freetype" - if not force_build_recast then - linkLib "recast" - end configuration { "vs*" } links { "psapi", "dxguid", "winmm" } @@ -766,10 +751,6 @@ if build_app then linkLib "basisu" end - if not force_build_recast then - linkLib "recast" - end - configuration { "windows" } kind "WindowedApp" libdirs { path.join(ROOT_DIR, "./external/pix/bin/x64") } @@ -859,8 +840,6 @@ if build_studio then linkLib "freetype" useLua() if use_basisu then linkLib "basisu" end - if not force_build_recast then linkLib "recast" end - if has_plugin "renderer" then linkOpenGL() end if has_plugin "physics" then linkPhysX() end configuration { "linux" } @@ -916,69 +895,68 @@ if force_build_physx == true then end end -if force_build_freetype then - if os.isdir("3rdparty/freetype") then - project "freetype" - kind "StaticLib" - files { --"3rdparty/freetype/src/**.c", "3rdparty/freetype/include/**.h", - "3rdparty/freetype/src/autofit/autofit.c", - "3rdparty/freetype/src/base/ftbase.c", - "3rdparty/freetype/src/base/ftbbox.c", - "3rdparty/freetype/src/base/ftbdf.c", - "3rdparty/freetype/src/base/ftbitmap.c", - "3rdparty/freetype/src/base/ftcid.c", - "3rdparty/freetype/src/base/ftfstype.c", - "3rdparty/freetype/src/base/ftgasp.c", - "3rdparty/freetype/src/base/ftglyph.c", - "3rdparty/freetype/src/base/ftgxval.c", - "3rdparty/freetype/src/base/ftinit.c", - "3rdparty/freetype/src/base/ftmm.c", - "3rdparty/freetype/src/base/ftotval.c", - "3rdparty/freetype/src/base/ftpatent.c", - "3rdparty/freetype/src/base/ftpfr.c", - "3rdparty/freetype/src/base/ftstroke.c", - "3rdparty/freetype/src/base/ftsynth.c", - "3rdparty/freetype/src/base/ftsystem.c", - "3rdparty/freetype/src/base/fttype1.c", - "3rdparty/freetype/src/base/ftwinfnt.c", - "3rdparty/freetype/src/bdf/bdf.c", - "3rdparty/freetype/src/cache/ftcache.c", - "3rdparty/freetype/src/cff/cff.c", - "3rdparty/freetype/src/cid/type1cid.c", - "3rdparty/freetype/src/gzip/ftgzip.c", - "3rdparty/freetype/src/lzw/ftlzw.c", - "3rdparty/freetype/src/pcf/pcf.c", - "3rdparty/freetype/src/pfr/pfr.c", - "3rdparty/freetype/src/psaux/psaux.c", - "3rdparty/freetype/src/pshinter/pshinter.c", - "3rdparty/freetype/src/psnames/psmodule.c", - "3rdparty/freetype/src/raster/raster.c", - "3rdparty/freetype/src/sfnt/sfnt.c", - "3rdparty/freetype/src/smooth/smooth.c", - "3rdparty/freetype/src/truetype/truetype.c", - "3rdparty/freetype/src/type1/type1.c", - "3rdparty/freetype/src/type42/type42.c", - "3rdparty/freetype/src/winfonts/winfnt.c", - "3rdparty/freetype/builds/windows/ftdebug.c" +if os.isdir("../external/_repos/freetype") then + printf("Using FreeType from external/_repos/freetype (build from source code)") + project "freetype" + kind "StaticLib" + files { + "../external/_repos/freetype/src/autofit/autofit.c", + "../external/_repos/freetype/src/base/ftbase.c", + "../external/_repos/freetype/src/base/ftbbox.c", + "../external/_repos/freetype/src/base/ftbdf.c", + "../external/_repos/freetype/src/base/ftbitmap.c", + "../external/_repos/freetype/src/base/ftcid.c", + "../external/_repos/freetype/src/base/ftfstype.c", + "../external/_repos/freetype/src/base/ftgasp.c", + "../external/_repos/freetype/src/base/ftglyph.c", + "../external/_repos/freetype/src/base/ftgxval.c", + "../external/_repos/freetype/src/base/ftinit.c", + "../external/_repos/freetype/src/base/ftmm.c", + "../external/_repos/freetype/src/base/ftotval.c", + "../external/_repos/freetype/src/base/ftpatent.c", + "../external/_repos/freetype/src/base/ftpfr.c", + "../external/_repos/freetype/src/base/ftstroke.c", + "../external/_repos/freetype/src/base/ftsynth.c", + "../external/_repos/freetype/src/base/ftsystem.c", + "../external/_repos/freetype/src/base/fttype1.c", + "../external/_repos/freetype/src/base/ftwinfnt.c", + "../external/_repos/freetype/src/bdf/bdf.c", + "../external/_repos/freetype/src/cache/ftcache.c", + "../external/_repos/freetype/src/cff/cff.c", + "../external/_repos/freetype/src/cid/type1cid.c", + "../external/_repos/freetype/src/gzip/ftgzip.c", + "../external/_repos/freetype/src/lzw/ftlzw.c", + "../external/_repos/freetype/src/pcf/pcf.c", + "../external/_repos/freetype/src/pfr/pfr.c", + "../external/_repos/freetype/src/psaux/psaux.c", + "../external/_repos/freetype/src/pshinter/pshinter.c", + "../external/_repos/freetype/src/psnames/psmodule.c", + "../external/_repos/freetype/src/raster/raster.c", + "../external/_repos/freetype/src/sfnt/sfnt.c", + "../external/_repos/freetype/src/smooth/smooth.c", + "../external/_repos/freetype/src/truetype/truetype.c", + "../external/_repos/freetype/src/type1/type1.c", + "../external/_repos/freetype/src/type42/type42.c", + "../external/_repos/freetype/src/winfonts/winfnt.c", + "../external/_repos/freetype/builds/windows/ftdebug.c" } - includedirs { "3rdparty/freetype/include" } - defines { "NDEBUG", "FT2_BUILD_LIBRARY", "_CRT_SECURE_NO_WARNINGS" } - flags { "OptimizeSize", "ReleaseRuntime", "MinimumWarnings" } - targetname "freetype" - targetprefix "" - targetextension ".lib" - - configuration { "linux" } + includedirs { "../external/_repos/freetype/include" } + defines { "NDEBUG", "FT2_BUILD_LIBRARY", "_CRT_SECURE_NO_WARNINGS" } + flags { "ReleaseRuntime", "MinimumWarnings" } + targetname "freetype" + + -- TODO release/debug target dirs + configuration { "linux" } targetdir "../external/freetype/lib/linux" - - configuration { "windows" } - buildoptions { "/wd4312"} - targetdir "../external/freetype/lib/win" - else - printf("--force-build-freetype used but FreeType source code not found") - end -end + + configuration { "windows" } + buildoptions { "/wd4312"} + targetdir "../external/freetype/lib/win" + defaultConfigurations() +else + printf("Using FreeType from external/freetype (prebuilt)") +end if force_build_luau == true then if os.isdir("3rdparty/luau") then @@ -1026,7 +1004,7 @@ if force_build_luau == true then configuration { "windows" } targetdir "../external/luau/lib/win" - defines { + defines { "_CRT_SECURE_NO_WARNINGS", "LUA_API=__declspec(dllexport)", "LUACODE_API=__declspec(dllexport)" diff --git a/scripts/main.bat b/scripts/main.bat index 67e9679878..79939e0381 100644 --- a/scripts/main.bat +++ b/scripts/main.bat @@ -1,16 +1,22 @@ @echo off -REM detect paths -set msbuild_cmd=msbuild.exe -set devenv_cmd=devenv.exe -where /q devenv.exe -if not %errorlevel%==0 set devenv_cmd="C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\devenv.exe" -where /q msbuild.exe -if not %errorlevel%==0 set msbuild_cmd="C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\MSBuild.exe" +REM all path are relative to the script, so run in the script's directory +REM in case user runs the script from another directory +pushd %~dp0 + +setlocal + set dir_3rdparty_src="../external/_repos/" + REM detect paths + set msbuild_cmd=msbuild.exe + set devenv_cmd=devenv.exe + where /q devenv.exe + if not %errorlevel%==0 set devenv_cmd="C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\devenv.exe" + where /q msbuild.exe + if not %errorlevel%==0 set msbuild_cmd="C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\MSBuild.exe" :begin cls -echo %cd% + echo Wut? echo =============================== echo 1. Exit @@ -25,7 +31,10 @@ echo %cd% choice /C 12345678 /N /M "Your choice:" echo. - if %errorlevel%==1 goto :EOF + if %errorlevel%==1 ( + popd + goto :EOF + ) if %errorlevel%==2 call :create_project if %errorlevel%==3 call :run_studio if %errorlevel%==4 call :open_in_vs @@ -265,52 +274,27 @@ exit /B 0 popd exit /B 0 -:third_party +:third_party REM we should use specific 3rd party revision cls echo Wut2? echo =============================== echo 1. Go back - echo 2. Download, build and deploy all - echo 3. Recast navigation - echo 4. PhysX - echo 5. Download Luau - echo 6. FreeType2 - echo 7. Basis Universal + echo 2. PhysX + echo 3. Download Luau + echo 4. Basis Universal echo =============================== - choice /C 1234567 /N /M "Your choice:" + choice /C 1234 /N /M "Your choice:" echo. if %errorlevel%==1 exit /B 0 - if %errorlevel%==2 call :all_3rdparty - if %errorlevel%==3 call :recast - if %errorlevel%==4 call :physx - if %errorlevel%==5 call :download_luau - if %errorlevel%==6 call :freetype - if %errorlevel%==7 call :basisu + if %errorlevel%==2 call :physx + if %errorlevel%==3 call :download_luau + if %errorlevel%==4 call :basisu goto :third_party -:all_3rdparty - call :download_physx - call :download_recast - call :download_freetype - call :download_basisu - - call :build_physx - call :build_recast - call :build_freetype - call :build_basisu - - call :deploy_physx - call :deploy_recast - call :deploy_freetype - call :deploy_basisu - pause - -exit /B 0 - :download_luau - if not exist 3rdparty mkdir 3rdparty - cd 3rdparty + if not exist %dir_3rdparty_src% mkdir %dir_3rdparty_src% + pushd 3rdparty if not exist luau ( git.exe clone https://github.com/nem0/Luau.git luau ) else ( @@ -318,7 +302,7 @@ exit /B 0 git pull cd .. ) - cd .. + podp exit /B 0 :basisu @@ -327,7 +311,7 @@ exit /B 0 echo =============================== echo 1. Go back echo 2. Download - if exist "3rdparty\basisu\" ( + if exist "../external/_repos/basisu/" ( echo 3. Build echo 4. Deploy echo 5. Open in VS @@ -339,50 +323,13 @@ exit /B 0 if %errorlevel%==2 call :download_basisu if %errorlevel%==3 call :build_basisu if %errorlevel%==4 call :deploy_basisu - if %errorlevel%==5 start "" %devenv_cmd% "3rdparty\basisu\lumix\vs2022\basis_lumix.sln" + if %errorlevel%==5 "../external/_repos/basisu/lumix/vs2022/basis_lumix.sln" pause goto :basisu -:freetype - cls - echo FreeType2 - echo =============================== - echo 1. Go back - echo 2. Download - if exist "3rdparty\freetype\" ( - echo 3. Build - echo 4. Deploy - echo 5. Open in VS - ) - echo =============================== - choice /C 12345 /N /M "Your choice:" - echo. - if %errorlevel%==1 exit /B 0 - if %errorlevel%==2 call :download_freetype - if %errorlevel%==3 call :build_freetype - if %errorlevel%==4 call :deploy_freetype - if %errorlevel%==5 start "" %devenv_cmd% "3rdparty\freetype\builds\windows\vc2010\freetype.sln" - pause -goto :freetype - -:build_freetype - %msbuild_cmd% 3rdparty\freetype\builds\windows\vc2010\freetype.sln /p:Configuration="Release Static" /p:Platform=x64 -exit /B 0 - :build_basisu - pushd 3rdparty\basisu\lumix\ - ..\..\..\genie.exe vs2022 - popd - %msbuild_cmd% 3rdparty\basisu\lumix\vs2022\basis_lumix.sln /p:Configuration="Release" /p:Platform=x64 -exit /B 0 - -:deploy_freetype - echo %CD% - del /Q ..\external\freetype\lib\win64_vs2017\release\* - copy "3rdparty\freetype\objs\x64\Release Static\freetype.lib" ..\external\freetype\lib\win64_vs2017\release\ - copy "3rdparty\freetype\objs\x64\Release Static\freetype.pdb" ..\external\freetype\lib\win64_vs2017\release\ - del /Q ..\external\freetype\include\* - xcopy /E /Y "3rdparty\freetype\include\*" ..\external\freetype\include\ + .\genie.exe --file=../external/_repos/basisu/lumix/genie.lua vs2022 + %msbuild_cmd% ..\external\_repos\basisu\lumix\vs2022\basis_lumix.sln /p:Configuration="Release" /p:Platform=x64 exit /B 0 :deploy_basisu @@ -475,45 +422,6 @@ exit /B 0 exit /B 0 -:recast - cls - echo Recast ^& Detour - echo =============================== - echo 1. Go back - echo 2. Download - if exist "3rdparty\recast\" ( - echo 3. Build - echo 4. Deploy - echo 5. Open in VS - ) - echo =============================== - choice /C 12345 /N /M "Your choice:" - echo. - if %errorlevel%==1 exit /B 0 - if %errorlevel%==2 call :download_recast - if %errorlevel%==3 call :build_recast - if %errorlevel%==4 call :deploy_recast - if %errorlevel%==5 start "" %devenv_cmd% "3rdparty\recast\_project\RecastDetour.sln" - pause -goto :recast - -:deploy_recast - del /Q ..\external\recast\include\* - del /Q ..\external\recast\src\* - copy 3rdparty\recast\Recast\Include\* ..\external\recast\include\ - copy 3rdparty\recast\Detour\Include\* ..\external\recast\include\ - copy 3rdparty\recast\DetourCrowd\Include\* ..\external\recast\include\ - copy 3rdparty\recast\DetourCrowd\Source\* ..\external\recast\src\ - copy 3rdparty\recast\DebugUtils\Include\* ..\external\recast\include\ - copy 3rdparty\recast\_build\Recast.lib ..\external\recast\lib\win64_vs2017\release\recast.lib - copy 3rdparty\recast\_build\Recast.pdb ..\external\recast\lib\win64_vs2017\release\recast.pdb -exit /B 0 - -:build_recast - .\genie.exe --file=recastnavigation.lua vs2022 - %msbuild_cmd% 3rdparty\recast\_project\RecastDetour.sln /p:Configuration=Release /p:Platform=x64 -exit /B 0 - :create_project echo Creating project... .\genie.exe --with-app vs2022 @@ -551,56 +459,40 @@ exit /B 0 pause exit /B 0 -:download_freetype - if not exist 3rdparty mkdir 3rdparty - cd 3rdparty - if not exist freetype ( - git.exe clone --depth=1 https://github.com/nem0/freetype2.git freetype - ) else ( - cd freetype - git pull - cd .. - ) - cd .. -exit /B 0 - :download_basisu - if not exist 3rdparty mkdir 3rdparty - cd 3rdparty + if not exist %dir_3rdparty_src% mkdir %dir_3rdparty_src% + pushd %dir_3rdparty_src% if not exist basisu ( git.exe clone --depth=1 https://github.com/nem0/basis_universal.git basisu ) else ( cd basisu git pull - cd .. ) - cd .. + popd exit /B 0 :download_recast - if not exist 3rdparty mkdir 3rdparty - cd 3rdparty + if not exist %dir_3rdparty_src% mkdir %dir_3rdparty_src% + pushd %dir_3rdparty_src% if not exist recast ( git.exe clone --depth=1 https://github.com/nem0/recastnavigation.git recast ) else ( cd recast git pull - cd .. ) - cd .. + popd exit /B 0 :download_physx - if not exist 3rdparty mkdir 3rdparty - cd 3rdparty + if not exist %dir_3rdparty_src% mkdir %dir_3rdparty_src% + pushd %dir_3rdparty_src% if not exist physx ( git.exe clone --depth=1 https://github.com/nem0/PhysX.git physx ) else ( cd physx git pull - cd .. ) - cd .. + popd exit /B 0 :open_discord diff --git a/scripts/main.sh b/scripts/main.sh index f970cd0af2..f6eb1319dc 100644 --- a/scripts/main.sh +++ b/scripts/main.sh @@ -31,8 +31,6 @@ download_plugin() cd ../scripts } - - build_recast() { ./genie --file=recastnavigation.lua gmake diff --git a/scripts/recastnavigation.lua b/scripts/recastnavigation.lua deleted file mode 100644 index 2419fbc647..0000000000 --- a/scripts/recastnavigation.lua +++ /dev/null @@ -1,29 +0,0 @@ -solution "RecastDetour" - location "3rdparty/recast/_project/" - targetdir "3rdparty/recast/_build/" - language "C++" - configurations { "Release", "Debug" } - platforms { "x64" } - flags { - "FatalWarnings", - "NoPCH", - "NoExceptions", - "NoRTTI", - "NoEditAndContinue", - "ReleaseRuntime", - "Symbols" - } - -project "Recast" - kind "StaticLib" - files { - "3rdparty/recast/Recast/Source/**.cpp", - "3rdparty/recast/Detour/Source/**.cpp" - } - includedirs { - "3rdparty/recast/Recast/Include/", - "3rdparty/recast/Detour/Include/" - } - - configuration { "Release" } - flags { "Optimize" } \ No newline at end of file