three-instanced-mesh
three-instanced-mesh copied to clipboard
Add/Remove objects from existing instance?
How would I go about adding or removing a given index from an already instantiated InstanceMesh? Can I just add/remove items from the list, increment/decrement the numInstances, and set needsUpdate()?
I tried without any success, so I am curious too.
I think I did something like this in my editor, I'll have to check my code.
Le mer. 5 juin 2019 à 13:34, Barnabás BARTHA [email protected] a écrit :
I tried without any success, so I am curious too.
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/pailhead/three-instanced-mesh/issues/17?email_source=notifications&email_token=AEN4HALWSSIE2M62BKTKRJLPY6QETA5CNFSM4HMS2VQ2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODW7NTUI#issuecomment-499046865, or mute the thread https://github.com/notifications/unsubscribe-auth/AEN4HALIDW5J3XZV4JI6C63PY6QETANCNFSM4HMS2VQQ .
It looks as if I could modify numInstances only once. If I increase it by one when I want to add a new object, only one instance will appear. But if I increase numInstances by 10, than 10 will appear, no more. Is it possible, that something updates only once after changing numInstances? @pailhead @PERECil
I had to extend the numInstances set function in index.js with the following line and it works fine now :)
this.geometry.maxInstancedCount = v;
I had to extend the numInstances set function in index.js with the following line and it works fine now :)
this.geometry.maxInstancedCount = v;
I am curious too! Can you share your code?
There is no much to share. Only thing I had to do is adding that one line.
Do you want to remove instances? Or only render the first N instances?
I'm not sure how to tackle this, my thinking was that you'd "disable" the instance, by setting it's scale to 0 for example, moving it behind the camera etc. If you're rendering a cube for example, i think it'd be faster to do that than mess with the buffers:
attribute float aInstanceVisible;
void main(){
//...
if(aInstanceVisible == 0.){
gl_Position = vec4(0.,0.,-2.,1.);
return;
}
//...
}
Adding makes more sense i think. Shrinking slightly less, it'd be hard to remove every odd instance, since you'd have to reorganize the entire buffer right? Adding individual instances would also make less sense than say adding 10 and disabling 9 for a time. With this being said, would it make more sense to have an intuitive way to merge and split this geometry? Say create 3 InstanceMesh
out of one, and combine them back together?
moving it behind the camera etc
I'm doing similar, just calling setPositionAt(index, Number.MAX_SAVE_INTEGER, Number.MAX_SAVE_INTEGER, Number.MAX_SAVE_INTEGER)
to "disable" it.
What about if addObject/removeObjectAt
methods do the trick where the removed "object" gets placed to the end of the array, and the one at the end of the array gets moved to where the removed one was. Similar to the following trick on a normal array, but on the array buffer:
// overwrite the item to be removed with whatever is currently the last item:
array[indexToRemove] = array[array.length - 1]
With the array buffer version, as long as the number of objects is less than the size, we can add and remove them that way.
I'd also think that add
can take a special parameter like shouldGrow
that when set to true, will automatically grow the buffer (it would dispose
the attribute from the GPU, equivalent to calling geometry.dispose()
with regular geometries), so that the next render would re-input it to the GPU using the new length.
What would be even more awesome, if instead of having an array, had a tree structure, much like an Object3D tree, but the root node of this tree would handle composition into the array buffer automatically. This would make it more similar to the Three.js scene graph API that everyone is familiar with, that way we wouldn't have to worry about indices.
Maybe those objects could all extend from Object3D, just so they have that easy-to-use position/rotation API, etc, then when updateMatrixWorld (or similar) gets called on an root object like InstancedMeshGroup
or similar, it would cause the object to update world matrices in the subtree, and then compose all the values into the array buffer like you have here in InstancedMesh
. The subobjects in the subtree would not have material
or geometry
, those would be on InstancedMeshGroup
(or whatever the name would be).
By default, it could automatically grow to accommodate the size of the meshes in the subtree. By default, removing items from the subtree would not shrink the root object's array buffer. Some options could be in place to configure this behavior.
I really like this idea, I think it could provide great benefit (though more code to maintain). It could be cool, as some of the instanced meshes could be children of other instanced meshes in the subtree, for the convenience of being able to organize transforms between parents and children, etc.
Maybe, even other non-instanced meshes can be in the subtree, and those would render like normal, and the InstancedMeshGroup
would simply ignore those. This would make composition with regular scene much more easy.
This sounds like an interesting approach, but is something that could be built on top of this. Dunno though.
If matrices are computed in the shader, then you wouldnt need the entire object3d, but yeah, using the position.set
interface would be super convenient.
Something like shouldGrow: chunkSize
is super interesting. You give it a starting size of whatever, and let it grow by say 10 instances if you ever exceed the number in the graph. Still it could all built on top of this?
I think it could be built on top of this, but I think it'd require a tiny change in WebGLRenderer
. If something like MeshInstance
(or whatever it would be called) is effectively similar to Mesh
but being only a data container with geometry and material, then the renderer would need to not render these objects, because instead the renderer will render the InstancedMeshGroup
(which may contain inside of it an InstancedMesh
, not inside of its children
but on a new property, or something so it doesn't interfere with the user's tree).
It would be possible that InstancedMeshGroup
when traversing the tree would set visible = false
on the MeshInstance
s, then this would allow the renderer not to render items, but this interferes with the user's view of the tree. So we'd need a small change in WebGLRenderer so that the renderer simply treats these objects like Object3D, and allows all the work to be done by InstancedMeshGroup
. This way the user's view of the tree can be untouched (they can set visible
to false
if they wish, and that instanced mesh should not be rendered).
I like this idea, I'd like to try it at some point.
Maybe before then, we should start making separate PRs to add features from here onto Three.js' InstancedMesh
?
I haven't gotten around to trying to make the InstancedMeshGroup
idea (but I think a better name might be InstancedMeshRoot
) because I'm unfortunately not paid to work on anything related to it yet (I wish!).
But I took a look at sinclairzx81
's instanced-mesh and regarding end usage some parts are more similar to Three.js APIs than setPositionAt
:
const mesh = new InstancedMesh(4096, geometry, material)
for(const instance of mesh.instances) {
instance.position.x = (Math.random() * 16) - 8
instance.position.y = (Math.random() * 16) - 8
instance.position.z = (Math.random() * 16) - 8
}
Each instance has .rotation
, .position
, .scale
. That's a nice bit.
Three's own InstancedMesh uses the array approach, and one example wants to use the convenient Vector3
parts, which leads to some ugly code like this:
function resampleParticle ( i ) {
sampler.sample( _position, _normal );
_normal.add( _position );
dummy.position.copy( _position );
dummy.scale.set( scales[ i ], scales[ i ], scales[ i ] );
dummy.lookAt( _normal );
dummy.updateMatrix();
stemMesh.setMatrixAt( i, dummy.matrix );
blossomMesh.setMatrixAt( i, dummy.matrix );
}
I suppose the array approach uses less memory, but the dev experience is less convenient.
may be, set instancemeshid matrix sacle to (0, 0, 0) simultate remove operation