From 602fa7d1ec45b102fa28d912d2bfbb2f1fe83b3e Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Sat, 6 Jul 2024 19:51:31 +0200 Subject: [PATCH] Recast: editor improvements - Separate disjoint set and traversal table building code - Use game types and data to determine what to build into the traversal tables (e.g., 5 traversal tables for _small and only 1 for the rest). - Automatically initialize the editor using the _small navmesh parameters instead of the broken defaults. --- src/naveditor/Editor.cpp | 2 + src/naveditor/Editor_TileMesh.cpp | 48 ++++++++++++++----- src/naveditor/include/Editor.h | 6 ++- src/naveditor/include/Editor_TileMesh.h | 2 + .../recast/Detour/Include/DetourNavMesh.h | 7 ++- .../Detour/Include/DetourNavMeshBuilder.h | 16 +++++-- .../recast/Detour/Source/DetourNavMesh.cpp | 3 +- .../Detour/Source/DetourNavMeshBuilder.cpp | 35 +++++++------- 8 files changed, 82 insertions(+), 37 deletions(-) diff --git a/src/naveditor/Editor.cpp b/src/naveditor/Editor.cpp index e6cb0927..7579630f 100644 --- a/src/naveditor/Editor.cpp +++ b/src/naveditor/Editor.cpp @@ -62,6 +62,8 @@ Editor::Editor() : m_filterLowHangingObstacles(true), m_filterLedgeSpans(true), m_filterWalkableLowHeightSpans(true), + m_navMeshType(NAVMESH_SMALL), + m_navmeshName(NavMesh_GetNameForType(NAVMESH_SMALL)), m_tool(0), m_ctx(0) { diff --git a/src/naveditor/Editor_TileMesh.cpp b/src/naveditor/Editor_TileMesh.cpp index 9903cf0f..0ac6196e 100644 --- a/src/naveditor/Editor_TileMesh.cpp +++ b/src/naveditor/Editor_TileMesh.cpp @@ -33,7 +33,6 @@ #include "NavEditor/Include/Editor.h" #include "NavEditor/Include/Editor_TileMesh.h" -#include "tier0/commonmacros.h" #include "game/server/ai_navmesh.h" #include "game/server/ai_hull.h" @@ -185,6 +184,8 @@ Editor_TileMesh::Editor_TileMesh() : m_tileTriCount(0) { resetCommonSettings(); + selectNavMeshType(NAVMESH_SMALL); + memset(m_lastBuiltTileBmin, 0, sizeof(m_lastBuiltTileBmin)); memset(m_lastBuiltTileBmax, 0, sizeof(m_lastBuiltTileBmax)); @@ -220,19 +221,32 @@ const hulldef hulls[NAVMESH_COUNT] = { { g_navMeshNames[NAVMESH_LARGE] , NAI_Hull::Width(HULL_TITAN) , NAI_Hull::Height(HULL_TITAN) * NAI_Hull::Scale(HULL_TITAN) , 60, 64.0f }, { g_navMeshNames[NAVMESH_EXTRA_LARGE], NAI_Hull::Width(HULL_GOLIATH), NAI_Hull::Height(HULL_GOLIATH) * NAI_Hull::Scale(HULL_GOLIATH), 65, 64.0f }, }; + +void Editor_TileMesh::selectNavMeshType(const NavMeshType_e navMeshType) +{ + const hulldef& h = hulls[navMeshType]; + + m_agentRadius = h.radius; + m_agentMaxClimb = h.climbHeight; + m_agentHeight = h.height; + m_navmeshName = h.name; + m_tileSize = h.tileSize; + + m_navMeshType = navMeshType; +} + void Editor_TileMesh::handleSettings() { - for (const hulldef& h : hulls) + for (int i = 0; i < NAVMESH_COUNT; i++) { - if (imguiButton(h.name)) + const NavMeshType_e navMeshType = NavMeshType_e(i); + + if (imguiButton(NavMesh_GetNameForType(navMeshType))) { - m_agentRadius = h.radius; - m_agentMaxClimb = h.climbHeight; - m_agentHeight = h.height; - m_navmeshName = h.name; - m_tileSize = h.tileSize; + selectNavMeshType(navMeshType); } } + Editor::handleCommonSettings(); if (imguiCheck("Keep Intermediate Results", m_keepInterResults)) @@ -654,7 +668,7 @@ bool Editor_TileMesh::handleBuild() params.maxPolys = m_maxPolysPerTile; params.polyGroupCount = 0; params.traversalTableSize = 0; - params.traversalTableCount = DT_NUM_TRAVERSAL_TABLES; + params.traversalTableCount = NavMesh_GetTraversalTableCountForNavMeshType(m_navMeshType); params.magicDataCount = 0; dtStatus status; @@ -662,7 +676,7 @@ bool Editor_TileMesh::handleBuild() status = m_navMesh->init(¶ms); if (dtStatusFailed(status)) { - m_ctx->log(RC_LOG_ERROR, "buildTiledNavigation: Could not init navmesh."); + m_ctx->log(RC_LOG_ERROR, "buildTiledNavigation: Could not init Detour navmesh."); return false; } @@ -796,9 +810,19 @@ void Editor_TileMesh::buildAllTiles() } } - if (!dtCreateStaticPathingData(m_navMesh)) + // Reserve the first poly groups + // 0 = technically usable for normal poly groups, but for possible internal usage we reserve it for now. + // 1 = DT_STRAY_POLY_GROUP. + dtDisjointSet data(DT_FIRST_USABLE_POLY_GROUP); + + if (!dtCreateDisjointPolyGroups(m_navMesh, data)) { - m_ctx->log(RC_LOG_ERROR, "buildNavigation: Failed to build static pathing data."); + m_ctx->log(RC_LOG_ERROR, "buildNavigation: Failed to build disjoint poly groups."); + } + + if (!dtCreateTraversalTableData(m_navMesh, data, NavMesh_GetTraversalTableCountForNavMeshType(m_navMeshType))) + { + m_ctx->log(RC_LOG_ERROR, "buildNavigation: Failed to build traversal table data."); } // Start the build process. diff --git a/src/naveditor/include/Editor.h b/src/naveditor/include/Editor.h index ae35adb1..8a379ea4 100644 --- a/src/naveditor/include/Editor.h +++ b/src/naveditor/include/Editor.h @@ -22,6 +22,8 @@ #include "Recast/Include/Recast.h" #include "NavEditor/Include/EditorInterfaces.h" +#include "game/server/ai_navmesh.h" + struct hulldef { const char* name; @@ -132,7 +134,9 @@ protected: float m_detailSampleDist; float m_detailSampleMaxError; int m_partitionType; - const char* m_navmeshName = "unnamed"; + + NavMeshType_e m_navMeshType; + const char* m_navmeshName; EditorTool* m_tool; EditorToolState* m_toolStates[MAX_TOOLS]; diff --git a/src/naveditor/include/Editor_TileMesh.h b/src/naveditor/include/Editor_TileMesh.h index 8e2e14cd..1cb1e611 100644 --- a/src/naveditor/include/Editor_TileMesh.h +++ b/src/naveditor/include/Editor_TileMesh.h @@ -94,6 +94,8 @@ public: virtual void handleMeshChanged(class InputGeom* geom); virtual bool handleBuild(); virtual void collectSettings(struct BuildSettings& settings); + + void selectNavMeshType(const NavMeshType_e navMeshType); void getTilePos(const float* pos, int& tx, int& ty); void getTileExtents(int tx, int ty, float* bmin, float* bmax); diff --git a/src/thirdparty/recast/Detour/Include/DetourNavMesh.h b/src/thirdparty/recast/Detour/Include/DetourNavMesh.h index 7be13253..0eaeeb8b 100644 --- a/src/thirdparty/recast/Detour/Include/DetourNavMesh.h +++ b/src/thirdparty/recast/Detour/Include/DetourNavMesh.h @@ -68,6 +68,9 @@ static const int DT_VERTS_PER_POLYGON = 6; /// For reference, Titanfall 2 single player NavMeshes also marked everything unconnected as '1'. static const unsigned short DT_STRAY_POLY_GROUP = 1; +/// The first non-reserved poly group; DT_STRAY_POLY_GROUP and below are reserved. +static const unsigned short DT_FIRST_USABLE_POLY_GROUP = 2; + /// The minimum required number of poly groups for static pathing logic to work. /// (E.g. if we have 2 poly groups, group id 1 (DT_STRAY_POLY_GROUP), and group /// id 2, then 1 is never reachable as its considered 'trash' by design, and 2 @@ -76,8 +79,8 @@ static const unsigned short DT_STRAY_POLY_GROUP = 1; /// on the same (or connected) poly island before trying to compute a path). static const int DT_MIN_POLY_GROUP_COUNT = 3; -/// The number of traversal tables that will be used for static pathing. -static const int DT_NUM_TRAVERSAL_TABLES = 5; +/// The maximum number of traversal tables per navmesh that will be used for static pathing. +static const int DT_MAX_TRAVERSAL_TABLES = 5; /// @{ /// @name Tile Serialization Constants diff --git a/src/thirdparty/recast/Detour/Include/DetourNavMeshBuilder.h b/src/thirdparty/recast/Detour/Include/DetourNavMeshBuilder.h index c85b77d6..46fbd76a 100644 --- a/src/thirdparty/recast/Detour/Include/DetourNavMeshBuilder.h +++ b/src/thirdparty/recast/Detour/Include/DetourNavMeshBuilder.h @@ -168,11 +168,19 @@ private: rdIntArray parent; }; -/// Builds navigation mesh static pathing data from the provided navmesh. +/// Builds navigation mesh disjoint poly groups from the provided navmesh. /// @ingroup detour -/// @param[in] nav The navigation mesh to use. -/// @return True if the static pathing data was successfully created. -bool dtCreateStaticPathingData(dtNavMesh* nav); +/// @param[in] nav The navigation mesh to use. +/// @param[Out] disjoint The disjoint set data. +/// @return True if the disjoint set data was successfully created. +bool dtCreateDisjointPolyGroups(dtNavMesh* nav, dtDisjointSet& disjoint); + +/// Builds navigation mesh static traversal table from the provided navmesh. +/// @ingroup detour +/// @param[in] nav The navigation mesh to use. +/// @param[in] disjoint The disjoint set data. +/// @return True if the static traversal table was successfully created. +bool dtCreateTraversalTableData(dtNavMesh* nav, const dtDisjointSet& disjoint, const int tableCount); /// Builds navigation mesh tile data from the provided tile creation data. /// @ingroup detour diff --git a/src/thirdparty/recast/Detour/Source/DetourNavMesh.cpp b/src/thirdparty/recast/Detour/Source/DetourNavMesh.cpp index 814ef6bd..8c32cf60 100644 --- a/src/thirdparty/recast/Detour/Source/DetourNavMesh.cpp +++ b/src/thirdparty/recast/Detour/Source/DetourNavMesh.cpp @@ -263,6 +263,7 @@ dtStatus dtNavMesh::init(const dtNavMeshParams* params) const int traversalTableCount = params->traversalTableCount; if (traversalTableCount) { + rdAssert(traversalTableCount > 0 && traversalTableCount <= DT_MAX_TRAVERSAL_TABLES); const int setTableBufSize = sizeof(int**)*traversalTableCount; m_traversalTables = (int**)rdAlloc(setTableBufSize, RD_ALLOC_PERM); @@ -308,7 +309,7 @@ dtStatus dtNavMesh::init(unsigned char* data, const int dataSize, const int flag params.maxPolys = header->polyCount; params.polyGroupCount = 0; params.traversalTableSize = 0; - params.traversalTableCount = DT_NUM_TRAVERSAL_TABLES; + params.traversalTableCount = 0; params.magicDataCount = 0; dtStatus status = init(¶ms); diff --git a/src/thirdparty/recast/Detour/Source/DetourNavMeshBuilder.cpp b/src/thirdparty/recast/Detour/Source/DetourNavMeshBuilder.cpp index 5ee8ea6d..5e0833e2 100644 --- a/src/thirdparty/recast/Detour/Source/DetourNavMeshBuilder.cpp +++ b/src/thirdparty/recast/Detour/Source/DetourNavMeshBuilder.cpp @@ -281,15 +281,10 @@ static void setPolyGroupsTraversalReachability(int* const tableData, const int n tableData[index] &= ~value; } -bool dtCreateStaticPathingData(dtNavMesh* nav) +bool dtCreateDisjointPolyGroups(dtNavMesh* nav, dtDisjointSet& disjoint) { rdAssert(nav); - // Reserve the first 2 poly groups - // 0 = technically usable for normal poly groups, but for simplicity we reserve it for now. - // 1 = DT_STRAY_POLY_GROUP. - dtDisjointSet data(2); - // Clear all labels. for (int i = 0; i < nav->getMaxTiles(); ++i) { @@ -353,7 +348,7 @@ bool dtCreateStaticPathingData(dtNavMesh* nav) if (poly.firstLink == DT_NULL_LINK) poly.groupId = DT_STRAY_POLY_GROUP; else - poly.groupId = (unsigned short)data.insertNew(); + poly.groupId = (unsigned short)disjoint.insertNew(); } else { @@ -361,7 +356,7 @@ bool dtCreateStaticPathingData(dtNavMesh* nav) poly.groupId = (unsigned short)l; for (const int nl : nlabels) - data.setUnion(l, nl); + disjoint.setUnion(l, nl); } if (!noLabels) @@ -380,7 +375,7 @@ bool dtCreateStaticPathingData(dtNavMesh* nav) dtPoly& poly = tile->polys[j]; if (poly.groupId != DT_STRAY_POLY_GROUP) { - int id = data.find(poly.groupId); + int id = disjoint.find(poly.groupId); poly.groupId = (unsigned short)id; } } @@ -388,7 +383,7 @@ bool dtCreateStaticPathingData(dtNavMesh* nav) // Gather all unique polygroups and map them to a contiguous range. std::map groupMap; - unsigned short numPolyGroups = DT_STRAY_POLY_GROUP+1; // Anything <= DT_STRAY_POLY_GROUP is reserved! + unsigned short numPolyGroups = DT_FIRST_USABLE_POLY_GROUP; // Anything <= DT_STRAY_POLY_GROUP is reserved! for (int i = 0; i < nav->getMaxTiles(); ++i) { dtMeshTile* tile = nav->getTile(i); @@ -417,8 +412,15 @@ bool dtCreateStaticPathingData(dtNavMesh* nav) } } - const int tableSize = calcTraversalTableSize(numPolyGroups); - const int tableCount = DT_NUM_TRAVERSAL_TABLES; + nav->m_params.polyGroupCount = numPolyGroups; + return true; +} + +// todo(amos): remove param 'tableCount' and make struct 'dtTraversalTableCreateParams' +bool dtCreateTraversalTableData(dtNavMesh* nav, const dtDisjointSet& disjoint, const int tableCount) +{ + const int polyGroupCount = nav->m_params.polyGroupCount; + const int tableSize = calcTraversalTableSize(polyGroupCount); rdAssert(nav->m_traversalTables); @@ -449,18 +451,17 @@ bool dtCreateStaticPathingData(dtNavMesh* nav) nav->m_traversalTables[i] = traversalTable; memset(traversalTable, 0, sizeof(int)*tableSize); - for (unsigned short j = 0; j < numPolyGroups; j++) + for (unsigned short j = 0; j < polyGroupCount; j++) { - for (unsigned short k = 0; k < numPolyGroups; k++) + for (unsigned short k = 0; k < polyGroupCount; k++) { // Only reachable if its the same polygroup or if they are linked! - const bool isReachable = j == k || data.find(j) == data.find(k); - setPolyGroupsTraversalReachability(traversalTable, numPolyGroups, j, k, isReachable); + const bool isReachable = j == k || disjoint.find(j) == disjoint.find(k); + setPolyGroupsTraversalReachability(traversalTable, polyGroupCount, j, k, isReachable); } } } - nav->m_params.polyGroupCount = numPolyGroups; nav->m_params.traversalTableSize = tableSize; nav->m_params.traversalTableCount = tableCount;