World composition in Babylon JS

I’m trying to understand how to work with a huge map as we can see in such RPG games as Lineage 2.
As I know we can’t just load the entire map in memory (10km x 10km). So we have to split it into small cells. Also I found that some game engines have this feature World Composition User Guide | Unreal Engine Documentation

Does Babylon JS have something suitable for it?
I’m aware of Auto-LOD but it’s not the same.

There is Dynamic Terrain | Babylon.js Documentation for this purpose.

1 Like

Thanks for that. As I understand it consumes an array of points

  1. What if I want to use a mesh as terrain (with attached houses/trees to it), created in Blender. How I can transform it into points?
  2. Still, it will load the whole mesh (instead of loading only needed) and then display needed area. Isn’t it?
  1. As far as I know there is no feature available that will convert a terrain created in Blender to a dynamic terrain in Babylon.js. You would need to find a way to extract the data points and house/tree positions from the Blender terrain. You could import each house/tree type as an individual mesh into Babylon.js then use Adding Objects to a Dynamic Terrain | Babylon.js Documentation.

  2. Dynamic Terrain loads the whole of the data points and then dynamically generates only the viewable terrain and objects as meshes.

If you have not already created the terrain in Blender it might be easier to build it directly in Babylon.js. You could still build the houses/trees in Blender and import.

If @jerome is available he might wish to add something.


The reason is why I am going to create it in Blender it’s because I will able to see the whole scene and edit it where it’s needed. But in Babylon it would be like a randomly generated terrain.

I wouldn’t recommend for your case to load the whole scene as one file (first of all, it can be just too heavy), you may try to build a kind of object pool.
Define a pool class that maintains a collection of reusable objects . Each object supports an “in use” query to tell if it is currently “alive”. When the pool is initialized, it creates the entire collection of objects up front (usually in a single contiguous allocation) and initializes them all to the “not in use” state.
And use, instead of unique geometry, as much clones, instances and thin instances as possible.

1 Like

As I understand that pool should also contain a part of terrain (needed cell/region) which should be loaded if we are going toward it. For example, we are on the cell A and going to cell B which is not loaded yet. It loads, we add it to our world and then we have an another issue with NavMesh - it doesn’t know about that just loaded region. So we have recreate navigation mesh again. Right? @labris

Here is my example
Was testing if NavMesh agent accepts two separated meshes.

@Cedric may be able to help with NavMesh questions :slight_smile:


Navigation plugin only support 1 single navmesh at a time. Mainly for 2 reasons:

  • no one asked for more (yet)
  • a navmesh doesn’t require much memory once baked, your whole level can fit in few 100Ko

I would test computing and baking the navmesh for whole world inside a single navmesh. It can be independant of the rendering geometry.
If it starts to be too big. Both in term of download or memory consumption, then we start discussing about multiple navmesh support.
Don’t forget that the navmesh can be tiled so dynamic updates can occur.
Actually, I’m not sure having multiple navmesh is a good idea because of agents going from 1 navmesh to another. I think the best is to handle dynamic tiles to be loaded/unloaded. But even just with that, what to do when an agent is attached to a tile that you want to unload? The problem gets very complex quite fast.


One of the possible approaches - to split all needed meshes and import them independently.
First import the terrain (as GLTF or as array of points).
Then import all reusable objects (actually you may need only 1 tree for the forest, for example).
Then create as much instances/thin instances as you need in relevant positions (it can be done dynamically with the kind of object pool).

1 Like

Thanks for your explaining. Could you take a look at this

I have added a 4 sec delay to show dynamic scene loading. It creates a proper debug navmesh but still uses the previous one.

So I had to recreate a whole crowd.
It fixes this issue. But there are several other issues.

  1. Open that PG, then click somewhere. The cube will be moving. But once a new part of ground is loaded (and navmesh + crowd have rebuilt) the agent stops and continues moving.
  2. As I understand, if the agent had some animations they will be stopped too. And we will have run them manually. It will be too noticeable for the player.

And if we have a much bigger nav mesh - it may freeze the agent much longer.
What do you think @Cedric ?

Computing a navmesh is an intensive task. I suggest to bake the navmesh and load it on demand : Creating A Navigation Mesh | Babylon.js Documentation
Recreating the crow might take some time. I don’t think it’ll be as intensive as creating a navmesh. but depending on the number of agents, it can be noticeable.
Did you try to generate (& bake) the navmesh for whole world? I’m curious to see its footprint. Maybe it’s not a problem if the navmesh is not that big for the entirety of the world then you should not be concerned by recomputing portions.
Navmesh works 2 ways: as a whole/single mesh and with tiles. tiles can be recomputed to allow dynamic obstacles. I can take a look to see if the tile can be hot loaded if the first test is not a success.

1 Like

Hi @Cedric. Been testing with bigger meshes.
For some reason I could not create navmesh with a big plane mesh (1000x1000 metres, 4 verts).
But if I scale this mesh to 100x100 metres it works.
I also tried with other sizes and with more amount of vertices.

You can change mesh name in the code from “sub4.obj” to “sub5.obj” to see the error.

Recast computes a voxelization of the meshes used for computing the navmesh. Each cell size is determined by cs and ch parameters. the smaller the values, the bigger the voxel…and the memory used. If you have too small values, the memory footprint will be too big and it will fail. If you increase the world mesh size, increase cs and ch values.


Hey @Cedric Could you please clarify next nuances.

  1. Why do we need to pass maxAgents param to createCrowd()? Why it can’t be dynamic?
  2. For example, I have a map with a hundred of players on it. Server sends every 1/10s data (Vector3) of every player. So on the client I need to move their models every time I get these data. Should I do it via NavigationPlugin (agentGoto() method) or do manual calculation of their speed, acceleration and apply some animation during moving from previous position to current?

Internally, Recast preallocates an agent array with maxAgents. I guess it’s for ease of implementation et performance reason. Then, it’s up to the user to add or remove agents as long as total number of agents doesn’t exceed the limit. You can set maxAgents to the maximum possible number of players in your map even if you have only 50 on average.
Recast manages agent avoidance, speed and motion along the compute path (computed with agentGoto). Then, you can retrieve the agent speed vector and with that value, compute the agent orientation for you character mesh. Still with the speed vector, its norm is the linear speed and you can set/blend character animation based on that.

1 Like