Skip to content

Commit a4fd61a

Browse files
committed
leaf-based navigation mesh
got an idea to use navigation volumes for a plugin that tells players where to go. 3D volumes are simpler to link together than flat floor polygons with differing heights, and allow more pathing possibilities. The mesh is the set of empty leaves in the clipnode tree for a crouching player. Leaves are linked together by their overlapping face regions.
1 parent 9ca8822 commit a4fd61a

20 files changed

+717
-23
lines changed

CMakeLists.txt

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,14 @@ set(SOURCE_FILES
3333
src/util/Polygon3D.h src/util/Polygon3D.cpp
3434
src/util/Line2D.h src/util/Line2D.cpp
3535
src/util/PolyOctree.h src/util/PolyOctree.cpp
36-
src/bsp/NavMesh.h src/bsp/NavMesh.cpp
37-
src/bsp/NavMeshGenerator.h src/bsp/NavMeshGenerator.cpp
3836
src/globals.h src/globals.cpp
3937

38+
# Navigation meshes
39+
src/nav/NavMesh.h src/nav/NavMesh.cpp
40+
src/nav/NavMeshGenerator.h src/nav/NavMeshGenerator.cpp
41+
src/nav/LeafNavMeshGenerator.h src/nav/LeafNavMeshGenerator.cpp
42+
src/nav/LeafNavMesh.h src/nav/LeafNavMesh.cpp
43+
4044
# OpenGL rendering
4145
src/gl/shaders.h src/gl/shaders.cpp
4246
src/gl/primitives.h src/gl/primitives.cpp
@@ -83,6 +87,7 @@ include_directories(src/editor)
8387
include_directories(src/gl)
8488
include_directories(src/qtools)
8589
include_directories(src/util)
90+
include_directories(src/nav)
8691
include_directories(imgui)
8792
include_directories(imgui/examples)
8893
include_directories(imgui/backends)
@@ -186,6 +191,16 @@ if(MSVC)
186191
src/util/Line2D.cpp
187192
src/util/PolyOctree.cpp
188193
src/util/mat4x4.cpp)
194+
195+
source_group("Header Files\\nav" FILES src/nav/NavMesh.h
196+
src/nav/NavMeshGenerator.h
197+
src/nav/LeafNavMeshGenerator.h
198+
src/nav/LeafNavMesh.h)
199+
200+
source_group("Source Files\\nav" FILES src/nav/NavMesh.cpp
201+
src/nav/NavMeshGenerator.cpp
202+
src/nav/LeafNavMeshGenerator.cpp
203+
src/nav/LeafNavMesh.cpp)
189204

190205
source_group("Header Files\\util\\lib" FILES src/util/lodepng.h)
191206

src/bsp/Bsp.cpp

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4418,28 +4418,55 @@ const char* Bsp::getLeafContentsName(int32_t contents) {
44184418
}
44194419
}
44204420

