Need help with r3f + morphTargets

Hi there,

I am trying to create a R3F component (called Door) that can have different scales defined by its prop:

<Door width={2} />

The prop is used in the component to calculate the morphTargetValue:

const meshRef = useRef();
  useEffect(() => {
    const morphTargetValue = (width - widthDefault) / (widthMax - widthDefault);
    meshRef.current.morphTargetInfluences[0] = morphTargetValue;
  }, [width]);

that works fine if i instantiate the component once. But if i instantiate multiple Doors with different width values all get the width value of the last instance… why?

      <group position-x={-2}>
        <Door width={2} />
      </group>

      <group position-x={2}>
        <Door width={3} />
      </group>

Here is the full Door component code:

import { useGLTF } from "@react-three/drei";
import { useEffect, useRef } from "react";

const widthDefault = 2;
const widthMax = 3;

export const Door = ({ width }) => {
  const { nodes } = useGLTF("./morphTest.glb");

  const meshRef = useRef();
  useEffect(() => {
    const morphTargetValue = (width - widthDefault) / (widthMax - widthDefault);
    meshRef.current.morphTargetInfluences[0] = morphTargetValue;
  }, [width]);

  return (
    <group dispose={null}>
      <mesh
        ref={meshRef}
        name="Cube"
        castShadow
        receiveShadow
        geometry={nodes.Cube.geometry}
        material={nodes.Cube.material}
        morphTargetDictionary={nodes.Cube.morphTargetDictionary}
        morphTargetInfluences={nodes.Cube.morphTargetInfluences}
      />
    </group>
  );
};

Here is a codesandbox providing the problem:
https://codesandbox.io/p/sandbox/morphtest-6z7jqq

Greetings Tom

The sandbox is not working

what does it say? for me the link works…

NVM it started working

I did, sorry the visibility was wrong, here is the correct link!

https://codesandbox.io/p/sandbox/morphtest-6z7jqq

Why are you not simply changing the scale?

Good point:D but it is an abstraction of a component where simple scaling is not an option!

Screenshot 2024-07-09 at 5.51.40 PM

morphTargerInfluences and morphTargetDictionary are both set to default.

Not for me :thinking:
grafik

you need to clone the gltf date, both doors refer to the same data model, thats fundamentally how loader hooks work. there’s also a useGraph hook in r3f which gives you nodes/materials back

const { scene } = useGltf(url)
const clone = useMemo(() => scene.clone(), [scene])
const { nodes, materials } = useGraph(clone)

not 100% sure if clone() will duplicate morphTargets, if not try clone(true). given that clone works each door would now have its own set of morph targets which can be altered separate from the other instances.

@drcmda thanks for providing the solution!
Does this mean that if i instantiate this component 3 times i’ll get 3 scene graphs? Performance wise this would be worse than just scaling it right, because there i am using 3 times the same graph?

Here is the updated codesandbox:
https://codesandbox.io/p/devbox/morphtest-6z7jqq?file=%2Fsrc%2FComponents%2FExperience.jsx%3A12%2C25

im guessing you use morphtargets to avoid stretching? if you can scale on the mesh, of course, that would be preferable.

yes, i do it to avoid stretching. So i just have to keep in mind that it creates a new scene graph every instance. Thanks

i dont know morphtargets well enough but you might be able to re-use the geo + materials, and just use the cloned morph targets on the mesh. this is essentially a threejs problem, maybe someone else can help.

Although it’s not r3f oriented, the official example of instanced morph targets may be of some help here too…