# right hand to left handed conversions

Discussion in 'Scripting' started by priceap, Mar 6, 2011.

1. ### priceap

Joined:
Apr 18, 2009
Posts:
274
hi -
I've been experimenting with some ideas to capture motion in unity and export that to maya. Currently I have been saving out rotations as euler angles into a text file, and then importing that into maya as a ".mov" format (specific to maya, and it uses euler angles).

Seeing that maya is right-handed space and unity is left-handed (the z-axis goes in opposite directions on each), I need to convert between the two. To try and get it right, I went the opposite way - exporting a .mov file from maya and then reading it into unity, so to get the scripting in unity to show the rotations correctly. Once I get that working, then reverse the process from unity to maya.

Can anyone help me out with the most direct way in unity to convert between the two? Just negating on an axis does not work right, and I have tried several approaches with lookRotation, building matrices and attempting a conversion there, but I can't seem to get it right.

thanks for any insight -

2. ### Jesse Anders

Joined:
Apr 5, 2008
Posts:
2,857
I haven't had occasion to do this myself, so I can't guarantee this is correct, but here's something you could try.

Presumably you're wanting to mirror the input across a cardinal plane (e.g. YZ). For points and vectors, this reduces to negating the appropriate component (e.g. to mirror across the YZ plane, you'd negate the X component).

For rotations, it seems mirroring the axis of rotation across the plane and negating the rotation angle would have the desired effect. (Negating the other two components of the axis instead would yield equivalent results, but is a little less intuitive.)

Although the real and imaginary parts of a quaternion that encodes a rotation aren't the same as the angle and axis, they are related to them, and negating the appropriate components of the quaternion should have the same effect. For example, to mirror a rotation in quaternion form across the YZ axis, you'd negate the x and w components.

You could still send the data back and forth using Euler angles of course, while using quaternions to perform the actual left-to-right conversion.

