Recast: fix bugs causing mesh links to leak when removing and readding tiles

Rework the way traverse links are tracked and how the traverse table gets recreated when a tile gets removed/added. We would rebuild the entire traverse network and thus leak links since we also cleared the map that tracks the connections.
This commit is contained in:
Kawe Mazidjatari 2024-08-25 23:58:04 +02:00
parent 794f619d84
commit d81f6a24aa
4 changed files with 66 additions and 10 deletions

View File

@ -783,6 +783,8 @@ void Editor::connectTileTraverseLinks(dtMeshTile* const baseTile, const bool lin
return; return;
const dtMeshHeader* baseHeader = baseTile->header; const dtMeshHeader* baseHeader = baseTile->header;
const dtPolyRef basePolyRefBase = m_navMesh->getPolyRefBase(baseTile);
bool firstBaseTileLinkUsed = false; bool firstBaseTileLinkUsed = false;
for (int i = 0; i < baseHeader->polyCount; ++i) for (int i = 0; i < baseHeader->polyCount; ++i)
@ -847,6 +849,8 @@ void Editor::connectTileTraverseLinks(dtMeshTile* const baseTile, const bool lin
continue; continue;
const dtMeshHeader* landHeader = landTile->header; const dtMeshHeader* landHeader = landTile->header;
const dtPolyRef landPolyRefBase = m_navMesh->getPolyRefBase(landTile);
bool firstLandTileLinkUsed = false; bool firstLandTileLinkUsed = false;
for (int l = 0; l < landHeader->polyCount; ++l) for (int l = 0; l < landHeader->polyCount; ++l)
@ -942,7 +946,10 @@ void Editor::connectTileTraverseLinks(dtMeshTile* const baseTile, const bool lin
continue; continue;
} }
const TraverseLinkPolyPair linkedPolyPair(basePoly, landPoly); const dtPolyRef basePolyRef = basePolyRefBase | i;
const dtPolyRef landPolyRef = landPolyRefBase | l;
const TraverseLinkPolyPair linkedPolyPair(basePolyRef, landPolyRef);
auto linkedIt = m_traverseLinkPolyMap.find(linkedPolyPair); auto linkedIt = m_traverseLinkPolyMap.find(linkedPolyPair);
bool traverseLinkFound = false; bool traverseLinkFound = false;
@ -992,7 +999,7 @@ void Editor::connectTileTraverseLinks(dtMeshTile* const baseTile, const bool lin
dtLink* const forwardLink = &baseTile->links[forwardIdx]; dtLink* const forwardLink = &baseTile->links[forwardIdx];
forwardLink->ref = m_navMesh->getPolyRefBase(landTile) | (dtPolyRef)l; forwardLink->ref = landPolyRef;
forwardLink->edge = (unsigned char)j; forwardLink->edge = (unsigned char)j;
forwardLink->side = landSide; forwardLink->side = landSide;
forwardLink->bmin = 0; forwardLink->bmin = 0;
@ -1005,7 +1012,7 @@ void Editor::connectTileTraverseLinks(dtMeshTile* const baseTile, const bool lin
dtLink* const reverseLink = &landTile->links[reverseIdx]; dtLink* const reverseLink = &landTile->links[reverseIdx];
reverseLink->ref = m_navMesh->getPolyRefBase(baseTile) | (dtPolyRef)i; reverseLink->ref = basePolyRef;
reverseLink->edge = (unsigned char)m; reverseLink->edge = (unsigned char)m;
reverseLink->side = baseSide; reverseLink->side = baseSide;
reverseLink->bmin = 0; reverseLink->bmin = 0;
@ -1030,6 +1037,8 @@ void Editor::connectTileTraverseLinks(dtMeshTile* const baseTile, const bool lin
bool Editor::createTraverseLinks() bool Editor::createTraverseLinks()
{ {
rdAssert(m_navMesh); rdAssert(m_navMesh);
m_traverseLinkPolyMap.clear();
const int maxTiles = m_navMesh->getMaxTiles(); const int maxTiles = m_navMesh->getMaxTiles();
// First pass to connect edges between external tiles together. // First pass to connect edges between external tiles together.
@ -1052,7 +1061,6 @@ bool Editor::createTraverseLinks()
connectTileTraverseLinks(baseTile, false); connectTileTraverseLinks(baseTile, false);
} }
m_traverseLinkPolyMap.clear();
return true; return true;
} }

View File

@ -243,8 +243,6 @@ public:
m_editor->removeTile(m_hitPos); m_editor->removeTile(m_hitPos);
else else
m_editor->buildTile(m_hitPos); m_editor->buildTile(m_hitPos);
m_editor->buildStaticPathingData();
} }
else if (m_cursorMode == TT_CURSOR_MODE_DEBUG) else if (m_cursorMode == TT_CURSOR_MODE_DEBUG)
{ {
@ -727,6 +725,18 @@ void Editor_TileMesh::buildTile(const float* pos)
m_navMesh->connectExtOffMeshLinks(targetRef); m_navMesh->connectExtOffMeshLinks(targetRef);
} }
} }
dtMeshTile* tile = (dtMeshTile*)m_navMesh->getTileByRef(tileRef);
// Reconnect the traverse links.
connectTileTraverseLinks(tile, true);
connectTileTraverseLinks(tile, false);
dtTraverseTableCreateParams params;
createTraverseTableParams(&params);
dtCreateDisjointPolyGroups(&params);
updateStaticPathingData(&params);
} }
} }
@ -768,7 +778,34 @@ void Editor_TileMesh::removeTile(const float* pos)
getTileExtents(tx, ty, m_lastBuiltTileBmin, m_lastBuiltTileBmax); getTileExtents(tx, ty, m_lastBuiltTileBmin, m_lastBuiltTileBmax);
m_tileCol = duRGBA(255,0,0,180); m_tileCol = duRGBA(255,0,0,180);
m_navMesh->removeTile(m_navMesh->getTileRefAt(tx,ty,0),0,0); const dtTileRef tileRef = m_navMesh->getTileRefAt(tx,ty, 0);
if (dtStatusSucceed(m_navMesh->removeTile(tileRef, 0, 0)))
{
// Update traverse link map so the next time we rebuild this
// tile, the polygon pairs will be marked as available.
const unsigned int tileId = m_navMesh->decodePolyIdTile(tileRef);
for (auto it = m_traverseLinkPolyMap.cbegin(); it != m_traverseLinkPolyMap.cend();)
{
const TraverseLinkPolyPair& pair = it->first;
if (m_navMesh->decodePolyIdTile(pair.poly1) == tileId ||
m_navMesh->decodePolyIdTile(pair.poly2) == tileId)
{
it = m_traverseLinkPolyMap.erase(it);
continue;
}
++it;
}
dtTraverseTableCreateParams params;
createTraverseTableParams(&params);
dtCreateDisjointPolyGroups(&params);
updateStaticPathingData(&params);
}
} }
void Editor_TileMesh::buildAllTiles() void Editor_TileMesh::buildAllTiles()
@ -837,6 +874,8 @@ void Editor_TileMesh::removeAllTiles()
for (int y = 0; y < th; ++y) for (int y = 0; y < th; ++y)
for (int x = 0; x < tw; ++x) for (int x = 0; x < tw; ++x)
m_navMesh->removeTile(m_navMesh->getTileRefAt(x,y,0),0,0); m_navMesh->removeTile(m_navMesh->getTileRefAt(x,y,0),0,0);
m_traverseLinkPolyMap.clear();
} }
void Editor_TileMesh::buildAllHulls() void Editor_TileMesh::buildAllHulls()

