1
0
mirror of https://github.com/ncblakely/GiantsTools synced 2024-11-26 23:53:09 +01:00

Compare commits

..

4 Commits

Author SHA1 Message Date
Nick Blakely
7874502ca2 Initial commit for Recast based nav mesh generator. 2021-01-04 00:39:07 -08:00
Nick Blakely
312774c745 Add Recast as a submodule. 2021-01-04 00:36:02 -08:00
Nick Blakely
8454b0df87 Use JSON vcpkg manifest. 2021-01-04 00:35:51 -08:00
Nick Blakely
382d67f7b1 Minor SDK fixes to ensure proper memory deallocation. 2021-01-04 00:35:21 -08:00
33 changed files with 3401 additions and 21 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "External/recastnavigation"]
path = External/recastnavigation
url = https://github.com/recastnavigation/recastnavigation.git

1
External/recastnavigation vendored Submodule

@ -0,0 +1 @@
Subproject commit c32297c2f9532a55a11c00f22ec763d771d3dae5

View File

@ -25,6 +25,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugins", "Plugins", "{0801
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "imp_gbs", "Plugins\imp_gbs\imp_gbs.vcxproj", "{448F061E-AE05-4E06-84A1-C95660FD048C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Giants.Services.Tests", "Giants.Services.Tests\Giants.Services.Tests.csproj", "{2AFB71CA-8313-472E-B242-0517343764B4}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ServerConsole", "ServerConsoleExample\ServerConsole.vcxproj", "{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NavMeshGenerator", "NavMeshGenerator\NavMeshGenerator.vcxproj", "{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -277,6 +283,74 @@ Global
{448F061E-AE05-4E06-84A1-C95660FD048C}.ReleaseBeta|x64.Build.0 = Release|x64
{448F061E-AE05-4E06-84A1-C95660FD048C}.ReleaseBeta|x86.ActiveCfg = Release|Win32
{448F061E-AE05-4E06-84A1-C95660FD048C}.ReleaseBeta|x86.Build.0 = Release|Win32
{2AFB71CA-8313-472E-B242-0517343764B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.Debug|x64.ActiveCfg = Debug|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.Debug|x64.Build.0 = Debug|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.Debug|x86.ActiveCfg = Debug|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.Debug|x86.Build.0 = Debug|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.DebugBeta|Any CPU.ActiveCfg = Debug|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.DebugBeta|Any CPU.Build.0 = Debug|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.DebugBeta|x64.ActiveCfg = Debug|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.DebugBeta|x64.Build.0 = Debug|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.DebugBeta|x86.ActiveCfg = Debug|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.DebugBeta|x86.Build.0 = Debug|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.Release|Any CPU.Build.0 = Release|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.Release|x64.ActiveCfg = Release|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.Release|x64.Build.0 = Release|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.Release|x86.ActiveCfg = Release|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.Release|x86.Build.0 = Release|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.ReleaseBeta|Any CPU.ActiveCfg = Release|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.ReleaseBeta|Any CPU.Build.0 = Release|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.ReleaseBeta|x64.ActiveCfg = Release|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.ReleaseBeta|x64.Build.0 = Release|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.ReleaseBeta|x86.ActiveCfg = Release|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.ReleaseBeta|x86.Build.0 = Release|Any CPU
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.Debug|Any CPU.ActiveCfg = Debug|Win32
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.Debug|x64.ActiveCfg = Debug|x64
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.Debug|x64.Build.0 = Debug|x64
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.Debug|x86.ActiveCfg = Debug|Win32
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.Debug|x86.Build.0 = Debug|Win32
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.DebugBeta|Any CPU.ActiveCfg = Debug|Win32
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.DebugBeta|Any CPU.Build.0 = Debug|Win32
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.DebugBeta|x64.ActiveCfg = Debug|x64
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.DebugBeta|x64.Build.0 = Debug|x64
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.DebugBeta|x86.ActiveCfg = Debug|Win32
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.DebugBeta|x86.Build.0 = Debug|Win32
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.Release|Any CPU.ActiveCfg = Release|Win32
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.Release|x64.ActiveCfg = Release|x64
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.Release|x64.Build.0 = Release|x64
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.Release|x86.ActiveCfg = Release|Win32
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.Release|x86.Build.0 = Release|Win32
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.ReleaseBeta|Any CPU.ActiveCfg = Debug|Win32
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.ReleaseBeta|Any CPU.Build.0 = Debug|Win32
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.ReleaseBeta|x64.ActiveCfg = Release|x64
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.ReleaseBeta|x64.Build.0 = Release|x64
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.ReleaseBeta|x86.ActiveCfg = Release|Win32
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.ReleaseBeta|x86.Build.0 = Release|Win32
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.Debug|Any CPU.ActiveCfg = Debug|Win32
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.Debug|x64.ActiveCfg = Debug|x64
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.Debug|x64.Build.0 = Debug|x64
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.Debug|x86.ActiveCfg = Debug|Win32
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.Debug|x86.Build.0 = Debug|Win32
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.DebugBeta|Any CPU.ActiveCfg = Debug|Win32
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.DebugBeta|Any CPU.Build.0 = Debug|Win32
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.DebugBeta|x64.ActiveCfg = Debug|x64
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.DebugBeta|x64.Build.0 = Debug|x64
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.DebugBeta|x86.ActiveCfg = Debug|Win32
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.DebugBeta|x86.Build.0 = Debug|Win32
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.Release|Any CPU.ActiveCfg = Release|Win32
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.Release|x64.ActiveCfg = Release|x64
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.Release|x64.Build.0 = Release|x64
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.Release|x86.ActiveCfg = Release|Win32
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.Release|x86.Build.0 = Release|Win32
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.ReleaseBeta|Any CPU.ActiveCfg = Debug|Win32
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.ReleaseBeta|Any CPU.Build.0 = Debug|Win32
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.ReleaseBeta|x64.ActiveCfg = Release|x64
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.ReleaseBeta|x64.Build.0 = Release|x64
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.ReleaseBeta|x86.ActiveCfg = Release|Win32
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.ReleaseBeta|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -0,0 +1,315 @@
//
// 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 "ChunkyTriMesh.h"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
struct BoundsItem
{
float bmin[2];
float bmax[2];
int i;
};
static int compareItemX(const void* va, const void* vb)
{
const BoundsItem* a = (const BoundsItem*)va;
const BoundsItem* b = (const BoundsItem*)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 BoundsItem* a = (const BoundsItem*)va;
const BoundsItem* b = (const BoundsItem*)vb;
if (a->bmin[1] < b->bmin[1])
return -1;
if (a->bmin[1] > b->bmin[1])
return 1;
return 0;
}
static void calcExtends(const BoundsItem* items, const int /*nitems*/,
const int imin, const int imax,
float* bmin, float* bmax)
{
bmin[0] = items[imin].bmin[0];
bmin[1] = items[imin].bmin[1];
bmax[0] = items[imin].bmax[0];
bmax[1] = items[imin].bmax[1];
for (int i = imin+1; i < imax; ++i)
{
const BoundsItem& 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.bmax[0] > bmax[0]) bmax[0] = it.bmax[0];
if (it.bmax[1] > bmax[1]) bmax[1] = it.bmax[1];
}
}
inline int longestAxis(float x, float y)
{
return y > x ? 1 : 0;
}
static void subdivide(BoundsItem* items, int nitems, int imin, int imax, int trisPerChunk,
int& curNode, rcChunkyTriMeshNode* nodes, const int maxNodes,
int& curTri, int* outTris, const int* inTris)
{
int inum = imax - imin;
int icur = curNode;
if (curNode >= maxNodes)
return;
rcChunkyTriMeshNode& node = nodes[curNode++];
if (inum <= trisPerChunk)
{
// Leaf
calcExtends(items, nitems, imin, imax, node.bmin, node.bmax);
// Copy triangles.
node.i = curTri;
node.n = inum;
for (int i = imin; i < imax; ++i)
{
const int* src = &inTris[items[i].i*3];
int* dst = &outTris[curTri*3];
curTri++;
dst[0] = src[0];
dst[1] = src[1];
dst[2] = src[2];
}
}
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]);
if (axis == 0)
{
// Sort along x-axis
qsort(items+imin, static_cast<size_t>(inum), sizeof(BoundsItem), compareItemX);
}
else if (axis == 1)
{
// Sort along y-axis
qsort(items+imin, static_cast<size_t>(inum), sizeof(BoundsItem), compareItemY);
}
int isplit = imin+inum/2;
// Left
subdivide(items, nitems, imin, isplit, trisPerChunk, curNode, nodes, maxNodes, curTri, outTris, inTris);
// Right
subdivide(items, nitems, isplit, imax, trisPerChunk, curNode, nodes, maxNodes, curTri, outTris, inTris);
int iescape = curNode - icur;
// Negative index means escape.
node.i = -iescape;
}
}
bool rcCreateChunkyTriMesh(const float* verts, const int* tris, int ntris,
int trisPerChunk, rcChunkyTriMesh* cm)
{
int nchunks = (ntris + trisPerChunk-1) / trisPerChunk;
cm->nodes = new rcChunkyTriMeshNode[nchunks*4];
if (!cm->nodes)
return false;
cm->tris = new int[ntris*3];
if (!cm->tris)
return false;
cm->ntris = ntris;
// Build tree
BoundsItem* items = new BoundsItem[ntris];
if (!items)
return false;
for (int i = 0; i < ntris; i++)
{
const int* t = &tris[i*3];
BoundsItem& it = items[i];
it.i = i;
// Calc triangle XZ bounds.
it.bmin[0] = it.bmax[0] = verts[t[0]*3+0];
it.bmin[1] = it.bmax[1] = verts[t[0]*3+2];
for (int j = 1; j < 3; ++j)
{
const float* v = &verts[t[j]*3];
if (v[0] < it.bmin[0]) it.bmin[0] = v[0];
if (v[2] < it.bmin[1]) it.bmin[1] = v[2];
if (v[0] > it.bmax[0]) it.bmax[0] = v[0];
if (v[2] > it.bmax[1]) it.bmax[1] = v[2];
}
}
int curTri = 0;
int curNode = 0;
subdivide(items, ntris, 0, ntris, trisPerChunk, curNode, cm->nodes, nchunks*4, curTri, cm->tris, tris);
delete [] items;
cm->nnodes = curNode;
// Calc max tris per node.
cm->maxTrisPerChunk = 0;
for (int i = 0; i < cm->nnodes; ++i)
{
rcChunkyTriMeshNode& node = cm->nodes[i];
const bool isLeaf = node.i >= 0;
if (!isLeaf) continue;
if (node.n > cm->maxTrisPerChunk)
cm->maxTrisPerChunk = node.n;
}
return true;
}
inline bool checkOverlapRect(const float amin[2], const float amax[2],
const float bmin[2], const float bmax[2])
{
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;
return overlap;
}
int rcGetChunksOverlappingRect(const rcChunkyTriMesh* cm,
float bmin[2], float bmax[2],
int* ids, const int maxIds)
{
// Traverse tree
int i = 0;
int n = 0;
while (i < cm->nnodes)
{
const rcChunkyTriMeshNode* node = &cm->nodes[i];
const bool overlap = checkOverlapRect(bmin, bmax, node->bmin, node->bmax);
const bool isLeafNode = node->i >= 0;
if (isLeafNode && overlap)
{
if (n < maxIds)
{
ids[n] = i;
n++;
}
}
if (overlap || isLeafNode)
i++;
else
{
const int escapeIndex = -node->i;
i += escapeIndex;
}
}
return n;
}
static bool checkOverlapSegment(const float p[2], const float q[2],
const float bmin[2], const float bmax[2])
{
static const float EPSILON = 1e-6f;
float tmin = 0;
float tmax = 1;
float d[2];
d[0] = q[0] - p[0];
d[1] = q[1] - p[1];
for (int i = 0; i < 2; i++)
{
if (fabsf(d[i]) < EPSILON)
{
// Ray is parallel to slab. No hit if origin not within slab
if (p[i] < bmin[i] || p[i] > bmax[i])
return false;
}
else
{
// Compute intersection t value of ray with near and far plane of slab
float ood = 1.0f / d[i];
float t1 = (bmin[i] - p[i]) * ood;
float t2 = (bmax[i] - p[i]) * ood;
if (t1 > t2) { float tmp = t1; t1 = t2; t2 = tmp; }
if (t1 > tmin) tmin = t1;
if (t2 < tmax) tmax = t2;
if (tmin > tmax) return false;
}
}
return true;
}
int rcGetChunksOverlappingSegment(const rcChunkyTriMesh* cm,
float p[2], float q[2],
int* ids, const int maxIds)
{
// Traverse tree
int i = 0;
int n = 0;
while (i < cm->nnodes)
{
const rcChunkyTriMeshNode* node = &cm->nodes[i];
const bool overlap = checkOverlapSegment(p, q, node->bmin, node->bmax);
const bool isLeafNode = node->i >= 0;
if (isLeafNode && overlap)
{
if (n < maxIds)
{
ids[n] = i;
n++;
}
}
if (overlap || isLeafNode)
i++;
else
{
const int escapeIndex = -node->i;
i += escapeIndex;
}
}
return n;
}

View 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 CHUNKYTRIMESH_H
#define CHUNKYTRIMESH_H
struct rcChunkyTriMeshNode
{
float bmin[2];
float bmax[2];
int i;
int n;
};
struct rcChunkyTriMesh
{
inline rcChunkyTriMesh() : nodes(0), nnodes(0), tris(0), ntris(0), maxTrisPerChunk(0) {};
inline ~rcChunkyTriMesh() { delete [] nodes; delete [] tris; }
rcChunkyTriMeshNode* nodes;
int nnodes;
int* tris;
int ntris;
int maxTrisPerChunk;
private:
// Explicitly disabled copy constructor and copy assignment operator.
rcChunkyTriMesh(const rcChunkyTriMesh&);
rcChunkyTriMesh& operator=(const rcChunkyTriMesh&);
};
/// Creates partitioned triangle mesh (AABB tree),
/// where each node contains at max trisPerChunk triangles.
bool rcCreateChunkyTriMesh(const float* verts, const int* tris, int ntris,
int trisPerChunk, rcChunkyTriMesh* cm);
/// Returns the chunk indices which overlap the input rectable.
int rcGetChunksOverlappingRect(const rcChunkyTriMesh* cm, float bmin[2], float bmax[2], int* ids, const int maxIds);
/// Returns the chunk indices which overlap the input segment.
int rcGetChunksOverlappingSegment(const rcChunkyTriMesh* cm, float p[2], float q[2], int* ids, const int maxIds);
#endif // CHUNKYTRIMESH_H

