From f832fcb13270bb9b8a2f0f41f4ce9141fa1dd42b Mon Sep 17 00:00:00 2001 From: wootguy Date: Fri, 27 Oct 2023 16:51:35 -0700 Subject: [PATCH] organize move nav mesh generation code to its own class. Start creating the nav mesh structure. --- CMakeLists.txt | 6 +- src/bsp/NavMesh.cpp | 43 +++++ src/bsp/NavMesh.h | 38 ++++ src/bsp/NavMeshGenerator.cpp | 349 +++++++++++++++++++++++++++++++++++ src/bsp/NavMeshGenerator.h | 43 +++++ src/editor/BspRenderer.cpp | 302 +----------------------------- src/util/Polygon3D.cpp | 4 +- src/util/Polygon3D.h | 4 + 8 files changed, 492 insertions(+), 297 deletions(-) create mode 100644 src/bsp/NavMesh.cpp create mode 100644 src/bsp/NavMesh.h create mode 100644 src/bsp/NavMeshGenerator.cpp create mode 100644 src/bsp/NavMeshGenerator.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ef2a1c9..4454b070 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,8 @@ set(SOURCE_FILES src/util/mat4x4.h src/util/mat4x4.cpp src/util/Polygon3D.h src/util/Polygon3D.cpp src/util/PolyOctree.h src/util/PolyOctree.cpp + src/bsp/NavMesh.h src/bsp/NavMesh.cpp + src/bsp/NavMeshGenerator.h src/bsp/NavMeshGenerator.cpp # OpenGL rendering src/gl/shaders.h src/gl/shaders.cpp @@ -89,8 +91,8 @@ if(MSVC) add_subdirectory(glfw) # compile using the static runtime - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT /wd4244 /wd4018") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd /wd4244 /wd4018 /W0") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT /W0") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd /wd4244 /wd4018") # Disable C++ exceptions set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT bspguy) diff --git a/src/bsp/NavMesh.cpp b/src/bsp/NavMesh.cpp new file mode 100644 index 00000000..d22e8367 --- /dev/null +++ b/src/bsp/NavMesh.cpp @@ -0,0 +1,43 @@ +#include "NavMesh.h" +#include "GLFW/glfw3.h" +#include "PolyOctree.h" +#include "Clipper.h" + +NavMesh::NavMesh() { + clear(); +} + +void NavMesh::clear() { + memset(nodes, 0, sizeof(NavPolyNode) * MAX_NAV_POLYS); + + for (int i = 0; i < MAX_NAV_POLYS; i++) { + polys[i] = Polygon3D(); + } +} + +NavMesh::NavMesh(vector faces) { + clear(); + + for (int i = 0; i < faces.size(); i++) { + polys[i] = faces[i]; + if (faces[i].verts.size() > MAX_NAV_POLY_VERTS) + logf("Error: Face %d has %d verts (max is %d)\n", i, faces[i].verts.size(), MAX_NAV_POLY_VERTS); + } + numPolys = faces.size(); + + logf("Created nav mesh with %d polys (x%d = %d KB)\n", + numPolys, sizeof(NavPolyNode), (sizeof(NavPolyNode)*numPolys) / 1024); + + logf("NavPolyNode = %d bytes, NavLink = %d bytes\n", + sizeof(NavPolyNode), sizeof(NavLink)); +} + +vector NavMesh::getPolys() { + vector ret; + + for (int i = 0; i < numPolys; i++) { + ret.push_back(polys[i]); + } + + return ret; +} diff --git a/src/bsp/NavMesh.h b/src/bsp/NavMesh.h new file mode 100644 index 00000000..951345e2 --- /dev/null +++ b/src/bsp/NavMesh.h @@ -0,0 +1,38 @@ +#pragma once +#include "Polygon3D.h" +#include "PolyOctree.h" +#include +#include "Bsp.h" + +#define MAX_NAV_POLYS 8192 +#define MAX_NAV_POLY_VERTS 12 +#define MAX_EDGE_LINKS 8 + +struct NavLink { + uint16_t node; // which poly is linked to. 0 = end of links + int16_t zDist; // minimum height difference between the connecting edges +}; + +struct NavPolyNode { + NavLink edges[MAX_NAV_POLY_VERTS][MAX_EDGE_LINKS]; + uint32_t flags; +}; + + +class NavMesh { +public: + NavPolyNode nodes[MAX_NAV_POLYS]; + Polygon3D polys[MAX_NAV_POLYS]; + + int numPolys; + + NavMesh(); + + NavMesh(vector polys); + + void clear(); + + vector getPolys(); + +private: +}; \ No newline at end of file diff --git a/src/bsp/NavMeshGenerator.cpp b/src/bsp/NavMeshGenerator.cpp new file mode 100644 index 00000000..da8cfe23 --- /dev/null +++ b/src/bsp/NavMeshGenerator.cpp @@ -0,0 +1,349 @@ +#include "NavMeshGenerator.h" +#include "GLFW/glfw3.h" +#include "PolyOctree.h" +#include "Clipper.h" + +NavMesh* NavMeshGenerator::generate(Bsp* map, int hull) { + float NavMeshGeneratorGenStart = glfwGetTime(); + BSPMODEL& model = map->models[0]; + + vector solidFaces = getHullFaces(map, hull); + vector faces = getInteriorFaces(map, hull, solidFaces); + mergeFaces(map, faces); + cullTinyFaces(faces); + + for (int i = 0; i < solidFaces.size(); i++) { + if (solidFaces[i]) + delete solidFaces[i]; + } + + logf("Generated nav mesh in %.2fs\n", faces.size(), glfwGetTime() - NavMeshGeneratorGenStart); + + NavMesh* navmesh = new NavMesh(faces); + linkNavPolys(navmesh); + + return navmesh; +} + +vector NavMeshGenerator::getHullFaces(Bsp* map, int hull) { + float hullShrink = 0; + vector solidFaces; + + Clipper clipper; + + vector solidNodes = map->get_model_leaf_volume_cuts(0, hull, CONTENTS_SOLID); + + vector solidMeshes; + for (int k = 0; k < solidNodes.size(); k++) { + solidMeshes.push_back(clipper.clip(solidNodes[k].cuts)); + } + + // GET FACES FROM MESHES + for (int m = 0; m < solidMeshes.size(); m++) { + CMesh& mesh = solidMeshes[m]; + + for (int f = 0; f < mesh.faces.size(); f++) { + CFace& face = mesh.faces[f]; + if (!face.visible) { + continue; + } + + set uniqueFaceVerts; + + for (int k = 0; k < face.edges.size(); k++) { + for (int v = 0; v < 2; v++) { + int vertIdx = mesh.edges[face.edges[k]].verts[v]; + if (!mesh.verts[vertIdx].visible) { + continue; + } + uniqueFaceVerts.insert(vertIdx); + } + } + + vector faceVerts; + for (auto vertIdx : uniqueFaceVerts) { + faceVerts.push_back(mesh.verts[vertIdx].pos); + } + + faceVerts = getSortedPlanarVerts(faceVerts); + + if (faceVerts.size() < 3) { + //logf("Degenerate clipnode face discarded %d\n", faceVerts.size()); + continue; + } + + vec3 normal = getNormalFromVerts(faceVerts); + + if (dotProduct(face.normal, normal) < 0) { + reverse(faceVerts.begin(), faceVerts.end()); + normal = normal.invert(); + } + + Polygon3D* poly = new Polygon3D(faceVerts, solidFaces.size()); + poly->removeDuplicateVerts(); + if (hullShrink) + poly->extendAlongAxis(hullShrink); + + solidFaces.push_back(poly); + + } + } + + return solidFaces; +} + +void NavMeshGenerator::getOctreeBox(Bsp* map, vec3& min, vec3& max) { + vec3 mapMins; + vec3 mapMaxs; + map->get_bounding_box(mapMins, mapMaxs); + + min = vec3(-MAX_COORD, -MAX_COORD, -MAX_COORD); + max = vec3(MAX_COORD, MAX_COORD, MAX_COORD); + + while (isBoxContained(mapMins, mapMaxs, min * 0.5f, max * 0.5f)) { + max *= 0.5f; + min *= 0.5f; + } +} + +PolygonOctree NavMeshGenerator::createPolyOctree(Bsp* map, const vector& faces, int treeDepth) { + vec3 treeMin, treeMax; + getOctreeBox(map, treeMin, treeMax); + + logf("Create octree depth %d, size %f -> %f\n", treeDepth, treeMax.x, treeMax.x / pow(2, treeDepth)); + PolygonOctree octree(treeMin, treeMax, treeDepth); + + for (int i = 0; i < faces.size(); i++) { + octree.insertPolygon(faces[i]); + } + + return octree; +} + +vector NavMeshGenerator::getInteriorFaces(Bsp* map, int hull, vector& faces) { + PolygonOctree octree = createPolyOctree(map, faces, octreeDepth); + + int debugPoly = 0; + //debugPoly = 601; + + int avgInRegion = 0; + int regionChecks = 0; + + vector interiorFaces; + + int cuttingPolyCount = faces.size(); + int presplit = faces.size(); + int numSplits = 0; + float startTime = glfwGetTime(); + bool doSplit = true; + bool doCull = true; + bool doMerge = true; + bool doTinyCull = true; + bool walkableSurfacesOnly = true; + + vector regionPolys; + regionPolys.resize(cuttingPolyCount); + + for (int i = 0; i < faces.size(); i++) { + Polygon3D* poly = faces[i]; + //if (debugPoly && i != debugPoly && i < cuttingPolys.size()) { + // continue; + //} + if (!poly->isValid) { + continue; + } + if (walkableSurfacesOnly && poly->plane_z.z < 0.7) { + continue; + } + + //logf("debug poly idx %d -> %d\n", didx, i); + //didx++; + + //logf("Splitting %d\n", i); + + octree.getPolysInRegion(poly, regionPolys); + if (poly->idx < cuttingPolyCount) + regionPolys[poly->idx] = false; + regionChecks++; + + bool anySplits = false; + int sz = cuttingPolyCount; + + if (!doSplit || (debugPoly && i != debugPoly && i < cuttingPolyCount)) + sz = 0; + + for (int k = 0; k < sz; k++) { + if (!regionPolys[k]) { + continue; + } + Polygon3D* cutPoly = faces[k]; + avgInRegion++; + //if (k != 1547) { + // continue; + //} + + vector> splitPolys = poly->split(*cutPoly); + + if (splitPolys.size()) { + Polygon3D* newpoly0 = new Polygon3D(splitPolys[0], faces.size()); + Polygon3D* newpoly1 = new Polygon3D(splitPolys[1], faces.size()); + + if (newpoly0->area < EPSILON || newpoly1->area < EPSILON) { + delete newpoly0; + delete newpoly1; + continue; + } + + faces.push_back(newpoly0); + faces.push_back(newpoly1); + + anySplits = true; + numSplits++; + + float newArea = newpoly0->area + newpoly1->area; + if (newArea < poly->area * 0.9f) { + logf("Poly %d area shrunk by %.1f (%.1f -> %1.f)\n", i, (poly->area - newArea), poly->area, newArea); + } + + //logf("Split poly %d by %d into areas %.1f %.1f\n", i, k, newpoly0->area, newpoly1->area); + break; + } + } + if (!doSplit) { + if (i < cuttingPolyCount) { + interiorFaces.push_back(*poly); + } + } + else if (!anySplits && (map->isInteriorFace(*poly, hull) || !doCull)) { + interiorFaces.push_back(*poly); + } + } + logf("Finished cutting in %.2fs\n", (float)(glfwGetTime() - startTime)); + logf("Split %d faces into %d (%d splits)\n", presplit, faces.size(), numSplits); + logf("Average of %d in poly regions\n", regionChecks ? (avgInRegion / regionChecks) : 0); + logf("Got %d interior faces\n", interiorFaces.size()); + + return interiorFaces; +} + +void NavMeshGenerator::mergeFaces(Bsp* map, vector& faces) { + float mergeStart = glfwGetTime(); + + vec3 treeMin, treeMax; + getOctreeBox(map, treeMin, treeMax); + + int preMergePolys = faces.size(); + vector mergedFaces = faces; + int pass = 0; + int maxPass = 10; + for (pass = 0; pass <= maxPass; pass++) { + + PolygonOctree mergeOctree(treeMin, treeMax, octreeDepth); + for (int i = 0; i < mergedFaces.size(); i++) { + mergedFaces[i].idx = i; + //interiorFaces[i].removeColinearVerts(); + mergeOctree.insertPolygon(&mergedFaces[i]); + } + + vector regionPolys; + regionPolys.resize(mergedFaces.size()); + + vector newMergedFaces; + + for (int i = 0; i < mergedFaces.size(); i++) { + Polygon3D& poly = mergedFaces[i]; + if (poly.idx == -1) + continue; + //if (pass == 4 && i != 149) + // continue; + + mergeOctree.getPolysInRegion(&poly, regionPolys); + regionPolys[poly.idx] = false; + + int sz = regionPolys.size(); + bool anyMerges = false; + + for (int k = i + 1; k < sz; k++) { + if (!regionPolys[k]) { + continue; + } + Polygon3D& mergePoly = mergedFaces[k]; + /* + if (pass == 4 && k != 242) { + continue; + } + if (pass == 4) { + logf("debug time\n"); + } + */ + + Polygon3D mergedPoly = poly.merge(mergePoly); + + if (!mergedPoly.isValid || mergedPoly.verts.size() > MAX_NAV_POLY_VERTS) { + continue; + } + + anyMerges = true; + + // prevent any further merges on the original polys + mergePoly.idx = -1; + poly.idx = -1; + + newMergedFaces.push_back(mergedPoly); + break; + } + + if (!anyMerges) + newMergedFaces.push_back(poly); + } + + //logf("Removed %d polys in pass %d\n", mergedFaces.size() - newMergedFaces.size(), pass + 1); + + if (mergedFaces.size() == newMergedFaces.size() || pass == maxPass) { + break; + } + else { + mergedFaces = newMergedFaces; + } + } + + logf("Finished merging in %.2fs\n", (float)(glfwGetTime() - mergeStart)); + logf("Merged %d polys down to %d in %d passes\n", preMergePolys, mergedFaces.size(), pass); + + faces = mergedFaces; +} + +void NavMeshGenerator::cullTinyFaces(vector& faces) { + const int TINY_POLY = 64; // cull faces smaller than this + + vector finalPolys; + for (int i = 0; i < faces.size(); i++) { + if (faces[i].area < TINY_POLY) { + // TODO: only remove if there is at least one unconnected edge, + // otherwise there will be holes + continue; + } + finalPolys.push_back(faces[i]); + } + + logf("Removed %d tiny polys\n", faces.size() - finalPolys.size()); + faces = finalPolys; +} + +void NavMeshGenerator::linkNavPolys(NavMesh* mesh) { + for (int i = 0; i < mesh->numPolys; i++) { + Polygon3D& poly = mesh->polys[i]; + vector& verts = poly.topdownVerts; + + for (int k = 0; k < mesh->numPolys; k++) { + if (i == k) + continue; + Polygon3D& otherPoly = mesh->polys[k]; + vector& otherVerts = otherPoly.topdownVerts; + + for (int j = 0; j < verts.size(); j++) { + vec2& e1 = verts[j]; + } + } + } +} \ No newline at end of file diff --git a/src/bsp/NavMeshGenerator.h b/src/bsp/NavMeshGenerator.h new file mode 100644 index 00000000..36497a54 --- /dev/null +++ b/src/bsp/NavMeshGenerator.h @@ -0,0 +1,43 @@ +#pragma once +#include "Polygon3D.h" +#include "PolyOctree.h" +#include +#include "Bsp.h" +#include "NavMesh.h" + +// generates a navigation mesh for a BSP +class NavMeshGenerator { +public: + NavMeshGenerator() {} + + // generate a nav mesh from the bsp + // returns polygons used to construct the mesh + NavMesh* generate(Bsp* map, int hull); + +private: + int octreeDepth = 6; + + // get faces of the hull that form the borders of the map + vector getHullFaces(Bsp* map, int hull); + + // get smallest octree box that can contain the entire map + void getOctreeBox(Bsp* map, vec3& min, vec3& max); + + // group polys that are close together for fewer collision checks later + PolygonOctree createPolyOctree(Bsp* map, const vector& faces, int treeDepth); + + // splits faces along their intersections with each other to clip polys that extend out + // into the void, then tests each poly to see if it faces into the map or into the void. + // Returns clipped faces that face the interior of the map + vector getInteriorFaces(Bsp* map, int hull, vector& faces); + + // merged polys adjacent to each other to reduce node count + void mergeFaces(Bsp* map, vector& faces); + + // removes tiny faces + void cullTinyFaces(vector& faces); + + // links nav polys that share an edge from a top-down view + // climbability depends on game settings (gravity, stepsize, autoclimb, grapple/gauss weapon, etc.) + void linkNavPolys(NavMesh* mesh); +}; \ No newline at end of file diff --git a/src/editor/BspRenderer.cpp b/src/editor/BspRenderer.cpp index 886eb8a5..04c3fedc 100644 --- a/src/editor/BspRenderer.cpp +++ b/src/editor/BspRenderer.cpp @@ -8,7 +8,7 @@ #include "Clipper.h" #include #include "Polygon3D.h" -#include "PolyOctree.h" +#include "NavMeshGenerator.h" #include "icons/missing.h" @@ -809,298 +809,16 @@ void BspRenderer::loadClipnodes() { } void BspRenderer::generateNavMeshBuffer() { - float navMeshGenStart = glfwGetTime(); - BSPMODEL& model = map->models[0]; - RenderClipnodes* renderClip = &renderClipnodes[0]; int hull = 3; - int headnode = map->models[0].iHeadnodes[hull]; - - vec3 min = vec3(model.nMins.x, model.nMins.y, model.nMins.z); - vec3 max = vec3(model.nMaxs.x, model.nMaxs.y, model.nMaxs.z); - + RenderClipnodes* renderClip = &renderClipnodes[0]; renderClip->clipnodeBuffer[hull] = NULL; renderClip->wireframeClipnodeBuffer[hull] = NULL; - Clipper clipper; - - vector solidNodes = map->get_model_leaf_volume_cuts(0, hull, CONTENTS_SOLID); - - vector solidMeshes; - for (int k = 0; k < solidNodes.size(); k++) { - solidMeshes.push_back(clipper.clip(solidNodes[k].cuts)); - } - - vector solidFaces; - - vec3 mapMins; - vec3 mapMaxs; - map->get_bounding_box(mapMins, mapMaxs); - - vec3 treeMin = vec3(-MAX_COORD, -MAX_COORD, -MAX_COORD); - vec3 treeMax = vec3(MAX_COORD, MAX_COORD, MAX_COORD); - float hullShrink = hull == 0 ? 18 : 0; - - while (isBoxContained(mapMins, mapMaxs, treeMin * 0.5f, treeMax * 0.5f)) { - treeMax *= 0.5f; - treeMin *= 0.5f; - } - - int treedepth = 6; - logf("Create octree depth %d, size %f -> %f\n", treedepth, treeMax.x, treeMax.x / pow(2, treedepth)); - PolygonOctree octree(treeMin, treeMax, treedepth); - - // GET FACES FROM MESHES - for (int m = 0; m < solidMeshes.size(); m++) { - CMesh& mesh = solidMeshes[m]; - - for (int f = 0; f < mesh.faces.size(); f++) { - CFace& face = mesh.faces[f]; - if (!face.visible) { - continue; - } - - set uniqueFaceVerts; - - for (int k = 0; k < face.edges.size(); k++) { - for (int v = 0; v < 2; v++) { - int vertIdx = mesh.edges[face.edges[k]].verts[v]; - if (!mesh.verts[vertIdx].visible) { - continue; - } - uniqueFaceVerts.insert(vertIdx); - } - } - - vector faceVerts; - for (auto vertIdx : uniqueFaceVerts) { - faceVerts.push_back(mesh.verts[vertIdx].pos); - } - - faceVerts = getSortedPlanarVerts(faceVerts); + NavMesh* navMesh = NavMeshGenerator().generate(map, hull); + vector navPolys = navMesh->getPolys(); + delete navMesh; - if (faceVerts.size() < 3) { - //logf("Degenerate clipnode face discarded %d\n", faceVerts.size()); - continue; - } - - vec3 normal = getNormalFromVerts(faceVerts); - - if (dotProduct(face.normal, normal) < 0) { - reverse(faceVerts.begin(), faceVerts.end()); - normal = normal.invert(); - } - - Polygon3D* poly = new Polygon3D(faceVerts, solidFaces.size()); - poly->removeDuplicateVerts(); - if (hullShrink) - poly->extendAlongAxis(hullShrink); - - solidFaces.push_back(poly); - octree.insertPolygon(solidFaces[solidFaces.size()-1]); - } - } - - int debugPoly = 0; - //debugPoly = 601; - - int avgInRegion = 0; - int regionChecks = 0; - - vector cuttingPolys = solidFaces; - vector interiorFaces; - - int presplit = solidFaces.size(); - int numSplits = 0; - float startTime = glfwGetTime(); - bool doSplit = true; - bool doCull = true; - bool doMerge = true; - bool doTinyCull = true; - bool walkableSurfacesOnly = true; - const int TINY_POLY = 64; // cull faces smaller than this - - vector regionPolys; - regionPolys.resize(cuttingPolys.size()); - - vector debugSplits; - - for (int i = 0; i < solidFaces.size(); i++) { - Polygon3D* poly = solidFaces[i]; - //if (debugPoly && i != debugPoly && i < cuttingPolys.size()) { - // continue; - //} - if (!poly->isValid) { - continue; - } - if (walkableSurfacesOnly && poly->plane_z.z < 0.7) { - continue; - } - - //logf("debug poly idx %d -> %d\n", didx, i); - //didx++; - - //logf("Splitting %d\n", i); - - octree.getPolysInRegion(poly, regionPolys); - if (poly->idx < cuttingPolys.size()) - regionPolys[poly->idx] = false; - regionChecks++; - - bool anySplits = false; - int sz = cuttingPolys.size(); - - if (!doSplit || (debugPoly && i != debugPoly && i < cuttingPolys.size())) - sz = 0; - - for (int k = 0; k < sz; k++) { - if (!regionPolys[k]) { - continue; - } - Polygon3D* cutPoly = cuttingPolys[k]; - avgInRegion++; - //if (k != 1547) { - // continue; - //} - - vector> splitPolys = poly->split(*cutPoly); - - if (splitPolys.size()) { - Polygon3D* newpoly0 = new Polygon3D(splitPolys[0], solidFaces.size()); - Polygon3D* newpoly1 = new Polygon3D(splitPolys[1], solidFaces.size()); - - if (newpoly0->area < EPSILON || newpoly1->area < EPSILON) { - delete newpoly0; - delete newpoly1; - continue; - } - - solidFaces.push_back(newpoly0); - solidFaces.push_back(newpoly1); - - anySplits = true; - numSplits++; - - float newArea = newpoly0->area + newpoly1->area; - if (newArea < poly->area * 0.9f) { - logf("Poly %d area shrunk by %.1f (%.1f -> %1.f)\n", i, (poly->area - newArea), poly->area, newArea); - } - - //logf("Split poly %d by %d into areas %.1f %.1f\n", i, k, newpoly0->area, newpoly1->area); - break; - } - } - if (!doSplit) { - if (i < cuttingPolys.size()) { - interiorFaces.push_back(*poly); - } - } - else if (!anySplits && (map->isInteriorFace(*poly, hull) || !doCull)) { - interiorFaces.push_back(*poly); - } - } - logf("Finished cutting in %.2fs\n", (float)(glfwGetTime() - startTime)); - logf("Split %d faces into %d (%d splits)\n", presplit, solidFaces.size(), numSplits); - logf("Average of %d in poly regions\n", regionChecks ? (avgInRegion / regionChecks) : 0); - logf("Got %d interior faces\n", interiorFaces.size()); - - float mergeStart = glfwGetTime(); - - int preMergePolys = interiorFaces.size(); - vector mergedFaces = interiorFaces; - int pass = 0; - int maxPass = 10; - for (pass = 0; pass <= maxPass; pass++) { - - PolygonOctree mergeOctree(treeMin, treeMax, treedepth); - for (int i = 0; i < mergedFaces.size(); i++) { - mergedFaces[i].idx = i; - //interiorFaces[i].removeColinearVerts(); - mergeOctree.insertPolygon(&mergedFaces[i]); - } - regionPolys.resize(mergedFaces.size()); - - vector newMergedFaces; - - for (int i = 0; i < mergedFaces.size(); i++) { - Polygon3D& poly = mergedFaces[i]; - if (poly.idx == -1) - continue; - //if (pass == 4 && i != 149) - // continue; - - mergeOctree.getPolysInRegion(&poly, regionPolys); - regionPolys[poly.idx] = false; - - int sz = doMerge ? regionPolys.size() : 0; - bool anyMerges = false; - - for (int k = i+1; k < sz; k++) { - if (!regionPolys[k]) { - continue; - } - Polygon3D& mergePoly = mergedFaces[k]; - /* - if (pass == 4 && k != 242) { - continue; - } - if (pass == 4) { - logf("debug time\n"); - } - */ - - //poly.removeColinearVerts(); - //mergePoly.removeColinearVerts(); - Polygon3D mergedPoly = poly.merge(mergePoly); - - if (!mergedPoly.isValid) { - continue; - } - - anyMerges = true; - - // prevent any further merges on the original polys - mergePoly.idx = -1; - poly.idx = -1; - - newMergedFaces.push_back(mergedPoly); - break; - } - - if (!anyMerges) - newMergedFaces.push_back(poly); - } - - logf("Removed %d polys in pass %d\n", mergedFaces.size() - newMergedFaces.size(), pass+1); - - if (mergedFaces.size() == newMergedFaces.size() || pass == maxPass) { - break; - } - else { - mergedFaces = newMergedFaces; - } - } - - vector finalPolys; - for (int i = 0; i < mergedFaces.size(); i++) { - if (doTinyCull && mergedFaces[i].area < TINY_POLY) { - // TODO: only remove if there is at least one unconnected edge, - // otherwise there will be holes - continue; - } - finalPolys.push_back(mergedFaces[i]); - } - - logf("Finished merging in %.2fs\n", (float)(glfwGetTime() - mergeStart)); - logf("Merged %d polys down to %d in %d passes\n", preMergePolys, finalPolys.size(), pass); - logf("Removed %d tiny polys\n", mergedFaces.size() - finalPolys.size()); - - debugFaces = finalPolys; - //debugFaces = interiorFaces; - - for (int i = 0; i < solidFaces.size(); i++) { - if (solidFaces[i]) - delete solidFaces[i]; - } + debugFaces = navPolys; static COLOR4 hullColors[] = { COLOR4(255, 255, 255, 128), @@ -1114,8 +832,8 @@ void BspRenderer::generateNavMeshBuffer() { vector wireframeVerts; vector faceMaths; - for (int m = 0; m < finalPolys.size(); m++) { - Polygon3D& poly = finalPolys[m]; + for (int m = 0; m < navPolys.size(); m++) { + Polygon3D& poly = navPolys[m]; vec3 normal = poly.plane_z; @@ -1198,8 +916,6 @@ void BspRenderer::generateNavMeshBuffer() { wireOutput[i] = wireframeVerts[i]; } - logf("Navigation mesh generated\n"); - if (allVerts.size() == 0 || wireframeVerts.size() == 0) { renderClip->clipnodeBuffer[hull] = NULL; renderClip->wireframeClipnodeBuffer[hull] = NULL; @@ -1214,8 +930,6 @@ void BspRenderer::generateNavMeshBuffer() { renderClip->faceMaths[hull] = faceMaths; - logf("Generated nav mesh in %.2fs\n", glfwGetTime() - navMeshGenStart); - ofstream file(map->name + "_hull" + to_string(hull) + ".obj", ios::out | ios::trunc); for (int i = 0; i < allVerts.size(); i++) { vec3 v = vec3(allVerts[i].x, allVerts[i].y, allVerts[i].z); diff --git a/src/util/Polygon3D.cpp b/src/util/Polygon3D.cpp index b61406ae..7fffb58e 100644 --- a/src/util/Polygon3D.cpp +++ b/src/util/Polygon3D.cpp @@ -104,6 +104,7 @@ Polygon3D::Polygon3D(const vector& verts, int idx) { void Polygon3D::init() { vector triangularVerts = getTriangularVerts(this->verts); localVerts.clear(); + topdownVerts.clear(); isValid = false; center = vec3(); area = 0; @@ -136,6 +137,7 @@ void Polygon3D::init() { for (int e = 0; e < verts.size(); e++) { vec2 localPoint = project(verts[e]); localVerts.push_back(localPoint); + topdownVerts.push_back(vec2(verts[e].x, verts[e].y)); expandBoundingBox(localPoint, localMins, localMaxs); expandBoundingBox(verts[e], worldMins, worldMaxs); center += verts[e]; @@ -372,7 +374,7 @@ void Polygon3D::removeDuplicateVerts() { } if (verts.size() != newVerts.size()) { - logf("Removed %d duplicate verts\n", verts.size() - newVerts.size()); + //logf("Removed %d duplicate verts\n", verts.size() - newVerts.size()); verts = newVerts; init(); } diff --git a/src/util/Polygon3D.h b/src/util/Polygon3D.h index c4e104f5..bba8fc0d 100644 --- a/src/util/Polygon3D.h +++ b/src/util/Polygon3D.h @@ -40,6 +40,7 @@ class Polygon3D { std::vector verts; std::vector localVerts; // points relative to the plane orientation + std::vector topdownVerts; // points without a z coordinate mat4x4 worldToLocal; mat4x4 localToWorld; @@ -103,4 +104,7 @@ class Polygon3D { // get the world position of a point in the polygon's local coordinate system vec3 unproject(vec2 p); + + + bool sharesTopDownEdge(); }; \ No newline at end of file