Jump to content

Streaming models for large environment


Luigi
 Share

Recommended Posts

Hello everybody,

we need to publish a large urban environment (150Km2) on-line and we really like the look that you can achieve in Babylon compared to alternatives like Cesium.

My question is, is it possible to have small tiles (0.25-1Km2) streamed from a server (AWS/Azure/GCloud) whenever the user gets within a certain distance?

 

Many thanks,

Luigi

Link to comment
Share on other sites

Thank you Deltakosh for your reply.

Our environment is made up of accurate 3d models of buildings and land. Will we be able to stream those too on-demand from a server?

If so, where should we start? I know it's a broad question, but I mean what do I need to publish those models and make them stream from an AWS server?

Link to comment
Share on other sites

An environment can be broken into tiles or chunks. I've only done this in two dimensions, but given that a realistic map exists almost entirely along the horizontal plane I think the same logic from a 2D game can apply.

In 2D games it is common to have tiles, and then we can convert the player's x,y coordinates (x,z if in 3d) to find the tile coordinates. The tiles are just in a 1D or 2D array. Usually its a 1D array because javascript arrays are 1D naturally and some simple math can turn x,y into an index within the array. Some more math can then load the tiles that are +/- 100 units away from the player (or whatever, depending on the view).  This approach can work for fairly large maps, but at some point the maps are so big that the idea of having the whole map in a single array in memory won't work.

When these maps get truly massive, we can no longer just have them in memory as an array... instead we need something conceptually similar to pagination, where we only work with a finite section of data that comes from something much larger or even infinite. In games this is sometimes called chunking. Minecraft popularized this term. A very large tile map (just an example) can be divided up into chunks that are 32x32 tiles. Converting the player's coordinates to chunk coordinates is just a matter of dividing their x,y by the size of a chunk (e.g. 32 tiles x 16 pixels, or 0.25 km). After we have the chunk coordinates we can load or generate the chunk the player is in, as well as any neighboring chunks up until we feel we have enough map to satisfy the view distance. In 3D this is going to be a similar process, but perhaps there are some cooler things at our disposal such as LoD.

I don't know enough about babylon to guess at chunk sizes or what the general constraints would be.

 

Link to comment
Share on other sites

thank you ALL for the pointers.

timetocode yes I am familiar with chunking, in-fact I use it in Unreal Engine. The issue now is, how to actually implement it in BabylonJs?

 

I mean where would you put the logic in?

In the player, so depending on the player's current coordinates you load/unload certain chunks/tiles?

 

Alternatively, would it be possible to have, say, a bounding box (maybe by creating a simple transparent box) for each chunk/tile. Make it larger than its contents. When the player enters that box, it triggers a little loading script for that chunk/tile?

That's one of the ways it works in Unreal Engine... would it work here as well? And if so, how do I do it?

 

Thank you,

Luigi

Link to comment
Share on other sites

Hi @Luigi and a belated welcome from me. First of all a word of warning - the example below is a proof of concept and it might not be even the concept you are looking for and could still have bugs. Secondly the loading is slow and the transition between 'chunks' is slow, please be patient.  More warnings - you must click on scene to activate the use of the keyboard however moving the scene with your mouse will throw out the movement directions. (So lots to improve). 

Use left arrow to rotate camera left and right arrow to rotate it right. Up arrow moves camera forward, down arrow moves it backward. When the camera moves out of the central section meshes in loaded blocks are moved or disposed of and fresh scenes loaded into the blocks in front. (Hold the up arrow down for a while and wait for the loading screen).

https://happy-hamilton-38ec27.netlify.com/

https://github.com/BabylonJSGuide/largemaps

TL;DR

The large map is split into 25 scenes in a 5 by 5 grid, each scene contains a ground and a SPS of randomly sized and placed boxes which are all given the same random colour. There are 9  blocks arranged in a 3 by 3 grid. These blocks contain the 'active' map sections. Moving forward and crossing from one block to the next will trigger the re-placement of scenes within the blocks, the disposal of those at the back and the loading of new scenes at the front.

Lots of things to consider:

Larger blocks would mean less transitions between blocks but longer loading times.

Perhaps not disposing of scenes at the 'back' could prevent re-loading if direction reversed.

Possible way forward could be to load 25 blocks, but with the outer 16 not enabled (ie only the inner 9 blocks viewable) transitioning between blocks would in addition to the current processes just enable some blocks and since the scenes to be loaded are now two blocks away they could be loaded in the background without interrupting the current view. This is what I think I will play with next.

How to optimise the process generally?

Link to comment
Share on other sites

This would be cool if you shifted it to an async process JohnK.

"Perhaps not disposing of scenes at the 'back' could prevent re-loading if direction reversed."
I would just disable them, until they are like double or triple distance from the inner ring.

Link to comment
Share on other sites

19 minutes ago, Pryme8 said:

This would be cool if you shifted it to an async process

This could be possible if the 'loading' scenes were far enough away. As it is when I tried an async process my row and column pointers got all out of sync because you could transition into a block that had not finished loading. I expect with more thought you could async generally and force a sync only when absolutely necessary. 

