3
$\begingroup$

The Setup:

I have a simple setup with two cube objects with a parent child relationship like so:

Objects in Blender

The Red cube is a child object of the green cube.

The Red cube has the following transforms:

Transform of red cube in Blender

The green cube has the following transforms:

Transform of red cube in Blender

The Problem

The thing I am confused about is, when I try to decompose the local_matrix of the red cube and compose it back to its original transform matrix. I get different results. Below is a script I used to decompose and compose the local matrix. Any clues why they are different? And what is the correct way to re-compose the local matrix given the translation, rotation and scale matrices.

Script and Result

import bpy
import mathutils
import math

myObject = bpy.data.objects['Cube.002']

print("\n location mat")
loc = myObject.matrix_local.to_translation()
print(loc)
mat_loc = mathutils.Matrix.Translation(loc)
print(mat_loc)

print("\n rotation mat")
rot = myObject.matrix_local.to_quaternion()
print(rot)
mat_rot = rot.to_matrix().to_4x4()
print(mat_rot)

print("\n scale mat")
scl = myObject.matrix_local.to_scale()
print(scl)
mat_scl = mathutils.Matrix.Identity(4)
mat_scl_x = mathutils.Matrix.Scale(scl.x, 4, (1,0,0))
mat_scl_y = mathutils.Matrix.Scale(scl.y, 4, (0,1,0))
mat_scl_z = mathutils.Matrix.Scale(scl.z, 4, (0,0,1))
mat_scl = mat_scl_x @ mat_scl_y @ mat_scl_z
print(mat_scl)

print("\n myLocalMat mat")
myLocalMat =  mat_loc @ mat_rot @ mat_scl
print(myLocalMat)

print("\n local mat")
print(myObject.matrix_local)
 location mat
<Vector (0.0000, 0.0000, 3.0000)>
<Matrix 4x4 (1.0000, 0.0000, 0.0000, 0.0000)
            (0.0000, 1.0000, 0.0000, 0.0000)
            (0.0000, 0.0000, 1.0000, 3.0000)
            (0.0000, 0.0000, 0.0000, 1.0000)>

 rotation mat
<Quaternion (w=0.9280, x=0.0000, y=0.0000, z=0.3726)>
<Matrix 4x4 (0.7224, -0.6915, 0.0000, 0.0000)
            (0.6915,  0.7224, 0.0000, 0.0000)
            (0.0000,  0.0000, 1.0000, 0.0000)
            (0.0000,  0.0000, 0.0000, 1.0000)>

 scale mat
<Vector (1.5811, 1.5811, 1.0000)>
<Matrix 4x4 (1.5811, 0.0000, 0.0000, 0.0000)
            (0.0000, 1.5811, 0.0000, 0.0000)
            (0.0000, 0.0000, 1.0000, 0.0000)
            (0.0000, 0.0000, 0.0000, 1.0000)>

 myLocalMat mat
<Matrix 4x4 (1.1422, -1.0934, 0.0000, 0.0000)
            (1.0934,  1.1422, 0.0000, 0.0000)
            (0.0000,  0.0000, 1.0000, 3.0000)
            (0.0000,  0.0000, 0.0000, 1.0000)>

 local mat
<Matrix 4x4 (1.4142, -1.4142, 0.0000, 0.0000)
            (0.7071,  0.7071, 0.0000, 0.0000)
            (0.0000,  0.0000, 1.0000, 3.0000)
            (0.0000,  0.0000, 0.0000, 1.0000)>

Best Regards. QC

$\endgroup$
4
  • $\begingroup$ See blender.stackexchange.com/questions/169416/… It's the basis matrix you see in the UI. If you have parented via UI have set the parent inverse. Can decompose with Matrix.decompose() $\endgroup$ – batFINGER May 26 at 12:46
  • $\begingroup$ @batFINGER Thanks for the reply. I was not comparing the transform in the UI to the transforms in the script result. I was only doing this to a transform matrix: Decompose --> recompose. And yet I get different matrices with the same parameters in the script $\endgroup$ – QuantumCoderQC May 26 at 13:15
  • $\begingroup$ Yes I can see what you are doing. The UI is the decomposed basis matrix. Composed as the basis matrix & multiplied by parent inverse gives the local matrix. Two rotations can be equivalent. Hence suggesting that perhaps decomposing / recomposing the local matrix, may give a different (but equivalent, eg try setting as a matrix to object to visualize) . To get the equivalent rotation matrix part would look at using the normalized 3x3 matrix of original, as it's the quaternion to matrix part that offers up different resultant matrix. $\endgroup$ – batFINGER May 26 at 13:23
  • $\begingroup$ Thanks again, I tried to visualize by setting the matrix_local to the matrix that I composed. The changes were: The object's rotation along the z axis changed from 45 to 42.8; the scale changed from (1, 1, 1) to (1.23, 1.23, 1). These changes were observed in the viewport.. $\endgroup$ – QuantumCoderQC May 26 at 13:57
