Recast: do not connect adjacent polygons with traverse links

Connecting adjacent polygons with traverse links will cause pathfinding to run into itself infinitely if either of the 2 poly's first link references the polygon from which the traverse link originates from. The behavior in-game is that the AI become stuck in the area with error code FAIL_NO_ROUTE_BLOCKED. Implemented the method 'dtNavMesh::arePolysAdjacent' to make sure we never connect adjacent polygons on our own or neighboring tile with a traverse link.
This commit is contained in:
Kawe Mazidjatari 2024-10-31 19:09:59 +01:00
parent e7240e5551
commit 582ecec038
2 changed files with 81 additions and 2 deletions

View File

@ -820,7 +820,22 @@ public:
/// @param[out] poly The polygon.
void getTileAndPolyByRefUnsafe(const dtPolyRef ref, const dtMeshTile** tile, const dtPoly** poly) const;
/// Returns whether goal poly is reachable from start poly
/// Returns whether both polygons are adjacent by a shared edge.
/// @param[in] baseRef The reference to the first poly.
/// @param[in] landRef The reference to the second poly.
/// @return True if both polygons are adjacent by a shared edge.
bool arePolysAdjacent(const dtPolyRef baseRef, const dtPolyRef landRef) const;
/// Returns whether both polygons are adjacent by a shared edge.
/// @param[in] basePoly The first poly.
/// @param[in] baseTile The first tile.
/// @param[in] landPoly The second poly.
/// @param[in] landTile The second tile.
/// @return True if both polygons are adjacent by a shared edge.
bool arePolysAdjacent(const dtPoly* const basePoly, const dtMeshTile* baseTile,
const dtPoly* const landPoly, const dtMeshTile* landTile) const;
/// Returns whether goal poly is reachable from start poly.
/// @param[in] fromRef The reference to the start poly.
/// @param[in] goalRef The reference to the goal poly.
/// @param[in] checkDisjointGroupsOnly Whether to only check disjoint poly groups.

View File

@ -889,6 +889,12 @@ dtStatus dtNavMesh::connectTraverseLinks(const dtTileRef tileRef, const dtTraver
if (landPoly->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
continue;
// If both polygons are sharing an edge, we should not establish the link as
// it will cause pathfinding to fail in this area when both polygons have
// their first link set to another; the path will never exit these polygons.
if (arePolysAdjacent(basePoly, baseTile, landPoly, landTile))
continue;
dtPolyDetail* const landDetail = &landTile->detailMeshes[o];
for (int p = 0; (p < landPoly->vertCount) && !moveToNextTile; ++p)
@ -1673,8 +1679,66 @@ void dtNavMesh::getTileAndPolyByRefUnsafe(const dtPolyRef ref, const dtMeshTile*
*poly = &m_tiles[it].polys[ip];
}
bool dtNavMesh::arePolysAdjacent(const dtPolyRef fromRef, const dtPolyRef goalRef) const
{
const dtMeshTile* fromTile = nullptr;
const dtMeshTile* goalTile = nullptr;
const dtPoly* fromPoly = nullptr;
const dtPoly* goalPoly = nullptr;
getTileAndPolyByRefUnsafe(fromRef, &fromTile, &fromPoly);
getTileAndPolyByRefUnsafe(goalRef, &goalTile, &goalPoly);
return arePolysAdjacent(fromPoly, fromTile, goalPoly, goalTile);
}
bool dtNavMesh::arePolysAdjacent(const dtPoly* const basePoly, const dtMeshTile* baseTile,
const dtPoly* const landPoly, const dtMeshTile* landTile) const
{
if (baseTile == landTile)
{
for (int i = 0; i < landPoly->vertCount; ++i)
{
const unsigned short nei = landPoly->neis[i];
if (!nei)
continue;
if (nei & DT_EXT_LINK)
continue;
const int idx = (nei-1);
if (&baseTile->polys[idx] == basePoly)
return true;
}
}
else // Check external linkage.
{
unsigned int linkIndex = landPoly->firstLink;
while (linkIndex != DT_NULL_LINK)
{
const dtLink* const link = &landTile->links[linkIndex];
if (link->side != 0xff && link->traverseType == DT_NULL_TRAVERSE_TYPE)
{
const dtMeshTile* neiTile;
const dtPoly* neiPoly;
getTileAndPolyByRefUnsafe(link->ref, &neiTile, &neiPoly);
if (neiPoly == basePoly)
return true;
}
linkIndex = link->next;
}
}
return false;
}
bool dtNavMesh::isGoalPolyReachable(const dtPolyRef fromRef, const dtPolyRef goalRef,
const bool checkDisjointGroupsOnly, const int traverseTableIndex) const
const bool checkDisjointGroupsOnly, const int traverseTableIndex) const
{
// Same poly is always reachable.
if (fromRef == goalRef)