View File

@ -0,0 +1,614 @@
//
// 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 <ctype.h>
#include <string.h>
#include <algorithm>
#include "Recast.h"
#include "InputGeom.h"
#include "ChunkyTriMesh.h"
#include "MeshLoaderObj.h"
#include "DebugDraw.h"
#include "RecastDebugDraw.h"
#include "DetourNavMesh.h"
#include "Sample.h"
static bool intersectSegmentTriangle(const float* sp, const float* sq,
const float* a, const float* b, const float* c,
float &t)
{
float v, w;
float ab[3], ac[3], qp[3], ap[3], norm[3], e[3];
rcVsub(ab, b, a);
rcVsub(ac, c, a);
rcVsub(qp, sp, sq);
// Compute triangle normal. Can be precalculated or cached if
// intersecting multiple segments against the same triangle
rcVcross(norm, ab, ac);
// Compute denominator d. If d <= 0, segment is parallel to or points
// away from triangle, so exit early
float d = rcVdot(qp, norm);
if (d <= 0.0f) return false;
// Compute intersection t value of pq with plane of triangle. A ray
// intersects iff 0 <= t. Segment intersects iff 0 <= t <= 1. Delay
// dividing by d until intersection has been found to pierce triangle
rcVsub(ap, sp, a);
t = rcVdot(ap, norm);
if (t < 0.0f) return false;
if (t > d) return false; // For segment; exclude this code line for a ray test
// Compute barycentric coordinate components and test if within bounds
rcVcross(e, qp, ap);
v = rcVdot(ac, e);
if (v < 0.0f || v > d) return false;
w = -rcVdot(ab, e);
if (w < 0.0f || v + w > d) return false;
// Segment/ray intersects triangle. Perform delayed division
t /= d;
return true;
}
static char* parseRow(char* buf, char* bufEnd, char* row, int len)
{
bool start = true;
bool done = false;
int n = 0;
while (!done && buf < bufEnd)
{
char c = *buf;
buf++;
// multirow
switch (c)
{
case '\n':
if (start) break;
done = true;
break;
case '\r':
break;
case '\t':
case ' ':
if (start) break;
// else falls through
default:
start = false;
row[n++] = c;
if (n >= len-1)
done = true;
break;
}
}
row[n] = '\0';
return buf;
}
InputGeom::InputGeom() :
m_chunkyMesh(0),
m_mesh(0),
m_hasBuildSettings(false),
m_offMeshConCount(0),
m_volumeCount(0)
{
}
InputGeom::~InputGeom()
{
delete m_chunkyMesh;
delete m_mesh;
}
bool InputGeom::loadMesh(rcContext* ctx, const std::string& filepath)
{
if (m_mesh)
{
delete m_chunkyMesh;
m_chunkyMesh = 0;
delete m_mesh;
m_mesh = 0;
}
m_offMeshConCount = 0;
m_volumeCount = 0;
m_mesh = new rcMeshLoaderObj;
if (!m_mesh)
{
ctx->log(RC_LOG_ERROR, "loadMesh: Out of memory 'm_mesh'.");
return false;
}
if (!m_mesh->load(filepath))
{
ctx->log(RC_LOG_ERROR, "buildTiledNavigation: Could not load '%s'", filepath.c_str());
return false;
}
rcCalcBounds(m_mesh->getVerts(), m_mesh->getVertCount(), m_meshBMin, m_meshBMax);
m_chunkyMesh = new rcChunkyTriMesh;
if (!m_chunkyMesh)
{
ctx->log(RC_LOG_ERROR, "buildTiledNavigation: Out of memory 'm_chunkyMesh'.");
return false;
}
if (!rcCreateChunkyTriMesh(m_mesh->getVerts(), m_mesh->getTris(), m_mesh->getTriCount(), 256, m_chunkyMesh))
{
ctx->log(RC_LOG_ERROR, "buildTiledNavigation: Failed to build chunky mesh.");
return false;
}
return true;
}
bool InputGeom::loadGeomSet(rcContext* ctx, const std::string& filepath)
{
char* buf = 0;
FILE* fp = fopen(filepath.c_str(), "rb");
if (!fp)
{
return false;
}
if (fseek(fp, 0, SEEK_END) != 0)
{
fclose(fp);
return false;
}
long bufSize = ftell(fp);
if (bufSize < 0)
{
fclose(fp);
return false;
}
if (fseek(fp, 0, SEEK_SET) != 0)
{
fclose(fp);
return false;
}
buf = new char[bufSize];
if (!buf)
{
fclose(fp);
return false;
}
size_t readLen = fread(buf, bufSize, 1, fp);
fclose(fp);
if (readLen != 1)
{
delete[] buf;
return false;
}
m_offMeshConCount = 0;
m_volumeCount = 0;
delete m_mesh;
m_mesh = 0;
char* src = buf;
char* srcEnd = buf + bufSize;
char row[512];
while (src < srcEnd)
{
// Parse one row
row[0] = '\0';
src = parseRow(src, srcEnd, row, sizeof(row)/sizeof(char));
if (row[0] == 'f')
{
// File name.
const char* name = row+1;
// Skip white spaces
while (*name && isspace(*name))
name++;
if (*name)
{
if (!loadMesh(ctx, name))
{
delete [] buf;
return false;
}
}
}
else if (row[0] == 'c')
{
// Off-mesh connection
if (m_offMeshConCount < MAX_OFFMESH_CONNECTIONS)
{
float* v = &m_offMeshConVerts[m_offMeshConCount*3*2];
int bidir, area = 0, flags = 0;
float rad;
sscanf(row+1, "%f %f %f %f %f %f %f %d %d %d",
&v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &rad, &bidir, &area, &flags);
m_offMeshConRads[m_offMeshConCount] = rad;
m_offMeshConDirs[m_offMeshConCount] = (unsigned char)bidir;
m_offMeshConAreas[m_offMeshConCount] = (unsigned char)area;
m_offMeshConFlags[m_offMeshConCount] = (unsigned short)flags;
m_offMeshConCount++;
}
}
else if (row[0] == 'v')
{
// Convex volumes
if (m_volumeCount < MAX_VOLUMES)
{
ConvexVolume* vol = &m_volumes[m_volumeCount++];
sscanf(row+1, "%d %d %f %f", &vol->nverts, &vol->area, &vol->hmin, &vol->hmax);
for (int i = 0; i < vol->nverts; ++i)
{
row[0] = '\0';
src = parseRow(src, srcEnd, row, sizeof(row)/sizeof(char));
sscanf(row, "%f %f %f", &vol->verts[i*3+0], &vol->verts[i*3+1], &vol->verts[i*3+2]);
}
}
}
else if (row[0] == 's')
{
// Settings
m_hasBuildSettings = true;
sscanf(row + 1, "%f %f %f %f %f %f %f %f %f %f %f %f %f %d %f %f %f %f %f %f %f",
&m_buildSettings.cellSize,
&m_buildSettings.cellHeight,
&m_buildSettings.agentHeight,
&m_buildSettings.agentRadius,
&m_buildSettings.agentMaxClimb,
&m_buildSettings.agentMaxSlope,
&m_buildSettings.regionMinSize,
&m_buildSettings.regionMergeSize,
&m_buildSettings.edgeMaxLen,
&m_buildSettings.edgeMaxError,
&m_buildSettings.vertsPerPoly,
&m_buildSettings.detailSampleDist,
&m_buildSettings.detailSampleMaxError,
&m_buildSettings.partitionType,
&m_buildSettings.navMeshBMin[0],
&m_buildSettings.navMeshBMin[1],
&m_buildSettings.navMeshBMin[2],
&m_buildSettings.navMeshBMax[0],
&m_buildSettings.navMeshBMax[1],
&m_buildSettings.navMeshBMax[2],
&m_buildSettings.tileSize);
}
}
delete [] buf;
return true;
}
bool InputGeom::load(rcContext* ctx, const std::string& filepath)
{
size_t extensionPos = filepath.find_last_of('.');
if (extensionPos == std::string::npos)
return false;
std::string extension = filepath.substr(extensionPos);
std::transform(extension.begin(), extension.end(), extension.begin(), tolower);
if (extension == ".gset")
return loadGeomSet(ctx, filepath);
if (extension == ".obj")
return loadMesh(ctx, filepath);
return false;
}
bool InputGeom::saveGeomSet(const BuildSettings* settings)
{
if (!m_mesh) return false;
// Change extension
std::string filepath = m_mesh->getFileName();
size_t extPos = filepath.find_last_of('.');
if (extPos != std::string::npos)
filepath = filepath.substr(0, extPos);
filepath += ".gset";
FILE* fp = fopen(filepath.c_str(), "w");
if (!fp) return false;
// Store mesh filename.
fprintf(fp, "f %s\n", m_mesh->getFileName().c_str());
// Store settings if any
if (settings)
{
fprintf(fp,
"s %f %f %f %f %f %f %f %f %f %f %f %f %f %d %f %f %f %f %f %f %f\n",
settings->cellSize,
settings->cellHeight,
settings->agentHeight,
settings->agentRadius,
settings->agentMaxClimb,
settings->agentMaxSlope,
settings->regionMinSize,
settings->regionMergeSize,
settings->edgeMaxLen,
settings->edgeMaxError,
settings->vertsPerPoly,
settings->detailSampleDist,
settings->detailSampleMaxError,
settings->partitionType,
settings->navMeshBMin[0],
settings->navMeshBMin[1],
settings->navMeshBMin[2],
settings->navMeshBMax[0],
settings->navMeshBMax[1],
settings->navMeshBMax[2],
settings->tileSize);
}
// Store off-mesh links.
for (int i = 0; i < m_offMeshConCount; ++i)
{
const float* v = &m_offMeshConVerts[i*3*2];
const float rad = m_offMeshConRads[i];
const int bidir = m_offMeshConDirs[i];
const int area = m_offMeshConAreas[i];
const int flags = m_offMeshConFlags[i];
fprintf(fp, "c %f %f %f %f %f %f %f %d %d %d\n",
v[0], v[1], v[2], v[3], v[4], v[5], rad, bidir, area, flags);
}
// Convex volumes
for (int i = 0; i < m_volumeCount; ++i)
{
ConvexVolume* vol = &m_volumes[i];
fprintf(fp, "v %d %d %f %f\n", vol->nverts, vol->area, vol->hmin, vol->hmax);
for (int j = 0; j < vol->nverts; ++j)
fprintf(fp, "%f %f %f\n", vol->verts[j*3+0], vol->verts[j*3+1], vol->verts[j*3+2]);
}
fclose(fp);
return true;
}
static bool isectSegAABB(const float* sp, const float* sq,
const float* amin, const float* amax,
float& tmin, float& tmax)
{
static const float EPS = 1e-6f;
float d[3];
d[0] = sq[0] - sp[0];
d[1] = sq[1] - sp[1];
d[2] = sq[2] - sp[2];
tmin = 0.0;
tmax = 1.0f;
for (int i = 0; i < 3; i++)
{
if (fabsf(d[i]) < EPS)
{
if (sp[i] < amin[i] || sp[i] > amax[i])
return false;
}
else
{
const float ood = 1.0f / d[i];
float t1 = (amin[i] - sp[i]) * ood;
float t2 = (amax[i] - sp[i]) * ood;
if (t1 > t2) { float tmp = t1; t1 = t2; t2 = tmp; }
if (t1 > tmin) tmin = t1;
if (t2 < tmax) tmax = t2;
if (tmin > tmax) return false;
}
}
return true;
}
bool InputGeom::raycastMesh(float* src, float* dst, float& tmin)
{
// Prune hit ray.
float btmin, btmax;
if (!isectSegAABB(src, dst, m_meshBMin, m_meshBMax, btmin, btmax))
return false;
float p[2], q[2];
p[0] = src[0] + (dst[0]-src[0])*btmin;
p[1] = src[2] + (dst[2]-src[2])*btmin;
q[0] = src[0] + (dst[0]-src[0])*btmax;
q[1] = src[2] + (dst[2]-src[2])*btmax;
int cid[512];
const int ncid = rcGetChunksOverlappingSegment(m_chunkyMesh, p, q, cid, 512);
if (!ncid)
return false;
tmin = 1.0f;
bool hit = false;
const float* verts = m_mesh->getVerts();
for (int i = 0; i < ncid; ++i)
{
const rcChunkyTriMeshNode& node = m_chunkyMesh->nodes[cid[i]];
const int* tris = &m_chunkyMesh->tris[node.i*3];
const int ntris = node.n;
for (int j = 0; j < ntris*3; j += 3)
{
float t = 1;
if (intersectSegmentTriangle(src, dst,
&verts[tris[j]*3],
&verts[tris[j+1]*3],
&verts[tris[j+2]*3], t))
{
if (t < tmin)
tmin = t;
hit = true;
}
}
}
return hit;
}
void InputGeom::addOffMeshConnection(const float* spos, const float* epos, const float rad,
unsigned char bidir, unsigned char area, unsigned short flags)
{
if (m_offMeshConCount >= MAX_OFFMESH_CONNECTIONS) return;
float* v = &m_offMeshConVerts[m_offMeshConCount*3*2];
m_offMeshConRads[m_offMeshConCount] = rad;
m_offMeshConDirs[m_offMeshConCount] = bidir;
m_offMeshConAreas[m_offMeshConCount] = area;
m_offMeshConFlags[m_offMeshConCount] = flags;
m_offMeshConId[m_offMeshConCount] = 1000 + m_offMeshConCount;
rcVcopy(&v[0], spos);
rcVcopy(&v[3], epos);
m_offMeshConCount++;
}
void InputGeom::deleteOffMeshConnection(int i)
{
m_offMeshConCount--;
float* src = &m_offMeshConVerts[m_offMeshConCount*3*2];
float* dst = &m_offMeshConVerts[i*3*2];
rcVcopy(&dst[0], &src[0]);
rcVcopy(&dst[3], &src[3]);
m_offMeshConRads[i] = m_offMeshConRads[m_offMeshConCount];
m_offMeshConDirs[i] = m_offMeshConDirs[m_offMeshConCount];
m_offMeshConAreas[i] = m_offMeshConAreas[m_offMeshConCount];
m_offMeshConFlags[i] = m_offMeshConFlags[m_offMeshConCount];
}
void InputGeom::drawOffMeshConnections(duDebugDraw* dd, bool hilight)
{
unsigned int conColor = duRGBA(192,0,128,192);
unsigned int baseColor = duRGBA(0,0,0,64);
dd->depthMask(false);
dd->begin(DU_DRAW_LINES, 2.0f);
for (int i = 0; i < m_offMeshConCount; ++i)
{
float* v = &m_offMeshConVerts[i*3*2];
dd->vertex(v[0],v[1],v[2], baseColor);
dd->vertex(v[0],v[1]+0.2f,v[2], baseColor);
dd->vertex(v[3],v[4],v[5], baseColor);
dd->vertex(v[3],v[4]+0.2f,v[5], baseColor);
duAppendCircle(dd, v[0],v[1]+0.1f,v[2], m_offMeshConRads[i], baseColor);
duAppendCircle(dd, v[3],v[4]+0.1f,v[5], m_offMeshConRads[i], baseColor);
if (hilight)
{
duAppendArc(dd, v[0],v[1],v[2], v[3],v[4],v[5], 0.25f,
(m_offMeshConDirs[i]&1) ? 0.6f : 0.0f, 0.6f, conColor);
}
}
dd->end();
dd->depthMask(true);
}
void InputGeom::addConvexVolume(const float* verts, const int nverts,
const float minh, const float maxh, unsigned char area)
{
if (m_volumeCount >= MAX_VOLUMES) return;
ConvexVolume* vol = &m_volumes[m_volumeCount++];
memset(vol, 0, sizeof(ConvexVolume));
memcpy(vol->verts, verts, sizeof(float)*3*nverts);
vol->hmin = minh;
vol->hmax = maxh;
vol->nverts = nverts;
vol->area = area;
}
void InputGeom::deleteConvexVolume(int i)
{
m_volumeCount--;
m_volumes[i] = m_volumes[m_volumeCount];
}
void InputGeom::drawConvexVolumes(struct duDebugDraw* dd, bool /*hilight*/)
{
dd->depthMask(false);
dd->begin(DU_DRAW_TRIS);
for (int i = 0; i < m_volumeCount; ++i)
{
const ConvexVolume* vol = &m_volumes[i];
unsigned int col = duTransCol(dd->areaToCol(vol->area), 32);
for (int j = 0, k = vol->nverts-1; j < vol->nverts; k = j++)
{
const float* va = &vol->verts[k*3];
const float* vb = &vol->verts[j*3];
dd->vertex(vol->verts[0],vol->hmax,vol->verts[2], col);
dd->vertex(vb[0],vol->hmax,vb[2], col);
dd->vertex(va[0],vol->hmax,va[2], col);
dd->vertex(va[0],vol->hmin,va[2], duDarkenCol(col));
dd->vertex(va[0],vol->hmax,va[2], col);
dd->vertex(vb[0],vol->hmax,vb[2], col);
dd->vertex(va[0],vol->hmin,va[2], duDarkenCol(col));
dd->vertex(vb[0],vol->hmax,vb[2], col);
dd->vertex(vb[0],vol->hmin,vb[2], duDarkenCol(col));
}
}
dd->end();
dd->begin(DU_DRAW_LINES, 2.0f);
for (int i = 0; i < m_volumeCount; ++i)
{
const ConvexVolume* vol = &m_volumes[i];
unsigned int col = duTransCol(dd->areaToCol(vol->area), 220);
for (int j = 0, k = vol->nverts-1; j < vol->nverts; k = j++)
{
const float* va = &vol->verts[k*3];
const float* vb = &vol->verts[j*3];
dd->vertex(va[0],vol->hmin,va[2], duDarkenCol(col));
dd->vertex(vb[0],vol->hmin,vb[2], duDarkenCol(col));
dd->vertex(va[0],vol->hmax,va[2], col);
dd->vertex(vb[0],vol->hmax,vb[2], col);
dd->vertex(va[0],vol->hmin,va[2], duDarkenCol(col));
dd->vertex(va[0],vol->hmax,va[2], col);
}
}
dd->end();
dd->begin(DU_DRAW_POINTS, 3.0f);
for (int i = 0; i < m_volumeCount; ++i)
{
const ConvexVolume* vol = &m_volumes[i];
unsigned int col = duDarkenCol(duTransCol(dd->areaToCol(vol->area), 220));
for (int j = 0; j < vol->nverts; ++j)
{
dd->vertex(vol->verts[j*3+0],vol->verts[j*3+1]+0.1f,vol->verts[j*3+2], col);
dd->vertex(vol->verts[j*3+0],vol->hmin,vol->verts[j*3+2], col);
dd->vertex(vol->verts[j*3+0],vol->hmax,vol->verts[j*3+2], col);
}
}
dd->end();
dd->depthMask(true);
}