2
$\begingroup$

Using the 3x3 matrix rotation part.

Does a child object inherit the matrix from the parent?

Can decompose a matrix via Matrix.decompose()

loc, rot, scale = M.decompose()

the rotation is given as a quaternion. There is no guarantee that that the same rotation matrix will be returned when converting that quaternion back to a matrix. https://en.wikipedia.org/wiki/Rotation_formalisms_in_three_dimensions#Rotation_matrix_%E2%86%94_quaternion

Test script, with both "standard" quaternion decompose and using the 3x3 rotation matrix part.

import bpy
from mathutils import Matrix

ob = bpy.context.object
# basis (or local) matrix
Mb = ob.matrix_local

print("Input")
print(Mb)


t, q, s = Mb.decompose()

T = Matrix.Translation(t)

R = q.to_matrix().to_4x4()

S = Matrix.Diagonal(s.to_4d())

M = T @ R @ S
print("Quaternion decompose")
print(M)

print((M.to_3x3() - Mb.to_3x3()).determinant())

R = Mb.to_3x3().normalized().to_4x4()

T = Matrix.Translation(
        Mb.to_translation()
        )

S = Matrix.Diagonal(
        Mb.to_scale().to_4d()
        )
        
M = T @ R @ S

print("3 x 3 decompose")
print(M)

print((M.to_3x3() - Mb.to_3x3()).determinant())

Sample output.

Input
<Matrix 4x4 (1.8334, -0.5595,  0.0113, 0.6324)
            (0.5749,  0.1069, -0.9128, 2.6868)
            (6.2012,  1.4492,  0.3085, 0.8749)
            (0.0000,  0.0000,  0.0000, 1.0000)>
Quaternion decompose
<Matrix 4x4 ( 4.5809, -1.0592, -0.1913, 0.6324)
            (-1.7931, -0.0093, -0.9261, 2.6868)
            ( 4.2364,  1.1414, -0.1851, 0.8749)
            ( 0.0000,  0.0000,  0.0000, 1.0000)>
0.6159369945526123
3 x 3 decompose
<Matrix 4x4 (1.8334, -0.5595,  0.0113, 0.6324)
            (0.5749,  0.1069, -0.9128, 2.6868)
            (6.2012,  1.4492,  0.3085, 0.8749)
            (0.0000,  0.0000,  0.0000, 1.0000)>
0.0
$\endgroup$
4
  • $\begingroup$ Great! The matrix approach works just as expected. But this then brings the question, given "t"(Vector), "q"(Quaternion) and "s"(Vector) (from your example), is there any way to arrive at the "input" matrix? If not the exact matrix, an equivalent one, that results in the same transform of the child object when its matrix_local set with this new equivalent matrix. Best Regards $\endgroup$ – QuantumCoderQC May 26 at 17:19
  • $\begingroup$ Going a bit full circle here re first comment (and link in answer) re matrix local being product of parent inverse and basis. Not sure of desired result if setting matrix local when the parent inverse is not identity. As pointed out, the loc rot and scale of object is decomposed matrix basis. BTW most of the time I would suggest using world matrix, in which case the local matrix is its parents world matrix inverted times world matrix. Very rarely see local matrix used directly. $\endgroup$ – batFINGER May 26 at 17:42
  • $\begingroup$ I see, from what I gather, its better not to use the matrix_local at all. The original reason for this question was for an add-on game engine called Armory3D in Blender. This add-on exports matrix_local parameter as the local matrix of each object in the Scene. For me I find it intuitive using a matrix_local than matrix_basis, since it is very straightforward to modify and also obtain the global matrix. Hence was exploring a way to compose decompose the local matrix in a better way. $\endgroup$ – QuantumCoderQC May 27 at 7:48
  • $\begingroup$ AFAIK there's no issue with exporting local matrix. The question was related to decomposing and recomposing. The "elephant in the room" is matrix_parent_inverse which if identity will be no issue. $\endgroup$ – batFINGER May 27 at 8:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.