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);
}
void Editor::buildTraverseLinks()
{
if (!m_navMesh) return;
dtCreateTraverseLinks(m_navMesh);
}
void Editor::buildStaticPathingData()
{
if (!m_navMesh) return;

View File

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

View File

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

View File

@ -415,6 +415,10 @@ struct dtMeshHeader
/// @ingroup detour
struct dtMeshTile
{
public:
unsigned int allocLink();
void freeLink(unsigned int link);
unsigned int salt; ///Counter describing modifications to the tile.
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.
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.
/// @ingroup detour
/// @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);
}
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;
unsigned int link = tile->linksFreeList;
tile->linksFreeList = tile->links[link].next;
unsigned int link = linksFreeList;
linksFreeList = links[link].next;
return link;
}
inline void freeLink(dtMeshTile* tile, unsigned int link)
void dtMeshTile::freeLink(unsigned int link)
{
tile->links[link].next = tile->linksFreeList;
tile->linksFreeList = link;
links[link].next = linksFreeList;
linksFreeList = link;
}
int dtCalcTraversalTableCellIndex(const int numPolyGroups,
@ -409,7 +409,7 @@ void dtNavMesh::unconnectLinks(dtMeshTile* tile, dtMeshTile* target)
poly->firstLink = nj;
else
tile->links[pj].next = nj;
freeLink(tile, j);
tile->freeLink(j);
j = nj;
}
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);
for (int k = 0; k < nnei; ++k)
{
unsigned int idx = allocLink(tile);
unsigned int idx = tile->allocLink();
if (idx != DT_NULL_LINK)
{
dtLink* link = &tile->links[idx];
@ -528,7 +528,7 @@ void dtNavMesh::connectExtOffMeshLinks(dtMeshTile* tile, dtMeshTile* target, int
rdVcopy(v, nearestPt);
// Link off-mesh connection to target poly.
unsigned int idx = allocLink(target);
unsigned int idx = target->allocLink();
dtLink* link = nullptr;
if (idx != DT_NULL_LINK)
{
@ -550,7 +550,7 @@ void dtNavMesh::connectExtOffMeshLinks(dtMeshTile* tile, dtMeshTile* target, int
dtLink* tlink = nullptr;
if (targetCon->flags & DT_OFFMESH_CON_BIDIR)
{
tidx = allocLink(tile);
tidx = tile->allocLink();
if (tidx != DT_NULL_LINK)
{
const unsigned short landPolyIdx = (unsigned short)decodePolyIdPoly(ref);
@ -611,7 +611,7 @@ void dtNavMesh::connectIntLinks(dtMeshTile* tile)
// Skip hard and non-internal edges.
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)
{
dtLink* link = &tile->links[idx];
@ -657,7 +657,7 @@ void dtNavMesh::baseOffMeshLinks(dtMeshTile* tile)
rdVcopy(v, nearestPt);
// Link off-mesh connection to target poly.
unsigned int idx = allocLink(tile);
unsigned int idx = tile->allocLink();
if (idx != DT_NULL_LINK)
{
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.
unsigned int tidx = allocLink(tile);
unsigned int tidx = tile->allocLink();
if (tidx != DT_NULL_LINK)
{
const unsigned short landPolyIdx = (unsigned short)decodePolyIdPoly(ref);

View File

@ -461,6 +461,103 @@ bool dtCreateDisjointPolyGroups(dtNavMesh* nav, dtDisjointSet& disjoint)
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'
bool dtCreateTraversalTableData(dtNavMesh* nav, const dtDisjointSet& disjoint, const int tableCount)
{
@ -877,18 +974,6 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData,
}
}
#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
// Mesh polys
const unsigned short* src = params->polys;
@ -1041,6 +1126,18 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData,
}
}
#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);
*outData = data;