GLTF model with skinned meshes, animations are not playing

Hi guys.
I’m learning Three.js and thought I’ll decide to write here as I feel that I’ve hit a wall.
I’m loading a gltf (tried FBX as well) model with animations and I can’t make them to play.
Here’s a sketchfab link to the model since it’s size is too big for uploading .
I’ve read possibly every single thread and tried every solution but none of them seem to help me.
The animations play successfully in Babylon.js sandbox and the gltf viewer so it has to be something missing in my code.

Looking through the console I can see it has 3 skinned meshes, each with an assigned skeleton, I don’t know yet how to approach these.

The animations array consists of one clip with quaternion and vector keyframetracks so everything seems to be ok.

Here’s how I load the model and access it’s animations:

let mixer;
const clock = new THREE.Clock();
const gltfLoader = new GLTFLoader().setDRACOLoader(new DRACOLoader());

gltfLoader.load(
  "models/peachy_balloon/scene.glb",
  function (gltf) {
    console.log("gltf ", gltf);
    const model = gltf.scene;

    model.traverse((node) => {
      if (node.isMesh) {
        console.log("node ", node);
        model.scale.set(0.001, 0.001, 0.001);
        model.position.set(0, 2, 2);
        gltf.animations.forEach((clip) => {
          mixer = new THREE.AnimationMixer(model);
          const action = mixer.clipAction(clip);
          action.setLoop(THREE.LoopRepeat);
          action.play();
        });
      }
      scene.add(model);
    });
    tick();
  },
  undefined,
  function (e) {
    console.error(e);
  }
);

And here’s the animation loop:

const tick = () => {
  // Update Orbital Controls
  controls.update();

  // Call tick again on the next frame
  requestAnimationFrame(tick);

  // Render
  renderer.render(scene, camera);

  // Update objects
  const delta = clock.getDelta();
  mixer.update(delta);
};

I must be missing something really stupid because I have everything that should supposedly make it work and they play flawlessly on the aforementioned model viewers, but it still doesn’t play anything in my app when I load everything. I really don’t know what to do so any help is appreciated.
Sorry in advance that I can’t make any JFiddle or code sandbox with the model since it’s above 30 MBs heavy.

.traverse returns all descendants(node) of the target(model), anything you want to apply directly to the target(model), it should be outside of the traverse callback. I don’t think you’d need to access to the descendants if you are just trying to play the animation of the scene. You can try something like this:

model.scale.set(0.001, 0.001, 0.001);
model.position.set(0, 2, 2);

mixer = new THREE.AnimationMixer(model);

// Depends on how many animations you have in the .gltf
const action0 = gltf.animations[0];
const action1 = gltf.animations[1];
...

// If you want to play one of them
action0.play();

// If you want to play both animation then you have to blend them
action0.weight = .5;
action1.weight = .5;
action1.play();

scene.add(model);

This example webgl_animation_skinning_blending might be helpful

Hi, thanks for replying.

The animation is indeed at the level of a target scene itself, but accessing from there directly doesn’t really change much, it’s logged out correctly in the console but still not playing.

const gltfLoader = new GLTFLoader().setDRACOLoader(new DRACOLoader());

gltfLoader.load(
  "models/peachy_balloon/scene.glb",
  function (gltf) {
    console.log("gltf ", gltf);
    const model = gltf.scene;
    model.scale.set(0.001, 0.001, 0.001);
    model.position.set(0, 2, 2);

    mixer = new THREE.AnimationMixer(model);

    const action = mixer.clipAction(gltf.animations[0]);
    console.log("action ", action);
    action.play();
    scene.add(model);

    tick();
  },
  undefined,
  function (e) {
    console.error(e);
  }
);

It’s a single animation clip, so no blending is required, but it animates multiple SkinnedMeshes with applied skeletons in the model at once, it looks something like this:

For reference here’s the scene tree logged out by the GLTF Viewer:

Oh hmm is your tick being called correctly every frame?

++ regardless how many skinned mesh bound to the skeleton, you only need to animate the skeleton. Threejs shaders will do the skinning part for you per each skinned mesh by the animated bones matrices and skinned mesh’s skinning parameters like bone index, skin weight.

Your updated code seems alright to me and seems to be working by a quick test, just make sure if tick is being called, and also possibly if the delta that you pass to the mixer is large enough to drive the animation to be visible.

ezgif.com-gif-maker (8)

Oh hmm is your tick being called correctly every frame?

Yup, just checked and tick is correctly called every frame, also getting delta values every tick.

Although I don’t understand what you mean by “delta being large enough to drive the animation”. Delta values logged that I pass to the mixer are mostly like this:

Just tested animations on a different, much simpler model that uses regular meshes instead of skinned. The gltf loader code block is basically the same as for the first model and the animations are playing correctly. Delta values passed to the mixer are also pretty much the same as for the first model.

Yea your delta seems reasonable. Thought there might be some issue with the clock.

That’s weird. Any chance you change the materials for the skinned mesh to something else? If so, try with the one that comes with when you load the .gltf or try to change it to MeshBasicMaterial just to see if it is a shader issue. If not, I’m not sure what’s the issue. Your code seems to work for me when I tested it. Maybe you want to report the issue to threejs github.

It might be a good idea to make sure there isn’t any mistake in the code before reporting it, like try to log everything to see if anything throws null or undefined, e.g. mixer in the update loop if it points to the correct instance of AnimationMixer. or try to print one of the bone’s matrix in the update loop to see if their rotation value is animated. Hope this helps

Found the issue!

renderer.info.autoReset = false;

My renderer instance had an autoReset flag set to false on it’s info object. I used it way back when I was debugging post processing performance. I completely forgot about it and somehow it affected animation playback so when I set it to true, the animations instantly played properly!

So here’s a note for the future, when playing Skinned Mesh animations, make sure you have autoReset set to true:

renderer.info.autoReset = true;

Anyways, thanks a lot for your time and assistance!

1 Like