Move recastnavigation to FetchContent
parent
5c0214142b
commit
1c9245bd58
@ -1,12 +0,0 @@
|
|||||||
# editorconfig.org
|
|
||||||
|
|
||||||
# top-most EditorConfig file
|
|
||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
indent_size = 4
|
|
||||||
indent_style = tab
|
|
||||||
|
|
||||||
[*.yml]
|
|
||||||
indent_size = 2
|
|
||||||
indent_style = space
|
|
@ -1,49 +0,0 @@
|
|||||||
## Compiled source #
|
|
||||||
*.com
|
|
||||||
*.class
|
|
||||||
*.dll
|
|
||||||
*.exe
|
|
||||||
*.ilk
|
|
||||||
*.o
|
|
||||||
*.pdb
|
|
||||||
*.so
|
|
||||||
*.idb
|
|
||||||
|
|
||||||
## Linux exes have no extension
|
|
||||||
RecastDemo/Bin/RecastDemo
|
|
||||||
RecastDemo/Bin/Tests
|
|
||||||
|
|
||||||
# Build directory
|
|
||||||
RecastDemo/Build
|
|
||||||
|
|
||||||
# Ignore meshes
|
|
||||||
RecastDemo/Bin/Meshes/*
|
|
||||||
|
|
||||||
## Logs and databases #
|
|
||||||
*.log
|
|
||||||
*.sql
|
|
||||||
*.sqlite
|
|
||||||
|
|
||||||
## OS generated files #
|
|
||||||
.DS_Store
|
|
||||||
.DS_Store?
|
|
||||||
._*
|
|
||||||
.Spotlight-V100
|
|
||||||
.Trashes
|
|
||||||
ehthumbs.db
|
|
||||||
Thumbs.db
|
|
||||||
*.swp
|
|
||||||
*.swo
|
|
||||||
|
|
||||||
## xcode specific
|
|
||||||
*xcuserdata*
|
|
||||||
|
|
||||||
## SDL contrib
|
|
||||||
RecastDemo/Contrib/SDL/*
|
|
||||||
|
|
||||||
## Generated doc files
|
|
||||||
Docs/html
|
|
||||||
|
|
||||||
## IDE files
|
|
||||||
.idea/
|
|
||||||
cmake-build-*/
|
|
@ -1 +0,0 @@
|
|||||||
6624e7aef5e15df11cb2f5673574df8e4c96af6a
|
|
@ -1 +0,0 @@
|
|||||||
https://github.com/recastnavigation/recastnavigation.git
|
|
@ -1,26 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
|
||||||
|
|
||||||
# for link time optimization, remove if cmake version is >= 3.9
|
|
||||||
if(POLICY CMP0069)
|
|
||||||
cmake_policy(SET CMP0069 NEW)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
project(RecastNavigation)
|
|
||||||
|
|
||||||
# lib versions
|
|
||||||
SET(SOVERSION 1)
|
|
||||||
SET(VERSION 1.0.0)
|
|
||||||
|
|
||||||
option(RECASTNAVIGATION_STATIC "Build static libraries" ON)
|
|
||||||
|
|
||||||
if(MSVC AND BUILD_SHARED_LIBS)
|
|
||||||
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
include(GNUInstallDirs)
|
|
||||||
|
|
||||||
add_subdirectory(DebugUtils)
|
|
||||||
add_subdirectory(Detour)
|
|
||||||
add_subdirectory(DetourCrowd)
|
|
||||||
add_subdirectory(DetourTileCache)
|
|
||||||
add_subdirectory(Recast)
|
|
@ -1,36 +0,0 @@
|
|||||||
file(GLOB SOURCES Source/*.cpp)
|
|
||||||
add_library(DebugUtils ${SOURCES})
|
|
||||||
|
|
||||||
add_library(RecastNavigation::DebugUtils ALIAS DebugUtils)
|
|
||||||
set_target_properties(DebugUtils PROPERTIES DEBUG_POSTFIX -d)
|
|
||||||
|
|
||||||
set(DebugUtils_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Include")
|
|
||||||
|
|
||||||
target_include_directories(DebugUtils PUBLIC
|
|
||||||
"$<BUILD_INTERFACE:${DebugUtils_INCLUDE_DIR}>"
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(DebugUtils
|
|
||||||
Recast
|
|
||||||
Detour
|
|
||||||
DetourTileCache
|
|
||||||
)
|
|
||||||
|
|
||||||
set_target_properties(DebugUtils PROPERTIES
|
|
||||||
SOVERSION ${SOVERSION}
|
|
||||||
VERSION ${VERSION}
|
|
||||||
COMPILE_PDB_OUTPUT_DIRECTORY .
|
|
||||||
COMPILE_PDB_NAME "DebugUtils-d"
|
|
||||||
)
|
|
||||||
|
|
||||||
install(TARGETS DebugUtils
|
|
||||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
|
||||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
||||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
||||||
COMPONENT library
|
|
||||||
)
|
|
||||||
|
|
||||||
file(GLOB INCLUDES Include/*.h)
|
|
||||||
install(FILES ${INCLUDES} DESTINATION
|
|
||||||
${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation)
|
|
||||||
install(FILES "$<TARGET_FILE_DIR:DebugUtils>/DebugUtils-d.pdb" CONFIGURATIONS "Debug" DESTINATION "lib")
|
|
@ -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();
|
|
||||||
void depthMask(bool state) override;
|
|
||||||
void begin(duDebugDrawPrimitives prim, float size = 1.0f) override;
|
|
||||||
void vertex(const float x, const float y, const float z, unsigned int color) override;
|
|
||||||
void vertex(const float* pos, unsigned int color) override;
|
|
||||||
void end() override;
|
|
||||||
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
|
|
@ -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
|
|
@ -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
|
|
@ -1,43 +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_DUMP_H
|
|
||||||
#define RECAST_DUMP_H
|
|
||||||
|
|
||||||
struct duFileIO
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
bool duDumpCompactHeightfield(struct rcCompactHeightfield& chf, duFileIO* io);
|
|
||||||
bool duReadCompactHeightfield(struct rcCompactHeightfield& chf, duFileIO* io);
|
|
||||||
|
|
||||||
void duLogBuildTimes(rcContext& ctx, const int totalTileUsec);
|
|
||||||
|
|
||||||
|
|
||||||
#endif // RECAST_DUMP_H
|
|
@ -1,612 +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.
|
|
||||||
//
|
|
||||||
|
|
||||||
#define _USE_MATH_DEFINES
|
|
||||||
#include <string.h>
|
|
||||||
#include "DebugDraw.h"
|
|
||||||
#include "DetourMath.h"
|
|
||||||
#include "DetourNavMesh.h"
|
|
||||||
|
|
||||||
|
|
||||||
duDebugDraw::~duDebugDraw()
|
|
||||||
{
|
|
||||||
// Empty
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int duDebugDraw::areaToCol(unsigned int area)
|
|
||||||
{
|
|
||||||
if (area == 0)
|
|
||||||
{
|
|
||||||
// Treat zero area type as default.
|
|
||||||
return duRGBA(0, 192, 255, 255);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return duIntToCol(area, 255);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int bit(int a, int b)
|
|
||||||
{
|
|
||||||
return (a & (1 << b)) >> b;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int duIntToCol(int i, int a)
|
|
||||||
{
|
|
||||||
int r = bit(i, 1) + bit(i, 3) * 2 + 1;
|
|
||||||
int g = bit(i, 2) + bit(i, 4) * 2 + 1;
|
|
||||||
int b = bit(i, 0) + bit(i, 5) * 2 + 1;
|
|
||||||
return duRGBA(r*63,g*63,b*63,a);
|
|
||||||
}
|
|
||||||
|
|
||||||
void duIntToCol(int i, float* col)
|
|
||||||
{
|
|
||||||
int r = bit(i, 0) + bit(i, 3) * 2 + 1;
|
|
||||||
int g = bit(i, 1) + bit(i, 4) * 2 + 1;
|
|
||||||
int b = bit(i, 2) + bit(i, 5) * 2 + 1;
|
|
||||||
col[0] = 1 - r*63.0f/255.0f;
|
|
||||||
col[1] = 1 - g*63.0f/255.0f;
|
|
||||||
col[2] = 1 - b*63.0f/255.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
void duCalcBoxColors(unsigned int* colors, unsigned int colTop, unsigned int colSide)
|
|
||||||
{
|
|
||||||
if (!colors) return;
|
|
||||||
|
|
||||||
colors[0] = duMultCol(colTop, 250);
|
|
||||||
colors[1] = duMultCol(colSide, 140);
|
|
||||||
colors[2] = duMultCol(colSide, 165);
|
|
||||||
colors[3] = duMultCol(colSide, 217);
|
|
||||||
colors[4] = duMultCol(colSide, 165);
|
|
||||||
colors[5] = duMultCol(colSide, 217);
|
|
||||||
}
|
|
||||||
|
|
||||||
void duDebugDrawCylinderWire(struct duDebugDraw* dd, float minx, float miny, float minz,
|
|
||||||
float maxx, float maxy, float maxz, unsigned int col, const float lineWidth)
|
|
||||||
{
|
|
||||||
if (!dd) return;
|
|
||||||
|
|
||||||
dd->begin(DU_DRAW_LINES, lineWidth);
|
|
||||||
duAppendCylinderWire(dd, minx,miny,minz, maxx,maxy,maxz, col);
|
|
||||||
dd->end();
|
|
||||||
}
|
|
||||||
|
|
||||||
void duDebugDrawBoxWire(struct duDebugDraw* dd, float minx, float miny, float minz,
|
|
||||||
float maxx, float maxy, float maxz, unsigned int col, const float lineWidth)
|
|
||||||
{
|
|
||||||
if (!dd) return;
|
|
||||||
|
|
||||||
dd->begin(DU_DRAW_LINES, lineWidth);
|
|
||||||
duAppendBoxWire(dd, minx,miny,minz, maxx,maxy,maxz, col);
|
|
||||||
dd->end();
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
if (!dd) return;
|
|
||||||
|
|
||||||
dd->begin(DU_DRAW_LINES, lineWidth);
|
|
||||||
duAppendArc(dd, x0,y0,z0, x1,y1,z1, h, as0, as1, col);
|
|
||||||
dd->end();
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
if (!dd) return;
|
|
||||||
|
|
||||||
dd->begin(DU_DRAW_LINES, lineWidth);
|
|
||||||
duAppendArrow(dd, x0,y0,z0, x1,y1,z1, as0, as1, col);
|
|
||||||
dd->end();
|
|
||||||
}
|
|
||||||
|
|
||||||
void duDebugDrawCircle(struct duDebugDraw* dd, const float x, const float y, const float z,
|
|
||||||
const float r, unsigned int col, const float lineWidth)
|
|
||||||
{
|
|
||||||
if (!dd) return;
|
|
||||||
|
|
||||||
dd->begin(DU_DRAW_LINES, lineWidth);
|
|
||||||
duAppendCircle(dd, x,y,z, r, col);
|
|
||||||
dd->end();
|
|
||||||
}
|
|
||||||
|
|
||||||
void duDebugDrawCross(struct duDebugDraw* dd, const float x, const float y, const float z,
|
|
||||||
const float size, unsigned int col, const float lineWidth)
|
|
||||||
{
|
|
||||||
if (!dd) return;
|
|
||||||
|
|
||||||
dd->begin(DU_DRAW_LINES, lineWidth);
|
|
||||||
duAppendCross(dd, x,y,z, size, col);
|
|
||||||
dd->end();
|
|
||||||
}
|
|
||||||
|
|
||||||
void duDebugDrawBox(struct duDebugDraw* dd, float minx, float miny, float minz,
|
|
||||||
float maxx, float maxy, float maxz, const unsigned int* fcol)
|
|
||||||
{
|
|
||||||
if (!dd) return;
|
|
||||||
|
|
||||||
dd->begin(DU_DRAW_QUADS);
|
|
||||||
duAppendBox(dd, minx,miny,minz, maxx,maxy,maxz, fcol);
|
|
||||||
dd->end();
|
|
||||||
}
|
|
||||||
|
|
||||||
void duDebugDrawCylinder(struct duDebugDraw* dd, float minx, float miny, float minz,
|
|
||||||
float maxx, float maxy, float maxz, unsigned int col)
|
|
||||||
{
|
|
||||||
if (!dd) return;
|
|
||||||
|
|
||||||
dd->begin(DU_DRAW_TRIS);
|
|
||||||
duAppendCylinder(dd, minx,miny,minz, maxx,maxy,maxz, col);
|
|
||||||
dd->end();
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
if (!dd) return;
|
|
||||||
|
|
||||||
dd->begin(DU_DRAW_LINES, lineWidth);
|
|
||||||
for (int i = 0; i <= h; ++i)
|
|
||||||
{
|
|
||||||
dd->vertex(ox,oy,oz+i*size, col);
|
|
||||||
dd->vertex(ox+w*size,oy,oz+i*size, col);
|
|
||||||
}
|
|
||||||
for (int i = 0; i <= w; ++i)
|
|
||||||
{
|
|
||||||
dd->vertex(ox+i*size,oy,oz, col);
|
|
||||||
dd->vertex(ox+i*size,oy,oz+h*size, col);
|
|
||||||
}
|
|
||||||
dd->end();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void duAppendCylinderWire(struct duDebugDraw* dd, float minx, float miny, float minz,
|
|
||||||
float maxx, float maxy, float maxz, unsigned int col)
|
|
||||||
{
|
|
||||||
if (!dd) return;
|
|
||||||
|
|
||||||
static const int NUM_SEG = 16;
|
|
||||||
static float dir[NUM_SEG*2];
|
|
||||||
static bool init = false;
|
|
||||||
if (!init)
|
|
||||||
{
|
|
||||||
init = true;
|
|
||||||
for (int i = 0; i < NUM_SEG; ++i)
|
|
||||||
{
|
|
||||||
const float a = (float)i/(float)NUM_SEG*DU_PI*2;
|
|
||||||
dir[i*2] = dtMathCosf(a);
|
|
||||||
dir[i*2+1] = dtMathSinf(a);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const float cx = (maxx + minx)/2;
|
|
||||||
const float cz = (maxz + minz)/2;
|
|
||||||
const float rx = (maxx - minx)/2;
|
|
||||||
const float rz = (maxz - minz)/2;
|
|
||||||
|
|
||||||
for (int i = 0, j = NUM_SEG-1; i < NUM_SEG; j = i++)
|
|
||||||
{
|
|
||||||
dd->vertex(cx+dir[j*2+0]*rx, miny, cz+dir[j*2+1]*rz, col);
|
|
||||||
dd->vertex(cx+dir[i*2+0]*rx, miny, cz+dir[i*2+1]*rz, col);
|
|
||||||
dd->vertex(cx+dir[j*2+0]*rx, maxy, cz+dir[j*2+1]*rz, col);
|
|
||||||
dd->vertex(cx+dir[i*2+0]*rx, maxy, cz+dir[i*2+1]*rz, col);
|
|
||||||
}
|
|
||||||
for (int i = 0; i < NUM_SEG; i += NUM_SEG/4)
|
|
||||||
{
|
|
||||||
dd->vertex(cx+dir[i*2+0]*rx, miny, cz+dir[i*2+1]*rz, col);
|
|
||||||
dd->vertex(cx+dir[i*2+0]*rx, maxy, cz+dir[i*2+1]*rz, col);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void duAppendBoxWire(struct duDebugDraw* dd, float minx, float miny, float minz,
|
|
||||||
float maxx, float maxy, float maxz, unsigned int col)
|
|
||||||
{
|
|
||||||
if (!dd) return;
|
|
||||||
// Top
|
|
||||||
dd->vertex(minx, miny, minz, col);
|
|
||||||
dd->vertex(maxx, miny, minz, col);
|
|
||||||
dd->vertex(maxx, miny, minz, col);
|
|
||||||
dd->vertex(maxx, miny, maxz, col);
|
|
||||||
dd->vertex(maxx, miny, maxz, col);
|
|
||||||
dd->vertex(minx, miny, maxz, col);
|
|
||||||
dd->vertex(minx, miny, maxz, col);
|
|
||||||
dd->vertex(minx, miny, minz, col);
|
|
||||||
|
|
||||||
// bottom
|
|
||||||
dd->vertex(minx, maxy, minz, col);
|
|
||||||
dd->vertex(maxx, maxy, minz, col);
|
|
||||||
dd->vertex(maxx, maxy, minz, col);
|
|
||||||
dd->vertex(maxx, maxy, maxz, col);
|
|
||||||
dd->vertex(maxx, maxy, maxz, col);
|
|
||||||
dd->vertex(minx, maxy, maxz, col);
|
|
||||||
dd->vertex(minx, maxy, maxz, col);
|
|
||||||
dd->vertex(minx, maxy, minz, col);
|
|
||||||
|
|
||||||
// Sides
|
|
||||||
dd->vertex(minx, miny, minz, col);
|
|
||||||
dd->vertex(minx, maxy, minz, col);
|
|
||||||
dd->vertex(maxx, miny, minz, col);
|
|
||||||
dd->vertex(maxx, maxy, minz, col);
|
|
||||||
dd->vertex(maxx, miny, maxz, col);
|
|
||||||
dd->vertex(maxx, maxy, maxz, col);
|
|
||||||
dd->vertex(minx, miny, maxz, col);
|
|
||||||
dd->vertex(minx, maxy, maxz, col);
|
|
||||||
}
|
|
||||||
|
|
||||||
void duAppendBoxPoints(struct duDebugDraw* dd, float minx, float miny, float minz,
|
|
||||||
float maxx, float maxy, float maxz, unsigned int col)
|
|
||||||
{
|
|
||||||
if (!dd) return;
|
|
||||||
// Top
|
|
||||||
dd->vertex(minx, miny, minz, col);
|
|
||||||
dd->vertex(maxx, miny, minz, col);
|
|
||||||
dd->vertex(maxx, miny, minz, col);
|
|
||||||
dd->vertex(maxx, miny, maxz, col);
|
|
||||||
dd->vertex(maxx, miny, maxz, col);
|
|
||||||
dd->vertex(minx, miny, maxz, col);
|
|
||||||
dd->vertex(minx, miny, maxz, col);
|
|
||||||
dd->vertex(minx, miny, minz, col);
|
|
||||||
|
|
||||||
// bottom
|
|
||||||
dd->vertex(minx, maxy, minz, col);
|
|
||||||
dd->vertex(maxx, maxy, minz, col);
|
|
||||||
dd->vertex(maxx, maxy, minz, col);
|
|
||||||
dd->vertex(maxx, maxy, maxz, col);
|
|
||||||
dd->vertex(maxx, maxy, maxz, col);
|
|
||||||
dd->vertex(minx, maxy, maxz, col);
|
|
||||||
dd->vertex(minx, maxy, maxz, col);
|
|
||||||
dd->vertex(minx, maxy, minz, col);
|
|
||||||
}
|
|
||||||
|
|
||||||
void duAppendBox(struct duDebugDraw* dd, float minx, float miny, float minz,
|
|
||||||
float maxx, float maxy, float maxz, const unsigned int* fcol)
|
|
||||||
{
|
|
||||||
if (!dd) return;
|
|
||||||
const float verts[8*3] =
|
|
||||||
{
|
|
||||||
minx, miny, minz,
|
|
||||||
maxx, miny, minz,
|
|
||||||
maxx, miny, maxz,
|
|
||||||
minx, miny, maxz,
|
|
||||||
minx, maxy, minz,
|
|
||||||
maxx, maxy, minz,
|
|
||||||
maxx, maxy, maxz,
|
|
||||||
minx, maxy, maxz,
|
|
||||||
};
|
|
||||||
static const unsigned char inds[6*4] =
|
|
||||||
{
|
|
||||||
7, 6, 5, 4,
|
|
||||||
0, 1, 2, 3,
|
|
||||||
1, 5, 6, 2,
|
|
||||||
3, 7, 4, 0,
|
|
||||||
2, 6, 7, 3,
|
|
||||||
0, 4, 5, 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
const unsigned char* in = inds;
|
|
||||||
for (int i = 0; i < 6; ++i)
|
|
||||||
{
|
|
||||||
dd->vertex(&verts[*in*3], fcol[i]); in++;
|
|
||||||
dd->vertex(&verts[*in*3], fcol[i]); in++;
|
|
||||||
dd->vertex(&verts[*in*3], fcol[i]); in++;
|
|
||||||
dd->vertex(&verts[*in*3], fcol[i]); in++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void duAppendCylinder(struct duDebugDraw* dd, float minx, float miny, float minz,
|
|
||||||
float maxx, float maxy, float maxz, unsigned int col)
|
|
||||||
{
|
|
||||||
if (!dd) return;
|
|
||||||
|
|
||||||
static const int NUM_SEG = 16;
|
|
||||||
static float dir[NUM_SEG*2];
|
|
||||||
static bool init = false;
|
|
||||||
if (!init)
|
|
||||||
{
|
|
||||||
init = true;
|
|
||||||
for (int i = 0; i < NUM_SEG; ++i)
|
|
||||||
{
|
|
||||||
const float a = (float)i/(float)NUM_SEG*DU_PI*2;
|
|
||||||
dir[i*2] = cosf(a);
|
|
||||||
dir[i*2+1] = sinf(a);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int col2 = duMultCol(col, 160);
|
|
||||||
|
|
||||||
const float cx = (maxx + minx)/2;
|
|
||||||
const float cz = (maxz + minz)/2;
|
|
||||||
const float rx = (maxx - minx)/2;
|
|
||||||
const float rz = (maxz - minz)/2;
|
|
||||||
|
|
||||||
for (int i = 2; i < NUM_SEG; ++i)
|
|
||||||
{
|
|
||||||
const int a = 0, b = i-1, c = i;
|
|
||||||
dd->vertex(cx+dir[a*2+0]*rx, miny, cz+dir[a*2+1]*rz, col2);
|
|
||||||
dd->vertex(cx+dir[b*2+0]*rx, miny, cz+dir[b*2+1]*rz, col2);
|
|
||||||
dd->vertex(cx+dir[c*2+0]*rx, miny, cz+dir[c*2+1]*rz, col2);
|
|
||||||
}
|
|
||||||
for (int i = 2; i < NUM_SEG; ++i)
|
|
||||||
{
|
|
||||||
const int a = 0, b = i, c = i-1;
|
|
||||||
dd->vertex(cx+dir[a*2+0]*rx, maxy, cz+dir[a*2+1]*rz, col);
|
|
||||||
dd->vertex(cx+dir[b*2+0]*rx, maxy, cz+dir[b*2+1]*rz, col);
|
|
||||||
dd->vertex(cx+dir[c*2+0]*rx, maxy, cz+dir[c*2+1]*rz, col);
|
|
||||||
}
|
|
||||||
for (int i = 0, j = NUM_SEG-1; i < NUM_SEG; j = i++)
|
|
||||||
{
|
|
||||||
dd->vertex(cx+dir[i*2+0]*rx, miny, cz+dir[i*2+1]*rz, col2);
|
|
||||||
dd->vertex(cx+dir[j*2+0]*rx, miny, cz+dir[j*2+1]*rz, col2);
|
|
||||||
dd->vertex(cx+dir[j*2+0]*rx, maxy, cz+dir[j*2+1]*rz, col);
|
|
||||||
|
|
||||||
dd->vertex(cx+dir[i*2+0]*rx, miny, cz+dir[i*2+1]*rz, col2);
|
|
||||||
dd->vertex(cx+dir[j*2+0]*rx, maxy, cz+dir[j*2+1]*rz, col);
|
|
||||||
dd->vertex(cx+dir[i*2+0]*rx, maxy, cz+dir[i*2+1]*rz, col);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inline void evalArc(const float x0, const float y0, const float z0,
|
|
||||||
const float dx, const float dy, const float dz,
|
|
||||||
const float h, const float u, float* res)
|
|
||||||
{
|
|
||||||
res[0] = x0 + dx * u;
|
|
||||||
res[1] = y0 + dy * u + h * (1-(u*2-1)*(u*2-1));
|
|
||||||
res[2] = z0 + dz * u;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inline void vcross(float* dest, const float* v1, const float* v2)
|
|
||||||
{
|
|
||||||
dest[0] = v1[1]*v2[2] - v1[2]*v2[1];
|
|
||||||
dest[1] = v1[2]*v2[0] - v1[0]*v2[2];
|
|
||||||
dest[2] = v1[0]*v2[1] - v1[1]*v2[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void vnormalize(float* v)
|
|
||||||
{
|
|
||||||
float d = 1.0f / sqrtf(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
|
|
||||||
v[0] *= d;
|
|
||||||
v[1] *= d;
|
|
||||||
v[2] *= d;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void vsub(float* dest, const float* v1, const float* v2)
|
|
||||||
{
|
|
||||||
dest[0] = v1[0]-v2[0];
|
|
||||||
dest[1] = v1[1]-v2[1];
|
|
||||||
dest[2] = v1[2]-v2[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline float vdistSqr(const float* v1, const float* v2)
|
|
||||||
{
|
|
||||||
const float x = v1[0]-v2[0];
|
|
||||||
const float y = v1[1]-v2[1];
|
|
||||||
const float z = v1[2]-v2[2];
|
|
||||||
return x*x + y*y + z*z;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void appendArrowHead(struct duDebugDraw* dd, const float* p, const float* q,
|
|
||||||
const float s, unsigned int col)
|
|
||||||
{
|
|
||||||
const float eps = 0.001f;
|
|
||||||
if (!dd) return;
|
|
||||||
if (vdistSqr(p,q) < eps*eps) return;
|
|
||||||
float ax[3], ay[3] = {0,1,0}, az[3];
|
|
||||||
vsub(az, q, p);
|
|
||||||
vnormalize(az);
|
|
||||||
vcross(ax, ay, az);
|
|
||||||
vcross(ay, az, ax);
|
|
||||||
vnormalize(ay);
|
|
||||||
|
|
||||||
dd->vertex(p, col);
|
|
||||||
// dd->vertex(p[0]+az[0]*s+ay[0]*s/2, p[1]+az[1]*s+ay[1]*s/2, p[2]+az[2]*s+ay[2]*s/2, col);
|
|
||||||
dd->vertex(p[0]+az[0]*s+ax[0]*s/3, p[1]+az[1]*s+ax[1]*s/3, p[2]+az[2]*s+ax[2]*s/3, col);
|
|
||||||
|
|
||||||
dd->vertex(p, col);
|
|
||||||
// dd->vertex(p[0]+az[0]*s-ay[0]*s/2, p[1]+az[1]*s-ay[1]*s/2, p[2]+az[2]*s-ay[2]*s/2, col);
|
|
||||||
dd->vertex(p[0]+az[0]*s-ax[0]*s/3, p[1]+az[1]*s-ax[1]*s/3, p[2]+az[2]*s-ax[2]*s/3, 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)
|
|
||||||
{
|
|
||||||
if (!dd) return;
|
|
||||||
static const int NUM_ARC_PTS = 8;
|
|
||||||
static const float PAD = 0.05f;
|
|
||||||
static const float ARC_PTS_SCALE = (1.0f-PAD*2) / (float)NUM_ARC_PTS;
|
|
||||||
const float dx = x1 - x0;
|
|
||||||
const float dy = y1 - y0;
|
|
||||||
const float dz = z1 - z0;
|
|
||||||
const float len = sqrtf(dx*dx + dy*dy + dz*dz);
|
|
||||||
float prev[3];
|
|
||||||
evalArc(x0,y0,z0, dx,dy,dz, len*h, PAD, prev);
|
|
||||||
for (int i = 1; i <= NUM_ARC_PTS; ++i)
|
|
||||||
{
|
|
||||||
const float u = PAD + i * ARC_PTS_SCALE;
|
|
||||||
float pt[3];
|
|
||||||
evalArc(x0,y0,z0, dx,dy,dz, len*h, u, pt);
|
|
||||||
dd->vertex(prev[0],prev[1],prev[2], col);
|
|
||||||
dd->vertex(pt[0],pt[1],pt[2], col);
|
|
||||||
prev[0] = pt[0]; prev[1] = pt[1]; prev[2] = pt[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
// End arrows
|
|
||||||
if (as0 > 0.001f)
|
|
||||||
{
|
|
||||||
float p[3], q[3];
|
|
||||||
evalArc(x0,y0,z0, dx,dy,dz, len*h, PAD, p);
|
|
||||||
evalArc(x0,y0,z0, dx,dy,dz, len*h, PAD+0.05f, q);
|
|
||||||
appendArrowHead(dd, p, q, as0, col);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (as1 > 0.001f)
|
|
||||||
{
|
|
||||||
float p[3], q[3];
|
|
||||||
evalArc(x0,y0,z0, dx,dy,dz, len*h, 1-PAD, p);
|
|
||||||
evalArc(x0,y0,z0, dx,dy,dz, len*h, 1-(PAD+0.05f), q);
|
|
||||||
appendArrowHead(dd, p, q, as1, 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)
|
|
||||||
{
|
|
||||||
if (!dd) return;
|
|
||||||
|
|
||||||
dd->vertex(x0,y0,z0, col);
|
|
||||||
dd->vertex(x1,y1,z1, col);
|
|
||||||
|
|
||||||
// End arrows
|
|
||||||
const float p[3] = {x0,y0,z0}, q[3] = {x1,y1,z1};
|
|
||||||
if (as0 > 0.001f)
|
|
||||||
appendArrowHead(dd, p, q, as0, col);
|
|
||||||
if (as1 > 0.001f)
|
|
||||||
appendArrowHead(dd, q, p, as1, col);
|
|
||||||
}
|
|
||||||
|
|
||||||
void duAppendCircle(struct duDebugDraw* dd, const float x, const float y, const float z,
|
|
||||||
const float r, unsigned int col)
|
|
||||||
{
|
|
||||||
if (!dd) return;
|
|
||||||
static const int NUM_SEG = 40;
|
|
||||||
static float dir[40*2];
|
|
||||||
static bool init = false;
|
|
||||||
if (!init)
|
|
||||||
{
|
|
||||||
init = true;
|
|
||||||
for (int i = 0; i < NUM_SEG; ++i)
|
|
||||||
{
|
|
||||||
const float a = (float)i/(float)NUM_SEG*DU_PI*2;
|
|
||||||
dir[i*2] = cosf(a);
|
|
||||||
dir[i*2+1] = sinf(a);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0, j = NUM_SEG-1; i < NUM_SEG; j = i++)
|
|
||||||
{
|
|
||||||
dd->vertex(x+dir[j*2+0]*r, y, z+dir[j*2+1]*r, col);
|
|
||||||
dd->vertex(x+dir[i*2+0]*r, y, z+dir[i*2+1]*r, col);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void duAppendCross(struct duDebugDraw* dd, const float x, const float y, const float z,
|
|
||||||
const float s, unsigned int col)
|
|
||||||
{
|
|
||||||
if (!dd) return;
|
|
||||||
dd->vertex(x-s,y,z, col);
|
|
||||||
dd->vertex(x+s,y,z, col);
|
|
||||||
dd->vertex(x,y-s,z, col);
|
|
||||||
dd->vertex(x,y+s,z, col);
|
|
||||||
dd->vertex(x,y,z-s, col);
|
|
||||||
dd->vertex(x,y,z+s, col);
|
|
||||||
}
|
|
||||||
|
|
||||||
duDisplayList::duDisplayList(int cap) :
|
|
||||||
m_pos(0),
|
|
||||||
m_color(0),
|
|
||||||
m_size(0),
|
|
||||||
m_cap(0),
|
|
||||||
m_depthMask(true),
|
|
||||||
m_prim(DU_DRAW_LINES),
|
|
||||||
m_primSize(1.0f)
|
|
||||||
{
|
|
||||||
if (cap < 8)
|
|
||||||
cap = 8;
|
|
||||||
resize(cap);
|
|
||||||
}
|
|
||||||
|
|
||||||
duDisplayList::~duDisplayList()
|
|
||||||
{
|
|
||||||
delete [] m_pos;
|
|
||||||
delete [] m_color;
|
|
||||||
}
|
|
||||||
|
|
||||||
void duDisplayList::resize(int cap)
|
|
||||||
{
|
|
||||||
float* newPos = new float[cap*3];
|
|
||||||
if (m_size)
|
|
||||||
memcpy(newPos, m_pos, sizeof(float)*3*m_size);
|
|
||||||
delete [] m_pos;
|
|
||||||
m_pos = newPos;
|
|
||||||
|
|
||||||
unsigned int* newColor = new unsigned int[cap];
|
|
||||||
if (m_size)
|
|
||||||
memcpy(newColor, m_color, sizeof(unsigned int)*m_size);
|
|
||||||
delete [] m_color;
|
|
||||||
m_color = newColor;
|
|
||||||
|
|
||||||
m_cap = cap;
|
|
||||||
}
|
|
||||||
|
|
||||||
void duDisplayList::clear()
|
|
||||||
{
|
|
||||||
m_size = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void duDisplayList::depthMask(bool state)
|
|
||||||
{
|
|
||||||
m_depthMask = state;
|
|
||||||
}
|
|
||||||
|
|
||||||
void duDisplayList::begin(duDebugDrawPrimitives prim, float size)
|
|
||||||
{
|
|
||||||
clear();
|
|
||||||
m_prim = prim;
|
|
||||||
m_primSize = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
void duDisplayList::vertex(const float x, const float y, const float z, unsigned int color)
|
|
||||||
{
|
|
||||||
if (m_size+1 >= m_cap)
|
|
||||||
resize(m_cap*2);
|
|
||||||
float* p = &m_pos[m_size*3];
|
|
||||||
p[0] = x;
|
|
||||||
p[1] = y;
|
|
||||||
p[2] = z;
|
|
||||||
m_color[m_size] = color;
|
|
||||||
m_size++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void duDisplayList::vertex(const float* pos, unsigned int color)
|
|
||||||
{
|
|
||||||
vertex(pos[0],pos[1],pos[2],color);
|
|
||||||
}
|
|
||||||
|
|
||||||
void duDisplayList::end()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void duDisplayList::draw(struct duDebugDraw* dd)
|
|
||||||
{
|
|
||||||
if (!dd) return;
|
|
||||||
if (!m_size) return;
|
|
||||||
dd->depthMask(m_depthMask);
|
|
||||||
dd->begin(m_prim, m_primSize);
|
|
||||||
for (int i = 0; i < m_size; ++i)
|
|
||||||
dd->vertex(&m_pos[i*3], m_color[i]);
|
|
||||||
dd->end();
|
|
||||||
}
|
|
@ -1,864 +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.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "DebugDraw.h"
|
|
||||||
#include "DetourDebugDraw.h"
|
|
||||||
#include "DetourNavMesh.h"
|
|
||||||
#include "DetourCommon.h"
|
|
||||||
#include "DetourNode.h"
|
|
||||||
|
|
||||||
|
|
||||||
static float distancePtLine2d(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;
|
|
||||||
dx = p[0] + t*pqx - pt[0];
|
|
||||||
dz = p[2] + t*pqz - pt[2];
|
|
||||||
return dx*dx + dz*dz;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void drawPolyBoundaries(duDebugDraw* dd, const dtMeshTile* tile,
|
|
||||||
const unsigned int col, const float linew,
|
|
||||||
bool inner)
|
|
||||||
{
|
|
||||||
static const float thr = 0.01f*0.01f;
|
|
||||||
|
|
||||||
dd->begin(DU_DRAW_LINES, linew);
|
|
||||||
|
|
||||||
for (int i = 0; i < tile->header->polyCount; ++i)
|
|
||||||
{
|
|
||||||
const dtPoly* p = &tile->polys[i];
|
|
||||||
|
|
||||||
if (p->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) continue;
|
|
||||||
|
|
||||||
const dtPolyDetail* pd = &tile->detailMeshes[i];
|
|
||||||
|
|
||||||
for (int j = 0, nj = (int)p->vertCount; j < nj; ++j)
|
|
||||||
{
|
|
||||||
unsigned int c = col;
|
|
||||||
if (inner)
|
|
||||||
{
|
|
||||||
if (p->neis[j] == 0) continue;
|
|
||||||
if (p->neis[j] & DT_EXT_LINK)
|
|
||||||
{
|
|
||||||
bool con = false;
|
|
||||||
for (unsigned int k = p->firstLink; k != DT_NULL_LINK; k = tile->links[k].next)
|
|
||||||
{
|
|
||||||
if (tile->links[k].edge == j)
|
|
||||||
{
|
|
||||||
con = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (con)
|
|
||||||
c = duRGBA(255,255,255,48);
|
|
||||||
else
|
|
||||||
c = duRGBA(0,0,0,48);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
c = duRGBA(0,48,64,32);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (p->neis[j] != 0) continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const float* v0 = &tile->verts[p->verts[j]*3];
|
|
||||||
const float* v1 = &tile->verts[p->verts[(j+1) % nj]*3];
|
|
||||||
|
|
||||||
// Draw detail mesh edges which align with the actual poly edge.
|
|
||||||
// This is really slow.
|
|
||||||
for (int k = 0; k < pd->triCount; ++k)
|
|
||||||
{
|
|
||||||
const unsigned char* t = &tile->detailTris[(pd->triBase+k)*4];
|
|
||||||
const float* tv[3];
|
|
||||||
for (int m = 0; m < 3; ++m)
|
|
||||||
{
|
|
||||||
if (t[m] < p->vertCount)
|
|
||||||
tv[m] = &tile->verts[p->verts[t[m]]*3];
|
|
||||||
else
|
|
||||||
tv[m] = &tile->detailVerts[(pd->vertBase+(t[m]-p->vertCount))*3];
|
|
||||||
}
|
|
||||||
for (int m = 0, n = 2; m < 3; n=m++)
|
|
||||||
{
|
|
||||||
if ((dtGetDetailTriEdgeFlags(t[3], n) & DT_DETAIL_EDGE_BOUNDARY) == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (distancePtLine2d(tv[n],v0,v1) < thr &&
|
|
||||||
distancePtLine2d(tv[m],v0,v1) < thr)
|
|
||||||
{
|
|
||||||
dd->vertex(tv[n], c);
|
|
||||||
dd->vertex(tv[m], c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dd->end();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void drawMeshTile(duDebugDraw* dd, const dtNavMesh& mesh, const dtNavMeshQuery* query,
|
|
||||||
const dtMeshTile* tile, unsigned char flags)
|
|
||||||
{
|
|
||||||
dtPolyRef base = mesh.getPolyRefBase(tile);
|
|
||||||
|
|
||||||
int tileNum = mesh.decodePolyIdTile(base);
|
|
||||||
const unsigned int tileColor = duIntToCol(tileNum, 128);
|
|
||||||
|
|
||||||
dd->depthMask(false);
|
|
||||||
|
|
||||||
dd->begin(DU_DRAW_TRIS);
|
|
||||||
for (int i = 0; i < tile->header->polyCount; ++i)
|
|
||||||
{
|
|
||||||
const dtPoly* p = &tile->polys[i];
|
|
||||||
if (p->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) // Skip off-mesh links.
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const dtPolyDetail* pd = &tile->detailMeshes[i];
|
|
||||||
|
|
||||||
unsigned int col;
|
|
||||||
if (query && query->isInClosedList(base | (dtPolyRef)i))
|
|
||||||
col = duRGBA(255,196,0,64);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (flags & DU_DRAWNAVMESH_COLOR_TILES)
|
|
||||||
col = tileColor;
|
|
||||||
else
|
|
||||||
col = duTransCol(dd->areaToCol(p->getArea()), 64);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int j = 0; j < pd->triCount; ++j)
|
|
||||||
{
|
|
||||||
const unsigned char* t = &tile->detailTris[(pd->triBase+j)*4];
|
|
||||||
for (int k = 0; k < 3; ++k)
|
|
||||||
{
|
|
||||||
if (t[k] < p->vertCount)
|
|
||||||
dd->vertex(&tile->verts[p->verts[t[k]]*3], col);
|
|
||||||
else
|
|
||||||
dd->vertex(&tile->detailVerts[(pd->vertBase+t[k]-p->vertCount)*3], col);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dd->end();
|
|
||||||
|
|
||||||
// Draw inter poly boundaries
|
|
||||||
drawPolyBoundaries(dd, tile, duRGBA(0,48,64,32), 1.5f, true);
|
|
||||||
|
|
||||||
// Draw outer poly boundaries
|
|
||||||
drawPolyBoundaries(dd, tile, duRGBA(0,48,64,220), 2.5f, false);
|
|
||||||
|
|
||||||
if (flags & DU_DRAWNAVMESH_OFFMESHCONS)
|
|
||||||
{
|
|
||||||
dd->begin(DU_DRAW_LINES, 2.0f);
|
|
||||||
for (int i = 0; i < tile->header->polyCount; ++i)
|
|
||||||
{
|
|
||||||
const dtPoly* p = &tile->polys[i];
|
|
||||||
if (p->getType() != DT_POLYTYPE_OFFMESH_CONNECTION) // Skip regular polys.
|
|
||||||
continue;
|
|
||||||
|
|
||||||
unsigned int col, col2;
|
|
||||||
if (query && query->isInClosedList(base | (dtPolyRef)i))
|
|
||||||
col = duRGBA(255,196,0,220);
|
|
||||||
else
|
|
||||||
col = duDarkenCol(duTransCol(dd->areaToCol(p->getArea()), 220));
|
|
||||||
|
|
||||||
const dtOffMeshConnection* con = &tile->offMeshCons[i - tile->header->offMeshBase];
|
|
||||||
const float* va = &tile->verts[p->verts[0]*3];
|
|
||||||
const float* vb = &tile->verts[p->verts[1]*3];
|
|
||||||
|
|
||||||
// Check to see if start and end end-points have links.
|
|
||||||
bool startSet = false;
|
|
||||||
bool endSet = false;
|
|
||||||
for (unsigned int k = p->firstLink; k != DT_NULL_LINK; k = tile->links[k].next)
|
|
||||||
{
|
|
||||||
if (tile->links[k].edge == 0)
|
|
||||||
startSet = true;
|
|
||||||
if (tile->links[k].edge == 1)
|
|
||||||
endSet = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// End points and their on-mesh locations.
|
|
||||||
dd->vertex(va[0],va[1],va[2], col);
|
|
||||||
dd->vertex(con->pos[0],con->pos[1],con->pos[2], col);
|
|
||||||
col2 = startSet ? col : duRGBA(220,32,16,196);
|
|
||||||
duAppendCircle(dd, con->pos[0],con->pos[1]+0.1f,con->pos[2], con->rad, col2);
|
|
||||||
|
|
||||||
dd->vertex(vb[0],vb[1],vb[2], col);
|
|
||||||
dd->vertex(con->pos[3],con->pos[4],con->pos[5], col);
|
|
||||||
col2 = endSet ? col : duRGBA(220,32,16,196);
|
|
||||||
duAppendCircle(dd, con->pos[3],con->pos[4]+0.1f,con->pos[5], con->rad, col2);
|
|
||||||
|
|
||||||
// End point vertices.
|
|
||||||
dd->vertex(con->pos[0],con->pos[1],con->pos[2], duRGBA(0,48,64,196));
|
|
||||||
dd->vertex(con->pos[0],con->pos[1]+0.2f,con->pos[2], duRGBA(0,48,64,196));
|
|
||||||
|
|
||||||
dd->vertex(con->pos[3],con->pos[4],con->pos[5], duRGBA(0,48,64,196));
|
|
||||||
dd->vertex(con->pos[3],con->pos[4]+0.2f,con->pos[5], duRGBA(0,48,64,196));
|
|
||||||
|
|
||||||
// Connection arc.
|
|
||||||
duAppendArc(dd, con->pos[0],con->pos[1],con->pos[2], con->pos[3],con->pos[4],con->pos[5], 0.25f,
|
|
||||||
(con->flags & 1) ? 0.6f : 0, 0.6f, col);
|
|
||||||
}
|
|
||||||
dd->end();
|
|
||||||
}
|
|
||||||
|
|
||||||
const unsigned int vcol = duRGBA(0,0,0,196);
|
|
||||||
dd->begin(DU_DRAW_POINTS, 3.0f);
|
|
||||||
for (int i = 0; i < tile->header->vertCount; ++i)
|
|
||||||
{
|
|
||||||
const float* v = &tile->verts[i*3];
|
|
||||||
dd->vertex(v[0], v[1], v[2], vcol);
|
|
||||||
}
|
|
||||||
dd->end();
|
|
||||||
|
|
||||||
dd->depthMask(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void duDebugDrawNavMesh(duDebugDraw* dd, const dtNavMesh& mesh, unsigned char flags)
|
|
||||||
{
|
|
||||||
if (!dd) return;
|
|
||||||
|
|
||||||
for (int i = 0; i < mesh.getMaxTiles(); ++i)
|
|
||||||
{
|
|
||||||
const dtMeshTile* tile = mesh.getTile(i);
|
|
||||||
if (!tile->header) continue;
|
|
||||||
drawMeshTile(dd, mesh, 0, tile, flags);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void duDebugDrawNavMeshWithClosedList(struct duDebugDraw* dd, const dtNavMesh& mesh, const dtNavMeshQuery& query, unsigned char flags)
|
|
||||||
{
|
|
||||||
if (!dd) return;
|
|
||||||
|
|
||||||
const dtNavMeshQuery* q = (flags & DU_DRAWNAVMESH_CLOSEDLIST) ? &query : 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < mesh.getMaxTiles(); ++i)
|
|
||||||
{
|
|
||||||
const dtMeshTile* tile = mesh.getTile(i);
|
|
||||||
if (!tile->header) continue;
|
|
||||||
drawMeshTile(dd, mesh, q, tile, flags);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void duDebugDrawNavMeshNodes(struct duDebugDraw* dd, const dtNavMeshQuery& query)
|
|
||||||
{
|
|
||||||
if (!dd) return;
|
|
||||||
|
|
||||||
const dtNodePool* pool = query.getNodePool();
|
|
||||||
if (pool)
|
|
||||||
{
|
|
||||||
const float off = 0.5f;
|
|
||||||
dd->begin(DU_DRAW_POINTS, 4.0f);
|
|
||||||
for (int i = 0; i < pool->getHashSize(); ++i)
|
|
||||||
{
|
|
||||||
for (dtNodeIndex j = pool->getFirst(i); j != DT_NULL_IDX; j = pool->getNext(j))
|
|
||||||
{
|
|
||||||
const dtNode* node = pool->getNodeAtIdx(j+1);
|
|
||||||
if (!node) continue;
|
|
||||||
dd->vertex(node->pos[0],node->pos[1]+off,node->pos[2], duRGBA(255,192,0,255));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dd->end();
|
|
||||||
|
|
||||||
dd->begin(DU_DRAW_LINES, 2.0f);
|
|
||||||
for (int i = 0; i < pool->getHashSize(); ++i)
|
|
||||||
{
|
|
||||||
for (dtNodeIndex j = pool->getFirst(i); j != DT_NULL_IDX; j = pool->getNext(j))
|
|
||||||
{
|
|
||||||
const dtNode* node = pool->getNodeAtIdx(j+1);
|
|
||||||
if (!node) continue;
|
|
||||||
if (!node->pidx) continue;
|
|
||||||
const dtNode* parent = pool->getNodeAtIdx(node->pidx);
|
|
||||||
if (!parent) continue;
|
|
||||||
dd->vertex(node->pos[0],node->pos[1]+off,node->pos[2], duRGBA(255,192,0,128));
|
|
||||||
dd->vertex(parent->pos[0],parent->pos[1]+off,parent->pos[2], duRGBA(255,192,0,128));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dd->end();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void drawMeshTileBVTree(duDebugDraw* dd, const dtMeshTile* tile)
|
|
||||||
{
|
|
||||||
// Draw BV nodes.
|
|
||||||
const float cs = 1.0f / tile->header->bvQuantFactor;
|
|
||||||
dd->begin(DU_DRAW_LINES, 1.0f);
|
|
||||||
for (int i = 0; i < tile->header->bvNodeCount; ++i)
|
|
||||||
{
|
|
||||||
const dtBVNode* n = &tile->bvTree[i];
|
|
||||||
if (n->i < 0) // Leaf indices are positive.
|
|
||||||
continue;
|
|
||||||
duAppendBoxWire(dd, tile->header->bmin[0] + n->bmin[0]*cs,
|
|
||||||
tile->header->bmin[1] + n->bmin[1]*cs,
|
|
||||||
tile->header->bmin[2] + n->bmin[2]*cs,
|
|
||||||
tile->header->bmin[0] + n->bmax[0]*cs,
|
|
||||||
tile->header->bmin[1] + n->bmax[1]*cs,
|
|
||||||
tile->header->bmin[2] + n->bmax[2]*cs,
|
|
||||||
duRGBA(255,255,255,128));
|
|
||||||
}
|
|
||||||
dd->end();
|
|
||||||
}
|
|
||||||
|
|
||||||
void duDebugDrawNavMeshBVTree(duDebugDraw* dd, const dtNavMesh& mesh)
|
|
||||||
{
|
|
||||||
if (!dd) return;
|
|
||||||
|
|
||||||
for (int i = 0; i < mesh.getMaxTiles(); ++i)
|
|
||||||
{
|
|
||||||
const dtMeshTile* tile = mesh.getTile(i);
|
|
||||||
if (!tile->header) continue;
|
|
||||||
drawMeshTileBVTree(dd, tile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void drawMeshTilePortal(duDebugDraw* dd, const dtMeshTile* tile)
|
|
||||||
{
|
|
||||||
// Draw portals
|
|
||||||
const float padx = 0.04f;
|
|
||||||
const float pady = tile->header->walkableClimb;
|
|
||||||
|
|
||||||
dd->begin(DU_DRAW_LINES, 2.0f);
|
|
||||||
|
|
||||||
for (int side = 0; side < 8; ++side)
|
|
||||||
{
|
|
||||||
unsigned short m = DT_EXT_LINK | (unsigned short)side;
|
|
||||||
|
|
||||||
for (int i = 0; i < tile->header->polyCount; ++i)
|
|
||||||
{
|
|
||||||
dtPoly* poly = &tile->polys[i];
|
|
||||||
|
|
||||||
// Create new links.
|
|
||||||
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;
|
|
||||||
|
|
||||||
// Create new links
|
|
||||||
const float* va = &tile->verts[poly->verts[j]*3];
|
|
||||||
const float* vb = &tile->verts[poly->verts[(j+1) % nv]*3];
|
|
||||||
|
|
||||||
if (side == 0 || side == 4)
|
|
||||||
{
|
|
||||||
unsigned int col = side == 0 ? duRGBA(128,0,0,128) : duRGBA(128,0,128,128);
|
|
||||||
|
|
||||||
const float x = va[0] + ((side == 0) ? -padx : padx);
|
|
||||||
|
|
||||||
dd->vertex(x,va[1]-pady,va[2], col);
|
|
||||||
dd->vertex(x,va[1]+pady,va[2], col);
|
|
||||||
|
|
||||||
dd->vertex(x,va[1]+pady,va[2], col);
|
|
||||||
dd->vertex(x,vb[1]+pady,vb[2], col);
|
|
||||||
|
|
||||||
dd->vertex(x,vb[1]+pady,vb[2], col);
|
|
||||||
dd->vertex(x,vb[1]-pady,vb[2], col);
|
|
||||||
|
|
||||||
dd->vertex(x,vb[1]-pady,vb[2], col);
|
|
||||||
dd->vertex(x,va[1]-pady,va[2], col);
|
|
||||||
}
|
|
||||||
else if (side == 2 || side == 6)
|
|
||||||
{
|
|
||||||
unsigned int col = side == 2 ? duRGBA(0,128,0,128) : duRGBA(0,128,128,128);
|
|
||||||
|
|
||||||
const float z = va[2] + ((side == 2) ? -padx : padx);
|
|
||||||
|
|
||||||
dd->vertex(va[0],va[1]-pady,z, col);
|
|
||||||
dd->vertex(va[0],va[1]+pady,z, col);
|
|
||||||
|
|
||||||
dd->vertex(va[0],va[1]+pady,z, col);
|
|
||||||
dd->vertex(vb[0],vb[1]+pady,z, col);
|
|
||||||
|
|
||||||
dd->vertex(vb[0],vb[1]+pady,z, col);
|
|
||||||
dd->vertex(vb[0],vb[1]-pady,z, col);
|
|
||||||
|
|
||||||
dd->vertex(vb[0],vb[1]-pady,z, col);
|
|
||||||
dd->vertex(va[0],va[1]-pady,z, col);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dd->end();
|
|
||||||
}
|
|
||||||
|
|
||||||
void duDebugDrawNavMeshPortals(duDebugDraw* dd, const dtNavMesh& mesh)
|
|
||||||
{
|
|
||||||
if (!dd) return;
|
|
||||||
|
|
||||||
for (int i = 0; i < mesh.getMaxTiles(); ++i)
|
|
||||||
{
|
|
||||||
const dtMeshTile* tile = mesh.getTile(i);
|
|
||||||
if (!tile->header) continue;
|
|
||||||
drawMeshTilePortal(dd, tile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void duDebugDrawNavMeshPolysWithFlags(struct duDebugDraw* dd, const dtNavMesh& mesh,
|
|
||||||
const unsigned short polyFlags, const unsigned int col)
|
|
||||||
{
|
|
||||||
if (!dd) return;
|
|
||||||
|
|
||||||
for (int i = 0; i < mesh.getMaxTiles(); ++i)
|
|
||||||
{
|
|
||||||
const dtMeshTile* tile = mesh.getTile(i);
|
|
||||||
if (!tile->header) continue;
|
|
||||||
dtPolyRef base = mesh.getPolyRefBase(tile);
|
|
||||||
|
|
||||||
for (int j = 0; j < tile->header->polyCount; ++j)
|
|
||||||
{
|
|
||||||
const dtPoly* p = &tile->polys[j];
|
|
||||||
if ((p->flags & polyFlags) == 0) continue;
|
|
||||||
duDebugDrawNavMeshPoly(dd, mesh, base|(dtPolyRef)j, col);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void duDebugDrawNavMeshPoly(duDebugDraw* dd, const dtNavMesh& mesh, dtPolyRef ref, const unsigned int col)
|
|
||||||
{
|
|
||||||
if (!dd) return;
|
|
||||||
|
|
||||||
const dtMeshTile* tile = 0;
|
|
||||||
const dtPoly* poly = 0;
|
|
||||||
if (dtStatusFailed(mesh.getTileAndPolyByRef(ref, &tile, &poly)))
|
|
||||||
return;
|
|
||||||
|
|
||||||
dd->depthMask(false);
|
|
||||||
|
|
||||||
const unsigned int c = duTransCol(col, 64);
|
|
||||||
const unsigned int ip = (unsigned int)(poly - tile->polys);
|
|
||||||
|
|
||||||
if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
|
|
||||||
{
|
|
||||||
dtOffMeshConnection* con = &tile->offMeshCons[ip - tile->header->offMeshBase];
|
|
||||||
|
|
||||||
dd->begin(DU_DRAW_LINES, 2.0f);
|
|
||||||
|
|
||||||
// Connection arc.
|
|
||||||
duAppendArc(dd, con->pos[0],con->pos[1],con->pos[2], con->pos[3],con->pos[4],con->pos[5], 0.25f,
|
|
||||||
(con->flags & 1) ? 0.6f : 0.0f, 0.6f, c);
|
|
||||||
|
|
||||||
dd->end();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const dtPolyDetail* pd = &tile->detailMeshes[ip];
|
|
||||||
|
|
||||||
dd->begin(DU_DRAW_TRIS);
|
|
||||||
for (int i = 0; i < pd->triCount; ++i)
|
|
||||||
{
|
|
||||||
const unsigned char* t = &tile->detailTris[(pd->triBase+i)*4];
|
|
||||||
for (int j = 0; j < 3; ++j)
|
|
||||||
{
|
|
||||||
if (t[j] < poly->vertCount)
|
|
||||||
dd->vertex(&tile->verts[poly->verts[t[j]]*3], c);
|
|
||||||
else
|
|
||||||
dd->vertex(&tile->detailVerts[(pd->vertBase+t[j]-poly->vertCount)*3], c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dd->end();
|
|
||||||
}
|
|
||||||
|
|
||||||
dd->depthMask(true);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void debugDrawTileCachePortals(struct duDebugDraw* dd, const dtTileCacheLayer& layer, const float cs, const float ch)
|
|
||||||
{
|
|
||||||
const int w = (int)layer.header->width;
|
|
||||||
const int h = (int)layer.header->height;
|
|
||||||
const float* bmin = layer.header->bmin;
|
|
||||||
|
|
||||||
// Portals
|
|
||||||
unsigned int pcol = duRGBA(255,255,255,255);
|
|
||||||
|
|
||||||
const int segs[4*4] = {0,0,0,1, 0,1,1,1, 1,1,1,0, 1,0,0,0};
|
|
||||||
|
|
||||||
// Layer portals
|
|
||||||
dd->begin(DU_DRAW_LINES, 2.0f);
|
|
||||||
for (int y = 0; y < h; ++y)
|
|
||||||
{
|
|
||||||
for (int x = 0; x < w; ++x)
|
|
||||||
{
|
|
||||||
const int idx = x+y*w;
|
|
||||||
const int lh = (int)layer.heights[idx];
|
|
||||||
if (lh == 0xff) continue;
|
|
||||||
|
|
||||||
for (int dir = 0; dir < 4; ++dir)
|
|
||||||
{
|
|
||||||
if (layer.cons[idx] & (1<<(dir+4)))
|
|
||||||
{
|
|
||||||
const int* seg = &segs[dir*4];
|
|
||||||
const float ax = bmin[0] + (x+seg[0])*cs;
|
|
||||||
const float ay = bmin[1] + (lh+2)*ch;
|
|
||||||
const float az = bmin[2] + (y+seg[1])*cs;
|
|
||||||
const float bx = bmin[0] + (x+seg[2])*cs;
|
|
||||||
const float by = bmin[1] + (lh+2)*ch;
|
|
||||||
const float bz = bmin[2] + (y+seg[3])*cs;
|
|
||||||
dd->vertex(ax, ay, az, pcol);
|
|
||||||
dd->vertex(bx, by, bz, pcol);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dd->end();
|
|
||||||
}
|
|
||||||
|
|
||||||
void duDebugDrawTileCacheLayerAreas(struct duDebugDraw* dd, const dtTileCacheLayer& layer, const float cs, const float ch)
|
|
||||||
{
|
|
||||||
const int w = (int)layer.header->width;
|
|
||||||
const int h = (int)layer.header->height;
|
|
||||||
const float* bmin = layer.header->bmin;
|
|
||||||
const float* bmax = layer.header->bmax;
|
|
||||||
const int idx = layer.header->tlayer;
|
|
||||||
|
|
||||||
unsigned int color = duIntToCol(idx+1, 255);
|
|
||||||
|
|
||||||
// Layer bounds
|
|
||||||
float lbmin[3], lbmax[3];
|
|
||||||
lbmin[0] = bmin[0] + layer.header->minx*cs;
|
|
||||||
lbmin[1] = bmin[1];
|
|
||||||
lbmin[2] = bmin[2] + layer.header->miny*cs;
|
|
||||||
lbmax[0] = bmin[0] + (layer.header->maxx+1)*cs;
|
|
||||||
lbmax[1] = bmax[1];
|
|
||||||
lbmax[2] = bmin[2] + (layer.header->maxy+1)*cs;
|
|
||||||
duDebugDrawBoxWire(dd, lbmin[0],lbmin[1],lbmin[2], lbmax[0],lbmax[1],lbmax[2], duTransCol(color,128), 2.0f);
|
|
||||||
|
|
||||||
// Layer height
|
|
||||||
dd->begin(DU_DRAW_QUADS);
|
|
||||||
for (int y = 0; y < h; ++y)
|
|
||||||
{
|
|
||||||
for (int x = 0; x < w; ++x)
|
|
||||||
{
|
|
||||||
const int lidx = x+y*w;
|
|
||||||
const int lh = (int)layer.heights[lidx];
|
|
||||||
if (lh == 0xff) continue;
|
|
||||||
|
|
||||||
const unsigned char area = layer.areas[lidx];
|
|
||||||
unsigned int col;
|
|
||||||
if (area == 63)
|
|
||||||
col = duLerpCol(color, duRGBA(0,192,255,64), 32);
|
|
||||||
else if (area == 0)
|
|
||||||
col = duLerpCol(color, duRGBA(0,0,0,64), 32);
|
|
||||||
else
|
|
||||||
col = duLerpCol(color, dd->areaToCol(area), 32);
|
|
||||||
|
|
||||||
const float fx = bmin[0] + x*cs;
|
|
||||||
const float fy = bmin[1] + (lh+1)*ch;
|
|
||||||
const float fz = bmin[2] + y*cs;
|
|
||||||
|
|
||||||
dd->vertex(fx, fy, fz, col);
|
|
||||||
dd->vertex(fx, fy, fz+cs, col);
|
|
||||||
dd->vertex(fx+cs, fy, fz+cs, col);
|
|
||||||
dd->vertex(fx+cs, fy, fz, col);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dd->end();
|
|
||||||
|
|
||||||
debugDrawTileCachePortals(dd, layer, cs, ch);
|
|
||||||
}
|
|
||||||
|
|
||||||
void duDebugDrawTileCacheLayerRegions(struct duDebugDraw* dd, const dtTileCacheLayer& layer, const float cs, const float ch)
|
|
||||||
{
|
|
||||||
const int w = (int)layer.header->width;
|
|
||||||
const int h = (int)layer.header->height;
|
|
||||||
const float* bmin = layer.header->bmin;
|
|
||||||
const float* bmax = layer.header->bmax;
|
|
||||||
const int idx = layer.header->tlayer;
|
|
||||||
|
|
||||||
unsigned int color = duIntToCol(idx+1, 255);
|
|
||||||
|
|
||||||
// Layer bounds
|
|
||||||
float lbmin[3], lbmax[3];
|
|
||||||
lbmin[0] = bmin[0] + layer.header->minx*cs;
|
|
||||||
lbmin[1] = bmin[1];
|
|
||||||
lbmin[2] = bmin[2] + layer.header->miny*cs;
|
|
||||||
lbmax[0] = bmin[0] + (layer.header->maxx+1)*cs;
|
|
||||||
lbmax[1] = bmax[1];
|
|
||||||
lbmax[2] = bmin[2] + (layer.header->maxy+1)*cs;
|
|
||||||
duDebugDrawBoxWire(dd, lbmin[0],lbmin[1],lbmin[2], lbmax[0],lbmax[1],lbmax[2], duTransCol(color,128), 2.0f);
|
|
||||||
|
|
||||||
// Layer height
|
|
||||||
dd->begin(DU_DRAW_QUADS);
|
|
||||||
for (int y = 0; y < h; ++y)
|
|
||||||
{
|
|
||||||
for (int x = 0; x < w; ++x)
|
|
||||||
{
|
|
||||||
const int lidx = x+y*w;
|
|
||||||
const int lh = (int)layer.heights[lidx];
|
|
||||||
if (lh == 0xff) continue;
|
|
||||||
const unsigned char reg = layer.regs[lidx];
|
|
||||||
|
|
||||||
unsigned int col = duLerpCol(color, duIntToCol(reg, 255), 192);
|
|
||||||
|
|
||||||
const float fx = bmin[0] + x*cs;
|
|
||||||
const float fy = bmin[1] + (lh+1)*ch;
|
|
||||||
const float fz = bmin[2] + y*cs;
|
|
||||||
|
|
||||||
dd->vertex(fx, fy, fz, col);
|
|
||||||
dd->vertex(fx, fy, fz+cs, col);
|
|
||||||
dd->vertex(fx+cs, fy, fz+cs, col);
|
|
||||||
dd->vertex(fx+cs, fy, fz, col);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dd->end();
|
|
||||||
|
|
||||||
debugDrawTileCachePortals(dd, layer, cs, ch);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*struct dtTileCacheContour
|
|
||||||
{
|
|
||||||
int nverts;
|
|
||||||
unsigned char* verts;
|
|
||||||
unsigned char reg;
|
|
||||||
unsigned char area;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct dtTileCacheContourSet
|
|
||||||
{
|
|
||||||
int nconts;
|
|
||||||
dtTileCacheContour* conts;
|
|
||||||
};*/
|
|
||||||
|
|
||||||
void duDebugDrawTileCacheContours(duDebugDraw* dd, const struct dtTileCacheContourSet& lcset,
|
|
||||||
const float* orig, const float cs, const float ch)
|
|
||||||
{
|
|
||||||
if (!dd) return;
|
|
||||||
|
|
||||||
const unsigned char a = 255;// (unsigned char)(alpha*255.0f);
|
|
||||||
|
|
||||||
const int offs[2*4] = {-1,0, 0,1, 1,0, 0,-1};
|
|
||||||
|
|
||||||
dd->begin(DU_DRAW_LINES, 2.0f);
|
|
||||||
|
|
||||||
for (int i = 0; i < lcset.nconts; ++i)
|
|
||||||
{
|
|
||||||
const dtTileCacheContour& c = lcset.conts[i];
|
|
||||||
unsigned int color = 0;
|
|
||||||
|
|
||||||
color = duIntToCol(i, a);
|
|
||||||
|
|
||||||
for (int j = 0; j < c.nverts; ++j)
|
|
||||||
{
|
|
||||||
const int k = (j+1) % c.nverts;
|
|
||||||
const unsigned char* va = &c.verts[j*4];
|
|
||||||
const unsigned char* vb = &c.verts[k*4];
|
|
||||||
const float ax = orig[0] + va[0]*cs;
|
|
||||||
const float ay = orig[1] + (va[1]+1+(i&1))*ch;
|
|
||||||
const float az = orig[2] + va[2]*cs;
|
|
||||||
const float bx = orig[0] + vb[0]*cs;
|
|
||||||
const float by = orig[1] + (vb[1]+1+(i&1))*ch;
|
|
||||||
const float bz = orig[2] + vb[2]*cs;
|
|
||||||
unsigned int col = color;
|
|
||||||
if ((va[3] & 0xf) != 0xf)
|
|
||||||
{
|
|
||||||
// Portal segment
|
|
||||||
col = duRGBA(255,255,255,128);
|
|
||||||
int d = va[3] & 0xf;
|
|
||||||
|
|
||||||
const float cx = (ax+bx)*0.5f;
|
|
||||||
const float cy = (ay+by)*0.5f;
|
|
||||||
const float cz = (az+bz)*0.5f;
|
|
||||||
|
|
||||||
const float dx = cx + offs[d*2+0]*2*cs;
|
|
||||||
const float dy = cy;
|
|
||||||
const float dz = cz + offs[d*2+1]*2*cs;
|
|
||||||
|
|
||||||
dd->vertex(cx,cy,cz,duRGBA(255,0,0,255));
|
|
||||||
dd->vertex(dx,dy,dz,duRGBA(255,0,0,255));
|
|
||||||
}
|
|
||||||
|
|
||||||
duAppendArrow(dd, ax,ay,az, bx,by,bz, 0.0f, cs*0.5f, col);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dd->end();
|
|
||||||
|
|
||||||
dd->begin(DU_DRAW_POINTS, 4.0f);
|
|
||||||
|
|
||||||
for (int i = 0; i < lcset.nconts; ++i)
|
|
||||||
{
|
|
||||||
const dtTileCacheContour& c = lcset.conts[i];
|
|
||||||
unsigned int color = 0;
|
|
||||||
|
|
||||||
for (int j = 0; j < c.nverts; ++j)
|
|
||||||
{
|
|
||||||
const unsigned char* va = &c.verts[j*4];
|
|
||||||
|
|
||||||
color = duDarkenCol(duIntToCol(i, a));
|
|
||||||
if (va[3] & 0x80)
|
|
||||||
{
|
|
||||||
// Border vertex
|
|
||||||
color = duRGBA(255,0,0,255);
|
|
||||||
}
|
|
||||||
|
|
||||||
float fx = orig[0] + va[0]*cs;
|
|
||||||
float fy = orig[1] + (va[1]+1+(i&1))*ch;
|
|
||||||
float fz = orig[2] + va[2]*cs;
|
|
||||||
dd->vertex(fx,fy,fz, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dd->end();
|
|
||||||
}
|
|
||||||
|
|
||||||
void duDebugDrawTileCachePolyMesh(duDebugDraw* dd, const struct dtTileCachePolyMesh& lmesh,
|
|
||||||
const float* orig, const float cs, const float ch)
|
|
||||||
{
|
|
||||||
if (!dd) return;
|
|
||||||
|
|
||||||
const int nvp = lmesh.nvp;
|
|
||||||
|
|
||||||
const int offs[2*4] = {-1,0, 0,1, 1,0, 0,-1};
|
|
||||||
|
|
||||||
dd->begin(DU_DRAW_TRIS);
|
|
||||||
|
|
||||||
for (int i = 0; i < lmesh.npolys; ++i)
|
|
||||||
{
|
|
||||||
const unsigned short* p = &lmesh.polys[i*nvp*2];
|
|
||||||
const unsigned char area = lmesh.areas[i];
|
|
||||||
|
|
||||||
unsigned int color;
|
|
||||||
if (area == DT_TILECACHE_WALKABLE_AREA)
|
|
||||||
color = duRGBA(0,192,255,64);
|
|
||||||
else if (area == DT_TILECACHE_NULL_AREA)
|
|
||||||
color = duRGBA(0,0,0,64);
|
|
||||||
else
|
|
||||||
color = dd->areaToCol(area);
|
|
||||||
|
|
||||||
unsigned short vi[3];
|
|
||||||
for (int j = 2; j < nvp; ++j)
|
|
||||||
{
|
|
||||||
if (p[j] == DT_TILECACHE_NULL_IDX) break;
|
|
||||||
vi[0] = p[0];
|
|
||||||
vi[1] = p[j-1];
|
|
||||||
vi[2] = p[j];
|
|
||||||
for (int k = 0; k < 3; ++k)
|
|
||||||
{
|
|
||||||
const unsigned short* v = &lmesh.verts[vi[k]*3];
|
|
||||||
const float x = orig[0] + v[0]*cs;
|
|
||||||
const float y = orig[1] + (v[1]+1)*ch;
|
|
||||||
const float z = orig[2] + v[2]*cs;
|
|
||||||
dd->vertex(x,y,z, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dd->end();
|
|
||||||
|
|
||||||
// Draw neighbours edges
|
|
||||||
const unsigned int coln = duRGBA(0,48,64,32);
|
|
||||||
dd->begin(DU_DRAW_LINES, 1.5f);
|
|
||||||
for (int i = 0; i < lmesh.npolys; ++i)
|
|
||||||
{
|
|
||||||
const unsigned short* p = &lmesh.polys[i*nvp*2];
|
|
||||||
for (int j = 0; j < nvp; ++j)
|
|
||||||
{
|
|
||||||
if (p[j] == DT_TILECACHE_NULL_IDX) break;
|
|
||||||
if (p[nvp+j] & 0x8000) continue;
|
|
||||||
const int nj = (j+1 >= nvp || p[j+1] == DT_TILECACHE_NULL_IDX) ? 0 : j+1;
|
|
||||||
int vi[2] = {p[j], p[nj]};
|
|
||||||
|
|
||||||
for (int k = 0; k < 2; ++k)
|
|
||||||
{
|
|
||||||
const unsigned short* v = &lmesh.verts[vi[k]*3];
|
|
||||||
const float x = orig[0] + v[0]*cs;
|
|
||||||
const float y = orig[1] + (v[1]+1)*ch + 0.1f;
|
|
||||||
const float z = orig[2] + v[2]*cs;
|
|
||||||
dd->vertex(x, y, z, coln);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dd->end();
|
|
||||||
|
|
||||||
// Draw boundary edges
|
|
||||||
const unsigned int colb = duRGBA(0,48,64,220);
|
|
||||||
dd->begin(DU_DRAW_LINES, 2.5f);
|
|
||||||
for (int i = 0; i < lmesh.npolys; ++i)
|
|
||||||
{
|
|
||||||
const unsigned short* p = &lmesh.polys[i*nvp*2];
|
|
||||||
for (int j = 0; j < nvp; ++j)
|
|
||||||
{
|
|
||||||
if (p[j] == DT_TILECACHE_NULL_IDX) break;
|
|
||||||
if ((p[nvp+j] & 0x8000) == 0) continue;
|
|
||||||
const int nj = (j+1 >= nvp || p[j+1] == DT_TILECACHE_NULL_IDX) ? 0 : j+1;
|
|
||||||
int vi[2] = {p[j], p[nj]};
|
|
||||||
|
|
||||||
unsigned int col = colb;
|
|
||||||
if ((p[nvp+j] & 0xf) != 0xf)
|
|
||||||
{
|
|
||||||
const unsigned short* va = &lmesh.verts[vi[0]*3];
|
|
||||||
const unsigned short* vb = &lmesh.verts[vi[1]*3];
|
|
||||||
|
|
||||||
const float ax = orig[0] + va[0]*cs;
|
|
||||||
const float ay = orig[1] + (va[1]+1+(i&1))*ch;
|
|
||||||
const float az = orig[2] + va[2]*cs;
|
|
||||||
const float bx = orig[0] + vb[0]*cs;
|
|
||||||
const float by = orig[1] + (vb[1]+1+(i&1))*ch;
|
|
||||||
const float bz = orig[2] + vb[2]*cs;
|
|
||||||
|
|
||||||
const float cx = (ax+bx)*0.5f;
|
|
||||||
const float cy = (ay+by)*0.5f;
|
|
||||||
const float cz = (az+bz)*0.5f;
|
|
||||||
|
|
||||||
int d = p[nvp+j] & 0xf;
|
|
||||||
|
|
||||||
const float dx = cx + offs[d*2+0]*2*cs;
|
|
||||||
const float dy = cy;
|
|
||||||
const float dz = cz + offs[d*2+1]*2*cs;
|
|
||||||
|
|
||||||
dd->vertex(cx,cy,cz,duRGBA(255,0,0,255));
|
|
||||||
dd->vertex(dx,dy,dz,duRGBA(255,0,0,255));
|
|
||||||
|
|
||||||
col = duRGBA(255,255,255,128);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int k = 0; k < 2; ++k)
|
|
||||||
{
|
|
||||||
const unsigned short* v = &lmesh.verts[vi[k]*3];
|
|
||||||
const float x = orig[0] + v[0]*cs;
|
|
||||||
const float y = orig[1] + (v[1]+1)*ch + 0.1f;
|
|
||||||
const float z = orig[2] + v[2]*cs;
|
|
||||||
dd->vertex(x, y, z, col);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dd->end();
|
|
||||||
|
|
||||||
dd->begin(DU_DRAW_POINTS, 3.0f);
|
|
||||||
const unsigned int colv = duRGBA(0,0,0,220);
|
|
||||||
for (int i = 0; i < lmesh.nverts; ++i)
|
|
||||||
{
|
|
||||||
const unsigned short* v = &lmesh.verts[i*3];
|
|
||||||
const float x = orig[0] + v[0]*cs;
|
|
||||||
const float y = orig[1] + (v[1]+1)*ch + 0.1f;
|
|
||||||
const float z = orig[2] + v[2]*cs;
|
|
||||||
dd->vertex(x,y,z, colv);
|
|
||||||
}
|
|
||||||
dd->end();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
@ -1,451 +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.
|
|
||||||
//
|
|
||||||
|
|
||||||
#define _USE_MATH_DEFINES
|
|
||||||
#include <math.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "Recast.h"
|
|
||||||
#include "RecastAlloc.h"
|
|
||||||
#include "RecastDump.h"
|
|
||||||
|
|
||||||
|
|
||||||
duFileIO::~duFileIO()
|
|
||||||
{
|
|
||||||
// Empty
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ioprintf(duFileIO* io, const char* format, ...)
|
|
||||||
{
|
|
||||||
char line[256];
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, format);
|
|
||||||
const int n = vsnprintf(line, sizeof(line), format, ap);
|
|
||||||
va_end(ap);
|
|
||||||
if (n > 0)
|
|
||||||
io->write(line, sizeof(char)*n);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool duDumpPolyMeshToObj(rcPolyMesh& pmesh, duFileIO* io)
|
|
||||||
{
|
|
||||||
if (!io)
|
|
||||||
{
|
|
||||||
printf("duDumpPolyMeshToObj: input IO is null.\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!io->isWriting())
|
|
||||||
{
|
|
||||||
printf("duDumpPolyMeshToObj: input IO not writing.\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const int nvp = pmesh.nvp;
|
|
||||||
const float cs = pmesh.cs;
|
|
||||||
const float ch = pmesh.ch;
|
|
||||||
const float* orig = pmesh.bmin;
|
|
||||||
|
|
||||||
ioprintf(io, "# Recast Navmesh\n");
|
|
||||||
ioprintf(io, "o NavMesh\n");
|
|
||||||
|
|
||||||
ioprintf(io, "\n");
|
|
||||||
|
|
||||||
for (int i = 0; i < pmesh.nverts; ++i)
|
|
||||||
{
|
|
||||||
const unsigned short* v = &pmesh.verts[i*3];
|
|
||||||
const float x = orig[0] + v[0]*cs;
|
|
||||||
const float y = orig[1] + (v[1]+1)*ch + 0.1f;
|
|
||||||
const float z = orig[2] + v[2]*cs;
|
|
||||||
ioprintf(io, "v %f %f %f\n", x,y,z);
|
|
||||||
}
|
|
||||||
|
|
||||||
ioprintf(io, "\n");
|
|
||||||
|
|
||||||
for (int i = 0; i < pmesh.npolys; ++i)
|
|
||||||
{
|
|
||||||
const unsigned short* p = &pmesh.polys[i*nvp*2];
|
|
||||||
for (int j = 2; j < nvp; ++j)
|
|
||||||
{
|
|
||||||
if (p[j] == RC_MESH_NULL_IDX) break;
|
|
||||||
ioprintf(io, "f %d %d %d\n", p[0]+1, p[j-1]+1, p[j]+1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool duDumpPolyMeshDetailToObj(rcPolyMeshDetail& dmesh, duFileIO* io)
|
|
||||||
{
|
|
||||||
if (!io)
|
|
||||||
{
|
|
||||||
printf("duDumpPolyMeshDetailToObj: input IO is null.\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!io->isWriting())
|
|
||||||
{
|
|
||||||
printf("duDumpPolyMeshDetailToObj: input IO not writing.\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ioprintf(io, "# Recast Navmesh\n");
|
|
||||||
ioprintf(io, "o NavMesh\n");
|
|
||||||
|
|
||||||
ioprintf(io, "\n");
|
|
||||||
|
|
||||||
for (int i = 0; i < dmesh.nverts; ++i)
|
|
||||||
{
|
|
||||||
const float* v = &dmesh.verts[i*3];
|
|
||||||
ioprintf(io, "v %f %f %f\n", v[0],v[1],v[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
ioprintf(io, "\n");
|
|
||||||
|
|
||||||
for (int i = 0; i < dmesh.nmeshes; ++i)
|
|
||||||
{
|
|
||||||
const unsigned int* m = &dmesh.meshes[i*4];
|
|
||||||
const unsigned int bverts = m[0];
|
|
||||||
const unsigned int btris = m[2];
|
|
||||||
const unsigned int ntris = m[3];
|
|
||||||
const unsigned char* tris = &dmesh.tris[btris*4];
|
|
||||||
for (unsigned int j = 0; j < ntris; ++j)
|
|
||||||
{
|
|
||||||
ioprintf(io, "f %d %d %d\n",
|
|
||||||
(int)(bverts+tris[j*4+0])+1,
|
|
||||||
(int)(bverts+tris[j*4+1])+1,
|
|
||||||
(int)(bverts+tris[j*4+2])+1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const int CSET_MAGIC = ('c' << 24) | ('s' << 16) | ('e' << 8) | 't';
|
|
||||||
static const int CSET_VERSION = 2;
|
|
||||||
|
|
||||||
bool duDumpContourSet(struct rcContourSet& cset, duFileIO* io)
|
|
||||||
{
|
|
||||||
if (!io)
|
|
||||||
{
|
|
||||||
printf("duDumpContourSet: input IO is null.\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!io->isWriting())
|
|
||||||
{
|
|
||||||
printf("duDumpContourSet: input IO not writing.\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
io->write(&CSET_MAGIC, sizeof(CSET_MAGIC));
|
|
||||||
io->write(&CSET_VERSION, sizeof(CSET_VERSION));
|
|
||||||
|
|
||||||
io->write(&cset.nconts, sizeof(cset.nconts));
|
|
||||||
|
|
||||||
io->write(cset.bmin, sizeof(cset.bmin));
|
|
||||||
io->write(cset.bmax, sizeof(cset.bmax));
|
|
||||||
|
|
||||||
io->write(&cset.cs, sizeof(cset.cs));
|
|
||||||
io->write(&cset.ch, sizeof(cset.ch));
|
|
||||||
|
|
||||||
io->write(&cset.width, sizeof(cset.width));
|
|
||||||
io->write(&cset.height, sizeof(cset.height));
|
|
||||||
io->write(&cset.borderSize, sizeof(cset.borderSize));
|
|
||||||
|
|
||||||
for (int i = 0; i < cset.nconts; ++i)
|
|
||||||
{
|
|
||||||
const rcContour& cont = cset.conts[i];
|
|
||||||
io->write(&cont.nverts, sizeof(cont.nverts));
|
|
||||||
io->write(&cont.nrverts, sizeof(cont.nrverts));
|
|
||||||
io->write(&cont.reg, sizeof(cont.reg));
|
|
||||||
io->write(&cont.area, sizeof(cont.area));
|
|
||||||
io->write(cont.verts, sizeof(int)*4*cont.nverts);
|
|
||||||
io->write(cont.rverts, sizeof(int)*4*cont.nrverts);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool duReadContourSet(struct rcContourSet& cset, duFileIO* io)
|
|
||||||
{
|
|
||||||
if (!io)
|
|
||||||
{
|
|
||||||
printf("duReadContourSet: input IO is null.\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!io->isReading())
|
|
||||||
{
|
|
||||||
printf("duReadContourSet: input IO not reading.\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int magic = 0;
|
|
||||||
int version = 0;
|
|
||||||
|
|
||||||
io->read(&magic, sizeof(magic));
|
|
||||||
io->read(&version, sizeof(version));
|
|
||||||
|
|
||||||
if (magic != CSET_MAGIC)
|
|
||||||
{
|
|
||||||
printf("duReadContourSet: Bad voodoo.\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (version != CSET_VERSION)
|
|
||||||
{
|
|
||||||
printf("duReadContourSet: Bad version.\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
io->read(&cset.nconts, sizeof(cset.nconts));
|
|
||||||
|
|
||||||
cset.conts = (rcContour*)rcAlloc(sizeof(rcContour)*cset.nconts, RC_ALLOC_PERM);
|
|
||||||
if (!cset.conts)
|
|
||||||
{
|
|
||||||
printf("duReadContourSet: Could not alloc contours (%d)\n", cset.nconts);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
memset(cset.conts, 0, sizeof(rcContour)*cset.nconts);
|
|
||||||
|
|
||||||
io->read(cset.bmin, sizeof(cset.bmin));
|
|
||||||
io->read(cset.bmax, sizeof(cset.bmax));
|
|
||||||
|
|
||||||
io->read(&cset.cs, sizeof(cset.cs));
|
|
||||||
io->read(&cset.ch, sizeof(cset.ch));
|
|
||||||
|
|
||||||
io->read(&cset.width, sizeof(cset.width));
|
|
||||||
io->read(&cset.height, sizeof(cset.height));
|
|
||||||
io->read(&cset.borderSize, sizeof(cset.borderSize));
|
|
||||||
|
|
||||||
for (int i = 0; i < cset.nconts; ++i)
|
|
||||||
{
|
|
||||||
rcContour& cont = cset.conts[i];
|
|
||||||
io->read(&cont.nverts, sizeof(cont.nverts));
|
|
||||||
io->read(&cont.nrverts, sizeof(cont.nrverts));
|
|
||||||
io->read(&cont.reg, sizeof(cont.reg));
|
|
||||||
io->read(&cont.area, sizeof(cont.area));
|
|
||||||
|
|
||||||
cont.verts = (int*)rcAlloc(sizeof(int)*4*cont.nverts, RC_ALLOC_PERM);
|
|
||||||
if (!cont.verts)
|
|
||||||
{
|
|
||||||
printf("duReadContourSet: Could not alloc contour verts (%d)\n", cont.nverts);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
cont.rverts = (int*)rcAlloc(sizeof(int)*4*cont.nrverts, RC_ALLOC_PERM);
|
|
||||||
if (!cont.rverts)
|
|
||||||
{
|
|
||||||
printf("duReadContourSet: Could not alloc contour rverts (%d)\n", cont.nrverts);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
io->read(cont.verts, sizeof(int)*4*cont.nverts);
|
|
||||||
io->read(cont.rverts, sizeof(int)*4*cont.nrverts);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static const int CHF_MAGIC = ('r' << 24) | ('c' << 16) | ('h' << 8) | 'f';
|
|
||||||
static const int CHF_VERSION = 3;
|
|
||||||
|
|
||||||
bool duDumpCompactHeightfield(struct rcCompactHeightfield& chf, duFileIO* io)
|
|
||||||
{
|
|
||||||
if (!io)
|
|
||||||
{
|
|
||||||
printf("duDumpCompactHeightfield: input IO is null.\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!io->isWriting())
|
|
||||||
{
|
|
||||||
printf("duDumpCompactHeightfield: input IO not writing.\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
io->write(&CHF_MAGIC, sizeof(CHF_MAGIC));
|
|
||||||
io->write(&CHF_VERSION, sizeof(CHF_VERSION));
|
|
||||||
|
|
||||||
io->write(&chf.width, sizeof(chf.width));
|
|
||||||
io->write(&chf.height, sizeof(chf.height));
|
|
||||||
io->write(&chf.spanCount, sizeof(chf.spanCount));
|
|
||||||
|
|
||||||
io->write(&chf.walkableHeight, sizeof(chf.walkableHeight));
|
|
||||||
io->write(&chf.walkableClimb, sizeof(chf.walkableClimb));
|
|
||||||
io->write(&chf.borderSize, sizeof(chf.borderSize));
|
|
||||||
|
|
||||||
io->write(&chf.maxDistance, sizeof(chf.maxDistance));
|
|
||||||
io->write(&chf.maxRegions, sizeof(chf.maxRegions));
|
|
||||||
|
|
||||||
io->write(chf.bmin, sizeof(chf.bmin));
|
|
||||||
io->write(chf.bmax, sizeof(chf.bmax));
|
|
||||||
|
|
||||||
io->write(&chf.cs, sizeof(chf.cs));
|
|
||||||
io->write(&chf.ch, sizeof(chf.ch));
|
|
||||||
|
|
||||||
int tmp = 0;
|
|
||||||
if (chf.cells) tmp |= 1;
|
|
||||||
if (chf.spans) tmp |= 2;
|
|
||||||
if (chf.dist) tmp |= 4;
|
|
||||||
if (chf.areas) tmp |= 8;
|
|
||||||
|
|
||||||
io->write(&tmp, sizeof(tmp));
|
|
||||||
|
|
||||||
if (chf.cells)
|
|
||||||
io->write(chf.cells, sizeof(rcCompactCell)*chf.width*chf.height);
|
|
||||||
if (chf.spans)
|
|
||||||
io->write(chf.spans, sizeof(rcCompactSpan)*chf.spanCount);
|
|
||||||
if (chf.dist)
|
|
||||||
io->write(chf.dist, sizeof(unsigned short)*chf.spanCount);
|
|
||||||
if (chf.areas)
|
|
||||||
io->write(chf.areas, sizeof(unsigned char)*chf.spanCount);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool duReadCompactHeightfield(struct rcCompactHeightfield& chf, duFileIO* io)
|
|
||||||
{
|
|
||||||
if (!io)
|
|
||||||
{
|
|
||||||
printf("duReadCompactHeightfield: input IO is null.\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!io->isReading())
|
|
||||||
{
|
|
||||||
printf("duReadCompactHeightfield: input IO not reading.\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int magic = 0;
|
|
||||||
int version = 0;
|
|
||||||
|
|
||||||
io->read(&magic, sizeof(magic));
|
|
||||||
io->read(&version, sizeof(version));
|
|
||||||
|
|
||||||
if (magic != CHF_MAGIC)
|
|
||||||
{
|
|
||||||
printf("duReadCompactHeightfield: Bad voodoo.\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (version != CHF_VERSION)
|
|
||||||
{
|
|
||||||
printf("duReadCompactHeightfield: Bad version.\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
io->read(&chf.width, sizeof(chf.width));
|
|
||||||
io->read(&chf.height, sizeof(chf.height));
|
|
||||||
io->read(&chf.spanCount, sizeof(chf.spanCount));
|
|
||||||
|
|
||||||
io->read(&chf.walkableHeight, sizeof(chf.walkableHeight));
|
|
||||||
io->read(&chf.walkableClimb, sizeof(chf.walkableClimb));
|
|
||||||
io->read(&chf.borderSize, sizeof(chf.borderSize));
|
|
||||||
|
|
||||||
io->read(&chf.maxDistance, sizeof(chf.maxDistance));
|
|
||||||
io->read(&chf.maxRegions, sizeof(chf.maxRegions));
|
|
||||||
|
|
||||||
io->read(chf.bmin, sizeof(chf.bmin));
|
|
||||||
io->read(chf.bmax, sizeof(chf.bmax));
|
|
||||||
|
|
||||||
io->read(&chf.cs, sizeof(chf.cs));
|
|
||||||
io->read(&chf.ch, sizeof(chf.ch));
|
|
||||||
|
|
||||||
int tmp = 0;
|
|
||||||
io->read(&tmp, sizeof(tmp));
|
|
||||||
|
|
||||||
if (tmp & 1)
|
|
||||||
{
|
|
||||||
chf.cells = (rcCompactCell*)rcAlloc(sizeof(rcCompactCell)*chf.width*chf.height, RC_ALLOC_PERM);
|
|
||||||
if (!chf.cells)
|
|
||||||
{
|
|
||||||
printf("duReadCompactHeightfield: Could not alloc cells (%d)\n", chf.width*chf.height);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
io->read(chf.cells, sizeof(rcCompactCell)*chf.width*chf.height);
|
|
||||||
}
|
|
||||||
if (tmp & 2)
|
|
||||||
{
|
|
||||||
chf.spans = (rcCompactSpan*)rcAlloc(sizeof(rcCompactSpan)*chf.spanCount, RC_ALLOC_PERM);
|
|
||||||
if (!chf.spans)
|
|
||||||
{
|
|
||||||
printf("duReadCompactHeightfield: Could not alloc spans (%d)\n", chf.spanCount);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
io->read(chf.spans, sizeof(rcCompactSpan)*chf.spanCount);
|
|
||||||
}
|
|
||||||
if (tmp & 4)
|
|
||||||
{
|
|
||||||
chf.dist = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_PERM);
|
|
||||||
if (!chf.dist)
|
|
||||||
{
|
|
||||||
printf("duReadCompactHeightfield: Could not alloc dist (%d)\n", chf.spanCount);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
io->read(chf.dist, sizeof(unsigned short)*chf.spanCount);
|
|
||||||
}
|
|
||||||
if (tmp & 8)
|
|
||||||
{
|
|
||||||
chf.areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_PERM);
|
|
||||||
if (!chf.areas)
|
|
||||||
{
|
|
||||||
printf("duReadCompactHeightfield: Could not alloc areas (%d)\n", chf.spanCount);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
io->read(chf.areas, sizeof(unsigned char)*chf.spanCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void logLine(rcContext& ctx, rcTimerLabel label, const char* name, const float pc)
|
|
||||||
{
|
|
||||||
const int t = ctx.getAccumulatedTime(label);
|
|
||||||
if (t < 0) return;
|
|
||||||
ctx.log(RC_LOG_PROGRESS, "%s:\t%.2fms\t(%.1f%%)", name, t/1000.0f, t*pc);
|
|
||||||
}
|
|
||||||
|
|
||||||
void duLogBuildTimes(rcContext& ctx, const int totalTimeUsec)
|
|
||||||
{
|
|
||||||
const float pc = 100.0f / totalTimeUsec;
|
|
||||||
|
|
||||||
ctx.log(RC_LOG_PROGRESS, "Build Times");
|
|
||||||
logLine(ctx, RC_TIMER_RASTERIZE_TRIANGLES, "- Rasterize", pc);
|
|
||||||
logLine(ctx, RC_TIMER_BUILD_COMPACTHEIGHTFIELD, "- Build Compact", pc);
|
|
||||||
logLine(ctx, RC_TIMER_FILTER_BORDER, "- Filter Border", pc);
|
|
||||||
logLine(ctx, RC_TIMER_FILTER_WALKABLE, "- Filter Walkable", pc);
|
|
||||||
logLine(ctx, RC_TIMER_ERODE_AREA, "- Erode Area", pc);
|
|
||||||
logLine(ctx, RC_TIMER_MEDIAN_AREA, "- Median Area", pc);
|
|
||||||
logLine(ctx, RC_TIMER_MARK_BOX_AREA, "- Mark Box Area", pc);
|
|
||||||
logLine(ctx, RC_TIMER_MARK_CONVEXPOLY_AREA, "- Mark Convex Area", pc);
|
|
||||||
logLine(ctx, RC_TIMER_MARK_CYLINDER_AREA, "- Mark Cylinder Area", pc);
|
|
||||||
logLine(ctx, RC_TIMER_BUILD_DISTANCEFIELD, "- Build Distance Field", pc);
|
|
||||||
logLine(ctx, RC_TIMER_BUILD_DISTANCEFIELD_DIST, " - Distance", pc);
|
|
||||||
logLine(ctx, RC_TIMER_BUILD_DISTANCEFIELD_BLUR, " - Blur", pc);
|
|
||||||
logLine(ctx, RC_TIMER_BUILD_REGIONS, "- Build Regions", pc);
|
|
||||||
logLine(ctx, RC_TIMER_BUILD_REGIONS_WATERSHED, " - Watershed", pc);
|
|
||||||
logLine(ctx, RC_TIMER_BUILD_REGIONS_EXPAND, " - Expand", pc);
|
|
||||||
logLine(ctx, RC_TIMER_BUILD_REGIONS_FLOOD, " - Find Basins", pc);
|
|
||||||
logLine(ctx, RC_TIMER_BUILD_REGIONS_FILTER, " - Filter", pc);
|
|
||||||
logLine(ctx, RC_TIMER_BUILD_LAYERS, "- Build Layers", pc);
|
|
||||||
logLine(ctx, RC_TIMER_BUILD_CONTOURS, "- Build Contours", pc);
|
|
||||||
logLine(ctx, RC_TIMER_BUILD_CONTOURS_TRACE, " - Trace", pc);
|
|
||||||
logLine(ctx, RC_TIMER_BUILD_CONTOURS_SIMPLIFY, " - Simplify", pc);
|
|
||||||
logLine(ctx, RC_TIMER_BUILD_POLYMESH, "- Build Polymesh", pc);
|
|
||||||
logLine(ctx, RC_TIMER_BUILD_POLYMESHDETAIL, "- Build Polymesh Detail", pc);
|
|
||||||
logLine(ctx, RC_TIMER_MERGE_POLYMESH, "- Merge Polymeshes", pc);
|
|
||||||
logLine(ctx, RC_TIMER_MERGE_POLYMESHDETAIL, "- Merge Polymesh Details", pc);
|
|
||||||
ctx.log(RC_LOG_PROGRESS, "=== TOTAL:\t%.2fms", totalTimeUsec/1000.0f);
|
|
||||||
}
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
|||||||
file(GLOB SOURCES Source/*.cpp)
|
|
||||||
add_library(Detour ${SOURCES})
|
|
||||||
|
|
||||||
add_library(RecastNavigation::Detour ALIAS Detour)
|
|
||||||
set_target_properties(Detour PROPERTIES DEBUG_POSTFIX -d)
|
|
||||||
|
|
||||||
set(Detour_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Include")
|
|
||||||
|
|
||||||
target_include_directories(Detour PUBLIC
|
|
||||||
"$<BUILD_INTERFACE:${Detour_INCLUDE_DIR}>"
|
|
||||||
)
|
|
||||||
|
|
||||||
set_target_properties(Detour PROPERTIES
|
|
||||||
SOVERSION ${SOVERSION}
|
|
||||||
VERSION ${VERSION}
|
|
||||||
COMPILE_PDB_OUTPUT_DIRECTORY .
|
|
||||||
COMPILE_PDB_NAME "Detour-d"
|
|
||||||
)
|
|
||||||
|
|
||||||
install(TARGETS Detour
|
|
||||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
|
||||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
||||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
||||||
COMPONENT library
|
|
||||||
)
|
|
||||||
|
|
||||||
file(GLOB INCLUDES Include/*.h)
|
|
||||||
install(FILES ${INCLUDES} DESTINATION
|
|
||||||
${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation)
|
|
||||||
install(FILES "$<TARGET_FILE_DIR:Detour>/Detour-d.pdb" CONFIGURATIONS "Debug" DESTINATION "lib")
|
|
@ -1,61 +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 DETOURALLOCATOR_H
|
|
||||||
#define DETOURALLOCATOR_H
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
/// Provides hint values to the memory allocator on how long the
|
|
||||||
/// memory is expected to be used.
|
|
||||||
enum dtAllocHint
|
|
||||||
{
|
|
||||||
DT_ALLOC_PERM, ///< Memory persist after a function call.
|
|
||||||
DT_ALLOC_TEMP ///< Memory used temporarily within a function.
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A memory allocation function.
|
|
||||||
// @param[in] size The size, in bytes of memory, to allocate.
|
|
||||||
// @param[in] rcAllocHint A hint to the allocator on how long the memory is expected to be in use.
|
|
||||||
// @return A pointer to the beginning of the allocated memory block, or null if the allocation failed.
|
|
||||||
/// @see dtAllocSetCustom
|
|
||||||
typedef void* (dtAllocFunc)(size_t size, dtAllocHint hint);
|
|
||||||
|
|
||||||
/// A memory deallocation function.
|
|
||||||
/// @param[in] ptr A pointer to a memory block previously allocated using #dtAllocFunc.
|
|
||||||
/// @see dtAllocSetCustom
|
|
||||||
typedef void (dtFreeFunc)(void* ptr);
|
|
||||||
|
|
||||||
/// Sets the base custom allocation functions to be used by Detour.
|
|
||||||
/// @param[in] allocFunc The memory allocation function to be used by #dtAlloc
|
|
||||||
/// @param[in] freeFunc The memory de-allocation function to be used by #dtFree
|
|
||||||
void dtAllocSetCustom(dtAllocFunc *allocFunc, dtFreeFunc *freeFunc);
|
|
||||||
|
|
||||||
/// Allocates a memory block.
|
|
||||||
/// @param[in] size The size, in bytes of memory, to allocate.
|
|
||||||
/// @param[in] hint A hint to the allocator on how long the memory is expected to be in use.
|
|
||||||
/// @return A pointer to the beginning of the allocated memory block, or null if the allocation failed.
|
|
||||||
/// @see dtFree
|
|
||||||
void* dtAlloc(size_t size, dtAllocHint hint);
|
|
||||||
|
|
||||||
/// Deallocates a memory block.
|
|
||||||
/// @param[in] ptr A pointer to a memory block previously allocated using #dtAlloc.
|
|
||||||
/// @see dtAlloc
|
|
||||||
void dtFree(void* ptr);
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,56 +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 DETOURASSERT_H
|
|
||||||
#define DETOURASSERT_H
|
|
||||||
|
|
||||||
// Note: This header file's only purpose is to include define assert.
|
|
||||||
// Feel free to change the file and include your own implementation instead.
|
|
||||||
|
|
||||||
#ifdef NDEBUG
|
|
||||||
|
|
||||||
// From http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/
|
|
||||||
# define dtAssert(x) do { (void)sizeof(x); } while((void)(__LINE__==-1),false)
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
/// An assertion failure function.
|
|
||||||
// @param[in] expression asserted expression.
|
|
||||||
// @param[in] file Filename of the failed assertion.
|
|
||||||
// @param[in] line Line number of the failed assertion.
|
|
||||||
/// @see dtAssertFailSetCustom
|
|
||||||
typedef void (dtAssertFailFunc)(const char* expression, const char* file, int line);
|
|
||||||
|
|
||||||
/// Sets the base custom assertion failure function to be used by Detour.
|
|
||||||
/// @param[in] assertFailFunc The function to be invoked in case of failure of #dtAssert
|
|
||||||
void dtAssertFailSetCustom(dtAssertFailFunc *assertFailFunc);
|
|
||||||
|
|
||||||
/// Gets the base custom assertion failure function to be used by Detour.
|
|
||||||
dtAssertFailFunc* dtAssertFailGetCustom();
|
|
||||||
|
|
||||||
# include <assert.h>
|
|
||||||
# define dtAssert(expression) \
|
|
||||||
{ \
|
|
||||||
dtAssertFailFunc* failFunc = dtAssertFailGetCustom(); \
|
|
||||||
if(failFunc == NULL) { assert(expression); } \
|
|
||||||
else if(!(expression)) { (*failFunc)(#expression, __FILE__, __LINE__); } \
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // DETOURASSERT_H
|
|
@ -1,572 +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 DETOURCOMMON_H
|
|
||||||
#define DETOURCOMMON_H
|
|
||||||
|
|
||||||
#include "DetourMath.h"
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
@defgroup detour Detour
|
|
||||||
|
|
||||||
Members in this module are used to create, manipulate, and query navigation
|
|
||||||
meshes.
|
|
||||||
|
|
||||||
@note This is a summary list of members. Use the index or search
|
|
||||||
feature to find minor members.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/// @name General helper functions
|
|
||||||
/// @{
|
|
||||||
|
|
||||||
/// Used to ignore a function parameter. VS complains about unused parameters
|
|
||||||
/// and this silences the warning.
|
|
||||||
/// @param [in] _ Unused parameter
|
|
||||||
template<class T> void dtIgnoreUnused(const T&) { }
|
|
||||||
|
|
||||||
/// Swaps the values of the two parameters.
|
|
||||||
/// @param[in,out] a Value A
|
|
||||||
/// @param[in,out] b Value B
|
|
||||||
template<class T> inline void dtSwap(T& a, T& b) { T t = a; a = b; b = t; }
|
|
||||||
|
|
||||||
/// Returns the minimum of two values.
|
|
||||||
/// @param[in] a Value A
|
|
||||||
/// @param[in] b Value B
|
|
||||||
/// @return The minimum of the two values.
|
|
||||||
template<class T> inline T dtMin(T a, T b) { return a < b ? a : b; }
|
|
||||||
|
|
||||||
/// Returns the maximum of two values.
|
|
||||||
/// @param[in] a Value A
|
|
||||||
/// @param[in] b Value B
|
|
||||||
/// @return The maximum of the two values.
|
|
||||||
template<class T> inline T dtMax(T a, T b) { return a > b ? a : b; }
|
|
||||||
|
|
||||||
/// Returns the absolute value.
|
|
||||||
/// @param[in] a The value.
|
|
||||||
/// @return The absolute value of the specified value.
|
|
||||||
template<class T> inline T dtAbs(T a) { return a < 0 ? -a : a; }
|
|
||||||
|
|
||||||
/// Returns the square of the value.
|
|
||||||
/// @param[in] a The value.
|
|
||||||
/// @return The square of the value.
|
|
||||||
template<class T> inline T dtSqr(T a) { return a*a; }
|
|
||||||
|
|
||||||
/// Clamps the value to the specified range.
|
|
||||||
/// @param[in] v The value to clamp.
|
|
||||||
/// @param[in] mn The minimum permitted return value.
|
|
||||||
/// @param[in] mx The maximum permitted return value.
|
|
||||||
/// @return The value, clamped to the specified range.
|
|
||||||
template<class T> inline T dtClamp(T v, T mn, T mx) { return v < mn ? mn : (v > mx ? mx : v); }
|
|
||||||
|
|
||||||
/// @}
|
|
||||||
/// @name Vector helper functions.
|
|
||||||
/// @{
|
|
||||||
|
|
||||||
/// Derives the cross product of two vectors. (@p v1 x @p v2)
|
|
||||||
/// @param[out] dest The cross product. [(x, y, z)]
|
|
||||||
/// @param[in] v1 A Vector [(x, y, z)]
|
|
||||||
/// @param[in] v2 A vector [(x, y, z)]
|
|
||||||
inline void dtVcross(float* dest, const float* v1, const float* v2)
|
|
||||||
{
|
|
||||||
dest[0] = v1[1]*v2[2] - v1[2]*v2[1];
|
|
||||||
dest[1] = v1[2]*v2[0] - v1[0]*v2[2];
|
|
||||||
dest[2] = v1[0]*v2[1] - v1[1]*v2[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Derives the dot product of two vectors. (@p v1 . @p v2)
|
|
||||||
/// @param[in] v1 A Vector [(x, y, z)]
|
|
||||||
/// @param[in] v2 A vector [(x, y, z)]
|
|
||||||
/// @return The dot product.
|
|
||||||
inline float dtVdot(const float* v1, const float* v2)
|
|
||||||
{
|
|
||||||
return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Performs a scaled vector addition. (@p v1 + (@p v2 * @p s))
|
|
||||||
/// @param[out] dest The result vector. [(x, y, z)]
|
|
||||||
/// @param[in] v1 The base vector. [(x, y, z)]
|
|
||||||
/// @param[in] v2 The vector to scale and add to @p v1. [(x, y, z)]
|
|
||||||
/// @param[in] s The amount to scale @p v2 by before adding to @p v1.
|
|
||||||
inline void dtVmad(float* dest, const float* v1, const float* v2, const float s)
|
|
||||||
{
|
|
||||||
dest[0] = v1[0]+v2[0]*s;
|
|
||||||
dest[1] = v1[1]+v2[1]*s;
|
|
||||||
dest[2] = v1[2]+v2[2]*s;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Performs a linear interpolation between two vectors. (@p v1 toward @p v2)
|
|
||||||
/// @param[out] dest The result vector. [(x, y, x)]
|
|
||||||
/// @param[in] v1 The starting vector.
|
|
||||||
/// @param[in] v2 The destination vector.
|
|
||||||
/// @param[in] t The interpolation factor. [Limits: 0 <= value <= 1.0]
|
|
||||||
inline void dtVlerp(float* dest, const float* v1, const float* v2, const float t)
|
|
||||||
{
|
|
||||||
dest[0] = v1[0]+(v2[0]-v1[0])*t;
|
|
||||||
dest[1] = v1[1]+(v2[1]-v1[1])*t;
|
|
||||||
dest[2] = v1[2]+(v2[2]-v1[2])*t;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Performs a vector addition. (@p v1 + @p v2)
|
|
||||||
/// @param[out] dest The result vector. [(x, y, z)]
|
|
||||||
/// @param[in] v1 The base vector. [(x, y, z)]
|
|
||||||
/// @param[in] v2 The vector to add to @p v1. [(x, y, z)]
|
|
||||||
inline void dtVadd(float* dest, const float* v1, const float* v2)
|
|
||||||
{
|
|
||||||
dest[0] = v1[0]+v2[0];
|
|
||||||
dest[1] = v1[1]+v2[1];
|
|
||||||
dest[2] = v1[2]+v2[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Performs a vector subtraction. (@p v1 - @p v2)
|
|
||||||
/// @param[out] dest The result vector. [(x, y, z)]
|
|
||||||
/// @param[in] v1 The base vector. [(x, y, z)]
|
|
||||||
/// @param[in] v2 The vector to subtract from @p v1. [(x, y, z)]
|
|
||||||
inline void dtVsub(float* dest, const float* v1, const float* v2)
|
|
||||||
{
|
|
||||||
dest[0] = v1[0]-v2[0];
|
|
||||||
dest[1] = v1[1]-v2[1];
|
|
||||||
dest[2] = v1[2]-v2[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Scales the vector by the specified value. (@p v * @p t)
|
|
||||||
/// @param[out] dest The result vector. [(x, y, z)]
|
|
||||||
/// @param[in] v The vector to scale. [(x, y, z)]
|
|
||||||
/// @param[in] t The scaling factor.
|
|
||||||
inline void dtVscale(float* dest, const float* v, const float t)
|
|
||||||
{
|
|
||||||
dest[0] = v[0]*t;
|
|
||||||
dest[1] = v[1]*t;
|
|
||||||
dest[2] = v[2]*t;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Selects the minimum value of each element from the specified vectors.
|
|
||||||
/// @param[in,out] mn A vector. (Will be updated with the result.) [(x, y, z)]
|
|
||||||
/// @param[in] v A vector. [(x, y, z)]
|
|
||||||
inline void dtVmin(float* mn, const float* v)
|
|
||||||
{
|
|
||||||
mn[0] = dtMin(mn[0], v[0]);
|
|
||||||
mn[1] = dtMin(mn[1], v[1]);
|
|
||||||
mn[2] = dtMin(mn[2], v[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Selects the maximum value of each element from the specified vectors.
|
|
||||||
/// @param[in,out] mx A vector. (Will be updated with the result.) [(x, y, z)]
|
|
||||||
/// @param[in] v A vector. [(x, y, z)]
|
|
||||||
inline void dtVmax(float* mx, const float* v)
|
|
||||||
{
|
|
||||||
mx[0] = dtMax(mx[0], v[0]);
|
|
||||||
mx[1] = dtMax(mx[1], v[1]);
|
|
||||||
mx[2] = dtMax(mx[2], v[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the vector elements to the specified values.
|
|
||||||
/// @param[out] dest The result vector. [(x, y, z)]
|
|
||||||
/// @param[in] x The x-value of the vector.
|
|
||||||
/// @param[in] y The y-value of the vector.
|
|
||||||
/// @param[in] z The z-value of the vector.
|
|
||||||
inline void dtVset(float* dest, const float x, const float y, const float z)
|
|
||||||
{
|
|
||||||
dest[0] = x; dest[1] = y; dest[2] = z;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Performs a vector copy.
|
|
||||||
/// @param[out] dest The result. [(x, y, z)]
|
|
||||||
/// @param[in] a The vector to copy. [(x, y, z)]
|
|
||||||
inline void dtVcopy(float* dest, const float* a)
|
|
||||||
{
|
|
||||||
dest[0] = a[0];
|
|
||||||
dest[1] = a[1];
|
|
||||||
dest[2] = a[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Derives the scalar length of the vector.
|
|
||||||
/// @param[in] v The vector. [(x, y, z)]
|
|
||||||
/// @return The scalar length of the vector.
|
|
||||||
inline float dtVlen(const float* v)
|
|
||||||
{
|
|
||||||
return dtMathSqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Derives the square of the scalar length of the vector. (len * len)
|
|
||||||
/// @param[in] v The vector. [(x, y, z)]
|
|
||||||
/// @return The square of the scalar length of the vector.
|
|
||||||
inline float dtVlenSqr(const float* v)
|
|
||||||
{
|
|
||||||
return v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the distance between two points.
|
|
||||||
/// @param[in] v1 A point. [(x, y, z)]
|
|
||||||
/// @param[in] v2 A point. [(x, y, z)]
|
|
||||||
/// @return The distance between the two points.
|
|
||||||
inline float dtVdist(const float* v1, const float* v2)
|
|
||||||
{
|
|
||||||
const float dx = v2[0] - v1[0];
|
|
||||||
const float dy = v2[1] - v1[1];
|
|
||||||
const float dz = v2[2] - v1[2];
|
|
||||||
return dtMathSqrtf(dx*dx + dy*dy + dz*dz);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the square of the distance between two points.
|
|
||||||
/// @param[in] v1 A point. [(x, y, z)]
|
|
||||||
/// @param[in] v2 A point. [(x, y, z)]
|
|
||||||
/// @return The square of the distance between the two points.
|
|
||||||
inline float dtVdistSqr(const float* v1, const float* v2)
|
|
||||||
{
|
|
||||||
const float dx = v2[0] - v1[0];
|
|
||||||
const float dy = v2[1] - v1[1];
|
|
||||||
const float dz = v2[2] - v1[2];
|
|
||||||
return dx*dx + dy*dy + dz*dz;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Derives the distance between the specified points on the xz-plane.
|
|
||||||
/// @param[in] v1 A point. [(x, y, z)]
|
|
||||||
/// @param[in] v2 A point. [(x, y, z)]
|
|
||||||
/// @return The distance between the point on the xz-plane.
|
|
||||||
///
|
|
||||||
/// The vectors are projected onto the xz-plane, so the y-values are ignored.
|
|
||||||
inline float dtVdist2D(const float* v1, const float* v2)
|
|
||||||
{
|
|
||||||
const float dx = v2[0] - v1[0];
|
|
||||||
const float dz = v2[2] - v1[2];
|
|
||||||
return dtMathSqrtf(dx*dx + dz*dz);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Derives the square of the distance between the specified points on the xz-plane.
|
|
||||||
/// @param[in] v1 A point. [(x, y, z)]
|
|
||||||
/// @param[in] v2 A point. [(x, y, z)]
|
|
||||||
/// @return The square of the distance between the point on the xz-plane.
|
|
||||||
inline float dtVdist2DSqr(const float* v1, const float* v2)
|
|
||||||
{
|
|
||||||
const float dx = v2[0] - v1[0];
|
|
||||||
const float dz = v2[2] - v1[2];
|
|
||||||
return dx*dx + dz*dz;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Normalizes the vector.
|
|
||||||
/// @param[in,out] v The vector to normalize. [(x, y, z)]
|
|
||||||
inline void dtVnormalize(float* v)
|
|
||||||
{
|
|
||||||
float d = 1.0f / dtMathSqrtf(dtSqr(v[0]) + dtSqr(v[1]) + dtSqr(v[2]));
|
|
||||||
v[0] *= d;
|
|
||||||
v[1] *= d;
|
|
||||||
v[2] *= d;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Performs a 'sloppy' colocation check of the specified points.
|
|
||||||
/// @param[in] p0 A point. [(x, y, z)]
|
|
||||||
/// @param[in] p1 A point. [(x, y, z)]
|
|
||||||
/// @return True if the points are considered to be at the same location.
|
|
||||||
///
|
|
||||||
/// Basically, this function will return true if the specified points are
|
|
||||||
/// close enough to eachother to be considered colocated.
|
|
||||||
inline bool dtVequal(const float* p0, const float* p1)
|
|
||||||
{
|
|
||||||
static const float thr = dtSqr(1.0f/16384.0f);
|
|
||||||
const float d = dtVdistSqr(p0, p1);
|
|
||||||
return d < thr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks that the specified vector's components are all finite.
|
|
||||||
/// @param[in] v A point. [(x, y, z)]
|
|
||||||
/// @return True if all of the point's components are finite, i.e. not NaN
|
|
||||||
/// or any of the infinities.
|
|
||||||
inline bool dtVisfinite(const float* v)
|
|
||||||
{
|
|
||||||
bool result =
|
|
||||||
dtMathIsfinite(v[0]) &&
|
|
||||||
dtMathIsfinite(v[1]) &&
|
|
||||||
dtMathIsfinite(v[2]);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks that the specified vector's 2D components are finite.
|
|
||||||
/// @param[in] v A point. [(x, y, z)]
|
|
||||||
inline bool dtVisfinite2D(const float* v)
|
|
||||||
{
|
|
||||||
bool result = dtMathIsfinite(v[0]) && dtMathIsfinite(v[2]);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Derives the dot product of two vectors on the xz-plane. (@p u . @p v)
|
|
||||||
/// @param[in] u A vector [(x, y, z)]
|
|
||||||
/// @param[in] v A vector [(x, y, z)]
|
|
||||||
/// @return The dot product on the xz-plane.
|
|
||||||
///
|
|
||||||
/// The vectors are projected onto the xz-plane, so the y-values are ignored.
|
|
||||||
inline float dtVdot2D(const float* u, const float* v)
|
|
||||||
{
|
|
||||||
return u[0]*v[0] + u[2]*v[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Derives the xz-plane 2D perp product of the two vectors. (uz*vx - ux*vz)
|
|
||||||
/// @param[in] u The LHV vector [(x, y, z)]
|
|
||||||
/// @param[in] v The RHV vector [(x, y, z)]
|
|
||||||
/// @return The dot product on the xz-plane.
|
|
||||||
///
|
|
||||||
/// The vectors are projected onto the xz-plane, so the y-values are ignored.
|
|
||||||
inline float dtVperp2D(const float* u, const float* v)
|
|
||||||
{
|
|
||||||
return u[2]*v[0] - u[0]*v[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @}
|
|
||||||
/// @name Computational geometry helper functions.
|
|
||||||
/// @{
|
|
||||||
|
|
||||||
/// Derives the signed xz-plane area of the triangle ABC, or the relationship of line AB to point C.
|
|
||||||
/// @param[in] a Vertex A. [(x, y, z)]
|
|
||||||
/// @param[in] b Vertex B. [(x, y, z)]
|
|
||||||
/// @param[in] c Vertex C. [(x, y, z)]
|
|
||||||
/// @return The signed xz-plane area of the triangle.
|
|
||||||
inline float dtTriArea2D(const float* a, const float* b, const float* c)
|
|
||||||
{
|
|
||||||
const float abx = b[0] - a[0];
|
|
||||||
const float abz = b[2] - a[2];
|
|
||||||
const float acx = c[0] - a[0];
|
|
||||||
const float acz = c[2] - a[2];
|
|
||||||
return acx*abz - abx*acz;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Determines if two axis-aligned bounding boxes overlap.
|
|
||||||
/// @param[in] amin Minimum bounds of box A. [(x, y, z)]
|
|
||||||
/// @param[in] amax Maximum bounds of box A. [(x, y, z)]
|
|
||||||
/// @param[in] bmin Minimum bounds of box B. [(x, y, z)]
|
|
||||||
/// @param[in] bmax Maximum bounds of box B. [(x, y, z)]
|
|
||||||
/// @return True if the two AABB's overlap.
|
|
||||||
/// @see dtOverlapBounds
|
|
||||||
inline bool dtOverlapQuantBounds(const unsigned short amin[3], const unsigned short amax[3],
|
|
||||||
const unsigned short bmin[3], const unsigned short bmax[3])
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Determines if two axis-aligned bounding boxes overlap.
|
|
||||||
/// @param[in] amin Minimum bounds of box A. [(x, y, z)]
|
|
||||||
/// @param[in] amax Maximum bounds of box A. [(x, y, z)]
|
|
||||||
/// @param[in] bmin Minimum bounds of box B. [(x, y, z)]
|
|
||||||
/// @param[in] bmax Maximum bounds of box B. [(x, y, z)]
|
|
||||||
/// @return True if the two AABB's overlap.
|
|
||||||
/// @see dtOverlapQuantBounds
|
|
||||||
inline bool dtOverlapBounds(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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Derives the closest point on a triangle from the specified reference point.
|
|
||||||
/// @param[out] closest The closest point on the triangle.
|
|
||||||
/// @param[in] p The reference point from which to test. [(x, y, z)]
|
|
||||||
/// @param[in] a Vertex A of triangle ABC. [(x, y, z)]
|
|
||||||
/// @param[in] b Vertex B of triangle ABC. [(x, y, z)]
|
|
||||||
/// @param[in] c Vertex C of triangle ABC. [(x, y, z)]
|
|
||||||
void dtClosestPtPointTriangle(float* closest, const float* p,
|
|
||||||
const float* a, const float* b, const float* c);
|
|
||||||
|
|
||||||
/// Derives the y-axis height of the closest point on the triangle from the specified reference point.
|
|
||||||
/// @param[in] p The reference point from which to test. [(x, y, z)]
|
|
||||||
/// @param[in] a Vertex A of triangle ABC. [(x, y, z)]
|
|
||||||
/// @param[in] b Vertex B of triangle ABC. [(x, y, z)]
|
|
||||||
/// @param[in] c Vertex C of triangle ABC. [(x, y, z)]
|
|
||||||
/// @param[out] h The resulting height.
|
|
||||||
bool dtClosestHeightPointTriangle(const float* p, const float* a, const float* b, const float* c, float& h);
|
|
||||||
|
|
||||||
bool dtIntersectSegmentPoly2D(const float* p0, const float* p1,
|
|
||||||
const float* verts, int nverts,
|
|
||||||
float& tmin, float& tmax,
|
|
||||||
int& segMin, int& segMax);
|
|
||||||
|
|
||||||
bool dtIntersectSegSeg2D(const float* ap, const float* aq,
|
|
||||||
const float* bp, const float* bq,
|
|
||||||
float& s, float& t);
|
|
||||||
|
|
||||||
/// Determines if the specified point is inside the convex polygon on the xz-plane.
|
|
||||||
/// @param[in] pt The point to check. [(x, y, z)]
|
|
||||||
/// @param[in] verts The polygon vertices. [(x, y, z) * @p nverts]
|
|
||||||
/// @param[in] nverts The number of vertices. [Limit: >= 3]
|
|
||||||
/// @return True if the point is inside the polygon.
|
|
||||||
bool dtPointInPolygon(const float* pt, const float* verts, const int nverts);
|
|
||||||
|
|
||||||
bool dtDistancePtPolyEdgesSqr(const float* pt, const float* verts, const int nverts,
|
|
||||||
float* ed, float* et);
|
|
||||||
|
|
||||||
float dtDistancePtSegSqr2D(const float* pt, const float* p, const float* q, float& t);
|
|
||||||
|
|
||||||
/// Derives the centroid of a convex polygon.
|
|
||||||
/// @param[out] tc The centroid of the polgyon. [(x, y, z)]
|
|
||||||
/// @param[in] idx The polygon indices. [(vertIndex) * @p nidx]
|
|
||||||
/// @param[in] nidx The number of indices in the polygon. [Limit: >= 3]
|
|
||||||
/// @param[in] verts The polygon vertices. [(x, y, z) * vertCount]
|
|
||||||
void dtCalcPolyCenter(float* tc, const unsigned short* idx, int nidx, const float* verts);
|
|
||||||
|
|
||||||
/// Determines if the two convex polygons overlap on the xz-plane.
|
|
||||||
/// @param[in] polya Polygon A vertices. [(x, y, z) * @p npolya]
|
|
||||||
/// @param[in] npolya The number of vertices in polygon A.
|
|
||||||
/// @param[in] polyb Polygon B vertices. [(x, y, z) * @p npolyb]
|
|
||||||
/// @param[in] npolyb The number of vertices in polygon B.
|
|
||||||
/// @return True if the two polygons overlap.
|
|
||||||
bool dtOverlapPolyPoly2D(const float* polya, const int npolya,
|
|
||||||
const float* polyb, const int npolyb);
|
|
||||||
|
|
||||||
/// @}
|
|
||||||
/// @name Miscellanious functions.
|
|
||||||
/// @{
|
|
||||||
|
|
||||||
inline unsigned int dtNextPow2(unsigned int v)
|
|
||||||
{
|
|
||||||
v--;
|
|
||||||
v |= v >> 1;
|
|
||||||
v |= v >> 2;
|
|
||||||
v |= v >> 4;
|
|
||||||
v |= v >> 8;
|
|
||||||
v |= v >> 16;
|
|
||||||
v++;
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline unsigned int dtIlog2(unsigned int v)
|
|
||||||
{
|
|
||||||
unsigned int r;
|
|
||||||
unsigned int shift;
|
|
||||||
r = (v > 0xffff) << 4; v >>= r;
|
|
||||||
shift = (v > 0xff) << 3; v >>= shift; r |= shift;
|
|
||||||
shift = (v > 0xf) << 2; v >>= shift; r |= shift;
|
|
||||||
shift = (v > 0x3) << 1; v >>= shift; r |= shift;
|
|
||||||
r |= (v >> 1);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int dtAlign4(int x) { return (x+3) & ~3; }
|
|
||||||
|
|
||||||
inline int dtOppositeTile(int side) { return (side+4) & 0x7; }
|
|
||||||
|
|
||||||
inline void dtSwapByte(unsigned char* a, unsigned char* b)
|
|
||||||
{
|
|
||||||
unsigned char tmp = *a;
|
|
||||||
*a = *b;
|
|
||||||
*b = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void dtSwapEndian(unsigned short* v)
|
|
||||||
{
|
|
||||||
unsigned char* x = (unsigned char*)v;
|
|
||||||
dtSwapByte(x+0, x+1);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void dtSwapEndian(short* v)
|
|
||||||
{
|
|
||||||
unsigned char* x = (unsigned char*)v;
|
|
||||||
dtSwapByte(x+0, x+1);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void dtSwapEndian(unsigned int* v)
|
|
||||||
{
|
|
||||||
unsigned char* x = (unsigned char*)v;
|
|
||||||
dtSwapByte(x+0, x+3); dtSwapByte(x+1, x+2);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void dtSwapEndian(int* v)
|
|
||||||
{
|
|
||||||
unsigned char* x = (unsigned char*)v;
|
|
||||||
dtSwapByte(x+0, x+3); dtSwapByte(x+1, x+2);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void dtSwapEndian(float* v)
|
|
||||||
{
|
|
||||||
unsigned char* x = (unsigned char*)v;
|
|
||||||
dtSwapByte(x+0, x+3); dtSwapByte(x+1, x+2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtRandomPointInConvexPoly(const float* pts, const int npts, float* areas,
|
|
||||||
const float s, const float t, float* out);
|
|
||||||
|
|
||||||
template<typename TypeToRetrieveAs>
|
|
||||||
TypeToRetrieveAs* dtGetThenAdvanceBufferPointer(const unsigned char*& buffer, const size_t distanceToAdvance)
|
|
||||||
{
|
|
||||||
TypeToRetrieveAs* returnPointer = reinterpret_cast<TypeToRetrieveAs*>(buffer);
|
|
||||||
buffer += distanceToAdvance;
|
|
||||||
return returnPointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename TypeToRetrieveAs>
|
|
||||||
TypeToRetrieveAs* dtGetThenAdvanceBufferPointer(unsigned char*& buffer, const size_t distanceToAdvance)
|
|
||||||
{
|
|
||||||
TypeToRetrieveAs* returnPointer = reinterpret_cast<TypeToRetrieveAs*>(buffer);
|
|
||||||
buffer += distanceToAdvance;
|
|
||||||
return returnPointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// @}
|
|
||||||
|
|
||||||
#endif // DETOURCOMMON_H
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// This section contains detailed documentation for members that don't have
|
|
||||||
// a source file. It reduces clutter in the main section of the header.
|
|
||||||
|
|
||||||
/**
|
|
||||||
|
|
||||||
@fn float dtTriArea2D(const float* a, const float* b, const float* c)
|
|
||||||
@par
|
|
||||||
|
|
||||||
The vertices are projected onto the xz-plane, so the y-values are ignored.
|
|
||||||
|
|
||||||
This is a low cost function than can be used for various purposes. Its main purpose
|
|
||||||
is for point/line relationship testing.
|
|
||||||
|
|
||||||
In all cases: A value of zero indicates that all vertices are collinear or represent the same point.
|
|
||||||
(On the xz-plane.)
|
|
||||||
|
|
||||||
When used for point/line relationship tests, AB usually represents a line against which
|
|
||||||
the C point is to be tested. In this case:
|
|
||||||
|
|
||||||
A positive value indicates that point C is to the left of line AB, looking from A toward B.<br/>
|
|
||||||
A negative value indicates that point C is to the right of lineAB, looking from A toward B.
|
|
||||||
|
|
||||||
When used for evaluating a triangle:
|
|
||||||
|
|
||||||
The absolute value of the return value is two times the area of the triangle when it is
|
|
||||||
projected onto the xz-plane.
|
|
||||||
|
|
||||||
A positive return value indicates:
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li>The vertices are wrapped in the normal Detour wrap direction.</li>
|
|
||||||
<li>The triangle's 3D face normal is in the general up direction.</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
A negative return value indicates:
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li>The vertices are reverse wrapped. (Wrapped opposite the normal Detour wrap direction.)</li>
|
|
||||||
<li>The triangle's 3D face normal is in the general down direction.</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
*/
|
|
@ -1,24 +0,0 @@
|
|||||||
/**
|
|
||||||
@defgroup detour Detour
|
|
||||||
|
|
||||||
Members in this module are wrappers around the standard math library
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef DETOURMATH_H
|
|
||||||
#define DETOURMATH_H
|
|
||||||
|
|
||||||
#include <math.h>
|
|
||||||
// This include is required because libstdc++ has problems with isfinite
|
|
||||||
// if cmath is included before math.h.
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
inline float dtMathFabsf(float x) { return fabsf(x); }
|
|
||||||
inline float dtMathSqrtf(float x) { return sqrtf(x); }
|
|
||||||
inline float dtMathFloorf(float x) { return floorf(x); }
|
|
||||||
inline float dtMathCeilf(float x) { return ceilf(x); }
|
|
||||||
inline float dtMathCosf(float x) { return cosf(x); }
|
|
||||||
inline float dtMathSinf(float x) { return sinf(x); }
|
|
||||||
inline float dtMathAtan2f(float y, float x) { return atan2f(y, x); }
|
|
||||||
inline bool dtMathIsfinite(float x) { return std::isfinite(x); }
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,785 +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 DETOURNAVMESH_H
|
|
||||||
#define DETOURNAVMESH_H
|
|
||||||
|
|
||||||
#include "DetourAlloc.h"
|
|
||||||
#include "DetourStatus.h"
|
|
||||||
|
|
||||||
// Undefine (or define in a build cofnig) the following line to use 64bit polyref.
|
|
||||||
// Generally not needed, useful for very large worlds.
|
|
||||||
// Note: tiles build using 32bit refs are not compatible with 64bit refs!
|
|
||||||
//#define DT_POLYREF64 1
|
|
||||||
|
|
||||||
#ifdef DT_POLYREF64
|
|
||||||
// TODO: figure out a multiplatform version of uint64_t
|
|
||||||
// - maybe: https://code.google.com/p/msinttypes/
|
|
||||||
// - or: http://www.azillionmonkeys.com/qed/pstdint.h
|
|
||||||
#include <stdint.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Note: If you want to use 64-bit refs, change the types of both dtPolyRef & dtTileRef.
|
|
||||||
// It is also recommended that you change dtHashRef() to a proper 64-bit hash.
|
|
||||||
|
|
||||||
/// A handle to a polygon within a navigation mesh tile.
|
|
||||||
/// @ingroup detour
|
|
||||||
#ifdef DT_POLYREF64
|
|
||||||
static const unsigned int DT_SALT_BITS = 16;
|
|
||||||
static const unsigned int DT_TILE_BITS = 28;
|
|
||||||
static const unsigned int DT_POLY_BITS = 20;
|
|
||||||
typedef uint64_t dtPolyRef;
|
|
||||||
#else
|
|
||||||
typedef unsigned int dtPolyRef;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// A handle to a tile within a navigation mesh.
|
|
||||||
/// @ingroup detour
|
|
||||||
#ifdef DT_POLYREF64
|
|
||||||
typedef uint64_t dtTileRef;
|
|
||||||
#else
|
|
||||||
typedef unsigned int dtTileRef;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// The maximum number of vertices per navigation polygon.
|
|
||||||
/// @ingroup detour
|
|
||||||
static const int DT_VERTS_PER_POLYGON = 6;
|
|
||||||
|
|
||||||
/// @{
|
|
||||||
/// @name Tile Serialization Constants
|
|
||||||
/// These constants are used to detect whether a navigation tile's data
|
|
||||||
/// and state format is compatible with the current build.
|
|
||||||
///
|
|
||||||
|
|
||||||
/// A magic number used to detect compatibility of navigation tile data.
|
|
||||||
static const int DT_NAVMESH_MAGIC = 'D'<<24 | 'N'<<16 | 'A'<<8 | 'V';
|
|
||||||
|
|
||||||
/// A version number used to detect compatibility of navigation tile data.
|
|
||||||
static const int DT_NAVMESH_VERSION = 7;
|
|
||||||
|
|
||||||
/// A magic number used to detect the compatibility of navigation tile states.
|
|
||||||
static const int DT_NAVMESH_STATE_MAGIC = 'D'<<24 | 'N'<<16 | 'M'<<8 | 'S';
|
|
||||||
|
|
||||||
/// A version number used to detect compatibility of navigation tile states.
|
|
||||||
static const int DT_NAVMESH_STATE_VERSION = 1;
|
|
||||||
|
|
||||||
/// @}
|
|
||||||
|
|
||||||
/// A flag that indicates that an entity links to an external entity.
|
|
||||||
/// (E.g. A polygon edge is a portal that links to another polygon.)
|
|
||||||
static const unsigned short DT_EXT_LINK = 0x8000;
|
|
||||||
|
|
||||||
/// A value that indicates the entity does not link to anything.
|
|
||||||
static const unsigned int DT_NULL_LINK = 0xffffffff;
|
|
||||||
|
|
||||||
/// A flag that indicates that an off-mesh connection can be traversed in both directions. (Is bidirectional.)
|
|
||||||
static const unsigned int DT_OFFMESH_CON_BIDIR = 1;
|
|
||||||
|
|
||||||
/// The maximum number of user defined area ids.
|
|
||||||
/// @ingroup detour
|
|
||||||
static const int DT_MAX_AREAS = 64;
|
|
||||||
|
|
||||||
/// Tile flags used for various functions and fields.
|
|
||||||
/// For an example, see dtNavMesh::addTile().
|
|
||||||
enum dtTileFlags
|
|
||||||
{
|
|
||||||
/// The navigation mesh owns the tile memory and is responsible for freeing it.
|
|
||||||
DT_TILE_FREE_DATA = 0x01,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Vertex flags returned by dtNavMeshQuery::findStraightPath.
|
|
||||||
enum dtStraightPathFlags
|
|
||||||
{
|
|
||||||
DT_STRAIGHTPATH_START = 0x01, ///< The vertex is the start position in the path.
|
|
||||||
DT_STRAIGHTPATH_END = 0x02, ///< The vertex is the end position in the path.
|
|
||||||
DT_STRAIGHTPATH_OFFMESH_CONNECTION = 0x04, ///< The vertex is the start of an off-mesh connection.
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Options for dtNavMeshQuery::findStraightPath.
|
|
||||||
enum dtStraightPathOptions
|
|
||||||
{
|
|
||||||
DT_STRAIGHTPATH_AREA_CROSSINGS = 0x01, ///< Add a vertex at every polygon edge crossing where area changes.
|
|
||||||
DT_STRAIGHTPATH_ALL_CROSSINGS = 0x02, ///< Add a vertex at every polygon edge crossing.
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// Options for dtNavMeshQuery::initSlicedFindPath and updateSlicedFindPath
|
|
||||||
enum dtFindPathOptions
|
|
||||||
{
|
|
||||||
DT_FINDPATH_ANY_ANGLE = 0x02, ///< use raycasts during pathfind to "shortcut" (raycast still consider costs)
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Options for dtNavMeshQuery::raycast
|
|
||||||
enum dtRaycastOptions
|
|
||||||
{
|
|
||||||
DT_RAYCAST_USE_COSTS = 0x01, ///< Raycast should calculate movement cost along the ray and fill RaycastHit::cost
|
|
||||||
};
|
|
||||||
|
|
||||||
enum dtDetailTriEdgeFlags
|
|
||||||
{
|
|
||||||
DT_DETAIL_EDGE_BOUNDARY = 0x01, ///< Detail triangle edge is part of the poly boundary
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// Limit raycasting during any angle pahfinding
|
|
||||||
/// The limit is given as a multiple of the character radius
|
|
||||||
static const float DT_RAY_CAST_LIMIT_PROPORTIONS = 50.0f;
|
|
||||||
|
|
||||||
/// Flags representing the type of a navigation mesh polygon.
|
|
||||||
enum dtPolyTypes
|
|
||||||
{
|
|
||||||
/// The polygon is a standard convex polygon that is part of the surface of the mesh.
|
|
||||||
DT_POLYTYPE_GROUND = 0,
|
|
||||||
/// The polygon is an off-mesh connection consisting of two vertices.
|
|
||||||
DT_POLYTYPE_OFFMESH_CONNECTION = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// Defines a polygon within a dtMeshTile object.
|
|
||||||
/// @ingroup detour
|
|
||||||
struct dtPoly
|
|
||||||
{
|
|
||||||
/// Index to first link in linked list. (Or #DT_NULL_LINK if there is no link.)
|
|
||||||
unsigned int firstLink;
|
|
||||||
|
|
||||||
/// The indices of the polygon's vertices.
|
|
||||||
/// The actual vertices are located in dtMeshTile::verts.
|
|
||||||
unsigned short verts[DT_VERTS_PER_POLYGON];
|
|
||||||
|
|
||||||
/// Packed data representing neighbor polygons references and flags for each edge.
|
|
||||||
unsigned short neis[DT_VERTS_PER_POLYGON];
|
|
||||||
|
|
||||||
/// The user defined polygon flags.
|
|
||||||
unsigned short flags;
|
|
||||||
|
|
||||||
/// The number of vertices in the polygon.
|
|
||||||
unsigned char vertCount;
|
|
||||||
|
|
||||||
/// The bit packed area id and polygon type.
|
|
||||||
/// @note Use the structure's set and get methods to acess this value.
|
|
||||||
unsigned char areaAndtype;
|
|
||||||
|
|
||||||
/// Sets the user defined area id. [Limit: < #DT_MAX_AREAS]
|
|
||||||
inline void setArea(unsigned char a) { areaAndtype = (areaAndtype & 0xc0) | (a & 0x3f); }
|
|
||||||
|
|
||||||
/// Sets the polygon type. (See: #dtPolyTypes.)
|
|
||||||
inline void setType(unsigned char t) { areaAndtype = (areaAndtype & 0x3f) | (t << 6); }
|
|
||||||
|
|
||||||
/// Gets the user defined area id.
|
|
||||||
inline unsigned char getArea() const { return areaAndtype & 0x3f; }
|
|
||||||
|
|
||||||
/// Gets the polygon type. (See: #dtPolyTypes)
|
|
||||||
inline unsigned char getType() const { return areaAndtype >> 6; }
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Defines the location of detail sub-mesh data within a dtMeshTile.
|
|
||||||
struct dtPolyDetail
|
|
||||||
{
|
|
||||||
unsigned int vertBase; ///< The offset of the vertices in the dtMeshTile::detailVerts array.
|
|
||||||
unsigned int triBase; ///< The offset of the triangles in the dtMeshTile::detailTris array.
|
|
||||||
unsigned char vertCount; ///< The number of vertices in the sub-mesh.
|
|
||||||
unsigned char triCount; ///< The number of triangles in the sub-mesh.
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Defines a link between polygons.
|
|
||||||
/// @note This structure is rarely if ever used by the end user.
|
|
||||||
/// @see dtMeshTile
|
|
||||||
struct dtLink
|
|
||||||
{
|
|
||||||
dtPolyRef ref; ///< Neighbour reference. (The neighbor that is linked to.)
|
|
||||||
unsigned int next; ///< Index of the next link.
|
|
||||||
unsigned char edge; ///< Index of the polygon edge that owns this link.
|
|
||||||
unsigned char side; ///< If a boundary link, defines on which side the link is.
|
|
||||||
unsigned char bmin; ///< If a boundary link, defines the minimum sub-edge area.
|
|
||||||
unsigned char bmax; ///< If a boundary link, defines the maximum sub-edge area.
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Bounding volume node.
|
|
||||||
/// @note This structure is rarely if ever used by the end user.
|
|
||||||
/// @see dtMeshTile
|
|
||||||
struct dtBVNode
|
|
||||||
{
|
|
||||||
unsigned short bmin[3]; ///< Minimum bounds of the node's AABB. [(x, y, z)]
|
|
||||||
unsigned short bmax[3]; ///< Maximum bounds of the node's AABB. [(x, y, z)]
|
|
||||||
int i; ///< The node's index. (Negative for escape sequence.)
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Defines an navigation mesh off-mesh connection within a dtMeshTile object.
|
|
||||||
/// An off-mesh connection is a user defined traversable connection made up to two vertices.
|
|
||||||
struct dtOffMeshConnection
|
|
||||||
{
|
|
||||||
/// The endpoints of the connection. [(ax, ay, az, bx, by, bz)]
|
|
||||||
float pos[6];
|
|
||||||
|
|
||||||
/// The radius of the endpoints. [Limit: >= 0]
|
|
||||||
float rad;
|
|
||||||
|
|
||||||
/// The polygon reference of the connection within the tile.
|
|
||||||
unsigned short poly;
|
|
||||||
|
|
||||||
/// Link flags.
|
|
||||||
/// @note These are not the connection's user defined flags. Those are assigned via the
|
|
||||||
/// connection's dtPoly definition. These are link flags used for internal purposes.
|
|
||||||
unsigned char flags;
|
|
||||||
|
|
||||||
/// End point side.
|
|
||||||
unsigned char side;
|
|
||||||
|
|
||||||
/// The id of the offmesh connection. (User assigned when the navigation mesh is built.)
|
|
||||||
unsigned int userId;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Provides high level information related to a dtMeshTile object.
|
|
||||||
/// @ingroup detour
|
|
||||||
struct dtMeshHeader
|
|
||||||
{
|
|
||||||
int magic; ///< Tile magic number. (Used to identify the data format.)
|
|
||||||
int version; ///< Tile data format version number.
|
|
||||||
int x; ///< The x-position of the tile within the dtNavMesh tile grid. (x, y, layer)
|
|
||||||
int y; ///< The y-position of the tile within the dtNavMesh tile grid. (x, y, layer)
|
|
||||||
int layer; ///< The layer of the tile within the dtNavMesh tile grid. (x, y, layer)
|
|
||||||
unsigned int userId; ///< The user defined id of the tile.
|
|
||||||
int polyCount; ///< The number of polygons in the tile.
|
|
||||||
int vertCount; ///< The number of vertices in the tile.
|
|
||||||
int maxLinkCount; ///< The number of allocated links.
|
|
||||||
int detailMeshCount; ///< The number of sub-meshes in the detail mesh.
|
|
||||||
|
|
||||||
/// The number of unique vertices in the detail mesh. (In addition to the polygon vertices.)
|
|
||||||
int detailVertCount;
|
|
||||||
|
|
||||||
int detailTriCount; ///< The number of triangles in the detail mesh.
|
|
||||||
int bvNodeCount; ///< The number of bounding volume nodes. (Zero if bounding volumes are disabled.)
|
|
||||||
int offMeshConCount; ///< The number of off-mesh connections.
|
|
||||||
int offMeshBase; ///< The index of the first polygon which is an off-mesh connection.
|
|
||||||
float walkableHeight; ///< The height of the agents using the tile.
|
|
||||||
float walkableRadius; ///< The radius of the agents using the tile.
|
|
||||||
float walkableClimb; ///< The maximum climb height of the agents using the tile.
|
|
||||||
float bmin[3]; ///< The minimum bounds of the tile's AABB. [(x, y, z)]
|
|
||||||
float bmax[3]; ///< The maximum bounds of the tile's AABB. [(x, y, z)]
|
|
||||||
|
|
||||||
/// The bounding volume quantization factor.
|
|
||||||
float bvQuantFactor;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Defines a navigation mesh tile.
|
|
||||||
/// @ingroup detour
|
|
||||||
struct dtMeshTile
|
|
||||||
{
|
|
||||||
unsigned int salt; ///< Counter describing modifications to the tile.
|
|
||||||
|
|
||||||
unsigned int linksFreeList; ///< Index to the next free link.
|
|
||||||
dtMeshHeader* header; ///< The tile header.
|
|
||||||
dtPoly* polys; ///< The tile polygons. [Size: dtMeshHeader::polyCount]
|
|
||||||
float* verts; ///< The tile vertices. [Size: dtMeshHeader::vertCount]
|
|
||||||
dtLink* links; ///< The tile links. [Size: dtMeshHeader::maxLinkCount]
|
|
||||||
dtPolyDetail* detailMeshes; ///< The tile's detail sub-meshes. [Size: dtMeshHeader::detailMeshCount]
|
|
||||||
|
|
||||||
/// The detail mesh's unique vertices. [(x, y, z) * dtMeshHeader::detailVertCount]
|
|
||||||
float* detailVerts;
|
|
||||||
|
|
||||||
/// The detail mesh's triangles. [(vertA, vertB, vertC, triFlags) * dtMeshHeader::detailTriCount].
|
|
||||||
/// See dtDetailTriEdgeFlags and dtGetDetailTriEdgeFlags.
|
|
||||||
unsigned char* detailTris;
|
|
||||||
|
|
||||||
/// The tile bounding volume nodes. [Size: dtMeshHeader::bvNodeCount]
|
|
||||||
/// (Will be null if bounding volumes are disabled.)
|
|
||||||
dtBVNode* bvTree;
|
|
||||||
|
|
||||||
dtOffMeshConnection* offMeshCons; ///< The tile off-mesh connections. [Size: dtMeshHeader::offMeshConCount]
|
|
||||||
|
|
||||||
unsigned char* data; ///< The tile data. (Not directly accessed under normal situations.)
|
|
||||||
int dataSize; ///< Size of the tile data.
|
|
||||||
int flags; ///< Tile flags. (See: #dtTileFlags)
|
|
||||||
dtMeshTile* next; ///< The next free tile, or the next tile in the spatial grid.
|
|
||||||
// OpenMW code - make dtMeshTile POD since R&D init it by memset
|
|
||||||
//private:
|
|
||||||
// dtMeshTile(const dtMeshTile&);
|
|
||||||
// dtMeshTile& operator=(const dtMeshTile&);
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Get flags for edge in detail triangle.
|
|
||||||
/// @param triFlags[in] The flags for the triangle (last component of detail vertices above).
|
|
||||||
/// @param edgeIndex[in] The index of the first vertex of the edge. For instance, if 0,
|
|
||||||
/// returns flags for edge AB.
|
|
||||||
inline int dtGetDetailTriEdgeFlags(unsigned char triFlags, int edgeIndex)
|
|
||||||
{
|
|
||||||
return (triFlags >> (edgeIndex * 2)) & 0x3;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Configuration parameters used to define multi-tile navigation meshes.
|
|
||||||
/// The values are used to allocate space during the initialization of a navigation mesh.
|
|
||||||
/// @see dtNavMesh::init()
|
|
||||||
/// @ingroup detour
|
|
||||||
struct dtNavMeshParams
|
|
||||||
{
|
|
||||||
float orig[3]; ///< The world space origin of the navigation mesh's tile space. [(x, y, z)]
|
|
||||||
float tileWidth; ///< The width of each tile. (Along the x-axis.)
|
|
||||||
float tileHeight; ///< The height of each tile. (Along the z-axis.)
|
|
||||||
int maxTiles; ///< The maximum number of tiles the navigation mesh can contain.
|
|
||||||
int maxPolys; ///< The maximum number of polygons each tile can contain.
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A navigation mesh based on tiles of convex polygons.
|
|
||||||
/// @ingroup detour
|
|
||||||
class dtNavMesh
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
dtNavMesh();
|
|
||||||
~dtNavMesh();
|
|
||||||
|
|
||||||
/// @{
|
|
||||||
/// @name Initialization and Tile Management
|
|
||||||
|
|
||||||
/// Initializes the navigation mesh for tiled use.
|
|
||||||
/// @param[in] params Initialization parameters.
|
|
||||||
/// @return The status flags for the operation.
|
|
||||||
dtStatus init(const dtNavMeshParams* params);
|
|
||||||
|
|
||||||
/// Initializes the navigation mesh for single tile use.
|
|
||||||
/// @param[in] data Data of the new tile. (See: #dtCreateNavMeshData)
|
|
||||||
/// @param[in] dataSize The data size of the new tile.
|
|
||||||
/// @param[in] flags The tile flags. (See: #dtTileFlags)
|
|
||||||
/// @return The status flags for the operation.
|
|
||||||
/// @see dtCreateNavMeshData
|
|
||||||
dtStatus init(unsigned char* data, const int dataSize, const int flags);
|
|
||||||
|
|
||||||
/// The navigation mesh initialization params.
|
|
||||||
const dtNavMeshParams* getParams() const;
|
|
||||||
|
|
||||||
/// Adds a tile to the navigation mesh.
|
|
||||||
/// @param[in] data Data for the new tile mesh. (See: #dtCreateNavMeshData)
|
|
||||||
/// @param[in] dataSize Data size of the new tile mesh.
|
|
||||||
/// @param[in] flags Tile flags. (See: #dtTileFlags)
|
|
||||||
/// @param[in] lastRef The desired reference for the tile. (When reloading a tile.) [opt] [Default: 0]
|
|
||||||
/// @param[out] result The tile reference. (If the tile was succesfully added.) [opt]
|
|
||||||
/// @return The status flags for the operation.
|
|
||||||
dtStatus addTile(unsigned char* data, int dataSize, int flags, dtTileRef lastRef, dtTileRef* result);
|
|
||||||
|
|
||||||
/// Removes the specified tile from the navigation mesh.
|
|
||||||
/// @param[in] ref The reference of the tile to remove.
|
|
||||||
/// @param[out] data Data associated with deleted tile.
|
|
||||||
/// @param[out] dataSize Size of the data associated with deleted tile.
|
|
||||||
/// @return The status flags for the operation.
|
|
||||||
dtStatus removeTile(dtTileRef ref, unsigned char** data, int* dataSize);
|
|
||||||
|
|
||||||
/// @}
|
|
||||||
|
|
||||||
/// @{
|
|
||||||
/// @name Query Functions
|
|
||||||
|
|
||||||
/// Calculates the tile grid location for the specified world position.
|
|
||||||
/// @param[in] pos The world position for the query. [(x, y, z)]
|
|
||||||
/// @param[out] tx The tile's x-location. (x, y)
|
|
||||||
/// @param[out] ty The tile's y-location. (x, y)
|
|
||||||
void calcTileLoc(const float* pos, int* tx, int* ty) const;
|
|
||||||
|
|
||||||
/// Gets the tile at the specified grid location.
|
|
||||||
/// @param[in] x The tile's x-location. (x, y, layer)
|
|
||||||
/// @param[in] y The tile's y-location. (x, y, layer)
|
|
||||||
/// @param[in] layer The tile's layer. (x, y, layer)
|
|
||||||
/// @return The tile, or null if the tile does not exist.
|
|
||||||
const dtMeshTile* getTileAt(const int x, const int y, const int layer) const;
|
|
||||||
|
|
||||||
/// Gets all tiles at the specified grid location. (All layers.)
|
|
||||||
/// @param[in] x The tile's x-location. (x, y)
|
|
||||||
/// @param[in] y The tile's y-location. (x, y)
|
|
||||||
/// @param[out] tiles A pointer to an array of tiles that will hold the result.
|
|
||||||
/// @param[in] maxTiles The maximum tiles the tiles parameter can hold.
|
|
||||||
/// @return The number of tiles returned in the tiles array.
|
|
||||||
int getTilesAt(const int x, const int y,
|
|
||||||
dtMeshTile const** tiles, const int maxTiles) const;
|
|
||||||
|
|
||||||
/// Gets the tile reference for the tile at specified grid location.
|
|
||||||
/// @param[in] x The tile's x-location. (x, y, layer)
|
|
||||||
/// @param[in] y The tile's y-location. (x, y, layer)
|
|
||||||
/// @param[in] layer The tile's layer. (x, y, layer)
|
|
||||||
/// @return The tile reference of the tile, or 0 if there is none.
|
|
||||||
dtTileRef getTileRefAt(int x, int y, int layer) const;
|
|
||||||
|
|
||||||
/// Gets the tile reference for the specified tile.
|
|
||||||
/// @param[in] tile The tile.
|
|
||||||
/// @return The tile reference of the tile.
|
|
||||||
dtTileRef getTileRef(const dtMeshTile* tile) const;
|
|
||||||
|
|
||||||
/// Gets the tile for the specified tile reference.
|
|
||||||
/// @param[in] ref The tile reference of the tile to retrieve.
|
|
||||||
/// @return The tile for the specified reference, or null if the
|
|
||||||
/// reference is invalid.
|
|
||||||
const dtMeshTile* getTileByRef(dtTileRef ref) const;
|
|
||||||
|
|
||||||
/// The maximum number of tiles supported by the navigation mesh.
|
|
||||||
/// @return The maximum number of tiles supported by the navigation mesh.
|
|
||||||
int getMaxTiles() const;
|
|
||||||
|
|
||||||
/// Gets the tile at the specified index.
|
|
||||||
/// @param[in] i The tile index. [Limit: 0 >= index < #getMaxTiles()]
|
|
||||||
/// @return The tile at the specified index.
|
|
||||||
const dtMeshTile* getTile(int i) const;
|
|
||||||
|
|
||||||
/// Gets the tile and polygon for the specified polygon reference.
|
|
||||||
/// @param[in] ref The reference for the a polygon.
|
|
||||||
/// @param[out] tile The tile containing the polygon.
|
|
||||||
/// @param[out] poly The polygon.
|
|
||||||
/// @return The status flags for the operation.
|
|
||||||
dtStatus getTileAndPolyByRef(const dtPolyRef ref, const dtMeshTile** tile, const dtPoly** poly) const;
|
|
||||||
|
|
||||||
/// Returns the tile and polygon for the specified polygon reference.
|
|
||||||
/// @param[in] ref A known valid reference for a polygon.
|
|
||||||
/// @param[out] tile The tile containing the polygon.
|
|
||||||
/// @param[out] poly The polygon.
|
|
||||||
void getTileAndPolyByRefUnsafe(const dtPolyRef ref, const dtMeshTile** tile, const dtPoly** poly) const;
|
|
||||||
|
|
||||||
/// Checks the validity of a polygon reference.
|
|
||||||
/// @param[in] ref The polygon reference to check.
|
|
||||||
/// @return True if polygon reference is valid for the navigation mesh.
|
|
||||||
bool isValidPolyRef(dtPolyRef ref) const;
|
|
||||||
|
|
||||||
/// Gets the polygon reference for the tile's base polygon.
|
|
||||||
/// @param[in] tile The tile.
|
|
||||||
/// @return The polygon reference for the base polygon in the specified tile.
|
|
||||||
dtPolyRef getPolyRefBase(const dtMeshTile* tile) const;
|
|
||||||
|
|
||||||
/// Gets the endpoints for an off-mesh connection, ordered by "direction of travel".
|
|
||||||
/// @param[in] prevRef The reference of the polygon before the connection.
|
|
||||||
/// @param[in] polyRef The reference of the off-mesh connection polygon.
|
|
||||||
/// @param[out] startPos The start position of the off-mesh connection. [(x, y, z)]
|
|
||||||
/// @param[out] endPos The end position of the off-mesh connection. [(x, y, z)]
|
|
||||||
/// @return The status flags for the operation.
|
|
||||||
dtStatus getOffMeshConnectionPolyEndPoints(dtPolyRef prevRef, dtPolyRef polyRef, float* startPos, float* endPos) const;
|
|
||||||
|
|
||||||
/// Gets the specified off-mesh connection.
|
|
||||||
/// @param[in] ref The polygon reference of the off-mesh connection.
|
|
||||||
/// @return The specified off-mesh connection, or null if the polygon reference is not valid.
|
|
||||||
const dtOffMeshConnection* getOffMeshConnectionByRef(dtPolyRef ref) const;
|
|
||||||
|
|
||||||
/// @}
|
|
||||||
|
|
||||||
/// @{
|
|
||||||
/// @name State Management
|
|
||||||
/// These functions do not effect #dtTileRef or #dtPolyRef's.
|
|
||||||
|
|
||||||
/// Sets the user defined flags for the specified polygon.
|
|
||||||
/// @param[in] ref The polygon reference.
|
|
||||||
/// @param[in] flags The new flags for the polygon.
|
|
||||||
/// @return The status flags for the operation.
|
|
||||||
dtStatus setPolyFlags(dtPolyRef ref, unsigned short flags);
|
|
||||||
|
|
||||||
/// Gets the user defined flags for the specified polygon.
|
|
||||||
/// @param[in] ref The polygon reference.
|
|
||||||
/// @param[out] resultFlags The polygon flags.
|
|
||||||
/// @return The status flags for the operation.
|
|
||||||
dtStatus getPolyFlags(dtPolyRef ref, unsigned short* resultFlags) const;
|
|
||||||
|
|
||||||
/// Sets the user defined area for the specified polygon.
|
|
||||||
/// @param[in] ref The polygon reference.
|
|
||||||
/// @param[in] area The new area id for the polygon. [Limit: < #DT_MAX_AREAS]
|
|
||||||
/// @return The status flags for the operation.
|
|
||||||
dtStatus setPolyArea(dtPolyRef ref, unsigned char area);
|
|
||||||
|
|
||||||
/// Gets the user defined area for the specified polygon.
|
|
||||||
/// @param[in] ref The polygon reference.
|
|
||||||
/// @param[out] resultArea The area id for the polygon.
|
|
||||||
/// @return The status flags for the operation.
|
|
||||||
dtStatus getPolyArea(dtPolyRef ref, unsigned char* resultArea) const;
|
|
||||||
|
|
||||||
/// Gets the size of the buffer required by #storeTileState to store the specified tile's state.
|
|
||||||
/// @param[in] tile The tile.
|
|
||||||
/// @return The size of the buffer required to store the state.
|
|
||||||
int getTileStateSize(const dtMeshTile* tile) const;
|
|
||||||
|
|
||||||
/// Stores the non-structural state of the tile in the specified buffer. (Flags, area ids, etc.)
|
|
||||||
/// @param[in] tile The tile.
|
|
||||||
/// @param[out] data The buffer to store the tile's state in.
|
|
||||||
/// @param[in] maxDataSize The size of the data buffer. [Limit: >= #getTileStateSize]
|
|
||||||
/// @return The status flags for the operation.
|
|
||||||
dtStatus storeTileState(const dtMeshTile* tile, unsigned char* data, const int maxDataSize) const;
|
|
||||||
|
|
||||||
/// Restores the state of the tile.
|
|
||||||
/// @param[in] tile The tile.
|
|
||||||
/// @param[in] data The new state. (Obtained from #storeTileState.)
|
|
||||||
/// @param[in] maxDataSize The size of the state within the data buffer.
|
|
||||||
/// @return The status flags for the operation.
|
|
||||||
dtStatus restoreTileState(dtMeshTile* tile, const unsigned char* data, const int maxDataSize);
|
|
||||||
|
|
||||||
/// @}
|
|
||||||
|
|
||||||
/// @{
|
|
||||||
/// @name Encoding and Decoding
|
|
||||||
/// These functions are generally meant for internal use only.
|
|
||||||
|
|
||||||
/// Derives a standard polygon reference.
|
|
||||||
/// @note This function is generally meant for internal use only.
|
|
||||||
/// @param[in] salt The tile's salt value.
|
|
||||||
/// @param[in] it The index of the tile.
|
|
||||||
/// @param[in] ip The index of the polygon within the tile.
|
|
||||||
inline dtPolyRef encodePolyId(unsigned int salt, unsigned int it, unsigned int ip) const
|
|
||||||
{
|
|
||||||
#ifdef DT_POLYREF64
|
|
||||||
return ((dtPolyRef)salt << (DT_POLY_BITS+DT_TILE_BITS)) | ((dtPolyRef)it << DT_POLY_BITS) | (dtPolyRef)ip;
|
|
||||||
#else
|
|
||||||
return ((dtPolyRef)salt << (m_polyBits+m_tileBits)) | ((dtPolyRef)it << m_polyBits) | (dtPolyRef)ip;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Decodes a standard polygon reference.
|
|
||||||
/// @note This function is generally meant for internal use only.
|
|
||||||
/// @param[in] ref The polygon reference to decode.
|
|
||||||
/// @param[out] salt The tile's salt value.
|
|
||||||
/// @param[out] it The index of the tile.
|
|
||||||
/// @param[out] ip The index of the polygon within the tile.
|
|
||||||
/// @see #encodePolyId
|
|
||||||
inline void decodePolyId(dtPolyRef ref, unsigned int& salt, unsigned int& it, unsigned int& ip) const
|
|
||||||
{
|
|
||||||
#ifdef DT_POLYREF64
|
|
||||||
const dtPolyRef saltMask = ((dtPolyRef)1<<DT_SALT_BITS)-1;
|
|
||||||
const dtPolyRef tileMask = ((dtPolyRef)1<<DT_TILE_BITS)-1;
|
|
||||||
const dtPolyRef polyMask = ((dtPolyRef)1<<DT_POLY_BITS)-1;
|
|
||||||
salt = (unsigned int)((ref >> (DT_POLY_BITS+DT_TILE_BITS)) & saltMask);
|
|
||||||
it = (unsigned int)((ref >> DT_POLY_BITS) & tileMask);
|
|
||||||
ip = (unsigned int)(ref & polyMask);
|
|
||||||
#else
|
|
||||||
const dtPolyRef saltMask = ((dtPolyRef)1<<m_saltBits)-1;
|
|
||||||
const dtPolyRef tileMask = ((dtPolyRef)1<<m_tileBits)-1;
|
|
||||||
const dtPolyRef polyMask = ((dtPolyRef)1<<m_polyBits)-1;
|
|
||||||
salt = (unsigned int)((ref >> (m_polyBits+m_tileBits)) & saltMask);
|
|
||||||
it = (unsigned int)((ref >> m_polyBits) & tileMask);
|
|
||||||
ip = (unsigned int)(ref & polyMask);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extracts a tile's salt value from the specified polygon reference.
|
|
||||||
/// @note This function is generally meant for internal use only.
|
|
||||||
/// @param[in] ref The polygon reference.
|
|
||||||
/// @see #encodePolyId
|
|
||||||
inline unsigned int decodePolyIdSalt(dtPolyRef ref) const
|
|
||||||
{
|
|
||||||
#ifdef DT_POLYREF64
|
|
||||||
const dtPolyRef saltMask = ((dtPolyRef)1<<DT_SALT_BITS)-1;
|
|
||||||
return (unsigned int)((ref >> (DT_POLY_BITS+DT_TILE_BITS)) & saltMask);
|
|
||||||
#else
|
|
||||||
const dtPolyRef saltMask = ((dtPolyRef)1<<m_saltBits)-1;
|
|
||||||
return (unsigned int)((ref >> (m_polyBits+m_tileBits)) & saltMask);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extracts the tile's index from the specified polygon reference.
|
|
||||||
/// @note This function is generally meant for internal use only.
|
|
||||||
/// @param[in] ref The polygon reference.
|
|
||||||
/// @see #encodePolyId
|
|
||||||
inline unsigned int decodePolyIdTile(dtPolyRef ref) const
|
|
||||||
{
|
|
||||||
#ifdef DT_POLYREF64
|
|
||||||
const dtPolyRef tileMask = ((dtPolyRef)1<<DT_TILE_BITS)-1;
|
|
||||||
return (unsigned int)((ref >> DT_POLY_BITS) & tileMask);
|
|
||||||
#else
|
|
||||||
const dtPolyRef tileMask = ((dtPolyRef)1<<m_tileBits)-1;
|
|
||||||
return (unsigned int)((ref >> m_polyBits) & tileMask);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extracts the polygon's index (within its tile) from the specified polygon reference.
|
|
||||||
/// @note This function is generally meant for internal use only.
|
|
||||||
/// @param[in] ref The polygon reference.
|
|
||||||
/// @see #encodePolyId
|
|
||||||
inline unsigned int decodePolyIdPoly(dtPolyRef ref) const
|
|
||||||
{
|
|
||||||
#ifdef DT_POLYREF64
|
|
||||||
const dtPolyRef polyMask = ((dtPolyRef)1<<DT_POLY_BITS)-1;
|
|
||||||
return (unsigned int)(ref & polyMask);
|
|
||||||
#else
|
|
||||||
const dtPolyRef polyMask = ((dtPolyRef)1<<m_polyBits)-1;
|
|
||||||
return (unsigned int)(ref & polyMask);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @}
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Explicitly disabled copy constructor and copy assignment operator.
|
|
||||||
dtNavMesh(const dtNavMesh&);
|
|
||||||
dtNavMesh& operator=(const dtNavMesh&);
|
|
||||||
|
|
||||||
/// Returns pointer to tile in the tile array.
|
|
||||||
dtMeshTile* getTile(int i);
|
|
||||||
|
|
||||||
/// Returns neighbour tile based on side.
|
|
||||||
int getTilesAt(const int x, const int y,
|
|
||||||
dtMeshTile** tiles, const int maxTiles) const;
|
|
||||||
|
|
||||||
/// Returns neighbour tile based on side.
|
|
||||||
int getNeighbourTilesAt(const int x, const int y, const int side,
|
|
||||||
dtMeshTile** tiles, const int maxTiles) const;
|
|
||||||
|
|
||||||
/// Returns all polygons in neighbour tile based on portal defined by the segment.
|
|
||||||
int findConnectingPolys(const float* va, const float* vb,
|
|
||||||
const dtMeshTile* tile, int side,
|
|
||||||
dtPolyRef* con, float* conarea, int maxcon) const;
|
|
||||||
|
|
||||||
/// Builds internal polygons links for a tile.
|
|
||||||
void connectIntLinks(dtMeshTile* tile);
|
|
||||||
/// Builds internal polygons links for a tile.
|
|
||||||
void baseOffMeshLinks(dtMeshTile* tile);
|
|
||||||
|
|
||||||
/// Builds external polygon links for a tile.
|
|
||||||
void connectExtLinks(dtMeshTile* tile, dtMeshTile* target, int side);
|
|
||||||
/// Builds external polygon links for a tile.
|
|
||||||
void connectExtOffMeshLinks(dtMeshTile* tile, dtMeshTile* target, int side);
|
|
||||||
|
|
||||||
/// Removes external links at specified side.
|
|
||||||
void unconnectLinks(dtMeshTile* tile, dtMeshTile* target);
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: These methods are duplicates from dtNavMeshQuery, but are needed for off-mesh connection finding.
|
|
||||||
|
|
||||||
/// Queries polygons within a tile.
|
|
||||||
int queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax,
|
|
||||||
dtPolyRef* polys, const int maxPolys) const;
|
|
||||||
/// Find nearest polygon within a tile.
|
|
||||||
dtPolyRef findNearestPolyInTile(const dtMeshTile* tile, const float* center,
|
|
||||||
const float* halfExtents, float* nearestPt) const;
|
|
||||||
/// Returns whether position is over the poly and the height at the position if so.
|
|
||||||
bool getPolyHeight(const dtMeshTile* tile, const dtPoly* poly, const float* pos, float* height) const;
|
|
||||||
/// Returns closest point on polygon.
|
|
||||||
void closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const;
|
|
||||||
|
|
||||||
dtNavMeshParams m_params; ///< Current initialization params. TODO: do not store this info twice.
|
|
||||||
float m_orig[3]; ///< Origin of the tile (0,0)
|
|
||||||
float m_tileWidth, m_tileHeight; ///< Dimensions of each tile.
|
|
||||||
int m_maxTiles; ///< Max number of tiles.
|
|
||||||
int m_tileLutSize; ///< Tile hash lookup size (must be pot).
|
|
||||||
int m_tileLutMask; ///< Tile hash lookup mask.
|
|
||||||
|
|
||||||
dtMeshTile** m_posLookup; ///< Tile hash lookup.
|
|
||||||
dtMeshTile* m_nextFree; ///< Freelist of tiles.
|
|
||||||
dtMeshTile* m_tiles; ///< List of tiles.
|
|
||||||
|
|
||||||
#ifndef DT_POLYREF64
|
|
||||||
unsigned int m_saltBits; ///< Number of salt bits in the tile ID.
|
|
||||||
unsigned int m_tileBits; ///< Number of tile bits in the tile ID.
|
|
||||||
unsigned int m_polyBits; ///< Number of poly bits in the tile ID.
|
|
||||||
#endif
|
|
||||||
|
|
||||||
friend class dtNavMeshQuery;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Allocates a navigation mesh object using the Detour allocator.
|
|
||||||
/// @return A navigation mesh that is ready for initialization, or null on failure.
|
|
||||||
/// @ingroup detour
|
|
||||||
dtNavMesh* dtAllocNavMesh();
|
|
||||||
|
|
||||||
/// Frees the specified navigation mesh object using the Detour allocator.
|
|
||||||
/// @param[in] navmesh A navigation mesh allocated using #dtAllocNavMesh
|
|
||||||
/// @ingroup detour
|
|
||||||
void dtFreeNavMesh(dtNavMesh* navmesh);
|
|
||||||
|
|
||||||
#endif // DETOURNAVMESH_H
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// This section contains detailed documentation for members that don't have
|
|
||||||
// a source file. It reduces clutter in the main section of the header.
|
|
||||||
|
|
||||||
/**
|
|
||||||
|
|
||||||
@typedef dtPolyRef
|
|
||||||
@par
|
|
||||||
|
|
||||||
Polygon references are subject to the same invalidate/preserve/restore
|
|
||||||
rules that apply to #dtTileRef's. If the #dtTileRef for the polygon's
|
|
||||||
tile changes, the polygon reference becomes invalid.
|
|
||||||
|
|
||||||
Changing a polygon's flags, area id, etc. does not impact its polygon
|
|
||||||
reference.
|
|
||||||
|
|
||||||
@typedef dtTileRef
|
|
||||||
@par
|
|
||||||
|
|
||||||
The following changes will invalidate a tile reference:
|
|
||||||
|
|
||||||
- The referenced tile has been removed from the navigation mesh.
|
|
||||||
- The navigation mesh has been initialized using a different set
|
|
||||||
of #dtNavMeshParams.
|
|
||||||
|
|
||||||
A tile reference is preserved/restored if the tile is added to a navigation
|
|
||||||
mesh initialized with the original #dtNavMeshParams and is added at the
|
|
||||||
original reference location. (E.g. The lastRef parameter is used with
|
|
||||||
dtNavMesh::addTile.)
|
|
||||||
|
|
||||||
Basically, if the storage structure of a tile changes, its associated
|
|
||||||
tile reference changes.
|
|
||||||
|
|
||||||
|
|
||||||
@var unsigned short dtPoly::neis[DT_VERTS_PER_POLYGON]
|
|
||||||
@par
|
|
||||||
|
|
||||||
Each entry represents data for the edge starting at the vertex of the same index.
|
|
||||||
E.g. The entry at index n represents the edge data for vertex[n] to vertex[n+1].
|
|
||||||
|
|
||||||
A value of zero indicates the edge has no polygon connection. (It makes up the
|
|
||||||
border of the navigation mesh.)
|
|
||||||
|
|
||||||
The information can be extracted as follows:
|
|
||||||
@code
|
|
||||||
neighborRef = neis[n] & 0xff; // Get the neighbor polygon reference.
|
|
||||||
|
|
||||||
if (neis[n] & #DT_EX_LINK)
|
|
||||||
{
|
|
||||||
// The edge is an external (portal) edge.
|
|
||||||
}
|
|
||||||
@endcode
|
|
||||||
|
|
||||||
@var float dtMeshHeader::bvQuantFactor
|
|
||||||
@par
|
|
||||||
|
|
||||||
This value is used for converting between world and bounding volume coordinates.
|
|
||||||
For example:
|
|
||||||
@code
|
|
||||||
const float cs = 1.0f / tile->header->bvQuantFactor;
|
|
||||||
const dtBVNode* n = &tile->bvTree[i];
|
|
||||||
if (n->i >= 0)
|
|
||||||
{
|
|
||||||
// This is a leaf node.
|
|
||||||
float worldMinX = tile->header->bmin[0] + n->bmin[0]*cs;
|
|
||||||
float worldMinY = tile->header->bmin[0] + n->bmin[1]*cs;
|
|
||||||
// Etc...
|
|
||||||
}
|
|
||||||
@endcode
|
|
||||||
|
|
||||||
@struct dtMeshTile
|
|
||||||
@par
|
|
||||||
|
|
||||||
Tiles generally only exist within the context of a dtNavMesh object.
|
|
||||||
|
|
||||||
Some tile content is optional. For example, a tile may not contain any
|
|
||||||
off-mesh connections. In this case the associated pointer will be null.
|
|
||||||
|
|
||||||
If a detail mesh exists it will share vertices with the base polygon mesh.
|
|
||||||
Only the vertices unique to the detail mesh will be stored in #detailVerts.
|
|
||||||
|
|
||||||
@warning Tiles returned by a dtNavMesh object are not guarenteed to be populated.
|
|
||||||
For example: The tile at a location might not have been loaded yet, or may have been removed.
|
|
||||||
In this case, pointers will be null. So if in doubt, check the polygon count in the
|
|
||||||
tile's header to determine if a tile has polygons defined.
|
|
||||||
|
|
||||||
@var float dtOffMeshConnection::pos[6]
|
|
||||||
@par
|
|
||||||
|
|
||||||
For a properly built navigation mesh, vertex A will always be within the bounds of the mesh.
|
|
||||||
Vertex B is not required to be within the bounds of the mesh.
|
|
||||||
|
|
||||||
*/
|
|
@ -1,149 +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 DETOURNAVMESHBUILDER_H
|
|
||||||
#define DETOURNAVMESHBUILDER_H
|
|
||||||
|
|
||||||
#include "DetourAlloc.h"
|
|
||||||
|
|
||||||
/// Represents the source data used to build an navigation mesh tile.
|
|
||||||
/// @ingroup detour
|
|
||||||
struct dtNavMeshCreateParams
|
|
||||||
{
|
|
||||||
|
|
||||||
/// @name Polygon Mesh Attributes
|
|
||||||
/// Used to create the base navigation graph.
|
|
||||||
/// See #rcPolyMesh for details related to these attributes.
|
|
||||||
/// @{
|
|
||||||
|
|
||||||
const unsigned short* verts; ///< The polygon mesh vertices. [(x, y, z) * #vertCount] [Unit: vx]
|
|
||||||
int vertCount; ///< The number vertices in the polygon mesh. [Limit: >= 3]
|
|
||||||
const unsigned short* polys; ///< The polygon data. [Size: #polyCount * 2 * #nvp]
|
|
||||||
const unsigned short* polyFlags; ///< The user defined flags assigned to each polygon. [Size: #polyCount]
|
|
||||||
const unsigned char* polyAreas; ///< The user defined area ids assigned to each polygon. [Size: #polyCount]
|
|
||||||
int polyCount; ///< Number of polygons in the mesh. [Limit: >= 1]
|
|
||||||
int nvp; ///< Number maximum number of vertices per polygon. [Limit: >= 3]
|
|
||||||
|
|
||||||
/// @}
|
|
||||||
/// @name Height Detail Attributes (Optional)
|
|
||||||
/// See #rcPolyMeshDetail for details related to these attributes.
|
|
||||||
/// @{
|
|
||||||
|
|
||||||
const unsigned int* detailMeshes; ///< The height detail sub-mesh data. [Size: 4 * #polyCount]
|
|
||||||
const float* detailVerts; ///< The detail mesh vertices. [Size: 3 * #detailVertsCount] [Unit: wu]
|
|
||||||
int detailVertsCount; ///< The number of vertices in the detail mesh.
|
|
||||||
const unsigned char* detailTris; ///< The detail mesh triangles. [Size: 4 * #detailTriCount]
|
|
||||||
int detailTriCount; ///< The number of triangles in the detail mesh.
|
|
||||||
|
|
||||||
/// @}
|
|
||||||
/// @name Off-Mesh Connections Attributes (Optional)
|
|
||||||
/// Used to define a custom point-to-point edge within the navigation graph, an
|
|
||||||
/// off-mesh connection is a user defined traversable connection made up to two vertices,
|
|
||||||
/// at least one of which resides within a navigation mesh polygon.
|
|
||||||
/// @{
|
|
||||||
|
|
||||||
/// Off-mesh connection vertices. [(ax, ay, az, bx, by, bz) * #offMeshConCount] [Unit: wu]
|
|
||||||
const float* offMeshConVerts;
|
|
||||||
/// Off-mesh connection radii. [Size: #offMeshConCount] [Unit: wu]
|
|
||||||
const float* offMeshConRad;
|
|
||||||
/// User defined flags assigned to the off-mesh connections. [Size: #offMeshConCount]
|
|
||||||
const unsigned short* offMeshConFlags;
|
|
||||||
/// User defined area ids assigned to the off-mesh connections. [Size: #offMeshConCount]
|
|
||||||
const unsigned char* offMeshConAreas;
|
|
||||||
/// The permitted travel direction of the off-mesh connections. [Size: #offMeshConCount]
|
|
||||||
///
|
|
||||||
/// 0 = Travel only from endpoint A to endpoint B.<br/>
|
|
||||||
/// #DT_OFFMESH_CON_BIDIR = Bidirectional travel.
|
|
||||||
const unsigned char* offMeshConDir;
|
|
||||||
/// The user defined ids of the off-mesh connection. [Size: #offMeshConCount]
|
|
||||||
const unsigned int* offMeshConUserID;
|
|
||||||
/// The number of off-mesh connections. [Limit: >= 0]
|
|
||||||
int offMeshConCount;
|
|
||||||
|
|
||||||
/// @}
|
|
||||||
/// @name Tile Attributes
|
|
||||||
/// @note The tile grid/layer data can be left at zero if the destination is a single tile mesh.
|
|
||||||
/// @{
|
|
||||||
|
|
||||||
unsigned int userId; ///< The user defined id of the tile.
|
|
||||||
int tileX; ///< The tile's x-grid location within the multi-tile destination mesh. (Along the x-axis.)
|
|
||||||
int tileY; ///< The tile's y-grid location within the multi-tile desitation mesh. (Along the z-axis.)
|
|
||||||
int tileLayer; ///< The tile's layer within the layered destination mesh. [Limit: >= 0] (Along the y-axis.)
|
|
||||||
float bmin[3]; ///< The minimum bounds of the tile. [(x, y, z)] [Unit: wu]
|
|
||||||
float bmax[3]; ///< The maximum bounds of the tile. [(x, y, z)] [Unit: wu]
|
|
||||||
|
|
||||||
/// @}
|
|
||||||
/// @name General Configuration Attributes
|
|
||||||
/// @{
|
|
||||||
|
|
||||||
float walkableHeight; ///< The agent height. [Unit: wu]
|
|
||||||
float walkableRadius; ///< The agent radius. [Unit: wu]
|
|
||||||
float walkableClimb; ///< The agent maximum traversable ledge. (Up/Down) [Unit: wu]
|
|
||||||
float cs; ///< The xz-plane cell size of the polygon mesh. [Limit: > 0] [Unit: wu]
|
|
||||||
float ch; ///< The y-axis cell height of the polygon mesh. [Limit: > 0] [Unit: wu]
|
|
||||||
|
|
||||||
/// True if a bounding volume tree should be built for the tile.
|
|
||||||
/// @note The BVTree is not normally needed for layered navigation meshes.
|
|
||||||
bool buildBvTree;
|
|
||||||
|
|
||||||
/// @}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Builds navigation mesh tile data from the provided tile creation data.
|
|
||||||
/// @ingroup detour
|
|
||||||
/// @param[in] params Tile creation data.
|
|
||||||
/// @param[out] outData The resulting tile data.
|
|
||||||
/// @param[out] outDataSize The size of the tile data array.
|
|
||||||
/// @return True if the tile data was successfully created.
|
|
||||||
bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData, int* outDataSize);
|
|
||||||
|
|
||||||
/// Swaps the endianess of the tile data's header (#dtMeshHeader).
|
|
||||||
/// @param[in,out] data The tile data array.
|
|
||||||
/// @param[in] dataSize The size of the data array.
|
|
||||||
bool dtNavMeshHeaderSwapEndian(unsigned char* data, const int dataSize);
|
|
||||||
|
|
||||||
/// Swaps endianess of the tile data.
|
|
||||||
/// @param[in,out] data The tile data array.
|
|
||||||
/// @param[in] dataSize The size of the data array.
|
|
||||||
bool dtNavMeshDataSwapEndian(unsigned char* data, const int dataSize);
|
|
||||||
|
|
||||||
#endif // DETOURNAVMESHBUILDER_H
|
|
||||||
|
|
||||||
// This section contains detailed documentation for members that don't have
|
|
||||||
// a source file. It reduces clutter in the main section of the header.
|
|
||||||
|
|
||||||
/**
|
|
||||||
|
|
||||||
@struct dtNavMeshCreateParams
|
|
||||||
@par
|
|
||||||
|
|
||||||
This structure is used to marshal data between the Recast mesh generation pipeline and Detour navigation components.
|
|
||||||
|
|
||||||
See the rcPolyMesh and rcPolyMeshDetail documentation for detailed information related to mesh structure.
|
|
||||||
|
|
||||||
Units are usually in voxels (vx) or world units (wu). The units for voxels, grid size, and cell size
|
|
||||||
are all based on the values of #cs and #ch.
|
|
||||||
|
|
||||||
The standard navigation mesh build process is to create tile data using dtCreateNavMeshData, then add the tile
|
|
||||||
to a navigation mesh using either the dtNavMesh single tile <tt>init()</tt> function or the dtNavMesh::addTile()
|
|
||||||
function.
|
|
||||||
|
|
||||||
@see dtCreateNavMeshData
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
@ -1,573 +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 DETOURNAVMESHQUERY_H
|
|
||||||
#define DETOURNAVMESHQUERY_H
|
|
||||||
|
|
||||||
#include "DetourNavMesh.h"
|
|
||||||
#include "DetourStatus.h"
|
|
||||||
|
|
||||||
|
|
||||||
// Define DT_VIRTUAL_QUERYFILTER if you wish to derive a custom filter from dtQueryFilter.
|
|
||||||
// On certain platforms indirect or virtual function call is expensive. The default
|
|
||||||
// setting is to use non-virtual functions, the actual implementations of the functions
|
|
||||||
// are declared as inline for maximum speed.
|
|
||||||
|
|
||||||
//#define DT_VIRTUAL_QUERYFILTER 1
|
|
||||||
|
|
||||||
/// Defines polygon filtering and traversal costs for navigation mesh query operations.
|
|
||||||
/// @ingroup detour
|
|
||||||
class dtQueryFilter
|
|
||||||
{
|
|
||||||
float m_areaCost[DT_MAX_AREAS]; ///< Cost per area type. (Used by default implementation.)
|
|
||||||
unsigned short m_includeFlags; ///< Flags for polygons that can be visited. (Used by default implementation.)
|
|
||||||
unsigned short m_excludeFlags; ///< Flags for polygons that should not be visted. (Used by default implementation.)
|
|
||||||
|
|
||||||
public:
|
|
||||||
dtQueryFilter();
|
|
||||||
|
|
||||||
#ifdef DT_VIRTUAL_QUERYFILTER
|
|
||||||
virtual ~dtQueryFilter() { }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// Returns true if the polygon can be visited. (I.e. Is traversable.)
|
|
||||||
/// @param[in] ref The reference id of the polygon test.
|
|
||||||
/// @param[in] tile The tile containing the polygon.
|
|
||||||
/// @param[in] poly The polygon to test.
|
|
||||||
#ifdef DT_VIRTUAL_QUERYFILTER
|
|
||||||
virtual bool passFilter(const dtPolyRef ref,
|
|
||||||
const dtMeshTile* tile,
|
|
||||||
const dtPoly* poly) const;
|
|
||||||
#else
|
|
||||||
bool passFilter(const dtPolyRef ref,
|
|
||||||
const dtMeshTile* tile,
|
|
||||||
const dtPoly* poly) const;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// Returns cost to move from the beginning to the end of a line segment
|
|
||||||
/// that is fully contained within a polygon.
|
|
||||||
/// @param[in] pa The start position on the edge of the previous and current polygon. [(x, y, z)]
|
|
||||||
/// @param[in] pb The end position on the edge of the current and next polygon. [(x, y, z)]
|
|
||||||
/// @param[in] prevRef The reference id of the previous polygon. [opt]
|
|
||||||
/// @param[in] prevTile The tile containing the previous polygon. [opt]
|
|
||||||
/// @param[in] prevPoly The previous polygon. [opt]
|
|
||||||
/// @param[in] curRef The reference id of the current polygon.
|
|
||||||
/// @param[in] curTile The tile containing the current polygon.
|
|
||||||
/// @param[in] curPoly The current polygon.
|
|
||||||
/// @param[in] nextRef The refernece id of the next polygon. [opt]
|
|
||||||
/// @param[in] nextTile The tile containing the next polygon. [opt]
|
|
||||||
/// @param[in] nextPoly The next polygon. [opt]
|
|
||||||
#ifdef DT_VIRTUAL_QUERYFILTER
|
|
||||||
virtual float 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;
|
|
||||||
#else
|
|
||||||
float 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;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// @name Getters and setters for the default implementation data.
|
|
||||||
///@{
|
|
||||||
|
|
||||||
/// Returns the traversal cost of the area.
|
|
||||||
/// @param[in] i The id of the area.
|
|
||||||
/// @returns The traversal cost of the area.
|
|
||||||
inline float getAreaCost(const int i) const { return m_areaCost[i]; }
|
|
||||||
|
|
||||||
/// Sets the traversal cost of the area.
|
|
||||||
/// @param[in] i The id of the area.
|
|
||||||
/// @param[in] cost The new cost of traversing the area.
|
|
||||||
inline void setAreaCost(const int i, const float cost) { m_areaCost[i] = cost; }
|
|
||||||
|
|
||||||
/// Returns the include flags for the filter.
|
|
||||||
/// Any polygons that include one or more of these flags will be
|
|
||||||
/// included in the operation.
|
|
||||||
inline unsigned short getIncludeFlags() const { return m_includeFlags; }
|
|
||||||
|
|
||||||
/// Sets the include flags for the filter.
|
|
||||||
/// @param[in] flags The new flags.
|
|
||||||
inline void setIncludeFlags(const unsigned short flags) { m_includeFlags = flags; }
|
|
||||||
|
|
||||||
/// Returns the exclude flags for the filter.
|
|
||||||
/// Any polygons that include one ore more of these flags will be
|
|
||||||
/// excluded from the operation.
|
|
||||||
inline unsigned short getExcludeFlags() const { return m_excludeFlags; }
|
|
||||||
|
|
||||||
/// Sets the exclude flags for the filter.
|
|
||||||
/// @param[in] flags The new flags.
|
|
||||||
inline void setExcludeFlags(const unsigned short flags) { m_excludeFlags = flags; }
|
|
||||||
|
|
||||||
///@}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Provides information about raycast hit
|
|
||||||
/// filled by dtNavMeshQuery::raycast
|
|
||||||
/// @ingroup detour
|
|
||||||
struct dtRaycastHit
|
|
||||||
{
|
|
||||||
/// The hit parameter. (FLT_MAX if no wall hit.)
|
|
||||||
float t;
|
|
||||||
|
|
||||||
/// hitNormal The normal of the nearest wall hit. [(x, y, z)]
|
|
||||||
float hitNormal[3];
|
|
||||||
|
|
||||||
/// The index of the edge on the final polygon where the wall was hit.
|
|
||||||
int hitEdgeIndex;
|
|
||||||
|
|
||||||
/// Pointer to an array of reference ids of the visited polygons. [opt]
|
|
||||||
dtPolyRef* path;
|
|
||||||
|
|
||||||
/// The number of visited polygons. [opt]
|
|
||||||
int pathCount;
|
|
||||||
|
|
||||||
/// The maximum number of polygons the @p path array can hold.
|
|
||||||
int maxPath;
|
|
||||||
|
|
||||||
/// The cost of the path until hit.
|
|
||||||
float pathCost;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Provides custom polygon query behavior.
|
|
||||||
/// Used by dtNavMeshQuery::queryPolygons.
|
|
||||||
/// @ingroup detour
|
|
||||||
class dtPolyQuery
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~dtPolyQuery() { }
|
|
||||||
|
|
||||||
/// Called for each batch of unique polygons touched by the search area in dtNavMeshQuery::queryPolygons.
|
|
||||||
/// This can be called multiple times for a single query.
|
|
||||||
virtual void process(const dtMeshTile* tile, dtPoly** polys, dtPolyRef* refs, int count) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Provides the ability to perform pathfinding related queries against
|
|
||||||
/// a navigation mesh.
|
|
||||||
/// @ingroup detour
|
|
||||||
class dtNavMeshQuery
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
dtNavMeshQuery();
|
|
||||||
~dtNavMeshQuery();
|
|
||||||
|
|
||||||
/// Initializes the query object.
|
|
||||||
/// @param[in] nav Pointer to the dtNavMesh object to use for all queries.
|
|
||||||
/// @param[in] maxNodes Maximum number of search nodes. [Limits: 0 < value <= 65535]
|
|
||||||
/// @returns The status flags for the query.
|
|
||||||
dtStatus init(const dtNavMesh* nav, const int maxNodes);
|
|
||||||
|
|
||||||
/// @name Standard Pathfinding Functions
|
|
||||||
// /@{
|
|
||||||
|
|
||||||
/// Finds a path from the start polygon to the end polygon.
|
|
||||||
/// @param[in] startRef The refrence id of the start polygon.
|
|
||||||
/// @param[in] endRef The reference id of the end polygon.
|
|
||||||
/// @param[in] startPos A position within the start polygon. [(x, y, z)]
|
|
||||||
/// @param[in] endPos A position within the end polygon. [(x, y, z)]
|
|
||||||
/// @param[in] filter The polygon filter to apply to the query.
|
|
||||||
/// @param[out] path An ordered list of polygon references representing the path. (Start to end.)
|
|
||||||
/// [(polyRef) * @p pathCount]
|
|
||||||
/// @param[out] pathCount The number of polygons returned in the @p path array.
|
|
||||||
/// @param[in] maxPath The maximum number of polygons the @p path array can hold. [Limit: >= 1]
|
|
||||||
dtStatus findPath(dtPolyRef startRef, dtPolyRef endRef,
|
|
||||||
const float* startPos, const float* endPos,
|
|
||||||
const dtQueryFilter* filter,
|
|
||||||
dtPolyRef* path, int* pathCount, const int maxPath) const;
|
|
||||||
|
|
||||||
/// Finds the straight path from the start to the end position within the polygon corridor.
|
|
||||||
/// @param[in] startPos Path start position. [(x, y, z)]
|
|
||||||
/// @param[in] endPos Path end position. [(x, y, z)]
|
|
||||||
/// @param[in] path An array of polygon references that represent the path corridor.
|
|
||||||
/// @param[in] pathSize The number of polygons in the @p path array.
|
|
||||||
/// @param[out] straightPath Points describing the straight path. [(x, y, z) * @p straightPathCount].
|
|
||||||
/// @param[out] straightPathFlags Flags describing each point. (See: #dtStraightPathFlags) [opt]
|
|
||||||
/// @param[out] straightPathRefs The reference id of the polygon that is being entered at each point. [opt]
|
|
||||||
/// @param[out] straightPathCount The number of points in the straight path.
|
|
||||||
/// @param[in] maxStraightPath The maximum number of points the straight path arrays can hold. [Limit: > 0]
|
|
||||||
/// @param[in] options Query options. (see: #dtStraightPathOptions)
|
|
||||||
/// @returns The status flags for the query.
|
|
||||||
dtStatus 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 = 0) const;
|
|
||||||
|
|
||||||
///@}
|
|
||||||
/// @name Sliced Pathfinding Functions
|
|
||||||
/// Common use case:
|
|
||||||
/// -# Call initSlicedFindPath() to initialize the sliced path query.
|
|
||||||
/// -# Call updateSlicedFindPath() until it returns complete.
|
|
||||||
/// -# Call finalizeSlicedFindPath() to get the path.
|
|
||||||
///@{
|
|
||||||
|
|
||||||
/// Intializes a sliced path query.
|
|
||||||
/// @param[in] startRef The refrence id of the start polygon.
|
|
||||||
/// @param[in] endRef The reference id of the end polygon.
|
|
||||||
/// @param[in] startPos A position within the start polygon. [(x, y, z)]
|
|
||||||
/// @param[in] endPos A position within the end polygon. [(x, y, z)]
|
|
||||||
/// @param[in] filter The polygon filter to apply to the query.
|
|
||||||
/// @param[in] options query options (see: #dtFindPathOptions)
|
|
||||||
/// @returns The status flags for the query.
|
|
||||||
dtStatus initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef,
|
|
||||||
const float* startPos, const float* endPos,
|
|
||||||
const dtQueryFilter* filter, const unsigned int options = 0);
|
|
||||||
|
|
||||||
/// Updates an in-progress sliced path query.
|
|
||||||
/// @param[in] maxIter The maximum number of iterations to perform.
|
|
||||||
/// @param[out] doneIters The actual number of iterations completed. [opt]
|
|
||||||
/// @returns The status flags for the query.
|
|
||||||
dtStatus updateSlicedFindPath(const int maxIter, int* doneIters);
|
|
||||||
|
|
||||||
/// Finalizes and returns the results of a sliced path query.
|
|
||||||
/// @param[out] path An ordered list of polygon references representing the path. (Start to end.)
|
|
||||||
/// [(polyRef) * @p pathCount]
|
|
||||||
/// @param[out] pathCount The number of polygons returned in the @p path array.
|
|
||||||
/// @param[in] maxPath The max number of polygons the path array can hold. [Limit: >= 1]
|
|
||||||
/// @returns The status flags for the query.
|
|
||||||
dtStatus finalizeSlicedFindPath(dtPolyRef* path, int* pathCount, const int maxPath);
|
|
||||||
|
|
||||||
/// Finalizes and returns the results of an incomplete sliced path query, returning the path to the furthest
|
|
||||||
/// polygon on the existing path that was visited during the search.
|
|
||||||
/// @param[in] existing An array of polygon references for the existing path.
|
|
||||||
/// @param[in] existingSize The number of polygon in the @p existing array.
|
|
||||||
/// @param[out] path An ordered list of polygon references representing the path. (Start to end.)
|
|
||||||
/// [(polyRef) * @p pathCount]
|
|
||||||
/// @param[out] pathCount The number of polygons returned in the @p path array.
|
|
||||||
/// @param[in] maxPath The max number of polygons the @p path array can hold. [Limit: >= 1]
|
|
||||||
/// @returns The status flags for the query.
|
|
||||||
dtStatus finalizeSlicedFindPathPartial(const dtPolyRef* existing, const int existingSize,
|
|
||||||
dtPolyRef* path, int* pathCount, const int maxPath);
|
|
||||||
|
|
||||||
///@}
|
|
||||||
/// @name Dijkstra Search Functions
|
|
||||||
/// @{
|
|
||||||
|
|
||||||
/// Finds the polygons along the navigation graph that touch the specified circle.
|
|
||||||
/// @param[in] startRef The reference id of the polygon where the search starts.
|
|
||||||
/// @param[in] centerPos The center of the search circle. [(x, y, z)]
|
|
||||||
/// @param[in] radius The radius of the search circle.
|
|
||||||
/// @param[in] filter The polygon filter to apply to the query.
|
|
||||||
/// @param[out] resultRef The reference ids of the polygons touched by the circle. [opt]
|
|
||||||
/// @param[out] resultParent The reference ids of the parent polygons for each result.
|
|
||||||
/// Zero if a result polygon has no parent. [opt]
|
|
||||||
/// @param[out] resultCost The search cost from @p centerPos to the polygon. [opt]
|
|
||||||
/// @param[out] resultCount The number of polygons found. [opt]
|
|
||||||
/// @param[in] maxResult The maximum number of polygons the result arrays can hold.
|
|
||||||
/// @returns The status flags for the query.
|
|
||||||
dtStatus findPolysAroundCircle(dtPolyRef startRef, const float* centerPos, const float radius,
|
|
||||||
const dtQueryFilter* filter,
|
|
||||||
dtPolyRef* resultRef, dtPolyRef* resultParent, float* resultCost,
|
|
||||||
int* resultCount, const int maxResult) const;
|
|
||||||
|
|
||||||
/// Finds the polygons along the naviation graph that touch the specified convex polygon.
|
|
||||||
/// @param[in] startRef The reference id of the polygon where the search starts.
|
|
||||||
/// @param[in] verts The vertices describing the convex polygon. (CCW)
|
|
||||||
/// [(x, y, z) * @p nverts]
|
|
||||||
/// @param[in] nverts The number of vertices in the polygon.
|
|
||||||
/// @param[in] filter The polygon filter to apply to the query.
|
|
||||||
/// @param[out] resultRef The reference ids of the polygons touched by the search polygon. [opt]
|
|
||||||
/// @param[out] resultParent The reference ids of the parent polygons for each result. Zero if a
|
|
||||||
/// result polygon has no parent. [opt]
|
|
||||||
/// @param[out] resultCost The search cost from the centroid point to the polygon. [opt]
|
|
||||||
/// @param[out] resultCount The number of polygons found.
|
|
||||||
/// @param[in] maxResult The maximum number of polygons the result arrays can hold.
|
|
||||||
/// @returns The status flags for the query.
|
|
||||||
dtStatus findPolysAroundShape(dtPolyRef startRef, const float* verts, const int nverts,
|
|
||||||
const dtQueryFilter* filter,
|
|
||||||
dtPolyRef* resultRef, dtPolyRef* resultParent, float* resultCost,
|
|
||||||
int* resultCount, const int maxResult) const;
|
|
||||||
|
|
||||||
/// Gets a path from the explored nodes in the previous search.
|
|
||||||
/// @param[in] endRef The reference id of the end polygon.
|
|
||||||
/// @param[out] path An ordered list of polygon references representing the path. (Start to end.)
|
|
||||||
/// [(polyRef) * @p pathCount]
|
|
||||||
/// @param[out] pathCount The number of polygons returned in the @p path array.
|
|
||||||
/// @param[in] maxPath The maximum number of polygons the @p path array can hold. [Limit: >= 0]
|
|
||||||
/// @returns The status flags. Returns DT_FAILURE | DT_INVALID_PARAM if any parameter is wrong, or if
|
|
||||||
/// @p endRef was not explored in the previous search. Returns DT_SUCCESS | DT_BUFFER_TOO_SMALL
|
|
||||||
/// if @p path cannot contain the entire path. In this case it is filled to capacity with a partial path.
|
|
||||||
/// Otherwise returns DT_SUCCESS.
|
|
||||||
/// @remarks The result of this function depends on the state of the query object. For that reason it should only
|
|
||||||
/// be used immediately after one of the two Dijkstra searches, findPolysAroundCircle or findPolysAroundShape.
|
|
||||||
dtStatus getPathFromDijkstraSearch(dtPolyRef endRef, dtPolyRef* path, int* pathCount, int maxPath) const;
|
|
||||||
|
|
||||||
/// @}
|
|
||||||
/// @name Local Query Functions
|
|
||||||
///@{
|
|
||||||
|
|
||||||
/// Finds the polygon nearest to the specified center point.
|
|
||||||
/// @param[in] center The center of the search box. [(x, y, z)]
|
|
||||||
/// @param[in] halfExtents The search distance along each axis. [(x, y, z)]
|
|
||||||
/// @param[in] filter The polygon filter to apply to the query.
|
|
||||||
/// @param[out] nearestRef The reference id of the nearest polygon.
|
|
||||||
/// @param[out] nearestPt The nearest point on the polygon. [opt] [(x, y, z)]
|
|
||||||
/// @returns The status flags for the query.
|
|
||||||
dtStatus findNearestPoly(const float* center, const float* halfExtents,
|
|
||||||
const dtQueryFilter* filter,
|
|
||||||
dtPolyRef* nearestRef, float* nearestPt) const;
|
|
||||||
|
|
||||||
/// Finds polygons that overlap the search box.
|
|
||||||
/// @param[in] center The center of the search box. [(x, y, z)]
|
|
||||||
/// @param[in] halfExtents The search distance along each axis. [(x, y, z)]
|
|
||||||
/// @param[in] filter The polygon filter to apply to the query.
|
|
||||||
/// @param[out] polys The reference ids of the polygons that overlap the query box.
|
|
||||||
/// @param[out] polyCount The number of polygons in the search result.
|
|
||||||
/// @param[in] maxPolys The maximum number of polygons the search result can hold.
|
|
||||||
/// @returns The status flags for the query.
|
|
||||||
dtStatus queryPolygons(const float* center, const float* halfExtents,
|
|
||||||
const dtQueryFilter* filter,
|
|
||||||
dtPolyRef* polys, int* polyCount, const int maxPolys) const;
|
|
||||||
|
|
||||||
/// Finds polygons that overlap the search box.
|
|
||||||
/// @param[in] center The center of the search box. [(x, y, z)]
|
|
||||||
/// @param[in] halfExtents The search distance along each axis. [(x, y, z)]
|
|
||||||
/// @param[in] filter The polygon filter to apply to the query.
|
|
||||||
/// @param[in] query The query. Polygons found will be batched together and passed to this query.
|
|
||||||
dtStatus queryPolygons(const float* center, const float* halfExtents,
|
|
||||||
const dtQueryFilter* filter, dtPolyQuery* query) const;
|
|
||||||
|
|
||||||
/// Finds the non-overlapping navigation polygons in the local neighbourhood around the center position.
|
|
||||||
/// @param[in] startRef The reference id of the polygon where the search starts.
|
|
||||||
/// @param[in] centerPos The center of the query circle. [(x, y, z)]
|
|
||||||
/// @param[in] radius The radius of the query circle.
|
|
||||||
/// @param[in] filter The polygon filter to apply to the query.
|
|
||||||
/// @param[out] resultRef The reference ids of the polygons touched by the circle.
|
|
||||||
/// @param[out] resultParent The reference ids of the parent polygons for each result.
|
|
||||||
/// Zero if a result polygon has no parent. [opt]
|
|
||||||
/// @param[out] resultCount The number of polygons found.
|
|
||||||
/// @param[in] maxResult The maximum number of polygons the result arrays can hold.
|
|
||||||
/// @returns The status flags for the query.
|
|
||||||
dtStatus findLocalNeighbourhood(dtPolyRef startRef, const float* centerPos, const float radius,
|
|
||||||
const dtQueryFilter* filter,
|
|
||||||
dtPolyRef* resultRef, dtPolyRef* resultParent,
|
|
||||||
int* resultCount, const int maxResult) const;
|
|
||||||
|
|
||||||
/// Moves from the start to the end position constrained to the navigation mesh.
|
|
||||||
/// @param[in] startRef The reference id of the start polygon.
|
|
||||||
/// @param[in] startPos A position of the mover within the start polygon. [(x, y, x)]
|
|
||||||
/// @param[in] endPos The desired end position of the mover. [(x, y, z)]
|
|
||||||
/// @param[in] filter The polygon filter to apply to the query.
|
|
||||||
/// @param[out] resultPos The result position of the mover. [(x, y, z)]
|
|
||||||
/// @param[out] visited The reference ids of the polygons visited during the move.
|
|
||||||
/// @param[out] visitedCount The number of polygons visited during the move.
|
|
||||||
/// @param[in] maxVisitedSize The maximum number of polygons the @p visited array can hold.
|
|
||||||
/// @returns The status flags for the query.
|
|
||||||
dtStatus moveAlongSurface(dtPolyRef startRef, const float* startPos, const float* endPos,
|
|
||||||
const dtQueryFilter* filter,
|
|
||||||
float* resultPos, dtPolyRef* visited, int* visitedCount, const int maxVisitedSize) const;
|
|
||||||
|
|
||||||
/// Casts a 'walkability' ray along the surface of the navigation mesh from
|
|
||||||
/// the start position toward the end position.
|
|
||||||
/// @note A wrapper around raycast(..., RaycastHit*). Retained for backward compatibility.
|
|
||||||
/// @param[in] startRef The reference id of the start polygon.
|
|
||||||
/// @param[in] startPos A position within the start polygon representing
|
|
||||||
/// the start of the ray. [(x, y, z)]
|
|
||||||
/// @param[in] endPos The position to cast the ray toward. [(x, y, z)]
|
|
||||||
/// @param[out] t The hit parameter. (FLT_MAX if no wall hit.)
|
|
||||||
/// @param[out] hitNormal The normal of the nearest wall hit. [(x, y, z)]
|
|
||||||
/// @param[in] filter The polygon filter to apply to the query.
|
|
||||||
/// @param[out] path The reference ids of the visited polygons. [opt]
|
|
||||||
/// @param[out] pathCount The number of visited polygons. [opt]
|
|
||||||
/// @param[in] maxPath The maximum number of polygons the @p path array can hold.
|
|
||||||
/// @returns The status flags for the query.
|
|
||||||
dtStatus raycast(dtPolyRef startRef, const float* startPos, const float* endPos,
|
|
||||||
const dtQueryFilter* filter,
|
|
||||||
float* t, float* hitNormal, dtPolyRef* path, int* pathCount, const int maxPath) const;
|
|
||||||
|
|
||||||
/// Casts a 'walkability' ray along the surface of the navigation mesh from
|
|
||||||
/// the start position toward the end position.
|
|
||||||
/// @param[in] startRef The reference id of the start polygon.
|
|
||||||
/// @param[in] startPos A position within the start polygon representing
|
|
||||||
/// the start of the ray. [(x, y, z)]
|
|
||||||
/// @param[in] endPos The position to cast the ray toward. [(x, y, z)]
|
|
||||||
/// @param[in] filter The polygon filter to apply to the query.
|
|
||||||
/// @param[in] flags govern how the raycast behaves. See dtRaycastOptions
|
|
||||||
/// @param[out] hit Pointer to a raycast hit structure which will be filled by the results.
|
|
||||||
/// @param[in] prevRef parent of start ref. Used during for cost calculation [opt]
|
|
||||||
/// @returns The status flags for the query.
|
|
||||||
dtStatus raycast(dtPolyRef startRef, const float* startPos, const float* endPos,
|
|
||||||
const dtQueryFilter* filter, const unsigned int options,
|
|
||||||
dtRaycastHit* hit, dtPolyRef prevRef = 0) const;
|
|
||||||
|
|
||||||
|
|
||||||
/// Finds the distance from the specified position to the nearest polygon wall.
|
|
||||||
/// @param[in] startRef The reference id of the polygon containing @p centerPos.
|
|
||||||
/// @param[in] centerPos The center of the search circle. [(x, y, z)]
|
|
||||||
/// @param[in] maxRadius The radius of the search circle.
|
|
||||||
/// @param[in] filter The polygon filter to apply to the query.
|
|
||||||
/// @param[out] hitDist The distance to the nearest wall from @p centerPos.
|
|
||||||
/// @param[out] hitPos The nearest position on the wall that was hit. [(x, y, z)]
|
|
||||||
/// @param[out] hitNormal The normalized ray formed from the wall point to the
|
|
||||||
/// source point. [(x, y, z)]
|
|
||||||
/// @returns The status flags for the query.
|
|
||||||
dtStatus findDistanceToWall(dtPolyRef startRef, const float* centerPos, const float maxRadius,
|
|
||||||
const dtQueryFilter* filter,
|
|
||||||
float* hitDist, float* hitPos, float* hitNormal) const;
|
|
||||||
|
|
||||||
/// Returns the segments for the specified polygon, optionally including portals.
|
|
||||||
/// @param[in] ref The reference id of the polygon.
|
|
||||||
/// @param[in] filter The polygon filter to apply to the query.
|
|
||||||
/// @param[out] segmentVerts The segments. [(ax, ay, az, bx, by, bz) * segmentCount]
|
|
||||||
/// @param[out] segmentRefs The reference ids of each segment's neighbor polygon.
|
|
||||||
/// Or zero if the segment is a wall. [opt] [(parentRef) * @p segmentCount]
|
|
||||||
/// @param[out] segmentCount The number of segments returned.
|
|
||||||
/// @param[in] maxSegments The maximum number of segments the result arrays can hold.
|
|
||||||
/// @returns The status flags for the query.
|
|
||||||
dtStatus getPolyWallSegments(dtPolyRef ref, const dtQueryFilter* filter,
|
|
||||||
float* segmentVerts, dtPolyRef* segmentRefs, int* segmentCount,
|
|
||||||
const int maxSegments) const;
|
|
||||||
|
|
||||||
/// Returns random location on navmesh.
|
|
||||||
/// Polygons are chosen weighted by area. The search runs in linear related to number of polygon.
|
|
||||||
/// @param[in] filter The polygon filter to apply to the query.
|
|
||||||
/// @param[in] frand Function returning a random number [0..1).
|
|
||||||
/// @param[out] randomRef The reference id of the random location.
|
|
||||||
/// @param[out] randomPt The random location.
|
|
||||||
/// @returns The status flags for the query.
|
|
||||||
dtStatus findRandomPoint(const dtQueryFilter* filter, float (*frand)(),
|
|
||||||
dtPolyRef* randomRef, float* randomPt) const;
|
|
||||||
|
|
||||||
/// Returns random location on navmesh within the reach of specified location.
|
|
||||||
/// Polygons are chosen weighted by area. The search runs in linear related to number of polygon.
|
|
||||||
/// The location is not exactly constrained by the circle, but it limits the visited polygons.
|
|
||||||
/// @param[in] startRef The reference id of the polygon where the search starts.
|
|
||||||
/// @param[in] centerPos The center of the search circle. [(x, y, z)]
|
|
||||||
/// @param[in] filter The polygon filter to apply to the query.
|
|
||||||
/// @param[in] frand Function returning a random number [0..1).
|
|
||||||
/// @param[out] randomRef The reference id of the random location.
|
|
||||||
/// @param[out] randomPt The random location. [(x, y, z)]
|
|
||||||
/// @returns The status flags for the query.
|
|
||||||
dtStatus findRandomPointAroundCircle(dtPolyRef startRef, const float* centerPos, const float maxRadius,
|
|
||||||
const dtQueryFilter* filter, float (*frand)(),
|
|
||||||
dtPolyRef* randomRef, float* randomPt) const;
|
|
||||||
|
|
||||||
/// Finds the closest point on the specified polygon.
|
|
||||||
/// @param[in] ref The reference id of the polygon.
|
|
||||||
/// @param[in] pos The position to check. [(x, y, z)]
|
|
||||||
/// @param[out] closest The closest point on the polygon. [(x, y, z)]
|
|
||||||
/// @param[out] posOverPoly True of the position is over the polygon.
|
|
||||||
/// @returns The status flags for the query.
|
|
||||||
dtStatus closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const;
|
|
||||||
|
|
||||||
/// Returns a point on the boundary closest to the source point if the source point is outside the
|
|
||||||
/// polygon's xz-bounds.
|
|
||||||
/// @param[in] ref The reference id to the polygon.
|
|
||||||
/// @param[in] pos The position to check. [(x, y, z)]
|
|
||||||
/// @param[out] closest The closest point. [(x, y, z)]
|
|
||||||
/// @returns The status flags for the query.
|
|
||||||
dtStatus closestPointOnPolyBoundary(dtPolyRef ref, const float* pos, float* closest) const;
|
|
||||||
|
|
||||||
/// Gets the height of the polygon at the provided position using the height detail. (Most accurate.)
|
|
||||||
/// @param[in] ref The reference id of the polygon.
|
|
||||||
/// @param[in] pos A position within the xz-bounds of the polygon. [(x, y, z)]
|
|
||||||
/// @param[out] height The height at the surface of the polygon.
|
|
||||||
/// @returns The status flags for the query.
|
|
||||||
dtStatus getPolyHeight(dtPolyRef ref, const float* pos, float* height) const;
|
|
||||||
|
|
||||||
/// @}
|
|
||||||
/// @name Miscellaneous Functions
|
|
||||||
/// @{
|
|
||||||
|
|
||||||
/// Returns true if the polygon reference is valid and passes the filter restrictions.
|
|
||||||
/// @param[in] ref The polygon reference to check.
|
|
||||||
/// @param[in] filter The filter to apply.
|
|
||||||
bool isValidPolyRef(dtPolyRef ref, const dtQueryFilter* filter) const;
|
|
||||||
|
|
||||||
/// Returns true if the polygon reference is in the closed list.
|
|
||||||
/// @param[in] ref The reference id of the polygon to check.
|
|
||||||
/// @returns True if the polygon is in closed list.
|
|
||||||
bool isInClosedList(dtPolyRef ref) const;
|
|
||||||
|
|
||||||
/// Gets the node pool.
|
|
||||||
/// @returns The node pool.
|
|
||||||
class dtNodePool* getNodePool() const { return m_nodePool; }
|
|
||||||
|
|
||||||
/// Gets the navigation mesh the query object is using.
|
|
||||||
/// @return The navigation mesh the query object is using.
|
|
||||||
const dtNavMesh* getAttachedNavMesh() const { return m_nav; }
|
|
||||||
|
|
||||||
/// @}
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Explicitly disabled copy constructor and copy assignment operator
|
|
||||||
dtNavMeshQuery(const dtNavMeshQuery&);
|
|
||||||
dtNavMeshQuery& operator=(const dtNavMeshQuery&);
|
|
||||||
|
|
||||||
/// Queries polygons within a tile.
|
|
||||||
void queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax,
|
|
||||||
const dtQueryFilter* filter, dtPolyQuery* query) const;
|
|
||||||
|
|
||||||
/// Returns portal points between two polygons.
|
|
||||||
dtStatus getPortalPoints(dtPolyRef from, dtPolyRef to, float* left, float* right,
|
|
||||||
unsigned char& fromType, unsigned char& toType) const;
|
|
||||||
dtStatus getPortalPoints(dtPolyRef from, const dtPoly* fromPoly, const dtMeshTile* fromTile,
|
|
||||||
dtPolyRef to, const dtPoly* toPoly, const dtMeshTile* toTile,
|
|
||||||
float* left, float* right) const;
|
|
||||||
|
|
||||||
/// Returns edge mid point between two polygons.
|
|
||||||
dtStatus getEdgeMidPoint(dtPolyRef from, dtPolyRef to, float* mid) const;
|
|
||||||
dtStatus getEdgeMidPoint(dtPolyRef from, const dtPoly* fromPoly, const dtMeshTile* fromTile,
|
|
||||||
dtPolyRef to, const dtPoly* toPoly, const dtMeshTile* toTile,
|
|
||||||
float* mid) const;
|
|
||||||
|
|
||||||
// Appends vertex to a straight path
|
|
||||||
dtStatus appendVertex(const float* pos, const unsigned char flags, const dtPolyRef ref,
|
|
||||||
float* straightPath, unsigned char* straightPathFlags, dtPolyRef* straightPathRefs,
|
|
||||||
int* straightPathCount, const int maxStraightPath) const;
|
|
||||||
|
|
||||||
// Appends intermediate portal points to a straight path.
|
|
||||||
dtStatus appendPortals(const int startIdx, const int endIdx, const float* endPos, const dtPolyRef* path,
|
|
||||||
float* straightPath, unsigned char* straightPathFlags, dtPolyRef* straightPathRefs,
|
|
||||||
int* straightPathCount, const int maxStraightPath, const int options) const;
|
|
||||||
|
|
||||||
// Gets the path leading to the specified end node.
|
|
||||||
dtStatus getPathToNode(struct dtNode* endNode, dtPolyRef* path, int* pathCount, int maxPath) const;
|
|
||||||
|
|
||||||
const dtNavMesh* m_nav; ///< Pointer to navmesh data.
|
|
||||||
|
|
||||||
struct dtQueryData
|
|
||||||
{
|
|
||||||
dtStatus status;
|
|
||||||
struct dtNode* lastBestNode;
|
|
||||||
float lastBestNodeCost;
|
|
||||||
dtPolyRef startRef, endRef;
|
|
||||||
float startPos[3], endPos[3];
|
|
||||||
const dtQueryFilter* filter;
|
|
||||||
unsigned int options;
|
|
||||||
float raycastLimitSqr;
|
|
||||||
};
|
|
||||||
dtQueryData m_query; ///< Sliced query state.
|
|
||||||
|
|
||||||
class dtNodePool* m_tinyNodePool; ///< Pointer to small node pool.
|
|
||||||
class dtNodePool* m_nodePool; ///< Pointer to node pool.
|
|
||||||
class dtNodeQueue* m_openList; ///< Pointer to open list queue.
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Allocates a query object using the Detour allocator.
|
|
||||||
/// @return An allocated query object, or null on failure.
|
|
||||||
/// @ingroup detour
|
|
||||||
dtNavMeshQuery* dtAllocNavMeshQuery();
|
|
||||||
|
|
||||||
/// Frees the specified query object using the Detour allocator.
|
|
||||||
/// @param[in] query A query object allocated using #dtAllocNavMeshQuery
|
|
||||||
/// @ingroup detour
|
|
||||||
void dtFreeNavMeshQuery(dtNavMeshQuery* query);
|
|
||||||
|
|
||||||
#endif // DETOURNAVMESHQUERY_H
|
|
@ -1,168 +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 DETOURNODE_H
|
|
||||||
#define DETOURNODE_H
|
|
||||||
|
|
||||||
#include "DetourNavMesh.h"
|
|
||||||
|
|
||||||
enum dtNodeFlags
|
|
||||||
{
|
|
||||||
DT_NODE_OPEN = 0x01,
|
|
||||||
DT_NODE_CLOSED = 0x02,
|
|
||||||
DT_NODE_PARENT_DETACHED = 0x04, // parent of the node is not adjacent. Found using raycast.
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef unsigned short dtNodeIndex;
|
|
||||||
static const dtNodeIndex DT_NULL_IDX = (dtNodeIndex)~0;
|
|
||||||
|
|
||||||
static const int DT_NODE_PARENT_BITS = 24;
|
|
||||||
static const int DT_NODE_STATE_BITS = 2;
|
|
||||||
struct dtNode
|
|
||||||
{
|
|
||||||
float pos[3]; ///< Position of the node.
|
|
||||||
float cost; ///< Cost from previous node to current node.
|
|
||||||
float total; ///< Cost up to the node.
|
|
||||||
unsigned int pidx : DT_NODE_PARENT_BITS; ///< Index to parent node.
|
|
||||||
unsigned int state : DT_NODE_STATE_BITS; ///< extra state information. A polyRef can have multiple nodes with different extra info. see DT_MAX_STATES_PER_NODE
|
|
||||||
unsigned int flags : 3; ///< Node flags. A combination of dtNodeFlags.
|
|
||||||
dtPolyRef id; ///< Polygon ref the node corresponds to.
|
|
||||||
};
|
|
||||||
|
|
||||||
static const int DT_MAX_STATES_PER_NODE = 1 << DT_NODE_STATE_BITS; // number of extra states per node. See dtNode::state
|
|
||||||
|
|
||||||
class dtNodePool
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
dtNodePool(int maxNodes, int hashSize);
|
|
||||||
~dtNodePool();
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
// Get a dtNode by ref and extra state information. If there is none then - allocate
|
|
||||||
// There can be more than one node for the same polyRef but with different extra state information
|
|
||||||
dtNode* getNode(dtPolyRef id, unsigned char state=0);
|
|
||||||
dtNode* findNode(dtPolyRef id, unsigned char state);
|
|
||||||
unsigned int findNodes(dtPolyRef id, dtNode** nodes, const int maxNodes);
|
|
||||||
|
|
||||||
inline unsigned int getNodeIdx(const dtNode* node) const
|
|
||||||
{
|
|
||||||
if (!node) return 0;
|
|
||||||
return (unsigned int)(node - m_nodes) + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline dtNode* getNodeAtIdx(unsigned int idx)
|
|
||||||
{
|
|
||||||
if (!idx) return 0;
|
|
||||||
return &m_nodes[idx - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const dtNode* getNodeAtIdx(unsigned int idx) const
|
|
||||||
{
|
|
||||||
if (!idx) return 0;
|
|
||||||
return &m_nodes[idx - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int getMemUsed() const
|
|
||||||
{
|
|
||||||
return sizeof(*this) +
|
|
||||||
sizeof(dtNode)*m_maxNodes +
|
|
||||||
sizeof(dtNodeIndex)*m_maxNodes +
|
|
||||||
sizeof(dtNodeIndex)*m_hashSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int getMaxNodes() const { return m_maxNodes; }
|
|
||||||
|
|
||||||
inline int getHashSize() const { return m_hashSize; }
|
|
||||||
inline dtNodeIndex getFirst(int bucket) const { return m_first[bucket]; }
|
|
||||||
inline dtNodeIndex getNext(int i) const { return m_next[i]; }
|
|
||||||
inline int getNodeCount() const { return m_nodeCount; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Explicitly disabled copy constructor and copy assignment operator.
|
|
||||||
dtNodePool(const dtNodePool&);
|
|
||||||
dtNodePool& operator=(const dtNodePool&);
|
|
||||||
|
|
||||||
dtNode* m_nodes;
|
|
||||||
dtNodeIndex* m_first;
|
|
||||||
dtNodeIndex* m_next;
|
|
||||||
const int m_maxNodes;
|
|
||||||
const int m_hashSize;
|
|
||||||
int m_nodeCount;
|
|
||||||
};
|
|
||||||
|
|
||||||
class dtNodeQueue
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
dtNodeQueue(int n);
|
|
||||||
~dtNodeQueue();
|
|
||||||
|
|
||||||
inline void clear() { m_size = 0; }
|
|
||||||
|
|
||||||
inline dtNode* top() { return m_heap[0]; }
|
|
||||||
|
|
||||||
inline dtNode* pop()
|
|
||||||
{
|
|
||||||
dtNode* result = m_heap[0];
|
|
||||||
m_size--;
|
|
||||||
trickleDown(0, m_heap[m_size]);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void push(dtNode* node)
|
|
||||||
{
|
|
||||||
m_size++;
|
|
||||||
bubbleUp(m_size-1, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void modify(dtNode* node)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < m_size; ++i)
|
|
||||||
{
|
|
||||||
if (m_heap[i] == node)
|
|
||||||
{
|
|
||||||
bubbleUp(i, node);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool empty() const { return m_size == 0; }
|
|
||||||
|
|
||||||
inline int getMemUsed() const
|
|
||||||
{
|
|
||||||
return sizeof(*this) +
|
|
||||||
sizeof(dtNode*) * (m_capacity + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int getCapacity() const { return m_capacity; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Explicitly disabled copy constructor and copy assignment operator.
|
|
||||||
dtNodeQueue(const dtNodeQueue&);
|
|
||||||
dtNodeQueue& operator=(const dtNodeQueue&);
|
|
||||||
|
|
||||||
void bubbleUp(int i, dtNode* node);
|
|
||||||
void trickleDown(int i, dtNode* node);
|
|
||||||
|
|
||||||
dtNode** m_heap;
|
|
||||||
const int m_capacity;
|
|
||||||
int m_size;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif // DETOURNODE_H
|
|
@ -1,65 +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 DETOURSTATUS_H
|
|
||||||
#define DETOURSTATUS_H
|
|
||||||
|
|
||||||
typedef unsigned int dtStatus;
|
|
||||||
|
|
||||||
// High level status.
|
|
||||||
static const unsigned int DT_FAILURE = 1u << 31; // Operation failed.
|
|
||||||
static const unsigned int DT_SUCCESS = 1u << 30; // Operation succeed.
|
|
||||||
static const unsigned int DT_IN_PROGRESS = 1u << 29; // Operation still in progress.
|
|
||||||
|
|
||||||
// Detail information for status.
|
|
||||||
static const unsigned int DT_STATUS_DETAIL_MASK = 0x0ffffff;
|
|
||||||
static const unsigned int DT_WRONG_MAGIC = 1 << 0; // Input data is not recognized.
|
|
||||||
static const unsigned int DT_WRONG_VERSION = 1 << 1; // Input data is in wrong version.
|
|
||||||
static const unsigned int DT_OUT_OF_MEMORY = 1 << 2; // Operation ran out of memory.
|
|
||||||
static const unsigned int DT_INVALID_PARAM = 1 << 3; // An input parameter was invalid.
|
|
||||||
static const unsigned int DT_BUFFER_TOO_SMALL = 1 << 4; // Result buffer for the query was too small to store all results.
|
|
||||||
static const unsigned int DT_OUT_OF_NODES = 1 << 5; // Query ran out of nodes during search.
|
|
||||||
static const unsigned int DT_PARTIAL_RESULT = 1 << 6; // Query did not reach the end location, returning best guess.
|
|
||||||
static const unsigned int DT_ALREADY_OCCUPIED = 1 << 7; // A tile has already been assigned to the given x,y coordinate
|
|
||||||
|
|
||||||
|
|
||||||
// Returns true of status is success.
|
|
||||||
inline bool dtStatusSucceed(dtStatus status)
|
|
||||||
{
|
|
||||||
return (status & DT_SUCCESS) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true of status is failure.
|
|
||||||
inline bool dtStatusFailed(dtStatus status)
|
|
||||||
{
|
|
||||||
return (status & DT_FAILURE) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true of status is in progress.
|
|
||||||
inline bool dtStatusInProgress(dtStatus status)
|
|
||||||
{
|
|
||||||
return (status & DT_IN_PROGRESS) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if specific detail is set.
|
|
||||||
inline bool dtStatusDetail(dtStatus status, unsigned int detail)
|
|
||||||
{
|
|
||||||
return (status & detail) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // DETOURSTATUS_H
|
|
@ -1,50 +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.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include "DetourAlloc.h"
|
|
||||||
|
|
||||||
static void *dtAllocDefault(size_t size, dtAllocHint)
|
|
||||||
{
|
|
||||||
return malloc(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dtFreeDefault(void *ptr)
|
|
||||||
{
|
|
||||||
free(ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static dtAllocFunc* sAllocFunc = dtAllocDefault;
|
|
||||||
static dtFreeFunc* sFreeFunc = dtFreeDefault;
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtFree(void* ptr)
|
|
||||||
{
|
|
||||||
if (ptr)
|
|
||||||
sFreeFunc(ptr);
|
|
||||||
}
|
|
@ -1,35 +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.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "DetourAssert.h"
|
|
||||||
|
|
||||||
#ifndef NDEBUG
|
|
||||||
|
|
||||||
static dtAssertFailFunc* sAssertFailFunc = 0;
|
|
||||||
|
|
||||||
void dtAssertFailSetCustom(dtAssertFailFunc *assertFailFunc)
|
|
||||||
{
|
|
||||||
sAssertFailFunc = assertFailFunc;
|
|
||||||
}
|
|
||||||
|
|
||||||
dtAssertFailFunc* dtAssertFailGetCustom()
|
|
||||||
{
|
|
||||||
return sAssertFailFunc;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,387 +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.
|
|
||||||
//
|
|
||||||
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
@ -1,802 +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.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <float.h>
|
|
||||||
#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<dtMeshHeader>(d, headerSize);
|
|
||||||
float* navVerts = dtGetThenAdvanceBufferPointer<float>(d, vertsSize);
|
|
||||||
dtPoly* navPolys = dtGetThenAdvanceBufferPointer<dtPoly>(d, polysSize);
|
|
||||||
d += linksSize; // Ignore links; just leave enough space for them. They'll be created on load.
|
|
||||||
dtPolyDetail* navDMeshes = dtGetThenAdvanceBufferPointer<dtPolyDetail>(d, detailMeshesSize);
|
|
||||||
float* navDVerts = dtGetThenAdvanceBufferPointer<float>(d, detailVertsSize);
|
|
||||||
unsigned char* navDTris = dtGetThenAdvanceBufferPointer<unsigned char>(d, detailTrisSize);
|
|
||||||
dtBVNode* navBvtree = dtGetThenAdvanceBufferPointer<dtBVNode>(d, bvTreeSize);
|
|
||||||
dtOffMeshConnection* offMeshCons = dtGetThenAdvanceBufferPointer<dtOffMeshConnection>(d, offMeshConsSize);
|
|
||||||
|
|
||||||
|
|
||||||
// Store header
|
|
||||||
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<float>(d, vertsSize);
|
|
||||||
dtPoly* polys = dtGetThenAdvanceBufferPointer<dtPoly>(d, polysSize);
|
|
||||||
d += linksSize; // Ignore links; they technically should be endian-swapped but all their data is overwritten on load anyway.
|
|
||||||
//dtLink* links = dtGetThenAdvanceBufferPointer<dtLink>(d, linksSize);
|
|
||||||
dtPolyDetail* detailMeshes = dtGetThenAdvanceBufferPointer<dtPolyDetail>(d, detailMeshesSize);
|
|
||||||
float* detailVerts = dtGetThenAdvanceBufferPointer<float>(d, detailVertsSize);
|
|
||||||
d += detailTrisSize; // Ignore detail tris; single bytes can't be endian-swapped.
|
|
||||||
//unsigned char* detailTris = dtGetThenAdvanceBufferPointer<unsigned char>(d, detailTrisSize);
|
|
||||||
dtBVNode* bvTree = dtGetThenAdvanceBufferPointer<dtBVNode>(d, bvtreeSize);
|
|
||||||
dtOffMeshConnection* offMeshCons = dtGetThenAdvanceBufferPointer<dtOffMeshConnection>(d, offMeshLinksSize);
|
|
||||||
|
|
||||||
// Vertices
|
|
||||||
for (int i = 0; i < header->vertCount*3; ++i)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,200 +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.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "DetourNode.h"
|
|
||||||
#include "DetourAlloc.h"
|
|
||||||
#include "DetourAssert.h"
|
|
||||||
#include "DetourCommon.h"
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#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);
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
file(GLOB SOURCES Source/*.cpp)
|
|
||||||
add_library(DetourCrowd ${SOURCES})
|
|
||||||
|
|
||||||
add_library(RecastNavigation::DetourCrowd ALIAS DetourCrowd)
|
|
||||||
set_target_properties(DetourCrowd PROPERTIES DEBUG_POSTFIX -d)
|
|
||||||
|
|
||||||
set(DetourCrowd_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Include")
|
|
||||||
|
|
||||||
target_include_directories(DetourCrowd PUBLIC
|
|
||||||
"$<BUILD_INTERFACE:${DetourCrowd_INCLUDE_DIR}>"
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(DetourCrowd
|
|
||||||
Detour
|
|
||||||
)
|
|
||||||
|
|
||||||
set_target_properties(DetourCrowd PROPERTIES
|
|
||||||
SOVERSION ${SOVERSION}
|
|
||||||
VERSION ${VERSION}
|
|
||||||
COMPILE_PDB_OUTPUT_DIRECTORY .
|
|
||||||
COMPILE_PDB_NAME "DetourCrowd-d"
|
|
||||||
)
|
|
||||||
|
|
||||||
install(TARGETS DetourCrowd
|
|
||||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
|
||||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
||||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
||||||
COMPONENT library
|
|
||||||
)
|
|
||||||
|
|
||||||
file(GLOB INCLUDES Include/*.h)
|
|
||||||
install(FILES ${INCLUDES} DESTINATION
|
|
||||||
${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation)
|
|
||||||
install(FILES "$<TARGET_FILE_DIR:DetourCrowd>/DetourCrowd-d.pdb" CONFIGURATIONS "Debug" DESTINATION "lib")
|
|
@ -1,460 +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 DETOURCROWD_H
|
|
||||||
#define DETOURCROWD_H
|
|
||||||
|
|
||||||
#include "DetourNavMeshQuery.h"
|
|
||||||
#include "DetourObstacleAvoidance.h"
|
|
||||||
#include "DetourLocalBoundary.h"
|
|
||||||
#include "DetourPathCorridor.h"
|
|
||||||
#include "DetourProximityGrid.h"
|
|
||||||
#include "DetourPathQueue.h"
|
|
||||||
|
|
||||||
/// The maximum number of neighbors that a crowd agent can take into account
|
|
||||||
/// for steering decisions.
|
|
||||||
/// @ingroup crowd
|
|
||||||
static const int DT_CROWDAGENT_MAX_NEIGHBOURS = 6;
|
|
||||||
|
|
||||||
/// The maximum number of corners a crowd agent will look ahead in the path.
|
|
||||||
/// This value is used for sizing the crowd agent corner buffers.
|
|
||||||
/// Due to the behavior of the crowd manager, the actual number of useful
|
|
||||||
/// corners will be one less than this number.
|
|
||||||
/// @ingroup crowd
|
|
||||||
static const int DT_CROWDAGENT_MAX_CORNERS = 4;
|
|
||||||
|
|
||||||
/// The maximum number of crowd avoidance configurations supported by the
|
|
||||||
/// crowd manager.
|
|
||||||
/// @ingroup crowd
|
|
||||||
/// @see dtObstacleAvoidanceParams, dtCrowd::setObstacleAvoidanceParams(), dtCrowd::getObstacleAvoidanceParams(),
|
|
||||||
/// dtCrowdAgentParams::obstacleAvoidanceType
|
|
||||||
static const int DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS = 8;
|
|
||||||
|
|
||||||
/// The maximum number of query filter types supported by the crowd manager.
|
|
||||||
/// @ingroup crowd
|
|
||||||
/// @see dtQueryFilter, dtCrowd::getFilter() dtCrowd::getEditableFilter(),
|
|
||||||
/// dtCrowdAgentParams::queryFilterType
|
|
||||||
static const int DT_CROWD_MAX_QUERY_FILTER_TYPE = 16;
|
|
||||||
|
|
||||||
/// Provides neighbor data for agents managed by the crowd.
|
|
||||||
/// @ingroup crowd
|
|
||||||
/// @see dtCrowdAgent::neis, dtCrowd
|
|
||||||
struct dtCrowdNeighbour
|
|
||||||
{
|
|
||||||
int idx; ///< The index of the neighbor in the crowd.
|
|
||||||
float dist; ///< The distance between the current agent and the neighbor.
|
|
||||||
};
|
|
||||||
|
|
||||||
/// The type of navigation mesh polygon the agent is currently traversing.
|
|
||||||
/// @ingroup crowd
|
|
||||||
enum CrowdAgentState
|
|
||||||
{
|
|
||||||
DT_CROWDAGENT_STATE_INVALID, ///< The agent is not in a valid state.
|
|
||||||
DT_CROWDAGENT_STATE_WALKING, ///< The agent is traversing a normal navigation mesh polygon.
|
|
||||||
DT_CROWDAGENT_STATE_OFFMESH, ///< The agent is traversing an off-mesh connection.
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Configuration parameters for a crowd agent.
|
|
||||||
/// @ingroup crowd
|
|
||||||
struct dtCrowdAgentParams
|
|
||||||
{
|
|
||||||
float radius; ///< Agent radius. [Limit: >= 0]
|
|
||||||
float height; ///< Agent height. [Limit: > 0]
|
|
||||||
float maxAcceleration; ///< Maximum allowed acceleration. [Limit: >= 0]
|
|
||||||
float maxSpeed; ///< Maximum allowed speed. [Limit: >= 0]
|
|
||||||
|
|
||||||
/// Defines how close a collision element must be before it is considered for steering behaviors. [Limits: > 0]
|
|
||||||
float collisionQueryRange;
|
|
||||||
|
|
||||||
float pathOptimizationRange; ///< The path visibility optimization range. [Limit: > 0]
|
|
||||||
|
|
||||||
/// How aggresive the agent manager should be at avoiding collisions with this agent. [Limit: >= 0]
|
|
||||||
float separationWeight;
|
|
||||||
|
|
||||||
/// Flags that impact steering behavior. (See: #UpdateFlags)
|
|
||||||
unsigned char updateFlags;
|
|
||||||
|
|
||||||
/// The index of the avoidance configuration to use for the agent.
|
|
||||||
/// [Limits: 0 <= value <= #DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS]
|
|
||||||
unsigned char obstacleAvoidanceType;
|
|
||||||
|
|
||||||
/// The index of the query filter used by this agent.
|
|
||||||
unsigned char queryFilterType;
|
|
||||||
|
|
||||||
/// User defined data attached to the agent.
|
|
||||||
void* userData;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum MoveRequestState
|
|
||||||
{
|
|
||||||
DT_CROWDAGENT_TARGET_NONE = 0,
|
|
||||||
DT_CROWDAGENT_TARGET_FAILED,
|
|
||||||
DT_CROWDAGENT_TARGET_VALID,
|
|
||||||
DT_CROWDAGENT_TARGET_REQUESTING,
|
|
||||||
DT_CROWDAGENT_TARGET_WAITING_FOR_QUEUE,
|
|
||||||
DT_CROWDAGENT_TARGET_WAITING_FOR_PATH,
|
|
||||||
DT_CROWDAGENT_TARGET_VELOCITY,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Represents an agent managed by a #dtCrowd object.
|
|
||||||
/// @ingroup crowd
|
|
||||||
struct dtCrowdAgent
|
|
||||||
{
|
|
||||||
/// True if the agent is active, false if the agent is in an unused slot in the agent pool.
|
|
||||||
bool active;
|
|
||||||
|
|
||||||
/// The type of mesh polygon the agent is traversing. (See: #CrowdAgentState)
|
|
||||||
unsigned char state;
|
|
||||||
|
|
||||||
/// True if the agent has valid path (targetState == DT_CROWDAGENT_TARGET_VALID) and the path does not lead to the requested position, else false.
|
|
||||||
bool partial;
|
|
||||||
|
|
||||||
/// The path corridor the agent is using.
|
|
||||||
dtPathCorridor corridor;
|
|
||||||
|
|
||||||
/// The local boundary data for the agent.
|
|
||||||
dtLocalBoundary boundary;
|
|
||||||
|
|
||||||
/// Time since the agent's path corridor was optimized.
|
|
||||||
float topologyOptTime;
|
|
||||||
|
|
||||||
/// The known neighbors of the agent.
|
|
||||||
dtCrowdNeighbour neis[DT_CROWDAGENT_MAX_NEIGHBOURS];
|
|
||||||
|
|
||||||
/// The number of neighbors.
|
|
||||||
int nneis;
|
|
||||||
|
|
||||||
/// The desired speed.
|
|
||||||
float desiredSpeed;
|
|
||||||
|
|
||||||
float npos[3]; ///< The current agent position. [(x, y, z)]
|
|
||||||
float disp[3]; ///< A temporary value used to accumulate agent displacement during iterative collision resolution. [(x, y, z)]
|
|
||||||
float dvel[3]; ///< The desired velocity of the agent. Based on the current path, calculated from scratch each frame. [(x, y, z)]
|
|
||||||
float nvel[3]; ///< The desired velocity adjusted by obstacle avoidance, calculated from scratch each frame. [(x, y, z)]
|
|
||||||
float vel[3]; ///< The actual velocity of the agent. The change from nvel -> vel is constrained by max acceleration. [(x, y, z)]
|
|
||||||
|
|
||||||
/// The agent's configuration parameters.
|
|
||||||
dtCrowdAgentParams params;
|
|
||||||
|
|
||||||
/// The local path corridor corners for the agent. (Staight path.) [(x, y, z) * #ncorners]
|
|
||||||
float cornerVerts[DT_CROWDAGENT_MAX_CORNERS*3];
|
|
||||||
|
|
||||||
/// The local path corridor corner flags. (See: #dtStraightPathFlags) [(flags) * #ncorners]
|
|
||||||
unsigned char cornerFlags[DT_CROWDAGENT_MAX_CORNERS];
|
|
||||||
|
|
||||||
/// The reference id of the polygon being entered at the corner. [(polyRef) * #ncorners]
|
|
||||||
dtPolyRef cornerPolys[DT_CROWDAGENT_MAX_CORNERS];
|
|
||||||
|
|
||||||
/// The number of corners.
|
|
||||||
int ncorners;
|
|
||||||
|
|
||||||
unsigned char targetState; ///< State of the movement request.
|
|
||||||
dtPolyRef targetRef; ///< Target polyref of the movement request.
|
|
||||||
float targetPos[3]; ///< Target position of the movement request (or velocity in case of DT_CROWDAGENT_TARGET_VELOCITY).
|
|
||||||
dtPathQueueRef targetPathqRef; ///< Path finder ref.
|
|
||||||
bool targetReplan; ///< Flag indicating that the current path is being replanned.
|
|
||||||
float targetReplanTime; /// <Time since the agent's target was replanned.
|
|
||||||
};
|
|
||||||
|
|
||||||
struct dtCrowdAgentAnimation
|
|
||||||
{
|
|
||||||
bool active;
|
|
||||||
float initPos[3], startPos[3], endPos[3];
|
|
||||||
dtPolyRef polyRef;
|
|
||||||
float t, tmax;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Crowd agent update flags.
|
|
||||||
/// @ingroup crowd
|
|
||||||
/// @see dtCrowdAgentParams::updateFlags
|
|
||||||
enum UpdateFlags
|
|
||||||
{
|
|
||||||
DT_CROWD_ANTICIPATE_TURNS = 1,
|
|
||||||
DT_CROWD_OBSTACLE_AVOIDANCE = 2,
|
|
||||||
DT_CROWD_SEPARATION = 4,
|
|
||||||
DT_CROWD_OPTIMIZE_VIS = 8, ///< Use #dtPathCorridor::optimizePathVisibility() to optimize the agent path.
|
|
||||||
DT_CROWD_OPTIMIZE_TOPO = 16, ///< Use dtPathCorridor::optimizePathTopology() to optimize the agent path.
|
|
||||||
};
|
|
||||||
|
|
||||||
struct dtCrowdAgentDebugInfo
|
|
||||||
{
|
|
||||||
int idx;
|
|
||||||
float optStart[3], optEnd[3];
|
|
||||||
dtObstacleAvoidanceDebugData* vod;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Provides local steering behaviors for a group of agents.
|
|
||||||
/// @ingroup crowd
|
|
||||||
class dtCrowd
|
|
||||||
{
|
|
||||||
int m_maxAgents;
|
|
||||||
dtCrowdAgent* m_agents;
|
|
||||||
dtCrowdAgent** m_activeAgents;
|
|
||||||
dtCrowdAgentAnimation* m_agentAnims;
|
|
||||||
|
|
||||||
dtPathQueue m_pathq;
|
|
||||||
|
|
||||||
dtObstacleAvoidanceParams m_obstacleQueryParams[DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS];
|
|
||||||
dtObstacleAvoidanceQuery* m_obstacleQuery;
|
|
||||||
|
|
||||||
dtProximityGrid* m_grid;
|
|
||||||
|
|
||||||
dtPolyRef* m_pathResult;
|
|
||||||
int m_maxPathResult;
|
|
||||||
|
|
||||||
float m_agentPlacementHalfExtents[3];
|
|
||||||
|
|
||||||
dtQueryFilter m_filters[DT_CROWD_MAX_QUERY_FILTER_TYPE];
|
|
||||||
|
|
||||||
float m_maxAgentRadius;
|
|
||||||
|
|
||||||
int m_velocitySampleCount;
|
|
||||||
|
|
||||||
dtNavMeshQuery* m_navquery;
|
|
||||||
|
|
||||||
void updateTopologyOptimization(dtCrowdAgent** agents, const int nagents, const float dt);
|
|
||||||
void updateMoveRequest(const float dt);
|
|
||||||
void checkPathValidity(dtCrowdAgent** agents, const int nagents, const float dt);
|
|
||||||
|
|
||||||
inline int getAgentIndex(const dtCrowdAgent* agent) const { return (int)(agent - m_agents); }
|
|
||||||
|
|
||||||
bool requestMoveTargetReplan(const int idx, dtPolyRef ref, const float* pos);
|
|
||||||
|
|
||||||
void purge();
|
|
||||||
|
|
||||||
public:
|
|
||||||
dtCrowd();
|
|
||||||
~dtCrowd();
|
|
||||||
|
|
||||||
/// Initializes the crowd.
|
|
||||||
/// @param[in] maxAgents The maximum number of agents the crowd can manage. [Limit: >= 1]
|
|
||||||
/// @param[in] maxAgentRadius The maximum radius of any agent that will be added to the crowd. [Limit: > 0]
|
|
||||||
/// @param[in] nav The navigation mesh to use for planning.
|
|
||||||
/// @return True if the initialization succeeded.
|
|
||||||
bool init(const int maxAgents, const float maxAgentRadius, dtNavMesh* nav);
|
|
||||||
|
|
||||||
/// Sets the shared avoidance configuration for the specified index.
|
|
||||||
/// @param[in] idx The index. [Limits: 0 <= value < #DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS]
|
|
||||||
/// @param[in] params The new configuration.
|
|
||||||
void setObstacleAvoidanceParams(const int idx, const dtObstacleAvoidanceParams* params);
|
|
||||||
|
|
||||||
/// Gets the shared avoidance configuration for the specified index.
|
|
||||||
/// @param[in] idx The index of the configuration to retreive.
|
|
||||||
/// [Limits: 0 <= value < #DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS]
|
|
||||||
/// @return The requested configuration.
|
|
||||||
const dtObstacleAvoidanceParams* getObstacleAvoidanceParams(const int idx) const;
|
|
||||||
|
|
||||||
/// Gets the specified agent from the pool.
|
|
||||||
/// @param[in] idx The agent index. [Limits: 0 <= value < #getAgentCount()]
|
|
||||||
/// @return The requested agent.
|
|
||||||
const dtCrowdAgent* getAgent(const int idx);
|
|
||||||
|
|
||||||
/// Gets the specified agent from the pool.
|
|
||||||
/// @param[in] idx The agent index. [Limits: 0 <= value < #getAgentCount()]
|
|
||||||
/// @return The requested agent.
|
|
||||||
dtCrowdAgent* getEditableAgent(const int idx);
|
|
||||||
|
|
||||||
/// The maximum number of agents that can be managed by the object.
|
|
||||||
/// @return The maximum number of agents.
|
|
||||||
int getAgentCount() const;
|
|
||||||
|
|
||||||
/// Adds a new agent to the crowd.
|
|
||||||
/// @param[in] pos The requested position of the agent. [(x, y, z)]
|
|
||||||
/// @param[in] params The configutation of the agent.
|
|
||||||
/// @return The index of the agent in the agent pool. Or -1 if the agent could not be added.
|
|
||||||
int addAgent(const float* pos, const dtCrowdAgentParams* params);
|
|
||||||
|
|
||||||
/// Updates the specified agent's configuration.
|
|
||||||
/// @param[in] idx The agent index. [Limits: 0 <= value < #getAgentCount()]
|
|
||||||
/// @param[in] params The new agent configuration.
|
|
||||||
void updateAgentParameters(const int idx, const dtCrowdAgentParams* params);
|
|
||||||
|
|
||||||
/// Removes the agent from the crowd.
|
|
||||||
/// @param[in] idx The agent index. [Limits: 0 <= value < #getAgentCount()]
|
|
||||||
void removeAgent(const int idx);
|
|
||||||
|
|
||||||
/// Submits a new move request for the specified agent.
|
|
||||||
/// @param[in] idx The agent index. [Limits: 0 <= value < #getAgentCount()]
|
|
||||||
/// @param[in] ref The position's polygon reference.
|
|
||||||
/// @param[in] pos The position within the polygon. [(x, y, z)]
|
|
||||||
/// @return True if the request was successfully submitted.
|
|
||||||
bool requestMoveTarget(const int idx, dtPolyRef ref, const float* pos);
|
|
||||||
|
|
||||||
/// Submits a new move request for the specified agent.
|
|
||||||
/// @param[in] idx The agent index. [Limits: 0 <= value < #getAgentCount()]
|
|
||||||
/// @param[in] vel The movement velocity. [(x, y, z)]
|
|
||||||
/// @return True if the request was successfully submitted.
|
|
||||||
bool requestMoveVelocity(const int idx, const float* vel);
|
|
||||||
|
|
||||||
/// Resets any request for the specified agent.
|
|
||||||
/// @param[in] idx The agent index. [Limits: 0 <= value < #getAgentCount()]
|
|
||||||
/// @return True if the request was successfully reseted.
|
|
||||||
bool resetMoveTarget(const int idx);
|
|
||||||
|
|
||||||
/// Gets the active agents int the agent pool.
|
|
||||||
/// @param[out] agents An array of agent pointers. [(#dtCrowdAgent *) * maxAgents]
|
|
||||||
/// @param[in] maxAgents The size of the crowd agent array.
|
|
||||||
/// @return The number of agents returned in @p agents.
|
|
||||||
int getActiveAgents(dtCrowdAgent** agents, const int maxAgents);
|
|
||||||
|
|
||||||
/// Updates the steering and positions of all agents.
|
|
||||||
/// @param[in] dt The time, in seconds, to update the simulation. [Limit: > 0]
|
|
||||||
/// @param[out] debug A debug object to load with debug information. [Opt]
|
|
||||||
void update(const float dt, dtCrowdAgentDebugInfo* debug);
|
|
||||||
|
|
||||||
/// Gets the filter used by the crowd.
|
|
||||||
/// @return The filter used by the crowd.
|
|
||||||
inline const dtQueryFilter* getFilter(const int i) const { return (i >= 0 && i < DT_CROWD_MAX_QUERY_FILTER_TYPE) ? &m_filters[i] : 0; }
|
|
||||||
|
|
||||||
/// Gets the filter used by the crowd.
|
|
||||||
/// @return The filter used by the crowd.
|
|
||||||
inline dtQueryFilter* getEditableFilter(const int i) { return (i >= 0 && i < DT_CROWD_MAX_QUERY_FILTER_TYPE) ? &m_filters[i] : 0; }
|
|
||||||
|
|
||||||
/// Gets the search halfExtents [(x, y, z)] used by the crowd for query operations.
|
|
||||||
/// @return The search halfExtents used by the crowd. [(x, y, z)]
|
|
||||||
const float* getQueryHalfExtents() const { return m_agentPlacementHalfExtents; }
|
|
||||||
|
|
||||||
/// Same as getQueryHalfExtents. Left to maintain backwards compatibility.
|
|
||||||
/// @return The search halfExtents used by the crowd. [(x, y, z)]
|
|
||||||
const float* getQueryExtents() const { return m_agentPlacementHalfExtents; }
|
|
||||||
|
|
||||||
/// Gets the velocity sample count.
|
|
||||||
/// @return The velocity sample count.
|
|
||||||
inline int getVelocitySampleCount() const { return m_velocitySampleCount; }
|
|
||||||
|
|
||||||
/// Gets the crowd's proximity grid.
|
|
||||||
/// @return The crowd's proximity grid.
|
|
||||||
const dtProximityGrid* getGrid() const { return m_grid; }
|
|
||||||
|
|
||||||
/// Gets the crowd's path request queue.
|
|
||||||
/// @return The crowd's path request queue.
|
|
||||||
const dtPathQueue* getPathQueue() const { return &m_pathq; }
|
|
||||||
|
|
||||||
/// Gets the query object used by the crowd.
|
|
||||||
const dtNavMeshQuery* getNavMeshQuery() const { return m_navquery; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Explicitly disabled copy constructor and copy assignment operator.
|
|
||||||
dtCrowd(const dtCrowd&);
|
|
||||||
dtCrowd& operator=(const dtCrowd&);
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Allocates a crowd object using the Detour allocator.
|
|
||||||
/// @return A crowd object that is ready for initialization, or null on failure.
|
|
||||||
/// @ingroup crowd
|
|
||||||
dtCrowd* dtAllocCrowd();
|
|
||||||
|
|
||||||
/// Frees the specified crowd object using the Detour allocator.
|
|
||||||
/// @param[in] ptr A crowd object allocated using #dtAllocCrowd
|
|
||||||
/// @ingroup crowd
|
|
||||||
void dtFreeCrowd(dtCrowd* ptr);
|
|
||||||
|
|
||||||
|
|
||||||
#endif // DETOURCROWD_H
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// This section contains detailed documentation for members that don't have
|
|
||||||
// a source file. It reduces clutter in the main section of the header.
|
|
||||||
|
|
||||||
/**
|
|
||||||
|
|
||||||
@defgroup crowd Crowd
|
|
||||||
|
|
||||||
Members in this module implement local steering and dynamic avoidance features.
|
|
||||||
|
|
||||||
The crowd is the big beast of the navigation features. It not only handles a
|
|
||||||
lot of the path management for you, but also local steering and dynamic
|
|
||||||
avoidance between members of the crowd. I.e. It can keep your agents from
|
|
||||||
running into each other.
|
|
||||||
|
|
||||||
Main class: #dtCrowd
|
|
||||||
|
|
||||||
The #dtNavMeshQuery and #dtPathCorridor classes provide perfectly good, easy
|
|
||||||
to use path planning features. But in the end they only give you points that
|
|
||||||
your navigation client should be moving toward. When it comes to deciding things
|
|
||||||
like agent velocity and steering to avoid other agents, that is up to you to
|
|
||||||
implement. Unless, of course, you decide to use #dtCrowd.
|
|
||||||
|
|
||||||
Basically, you add an agent to the crowd, providing various configuration
|
|
||||||
settings such as maximum speed and acceleration. You also provide a local
|
|
||||||
target to more toward. The crowd manager then provides, with every update, the
|
|
||||||
new agent position and velocity for the frame. The movement will be
|
|
||||||
constrained to the navigation mesh, and steering will be applied to ensure
|
|
||||||
agents managed by the crowd do not collide with each other.
|
|
||||||
|
|
||||||
This is very powerful feature set. But it comes with limitations.
|
|
||||||
|
|
||||||
The biggest limitation is that you must give control of the agent's position
|
|
||||||
completely over to the crowd manager. You can update things like maximum speed
|
|
||||||
and acceleration. But in order for the crowd manager to do its thing, it can't
|
|
||||||
allow you to constantly be giving it overrides to position and velocity. So
|
|
||||||
you give up direct control of the agent's movement. It belongs to the crowd.
|
|
||||||
|
|
||||||
The second biggest limitation revolves around the fact that the crowd manager
|
|
||||||
deals with local planning. So the agent's target should never be more than
|
|
||||||
256 polygons aways from its current position. If it is, you risk
|
|
||||||
your agent failing to reach its target. So you may still need to do long
|
|
||||||
distance planning and provide the crowd manager with intermediate targets.
|
|
||||||
|
|
||||||
Other significant limitations:
|
|
||||||
|
|
||||||
- All agents using the crowd manager will use the same #dtQueryFilter.
|
|
||||||
- Crowd management is relatively expensive. The maximum agents under crowd
|
|
||||||
management at any one time is between 20 and 30. A good place to start
|
|
||||||
is a maximum of 25 agents for 0.5ms per frame.
|
|
||||||
|
|
||||||
@note This is a summary list of members. Use the index or search
|
|
||||||
feature to find minor members.
|
|
||||||
|
|
||||||
@struct dtCrowdAgentParams
|
|
||||||
@see dtCrowdAgent, dtCrowd::addAgent(), dtCrowd::updateAgentParameters()
|
|
||||||
|
|
||||||
@var dtCrowdAgentParams::obstacleAvoidanceType
|
|
||||||
@par
|
|
||||||
|
|
||||||
#dtCrowd permits agents to use different avoidance configurations. This value
|
|
||||||
is the index of the #dtObstacleAvoidanceParams within the crowd.
|
|
||||||
|
|
||||||
@see dtObstacleAvoidanceParams, dtCrowd::setObstacleAvoidanceParams(),
|
|
||||||
dtCrowd::getObstacleAvoidanceParams()
|
|
||||||
|
|
||||||
@var dtCrowdAgentParams::collisionQueryRange
|
|
||||||
@par
|
|
||||||
|
|
||||||
Collision elements include other agents and navigation mesh boundaries.
|
|
||||||
|
|
||||||
This value is often based on the agent radius and/or maximum speed. E.g. radius * 8
|
|
||||||
|
|
||||||
@var dtCrowdAgentParams::pathOptimizationRange
|
|
||||||
@par
|
|
||||||
|
|
||||||
Only applicalbe if #updateFlags includes the #DT_CROWD_OPTIMIZE_VIS flag.
|
|
||||||
|
|
||||||
This value is often based on the agent radius. E.g. radius * 30
|
|
||||||
|
|
||||||
@see dtPathCorridor::optimizePathVisibility()
|
|
||||||
|
|
||||||
@var dtCrowdAgentParams::separationWeight
|
|
||||||
@par
|
|
||||||
|
|
||||||
A higher value will result in agents trying to stay farther away from each other at
|
|
||||||
the cost of more difficult steering in tight spaces.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
@ -1,66 +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 DETOURLOCALBOUNDARY_H
|
|
||||||
#define DETOURLOCALBOUNDARY_H
|
|
||||||
|
|
||||||
#include "DetourNavMeshQuery.h"
|
|
||||||
|
|
||||||
|
|
||||||
class dtLocalBoundary
|
|
||||||
{
|
|
||||||
static const int MAX_LOCAL_SEGS = 8;
|
|
||||||
static const int MAX_LOCAL_POLYS = 16;
|
|
||||||
|
|
||||||
struct Segment
|
|
||||||
{
|
|
||||||
float s[6]; ///< Segment start/end
|
|
||||||
float d; ///< Distance for pruning.
|
|
||||||
};
|
|
||||||
|
|
||||||
float m_center[3];
|
|
||||||
Segment m_segs[MAX_LOCAL_SEGS];
|
|
||||||
int m_nsegs;
|
|
||||||
|
|
||||||
dtPolyRef m_polys[MAX_LOCAL_POLYS];
|
|
||||||
int m_npolys;
|
|
||||||
|
|
||||||
void addSegment(const float dist, const float* s);
|
|
||||||
|
|
||||||
public:
|
|
||||||
dtLocalBoundary();
|
|
||||||
~dtLocalBoundary();
|
|
||||||
|
|
||||||
void reset();
|
|
||||||
|
|
||||||
void update(dtPolyRef ref, const float* pos, const float collisionQueryRange,
|
|
||||||
dtNavMeshQuery* navquery, const dtQueryFilter* filter);
|
|
||||||
|
|
||||||
bool isValid(dtNavMeshQuery* navquery, const dtQueryFilter* filter);
|
|
||||||
|
|
||||||
inline const float* getCenter() const { return m_center; }
|
|
||||||
inline int getSegmentCount() const { return m_nsegs; }
|
|
||||||
inline const float* getSegment(int i) const { return m_segs[i].s; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Explicitly disabled copy constructor and copy assignment operator.
|
|
||||||
dtLocalBoundary(const dtLocalBoundary&);
|
|
||||||
dtLocalBoundary& operator=(const dtLocalBoundary&);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // DETOURLOCALBOUNDARY_H
|
|
@ -1,159 +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 DETOUROBSTACLEAVOIDANCE_H
|
|
||||||
#define DETOUROBSTACLEAVOIDANCE_H
|
|
||||||
|
|
||||||
struct dtObstacleCircle
|
|
||||||
{
|
|
||||||
float p[3]; ///< Position of the obstacle
|
|
||||||
float vel[3]; ///< Velocity of the obstacle
|
|
||||||
float dvel[3]; ///< Velocity of the obstacle
|
|
||||||
float rad; ///< Radius of the obstacle
|
|
||||||
float dp[3], np[3]; ///< Use for side selection during sampling.
|
|
||||||
};
|
|
||||||
|
|
||||||
struct dtObstacleSegment
|
|
||||||
{
|
|
||||||
float p[3], q[3]; ///< End points of the obstacle segment
|
|
||||||
bool touch;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class dtObstacleAvoidanceDebugData
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
dtObstacleAvoidanceDebugData();
|
|
||||||
~dtObstacleAvoidanceDebugData();
|
|
||||||
|
|
||||||
bool init(const int maxSamples);
|
|
||||||
void reset();
|
|
||||||
void addSample(const float* vel, const float ssize, const float pen,
|
|
||||||
const float vpen, const float vcpen, const float spen, const float tpen);
|
|
||||||
|
|
||||||
void normalizeSamples();
|
|
||||||
|
|
||||||
inline int getSampleCount() const { return m_nsamples; }
|
|
||||||
inline const float* getSampleVelocity(const int i) const { return &m_vel[i*3]; }
|
|
||||||
inline float getSampleSize(const int i) const { return m_ssize[i]; }
|
|
||||||
inline float getSamplePenalty(const int i) const { return m_pen[i]; }
|
|
||||||
inline float getSampleDesiredVelocityPenalty(const int i) const { return m_vpen[i]; }
|
|
||||||
inline float getSampleCurrentVelocityPenalty(const int i) const { return m_vcpen[i]; }
|
|
||||||
inline float getSamplePreferredSidePenalty(const int i) const { return m_spen[i]; }
|
|
||||||
inline float getSampleCollisionTimePenalty(const int i) const { return m_tpen[i]; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Explicitly disabled copy constructor and copy assignment operator.
|
|
||||||
dtObstacleAvoidanceDebugData(const dtObstacleAvoidanceDebugData&);
|
|
||||||
dtObstacleAvoidanceDebugData& operator=(const dtObstacleAvoidanceDebugData&);
|
|
||||||
|
|
||||||
int m_nsamples;
|
|
||||||
int m_maxSamples;
|
|
||||||
float* m_vel;
|
|
||||||
float* m_ssize;
|
|
||||||
float* m_pen;
|
|
||||||
float* m_vpen;
|
|
||||||
float* m_vcpen;
|
|
||||||
float* m_spen;
|
|
||||||
float* m_tpen;
|
|
||||||
};
|
|
||||||
|
|
||||||
dtObstacleAvoidanceDebugData* dtAllocObstacleAvoidanceDebugData();
|
|
||||||
void dtFreeObstacleAvoidanceDebugData(dtObstacleAvoidanceDebugData* ptr);
|
|
||||||
|
|
||||||
|
|
||||||
static const int DT_MAX_PATTERN_DIVS = 32; ///< Max numver of adaptive divs.
|
|
||||||
static const int DT_MAX_PATTERN_RINGS = 4; ///< Max number of adaptive rings.
|
|
||||||
|
|
||||||
struct dtObstacleAvoidanceParams
|
|
||||||
{
|
|
||||||
float velBias;
|
|
||||||
float weightDesVel;
|
|
||||||
float weightCurVel;
|
|
||||||
float weightSide;
|
|
||||||
float weightToi;
|
|
||||||
float horizTime;
|
|
||||||
unsigned char gridSize; ///< grid
|
|
||||||
unsigned char adaptiveDivs; ///< adaptive
|
|
||||||
unsigned char adaptiveRings; ///< adaptive
|
|
||||||
unsigned char adaptiveDepth; ///< adaptive
|
|
||||||
};
|
|
||||||
|
|
||||||
class dtObstacleAvoidanceQuery
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
dtObstacleAvoidanceQuery();
|
|
||||||
~dtObstacleAvoidanceQuery();
|
|
||||||
|
|
||||||
bool init(const int maxCircles, const int maxSegments);
|
|
||||||
|
|
||||||
void reset();
|
|
||||||
|
|
||||||
void addCircle(const float* pos, const float rad,
|
|
||||||
const float* vel, const float* dvel);
|
|
||||||
|
|
||||||
void addSegment(const float* p, const float* q);
|
|
||||||
|
|
||||||
int sampleVelocityGrid(const float* pos, const float rad, const float vmax,
|
|
||||||
const float* vel, const float* dvel, float* nvel,
|
|
||||||
const dtObstacleAvoidanceParams* params,
|
|
||||||
dtObstacleAvoidanceDebugData* debug = 0);
|
|
||||||
|
|
||||||
int sampleVelocityAdaptive(const float* pos, const float rad, const float vmax,
|
|
||||||
const float* vel, const float* dvel, float* nvel,
|
|
||||||
const dtObstacleAvoidanceParams* params,
|
|
||||||
dtObstacleAvoidanceDebugData* debug = 0);
|
|
||||||
|
|
||||||
inline int getObstacleCircleCount() const { return m_ncircles; }
|
|
||||||
const dtObstacleCircle* getObstacleCircle(const int i) { return &m_circles[i]; }
|
|
||||||
|
|
||||||
inline int getObstacleSegmentCount() const { return m_nsegments; }
|
|
||||||
const dtObstacleSegment* getObstacleSegment(const int i) { return &m_segments[i]; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Explicitly disabled copy constructor and copy assignment operator.
|
|
||||||
dtObstacleAvoidanceQuery(const dtObstacleAvoidanceQuery&);
|
|
||||||
dtObstacleAvoidanceQuery& operator=(const dtObstacleAvoidanceQuery&);
|
|
||||||
|
|
||||||
void prepare(const float* pos, const float* dvel);
|
|
||||||
|
|
||||||
float processSample(const float* vcand, const float cs,
|
|
||||||
const float* pos, const float rad,
|
|
||||||
const float* vel, const float* dvel,
|
|
||||||
const float minPenalty,
|
|
||||||
dtObstacleAvoidanceDebugData* debug);
|
|
||||||
|
|
||||||
dtObstacleAvoidanceParams m_params;
|
|
||||||
float m_invHorizTime;
|
|
||||||
float m_vmax;
|
|
||||||
float m_invVmax;
|
|
||||||
|
|
||||||
int m_maxCircles;
|
|
||||||
dtObstacleCircle* m_circles;
|
|
||||||
int m_ncircles;
|
|
||||||
|
|
||||||
int m_maxSegments;
|
|
||||||
dtObstacleSegment* m_segments;
|
|
||||||
int m_nsegments;
|
|
||||||
};
|
|
||||||
|
|
||||||
dtObstacleAvoidanceQuery* dtAllocObstacleAvoidanceQuery();
|
|
||||||
void dtFreeObstacleAvoidanceQuery(dtObstacleAvoidanceQuery* ptr);
|
|
||||||
|
|
||||||
|
|
||||||
#endif // DETOUROBSTACLEAVOIDANCE_H
|
|
@ -1,151 +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 DETOUTPATHCORRIDOR_H
|
|
||||||
#define DETOUTPATHCORRIDOR_H
|
|
||||||
|
|
||||||
#include "DetourNavMeshQuery.h"
|
|
||||||
|
|
||||||
/// Represents a dynamic polygon corridor used to plan agent movement.
|
|
||||||
/// @ingroup crowd, detour
|
|
||||||
class dtPathCorridor
|
|
||||||
{
|
|
||||||
float m_pos[3];
|
|
||||||
float m_target[3];
|
|
||||||
|
|
||||||
dtPolyRef* m_path;
|
|
||||||
int m_npath;
|
|
||||||
int m_maxPath;
|
|
||||||
|
|
||||||
public:
|
|
||||||
dtPathCorridor();
|
|
||||||
~dtPathCorridor();
|
|
||||||
|
|
||||||
/// Allocates the corridor's path buffer.
|
|
||||||
/// @param[in] maxPath The maximum path size the corridor can handle.
|
|
||||||
/// @return True if the initialization succeeded.
|
|
||||||
bool init(const int maxPath);
|
|
||||||
|
|
||||||
/// Resets the path corridor to the specified position.
|
|
||||||
/// @param[in] ref The polygon reference containing the position.
|
|
||||||
/// @param[in] pos The new position in the corridor. [(x, y, z)]
|
|
||||||
void reset(dtPolyRef ref, const float* pos);
|
|
||||||
|
|
||||||
/// Finds the corners in the corridor from the position toward the target. (The straightened path.)
|
|
||||||
/// @param[out] cornerVerts The corner vertices. [(x, y, z) * cornerCount] [Size: <= maxCorners]
|
|
||||||
/// @param[out] cornerFlags The flag for each corner. [(flag) * cornerCount] [Size: <= maxCorners]
|
|
||||||
/// @param[out] cornerPolys The polygon reference for each corner. [(polyRef) * cornerCount]
|
|
||||||
/// [Size: <= @p maxCorners]
|
|
||||||
/// @param[in] maxCorners The maximum number of corners the buffers can hold.
|
|
||||||
/// @param[in] navquery The query object used to build the corridor.
|
|
||||||
/// @param[in] filter The filter to apply to the operation.
|
|
||||||
/// @return The number of corners returned in the corner buffers. [0 <= value <= @p maxCorners]
|
|
||||||
int findCorners(float* cornerVerts, unsigned char* cornerFlags,
|
|
||||||
dtPolyRef* cornerPolys, const int maxCorners,
|
|
||||||
dtNavMeshQuery* navquery, const dtQueryFilter* filter);
|
|
||||||
|
|
||||||
/// Attempts to optimize the path if the specified point is visible from the current position.
|
|
||||||
/// @param[in] next The point to search toward. [(x, y, z])
|
|
||||||
/// @param[in] pathOptimizationRange The maximum range to search. [Limit: > 0]
|
|
||||||
/// @param[in] navquery The query object used to build the corridor.
|
|
||||||
/// @param[in] filter The filter to apply to the operation.
|
|
||||||
void optimizePathVisibility(const float* next, const float pathOptimizationRange,
|
|
||||||
dtNavMeshQuery* navquery, const dtQueryFilter* filter);
|
|
||||||
|
|
||||||
/// Attempts to optimize the path using a local area search. (Partial replanning.)
|
|
||||||
/// @param[in] navquery The query object used to build the corridor.
|
|
||||||
/// @param[in] filter The filter to apply to the operation.
|
|
||||||
bool optimizePathTopology(dtNavMeshQuery* navquery, const dtQueryFilter* filter);
|
|
||||||
|
|
||||||
bool moveOverOffmeshConnection(dtPolyRef offMeshConRef, dtPolyRef* refs,
|
|
||||||
float* startPos, float* endPos,
|
|
||||||
dtNavMeshQuery* navquery);
|
|
||||||
|
|
||||||
bool fixPathStart(dtPolyRef safeRef, const float* safePos);
|
|
||||||
|
|
||||||
bool trimInvalidPath(dtPolyRef safeRef, const float* safePos,
|
|
||||||
dtNavMeshQuery* navquery, const dtQueryFilter* filter);
|
|
||||||
|
|
||||||
/// Checks the current corridor path to see if its polygon references remain valid.
|
|
||||||
/// @param[in] maxLookAhead The number of polygons from the beginning of the corridor to search.
|
|
||||||
/// @param[in] navquery The query object used to build the corridor.
|
|
||||||
/// @param[in] filter The filter to apply to the operation.
|
|
||||||
bool isValid(const int maxLookAhead, dtNavMeshQuery* navquery, const dtQueryFilter* filter);
|
|
||||||
|
|
||||||
/// Moves the position from the current location to the desired location, adjusting the corridor
|
|
||||||
/// as needed to reflect the change.
|
|
||||||
/// @param[in] npos The desired new position. [(x, y, z)]
|
|
||||||
/// @param[in] navquery The query object used to build the corridor.
|
|
||||||
/// @param[in] filter The filter to apply to the operation.
|
|
||||||
/// @return Returns true if move succeeded.
|
|
||||||
bool movePosition(const float* npos, dtNavMeshQuery* navquery, const dtQueryFilter* filter);
|
|
||||||
|
|
||||||
/// Moves the target from the curent location to the desired location, adjusting the corridor
|
|
||||||
/// as needed to reflect the change.
|
|
||||||
/// @param[in] npos The desired new target position. [(x, y, z)]
|
|
||||||
/// @param[in] navquery The query object used to build the corridor.
|
|
||||||
/// @param[in] filter The filter to apply to the operation.
|
|
||||||
/// @return Returns true if move succeeded.
|
|
||||||
bool moveTargetPosition(const float* npos, dtNavMeshQuery* navquery, const dtQueryFilter* filter);
|
|
||||||
|
|
||||||
/// Loads a new path and target into the corridor.
|
|
||||||
/// @param[in] target The target location within the last polygon of the path. [(x, y, z)]
|
|
||||||
/// @param[in] path The path corridor. [(polyRef) * @p npolys]
|
|
||||||
/// @param[in] npath The number of polygons in the path.
|
|
||||||
void setCorridor(const float* target, const dtPolyRef* polys, const int npath);
|
|
||||||
|
|
||||||
/// Gets the current position within the corridor. (In the first polygon.)
|
|
||||||
/// @return The current position within the corridor.
|
|
||||||
inline const float* getPos() const { return m_pos; }
|
|
||||||
|
|
||||||
/// Gets the current target within the corridor. (In the last polygon.)
|
|
||||||
/// @return The current target within the corridor.
|
|
||||||
inline const float* getTarget() const { return m_target; }
|
|
||||||
|
|
||||||
/// The polygon reference id of the first polygon in the corridor, the polygon containing the position.
|
|
||||||
/// @return The polygon reference id of the first polygon in the corridor. (Or zero if there is no path.)
|
|
||||||
inline dtPolyRef getFirstPoly() const { return m_npath ? m_path[0] : 0; }
|
|
||||||
|
|
||||||
/// The polygon reference id of the last polygon in the corridor, the polygon containing the target.
|
|
||||||
/// @return The polygon reference id of the last polygon in the corridor. (Or zero if there is no path.)
|
|
||||||
inline dtPolyRef getLastPoly() const { return m_npath ? m_path[m_npath-1] : 0; }
|
|
||||||
|
|
||||||
/// The corridor's path.
|
|
||||||
/// @return The corridor's path. [(polyRef) * #getPathCount()]
|
|
||||||
inline const dtPolyRef* getPath() const { return m_path; }
|
|
||||||
|
|
||||||
/// The number of polygons in the current corridor path.
|
|
||||||
/// @return The number of polygons in the current corridor path.
|
|
||||||
inline int getPathCount() const { return m_npath; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Explicitly disabled copy constructor and copy assignment operator.
|
|
||||||
dtPathCorridor(const dtPathCorridor&);
|
|
||||||
dtPathCorridor& operator=(const dtPathCorridor&);
|
|
||||||
};
|
|
||||||
|
|
||||||
int dtMergeCorridorStartMoved(dtPolyRef* path, const int npath, const int maxPath,
|
|
||||||
const dtPolyRef* visited, const int nvisited);
|
|
||||||
|
|
||||||
int dtMergeCorridorEndMoved(dtPolyRef* path, const int npath, const int maxPath,
|
|
||||||
const dtPolyRef* visited, const int nvisited);
|
|
||||||
|
|
||||||
int dtMergeCorridorStartShortcut(dtPolyRef* path, const int npath, const int maxPath,
|
|
||||||
const dtPolyRef* visited, const int nvisited);
|
|
||||||
|
|
||||||
#endif // DETOUTPATHCORRIDOR_H
|
|
@ -1,79 +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 DETOURPATHQUEUE_H
|
|
||||||
#define DETOURPATHQUEUE_H
|
|
||||||
|
|
||||||
#include "DetourNavMesh.h"
|
|
||||||
#include "DetourNavMeshQuery.h"
|
|
||||||
|
|
||||||
static const unsigned int DT_PATHQ_INVALID = 0;
|
|
||||||
|
|
||||||
typedef unsigned int dtPathQueueRef;
|
|
||||||
|
|
||||||
class dtPathQueue
|
|
||||||
{
|
|
||||||
struct PathQuery
|
|
||||||
{
|
|
||||||
dtPathQueueRef ref;
|
|
||||||
/// Path find start and end location.
|
|
||||||
float startPos[3], endPos[3];
|
|
||||||
dtPolyRef startRef, endRef;
|
|
||||||
/// Result.
|
|
||||||
dtPolyRef* path;
|
|
||||||
int npath;
|
|
||||||
/// State.
|
|
||||||
dtStatus status;
|
|
||||||
int keepAlive;
|
|
||||||
const dtQueryFilter* filter; ///< TODO: This is potentially dangerous!
|
|
||||||
};
|
|
||||||
|
|
||||||
static const int MAX_QUEUE = 8;
|
|
||||||
PathQuery m_queue[MAX_QUEUE];
|
|
||||||
dtPathQueueRef m_nextHandle;
|
|
||||||
int m_maxPathSize;
|
|
||||||
int m_queueHead;
|
|
||||||
dtNavMeshQuery* m_navquery;
|
|
||||||
|
|
||||||
void purge();
|
|
||||||
|
|
||||||
public:
|
|
||||||
dtPathQueue();
|
|
||||||
~dtPathQueue();
|
|
||||||
|
|
||||||
bool init(const int maxPathSize, const int maxSearchNodeCount, dtNavMesh* nav);
|
|
||||||
|
|
||||||
void update(const int maxIters);
|
|
||||||
|
|
||||||
dtPathQueueRef request(dtPolyRef startRef, dtPolyRef endRef,
|
|
||||||
const float* startPos, const float* endPos,
|
|
||||||
const dtQueryFilter* filter);
|
|
||||||
|
|
||||||
dtStatus getRequestStatus(dtPathQueueRef ref) const;
|
|
||||||
|
|
||||||
dtStatus getPathResult(dtPathQueueRef ref, dtPolyRef* path, int* pathSize, const int maxPath);
|
|
||||||
|
|
||||||
inline const dtNavMeshQuery* getNavQuery() const { return m_navquery; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Explicitly disabled copy constructor and copy assignment operator.
|
|
||||||
dtPathQueue(const dtPathQueue&);
|
|
||||||
dtPathQueue& operator=(const dtPathQueue&);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // DETOURPATHQUEUE_H
|
|
@ -1,74 +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 DETOURPROXIMITYGRID_H
|
|
||||||
#define DETOURPROXIMITYGRID_H
|
|
||||||
|
|
||||||
class dtProximityGrid
|
|
||||||
{
|
|
||||||
float m_cellSize;
|
|
||||||
float m_invCellSize;
|
|
||||||
|
|
||||||
struct Item
|
|
||||||
{
|
|
||||||
unsigned short id;
|
|
||||||
short x,y;
|
|
||||||
unsigned short next;
|
|
||||||
};
|
|
||||||
Item* m_pool;
|
|
||||||
int m_poolHead;
|
|
||||||
int m_poolSize;
|
|
||||||
|
|
||||||
unsigned short* m_buckets;
|
|
||||||
int m_bucketsSize;
|
|
||||||
|
|
||||||
int m_bounds[4];
|
|
||||||
|
|
||||||
public:
|
|
||||||
dtProximityGrid();
|
|
||||||
~dtProximityGrid();
|
|
||||||
|
|
||||||
bool init(const int poolSize, const float cellSize);
|
|
||||||
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
void addItem(const unsigned short id,
|
|
||||||
const float minx, const float miny,
|
|
||||||
const float maxx, const float maxy);
|
|
||||||
|
|
||||||
int queryItems(const float minx, const float miny,
|
|
||||||
const float maxx, const float maxy,
|
|
||||||
unsigned short* ids, const int maxIds) const;
|
|
||||||
|
|
||||||
int getItemCountAt(const int x, const int y) const;
|
|
||||||
|
|
||||||
inline const int* getBounds() const { return m_bounds; }
|
|
||||||
inline float getCellSize() const { return m_cellSize; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Explicitly disabled copy constructor and copy assignment operator.
|
|
||||||
dtProximityGrid(const dtProximityGrid&);
|
|
||||||
dtProximityGrid& operator=(const dtProximityGrid&);
|
|
||||||
};
|
|
||||||
|
|
||||||
dtProximityGrid* dtAllocProximityGrid();
|
|
||||||
void dtFreeProximityGrid(dtProximityGrid* ptr);
|
|
||||||
|
|
||||||
|
|
||||||
#endif // DETOURPROXIMITYGRID_H
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
@ -1,137 +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.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <float.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "DetourLocalBoundary.h"
|
|
||||||
#include "DetourNavMeshQuery.h"
|
|
||||||
#include "DetourCommon.h"
|
|
||||||
#include "DetourAssert.h"
|
|
||||||
|
|
||||||
|
|
||||||
dtLocalBoundary::dtLocalBoundary() :
|
|
||||||
m_nsegs(0),
|
|
||||||
m_npolys(0)
|
|
||||||
{
|
|
||||||
dtVset(m_center, FLT_MAX,FLT_MAX,FLT_MAX);
|
|
||||||
}
|
|
||||||
|
|
||||||
dtLocalBoundary::~dtLocalBoundary()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtLocalBoundary::reset()
|
|
||||||
{
|
|
||||||
dtVset(m_center, FLT_MAX,FLT_MAX,FLT_MAX);
|
|
||||||
m_npolys = 0;
|
|
||||||
m_nsegs = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtLocalBoundary::addSegment(const float dist, const float* s)
|
|
||||||
{
|
|
||||||
// Insert neighbour based on the distance.
|
|
||||||
Segment* seg = 0;
|
|
||||||
if (!m_nsegs)
|
|
||||||
{
|
|
||||||
// First, trivial accept.
|
|
||||||
seg = &m_segs[0];
|
|
||||||
}
|
|
||||||
else if (dist >= m_segs[m_nsegs-1].d)
|
|
||||||
{
|
|
||||||
// Further than the last segment, skip.
|
|
||||||
if (m_nsegs >= MAX_LOCAL_SEGS)
|
|
||||||
return;
|
|
||||||
// Last, trivial accept.
|
|
||||||
seg = &m_segs[m_nsegs];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Insert inbetween.
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < m_nsegs; ++i)
|
|
||||||
if (dist <= m_segs[i].d)
|
|
||||||
break;
|
|
||||||
const int tgt = i+1;
|
|
||||||
const int n = dtMin(m_nsegs-i, MAX_LOCAL_SEGS-tgt);
|
|
||||||
dtAssert(tgt+n <= MAX_LOCAL_SEGS);
|
|
||||||
if (n > 0)
|
|
||||||
memmove(&m_segs[tgt], &m_segs[i], sizeof(Segment)*n);
|
|
||||||
seg = &m_segs[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
seg->d = dist;
|
|
||||||
memcpy(seg->s, s, sizeof(float)*6);
|
|
||||||
|
|
||||||
if (m_nsegs < MAX_LOCAL_SEGS)
|
|
||||||
m_nsegs++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtLocalBoundary::update(dtPolyRef ref, const float* pos, const float collisionQueryRange,
|
|
||||||
dtNavMeshQuery* navquery, const dtQueryFilter* filter)
|
|
||||||
{
|
|
||||||
static const int MAX_SEGS_PER_POLY = DT_VERTS_PER_POLYGON*3;
|
|
||||||
|
|
||||||
if (!ref)
|
|
||||||
{
|
|
||||||
dtVset(m_center, FLT_MAX,FLT_MAX,FLT_MAX);
|
|
||||||
m_nsegs = 0;
|
|
||||||
m_npolys = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dtVcopy(m_center, pos);
|
|
||||||
|
|
||||||
// First query non-overlapping polygons.
|
|
||||||
navquery->findLocalNeighbourhood(ref, pos, collisionQueryRange,
|
|
||||||
filter, m_polys, 0, &m_npolys, MAX_LOCAL_POLYS);
|
|
||||||
|
|
||||||
// Secondly, store all polygon edges.
|
|
||||||
m_nsegs = 0;
|
|
||||||
float segs[MAX_SEGS_PER_POLY*6];
|
|
||||||
int nsegs = 0;
|
|
||||||
for (int j = 0; j < m_npolys; ++j)
|
|
||||||
{
|
|
||||||
navquery->getPolyWallSegments(m_polys[j], filter, segs, 0, &nsegs, MAX_SEGS_PER_POLY);
|
|
||||||
for (int k = 0; k < nsegs; ++k)
|
|
||||||
{
|
|
||||||
const float* s = &segs[k*6];
|
|
||||||
// Skip too distant segments.
|
|
||||||
float tseg;
|
|
||||||
const float distSqr = dtDistancePtSegSqr2D(pos, s, s+3, tseg);
|
|
||||||
if (distSqr > dtSqr(collisionQueryRange))
|
|
||||||
continue;
|
|
||||||
addSegment(distSqr, s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool dtLocalBoundary::isValid(dtNavMeshQuery* navquery, const dtQueryFilter* filter)
|
|
||||||
{
|
|
||||||
if (!m_npolys)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Check that all polygons still pass query filter.
|
|
||||||
for (int i = 0; i < m_npolys; ++i)
|
|
||||||
{
|
|
||||||
if (!navquery->isValidPolyRef(m_polys[i], filter))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
@ -1,619 +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.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "DetourObstacleAvoidance.h"
|
|
||||||
#include "DetourCommon.h"
|
|
||||||
#include "DetourMath.h"
|
|
||||||
#include "DetourAlloc.h"
|
|
||||||
#include "DetourAssert.h"
|
|
||||||
#include <string.h>
|
|
||||||
#include <float.h>
|
|
||||||
#include <new>
|
|
||||||
|
|
||||||
static const float DT_PI = 3.14159265f;
|
|
||||||
|
|
||||||
static int sweepCircleCircle(const float* c0, const float r0, const float* v,
|
|
||||||
const float* c1, const float r1,
|
|
||||||
float& tmin, float& tmax)
|
|
||||||
{
|
|
||||||
static const float EPS = 0.0001f;
|
|
||||||
float s[3];
|
|
||||||
dtVsub(s,c1,c0);
|
|
||||||
float r = r0+r1;
|
|
||||||
float c = dtVdot2D(s,s) - r*r;
|
|
||||||
float a = dtVdot2D(v,v);
|
|
||||||
if (a < EPS) return 0; // not moving
|
|
||||||
|
|
||||||
// Overlap, calc time to exit.
|
|
||||||
float b = dtVdot2D(v,s);
|
|
||||||
float d = b*b - a*c;
|
|
||||||
if (d < 0.0f) return 0; // no intersection.
|
|
||||||
a = 1.0f / a;
|
|
||||||
const float rd = dtMathSqrtf(d);
|
|
||||||
tmin = (b - rd) * a;
|
|
||||||
tmax = (b + rd) * a;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int isectRaySeg(const float* ap, const float* u,
|
|
||||||
const float* bp, const float* bq,
|
|
||||||
float& t)
|
|
||||||
{
|
|
||||||
float v[3], w[3];
|
|
||||||
dtVsub(v,bq,bp);
|
|
||||||
dtVsub(w,ap,bp);
|
|
||||||
float d = dtVperp2D(u,v);
|
|
||||||
if (dtMathFabsf(d) < 1e-6f) return 0;
|
|
||||||
d = 1.0f/d;
|
|
||||||
t = dtVperp2D(v,w) * d;
|
|
||||||
if (t < 0 || t > 1) return 0;
|
|
||||||
float s = dtVperp2D(u,w) * d;
|
|
||||||
if (s < 0 || s > 1) return 0;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
dtObstacleAvoidanceDebugData* dtAllocObstacleAvoidanceDebugData()
|
|
||||||
{
|
|
||||||
void* mem = dtAlloc(sizeof(dtObstacleAvoidanceDebugData), DT_ALLOC_PERM);
|
|
||||||
if (!mem) return 0;
|
|
||||||
return new(mem) dtObstacleAvoidanceDebugData;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtFreeObstacleAvoidanceDebugData(dtObstacleAvoidanceDebugData* ptr)
|
|
||||||
{
|
|
||||||
if (!ptr) return;
|
|
||||||
ptr->~dtObstacleAvoidanceDebugData();
|
|
||||||
dtFree(ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
dtObstacleAvoidanceDebugData::dtObstacleAvoidanceDebugData() :
|
|
||||||
m_nsamples(0),
|
|
||||||
m_maxSamples(0),
|
|
||||||
m_vel(0),
|
|
||||||
m_ssize(0),
|
|
||||||
m_pen(0),
|
|
||||||
m_vpen(0),
|
|
||||||
m_vcpen(0),
|
|
||||||
m_spen(0),
|
|
||||||
m_tpen(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
dtObstacleAvoidanceDebugData::~dtObstacleAvoidanceDebugData()
|
|
||||||
{
|
|
||||||
dtFree(m_vel);
|
|
||||||
dtFree(m_ssize);
|
|
||||||
dtFree(m_pen);
|
|
||||||
dtFree(m_vpen);
|
|
||||||
dtFree(m_vcpen);
|
|
||||||
dtFree(m_spen);
|
|
||||||
dtFree(m_tpen);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool dtObstacleAvoidanceDebugData::init(const int maxSamples)
|
|
||||||
{
|
|
||||||
dtAssert(maxSamples);
|
|
||||||
m_maxSamples = maxSamples;
|
|
||||||
|
|
||||||
m_vel = (float*)dtAlloc(sizeof(float)*3*m_maxSamples, DT_ALLOC_PERM);
|
|
||||||
if (!m_vel)
|
|
||||||
return false;
|
|
||||||
m_pen = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
|
|
||||||
if (!m_pen)
|
|
||||||
return false;
|
|
||||||
m_ssize = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
|
|
||||||
if (!m_ssize)
|
|
||||||
return false;
|
|
||||||
m_vpen = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
|
|
||||||
if (!m_vpen)
|
|
||||||
return false;
|
|
||||||
m_vcpen = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
|
|
||||||
if (!m_vcpen)
|
|
||||||
return false;
|
|
||||||
m_spen = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
|
|
||||||
if (!m_spen)
|
|
||||||
return false;
|
|
||||||
m_tpen = (float*)dtAlloc(sizeof(float)*m_maxSamples, DT_ALLOC_PERM);
|
|
||||||
if (!m_tpen)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtObstacleAvoidanceDebugData::reset()
|
|
||||||
{
|
|
||||||
m_nsamples = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtObstacleAvoidanceDebugData::addSample(const float* vel, const float ssize, const float pen,
|
|
||||||
const float vpen, const float vcpen, const float spen, const float tpen)
|
|
||||||
{
|
|
||||||
if (m_nsamples >= m_maxSamples)
|
|
||||||
return;
|
|
||||||
dtAssert(m_vel);
|
|
||||||
dtAssert(m_ssize);
|
|
||||||
dtAssert(m_pen);
|
|
||||||
dtAssert(m_vpen);
|
|
||||||
dtAssert(m_vcpen);
|
|
||||||
dtAssert(m_spen);
|
|
||||||
dtAssert(m_tpen);
|
|
||||||
dtVcopy(&m_vel[m_nsamples*3], vel);
|
|
||||||
m_ssize[m_nsamples] = ssize;
|
|
||||||
m_pen[m_nsamples] = pen;
|
|
||||||
m_vpen[m_nsamples] = vpen;
|
|
||||||
m_vcpen[m_nsamples] = vcpen;
|
|
||||||
m_spen[m_nsamples] = spen;
|
|
||||||
m_tpen[m_nsamples] = tpen;
|
|
||||||
m_nsamples++;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void normalizeArray(float* arr, const int n)
|
|
||||||
{
|
|
||||||
// Normalize penaly range.
|
|
||||||
float minPen = FLT_MAX;
|
|
||||||
float maxPen = -FLT_MAX;
|
|
||||||
for (int i = 0; i < n; ++i)
|
|
||||||
{
|
|
||||||
minPen = dtMin(minPen, arr[i]);
|
|
||||||
maxPen = dtMax(maxPen, arr[i]);
|
|
||||||
}
|
|
||||||
const float penRange = maxPen-minPen;
|
|
||||||
const float s = penRange > 0.001f ? (1.0f / penRange) : 1;
|
|
||||||
for (int i = 0; i < n; ++i)
|
|
||||||
arr[i] = dtClamp((arr[i]-minPen)*s, 0.0f, 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtObstacleAvoidanceDebugData::normalizeSamples()
|
|
||||||
{
|
|
||||||
normalizeArray(m_pen, m_nsamples);
|
|
||||||
normalizeArray(m_vpen, m_nsamples);
|
|
||||||
normalizeArray(m_vcpen, m_nsamples);
|
|
||||||
normalizeArray(m_spen, m_nsamples);
|
|
||||||
normalizeArray(m_tpen, m_nsamples);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
dtObstacleAvoidanceQuery* dtAllocObstacleAvoidanceQuery()
|
|
||||||
{
|
|
||||||
void* mem = dtAlloc(sizeof(dtObstacleAvoidanceQuery), DT_ALLOC_PERM);
|
|
||||||
if (!mem) return 0;
|
|
||||||
return new(mem) dtObstacleAvoidanceQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtFreeObstacleAvoidanceQuery(dtObstacleAvoidanceQuery* ptr)
|
|
||||||
{
|
|
||||||
if (!ptr) return;
|
|
||||||
ptr->~dtObstacleAvoidanceQuery();
|
|
||||||
dtFree(ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
dtObstacleAvoidanceQuery::dtObstacleAvoidanceQuery() :
|
|
||||||
m_invHorizTime(0),
|
|
||||||
m_vmax(0),
|
|
||||||
m_invVmax(0),
|
|
||||||
m_maxCircles(0),
|
|
||||||
m_circles(0),
|
|
||||||
m_ncircles(0),
|
|
||||||
m_maxSegments(0),
|
|
||||||
m_segments(0),
|
|
||||||
m_nsegments(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
dtObstacleAvoidanceQuery::~dtObstacleAvoidanceQuery()
|
|
||||||
{
|
|
||||||
dtFree(m_circles);
|
|
||||||
dtFree(m_segments);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool dtObstacleAvoidanceQuery::init(const int maxCircles, const int maxSegments)
|
|
||||||
{
|
|
||||||
m_maxCircles = maxCircles;
|
|
||||||
m_ncircles = 0;
|
|
||||||
m_circles = (dtObstacleCircle*)dtAlloc(sizeof(dtObstacleCircle)*m_maxCircles, DT_ALLOC_PERM);
|
|
||||||
if (!m_circles)
|
|
||||||
return false;
|
|
||||||
memset(m_circles, 0, sizeof(dtObstacleCircle)*m_maxCircles);
|
|
||||||
|
|
||||||
m_maxSegments = maxSegments;
|
|
||||||
m_nsegments = 0;
|
|
||||||
m_segments = (dtObstacleSegment*)dtAlloc(sizeof(dtObstacleSegment)*m_maxSegments, DT_ALLOC_PERM);
|
|
||||||
if (!m_segments)
|
|
||||||
return false;
|
|
||||||
memset(m_segments, 0, sizeof(dtObstacleSegment)*m_maxSegments);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtObstacleAvoidanceQuery::reset()
|
|
||||||
{
|
|
||||||
m_ncircles = 0;
|
|
||||||
m_nsegments = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtObstacleAvoidanceQuery::addCircle(const float* pos, const float rad,
|
|
||||||
const float* vel, const float* dvel)
|
|
||||||
{
|
|
||||||
if (m_ncircles >= m_maxCircles)
|
|
||||||
return;
|
|
||||||
|
|
||||||
dtObstacleCircle* cir = &m_circles[m_ncircles++];
|
|
||||||
dtVcopy(cir->p, pos);
|
|
||||||
cir->rad = rad;
|
|
||||||
dtVcopy(cir->vel, vel);
|
|
||||||
dtVcopy(cir->dvel, dvel);
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtObstacleAvoidanceQuery::addSegment(const float* p, const float* q)
|
|
||||||
{
|
|
||||||
if (m_nsegments >= m_maxSegments)
|
|
||||||
return;
|
|
||||||
|
|
||||||
dtObstacleSegment* seg = &m_segments[m_nsegments++];
|
|
||||||
dtVcopy(seg->p, p);
|
|
||||||
dtVcopy(seg->q, q);
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtObstacleAvoidanceQuery::prepare(const float* pos, const float* dvel)
|
|
||||||
{
|
|
||||||
// Prepare obstacles
|
|
||||||
for (int i = 0; i < m_ncircles; ++i)
|
|
||||||
{
|
|
||||||
dtObstacleCircle* cir = &m_circles[i];
|
|
||||||
|
|
||||||
// Side
|
|
||||||
const float* pa = pos;
|
|
||||||
const float* pb = cir->p;
|
|
||||||
|
|
||||||
const float orig[3] = {0,0,0};
|
|
||||||
float dv[3];
|
|
||||||
dtVsub(cir->dp,pb,pa);
|
|
||||||
dtVnormalize(cir->dp);
|
|
||||||
dtVsub(dv, cir->dvel, dvel);
|
|
||||||
|
|
||||||
const float a = dtTriArea2D(orig, cir->dp,dv);
|
|
||||||
if (a < 0.01f)
|
|
||||||
{
|
|
||||||
cir->np[0] = -cir->dp[2];
|
|
||||||
cir->np[2] = cir->dp[0];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cir->np[0] = cir->dp[2];
|
|
||||||
cir->np[2] = -cir->dp[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < m_nsegments; ++i)
|
|
||||||
{
|
|
||||||
dtObstacleSegment* seg = &m_segments[i];
|
|
||||||
|
|
||||||
// Precalc if the agent is really close to the segment.
|
|
||||||
const float r = 0.01f;
|
|
||||||
float t;
|
|
||||||
seg->touch = dtDistancePtSegSqr2D(pos, seg->p, seg->q, t) < dtSqr(r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Calculate the collision penalty for a given velocity vector
|
|
||||||
*
|
|
||||||
* @param vcand sampled velocity
|
|
||||||
* @param dvel desired velocity
|
|
||||||
* @param minPenalty threshold penalty for early out
|
|
||||||
*/
|
|
||||||
float dtObstacleAvoidanceQuery::processSample(const float* vcand, const float cs,
|
|
||||||
const float* pos, const float rad,
|
|
||||||
const float* vel, const float* dvel,
|
|
||||||
const float minPenalty,
|
|
||||||
dtObstacleAvoidanceDebugData* debug)
|
|
||||||
{
|
|
||||||
// penalty for straying away from the desired and current velocities
|
|
||||||
const float vpen = m_params.weightDesVel * (dtVdist2D(vcand, dvel) * m_invVmax);
|
|
||||||
const float vcpen = m_params.weightCurVel * (dtVdist2D(vcand, vel) * m_invVmax);
|
|
||||||
|
|
||||||
// find the threshold hit time to bail out based on the early out penalty
|
|
||||||
// (see how the penalty is calculated below to understnad)
|
|
||||||
float minPen = minPenalty - vpen - vcpen;
|
|
||||||
float tThresold = (m_params.weightToi / minPen - 0.1f) * m_params.horizTime;
|
|
||||||
if (tThresold - m_params.horizTime > -FLT_EPSILON)
|
|
||||||
return minPenalty; // already too much
|
|
||||||
|
|
||||||
// Find min time of impact and exit amongst all obstacles.
|
|
||||||
float tmin = m_params.horizTime;
|
|
||||||
float side = 0;
|
|
||||||
int nside = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < m_ncircles; ++i)
|
|
||||||
{
|
|
||||||
const dtObstacleCircle* cir = &m_circles[i];
|
|
||||||
|
|
||||||
// RVO
|
|
||||||
float vab[3];
|
|
||||||
dtVscale(vab, vcand, 2);
|
|
||||||
dtVsub(vab, vab, vel);
|
|
||||||
dtVsub(vab, vab, cir->vel);
|
|
||||||
|
|
||||||
// Side
|
|
||||||
side += dtClamp(dtMin(dtVdot2D(cir->dp,vab)*0.5f+0.5f, dtVdot2D(cir->np,vab)*2), 0.0f, 1.0f);
|
|
||||||
nside++;
|
|
||||||
|
|
||||||
float htmin = 0, htmax = 0;
|
|
||||||
if (!sweepCircleCircle(pos,rad, vab, cir->p,cir->rad, htmin, htmax))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Handle overlapping obstacles.
|
|
||||||
if (htmin < 0.0f && htmax > 0.0f)
|
|
||||||
{
|
|
||||||
// Avoid more when overlapped.
|
|
||||||
htmin = -htmin * 0.5f;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (htmin >= 0.0f)
|
|
||||||
{
|
|
||||||
// The closest obstacle is somewhere ahead of us, keep track of nearest obstacle.
|
|
||||||
if (htmin < tmin)
|
|
||||||
{
|
|
||||||
tmin = htmin;
|
|
||||||
if (tmin < tThresold)
|
|
||||||
return minPenalty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < m_nsegments; ++i)
|
|
||||||
{
|
|
||||||
const dtObstacleSegment* seg = &m_segments[i];
|
|
||||||
float htmin = 0;
|
|
||||||
|
|
||||||
if (seg->touch)
|
|
||||||
{
|
|
||||||
// Special case when the agent is very close to the segment.
|
|
||||||
float sdir[3], snorm[3];
|
|
||||||
dtVsub(sdir, seg->q, seg->p);
|
|
||||||
snorm[0] = -sdir[2];
|
|
||||||
snorm[2] = sdir[0];
|
|
||||||
// If the velocity is pointing towards the segment, no collision.
|
|
||||||
if (dtVdot2D(snorm, vcand) < 0.0f)
|
|
||||||
continue;
|
|
||||||
// Else immediate collision.
|
|
||||||
htmin = 0.0f;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!isectRaySeg(pos, vcand, seg->p, seg->q, htmin))
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Avoid less when facing walls.
|
|
||||||
htmin *= 2.0f;
|
|
||||||
|
|
||||||
// The closest obstacle is somewhere ahead of us, keep track of nearest obstacle.
|
|
||||||
if (htmin < tmin)
|
|
||||||
{
|
|
||||||
tmin = htmin;
|
|
||||||
if (tmin < tThresold)
|
|
||||||
return minPenalty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normalize side bias, to prevent it dominating too much.
|
|
||||||
if (nside)
|
|
||||||
side /= nside;
|
|
||||||
|
|
||||||
const float spen = m_params.weightSide * side;
|
|
||||||
const float tpen = m_params.weightToi * (1.0f/(0.1f+tmin*m_invHorizTime));
|
|
||||||
|
|
||||||
const float penalty = vpen + vcpen + spen + tpen;
|
|
||||||
|
|
||||||
// Store different penalties for debug viewing
|
|
||||||
if (debug)
|
|
||||||
debug->addSample(vcand, cs, penalty, vpen, vcpen, spen, tpen);
|
|
||||||
|
|
||||||
return penalty;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dtObstacleAvoidanceQuery::sampleVelocityGrid(const float* pos, const float rad, const float vmax,
|
|
||||||
const float* vel, const float* dvel, float* nvel,
|
|
||||||
const dtObstacleAvoidanceParams* params,
|
|
||||||
dtObstacleAvoidanceDebugData* debug)
|
|
||||||
{
|
|
||||||
prepare(pos, dvel);
|
|
||||||
|
|
||||||
memcpy(&m_params, params, sizeof(dtObstacleAvoidanceParams));
|
|
||||||
m_invHorizTime = 1.0f / m_params.horizTime;
|
|
||||||
m_vmax = vmax;
|
|
||||||
m_invVmax = vmax > 0 ? 1.0f / vmax : FLT_MAX;
|
|
||||||
|
|
||||||
dtVset(nvel, 0,0,0);
|
|
||||||
|
|
||||||
if (debug)
|
|
||||||
debug->reset();
|
|
||||||
|
|
||||||
const float cvx = dvel[0] * m_params.velBias;
|
|
||||||
const float cvz = dvel[2] * m_params.velBias;
|
|
||||||
const float cs = vmax * 2 * (1 - m_params.velBias) / (float)(m_params.gridSize-1);
|
|
||||||
const float half = (m_params.gridSize-1)*cs*0.5f;
|
|
||||||
|
|
||||||
float minPenalty = FLT_MAX;
|
|
||||||
int ns = 0;
|
|
||||||
|
|
||||||
for (int y = 0; y < m_params.gridSize; ++y)
|
|
||||||
{
|
|
||||||
for (int x = 0; x < m_params.gridSize; ++x)
|
|
||||||
{
|
|
||||||
float vcand[3];
|
|
||||||
vcand[0] = cvx + x*cs - half;
|
|
||||||
vcand[1] = 0;
|
|
||||||
vcand[2] = cvz + y*cs - half;
|
|
||||||
|
|
||||||
if (dtSqr(vcand[0])+dtSqr(vcand[2]) > dtSqr(vmax+cs/2)) continue;
|
|
||||||
|
|
||||||
const float penalty = processSample(vcand, cs, pos,rad,vel,dvel, minPenalty, debug);
|
|
||||||
ns++;
|
|
||||||
if (penalty < minPenalty)
|
|
||||||
{
|
|
||||||
minPenalty = penalty;
|
|
||||||
dtVcopy(nvel, vcand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ns;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// vector normalization that ignores the y-component.
|
|
||||||
inline void dtNormalize2D(float* v)
|
|
||||||
{
|
|
||||||
float d = dtMathSqrtf(v[0] * v[0] + v[2] * v[2]);
|
|
||||||
if (d==0)
|
|
||||||
return;
|
|
||||||
d = 1.0f / d;
|
|
||||||
v[0] *= d;
|
|
||||||
v[2] *= d;
|
|
||||||
}
|
|
||||||
|
|
||||||
// vector normalization that ignores the y-component.
|
|
||||||
inline void dtRorate2D(float* dest, const float* v, float ang)
|
|
||||||
{
|
|
||||||
float c = cosf(ang);
|
|
||||||
float s = sinf(ang);
|
|
||||||
dest[0] = v[0]*c - v[2]*s;
|
|
||||||
dest[2] = v[0]*s + v[2]*c;
|
|
||||||
dest[1] = v[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int dtObstacleAvoidanceQuery::sampleVelocityAdaptive(const float* pos, const float rad, const float vmax,
|
|
||||||
const float* vel, const float* dvel, float* nvel,
|
|
||||||
const dtObstacleAvoidanceParams* params,
|
|
||||||
dtObstacleAvoidanceDebugData* debug)
|
|
||||||
{
|
|
||||||
prepare(pos, dvel);
|
|
||||||
|
|
||||||
memcpy(&m_params, params, sizeof(dtObstacleAvoidanceParams));
|
|
||||||
m_invHorizTime = 1.0f / m_params.horizTime;
|
|
||||||
m_vmax = vmax;
|
|
||||||
m_invVmax = vmax > 0 ? 1.0f / vmax : FLT_MAX;
|
|
||||||
|
|
||||||
dtVset(nvel, 0,0,0);
|
|
||||||
|
|
||||||
if (debug)
|
|
||||||
debug->reset();
|
|
||||||
|
|
||||||
// Build sampling pattern aligned to desired velocity.
|
|
||||||
float pat[(DT_MAX_PATTERN_DIVS*DT_MAX_PATTERN_RINGS+1)*2];
|
|
||||||
int npat = 0;
|
|
||||||
|
|
||||||
const int ndivs = (int)m_params.adaptiveDivs;
|
|
||||||
const int nrings= (int)m_params.adaptiveRings;
|
|
||||||
const int depth = (int)m_params.adaptiveDepth;
|
|
||||||
|
|
||||||
const int nd = dtClamp(ndivs, 1, DT_MAX_PATTERN_DIVS);
|
|
||||||
const int nr = dtClamp(nrings, 1, DT_MAX_PATTERN_RINGS);
|
|
||||||
const float da = (1.0f/nd) * DT_PI*2;
|
|
||||||
const float ca = cosf(da);
|
|
||||||
const float sa = sinf(da);
|
|
||||||
|
|
||||||
// desired direction
|
|
||||||
float ddir[6];
|
|
||||||
dtVcopy(ddir, dvel);
|
|
||||||
dtNormalize2D(ddir);
|
|
||||||
dtRorate2D (ddir+3, ddir, da*0.5f); // rotated by da/2
|
|
||||||
|
|
||||||
// Always add sample at zero
|
|
||||||
pat[npat*2+0] = 0;
|
|
||||||
pat[npat*2+1] = 0;
|
|
||||||
npat++;
|
|
||||||
|
|
||||||
for (int j = 0; j < nr; ++j)
|
|
||||||
{
|
|
||||||
const float r = (float)(nr-j)/(float)nr;
|
|
||||||
pat[npat*2+0] = ddir[(j%2)*3] * r;
|
|
||||||
pat[npat*2+1] = ddir[(j%2)*3+2] * r;
|
|
||||||
float* last1 = pat + npat*2;
|
|
||||||
float* last2 = last1;
|
|
||||||
npat++;
|
|
||||||
|
|
||||||
for (int i = 1; i < nd-1; i+=2)
|
|
||||||
{
|
|
||||||
// get next point on the "right" (rotate CW)
|
|
||||||
pat[npat*2+0] = last1[0]*ca + last1[1]*sa;
|
|
||||||
pat[npat*2+1] = -last1[0]*sa + last1[1]*ca;
|
|
||||||
// get next point on the "left" (rotate CCW)
|
|
||||||
pat[npat*2+2] = last2[0]*ca - last2[1]*sa;
|
|
||||||
pat[npat*2+3] = last2[0]*sa + last2[1]*ca;
|
|
||||||
|
|
||||||
last1 = pat + npat*2;
|
|
||||||
last2 = last1 + 2;
|
|
||||||
npat += 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((nd&1) == 0)
|
|
||||||
{
|
|
||||||
pat[npat*2+2] = last2[0]*ca - last2[1]*sa;
|
|
||||||
pat[npat*2+3] = last2[0]*sa + last2[1]*ca;
|
|
||||||
npat++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Start sampling.
|
|
||||||
float cr = vmax * (1.0f - m_params.velBias);
|
|
||||||
float res[3];
|
|
||||||
dtVset(res, dvel[0] * m_params.velBias, 0, dvel[2] * m_params.velBias);
|
|
||||||
int ns = 0;
|
|
||||||
|
|
||||||
for (int k = 0; k < depth; ++k)
|
|
||||||
{
|
|
||||||
float minPenalty = FLT_MAX;
|
|
||||||
float bvel[3];
|
|
||||||
dtVset(bvel, 0,0,0);
|
|
||||||
|
|
||||||
for (int i = 0; i < npat; ++i)
|
|
||||||
{
|
|
||||||
float vcand[3];
|
|
||||||
vcand[0] = res[0] + pat[i*2+0]*cr;
|
|
||||||
vcand[1] = 0;
|
|
||||||
vcand[2] = res[2] + pat[i*2+1]*cr;
|
|
||||||
|
|
||||||
if (dtSqr(vcand[0])+dtSqr(vcand[2]) > dtSqr(vmax+0.001f)) continue;
|
|
||||||
|
|
||||||
const float penalty = processSample(vcand,cr/10, pos,rad,vel,dvel, minPenalty, debug);
|
|
||||||
ns++;
|
|
||||||
if (penalty < minPenalty)
|
|
||||||
{
|
|
||||||
minPenalty = penalty;
|
|
||||||
dtVcopy(bvel, vcand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dtVcopy(res, bvel);
|
|
||||||
|
|
||||||
cr *= 0.5f;
|
|
||||||
}
|
|
||||||
|
|
||||||
dtVcopy(nvel, res);
|
|
||||||
|
|
||||||
return ns;
|
|
||||||
}
|
|
@ -1,597 +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.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include "DetourPathCorridor.h"
|
|
||||||
#include "DetourNavMeshQuery.h"
|
|
||||||
#include "DetourCommon.h"
|
|
||||||
#include "DetourAssert.h"
|
|
||||||
#include "DetourAlloc.h"
|
|
||||||
|
|
||||||
|
|
||||||
int dtMergeCorridorStartMoved(dtPolyRef* path, const int npath, const int maxPath,
|
|
||||||
const dtPolyRef* visited, const int nvisited)
|
|
||||||
{
|
|
||||||
int furthestPath = -1;
|
|
||||||
int furthestVisited = -1;
|
|
||||||
|
|
||||||
// Find furthest common polygon.
|
|
||||||
for (int i = npath-1; i >= 0; --i)
|
|
||||||
{
|
|
||||||
bool found = false;
|
|
||||||
for (int j = nvisited-1; j >= 0; --j)
|
|
||||||
{
|
|
||||||
if (path[i] == visited[j])
|
|
||||||
{
|
|
||||||
furthestPath = i;
|
|
||||||
furthestVisited = j;
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (found)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no intersection found just return current path.
|
|
||||||
if (furthestPath == -1 || furthestVisited == -1)
|
|
||||||
return npath;
|
|
||||||
|
|
||||||
// Concatenate paths.
|
|
||||||
|
|
||||||
// Adjust beginning of the buffer to include the visited.
|
|
||||||
const int req = nvisited - furthestVisited;
|
|
||||||
const int orig = dtMin(furthestPath+1, npath);
|
|
||||||
int size = dtMax(0, npath-orig);
|
|
||||||
if (req+size > maxPath)
|
|
||||||
size = maxPath-req;
|
|
||||||
if (size)
|
|
||||||
memmove(path+req, path+orig, size*sizeof(dtPolyRef));
|
|
||||||
|
|
||||||
// Store visited
|
|
||||||
for (int i = 0; i < req; ++i)
|
|
||||||
path[i] = visited[(nvisited-1)-i];
|
|
||||||
|
|
||||||
return req+size;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dtMergeCorridorEndMoved(dtPolyRef* path, const int npath, const int maxPath,
|
|
||||||
const dtPolyRef* visited, const int nvisited)
|
|
||||||
{
|
|
||||||
int furthestPath = -1;
|
|
||||||
int furthestVisited = -1;
|
|
||||||
|
|
||||||
// Find furthest common polygon.
|
|
||||||
for (int i = 0; i < npath; ++i)
|
|
||||||
{
|
|
||||||
bool found = false;
|
|
||||||
for (int j = nvisited-1; j >= 0; --j)
|
|
||||||
{
|
|
||||||
if (path[i] == visited[j])
|
|
||||||
{
|
|
||||||
furthestPath = i;
|
|
||||||
furthestVisited = j;
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (found)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no intersection found just return current path.
|
|
||||||
if (furthestPath == -1 || furthestVisited == -1)
|
|
||||||
return npath;
|
|
||||||
|
|
||||||
// Concatenate paths.
|
|
||||||
const int ppos = furthestPath+1;
|
|
||||||
const int vpos = furthestVisited+1;
|
|
||||||
const int count = dtMin(nvisited-vpos, maxPath-ppos);
|
|
||||||
dtAssert(ppos+count <= maxPath);
|
|
||||||
if (count)
|
|
||||||
memcpy(path+ppos, visited+vpos, sizeof(dtPolyRef)*count);
|
|
||||||
|
|
||||||
return ppos+count;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dtMergeCorridorStartShortcut(dtPolyRef* path, const int npath, const int maxPath,
|
|
||||||
const dtPolyRef* visited, const int nvisited)
|
|
||||||
{
|
|
||||||
int furthestPath = -1;
|
|
||||||
int furthestVisited = -1;
|
|
||||||
|
|
||||||
// Find furthest common polygon.
|
|
||||||
for (int i = npath-1; i >= 0; --i)
|
|
||||||
{
|
|
||||||
bool found = false;
|
|
||||||
for (int j = nvisited-1; j >= 0; --j)
|
|
||||||
{
|
|
||||||
if (path[i] == visited[j])
|
|
||||||
{
|
|
||||||
furthestPath = i;
|
|
||||||
furthestVisited = j;
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (found)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no intersection found just return current path.
|
|
||||||
if (furthestPath == -1 || furthestVisited == -1)
|
|
||||||
return npath;
|
|
||||||
|
|
||||||
// Concatenate paths.
|
|
||||||
|
|
||||||
// Adjust beginning of the buffer to include the visited.
|
|
||||||
const int req = furthestVisited;
|
|
||||||
if (req <= 0)
|
|
||||||
return npath;
|
|
||||||
|
|
||||||
const int orig = furthestPath;
|
|
||||||
int size = dtMax(0, npath-orig);
|
|
||||||
if (req+size > maxPath)
|
|
||||||
size = maxPath-req;
|
|
||||||
if (size)
|
|
||||||
memmove(path+req, path+orig, size*sizeof(dtPolyRef));
|
|
||||||
|
|
||||||
// Store visited
|
|
||||||
for (int i = 0; i < req; ++i)
|
|
||||||
path[i] = visited[i];
|
|
||||||
|
|
||||||
return req+size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
@class dtPathCorridor
|
|
||||||
@par
|
|
||||||
|
|
||||||
The corridor is loaded with a path, usually obtained from a #dtNavMeshQuery::findPath() query. The corridor
|
|
||||||
is then used to plan local movement, with the corridor automatically updating as needed to deal with inaccurate
|
|
||||||
agent locomotion.
|
|
||||||
|
|
||||||
Example of a common use case:
|
|
||||||
|
|
||||||
-# Construct the corridor object and call #init() to allocate its path buffer.
|
|
||||||
-# Obtain a path from a #dtNavMeshQuery object.
|
|
||||||
-# Use #reset() to set the agent's current position. (At the beginning of the path.)
|
|
||||||
-# Use #setCorridor() to load the path and target.
|
|
||||||
-# Use #findCorners() to plan movement. (This handles dynamic path straightening.)
|
|
||||||
-# Use #movePosition() to feed agent movement back into the corridor. (The corridor will automatically adjust as needed.)
|
|
||||||
-# If the target is moving, use #moveTargetPosition() to update the end of the corridor.
|
|
||||||
(The corridor will automatically adjust as needed.)
|
|
||||||
-# Repeat the previous 3 steps to continue to move the agent.
|
|
||||||
|
|
||||||
The corridor position and target are always constrained to the navigation mesh.
|
|
||||||
|
|
||||||
One of the difficulties in maintaining a path is that floating point errors, locomotion inaccuracies, and/or local
|
|
||||||
steering can result in the agent crossing the boundary of the path corridor, temporarily invalidating the path.
|
|
||||||
This class uses local mesh queries to detect and update the corridor as needed to handle these types of issues.
|
|
||||||
|
|
||||||
The fact that local mesh queries are used to move the position and target locations results in two beahviors that
|
|
||||||
need to be considered:
|
|
||||||
|
|
||||||
Every time a move function is used there is a chance that the path will become non-optimial. Basically, the further
|
|
||||||
the target is moved from its original location, and the further the position is moved outside the original corridor,
|
|
||||||
the more likely the path will become non-optimal. This issue can be addressed by periodically running the
|
|
||||||
#optimizePathTopology() and #optimizePathVisibility() methods.
|
|
||||||
|
|
||||||
All local mesh queries have distance limitations. (Review the #dtNavMeshQuery methods for details.) So the most accurate
|
|
||||||
use case is to move the position and target in small increments. If a large increment is used, then the corridor
|
|
||||||
may not be able to accurately find the new location. Because of this limiation, if a position is moved in a large
|
|
||||||
increment, then compare the desired and resulting polygon references. If the two do not match, then path replanning
|
|
||||||
may be needed. E.g. If you move the target, check #getLastPoly() to see if it is the expected polygon.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
dtPathCorridor::dtPathCorridor() :
|
|
||||||
m_path(0),
|
|
||||||
m_npath(0),
|
|
||||||
m_maxPath(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
dtPathCorridor::~dtPathCorridor()
|
|
||||||
{
|
|
||||||
dtFree(m_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @par
|
|
||||||
///
|
|
||||||
/// @warning Cannot be called more than once.
|
|
||||||
bool dtPathCorridor::init(const int maxPath)
|
|
||||||
{
|
|
||||||
dtAssert(!m_path);
|
|
||||||
m_path = (dtPolyRef*)dtAlloc(sizeof(dtPolyRef)*maxPath, DT_ALLOC_PERM);
|
|
||||||
if (!m_path)
|
|
||||||
return false;
|
|
||||||
m_npath = 0;
|
|
||||||
m_maxPath = maxPath;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @par
|
|
||||||
///
|
|
||||||
/// Essentially, the corridor is set of one polygon in size with the target
|
|
||||||
/// equal to the position.
|
|
||||||
void dtPathCorridor::reset(dtPolyRef ref, const float* pos)
|
|
||||||
{
|
|
||||||
dtAssert(m_path);
|
|
||||||
dtVcopy(m_pos, pos);
|
|
||||||
dtVcopy(m_target, pos);
|
|
||||||
m_path[0] = ref;
|
|
||||||
m_npath = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
@par
|
|
||||||
|
|
||||||
This is the function used to plan local movement within the corridor. One or more corners can be
|
|
||||||
detected in order to plan movement. It performs essentially the same function as #dtNavMeshQuery::findStraightPath.
|
|
||||||
|
|
||||||
Due to internal optimizations, the maximum number of corners returned will be (@p maxCorners - 1)
|
|
||||||
For example: If the buffers are sized to hold 10 corners, the function will never return more than 9 corners.
|
|
||||||
So if 10 corners are needed, the buffers should be sized for 11 corners.
|
|
||||||
|
|
||||||
If the target is within range, it will be the last corner and have a polygon reference id of zero.
|
|
||||||
*/
|
|
||||||
int dtPathCorridor::findCorners(float* cornerVerts, unsigned char* cornerFlags,
|
|
||||||
dtPolyRef* cornerPolys, const int maxCorners,
|
|
||||||
dtNavMeshQuery* navquery, const dtQueryFilter* /*filter*/)
|
|
||||||
{
|
|
||||||
dtAssert(m_path);
|
|
||||||
dtAssert(m_npath);
|
|
||||||
|
|
||||||
static const float MIN_TARGET_DIST = 0.01f;
|
|
||||||
|
|
||||||
int ncorners = 0;
|
|
||||||
navquery->findStraightPath(m_pos, m_target, m_path, m_npath,
|
|
||||||
cornerVerts, cornerFlags, cornerPolys, &ncorners, maxCorners);
|
|
||||||
|
|
||||||
// Prune points in the beginning of the path which are too close.
|
|
||||||
while (ncorners)
|
|
||||||
{
|
|
||||||
if ((cornerFlags[0] & DT_STRAIGHTPATH_OFFMESH_CONNECTION) ||
|
|
||||||
dtVdist2DSqr(&cornerVerts[0], m_pos) > dtSqr(MIN_TARGET_DIST))
|
|
||||||
break;
|
|
||||||
ncorners--;
|
|
||||||
if (ncorners)
|
|
||||||
{
|
|
||||||
memmove(cornerFlags, cornerFlags+1, sizeof(unsigned char)*ncorners);
|
|
||||||
memmove(cornerPolys, cornerPolys+1, sizeof(dtPolyRef)*ncorners);
|
|
||||||
memmove(cornerVerts, cornerVerts+3, sizeof(float)*3*ncorners);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prune points after an off-mesh connection.
|
|
||||||
for (int i = 0; i < ncorners; ++i)
|
|
||||||
{
|
|
||||||
if (cornerFlags[i] & DT_STRAIGHTPATH_OFFMESH_CONNECTION)
|
|
||||||
{
|
|
||||||
ncorners = i+1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ncorners;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
@par
|
|
||||||
|
|
||||||
Inaccurate locomotion or dynamic obstacle avoidance can force the argent position significantly outside the
|
|
||||||
original corridor. Over time this can result in the formation of a non-optimal corridor. Non-optimal paths can
|
|
||||||
also form near the corners of tiles.
|
|
||||||
|
|
||||||
This function uses an efficient local visibility search to try to optimize the corridor
|
|
||||||
between the current position and @p next.
|
|
||||||
|
|
||||||
The corridor will change only if @p next is visible from the current position and moving directly toward the point
|
|
||||||
is better than following the existing path.
|
|
||||||
|
|
||||||
The more inaccurate the agent movement, the more beneficial this function becomes. Simply adjust the frequency
|
|
||||||
of the call to match the needs to the agent.
|
|
||||||
|
|
||||||
This function is not suitable for long distance searches.
|
|
||||||
*/
|
|
||||||
void dtPathCorridor::optimizePathVisibility(const float* next, const float pathOptimizationRange,
|
|
||||||
dtNavMeshQuery* navquery, const dtQueryFilter* filter)
|
|
||||||
{
|
|
||||||
dtAssert(m_path);
|
|
||||||
|
|
||||||
// Clamp the ray to max distance.
|
|
||||||
float goal[3];
|
|
||||||
dtVcopy(goal, next);
|
|
||||||
float dist = dtVdist2D(m_pos, goal);
|
|
||||||
|
|
||||||
// If too close to the goal, do not try to optimize.
|
|
||||||
if (dist < 0.01f)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Overshoot a little. This helps to optimize open fields in tiled meshes.
|
|
||||||
dist = dtMin(dist+0.01f, pathOptimizationRange);
|
|
||||||
|
|
||||||
// Adjust ray length.
|
|
||||||
float delta[3];
|
|
||||||
dtVsub(delta, goal, m_pos);
|
|
||||||
dtVmad(goal, m_pos, delta, pathOptimizationRange/dist);
|
|
||||||
|
|
||||||
static const int MAX_RES = 32;
|
|
||||||
dtPolyRef res[MAX_RES];
|
|
||||||
float t, norm[3];
|
|
||||||
int nres = 0;
|
|
||||||
navquery->raycast(m_path[0], m_pos, goal, filter, &t, norm, res, &nres, MAX_RES);
|
|
||||||
if (nres > 1 && t > 0.99f)
|
|
||||||
{
|
|
||||||
m_npath = dtMergeCorridorStartShortcut(m_path, m_npath, m_maxPath, res, nres);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
@par
|
|
||||||
|
|
||||||
Inaccurate locomotion or dynamic obstacle avoidance can force the agent position significantly outside the
|
|
||||||
original corridor. Over time this can result in the formation of a non-optimal corridor. This function will use a
|
|
||||||
local area path search to try to re-optimize the corridor.
|
|
||||||
|
|
||||||
The more inaccurate the agent movement, the more beneficial this function becomes. Simply adjust the frequency of
|
|
||||||
the call to match the needs to the agent.
|
|
||||||
*/
|
|
||||||
bool dtPathCorridor::optimizePathTopology(dtNavMeshQuery* navquery, const dtQueryFilter* filter)
|
|
||||||
{
|
|
||||||
dtAssert(navquery);
|
|
||||||
dtAssert(filter);
|
|
||||||
dtAssert(m_path);
|
|
||||||
|
|
||||||
if (m_npath < 3)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
static const int MAX_ITER = 32;
|
|
||||||
static const int MAX_RES = 32;
|
|
||||||
|
|
||||||
dtPolyRef res[MAX_RES];
|
|
||||||
int nres = 0;
|
|
||||||
navquery->initSlicedFindPath(m_path[0], m_path[m_npath-1], m_pos, m_target, filter);
|
|
||||||
navquery->updateSlicedFindPath(MAX_ITER, 0);
|
|
||||||
dtStatus status = navquery->finalizeSlicedFindPathPartial(m_path, m_npath, res, &nres, MAX_RES);
|
|
||||||
|
|
||||||
if (dtStatusSucceed(status) && nres > 0)
|
|
||||||
{
|
|
||||||
m_npath = dtMergeCorridorStartShortcut(m_path, m_npath, m_maxPath, res, nres);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool dtPathCorridor::moveOverOffmeshConnection(dtPolyRef offMeshConRef, dtPolyRef* refs,
|
|
||||||
float* startPos, float* endPos,
|
|
||||||
dtNavMeshQuery* navquery)
|
|
||||||
{
|
|
||||||
dtAssert(navquery);
|
|
||||||
dtAssert(m_path);
|
|
||||||
dtAssert(m_npath);
|
|
||||||
|
|
||||||
// Advance the path up to and over the off-mesh connection.
|
|
||||||
dtPolyRef prevRef = 0, polyRef = m_path[0];
|
|
||||||
int npos = 0;
|
|
||||||
while (npos < m_npath && polyRef != offMeshConRef)
|
|
||||||
{
|
|
||||||
prevRef = polyRef;
|
|
||||||
polyRef = m_path[npos];
|
|
||||||
npos++;
|
|
||||||
}
|
|
||||||
if (npos == m_npath)
|
|
||||||
{
|
|
||||||
// Could not find offMeshConRef
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prune path
|
|
||||||
for (int i = npos; i < m_npath; ++i)
|
|
||||||
m_path[i-npos] = m_path[i];
|
|
||||||
m_npath -= npos;
|
|
||||||
|
|
||||||
refs[0] = prevRef;
|
|
||||||
refs[1] = polyRef;
|
|
||||||
|
|
||||||
const dtNavMesh* nav = navquery->getAttachedNavMesh();
|
|
||||||
dtAssert(nav);
|
|
||||||
|
|
||||||
dtStatus status = nav->getOffMeshConnectionPolyEndPoints(refs[0], refs[1], startPos, endPos);
|
|
||||||
if (dtStatusSucceed(status))
|
|
||||||
{
|
|
||||||
dtVcopy(m_pos, endPos);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
@par
|
|
||||||
|
|
||||||
Behavior:
|
|
||||||
|
|
||||||
- The movement is constrained to the surface of the navigation mesh.
|
|
||||||
- The corridor is automatically adjusted (shorted or lengthened) in order to remain valid.
|
|
||||||
- The new position will be located in the adjusted corridor's first polygon.
|
|
||||||
|
|
||||||
The expected use case is that the desired position will be 'near' the current corridor. What is considered 'near'
|
|
||||||
depends on local polygon density, query search half extents, etc.
|
|
||||||
|
|
||||||
The resulting position will differ from the desired position if the desired position is not on the navigation mesh,
|
|
||||||
or it can't be reached using a local search.
|
|
||||||
*/
|
|
||||||
bool dtPathCorridor::movePosition(const float* npos, dtNavMeshQuery* navquery, const dtQueryFilter* filter)
|
|
||||||
{
|
|
||||||
dtAssert(m_path);
|
|
||||||
dtAssert(m_npath);
|
|
||||||
|
|
||||||
// Move along navmesh and update new position.
|
|
||||||
float result[3];
|
|
||||||
static const int MAX_VISITED = 16;
|
|
||||||
dtPolyRef visited[MAX_VISITED];
|
|
||||||
int nvisited = 0;
|
|
||||||
dtStatus status = navquery->moveAlongSurface(m_path[0], m_pos, npos, filter,
|
|
||||||
result, visited, &nvisited, MAX_VISITED);
|
|
||||||
if (dtStatusSucceed(status)) {
|
|
||||||
m_npath = dtMergeCorridorStartMoved(m_path, m_npath, m_maxPath, visited, nvisited);
|
|
||||||
|
|
||||||
// Adjust the position to stay on top of the navmesh.
|
|
||||||
float h = m_pos[1];
|
|
||||||
navquery->getPolyHeight(m_path[0], result, &h);
|
|
||||||
result[1] = h;
|
|
||||||
dtVcopy(m_pos, result);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
@par
|
|
||||||
|
|
||||||
Behavior:
|
|
||||||
|
|
||||||
- The movement is constrained to the surface of the navigation mesh.
|
|
||||||
- The corridor is automatically adjusted (shorted or lengthened) in order to remain valid.
|
|
||||||
- The new target will be located in the adjusted corridor's last polygon.
|
|
||||||
|
|
||||||
The expected use case is that the desired target will be 'near' the current corridor. What is considered 'near' depends on local polygon density, query search half extents, etc.
|
|
||||||
|
|
||||||
The resulting target will differ from the desired target if the desired target is not on the navigation mesh, or it can't be reached using a local search.
|
|
||||||
*/
|
|
||||||
bool dtPathCorridor::moveTargetPosition(const float* npos, dtNavMeshQuery* navquery, const dtQueryFilter* filter)
|
|
||||||
{
|
|
||||||
dtAssert(m_path);
|
|
||||||
dtAssert(m_npath);
|
|
||||||
|
|
||||||
// Move along navmesh and update new position.
|
|
||||||
float result[3];
|
|
||||||
static const int MAX_VISITED = 16;
|
|
||||||
dtPolyRef visited[MAX_VISITED];
|
|
||||||
int nvisited = 0;
|
|
||||||
dtStatus status = navquery->moveAlongSurface(m_path[m_npath-1], m_target, npos, filter,
|
|
||||||
result, visited, &nvisited, MAX_VISITED);
|
|
||||||
if (dtStatusSucceed(status))
|
|
||||||
{
|
|
||||||
m_npath = dtMergeCorridorEndMoved(m_path, m_npath, m_maxPath, visited, nvisited);
|
|
||||||
// TODO: should we do that?
|
|
||||||
// Adjust the position to stay on top of the navmesh.
|
|
||||||
/* float h = m_target[1];
|
|
||||||
navquery->getPolyHeight(m_path[m_npath-1], result, &h);
|
|
||||||
result[1] = h;*/
|
|
||||||
|
|
||||||
dtVcopy(m_target, result);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @par
|
|
||||||
///
|
|
||||||
/// The current corridor position is expected to be within the first polygon in the path. The target
|
|
||||||
/// is expected to be in the last polygon.
|
|
||||||
///
|
|
||||||
/// @warning The size of the path must not exceed the size of corridor's path buffer set during #init().
|
|
||||||
void dtPathCorridor::setCorridor(const float* target, const dtPolyRef* path, const int npath)
|
|
||||||
{
|
|
||||||
dtAssert(m_path);
|
|
||||||
dtAssert(npath > 0);
|
|
||||||
dtAssert(npath < m_maxPath);
|
|
||||||
|
|
||||||
dtVcopy(m_target, target);
|
|
||||||
memcpy(m_path, path, sizeof(dtPolyRef)*npath);
|
|
||||||
m_npath = npath;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool dtPathCorridor::fixPathStart(dtPolyRef safeRef, const float* safePos)
|
|
||||||
{
|
|
||||||
dtAssert(m_path);
|
|
||||||
|
|
||||||
dtVcopy(m_pos, safePos);
|
|
||||||
if (m_npath < 3 && m_npath > 0)
|
|
||||||
{
|
|
||||||
m_path[2] = m_path[m_npath-1];
|
|
||||||
m_path[0] = safeRef;
|
|
||||||
m_path[1] = 0;
|
|
||||||
m_npath = 3;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_path[0] = safeRef;
|
|
||||||
m_path[1] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool dtPathCorridor::trimInvalidPath(dtPolyRef safeRef, const float* safePos,
|
|
||||||
dtNavMeshQuery* navquery, const dtQueryFilter* filter)
|
|
||||||
{
|
|
||||||
dtAssert(navquery);
|
|
||||||
dtAssert(filter);
|
|
||||||
dtAssert(m_path);
|
|
||||||
|
|
||||||
// Keep valid path as far as possible.
|
|
||||||
int n = 0;
|
|
||||||
while (n < m_npath && navquery->isValidPolyRef(m_path[n], filter)) {
|
|
||||||
n++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n == m_npath)
|
|
||||||
{
|
|
||||||
// All valid, no need to fix.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (n == 0)
|
|
||||||
{
|
|
||||||
// The first polyref is bad, use current safe values.
|
|
||||||
dtVcopy(m_pos, safePos);
|
|
||||||
m_path[0] = safeRef;
|
|
||||||
m_npath = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// The path is partially usable.
|
|
||||||
m_npath = n;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clamp target pos to last poly
|
|
||||||
float tgt[3];
|
|
||||||
dtVcopy(tgt, m_target);
|
|
||||||
navquery->closestPointOnPolyBoundary(m_path[m_npath-1], tgt, m_target);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @par
|
|
||||||
///
|
|
||||||
/// The path can be invalidated if there are structural changes to the underlying navigation mesh, or the state of
|
|
||||||
/// a polygon within the path changes resulting in it being filtered out. (E.g. An exclusion or inclusion flag changes.)
|
|
||||||
bool dtPathCorridor::isValid(const int maxLookAhead, dtNavMeshQuery* navquery, const dtQueryFilter* filter)
|
|
||||||
{
|
|
||||||
// Check that all polygons still pass query filter.
|
|
||||||
const int n = dtMin(m_npath, maxLookAhead);
|
|
||||||
for (int i = 0; i < n; ++i)
|
|
||||||
{
|
|
||||||
if (!navquery->isValidPolyRef(m_path[i], filter))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
@ -1,200 +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.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include "DetourPathQueue.h"
|
|
||||||
#include "DetourNavMesh.h"
|
|
||||||
#include "DetourNavMeshQuery.h"
|
|
||||||
#include "DetourAlloc.h"
|
|
||||||
#include "DetourCommon.h"
|
|
||||||
|
|
||||||
|
|
||||||
dtPathQueue::dtPathQueue() :
|
|
||||||
m_nextHandle(1),
|
|
||||||
m_maxPathSize(0),
|
|
||||||
m_queueHead(0),
|
|
||||||
m_navquery(0)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < MAX_QUEUE; ++i)
|
|
||||||
m_queue[i].path = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
dtPathQueue::~dtPathQueue()
|
|
||||||
{
|
|
||||||
purge();
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtPathQueue::purge()
|
|
||||||
{
|
|
||||||
dtFreeNavMeshQuery(m_navquery);
|
|
||||||
m_navquery = 0;
|
|
||||||
for (int i = 0; i < MAX_QUEUE; ++i)
|
|
||||||
{
|
|
||||||
dtFree(m_queue[i].path);
|
|
||||||
m_queue[i].path = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool dtPathQueue::init(const int maxPathSize, const int maxSearchNodeCount, dtNavMesh* nav)
|
|
||||||
{
|
|
||||||
purge();
|
|
||||||
|
|
||||||
m_navquery = dtAllocNavMeshQuery();
|
|
||||||
if (!m_navquery)
|
|
||||||
return false;
|
|
||||||
if (dtStatusFailed(m_navquery->init(nav, maxSearchNodeCount)))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
m_maxPathSize = maxPathSize;
|
|
||||||
for (int i = 0; i < MAX_QUEUE; ++i)
|
|
||||||
{
|
|
||||||
m_queue[i].ref = DT_PATHQ_INVALID;
|
|
||||||
m_queue[i].path = (dtPolyRef*)dtAlloc(sizeof(dtPolyRef)*m_maxPathSize, DT_ALLOC_PERM);
|
|
||||||
if (!m_queue[i].path)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_queueHead = 0;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtPathQueue::update(const int maxIters)
|
|
||||||
{
|
|
||||||
static const int MAX_KEEP_ALIVE = 2; // in update ticks.
|
|
||||||
|
|
||||||
// Update path request until there is nothing to update
|
|
||||||
// or upto maxIters pathfinder iterations has been consumed.
|
|
||||||
int iterCount = maxIters;
|
|
||||||
|
|
||||||
for (int i = 0; i < MAX_QUEUE; ++i)
|
|
||||||
{
|
|
||||||
PathQuery& q = m_queue[m_queueHead % MAX_QUEUE];
|
|
||||||
|
|
||||||
// Skip inactive requests.
|
|
||||||
if (q.ref == DT_PATHQ_INVALID)
|
|
||||||
{
|
|
||||||
m_queueHead++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle completed request.
|
|
||||||
if (dtStatusSucceed(q.status) || dtStatusFailed(q.status))
|
|
||||||
{
|
|
||||||
// If the path result has not been read in few frames, free the slot.
|
|
||||||
q.keepAlive++;
|
|
||||||
if (q.keepAlive > MAX_KEEP_ALIVE)
|
|
||||||
{
|
|
||||||
q.ref = DT_PATHQ_INVALID;
|
|
||||||
q.status = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_queueHead++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle query start.
|
|
||||||
if (q.status == 0)
|
|
||||||
{
|
|
||||||
q.status = m_navquery->initSlicedFindPath(q.startRef, q.endRef, q.startPos, q.endPos, q.filter);
|
|
||||||
}
|
|
||||||
// Handle query in progress.
|
|
||||||
if (dtStatusInProgress(q.status))
|
|
||||||
{
|
|
||||||
int iters = 0;
|
|
||||||
q.status = m_navquery->updateSlicedFindPath(iterCount, &iters);
|
|
||||||
iterCount -= iters;
|
|
||||||
}
|
|
||||||
if (dtStatusSucceed(q.status))
|
|
||||||
{
|
|
||||||
q.status = m_navquery->finalizeSlicedFindPath(q.path, &q.npath, m_maxPathSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iterCount <= 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
m_queueHead++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dtPathQueueRef dtPathQueue::request(dtPolyRef startRef, dtPolyRef endRef,
|
|
||||||
const float* startPos, const float* endPos,
|
|
||||||
const dtQueryFilter* filter)
|
|
||||||
{
|
|
||||||
// Find empty slot
|
|
||||||
int slot = -1;
|
|
||||||
for (int i = 0; i < MAX_QUEUE; ++i)
|
|
||||||
{
|
|
||||||
if (m_queue[i].ref == DT_PATHQ_INVALID)
|
|
||||||
{
|
|
||||||
slot = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Could not find slot.
|
|
||||||
if (slot == -1)
|
|
||||||
return DT_PATHQ_INVALID;
|
|
||||||
|
|
||||||
dtPathQueueRef ref = m_nextHandle++;
|
|
||||||
if (m_nextHandle == DT_PATHQ_INVALID) m_nextHandle++;
|
|
||||||
|
|
||||||
PathQuery& q = m_queue[slot];
|
|
||||||
q.ref = ref;
|
|
||||||
dtVcopy(q.startPos, startPos);
|
|
||||||
q.startRef = startRef;
|
|
||||||
dtVcopy(q.endPos, endPos);
|
|
||||||
q.endRef = endRef;
|
|
||||||
|
|
||||||
q.status = 0;
|
|
||||||
q.npath = 0;
|
|
||||||
q.filter = filter;
|
|
||||||
q.keepAlive = 0;
|
|
||||||
|
|
||||||
return ref;
|
|
||||||
}
|
|
||||||
|
|
||||||
dtStatus dtPathQueue::getRequestStatus(dtPathQueueRef ref) const
|
|
||||||
{
|
|
||||||
for (int i = 0; i < MAX_QUEUE; ++i)
|
|
||||||
{
|
|
||||||
if (m_queue[i].ref == ref)
|
|
||||||
return m_queue[i].status;
|
|
||||||
}
|
|
||||||
return DT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
dtStatus dtPathQueue::getPathResult(dtPathQueueRef ref, dtPolyRef* path, int* pathSize, const int maxPath)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < MAX_QUEUE; ++i)
|
|
||||||
{
|
|
||||||
if (m_queue[i].ref == ref)
|
|
||||||
{
|
|
||||||
PathQuery& q = m_queue[i];
|
|
||||||
dtStatus details = q.status & DT_STATUS_DETAIL_MASK;
|
|
||||||
// Free request for reuse.
|
|
||||||
q.ref = DT_PATHQ_INVALID;
|
|
||||||
q.status = 0;
|
|
||||||
// Copy path
|
|
||||||
int n = dtMin(q.npath, maxPath);
|
|
||||||
memcpy(path, q.path, sizeof(dtPolyRef)*n);
|
|
||||||
*pathSize = n;
|
|
||||||
return details | DT_SUCCESS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return DT_FAILURE;
|
|
||||||
}
|
|
@ -1,194 +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.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <new>
|
|
||||||
#include "DetourProximityGrid.h"
|
|
||||||
#include "DetourCommon.h"
|
|
||||||
#include "DetourMath.h"
|
|
||||||
#include "DetourAlloc.h"
|
|
||||||
#include "DetourAssert.h"
|
|
||||||
|
|
||||||
|
|
||||||
dtProximityGrid* dtAllocProximityGrid()
|
|
||||||
{
|
|
||||||
void* mem = dtAlloc(sizeof(dtProximityGrid), DT_ALLOC_PERM);
|
|
||||||
if (!mem) return 0;
|
|
||||||
return new(mem) dtProximityGrid;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtFreeProximityGrid(dtProximityGrid* ptr)
|
|
||||||
{
|
|
||||||
if (!ptr) return;
|
|
||||||
ptr->~dtProximityGrid();
|
|
||||||
dtFree(ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inline int hashPos2(int x, int y, int n)
|
|
||||||
{
|
|
||||||
return ((x*73856093) ^ (y*19349663)) & (n-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
dtProximityGrid::dtProximityGrid() :
|
|
||||||
m_cellSize(0),
|
|
||||||
m_invCellSize(0),
|
|
||||||
m_pool(0),
|
|
||||||
m_poolHead(0),
|
|
||||||
m_poolSize(0),
|
|
||||||
m_buckets(0),
|
|
||||||
m_bucketsSize(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
dtProximityGrid::~dtProximityGrid()
|
|
||||||
{
|
|
||||||
dtFree(m_buckets);
|
|
||||||
dtFree(m_pool);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool dtProximityGrid::init(const int poolSize, const float cellSize)
|
|
||||||
{
|
|
||||||
dtAssert(poolSize > 0);
|
|
||||||
dtAssert(cellSize > 0.0f);
|
|
||||||
|
|
||||||
m_cellSize = cellSize;
|
|
||||||
m_invCellSize = 1.0f / m_cellSize;
|
|
||||||
|
|
||||||
// Allocate hashs buckets
|
|
||||||
m_bucketsSize = dtNextPow2(poolSize);
|
|
||||||
m_buckets = (unsigned short*)dtAlloc(sizeof(unsigned short)*m_bucketsSize, DT_ALLOC_PERM);
|
|
||||||
if (!m_buckets)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Allocate pool of items.
|
|
||||||
m_poolSize = poolSize;
|
|
||||||
m_poolHead = 0;
|
|
||||||
m_pool = (Item*)dtAlloc(sizeof(Item)*m_poolSize, DT_ALLOC_PERM);
|
|
||||||
if (!m_pool)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
clear();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtProximityGrid::clear()
|
|
||||||
{
|
|
||||||
memset(m_buckets, 0xff, sizeof(unsigned short)*m_bucketsSize);
|
|
||||||
m_poolHead = 0;
|
|
||||||
m_bounds[0] = 0xffff;
|
|
||||||
m_bounds[1] = 0xffff;
|
|
||||||
m_bounds[2] = -0xffff;
|
|
||||||
m_bounds[3] = -0xffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtProximityGrid::addItem(const unsigned short id,
|
|
||||||
const float minx, const float miny,
|
|
||||||
const float maxx, const float maxy)
|
|
||||||
{
|
|
||||||
const int iminx = (int)dtMathFloorf(minx * m_invCellSize);
|
|
||||||
const int iminy = (int)dtMathFloorf(miny * m_invCellSize);
|
|
||||||
const int imaxx = (int)dtMathFloorf(maxx * m_invCellSize);
|
|
||||||
const int imaxy = (int)dtMathFloorf(maxy * m_invCellSize);
|
|
||||||
|
|
||||||
m_bounds[0] = dtMin(m_bounds[0], iminx);
|
|
||||||
m_bounds[1] = dtMin(m_bounds[1], iminy);
|
|
||||||
m_bounds[2] = dtMax(m_bounds[2], imaxx);
|
|
||||||
m_bounds[3] = dtMax(m_bounds[3], imaxy);
|
|
||||||
|
|
||||||
for (int y = iminy; y <= imaxy; ++y)
|
|
||||||
{
|
|
||||||
for (int x = iminx; x <= imaxx; ++x)
|
|
||||||
{
|
|
||||||
if (m_poolHead < m_poolSize)
|
|
||||||
{
|
|
||||||
const int h = hashPos2(x, y, m_bucketsSize);
|
|
||||||
const unsigned short idx = (unsigned short)m_poolHead;
|
|
||||||
m_poolHead++;
|
|
||||||
Item& item = m_pool[idx];
|
|
||||||
item.x = (short)x;
|
|
||||||
item.y = (short)y;
|
|
||||||
item.id = id;
|
|
||||||
item.next = m_buckets[h];
|
|
||||||
m_buckets[h] = idx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int dtProximityGrid::queryItems(const float minx, const float miny,
|
|
||||||
const float maxx, const float maxy,
|
|
||||||
unsigned short* ids, const int maxIds) const
|
|
||||||
{
|
|
||||||
const int iminx = (int)dtMathFloorf(minx * m_invCellSize);
|
|
||||||
const int iminy = (int)dtMathFloorf(miny * m_invCellSize);
|
|
||||||
const int imaxx = (int)dtMathFloorf(maxx * m_invCellSize);
|
|
||||||
const int imaxy = (int)dtMathFloorf(maxy * m_invCellSize);
|
|
||||||
|
|
||||||
int n = 0;
|
|
||||||
|
|
||||||
for (int y = iminy; y <= imaxy; ++y)
|
|
||||||
{
|
|
||||||
for (int x = iminx; x <= imaxx; ++x)
|
|
||||||
{
|
|
||||||
const int h = hashPos2(x, y, m_bucketsSize);
|
|
||||||
unsigned short idx = m_buckets[h];
|
|
||||||
while (idx != 0xffff)
|
|
||||||
{
|
|
||||||
Item& item = m_pool[idx];
|
|
||||||
if ((int)item.x == x && (int)item.y == y)
|
|
||||||
{
|
|
||||||
// Check if the id exists already.
|
|
||||||
const unsigned short* end = ids + n;
|
|
||||||
unsigned short* i = ids;
|
|
||||||
while (i != end && *i != item.id)
|
|
||||||
++i;
|
|
||||||
// Item not found, add it.
|
|
||||||
if (i == end)
|
|
||||||
{
|
|
||||||
if (n >= maxIds)
|
|
||||||
return n;
|
|
||||||
ids[n++] = item.id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
idx = item.next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dtProximityGrid::getItemCountAt(const int x, const int y) const
|
|
||||||
{
|
|
||||||
int n = 0;
|
|
||||||
|
|
||||||
const int h = hashPos2(x, y, m_bucketsSize);
|
|
||||||
unsigned short idx = m_buckets[h];
|
|
||||||
while (idx != 0xffff)
|
|
||||||
{
|
|
||||||
Item& item = m_pool[idx];
|
|
||||||
if ((int)item.x == x && (int)item.y == y)
|
|
||||||
n++;
|
|
||||||
idx = item.next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return n;
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
file(GLOB SOURCES Source/*.cpp)
|
|
||||||
add_library(DetourTileCache ${SOURCES})
|
|
||||||
|
|
||||||
add_library(RecastNavigation::DetourTileCache ALIAS DetourTileCache)
|
|
||||||
set_target_properties(DetourTileCache PROPERTIES DEBUG_POSTFIX -d)
|
|
||||||
|
|
||||||
set(DetourTileCache_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Include")
|
|
||||||
|
|
||||||
target_include_directories(DetourTileCache PUBLIC
|
|
||||||
"$<BUILD_INTERFACE:${DetourTileCache_INCLUDE_DIR}>"
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(DetourTileCache
|
|
||||||
Detour
|
|
||||||
)
|
|
||||||
|
|
||||||
set_target_properties(DetourTileCache PROPERTIES
|
|
||||||
SOVERSION ${SOVERSION}
|
|
||||||
VERSION ${VERSION}
|
|
||||||
COMPILE_PDB_OUTPUT_DIRECTORY .
|
|
||||||
COMPILE_PDB_NAME "DetourTileCache-d"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
install(TARGETS DetourTileCache
|
|
||||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
|
||||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
||||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
||||||
COMPONENT library
|
|
||||||
)
|
|
||||||
|
|
||||||
file(GLOB INCLUDES Include/*.h)
|
|
||||||
install(FILES ${INCLUDES} DESTINATION
|
|
||||||
${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation)
|
|
||||||
install(FILES "$<TARGET_FILE_DIR:DetourTileCache>/DetourTileCache-d.pdb" CONFIGURATIONS "Debug" DESTINATION "lib")
|
|
@ -1,262 +0,0 @@
|
|||||||
#ifndef DETOURTILECACHE_H
|
|
||||||
#define DETOURTILECACHE_H
|
|
||||||
|
|
||||||
#include "DetourStatus.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
typedef unsigned int dtObstacleRef;
|
|
||||||
|
|
||||||
typedef unsigned int dtCompressedTileRef;
|
|
||||||
|
|
||||||
/// Flags for addTile
|
|
||||||
enum dtCompressedTileFlags
|
|
||||||
{
|
|
||||||
DT_COMPRESSEDTILE_FREE_DATA = 0x01, ///< Navmesh owns the tile memory and should free it.
|
|
||||||
};
|
|
||||||
|
|
||||||
struct dtCompressedTile
|
|
||||||
{
|
|
||||||
unsigned int salt; ///< Counter describing modifications to the tile.
|
|
||||||
struct dtTileCacheLayerHeader* header;
|
|
||||||
unsigned char* compressed;
|
|
||||||
int compressedSize;
|
|
||||||
unsigned char* data;
|
|
||||||
int dataSize;
|
|
||||||
unsigned int flags;
|
|
||||||
dtCompressedTile* next;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum ObstacleState
|
|
||||||
{
|
|
||||||
DT_OBSTACLE_EMPTY,
|
|
||||||
DT_OBSTACLE_PROCESSING,
|
|
||||||
DT_OBSTACLE_PROCESSED,
|
|
||||||
DT_OBSTACLE_REMOVING,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum ObstacleType
|
|
||||||
{
|
|
||||||
DT_OBSTACLE_CYLINDER,
|
|
||||||
DT_OBSTACLE_BOX, // AABB
|
|
||||||
DT_OBSTACLE_ORIENTED_BOX, // OBB
|
|
||||||
};
|
|
||||||
|
|
||||||
struct dtObstacleCylinder
|
|
||||||
{
|
|
||||||
float pos[ 3 ];
|
|
||||||
float radius;
|
|
||||||
float height;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct dtObstacleBox
|
|
||||||
{
|
|
||||||
float bmin[ 3 ];
|
|
||||||
float bmax[ 3 ];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct dtObstacleOrientedBox
|
|
||||||
{
|
|
||||||
float center[ 3 ];
|
|
||||||
float halfExtents[ 3 ];
|
|
||||||
float rotAux[ 2 ]; //{ cos(0.5f*angle)*sin(-0.5f*angle); cos(0.5f*angle)*cos(0.5f*angle) - 0.5 }
|
|
||||||
};
|
|
||||||
|
|
||||||
static const int DT_MAX_TOUCHED_TILES = 8;
|
|
||||||
struct dtTileCacheObstacle
|
|
||||||
{
|
|
||||||
union
|
|
||||||
{
|
|
||||||
dtObstacleCylinder cylinder;
|
|
||||||
dtObstacleBox box;
|
|
||||||
dtObstacleOrientedBox orientedBox;
|
|
||||||
};
|
|
||||||
|
|
||||||
dtCompressedTileRef touched[DT_MAX_TOUCHED_TILES];
|
|
||||||
dtCompressedTileRef pending[DT_MAX_TOUCHED_TILES];
|
|
||||||
unsigned short salt;
|
|
||||||
unsigned char type;
|
|
||||||
unsigned char state;
|
|
||||||
unsigned char ntouched;
|
|
||||||
unsigned char npending;
|
|
||||||
dtTileCacheObstacle* next;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct dtTileCacheParams
|
|
||||||
{
|
|
||||||
float orig[3];
|
|
||||||
float cs, ch;
|
|
||||||
int width, height;
|
|
||||||
float walkableHeight;
|
|
||||||
float walkableRadius;
|
|
||||||
float walkableClimb;
|
|
||||||
float maxSimplificationError;
|
|
||||||
int maxTiles;
|
|
||||||
int maxObstacles;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct dtTileCacheMeshProcess
|
|
||||||
{
|
|
||||||
virtual ~dtTileCacheMeshProcess() { }
|
|
||||||
|
|
||||||
virtual void process(struct dtNavMeshCreateParams* params,
|
|
||||||
unsigned char* polyAreas, unsigned short* polyFlags) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class dtTileCache
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
dtTileCache();
|
|
||||||
~dtTileCache();
|
|
||||||
|
|
||||||
struct dtTileCacheAlloc* getAlloc() { return m_talloc; }
|
|
||||||
struct dtTileCacheCompressor* getCompressor() { return m_tcomp; }
|
|
||||||
const dtTileCacheParams* getParams() const { return &m_params; }
|
|
||||||
|
|
||||||
inline int getTileCount() const { return m_params.maxTiles; }
|
|
||||||
inline const dtCompressedTile* getTile(const int i) const { return &m_tiles[i]; }
|
|
||||||
|
|
||||||
inline int getObstacleCount() const { return m_params.maxObstacles; }
|
|
||||||
inline const dtTileCacheObstacle* getObstacle(const int i) const { return &m_obstacles[i]; }
|
|
||||||
|
|
||||||
const dtTileCacheObstacle* getObstacleByRef(dtObstacleRef ref);
|
|
||||||
|
|
||||||
dtObstacleRef getObstacleRef(const dtTileCacheObstacle* obmin) const;
|
|
||||||
|
|
||||||
dtStatus init(const dtTileCacheParams* params,
|
|
||||||
struct dtTileCacheAlloc* talloc,
|
|
||||||
struct dtTileCacheCompressor* tcomp,
|
|
||||||
struct dtTileCacheMeshProcess* tmproc);
|
|
||||||
|
|
||||||
int getTilesAt(const int tx, const int ty, dtCompressedTileRef* tiles, const int maxTiles) const ;
|
|
||||||
|
|
||||||
dtCompressedTile* getTileAt(const int tx, const int ty, const int tlayer);
|
|
||||||
dtCompressedTileRef getTileRef(const dtCompressedTile* tile) const;
|
|
||||||
const dtCompressedTile* getTileByRef(dtCompressedTileRef ref) const;
|
|
||||||
|
|
||||||
dtStatus addTile(unsigned char* data, const int dataSize, unsigned char flags, dtCompressedTileRef* result);
|
|
||||||
|
|
||||||
dtStatus removeTile(dtCompressedTileRef ref, unsigned char** data, int* dataSize);
|
|
||||||
|
|
||||||
// Cylinder obstacle.
|
|
||||||
dtStatus addObstacle(const float* pos, const float radius, const float height, dtObstacleRef* result);
|
|
||||||
|
|
||||||
// Aabb obstacle.
|
|
||||||
dtStatus addBoxObstacle(const float* bmin, const float* bmax, dtObstacleRef* result);
|
|
||||||
|
|
||||||
// Box obstacle: can be rotated in Y.
|
|
||||||
dtStatus addBoxObstacle(const float* center, const float* halfExtents, const float yRadians, dtObstacleRef* result);
|
|
||||||
|
|
||||||
dtStatus removeObstacle(const dtObstacleRef ref);
|
|
||||||
|
|
||||||
dtStatus queryTiles(const float* bmin, const float* bmax,
|
|
||||||
dtCompressedTileRef* results, int* resultCount, const int maxResults) const;
|
|
||||||
|
|
||||||
/// Updates the tile cache by rebuilding tiles touched by unfinished obstacle requests.
|
|
||||||
/// @param[in] dt The time step size. Currently not used.
|
|
||||||
/// @param[in] navmesh The mesh to affect when rebuilding tiles.
|
|
||||||
/// @param[out] upToDate Whether the tile cache is fully up to date with obstacle requests and tile rebuilds.
|
|
||||||
/// If the tile cache is up to date another (immediate) call to update will have no effect;
|
|
||||||
/// otherwise another call will continue processing obstacle requests and tile rebuilds.
|
|
||||||
dtStatus update(const float dt, class dtNavMesh* navmesh, bool* upToDate = 0);
|
|
||||||
|
|
||||||
dtStatus buildNavMeshTilesAt(const int tx, const int ty, class dtNavMesh* navmesh);
|
|
||||||
|
|
||||||
dtStatus buildNavMeshTile(const dtCompressedTileRef ref, class dtNavMesh* navmesh);
|
|
||||||
|
|
||||||
void calcTightTileBounds(const struct dtTileCacheLayerHeader* header, float* bmin, float* bmax) const;
|
|
||||||
|
|
||||||
void getObstacleBounds(const struct dtTileCacheObstacle* ob, float* bmin, float* bmax) const;
|
|
||||||
|
|
||||||
|
|
||||||
/// Encodes a tile id.
|
|
||||||
inline dtCompressedTileRef encodeTileId(unsigned int salt, unsigned int it) const
|
|
||||||
{
|
|
||||||
return ((dtCompressedTileRef)salt << m_tileBits) | (dtCompressedTileRef)it;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Decodes a tile salt.
|
|
||||||
inline unsigned int decodeTileIdSalt(dtCompressedTileRef ref) const
|
|
||||||
{
|
|
||||||
const dtCompressedTileRef saltMask = ((dtCompressedTileRef)1<<m_saltBits)-1;
|
|
||||||
return (unsigned int)((ref >> m_tileBits) & saltMask);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Decodes a tile id.
|
|
||||||
inline unsigned int decodeTileIdTile(dtCompressedTileRef ref) const
|
|
||||||
{
|
|
||||||
const dtCompressedTileRef tileMask = ((dtCompressedTileRef)1<<m_tileBits)-1;
|
|
||||||
return (unsigned int)(ref & tileMask);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Encodes an obstacle id.
|
|
||||||
inline dtObstacleRef encodeObstacleId(unsigned int salt, unsigned int it) const
|
|
||||||
{
|
|
||||||
return ((dtObstacleRef)salt << 16) | (dtObstacleRef)it;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Decodes an obstacle salt.
|
|
||||||
inline unsigned int decodeObstacleIdSalt(dtObstacleRef ref) const
|
|
||||||
{
|
|
||||||
const dtObstacleRef saltMask = ((dtObstacleRef)1<<16)-1;
|
|
||||||
return (unsigned int)((ref >> 16) & saltMask);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Decodes an obstacle id.
|
|
||||||
inline unsigned int decodeObstacleIdObstacle(dtObstacleRef ref) const
|
|
||||||
{
|
|
||||||
const dtObstacleRef tileMask = ((dtObstacleRef)1<<16)-1;
|
|
||||||
return (unsigned int)(ref & tileMask);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Explicitly disabled copy constructor and copy assignment operator.
|
|
||||||
dtTileCache(const dtTileCache&);
|
|
||||||
dtTileCache& operator=(const dtTileCache&);
|
|
||||||
|
|
||||||
enum ObstacleRequestAction
|
|
||||||
{
|
|
||||||
REQUEST_ADD,
|
|
||||||
REQUEST_REMOVE,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ObstacleRequest
|
|
||||||
{
|
|
||||||
int action;
|
|
||||||
dtObstacleRef ref;
|
|
||||||
};
|
|
||||||
|
|
||||||
int m_tileLutSize; ///< Tile hash lookup size (must be pot).
|
|
||||||
int m_tileLutMask; ///< Tile hash lookup mask.
|
|
||||||
|
|
||||||
dtCompressedTile** m_posLookup; ///< Tile hash lookup.
|
|
||||||
dtCompressedTile* m_nextFreeTile; ///< Freelist of tiles.
|
|
||||||
dtCompressedTile* m_tiles; ///< List of tiles.
|
|
||||||
|
|
||||||
unsigned int m_saltBits; ///< Number of salt bits in the tile ID.
|
|
||||||
unsigned int m_tileBits; ///< Number of tile bits in the tile ID.
|
|
||||||
|
|
||||||
dtTileCacheParams m_params;
|
|
||||||
|
|
||||||
dtTileCacheAlloc* m_talloc;
|
|
||||||
dtTileCacheCompressor* m_tcomp;
|
|
||||||
dtTileCacheMeshProcess* m_tmproc;
|
|
||||||
|
|
||||||
dtTileCacheObstacle* m_obstacles;
|
|
||||||
dtTileCacheObstacle* m_nextFreeObstacle;
|
|
||||||
|
|
||||||
static const int MAX_REQUESTS = 64;
|
|
||||||
ObstacleRequest m_reqs[MAX_REQUESTS];
|
|
||||||
int m_nreqs;
|
|
||||||
|
|
||||||
static const int MAX_UPDATE = 64;
|
|
||||||
dtCompressedTileRef m_update[MAX_UPDATE];
|
|
||||||
int m_nupdate;
|
|
||||||
};
|
|
||||||
|
|
||||||
dtTileCache* dtAllocTileCache();
|
|
||||||
void dtFreeTileCache(dtTileCache* tc);
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,156 +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 DETOURTILECACHEBUILDER_H
|
|
||||||
#define DETOURTILECACHEBUILDER_H
|
|
||||||
|
|
||||||
#include "DetourAlloc.h"
|
|
||||||
#include "DetourStatus.h"
|
|
||||||
|
|
||||||
static const int DT_TILECACHE_MAGIC = 'D'<<24 | 'T'<<16 | 'L'<<8 | 'R'; ///< 'DTLR';
|
|
||||||
static const int DT_TILECACHE_VERSION = 1;
|
|
||||||
|
|
||||||
static const unsigned char DT_TILECACHE_NULL_AREA = 0;
|
|
||||||
static const unsigned char DT_TILECACHE_WALKABLE_AREA = 63;
|
|
||||||
static const unsigned short DT_TILECACHE_NULL_IDX = 0xffff;
|
|
||||||
|
|
||||||
struct dtTileCacheLayerHeader
|
|
||||||
{
|
|
||||||
int magic; ///< Data magic
|
|
||||||
int version; ///< Data version
|
|
||||||
int tx,ty,tlayer;
|
|
||||||
float bmin[3], bmax[3];
|
|
||||||
unsigned short hmin, hmax; ///< Height min/max range
|
|
||||||
unsigned char width, height; ///< Dimension of the layer.
|
|
||||||
unsigned char minx, maxx, miny, maxy; ///< Usable sub-region.
|
|
||||||
};
|
|
||||||
|
|
||||||
struct dtTileCacheLayer
|
|
||||||
{
|
|
||||||
dtTileCacheLayerHeader* header;
|
|
||||||
unsigned char regCount; ///< Region count.
|
|
||||||
unsigned char* heights;
|
|
||||||
unsigned char* areas;
|
|
||||||
unsigned char* cons;
|
|
||||||
unsigned char* regs;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct dtTileCacheContour
|
|
||||||
{
|
|
||||||
int nverts;
|
|
||||||
unsigned char* verts;
|
|
||||||
unsigned char reg;
|
|
||||||
unsigned char area;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct dtTileCacheContourSet
|
|
||||||
{
|
|
||||||
int nconts;
|
|
||||||
dtTileCacheContour* conts;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct dtTileCachePolyMesh
|
|
||||||
{
|
|
||||||
int nvp;
|
|
||||||
int nverts; ///< Number of vertices.
|
|
||||||
int npolys; ///< Number of polygons.
|
|
||||||
unsigned short* verts; ///< Vertices of the mesh, 3 elements per vertex.
|
|
||||||
unsigned short* polys; ///< Polygons of the mesh, nvp*2 elements per polygon.
|
|
||||||
unsigned short* flags; ///< Per polygon flags.
|
|
||||||
unsigned char* areas; ///< Area ID of polygons.
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct dtTileCacheAlloc
|
|
||||||
{
|
|
||||||
virtual ~dtTileCacheAlloc() {}
|
|
||||||
|
|
||||||
virtual void reset() {}
|
|
||||||
|
|
||||||
virtual void* alloc(const size_t size)
|
|
||||||
{
|
|
||||||
return dtAlloc(size, DT_ALLOC_TEMP);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void free(void* ptr)
|
|
||||||
{
|
|
||||||
dtFree(ptr);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct dtTileCacheCompressor
|
|
||||||
{
|
|
||||||
virtual ~dtTileCacheCompressor() { }
|
|
||||||
|
|
||||||
virtual int maxCompressedSize(const int bufferSize) = 0;
|
|
||||||
virtual dtStatus compress(const unsigned char* buffer, const int bufferSize,
|
|
||||||
unsigned char* compressed, const int maxCompressedSize, int* compressedSize) = 0;
|
|
||||||
virtual dtStatus decompress(const unsigned char* compressed, const int compressedSize,
|
|
||||||
unsigned char* buffer, const int maxBufferSize, int* bufferSize) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
dtStatus dtBuildTileCacheLayer(dtTileCacheCompressor* comp,
|
|
||||||
dtTileCacheLayerHeader* header,
|
|
||||||
const unsigned char* heights,
|
|
||||||
const unsigned char* areas,
|
|
||||||
const unsigned char* cons,
|
|
||||||
unsigned char** outData, int* outDataSize);
|
|
||||||
|
|
||||||
void dtFreeTileCacheLayer(dtTileCacheAlloc* alloc, dtTileCacheLayer* layer);
|
|
||||||
|
|
||||||
dtStatus dtDecompressTileCacheLayer(dtTileCacheAlloc* alloc, dtTileCacheCompressor* comp,
|
|
||||||
unsigned char* compressed, const int compressedSize,
|
|
||||||
dtTileCacheLayer** layerOut);
|
|
||||||
|
|
||||||
dtTileCacheContourSet* dtAllocTileCacheContourSet(dtTileCacheAlloc* alloc);
|
|
||||||
void dtFreeTileCacheContourSet(dtTileCacheAlloc* alloc, dtTileCacheContourSet* cset);
|
|
||||||
|
|
||||||
dtTileCachePolyMesh* dtAllocTileCachePolyMesh(dtTileCacheAlloc* alloc);
|
|
||||||
void dtFreeTileCachePolyMesh(dtTileCacheAlloc* alloc, dtTileCachePolyMesh* lmesh);
|
|
||||||
|
|
||||||
dtStatus dtMarkCylinderArea(dtTileCacheLayer& layer, const float* orig, const float cs, const float ch,
|
|
||||||
const float* pos, const float radius, const float height, const unsigned char areaId);
|
|
||||||
|
|
||||||
dtStatus dtMarkBoxArea(dtTileCacheLayer& layer, const float* orig, const float cs, const float ch,
|
|
||||||
const float* bmin, const float* bmax, const unsigned char areaId);
|
|
||||||
|
|
||||||
dtStatus dtMarkBoxArea(dtTileCacheLayer& layer, const float* orig, const float cs, const float ch,
|
|
||||||
const float* center, const float* halfExtents, const float* rotAux, const unsigned char areaId);
|
|
||||||
|
|
||||||
dtStatus dtBuildTileCacheRegions(dtTileCacheAlloc* alloc,
|
|
||||||
dtTileCacheLayer& layer,
|
|
||||||
const int walkableClimb);
|
|
||||||
|
|
||||||
dtStatus dtBuildTileCacheContours(dtTileCacheAlloc* alloc,
|
|
||||||
dtTileCacheLayer& layer,
|
|
||||||
const int walkableClimb, const float maxError,
|
|
||||||
dtTileCacheContourSet& lcset);
|
|
||||||
|
|
||||||
dtStatus dtBuildTileCachePolyMesh(dtTileCacheAlloc* alloc,
|
|
||||||
dtTileCacheContourSet& lcset,
|
|
||||||
dtTileCachePolyMesh& mesh);
|
|
||||||
|
|
||||||
/// Swaps the endianess of the compressed tile data's header (#dtTileCacheLayerHeader).
|
|
||||||
/// Tile layer data does not need endian swapping as it consits only of bytes.
|
|
||||||
/// @param[in,out] data The tile data array.
|
|
||||||
/// @param[in] dataSize The size of the data array.
|
|
||||||
bool dtTileCacheHeaderSwapEndian(unsigned char* data, const int dataSize);
|
|
||||||
|
|
||||||
|
|
||||||
#endif // DETOURTILECACHEBUILDER_H
|
|
@ -1,820 +0,0 @@
|
|||||||
#include "DetourTileCache.h"
|
|
||||||
#include "DetourTileCacheBuilder.h"
|
|
||||||
#include "DetourNavMeshBuilder.h"
|
|
||||||
#include "DetourNavMesh.h"
|
|
||||||
#include "DetourCommon.h"
|
|
||||||
#include "DetourMath.h"
|
|
||||||
#include "DetourAlloc.h"
|
|
||||||
#include "DetourAssert.h"
|
|
||||||
#include <string.h>
|
|
||||||
#include <new>
|
|
||||||
|
|
||||||
dtTileCache* dtAllocTileCache()
|
|
||||||
{
|
|
||||||
void* mem = dtAlloc(sizeof(dtTileCache), DT_ALLOC_PERM);
|
|
||||||
if (!mem) return 0;
|
|
||||||
return new(mem) dtTileCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtFreeTileCache(dtTileCache* tc)
|
|
||||||
{
|
|
||||||
if (!tc) return;
|
|
||||||
tc->~dtTileCache();
|
|
||||||
dtFree(tc);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool contains(const dtCompressedTileRef* a, const int n, const dtCompressedTileRef v)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < n; ++i)
|
|
||||||
if (a[i] == v)
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct NavMeshTileBuildContext
|
|
||||||
{
|
|
||||||
inline NavMeshTileBuildContext(struct dtTileCacheAlloc* a) : layer(0), lcset(0), lmesh(0), alloc(a) {}
|
|
||||||
inline ~NavMeshTileBuildContext() { purge(); }
|
|
||||||
void purge()
|
|
||||||
{
|
|
||||||
dtFreeTileCacheLayer(alloc, layer);
|
|
||||||
layer = 0;
|
|
||||||
dtFreeTileCacheContourSet(alloc, lcset);
|
|
||||||
lcset = 0;
|
|
||||||
dtFreeTileCachePolyMesh(alloc, lmesh);
|
|
||||||
lmesh = 0;
|
|
||||||
}
|
|
||||||
struct dtTileCacheLayer* layer;
|
|
||||||
struct dtTileCacheContourSet* lcset;
|
|
||||||
struct dtTileCachePolyMesh* lmesh;
|
|
||||||
struct dtTileCacheAlloc* alloc;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
dtTileCache::dtTileCache() :
|
|
||||||
m_tileLutSize(0),
|
|
||||||
m_tileLutMask(0),
|
|
||||||
m_posLookup(0),
|
|
||||||
m_nextFreeTile(0),
|
|
||||||
m_tiles(0),
|
|
||||||
m_saltBits(0),
|
|
||||||
m_tileBits(0),
|
|
||||||
m_talloc(0),
|
|
||||||
m_tcomp(0),
|
|
||||||
m_tmproc(0),
|
|
||||||
m_obstacles(0),
|
|
||||||
m_nextFreeObstacle(0),
|
|
||||||
m_nreqs(0),
|
|
||||||
m_nupdate(0)
|
|
||||||
{
|
|
||||||
memset(&m_params, 0, sizeof(m_params));
|
|
||||||
memset(m_reqs, 0, sizeof(ObstacleRequest) * MAX_REQUESTS);
|
|
||||||
}
|
|
||||||
|
|
||||||
dtTileCache::~dtTileCache()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < m_params.maxTiles; ++i)
|
|
||||||
{
|
|
||||||
if (m_tiles[i].flags & DT_COMPRESSEDTILE_FREE_DATA)
|
|
||||||
{
|
|
||||||
dtFree(m_tiles[i].data);
|
|
||||||
m_tiles[i].data = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dtFree(m_obstacles);
|
|
||||||
m_obstacles = 0;
|
|
||||||
dtFree(m_posLookup);
|
|
||||||
m_posLookup = 0;
|
|
||||||
dtFree(m_tiles);
|
|
||||||
m_tiles = 0;
|
|
||||||
m_nreqs = 0;
|
|
||||||
m_nupdate = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const dtCompressedTile* dtTileCache::getTileByRef(dtCompressedTileRef ref) const
|
|
||||||
{
|
|
||||||
if (!ref)
|
|
||||||
return 0;
|
|
||||||
unsigned int tileIndex = decodeTileIdTile(ref);
|
|
||||||
unsigned int tileSalt = decodeTileIdSalt(ref);
|
|
||||||
if ((int)tileIndex >= m_params.maxTiles)
|
|
||||||
return 0;
|
|
||||||
const dtCompressedTile* tile = &m_tiles[tileIndex];
|
|
||||||
if (tile->salt != tileSalt)
|
|
||||||
return 0;
|
|
||||||
return tile;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
dtStatus dtTileCache::init(const dtTileCacheParams* params,
|
|
||||||
dtTileCacheAlloc* talloc,
|
|
||||||
dtTileCacheCompressor* tcomp,
|
|
||||||
dtTileCacheMeshProcess* tmproc)
|
|
||||||
{
|
|
||||||
m_talloc = talloc;
|
|
||||||
m_tcomp = tcomp;
|
|
||||||
m_tmproc = tmproc;
|
|
||||||
m_nreqs = 0;
|
|
||||||
memcpy(&m_params, params, sizeof(m_params));
|
|
||||||
|
|
||||||
// Alloc space for obstacles.
|
|
||||||
m_obstacles = (dtTileCacheObstacle*)dtAlloc(sizeof(dtTileCacheObstacle)*m_params.maxObstacles, DT_ALLOC_PERM);
|
|
||||||
if (!m_obstacles)
|
|
||||||
return DT_FAILURE | DT_OUT_OF_MEMORY;
|
|
||||||
memset(m_obstacles, 0, sizeof(dtTileCacheObstacle)*m_params.maxObstacles);
|
|
||||||
m_nextFreeObstacle = 0;
|
|
||||||
for (int i = m_params.maxObstacles-1; i >= 0; --i)
|
|
||||||
{
|
|
||||||
m_obstacles[i].salt = 1;
|
|
||||||
m_obstacles[i].next = m_nextFreeObstacle;
|
|
||||||
m_nextFreeObstacle = &m_obstacles[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init tiles
|
|
||||||
m_tileLutSize = dtNextPow2(m_params.maxTiles/4);
|
|
||||||
if (!m_tileLutSize) m_tileLutSize = 1;
|
|
||||||
m_tileLutMask = m_tileLutSize-1;
|
|
||||||
|
|
||||||
m_tiles = (dtCompressedTile*)dtAlloc(sizeof(dtCompressedTile)*m_params.maxTiles, DT_ALLOC_PERM);
|
|
||||||
if (!m_tiles)
|
|
||||||
return DT_FAILURE | DT_OUT_OF_MEMORY;
|
|
||||||
m_posLookup = (dtCompressedTile**)dtAlloc(sizeof(dtCompressedTile*)*m_tileLutSize, DT_ALLOC_PERM);
|
|
||||||
if (!m_posLookup)
|
|
||||||
return DT_FAILURE | DT_OUT_OF_MEMORY;
|
|
||||||
memset(m_tiles, 0, sizeof(dtCompressedTile)*m_params.maxTiles);
|
|
||||||
memset(m_posLookup, 0, sizeof(dtCompressedTile*)*m_tileLutSize);
|
|
||||||
m_nextFreeTile = 0;
|
|
||||||
for (int i = m_params.maxTiles-1; i >= 0; --i)
|
|
||||||
{
|
|
||||||
m_tiles[i].salt = 1;
|
|
||||||
m_tiles[i].next = m_nextFreeTile;
|
|
||||||
m_nextFreeTile = &m_tiles[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init ID generator values.
|
|
||||||
m_tileBits = dtIlog2(dtNextPow2((unsigned int)m_params.maxTiles));
|
|
||||||
// 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);
|
|
||||||
if (m_saltBits < 10)
|
|
||||||
return DT_FAILURE | DT_INVALID_PARAM;
|
|
||||||
|
|
||||||
return DT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dtTileCache::getTilesAt(const int tx, const int ty, dtCompressedTileRef* tiles, const int maxTiles) const
|
|
||||||
{
|
|
||||||
int n = 0;
|
|
||||||
|
|
||||||
// Find tile based on hash.
|
|
||||||
int h = computeTileHash(tx,ty,m_tileLutMask);
|
|
||||||
dtCompressedTile* tile = m_posLookup[h];
|
|
||||||
while (tile)
|
|
||||||
{
|
|
||||||
if (tile->header &&
|
|
||||||
tile->header->tx == tx &&
|
|
||||||
tile->header->ty == ty)
|
|
||||||
{
|
|
||||||
if (n < maxTiles)
|
|
||||||
tiles[n++] = getTileRef(tile);
|
|
||||||
}
|
|
||||||
tile = tile->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
dtCompressedTile* dtTileCache::getTileAt(const int tx, const int ty, const int tlayer)
|
|
||||||
{
|
|
||||||
// Find tile based on hash.
|
|
||||||
int h = computeTileHash(tx,ty,m_tileLutMask);
|
|
||||||
dtCompressedTile* tile = m_posLookup[h];
|
|
||||||
while (tile)
|
|
||||||
{
|
|
||||||
if (tile->header &&
|
|
||||||
tile->header->tx == tx &&
|
|
||||||
tile->header->ty == ty &&
|
|
||||||
tile->header->tlayer == tlayer)
|
|
||||||
{
|
|
||||||
return tile;
|
|
||||||
}
|
|
||||||
tile = tile->next;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
dtCompressedTileRef dtTileCache::getTileRef(const dtCompressedTile* tile) const
|
|
||||||
{
|
|
||||||
if (!tile) return 0;
|
|
||||||
const unsigned int it = (unsigned int)(tile - m_tiles);
|
|
||||||
return (dtCompressedTileRef)encodeTileId(tile->salt, it);
|
|
||||||
}
|
|
||||||
|
|
||||||
dtObstacleRef dtTileCache::getObstacleRef(const dtTileCacheObstacle* ob) const
|
|
||||||
{
|
|
||||||
if (!ob) return 0;
|
|
||||||
const unsigned int idx = (unsigned int)(ob - m_obstacles);
|
|
||||||
return encodeObstacleId(ob->salt, idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
const dtTileCacheObstacle* dtTileCache::getObstacleByRef(dtObstacleRef ref)
|
|
||||||
{
|
|
||||||
if (!ref)
|
|
||||||
return 0;
|
|
||||||
unsigned int idx = decodeObstacleIdObstacle(ref);
|
|
||||||
if ((int)idx >= m_params.maxObstacles)
|
|
||||||
return 0;
|
|
||||||
const dtTileCacheObstacle* ob = &m_obstacles[idx];
|
|
||||||
unsigned int salt = decodeObstacleIdSalt(ref);
|
|
||||||
if (ob->salt != salt)
|
|
||||||
return 0;
|
|
||||||
return ob;
|
|
||||||
}
|
|
||||||
|
|
||||||
dtStatus dtTileCache::addTile(unsigned char* data, const int dataSize, unsigned char flags, dtCompressedTileRef* result)
|
|
||||||
{
|
|
||||||
// Make sure the data is in right format.
|
|
||||||
dtTileCacheLayerHeader* header = (dtTileCacheLayerHeader*)data;
|
|
||||||
if (header->magic != DT_TILECACHE_MAGIC)
|
|
||||||
return DT_FAILURE | DT_WRONG_MAGIC;
|
|
||||||
if (header->version != DT_TILECACHE_VERSION)
|
|
||||||
return DT_FAILURE | DT_WRONG_VERSION;
|
|
||||||
|
|
||||||
// Make sure the location is free.
|
|
||||||
if (getTileAt(header->tx, header->ty, header->tlayer))
|
|
||||||
return DT_FAILURE;
|
|
||||||
|
|
||||||
// Allocate a tile.
|
|
||||||
dtCompressedTile* tile = 0;
|
|
||||||
if (m_nextFreeTile)
|
|
||||||
{
|
|
||||||
tile = m_nextFreeTile;
|
|
||||||
m_nextFreeTile = tile->next;
|
|
||||||
tile->next = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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->tx, header->ty, m_tileLutMask);
|
|
||||||
tile->next = m_posLookup[h];
|
|
||||||
m_posLookup[h] = tile;
|
|
||||||
|
|
||||||
// Init tile.
|
|
||||||
const int headerSize = dtAlign4(sizeof(dtTileCacheLayerHeader));
|
|
||||||
tile->header = (dtTileCacheLayerHeader*)data;
|
|
||||||
tile->data = data;
|
|
||||||
tile->dataSize = dataSize;
|
|
||||||
tile->compressed = tile->data + headerSize;
|
|
||||||
tile->compressedSize = tile->dataSize - headerSize;
|
|
||||||
tile->flags = flags;
|
|
||||||
|
|
||||||
if (result)
|
|
||||||
*result = getTileRef(tile);
|
|
||||||
|
|
||||||
return DT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
dtStatus dtTileCache::removeTile(dtCompressedTileRef ref, unsigned char** data, int* dataSize)
|
|
||||||
{
|
|
||||||
if (!ref)
|
|
||||||
return DT_FAILURE | DT_INVALID_PARAM;
|
|
||||||
unsigned int tileIndex = decodeTileIdTile(ref);
|
|
||||||
unsigned int tileSalt = decodeTileIdSalt(ref);
|
|
||||||
if ((int)tileIndex >= m_params.maxTiles)
|
|
||||||
return DT_FAILURE | DT_INVALID_PARAM;
|
|
||||||
dtCompressedTile* tile = &m_tiles[tileIndex];
|
|
||||||
if (tile->salt != tileSalt)
|
|
||||||
return DT_FAILURE | DT_INVALID_PARAM;
|
|
||||||
|
|
||||||
// Remove tile from hash lookup.
|
|
||||||
const int h = computeTileHash(tile->header->tx,tile->header->ty,m_tileLutMask);
|
|
||||||
dtCompressedTile* prev = 0;
|
|
||||||
dtCompressedTile* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset tile.
|
|
||||||
if (tile->flags & DT_COMPRESSEDTILE_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->data = 0;
|
|
||||||
tile->dataSize = 0;
|
|
||||||
tile->compressed = 0;
|
|
||||||
tile->compressedSize = 0;
|
|
||||||
tile->flags = 0;
|
|
||||||
|
|
||||||
// Update salt, salt should never be zero.
|
|
||||||
tile->salt = (tile->salt+1) & ((1<<m_saltBits)-1);
|
|
||||||
if (tile->salt == 0)
|
|
||||||
tile->salt++;
|
|
||||||
|
|
||||||
// Add to free list.
|
|
||||||
tile->next = m_nextFreeTile;
|
|
||||||
m_nextFreeTile = tile;
|
|
||||||
|
|
||||||
return DT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
dtStatus dtTileCache::addObstacle(const float* pos, const float radius, const float height, dtObstacleRef* result)
|
|
||||||
{
|
|
||||||
if (m_nreqs >= MAX_REQUESTS)
|
|
||||||
return DT_FAILURE | DT_BUFFER_TOO_SMALL;
|
|
||||||
|
|
||||||
dtTileCacheObstacle* ob = 0;
|
|
||||||
if (m_nextFreeObstacle)
|
|
||||||
{
|
|
||||||
ob = m_nextFreeObstacle;
|
|
||||||
m_nextFreeObstacle = ob->next;
|
|
||||||
ob->next = 0;
|
|
||||||
}
|
|
||||||
if (!ob)
|
|
||||||
return DT_FAILURE | DT_OUT_OF_MEMORY;
|
|
||||||
|
|
||||||
unsigned short salt = ob->salt;
|
|
||||||
memset(ob, 0, sizeof(dtTileCacheObstacle));
|
|
||||||
ob->salt = salt;
|
|
||||||
ob->state = DT_OBSTACLE_PROCESSING;
|
|
||||||
ob->type = DT_OBSTACLE_CYLINDER;
|
|
||||||
dtVcopy(ob->cylinder.pos, pos);
|
|
||||||
ob->cylinder.radius = radius;
|
|
||||||
ob->cylinder.height = height;
|
|
||||||
|
|
||||||
ObstacleRequest* req = &m_reqs[m_nreqs++];
|
|
||||||
memset(req, 0, sizeof(ObstacleRequest));
|
|
||||||
req->action = REQUEST_ADD;
|
|
||||||
req->ref = getObstacleRef(ob);
|
|
||||||
|
|
||||||
if (result)
|
|
||||||
*result = req->ref;
|
|
||||||
|
|
||||||
return DT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
dtStatus dtTileCache::addBoxObstacle(const float* bmin, const float* bmax, dtObstacleRef* result)
|
|
||||||
{
|
|
||||||
if (m_nreqs >= MAX_REQUESTS)
|
|
||||||
return DT_FAILURE | DT_BUFFER_TOO_SMALL;
|
|
||||||
|
|
||||||
dtTileCacheObstacle* ob = 0;
|
|
||||||
if (m_nextFreeObstacle)
|
|
||||||
{
|
|
||||||
ob = m_nextFreeObstacle;
|
|
||||||
m_nextFreeObstacle = ob->next;
|
|
||||||
ob->next = 0;
|
|
||||||
}
|
|
||||||
if (!ob)
|
|
||||||
return DT_FAILURE | DT_OUT_OF_MEMORY;
|
|
||||||
|
|
||||||
unsigned short salt = ob->salt;
|
|
||||||
memset(ob, 0, sizeof(dtTileCacheObstacle));
|
|
||||||
ob->salt = salt;
|
|
||||||
ob->state = DT_OBSTACLE_PROCESSING;
|
|
||||||
ob->type = DT_OBSTACLE_BOX;
|
|
||||||
dtVcopy(ob->box.bmin, bmin);
|
|
||||||
dtVcopy(ob->box.bmax, bmax);
|
|
||||||
|
|
||||||
ObstacleRequest* req = &m_reqs[m_nreqs++];
|
|
||||||
memset(req, 0, sizeof(ObstacleRequest));
|
|
||||||
req->action = REQUEST_ADD;
|
|
||||||
req->ref = getObstacleRef(ob);
|
|
||||||
|
|
||||||
if (result)
|
|
||||||
*result = req->ref;
|
|
||||||
|
|
||||||
return DT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
dtStatus dtTileCache::addBoxObstacle(const float* center, const float* halfExtents, const float yRadians, dtObstacleRef* result)
|
|
||||||
{
|
|
||||||
if (m_nreqs >= MAX_REQUESTS)
|
|
||||||
return DT_FAILURE | DT_BUFFER_TOO_SMALL;
|
|
||||||
|
|
||||||
dtTileCacheObstacle* ob = 0;
|
|
||||||
if (m_nextFreeObstacle)
|
|
||||||
{
|
|
||||||
ob = m_nextFreeObstacle;
|
|
||||||
m_nextFreeObstacle = ob->next;
|
|
||||||
ob->next = 0;
|
|
||||||
}
|
|
||||||
if (!ob)
|
|
||||||
return DT_FAILURE | DT_OUT_OF_MEMORY;
|
|
||||||
|
|
||||||
unsigned short salt = ob->salt;
|
|
||||||
memset(ob, 0, sizeof(dtTileCacheObstacle));
|
|
||||||
ob->salt = salt;
|
|
||||||
ob->state = DT_OBSTACLE_PROCESSING;
|
|
||||||
ob->type = DT_OBSTACLE_ORIENTED_BOX;
|
|
||||||
dtVcopy(ob->orientedBox.center, center);
|
|
||||||
dtVcopy(ob->orientedBox.halfExtents, halfExtents);
|
|
||||||
|
|
||||||
float coshalf= cosf(0.5f*yRadians);
|
|
||||||
float sinhalf = sinf(-0.5f*yRadians);
|
|
||||||
ob->orientedBox.rotAux[0] = coshalf*sinhalf;
|
|
||||||
ob->orientedBox.rotAux[1] = coshalf*coshalf - 0.5f;
|
|
||||||
|
|
||||||
ObstacleRequest* req = &m_reqs[m_nreqs++];
|
|
||||||
memset(req, 0, sizeof(ObstacleRequest));
|
|
||||||
req->action = REQUEST_ADD;
|
|
||||||
req->ref = getObstacleRef(ob);
|
|
||||||
|
|
||||||
if (result)
|
|
||||||
*result = req->ref;
|
|
||||||
|
|
||||||
return DT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
dtStatus dtTileCache::removeObstacle(const dtObstacleRef ref)
|
|
||||||
{
|
|
||||||
if (!ref)
|
|
||||||
return DT_SUCCESS;
|
|
||||||
if (m_nreqs >= MAX_REQUESTS)
|
|
||||||
return DT_FAILURE | DT_BUFFER_TOO_SMALL;
|
|
||||||
|
|
||||||
ObstacleRequest* req = &m_reqs[m_nreqs++];
|
|
||||||
memset(req, 0, sizeof(ObstacleRequest));
|
|
||||||
req->action = REQUEST_REMOVE;
|
|
||||||
req->ref = ref;
|
|
||||||
|
|
||||||
return DT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
dtStatus dtTileCache::queryTiles(const float* bmin, const float* bmax,
|
|
||||||
dtCompressedTileRef* results, int* resultCount, const int maxResults) const
|
|
||||||
{
|
|
||||||
const int MAX_TILES = 32;
|
|
||||||
dtCompressedTileRef tiles[MAX_TILES];
|
|
||||||
|
|
||||||
int n = 0;
|
|
||||||
|
|
||||||
const float tw = m_params.width * m_params.cs;
|
|
||||||
const float th = m_params.height * m_params.cs;
|
|
||||||
const int tx0 = (int)dtMathFloorf((bmin[0]-m_params.orig[0]) / tw);
|
|
||||||
const int tx1 = (int)dtMathFloorf((bmax[0]-m_params.orig[0]) / tw);
|
|
||||||
const int ty0 = (int)dtMathFloorf((bmin[2]-m_params.orig[2]) / th);
|
|
||||||
const int ty1 = (int)dtMathFloorf((bmax[2]-m_params.orig[2]) / th);
|
|
||||||
|
|
||||||
for (int ty = ty0; ty <= ty1; ++ty)
|
|
||||||
{
|
|
||||||
for (int tx = tx0; tx <= tx1; ++tx)
|
|
||||||
{
|
|
||||||
const int ntiles = getTilesAt(tx,ty,tiles,MAX_TILES);
|
|
||||||
|
|
||||||
for (int i = 0; i < ntiles; ++i)
|
|
||||||
{
|
|
||||||
const dtCompressedTile* tile = &m_tiles[decodeTileIdTile(tiles[i])];
|
|
||||||
float tbmin[3], tbmax[3];
|
|
||||||
calcTightTileBounds(tile->header, tbmin, tbmax);
|
|
||||||
|
|
||||||
if (dtOverlapBounds(bmin,bmax, tbmin,tbmax))
|
|
||||||
{
|
|
||||||
if (n < maxResults)
|
|
||||||
results[n++] = tiles[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*resultCount = n;
|
|
||||||
|
|
||||||
return DT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
dtStatus dtTileCache::update(const float /*dt*/, dtNavMesh* navmesh,
|
|
||||||
bool* upToDate)
|
|
||||||
{
|
|
||||||
if (m_nupdate == 0)
|
|
||||||
{
|
|
||||||
// Process requests.
|
|
||||||
for (int i = 0; i < m_nreqs; ++i)
|
|
||||||
{
|
|
||||||
ObstacleRequest* req = &m_reqs[i];
|
|
||||||
|
|
||||||
unsigned int idx = decodeObstacleIdObstacle(req->ref);
|
|
||||||
if ((int)idx >= m_params.maxObstacles)
|
|
||||||
continue;
|
|
||||||
dtTileCacheObstacle* ob = &m_obstacles[idx];
|
|
||||||
unsigned int salt = decodeObstacleIdSalt(req->ref);
|
|
||||||
if (ob->salt != salt)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (req->action == REQUEST_ADD)
|
|
||||||
{
|
|
||||||
// Find touched tiles.
|
|
||||||
float bmin[3], bmax[3];
|
|
||||||
getObstacleBounds(ob, bmin, bmax);
|
|
||||||
|
|
||||||
int ntouched = 0;
|
|
||||||
queryTiles(bmin, bmax, ob->touched, &ntouched, DT_MAX_TOUCHED_TILES);
|
|
||||||
ob->ntouched = (unsigned char)ntouched;
|
|
||||||
// Add tiles to update list.
|
|
||||||
ob->npending = 0;
|
|
||||||
for (int j = 0; j < ob->ntouched; ++j)
|
|
||||||
{
|
|
||||||
if (m_nupdate < MAX_UPDATE)
|
|
||||||
{
|
|
||||||
if (!contains(m_update, m_nupdate, ob->touched[j]))
|
|
||||||
m_update[m_nupdate++] = ob->touched[j];
|
|
||||||
ob->pending[ob->npending++] = ob->touched[j];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (req->action == REQUEST_REMOVE)
|
|
||||||
{
|
|
||||||
// Prepare to remove obstacle.
|
|
||||||
ob->state = DT_OBSTACLE_REMOVING;
|
|
||||||
// Add tiles to update list.
|
|
||||||
ob->npending = 0;
|
|
||||||
for (int j = 0; j < ob->ntouched; ++j)
|
|
||||||
{
|
|
||||||
if (m_nupdate < MAX_UPDATE)
|
|
||||||
{
|
|
||||||
if (!contains(m_update, m_nupdate, ob->touched[j]))
|
|
||||||
m_update[m_nupdate++] = ob->touched[j];
|
|
||||||
ob->pending[ob->npending++] = ob->touched[j];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_nreqs = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
dtStatus status = DT_SUCCESS;
|
|
||||||
// Process updates
|
|
||||||
if (m_nupdate)
|
|
||||||
{
|
|
||||||
// Build mesh
|
|
||||||
const dtCompressedTileRef ref = m_update[0];
|
|
||||||
status = buildNavMeshTile(ref, navmesh);
|
|
||||||
m_nupdate--;
|
|
||||||
if (m_nupdate > 0)
|
|
||||||
memmove(m_update, m_update+1, m_nupdate*sizeof(dtCompressedTileRef));
|
|
||||||
|
|
||||||
// Update obstacle states.
|
|
||||||
for (int i = 0; i < m_params.maxObstacles; ++i)
|
|
||||||
{
|
|
||||||
dtTileCacheObstacle* ob = &m_obstacles[i];
|
|
||||||
if (ob->state == DT_OBSTACLE_PROCESSING || ob->state == DT_OBSTACLE_REMOVING)
|
|
||||||
{
|
|
||||||
// Remove handled tile from pending list.
|
|
||||||
for (int j = 0; j < (int)ob->npending; j++)
|
|
||||||
{
|
|
||||||
if (ob->pending[j] == ref)
|
|
||||||
{
|
|
||||||
ob->pending[j] = ob->pending[(int)ob->npending-1];
|
|
||||||
ob->npending--;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If all pending tiles processed, change state.
|
|
||||||
if (ob->npending == 0)
|
|
||||||
{
|
|
||||||
if (ob->state == DT_OBSTACLE_PROCESSING)
|
|
||||||
{
|
|
||||||
ob->state = DT_OBSTACLE_PROCESSED;
|
|
||||||
}
|
|
||||||
else if (ob->state == DT_OBSTACLE_REMOVING)
|
|
||||||
{
|
|
||||||
ob->state = DT_OBSTACLE_EMPTY;
|
|
||||||
// Update salt, salt should never be zero.
|
|
||||||
ob->salt = (ob->salt+1) & ((1<<16)-1);
|
|
||||||
if (ob->salt == 0)
|
|
||||||
ob->salt++;
|
|
||||||
// Return obstacle to free list.
|
|
||||||
ob->next = m_nextFreeObstacle;
|
|
||||||
m_nextFreeObstacle = ob;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (upToDate)
|
|
||||||
*upToDate = m_nupdate == 0 && m_nreqs == 0;
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
dtStatus dtTileCache::buildNavMeshTilesAt(const int tx, const int ty, dtNavMesh* navmesh)
|
|
||||||
{
|
|
||||||
const int MAX_TILES = 32;
|
|
||||||
dtCompressedTileRef tiles[MAX_TILES];
|
|
||||||
const int ntiles = getTilesAt(tx,ty,tiles,MAX_TILES);
|
|
||||||
|
|
||||||
for (int i = 0; i < ntiles; ++i)
|
|
||||||
{
|
|
||||||
dtStatus status = buildNavMeshTile(tiles[i], navmesh);
|
|
||||||
if (dtStatusFailed(status))
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
return DT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
dtStatus dtTileCache::buildNavMeshTile(const dtCompressedTileRef ref, dtNavMesh* navmesh)
|
|
||||||
{
|
|
||||||
dtAssert(m_talloc);
|
|
||||||
dtAssert(m_tcomp);
|
|
||||||
|
|
||||||
unsigned int idx = decodeTileIdTile(ref);
|
|
||||||
if (idx > (unsigned int)m_params.maxTiles)
|
|
||||||
return DT_FAILURE | DT_INVALID_PARAM;
|
|
||||||
const dtCompressedTile* tile = &m_tiles[idx];
|
|
||||||
unsigned int salt = decodeTileIdSalt(ref);
|
|
||||||
if (tile->salt != salt)
|
|
||||||
return DT_FAILURE | DT_INVALID_PARAM;
|
|
||||||
|
|
||||||
m_talloc->reset();
|
|
||||||
|
|
||||||
NavMeshTileBuildContext bc(m_talloc);
|
|
||||||
const int walkableClimbVx = (int)(m_params.walkableClimb / m_params.ch);
|
|
||||||
dtStatus status;
|
|
||||||
|
|
||||||
// Decompress tile layer data.
|
|
||||||
status = dtDecompressTileCacheLayer(m_talloc, m_tcomp, tile->data, tile->dataSize, &bc.layer);
|
|
||||||
if (dtStatusFailed(status))
|
|
||||||
return status;
|
|
||||||
|
|
||||||
// Rasterize obstacles.
|
|
||||||
for (int i = 0; i < m_params.maxObstacles; ++i)
|
|
||||||
{
|
|
||||||
const dtTileCacheObstacle* ob = &m_obstacles[i];
|
|
||||||
if (ob->state == DT_OBSTACLE_EMPTY || ob->state == DT_OBSTACLE_REMOVING)
|
|
||||||
continue;
|
|
||||||
if (contains(ob->touched, ob->ntouched, ref))
|
|
||||||
{
|
|
||||||
if (ob->type == DT_OBSTACLE_CYLINDER)
|
|
||||||
{
|
|
||||||
dtMarkCylinderArea(*bc.layer, tile->header->bmin, m_params.cs, m_params.ch,
|
|
||||||
ob->cylinder.pos, ob->cylinder.radius, ob->cylinder.height, 0);
|
|
||||||
}
|
|
||||||
else if (ob->type == DT_OBSTACLE_BOX)
|
|
||||||
{
|
|
||||||
dtMarkBoxArea(*bc.layer, tile->header->bmin, m_params.cs, m_params.ch,
|
|
||||||
ob->box.bmin, ob->box.bmax, 0);
|
|
||||||
}
|
|
||||||
else if (ob->type == DT_OBSTACLE_ORIENTED_BOX)
|
|
||||||
{
|
|
||||||
dtMarkBoxArea(*bc.layer, tile->header->bmin, m_params.cs, m_params.ch,
|
|
||||||
ob->orientedBox.center, ob->orientedBox.halfExtents, ob->orientedBox.rotAux, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build navmesh
|
|
||||||
status = dtBuildTileCacheRegions(m_talloc, *bc.layer, walkableClimbVx);
|
|
||||||
if (dtStatusFailed(status))
|
|
||||||
return status;
|
|
||||||
|
|
||||||
bc.lcset = dtAllocTileCacheContourSet(m_talloc);
|
|
||||||
if (!bc.lcset)
|
|
||||||
return DT_FAILURE | DT_OUT_OF_MEMORY;
|
|
||||||
status = dtBuildTileCacheContours(m_talloc, *bc.layer, walkableClimbVx,
|
|
||||||
m_params.maxSimplificationError, *bc.lcset);
|
|
||||||
if (dtStatusFailed(status))
|
|
||||||
return status;
|
|
||||||
|
|
||||||
bc.lmesh = dtAllocTileCachePolyMesh(m_talloc);
|
|
||||||
if (!bc.lmesh)
|
|
||||||
return DT_FAILURE | DT_OUT_OF_MEMORY;
|
|
||||||
status = dtBuildTileCachePolyMesh(m_talloc, *bc.lcset, *bc.lmesh);
|
|
||||||
if (dtStatusFailed(status))
|
|
||||||
return status;
|
|
||||||
|
|
||||||
// Early out if the mesh tile is empty.
|
|
||||||
if (!bc.lmesh->npolys)
|
|
||||||
{
|
|
||||||
// Remove existing tile.
|
|
||||||
navmesh->removeTile(navmesh->getTileRefAt(tile->header->tx,tile->header->ty,tile->header->tlayer),0,0);
|
|
||||||
return DT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
dtNavMeshCreateParams params;
|
|
||||||
memset(¶ms, 0, sizeof(params));
|
|
||||||
params.verts = bc.lmesh->verts;
|
|
||||||
params.vertCount = bc.lmesh->nverts;
|
|
||||||
params.polys = bc.lmesh->polys;
|
|
||||||
params.polyAreas = bc.lmesh->areas;
|
|
||||||
params.polyFlags = bc.lmesh->flags;
|
|
||||||
params.polyCount = bc.lmesh->npolys;
|
|
||||||
params.nvp = DT_VERTS_PER_POLYGON;
|
|
||||||
params.walkableHeight = m_params.walkableHeight;
|
|
||||||
params.walkableRadius = m_params.walkableRadius;
|
|
||||||
params.walkableClimb = m_params.walkableClimb;
|
|
||||||
params.tileX = tile->header->tx;
|
|
||||||
params.tileY = tile->header->ty;
|
|
||||||
params.tileLayer = tile->header->tlayer;
|
|
||||||
params.cs = m_params.cs;
|
|
||||||
params.ch = m_params.ch;
|
|
||||||
params.buildBvTree = false;
|
|
||||||
dtVcopy(params.bmin, tile->header->bmin);
|
|
||||||
dtVcopy(params.bmax, tile->header->bmax);
|
|
||||||
|
|
||||||
if (m_tmproc)
|
|
||||||
{
|
|
||||||
m_tmproc->process(¶ms, bc.lmesh->areas, bc.lmesh->flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned char* navData = 0;
|
|
||||||
int navDataSize = 0;
|
|
||||||
if (!dtCreateNavMeshData(¶ms, &navData, &navDataSize))
|
|
||||||
return DT_FAILURE;
|
|
||||||
|
|
||||||
// Remove existing tile.
|
|
||||||
navmesh->removeTile(navmesh->getTileRefAt(tile->header->tx,tile->header->ty,tile->header->tlayer),0,0);
|
|
||||||
|
|
||||||
// Add new tile, or leave the location empty.
|
|
||||||
if (navData)
|
|
||||||
{
|
|
||||||
// Let the navmesh own the data.
|
|
||||||
status = navmesh->addTile(navData,navDataSize,DT_TILE_FREE_DATA,0,0);
|
|
||||||
if (dtStatusFailed(status))
|
|
||||||
{
|
|
||||||
dtFree(navData);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return DT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtTileCache::calcTightTileBounds(const dtTileCacheLayerHeader* header, float* bmin, float* bmax) const
|
|
||||||
{
|
|
||||||
const float cs = m_params.cs;
|
|
||||||
bmin[0] = header->bmin[0] + header->minx*cs;
|
|
||||||
bmin[1] = header->bmin[1];
|
|
||||||
bmin[2] = header->bmin[2] + header->miny*cs;
|
|
||||||
bmax[0] = header->bmin[0] + (header->maxx+1)*cs;
|
|
||||||
bmax[1] = header->bmax[1];
|
|
||||||
bmax[2] = header->bmin[2] + (header->maxy+1)*cs;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dtTileCache::getObstacleBounds(const struct dtTileCacheObstacle* ob, float* bmin, float* bmax) const
|
|
||||||
{
|
|
||||||
if (ob->type == DT_OBSTACLE_CYLINDER)
|
|
||||||
{
|
|
||||||
const dtObstacleCylinder &cl = ob->cylinder;
|
|
||||||
|
|
||||||
bmin[0] = cl.pos[0] - cl.radius;
|
|
||||||
bmin[1] = cl.pos[1];
|
|
||||||
bmin[2] = cl.pos[2] - cl.radius;
|
|
||||||
bmax[0] = cl.pos[0] + cl.radius;
|
|
||||||
bmax[1] = cl.pos[1] + cl.height;
|
|
||||||
bmax[2] = cl.pos[2] + cl.radius;
|
|
||||||
}
|
|
||||||
else if (ob->type == DT_OBSTACLE_BOX)
|
|
||||||
{
|
|
||||||
dtVcopy(bmin, ob->box.bmin);
|
|
||||||
dtVcopy(bmax, ob->box.bmax);
|
|
||||||
}
|
|
||||||
else if (ob->type == DT_OBSTACLE_ORIENTED_BOX)
|
|
||||||
{
|
|
||||||
const dtObstacleOrientedBox &orientedBox = ob->orientedBox;
|
|
||||||
|
|
||||||
float maxr = 1.41f*dtMax(orientedBox.halfExtents[0], orientedBox.halfExtents[2]);
|
|
||||||
bmin[0] = orientedBox.center[0] - maxr;
|
|
||||||
bmax[0] = orientedBox.center[0] + maxr;
|
|
||||||
bmin[1] = orientedBox.center[1] - orientedBox.halfExtents[1];
|
|
||||||
bmax[1] = orientedBox.center[1] + orientedBox.halfExtents[1];
|
|
||||||
bmin[2] = orientedBox.center[2] - maxr;
|
|
||||||
bmax[2] = orientedBox.center[2] + maxr;
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,18 +0,0 @@
|
|||||||
Copyright (c) 2009 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.
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
|||||||
file(GLOB SOURCES Source/*.cpp)
|
|
||||||
add_library(Recast ${SOURCES})
|
|
||||||
|
|
||||||
add_library(RecastNavigation::Recast ALIAS Recast)
|
|
||||||
set_target_properties(Recast PROPERTIES DEBUG_POSTFIX -d)
|
|
||||||
|
|
||||||
set(Recast_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Include")
|
|
||||||
|
|
||||||
target_include_directories(Recast PUBLIC
|
|
||||||
"$<BUILD_INTERFACE:${Recast_INCLUDE_DIR}>"
|
|
||||||
)
|
|
||||||
|
|
||||||
set_target_properties(Recast PROPERTIES
|
|
||||||
SOVERSION ${SOVERSION}
|
|
||||||
VERSION ${VERSION}
|
|
||||||
COMPILE_PDB_OUTPUT_DIRECTORY .
|
|
||||||
COMPILE_PDB_NAME "Recast-d"
|
|
||||||
)
|
|
||||||
|
|
||||||
install(TARGETS Recast
|
|
||||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
|
||||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
||||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
||||||
COMPONENT library
|
|
||||||
)
|
|
||||||
|
|
||||||
file(GLOB INCLUDES Include/*.h)
|
|
||||||
install(FILES ${INCLUDES} DESTINATION
|
|
||||||
${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation)
|
|
||||||
install(FILES "$<TARGET_FILE_DIR:Recast>/Recast-d.pdb" CONFIGURATIONS "Debug" DESTINATION "lib")
|
|
File diff suppressed because it is too large
Load Diff
@ -1,342 +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 RECASTALLOC_H
|
|
||||||
#define RECASTALLOC_H
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include <RecastAssert.h>
|
|
||||||
|
|
||||||
/// Provides hint values to the memory allocator on how long the
|
|
||||||
/// memory is expected to be used.
|
|
||||||
enum rcAllocHint
|
|
||||||
{
|
|
||||||
RC_ALLOC_PERM, ///< Memory will persist after a function call.
|
|
||||||
RC_ALLOC_TEMP ///< Memory used temporarily within a function.
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A memory allocation function.
|
|
||||||
// @param[in] size The size, in bytes of memory, to allocate.
|
|
||||||
// @param[in] rcAllocHint A hint to the allocator on how long the memory is expected to be in use.
|
|
||||||
// @return A pointer to the beginning of the allocated memory block, or null if the allocation failed.
|
|
||||||
/// @see rcAllocSetCustom
|
|
||||||
typedef void* (rcAllocFunc)(size_t size, rcAllocHint hint);
|
|
||||||
|
|
||||||
/// A memory deallocation function.
|
|
||||||
/// @param[in] ptr A pointer to a memory block previously allocated using #rcAllocFunc.
|
|
||||||
/// @see rcAllocSetCustom
|
|
||||||
typedef void (rcFreeFunc)(void* ptr);
|
|
||||||
|
|
||||||
/// Sets the base custom allocation functions to be used by Recast.
|
|
||||||
/// @param[in] allocFunc The memory allocation function to be used by #rcAlloc
|
|
||||||
/// @param[in] freeFunc The memory de-allocation function to be used by #rcFree
|
|
||||||
void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc);
|
|
||||||
|
|
||||||
/// Allocates a memory block.
|
|
||||||
/// @param[in] size The size, in bytes of memory, to allocate.
|
|
||||||
/// @param[in] hint A hint to the allocator on how long the memory is expected to be in use.
|
|
||||||
/// @return A pointer to the beginning of the allocated memory block, or null if the allocation failed.
|
|
||||||
/// @see rcFree
|
|
||||||
void* rcAlloc(size_t size, rcAllocHint hint);
|
|
||||||
|
|
||||||
/// Deallocates a memory block.
|
|
||||||
/// @param[in] ptr A pointer to a memory block previously allocated using #rcAlloc.
|
|
||||||
/// @see rcAlloc
|
|
||||||
void rcFree(void* ptr);
|
|
||||||
|
|
||||||
/// An implementation of operator new usable for placement new. The default one is part of STL (which we don't use).
|
|
||||||
/// rcNewTag is a dummy type used to differentiate our operator from the STL one, in case users import both Recast
|
|
||||||
/// and STL.
|
|
||||||
struct rcNewTag {};
|
|
||||||
inline void* operator new(size_t, const rcNewTag&, void* p) { return p; }
|
|
||||||
inline void operator delete(void*, const rcNewTag&, void*) {}
|
|
||||||
|
|
||||||
/// Signed to avoid warnnings when comparing to int loop indexes, and common error with comparing to zero.
|
|
||||||
/// MSVC2010 has a bug where ssize_t is unsigned (!!!).
|
|
||||||
typedef intptr_t rcSizeType;
|
|
||||||
#define RC_SIZE_MAX INTPTR_MAX
|
|
||||||
|
|
||||||
/// Macros to hint to the compiler about the likeliest branch. Please add a benchmark that demonstrates a performance
|
|
||||||
/// improvement before introducing use cases.
|
|
||||||
#if defined(__GNUC__) || defined(__clang__)
|
|
||||||
#define rcLikely(x) __builtin_expect((x), true)
|
|
||||||
#define rcUnlikely(x) __builtin_expect((x), false)
|
|
||||||
#else
|
|
||||||
#define rcLikely(x) (x)
|
|
||||||
#define rcUnlikely(x) (x)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// Variable-sized storage type. Mimics the interface of std::vector<T> with some notable differences:
|
|
||||||
/// * Uses rcAlloc()/rcFree() to handle storage.
|
|
||||||
/// * No support for a custom allocator.
|
|
||||||
/// * Uses signed size instead of size_t to avoid warnings in for loops: "for (int i = 0; i < foo.size(); i++)"
|
|
||||||
/// * Omits methods of limited utility: insert/erase, (bad performance), at (we don't use exceptions), operator=.
|
|
||||||
/// * assign() and the pre-sizing constructor follow C++11 semantics -- they don't construct a temporary if no value is provided.
|
|
||||||
/// * push_back() and resize() support adding values from the current vector. Range-based constructors and assign(begin, end) do not.
|
|
||||||
/// * No specialization for bool.
|
|
||||||
template <typename T, rcAllocHint H>
|
|
||||||
class rcVectorBase {
|
|
||||||
rcSizeType m_size;
|
|
||||||
rcSizeType m_cap;
|
|
||||||
T* m_data;
|
|
||||||
// Constructs a T at the give address with either the copy constructor or the default.
|
|
||||||
static void construct(T* p, const T& v) { ::new(rcNewTag(), (void*)p) T(v); }
|
|
||||||
static void construct(T* p) { ::new(rcNewTag(), (void*)p) T; }
|
|
||||||
static void construct_range(T* begin, T* end);
|
|
||||||
static void construct_range(T* begin, T* end, const T& value);
|
|
||||||
static void copy_range(T* dst, const T* begin, const T* end);
|
|
||||||
void destroy_range(rcSizeType begin, rcSizeType end);
|
|
||||||
// Creates an array of the given size, copies all of this vector's data into it, and returns it.
|
|
||||||
T* allocate_and_copy(rcSizeType size);
|
|
||||||
void resize_impl(rcSizeType size, const T* value);
|
|
||||||
public:
|
|
||||||
typedef rcSizeType size_type;
|
|
||||||
typedef T value_type;
|
|
||||||
|
|
||||||
rcVectorBase() : m_size(0), m_cap(0), m_data(0) {};
|
|
||||||
rcVectorBase(const rcVectorBase<T, H>& other) : m_size(0), m_cap(0), m_data(0) { assign(other.begin(), other.end()); }
|
|
||||||
explicit rcVectorBase(rcSizeType count) : m_size(0), m_cap(0), m_data(0) { resize(count); }
|
|
||||||
rcVectorBase(rcSizeType count, const T& value) : m_size(0), m_cap(0), m_data(0) { resize(count, value); }
|
|
||||||
rcVectorBase(const T* begin, const T* end) : m_size(0), m_cap(0), m_data(0) { assign(begin, end); }
|
|
||||||
~rcVectorBase() { destroy_range(0, m_size); rcFree(m_data); }
|
|
||||||
|
|
||||||
// Unlike in std::vector, we return a bool to indicate whether the alloc was successful.
|
|
||||||
bool reserve(rcSizeType size);
|
|
||||||
|
|
||||||
void assign(rcSizeType count, const T& value) { clear(); resize(count, value); }
|
|
||||||
void assign(const T* begin, const T* end);
|
|
||||||
|
|
||||||
void resize(rcSizeType size) { resize_impl(size, NULL); }
|
|
||||||
void resize(rcSizeType size, const T& value) { resize_impl(size, &value); }
|
|
||||||
// Not implemented as resize(0) because resize requires T to be default-constructible.
|
|
||||||
void clear() { destroy_range(0, m_size); m_size = 0; }
|
|
||||||
|
|
||||||
void push_back(const T& value);
|
|
||||||
void pop_back() { rcAssert(m_size > 0); back().~T(); m_size--; }
|
|
||||||
|
|
||||||
rcSizeType size() const { return m_size; }
|
|
||||||
rcSizeType capacity() const { return m_cap; }
|
|
||||||
bool empty() const { return size() == 0; }
|
|
||||||
|
|
||||||
const T& operator[](rcSizeType i) const { rcAssert(i >= 0 && i < m_size); return m_data[i]; }
|
|
||||||
T& operator[](rcSizeType i) { rcAssert(i >= 0 && i < m_size); return m_data[i]; }
|
|
||||||
|
|
||||||
const T& front() const { rcAssert(m_size); return m_data[0]; }
|
|
||||||
T& front() { rcAssert(m_size); return m_data[0]; }
|
|
||||||
const T& back() const { rcAssert(m_size); return m_data[m_size - 1]; };
|
|
||||||
T& back() { rcAssert(m_size); return m_data[m_size - 1]; };
|
|
||||||
const T* data() const { return m_data; }
|
|
||||||
T* data() { return m_data; }
|
|
||||||
|
|
||||||
T* begin() { return m_data; }
|
|
||||||
T* end() { return m_data + m_size; }
|
|
||||||
const T* begin() const { return m_data; }
|
|
||||||
const T* end() const { return m_data + m_size; }
|
|
||||||
|
|
||||||
void swap(rcVectorBase<T, H>& other);
|
|
||||||
|
|
||||||
// Explicitly deleted.
|
|
||||||
rcVectorBase& operator=(const rcVectorBase<T, H>& other);
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T, rcAllocHint H>
|
|
||||||
bool rcVectorBase<T, H>::reserve(rcSizeType count) {
|
|
||||||
if (count <= m_cap) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
T* new_data = allocate_and_copy(count);
|
|
||||||
if (!new_data) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
destroy_range(0, m_size);
|
|
||||||
rcFree(m_data);
|
|
||||||
m_data = new_data;
|
|
||||||
m_cap = count;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
template <typename T, rcAllocHint H>
|
|
||||||
T* rcVectorBase<T, H>::allocate_and_copy(rcSizeType size) {
|
|
||||||
rcAssert(RC_SIZE_MAX / static_cast<rcSizeType>(sizeof(T)) >= size);
|
|
||||||
T* new_data = static_cast<T*>(rcAlloc(sizeof(T) * size, H));
|
|
||||||
if (new_data) {
|
|
||||||
copy_range(new_data, m_data, m_data + m_size);
|
|
||||||
}
|
|
||||||
return new_data;
|
|
||||||
}
|
|
||||||
template <typename T, rcAllocHint H>
|
|
||||||
void rcVectorBase<T, H>::assign(const T* begin, const T* end) {
|
|
||||||
clear();
|
|
||||||
reserve(end - begin);
|
|
||||||
m_size = end - begin;
|
|
||||||
copy_range(m_data, begin, end);
|
|
||||||
}
|
|
||||||
template <typename T, rcAllocHint H>
|
|
||||||
void rcVectorBase<T, H>::push_back(const T& value) {
|
|
||||||
// rcLikely increases performance by ~50% on BM_rcVector_PushPreallocated,
|
|
||||||
// and by ~2-5% on BM_rcVector_Push.
|
|
||||||
if (rcLikely(m_size < m_cap)) {
|
|
||||||
construct(m_data + m_size++, value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
rcAssert(RC_SIZE_MAX / 2 >= m_size);
|
|
||||||
rcSizeType new_cap = m_size ? 2*m_size : 1;
|
|
||||||
T* data = allocate_and_copy(new_cap);
|
|
||||||
// construct between allocate and destroy+free in case value is
|
|
||||||
// in this vector.
|
|
||||||
construct(data + m_size, value);
|
|
||||||
destroy_range(0, m_size);
|
|
||||||
m_size++;
|
|
||||||
m_cap = new_cap;
|
|
||||||
rcFree(m_data);
|
|
||||||
m_data = data;
|
|
||||||
}
|
|
||||||
template <typename T, rcAllocHint H>
|
|
||||||
void rcVectorBase<T, H>::resize_impl(rcSizeType size, const T* value) {
|
|
||||||
if (size < m_size) {
|
|
||||||
destroy_range(size, m_size);
|
|
||||||
m_size = size;
|
|
||||||
} else if (size > m_size) {
|
|
||||||
T* new_data = allocate_and_copy(size);
|
|
||||||
// We defer deconstructing/freeing old data until after constructing
|
|
||||||
// new elements in case "value" is there.
|
|
||||||
if (value) {
|
|
||||||
construct_range(new_data + m_size, new_data + size, *value);
|
|
||||||
} else {
|
|
||||||
construct_range(new_data + m_size, new_data + size);
|
|
||||||
}
|
|
||||||
destroy_range(0, m_size);
|
|
||||||
rcFree(m_data);
|
|
||||||
m_data = new_data;
|
|
||||||
m_cap = size;
|
|
||||||
m_size = size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
template <typename T, rcAllocHint H>
|
|
||||||
void rcVectorBase<T, H>::swap(rcVectorBase<T, H>& other) {
|
|
||||||
// TODO: Reorganize headers so we can use rcSwap here.
|
|
||||||
rcSizeType tmp_cap = other.m_cap;
|
|
||||||
rcSizeType tmp_size = other.m_size;
|
|
||||||
T* tmp_data = other.m_data;
|
|
||||||
|
|
||||||
other.m_cap = m_cap;
|
|
||||||
other.m_size = m_size;
|
|
||||||
other.m_data = m_data;
|
|
||||||
|
|
||||||
m_cap = tmp_cap;
|
|
||||||
m_size = tmp_size;
|
|
||||||
m_data = tmp_data;
|
|
||||||
}
|
|
||||||
// static
|
|
||||||
template <typename T, rcAllocHint H>
|
|
||||||
void rcVectorBase<T, H>::construct_range(T* begin, T* end) {
|
|
||||||
for (T* p = begin; p < end; p++) {
|
|
||||||
construct(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// static
|
|
||||||
template <typename T, rcAllocHint H>
|
|
||||||
void rcVectorBase<T, H>::construct_range(T* begin, T* end, const T& value) {
|
|
||||||
for (T* p = begin; p < end; p++) {
|
|
||||||
construct(p, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// static
|
|
||||||
template <typename T, rcAllocHint H>
|
|
||||||
void rcVectorBase<T, H>::copy_range(T* dst, const T* begin, const T* end) {
|
|
||||||
for (rcSizeType i = 0 ; i < end - begin; i++) {
|
|
||||||
construct(dst + i, begin[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
template <typename T, rcAllocHint H>
|
|
||||||
void rcVectorBase<T, H>::destroy_range(rcSizeType begin, rcSizeType end) {
|
|
||||||
for (rcSizeType i = begin; i < end; i++) {
|
|
||||||
m_data[i].~T();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class rcTempVector : public rcVectorBase<T, RC_ALLOC_TEMP> {
|
|
||||||
typedef rcVectorBase<T, RC_ALLOC_TEMP> Base;
|
|
||||||
public:
|
|
||||||
rcTempVector() : Base() {}
|
|
||||||
explicit rcTempVector(rcSizeType size) : Base(size) {}
|
|
||||||
rcTempVector(rcSizeType size, const T& value) : Base(size, value) {}
|
|
||||||
rcTempVector(const rcTempVector<T>& other) : Base(other) {}
|
|
||||||
rcTempVector(const T* begin, const T* end) : Base(begin, end) {}
|
|
||||||
};
|
|
||||||
template <typename T>
|
|
||||||
class rcPermVector : public rcVectorBase<T, RC_ALLOC_PERM> {
|
|
||||||
typedef rcVectorBase<T, RC_ALLOC_PERM> Base;
|
|
||||||
public:
|
|
||||||
rcPermVector() : Base() {}
|
|
||||||
explicit rcPermVector(rcSizeType size) : Base(size) {}
|
|
||||||
rcPermVector(rcSizeType size, const T& value) : Base(size, value) {}
|
|
||||||
rcPermVector(const rcPermVector<T>& other) : Base(other) {}
|
|
||||||
rcPermVector(const T* begin, const T* end) : Base(begin, end) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// Legacy class. Prefer rcVector<int>.
|
|
||||||
class rcIntArray
|
|
||||||
{
|
|
||||||
rcTempVector<int> m_impl;
|
|
||||||
public:
|
|
||||||
rcIntArray() {}
|
|
||||||
rcIntArray(int n) : m_impl(n, 0) {}
|
|
||||||
void push(int item) { m_impl.push_back(item); }
|
|
||||||
void resize(int size) { m_impl.resize(size); }
|
|
||||||
int pop()
|
|
||||||
{
|
|
||||||
int v = m_impl.back();
|
|
||||||
m_impl.pop_back();
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
int size() const { return static_cast<int>(m_impl.size()); }
|
|
||||||
int& operator[](int index) { return m_impl[index]; }
|
|
||||||
int operator[](int index) const { return m_impl[index]; }
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A simple helper class used to delete an array when it goes out of scope.
|
|
||||||
/// @note This class is rarely if ever used by the end user.
|
|
||||||
template<class T> class rcScopedDelete
|
|
||||||
{
|
|
||||||
T* ptr;
|
|
||||||
public:
|
|
||||||
|
|
||||||
/// Constructs an instance with a null pointer.
|
|
||||||
inline rcScopedDelete() : ptr(0) {}
|
|
||||||
|
|
||||||
/// Constructs an instance with the specified pointer.
|
|
||||||
/// @param[in] p An pointer to an allocated array.
|
|
||||||
inline rcScopedDelete(T* p) : ptr(p) {}
|
|
||||||
inline ~rcScopedDelete() { rcFree(ptr); }
|
|
||||||
|
|
||||||
/// The root array pointer.
|
|
||||||
/// @return The root array pointer.
|
|
||||||
inline operator T*() { return ptr; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Explicitly disabled copy constructor and copy assignment operator.
|
|
||||||
rcScopedDelete(const rcScopedDelete&);
|
|
||||||
rcScopedDelete& operator=(const rcScopedDelete&);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,56 +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 RECASTASSERT_H
|
|
||||||
#define RECASTASSERT_H
|
|
||||||
|
|
||||||
// Note: This header file's only purpose is to include define assert.
|
|
||||||
// Feel free to change the file and include your own implementation instead.
|
|
||||||
|
|
||||||
#ifdef NDEBUG
|
|
||||||
|
|
||||||
// From http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/
|
|
||||||
# define rcAssert(x) do { (void)sizeof(x); } while((void)(__LINE__==-1),false)
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
/// An assertion failure function.
|
|
||||||
// @param[in] expression asserted expression.
|
|
||||||
// @param[in] file Filename of the failed assertion.
|
|
||||||
// @param[in] line Line number of the failed assertion.
|
|
||||||
/// @see rcAssertFailSetCustom
|
|
||||||
typedef void (rcAssertFailFunc)(const char* expression, const char* file, int line);
|
|
||||||
|
|
||||||
/// Sets the base custom assertion failure function to be used by Recast.
|
|
||||||
/// @param[in] assertFailFunc The function to be used in case of failure of #dtAssert
|
|
||||||
void rcAssertFailSetCustom(rcAssertFailFunc *assertFailFunc);
|
|
||||||
|
|
||||||
/// Gets the base custom assertion failure function to be used by Recast.
|
|
||||||
rcAssertFailFunc* rcAssertFailGetCustom();
|
|
||||||
|
|
||||||
# include <assert.h>
|
|
||||||
# define rcAssert(expression) \
|
|
||||||
{ \
|
|
||||||
rcAssertFailFunc* failFunc = rcAssertFailGetCustom(); \
|
|
||||||
if(failFunc == NULL) { assert(expression); } \
|
|
||||||
else if(!(expression)) { (*failFunc)(#expression, __FILE__, __LINE__); } \
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // RECASTASSERT_H
|
|
@ -1,575 +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.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <float.h>
|
|
||||||
#define _USE_MATH_DEFINES
|
|
||||||
#include <math.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#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 <typename T>
|
|
||||||
T* rcNew(rcAllocHint hint) {
|
|
||||||
T* ptr = (T*)rcAlloc(sizeof(T), hint);
|
|
||||||
::new(rcNewTag(), (void*)ptr) T();
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Destroys and frees an object allocated with rcNew.
|
|
||||||
/// @param[in] ptr The object pointer to delete.
|
|
||||||
template <typename T>
|
|
||||||
void rcDelete(T* ptr) {
|
|
||||||
if (ptr) {
|
|
||||||
ptr->~T();
|
|
||||||
rcFree((void*)ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
|
|
||||||
float rcSqrt(float x)
|
|
||||||
{
|
|
||||||
return sqrtf(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @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<rcHeightfield>(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<rcCompactHeightfield>(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<rcHeightfieldLayerSet>(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<rcContourSet>(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<rcPolyMesh>(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;
|
|
||||||
}
|
|
||||||
*/
|
|
@ -1,60 +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.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#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);
|
|
||||||
}
|
|
@ -1,591 +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.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <float.h>
|
|
||||||
#define _USE_MATH_DEFINES
|
|
||||||
#include <math.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "Recast.h"
|
|
||||||
#include "RecastAlloc.h"
|
|
||||||
#include "RecastAssert.h"
|
|
||||||
|
|
||||||
/// @par
|
|
||||||
///
|
|
||||||
/// Basically, any spans that are closer to a boundary or obstruction than the specified radius
|
|
||||||
/// are marked as unwalkable.
|
|
||||||
///
|
|
||||||
/// This method is usually called immediately after the heightfield has been built.
|
|
||||||
///
|
|
||||||
/// @see rcCompactHeightfield, rcBuildCompactHeightfield, rcConfig::walkableRadius
|
|
||||||
bool rcErodeWalkableArea(rcContext* ctx, int radius, rcCompactHeightfield& chf)
|
|
||||||
{
|
|
||||||
rcAssert(ctx);
|
|
||||||
|
|
||||||
const int w = chf.width;
|
|
||||||
const int h = chf.height;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,35 +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.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "RecastAssert.h"
|
|
||||||
|
|
||||||
#ifndef NDEBUG
|
|
||||||
|
|
||||||
static rcAssertFailFunc* sRecastAssertFailFunc = 0;
|
|
||||||
|
|
||||||
void rcAssertFailSetCustom(rcAssertFailFunc *assertFailFunc)
|
|
||||||
{
|
|
||||||
sRecastAssertFailFunc = assertFailFunc;
|
|
||||||
}
|
|
||||||
|
|
||||||
rcAssertFailFunc* rcAssertFailGetCustom()
|
|
||||||
{
|
|
||||||
return sRecastAssertFailFunc;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
File diff suppressed because it is too large
Load Diff
@ -1,202 +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.
|
|
||||||
//
|
|
||||||
|
|
||||||
#define _USE_MATH_DEFINES
|
|
||||||
#include <math.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#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: <tt>rcAbs(currentSpan.smax - neighborSpan.smax) < waklableClimb</tt>
|
|
||||||
///
|
|
||||||
/// @warning Will override the effect of #rcFilterLedgeSpans. So if both filters are used, call
|
|
||||||
/// #rcFilterLedgeSpans after calling this filter.
|
|
||||||
///
|
|
||||||
/// @see rcHeightfield, rcConfig
|
|
||||||
void rcFilterLowHangingWalkableObstacles(rcContext* ctx, const int walkableClimb, rcHeightfield& solid)
|
|
||||||
{
|
|
||||||
rcAssert(ctx);
|
|
||||||
|
|
||||||
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: <tt>rcAbs(currentSpan.smax - neighborSpan.smax) > walkableClimb</tt>
|
|
||||||
///
|
|
||||||
/// @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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,644 +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.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <float.h>
|
|
||||||
#define _USE_MATH_DEFINES
|
|
||||||
#include <math.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "Recast.h"
|
|
||||||
#include "RecastAlloc.h"
|
|
||||||
#include "RecastAssert.h"
|
|
||||||
|
|
||||||
|
|
||||||
// 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<unsigned char> srcReg((unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP));
|
|
||||||
if (!srcReg)
|
|
||||||
{
|
|
||||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'srcReg' (%d).", chf.spanCount);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
memset(srcReg,0xff,sizeof(unsigned char)*chf.spanCount);
|
|
||||||
|
|
||||||
const int nsweeps = chf.width;
|
|
||||||
rcScopedDelete<rcLayerSweepSpan> sweeps((rcLayerSweepSpan*)rcAlloc(sizeof(rcLayerSweepSpan)*nsweeps, RC_ALLOC_TEMP));
|
|
||||||
if (!sweeps)
|
|
||||||
{
|
|
||||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'sweeps' (%d).", nsweeps);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Partition walkable area into monotone regions.
|
|
||||||
int prevCount[256];
|
|
||||||
unsigned char regId = 0;
|
|
||||||
|
|
||||||
for (int y = borderSize; y < h-borderSize; ++y)
|
|
||||||
{
|
|
||||||
memset(prevCount,0,sizeof(int)*regId);
|
|
||||||
unsigned char sweepId = 0;
|
|
||||||
|
|
||||||
for (int x = borderSize; x < w-borderSize; ++x)
|
|
||||||
{
|
|
||||||
const rcCompactCell& c = chf.cells[x+y*w];
|
|
||||||
|
|
||||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
|
||||||
{
|
|
||||||
const rcCompactSpan& s = chf.spans[i];
|
|
||||||
if (chf.areas[i] == RC_NULL_AREA) continue;
|
|
||||||
|
|
||||||
unsigned char sid = 0xff;
|
|
||||||
|
|
||||||
// -x
|
|
||||||
if (rcGetCon(s, 0) != RC_NOT_CONNECTED)
|
|
||||||
{
|
|
||||||
const int ax = x + rcGetDirOffsetX(0);
|
|
||||||
const int ay = y + rcGetDirOffsetY(0);
|
|
||||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0);
|
|
||||||
if (chf.areas[ai] != RC_NULL_AREA && srcReg[ai] != 0xff)
|
|
||||||
sid = srcReg[ai];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sid == 0xff)
|
|
||||||
{
|
|
||||||
sid = sweepId++;
|
|
||||||
sweeps[sid].nei = 0xff;
|
|
||||||
sweeps[sid].ns = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -y
|
|
||||||
if (rcGetCon(s,3) != RC_NOT_CONNECTED)
|
|
||||||
{
|
|
||||||
const int ax = x + rcGetDirOffsetX(3);
|
|
||||||
const int ay = y + rcGetDirOffsetY(3);
|
|
||||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3);
|
|
||||||
const unsigned char nr = srcReg[ai];
|
|
||||||
if (nr != 0xff)
|
|
||||||
{
|
|
||||||
// Set neighbour when first valid neighbour is encoutered.
|
|
||||||
if (sweeps[sid].ns == 0)
|
|
||||||
sweeps[sid].nei = nr;
|
|
||||||
|
|
||||||
if (sweeps[sid].nei == nr)
|
|
||||||
{
|
|
||||||
// Update existing neighbour
|
|
||||||
sweeps[sid].ns++;
|
|
||||||
prevCount[nr]++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// This is hit if there is nore than one neighbour.
|
|
||||||
// Invalidate the neighbour.
|
|
||||||
sweeps[sid].nei = 0xff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
srcReg[i] = sid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create unique ID.
|
|
||||||
for (int i = 0; i < sweepId; ++i)
|
|
||||||
{
|
|
||||||
// If the neighbour is set and there is only one continuous connection to it,
|
|
||||||
// the sweep will be merged with the previous one, else new region is created.
|
|
||||||
if (sweeps[i].nei != 0xff && prevCount[sweeps[i].nei] == (int)sweeps[i].ns)
|
|
||||||
{
|
|
||||||
sweeps[i].id = sweeps[i].nei;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (regId == 255)
|
|
||||||
{
|
|
||||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Region ID overflow.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
sweeps[i].id = regId++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remap local sweep ids to region ids.
|
|
||||||
for (int x = borderSize; x < w-borderSize; ++x)
|
|
||||||
{
|
|
||||||
const rcCompactCell& c = chf.cells[x+y*w];
|
|
||||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
|
||||||
{
|
|
||||||
if (srcReg[i] != 0xff)
|
|
||||||
srcReg[i] = sweeps[srcReg[i]].id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate and init layer regions.
|
|
||||||
const int nregs = (int)regId;
|
|
||||||
rcScopedDelete<rcLayerRegion> regs((rcLayerRegion*)rcAlloc(sizeof(rcLayerRegion)*nregs, RC_ALLOC_TEMP));
|
|
||||||
if (!regs)
|
|
||||||
{
|
|
||||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'regs' (%d).", nregs);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
memset(regs, 0, sizeof(rcLayerRegion)*nregs);
|
|
||||||
for (int i = 0; i < nregs; ++i)
|
|
||||||
{
|
|
||||||
regs[i].layerId = 0xff;
|
|
||||||
regs[i].ymin = 0xffff;
|
|
||||||
regs[i].ymax = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find region neighbours and overlapping regions.
|
|
||||||
for (int y = 0; y < h; ++y)
|
|
||||||
{
|
|
||||||
for (int x = 0; x < w; ++x)
|
|
||||||
{
|
|
||||||
const rcCompactCell& c = chf.cells[x+y*w];
|
|
||||||
|
|
||||||
unsigned char lregs[RC_MAX_LAYERS];
|
|
||||||
int nlregs = 0;
|
|
||||||
|
|
||||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
|
||||||
{
|
|
||||||
const rcCompactSpan& s = chf.spans[i];
|
|
||||||
const unsigned char ri = srcReg[i];
|
|
||||||
if (ri == 0xff) continue;
|
|
||||||
|
|
||||||
regs[ri].ymin = rcMin(regs[ri].ymin, s.y);
|
|
||||||
regs[ri].ymax = rcMax(regs[ri].ymax, s.y);
|
|
||||||
|
|
||||||
// Collect all region layers.
|
|
||||||
if (nlregs < RC_MAX_LAYERS)
|
|
||||||
lregs[nlregs++] = ri;
|
|
||||||
|
|
||||||
// Update neighbours
|
|
||||||
for (int dir = 0; dir < 4; ++dir)
|
|
||||||
{
|
|
||||||
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
|
|
||||||
{
|
|
||||||
const int ax = x + rcGetDirOffsetX(dir);
|
|
||||||
const int ay = y + rcGetDirOffsetY(dir);
|
|
||||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
|
|
||||||
const unsigned char rai = srcReg[ai];
|
|
||||||
if (rai != 0xff && rai != ri)
|
|
||||||
{
|
|
||||||
// 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<<dir);
|
|
||||||
// Update height so that it matches on both sides of the portal.
|
|
||||||
const rcCompactSpan& as = chf.spans[ai];
|
|
||||||
if (as.y > hmin)
|
|
||||||
layer->heights[idx] = rcMax(layer->heights[idx], (unsigned char)(as.y - hmin));
|
|
||||||
}
|
|
||||||
// Valid connection mask
|
|
||||||
if (chf.areas[ai] != RC_NULL_AREA && lid == alid)
|
|
||||||
{
|
|
||||||
const int nx = ax - borderSize;
|
|
||||||
const int ny = ay - borderSize;
|
|
||||||
if (nx >= 0 && ny >= 0 && nx < lw && ny < lh)
|
|
||||||
con |= (unsigned char)(1<<dir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
layer->cons[idx] = (portal << 4) | con;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (layer->minx > layer->maxx)
|
|
||||||
layer->minx = layer->maxx = 0;
|
|
||||||
if (layer->miny > layer->maxy)
|
|
||||||
layer->miny = layer->maxy = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,454 +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.
|
|
||||||
//
|
|
||||||
|
|
||||||
#define _USE_MATH_DEFINES
|
|
||||||
#include <math.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#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<nvrow; ++i)
|
|
||||||
{
|
|
||||||
if (minX > 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;
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue