From d03dfe645f0bb97af5b56db8483fca64c59685f7 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Wed, 3 Jul 2024 11:55:24 +0200 Subject: [PATCH] Recast: group all unconnected poly's (optimization & bug fix) Reserve poly group ID 1 (defined as DT_STRAY_POLY_GROUP) for unlinked poly's. The game skips all poly's that are marked 1. Previously, before this optimization, the AI would become stuck when they walk over an island who's poly's are marked 1 as we didn't take this engine feature into account in Recast. - Recast will now render all poly's marked 'DT_STRAY_POLY_GROUP' as red. - This patch also reduces the number of poly group id's and therefore reduces the overall size of the NavMesh. --- src/naveditor/Editor.cpp | 6 ++-- src/naveditor/GameUtils.cpp | 22 ++++++++++---- src/naveditor/include/GameUtils.h | 2 +- .../DebugUtils/Source/DetourDebugDraw.cpp | 29 ++++++++++++++----- .../recast/Detour/Include/DetourNavMesh.h | 4 +++ 5 files changed, 46 insertions(+), 17 deletions(-) diff --git a/src/naveditor/Editor.cpp b/src/naveditor/Editor.cpp index 0dd59fce..cf26ddd7 100644 --- a/src/naveditor/Editor.cpp +++ b/src/naveditor/Editor.cpp @@ -435,11 +435,11 @@ void Editor::saveAll(std::string path, dtNavMesh* mesh) // TODO: this has to be done during navmesh init, not here!!! LinkTableData linkData; - buildLinkTable(mesh, linkData); + const int tableSize = buildLinkTable(mesh, linkData); header.params.disjointPolyGroupCount = linkData.setCount; header.params.reachabilityTableCount = m_reachabilityTableCount; - header.params.reachabilityTableSize = (linkData.setCount - 1) * ((linkData.setCount + 31) / 32) + (linkData.setCount - 1) / 32; + header.params.reachabilityTableSize = tableSize; fwrite(&header, sizeof(NavMeshSetHeader), 1, fp); @@ -458,7 +458,7 @@ void Editor::saveAll(std::string path, dtNavMesh* mesh) fwrite(tile->data, tile->dataSize, 1, fp); } - std::vector reachability(header.params.reachabilityTableSize, 0); + std::vector reachability(tableSize, 0); for (int i = 0; i < linkData.setCount; i++) setReachable(reachability, linkData.setCount, i, i, true); diff --git a/src/naveditor/GameUtils.cpp b/src/naveditor/GameUtils.cpp index 27283444..b7366bf3 100644 --- a/src/naveditor/GameUtils.cpp +++ b/src/naveditor/GameUtils.cpp @@ -98,8 +98,12 @@ void unpatchTileGame(dtMeshTile* t) } } -void buildLinkTable(dtNavMesh* mesh, LinkTableData& data) +int buildLinkTable(dtNavMesh* mesh, LinkTableData& data) { + // Reserve the first 2 poly groups + data.insert_new(); // 0 = technically usable for normal poly groups, but for simplicity we reserve it for now. + data.insert_new(); // 1 = DT_STRAY_POLY_GROUP. + //clear all labels for (int i = 0; i < mesh->getMaxTiles(); ++i) { @@ -118,7 +122,7 @@ void buildLinkTable(dtNavMesh* mesh, LinkTableData& data) { dtMeshTile* tile = mesh->getTile(i); if (!tile || !tile->header || !tile->dataSize) continue; - int pcount = tile->header->polyCount; + const int pcount = tile->header->polyCount; for (int j = 0; j < pcount; j++) { dtPoly& poly = tile->polys[j]; @@ -132,16 +136,21 @@ void buildLinkTable(dtNavMesh* mesh, LinkTableData& data) if (p->disjointSetId != (unsigned short)-1) nlabels.insert(p->disjointSetId); + plink = l.next; } if (nlabels.empty()) { - poly.disjointSetId = (unsigned short)data.insert_new(); + if (poly.firstLink == DT_NULL_LINK) + poly.disjointSetId = DT_STRAY_POLY_GROUP; + else + poly.disjointSetId = (unsigned short)data.insert_new(); } else { - auto l = *nlabels.begin(); + int l = *nlabels.begin(); poly.disjointSetId = (unsigned short)l; + for (const int nl : nlabels) data.set_union(l, nl); } @@ -149,11 +158,12 @@ void buildLinkTable(dtNavMesh* mesh, LinkTableData& data) } } //second pass + // TODO[ AMOS ]: is this necessary? for (int i = 0; i < mesh->getMaxTiles(); ++i) { dtMeshTile* tile = mesh->getTile(i); if (!tile || !tile->header || !tile->dataSize) continue; - int pcount = tile->header->polyCount; + const int pcount = tile->header->polyCount; for (int j = 0; j < pcount; j++) { dtPoly& poly = tile->polys[j]; @@ -161,6 +171,8 @@ void buildLinkTable(dtNavMesh* mesh, LinkTableData& data) poly.disjointSetId = (unsigned short)id; } } + + return (data.setCount-1) * ((data.setCount + 31) / 32) + (data.setCount-1) / 32; } void setReachable(std::vector& data, int count, int id1, int id2, bool value) { diff --git a/src/naveditor/include/GameUtils.h b/src/naveditor/include/GameUtils.h index 83b53738..eb172e71 100644 --- a/src/naveditor/include/GameUtils.h +++ b/src/naveditor/include/GameUtils.h @@ -14,7 +14,7 @@ void unpatchHeaderGame(NavMeshSetHeader& h); void patchTileGame(dtMeshTile* t); void unpatchTileGame(dtMeshTile* t); -void buildLinkTable(dtNavMesh* mesh, LinkTableData& data); +int buildLinkTable(dtNavMesh* mesh, LinkTableData& data); void setReachable(std::vector& data, int count, int id1, int id2, bool value); #endif // GAMEUTILS_H \ No newline at end of file diff --git a/src/thirdparty/recast/DebugUtils/Source/DetourDebugDraw.cpp b/src/thirdparty/recast/DebugUtils/Source/DetourDebugDraw.cpp index 2b3db83e..5458eb8d 100644 --- a/src/thirdparty/recast/DebugUtils/Source/DetourDebugDraw.cpp +++ b/src/thirdparty/recast/DebugUtils/Source/DetourDebugDraw.cpp @@ -22,18 +22,31 @@ #include "Detour/Include/DetourCommon.h" #include "Detour/Include/DetourNode.h" +static unsigned int getPolySurfaceColor(const dtPoly* poly, duDebugDraw* dd) +{ + return poly->disjointSetId == DT_STRAY_POLY_GROUP + ? duTransCol(duRGBA(240,20,10,255), 170) + : duTransCol(dd->areaToCol(poly->getArea()), 170); +} + +static unsigned int getPolyBoundaryColor(const dtPoly* poly, const bool inner) +{ + return poly->disjointSetId == DT_STRAY_POLY_GROUP + ? inner ? duRGBA(32,24,0,32) : duRGBA(32,24,0,220) + : inner ? duRGBA(0,48,64,32) : duRGBA(0,48,64,220); +} + static void drawOffMeshConnectionRefPosition(duDebugDraw* dd, const dtOffMeshConnection* con) { float refPosDir[3]; dtCalcOffMeshRefPos(con->refPos, con->refYaw, DT_OFFMESH_CON_REFPOS_OFFSET, refPosDir); duAppendArrow(dd, con->refPos[0], con->refPos[1], con->refPos[2], - refPosDir[0], refPosDir[1], refPosDir[2], 0.f, 10.f, duRGBA(255, 255, 0, 255)); + refPosDir[0], refPosDir[1], refPosDir[2], 0.f, 10.f, duRGBA(255,255,0,255)); } static void drawPolyBoundaries(duDebugDraw* dd, const dtMeshTile* tile, - const unsigned int col, const float linew, - bool inner) + const float linew, bool inner) { static const float thr = 0.01f*0.01f; @@ -49,7 +62,7 @@ static void drawPolyBoundaries(duDebugDraw* dd, const dtMeshTile* tile, for (int j = 0, nj = (int)p->vertCount; j < nj; ++j) { - unsigned int c = col; + unsigned int c = getPolyBoundaryColor(p, inner); if (inner) { if (p->neis[j] == 0) continue; @@ -147,7 +160,7 @@ static void drawMeshTile(duDebugDraw* dd, const dtNavMesh& mesh, const dtNavMesh if (flags & DU_DRAWNAVMESH_COLOR_TILES) col = tileColor; else - col = duTransCol(dd->areaToCol(p->getArea()), 170); + col = getPolySurfaceColor(p, dd); } for (int j = 0; j < pd->triCount; ++j) @@ -164,11 +177,11 @@ static void drawMeshTile(duDebugDraw* dd, const dtNavMesh& mesh, const dtNavMesh } dd->end(); - // Draw inter poly boundaries - drawPolyBoundaries(dd, tile, duRGBA(0,48,64,32), 1.5f, true); + // Draw inner poly boundaries + drawPolyBoundaries(dd, tile, 1.5f, true); // Draw outer poly boundaries - drawPolyBoundaries(dd, tile, duRGBA(0,48,64,220), 2.5f, false); + drawPolyBoundaries(dd, tile, 2.5f, false); // Draw poly centers drawPolyCenters(dd, tile, duRGBA(255, 255, 255, 100), 1.0f); diff --git a/src/thirdparty/recast/Detour/Include/DetourNavMesh.h b/src/thirdparty/recast/Detour/Include/DetourNavMesh.h index 4b6147d1..0ee390df 100644 --- a/src/thirdparty/recast/Detour/Include/DetourNavMesh.h +++ b/src/thirdparty/recast/Detour/Include/DetourNavMesh.h @@ -63,6 +63,10 @@ typedef unsigned int dtTileRef; /// @ingroup detour static const int DT_VERTS_PER_POLYGON = 6; +/// A poly group that is unconnected and considered 'trash'; see [r5apex_ds + CA88B2]. +/// For reference, r2 single player NavMeshes also marked everything unconnected as '1'. +static const unsigned short DT_STRAY_POLY_GROUP = 1; + /// @{ /// @name Tile Serialization Constants /// These constants are used to detect whether a navigation tile's data