Copy animationGroup with skeleton

Hi team,

I am in a scenario where I have identical armatures meshes, I am thinking if its possible to clone skeleton and animationGroup of one mesh to another, for example I am loading an armature’s gltf model like this, please see comments at last here I am not sure how to copy animation group and start playing ?:

var firstArmatureMesh = false;
 //below I just get the mesh from first task, it doesn't have skeleton/animations
const firstPerson = assetsManager.addMeshTask('firstPerson', '', '/assets/', 'firstPerson.glb');
   firstPerson.onSuccess = (task) => {
      firstArmatureMesh = task.loadedMeshes[0];
  }

// next I need to copy skeleton and animations to first
const secondPerson = assetsManager.addMeshTask('secondPerson', '', '/assets/', 'secondPerson.glb');
secondPerson.onSuccess = (task) => {

let skeleton = task.loadedSkeletons[0];
let secondArmatureMesh = task.loadedMeshes[0];
let animations = task.loadedAnimationGroups;

// here I am playing second animation, which works fine
animations[0].stop();
animations[1].play(true);

 // here I have check if first armature model is loaded and copy skeleton and animations to it
  if(firstArmatureMesh){
    firstArmatureMesh.skelton = skeleton.clone("copy");
  // here I am not sure how to copy animation group and start playing, something like below ?
    firstArmatureMesh.animations = animations.clone("copy");
  }
}

Thanks

Edit:
I found about cloning animation group, but it doesn’t effect anything.

        let idle = animations[0].clone('idle01', (target) => {
            let idx = firstArmatureMesh.skeleton.getBoneIndexByName(target.name)
            return firstArmatureMesh.skeleton.bones[idx]
        })
        
        let walk = animations[1].clone('walk01', (target) => {
            let idx = firstArmatureMesh.skeleton.getBoneIndexByName(target.name)
            return firstArmatureMesh.skeleton.bones[idx]
        })
        walk.play(true);

PG: https://playground.babylonjs.com/#AR6TLI#3

1 Like

The clone function is the way to go, the targetConverter is where you will retarget the animations. Make sure you are returning the correct target.

in gltf, the bones are not the target of the animations (yeah I also find that dumb, but this is how gltf was designed). The real target of animations are transformNode linked to the bones. From a bone you can find its control TransformNode with bone.getTransformNode()

1 Like

Thanks boss but I am still struggling to figure this out, I tried to use getTransformNode but seems my mind is not helping here, I have created a PG if you could please update with example :bowing_man: https://playground.babylonjs.com/#AR6TLI#3

Thanks

Fyi battling the same battle here: https://forum.babylonjs.com/t/loading-character-with-animations-to-scene-and-cloning-it-with-animation-groups :slight_smile:

@Panuchka boss @Deltakosh is missing :upside_down_face:, lets hope he come back soon or if some other lord here would be able to help
@sebavan :bowing_man: any help here?
Thanks

1 Like

It looks like your mesh structures are completely different, you do not have any transform node on the cloned one.

1 Like

Thanks, exactly, they are two different models, is there any way I can copy skeleton and animations to other model?

I do not think so as at least their structure should be the same to ensure the animation could be retargeted.

Adding @Drigax who did an amazing job at that recently.

1 Like

Thanks for the mention Seb,

@Dshah_H in this playground, I retarget by cloning the animation group, then for each targetedAnimation, I set the target to be the driving node in the skeleton that I want the animation track to control. In this example, I search by name, since both the source skeleton and the target skeleton had matching hierarchy. However, you can define your own mapping and use that instead, if the skeletons aren’t identical.

One thing that isn’t taken into account would being able to set some offset in the animations to account for if the skeletons differ in convention (i.e: does each bone point +Y?)

        function retargetAnimationGroup(animationGroup, targetSkeleton)
        {
            //console.log("Retargeting animation group: " + animationGroup.name);
            animationGroup.targetedAnimations.forEach((targetedAnimation) => {
                 const newTargetBone = targetSkeleton.bones.filter((bone)=>{return bone.name === targetedAnimation.target.name})[0];
                //console.log("Retargeting bone: " + target.name + "->" + newTargetBone.name);
                targetedAnimation.target = newTargetBone? newTargetBone.getTransformNode() : null;
            });
        }
3 Likes

Thank you very much @Drigax
I tried to replicate that but no success https://playground.babylonjs.com/#AR6TLI#4
while I could see in console logs its retargeting to correct bones, but as you notice in PG I am cloning skeleton, I’m definitely missing something… do I must need to have skeleton for firstPerson mesh?

It looks like you have a gltf, one.glb that contains mesh, that is rigged and animated with a skeleton.

The second gltf, two.glb contains a second mesh, but no skeleton.

I’m assuming you want to be able to take the animations from one.glb and apply them to the mesh in two.glb, right? This is going to take a bit more preparation of your assets, as the mesh in two.glb needs to be rigged and skinned in order to be animated. In the playground you’re only cloning the skeleton of one.glb, and assigning it to two.glb’s mesh, but this isn’t enough. the mesh of two.glb has no idea how its vertices should move in respect to the bones of the other skeleton.

I’d recommend rigging and skinning the mesh in two.glb, then defining some mapping of how you want to bones of two.glb’s skeleton to match up to the bones used in one.glb’s skeleton, and using that in your retargeting function as I hinted to before.

Hopefully this helps.

2 Likes

All you said is correct, I was expecting skeleton to auto fit/righ/skin with new meshes, actually working with BababylonJS makes me think that it can do anything :joy: , well here I should do some manual work. Thank you for your explanation, it has started to make sense, I will try it asap and post a result here.

2 Likes

Hi there, @Deltakosh:

I’m interested in your statment “The clone function is the way to go”.

In case we have an animation intended only to be “attached” to a mesh, is not a more “clean” approach to only retarget it? I mean, something like:

for (var j = 0; j < result.animationGroups[i].targetedAnimations.length; j++) {
    result.animationGroups[i].targetedAnimations[j].target = _targetMesh.getChildren()[0];
}

Thanks for your time.

1 Like

This is totally acceptable :slight_smile:
My answer was more in a general fashion

1 Like