Optimized Loading System For Generating an entire Planet

Hello!
For those who have helped me with my newbie problems with my voxel game, you know what I will be talking about! Thanks to @Leonidas, @yaustar, & @LeXXik, I have come far.

I will not talk much on my game, but what I will say is that I will be ambitious. Cubic voxels are cubes… so why should not the world they make up also be a cube? Yes, I am talking about making a world with 6 sides, something that many have never done before.

That part is not what I will need a loading system for, but the biome system instead. Because of the scale of the planet, the biome calculation system would be much more complex than Minecraft’s blobby and incoherent system. How can I make a loading system that breaks up the world loading across multiple frames? JavaScript is single threaded, which is horrible.

My idea was that I could break up the loading into separate functions, and use window.requestAnimationFrame() at the end of each one to call the next in line. Think of it as daisy-chaining the functions until all the necessary data is computed. Would requestAnimationFrame work in this way?

Example:

function stepOne(){
    //Do stuff
    window.requestAnimationFrame(stepTwo);
}

function stepTwo(){
    //Do more things
    window.requestAnimationFrame(stepThree);
}
//Etc…

Hi @Kurios,

Yes, a system where you offload/split your calculations along a number of frames is a common way to approach this. If what you are asking is to stop rendering your application and render only when it’s required e.g. after a biome has finished processing, then you can use the following method:

// disable the Application from rendering automatically
this.app.autoRender = false;

Now use the following property to render a single frame on demand:

function stepOne(){
    //Do stuff
    this.app.renderNextFrame = true;
}

function stepTwo(){
    //Do more things
    this.app.renderNextFrame = true;
}
//Etc…
1 Like

This may just work! I will test this out!

By the way, if you are looking for ways to offload calculations away from the main thread that is rendering, you can give web workers a try.

They come with their own set of problems but potentially can be a solution: Web Workers API - Web APIs | MDN

But one thing I just realized, how would I chain those functions?

No, those properties can’t be chained, they will just signal PlayCanvas to render a single frame at the next iteration of the main loop and that’s it.

I think what you are looking for is to offload calculations over a number of frames. That usually requires splitting your workload in smaller chunks and executing them in small batches per frame. The main goal being to avoid calculating something for longer than 16.67ms and keep your frame rate steady e.g. at 60FPS.

Or an alternative is the web workers I shared above.

1 Like

Yes, I think I will check those out. Thank you for your help!

1 Like

Looking forward to this project!

I would look into webworkers, as Leonidas mentioned. They allow to process multiple chunks of your world in parallel. Spreading your workload over frames on the other hand is a single threaded process.

Keep in mind that depending on how much is processed during a single frame will affect the total time you need to process your world. You want to profile using Chrome dev tools (performance tab) to see if you are within 0.16 ms budget, which will give you 60 FPS. However, different devices have different processing capabilities, so some lower end devices might go over budget, doing the same stuff. You’d need to find a balance between shorter loading times and responsiveness of your app. Using webworkers eliminates the application lock, as they don’t block the main thread, where the app is running.

Distributing your task over frames is easier, than using webworkers, so you might want to try that first. Apart from example that Leonidas suggested, another option is to use the update method of your script, which runs once per frame:

// pseudo code

function stepOne() {...}
function stepTwo() {...}

const steps = [stepOne, stepTwo];
let index = 0;

update(dt) {
    const step = steps[index++];
    if (step) step();
}

2 Likes