Rotation problem while dynamically changing animations

I am trying to implement animation mirroring for a character model. The model uses an hierarchy of bones and has a skinned mesh. The models original orientations and having to deal with a hierarchy of bones gave a lot of headache while implementing the mirroring, but in the end I managed to get it working.

To change the animation dynamically I change the rotation and position of bones in the LateUpdate function of a script that is placed on the same object that has the SkinnedMeshRenderer placed on it. I almost have the mirroring of the character working. However there is a one problem I am unable to solve.

At some point during the mirroring of the animation, the root bone (in top of the hierarchy) suddenly flips/snaps making my character model twist badly in the area of the root bone. The root bone is even rotating in the wrong direction at some point.

In my script I calculate the new rotation of each bone and the rotation I calculate is correct. However when I apply this rotation to the bone, it is changed into another rotation by Unity. An example would be (pseudo code, but actual values from Debug.Log):

Vector3 mirrRot = new Vector3(267.4f, -232.4f, 189.8f); // correct mirrored rotation
Quaternion mirrored = Quaternion.Euler(mirrRot); // create a quaternion using the correct euler rotation

// lets see what the actual rotation of the root bone looks like

// mirroring should be applied over (270, 90, 0) as this is the original bone orientation
// unmirrored value: (272.6, 52.4, 189.8)
// expected value:   (267.4, -232.4, 189.8), note that -232.4 equals 127.6
// actual result:    (272.6, 307.6, 9.8), output of the Debug.Log statement above

I guess there is some quaternion magic happening here. I suspect something like (x, y, z, w) becoming (-x, -y, -z, -w) or something like that. The expected euler rotation and the actual euler rotation might be the same (I’m unsure tbh). However it totally ruins the model (causing a twist and a rotation in the wrong direction) while animating it.

All other bones in the hierarchy are working perfectly fine and are mirrored as expected (but this might be pure luck of course ;-)).

I also tried throwing overboard the euler angles and doing the entire calculation with quaternions. However the problem was exactly the same. Does anybody have a clue how I would be able to fix this? Is there any way to let Unity use the exact euler angles I calculated (and about which I know they are correct)?

These results are correct: Euler(267.4, -232.4, 189.8) and Euler(272.6, 307.6, 9.8) give the same rotation. There are several (infinite?) combinations which result in the same rotation: rotating 180 around Z plus 180 around X gives the same result as rotating 180 around Y (but it’s a stupid way to do that, for sure!). The two Euler angles you think are different actually represent the same rotation.

A commom error is to assign a rotation to some object which already was rotated from its original orientation - the new rotation replaces the older one, and the object is rotated from its original orientation, not from the present one. If you want some object to be rotated from its present orientation to a new one, you must combine the old and the new rotations:

  transform.rotation = transform.rotation * Quaternion(10, 20, 30);

This will combine the rotations (not multiply them).

I’m a complete stupid about animation, so I just can’t tell you how to rotate the bones, but the basic idea probably applies to your case.

I managed to get it working properly in the end. Switched back to using quaternions and did the mirroring in the following way:

  • Store original position of each bone in the hierarchy.
  • Calculate bone order in hierarchy (rotation will start at the root bone).
  • In LateUpdate store the current rotation of each bone.
  • Start mirroring at the root bone and work your way down to the bottom of the hierarchy.
  • Calculate difference between original and current rotation:

Quaternion fromOrigToCurr = Quaternion.Inverse(original) * current;

  • Mirror the difference rotation:

Quaternion mirror = new Quaternion(1, 0, 0, 0);

Quaternion fromOrigToMirrored = mirror * fromOrigToCurr * mirror;

  • Rotate from orignal to mirrored position:

Quaternion mirroredRot = original * fromOrigToMirrored;

This worked on either rotation or localrotation of the bones. The mirror quaternion I had to use for the bones was not exactly the same for each bone because there were some weird rotations between bones (all bones below the root bone needed another mirror Quaternion, I think it was Quaternion(0, 0, 1, 0) or so.

For all bones that had a left and right side version, I just had to mirror the rotation and place it on the bone on the other side (no need to calculate difference between original and current).

In the end it appeared that the bones also had their positions animated which I didn’t expect (I assumed bones only had a rotation, except the root bone that would move the entire skeleton). This caused some weird looking mirrored animations, so the positions of the bones had to be mirrored as well (fortunately this is much easier than mirroring rotations).