GameObject transform Quaternion logic mismatch, it should be fixed!

Look at this code please:

	private void Test()
	{
		GameObject go = new GameObject();
		
		Quaternion orientation = new Quaternion( 0, 0, 0.7071068f, 0.7071068f);
				
		Matrix4x4 mat = Matrix4x4.TRS(Vector3.zero, orientation, Vector3.one);
			
		Debug.Log("Matrix TRS");
		Debug.Log(mat.GetRow(0));
		Debug.Log(mat.GetRow(1));
		Debug.Log(mat.GetRow(2));
		
		go.transform.rotation = orientation;
		
		Debug.Log("Game Object Rotation");
		Debug.Log(go.transform.right);
		Debug.Log(go.transform.up);
		Debug.Log(go.transform.forward);
	}

If you run it the result will be:

Matrix TRS

(0.0, -1.0, 0.0, 0.0)
(1.0, 0.0, 0.0, 0.0)
(0.0, 0.0, 1.0, 0.0)

Game Object Rotation

(0.0, 1.0, 0.0)
(-1.0, 0.0, 0.0)
(0.0, 0.0, 1.0)

Yesterday I spent half a day on an algorithm I was sure to be mathematically correct because I could not achieve the correct result on the screen (I was expecting the TRS result instead I was getting the GameObject result).

I was trying to figure out what the error in my logic was, when then I realized that the problem is how the GameObject.Transform handles the rotation quaternion.

While the Matrix.TRS result should be identical to the one generated by a GameObject transform, they are instead different.

Or am I missing something?

I just noticed that the GameObjectRotation looks like the transpose of Matrix4x4.TRS result

I start to wonder if Matrix4x4 and Transform are using 2 different Matrix conventions (like Matrix4x4 uses translation on the 4th column while Transform uses the more standard translation on the 4th row?)

Edit: yeah it seems like Matrix4x4 uses the translation on the 4th column (opengl standard if I remember correctly) while GameObject.Transform transpose it using the DirectX standard. Can somebody confirm it?

It seems you’re trying to compare something which is essentially not the same. The direction properties (up, right, forward) are just vectors and they aren’t ment to be interpreted as rows or columns of a matrix. Transform has actually a matrix and i guess you want Transform.localToWorldMatrix

The properties are implemented this way:

public Vector3 forward
{
    get { return this.rotation * Vector3.forward; }
    set { this.rotation = Quaternion.LookRotation(value); }
}
public Vector3 right
{
	get { return this.rotation * Vector3.right; }
	set { this.rotation = Quaternion.FromToRotation(Vector3.right, value); }
}
public Vector3 up
{
	get { return this.rotation * Vector3.up; }
	set { this.rotation = Quaternion.FromToRotation(Vector3.up, value); }
}

Btw if you have the transpose of a matrix you just have to change the multiplication order.

So Mp == pMT where MT is the transpose of M

However Unity’s Matrix4x4 only allows M*p multiplication. So it’s always:

    vector in 5,6,7,1
              | | | |
              1,0,0,0 --> 5
              0,1,0,0 --> 6
              0,0,1,0 --> 7
              0,0,0,1 --> 1

//That means the matrix looks like this in Unity:

              R,R,R,P
              R,R,R,P
              R,R,R,P
              0,0,0,1
 R => combined rotation / scale
 P => translation

Since you’re messing around with matrices i assume you’re familiar with matrix - point multiplications :wink:

yeah sure, I used many engines in my life and coded my own one a long while ago. However I always used the convention translations on 4th row (row-major), so I was stupidly assuming that right, up and forward were the rows.

Ok since it uses the convention translation on 4th column (column-major) it makes sense that the right, up and forward are actually the columns and not the rows. That was my mistake thank you for the explanation!

a quick link for who does not know what we are talking about: mathematics - Can someone explain the (reasons for the) implications of colum vs row major in multiplication/concatenation? - Game Development Stack Exchange

Knowing that an orthonormal matrix represents a basis, I use the forward,up and right vectors to find the adjacent entities in a 3d grid according the current orientation. However I should have used the columns and not the rows.

thank you for the clarification! I should have thought about it on my own :wink:

Edit: about the first part of your answer: when an orthonormal matrix represents a basis, the rows are actually the right, up and forward when the matrix is row-major and they are the columns when the matrix is column-major.
“A set of vectors can be represented by a matrix whose columns are the components of each vector of the set. As a basis is a set of vectors, a basis can be given by a matrix of this kind”