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.
This commit is contained in:
Kawe Mazidjatari 2024-07-03 11:55:24 +02:00
parent c215fcb220
commit d03dfe645f
5 changed files with 46 additions and 17 deletions

View File

@ -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<int> reachability(header.params.reachabilityTableSize, 0);
std::vector<int> reachability(tableSize, 0);
for (int i = 0; i < linkData.setCount; i++)
setReachable(reachability, linkData.setCount, i, i, true);

View File

@ -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<int>& data, int count, int id1, int id2, bool value)
{

View File

@ -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<int>& data, int count, int id1, int id2, bool value);
#endif // GAMEUTILS_H

View File

@ -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);

View File

@ -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