How to retrieve animations of a SkinnedMesh?

I noticed that SkinnedMeshes’ animations array never contains any clips when animations are added to the object/skeleton… They don’t even get added to the root bone/object of the skeleton that the mesh is bound to. So how would I retrieve the animations of a SkinnedMesh??
I’m capable of retrieving them from the animations array if it is just a normal Mesh, but if it is SkinnedMesh, they are nowhere to be found. Not in the bones, not in the skeleton, not in the skinnedmesh… Where are they?

Attaching a list of animations to specific objects is done by certain loaders, but is not any expectation of the three.js library itself. For example, GLTFLoader returns the animation list in its top level response, and each animation could affect multiple objects:

var loader = new GLTFLoader();

loader.load('model.glb', function ({scene, animations}) {
  console.log(scene, animations);
}, onProgress, onError);

I’m not loading animations though, I am creating the clips myself. When I create them using a normal Mesh, the animations array in the mesh will automatically hold the animations, but if I use a skinnedmesh the animations are nowhere to be found once I’ve added them. I am forced to keep track of those myself.
Why doesn’t it add the animations to the arrays of the skinnedmesh or the root object like it does for the normal Mesh??
This suggests there is an inconsistency in the Three.js library itself.

I mean, I understand you can bind multiple skinnedmeshes to the same skeleton, but the animations don’t get placed into the root object, which would be the bone of the skeleton(or multiple bones). This is inconsistent from when you add the animation clip to a regular mesh, because those get automatically added to the animations array.

What functions are you using to create the animations? I’ve programmatically created animations before, and never attached the animations list to any object at all. Personally I ignore the mesh.animations property entirely, I consider it an artifact of older loaders. Certainly it isn’t needed to play back the animation.

I’m using these…

	mesh.bind( skeleton );
	this.mixer = new THREE.AnimationMixer( skeleton.bones[ 0 ] );

	tracks.push( new THREE.QuaternionKeyframeTrack( '.quaternion', [ 0, 1, 2 ], quaternionArray ) );
	tracks.push( new THREE.VectorKeyframeTrack( '.position', [0,2,4], vectorArray ) );

	clip = new THREE.AnimationClip( 'bone2stuff', 4, [ tracks[1],tracks[2] ] );
	this.mixer.clipAction( clip, skeleton.bones[ 2 ] ).play();

I saw that with regular Meshes, the animations array got updated automatically, so I thought, “Hey, I don’t have to use a variable to hold them myself since I can just keep it organized in the objects that are animated. If I need to reference the data I can just lookup the object and get it…” But no, bones don’t get automatically updated with their animations.

So you’re telling me I should ignore the animations array and create my own system to store those. Why have the animations array in the objects then and further in the documentation?..

I mean it is the first thing listed in the Object3D: three.js docs …A bone is an Object3D, so this seems like a bug, since the bones don’t get their animations listed.

The object.animations is probably less useful than you are hoping, In my opinion the property should be deprecated, but haven’t gotten around to suggesting that. Creating individual AnimationClips for every bone in a skeleton is unusual, too — usually a single AnimationClip would animate all of the bones together. This makes it much easier to, e.g., fade between a walking and running animation keeping all bones in sync; a clip can represent a meaningful thing happening, rather than a tiny part of a larger motion.

Feel free to file something on GitHub though, there may be other workflows here that I’m not familiar with.

An animation clip has to be applied on a per bone basis though, because if you have a track controlling the rotation of a bone, you don’t want all the bones rotating- you just want one of them to rotate.

The AnimationClips consist of an array of tracks, each one for a particular property of the object that needs to be transformed.

If you have a skeleton with various bones, you don’t want them to all rotate the exact same way, that’s why you have separate clips that track the properties for their corresponding bone.

ah, wait a second… maybe I can reference the bone in the track name? hmm
I’ll test that…

Yep, that’s the trick! Each track can (and often does) target a specific object within the subtree of the AnimationMixer’s root object. The track can be given an object’s name or UUID to target. For example:

tracks.push( new THREE.QuaternionKeyframeTrack( 'bone1.quaternion', [ ... ], [ ... ] ) );
tracks.push( new THREE.QuaternionKeyframeTrack( 'bone2.quaternion', [ ... ], [ ... ] ) );

clip = new THREE.AnimationClip( 'Walk', -1, tracks );

So then you’d create a single mixer for some object containing all of the bones, often the SkinnedMesh.

Thanks, that’s good to know! I just tested it, and I got it working that way now.

I also assume there should be just one mixer for one object, right? Mixers shouldn’t be used between objects (I don’t think they can be since a mixer is setup with a root object), but the wording in the documentation is a bit confusing about it, and also the fact there are methods that you can pass an optionalRoot Object other than the mixer’s default root…

The same object should not belong to more than one mixer, or animation playback will have conflicts. But it is fine for a single mixer to have a root with many descendant objects, or for the Scene itself to be the mixer root. Not a hard rule, but if you have multiple animated characters in the scene, controlled individually, I would use one mixer for each. A scene where all the animation of many objects is synchronized could probably be fine with a single mixer.

Alright, thanks for clearing that up for me- I’ll have to experiment with that then.

1 Like