# Euler (right-handed) to Quaternion (left-handed) conversion in Unity

I'm having trouble figuring out the proper conversions from Euler angles to Quaternions. The Eulers come from BVH files, which use a right-handed coordinate system (Y is up, Z is forward) and can have any rotation order specified. The Quaternions are in Unity, which is left-handed (Y is up, Z is back).

I'm composing the Quaternions from three AngleAxis rotations, but messing up the order somewhere. So far I can't find understandable and generalized information for doing these types of conversions.

public enum AxisOrder
{
XYZ, XZY, YXZ, YZX, ZXY, ZYX, None
}

public Quaternion EulerToQuat(Vector3 eulerAngles, AxisOrder rotationOrder)
{
// I've tried various combinations of axis angles here, but would
//  like to understand what's wrong rather than brute-force
//  every combination
var xRot = Quaternion.AngleAxis(eulerAngles.x, Vector3.left);
var yRot = Quaternion.AngleAxis(eulerAngles.y, Vector3.down);
var zRot = Quaternion.AngleAxis(eulerAngles.z, Vector3.back);

switch (rotationOrder)
{
case AxisOrder.XYZ: return (xRot * yRot) * zRot;
case AxisOrder.XZY: return (xRot * zRot) * yRot;
case AxisOrder.YXZ: return (yRot * xRot) * zRot;
case AxisOrder.YZX: return (yRot * zRot) * xRot;
case AxisOrder.ZXY: return (zRot * xRot) * yRot;
case AxisOrder.ZYX: return (zRot * yRot) * xRot;
}

return Quaternion.identity;
}


...and converting translation data like so (I believe this is working):

public static Vector3 BvhToUnityTranslation(float xPos, float yPos, float zPos)
{
return new Vector3(xPos, yPos, -zPos);
}


If anyone can help me better understand how to conceptualize coordinate system conversions, and where I'm going wrong, I'd greatly appreciate it.

• Your description of the axes differs from Unity's conventions, where z+ is defined as "forward." To see this, take your left hand and point your thumb (x) right and index finger (y) up. Your middle finger (z) bent at the first knuckle should point forward in front of you, as Unity's does. Given this, there's two ways to interpret your description of BVH's right-handed coordinate system — either it has Z forward as written and X left, or X right and Z backward (opposite Unity's z convention). Can you clarify which you mean? May 1, 2017 at 19:04
• Sorry for the confusion. BVH uses Z forward, X left, and Y up. I may be misinterpreting Unity's coordinate system--are you saying it's Z forward, X right, and Y up? May 1, 2017 at 19:28

Three points we need to consider:

1. Matrices and quaternions in Unity multiply from right to left, if we think of each subsequent rotation being applied with regard to the world axes:

output space or vector = <-- transformation * <--- input space or vector


As discussed below, if BVH's "XYZ" order means "rotate about the world X axis, then the world Y axis, then the world Z axis" you'd stack your rotations in Unity right-to-left: Z * Y * X

But, it sounds like BVH's XYZ means "rotate about the local X axis, then the local Y axis, then the local Z axis" which means in Unity we'd match the order left-to-right: X * Y * Z

2. When switching the handedness of the coordinate system, all rotation angles are negated (if we keep the axis of rotation consistent)

3. The axes map up like so, based on the conversation above:

direction    Unity       BVH
----------------------------
right         x+         x-
up            y+         y+
forward       z+         z+


So we should be able to get correct conversion between the two spaces with a few small changes:

public Quaternion BvhToUnityRotation(Vector3 eulerAngles, AxisOrder rotationOrder)
{
// BVH's x+ axis is Unity's left (x-)
var xRot = Quaternion.AngleAxis(-eulerAngles.x, Vector3.left);
// Unity & BVH agree on the y & z axes
var yRot = Quaternion.AngleAxis(-eulerAngles.y, Vector3.up);
var zRot = Quaternion.AngleAxis(-eulerAngles.z, Vector3.forward);

switch (rotationOrder)
{
// Reproduce rotation order (no need for parentheses - it's associative)
case AxisOrder.XYZ: return xRot * yRot * zRot;
case AxisOrder.XZY: return xRot * zRot * yRot;
case AxisOrder.YXZ: return yRot * xRot * zRot;
case AxisOrder.YZX: return yRot * zRot * xRot;
case AxisOrder.ZXY: return zRot * xRot * yRot;
case AxisOrder.ZYX: return zRot * yRot * xRot;
}

return Quaternion.identity;
}


And:

public static Vector3 BvhToUnityTranslation(float xPos, float yPos, float zPos)
{