View File

@ -194,7 +194,7 @@ enum EditorPolyFlags
struct TraverseLinkPolyPair struct TraverseLinkPolyPair
{ {
TraverseLinkPolyPair(const dtPoly* p1, const dtPoly* p2) TraverseLinkPolyPair(dtPolyRef p1, dtPolyRef p2)
{ {
if (p1 > p2) if (p1 > p2)
rdSwap(p1, p2); rdSwap(p1, p2);
@ -213,8 +213,8 @@ struct TraverseLinkPolyPair
return poly2 < other.poly2; return poly2 < other.poly2;
} }
const dtPoly* poly1; dtPolyRef poly1;
const dtPoly* poly2; dtPolyRef poly2;
}; };
class EditorDebugDraw : public DebugDrawGL class EditorDebugDraw : public DebugDrawGL

View File

@ -316,6 +316,15 @@ bool dtCreateDisjointPolyGroups(const dtTraverseTableCreateParams* params)
while (plink != DT_NULL_LINK) while (plink != DT_NULL_LINK)
{ {
const dtLink l = tile->links[plink]; const dtLink l = tile->links[plink];
// Polygons linked with traverse links are not necessarily on
// the same group, these should be skipped.
if (l.traverseType != DT_NULL_TRAVERSE_TYPE)
{
plink = l.next;
continue;
}
const dtMeshTile* t; const dtMeshTile* t;
const dtPoly* p; const dtPoly* p;
nav->getTileAndPolyByRefUnsafe(l.ref, &t, &p); nav->getTileAndPolyByRefUnsafe(l.ref, &t, &p);