View File

@ -0,0 +1,150 @@
//
// 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 INPUTGEOM_H
#define INPUTGEOM_H
#include "ChunkyTriMesh.h"
#include "MeshLoaderObj.h"
static const int MAX_CONVEXVOL_PTS = 12;
struct ConvexVolume
{
float verts[MAX_CONVEXVOL_PTS*3];
float hmin, hmax;
int nverts;
int area;
};
struct BuildSettings
{
// Cell size in world units
float cellSize;
// Cell height in world units
float cellHeight;
// Agent height in world units
float agentHeight;
// Agent radius in world units
float agentRadius;
// Agent max climb in world units
float agentMaxClimb;
// Agent max slope in degrees
float agentMaxSlope;
// Region minimum size in voxels.
// regionMinSize = sqrt(regionMinArea)
float regionMinSize;
// Region merge size in voxels.
// regionMergeSize = sqrt(regionMergeArea)
float regionMergeSize;
// Edge max length in world units
float edgeMaxLen;
// Edge max error in voxels
float edgeMaxError;
float vertsPerPoly;
// Detail sample distance in voxels
float detailSampleDist;
// Detail sample max error in voxel heights.
float detailSampleMaxError;
// Partition type, see SamplePartitionType
int partitionType;
// Bounds of the area to mesh
float navMeshBMin[3];
float navMeshBMax[3];
// Size of the tiles in voxels
float tileSize;
};
class InputGeom
{
rcChunkyTriMesh* m_chunkyMesh;
rcMeshLoaderObj* m_mesh;
float m_meshBMin[3], m_meshBMax[3];
BuildSettings m_buildSettings;
bool m_hasBuildSettings;
/// @name Off-Mesh connections.
///@{
static const int MAX_OFFMESH_CONNECTIONS = 256;
float m_offMeshConVerts[MAX_OFFMESH_CONNECTIONS*3*2];
float m_offMeshConRads[MAX_OFFMESH_CONNECTIONS];
unsigned char m_offMeshConDirs[MAX_OFFMESH_CONNECTIONS];
unsigned char m_offMeshConAreas[MAX_OFFMESH_CONNECTIONS];
unsigned short m_offMeshConFlags[MAX_OFFMESH_CONNECTIONS];
unsigned int m_offMeshConId[MAX_OFFMESH_CONNECTIONS];
int m_offMeshConCount;
///@}
/// @name Convex Volumes.
///@{
static const int MAX_VOLUMES = 256;
ConvexVolume m_volumes[MAX_VOLUMES];
int m_volumeCount;
///@}
bool loadMesh(class rcContext* ctx, const std::string& filepath);
bool loadGeomSet(class rcContext* ctx, const std::string& filepath);
public:
InputGeom();
~InputGeom();
bool load(class rcContext* ctx, const std::string& filepath);
bool saveGeomSet(const BuildSettings* settings);
/// Method to return static mesh data.
const rcMeshLoaderObj* getMesh() const { return m_mesh; }
const float* getMeshBoundsMin() const { return m_meshBMin; }
const float* getMeshBoundsMax() const { return m_meshBMax; }
const float* getNavMeshBoundsMin() const { return m_hasBuildSettings ? m_buildSettings.navMeshBMin : m_meshBMin; }
const float* getNavMeshBoundsMax() const { return m_hasBuildSettings ? m_buildSettings.navMeshBMax : m_meshBMax; }
const rcChunkyTriMesh* getChunkyMesh() const { return m_chunkyMesh; }
const BuildSettings* getBuildSettings() const { return m_hasBuildSettings ? &m_buildSettings : 0; }
bool raycastMesh(float* src, float* dst, float& tmin);
/// @name Off-Mesh connections.
///@{
int getOffMeshConnectionCount() const { return m_offMeshConCount; }
const float* getOffMeshConnectionVerts() const { return m_offMeshConVerts; }
const float* getOffMeshConnectionRads() const { return m_offMeshConRads; }
const unsigned char* getOffMeshConnectionDirs() const { return m_offMeshConDirs; }
const unsigned char* getOffMeshConnectionAreas() const { return m_offMeshConAreas; }
const unsigned short* getOffMeshConnectionFlags() const { return m_offMeshConFlags; }
const unsigned int* getOffMeshConnectionId() const { return m_offMeshConId; }
void addOffMeshConnection(const float* spos, const float* epos, const float rad,
unsigned char bidir, unsigned char area, unsigned short flags);
void deleteOffMeshConnection(int i);
void drawOffMeshConnections(struct duDebugDraw* dd, bool hilight = false);
///@}
/// @name Box Volumes.
///@{
int getConvexVolumeCount() const { return m_volumeCount; }
const ConvexVolume* getConvexVolumes() const { return m_volumes; }
void addConvexVolume(const float* verts, const int nverts,
const float minh, const float maxh, unsigned char area);
void deleteConvexVolume(int i);
void drawConvexVolumes(struct duDebugDraw* dd, bool hilight = false);
///@}
private:
// Explicitly disabled copy constructor and copy assignment operator.
InputGeom(const InputGeom&);
InputGeom& operator=(const InputGeom&);
};
#endif // INPUTGEOM_H

View File

@ -0,0 +1,245 @@
//
// 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 "MeshLoaderObj.h"
#include <stdio.h>
#include <stdlib.h>
#include <cstring>
#define _USE_MATH_DEFINES
#include <math.h>
rcMeshLoaderObj::rcMeshLoaderObj() :
m_scale(1.0f),
m_verts(0),
m_tris(0),
m_normals(0),
m_vertCount(0),
m_triCount(0)
{
}
rcMeshLoaderObj::~rcMeshLoaderObj()
{
delete [] m_verts;
delete [] m_normals;
delete [] m_tris;
}
void rcMeshLoaderObj::addVertex(float x, float y, float z, int& cap)
{
if (m_vertCount+1 > cap)
{
cap = !cap ? 8 : cap*2;
float* nv = new float[cap*3];
if (m_vertCount)
memcpy(nv, m_verts, m_vertCount*3*sizeof(float));
delete [] m_verts;
m_verts = nv;
}
float* dst = &m_verts[m_vertCount*3];
*dst++ = x*m_scale;
*dst++ = y*m_scale;
*dst++ = z*m_scale;
m_vertCount++;
}
void rcMeshLoaderObj::addTriangle(int a, int b, int c, int& cap)
{
if (m_triCount+1 > cap)
{
cap = !cap ? 8 : cap*2;
int* nv = new int[cap*3];
if (m_triCount)
memcpy(nv, m_tris, m_triCount*3*sizeof(int));
delete [] m_tris;
m_tris = nv;
}
int* dst = &m_tris[m_triCount*3];
*dst++ = a;
*dst++ = b;
*dst++ = c;
m_triCount++;
}
static char* parseRow(char* buf, char* bufEnd, char* row, int len)
{
bool start = true;
bool done = false;
int n = 0;
while (!done && buf < bufEnd)
{
char c = *buf;
buf++;
// multirow
switch (c)
{
case '\\':
break;
case '\n':
if (start) break;
done = true;
break;
case '\r':
break;
case '\t':
case ' ':
if (start) break;
// else falls through
default:
start = false;
row[n++] = c;
if (n >= len-1)
done = true;
break;
}
}
row[n] = '\0';
return buf;
}
static int parseFace(char* row, int* data, int n, int vcnt)
{
int j = 0;
while (*row != '\0')
{
// Skip initial white space
while (*row != '\0' && (*row == ' ' || *row == '\t'))
row++;
char* s = row;
// Find vertex delimiter and terminated the string there for conversion.
while (*row != '\0' && *row != ' ' && *row != '\t')
{
if (*row == '/') *row = '\0';
row++;
}
if (*s == '\0')
continue;
int vi = atoi(s);
data[j++] = vi < 0 ? vi+vcnt : vi-1;
if (j >= n) return j;
}
return j;
}
bool rcMeshLoaderObj::load(const std::string& filename)
{
char* buf = 0;
FILE* fp = fopen(filename.c_str(), "rb");
if (!fp)
return false;
if (fseek(fp, 0, SEEK_END) != 0)
{
fclose(fp);
return false;
}
long bufSize = ftell(fp);
if (bufSize < 0)
{
fclose(fp);
return false;
}
if (fseek(fp, 0, SEEK_SET) != 0)
{
fclose(fp);
return false;
}
buf = new char[bufSize];
if (!buf)
{
fclose(fp);
return false;
}
size_t readLen = fread(buf, bufSize, 1, fp);
fclose(fp);
if (readLen != 1)
{
delete[] buf;
return false;
}
char* src = buf;
char* srcEnd = buf + bufSize;
char row[512];
int face[32];
float x,y,z;
int nv;
int vcap = 0;
int tcap = 0;
while (src < srcEnd)
{
// Parse one row
row[0] = '\0';
src = parseRow(src, srcEnd, row, sizeof(row)/sizeof(char));
// Skip comments
if (row[0] == '#') continue;
if (row[0] == 'v' && row[1] != 'n' && row[1] != 't')
{
// Vertex pos
sscanf(row+1, "%f %f %f", &x, &y, &z);
addVertex(x, y, z, vcap);
}
if (row[0] == 'f')
{
// Faces
nv = parseFace(row+1, face, 32, m_vertCount);
for (int i = 2; i < nv; ++i)
{
const int a = face[0];
const int b = face[i-1];
const int c = face[i];
if (a < 0 || a >= m_vertCount || b < 0 || b >= m_vertCount || c < 0 || c >= m_vertCount)
continue;
addTriangle(a, b, c, tcap);
}
}
}
delete [] buf;
// Calculate normals.
m_normals = new float[m_triCount*3];
for (int i = 0; i < m_triCount*3; i += 3)
{
const float* v0 = &m_verts[m_tris[i]*3];
const float* v1 = &m_verts[m_tris[i+1]*3];
const float* v2 = &m_verts[m_tris[i+2]*3];
float e0[3], e1[3];
for (int j = 0; j < 3; ++j)
{
e0[j] = v1[j] - v0[j];
e1[j] = v2[j] - v0[j];
}
float* n = &m_normals[i];
n[0] = e0[1]*e1[2] - e0[2]*e1[1];
n[1] = e0[2]*e1[0] - e0[0]*e1[2];
n[2] = e0[0]*e1[1] - e0[1]*e1[0];
float d = sqrtf(n[0]*n[0] + n[1]*n[1] + n[2]*n[2]);
if (d > 0)
{
d = 1.0f/d;
n[0] *= d;
n[1] *= d;
n[2] *= d;
}
}
m_filename = filename;
return true;
}

