How to assign Matrix4x4 to Transform?

I have a calculated matrix, and I need to instantiate a new object with the exact same transformation as the matrix describes.

Do I really have to extract position, rotation, and scale values from the matrix, or there is a nice and simple way to assign the whole matrix to Transform, which I haven’t found yet?

[deleted]

Thanks to all the people in this thread.
I’ve updated my solution. The previous one didn’t work well if non-unit/non-uniform scale was involved.

using UnityEngine;

public static class TransformExtensions
{
    public static void FromMatrix(this Transform transform, Matrix4x4 matrix)
    {
        transform.localScale = matrix.ExtractScale();
        transform.rotation = matrix.ExtractRotation();
        transform.position = matrix.ExtractPosition();
    }
}
using UnityEngine;

public static class MatrixExtensions
{
    public static Quaternion ExtractRotation(this 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);
    }

    public static Vector3 ExtractPosition(this Matrix4x4 matrix)
    {
        Vector3 position;
        position.x = matrix.m03;
        position.y = matrix.m13;
        position.z = matrix.m23;
        return position;
    }

    public static Vector3 ExtractScale(this Matrix4x4 matrix)
    {
        Vector3 scale;
        scale.x = new Vector4(matrix.m00, matrix.m10, matrix.m20, matrix.m30).magnitude;
        scale.y = new Vector4(matrix.m01, matrix.m11, matrix.m21, matrix.m31).magnitude;
        scale.z = new Vector4(matrix.m02, matrix.m12, matrix.m22, matrix.m32).magnitude;
        return scale;
    }
}

Now let’s test:

        var position = new Vector3(11, 22, 33);
        var rotation = Quaternion.Euler(10, 20, 30);
        var scale = new Vector3(1, 2, 3);

        Debug.Log("Before:");
        Debug.Log(position);
        Debug.Log(rotation);
        Debug.Log(scale);

        var m = Matrix4x4.TRS(position, rotation, scale);

        position = m.ExtractPosition();
        rotation = m.ExtractRotation();
        scale = m.ExtractScale();

        Debug.Log("After:");
        Debug.Log(position);
        Debug.Log(rotation);
        Debug.Log(scale);

819280--218392--Test.PNG

10 Likes

Just for future viewers. I had some problems with the above method to get rotation (but thanks for sharing it alex, it got me on the right track), something to do with division by zero caused by 180 degree rotations or something like that. so I got this off of Martin John Bakers maths pages EuclideanSpace.com. I havent tested it much yet but so far more luck with it.

public static Quaternion MatrixToQuaternion(Matrix4x4 m)
{
float tr = m.m00 + m.m11 + m.m22;
float w, x, y, z;
if(tr>0f){
float s = Mathf.Sqrt(1f + tr) * 2f;
w = 0.25fs;
x = (m.m21-m.m12)/s;
y = (m.m02-m.m20)/s;
z = (m.m10-m.m01)/s;
} else if( (m.m00 > m.m11) && (m.m00 > m.m22) ){
float s = Mathf.Sqrt(1f+m.m00-m.m11-m.m22) * 2f;
w = (m.m21-m.m12)/s;
x = 0.25f
s;
y = (m.m01+m.m10)/s;
z = (m.m02+m.m20)/s;
} else if(m.m11 > m.m22){
float s = Mathf.Sqrt(1f+m.m11-m.m00-m.m22) * 2f;
w = (m.m02-m.m20)/s;
x = (m.m01+m.m10)/s;
y = 0.25fs;
z = (m.m12+m.m21)/s;
} else {
float s = Mathf.Sqrt(1f+m.m22-m.m00-m.m11) * 2f;
w = (m.m10-m.m01)/s;
x = (m.m02+m.m20)/s;
y = (m.m12+m.m21)/s;
z = 0.25f
s;
}

Quaternion quat = new Quaternion(x, y, z, w);
//Debug.Log("Quat is " + quat.ToString() );
return quat;
}

Use code tags.

This is the version I use:

        public static Quaternion MatrixToRotation(Matrix4x4 m)
        {
            // Adapted from: http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
            Quaternion q = new Quaternion();
            q.w =Mathf.Sqrt(Mathf.Max(0,1+ m[0,0]+ m[1,1]+ m[2,2]))/2;
            q.x =Mathf.Sqrt(Mathf.Max(0,1+ m[0,0]- m[1,1]- m[2,2]))/2;
            q.y =Mathf.Sqrt(Mathf.Max(0,1- m[0,0]+ m[1,1]- m[2,2]))/2;
            q.z =Mathf.Sqrt(Mathf.Max(0,1- m[0,0]- m[1,1]+ m[2,2]))/2;
            q.x *=Mathf.Sign(q.x *(m[2,1]- m[1,2]));
            q.y *=Mathf.Sign(q.y *(m[0,2]- m[2,0]));
            q.z *=Mathf.Sign(q.z *(m[1,0]- m[0,1]));
            return q;
        }

Found here:

Since everyone is sharing, here is the version I use:

