Closed Andrej730 closed 10 months ago
Hi,
this is an interesting topic, I hope that elaborating on your example can help a bit. There are two tricky points to keep in mind:
matrix_local
, and it doesn't necessarily take matrix_basis
into account. For instance Copy Transforms ignores matrix_basis
entirely. So matrix_local != constraints @ matrix_basis
matrix_local
, but the object hierarchy is still in place, so the following is True
, wether constraints are involved or not.print(same(obj.matrix_world, parent.matrix_world @ obj.matrix_local)) # True
matrix_local
is changed so that the result of parent.matrix_world @ obj.matrix_local
satisfies the condition of the constraint.
Child of is designed so that obj
is subject to both the transformations coming from its hierarchy (parent
) and from its target (child_of_parent
). On top of that, obj
can be moved, rotated and scaled locally: these channels are stored in matrix_basis
.
This is a World-Space
constraint, it's goal is for obj.matrix_world
to satisfy:
print(same(obj.matrix_world, child_of_parent.matrix_world @ parent.matrix_world @ obj.matrix_basis)) # True
In other words
parent.matrix_world @ obj.matrix_local = child_of_parent.matrix_world @ parent.matrix_world @ obj.matrix_basis
If those were scalars we could just divide by parent.matrix_world
and find obj.matrix_local
, but matrices are different and instead we premultiply by parent.matrix_world.inverted()
on both sides.
It's tricky if one thinks of numbers, but keeping in mind that
parent.matrix_world.inverted() @ parent.matrix_world = identity
makes it more intuitive how come the transformation
parent.matrix_world.inverted() @ parent.matrix_world @ obj.matrix_local = parent.matrix_world.inverted() @ child_of_parent.matrix_world @ parent.matrix_world @ obj.matrix_basis
is the formula for computing the matrix_local
enforced by Child of: obj.matrix_local
is the only term remaining on the left. You can see how adding this line to your script prints "True"
print(same(obj.matrix_local, parent.matrix_world.inverted() @ child_of_parent.matrix_world @ parent.matrix_world @ obj.matrix_basis)) # True
Please let me know if this reply answers to your question, congratulations on your progress!
p.
Thank you, appreciate the detailed explanation!
The main takeaways for me:
parent @ constraints @ basis
, when actually it is constraints @ parent @ basis
matrix_local
is not necessarily used in final world matrix calculation directly and it's derived later by multiplying matrix_world
by parent's inverse parent.matrix_world.inverted() @ obj.matrix_world
.
Hi! In the book it's mentioned that
obj.matrix_local
contains the local location, rotation, and scale of an object, omitting the transformation inherited by the parent object but not the one resulting from constraints. Haven't used this matrix before and end up putting some experiments to understand it.Doesn't it mean that
matrix_local
should should be equal tomatrix_basis
(contains the local location, rotation, and scale of an object before it is transformed by object constraints) multiplied by constraints matrix? Assumingmatrix_world = parent @ matrix_local
andmatrix_local = constraints @ matrix_basis
.Here's the short example to reproduce this. With my understanding I expected it to print
True
at the end, perhaps I'm missing something in understandingmatrix_local
.