Moving a Streaming Level's static nav mesh

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.

Hello, I’ve encountered the same issue as you did. I need to transform the nav mesh from level instances that are loaded in. If you managed to get this to work, can you share your solution?