Recast: initial working implementation of traverse link generation algorithm

Traversing now works in-game, but there's still work needed to polish the algorithm and creating a proper lookup for jump types as currently only 1 jump type is supported.
This commit is contained in:
Kawe Mazidjatari 2024-08-08 17:44:27 +02:00
parent c12690b33c
commit acaf4cf32b
7 changed files with 144 additions and 28 deletions

View File

@ -353,6 +353,12 @@ void Editor::handleUpdate(const float dt)
updateToolStates(dt); updateToolStates(dt);
} }
void Editor::buildTraverseLinks()
{
if (!m_navMesh) return;
dtCreateTraverseLinks(m_navMesh);
}
void Editor::buildStaticPathingData() void Editor::buildStaticPathingData()
{ {
if (!m_navMesh) return; if (!m_navMesh) return;

View File

@ -145,6 +145,7 @@ public:
else else
m_editor->buildTile(m_hitPos); m_editor->buildTile(m_hitPos);
m_editor->buildTraverseLinks();
m_editor->buildStaticPathingData(); m_editor->buildStaticPathingData();
} }
} }
@ -597,6 +598,7 @@ void Editor_TileMesh::buildAllTiles()
} }
} }
buildTraverseLinks();
buildStaticPathingData(); buildStaticPathingData();
// Start the build process. // Start the build process.

View File

@ -239,6 +239,7 @@ public:
void resetCommonSettings(); void resetCommonSettings();
void handleCommonSettings(); void handleCommonSettings();
void buildTraverseLinks();
void buildStaticPathingData(); void buildStaticPathingData();
private: private:

View File

