I want to transform an instance of a sublevel including its nav mesh.
Why not use dynamic/runtime generation and move the nav mesh around? I’m assuming it is too slow. Nav mesh generation is not a cheap operation. It is likely far too expensive for a dedicated server handling 100s of clients running in and out of 100s of Areas.
Problem
Let me drill down from what I’m doing specifically to what I see as going on in the nav mesh code:
- I have a game with randomly generated Areas.
- The Areas are made of randomly placed Levels.
- Levels can be instanced multiple times in the same Area at different positions and rotations.
- Each level instance is loaded by adding a ULevelStreamingKismet to UWorld::StreamingLevels.
- ULevelStreamingKismet inherits from ULevelStreaming - which has a LevelTransform. ULevelStreaming::LevelTransform moves almost everything through UWorld::AddToWorld() and FLevelUtils::ApplyLevelTransform(). This doesn’t transform the navmesh
- In ARecastNavMesh::OnStreamingLevelAdded(ULevel* InLevel, UWorld* InWorld), it adds the URecastNavMeshDataChunk::Tiles to the global nav mesh using URecastNavMeshDataChunk::AttachTiles() and dtNavMesh::addTile().
- Neither URecastNavMeshDataChunk::AttachTiles() nor dtNavMesh::addTile() respect nor see ULevelStreaming::LevelTransform.
Can’t avoid changing Unreal source
I’m not seeing a way to solve this without changing our fork of Unreal 4:
- ARecastNavMesh::OnStreamingLevelAdded() is virtual and could be overriden, but…
- URecastNavMeshDataChunk can’t be overriden and has almost nothing that is virtual.
- URecastNavMeshDataChunk keeps the Tiles private.
Proposed Solution
So, (deep breath) I’m thinking that our solution needs to change several things in UE4:
- ARecastNavMesh::OnStreamingLevelAdded() needs to respect the Level Transform. It should find the ULevelStreaming instance associated with this ULevel to get the LevelTransform - which isn’t stored in the ULevel.
- Pass the LevelTransform into URecastNavMeshDataChunk::AttachTiles(). Update URecastNavMeshDataChunk::AttachTiles() so that it respects the transform when organizing the Tiles. Pass the LevelTransform to dtNavMesh::addTile().
- Change dtNavMesh::addTile() so that it transforms its data. This looks rather daunting. There is a lot of data being wrangled in this function.
Questions:
- Before diving into this, am I looking the problem correctly?
- Am I misreading how a Level’s nav mesh is being loaded and integrated into the global nav mesh?
- How fast is dynamic nav mesh generation? For a typical 1k by 1k level, are we talking milliseconds, seconds, 10s of seconds?
- Can the nav mesh selectively regenerate? Or do we have to cast the nav mesh against the entire random level at once each time a level streams in or out?
- Are we also going to need to make a copy of the URecastNavMeshDataChunk for each instance of a Level in order to transform it into place?
- Epic, if I added this in as a pull request, would it be considered? Or is there a better approach that we should take?
For anyone who made it this far. Thanks! Let me know if I can/should clarify anything.