Search code examples
c#mathunity-game-enginethree.jseuler-angles

Convert euler angles between systems with different coordinate axis's (Unity and Threejs)


I am attempting to convert euler angle rotations between Unity and Threejs. There are two main issues with this.

Problem 1:

Unity and Threejs have different coordinate systems

Unity:

enter image description here

Threejs

enter image description here

Problem 2:

Unity does euler math in the order ZXY whereas Threejs defaults to XYZ. I have found some formulas on the Threejs side for creating Euler angles using a different order of multiplication, but I would like to know the math behind this so I can go back and forth between the two systems. I am also not sure how the different coordinate systems plays into this conversion math.

EDIT 1

I found this stack overflow post about converting a Unity Quaternion to Threejs:

Convert Unity transforms to THREE.js rotations

However, I was not able to get this code to work for going the opposite direction of Threejs to Unity which is what I need.


Solution

  • I finally found a solution to this using the links below. There may be an easier solution, but nothing else I tried gave me the intended effect. It is worth noting that this was tested with a Threejs camera that is -z facing where +y is up. My unity camera is -z facing with +y facing up. If you have a +z facing camera, which is common in Unity, simply child the GameObject to an empty GameObject and apply a 180 degree Euler rotation to the empty GameObject. This also assumes that the Threejs Euler rotation is the default XYZ ordering.

    http://answers.unity3d.com/storage/temp/12048-lefthandedtorighthanded.pdf

    http://en.wikipedia.org/wiki/Euler_angles

    http://forum.unity3d.com/threads/how-to-assign-matrix4x4-to-transform.121966/

        /// <summary>
        /// Converts the given XYZ euler rotation taken from Threejs to a Unity Euler rotation
        /// </summary>
        public static Vector3 ConvertThreejsEulerToUnity(Vector3 eulerThreejs)
        {
            eulerThreejs.x *= -1;
            eulerThreejs.z *= -1;
            Matrix4x4 threejsMatrix = CreateRotationalMatrixThreejs(ref eulerThreejs);
    
            Matrix4x4 unityMatrix = threejsMatrix;
            unityMatrix.m02 *= -1;
            unityMatrix.m12 *= -1;
            unityMatrix.m20 *= -1;
            unityMatrix.m21 *= -1;
    
            Quaternion rotation = ExtractRotationFromMatrix(ref unityMatrix);
            Vector3 eulerRotation = rotation.eulerAngles;
            return eulerRotation;
        }
    
        /// <summary>
        /// Creates a rotation matrix for the given threejs euler rotation
        /// </summary>
        private static Matrix4x4 CreateRotationalMatrixThreejs(ref Vector3 eulerThreejs)
        {
            float c1 = Mathf.Cos(eulerThreejs.x);
            float c2 = Mathf.Cos(eulerThreejs.y);
            float c3 = Mathf.Cos(eulerThreejs.z);
            float s1 = Mathf.Sin(eulerThreejs.x);
            float s2 = Mathf.Sin(eulerThreejs.y);
            float s3 = Mathf.Sin(eulerThreejs.z);
            Matrix4x4 threejsMatrix = new Matrix4x4();
            threejsMatrix.m00 = c2 * c3;
            threejsMatrix.m01 = -c2 * s3;
            threejsMatrix.m02 = s2;
            threejsMatrix.m10 = c1 * s3 + c3 * s1 * s2;
            threejsMatrix.m11 = c1 * c3 - s1 * s2 * s3;
            threejsMatrix.m12 = -c2 * s1;
            threejsMatrix.m20 = s1 * s3 - c1 * c3 * s2;
            threejsMatrix.m21 = c3 * s1 + c1 * s2 * s3;
            threejsMatrix.m22 = c1 * c2;
            threejsMatrix.m33 = 1;
            return threejsMatrix;
        }
    
        /// <summary>
        /// Extract rotation quaternion from transform matrix.
        /// </summary>
        /// <param name="matrix">Transform matrix. This parameter is passed by reference
        /// to improve performance; no changes will be made to it.</param>
        /// <returns>
        /// Quaternion representation of rotation transform.
        /// </returns>
        public static Quaternion ExtractRotationFromMatrix(ref Matrix4x4 matrix)
        {
            Vector3 forward;
            forward.x = matrix.m02;
            forward.y = matrix.m12;
            forward.z = matrix.m22;
    
            Vector3 upwards;
            upwards.x = matrix.m01;
            upwards.y = matrix.m11;
            upwards.z = matrix.m21;
    
            return Quaternion.LookRotation(forward, upwards);
        }