Three.js is the only open-source library (I could find) to address conversion in different rotation orders. For example, let's say you have a third-party program (e.g. your 3D modeling program) with a rotation order of XZY and it exports rotations as Euler angles in degrees. The conversion to a Unity rotation is
public static Quaternion XZYToUnity( Vector3 r ) {
return
Quaternion.AngleAxis( r.x, Vector3.right ) *
Quaternion.AngleAxis( r.z, Vector3.forward ) *
Quaternion.AngleAxis( r.y, Vector3.up );
}
Notice that in Unity, quaternions are applied from left to right, opposite to (typically) rotation matrices. To be clear, the above code yields a rotation that, when you assign it to Transform.localRotation
, rotates the transform first around the local X axis, then the local Z axis, then the local Y axis, in that order. See this thread about quaternion multiplication order.
Now, for the other direction, you can find the correct case for your rotation order in the Three.js Euler source code. Converting our particular case 'XZY' to C#:
public static Vector3 UnityToXZY( Quaternion q ) {
var m = Matrix4x4.Rotate( q );
float m11 = m[ 0, 0 ], m12 = m[ 0, 1 ], m13 = m[ 0, 2 ];
float m21 = m[ 1, 0 ], m22 = m[ 1, 1 ], m23 = m[ 1, 2 ];
float m31 = m[ 2, 0 ], m32 = m[ 2, 1 ], m33 = m[ 2, 2 ];
float x, y, z;
z = Mathf.Asin( - Mathf.Clamp( m12, -1, 1 ));
if( Mathf.Abs( m12 ) < 0.9999999f ) {
x = Mathf.Atan2( m32, m22 );
y = Mathf.Atan2( m13, m11 );
}
else {
x = Mathf.Atan2( - m23, m33 );
y = 0;
}
return new Vector3( x, y, z ) * Mathf.Rad2Deg;
}
If you feed a Transform.localRotation
to this method, it gives us back the XZY Euler angles in degrees we put in the first method (ignoring the numerical error and representational non-uniqueness of Euler angles). Formally, going from Unity quaternion to XZY and back guarantees to preserve the orientation in space.