Bones - Freeze transformations

Is there a way to freeze transformations on a bone?

My use case:

My character rig’s rest pose does not match up with rest pose of the input animation.
I want to rotate my rigs rest pose around the it’s Y axis to line up with the rest pose of the input data.

I tried passing a local matrix with the desired rotation to the skeleton root bone’s constructor and a matrix with zeroed rotation as the rest pose matrix but that did not work.

I have a working solution where I managed to create a new skeleton from the rotated skeleton but it feels quite hacky.

Is there an easier way to do it?

Hey! do you mind reproducing the question in the Playground? there are some options (like forcing a call to return to rest, or cancelling animations but it will be easier with a repro)

I’m working on it, but I’ve run into an other issue while doing it.

@Deltakosh here’s a Playground that shows what I’m trying to do.

It works, but it ain’t pretty. The only way I managed to get the desired result was by copying my skeleton and modifying it in steps.

Gray (srcSkeleton): Input data

Gray (rigSkeleton): This is the rig from Maya, it’s inside the purple skeleton so you can’t see it. The pose doesn’t match the input data so I can’t use it directly.

Purple (rigCopy): Copy of rigSkeleton. The way I copy it, I guess the transformation of the root bone is frozen.

Yellow (rotatedSkeleton): Rotated copy of rigCopy. This is aligned with the rest pose of the input data. I did not manage to just rotate an existing skeleton without messing something up so I came up with this method.

Green (targetSkeleton): Again, frozen version of rotatedSkeleton. This drives the mesh.

This all seems a bit crazy and it’s probably my lack of understanding how things work that led me to this complicated solution.

If anyone happen to come up with ways to simplify it I’m all ears :kissing_smiling_eyes:

UPDATE

This Playground shows the rest pose of the input data.

pinging @Cedric and @Evgeni_Popov for help :slight_smile:

Is it not possible to provide a simplified repro which would only show the problem?

The repro you provided is quite complicated as you create multiple skeletons and manually modify bones and animations… I don’t think you need to do that to repro (?)

@Evgeni_Popov that’s easy!
Check out this one.

The interesting lines are

hips.rotationQuaternion = new BABYLON.Quaternion.FromEulerAngles(0, Math.PI, 0);

// Freze somehow?
//hips.freezeTransformations();

// If frozen correctly, this should not reset the skeleton.
hips.rotationQuaternion.set(0, 0, 0, 1);

UPDATE 1
Maybe it’s possible with setRestPose. I will do some tests…

UPDATE 2
I would have thought the following should work.

  1. Orient bone as desired.
  2. Get current local matrix.
  3. Use matrix to update rest pose.
  4. Set all rotations to zero.

Expected result: Bone oriented as desired in new rest pose.

But it doesn’t seem to work that way:

    hips.rotationQuaternion = new BABYLON.Quaternion.FromEulerAngles(0, Math.PI, 0);

    var m = hips.getLocalMatrix();
    hips.setRestPose(m);

    hips.rotationQuaternion.set(0, 0, 0, 1);

mySkel.bones.forEach(b=>{
b.setRestPose(b.getLocalMatrix())
})

https://www.babylonjs-playground.com/#308TR3#1

We had a argument for this on the SkeletonViewer updates at one point but that was not the correct place for it to live. Maybe need to add a method on the skeleton class that is skeleton.setCurrentPoseAsRest(), or something like that?

That seems nice to have to me.

Added it, will be pushing in after my dumb long build process…
We have it for the whole skeleton and also per bone now.

Thanks @Pryme8.

It’s just that hips.setRestPose(hips.getLocalMatrix()) doesn’t really work. Or perhaps I’m just confused since mySkel.returnToRest() does not assume the new rest pose, but instead reverts to the original pose.

Is it a bug?

Im confused now?

It seems to work for me in the PG above, after setting the rotation back to Identity after setting the restPose keeps its in the same position its in.

I guess I dont know what you are trying to accomplish?

Ohh I see what you are saying, thats odd the returnToRest should work.

@Evgeni_Popov
https://www.babylonjs-playground.com/#308TR3#3

Why does this work

hips.updateMatrix(hips._restPose.clone(), false, false);

but this does not?

hips.returnToRest()

@Pryme8

Another weird thing I noticed is that bone.getLocalMatrix() has side effects. I’m not sure if it’s by design or not but it was kind of suprising to me.

As shown here
https://www.babylonjs-playground.com/#308TR3#4

Whoa; No I do not think using a getter should modify the scene.

That is kinda wonky, looking into it now.

1 Like

https://www.babylonjs-playground.com/#308TR3#6
I think its was just a sequence thing.

If you get rid of the comment out of the getLocalMatrix you will notice it works the way I think you intend it to?

inside the method Babylon checks if the local matrix is dirty or before returning it which if it was dirty requires and update.

Maybe I am still not doing what you want with the rotations though.

Maybe it’s OK, I’m not sure, I just noticed it and was a bit suprised.

It’s unrelated to my main bone issue though, ie setting a new rest pose/freezing transformations. It’s like the rest pose is not updating properly after calling setRestPose.

You are right sir. I honestly dont know what is going on would need someone else to step in at this point.

1 Like

The call to hips.rotationQuaternion = new BABYLON.Quaternion.FromEulerAngles(0, Math.PI, 0); record the new quaternion and mark the underlying as dirty but does not actually updates it. The update will be done when a call on the bone instance requires the matrix to be up to date: getLocalMatrix() is one of these calls.

As the skeleton viewer does not use any call that would require the matrix to be recomputed, you see the bone in a position that does not take into account the change of the quaternion when you don’t call getLocalMatrix() and you see the change when calling it.

@Pryme8 Maybe the skeleton viewer should start by making sure all matrices are up to date by calling getLocalMatrix on each bone?

@Evgeni_Popov I see, thank you for the explaining that, it makes sense to me now!