- Home /
The question is answered, right answer was accepted
How to Decompose a TRS Matrix?
How can the position, rotation and scale components be extracted from a transform matrix?
Hi, i need apply my own $$anonymous$$ATRIX4X4= [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] to my gameobject, how can i do that? I need to apply it to obtain a new transformation to my object.
this is correct? $$anonymous$$atrix4x4 worldToLocal = [15,0,0,0,0,15,0,0,0,0,15,0,0,0,0,1]; gameObj.Transform.localToWorld$$anonymous$$atrix = worldToLocal.inverse;
You should post this as a separate question, since the original question has been already answered.
Answer by numberkruncher · Feb 19, 2013 at 03:13 PM
The components of a matrix can be extracted as follows:
// Extract new local position
Vector3 position = m.GetColumn(3);
// Extract new local rotation
Quaternion rotation = Quaternion.LookRotation(
m.GetColumn(2),
m.GetColumn(1)
);
// Extract new local scale
Vector3 scale = new Vector3(
m.GetColumn(0).magnitude,
m.GetColumn(1).magnitude,
m.GetColumn(2).magnitude
);
Note: The purpose of this question was to share this knowledge in Q&A style :-)
This method is not able to extract negative scale values (flip transforms).
I agree it's not good but real world is such. In my case, I need to import GoogleEarth $$anonymous$$$$anonymous$$Z models (Collada inside), which often have a number of such flip transforms produced by Google SketchUp. Probably other 3D-editors also can produce such models. Unfortunately my project managers will not agree to drop this feature because of Google immorality.
@vagran_zulu Can you not just write a script to generate a new mesh which does not have flipped transforms? i.e. recompose your mesh... just an idea I thought I'd throw in there for you to play with ;)
Yes, it can be done, it will work, and can be used as workaround. The bad think is that mesh data will be duplicated and will waste a lot of resources. Typically a model has nodes graph, each node has some transform and may have some meshes attached to it. The same mesh can be attached to many nodes thus just having different overall transform applied (and different material bound). So in model which has many parts shared in such a way the wasting factor could be significant.
Actually I have found another workaround but it also has problems: the answer can be extended by checking deter$$anonymous$$ant value of the matrix and in case it is negative, one (any) component of scale vector should be negated. It works for vertex coordinates but unfortunately the Unity is unable to apply this transform also to normals - they became inverted. Seems when it calculates matrix for normals it does not account scaling sign (however according to documentation it accounts scaling as such - it makes some workaround for non-uniform scaling). Before that I tried using Graphics.Draw$$anonymous$$esh with matrix but there is also a problem. Seems the provided matrix applied to both positions and normals as is. Usually for normals separate matrix is calculated if necessary (orthonormal matrix, transpose of inverse of 3x3 upper-left submatrix), but Unity does not do it.
Answer by frogsbo · Mar 12, 2014 at 07:26 AM
Here is an example of Matrix TRS from extrusion demo in .js:
for (var i=0;i<sections.length;i++)//for every vertex as edges of 2 pairs
{
if (autoCalculateOrientation)
{
if (i == 0)
{
var direction = sections[0].point - sections[1].point;
var rotation = Quaternion.LookRotation(direction, Vector3.up);
previousRotation = rotation;
finalSections[i] = worldToLocal * Matrix4x4.TRS(position, rotation, Vector3.one);
}
// all elements get the direction by looking up the next section
else if (i != sections.length - 1)
{
direction = sections[i].point - sections[i+1].point;
rotation = Quaternion.LookRotation(direction, Vector3.up);
// When the angle of the rotation compared to the last segment is too high
// smooth the rotation a little bit. Optimally we would smooth the entire sections array.
if (Quaternion.Angle (previousRotation, rotation) > 20)
rotation = Quaternion.Slerp(previousRotation, rotation, 0.5);
previousRotation = rotation;
finalSections[i] = worldToLocal * Matrix4x4.TRS(sections[i].point, rotation, Vector3.one);
}
// except the last one, which just copies the previous one
else
{
finalSections[i] = finalSections[i-1];
}
}
else
{
if (i == 0)
{
finalSections[i] = Matrix4x4.identity;
}
else
{
finalSections[i] = worldToLocal * sections[i].matrix;
}
}
}
Your answer shows how to Compose a TRS matrix whereas the purpose of this question/topic is to Decompose a TRS matrix.
Answer by ETdoFresh · Jan 02, 2016 at 05:59 PM
Here's a Monobehaviour Script I've been working on for a few hours for doing it the Hard Way, for those interested...
public class DisplayTransformMatrix : MonoBehaviour
{
[Header("Matrix4x4.localToWorldMatrix")]
public Vector3 MatrixRow1;
public Vector3 MatrixRow2;
public Vector3 MatrixRow3;
public Vector3 MatrixCol3;
public string MatrixRow4;
[Header("Local Transform")]
public Vector3 localPosition;
public string localRotation;
public Vector3 localEuler;
public Vector3 localScale;
[Header("World Transform")]
public Vector3 worldPosition;
public string worldRotation;
public Vector3 worldEuler;
public Vector3 worldScale;
[Header("UI Element")]
public Text textToUpdate;
void Update()
{
// Update Matrix4x4 Information in Inspector
Matrix4x4 m = transform.localToWorldMatrix;
MatrixRow1.x = m[0, 0];
MatrixRow1.y = m[0, 1];
MatrixRow1.z = m[0, 2];
MatrixRow2.x = m[1, 0];
MatrixRow2.y = m[1, 1];
MatrixRow2.z = m[1, 2];
MatrixRow3.x = m[2, 0];
MatrixRow3.y = m[2, 1];
MatrixRow3.z = m[2, 2];
MatrixCol3.x = m[0, 3];
MatrixCol3.y = m[1, 3];
MatrixCol3.z = m[2, 3];
MatrixRow4 = "" + m[3, 0] + " " + m[3, 1] + " " + m[3, 2] + " " + m[3, 3];
// Update Local Transform in Inspector
localPosition = transform.localPosition;
localRotation = string.Format("{0} {1} {2} {3}", transform.localRotation.w,
transform.localRotation.x, transform.localRotation.y, transform.localRotation.z);
localEuler = QuaternionToEuler(transform.localRotation);
localScale = transform.localScale;
// Update World Transform in Inspector
worldPosition = GetPosition(m);
worldRotation = string.Format("{0} {1} {2} {3}", GetRotation(m).w,
GetRotation(m).x, GetRotation(m).y, GetRotation(m).z);
worldEuler = QuaternionToEuler(GetRotation(m));
worldScale = GetScale(m);
// Update UI Element
if (textToUpdate)
textToUpdate.text = m.ToString();
}
public Vector3 GetPosition(Matrix4x4 m)
{
return new Vector3(m[0, 3], m[1, 3], m[2, 3]);
}
public Vector3 GetScale(Matrix4x4 m)
{
return new Vector3
(m.GetColumn(0).magnitude, m.GetColumn(1).magnitude, m.GetColumn(2).magnitude);
}
public Quaternion GetRotation(Matrix4x4 m)
{
Vector3 s = GetScale(m);
// Normalize Scale from Matrix4x4
float m00 = m[0, 0] / s.x;
float m01 = m[0, 1] / s.y;
float m02 = m[0, 2] / s.z;
float m10 = m[1, 0] / s.x;
float m11 = m[1, 1] / s.y;
float m12 = m[1, 2] / s.z;
float m20 = m[2, 0] / s.x;
float m21 = m[2, 1] / s.y;
float m22 = m[2, 2] / s.z;
Quaternion q = new Quaternion();
q.w = Mathf.Sqrt(Mathf.Max(0, 1 + m00 + m11 + m22)) / 2;
q.x = Mathf.Sqrt(Mathf.Max(0, 1 + m00 - m11 - m22)) / 2;
q.y = Mathf.Sqrt(Mathf.Max(0, 1 - m00 + m11 - m22)) / 2;
q.z = Mathf.Sqrt(Mathf.Max(0, 1 - m00 - m11 + m22)) / 2;
q.x *= Mathf.Sign(q.x * (m21 - m12));
q.y *= Mathf.Sign(q.y * (m02 - m20));
q.z *= Mathf.Sign(q.z * (m10 - m01));
// q.Normalize()
float qMagnitude = Mathf.Sqrt(q.w * q.w + q.x * q.x + q.y * q.y + q.z * q.z);
q.w /= qMagnitude;
q.x /= qMagnitude;
q.y /= qMagnitude;
q.z /= qMagnitude;
return q;
}
public Vector3 QuaternionToEuler(Quaternion q)
{
Vector3 result;
float test = q.x * q.y + q.z * q.w;
// singularity at north pole
if (test > 0.499)
{
result.x = 0;
result.y = 2 * Mathf.Atan2(q.x, q.w);
result.z = Mathf.PI / 2;
}
// singularity at south pole
else if (test < -0.499)
{
result.x = 0;
result.y = -2 * Mathf.Atan2(q.x, q.w);
result.z = -Mathf.PI / 2;
}
else
{
result.x = Mathf.Rad2Deg * Mathf.Atan2(2 * q.x * q.w - 2 * q.y * q.z, 1 - 2 * q.x * q.x - 2 * q.z * q.z);
result.y = Mathf.Rad2Deg * Mathf.Atan2(2 * q.y * q.w - 2 * q.x * q.z, 1 - 2 * q.y * q.y - 2 * q.z * q.z);
result.z = Mathf.Rad2Deg * Mathf.Asin(2 * q.x * q.y + 2 * q.z * q.w);
if (result.x < 0) result.x += 360;
if (result.y < 0) result.y += 360;
if (result.z < 0) result.z += 360;
}
return result;
}
}
If the value of scale is negative,the result of calculation is incorrect
Follow this Question
Related Questions
Quaternion to Matrix Conversion Error - Camera Script 0 Answers
Having trouble instantiating a gameobject with rotation 0 Answers
Creating a Matrix4x4 from an Object's Transform 1 Answer
How to write a script to disable position and rotation tracking for VR 0 Answers
Function similar to Quaternion.Angle for Scale or Position? 1 Answer