I think that enabling, disabling and loading two or three times distance from the inner ring and only those blocks directly in front of or behind the current viewing direction is the way to go.

I will keep playing when I get the chance. It is an interesting challenge and insights and views very welcome.

Link to comment
Share on other sites

set your ring out a little farther and have them start caching into a container object that has their keys as a location identifier.   Then toggle active/inactive with a basic distance calc and maybe even turn the distance calc into an AABB to keep the ring as a box.  Then keep the objects loaded even if they are out of site and obfuscated by fog, disable them if the bounding box is out of the frustum and dispose them only if they are double your distance threshold and then make sure you delete the key and value out of the container object for housekeeping.

Also maybe have a variable that keeps track of how many tasks are being pulled at the moment,  then maybe limit it and have a "stack" of loading task to parse through when ever the limit count variable is under the set value.

Link to comment
Share on other sites

On 5/25/2018 at 5:52 PM, JohnK said:

Hi @Luigi and a belated welcome from me. First of all a word of warning - the example below is a proof of concept and it might not be even the concept you are looking for and could still have bugs. Secondly the loading is slow and the transition between 'chunks' is slow, please be patient.  More warnings - you must click on scene to activate the use of the keyboard however moving the scene with your mouse will throw out the movement directions. (So lots to improve). 

Use left arrow to rotate camera left and right arrow to rotate it right. Up arrow moves camera forward, down arrow moves it backward. When the camera moves out of the central section meshes in loaded blocks are moved or disposed of and fresh scenes loaded into the blocks in front. (Hold the up arrow down for a while and wait for the loading screen).

https://happy-hamilton-38ec27.netlify.com/

https://github.com/BabylonJSGuide/largemaps

TL;DR

The large map is split into 25 scenes in a 5 by 5 grid, each scene contains a ground and a SPS of randomly sized and placed boxes which are all given the same random colour. There are 9  blocks arranged in a 3 by 3 grid. These blocks contain the 'active' map sections. Moving forward and crossing from one block to the next will trigger the re-placement of scenes within the blocks, the disposal of those at the back and the loading of new scenes at the front.

Lots of things to consider:

Larger blocks would mean less transitions between blocks but longer loading times.

Perhaps not disposing of scenes at the 'back' could prevent re-loading if direction reversed.

Possible way forward could be to load 25 blocks, but with the outer 16 not enabled (ie only the inner 9 blocks viewable) transitioning between blocks would in addition to the current processes just enable some blocks and since the scenes to be loaded are now two blocks away they could be loaded in the background without interrupting the current view. This is what I think I will play with next.

How to optimise the process generally?

 

Thank you John that's very interesting and looks promising. It's something similar to what I'm trying to do. 

Link to comment
Share on other sites

@Luigi and @Pryme8 further developments, but still a long way to go. Use this example with the provisos below https://happy-hamilton-38ec27.netlify.com/mapim15

Give time for blocks to load. Click to use keys. Use keys slowly one press at a time (otherwise no time for blocks to be loaded). You start facing North, use left and right arrow keys to turn, only move forward (or backward) when facing north, east, south or west (NE etc as yet gets out of phase because of  loading) There are now 225 blocks alternating between black and white ground and boxes and spheres. A grid of 7 x 7 blocks is loaded at any one time with a 3 x 3 grid enabled. Loading method changed from `append` to `import`. Need to give even more thought to which blocks need loading at any point.

As this challenge interests me I will keep playing. Any suggestions for anybody welcome.

 

Link to comment
Share on other sites

 

Had that problem sometimes. I think it is to do with moving forward before the loading blocks have finished loading. Try single one press on up arrow then pause before another quick tap on key. Not a satisfactory method and am looking at ways around it.

Could mean going back to a loading screen during loading and making user wait until all blocks are loaded.  Then maybe rather than loading new blocks every time you cross into a new block load a larger number of blocks when you get near the edge of the loaded blocks. Since Javascript is sngle threaded there will always be a hexitation when new blocks are loaded. Just depends on the way you choose to deal with it.

On the other hand it mght be some other bug I missed.

Link to comment
Share on other sites

Reverted and simplified somewhat. Now only uses one buffer not three and that is integrated into the core blocks, so a bit more like the original method. Can move in any direction but still with the same warning as before. Next stage is to see if I can smooth out the loading by breaking it up into sections that occur during the forward motion between the trigger points for resetting the blocks. Will be away for a week so nothing new soon.

https://happy-hamilton-38ec27.netlify.com/workercoreblocks22

Link to comment
Share on other sites

  • 2 weeks later...

Have you tried to simulate multi-threading, and have a recursive "stack" function that has an IO count limit.

Basically an array of items that need to be parsed, a timeout function that fires the function every set number of milliseconds to check a secondary array that has the items that are currently being parsed, once it sees the item is flagged as done, it splice it out of the second array, drop your IO count by one.  

Then when the function fires again it will see that there are items in the Array of needs to be parsed (and  the system is under the IO count{I usually limit to like 5}) and splices items out of it and places them into the processing array until there are no items left.

If you have trouble with this concept I can upload a working example for you to reference.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...