mirror of
https://github.com/EQEmu/Server.git
synced 2026-01-03 23:03:51 +00:00
find command uses new pathing system now which loads .nav files saved by map_edit, still in initial stages of testing. Fixed a bug where find might cause a desync in some cases as well.
This commit is contained in:
parent
c6e9fbc878
commit
e61e2e7f02
@ -331,8 +331,14 @@ IF(EQEMU_BUILD_LUA)
|
||||
ADD_SUBDIRECTORY(luabind)
|
||||
ENDIF(EQEMU_BUILD_LUA)
|
||||
|
||||
INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/recast/debug_utils/include")
|
||||
INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/recast/detour/include")
|
||||
INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/recast/detour_tile_cache/include")
|
||||
INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/recast/recast/include")
|
||||
|
||||
IF(EQEMU_BUILD_SERVER OR EQEMU_BUILD_LOGIN OR EQEMU_BUILD_TESTS)
|
||||
ADD_SUBDIRECTORY(common)
|
||||
ADD_SUBDIRECTORY(recast)
|
||||
ENDIF(EQEMU_BUILD_SERVER OR EQEMU_BUILD_LOGIN OR EQEMU_BUILD_TESTS)
|
||||
IF(EQEMU_BUILD_SERVER)
|
||||
ADD_SUBDIRECTORY(shared_memory)
|
||||
|
||||
@ -64,6 +64,7 @@ SET(common_sources
|
||||
unix.cpp
|
||||
worldconn.cpp
|
||||
xml_parser.cpp
|
||||
pathfind.cpp
|
||||
platform.cpp
|
||||
patches/patches.cpp
|
||||
patches/sod.cpp
|
||||
@ -156,6 +157,7 @@ SET(common_headers
|
||||
packet_dump.h
|
||||
packet_dump_file.h
|
||||
packet_functions.h
|
||||
pathfind.h
|
||||
platform.h
|
||||
proc_launcher.h
|
||||
profiler.h
|
||||
|
||||
250
common/pathfind.cpp
Normal file
250
common/pathfind.cpp
Normal file
@ -0,0 +1,250 @@
|
||||
#include "pathfind.h"
|
||||
#include <stdio.h>
|
||||
#include <DetourNavMeshQuery.h>
|
||||
|
||||
const uint32_t nav_mesh_file_version = 1;
|
||||
const float max_dest_drift = 10.0f;
|
||||
|
||||
PathfindingManager::PathfindingManager()
|
||||
{
|
||||
m_nav_mesh = nullptr;
|
||||
}
|
||||
|
||||
PathfindingManager::~PathfindingManager()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
void PathfindingManager::Load(const std::string &zone_name)
|
||||
{
|
||||
std::string filename = MAP_DIR + std::string("/") + zone_name + ".nav";
|
||||
FILE *f = fopen(filename.c_str(), "rb");
|
||||
if (f) {
|
||||
char magic[9] = { 0 };
|
||||
if (fread(magic, 9, 1, f) != 1) {
|
||||
fclose(f);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strncmp(magic, "EQNAVMESH", 9) != 0)
|
||||
{
|
||||
fclose(f);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t version = 0;
|
||||
if (fread(&version, sizeof(uint32_t), 1, f) != 1) {
|
||||
fclose(f);
|
||||
return;
|
||||
}
|
||||
|
||||
if (version != nav_mesh_file_version) {
|
||||
fclose(f);
|
||||
return;
|
||||
}
|
||||
|
||||
m_nav_mesh = dtAllocNavMesh();
|
||||
|
||||
uint32_t number_of_tiles = 0;
|
||||
if (fread(&number_of_tiles, sizeof(uint32_t), 1, f) != 1) {
|
||||
Clear();
|
||||
fclose(f);
|
||||
return;
|
||||
}
|
||||
|
||||
dtNavMeshParams params;
|
||||
if (fread(¶ms, sizeof(dtNavMeshParams), 1, f) != 1) {
|
||||
Clear();
|
||||
fclose(f);
|
||||
return;
|
||||
}
|
||||
|
||||
dtStatus status = m_nav_mesh->init(¶ms);
|
||||
if (dtStatusFailed(status))
|
||||
{
|
||||
Clear();
|
||||
fclose(f);
|
||||
return;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < number_of_tiles; ++i)
|
||||
{
|
||||
uint32_t tile_ref = 0;
|
||||
if (fread(&tile_ref, sizeof(uint32_t), 1, f) != 1) {
|
||||
Clear();
|
||||
fclose(f);
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t data_size = 0;
|
||||
if (fread(&data_size, sizeof(int32_t), 1, f) != 1) {
|
||||
Clear();
|
||||
fclose(f);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tile_ref || !data_size) {
|
||||
Clear();
|
||||
fclose(f);
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned char* data = (unsigned char*)dtAlloc(data_size, DT_ALLOC_PERM);
|
||||
if (fread(data, data_size, 1, f) != 1) {
|
||||
Clear();
|
||||
fclose(f);
|
||||
return;
|
||||
}
|
||||
|
||||
m_nav_mesh->addTile(data, data_size, DT_TILE_FREE_DATA, tile_ref, 0);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
void PathfindingManager::Clear()
|
||||
{
|
||||
if (m_nav_mesh) {
|
||||
dtFreeNavMesh(m_nav_mesh);
|
||||
m_nav_mesh = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
PathfindingRoute PathfindingManager::FindRoute(const glm::vec3 &src_loc, const glm::vec3 &dest_loc)
|
||||
{
|
||||
glm::vec3 current_location(src_loc.x, src_loc.z, src_loc.y);
|
||||
glm::vec3 dest_location(dest_loc.x, dest_loc.z, dest_loc.y);
|
||||
|
||||
PathfindingRoute ret;
|
||||
ret.m_dest = dest_location;
|
||||
ret.m_current_node = 0;
|
||||
|
||||
if (!m_nav_mesh) {
|
||||
PathfindingNode src;
|
||||
src.flag = NavigationPolyFlagNormal;
|
||||
src.position = current_location;
|
||||
|
||||
PathfindingNode dest;
|
||||
dest.flag = NavigationPolyFlagNormal;
|
||||
dest.position = dest_location;
|
||||
|
||||
ret.m_nodes.push_back(src);
|
||||
ret.m_nodes.push_back(dest);
|
||||
return ret;
|
||||
}
|
||||
|
||||
glm::vec3 ext(5.0f, 5.0f, 5.0f);
|
||||
dtQueryFilter filter;
|
||||
filter.setIncludeFlags(NavigationPolyFlagAll);
|
||||
filter.setAreaCost(NavigationAreaFlagNormal, 1.0f);
|
||||
filter.setAreaCost(NavigationAreaFlagWater, 2.5f);
|
||||
filter.setAreaCost(NavigationAreaFlagLava, 2.5f);
|
||||
filter.setAreaCost(NavigationAreaFlagPvP, 1.0f);
|
||||
filter.setAreaCost(NavigationAreaFlagSlime, 1.0f);
|
||||
filter.setAreaCost(NavigationAreaFlagIce, 1.0f);
|
||||
filter.setAreaCost(NavigationAreaFlagVWater, 2.5f);
|
||||
filter.setAreaCost(NavigationAreaFlagGeneralArea, 1.0f);
|
||||
filter.setAreaCost(NavigationAreaFlagPortal, 1.0f);
|
||||
|
||||
dtNavMeshQuery *query = dtAllocNavMeshQuery();
|
||||
query->init(m_nav_mesh, 4092);
|
||||
dtPolyRef start_ref;
|
||||
dtPolyRef end_ref;
|
||||
|
||||
query->findNearestPoly(¤t_location[0], &ext[0], &filter, &start_ref, 0);
|
||||
query->findNearestPoly(&dest_location[0], &ext[0], &filter, &end_ref, 0);
|
||||
|
||||
if (!start_ref || !end_ref) {
|
||||
dtFreeNavMeshQuery(query);
|
||||
|
||||
PathfindingNode src;
|
||||
src.flag = NavigationPolyFlagNormal;
|
||||
src.position = current_location;
|
||||
|
||||
PathfindingNode dest;
|
||||
dest.flag = NavigationPolyFlagNormal;
|
||||
dest.position = dest_location;
|
||||
|
||||
ret.m_nodes.push_back(src);
|
||||
ret.m_nodes.push_back(dest);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int npoly = 0;
|
||||
dtPolyRef path[256] = { 0 };
|
||||
query->findPath(start_ref, end_ref, ¤t_location[0], &dest_location[0], &filter, path, &npoly, 256);
|
||||
|
||||
if (npoly) {
|
||||
glm::vec3 epos = dest_location;
|
||||
if (path[npoly - 1] != end_ref)
|
||||
query->closestPointOnPoly(path[npoly - 1], &dest_location[0], &epos[0], 0);
|
||||
|
||||
float straight_path[256 * 3];
|
||||
unsigned char straight_path_flags[256];
|
||||
int n_straight_polys;
|
||||
dtPolyRef straight_path_polys[256];
|
||||
query->findStraightPath(¤t_location[0], &epos[0], path, npoly,
|
||||
straight_path, straight_path_flags,
|
||||
straight_path_polys, &n_straight_polys, 256, DT_STRAIGHTPATH_ALL_CROSSINGS);
|
||||
|
||||
dtFreeNavMeshQuery(query);
|
||||
|
||||
if (n_straight_polys) {
|
||||
ret.m_nodes.reserve(n_straight_polys);
|
||||
for (int i = 0; i < n_straight_polys; ++i)
|
||||
{
|
||||
PathfindingNode node;
|
||||
node.position.x = straight_path[i * 3];
|
||||
node.position.z = straight_path[i * 3 + 1];
|
||||
node.position.y = straight_path[i * 3 + 2];
|
||||
if (!dtStatusSucceed(m_nav_mesh->getPolyFlags(straight_path_polys[i], &node.flag))) {
|
||||
node.flag = 0;
|
||||
}
|
||||
|
||||
ret.m_nodes.push_back(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
PathfindingNode src;
|
||||
src.flag = NavigationPolyFlagNormal;
|
||||
src.position = current_location;
|
||||
|
||||
PathfindingNode dest;
|
||||
dest.flag = NavigationPolyFlagNormal;
|
||||
dest.position = dest_location;
|
||||
|
||||
ret.m_nodes.push_back(src);
|
||||
ret.m_nodes.push_back(dest);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
PathfindingRoute::PathfindingRoute()
|
||||
{
|
||||
}
|
||||
|
||||
PathfindingRoute::~PathfindingRoute()
|
||||
{
|
||||
}
|
||||
|
||||
bool PathfindingRoute::Valid(const glm::vec3 &dest)
|
||||
{
|
||||
auto dist = (dest - m_dest).length();
|
||||
if (dist <= max_dest_drift) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void PathfindingRoute::CalcCurrentNode()
|
||||
{
|
||||
//if we're at last node then we dont need to do anything.
|
||||
|
||||
//else need to see if we're at current_node
|
||||
//if so then we advance to the next node and return it
|
||||
//else just return the current node
|
||||
}
|
||||
77
common/pathfind.h
Normal file
77
common/pathfind.h
Normal file
@ -0,0 +1,77 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <DetourNavMesh.h>
|
||||
#include <glm/vec3.hpp>
|
||||
|
||||
enum NavigationAreaFlags
|
||||
{
|
||||
NavigationAreaFlagNormal,
|
||||
NavigationAreaFlagWater,
|
||||
NavigationAreaFlagLava,
|
||||
NavigationAreaFlagZoneLine,
|
||||
NavigationAreaFlagPvP,
|
||||
NavigationAreaFlagSlime,
|
||||
NavigationAreaFlagIce,
|
||||
NavigationAreaFlagVWater,
|
||||
NavigationAreaFlagGeneralArea,
|
||||
NavigationAreaFlagPortal,
|
||||
NavigationAreaFlagDisabled,
|
||||
};
|
||||
|
||||
enum NavigationPolyFlags
|
||||
{
|
||||
NavigationPolyFlagNormal = 1,
|
||||
NavigationPolyFlagWater = 2,
|
||||
NavigationPolyFlagLava = 4,
|
||||
NavigationPolyFlagZoneLine = 8,
|
||||
NavigationPolyFlagPvP = 16,
|
||||
NavigationPolyFlagSlime = 32,
|
||||
NavigationPolyFlagIce = 64,
|
||||
NavigationPolyFlagVWater = 128,
|
||||
NavigationPolyFlagGeneralArea = 256,
|
||||
NavigationPolyFlagPortal = 512,
|
||||
NavigationPolyFlagDisabled = 1024,
|
||||
NavigationPolyFlagAll = 0xFFFF
|
||||
};
|
||||
|
||||
struct PathfindingNode
|
||||
{
|
||||
glm::vec3 position;
|
||||
unsigned short flag;
|
||||
};
|
||||
|
||||
class PathfindingRoute
|
||||
{
|
||||
public:
|
||||
PathfindingRoute();
|
||||
~PathfindingRoute();
|
||||
|
||||
bool Valid(const glm::vec3 &dest);
|
||||
void CalcCurrentNode();
|
||||
const PathfindingNode& GetCurrentNode() { return m_nodes[m_current_node]; }
|
||||
const std::vector<PathfindingNode>& GetNodes() const { return m_nodes; }
|
||||
std::vector<PathfindingNode>& GetNodesEdit() { return m_nodes; }
|
||||
private:
|
||||
glm::vec3 m_dest;
|
||||
std::vector<PathfindingNode> m_nodes;
|
||||
int m_current_node;
|
||||
|
||||
friend class PathfindingManager;
|
||||
};
|
||||
|
||||
class PathfindingManager
|
||||
{
|
||||
public:
|
||||
PathfindingManager();
|
||||
~PathfindingManager();
|
||||
|
||||
void Load(const std::string &filename);
|
||||
void Clear();
|
||||
|
||||
//Expects locations in EQEmu internal format eg what #loc returns not what /loc returns.
|
||||
PathfindingRoute FindRoute(const glm::vec3 ¤t_location, const glm::vec3 &dest_location);
|
||||
private:
|
||||
dtNavMesh *m_nav_mesh;
|
||||
};
|
||||
103
recast/CMakeLists.txt
Normal file
103
recast/CMakeLists.txt
Normal file
@ -0,0 +1,103 @@
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
|
||||
|
||||
SET(recast_navigation_sources
|
||||
debug_utils/src/DebugDraw.cpp
|
||||
debug_utils/src/DetourDebugDraw.cpp
|
||||
debug_utils/src/RecastDebugDraw.cpp
|
||||
debug_utils/src/RecastDump.cpp
|
||||
detour/src/DetourAlloc.cpp
|
||||
detour/src/DetourCommon.cpp
|
||||
detour/src/DetourNavMesh.cpp
|
||||
detour/src/DetourNavMeshBuilder.cpp
|
||||
detour/src/DetourNavMeshQuery.cpp
|
||||
detour/src/DetourNode.cpp
|
||||
detour_tile_cache/src/DetourTileCache.cpp
|
||||
detour_tile_cache/src/DetourTileCacheBuilder.cpp
|
||||
recast/src/Recast.cpp
|
||||
recast/src/RecastAlloc.cpp
|
||||
recast/src/RecastArea.cpp
|
||||
recast/src/RecastContour.cpp
|
||||
recast/src/RecastFilter.cpp
|
||||
recast/src/RecastLayers.cpp
|
||||
recast/src/RecastMesh.cpp
|
||||
recast/src/RecastMeshDetail.cpp
|
||||
recast/src/RecastRasterization.cpp
|
||||
recast/src/RecastRegion.cpp
|
||||
)
|
||||
|
||||
SET(recast_navigation_headers
|
||||
debug_utils/include/DebugDraw.h
|
||||
debug_utils/include/DetourDebugDraw.h
|
||||
debug_utils/include/RecastDebugDraw.h
|
||||
debug_utils/include/RecastDump.h
|
||||
detour/include/DetourAlloc.h
|
||||
detour/include/DetourAssert.h
|
||||
detour/include/DetourCommon.h
|
||||
detour/include/DetourMath.h
|
||||
detour/include/DetourNavMesh.h
|
||||
detour/include/DetourNavMeshBuilder.h
|
||||
detour/include/DetourNavMeshQuery.h
|
||||
detour/include/DetourNode.h
|
||||
detour/include/DetourStatus.h
|
||||
detour_tile_cache/include/DetourTileCache.h
|
||||
detour_tile_cache/include/DetourTileCacheBuilder.h
|
||||
recast/include/Recast.h
|
||||
recast/include/RecastAlloc.h
|
||||
recast/include/RecastAssert.h
|
||||
)
|
||||
|
||||
SOURCE_GROUP(DebugUtils FILES
|
||||
debug_utils/src/DebugDraw.cpp
|
||||
debug_utils/src/DetourDebugDraw.cpp
|
||||
debug_utils/src/RecastDebugDraw.cpp
|
||||
debug_utils/src/RecastDump.cpp
|
||||
debug_utils/include/DebugDraw.h
|
||||
debug_utils/include/DetourDebugDraw.h
|
||||
debug_utils/include/RecastDebugDraw.h
|
||||
debug_utils/include/RecastDump.h
|
||||
)
|
||||
|
||||
SOURCE_GROUP(Detour FILES
|
||||
detour/src/DetourAlloc.cpp
|
||||
detour/src/DetourCommon.cpp
|
||||
detour/src/DetourNavMesh.cpp
|
||||
detour/src/DetourNavMeshBuilder.cpp
|
||||
detour/src/DetourNavMeshQuery.cpp
|
||||
detour/src/DetourNode.cpp
|
||||
detour/include/DetourAlloc.h
|
||||
detour/include/DetourAssert.h
|
||||
detour/include/DetourCommon.h
|
||||
detour/include/DetourMath.h
|
||||
detour/include/DetourNavMesh.h
|
||||
detour/include/DetourNavMeshBuilder.h
|
||||
detour/include/DetourNavMeshQuery.h
|
||||
detour/include/DetourNode.h
|
||||
detour/include/DetourStatus.h
|
||||
)
|
||||
|
||||
SOURCE_GROUP(DetourTileCache FILES
|
||||
detour_tile_cache/src/DetourTileCache.cpp
|
||||
detour_tile_cache/src/DetourTileCacheBuilder.cpp
|
||||
detour_tile_cache/include/DetourTileCache.h
|
||||
detour_tile_cache/include/DetourTileCacheBuilder.h
|
||||
)
|
||||
|
||||
SOURCE_GROUP(Recast FILES
|
||||
recast/src/Recast.cpp
|
||||
recast/src/RecastAlloc.cpp
|
||||
recast/src/RecastArea.cpp
|
||||
recast/src/RecastContour.cpp
|
||||
recast/src/RecastFilter.cpp
|
||||
recast/src/RecastLayers.cpp
|
||||
recast/src/RecastMesh.cpp
|
||||
recast/src/RecastMeshDetail.cpp
|
||||
recast/src/RecastRasterization.cpp
|
||||
recast/src/RecastRegion.cpp
|
||||
recast/include/Recast.h
|
||||
recast/include/RecastAlloc.h
|
||||
recast/include/RecastAssert.h
|
||||
)
|
||||
|
||||
ADD_LIBRARY(recast_navigation ${recast_navigation_sources} ${recast_navigation_headers})
|
||||
|
||||
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
|
||||
216
recast/debug_utils/include/DebugDraw.h
Normal file
216
recast/debug_utils/include/DebugDraw.h
Normal file
@ -0,0 +1,216 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#ifndef DEBUGDRAW_H
|
||||
#define DEBUGDRAW_H
|
||||
|
||||
// Some math headers don't have PI defined.
|
||||
static const float DU_PI = 3.14159265f;
|
||||
|
||||
enum duDebugDrawPrimitives
|
||||
{
|
||||
DU_DRAW_POINTS,
|
||||
DU_DRAW_LINES,
|
||||
DU_DRAW_TRIS,
|
||||
DU_DRAW_QUADS,
|
||||
};
|
||||
|
||||
/// Abstract debug draw interface.
|
||||
struct duDebugDraw
|
||||
{
|
||||
virtual ~duDebugDraw() = 0;
|
||||
|
||||
virtual void depthMask(bool state) = 0;
|
||||
|
||||
virtual void texture(bool state) = 0;
|
||||
|
||||
/// Begin drawing primitives.
|
||||
/// @param prim [in] primitive type to draw, one of rcDebugDrawPrimitives.
|
||||
/// @param size [in] size of a primitive, applies to point size and line width only.
|
||||
virtual void begin(duDebugDrawPrimitives prim, float size = 1.0f) = 0;
|
||||
|
||||
/// Submit a vertex
|
||||
/// @param pos [in] position of the verts.
|
||||
/// @param color [in] color of the verts.
|
||||
virtual void vertex(const float* pos, unsigned int color) = 0;
|
||||
|
||||
/// Submit a vertex
|
||||
/// @param x,y,z [in] position of the verts.
|
||||
/// @param color [in] color of the verts.
|
||||
virtual void vertex(const float x, const float y, const float z, unsigned int color) = 0;
|
||||
|
||||
/// Submit a vertex
|
||||
/// @param pos [in] position of the verts.
|
||||
/// @param color [in] color of the verts.
|
||||
virtual void vertex(const float* pos, unsigned int color, const float* uv) = 0;
|
||||
|
||||
/// Submit a vertex
|
||||
/// @param x,y,z [in] position of the verts.
|
||||
/// @param color [in] color of the verts.
|
||||
virtual void vertex(const float x, const float y, const float z, unsigned int color, const float u, const float v) = 0;
|
||||
|
||||
/// End drawing primitives.
|
||||
virtual void end() = 0;
|
||||
};
|
||||
|
||||
inline unsigned int duRGBA(int r, int g, int b, int a)
|
||||
{
|
||||
return ((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16) | ((unsigned int)a << 24);
|
||||
}
|
||||
|
||||
inline unsigned int duRGBAf(float fr, float fg, float fb, float fa)
|
||||
{
|
||||
unsigned char r = (unsigned char)(fr*255.0f);
|
||||
unsigned char g = (unsigned char)(fg*255.0f);
|
||||
unsigned char b = (unsigned char)(fb*255.0f);
|
||||
unsigned char a = (unsigned char)(fa*255.0f);
|
||||
return duRGBA(r,g,b,a);
|
||||
}
|
||||
|
||||
unsigned int duIntToCol(int i, int a);
|
||||
void duIntToCol(int i, float* col);
|
||||
|
||||
inline unsigned int duMultCol(const unsigned int col, const unsigned int d)
|
||||
{
|
||||
const unsigned int r = col & 0xff;
|
||||
const unsigned int g = (col >> 8) & 0xff;
|
||||
const unsigned int b = (col >> 16) & 0xff;
|
||||
const unsigned int a = (col >> 24) & 0xff;
|
||||
return duRGBA((r*d) >> 8, (g*d) >> 8, (b*d) >> 8, a);
|
||||
}
|
||||
|
||||
inline unsigned int duDarkenCol(unsigned int col)
|
||||
{
|
||||
return ((col >> 1) & 0x007f7f7f) | (col & 0xff000000);
|
||||
}
|
||||
|
||||
inline unsigned int duLerpCol(unsigned int ca, unsigned int cb, unsigned int u)
|
||||
{
|
||||
const unsigned int ra = ca & 0xff;
|
||||
const unsigned int ga = (ca >> 8) & 0xff;
|
||||
const unsigned int ba = (ca >> 16) & 0xff;
|
||||
const unsigned int aa = (ca >> 24) & 0xff;
|
||||
const unsigned int rb = cb & 0xff;
|
||||
const unsigned int gb = (cb >> 8) & 0xff;
|
||||
const unsigned int bb = (cb >> 16) & 0xff;
|
||||
const unsigned int ab = (cb >> 24) & 0xff;
|
||||
|
||||
unsigned int r = (ra*(255-u) + rb*u)/255;
|
||||
unsigned int g = (ga*(255-u) + gb*u)/255;
|
||||
unsigned int b = (ba*(255-u) + bb*u)/255;
|
||||
unsigned int a = (aa*(255-u) + ab*u)/255;
|
||||
return duRGBA(r,g,b,a);
|
||||
}
|
||||
|
||||
inline unsigned int duTransCol(unsigned int c, unsigned int a)
|
||||
{
|
||||
return (a<<24) | (c & 0x00ffffff);
|
||||
}
|
||||
|
||||
|
||||
void duCalcBoxColors(unsigned int* colors, unsigned int colTop, unsigned int colSide);
|
||||
|
||||
void duDebugDrawCylinderWire(struct duDebugDraw* dd, float minx, float miny, float minz,
|
||||
float maxx, float maxy, float maxz, unsigned int col, const float lineWidth);
|
||||
|
||||
void duDebugDrawBoxWire(struct duDebugDraw* dd, float minx, float miny, float minz,
|
||||
float maxx, float maxy, float maxz, unsigned int col, const float lineWidth);
|
||||
|
||||
void duDebugDrawArc(struct duDebugDraw* dd, const float x0, const float y0, const float z0,
|
||||
const float x1, const float y1, const float z1, const float h,
|
||||
const float as0, const float as1, unsigned int col, const float lineWidth);
|
||||
|
||||
void duDebugDrawArrow(struct duDebugDraw* dd, const float x0, const float y0, const float z0,
|
||||
const float x1, const float y1, const float z1,
|
||||
const float as0, const float as1, unsigned int col, const float lineWidth);
|
||||
|
||||
void duDebugDrawCircle(struct duDebugDraw* dd, const float x, const float y, const float z,
|
||||
const float r, unsigned int col, const float lineWidth);
|
||||
|
||||
void duDebugDrawCross(struct duDebugDraw* dd, const float x, const float y, const float z,
|
||||
const float size, unsigned int col, const float lineWidth);
|
||||
|
||||
void duDebugDrawBox(struct duDebugDraw* dd, float minx, float miny, float minz,
|
||||
float maxx, float maxy, float maxz, const unsigned int* fcol);
|
||||
|
||||
void duDebugDrawCylinder(struct duDebugDraw* dd, float minx, float miny, float minz,
|
||||
float maxx, float maxy, float maxz, unsigned int col);
|
||||
|
||||
void duDebugDrawGridXZ(struct duDebugDraw* dd, const float ox, const float oy, const float oz,
|
||||
const int w, const int h, const float size,
|
||||
const unsigned int col, const float lineWidth);
|
||||
|
||||
|
||||
// Versions without begin/end, can be used to draw multiple primitives.
|
||||
void duAppendCylinderWire(struct duDebugDraw* dd, float minx, float miny, float minz,
|
||||
float maxx, float maxy, float maxz, unsigned int col);
|
||||
|
||||
void duAppendBoxWire(struct duDebugDraw* dd, float minx, float miny, float minz,
|
||||
float maxx, float maxy, float maxz, unsigned int col);
|
||||
|
||||
void duAppendBoxPoints(struct duDebugDraw* dd, float minx, float miny, float minz,
|
||||
float maxx, float maxy, float maxz, unsigned int col);
|
||||
|
||||
void duAppendArc(struct duDebugDraw* dd, const float x0, const float y0, const float z0,
|
||||
const float x1, const float y1, const float z1, const float h,
|
||||
const float as0, const float as1, unsigned int col);
|
||||
|
||||
void duAppendArrow(struct duDebugDraw* dd, const float x0, const float y0, const float z0,
|
||||
const float x1, const float y1, const float z1,
|
||||
const float as0, const float as1, unsigned int col);
|
||||
|
||||
void duAppendCircle(struct duDebugDraw* dd, const float x, const float y, const float z,
|
||||
const float r, unsigned int col);
|
||||
|
||||
void duAppendCross(struct duDebugDraw* dd, const float x, const float y, const float z,
|
||||
const float size, unsigned int col);
|
||||
|
||||
void duAppendBox(struct duDebugDraw* dd, float minx, float miny, float minz,
|
||||
float maxx, float maxy, float maxz, const unsigned int* fcol);
|
||||
|
||||
void duAppendCylinder(struct duDebugDraw* dd, float minx, float miny, float minz,
|
||||
float maxx, float maxy, float maxz, unsigned int col);
|
||||
|
||||
|
||||
class duDisplayList : public duDebugDraw
|
||||
{
|
||||
float* m_pos;
|
||||
unsigned int* m_color;
|
||||
int m_size;
|
||||
int m_cap;
|
||||
|
||||
bool m_depthMask;
|
||||
duDebugDrawPrimitives m_prim;
|
||||
float m_primSize;
|
||||
|
||||
void resize(int cap);
|
||||
|
||||
public:
|
||||
duDisplayList(int cap = 512);
|
||||
~duDisplayList();
|
||||
virtual void depthMask(bool state);
|
||||
virtual void begin(duDebugDrawPrimitives prim, float size = 1.0f);
|
||||
virtual void vertex(const float x, const float y, const float z, unsigned int color);
|
||||
virtual void vertex(const float* pos, unsigned int color);
|
||||
virtual void end();
|
||||
void clear();
|
||||
void draw(struct duDebugDraw* dd);
|
||||
};
|
||||
|
||||
|
||||
#endif // DEBUGDRAW_H
|
||||
49
recast/debug_utils/include/DetourDebugDraw.h
Normal file
49
recast/debug_utils/include/DetourDebugDraw.h
Normal file
@ -0,0 +1,49 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#ifndef DETOURDEBUGDRAW_H
|
||||
#define DETOURDEBUGDRAW_H
|
||||
|
||||
#include "DetourNavMesh.h"
|
||||
#include "DetourNavMeshQuery.h"
|
||||
#include "DetourTileCacheBuilder.h"
|
||||
|
||||
enum DrawNavMeshFlags
|
||||
{
|
||||
DU_DRAWNAVMESH_OFFMESHCONS = 0x01,
|
||||
DU_DRAWNAVMESH_CLOSEDLIST = 0x02,
|
||||
DU_DRAWNAVMESH_COLOR_TILES = 0x04,
|
||||
};
|
||||
|
||||
void duDebugDrawNavMesh(struct duDebugDraw* dd, const dtNavMesh& mesh, unsigned char flags);
|
||||
void duDebugDrawNavMeshTile(struct duDebugDraw* dd, const dtNavMesh& mesh, int w, int h, int layer, unsigned char flags);
|
||||
void duDebugDrawNavMeshWithClosedList(struct duDebugDraw* dd, const dtNavMesh& mesh, const dtNavMeshQuery& query, unsigned char flags);
|
||||
void duDebugDrawNavMeshNodes(struct duDebugDraw* dd, const dtNavMeshQuery& query);
|
||||
void duDebugDrawNavMeshBVTree(struct duDebugDraw* dd, const dtNavMesh& mesh);
|
||||
void duDebugDrawNavMeshPortals(struct duDebugDraw* dd, const dtNavMesh& mesh);
|
||||
void duDebugDrawNavMeshPolysWithFlags(struct duDebugDraw* dd, const dtNavMesh& mesh, const unsigned short polyFlags, const unsigned int col);
|
||||
void duDebugDrawNavMeshPoly(struct duDebugDraw* dd, const dtNavMesh& mesh, dtPolyRef ref, const unsigned int col);
|
||||
|
||||
void duDebugDrawTileCacheLayerAreas(struct duDebugDraw* dd, const dtTileCacheLayer& layer, const float cs, const float ch);
|
||||
void duDebugDrawTileCacheLayerRegions(struct duDebugDraw* dd, const dtTileCacheLayer& layer, const float cs, const float ch);
|
||||
void duDebugDrawTileCacheContours(duDebugDraw* dd, const struct dtTileCacheContourSet& lcset,
|
||||
const float* orig, const float cs, const float ch);
|
||||
void duDebugDrawTileCachePolyMesh(duDebugDraw* dd, const struct dtTileCachePolyMesh& lmesh,
|
||||
const float* orig, const float cs, const float ch);
|
||||
|
||||
#endif // DETOURDEBUGDRAW_H
|
||||
42
recast/debug_utils/include/RecastDebugDraw.h
Normal file
42
recast/debug_utils/include/RecastDebugDraw.h
Normal file
@ -0,0 +1,42 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#ifndef RECAST_DEBUGDRAW_H
|
||||
#define RECAST_DEBUGDRAW_H
|
||||
|
||||
void duDebugDrawTriMesh(struct duDebugDraw* dd, const float* verts, int nverts, const int* tris, const float* normals, int ntris, const unsigned char* flags, const float texScale);
|
||||
void duDebugDrawTriMeshSlope(struct duDebugDraw* dd, const float* verts, int nverts, const int* tris, const float* normals, int ntris, const float walkableSlopeAngle, const float texScale);
|
||||
|
||||
void duDebugDrawHeightfieldSolid(struct duDebugDraw* dd, const struct rcHeightfield& hf);
|
||||
void duDebugDrawHeightfieldWalkable(struct duDebugDraw* dd, const struct rcHeightfield& hf);
|
||||
|
||||
void duDebugDrawCompactHeightfieldSolid(struct duDebugDraw* dd, const struct rcCompactHeightfield& chf);
|
||||
void duDebugDrawCompactHeightfieldRegions(struct duDebugDraw* dd, const struct rcCompactHeightfield& chf);
|
||||
void duDebugDrawCompactHeightfieldDistance(struct duDebugDraw* dd, const struct rcCompactHeightfield& chf);
|
||||
|
||||
void duDebugDrawHeightfieldLayer(duDebugDraw* dd, const struct rcHeightfieldLayer& layer, const int idx);
|
||||
void duDebugDrawHeightfieldLayers(duDebugDraw* dd, const struct rcHeightfieldLayerSet& lset);
|
||||
void duDebugDrawHeightfieldLayersRegions(duDebugDraw* dd, const struct rcHeightfieldLayerSet& lset);
|
||||
|
||||
void duDebugDrawRegionConnections(struct duDebugDraw* dd, const struct rcContourSet& cset, const float alpha = 1.0f);
|
||||
void duDebugDrawRawContours(struct duDebugDraw* dd, const struct rcContourSet& cset, const float alpha = 1.0f);
|
||||
void duDebugDrawContours(struct duDebugDraw* dd, const struct rcContourSet& cset, const float alpha = 1.0f);
|
||||
void duDebugDrawPolyMesh(struct duDebugDraw* dd, const struct rcPolyMesh& mesh);
|
||||
void duDebugDrawPolyMeshDetail(struct duDebugDraw* dd, const struct rcPolyMeshDetail& dmesh);
|
||||
|
||||
#endif // RECAST_DEBUGDRAW_H
|
||||
43
recast/debug_utils/include/RecastDump.h
Normal file
43
recast/debug_utils/include/RecastDump.h
Normal file
@ -0,0 +1,43 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#ifndef RECAST_DUMP_H
|
||||
#define RECAST_DUMP_H
|
||||
|
||||
struct duFileIO
|
||||
{
|
||||
virtual ~duFileIO() = 0;
|
||||
virtual bool isWriting() const = 0;
|
||||
virtual bool isReading() const = 0;
|
||||
virtual bool write(const void* ptr, const size_t size) = 0;
|
||||
virtual bool read(void* ptr, const size_t size) = 0;
|
||||
};
|
||||
|
||||
bool duDumpPolyMeshToObj(struct rcPolyMesh& pmesh, duFileIO* io);
|
||||
bool duDumpPolyMeshDetailToObj(struct rcPolyMeshDetail& dmesh, duFileIO* io);
|
||||
|
||||
bool duDumpContourSet(struct rcContourSet& cset, duFileIO* io);
|
||||
bool duReadContourSet(struct rcContourSet& cset, duFileIO* io);
|
||||
|
||||
bool duDumpCompactHeightfield(struct rcCompactHeightfield& chf, duFileIO* io);
|
||||
bool duReadCompactHeightfield(struct rcCompactHeightfield& chf, duFileIO* io);
|
||||
|
||||
void duLogBuildTimes(rcContext& ctx, const int totalTileUsec);
|
||||
|
||||
|
||||
#endif // RECAST_DUMP_H
|
||||
599
recast/debug_utils/src/DebugDraw.cpp
Normal file
599
recast/debug_utils/src/DebugDraw.cpp
Normal file
@ -0,0 +1,599 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <string.h>
|
||||
#include "DebugDraw.h"
|
||||
#include "DetourMath.h"
|
||||
|
||||
|
||||
duDebugDraw::~duDebugDraw()
|
||||
{
|
||||
// Empty
|
||||
}
|
||||
|
||||
|
||||
inline int bit(int a, int b)
|
||||
{
|
||||
return (a & (1 << b)) >> b;
|
||||
}
|
||||
|
||||
unsigned int duIntToCol(int i, int a)
|
||||
{
|
||||
int r = bit(i, 1) + bit(i, 3) * 2 + 1;
|
||||
int g = bit(i, 2) + bit(i, 4) * 2 + 1;
|
||||
int b = bit(i, 0) + bit(i, 5) * 2 + 1;
|
||||
return duRGBA(r*63,g*63,b*63,a);
|
||||
}
|
||||
|
||||
void duIntToCol(int i, float* col)
|
||||
{
|
||||
int r = bit(i, 0) + bit(i, 3) * 2 + 1;
|
||||
int g = bit(i, 1) + bit(i, 4) * 2 + 1;
|
||||
int b = bit(i, 2) + bit(i, 5) * 2 + 1;
|
||||
col[0] = 1 - r*63.0f/255.0f;
|
||||
col[1] = 1 - g*63.0f/255.0f;
|
||||
col[2] = 1 - b*63.0f/255.0f;
|
||||
}
|
||||
|
||||
void duCalcBoxColors(unsigned int* colors, unsigned int colTop, unsigned int colSide)
|
||||
{
|
||||
if (!colors) return;
|
||||
|
||||
colors[0] = duMultCol(colTop, 250);
|
||||
colors[1] = duMultCol(colSide, 140);
|
||||
colors[2] = duMultCol(colSide, 165);
|
||||
colors[3] = duMultCol(colSide, 217);
|
||||
colors[4] = duMultCol(colSide, 165);
|
||||
colors[5] = duMultCol(colSide, 217);
|
||||
}
|
||||
|
||||
void duDebugDrawCylinderWire(struct duDebugDraw* dd, float minx, float miny, float minz,
|
||||
float maxx, float maxy, float maxz, unsigned int col, const float lineWidth)
|
||||
{
|
||||
if (!dd) return;
|
||||
|
||||
dd->begin(DU_DRAW_LINES, lineWidth);
|
||||
duAppendCylinderWire(dd, minx,miny,minz, maxx,maxy,maxz, col);
|
||||
dd->end();
|
||||
}
|
||||
|
||||
void duDebugDrawBoxWire(struct duDebugDraw* dd, float minx, float miny, float minz,
|
||||
float maxx, float maxy, float maxz, unsigned int col, const float lineWidth)
|
||||
{
|
||||
if (!dd) return;
|
||||
|
||||
dd->begin(DU_DRAW_LINES, lineWidth);
|
||||
duAppendBoxWire(dd, minx,miny,minz, maxx,maxy,maxz, col);
|
||||
dd->end();
|
||||
}
|
||||
|
||||
void duDebugDrawArc(struct duDebugDraw* dd, const float x0, const float y0, const float z0,
|
||||
const float x1, const float y1, const float z1, const float h,
|
||||
const float as0, const float as1, unsigned int col, const float lineWidth)
|
||||
{
|
||||
if (!dd) return;
|
||||
|
||||
dd->begin(DU_DRAW_LINES, lineWidth);
|
||||
duAppendArc(dd, x0,y0,z0, x1,y1,z1, h, as0, as1, col);
|
||||
dd->end();
|
||||
}
|
||||
|
||||
void duDebugDrawArrow(struct duDebugDraw* dd, const float x0, const float y0, const float z0,
|
||||
const float x1, const float y1, const float z1,
|
||||
const float as0, const float as1, unsigned int col, const float lineWidth)
|
||||
{
|
||||
if (!dd) return;
|
||||
|
||||
dd->begin(DU_DRAW_LINES, lineWidth);
|
||||
duAppendArrow(dd, x0,y0,z0, x1,y1,z1, as0, as1, col);
|
||||
dd->end();
|
||||
}
|
||||
|
||||
void duDebugDrawCircle(struct duDebugDraw* dd, const float x, const float y, const float z,
|
||||
const float r, unsigned int col, const float lineWidth)
|
||||
{
|
||||
if (!dd) return;
|
||||
|
||||
dd->begin(DU_DRAW_LINES, lineWidth);
|
||||
duAppendCircle(dd, x,y,z, r, col);
|
||||
dd->end();
|
||||
}
|
||||
|
||||
void duDebugDrawCross(struct duDebugDraw* dd, const float x, const float y, const float z,
|
||||
const float size, unsigned int col, const float lineWidth)
|
||||
{
|
||||
if (!dd) return;
|
||||
|
||||
dd->begin(DU_DRAW_LINES, lineWidth);
|
||||
duAppendCross(dd, x,y,z, size, col);
|
||||
dd->end();
|
||||
}
|
||||
|
||||
void duDebugDrawBox(struct duDebugDraw* dd, float minx, float miny, float minz,
|
||||
float maxx, float maxy, float maxz, const unsigned int* fcol)
|
||||
{
|
||||
if (!dd) return;
|
||||
|
||||
dd->begin(DU_DRAW_QUADS);
|
||||
duAppendBox(dd, minx,miny,minz, maxx,maxy,maxz, fcol);
|
||||
dd->end();
|
||||
}
|
||||
|
||||
void duDebugDrawCylinder(struct duDebugDraw* dd, float minx, float miny, float minz,
|
||||
float maxx, float maxy, float maxz, unsigned int col)
|
||||
{
|
||||
if (!dd) return;
|
||||
|
||||
dd->begin(DU_DRAW_TRIS);
|
||||
duAppendCylinder(dd, minx,miny,minz, maxx,maxy,maxz, col);
|
||||
dd->end();
|
||||
}
|
||||
|
||||
void duDebugDrawGridXZ(struct duDebugDraw* dd, const float ox, const float oy, const float oz,
|
||||
const int w, const int h, const float size,
|
||||
const unsigned int col, const float lineWidth)
|
||||
{
|
||||
if (!dd) return;
|
||||
|
||||
dd->begin(DU_DRAW_LINES, lineWidth);
|
||||
for (int i = 0; i <= h; ++i)
|
||||
{
|
||||
dd->vertex(ox,oy,oz+i*size, col);
|
||||
dd->vertex(ox+w*size,oy,oz+i*size, col);
|
||||
}
|
||||
for (int i = 0; i <= w; ++i)
|
||||
{
|
||||
dd->vertex(ox+i*size,oy,oz, col);
|
||||
dd->vertex(ox+i*size,oy,oz+h*size, col);
|
||||
}
|
||||
dd->end();
|
||||
}
|
||||
|
||||
|
||||
void duAppendCylinderWire(struct duDebugDraw* dd, float minx, float miny, float minz,
|
||||
float maxx, float maxy, float maxz, unsigned int col)
|
||||
{
|
||||
if (!dd) return;
|
||||
|
||||
static const int NUM_SEG = 16;
|
||||
static float dir[NUM_SEG*2];
|
||||
static bool init = false;
|
||||
if (!init)
|
||||
{
|
||||
init = true;
|
||||
for (int i = 0; i < NUM_SEG; ++i)
|
||||
{
|
||||
const float a = (float)i/(float)NUM_SEG*DU_PI*2;
|
||||
dir[i*2] = dtMathCosf(a);
|
||||
dir[i*2+1] = dtMathSinf(a);
|
||||
}
|
||||
}
|
||||
|
||||
const float cx = (maxx + minx)/2;
|
||||
const float cz = (maxz + minz)/2;
|
||||
const float rx = (maxx - minx)/2;
|
||||
const float rz = (maxz - minz)/2;
|
||||
|
||||
for (int i = 0, j = NUM_SEG-1; i < NUM_SEG; j = i++)
|
||||
{
|
||||
dd->vertex(cx+dir[j*2+0]*rx, miny, cz+dir[j*2+1]*rz, col);
|
||||
dd->vertex(cx+dir[i*2+0]*rx, miny, cz+dir[i*2+1]*rz, col);
|
||||
dd->vertex(cx+dir[j*2+0]*rx, maxy, cz+dir[j*2+1]*rz, col);
|
||||
dd->vertex(cx+dir[i*2+0]*rx, maxy, cz+dir[i*2+1]*rz, col);
|
||||
}
|
||||
for (int i = 0; i < NUM_SEG; i += NUM_SEG/4)
|
||||
{
|
||||
dd->vertex(cx+dir[i*2+0]*rx, miny, cz+dir[i*2+1]*rz, col);
|
||||
dd->vertex(cx+dir[i*2+0]*rx, maxy, cz+dir[i*2+1]*rz, col);
|
||||
}
|
||||
}
|
||||
|
||||
void duAppendBoxWire(struct duDebugDraw* dd, float minx, float miny, float minz,
|
||||
float maxx, float maxy, float maxz, unsigned int col)
|
||||
{
|
||||
if (!dd) return;
|
||||
// Top
|
||||
dd->vertex(minx, miny, minz, col);
|
||||
dd->vertex(maxx, miny, minz, col);
|
||||
dd->vertex(maxx, miny, minz, col);
|
||||
dd->vertex(maxx, miny, maxz, col);
|
||||
dd->vertex(maxx, miny, maxz, col);
|
||||
dd->vertex(minx, miny, maxz, col);
|
||||
dd->vertex(minx, miny, maxz, col);
|
||||
dd->vertex(minx, miny, minz, col);
|
||||
|
||||
// bottom
|
||||
dd->vertex(minx, maxy, minz, col);
|
||||
dd->vertex(maxx, maxy, minz, col);
|
||||
dd->vertex(maxx, maxy, minz, col);
|
||||
dd->vertex(maxx, maxy, maxz, col);
|
||||
dd->vertex(maxx, maxy, maxz, col);
|
||||
dd->vertex(minx, maxy, maxz, col);
|
||||
dd->vertex(minx, maxy, maxz, col);
|
||||
dd->vertex(minx, maxy, minz, col);
|
||||
|
||||
// Sides
|
||||
dd->vertex(minx, miny, minz, col);
|
||||
dd->vertex(minx, maxy, minz, col);
|
||||
dd->vertex(maxx, miny, minz, col);
|
||||
dd->vertex(maxx, maxy, minz, col);
|
||||
dd->vertex(maxx, miny, maxz, col);
|
||||
dd->vertex(maxx, maxy, maxz, col);
|
||||
dd->vertex(minx, miny, maxz, col);
|
||||
dd->vertex(minx, maxy, maxz, col);
|
||||
}
|
||||
|
||||
void duAppendBoxPoints(struct duDebugDraw* dd, float minx, float miny, float minz,
|
||||
float maxx, float maxy, float maxz, unsigned int col)
|
||||
{
|
||||
if (!dd) return;
|
||||
// Top
|
||||
dd->vertex(minx, miny, minz, col);
|
||||
dd->vertex(maxx, miny, minz, col);
|
||||
dd->vertex(maxx, miny, minz, col);
|
||||
dd->vertex(maxx, miny, maxz, col);
|
||||
dd->vertex(maxx, miny, maxz, col);
|
||||
dd->vertex(minx, miny, maxz, col);
|
||||
dd->vertex(minx, miny, maxz, col);
|
||||
dd->vertex(minx, miny, minz, col);
|
||||
|
||||
// bottom
|
||||
dd->vertex(minx, maxy, minz, col);
|
||||
dd->vertex(maxx, maxy, minz, col);
|
||||
dd->vertex(maxx, maxy, minz, col);
|
||||
dd->vertex(maxx, maxy, maxz, col);
|
||||
dd->vertex(maxx, maxy, maxz, col);
|
||||
dd->vertex(minx, maxy, maxz, col);
|
||||
dd->vertex(minx, maxy, maxz, col);
|
||||
dd->vertex(minx, maxy, minz, col);
|
||||
}
|
||||
|
||||
void duAppendBox(struct duDebugDraw* dd, float minx, float miny, float minz,
|
||||
float maxx, float maxy, float maxz, const unsigned int* fcol)
|
||||
{
|
||||
if (!dd) return;
|
||||
const float verts[8*3] =
|
||||
{
|
||||
minx, miny, minz,
|
||||
maxx, miny, minz,
|
||||
maxx, miny, maxz,
|
||||
minx, miny, maxz,
|
||||
minx, maxy, minz,
|
||||
maxx, maxy, minz,
|
||||
maxx, maxy, maxz,
|
||||
minx, maxy, maxz,
|
||||
};
|
||||
static const unsigned char inds[6*4] =
|
||||
{
|
||||
7, 6, 5, 4,
|
||||
0, 1, 2, 3,
|
||||
1, 5, 6, 2,
|
||||
3, 7, 4, 0,
|
||||
2, 6, 7, 3,
|
||||
0, 4, 5, 1,
|
||||
};
|
||||
|
||||
const unsigned char* in = inds;
|
||||
for (int i = 0; i < 6; ++i)
|
||||
{
|
||||
dd->vertex(&verts[*in*3], fcol[i]); in++;
|
||||
dd->vertex(&verts[*in*3], fcol[i]); in++;
|
||||
dd->vertex(&verts[*in*3], fcol[i]); in++;
|
||||
dd->vertex(&verts[*in*3], fcol[i]); in++;
|
||||
}
|
||||
}
|
||||
|
||||
void duAppendCylinder(struct duDebugDraw* dd, float minx, float miny, float minz,
|
||||
float maxx, float maxy, float maxz, unsigned int col)
|
||||
{
|
||||
if (!dd) return;
|
||||
|
||||
static const int NUM_SEG = 16;
|
||||
static float dir[NUM_SEG*2];
|
||||
static bool init = false;
|
||||
if (!init)
|
||||
{
|
||||
init = true;
|
||||
for (int i = 0; i < NUM_SEG; ++i)
|
||||
{
|
||||
const float a = (float)i/(float)NUM_SEG*DU_PI*2;
|
||||
dir[i*2] = cosf(a);
|
||||
dir[i*2+1] = sinf(a);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int col2 = duMultCol(col, 160);
|
||||
|
||||
const float cx = (maxx + minx)/2;
|
||||
const float cz = (maxz + minz)/2;
|
||||
const float rx = (maxx - minx)/2;
|
||||
const float rz = (maxz - minz)/2;
|
||||
|
||||
for (int i = 2; i < NUM_SEG; ++i)
|
||||
{
|
||||
const int a = 0, b = i-1, c = i;
|
||||
dd->vertex(cx+dir[a*2+0]*rx, miny, cz+dir[a*2+1]*rz, col2);
|
||||
dd->vertex(cx+dir[b*2+0]*rx, miny, cz+dir[b*2+1]*rz, col2);
|
||||
dd->vertex(cx+dir[c*2+0]*rx, miny, cz+dir[c*2+1]*rz, col2);
|
||||
}
|
||||
for (int i = 2; i < NUM_SEG; ++i)
|
||||
{
|
||||
const int a = 0, b = i, c = i-1;
|
||||
dd->vertex(cx+dir[a*2+0]*rx, maxy, cz+dir[a*2+1]*rz, col);
|
||||
dd->vertex(cx+dir[b*2+0]*rx, maxy, cz+dir[b*2+1]*rz, col);
|
||||
dd->vertex(cx+dir[c*2+0]*rx, maxy, cz+dir[c*2+1]*rz, col);
|
||||
}
|
||||
for (int i = 0, j = NUM_SEG-1; i < NUM_SEG; j = i++)
|
||||
{
|
||||
dd->vertex(cx+dir[i*2+0]*rx, miny, cz+dir[i*2+1]*rz, col2);
|
||||
dd->vertex(cx+dir[j*2+0]*rx, miny, cz+dir[j*2+1]*rz, col2);
|
||||
dd->vertex(cx+dir[j*2+0]*rx, maxy, cz+dir[j*2+1]*rz, col);
|
||||
|
||||
dd->vertex(cx+dir[i*2+0]*rx, miny, cz+dir[i*2+1]*rz, col2);
|
||||
dd->vertex(cx+dir[j*2+0]*rx, maxy, cz+dir[j*2+1]*rz, col);
|
||||
dd->vertex(cx+dir[i*2+0]*rx, maxy, cz+dir[i*2+1]*rz, col);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline void evalArc(const float x0, const float y0, const float z0,
|
||||
const float dx, const float dy, const float dz,
|
||||
const float h, const float u, float* res)
|
||||
{
|
||||
res[0] = x0 + dx * u;
|
||||
res[1] = y0 + dy * u + h * (1-(u*2-1)*(u*2-1));
|
||||
res[2] = z0 + dz * u;
|
||||
}
|
||||
|
||||
|
||||
inline void vcross(float* dest, const float* v1, const float* v2)
|
||||
{
|
||||
dest[0] = v1[1]*v2[2] - v1[2]*v2[1];
|
||||
dest[1] = v1[2]*v2[0] - v1[0]*v2[2];
|
||||
dest[2] = v1[0]*v2[1] - v1[1]*v2[0];
|
||||
}
|
||||
|
||||
inline void vnormalize(float* v)
|
||||
{
|
||||
float d = 1.0f / sqrtf(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
|
||||
v[0] *= d;
|
||||
v[1] *= d;
|
||||
v[2] *= d;
|
||||
}
|
||||
|
||||
inline void vsub(float* dest, const float* v1, const float* v2)
|
||||
{
|
||||
dest[0] = v1[0]-v2[0];
|
||||
dest[1] = v1[1]-v2[1];
|
||||
dest[2] = v1[2]-v2[2];
|
||||
}
|
||||
|
||||
inline float vdistSqr(const float* v1, const float* v2)
|
||||
{
|
||||
const float x = v1[0]-v2[0];
|
||||
const float y = v1[1]-v2[1];
|
||||
const float z = v1[2]-v2[2];
|
||||
return x*x + y*y + z*z;
|
||||
}
|
||||
|
||||
|
||||
void appendArrowHead(struct duDebugDraw* dd, const float* p, const float* q,
|
||||
const float s, unsigned int col)
|
||||
{
|
||||
const float eps = 0.001f;
|
||||
if (!dd) return;
|
||||
if (vdistSqr(p,q) < eps*eps) return;
|
||||
float ax[3], ay[3] = {0,1,0}, az[3];
|
||||
vsub(az, q, p);
|
||||
vnormalize(az);
|
||||
vcross(ax, ay, az);
|
||||
vcross(ay, az, ax);
|
||||
vnormalize(ay);
|
||||
|
||||
dd->vertex(p, col);
|
||||
// dd->vertex(p[0]+az[0]*s+ay[0]*s/2, p[1]+az[1]*s+ay[1]*s/2, p[2]+az[2]*s+ay[2]*s/2, col);
|
||||
dd->vertex(p[0]+az[0]*s+ax[0]*s/3, p[1]+az[1]*s+ax[1]*s/3, p[2]+az[2]*s+ax[2]*s/3, col);
|
||||
|
||||
dd->vertex(p, col);
|
||||
// dd->vertex(p[0]+az[0]*s-ay[0]*s/2, p[1]+az[1]*s-ay[1]*s/2, p[2]+az[2]*s-ay[2]*s/2, col);
|
||||
dd->vertex(p[0]+az[0]*s-ax[0]*s/3, p[1]+az[1]*s-ax[1]*s/3, p[2]+az[2]*s-ax[2]*s/3, col);
|
||||
|
||||
}
|
||||
|
||||
void duAppendArc(struct duDebugDraw* dd, const float x0, const float y0, const float z0,
|
||||
const float x1, const float y1, const float z1, const float h,
|
||||
const float as0, const float as1, unsigned int col)
|
||||
{
|
||||
if (!dd) return;
|
||||
static const int NUM_ARC_PTS = 8;
|
||||
static const float PAD = 0.05f;
|
||||
static const float ARC_PTS_SCALE = (1.0f-PAD*2) / (float)NUM_ARC_PTS;
|
||||
const float dx = x1 - x0;
|
||||
const float dy = y1 - y0;
|
||||
const float dz = z1 - z0;
|
||||
const float len = sqrtf(dx*dx + dy*dy + dz*dz);
|
||||
float prev[3];
|
||||
evalArc(x0,y0,z0, dx,dy,dz, len*h, PAD, prev);
|
||||
for (int i = 1; i <= NUM_ARC_PTS; ++i)
|
||||
{
|
||||
const float u = PAD + i * ARC_PTS_SCALE;
|
||||
float pt[3];
|
||||
evalArc(x0,y0,z0, dx,dy,dz, len*h, u, pt);
|
||||
dd->vertex(prev[0],prev[1],prev[2], col);
|
||||
dd->vertex(pt[0],pt[1],pt[2], col);
|
||||
prev[0] = pt[0]; prev[1] = pt[1]; prev[2] = pt[2];
|
||||
}
|
||||
|
||||
// End arrows
|
||||
if (as0 > 0.001f)
|
||||
{
|
||||
float p[3], q[3];
|
||||
evalArc(x0,y0,z0, dx,dy,dz, len*h, PAD, p);
|
||||
evalArc(x0,y0,z0, dx,dy,dz, len*h, PAD+0.05f, q);
|
||||
appendArrowHead(dd, p, q, as0, col);
|
||||
}
|
||||
|
||||
if (as1 > 0.001f)
|
||||
{
|
||||
float p[3], q[3];
|
||||
evalArc(x0,y0,z0, dx,dy,dz, len*h, 1-PAD, p);
|
||||
evalArc(x0,y0,z0, dx,dy,dz, len*h, 1-(PAD+0.05f), q);
|
||||
appendArrowHead(dd, p, q, as1, col);
|
||||
}
|
||||
}
|
||||
|
||||
void duAppendArrow(struct duDebugDraw* dd, const float x0, const float y0, const float z0,
|
||||
const float x1, const float y1, const float z1,
|
||||
const float as0, const float as1, unsigned int col)
|
||||
{
|
||||
if (!dd) return;
|
||||
|
||||
dd->vertex(x0,y0,z0, col);
|
||||
dd->vertex(x1,y1,z1, col);
|
||||
|
||||
// End arrows
|
||||
const float p[3] = {x0,y0,z0}, q[3] = {x1,y1,z1};
|
||||
if (as0 > 0.001f)
|
||||
appendArrowHead(dd, p, q, as0, col);
|
||||
if (as1 > 0.001f)
|
||||
appendArrowHead(dd, q, p, as1, col);
|
||||
}
|
||||
|
||||
void duAppendCircle(struct duDebugDraw* dd, const float x, const float y, const float z,
|
||||
const float r, unsigned int col)
|
||||
{
|
||||
if (!dd) return;
|
||||
static const int NUM_SEG = 40;
|
||||
static float dir[40*2];
|
||||
static bool init = false;
|
||||
if (!init)
|
||||
{
|
||||
init = true;
|
||||
for (int i = 0; i < NUM_SEG; ++i)
|
||||
{
|
||||
const float a = (float)i/(float)NUM_SEG*DU_PI*2;
|
||||
dir[i*2] = cosf(a);
|
||||
dir[i*2+1] = sinf(a);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0, j = NUM_SEG-1; i < NUM_SEG; j = i++)
|
||||
{
|
||||
dd->vertex(x+dir[j*2+0]*r, y, z+dir[j*2+1]*r, col);
|
||||
dd->vertex(x+dir[i*2+0]*r, y, z+dir[i*2+1]*r, col);
|
||||
}
|
||||
}
|
||||
|
||||
void duAppendCross(struct duDebugDraw* dd, const float x, const float y, const float z,
|
||||
const float s, unsigned int col)
|
||||
{
|
||||
if (!dd) return;
|
||||
dd->vertex(x-s,y,z, col);
|
||||
dd->vertex(x+s,y,z, col);
|
||||
dd->vertex(x,y-s,z, col);
|
||||
dd->vertex(x,y+s,z, col);
|
||||
dd->vertex(x,y,z-s, col);
|
||||
dd->vertex(x,y,z+s, col);
|
||||
}
|
||||
|
||||
duDisplayList::duDisplayList(int cap) :
|
||||
m_pos(0),
|
||||
m_color(0),
|
||||
m_size(0),
|
||||
m_cap(0),
|
||||
m_depthMask(true),
|
||||
m_prim(DU_DRAW_LINES),
|
||||
m_primSize(1.0f)
|
||||
{
|
||||
if (cap < 8)
|
||||
cap = 8;
|
||||
resize(cap);
|
||||
}
|
||||
|
||||
duDisplayList::~duDisplayList()
|
||||
{
|
||||
delete [] m_pos;
|
||||
delete [] m_color;
|
||||
}
|
||||
|
||||
void duDisplayList::resize(int cap)
|
||||
{
|
||||
float* newPos = new float[cap*3];
|
||||
if (m_size)
|
||||
memcpy(newPos, m_pos, sizeof(float)*3*m_size);
|
||||
delete [] m_pos;
|
||||
m_pos = newPos;
|
||||
|
||||
unsigned int* newColor = new unsigned int[cap];
|
||||
if (m_size)
|
||||
memcpy(newColor, m_color, sizeof(unsigned int)*m_size);
|
||||
delete [] m_color;
|
||||
m_color = newColor;
|
||||
|
||||
m_cap = cap;
|
||||
}
|
||||
|
||||
void duDisplayList::clear()
|
||||
{
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
void duDisplayList::depthMask(bool state)
|
||||
{
|
||||
m_depthMask = state;
|
||||
}
|
||||
|
||||
void duDisplayList::begin(duDebugDrawPrimitives prim, float size)
|
||||
{
|
||||
clear();
|
||||
m_prim = prim;
|
||||
m_primSize = size;
|
||||
}
|
||||
|
||||
void duDisplayList::vertex(const float x, const float y, const float z, unsigned int color)
|
||||
{
|
||||
if (m_size+1 >= m_cap)
|
||||
resize(m_cap*2);
|
||||
float* p = &m_pos[m_size*3];
|
||||
p[0] = x;
|
||||
p[1] = y;
|
||||
p[2] = z;
|
||||
m_color[m_size] = color;
|
||||
m_size++;
|
||||
}
|
||||
|
||||
void duDisplayList::vertex(const float* pos, unsigned int color)
|
||||
{
|
||||
vertex(pos[0],pos[1],pos[2],color);
|
||||
}
|
||||
|
||||
void duDisplayList::end()
|
||||
{
|
||||
}
|
||||
|
||||
void duDisplayList::draw(struct duDebugDraw* dd)
|
||||
{
|
||||
if (!dd) return;
|
||||
if (!m_size) return;
|
||||
dd->depthMask(m_depthMask);
|
||||
dd->begin(m_prim, m_primSize);
|
||||
for (int i = 0; i < m_size; ++i)
|
||||
dd->vertex(&m_pos[i*3], m_color[i]);
|
||||
dd->end();
|
||||
}
|
||||
880
recast/debug_utils/src/DetourDebugDraw.cpp
Normal file
880
recast/debug_utils/src/DetourDebugDraw.cpp
Normal file
@ -0,0 +1,880 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#include "DebugDraw.h"
|
||||
#include "DetourDebugDraw.h"
|
||||
#include "DetourNavMesh.h"
|
||||
#include "DetourCommon.h"
|
||||
#include "DetourNode.h"
|
||||
|
||||
|
||||
static float distancePtLine2d(const float* pt, const float* p, const float* q)
|
||||
{
|
||||
float pqx = q[0] - p[0];
|
||||
float pqz = q[2] - p[2];
|
||||
float dx = pt[0] - p[0];
|
||||
float dz = pt[2] - p[2];
|
||||
float d = pqx*pqx + pqz*pqz;
|
||||
float t = pqx*dx + pqz*dz;
|
||||
if (d != 0) t /= d;
|
||||
dx = p[0] + t*pqx - pt[0];
|
||||
dz = p[2] + t*pqz - pt[2];
|
||||
return dx*dx + dz*dz;
|
||||
}
|
||||
|
||||
static void drawPolyBoundaries(duDebugDraw* dd, const dtMeshTile* tile,
|
||||
const unsigned int col, const float linew,
|
||||
bool inner)
|
||||
{
|
||||
static const float thr = 0.01f*0.01f;
|
||||
|
||||
dd->begin(DU_DRAW_LINES, linew);
|
||||
|
||||
for (int i = 0; i < tile->header->polyCount; ++i)
|
||||
{
|
||||
const dtPoly* p = &tile->polys[i];
|
||||
|
||||
if (p->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) continue;
|
||||
|
||||
const dtPolyDetail* pd = &tile->detailMeshes[i];
|
||||
|
||||
for (int j = 0, nj = (int)p->vertCount; j < nj; ++j)
|
||||
{
|
||||
unsigned int c = col;
|
||||
if (inner)
|
||||
{
|
||||
if (p->neis[j] == 0) continue;
|
||||
if (p->neis[j] & DT_EXT_LINK)
|
||||
{
|
||||
bool con = false;
|
||||
for (unsigned int k = p->firstLink; k != DT_NULL_LINK; k = tile->links[k].next)
|
||||
{
|
||||
if (tile->links[k].edge == j)
|
||||
{
|
||||
con = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (con)
|
||||
c = duRGBA(255,255,255,48);
|
||||
else
|
||||
c = duRGBA(0,0,0,48);
|
||||
}
|
||||
else
|
||||
c = duRGBA(0,48,64,32);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (p->neis[j] != 0) continue;
|
||||
}
|
||||
|
||||
const float* v0 = &tile->verts[p->verts[j]*3];
|
||||
const float* v1 = &tile->verts[p->verts[(j+1) % nj]*3];
|
||||
|
||||
// Draw detail mesh edges which align with the actual poly edge.
|
||||
// This is really slow.
|
||||
for (int k = 0; k < pd->triCount; ++k)
|
||||
{
|
||||
const unsigned char* t = &tile->detailTris[(pd->triBase+k)*4];
|
||||
const float* tv[3];
|
||||
for (int m = 0; m < 3; ++m)
|
||||
{
|
||||
if (t[m] < p->vertCount)
|
||||
tv[m] = &tile->verts[p->verts[t[m]]*3];
|
||||
else
|
||||
tv[m] = &tile->detailVerts[(pd->vertBase+(t[m]-p->vertCount))*3];
|
||||
}
|
||||
for (int m = 0, n = 2; m < 3; n=m++)
|
||||
{
|
||||
if (((t[3] >> (n*2)) & 0x3) == 0) continue; // Skip inner detail edges.
|
||||
if (distancePtLine2d(tv[n],v0,v1) < thr &&
|
||||
distancePtLine2d(tv[m],v0,v1) < thr)
|
||||
{
|
||||
dd->vertex(tv[n], c);
|
||||
dd->vertex(tv[m], c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dd->end();
|
||||
}
|
||||
|
||||
static void drawMeshTile(duDebugDraw* dd, const dtNavMesh& mesh, const dtNavMeshQuery* query,
|
||||
const dtMeshTile* tile, unsigned char flags)
|
||||
{
|
||||
dtPolyRef base = mesh.getPolyRefBase(tile);
|
||||
|
||||
int tileNum = mesh.decodePolyIdTile(base);
|
||||
|
||||
dd->depthMask(false);
|
||||
|
||||
dd->begin(DU_DRAW_TRIS);
|
||||
for (int i = 0; i < tile->header->polyCount; ++i)
|
||||
{
|
||||
const dtPoly* p = &tile->polys[i];
|
||||
if (p->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) // Skip off-mesh links.
|
||||
continue;
|
||||
|
||||
const dtPolyDetail* pd = &tile->detailMeshes[i];
|
||||
|
||||
unsigned int col;
|
||||
if (query && query->isInClosedList(base | (dtPolyRef)i))
|
||||
col = duRGBA(255,196,0,64);
|
||||
else
|
||||
{
|
||||
if (flags & DU_DRAWNAVMESH_COLOR_TILES)
|
||||
{
|
||||
col = duIntToCol(tileNum, 128);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (p->getArea() == 0) // Treat zero area type as default.
|
||||
col = duRGBA(0,192,255,64);
|
||||
else
|
||||
col = duIntToCol(p->getArea(), 64);
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < pd->triCount; ++j)
|
||||
{
|
||||
const unsigned char* t = &tile->detailTris[(pd->triBase+j)*4];
|
||||
for (int k = 0; k < 3; ++k)
|
||||
{
|
||||
if (t[k] < p->vertCount)
|
||||
dd->vertex(&tile->verts[p->verts[t[k]]*3], col);
|
||||
else
|
||||
dd->vertex(&tile->detailVerts[(pd->vertBase+t[k]-p->vertCount)*3], col);
|
||||
}
|
||||
}
|
||||
}
|
||||
dd->end();
|
||||
|
||||
// Draw inter poly boundaries
|
||||
drawPolyBoundaries(dd, tile, duRGBA(0,48,64,32), 1.5f, true);
|
||||
|
||||
// Draw outer poly boundaries
|
||||
drawPolyBoundaries(dd, tile, duRGBA(0,48,64,220), 2.5f, false);
|
||||
|
||||
if (flags & DU_DRAWNAVMESH_OFFMESHCONS)
|
||||
{
|
||||
dd->begin(DU_DRAW_LINES, 2.0f);
|
||||
for (int i = 0; i < tile->header->polyCount; ++i)
|
||||
{
|
||||
const dtPoly* p = &tile->polys[i];
|
||||
if (p->getType() != DT_POLYTYPE_OFFMESH_CONNECTION) // Skip regular polys.
|
||||
continue;
|
||||
|
||||
unsigned int col, col2;
|
||||
if (query && query->isInClosedList(base | (dtPolyRef)i))
|
||||
col = duRGBA(255,196,0,220);
|
||||
else
|
||||
col = duDarkenCol(duIntToCol(p->getArea(), 220));
|
||||
|
||||
const dtOffMeshConnection* con = &tile->offMeshCons[i - tile->header->offMeshBase];
|
||||
const float* va = &tile->verts[p->verts[0]*3];
|
||||
const float* vb = &tile->verts[p->verts[1]*3];
|
||||
|
||||
// Check to see if start and end end-points have links.
|
||||
bool startSet = false;
|
||||
bool endSet = false;
|
||||
for (unsigned int k = p->firstLink; k != DT_NULL_LINK; k = tile->links[k].next)
|
||||
{
|
||||
if (tile->links[k].edge == 0)
|
||||
startSet = true;
|
||||
if (tile->links[k].edge == 1)
|
||||
endSet = true;
|
||||
}
|
||||
|
||||
// End points and their on-mesh locations.
|
||||
dd->vertex(va[0],va[1],va[2], col);
|
||||
dd->vertex(con->pos[0],con->pos[1],con->pos[2], col);
|
||||
col2 = startSet ? col : duRGBA(220,32,16,196);
|
||||
duAppendCircle(dd, con->pos[0],con->pos[1]+0.1f,con->pos[2], con->rad, col2);
|
||||
|
||||
dd->vertex(vb[0],vb[1],vb[2], col);
|
||||
dd->vertex(con->pos[3],con->pos[4],con->pos[5], col);
|
||||
col2 = endSet ? col : duRGBA(220,32,16,196);
|
||||
duAppendCircle(dd, con->pos[3],con->pos[4]+0.1f,con->pos[5], con->rad, col2);
|
||||
|
||||
// End point vertices.
|
||||
dd->vertex(con->pos[0],con->pos[1],con->pos[2], duRGBA(0,48,64,196));
|
||||
dd->vertex(con->pos[0],con->pos[1]+0.2f,con->pos[2], duRGBA(0,48,64,196));
|
||||
|
||||
dd->vertex(con->pos[3],con->pos[4],con->pos[5], duRGBA(0,48,64,196));
|
||||
dd->vertex(con->pos[3],con->pos[4]+0.2f,con->pos[5], duRGBA(0,48,64,196));
|
||||
|
||||
// Connection arc.
|
||||
duAppendArc(dd, con->pos[0],con->pos[1],con->pos[2], con->pos[3],con->pos[4],con->pos[5], 0.25f,
|
||||
(con->flags & 1) ? 0.6f : 0, 0.6f, col);
|
||||
}
|
||||
dd->end();
|
||||
}
|
||||
|
||||
const unsigned int vcol = duRGBA(0,0,0,196);
|
||||
dd->begin(DU_DRAW_POINTS, 3.0f);
|
||||
for (int i = 0; i < tile->header->vertCount; ++i)
|
||||
{
|
||||
const float* v = &tile->verts[i*3];
|
||||
dd->vertex(v[0], v[1], v[2], vcol);
|
||||
}
|
||||
dd->end();
|
||||
|
||||
dd->depthMask(true);
|
||||
}
|
||||
|
||||
void duDebugDrawNavMesh(duDebugDraw* dd, const dtNavMesh& mesh, unsigned char flags)
|
||||
{
|
||||
if (!dd) return;
|
||||
|
||||
for (int i = 0; i < mesh.getMaxTiles(); ++i)
|
||||
{
|
||||
const dtMeshTile* tile = mesh.getTile(i);
|
||||
if (!tile->header) continue;
|
||||
drawMeshTile(dd, mesh, 0, tile, flags);
|
||||
}
|
||||
}
|
||||
|
||||
void duDebugDrawNavMeshTile(struct duDebugDraw* dd, const dtNavMesh& mesh, int w, int h, int layer, unsigned char flags) {
|
||||
const dtMeshTile* tile = mesh.getTileAt(w, h, layer);
|
||||
if (!tile) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tile->header) {
|
||||
return;
|
||||
}
|
||||
|
||||
drawMeshTile(dd, mesh, 0, tile, flags);
|
||||
}
|
||||
|
||||
void duDebugDrawNavMeshWithClosedList(struct duDebugDraw* dd, const dtNavMesh& mesh, const dtNavMeshQuery& query, unsigned char flags)
|
||||
{
|
||||
if (!dd) return;
|
||||
|
||||
const dtNavMeshQuery* q = (flags & DU_DRAWNAVMESH_CLOSEDLIST) ? &query : 0;
|
||||
|
||||
for (int i = 0; i < mesh.getMaxTiles(); ++i)
|
||||
{
|
||||
const dtMeshTile* tile = mesh.getTile(i);
|
||||
if (!tile->header) continue;
|
||||
drawMeshTile(dd, mesh, q, tile, flags);
|
||||
}
|
||||
}
|
||||
|
||||
void duDebugDrawNavMeshNodes(struct duDebugDraw* dd, const dtNavMeshQuery& query)
|
||||
{
|
||||
if (!dd) return;
|
||||
|
||||
const dtNodePool* pool = query.getNodePool();
|
||||
if (pool)
|
||||
{
|
||||
const float off = 0.5f;
|
||||
dd->begin(DU_DRAW_POINTS, 4.0f);
|
||||
for (int i = 0; i < pool->getHashSize(); ++i)
|
||||
{
|
||||
for (dtNodeIndex j = pool->getFirst(i); j != DT_NULL_IDX; j = pool->getNext(j))
|
||||
{
|
||||
const dtNode* node = pool->getNodeAtIdx(j+1);
|
||||
if (!node) continue;
|
||||
dd->vertex(node->pos[0],node->pos[1]+off,node->pos[2], duRGBA(255,192,0,255));
|
||||
}
|
||||
}
|
||||
dd->end();
|
||||
|
||||
dd->begin(DU_DRAW_LINES, 2.0f);
|
||||
for (int i = 0; i < pool->getHashSize(); ++i)
|
||||
{
|
||||
for (dtNodeIndex j = pool->getFirst(i); j != DT_NULL_IDX; j = pool->getNext(j))
|
||||
{
|
||||
const dtNode* node = pool->getNodeAtIdx(j+1);
|
||||
if (!node) continue;
|
||||
if (!node->pidx) continue;
|
||||
const dtNode* parent = pool->getNodeAtIdx(node->pidx);
|
||||
if (!parent) continue;
|
||||
dd->vertex(node->pos[0],node->pos[1]+off,node->pos[2], duRGBA(255,192,0,128));
|
||||
dd->vertex(parent->pos[0],parent->pos[1]+off,parent->pos[2], duRGBA(255,192,0,128));
|
||||
}
|
||||
}
|
||||
dd->end();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void drawMeshTileBVTree(duDebugDraw* dd, const dtMeshTile* tile)
|
||||
{
|
||||
// Draw BV nodes.
|
||||
const float cs = 1.0f / tile->header->bvQuantFactor;
|
||||
dd->begin(DU_DRAW_LINES, 1.0f);
|
||||
for (int i = 0; i < tile->header->bvNodeCount; ++i)
|
||||
{
|
||||
const dtBVNode* n = &tile->bvTree[i];
|
||||
if (n->i < 0) // Leaf indices are positive.
|
||||
continue;
|
||||
duAppendBoxWire(dd, tile->header->bmin[0] + n->bmin[0]*cs,
|
||||
tile->header->bmin[1] + n->bmin[1]*cs,
|
||||
tile->header->bmin[2] + n->bmin[2]*cs,
|
||||
tile->header->bmin[0] + n->bmax[0]*cs,
|
||||
tile->header->bmin[1] + n->bmax[1]*cs,
|
||||
tile->header->bmin[2] + n->bmax[2]*cs,
|
||||
duRGBA(255,255,255,128));
|
||||
}
|
||||
dd->end();
|
||||
}
|
||||
|
||||
void duDebugDrawNavMeshBVTree(duDebugDraw* dd, const dtNavMesh& mesh)
|
||||
{
|
||||
if (!dd) return;
|
||||
|
||||
for (int i = 0; i < mesh.getMaxTiles(); ++i)
|
||||
{
|
||||
const dtMeshTile* tile = mesh.getTile(i);
|
||||
if (!tile->header) continue;
|
||||
drawMeshTileBVTree(dd, tile);
|
||||
}
|
||||
}
|
||||
|
||||
static void drawMeshTilePortal(duDebugDraw* dd, const dtMeshTile* tile)
|
||||
{
|
||||
// Draw portals
|
||||
const float padx = 0.04f;
|
||||
const float pady = tile->header->walkableClimb;
|
||||
|
||||
dd->begin(DU_DRAW_LINES, 2.0f);
|
||||
|
||||
for (int side = 0; side < 8; ++side)
|
||||
{
|
||||
unsigned short m = DT_EXT_LINK | (unsigned short)side;
|
||||
|
||||
for (int i = 0; i < tile->header->polyCount; ++i)
|
||||
{
|
||||
dtPoly* poly = &tile->polys[i];
|
||||
|
||||
// Create new links.
|
||||
const int nv = poly->vertCount;
|
||||
for (int j = 0; j < nv; ++j)
|
||||
{
|
||||
// Skip edges which do not point to the right side.
|
||||
if (poly->neis[j] != m)
|
||||
continue;
|
||||
|
||||
// Create new links
|
||||
const float* va = &tile->verts[poly->verts[j]*3];
|
||||
const float* vb = &tile->verts[poly->verts[(j+1) % nv]*3];
|
||||
|
||||
if (side == 0 || side == 4)
|
||||
{
|
||||
unsigned int col = side == 0 ? duRGBA(128,0,0,128) : duRGBA(128,0,128,128);
|
||||
|
||||
const float x = va[0] + ((side == 0) ? -padx : padx);
|
||||
|
||||
dd->vertex(x,va[1]-pady,va[2], col);
|
||||
dd->vertex(x,va[1]+pady,va[2], col);
|
||||
|
||||
dd->vertex(x,va[1]+pady,va[2], col);
|
||||
dd->vertex(x,vb[1]+pady,vb[2], col);
|
||||
|
||||
dd->vertex(x,vb[1]+pady,vb[2], col);
|
||||
dd->vertex(x,vb[1]-pady,vb[2], col);
|
||||
|
||||
dd->vertex(x,vb[1]-pady,vb[2], col);
|
||||
dd->vertex(x,va[1]-pady,va[2], col);
|
||||
}
|
||||
else if (side == 2 || side == 6)
|
||||
{
|
||||
unsigned int col = side == 2 ? duRGBA(0,128,0,128) : duRGBA(0,128,128,128);
|
||||
|
||||
const float z = va[2] + ((side == 2) ? -padx : padx);
|
||||
|
||||
dd->vertex(va[0],va[1]-pady,z, col);
|
||||
dd->vertex(va[0],va[1]+pady,z, col);
|
||||
|
||||
dd->vertex(va[0],va[1]+pady,z, col);
|
||||
dd->vertex(vb[0],vb[1]+pady,z, col);
|
||||
|
||||
dd->vertex(vb[0],vb[1]+pady,z, col);
|
||||
dd->vertex(vb[0],vb[1]-pady,z, col);
|
||||
|
||||
dd->vertex(vb[0],vb[1]-pady,z, col);
|
||||
dd->vertex(va[0],va[1]-pady,z, col);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dd->end();
|
||||
}
|
||||
|
||||
void duDebugDrawNavMeshPortals(duDebugDraw* dd, const dtNavMesh& mesh)
|
||||
{
|
||||
if (!dd) return;
|
||||
|
||||
for (int i = 0; i < mesh.getMaxTiles(); ++i)
|
||||
{
|
||||
const dtMeshTile* tile = mesh.getTile(i);
|
||||
if (!tile->header) continue;
|
||||
drawMeshTilePortal(dd, tile);
|
||||
}
|
||||
}
|
||||
|
||||
void duDebugDrawNavMeshPolysWithFlags(struct duDebugDraw* dd, const dtNavMesh& mesh,
|
||||
const unsigned short polyFlags, const unsigned int col)
|
||||
{
|
||||
if (!dd) return;
|
||||
|
||||
for (int i = 0; i < mesh.getMaxTiles(); ++i)
|
||||
{
|
||||
const dtMeshTile* tile = mesh.getTile(i);
|
||||
if (!tile->header) continue;
|
||||
dtPolyRef base = mesh.getPolyRefBase(tile);
|
||||
|
||||
for (int j = 0; j < tile->header->polyCount; ++j)
|
||||
{
|
||||
const dtPoly* p = &tile->polys[j];
|
||||
if ((p->flags & polyFlags) == 0) continue;
|
||||
duDebugDrawNavMeshPoly(dd, mesh, base|(dtPolyRef)j, col);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void duDebugDrawNavMeshPoly(duDebugDraw* dd, const dtNavMesh& mesh, dtPolyRef ref, const unsigned int col)
|
||||
{
|
||||
if (!dd) return;
|
||||
|
||||
const dtMeshTile* tile = 0;
|
||||
const dtPoly* poly = 0;
|
||||
if (dtStatusFailed(mesh.getTileAndPolyByRef(ref, &tile, &poly)))
|
||||
return;
|
||||
|
||||
dd->depthMask(false);
|
||||
|
||||
const unsigned int c = (col & 0x00ffffff) | (64 << 24);
|
||||
const unsigned int ip = (unsigned int)(poly - tile->polys);
|
||||
|
||||
if (poly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
|
||||
{
|
||||
dtOffMeshConnection* con = &tile->offMeshCons[ip - tile->header->offMeshBase];
|
||||
|
||||
dd->begin(DU_DRAW_LINES, 2.0f);
|
||||
|
||||
// Connection arc.
|
||||
duAppendArc(dd, con->pos[0],con->pos[1],con->pos[2], con->pos[3],con->pos[4],con->pos[5], 0.25f,
|
||||
(con->flags & 1) ? 0.6f : 0, 0.6f, c);
|
||||
|
||||
dd->end();
|
||||
}
|
||||
else
|
||||
{
|
||||
const dtPolyDetail* pd = &tile->detailMeshes[ip];
|
||||
|
||||
dd->begin(DU_DRAW_TRIS);
|
||||
for (int i = 0; i < pd->triCount; ++i)
|
||||
{
|
||||
const unsigned char* t = &tile->detailTris[(pd->triBase+i)*4];
|
||||
for (int j = 0; j < 3; ++j)
|
||||
{
|
||||
if (t[j] < poly->vertCount)
|
||||
dd->vertex(&tile->verts[poly->verts[t[j]]*3], c);
|
||||
else
|
||||
dd->vertex(&tile->detailVerts[(pd->vertBase+t[j]-poly->vertCount)*3], c);
|
||||
}
|
||||
}
|
||||
dd->end();
|
||||
}
|
||||
|
||||
dd->depthMask(true);
|
||||
|
||||
}
|
||||
|
||||
static void debugDrawTileCachePortals(struct duDebugDraw* dd, const dtTileCacheLayer& layer, const float cs, const float ch)
|
||||
{
|
||||
const int w = (int)layer.header->width;
|
||||
const int h = (int)layer.header->height;
|
||||
const float* bmin = layer.header->bmin;
|
||||
|
||||
// Portals
|
||||
unsigned int pcol = duRGBA(255,255,255,255);
|
||||
|
||||
const int segs[4*4] = {0,0,0,1, 0,1,1,1, 1,1,1,0, 1,0,0,0};
|
||||
|
||||
// Layer portals
|
||||
dd->begin(DU_DRAW_LINES, 2.0f);
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
const int idx = x+y*w;
|
||||
const int lh = (int)layer.heights[idx];
|
||||
if (lh == 0xff) continue;
|
||||
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
if (layer.cons[idx] & (1<<(dir+4)))
|
||||
{
|
||||
const int* seg = &segs[dir*4];
|
||||
const float ax = bmin[0] + (x+seg[0])*cs;
|
||||
const float ay = bmin[1] + (lh+2)*ch;
|
||||
const float az = bmin[2] + (y+seg[1])*cs;
|
||||
const float bx = bmin[0] + (x+seg[2])*cs;
|
||||
const float by = bmin[1] + (lh+2)*ch;
|
||||
const float bz = bmin[2] + (y+seg[3])*cs;
|
||||
dd->vertex(ax, ay, az, pcol);
|
||||
dd->vertex(bx, by, bz, pcol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dd->end();
|
||||
}
|
||||
|
||||
void duDebugDrawTileCacheLayerAreas(struct duDebugDraw* dd, const dtTileCacheLayer& layer, const float cs, const float ch)
|
||||
{
|
||||
const int w = (int)layer.header->width;
|
||||
const int h = (int)layer.header->height;
|
||||
const float* bmin = layer.header->bmin;
|
||||
const float* bmax = layer.header->bmax;
|
||||
const int idx = layer.header->tlayer;
|
||||
|
||||
unsigned int color = duIntToCol(idx+1, 255);
|
||||
|
||||
// Layer bounds
|
||||
float lbmin[3], lbmax[3];
|
||||
lbmin[0] = bmin[0] + layer.header->minx*cs;
|
||||
lbmin[1] = bmin[1];
|
||||
lbmin[2] = bmin[2] + layer.header->miny*cs;
|
||||
lbmax[0] = bmin[0] + (layer.header->maxx+1)*cs;
|
||||
lbmax[1] = bmax[1];
|
||||
lbmax[2] = bmin[2] + (layer.header->maxy+1)*cs;
|
||||
duDebugDrawBoxWire(dd, lbmin[0],lbmin[1],lbmin[2], lbmax[0],lbmax[1],lbmax[2], duTransCol(color,128), 2.0f);
|
||||
|
||||
// Layer height
|
||||
dd->begin(DU_DRAW_QUADS);
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
const int lidx = x+y*w;
|
||||
const int lh = (int)layer.heights[lidx];
|
||||
if (lh == 0xff) continue;
|
||||
const unsigned char area = layer.areas[lidx];
|
||||
|
||||
unsigned int col;
|
||||
if (area == 63)
|
||||
col = duLerpCol(color, duRGBA(0,192,255,64), 32);
|
||||
else if (area == 0)
|
||||
col = duLerpCol(color, duRGBA(0,0,0,64), 32);
|
||||
else
|
||||
col = duLerpCol(color, duIntToCol(area, 255), 32);
|
||||
|
||||
const float fx = bmin[0] + x*cs;
|
||||
const float fy = bmin[1] + (lh+1)*ch;
|
||||
const float fz = bmin[2] + y*cs;
|
||||
|
||||
dd->vertex(fx, fy, fz, col);
|
||||
dd->vertex(fx, fy, fz+cs, col);
|
||||
dd->vertex(fx+cs, fy, fz+cs, col);
|
||||
dd->vertex(fx+cs, fy, fz, col);
|
||||
}
|
||||
}
|
||||
dd->end();
|
||||
|
||||
debugDrawTileCachePortals(dd, layer, cs, ch);
|
||||
}
|
||||
|
||||
void duDebugDrawTileCacheLayerRegions(struct duDebugDraw* dd, const dtTileCacheLayer& layer, const float cs, const float ch)
|
||||
{
|
||||
const int w = (int)layer.header->width;
|
||||
const int h = (int)layer.header->height;
|
||||
const float* bmin = layer.header->bmin;
|
||||
const float* bmax = layer.header->bmax;
|
||||
const int idx = layer.header->tlayer;
|
||||
|
||||
unsigned int color = duIntToCol(idx+1, 255);
|
||||
|
||||
// Layer bounds
|
||||
float lbmin[3], lbmax[3];
|
||||
lbmin[0] = bmin[0] + layer.header->minx*cs;
|
||||
lbmin[1] = bmin[1];
|
||||
lbmin[2] = bmin[2] + layer.header->miny*cs;
|
||||
lbmax[0] = bmin[0] + (layer.header->maxx+1)*cs;
|
||||
lbmax[1] = bmax[1];
|
||||
lbmax[2] = bmin[2] + (layer.header->maxy+1)*cs;
|
||||
duDebugDrawBoxWire(dd, lbmin[0],lbmin[1],lbmin[2], lbmax[0],lbmax[1],lbmax[2], duTransCol(color,128), 2.0f);
|
||||
|
||||
// Layer height
|
||||
dd->begin(DU_DRAW_QUADS);
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
const int lidx = x+y*w;
|
||||
const int lh = (int)layer.heights[lidx];
|
||||
if (lh == 0xff) continue;
|
||||
const unsigned char reg = layer.regs[lidx];
|
||||
|
||||
unsigned int col = duLerpCol(color, duIntToCol(reg, 255), 192);
|
||||
|
||||
const float fx = bmin[0] + x*cs;
|
||||
const float fy = bmin[1] + (lh+1)*ch;
|
||||
const float fz = bmin[2] + y*cs;
|
||||
|
||||
dd->vertex(fx, fy, fz, col);
|
||||
dd->vertex(fx, fy, fz+cs, col);
|
||||
dd->vertex(fx+cs, fy, fz+cs, col);
|
||||
dd->vertex(fx+cs, fy, fz, col);
|
||||
}
|
||||
}
|
||||
dd->end();
|
||||
|
||||
debugDrawTileCachePortals(dd, layer, cs, ch);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*struct dtTileCacheContour
|
||||
{
|
||||
int nverts;
|
||||
unsigned char* verts;
|
||||
unsigned char reg;
|
||||
unsigned char area;
|
||||
};
|
||||
|
||||
struct dtTileCacheContourSet
|
||||
{
|
||||
int nconts;
|
||||
dtTileCacheContour* conts;
|
||||
};*/
|
||||
|
||||
void duDebugDrawTileCacheContours(duDebugDraw* dd, const struct dtTileCacheContourSet& lcset,
|
||||
const float* orig, const float cs, const float ch)
|
||||
{
|
||||
if (!dd) return;
|
||||
|
||||
const unsigned char a = 255;// (unsigned char)(alpha*255.0f);
|
||||
|
||||
const int offs[2*4] = {-1,0, 0,1, 1,0, 0,-1};
|
||||
|
||||
dd->begin(DU_DRAW_LINES, 2.0f);
|
||||
|
||||
for (int i = 0; i < lcset.nconts; ++i)
|
||||
{
|
||||
const dtTileCacheContour& c = lcset.conts[i];
|
||||
unsigned int color = 0;
|
||||
|
||||
color = duIntToCol(i, a);
|
||||
|
||||
for (int j = 0; j < c.nverts; ++j)
|
||||
{
|
||||
const int k = (j+1) % c.nverts;
|
||||
const unsigned char* va = &c.verts[j*4];
|
||||
const unsigned char* vb = &c.verts[k*4];
|
||||
const float ax = orig[0] + va[0]*cs;
|
||||
const float ay = orig[1] + (va[1]+1+(i&1))*ch;
|
||||
const float az = orig[2] + va[2]*cs;
|
||||
const float bx = orig[0] + vb[0]*cs;
|
||||
const float by = orig[1] + (vb[1]+1+(i&1))*ch;
|
||||
const float bz = orig[2] + vb[2]*cs;
|
||||
unsigned int col = color;
|
||||
if ((va[3] & 0xf) != 0xf)
|
||||
{
|
||||
// Portal segment
|
||||
col = duRGBA(255,255,255,128);
|
||||
int d = va[3] & 0xf;
|
||||
|
||||
const float cx = (ax+bx)*0.5f;
|
||||
const float cy = (ay+by)*0.5f;
|
||||
const float cz = (az+bz)*0.5f;
|
||||
|
||||
const float dx = cx + offs[d*2+0]*2*cs;
|
||||
const float dy = cy;
|
||||
const float dz = cz + offs[d*2+1]*2*cs;
|
||||
|
||||
dd->vertex(cx,cy,cz,duRGBA(255,0,0,255));
|
||||
dd->vertex(dx,dy,dz,duRGBA(255,0,0,255));
|
||||
}
|
||||
|
||||
duAppendArrow(dd, ax,ay,az, bx,by,bz, 0.0f, cs*0.5f, col);
|
||||
}
|
||||
}
|
||||
dd->end();
|
||||
|
||||
dd->begin(DU_DRAW_POINTS, 4.0f);
|
||||
|
||||
for (int i = 0; i < lcset.nconts; ++i)
|
||||
{
|
||||
const dtTileCacheContour& c = lcset.conts[i];
|
||||
unsigned int color = 0;
|
||||
|
||||
for (int j = 0; j < c.nverts; ++j)
|
||||
{
|
||||
const unsigned char* va = &c.verts[j*4];
|
||||
|
||||
color = duDarkenCol(duIntToCol(i, a));
|
||||
if (va[3] & 0x80)
|
||||
{
|
||||
// Border vertex
|
||||
color = duRGBA(255,0,0,255);
|
||||
}
|
||||
|
||||
float fx = orig[0] + va[0]*cs;
|
||||
float fy = orig[1] + (va[1]+1+(i&1))*ch;
|
||||
float fz = orig[2] + va[2]*cs;
|
||||
dd->vertex(fx,fy,fz, color);
|
||||
}
|
||||
}
|
||||
dd->end();
|
||||
}
|
||||
|
||||
void duDebugDrawTileCachePolyMesh(duDebugDraw* dd, const struct dtTileCachePolyMesh& lmesh,
|
||||
const float* orig, const float cs, const float ch)
|
||||
{
|
||||
if (!dd) return;
|
||||
|
||||
const int nvp = lmesh.nvp;
|
||||
|
||||
const int offs[2*4] = {-1,0, 0,1, 1,0, 0,-1};
|
||||
|
||||
dd->begin(DU_DRAW_TRIS);
|
||||
|
||||
for (int i = 0; i < lmesh.npolys; ++i)
|
||||
{
|
||||
const unsigned short* p = &lmesh.polys[i*nvp*2];
|
||||
|
||||
unsigned int color;
|
||||
if (lmesh.areas[i] == DT_TILECACHE_WALKABLE_AREA)
|
||||
color = duRGBA(0,192,255,64);
|
||||
else if (lmesh.areas[i] == DT_TILECACHE_NULL_AREA)
|
||||
color = duRGBA(0,0,0,64);
|
||||
else
|
||||
color = duIntToCol(lmesh.areas[i], 255);
|
||||
|
||||
unsigned short vi[3];
|
||||
for (int j = 2; j < nvp; ++j)
|
||||
{
|
||||
if (p[j] == DT_TILECACHE_NULL_IDX) break;
|
||||
vi[0] = p[0];
|
||||
vi[1] = p[j-1];
|
||||
vi[2] = p[j];
|
||||
for (int k = 0; k < 3; ++k)
|
||||
{
|
||||
const unsigned short* v = &lmesh.verts[vi[k]*3];
|
||||
const float x = orig[0] + v[0]*cs;
|
||||
const float y = orig[1] + (v[1]+1)*ch;
|
||||
const float z = orig[2] + v[2]*cs;
|
||||
dd->vertex(x,y,z, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
dd->end();
|
||||
|
||||
// Draw neighbours edges
|
||||
const unsigned int coln = duRGBA(0,48,64,32);
|
||||
dd->begin(DU_DRAW_LINES, 1.5f);
|
||||
for (int i = 0; i < lmesh.npolys; ++i)
|
||||
{
|
||||
const unsigned short* p = &lmesh.polys[i*nvp*2];
|
||||
for (int j = 0; j < nvp; ++j)
|
||||
{
|
||||
if (p[j] == DT_TILECACHE_NULL_IDX) break;
|
||||
if (p[nvp+j] & 0x8000) continue;
|
||||
const int nj = (j+1 >= nvp || p[j+1] == DT_TILECACHE_NULL_IDX) ? 0 : j+1;
|
||||
int vi[2] = {p[j], p[nj]};
|
||||
|
||||
for (int k = 0; k < 2; ++k)
|
||||
{
|
||||
const unsigned short* v = &lmesh.verts[vi[k]*3];
|
||||
const float x = orig[0] + v[0]*cs;
|
||||
const float y = orig[1] + (v[1]+1)*ch + 0.1f;
|
||||
const float z = orig[2] + v[2]*cs;
|
||||
dd->vertex(x, y, z, coln);
|
||||
}
|
||||
}
|
||||
}
|
||||
dd->end();
|
||||
|
||||
// Draw boundary edges
|
||||
const unsigned int colb = duRGBA(0,48,64,220);
|
||||
dd->begin(DU_DRAW_LINES, 2.5f);
|
||||
for (int i = 0; i < lmesh.npolys; ++i)
|
||||
{
|
||||
const unsigned short* p = &lmesh.polys[i*nvp*2];
|
||||
for (int j = 0; j < nvp; ++j)
|
||||
{
|
||||
if (p[j] == DT_TILECACHE_NULL_IDX) break;
|
||||
if ((p[nvp+j] & 0x8000) == 0) continue;
|
||||
const int nj = (j+1 >= nvp || p[j+1] == DT_TILECACHE_NULL_IDX) ? 0 : j+1;
|
||||
int vi[2] = {p[j], p[nj]};
|
||||
|
||||
unsigned int col = colb;
|
||||
if ((p[nvp+j] & 0xf) != 0xf)
|
||||
{
|
||||
const unsigned short* va = &lmesh.verts[vi[0]*3];
|
||||
const unsigned short* vb = &lmesh.verts[vi[1]*3];
|
||||
|
||||
const float ax = orig[0] + va[0]*cs;
|
||||
const float ay = orig[1] + (va[1]+1+(i&1))*ch;
|
||||
const float az = orig[2] + va[2]*cs;
|
||||
const float bx = orig[0] + vb[0]*cs;
|
||||
const float by = orig[1] + (vb[1]+1+(i&1))*ch;
|
||||
const float bz = orig[2] + vb[2]*cs;
|
||||
|
||||
const float cx = (ax+bx)*0.5f;
|
||||
const float cy = (ay+by)*0.5f;
|
||||
const float cz = (az+bz)*0.5f;
|
||||
|
||||
int d = p[nvp+j] & 0xf;
|
||||
|
||||
const float dx = cx + offs[d*2+0]*2*cs;
|
||||
const float dy = cy;
|
||||
const float dz = cz + offs[d*2+1]*2*cs;
|
||||
|
||||
dd->vertex(cx,cy,cz,duRGBA(255,0,0,255));
|
||||
dd->vertex(dx,dy,dz,duRGBA(255,0,0,255));
|
||||
|
||||
col = duRGBA(255,255,255,128);
|
||||
}
|
||||
|
||||
for (int k = 0; k < 2; ++k)
|
||||
{
|
||||
const unsigned short* v = &lmesh.verts[vi[k]*3];
|
||||
const float x = orig[0] + v[0]*cs;
|
||||
const float y = orig[1] + (v[1]+1)*ch + 0.1f;
|
||||
const float z = orig[2] + v[2]*cs;
|
||||
dd->vertex(x, y, z, col);
|
||||
}
|
||||
}
|
||||
}
|
||||
dd->end();
|
||||
|
||||
dd->begin(DU_DRAW_POINTS, 3.0f);
|
||||
const unsigned int colv = duRGBA(0,0,0,220);
|
||||
for (int i = 0; i < lmesh.nverts; ++i)
|
||||
{
|
||||
const unsigned short* v = &lmesh.verts[i*3];
|
||||
const float x = orig[0] + v[0]*cs;
|
||||
const float y = orig[1] + (v[1]+1)*ch + 0.1f;
|
||||
const float z = orig[2] + v[2]*cs;
|
||||
dd->vertex(x,y,z, colv);
|
||||
}
|
||||
dd->end();
|
||||
}
|
||||
|
||||
|
||||
|
||||
1062
recast/debug_utils/src/RecastDebugDraw.cpp
Normal file
1062
recast/debug_utils/src/RecastDebugDraw.cpp
Normal file
File diff suppressed because it is too large
Load Diff
451
recast/debug_utils/src/RecastDump.cpp
Normal file
451
recast/debug_utils/src/RecastDump.cpp
Normal file
@ -0,0 +1,451 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include "Recast.h"
|
||||
#include "RecastAlloc.h"
|
||||
#include "RecastDump.h"
|
||||
|
||||
|
||||
duFileIO::~duFileIO()
|
||||
{
|
||||
// Empty
|
||||
}
|
||||
|
||||
static void ioprintf(duFileIO* io, const char* format, ...)
|
||||
{
|
||||
char line[256];
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
const int n = vsnprintf(line, sizeof(line), format, ap);
|
||||
va_end(ap);
|
||||
if (n > 0)
|
||||
io->write(line, sizeof(char)*n);
|
||||
}
|
||||
|
||||
bool duDumpPolyMeshToObj(rcPolyMesh& pmesh, duFileIO* io)
|
||||
{
|
||||
if (!io)
|
||||
{
|
||||
printf("duDumpPolyMeshToObj: input IO is null.\n");
|
||||
return false;
|
||||
}
|
||||
if (!io->isWriting())
|
||||
{
|
||||
printf("duDumpPolyMeshToObj: input IO not writing.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
const int nvp = pmesh.nvp;
|
||||
const float cs = pmesh.cs;
|
||||
const float ch = pmesh.ch;
|
||||
const float* orig = pmesh.bmin;
|
||||
|
||||
ioprintf(io, "# Recast Navmesh\n");
|
||||
ioprintf(io, "o NavMesh\n");
|
||||
|
||||
ioprintf(io, "\n");
|
||||
|
||||
for (int i = 0; i < pmesh.nverts; ++i)
|
||||
{
|
||||
const unsigned short* v = &pmesh.verts[i*3];
|
||||
const float x = orig[0] + v[0]*cs;
|
||||
const float y = orig[1] + (v[1]+1)*ch + 0.1f;
|
||||
const float z = orig[2] + v[2]*cs;
|
||||
ioprintf(io, "v %f %f %f\n", x,y,z);
|
||||
}
|
||||
|
||||
ioprintf(io, "\n");
|
||||
|
||||
for (int i = 0; i < pmesh.npolys; ++i)
|
||||
{
|
||||
const unsigned short* p = &pmesh.polys[i*nvp*2];
|
||||
for (int j = 2; j < nvp; ++j)
|
||||
{
|
||||
if (p[j] == RC_MESH_NULL_IDX) break;
|
||||
ioprintf(io, "f %d %d %d\n", p[0]+1, p[j-1]+1, p[j]+1);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool duDumpPolyMeshDetailToObj(rcPolyMeshDetail& dmesh, duFileIO* io)
|
||||
{
|
||||
if (!io)
|
||||
{
|
||||
printf("duDumpPolyMeshDetailToObj: input IO is null.\n");
|
||||
return false;
|
||||
}
|
||||
if (!io->isWriting())
|
||||
{
|
||||
printf("duDumpPolyMeshDetailToObj: input IO not writing.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
ioprintf(io, "# Recast Navmesh\n");
|
||||
ioprintf(io, "o NavMesh\n");
|
||||
|
||||
ioprintf(io, "\n");
|
||||
|
||||
for (int i = 0; i < dmesh.nverts; ++i)
|
||||
{
|
||||
const float* v = &dmesh.verts[i*3];
|
||||
ioprintf(io, "v %f %f %f\n", v[0],v[1],v[2]);
|
||||
}
|
||||
|
||||
ioprintf(io, "\n");
|
||||
|
||||
for (int i = 0; i < dmesh.nmeshes; ++i)
|
||||
{
|
||||
const unsigned int* m = &dmesh.meshes[i*4];
|
||||
const unsigned int bverts = m[0];
|
||||
const unsigned int btris = m[2];
|
||||
const unsigned int ntris = m[3];
|
||||
const unsigned char* tris = &dmesh.tris[btris*4];
|
||||
for (unsigned int j = 0; j < ntris; ++j)
|
||||
{
|
||||
ioprintf(io, "f %d %d %d\n",
|
||||
(int)(bverts+tris[j*4+0])+1,
|
||||
(int)(bverts+tris[j*4+1])+1,
|
||||
(int)(bverts+tris[j*4+2])+1);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const int CSET_MAGIC = ('c' << 24) | ('s' << 16) | ('e' << 8) | 't';
|
||||
static const int CSET_VERSION = 2;
|
||||
|
||||
bool duDumpContourSet(struct rcContourSet& cset, duFileIO* io)
|
||||
{
|
||||
if (!io)
|
||||
{
|
||||
printf("duDumpContourSet: input IO is null.\n");
|
||||
return false;
|
||||
}
|
||||
if (!io->isWriting())
|
||||
{
|
||||
printf("duDumpContourSet: input IO not writing.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
io->write(&CSET_MAGIC, sizeof(CSET_MAGIC));
|
||||
io->write(&CSET_VERSION, sizeof(CSET_VERSION));
|
||||
|
||||
io->write(&cset.nconts, sizeof(cset.nconts));
|
||||
|
||||
io->write(cset.bmin, sizeof(cset.bmin));
|
||||
io->write(cset.bmax, sizeof(cset.bmax));
|
||||
|
||||
io->write(&cset.cs, sizeof(cset.cs));
|
||||
io->write(&cset.ch, sizeof(cset.ch));
|
||||
|
||||
io->write(&cset.width, sizeof(cset.width));
|
||||
io->write(&cset.height, sizeof(cset.height));
|
||||
io->write(&cset.borderSize, sizeof(cset.borderSize));
|
||||
|
||||
for (int i = 0; i < cset.nconts; ++i)
|
||||
{
|
||||
const rcContour& cont = cset.conts[i];
|
||||
io->write(&cont.nverts, sizeof(cont.nverts));
|
||||
io->write(&cont.nrverts, sizeof(cont.nrverts));
|
||||
io->write(&cont.reg, sizeof(cont.reg));
|
||||
io->write(&cont.area, sizeof(cont.area));
|
||||
io->write(cont.verts, sizeof(int)*4*cont.nverts);
|
||||
io->write(cont.rverts, sizeof(int)*4*cont.nrverts);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool duReadContourSet(struct rcContourSet& cset, duFileIO* io)
|
||||
{
|
||||
if (!io)
|
||||
{
|
||||
printf("duReadContourSet: input IO is null.\n");
|
||||
return false;
|
||||
}
|
||||
if (!io->isReading())
|
||||
{
|
||||
printf("duReadContourSet: input IO not reading.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
int magic = 0;
|
||||
int version = 0;
|
||||
|
||||
io->read(&magic, sizeof(magic));
|
||||
io->read(&version, sizeof(version));
|
||||
|
||||
if (magic != CSET_MAGIC)
|
||||
{
|
||||
printf("duReadContourSet: Bad voodoo.\n");
|
||||
return false;
|
||||
}
|
||||
if (version != CSET_VERSION)
|
||||
{
|
||||
printf("duReadContourSet: Bad version.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
io->read(&cset.nconts, sizeof(cset.nconts));
|
||||
|
||||
cset.conts = (rcContour*)rcAlloc(sizeof(rcContour)*cset.nconts, RC_ALLOC_PERM);
|
||||
if (!cset.conts)
|
||||
{
|
||||
printf("duReadContourSet: Could not alloc contours (%d)\n", cset.nconts);
|
||||
return false;
|
||||
}
|
||||
memset(cset.conts, 0, sizeof(rcContour)*cset.nconts);
|
||||
|
||||
io->read(cset.bmin, sizeof(cset.bmin));
|
||||
io->read(cset.bmax, sizeof(cset.bmax));
|
||||
|
||||
io->read(&cset.cs, sizeof(cset.cs));
|
||||
io->read(&cset.ch, sizeof(cset.ch));
|
||||
|
||||
io->read(&cset.width, sizeof(cset.width));
|
||||
io->read(&cset.height, sizeof(cset.height));
|
||||
io->read(&cset.borderSize, sizeof(cset.borderSize));
|
||||
|
||||
for (int i = 0; i < cset.nconts; ++i)
|
||||
{
|
||||
rcContour& cont = cset.conts[i];
|
||||
io->read(&cont.nverts, sizeof(cont.nverts));
|
||||
io->read(&cont.nrverts, sizeof(cont.nrverts));
|
||||
io->read(&cont.reg, sizeof(cont.reg));
|
||||
io->read(&cont.area, sizeof(cont.area));
|
||||
|
||||
cont.verts = (int*)rcAlloc(sizeof(int)*4*cont.nverts, RC_ALLOC_PERM);
|
||||
if (!cont.verts)
|
||||
{
|
||||
printf("duReadContourSet: Could not alloc contour verts (%d)\n", cont.nverts);
|
||||
return false;
|
||||
}
|
||||
cont.rverts = (int*)rcAlloc(sizeof(int)*4*cont.nrverts, RC_ALLOC_PERM);
|
||||
if (!cont.rverts)
|
||||
{
|
||||
printf("duReadContourSet: Could not alloc contour rverts (%d)\n", cont.nrverts);
|
||||
return false;
|
||||
}
|
||||
|
||||
io->read(cont.verts, sizeof(int)*4*cont.nverts);
|
||||
io->read(cont.rverts, sizeof(int)*4*cont.nrverts);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static const int CHF_MAGIC = ('r' << 24) | ('c' << 16) | ('h' << 8) | 'f';
|
||||
static const int CHF_VERSION = 3;
|
||||
|
||||
bool duDumpCompactHeightfield(struct rcCompactHeightfield& chf, duFileIO* io)
|
||||
{
|
||||
if (!io)
|
||||
{
|
||||
printf("duDumpCompactHeightfield: input IO is null.\n");
|
||||
return false;
|
||||
}
|
||||
if (!io->isWriting())
|
||||
{
|
||||
printf("duDumpCompactHeightfield: input IO not writing.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
io->write(&CHF_MAGIC, sizeof(CHF_MAGIC));
|
||||
io->write(&CHF_VERSION, sizeof(CHF_VERSION));
|
||||
|
||||
io->write(&chf.width, sizeof(chf.width));
|
||||
io->write(&chf.height, sizeof(chf.height));
|
||||
io->write(&chf.spanCount, sizeof(chf.spanCount));
|
||||
|
||||
io->write(&chf.walkableHeight, sizeof(chf.walkableHeight));
|
||||
io->write(&chf.walkableClimb, sizeof(chf.walkableClimb));
|
||||
io->write(&chf.borderSize, sizeof(chf.borderSize));
|
||||
|
||||
io->write(&chf.maxDistance, sizeof(chf.maxDistance));
|
||||
io->write(&chf.maxRegions, sizeof(chf.maxRegions));
|
||||
|
||||
io->write(chf.bmin, sizeof(chf.bmin));
|
||||
io->write(chf.bmax, sizeof(chf.bmax));
|
||||
|
||||
io->write(&chf.cs, sizeof(chf.cs));
|
||||
io->write(&chf.ch, sizeof(chf.ch));
|
||||
|
||||
int tmp = 0;
|
||||
if (chf.cells) tmp |= 1;
|
||||
if (chf.spans) tmp |= 2;
|
||||
if (chf.dist) tmp |= 4;
|
||||
if (chf.areas) tmp |= 8;
|
||||
|
||||
io->write(&tmp, sizeof(tmp));
|
||||
|
||||
if (chf.cells)
|
||||
io->write(chf.cells, sizeof(rcCompactCell)*chf.width*chf.height);
|
||||
if (chf.spans)
|
||||
io->write(chf.spans, sizeof(rcCompactSpan)*chf.spanCount);
|
||||
if (chf.dist)
|
||||
io->write(chf.dist, sizeof(unsigned short)*chf.spanCount);
|
||||
if (chf.areas)
|
||||
io->write(chf.areas, sizeof(unsigned char)*chf.spanCount);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool duReadCompactHeightfield(struct rcCompactHeightfield& chf, duFileIO* io)
|
||||
{
|
||||
if (!io)
|
||||
{
|
||||
printf("duReadCompactHeightfield: input IO is null.\n");
|
||||
return false;
|
||||
}
|
||||
if (!io->isReading())
|
||||
{
|
||||
printf("duReadCompactHeightfield: input IO not reading.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
int magic = 0;
|
||||
int version = 0;
|
||||
|
||||
io->read(&magic, sizeof(magic));
|
||||
io->read(&version, sizeof(version));
|
||||
|
||||
if (magic != CHF_MAGIC)
|
||||
{
|
||||
printf("duReadCompactHeightfield: Bad voodoo.\n");
|
||||
return false;
|
||||
}
|
||||
if (version != CHF_VERSION)
|
||||
{
|
||||
printf("duReadCompactHeightfield: Bad version.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
io->read(&chf.width, sizeof(chf.width));
|
||||
io->read(&chf.height, sizeof(chf.height));
|
||||
io->read(&chf.spanCount, sizeof(chf.spanCount));
|
||||
|
||||
io->read(&chf.walkableHeight, sizeof(chf.walkableHeight));
|
||||
io->read(&chf.walkableClimb, sizeof(chf.walkableClimb));
|
||||
io->read(&chf.borderSize, sizeof(chf.borderSize));
|
||||
|
||||
io->read(&chf.maxDistance, sizeof(chf.maxDistance));
|
||||
io->read(&chf.maxRegions, sizeof(chf.maxRegions));
|
||||
|
||||
io->read(chf.bmin, sizeof(chf.bmin));
|
||||
io->read(chf.bmax, sizeof(chf.bmax));
|
||||
|
||||
io->read(&chf.cs, sizeof(chf.cs));
|
||||
io->read(&chf.ch, sizeof(chf.ch));
|
||||
|
||||
int tmp = 0;
|
||||
io->read(&tmp, sizeof(tmp));
|
||||
|
||||
if (tmp & 1)
|
||||
{
|
||||
chf.cells = (rcCompactCell*)rcAlloc(sizeof(rcCompactCell)*chf.width*chf.height, RC_ALLOC_PERM);
|
||||
if (!chf.cells)
|
||||
{
|
||||
printf("duReadCompactHeightfield: Could not alloc cells (%d)\n", chf.width*chf.height);
|
||||
return false;
|
||||
}
|
||||
io->read(chf.cells, sizeof(rcCompactCell)*chf.width*chf.height);
|
||||
}
|
||||
if (tmp & 2)
|
||||
{
|
||||
chf.spans = (rcCompactSpan*)rcAlloc(sizeof(rcCompactSpan)*chf.spanCount, RC_ALLOC_PERM);
|
||||
if (!chf.spans)
|
||||
{
|
||||
printf("duReadCompactHeightfield: Could not alloc spans (%d)\n", chf.spanCount);
|
||||
return false;
|
||||
}
|
||||
io->read(chf.spans, sizeof(rcCompactSpan)*chf.spanCount);
|
||||
}
|
||||
if (tmp & 4)
|
||||
{
|
||||
chf.dist = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_PERM);
|
||||
if (!chf.dist)
|
||||
{
|
||||
printf("duReadCompactHeightfield: Could not alloc dist (%d)\n", chf.spanCount);
|
||||
return false;
|
||||
}
|
||||
io->read(chf.dist, sizeof(unsigned short)*chf.spanCount);
|
||||
}
|
||||
if (tmp & 8)
|
||||
{
|
||||
chf.areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_PERM);
|
||||
if (!chf.areas)
|
||||
{
|
||||
printf("duReadCompactHeightfield: Could not alloc areas (%d)\n", chf.spanCount);
|
||||
return false;
|
||||
}
|
||||
io->read(chf.areas, sizeof(unsigned char)*chf.spanCount);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void logLine(rcContext& ctx, rcTimerLabel label, const char* name, const float pc)
|
||||
{
|
||||
const int t = ctx.getAccumulatedTime(label);
|
||||
if (t < 0) return;
|
||||
ctx.log(RC_LOG_PROGRESS, "%s:\t%.2fms\t(%.1f%%)", name, t/1000.0f, t*pc);
|
||||
}
|
||||
|
||||
void duLogBuildTimes(rcContext& ctx, const int totalTimeUsec)
|
||||
{
|
||||
const float pc = 100.0f / totalTimeUsec;
|
||||
|
||||
ctx.log(RC_LOG_PROGRESS, "Build Times");
|
||||
logLine(ctx, RC_TIMER_RASTERIZE_TRIANGLES, "- Rasterize", pc);
|
||||
logLine(ctx, RC_TIMER_BUILD_COMPACTHEIGHTFIELD, "- Build Compact", pc);
|
||||
logLine(ctx, RC_TIMER_FILTER_BORDER, "- Filter Border", pc);
|
||||
logLine(ctx, RC_TIMER_FILTER_WALKABLE, "- Filter Walkable", pc);
|
||||
logLine(ctx, RC_TIMER_ERODE_AREA, "- Erode Area", pc);
|
||||
logLine(ctx, RC_TIMER_MEDIAN_AREA, "- Median Area", pc);
|
||||
logLine(ctx, RC_TIMER_MARK_BOX_AREA, "- Mark Box Area", pc);
|
||||
logLine(ctx, RC_TIMER_MARK_CONVEXPOLY_AREA, "- Mark Convex Area", pc);
|
||||
logLine(ctx, RC_TIMER_MARK_CYLINDER_AREA, "- Mark Cylinder Area", pc);
|
||||
logLine(ctx, RC_TIMER_BUILD_DISTANCEFIELD, "- Build Distance Field", pc);
|
||||
logLine(ctx, RC_TIMER_BUILD_DISTANCEFIELD_DIST, " - Distance", pc);
|
||||
logLine(ctx, RC_TIMER_BUILD_DISTANCEFIELD_BLUR, " - Blur", pc);
|
||||
logLine(ctx, RC_TIMER_BUILD_REGIONS, "- Build Regions", pc);
|
||||
logLine(ctx, RC_TIMER_BUILD_REGIONS_WATERSHED, " - Watershed", pc);
|
||||
logLine(ctx, RC_TIMER_BUILD_REGIONS_EXPAND, " - Expand", pc);
|
||||
logLine(ctx, RC_TIMER_BUILD_REGIONS_FLOOD, " - Find Basins", pc);
|
||||
logLine(ctx, RC_TIMER_BUILD_REGIONS_FILTER, " - Filter", pc);
|
||||
logLine(ctx, RC_TIMER_BUILD_LAYERS, "- Build Layers", pc);
|
||||
logLine(ctx, RC_TIMER_BUILD_CONTOURS, "- Build Contours", pc);
|
||||
logLine(ctx, RC_TIMER_BUILD_CONTOURS_TRACE, " - Trace", pc);
|
||||
logLine(ctx, RC_TIMER_BUILD_CONTOURS_SIMPLIFY, " - Simplify", pc);
|
||||
logLine(ctx, RC_TIMER_BUILD_POLYMESH, "- Build Polymesh", pc);
|
||||
logLine(ctx, RC_TIMER_BUILD_POLYMESHDETAIL, "- Build Polymesh Detail", pc);
|
||||
logLine(ctx, RC_TIMER_MERGE_POLYMESH, "- Merge Polymeshes", pc);
|
||||
logLine(ctx, RC_TIMER_MERGE_POLYMESHDETAIL, "- Merge Polymesh Details", pc);
|
||||
ctx.log(RC_LOG_PROGRESS, "=== TOTAL:\t%.2fms", totalTimeUsec/1000.0f);
|
||||
}
|
||||
|
||||
59
recast/detour/include/DetourAlloc.h
Normal file
59
recast/detour/include/DetourAlloc.h
Normal file
@ -0,0 +1,59 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#ifndef DETOURALLOCATOR_H
|
||||
#define DETOURALLOCATOR_H
|
||||
|
||||
/// Provides hint values to the memory allocator on how long the
|
||||
/// memory is expected to be used.
|
||||
enum dtAllocHint
|
||||
{
|
||||
DT_ALLOC_PERM, ///< Memory persist after a function call.
|
||||
DT_ALLOC_TEMP ///< Memory used temporarily within a function.
|
||||
};
|
||||
|
||||
/// A memory allocation function.
|
||||
// @param[in] size The size, in bytes of memory, to allocate.
|
||||
// @param[in] rcAllocHint A hint to the allocator on how long the memory is expected to be in use.
|
||||
// @return A pointer to the beginning of the allocated memory block, or null if the allocation failed.
|
||||
/// @see dtAllocSetCustom
|
||||
typedef void* (dtAllocFunc)(int size, dtAllocHint hint);
|
||||
|
||||
/// A memory deallocation function.
|
||||
/// @param[in] ptr A pointer to a memory block previously allocated using #dtAllocFunc.
|
||||
/// @see dtAllocSetCustom
|
||||
typedef void (dtFreeFunc)(void* ptr);
|
||||
|
||||
/// Sets the base custom allocation functions to be used by Detour.
|
||||
/// @param[in] allocFunc The memory allocation function to be used by #dtAlloc
|
||||
/// @param[in] freeFunc The memory de-allocation function to be used by #dtFree
|
||||
void dtAllocSetCustom(dtAllocFunc *allocFunc, dtFreeFunc *freeFunc);
|
||||
|
||||
/// Allocates a memory block.
|
||||
/// @param[in] size The size, in bytes of memory, to allocate.
|
||||
/// @param[in] hint A hint to the allocator on how long the memory is expected to be in use.
|
||||
/// @return A pointer to the beginning of the allocated memory block, or null if the allocation failed.
|
||||
/// @see dtFree
|
||||
void* dtAlloc(int size, dtAllocHint hint);
|
||||
|
||||
/// Deallocates a memory block.
|
||||
/// @param[in] ptr A pointer to a memory block previously allocated using #dtAlloc.
|
||||
/// @see dtAlloc
|
||||
void dtFree(void* ptr);
|
||||
|
||||
#endif
|
||||
33
recast/detour/include/DetourAssert.h
Normal file
33
recast/detour/include/DetourAssert.h
Normal file
@ -0,0 +1,33 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#ifndef DETOURASSERT_H
|
||||
#define DETOURASSERT_H
|
||||
|
||||
// Note: This header file's only purpose is to include define assert.
|
||||
// Feel free to change the file and include your own implementation instead.
|
||||
|
||||
#ifdef NDEBUG
|
||||
// From http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/
|
||||
# define dtAssert(x) do { (void)sizeof(x); } while((void)(__LINE__==-1),false)
|
||||
#else
|
||||
# include <assert.h>
|
||||
# define dtAssert assert
|
||||
#endif
|
||||
|
||||
#endif // DETOURASSERT_H
|
||||
532
recast/detour/include/DetourCommon.h
Normal file
532
recast/detour/include/DetourCommon.h
Normal file
@ -0,0 +1,532 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#ifndef DETOURCOMMON_H
|
||||
#define DETOURCOMMON_H
|
||||
|
||||
#include "DetourMath.h"
|
||||
|
||||
/**
|
||||
@defgroup detour Detour
|
||||
|
||||
Members in this module are used to create, manipulate, and query navigation
|
||||
meshes.
|
||||
|
||||
@note This is a summary list of members. Use the index or search
|
||||
feature to find minor members.
|
||||
*/
|
||||
|
||||
/// @name General helper functions
|
||||
/// @{
|
||||
|
||||
/// Used to ignore a function parameter. VS complains about unused parameters
|
||||
/// and this silences the warning.
|
||||
/// @param [in] _ Unused parameter
|
||||
template<class T> void dtIgnoreUnused(const T&) { }
|
||||
|
||||
/// Swaps the values of the two parameters.
|
||||
/// @param[in,out] a Value A
|
||||
/// @param[in,out] b Value B
|
||||
template<class T> inline void dtSwap(T& a, T& b) { T t = a; a = b; b = t; }
|
||||
|
||||
/// Returns the minimum of two values.
|
||||
/// @param[in] a Value A
|
||||
/// @param[in] b Value B
|
||||
/// @return The minimum of the two values.
|
||||
template<class T> inline T dtMin(T a, T b) { return a < b ? a : b; }
|
||||
|
||||
/// Returns the maximum of two values.
|
||||
/// @param[in] a Value A
|
||||
/// @param[in] b Value B
|
||||
/// @return The maximum of the two values.
|
||||
template<class T> inline T dtMax(T a, T b) { return a > b ? a : b; }
|
||||
|
||||
/// Returns the absolute value.
|
||||
/// @param[in] a The value.
|
||||
/// @return The absolute value of the specified value.
|
||||
template<class T> inline T dtAbs(T a) { return a < 0 ? -a : a; }
|
||||
|
||||
/// Returns the square of the value.
|
||||
/// @param[in] a The value.
|
||||
/// @return The square of the value.
|
||||
template<class T> inline T dtSqr(T a) { return a*a; }
|
||||
|
||||
/// Clamps the value to the specified range.
|
||||
/// @param[in] v The value to clamp.
|
||||
/// @param[in] mn The minimum permitted return value.
|
||||
/// @param[in] mx The maximum permitted return value.
|
||||
/// @return The value, clamped to the specified range.
|
||||
template<class T> inline T dtClamp(T v, T mn, T mx) { return v < mn ? mn : (v > mx ? mx : v); }
|
||||
|
||||
/// @}
|
||||
/// @name Vector helper functions.
|
||||
/// @{
|
||||
|
||||
/// Derives the cross product of two vectors. (@p v1 x @p v2)
|
||||
/// @param[out] dest The cross product. [(x, y, z)]
|
||||
/// @param[in] v1 A Vector [(x, y, z)]
|
||||
/// @param[in] v2 A vector [(x, y, z)]
|
||||
inline void dtVcross(float* dest, const float* v1, const float* v2)
|
||||
{
|
||||
dest[0] = v1[1]*v2[2] - v1[2]*v2[1];
|
||||
dest[1] = v1[2]*v2[0] - v1[0]*v2[2];
|
||||
dest[2] = v1[0]*v2[1] - v1[1]*v2[0];
|
||||
}
|
||||
|
||||
/// Derives the dot product of two vectors. (@p v1 . @p v2)
|
||||
/// @param[in] v1 A Vector [(x, y, z)]
|
||||
/// @param[in] v2 A vector [(x, y, z)]
|
||||
/// @return The dot product.
|
||||
inline float dtVdot(const float* v1, const float* v2)
|
||||
{
|
||||
return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
|
||||
}
|
||||
|
||||
/// Performs a scaled vector addition. (@p v1 + (@p v2 * @p s))
|
||||
/// @param[out] dest The result vector. [(x, y, z)]
|
||||
/// @param[in] v1 The base vector. [(x, y, z)]
|
||||
/// @param[in] v2 The vector to scale and add to @p v1. [(x, y, z)]
|
||||
/// @param[in] s The amount to scale @p v2 by before adding to @p v1.
|
||||
inline void dtVmad(float* dest, const float* v1, const float* v2, const float s)
|
||||
{
|
||||
dest[0] = v1[0]+v2[0]*s;
|
||||
dest[1] = v1[1]+v2[1]*s;
|
||||
dest[2] = v1[2]+v2[2]*s;
|
||||
}
|
||||
|
||||
/// Performs a linear interpolation between two vectors. (@p v1 toward @p v2)
|
||||
/// @param[out] dest The result vector. [(x, y, x)]
|
||||
/// @param[in] v1 The starting vector.
|
||||
/// @param[in] v2 The destination vector.
|
||||
/// @param[in] t The interpolation factor. [Limits: 0 <= value <= 1.0]
|
||||
inline void dtVlerp(float* dest, const float* v1, const float* v2, const float t)
|
||||
{
|
||||
dest[0] = v1[0]+(v2[0]-v1[0])*t;
|
||||
dest[1] = v1[1]+(v2[1]-v1[1])*t;
|
||||
dest[2] = v1[2]+(v2[2]-v1[2])*t;
|
||||
}
|
||||
|
||||
/// Performs a vector addition. (@p v1 + @p v2)
|
||||
/// @param[out] dest The result vector. [(x, y, z)]
|
||||
/// @param[in] v1 The base vector. [(x, y, z)]
|
||||
/// @param[in] v2 The vector to add to @p v1. [(x, y, z)]
|
||||
inline void dtVadd(float* dest, const float* v1, const float* v2)
|
||||
{
|
||||
dest[0] = v1[0]+v2[0];
|
||||
dest[1] = v1[1]+v2[1];
|
||||
dest[2] = v1[2]+v2[2];
|
||||
}
|
||||
|
||||
/// Performs a vector subtraction. (@p v1 - @p v2)
|
||||
/// @param[out] dest The result vector. [(x, y, z)]
|
||||
/// @param[in] v1 The base vector. [(x, y, z)]
|
||||
/// @param[in] v2 The vector to subtract from @p v1. [(x, y, z)]
|
||||
inline void dtVsub(float* dest, const float* v1, const float* v2)
|
||||
{
|
||||
dest[0] = v1[0]-v2[0];
|
||||
dest[1] = v1[1]-v2[1];
|
||||
dest[2] = v1[2]-v2[2];
|
||||
}
|
||||
|
||||
/// Scales the vector by the specified value. (@p v * @p t)
|
||||
/// @param[out] dest The result vector. [(x, y, z)]
|
||||
/// @param[in] v The vector to scale. [(x, y, z)]
|
||||
/// @param[in] t The scaling factor.
|
||||
inline void dtVscale(float* dest, const float* v, const float t)
|
||||
{
|
||||
dest[0] = v[0]*t;
|
||||
dest[1] = v[1]*t;
|
||||
dest[2] = v[2]*t;
|
||||
}
|
||||
|
||||
/// Selects the minimum value of each element from the specified vectors.
|
||||
/// @param[in,out] mn A vector. (Will be updated with the result.) [(x, y, z)]
|
||||
/// @param[in] v A vector. [(x, y, z)]
|
||||
inline void dtVmin(float* mn, const float* v)
|
||||
{
|
||||
mn[0] = dtMin(mn[0], v[0]);
|
||||
mn[1] = dtMin(mn[1], v[1]);
|
||||
mn[2] = dtMin(mn[2], v[2]);
|
||||
}
|
||||
|
||||
/// Selects the maximum value of each element from the specified vectors.
|
||||
/// @param[in,out] mx A vector. (Will be updated with the result.) [(x, y, z)]
|
||||
/// @param[in] v A vector. [(x, y, z)]
|
||||
inline void dtVmax(float* mx, const float* v)
|
||||
{
|
||||
mx[0] = dtMax(mx[0], v[0]);
|
||||
mx[1] = dtMax(mx[1], v[1]);
|
||||
mx[2] = dtMax(mx[2], v[2]);
|
||||
}
|
||||
|
||||
/// Sets the vector elements to the specified values.
|
||||
/// @param[out] dest The result vector. [(x, y, z)]
|
||||
/// @param[in] x The x-value of the vector.
|
||||
/// @param[in] y The y-value of the vector.
|
||||
/// @param[in] z The z-value of the vector.
|
||||
inline void dtVset(float* dest, const float x, const float y, const float z)
|
||||
{
|
||||
dest[0] = x; dest[1] = y; dest[2] = z;
|
||||
}
|
||||
|
||||
/// Performs a vector copy.
|
||||
/// @param[out] dest The result. [(x, y, z)]
|
||||
/// @param[in] a The vector to copy. [(x, y, z)]
|
||||
inline void dtVcopy(float* dest, const float* a)
|
||||
{
|
||||
dest[0] = a[0];
|
||||
dest[1] = a[1];
|
||||
dest[2] = a[2];
|
||||
}
|
||||
|
||||
/// Derives the scalar length of the vector.
|
||||
/// @param[in] v The vector. [(x, y, z)]
|
||||
/// @return The scalar length of the vector.
|
||||
inline float dtVlen(const float* v)
|
||||
{
|
||||
return dtMathSqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
|
||||
}
|
||||
|
||||
/// Derives the square of the scalar length of the vector. (len * len)
|
||||
/// @param[in] v The vector. [(x, y, z)]
|
||||
/// @return The square of the scalar length of the vector.
|
||||
inline float dtVlenSqr(const float* v)
|
||||
{
|
||||
return v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
|
||||
}
|
||||
|
||||
/// Returns the distance between two points.
|
||||
/// @param[in] v1 A point. [(x, y, z)]
|
||||
/// @param[in] v2 A point. [(x, y, z)]
|
||||
/// @return The distance between the two points.
|
||||
inline float dtVdist(const float* v1, const float* v2)
|
||||
{
|
||||
const float dx = v2[0] - v1[0];
|
||||
const float dy = v2[1] - v1[1];
|
||||
const float dz = v2[2] - v1[2];
|
||||
return dtMathSqrtf(dx*dx + dy*dy + dz*dz);
|
||||
}
|
||||
|
||||
/// Returns the square of the distance between two points.
|
||||
/// @param[in] v1 A point. [(x, y, z)]
|
||||
/// @param[in] v2 A point. [(x, y, z)]
|
||||
/// @return The square of the distance between the two points.
|
||||
inline float dtVdistSqr(const float* v1, const float* v2)
|
||||
{
|
||||
const float dx = v2[0] - v1[0];
|
||||
const float dy = v2[1] - v1[1];
|
||||
const float dz = v2[2] - v1[2];
|
||||
return dx*dx + dy*dy + dz*dz;
|
||||
}
|
||||
|
||||
/// Derives the distance between the specified points on the xz-plane.
|
||||
/// @param[in] v1 A point. [(x, y, z)]
|
||||
/// @param[in] v2 A point. [(x, y, z)]
|
||||
/// @return The distance between the point on the xz-plane.
|
||||
///
|
||||
/// The vectors are projected onto the xz-plane, so the y-values are ignored.
|
||||
inline float dtVdist2D(const float* v1, const float* v2)
|
||||
{
|
||||
const float dx = v2[0] - v1[0];
|
||||
const float dz = v2[2] - v1[2];
|
||||
return dtMathSqrtf(dx*dx + dz*dz);
|
||||
}
|
||||
|
||||
/// Derives the square of the distance between the specified points on the xz-plane.
|
||||
/// @param[in] v1 A point. [(x, y, z)]
|
||||
/// @param[in] v2 A point. [(x, y, z)]
|
||||
/// @return The square of the distance between the point on the xz-plane.
|
||||
inline float dtVdist2DSqr(const float* v1, const float* v2)
|
||||
{
|
||||
const float dx = v2[0] - v1[0];
|
||||
const float dz = v2[2] - v1[2];
|
||||
return dx*dx + dz*dz;
|
||||
}
|
||||
|
||||
/// Normalizes the vector.
|
||||
/// @param[in,out] v The vector to normalize. [(x, y, z)]
|
||||
inline void dtVnormalize(float* v)
|
||||
{
|
||||
float d = 1.0f / dtMathSqrtf(dtSqr(v[0]) + dtSqr(v[1]) + dtSqr(v[2]));
|
||||
v[0] *= d;
|
||||
v[1] *= d;
|
||||
v[2] *= d;
|
||||
}
|
||||
|
||||
/// Performs a 'sloppy' colocation check of the specified points.
|
||||
/// @param[in] p0 A point. [(x, y, z)]
|
||||
/// @param[in] p1 A point. [(x, y, z)]
|
||||
/// @return True if the points are considered to be at the same location.
|
||||
///
|
||||
/// Basically, this function will return true if the specified points are
|
||||
/// close enough to eachother to be considered colocated.
|
||||
inline bool dtVequal(const float* p0, const float* p1)
|
||||
{
|
||||
static const float thr = dtSqr(1.0f/16384.0f);
|
||||
const float d = dtVdistSqr(p0, p1);
|
||||
return d < thr;
|
||||
}
|
||||
|
||||
/// Derives the dot product of two vectors on the xz-plane. (@p u . @p v)
|
||||
/// @param[in] u A vector [(x, y, z)]
|
||||
/// @param[in] v A vector [(x, y, z)]
|
||||
/// @return The dot product on the xz-plane.
|
||||
///
|
||||
/// The vectors are projected onto the xz-plane, so the y-values are ignored.
|
||||
inline float dtVdot2D(const float* u, const float* v)
|
||||
{
|
||||
return u[0]*v[0] + u[2]*v[2];
|
||||
}
|
||||
|
||||
/// Derives the xz-plane 2D perp product of the two vectors. (uz*vx - ux*vz)
|
||||
/// @param[in] u The LHV vector [(x, y, z)]
|
||||
/// @param[in] v The RHV vector [(x, y, z)]
|
||||
/// @return The dot product on the xz-plane.
|
||||
///
|
||||
/// The vectors are projected onto the xz-plane, so the y-values are ignored.
|
||||
inline float dtVperp2D(const float* u, const float* v)
|
||||
{
|
||||
return u[2]*v[0] - u[0]*v[2];
|
||||
}
|
||||
|
||||
/// @}
|
||||
/// @name Computational geometry helper functions.
|
||||
/// @{
|
||||
|
||||
/// Derives the signed xz-plane area of the triangle ABC, or the relationship of line AB to point C.
|
||||
/// @param[in] a Vertex A. [(x, y, z)]
|
||||
/// @param[in] b Vertex B. [(x, y, z)]
|
||||
/// @param[in] c Vertex C. [(x, y, z)]
|
||||
/// @return The signed xz-plane area of the triangle.
|
||||
inline float dtTriArea2D(const float* a, const float* b, const float* c)
|
||||
{
|
||||
const float abx = b[0] - a[0];
|
||||
const float abz = b[2] - a[2];
|
||||
const float acx = c[0] - a[0];
|
||||
const float acz = c[2] - a[2];
|
||||
return acx*abz - abx*acz;
|
||||
}
|
||||
|
||||
/// Determines if two axis-aligned bounding boxes overlap.
|
||||
/// @param[in] amin Minimum bounds of box A. [(x, y, z)]
|
||||
/// @param[in] amax Maximum bounds of box A. [(x, y, z)]
|
||||
/// @param[in] bmin Minimum bounds of box B. [(x, y, z)]
|
||||
/// @param[in] bmax Maximum bounds of box B. [(x, y, z)]
|
||||
/// @return True if the two AABB's overlap.
|
||||
/// @see dtOverlapBounds
|
||||
inline bool dtOverlapQuantBounds(const unsigned short amin[3], const unsigned short amax[3],
|
||||
const unsigned short bmin[3], const unsigned short bmax[3])
|
||||
{
|
||||
bool overlap = true;
|
||||
overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap;
|
||||
overlap = (amin[1] > bmax[1] || amax[1] < bmin[1]) ? false : overlap;
|
||||
overlap = (amin[2] > bmax[2] || amax[2] < bmin[2]) ? false : overlap;
|
||||
return overlap;
|
||||
}
|
||||
|
||||
/// Determines if two axis-aligned bounding boxes overlap.
|
||||
/// @param[in] amin Minimum bounds of box A. [(x, y, z)]
|
||||
/// @param[in] amax Maximum bounds of box A. [(x, y, z)]
|
||||
/// @param[in] bmin Minimum bounds of box B. [(x, y, z)]
|
||||
/// @param[in] bmax Maximum bounds of box B. [(x, y, z)]
|
||||
/// @return True if the two AABB's overlap.
|
||||
/// @see dtOverlapQuantBounds
|
||||
inline bool dtOverlapBounds(const float* amin, const float* amax,
|
||||
const float* bmin, const float* bmax)
|
||||
{
|
||||
bool overlap = true;
|
||||
overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap;
|
||||
overlap = (amin[1] > bmax[1] || amax[1] < bmin[1]) ? false : overlap;
|
||||
overlap = (amin[2] > bmax[2] || amax[2] < bmin[2]) ? false : overlap;
|
||||
return overlap;
|
||||
}
|
||||
|
||||
/// Derives the closest point on a triangle from the specified reference point.
|
||||
/// @param[out] closest The closest point on the triangle.
|
||||
/// @param[in] p The reference point from which to test. [(x, y, z)]
|
||||
/// @param[in] a Vertex A of triangle ABC. [(x, y, z)]
|
||||
/// @param[in] b Vertex B of triangle ABC. [(x, y, z)]
|
||||
/// @param[in] c Vertex C of triangle ABC. [(x, y, z)]
|
||||
void dtClosestPtPointTriangle(float* closest, const float* p,
|
||||
const float* a, const float* b, const float* c);
|
||||
|
||||
/// Derives the y-axis height of the closest point on the triangle from the specified reference point.
|
||||
/// @param[in] p The reference point from which to test. [(x, y, z)]
|
||||
/// @param[in] a Vertex A of triangle ABC. [(x, y, z)]
|
||||
/// @param[in] b Vertex B of triangle ABC. [(x, y, z)]
|
||||
/// @param[in] c Vertex C of triangle ABC. [(x, y, z)]
|
||||
/// @param[out] h The resulting height.
|
||||
bool dtClosestHeightPointTriangle(const float* p, const float* a, const float* b, const float* c, float& h);
|
||||
|
||||
bool dtIntersectSegmentPoly2D(const float* p0, const float* p1,
|
||||
const float* verts, int nverts,
|
||||
float& tmin, float& tmax,
|
||||
int& segMin, int& segMax);
|
||||
|
||||
bool dtIntersectSegSeg2D(const float* ap, const float* aq,
|
||||
const float* bp, const float* bq,
|
||||
float& s, float& t);
|
||||
|
||||
/// Determines if the specified point is inside the convex polygon on the xz-plane.
|
||||
/// @param[in] pt The point to check. [(x, y, z)]
|
||||
/// @param[in] verts The polygon vertices. [(x, y, z) * @p nverts]
|
||||
/// @param[in] nverts The number of vertices. [Limit: >= 3]
|
||||
/// @return True if the point is inside the polygon.
|
||||
bool dtPointInPolygon(const float* pt, const float* verts, const int nverts);
|
||||
|
||||
bool dtDistancePtPolyEdgesSqr(const float* pt, const float* verts, const int nverts,
|
||||
float* ed, float* et);
|
||||
|
||||
float dtDistancePtSegSqr2D(const float* pt, const float* p, const float* q, float& t);
|
||||
|
||||
/// Derives the centroid of a convex polygon.
|
||||
/// @param[out] tc The centroid of the polgyon. [(x, y, z)]
|
||||
/// @param[in] idx The polygon indices. [(vertIndex) * @p nidx]
|
||||
/// @param[in] nidx The number of indices in the polygon. [Limit: >= 3]
|
||||
/// @param[in] verts The polygon vertices. [(x, y, z) * vertCount]
|
||||
void dtCalcPolyCenter(float* tc, const unsigned short* idx, int nidx, const float* verts);
|
||||
|
||||
/// Determines if the two convex polygons overlap on the xz-plane.
|
||||
/// @param[in] polya Polygon A vertices. [(x, y, z) * @p npolya]
|
||||
/// @param[in] npolya The number of vertices in polygon A.
|
||||
/// @param[in] polyb Polygon B vertices. [(x, y, z) * @p npolyb]
|
||||
/// @param[in] npolyb The number of vertices in polygon B.
|
||||
/// @return True if the two polygons overlap.
|
||||
bool dtOverlapPolyPoly2D(const float* polya, const int npolya,
|
||||
const float* polyb, const int npolyb);
|
||||
|
||||
/// @}
|
||||
/// @name Miscellanious functions.
|
||||
/// @{
|
||||
|
||||
inline unsigned int dtNextPow2(unsigned int v)
|
||||
{
|
||||
v--;
|
||||
v |= v >> 1;
|
||||
v |= v >> 2;
|
||||
v |= v >> 4;
|
||||
v |= v >> 8;
|
||||
v |= v >> 16;
|
||||
v++;
|
||||
return v;
|
||||
}
|
||||
|
||||
inline unsigned int dtIlog2(unsigned int v)
|
||||
{
|
||||
unsigned int r;
|
||||
unsigned int shift;
|
||||
r = (v > 0xffff) << 4; v >>= r;
|
||||
shift = (v > 0xff) << 3; v >>= shift; r |= shift;
|
||||
shift = (v > 0xf) << 2; v >>= shift; r |= shift;
|
||||
shift = (v > 0x3) << 1; v >>= shift; r |= shift;
|
||||
r |= (v >> 1);
|
||||
return r;
|
||||
}
|
||||
|
||||
inline int dtAlign4(int x) { return (x+3) & ~3; }
|
||||
|
||||
inline int dtOppositeTile(int side) { return (side+4) & 0x7; }
|
||||
|
||||
inline void dtSwapByte(unsigned char* a, unsigned char* b)
|
||||
{
|
||||
unsigned char tmp = *a;
|
||||
*a = *b;
|
||||
*b = tmp;
|
||||
}
|
||||
|
||||
inline void dtSwapEndian(unsigned short* v)
|
||||
{
|
||||
unsigned char* x = (unsigned char*)v;
|
||||
dtSwapByte(x+0, x+1);
|
||||
}
|
||||
|
||||
inline void dtSwapEndian(short* v)
|
||||
{
|
||||
unsigned char* x = (unsigned char*)v;
|
||||
dtSwapByte(x+0, x+1);
|
||||
}
|
||||
|
||||
inline void dtSwapEndian(unsigned int* v)
|
||||
{
|
||||
unsigned char* x = (unsigned char*)v;
|
||||
dtSwapByte(x+0, x+3); dtSwapByte(x+1, x+2);
|
||||
}
|
||||
|
||||
inline void dtSwapEndian(int* v)
|
||||
{
|
||||
unsigned char* x = (unsigned char*)v;
|
||||
dtSwapByte(x+0, x+3); dtSwapByte(x+1, x+2);
|
||||
}
|
||||
|
||||
inline void dtSwapEndian(float* v)
|
||||
{
|
||||
unsigned char* x = (unsigned char*)v;
|
||||
dtSwapByte(x+0, x+3); dtSwapByte(x+1, x+2);
|
||||
}
|
||||
|
||||
void dtRandomPointInConvexPoly(const float* pts, const int npts, float* areas,
|
||||
const float s, const float t, float* out);
|
||||
|
||||
/// @}
|
||||
|
||||
#endif // DETOURCOMMON_H
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This section contains detailed documentation for members that don't have
|
||||
// a source file. It reduces clutter in the main section of the header.
|
||||
|
||||
/**
|
||||
|
||||
@fn float dtTriArea2D(const float* a, const float* b, const float* c)
|
||||
@par
|
||||
|
||||
The vertices are projected onto the xz-plane, so the y-values are ignored.
|
||||
|
||||
This is a low cost function than can be used for various purposes. Its main purpose
|
||||
is for point/line relationship testing.
|
||||
|
||||
In all cases: A value of zero indicates that all vertices are collinear or represent the same point.
|
||||
(On the xz-plane.)
|
||||
|
||||
When used for point/line relationship tests, AB usually represents a line against which
|
||||
the C point is to be tested. In this case:
|
||||
|
||||
A positive value indicates that point C is to the left of line AB, looking from A toward B.<br/>
|
||||
A negative value indicates that point C is to the right of lineAB, looking from A toward B.
|
||||
|
||||
When used for evaluating a triangle:
|
||||
|
||||
The absolute value of the return value is two times the area of the triangle when it is
|
||||
projected onto the xz-plane.
|
||||
|
||||
A positive return value indicates:
|
||||
|
||||
<ul>
|
||||
<li>The vertices are wrapped in the normal Detour wrap direction.</li>
|
||||
<li>The triangle's 3D face normal is in the general up direction.</li>
|
||||
</ul>
|
||||
|
||||
A negative return value indicates:
|
||||
|
||||
<ul>
|
||||
<li>The vertices are reverse wrapped. (Wrapped opposite the normal Detour wrap direction.)</li>
|
||||
<li>The triangle's 3D face normal is in the general down direction.</li>
|
||||
</ul>
|
||||
|
||||
*/
|
||||
20
recast/detour/include/DetourMath.h
Normal file
20
recast/detour/include/DetourMath.h
Normal file
@ -0,0 +1,20 @@
|
||||
/**
|
||||
@defgroup detour Detour
|
||||
|
||||
Members in this module are wrappers around the standard math library
|
||||
*/
|
||||
|
||||
#ifndef DETOURMATH_H
|
||||
#define DETOURMATH_H
|
||||
|
||||
#include <math.h>
|
||||
|
||||
inline float dtMathFabsf(float x) { return fabsf(x); }
|
||||
inline float dtMathSqrtf(float x) { return sqrtf(x); }
|
||||
inline float dtMathFloorf(float x) { return floorf(x); }
|
||||
inline float dtMathCeilf(float x) { return ceilf(x); }
|
||||
inline float dtMathCosf(float x) { return cosf(x); }
|
||||
inline float dtMathSinf(float x) { return sinf(x); }
|
||||
inline float dtMathAtan2f(float y, float x) { return atan2f(y, x); }
|
||||
|
||||
#endif
|
||||
760
recast/detour/include/DetourNavMesh.h
Normal file
760
recast/detour/include/DetourNavMesh.h
Normal file
@ -0,0 +1,760 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#ifndef DETOURNAVMESH_H
|
||||
#define DETOURNAVMESH_H
|
||||
|
||||
#include "DetourAlloc.h"
|
||||
#include "DetourStatus.h"
|
||||
|
||||
// Undefine (or define in a build cofnig) the following line to use 64bit polyref.
|
||||
// Generally not needed, useful for very large worlds.
|
||||
// Note: tiles build using 32bit refs are not compatible with 64bit refs!
|
||||
//#define DT_POLYREF64 1
|
||||
|
||||
#ifdef DT_POLYREF64
|
||||
// TODO: figure out a multiplatform version of uint64_t
|
||||
// - maybe: https://code.google.com/p/msinttypes/
|
||||
// - or: http://www.azillionmonkeys.com/qed/pstdint.h
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
// Note: If you want to use 64-bit refs, change the types of both dtPolyRef & dtTileRef.
|
||||
// It is also recommended that you change dtHashRef() to a proper 64-bit hash.
|
||||
|
||||
/// A handle to a polygon within a navigation mesh tile.
|
||||
/// @ingroup detour
|
||||
#ifdef DT_POLYREF64
|
||||
static const unsigned int DT_SALT_BITS = 16;
|
||||
static const unsigned int DT_TILE_BITS = 28;
|
||||
static const unsigned int DT_POLY_BITS = 20;
|
||||
typedef uint64_t dtPolyRef;
|
||||
#else
|
||||
typedef unsigned int dtPolyRef;
|
||||
#endif
|
||||
|
||||
/// A handle to a tile within a navigation mesh.
|
||||
/// @ingroup detour
|
||||
#ifdef DT_POLYREF64
|
||||
typedef uint64_t dtTileRef;
|
||||
#else
|
||||
typedef unsigned int dtTileRef;
|
||||
#endif
|
||||
|
||||
/// The maximum number of vertices per navigation polygon.
|
||||
/// @ingroup detour
|
||||
static const int DT_VERTS_PER_POLYGON = 6;
|
||||
|
||||
/// @{
|
||||
/// @name Tile Serialization Constants
|
||||
/// These constants are used to detect whether a navigation tile's data
|
||||
/// and state format is compatible with the current build.
|
||||
///
|
||||
|
||||
/// A magic number used to detect compatibility of navigation tile data.
|
||||
static const int DT_NAVMESH_MAGIC = 'D'<<24 | 'N'<<16 | 'A'<<8 | 'V';
|
||||
|
||||
/// A version number used to detect compatibility of navigation tile data.
|
||||
static const int DT_NAVMESH_VERSION = 7;
|
||||
|
||||
/// A magic number used to detect the compatibility of navigation tile states.
|
||||
static const int DT_NAVMESH_STATE_MAGIC = 'D'<<24 | 'N'<<16 | 'M'<<8 | 'S';
|
||||
|
||||
/// A version number used to detect compatibility of navigation tile states.
|
||||
static const int DT_NAVMESH_STATE_VERSION = 1;
|
||||
|
||||
/// @}
|
||||
|
||||
/// A flag that indicates that an entity links to an external entity.
|
||||
/// (E.g. A polygon edge is a portal that links to another polygon.)
|
||||
static const unsigned short DT_EXT_LINK = 0x8000;
|
||||
|
||||
/// A value that indicates the entity does not link to anything.
|
||||
static const unsigned int DT_NULL_LINK = 0xffffffff;
|
||||
|
||||
/// A flag that indicates that an off-mesh connection can be traversed in both directions. (Is bidirectional.)
|
||||
static const unsigned int DT_OFFMESH_CON_BIDIR = 1;
|
||||
|
||||
/// The maximum number of user defined area ids.
|
||||
/// @ingroup detour
|
||||
static const int DT_MAX_AREAS = 64;
|
||||
|
||||
/// Tile flags used for various functions and fields.
|
||||
/// For an example, see dtNavMesh::addTile().
|
||||
enum dtTileFlags
|
||||
{
|
||||
/// The navigation mesh owns the tile memory and is responsible for freeing it.
|
||||
DT_TILE_FREE_DATA = 0x01,
|
||||
};
|
||||
|
||||
/// Vertex flags returned by dtNavMeshQuery::findStraightPath.
|
||||
enum dtStraightPathFlags
|
||||
{
|
||||
DT_STRAIGHTPATH_START = 0x01, ///< The vertex is the start position in the path.
|
||||
DT_STRAIGHTPATH_END = 0x02, ///< The vertex is the end position in the path.
|
||||
DT_STRAIGHTPATH_OFFMESH_CONNECTION = 0x04, ///< The vertex is the start of an off-mesh connection.
|
||||
};
|
||||
|
||||
/// Options for dtNavMeshQuery::findStraightPath.
|
||||
enum dtStraightPathOptions
|
||||
{
|
||||
DT_STRAIGHTPATH_AREA_CROSSINGS = 0x01, ///< Add a vertex at every polygon edge crossing where area changes.
|
||||
DT_STRAIGHTPATH_ALL_CROSSINGS = 0x02, ///< Add a vertex at every polygon edge crossing.
|
||||
};
|
||||
|
||||
|
||||
/// Options for dtNavMeshQuery::findPath
|
||||
enum dtFindPathOptions
|
||||
{
|
||||
DT_FINDPATH_LOW_QUALITY_FAR = 0x01, ///< [provisional] trade quality for performance far from the origin. The idea is that by then a new query will be issued
|
||||
DT_FINDPATH_ANY_ANGLE = 0x02, ///< use raycasts during pathfind to "shortcut" (raycast still consider costs)
|
||||
};
|
||||
|
||||
/// Options for dtNavMeshQuery::raycast
|
||||
enum dtRaycastOptions
|
||||
{
|
||||
DT_RAYCAST_USE_COSTS = 0x01, ///< Raycast should calculate movement cost along the ray and fill RaycastHit::cost
|
||||
};
|
||||
|
||||
|
||||
/// Limit raycasting during any angle pahfinding
|
||||
/// The limit is given as a multiple of the character radius
|
||||
static const float DT_RAY_CAST_LIMIT_PROPORTIONS = 50.0f;
|
||||
|
||||
/// Flags representing the type of a navigation mesh polygon.
|
||||
enum dtPolyTypes
|
||||
{
|
||||
/// The polygon is a standard convex polygon that is part of the surface of the mesh.
|
||||
DT_POLYTYPE_GROUND = 0,
|
||||
/// The polygon is an off-mesh connection consisting of two vertices.
|
||||
DT_POLYTYPE_OFFMESH_CONNECTION = 1,
|
||||
};
|
||||
|
||||
|
||||
/// Defines a polyogn within a dtMeshTile object.
|
||||
/// @ingroup detour
|
||||
struct dtPoly
|
||||
{
|
||||
/// Index to first link in linked list. (Or #DT_NULL_LINK if there is no link.)
|
||||
unsigned int firstLink;
|
||||
|
||||
/// The indices of the polygon's vertices.
|
||||
/// The actual vertices are located in dtMeshTile::verts.
|
||||
unsigned short verts[DT_VERTS_PER_POLYGON];
|
||||
|
||||
/// Packed data representing neighbor polygons references and flags for each edge.
|
||||
unsigned short neis[DT_VERTS_PER_POLYGON];
|
||||
|
||||
/// The user defined polygon flags.
|
||||
unsigned short flags;
|
||||
|
||||
/// The number of vertices in the polygon.
|
||||
unsigned char vertCount;
|
||||
|
||||
/// The bit packed area id and polygon type.
|
||||
/// @note Use the structure's set and get methods to acess this value.
|
||||
unsigned char areaAndtype;
|
||||
|
||||
/// Sets the user defined area id. [Limit: < #DT_MAX_AREAS]
|
||||
inline void setArea(unsigned char a) { areaAndtype = (areaAndtype & 0xc0) | (a & 0x3f); }
|
||||
|
||||
/// Sets the polygon type. (See: #dtPolyTypes.)
|
||||
inline void setType(unsigned char t) { areaAndtype = (areaAndtype & 0x3f) | (t << 6); }
|
||||
|
||||
/// Gets the user defined area id.
|
||||
inline unsigned char getArea() const { return areaAndtype & 0x3f; }
|
||||
|
||||
/// Gets the polygon type. (See: #dtPolyTypes)
|
||||
inline unsigned char getType() const { return areaAndtype >> 6; }
|
||||
};
|
||||
|
||||
/// Defines the location of detail sub-mesh data within a dtMeshTile.
|
||||
struct dtPolyDetail
|
||||
{
|
||||
unsigned int vertBase; ///< The offset of the vertices in the dtMeshTile::detailVerts array.
|
||||
unsigned int triBase; ///< The offset of the triangles in the dtMeshTile::detailTris array.
|
||||
unsigned char vertCount; ///< The number of vertices in the sub-mesh.
|
||||
unsigned char triCount; ///< The number of triangles in the sub-mesh.
|
||||
};
|
||||
|
||||
/// Defines a link between polygons.
|
||||
/// @note This structure is rarely if ever used by the end user.
|
||||
/// @see dtMeshTile
|
||||
struct dtLink
|
||||
{
|
||||
dtPolyRef ref; ///< Neighbour reference. (The neighbor that is linked to.)
|
||||
unsigned int next; ///< Index of the next link.
|
||||
unsigned char edge; ///< Index of the polygon edge that owns this link.
|
||||
unsigned char side; ///< If a boundary link, defines on which side the link is.
|
||||
unsigned char bmin; ///< If a boundary link, defines the minimum sub-edge area.
|
||||
unsigned char bmax; ///< If a boundary link, defines the maximum sub-edge area.
|
||||
};
|
||||
|
||||
/// Bounding volume node.
|
||||
/// @note This structure is rarely if ever used by the end user.
|
||||
/// @see dtMeshTile
|
||||
struct dtBVNode
|
||||
{
|
||||
unsigned short bmin[3]; ///< Minimum bounds of the node's AABB. [(x, y, z)]
|
||||
unsigned short bmax[3]; ///< Maximum bounds of the node's AABB. [(x, y, z)]
|
||||
int i; ///< The node's index. (Negative for escape sequence.)
|
||||
};
|
||||
|
||||
/// Defines an navigation mesh off-mesh connection within a dtMeshTile object.
|
||||
/// An off-mesh connection is a user defined traversable connection made up to two vertices.
|
||||
struct dtOffMeshConnection
|
||||
{
|
||||
/// The endpoints of the connection. [(ax, ay, az, bx, by, bz)]
|
||||
float pos[6];
|
||||
|
||||
/// The radius of the endpoints. [Limit: >= 0]
|
||||
float rad;
|
||||
|
||||
/// The polygon reference of the connection within the tile.
|
||||
unsigned short poly;
|
||||
|
||||
/// Link flags.
|
||||
/// @note These are not the connection's user defined flags. Those are assigned via the
|
||||
/// connection's dtPoly definition. These are link flags used for internal purposes.
|
||||
unsigned char flags;
|
||||
|
||||
/// End point side.
|
||||
unsigned char side;
|
||||
|
||||
/// The id of the offmesh connection. (User assigned when the navigation mesh is built.)
|
||||
unsigned int userId;
|
||||
};
|
||||
|
||||
/// Provides high level information related to a dtMeshTile object.
|
||||
/// @ingroup detour
|
||||
struct dtMeshHeader
|
||||
{
|
||||
int magic; ///< Tile magic number. (Used to identify the data format.)
|
||||
int version; ///< Tile data format version number.
|
||||
int x; ///< The x-position of the tile within the dtNavMesh tile grid. (x, y, layer)
|
||||
int y; ///< The y-position of the tile within the dtNavMesh tile grid. (x, y, layer)
|
||||
int layer; ///< The layer of the tile within the dtNavMesh tile grid. (x, y, layer)
|
||||
unsigned int userId; ///< The user defined id of the tile.
|
||||
int polyCount; ///< The number of polygons in the tile.
|
||||
int vertCount; ///< The number of vertices in the tile.
|
||||
int maxLinkCount; ///< The number of allocated links.
|
||||
int detailMeshCount; ///< The number of sub-meshes in the detail mesh.
|
||||
|
||||
/// The number of unique vertices in the detail mesh. (In addition to the polygon vertices.)
|
||||
int detailVertCount;
|
||||
|
||||
int detailTriCount; ///< The number of triangles in the detail mesh.
|
||||
int bvNodeCount; ///< The number of bounding volume nodes. (Zero if bounding volumes are disabled.)
|
||||
int offMeshConCount; ///< The number of off-mesh connections.
|
||||
int offMeshBase; ///< The index of the first polygon which is an off-mesh connection.
|
||||
float walkableHeight; ///< The height of the agents using the tile.
|
||||
float walkableRadius; ///< The radius of the agents using the tile.
|
||||
float walkableClimb; ///< The maximum climb height of the agents using the tile.
|
||||
float bmin[3]; ///< The minimum bounds of the tile's AABB. [(x, y, z)]
|
||||
float bmax[3]; ///< The maximum bounds of the tile's AABB. [(x, y, z)]
|
||||
|
||||
/// The bounding volume quantization factor.
|
||||
float bvQuantFactor;
|
||||
};
|
||||
|
||||
/// Defines a navigation mesh tile.
|
||||
/// @ingroup detour
|
||||
struct dtMeshTile
|
||||
{
|
||||
unsigned int salt; ///< Counter describing modifications to the tile.
|
||||
|
||||
unsigned int linksFreeList; ///< Index to the next free link.
|
||||
dtMeshHeader* header; ///< The tile header.
|
||||
dtPoly* polys; ///< The tile polygons. [Size: dtMeshHeader::polyCount]
|
||||
float* verts; ///< The tile vertices. [Size: dtMeshHeader::vertCount]
|
||||
dtLink* links; ///< The tile links. [Size: dtMeshHeader::maxLinkCount]
|
||||
dtPolyDetail* detailMeshes; ///< The tile's detail sub-meshes. [Size: dtMeshHeader::detailMeshCount]
|
||||
|
||||
/// The detail mesh's unique vertices. [(x, y, z) * dtMeshHeader::detailVertCount]
|
||||
float* detailVerts;
|
||||
|
||||
/// The detail mesh's triangles. [(vertA, vertB, vertC) * dtMeshHeader::detailTriCount]
|
||||
unsigned char* detailTris;
|
||||
|
||||
/// The tile bounding volume nodes. [Size: dtMeshHeader::bvNodeCount]
|
||||
/// (Will be null if bounding volumes are disabled.)
|
||||
dtBVNode* bvTree;
|
||||
|
||||
dtOffMeshConnection* offMeshCons; ///< The tile off-mesh connections. [Size: dtMeshHeader::offMeshConCount]
|
||||
|
||||
unsigned char* data; ///< The tile data. (Not directly accessed under normal situations.)
|
||||
int dataSize; ///< Size of the tile data.
|
||||
int flags; ///< Tile flags. (See: #dtTileFlags)
|
||||
dtMeshTile* next; ///< The next free tile, or the next tile in the spatial grid.
|
||||
};
|
||||
|
||||
/// Configuration parameters used to define multi-tile navigation meshes.
|
||||
/// The values are used to allocate space during the initialization of a navigation mesh.
|
||||
/// @see dtNavMesh::init()
|
||||
/// @ingroup detour
|
||||
struct dtNavMeshParams
|
||||
{
|
||||
float orig[3]; ///< The world space origin of the navigation mesh's tile space. [(x, y, z)]
|
||||
float tileWidth; ///< The width of each tile. (Along the x-axis.)
|
||||
float tileHeight; ///< The height of each tile. (Along the z-axis.)
|
||||
int maxTiles; ///< The maximum number of tiles the navigation mesh can contain.
|
||||
int maxPolys; ///< The maximum number of polygons each tile can contain.
|
||||
};
|
||||
|
||||
/// A navigation mesh based on tiles of convex polygons.
|
||||
/// @ingroup detour
|
||||
class dtNavMesh
|
||||
{
|
||||
public:
|
||||
dtNavMesh();
|
||||
~dtNavMesh();
|
||||
|
||||
/// @{
|
||||
/// @name Initialization and Tile Management
|
||||
|
||||
/// Initializes the navigation mesh for tiled use.
|
||||
/// @param[in] params Initialization parameters.
|
||||
/// @return The status flags for the operation.
|
||||
dtStatus init(const dtNavMeshParams* params);
|
||||
|
||||
/// Initializes the navigation mesh for single tile use.
|
||||
/// @param[in] data Data of the new tile. (See: #dtCreateNavMeshData)
|
||||
/// @param[in] dataSize The data size of the new tile.
|
||||
/// @param[in] flags The tile flags. (See: #dtTileFlags)
|
||||
/// @return The status flags for the operation.
|
||||
/// @see dtCreateNavMeshData
|
||||
dtStatus init(unsigned char* data, const int dataSize, const int flags);
|
||||
|
||||
/// The navigation mesh initialization params.
|
||||
const dtNavMeshParams* getParams() const;
|
||||
|
||||
/// Adds a tile to the navigation mesh.
|
||||
/// @param[in] data Data for the new tile mesh. (See: #dtCreateNavMeshData)
|
||||
/// @param[in] dataSize Data size of the new tile mesh.
|
||||
/// @param[in] flags Tile flags. (See: #dtTileFlags)
|
||||
/// @param[in] lastRef The desired reference for the tile. (When reloading a tile.) [opt] [Default: 0]
|
||||
/// @param[out] result The tile reference. (If the tile was succesfully added.) [opt]
|
||||
/// @return The status flags for the operation.
|
||||
dtStatus addTile(unsigned char* data, int dataSize, int flags, dtTileRef lastRef, dtTileRef* result);
|
||||
|
||||
/// Removes the specified tile from the navigation mesh.
|
||||
/// @param[in] ref The reference of the tile to remove.
|
||||
/// @param[out] data Data associated with deleted tile.
|
||||
/// @param[out] dataSize Size of the data associated with deleted tile.
|
||||
/// @return The status flags for the operation.
|
||||
dtStatus removeTile(dtTileRef ref, unsigned char** data, int* dataSize);
|
||||
|
||||
/// @}
|
||||
|
||||
/// @{
|
||||
/// @name Query Functions
|
||||
|
||||
/// Calculates the tile grid location for the specified world position.
|
||||
/// @param[in] pos The world position for the query. [(x, y, z)]
|
||||
/// @param[out] tx The tile's x-location. (x, y)
|
||||
/// @param[out] ty The tile's y-location. (x, y)
|
||||
void calcTileLoc(const float* pos, int* tx, int* ty) const;
|
||||
|
||||
/// Gets the tile at the specified grid location.
|
||||
/// @param[in] x The tile's x-location. (x, y, layer)
|
||||
/// @param[in] y The tile's y-location. (x, y, layer)
|
||||
/// @param[in] layer The tile's layer. (x, y, layer)
|
||||
/// @return The tile, or null if the tile does not exist.
|
||||
const dtMeshTile* getTileAt(const int x, const int y, const int layer) const;
|
||||
|
||||
/// Gets all tiles at the specified grid location. (All layers.)
|
||||
/// @param[in] x The tile's x-location. (x, y)
|
||||
/// @param[in] y The tile's y-location. (x, y)
|
||||
/// @param[out] tiles A pointer to an array of tiles that will hold the result.
|
||||
/// @param[in] maxTiles The maximum tiles the tiles parameter can hold.
|
||||
/// @return The number of tiles returned in the tiles array.
|
||||
int getTilesAt(const int x, const int y,
|
||||
dtMeshTile const** tiles, const int maxTiles) const;
|
||||
|
||||
/// Gets the tile reference for the tile at specified grid location.
|
||||
/// @param[in] x The tile's x-location. (x, y, layer)
|
||||
/// @param[in] y The tile's y-location. (x, y, layer)
|
||||
/// @param[in] layer The tile's layer. (x, y, layer)
|
||||
/// @return The tile reference of the tile, or 0 if there is none.
|
||||
dtTileRef getTileRefAt(int x, int y, int layer) const;
|
||||
|
||||
/// Gets the tile reference for the specified tile.
|
||||
/// @param[in] tile The tile.
|
||||
/// @return The tile reference of the tile.
|
||||
dtTileRef getTileRef(const dtMeshTile* tile) const;
|
||||
|
||||
/// Gets the tile for the specified tile reference.
|
||||
/// @param[in] ref The tile reference of the tile to retrieve.
|
||||
/// @return The tile for the specified reference, or null if the
|
||||
/// reference is invalid.
|
||||
const dtMeshTile* getTileByRef(dtTileRef ref) const;
|
||||
|
||||
/// The maximum number of tiles supported by the navigation mesh.
|
||||
/// @return The maximum number of tiles supported by the navigation mesh.
|
||||
int getMaxTiles() const;
|
||||
|
||||
/// Gets the tile at the specified index.
|
||||
/// @param[in] i The tile index. [Limit: 0 >= index < #getMaxTiles()]
|
||||
/// @return The tile at the specified index.
|
||||
const dtMeshTile* getTile(int i) const;
|
||||
|
||||
/// Gets the tile and polygon for the specified polygon reference.
|
||||
/// @param[in] ref The reference for the a polygon.
|
||||
/// @param[out] tile The tile containing the polygon.
|
||||
/// @param[out] poly The polygon.
|
||||
/// @return The status flags for the operation.
|
||||
dtStatus getTileAndPolyByRef(const dtPolyRef ref, const dtMeshTile** tile, const dtPoly** poly) const;
|
||||
|
||||
/// Returns the tile and polygon for the specified polygon reference.
|
||||
/// @param[in] ref A known valid reference for a polygon.
|
||||
/// @param[out] tile The tile containing the polygon.
|
||||
/// @param[out] poly The polygon.
|
||||
void getTileAndPolyByRefUnsafe(const dtPolyRef ref, const dtMeshTile** tile, const dtPoly** poly) const;
|
||||
|
||||
/// Checks the validity of a polygon reference.
|
||||
/// @param[in] ref The polygon reference to check.
|
||||
/// @return True if polygon reference is valid for the navigation mesh.
|
||||
bool isValidPolyRef(dtPolyRef ref) const;
|
||||
|
||||
/// Gets the polygon reference for the tile's base polygon.
|
||||
/// @param[in] tile The tile.
|
||||
/// @return The polygon reference for the base polygon in the specified tile.
|
||||
dtPolyRef getPolyRefBase(const dtMeshTile* tile) const;
|
||||
|
||||
/// Gets the endpoints for an off-mesh connection, ordered by "direction of travel".
|
||||
/// @param[in] prevRef The reference of the polygon before the connection.
|
||||
/// @param[in] polyRef The reference of the off-mesh connection polygon.
|
||||
/// @param[out] startPos The start position of the off-mesh connection. [(x, y, z)]
|
||||
/// @param[out] endPos The end position of the off-mesh connection. [(x, y, z)]
|
||||
/// @return The status flags for the operation.
|
||||
dtStatus getOffMeshConnectionPolyEndPoints(dtPolyRef prevRef, dtPolyRef polyRef, float* startPos, float* endPos) const;
|
||||
|
||||
/// Gets the specified off-mesh connection.
|
||||
/// @param[in] ref The polygon reference of the off-mesh connection.
|
||||
/// @return The specified off-mesh connection, or null if the polygon reference is not valid.
|
||||
const dtOffMeshConnection* getOffMeshConnectionByRef(dtPolyRef ref) const;
|
||||
|
||||
/// @}
|
||||
|
||||
/// @{
|
||||
/// @name State Management
|
||||
/// These functions do not effect #dtTileRef or #dtPolyRef's.
|
||||
|
||||
/// Sets the user defined flags for the specified polygon.
|
||||
/// @param[in] ref The polygon reference.
|
||||
/// @param[in] flags The new flags for the polygon.
|
||||
/// @return The status flags for the operation.
|
||||
dtStatus setPolyFlags(dtPolyRef ref, unsigned short flags);
|
||||
|
||||
/// Gets the user defined flags for the specified polygon.
|
||||
/// @param[in] ref The polygon reference.
|
||||
/// @param[out] resultFlags The polygon flags.
|
||||
/// @return The status flags for the operation.
|
||||
dtStatus getPolyFlags(dtPolyRef ref, unsigned short* resultFlags) const;
|
||||
|
||||
/// Sets the user defined area for the specified polygon.
|
||||
/// @param[in] ref The polygon reference.
|
||||
/// @param[in] area The new area id for the polygon. [Limit: < #DT_MAX_AREAS]
|
||||
/// @return The status flags for the operation.
|
||||
dtStatus setPolyArea(dtPolyRef ref, unsigned char area);
|
||||
|
||||
/// Gets the user defined area for the specified polygon.
|
||||
/// @param[in] ref The polygon reference.
|
||||
/// @param[out] resultArea The area id for the polygon.
|
||||
/// @return The status flags for the operation.
|
||||
dtStatus getPolyArea(dtPolyRef ref, unsigned char* resultArea) const;
|
||||
|
||||
/// Gets the size of the buffer required by #storeTileState to store the specified tile's state.
|
||||
/// @param[in] tile The tile.
|
||||
/// @return The size of the buffer required to store the state.
|
||||
int getTileStateSize(const dtMeshTile* tile) const;
|
||||
|
||||
/// Stores the non-structural state of the tile in the specified buffer. (Flags, area ids, etc.)
|
||||
/// @param[in] tile The tile.
|
||||
/// @param[out] data The buffer to store the tile's state in.
|
||||
/// @param[in] maxDataSize The size of the data buffer. [Limit: >= #getTileStateSize]
|
||||
/// @return The status flags for the operation.
|
||||
dtStatus storeTileState(const dtMeshTile* tile, unsigned char* data, const int maxDataSize) const;
|
||||
|
||||
/// Restores the state of the tile.
|
||||
/// @param[in] tile The tile.
|
||||
/// @param[in] data The new state. (Obtained from #storeTileState.)
|
||||
/// @param[in] maxDataSize The size of the state within the data buffer.
|
||||
/// @return The status flags for the operation.
|
||||
dtStatus restoreTileState(dtMeshTile* tile, const unsigned char* data, const int maxDataSize);
|
||||
|
||||
/// @}
|
||||
|
||||
/// @{
|
||||
/// @name Encoding and Decoding
|
||||
/// These functions are generally meant for internal use only.
|
||||
|
||||
/// Derives a standard polygon reference.
|
||||
/// @note This function is generally meant for internal use only.
|
||||
/// @param[in] salt The tile's salt value.
|
||||
/// @param[in] it The index of the tile.
|
||||
/// @param[in] ip The index of the polygon within the tile.
|
||||
inline dtPolyRef encodePolyId(unsigned int salt, unsigned int it, unsigned int ip) const
|
||||
{
|
||||
#ifdef DT_POLYREF64
|
||||
return ((dtPolyRef)salt << (DT_POLY_BITS+DT_TILE_BITS)) | ((dtPolyRef)it << DT_POLY_BITS) | (dtPolyRef)ip;
|
||||
#else
|
||||
return ((dtPolyRef)salt << (m_polyBits+m_tileBits)) | ((dtPolyRef)it << m_polyBits) | (dtPolyRef)ip;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Decodes a standard polygon reference.
|
||||
/// @note This function is generally meant for internal use only.
|
||||
/// @param[in] ref The polygon reference to decode.
|
||||
/// @param[out] salt The tile's salt value.
|
||||
/// @param[out] it The index of the tile.
|
||||
/// @param[out] ip The index of the polygon within the tile.
|
||||
/// @see #encodePolyId
|
||||
inline void decodePolyId(dtPolyRef ref, unsigned int& salt, unsigned int& it, unsigned int& ip) const
|
||||
{
|
||||
#ifdef DT_POLYREF64
|
||||
const dtPolyRef saltMask = ((dtPolyRef)1<<DT_SALT_BITS)-1;
|
||||
const dtPolyRef tileMask = ((dtPolyRef)1<<DT_TILE_BITS)-1;
|
||||
const dtPolyRef polyMask = ((dtPolyRef)1<<DT_POLY_BITS)-1;
|
||||
salt = (unsigned int)((ref >> (DT_POLY_BITS+DT_TILE_BITS)) & saltMask);
|
||||
it = (unsigned int)((ref >> DT_POLY_BITS) & tileMask);
|
||||
ip = (unsigned int)(ref & polyMask);
|
||||
#else
|
||||
const dtPolyRef saltMask = ((dtPolyRef)1<<m_saltBits)-1;
|
||||
const dtPolyRef tileMask = ((dtPolyRef)1<<m_tileBits)-1;
|
||||
const dtPolyRef polyMask = ((dtPolyRef)1<<m_polyBits)-1;
|
||||
salt = (unsigned int)((ref >> (m_polyBits+m_tileBits)) & saltMask);
|
||||
it = (unsigned int)((ref >> m_polyBits) & tileMask);
|
||||
ip = (unsigned int)(ref & polyMask);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Extracts a tile's salt value from the specified polygon reference.
|
||||
/// @note This function is generally meant for internal use only.
|
||||
/// @param[in] ref The polygon reference.
|
||||
/// @see #encodePolyId
|
||||
inline unsigned int decodePolyIdSalt(dtPolyRef ref) const
|
||||
{
|
||||
#ifdef DT_POLYREF64
|
||||
const dtPolyRef saltMask = ((dtPolyRef)1<<DT_SALT_BITS)-1;
|
||||
return (unsigned int)((ref >> (DT_POLY_BITS+DT_TILE_BITS)) & saltMask);
|
||||
#else
|
||||
const dtPolyRef saltMask = ((dtPolyRef)1<<m_saltBits)-1;
|
||||
return (unsigned int)((ref >> (m_polyBits+m_tileBits)) & saltMask);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Extracts the tile's index from the specified polygon reference.
|
||||
/// @note This function is generally meant for internal use only.
|
||||
/// @param[in] ref The polygon reference.
|
||||
/// @see #encodePolyId
|
||||
inline unsigned int decodePolyIdTile(dtPolyRef ref) const
|
||||
{
|
||||
#ifdef DT_POLYREF64
|
||||
const dtPolyRef tileMask = ((dtPolyRef)1<<DT_TILE_BITS)-1;
|
||||
return (unsigned int)((ref >> DT_POLY_BITS) & tileMask);
|
||||
#else
|
||||
const dtPolyRef tileMask = ((dtPolyRef)1<<m_tileBits)-1;
|
||||
return (unsigned int)((ref >> m_polyBits) & tileMask);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Extracts the polygon's index (within its tile) from the specified polygon reference.
|
||||
/// @note This function is generally meant for internal use only.
|
||||
/// @param[in] ref The polygon reference.
|
||||
/// @see #encodePolyId
|
||||
inline unsigned int decodePolyIdPoly(dtPolyRef ref) const
|
||||
{
|
||||
#ifdef DT_POLYREF64
|
||||
const dtPolyRef polyMask = ((dtPolyRef)1<<DT_POLY_BITS)-1;
|
||||
return (unsigned int)(ref & polyMask);
|
||||
#else
|
||||
const dtPolyRef polyMask = ((dtPolyRef)1<<m_polyBits)-1;
|
||||
return (unsigned int)(ref & polyMask);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
private:
|
||||
|
||||
/// Returns pointer to tile in the tile array.
|
||||
dtMeshTile* getTile(int i);
|
||||
|
||||
/// Returns neighbour tile based on side.
|
||||
int getTilesAt(const int x, const int y,
|
||||
dtMeshTile** tiles, const int maxTiles) const;
|
||||
|
||||
/// Returns neighbour tile based on side.
|
||||
int getNeighbourTilesAt(const int x, const int y, const int side,
|
||||
dtMeshTile** tiles, const int maxTiles) const;
|
||||
|
||||
/// Returns all polygons in neighbour tile based on portal defined by the segment.
|
||||
int findConnectingPolys(const float* va, const float* vb,
|
||||
const dtMeshTile* tile, int side,
|
||||
dtPolyRef* con, float* conarea, int maxcon) const;
|
||||
|
||||
/// Builds internal polygons links for a tile.
|
||||
void connectIntLinks(dtMeshTile* tile);
|
||||
/// Builds internal polygons links for a tile.
|
||||
void baseOffMeshLinks(dtMeshTile* tile);
|
||||
|
||||
/// Builds external polygon links for a tile.
|
||||
void connectExtLinks(dtMeshTile* tile, dtMeshTile* target, int side);
|
||||
/// Builds external polygon links for a tile.
|
||||
void connectExtOffMeshLinks(dtMeshTile* tile, dtMeshTile* target, int side);
|
||||
|
||||
/// Removes external links at specified side.
|
||||
void unconnectExtLinks(dtMeshTile* tile, dtMeshTile* target);
|
||||
|
||||
|
||||
// TODO: These methods are duplicates from dtNavMeshQuery, but are needed for off-mesh connection finding.
|
||||
|
||||
/// Queries polygons within a tile.
|
||||
int queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax,
|
||||
dtPolyRef* polys, const int maxPolys) const;
|
||||
/// Find nearest polygon within a tile.
|
||||
dtPolyRef findNearestPolyInTile(const dtMeshTile* tile, const float* center,
|
||||
const float* extents, float* nearestPt) const;
|
||||
/// Returns closest point on polygon.
|
||||
void closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const;
|
||||
|
||||
dtNavMeshParams m_params; ///< Current initialization params. TODO: do not store this info twice.
|
||||
float m_orig[3]; ///< Origin of the tile (0,0)
|
||||
float m_tileWidth, m_tileHeight; ///< Dimensions of each tile.
|
||||
int m_maxTiles; ///< Max number of tiles.
|
||||
int m_tileLutSize; ///< Tile hash lookup size (must be pot).
|
||||
int m_tileLutMask; ///< Tile hash lookup mask.
|
||||
|
||||
dtMeshTile** m_posLookup; ///< Tile hash lookup.
|
||||
dtMeshTile* m_nextFree; ///< Freelist of tiles.
|
||||
dtMeshTile* m_tiles; ///< List of tiles.
|
||||
|
||||
#ifndef DT_POLYREF64
|
||||
unsigned int m_saltBits; ///< Number of salt bits in the tile ID.
|
||||
unsigned int m_tileBits; ///< Number of tile bits in the tile ID.
|
||||
unsigned int m_polyBits; ///< Number of poly bits in the tile ID.
|
||||
#endif
|
||||
};
|
||||
|
||||
/// Allocates a navigation mesh object using the Detour allocator.
|
||||
/// @return A navigation mesh that is ready for initialization, or null on failure.
|
||||
/// @ingroup detour
|
||||
dtNavMesh* dtAllocNavMesh();
|
||||
|
||||
/// Frees the specified navigation mesh object using the Detour allocator.
|
||||
/// @param[in] navmesh A navigation mesh allocated using #dtAllocNavMesh
|
||||
/// @ingroup detour
|
||||
void dtFreeNavMesh(dtNavMesh* navmesh);
|
||||
|
||||
#endif // DETOURNAVMESH_H
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This section contains detailed documentation for members that don't have
|
||||
// a source file. It reduces clutter in the main section of the header.
|
||||
|
||||
/**
|
||||
|
||||
@typedef dtPolyRef
|
||||
@par
|
||||
|
||||
Polygon references are subject to the same invalidate/preserve/restore
|
||||
rules that apply to #dtTileRef's. If the #dtTileRef for the polygon's
|
||||
tile changes, the polygon reference becomes invalid.
|
||||
|
||||
Changing a polygon's flags, area id, etc. does not impact its polygon
|
||||
reference.
|
||||
|
||||
@typedef dtTileRef
|
||||
@par
|
||||
|
||||
The following changes will invalidate a tile reference:
|
||||
|
||||
- The referenced tile has been removed from the navigation mesh.
|
||||
- The navigation mesh has been initialized using a different set
|
||||
of #dtNavMeshParams.
|
||||
|
||||
A tile reference is preserved/restored if the tile is added to a navigation
|
||||
mesh initialized with the original #dtNavMeshParams and is added at the
|
||||
original reference location. (E.g. The lastRef parameter is used with
|
||||
dtNavMesh::addTile.)
|
||||
|
||||
Basically, if the storage structure of a tile changes, its associated
|
||||
tile reference changes.
|
||||
|
||||
|
||||
@var unsigned short dtPoly::neis[DT_VERTS_PER_POLYGON]
|
||||
@par
|
||||
|
||||
Each entry represents data for the edge starting at the vertex of the same index.
|
||||
E.g. The entry at index n represents the edge data for vertex[n] to vertex[n+1].
|
||||
|
||||
A value of zero indicates the edge has no polygon connection. (It makes up the
|
||||
border of the navigation mesh.)
|
||||
|
||||
The information can be extracted as follows:
|
||||
@code
|
||||
neighborRef = neis[n] & 0xff; // Get the neighbor polygon reference.
|
||||
|
||||
if (neis[n] & #DT_EX_LINK)
|
||||
{
|
||||
// The edge is an external (portal) edge.
|
||||
}
|
||||
@endcode
|
||||
|
||||
@var float dtMeshHeader::bvQuantFactor
|
||||
@par
|
||||
|
||||
This value is used for converting between world and bounding volume coordinates.
|
||||
For example:
|
||||
@code
|
||||
const float cs = 1.0f / tile->header->bvQuantFactor;
|
||||
const dtBVNode* n = &tile->bvTree[i];
|
||||
if (n->i >= 0)
|
||||
{
|
||||
// This is a leaf node.
|
||||
float worldMinX = tile->header->bmin[0] + n->bmin[0]*cs;
|
||||
float worldMinY = tile->header->bmin[0] + n->bmin[1]*cs;
|
||||
// Etc...
|
||||
}
|
||||
@endcode
|
||||
|
||||
@struct dtMeshTile
|
||||
@par
|
||||
|
||||
Tiles generally only exist within the context of a dtNavMesh object.
|
||||
|
||||
Some tile content is optional. For example, a tile may not contain any
|
||||
off-mesh connections. In this case the associated pointer will be null.
|
||||
|
||||
If a detail mesh exists it will share vertices with the base polygon mesh.
|
||||
Only the vertices unique to the detail mesh will be stored in #detailVerts.
|
||||
|
||||
@warning Tiles returned by a dtNavMesh object are not guarenteed to be populated.
|
||||
For example: The tile at a location might not have been loaded yet, or may have been removed.
|
||||
In this case, pointers will be null. So if in doubt, check the polygon count in the
|
||||
tile's header to determine if a tile has polygons defined.
|
||||
|
||||
@var float dtOffMeshConnection::pos[6]
|
||||
@par
|
||||
|
||||
For a properly built navigation mesh, vertex A will always be within the bounds of the mesh.
|
||||
Vertex B is not required to be within the bounds of the mesh.
|
||||
|
||||
*/
|
||||
148
recast/detour/include/DetourNavMeshBuilder.h
Normal file
148
recast/detour/include/DetourNavMeshBuilder.h
Normal file
@ -0,0 +1,148 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#ifndef DETOURNAVMESHBUILDER_H
|
||||
#define DETOURNAVMESHBUILDER_H
|
||||
|
||||
#include "DetourAlloc.h"
|
||||
|
||||
/// Represents the source data used to build an navigation mesh tile.
|
||||
/// @ingroup detour
|
||||
struct dtNavMeshCreateParams
|
||||
{
|
||||
|
||||
/// @name Polygon Mesh Attributes
|
||||
/// Used to create the base navigation graph.
|
||||
/// See #rcPolyMesh for details related to these attributes.
|
||||
/// @{
|
||||
|
||||
const unsigned short* verts; ///< The polygon mesh vertices. [(x, y, z) * #vertCount] [Unit: vx]
|
||||
int vertCount; ///< The number vertices in the polygon mesh. [Limit: >= 3]
|
||||
const unsigned short* polys; ///< The polygon data. [Size: #polyCount * 2 * #nvp]
|
||||
const unsigned short* polyFlags; ///< The user defined flags assigned to each polygon. [Size: #polyCount]
|
||||
const unsigned char* polyAreas; ///< The user defined area ids assigned to each polygon. [Size: #polyCount]
|
||||
int polyCount; ///< Number of polygons in the mesh. [Limit: >= 1]
|
||||
int nvp; ///< Number maximum number of vertices per polygon. [Limit: >= 3]
|
||||
|
||||
/// @}
|
||||
/// @name Height Detail Attributes (Optional)
|
||||
/// See #rcPolyMeshDetail for details related to these attributes.
|
||||
/// @{
|
||||
|
||||
const unsigned int* detailMeshes; ///< The height detail sub-mesh data. [Size: 4 * #polyCount]
|
||||
const float* detailVerts; ///< The detail mesh vertices. [Size: 3 * #detailVertsCount] [Unit: wu]
|
||||
int detailVertsCount; ///< The number of vertices in the detail mesh.
|
||||
const unsigned char* detailTris; ///< The detail mesh triangles. [Size: 4 * #detailTriCount]
|
||||
int detailTriCount; ///< The number of triangles in the detail mesh.
|
||||
|
||||
/// @}
|
||||
/// @name Off-Mesh Connections Attributes (Optional)
|
||||
/// Used to define a custom point-to-point edge within the navigation graph, an
|
||||
/// off-mesh connection is a user defined traversable connection made up to two vertices,
|
||||
/// at least one of which resides within a navigation mesh polygon.
|
||||
/// @{
|
||||
|
||||
/// Off-mesh connection vertices. [(ax, ay, az, bx, by, bz) * #offMeshConCount] [Unit: wu]
|
||||
const float* offMeshConVerts;
|
||||
/// Off-mesh connection radii. [Size: #offMeshConCount] [Unit: wu]
|
||||
const float* offMeshConRad;
|
||||
/// User defined flags assigned to the off-mesh connections. [Size: #offMeshConCount]
|
||||
const unsigned short* offMeshConFlags;
|
||||
/// User defined area ids assigned to the off-mesh connections. [Size: #offMeshConCount]
|
||||
const unsigned char* offMeshConAreas;
|
||||
/// The permitted travel direction of the off-mesh connections. [Size: #offMeshConCount]
|
||||
///
|
||||
/// 0 = Travel only from endpoint A to endpoint B.<br/>
|
||||
/// #DT_OFFMESH_CON_BIDIR = Bidirectional travel.
|
||||
const unsigned char* offMeshConDir;
|
||||
/// The user defined ids of the off-mesh connection. [Size: #offMeshConCount]
|
||||
const unsigned int* offMeshConUserID;
|
||||
/// The number of off-mesh connections. [Limit: >= 0]
|
||||
int offMeshConCount;
|
||||
|
||||
/// @}
|
||||
/// @name Tile Attributes
|
||||
/// @note The tile grid/layer data can be left at zero if the destination is a single tile mesh.
|
||||
/// @{
|
||||
|
||||
unsigned int userId; ///< The user defined id of the tile.
|
||||
int tileX; ///< The tile's x-grid location within the multi-tile destination mesh. (Along the x-axis.)
|
||||
int tileY; ///< The tile's y-grid location within the multi-tile desitation mesh. (Along the z-axis.)
|
||||
int tileLayer; ///< The tile's layer within the layered destination mesh. [Limit: >= 0] (Along the y-axis.)
|
||||
float bmin[3]; ///< The minimum bounds of the tile. [(x, y, z)] [Unit: wu]
|
||||
float bmax[3]; ///< The maximum bounds of the tile. [(x, y, z)] [Unit: wu]
|
||||
|
||||
/// @}
|
||||
/// @name General Configuration Attributes
|
||||
/// @{
|
||||
|
||||
float walkableHeight; ///< The agent height. [Unit: wu]
|
||||
float walkableRadius; ///< The agent radius. [Unit: wu]
|
||||
float walkableClimb; ///< The agent maximum traversable ledge. (Up/Down) [Unit: wu]
|
||||
float cs; ///< The xz-plane cell size of the polygon mesh. [Limit: > 0] [Unit: wu]
|
||||
float ch; ///< The y-axis cell height of the polygon mesh. [Limit: > 0] [Unit: wu]
|
||||
|
||||
/// True if a bounding volume tree should be built for the tile.
|
||||
/// @note The BVTree is not normally needed for layered navigation meshes.
|
||||
bool buildBvTree;
|
||||
|
||||
/// @}
|
||||
};
|
||||
|
||||
/// Builds navigation mesh tile data from the provided tile creation data.
|
||||
/// @ingroup detour
|
||||
/// @param[in] params Tile creation data.
|
||||
/// @param[out] outData The resulting tile data.
|
||||
/// @param[out] outDataSize The size of the tile data array.
|
||||
/// @return True if the tile data was successfully created.
|
||||
bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData, int* outDataSize);
|
||||
|
||||
/// Swaps the endianess of the tile data's header (#dtMeshHeader).
|
||||
/// @param[in,out] data The tile data array.
|
||||
/// @param[in] dataSize The size of the data array.
|
||||
bool dtNavMeshHeaderSwapEndian(unsigned char* data, const int dataSize);
|
||||
|
||||
/// Swaps endianess of the tile data.
|
||||
/// @param[in,out] data The tile data array.
|
||||
/// @param[in] dataSize The size of the data array.
|
||||
bool dtNavMeshDataSwapEndian(unsigned char* data, const int dataSize);
|
||||
|
||||
#endif // DETOURNAVMESHBUILDER_H
|
||||
|
||||
// This section contains detailed documentation for members that don't have
|
||||
// a source file. It reduces clutter in the main section of the header.
|
||||
|
||||
/**
|
||||
|
||||
@struct dtNavMeshCreateParams
|
||||
@par
|
||||
|
||||
This structure is used to marshal data between the Recast mesh generation pipeline and Detour navigation components.
|
||||
|
||||
See the rcPolyMesh and rcPolyMeshDetail documentation for detailed information related to mesh structure.
|
||||
|
||||
Units are usually in voxels (vx) or world units (wu). The units for voxels, grid size, and cell size
|
||||
are all based on the values of #cs and #ch.
|
||||
|
||||
The standard navigation mesh build process is to create tile data using dtCreateNavMeshData, then add the tile
|
||||
to a navigation mesh using either the dtNavMesh single tile <tt>init()</tt> function or the dtNavMesh::addTile()
|
||||
function.
|
||||
|
||||
@see dtCreateNavMeshData
|
||||
|
||||
*/
|
||||
536
recast/detour/include/DetourNavMeshQuery.h
Normal file
536
recast/detour/include/DetourNavMeshQuery.h
Normal file
@ -0,0 +1,536 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#ifndef DETOURNAVMESHQUERY_H
|
||||
#define DETOURNAVMESHQUERY_H
|
||||
|
||||
#include "DetourNavMesh.h"
|
||||
#include "DetourStatus.h"
|
||||
|
||||
|
||||
// Define DT_VIRTUAL_QUERYFILTER if you wish to derive a custom filter from dtQueryFilter.
|
||||
// On certain platforms indirect or virtual function call is expensive. The default
|
||||
// setting is to use non-virtual functions, the actual implementations of the functions
|
||||
// are declared as inline for maximum speed.
|
||||
|
||||
//#define DT_VIRTUAL_QUERYFILTER 1
|
||||
|
||||
/// Defines polygon filtering and traversal costs for navigation mesh query operations.
|
||||
/// @ingroup detour
|
||||
class dtQueryFilter
|
||||
{
|
||||
float m_areaCost[DT_MAX_AREAS]; ///< Cost per area type. (Used by default implementation.)
|
||||
unsigned short m_includeFlags; ///< Flags for polygons that can be visited. (Used by default implementation.)
|
||||
unsigned short m_excludeFlags; ///< Flags for polygons that should not be visted. (Used by default implementation.)
|
||||
|
||||
public:
|
||||
dtQueryFilter();
|
||||
|
||||
#ifdef DT_VIRTUAL_QUERYFILTER
|
||||
virtual ~dtQueryFilter() { }
|
||||
#endif
|
||||
|
||||
/// Returns true if the polygon can be visited. (I.e. Is traversable.)
|
||||
/// @param[in] ref The reference id of the polygon test.
|
||||
/// @param[in] tile The tile containing the polygon.
|
||||
/// @param[in] poly The polygon to test.
|
||||
#ifdef DT_VIRTUAL_QUERYFILTER
|
||||
virtual bool passFilter(const dtPolyRef ref,
|
||||
const dtMeshTile* tile,
|
||||
const dtPoly* poly) const;
|
||||
#else
|
||||
bool passFilter(const dtPolyRef ref,
|
||||
const dtMeshTile* tile,
|
||||
const dtPoly* poly) const;
|
||||
#endif
|
||||
|
||||
/// Returns cost to move from the beginning to the end of a line segment
|
||||
/// that is fully contained within a polygon.
|
||||
/// @param[in] pa The start position on the edge of the previous and current polygon. [(x, y, z)]
|
||||
/// @param[in] pb The end position on the edge of the current and next polygon. [(x, y, z)]
|
||||
/// @param[in] prevRef The reference id of the previous polygon. [opt]
|
||||
/// @param[in] prevTile The tile containing the previous polygon. [opt]
|
||||
/// @param[in] prevPoly The previous polygon. [opt]
|
||||
/// @param[in] curRef The reference id of the current polygon.
|
||||
/// @param[in] curTile The tile containing the current polygon.
|
||||
/// @param[in] curPoly The current polygon.
|
||||
/// @param[in] nextRef The refernece id of the next polygon. [opt]
|
||||
/// @param[in] nextTile The tile containing the next polygon. [opt]
|
||||
/// @param[in] nextPoly The next polygon. [opt]
|
||||
#ifdef DT_VIRTUAL_QUERYFILTER
|
||||
virtual float getCost(const float* pa, const float* pb,
|
||||
const dtPolyRef prevRef, const dtMeshTile* prevTile, const dtPoly* prevPoly,
|
||||
const dtPolyRef curRef, const dtMeshTile* curTile, const dtPoly* curPoly,
|
||||
const dtPolyRef nextRef, const dtMeshTile* nextTile, const dtPoly* nextPoly) const;
|
||||
#else
|
||||
float getCost(const float* pa, const float* pb,
|
||||
const dtPolyRef prevRef, const dtMeshTile* prevTile, const dtPoly* prevPoly,
|
||||
const dtPolyRef curRef, const dtMeshTile* curTile, const dtPoly* curPoly,
|
||||
const dtPolyRef nextRef, const dtMeshTile* nextTile, const dtPoly* nextPoly) const;
|
||||
#endif
|
||||
|
||||
/// @name Getters and setters for the default implementation data.
|
||||
///@{
|
||||
|
||||
/// Returns the traversal cost of the area.
|
||||
/// @param[in] i The id of the area.
|
||||
/// @returns The traversal cost of the area.
|
||||
inline float getAreaCost(const int i) const { return m_areaCost[i]; }
|
||||
|
||||
/// Sets the traversal cost of the area.
|
||||
/// @param[in] i The id of the area.
|
||||
/// @param[in] cost The new cost of traversing the area.
|
||||
inline void setAreaCost(const int i, const float cost) { m_areaCost[i] = cost; }
|
||||
|
||||
/// Returns the include flags for the filter.
|
||||
/// Any polygons that include one or more of these flags will be
|
||||
/// included in the operation.
|
||||
inline unsigned short getIncludeFlags() const { return m_includeFlags; }
|
||||
|
||||
/// Sets the include flags for the filter.
|
||||
/// @param[in] flags The new flags.
|
||||
inline void setIncludeFlags(const unsigned short flags) { m_includeFlags = flags; }
|
||||
|
||||
/// Returns the exclude flags for the filter.
|
||||
/// Any polygons that include one ore more of these flags will be
|
||||
/// excluded from the operation.
|
||||
inline unsigned short getExcludeFlags() const { return m_excludeFlags; }
|
||||
|
||||
/// Sets the exclude flags for the filter.
|
||||
/// @param[in] flags The new flags.
|
||||
inline void setExcludeFlags(const unsigned short flags) { m_excludeFlags = flags; }
|
||||
|
||||
///@}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// Provides information about raycast hit
|
||||
/// filled by dtNavMeshQuery::raycast
|
||||
/// @ingroup detour
|
||||
struct dtRaycastHit
|
||||
{
|
||||
/// The hit parameter. (FLT_MAX if no wall hit.)
|
||||
float t;
|
||||
|
||||
/// hitNormal The normal of the nearest wall hit. [(x, y, z)]
|
||||
float hitNormal[3];
|
||||
|
||||
/// Pointer to an array of reference ids of the visited polygons. [opt]
|
||||
dtPolyRef* path;
|
||||
|
||||
/// The number of visited polygons. [opt]
|
||||
int pathCount;
|
||||
|
||||
/// The maximum number of polygons the @p path array can hold.
|
||||
int maxPath;
|
||||
|
||||
/// The cost of the path until hit.
|
||||
float pathCost;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// Provides the ability to perform pathfinding related queries against
|
||||
/// a navigation mesh.
|
||||
/// @ingroup detour
|
||||
class dtNavMeshQuery
|
||||
{
|
||||
public:
|
||||
dtNavMeshQuery();
|
||||
~dtNavMeshQuery();
|
||||
|
||||
/// Initializes the query object.
|
||||
/// @param[in] nav Pointer to the dtNavMesh object to use for all queries.
|
||||
/// @param[in] maxNodes Maximum number of search nodes. [Limits: 0 < value <= 65536]
|
||||
/// @returns The status flags for the query.
|
||||
dtStatus init(const dtNavMesh* nav, const int maxNodes);
|
||||
|
||||
/// @name Standard Pathfinding Functions
|
||||
// /@{
|
||||
|
||||
/// Finds a path from the start polygon to the end polygon.
|
||||
/// @param[in] startRef The refrence id of the start polygon.
|
||||
/// @param[in] endRef The reference id of the end polygon.
|
||||
/// @param[in] startPos A position within the start polygon. [(x, y, z)]
|
||||
/// @param[in] endPos A position within the end polygon. [(x, y, z)]
|
||||
/// @param[in] filter The polygon filter to apply to the query.
|
||||
/// @param[out] path An ordered list of polygon references representing the path. (Start to end.)
|
||||
/// [(polyRef) * @p pathCount]
|
||||
/// @param[out] pathCount The number of polygons returned in the @p path array.
|
||||
/// @param[in] maxPath The maximum number of polygons the @p path array can hold. [Limit: >= 1]
|
||||
dtStatus findPath(dtPolyRef startRef, dtPolyRef endRef,
|
||||
const float* startPos, const float* endPos,
|
||||
const dtQueryFilter* filter,
|
||||
dtPolyRef* path, int* pathCount, const int maxPath) const;
|
||||
|
||||
/// Finds the straight path from the start to the end position within the polygon corridor.
|
||||
/// @param[in] startPos Path start position. [(x, y, z)]
|
||||
/// @param[in] endPos Path end position. [(x, y, z)]
|
||||
/// @param[in] path An array of polygon references that represent the path corridor.
|
||||
/// @param[in] pathSize The number of polygons in the @p path array.
|
||||
/// @param[out] straightPath Points describing the straight path. [(x, y, z) * @p straightPathCount].
|
||||
/// @param[out] straightPathFlags Flags describing each point. (See: #dtStraightPathFlags) [opt]
|
||||
/// @param[out] straightPathRefs The reference id of the polygon that is being entered at each point. [opt]
|
||||
/// @param[out] straightPathCount The number of points in the straight path.
|
||||
/// @param[in] maxStraightPath The maximum number of points the straight path arrays can hold. [Limit: > 0]
|
||||
/// @param[in] options Query options. (see: #dtStraightPathOptions)
|
||||
/// @returns The status flags for the query.
|
||||
dtStatus findStraightPath(const float* startPos, const float* endPos,
|
||||
const dtPolyRef* path, const int pathSize,
|
||||
float* straightPath, unsigned char* straightPathFlags, dtPolyRef* straightPathRefs,
|
||||
int* straightPathCount, const int maxStraightPath, const int options = 0) const;
|
||||
|
||||
///@}
|
||||
/// @name Sliced Pathfinding Functions
|
||||
/// Common use case:
|
||||
/// -# Call initSlicedFindPath() to initialize the sliced path query.
|
||||
/// -# Call updateSlicedFindPath() until it returns complete.
|
||||
/// -# Call finalizeSlicedFindPath() to get the path.
|
||||
///@{
|
||||
|
||||
/// Intializes a sliced path query.
|
||||
/// @param[in] startRef The refrence id of the start polygon.
|
||||
/// @param[in] endRef The reference id of the end polygon.
|
||||
/// @param[in] startPos A position within the start polygon. [(x, y, z)]
|
||||
/// @param[in] endPos A position within the end polygon. [(x, y, z)]
|
||||
/// @param[in] filter The polygon filter to apply to the query.
|
||||
/// @param[in] options query options (see: #dtFindPathOptions)
|
||||
/// @returns The status flags for the query.
|
||||
dtStatus initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef,
|
||||
const float* startPos, const float* endPos,
|
||||
const dtQueryFilter* filter, const unsigned int options = 0);
|
||||
|
||||
/// Updates an in-progress sliced path query.
|
||||
/// @param[in] maxIter The maximum number of iterations to perform.
|
||||
/// @param[out] doneIters The actual number of iterations completed. [opt]
|
||||
/// @returns The status flags for the query.
|
||||
dtStatus updateSlicedFindPath(const int maxIter, int* doneIters);
|
||||
|
||||
/// Finalizes and returns the results of a sliced path query.
|
||||
/// @param[out] path An ordered list of polygon references representing the path. (Start to end.)
|
||||
/// [(polyRef) * @p pathCount]
|
||||
/// @param[out] pathCount The number of polygons returned in the @p path array.
|
||||
/// @param[in] maxPath The max number of polygons the path array can hold. [Limit: >= 1]
|
||||
/// @returns The status flags for the query.
|
||||
dtStatus finalizeSlicedFindPath(dtPolyRef* path, int* pathCount, const int maxPath);
|
||||
|
||||
/// Finalizes and returns the results of an incomplete sliced path query, returning the path to the furthest
|
||||
/// polygon on the existing path that was visited during the search.
|
||||
/// @param[in] existing An array of polygon references for the existing path.
|
||||
/// @param[in] existingSize The number of polygon in the @p existing array.
|
||||
/// @param[out] path An ordered list of polygon references representing the path. (Start to end.)
|
||||
/// [(polyRef) * @p pathCount]
|
||||
/// @param[out] pathCount The number of polygons returned in the @p path array.
|
||||
/// @param[in] maxPath The max number of polygons the @p path array can hold. [Limit: >= 1]
|
||||
/// @returns The status flags for the query.
|
||||
dtStatus finalizeSlicedFindPathPartial(const dtPolyRef* existing, const int existingSize,
|
||||
dtPolyRef* path, int* pathCount, const int maxPath);
|
||||
|
||||
///@}
|
||||
/// @name Dijkstra Search Functions
|
||||
/// @{
|
||||
|
||||
/// Finds the polygons along the navigation graph that touch the specified circle.
|
||||
/// @param[in] startRef The reference id of the polygon where the search starts.
|
||||
/// @param[in] centerPos The center of the search circle. [(x, y, z)]
|
||||
/// @param[in] radius The radius of the search circle.
|
||||
/// @param[in] filter The polygon filter to apply to the query.
|
||||
/// @param[out] resultRef The reference ids of the polygons touched by the circle. [opt]
|
||||
/// @param[out] resultParent The reference ids of the parent polygons for each result.
|
||||
/// Zero if a result polygon has no parent. [opt]
|
||||
/// @param[out] resultCost The search cost from @p centerPos to the polygon. [opt]
|
||||
/// @param[out] resultCount The number of polygons found. [opt]
|
||||
/// @param[in] maxResult The maximum number of polygons the result arrays can hold.
|
||||
/// @returns The status flags for the query.
|
||||
dtStatus findPolysAroundCircle(dtPolyRef startRef, const float* centerPos, const float radius,
|
||||
const dtQueryFilter* filter,
|
||||
dtPolyRef* resultRef, dtPolyRef* resultParent, float* resultCost,
|
||||
int* resultCount, const int maxResult) const;
|
||||
|
||||
/// Finds the polygons along the naviation graph that touch the specified convex polygon.
|
||||
/// @param[in] startRef The reference id of the polygon where the search starts.
|
||||
/// @param[in] verts The vertices describing the convex polygon. (CCW)
|
||||
/// [(x, y, z) * @p nverts]
|
||||
/// @param[in] nverts The number of vertices in the polygon.
|
||||
/// @param[in] filter The polygon filter to apply to the query.
|
||||
/// @param[out] resultRef The reference ids of the polygons touched by the search polygon. [opt]
|
||||
/// @param[out] resultParent The reference ids of the parent polygons for each result. Zero if a
|
||||
/// result polygon has no parent. [opt]
|
||||
/// @param[out] resultCost The search cost from the centroid point to the polygon. [opt]
|
||||
/// @param[out] resultCount The number of polygons found.
|
||||
/// @param[in] maxResult The maximum number of polygons the result arrays can hold.
|
||||
/// @returns The status flags for the query.
|
||||
dtStatus findPolysAroundShape(dtPolyRef startRef, const float* verts, const int nverts,
|
||||
const dtQueryFilter* filter,
|
||||
dtPolyRef* resultRef, dtPolyRef* resultParent, float* resultCost,
|
||||
int* resultCount, const int maxResult) const;
|
||||
|
||||
/// @}
|
||||
/// @name Local Query Functions
|
||||
///@{
|
||||
|
||||
/// Finds the polygon nearest to the specified center point.
|
||||
/// @param[in] center The center of the search box. [(x, y, z)]
|
||||
/// @param[in] extents The search distance along each axis. [(x, y, z)]
|
||||
/// @param[in] filter The polygon filter to apply to the query.
|
||||
/// @param[out] nearestRef The reference id of the nearest polygon.
|
||||
/// @param[out] nearestPt The nearest point on the polygon. [opt] [(x, y, z)]
|
||||
/// @returns The status flags for the query.
|
||||
dtStatus findNearestPoly(const float* center, const float* extents,
|
||||
const dtQueryFilter* filter,
|
||||
dtPolyRef* nearestRef, float* nearestPt) const;
|
||||
|
||||
/// Finds polygons that overlap the search box.
|
||||
/// @param[in] center The center of the search box. [(x, y, z)]
|
||||
/// @param[in] extents The search distance along each axis. [(x, y, z)]
|
||||
/// @param[in] 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,
|
||||
const dtQueryFilter* filter,
|
||||
dtPolyRef* polys, int* polyCount, const int maxPolys) const;
|
||||
|
||||
/// Finds the non-overlapping navigation polygons in the local neighbourhood around the center position.
|
||||
/// @param[in] startRef The reference id of the polygon where the search starts.
|
||||
/// @param[in] centerPos The center of the query circle. [(x, y, z)]
|
||||
/// @param[in] radius The radius of the query circle.
|
||||
/// @param[in] filter The polygon filter to apply to the query.
|
||||
/// @param[out] resultRef The reference ids of the polygons touched by the circle.
|
||||
/// @param[out] resultParent The reference ids of the parent polygons for each result.
|
||||
/// Zero if a result polygon has no parent. [opt]
|
||||
/// @param[out] resultCount The number of polygons found.
|
||||
/// @param[in] maxResult The maximum number of polygons the result arrays can hold.
|
||||
/// @returns The status flags for the query.
|
||||
dtStatus findLocalNeighbourhood(dtPolyRef startRef, const float* centerPos, const float radius,
|
||||
const dtQueryFilter* filter,
|
||||
dtPolyRef* resultRef, dtPolyRef* resultParent,
|
||||
int* resultCount, const int maxResult) const;
|
||||
|
||||
/// Moves from the start to the end position constrained to the navigation mesh.
|
||||
/// @param[in] startRef The reference id of the start polygon.
|
||||
/// @param[in] startPos A position of the mover within the start polygon. [(x, y, x)]
|
||||
/// @param[in] endPos The desired end position of the mover. [(x, y, z)]
|
||||
/// @param[in] filter The polygon filter to apply to the query.
|
||||
/// @param[out] resultPos The result position of the mover. [(x, y, z)]
|
||||
/// @param[out] visited The reference ids of the polygons visited during the move.
|
||||
/// @param[out] visitedCount The number of polygons visited during the move.
|
||||
/// @param[in] maxVisitedSize The maximum number of polygons the @p visited array can hold.
|
||||
/// @returns The status flags for the query.
|
||||
dtStatus moveAlongSurface(dtPolyRef startRef, const float* startPos, const float* endPos,
|
||||
const dtQueryFilter* filter,
|
||||
float* resultPos, dtPolyRef* visited, int* visitedCount, const int maxVisitedSize) const;
|
||||
|
||||
/// Casts a 'walkability' ray along the surface of the navigation mesh from
|
||||
/// the start position toward the end position.
|
||||
/// @note A wrapper around raycast(..., RaycastHit*). Retained for backward compatibility.
|
||||
/// @param[in] startRef The reference id of the start polygon.
|
||||
/// @param[in] startPos A position within the start polygon representing
|
||||
/// the start of the ray. [(x, y, z)]
|
||||
/// @param[in] endPos The position to cast the ray toward. [(x, y, z)]
|
||||
/// @param[out] t The hit parameter. (FLT_MAX if no wall hit.)
|
||||
/// @param[out] hitNormal The normal of the nearest wall hit. [(x, y, z)]
|
||||
/// @param[in] filter The polygon filter to apply to the query.
|
||||
/// @param[out] path The reference ids of the visited polygons. [opt]
|
||||
/// @param[out] pathCount The number of visited polygons. [opt]
|
||||
/// @param[in] maxPath The maximum number of polygons the @p path array can hold.
|
||||
/// @returns The status flags for the query.
|
||||
dtStatus raycast(dtPolyRef startRef, const float* startPos, const float* endPos,
|
||||
const dtQueryFilter* filter,
|
||||
float* t, float* hitNormal, dtPolyRef* path, int* pathCount, const int maxPath) const;
|
||||
|
||||
/// Casts a 'walkability' ray along the surface of the navigation mesh from
|
||||
/// the start position toward the end position.
|
||||
/// @param[in] startRef The reference id of the start polygon.
|
||||
/// @param[in] startPos A position within the start polygon representing
|
||||
/// the start of the ray. [(x, y, z)]
|
||||
/// @param[in] endPos The position to cast the ray toward. [(x, y, z)]
|
||||
/// @param[in] filter The polygon filter to apply to the query.
|
||||
/// @param[in] flags govern how the raycast behaves. See dtRaycastOptions
|
||||
/// @param[out] hit Pointer to a raycast hit structure which will be filled by the results.
|
||||
/// @param[in] prevRef parent of start ref. Used during for cost calculation [opt]
|
||||
/// @returns The status flags for the query.
|
||||
dtStatus raycast(dtPolyRef startRef, const float* startPos, const float* endPos,
|
||||
const dtQueryFilter* filter, const unsigned int options,
|
||||
dtRaycastHit* hit, dtPolyRef prevRef = 0) const;
|
||||
|
||||
|
||||
/// Finds the distance from the specified position to the nearest polygon wall.
|
||||
/// @param[in] startRef The reference id of the polygon containing @p centerPos.
|
||||
/// @param[in] centerPos The center of the search circle. [(x, y, z)]
|
||||
/// @param[in] maxRadius The radius of the search circle.
|
||||
/// @param[in] filter The polygon filter to apply to the query.
|
||||
/// @param[out] hitDist The distance to the nearest wall from @p centerPos.
|
||||
/// @param[out] hitPos The nearest position on the wall that was hit. [(x, y, z)]
|
||||
/// @param[out] hitNormal The normalized ray formed from the wall point to the
|
||||
/// source point. [(x, y, z)]
|
||||
/// @returns The status flags for the query.
|
||||
dtStatus findDistanceToWall(dtPolyRef startRef, const float* centerPos, const float maxRadius,
|
||||
const dtQueryFilter* filter,
|
||||
float* hitDist, float* hitPos, float* hitNormal) const;
|
||||
|
||||
/// Returns the segments for the specified polygon, optionally including portals.
|
||||
/// @param[in] ref The reference id of the polygon.
|
||||
/// @param[in] filter The polygon filter to apply to the query.
|
||||
/// @param[out] segmentVerts The segments. [(ax, ay, az, bx, by, bz) * segmentCount]
|
||||
/// @param[out] segmentRefs The reference ids of each segment's neighbor polygon.
|
||||
/// Or zero if the segment is a wall. [opt] [(parentRef) * @p segmentCount]
|
||||
/// @param[out] segmentCount The number of segments returned.
|
||||
/// @param[in] maxSegments The maximum number of segments the result arrays can hold.
|
||||
/// @returns The status flags for the query.
|
||||
dtStatus getPolyWallSegments(dtPolyRef ref, const dtQueryFilter* filter,
|
||||
float* segmentVerts, dtPolyRef* segmentRefs, int* segmentCount,
|
||||
const int maxSegments) const;
|
||||
|
||||
/// Returns random location on navmesh.
|
||||
/// Polygons are chosen weighted by area. The search runs in linear related to number of polygon.
|
||||
/// @param[in] filter The polygon filter to apply to the query.
|
||||
/// @param[in] frand Function returning a random number [0..1).
|
||||
/// @param[out] randomRef The reference id of the random location.
|
||||
/// @param[out] randomPt The random location.
|
||||
/// @returns The status flags for the query.
|
||||
dtStatus findRandomPoint(const dtQueryFilter* filter, float (*frand)(),
|
||||
dtPolyRef* randomRef, float* randomPt) const;
|
||||
|
||||
/// Returns random location on navmesh within the reach of specified location.
|
||||
/// Polygons are chosen weighted by area. The search runs in linear related to number of polygon.
|
||||
/// The location is not exactly constrained by the circle, but it limits the visited polygons.
|
||||
/// @param[in] startRef The reference id of the polygon where the search starts.
|
||||
/// @param[in] centerPos The center of the search circle. [(x, y, z)]
|
||||
/// @param[in] filter The polygon filter to apply to the query.
|
||||
/// @param[in] frand Function returning a random number [0..1).
|
||||
/// @param[out] randomRef The reference id of the random location.
|
||||
/// @param[out] randomPt The random location. [(x, y, z)]
|
||||
/// @returns The status flags for the query.
|
||||
dtStatus findRandomPointAroundCircle(dtPolyRef startRef, const float* centerPos, const float maxRadius,
|
||||
const dtQueryFilter* filter, float (*frand)(),
|
||||
dtPolyRef* randomRef, float* randomPt) const;
|
||||
|
||||
/// Finds the closest point on the specified polygon.
|
||||
/// @param[in] ref The reference id of the polygon.
|
||||
/// @param[in] pos The position to check. [(x, y, z)]
|
||||
/// @param[out] closest The closest point on the polygon. [(x, y, z)]
|
||||
/// @param[out] posOverPoly True of the position is over the polygon.
|
||||
/// @returns The status flags for the query.
|
||||
dtStatus closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const;
|
||||
|
||||
/// Returns a point on the boundary closest to the source point if the source point is outside the
|
||||
/// polygon's xz-bounds.
|
||||
/// @param[in] ref The reference id to the polygon.
|
||||
/// @param[in] pos The position to check. [(x, y, z)]
|
||||
/// @param[out] closest The closest point. [(x, y, z)]
|
||||
/// @returns The status flags for the query.
|
||||
dtStatus closestPointOnPolyBoundary(dtPolyRef ref, const float* pos, float* closest) const;
|
||||
|
||||
/// Gets the height of the polygon at the provided position using the height detail. (Most accurate.)
|
||||
/// @param[in] ref The reference id of the polygon.
|
||||
/// @param[in] pos A position within the xz-bounds of the polygon. [(x, y, z)]
|
||||
/// @param[out] height The height at the surface of the polygon.
|
||||
/// @returns The status flags for the query.
|
||||
dtStatus getPolyHeight(dtPolyRef ref, const float* pos, float* height) const;
|
||||
|
||||
/// @}
|
||||
/// @name Miscellaneous Functions
|
||||
/// @{
|
||||
|
||||
/// Returns true if the polygon reference is valid and passes the filter restrictions.
|
||||
/// @param[in] ref The polygon reference to check.
|
||||
/// @param[in] filter The filter to apply.
|
||||
bool isValidPolyRef(dtPolyRef ref, const dtQueryFilter* filter) const;
|
||||
|
||||
/// Returns true if the polygon reference is in the closed list.
|
||||
/// @param[in] ref The reference id of the polygon to check.
|
||||
/// @returns True if the polygon is in closed list.
|
||||
bool isInClosedList(dtPolyRef ref) const;
|
||||
|
||||
/// Gets the node pool.
|
||||
/// @returns The node pool.
|
||||
class dtNodePool* getNodePool() const { return m_nodePool; }
|
||||
|
||||
/// Gets the navigation mesh the query object is using.
|
||||
/// @return The navigation mesh the query object is using.
|
||||
const dtNavMesh* getAttachedNavMesh() const { return m_nav; }
|
||||
|
||||
/// @}
|
||||
|
||||
private:
|
||||
|
||||
/// Returns neighbour tile based on side.
|
||||
dtMeshTile* getNeighbourTileAt(int x, int y, int side) const;
|
||||
|
||||
/// Queries polygons within a tile.
|
||||
int queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, const float* qmax, const dtQueryFilter* filter,
|
||||
dtPolyRef* polys, const int maxPolys) const;
|
||||
|
||||
/// Returns portal points between two polygons.
|
||||
dtStatus getPortalPoints(dtPolyRef from, dtPolyRef to, float* left, float* right,
|
||||
unsigned char& fromType, unsigned char& toType) const;
|
||||
dtStatus getPortalPoints(dtPolyRef from, const dtPoly* fromPoly, const dtMeshTile* fromTile,
|
||||
dtPolyRef to, const dtPoly* toPoly, const dtMeshTile* toTile,
|
||||
float* left, float* right) const;
|
||||
|
||||
/// Returns edge mid point between two polygons.
|
||||
dtStatus getEdgeMidPoint(dtPolyRef from, dtPolyRef to, float* mid) const;
|
||||
dtStatus getEdgeMidPoint(dtPolyRef from, const dtPoly* fromPoly, const dtMeshTile* fromTile,
|
||||
dtPolyRef to, const dtPoly* toPoly, const dtMeshTile* toTile,
|
||||
float* mid) const;
|
||||
|
||||
// Appends vertex to a straight path
|
||||
dtStatus appendVertex(const float* pos, const unsigned char flags, const dtPolyRef ref,
|
||||
float* straightPath, unsigned char* straightPathFlags, dtPolyRef* straightPathRefs,
|
||||
int* straightPathCount, const int maxStraightPath) const;
|
||||
|
||||
// Appends intermediate portal points to a straight path.
|
||||
dtStatus appendPortals(const int startIdx, const int endIdx, const float* endPos, const dtPolyRef* path,
|
||||
float* straightPath, unsigned char* straightPathFlags, dtPolyRef* straightPathRefs,
|
||||
int* straightPathCount, const int maxStraightPath, const int options) const;
|
||||
|
||||
const dtNavMesh* m_nav; ///< Pointer to navmesh data.
|
||||
|
||||
struct dtQueryData
|
||||
{
|
||||
dtStatus status;
|
||||
struct dtNode* lastBestNode;
|
||||
float lastBestNodeCost;
|
||||
dtPolyRef startRef, endRef;
|
||||
float startPos[3], endPos[3];
|
||||
const dtQueryFilter* filter;
|
||||
unsigned int options;
|
||||
float raycastLimitSqr;
|
||||
};
|
||||
dtQueryData m_query; ///< Sliced query state.
|
||||
|
||||
class dtNodePool* m_tinyNodePool; ///< Pointer to small node pool.
|
||||
class dtNodePool* m_nodePool; ///< Pointer to node pool.
|
||||
class dtNodeQueue* m_openList; ///< Pointer to open list queue.
|
||||
};
|
||||
|
||||
/// Allocates a query object using the Detour allocator.
|
||||
/// @return An allocated query object, or null on failure.
|
||||
/// @ingroup detour
|
||||
dtNavMeshQuery* dtAllocNavMeshQuery();
|
||||
|
||||
/// Frees the specified query object using the Detour allocator.
|
||||
/// @param[in] query A query object allocated using #dtAllocNavMeshQuery
|
||||
/// @ingroup detour
|
||||
void dtFreeNavMeshQuery(dtNavMeshQuery* query);
|
||||
|
||||
#endif // DETOURNAVMESHQUERY_H
|
||||
170
recast/detour/include/DetourNode.h
Normal file
170
recast/detour/include/DetourNode.h
Normal file
@ -0,0 +1,170 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#ifndef DETOURNODE_H
|
||||
#define DETOURNODE_H
|
||||
|
||||
#include "DetourNavMesh.h"
|
||||
|
||||
enum dtNodeFlags
|
||||
{
|
||||
DT_NODE_OPEN = 0x01,
|
||||
DT_NODE_CLOSED = 0x02,
|
||||
DT_NODE_PARENT_DETACHED = 0x04, // parent of the node is not adjacent. Found using raycast.
|
||||
};
|
||||
|
||||
typedef unsigned short dtNodeIndex;
|
||||
static const dtNodeIndex DT_NULL_IDX = (dtNodeIndex)~0;
|
||||
|
||||
struct dtNode
|
||||
{
|
||||
float pos[3]; ///< Position of the node.
|
||||
float cost; ///< Cost from previous node to current node.
|
||||
float total; ///< Cost up to the node.
|
||||
unsigned int pidx : 24; ///< Index to parent node.
|
||||
unsigned int state : 2; ///< extra state information. A polyRef can have multiple nodes with different extra info. see DT_MAX_STATES_PER_NODE
|
||||
unsigned int flags : 3; ///< Node flags. A combination of dtNodeFlags.
|
||||
dtPolyRef id; ///< Polygon ref the node corresponds to.
|
||||
};
|
||||
|
||||
|
||||
static const int DT_MAX_STATES_PER_NODE = 4; // number of extra states per node. See dtNode::state
|
||||
|
||||
|
||||
|
||||
class dtNodePool
|
||||
{
|
||||
public:
|
||||
dtNodePool(int maxNodes, int hashSize);
|
||||
~dtNodePool();
|
||||
inline void operator=(const dtNodePool&) {}
|
||||
void clear();
|
||||
|
||||
// Get a dtNode by ref and extra state information. If there is none then - allocate
|
||||
// There can be more than one node for the same polyRef but with different extra state information
|
||||
dtNode* getNode(dtPolyRef id, unsigned char state=0);
|
||||
dtNode* findNode(dtPolyRef id, unsigned char state);
|
||||
unsigned int findNodes(dtPolyRef id, dtNode** nodes, const int maxNodes);
|
||||
|
||||
inline unsigned int getNodeIdx(const dtNode* node) const
|
||||
{
|
||||
if (!node) return 0;
|
||||
return (unsigned int)(node - m_nodes)+1;
|
||||
}
|
||||
|
||||
inline dtNode* getNodeAtIdx(unsigned int idx)
|
||||
{
|
||||
if (!idx) return 0;
|
||||
return &m_nodes[idx-1];
|
||||
}
|
||||
|
||||
inline const dtNode* getNodeAtIdx(unsigned int idx) const
|
||||
{
|
||||
if (!idx) return 0;
|
||||
return &m_nodes[idx-1];
|
||||
}
|
||||
|
||||
inline int getMemUsed() const
|
||||
{
|
||||
return sizeof(*this) +
|
||||
sizeof(dtNode)*m_maxNodes +
|
||||
sizeof(dtNodeIndex)*m_maxNodes +
|
||||
sizeof(dtNodeIndex)*m_hashSize;
|
||||
}
|
||||
|
||||
inline int getMaxNodes() const { return m_maxNodes; }
|
||||
|
||||
inline int getHashSize() const { return m_hashSize; }
|
||||
inline dtNodeIndex getFirst(int bucket) const { return m_first[bucket]; }
|
||||
inline dtNodeIndex getNext(int i) const { return m_next[i]; }
|
||||
inline int getNodeCount() const { return m_nodeCount; }
|
||||
|
||||
private:
|
||||
|
||||
dtNode* m_nodes;
|
||||
dtNodeIndex* m_first;
|
||||
dtNodeIndex* m_next;
|
||||
const int m_maxNodes;
|
||||
const int m_hashSize;
|
||||
int m_nodeCount;
|
||||
};
|
||||
|
||||
class dtNodeQueue
|
||||
{
|
||||
public:
|
||||
dtNodeQueue(int n);
|
||||
~dtNodeQueue();
|
||||
inline void operator=(dtNodeQueue&) {}
|
||||
|
||||
inline void clear()
|
||||
{
|
||||
m_size = 0;
|
||||
}
|
||||
|
||||
inline dtNode* top()
|
||||
{
|
||||
return m_heap[0];
|
||||
}
|
||||
|
||||
inline dtNode* pop()
|
||||
{
|
||||
dtNode* result = m_heap[0];
|
||||
m_size--;
|
||||
trickleDown(0, m_heap[m_size]);
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void push(dtNode* node)
|
||||
{
|
||||
m_size++;
|
||||
bubbleUp(m_size-1, node);
|
||||
}
|
||||
|
||||
inline void modify(dtNode* node)
|
||||
{
|
||||
for (int i = 0; i < m_size; ++i)
|
||||
{
|
||||
if (m_heap[i] == node)
|
||||
{
|
||||
bubbleUp(i, node);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline bool empty() const { return m_size == 0; }
|
||||
|
||||
inline int getMemUsed() const
|
||||
{
|
||||
return sizeof(*this) +
|
||||
sizeof(dtNode*)*(m_capacity+1);
|
||||
}
|
||||
|
||||
inline int getCapacity() const { return m_capacity; }
|
||||
|
||||
private:
|
||||
void bubbleUp(int i, dtNode* node);
|
||||
void trickleDown(int i, dtNode* node);
|
||||
|
||||
dtNode** m_heap;
|
||||
const int m_capacity;
|
||||
int m_size;
|
||||
};
|
||||
|
||||
|
||||
#endif // DETOURNODE_H
|
||||
64
recast/detour/include/DetourStatus.h
Normal file
64
recast/detour/include/DetourStatus.h
Normal file
@ -0,0 +1,64 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#ifndef DETOURSTATUS_H
|
||||
#define DETOURSTATUS_H
|
||||
|
||||
typedef unsigned int dtStatus;
|
||||
|
||||
// High level status.
|
||||
static const unsigned int DT_FAILURE = 1u << 31; // Operation failed.
|
||||
static const unsigned int DT_SUCCESS = 1u << 30; // Operation succeed.
|
||||
static const unsigned int DT_IN_PROGRESS = 1u << 29; // Operation still in progress.
|
||||
|
||||
// Detail information for status.
|
||||
static const unsigned int DT_STATUS_DETAIL_MASK = 0x0ffffff;
|
||||
static const unsigned int DT_WRONG_MAGIC = 1 << 0; // Input data is not recognized.
|
||||
static const unsigned int DT_WRONG_VERSION = 1 << 1; // Input data is in wrong version.
|
||||
static const unsigned int DT_OUT_OF_MEMORY = 1 << 2; // Operation ran out of memory.
|
||||
static const unsigned int DT_INVALID_PARAM = 1 << 3; // An input parameter was invalid.
|
||||
static const unsigned int DT_BUFFER_TOO_SMALL = 1 << 4; // Result buffer for the query was too small to store all results.
|
||||
static const unsigned int DT_OUT_OF_NODES = 1 << 5; // Query ran out of nodes during search.
|
||||
static const unsigned int DT_PARTIAL_RESULT = 1 << 6; // Query did not reach the end location, returning best guess.
|
||||
|
||||
|
||||
// Returns true of status is success.
|
||||
inline bool dtStatusSucceed(dtStatus status)
|
||||
{
|
||||
return (status & DT_SUCCESS) != 0;
|
||||
}
|
||||
|
||||
// Returns true of status is failure.
|
||||
inline bool dtStatusFailed(dtStatus status)
|
||||
{
|
||||
return (status & DT_FAILURE) != 0;
|
||||
}
|
||||
|
||||
// Returns true of status is in progress.
|
||||
inline bool dtStatusInProgress(dtStatus status)
|
||||
{
|
||||
return (status & DT_IN_PROGRESS) != 0;
|
||||
}
|
||||
|
||||
// Returns true if specific detail is set.
|
||||
inline bool dtStatusDetail(dtStatus status, unsigned int detail)
|
||||
{
|
||||
return (status & detail) != 0;
|
||||
}
|
||||
|
||||
#endif // DETOURSTATUS_H
|
||||
50
recast/detour/src/DetourAlloc.cpp
Normal file
50
recast/detour/src/DetourAlloc.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "DetourAlloc.h"
|
||||
|
||||
static void *dtAllocDefault(int size, dtAllocHint)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
static void dtFreeDefault(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
static dtAllocFunc* sAllocFunc = dtAllocDefault;
|
||||
static dtFreeFunc* sFreeFunc = dtFreeDefault;
|
||||
|
||||
void dtAllocSetCustom(dtAllocFunc *allocFunc, dtFreeFunc *freeFunc)
|
||||
{
|
||||
sAllocFunc = allocFunc ? allocFunc : dtAllocDefault;
|
||||
sFreeFunc = freeFunc ? freeFunc : dtFreeDefault;
|
||||
}
|
||||
|
||||
void* dtAlloc(int size, dtAllocHint hint)
|
||||
{
|
||||
return sAllocFunc(size, hint);
|
||||
}
|
||||
|
||||
void dtFree(void* ptr)
|
||||
{
|
||||
if (ptr)
|
||||
sFreeFunc(ptr);
|
||||
}
|
||||
388
recast/detour/src/DetourCommon.cpp
Normal file
388
recast/detour/src/DetourCommon.cpp
Normal file
@ -0,0 +1,388 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#include "DetourCommon.h"
|
||||
#include "DetourMath.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void dtClosestPtPointTriangle(float* closest, const float* p,
|
||||
const float* a, const float* b, const float* c)
|
||||
{
|
||||
// Check if P in vertex region outside A
|
||||
float ab[3], ac[3], ap[3];
|
||||
dtVsub(ab, b, a);
|
||||
dtVsub(ac, c, a);
|
||||
dtVsub(ap, p, a);
|
||||
float d1 = dtVdot(ab, ap);
|
||||
float d2 = dtVdot(ac, ap);
|
||||
if (d1 <= 0.0f && d2 <= 0.0f)
|
||||
{
|
||||
// barycentric coordinates (1,0,0)
|
||||
dtVcopy(closest, a);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if P in vertex region outside B
|
||||
float bp[3];
|
||||
dtVsub(bp, p, b);
|
||||
float d3 = dtVdot(ab, bp);
|
||||
float d4 = dtVdot(ac, bp);
|
||||
if (d3 >= 0.0f && d4 <= d3)
|
||||
{
|
||||
// barycentric coordinates (0,1,0)
|
||||
dtVcopy(closest, b);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if P in edge region of AB, if so return projection of P onto AB
|
||||
float vc = d1*d4 - d3*d2;
|
||||
if (vc <= 0.0f && d1 >= 0.0f && d3 <= 0.0f)
|
||||
{
|
||||
// barycentric coordinates (1-v,v,0)
|
||||
float v = d1 / (d1 - d3);
|
||||
closest[0] = a[0] + v * ab[0];
|
||||
closest[1] = a[1] + v * ab[1];
|
||||
closest[2] = a[2] + v * ab[2];
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if P in vertex region outside C
|
||||
float cp[3];
|
||||
dtVsub(cp, p, c);
|
||||
float d5 = dtVdot(ab, cp);
|
||||
float d6 = dtVdot(ac, cp);
|
||||
if (d6 >= 0.0f && d5 <= d6)
|
||||
{
|
||||
// barycentric coordinates (0,0,1)
|
||||
dtVcopy(closest, c);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if P in edge region of AC, if so return projection of P onto AC
|
||||
float vb = d5*d2 - d1*d6;
|
||||
if (vb <= 0.0f && d2 >= 0.0f && d6 <= 0.0f)
|
||||
{
|
||||
// barycentric coordinates (1-w,0,w)
|
||||
float w = d2 / (d2 - d6);
|
||||
closest[0] = a[0] + w * ac[0];
|
||||
closest[1] = a[1] + w * ac[1];
|
||||
closest[2] = a[2] + w * ac[2];
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if P in edge region of BC, if so return projection of P onto BC
|
||||
float va = d3*d6 - d5*d4;
|
||||
if (va <= 0.0f && (d4 - d3) >= 0.0f && (d5 - d6) >= 0.0f)
|
||||
{
|
||||
// barycentric coordinates (0,1-w,w)
|
||||
float w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
|
||||
closest[0] = b[0] + w * (c[0] - b[0]);
|
||||
closest[1] = b[1] + w * (c[1] - b[1]);
|
||||
closest[2] = b[2] + w * (c[2] - b[2]);
|
||||
return;
|
||||
}
|
||||
|
||||
// P inside face region. Compute Q through its barycentric coordinates (u,v,w)
|
||||
float denom = 1.0f / (va + vb + vc);
|
||||
float v = vb * denom;
|
||||
float w = vc * denom;
|
||||
closest[0] = a[0] + ab[0] * v + ac[0] * w;
|
||||
closest[1] = a[1] + ab[1] * v + ac[1] * w;
|
||||
closest[2] = a[2] + ab[2] * v + ac[2] * w;
|
||||
}
|
||||
|
||||
bool dtIntersectSegmentPoly2D(const float* p0, const float* p1,
|
||||
const float* verts, int nverts,
|
||||
float& tmin, float& tmax,
|
||||
int& segMin, int& segMax)
|
||||
{
|
||||
static const float EPS = 0.00000001f;
|
||||
|
||||
tmin = 0;
|
||||
tmax = 1;
|
||||
segMin = -1;
|
||||
segMax = -1;
|
||||
|
||||
float dir[3];
|
||||
dtVsub(dir, p1, p0);
|
||||
|
||||
for (int i = 0, j = nverts-1; i < nverts; j=i++)
|
||||
{
|
||||
float edge[3], diff[3];
|
||||
dtVsub(edge, &verts[i*3], &verts[j*3]);
|
||||
dtVsub(diff, p0, &verts[j*3]);
|
||||
const float n = dtVperp2D(edge, diff);
|
||||
const float d = dtVperp2D(dir, edge);
|
||||
if (fabsf(d) < EPS)
|
||||
{
|
||||
// S is nearly parallel to this edge
|
||||
if (n < 0)
|
||||
return false;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
const float t = n / d;
|
||||
if (d < 0)
|
||||
{
|
||||
// segment S is entering across this edge
|
||||
if (t > tmin)
|
||||
{
|
||||
tmin = t;
|
||||
segMin = j;
|
||||
// S enters after leaving polygon
|
||||
if (tmin > tmax)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// segment S is leaving across this edge
|
||||
if (t < tmax)
|
||||
{
|
||||
tmax = t;
|
||||
segMax = j;
|
||||
// S leaves before entering polygon
|
||||
if (tmax < tmin)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
float dtDistancePtSegSqr2D(const float* pt, const float* p, const float* q, float& t)
|
||||
{
|
||||
float pqx = q[0] - p[0];
|
||||
float pqz = q[2] - p[2];
|
||||
float dx = pt[0] - p[0];
|
||||
float dz = pt[2] - p[2];
|
||||
float d = pqx*pqx + pqz*pqz;
|
||||
t = pqx*dx + pqz*dz;
|
||||
if (d > 0) t /= d;
|
||||
if (t < 0) t = 0;
|
||||
else if (t > 1) t = 1;
|
||||
dx = p[0] + t*pqx - pt[0];
|
||||
dz = p[2] + t*pqz - pt[2];
|
||||
return dx*dx + dz*dz;
|
||||
}
|
||||
|
||||
void dtCalcPolyCenter(float* tc, const unsigned short* idx, int nidx, const float* verts)
|
||||
{
|
||||
tc[0] = 0.0f;
|
||||
tc[1] = 0.0f;
|
||||
tc[2] = 0.0f;
|
||||
for (int j = 0; j < nidx; ++j)
|
||||
{
|
||||
const float* v = &verts[idx[j]*3];
|
||||
tc[0] += v[0];
|
||||
tc[1] += v[1];
|
||||
tc[2] += v[2];
|
||||
}
|
||||
const float s = 1.0f / nidx;
|
||||
tc[0] *= s;
|
||||
tc[1] *= s;
|
||||
tc[2] *= s;
|
||||
}
|
||||
|
||||
bool dtClosestHeightPointTriangle(const float* p, const float* a, const float* b, const float* c, float& h)
|
||||
{
|
||||
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;
|
||||
|
||||
// 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;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// All points are projected onto the xz-plane, so the y-values are ignored.
|
||||
bool dtPointInPolygon(const float* pt, const float* verts, const int nverts)
|
||||
{
|
||||
// TODO: Replace pnpoly with triArea2D tests?
|
||||
int i, j;
|
||||
bool c = false;
|
||||
for (i = 0, j = nverts-1; i < nverts; j = i++)
|
||||
{
|
||||
const float* vi = &verts[i*3];
|
||||
const float* vj = &verts[j*3];
|
||||
if (((vi[2] > pt[2]) != (vj[2] > pt[2])) &&
|
||||
(pt[0] < (vj[0]-vi[0]) * (pt[2]-vi[2]) / (vj[2]-vi[2]) + vi[0]) )
|
||||
c = !c;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
bool dtDistancePtPolyEdgesSqr(const float* pt, const float* verts, const int nverts,
|
||||
float* ed, float* et)
|
||||
{
|
||||
// TODO: Replace pnpoly with triArea2D tests?
|
||||
int i, j;
|
||||
bool c = false;
|
||||
for (i = 0, j = nverts-1; i < nverts; j = i++)
|
||||
{
|
||||
const float* vi = &verts[i*3];
|
||||
const float* vj = &verts[j*3];
|
||||
if (((vi[2] > pt[2]) != (vj[2] > pt[2])) &&
|
||||
(pt[0] < (vj[0]-vi[0]) * (pt[2]-vi[2]) / (vj[2]-vi[2]) + vi[0]) )
|
||||
c = !c;
|
||||
ed[j] = dtDistancePtSegSqr2D(pt, vj, vi, et[j]);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
static void projectPoly(const float* axis, const float* poly, const int npoly,
|
||||
float& rmin, float& rmax)
|
||||
{
|
||||
rmin = rmax = dtVdot2D(axis, &poly[0]);
|
||||
for (int i = 1; i < npoly; ++i)
|
||||
{
|
||||
const float d = dtVdot2D(axis, &poly[i*3]);
|
||||
rmin = dtMin(rmin, d);
|
||||
rmax = dtMax(rmax, d);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool overlapRange(const float amin, const float amax,
|
||||
const float bmin, const float bmax,
|
||||
const float eps)
|
||||
{
|
||||
return ((amin+eps) > bmax || (amax-eps) < bmin) ? false : true;
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// All vertices are projected onto the xz-plane, so the y-values are ignored.
|
||||
bool dtOverlapPolyPoly2D(const float* polya, const int npolya,
|
||||
const float* polyb, const int npolyb)
|
||||
{
|
||||
const float eps = 1e-4f;
|
||||
|
||||
for (int i = 0, j = npolya-1; i < npolya; j=i++)
|
||||
{
|
||||
const float* va = &polya[j*3];
|
||||
const float* vb = &polya[i*3];
|
||||
const float n[3] = { vb[2]-va[2], 0, -(vb[0]-va[0]) };
|
||||
float amin,amax,bmin,bmax;
|
||||
projectPoly(n, polya, npolya, amin,amax);
|
||||
projectPoly(n, polyb, npolyb, bmin,bmax);
|
||||
if (!overlapRange(amin,amax, bmin,bmax, eps))
|
||||
{
|
||||
// Found separating axis
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (int i = 0, j = npolyb-1; i < npolyb; j=i++)
|
||||
{
|
||||
const float* va = &polyb[j*3];
|
||||
const float* vb = &polyb[i*3];
|
||||
const float n[3] = { vb[2]-va[2], 0, -(vb[0]-va[0]) };
|
||||
float amin,amax,bmin,bmax;
|
||||
projectPoly(n, polya, npolya, amin,amax);
|
||||
projectPoly(n, polyb, npolyb, bmin,bmax);
|
||||
if (!overlapRange(amin,amax, bmin,bmax, eps))
|
||||
{
|
||||
// Found separating axis
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns a random point in a convex polygon.
|
||||
// Adapted from Graphics Gems article.
|
||||
void dtRandomPointInConvexPoly(const float* pts, const int npts, float* areas,
|
||||
const float s, const float t, float* out)
|
||||
{
|
||||
// Calc triangle araes
|
||||
float areasum = 0.0f;
|
||||
for (int i = 2; i < npts; i++) {
|
||||
areas[i] = dtTriArea2D(&pts[0], &pts[(i-1)*3], &pts[i*3]);
|
||||
areasum += dtMax(0.001f, areas[i]);
|
||||
}
|
||||
// Find sub triangle weighted by area.
|
||||
const float thr = s*areasum;
|
||||
float acc = 0.0f;
|
||||
float u = 0.0f;
|
||||
int tri = 0;
|
||||
for (int i = 2; i < npts; i++) {
|
||||
const float dacc = areas[i];
|
||||
if (thr >= acc && thr < (acc+dacc))
|
||||
{
|
||||
u = (thr - acc) / dacc;
|
||||
tri = i;
|
||||
break;
|
||||
}
|
||||
acc += dacc;
|
||||
}
|
||||
|
||||
float v = dtMathSqrtf(t);
|
||||
|
||||
const float a = 1 - v;
|
||||
const float b = (1 - u) * v;
|
||||
const float c = u * v;
|
||||
const float* pa = &pts[0];
|
||||
const float* pb = &pts[(tri-1)*3];
|
||||
const float* pc = &pts[tri*3];
|
||||
|
||||
out[0] = a*pa[0] + b*pb[0] + c*pc[0];
|
||||
out[1] = a*pa[1] + b*pb[1] + c*pc[1];
|
||||
out[2] = a*pa[2] + b*pb[2] + c*pc[2];
|
||||
}
|
||||
|
||||
inline float vperpXZ(const float* a, const float* b) { return a[0]*b[2] - a[2]*b[0]; }
|
||||
|
||||
bool dtIntersectSegSeg2D(const float* ap, const float* aq,
|
||||
const float* bp, const float* bq,
|
||||
float& s, float& t)
|
||||
{
|
||||
float u[3], v[3], w[3];
|
||||
dtVsub(u,aq,ap);
|
||||
dtVsub(v,bq,bp);
|
||||
dtVsub(w,ap,bp);
|
||||
float d = vperpXZ(u,v);
|
||||
if (fabsf(d) < 1e-6f) return false;
|
||||
s = vperpXZ(v,w) / d;
|
||||
t = vperpXZ(u,w) / d;
|
||||
return true;
|
||||
}
|
||||
|
||||
1516
recast/detour/src/DetourNavMesh.cpp
Normal file
1516
recast/detour/src/DetourNavMesh.cpp
Normal file
File diff suppressed because it is too large
Load Diff
775
recast/detour/src/DetourNavMeshBuilder.cpp
Normal file
775
recast/detour/src/DetourNavMeshBuilder.cpp
Normal file
@ -0,0 +1,775 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <float.h>
|
||||
#include "DetourNavMesh.h"
|
||||
#include "DetourCommon.h"
|
||||
#include "DetourMath.h"
|
||||
#include "DetourNavMeshBuilder.h"
|
||||
#include "DetourAlloc.h"
|
||||
#include "DetourAssert.h"
|
||||
|
||||
static unsigned short MESH_NULL_IDX = 0xffff;
|
||||
|
||||
|
||||
struct BVItem
|
||||
{
|
||||
unsigned short bmin[3];
|
||||
unsigned short bmax[3];
|
||||
int i;
|
||||
};
|
||||
|
||||
static int compareItemX(const void* va, const void* vb)
|
||||
{
|
||||
const BVItem* a = (const BVItem*)va;
|
||||
const BVItem* b = (const BVItem*)vb;
|
||||
if (a->bmin[0] < b->bmin[0])
|
||||
return -1;
|
||||
if (a->bmin[0] > b->bmin[0])
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int compareItemY(const void* va, const void* vb)
|
||||
{
|
||||
const BVItem* a = (const BVItem*)va;
|
||||
const BVItem* b = (const BVItem*)vb;
|
||||
if (a->bmin[1] < b->bmin[1])
|
||||
return -1;
|
||||
if (a->bmin[1] > b->bmin[1])
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int compareItemZ(const void* va, const void* vb)
|
||||
{
|
||||
const BVItem* a = (const BVItem*)va;
|
||||
const BVItem* b = (const BVItem*)vb;
|
||||
if (a->bmin[2] < b->bmin[2])
|
||||
return -1;
|
||||
if (a->bmin[2] > b->bmin[2])
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void calcExtends(BVItem* items, const int /*nitems*/, const int imin, const int imax,
|
||||
unsigned short* bmin, unsigned short* bmax)
|
||||
{
|
||||
bmin[0] = items[imin].bmin[0];
|
||||
bmin[1] = items[imin].bmin[1];
|
||||
bmin[2] = items[imin].bmin[2];
|
||||
|
||||
bmax[0] = items[imin].bmax[0];
|
||||
bmax[1] = items[imin].bmax[1];
|
||||
bmax[2] = items[imin].bmax[2];
|
||||
|
||||
for (int i = imin+1; i < imax; ++i)
|
||||
{
|
||||
const BVItem& it = items[i];
|
||||
if (it.bmin[0] < bmin[0]) bmin[0] = it.bmin[0];
|
||||
if (it.bmin[1] < bmin[1]) bmin[1] = it.bmin[1];
|
||||
if (it.bmin[2] < bmin[2]) bmin[2] = it.bmin[2];
|
||||
|
||||
if (it.bmax[0] > bmax[0]) bmax[0] = it.bmax[0];
|
||||
if (it.bmax[1] > bmax[1]) bmax[1] = it.bmax[1];
|
||||
if (it.bmax[2] > bmax[2]) bmax[2] = it.bmax[2];
|
||||
}
|
||||
}
|
||||
|
||||
inline int longestAxis(unsigned short x, unsigned short y, unsigned short z)
|
||||
{
|
||||
int axis = 0;
|
||||
unsigned short maxVal = x;
|
||||
if (y > maxVal)
|
||||
{
|
||||
axis = 1;
|
||||
maxVal = y;
|
||||
}
|
||||
if (z > maxVal)
|
||||
{
|
||||
axis = 2;
|
||||
maxVal = z;
|
||||
}
|
||||
return axis;
|
||||
}
|
||||
|
||||
static void subdivide(BVItem* items, int nitems, int imin, int imax, int& curNode, dtBVNode* nodes)
|
||||
{
|
||||
int inum = imax - imin;
|
||||
int icur = curNode;
|
||||
|
||||
dtBVNode& node = nodes[curNode++];
|
||||
|
||||
if (inum == 1)
|
||||
{
|
||||
// Leaf
|
||||
node.bmin[0] = items[imin].bmin[0];
|
||||
node.bmin[1] = items[imin].bmin[1];
|
||||
node.bmin[2] = items[imin].bmin[2];
|
||||
|
||||
node.bmax[0] = items[imin].bmax[0];
|
||||
node.bmax[1] = items[imin].bmax[1];
|
||||
node.bmax[2] = items[imin].bmax[2];
|
||||
|
||||
node.i = items[imin].i;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Split
|
||||
calcExtends(items, nitems, imin, imax, node.bmin, node.bmax);
|
||||
|
||||
int axis = longestAxis(node.bmax[0] - node.bmin[0],
|
||||
node.bmax[1] - node.bmin[1],
|
||||
node.bmax[2] - node.bmin[2]);
|
||||
|
||||
if (axis == 0)
|
||||
{
|
||||
// Sort along x-axis
|
||||
qsort(items+imin, inum, sizeof(BVItem), compareItemX);
|
||||
}
|
||||
else if (axis == 1)
|
||||
{
|
||||
// Sort along y-axis
|
||||
qsort(items+imin, inum, sizeof(BVItem), compareItemY);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Sort along z-axis
|
||||
qsort(items+imin, inum, sizeof(BVItem), compareItemZ);
|
||||
}
|
||||
|
||||
int isplit = imin+inum/2;
|
||||
|
||||
// Left
|
||||
subdivide(items, nitems, imin, isplit, curNode, nodes);
|
||||
// Right
|
||||
subdivide(items, nitems, isplit, imax, curNode, nodes);
|
||||
|
||||
int iescape = curNode - icur;
|
||||
// Negative index means escape.
|
||||
node.i = -iescape;
|
||||
}
|
||||
}
|
||||
|
||||
static int createBVTree(const unsigned short* verts, const int /*nverts*/,
|
||||
const unsigned short* polys, const int npolys, const int nvp,
|
||||
const float cs, const float ch,
|
||||
const int /*nnodes*/, dtBVNode* nodes)
|
||||
{
|
||||
// Build tree
|
||||
BVItem* items = (BVItem*)dtAlloc(sizeof(BVItem)*npolys, DT_ALLOC_TEMP);
|
||||
for (int i = 0; i < npolys; i++)
|
||||
{
|
||||
BVItem& it = items[i];
|
||||
it.i = i;
|
||||
// Calc polygon bounds.
|
||||
const unsigned short* p = &polys[i*nvp*2];
|
||||
it.bmin[0] = it.bmax[0] = verts[p[0]*3+0];
|
||||
it.bmin[1] = it.bmax[1] = verts[p[0]*3+1];
|
||||
it.bmin[2] = it.bmax[2] = verts[p[0]*3+2];
|
||||
|
||||
for (int j = 1; j < nvp; ++j)
|
||||
{
|
||||
if (p[j] == MESH_NULL_IDX) break;
|
||||
unsigned short x = verts[p[j]*3+0];
|
||||
unsigned short y = verts[p[j]*3+1];
|
||||
unsigned short z = verts[p[j]*3+2];
|
||||
|
||||
if (x < it.bmin[0]) it.bmin[0] = x;
|
||||
if (y < it.bmin[1]) it.bmin[1] = y;
|
||||
if (z < it.bmin[2]) it.bmin[2] = z;
|
||||
|
||||
if (x > it.bmax[0]) it.bmax[0] = x;
|
||||
if (y > it.bmax[1]) it.bmax[1] = y;
|
||||
if (z > it.bmax[2]) it.bmax[2] = z;
|
||||
}
|
||||
// Remap y
|
||||
it.bmin[1] = (unsigned short)dtMathFloorf((float)it.bmin[1]*ch/cs);
|
||||
it.bmax[1] = (unsigned short)dtMathCeilf((float)it.bmax[1]*ch/cs);
|
||||
}
|
||||
|
||||
int curNode = 0;
|
||||
subdivide(items, npolys, 0, npolys, curNode, nodes);
|
||||
|
||||
dtFree(items);
|
||||
|
||||
return curNode;
|
||||
}
|
||||
|
||||
static unsigned char classifyOffMeshPoint(const float* pt, const float* bmin, const float* bmax)
|
||||
{
|
||||
static const unsigned char XP = 1<<0;
|
||||
static const unsigned char ZP = 1<<1;
|
||||
static const unsigned char XM = 1<<2;
|
||||
static const unsigned char ZM = 1<<3;
|
||||
|
||||
unsigned char outcode = 0;
|
||||
outcode |= (pt[0] >= bmax[0]) ? XP : 0;
|
||||
outcode |= (pt[2] >= bmax[2]) ? ZP : 0;
|
||||
outcode |= (pt[0] < bmin[0]) ? XM : 0;
|
||||
outcode |= (pt[2] < bmin[2]) ? ZM : 0;
|
||||
|
||||
switch (outcode)
|
||||
{
|
||||
case XP: return 0;
|
||||
case XP|ZP: return 1;
|
||||
case ZP: return 2;
|
||||
case XM|ZP: return 3;
|
||||
case XM: return 4;
|
||||
case XM|ZM: return 5;
|
||||
case ZM: return 6;
|
||||
case XP|ZM: return 7;
|
||||
};
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
// TODO: Better error handling.
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// The output data array is allocated using the detour allocator (dtAlloc()). The method
|
||||
/// used to free the memory will be determined by how the tile is added to the navigation
|
||||
/// mesh.
|
||||
///
|
||||
/// @see dtNavMesh, dtNavMesh::addTile()
|
||||
bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData, int* outDataSize)
|
||||
{
|
||||
if (params->nvp > DT_VERTS_PER_POLYGON)
|
||||
return false;
|
||||
if (params->vertCount >= 0xffff)
|
||||
return false;
|
||||
if (!params->vertCount || !params->verts)
|
||||
return false;
|
||||
if (!params->polyCount || !params->polys)
|
||||
return false;
|
||||
|
||||
const int nvp = params->nvp;
|
||||
|
||||
// Classify off-mesh connection points. We store only the connections
|
||||
// whose start point is inside the tile.
|
||||
unsigned char* offMeshConClass = 0;
|
||||
int storedOffMeshConCount = 0;
|
||||
int offMeshConLinkCount = 0;
|
||||
|
||||
if (params->offMeshConCount > 0)
|
||||
{
|
||||
offMeshConClass = (unsigned char*)dtAlloc(sizeof(unsigned char)*params->offMeshConCount*2, DT_ALLOC_TEMP);
|
||||
if (!offMeshConClass)
|
||||
return false;
|
||||
|
||||
// Find tight heigh bounds, used for culling out off-mesh start locations.
|
||||
float hmin = FLT_MAX;
|
||||
float hmax = -FLT_MAX;
|
||||
|
||||
if (params->detailVerts && params->detailVertsCount)
|
||||
{
|
||||
for (int i = 0; i < params->detailVertsCount; ++i)
|
||||
{
|
||||
const float h = params->detailVerts[i*3+1];
|
||||
hmin = dtMin(hmin,h);
|
||||
hmax = dtMax(hmax,h);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < params->vertCount; ++i)
|
||||
{
|
||||
const unsigned short* iv = ¶ms->verts[i*3];
|
||||
const float h = params->bmin[1] + iv[1] * params->ch;
|
||||
hmin = dtMin(hmin,h);
|
||||
hmax = dtMax(hmax,h);
|
||||
}
|
||||
}
|
||||
hmin -= params->walkableClimb;
|
||||
hmax += params->walkableClimb;
|
||||
float bmin[3], bmax[3];
|
||||
dtVcopy(bmin, params->bmin);
|
||||
dtVcopy(bmax, params->bmax);
|
||||
bmin[1] = hmin;
|
||||
bmax[1] = hmax;
|
||||
|
||||
for (int i = 0; i < params->offMeshConCount; ++i)
|
||||
{
|
||||
const float* p0 = ¶ms->offMeshConVerts[(i*2+0)*3];
|
||||
const float* p1 = ¶ms->offMeshConVerts[(i*2+1)*3];
|
||||
offMeshConClass[i*2+0] = classifyOffMeshPoint(p0, bmin, bmax);
|
||||
offMeshConClass[i*2+1] = classifyOffMeshPoint(p1, bmin, bmax);
|
||||
|
||||
// Zero out off-mesh start positions which are not even potentially touching the mesh.
|
||||
if (offMeshConClass[i*2+0] == 0xff)
|
||||
{
|
||||
if (p0[1] < bmin[1] || p0[1] > bmax[1])
|
||||
offMeshConClass[i*2+0] = 0;
|
||||
}
|
||||
|
||||
// Cound how many links should be allocated for off-mesh connections.
|
||||
if (offMeshConClass[i*2+0] == 0xff)
|
||||
offMeshConLinkCount++;
|
||||
if (offMeshConClass[i*2+1] == 0xff)
|
||||
offMeshConLinkCount++;
|
||||
|
||||
if (offMeshConClass[i*2+0] == 0xff)
|
||||
storedOffMeshConCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// Off-mesh connectionss are stored as polygons, adjust values.
|
||||
const int totPolyCount = params->polyCount + storedOffMeshConCount;
|
||||
const int totVertCount = params->vertCount + storedOffMeshConCount*2;
|
||||
|
||||
// Find portal edges which are at tile borders.
|
||||
int edgeCount = 0;
|
||||
int portalCount = 0;
|
||||
for (int i = 0; i < params->polyCount; ++i)
|
||||
{
|
||||
const unsigned short* p = ¶ms->polys[i*2*nvp];
|
||||
for (int j = 0; j < nvp; ++j)
|
||||
{
|
||||
if (p[j] == MESH_NULL_IDX) break;
|
||||
edgeCount++;
|
||||
|
||||
if (p[nvp+j] & 0x8000)
|
||||
{
|
||||
unsigned short dir = p[nvp+j] & 0xf;
|
||||
if (dir != 0xf)
|
||||
portalCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const int maxLinkCount = edgeCount + portalCount*2 + offMeshConLinkCount*2;
|
||||
|
||||
// Find unique detail vertices.
|
||||
int uniqueDetailVertCount = 0;
|
||||
int detailTriCount = 0;
|
||||
if (params->detailMeshes)
|
||||
{
|
||||
// Has detail mesh, count unique detail vertex count and use input detail tri count.
|
||||
detailTriCount = params->detailTriCount;
|
||||
for (int i = 0; i < params->polyCount; ++i)
|
||||
{
|
||||
const unsigned short* p = ¶ms->polys[i*nvp*2];
|
||||
int ndv = params->detailMeshes[i*4+1];
|
||||
int nv = 0;
|
||||
for (int j = 0; j < nvp; ++j)
|
||||
{
|
||||
if (p[j] == MESH_NULL_IDX) break;
|
||||
nv++;
|
||||
}
|
||||
ndv -= nv;
|
||||
uniqueDetailVertCount += ndv;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No input detail mesh, build detail mesh from nav polys.
|
||||
uniqueDetailVertCount = 0; // No extra detail verts.
|
||||
detailTriCount = 0;
|
||||
for (int i = 0; i < params->polyCount; ++i)
|
||||
{
|
||||
const unsigned short* p = ¶ms->polys[i*nvp*2];
|
||||
int nv = 0;
|
||||
for (int j = 0; j < nvp; ++j)
|
||||
{
|
||||
if (p[j] == MESH_NULL_IDX) break;
|
||||
nv++;
|
||||
}
|
||||
detailTriCount += nv-2;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate data size
|
||||
const int headerSize = dtAlign4(sizeof(dtMeshHeader));
|
||||
const int vertsSize = dtAlign4(sizeof(float)*3*totVertCount);
|
||||
const int polysSize = dtAlign4(sizeof(dtPoly)*totPolyCount);
|
||||
const int linksSize = dtAlign4(sizeof(dtLink)*maxLinkCount);
|
||||
const int detailMeshesSize = dtAlign4(sizeof(dtPolyDetail)*params->polyCount);
|
||||
const int detailVertsSize = dtAlign4(sizeof(float)*3*uniqueDetailVertCount);
|
||||
const int detailTrisSize = dtAlign4(sizeof(unsigned char)*4*detailTriCount);
|
||||
const int bvTreeSize = params->buildBvTree ? dtAlign4(sizeof(dtBVNode)*params->polyCount*2) : 0;
|
||||
const int offMeshConsSize = dtAlign4(sizeof(dtOffMeshConnection)*storedOffMeshConCount);
|
||||
|
||||
const int dataSize = headerSize + vertsSize + polysSize + linksSize +
|
||||
detailMeshesSize + detailVertsSize + detailTrisSize +
|
||||
bvTreeSize + offMeshConsSize;
|
||||
|
||||
unsigned char* data = (unsigned char*)dtAlloc(sizeof(unsigned char)*dataSize, DT_ALLOC_PERM);
|
||||
if (!data)
|
||||
{
|
||||
dtFree(offMeshConClass);
|
||||
return false;
|
||||
}
|
||||
memset(data, 0, dataSize);
|
||||
|
||||
unsigned char* d = data;
|
||||
dtMeshHeader* header = (dtMeshHeader*)d; d += headerSize;
|
||||
float* navVerts = (float*)d; d += vertsSize;
|
||||
dtPoly* navPolys = (dtPoly*)d; d += polysSize;
|
||||
d += linksSize;
|
||||
dtPolyDetail* navDMeshes = (dtPolyDetail*)d; d += detailMeshesSize;
|
||||
float* navDVerts = (float*)d; d += detailVertsSize;
|
||||
unsigned char* navDTris = (unsigned char*)d; d += detailTrisSize;
|
||||
dtBVNode* navBvtree = (dtBVNode*)d; d += bvTreeSize;
|
||||
dtOffMeshConnection* offMeshCons = (dtOffMeshConnection*)d; d += offMeshConsSize;
|
||||
|
||||
|
||||
// Store header
|
||||
header->magic = DT_NAVMESH_MAGIC;
|
||||
header->version = DT_NAVMESH_VERSION;
|
||||
header->x = params->tileX;
|
||||
header->y = params->tileY;
|
||||
header->layer = params->tileLayer;
|
||||
header->userId = params->userId;
|
||||
header->polyCount = totPolyCount;
|
||||
header->vertCount = totVertCount;
|
||||
header->maxLinkCount = maxLinkCount;
|
||||
dtVcopy(header->bmin, params->bmin);
|
||||
dtVcopy(header->bmax, params->bmax);
|
||||
header->detailMeshCount = params->polyCount;
|
||||
header->detailVertCount = uniqueDetailVertCount;
|
||||
header->detailTriCount = detailTriCount;
|
||||
header->bvQuantFactor = 1.0f / params->cs;
|
||||
header->offMeshBase = params->polyCount;
|
||||
header->walkableHeight = params->walkableHeight;
|
||||
header->walkableRadius = params->walkableRadius;
|
||||
header->walkableClimb = params->walkableClimb;
|
||||
header->offMeshConCount = storedOffMeshConCount;
|
||||
header->bvNodeCount = params->buildBvTree ? params->polyCount*2 : 0;
|
||||
|
||||
const int offMeshVertsBase = params->vertCount;
|
||||
const int offMeshPolyBase = params->polyCount;
|
||||
|
||||
// Store vertices
|
||||
// Mesh vertices
|
||||
for (int i = 0; i < params->vertCount; ++i)
|
||||
{
|
||||
const unsigned short* iv = ¶ms->verts[i*3];
|
||||
float* v = &navVerts[i*3];
|
||||
v[0] = params->bmin[0] + iv[0] * params->cs;
|
||||
v[1] = params->bmin[1] + iv[1] * params->ch;
|
||||
v[2] = params->bmin[2] + iv[2] * params->cs;
|
||||
}
|
||||
// Off-mesh link vertices.
|
||||
int n = 0;
|
||||
for (int i = 0; i < params->offMeshConCount; ++i)
|
||||
{
|
||||
// Only store connections which start from this tile.
|
||||
if (offMeshConClass[i*2+0] == 0xff)
|
||||
{
|
||||
const float* linkv = ¶ms->offMeshConVerts[i*2*3];
|
||||
float* v = &navVerts[(offMeshVertsBase + n*2)*3];
|
||||
dtVcopy(&v[0], &linkv[0]);
|
||||
dtVcopy(&v[3], &linkv[3]);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
// Store polygons
|
||||
// Mesh polys
|
||||
const unsigned short* src = params->polys;
|
||||
for (int i = 0; i < params->polyCount; ++i)
|
||||
{
|
||||
dtPoly* p = &navPolys[i];
|
||||
p->vertCount = 0;
|
||||
p->flags = params->polyFlags[i];
|
||||
p->setArea(params->polyAreas[i]);
|
||||
p->setType(DT_POLYTYPE_GROUND);
|
||||
for (int j = 0; j < nvp; ++j)
|
||||
{
|
||||
if (src[j] == MESH_NULL_IDX) break;
|
||||
p->verts[j] = src[j];
|
||||
if (src[nvp+j] & 0x8000)
|
||||
{
|
||||
// Border or portal edge.
|
||||
unsigned short dir = src[nvp+j] & 0xf;
|
||||
if (dir == 0xf) // Border
|
||||
p->neis[j] = 0;
|
||||
else if (dir == 0) // Portal x-
|
||||
p->neis[j] = DT_EXT_LINK | 4;
|
||||
else if (dir == 1) // Portal z+
|
||||
p->neis[j] = DT_EXT_LINK | 2;
|
||||
else if (dir == 2) // Portal x+
|
||||
p->neis[j] = DT_EXT_LINK | 0;
|
||||
else if (dir == 3) // Portal z-
|
||||
p->neis[j] = DT_EXT_LINK | 6;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Normal connection
|
||||
p->neis[j] = src[nvp+j]+1;
|
||||
}
|
||||
|
||||
p->vertCount++;
|
||||
}
|
||||
src += nvp*2;
|
||||
}
|
||||
// Off-mesh connection vertices.
|
||||
n = 0;
|
||||
for (int i = 0; i < params->offMeshConCount; ++i)
|
||||
{
|
||||
// Only store connections which start from this tile.
|
||||
if (offMeshConClass[i*2+0] == 0xff)
|
||||
{
|
||||
dtPoly* p = &navPolys[offMeshPolyBase+n];
|
||||
p->vertCount = 2;
|
||||
p->verts[0] = (unsigned short)(offMeshVertsBase + n*2+0);
|
||||
p->verts[1] = (unsigned short)(offMeshVertsBase + n*2+1);
|
||||
p->flags = params->offMeshConFlags[i];
|
||||
p->setArea(params->offMeshConAreas[i]);
|
||||
p->setType(DT_POLYTYPE_OFFMESH_CONNECTION);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
// Store detail meshes and vertices.
|
||||
// The nav polygon vertices are stored as the first vertices on each mesh.
|
||||
// We compress the mesh data by skipping them and using the navmesh coordinates.
|
||||
if (params->detailMeshes)
|
||||
{
|
||||
unsigned short vbase = 0;
|
||||
for (int i = 0; i < params->polyCount; ++i)
|
||||
{
|
||||
dtPolyDetail& dtl = navDMeshes[i];
|
||||
const int vb = (int)params->detailMeshes[i*4+0];
|
||||
const int ndv = (int)params->detailMeshes[i*4+1];
|
||||
const int nv = navPolys[i].vertCount;
|
||||
dtl.vertBase = (unsigned int)vbase;
|
||||
dtl.vertCount = (unsigned char)(ndv-nv);
|
||||
dtl.triBase = (unsigned int)params->detailMeshes[i*4+2];
|
||||
dtl.triCount = (unsigned char)params->detailMeshes[i*4+3];
|
||||
// Copy vertices except the first 'nv' verts which are equal to nav poly verts.
|
||||
if (ndv-nv)
|
||||
{
|
||||
memcpy(&navDVerts[vbase*3], ¶ms->detailVerts[(vb+nv)*3], sizeof(float)*3*(ndv-nv));
|
||||
vbase += (unsigned short)(ndv-nv);
|
||||
}
|
||||
}
|
||||
// Store triangles.
|
||||
memcpy(navDTris, params->detailTris, sizeof(unsigned char)*4*params->detailTriCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create dummy detail mesh by triangulating polys.
|
||||
int tbase = 0;
|
||||
for (int i = 0; i < params->polyCount; ++i)
|
||||
{
|
||||
dtPolyDetail& dtl = navDMeshes[i];
|
||||
const int nv = navPolys[i].vertCount;
|
||||
dtl.vertBase = 0;
|
||||
dtl.vertCount = 0;
|
||||
dtl.triBase = (unsigned int)tbase;
|
||||
dtl.triCount = (unsigned char)(nv-2);
|
||||
// Triangulate polygon (local indices).
|
||||
for (int j = 2; j < nv; ++j)
|
||||
{
|
||||
unsigned char* t = &navDTris[tbase*4];
|
||||
t[0] = 0;
|
||||
t[1] = (unsigned char)(j-1);
|
||||
t[2] = (unsigned char)j;
|
||||
// Bit for each edge that belongs to poly boundary.
|
||||
t[3] = (1<<2);
|
||||
if (j == 2) t[3] |= (1<<0);
|
||||
if (j == nv-1) t[3] |= (1<<4);
|
||||
tbase++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store and create BVtree.
|
||||
// TODO: take detail mesh into account! use byte per bbox extent?
|
||||
if (params->buildBvTree)
|
||||
{
|
||||
createBVTree(params->verts, params->vertCount, params->polys, params->polyCount,
|
||||
nvp, params->cs, params->ch, params->polyCount*2, navBvtree);
|
||||
}
|
||||
|
||||
// Store Off-Mesh connections.
|
||||
n = 0;
|
||||
for (int i = 0; i < params->offMeshConCount; ++i)
|
||||
{
|
||||
// Only store connections which start from this tile.
|
||||
if (offMeshConClass[i*2+0] == 0xff)
|
||||
{
|
||||
dtOffMeshConnection* con = &offMeshCons[n];
|
||||
con->poly = (unsigned short)(offMeshPolyBase + n);
|
||||
// Copy connection end-points.
|
||||
const float* endPts = ¶ms->offMeshConVerts[i*2*3];
|
||||
dtVcopy(&con->pos[0], &endPts[0]);
|
||||
dtVcopy(&con->pos[3], &endPts[3]);
|
||||
con->rad = params->offMeshConRad[i];
|
||||
con->flags = params->offMeshConDir[i] ? DT_OFFMESH_CON_BIDIR : 0;
|
||||
con->side = offMeshConClass[i*2+1];
|
||||
if (params->offMeshConUserID)
|
||||
con->userId = params->offMeshConUserID[i];
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
dtFree(offMeshConClass);
|
||||
|
||||
*outData = data;
|
||||
*outDataSize = dataSize;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dtNavMeshHeaderSwapEndian(unsigned char* data, const int /*dataSize*/)
|
||||
{
|
||||
dtMeshHeader* header = (dtMeshHeader*)data;
|
||||
|
||||
int swappedMagic = DT_NAVMESH_MAGIC;
|
||||
int swappedVersion = DT_NAVMESH_VERSION;
|
||||
dtSwapEndian(&swappedMagic);
|
||||
dtSwapEndian(&swappedVersion);
|
||||
|
||||
if ((header->magic != DT_NAVMESH_MAGIC || header->version != DT_NAVMESH_VERSION) &&
|
||||
(header->magic != swappedMagic || header->version != swappedVersion))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
dtSwapEndian(&header->magic);
|
||||
dtSwapEndian(&header->version);
|
||||
dtSwapEndian(&header->x);
|
||||
dtSwapEndian(&header->y);
|
||||
dtSwapEndian(&header->layer);
|
||||
dtSwapEndian(&header->userId);
|
||||
dtSwapEndian(&header->polyCount);
|
||||
dtSwapEndian(&header->vertCount);
|
||||
dtSwapEndian(&header->maxLinkCount);
|
||||
dtSwapEndian(&header->detailMeshCount);
|
||||
dtSwapEndian(&header->detailVertCount);
|
||||
dtSwapEndian(&header->detailTriCount);
|
||||
dtSwapEndian(&header->bvNodeCount);
|
||||
dtSwapEndian(&header->offMeshConCount);
|
||||
dtSwapEndian(&header->offMeshBase);
|
||||
dtSwapEndian(&header->walkableHeight);
|
||||
dtSwapEndian(&header->walkableRadius);
|
||||
dtSwapEndian(&header->walkableClimb);
|
||||
dtSwapEndian(&header->bmin[0]);
|
||||
dtSwapEndian(&header->bmin[1]);
|
||||
dtSwapEndian(&header->bmin[2]);
|
||||
dtSwapEndian(&header->bmax[0]);
|
||||
dtSwapEndian(&header->bmax[1]);
|
||||
dtSwapEndian(&header->bmax[2]);
|
||||
dtSwapEndian(&header->bvQuantFactor);
|
||||
|
||||
// Freelist index and pointers are updated when tile is added, no need to swap.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// @warning This function assumes that the header is in the correct endianess already.
|
||||
/// Call #dtNavMeshHeaderSwapEndian() first on the data if the data is expected to be in wrong endianess
|
||||
/// to start with. Call #dtNavMeshHeaderSwapEndian() after the data has been swapped if converting from
|
||||
/// native to foreign endianess.
|
||||
bool dtNavMeshDataSwapEndian(unsigned char* data, const int /*dataSize*/)
|
||||
{
|
||||
// Make sure the data is in right format.
|
||||
dtMeshHeader* header = (dtMeshHeader*)data;
|
||||
if (header->magic != DT_NAVMESH_MAGIC)
|
||||
return false;
|
||||
if (header->version != DT_NAVMESH_VERSION)
|
||||
return false;
|
||||
|
||||
// Patch header pointers.
|
||||
const int headerSize = dtAlign4(sizeof(dtMeshHeader));
|
||||
const int vertsSize = dtAlign4(sizeof(float)*3*header->vertCount);
|
||||
const int polysSize = dtAlign4(sizeof(dtPoly)*header->polyCount);
|
||||
const int linksSize = dtAlign4(sizeof(dtLink)*(header->maxLinkCount));
|
||||
const int detailMeshesSize = dtAlign4(sizeof(dtPolyDetail)*header->detailMeshCount);
|
||||
const int detailVertsSize = dtAlign4(sizeof(float)*3*header->detailVertCount);
|
||||
const int detailTrisSize = dtAlign4(sizeof(unsigned char)*4*header->detailTriCount);
|
||||
const int bvtreeSize = dtAlign4(sizeof(dtBVNode)*header->bvNodeCount);
|
||||
const int offMeshLinksSize = dtAlign4(sizeof(dtOffMeshConnection)*header->offMeshConCount);
|
||||
|
||||
unsigned char* d = data + headerSize;
|
||||
float* verts = (float*)d; d += vertsSize;
|
||||
dtPoly* polys = (dtPoly*)d; d += polysSize;
|
||||
/*dtLink* links = (dtLink*)d;*/ d += linksSize;
|
||||
dtPolyDetail* detailMeshes = (dtPolyDetail*)d; d += detailMeshesSize;
|
||||
float* detailVerts = (float*)d; d += detailVertsSize;
|
||||
/*unsigned char* detailTris = (unsigned char*)d;*/ d += detailTrisSize;
|
||||
dtBVNode* bvTree = (dtBVNode*)d; d += bvtreeSize;
|
||||
dtOffMeshConnection* offMeshCons = (dtOffMeshConnection*)d; d += offMeshLinksSize;
|
||||
|
||||
// Vertices
|
||||
for (int i = 0; i < header->vertCount*3; ++i)
|
||||
{
|
||||
dtSwapEndian(&verts[i]);
|
||||
}
|
||||
|
||||
// Polys
|
||||
for (int i = 0; i < header->polyCount; ++i)
|
||||
{
|
||||
dtPoly* p = &polys[i];
|
||||
// poly->firstLink is update when tile is added, no need to swap.
|
||||
for (int j = 0; j < DT_VERTS_PER_POLYGON; ++j)
|
||||
{
|
||||
dtSwapEndian(&p->verts[j]);
|
||||
dtSwapEndian(&p->neis[j]);
|
||||
}
|
||||
dtSwapEndian(&p->flags);
|
||||
}
|
||||
|
||||
// Links are rebuild when tile is added, no need to swap.
|
||||
|
||||
// Detail meshes
|
||||
for (int i = 0; i < header->detailMeshCount; ++i)
|
||||
{
|
||||
dtPolyDetail* pd = &detailMeshes[i];
|
||||
dtSwapEndian(&pd->vertBase);
|
||||
dtSwapEndian(&pd->triBase);
|
||||
}
|
||||
|
||||
// Detail verts
|
||||
for (int i = 0; i < header->detailVertCount*3; ++i)
|
||||
{
|
||||
dtSwapEndian(&detailVerts[i]);
|
||||
}
|
||||
|
||||
// BV-tree
|
||||
for (int i = 0; i < header->bvNodeCount; ++i)
|
||||
{
|
||||
dtBVNode* node = &bvTree[i];
|
||||
for (int j = 0; j < 3; ++j)
|
||||
{
|
||||
dtSwapEndian(&node->bmin[j]);
|
||||
dtSwapEndian(&node->bmax[j]);
|
||||
}
|
||||
dtSwapEndian(&node->i);
|
||||
}
|
||||
|
||||
// Off-mesh Connections.
|
||||
for (int i = 0; i < header->offMeshConCount; ++i)
|
||||
{
|
||||
dtOffMeshConnection* con = &offMeshCons[i];
|
||||
for (int j = 0; j < 6; ++j)
|
||||
dtSwapEndian(&con->pos[j]);
|
||||
dtSwapEndian(&con->rad);
|
||||
dtSwapEndian(&con->poly);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
3542
recast/detour/src/DetourNavMeshQuery.cpp
Normal file
3542
recast/detour/src/DetourNavMeshQuery.cpp
Normal file
File diff suppressed because it is too large
Load Diff
198
recast/detour/src/DetourNode.cpp
Normal file
198
recast/detour/src/DetourNode.cpp
Normal file
@ -0,0 +1,198 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#include "DetourNode.h"
|
||||
#include "DetourAlloc.h"
|
||||
#include "DetourAssert.h"
|
||||
#include "DetourCommon.h"
|
||||
#include <string.h>
|
||||
|
||||
#ifdef DT_POLYREF64
|
||||
// From Thomas Wang, https://gist.github.com/badboy/6267743
|
||||
inline unsigned int dtHashRef(dtPolyRef a)
|
||||
{
|
||||
a = (~a) + (a << 18); // a = (a << 18) - a - 1;
|
||||
a = a ^ (a >> 31);
|
||||
a = a * 21; // a = (a + (a << 2)) + (a << 4);
|
||||
a = a ^ (a >> 11);
|
||||
a = a + (a << 6);
|
||||
a = a ^ (a >> 22);
|
||||
return (unsigned int)a;
|
||||
}
|
||||
#else
|
||||
inline unsigned int dtHashRef(dtPolyRef a)
|
||||
{
|
||||
a += ~(a<<15);
|
||||
a ^= (a>>10);
|
||||
a += (a<<3);
|
||||
a ^= (a>>6);
|
||||
a += ~(a<<11);
|
||||
a ^= (a>>16);
|
||||
return (unsigned int)a;
|
||||
}
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
dtNodePool::dtNodePool(int maxNodes, int hashSize) :
|
||||
m_nodes(0),
|
||||
m_first(0),
|
||||
m_next(0),
|
||||
m_maxNodes(maxNodes),
|
||||
m_hashSize(hashSize),
|
||||
m_nodeCount(0)
|
||||
{
|
||||
dtAssert(dtNextPow2(m_hashSize) == (unsigned int)m_hashSize);
|
||||
dtAssert(m_maxNodes > 0);
|
||||
|
||||
m_nodes = (dtNode*)dtAlloc(sizeof(dtNode)*m_maxNodes, DT_ALLOC_PERM);
|
||||
m_next = (dtNodeIndex*)dtAlloc(sizeof(dtNodeIndex)*m_maxNodes, DT_ALLOC_PERM);
|
||||
m_first = (dtNodeIndex*)dtAlloc(sizeof(dtNodeIndex)*hashSize, DT_ALLOC_PERM);
|
||||
|
||||
dtAssert(m_nodes);
|
||||
dtAssert(m_next);
|
||||
dtAssert(m_first);
|
||||
|
||||
memset(m_first, 0xff, sizeof(dtNodeIndex)*m_hashSize);
|
||||
memset(m_next, 0xff, sizeof(dtNodeIndex)*m_maxNodes);
|
||||
}
|
||||
|
||||
dtNodePool::~dtNodePool()
|
||||
{
|
||||
dtFree(m_nodes);
|
||||
dtFree(m_next);
|
||||
dtFree(m_first);
|
||||
}
|
||||
|
||||
void dtNodePool::clear()
|
||||
{
|
||||
memset(m_first, 0xff, sizeof(dtNodeIndex)*m_hashSize);
|
||||
m_nodeCount = 0;
|
||||
}
|
||||
|
||||
unsigned int dtNodePool::findNodes(dtPolyRef id, dtNode** nodes, const int maxNodes)
|
||||
{
|
||||
int n = 0;
|
||||
unsigned int bucket = dtHashRef(id) & (m_hashSize-1);
|
||||
dtNodeIndex i = m_first[bucket];
|
||||
while (i != DT_NULL_IDX)
|
||||
{
|
||||
if (m_nodes[i].id == id)
|
||||
{
|
||||
if (n >= maxNodes)
|
||||
return n;
|
||||
nodes[n++] = &m_nodes[i];
|
||||
}
|
||||
i = m_next[i];
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
dtNode* dtNodePool::findNode(dtPolyRef id, unsigned char state)
|
||||
{
|
||||
unsigned int bucket = dtHashRef(id) & (m_hashSize-1);
|
||||
dtNodeIndex i = m_first[bucket];
|
||||
while (i != DT_NULL_IDX)
|
||||
{
|
||||
if (m_nodes[i].id == id && m_nodes[i].state == state)
|
||||
return &m_nodes[i];
|
||||
i = m_next[i];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
dtNode* dtNodePool::getNode(dtPolyRef id, unsigned char state)
|
||||
{
|
||||
unsigned int bucket = dtHashRef(id) & (m_hashSize-1);
|
||||
dtNodeIndex i = m_first[bucket];
|
||||
dtNode* node = 0;
|
||||
while (i != DT_NULL_IDX)
|
||||
{
|
||||
if (m_nodes[i].id == id && m_nodes[i].state == state)
|
||||
return &m_nodes[i];
|
||||
i = m_next[i];
|
||||
}
|
||||
|
||||
if (m_nodeCount >= m_maxNodes)
|
||||
return 0;
|
||||
|
||||
i = (dtNodeIndex)m_nodeCount;
|
||||
m_nodeCount++;
|
||||
|
||||
// Init node
|
||||
node = &m_nodes[i];
|
||||
node->pidx = 0;
|
||||
node->cost = 0;
|
||||
node->total = 0;
|
||||
node->id = id;
|
||||
node->state = state;
|
||||
node->flags = 0;
|
||||
|
||||
m_next[i] = m_first[bucket];
|
||||
m_first[bucket] = i;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
dtNodeQueue::dtNodeQueue(int n) :
|
||||
m_heap(0),
|
||||
m_capacity(n),
|
||||
m_size(0)
|
||||
{
|
||||
dtAssert(m_capacity > 0);
|
||||
|
||||
m_heap = (dtNode**)dtAlloc(sizeof(dtNode*)*(m_capacity+1), DT_ALLOC_PERM);
|
||||
dtAssert(m_heap);
|
||||
}
|
||||
|
||||
dtNodeQueue::~dtNodeQueue()
|
||||
{
|
||||
dtFree(m_heap);
|
||||
}
|
||||
|
||||
void dtNodeQueue::bubbleUp(int i, dtNode* node)
|
||||
{
|
||||
int parent = (i-1)/2;
|
||||
// note: (index > 0) means there is a parent
|
||||
while ((i > 0) && (m_heap[parent]->total > node->total))
|
||||
{
|
||||
m_heap[i] = m_heap[parent];
|
||||
i = parent;
|
||||
parent = (i-1)/2;
|
||||
}
|
||||
m_heap[i] = node;
|
||||
}
|
||||
|
||||
void dtNodeQueue::trickleDown(int i, dtNode* node)
|
||||
{
|
||||
int child = (i*2)+1;
|
||||
while (child < m_size)
|
||||
{
|
||||
if (((child+1) < m_size) &&
|
||||
(m_heap[child]->total > m_heap[child+1]->total))
|
||||
{
|
||||
child++;
|
||||
}
|
||||
m_heap[i] = m_heap[child];
|
||||
i = child;
|
||||
child = (i*2)+1;
|
||||
}
|
||||
bubbleUp(i, node);
|
||||
}
|
||||
212
recast/detour_tile_cache/include/DetourTileCache.h
Normal file
212
recast/detour_tile_cache/include/DetourTileCache.h
Normal file
@ -0,0 +1,212 @@
|
||||
#ifndef DETOURTILECACHE_H
|
||||
#define DETOURTILECACHE_H
|
||||
|
||||
#include "DetourStatus.h"
|
||||
|
||||
|
||||
|
||||
typedef unsigned int dtObstacleRef;
|
||||
|
||||
typedef unsigned int dtCompressedTileRef;
|
||||
|
||||
/// Flags for addTile
|
||||
enum dtCompressedTileFlags
|
||||
{
|
||||
DT_COMPRESSEDTILE_FREE_DATA = 0x01, ///< Navmesh owns the tile memory and should free it.
|
||||
};
|
||||
|
||||
struct dtCompressedTile
|
||||
{
|
||||
unsigned int salt; ///< Counter describing modifications to the tile.
|
||||
struct dtTileCacheLayerHeader* header;
|
||||
unsigned char* compressed;
|
||||
int compressedSize;
|
||||
unsigned char* data;
|
||||
int dataSize;
|
||||
unsigned int flags;
|
||||
dtCompressedTile* next;
|
||||
};
|
||||
|
||||
enum ObstacleState
|
||||
{
|
||||
DT_OBSTACLE_EMPTY,
|
||||
DT_OBSTACLE_PROCESSING,
|
||||
DT_OBSTACLE_PROCESSED,
|
||||
DT_OBSTACLE_REMOVING,
|
||||
};
|
||||
|
||||
static const int DT_MAX_TOUCHED_TILES = 8;
|
||||
struct dtTileCacheObstacle
|
||||
{
|
||||
float pos[3], radius, height;
|
||||
dtCompressedTileRef touched[DT_MAX_TOUCHED_TILES];
|
||||
dtCompressedTileRef pending[DT_MAX_TOUCHED_TILES];
|
||||
unsigned short salt;
|
||||
unsigned char state;
|
||||
unsigned char ntouched;
|
||||
unsigned char npending;
|
||||
dtTileCacheObstacle* next;
|
||||
};
|
||||
|
||||
struct dtTileCacheParams
|
||||
{
|
||||
float orig[3];
|
||||
float cs, ch;
|
||||
int width, height;
|
||||
float walkableHeight;
|
||||
float walkableRadius;
|
||||
float walkableClimb;
|
||||
float maxSimplificationError;
|
||||
int maxTiles;
|
||||
int maxObstacles;
|
||||
};
|
||||
|
||||
struct dtTileCacheMeshProcess
|
||||
{
|
||||
virtual ~dtTileCacheMeshProcess() { }
|
||||
|
||||
virtual void process(struct dtNavMeshCreateParams* params,
|
||||
unsigned char* polyAreas, unsigned short* polyFlags) = 0;
|
||||
};
|
||||
|
||||
|
||||
class dtTileCache
|
||||
{
|
||||
public:
|
||||
dtTileCache();
|
||||
~dtTileCache();
|
||||
|
||||
struct dtTileCacheAlloc* getAlloc() { return m_talloc; }
|
||||
struct dtTileCacheCompressor* getCompressor() { return m_tcomp; }
|
||||
const dtTileCacheParams* getParams() const { return &m_params; }
|
||||
|
||||
inline int getTileCount() const { return m_params.maxTiles; }
|
||||
inline const dtCompressedTile* getTile(const int i) const { return &m_tiles[i]; }
|
||||
|
||||
inline int getObstacleCount() const { return m_params.maxObstacles; }
|
||||
inline const dtTileCacheObstacle* getObstacle(const int i) const { return &m_obstacles[i]; }
|
||||
|
||||
const dtTileCacheObstacle* getObstacleByRef(dtObstacleRef ref);
|
||||
|
||||
dtObstacleRef getObstacleRef(const dtTileCacheObstacle* obmin) const;
|
||||
|
||||
dtStatus init(const dtTileCacheParams* params,
|
||||
struct dtTileCacheAlloc* talloc,
|
||||
struct dtTileCacheCompressor* tcomp,
|
||||
struct dtTileCacheMeshProcess* tmproc);
|
||||
|
||||
int getTilesAt(const int tx, const int ty, dtCompressedTileRef* tiles, const int maxTiles) const ;
|
||||
|
||||
dtCompressedTile* getTileAt(const int tx, const int ty, const int tlayer);
|
||||
dtCompressedTileRef getTileRef(const dtCompressedTile* tile) const;
|
||||
const dtCompressedTile* getTileByRef(dtCompressedTileRef ref) const;
|
||||
|
||||
dtStatus addTile(unsigned char* data, const int dataSize, unsigned char flags, dtCompressedTileRef* result);
|
||||
|
||||
dtStatus removeTile(dtCompressedTileRef ref, unsigned char** data, int* dataSize);
|
||||
|
||||
dtStatus addObstacle(const float* pos, const float radius, const float height, dtObstacleRef* result);
|
||||
dtStatus removeObstacle(const dtObstacleRef ref);
|
||||
|
||||
dtStatus queryTiles(const float* bmin, const float* bmax,
|
||||
dtCompressedTileRef* results, int* resultCount, const int maxResults) const;
|
||||
|
||||
dtStatus update(const float /*dt*/, class dtNavMesh* navmesh);
|
||||
|
||||
dtStatus buildNavMeshTilesAt(const int tx, const int ty, class dtNavMesh* navmesh);
|
||||
|
||||
dtStatus buildNavMeshTile(const dtCompressedTileRef ref, class dtNavMesh* navmesh);
|
||||
|
||||
void calcTightTileBounds(const struct dtTileCacheLayerHeader* header, float* bmin, float* bmax) const;
|
||||
|
||||
void getObstacleBounds(const struct dtTileCacheObstacle* ob, float* bmin, float* bmax) const;
|
||||
|
||||
|
||||
/// Encodes a tile id.
|
||||
inline dtCompressedTileRef encodeTileId(unsigned int salt, unsigned int it) const
|
||||
{
|
||||
return ((dtCompressedTileRef)salt << m_tileBits) | (dtCompressedTileRef)it;
|
||||
}
|
||||
|
||||
/// Decodes a tile salt.
|
||||
inline unsigned int decodeTileIdSalt(dtCompressedTileRef ref) const
|
||||
{
|
||||
const dtCompressedTileRef saltMask = ((dtCompressedTileRef)1<<m_saltBits)-1;
|
||||
return (unsigned int)((ref >> m_tileBits) & saltMask);
|
||||
}
|
||||
|
||||
/// Decodes a tile id.
|
||||
inline unsigned int decodeTileIdTile(dtCompressedTileRef ref) const
|
||||
{
|
||||
const dtCompressedTileRef tileMask = ((dtCompressedTileRef)1<<m_tileBits)-1;
|
||||
return (unsigned int)(ref & tileMask);
|
||||
}
|
||||
|
||||
/// Encodes an obstacle id.
|
||||
inline dtObstacleRef encodeObstacleId(unsigned int salt, unsigned int it) const
|
||||
{
|
||||
return ((dtObstacleRef)salt << 16) | (dtObstacleRef)it;
|
||||
}
|
||||
|
||||
/// Decodes an obstacle salt.
|
||||
inline unsigned int decodeObstacleIdSalt(dtObstacleRef ref) const
|
||||
{
|
||||
const dtObstacleRef saltMask = ((dtObstacleRef)1<<16)-1;
|
||||
return (unsigned int)((ref >> 16) & saltMask);
|
||||
}
|
||||
|
||||
/// Decodes an obstacle id.
|
||||
inline unsigned int decodeObstacleIdObstacle(dtObstacleRef ref) const
|
||||
{
|
||||
const dtObstacleRef tileMask = ((dtObstacleRef)1<<16)-1;
|
||||
return (unsigned int)(ref & tileMask);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
enum ObstacleRequestAction
|
||||
{
|
||||
REQUEST_ADD,
|
||||
REQUEST_REMOVE,
|
||||
};
|
||||
|
||||
struct ObstacleRequest
|
||||
{
|
||||
int action;
|
||||
dtObstacleRef ref;
|
||||
};
|
||||
|
||||
int m_tileLutSize; ///< Tile hash lookup size (must be pot).
|
||||
int m_tileLutMask; ///< Tile hash lookup mask.
|
||||
|
||||
dtCompressedTile** m_posLookup; ///< Tile hash lookup.
|
||||
dtCompressedTile* m_nextFreeTile; ///< Freelist of tiles.
|
||||
dtCompressedTile* m_tiles; ///< List of tiles.
|
||||
|
||||
unsigned int m_saltBits; ///< Number of salt bits in the tile ID.
|
||||
unsigned int m_tileBits; ///< Number of tile bits in the tile ID.
|
||||
|
||||
dtTileCacheParams m_params;
|
||||
|
||||
dtTileCacheAlloc* m_talloc;
|
||||
dtTileCacheCompressor* m_tcomp;
|
||||
dtTileCacheMeshProcess* m_tmproc;
|
||||
|
||||
dtTileCacheObstacle* m_obstacles;
|
||||
dtTileCacheObstacle* m_nextFreeObstacle;
|
||||
|
||||
static const int MAX_REQUESTS = 64;
|
||||
ObstacleRequest m_reqs[MAX_REQUESTS];
|
||||
int m_nreqs;
|
||||
|
||||
static const int MAX_UPDATE = 64;
|
||||
dtCompressedTileRef m_update[MAX_UPDATE];
|
||||
int m_nupdate;
|
||||
|
||||
};
|
||||
|
||||
dtTileCache* dtAllocTileCache();
|
||||
void dtFreeTileCache(dtTileCache* tc);
|
||||
|
||||
#endif
|
||||
152
recast/detour_tile_cache/include/DetourTileCacheBuilder.h
Normal file
152
recast/detour_tile_cache/include/DetourTileCacheBuilder.h
Normal file
@ -0,0 +1,152 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#ifndef DETOURTILECACHEBUILDER_H
|
||||
#define DETOURTILECACHEBUILDER_H
|
||||
|
||||
#include "DetourAlloc.h"
|
||||
#include "DetourStatus.h"
|
||||
|
||||
static const int DT_TILECACHE_MAGIC = 'D'<<24 | 'T'<<16 | 'L'<<8 | 'R'; ///< 'DTLR';
|
||||
static const int DT_TILECACHE_VERSION = 1;
|
||||
|
||||
static const unsigned char DT_TILECACHE_NULL_AREA = 0;
|
||||
static const unsigned char DT_TILECACHE_WALKABLE_AREA = 63;
|
||||
static const unsigned short DT_TILECACHE_NULL_IDX = 0xffff;
|
||||
|
||||
struct dtTileCacheLayerHeader
|
||||
{
|
||||
int magic; ///< Data magic
|
||||
int version; ///< Data version
|
||||
int tx,ty,tlayer;
|
||||
float bmin[3], bmax[3];
|
||||
unsigned short hmin, hmax; ///< Height min/max range
|
||||
unsigned char width, height; ///< Dimension of the layer.
|
||||
unsigned char minx, maxx, miny, maxy; ///< Usable sub-region.
|
||||
};
|
||||
|
||||
struct dtTileCacheLayer
|
||||
{
|
||||
dtTileCacheLayerHeader* header;
|
||||
unsigned char regCount; ///< Region count.
|
||||
unsigned char* heights;
|
||||
unsigned char* areas;
|
||||
unsigned char* cons;
|
||||
unsigned char* regs;
|
||||
};
|
||||
|
||||
struct dtTileCacheContour
|
||||
{
|
||||
int nverts;
|
||||
unsigned char* verts;
|
||||
unsigned char reg;
|
||||
unsigned char area;
|
||||
};
|
||||
|
||||
struct dtTileCacheContourSet
|
||||
{
|
||||
int nconts;
|
||||
dtTileCacheContour* conts;
|
||||
};
|
||||
|
||||
struct dtTileCachePolyMesh
|
||||
{
|
||||
int nvp;
|
||||
int nverts; ///< Number of vertices.
|
||||
int npolys; ///< Number of polygons.
|
||||
unsigned short* verts; ///< Vertices of the mesh, 3 elements per vertex.
|
||||
unsigned short* polys; ///< Polygons of the mesh, nvp*2 elements per polygon.
|
||||
unsigned short* flags; ///< Per polygon flags.
|
||||
unsigned char* areas; ///< Area ID of polygons.
|
||||
};
|
||||
|
||||
|
||||
struct dtTileCacheAlloc
|
||||
{
|
||||
virtual ~dtTileCacheAlloc() { }
|
||||
|
||||
virtual void reset()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void* alloc(const int size)
|
||||
{
|
||||
return dtAlloc(size, DT_ALLOC_TEMP);
|
||||
}
|
||||
|
||||
virtual void free(void* ptr)
|
||||
{
|
||||
dtFree(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
struct dtTileCacheCompressor
|
||||
{
|
||||
virtual ~dtTileCacheCompressor() { }
|
||||
|
||||
virtual int maxCompressedSize(const int bufferSize) = 0;
|
||||
virtual dtStatus compress(const unsigned char* buffer, const int bufferSize,
|
||||
unsigned char* compressed, const int maxCompressedSize, int* compressedSize) = 0;
|
||||
virtual dtStatus decompress(const unsigned char* compressed, const int compressedSize,
|
||||
unsigned char* buffer, const int maxBufferSize, int* bufferSize) = 0;
|
||||
};
|
||||
|
||||
|
||||
dtStatus dtBuildTileCacheLayer(dtTileCacheCompressor* comp,
|
||||
dtTileCacheLayerHeader* header,
|
||||
const unsigned char* heights,
|
||||
const unsigned char* areas,
|
||||
const unsigned char* cons,
|
||||
unsigned char** outData, int* outDataSize);
|
||||
|
||||
void dtFreeTileCacheLayer(dtTileCacheAlloc* alloc, dtTileCacheLayer* layer);
|
||||
|
||||
dtStatus dtDecompressTileCacheLayer(dtTileCacheAlloc* alloc, dtTileCacheCompressor* comp,
|
||||
unsigned char* compressed, const int compressedSize,
|
||||
dtTileCacheLayer** layerOut);
|
||||
|
||||
dtTileCacheContourSet* dtAllocTileCacheContourSet(dtTileCacheAlloc* alloc);
|
||||
void dtFreeTileCacheContourSet(dtTileCacheAlloc* alloc, dtTileCacheContourSet* cset);
|
||||
|
||||
dtTileCachePolyMesh* dtAllocTileCachePolyMesh(dtTileCacheAlloc* alloc);
|
||||
void dtFreeTileCachePolyMesh(dtTileCacheAlloc* alloc, dtTileCachePolyMesh* lmesh);
|
||||
|
||||
dtStatus dtMarkCylinderArea(dtTileCacheLayer& layer, const float* orig, const float cs, const float ch,
|
||||
const float* pos, const float radius, const float height, const unsigned char areaId);
|
||||
|
||||
dtStatus dtBuildTileCacheRegions(dtTileCacheAlloc* alloc,
|
||||
dtTileCacheLayer& layer,
|
||||
const int walkableClimb);
|
||||
|
||||
dtStatus dtBuildTileCacheContours(dtTileCacheAlloc* alloc,
|
||||
dtTileCacheLayer& layer,
|
||||
const int walkableClimb, const float maxError,
|
||||
dtTileCacheContourSet& lcset);
|
||||
|
||||
dtStatus dtBuildTileCachePolyMesh(dtTileCacheAlloc* alloc,
|
||||
dtTileCacheContourSet& lcset,
|
||||
dtTileCachePolyMesh& mesh);
|
||||
|
||||
/// Swaps the endianess of the compressed tile data's header (#dtTileCacheLayerHeader).
|
||||
/// Tile layer data does not need endian swapping as it consits only of bytes.
|
||||
/// @param[in,out] data The tile data array.
|
||||
/// @param[in] dataSize The size of the data array.
|
||||
bool dtTileCacheHeaderSwapEndian(unsigned char* data, const int dataSize);
|
||||
|
||||
|
||||
#endif // DETOURTILECACHEBUILDER_H
|
||||
704
recast/detour_tile_cache/src/DetourTileCache.cpp
Normal file
704
recast/detour_tile_cache/src/DetourTileCache.cpp
Normal file
@ -0,0 +1,704 @@
|
||||
#include "DetourTileCache.h"
|
||||
#include "DetourTileCacheBuilder.h"
|
||||
#include "DetourNavMeshBuilder.h"
|
||||
#include "DetourNavMesh.h"
|
||||
#include "DetourCommon.h"
|
||||
#include "DetourMath.h"
|
||||
#include "DetourAlloc.h"
|
||||
#include "DetourAssert.h"
|
||||
#include <string.h>
|
||||
#include <new>
|
||||
|
||||
dtTileCache* dtAllocTileCache()
|
||||
{
|
||||
void* mem = dtAlloc(sizeof(dtTileCache), DT_ALLOC_PERM);
|
||||
if (!mem) return 0;
|
||||
return new(mem) dtTileCache;
|
||||
}
|
||||
|
||||
void dtFreeTileCache(dtTileCache* tc)
|
||||
{
|
||||
if (!tc) return;
|
||||
tc->~dtTileCache();
|
||||
dtFree(tc);
|
||||
}
|
||||
|
||||
static bool contains(const dtCompressedTileRef* a, const int n, const dtCompressedTileRef v)
|
||||
{
|
||||
for (int i = 0; i < n; ++i)
|
||||
if (a[i] == v)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
inline int computeTileHash(int x, int y, const int mask)
|
||||
{
|
||||
const unsigned int h1 = 0x8da6b343; // Large multiplicative constants;
|
||||
const unsigned int h2 = 0xd8163841; // here arbitrarily chosen primes
|
||||
unsigned int n = h1 * x + h2 * y;
|
||||
return (int)(n & mask);
|
||||
}
|
||||
|
||||
|
||||
struct BuildContext
|
||||
{
|
||||
inline BuildContext(struct dtTileCacheAlloc* a) : layer(0), lcset(0), lmesh(0), alloc(a) {}
|
||||
inline ~BuildContext() { purge(); }
|
||||
void purge()
|
||||
{
|
||||
dtFreeTileCacheLayer(alloc, layer);
|
||||
layer = 0;
|
||||
dtFreeTileCacheContourSet(alloc, lcset);
|
||||
lcset = 0;
|
||||
dtFreeTileCachePolyMesh(alloc, lmesh);
|
||||
lmesh = 0;
|
||||
}
|
||||
struct dtTileCacheLayer* layer;
|
||||
struct dtTileCacheContourSet* lcset;
|
||||
struct dtTileCachePolyMesh* lmesh;
|
||||
struct dtTileCacheAlloc* alloc;
|
||||
};
|
||||
|
||||
|
||||
dtTileCache::dtTileCache() :
|
||||
m_tileLutSize(0),
|
||||
m_tileLutMask(0),
|
||||
m_posLookup(0),
|
||||
m_nextFreeTile(0),
|
||||
m_tiles(0),
|
||||
m_saltBits(0),
|
||||
m_tileBits(0),
|
||||
m_talloc(0),
|
||||
m_tcomp(0),
|
||||
m_tmproc(0),
|
||||
m_obstacles(0),
|
||||
m_nextFreeObstacle(0),
|
||||
m_nreqs(0),
|
||||
m_nupdate(0)
|
||||
{
|
||||
memset(&m_params, 0, sizeof(m_params));
|
||||
}
|
||||
|
||||
dtTileCache::~dtTileCache()
|
||||
{
|
||||
for (int i = 0; i < m_params.maxTiles; ++i)
|
||||
{
|
||||
if (m_tiles[i].flags & DT_COMPRESSEDTILE_FREE_DATA)
|
||||
{
|
||||
dtFree(m_tiles[i].data);
|
||||
m_tiles[i].data = 0;
|
||||
}
|
||||
}
|
||||
dtFree(m_obstacles);
|
||||
m_obstacles = 0;
|
||||
dtFree(m_posLookup);
|
||||
m_posLookup = 0;
|
||||
dtFree(m_tiles);
|
||||
m_tiles = 0;
|
||||
m_nreqs = 0;
|
||||
m_nupdate = 0;
|
||||
}
|
||||
|
||||
const dtCompressedTile* dtTileCache::getTileByRef(dtCompressedTileRef ref) const
|
||||
{
|
||||
if (!ref)
|
||||
return 0;
|
||||
unsigned int tileIndex = decodeTileIdTile(ref);
|
||||
unsigned int tileSalt = decodeTileIdSalt(ref);
|
||||
if ((int)tileIndex >= m_params.maxTiles)
|
||||
return 0;
|
||||
const dtCompressedTile* tile = &m_tiles[tileIndex];
|
||||
if (tile->salt != tileSalt)
|
||||
return 0;
|
||||
return tile;
|
||||
}
|
||||
|
||||
|
||||
dtStatus dtTileCache::init(const dtTileCacheParams* params,
|
||||
dtTileCacheAlloc* talloc,
|
||||
dtTileCacheCompressor* tcomp,
|
||||
dtTileCacheMeshProcess* tmproc)
|
||||
{
|
||||
m_talloc = talloc;
|
||||
m_tcomp = tcomp;
|
||||
m_tmproc = tmproc;
|
||||
m_nreqs = 0;
|
||||
memcpy(&m_params, params, sizeof(m_params));
|
||||
|
||||
// Alloc space for obstacles.
|
||||
m_obstacles = (dtTileCacheObstacle*)dtAlloc(sizeof(dtTileCacheObstacle)*m_params.maxObstacles, DT_ALLOC_PERM);
|
||||
if (!m_obstacles)
|
||||
return DT_FAILURE | DT_OUT_OF_MEMORY;
|
||||
memset(m_obstacles, 0, sizeof(dtTileCacheObstacle)*m_params.maxObstacles);
|
||||
m_nextFreeObstacle = 0;
|
||||
for (int i = m_params.maxObstacles-1; i >= 0; --i)
|
||||
{
|
||||
m_obstacles[i].salt = 1;
|
||||
m_obstacles[i].next = m_nextFreeObstacle;
|
||||
m_nextFreeObstacle = &m_obstacles[i];
|
||||
}
|
||||
|
||||
// Init tiles
|
||||
m_tileLutSize = dtNextPow2(m_params.maxTiles/4);
|
||||
if (!m_tileLutSize) m_tileLutSize = 1;
|
||||
m_tileLutMask = m_tileLutSize-1;
|
||||
|
||||
m_tiles = (dtCompressedTile*)dtAlloc(sizeof(dtCompressedTile)*m_params.maxTiles, DT_ALLOC_PERM);
|
||||
if (!m_tiles)
|
||||
return DT_FAILURE | DT_OUT_OF_MEMORY;
|
||||
m_posLookup = (dtCompressedTile**)dtAlloc(sizeof(dtCompressedTile*)*m_tileLutSize, DT_ALLOC_PERM);
|
||||
if (!m_posLookup)
|
||||
return DT_FAILURE | DT_OUT_OF_MEMORY;
|
||||
memset(m_tiles, 0, sizeof(dtCompressedTile)*m_params.maxTiles);
|
||||
memset(m_posLookup, 0, sizeof(dtCompressedTile*)*m_tileLutSize);
|
||||
m_nextFreeTile = 0;
|
||||
for (int i = m_params.maxTiles-1; i >= 0; --i)
|
||||
{
|
||||
m_tiles[i].salt = 1;
|
||||
m_tiles[i].next = m_nextFreeTile;
|
||||
m_nextFreeTile = &m_tiles[i];
|
||||
}
|
||||
|
||||
// Init ID generator values.
|
||||
m_tileBits = dtIlog2(dtNextPow2((unsigned int)m_params.maxTiles));
|
||||
// Only allow 31 salt bits, since the salt mask is calculated using 32bit uint and it will overflow.
|
||||
m_saltBits = dtMin((unsigned int)31, 32 - m_tileBits);
|
||||
if (m_saltBits < 10)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
return DT_SUCCESS;
|
||||
}
|
||||
|
||||
int dtTileCache::getTilesAt(const int tx, const int ty, dtCompressedTileRef* tiles, const int maxTiles) const
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
// Find tile based on hash.
|
||||
int h = computeTileHash(tx,ty,m_tileLutMask);
|
||||
dtCompressedTile* tile = m_posLookup[h];
|
||||
while (tile)
|
||||
{
|
||||
if (tile->header &&
|
||||
tile->header->tx == tx &&
|
||||
tile->header->ty == ty)
|
||||
{
|
||||
if (n < maxTiles)
|
||||
tiles[n++] = getTileRef(tile);
|
||||
}
|
||||
tile = tile->next;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
dtCompressedTile* dtTileCache::getTileAt(const int tx, const int ty, const int tlayer)
|
||||
{
|
||||
// Find tile based on hash.
|
||||
int h = computeTileHash(tx,ty,m_tileLutMask);
|
||||
dtCompressedTile* tile = m_posLookup[h];
|
||||
while (tile)
|
||||
{
|
||||
if (tile->header &&
|
||||
tile->header->tx == tx &&
|
||||
tile->header->ty == ty &&
|
||||
tile->header->tlayer == tlayer)
|
||||
{
|
||||
return tile;
|
||||
}
|
||||
tile = tile->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
dtCompressedTileRef dtTileCache::getTileRef(const dtCompressedTile* tile) const
|
||||
{
|
||||
if (!tile) return 0;
|
||||
const unsigned int it = (unsigned int)(tile - m_tiles);
|
||||
return (dtCompressedTileRef)encodeTileId(tile->salt, it);
|
||||
}
|
||||
|
||||
dtObstacleRef dtTileCache::getObstacleRef(const dtTileCacheObstacle* ob) const
|
||||
{
|
||||
if (!ob) return 0;
|
||||
const unsigned int idx = (unsigned int)(ob - m_obstacles);
|
||||
return encodeObstacleId(ob->salt, idx);
|
||||
}
|
||||
|
||||
const dtTileCacheObstacle* dtTileCache::getObstacleByRef(dtObstacleRef ref)
|
||||
{
|
||||
if (!ref)
|
||||
return 0;
|
||||
unsigned int idx = decodeObstacleIdObstacle(ref);
|
||||
if ((int)idx >= m_params.maxObstacles)
|
||||
return 0;
|
||||
const dtTileCacheObstacle* ob = &m_obstacles[idx];
|
||||
unsigned int salt = decodeObstacleIdSalt(ref);
|
||||
if (ob->salt != salt)
|
||||
return 0;
|
||||
return ob;
|
||||
}
|
||||
|
||||
dtStatus dtTileCache::addTile(unsigned char* data, const int dataSize, unsigned char flags, dtCompressedTileRef* result)
|
||||
{
|
||||
// Make sure the data is in right format.
|
||||
dtTileCacheLayerHeader* header = (dtTileCacheLayerHeader*)data;
|
||||
if (header->magic != DT_TILECACHE_MAGIC)
|
||||
return DT_FAILURE | DT_WRONG_MAGIC;
|
||||
if (header->version != DT_TILECACHE_VERSION)
|
||||
return DT_FAILURE | DT_WRONG_VERSION;
|
||||
|
||||
// Make sure the location is free.
|
||||
if (getTileAt(header->tx, header->ty, header->tlayer))
|
||||
return DT_FAILURE;
|
||||
|
||||
// Allocate a tile.
|
||||
dtCompressedTile* tile = 0;
|
||||
if (m_nextFreeTile)
|
||||
{
|
||||
tile = m_nextFreeTile;
|
||||
m_nextFreeTile = tile->next;
|
||||
tile->next = 0;
|
||||
}
|
||||
|
||||
// Make sure we could allocate a tile.
|
||||
if (!tile)
|
||||
return DT_FAILURE | DT_OUT_OF_MEMORY;
|
||||
|
||||
// Insert tile into the position lut.
|
||||
int h = computeTileHash(header->tx, header->ty, m_tileLutMask);
|
||||
tile->next = m_posLookup[h];
|
||||
m_posLookup[h] = tile;
|
||||
|
||||
// Init tile.
|
||||
const int headerSize = dtAlign4(sizeof(dtTileCacheLayerHeader));
|
||||
tile->header = (dtTileCacheLayerHeader*)data;
|
||||
tile->data = data;
|
||||
tile->dataSize = dataSize;
|
||||
tile->compressed = tile->data + headerSize;
|
||||
tile->compressedSize = tile->dataSize - headerSize;
|
||||
tile->flags = flags;
|
||||
|
||||
if (result)
|
||||
*result = getTileRef(tile);
|
||||
|
||||
return DT_SUCCESS;
|
||||
}
|
||||
|
||||
dtStatus dtTileCache::removeTile(dtCompressedTileRef ref, unsigned char** data, int* dataSize)
|
||||
{
|
||||
if (!ref)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
unsigned int tileIndex = decodeTileIdTile(ref);
|
||||
unsigned int tileSalt = decodeTileIdSalt(ref);
|
||||
if ((int)tileIndex >= m_params.maxTiles)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
dtCompressedTile* tile = &m_tiles[tileIndex];
|
||||
if (tile->salt != tileSalt)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
// Remove tile from hash lookup.
|
||||
const int h = computeTileHash(tile->header->tx,tile->header->ty,m_tileLutMask);
|
||||
dtCompressedTile* prev = 0;
|
||||
dtCompressedTile* cur = m_posLookup[h];
|
||||
while (cur)
|
||||
{
|
||||
if (cur == tile)
|
||||
{
|
||||
if (prev)
|
||||
prev->next = cur->next;
|
||||
else
|
||||
m_posLookup[h] = cur->next;
|
||||
break;
|
||||
}
|
||||
prev = cur;
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
// Reset tile.
|
||||
if (tile->flags & DT_COMPRESSEDTILE_FREE_DATA)
|
||||
{
|
||||
// Owns data
|
||||
dtFree(tile->data);
|
||||
tile->data = 0;
|
||||
tile->dataSize = 0;
|
||||
if (data) *data = 0;
|
||||
if (dataSize) *dataSize = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (data) *data = tile->data;
|
||||
if (dataSize) *dataSize = tile->dataSize;
|
||||
}
|
||||
|
||||
tile->header = 0;
|
||||
tile->data = 0;
|
||||
tile->dataSize = 0;
|
||||
tile->compressed = 0;
|
||||
tile->compressedSize = 0;
|
||||
tile->flags = 0;
|
||||
|
||||
// Update salt, salt should never be zero.
|
||||
tile->salt = (tile->salt+1) & ((1<<m_saltBits)-1);
|
||||
if (tile->salt == 0)
|
||||
tile->salt++;
|
||||
|
||||
// Add to free list.
|
||||
tile->next = m_nextFreeTile;
|
||||
m_nextFreeTile = tile;
|
||||
|
||||
return DT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
dtObstacleRef dtTileCache::addObstacle(const float* pos, const float radius, const float height, dtObstacleRef* result)
|
||||
{
|
||||
if (m_nreqs >= MAX_REQUESTS)
|
||||
return DT_FAILURE | DT_BUFFER_TOO_SMALL;
|
||||
|
||||
dtTileCacheObstacle* ob = 0;
|
||||
if (m_nextFreeObstacle)
|
||||
{
|
||||
ob = m_nextFreeObstacle;
|
||||
m_nextFreeObstacle = ob->next;
|
||||
ob->next = 0;
|
||||
}
|
||||
if (!ob)
|
||||
return DT_FAILURE | DT_OUT_OF_MEMORY;
|
||||
|
||||
unsigned short salt = ob->salt;
|
||||
memset(ob, 0, sizeof(dtTileCacheObstacle));
|
||||
ob->salt = salt;
|
||||
ob->state = DT_OBSTACLE_PROCESSING;
|
||||
dtVcopy(ob->pos, pos);
|
||||
ob->radius = radius;
|
||||
ob->height = height;
|
||||
|
||||
ObstacleRequest* req = &m_reqs[m_nreqs++];
|
||||
memset(req, 0, sizeof(ObstacleRequest));
|
||||
req->action = REQUEST_ADD;
|
||||
req->ref = getObstacleRef(ob);
|
||||
|
||||
if (result)
|
||||
*result = req->ref;
|
||||
|
||||
return DT_SUCCESS;
|
||||
}
|
||||
|
||||
dtObstacleRef dtTileCache::removeObstacle(const dtObstacleRef ref)
|
||||
{
|
||||
if (!ref)
|
||||
return DT_SUCCESS;
|
||||
if (m_nreqs >= MAX_REQUESTS)
|
||||
return DT_FAILURE | DT_BUFFER_TOO_SMALL;
|
||||
|
||||
ObstacleRequest* req = &m_reqs[m_nreqs++];
|
||||
memset(req, 0, sizeof(ObstacleRequest));
|
||||
req->action = REQUEST_REMOVE;
|
||||
req->ref = ref;
|
||||
|
||||
return DT_SUCCESS;
|
||||
}
|
||||
|
||||
dtStatus dtTileCache::queryTiles(const float* bmin, const float* bmax,
|
||||
dtCompressedTileRef* results, int* resultCount, const int maxResults) const
|
||||
{
|
||||
const int MAX_TILES = 32;
|
||||
dtCompressedTileRef tiles[MAX_TILES];
|
||||
|
||||
int n = 0;
|
||||
|
||||
const float tw = m_params.width * m_params.cs;
|
||||
const float th = m_params.height * m_params.cs;
|
||||
const int tx0 = (int)dtMathFloorf((bmin[0]-m_params.orig[0]) / tw);
|
||||
const int tx1 = (int)dtMathFloorf((bmax[0]-m_params.orig[0]) / tw);
|
||||
const int ty0 = (int)dtMathFloorf((bmin[2]-m_params.orig[2]) / th);
|
||||
const int ty1 = (int)dtMathFloorf((bmax[2]-m_params.orig[2]) / th);
|
||||
|
||||
for (int ty = ty0; ty <= ty1; ++ty)
|
||||
{
|
||||
for (int tx = tx0; tx <= tx1; ++tx)
|
||||
{
|
||||
const int ntiles = getTilesAt(tx,ty,tiles,MAX_TILES);
|
||||
|
||||
for (int i = 0; i < ntiles; ++i)
|
||||
{
|
||||
const dtCompressedTile* tile = &m_tiles[decodeTileIdTile(tiles[i])];
|
||||
float tbmin[3], tbmax[3];
|
||||
calcTightTileBounds(tile->header, tbmin, tbmax);
|
||||
|
||||
if (dtOverlapBounds(bmin,bmax, tbmin,tbmax))
|
||||
{
|
||||
if (n < maxResults)
|
||||
results[n++] = tiles[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*resultCount = n;
|
||||
|
||||
return DT_SUCCESS;
|
||||
}
|
||||
|
||||
dtStatus dtTileCache::update(const float /*dt*/, dtNavMesh* navmesh)
|
||||
{
|
||||
if (m_nupdate == 0)
|
||||
{
|
||||
// Process requests.
|
||||
for (int i = 0; i < m_nreqs; ++i)
|
||||
{
|
||||
ObstacleRequest* req = &m_reqs[i];
|
||||
|
||||
unsigned int idx = decodeObstacleIdObstacle(req->ref);
|
||||
if ((int)idx >= m_params.maxObstacles)
|
||||
continue;
|
||||
dtTileCacheObstacle* ob = &m_obstacles[idx];
|
||||
unsigned int salt = decodeObstacleIdSalt(req->ref);
|
||||
if (ob->salt != salt)
|
||||
continue;
|
||||
|
||||
if (req->action == REQUEST_ADD)
|
||||
{
|
||||
// Find touched tiles.
|
||||
float bmin[3], bmax[3];
|
||||
getObstacleBounds(ob, bmin, bmax);
|
||||
|
||||
int ntouched = 0;
|
||||
queryTiles(bmin, bmax, ob->touched, &ntouched, DT_MAX_TOUCHED_TILES);
|
||||
ob->ntouched = (unsigned char)ntouched;
|
||||
// Add tiles to update list.
|
||||
ob->npending = 0;
|
||||
for (int j = 0; j < ob->ntouched; ++j)
|
||||
{
|
||||
if (m_nupdate < MAX_UPDATE)
|
||||
{
|
||||
if (!contains(m_update, m_nupdate, ob->touched[j]))
|
||||
m_update[m_nupdate++] = ob->touched[j];
|
||||
ob->pending[ob->npending++] = ob->touched[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (req->action == REQUEST_REMOVE)
|
||||
{
|
||||
// Prepare to remove obstacle.
|
||||
ob->state = DT_OBSTACLE_REMOVING;
|
||||
// Add tiles to update list.
|
||||
ob->npending = 0;
|
||||
for (int j = 0; j < ob->ntouched; ++j)
|
||||
{
|
||||
if (m_nupdate < MAX_UPDATE)
|
||||
{
|
||||
if (!contains(m_update, m_nupdate, ob->touched[j]))
|
||||
m_update[m_nupdate++] = ob->touched[j];
|
||||
ob->pending[ob->npending++] = ob->touched[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_nreqs = 0;
|
||||
}
|
||||
|
||||
// Process updates
|
||||
if (m_nupdate)
|
||||
{
|
||||
// Build mesh
|
||||
const dtCompressedTileRef ref = m_update[0];
|
||||
dtStatus status = buildNavMeshTile(ref, navmesh);
|
||||
m_nupdate--;
|
||||
if (m_nupdate > 0)
|
||||
memmove(m_update, m_update+1, m_nupdate*sizeof(dtCompressedTileRef));
|
||||
|
||||
// Update obstacle states.
|
||||
for (int i = 0; i < m_params.maxObstacles; ++i)
|
||||
{
|
||||
dtTileCacheObstacle* ob = &m_obstacles[i];
|
||||
if (ob->state == DT_OBSTACLE_PROCESSING || ob->state == DT_OBSTACLE_REMOVING)
|
||||
{
|
||||
// Remove handled tile from pending list.
|
||||
for (int j = 0; j < (int)ob->npending; j++)
|
||||
{
|
||||
if (ob->pending[j] == ref)
|
||||
{
|
||||
ob->pending[j] = ob->pending[(int)ob->npending-1];
|
||||
ob->npending--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If all pending tiles processed, change state.
|
||||
if (ob->npending == 0)
|
||||
{
|
||||
if (ob->state == DT_OBSTACLE_PROCESSING)
|
||||
{
|
||||
ob->state = DT_OBSTACLE_PROCESSED;
|
||||
}
|
||||
else if (ob->state == DT_OBSTACLE_REMOVING)
|
||||
{
|
||||
ob->state = DT_OBSTACLE_EMPTY;
|
||||
// Update salt, salt should never be zero.
|
||||
ob->salt = (ob->salt+1) & ((1<<16)-1);
|
||||
if (ob->salt == 0)
|
||||
ob->salt++;
|
||||
// Return obstacle to free list.
|
||||
ob->next = m_nextFreeObstacle;
|
||||
m_nextFreeObstacle = ob;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dtStatusFailed(status))
|
||||
return status;
|
||||
}
|
||||
|
||||
return DT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
dtStatus dtTileCache::buildNavMeshTilesAt(const int tx, const int ty, dtNavMesh* navmesh)
|
||||
{
|
||||
const int MAX_TILES = 32;
|
||||
dtCompressedTileRef tiles[MAX_TILES];
|
||||
const int ntiles = getTilesAt(tx,ty,tiles,MAX_TILES);
|
||||
|
||||
for (int i = 0; i < ntiles; ++i)
|
||||
{
|
||||
dtStatus status = buildNavMeshTile(tiles[i], navmesh);
|
||||
if (dtStatusFailed(status))
|
||||
return status;
|
||||
}
|
||||
|
||||
return DT_SUCCESS;
|
||||
}
|
||||
|
||||
dtStatus dtTileCache::buildNavMeshTile(const dtCompressedTileRef ref, dtNavMesh* navmesh)
|
||||
{
|
||||
dtAssert(m_talloc);
|
||||
dtAssert(m_tcomp);
|
||||
|
||||
unsigned int idx = decodeTileIdTile(ref);
|
||||
if (idx > (unsigned int)m_params.maxTiles)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
const dtCompressedTile* tile = &m_tiles[idx];
|
||||
unsigned int salt = decodeTileIdSalt(ref);
|
||||
if (tile->salt != salt)
|
||||
return DT_FAILURE | DT_INVALID_PARAM;
|
||||
|
||||
m_talloc->reset();
|
||||
|
||||
BuildContext bc(m_talloc);
|
||||
const int walkableClimbVx = (int)(m_params.walkableClimb / m_params.ch);
|
||||
dtStatus status;
|
||||
|
||||
// Decompress tile layer data.
|
||||
status = dtDecompressTileCacheLayer(m_talloc, m_tcomp, tile->data, tile->dataSize, &bc.layer);
|
||||
if (dtStatusFailed(status))
|
||||
return status;
|
||||
|
||||
// Rasterize obstacles.
|
||||
for (int i = 0; i < m_params.maxObstacles; ++i)
|
||||
{
|
||||
const dtTileCacheObstacle* ob = &m_obstacles[i];
|
||||
if (ob->state == DT_OBSTACLE_EMPTY || ob->state == DT_OBSTACLE_REMOVING)
|
||||
continue;
|
||||
if (contains(ob->touched, ob->ntouched, ref))
|
||||
{
|
||||
dtMarkCylinderArea(*bc.layer, tile->header->bmin, m_params.cs, m_params.ch,
|
||||
ob->pos, ob->radius, ob->height, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Build navmesh
|
||||
status = dtBuildTileCacheRegions(m_talloc, *bc.layer, walkableClimbVx);
|
||||
if (dtStatusFailed(status))
|
||||
return status;
|
||||
|
||||
bc.lcset = dtAllocTileCacheContourSet(m_talloc);
|
||||
if (!bc.lcset)
|
||||
return status;
|
||||
status = dtBuildTileCacheContours(m_talloc, *bc.layer, walkableClimbVx,
|
||||
m_params.maxSimplificationError, *bc.lcset);
|
||||
if (dtStatusFailed(status))
|
||||
return status;
|
||||
|
||||
bc.lmesh = dtAllocTileCachePolyMesh(m_talloc);
|
||||
if (!bc.lmesh)
|
||||
return status;
|
||||
status = dtBuildTileCachePolyMesh(m_talloc, *bc.lcset, *bc.lmesh);
|
||||
if (dtStatusFailed(status))
|
||||
return status;
|
||||
|
||||
// Early out if the mesh tile is empty.
|
||||
if (!bc.lmesh->npolys)
|
||||
return DT_SUCCESS;
|
||||
|
||||
dtNavMeshCreateParams params;
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
params.verts = bc.lmesh->verts;
|
||||
params.vertCount = bc.lmesh->nverts;
|
||||
params.polys = bc.lmesh->polys;
|
||||
params.polyAreas = bc.lmesh->areas;
|
||||
params.polyFlags = bc.lmesh->flags;
|
||||
params.polyCount = bc.lmesh->npolys;
|
||||
params.nvp = DT_VERTS_PER_POLYGON;
|
||||
params.walkableHeight = m_params.walkableHeight;
|
||||
params.walkableRadius = m_params.walkableRadius;
|
||||
params.walkableClimb = m_params.walkableClimb;
|
||||
params.tileX = tile->header->tx;
|
||||
params.tileY = tile->header->ty;
|
||||
params.tileLayer = tile->header->tlayer;
|
||||
params.cs = m_params.cs;
|
||||
params.ch = m_params.ch;
|
||||
params.buildBvTree = false;
|
||||
dtVcopy(params.bmin, tile->header->bmin);
|
||||
dtVcopy(params.bmax, tile->header->bmax);
|
||||
|
||||
if (m_tmproc)
|
||||
{
|
||||
m_tmproc->process(¶ms, bc.lmesh->areas, bc.lmesh->flags);
|
||||
}
|
||||
|
||||
unsigned char* navData = 0;
|
||||
int navDataSize = 0;
|
||||
if (!dtCreateNavMeshData(¶ms, &navData, &navDataSize))
|
||||
return DT_FAILURE;
|
||||
|
||||
// Remove existing tile.
|
||||
navmesh->removeTile(navmesh->getTileRefAt(tile->header->tx,tile->header->ty,tile->header->tlayer),0,0);
|
||||
|
||||
// Add new tile, or leave the location empty.
|
||||
if (navData)
|
||||
{
|
||||
// Let the navmesh own the data.
|
||||
status = navmesh->addTile(navData,navDataSize,DT_TILE_FREE_DATA,0,0);
|
||||
if (dtStatusFailed(status))
|
||||
{
|
||||
dtFree(navData);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
return DT_SUCCESS;
|
||||
}
|
||||
|
||||
void dtTileCache::calcTightTileBounds(const dtTileCacheLayerHeader* header, float* bmin, float* bmax) const
|
||||
{
|
||||
const float cs = m_params.cs;
|
||||
bmin[0] = header->bmin[0] + header->minx*cs;
|
||||
bmin[1] = header->bmin[1];
|
||||
bmin[2] = header->bmin[2] + header->miny*cs;
|
||||
bmax[0] = header->bmin[0] + (header->maxx+1)*cs;
|
||||
bmax[1] = header->bmax[1];
|
||||
bmax[2] = header->bmin[2] + (header->maxy+1)*cs;
|
||||
}
|
||||
|
||||
void dtTileCache::getObstacleBounds(const struct dtTileCacheObstacle* ob, float* bmin, float* bmax) const
|
||||
{
|
||||
bmin[0] = ob->pos[0] - ob->radius;
|
||||
bmin[1] = ob->pos[1];
|
||||
bmin[2] = ob->pos[2] - ob->radius;
|
||||
bmax[0] = ob->pos[0] + ob->radius;
|
||||
bmax[1] = ob->pos[1] + ob->height;
|
||||
bmax[2] = ob->pos[2] + ob->radius;
|
||||
}
|
||||
2152
recast/detour_tile_cache/src/DetourTileCacheBuilder.cpp
Normal file
2152
recast/detour_tile_cache/src/DetourTileCacheBuilder.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1146
recast/recast/include/Recast.h
Normal file
1146
recast/recast/include/Recast.h
Normal file
File diff suppressed because it is too large
Load Diff
124
recast/recast/include/RecastAlloc.h
Normal file
124
recast/recast/include/RecastAlloc.h
Normal file
@ -0,0 +1,124 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#ifndef RECASTALLOC_H
|
||||
#define RECASTALLOC_H
|
||||
|
||||
/// Provides hint values to the memory allocator on how long the
|
||||
/// memory is expected to be used.
|
||||
enum rcAllocHint
|
||||
{
|
||||
RC_ALLOC_PERM, ///< Memory will persist after a function call.
|
||||
RC_ALLOC_TEMP ///< Memory used temporarily within a function.
|
||||
};
|
||||
|
||||
/// A memory allocation function.
|
||||
// @param[in] size The size, in bytes of memory, to allocate.
|
||||
// @param[in] rcAllocHint A hint to the allocator on how long the memory is expected to be in use.
|
||||
// @return A pointer to the beginning of the allocated memory block, or null if the allocation failed.
|
||||
/// @see rcAllocSetCustom
|
||||
typedef void* (rcAllocFunc)(int size, rcAllocHint hint);
|
||||
|
||||
/// A memory deallocation function.
|
||||
/// @param[in] ptr A pointer to a memory block previously allocated using #rcAllocFunc.
|
||||
/// @see rcAllocSetCustom
|
||||
typedef void (rcFreeFunc)(void* ptr);
|
||||
|
||||
/// Sets the base custom allocation functions to be used by Recast.
|
||||
/// @param[in] allocFunc The memory allocation function to be used by #rcAlloc
|
||||
/// @param[in] freeFunc The memory de-allocation function to be used by #rcFree
|
||||
void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc);
|
||||
|
||||
/// Allocates a memory block.
|
||||
/// @param[in] size The size, in bytes of memory, to allocate.
|
||||
/// @param[in] hint A hint to the allocator on how long the memory is expected to be in use.
|
||||
/// @return A pointer to the beginning of the allocated memory block, or null if the allocation failed.
|
||||
/// @see rcFree
|
||||
void* rcAlloc(int size, rcAllocHint hint);
|
||||
|
||||
/// Deallocates a memory block.
|
||||
/// @param[in] ptr A pointer to a memory block previously allocated using #rcAlloc.
|
||||
/// @see rcAlloc
|
||||
void rcFree(void* ptr);
|
||||
|
||||
|
||||
/// A simple dynamic array of integers.
|
||||
class rcIntArray
|
||||
{
|
||||
int* m_data;
|
||||
int m_size, m_cap;
|
||||
inline rcIntArray(const rcIntArray&);
|
||||
inline rcIntArray& operator=(const rcIntArray&);
|
||||
public:
|
||||
|
||||
/// Constructs an instance with an initial array size of zero.
|
||||
inline rcIntArray() : m_data(0), m_size(0), m_cap(0) {}
|
||||
|
||||
/// Constructs an instance initialized to the specified size.
|
||||
/// @param[in] n The initial size of the integer array.
|
||||
inline rcIntArray(int n) : m_data(0), m_size(0), m_cap(0) { resize(n); }
|
||||
inline ~rcIntArray() { rcFree(m_data); }
|
||||
|
||||
/// Specifies the new size of the integer array.
|
||||
/// @param[in] n The new size of the integer array.
|
||||
void resize(int n);
|
||||
|
||||
/// Push the specified integer onto the end of the array and increases the size by one.
|
||||
/// @param[in] item The new value.
|
||||
inline void push(int item) { resize(m_size+1); m_data[m_size-1] = item; }
|
||||
|
||||
/// Returns the value at the end of the array and reduces the size by one.
|
||||
/// @return The value at the end of the array.
|
||||
inline int pop() { if (m_size > 0) m_size--; return m_data[m_size]; }
|
||||
|
||||
/// The value at the specified array index.
|
||||
/// @warning Does not provide overflow protection.
|
||||
/// @param[in] i The index of the value.
|
||||
inline const int& operator[](int i) const { return m_data[i]; }
|
||||
|
||||
/// The value at the specified array index.
|
||||
/// @warning Does not provide overflow protection.
|
||||
/// @param[in] i The index of the value.
|
||||
inline int& operator[](int i) { return m_data[i]; }
|
||||
|
||||
/// The current size of the integer array.
|
||||
inline int size() const { return m_size; }
|
||||
};
|
||||
|
||||
/// A simple helper class used to delete an array when it goes out of scope.
|
||||
/// @note This class is rarely if ever used by the end user.
|
||||
template<class T> class rcScopedDelete
|
||||
{
|
||||
T* ptr;
|
||||
inline T* operator=(T* p);
|
||||
public:
|
||||
|
||||
/// Constructs an instance with a null pointer.
|
||||
inline rcScopedDelete() : ptr(0) {}
|
||||
|
||||
/// Constructs an instance with the specified pointer.
|
||||
/// @param[in] p An pointer to an allocated array.
|
||||
inline rcScopedDelete(T* p) : ptr(p) {}
|
||||
inline ~rcScopedDelete() { rcFree(ptr); }
|
||||
|
||||
/// The root array pointer.
|
||||
/// @return The root array pointer.
|
||||
inline operator T*() { return ptr; }
|
||||
};
|
||||
|
||||
#endif
|
||||
33
recast/recast/include/RecastAssert.h
Normal file
33
recast/recast/include/RecastAssert.h
Normal file
@ -0,0 +1,33 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#ifndef RECASTASSERT_H
|
||||
#define RECASTASSERT_H
|
||||
|
||||
// Note: This header file's only purpose is to include define assert.
|
||||
// Feel free to change the file and include your own implementation instead.
|
||||
|
||||
#ifdef NDEBUG
|
||||
// From http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/
|
||||
# define rcAssert(x) do { (void)sizeof(x); } while((void)(__LINE__==-1),false)
|
||||
#else
|
||||
# include <assert.h>
|
||||
# define rcAssert assert
|
||||
#endif
|
||||
|
||||
#endif // RECASTASSERT_H
|
||||
489
recast/recast/src/Recast.cpp
Normal file
489
recast/recast/src/Recast.cpp
Normal file
@ -0,0 +1,489 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#include <float.h>
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include "Recast.h"
|
||||
#include "RecastAlloc.h"
|
||||
#include "RecastAssert.h"
|
||||
|
||||
float rcSqrt(float x)
|
||||
{
|
||||
return sqrtf(x);
|
||||
}
|
||||
|
||||
/// @class rcContext
|
||||
/// @par
|
||||
///
|
||||
/// This class does not provide logging or timer functionality on its
|
||||
/// own. Both must be provided by a concrete implementation
|
||||
/// by overriding the protected member functions. Also, this class does not
|
||||
/// provide an interface for extracting log messages. (Only adding them.)
|
||||
/// So concrete implementations must provide one.
|
||||
///
|
||||
/// If no logging or timers are required, just pass an instance of this
|
||||
/// class through the Recast build process.
|
||||
///
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// Example:
|
||||
/// @code
|
||||
/// // Where ctx is an instance of rcContext and filepath is a char array.
|
||||
/// ctx->log(RC_LOG_ERROR, "buildTiledNavigation: Could not load '%s'", filepath);
|
||||
/// @endcode
|
||||
void rcContext::log(const rcLogCategory category, const char* format, ...)
|
||||
{
|
||||
if (!m_logEnabled)
|
||||
return;
|
||||
static const int MSG_SIZE = 512;
|
||||
char msg[MSG_SIZE];
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
int len = vsnprintf(msg, MSG_SIZE, format, ap);
|
||||
if (len >= MSG_SIZE)
|
||||
{
|
||||
len = MSG_SIZE-1;
|
||||
msg[MSG_SIZE-1] = '\0';
|
||||
}
|
||||
va_end(ap);
|
||||
doLog(category, msg, len);
|
||||
}
|
||||
|
||||
rcHeightfield* rcAllocHeightfield()
|
||||
{
|
||||
rcHeightfield* hf = (rcHeightfield*)rcAlloc(sizeof(rcHeightfield), RC_ALLOC_PERM);
|
||||
memset(hf, 0, sizeof(rcHeightfield));
|
||||
return hf;
|
||||
}
|
||||
|
||||
void rcFreeHeightField(rcHeightfield* hf)
|
||||
{
|
||||
if (!hf) return;
|
||||
// Delete span array.
|
||||
rcFree(hf->spans);
|
||||
// Delete span pools.
|
||||
while (hf->pools)
|
||||
{
|
||||
rcSpanPool* next = hf->pools->next;
|
||||
rcFree(hf->pools);
|
||||
hf->pools = next;
|
||||
}
|
||||
rcFree(hf);
|
||||
}
|
||||
|
||||
rcCompactHeightfield* rcAllocCompactHeightfield()
|
||||
{
|
||||
rcCompactHeightfield* chf = (rcCompactHeightfield*)rcAlloc(sizeof(rcCompactHeightfield), RC_ALLOC_PERM);
|
||||
memset(chf, 0, sizeof(rcCompactHeightfield));
|
||||
return chf;
|
||||
}
|
||||
|
||||
void rcFreeCompactHeightfield(rcCompactHeightfield* chf)
|
||||
{
|
||||
if (!chf) return;
|
||||
rcFree(chf->cells);
|
||||
rcFree(chf->spans);
|
||||
rcFree(chf->dist);
|
||||
rcFree(chf->areas);
|
||||
rcFree(chf);
|
||||
}
|
||||
|
||||
|
||||
rcHeightfieldLayerSet* rcAllocHeightfieldLayerSet()
|
||||
{
|
||||
rcHeightfieldLayerSet* lset = (rcHeightfieldLayerSet*)rcAlloc(sizeof(rcHeightfieldLayerSet), RC_ALLOC_PERM);
|
||||
memset(lset, 0, sizeof(rcHeightfieldLayerSet));
|
||||
return lset;
|
||||
}
|
||||
|
||||
void rcFreeHeightfieldLayerSet(rcHeightfieldLayerSet* lset)
|
||||
{
|
||||
if (!lset) return;
|
||||
for (int i = 0; i < lset->nlayers; ++i)
|
||||
{
|
||||
rcFree(lset->layers[i].heights);
|
||||
rcFree(lset->layers[i].areas);
|
||||
rcFree(lset->layers[i].cons);
|
||||
}
|
||||
rcFree(lset->layers);
|
||||
rcFree(lset);
|
||||
}
|
||||
|
||||
|
||||
rcContourSet* rcAllocContourSet()
|
||||
{
|
||||
rcContourSet* cset = (rcContourSet*)rcAlloc(sizeof(rcContourSet), RC_ALLOC_PERM);
|
||||
memset(cset, 0, sizeof(rcContourSet));
|
||||
return cset;
|
||||
}
|
||||
|
||||
void rcFreeContourSet(rcContourSet* cset)
|
||||
{
|
||||
if (!cset) return;
|
||||
for (int i = 0; i < cset->nconts; ++i)
|
||||
{
|
||||
rcFree(cset->conts[i].verts);
|
||||
rcFree(cset->conts[i].rverts);
|
||||
}
|
||||
rcFree(cset->conts);
|
||||
rcFree(cset);
|
||||
}
|
||||
|
||||
rcPolyMesh* rcAllocPolyMesh()
|
||||
{
|
||||
rcPolyMesh* pmesh = (rcPolyMesh*)rcAlloc(sizeof(rcPolyMesh), RC_ALLOC_PERM);
|
||||
memset(pmesh, 0, sizeof(rcPolyMesh));
|
||||
return pmesh;
|
||||
}
|
||||
|
||||
void rcFreePolyMesh(rcPolyMesh* pmesh)
|
||||
{
|
||||
if (!pmesh) return;
|
||||
rcFree(pmesh->verts);
|
||||
rcFree(pmesh->polys);
|
||||
rcFree(pmesh->regs);
|
||||
rcFree(pmesh->flags);
|
||||
rcFree(pmesh->areas);
|
||||
rcFree(pmesh);
|
||||
}
|
||||
|
||||
rcPolyMeshDetail* rcAllocPolyMeshDetail()
|
||||
{
|
||||
rcPolyMeshDetail* dmesh = (rcPolyMeshDetail*)rcAlloc(sizeof(rcPolyMeshDetail), RC_ALLOC_PERM);
|
||||
memset(dmesh, 0, sizeof(rcPolyMeshDetail));
|
||||
return dmesh;
|
||||
}
|
||||
|
||||
void rcFreePolyMeshDetail(rcPolyMeshDetail* dmesh)
|
||||
{
|
||||
if (!dmesh) return;
|
||||
rcFree(dmesh->meshes);
|
||||
rcFree(dmesh->verts);
|
||||
rcFree(dmesh->tris);
|
||||
rcFree(dmesh);
|
||||
}
|
||||
|
||||
void rcCalcBounds(const float* verts, int nv, float* bmin, float* bmax)
|
||||
{
|
||||
// Calculate bounding box.
|
||||
rcVcopy(bmin, verts);
|
||||
rcVcopy(bmax, verts);
|
||||
for (int i = 1; i < nv; ++i)
|
||||
{
|
||||
const float* v = &verts[i*3];
|
||||
rcVmin(bmin, v);
|
||||
rcVmax(bmax, v);
|
||||
}
|
||||
}
|
||||
|
||||
void rcCalcGridSize(const float* bmin, const float* bmax, float cs, int* w, int* h)
|
||||
{
|
||||
*w = (int)((bmax[0] - bmin[0])/cs+0.5f);
|
||||
*h = (int)((bmax[2] - bmin[2])/cs+0.5f);
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// See the #rcConfig documentation for more information on the configuration parameters.
|
||||
///
|
||||
/// @see rcAllocHeightfield, rcHeightfield
|
||||
bool rcCreateHeightfield(rcContext* ctx, rcHeightfield& hf, int width, int height,
|
||||
const float* bmin, const float* bmax,
|
||||
float cs, float ch)
|
||||
{
|
||||
rcIgnoreUnused(ctx);
|
||||
|
||||
hf.width = width;
|
||||
hf.height = height;
|
||||
rcVcopy(hf.bmin, bmin);
|
||||
rcVcopy(hf.bmax, bmax);
|
||||
hf.cs = cs;
|
||||
hf.ch = ch;
|
||||
hf.spans = (rcSpan**)rcAlloc(sizeof(rcSpan*)*hf.width*hf.height, RC_ALLOC_PERM);
|
||||
if (!hf.spans)
|
||||
return false;
|
||||
memset(hf.spans, 0, sizeof(rcSpan*)*hf.width*hf.height);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void calcTriNormal(const float* v0, const float* v1, const float* v2, float* norm)
|
||||
{
|
||||
float e0[3], e1[3];
|
||||
rcVsub(e0, v1, v0);
|
||||
rcVsub(e1, v2, v0);
|
||||
rcVcross(norm, e0, e1);
|
||||
rcVnormalize(norm);
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// Only sets the area id's for the walkable triangles. Does not alter the
|
||||
/// area id's for unwalkable triangles.
|
||||
///
|
||||
/// See the #rcConfig documentation for more information on the configuration parameters.
|
||||
///
|
||||
/// @see rcHeightfield, rcClearUnwalkableTriangles, rcRasterizeTriangles
|
||||
void rcMarkWalkableTriangles(rcContext* ctx, const float walkableSlopeAngle,
|
||||
const float* verts, int /*nv*/,
|
||||
const int* tris, int nt,
|
||||
unsigned char* areas)
|
||||
{
|
||||
rcIgnoreUnused(ctx);
|
||||
|
||||
const float walkableThr = cosf(walkableSlopeAngle/180.0f*RC_PI);
|
||||
|
||||
float norm[3];
|
||||
|
||||
for (int i = 0; i < nt; ++i)
|
||||
{
|
||||
const int* tri = &tris[i*3];
|
||||
calcTriNormal(&verts[tri[0]*3], &verts[tri[1]*3], &verts[tri[2]*3], norm);
|
||||
// Check if the face is walkable.
|
||||
if (norm[1] > walkableThr)
|
||||
areas[i] = RC_WALKABLE_AREA;
|
||||
}
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// Only sets the area id's for the unwalkable triangles. Does not alter the
|
||||
/// area id's for walkable triangles.
|
||||
///
|
||||
/// See the #rcConfig documentation for more information on the configuration parameters.
|
||||
///
|
||||
/// @see rcHeightfield, rcClearUnwalkableTriangles, rcRasterizeTriangles
|
||||
void rcClearUnwalkableTriangles(rcContext* ctx, const float walkableSlopeAngle,
|
||||
const float* verts, int /*nv*/,
|
||||
const int* tris, int nt,
|
||||
unsigned char* areas)
|
||||
{
|
||||
rcIgnoreUnused(ctx);
|
||||
|
||||
const float walkableThr = cosf(walkableSlopeAngle/180.0f*RC_PI);
|
||||
|
||||
float norm[3];
|
||||
|
||||
for (int i = 0; i < nt; ++i)
|
||||
{
|
||||
const int* tri = &tris[i*3];
|
||||
calcTriNormal(&verts[tri[0]*3], &verts[tri[1]*3], &verts[tri[2]*3], norm);
|
||||
// Check if the face is walkable.
|
||||
if (norm[1] <= walkableThr)
|
||||
areas[i] = RC_NULL_AREA;
|
||||
}
|
||||
}
|
||||
|
||||
int rcGetHeightFieldSpanCount(rcContext* ctx, rcHeightfield& hf)
|
||||
{
|
||||
rcIgnoreUnused(ctx);
|
||||
|
||||
const int w = hf.width;
|
||||
const int h = hf.height;
|
||||
int spanCount = 0;
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
for (rcSpan* s = hf.spans[x + y*w]; s; s = s->next)
|
||||
{
|
||||
if (s->area != RC_NULL_AREA)
|
||||
spanCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return spanCount;
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// This is just the beginning of the process of fully building a compact heightfield.
|
||||
/// Various filters may be applied, then the distance field and regions built.
|
||||
/// E.g: #rcBuildDistanceField and #rcBuildRegions
|
||||
///
|
||||
/// See the #rcConfig documentation for more information on the configuration parameters.
|
||||
///
|
||||
/// @see rcAllocCompactHeightfield, rcHeightfield, rcCompactHeightfield, rcConfig
|
||||
bool rcBuildCompactHeightfield(rcContext* ctx, const int walkableHeight, const int walkableClimb,
|
||||
rcHeightfield& hf, rcCompactHeightfield& chf)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_BUILD_COMPACTHEIGHTFIELD);
|
||||
|
||||
const int w = hf.width;
|
||||
const int h = hf.height;
|
||||
const int spanCount = rcGetHeightFieldSpanCount(ctx, hf);
|
||||
|
||||
// Fill in header.
|
||||
chf.width = w;
|
||||
chf.height = h;
|
||||
chf.spanCount = spanCount;
|
||||
chf.walkableHeight = walkableHeight;
|
||||
chf.walkableClimb = walkableClimb;
|
||||
chf.maxRegions = 0;
|
||||
rcVcopy(chf.bmin, hf.bmin);
|
||||
rcVcopy(chf.bmax, hf.bmax);
|
||||
chf.bmax[1] += walkableHeight*hf.ch;
|
||||
chf.cs = hf.cs;
|
||||
chf.ch = hf.ch;
|
||||
chf.cells = (rcCompactCell*)rcAlloc(sizeof(rcCompactCell)*w*h, RC_ALLOC_PERM);
|
||||
if (!chf.cells)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.cells' (%d)", w*h);
|
||||
return false;
|
||||
}
|
||||
memset(chf.cells, 0, sizeof(rcCompactCell)*w*h);
|
||||
chf.spans = (rcCompactSpan*)rcAlloc(sizeof(rcCompactSpan)*spanCount, RC_ALLOC_PERM);
|
||||
if (!chf.spans)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.spans' (%d)", spanCount);
|
||||
return false;
|
||||
}
|
||||
memset(chf.spans, 0, sizeof(rcCompactSpan)*spanCount);
|
||||
chf.areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*spanCount, RC_ALLOC_PERM);
|
||||
if (!chf.areas)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.areas' (%d)", spanCount);
|
||||
return false;
|
||||
}
|
||||
memset(chf.areas, RC_NULL_AREA, sizeof(unsigned char)*spanCount);
|
||||
|
||||
const int MAX_HEIGHT = 0xffff;
|
||||
|
||||
// Fill in cells and spans.
|
||||
int idx = 0;
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
const rcSpan* s = hf.spans[x + y*w];
|
||||
// If there are no spans at this cell, just leave the data to index=0, count=0.
|
||||
if (!s) continue;
|
||||
rcCompactCell& c = chf.cells[x+y*w];
|
||||
c.index = idx;
|
||||
c.count = 0;
|
||||
while (s)
|
||||
{
|
||||
if (s->area != RC_NULL_AREA)
|
||||
{
|
||||
const int bot = (int)s->smax;
|
||||
const int top = s->next ? (int)s->next->smin : MAX_HEIGHT;
|
||||
chf.spans[idx].y = (unsigned short)rcClamp(bot, 0, 0xffff);
|
||||
chf.spans[idx].h = (unsigned char)rcClamp(top - bot, 0, 0xff);
|
||||
chf.areas[idx] = s->area;
|
||||
idx++;
|
||||
c.count++;
|
||||
}
|
||||
s = s->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find neighbour connections.
|
||||
const int MAX_LAYERS = RC_NOT_CONNECTED-1;
|
||||
int tooHighNeighbour = 0;
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+y*w];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
rcCompactSpan& s = chf.spans[i];
|
||||
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
rcSetCon(s, dir, RC_NOT_CONNECTED);
|
||||
const int nx = x + rcGetDirOffsetX(dir);
|
||||
const int ny = y + rcGetDirOffsetY(dir);
|
||||
// First check that the neighbour cell is in bounds.
|
||||
if (nx < 0 || ny < 0 || nx >= w || ny >= h)
|
||||
continue;
|
||||
|
||||
// Iterate over all neighbour spans and check if any of the is
|
||||
// accessible from current cell.
|
||||
const rcCompactCell& nc = chf.cells[nx+ny*w];
|
||||
for (int k = (int)nc.index, nk = (int)(nc.index+nc.count); k < nk; ++k)
|
||||
{
|
||||
const rcCompactSpan& ns = chf.spans[k];
|
||||
const int bot = rcMax(s.y, ns.y);
|
||||
const int top = rcMin(s.y+s.h, ns.y+ns.h);
|
||||
|
||||
// Check that the gap between the spans is walkable,
|
||||
// and that the climb height between the gaps is not too high.
|
||||
if ((top - bot) >= walkableHeight && rcAbs((int)ns.y - (int)s.y) <= walkableClimb)
|
||||
{
|
||||
// Mark direction as walkable.
|
||||
const int lidx = k - (int)nc.index;
|
||||
if (lidx < 0 || lidx > MAX_LAYERS)
|
||||
{
|
||||
tooHighNeighbour = rcMax(tooHighNeighbour, lidx);
|
||||
continue;
|
||||
}
|
||||
rcSetCon(s, dir, lidx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tooHighNeighbour > MAX_LAYERS)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Heightfield has too many layers %d (max: %d)",
|
||||
tooHighNeighbour, MAX_LAYERS);
|
||||
}
|
||||
|
||||
ctx->stopTimer(RC_TIMER_BUILD_COMPACTHEIGHTFIELD);
|
||||
|
||||
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;
|
||||
}
|
||||
*/
|
||||
88
recast/recast/src/RecastAlloc.cpp
Normal file
88
recast/recast/src/RecastAlloc.cpp
Normal file
@ -0,0 +1,88 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "RecastAlloc.h"
|
||||
|
||||
static void *rcAllocDefault(int size, rcAllocHint)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
static void rcFreeDefault(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
static rcAllocFunc* sRecastAllocFunc = rcAllocDefault;
|
||||
static rcFreeFunc* sRecastFreeFunc = rcFreeDefault;
|
||||
|
||||
/// @see rcAlloc, rcFree
|
||||
void rcAllocSetCustom(rcAllocFunc *allocFunc, rcFreeFunc *freeFunc)
|
||||
{
|
||||
sRecastAllocFunc = allocFunc ? allocFunc : rcAllocDefault;
|
||||
sRecastFreeFunc = freeFunc ? freeFunc : rcFreeDefault;
|
||||
}
|
||||
|
||||
/// @see rcAllocSetCustom
|
||||
void* rcAlloc(int size, rcAllocHint hint)
|
||||
{
|
||||
return sRecastAllocFunc(size, hint);
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// @warning This function leaves the value of @p ptr unchanged. So it still
|
||||
/// points to the same (now invalid) location, and not to null.
|
||||
///
|
||||
/// @see rcAllocSetCustom
|
||||
void rcFree(void* ptr)
|
||||
{
|
||||
if (ptr)
|
||||
sRecastFreeFunc(ptr);
|
||||
}
|
||||
|
||||
/// @class rcIntArray
|
||||
///
|
||||
/// While it is possible to pre-allocate a specific array size during
|
||||
/// construction or by using the #resize method, certain methods will
|
||||
/// automatically resize the array as needed.
|
||||
///
|
||||
/// @warning The array memory is not initialized to zero when the size is
|
||||
/// manually set during construction or when using #resize.
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// Using this method ensures the array is at least large enough to hold
|
||||
/// the specified number of elements. This can improve performance by
|
||||
/// avoiding auto-resizing during use.
|
||||
void rcIntArray::resize(int n)
|
||||
{
|
||||
if (n > m_cap)
|
||||
{
|
||||
if (!m_cap) m_cap = n;
|
||||
while (m_cap < n) m_cap *= 2;
|
||||
int* newData = (int*)rcAlloc(m_cap*sizeof(int), RC_ALLOC_TEMP);
|
||||
if (m_size && newData) memcpy(newData, m_data, m_size*sizeof(int));
|
||||
rcFree(m_data);
|
||||
m_data = newData;
|
||||
}
|
||||
m_size = n;
|
||||
}
|
||||
|
||||
602
recast/recast/src/RecastArea.cpp
Normal file
602
recast/recast/src/RecastArea.cpp
Normal file
@ -0,0 +1,602 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#include <float.h>
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "Recast.h"
|
||||
#include "RecastAlloc.h"
|
||||
#include "RecastAssert.h"
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// Basically, any spans that are closer to a boundary or obstruction than the specified radius
|
||||
/// are marked as unwalkable.
|
||||
///
|
||||
/// This method is usually called immediately after the heightfield has been built.
|
||||
///
|
||||
/// @see rcCompactHeightfield, rcBuildCompactHeightfield, rcConfig::walkableRadius
|
||||
bool rcErodeWalkableArea(rcContext* ctx, int radius, rcCompactHeightfield& chf)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
const int w = chf.width;
|
||||
const int h = chf.height;
|
||||
|
||||
ctx->startTimer(RC_TIMER_ERODE_AREA);
|
||||
|
||||
unsigned char* dist = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP);
|
||||
if (!dist)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "erodeWalkableArea: Out of memory 'dist' (%d).", chf.spanCount);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Init distance.
|
||||
memset(dist, 0xff, sizeof(unsigned char)*chf.spanCount);
|
||||
|
||||
// Mark boundary cells.
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+y*w];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
if (chf.areas[i] == RC_NULL_AREA)
|
||||
{
|
||||
dist[i] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
int nc = 0;
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int nx = x + rcGetDirOffsetX(dir);
|
||||
const int ny = y + rcGetDirOffsetY(dir);
|
||||
const int nidx = (int)chf.cells[nx+ny*w].index + rcGetCon(s, dir);
|
||||
if (chf.areas[nidx] != RC_NULL_AREA)
|
||||
{
|
||||
nc++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// At least one missing neighbour.
|
||||
if (nc != 4)
|
||||
dist[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char nd;
|
||||
|
||||
// Pass 1
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+y*w];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
|
||||
if (rcGetCon(s, 0) != RC_NOT_CONNECTED)
|
||||
{
|
||||
// (-1,0)
|
||||
const int ax = x + rcGetDirOffsetX(0);
|
||||
const int ay = y + rcGetDirOffsetY(0);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0);
|
||||
const rcCompactSpan& as = chf.spans[ai];
|
||||
nd = (unsigned char)rcMin((int)dist[ai]+2, 255);
|
||||
if (nd < dist[i])
|
||||
dist[i] = nd;
|
||||
|
||||
// (-1,-1)
|
||||
if (rcGetCon(as, 3) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int aax = ax + rcGetDirOffsetX(3);
|
||||
const int aay = ay + rcGetDirOffsetY(3);
|
||||
const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 3);
|
||||
nd = (unsigned char)rcMin((int)dist[aai]+3, 255);
|
||||
if (nd < dist[i])
|
||||
dist[i] = nd;
|
||||
}
|
||||
}
|
||||
if (rcGetCon(s, 3) != RC_NOT_CONNECTED)
|
||||
{
|
||||
// (0,-1)
|
||||
const int ax = x + rcGetDirOffsetX(3);
|
||||
const int ay = y + rcGetDirOffsetY(3);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3);
|
||||
const rcCompactSpan& as = chf.spans[ai];
|
||||
nd = (unsigned char)rcMin((int)dist[ai]+2, 255);
|
||||
if (nd < dist[i])
|
||||
dist[i] = nd;
|
||||
|
||||
// (1,-1)
|
||||
if (rcGetCon(as, 2) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int aax = ax + rcGetDirOffsetX(2);
|
||||
const int aay = ay + rcGetDirOffsetY(2);
|
||||
const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 2);
|
||||
nd = (unsigned char)rcMin((int)dist[aai]+3, 255);
|
||||
if (nd < dist[i])
|
||||
dist[i] = nd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pass 2
|
||||
for (int y = h-1; y >= 0; --y)
|
||||
{
|
||||
for (int x = w-1; x >= 0; --x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+y*w];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
|
||||
if (rcGetCon(s, 2) != RC_NOT_CONNECTED)
|
||||
{
|
||||
// (1,0)
|
||||
const int ax = x + rcGetDirOffsetX(2);
|
||||
const int ay = y + rcGetDirOffsetY(2);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 2);
|
||||
const rcCompactSpan& as = chf.spans[ai];
|
||||
nd = (unsigned char)rcMin((int)dist[ai]+2, 255);
|
||||
if (nd < dist[i])
|
||||
dist[i] = nd;
|
||||
|
||||
// (1,1)
|
||||
if (rcGetCon(as, 1) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int aax = ax + rcGetDirOffsetX(1);
|
||||
const int aay = ay + rcGetDirOffsetY(1);
|
||||
const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 1);
|
||||
nd = (unsigned char)rcMin((int)dist[aai]+3, 255);
|
||||
if (nd < dist[i])
|
||||
dist[i] = nd;
|
||||
}
|
||||
}
|
||||
if (rcGetCon(s, 1) != RC_NOT_CONNECTED)
|
||||
{
|
||||
// (0,1)
|
||||
const int ax = x + rcGetDirOffsetX(1);
|
||||
const int ay = y + rcGetDirOffsetY(1);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 1);
|
||||
const rcCompactSpan& as = chf.spans[ai];
|
||||
nd = (unsigned char)rcMin((int)dist[ai]+2, 255);
|
||||
if (nd < dist[i])
|
||||
dist[i] = nd;
|
||||
|
||||
// (-1,1)
|
||||
if (rcGetCon(as, 0) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int aax = ax + rcGetDirOffsetX(0);
|
||||
const int aay = ay + rcGetDirOffsetY(0);
|
||||
const int aai = (int)chf.cells[aax+aay*w].index + rcGetCon(as, 0);
|
||||
nd = (unsigned char)rcMin((int)dist[aai]+3, 255);
|
||||
if (nd < dist[i])
|
||||
dist[i] = nd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const unsigned char thr = (unsigned char)(radius*2);
|
||||
for (int i = 0; i < chf.spanCount; ++i)
|
||||
if (dist[i] < thr)
|
||||
chf.areas[i] = RC_NULL_AREA;
|
||||
|
||||
rcFree(dist);
|
||||
|
||||
ctx->stopTimer(RC_TIMER_ERODE_AREA);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void insertSort(unsigned char* a, const int n)
|
||||
{
|
||||
int i, j;
|
||||
for (i = 1; i < n; i++)
|
||||
{
|
||||
const unsigned char value = a[i];
|
||||
for (j = i - 1; j >= 0 && a[j] > value; j--)
|
||||
a[j+1] = a[j];
|
||||
a[j+1] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// This filter is usually applied after applying area id's using functions
|
||||
/// such as #rcMarkBoxArea, #rcMarkConvexPolyArea, and #rcMarkCylinderArea.
|
||||
///
|
||||
/// @see rcCompactHeightfield
|
||||
bool rcMedianFilterWalkableArea(rcContext* ctx, rcCompactHeightfield& chf)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
const int w = chf.width;
|
||||
const int h = chf.height;
|
||||
|
||||
ctx->startTimer(RC_TIMER_MEDIAN_AREA);
|
||||
|
||||
unsigned char* areas = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP);
|
||||
if (!areas)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "medianFilterWalkableArea: Out of memory 'areas' (%d).", chf.spanCount);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Init distance.
|
||||
memset(areas, 0xff, sizeof(unsigned char)*chf.spanCount);
|
||||
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+y*w];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
if (chf.areas[i] == RC_NULL_AREA)
|
||||
{
|
||||
areas[i] = chf.areas[i];
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned char nei[9];
|
||||
for (int j = 0; j < 9; ++j)
|
||||
nei[j] = chf.areas[i];
|
||||
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax = x + rcGetDirOffsetX(dir);
|
||||
const int ay = y + rcGetDirOffsetY(dir);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
|
||||
if (chf.areas[ai] != RC_NULL_AREA)
|
||||
nei[dir*2+0] = chf.areas[ai];
|
||||
|
||||
const rcCompactSpan& as = chf.spans[ai];
|
||||
const int dir2 = (dir+1) & 0x3;
|
||||
if (rcGetCon(as, dir2) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax2 = ax + rcGetDirOffsetX(dir2);
|
||||
const int ay2 = ay + rcGetDirOffsetY(dir2);
|
||||
const int ai2 = (int)chf.cells[ax2+ay2*w].index + rcGetCon(as, dir2);
|
||||
if (chf.areas[ai2] != RC_NULL_AREA)
|
||||
nei[dir*2+1] = chf.areas[ai2];
|
||||
}
|
||||
}
|
||||
}
|
||||
insertSort(nei, 9);
|
||||
areas[i] = nei[4];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(chf.areas, areas, sizeof(unsigned char)*chf.spanCount);
|
||||
|
||||
rcFree(areas);
|
||||
|
||||
ctx->stopTimer(RC_TIMER_MEDIAN_AREA);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// The value of spacial parameters are in world units.
|
||||
///
|
||||
/// @see rcCompactHeightfield, rcMedianFilterWalkableArea
|
||||
void rcMarkBoxArea(rcContext* ctx, const float* bmin, const float* bmax, unsigned char areaId,
|
||||
rcCompactHeightfield& chf)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_MARK_BOX_AREA);
|
||||
|
||||
int minx = (int)((bmin[0]-chf.bmin[0])/chf.cs);
|
||||
int miny = (int)((bmin[1]-chf.bmin[1])/chf.ch);
|
||||
int minz = (int)((bmin[2]-chf.bmin[2])/chf.cs);
|
||||
int maxx = (int)((bmax[0]-chf.bmin[0])/chf.cs);
|
||||
int maxy = (int)((bmax[1]-chf.bmin[1])/chf.ch);
|
||||
int maxz = (int)((bmax[2]-chf.bmin[2])/chf.cs);
|
||||
|
||||
if (maxx < 0) return;
|
||||
if (minx >= chf.width) return;
|
||||
if (maxz < 0) return;
|
||||
if (minz >= chf.height) return;
|
||||
|
||||
if (minx < 0) minx = 0;
|
||||
if (maxx >= chf.width) maxx = chf.width-1;
|
||||
if (minz < 0) minz = 0;
|
||||
if (maxz >= chf.height) maxz = chf.height-1;
|
||||
|
||||
for (int z = minz; z <= maxz; ++z)
|
||||
{
|
||||
for (int x = minx; x <= maxx; ++x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+z*chf.width];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
rcCompactSpan& s = chf.spans[i];
|
||||
if ((int)s.y >= miny && (int)s.y <= maxy)
|
||||
{
|
||||
if (chf.areas[i] != RC_NULL_AREA)
|
||||
chf.areas[i] = areaId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx->stopTimer(RC_TIMER_MARK_BOX_AREA);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static int pointInPoly(int nvert, const float* verts, const float* p)
|
||||
{
|
||||
int i, j, c = 0;
|
||||
for (i = 0, j = nvert-1; i < nvert; j = i++)
|
||||
{
|
||||
const float* vi = &verts[i*3];
|
||||
const float* vj = &verts[j*3];
|
||||
if (((vi[2] > p[2]) != (vj[2] > p[2])) &&
|
||||
(p[0] < (vj[0]-vi[0]) * (p[2]-vi[2]) / (vj[2]-vi[2]) + vi[0]) )
|
||||
c = !c;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// The value of spacial parameters are in world units.
|
||||
///
|
||||
/// The y-values of the polygon vertices are ignored. So the polygon is effectively
|
||||
/// projected onto the xz-plane at @p hmin, then extruded to @p hmax.
|
||||
///
|
||||
/// @see rcCompactHeightfield, rcMedianFilterWalkableArea
|
||||
void rcMarkConvexPolyArea(rcContext* ctx, const float* verts, const int nverts,
|
||||
const float hmin, const float hmax, unsigned char areaId,
|
||||
rcCompactHeightfield& chf)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_MARK_CONVEXPOLY_AREA);
|
||||
|
||||
float bmin[3], bmax[3];
|
||||
rcVcopy(bmin, verts);
|
||||
rcVcopy(bmax, verts);
|
||||
for (int i = 1; i < nverts; ++i)
|
||||
{
|
||||
rcVmin(bmin, &verts[i*3]);
|
||||
rcVmax(bmax, &verts[i*3]);
|
||||
}
|
||||
bmin[1] = hmin;
|
||||
bmax[1] = hmax;
|
||||
|
||||
int minx = (int)((bmin[0]-chf.bmin[0])/chf.cs);
|
||||
int miny = (int)((bmin[1]-chf.bmin[1])/chf.ch);
|
||||
int minz = (int)((bmin[2]-chf.bmin[2])/chf.cs);
|
||||
int maxx = (int)((bmax[0]-chf.bmin[0])/chf.cs);
|
||||
int maxy = (int)((bmax[1]-chf.bmin[1])/chf.ch);
|
||||
int maxz = (int)((bmax[2]-chf.bmin[2])/chf.cs);
|
||||
|
||||
if (maxx < 0) return;
|
||||
if (minx >= chf.width) return;
|
||||
if (maxz < 0) return;
|
||||
if (minz >= chf.height) return;
|
||||
|
||||
if (minx < 0) minx = 0;
|
||||
if (maxx >= chf.width) maxx = chf.width-1;
|
||||
if (minz < 0) minz = 0;
|
||||
if (maxz >= chf.height) maxz = chf.height-1;
|
||||
|
||||
|
||||
// TODO: Optimize.
|
||||
for (int z = minz; z <= maxz; ++z)
|
||||
{
|
||||
for (int x = minx; x <= maxx; ++x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+z*chf.width];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
rcCompactSpan& s = chf.spans[i];
|
||||
if (chf.areas[i] == RC_NULL_AREA)
|
||||
continue;
|
||||
if ((int)s.y >= miny && (int)s.y <= maxy)
|
||||
{
|
||||
float p[3];
|
||||
p[0] = chf.bmin[0] + (x+0.5f)*chf.cs;
|
||||
p[1] = 0;
|
||||
p[2] = chf.bmin[2] + (z+0.5f)*chf.cs;
|
||||
|
||||
if (pointInPoly(nverts, verts, p))
|
||||
{
|
||||
chf.areas[i] = areaId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx->stopTimer(RC_TIMER_MARK_CONVEXPOLY_AREA);
|
||||
}
|
||||
|
||||
int rcOffsetPoly(const float* verts, const int nverts, const float offset,
|
||||
float* outVerts, const int maxOutVerts)
|
||||
{
|
||||
const float MITER_LIMIT = 1.20f;
|
||||
|
||||
int n = 0;
|
||||
|
||||
for (int i = 0; i < nverts; i++)
|
||||
{
|
||||
const int a = (i+nverts-1) % nverts;
|
||||
const int b = i;
|
||||
const int c = (i+1) % nverts;
|
||||
const float* va = &verts[a*3];
|
||||
const float* vb = &verts[b*3];
|
||||
const float* vc = &verts[c*3];
|
||||
float dx0 = vb[0] - va[0];
|
||||
float dy0 = vb[2] - va[2];
|
||||
float d0 = dx0*dx0 + dy0*dy0;
|
||||
if (d0 > 1e-6f)
|
||||
{
|
||||
d0 = 1.0f/rcSqrt(d0);
|
||||
dx0 *= d0;
|
||||
dy0 *= d0;
|
||||
}
|
||||
float dx1 = vc[0] - vb[0];
|
||||
float dy1 = vc[2] - vb[2];
|
||||
float d1 = dx1*dx1 + dy1*dy1;
|
||||
if (d1 > 1e-6f)
|
||||
{
|
||||
d1 = 1.0f/rcSqrt(d1);
|
||||
dx1 *= d1;
|
||||
dy1 *= d1;
|
||||
}
|
||||
const float dlx0 = -dy0;
|
||||
const float dly0 = dx0;
|
||||
const float dlx1 = -dy1;
|
||||
const float dly1 = dx1;
|
||||
float cross = dx1*dy0 - dx0*dy1;
|
||||
float dmx = (dlx0 + dlx1) * 0.5f;
|
||||
float dmy = (dly0 + dly1) * 0.5f;
|
||||
float dmr2 = dmx*dmx + dmy*dmy;
|
||||
bool bevel = dmr2 * MITER_LIMIT*MITER_LIMIT < 1.0f;
|
||||
if (dmr2 > 1e-6f)
|
||||
{
|
||||
const float scale = 1.0f / dmr2;
|
||||
dmx *= scale;
|
||||
dmy *= scale;
|
||||
}
|
||||
|
||||
if (bevel && cross < 0.0f)
|
||||
{
|
||||
if (n+2 >= maxOutVerts)
|
||||
return 0;
|
||||
float d = (1.0f - (dx0*dx1 + dy0*dy1))*0.5f;
|
||||
outVerts[n*3+0] = vb[0] + (-dlx0+dx0*d)*offset;
|
||||
outVerts[n*3+1] = vb[1];
|
||||
outVerts[n*3+2] = vb[2] + (-dly0+dy0*d)*offset;
|
||||
n++;
|
||||
outVerts[n*3+0] = vb[0] + (-dlx1-dx1*d)*offset;
|
||||
outVerts[n*3+1] = vb[1];
|
||||
outVerts[n*3+2] = vb[2] + (-dly1-dy1*d)*offset;
|
||||
n++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (n+1 >= maxOutVerts)
|
||||
return 0;
|
||||
outVerts[n*3+0] = vb[0] - dmx*offset;
|
||||
outVerts[n*3+1] = vb[1];
|
||||
outVerts[n*3+2] = vb[2] - dmy*offset;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// The value of spacial parameters are in world units.
|
||||
///
|
||||
/// @see rcCompactHeightfield, rcMedianFilterWalkableArea
|
||||
void rcMarkCylinderArea(rcContext* ctx, const float* pos,
|
||||
const float r, const float h, unsigned char areaId,
|
||||
rcCompactHeightfield& chf)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_MARK_CYLINDER_AREA);
|
||||
|
||||
float bmin[3], bmax[3];
|
||||
bmin[0] = pos[0] - r;
|
||||
bmin[1] = pos[1];
|
||||
bmin[2] = pos[2] - r;
|
||||
bmax[0] = pos[0] + r;
|
||||
bmax[1] = pos[1] + h;
|
||||
bmax[2] = pos[2] + r;
|
||||
const float r2 = r*r;
|
||||
|
||||
int minx = (int)((bmin[0]-chf.bmin[0])/chf.cs);
|
||||
int miny = (int)((bmin[1]-chf.bmin[1])/chf.ch);
|
||||
int minz = (int)((bmin[2]-chf.bmin[2])/chf.cs);
|
||||
int maxx = (int)((bmax[0]-chf.bmin[0])/chf.cs);
|
||||
int maxy = (int)((bmax[1]-chf.bmin[1])/chf.ch);
|
||||
int maxz = (int)((bmax[2]-chf.bmin[2])/chf.cs);
|
||||
|
||||
if (maxx < 0) return;
|
||||
if (minx >= chf.width) return;
|
||||
if (maxz < 0) return;
|
||||
if (minz >= chf.height) return;
|
||||
|
||||
if (minx < 0) minx = 0;
|
||||
if (maxx >= chf.width) maxx = chf.width-1;
|
||||
if (minz < 0) minz = 0;
|
||||
if (maxz >= chf.height) maxz = chf.height-1;
|
||||
|
||||
|
||||
for (int z = minz; z <= maxz; ++z)
|
||||
{
|
||||
for (int x = minx; x <= maxx; ++x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+z*chf.width];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
rcCompactSpan& s = chf.spans[i];
|
||||
|
||||
if (chf.areas[i] == RC_NULL_AREA)
|
||||
continue;
|
||||
|
||||
if ((int)s.y >= miny && (int)s.y <= maxy)
|
||||
{
|
||||
const float sx = chf.bmin[0] + (x+0.5f)*chf.cs;
|
||||
const float sz = chf.bmin[2] + (z+0.5f)*chf.cs;
|
||||
const float dx = sx - pos[0];
|
||||
const float dz = sz - pos[2];
|
||||
|
||||
if (dx*dx + dz*dz < r2)
|
||||
{
|
||||
chf.areas[i] = areaId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx->stopTimer(RC_TIMER_MARK_CYLINDER_AREA);
|
||||
}
|
||||
1106
recast/recast/src/RecastContour.cpp
Normal file
1106
recast/recast/src/RecastContour.cpp
Normal file
File diff suppressed because it is too large
Load Diff
207
recast/recast/src/RecastFilter.cpp
Normal file
207
recast/recast/src/RecastFilter.cpp
Normal file
@ -0,0 +1,207 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include "Recast.h"
|
||||
#include "RecastAssert.h"
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// Allows the formation of walkable regions that will flow over low lying
|
||||
/// objects such as curbs, and up structures such as stairways.
|
||||
///
|
||||
/// Two neighboring spans are walkable if: <tt>rcAbs(currentSpan.smax - neighborSpan.smax) < waklableClimb</tt>
|
||||
///
|
||||
/// @warning Will override the effect of #rcFilterLedgeSpans. So if both filters are used, call
|
||||
/// #rcFilterLedgeSpans after calling this filter.
|
||||
///
|
||||
/// @see rcHeightfield, rcConfig
|
||||
void rcFilterLowHangingWalkableObstacles(rcContext* ctx, const int walkableClimb, rcHeightfield& solid)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_FILTER_LOW_OBSTACLES);
|
||||
|
||||
const int w = solid.width;
|
||||
const int h = solid.height;
|
||||
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
rcSpan* ps = 0;
|
||||
bool previousWalkable = false;
|
||||
unsigned char previousArea = RC_NULL_AREA;
|
||||
|
||||
for (rcSpan* s = solid.spans[x + y*w]; s; ps = s, s = s->next)
|
||||
{
|
||||
const bool walkable = s->area != RC_NULL_AREA;
|
||||
// If current span is not walkable, but there is walkable
|
||||
// span just below it, mark the span above it walkable too.
|
||||
if (!walkable && previousWalkable)
|
||||
{
|
||||
if (rcAbs((int)s->smax - (int)ps->smax) <= walkableClimb)
|
||||
s->area = previousArea;
|
||||
}
|
||||
// Copy walkable flag so that it cannot propagate
|
||||
// past multiple non-walkable objects.
|
||||
previousWalkable = walkable;
|
||||
previousArea = s->area;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx->stopTimer(RC_TIMER_FILTER_LOW_OBSTACLES);
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// A ledge is a span with one or more neighbors whose maximum is further away than @p walkableClimb
|
||||
/// from the current span's maximum.
|
||||
/// This method removes the impact of the overestimation of conservative voxelization
|
||||
/// so the resulting mesh will not have regions hanging in the air over ledges.
|
||||
///
|
||||
/// A span is a ledge if: <tt>rcAbs(currentSpan.smax - neighborSpan.smax) > walkableClimb</tt>
|
||||
///
|
||||
/// @see rcHeightfield, rcConfig
|
||||
void rcFilterLedgeSpans(rcContext* ctx, const int walkableHeight, const int walkableClimb,
|
||||
rcHeightfield& solid)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_FILTER_BORDER);
|
||||
|
||||
const int w = solid.width;
|
||||
const int h = solid.height;
|
||||
const int MAX_HEIGHT = 0xffff;
|
||||
|
||||
// Mark border spans.
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
for (rcSpan* s = solid.spans[x + y*w]; s; s = s->next)
|
||||
{
|
||||
// Skip non walkable spans.
|
||||
if (s->area == RC_NULL_AREA)
|
||||
continue;
|
||||
|
||||
const int bot = (int)(s->smax);
|
||||
const int top = s->next ? (int)(s->next->smin) : MAX_HEIGHT;
|
||||
|
||||
// Find neighbours minimum height.
|
||||
int minh = MAX_HEIGHT;
|
||||
|
||||
// Min and max height of accessible neighbours.
|
||||
int asmin = s->smax;
|
||||
int asmax = s->smax;
|
||||
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
int dx = x + rcGetDirOffsetX(dir);
|
||||
int dy = y + rcGetDirOffsetY(dir);
|
||||
// Skip neighbours which are out of bounds.
|
||||
if (dx < 0 || dy < 0 || dx >= w || dy >= h)
|
||||
{
|
||||
minh = rcMin(minh, -walkableClimb - bot);
|
||||
continue;
|
||||
}
|
||||
|
||||
// From minus infinity to the first span.
|
||||
rcSpan* ns = solid.spans[dx + dy*w];
|
||||
int nbot = -walkableClimb;
|
||||
int ntop = ns ? (int)ns->smin : MAX_HEIGHT;
|
||||
// Skip neightbour if the gap between the spans is too small.
|
||||
if (rcMin(top,ntop) - rcMax(bot,nbot) > walkableHeight)
|
||||
minh = rcMin(minh, nbot - bot);
|
||||
|
||||
// Rest of the spans.
|
||||
for (ns = solid.spans[dx + dy*w]; ns; ns = ns->next)
|
||||
{
|
||||
nbot = (int)ns->smax;
|
||||
ntop = ns->next ? (int)ns->next->smin : MAX_HEIGHT;
|
||||
// Skip neightbour if the gap between the spans is too small.
|
||||
if (rcMin(top,ntop) - rcMax(bot,nbot) > walkableHeight)
|
||||
{
|
||||
minh = rcMin(minh, nbot - bot);
|
||||
|
||||
// Find min/max accessible neighbour height.
|
||||
if (rcAbs(nbot - bot) <= walkableClimb)
|
||||
{
|
||||
if (nbot < asmin) asmin = nbot;
|
||||
if (nbot > asmax) asmax = nbot;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The current span is close to a ledge if the drop to any
|
||||
// neighbour span is less than the walkableClimb.
|
||||
if (minh < -walkableClimb)
|
||||
s->area = RC_NULL_AREA;
|
||||
|
||||
// If the difference between all neighbours is too large,
|
||||
// we are at steep slope, mark the span as ledge.
|
||||
if ((asmax - asmin) > walkableClimb)
|
||||
{
|
||||
s->area = RC_NULL_AREA;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx->stopTimer(RC_TIMER_FILTER_BORDER);
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// For this filter, the clearance above the span is the distance from the span's
|
||||
/// maximum to the next higher span's minimum. (Same grid column.)
|
||||
///
|
||||
/// @see rcHeightfield, rcConfig
|
||||
void rcFilterWalkableLowHeightSpans(rcContext* ctx, int walkableHeight, rcHeightfield& solid)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_FILTER_WALKABLE);
|
||||
|
||||
const int w = solid.width;
|
||||
const int h = solid.height;
|
||||
const int MAX_HEIGHT = 0xffff;
|
||||
|
||||
// Remove walkable flag from spans which do not have enough
|
||||
// space above them for the agent to stand there.
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
for (rcSpan* s = solid.spans[x + y*w]; s; s = s->next)
|
||||
{
|
||||
const int bot = (int)(s->smax);
|
||||
const int top = s->next ? (int)(s->next->smin) : MAX_HEIGHT;
|
||||
if ((top - bot) <= walkableHeight)
|
||||
s->area = RC_NULL_AREA;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx->stopTimer(RC_TIMER_FILTER_WALKABLE);
|
||||
}
|
||||
618
recast/recast/src/RecastLayers.cpp
Normal file
618
recast/recast/src/RecastLayers.cpp
Normal file
@ -0,0 +1,618 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#include <float.h>
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "Recast.h"
|
||||
#include "RecastAlloc.h"
|
||||
#include "RecastAssert.h"
|
||||
|
||||
|
||||
static const int RC_MAX_LAYERS = RC_NOT_CONNECTED;
|
||||
static const int RC_MAX_NEIS = 16;
|
||||
|
||||
struct rcLayerRegion
|
||||
{
|
||||
unsigned char layers[RC_MAX_LAYERS];
|
||||
unsigned char neis[RC_MAX_NEIS];
|
||||
unsigned short ymin, ymax;
|
||||
unsigned char layerId; // Layer ID
|
||||
unsigned char nlayers; // Layer count
|
||||
unsigned char nneis; // Neighbour count
|
||||
unsigned char base; // Flag indicating if the region is the base of merged regions.
|
||||
};
|
||||
|
||||
|
||||
static void addUnique(unsigned char* a, unsigned char& an, unsigned char v)
|
||||
{
|
||||
const int n = (int)an;
|
||||
for (int i = 0; i < n; ++i)
|
||||
if (a[i] == v)
|
||||
return;
|
||||
a[an] = v;
|
||||
an++;
|
||||
}
|
||||
|
||||
static bool contains(const unsigned char* a, const unsigned char an, const unsigned char v)
|
||||
{
|
||||
const int n = (int)an;
|
||||
for (int i = 0; i < n; ++i)
|
||||
if (a[i] == v)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool overlapRange(const unsigned short amin, const unsigned short amax,
|
||||
const unsigned short bmin, const unsigned short bmax)
|
||||
{
|
||||
return (amin > bmax || amax < bmin) ? false : true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct rcLayerSweepSpan
|
||||
{
|
||||
unsigned short ns; // number samples
|
||||
unsigned char id; // region id
|
||||
unsigned char nei; // neighbour id
|
||||
};
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// See the #rcConfig documentation for more information on the configuration parameters.
|
||||
///
|
||||
/// @see rcAllocHeightfieldLayerSet, rcCompactHeightfield, rcHeightfieldLayerSet, rcConfig
|
||||
bool rcBuildHeightfieldLayers(rcContext* ctx, rcCompactHeightfield& chf,
|
||||
const int borderSize, const int walkableHeight,
|
||||
rcHeightfieldLayerSet& lset)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_BUILD_LAYERS);
|
||||
|
||||
const int w = chf.width;
|
||||
const int h = chf.height;
|
||||
|
||||
rcScopedDelete<unsigned char> srcReg = (unsigned char*)rcAlloc(sizeof(unsigned char)*chf.spanCount, RC_ALLOC_TEMP);
|
||||
if (!srcReg)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'srcReg' (%d).", chf.spanCount);
|
||||
return false;
|
||||
}
|
||||
memset(srcReg,0xff,sizeof(unsigned char)*chf.spanCount);
|
||||
|
||||
const int nsweeps = chf.width;
|
||||
rcScopedDelete<rcLayerSweepSpan> sweeps = (rcLayerSweepSpan*)rcAlloc(sizeof(rcLayerSweepSpan)*nsweeps, RC_ALLOC_TEMP);
|
||||
if (!sweeps)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'sweeps' (%d).", nsweeps);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Partition walkable area into monotone regions.
|
||||
int prevCount[256];
|
||||
unsigned char regId = 0;
|
||||
|
||||
for (int y = borderSize; y < h-borderSize; ++y)
|
||||
{
|
||||
memset(prevCount,0,sizeof(int)*regId);
|
||||
unsigned char sweepId = 0;
|
||||
|
||||
for (int x = borderSize; x < w-borderSize; ++x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+y*w];
|
||||
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
if (chf.areas[i] == RC_NULL_AREA) continue;
|
||||
|
||||
unsigned char sid = 0xff;
|
||||
|
||||
// -x
|
||||
if (rcGetCon(s, 0) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax = x + rcGetDirOffsetX(0);
|
||||
const int ay = y + rcGetDirOffsetY(0);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0);
|
||||
if (chf.areas[ai] != RC_NULL_AREA && srcReg[ai] != 0xff)
|
||||
sid = srcReg[ai];
|
||||
}
|
||||
|
||||
if (sid == 0xff)
|
||||
{
|
||||
sid = sweepId++;
|
||||
sweeps[sid].nei = 0xff;
|
||||
sweeps[sid].ns = 0;
|
||||
}
|
||||
|
||||
// -y
|
||||
if (rcGetCon(s,3) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax = x + rcGetDirOffsetX(3);
|
||||
const int ay = y + rcGetDirOffsetY(3);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3);
|
||||
const unsigned char nr = srcReg[ai];
|
||||
if (nr != 0xff)
|
||||
{
|
||||
// Set neighbour when first valid neighbour is encoutered.
|
||||
if (sweeps[sid].ns == 0)
|
||||
sweeps[sid].nei = nr;
|
||||
|
||||
if (sweeps[sid].nei == nr)
|
||||
{
|
||||
// Update existing neighbour
|
||||
sweeps[sid].ns++;
|
||||
prevCount[nr]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is hit if there is nore than one neighbour.
|
||||
// Invalidate the neighbour.
|
||||
sweeps[sid].nei = 0xff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
srcReg[i] = sid;
|
||||
}
|
||||
}
|
||||
|
||||
// Create unique ID.
|
||||
for (int i = 0; i < sweepId; ++i)
|
||||
{
|
||||
// If the neighbour is set and there is only one continuous connection to it,
|
||||
// the sweep will be merged with the previous one, else new region is created.
|
||||
if (sweeps[i].nei != 0xff && prevCount[sweeps[i].nei] == (int)sweeps[i].ns)
|
||||
{
|
||||
sweeps[i].id = sweeps[i].nei;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (regId == 255)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Region ID overflow.");
|
||||
return false;
|
||||
}
|
||||
sweeps[i].id = regId++;
|
||||
}
|
||||
}
|
||||
|
||||
// Remap local sweep ids to region ids.
|
||||
for (int x = borderSize; x < w-borderSize; ++x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+y*w];
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
if (srcReg[i] != 0xff)
|
||||
srcReg[i] = sweeps[srcReg[i]].id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate and init layer regions.
|
||||
const int nregs = (int)regId;
|
||||
rcScopedDelete<rcLayerRegion> regs = (rcLayerRegion*)rcAlloc(sizeof(rcLayerRegion)*nregs, RC_ALLOC_TEMP);
|
||||
if (!regs)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'regs' (%d).", nregs);
|
||||
return false;
|
||||
}
|
||||
memset(regs, 0, sizeof(rcLayerRegion)*nregs);
|
||||
for (int i = 0; i < nregs; ++i)
|
||||
{
|
||||
regs[i].layerId = 0xff;
|
||||
regs[i].ymin = 0xffff;
|
||||
regs[i].ymax = 0;
|
||||
}
|
||||
|
||||
// Find region neighbours and overlapping regions.
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
for (int x = 0; x < w; ++x)
|
||||
{
|
||||
const rcCompactCell& c = chf.cells[x+y*w];
|
||||
|
||||
unsigned char lregs[RC_MAX_LAYERS];
|
||||
int nlregs = 0;
|
||||
|
||||
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
|
||||
{
|
||||
const rcCompactSpan& s = chf.spans[i];
|
||||
const unsigned char ri = srcReg[i];
|
||||
if (ri == 0xff) continue;
|
||||
|
||||
regs[ri].ymin = rcMin(regs[ri].ymin, s.y);
|
||||
regs[ri].ymax = rcMax(regs[ri].ymax, s.y);
|
||||
|
||||
// Collect all region layers.
|
||||
if (nlregs < RC_MAX_LAYERS)
|
||||
lregs[nlregs++] = ri;
|
||||
|
||||
// Update neighbours
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax = x + rcGetDirOffsetX(dir);
|
||||
const int ay = y + rcGetDirOffsetY(dir);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
|
||||
const unsigned char rai = srcReg[ai];
|
||||
if (rai != 0xff && rai != ri)
|
||||
addUnique(regs[ri].neis, regs[ri].nneis, rai);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Update overlapping regions.
|
||||
for (int i = 0; i < nlregs-1; ++i)
|
||||
{
|
||||
for (int j = i+1; j < nlregs; ++j)
|
||||
{
|
||||
if (lregs[i] != lregs[j])
|
||||
{
|
||||
rcLayerRegion& ri = regs[lregs[i]];
|
||||
rcLayerRegion& rj = regs[lregs[j]];
|
||||
addUnique(ri.layers, ri.nlayers, lregs[j]);
|
||||
addUnique(rj.layers, rj.nlayers, lregs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Create 2D layers from regions.
|
||||
unsigned char layerId = 0;
|
||||
|
||||
static const int MAX_STACK = 64;
|
||||
unsigned char stack[MAX_STACK];
|
||||
int nstack = 0;
|
||||
|
||||
for (int i = 0; i < nregs; ++i)
|
||||
{
|
||||
rcLayerRegion& root = regs[i];
|
||||
// Skip already visited.
|
||||
if (root.layerId != 0xff)
|
||||
continue;
|
||||
|
||||
// Start search.
|
||||
root.layerId = layerId;
|
||||
root.base = 1;
|
||||
|
||||
nstack = 0;
|
||||
stack[nstack++] = (unsigned char)i;
|
||||
|
||||
while (nstack)
|
||||
{
|
||||
// Pop front
|
||||
rcLayerRegion& reg = regs[stack[0]];
|
||||
nstack--;
|
||||
for (int j = 0; j < nstack; ++j)
|
||||
stack[j] = stack[j+1];
|
||||
|
||||
const int nneis = (int)reg.nneis;
|
||||
for (int j = 0; j < nneis; ++j)
|
||||
{
|
||||
const unsigned char nei = reg.neis[j];
|
||||
rcLayerRegion& regn = regs[nei];
|
||||
// Skip already visited.
|
||||
if (regn.layerId != 0xff)
|
||||
continue;
|
||||
// Skip if the neighbour is overlapping root region.
|
||||
if (contains(root.layers, root.nlayers, nei))
|
||||
continue;
|
||||
// Skip if the height range would become too large.
|
||||
const int ymin = rcMin(root.ymin, regn.ymin);
|
||||
const int ymax = rcMax(root.ymax, regn.ymax);
|
||||
if ((ymax - ymin) >= 255)
|
||||
continue;
|
||||
|
||||
if (nstack < MAX_STACK)
|
||||
{
|
||||
// Deepen
|
||||
stack[nstack++] = (unsigned char)nei;
|
||||
|
||||
// Mark layer id
|
||||
regn.layerId = layerId;
|
||||
// Merge current layers to root.
|
||||
for (int k = 0; k < regn.nlayers; ++k)
|
||||
addUnique(root.layers, root.nlayers, regn.layers[k]);
|
||||
root.ymin = rcMin(root.ymin, regn.ymin);
|
||||
root.ymax = rcMax(root.ymax, regn.ymax);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layerId++;
|
||||
}
|
||||
|
||||
// Merge non-overlapping regions that are close in height.
|
||||
const unsigned short mergeHeight = (unsigned short)walkableHeight * 4;
|
||||
|
||||
for (int i = 0; i < nregs; ++i)
|
||||
{
|
||||
rcLayerRegion& ri = regs[i];
|
||||
if (!ri.base) continue;
|
||||
|
||||
unsigned char newId = ri.layerId;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
unsigned char oldId = 0xff;
|
||||
|
||||
for (int j = 0; j < nregs; ++j)
|
||||
{
|
||||
if (i == j) continue;
|
||||
rcLayerRegion& rj = regs[j];
|
||||
if (!rj.base) continue;
|
||||
|
||||
// Skip if the regions are not close to each other.
|
||||
if (!overlapRange(ri.ymin,ri.ymax+mergeHeight, rj.ymin,rj.ymax+mergeHeight))
|
||||
continue;
|
||||
// Skip if the height range would become too large.
|
||||
const int ymin = rcMin(ri.ymin, rj.ymin);
|
||||
const int ymax = rcMax(ri.ymax, rj.ymax);
|
||||
if ((ymax - ymin) >= 255)
|
||||
continue;
|
||||
|
||||
// Make sure that there is no overlap when merging 'ri' and 'rj'.
|
||||
bool overlap = false;
|
||||
// Iterate over all regions which have the same layerId as 'rj'
|
||||
for (int k = 0; k < nregs; ++k)
|
||||
{
|
||||
if (regs[k].layerId != rj.layerId)
|
||||
continue;
|
||||
// Check if region 'k' is overlapping region 'ri'
|
||||
// Index to 'regs' is the same as region id.
|
||||
if (contains(ri.layers,ri.nlayers, (unsigned char)k))
|
||||
{
|
||||
overlap = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Cannot merge of regions overlap.
|
||||
if (overlap)
|
||||
continue;
|
||||
|
||||
// Can merge i and j.
|
||||
oldId = rj.layerId;
|
||||
break;
|
||||
}
|
||||
|
||||
// Could not find anything to merge with, stop.
|
||||
if (oldId == 0xff)
|
||||
break;
|
||||
|
||||
// Merge
|
||||
for (int j = 0; j < nregs; ++j)
|
||||
{
|
||||
rcLayerRegion& rj = regs[j];
|
||||
if (rj.layerId == oldId)
|
||||
{
|
||||
rj.base = 0;
|
||||
// Remap layerIds.
|
||||
rj.layerId = newId;
|
||||
// Add overlaid layers from 'rj' to 'ri'.
|
||||
for (int k = 0; k < rj.nlayers; ++k)
|
||||
addUnique(ri.layers, ri.nlayers, rj.layers[k]);
|
||||
// Update height bounds.
|
||||
ri.ymin = rcMin(ri.ymin, rj.ymin);
|
||||
ri.ymax = rcMax(ri.ymax, rj.ymax);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compact layerIds
|
||||
unsigned char remap[256];
|
||||
memset(remap, 0, 256);
|
||||
|
||||
// Find number of unique layers.
|
||||
layerId = 0;
|
||||
for (int i = 0; i < nregs; ++i)
|
||||
remap[regs[i].layerId] = 1;
|
||||
for (int i = 0; i < 256; ++i)
|
||||
{
|
||||
if (remap[i])
|
||||
remap[i] = layerId++;
|
||||
else
|
||||
remap[i] = 0xff;
|
||||
}
|
||||
// Remap ids.
|
||||
for (int i = 0; i < nregs; ++i)
|
||||
regs[i].layerId = remap[regs[i].layerId];
|
||||
|
||||
// No layers, return empty.
|
||||
if (layerId == 0)
|
||||
{
|
||||
ctx->stopTimer(RC_TIMER_BUILD_LAYERS);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create layers.
|
||||
rcAssert(lset.layers == 0);
|
||||
|
||||
const int lw = w - borderSize*2;
|
||||
const int lh = h - borderSize*2;
|
||||
|
||||
// Build contracted bbox for layers.
|
||||
float bmin[3], bmax[3];
|
||||
rcVcopy(bmin, chf.bmin);
|
||||
rcVcopy(bmax, chf.bmax);
|
||||
bmin[0] += borderSize*chf.cs;
|
||||
bmin[2] += borderSize*chf.cs;
|
||||
bmax[0] -= borderSize*chf.cs;
|
||||
bmax[2] -= borderSize*chf.cs;
|
||||
|
||||
lset.nlayers = (int)layerId;
|
||||
|
||||
lset.layers = (rcHeightfieldLayer*)rcAlloc(sizeof(rcHeightfieldLayer)*lset.nlayers, RC_ALLOC_PERM);
|
||||
if (!lset.layers)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'layers' (%d).", lset.nlayers);
|
||||
return false;
|
||||
}
|
||||
memset(lset.layers, 0, sizeof(rcHeightfieldLayer)*lset.nlayers);
|
||||
|
||||
|
||||
// Store layers.
|
||||
for (int i = 0; i < lset.nlayers; ++i)
|
||||
{
|
||||
unsigned char curId = (unsigned char)i;
|
||||
|
||||
rcHeightfieldLayer* layer = &lset.layers[i];
|
||||
|
||||
const int gridSize = sizeof(unsigned char)*lw*lh;
|
||||
|
||||
layer->heights = (unsigned char*)rcAlloc(gridSize, RC_ALLOC_PERM);
|
||||
if (!layer->heights)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'heights' (%d).", gridSize);
|
||||
return false;
|
||||
}
|
||||
memset(layer->heights, 0xff, gridSize);
|
||||
|
||||
layer->areas = (unsigned char*)rcAlloc(gridSize, RC_ALLOC_PERM);
|
||||
if (!layer->areas)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'areas' (%d).", gridSize);
|
||||
return false;
|
||||
}
|
||||
memset(layer->areas, 0, gridSize);
|
||||
|
||||
layer->cons = (unsigned char*)rcAlloc(gridSize, RC_ALLOC_PERM);
|
||||
if (!layer->cons)
|
||||
{
|
||||
ctx->log(RC_LOG_ERROR, "rcBuildHeightfieldLayers: Out of memory 'cons' (%d).", gridSize);
|
||||
return false;
|
||||
}
|
||||
memset(layer->cons, 0, gridSize);
|
||||
|
||||
// Find layer height bounds.
|
||||
int hmin = 0, hmax = 0;
|
||||
for (int j = 0; j < nregs; ++j)
|
||||
{
|
||||
if (regs[j].base && regs[j].layerId == curId)
|
||||
{
|
||||
hmin = (int)regs[j].ymin;
|
||||
hmax = (int)regs[j].ymax;
|
||||
}
|
||||
}
|
||||
|
||||
layer->width = lw;
|
||||
layer->height = lh;
|
||||
layer->cs = chf.cs;
|
||||
layer->ch = chf.ch;
|
||||
|
||||
// Adjust the bbox to fit the heightfield.
|
||||
rcVcopy(layer->bmin, bmin);
|
||||
rcVcopy(layer->bmax, bmax);
|
||||
layer->bmin[1] = bmin[1] + hmin*chf.ch;
|
||||
layer->bmax[1] = bmin[1] + hmax*chf.ch;
|
||||
layer->hmin = hmin;
|
||||
layer->hmax = hmax;
|
||||
|
||||
// Update usable data region.
|
||||
layer->minx = layer->width;
|
||||
layer->maxx = 0;
|
||||
layer->miny = layer->height;
|
||||
layer->maxy = 0;
|
||||
|
||||
// Copy height and area from compact heightfield.
|
||||
for (int y = 0; y < lh; ++y)
|
||||
{
|
||||
for (int x = 0; x < lw; ++x)
|
||||
{
|
||||
const int cx = borderSize+x;
|
||||
const int cy = borderSize+y;
|
||||
const rcCompactCell& c = chf.cells[cx+cy*w];
|
||||
for (int j = (int)c.index, nj = (int)(c.index+c.count); j < nj; ++j)
|
||||
{
|
||||
const rcCompactSpan& s = chf.spans[j];
|
||||
// Skip unassigned regions.
|
||||
if (srcReg[j] == 0xff)
|
||||
continue;
|
||||
// Skip of does nto belong to current layer.
|
||||
unsigned char lid = regs[srcReg[j]].layerId;
|
||||
if (lid != curId)
|
||||
continue;
|
||||
|
||||
// Update data bounds.
|
||||
layer->minx = rcMin(layer->minx, x);
|
||||
layer->maxx = rcMax(layer->maxx, x);
|
||||
layer->miny = rcMin(layer->miny, y);
|
||||
layer->maxy = rcMax(layer->maxy, y);
|
||||
|
||||
// Store height and area type.
|
||||
const int idx = x+y*lw;
|
||||
layer->heights[idx] = (unsigned char)(s.y - hmin);
|
||||
layer->areas[idx] = chf.areas[j];
|
||||
|
||||
// Check connection.
|
||||
unsigned char portal = 0;
|
||||
unsigned char con = 0;
|
||||
for (int dir = 0; dir < 4; ++dir)
|
||||
{
|
||||
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
|
||||
{
|
||||
const int ax = cx + rcGetDirOffsetX(dir);
|
||||
const int ay = cy + rcGetDirOffsetY(dir);
|
||||
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
|
||||
unsigned char alid = srcReg[ai] != 0xff ? regs[srcReg[ai]].layerId : 0xff;
|
||||
// Portal mask
|
||||
if (chf.areas[ai] != RC_NULL_AREA && lid != alid)
|
||||
{
|
||||
portal |= (unsigned char)(1<<dir);
|
||||
// Update height so that it matches on both sides of the portal.
|
||||
const rcCompactSpan& as = chf.spans[ai];
|
||||
if (as.y > hmin)
|
||||
layer->heights[idx] = rcMax(layer->heights[idx], (unsigned char)(as.y - hmin));
|
||||
}
|
||||
// Valid connection mask
|
||||
if (chf.areas[ai] != RC_NULL_AREA && lid == alid)
|
||||
{
|
||||
const int nx = ax - borderSize;
|
||||
const int ny = ay - borderSize;
|
||||
if (nx >= 0 && ny >= 0 && nx < lw && ny < lh)
|
||||
con |= (unsigned char)(1<<dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layer->cons[idx] = (portal << 4) | con;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (layer->minx > layer->maxx)
|
||||
layer->minx = layer->maxx = 0;
|
||||
if (layer->miny > layer->maxy)
|
||||
layer->miny = layer->maxy = 0;
|
||||
}
|
||||
|
||||
ctx->stopTimer(RC_TIMER_BUILD_LAYERS);
|
||||
|
||||
return true;
|
||||
}
|
||||
1544
recast/recast/src/RecastMesh.cpp
Normal file
1544
recast/recast/src/RecastMesh.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1412
recast/recast/src/RecastMeshDetail.cpp
Normal file
1412
recast/recast/src/RecastMeshDetail.cpp
Normal file
File diff suppressed because it is too large
Load Diff
425
recast/recast/src/RecastRasterization.cpp
Normal file
425
recast/recast/src/RecastRasterization.cpp
Normal file
@ -0,0 +1,425 @@
|
||||
//
|
||||
// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include "Recast.h"
|
||||
#include "RecastAlloc.h"
|
||||
#include "RecastAssert.h"
|
||||
|
||||
inline bool overlapBounds(const float* amin, const float* amax, const float* bmin, const float* bmax)
|
||||
{
|
||||
bool overlap = true;
|
||||
overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap;
|
||||
overlap = (amin[1] > bmax[1] || amax[1] < bmin[1]) ? false : overlap;
|
||||
overlap = (amin[2] > bmax[2] || amax[2] < bmin[2]) ? false : overlap;
|
||||
return overlap;
|
||||
}
|
||||
|
||||
inline bool overlapInterval(unsigned short amin, unsigned short amax,
|
||||
unsigned short bmin, unsigned short bmax)
|
||||
{
|
||||
if (amax < bmin) return false;
|
||||
if (amin > bmax) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static rcSpan* allocSpan(rcHeightfield& hf)
|
||||
{
|
||||
// If running out of memory, allocate new page and update the freelist.
|
||||
if (!hf.freelist || !hf.freelist->next)
|
||||
{
|
||||
// Create new page.
|
||||
// Allocate memory for the new pool.
|
||||
rcSpanPool* pool = (rcSpanPool*)rcAlloc(sizeof(rcSpanPool), RC_ALLOC_PERM);
|
||||
if (!pool) return 0;
|
||||
|
||||
// Add the pool into the list of pools.
|
||||
pool->next = hf.pools;
|
||||
hf.pools = pool;
|
||||
// Add new items to the free list.
|
||||
rcSpan* freelist = hf.freelist;
|
||||
rcSpan* head = &pool->items[0];
|
||||
rcSpan* it = &pool->items[RC_SPANS_PER_POOL];
|
||||
do
|
||||
{
|
||||
--it;
|
||||
it->next = freelist;
|
||||
freelist = it;
|
||||
}
|
||||
while (it != head);
|
||||
hf.freelist = it;
|
||||
}
|
||||
|
||||
// Pop item from in front of the free list.
|
||||
rcSpan* it = hf.freelist;
|
||||
hf.freelist = hf.freelist->next;
|
||||
return it;
|
||||
}
|
||||
|
||||
static void freeSpan(rcHeightfield& hf, rcSpan* ptr)
|
||||
{
|
||||
if (!ptr) return;
|
||||
// Add the node in front of the free list.
|
||||
ptr->next = hf.freelist;
|
||||
hf.freelist = ptr;
|
||||
}
|
||||
|
||||
static void addSpan(rcHeightfield& hf, const int x, const int y,
|
||||
const unsigned short smin, const unsigned short smax,
|
||||
const unsigned char area, const int flagMergeThr)
|
||||
{
|
||||
|
||||
int idx = x + y*hf.width;
|
||||
|
||||
rcSpan* s = allocSpan(hf);
|
||||
s->smin = smin;
|
||||
s->smax = smax;
|
||||
s->area = area;
|
||||
s->next = 0;
|
||||
|
||||
// Empty cell, add the first span.
|
||||
if (!hf.spans[idx])
|
||||
{
|
||||
hf.spans[idx] = s;
|
||||
return;
|
||||
}
|
||||
rcSpan* prev = 0;
|
||||
rcSpan* cur = hf.spans[idx];
|
||||
|
||||
// Insert and merge spans.
|
||||
while (cur)
|
||||
{
|
||||
if (cur->smin > s->smax)
|
||||
{
|
||||
// Current span is further than the new span, break.
|
||||
break;
|
||||
}
|
||||
else if (cur->smax < s->smin)
|
||||
{
|
||||
// Current span is before the new span advance.
|
||||
prev = cur;
|
||||
cur = cur->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Merge spans.
|
||||
if (cur->smin < s->smin)
|
||||
s->smin = cur->smin;
|
||||
if (cur->smax > s->smax)
|
||||
s->smax = cur->smax;
|
||||
|
||||
// Merge flags.
|
||||
if (rcAbs((int)s->smax - (int)cur->smax) <= flagMergeThr)
|
||||
s->area = rcMax(s->area, cur->area);
|
||||
|
||||
// Remove current span.
|
||||
rcSpan* next = cur->next;
|
||||
freeSpan(hf, cur);
|
||||
if (prev)
|
||||
prev->next = next;
|
||||
else
|
||||
hf.spans[idx] = next;
|
||||
cur = next;
|
||||
}
|
||||
}
|
||||
|
||||
// Insert new span.
|
||||
if (prev)
|
||||
{
|
||||
s->next = prev->next;
|
||||
prev->next = s;
|
||||
}
|
||||
else
|
||||
{
|
||||
s->next = hf.spans[idx];
|
||||
hf.spans[idx] = s;
|
||||
}
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// The span addition can be set to favor flags. If the span is merged to
|
||||
/// another span and the new @p smax is within @p flagMergeThr units
|
||||
/// from the existing span, the span flags are merged.
|
||||
///
|
||||
/// @see rcHeightfield, rcSpan.
|
||||
void rcAddSpan(rcContext* /*ctx*/, rcHeightfield& hf, const int x, const int y,
|
||||
const unsigned short smin, const unsigned short smax,
|
||||
const unsigned char area, const int flagMergeThr)
|
||||
{
|
||||
// rcAssert(ctx);
|
||||
addSpan(hf, x,y, smin, smax, area, flagMergeThr);
|
||||
}
|
||||
|
||||
// divides a convex polygons into two convex polygons on both sides of a line
|
||||
static void dividePoly(const float* in, int nin,
|
||||
float* out1, int* nout1,
|
||||
float* out2, int* nout2,
|
||||
float x, int axis)
|
||||
{
|
||||
float d[12];
|
||||
for (int i = 0; i < nin; ++i)
|
||||
d[i] = x - in[i*3+axis];
|
||||
|
||||
int m = 0, n = 0;
|
||||
for (int i = 0, j = nin-1; i < nin; j=i, ++i)
|
||||
{
|
||||
bool ina = d[j] >= 0;
|
||||
bool inb = d[i] >= 0;
|
||||
if (ina != inb)
|
||||
{
|
||||
float s = d[j] / (d[j] - d[i]);
|
||||
out1[m*3+0] = in[j*3+0] + (in[i*3+0] - in[j*3+0])*s;
|
||||
out1[m*3+1] = in[j*3+1] + (in[i*3+1] - in[j*3+1])*s;
|
||||
out1[m*3+2] = in[j*3+2] + (in[i*3+2] - in[j*3+2])*s;
|
||||
rcVcopy(out2 + n*3, out1 + m*3);
|
||||
m++;
|
||||
n++;
|
||||
// add the i'th point to the right polygon. Do NOT add points that are on the dividing line
|
||||
// since these were already added above
|
||||
if (d[i] > 0)
|
||||
{
|
||||
rcVcopy(out1 + m*3, in + i*3);
|
||||
m++;
|
||||
}
|
||||
else if (d[i] < 0)
|
||||
{
|
||||
rcVcopy(out2 + n*3, in + i*3);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
else // same side
|
||||
{
|
||||
// add the i'th point to the right polygon. Addition is done even for points on the dividing line
|
||||
if (d[i] >= 0)
|
||||
{
|
||||
rcVcopy(out1 + m*3, in + i*3);
|
||||
m++;
|
||||
if (d[i] != 0)
|
||||
continue;
|
||||
}
|
||||
rcVcopy(out2 + n*3, in + i*3);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
*nout1 = m;
|
||||
*nout2 = n;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void rasterizeTri(const float* v0, const float* v1, const float* v2,
|
||||
const unsigned char area, rcHeightfield& hf,
|
||||
const float* bmin, const float* bmax,
|
||||
const float cs, const float ics, const float ich,
|
||||
const int flagMergeThr)
|
||||
{
|
||||
const int w = hf.width;
|
||||
const int h = hf.height;
|
||||
float tmin[3], tmax[3];
|
||||
const float by = bmax[1] - bmin[1];
|
||||
|
||||
// Calculate the bounding box of the triangle.
|
||||
rcVcopy(tmin, v0);
|
||||
rcVcopy(tmax, v0);
|
||||
rcVmin(tmin, v1);
|
||||
rcVmin(tmin, v2);
|
||||
rcVmax(tmax, v1);
|
||||
rcVmax(tmax, v2);
|
||||
|
||||
// If the triangle does not touch the bbox of the heightfield, skip the triagle.
|
||||
if (!overlapBounds(bmin, bmax, tmin, tmax))
|
||||
return;
|
||||
|
||||
// Calculate the footprint of the triangle on the grid's y-axis
|
||||
int y0 = (int)((tmin[2] - bmin[2])*ics);
|
||||
int y1 = (int)((tmax[2] - bmin[2])*ics);
|
||||
y0 = rcClamp(y0, 0, h-1);
|
||||
y1 = rcClamp(y1, 0, h-1);
|
||||
|
||||
// Clip the triangle into all grid cells it touches.
|
||||
float buf[7*3*4];
|
||||
float *in = buf, *inrow = buf+7*3, *p1 = inrow+7*3, *p2 = p1+7*3;
|
||||
|
||||
rcVcopy(&in[0], v0);
|
||||
rcVcopy(&in[1*3], v1);
|
||||
rcVcopy(&in[2*3], v2);
|
||||
int nvrow, nvIn = 3;
|
||||
|
||||
for (int y = y0; y <= y1; ++y)
|
||||
{
|
||||
// Clip polygon to row. Store the remaining polygon as well
|
||||
const float cz = bmin[2] + y*cs;
|
||||
dividePoly(in, nvIn, inrow, &nvrow, p1, &nvIn, cz+cs, 2);
|
||||
rcSwap(in, p1);
|
||||
if (nvrow < 3) continue;
|
||||
|
||||
// find the horizontal bounds in the row
|
||||
float minX = inrow[0], maxX = inrow[0];
|
||||
for (int i=1; i<nvrow; ++i)
|
||||
{
|
||||
if (minX > inrow[i*3]) minX = inrow[i*3];
|
||||
if (maxX < inrow[i*3]) maxX = inrow[i*3];
|
||||
}
|
||||
int x0 = (int)((minX - bmin[0])*ics);
|
||||
int x1 = (int)((maxX - bmin[0])*ics);
|
||||
x0 = rcClamp(x0, 0, w-1);
|
||||
x1 = rcClamp(x1, 0, w-1);
|
||||
|
||||
int nv, nv2 = nvrow;
|
||||
|
||||
for (int x = x0; x <= x1; ++x)
|
||||
{
|
||||
// Clip polygon to column. store the remaining polygon as well
|
||||
const float cx = bmin[0] + x*cs;
|
||||
dividePoly(inrow, nv2, p1, &nv, p2, &nv2, cx+cs, 0);
|
||||
rcSwap(inrow, p2);
|
||||
if (nv < 3) continue;
|
||||
|
||||
// Calculate min and max of the span.
|
||||
float smin = p1[1], smax = p1[1];
|
||||
for (int i = 1; i < nv; ++i)
|
||||
{
|
||||
smin = rcMin(smin, p1[i*3+1]);
|
||||
smax = rcMax(smax, p1[i*3+1]);
|
||||
}
|
||||
smin -= bmin[1];
|
||||
smax -= bmin[1];
|
||||
// Skip the span if it is outside the heightfield bbox
|
||||
if (smax < 0.0f) continue;
|
||||
if (smin > by) continue;
|
||||
// Clamp the span to the heightfield bbox.
|
||||
if (smin < 0.0f) smin = 0;
|
||||
if (smax > by) smax = by;
|
||||
|
||||
// Snap the span to the heightfield height grid.
|
||||
unsigned short ismin = (unsigned short)rcClamp((int)floorf(smin * ich), 0, RC_SPAN_MAX_HEIGHT);
|
||||
unsigned short ismax = (unsigned short)rcClamp((int)ceilf(smax * ich), (int)ismin+1, RC_SPAN_MAX_HEIGHT);
|
||||
|
||||
addSpan(hf, x, y, ismin, ismax, area, flagMergeThr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// No spans will be added if the triangle does not overlap the heightfield grid.
|
||||
///
|
||||
/// @see rcHeightfield
|
||||
void rcRasterizeTriangle(rcContext* ctx, const float* v0, const float* v1, const float* v2,
|
||||
const unsigned char area, rcHeightfield& solid,
|
||||
const int flagMergeThr)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
|
||||
const float ics = 1.0f/solid.cs;
|
||||
const float ich = 1.0f/solid.ch;
|
||||
rasterizeTri(v0, v1, v2, area, solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
|
||||
|
||||
ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// Spans will only be added for triangles that overlap the heightfield grid.
|
||||
///
|
||||
/// @see rcHeightfield
|
||||
void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/,
|
||||
const int* tris, const unsigned char* areas, const int nt,
|
||||
rcHeightfield& solid, const int flagMergeThr)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
|
||||
const float ics = 1.0f/solid.cs;
|
||||
const float ich = 1.0f/solid.ch;
|
||||
// Rasterize triangles.
|
||||
for (int i = 0; i < nt; ++i)
|
||||
{
|
||||
const float* v0 = &verts[tris[i*3+0]*3];
|
||||
const float* v1 = &verts[tris[i*3+1]*3];
|
||||
const float* v2 = &verts[tris[i*3+2]*3];
|
||||
|
||||
// Rasterize.
|
||||
rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
|
||||
}
|
||||
|
||||
ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// Spans will only be added for triangles that overlap the heightfield grid.
|
||||
///
|
||||
/// @see rcHeightfield
|
||||
void rcRasterizeTriangles(rcContext* ctx, const float* verts, const int /*nv*/,
|
||||
const unsigned short* tris, const unsigned char* areas, const int nt,
|
||||
rcHeightfield& solid, const int flagMergeThr)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
|
||||
const float ics = 1.0f/solid.cs;
|
||||
const float ich = 1.0f/solid.ch;
|
||||
// Rasterize triangles.
|
||||
for (int i = 0; i < nt; ++i)
|
||||
{
|
||||
const float* v0 = &verts[tris[i*3+0]*3];
|
||||
const float* v1 = &verts[tris[i*3+1]*3];
|
||||
const float* v2 = &verts[tris[i*3+2]*3];
|
||||
// Rasterize.
|
||||
rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
|
||||
}
|
||||
|
||||
ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
}
|
||||
|
||||
/// @par
|
||||
///
|
||||
/// Spans will only be added for triangles that overlap the heightfield grid.
|
||||
///
|
||||
/// @see rcHeightfield
|
||||
void rcRasterizeTriangles(rcContext* ctx, const float* verts, const unsigned char* areas, const int nt,
|
||||
rcHeightfield& solid, const int flagMergeThr)
|
||||
{
|
||||
rcAssert(ctx);
|
||||
|
||||
ctx->startTimer(RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
|
||||
const float ics = 1.0f/solid.cs;
|
||||
const float ich = 1.0f/solid.ch;
|
||||
// Rasterize triangles.
|
||||
for (int i = 0; i < nt; ++i)
|
||||
{
|
||||
const float* v0 = &verts[(i*3+0)*3];
|
||||
const float* v1 = &verts[(i*3+1)*3];
|
||||
const float* v2 = &verts[(i*3+2)*3];
|
||||
// Rasterize.
|
||||
rasterizeTri(v0, v1, v2, areas[i], solid, solid.bmin, solid.bmax, solid.cs, ics, ich, flagMergeThr);
|
||||
}
|
||||
|
||||
ctx->stopTimer(RC_TIMER_RASTERIZE_TRIANGLES);
|
||||
}
|
||||
1823
recast/recast/src/RecastRegion.cpp
Normal file
1823
recast/recast/src/RecastRegion.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -33,7 +33,6 @@ SET(zone_sources
|
||||
horse.cpp
|
||||
inventory.cpp
|
||||
loottables.cpp
|
||||
lua_behavior_interface.cpp
|
||||
lua_bit.cpp
|
||||
lua_corpse.cpp
|
||||
lua_client.cpp
|
||||
@ -151,7 +150,6 @@ SET(zone_headers
|
||||
guild_mgr.h
|
||||
hate_list.h
|
||||
horse.h
|
||||
lua_behavior_interface.h
|
||||
lua_bit.h
|
||||
lua_client.h
|
||||
lua_corpse.h
|
||||
@ -227,7 +225,7 @@ INSTALL(TARGETS zone RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX})
|
||||
|
||||
ADD_DEFINITIONS(-DZONE)
|
||||
|
||||
TARGET_LINK_LIBRARIES(zone common debug ${MySQL_LIBRARY_DEBUG} optimized ${MySQL_LIBRARY_RELEASE} ${ZLIB_LIBRARY})
|
||||
TARGET_LINK_LIBRARIES(zone common debug ${MySQL_LIBRARY_DEBUG} optimized ${MySQL_LIBRARY_RELEASE} ${ZLIB_LIBRARY} recast_navigation)
|
||||
|
||||
IF(EQEMU_BUILD_PERL)
|
||||
TARGET_LINK_LIBRARIES(zone ${PERL_LIBRARY})
|
||||
|
||||
@ -8728,3 +8728,31 @@ uint32 Client::GetMoney(uint8 type, uint8 subtype) {
|
||||
int Client::GetAccountAge() {
|
||||
return (time(nullptr) - GetAccountCreation());
|
||||
}
|
||||
|
||||
void Client::CreatePathFromRoute(const glm::vec3 &dest, const PathfindingRoute &route)
|
||||
{
|
||||
auto nodes = route.GetNodes();
|
||||
|
||||
if (nodes.size() < 2) {
|
||||
EQApplicationPacket outapp(OP_FindPersonReply, 0);
|
||||
QueuePacket(&outapp);
|
||||
return;
|
||||
}
|
||||
|
||||
EQApplicationPacket outapp(OP_FindPersonReply, (nodes.size() + 1) * 12);
|
||||
int i = 0;
|
||||
FindPerson_Point *point_buffer = (FindPerson_Point*)outapp.pBuffer;
|
||||
point_buffer[i].x = dest.x;
|
||||
point_buffer[i].y = dest.y;
|
||||
point_buffer[i].z = dest.z;
|
||||
i++;
|
||||
|
||||
for (auto &node : nodes) {
|
||||
point_buffer[i].x = node.position.x;
|
||||
point_buffer[i].y = node.position.y;
|
||||
point_buffer[i].z = node.position.z;
|
||||
i++;
|
||||
}
|
||||
|
||||
QueuePacket(&outapp);
|
||||
}
|
||||
|
||||
@ -759,7 +759,7 @@ public:
|
||||
void ChangeTributeSettings(TributeInfo_Struct *t);
|
||||
void SendTributeTimer();
|
||||
void ToggleTribute(bool enabled);
|
||||
void SendPathPacket(std::vector<FindPerson_Point> &path);
|
||||
void CreatePathFromRoute(const glm::vec3 &dest, const PathfindingRoute &route);
|
||||
|
||||
inline PTimerList &GetPTimers() { return(p_timers); }
|
||||
|
||||
|
||||
@ -5629,107 +5629,10 @@ void Client::Handle_OP_FindPersonRequest(const EQApplicationPacket *app)
|
||||
MovePC(zone->GetZoneID(), zone->GetInstanceID(), target->GetX(), target->GetY(), target->GetZ(), 0.0f);
|
||||
}
|
||||
|
||||
if (!RuleB(Pathing, Find) || !zone->pathing)
|
||||
{
|
||||
//fill in the path array...
|
||||
//
|
||||
points.resize(2);
|
||||
points[0].x = GetX();
|
||||
points[0].y = GetY();
|
||||
points[0].z = GetZ();
|
||||
points[1].x = target->GetX();
|
||||
points[1].y = target->GetY();
|
||||
points[1].z = target->GetZ();
|
||||
}
|
||||
else
|
||||
{
|
||||
glm::vec3 Start(GetX(), GetY(), GetZ() + (GetSize() < 6.0 ? 6 : GetSize()) * HEAD_POSITION);
|
||||
glm::vec3 End(target->GetX(), target->GetY(), target->GetZ() + (target->GetSize() < 6.0 ? 6 : target->GetSize()) * HEAD_POSITION);
|
||||
|
||||
if (!zone->zonemap->LineIntersectsZone(Start, End, 1.0f, nullptr) && zone->pathing->NoHazards(Start, End))
|
||||
{
|
||||
points.resize(2);
|
||||
points[0].x = Start.x;
|
||||
points[0].y = Start.y;
|
||||
points[0].z = Start.z;
|
||||
|
||||
points[1].x = End.x;
|
||||
points[1].y = End.y;
|
||||
points[1].z = End.z;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
std::deque<int> pathlist = zone->pathing->FindRoute(Start, End);
|
||||
|
||||
if (pathlist.size() == 0)
|
||||
{
|
||||
EQApplicationPacket outapp(OP_FindPersonReply, 0);
|
||||
QueuePacket(&outapp);
|
||||
return;
|
||||
}
|
||||
|
||||
//the client seems to have issues with packets larger than this
|
||||
if (pathlist.size() > 36)
|
||||
{
|
||||
EQApplicationPacket outapp(OP_FindPersonReply, 0);
|
||||
QueuePacket(&outapp);
|
||||
return;
|
||||
}
|
||||
|
||||
// Live appears to send the points in this order:
|
||||
// Final destination.
|
||||
// Current Position.
|
||||
// rest of the points.
|
||||
FindPerson_Point p;
|
||||
|
||||
int PointNumber = 0;
|
||||
|
||||
bool LeadsToTeleporter = false;
|
||||
|
||||
glm::vec3 v = zone->pathing->GetPathNodeCoordinates(pathlist.back());
|
||||
|
||||
p.x = v.x;
|
||||
p.y = v.y;
|
||||
p.z = v.z;
|
||||
points.push_back(p);
|
||||
|
||||
p.x = GetX();
|
||||
p.y = GetY();
|
||||
p.z = GetZ();
|
||||
points.push_back(p);
|
||||
|
||||
for (auto Iterator = pathlist.begin(); Iterator != pathlist.end(); ++Iterator)
|
||||
{
|
||||
if ((*Iterator) == -1) // Teleporter
|
||||
{
|
||||
LeadsToTeleporter = true;
|
||||
break;
|
||||
}
|
||||
|
||||
glm::vec3 v = zone->pathing->GetPathNodeCoordinates((*Iterator), false);
|
||||
p.x = v.x;
|
||||
p.y = v.y;
|
||||
p.z = v.z;
|
||||
points.push_back(p);
|
||||
++PointNumber;
|
||||
}
|
||||
|
||||
if (!LeadsToTeleporter)
|
||||
{
|
||||
p.x = target->GetX();
|
||||
p.y = target->GetY();
|
||||
p.z = target->GetZ();
|
||||
|
||||
points.push_back(p);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
SendPathPacket(points);
|
||||
glm::vec3 dest(target->GetX(), target->GetY(), target->GetZ());
|
||||
auto route = zone->pathing_new.FindRoute(glm::vec3(GetX(), GetY(), GetZ()), dest);
|
||||
CreatePathFromRoute(dest, route);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void Client::Handle_OP_Fishing(const EQApplicationPacket *app)
|
||||
|
||||
311
zone/command.cpp
311
zone/command.cpp
@ -290,7 +290,6 @@ int command_init(void)
|
||||
command_add("packetprofile", "- Dump packet profile for target or self.", 250, command_packetprofile) ||
|
||||
#endif
|
||||
|
||||
command_add("path", "- view and edit pathing", 200, command_path) ||
|
||||
command_add("peekinv", "[worn/inv/cursor/trib/bank/trade/world/all] - Print out contents of your player target's inventory", 100, command_peekinv) ||
|
||||
command_add("peqzone", "[zonename] - Go to specified zone, if you have > 75% health", 0, command_peqzone) ||
|
||||
command_add("permaclass", "[classnum] - Change your or your player target's class (target is disconnected)", 80, command_permaclass) ||
|
||||
@ -6777,316 +6776,6 @@ void command_qglobal(Client *c, const Seperator *sep) {
|
||||
c->Message(15, "Invalid action specified.");
|
||||
}
|
||||
|
||||
void command_path(Client *c, const Seperator *sep)
|
||||
{
|
||||
if(sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help"))
|
||||
{
|
||||
c->Message(0, "Syntax: #path shownodes: Spawns a npc to represent every npc node.");
|
||||
c->Message(0, "#path info node_id: Gives information about node info (requires shownode target).");
|
||||
c->Message(0, "#path dump file_name: Dumps the current zone->pathing to a file of your naming.");
|
||||
c->Message(0, "#path add [requested_id]: Adds a node at your current location will try to take the requested id if possible.");
|
||||
c->Message(0, "#path connect connect_to_id [is_teleport] [door_id]: Connects the currently targeted node to connect_to_id's node and connects that node back (requires shownode target).");
|
||||
c->Message(0, "#path sconnect connect_to_id [is_teleport] [door_id]: Connects the currently targeted node to connect_to_id's node (requires shownode target).");
|
||||
c->Message(0, "#path qconnect [set]: short cut connect, connects the targeted node to the node you set with #path qconnect set (requires shownode target).");
|
||||
c->Message(0, "#path disconnect [all]/disconnect_from_id: Disconnects the currently targeted node to disconnect from disconnect from id's node (requires shownode target), if passed all as the second argument it will disconnect this node from every other node.");
|
||||
c->Message(0, "#path move: Moves your targeted node to your current position");
|
||||
c->Message(0, "#path process file_name: processes the map file and tries to automatically generate a rudimentary path setup and then dumps the current zone->pathing to a file of your naming.");
|
||||
c->Message(0, "#path resort [nodes]: resorts the connections/nodes after you've manually altered them so they'll work.");
|
||||
return;
|
||||
}
|
||||
if(!strcasecmp(sep->arg[1], "shownodes"))
|
||||
{
|
||||
if(zone->pathing)
|
||||
zone->pathing->SpawnPathNodes();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(!strcasecmp(sep->arg[1], "info"))
|
||||
{
|
||||
if(zone->pathing)
|
||||
{
|
||||
zone->pathing->NodeInfo(c);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(!strcasecmp(sep->arg[1], "dump"))
|
||||
{
|
||||
if(zone->pathing)
|
||||
{
|
||||
if(sep->arg[2][0] == '\0')
|
||||
return;
|
||||
|
||||
zone->pathing->DumpPath(sep->arg[2]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(!strcasecmp(sep->arg[1], "add"))
|
||||
{
|
||||
if(zone->pathing)
|
||||
{
|
||||
float px = c->GetX();
|
||||
float py = c->GetY();
|
||||
float pz = c->GetZ();
|
||||
float best_z;
|
||||
|
||||
if(zone->zonemap)
|
||||
{
|
||||
glm::vec3 loc(px, py, pz);
|
||||
best_z = zone->zonemap->FindBestZ(loc, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
best_z = pz;
|
||||
}
|
||||
int32 res = zone->pathing->AddNode(px, py, pz, best_z, atoi(sep->arg[2]));
|
||||
if(res >= 0)
|
||||
{
|
||||
c->Message(0, "Added Path Node: %i", res);
|
||||
}
|
||||
else
|
||||
{
|
||||
c->Message(0, "Failed to add Path Node");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
zone->pathing = new PathManager();
|
||||
float px = c->GetX();
|
||||
float py = c->GetY();
|
||||
float pz = c->GetZ();
|
||||
float best_z;
|
||||
|
||||
if(zone->zonemap)
|
||||
{
|
||||
glm::vec3 loc(px, py, pz);
|
||||
best_z = zone->zonemap->FindBestZ(loc, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
best_z = pz;
|
||||
}
|
||||
int32 res = zone->pathing->AddNode(px, py, pz, best_z, atoi(sep->arg[2]));
|
||||
if(res >= 0)
|
||||
{
|
||||
c->Message(0, "Added Path Node: %i", res);
|
||||
}
|
||||
else
|
||||
{
|
||||
c->Message(0, "Failed to add Path Node");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(!strcasecmp(sep->arg[1], "remove"))
|
||||
{
|
||||
if(zone->pathing)
|
||||
{
|
||||
if(zone->pathing->DeleteNode(c))
|
||||
{
|
||||
c->Message(0, "Removed Node.");
|
||||
}
|
||||
else
|
||||
{
|
||||
c->Message(0, "Unable to Remove Node.");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(!strcasecmp(sep->arg[1], "connect"))
|
||||
{
|
||||
if(zone->pathing)
|
||||
{
|
||||
zone->pathing->ConnectNodeToNode(c, atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(!strcasecmp(sep->arg[1], "sconnect"))
|
||||
{
|
||||
if(zone->pathing)
|
||||
{
|
||||
zone->pathing->ConnectNode(c, atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(!strcasecmp(sep->arg[1], "qconnect"))
|
||||
{
|
||||
if(zone->pathing)
|
||||
{
|
||||
if(!strcasecmp(sep->arg[2], "set"))
|
||||
{
|
||||
zone->pathing->QuickConnect(c, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
zone->pathing->QuickConnect(c, false);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(!strcasecmp(sep->arg[1], "disconnect"))
|
||||
{
|
||||
if(zone->pathing)
|
||||
{
|
||||
if(!strcasecmp(sep->arg[2], "all"))
|
||||
{
|
||||
zone->pathing->DisconnectAll(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
zone->pathing->DisconnectNodeToNode(c, atoi(sep->arg[2]));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if(!strcasecmp(sep->arg[1], "move"))
|
||||
{
|
||||
if(zone->pathing)
|
||||
{
|
||||
zone->pathing->MoveNode(c);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(!strcasecmp(sep->arg[1], "process"))
|
||||
{
|
||||
if(zone->pathing)
|
||||
{
|
||||
if(sep->arg[2][0] == '\0')
|
||||
return;
|
||||
|
||||
zone->pathing->ProcessNodesAndSave(sep->arg[2]);
|
||||
c->Message(0, "Path processed...");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(!strcasecmp(sep->arg[1], "resort"))
|
||||
{
|
||||
if(zone->pathing)
|
||||
{
|
||||
if(!strcasecmp(sep->arg[2], "nodes"))
|
||||
{
|
||||
zone->pathing->SortNodes();
|
||||
c->Message(0, "Nodes resorted...");
|
||||
}
|
||||
else
|
||||
{
|
||||
zone->pathing->ResortConnections();
|
||||
c->Message(0, "Connections resorted...");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(!strcasecmp(sep->arg[1], "hazard"))
|
||||
{
|
||||
if(zone->pathing)
|
||||
{
|
||||
if(c && c->GetTarget())
|
||||
{
|
||||
if (zone->pathing->NoHazardsAccurate(glm::vec3(c->GetX(), c->GetY(), c->GetZ()),
|
||||
glm::vec3(c->GetTarget()->GetX(), c->GetTarget()->GetY(), c->GetTarget()->GetZ())))
|
||||
{
|
||||
c->Message(0, "No hazards.");
|
||||
}
|
||||
else
|
||||
{
|
||||
c->Message(0, "Hazard Detected...");
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(!strcasecmp(sep->arg[1], "print"))
|
||||
{
|
||||
if(zone->pathing)
|
||||
{
|
||||
zone->pathing->PrintPathing();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(!strcasecmp(sep->arg[1], "showneighbours") || !strcasecmp(sep->arg[1], "showneighbors"))
|
||||
{
|
||||
if(!c->GetTarget())
|
||||
{
|
||||
c->Message(0, "First #path shownodes to spawn the pathnodes, and then target one of them.");
|
||||
return;
|
||||
}
|
||||
if(zone->pathing)
|
||||
{
|
||||
zone->pathing->ShowPathNodeNeighbours(c);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(!strcasecmp(sep->arg[1], "meshtest"))
|
||||
{
|
||||
if(zone->pathing)
|
||||
{
|
||||
if(!strcasecmp(sep->arg[2], "simple"))
|
||||
{
|
||||
c->Message(0, "You may go linkdead. Results will be in the log file.");
|
||||
zone->pathing->SimpleMeshTest();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
c->Message(0, "You may go linkdead. Results will be in the log file.");
|
||||
zone->pathing->MeshTest();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!strcasecmp(sep->arg[1], "allspawns"))
|
||||
{
|
||||
if(zone->pathing)
|
||||
{
|
||||
c->Message(0, "You may go linkdead. Results will be in the log file.");
|
||||
entity_list.FindPathsToAllNPCs();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(!strcasecmp(sep->arg[1], "nearest"))
|
||||
{
|
||||
if(!c->GetTarget() || !c->GetTarget()->IsMob())
|
||||
{
|
||||
c->Message(0, "You must target something.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(zone->pathing)
|
||||
{
|
||||
Mob *m = c->GetTarget();
|
||||
|
||||
glm::vec3 Position(m->GetX(), m->GetY(), m->GetZ());
|
||||
|
||||
int Node = zone->pathing->FindNearestPathNode(Position);
|
||||
|
||||
if(Node == -1)
|
||||
c->Message(0, "Unable to locate a path node within range.");
|
||||
else
|
||||
c->Message(0, "Nearest path node is %i", Node);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
c->Message(0, "Unknown path command.");
|
||||
}
|
||||
|
||||
void Client::Undye() {
|
||||
for (int cur_slot = EmuConstants::MATERIAL_BEGIN; cur_slot <= EmuConstants::MATERIAL_END; cur_slot++ ) {
|
||||
uint8 slot2=SlotConvert(cur_slot);
|
||||
|
||||
@ -196,7 +196,6 @@ void command_optest(Client *c, const Seperator *sep);
|
||||
void command_packetprofile(Client *c, const Seperator *sep);
|
||||
#endif
|
||||
|
||||
void command_path(Client *c, const Seperator *sep);
|
||||
void command_peekinv(Client *c, const Seperator *sep);
|
||||
void command_peqzone(Client *c, const Seperator *sep);
|
||||
void command_permaclass(Client *c, const Seperator *sep);
|
||||
|
||||
@ -1,166 +0,0 @@
|
||||
#ifdef LUA_EQEMU
|
||||
#include "lua_behavior_interface.h"
|
||||
|
||||
#include <lua.hpp>
|
||||
#include <luabind/luabind.hpp>
|
||||
#include <luabind/object.hpp>
|
||||
|
||||
#include "masterentity.h"
|
||||
#include "../common/spdat.h"
|
||||
#include "lua_bit.h"
|
||||
#include "lua_entity.h"
|
||||
#include "lua_item.h"
|
||||
#include "lua_iteminst.h"
|
||||
#include "lua_mob.h"
|
||||
#include "lua_hate_list.h"
|
||||
#include "lua_client.h"
|
||||
#include "lua_inventory.h"
|
||||
#include "lua_npc.h"
|
||||
#include "lua_spell.h"
|
||||
#include "lua_entity_list.h"
|
||||
#include "lua_group.h"
|
||||
#include "lua_raid.h"
|
||||
#include "lua_corpse.h"
|
||||
#include "lua_object.h"
|
||||
#include "lua_door.h"
|
||||
#include "lua_spawn.h"
|
||||
#include "lua_packet.h"
|
||||
#include "lua_general.h"
|
||||
|
||||
LuaBehaviorInterface::LuaBehaviorInterface()
|
||||
{
|
||||
L = nullptr;
|
||||
}
|
||||
|
||||
LuaBehaviorInterface::~LuaBehaviorInterface()
|
||||
{
|
||||
if (L) {
|
||||
lua_close(L);
|
||||
}
|
||||
}
|
||||
|
||||
bool LuaBehaviorInterface::Init()
|
||||
{
|
||||
if (L) {
|
||||
return false;
|
||||
}
|
||||
|
||||
L = luaL_newstate();
|
||||
luaL_openlibs(L);
|
||||
|
||||
if (luaopen_bit(L) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (luaL_dostring(L, "math.randomseed(os.time())")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef SANITIZE_LUA_LIBS
|
||||
lua_pushnil(L);
|
||||
lua_setglobal(L, "io");
|
||||
|
||||
lua_getglobal(L, "os");
|
||||
lua_pushnil(L);
|
||||
lua_setfield(L, -2, "exit");
|
||||
lua_pushnil(L);
|
||||
lua_setfield(L, -2, "execute");
|
||||
lua_pushnil(L);
|
||||
lua_setfield(L, -2, "getenv");
|
||||
lua_pushnil(L);
|
||||
lua_setfield(L, -2, "remove");
|
||||
lua_pushnil(L);
|
||||
lua_setfield(L, -2, "rename");
|
||||
lua_pushnil(L);
|
||||
lua_setfield(L, -2, "setlocale");
|
||||
lua_pushnil(L);
|
||||
lua_setfield(L, -2, "tmpname");
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_pushnil(L);
|
||||
lua_setglobal(L, "collectgarbage");
|
||||
|
||||
lua_pushnil(L);
|
||||
lua_setglobal(L, "loadfile");
|
||||
#endif
|
||||
|
||||
lua_getglobal(L, "package");
|
||||
lua_getfield(L, -1, "path");
|
||||
std::string module_path = lua_tostring(L, -1);
|
||||
module_path += ";./lua_modules/?.lua";
|
||||
lua_pop(L, 1);
|
||||
lua_pushstring(L, module_path.c_str());
|
||||
lua_setfield(L, -2, "path");
|
||||
lua_pop(L, 1);
|
||||
|
||||
if (!MapFunctions()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path = "ai/script_init.lua";
|
||||
|
||||
FILE *f = fopen(path.c_str(), "r");
|
||||
if (f) {
|
||||
fclose(f);
|
||||
|
||||
if (luaL_dofile(L, path.c_str())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LuaBehaviorInterface::MapFunctions()
|
||||
{
|
||||
try {
|
||||
luabind::open(L);
|
||||
|
||||
luabind::module(L)
|
||||
[
|
||||
lua_register_general(),
|
||||
lua_register_events(),
|
||||
lua_register_faction(),
|
||||
lua_register_slot(),
|
||||
lua_register_material(),
|
||||
lua_register_client_version(),
|
||||
lua_register_appearance(),
|
||||
lua_register_entity(),
|
||||
lua_register_mob(),
|
||||
lua_register_special_abilities(),
|
||||
lua_register_npc(),
|
||||
lua_register_client(),
|
||||
lua_register_inventory(),
|
||||
lua_register_inventory_where(),
|
||||
lua_register_iteminst(),
|
||||
lua_register_item(),
|
||||
lua_register_spell(),
|
||||
lua_register_spawn(),
|
||||
lua_register_hate_entry(),
|
||||
lua_register_hate_list(),
|
||||
lua_register_entity_list(),
|
||||
lua_register_mob_list(),
|
||||
lua_register_client_list(),
|
||||
lua_register_npc_list(),
|
||||
lua_register_corpse_list(),
|
||||
lua_register_object_list(),
|
||||
lua_register_door_list(),
|
||||
lua_register_spawn_list(),
|
||||
lua_register_group(),
|
||||
lua_register_raid(),
|
||||
lua_register_corpse(),
|
||||
lua_register_door(),
|
||||
lua_register_object(),
|
||||
lua_register_packet(),
|
||||
lua_register_packet_opcodes()
|
||||
];
|
||||
|
||||
}
|
||||
catch (std::exception &ex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -1,25 +0,0 @@
|
||||
#ifndef EQEMU_LUA_BEHAVIOR_INTERFACE_H
|
||||
#define EQEMU_LUA_BEHAVIOR_INTERFACE_H
|
||||
#ifdef LUA_EQEMU
|
||||
|
||||
namespace luabind {
|
||||
namespace adl {
|
||||
class object;
|
||||
}
|
||||
}
|
||||
|
||||
struct lua_State;
|
||||
|
||||
class LuaBehaviorInterface
|
||||
{
|
||||
public:
|
||||
LuaBehaviorInterface();
|
||||
~LuaBehaviorInterface();
|
||||
bool Init();
|
||||
private:
|
||||
bool MapFunctions();
|
||||
lua_State *L;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@ -1300,38 +1300,6 @@ void PathManager::OpenDoors(int Node1, int Node2, Mob *ForWho)
|
||||
}
|
||||
}
|
||||
|
||||
//this assumes that the first point in the list is the player's
|
||||
//current position, I dont know how well it works if its not.
|
||||
void Client::SendPathPacket(std::vector<FindPerson_Point> &points) {
|
||||
if(points.size() < 2) {
|
||||
//empty length packet == not found.
|
||||
EQApplicationPacket outapp(OP_FindPersonReply, 0);
|
||||
QueuePacket(&outapp);
|
||||
return;
|
||||
}
|
||||
|
||||
int len = sizeof(FindPersonResult_Struct) + (points.size()+1) * sizeof(FindPerson_Point);
|
||||
EQApplicationPacket *outapp = new EQApplicationPacket(OP_FindPersonReply, len);
|
||||
FindPersonResult_Struct* fpr=(FindPersonResult_Struct*)outapp->pBuffer;
|
||||
|
||||
std::vector<FindPerson_Point>::iterator cur, end;
|
||||
cur = points.begin();
|
||||
end = points.end();
|
||||
unsigned int r;
|
||||
for(r = 0; cur != end; ++cur, r++) {
|
||||
fpr->path[r] = *cur;
|
||||
|
||||
}
|
||||
//put the last element into the destination field
|
||||
--cur;
|
||||
fpr->path[r] = *cur;
|
||||
fpr->dest = *cur;
|
||||
|
||||
FastQueuePacket(&outapp);
|
||||
|
||||
|
||||
}
|
||||
|
||||
PathNode* PathManager::FindPathNodeByCoordinates(float x, float y, float z)
|
||||
{
|
||||
for(uint32 i = 0; i < Head.PathNodeCount; ++i)
|
||||
|
||||
@ -1708,14 +1708,6 @@ void QuestManager::showgrid(int grid) {
|
||||
if(initiator == nullptr)
|
||||
return;
|
||||
|
||||
FindPerson_Point pt;
|
||||
std::vector<FindPerson_Point> pts;
|
||||
|
||||
pt.x = initiator->GetX();
|
||||
pt.y = initiator->GetY();
|
||||
pt.z = initiator->GetZ();
|
||||
pts.push_back(pt);
|
||||
|
||||
// Retrieve all waypoints for this grid
|
||||
std::string query = StringFormat("SELECT `x`,`y`,`z` FROM grid_entries "
|
||||
"WHERE `gridid` = %i AND `zoneid` = %i "
|
||||
@ -1726,16 +1718,29 @@ void QuestManager::showgrid(int grid) {
|
||||
return;
|
||||
}
|
||||
|
||||
for(auto row = results.begin(); row != results.end(); ++row) {
|
||||
pt.x = atof(row[0]);
|
||||
pt.y = atof(row[1]);
|
||||
pt.z = atof(row[2]);
|
||||
PathfindingRoute route;
|
||||
auto& nodes = route.GetNodesEdit();
|
||||
PathfindingNode node;
|
||||
node.position.x = initiator->GetX();
|
||||
node.position.y = initiator->GetY();
|
||||
node.position.z = initiator->GetZ();
|
||||
nodes.push_back(node);
|
||||
|
||||
pts.push_back(pt);
|
||||
for(auto row = results.begin(); row != results.end(); ++row) {
|
||||
node.position.x = atof(row[0]);
|
||||
node.position.y = atof(row[1]);
|
||||
node.position.z = atof(row[2]);
|
||||
|
||||
nodes.push_back(node);
|
||||
}
|
||||
|
||||
initiator->SendPathPacket(pts);
|
||||
auto sz = nodes.size();
|
||||
if (sz < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
glm::vec3 dest = nodes[sz].position;
|
||||
initiator->CreatePathFromRoute(dest, route);
|
||||
}
|
||||
|
||||
//change the value of a spawn condition
|
||||
|
||||
@ -103,6 +103,7 @@ bool Zone::Bootup(uint32 iZoneID, uint32 iInstanceID, bool iStaticZone) {
|
||||
zone->zonemap = Map::LoadMapFile(zone->map_name);
|
||||
zone->watermap = WaterMap::LoadWaterMapfile(zone->map_name);
|
||||
zone->pathing = PathManager::LoadPathFile(zone->map_name);
|
||||
zone->pathing_new.Load(zone->map_name);
|
||||
|
||||
char tmp[10];
|
||||
if (database.GetVariable("loglevel",tmp, 9)) {
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
#include "../common/types.h"
|
||||
#include "../common/random.h"
|
||||
#include "../common/string_util.h"
|
||||
#include "../common/pathfind.h"
|
||||
#include "qglobals.h"
|
||||
#include "spawn2.h"
|
||||
#include "spawngroup.h"
|
||||
@ -212,6 +213,7 @@ public:
|
||||
Map* zonemap;
|
||||
WaterMap* watermap;
|
||||
PathManager *pathing;
|
||||
PathfindingManager pathing_new;
|
||||
NewZone_Struct newzone_data;
|
||||
|
||||
SpawnConditionManager spawn_conditions;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user