Search Unity

How to assign Matrix4x4 to Transform?

Discussion in 'Scripting' started by alexzzzz, Feb 3, 2012.

  1. alexzzzz

    alexzzzz

    Joined:
    Nov 20, 2010
    Posts:
    1,408
    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?
     
    Last edited: Jun 21, 2017
  2. alexzzzz

    alexzzzz

    Joined:
    Nov 20, 2010
    Posts:
    1,408
    [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.

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public static class TransformExtensions
    4. {
    5.     public static void FromMatrix(this Transform transform, Matrix4x4 matrix)
    6.     {
    7.         transform.localScale = matrix.ExtractScale();
    8.         transform.rotation = matrix.ExtractRotation();
    9.         transform.position = matrix.ExtractPosition();
    10.     }
    11. }
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public static class MatrixExtensions
    4. {
    5.     public static Quaternion ExtractRotation(this Matrix4x4 matrix)
    6.     {
    7.         Vector3 forward;
    8.         forward.x = matrix.m02;
    9.         forward.y = matrix.m12;
    10.         forward.z = matrix.m22;
    11.  
    12.         Vector3 upwards;
    13.         upwards.x = matrix.m01;
    14.         upwards.y = matrix.m11;
    15.         upwards.z = matrix.m21;
    16.  
    17.         return Quaternion.LookRotation(forward, upwards);
    18.     }
    19.  
    20.     public static Vector3 ExtractPosition(this Matrix4x4 matrix)
    21.     {
    22.         Vector3 position;
    23.         position.x = matrix.m03;
    24.         position.y = matrix.m13;
    25.         position.z = matrix.m23;
    26.         return position;
    27.     }
    28.  
    29.     public static Vector3 ExtractScale(this Matrix4x4 matrix)
    30.     {
    31.         Vector3 scale;
    32.         scale.x = new Vector4(matrix.m00, matrix.m10, matrix.m20, matrix.m30).magnitude;
    33.         scale.y = new Vector4(matrix.m01, matrix.m11, matrix.m21, matrix.m31).magnitude;
    34.         scale.z = new Vector4(matrix.m02, matrix.m12, matrix.m22, matrix.m32).magnitude;
    35.         return scale;
    36.     }
    37. }

    Now let's test:
    Code (CSharp):
    1.         var position = new Vector3(11, 22, 33);
    2.         var rotation = Quaternion.Euler(10, 20, 30);
    3.         var scale = new Vector3(1, 2, 3);
    4.  
    5.         Debug.Log("Before:");
    6.         Debug.Log(position);
    7.         Debug.Log(rotation);
    8.         Debug.Log(scale);
    9.  
    10.         var m = Matrix4x4.TRS(position, rotation, scale);
    11.  
    12.         position = m.ExtractPosition();
    13.         rotation = m.ExtractRotation();
    14.         scale = m.ExtractScale();
    15.  
    16.         Debug.Log("After:");
    17.         Debug.Log(position);
    18.         Debug.Log(rotation);
    19.         Debug.Log(scale);
    20.  
    Test.PNG
     
    Last edited: Feb 22, 2017
  3. mandren

    mandren

    Joined:
    Jul 4, 2014
    Posts:
    1
    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.25f*s;
    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.25f*s;
    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;
    }
     
  4. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    6,775
    Use code tags.

    This is the version I use:
    Code (csharp):
    1.  
    2.         public static Quaternion MatrixToRotation(Matrix4x4 m)
    3.         {
    4.             // Adapted from: http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
    5.             Quaternion q = new Quaternion();
    6.             q.w =Mathf.Sqrt(Mathf.Max(0,1+ m[0,0]+ m[1,1]+ m[2,2]))/2;
    7.             q.x =Mathf.Sqrt(Mathf.Max(0,1+ m[0,0]- m[1,1]- m[2,2]))/2;
    8.             q.y =Mathf.Sqrt(Mathf.Max(0,1- m[0,0]+ m[1,1]- m[2,2]))/2;
    9.             q.z =Mathf.Sqrt(Mathf.Max(0,1- m[0,0]- m[1,1]+ m[2,2]))/2;
    10.             q.x *=Mathf.Sign(q.x *(m[2,1]- m[1,2]));
    11.             q.y *=Mathf.Sign(q.y *(m[0,2]- m[2,0]));
    12.             q.z *=Mathf.Sign(q.z *(m[1,0]- m[0,1]));
    13.             return q;
    14.         }
    15.  
    Found here:
    https://github.com/lordofduct/space.../master/SpacepuppyBase/Utils/TransformUtil.cs
     
    Last edited: Jun 2, 2015
  5. numberkruncher

    numberkruncher

    Joined:
    Feb 18, 2012
    Posts:
    949
    Since everyone is sharing, here is the version I use:
    Code (csharp):
    1. /// <summary>
    2. /// Extract translation from transform matrix.
    3. /// </summary>
    4. /// <param name="matrix">Transform matrix. This parameter is passed by reference
    5. /// to improve performance; no changes will be made to it.</param>
    6. /// <returns>
    7. /// Translation offset.
    8. /// </returns>
    9. public static Vector3 ExtractTranslationFromMatrix(ref Matrix4x4 matrix) {
    10.     Vector3 translate;
    11.     translate.x = matrix.m03;
    12.     translate.y = matrix.m13;
    13.     translate.z = matrix.m23;
    14.     return translate;
    15. }
    16.  
    17. /// <summary>
    18. /// Extract rotation quaternion from transform matrix.
    19. /// </summary>
    20. /// <param name="matrix">Transform matrix. This parameter is passed by reference
    21. /// to improve performance; no changes will be made to it.</param>
    22. /// <returns>
    23. /// Quaternion representation of rotation transform.
    24. /// </returns>
    25. public static Quaternion ExtractRotationFromMatrix(ref Matrix4x4 matrix) {
    26.     Vector3 forward;
    27.     forward.x = matrix.m02;
    28.     forward.y = matrix.m12;
    29.     forward.z = matrix.m22;
    30.  
    31.     Vector3 upwards;
    32.     upwards.x = matrix.m01;
    33.     upwards.y = matrix.m11;
    34.     upwards.z = matrix.m21;
    35.  
    36.     return Quaternion.LookRotation(forward, upwards);
    37. }
    38.  
    39. /// <summary>
    40. /// Extract scale from transform matrix.
    41. /// </summary>
    42. /// <param name="matrix">Transform matrix. This parameter is passed by reference
    43. /// to improve performance; no changes will be made to it.</param>
    44. /// <returns>
    45. /// Scale vector.
    46. /// </returns>
    47. public static Vector3 ExtractScaleFromMatrix(ref Matrix4x4 matrix) {
    48.     Vector3 scale;
    49.     scale.x = new Vector4(matrix.m00, matrix.m10, matrix.m20, matrix.m30).magnitude;
    50.     scale.y = new Vector4(matrix.m01, matrix.m11, matrix.m21, matrix.m31).magnitude;
    51.     scale.z = new Vector4(matrix.m02, matrix.m12, matrix.m22, matrix.m32).magnitude;
    52.     return scale;
    53. }
    54.  
    55. /// <summary>
    56. /// Extract position, rotation and scale from TRS matrix.
    57. /// </summary>
    58. /// <param name="matrix">Transform matrix. This parameter is passed by reference
    59. /// to improve performance; no changes will be made to it.</param>
    60. /// <param name="localPosition">Output position.</param>
    61. /// <param name="localRotation">Output rotation.</param>
    62. /// <param name="localScale">Output scale.</param>
    63. public static void DecomposeMatrix(ref Matrix4x4 matrix, out Vector3 localPosition, out Quaternion localRotation, out Vector3 localScale) {
    64.     localPosition = ExtractTranslationFromMatrix(ref matrix);
    65.     localRotation = ExtractRotationFromMatrix(ref matrix);
    66.     localScale = ExtractScaleFromMatrix(ref matrix);
    67. }
    68.  
    69. /// <summary>
    70. /// Set transform component from TRS matrix.
    71. /// </summary>
    72. /// <param name="transform">Transform component.</param>
    73. /// <param name="matrix">Transform matrix. This parameter is passed by reference
    74. /// to improve performance; no changes will be made to it.</param>
    75. public static void SetTransformFromMatrix(Transform transform, ref Matrix4x4 matrix) {
    76.     transform.localPosition = ExtractTranslationFromMatrix(ref matrix);
    77.     transform.localRotation = ExtractRotationFromMatrix(ref matrix);
    78.     transform.localScale = ExtractScaleFromMatrix(ref matrix);
    79. }
    80.  
    81.  
    82. // EXTRAS!
    83.  
    84. /// <summary>
    85. /// Identity quaternion.
    86. /// </summary>
    87. /// <remarks>
    88. /// <para>It is faster to access this variation than <c>Quaternion.identity</c>.</para>
    89. /// </remarks>
    90. public static readonly Quaternion IdentityQuaternion = Quaternion.identity;
    91. /// <summary>
    92. /// Identity matrix.
    93. /// </summary>
    94. /// <remarks>
    95. /// <para>It is faster to access this variation than <c>Matrix4x4.identity</c>.</para>
    96. /// </remarks>
    97. public static readonly Matrix4x4 IdentityMatrix = Matrix4x4.identity;
    98.  
    99. /// <summary>
    100. /// Get translation matrix.
    101. /// </summary>
    102. /// <param name="offset">Translation offset.</param>
    103. /// <returns>
    104. /// The translation transform matrix.
    105. /// </returns>
    106. public static Matrix4x4 TranslationMatrix(Vector3 offset) {
    107.     Matrix4x4 matrix = IdentityMatrix;
    108.     matrix.m03 = offset.x;
    109.     matrix.m13 = offset.y;
    110.     matrix.m23 = offset.z;
    111.     return matrix;
    112. }
     
  6. Guillaume-Swing

    Guillaume-Swing

    Joined:
    Nov 29, 2012
    Posts:
    5
    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 :) ?
     
  7. numberkruncher

    numberkruncher

    Joined:
    Feb 18, 2012
    Posts:
    949
    That particular implementation of `ExtractScaleFromMatrix` was expanded since it performed slightly better than the following when I profiled it:

    1. // Extract new local scale
    2. Vector3 scale = new Vector3(
    3. m.GetColumn(0).magnitude,
    4. m.GetColumn(1).magnitude,
    5. m.GetColumn(2).magnitude
    6. );

    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.
     
  8. alexzzzz

    alexzzzz

    Joined:
    Nov 20, 2010
    Posts:
    1,408
    numberkruncher's code is correct, mine is not.

    Code (csharp):
    1.   public static Vector3 ExtractScaleFromMatrix(Matrix4x4 matrix)
    2.    {
    3.      Vector3 scale;
    4.      scale.x = new Vector4(matrix.m00, matrix.m10, matrix.m20, matrix.m30).magnitude;
    5.      scale.y = new Vector4(matrix.m01, matrix.m11, matrix.m21, matrix.m31).magnitude;
    6.      scale.z = new Vector4(matrix.m02, matrix.m12, matrix.m22, matrix.m32).magnitude;
    7.      return scale;
    8.    }
    9.  
    10.    public static Vector3 GetScale(Matrix4x4 m)
    11.    {
    12.      var x = Mathf.Sqrt(m.m00 * m.m00 + m.m01 * m.m01 + m.m02 * m.m02);
    13.      var y = Mathf.Sqrt(m.m10 * m.m10 + m.m11 * m.m11 + m.m12 * m.m12);
    14.      var z = Mathf.Sqrt(m.m20 * m.m20 + m.m21 * m.m21 + m.m22 * m.m22);
    15.  
    16.      return new Vector3(x, y, z);
    17.    }
    18.  
    19.    private void Start()
    20.    {
    21.      var matrix = Matrix4x4.TRS(new Vector3(1, 2, 3), Quaternion.Euler(10, 20, 30), new Vector3(12, 23, 34));
    22.      var scale1 = ExtractScaleFromMatrix(matrix);
    23.      var scale2 = GetScale(matrix);
    24.  
    25.      Debug.Log(scale1); // (12.0, 23.0, 34.0)
    26.      Debug.Log(scale2); // (18.1, 21.3, 32.4)
    27.   }
     
  9. Guillaume-Swing

    Guillaume-Swing

    Joined:
    Nov 29, 2012
    Posts:
    5
    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 :) !
    That doesn't really tell me what kind of black magic makes it correct.
     
  10. numberkruncher

    numberkruncher

    Joined:
    Feb 18, 2012
    Posts:
    949
    Sorry, just noticed your reply!

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


    - http://www.songho.ca/opengl/gl_transform.html

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

    I hope that this makes a little more sense :)
     
    Circool likes this.
  11. mtalbott

    mtalbott

    Joined:
    Dec 21, 2011
    Posts:
    109
    @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?
     
  12. numberkruncher

    numberkruncher

    Joined:
    Feb 18, 2012
    Posts:
    949
    @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.
     
  13. nick.mann

    nick.mann

    Joined:
    Feb 16, 2015
    Posts:
    2
    Are there any solutions that you know of that do account for negative scale?
     
  14. benblo

    benblo

    Joined:
    Aug 14, 2007
    Posts:
    469
    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.
     
  15. numberkruncher

    numberkruncher

    Joined:
    Feb 18, 2012
    Posts:
    949
    Here is a snippet from a website which may (or may not) be relevant to detecting the negative scale:
    Code (csharp):
    1. // The scale is simply the removal of the rotation from the non-translated matrix
    2. Matrix4x4<T> scaleMatrix = Matrix4x4<T>::Inverse(rotation) * mCopy;
    3. scale = Vector3D<T>(scaleMatrix.rows[0][0],
    4. scaleMatrix.rows[1][1],
    5. scaleMatrix.rows[2][2]);
    6.  
    7. // Calculate the normalized rotation matrix and take its determinant to determine whether
    8. // it had a negative scale or not...
    9. Vector3D<T> row1(mCopy.rows[0][0], mCopy.rows[0][1], mCopy.rows[0][2]);
    10. Vector3D<T> row2(mCopy.rows[1][0], mCopy.rows[1][1], mCopy.rows[1][2]);
    11. Vector3D<T> row3(mCopy.rows[2][0], mCopy.rows[2][1], mCopy.rows[2][2]);
    12. row1.Normalize();
    13. row2.Normalize();
    14. row3.Normalize();
    15. Matrix3x3<T> nRotation(row1, row2, row3);
    16.  
    17. // Special consideration: if there's a single negative scale
    18. // (all other combinations of negative scales will
    19. // be part of the rotation matrix), the determinant of the
    20. // normalized rotation matrix will be < 0.
    21. // If this is the case we apply an arbitrary negative to one
    22. // of the component of the scale.
    23. T determinant = nRotation.Determinant();
    24. if (determinant < 0.0) {
    25.     scale.SetX(scale.GetX() * -1.0);
    26. }
    - http://callumhay.blogspot.co.uk/2010/10/decomposing-affine-transforms.html

    Please let me know if this helps :)
     
  16. bitoffdev

    bitoffdev

    Joined:
    Aug 12, 2013
    Posts:
    43
    Here is the script that I use to extract the scale from a matrix:
    Code (CSharp):
    1. static Vector3 ExtractScaleFromMatrix(Matrix4x4 matrix){
    2.     Vector3 scale = new Vector3(
    3.         matrix.GetColumn(0).magnitude,
    4.         matrix.GetColumn(1).magnitude,
    5.         matrix.GetColumn(2).magnitude
    6.         );
    7.     if (Vector3.Cross (matrix.GetColumn (0), matrix.GetColumn (1)).normalized != (Vector3)matrix.GetColumn (2).normalized) {
    8.         scale.x *= -1;
    9.     }
    10.     return scale;
    11. }
    It does account for negative scaling.
     
    Last edited: Aug 2, 2015
  17. pld

    pld

    Joined:
    Dec 12, 2014
    Posts:
    7
    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.
     
  18. cubrman

    cubrman

    Joined:
    Jun 18, 2016
    Posts:
    191
    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?
     
  19. numberkruncher

    numberkruncher

    Joined:
    Feb 18, 2012
    Posts:
    949
    I would suggest starting a new thread with your question since it is quite off topic for this thread.
     
  20. cubrman

    cubrman

    Joined:
    Jun 18, 2016
    Posts:
    191
    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...
     
  21. alexzzzz

    alexzzzz

    Joined:
    Nov 20, 2010
    Posts:
    1,408
    I've finally updated the code in the beginning of the thread. Seems to produce the correct results for all the position, rotation and scale.
     
  22. jethrogillgren

    jethrogillgren

    Joined:
    Jan 11, 2017
    Posts:
    28
    New Method:

    Code (CSharp):
    1. public static Vector3 PositionFromMatrix(Matrix4x4 m)
    2.     {
    3.         return m.GetColumn(3);
    4.     }
     
  23. wynott

    wynott

    Joined:
    Nov 16, 2016
    Posts:
    29
    I found this theread based on the initial post.

    I already have a matrix that I've exported from another piece of software.

    Is there an elegant way to take my 16 numbers and just directly stick them into the 4x4?
     
  24. wynott

    wynott

    Joined:
    Nov 16, 2016
    Posts:
    29
    Think this works. Hardcoded matrix for testing but will do a parser etc.

    Matrix4x4 matrix = new Matrix4x4();

    Vector4 column0 = new Vector4(0.9439778387f, -0.2836667956f, 0.1686386348f, 0f);
    Vector4 column1 = new Vector4(-0.1615042061f, 4.85E-02f, -0.9856779448f, 0f);
    Vector4 column2 = new Vector4(0.2877885186f, 0.9576939848f, 0f, 0f);
    Vector4 column3 = new Vector4(-46.82842202f, 4.95691376f, -4.889713775f, 1f);

    matrix.SetColumn(0, column0);
    matrix.SetColumn(1, column1);
    matrix.SetColumn(2, column2);
    matrix.SetColumn(3, column3);

    Test.transform.rotation = Quaternion.LookRotation(matrix.GetColumn(2), matrix.GetColumn(1));
    Test.transform.position = matrix.GetColumn(3);
     
  25. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    Just be aware Matrix4x4 is column based. So if your looking at examples of matrix multiplies online, any non graphics stuff will be showing you examples row based. So sometimes you maybe to to transpose a matrix you see online after making it. Though the fact you are settting up your matrix with SetColumn makes me think you already have a handle on this.
     
  26. wynott

    wynott

    Joined:
    Nov 16, 2016
    Posts:
    29
    Thanks @takatok

    to be honest it's a bit of a black mystery box for me. I wrote a script in Rhino that pulls the transforms from my block instances in Rhinoceros. I then did semi-guess transforms in Rhino via trial and error until it looked right in Unity. Then compared the original object matrix values with the one that resulted in the correct transform in Unity, and manually transposed the values with the script... Every cell needed either a swap with another or a sign inversion.... But it works! Can select all the bits in my Rhino model, hit a button and then Unity spawns a prefab based on each matrix in the correct spot and orientation.
     
  27. w_adams

    w_adams

    Joined:
    Apr 7, 2016
    Posts:
    14
    Hey @wynott, I'm trying to tackle the same problem as you - rhino to unity via transform matrix. Would you be willing to shed some light for me on what cells needed swapping? I'm using grasshopper to grab the matrix from rhino.
     
  28. Lesliehdez

    Lesliehdez

    Joined:
    May 23, 2017
    Posts:
    5
    I need to work with a 4x4 matrix (16 numbers) and apply this transformation matrix to obtain a new position, rotation and scale, as I can work it?