View File

@ -0,0 +1,56 @@
//
// 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 MESHLOADER_OBJ
#define MESHLOADER_OBJ
#include <string>
class rcMeshLoaderObj
{
public:
rcMeshLoaderObj();
~rcMeshLoaderObj();
bool load(const std::string& fileName);
const float* getVerts() const { return m_verts; }
const float* getNormals() const { return m_normals; }
const int* getTris() const { return m_tris; }
int getVertCount() const { return m_vertCount; }
int getTriCount() const { return m_triCount; }
const std::string& getFileName() const { return m_filename; }
private:
// Explicitly disabled copy constructor and copy assignment operator.
rcMeshLoaderObj(const rcMeshLoaderObj&);
rcMeshLoaderObj& operator=(const rcMeshLoaderObj&);
void addVertex(float x, float y, float z, int& cap);
void addTriangle(int a, int b, int c, int& cap);
std::string m_filename;
float m_scale;
float* m_verts;
int* m_tris;
float* m_normals;
int m_vertCount;
int m_triCount;
};
#endif // MESHLOADER_OBJ

View 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.
//
#include "PerfTimer.h"
#if defined(WIN32)
// Win32
#include <windows.h>
TimeVal getPerfTime()
{
__int64 count;
QueryPerformanceCounter((LARGE_INTEGER*)&count);
return count;
}
int getPerfTimeUsec(const TimeVal duration)
{
static __int64 freq = 0;
if (freq == 0)
QueryPerformanceFrequency((LARGE_INTEGER*)&freq);
return (int)(duration*1000000 / freq);
}
#else
// Linux, BSD, OSX
#include <sys/time.h>
TimeVal getPerfTime()
{
timeval now;
gettimeofday(&now, 0);
return (TimeVal)now.tv_sec*1000000L + (TimeVal)now.tv_usec;
}
int getPerfTimeUsec(const TimeVal duration)
{
return (int)duration;
}
#endif

View File

@ -0,0 +1,32 @@
//
// 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 PERFTIMER_H
#define PERFTIMER_H
#ifdef __GNUC__
#include <stdint.h>
typedef int64_t TimeVal;
#else
typedef __int64 TimeVal;
#endif
TimeVal getPerfTime();
int getPerfTimeUsec(const TimeVal duration);
#endif // PERFTIMER_H

View File

@ -0,0 +1,190 @@
//
// 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 RECASTSAMPLE_H
#define RECASTSAMPLE_H
#include "Recast.h"
#include "SampleInterfaces.h"
/// Tool types.
enum SampleToolType
{
TOOL_NONE = 0,
TOOL_TILE_EDIT,
TOOL_TILE_HIGHLIGHT,
TOOL_TEMP_OBSTACLE,
TOOL_NAVMESH_TESTER,
TOOL_NAVMESH_PRUNE,
TOOL_OFFMESH_CONNECTION,
TOOL_CONVEX_VOLUME,
TOOL_CROWD,
MAX_TOOLS
};
/// These are just sample areas to use consistent values across the samples.
/// The use should specify these base on his needs.
enum SamplePolyAreas
{
SAMPLE_POLYAREA_GROUND,
SAMPLE_POLYAREA_WATER,
SAMPLE_POLYAREA_ROAD,
SAMPLE_POLYAREA_DOOR,
SAMPLE_POLYAREA_GRASS,
SAMPLE_POLYAREA_JUMP,
};
enum SamplePolyFlags
{
SAMPLE_POLYFLAGS_WALK = 0x01, // Ability to walk (ground, grass, road)
SAMPLE_POLYFLAGS_SWIM = 0x02, // Ability to swim (water).
SAMPLE_POLYFLAGS_DOOR = 0x04, // Ability to move through doors.
SAMPLE_POLYFLAGS_JUMP = 0x08, // Ability to jump.
SAMPLE_POLYFLAGS_DISABLED = 0x10, // Disabled polygon
SAMPLE_POLYFLAGS_ALL = 0xffff // All abilities.
};
class SampleDebugDraw : public DebugDrawGL
{
public:
virtual unsigned int areaToCol(unsigned int area);
};
enum SamplePartitionType
{
SAMPLE_PARTITION_WATERSHED,
SAMPLE_PARTITION_MONOTONE,
SAMPLE_PARTITION_LAYERS,
};
struct SampleTool
{
virtual ~SampleTool() {}
virtual int type() = 0;
virtual void init(class Sample* sample) = 0;
virtual void reset() = 0;
virtual void handleMenu() = 0;
virtual void handleClick(const float* s, const float* p, bool shift) = 0;
virtual void handleRender() = 0;
virtual void handleRenderOverlay(double* proj, double* model, int* view) = 0;
virtual void handleToggle() = 0;
virtual void handleStep() = 0;
virtual void handleUpdate(const float dt) = 0;
};
struct SampleToolState {
virtual ~SampleToolState() {}
virtual void init(class Sample* sample) = 0;
virtual void reset() = 0;
virtual void handleRender() = 0;
virtual void handleRenderOverlay(double* proj, double* model, int* view) = 0;
virtual void handleUpdate(const float dt) = 0;
};
class Sample
{
protected:
class InputGeom* m_geom;
class dtNavMesh* m_navMesh;
class dtNavMeshQuery* m_navQuery;
class dtCrowd* m_crowd;
unsigned char m_navMeshDrawFlags;
float m_cellSize;
float m_cellHeight;
float m_agentHeight;
float m_agentRadius;
float m_agentMaxClimb;
float m_agentMaxSlope;
float m_regionMinSize;
float m_regionMergeSize;
float m_edgeMaxLen;
float m_edgeMaxError;
float m_vertsPerPoly;
float m_detailSampleDist;
float m_detailSampleMaxError;
int m_partitionType;
bool m_filterLowHangingObstacles;
bool m_filterLedgeSpans;
bool m_filterWalkableLowHeightSpans;
SampleTool* m_tool;
SampleToolState* m_toolStates[MAX_TOOLS];
BuildContext* m_ctx;
SampleDebugDraw m_dd;
dtNavMesh* loadAll(const char* path);
void saveAll(const char* path, const dtNavMesh* mesh);
public:
Sample();
virtual ~Sample();
void setContext(BuildContext* ctx) { m_ctx = ctx; }
void setTool(SampleTool* tool);
SampleToolState* getToolState(int type) { return m_toolStates[type]; }
void setToolState(int type, SampleToolState* s) { m_toolStates[type] = s; }
SampleDebugDraw& getDebugDraw() { return m_dd; }
virtual void handleSettings();
virtual void handleTools();
virtual void handleDebugMode();
virtual void handleClick(const float* s, const float* p, bool shift);
virtual void handleToggle();
virtual void handleStep();
virtual void handleRender();
virtual void handleRenderOverlay(double* proj, double* model, int* view);
virtual void handleMeshChanged(class InputGeom* geom);
virtual bool handleBuild();
virtual void handleUpdate(const float dt);
virtual void collectSettings(struct BuildSettings& settings);
virtual class InputGeom* getInputGeom() { return m_geom; }
virtual class dtNavMesh* getNavMesh() { return m_navMesh; }
virtual class dtNavMeshQuery* getNavMeshQuery() { return m_navQuery; }
virtual class dtCrowd* getCrowd() { return m_crowd; }
virtual float getAgentRadius() { return m_agentRadius; }
virtual float getAgentHeight() { return m_agentHeight; }
virtual float getAgentClimb() { return m_agentMaxClimb; }
unsigned char getNavMeshDrawFlags() const { return m_navMeshDrawFlags; }
void setNavMeshDrawFlags(unsigned char flags) { m_navMeshDrawFlags = flags; }
void updateToolStates(const float dt);
void initToolStates(Sample* sample);
void resetToolStates();
void renderToolStates();
void renderOverlayToolStates(double* proj, double* model, int* view);
void resetCommonSettings();
void handleCommonSettings();
private:
// Explicitly disabled copy constructor and copy assignment operator.
Sample(const Sample&);
Sample& operator=(const Sample&);
};
#endif // RECASTSAMPLE_H

View File

@ -0,0 +1,99 @@
//
// 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 SAMPLEINTERFACES_H
#define SAMPLEINTERFACES_H
#include "DebugDraw.h"
#include "Recast.h"
#include "RecastDump.h"
#include "PerfTimer.h"
// These are example implementations of various interfaces used in Recast and Detour.
/// Recast build context.
class BuildContext : public rcContext
{
TimeVal m_startTime[RC_MAX_TIMERS];
TimeVal m_accTime[RC_MAX_TIMERS];
static const int MAX_MESSAGES = 1000;
const char* m_messages[MAX_MESSAGES];
int m_messageCount;
static const int TEXT_POOL_SIZE = 8000;
char m_textPool[TEXT_POOL_SIZE];
int m_textPoolSize;
public:
BuildContext();
/// Dumps the log to stdout.
void dumpLog(const char* format, ...);
/// Returns number of log messages.
int getLogCount() const;
/// Returns log message text.
const char* getLogText(const int i) const;
protected:
/// Virtual functions for custom implementations.
///@{
virtual void doResetLog();
virtual void doLog(const rcLogCategory category, const char* msg, const int len);
virtual void doResetTimers();
virtual void doStartTimer(const rcTimerLabel label);
virtual void doStopTimer(const rcTimerLabel label);
virtual int doGetAccumulatedTime(const rcTimerLabel label) const;
///@}
};
/// OpenGL debug draw implementation.
class DebugDrawGL : public duDebugDraw
{
public:
virtual void depthMask(bool state);
virtual void texture(bool state);
virtual void begin(duDebugDrawPrimitives prim, float size = 1.0f);
virtual void vertex(const float* pos, unsigned int color);
virtual void vertex(const float x, const float y, const float z, unsigned int color);
virtual void vertex(const float* pos, unsigned int color, const float* uv);
virtual void vertex(const float x, const float y, const float z, unsigned int color, const float u, const float v);
virtual void end();
};
/// stdio file implementation.
class FileIO : public duFileIO
{
FILE* m_fp;
int m_mode;
public:
FileIO();
virtual ~FileIO();
bool openForWrite(const char* path);
bool openForRead(const char* path);
virtual bool isWriting() const;
virtual bool isReading() const;
virtual bool write(const void* ptr, const size_t size);
virtual bool read(void* ptr, const size_t size);
private:
// Explicitly disabled copy constructor and copy assignment operator.
FileIO(const FileIO&);
FileIO& operator=(const FileIO&);
};
#endif // SAMPLEINTERFACES_H

49
NavMeshGenerator/Main.cpp Normal file
View File

@ -0,0 +1,49 @@
#include "Framework/InputGeom.h"
#include "NavMeshGenerator.h"
#include "RecastContext.h"
int main(int argc, char** argv)
{
std::filesystem::path inputPath;
std::filesystem::path outputPath;
bool enableLogging = false;
for (int i = 1; i < argc; ++i)
{
if (!_stricmp(argv[i], "--input"))
{
inputPath = argv[++i];
}
else if (!_stricmp(argv[i], "--output"))
{
outputPath = argv[++i];
}
else if (!_stricmp(argv[i], "--enableLogging"))
{
enableLogging = true;
}
}
InputGeom* geom = new InputGeom();
geom->load(nullptr, inputPath.string());
if (outputPath.empty())
{
printf("Warning: no output path set, no file will be generated.\n");
}
const auto context = std::make_shared<RecastContext>(enableLogging);
NavMeshGenerator generator(geom, context);
bool success = generator.BuildNavMesh();
float totalTime = context->getAccumulatedTime(RC_TIMER_TOTAL) / 1000.0f;
printf("Success: %d\n", success);
printf("Total time in milliseconds: %.2f\n", totalTime);
if (!outputPath.empty())
{
generator.Serialize(outputPath);
}
}

