How to wait until cloning Group with BufferGeometry in it completes

I am pre instantiating clones of Groups with BufferGeometries in them in order to use them later in our game.
However, when I load the page it is initially blank and I created a loading screen for it. Now I need to find out when I need to stop displaying the loading screen and switch to displaying the canvas.

What I am currently doing is first creating a group with, e.g. a cube and a point cloud in it.
I then create a buffer and clone all the objects as well as adding them to the scene in a position below the floor.

// create a single group
const particleCount = 50;
const spawnable = new THREE.Group();
const cube = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshBasicMaterial({color: 0xff0000}));
const bufferGeometry = new THREE.BufferGeometry();
const pointBuffer = new Float32Array(particleCount * 3);
for (let i = 0; i < particleCount; i++) {
    pointBuffer[i*3] = Math.random();
    pointBuffer[i*3+1] = Math.random();
    pointBuffer[i*3+2] = Math.random();
}
const positionAttribute = new THREE.BufferAttribute(pointBuffer, 3);
positionAttribute.setUsage(THREE.StreamDrawUsage);
bufferGeometry.setAttribute('position', positionAttribute);
const particles = new THREE.Points(bufferGeometry, new THREE.PointsMaterial({color: 0x0000ff}));
spawnable.add(cube, particles);

// create and fill buffer
const buffer = [];
for (let i = 0; i < 50; i++) {
    buffer.push(new Promise((resolve) => {
        const bufferedObject = spawnable.clone(true);
        bufferedObject.position(0, -10, 0); // move object below floor
        scene.add(bufferedObject);
        resolve(bufferedObject);
    });
}

Now buffer should hold Promises that should, in my limited experience, resolve when each bufferedObject is ready.
However I still receive a blank page for significantly longer when the page loads with the buffered groups than if I do not buffer the groups.
What am I missing?

Is there a reason why you’re using async Promise? cloning the groups is synchronous process, and yes, it may block the UI if the groups are too heavy.

You could:

  1. display your loading page (preferably without animation).
  2. start the cloning process (the UI may get blocked)
  3. once the process finished, display your objects
  4. remove the loading page
//1. Set the loading page
//2. start the cloning process
const particleCount = 50;
//...
for (let i = 0; i < 50; i++) {
    const bufferedObject = spawnable.clone(true);
    bufferedObject.position(0, -10, 0); // move object below floor
    scene.add(bufferedObject);
    buffer.push(bufferedObject);
}

//3. display your objects
//4. remove the loading page

Thank you for your reply.

It was suggested to me by ChatGPT to fix my problem and somehow it made loading a slight bit faster but did not solve the issue.

That’s exactly my problem, I don’t know when to do this, because I don’t know when loading is finished. Do I just have to wait an arbitrary amount of time? To me that does not seem like a clean solution.

So I’ve tried GTP4o, and got the same response with the asyc Promise solution.

  • Q:
    You’re using new Promise((resolve)), can you explain why? Creating the points is not an async process!
  • Y:

    the use of a Promise and setTimeout is a way to break up the execution, allowing the browser to render the UI and avoid blocking it. This approach leverages JavaScript’s event loop to ensure the browser can update the UI

I’ve tested the two approaches, with a counter to test the UI responsiveness, and one million points (Had to go up to 100 millions to crash the browser :grin:).

createSpherePoints(POINTS_COUNT);
document.querySelector("#loading").style.display = 'none';
createSpherePointsAsync(POINTS_COUNT).then(() => {
  document.querySelector("#loading").style.display = 'none';
});

To be honest, I didn’t notice much of a difference. I’ll stick with the simple straightforward solution, and if I really care about the UI responsiveness, I’d use web-workers.

2 Likes

Thanks for the extensive testing. I reverted to not using Promises and implemented an arbitrary load time instead of trying to wait for cloning to finish.