Animating GLB files via R3F

Hi there,

As the title mentions - I’ve been attempting to animate a GLB model, but after many hours of reading and trying different things, I’ve made no progress.

What I’ve Done

  • Exported my GLB from Blender.

  • Placed the GLB in my public folder.

  • Ran GLTFXJS via npx to create a JSX version of my model:

export default function Model({ ...props }) {
  const group = useRef()
  const { nodes, materials, animations } = useGLTF('/Heartv2.glb')
  const { actions } = useAnimations(animations, group)

  console.log(animations);
  console.log(actions);

  useEffect(() => {
    console.log(actions)
    actions.EyeMovement.play()
  }, [actions]);

  return (
    <group ref={group} {...props} dispose={null}>
      <group name="Scene">
        <group name="Lattice" position={[0.99, 2.69, 0.56]} scale={0.6} />
        <group name="Lattice001" position={[0.99, 2.69, -0.56]} scale={0.6} />
        <group name="EyeMovement" position={[9.05, 2.61, 0]} scale={1.46}>
          <primitive object={nodes.Bone} />
          <primitive object={nodes.Bone001} />
          <primitive object={nodes.Bone002} />
        </group>
        <group name="Armature005" position={[6.36, 2.69, 0]} rotation={[0, 0, 0.79]} scale={1.61}>
          <primitive object={nodes.Bone_1} />
          <group name="Armature001" position={[0, 0, 0.35]} scale={0.62}>
            <primitive object={nodes.Bone_2} />
          </group>
          <group name="Armature003" position={[0, 0, -0.35]} scale={0.62}>
            <primitive object={nodes.Bone_3} />
          </group>
        </group>
        <mesh name="Body" geometry={nodes.Body.geometry} material={materials.Material} position={[0, 1.8, 0]} rotation={[-Math.PI / 4, 0, 0]} />
        <group name="Eye" position={[0.99, 2.69, 0.56]} rotation={[1.57, -0.01, -0.07]} scale={0.96}>
          <mesh name="Sphere" geometry={nodes.Sphere.geometry} material={materials.Eyeball} />
          <mesh name="Sphere_1" geometry={nodes.Sphere_1.geometry} material={materials.Iris} />
        </group>
        <mesh name="Eyelid_Bottom" geometry={nodes.Eyelid_Bottom.geometry} material={materials.Eyelid} position={[0.99, 2.69, 0.56]} rotation={[0, 0, -0.79]} />
        <mesh name="Eyelid_Top" geometry={nodes.Eyelid_Top.geometry} material={materials.Eyelid} position={[0.99, 2.69, 0.56]} rotation={[0, 0, 0.79]} />
        <group name="Eye001" position={[0.99, 2.69, -0.56]} rotation={[1.57, -0.01, 0.07]} scale={0.96}>
          <mesh name="Sphere006" geometry={nodes.Sphere006.geometry} material={materials.Eyeball} />
          <mesh name="Sphere006_1" geometry={nodes.Sphere006_1.geometry} material={materials.Iris} />
        </group>
        <mesh name="Eyelid_Bottom001" geometry={nodes.Eyelid_Bottom001.geometry} material={materials.Eyelid} position={[0.99, 2.69, -0.56]} rotation={[0, 0, -0.79]} />
        <mesh name="Eyelid_Top001" geometry={nodes.Eyelid_Top001.geometry} material={materials.Eyelid} position={[0.99, 2.69, -0.56]} rotation={[0, 0, 0.79]} />
      </group>
    </group>
  )
}

useGLTF.preload('/Heartv2.glb')
  • Created a simple scene and rendered the model:
const Scene = () => {


    return (
        <Layout>

            <Divider padding="12.5rem 0 4rem 0" tabletPadding="8rem 0 0 0">
                <Container>
                    <Canvas
                        style={{ height: '100vh', width: '100vw' }}
                        camera={{
                            aspect: 2,
                            far: 1000,
                            fov: 40,
                            near: 0.1,
                            position: [10, 5, 0]
                        }}
                    >
                        <OrbitControls target={[0, 1, 0]} />
                        
                        <Stage preset="rembrandt" intensity={0.2}  environment="sunset">
                            <Model />
                        </Stage>

                        <color attach="background" args={["white"]} />
                    </Canvas>
                </Container>
            </Divider>

        </Layout>
    )
};

export default Scene;

The Issue

While I am able to see and interact with the JSX model in the canvas element, none of the animations play. When I log the animations variable I can see the two animationClip (correct number of animations) objects. However, when I log the actions variable (once outside of a useEffect and once within a useEffect, as per the code above) I see two empty objects. When I open up the empty objects in the console output I can see details of the two animations (EyeMovement and EyelidMovement).

I’ve gone through about 20 different examples on Codepen and can’t see any difference between my code and the code there. I’m hoping somebody here may have an inkling to what’s going on here?

Many thanks in advance!

Update

  • Somebody recommended checking that the GLB file animations are working via a model viewer. I tested this and unfortunately, despite the animations showing in the model viewer, the animations do not run there. So it appears this issue is due to my blender export, as opposed to the above code.

Update 2

  • The issue is that the animations I was using were based on tracking armatures, as opposed to moving meshes. I’m not sure if it is possible to export tracking animations, so may need to re-write them by moving the meshes directly.

Duplicate the absolute timeline positional meshes of your armiture poses as seperate meshes and use them as pose morph attributes of an original state driving mesh.

actions are a side effect, logging them out before the model is being rendered out (by react) is futile, they become available in useEffect. here’s an example: GLTF Animations - CodeSandbox

keep in mind, “useAnimations” is just a little helper that creates clips and a mixer for you from the gltf data. nothing stops you from doing this your self as you always did in threejs, though.