View File

@ -0,0 +1,527 @@
#include "NavMeshGenerator.h"
#include "DetourNavMesh.h"
#include "DetourNavMeshBuilder.h"
#include "Recast.h"
#include "RecastContext.h"
NavMeshGenerator::NavMeshGenerator(InputGeom* geom, std::shared_ptr<RecastContext> context)
: m_geom(geom),
m_navMeshQuery(dtAllocNavMeshQuery()),
m_navMesh(dtAllocNavMesh()),
m_ctx(context)
{
}
NavMeshGenerator::~NavMeshGenerator()
{
Cleanup();
}
void NavMeshGenerator::Cleanup()
{
delete[] m_triareas;
m_triareas = 0;
rcFreeHeightField(m_solid);
m_solid = 0;
rcFreeCompactHeightfield(m_chf);
m_chf = 0;
rcFreeContourSet(m_cset);
m_cset = 0;
rcFreePolyMesh(m_pmesh);
m_pmesh = 0;
rcFreePolyMeshDetail(m_dmesh);
m_dmesh = 0;
}
bool NavMeshGenerator::BuildNavMesh()
{
dtNavMeshParams params{};
rcVcopy(params.orig, m_geom->getNavMeshBoundsMin());
params.tileWidth = m_tileSize * m_cellSize;
params.tileHeight = m_tileSize * m_cellSize;
params.maxTiles = m_maxTiles;
params.maxPolys = m_maxPolysPerTile;
dtStatus status = m_navMesh->init(&params);
if (dtStatusFailed(status))
{
return false;
}
status = m_navMeshQuery->init(m_navMesh.get(), 2048);
if (dtStatusFailed(status))
{
//m_ctx->log(RC_LOG_ERROR, "buildTiledNavigation: Could not init Detour navmesh query");
return false;
}
BuildAllTiles();
return true;
}
void NavMeshGenerator::BuildAllTiles()
{
const float* bmin = m_geom->getNavMeshBoundsMin();
const float* bmax = m_geom->getNavMeshBoundsMax();
int gw = 0, gh = 0;
rcCalcGridSize(bmin, bmax, m_cellSize, &gw, &gh);
const int ts = (int)m_tileSize;
const int tw = (gw + ts - 1) / ts;
const int th = (gh + ts - 1) / ts;
const float tcs = m_tileSize * m_cellSize;
m_ctx->startTimer(RC_TIMER_TEMP);
for (int y = 0; y < th; ++y)
{
for (int x = 0; x < tw; ++x)
{
m_lastBuiltTileBmin[0] = bmin[0] + x * tcs;
m_lastBuiltTileBmin[1] = bmin[1];
m_lastBuiltTileBmin[2] = bmin[2] + y * tcs;
m_lastBuiltTileBmax[0] = bmin[0] + (x + 1) * tcs;
m_lastBuiltTileBmax[1] = bmax[1];
m_lastBuiltTileBmax[2] = bmin[2] + (y + 1) * tcs;
int dataSize = 0;
unsigned char* data = BuildTileMesh(x, y, m_lastBuiltTileBmin, m_lastBuiltTileBmax, dataSize);
if (data)
{
// Remove any previous data (navmesh owns and deletes the data).
m_navMesh->removeTile(m_navMesh->getTileRefAt(x, y, 0), 0, 0);
// Let the navmesh own the data.
dtStatus status = m_navMesh->addTile(data, dataSize, DT_TILE_FREE_DATA, 0, 0);
if (dtStatusFailed(status))
dtFree(data);
}
}
}
// Start the build process.
m_ctx->stopTimer(RC_TIMER_TEMP);
//m_totalBuildTimeMs = m_ctx->getAccumulatedTime(RC_TIMER_TEMP) / 1000.0f;
}
unsigned char* NavMeshGenerator::BuildTileMesh(const int tx, const int ty, const float* bmin, const float* bmax, int& dataSize)
{
if (!m_geom || !m_geom->getMesh() || !m_geom->getChunkyMesh())
{
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Input mesh is not specified.");
return 0;
}
m_tileMemUsage = 0;
m_tileBuildTime = 0;
Cleanup();
const float* verts = m_geom->getMesh()->getVerts();
const int nverts = m_geom->getMesh()->getVertCount();
const int ntris = m_geom->getMesh()->getTriCount();
const rcChunkyTriMesh* chunkyMesh = m_geom->getChunkyMesh();
// Init build configuration from GUI
memset(&m_cfg, 0, sizeof(m_cfg));
m_cfg.cs = m_cellSize;
m_cfg.ch = m_cellHeight;
m_cfg.walkableSlopeAngle = m_agentMaxSlope;
m_cfg.walkableHeight = (int)ceilf(m_agentHeight / m_cfg.ch);
m_cfg.walkableClimb = (int)floorf(m_agentMaxClimb / m_cfg.ch);
m_cfg.walkableRadius = (int)ceilf(m_agentRadius / m_cfg.cs);
m_cfg.maxEdgeLen = (int)(m_edgeMaxLen / m_cellSize);
m_cfg.maxSimplificationError = m_edgeMaxError;
m_cfg.minRegionArea = (int)rcSqr(m_regionMinSize); // Note: area = size*size
m_cfg.mergeRegionArea = (int)rcSqr(m_regionMergeSize); // Note: area = size*size
m_cfg.maxVertsPerPoly = (int)m_vertsPerPoly;
m_cfg.tileSize = (int)m_tileSize;
m_cfg.borderSize = m_cfg.walkableRadius + 3; // Reserve enough padding.
m_cfg.width = m_cfg.tileSize + m_cfg.borderSize * 2;
m_cfg.height = m_cfg.tileSize + m_cfg.borderSize * 2;
m_cfg.detailSampleDist = m_detailSampleDist < 0.9f ? 0 : m_cellSize * m_detailSampleDist;
m_cfg.detailSampleMaxError = m_cellHeight * m_detailSampleMaxError;
// Expand the heighfield bounding box by border size to find the extents of geometry we need to build this tile.
//
// This is done in order to make sure that the navmesh tiles connect correctly at the borders,
// and the obstacles close to the border work correctly with the dilation process.
// No polygons (or contours) will be created on the border area.
//
// IMPORTANT!
//
// :''''''''':
// : +-----+ :
// : | | :
// : | |<--- tile to build
// : | | :
// : +-----+ :<-- geometry needed
// :.........:
//
// You should use this bounding box to query your input geometry.
//
// For example if you build a navmesh for terrain, and want the navmesh tiles to match the terrain tile size
// you will need to pass in data from neighbour terrain tiles too! In a simple case, just pass in all the 8 neighbours,
// or use the bounding box below to only pass in a sliver of each of the 8 neighbours.
rcVcopy(m_cfg.bmin, bmin);
rcVcopy(m_cfg.bmax, bmax);
m_cfg.bmin[0] -= m_cfg.borderSize * m_cfg.cs;
m_cfg.bmin[2] -= m_cfg.borderSize * m_cfg.cs;
m_cfg.bmax[0] += m_cfg.borderSize * m_cfg.cs;
m_cfg.bmax[2] += m_cfg.borderSize * m_cfg.cs;
// Reset build times gathering.
m_ctx->resetTimers();
// Start the build process.
m_ctx->startTimer(RC_TIMER_TOTAL);
m_ctx->log(RC_LOG_PROGRESS, "Building navigation:");
m_ctx->log(RC_LOG_PROGRESS, " - %d x %d cells", m_cfg.width, m_cfg.height);
m_ctx->log(RC_LOG_PROGRESS, " - %.1fK verts, %.1fK tris", nverts / 1000.0f, ntris / 1000.0f);
// Allocate voxel heightfield where we rasterize our input data to.
m_solid = rcAllocHeightfield();
if (!m_solid)
{
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'solid'.");
return 0;
}
if (!rcCreateHeightfield(m_ctx.get(), *m_solid, m_cfg.width, m_cfg.height, m_cfg.bmin, m_cfg.bmax, m_cfg.cs, m_cfg.ch))
{
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not create solid heightfield.");
return 0;
}
// Allocate array that can hold triangle flags.
// If you have multiple meshes you need to process, allocate
// and array which can hold the max number of triangles you need to process.
m_triareas = new unsigned char[chunkyMesh->maxTrisPerChunk];
if (!m_triareas)
{
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'm_triareas' (%d).", chunkyMesh->maxTrisPerChunk);
return 0;
}
float tbmin[2], tbmax[2];
tbmin[0] = m_cfg.bmin[0];
tbmin[1] = m_cfg.bmin[2];
tbmax[0] = m_cfg.bmax[0];
tbmax[1] = m_cfg.bmax[2];
int cid[512];// TODO: Make grow when returning too many items.
const int ncid = rcGetChunksOverlappingRect(chunkyMesh, tbmin, tbmax, cid, 512);
if (!ncid)
return 0;
m_tileTriCount = 0;
for (int i = 0; i < ncid; ++i)
{
const rcChunkyTriMeshNode& node = chunkyMesh->nodes[cid[i]];
const int* ctris = &chunkyMesh->tris[node.i * 3];
const int nctris = node.n;
m_tileTriCount += nctris;
memset(m_triareas, 0, nctris * sizeof(unsigned char));
rcMarkWalkableTriangles(m_ctx.get(), m_cfg.walkableSlopeAngle,
verts, nverts, ctris, nctris, m_triareas);
if (!rcRasterizeTriangles(m_ctx.get(), verts, nverts, ctris, m_triareas, nctris, *m_solid, m_cfg.walkableClimb))
return 0;
}
if (!m_keepInterResults)
{
delete[] m_triareas;
m_triareas = 0;
}
// Once all geometry is rasterized, we do initial pass of filtering to
// remove unwanted overhangs caused by the conservative rasterization
// as well as filter spans where the character cannot possibly stand.
if (m_filterLowHangingObstacles)
rcFilterLowHangingWalkableObstacles(m_ctx.get(), m_cfg.walkableClimb, *m_solid);
if (m_filterLedgeSpans)
rcFilterLedgeSpans(m_ctx.get(), m_cfg.walkableHeight, m_cfg.walkableClimb, *m_solid);
if (m_filterWalkableLowHeightSpans)
rcFilterWalkableLowHeightSpans(m_ctx.get(), m_cfg.walkableHeight, *m_solid);
// Compact the heightfield so that it is faster to handle from now on.
// This will result more cache coherent data as well as the neighbours
// between walkable cells will be calculated.
m_chf = rcAllocCompactHeightfield();
if (!m_chf)
{
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'chf'.");
return 0;
}
if (!rcBuildCompactHeightfield(m_ctx.get(), m_cfg.walkableHeight, m_cfg.walkableClimb, *m_solid, *m_chf))
{
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build compact data.");
return 0;
}
if (!m_keepInterResults)
{
rcFreeHeightField(m_solid);
m_solid = 0;
}
// Erode the walkable area by agent radius.
if (!rcErodeWalkableArea(m_ctx.get(), m_cfg.walkableRadius, *m_chf))
{
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not erode.");
return 0;
}
// (Optional) Mark areas.
const ConvexVolume* vols = m_geom->getConvexVolumes();
for (int i = 0; i < m_geom->getConvexVolumeCount(); ++i)
rcMarkConvexPolyArea(m_ctx.get(), vols[i].verts, vols[i].nverts, vols[i].hmin, vols[i].hmax, (unsigned char)vols[i].area, *m_chf);
// Partition the heightfield so that we can use simple algorithm later to triangulate the walkable areas.
// There are 3 martitioning methods, each with some pros and cons:
// 1) Watershed partitioning
// - the classic Recast partitioning
// - creates the nicest tessellation
// - usually slowest
// - partitions the heightfield into nice regions without holes or overlaps
// - the are some corner cases where this method creates produces holes and overlaps
// - holes may appear when a small obstacles is close to large open area (triangulation can handle this)
// - overlaps may occur if you have narrow spiral corridors (i.e stairs), this make triangulation to fail
// * generally the best choice if you precompute the nacmesh, use this if you have large open areas
// 2) Monotone partioning
// - fastest
// - partitions the heightfield into regions without holes and overlaps (guaranteed)
// - creates long thin polygons, which sometimes causes paths with detours
// * use this if you want fast navmesh generation
// 3) Layer partitoining
// - quite fast
// - partitions the heighfield into non-overlapping regions
// - relies on the triangulation code to cope with holes (thus slower than monotone partitioning)
// - produces better triangles than monotone partitioning
// - does not have the corner cases of watershed partitioning
// - can be slow and create a bit ugly tessellation (still better than monotone)
// if you have large open areas with small obstacles (not a problem if you use tiles)
// * good choice to use for tiled navmesh with medium and small sized tiles
if (m_partitionType == SAMPLE_PARTITION_WATERSHED)
{
// Prepare for region partitioning, by calculating distance field along the walkable surface.
if (!rcBuildDistanceField(m_ctx.get(), *m_chf))
{
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build distance field.");
return 0;
}
// Partition the walkable surface into simple regions without holes.
if (!rcBuildRegions(m_ctx.get(), *m_chf, m_cfg.borderSize, m_cfg.minRegionArea, m_cfg.mergeRegionArea))
{
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build watershed regions.");
return 0;
}
}
else if (m_partitionType == SAMPLE_PARTITION_MONOTONE)
{
// Partition the walkable surface into simple regions without holes.
// Monotone partitioning does not need distancefield.
if (!rcBuildRegionsMonotone(m_ctx.get(), *m_chf, m_cfg.borderSize, m_cfg.minRegionArea, m_cfg.mergeRegionArea))
{
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build monotone regions.");
return 0;
}
}
else // SAMPLE_PARTITION_LAYERS
{
// Partition the walkable surface into simple regions without holes.
if (!rcBuildLayerRegions(m_ctx.get(), *m_chf, m_cfg.borderSize, m_cfg.minRegionArea))
{
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build layer regions.");
return 0;
}
}
// Create contours.
m_cset = rcAllocContourSet();
if (!m_cset)
{
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'cset'.");
return 0;
}
if (!rcBuildContours(m_ctx.get(), *m_chf, m_cfg.maxSimplificationError, m_cfg.maxEdgeLen, *m_cset))
{
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not create contours.");
return 0;
}
if (m_cset->nconts == 0)
{
return 0;
}
// Build polygon navmesh from the contours.
m_pmesh = rcAllocPolyMesh();
if (!m_pmesh)
{
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'pmesh'.");
return 0;
}
if (!rcBuildPolyMesh(m_ctx.get(), *m_cset, m_cfg.maxVertsPerPoly, *m_pmesh))
{
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could not triangulate contours.");
return 0;
}
// Build detail mesh.
m_dmesh = rcAllocPolyMeshDetail();
if (!m_dmesh)
{
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'dmesh'.");
return 0;
}
if (!rcBuildPolyMeshDetail(m_ctx.get(), *m_pmesh, *m_chf,
m_cfg.detailSampleDist, m_cfg.detailSampleMaxError,
*m_dmesh))
{
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Could build polymesh detail.");
return 0;
}
if (!m_keepInterResults)
{
rcFreeCompactHeightfield(m_chf);
m_chf = 0;
rcFreeContourSet(m_cset);
m_cset = 0;
}
unsigned char* navData = 0;
int navDataSize = 0;
if (m_cfg.maxVertsPerPoly <= DT_VERTS_PER_POLYGON)
{
if (m_pmesh->nverts >= 0xffff)
{
// The vertex indices are ushorts, and cannot point to more than 0xffff vertices.
m_ctx->log(RC_LOG_ERROR, "Too many vertices per tile %d (max: %d).", m_pmesh->nverts, 0xffff);
return 0;
}
// Update poly flags from areas.
for (int i = 0; i < m_pmesh->npolys; ++i)
{
if (m_pmesh->areas[i] == RC_WALKABLE_AREA)
m_pmesh->areas[i] = SAMPLE_POLYAREA_GROUND;
if (m_pmesh->areas[i] == SAMPLE_POLYAREA_GROUND ||
m_pmesh->areas[i] == SAMPLE_POLYAREA_GRASS ||
m_pmesh->areas[i] == SAMPLE_POLYAREA_ROAD)
{
m_pmesh->flags[i] = SAMPLE_POLYFLAGS_WALK;
}
else if (m_pmesh->areas[i] == SAMPLE_POLYAREA_WATER)
{
m_pmesh->flags[i] = SAMPLE_POLYFLAGS_SWIM;
}
else if (m_pmesh->areas[i] == SAMPLE_POLYAREA_DOOR)
{
m_pmesh->flags[i] = SAMPLE_POLYFLAGS_WALK | SAMPLE_POLYFLAGS_DOOR;
}
}
dtNavMeshCreateParams params{};
params.verts = m_pmesh->verts;
params.vertCount = m_pmesh->nverts;
params.polys = m_pmesh->polys;
params.polyAreas = m_pmesh->areas;
params.polyFlags = m_pmesh->flags;
params.polyCount = m_pmesh->npolys;
params.nvp = m_pmesh->nvp;
params.detailMeshes = m_dmesh->meshes;
params.detailVerts = m_dmesh->verts;
params.detailVertsCount = m_dmesh->nverts;
params.detailTris = m_dmesh->tris;
params.detailTriCount = m_dmesh->ntris;
params.offMeshConVerts = m_geom->getOffMeshConnectionVerts();
params.offMeshConRad = m_geom->getOffMeshConnectionRads();
params.offMeshConDir = m_geom->getOffMeshConnectionDirs();
params.offMeshConAreas = m_geom->getOffMeshConnectionAreas();
params.offMeshConFlags = m_geom->getOffMeshConnectionFlags();
params.offMeshConUserID = m_geom->getOffMeshConnectionId();
params.offMeshConCount = m_geom->getOffMeshConnectionCount();
params.walkableHeight = m_agentHeight;
params.walkableRadius = m_agentRadius;
params.walkableClimb = m_agentMaxClimb;
params.tileX = tx;
params.tileY = ty;
params.tileLayer = 0;
rcVcopy(params.bmin, m_pmesh->bmin);
rcVcopy(params.bmax, m_pmesh->bmax);
params.cs = m_cfg.cs;
params.ch = m_cfg.ch;
params.buildBvTree = true;
if (!dtCreateNavMeshData(&params, &navData, &navDataSize))
{
m_ctx->log(RC_LOG_ERROR, "Could not build Detour navmesh.");
return 0;
}
}
m_tileMemUsage = navDataSize / 1024.0f;
m_ctx->stopTimer(RC_TIMER_TOTAL);
// Show performance stats.
duLogBuildTimes(*m_ctx, m_ctx->getAccumulatedTime(RC_TIMER_TOTAL));
m_ctx->log(RC_LOG_PROGRESS, ">> Polymesh: %d vertices %d polygons", m_pmesh->nverts, m_pmesh->npolys);
m_tileBuildTime = m_ctx->getAccumulatedTime(RC_TIMER_TOTAL) / 1000.0f;
dataSize = navDataSize;
return navData;
}
void NavMeshGenerator::Serialize(const std::filesystem::path& path) const
{
if (!m_navMesh) return;
FILE* fp = fopen(path.string().c_str(), "wb");
if (!fp)
return;
// Store header.
NavMeshSetHeader header;
header.magic = NAVMESHSET_MAGIC;
header.version = NAVMESHSET_VERSION;
header.numTiles = 0;
for (int i = 0; i < m_navMesh->getMaxTiles(); ++i)
{
const dtNavMesh* navMesh = m_navMesh.get();
const dtMeshTile* tile = navMesh->getTile(i);
if (!tile || !tile->header || !tile->dataSize) continue;
header.numTiles++;
}
memcpy(&header.params, m_navMesh->getParams(), sizeof(dtNavMeshParams));
fwrite(&header, sizeof(NavMeshSetHeader), 1, fp);
// Store tiles.
for (int i = 0; i < m_navMesh->getMaxTiles(); ++i)
{
const dtNavMesh* navMesh = m_navMesh.get();
const dtMeshTile* tile = navMesh->getTile(i);
if (!tile || !tile->header || !tile->dataSize) continue;
NavMeshTileHeader tileHeader;
tileHeader.tileRef = m_navMesh->getTileRef(tile);
tileHeader.dataSize = tile->dataSize;
fwrite(&tileHeader, sizeof(tileHeader), 1, fp);
fwrite(tile->data, tile->dataSize, 1, fp);
}
fclose(fp);
}

