amaclean
amaclean

Reputation: 69

Making multiple instances of an animated character in react three fiber

I'm trying to make several instances of this chicken component, but running into issues with copying skinnedMeshs. Looking through PRs, it looks like there a method, SkeletonUtils.clone, and this seems to create multiple instances of the chicken in the dom, but only the last instance shows up and has bones.

Cloning non-animated objects seems to work fine, because there's no skinnedMesh component or bones and so Threejs doesn't have to worry about trying to apply an animation to multiple meshes. I'm not attached to using GLTF, if another export works better, I'm down to try it as long as Blender exports it.

Does anyone have any ideas on how to get these other meshes to show up?

Thanks

const Chicken = ({ initPos = [-4, 5], count = 3 }) => {
  // loading a single instance of nodes, materials, animations
  // I think creating copies of actions for each chicken instance
  // will be enough, but correct me if I'm wrong
  const { nodes, materials, animations } = useLoader(
    GLTFLoader,
    "/chicken.gltf"
  );
  // storing the different chicken refs
  const [chickenRefs, setChickenRefs] = useState([]);

  useEffect(() => {
    if (chickenRefs?.length) {

      if (chickenRefs?.length >= count) {
        return;
      }

      const newChicken = SkeletonUtils.clone(chickenRefs[0].current);
      const newChickenRef = createRef();
      newChickenRef.current = newChicken;
      setChickenRefs([...chickenRefs, newChickenRef]);
    } else {
      setChickenRefs([createRef()])
    }
  }, [chickenRefs]);

  // creating the jsx for the component functionally so I can add it
  // individually to each chickenRef
  const chickenJSX = (chickenRef, idx) => (
    <group
      ref={chickenRef}
      position={[initPos[0] + idx * 3, 0, initPos[1] + idx * 3]}
      dispose={null}
      key={`chicken_${idx}`}
    >
      <group position={[0, 0.5, 0]} scale={[0.5, 0.5, 0.5]}>
        <primitive object={nodes.Body} />
        <primitive object={nodes.Bottom} />
        <primitive object={nodes.LegIKL} />
        <primitive object={nodes.LegPullTargetL} />
        <primitive object={nodes.LegIKR} />
        <primitive object={nodes.LegPullTargetR} />
        <skinnedMesh
          geometry={nodes.Plane.geometry}
          material={materials["Material.002"]}
          skeleton={nodes.Plane.skeleton}
        />
        <skinnedMesh
          geometry={nodes.Plane_1.geometry}
          material={materials["Material.003"]}
          skeleton={nodes.Plane_1.skeleton}
        />
        <skinnedMesh
          geometry={nodes.Plane_2.geometry}
          material={materials["Material.004"]}
          skeleton={nodes.Plane_2.skeleton}
        />
        <skinnedMesh
          geometry={nodes.Plane_3.geometry}
          material={materials["Material.005"]}
          skeleton={nodes.Plane_3.skeleton}
        />
      </group>
    </group>;
  );

  // mapping the refs to the jsx
  return <>
    {
      chickenRefs?.map((chickenRef, idx) => chickenJSX(chickenRef, idx))
    }
  </>;
};

Upvotes: 1

Views: 2326

Answers (1)

codeanjero
codeanjero

Reputation: 694

You might need to use clone on the nodes,

you can use the package @react-three/drei to simplify the import a bit

drei npm

here is an example from multiple instances of skinned meshes working :

https://codesandbox.io/s/react-three-fiber-wildlife-nrbnq?file=/src/Model.js

You can also use the package gltfjsx so you dont have to wory about your model file it will be generated the right way by gltfjsx :

gltfjsx npm

i'll gladly update my post if you have any additional questions

Upvotes: 3

Related Questions