Merge branch 'main' into release-2.0

# Conflicts:
#	Recast/Include/Recast.h
#	Recast/Source/Recast.cpp
#	RecastDemo/Include/Sample.h
#	RecastDemo/Source/Sample.cpp
This commit is contained in:
Graham Pentheny
2023-01-02 14:25:28 -05:00
121 changed files with 39170 additions and 13229 deletions
+5 -1
View File
@@ -5,4 +5,8 @@ root = true
[*]
indent_size = 4
indent_style = tab
indent_style = tab
[*.yml]
indent_size = 2
indent_style = space
+221
View File
@@ -0,0 +1,221 @@
name: Build
on:
push:
branches: [ "**" ]
pull_request:
branches: [ "**" ]
jobs:
macOS-premake:
strategy:
matrix:
conf:
- Debug
- Release
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- name: Download & install SDL
run: |
curl -L -o SDL2.dmg https://github.com/libsdl-org/SDL/releases/download/release-2.24.2/SDL2-2.24.2.dmg
hdiutil attach SDL2.dmg
cp -r /Volumes/SDL2/SDL2.framework RecastDemo/Bin
hdiutil detach /Volumes/SDL2
rm SDL2.dmg
- name: Download & install premake
working-directory: RecastDemo
run: |
curl -L -o premake.tar.gz https://github.com/premake/premake-core/releases/download/v5.0.0-beta2/premake-5.0.0-beta2-macosx.tar.gz
tar -xzf premake.tar.gz
rm premake.tar.gz
- name: Run premake
working-directory: RecastDemo
run: ./premake5 xcode4
- name: Build With Xcode
working-directory: RecastDemo/Build/xcode4/
run: xcodebuild -scheme RecastDemo -configuration ${{matrix.conf}} -project RecastDemo.xcodeproj build
- name: Build Unit Tests With Xcode
working-directory: RecastDemo/Build/xcode4/
run: xcodebuild -scheme Tests -configuration ${{matrix.conf}} -project Tests.xcodeproj build
macos-cmake:
strategy:
matrix:
conf:
- Debug
- Release
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- name: Download & install SDL
run: |
curl -L -o SDL2.dmg https://github.com/libsdl-org/SDL/releases/download/release-2.24.2/SDL2-2.24.2.dmg
hdiutil attach SDL2.dmg
cp -r /Volumes/SDL2/SDL2.framework ${{github.workspace}}/RecastDemo/Bin/
hdiutil detach /Volumes/SDL2
rm SDL2.dmg
- name: Configure CMake
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{matrix.conf}} -DSDL2_LIBRARY=${{github.workspace}}/RecastDemo/Bin/SDL2.framework
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{matrix.conf}}
linux-premake:
strategy:
matrix:
conf:
- debug
- release
compiler:
- clang
- gcc
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install opengl and SDL
run: |
sudo apt-get update
sudo apt-get install -y libgl1-mesa-dev libsdl2-dev
- name: Download & Install premake
working-directory: RecastDemo
run: |
curl -L -o premake.tar.gz https://github.com/premake/premake-core/releases/download/v5.0.0-beta2/premake-5.0.0-beta2-linux.tar.gz
tar -xzf premake.tar.gz
rm premake.tar.gz
- name: Run premake
working-directory: RecastDemo
run: ./premake5 --cc=${{matrix.compiler}} gmake2
- name: Build
working-directory: RecastDemo/Build/gmake2
run: make config=${{matrix.conf}} verbose=true
linux-cmake:
strategy:
matrix:
conf:
- Debug
- Release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install opengl and SDL
run: |
sudo apt-get update
sudo apt-get install -y libgl1-mesa-dev libsdl2-dev
- name: Configure CMake
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{matrix.conf}}
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{matrix.conf}}
windows-premake:
strategy:
matrix:
conf:
- Debug
- Release
vs-version:
- vs2019
- vs2022
include:
- vs-version: vs2019
version-range: '16.0'
runner: windows-2019
- vs-version: vs2022
version-range: '17.0'
runner: windows-2022
runs-on: ${{matrix.runner}}
steps:
- uses: actions/checkout@v3
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v1.1
with:
vs-version: ${{matrix.version-range}}
- name: Download and Install SDL
working-directory: RecastDemo/Contrib
shell: pwsh
run: |
(new-object System.Net.WebClient).DownloadFile("https://github.com/libsdl-org/SDL/releases/download/release-2.24.2/SDL2-devel-2.24.2-VC.zip","${{github.workspace}}/RecastDemo/Contrib/SDL.zip")
tar -xf SDL.zip
ren SDL2-2.24.2 SDL
del SDL.zip
- name: Download and Install Premake
working-directory: RecastDemo
shell: pwsh
run: |
(new-object System.Net.WebClient).DownloadFile("https://github.com/premake/premake-core/releases/download/v5.0.0-beta2/premake-5.0.0-beta2-windows.zip","${{github.workspace}}/RecastDemo/premake.zip")
tar -xf premake.zip
del premake.zip
- name: Run Premake
working-directory: RecastDemo
run: ./premake5.exe ${{matrix.vs-version}}
- name: Build
working-directory: RecastDemo/Build/${{matrix.vs-version}}
run: msbuild RecastDemo.vcxproj -property:Configuration=${{matrix.conf}}
windows-cmake:
strategy:
matrix:
conf:
- Debug
- Release
vs-version:
- vs2019
- vs2022
include:
- vs-version: vs2019
cmake-generator: Visual Studio 16 2019
runner: windows-2019
- vs-version: vs2022
cmake-generator: Visual Studio 17 2022
runner: windows-2022
runs-on: ${{matrix.runner}}
steps:
- uses: actions/checkout@v3
- name: Download and Install SDL
working-directory: RecastDemo/Contrib
shell: pwsh
run: |
(new-object System.Net.WebClient).DownloadFile("https://github.com/libsdl-org/SDL/releases/download/release-2.24.2/SDL2-devel-2.24.2-VC.zip","${{github.workspace}}/RecastDemo/Contrib/SDL.zip")
tar -xf SDL.zip
ren SDL2-2.24.2 SDL
del SDL.zip
- name: Configure CMake
run: cmake -G "${{matrix.cmake-generator}}" -B ${{github.workspace}}/build -D CMAKE_BUILD_TYPE=${{matrix.conf}} -D CMAKE_INSTALL_PREFIX=${{github.workspace}}/build
- name: Build with CMake
run: cmake --build ${{github.workspace}}/build --config ${{matrix.conf}}
+109
View File
@@ -0,0 +1,109 @@
name: Tests
on:
push:
branches: [ "**" ]
pull_request:
branches: [ "**" ]
jobs:
macos-tests:
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- name: Download & install SDL
run: |
curl -L -o SDL2.dmg https://github.com/libsdl-org/SDL/releases/download/release-2.24.2/SDL2-2.24.2.dmg
hdiutil attach SDL2.dmg
cp -r /Volumes/SDL2/SDL2.framework RecastDemo/Bin
hdiutil detach /Volumes/SDL2
rm SDL2.dmg
- name: Download & install premake
working-directory: RecastDemo
run: |
curl -L -o premake.tar.gz https://github.com/premake/premake-core/releases/download/v5.0.0-beta2/premake-5.0.0-beta2-macosx.tar.gz
tar -xzf premake.tar.gz
rm premake.tar.gz
- name: Run premake
working-directory: RecastDemo
run: ./premake5 xcode4
- name: Build Unit Tests With Xcode
working-directory: RecastDemo/Build/xcode4/
run: xcodebuild -scheme Tests -configuration Debug -project Tests.xcodeproj build
- name: Run unit tests
working-directory: RecastDemo/Bin
run: ./Tests
linux-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install opengl and SDL
run: |
sudo apt-get update
sudo apt-get install -y libgl1-mesa-dev libsdl2-dev
- name: Download & Install premake
working-directory: RecastDemo
run: |
curl -L -o premake.tar.gz https://github.com/premake/premake-core/releases/download/v5.0.0-beta2/premake-5.0.0-beta2-linux.tar.gz
tar -xzf premake.tar.gz
rm premake.tar.gz
- name: Run premake
working-directory: RecastDemo
run: ./premake5 --cc=clang gmake2
- name: Build
working-directory: RecastDemo/Build/gmake2
run: make config=debug verbose=true
- name: Run Tests
working-directory: RecastDemo/Bin
run: ./Tests
windows-tests:
runs-on: windows-2022
steps:
- uses: actions/checkout@v3
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v1.1
- name: Download and Install SDL
working-directory: RecastDemo/Contrib
shell: pwsh
run: |
(new-object System.Net.WebClient).DownloadFile("https://github.com/libsdl-org/SDL/releases/download/release-2.24.2/SDL2-devel-2.24.2-VC.zip","${{github.workspace}}/RecastDemo/Contrib/SDL.zip")
tar -xf SDL.zip
ren SDL2-2.24.2 SDL
del SDL.zip
- name: Download and Install Premake
working-directory: RecastDemo
shell: pwsh
run: |
(new-object System.Net.WebClient).DownloadFile("https://github.com/premake/premake-core/releases/download/v5.0.0-beta2/premake-5.0.0-beta2-windows.zip","${{github.workspace}}/RecastDemo/premake.zip")
tar -xf premake.zip
del premake.zip
- name: Run Premake
working-directory: RecastDemo
run: ./premake5.exe vs2022
- name: Build
working-directory: RecastDemo/Build/vs2022
run: msbuild Tests.vcxproj -property:Configuration=Debug
- name: Run Tests
working-directory: RecastDemo/Bin/
run: ./Tests.exe
+10
View File
@@ -16,6 +16,9 @@ RecastDemo/Bin/Tests
# Build directory
RecastDemo/Build
# XCode debug symbols archive
RecastDemo/Bin/*.dSYM
# Ignore meshes
RecastDemo/Bin/Meshes/*
@@ -43,3 +46,10 @@ RecastDemo/Contrib/SDL/*
## Generated doc files
Docs/html
## CMake build cache
build
## IDE files
.idea/
cmake-build-*/
-45
View File
@@ -1,45 +0,0 @@
sudo: false
language: cpp
# Build with gcc and clang.
compiler:
- gcc
# Disable clang for now since it seems there's a problem using it with fast fp math; it's likely Travis' ancient environment that's at fault.
# - clang
# Build both debug and release configurations, through use of an environment variable in the build matrix.
env:
- CONFIGURATION=debug
- CONFIGURATION=release
install:
# Download and build SDL2 from source.
- mkdir dummyprefix
- export PREFIX=$PWD/dummyprefix
- export PKG_CONFIG_PATH=$PREFIX/lib/pkgconfig
- export PATH=$PATH:$PWD/dummyprefix/bin
- wget https://www.libsdl.org/release/SDL2-2.0.4.tar.gz -O SDL2.tar.gz
- tar -xzf SDL2.tar.gz
- cd SDL2-2.0.4
- ./configure --prefix=$PREFIX
- make -j5
- make install
- cd ..
# Download and build premake5 from source; the Travis environment doesn't have the right version of glibc6 for the prebuilt binaries to work.
- wget https://github.com/premake/premake-core/releases/download/v5.0.0-alpha6/premake-5.0.0-alpha6-src.zip -O premake.zip
- unzip premake.zip
- cd premake-5.0.0-alpha6/build/gmake.unix
- make config=release
- cd ../../..
- mv premake-5.0.0-alpha6/bin/release/premake5 premake5
# Run premake to generate makefiles.
# Have to cd into directory and back out since premake5 doesn't appear to accept a directory argument.
before_script:
- cd RecastDemo && ../premake5 gmake && cd ..
# Run make in the directory containing generated makefiles, on the configuration specified by the environment variable.
script:
- make -C RecastDemo/Build/gmake config=$CONFIGURATION
- RecastDemo/Bin/Tests
+88
View File
@@ -0,0 +1,88 @@
cmake_minimum_required(VERSION 3.0)
project(RecastNavigation)
# lib versions
SET(SOVERSION 1)
set(LIB_VERSION 1.5.1)
string(REPLACE "." "," LIB_VERSION_NUM "${LIB_VERSION}.0")
set_property(GLOBAL PROPERTY CXX_STANDARD 98)
option(RECASTNAVIGATION_DEMO "Build demo" ON)
option(RECASTNAVIGATION_TESTS "Build tests" ON)
option(RECASTNAVIGATION_EXAMPLES "Build examples" ON)
if(MSVC AND BUILD_SHARED_LIBS)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
endif()
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)
configure_file(
"${RecastNavigation_SOURCE_DIR}/version.h.in"
"${RecastNavigation_BINARY_DIR}/version.h")
install(FILES "${RecastNavigation_BINARY_DIR}/version.h" DESTINATION
${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation)
# Needed for recastnavigation.pc.in
set(prefix ${CMAKE_INSTALL_PREFIX})
set(exec_prefix "\${prefix}")
set(libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}")
set(bindir "\${exec_prefix}/${CMAKE_INSTALL_BINDIR}")
set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
set(PACKAGE_VERSION "${LIB_VERSION}")
configure_file(
"${RecastNavigation_SOURCE_DIR}/recastnavigation.pc.in"
"${RecastNavigation_BINARY_DIR}/recastnavigation.pc"
@ONLY)
install(FILES "${RecastNavigation_BINARY_DIR}/recastnavigation.pc"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
add_subdirectory(DebugUtils)
add_subdirectory(Detour)
add_subdirectory(DetourCrowd)
add_subdirectory(DetourTileCache)
add_subdirectory(Recast)
configure_package_config_file(
${PROJECT_SOURCE_DIR}/recastnavigation-config.cmake.in
recastnavigation-config.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/recastnavigation
)
write_basic_package_version_file(
recastnavigation-config-version.cmake
VERSION ${LIB_VERSION}
COMPATIBILITY AnyNewerVersion
)
export(
EXPORT recastnavigation-targets
NAMESPACE RecastNavigation::
FILE recastnavigation-targets.cmake
)
install(
EXPORT recastnavigation-targets
NAMESPACE RecastNavigation::
FILE recastnavigation-targets.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/recastnavigation
)
install(
FILES
${PROJECT_BINARY_DIR}/recastnavigation-config.cmake
${PROJECT_BINARY_DIR}/recastnavigation-config-version.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/recastnavigation
)
if (RECASTNAVIGATION_DEMO)
add_subdirectory(RecastDemo)
endif ()
if (RECASTNAVIGATION_TESTS)
enable_testing()
add_subdirectory(Tests)
endif ()
+9 -17
View File
@@ -3,30 +3,23 @@
We'd love for you to contribute to our source code and to make Recast and Detour even better than they are
today! Here are the guidelines we'd like you to follow:
- [Code of Conduct](#coc)
- [Question or Problem?](#question)
- [Issues and Bugs](#issue)
- [Feature Requests](#feature)
- [Submission Guidelines](#submission-guidelines)
- [Git Commit Guidelines](#git-commit-guidelines)
## <a name="coc"></a> Code of Conduct
## Code of Conduct
This project adheres to the [Open Code of Conduct][code-of-conduct].
By participating, you are expected to honor this code.
## <a name="question"></a> Got a Question or Problem?
## Got a Question or Problem?
If you have questions about how to use Recast or Detour, please direct these to the [Google Group][groups]
discussion list. We are also available on [Gitter][gitter].
## <a name="issue"></a> Found an Issue?
## Found an Issue?
If you find a bug in the source code or a mistake in the documentation, you can help us by
submitting an issue to our [GitHub Repository][github]. Even better you can submit a Pull Request
with a fix.
**Please see the Submission Guidelines below**.
## <a name="feature"></a> Want a Feature?
## Want a Feature?
You can request a new feature by submitting an issue to our [GitHub Repository][github]. If you
would like to implement a new feature then consider what kind of change it is:
@@ -78,8 +71,7 @@ Before you submit your pull request consider the following guidelines:
```
* Implement your changes, **including appropriate tests if appropriate/possible**.
* Commit your changes using a descriptive commit message that follows our
[commit message conventions](#commit-message-format).
* Commit your changes using a descriptive commit message that follows our commit message conventions (see below).
```shell
git commit -a
@@ -108,10 +100,10 @@ Before you submit your pull request consider the following guidelines:
If you have rebased to squash commits together, you will need to force push to update the PR:
```shell
git rebase master -i
git push origin my-fix-branch -f
```
```shell
git rebase master -i
git push origin my-fix-branch -f
```
That's it! Thank you for your contribution!
+39
View File
@@ -0,0 +1,39 @@
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 ${LIB_VERSION}
COMPILE_PDB_OUTPUT_DIRECTORY .
COMPILE_PDB_NAME "DebugUtils-d"
)
install(TARGETS DebugUtils
EXPORT recastnavigation-targets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT library
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation
)
file(GLOB INCLUDES Include/*.h)
install(FILES ${INCLUDES} DESTINATION
${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation)
if(MSVC)
install(FILES "$<TARGET_FILE_DIR:DebugUtils>/DebugUtils-d.pdb" CONFIGURATIONS "Debug" DESTINATION "lib")
endif()
+5 -3
View File
@@ -27,7 +27,7 @@ enum duDebugDrawPrimitives
DU_DRAW_POINTS,
DU_DRAW_LINES,
DU_DRAW_TRIS,
DU_DRAW_QUADS,
DU_DRAW_QUADS
};
/// Abstract debug draw interface.
@@ -57,11 +57,13 @@ struct duDebugDraw
/// Submit a vertex
/// @param pos [in] position of the verts.
/// @param color [in] color of the verts.
/// @param uv [in] the uv coordinates 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.
/// @param u,v [in] the uv coordinates 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.
@@ -197,15 +199,15 @@ class duDisplayList : public duDebugDraw
int m_size;
int m_cap;
bool m_depthMask;
duDebugDrawPrimitives m_prim;
float m_primSize;
bool m_depthMask;
void resize(int cap);
public:
duDisplayList(int cap = 512);
~duDisplayList();
virtual ~duDisplayList();
virtual void depthMask(bool state);
virtual void begin(duDebugDrawPrimitives prim, float size = 1.0f);
virtual void vertex(const float x, const float y, const float z, unsigned int color);
+1 -1
View File
@@ -27,7 +27,7 @@ enum DrawNavMeshFlags
{
DU_DRAWNAVMESH_OFFMESHCONS = 0x01,
DU_DRAWNAVMESH_CLOSEDLIST = 0x02,
DU_DRAWNAVMESH_COLOR_TILES = 0x04,
DU_DRAWNAVMESH_COLOR_TILES = 0x04
};
void duDebugDrawNavMesh(struct duDebugDraw* dd, const dtNavMesh& mesh, unsigned char flags);
+1 -2
View File
@@ -21,7 +21,7 @@
struct duFileIO
{
virtual ~duFileIO() = 0;
virtual ~duFileIO();
virtual bool isWriting() const = 0;
virtual bool isReading() const = 0;
virtual bool write(const void* ptr, const size_t size) = 0;
@@ -39,5 +39,4 @@ bool duReadCompactHeightfield(struct rcCompactHeightfield& chf, duFileIO* io);
void duLogBuildTimes(rcContext& ctx, const int totalTileUsec);
#endif // RECAST_DUMP_H
+2 -3
View File
@@ -16,7 +16,6 @@
// 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"
@@ -530,9 +529,9 @@ duDisplayList::duDisplayList(int cap) :
m_color(0),
m_size(0),
m_cap(0),
m_depthMask(true),
m_prim(DU_DRAW_LINES),
m_primSize(1.0f)
m_primSize(1.0f),
m_depthMask(true)
{
if (cap < 8)
cap = 8;
+3 -1
View File
@@ -101,7 +101,9 @@ static void drawPolyBoundaries(duDebugDraw* dd, const dtMeshTile* tile,
}
for (int m = 0, n = 2; m < 3; n=m++)
{
if (((t[3] >> (n*2)) & 0x3) == 0) continue; // Skip inner detail edges.
if ((dtGetDetailTriEdgeFlags(t[3], n) & DT_DETAIL_EDGE_BOUNDARY) == 0)
continue;
if (distancePtLine2d(tv[n],v0,v1) < thr &&
distancePtLine2d(tv[m],v0,v1) < thr)
{
-1
View File
@@ -16,7 +16,6 @@
// 3. This notice may not be removed or altered from any source distribution.
//
#define _USE_MATH_DEFINES
#include <math.h>
#include "DebugDraw.h"
#include "RecastDebugDraw.h"
+1 -3
View File
@@ -16,7 +16,6 @@
// 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>
@@ -25,10 +24,9 @@
#include "RecastAlloc.h"
#include "RecastDump.h"
duFileIO::~duFileIO()
{
// Empty
// Defined out of line to fix the weak v-tables warning
}
static void ioprintf(duFileIO* io, const char* format, ...)
+33
View File
@@ -0,0 +1,33 @@
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 ${LIB_VERSION}
COMPILE_PDB_OUTPUT_DIRECTORY .
COMPILE_PDB_NAME "Detour-d"
)
install(TARGETS Detour
EXPORT recastnavigation-targets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT library
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation
)
file(GLOB INCLUDES Include/*.h)
install(FILES ${INCLUDES} DESTINATION
${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation)
if(MSVC)
install(FILES "$<TARGET_FILE_DIR:Detour>/Detour-d.pdb" CONFIGURATIONS "Debug" DESTINATION "lib")
endif()
+23 -2
View File
@@ -37,7 +37,6 @@ feature to find minor members.
/// 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.
@@ -283,6 +282,28 @@ inline bool dtVequal(const float* p0, const float* 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)]
@@ -297,7 +318,7 @@ inline float dtVdot2D(const float* u, const float* v)
/// 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.
/// @return The perp 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)
+4
View File
@@ -8,6 +8,9 @@ Members in this module are wrappers around the standard math library
#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); }
@@ -16,5 +19,6 @@ 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
+30 -11
View File
@@ -92,7 +92,7 @@ static const int DT_MAX_AREAS = 64;
enum dtTileFlags
{
/// The navigation mesh owns the tile memory and is responsible for freeing it.
DT_TILE_FREE_DATA = 0x01,
DT_TILE_FREE_DATA = 0x01
};
/// Vertex flags returned by dtNavMeshQuery::findStraightPath.
@@ -100,27 +100,32 @@ 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.
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.
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)
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
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
};
@@ -134,7 +139,7 @@ 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,
DT_POLYTYPE_OFFMESH_CONNECTION = 1
};
@@ -273,14 +278,15 @@ struct dtMeshTile
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]
float* verts; ///< The tile vertices. [(x, y, z) * 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) * dtMeshHeader::detailTriCount]
/// 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]
@@ -298,6 +304,15 @@ private:
dtMeshTile& operator=(const dtMeshTile&);
};
/// Get flags for edge in detail triangle.
/// @param[in] triFlags The flags for the triangle (last component of detail vertices above).
/// @param[in] edgeIndex 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()
@@ -307,8 +322,8 @@ 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.
int maxTiles; ///< The maximum number of tiles the navigation mesh can contain. This and maxPolys are used to calculate how many bits are needed to identify tiles and polygons uniquely.
int maxPolys; ///< The maximum number of polygons each tile can contain. This and maxTiles are used to calculate how many bits are needed to identify tiles and polygons uniquely.
};
/// A navigation mesh based on tiles of convex polygons.
@@ -628,7 +643,9 @@ private:
dtPolyRef* polys, const int maxPolys) const;
/// Find nearest polygon within a tile.
dtPolyRef findNearestPolyInTile(const dtMeshTile* tile, const float* center,
const float* extents, float* nearestPt) const;
const float* halfExtents, float* nearestPt) const;
/// Returns 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;
@@ -648,6 +665,8 @@ private:
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.
+29 -14
View File
@@ -112,8 +112,6 @@ public:
};
/// Provides information about raycast hit
/// filled by dtNavMeshQuery::raycast
/// @ingroup detour
@@ -147,7 +145,7 @@ struct dtRaycastHit
class dtPolyQuery
{
public:
virtual ~dtPolyQuery() { }
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.
@@ -170,7 +168,7 @@ public:
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.
@@ -308,34 +306,50 @@ public:
///@{
/// Finds the polygon nearest to the specified center point.
/// [opt] means the specified parameter can be a null pointer, in that case the output parameter will not be set.
///
/// @param[in] center The center of the search box. [(x, y, z)]
/// @param[in] extents The search distance along each axis. [(x, y, z)]
/// @param[in] halfExtents The search distance along each axis. [(x, y, z)]
/// @param[in] filter The polygon filter to apply to the query.
/// @param[out] nearestRef The reference id of the nearest polygon.
/// @param[out] nearestPt The nearest point on the polygon. [opt] [(x, y, z)]
/// @param[out] nearestRef The reference id of the nearest polygon. Will be set to 0 if no polygon is found.
/// @param[out] nearestPt The nearest point on the polygon. Unchanged if no polygon is found. [opt] [(x, y, z)]
/// @returns The status flags for the query.
dtStatus findNearestPoly(const float* center, const float* extents,
dtStatus findNearestPoly(const float* center, const float* halfExtents,
const dtQueryFilter* filter,
dtPolyRef* nearestRef, float* nearestPt) const;
/// Finds the polygon nearest to the specified center point.
/// [opt] means the specified parameter can be a null pointer, in that case the output parameter will not be set.
///
/// @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. Will be set to 0 if no polygon is found.
/// @param[out] nearestPt The nearest point on the polygon. Unchanged if no polygon is found. [opt] [(x, y, z)]
/// @param[out] isOverPoly Set to true if the point's X/Z coordinate lies inside the polygon, false otherwise. Unchanged if no polygon is found. [opt]
/// @returns The status flags for the query.
dtStatus findNearestPoly(const float* center, const float* halfExtents,
const dtQueryFilter* filter,
dtPolyRef* nearestRef, float* nearestPt, bool* isOverPoly) const;
/// Finds polygons that overlap the search box.
/// @param[in] center The center of the search box. [(x, y, z)]
/// @param[in] extents The search distance along each axis. [(x, y, z)]
/// @param[in] halfExtents The search distance along each axis. [(x, y, z)]
/// @param[in] filter The polygon filter to apply to the query.
/// @param[out] polys The reference ids of the polygons that overlap the query box.
/// @param[out] polyCount The number of polygons in the search result.
/// @param[in] maxPolys The maximum number of polygons the search result can hold.
/// @returns The status flags for the query.
dtStatus queryPolygons(const float* center, const float* extents,
dtStatus queryPolygons(const float* center, const float* halfExtents,
const dtQueryFilter* filter,
dtPolyRef* polys, int* polyCount, const int maxPolys) const;
/// Finds polygons that overlap the search box.
/// @param[in] center The center of the search box. [(x, y, z)]
/// @param[in] extents The search distance along each axis. [(x, y, z)]
/// @param[in] halfExtents The search distance along each axis. [(x, y, z)]
/// @param[in] filter The polygon filter to apply to the query.
/// @param[in] query The query. Polygons found will be batched together and passed to this query.
dtStatus queryPolygons(const float* center, const float* extents,
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.
@@ -375,9 +389,9 @@ public:
/// @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[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.
@@ -393,7 +407,7 @@ public:
/// 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[in] options 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.
@@ -444,6 +458,7 @@ public:
/// 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] maxRadius The radius of the search circle. [Units: wu]
/// @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.
+1 -1
View File
@@ -26,7 +26,7 @@ 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.
DT_NODE_PARENT_DETACHED = 0x04 // parent of the node is not adjacent. Found using raycast.
};
static const dtNodeIndex DT_NULL_IDX = (dtNodeIndex)~0;
+1
View File
@@ -35,6 +35,7 @@ static const unsigned int DT_INVALID_PARAM = 1 << 3; // An input parameter was i
static const unsigned int DT_BUFFER_TOO_SMALL = 1 << 4; // Result buffer for the query was too small to store all results.
static const unsigned int DT_OUT_OF_NODES = 1 << 5; // Query ran out of nodes during search.
static const unsigned int DT_PARTIAL_RESULT = 1 << 6; // Query did not reach the end location, returning best guess.
static const unsigned int DT_ALREADY_OCCUPIED = 1 << 7; // A tile has already been assigned to the given x,y coordinate
// Returns true of status is success.
+23 -24
View File
@@ -203,33 +203,32 @@ void dtCalcPolyCenter(float* tc, const unsigned short* idx, int nidx, const floa
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);
const float dot00 = dtVdot2D(v0, v0);
const float dot01 = dtVdot2D(v0, v1);
const float dot02 = dtVdot2D(v0, v2);
const float dot11 = dtVdot2D(v1, v1);
const float dot12 = dtVdot2D(v1, v2);
// Compute barycentric coordinates
const float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01);
const float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
const float v = (dot00 * dot12 - dot01 * dot02) * invDenom;
// The (sloppy) epsilon is needed to allow to get height of points which
// are interpolated along the edges of the triangles.
static const float EPS = 1e-4f;
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 >= -EPS && v >= -EPS && (u+v) <= 1+EPS)
{
h = a[1] + v0[1]*u + v1[1]*v;
if (u >= 0.0f && v >= 0.0f && (u + v) <= denom) {
h = a[1] + (v0[1] * u + v1[1] * v) / denom;
return true;
}
return false;
}
@@ -342,8 +341,8 @@ void dtRandomPointInConvexPoly(const float* pts, const int npts, float* areas,
// Find sub triangle weighted by area.
const float thr = s*areasum;
float acc = 0.0f;
float u = 0.0f;
int tri = 0;
float u = 1.0f;
int tri = npts - 1;
for (int i = 2; i < npts; i++) {
const float dacc = areas[i];
if (thr >= acc && thr < (acc+dacc))
+130 -61
View File
@@ -433,8 +433,8 @@ void dtNavMesh::connectExtLinks(dtMeshTile* tile, dtMeshTile* target, int side)
float tmax = (neia[k*2+1]-va[2]) / (vb[2]-va[2]);
if (tmin > tmax)
dtSwap(tmin,tmax);
link->bmin = (unsigned char)(dtClamp(tmin, 0.0f, 1.0f)*255.0f);
link->bmax = (unsigned char)(dtClamp(tmax, 0.0f, 1.0f)*255.0f);
link->bmin = (unsigned char)roundf(dtClamp(tmin, 0.0f, 1.0f)*255.0f);
link->bmax = (unsigned char)roundf(dtClamp(tmax, 0.0f, 1.0f)*255.0f);
}
else if (dir == 2 || dir == 6)
{
@@ -442,8 +442,8 @@ void dtNavMesh::connectExtLinks(dtMeshTile* tile, dtMeshTile* target, int side)
float tmax = (neia[k*2+1]-va[0]) / (vb[0]-va[0]);
if (tmin > tmax)
dtSwap(tmin,tmax);
link->bmin = (unsigned char)(dtClamp(tmin, 0.0f, 1.0f)*255.0f);
link->bmax = (unsigned char)(dtClamp(tmax, 0.0f, 1.0f)*255.0f);
link->bmin = (unsigned char)roundf(dtClamp(tmin, 0.0f, 1.0f)*255.0f);
link->bmax = (unsigned char)roundf(dtClamp(tmax, 0.0f, 1.0f)*255.0f);
}
}
}
@@ -470,12 +470,12 @@ void dtNavMesh::connectExtOffMeshLinks(dtMeshTile* tile, dtMeshTile* target, int
if (targetPoly->firstLink == DT_NULL_LINK)
continue;
const float ext[3] = { targetCon->rad, target->header->walkableClimb, targetCon->rad };
const float halfExtents[3] = { targetCon->rad, target->header->walkableClimb, targetCon->rad };
// Find polygon to connect to.
const float* p = &targetCon->pos[3];
float nearestPt[3];
dtPolyRef ref = findNearestPolyInTile(tile, p, ext, nearestPt);
dtPolyRef ref = findNearestPolyInTile(tile, p, halfExtents, nearestPt);
if (!ref)
continue;
// findNearestPoly may return too optimistic results, further check to make sure.
@@ -570,12 +570,12 @@ void dtNavMesh::baseOffMeshLinks(dtMeshTile* tile)
dtOffMeshConnection* con = &tile->offMeshCons[i];
dtPoly* poly = &tile->polys[con->poly];
const float ext[3] = { con->rad, tile->header->walkableClimb, con->rad };
const float halfExtents[3] = { con->rad, tile->header->walkableClimb, con->rad };
// Find polygon to connect to.
const float* p = &con->pos[0]; // First vertex
float nearestPt[3];
dtPolyRef ref = findNearestPolyInTile(tile, p, ext, nearestPt);
dtPolyRef ref = findNearestPolyInTile(tile, p, halfExtents, nearestPt);
if (!ref) continue;
// findNearestPoly may return too optimistic results, further check to make sure.
if (dtSqr(nearestPt[0]-p[0])+dtSqr(nearestPt[2]-p[2]) > dtSqr(con->rad))
@@ -616,63 +616,84 @@ void dtNavMesh::baseOffMeshLinks(dtMeshTile* tile)
}
}
void dtNavMesh::closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const
namespace
{
const dtMeshTile* tile = 0;
const dtPoly* poly = 0;
getTileAndPolyByRefUnsafe(ref, &tile, &poly);
// Off-mesh connections don't have detail polygons.
if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
template<bool onlyBoundary>
void closestPointOnDetailEdges(const dtMeshTile* tile, const dtPoly* poly, const float* pos, float* closest)
{
const float* v0 = &tile->verts[poly->verts[0]*3];
const float* v1 = &tile->verts[poly->verts[1]*3];
const float d0 = dtVdist(pos, v0);
const float d1 = dtVdist(pos, v1);
const float u = d0 / (d0+d1);
dtVlerp(closest, v0, v1, u);
if (posOverPoly)
*posOverPoly = false;
return;
const unsigned int ip = (unsigned int)(poly - tile->polys);
const dtPolyDetail* pd = &tile->detailMeshes[ip];
float dmin = FLT_MAX;
float tmin = 0;
const float* pmin = 0;
const float* pmax = 0;
for (int i = 0; i < pd->triCount; i++)
{
const unsigned char* tris = &tile->detailTris[(pd->triBase + i) * 4];
const int ANY_BOUNDARY_EDGE =
(DT_DETAIL_EDGE_BOUNDARY << 0) |
(DT_DETAIL_EDGE_BOUNDARY << 2) |
(DT_DETAIL_EDGE_BOUNDARY << 4);
if (onlyBoundary && (tris[3] & ANY_BOUNDARY_EDGE) == 0)
continue;
const float* v[3];
for (int j = 0; j < 3; ++j)
{
if (tris[j] < poly->vertCount)
v[j] = &tile->verts[poly->verts[tris[j]] * 3];
else
v[j] = &tile->detailVerts[(pd->vertBase + (tris[j] - poly->vertCount)) * 3];
}
for (int k = 0, j = 2; k < 3; j = k++)
{
if ((dtGetDetailTriEdgeFlags(tris[3], j) & DT_DETAIL_EDGE_BOUNDARY) == 0 &&
(onlyBoundary || tris[j] < tris[k]))
{
// Only looking at boundary edges and this is internal, or
// this is an inner edge that we will see again or have already seen.
continue;
}
float t;
float d = dtDistancePtSegSqr2D(pos, v[j], v[k], t);
if (d < dmin)
{
dmin = d;
tmin = t;
pmin = v[j];
pmax = v[k];
}
}
}
dtVlerp(closest, pmin, pmax, tmin);
}
}
bool dtNavMesh::getPolyHeight(const dtMeshTile* tile, const dtPoly* poly, const float* pos, float* height) const
{
// Off-mesh connections do not have detail polys and getting height
// over them does not make sense.
if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
return false;
const unsigned int ip = (unsigned int)(poly - tile->polys);
const dtPolyDetail* pd = &tile->detailMeshes[ip];
// Clamp point to be inside the polygon.
float verts[DT_VERTS_PER_POLYGON*3];
float edged[DT_VERTS_PER_POLYGON];
float edget[DT_VERTS_PER_POLYGON];
const int nv = poly->vertCount;
for (int i = 0; i < nv; ++i)
dtVcopy(&verts[i*3], &tile->verts[poly->verts[i]*3]);
dtVcopy(closest, pos);
if (!dtDistancePtPolyEdgesSqr(pos, verts, nv, edged, edget))
{
// Point is outside the polygon, dtClamp to nearest edge.
float dmin = edged[0];
int imin = 0;
for (int i = 1; i < nv; ++i)
{
if (edged[i] < dmin)
{
dmin = edged[i];
imin = i;
}
}
const float* va = &verts[imin*3];
const float* vb = &verts[((imin+1)%nv)*3];
dtVlerp(closest, va, vb, edget[imin]);
if (posOverPoly)
*posOverPoly = false;
}
else
{
if (posOverPoly)
*posOverPoly = true;
}
if (!dtPointInPolygon(pos, verts, nv))
return false;
if (!height)
return true;
// Find height at the location.
for (int j = 0; j < pd->triCount; ++j)
@@ -687,21 +708,62 @@ void dtNavMesh::closestPointOnPoly(dtPolyRef ref, const float* pos, float* close
v[k] = &tile->detailVerts[(pd->vertBase+(t[k]-poly->vertCount))*3];
}
float h;
if (dtClosestHeightPointTriangle(closest, v[0], v[1], v[2], h))
if (dtClosestHeightPointTriangle(pos, v[0], v[1], v[2], h))
{
closest[1] = h;
break;
*height = h;
return true;
}
}
// If all triangle checks failed above (can happen with degenerate triangles
// or larger floating point values) the point is on an edge, so just select
// closest. This should almost never happen so the extra iteration here is
// ok.
float closest[3];
closestPointOnDetailEdges<false>(tile, poly, pos, closest);
*height = closest[1];
return true;
}
void dtNavMesh::closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const
{
const dtMeshTile* tile = 0;
const dtPoly* poly = 0;
getTileAndPolyByRefUnsafe(ref, &tile, &poly);
dtVcopy(closest, pos);
if (getPolyHeight(tile, poly, pos, &closest[1]))
{
if (posOverPoly)
*posOverPoly = true;
return;
}
if (posOverPoly)
*posOverPoly = false;
// Off-mesh connections don't have detail polygons.
if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
{
const float* v0 = &tile->verts[poly->verts[0]*3];
const float* v1 = &tile->verts[poly->verts[1]*3];
float t;
dtDistancePtSegSqr2D(pos, v0, v1, t);
dtVlerp(closest, v0, v1, t);
return;
}
// Outside poly that is not an offmesh connection.
closestPointOnDetailEdges<true>(tile, poly, pos, closest);
}
dtPolyRef dtNavMesh::findNearestPolyInTile(const dtMeshTile* tile,
const float* center, const float* extents,
const float* center, const float* halfExtents,
float* nearestPt) const
{
float bmin[3], bmax[3];
dtVsub(bmin, center, extents);
dtVadd(bmax, center, extents);
dtVsub(bmin, center, halfExtents);
dtVadd(bmax, center, halfExtents);
// Get nearby polygons from proximity grid.
dtPolyRef polys[128];
@@ -852,10 +914,17 @@ dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags,
return DT_FAILURE | DT_WRONG_MAGIC;
if (header->version != DT_NAVMESH_VERSION)
return DT_FAILURE | DT_WRONG_VERSION;
#ifndef DT_POLYREF64
// Do not allow adding more polygons than specified in the NavMesh's maxPolys constraint.
// Otherwise, the poly ID cannot be represented with the given number of bits.
if (m_polyBits < dtIlog2(dtNextPow2((unsigned int)header->polyCount)))
return DT_FAILURE | DT_INVALID_PARAM;
#endif
// Make sure the location is free.
if (getTileAt(header->x, header->y, header->layer))
return DT_FAILURE;
return DT_FAILURE | DT_ALREADY_OCCUPIED;
// Allocate a tile.
dtMeshTile* tile = 0;
+198 -172
View File
@@ -117,6 +117,11 @@ void dtFreeNavMeshQuery(dtNavMeshQuery* navmesh)
dtFree(navmesh);
}
dtPolyQuery::~dtPolyQuery()
{
// Defined out of line to fix the weak v-tables warning
}
//////////////////////////////////////////////////////////////////////////////////////////
/// @class dtNavMeshQuery
@@ -222,7 +227,10 @@ dtStatus dtNavMeshQuery::findRandomPoint(const dtQueryFilter* filter, float (*fr
dtPolyRef* randomRef, float* randomPt) const
{
dtAssert(m_nav);
if (!filter || !frand || !randomRef || !randomPt)
return DT_FAILURE | DT_INVALID_PARAM;
// Randomly pick one tile. Assume that all tiles cover roughly the same area.
const dtMeshTile* tile = 0;
float tsum = 0.0f;
@@ -298,11 +306,7 @@ dtStatus dtNavMeshQuery::findRandomPoint(const dtQueryFilter* filter, float (*fr
float pt[3];
dtRandomPointInConvexPoly(verts, poly->vertCount, areas, s, t, pt);
float h = 0.0f;
dtStatus status = getPolyHeight(polyRef, pt, &h);
if (dtStatusFailed(status))
return status;
pt[1] = h;
closestPointOnPoly(polyRef, pt, pt, NULL);
dtVcopy(randomPt, pt);
*randomRef = polyRef;
@@ -319,8 +323,13 @@ dtStatus dtNavMeshQuery::findRandomPointAroundCircle(dtPolyRef startRef, const f
dtAssert(m_openList);
// Validate input
if (!startRef || !m_nav->isValidPolyRef(startRef))
if (!m_nav->isValidPolyRef(startRef) ||
!centerPos || !dtVisfinite(centerPos) ||
maxRadius < 0 || !dtMathIsfinite(maxRadius) ||
!filter || !frand || !randomRef || !randomPt)
{
return DT_FAILURE | DT_INVALID_PARAM;
}
const dtMeshTile* startTile = 0;
const dtPoly* startPoly = 0;
@@ -480,16 +489,12 @@ dtStatus dtNavMeshQuery::findRandomPointAroundCircle(dtPolyRef startRef, const f
float pt[3];
dtRandomPointInConvexPoly(verts, randomPoly->vertCount, areas, s, t, pt);
float h = 0.0f;
dtStatus stat = getPolyHeight(randomPolyRef, pt, &h);
if (dtStatusFailed(status))
return stat;
pt[1] = h;
closestPointOnPoly(randomPolyRef, pt, pt, NULL);
dtVcopy(randomPt, pt);
*randomRef = randomPolyRef;
return DT_SUCCESS;
return status;
}
@@ -506,85 +511,14 @@ dtStatus dtNavMeshQuery::findRandomPointAroundCircle(dtPolyRef startRef, const f
dtStatus dtNavMeshQuery::closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const
{
dtAssert(m_nav);
const dtMeshTile* tile = 0;
const dtPoly* poly = 0;
if (dtStatusFailed(m_nav->getTileAndPolyByRef(ref, &tile, &poly)))
if (!m_nav->isValidPolyRef(ref) ||
!pos || !dtVisfinite(pos) ||
!closest)
{
return DT_FAILURE | DT_INVALID_PARAM;
if (!tile)
return DT_FAILURE | DT_INVALID_PARAM;
// Off-mesh connections don't have detail polygons.
if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
{
const float* v0 = &tile->verts[poly->verts[0]*3];
const float* v1 = &tile->verts[poly->verts[1]*3];
const float d0 = dtVdist(pos, v0);
const float d1 = dtVdist(pos, v1);
const float u = d0 / (d0+d1);
dtVlerp(closest, v0, v1, u);
if (posOverPoly)
*posOverPoly = false;
return DT_SUCCESS;
}
const unsigned int ip = (unsigned int)(poly - tile->polys);
const dtPolyDetail* pd = &tile->detailMeshes[ip];
// Clamp point to be inside the polygon.
float verts[DT_VERTS_PER_POLYGON*3];
float edged[DT_VERTS_PER_POLYGON];
float edget[DT_VERTS_PER_POLYGON];
const int nv = poly->vertCount;
for (int i = 0; i < nv; ++i)
dtVcopy(&verts[i*3], &tile->verts[poly->verts[i]*3]);
dtVcopy(closest, pos);
if (!dtDistancePtPolyEdgesSqr(pos, verts, nv, edged, edget))
{
// Point is outside the polygon, dtClamp to nearest edge.
float dmin = edged[0];
int imin = 0;
for (int i = 1; i < nv; ++i)
{
if (edged[i] < dmin)
{
dmin = edged[i];
imin = i;
}
}
const float* va = &verts[imin*3];
const float* vb = &verts[((imin+1)%nv)*3];
dtVlerp(closest, va, vb, edget[imin]);
if (posOverPoly)
*posOverPoly = false;
}
else
{
if (posOverPoly)
*posOverPoly = true;
}
// Find height at the location.
for (int j = 0; j < pd->triCount; ++j)
{
const unsigned char* t = &tile->detailTris[(pd->triBase+j)*4];
const float* v[3];
for (int k = 0; k < 3; ++k)
{
if (t[k] < poly->vertCount)
v[k] = &tile->verts[poly->verts[t[k]]*3];
else
v[k] = &tile->detailVerts[(pd->vertBase+(t[k]-poly->vertCount))*3];
}
float h;
if (dtClosestHeightPointTriangle(closest, v[0], v[1], v[2], h))
{
closest[1] = h;
break;
}
}
m_nav->closestPointOnPoly(ref, pos, closest, posOverPoly);
return DT_SUCCESS;
}
@@ -607,6 +541,9 @@ dtStatus dtNavMeshQuery::closestPointOnPolyBoundary(dtPolyRef ref, const float*
const dtPoly* poly = 0;
if (dtStatusFailed(m_nav->getTileAndPolyByRef(ref, &tile, &poly)))
return DT_FAILURE | DT_INVALID_PARAM;
if (!pos || !dtVisfinite(pos) || !closest)
return DT_FAILURE | DT_INVALID_PARAM;
// Collect vertices.
float verts[DT_VERTS_PER_POLYGON*3];
@@ -648,7 +585,7 @@ dtStatus dtNavMeshQuery::closestPointOnPolyBoundary(dtPolyRef ref, const float*
/// @par
///
/// Will return #DT_FAILURE if the provided position is outside the xz-bounds
/// Will return #DT_FAILURE | DT_INVALID_PARAM if the provided position is outside the xz-bounds
/// of the polygon.
///
dtStatus dtNavMeshQuery::getPolyHeight(dtPolyRef ref, const float* pos, float* height) const
@@ -659,44 +596,28 @@ dtStatus dtNavMeshQuery::getPolyHeight(dtPolyRef ref, const float* pos, float* h
const dtPoly* poly = 0;
if (dtStatusFailed(m_nav->getTileAndPolyByRef(ref, &tile, &poly)))
return DT_FAILURE | DT_INVALID_PARAM;
if (!pos || !dtVisfinite2D(pos))
return DT_FAILURE | DT_INVALID_PARAM;
// We used to return success for offmesh connections, but the
// getPolyHeight in DetourNavMesh does not do this, so special
// case it here.
if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
{
const float* v0 = &tile->verts[poly->verts[0]*3];
const float* v1 = &tile->verts[poly->verts[1]*3];
const float d0 = dtVdist2D(pos, v0);
const float d1 = dtVdist2D(pos, v1);
const float u = d0 / (d0+d1);
float t;
dtDistancePtSegSqr2D(pos, v0, v1, t);
if (height)
*height = v0[1] + (v1[1] - v0[1]) * u;
*height = v0[1] + (v1[1] - v0[1])*t;
return DT_SUCCESS;
}
else
{
const unsigned int ip = (unsigned int)(poly - tile->polys);
const dtPolyDetail* pd = &tile->detailMeshes[ip];
for (int j = 0; j < pd->triCount; ++j)
{
const unsigned char* t = &tile->detailTris[(pd->triBase+j)*4];
const float* v[3];
for (int k = 0; k < 3; ++k)
{
if (t[k] < poly->vertCount)
v[k] = &tile->verts[poly->verts[t[k]]*3];
else
v[k] = &tile->detailVerts[(pd->vertBase+(t[k]-poly->vertCount))*3];
}
float h;
if (dtClosestHeightPointTriangle(pos, v[0], v[1], v[2], h))
{
if (height)
*height = h;
return DT_SUCCESS;
}
}
}
return DT_FAILURE | DT_INVALID_PARAM;
return m_nav->getPolyHeight(tile, poly, pos, height)
? DT_SUCCESS
: DT_FAILURE | DT_INVALID_PARAM;
}
class dtFindNearestPolyQuery : public dtPolyQuery
@@ -706,15 +627,19 @@ class dtFindNearestPolyQuery : public dtPolyQuery
float m_nearestDistanceSqr;
dtPolyRef m_nearestRef;
float m_nearestPoint[3];
bool m_overPoly;
public:
dtFindNearestPolyQuery(const dtNavMeshQuery* query, const float* center)
: m_query(query), m_center(center), m_nearestDistanceSqr(FLT_MAX), m_nearestRef(0), m_nearestPoint()
: m_query(query), m_center(center), m_nearestDistanceSqr(FLT_MAX), m_nearestRef(0), m_nearestPoint(), m_overPoly(false)
{
}
virtual ~dtFindNearestPolyQuery();
dtPolyRef nearestRef() const { return m_nearestRef; }
const float* nearestPoint() const { return m_nearestPoint; }
bool isOverPoly() const { return m_overPoly; }
void process(const dtMeshTile* tile, dtPoly** polys, dtPolyRef* refs, int count)
{
@@ -748,29 +673,46 @@ public:
m_nearestDistanceSqr = d;
m_nearestRef = ref;
m_overPoly = posOverPoly;
}
}
}
};
dtFindNearestPolyQuery::~dtFindNearestPolyQuery()
{
// Defined out of line to fix the weak v-tables warning
}
/// @par
///
/// @note If the search box does not intersect any polygons the search will
/// return #DT_SUCCESS, but @p nearestRef will be zero. So if in doubt, check
/// @p nearestRef before using @p nearestPt.
///
dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* extents,
dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* halfExtents,
const dtQueryFilter* filter,
dtPolyRef* nearestRef, float* nearestPt) const
{
return findNearestPoly(center, halfExtents, filter, nearestRef, nearestPt, NULL);
}
// If center and nearestPt point to an equal position, isOverPoly will be true;
// however there's also a special case of climb height inside the polygon (see dtFindNearestPolyQuery)
dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* halfExtents,
const dtQueryFilter* filter,
dtPolyRef* nearestRef, float* nearestPt, bool* isOverPoly) const
{
dtAssert(m_nav);
if (!nearestRef)
return DT_FAILURE | DT_INVALID_PARAM;
// queryPolygons below will check rest of params
dtFindNearestPolyQuery query(this, center);
dtStatus status = queryPolygons(center, extents, filter, &query);
dtStatus status = queryPolygons(center, halfExtents, filter, &query);
if (dtStatusFailed(status))
return status;
@@ -778,7 +720,11 @@ dtStatus dtNavMeshQuery::findNearestPoly(const float* center, const float* exten
// Only override nearestPt if we actually found a poly so the nearest point
// is valid.
if (nearestPt && *nearestRef)
{
dtVcopy(nearestPt, query.nearestPoint());
if (isOverPoly)
*isOverPoly = query.isOverPoly();
}
return DT_SUCCESS;
}
@@ -913,6 +859,8 @@ public:
{
}
virtual ~dtCollectPolysQuery();
int numCollected() const { return m_numCollected; }
bool overflowed() const { return m_overflow; }
@@ -934,6 +882,11 @@ public:
}
};
dtCollectPolysQuery::~dtCollectPolysQuery()
{
// Defined out of line to fix the weak v-tables warning
}
/// @par
///
/// If no polygons are found, the function will return #DT_SUCCESS with a
@@ -943,7 +896,7 @@ public:
/// be filled to capacity. The method of choosing which polygons from the
/// full set are included in the partial result set is undefined.
///
dtStatus dtNavMeshQuery::queryPolygons(const float* center, const float* extents,
dtStatus dtNavMeshQuery::queryPolygons(const float* center, const float* halfExtents,
const dtQueryFilter* filter,
dtPolyRef* polys, int* polyCount, const int maxPolys) const
{
@@ -952,7 +905,7 @@ dtStatus dtNavMeshQuery::queryPolygons(const float* center, const float* extents
dtCollectPolysQuery collector(polys, maxPolys);
dtStatus status = queryPolygons(center, extents, filter, &collector);
dtStatus status = queryPolygons(center, halfExtents, filter, &collector);
if (dtStatusFailed(status))
return status;
@@ -963,21 +916,25 @@ dtStatus dtNavMeshQuery::queryPolygons(const float* center, const float* extents
/// @par
///
/// The query will be invoked with batches of polygons. Polygons passed
/// to the query have bounding boxes that overlap with the center and extents
/// to the query have bounding boxes that overlap with the center and halfExtents
/// passed to this function. The dtPolyQuery::process function is invoked multiple
/// times until all overlapping polygons have been processed.
///
dtStatus dtNavMeshQuery::queryPolygons(const float* center, const float* extents,
dtStatus dtNavMeshQuery::queryPolygons(const float* center, const float* halfExtents,
const dtQueryFilter* filter, dtPolyQuery* query) const
{
dtAssert(m_nav);
if (!center || !extents || !filter || !query)
if (!center || !dtVisfinite(center) ||
!halfExtents || !dtVisfinite(halfExtents) ||
!filter || !query)
{
return DT_FAILURE | DT_INVALID_PARAM;
}
float bmin[3], bmax[3];
dtVsub(bmin, center, extents);
dtVadd(bmax, center, extents);
dtVsub(bmin, center, halfExtents);
dtVadd(bmax, center, halfExtents);
// Find tiles the query touches.
int minx, miny, maxx, maxy;
@@ -1021,14 +978,20 @@ dtStatus dtNavMeshQuery::findPath(dtPolyRef startRef, dtPolyRef endRef,
dtAssert(m_nav);
dtAssert(m_nodePool);
dtAssert(m_openList);
if (pathCount)
*pathCount = 0;
if (!pathCount)
return DT_FAILURE | DT_INVALID_PARAM;
*pathCount = 0;
// Validate input
if (!m_nav->isValidPolyRef(startRef) || !m_nav->isValidPolyRef(endRef) ||
!startPos || !endPos || !filter || maxPath <= 0 || !path || !pathCount)
!startPos || !dtVisfinite(startPos) ||
!endPos || !dtVisfinite(endPos) ||
!filter || !path || maxPath <= 0)
{
return DT_FAILURE | DT_INVALID_PARAM;
}
if (startRef == endRef)
{
@@ -1263,18 +1226,21 @@ dtStatus dtNavMeshQuery::initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef
m_query.status = DT_FAILURE;
m_query.startRef = startRef;
m_query.endRef = endRef;
dtVcopy(m_query.startPos, startPos);
dtVcopy(m_query.endPos, endPos);
if (startPos)
dtVcopy(m_query.startPos, startPos);
if (endPos)
dtVcopy(m_query.endPos, endPos);
m_query.filter = filter;
m_query.options = options;
m_query.raycastLimitSqr = FLT_MAX;
if (!startRef || !endRef)
return DT_FAILURE | DT_INVALID_PARAM;
// Validate input
if (!m_nav->isValidPolyRef(startRef) || !m_nav->isValidPolyRef(endRef))
if (!m_nav->isValidPolyRef(startRef) || !m_nav->isValidPolyRef(endRef) ||
!startPos || !dtVisfinite(startPos) ||
!endPos || !dtVisfinite(endPos) || !filter)
{
return DT_FAILURE | DT_INVALID_PARAM;
}
// trade quality with performance?
if (options & DT_FINDPATH_ANY_ANGLE)
@@ -1530,7 +1496,13 @@ dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter, int* doneIters)
dtStatus dtNavMeshQuery::finalizeSlicedFindPath(dtPolyRef* path, int* pathCount, const int maxPath)
{
if (!pathCount)
return DT_FAILURE | DT_INVALID_PARAM;
*pathCount = 0;
if (!path || maxPath <= 0)
return DT_FAILURE | DT_INVALID_PARAM;
if (dtStatusFailed(m_query.status))
{
@@ -1615,12 +1587,13 @@ dtStatus dtNavMeshQuery::finalizeSlicedFindPath(dtPolyRef* path, int* pathCount,
dtStatus dtNavMeshQuery::finalizeSlicedFindPathPartial(const dtPolyRef* existing, const int existingSize,
dtPolyRef* path, int* pathCount, const int maxPath)
{
if (!pathCount)
return DT_FAILURE | DT_INVALID_PARAM;
*pathCount = 0;
if (existingSize == 0)
{
return DT_FAILURE;
}
if (!existing || existingSize <= 0 || !path || !pathCount || maxPath <= 0)
return DT_FAILURE | DT_INVALID_PARAM;
if (dtStatusFailed(m_query.status))
{
@@ -1823,14 +1796,19 @@ dtStatus dtNavMeshQuery::findStraightPath(const float* startPos, const float* en
int* straightPathCount, const int maxStraightPath, const int options) const
{
dtAssert(m_nav);
if (!straightPathCount)
return DT_FAILURE | DT_INVALID_PARAM;
*straightPathCount = 0;
if (!maxStraightPath)
return DT_FAILURE | DT_INVALID_PARAM;
if (!path[0])
if (!startPos || !dtVisfinite(startPos) ||
!endPos || !dtVisfinite(endPos) ||
!path || pathSize <= 0 || !path[0] ||
maxStraightPath <= 0)
{
return DT_FAILURE | DT_INVALID_PARAM;
}
dtStatus stat = 0;
@@ -2070,13 +2048,19 @@ dtStatus dtNavMeshQuery::moveAlongSurface(dtPolyRef startRef, const float* start
dtAssert(m_nav);
dtAssert(m_tinyNodePool);
if (!visitedCount)
return DT_FAILURE | DT_INVALID_PARAM;
*visitedCount = 0;
// Validate input
if (!startRef)
return DT_FAILURE | DT_INVALID_PARAM;
if (!m_nav->isValidPolyRef(startRef))
if (!m_nav->isValidPolyRef(startRef) ||
!startPos || !dtVisfinite(startPos) ||
!endPos || !dtVisfinite(endPos) ||
!filter || !resultPos || !visited ||
maxVisitedSize <= 0)
{
return DT_FAILURE | DT_INVALID_PARAM;
}
dtStatus status = DT_SUCCESS;
@@ -2484,16 +2468,23 @@ dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, cons
dtRaycastHit* hit, dtPolyRef prevRef) const
{
dtAssert(m_nav);
if (!hit)
return DT_FAILURE | DT_INVALID_PARAM;
hit->t = 0;
hit->pathCount = 0;
hit->pathCost = 0;
// Validate input
if (!startRef || !m_nav->isValidPolyRef(startRef))
return DT_FAILURE | DT_INVALID_PARAM;
if (prevRef && !m_nav->isValidPolyRef(prevRef))
if (!m_nav->isValidPolyRef(startRef) ||
!startPos || !dtVisfinite(startPos) ||
!endPos || !dtVisfinite(endPos) ||
!filter ||
(prevRef && !m_nav->isValidPolyRef(prevRef)))
{
return DT_FAILURE | DT_INVALID_PARAM;
}
float dir[3], curPos[3], lastPos[3];
float verts[DT_VERTS_PER_POLYGON*3+3];
@@ -2735,11 +2726,18 @@ dtStatus dtNavMeshQuery::findPolysAroundCircle(dtPolyRef startRef, const float*
dtAssert(m_nodePool);
dtAssert(m_openList);
*resultCount = 0;
// Validate input
if (!startRef || !m_nav->isValidPolyRef(startRef))
if (!resultCount)
return DT_FAILURE | DT_INVALID_PARAM;
*resultCount = 0;
if (!m_nav->isValidPolyRef(startRef) ||
!centerPos || !dtVisfinite(centerPos) ||
radius < 0 || !dtMathIsfinite(radius) ||
!filter || maxResult < 0)
{
return DT_FAILURE | DT_INVALID_PARAM;
}
m_nodePool->clear();
m_openList->clear();
@@ -2901,8 +2899,18 @@ dtStatus dtNavMeshQuery::findPolysAroundShape(dtPolyRef startRef, const float* v
dtAssert(m_nav);
dtAssert(m_nodePool);
dtAssert(m_openList);
if (!resultCount)
return DT_FAILURE | DT_INVALID_PARAM;
*resultCount = 0;
if (!m_nav->isValidPolyRef(startRef) ||
!verts || nverts < 3 ||
!filter || maxResult < 0)
{
return DT_FAILURE | DT_INVALID_PARAM;
}
// Validate input
if (!startRef || !m_nav->isValidPolyRef(startRef))
@@ -3088,13 +3096,20 @@ dtStatus dtNavMeshQuery::findLocalNeighbourhood(dtPolyRef startRef, const float*
{
dtAssert(m_nav);
dtAssert(m_tinyNodePool);
if (!resultCount)
return DT_FAILURE | DT_INVALID_PARAM;
*resultCount = 0;
// Validate input
if (!startRef || !m_nav->isValidPolyRef(startRef))
if (!m_nav->isValidPolyRef(startRef) ||
!centerPos || !dtVisfinite(centerPos) ||
radius < 0 || !dtMathIsfinite(radius) ||
!filter || maxResult < 0)
{
return DT_FAILURE | DT_INVALID_PARAM;
}
static const int MAX_STACK = 48;
dtNode* stack[MAX_STACK];
int nstack = 0;
@@ -3301,13 +3316,19 @@ dtStatus dtNavMeshQuery::getPolyWallSegments(dtPolyRef ref, const dtQueryFilter*
const int maxSegments) const
{
dtAssert(m_nav);
if (!segmentCount)
return DT_FAILURE | DT_INVALID_PARAM;
*segmentCount = 0;
const dtMeshTile* tile = 0;
const dtPoly* poly = 0;
if (dtStatusFailed(m_nav->getTileAndPolyByRef(ref, &tile, &poly)))
return DT_FAILURE | DT_INVALID_PARAM;
if (!filter || !segmentVerts || maxSegments < 0)
return DT_FAILURE | DT_INVALID_PARAM;
int n = 0;
static const int MAX_INTERVAL = 16;
@@ -3455,8 +3476,13 @@ dtStatus dtNavMeshQuery::findDistanceToWall(dtPolyRef startRef, const float* cen
dtAssert(m_openList);
// Validate input
if (!startRef || !m_nav->isValidPolyRef(startRef))
if (!m_nav->isValidPolyRef(startRef) ||
!centerPos || !dtVisfinite(centerPos) ||
maxRadius < 0 || !dtMathIsfinite(maxRadius) ||
!filter || !hitDist || !hitPos || !hitNormal)
{
return DT_FAILURE | DT_INVALID_PARAM;
}
m_nodePool->clear();
m_openList->clear();
+37
View File
@@ -0,0 +1,37 @@
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 ${LIB_VERSION}
COMPILE_PDB_OUTPUT_DIRECTORY .
COMPILE_PDB_NAME "DetourCrowd-d"
)
install(TARGETS DetourCrowd
EXPORT recastnavigation-targets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT library
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation
)
file(GLOB INCLUDES Include/*.h)
install(FILES ${INCLUDES} DESTINATION
${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation)
if(MSVC)
install(FILES "$<TARGET_FILE_DIR:DetourCrowd>/DetourCrowd-d.pdb" CONFIGURATIONS "Debug" DESTINATION "lib")
endif()
+11 -7
View File
@@ -66,7 +66,7 @@ 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.
DT_CROWDAGENT_STATE_OFFMESH ///< The agent is traversing an off-mesh connection.
};
/// Configuration parameters for a crowd agent.
@@ -108,7 +108,7 @@ enum MoveRequestState
DT_CROWDAGENT_TARGET_REQUESTING,
DT_CROWDAGENT_TARGET_WAITING_FOR_QUEUE,
DT_CROWDAGENT_TARGET_WAITING_FOR_PATH,
DT_CROWDAGENT_TARGET_VELOCITY,
DT_CROWDAGENT_TARGET_VELOCITY
};
/// Represents an agent managed by a #dtCrowd object.
@@ -188,7 +188,7 @@ enum UpdateFlags
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.
DT_CROWD_OPTIMIZE_TOPO = 16 ///< Use dtPathCorridor::optimizePathTopology() to optimize the agent path.
};
struct dtCrowdAgentDebugInfo
@@ -217,7 +217,7 @@ class dtCrowd
dtPolyRef* m_pathResult;
int m_maxPathResult;
float m_ext[3];
float m_agentPlacementHalfExtents[3];
dtQueryFilter m_filters[DT_CROWD_MAX_QUERY_FILTER_TYPE];
@@ -325,9 +325,13 @@ public:
/// @return The filter used by the crowd.
inline dtQueryFilter* getEditableFilter(const int i) { return (i >= 0 && i < DT_CROWD_MAX_QUERY_FILTER_TYPE) ? &m_filters[i] : 0; }
/// Gets the search extents [(x, y, z)] used by the crowd for query operations.
/// @return The search extents used by the crowd. [(x, y, z)]
const float* getQueryExtents() const { return m_ext; }
/// Gets the search halfExtents [(x, y, z)] used by the crowd for query operations.
/// @return The search halfExtents used by the crowd. [(x, y, z)]
const float* getQueryHalfExtents() const { return m_agentPlacementHalfExtents; }
/// Same as getQueryHalfExtents. Left to maintain backwards compatibility.
/// @return The search halfExtents used by the crowd. [(x, y, z)]
const float* getQueryExtents() const { return m_agentPlacementHalfExtents; }
/// Gets the velocity sample count.
/// @return The velocity sample count.
+10 -8
View File
@@ -16,7 +16,6 @@
// 3. This notice may not be removed or altered from any source distribution.
//
#define _USE_MATH_DEFINES
#include <string.h>
#include <float.h>
#include <stdlib.h>
@@ -386,7 +385,8 @@ bool dtCrowd::init(const int maxAgents, const float maxAgentRadius, dtNavMesh* n
m_maxAgents = maxAgents;
m_maxAgentRadius = maxAgentRadius;
dtVset(m_ext, m_maxAgentRadius*2.0f,m_maxAgentRadius*1.5f,m_maxAgentRadius*2.0f);
// Larger than agent radius because it is also used for agent recovery.
dtVset(m_agentPlacementHalfExtents, m_maxAgentRadius*2.0f, m_maxAgentRadius*1.5f, m_maxAgentRadius*2.0f);
m_grid = dtAllocProximityGrid();
if (!m_grid)
@@ -531,7 +531,7 @@ int dtCrowd::addAgent(const float* pos, const dtCrowdAgentParams* params)
float nearest[3];
dtPolyRef ref = 0;
dtVcopy(nearest, pos);
dtStatus status = m_navquery->findNearestPoly(pos, m_ext, &m_filters[ag->params.queryFilterType], &ref, nearest);
dtStatus status = m_navquery->findNearestPoly(pos, m_agentPlacementHalfExtents, &m_filters[ag->params.queryFilterType], &ref, nearest);
if (dtStatusFailed(status))
{
dtVcopy(nearest, pos);
@@ -965,7 +965,7 @@ void dtCrowd::checkPathValidity(dtCrowdAgent** agents, const int nagents, const
float nearest[3];
dtVcopy(nearest, agentPos);
agentRef = 0;
m_navquery->findNearestPoly(ag->npos, m_ext, &m_filters[ag->params.queryFilterType], &agentRef, nearest);
m_navquery->findNearestPoly(ag->npos, m_agentPlacementHalfExtents, &m_filters[ag->params.queryFilterType], &agentRef, nearest);
dtVcopy(agentPos, nearest);
if (!agentRef)
@@ -1001,7 +1001,7 @@ void dtCrowd::checkPathValidity(dtCrowdAgent** agents, const int nagents, const
float nearest[3];
dtVcopy(nearest, ag->targetPos);
ag->targetRef = 0;
m_navquery->findNearestPoly(ag->targetPos, m_ext, &m_filters[ag->params.queryFilterType], &ag->targetRef, nearest);
m_navquery->findNearestPoly(ag->targetPos, m_agentPlacementHalfExtents, &m_filters[ag->params.queryFilterType], &ag->targetRef, nearest);
dtVcopy(ag->targetPos, nearest);
replan = true;
}
@@ -1408,12 +1408,14 @@ void dtCrowd::update(const float dt, dtCrowdAgentDebugInfo* debug)
}
// Update agents using off-mesh connection.
for (int i = 0; i < m_maxAgents; ++i)
for (int i = 0; i < nagents; ++i)
{
dtCrowdAgentAnimation* anim = &m_agentAnims[i];
dtCrowdAgent* ag = agents[i];
const int idx = (int)(ag - m_agents);
dtCrowdAgentAnimation* anim = &m_agentAnims[idx];
if (!anim->active)
continue;
dtCrowdAgent* ag = agents[i];
anim->t += dt;
if (anim->t > anim->tmax)
+2 -2
View File
@@ -431,7 +431,7 @@ Behavior:
- The new position will be located in the adjusted corridor's first polygon.
The expected use case is that the desired position will be 'near' the current corridor. What is considered 'near'
depends on local polygon density, query search extents, etc.
depends on local polygon density, query search half extents, etc.
The resulting position will differ from the desired position if the desired position is not on the navigation mesh,
or it can't be reached using a local search.
@@ -470,7 +470,7 @@ Behavior:
- The corridor is automatically adjusted (shorted or lengthened) in order to remain valid.
- The new target will be located in the adjusted corridor's last polygon.
The expected use case is that the desired target will be 'near' the current corridor. What is considered 'near' depends on local polygon density, query search extents, etc.
The expected use case is that the desired target will be 'near' the current corridor. What is considered 'near' depends on local polygon density, query search half extents, etc.
The resulting target will differ from the desired target if the desired target is not on the navigation mesh, or it can't be reached using a local search.
*/
+38
View File
@@ -0,0 +1,38 @@
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 ${LIB_VERSION}
COMPILE_PDB_OUTPUT_DIRECTORY .
COMPILE_PDB_NAME "DetourTileCache-d"
)
install(TARGETS DetourTileCache
EXPORT recastnavigation-targets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT library
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation
)
file(GLOB INCLUDES Include/*.h)
install(FILES ${INCLUDES} DESTINATION
${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation)
if(MSVC)
install(FILES "$<TARGET_FILE_DIR:DetourTileCache>/DetourTileCache-d.pdb" CONFIGURATIONS "Debug" DESTINATION "lib")
endif()
+21 -12
View File
@@ -3,16 +3,13 @@
#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.
DT_COMPRESSEDTILE_FREE_DATA = 0x01 ///< Navmesh owns the tile memory and should free it.
};
struct dtCompressedTile
@@ -32,13 +29,14 @@ enum ObstacleState
DT_OBSTACLE_EMPTY,
DT_OBSTACLE_PROCESSING,
DT_OBSTACLE_PROCESSED,
DT_OBSTACLE_REMOVING,
DT_OBSTACLE_REMOVING
};
enum ObstacleType
{
DT_OBSTACLE_CYLINDER,
DT_OBSTACLE_BOX,
DT_OBSTACLE_BOX, // AABB
DT_OBSTACLE_ORIENTED_BOX // OBB
};
struct dtObstacleCylinder
@@ -54,6 +52,13 @@ struct dtObstacleBox
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
{
@@ -61,6 +66,7 @@ struct dtTileCacheObstacle
{
dtObstacleCylinder cylinder;
dtObstacleBox box;
dtObstacleOrientedBox orientedBox;
};
dtCompressedTileRef touched[DT_MAX_TOUCHED_TILES];
@@ -88,13 +94,10 @@ struct dtTileCacheParams
struct dtTileCacheMeshProcess
{
virtual ~dtTileCacheMeshProcess() { }
virtual void process(struct dtNavMeshCreateParams* params,
unsigned char* polyAreas, unsigned short* polyFlags) = 0;
virtual ~dtTileCacheMeshProcess();
virtual void process(struct dtNavMeshCreateParams* params, unsigned char* polyAreas, unsigned short* polyFlags) = 0;
};
class dtTileCache
{
public:
@@ -130,8 +133,14 @@ public:
dtStatus removeTile(dtCompressedTileRef ref, unsigned char** data, int* dataSize);
// Cylinder obstacle.
dtStatus addObstacle(const float* pos, const float radius, const float height, dtObstacleRef* result);
// Aabb obstacle.
dtStatus addBoxObstacle(const float* bmin, const float* bmax, dtObstacleRef* result);
// Box obstacle: can be rotated in Y.
dtStatus addBoxObstacle(const float* center, const float* halfExtents, const float yRadians, dtObstacleRef* result);
dtStatus removeObstacle(const dtObstacleRef ref);
@@ -204,7 +213,7 @@ private:
enum ObstacleRequestAction
{
REQUEST_ADD,
REQUEST_REMOVE,
REQUEST_REMOVE
};
struct ObstacleRequest
@@ -78,7 +78,7 @@ struct dtTileCachePolyMesh
struct dtTileCacheAlloc
{
virtual ~dtTileCacheAlloc() {}
virtual ~dtTileCacheAlloc();
virtual void reset() {}
@@ -95,7 +95,7 @@ struct dtTileCacheAlloc
struct dtTileCacheCompressor
{
virtual ~dtTileCacheCompressor() { }
virtual ~dtTileCacheCompressor();
virtual int maxCompressedSize(const int bufferSize) = 0;
virtual dtStatus compress(const unsigned char* buffer, const int bufferSize,
@@ -130,6 +130,9 @@ dtStatus dtMarkCylinderArea(dtTileCacheLayer& layer, const float* orig, const fl
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);
+64 -3
View File
@@ -239,6 +239,11 @@ const dtTileCacheObstacle* dtTileCache::getObstacleByRef(dtObstacleRef ref)
return ob;
}
dtTileCacheMeshProcess::~dtTileCacheMeshProcess()
{
// Defined out of line to fix the weak v-tables warning
}
dtStatus dtTileCache::addTile(unsigned char* data, const int dataSize, unsigned char flags, dtCompressedTileRef* result)
{
// Make sure the data is in right format.
@@ -420,6 +425,45 @@ dtStatus dtTileCache::addBoxObstacle(const float* bmin, const float* bmax, dtObs
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)
@@ -650,7 +694,12 @@ dtStatus dtTileCache::buildNavMeshTile(const dtCompressedTileRef ref, dtNavMesh*
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);
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);
}
}
}
@@ -662,7 +711,7 @@ dtStatus dtTileCache::buildNavMeshTile(const dtCompressedTileRef ref, dtNavMesh*
bc.lcset = dtAllocTileCacheContourSet(m_talloc);
if (!bc.lcset)
return status;
return DT_FAILURE | DT_OUT_OF_MEMORY;
status = dtBuildTileCacheContours(m_talloc, *bc.layer, walkableClimbVx,
m_params.maxSimplificationError, *bc.lcset);
if (dtStatusFailed(status))
@@ -670,7 +719,7 @@ dtStatus dtTileCache::buildNavMeshTile(const dtCompressedTileRef ref, dtNavMesh*
bc.lmesh = dtAllocTileCachePolyMesh(m_talloc);
if (!bc.lmesh)
return status;
return DT_FAILURE | DT_OUT_OF_MEMORY;
status = dtBuildTileCachePolyMesh(m_talloc, *bc.lcset, *bc.lmesh);
if (dtStatusFailed(status))
return status;
@@ -761,4 +810,16 @@ void dtTileCache::getObstacleBounds(const struct dtTileCacheObstacle* ob, float*
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;
}
}
@@ -23,6 +23,15 @@
#include "DetourTileCacheBuilder.h"
#include <string.h>
dtTileCacheAlloc::~dtTileCacheAlloc()
{
// Defined out of line to fix the weak v-tables warning
}
dtTileCacheCompressor::~dtTileCacheCompressor()
{
// Defined out of line to fix the weak v-tables warning
}
template<class T> class dtFixedArray
{
@@ -1399,7 +1408,6 @@ static void pushBack(unsigned short v, unsigned short* arr, int& an)
static bool canRemoveVertex(dtTileCachePolyMesh& mesh, const unsigned short rem)
{
// Count number of polygons to remove.
int numRemovedVerts = 0;
int numTouchedVerts = 0;
int numRemainingEdges = 0;
for (int i = 0; i < mesh.npolys; ++i)
@@ -1419,7 +1427,6 @@ static bool canRemoveVertex(dtTileCachePolyMesh& mesh, const unsigned short rem)
}
if (numRemoved)
{
numRemovedVerts += numRemoved;
numRemainingEdges += numVerts-(numRemoved+1);
}
}
@@ -2041,6 +2048,60 @@ dtStatus dtMarkBoxArea(dtTileCacheLayer& layer, const float* orig, const float c
return DT_SUCCESS;
}
dtStatus dtMarkBoxArea(dtTileCacheLayer& layer, const float* orig, const float cs, const float ch,
const float* center, const float* halfExtents, const float* rotAux, const unsigned char areaId)
{
const int w = (int)layer.header->width;
const int h = (int)layer.header->height;
const float ics = 1.0f/cs;
const float ich = 1.0f/ch;
float cx = (center[0] - orig[0])*ics;
float cz = (center[2] - orig[2])*ics;
float maxr = 1.41f*dtMax(halfExtents[0], halfExtents[2]);
int minx = (int)floorf(cx - maxr*ics);
int maxx = (int)floorf(cx + maxr*ics);
int minz = (int)floorf(cz - maxr*ics);
int maxz = (int)floorf(cz + maxr*ics);
int miny = (int)floorf((center[1]-halfExtents[1]-orig[1])*ich);
int maxy = (int)floorf((center[1]+halfExtents[1]-orig[1])*ich);
if (maxx < 0) return DT_SUCCESS;
if (minx >= w) return DT_SUCCESS;
if (maxz < 0) return DT_SUCCESS;
if (minz >= h) return DT_SUCCESS;
if (minx < 0) minx = 0;
if (maxx >= w) maxx = w-1;
if (minz < 0) minz = 0;
if (maxz >= h) maxz = h-1;
float xhalf = halfExtents[0]*ics + 0.5f;
float zhalf = halfExtents[2]*ics + 0.5f;
for (int z = minz; z <= maxz; ++z)
{
for (int x = minx; x <= maxx; ++x)
{
float x2 = 2.0f*(float(x) - cx);
float z2 = 2.0f*(float(z) - cz);
float xrot = rotAux[1]*x2 + rotAux[0]*z2;
if (xrot > xhalf || xrot < -xhalf)
continue;
float zrot = rotAux[1]*z2 - rotAux[0]*x2;
if (zrot > zhalf || zrot < -zhalf)
continue;
const int y = layer.heights[x+z*w];
if (y < miny || y > maxy)
continue;
layer.areas[x+z*w] = areaId;
}
}
return DT_SUCCESS;
}
dtStatus dtBuildTileCacheLayer(dtTileCacheCompressor* comp,
dtTileCacheLayerHeader* header,
const unsigned char* heights,
@@ -2142,7 +2203,7 @@ dtStatus dtDecompressTileCacheLayer(dtTileCacheAlloc* alloc, dtTileCacheCompress
grids, gridsSize, &size);
if (dtStatusFailed(status))
{
dtFree(buffer);
alloc->free(buffer);
return status;
}
-27
View File
@@ -1,27 +0,0 @@
/**
@page License License
<pre>
Copyright (c) 2009-2011 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.
</pre>
*/
-109
View File
@@ -1,109 +0,0 @@
/// @mainpage Recast Navigation
///
/// @image html recast_intro.png
///
/// <h2>Recast</h2>
///
/// _Recast_ is a state of the art navigation mesh construction toolset for
/// games.
///
/// - It is automatic, which means that you can throw any level
/// geometry at it and you will get a robust mesh out.
/// - It is fast, which means swift turnaround times for level designers.
/// - It is open source, so it comes with full source and you can
/// customize it to your hearts content.
///
/// The latest version can be found on
/// <a href="https://github.com/recastnavigation/recastnavigation">GitHub</a>.
///
/// The _Recast_ process starts with constructing a voxel mold from level
/// geometry and then casting a navigation mesh over it. The process
/// consists of three steps: building the voxel mold, partitioning the
/// mold into simple regions, and triangulating the regions as simple polygons.
///
/// -# The voxel mold is built from the input triangle mesh by
/// rasterizing the triangles into a multi-layer heightfield. Some
/// simple filters are then applied to the mold to prune out locations
/// where the character would not be able to move.
/// -# The walkable areas described by the mold are divided into simple
/// overlayed 2D regions. The resulting regions have only one
/// non-overlapping contour, which simplifies the final step of the
/// process tremendously.
/// -# The navigation polygons are generated from the regions by first
/// tracing the boundaries and then simplifying them. The resulting
/// polygons are finally converted to convex polygons which makes them
/// perfect for pathfinding and spatial reasoning about the level.
///
/// <h2>Detour</h2>
///
/// _Recast_ is accompanied by _Detour_, a path-finding and spatial reasoning
/// toolkit. You can use any navigation mesh with _Detour_, but of course
/// the data generated by _Recast_ fits perfectly.
///
/// _Detour_ offers a simple static navigation mesh that is suitable for
/// many simple cases, as well as a tiled navigation mesh that allows you
/// to add and remove pieces of the mesh. The tiled mesh allows you to
/// create systems where you stream new navigation data in and out as
/// the player progresses the level, or regenerate tiles as the
/// world changes.
///
/// <h2>Recast Demo</h2>
///
/// You can find a comprehensive demo project in the `RecastDemo` folder. It
/// is a kitchen sink demo containing all the major functionality of the library.
/// If you are new to _Recast_ & _Detour_, check out
/// <a href="https://github.com/recastnavigation/recastnavigation/blob/master/RecastDemo/Source/Sample_SoloMesh.cpp">
/// Sample_SoloMesh.cpp</a> to get started with building navmeshes and
/// <a href="https://github.com/recastnavigation/recastnavigation/blob/master/RecastDemo/Source/NavMeshTesterTool.cpp">
/// NavMeshTesterTool.cpp</a> to see how _Detour_ can be used to find paths.
///
/// <h3>Building RecastDemo</h3>
///
/// RecastDemo uses [premake5](http://premake.github.io/) to build platform specific projects.
/// Download it and make sure it's available on your path, or specify the path to it.
///
/// <h4>Linux</h4>
///
/// - Install SDl2 and its dependencies according to your distro's guidelines.
/// - run `premake5 gmake` from the `RecastDemo` folder.
/// - `cd Build/gmake` then `make`
/// - Run `RecastDemo\Bin\RecastDemo`
///
/// <h4>OSX</h4>
///
/// - Grab the latest SDL2 development library dmg from [here](https://www.libsdl.org/download-2.0.php) and place `SDL2.framework` in `/Library/Frameworks/`
/// - Navigate to the `RecastDemo` folder and run `premake5 xcode4`
/// - Open `Build/xcode4/recastnavigation.xcworkspace`
/// - Select the "RecastDemo" project in the left pane, go to the "BuildPhases" tab and expand "Link Binary With Libraries"
/// - Remove the existing entry for SDL2 (it should have a white box icon) and re-add it by hitting the plus, selecting "Add Other", and selecting `/Library/Frameworks/SDL2.framework`. It should now have a suitcase icon.
/// - Set the RecastDemo project as the target and build.
///
/// <h4>Windows</h4>
///
/// - Grab the latest SDL2 development library release from [here](https://www.libsdl.org/download-2.0.php) and unzip it `RecastDemo\Contrib`. Rename the SDL folder such that the path `RecastDemo\Contrib\SDL\lib\x86` is valid.
/// - Run `"premake5" vs2015` from the `RecastDemo` folder
/// - Open the solution, build, and run.
///
/// <h2>Integrating With Your Own Project</h2>
///
/// It is recommended to add the source directories `DebugUtils`, `Detour`,
/// `DetourCrowd`, `DetourTileCache`, and `Recast` into your own project
/// depending on which parts of the project you need. For example your
/// level building tool could include `DebugUtils`, `Recast`, and `Detour`,
/// and your game runtime could just include `Detour`.
///
/// <h2>Contributing</h2>
/// All development is centralized in github. Check out the <a href="https://github.com/recastnavigation/recastnavigation/blob/master/CONTRIBUTING.md">Contributing Guidelines</a> for more information.
///
/// <h2>Discuss</h2>
///
/// - Discuss _Recast_ and _Detour_:
/// <a href="http://groups.google.com/group/recastnavigation">
/// Recast Navigation Group</a>
/// - Development Blog:
/// <a href="http://digestingduck.blogspot.com/">Digesting Duck</a>
///
/// <h2>License</h2>
///
/// _Recast Navigation_ is licensed under the ZLib license.
///
+1 -1
View File
@@ -107,7 +107,7 @@
<!-- Layout definition for a file page -->
<file>
<briefdescription visible="Description"/>
<briefdescription visible="yes"/>
<detaileddescription title=""/>
<includes visible="$SHOW_INCLUDE_FILES"/>
<includegraph visible="$INCLUDE_GRAPH"/>
+102 -7
View File
@@ -28,9 +28,6 @@ The general life-cycle of the main classes is as follows:
-# Use the object as part of the pipeline.
-# Free the object using the Recast allocator. (E.g. #rcFreeHeightField)
@note This is a summary list of members. Use the index or search
feature to find minor members.
@struct rcConfig
@par
@@ -51,6 +48,10 @@ the platform's floating point accuraccy as well as interdependencies between
the values of multiple parameters. See the individual parameter
documentation for details.
@note First you should decide the size of your agent's logical cylinder.
If your game world uses meters as units, a reasonable starting point for
a human-sized agent might be a radius of `0.4` and a height of `2.0`.
@var rcConfig::borderSize
@par
@@ -66,7 +67,24 @@ This field is only used when building multi-tile meshes.
@var rcConfig::cs
@par
@p cs and #ch define voxel/grid/cell size. So their values have significant
The voxelization cell size #cs defines the voxel size along both axes of
the ground plane: x and z in Recast. This value is usually derived from the
character radius `r`. A recommended starting value for #cs is either `r/2`
or `r/3`. Smaller values of #cs will increase rasterization resolution and
navmesh detail, but total generation time will increase exponentially. In
outdoor environments, `r/2` is often good enough. For indoor scenes with
tight spaces you might want the extra precision, so a value of `r/3` or
smaller may give better results.
The initial instinct is to reduce this value to something very close to zero
to maximize the detail of the generated navmesh. This quickly becomes a case
of diminishing returns, however. Beyond a certain point there's usually not
much perceptable difference in the generated navmesh, but huge increases in
generation time. This hinders your ability to quickly iterate on level
designs and provides little benefit. The general recommendation here is to
use as large a value for #cs as you can get away with.
#cs and #ch define voxel/grid/cell size. So their values have significant
side effects on all parameters defined in voxel units.
The minimum value for this parameter depends on the platform's floating point
@@ -75,7 +93,15 @@ accuracy, with the practical minimum usually around 0.05.
@var rcConfig::ch
@par
#cs and @p ch define voxel/grid/cell size. So their values have significant
The voxelization cell height #ch is defined separately in order to allow for
greater precision in height tests. A good starting point for #ch is half the
#cs value. Smaller #ch values ensure that the navmesh properly connects areas
that are only separated by a small curb or ditch. If small holes are generated
in your navmesh around where there are discontinuities in height (for example,
stairs or curbs), you may want to decrease the cell height value to increase
the vertical rasterization precision of Recast.
#cs and #ch define voxel/grid/cell size. So their values have significant
side effects on all parameters defined in voxel units.
The minimum value for this parameter depends on the platform's floating point
@@ -84,44 +110,106 @@ accuracy, with the practical minimum usually around 0.05.
@var rcConfig::walkableSlopeAngle
@par
The parameter #walkableSlopeAngle is to filter out areas of the world where
the ground slope would be too steep for an agent to traverse. This value is
defined as a maximum angle in degrees that the surface normal of a polgyon
can differ from the world's up vector. This value must be within the range
`[0, 90]`.
The practical upper limit for this parameter is usually around 85 degrees.
@var rcConfig::walkableHeight
@par
This value defines the worldspace height `h` of the agent in voxels. Th value
of #walkableHeight should be calculated as `ceil(h / ch)`. Note this is based
on #ch not #cs since it's a height value.
Permits detection of overhangs in the source geometry that make the geometry
below un-walkable. The value is usually set to the maximum agent height.
@var rcConfig::walkableClimb
@par
The #walkableClimb value defines the maximum height of ledges and steps that
the agent can walk up. Given a designer-defined `maxClimb` distance in world
units, the value of #walkableClimb should be calculated as `ceil(maxClimb / ch)`.
Note that this is using #ch not #cs because it's a height-based value.
Allows the mesh to flow over low lying obstructions such as curbs and
up/down stairways. The value is usually set to how far up/down an agent can step.
@var rcConfig::walkableRadius
@par
The parameter #walkableRadius defines the worldspace agent radius `r` in voxels.
Most often, this value of #walkableRadius should be calculated as `ceil(r / cs)`.
Note this is based on #cs since the agent radius is always parallel to the ground
plane.
If the #walkableRadius value is greater than zero, the edges of the navmesh will
be pushed away from all obstacles by this amount.
A non-zero #walkableRadius allows for much simpler runtime navmesh collision checks.
The game only needs to check that the center point of the agent is contained within
a navmesh polygon. Without this erosion, runtime navigation checks need to collide
the geometric projection of the agent's logical cylinder onto the navmesh with the
boundary edges of the navmesh polygons.
In general, this is the closest any part of the final mesh should get to an
obstruction in the source geometry. It is usually set to the maximum
agent radius.
While a value of zero is legal, it is not recommended and can result in
odd edge case issues.
If you want to have tight-fitting navmesh, or want to reuse the same navmesh for
multiple agents with differing radii, you can use a `walkableRadius` value of zero.
Be advised though that you will need to perform your own collisions with the navmesh
edges, and odd edge cases issues in the mesh generation can potentially occur. For
these reasons, specifying a radius of zero is allowed but is not recommended.
@var rcConfig::maxEdgeLen
@par
In certain cases, long outer edges may decrease the quality of the resulting
triangulation, creating very long thin triangles. This can sometimes be
remedied by limiting the maximum edge length, causing the problematic long
edges to be broken up into smaller segments.
The parameter #maxEdgeLen defines the maximum edge length and is defined in
terms of voxels. A good value for #maxEdgeLen is something like
`walkableRadius * 8`. A good way to adjust this value is to first set it really
high and see if your data creates long edges. If it does, decrease #maxEdgeLen
until you find the largest value which improves the resulting tesselation.
Extra vertices will be inserted as needed to keep contour edges below this
length. A value of zero effectively disables this feature.
@var rcConfig::maxSimplificationError
@par
When the rasterized areas are converted back to a vectorized representation,
the #maxSimplificationError describes how loosely the simplification is done.
The simplification process uses the
<a href="https://en.wikipedia.org/wiki/Ramer–Douglas–Peucker_algorithm">Ramer–Douglas-Peucker algorithm</a>,
and this value describes the max deviation in voxels.
Good values for #maxSimplificationError are in the range `[1.1, 1.5]`.
A value of `1.3` is a good starting point and usually yields good results.
If the value is less than `1.1`, some sawtoothing starts to appear at the
generated edges. If the value is more than `1.5`, the mesh simplification
starts to cut some corners it shouldn't.
The effect of this parameter only applies to the xz-plane.
@var rcConfig::minRegionArea
@par
Watershed partitioning is really prone to noise in the input distance field.
In order to get nicer areas, the areas are merged and small disconnected areas
are removed after the water shed partitioning. The parameter #minRegionArea
describes the minimum isolated region size that is still kept. A region is
removed if the number of voxels in the region is less than the square of
#minRegionArea.
Any regions that are smaller than this area will be marked as unwalkable.
This is useful in removing useless regions that can sometimes form on
geometry such as table tops, box tops, etc.
@@ -132,6 +220,13 @@ geometry such as table tops, box tops, etc.
If the mesh data is to be used to construct a Detour navigation mesh, then the upper limit
is limited to <= #DT_VERTS_PER_POLYGON.
@var rcConfig::mergeRegionArea
@par
The triangulation process works best with small, localized voxel regions.
The parameter #mergeRegionArea controls the maximum voxel area of a region
that is allowed to be merged with another region. If you see small patches
missing here and there, you could lower the #minRegionArea value.
@struct rcHeightfield
@par
Binary file not shown.

Before

Width:  |  Height:  |  Size: 370 KiB

Before

Width:  |  Height:  |  Size: 450 KiB

After

Width:  |  Height:  |  Size: 450 KiB

-5
View File
@@ -7,11 +7,6 @@ Directory Layout
High level content and format files. (E.g. css, header, footer.)
./Conceptual
Conceptual (non-api) documentation such as overviews, how-to's, etc.
The main index page content is also in this directory.
./Extern
API documentation that is located outside the source files.
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+124
View File
@@ -0,0 +1,124 @@
# Doxygen Awesome
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/jothepro/doxygen-awesome-css)](https://github.com/jothepro/doxygen-awesome-css/releases/latest)
[![GitHub](https://img.shields.io/github/license/jothepro/doxygen-awesome-css)](https://github.com/jothepro/doxygen-awesome-css/blob/main/LICENSE)
![GitHub Repo stars](https://img.shields.io/github/stars/jothepro/doxygen-awesome-css)
<div class="title_screenshot">
![Screenshot of Doxygen Awesome CSS](img/screenshot.png)
</div>
**Doxygen Awesome** is a custom **CSS theme for Doxygen HTML-documentation** with lots of customization parameters.
## Motivation
I really like how the Doxygen HTML-documentation is structured! But IMHO it looks a bit outdated.
This theme is an attempt to update the visuals of Doxygen without changing its overall layout too much.
## Features
- 🌈 Clean, modern design
- 🚀 Heavily customizable by adjusting CSS-variables
- 🧩 No changes to the HTML structure of Doxygen required
- 📱 Improved mobile usability
- 🌘 Dark mode support!
- 🥇 Works best with **doxygen 1.9.1** - **1.9.4**
## Examples
Some websites using this theme:
- [Documentation of this repository](https://jothepro.github.io/doxygen-awesome-css/)
- [wxWidgets](https://docs.wxwidgets.org/3.2/)
- [OpenCV 5.x](https://docs.opencv.org/5.x/)
- [Zephyr](https://docs.zephyrproject.org/latest/doxygen/html/index.html)
- [FELTOR](https://mwiesenberger.github.io/feltor/dg/html/modules.html)
- [Spatial Audio Framework (SAF)](https://leomccormack.github.io/Spatial_Audio_Framework/index.html)
- [libCloudSync](https://jothepro.github.io/libCloudSync/)
- [libsl3](https://a4z.github.io/libsl3/)
## Installation
To use the theme in your documentation, copy the required CSS and JS files from this repository into your project or add the repository as submodule and check out the latest release:
```bash
git submodule add https://github.com/jothepro/doxygen-awesome-css.git
cd doxygen-awesome-css
git checkout v2.1.0
```
All theme files are located in the root of this repository and start with the prefix `doxygen-awesome-`. You may not need all of them. Follow the install instructions to figure out what files are required for your setup.
### Choosing a layout
There is two layout options. Choose one of them and configure Doxygen accordingly:
<div class="darkmode_inverted_image">
![Available theme variants](img/theme-variants.drawio.svg)
</div>
#### Base Theme (1)
Comes with the typical Doxygen titlebar. Optionally the treeview in the sidebar can be enabled.
Required files: `doxygen-awesome.css`
Required `Doxyfile` configuration:
```
GENERATE_TREEVIEW = YES # optional. Also works without treeview
DISABLE_INDEX = NO
FULL_SIDEBAR = NO
HTML_EXTRA_STYLESHEET = doxygen-awesome-css/doxygen-awesome.css
```
#### Sidebar-Only Theme (2)
Hides the top titlebar to give more space to the content. The treeview must be enabled in order for this theme to work.
Required files: `doxygen-awesome.css`, `doxygen-awesome-sidebar-only.css`
Required `Doxyfile` configuration:
```
GENERATE_TREEVIEW = YES # required!
DISABLE_INDEX = NO
FULL_SIDEBAR = NO
HTML_EXTRA_STYLESHEET = doxygen-awesome-css/doxygen-awesome.css \
doxygen-awesome-css/doxygen-awesome-sidebar-only.css
```
**Caution**: This theme is not compatible with the `FULL_SIDEBAR = YES` option provided by Doxygen!
### Further installation instructions:
- [Installing extensions](docs/extensions.md)
- [Customizing the theme (colors, spacing, border-radius, ...)](docs/customization.md)
- [Tips and Tricks for further configuration](docs/tricks.md)
## Browser support
Tested with
- Chrome 104, Chrome 104 for Android, Chrome 103 for iOS
- Safari 15, Safari for iOS 15
- Firefox 103, Firefox 103 for Android, Firefox Daylight 102 for iOS
- Edge 104
The theme does not strive to be backwards compatible to (significantly) older browser versions.
## Credits
- This theme was initially inspired by the [vuepress](https://vuepress.vuejs.org/) static site generator default theme.
- Thank you for all the bug reports, pull requests and inspiring feedback on github!
<span class="next_section_button">
Read Next: [Extensions](docs/extensions.md)
</span>
@@ -0,0 +1,157 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2021 - 2022 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
class DoxygenAwesomeDarkModeToggle extends HTMLElement {
// SVG icons from https://fonts.google.com/icons
// Licensed under the Apache 2.0 license:
// https://www.apache.org/licenses/LICENSE-2.0.html
static lightModeIcon = `<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#FCBF00"><rect fill="none" height="24" width="24"/><circle cx="12" cy="12" opacity=".3" r="3"/><path d="M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"/></svg>`
static darkModeIcon = `<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#FE9700"><rect fill="none" height="24" width="24"/><path d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27 C17.45,17.19,14.93,19,12,19c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z" opacity=".3"/><path d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"/></svg>`
static title = "Toggle Light/Dark Mode"
static prefersLightModeInDarkModeKey = "prefers-light-mode-in-dark-mode"
static prefersDarkModeInLightModeKey = "prefers-dark-mode-in-light-mode"
static _staticConstructor = function() {
DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.userPreference)
// Update the color scheme when the browsers preference changes
// without user interaction on the website.
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
DoxygenAwesomeDarkModeToggle.onSystemPreferenceChanged()
})
// Update the color scheme when the tab is made visible again.
// It is possible that the appearance was changed in another tab
// while this tab was in the background.
document.addEventListener("visibilitychange", visibilityState => {
if (document.visibilityState === 'visible') {
DoxygenAwesomeDarkModeToggle.onSystemPreferenceChanged()
}
});
}()
static init() {
$(function() {
$(document).ready(function() {
const toggleButton = document.createElement('doxygen-awesome-dark-mode-toggle')
toggleButton.title = DoxygenAwesomeDarkModeToggle.title
toggleButton.updateIcon()
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
toggleButton.updateIcon()
})
document.addEventListener("visibilitychange", visibilityState => {
if (document.visibilityState === 'visible') {
toggleButton.updateIcon()
}
});
$(document).ready(function(){
document.getElementById("MSearchBox").parentNode.appendChild(toggleButton)
})
$(window).resize(function(){
document.getElementById("MSearchBox").parentNode.appendChild(toggleButton)
})
})
})
}
constructor() {
super();
this.onclick=this.toggleDarkMode
}
/**
* @returns `true` for dark-mode, `false` for light-mode system preference
*/
static get systemPreference() {
return window.matchMedia('(prefers-color-scheme: dark)').matches
}
/**
* @returns `true` for dark-mode, `false` for light-mode user preference
*/
static get userPreference() {
return (!DoxygenAwesomeDarkModeToggle.systemPreference && localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey)) ||
(DoxygenAwesomeDarkModeToggle.systemPreference && !localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey))
}
static set userPreference(userPreference) {
DoxygenAwesomeDarkModeToggle.darkModeEnabled = userPreference
if(!userPreference) {
if(DoxygenAwesomeDarkModeToggle.systemPreference) {
localStorage.setItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey, true)
} else {
localStorage.removeItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey)
}
} else {
if(!DoxygenAwesomeDarkModeToggle.systemPreference) {
localStorage.setItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey, true)
} else {
localStorage.removeItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey)
}
}
DoxygenAwesomeDarkModeToggle.onUserPreferenceChanged()
}
static enableDarkMode(enable) {
if(enable) {
DoxygenAwesomeDarkModeToggle.darkModeEnabled = true
document.documentElement.classList.add("dark-mode")
document.documentElement.classList.remove("light-mode")
} else {
DoxygenAwesomeDarkModeToggle.darkModeEnabled = false
document.documentElement.classList.remove("dark-mode")
document.documentElement.classList.add("light-mode")
}
}
static onSystemPreferenceChanged() {
DoxygenAwesomeDarkModeToggle.darkModeEnabled = DoxygenAwesomeDarkModeToggle.userPreference
DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.darkModeEnabled)
}
static onUserPreferenceChanged() {
DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.darkModeEnabled)
}
toggleDarkMode() {
DoxygenAwesomeDarkModeToggle.userPreference = !DoxygenAwesomeDarkModeToggle.userPreference
this.updateIcon()
}
updateIcon() {
if(DoxygenAwesomeDarkModeToggle.darkModeEnabled) {
this.innerHTML = DoxygenAwesomeDarkModeToggle.darkModeIcon
} else {
this.innerHTML = DoxygenAwesomeDarkModeToggle.lightModeIcon
}
}
}
customElements.define("doxygen-awesome-dark-mode-toggle", DoxygenAwesomeDarkModeToggle);
@@ -0,0 +1,85 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2022 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
class DoxygenAwesomeFragmentCopyButton extends HTMLElement {
constructor() {
super();
this.onclick=this.copyContent
}
static title = "Copy to clipboard"
static copyIcon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>`
static successIcon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z"/></svg>`
static successDuration = 980
static init() {
$(function() {
$(document).ready(function() {
if(navigator.clipboard) {
const fragments = document.getElementsByClassName("fragment")
for(const fragment of fragments) {
const fragmentWrapper = document.createElement("div")
fragmentWrapper.className = "doxygen-awesome-fragment-wrapper"
const fragmentCopyButton = document.createElement("doxygen-awesome-fragment-copy-button")
fragmentCopyButton.innerHTML = DoxygenAwesomeFragmentCopyButton.copyIcon
fragmentCopyButton.title = DoxygenAwesomeFragmentCopyButton.title
fragment.parentNode.replaceChild(fragmentWrapper, fragment)
fragmentWrapper.appendChild(fragment)
fragmentWrapper.appendChild(fragmentCopyButton)
}
}
})
})
}
copyContent() {
const content = this.previousSibling.cloneNode(true)
// filter out line number from file listings
content.querySelectorAll(".lineno, .ttc").forEach((node) => {
node.remove()
})
let textContent = content.textContent
// remove trailing newlines that appear in file listings
let numberOfTrailingNewlines = 0
while(textContent.charAt(textContent.length - (numberOfTrailingNewlines + 1)) == '\n') {
numberOfTrailingNewlines++;
}
textContent = textContent.substring(0, textContent.length - numberOfTrailingNewlines)
navigator.clipboard.writeText(textContent);
this.classList.add("success")
this.innerHTML = DoxygenAwesomeFragmentCopyButton.successIcon
window.setTimeout(() => {
this.classList.remove("success")
this.innerHTML = DoxygenAwesomeFragmentCopyButton.copyIcon
}, DoxygenAwesomeFragmentCopyButton.successDuration);
}
}
customElements.define("doxygen-awesome-fragment-copy-button", DoxygenAwesomeFragmentCopyButton)
@@ -0,0 +1,81 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2022 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
class DoxygenAwesomeInteractiveToc {
static topOffset = 38
static hideMobileMenu = true
static headers = []
static init() {
window.addEventListener("load", () => {
let toc = document.querySelector(".contents > .toc")
if(toc) {
toc.classList.add("interactive")
if(!DoxygenAwesomeInteractiveToc.hideMobileMenu) {
toc.classList.add("open")
}
document.querySelector(".contents > .toc > h3")?.addEventListener("click", () => {
if(toc.classList.contains("open")) {
toc.classList.remove("open")
} else {
toc.classList.add("open")
}
})
document.querySelectorAll(".contents > .toc > ul a").forEach((node) => {
let id = node.getAttribute("href").substring(1)
DoxygenAwesomeInteractiveToc.headers.push({
node: node,
headerNode: document.getElementById(id)
})
document.getElementById("doc-content")?.addEventListener("scroll", () => {
DoxygenAwesomeInteractiveToc.update()
})
})
DoxygenAwesomeInteractiveToc.update()
}
})
}
static update() {
let active = DoxygenAwesomeInteractiveToc.headers[0]?.node
DoxygenAwesomeInteractiveToc.headers.forEach((header) => {
let position = header.headerNode.getBoundingClientRect().top
header.node.classList.remove("active")
header.node.classList.remove("aboveActive")
if(position < DoxygenAwesomeInteractiveToc.topOffset) {
active = header.node
active?.classList.add("aboveActive")
}
})
active?.classList.add("active")
active?.classList.remove("aboveActive")
}
}
@@ -0,0 +1,51 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2022 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
class DoxygenAwesomeParagraphLink {
// Icon from https://fonts.google.com/icons
// Licensed under the Apache 2.0 license:
// https://www.apache.org/licenses/LICENSE-2.0.html
static icon = `<svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 0 24 24" width="20px"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M17 7h-4v2h4c1.65 0 3 1.35 3 3s-1.35 3-3 3h-4v2h4c2.76 0 5-2.24 5-5s-2.24-5-5-5zm-6 8H7c-1.65 0-3-1.35-3-3s1.35-3 3-3h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-2zm-3-4h8v2H8z"/></svg>`
static title = "Permanent Link"
static init() {
$(function() {
$(document).ready(function() {
document.querySelectorAll(".contents a.anchor[id], .contents .groupheader > a[id]").forEach((node) => {
let anchorlink = document.createElement("a")
anchorlink.setAttribute("href", `#${node.getAttribute("id")}`)
anchorlink.setAttribute("title", DoxygenAwesomeParagraphLink.title)
anchorlink.classList.add("anchorlink")
node.classList.add("anchor")
anchorlink.innerHTML = DoxygenAwesomeParagraphLink.icon
node.parentElement.appendChild(anchorlink)
})
})
})
}
}
@@ -0,0 +1,40 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2021 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
@media screen and (min-width: 768px) {
#MSearchBox {
width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - var(--searchbar-height) - 1px);
}
#MSearchField {
width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - 66px - var(--searchbar-height));
}
}
@@ -0,0 +1,115 @@
/**
Doxygen Awesome
https://github.com/jothepro/doxygen-awesome-css
MIT License
Copyright (c) 2021 jothepro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
html {
/* side nav width. MUST be = `TREEVIEW_WIDTH`.
* Make sure it is wide enough to contain the page title (logo + title + version)
*/
--side-nav-fixed-width: 335px;
--menu-display: none;
--top-height: 120px;
--toc-sticky-top: -25px;
--toc-max-height: calc(100vh - 2 * var(--spacing-medium) - 25px);
}
#projectname {
white-space: nowrap;
}
@media screen and (min-width: 768px) {
html {
--searchbar-background: var(--page-background-color);
}
#side-nav {
min-width: var(--side-nav-fixed-width);
max-width: var(--side-nav-fixed-width);
top: var(--top-height);
overflow: visible;
}
#nav-tree, #side-nav {
height: calc(100vh - var(--top-height)) !important;
}
#nav-tree {
padding: 0;
}
#top {
display: block;
border-bottom: none;
height: var(--top-height);
margin-bottom: calc(0px - var(--top-height));
max-width: var(--side-nav-fixed-width);
overflow: hidden;
background: var(--side-nav-background);
}
#main-nav {
float: left;
padding-right: 0;
}
.ui-resizable-handle {
cursor: default;
width: 1px !important;
box-shadow: 0 calc(-2 * var(--top-height)) 0 0 var(--separator-color);
}
#nav-path {
position: fixed;
right: 0;
left: var(--side-nav-fixed-width);
bottom: 0;
width: auto;
}
#doc-content {
height: calc(100vh - 31px) !important;
padding-bottom: calc(3 * var(--spacing-large));
padding-top: calc(var(--top-height) - 80px);
box-sizing: border-box;
margin-left: var(--side-nav-fixed-width) !important;
}
#MSearchBox {
width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)));
}
#MSearchField {
width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - 65px);
}
#MSearchResultsWindow {
left: var(--spacing-medium) !important;
right: auto;
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,54 @@
html.alternative {
/* primary theme color. This will affect the entire websites color scheme: links, arrows, labels, ... */
--primary-color: #AF7FE4;
--primary-dark-color: #9270E4;
--primary-light-color: #7aabd6;
--primary-lighter-color: #cae1f1;
--primary-lightest-color: #e9f1f8;
/* page base colors */
--page-background-color: white;
--page-foreground-color: #2c3e50;
--page-secondary-foreground-color: #67727e;
--border-radius-large: 22px;
--border-radius-small: 9px;
--border-radius-medium: 14px;
--spacing-small: 8px;
--spacing-medium: 14px;
--spacing-large: 19px;
--top-height: 125px;
--side-nav-background: #324067;
--side-nav-foreground: #F1FDFF;
--header-foreground: var(--side-nav-foreground);
--searchbar-background: var(--side-nav-foreground);
--searchbar-border-radius: var(--border-radius-medium);
--header-background: var(--side-nav-background);
--header-foreground: var(--side-nav-foreground);
--toc-background: rgb(243, 240, 252);
--toc-foreground: var(--page-foreground-color);
}
html.alternative.dark-mode {
color-scheme: dark;
--primary-color: #AF7FE4;
--primary-dark-color: #9270E4;
--primary-light-color: #4779ac;
--primary-lighter-color: #191e21;
--primary-lightest-color: #191a1c;
--page-background-color: #1C1D1F;
--page-foreground-color: #d2dbde;
--page-secondary-foreground-color: #859399;
--separator-color: #3a3246;
--side-nav-background: #171D32;
--side-nav-foreground: #F1FDFF;
--toc-background: #20142C;
--searchbar-background: var(--page-background-color);
}
@@ -0,0 +1,101 @@
.github-corner svg {
fill: var(--primary-light-color);
color: var(--page-background-color);
width: 72px;
height: 72px;
}
@media screen and (max-width: 767px) {
.github-corner svg {
width: 50px;
height: 50px;
}
#projectnumber {
margin-right: 22px;
}
}
.alter-theme-button {
display: inline-block;
cursor: pointer;
background: var(--primary-color);
color: var(--page-background-color) !important;
border-radius: var(--border-radius-medium);
padding: var(--spacing-small) var(--spacing-medium);
text-decoration: none;
}
.next_section_button {
display: block;
padding: var(--spacing-large) 0 var(--spacing-small) 0;
color: var(--page-background-color);
user-select: none;
}
.next_section_button::after {
/* clearfix */
content: "";
clear: both;
display: table;
}
.next_section_button a {
overflow: hidden;
float: right;
border: 1px solid var(--separator-color);
padding: var(--spacing-medium) calc(var(--spacing-large) / 2) var(--spacing-medium) var(--spacing-large);
border-radius: var(--border-radius-medium);
color: var(--page-secondary-foreground-color) !important;
text-decoration: none;
background-color: var(--page-background-color);
transition: color .08s ease-in-out, background-color .1s ease-in-out;
}
.next_section_button a:hover {
color: var(--page-foreground-color) !important;
background-color: var(--odd-color);
}
.next_section_button a::after {
content: '〉';
color: var(--page-secondary-foreground-color) !important;
padding-left: var(--spacing-large);
display: inline-block;
transition: color .08s ease-in-out, transform .09s ease-in-out;
}
.next_section_button a:hover::after {
color: var(--page-foreground-color) !important;
transform: translateX(3px);
}
.alter-theme-button:hover {
background: var(--primary-dark-color);
}
html.dark-mode .darkmode_inverted_image img, /* < doxygen 1.9.3 */
html.dark-mode .darkmode_inverted_image object[type="image/svg+xml"] /* doxygen 1.9.3 */ {
filter: brightness(87%) hue-rotate(180deg) invert();
}
.bordered_image {
border-radius: var(--border-radius-small);
border: 1px solid var(--separator-color);
display: inline-block;
overflow: hidden;
}
html.dark-mode .bordered_image img, /* < doxygen 1.9.3 */
html.dark-mode .bordered_image object[type="image/svg+xml"] /* doxygen 1.9.3 */ {
border-radius: var(--border-radius-small);
}
.title_screenshot {
filter: drop-shadow(0px 3px 10px rgba(0,0,0,0.22));
max-width: 500px;
margin: var(--spacing-large) 0;
}
.title_screenshot .caption {
display: none;
}
@@ -0,0 +1,88 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen $doxygenversion"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<!-- BEGIN opengraph metadata -->
<meta property="og:title" content="Doxygen Awesome" />
<meta property="og:image" content="https://repository-images.githubusercontent.com/348492097/4f16df80-88fb-11eb-9d31-4015ff22c452" />
<meta property="og:description" content="Custom CSS theme for doxygen html-documentation with lots of customization parameters." />
<meta property="og:url" content="https://jothepro.github.io/doxygen-awesome-css/" />
<!-- END opengraph metadata -->
<!-- BEGIN twitter metadata -->
<meta name="twitter:image:src" content="https://repository-images.githubusercontent.com/348492097/4f16df80-88fb-11eb-9d31-4015ff22c452" />
<meta name="twitter:title" content="Doxygen Awesome" />
<meta name="twitter:description" content="Custom CSS theme for doxygen html-documentation with lots of customization parameters." />
<!-- END twitter metadata -->
<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
<link rel="icon" type="image/svg+xml" href="logo.drawio.svg"/>
<script type="text/javascript" src="$relpath^jquery.js"></script>
<script type="text/javascript" src="$relpath^dynsections.js"></script>
<script type="text/javascript" src="$relpath^doxygen-awesome-darkmode-toggle.js"></script>
<script type="text/javascript" src="$relpath^doxygen-awesome-fragment-copy-button.js"></script>
<script type="text/javascript" src="$relpath^doxygen-awesome-paragraph-link.js"></script>
<script type="text/javascript" src="$relpath^doxygen-awesome-interactive-toc.js"></script>
<script type="text/javascript" src="$relpath^toggle-alternative-theme.js"></script>
<script type="text/javascript">
DoxygenAwesomeFragmentCopyButton.init()
DoxygenAwesomeDarkModeToggle.init()
DoxygenAwesomeParagraphLink.init()
DoxygenAwesomeInteractiveToc.init()
</script>
$treeview
$search
$mathjax
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
$extrastylesheet
</head>
<body>
<!-- https://tholman.com/github-corners/ -->
<a href="https://github.com/jothepro/doxygen-awesome-css" class="github-corner" title="View source on GitHub" target="_blank">
<svg viewBox="0 0 250 250" width="40" height="40" style="position: absolute; top: 0; border: 0; right: 0; z-index: 99;" aria-hidden="true">
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<!--BEGIN TITLEAREA-->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<!--BEGIN PROJECT_LOGO-->
<td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo"/></td>
<!--END PROJECT_LOGO-->
<!--BEGIN PROJECT_NAME-->
<td id="projectalign" style="padding-left: 0.5em;">
<div id="projectname">$projectname
<!--BEGIN PROJECT_NUMBER-->&#160;<span id="projectnumber">$projectnumber</span><!--END PROJECT_NUMBER-->
</div>
<!--BEGIN PROJECT_BRIEF--><div id="projectbrief">$projectbrief</div><!--END PROJECT_BRIEF-->
</td>
<!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME-->
<!--BEGIN PROJECT_BRIEF-->
<td style="padding-left: 0.5em;">
<div id="projectbrief">$projectbrief</div>
</td>
<!--END PROJECT_BRIEF-->
<!--END !PROJECT_NAME-->
<!--BEGIN DISABLE_INDEX-->
<!--BEGIN SEARCHENGINE-->
<td>$searchbox</td>
<!--END SEARCHENGINE-->
<!--END DISABLE_INDEX-->
</tr>
</tbody>
</table>
</div>
<!--END TITLEAREA-->
<!-- end header part -->
@@ -0,0 +1,12 @@
let original_theme_active = true;
function toggle_alternative_theme() {
if(original_theme_active) {
document.documentElement.classList.add("alternative")
original_theme_active = false;
} else {
document.documentElement.classList.remove("alternative")
original_theme_active = true;
}
}
+4 -4
View File
@@ -5,16 +5,16 @@
<ul>
$navpath
&nbsp;&nbsp;<a href="https://github.com/recastnavigation/recastnavigation">Project Home</a>
| <a href="./License.html">Licence: ZLib</a>
| Copyright (c) 2009-2014 Mikko Mononen
| <a href="./License.txt">Licence (ZLib)</a>
| Copyright 2009 Mikko Mononen
</ul>
</div>
<!--END GENERATE_TREEVIEW-->
<!--BEGIN !GENERATE_TREEVIEW-->
<hr class="footer"/><address class="footer"><small>
&nbsp;&nbsp;<a href="https://github.com/recastnavigation/recastnavigation">Project Home</a>
| <a href="./License.html">Licence: ZLib</a>
| Copyright (c) 2009-2014 Mikko Mononen
| <a href="./License.txt">Licence (ZLib)</a>
| Copyright 2009 Mikko Mononen
</small></address>
<!--END !GENERATE_TREEVIEW-->
</body>
+595 -324
View File
File diff suppressed because it is too large Load Diff
+37 -36
View File
@@ -2,42 +2,36 @@
Recast & Detour
===============
[![Travis (Linux) Build Status](https://travis-ci.org/recastnavigation/recastnavigation.svg?branch=master)](https://travis-ci.org/recastnavigation/recastnavigation)
[![Appveyor (Windows) Build Status](https://ci.appveyor.com/api/projects/status/20w84u25b3f8h179/branch/master?svg=true)](https://ci.appveyor.com/project/recastnavigation/recastnavigation/branch/master)
[![Build](https://github.com/recastnavigation/recastnavigation/actions/workflows/Build.yaml/badge.svg)](https://github.com/recastnavigation/recastnavigation/actions/workflows/Build.yaml)
[![Tests](https://github.com/recastnavigation/recastnavigation/actions/workflows/Tests.yaml/badge.svg)](https://github.com/recastnavigation/recastnavigation/actions/workflows/Tests.yaml)
[![Issue Stats](http://www.issuestats.com/github/recastnavigation/recastnavigation/badge/pr?style=flat)](http://www.issuestats.com/github/recastnavigation/recastnavigation)
[![Issue Stats](http://www.issuestats.com/github/recastnavigation/recastnavigation/badge/issue?style=flat)](http://www.issuestats.com/github/recastnavigation/recastnavigation)
![screenshot of a navmesh baked with the sample program](/RecastDemo/screenshot.png?raw=true)
![screenshot of a navmesh baked with the sample program](/Docs/Images/screenshot.png)
## Recast
Recast is state of the art navigation mesh construction toolset for games.
* It is automatic, which means that you can throw any level geometry at it and you will get robust mesh out
* It is fast which means swift turnaround times for level designers
* It is open source so it comes with full source and you can customize it to your heart's content.
Recast is...
* 🤖 **Automatic** - throw any level geometry at it and you will get a robust navmesh out
* 🏎️ **Fast** - swift turnaround times for level designers
* 🧘 **Flexible** - easily customize the navmesh generation and runtime navigation systems to suit your specific game's needs.
The Recast process starts with constructing a voxel mold from a level geometry
and then casting a navigation mesh over it. The process consists of three steps,
building the voxel mold, partitioning the mold into simple regions, peeling off
the regions as simple polygons.
1. The voxel mold is build from the input triangle mesh by rasterizing the triangles into a multi-layer heightfield. Some simple filters are then applied to the mold to prune out locations where the character would not be able to move.
2. The walkable areas described by the mold are divided into simple overlayed 2D regions. The resulting regions have only one non-overlapping contour, which simplifies the final step of the process tremendously.
3. The navigation polygons are peeled off from the regions by first tracing the boundaries and then simplifying them. The resulting polygons are finally converted to convex polygons which makes them perfect for pathfinding and spatial reasoning about the level.
Recast constructs a navmesh through a multi-step rasterization process:
1. First Recast voxelizes the input triangle mesh by rasterizing the triangles into a multi-layer heightfield.
2. Voxels in areas where the character would not be able to move are removed by applying simple voxel data filters.
3. The walkable areas described by the voxel grid are then divided into sets of 2D polygonal regions.
4. The navigation polygons are generated by triangulating and stiching together the generated 2d plygonal regions.
## Detour
Recast is accompanied with Detour, path-finding and spatial reasoning toolkit. You can use any navigation mesh with Detour, but of course the data generated with Recast fits perfectly.
Recast is accompanied by Detour, a path-finding and spatial reasoning toolkit. You can use any navigation mesh with Detour, but of course the data generated with Recast fits perfectly.
Detour offers simple static navigation mesh which is suitable for many simple cases, as well as tiled navigation mesh which allows you to plug in and out pieces of the mesh. The tiled mesh allows you to create systems where you stream new navigation data in and out as the player progresses the level, or you may regenerate tiles as the world changes.
Detour offers a simple static navmesh data representation which is suitable for many simple cases. It also provides a tiled navigation mesh representation, which allows you to stream of navigation data in and out as the player progresses through the world and regenerate sections of the navmesh data as the world changes.
## RecastDemo
## Recast Demo
You can find a comprehensive demo project in RecastDemo folder. It is a kitchen sink demo containing all the functionality of the library. If you are new to Recast & Detour, check out [Sample_SoloMesh.cpp](/RecastDemo/Source/Sample_SoloMesh.cpp) to get started with building navmeshes and [NavMeshTesterTool.cpp](/RecastDemo/Source/NavMeshTesterTool.cpp) to see how Detour can be used to find paths.
You can find a comprehensive demo project in the `RecastDemo` folder. It's a kitchen sink demo showcasing all the functionality of the library. If you are new to Recast & Detour, check out [Sample_SoloMesh.cpp](/RecastDemo/Source/Sample_SoloMesh.cpp) to get started with building navmeshes and [NavMeshTesterTool.cpp](/RecastDemo/Source/NavMeshTesterTool.cpp) to see how Detour can be used to find paths.
### Building RecastDemo
@@ -45,25 +39,24 @@ RecastDemo uses [premake5](http://premake.github.io/) to build platform specific
#### Linux
- Install SDl2 and its dependencies according to your distro's guidelines.
- run `premake5 gmake` from the `RecastDemo` folder.
- `cd Build/gmake` then `make`
- Run `RecastDemo\Bin\RecastDemo`
- Install SDL2 and its dependencies according to your distro's guidelines.
- Navigate to the `RecastDemo` folder and run `premake5 gmake2`
- Navigate to `RecastDemo/Build/gmake2` and run `make`
- Navigate to `RecastDemo/Bin` and run `./RecastDemo`
#### OSX
#### macOS
- Grab the latest SDL2 development library dmg from [here](https://www.libsdl.org/download-2.0.php) and place `SDL2.framework` in `/Library/Frameworks/`
- Grab the latest SDL2 development library dmg from [here](https://github.com/libsdl-org/SDL) and place `SDL2.framework` in `RecastDemo/Bin`
- Navigate to the `RecastDemo` folder and run `premake5 xcode4`
- Open `Build/xcode4/recastnavigation.xcworkspace`
- Select the "RecastDemo" project in the left pane, go to the "BuildPhases" tab and expand "Link Binary With Libraries"
- Remove the existing entry for SDL2 (it should have a white box icon) and re-add it by hitting the plus, selecting "Add Other", and selecting `/Library/Frameworks/SDL2.framework`. It should now have a suitcase icon.
- Set the RecastDemo project as the target and build.
#### Windows
- Grab the latest SDL2 development library release from [here](https://www.libsdl.org/download-2.0.php) and unzip it `RecastDemo\Contrib`. Rename the SDL folder such that the path `RecastDemo\Contrib\SDL\lib\x86` is valid.
- Run `"premake5" vs2015` from the `RecastDemo` folder
- Open the solution, build, and run.
- Grab the latest SDL2 development library release from [here](https://github.com/libsdl-org/SDL) and unzip it `RecastDemo\Contrib`. Rename the SDL folder such that the path `RecastDemo\Contrib\SDL\lib\x86` is valid.
- Navigate to the `RecastDemo` folder and run `premake5 vs2022`
- Open `Build/vs2022/recastnavigation.sln`.
- Set `RecastDemo` as the startup project, build, and run.
### Running Unit tests
@@ -71,13 +64,21 @@ RecastDemo uses [premake5](http://premake.github.io/) to build platform specific
- Build the "Tests" project. This will generate an executable named "Tests" in `RecastDemo/Bin/`
- Run the "Tests" executable. It will execute all the unit tests, indicate those that failed, and display a count of those that succeeded.
## Integrating with your own project
## Integrating with your game or engine
It is recommended to add the source directories `DebugUtils`, `Detour`, `DetourCrowd`, `DetourTileCache`, and `Recast` into your own project depending on which parts of the project you need. For example your level building tool could include `DebugUtils`, `Recast`, and `Detour`, and your game runtime could just include `Detour`.
## Contributing
### Install through vcpkg
See the [Contributing document](CONTRIBUTING.md) for guidelines for making contributions.
If you are using the [vcpkg](https://github.com/Microsoft/vcpkg/) dependency manager you can download and install Recast with:
```
vcpkg install recast
```
## Contribute
Check out the [Project Roadmap](Roadmap.md) to see what features and functionality you might be able to help with, and the [Contributing doc](CONTRIBUTING.md) for guidance on making contributions.
## Discuss
+33
View File
@@ -0,0 +1,33 @@
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 ${LIB_VERSION}
COMPILE_PDB_OUTPUT_DIRECTORY .
COMPILE_PDB_NAME "Recast-d"
)
install(TARGETS Recast
EXPORT recastnavigation-targets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT library
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation
)
file(GLOB INCLUDES Include/*.h)
install(FILES ${INCLUDES} DESTINATION
${CMAKE_INSTALL_INCLUDEDIR}/recastnavigation)
if(MSVC)
install(FILES "$<TARGET_FILE_DIR:Recast>/Recast-d.pdb" CONFIGURATIONS "Debug" DESTINATION "lib")
endif()
+558 -421
View File
File diff suppressed because it is too large Load Diff
+285 -59
View File
@@ -19,7 +19,10 @@
#ifndef RECASTALLOC_H
#define RECASTALLOC_H
#include <stddef.h>
#include "RecastAssert.h"
#include <stdlib.h>
#include <stdint.h>
/// Provides hint values to the memory allocator on how long the
/// memory is expected to be used.
@@ -44,78 +47,301 @@ 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
///
/// @see rcAlloc, 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
///
/// @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, rcAllocSetCustom
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
/// Deallocates a memory block. If @p ptr is NULL, this does nothing.
///
/// @warning This function leaves the value of @p ptr unchanged. So it still
/// points to the same (now invalid) location, and not to null.
///
/// @param[in] ptr A pointer to a memory block previously allocated using #rcAlloc.
///
/// @see rcAlloc, rcAllocSetCustom
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*) {}
/// A simple dynamic array of integers.
/// 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);
// Requires: min_capacity > m_cap.
rcSizeType get_new_capacity(rcSizeType min_capacity);
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;
}
const rcSizeType new_cap = get_new_capacity(m_cap + 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>
rcSizeType rcVectorBase<T, H>::get_new_capacity(rcSizeType min_capacity) {
rcAssert(min_capacity <= RC_SIZE_MAX);
if (rcUnlikely(m_cap >= RC_SIZE_MAX / 2))
return RC_SIZE_MAX;
return 2 * m_cap > min_capacity ? 2 * m_cap : min_capacity;
}
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) {
if (size <= m_cap) {
if (value) {
construct_range(m_data + m_size, m_data + size, *value);
} else {
construct_range(m_data + m_size, m_data + size);
}
m_size = size;
} else {
const rcSizeType new_cap = get_new_capacity(size);
T* new_data = allocate_and_copy(new_cap);
// 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 = new_cap;
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
{
int* m_data;
int m_size, m_cap;
void doResize(int n);
// Explicitly disabled copy constructor and copy assignment operator.
rcIntArray(const rcIntArray&);
rcIntArray& operator=(const rcIntArray&);
rcTempVector<int> m_impl;
public:
/// Constructs an instance with an initial array size of zero.
rcIntArray() : m_data(0), m_size(0), m_cap(0) {}
/// Constructs an instance initialized to the specified size.
/// @param[in] n The initial size of the integer array.
rcIntArray(int n) : m_data(0), m_size(0), m_cap(0) { resize(n); }
~rcIntArray() { rcFree(m_data); }
/// Specifies the new size of the integer array.
/// @param[in] n The new size of the integer array.
void resize(int n)
{
if (n > m_cap)
doResize(n);
m_size = n;
}
/// Push the specified integer onto the end of the array and increases the size by one.
/// @param[in] item The new value.
void push(int item) { resize(m_size+1); m_data[m_size-1] = item; }
/// Returns the value at the end of the array and reduces the size by one.
/// @return The value at the end of the array.
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); }
void clear() { m_impl.clear(); }
int pop()
{
if (m_size > 0)
m_size--;
return m_data[m_size];
int v = m_impl.back();
m_impl.pop_back();
return v;
}
/// The value at the specified array index.
/// @warning Does not provide overflow protection.
/// @param[in] i The index of the value.
const int& operator[](int i) const { return m_data[i]; }
/// The value at the specified array index.
/// @warning Does not provide overflow protection.
/// @param[in] i The index of the value.
int& operator[](int i) { return m_data[i]; }
/// The current size of the integer array.
int size() const { return m_size; }
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.
+4 -7
View File
@@ -19,13 +19,10 @@
#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)
# define rcAssert(x) do { (void)sizeof(x); } while ((void)(__LINE__==-1), false)
#else
@@ -38,7 +35,7 @@ typedef void (rcAssertFailFunc)(const char* expression, const char* file, int li
/// 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);
void rcAssertFailSetCustom(rcAssertFailFunc* assertFailFunc);
/// Gets the base custom assertion failure function to be used by Recast.
rcAssertFailFunc* rcAssertFailGetCustom();
@@ -47,8 +44,8 @@ rcAssertFailFunc* rcAssertFailGetCustom();
# define rcAssert(expression) \
{ \
rcAssertFailFunc* failFunc = rcAssertFailGetCustom(); \
if(failFunc == NULL) { assert(expression); } \
else if(!(expression)) { (*failFunc)(#expression, __FILE__, __LINE__); } \
if (failFunc == NULL) { assert(expression); } \
else if (!(expression)) { (*failFunc)(#expression, __FILE__, __LINE__); } \
}
#endif
+359 -321
View File
@@ -16,61 +16,73 @@
// 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 <new>
#include "Recast.h"
#include "RecastAlloc.h"
#include "RecastAssert.h"
#include <math.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
namespace
{
/// Allocates and constructs an object of the given type, returning a pointer.
/// @param[in] allocLifetime Allocation lifetime hint
template<typename T>
T* rcNew(const rcAllocHint allocLifetime)
{
T* ptr = (T*)rcAlloc(sizeof(T), allocLifetime);
::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);
}
}
} // anonymous 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);
va_list argList;
va_start(argList, format);
int len = vsnprintf(msg, MSG_SIZE, format, argList);
if (len >= MSG_SIZE)
{
len = MSG_SIZE-1;
msg[MSG_SIZE-1] = '\0';
len = MSG_SIZE - 1;
msg[MSG_SIZE - 1] = '\0';
const char* errorMessage = "Log message was truncated";
doLog(RC_LOG_ERROR, errorMessage, (int)strlen(errorMessage));
}
va_end(ap);
va_end(argList);
doLog(category, msg, len);
}
void rcContext::doResetLog()
{
// Defined out of line to fix the weak v-tables warning
}
rcAreaModification::rcAreaModification(unsigned char value) :
m_value(value),
m_mask(RC_AREA_FLAGS_MASK)
@@ -117,19 +129,24 @@ unsigned char rcAreaModification::getMaskedValue() const
rcHeightfield* rcAllocHeightfield()
{
return new (rcAlloc(sizeof(rcHeightfield), RC_ALLOC_PERM)) rcHeightfield;
return rcNew<rcHeightfield>(RC_ALLOC_PERM);
}
void rcFreeHeightField(rcHeightfield* heightfield)
{
rcDelete(heightfield);
}
rcHeightfield::rcHeightfield()
: width()
, height()
, bmin()
, bmax()
, cs()
, ch()
, spans()
, pools()
, freelist()
: width()
, height()
, bmin()
, bmax()
, cs()
, ch()
, spans()
, pools()
, freelist()
{
}
@@ -146,404 +163,425 @@ rcHeightfield::~rcHeightfield()
}
}
void rcFreeHeightField(rcHeightfield* hf)
{
if (!hf) return;
hf->~rcHeightfield();
rcFree(hf);
}
rcCompactHeightfield* rcAllocCompactHeightfield()
{
rcCompactHeightfield* chf = (rcCompactHeightfield*)rcAlloc(sizeof(rcCompactHeightfield), RC_ALLOC_PERM);
memset(chf, 0, sizeof(rcCompactHeightfield));
return chf;
return rcNew<rcCompactHeightfield>(RC_ALLOC_PERM);
}
void rcFreeCompactHeightfield(rcCompactHeightfield* chf)
void rcFreeCompactHeightfield(rcCompactHeightfield* compactHeightfield)
{
if (!chf) return;
rcFree(chf->cells);
rcFree(chf->spans);
rcFree(chf->dist);
rcFree(chf->areas);
rcFree(chf);
rcDelete(compactHeightfield);
}
rcCompactHeightfield::rcCompactHeightfield()
: width()
, height()
, spanCount()
, walkableHeight()
, walkableClimb()
, borderSize()
, maxDistance()
, maxRegions()
, bmin()
, bmax()
, cs()
, ch()
, cells()
, spans()
, dist()
, areas()
{
}
rcCompactHeightfield::~rcCompactHeightfield()
{
rcFree(cells);
rcFree(spans);
rcFree(dist);
rcFree(areas);
}
rcHeightfieldLayerSet* rcAllocHeightfieldLayerSet()
{
rcHeightfieldLayerSet* lset = (rcHeightfieldLayerSet*)rcAlloc(sizeof(rcHeightfieldLayerSet), RC_ALLOC_PERM);
memset(lset, 0, sizeof(rcHeightfieldLayerSet));
return lset;
return rcNew<rcHeightfieldLayerSet>(RC_ALLOC_PERM);
}
void rcFreeHeightfieldLayerSet(rcHeightfieldLayerSet* lset)
void rcFreeHeightfieldLayerSet(rcHeightfieldLayerSet* layerSet)
{
if (!lset) return;
for (int i = 0; i < lset->nlayers; ++i)
rcDelete(layerSet);
}
rcHeightfieldLayerSet::rcHeightfieldLayerSet()
: layers()
, nlayers()
{
}
rcHeightfieldLayerSet::~rcHeightfieldLayerSet()
{
for (int i = 0; i < nlayers; ++i)
{
rcFree(lset->layers[i].heights);
rcFree(lset->layers[i].areas);
rcFree(lset->layers[i].cons);
rcFree(layers[i].heights);
rcFree(layers[i].areas);
rcFree(layers[i].cons);
}
rcFree(lset->layers);
rcFree(lset);
rcFree(layers);
}
rcContourSet* rcAllocContourSet()
{
rcContourSet* cset = (rcContourSet*)rcAlloc(sizeof(rcContourSet), RC_ALLOC_PERM);
memset(cset, 0, sizeof(rcContourSet));
return cset;
return rcNew<rcContourSet>(RC_ALLOC_PERM);
}
void rcFreeContourSet(rcContourSet* cset)
void rcFreeContourSet(rcContourSet* contourSet)
{
if (!cset) return;
for (int i = 0; i < cset->nconts; ++i)
rcDelete(contourSet);
}
rcContourSet::rcContourSet()
: conts()
, nconts()
, bmin()
, bmax()
, cs()
, ch()
, width()
, height()
, borderSize()
, maxError()
{
}
rcContourSet::~rcContourSet()
{
for (int i = 0; i < nconts; ++i)
{
rcFree(cset->conts[i].verts);
rcFree(cset->conts[i].rverts);
rcFree(conts[i].verts);
rcFree(conts[i].rverts);
}
rcFree(cset->conts);
rcFree(cset);
rcFree(conts);
}
rcPolyMesh* rcAllocPolyMesh()
{
rcPolyMesh* pmesh = (rcPolyMesh*)rcAlloc(sizeof(rcPolyMesh), RC_ALLOC_PERM);
memset(pmesh, 0, sizeof(rcPolyMesh));
return pmesh;
return rcNew<rcPolyMesh>(RC_ALLOC_PERM);
}
void rcFreePolyMesh(rcPolyMesh* pmesh)
void rcFreePolyMesh(rcPolyMesh* polyMesh)
{
if (!pmesh) return;
rcFree(pmesh->verts);
rcFree(pmesh->polys);
rcFree(pmesh->regs);
rcFree(pmesh->flags);
rcFree(pmesh->areas);
rcFree(pmesh);
rcDelete(polyMesh);
}
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;
return rcNew<rcPolyMeshDetail>(RC_ALLOC_PERM);
}
void rcFreePolyMeshDetail(rcPolyMeshDetail* dmesh)
void rcFreePolyMeshDetail(rcPolyMeshDetail* detailMesh)
{
if (!dmesh) return;
rcFree(dmesh->meshes);
rcFree(dmesh->verts);
rcFree(dmesh->tris);
rcFree(dmesh);
rcDelete(detailMesh);
}
void rcCalcBounds(const float* verts, int nv, float* bmin, float* bmax)
rcPolyMeshDetail::rcPolyMeshDetail()
: meshes()
, verts()
, tris()
, nmeshes()
, nverts()
, ntris()
{
}
rcPolyMeshDetail::~rcPolyMeshDetail()
{
rcFree(meshes);
rcFree(verts);
rcFree(tris);
}
void rcCalcBounds(const float* verts, int numVerts, float* minBounds, float* maxBounds)
{
// Calculate bounding box.
rcVcopy(bmin, verts);
rcVcopy(bmax, verts);
for (int i = 1; i < nv; ++i)
rcVcopy(minBounds, verts);
rcVcopy(maxBounds, verts);
for (int i = 1; i < numVerts; ++i)
{
const float* v = &verts[i*3];
rcVmin(bmin, v);
rcVmax(bmax, v);
const float* v = &verts[i * 3];
rcVmin(minBounds, v);
rcVmax(maxBounds, v);
}
}
void rcCalcGridSize(const float* bmin, const float* bmax, float cs, int* w, int* h)
void rcCalcGridSize(const float* minBounds, const float* maxBounds, const float cellSize, int* sizeX, int* sizeZ)
{
*w = (int)((bmax[0] - bmin[0])/cs+0.5f);
*h = (int)((bmax[2] - bmin[2])/cs+0.5f);
*sizeX = (int)((maxBounds[0] - minBounds[0]) / cellSize + 0.5f);
*sizeZ = (int)((maxBounds[2] - minBounds[2]) / cellSize + 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)
bool rcCreateHeightfield(rcContext* context, rcHeightfield& heightfield, int sizeX, int sizeZ,
const float* minBounds, const float* maxBounds,
float cellSize, float cellHeight)
{
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)
rcIgnoreUnused(context);
heightfield.width = sizeX;
heightfield.height = sizeZ;
rcVcopy(heightfield.bmin, minBounds);
rcVcopy(heightfield.bmax, maxBounds);
heightfield.cs = cellSize;
heightfield.ch = cellHeight;
heightfield.spans = (rcSpan**)rcAlloc(sizeof(rcSpan*) * heightfield.width * heightfield.height, RC_ALLOC_PERM);
if (!heightfield.spans)
{
return false;
memset(hf.spans, 0, sizeof(rcSpan*)*hf.width*hf.height);
}
memset(heightfield.spans, 0, sizeof(rcSpan*) * heightfield.width * heightfield.height);
return true;
}
static void calcTriNormal(const float* v0, const float* v1, const float* v2, float* norm)
static void calcTriNormal(const float* v0, const float* v1, const float* v2, float* faceNormal)
{
float e0[3], e1[3];
rcVsub(e0, v1, v0);
rcVsub(e1, v2, v0);
rcVcross(norm, e0, e1);
rcVnormalize(norm);
rcVcross(faceNormal, e0, e1);
rcVnormalize(faceNormal);
}
/// @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,
rcAreaModification areaMod)
void rcMarkWalkableTriangles(rcContext* context, const float walkableSlopeAngle,
const float* verts, const int numVerts,
const int* tris, const int numTris,
unsigned char* triAreaIDs,
rcAreaModification areaMod)
{
rcIgnoreUnused(ctx);
rcIgnoreUnused(nv);
const float walkableThr = cosf(walkableSlopeAngle/180.0f*RC_PI);
rcIgnoreUnused(context);
rcIgnoreUnused(numVerts);
const float walkableThr = cosf(walkableSlopeAngle / 180.0f * RC_PI);
float norm[3];
for (int i = 0; i < nt; ++i)
for (int i = 0; i < numTris; ++i)
{
const int* tri = &tris[i*3];
calcTriNormal(&verts[tri[0]*3], &verts[tri[1]*3], &verts[tri[2]*3], norm);
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)
areaMod.apply(areas[i]);
}
}
/// @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)
areaMod.apply(triAreaIDs[i]);
}
}
}
void rcClearUnwalkableTriangles(rcContext* context, const float walkableSlopeAngle,
const float* verts, int numVerts,
const int* tris, int numTris,
unsigned char* triAreaIDs)
{
rcIgnoreUnused(context);
rcIgnoreUnused(numVerts);
// The minimum Y value for a face normal of a triangle with a walkable slope.
const float walkableLimitY = cosf(walkableSlopeAngle / 180.0f * RC_PI);
float faceNormal[3];
for (int i = 0; i < numTris; ++i)
{
const int* tri = &tris[i * 3];
calcTriNormal(&verts[tri[0] * 3], &verts[tri[1] * 3], &verts[tri[2] * 3], faceNormal);
// Check if the face is walkable.
if (faceNormal[1] <= walkableLimitY)
{
triAreaIDs[i] = RC_NULL_AREA;
}
}
}
int rcGetHeightFieldSpanCount(rcContext* context, const rcHeightfield& heightfield)
{
rcIgnoreUnused(context);
const int numCols = heightfield.width * heightfield.height;
int spanCount = 0;
for (int columnIndex = 0; columnIndex < numCols; ++columnIndex)
{
for (rcSpan* span = heightfield.spans[columnIndex]; span != NULL; span = span->next)
{
if (span->area != RC_NULL_AREA)
{
if (s->area != RC_NULL_AREA)
spanCount++;
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)
bool rcBuildCompactHeightfield(rcContext* context, const int walkableHeight, const int walkableClimb,
rcHeightfield& heightfield, rcCompactHeightfield& compactHeightfield)
{
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);
rcAssert(context);
rcScopedTimer timer(context, RC_TIMER_BUILD_COMPACTHEIGHTFIELD);
const int xSize = heightfield.width;
const int zSize = heightfield.height;
const int spanCount = rcGetHeightFieldSpanCount(context, heightfield);
// 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)
compactHeightfield.width = xSize;
compactHeightfield.height = zSize;
compactHeightfield.spanCount = spanCount;
compactHeightfield.walkableHeight = walkableHeight;
compactHeightfield.walkableClimb = walkableClimb;
compactHeightfield.maxRegions = 0;
rcVcopy(compactHeightfield.bmin, heightfield.bmin);
rcVcopy(compactHeightfield.bmax, heightfield.bmax);
compactHeightfield.bmax[1] += walkableHeight * heightfield.ch;
compactHeightfield.cs = heightfield.cs;
compactHeightfield.ch = heightfield.ch;
compactHeightfield.cells = (rcCompactCell*)rcAlloc(sizeof(rcCompactCell) * xSize * zSize, RC_ALLOC_PERM);
if (!compactHeightfield.cells)
{
ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.cells' (%d)", w*h);
context->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.cells' (%d)", xSize * zSize);
return false;
}
memset(chf.cells, 0, sizeof(rcCompactCell)*w*h);
chf.spans = (rcCompactSpan*)rcAlloc(sizeof(rcCompactSpan)*spanCount, RC_ALLOC_PERM);
if (!chf.spans)
memset(compactHeightfield.cells, 0, sizeof(rcCompactCell) * xSize * zSize);
compactHeightfield.spans = (rcCompactSpan*)rcAlloc(sizeof(rcCompactSpan) * spanCount, RC_ALLOC_PERM);
if (!compactHeightfield.spans)
{
ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.spans' (%d)", spanCount);
context->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)
memset(compactHeightfield.spans, 0, sizeof(rcCompactSpan) * spanCount);
compactHeightfield.areas = (unsigned char*)rcAlloc(sizeof(unsigned char) * spanCount, RC_ALLOC_PERM);
if (!compactHeightfield.areas)
{
ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.areas' (%d)", spanCount);
context->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.areas' (%d)", spanCount);
return false;
}
memset(chf.areas, RC_NULL_AREA, sizeof(unsigned char)*spanCount);
memset(compactHeightfield.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)
int currentCellIndex = 0;
const int numColumns = xSize * zSize;
for (int columnIndex = 0; columnIndex < numColumns; ++columnIndex)
{
for (int x = 0; x < w; ++x)
const rcSpan* span = heightfield.spans[columnIndex];
// If there are no spans at this cell, just leave the data to index=0, count=0.
if (span == NULL)
{
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)
continue;
}
rcCompactCell& cell = compactHeightfield.cells[columnIndex];
cell.index = currentCellIndex;
cell.count = 0;
for (; span != NULL; span = span->next)
{
if (span->area != RC_NULL_AREA)
{
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;
const int bot = (int)span->smax;
const int top = span->next ? (int)span->next->smin : MAX_HEIGHT;
compactHeightfield.spans[currentCellIndex].y = (unsigned short)rcClamp(bot, 0, 0xffff);
compactHeightfield.spans[currentCellIndex].h = (unsigned char)rcClamp(top - bot, 0, 0xff);
compactHeightfield.areas[currentCellIndex] = span->area;
currentCellIndex++;
cell.count++;
}
}
}
// Find neighbour connections.
const int MAX_LAYERS = RC_NOT_CONNECTED-1;
int tooHighNeighbour = 0;
for (int y = 0; y < h; ++y)
const int MAX_LAYERS = RC_NOT_CONNECTED - 1;
int maxLayerIndex = 0;
const int zStride = xSize; // for readability
for (int z = 0; z < zSize; ++z)
{
for (int x = 0; x < w; ++x)
for (int x = 0; x < xSize; ++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 rcCompactCell& cell = compactHeightfield.cells[x + z * zStride];
for (int i = (int)cell.index, ni = (int)(cell.index + cell.count); i < ni; ++i)
{
rcCompactSpan& s = chf.spans[i];
rcCompactSpan& span = compactHeightfield.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);
rcSetCon(span, dir, RC_NOT_CONNECTED);
const int neighborX = x + rcGetDirOffsetX(dir);
const int neighborZ = z + rcGetDirOffsetY(dir);
// First check that the neighbour cell is in bounds.
if (nx < 0 || ny < 0 || nx >= w || ny >= h)
if (neighborX < 0 || neighborZ < 0 || neighborX >= xSize || neighborZ >= zSize)
{
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 rcCompactCell& neighborCell = compactHeightfield.cells[neighborX + neighborZ * zStride];
for (int k = (int)neighborCell.index, nk = (int)(neighborCell.index + neighborCell.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);
const rcCompactSpan& neighborSpan = compactHeightfield.spans[k];
const int bot = rcMax(span.y, neighborSpan.y);
const int top = rcMin(span.y + span.h, neighborSpan.y + neighborSpan.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)
if ((top - bot) >= walkableHeight && rcAbs((int)neighborSpan.y - (int)span.y) <= walkableClimb)
{
// Mark direction as walkable.
const int lidx = k - (int)nc.index;
if (lidx < 0 || lidx > MAX_LAYERS)
const int layerIndex = k - (int)neighborCell.index;
if (layerIndex < 0 || layerIndex > MAX_LAYERS)
{
tooHighNeighbour = rcMax(tooHighNeighbour, lidx);
maxLayerIndex = rcMax(maxLayerIndex, layerIndex);
continue;
}
rcSetCon(s, dir, lidx);
rcSetCon(span, dir, layerIndex);
break;
}
}
}
}
}
}
if (tooHighNeighbour > MAX_LAYERS)
if (maxLayerIndex > MAX_LAYERS)
{
ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Heightfield has too many layers %d (max: %d)",
tooHighNeighbour, MAX_LAYERS);
context->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Heightfield has too many layers %d (max: %d)",
maxLayerIndex, 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;
}
*/
+5 -40
View File
@@ -16,12 +16,9 @@
// 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)
static void* rcAllocDefault(size_t size, rcAllocHint)
{
return malloc(size);
}
@@ -34,53 +31,21 @@ static void rcFreeDefault(void *ptr)
static rcAllocFunc* sRecastAllocFunc = rcAllocDefault;
static rcFreeFunc* sRecastFreeFunc = rcFreeDefault;
/// @see rcAlloc, rcFree
void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc)
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)
if (ptr != NULL)
{
sRecastFreeFunc(ptr);
}
}
/// @class rcIntArray
///
/// While it is possible to pre-allocate a specific array size during
/// construction or by using the #resize method, certain methods will
/// automatically resize the array as needed.
///
/// @warning The array memory is not initialized to zero when the size is
/// manually set during construction or when using #resize.
/// @par
///
/// Using this method ensures the array is at least large enough to hold
/// the specified number of elements. This can improve performance by
/// avoiding auto-resizing during use.
void rcIntArray::doResize(int n)
{
if (!m_cap) m_cap = n;
while (m_cap < n) m_cap *= 2;
int* newData = (int*)rcAlloc(m_cap*sizeof(int), RC_ALLOC_TEMP);
rcAssert(newData);
if (m_size && newData) memcpy(newData, m_data, m_size*sizeof(int));
rcFree(m_data);
m_data = newData;
}
-1
View File
@@ -17,7 +17,6 @@
//
#include <float.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include <string.h>
#include <stdlib.h>
+1 -1
View File
@@ -22,7 +22,7 @@
static rcAssertFailFunc* sRecastAssertFailFunc = 0;
void rcAssertFailSetCustom(rcAssertFailFunc *assertFailFunc)
void rcAssertFailSetCustom(rcAssertFailFunc* assertFailFunc)
{
sRecastAssertFailFunc = assertFailFunc;
}
+3 -4
View File
@@ -16,7 +16,6 @@
// 3. This notice may not be removed or altered from any source distribution.
//
#define _USE_MATH_DEFINES
#include <math.h>
#include <string.h>
#include <stdio.h>
@@ -921,8 +920,8 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
continue;
const unsigned char area = chf.areas[i];
verts.resize(0);
simplified.resize(0);
verts.clear();
simplified.clear();
ctx->startTimer(RC_TIMER_BUILD_CONTOURS_TRACE);
walkContour(x, y, i, chf, flags, verts);
@@ -1009,7 +1008,7 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
if (cset.nconts > 0)
{
// Calculate winding of all polygons.
rcScopedDelete<char> winding((char*)rcAlloc(sizeof(char)*cset.nconts, RC_ALLOC_TEMP));
rcScopedDelete<signed char> winding((signed char*)rcAlloc(sizeof(signed char)*cset.nconts, RC_ALLOC_TEMP));
if (!winding)
{
ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'hole' (%d).", cset.nconts);
+89 -107
View File
@@ -16,186 +16,168 @@
// 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);
#include <stdlib.h>
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)
void rcFilterLowHangingWalkableObstacles(rcContext* context, const int walkableClimb, rcHeightfield& heightfield)
{
rcAssert(context);
rcScopedTimer timer(context, RC_TIMER_FILTER_LOW_OBSTACLES);
const int xSize = heightfield.width;
const int zSize = heightfield.height;
for (int z = 0; z < zSize; ++z)
{
for (int x = 0; x < w; ++x)
for (int x = 0; x < xSize; ++x)
{
rcSpan* ps = 0;
bool previousWalkable = false;
rcSpan* previousSpan = NULL;
bool previousWasWalkable = false;
unsigned char previousArea = RC_NULL_AREA;
for (rcSpan* s = solid.spans[x + y*w]; s; ps = s, s = s->next)
for (rcSpan* span = heightfield.spans[x + z * xSize]; span != NULL; previousSpan = span, span = span->next)
{
const bool walkable = s->area != RC_NULL_AREA;
const bool walkable = span->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 (!walkable && previousWasWalkable)
{
if (rcAbs((int)s->smax - (int)ps->smax) <= walkableClimb)
s->area = previousArea;
if (rcAbs((int)span->smax - (int)previousSpan->smax) <= walkableClimb)
{
span->area = previousArea;
}
}
// Copy walkable flag so that it cannot propagate
// past multiple non-walkable objects.
previousWalkable = walkable;
previousArea = s->area;
previousWasWalkable = walkable;
previousArea = span->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)
void rcFilterLedgeSpans(rcContext* context, const int walkableHeight, const int walkableClimb,
rcHeightfield& heightfield)
{
rcAssert(ctx);
rcAssert(context);
rcScopedTimer timer(ctx, RC_TIMER_FILTER_BORDER);
rcScopedTimer timer(context, RC_TIMER_FILTER_BORDER);
const int w = solid.width;
const int h = solid.height;
const int MAX_HEIGHT = 0xffff;
const int xSize = heightfield.width;
const int zSize = heightfield.height;
const int MAX_HEIGHT = 0xffff; // TODO (graham): Move this to a more visible constant and update usages.
// Mark border spans.
for (int y = 0; y < h; ++y)
for (int z = 0; z < zSize; ++z)
{
for (int x = 0; x < w; ++x)
for (int x = 0; x < xSize; ++x)
{
for (rcSpan* s = solid.spans[x + y*w]; s; s = s->next)
for (rcSpan* span = heightfield.spans[x + z * xSize]; span; span = span->next)
{
// Skip non walkable spans.
if (s->area == RC_NULL_AREA)
if (span->area == RC_NULL_AREA)
{
continue;
const int bot = (int)(s->smax);
const int top = s->next ? (int)(s->next->smin) : MAX_HEIGHT;
}
const int bot = (int)(span->smax);
const int top = span->next ? (int)(span->next->smin) : MAX_HEIGHT;
// Find neighbours minimum height.
int minh = MAX_HEIGHT;
int minNeighborHeight = MAX_HEIGHT;
// Min and max height of accessible neighbours.
int asmin = s->smax;
int asmax = s->smax;
int accessibleNeighborMinHeight = span->smax;
int accessibleNeighborMaxHeight = span->smax;
for (int dir = 0; dir < 4; ++dir)
for (int direction = 0; direction < 4; ++direction)
{
int dx = x + rcGetDirOffsetX(dir);
int dy = y + rcGetDirOffsetY(dir);
int dx = x + rcGetDirOffsetX(direction);
int dy = z + rcGetDirOffsetY(direction);
// Skip neighbours which are out of bounds.
if (dx < 0 || dy < 0 || dx >= w || dy >= h)
if (dx < 0 || dy < 0 || dx >= xSize || dy >= zSize)
{
minh = rcMin(minh, -walkableClimb - bot);
minNeighborHeight = rcMin(minNeighborHeight, -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);
const rcSpan* neighborSpan = heightfield.spans[dx + dy * xSize];
int neighborBot = -walkableClimb;
int neighborTop = neighborSpan ? (int)neighborSpan->smin : MAX_HEIGHT;
// Rest of the spans.
for (ns = solid.spans[dx + dy*w]; ns; ns = ns->next)
// Skip neighbour if the gap between the spans is too small.
if (rcMin(top, neighborTop) - rcMax(bot, neighborBot) > walkableHeight)
{
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);
minNeighborHeight = rcMin(minNeighborHeight, neighborBot - bot);
}
// Rest of the spans.
for (neighborSpan = heightfield.spans[dx + dy * xSize]; neighborSpan; neighborSpan = neighborSpan->next)
{
neighborBot = (int)neighborSpan->smax;
neighborTop = neighborSpan->next ? (int)neighborSpan->next->smin : MAX_HEIGHT;
// Skip neighbour if the gap between the spans is too small.
if (rcMin(top, neighborTop) - rcMax(bot, neighborBot) > walkableHeight)
{
minNeighborHeight = rcMin(minNeighborHeight, neighborBot - bot);
// Find min/max accessible neighbour height.
if (rcAbs(nbot - bot) <= walkableClimb)
if (rcAbs(neighborBot - bot) <= walkableClimb)
{
if (nbot < asmin) asmin = nbot;
if (nbot > asmax) asmax = nbot;
if (neighborBot < accessibleNeighborMinHeight) accessibleNeighborMinHeight = neighborBot;
if (neighborBot > accessibleNeighborMaxHeight) accessibleNeighborMaxHeight = neighborBot;
}
}
}
}
// The current span is close to a ledge if the drop to any
// neighbour span is less than the walkableClimb.
if (minh < -walkableClimb)
if (minNeighborHeight < -walkableClimb)
{
s->area = RC_NULL_AREA;
span->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)
else if ((accessibleNeighborMaxHeight - accessibleNeighborMinHeight) > walkableClimb)
{
s->area = RC_NULL_AREA;
span->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)
void rcFilterWalkableLowHeightSpans(rcContext* context, const int walkableHeight, rcHeightfield& heightfield)
{
rcAssert(ctx);
rcAssert(context);
rcScopedTimer timer(ctx, RC_TIMER_FILTER_WALKABLE);
rcScopedTimer timer(context, RC_TIMER_FILTER_WALKABLE);
const int w = solid.width;
const int h = solid.height;
const int xSize = heightfield.width;
const int zSize = heightfield.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 z = 0; z < zSize; ++z)
{
for (int x = 0; x < w; ++x)
for (int x = 0; x < xSize; ++x)
{
for (rcSpan* s = solid.spans[x + y*w]; s; s = s->next)
for (rcSpan* span = heightfield.spans[x + z*xSize]; span; span = span->next)
{
const int bot = (int)(s->smax);
const int top = s->next ? (int)(s->next->smin) : MAX_HEIGHT;
const int bot = (int)(span->smax);
const int top = span->next ? (int)(span->next->smin) : MAX_HEIGHT;
if ((top - bot) <= walkableHeight)
s->area = RC_NULL_AREA;
{
span->area = RC_NULL_AREA;
}
}
}
}
-1
View File
@@ -17,7 +17,6 @@
//
#include <float.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include <string.h>
#include <stdlib.h>
-3
View File
@@ -16,7 +16,6 @@
// 3. This notice may not be removed or altered from any source distribution.
//
#define _USE_MATH_DEFINES
#include <math.h>
#include <string.h>
#include <stdio.h>
@@ -566,7 +565,6 @@ static bool canRemoveVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned sho
const int nvp = mesh.nvp;
// Count number of polygons to remove.
int numRemovedVerts = 0;
int numTouchedVerts = 0;
int numRemainingEdges = 0;
for (int i = 0; i < mesh.npolys; ++i)
@@ -586,7 +584,6 @@ static bool canRemoveVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned sho
}
if (numRemoved)
{
numRemovedVerts += numRemoved;
numRemainingEdges += numVerts-(numRemoved+1);
}
}
+16 -15
View File
@@ -17,7 +17,6 @@
//
#include <float.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include <string.h>
#include <stdlib.h>
@@ -284,7 +283,7 @@ static unsigned short getHeight(const float fx, const float fy, const float fz,
enum EdgeValues
{
EV_UNDEF = -1,
EV_HULL = -2,
EV_HULL = -2
};
static int findEdge(const int* edges, int nedges, int s, int t)
@@ -557,15 +556,16 @@ static float polyMinExtent(const float* verts, const int nverts)
inline int prev(int i, int n) { return i-1 >= 0 ? i-1 : n-1; }
inline int next(int i, int n) { return i+1 < n ? i+1 : 0; }
static void triangulateHull(const int /*nverts*/, const float* verts, const int nhull, const int* hull, rcIntArray& tris)
static void triangulateHull(const int /*nverts*/, const float* verts, const int nhull, const int* hull, const int nin, rcIntArray& tris)
{
int start = 0, left = 1, right = nhull-1;
// Start from an ear with shortest perimeter.
// This tends to favor well formed triangles as starting point.
float dmin = 0;
float dmin = FLT_MAX;
for (int i = 0; i < nhull; i++)
{
if (hull[i] >= nin) continue; // Ears are triangles with original vertices as middle vertex while others are actually line segments on edges
int pi = prev(i, nhull);
int ni = next(i, nhull);
const float* pv = &verts[hull[pi]*3];
@@ -652,8 +652,8 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin,
for (int i = 0; i < nin; ++i)
rcVcopy(&verts[i*3], &in[i*3]);
edges.resize(0);
tris.resize(0);
edges.clear();
tris.clear();
const float cs = chf.cs;
const float ics = 1.0f/cs;
@@ -770,7 +770,7 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin,
// If the polygon minimum extent is small (sliver or small triangle), do not try to add internal points.
if (minExtent < sampleDist*2)
{
triangulateHull(nverts, verts, nhull, hull, tris);
triangulateHull(nverts, verts, nhull, hull, nin, tris);
return true;
}
@@ -778,7 +778,7 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin,
// We're using the triangulateHull instead of delaunayHull as it tends to
// create a bit better triangulation for long thin triangles when there
// are no internal points.
triangulateHull(nverts, verts, nhull, hull, tris);
triangulateHull(nverts, verts, nhull, hull, nin, tris);
if (tris.size() == 0)
{
@@ -802,7 +802,7 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin,
int x1 = (int)ceilf(bmax[0]/sampleDist);
int z0 = (int)floorf(bmin[2]/sampleDist);
int z1 = (int)ceilf(bmax[2]/sampleDist);
samples.resize(0);
samples.clear();
for (int z = z0; z < z1; ++z)
{
for (int x = x0; x < x1; ++x)
@@ -863,8 +863,8 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin,
// Create new triangulation.
// TODO: Incremental add instead of full rebuild.
edges.resize(0);
tris.resize(0);
edges.clear();
tris.clear();
delaunayHull(ctx, nverts, verts, nhull, hull, tris, edges);
}
}
@@ -934,7 +934,7 @@ static void seedArrayWithPolyCenter(rcContext* ctx, const rcCompactHeightfield&
pcy /= npoly;
// Use seeds array as a stack for DFS
array.resize(0);
array.clear();
array.push(startCellX);
array.push(startCellY);
array.push(startSpanIndex);
@@ -1000,7 +1000,7 @@ static void seedArrayWithPolyCenter(rcContext* ctx, const rcCompactHeightfield&
rcSwap(dirs[directDir], dirs[3]);
}
array.resize(0);
array.clear();
// getHeightData seeds are given in coordinates with borders
array.push(cx+bs);
array.push(cy+bs);
@@ -1029,7 +1029,7 @@ static void getHeightData(rcContext* ctx, const rcCompactHeightfield& chf,
// Note: Reads to the compact heightfield are offset by border size (bs)
// since border size offset is already removed from the polymesh vertices.
queue.resize(0);
queue.clear();
// Set all heights to RC_UNSET_HEIGHT.
memset(hp.data, 0xff, sizeof(unsigned short)*hp.width*hp.height);
@@ -1140,7 +1140,8 @@ static void getHeightData(rcContext* ctx, const rcCompactHeightfield& chf,
static unsigned char getEdgeFlags(const float* va, const float* vb,
const float* vpoly, const int npoly)
{
// Return true if edge (va,vb) is part of the polygon.
// The flag returned by this function matches dtDetailTriEdgeFlags in Detour.
// Figure out if edge (va,vb) is part of the polygon boundary.
static const float thrSqr = rcSqr(0.001f);
for (int i = 0, j = npoly-1; i < npoly; j=i++)
{
+383 -281
View File
@@ -16,373 +16,485 @@
// 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)
/// Check whether two bounding boxes overlap
///
/// @param[in] aMin Min axis extents of bounding box A
/// @param[in] aMax Max axis extents of bounding box A
/// @param[in] bMin Min axis extents of bounding box B
/// @param[in] bMax Max axis extents of bounding box B
/// @returns true if the two bounding boxes overlap. False otherwise.
static 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;
return
aMin[0] <= bMax[0] && aMax[0] >= bMin[0] &&
aMin[1] <= bMax[1] && aMax[1] >= bMin[1] &&
aMin[2] <= bMax[2] && aMax[2] >= bMin[2];
}
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;
}
/// Allocates a new span in the heightfield.
/// Use a memory pool and free list to minimize actual allocations.
///
/// @param[in] hf The heightfield
/// @returns A pointer to the allocated or re-used span memory.
static rcSpan* allocSpan(rcHeightfield& hf)
{
// If running out of memory, allocate new page and update the freelist.
if (!hf.freelist || !hf.freelist->next)
// If necessary, allocate new page and update the freelist.
if (hf.freelist == NULL || hf.freelist->next == NULL)
{
// Create new page.
// Allocate memory for the new pool.
rcSpanPool* pool = (rcSpanPool*)rcAlloc(sizeof(rcSpanPool), RC_ALLOC_PERM);
if (!pool) return 0;
rcSpanPool* spanPool = (rcSpanPool*)rcAlloc(sizeof(rcSpanPool), RC_ALLOC_PERM);
if (spanPool == NULL)
{
return NULL;
}
// 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];
spanPool->next = hf.pools;
hf.pools = spanPool;
// Add new spans to the free list.
rcSpan* freeList = hf.freelist;
rcSpan* head = &spanPool->items[0];
rcSpan* it = &spanPool->items[RC_SPANS_PER_POOL];
do
{
--it;
it->next = freelist;
freelist = 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;
// Pop item from the front of the free list.
rcSpan* newSpan = hf.freelist;
hf.freelist = hf.freelist->next;
return it;
return newSpan;
}
static void freeSpan(rcHeightfield& hf, rcSpan* ptr)
/// Releases the memory used by the span back to the heightfield, so it can be re-used for new spans.
/// @param[in] hf The heightfield.
/// @param[in] span A pointer to the span to free
static void freeSpan(rcHeightfield& hf, rcSpan* span)
{
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])
if (span == NULL)
{
hf.spans[idx] = s;
return true;
return;
}
rcSpan* prev = 0;
rcSpan* cur = hf.spans[idx];
// Insert and merge spans.
while (cur)
// Add the span to the front of the free list.
span->next = hf.freelist;
hf.freelist = span;
}
/// Adds a span to the heightfield. If the new span overlaps existing spans,
/// it will merge the new span with the existing ones.
///
/// @param[in] hf Heightfield to add spans to
/// @param[in] x The new span's column cell x index
/// @param[in] z The new span's column cell z index
/// @param[in] min The new span's minimum cell index
/// @param[in] max The new span's maximum cell index
/// @param[in] areaID The new span's area type ID
/// @param[in] flagMergeThreshold How close two spans maximum extents need to be to merge area type IDs
static bool addSpan(rcHeightfield& hf,
const int x, const int z,
const unsigned short min, const unsigned short max,
const unsigned char areaID, const int flagMergeThreshold)
{
// Create the new span.
rcSpan* newSpan = allocSpan(hf);
if (newSpan == NULL)
{
if (cur->smin > s->smax)
return false;
}
newSpan->smin = min;
newSpan->smax = max;
newSpan->area = areaID;
newSpan->next = NULL;
const int columnIndex = x + z * hf.width;
rcSpan* previousSpan = NULL;
rcSpan* currentSpan = hf.spans[columnIndex];
// Insert the new span, possibly merging it with existing spans.
while (currentSpan != NULL)
{
if (currentSpan->smin > newSpan->smax)
{
// Current span is further than the new span, break.
// Current span is completely after the new span, break.
break;
}
else if (cur->smax < s->smin)
if (currentSpan->smax < newSpan->smin)
{
// Current span is before the new span advance.
prev = cur;
cur = cur->next;
// Current span is completely before the new span. Keep going.
previousSpan = currentSpan;
currentSpan = currentSpan->next;
}
else
{
// Merge spans.
if (cur->smin < s->smin)
s->smin = cur->smin;
if (cur->smax > s->smax)
s->smax = cur->smax;
// The new span overlaps with an existing span. Merge them.
if (currentSpan->smin < newSpan->smin)
{
newSpan->smin = currentSpan->smin;
}
if (currentSpan->smax > newSpan->smax)
{
newSpan->smax = currentSpan->smax;
}
// Merge flags.
if (rcAbs((int)s->smax - (int)cur->smax) <= flagMergeThr)
s->area = rcMax(s->area, cur->area);
if (rcAbs((int)newSpan->smax - (int)currentSpan->smax) <= flagMergeThreshold)
{
// Higher area ID numbers indicate higher resolution priority.
newSpan->area = rcMax(newSpan->area, currentSpan->area);
}
// Remove current span.
rcSpan* next = cur->next;
freeSpan(hf, cur);
if (prev)
prev->next = next;
// Remove the current span since it's now merged with newSpan.
// Keep going because there might be other overlapping spans that also need to be merged.
rcSpan* next = currentSpan->next;
freeSpan(hf, currentSpan);
if (previousSpan)
{
previousSpan->next = next;
}
else
hf.spans[idx] = next;
cur = next;
{
hf.spans[columnIndex] = next;
}
currentSpan = next;
}
}
// Insert new span.
if (prev)
// Insert new span after prev
if (previousSpan != NULL)
{
s->next = prev->next;
prev->next = s;
newSpan->next = previousSpan->next;
previousSpan->next = newSpan;
}
else
{
s->next = hf.spans[idx];
hf.spans[idx] = s;
// This span should go before the others in the list
newSpan->next = hf.spans[columnIndex];
hf.spans[columnIndex] = newSpan;
}
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)
bool rcAddSpan(rcContext* context, rcHeightfield& heightfield,
const int x, const int z,
const unsigned short spanMin, const unsigned short spanMax,
const unsigned char areaID, const int flagMergeThreshold)
{
rcAssert(ctx);
rcAssert(context);
if (!addSpan(hf, x, y, smin, smax, area, flagMergeThr))
if (!addSpan(heightfield, x, z, spanMin, spanMax, areaID, flagMergeThreshold))
{
ctx->log(RC_LOG_ERROR, "rcAddSpan: Out of memory.");
context->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)
enum rcAxis
{
float d[12];
for (int i = 0; i < nin; ++i)
d[i] = x - in[i*3+axis];
RC_AXIS_X = 0,
RC_AXIS_Y = 1,
RC_AXIS_Z = 2
};
int m = 0, n = 0;
for (int i = 0, j = nin-1; i < nin; j=i, ++i)
/// Divides a convex polygon of max 12 vertices into two convex polygons
/// across a separating axis.
///
/// @param[in] inVerts The input polygon vertices
/// @param[in] inVertsCount The number of input polygon vertices
/// @param[out] outVerts1 Resulting polygon 1's vertices
/// @param[out] outVerts1Count The number of resulting polygon 1 vertices
/// @param[out] outVerts2 Resulting polygon 2's vertices
/// @param[out] outVerts2Count The number of resulting polygon 2 vertices
/// @param[in] axisOffset THe offset along the specified axis
/// @param[in] axis The separating axis
static void dividePoly(const float* inVerts, int inVertsCount,
float* outVerts1, int* outVerts1Count,
float* outVerts2, int* outVerts2Count,
float axisOffset, rcAxis axis)
{
rcAssert(inVertsCount <= 12);
// How far positive or negative away from the separating axis is each vertex.
float inVertAxisDelta[12];
for (int inVert = 0; inVert < inVertsCount; ++inVert)
{
bool ina = d[j] >= 0;
bool inb = d[i] >= 0;
if (ina != inb)
inVertAxisDelta[inVert] = axisOffset - inVerts[inVert * 3 + axis];
}
int poly1Vert = 0;
int poly2Vert = 0;
for (int inVertA = 0, inVertB = inVertsCount - 1; inVertA < inVertsCount; inVertB = inVertA, ++inVertA)
{
// If the two vertices are on the same side of the separating axis
bool sameSide = (inVertAxisDelta[inVertA] >= 0) == (inVertAxisDelta[inVertB] >= 0);
if (!sameSide)
{
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
float s = inVertAxisDelta[inVertB] / (inVertAxisDelta[inVertB] - inVertAxisDelta[inVertA]);
outVerts1[poly1Vert * 3 + 0] = inVerts[inVertB * 3 + 0] + (inVerts[inVertA * 3 + 0] - inVerts[inVertB * 3 + 0]) * s;
outVerts1[poly1Vert * 3 + 1] = inVerts[inVertB * 3 + 1] + (inVerts[inVertA * 3 + 1] - inVerts[inVertB * 3 + 1]) * s;
outVerts1[poly1Vert * 3 + 2] = inVerts[inVertB * 3 + 2] + (inVerts[inVertA * 3 + 2] - inVerts[inVertB * 3 + 2]) * s;
rcVcopy(&outVerts2[poly2Vert * 3], &outVerts1[poly1Vert * 3]);
poly1Vert++;
poly2Vert++;
// add the inVertA 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)
if (inVertAxisDelta[inVertA] > 0)
{
rcVcopy(out1 + m*3, in + i*3);
m++;
rcVcopy(&outVerts1[poly1Vert * 3], &inVerts[inVertA * 3]);
poly1Vert++;
}
else if (d[i] < 0)
else if (inVertAxisDelta[inVertA] < 0)
{
rcVcopy(out2 + n*3, in + i*3);
n++;
rcVcopy(&outVerts2[poly2Vert * 3], &inVerts[inVertA * 3]);
poly2Vert++;
}
}
else // same side
else
{
// add the i'th point to the right polygon. Addition is done even for points on the dividing line
if (d[i] >= 0)
// add the inVertA point to the right polygon. Addition is done even for points on the dividing line
if (inVertAxisDelta[inVertA] >= 0)
{
rcVcopy(out1 + m*3, in + i*3);
m++;
if (d[i] != 0)
rcVcopy(&outVerts1[poly1Vert * 3], &inVerts[inVertA * 3]);
poly1Vert++;
if (inVertAxisDelta[inVertA] != 0)
{
continue;
}
}
rcVcopy(out2 + n*3, in + i*3);
n++;
rcVcopy(&outVerts2[poly2Vert * 3], &inVerts[inVertA * 3]);
poly2Vert++;
}
}
*nout1 = m;
*nout2 = n;
*outVerts1Count = poly1Vert;
*outVerts2Count = poly2Vert;
}
/// Rasterize a single triangle to the heightfield.
///
/// This code is extremely hot, so much care should be given to maintaining maximum perf here.
///
/// @param[in] v0 Triangle vertex 0
/// @param[in] v1 Triangle vertex 1
/// @param[in] v2 Triangle vertex 2
/// @param[in] areaID The area ID to assign to the rasterized spans
/// @param[in] hf Heightfield to rasterize into
/// @param[in] hfBBMin The min extents of the heightfield bounding box
/// @param[in] hfBBMax The max extents of the heightfield bounding box
/// @param[in] cellSize The x and z axis size of a voxel in the heightfield
/// @param[in] inverseCellSize 1 / cellSize
/// @param[in] inverseCellHeight 1 / cellHeight
/// @param[in] flagMergeThreshold The threshold in which area flags will be merged
/// @returns true if the operation completes successfully. false if there was an error adding spans to the heightfield.
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 unsigned char areaID, rcHeightfield& hf,
const float* hfBBMin, const float* hfBBMax,
const float cellSize, const float inverseCellSize, const float inverseCellHeight,
const int flagMergeThreshold)
{
// Calculate the bounding box of the triangle.
float triBBMin[3];
rcVcopy(triBBMin, v0);
rcVmin(triBBMin, v1);
rcVmin(triBBMin, v2);
float triBBMax[3];
rcVcopy(triBBMax, v0);
rcVmax(triBBMax, v1);
rcVmax(triBBMax, v2);
// If the triangle does not touch the bounding box of the heightfield, skip the triangle.
if (!overlapBounds(triBBMin, triBBMax, hfBBMin, hfBBMax))
{
return true;
}
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);
const float by = hfBBMax[1] - hfBBMin[1];
// Calculate the footprint of the triangle on the grid's z-axis
int z0 = (int)((triBBMin[2] - hfBBMin[2]) * inverseCellSize);
int z1 = (int)((triBBMax[2] - hfBBMin[2]) * inverseCellSize);
// use -1 rather than 0 to cut the polygon properly at the start of the tile
z0 = rcClamp(z0, -1, h - 1);
z1 = rcClamp(z1, 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;
float buf[7 * 3 * 4];
float* in = buf;
float* inRow = buf + 7 * 3;
float* p1 = inRow + 7 * 3;
float* 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)
rcVcopy(&in[1 * 3], v1);
rcVcopy(&in[2 * 3], v2);
int nvRow;
int nvIn = 3;
for (int z = z0; z <= z1; ++z)
{
// 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);
const float cellZ = hfBBMin[2] + (float)z * cellSize;
dividePoly(in, nvIn, inRow, &nvRow, p1, &nvIn, cellZ + cellSize, RC_AXIS_Z);
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 (nvRow < 3)
{
if (minX > inrow[i*3]) minX = inrow[i*3];
if (maxX < inrow[i*3]) maxX = inrow[i*3];
continue;
}
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);
if (z < 0)
{
continue;
}
// find X-axis bounds of the row
float minX = inRow[0];
float maxX = inRow[0];
for (int vert = 1; vert < nvRow; ++vert)
{
if (minX > inRow[vert * 3])
{
minX = inRow[vert * 3];
}
if (maxX < inRow[vert * 3])
{
maxX = inRow[vert * 3];
}
}
int x0 = (int)((minX - hfBBMin[0]) * inverseCellSize);
int x1 = (int)((maxX - hfBBMin[0]) * inverseCellSize);
if (x1 < 0 || x0 >= w)
{
continue;
}
x0 = rcClamp(x0, -1, w - 1);
x1 = rcClamp(x1, 0, w - 1);
int nv, nv2 = nvrow;
int nv;
int 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;
const float cx = hfBBMin[0] + (float)x * cellSize;
dividePoly(inRow, nv2, p1, &nv, p2, &nv2, cx + cellSize, RC_AXIS_X);
rcSwap(inRow, p2);
if (nv < 3)
{
continue;
}
if (x < 0)
{
continue;
}
// Calculate min and max of the span.
float smin = p1[1], smax = p1[1];
for (int i = 1; i < nv; ++i)
float spanMin = p1[1];
float spanMax = p1[1];
for (int vert = 1; vert < nv; ++vert)
{
smin = rcMin(smin, p1[i*3+1]);
smax = rcMax(smax, p1[i*3+1]);
spanMin = rcMin(spanMin, p1[vert * 3 + 1]);
spanMax = rcMax(spanMax, p1[vert * 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;
spanMin -= hfBBMin[1];
spanMax -= hfBBMin[1];
// Skip the span if it's completely outside the heightfield bounding box
if (spanMax < 0.0f)
{
continue;
}
if (spanMin > by)
{
continue;
}
// Clamp the span to the heightfield bounding box.
if (spanMin < 0.0f)
{
spanMin = 0;
}
if (spanMax > by)
{
spanMax = 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))
unsigned short spanMinCellIndex = (unsigned short)rcClamp((int)floorf(spanMin * inverseCellHeight), 0, RC_SPAN_MAX_HEIGHT);
unsigned short spanMaxCellIndex = (unsigned short)rcClamp((int)ceilf(spanMax * inverseCellHeight), (int)spanMinCellIndex + 1, RC_SPAN_MAX_HEIGHT);
if (!addSpan(hf, x, z, spanMinCellIndex, spanMaxCellIndex, areaID, flagMergeThreshold))
{
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)
bool rcRasterizeTriangle(rcContext* context,
const float* v0, const float* v1, const float* v2,
const unsigned char areaID, rcHeightfield& heightfield, const int flagMergeThreshold)
{
rcAssert(ctx);
rcAssert(context != NULL);
rcScopedTimer timer(ctx, RC_TIMER_RASTERIZE_TRIANGLES);
rcScopedTimer timer(context, 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))
// Rasterize the single triangle.
const float inverseCellSize = 1.0f / heightfield.cs;
const float inverseCellHeight = 1.0f / heightfield.ch;
if (!rasterizeTri(v0, v1, v2, areaID, heightfield, heightfield.bmin, heightfield.bmax, heightfield.cs, inverseCellSize, inverseCellHeight, flagMergeThreshold))
{
ctx->log(RC_LOG_ERROR, "rcRasterizeTriangle: Out of memory.");
context->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)
bool rcRasterizeTriangles(rcContext* context,
const float* verts, const int /*nv*/,
const int* tris, const unsigned char* triAreaIDs, const int numTris,
rcHeightfield& heightfield, const int flagMergeThreshold)
{
rcAssert(ctx);
rcAssert(context != NULL);
rcScopedTimer timer(ctx, RC_TIMER_RASTERIZE_TRIANGLES);
rcScopedTimer timer(context, 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)
// Rasterize the triangles.
const float inverseCellSize = 1.0f / heightfield.cs;
const float inverseCellHeight = 1.0f / heightfield.ch;
for (int triIndex = 0; triIndex < numTris; ++triIndex)
{
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))
const float* v0 = &verts[tris[triIndex * 3 + 0] * 3];
const float* v1 = &verts[tris[triIndex * 3 + 1] * 3];
const float* v2 = &verts[tris[triIndex * 3 + 2] * 3];
if (!rasterizeTri(v0, v1, v2, triAreaIDs[triIndex], heightfield, heightfield.bmin, heightfield.bmax, heightfield.cs, inverseCellSize, inverseCellHeight, flagMergeThreshold))
{
ctx->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory.");
context->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory.");
return false;
}
}
@@ -390,31 +502,26 @@ bool rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/,
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)
bool rcRasterizeTriangles(rcContext* context,
const float* verts, const int /*nv*/,
const unsigned short* tris, const unsigned char* triAreaIDs, const int numTris,
rcHeightfield& heightfield, const int flagMergeThreshold)
{
rcAssert(ctx);
rcAssert(context != NULL);
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)
rcScopedTimer timer(context, RC_TIMER_RASTERIZE_TRIANGLES);
// Rasterize the triangles.
const float inverseCellSize = 1.0f / heightfield.cs;
const float inverseCellHeight = 1.0f / heightfield.ch;
for (int triIndex = 0; triIndex < numTris; ++triIndex)
{
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))
const float* v0 = &verts[tris[triIndex * 3 + 0] * 3];
const float* v1 = &verts[tris[triIndex * 3 + 1] * 3];
const float* v2 = &verts[tris[triIndex * 3 + 2] * 3];
if (!rasterizeTri(v0, v1, v2, triAreaIDs[triIndex], heightfield, heightfield.bmin, heightfield.bmax, heightfield.cs, inverseCellSize, inverseCellHeight, flagMergeThreshold))
{
ctx->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory.");
context->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory.");
return false;
}
}
@@ -422,30 +529,25 @@ bool rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/,
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)
bool rcRasterizeTriangles(rcContext* context,
const float* verts, const unsigned char* triAreaIDs, const int numTris,
rcHeightfield& heightfield, const int flagMergeThreshold)
{
rcAssert(ctx);
rcAssert(context != NULL);
rcScopedTimer timer(context, RC_TIMER_RASTERIZE_TRIANGLES);
rcScopedTimer timer(ctx, RC_TIMER_RASTERIZE_TRIANGLES);
const float ics = 1.0f/solid.cs;
const float ich = 1.0f/solid.ch;
// Rasterize triangles.
for (int i = 0; i < nt; ++i)
// Rasterize the triangles.
const float inverseCellSize = 1.0f / heightfield.cs;
const float inverseCellHeight = 1.0f / heightfield.ch;
for (int triIndex = 0; triIndex < numTris; ++triIndex)
{
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))
const float* v0 = &verts[(triIndex * 3 + 0) * 3];
const float* v1 = &verts[(triIndex * 3 + 1) * 3];
const float* v2 = &verts[(triIndex * 3 + 2) * 3];
if (!rasterizeTri(v0, v1, v2, triAreaIDs[triIndex], heightfield, heightfield.bmin, heightfield.bmax, heightfield.cs, inverseCellSize, inverseCellHeight, flagMergeThreshold))
{
ctx->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory.");
context->log(RC_LOG_ERROR, "rcRasterizeTriangles: Out of memory.");
return false;
}
}
+98 -111
View File
@@ -17,7 +17,6 @@
//
#include <float.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include <string.h>
#include <stdlib.h>
@@ -25,8 +24,17 @@
#include "Recast.h"
#include "RecastAlloc.h"
#include "RecastAssert.h"
#include <new>
namespace
{
struct LevelStackEntry
{
LevelStackEntry(int x_, int y_, int index_) : x(x_), y(y_), index(index_) {}
int x;
int y;
int index;
};
} // namespace
static void calculateDistanceField(rcCompactHeightfield& chf, unsigned short* src, unsigned short& maxDist)
{
@@ -245,17 +253,15 @@ static bool floodRegion(int x, int y, int i,
unsigned short level, unsigned short r,
rcCompactHeightfield& chf,
unsigned short* srcReg, unsigned short* srcDist,
rcIntArray& stack)
rcTempVector<LevelStackEntry>& stack)
{
const int w = chf.width;
const unsigned char area = chf.areas[i];
// Flood fill mark region.
stack.resize(0);
stack.push((int)x);
stack.push((int)y);
stack.push((int)i);
stack.clear();
stack.push_back(LevelStackEntry(x, y, i));
srcReg[i] = r;
srcDist[i] = 0;
@@ -264,9 +270,11 @@ static bool floodRegion(int x, int y, int i,
while (stack.size() > 0)
{
int ci = stack.pop();
int cy = stack.pop();
int cx = stack.pop();
LevelStackEntry& back = stack.back();
int cx = back.x;
int cy = back.y;
int ci = back.index;
stack.pop_back();
const rcCompactSpan& cs = chf.spans[ci];
@@ -332,9 +340,7 @@ static bool floodRegion(int x, int y, int i,
{
srcReg[ai] = r;
srcDist[ai] = 0;
stack.push(ax);
stack.push(ay);
stack.push(ai);
stack.push_back(LevelStackEntry(ax, ay, ai));
}
}
}
@@ -343,12 +349,20 @@ static bool floodRegion(int x, int y, int i,
return count > 0;
}
static unsigned short* expandRegions(int maxIter, unsigned short level,
rcCompactHeightfield& chf,
unsigned short* srcReg, unsigned short* srcDist,
unsigned short* dstReg, unsigned short* dstDist,
rcIntArray& stack,
bool fillStack)
// Struct to keep track of entries in the region table that have been changed.
struct DirtyEntry
{
DirtyEntry(int index_, unsigned short region_, unsigned short distance2_)
: index(index_), region(region_), distance2(distance2_) {}
int index;
unsigned short region;
unsigned short distance2;
};
static void expandRegions(int maxIter, unsigned short level,
rcCompactHeightfield& chf,
unsigned short* srcReg, unsigned short* srcDist,
rcTempVector<LevelStackEntry>& stack,
bool fillStack)
{
const int w = chf.width;
const int h = chf.height;
@@ -356,7 +370,7 @@ static unsigned short* expandRegions(int maxIter, unsigned short level,
if (fillStack)
{
// Find cells revealed by the raised level.
stack.resize(0);
stack.clear();
for (int y = 0; y < h; ++y)
{
for (int x = 0; x < w; ++x)
@@ -366,9 +380,7 @@ static unsigned short* expandRegions(int maxIter, unsigned short level,
{
if (chf.dist[i] >= level && srcReg[i] == 0 && chf.areas[i] != RC_NULL_AREA)
{
stack.push(x);
stack.push(y);
stack.push(i);
stack.push_back(LevelStackEntry(x, y, i));
}
}
}
@@ -377,27 +389,26 @@ static unsigned short* expandRegions(int maxIter, unsigned short level,
else // use cells in the input stack
{
// mark all cells which already have a region
for (int j=0; j<stack.size(); j+=3)
for (int j=0; j<stack.size(); j++)
{
int i = stack[j+2];
int i = stack[j].index;
if (srcReg[i] != 0)
stack[j+2] = -1;
stack[j].index = -1;
}
}
rcTempVector<DirtyEntry> dirtyEntries;
int iter = 0;
while (stack.size() > 0)
{
int failed = 0;
dirtyEntries.clear();
memcpy(dstReg, srcReg, sizeof(unsigned short)*chf.spanCount);
memcpy(dstDist, srcDist, sizeof(unsigned short)*chf.spanCount);
for (int j = 0; j < stack.size(); j += 3)
for (int j = 0; j < stack.size(); j++)
{
int x = stack[j+0];
int y = stack[j+1];
int i = stack[j+2];
int x = stack[j].x;
int y = stack[j].y;
int i = stack[j].index;
if (i < 0)
{
failed++;
@@ -426,9 +437,8 @@ static unsigned short* expandRegions(int maxIter, unsigned short level,
}
if (r)
{
stack[j+2] = -1; // mark as used
dstReg[i] = r;
dstDist[i] = d2;
stack[j].index = -1; // mark as used
dirtyEntries.push_back(DirtyEntry(i, r, d2));
}
else
{
@@ -436,11 +446,14 @@ static unsigned short* expandRegions(int maxIter, unsigned short level,
}
}
// rcSwap source and dest.
rcSwap(srcReg, dstReg);
rcSwap(srcDist, dstDist);
// Copy entries that differ between src and dst to keep them in sync.
for (int i = 0; i < dirtyEntries.size(); i++) {
int idx = dirtyEntries[i].index;
srcReg[idx] = dirtyEntries[i].region;
srcDist[idx] = dirtyEntries[i].distance2;
}
if (failed*3 == stack.size())
if (failed == stack.size())
break;
if (level > 0)
@@ -450,16 +463,14 @@ static unsigned short* expandRegions(int maxIter, unsigned short level,
break;
}
}
return srcReg;
}
static void sortCellsByLevel(unsigned short startLevel,
rcCompactHeightfield& chf,
unsigned short* srcReg,
unsigned int nbStacks, rcIntArray* stacks,
const unsigned short* srcReg,
unsigned int nbStacks, rcTempVector<LevelStackEntry>* stacks,
unsigned short loglevelsPerStack) // the levels per stack (2 in our case) as a bit shift
{
const int w = chf.width;
@@ -467,7 +478,7 @@ static void sortCellsByLevel(unsigned short startLevel,
startLevel = startLevel >> loglevelsPerStack;
for (unsigned int j=0; j<nbStacks; ++j)
stacks[j].resize(0);
stacks[j].clear();
// put all cells in the level range into the appropriate stacks
for (int y = 0; y < h; ++y)
@@ -487,26 +498,23 @@ static void sortCellsByLevel(unsigned short startLevel,
if (sId < 0)
sId = 0;
stacks[sId].push(x);
stacks[sId].push(y);
stacks[sId].push(i);
stacks[sId].push_back(LevelStackEntry(x, y, i));
}
}
}
}
static void appendStacks(rcIntArray& srcStack, rcIntArray& dstStack,
unsigned short* srcReg)
static void appendStacks(const rcTempVector<LevelStackEntry>& srcStack,
rcTempVector<LevelStackEntry>& dstStack,
const unsigned short* srcReg)
{
for (int j=0; j<srcStack.size(); j+=3)
for (int j=0; j<srcStack.size(); j++)
{
int i = srcStack[j+2];
int i = srcStack[j].index;
if ((i < 0) || (srcReg[i] != 0))
continue;
dstStack.push(srcStack[j]);
dstStack.push(srcStack[j+1]);
dstStack.push(srcStack[j+2]);
dstStack.push_back(srcStack[j]);
}
}
@@ -641,7 +649,7 @@ static bool mergeRegions(rcRegion& rega, rcRegion& regb)
return false;
// Merge neighbours.
rega.connections.resize(0);
rega.connections.clear();
for (int i = 0, ni = acon.size(); i < ni-1; ++i)
rega.connections.push(acon[(insa+1+i) % ni]);
@@ -671,7 +679,7 @@ static bool isRegionConnectedToBorder(const rcRegion& reg)
return false;
}
static bool isSolidEdge(rcCompactHeightfield& chf, unsigned short* srcReg,
static bool isSolidEdge(rcCompactHeightfield& chf, const unsigned short* srcReg,
int x, int y, int i, int dir)
{
const rcCompactSpan& s = chf.spans[i];
@@ -690,7 +698,7 @@ static bool isSolidEdge(rcCompactHeightfield& chf, unsigned short* srcReg,
static void walkContour(int x, int y, int i, int dir,
rcCompactHeightfield& chf,
unsigned short* srcReg,
const unsigned short* srcReg,
rcIntArray& cont)
{
int startDir = dir;
@@ -786,16 +794,15 @@ static bool mergeAndFilterRegions(rcContext* ctx, int minRegionArea, int mergeRe
const int h = chf.height;
const int nreg = maxRegionId+1;
rcRegion* regions = (rcRegion*)rcAlloc(sizeof(rcRegion)*nreg, RC_ALLOC_TEMP);
if (!regions)
{
rcTempVector<rcRegion> regions;
if (!regions.reserve(nreg)) {
ctx->log(RC_LOG_ERROR, "mergeAndFilterRegions: Out of memory 'regions' (%d).", nreg);
return false;
}
// Construct regions
for (int i = 0; i < nreg; ++i)
new(&regions[i]) rcRegion((unsigned short)i);
regions.push_back(rcRegion((unsigned short) i));
// Find edge of a region and find connections around the contour.
for (int y = 0; y < h; ++y)
@@ -868,8 +875,8 @@ static bool mergeAndFilterRegions(rcContext* ctx, int minRegionArea, int mergeRe
// Also keep track of the regions connects to a tile border.
bool connectsToBorder = false;
int spanCount = 0;
stack.resize(0);
trace.resize(0);
stack.clear();
trace.clear();
reg.visited = true;
stack.push(i);
@@ -1021,11 +1028,6 @@ static bool mergeAndFilterRegions(rcContext* ctx, int minRegionArea, int mergeRe
if (regions[i].overlap)
overlaps.push(regions[i].id);
for (int i = 0; i < nreg; ++i)
regions[i].~rcRegion();
rcFree(regions);
return true;
}
@@ -1041,22 +1043,21 @@ static void addUniqueConnection(rcRegion& reg, int n)
static bool mergeAndFilterLayerRegions(rcContext* ctx, int minRegionArea,
unsigned short& maxRegionId,
rcCompactHeightfield& chf,
unsigned short* srcReg, rcIntArray& /*overlaps*/)
unsigned short* srcReg)
{
const int w = chf.width;
const int h = chf.height;
const int nreg = maxRegionId+1;
rcRegion* regions = (rcRegion*)rcAlloc(sizeof(rcRegion)*nreg, RC_ALLOC_TEMP);
if (!regions)
{
rcTempVector<rcRegion> regions;
// Construct regions
if (!regions.reserve(nreg)) {
ctx->log(RC_LOG_ERROR, "mergeAndFilterLayerRegions: Out of memory 'regions' (%d).", nreg);
return false;
}
// Construct regions
for (int i = 0; i < nreg; ++i)
new(&regions[i]) rcRegion((unsigned short)i);
regions.push_back(rcRegion((unsigned short) i));
// Find region neighbours and overlapping regions.
rcIntArray lregs(32);
@@ -1066,7 +1067,7 @@ static bool mergeAndFilterLayerRegions(rcContext* ctx, int minRegionArea,
{
const rcCompactCell& c = chf.cells[x+y*w];
lregs.resize(0);
lregs.clear();
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
{
@@ -1137,7 +1138,7 @@ static bool mergeAndFilterLayerRegions(rcContext* ctx, int minRegionArea,
// Start search.
root.id = layerId;
stack.resize(0);
stack.clear();
stack.push(i);
while (stack.size() > 0)
@@ -1234,10 +1235,6 @@ static bool mergeAndFilterLayerRegions(rcContext* ctx, int minRegionArea,
srcReg[i] = regions[srcReg[i]].id;
}
for (int i = 0; i < nreg; ++i)
regions[i].~rcRegion();
rcFree(regions);
return true;
}
@@ -1391,9 +1388,9 @@ bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf,
paintRectRegion(w-bw, w, 0, h, id|RC_BORDER_REG, chf, srcReg); id++;
paintRectRegion(0, w, 0, bh, id|RC_BORDER_REG, chf, srcReg); id++;
paintRectRegion(0, w, h-bh, h, id|RC_BORDER_REG, chf, srcReg); id++;
chf.borderSize = borderSize;
}
chf.borderSize = borderSize;
rcIntArray prev(256);
@@ -1535,7 +1532,7 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
const int w = chf.width;
const int h = chf.height;
rcScopedDelete<unsigned short> buf((unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount*4, RC_ALLOC_TEMP));
rcScopedDelete<unsigned short> buf((unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount*2, RC_ALLOC_TEMP));
if (!buf)
{
ctx->log(RC_LOG_ERROR, "rcBuildRegions: Out of memory 'tmp' (%d).", chf.spanCount*4);
@@ -1546,17 +1543,15 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
const int LOG_NB_STACKS = 3;
const int NB_STACKS = 1 << LOG_NB_STACKS;
rcIntArray lvlStacks[NB_STACKS];
rcTempVector<LevelStackEntry> lvlStacks[NB_STACKS];
for (int i=0; i<NB_STACKS; ++i)
lvlStacks[i].resize(1024);
lvlStacks[i].reserve(256);
rcIntArray stack(1024);
rcIntArray visited(1024);
rcTempVector<LevelStackEntry> stack;
stack.reserve(256);
unsigned short* srcReg = buf;
unsigned short* srcDist = buf+chf.spanCount;
unsigned short* dstReg = buf+chf.spanCount*2;
unsigned short* dstDist = buf+chf.spanCount*3;
memset(srcReg, 0, sizeof(unsigned short)*chf.spanCount);
memset(srcDist, 0, sizeof(unsigned short)*chf.spanCount);
@@ -1581,9 +1576,9 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
paintRectRegion(w-bw, w, 0, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++;
paintRectRegion(0, w, 0, bh, regionId|RC_BORDER_REG, chf, srcReg); regionId++;
paintRectRegion(0, w, h-bh, h, regionId|RC_BORDER_REG, chf, srcReg); regionId++;
chf.borderSize = borderSize;
}
chf.borderSize = borderSize;
int sId = -1;
while (level > 0)
@@ -1604,22 +1599,19 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
rcScopedTimer timerExpand(ctx, RC_TIMER_BUILD_REGIONS_EXPAND);
// Expand current regions until no empty connected cells found.
if (expandRegions(expandIters, level, chf, srcReg, srcDist, dstReg, dstDist, lvlStacks[sId], false) != srcReg)
{
rcSwap(srcReg, dstReg);
rcSwap(srcDist, dstDist);
}
expandRegions(expandIters, level, chf, srcReg, srcDist, lvlStacks[sId], false);
}
{
rcScopedTimer timerFloor(ctx, RC_TIMER_BUILD_REGIONS_FLOOD);
// Mark new regions with IDs.
for (int j = 0; j<lvlStacks[sId].size(); j += 3)
for (int j = 0; j<lvlStacks[sId].size(); j++)
{
int x = lvlStacks[sId][j];
int y = lvlStacks[sId][j+1];
int i = lvlStacks[sId][j+2];
LevelStackEntry current = lvlStacks[sId][j];
int x = current.x;
int y = current.y;
int i = current.index;
if (i >= 0 && srcReg[i] == 0)
{
if (floodRegion(x, y, i, level, regionId, chf, srcReg, srcDist, stack))
@@ -1638,11 +1630,7 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
}
// Expand current regions until no empty connected cells found.
if (expandRegions(expandIters*8, 0, chf, srcReg, srcDist, dstReg, dstDist, stack, true) != srcReg)
{
rcSwap(srcReg, dstReg);
rcSwap(srcDist, dstDist);
}
expandRegions(expandIters*8, 0, chf, srcReg, srcDist, stack, true);
ctx->stopTimer(RC_TIMER_BUILD_REGIONS_WATERSHED);
@@ -1709,9 +1697,9 @@ bool rcBuildLayerRegions(rcContext* ctx, rcCompactHeightfield& chf,
paintRectRegion(w-bw, w, 0, h, id|RC_BORDER_REG, chf, srcReg); id++;
paintRectRegion(0, w, 0, bh, id|RC_BORDER_REG, chf, srcReg); id++;
paintRectRegion(0, w, h-bh, h, id|RC_BORDER_REG, chf, srcReg); id++;
chf.borderSize = borderSize;
}
chf.borderSize = borderSize;
rcIntArray prev(256);
@@ -1809,9 +1797,8 @@ bool rcBuildLayerRegions(rcContext* ctx, rcCompactHeightfield& chf,
rcScopedTimer timerFilter(ctx, RC_TIMER_BUILD_REGIONS_FILTER);
// Merge monotone regions to layers and remove small regions.
rcIntArray overlaps;
chf.maxRegions = id;
if (!mergeAndFilterLayerRegions(ctx, minRegionArea, chf.maxRegions, chf, srcReg, overlaps))
if (!mergeAndFilterLayerRegions(ctx, minRegionArea, chf.maxRegions, chf, srcReg))
return false;
}
+1
View File
@@ -1 +1,2 @@
Recast.app
SDL2.framework
File diff suppressed because it is too large Load Diff
+87
View File
@@ -0,0 +1,87 @@
file(GLOB SOURCES Source/*.cpp Contrib/fastlz/fastlz.c)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
if(NOT SDL2_ROOT_DIR)
if(WIN32)
set(SDL2_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Contrib/SDL")
elseif(APPLE)
set(SDL2_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Bin")
endif()
endif()
find_package(OpenGL REQUIRED)
if(APPLE)
find_library(SDL2_LIBRARY
NAMES SDL2
PATHS ${SDL2_ROOT_DIR}
REQUIRED)
else()
find_package(SDL2 REQUIRED)
endif()
include_directories(SYSTEM ${OPENGL_INCLUDE_DIR})
include_directories(SYSTEM Contrib/fastlz)
include_directories(SYSTEM Contrib)
include_directories(../DebugUtils/Include)
include_directories(../Detour/Include)
include_directories(../DetourCrowd/Include)
include_directories(../DetourTileCache/Include)
include_directories(../Recast/Include)
include_directories(Include)
include_directories(Contrib/SDL/include)
if(APPLE)
include_directories(${SDL2_LIBRARY}/Headers)
endif()
if (WIN32)
add_executable(RecastDemo WIN32 ${SOURCES})
elseif(APPLE)
add_executable(RecastDemo MACOSX_BUNDLE ${SOURCES})
else()
add_executable(RecastDemo ${SOURCES})
endif()
if( WIN32 )
if ( "${CMAKE_MAKE_PROGRAM}" MATCHES "MSBuild" )
add_custom_command(TARGET RecastDemo
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy "${SDL2_RUNTIME_LIBRARY}" ${CMAKE_BINARY_DIR}/RecastDemo/$(ConfigurationName)/
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/Bin/Meshes ${CMAKE_BINARY_DIR}/RecastDemo/$(ConfigurationName)/Meshes
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/Bin/TestCases ${CMAKE_BINARY_DIR}/RecastDemo/$(ConfigurationName)/TestCases
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/Bin/DroidSans.ttf ${CMAKE_BINARY_DIR}/RecastDemo/$(ConfigurationName)/
)
elseif ( MINGW )
add_custom_command(TARGET RecastDemo
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy "${SDL2_RUNTIME_LIBRARY}" ${CMAKE_BINARY_DIR}/RecastDemo/
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/Bin/Meshes ${CMAKE_BINARY_DIR}/RecastDemo/Meshes
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/Bin/TestCases ${CMAKE_BINARY_DIR}/RecastDemo/TestCases
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/Bin/DroidSans.ttf ${CMAKE_BINARY_DIR}/RecastDemo/
)
endif()
else()
file(COPY Bin/Meshes DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
file(COPY Bin/TestCases DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
file(COPY Bin/DroidSans.ttf DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
endif()
add_dependencies(RecastDemo DebugUtils Detour DetourCrowd DetourTileCache Recast)
if(APPLE)
target_link_libraries(RecastDemo ${OPENGL_LIBRARIES} ${SDL2_LIBRARY} DebugUtils Detour DetourCrowd DetourTileCache Recast)
else()
target_link_libraries(RecastDemo ${OPENGL_LIBRARIES} SDL2::SDL2main DebugUtils Detour DetourCrowd DetourTileCache Recast)
endif()
install(TARGETS RecastDemo
RUNTIME DESTINATION bin
BUNDLE DESTINATION bin)
install(DIRECTORY Bin/Meshes DESTINATION bin)
install(DIRECTORY Bin/TestCases DESTINATION bin)
install(FILES Bin/DroidSans.ttf DESTINATION bin)
if (WIN32)
install(FILES "${SDL2_RUNTIME_LIBRARY}" DESTINATION bin)
endif()
+5
View File
@@ -81,6 +81,11 @@ typedef unsigned char flzuint8;
typedef unsigned short flzuint16;
typedef unsigned int flzuint32;
/* Disable "conversion from A to B, possible loss of data" warning when using MSVC */
#if defined(_MSC_VER)
#pragma warning(disable: 4244)
#endif
/* prototypes */
int fastlz_compress(const void* input, int length, void* output);
int fastlz_compress_level(int level, const void* input, int length, void* output);
+1 -1
View File
@@ -29,7 +29,7 @@ struct rcChunkyTriMeshNode
struct rcChunkyTriMesh
{
inline rcChunkyTriMesh() : nodes(0), nnodes(0), tris(0), ntris(0), maxTrisPerChunk(0) {};
inline rcChunkyTriMesh() : nodes(0), nnodes(0), tris(0), ntris(0), maxTrisPerChunk(0) {}
inline ~rcChunkyTriMesh() { delete [] nodes; delete [] tris; }
rcChunkyTriMeshNode* nodes;
+1 -1
View File
@@ -122,7 +122,7 @@ class CrowdTool : public SampleTool
TOOLMODE_CREATE,
TOOLMODE_MOVE_TARGET,
TOOLMODE_SELECT,
TOOLMODE_TOGGLE_POLYS,
TOOLMODE_TOGGLE_POLYS
};
ToolMode m_mode;
+3 -2
View File
@@ -43,7 +43,7 @@ class NavMeshTesterTool : public SampleTool
TOOLMODE_DISTANCE_TO_WALL,
TOOLMODE_FIND_POLYS_IN_CIRCLE,
TOOLMODE_FIND_POLYS_IN_SHAPE,
TOOLMODE_FIND_LOCAL_NEIGHBOURHOOD,
TOOLMODE_FIND_LOCAL_NEIGHBOURHOOD
};
ToolMode m_toolMode;
@@ -110,4 +110,5 @@ public:
void drawAgent(const float* pos, float r, float h, float c, const unsigned int col);
};
#endif // NAVMESHTESTERTOOL_H
#endif // NAVMESHTESTERTOOL_H
+2 -1
View File
@@ -29,4 +29,5 @@ typedef __int64 TimeVal;
TimeVal getPerfTime();
int getPerfTimeUsec(const TimeVal duration);
#endif // PERFTIMER_H
#endif // PERFTIMER_H
+6 -3
View File
@@ -78,12 +78,12 @@ enum SamplePartitionType
{
SAMPLE_PARTITION_WATERSHED,
SAMPLE_PARTITION_MONOTONE,
SAMPLE_PARTITION_LAYERS,
SAMPLE_PARTITION_LAYERS
};
struct SampleTool
{
virtual ~SampleTool() {}
virtual ~SampleTool();
virtual int type() = 0;
virtual void init(class Sample* sample) = 0;
virtual void reset() = 0;
@@ -97,7 +97,7 @@ struct SampleTool
};
struct SampleToolState {
virtual ~SampleToolState() {}
virtual ~SampleToolState();
virtual void init(class Sample* sample) = 0;
virtual void reset() = 0;
virtual void handleRender() = 0;
@@ -141,6 +141,9 @@ protected:
SampleDebugDraw m_dd;
dtNavMesh* loadAll(const char* path);
void saveAll(const char* path, const dtNavMesh* mesh);
public:
Sample();
virtual ~Sample();
+1 -1
View File
@@ -31,7 +31,7 @@ protected:
rcContourSet* m_cset;
rcPolyMesh* m_pmesh;
float m_ext[3];
float m_halfExtents[3];
float m_center[3];
float m_bmin[3], m_bmax[3];
dtPolyRef m_ref;
+1 -1
View File
@@ -62,7 +62,7 @@ protected:
DrawMode m_drawMode;
void cleanup();
public:
Sample_SoloMesh();
virtual ~Sample_SoloMesh();
+1 -1
View File
@@ -40,7 +40,7 @@ protected:
int m_cacheCompressedSize;
int m_cacheRawSize;
int m_cacheLayerCount;
int m_cacheBuildMemUsage;
unsigned int m_cacheBuildMemUsage;
enum DrawMode
{
+6 -2
View File
@@ -27,13 +27,17 @@ class TestCase
enum TestType
{
TEST_PATHFIND,
TEST_RAYCAST,
TEST_RAYCAST
};
struct Test
{
Test() :
type(),
spos(),
epos(),
nspos(),
nepos(),
radius(0),
includeFlags(0),
excludeFlags(0),
@@ -107,4 +111,4 @@ private:
TestCase& operator=(const TestCase&);
};
#endif // TESTCASE_H
#endif // TESTCASE_H
+2 -1
View File
@@ -47,4 +47,5 @@ void drawGraph(const GraphParams* p, const ValueHistory* graph,
int idx, const char* label, const unsigned int col);
#endif // VALUEHISTORY_H
#endif // VALUEHISTORY_H
+3 -3
View File
@@ -22,14 +22,14 @@
enum imguiMouseButton
{
IMGUI_MBUT_LEFT = 0x01,
IMGUI_MBUT_RIGHT = 0x02,
IMGUI_MBUT_RIGHT = 0x02
};
enum imguiTextAlign
{
IMGUI_ALIGN_LEFT,
IMGUI_ALIGN_CENTER,
IMGUI_ALIGN_RIGHT,
IMGUI_ALIGN_RIGHT
};
inline unsigned int imguiRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a=255)
@@ -68,7 +68,7 @@ enum imguiGfxCmdType
IMGUI_GFXCMD_TRIANGLE,
IMGUI_GFXCMD_LINE,
IMGUI_GFXCMD_TEXT,
IMGUI_GFXCMD_SCISSOR,
IMGUI_GFXCMD_SCISSOR
};
struct imguiGfxRect
+2 -1
View File
@@ -23,4 +23,5 @@ bool imguiRenderGLInit(const char* fontpath);
void imguiRenderGLDestroy();
void imguiRenderGLDraw();
#endif // IMGUI_RENDER_GL_H
#endif // IMGUI_RENDER_GL_H
+1 -1
View File
@@ -83,7 +83,7 @@ static void subdivide(BoundsItem* items, int nitems, int imin, int imax, int tri
int inum = imax - imin;
int icur = curNode;
if (curNode > maxNodes)
if (curNode >= maxNodes)
return;
rcChunkyTriMeshNode& node = nodes[curNode++];
+1 -2
View File
@@ -16,7 +16,6 @@
// 3. This notice may not be removed or altered from any source distribution.
//
#define _USE_MATH_DEFINES
#include <math.h>
#include <stdio.h>
#include <string.h>
@@ -254,7 +253,7 @@ void ConvexVolumeTool::handleRender()
{
duDebugDraw& dd = m_sample->getDebugDraw();
// Find height extents of the shape.
// Find height extent of the shape.
float minh = FLT_MAX, maxh = 0;
for (int i = 0; i < m_npts; ++i)
minh = rcMin(minh, m_pts[i*3+1]);
+4 -7
View File
@@ -16,7 +16,6 @@
// 3. This notice may not be removed or altered from any source distribution.
//
#define _USE_MATH_DEFINES
#include <math.h>
#include <stdio.h>
#include <string.h>
@@ -144,8 +143,6 @@ void CrowdToolState::init(class Sample* sample)
if (m_sample != sample)
{
m_sample = sample;
// m_oldFlags = m_sample->getNavMeshDrawFlags();
// m_sample->setNavMeshDrawFlags(m_oldFlags & ~DU_DRAWNAVMESH_CLOSEDLIST);
}
dtNavMesh* nav = m_sample->getNavMesh();
@@ -712,7 +709,7 @@ void CrowdToolState::setMoveTarget(const float* p, bool adjust)
dtNavMeshQuery* navquery = m_sample->getNavMeshQuery();
dtCrowd* crowd = m_sample->getCrowd();
const dtQueryFilter* filter = crowd->getFilter(0);
const float* ext = crowd->getQueryExtents();
const float* halfExtents = crowd->getQueryExtents();
if (adjust)
{
@@ -740,7 +737,7 @@ void CrowdToolState::setMoveTarget(const float* p, bool adjust)
}
else
{
navquery->findNearestPoly(p, ext, filter, &m_targetRef, m_targetPos);
navquery->findNearestPoly(p, halfExtents, filter, &m_targetRef, m_targetPos);
if (m_agentDebug.idx != -1)
{
@@ -1032,10 +1029,10 @@ void CrowdTool::handleClick(const float* s, const float* p, bool shift)
if (nav && navquery)
{
dtQueryFilter filter;
const float* ext = crowd->getQueryExtents();
const float* halfExtents = crowd->getQueryExtents();
float tgt[3];
dtPolyRef ref;
navquery->findNearestPoly(p, ext, &filter, &ref, tgt);
navquery->findNearestPoly(p, halfExtents, &filter, &ref, tgt);
if (ref)
{
unsigned short flags = 0;
+4 -3
View File
@@ -55,10 +55,11 @@ void scanDirectoryAppend(const string& path, const string& ext, vector<string>&
return;
}
size_t extLen = strlen(ext.c_str());
while ((current = readdir(dp)) != 0)
{
int len = strlen(current->d_name);
if (len > 4 && strncmp(current->d_name + len - 4, ext.c_str(), 4) == 0)
size_t len = strlen(current->d_name);
if (len > extLen && strncmp(current->d_name + len - extLen, ext.c_str(), extLen) == 0)
{
filelist.push_back(current->d_name);
}
@@ -74,4 +75,4 @@ void scanDirectory(const string& path, const string& ext, vector<string>& fileli
{
filelist.clear();
scanDirectoryAppend(path, ext, filelist);
}
}
+1 -4
View File
@@ -16,7 +16,6 @@
// 3. This notice may not be removed or altered from any source distribution.
//
#define _USE_MATH_DEFINES
#include <math.h>
#include <stdio.h>
#include <ctype.h>
@@ -92,6 +91,7 @@ static char* parseRow(char* buf, char* bufEnd, char* row, int len)
case '\t':
case ' ':
if (start) break;
// else falls through
default:
start = false;
row[n++] = c;
@@ -423,9 +423,6 @@ static bool isectSegAABB(const float* sp, const float* sq,
bool InputGeom::raycastMesh(float* src, float* dst, float& tmin)
{
float dir[3];
rcVsub(dir, dst, src);
// Prune hit ray.
float btmin, btmax;
if (!isectSegAABB(src, dst, m_meshBMin, m_meshBMax, btmin, btmax))
+1 -1
View File
@@ -20,7 +20,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <cstring>
#define _USE_MATH_DEFINES
#include <math.h>
rcMeshLoaderObj::rcMeshLoaderObj() :
@@ -99,6 +98,7 @@ static char* parseRow(char* buf, char* bufEnd, char* row, int len)
case '\t':
case ' ':
if (start) break;
// else falls through
default:
start = false;
row[n++] = c;
+2 -3
View File
@@ -16,7 +16,6 @@
// 3. This notice may not be removed or altered from any source distribution.
//
#define _USE_MATH_DEFINES
#include <math.h>
#include <stdio.h>
#include <string.h>
@@ -252,10 +251,10 @@ void NavMeshPruneTool::handleClick(const float* s, const float* p, bool shift)
m_flags->init(nav);
}
const float ext[3] = {2,4,2};
const float halfExtents[3] = { 2, 4, 2 };
dtQueryFilter filter;
dtPolyRef ref = 0;
query->findNearestPoly(p, ext, &filter, &ref, 0);
query->findNearestPoly(p, halfExtents, &filter, &ref, 0);
floodNavmesh(nav, m_flags, ref, 1);
}
+1 -2
View File
@@ -16,7 +16,6 @@
// 3. This notice may not be removed or altered from any source distribution.
//
#define _USE_MATH_DEFINES
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
@@ -45,7 +44,7 @@
// Uncomment this to dump all the requests in stdout.
#define DUMP_REQS
// Returns a random number [0..1)
// Returns a random number [0..1]
static float frand()
{
// return ((float)(rand() & 0xffff)/(float)0xffff);
@@ -16,7 +16,6 @@
// 3. This notice may not be removed or altered from any source distribution.
//
#define _USE_MATH_DEFINES
#include <math.h>
#include <stdio.h>
#include <string.h>
+135 -1
View File
@@ -16,7 +16,6 @@
// 3. This notice may not be removed or altered from any source distribution.
//
#define _USE_MATH_DEFINES
#include <math.h>
#include <stdio.h>
#include "Sample.h"
@@ -35,6 +34,16 @@
# define snprintf _snprintf
#endif
SampleTool::~SampleTool()
{
// Defined out of line to fix the weak v-tables warning
}
SampleToolState::~SampleToolState()
{
// Defined out of line to fix the weak v-tables warning
}
unsigned short sampleAreaToFlags(unsigned char area)
{
unsigned char areaType = (area & SAMPLE_POLYAREA_TYPE_MASK);
@@ -350,3 +359,128 @@ void Sample::renderOverlayToolStates(double* proj, double* model, int* view)
}
}
static const int NAVMESHSET_MAGIC = 'M'<<24 | 'S'<<16 | 'E'<<8 | 'T'; //'MSET';
static const int NAVMESHSET_VERSION = 1;
struct NavMeshSetHeader
{
int magic;
int version;
int numTiles;
dtNavMeshParams params;
};
struct NavMeshTileHeader
{
dtTileRef tileRef;
int dataSize;
};
dtNavMesh* Sample::loadAll(const char* path)
{
FILE* fp = fopen(path, "rb");
if (!fp) return 0;
// Read header.
NavMeshSetHeader header;
size_t readLen = fread(&header, sizeof(NavMeshSetHeader), 1, fp);
if (readLen != 1)
{
fclose(fp);
return 0;
}
if (header.magic != NAVMESHSET_MAGIC)
{
fclose(fp);
return 0;
}
if (header.version != NAVMESHSET_VERSION)
{
fclose(fp);
return 0;
}
dtNavMesh* mesh = dtAllocNavMesh();
if (!mesh)
{
fclose(fp);
return 0;
}
dtStatus status = mesh->init(&header.params);
if (dtStatusFailed(status))
{
fclose(fp);
return 0;
}
// Read tiles.
for (int i = 0; i < header.numTiles; ++i)
{
NavMeshTileHeader tileHeader;
readLen = fread(&tileHeader, sizeof(tileHeader), 1, fp);
if (readLen != 1)
{
fclose(fp);
return 0;
}
if (!tileHeader.tileRef || !tileHeader.dataSize)
break;
unsigned char* data = (unsigned char*)dtAlloc(tileHeader.dataSize, DT_ALLOC_PERM);
if (!data) break;
memset(data, 0, tileHeader.dataSize);
readLen = fread(data, tileHeader.dataSize, 1, fp);
if (readLen != 1)
{
dtFree(data);
fclose(fp);
return 0;
}
mesh->addTile(data, tileHeader.dataSize, DT_TILE_FREE_DATA, tileHeader.tileRef, 0);
}
fclose(fp);
return mesh;
}
void Sample::saveAll(const char* path, const dtNavMesh* mesh)
{
if (!mesh) return;
FILE* fp = fopen(path, "wb");
if (!fp)
return;
// Store header.
NavMeshSetHeader header;
header.magic = NAVMESHSET_MAGIC;
header.version = NAVMESHSET_VERSION;
header.numTiles = 0;
for (int i = 0; i < mesh->getMaxTiles(); ++i)
{
const dtMeshTile* tile = mesh->getTile(i);
if (!tile || !tile->header || !tile->dataSize) continue;
header.numTiles++;
}
memcpy(&header.params, mesh->getParams(), sizeof(dtNavMeshParams));
fwrite(&header, sizeof(NavMeshSetHeader), 1, fp);
// Store tiles.
for (int i = 0; i < mesh->getMaxTiles(); ++i)
{
const dtMeshTile* tile = mesh->getTile(i);
if (!tile || !tile->header || !tile->dataSize) continue;
NavMeshTileHeader tileHeader;
tileHeader.tileRef = mesh->getTileRef(tile);
tileHeader.dataSize = tile->dataSize;
fwrite(&tileHeader, sizeof(tileHeader), 1, fp);
fwrite(tile->data, tile->dataSize, 1, fp);
}
fclose(fp);
}
+2 -3
View File
@@ -1,4 +1,3 @@
#define _USE_MATH_DEFINES
#include <math.h>
#include <stdio.h>
#include <stdarg.h>
@@ -68,7 +67,7 @@ void BuildContext::doStartTimer(const rcTimerLabel label)
void BuildContext::doStopTimer(const rcTimerLabel label)
{
const TimeVal endTime = getPerfTime();
const int deltaTime = (int)(endTime - m_startTime[label]);
const TimeVal deltaTime = endTime - m_startTime[label];
if (m_accTime[label] == -1)
m_accTime[label] = deltaTime;
else
@@ -184,7 +183,7 @@ public:
}
}
};
GLCheckerTexture g_tex;
static GLCheckerTexture g_tex;
void DebugDrawGL::depthMask(bool state)
+5 -6
View File
@@ -16,7 +16,6 @@
// 3. This notice may not be removed or altered from any source distribution.
//
#define _USE_MATH_DEFINES
#include <math.h>
#include <stdio.h>
#include "Sample_Debug.h"
@@ -124,12 +123,12 @@ Sample_Debug::Sample_Debug() :
m_navMesh->addTileAt(-14,-14, data, dataSize, true);
}
const float ext[3] = {40,100,40};
const float halfExtents[3] = {40,100,40};
const float center[3] = { -1667.9491f, 135.52649f, -1680.6149f };
dtQueryFilter filter;
m_ref = m_navMesh->findNearestPoly(center, ext, &filter, 0);
m_ref = m_navMesh->findNearestPoly(center, halfExtents, &filter, 0);
vcopy(m_ext, ext);
vcopy(m_halfExtents, halfExtents);
vcopy(m_center, center);*/
@@ -204,8 +203,8 @@ void Sample_Debug::handleRender()
duDebugDrawNavMeshPoly(&m_dd, *m_navMesh, m_ref, duRGBA(255,0,0,128));
/* float bmin[3], bmax[3];
rcVsub(bmin, m_center, m_ext);
rcVadd(bmax, m_center, m_ext);
rcVsub(bmin, m_center, m_halfExtents);
rcVadd(bmax, m_center, m_halfExtents);
duDebugDrawBoxWire(&dd, bmin[0],bmin[1],bmin[2], bmax[0],bmax[1],bmax[2], duRGBA(255,255,255,128), 1.0f);
duDebugDrawCross(&dd, m_center[0], m_center[1], m_center[2], 1.0f, duRGBA(255,255,255,128), 2.0f);*/

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