/// <summary>
/// Extract translation 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>
/// Translation offset.
/// </returns>
public static Vector3 ExtractTranslationFromMatrix(ref Matrix4x4 matrix) {
    Vector3 translate;
    translate.x = matrix.m03;
    translate.y = matrix.m13;
    translate.z = matrix.m23;
    return translate;
}

/// <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);
}

/// <summary>
/// Extract scale 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>
/// Scale vector.
/// </returns>
public static Vector3 ExtractScaleFromMatrix(ref Matrix4x4 matrix) {
    Vector3 scale;
    scale.x = new Vector4(matrix.m00, matrix.m10, matrix.m20, matrix.m30).magnitude;
    scale.y = new Vector4(matrix.m01, matrix.m11, matrix.m21, matrix.m31).magnitude;
    scale.z = new Vector4(matrix.m02, matrix.m12, matrix.m22, matrix.m32).magnitude;
    return scale;
}

/// <summary>
/// Extract position, rotation and scale from TRS matrix.
/// </summary>
/// <param name="matrix">Transform matrix. This parameter is passed by reference
/// to improve performance; no changes will be made to it.</param>
/// <param name="localPosition">Output position.</param>
/// <param name="localRotation">Output rotation.</param>
/// <param name="localScale">Output scale.</param>
public static void DecomposeMatrix(ref Matrix4x4 matrix, out Vector3 localPosition, out Quaternion localRotation, out Vector3 localScale) {
    localPosition = ExtractTranslationFromMatrix(ref matrix);
    localRotation = ExtractRotationFromMatrix(ref matrix);
    localScale = ExtractScaleFromMatrix(ref matrix);
}

/// <summary>
/// Set transform component from TRS matrix.
/// </summary>
/// <param name="transform">Transform component.</param>
/// <param name="matrix">Transform matrix. This parameter is passed by reference
/// to improve performance; no changes will be made to it.</param>
public static void SetTransformFromMatrix(Transform transform, ref Matrix4x4 matrix) {
    transform.localPosition = ExtractTranslationFromMatrix(ref matrix);
    transform.localRotation = ExtractRotationFromMatrix(ref matrix);
    transform.localScale = ExtractScaleFromMatrix(ref matrix);
}


// EXTRAS!

/// <summary>
/// Identity quaternion.
/// </summary>
/// <remarks>
/// <para>It is faster to access this variation than <c>Quaternion.identity</c>.</para>
/// </remarks>
public static readonly Quaternion IdentityQuaternion = Quaternion.identity;
/// <summary>
/// Identity matrix.
/// </summary>
/// <remarks>
/// <para>It is faster to access this variation than <c>Matrix4x4.identity</c>.</para>
/// </remarks>
public static readonly Matrix4x4 IdentityMatrix = Matrix4x4.identity;

/// <summary>
/// Get translation matrix.
/// </summary>
/// <param name="offset">Translation offset.</param>
/// <returns>
/// The translation transform matrix.
/// </returns>
public static Matrix4x4 TranslationMatrix(Vector3 offset) {
    Matrix4x4 matrix = IdentityMatrix;
    matrix.m03 = offset.x;
    matrix.m13 = offset.y;
    matrix.m23 = offset.z;
    return matrix;
}
6 Likes

Very cool!
I’m a little curious though: if the matrix has both scale & rotation, won’t the rotation mess up the scale? or does the 4th component compensate for it somehow?

Also, your version uses the matrix row-by-row, and all 4 components, whereas alexzzzz does it column-by-column, using only the first 3 components:

… so who’s right :slight_smile: ?

That particular implementation of ExtractScaleFromMatrix was expanded since it performed slightly better than the following when I profiled it:

  • // Extract new local scale
  • Vector3 scale = new Vector3(
  • m.GetColumn(0).magnitude,
  • m.GetColumn(1).magnitude,
  • m.GetColumn(2).magnitude
  • );

Normally I wouldn’t bother expanding such logic, but in my case I am crunching a lot of matrices and so it made a notable difference.

numberkruncher’s code is correct, mine is not.

  public static Vector3 ExtractScaleFromMatrix(Matrix4x4 matrix)
   {
     Vector3 scale;
     scale.x = new Vector4(matrix.m00, matrix.m10, matrix.m20, matrix.m30).magnitude;
     scale.y = new Vector4(matrix.m01, matrix.m11, matrix.m21, matrix.m31).magnitude;
     scale.z = new Vector4(matrix.m02, matrix.m12, matrix.m22, matrix.m32).magnitude;
     return scale;
   }

   public static Vector3 GetScale(Matrix4x4 m)
   {
     var x = Mathf.Sqrt(m.m00 * m.m00 + m.m01 * m.m01 + m.m02 * m.m02);
     var y = Mathf.Sqrt(m.m10 * m.m10 + m.m11 * m.m11 + m.m12 * m.m12);
     var z = Mathf.Sqrt(m.m20 * m.m20 + m.m21 * m.m21 + m.m22 * m.m22);

     return new Vector3(x, y, z);
   }

   private void Start()
   {
     var matrix = Matrix4x4.TRS(new Vector3(1, 2, 3), Quaternion.Euler(10, 20, 30), new Vector3(12, 23, 34));
     var scale1 = ExtractScaleFromMatrix(matrix);
     var scale2 = GetScale(matrix);

     Debug.Log(scale1); // (12.0, 23.0, 34.0)
     Debug.Log(scale2); // (18.1, 21.3, 32.4)
  }
