Recast: several large improvements

The dtNavMesh structure members were marked as public, but they should be private. Made them private. Also, dtNavMesh::addTile was reconstructing all links, even when we add tiles with pre-existing links. The link building logic has been moved to a new member function 'dtNavMesh::connectTile'. This should only be called once the navmesh tile has been added succesfully during build. If we load a navmesh now, the existing links will be used from now on. The dtNavMesh::m_tileCount member is now also correctly incremented and decremented in addTile/removetile.
This commit is contained in:
Kawe Mazidjatari 2024-07-17 17:51:01 +02:00
parent 3b10774ca4
commit 38f57d95ad
8 changed files with 128 additions and 55 deletions

View File

@ -624,7 +624,7 @@ dtNavMesh* Editor::loadAll(std::string path)
return 0;
}
mesh->m_traversalTables[i] = traversalTable;
mesh->setTraverseTable(i, traversalTable);
}
}
@ -665,7 +665,9 @@ void Editor::saveAll(std::string path, const dtNavMesh* mesh)
header.numTiles++;
}
memcpy(&header.params, mesh->getParams(), sizeof(dtNavMeshParams));
const dtNavMeshParams* params = mesh->getParams();
memcpy(&header.params, params, sizeof(dtNavMeshParams));
fwrite(&header, sizeof(NavMeshSetHeader), 1, fp);
// Store tiles.
@ -684,13 +686,15 @@ void Editor::saveAll(std::string path, const dtNavMesh* mesh)
}
// Only store if we have 3 or more poly groups.
if (mesh->m_params.polyGroupCount >= DT_MIN_POLY_GROUP_COUNT)
if (params->polyGroupCount >= DT_MIN_POLY_GROUP_COUNT)
{
rdAssert(mesh->m_traversalTables);
int** traverseTables = mesh->getTraverseTables();
rdAssert(traverseTables);
for (int i = 0; i < header.params.traversalTableCount; i++)
{
const int* const tableData = mesh->m_traversalTables[i];
const int* const tableData = traverseTables[i];
rdAssert(tableData);
fwrite(tableData, sizeof(int), (header.params.traversalTableSize/4), fp);

View File

@ -432,10 +432,10 @@ void Editor_StaticTileMeshCommon::renderIntermediateTileMeshOptions()
if (m_navMesh)
{
const dtNavMeshParams& params = m_navMesh->m_params;
const float* origin = m_navMesh->m_orig;
const dtNavMeshParams& params = *m_navMesh->getParams();
//const float* origin = m_navMesh->m_orig;
ImGui::Text("Mesh Origin: \n\tX: %g \n\tY: %g \n\tZ: %g", origin[0], origin[1], origin[2]);
//ImGui::Text("Mesh Origin: \n\tX: %g \n\tY: %g \n\tZ: %g", origin[0], origin[1], origin[2]);
ImGui::Text("Tile Dimensions: %g x %g", params.tileWidth, params.tileHeight);
ImGui::Text("Poly Group Count: %d", params.polyGroupCount);
ImGui::Text("Traversal Table Size: %d", params.traversalTableSize);

View File

@ -850,10 +850,10 @@ void Editor_TempObstacles::handleSettings()
if (m_navMesh)
{
const dtNavMeshParams& params = m_navMesh->m_params;
const float* origin = m_navMesh->m_orig;
const dtNavMeshParams& params = *m_navMesh->getParams();
//const float* origin = m_navMesh->m_orig;
ImGui::Text("Mesh Origin: \n\tX: %g \n\tY: %g \n\tZ: %g", origin[0], origin[1], origin[2]);
//ImGui::Text("Mesh Origin: \n\tX: %g \n\tY: %g \n\tZ: %g", origin[0], origin[1], origin[2]);
ImGui::Text("Tile Dimensions: %g x %g", params.tileWidth, params.tileHeight);
ImGui::Text("Poly Group Count: %d", params.polyGroupCount);
ImGui::Text("Traversal Table Size: %d", params.traversalTableSize);

View File

@ -455,9 +455,12 @@ void Editor_TileMesh::buildTile(const float* pos)
if (data)
{
// Let the navmesh own the data.
dtStatus status = m_navMesh->addTile(data,dataSize,DT_TILE_FREE_DATA,0,0);
dtTileRef tileRef = 0;
dtStatus status = m_navMesh->addTile(data,dataSize,DT_TILE_FREE_DATA,0,&tileRef);
if (dtStatusFailed(status))
rdFree(data);
else
m_navMesh->connectTile(tileRef);
}
m_ctx->dumpLog("Build Tile (%d,%d):", tx,ty);
@ -531,9 +534,13 @@ void Editor_TileMesh::buildAllTiles()
// Remove any previous data (navmesh owns and deletes the data).
m_navMesh->removeTile(m_navMesh->getTileRefAt(x,y,0),0,0);
// Let the navmesh own the data.
dtStatus status = m_navMesh->addTile(data,dataSize,DT_TILE_FREE_DATA,0,0);
dtTileRef tileRef = 0;
dtStatus status = m_navMesh->addTile(data,dataSize,DT_TILE_FREE_DATA,0,&tileRef);
if (dtStatusFailed(status))
rdFree(data);
else
m_navMesh->connectTile(tileRef);
}
}
}

View File

@ -387,7 +387,7 @@ struct dtMeshTile
int dataSize; ///< Size of the tile data.
int flags; ///< Tile flags. (See: #dtTileFlags)
dtMeshTile* next; ///< The next free tile, or the next tile in the spatial grid.
void* unknownVTableInstance; // See [r5apex_ds + F437D9] for usage
void* deleteCallback; ///< Custom destruction callback, called after free. (See [r5apex_ds + F437D9] for usage.)
private:
dtMeshTile(const dtMeshTile&);
dtMeshTile& operator=(const dtMeshTile&);
@ -449,7 +449,9 @@ public:
dtStatus init(unsigned char* data, const int dataSize, const int tableCount, const int flags);
/// The navigation mesh initialization params.
const dtNavMeshParams* getParams() const;
/// @note The parameters are created automatically when the single tile
/// initialization is performed.
const dtNavMeshParams* getParams() const { return &m_params; }
/// Adds a tile to the navigation mesh.
/// @param[in] data Data for the new tile mesh. (See: #dtCreateNavMeshData)
@ -467,6 +469,11 @@ public:
/// @return The status flags for the operation.
dtStatus removeTile(dtTileRef ref, unsigned char** data, int* dataSize);
/// Connects the specified tile to the navigation mesh.
/// @param[in] ref The reference of the tile to connect.
/// @return The status flags for the operation.
dtStatus connectTile(const dtTileRef tileRef);
/// @}
/// @{
@ -570,6 +577,18 @@ public:
/// @return The specified off-mesh connection, or null if the polygon reference is not valid.
const dtOffMeshConnection* getOffMeshConnectionByRef(dtPolyRef ref) const;
/// The navigation mesh traversal tables.
int** getTraverseTables() const { return m_traversalTables; }
/// Sets the traverse table slot.
/// @param[in] index The index of the traverse table.
/// @param[in] table The traverse table data.
void setTraverseTable(const int index, int* const table);
/// Sets the size of the traverse table.
/// @param[in] size The size of the traverse table.
void setTraverseTableSize(const int size) { m_params.traversalTableSize = size; }
/// @}
/// @{
@ -600,6 +619,14 @@ public:
/// @return The status flags for the operation.
dtStatus getPolyArea(dtPolyRef ref, unsigned char* resultArea) const;
/// Gets the polygon group count.
/// @return The total number of polygon groups.
int getPolyGroupCount() const { return m_params.polyGroupCount; }
/// Sets the polygon group count.
/// @param[in] count The polygon group count.
void setPolyGroupcount(const int count) { m_params.polyGroupCount = count; }
/// Gets the size of the buffer required by #storeTileState to store the specified tile's state.
/// @param[in] tile The tile.
/// @return The size of the buffer required to store the state.
@ -713,7 +740,7 @@ public:
/// @}
/// Returns pointer to tile in the tile array.
dtMeshTile* getTile(int i);
public:
private:
// Explicitly disabled copy constructor and copy assignment operator.
dtNavMesh(const dtNavMesh&);
dtNavMesh& operator=(const dtNavMesh&);

View File

@ -194,6 +194,7 @@ Notes:
dtNavMesh::dtNavMesh() :
m_tileWidth(0),
m_tileHeight(0),
m_tileCount(0),
m_maxTiles(0),
m_tileLutSize(0),
m_tileLutMask(0),
@ -322,16 +323,13 @@ dtStatus dtNavMesh::init(unsigned char* data, const int dataSize, const int tabl
if (dtStatusFailed(status))
return status;
return addTile(data, dataSize, flags, 0, 0);
}
dtTileRef tileRef;
status = addTile(data,dataSize,flags,0,&tileRef);
/// @par
///
/// @note The parameters are created automatically when the single tile
/// initialization is performed.
const dtNavMeshParams* dtNavMesh::getParams() const
{
return &m_params;
if (dtStatusFailed(status))
return status;
return connectTile(tileRef);
}
//////////////////////////////////////////////////////////////////////////////////////////
@ -531,9 +529,10 @@ void dtNavMesh::connectExtOffMeshLinks(dtMeshTile* tile, dtMeshTile* target, int
// Link off-mesh connection to target poly.
unsigned int idx = allocLink(target);
dtLink* link = nullptr;
if (idx != DT_NULL_LINK)
{
dtLink* link = &target->links[idx];
link = &target->links[idx];
link->ref = ref;
link->edge = (unsigned char)1;
link->side = oppositeSide;
@ -547,26 +546,37 @@ void dtNavMesh::connectExtOffMeshLinks(dtMeshTile* tile, dtMeshTile* target, int
}
// Link target poly to off-mesh connection.
unsigned int tidx = DT_NULL_LINK;
dtLink* tlink = nullptr;
if (targetCon->flags & DT_OFFMESH_CON_BIDIR)
{
unsigned int tidx = allocLink(tile);
tidx = allocLink(tile);
if (tidx != DT_NULL_LINK)
{
const unsigned short landPolyIdx = (unsigned short)decodePolyIdPoly(ref);
dtPoly* landPoly = &tile->polys[landPolyIdx];
dtLink* link = &tile->links[tidx];
link->ref = getPolyRefBase(target) | (dtPolyRef)(targetCon->poly);
link->edge = 0xff;
link->side = (unsigned char)(side == -1 ? 0xff : side);
link->bmin = link->bmax = 0;
tlink = &tile->links[tidx];
tlink->ref = getPolyRefBase(target) | (dtPolyRef)(targetCon->poly);
tlink->edge = 0xff;
tlink->side = (unsigned char)(side == -1 ? 0xff : side);
tlink->bmin = tlink->bmax = 0;
// Add to linked list.
link->next = landPoly->firstLink;
tlink->next = landPoly->firstLink;
landPoly->firstLink = tidx;
link->jumpType = 0xFF;
link->otherUnk = 0;
link->reverseLinkIndex = 0xFFFF;
tlink->jumpType = 0xFF;
tlink->otherUnk = 0;
tlink->reverseLinkIndex = 0xFFFF;
}
}
// Set the reverse link indices if there is a possibility to reverse.
// NOTE: it appears that Titanfall 2 doesn't seem to do this on their
// navmeshes, so we probably shouldn't do it either. Commented for now.
//if (link && tlink)
//{
// link->reverseLinkIndex = (unsigned short)tidx;
// tlink->reverseLinkIndex = (unsigned short)idx;
//}
}
}
@ -1026,6 +1036,8 @@ dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags,
// Make sure we could allocate a tile.
if (!tile)
return DT_FAILURE | DT_OUT_OF_MEMORY;
tile->deleteCallback = nullptr;
// Insert tile into the position lut.
int h = computeTileHash(header->x, header->y, m_tileLutMask);
@ -1064,18 +1076,34 @@ dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags,
if (!bvtreeSize)
tile->bvTree = 0;
// Build links freelist
tile->linksFreeList = 0;
tile->links[header->maxLinkCount-1].next = DT_NULL_LINK;
for (int i = 0; i < header->maxLinkCount-1; ++i)
tile->links[i].next = i+1;
// Init tile.
tile->header = header;
tile->data = data;
tile->dataSize = dataSize;
tile->flags = flags;
tile->unknownVTableInstance = nullptr;
m_tileCount++;
if (result)
*result = getTileRef(tile);
return DT_SUCCESS;
}
dtStatus dtNavMesh::connectTile(const dtTileRef tileRef)
{
const int tileIndex = (int)decodePolyIdTile((dtPolyRef)tileRef);
if (tileIndex >= m_maxTiles)
return DT_FAILURE | DT_OUT_OF_MEMORY;
dtMeshTile* tile = &m_tiles[tileIndex];
const dtMeshHeader* header = tile->header;
// Build links freelist
tile->linksFreeList = 0;
tile->links[header->maxLinkCount - 1].next = DT_NULL_LINK;
for (int i = 0; i < header->maxLinkCount - 1; ++i)
tile->links[i].next = i + 1;
connectIntLinks(tile);
@ -1113,10 +1141,7 @@ dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags,
connectExtOffMeshLinks(neis[j], tile, rdOppositeTile(i));
}
}
if (result)
*result = getTileRef(tile);
return DT_SUCCESS;
}
@ -1436,6 +1461,8 @@ dtStatus dtNavMesh::removeTile(dtTileRef ref, unsigned char** data, int* dataSiz
tile->next = m_nextFree;
m_nextFree = tile;
m_tileCount--;
return DT_SUCCESS;
}
@ -1631,6 +1658,14 @@ const dtOffMeshConnection* dtNavMesh::getOffMeshConnectionByRef(dtPolyRef ref) c
}
void dtNavMesh::setTraverseTable(const int index, int* const table)
{
rdAssert(index >= 0 && index < m_params.traversalTableCount);
rdAssert(m_traversalTables);
m_traversalTables[index] = table;
}
dtStatus dtNavMesh::setPolyFlags(dtPolyRef ref, unsigned short flags)
{
if (!ref) return DT_FAILURE;

View File

@ -455,18 +455,16 @@ bool dtCreateDisjointPolyGroups(dtNavMesh* nav, dtDisjointSet& disjoint)
}
}
nav->m_params.polyGroupCount = disjoint.getSetCount();
nav->setPolyGroupcount(disjoint.getSetCount());
return true;
}
// todo(amos): remove param 'tableCount' and make struct 'dtTraversalTableCreateParams'
bool dtCreateTraversalTableData(dtNavMesh* nav, const dtDisjointSet& disjoint, const int tableCount)
{
const int polyGroupCount = nav->m_params.polyGroupCount;
const int polyGroupCount = nav->getPolyGroupCount();
const int tableSize = calcTraversalTableSize(polyGroupCount);
rdAssert(nav->m_traversalTables);
// TODO: currently we allocate 5 buffers and just copy the same traversal
// tables in, this works fine since we don't generate jump links and
// therefore all poly islands should be marked unreachable from each other.
@ -491,7 +489,7 @@ bool dtCreateTraversalTableData(dtNavMesh* nav, const dtDisjointSet& disjoint, c
if (!traversalTable)
return false;
nav->m_traversalTables[i] = traversalTable;
nav->setTraverseTable(i, traversalTable);
memset(traversalTable, 0, sizeof(int)*tableSize);
for (unsigned short j = 0; j < polyGroupCount; j++)
@ -505,8 +503,7 @@ bool dtCreateTraversalTableData(dtNavMesh* nav, const dtDisjointSet& disjoint, c
}
}
nav->m_params.traversalTableSize = tableSize;
nav->m_params.traversalTableCount = tableCount;
nav->setTraverseTableSize(tableSize);
return true;
}

View File

@ -762,12 +762,15 @@ dtStatus dtTileCache::buildNavMeshTile(const dtCompressedTileRef ref, dtNavMesh*
if (navData)
{
// Let the navmesh own the data.
status = navmesh->addTile(navData,navDataSize,DT_TILE_FREE_DATA,0,0);
dtTileRef tileRef = 0;
status = navmesh->addTile(navData,navDataSize,DT_TILE_FREE_DATA,0,&tileRef);
if (dtStatusFailed(status))
{
rdFree(navData);
return status;
}
else
navmesh->connectTile(tileRef);
}
return DT_SUCCESS;