Thin Instance remove by idx

Hi!
Is there an easy way to remove a thin instance by index?

var matrix = BABYLON.Matrix.Translation(-2, 2, 0);
var idx = sphere.thinInstanceAdd(matrix);
sphere.thinInstanceRemove(idx) // this is the method I miss

I had a naive thought, that sphere.thinInstanceSetMatrixAt(idx, undefined); might work, but it throws an Error.

No there is not.

The goal of thin instances is to be fast and so you don’t have easy adding/removing. To do that, you must update the buffer of matrices yourself and pass it to the underlying function.

Maybe passing a matrix with all 0 would work to remove an instance, but that would be quite an edge case and I don’t know if it would work on all GPU…

2 Likes

Thanks for the quick response and explanation!
In this case I will use plain instances which are removable.

There’s an easy way to do this - just go into the matrixData array, and overwrite the data for the instance you want to remove with the data of the last thin instance in your set, and then decrement thinInstanceCount.

4 Likes

Thanks this is really a good solution, works perfectly!

This would mean we could simply splice the element out of the array right?

matrixData.splice(instanceIndex, 16);
thinInstanceCount--;

?

I think if you did it that way with splicing then the indexes that you have cached would be incorrect, for all of the thin instances after the removed one instead of just the last one.

Hm,

I’m doing this, but both the instance in question AND the last instance in the set are being removed.

Here is the code that’s performing that, the instanceIndex is coming from a picked event.

If I don’t deincriment thinInstanceCount the expected thin Instance is ‘removed’, if I do, both the instance and the last one in the set are removed.

Am I messing this up somewhere?

EDIT: Edited with working code for future readers

    private static RemoveThinInstance(instanceIndex: number, buffer: Float32Array, prefab: BABYLON.Mesh) {
        let indexToRemove = (instanceIndex * 16);
        let indexToCopy = buffer.length - 1 - 15; // 16 items inclusive

        // Copy the last instance in the array to the spot where the instance we want to remove is
        // And then deincriment the instance count, cutting the end off
        for(let i = 15; i >= 0; i--) {
            indexToCopy = buffer.length - 1 - i;
            
            buffer[indexToRemove] = buffer[indexToCopy];

            indexToRemove++;
        }
 
       prefab.thinInstanceSetBuffer('matrix', buffer); // <--- do this instead
        //prefab.thinInstanceBufferUpdated('matrix'); <--- don't do this instead
        prefab.thinInstanceCount--;
    }

@Blake You’re right, I didn’t quite get the concept till now.

Actually, looks like I needed to set the buffer, instead of calling that it was updated.

prefab.thinInstanceSetBuffer('matrix', buffer);

instead of

prefab.thinInstanceBufferUpdated('matrix');

Using thinInstanceBufferUpdated should work, see this PG:
https://playground.babylonjs.com/#217750#48

After 2s, it will copy the 3rd sphere (green) over the 2nd sphere (red) in the matrix buffer and decrement the thin instance count.

Are you sure the buffer you update is the one you passed to thinInstanceSetBuffer in the first place?

I’m very sure that it is, if it wasn’t I would have some very weird effects happening. The behavior is consistent in that it works as expected until thinInstanceCount is deincrimented, at which point the last item is lost even though it was copied.

Setting the buffer instead of updating it is necessary for another reason as well, pick events still fire for thin instances that have been ‘removed’ when only calling thinInstanceBufferUpdated('matrix').

Here is the behavior of the scene using both methods, note how in the 2nd gif we lose both the picked instance and the last instance in the array? It’s pretty weird.

If need be I can see about trying to make a self-contained repro in another thread.

2021-11-15_NrZ9rbcFus

2021-11-15_PoEtizOh26


TIL of Float32Array.copyWithin() :man_facepalming:

You can use thinInstanceRefreshBoundingInfo() to update the bounding info of the thin instances.