1 Like

Yeah I figured that, I noticed you don’t new any struct, only assign all their fields, and use ref as much as possible, very clean!
But one implementation or the other, they do exactly the same thing :slight_smile: !
That doesn’t really tell me what kind of black magic makes it correct.

Sorry, just noticed your reply!

Here is an illustration which shows what the various parts of the transform matrix layout are:

Unfortunately there is no black magic at play here; sorry to disappoint :stuck_out_tongue:

I hope that this makes a little more sense :slight_smile:

2 Likes

@numberkruncher , please correctly me if I’m wrong but It doesn’t look like your code would handle a negative scale. Any ideas on how to handle a negative scale?

@mtalbott : You are correct, it does not work with a negative scale; although to me a negative scale doesn’t make an awful lot of sense.

Are there any solutions that you know of that do account for negative scale?

I agree that mathematically it might not make sense, but in practice it’s very useful for flipping (especially in 2D)… I too would love to find a workaround for negative scale.

Here is a snippet from a website which may (or may not) be relevant to detecting the negative scale:

// The scale is simply the removal of the rotation from the non-translated matrix
Matrix4x4<T> scaleMatrix = Matrix4x4<T>::Inverse(rotation) * mCopy;
scale = Vector3D<T>(scaleMatrix.rows[0][0],
scaleMatrix.rows[1][1],
scaleMatrix.rows[2][2]);

// Calculate the normalized rotation matrix and take its determinant to determine whether
// it had a negative scale or not...
Vector3D<T> row1(mCopy.rows[0][0], mCopy.rows[0][1], mCopy.rows[0][2]);
Vector3D<T> row2(mCopy.rows[1][0], mCopy.rows[1][1], mCopy.rows[1][2]);
Vector3D<T> row3(mCopy.rows[2][0], mCopy.rows[2][1], mCopy.rows[2][2]);
row1.Normalize();
row2.Normalize();
row3.Normalize();
Matrix3x3<T> nRotation(row1, row2, row3);

// Special consideration: if there's a single negative scale
// (all other combinations of negative scales will
// be part of the rotation matrix), the determinant of the
// normalized rotation matrix will be < 0.
// If this is the case we apply an arbitrary negative to one
// of the component of the scale.
T determinant = nRotation.Determinant();
if (determinant < 0.0) {
    scale.SetX(scale.GetX() * -1.0);
}

Please let me know if this helps :slight_smile:

Here is the script that I use to extract the scale from a matrix:

static Vector3 ExtractScaleFromMatrix(Matrix4x4 matrix){
    Vector3 scale = new Vector3(
        matrix.GetColumn(0).magnitude,
        matrix.GetColumn(1).magnitude,
        matrix.GetColumn(2).magnitude
        );
    if (Vector3.Cross (matrix.GetColumn (0), matrix.GetColumn (1)).normalized != (Vector3)matrix.GetColumn (2).normalized) {
        scale.x *= -1;
    }
    return scale;
}

It does account for negative scaling.

A word of warning: the methods here will break down if the 3x3 submatrix of your 4x4 contains skew (which can be explained in a hand-wavy fashion as “non-uniform non-axis-aligned scaling”). Imagine a matrix having scale of (4, 1, 1), with the “4” scale being along some diagonal direction. Apply this to a cube and it will become slanted. That’s skew.

A fully-general decomposition of a 3x3 matrix is Rb * D * Ra. Where Ra are rotations and D is axis-aligned scale (ie, a diagonal matrix or a Vec3 of scale values). You can think of Ra as rotating the object into the orientation along which the axis-aligned scale values can be applied. Rb then rotates the object into its final orientation.

Performing this decomposition involves a bunch of deep math and if you need it, you should grab the code from David Eberly or some other authoritative source. But hopefully you just have axis-aligned scale, in which case Ra is identity.

Note that if you have non-uniform scale somewhere in your transform stack, it can cause skew; this is why transform has “lossyScale”, why you can’t just stuff a Mat4x4 into a Transform, etc etc.

Guys, what about changing the world matrix just before it will go into the shader? Does Unity has any inception point to make it happen? What I basically want is a billboard shader, where you can controll the sale and the position of the object in the editor. Any chance to do that?

I would suggest starting a new thread with your question since it is quite off topic for this thread.

I already did, but it turns out your earlier comment about magnitude completely solves my issue. Thanks!

Btw, I did not find any problems with non-uniform scale when using magnitude.

If anyone ever stumbles across this, here is the shader code for scale values:

float scaleX = length(float4(UNITY_MATRIX_M[0].r, UNITY_MATRIX_M[1].r, UNITY_MATRIX_M[2].r, UNITY_MATRIX_M[3].r));
float scaleY = length(float4(UNITY_MATRIX_M[0].g, UNITY_MATRIX_M[1].g, UNITY_MATRIX_M[2].g, UNITY_MATRIX_M[3].g));

For some reason, I cannot find a button for code snippet highlighting…