Hi all,
I’m trying my hand at animation, have run into a problem and could do with some advice.
My approach is to load character models separately from their animations, thereby avoiding the need for the animations and models to be tightly coupled (and the associated admin overhead!).
In my latest attempt I managed to:
- Load a single glb from an exported scene in blender, which includes 2 rigged character models
- Load an fbx file which includes a single animation, create an animation clip from it
- Find character models in loaded scene, create its own mixer, get the anim clip to create an action with the mixer
- Play the action
The animation works on the first model, but not the second. Reason being, threejs is renaming each bone in the second model to ‘boneName_1’ which voids the clip because the tracks are trying to find ‘boneName’. In blender, the bones for both models have duplicate names so I’m assuming threejs is doing the renaming.
At this point, I thought I’d reach out and check if this approach (separate anim clips to models) is simply not going to work. I’d rather not have to export the animations as part of the models individually, as that’s just work (esp when you want to add/remove animations). Similarly, I’d rather not start renaming bones on load as that’s verbose and error-prone.
So is my approach valid or daft? Cast your vote!
If it helps, you can go here and check the console for error and object logs.
For full code, see repo here this or I’ve pasted pertinent logic below.
// In animLoader:
private loadAnims() {
const loader = new FBXLoader(this.loadingManager);
const url = new URL("/anims/sitting.fbx", import.meta.url).href;
loader.load(url, (group) => {
// Should only be one animation present
if (group.animations.length) {
const clip = group.animations[0];
this.anims.set("sitting", clip);
}
});
}
// I'm loading in a glb file similarly, assigning gltf.scene to a map which I get here
const mainScene = this.gameLoader.modelLoader.get("my-scene-model-name");
// Create animated character objects
const { animLoader } = this.gameLoader;
const chars: AnimatedCharacter[] = [];
["bandit1", "thug1"].forEach((name) => {
// Find the character model within the main scene object
const object = mainScene.getObjectByName(name);
if (object) {
// Create its own animation mixer
const mixer = new THREE.AnimationMixer(object);
// Get the animation clips for this character
const clips = animLoader.getClips(["sitting"]);
// Create animation actions for this character from clips
const actions: THREE.AnimationAction[] = [];
clips.forEach((clip) => {
// Create a character animation action for this clip
actions.push(mixer.clipAction(clip));
});
// Add the object
chars.push({
object,
mixer,
actions,
});
}
});
Thanks for reading!