This may not work, or it may in fact be the standard way of doing it. (Not having had to do this myself, I can't say for sure either way.)

3. ### priceap

Joined:
Apr 18, 2009
Posts:
274
thanks for the reply Jesse -
I have tried negating an axis on the rotation, and it did not seem to work. After reading your reply, I decided to start over with a more clearly defined test. I built a scene in Maya with animation and imported it into unity. I also imported the scene without any animation. I then exported the text format motion files out of maya, and run my script in unity to read the motion files and map them to the same objects.

For proper position, I have to invert transform.position on x. Then everything translates in the way it does compared to the direct import from maya.

To try the inversion of quaternion rotation, I take the euler angles from the exported maya text file:

Code (csharp):
1. var inputRot = Vector3( parseFloat(lineEntry[i]), parseFloat(lineEntry[i+1]), parseFloat(lineEntry[i+2]) );
2. var rot = Quaternion.Euler( inputRot );
3. var changedRot = Quaternion( rot.x , -rot.y, rot.z , rot.w );
When I do this, objects will appear to rotate properly, but only if they are rotating along the x y OR z axis. If they rotate on more than one axis, the rotation result appears wrong. In my earlier test I was doing articulated motions like an arm bending, and some rotations looked right but then would 'swing' in strange ways. This test makes it more clear. No combination of inverting the quaternion elements gets it to look correct, including negating the w.

To try to illustrate, I did a screen cap video. The green objects are imported from maya with animation. The gray ones are running the script reading the text files. Once the Y axis is inverted on the rotations (second half of video), the three objects on the left rotate correctly, but the two on the right do not.

Last edited: Mar 6, 2011
4. ### Jesse Anders

Joined:
Apr 5, 2008
Posts:
2,857
What I was suggesting was negating both w and the component corresponding to the axis along which you're mirroring. So, if you're mirroring along the x axis (i.e. across the yz plane), you would do this:

Code (csharp):
1. var changedRot = Quaternion( -rot.x , rot.y, rot.z, -rot.w );
Have you tried that?

5. ### priceap

Joined:
Apr 18, 2009
Posts:
274
[ I edited this again because I made a mistake in the negating on the Y example, but the rotations are still not correct - also changed the video in the earlier post to reflect this ]

Quaternion( -rot.x , rot.y, rot.z, -rot.w ) results in the left three rotating correctly but the two right objects still rotate wrong.

I made positive or negative multiplier variables and exposed them in the inspector to try just about every logical and illogical variation of negating the components.

Edit 2:
I tested this out in a separate unity scene with a "from" cube and a "to" cube.

"to cube".rotation = Quaternion( -rot.x, rot.y, rot.z, -rot.w) creates a perfect mirroring of rotation (reflection on YZ plane). You are correct.

However, it appears there is something else going on with euler angles coming from right hand coordinate system and being read into a left-hand coordinate system, because reading the euler angles from maya is not behaving the same way.

Last edited: Mar 6, 2011
6. ### priceap

Joined:
Apr 18, 2009
Posts:
274
Rotation order appears to be a factor.

Unity says: "a rotation of eulerAngles.z degrees around the z axis, eulerAngles.x degrees around the x axis, and eulerAngles.y degrees around the y axis (in that order)."

Maya's default rotation order is XYZ, but you can change it in the attribute editor for any transform.

I reversed my process and captured rotations in unity and imported that into maya, where I had to invert the x for position, and inverted the y and z axis rotation by scaling their values by -1.0. The result then had the same kind of 'swinging' that resulted from going from maya to unity. I changed the rotation order for the object in maya from XYZ to ZXY, and then the rotations matched the original just fine.

Going from unity to maya was what I had in mind in the first place, but I thought I could figure out how to do the conversion by first converting values coming from maya inside unity. Now I see I can convert the data in Maya more easily, but I would like to make it a streamlined process so the data that comes out of unity will just work in Maya.

I can invert the x positions and y an z rotation when recording the values in unity, but how would I convert what the rotation values should be if compensating for the ZXY > XYZ rotation orders?

Last edited: Mar 7, 2011
7. ### Jesse Anders

Joined:
Apr 5, 2008
Posts:
2,857
Yeah, I didn't mention it directly, but the method I proposed is of course predicated on the Euler-angle conversions being performed correctly in the first place.

You can construct a quaternion from a set of Euler angles in any order you wish in Unity; just create a quaternion for each rotation and multiply them together in the proper order. As such, you should be able to perform the conversion entirely in Unity if you wish.

(Again though, note that I haven't actually used the method I'm proposing in practice, so I can't guarantee its correctness.)

8. ### priceap

Joined:
Apr 18, 2009
Posts:
274
thanks for your help on this -
I am not confident on how to create a quat for each rotation. I am assuming this means a quat for each of the x y and z axis rotations independent of each other.

When looking at matrix conversion examples, I can see how there is a vector representing each, but it also looked like that is equivalent to transform.forward, right, and up - is that correct? I tried a test with that without good results. Do I need to first convert the euler angles to a rotation matrix to get each angle, or are the direction vectors a way to do it? In short, what is the right way to create a quat for each rotation?

9. ### Jesse Anders

Joined:
Apr 5, 2008
Posts:
2,857
Given an Euler-angle triple (x, y, z), one way to construct a quaternion representation is as follows (untested C#):

Code (csharp):
The axis order is determined by the order in which x, y, and z appear in the last statement; you can re-arrange these however you need to in order to get the right results.

When specifying Euler-angle axis order, there can sometimes be confusion with respect to whether the order represents the order in which the rotations are applied, or the order in which the multiplication occurs. For example, using column vectors or standard quaternion multiplication order, the rotation order x->y->z would actually be written as z * y * x.

However, that shouldn't be a problem, since there are only two options for each order. If Maya's default order is XYZ, I'm willing to bet that specifies the rotation order, in which case z * y * x is what you want. If that doesn't work though, try x * y * z.

10. ### priceap

Joined:
Apr 18, 2009
Posts:
274
Thanks again!
I tried this out, but not getting good results. The object rotates nicely on one axis at a time, but again when I rotate it to something like 30 deg on both x and z and then spin it on local Y, the recorded result flips all around in maya.

In case I interpreted the example wrong, here is how I used it:

Code (csharp):
1. var reOrdered = ZXYtoXYZ( Vector3(recordObj.eulerAngles.x, -recordObj.eulerAngles.y, -recordObj.eulerAngles.z) );
2.
3. function ZXYtoXYZ(v : Vector3)
4.    var qx = Quaternion.AngleAxis(v.x, Vector3.right);
5.    var qy = Quaternion.AngleAxis(v.y, Vector3.up);
6.    var qz = Quaternion.AngleAxis(v.z, Vector3.forward);
7.    var qq = qz * qy * qx;
8.    return qq.eulerAngles;
9. }
I tried both (qz * qy * qx) and (qx * qy * qz)

The "reOrdered" result is then put in an array and written out to a text file for importing into maya.

If I do not use the ZXYtoXYZ function and just use the eulerAngles ( x, -y, -z) and then in maya manually switch from xyz to zxy rotation order it works. I get the idea for how your suggestion can work, but I must be doing something wrong.

11. ### Jesse Anders

Joined:
Apr 5, 2008
Posts:
2,857
Well, there's a lot going on there, and I can't really say for sure where the problem is.

But, with the method I'm proposing at least, you shouldn't be negating any of the angles in the Euler-angle representation.

Importing from Maya to Unity would go something like this:

2. Convert them to quaternion form using the method I described earlier
3. Mirror the quaternion as described earlier

Theoretically at least, that should leave you with a quaternion that correctly represents the orientation.

Going from Unity to Maya is trickier because Unity's quaternion-to-Euler conversion may not perform the conversion using the order that Maya expects. You can certainly write custom code for this, but it's not entirely trivial. Anyway, the Unity->Maya conversion would then look like this:

1. Mirror the quaternion
2. Convert the quaternion to Euler angles using Maya's conventions
3. Write out the Euler angles

In either case though, I'm pretty sure you don't want to be negating the Euler angles *and* mirroring the quaternion (if that's what you're doing).

Again though, most of this is speculative; to be confident in the method I'm proposing, I'd have to actually implement the whole thing myself to make sure it worked.

12. ### priceap

Joined:
Apr 18, 2009
Posts:
274
I did try leaving off the negation of the euler angles before to see what effect that would have (no better). But I was not mirroring the original quat on top of that. So I brought back the quat mirroring to see if that would work.
It still does the crazy flipping when rotating on a compound axis, but at least for now I have the manual maya reordering solution working. I will keep at it to see if I can understand it better. Thanks for all your helpful suggestions. I am learning from it.

Code (csharp):
1. var mirrorQuat = Quaternion( -recordObj.rotation.x, recordObj.rotation.y, recordObj.rotation.z, -recordObj.rotation.w);
2. var reOrdered = ZXYtoXYZ( mirrorQuat.eulerAngles) ;
3.
4. function ZXYtoXYZ(v : Vector3)
5.    var qx = Quaternion.AngleAxis(v.x, Vector3.right);
6.    var qy = Quaternion.AngleAxis(v.y, Vector3.up);
7.    var qz = Quaternion.AngleAxis(v.z, Vector3.forward);
8.    var qq = qz * qy * qx;
9.    return qq.eulerAngles;
10. }

13. ### Jesse Anders

Joined:
Apr 5, 2008
Posts:
2,857
Although I'm just guessing, the problems you're still seeing may be due to Unity using a different order for the Euler-angle conversion than Maya expects. (I'd have to dig into it myself to say for sure though.)

14. ### guavaman

Joined:
Nov 20, 2009
Posts:
2,665
I need to do the opposite of you -- import rotations from Maya into Unity.

Update: SOLUTION FOUND ... see below under Update.

(Note: This code here does not work properly. See below for working code.)
Code (csharp):
1.
2. var qRot : Quaternion = Quaternion.Euler(mayaRotation); // Convert Vector3 output from Maya to Quaternion
3. var mirrorQuat : Quaternion = Quaternion(-qRot.x, qRot.y, qRot.z, -qRot.w); // Mirror the quaternion on X  W
4. var reOrderedEulers : Vector3 = XYZtoZXY(mirrorQuat.eulerAngles); // Reorder XYZ (maya) to ZXY (unity)
5. theObject.transform.localEulerAngles = Vector3(reOrderedEulers.x, reOrderedEulers.y, reOrderedEulers.z);
6. theObject.transform.Rotate(new Vector3(0, 180, 0));
7.
8. static function XYZtoZXY(v : Vector3) : Vector3 {
9.    var qy : Quaternion = Quaternion.AngleAxis(v.y, Vector3.up);
10.    var qq : Quaternion = qz * qx * qy;
11.    return qq.eulerAngles;
12. }
In some instances, it comes close, but not always. Obviously Unity must do this when importing maya files as animations come in fine.

Update: Okay after spending the better part of two days on this from converting eulers to rotation matrices, writing axisangle and matrix3 classes, scaling, writing a matrix3 to quaternion converter, poring over papers and a zillion different approaches, I threw it all out and went back to this system. The solution was simpler than I thought (isn't it always?):

To convert Euler rotations from Maya (Right-handed XYZ to Unity left-handed ZXY):
1) Invert rotation's y and z signs: x, -y, -z
2) Convert to ZYX

I'm actually terrible with matrix math so maybe I'm not understanding it properly, but I had to modify my code to qz * qy * qx. Once I did that it worked perfectly. To me this sounds like ZYX, not ZXY like all the docs say.

My working code:
Code (csharp):
1.
2. static function MayaRotationToUnity(rotation : Vector3) : Quaternion {
3.    var flippedRotation : Vector3 = Vector3(rotation.x, -rotation.y, -rotation.z); // flip Y and Z axis for right->left handed conversion
4.    // convert XYZ to ZYX
5.    var qx : Quaternion = Quaternion.AngleAxis(flippedRotation.x, Vector3.right);
6.    var qy : Quaternion = Quaternion.AngleAxis(flippedRotation.y, Vector3.up);
7.    var qz : Quaternion = Quaternion.AngleAxis(flippedRotation.z, Vector3.forward);
8.    var qq : Quaternion = qz * qy * qx ; // this is the order
9.    return qq;
10. }
11.
Thanks to this forum post for the simple y, z flip left->right conversion.

I hope this helps someone.

Last edited: May 25, 2011
SuppleTeets likes this.
15. ### LKIM

Joined:
Feb 17, 2011
Posts:
40
Thanks, this really helped us out. We had a script which spit out SDK definitions from a Maya Rig which imported backwards into Unity.

The code above, along with flipping the translate X from + to - made them work.

16. ### iinsia

Joined:
Oct 13, 2011
Posts:
1
Can you explain why you don't negate rotation.x for flipped rotation? The direction of rotation reverses on all 3 axis between the RHS and LHS right?

17. ### QuantumRyan

Joined:
Aug 30, 2011
Posts:
11
Thanks to this thread I realized what I was doing wrong.

My solution goes like this:
given a right handed position and orientation, you can convert it to Unity left handed this way.

Code (csharp):
1. flipPosition = new Vector3(-position.x, position.y, position.z);
2. flipRotation = Quaternion.Inverse(rotation);

CharlieBrown likes this.
18. ### SuppleTeets

Joined:
Aug 22, 2014
Posts:
9
Thanks! It helped me!

Last edited: Sep 13, 2017