From 0ec03a62b11f4b0e047f9724171b0425e96cd145 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Tue, 19 Jul 2022 02:38:01 +0200 Subject: [PATCH] Working AI reachability tables * Set reachability table count to 1. * Don't divide reachability write size by 4. * Trim off all unused bytes after building reachability table. * Default cvar 'navmesh_always_reachable' to 0. AI behavior seems to have improved a bit, they attempt to pathfind better, and if they find a certain route they attempt another route where originally they would become stuck in such situation. If you build the SDK after this commit, you need to rebuild all navmeshes, else the game would segfault in dtNavMesh::isPolyReachable(). --- r5dev/naveditor/Sample.cpp | 115 ++++++++-------------------- r5dev/naveditor/Sample_TileMesh.cpp | 4 - r5dev/tier1/IConVar.cpp | 2 +- 3 files changed, 32 insertions(+), 89 deletions(-) diff --git a/r5dev/naveditor/Sample.cpp b/r5dev/naveditor/Sample.cpp index ac6e0844..2199569c 100644 --- a/r5dev/naveditor/Sample.cpp +++ b/r5dev/naveditor/Sample.cpp @@ -185,7 +185,6 @@ hulldef hulls[5] = { }; void Sample::handleCommonSettings() { - bool is_human = true; for (auto& h : hulls) { if (imguiButton(h.name)) @@ -193,11 +192,8 @@ void Sample::handleCommonSettings() m_agentRadius = h.radius; m_agentMaxClimb = h.climb_height; m_agentHeight = h.height; - if (is_human) - m_reachabilityTableCount = 4; m_navmeshName = h.name; } - is_human = false; } imguiLabel("Rasterization"); imguiSlider("Cell Size", &m_cellSize, 0.1f, 100.0f, 0.01f); @@ -656,7 +652,30 @@ void Sample::saveAll(std::string path, dtNavMesh* mesh) } memcpy(&header.params, mesh->getParams(), sizeof(dtNavMeshParams)); header.params.disjointPolyGroupCount = 3; - header.params.reachabilityTableSize = ((header.params.disjointPolyGroupCount + 31) / 32) * header.params.disjointPolyGroupCount * 32; + + LinkTableData link_data; + build_link_table(mesh, link_data); + int table_size = ((link_data.setCount + 31) / 32) * link_data.setCount * 32; + + std::vector reachability(table_size, 0); + for (int i = 0; i < link_data.setCount; i++) + set_reachable(reachability, link_data.setCount, i, i, true); + + size_t del = 0; + for (size_t i = reachability.size() - 1; i >= 0; i--) + { + if (reachability[i] == 0) + { + reachability.erase(reachability.begin() + i); + table_size--; + } + else + break; + } + + header.params.disjointPolyGroupCount = link_data.setCount; + header.params.reachabilityTableCount = m_reachabilityTableCount; + header.params.reachabilityTableSize = table_size; if (*is_tf2)unpatch_headertf2(header); fwrite(&header, sizeof(NavMeshSetHeader), 1, fp); @@ -677,85 +696,13 @@ void Sample::saveAll(std::string path, dtNavMesh* mesh) if (*is_tf2)patch_tiletf2(const_cast(tile)); } - int header_sth[3] = { 0,0,0 }; - fwrite(header_sth, sizeof(int), 3, fp); - unsigned int reachability[32 * 3]; - for (int i = 0; i < 32 * 3; i++) - reachability[i] = 0xffffffff; + //still dont know what this thing is... + int header_sth = 0; + for (int i = 0; i < link_data.setCount; i++) + fwrite(&header_sth, sizeof(int), 1, fp); for (int i = 0; i < header.params.reachabilityTableCount; i++) - fwrite(reachability, sizeof(int), (header.params.reachabilityTableSize / 4), fp); - fclose(fp); -} + fwrite(reachability.data(), sizeof(int), table_size, fp); -//void Sample::saveAll(std::string path, dtNavMesh* mesh) -//{ -// if (!mesh) return; -// -// std::filesystem::path p = "..\\maps\\navmesh\\"; -// -// if (std::filesystem::is_directory(p)) -// { -// path.insert(0, p.string()); -// } -// -// char buffer[256]; -// sprintf(buffer, "%s_%s.nm", path.c_str(), m_navmeshName); -// -// FILE* fp = fopen(buffer, "wb"); -// if (!fp) -// return; -// -// // Store header. -// NavMeshSetHeader header; -// header.magic = NAVMESHSET_MAGIC; -// header.version = NAVMESHSET_VERSION; -// header.numTiles = 0; -// -// for (int i = 0; i < mesh->getMaxTiles(); ++i) -// { -// dtMeshTile* tile = mesh->getTile(i); -// if (!tile || !tile->header || !tile->dataSize) continue; -// header.numTiles++; -// } -// memcpy(&header.params, mesh->getParams(), sizeof(dtNavMeshParams)); -// header.params.disjointPolyGroupCount = 3; -// -// LinkTableData link_data; -// build_link_table(mesh, link_data); -// int table_size = ((link_data.setCount + 31) / 32) * link_data.setCount * 32; -// header.params.disjointPolyGroupCount = link_data.setCount; -// header.params.reachabilityTableCount = m_reachabilityTableCount; -// header.params.reachabilityTableSize = table_size; -// -// if (*is_tf2)unpatch_headertf2(header); -// fwrite(&header, sizeof(NavMeshSetHeader), 1, fp); -// -// // Store tiles. -// for (int i = 0; i < mesh->getMaxTiles(); ++i) -// { -// dtMeshTile* tile = mesh->getTile(i); -// if (!tile || !tile->header || !tile->dataSize) continue; -// -// NavMeshTileHeader tileHeader; -// tileHeader.tileRef = mesh->getTileRef(tile); -// tileHeader.dataSize = tile->dataSize; -// fwrite(&tileHeader, sizeof(tileHeader), 1, fp); -// -// if (*is_tf2)unpatch_tiletf2(const_cast(tile)); -// fwrite(tile->data, tile->dataSize, 1, fp); -// if (*is_tf2)patch_tiletf2(const_cast(tile)); -// } -// -// //still dont know what this thing is... -// int header_sth = 0; -// for (int i = 0; i < link_data.setCount; i++) -// fwrite(&header_sth, sizeof(int), 1, fp); -// -// std::vector reachability(table_size, 0); -// for (int i = 0; i < link_data.setCount; i++) -// set_reachable(reachability, link_data.setCount, i, i, true); -// for (int i = 0; i < header.params.reachabilityTableCount; i++) -// fwrite(reachability.data(), sizeof(int), (table_size / 4), fp); -// fclose(fp); -//} \ No newline at end of file + fclose(fp); +} \ No newline at end of file diff --git a/r5dev/naveditor/Sample_TileMesh.cpp b/r5dev/naveditor/Sample_TileMesh.cpp index c6e4f6d5..afd49f34 100644 --- a/r5dev/naveditor/Sample_TileMesh.cpp +++ b/r5dev/naveditor/Sample_TileMesh.cpp @@ -770,16 +770,12 @@ void Sample_TileMesh::removeAllTiles() void Sample_TileMesh::buildAllHulls() { - bool is_human = true; for (auto& h : hulls) { m_agentRadius = h.radius; m_agentMaxClimb = h.climb_height; m_agentHeight = h.height; - if (is_human) - m_reachabilityTableCount = 4; m_navmeshName = h.name; - is_human = false; handleSettings(); handleBuild(); diff --git a/r5dev/tier1/IConVar.cpp b/r5dev/tier1/IConVar.cpp index 988d0e9f..630e11f5 100644 --- a/r5dev/tier1/IConVar.cpp +++ b/r5dev/tier1/IConVar.cpp @@ -64,7 +64,7 @@ void ConVar::Init(void) const ai_ainDumpOnLoad = new ConVar("ai_ainDumpOnLoad" , "0", FCVAR_DEVELOPMENTONLY, "Dumps AIN data from node graphs loaded from the disk on load.", false, 0.f, false, 0.f, nullptr, nullptr); ai_ainDebugConnect = new ConVar("ai_ainDebugConnect" , "0", FCVAR_DEVELOPMENTONLY, "Debug AIN node connections.", false, 0.f, false, 0.f, nullptr, nullptr); ai_script_nodes_draw_index = new ConVar("ai_script_nodes_draw_index", "0", FCVAR_DEVELOPMENTONLY, "Start index for drawing script nodes.", false, 0.f, false, 0.f, nullptr, nullptr); - navmesh_always_reachable = new ConVar("navmesh_always_reachable" , "1", FCVAR_DEVELOPMENTONLY, "Marks poly from agent to target on navmesh as reachable regardless of table data ( !slower! ).", false, 0.f, false, 0.f, nullptr, nullptr); // !TODO: Default to '0' once the reachability table gets properly parsed. + navmesh_always_reachable = new ConVar("navmesh_always_reachable" , "0", FCVAR_DEVELOPMENTONLY, "Marks poly from agent to target on navmesh as reachable regardless of table data ( !slower! ).", false, 0.f, false, 0.f, nullptr, nullptr); sv_showconnecting = new ConVar("sv_showconnecting" , "1", FCVAR_RELEASE, "Logs information about the connecting client to the console.", false, 0.f, false, 0.f, nullptr, nullptr); sv_pylonVisibility = new ConVar("sv_pylonVisibility", "0", FCVAR_RELEASE, "Determines the visiblity to the Pylon master server, 0 = Offline, 1 = Hidden, 2 = Public.", false, 0.f, false, 0.f, nullptr, nullptr);