4421-
int Bsp::get_leaf(vec3 pos) {
4422-
int iNode = models->iHeadnodes[0];
4421+
int Bsp::get_leaf(vec3 pos, int hull) {
4422+
int iNode = models->iHeadnodes[hull];
4423+
4424+
if (hull == 0) {
4425+
while (iNode >= 0)
4426+
{
4427+
BSPNODE& node = nodes[iNode];
4428+
BSPPLANE& plane = planes[node.iPlane];
4429+
4430+
float d = dotProduct(plane.vNormal, pos) - plane.fDist;
4431+
if (d < 0) {
4432+
iNode = node.iChildren[1];
4433+
}
4434+
else {
4435+
iNode = node.iChildren[0];
4436+
}
4437+
}
4438+
4439+
return ~iNode;
4440+
}
4441+
4442+
int lastNode = -1;
4443+
int lastSide = 0;
44234444

44244445
while (iNode >= 0)
44254446
{
4426-
BSPNODE& node = nodes[iNode];
4447+
BSPCLIPNODE& node = clipnodes[iNode];
44274448
BSPPLANE& plane = planes[node.iPlane];
44284449

44294450
float d = dotProduct(plane.vNormal, pos) - plane.fDist;
44304451
if (d < 0) {
4452+
lastNode = iNode;
44314453
iNode = node.iChildren[1];
4454+
lastSide = 1;
44324455
}
44334456
else {
4457+
lastNode = iNode;
44344458
iNode = node.iChildren[0];
4459+
lastSide = 0;
44354460
}
44364461
}
44374462

4438-
return ~iNode;
4463+
// clipnodes don't have leaf structs, so generate an id based on the last clipnode index and
4464+
// the side of the plane that would be recursed to reach the leaf contents, if there were a leaf
4465+
return lastNode * 2 + lastSide;
44394466
}
44404467

44414468
bool Bsp::is_leaf_visible(int ileaf, vec3 pos) {
4442-
int ipvsLeaf = get_leaf(pos);
4469+
int ipvsLeaf = get_leaf(pos, 0);
44434470
BSPLEAF& pvsLeaf = leaves[ipvsLeaf];
44444471

44454472
int p = pvsLeaf.nVisOffset; // pvs offset
@@ -4491,7 +4518,7 @@ bool Bsp::is_face_visible(int faceIdx, vec3 pos, vec3 angles) {
44914518
}
44924519

44934520
int Bsp::count_visible_polys(vec3 pos, vec3 angles) {
4494-
int ipvsLeaf = get_leaf(pos);
4521+
int ipvsLeaf = get_leaf(pos, 0);
44954522
BSPLEAF& pvsLeaf = leaves[ipvsLeaf];
44964523

44974524
int p = pvsLeaf.nVisOffset; // pvs offset

src/bsp/Bsp.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ class Bsp
103103
int count_visible_polys(vec3 pos, vec3 angles);
104104

105105
// get leaf index from world position
106-
int get_leaf(vec3 pos);
106+
int get_leaf(vec3 pos, int hull);
107107

108108
// strips a collision hull from the given model index
109109
// and redirects to the given hull, if redirect>0

src/editor/BspRenderer.cpp

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "Clipper.h"
88
#include "Polygon3D.h"
99
#include "NavMeshGenerator.h"
10+
#include "LeafNavMeshGenerator.h"
1011
#include "PointEntRenderer.h"
1112
#include "Texture.h"
1213
#include "LightmapNode.h"
@@ -956,6 +957,129 @@ void BspRenderer::generateNavMeshBuffer() {
956957
file.close();
957958
}
958959

960+
void BspRenderer::generateLeafNavMeshBuffer() {
961+
int hull = 3;
962+
RenderClipnodes* renderClip = &renderClipnodes[0];
963+
renderClip->clipnodeBuffer[hull] = NULL;
964+
renderClip->wireframeClipnodeBuffer[hull] = NULL;
965+
966+
LeafNavMesh* navMesh = LeafNavMeshGenerator().generate(map, hull);
967+
g_app->debugLeafNavMesh = navMesh;
968+
969+
static COLOR4 hullColors[] = {
970+
COLOR4(255, 255, 255, 128),
971+
COLOR4(96, 255, 255, 128),
972+
COLOR4(255, 96, 255, 128),
973+
COLOR4(255, 255, 96, 128),
974+
};
975+
COLOR4 color = hullColors[hull];
976+
977+
vector<cVert> allVerts;
978+
vector<cVert> wireframeVerts;
979+
vector<FaceMath> faceMaths;
980+
981+
for (int lf = 0; lf < navMesh->numLeaves; lf++) {
982+
LeafMesh& mesh = navMesh->leaves[lf];
983+
984+
color = hullColors[hull];
985+
static int r = 0;
986+
r = (r + 1) % 8;
987+
if (r == 0) {
988+
color = COLOR4(255, 32, 32, 128);
989+
}
990+
else if (r == 1) {
991+
color = COLOR4(255, 255, 32, 128);
992+
}
993+
else if (r == 2) {
994+
color = COLOR4(255, 32, 255, 128);
995+
}
996+
else if (r == 3) {
997+
color = COLOR4(255, 128, 255, 128);
998+
}
999+
else if (r == 4) {
1000+
color = COLOR4(32, 32, 255, 128);
1001+
}
1002+
else if (r == 5) {
1003+
color = COLOR4(32, 255, 255, 128);
1004+
}
1005+
else if (r == 6) {
1006+
color = COLOR4(32, 128, 255, 128);
1007+
}
1008+
else if (r == 7) {
1009+
color = COLOR4(32, 255, 128, 128);
1010+
}
1011+
1012+
for (int m = 0; m < mesh.leafFaces.size(); m++) {
1013+
Polygon3D& poly = mesh.leafFaces[m];
1014+
1015+
vec3 normal = poly.plane_z;
1016+
1017+
// calculations for face picking
1018+
{
1019+
FaceMath faceMath;
1020+
faceMath.normal = normal;
1021+
faceMath.fdist = poly.fdist;
1022+
faceMath.worldToLocal = poly.worldToLocal;
1023+
faceMath.localVerts = poly.localVerts;
1024+
faceMaths.push_back(faceMath);
1025+
}
1026+
1027+
// create the verts for rendering
1028+
{
1029+
vector<vec3> renderVerts;
1030+
renderVerts.resize(poly.verts.size());
1031+
for (int i = 0; i < poly.verts.size(); i++) {
1032+
renderVerts[i] = poly.verts[i].flip();
1033+
}
1034+
1035+
COLOR4 wireframeColor = { 0, 0, 0, 255 };
1036+
for (int k = 0; k < renderVerts.size(); k++) {
1037+
wireframeVerts.push_back(cVert(renderVerts[k], wireframeColor));
1038+
wireframeVerts.push_back(cVert(renderVerts[(k + 1) % renderVerts.size()], wireframeColor));
1039+
}
1040+
1041+
vec3 lightDir = vec3(1, 1, -1).normalize();
1042+
float dot = (dotProduct(normal, lightDir) + 1) / 2.0f;
1043+
if (dot > 0.5f) {
1044+
dot = dot * dot;
1045+
}
1046+
COLOR4 faceColor = color * (dot);
1047+
1048+
// convert from TRIANGLE_FAN style verts to TRIANGLES
1049+
for (int k = 2; k < renderVerts.size(); k++) {
1050+
allVerts.push_back(cVert(renderVerts[0], faceColor));
1051+
allVerts.push_back(cVert(renderVerts[k - 1], faceColor));
1052+
allVerts.push_back(cVert(renderVerts[k], faceColor));
1053+
}
1054+
}
1055+
}
1056+
}
1057+
1058+
cVert* output = new cVert[allVerts.size()];
1059+
for (int i = 0; i < allVerts.size(); i++) {
1060+
output[i] = allVerts[i];
1061+
}
1062+
1063+
cVert* wireOutput = new cVert[wireframeVerts.size()];
1064+
for (int i = 0; i < wireframeVerts.size(); i++) {
1065+
wireOutput[i] = wireframeVerts[i];
1066+
}
1067+
1068+
if (allVerts.size() == 0 || wireframeVerts.size() == 0) {
1069+
renderClip->clipnodeBuffer[hull] = NULL;
1070+
renderClip->wireframeClipnodeBuffer[hull] = NULL;
1071+
return;
1072+
}
1073+
1074+
renderClip->clipnodeBuffer[hull] = new VertexBuffer(colorShader, COLOR_4B | POS_3F, output, allVerts.size());
1075+
renderClip->clipnodeBuffer[hull]->ownData = true;
1076+
1077+
renderClip->wireframeClipnodeBuffer[hull] = new VertexBuffer(colorShader, COLOR_4B | POS_3F, wireOutput, wireframeVerts.size());
1078+
renderClip->wireframeClipnodeBuffer[hull]->ownData = true;
1079+
1080+
renderClip->faceMaths[hull] = faceMaths;
1081+
}
1082+
9591083
void BspRenderer::generateClipnodeBuffer(int modelIdx) {
9601084
BSPMODEL& model = map->models[modelIdx];
9611085
RenderClipnodes* renderClip = &renderClipnodes[modelIdx];
@@ -1120,6 +1244,7 @@ void BspRenderer::generateClipnodeBuffer(int modelIdx) {
11201244

11211245
if (modelIdx == 0) {
11221246
//generateNavMeshBuffer();
1247+
//generateLeafNavMeshBuffer();
11231248
}
11241249
}
11251250

src/editor/BspRenderer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ class BspRenderer {
205205
void loadClipnodes();
206206
void generateClipnodeBuffer(int modelIdx);
207207
void generateNavMeshBuffer();
208+
void generateLeafNavMeshBuffer();
208209
void deleteRenderModel(RenderModel* renderModel);
209210
void deleteRenderModelClipnodes(RenderClipnodes* renderModel);
210211
void deleteRenderClipnodes();

src/editor/Gui.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1951,6 +1951,9 @@ void Gui::drawDebugWidget() {
19511951
if (i == 0) {
19521952
ImGui::Text("Leaf: %d", leafIdx);
19531953
}
1954+
else {
1955+
ImGui::Text("Pseudo ID: %d", map->get_leaf(localCamera, i));
1956+
}
19541957
ImGui::Text("Parent Node: %d (child %d)",
19551958
nodeBranch.size() ? nodeBranch[nodeBranch.size() - 1] : headNode,
19561959
childIdx);

src/editor/Renderer.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <fstream>
1515
#include "globals.h"
1616
#include "NavMesh.h"
17+
#include "LeafNavMesh.h"
1718
#include <algorithm>
1819
#include "BspMerger.h"
1920

@@ -453,6 +454,71 @@ void Renderer::renderLoop() {
453454
}
454455
}
455456

457+
if (debugLeafNavMesh) {
458+
glLineWidth(1);
459+
460+
int leafIdx = mapRenderers[0]->map->get_leaf(cameraOrigin, 3);
461+
int leafNavIdx = MAX_NAV_LEAVES;
462+
463+
if (leafIdx >= 0 && leafIdx < MAX_MAP_CLIPNODE_LEAVES) {
464+
leafNavIdx = debugLeafNavMesh->leafMap[leafIdx];
465+
}
466+
467+
if (leafNavIdx < MAX_NAV_LEAVES) {
468+
LeafNavNode& node = debugLeafNavMesh->nodes[leafNavIdx];
469+
LeafMesh& leaf = debugLeafNavMesh->leaves[leafNavIdx];
470+
471+
drawBox(leaf.center, 2, COLOR4(0, 255, 0, 255));
472+
473+
std::string linkStr;
474+
475+
for (int i = 0; i < MAX_NAV_LEAF_LINKS; i++) {
476+
LeafNavLink& link = node.links[i];
477+
if (link.node == -1) {
478+
break;
479+
}
480+
LeafMesh& linkLeaf = debugLeafNavMesh->leaves[link.node];
481+
Polygon3D& linkArea = link.linkArea;
482+
483+
drawLine(leaf.center, linkArea.center, COLOR4(0, 255, 255, 255));
484+
drawLine(linkArea.center, linkLeaf.center, COLOR4(0, 255, 255, 255));
485+
486+
for (int k = 0; k < linkArea.verts.size(); k++) {
487+
drawBox(linkArea.verts[k], 1, COLOR4(255, 255, 0, 255));
488+
}
489+
drawBox(linkArea.center, 1, COLOR4(0, 255, 0, 255));
490+
drawBox(linkLeaf.center, 2, COLOR4(0, 255, 255, 255));
491+
linkStr += to_string(link.node) + " (" + to_string(linkArea.verts.size()) + "v), ";
492+
}
493+
494+
//logf("Leaf node idx: %d, links: %s\n", leafNavIdx, linkStr.c_str());
495+
}
496+
497+
glDisable(GL_DEPTH_TEST);
498+
499+
colorShader->pushMatrix(MAT_PROJECTION);
500+
colorShader->pushMatrix(MAT_VIEW);
501+
projection.ortho(0, windowWidth, windowHeight, 0, -1.0f, 1.0f);
502+
view.loadIdentity();
503+
colorShader->updateMatrixes();
504+
505+
Line2D edge(vec2(1000, 400), vec2(1400, 630));
506+
drawLine2D(edge.start, edge.end, COLOR4(255, 0, 0, 255));
507+
508+
/*
509+
double xpos, ypos;
510+
glfwGetCursorPos(window, &xpos, &ypos);
511+
vec2 mousepos = vec2(xpos, ypos);
512+
drawBox2D(mousepos, 8, COLOR4(255, 0, 0, 255));
513+
drawBox2D(edge.project(mousepos), 8, COLOR4(255, 0, 0, 255));
514+
float dist = edge.distance(mousepos);
515+
logf("dist: %f\n", edge.distance(mousepos));
516+
*/
517+
518+
colorShader->popMatrix(MAT_PROJECTION);
519+
colorShader->popMatrix(MAT_VIEW);
520+
}
521+
456522
if (debugPoly.isValid) {
457523
if (debugPoly.verts.size() > 1) {
458524
vec3 v1 = debugPoly.verts[0];

src/editor/Renderer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ class ShaderProgram;
1414
class PointEntRenderer;
1515
class Entity;
1616
class Bsp;
17+
class LeafNavMesh;
1718

1819
enum transform_modes {
1920
TRANSFORM_NONE = -1,
@@ -77,6 +78,7 @@ class Renderer {
7778
Polygon3D debugPoly;
7879
Polygon3D debugPoly2;
7980
NavMesh* debugNavMesh = NULL;
81+
LeafNavMesh* debugLeafNavMesh = NULL;
8082
int debugNavPoly = -1;
8183
vec3 debugTraceStart;
8284
TraceResult debugTrace;

0 commit comments

Comments
 (0)