View File

@ -0,0 +1,77 @@
#pragma once
// Recast
#include "Recast.h"
#include "DetourNavMesh.h"
#include "DetourNavMeshQuery.h"
// Framework
#include "Framework/InputGeom.h"
#include "Framework/Sample.h"
#include "NavMeshUtil.h"
#include "RecastContext.h"
class NavMeshGenerator
{
public:
NavMeshGenerator(InputGeom* geom, std::shared_ptr<RecastContext> context);
virtual ~NavMeshGenerator();
bool BuildNavMesh();
void Serialize(const std::filesystem::path& path) const;
private:
void BuildAllTiles();
void Cleanup();
unsigned char* BuildTileMesh(const int tx, const int ty, const float* bmin, const float* bmax, int& dataSize);
std::unique_ptr<InputGeom> m_geom;
std::unique_ptr<dtNavMesh, NavMeshDeleter> m_navMesh;
std::unique_ptr<dtNavMeshQuery, NavMeshQueryDeleter> m_navMeshQuery;
std::shared_ptr<rcContext> m_ctx;
unsigned char* m_triareas{};
rcHeightfield* m_solid{};
rcCompactHeightfield* m_chf{};
rcContourSet* m_cset{};
rcPolyMesh* m_pmesh{};
rcPolyMeshDetail* m_dmesh{};
rcConfig m_cfg{};
// Core configuration
float m_cellSize = 0.3f;
float m_cellHeight = 0.2f;
float m_agentHeight = 2.0f;
float m_agentRadius = 0.6f;
float m_agentMaxClimb = 0.9f;
float m_agentMaxSlope = 50.0f; //45.0f;
float m_regionMinSize = 8;
float m_regionMergeSize = 20;
float m_edgeMaxLen = 12.0f;
float m_edgeMaxError = 1.3f;
float m_vertsPerPoly = 6.0f;
float m_detailSampleDist = 6.0f;
float m_detailSampleMaxError = 1.0f;
int m_partitionType = SAMPLE_PARTITION_WATERSHED;
bool m_keepInterResults = false;
// Core filtering configuration
bool m_filterLowHangingObstacles = true;
bool m_filterLedgeSpans = true;
bool m_filterWalkableLowHeightSpans = true;
// Tile configuration
int m_maxTiles = 0;
int m_maxPolysPerTile = 0;
float m_tileSize = 32;
unsigned int m_tileCol{};
float m_lastBuiltTileBmin[3]{};
float m_lastBuiltTileBmax[3]{};
float m_tileMemUsage{};
float m_tileBuildTime{};
int m_tileTriCount{};
};

View File

