How do you play two AnimationGroups on one skeleton?

So I’ve rigged my character model with a single skeleton in Blender, and I’ve created two different animation actions on the skeleton which I carefully made sure targeted different bones (no bones in common between the two animations).

One animation is for the top-half of my player model, the other is for the bottom-half.

The goal here is to be able to choose an animation for the top half of the character, and one for the bottom half independently… Ie: swinging a sword with his arms, and moving or idle with his legs.

I’m instantiating my player model(s) with skeletons to the scene via:

const entries = container.instantiateModelsToScene();

And from that I have access to the following for each player model:

entries.animationGroups;
entries.skeletons;

I’m also fetching the root mesh via:

const mesh = entries.rootNodes[0] as WizardMesh;

(WizardMesh being a custom type I made, extending Mesh with a few additional functions I’m using).

Given the setup I’ve described above, how would the Babylon.js veterans around here accomplish what I’ve described?

Hope this thread is helpful. Unsure if this is the best way to go about this, but it can work

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.

This method I made for transition to a new animation, and it has been battle-tested.

/**
 * This method transitions to a new animation group with a smooth blending effect
 * @param ag the animation group to transition to
 */
    animateTransitionTo(ag: BABYLON.AnimationGroup): void {
        const WEIGHT_INCREMENT = 0.02;
        const INTERVAL_TIME = 5;
        let weightShift = 0;

        ag.setWeightForAllAnimatables(weightShift);

        if (this.blendingKey !== null) {
            clearInterval(this.blendingKey);
        }

        if (!this.assetContainer || !this.assetContainer.animationGroups) {
            log.error('No animationGroups found');
            
            return;
        }

        const endingGroups = this.assetContainer.animationGroups.filter(
            t => t.uniqueId !== ag.uniqueId && (t.isPlaying || t.name === this.activeanimname)
        );

        endingGroups.forEach(t => t.setWeightForAllAnimatables(1));

        const blendAnimation = (): void => {
            if (weightShift > 1) {
                this.blendingKey = null;
                endingGroups.forEach(t => {
                    t.stop();
                });

                return;
            }
            weightShift += WEIGHT_INCREMENT;
            endingGroups.forEach(t => {
                if (t.isPlaying) {
                    t.setWeightForAllAnimatables(1 - weightShift);
                }
            });
            ag.setWeightForAllAnimatables(weightShift);
            this.blendingKey = setTimeout(blendAnimation, INTERVAL_TIME);
        };

        this.blendingKey = setTimeout(blendAnimation, INTERVAL_TIME);
    }

I hope this can be useful for your question.

2 Likes

Hey thanks @Evgeni_Popov I’m trying this. I think I’ve got it setup properly in Blender, however when I check the entries.skeletons (entries being the result of calling instantiateToScene() on the container, it’s only showing one skeleton in the array, where I’m expecting there would be two now.

I’m currently going through my export settings in Blender to see if perhaps it’s being omitted from the actual export somehow.

If you have a single skeleton in Blender (according to what you said in your first post), you will have a single skeleton in Babylon.

Having two animations that target the same skeleton should work, as long as each animation does not animate the same bones.

No that’s what I’m saying, I’ve since taken your advice and split it into two separate skeletons both influencing the same mesh in Blender (see screenshot below), but it’s only saying I have one skeleton when I log the results of the instantiation.

@Evgeni_Popov :point_up_2:

I’ve installed the .babylon exporter in Blender and am going to try to refactor my loader to use that and see if I have better results.

I don’t know Blender, so I may be wrong, but if you have a single skeleton with two animations, and animation A updates one subset of bones and animation B updates another subset of bones with no intersection between the two subsets, then it should work. Unless Blender is generating data for the bones you’re not updating… But in that case, it’s possible to remove this data programmatically (after the animation is loaded into Babylon) to ensure that only bones that move, rotate or scale are retained in the animation.

You don’t need two skeletons, you just need one skeleton and two animation groups that control the upper and lower body.
I made a simple example by programmatically limiting two animations to the upper and lower body, and then playing them simultaneously.

eidt: reversal

4 Likes

Awesome thank you @qq2315137135 :smiley:

I need to remake my model, but I’ll try this soon as I can and report back!

Confirming this works for my use case! Thanks so much! :smiley: