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

View File

@ -243,8 +243,6 @@ public:
m_editor->removeTile(m_hitPos);
else
m_editor->buildTile(m_hitPos);
m_editor->buildStaticPathingData();
}
else if (m_cursorMode == TT_CURSOR_MODE_DEBUG)
{
@ -727,6 +725,18 @@ void Editor_TileMesh::buildTile(const float* pos)
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);
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()
@ -837,6 +874,8 @@ void Editor_TileMesh::removeAllTiles()
for (int y = 0; y < th; ++y)
for (int x = 0; x < tw; ++x)
m_navMesh->removeTile(m_navMesh->getTileRefAt(x,y,0),0,0);
m_traverseLinkPolyMap.clear();
}
void Editor_TileMesh::buildAllHulls()

View File

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

View File

@ -316,6 +316,15 @@ bool dtCreateDisjointPolyGroups(const dtTraverseTableCreateParams* params)
while (plink != DT_NULL_LINK)
{
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 dtPoly* p;
nav->getTileAndPolyByRefUnsafe(l.ref, &t, &p);