Trying to play more than one animation of Blender-created .glb character on same skeleton simultaneously but failing

Hey,

Very new to Babylon. Recently learned Blender and animated my own character, which is a cat. There are different animation actions which control different parts of the body (head, main body, tail). I know being new to Blender might mean trouble from there, but I believe I exported everything fine following guides to glb. I made sure that only one animation should have control of a specific bone at a time. Animations don’t have keyframes defined (in Blender) for bones which they don’t control. In Blender, having two simultaneous animations play together in NLA looks as intended.

In Babylon, each animation action is imported as an AnimationGroup. I have read in the docs about Additive animations, animation Blending and Weighted animations. Taking a specific animation for body and head, I did everything I could to have them play together, but only managed to make one of the two play.

Merging the two AnimationGroups into one, enabling Additive animations in merged group.

const mergedGroup = new AnimationGroup(‘MergedAnimationGroup’, this.scene);

for (const targetedAnimation of bodyAnimationGroup.targetedAnimations) {
//targetedAnimation.animation.enableBlending = true;
const animation = targetedAnimation.animation;
// animation.enableBlending = true;
mergedGroup.addTargetedAnimation(animation, targetedAnimation.target);
}

for (const targetedAnimation of tailAnimationGroup.targetedAnimations) {
//targetedAnimation.animation.enableBlending = true;
const animation = targetedAnimation.animation;
// animation.enableBlending = true;
mergedGroup.addTargetedAnimation(animation, targetedAnimation.target);
}

mergedGroup.isAdditive = true;
mergedGroup.start(true, 1, 0, 500, true);

Used both .isAdditive and AnimationGroup.MakeAnimationAdditive()
This will only play only tailAnimationGroup (whichever group is added last to merged).

If, instead of merging, both animation groups are played simultaneously using .play() or .start(), only the last one will take effect, with either tail or main body stuck in default pose mode, depending of which is playing.
If I play an override animation (e.g. body) normally and make the other group additive (e.g. tail), then play, my model will disappear completely.
e.g.
const tailAnimationGroup = AnimationGroup.MakeAnimationAdditive(this.scene.getAnimationGroupByName(‘Idle_Cycle_Tail_Down’))
tailAnimationGroup.play(true);

I have also tried to make a base (override) animation and an additive animation and setting the weight of one or both animations to 1, using:
animGroup.setWeightForAllAnimatables()
as shown in Babylon.js Playground
And syncing one animation with the other using:
tailAnimationGroup.syncAllAnimationsWith(bodyAnimationGroup.animatables[0])
as shown in Babylon.js Playground

From what I understood, blending and weights do not directly concern my use-case, as blending is for interpolating from one animation to another and weights is when two or more animations control the same bones.

In How do you play two AnimationGroups on one skeleton?, I saw this answer: “If your two animation groups target different set of bones, then playing both animations would just work: the first animation group will animate the first set of bones, and the second animation group will animate the second set.”
In my case, only the latter animation to which play() is called has any influence, with the rest of the body taking the default pose. I’m not expecting for something this simple to be hard to do. Does that mean that something is fundamentally wrong with my model/animations or how they were exported?

If I don’t manage to fix this problem, the alternative would be making an animation for every possible combination of (body animation, head animation and tail animation) and that would be far from a nice way to do it. Any recommendations would be very appreciated. :slight_smile:

Thanks!

Welcome aboard!

This PG is taken from the thread you refer to:

This shows that playing back two animations that don’t target the same bones works.

In my opinion, your animations define animation data for all bones, not just those corresponding to the upper/lower part of the cat.

If you can provide us with the asset file, we can take a closer look.

Hey Evngeni,

Thanks for the swift reply!

Your comment actually gave me an idea and nudged me to the correct direction that has solved the problem, for now.
I’m not sure if this will create other problems down the line.

Basically, when I was working on my project in Blender (sometime ago), as I said, I made sure actions that played simultaneously wouldn’t control the same bones.

When I exported it out to gltf and started loading it in gltf viewers, I had noticed that - in some instances - animations that had undefined bones for some bones would inherit poses from other animations for those, causing unintended consequences. (e.g. a walking animation would cause my cat to close its eyes, even though eyelid bones were undefined for that action).

The solution I had found then was to export to .glb with the option ‘Always Sample Animation’ unchecked in Blender’s gltf exporter. This fixed the problem, as only the bones influenced by each action would move and there was no more weird additive inheritance. The problem it introduced was that rotations in the animation would sometimes be wrong (e.g. cat rotating legs full 360 degrees when walking, limps get distorted between frames. I believe it has to do with how gltf handles rotations and this setting means no interpolation. Thus, I baked the animations frame-by-frame for all involved bones as a solution to this problem. But the problem seemed to have been fixed after more work on my model and, since sampled model seemed to be more stable with its animations, I forgot about exporting an ‘unsampled’ version.

I have exported a version of my cat with no sampling enabled in .gltf to import to Babylon and now both tail and body animations play together.

I can still post my assets if you’d like to have a look or maybe update this post if I face other problems down the line as a result of this. XD

Thanks!

I don’t think we need it anymore, as you finally found the reason why it was not working!