@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30804.86
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NavMeshGenerator", "NavMeshGenerator.vcxproj", "{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.Debug|x64.ActiveCfg = Debug|x64
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.Debug|x64.Build.0 = Debug|x64
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.Debug|x86.ActiveCfg = Debug|Win32
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.Debug|x86.Build.0 = Debug|Win32
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.Release|x64.ActiveCfg = Release|x64
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.Release|x64.Build.0 = Release|x64
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.Release|x86.ActiveCfg = Release|Win32
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4274A358-82D6-4297-99D3-9647B7FC1368}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,221 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{ca9c0938-3ada-4c73-a89a-e9447abce101}</ProjectGuid>
<RootNamespace>NavMeshGenerator</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_CRT_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>..\External\recastnavigation\DebugUtils\Include;..\External\recastnavigation\Detour\Include;..\External\recastnavigation\DetourTileCache\Include;;..\External\recastnavigation\Recast\Include</AdditionalIncludeDirectories>
<LanguageStandard>stdcpplatest</LanguageStandard>
<ForcedIncludeFiles>pch.h</ForcedIncludeFiles>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>..\External\recastnavigation\DebugUtils\Include;..\External\recastnavigation\Detour\Include;..\External\recastnavigation\DetourTileCache\Include;;..\External\recastnavigation\Recast\Include</AdditionalIncludeDirectories>
<LanguageStandard>stdcpplatest</LanguageStandard>
<ForcedIncludeFiles>pch.h</ForcedIncludeFiles>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\External\recastnavigation\DebugUtils\Source\DebugDraw.cpp" />
<ClCompile Include="..\External\recastnavigation\DebugUtils\Source\DetourDebugDraw.cpp" />
<ClCompile Include="..\External\recastnavigation\DebugUtils\Source\RecastDebugDraw.cpp" />
<ClCompile Include="..\External\recastnavigation\DebugUtils\Source\RecastDump.cpp" />
<ClCompile Include="..\External\recastnavigation\DetourTileCache\Source\DetourTileCache.cpp" />
<ClCompile Include="..\External\recastnavigation\DetourTileCache\Source\DetourTileCacheBuilder.cpp" />
<ClCompile Include="..\External\recastnavigation\Detour\Source\DetourAlloc.cpp" />
<ClCompile Include="..\External\recastnavigation\Detour\Source\DetourAssert.cpp" />
<ClCompile Include="..\External\recastnavigation\Detour\Source\DetourCommon.cpp" />
<ClCompile Include="..\External\recastnavigation\Detour\Source\DetourNavMesh.cpp" />
<ClCompile Include="..\External\recastnavigation\Detour\Source\DetourNavMeshBuilder.cpp" />
<ClCompile Include="..\External\recastnavigation\Detour\Source\DetourNavMeshQuery.cpp" />
<ClCompile Include="..\External\recastnavigation\Detour\Source\DetourNode.cpp" />
<ClCompile Include="..\External\recastnavigation\Recast\Source\Recast.cpp" />
<ClCompile Include="..\External\recastnavigation\Recast\Source\RecastAlloc.cpp" />
<ClCompile Include="..\External\recastnavigation\Recast\Source\RecastArea.cpp" />
<ClCompile Include="..\External\recastnavigation\Recast\Source\RecastAssert.cpp" />
<ClCompile Include="..\External\recastnavigation\Recast\Source\RecastContour.cpp" />
<ClCompile Include="..\External\recastnavigation\Recast\Source\RecastFilter.cpp" />
<ClCompile Include="..\External\recastnavigation\Recast\Source\RecastLayers.cpp" />
<ClCompile Include="..\External\recastnavigation\Recast\Source\RecastMesh.cpp" />
<ClCompile Include="..\External\recastnavigation\Recast\Source\RecastMeshDetail.cpp" />
<ClCompile Include="..\External\recastnavigation\Recast\Source\RecastRasterization.cpp" />
<ClCompile Include="..\External\recastnavigation\Recast\Source\RecastRegion.cpp" />
<ClCompile Include="Framework\ChunkyTriMesh.cpp" />
<ClCompile Include="Framework\InputGeom.cpp" />
<ClCompile Include="Framework\MeshLoaderObj.cpp" />
<ClCompile Include="Framework\PerfTimer.cpp" />
<ClCompile Include="Main.cpp" />
<ClCompile Include="NavMeshGenerator.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="RecastContext.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\External\recastnavigation\DebugUtils\Include\DebugDraw.h" />
<ClInclude Include="..\External\recastnavigation\DebugUtils\Include\DetourDebugDraw.h" />
<ClInclude Include="..\External\recastnavigation\DebugUtils\Include\RecastDebugDraw.h" />
<ClInclude Include="..\External\recastnavigation\DebugUtils\Include\RecastDump.h" />
<ClInclude Include="..\External\recastnavigation\DetourTileCache\Include\DetourTileCache.h" />
<ClInclude Include="..\External\recastnavigation\DetourTileCache\Include\DetourTileCacheBuilder.h" />
<ClInclude Include="..\External\recastnavigation\Detour\Include\DetourAlloc.h" />
<ClInclude Include="..\External\recastnavigation\Detour\Include\DetourAssert.h" />
<ClInclude Include="..\External\recastnavigation\Detour\Include\DetourCommon.h" />
<ClInclude Include="..\External\recastnavigation\Detour\Include\DetourMath.h" />
<ClInclude Include="..\External\recastnavigation\Detour\Include\DetourNavMesh.h" />
<ClInclude Include="..\External\recastnavigation\Detour\Include\DetourNavMeshBuilder.h" />
<ClInclude Include="..\External\recastnavigation\Detour\Include\DetourNavMeshQuery.h" />
<ClInclude Include="..\External\recastnavigation\Detour\Include\DetourNode.h" />
<ClInclude Include="..\External\recastnavigation\Detour\Include\DetourStatus.h" />
<ClInclude Include="..\External\recastnavigation\Recast\Include\Recast.h" />
<ClInclude Include="..\External\recastnavigation\Recast\Include\RecastAlloc.h" />
<ClInclude Include="..\External\recastnavigation\Recast\Include\RecastAssert.h" />
<ClInclude Include="Framework\ChunkyTriMesh.h" />
<ClInclude Include="Framework\InputGeom.h" />
<ClInclude Include="Framework\MeshLoaderObj.h" />
<ClInclude Include="Framework\PerfTimer.h" />
<ClInclude Include="Framework\Sample.h" />
<ClInclude Include="Framework\SampleInterfaces.h" />
<ClInclude Include="NavMeshGenerator.h" />
<ClInclude Include="NavMeshUtil.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="RecastContext.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,243 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="Recast">
<UniqueIdentifier>{82f0fd75-bb9d-4e6c-849a-71f66d5a8f0a}</UniqueIdentifier>
</Filter>
<Filter Include="Recast\DebugUtils">
<UniqueIdentifier>{31af1122-2287-4254-860d-7d9819551310}</UniqueIdentifier>
</Filter>
<Filter Include="Recast\DebugUtils\Include">
<UniqueIdentifier>{7589cc4f-b87f-4191-81fc-17622331c7ca}</UniqueIdentifier>
</Filter>
<Filter Include="Recast\DebugUtils\Source">
<UniqueIdentifier>{203ec006-7bdb-47fa-858a-6f12327c5d44}</UniqueIdentifier>
</Filter>
<Filter Include="Recast\Detour">
<UniqueIdentifier>{b05a0256-22d5-478e-85d7-c56cef704e1e}</UniqueIdentifier>
</Filter>
<Filter Include="Recast\Detour\Source">
<UniqueIdentifier>{d30da57a-2663-4461-a272-be169bd1a3a5}</UniqueIdentifier>
</Filter>
<Filter Include="Recast\Detour\Include">
<UniqueIdentifier>{1cb89f5c-da8d-49cf-9bfa-0cb8b38fabac}</UniqueIdentifier>
</Filter>
<Filter Include="Recast\DetourTileCache">
<UniqueIdentifier>{b4850d74-861d-4dc8-aa4e-59b6f18a7754}</UniqueIdentifier>
</Filter>
<Filter Include="Recast\DetourTileCache\Include">
<UniqueIdentifier>{f0447598-7859-485b-97e8-61035bd87634}</UniqueIdentifier>
</Filter>
<Filter Include="Recast\DetourTileCache\Source">
<UniqueIdentifier>{1bc45b59-e845-4a30-b042-2c6ff3fdc3fe}</UniqueIdentifier>
</Filter>
<Filter Include="Recast\Recast">
<UniqueIdentifier>{b17dc902-8d50-4da4-9013-e1bd39206ca5}</UniqueIdentifier>
</Filter>
<Filter Include="Recast\Recast\Source">
<UniqueIdentifier>{091ace17-5d99-46d0-9d81-57fd2b4eb225}</UniqueIdentifier>
</Filter>
<Filter Include="Recast\Recast\Include">
<UniqueIdentifier>{54fffb89-f106-45f9-9a78-119f59e17fed}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\Framework">
<UniqueIdentifier>{c5ca75e6-1ec9-4c06-a733-4323f130f5c4}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\External\recastnavigation\DebugUtils\Source\DebugDraw.cpp">
<Filter>Recast\DebugUtils\Source</Filter>
</ClCompile>
<ClCompile Include="..\External\recastnavigation\DebugUtils\Source\DetourDebugDraw.cpp">
<Filter>Recast\DebugUtils\Source</Filter>
</ClCompile>
<ClCompile Include="..\External\recastnavigation\DebugUtils\Source\RecastDebugDraw.cpp">
<Filter>Recast\DebugUtils\Source</Filter>
</ClCompile>
<ClCompile Include="..\External\recastnavigation\DebugUtils\Source\RecastDump.cpp">
<Filter>Recast\DebugUtils\Source</Filter>
</ClCompile>
<ClCompile Include="..\External\recastnavigation\Detour\Source\DetourAlloc.cpp">
<Filter>Recast\Detour\Source</Filter>
</ClCompile>
<ClCompile Include="..\External\recastnavigation\Detour\Source\DetourAssert.cpp">
<Filter>Recast\Detour\Source</Filter>
</ClCompile>
<ClCompile Include="..\External\recastnavigation\Detour\Source\DetourCommon.cpp">
<Filter>Recast\Detour\Source</Filter>
</ClCompile>
<ClCompile Include="..\External\recastnavigation\Detour\Source\DetourNavMesh.cpp">
<Filter>Recast\Detour\Source</Filter>
</ClCompile>
<ClCompile Include="..\External\recastnavigation\Detour\Source\DetourNavMeshBuilder.cpp">
<Filter>Recast\Detour\Source</Filter>
</ClCompile>
<ClCompile Include="..\External\recastnavigation\Detour\Source\DetourNavMeshQuery.cpp">
<Filter>Recast\Detour\Source</Filter>
</ClCompile>
<ClCompile Include="..\External\recastnavigation\Detour\Source\DetourNode.cpp">
<Filter>Recast\Detour\Source</Filter>
</ClCompile>
<ClCompile Include="..\External\recastnavigation\DetourTileCache\Source\DetourTileCache.cpp">
<Filter>Recast\DetourTileCache\Source</Filter>
</ClCompile>
<ClCompile Include="..\External\recastnavigation\DetourTileCache\Source\DetourTileCacheBuilder.cpp">
<Filter>Recast\DetourTileCache\Source</Filter>
</ClCompile>
<ClCompile Include="..\External\recastnavigation\Recast\Source\Recast.cpp">
<Filter>Recast\Recast\Source</Filter>
</ClCompile>
<ClCompile Include="..\External\recastnavigation\Recast\Source\RecastAlloc.cpp">
<Filter>Recast\Recast\Source</Filter>
</ClCompile>
<ClCompile Include="..\External\recastnavigation\Recast\Source\RecastArea.cpp">
<Filter>Recast\Recast\Source</Filter>
</ClCompile>
<ClCompile Include="..\External\recastnavigation\Recast\Source\RecastAssert.cpp">
<Filter>Recast\Recast\Source</Filter>
</ClCompile>
<ClCompile Include="..\External\recastnavigation\Recast\Source\RecastContour.cpp">
<Filter>Recast\Recast\Source</Filter>
</ClCompile>
<ClCompile Include="..\External\recastnavigation\Recast\Source\RecastFilter.cpp">
<Filter>Recast\Recast\Source</Filter>
</ClCompile>
<ClCompile Include="..\External\recastnavigation\Recast\Source\RecastLayers.cpp">
<Filter>Recast\Recast\Source</Filter>
</ClCompile>
<ClCompile Include="..\External\recastnavigation\Recast\Source\RecastMesh.cpp">
<Filter>Recast\Recast\Source</Filter>
</ClCompile>
<ClCompile Include="..\External\recastnavigation\Recast\Source\RecastMeshDetail.cpp">
<Filter>Recast\Recast\Source</Filter>
</ClCompile>
<ClCompile Include="..\External\recastnavigation\Recast\Source\RecastRasterization.cpp">
<Filter>Recast\Recast\Source</Filter>
</ClCompile>
<ClCompile Include="..\External\recastnavigation\Recast\Source\RecastRegion.cpp">
<Filter>Recast\Recast\Source</Filter>
</ClCompile>
<ClCompile Include="Main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="NavMeshGenerator.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RecastContext.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Framework\ChunkyTriMesh.cpp">
<Filter>Source Files\Framework</Filter>
</ClCompile>
<ClCompile Include="Framework\InputGeom.cpp">
<Filter>Source Files\Framework</Filter>
</ClCompile>
<ClCompile Include="Framework\MeshLoaderObj.cpp">
<Filter>Source Files\Framework</Filter>
</ClCompile>
<ClCompile Include="Framework\PerfTimer.cpp">
<Filter>Source Files\Framework</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\External\recastnavigation\DebugUtils\Include\DebugDraw.h">
<Filter>Recast\DebugUtils\Include</Filter>
</ClInclude>
<ClInclude Include="..\External\recastnavigation\DebugUtils\Include\DetourDebugDraw.h">
<Filter>Recast\DebugUtils\Include</Filter>
</ClInclude>
<ClInclude Include="..\External\recastnavigation\DebugUtils\Include\RecastDebugDraw.h">
<Filter>Recast\DebugUtils\Include</Filter>
</ClInclude>
<ClInclude Include="..\External\recastnavigation\DebugUtils\Include\RecastDump.h">
<Filter>Recast\DebugUtils\Include</Filter>
</ClInclude>
<ClInclude Include="..\External\recastnavigation\Detour\Include\DetourAlloc.h">
<Filter>Recast\Detour\Include</Filter>
</ClInclude>
<ClInclude Include="..\External\recastnavigation\Detour\Include\DetourAssert.h">
<Filter>Recast\Detour\Include</Filter>
</ClInclude>
<ClInclude Include="..\External\recastnavigation\Detour\Include\DetourCommon.h">
<Filter>Recast\Detour\Include</Filter>
</ClInclude>
<ClInclude Include="..\External\recastnavigation\Detour\Include\DetourMath.h">
<Filter>Recast\Detour\Include</Filter>
</ClInclude>
<ClInclude Include="..\External\recastnavigation\Detour\Include\DetourNavMesh.h">
<Filter>Recast\Detour\Include</Filter>
</ClInclude>
<ClInclude Include="..\External\recastnavigation\Detour\Include\DetourNavMeshBuilder.h">
<Filter>Recast\Detour\Include</Filter>
</ClInclude>
<ClInclude Include="..\External\recastnavigation\Detour\Include\DetourNavMeshQuery.h">
<Filter>Recast\Detour\Include</Filter>
</ClInclude>
<ClInclude Include="..\External\recastnavigation\Detour\Include\DetourNode.h">
<Filter>Recast\Detour\Include</Filter>
</ClInclude>
<ClInclude Include="..\External\recastnavigation\Detour\Include\DetourStatus.h">
<Filter>Recast\Detour\Include</Filter>
</ClInclude>
<ClInclude Include="..\External\recastnavigation\DetourTileCache\Include\DetourTileCache.h">
<Filter>Recast\DetourTileCache\Include</Filter>
</ClInclude>
<ClInclude Include="..\External\recastnavigation\DetourTileCache\Include\DetourTileCacheBuilder.h">
<Filter>Recast\DetourTileCache\Include</Filter>
</ClInclude>
<ClInclude Include="..\External\recastnavigation\Recast\Include\Recast.h">
<Filter>Recast\Recast\Include</Filter>
</ClInclude>
<ClInclude Include="..\External\recastnavigation\Recast\Include\RecastAlloc.h">
<Filter>Recast\Recast\Include</Filter>
</ClInclude>
<ClInclude Include="..\External\recastnavigation\Recast\Include\RecastAssert.h">
<Filter>Recast\Recast\Include</Filter>
</ClInclude>
<ClInclude Include="NavMeshGenerator.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="pch.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="NavMeshUtil.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="RecastContext.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="Framework\ChunkyTriMesh.h">
<Filter>Source Files\Framework</Filter>
</ClInclude>
<ClInclude Include="Framework\InputGeom.h">
<Filter>Source Files\Framework</Filter>
</ClInclude>
<ClInclude Include="Framework\MeshLoaderObj.h">
<Filter>Source Files\Framework</Filter>
</ClInclude>
<ClInclude Include="Framework\PerfTimer.h">
<Filter>Source Files\Framework</Filter>
</ClInclude>
<ClInclude Include="Framework\Sample.h">
<Filter>Source Files\Framework</Filter>
</ClInclude>
<ClInclude Include="Framework\SampleInterfaces.h">
<Filter>Source Files\Framework</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -0,0 +1,41 @@
#pragma once
//////////////////////////////////////////////////////////
// Smart pointer deleters
struct NavMeshDeleter
{
void operator()(dtNavMesh* navMesh)
{
if (navMesh)
dtFreeNavMesh(navMesh);
}
};
struct NavMeshQueryDeleter
{
void operator()(dtNavMeshQuery* navMeshQuery)
{
if (navMeshQuery)
dtFreeNavMeshQuery(navMeshQuery);
}
};
//////////////////////////////////////////////////////////
// Serialization logic
struct NavMeshSetHeader
{
int magic;
int version;
int numTiles;
dtNavMeshParams params;
};
struct NavMeshTileHeader
{
dtTileRef tileRef;
int dataSize;
};
static const int NAVMESHSET_MAGIC = 'M' << 24 | 'S' << 16 | 'E' << 8 | 'T'; //'MSET';
static const int NAVMESHSET_VERSION = 1;

View File

