[SOLVED] How do we swap the geometry and material of a mesh?

I can remove the old mesh, and add a new Mesh using the new geometry. Isn’t this just as costly (or more) as the old setGeometry/setMaterial methods that there were before?


TLDR: Before you waste time reading further, it is easy to change the geometry or material of a Mesh (@mrdoob please confirm) :

// clean up the old geometry if you won't use it anymore
mesh.geometry.dispose()
mesh.geometry = new SomeGeometry(...)


// clean up the old material if you won't use it anymore
mesh.material.dispose()
mesh.material = new MeshSomeMaterial(...)

I thought it would be as easy as mesh.geometry = new SomeGeometry(...) or mesh.material = new MeshSomeMaterial(...).

I’m not sure if I did it right, but this is how a class of mine sets geometry:

    // this class does surgery on a tree, replacing a Mesh object with another Mesh object:
    // element is just an object that contains THREE stuff
    setGeometry(element, geometry) {
        console.log(' ###### GeometryBehavior setGeometry ')
        element.geometry = geometry

        const parent = element.threeObject3d.parent
        const children = [...element.threeObject3d.children]

        // remove the old Mesh
        if (children.length) element.threeObject3d.remove(...children)
        if (parent) parent.remove(element.threeObject3d)

        // add the new Mesh
        element.threeObject3d = new Mesh( geometry, element.material )
        if (parent) parent.add(element.threeObject3d)
        if (children.length) element.threeObject3d.add(...children)
    }

This seems wasteful (both CPU and Memory). Have I missed the easier way to do it (from the outside, not monkey patching instance properties, only using public APIs)?

Hmmm, maybe doing it that way is leaky? Because I’m not calling a dispose method. This is a bit too complicated (or maybe I’m doing something wrong or missing something small, I should make a small example).

Alright, does this seem right, in order not to leak memory?

    // we have to perform surgery on the scene ourselves? :(
    // element is just an object that contains THREE stuff
    setGeometry(element, newGeometry) {
        console.log(' ###### GeometryBehavior setGeometry ')
        const parent = element.threeObject3d.parent
        const children = [...element.threeObject3d.children]

        // remove the old mesh and geometry
        if (children.length) element.threeObject3d.remove(...children)
        if (parent) parent.remove(element.threeObject3d)
        element.geometry.dispose()

        // add the new mesh and geometry
        element.geometry = newGeometry
        element.threeObject3d = new Mesh( newGeometry, element.material )
        if (parent) parent.add(element.threeObject3d)
        if (children.length) element.threeObject3d.add(...children)
    }

    // we have to perform surgery on the scene ourselves? :(
    // element is just an object that contains THREE stuff
    setMaterial(element, newMaterial) {
        console.log(' ###### MaterialBehavior setMaterial ')
        const parent = element.threeObject3d.parent
        const children = [...element.threeObject3d.children]

        // remove the old mesh and material
        if (children.length) element.threeObject3d.remove(...children)
        if (parent) parent.remove(element.threeObject3d)
        element.material.dispose()

        // add the new mesh and material
        element.material = newMaterial
        element.threeObject3d = new Mesh( element.geometry, newMaterial )
        if (parent) parent.add(element.threeObject3d)
        if (children.length) element.threeObject3d.add(...children)
    }

Ah crap, I also need to copy the old mesh to the new mesh, so those lines now look like:

        element.threeObject3d = new Mesh( newGeometry, element.material )
            .copy(element.threeObject3d) // COPY!

Hmmmmm, :thinking:, apparently I was doing something wrong, because now that I try it the simple way, it just works:

   setGeometry(element, newGeometry) {
       element.geometry.dispose()
       element.geometry = newGeometry
       element.threeObject3d.geometry = newGeometry
   }
   setGeometry(element, newMaterial) {
       element.material.dispose()
       element.material = newMaterial
       element.threeObject3d.material = newMaterial
   }

So, apparently I missed something very simple, and after complicating my code with the “surgery” then converting back to simple, it just works. hehe. SOLVED

1 Like

For reference…
https://threejs.org/examples/#webgl_test_memory

This tells me I’m going to have problems animating the size of a handful of geometries this way. :smile:

My goal is to animate the size of any geometry in the scene graph without affecting children (meshes having children is useful). As a placeholder implementation for now I’ll create a new geometry for each new size, then I’ll revisit this later once I have the functionality I want.

My goal is to animate the size of any geometry in the scene graph without affecting children (meshes having children is useful).

1 Like