-
Notifications
You must be signed in to change notification settings - Fork 42
AINavData
DummkopfOfHachtenduden edited this page Jan 14, 2025
·
16 revisions
// Header
1 byte version // expecting 1
4 uint simpleDungeonDataOffset
//AI_NAVIGATION Data
//CRefDungeon
2 ushort regionID
4 uint blockCount
for (int i = 0; i < blockCount; i++)
{
//CRefBlock
4 uint block.Index
4 uint block.CellCount // number of refCells in this block
4 uint block.EdgeCount // number of refEdges in this block
// CellLookupTable
// A table that when given a start and goal cell returns the index of the edge that should be used in order to reach the goal.
for(int goalCellID = 0; goalCellID < cellCount; goalCellID++)
{
for(int startCellID = 0; startCellID < cellCount; startCellID++)
{
2 short refEdgeIndex0; // refEdge to use when pathing forwards (start cell to goal cell)
2 short refEdgeIndex1; // refEdge to use when pathing backwards (goal cell to start cell)
}
}
// Links
4 uint linkCount
for (int i = 0; i < linkCount; i++)
{
4 uint link.ID // same as CellID
2 ushort link.CellID
2 ushort link.LinkedObjID // the other block
2 ushort link.LinkedObjRefEdgeIndex // a global edge of the other block
}
}
// BlockLookupTable
// A table that when given a start and goal block returns the index of the cell that should be used as subgoal in order to reach the actual goal.
for(int goalBlockID = 0; goalBlockID < blockCount; goalBlockID++)
{
for(int startBlockID = 0; startBlockID < blockCount; startBlockID++)
{
2 short refCellID // the memory for this is not zeroed and may contain garbage where startBlockID == goalBlockID which are invalid paths anyway.
}
}
4 uint int0 // expecting 0
//SimpleDungeonData
//simpleDungeonDataOffset will get you here
2 ushort regionID // yes twice
4 uint blockCount
for (uint i = 0; i < blockCount; i++)
{
4 uint edgeCount
for (uint i = 0; i < edgeCount; i++)
{
12 Vector3 edgeCenter
}
}
4 uint int1 // expecting 0
#pragma array_limit 4294967296
#pragma pattern_limit 4294967296
struct RefCell
{
u16 RefEdgeIndex0;
u16 RefEdgeIndex1;
};
struct RefBlockLink
{
u32 ID;
u16 CellID;
u16 LinkedObjID;
u16 LinkedObjRefEdgeIndex;
};
struct RefBlock
{
u32 Index;
u32 CellCount;
u32 EdgeCount;
RefCell CellLookupTable[CellCount*CellCount];
u32 LinkCount;
RefBlockLink Links[LinkCount];
};
struct RefDungeon
{
u16 RegionID;
u32 BlockCount;
RefBlock Blocks[BlockCount];
u16 BlockLookupTable[BlockCount*BlockCount];
u32 Int0;
};
struct Vector3
{
float X;
float Y;
float Z;
};
struct SimpleDungeonBlock
{
u32 EdgeCount;
Vector3 EdgeCenterPoints[EdgeCount];
};
struct SimpleDungeonData
{
u16 RegionID;
u32 BlockCount;
SimpleDungeonBlock Blocks[BlockCount];
u32 Int0;
};
struct AI_NAVIGATION
{
u8 Version;
u32 SimpleDungeonDataOffset;
RefDungeon refDungeon;
SimpleDungeonData simpleDungeonData @ SimpleDungeonDataOffset;
};
AI_NAVIGATION file @ 0;
Some RefBlocks in AINavData may have be computed based on simplified mesh versions resulting in a mismatch of cell indices if you use the original mesh. This has been the major reason why this format took so long to figure out in the fist place. 😄
The simplified mesh is a retriangulated version of the original with only the most important points and some additional Steiner points. The actual algorithm is unknown to me but you may reverse engineer it at vsro_client.sub_0044BF10. It's also possible to patch the client into dumping these as the simplification code is still included despite not being used there.
Unsmoothed paths generated with AINavData.