@ -0,0 +1,44 @@
#include "RecastContext.h"
#include <windows.h>
RecastContext::RecastContext(bool enableLogging)
: m_enableLogging(enableLogging)
{
}
void RecastContext::doResetLog()
{
}
void RecastContext::doLog(const rcLogCategory category, const char* msg, const int len)
{
if (m_enableLogging)
printf("RECAST: %s\n", msg);
}
void RecastContext::doResetTimers()
{
for (int i = 0; i < RC_MAX_TIMERS; ++i)
m_accTime[i] = -1;
}
void RecastContext::doStartTimer(const rcTimerLabel label)
{
m_startTime[label] = getPerfTime();
}
void RecastContext::doStopTimer(const rcTimerLabel label)
{
const TimeVal endTime = getPerfTime();
const TimeVal deltaTime = endTime - m_startTime[label];
if (m_accTime[label] == -1)
m_accTime[label] = deltaTime;
else
m_accTime[label] += deltaTime;
}
int RecastContext::doGetAccumulatedTime(const rcTimerLabel label) const
{
return getPerfTimeUsec(m_accTime[label]);
}

View File

@ -0,0 +1,23 @@
#pragma once
#include "Recast.h"
#include "Framework/PerfTimer.h"
class RecastContext : public rcContext
{
public:
RecastContext(bool enableLogging);
protected:
virtual void doResetLog();
virtual void doLog(const rcLogCategory category, const char* msg, const int len);
virtual void doResetTimers();
virtual void doStartTimer(const rcTimerLabel label);
virtual void doStopTimer(const rcTimerLabel label);
virtual int doGetAccumulatedTime(const rcTimerLabel label) const;
private:
bool m_enableLogging{};
TimeVal m_startTime[RC_MAX_TIMERS]{};
TimeVal m_accTime[RC_MAX_TIMERS]{};
};

0
NavMeshGenerator/pch.cpp Normal file
View File

3
NavMeshGenerator/pch.h Normal file
View File

@ -0,0 +1,3 @@
#pragma once
#include <filesystem>

View File

@ -19,6 +19,7 @@
<RootNamespace>GiantsExp</RootNamespace>
<Keyword>Win32Proj</Keyword>
<ProjectName>imp_gbs</ProjectName>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
@ -74,7 +75,7 @@
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<CompileAs>Default</CompileAs>
<LanguageStandard>stdcpplatest</LanguageStandard>
<LanguageStandard>stdcpp17</LanguageStandard>
<ForcedIncludeFiles>stdafx.h</ForcedIncludeFiles>
</ClCompile>
<ResourceCompile>
@ -110,7 +111,7 @@
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<CompileAs>Default</CompileAs>
<LanguageStandard>stdcpplatest</LanguageStandard>
<LanguageStandard>stdcpp17</LanguageStandard>
<ForcedIncludeFiles>stdafx.h</ForcedIncludeFiles>
</ClCompile>
<ResourceCompile>
@ -142,7 +143,7 @@
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<LanguageStandard>stdcpplatest</LanguageStandard>
<LanguageStandard>stdcpp17</LanguageStandard>
<ForcedIncludeFiles>stdafx.h</ForcedIncludeFiles>
</ClCompile>
<ResourceCompile>

165
Sdk/Include/DataTypes.h Normal file
View File

@ -0,0 +1,165 @@
#pragma once
#include <float.h>
#include <math.h>
#include <memory>
#include <string>
#include <string_view>
//////////////////////////////////////////////////////////////////////////////////////
// Basic game data types and macros
typedef unsigned int uint;
typedef unsigned char UBYTE;
typedef signed char SBYTE;
typedef unsigned short UWORD;
typedef int BOOL;
typedef unsigned long ULONG;
typedef unsigned long DWORD;
typedef std::int64_t int64;
typedef std::uint64_t uint64;
#ifdef UNICODE
typedef std::wstring tstring;
typedef std::wstring_view tstring_view;
#else
typedef std::string tstring;
typedef std::string_view tstring_view;
#endif
#define countof(array) (sizeof((array)) / sizeof((array)[0]))
#define FlagSet(b, f) ((b) |= (f))
#define FlagClear(b, f) ((b) &= ~(f))
#define FlagIsClear(b, f) (!FlagIsSet(b, f))
#define FlagFlip(b, f) ((b) ^= (f))
#define FlagIsSet(b, f) (((b) & (f)) != 0)
//////////////////////////////////////////////////////////////////////////////////////
// Vectors
struct P3D
{
float x;
float y;
float z;
inline const P3D& operator -= (const P3D& rhs)
{
x -= rhs.x;
y -= rhs.y;
z -= rhs.z;
return *this;
}
inline const P3D& operator += (const P3D& rhs)
{
x += rhs.x;
y += rhs.y;
z += rhs.z;
return *this;
}
inline P3D operator - (const P3D& rhs)
{
P3D result;
result.x = x - rhs.x;
result.y = y - rhs.y;
result.z = z - rhs.z;
return result;
}
inline P3D Cross(const P3D& v2)
{
P3D result;
result.x = y * v2.z - z * v2.y;
result.y = z * v2.x - x * v2.z;
result.z = x * v2.y - y * v2.x;
return result;
}
inline float Dot(const P3D& v2)
{
return x * v2.x + y * v2.y + z * v2.z;
}
inline float Length()
{
return (float)(sqrt(x * x + y * y + z * z));
}
inline P3D Scale(float scale)
{
x *= scale;
y *= scale;
z *= scale;
return *this;
}
inline P3D Normalize()
{
const float length = Length();
P3D normalized = *this;
float factor = 0.0f;
if (length)
{
factor = 1 / length;
}
else
{
factor = 1.0f;
}
normalized.x *= factor;
normalized.y *= factor;
normalized.z *= factor;
return normalized;
}
bool IsNaN() const
{
return (_isnan(x) || _isnan(y) || _isnan(z));
}
bool Finite() const
{
return (_finite(x) && _finite(y) && _finite(z));
}
};
#pragma pack (push, 1)
// Optimized 3D vector for network packets.
struct NetP3D
{
short x, y, z;
};
#pragma pack (pop)
//////////////////////////////////////////////////////////////////////////////////////
struct RGBFloat
{
float r{};
float g{};
float b{};
};
struct VertRGB
{
unsigned char r;
unsigned char g;
unsigned char b;
};
struct UV
{
float u, v;
};

View File

@ -1,5 +1,7 @@
#pragma once
#include <DataTypes.h>
#include "NetCommon.h"
/// <summary>
@ -29,21 +31,22 @@ struct PlayerConnectedEvent : GameServerEvent
{
PlayerConnectedEvent() noexcept : GameServerEvent(GameServerEventType::PlayerConnected) { }
std::unique_ptr<PlayerInfo> info;
std::shared_ptr<PlayerInfo> info;
};
struct PlayerDisconnectedEvent : GameServerEvent
{
PlayerDisconnectedEvent() noexcept : GameServerEvent(GameServerEventType::PlayerDisconnected) { }
std::unique_ptr<PlayerInfo> info;
std::shared_ptr<PlayerInfo> info;
};
struct ChatMessageEvent : GameServerEvent
{
ChatMessageEvent() noexcept : GameServerEvent(GameServerEventType::ChatMessage) { }
std::string_view message;
tstring_view message{};
PlayerIndex senderIndex{};
};
struct WorldLoadedEvent : GameServerEvent

View File

@ -3,6 +3,8 @@
#include <vector>
#include <functional>
#include <memory>
#include <DataTypes.h>
#include <IComponent.h>
#include <IEventSource.h>
@ -19,19 +21,60 @@ struct IGameServer : IComponent, IEventSource<GameServerEventType, GameServerEve
{
virtual ~IGameServer() = default;
virtual void STDMETHODCALLTYPE SendChatMessage(const std::string_view& message, ChatColor color, int flags, PlayerIndex indexTo) = 0;
/// <summary>
/// Sends a chat message to all players or to a specified player.
/// </summary>
/// <param name="message">The chat message.</param>
/// <param name="color">Text color.</param>
/// <param name="flags">Flags for the message.</param>
/// <param name="indexTo">The index to send the message to. If 0, it will be sent to all players.</param>
virtual void STDMETHODCALLTYPE SendChatMessage(const tstring_view& message, ChatColor color, int flags, PlayerIndex indexTo) = 0;
/// <summary>
/// Bans the player at the specified index.
/// </summary>
/// <param name="index">The player index.</param>
/// <returns></returns>
virtual void STDMETHODCALLTYPE BanPlayer(int index) = 0;
/// <summary>
/// Kicks the player at the specified index.
/// </summary>
/// <param name="index">The player index.</param>
/// <param name="reason">The reason for kicking the player.</param>
/// <returns></returns>
virtual void STDMETHODCALLTYPE KickPlayer(int index, KickReason reason) = 0;
virtual PlayerInfo STDMETHODCALLTYPE GetPlayer(int index) = 0;
/// <summary>
/// Gets player data for the specified index.
/// </summary>
/// <param name="index">The zero-based player index.</param>
/// <throws>std::out_of_range</throws>
virtual const std::shared_ptr<PlayerInfo> STDMETHODCALLTYPE GetPlayer(int index) const = 0;
virtual std::vector<PlayerInfo> STDMETHODCALLTYPE GetPlayers() = 0;
/// <summary>
/// Gets data for all players in the current game.
/// </summary>
virtual std::vector<std::shared_ptr<PlayerInfo>> STDMETHODCALLTYPE GetPlayers() const = 0;
/// <summary>
/// Toggles or increments the specified game option.
/// Note: the behavior of this method is the same as changing an option from the F1 in-game menu.
/// </summary>
/// <param name="option"></param>
/// <returns></returns>
virtual void STDMETHODCALLTYPE ChangeGameOption(GameOption option) = 0;
virtual NetGameDetails STDMETHODCALLTYPE GetGameDetails() = 0;
/// <summary>
/// Gets details for the current game.
/// </summary>
virtual const std::shared_ptr<NetGameDetails> STDMETHODCALLTYPE GetGameDetails() const = 0;
/// <summary>
/// Modifies the settings for the current game.
/// </summary>
/// <param name="gameDetails">The game details.</param>
virtual void STDMETHODCALLTYPE ChangeGameDetails(const NetGameDetails& gameDetails) = 0;
};
struct DECLSPEC_UUID("{B2D67EE7-8063-488F-B3B9-E7DA675CB752}") IGameServer;

View File

@ -1,5 +1,6 @@
#pragma once
#include <DataTypes.h>
#include <IComponent.h>
#include <string>
@ -14,7 +15,7 @@ inline const GUID IID_ITextLookupService = { 0x770debd3, 0x165d, 0x4340, 0x82, 0
/// </summary>
struct ITextLookupService : public IComponent
{
virtual std::string STDMETHODCALLTYPE GetLocalized(std::string_view lookup) = 0;
virtual std::string STDMETHODCALLTYPE GetLocalized(tstring_view lookup) = 0;
virtual std::string STDMETHODCALLTYPE GetNetPlayerStateName(enum class NetPlayerState state) = 0;

1
ServerConsoleExample/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
vcpkg_installed/

View File

@ -93,6 +93,9 @@
</PropertyGroup>
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
<PropertyGroup Label="Vcpkg">
<VcpkgEnableManifest>true</VcpkgEnableManifest>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
@ -100,7 +103,7 @@
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_WINDOWS;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;_DEBUG;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<AdditionalIncludeDirectories>..\..\Sdk\Include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\Sdk\Include</AdditionalIncludeDirectories>
<EnablePREfast>true</EnablePREfast>
<LanguageStandard>stdcpplatest</LanguageStandard>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
@ -154,7 +157,7 @@
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_WINDOWS;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;NDEBUG;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<AdditionalIncludeDirectories>..\..\Sdk\Include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\Sdk\Include</AdditionalIncludeDirectories>
<EnablePREfast>true</EnablePREfast>
<LanguageStandard>stdcpplatest</LanguageStandard>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>

View File

@ -132,7 +132,7 @@ void ServerDialog::RefreshPlayers()
const auto& pGameServer = m_pContainer->Get<IGameServer>();
for (const auto& player : pGameServer->GetPlayers())
{
if (player.host)
if (player->host)
{
continue; // Skip host player
}
@ -140,14 +140,14 @@ void ServerDialog::RefreshPlayers()
LVITEM item{};
item.cColumns = NumColumns;
item.mask = LVIF_COLUMNS | LVIF_PARAM;
item.lParam = player.index;
item.lParam = player->index;
const int index = PlayersListCtrl.InsertItem(&item);
PlayersListCtrl.SetItemText(index, 0, player.name.c_str());
PlayersListCtrl.SetItemText(index, 1, pTextLookupService->GetNetPlayerStateName(player.state).c_str());
PlayersListCtrl.SetItemText(index, 2, fmt::format(_T("{0}"), player.ping).c_str());
PlayersListCtrl.SetItemText(index, 3, fmt::format(_T("{0}"), player.score).c_str());
PlayersListCtrl.SetItemText(index, 4, pTextLookupService->GetPlayerTeamName(player.team).c_str());
PlayersListCtrl.SetItemText(index, 0, player->name.c_str());
PlayersListCtrl.SetItemText(index, 1, pTextLookupService->GetNetPlayerStateName(player->state).c_str());
PlayersListCtrl.SetItemText(index, 2, fmt::format(_T("{0}"), player->ping).c_str());
PlayersListCtrl.SetItemText(index, 3, fmt::format(_T("{0}"), player->score).c_str());
PlayersListCtrl.SetItemText(index, 4, pTextLookupService->GetPlayerTeamName(player->team).c_str());
}
PlayersListCtrl.SetSelectionMark(savedSelection);
@ -181,7 +181,7 @@ void ServerDialog::HandleWorldLoaded(const GameServerEvent& event)
const auto& pGameServer = m_pContainer->Get<IGameServer>();
NetGameDetails details = pGameServer->GetGameDetails();
auto details = pGameServer->GetGameDetails();
// TODO: Connect to world state controls
}

View File

@ -0,0 +1,7 @@
{
"name": "server-console",
"version-string": "1.0.0",
"dependencies": [
"fmt"
]
}