Recast: initial implementation of multi-set support

Allow to compile the editor for separate versions of the navmesh set. NavMesh cells are now also partially reversed and properly read out. Creating and writing them will happen in the future.
This commit is contained in:
Amos 2024-07-22 18:48:41 +02:00
parent 38223fb6f3
commit 49a0ceb148
4 changed files with 100 additions and 25 deletions

View File

@ -232,6 +232,18 @@ public:
ImVec2((float)x, h - (float)y), ImVec4(0, 0, 0, 0.8f), "%hu (%d,%d)", value, i, j);
}
}
for (int j = 0; j < tile->header->maxCellCount; j++)
{
const dtCell* cell = &tile->cells[j];
if (gluProject((GLdouble)cell->pos[0]+drawOffset[0], (GLdouble)cell->pos[1]+drawOffset[1], (GLdouble)cell->pos[2]+drawOffset[2]+30,
model, proj, view, &x, &y, &z))
{
ImGui_RenderText(ImGuiTextAlign_e::kAlignCenter,
ImVec2((float)x, h - (float)y), ImVec4(0, 0.4, 0, 0.8f), "(%d,%d)", j, cell->flags);
}
}
}
}

View File

@ -24,8 +24,9 @@
// NOTE: these are defines as we need to be able to switch between code that is
// dedicated for each version, during compile time.
#define DT_NAVMESH_SET_VERSION 8 // Public versions: 5,7,8,9.
#define DT_NAVMESH_SET_VERSION 9 // Public versions: 5,7,8,9.
#define DT_NAVMESH_SET_MAGIC ('M'<<24 | 'S'<<16 | 'E'<<8 | 'T')
int dtGetNavMeshVersionForSet(const int setVersion);
// Undefine (or define in a build config) the following line to use 64bit polyref.
// Generally not needed, useful for very large worlds.
@ -99,6 +100,9 @@ static const unsigned short DT_NULL_TRAVERSE_REVERSE_LINK = 0xffff;
/// The cached traverse link distance quantization factor.
static const float DT_TRAVERSE_DIST_QUANT_FACTOR = 10.f;
/// A value that indicates the link doesn't contain a hint index.
static const unsigned short DT_NULL_HINT = 0xffff;
/// @{
/// @name Tile Serialization Constants
/// These constants are used to detect whether a navigation tile's data
@ -109,7 +113,7 @@ static const float DT_TRAVERSE_DIST_QUANT_FACTOR = 10.f;
static const int DT_NAVMESH_MAGIC = 'D'<<24 | 'N'<<16 | 'A'<<8 | 'V';
/// A version number used to detect compatibility of navigation tile data.
static const int DT_NAVMESH_VERSION = 16;
static const int DT_NAVMESH_VERSION = dtGetNavMeshVersionForSet(DT_NAVMESH_SET_VERSION);
/// A magic number used to detect the compatibility of navigation tile states.
static const int DT_NAVMESH_STATE_MAGIC = 'D'<<24 | 'N'<<16 | 'M'<<8 | 'S';
@ -143,8 +147,8 @@ enum dtTileFlags
/// The navigation mesh owns the tile memory and is responsible for freeing it.
DT_TILE_FREE_DATA = 0x01,
/// The navigation mesh owns the offmesh connection memory and is responsible for freeing it.
DT_OFFMESH_FREE_DATA = 0x02,
/// The navigation mesh owns the cell memory and is responsible for freeing it.
DT_CELL_FREE_DATA = 0x02,
};
/// Vertex flags returned by dtNavMeshQuery::findStraightPath.
@ -281,6 +285,23 @@ struct dtLink
unsigned char dtCalcLinkDistance(const float* spos, const float* epos);
/// Defines a cell in a tile.
/// @note This is used to prevent entities from clipping into each other.
/// @see dtMeshTile
struct dtCell
{
float pos[3]; ///< The position of the cell.
unsigned int polyIndex; ///< The index of the poly this cell is on.
unsigned char pad;
unsigned char occupystate[4]; ///< The occupation state of this cell, -1 means not occupied. See [r5apex_ds + 0xEF86C9].
#if DT_NAVMESH_SET_VERSION >= 9
unsigned char data[27]; // TODO: reverse this, always appears 0.
#else
unsigned char data[52]; // TODO: reverse this, always appears 0.
#endif
};
/// Bounding volume node.
/// @note This structure is rarely if ever used by the end user.
/// @see dtMeshTile
@ -312,17 +333,29 @@ struct dtOffMeshConnection
/// End point side.
unsigned char side;
#if DT_NAVMESH_SET_VERSION == 5
/// NOTE: this is unconfirmed, it might not be the jumpType.
unsigned char jumpType;
unsigned char unk1;
#endif
/// The id of the off-mesh connection. (User assigned when the navigation mesh is built.)
unsigned short userId;
#if DT_NAVMESH_SET_VERSION >= 7
/// The hint index of the off-mesh connection. (Or #DT_NULL_HINT if there is no hint.)
unsigned short hintIdx;
#endif
/// The reference position set to the start of the off-mesh connection with an offset of DT_OFFMESH_CON_REFPOS_OFFSET
float refPos[3]; // See [r5apex_ds + F114CF], [r5apex_ds + F11B42], [r5apex_ds + F12447].
/// The reference yaw angle set towards the end position of the off-mesh connection.
float refYaw; // See [r5apex_ds + F11527], [r5apex_ds + F11F90], [r5apex_ds + F12836].
#if DT_NAVMESH_SET_VERSION >= 9
/// Off-mesh connections are always placed in pairs, in version 5 to 8, each connection for
/// the pair was a separate instance. In newer versions, this is squashed into 1 connection.
float secPos[6];
#endif
};
/// Calculates the yaw angle in an off-mesh connection.
@ -349,7 +382,7 @@ struct dtMeshHeader
unsigned int userId; ///< The user defined id of the tile.
int polyCount; ///< The number of polygons in the tile.
int unkPerPoly;
int polyMapCount;
int vertCount; ///< The number of vertices in the tile.
int maxLinkCount; ///< The number of allocated links.
@ -362,7 +395,7 @@ struct dtMeshHeader
int bvNodeCount; ///< The number of bounding volume nodes. (Zero if bounding volumes are disabled.)
int offMeshConCount; ///< The number of off-mesh connections.
int offMeshBase; ///< The index of the first polygon which is an off-mesh connection.
int offMeshEnds;
int maxCellCount; ///< The number of allocated cells.
float walkableHeight; ///< The height of the agents using the tile.
float walkableRadius; ///< The radius of the agents using the tile.
@ -383,7 +416,7 @@ struct dtMeshTile
unsigned int linksFreeList; ///Index to the next free link.
dtMeshHeader* header; ///The tile header.
dtPoly* polys; ///The tile polygons. [Size: dtMeshHeader::polyCount]
dtPoly* polysEnd; ///The tile polygons array end pointer.
int* polyMap; ///TODO: needs to be reversed.
float* verts; ///The tile vertices. [Size: dtMeshHeader::vertCount]
dtLink* links; ///The tile links. [Size: dtMeshHeader::maxLinkCount]
dtPolyDetail* detailMeshes; ///The tile's detail sub-meshes. [Size: dtMeshHeader::detailMeshCount]
@ -400,7 +433,7 @@ struct dtMeshTile
dtBVNode* bvTree;
dtOffMeshConnection* offMeshCons; ///< The tile off-mesh connections. [Size: dtMeshHeader::offMeshConCount]
dtOffMeshConnection* offMeshConsEnd; ///< The tile off-mesh connections array end pointer.
dtCell* cells; ///< The tile cells. [Size: dtMeshHeader::tileCellCount]
unsigned char* data; ///< The tile data. (Not directly accessed under normal situations.)
@ -437,10 +470,12 @@ struct dtNavMeshParams
int traversalTableSize; ///< The total size of the static traversal table. This is computed using calcTraversalTableSize(polyGroupcount).
int traversalTableCount; ///< The total number of traversal tables in this navmesh. Each TraverseAnimType uses its own table as their available jump links should match their behavior and abilities.
#if DT_NAVMESH_SET_VERSION >= 7
// NOTE: this seems to be used for some wallrunning code. This allocates a buffer of size 0x30 * magicDataCount,
// then copies in the data 0x30 * magicDataCount at the end of the navmesh file (past the traversal tables).
// See [r5apex_ds + F43600] for buffer allocation and data copy, see note at dtNavMesh::m_someMagicData for usage.
int magicDataCount;
#endif
};
/// A navigation mesh based on tiles of convex polygons.

View File

@ -567,6 +567,7 @@ void dtNavMesh::connectExtOffMeshLinks(dtMeshTile* tile, dtMeshTile* target, int
}
}
#if DT_NAVMESH_SET_VERSION == 5
// NOTE: this might not be correct; Titanfall 2 off-mesh link dtLink
// objects don't set these, however when setting these, the jump links
// do work. More research is needed, this might also be correct, just
@ -583,6 +584,7 @@ void dtNavMesh::connectExtOffMeshLinks(dtMeshTile* tile, dtMeshTile* target, int
tlink->traverseDist = linkDist;
tlink->reverseLink = (unsigned short)idx;
}
#endif
}
}
@ -1054,29 +1056,30 @@ dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags,
const int headerSize = rdAlign4(sizeof(dtMeshHeader));
const int vertsSize = rdAlign4(sizeof(float)*3*header->vertCount);
const int polysSize = rdAlign4(sizeof(dtPoly)*header->polyCount);
const int polyMapSize = rdAlign4(sizeof(int)*(header->polyCount*header->polyMapCount));
const int linksSize = rdAlign4(sizeof(dtLink)*(header->maxLinkCount));
const int detailMeshesSize = rdAlign4(sizeof(dtPolyDetail)*header->detailMeshCount);
const int detailVertsSize = rdAlign4(sizeof(float)*3*header->detailVertCount);
const int detailTrisSize = rdAlign4(sizeof(unsigned char)*4*header->detailTriCount);
const int bvtreeSize = rdAlign4(sizeof(dtBVNode)*header->bvNodeCount);
const int offMeshLinksSize = rdAlign4(sizeof(dtOffMeshConnection)*header->offMeshConCount);
const int polyCount = header->polyCount;
const int unkPerPoly = header->unkPerPoly;
const int offMeshConCount = header->offMeshConCount;
#if DT_NAVMESH_SET_VERSION >= 8
const int probesSize = rdAlign4(sizeof(dtCell)*header->maxCellCount);
#endif
unsigned char* d = data + headerSize;
tile->verts = rdGetThenAdvanceBufferPointer<float>(d, vertsSize);
tile->polys = rdGetThenAdvanceBufferPointer<dtPoly>(d, polysSize);
tile->polysEnd = &tile->polys[polyCount];
d = (unsigned char*)(tile->polysEnd) + (polyCount * unkPerPoly) * sizeof(int);
tile->polyMap = rdGetThenAdvanceBufferPointer<int>(d, polyMapSize);
tile->links = rdGetThenAdvanceBufferPointer<dtLink>(d, linksSize);
tile->detailMeshes = rdGetThenAdvanceBufferPointer<dtPolyDetail>(d, detailMeshesSize);
tile->detailVerts = rdGetThenAdvanceBufferPointer<float>(d, detailVertsSize);
tile->detailTris = rdGetThenAdvanceBufferPointer<unsigned char>(d, detailTrisSize);
tile->bvTree = rdGetThenAdvanceBufferPointer<dtBVNode>(d, bvtreeSize);
tile->offMeshCons = rdGetThenAdvanceBufferPointer<dtOffMeshConnection>(d, offMeshLinksSize);
tile->offMeshConsEnd = &tile->offMeshCons[offMeshConCount];
#if DT_NAVMESH_SET_VERSION >= 8
tile->cells = rdGetThenAdvanceBufferPointer<dtCell>(d, probesSize);
#endif
// If there are no items in the bvtree, reset the tree pointer.
if (!bvtreeSize)
@ -1429,6 +1432,12 @@ dtStatus dtNavMesh::removeTile(dtTileRef ref, unsigned char** data, int* dataSiz
// Reset tile.
if (tile->flags & DT_TILE_FREE_DATA)
{
if (tile->flags & DT_CELL_FREE_DATA)
{
rdFree(tile->cells);
tile->cells = 0;
}
// Owns data
rdFree(tile->data);
tile->data = 0;
@ -1778,3 +1787,18 @@ void dtCalcOffMeshRefPos(const float* spos, float yaw, float offset, float* res)
res[1] = spos[1]+dy;
res[2] = spos[2];
}
int dtGetNavMeshVersionForSet(const int setVersion)
{
// TODO: check r2tt (Titanfall 2 tech test). It might have a set version lower
// than 5, which might be interesting to reverse as well and support.
switch (setVersion)
{
case 5: return 13;
case 6: return 14;
case 7: return 15;
case 8: // 8 & 9 use the same navmesh version, but they are different!
case 9: return 16;
default: rdAssert(0); return -1; // Unsupported set version!
}
}

View File

@ -674,12 +674,12 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData,
const int bvTreeSize = params->buildBvTree ? rdAlign4(sizeof(dtBVNode)*params->polyCount*2) : 0;
const int offMeshConsSize = rdAlign4(sizeof(dtOffMeshConnection)*storedOffMeshConCount);
int unkPerPoly = 0; // TODO: this data has to be reversed still from the NavMesh!
const int sthSize = unkPerPoly * totPolyCount;
int polyMapCount = 0; // TODO: this data has to be reversed still from the NavMesh!
const int polyMapSize = polyMapCount * totPolyCount;
const int dataSize = headerSize + vertsSize + polysSize + linksSize +
detailMeshesSize + detailVertsSize + detailTrisSize +
bvTreeSize + offMeshConsSize + sthSize;
bvTreeSize + offMeshConsSize + polyMapSize;
//printf("%i %i %i %i(%i links) %i %i %i %i %i\n", headerSize, vertsSize, polysSize, linksSize, maxLinkCount, detailMeshesSize, detailVertsSize, detailTrisSize, bvTreeSize, offMeshConsSize);
//printf("%i\n", dataSize);
@ -697,7 +697,7 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData,
dtMeshHeader* header = rdGetThenAdvanceBufferPointer<dtMeshHeader>(d, headerSize);
float* navVerts = rdGetThenAdvanceBufferPointer<float>(d, vertsSize);
dtPoly* navPolys = rdGetThenAdvanceBufferPointer<dtPoly>(d, polysSize);
unsigned int* unknownArray = rdGetThenAdvanceBufferPointer<unsigned int>(d, sthSize);
unsigned int* polyMap = rdGetThenAdvanceBufferPointer<unsigned int>(d, polyMapSize);
d += linksSize; // Ignore links; just leave enough space for them. They'll be created on load.
//dtLink* links = rdGetThenAdvanceBufferPointer<dtLink>(d, linksSize);
dtPolyDetail* navDMeshes = rdGetThenAdvanceBufferPointer<dtPolyDetail>(d, detailMeshesSize);
@ -706,7 +706,7 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData,
dtBVNode* navBvtree = rdGetThenAdvanceBufferPointer<dtBVNode>(d, bvTreeSize);
dtOffMeshConnection* offMeshCons = rdGetThenAdvanceBufferPointer<dtOffMeshConnection>(d, offMeshConsSize);
rdIgnoreUnused(unknownArray);
rdIgnoreUnused(polyMap);
//for(int i=0;i<unkPerPoly*totPolyCount;i++)
// unknownArray[i] = rand();
@ -727,13 +727,13 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData,
header->detailTriCount = detailTriCount;
header->bvQuantFactor = 1.0f / params->cs;
header->offMeshBase = params->polyCount;
header->offMeshEnds = -1;
header->maxCellCount = -1;
header->walkableHeight = params->walkableHeight;
header->walkableRadius = params->walkableRadius;
header->walkableClimb = params->walkableClimb;
header->offMeshConCount = storedOffMeshConCount;
header->bvNodeCount = params->buildBvTree ? params->polyCount*2 : 0;
header->unkPerPoly = unkPerPoly;
header->polyMapCount = polyMapCount;
const int offMeshVertsBase = params->vertCount;
const int offMeshPolyBase = params->polyCount;
@ -903,10 +903,14 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData,
con->refYaw = params->offMeshConRefYaw[i];
con->flags = params->offMeshConDir[i] ? DT_OFFMESH_CON_BIDIR : 0;
con->side = offMeshConClass[i*2+1];
#if DT_NAVMESH_SET_VERSION == 5
con->jumpType = params->offMeshConJumps[i];
con->unk1 = 1;
if (params->offMeshConUserID)
con->userId = params->offMeshConUserID[i];
#endif
con->userId = params->offMeshConUserID[i];
#if DT_NAVMESH_SET_VERSION >= 7
con->hintIdx = DT_NULL_HINT; // todo(amos): hints are currently not supported.
#endif
n++;
}
}