@ -415,6 +415,10 @@ struct dtMeshHeader
/// @ingroup detour /// @ingroup detour
struct dtMeshTile struct dtMeshTile
{ {
public:
unsigned int allocLink();
void freeLink(unsigned int link);
unsigned int salt; ///Counter describing modifications to the tile. unsigned int salt; ///Counter describing modifications to the tile.
unsigned int linksFreeList; ///Index to the next free link. unsigned int linksFreeList; ///Index to the next free link.

View File

@ -182,6 +182,12 @@ private:
/// @return True if the disjoint set data was successfully created. /// @return True if the disjoint set data was successfully created.
bool dtCreateDisjointPolyGroups(dtNavMesh* nav, dtDisjointSet& disjoint); bool dtCreateDisjointPolyGroups(dtNavMesh* nav, dtDisjointSet& disjoint);
/// Builds navigation mesh traverse links from the provided navmesh.
/// @ingroup detour
/// @param[in] nav The navigation mesh to use.
/// @return True if the traverse links were successfully created.
bool dtCreateTraverseLinks(dtNavMesh* nav);
/// Builds navigation mesh static traversal table from the provided navmesh. /// Builds navigation mesh static traversal table from the provided navmesh.
/// @ingroup detour /// @ingroup detour
/// @param[in] nav The navigation mesh to use. /// @param[in] nav The navigation mesh to use.

View File

@ -115,19 +115,19 @@ inline int computeTileHash(int x, int y, const int mask)
return (int)(n & mask); return (int)(n & mask);
} }
inline unsigned int allocLink(dtMeshTile* tile) unsigned int dtMeshTile::allocLink()
{ {
if (tile->linksFreeList == DT_NULL_LINK) if (linksFreeList == DT_NULL_LINK)
return DT_NULL_LINK; return DT_NULL_LINK;
unsigned int link = tile->linksFreeList; unsigned int link = linksFreeList;
tile->linksFreeList = tile->links[link].next; linksFreeList = links[link].next;
return link; return link;
} }
inline void freeLink(dtMeshTile* tile, unsigned int link) void dtMeshTile::freeLink(unsigned int link)
{ {
tile->links[link].next = tile->linksFreeList; links[link].next = linksFreeList;
tile->linksFreeList = link; linksFreeList = link;
} }
int dtCalcTraversalTableCellIndex(const int numPolyGroups, int dtCalcTraversalTableCellIndex(const int numPolyGroups,
@ -409,7 +409,7 @@ void dtNavMesh::unconnectLinks(dtMeshTile* tile, dtMeshTile* target)
poly->firstLink = nj; poly->firstLink = nj;
else else
tile->links[pj].next = nj; tile->links[pj].next = nj;
freeLink(tile, j); tile->freeLink(j);
j = nj; j = nj;
} }
else else
@ -453,7 +453,7 @@ void dtNavMesh::connectExtLinks(dtMeshTile* tile, dtMeshTile* target, int side)
int nnei = findConnectingPolys(va,vb, target, rdOppositeTile(dir), nei,neia,4); int nnei = findConnectingPolys(va,vb, target, rdOppositeTile(dir), nei,neia,4);
for (int k = 0; k < nnei; ++k) for (int k = 0; k < nnei; ++k)
{ {
unsigned int idx = allocLink(tile); unsigned int idx = tile->allocLink();
if (idx != DT_NULL_LINK) if (idx != DT_NULL_LINK)
{ {
dtLink* link = &tile->links[idx]; dtLink* link = &tile->links[idx];
@ -528,7 +528,7 @@ void dtNavMesh::connectExtOffMeshLinks(dtMeshTile* tile, dtMeshTile* target, int
rdVcopy(v, nearestPt); rdVcopy(v, nearestPt);
// Link off-mesh connection to target poly. // Link off-mesh connection to target poly.
unsigned int idx = allocLink(target); unsigned int idx = target->allocLink();
dtLink* link = nullptr; dtLink* link = nullptr;
if (idx != DT_NULL_LINK) if (idx != DT_NULL_LINK)
{ {
@ -550,7 +550,7 @@ void dtNavMesh::connectExtOffMeshLinks(dtMeshTile* tile, dtMeshTile* target, int
dtLink* tlink = nullptr; dtLink* tlink = nullptr;
if (targetCon->flags & DT_OFFMESH_CON_BIDIR) if (targetCon->flags & DT_OFFMESH_CON_BIDIR)
{ {
tidx = allocLink(tile); tidx = tile->allocLink();
if (tidx != DT_NULL_LINK) if (tidx != DT_NULL_LINK)
{ {
const unsigned short landPolyIdx = (unsigned short)decodePolyIdPoly(ref); const unsigned short landPolyIdx = (unsigned short)decodePolyIdPoly(ref);
@ -611,7 +611,7 @@ void dtNavMesh::connectIntLinks(dtMeshTile* tile)
// Skip hard and non-internal edges. // Skip hard and non-internal edges.
if (poly->neis[j] == 0 || (poly->neis[j] & DT_EXT_LINK)) continue; if (poly->neis[j] == 0 || (poly->neis[j] & DT_EXT_LINK)) continue;
unsigned int idx = allocLink(tile); unsigned int idx = tile->allocLink();
if (idx != DT_NULL_LINK) if (idx != DT_NULL_LINK)
{ {
dtLink* link = &tile->links[idx]; dtLink* link = &tile->links[idx];
@ -657,7 +657,7 @@ void dtNavMesh::baseOffMeshLinks(dtMeshTile* tile)
rdVcopy(v, nearestPt); rdVcopy(v, nearestPt);
// Link off-mesh connection to target poly. // Link off-mesh connection to target poly.
unsigned int idx = allocLink(tile); unsigned int idx = tile->allocLink();
if (idx != DT_NULL_LINK) if (idx != DT_NULL_LINK)
{ {
dtLink* link = &tile->links[idx]; dtLink* link = &tile->links[idx];
@ -674,7 +674,7 @@ void dtNavMesh::baseOffMeshLinks(dtMeshTile* tile)
} }
// Start end-point is always connect back to off-mesh connection. // Start end-point is always connect back to off-mesh connection.
unsigned int tidx = allocLink(tile); unsigned int tidx = tile->allocLink();
if (tidx != DT_NULL_LINK) if (tidx != DT_NULL_LINK)
{ {
const unsigned short landPolyIdx = (unsigned short)decodePolyIdPoly(ref); const unsigned short landPolyIdx = (unsigned short)decodePolyIdPoly(ref);

View File

@ -461,6 +461,103 @@ bool dtCreateDisjointPolyGroups(dtNavMesh* nav, dtDisjointSet& disjoint)
return true; return true;
} }
// TODO: create lookup table and look for distance + slope to determine the
// correct jumpType.
// TODO: make sure we don't generate duplicate pairs of jump types between
// 2 polygons.
static void connectTileTraverseLinks(dtNavMesh* const nav, dtMeshTile* const tile)
{
for (int i = 0; i < tile->header->polyCount; ++i)
{
dtPoly* const startPoly = &tile->polys[i];
for (int j = 0; j < startPoly->vertCount; ++j)
{
// Hard edges only!
if (startPoly->neis[j] != 0)
continue;
// Polygon 1 edge
const float* const startPolySpos = &tile->verts[startPoly->verts[j] * 3];
const float* const startPolyEpos = &tile->verts[startPoly->verts[(j + 1) % startPoly->vertCount] * 3];
for (int k = 0; k < tile->header->polyCount; ++k)
{
if (i == k) continue; // Skip self
dtPoly* const endPoly = &tile->polys[k];
for (int m = 0; m < endPoly->vertCount; ++m)
{
// Hard edges only!
if (endPoly->neis[m] != 0)
continue;
// Polygon 2 edge
const float* const endPolySpos = &tile->verts[endPoly->verts[m] * 3];
const float* const endPolyEpos = &tile->verts[endPoly->verts[(m + 1) % endPoly->vertCount] * 3];
// TODO: calculate edge midpoint first !!!
const unsigned char dist1 = dtCalcLinkDistance(startPolySpos, endPolyEpos);
const unsigned char dist2 = dtCalcLinkDistance(startPolyEpos, endPolySpos);
// TODO: needs lookup table for distance !!!
if ((dist1 >= 10 && dist1 <= 30) && (dist2 >= 10 && dist2 <= 30))
{
const unsigned int idx = tile->allocLink();
if (idx == DT_NULL_LINK) // TODO: should move on to next tile.
continue;
dtLink* const forwardLink = &tile->links[idx];
forwardLink->ref = nav->getPolyRefBase(tile) | (unsigned int)k;
forwardLink->edge = (unsigned char)j;
forwardLink->side = 0xFF;
forwardLink->next = startPoly->firstLink;
startPoly->firstLink = idx;
forwardLink->traverseType = 1;
forwardLink->traverseDist = dist1;
const unsigned int tidx = tile->allocLink();
if (tidx == DT_NULL_LINK) // TODO: should move on to next tile.
continue;
dtLink* const reverseLink = &tile->links[tidx];
reverseLink->ref = nav->getPolyRefBase(tile) | (unsigned int)i;
reverseLink->edge = (unsigned char)m;
reverseLink->side = 0xFF;
reverseLink->next = endPoly->firstLink;
endPoly->firstLink = tidx;
reverseLink->traverseType = 1;
reverseLink->traverseDist = dist2;
forwardLink->reverseLink = (unsigned short)tidx;
reverseLink->reverseLink = (unsigned short)idx;
}
}
}
}
}
}
bool dtCreateTraverseLinks(dtNavMesh* nav)
{
rdAssert(nav);
for (int i = 0; i < nav->getMaxTiles(); i++)
{
dtMeshTile* tile = nav->getTile(i);
if (!tile->header) continue;
connectTileTraverseLinks(nav, tile);
}
return true;
}
// todo(amos): remove param 'tableCount' and make struct 'dtTraversalTableCreateParams' // todo(amos): remove param 'tableCount' and make struct 'dtTraversalTableCreateParams'
bool dtCreateTraversalTableData(dtNavMesh* nav, const dtDisjointSet& disjoint, const int tableCount) bool dtCreateTraversalTableData(dtNavMesh* nav, const dtDisjointSet& disjoint, const int tableCount)
{ {
@ -876,18 +973,6 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData,
n++; n++;
} }
} }
#if DT_NAVMESH_SET_VERSION >= 8
// Polygon cells.
for (int i = 0; i < (int)cellItems.size(); i++)
{
const CellItem& cellItem = cellItems[i];
dtCell& cell = navCells[i];
rdVcopy(cell.pos, cellItem.pos);
cell.polyIndex = cellItem.polyIndex;
}
#endif
// Store polygons // Store polygons
// Mesh polys // Mesh polys
@ -1040,9 +1125,21 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData,
n++; n++;
} }
} }
#if DT_NAVMESH_SET_VERSION >= 8
// Polygon cells.
for (int i = 0; i < (int)cellItems.size(); i++)
{
const CellItem& cellItem = cellItems[i];
dtCell& cell = navCells[i];
rdVcopy(cell.pos, cellItem.pos);
cell.polyIndex = cellItem.polyIndex;
}
#endif
rdFree(offMeshConClass); rdFree(offMeshConClass);
*outData = data; *outData = data;
*outDataSize = dataSize; *outDataSize = dataSize;