0
$\begingroup$

When I select the Mesh (scaled by empty), hit Alt+P choose "Clear Parent and Keep Transformation", it clears parenting, but transformation is not kept.

Is there any visible reason why the operator is not working on this specific model (blend file, collection 02)? What kind of hidden setup can cause this issue?

For now it seems like different rotation (mesh and empty), cause the issue. If empty and mesh is the same rotation (collection 01 and 03) Alt+P works fine. Since Empty is rotated it fails (see collection 02).

enter image description here

$\endgroup$
  • $\begingroup$ Scale can not be propagated to children, since Blender doesn't support shearing. $\endgroup$ – Leander Oct 7 '19 at 19:00
  • 1
    $\begingroup$ Leander, I'm not sure what you mean, if you parent a cube to an empty, scale the empty, the cube will scale as well. If you unparent the cube with the Keep Transformation option, it should keep its inherit scale. It's not happening here, why that? $\endgroup$ – moonboots Oct 8 '19 at 6:44
  • $\begingroup$ @moonboots Only if the scaling is uniform on all axes (e.g. 3, 3, 3). Any "non-uniform" (don't know a better word) scale like 1, 2, 3 combined with a rotation would apply into a sheared matrix, which Blender doesn't support. (Search for shear.) $\endgroup$ – Leander Oct 8 '19 at 12:54
  • $\begingroup$ ok so maybe give a full explanation as answer ;) $\endgroup$ – moonboots Oct 8 '19 at 12:55
  • $\begingroup$ @ Leander: Scaling 1,2,3 itself doesn't seem to be a problem, so since a child is in angle against a parent, it results in "shearing" transformation, right? But since there is not a problem to transform mesh in some result as child, what's the problem to let blender regenerate vertecies in the same exact position? $\endgroup$ – vklidu Oct 8 '19 at 13:29
3
$\begingroup$

Blender does not support shear or skew transformations, thats why the state you request can not be reached on object/transform level but only on data/mesh level. I have added a possible workaround at the end of the post.

Translation, Rotation, Scale

First, let's confirm that Blender only supports translation, rotation and scaling transformations. Select the untransformed default cube.

Open up the Blender Python console, type bpy.context.object.matrix_world and press enter.

python console, matrix.world

Cool, we are confronted by 16 numbers of a matrix. bpy.context.object is simply a reference to this object. matrix_world is the transformation matrix. A transformation matrix contains the objects transformation: translation, rotation, resizing, shearing and skewing.
Look at these slides for a brief overview or this beautifully crafted blog post for a more detailled explanation.
The _world part in matrix_world simply refers to the resulting matrix with all parents transformations applied. Since the default cube has no parents, we could also use matrix_local.

We can not only output the matrix of the default, we can also assign it using python. Obviously we usually only work with partial components of the transformation matrix of object (tranlation, rotation, scale) since the matrix is not very intuitive. Open Blenders Text Editor and paste the following script.

import bpy
import mathutils
from math import cos, sin, pi

mat = mathutils.Matrix( [ [1, 0, 0, 0],
                          [0, 1, 0, 0],
                          [0, 0, 1, 0],
                          [0, 0, 0, 1] ])

ob = bpy.context.object
ob.matrix_world = mat

Here we assign a matrix to the active object. Pressing ⎇ AltP or the Run Script button will assign this matrix to the cube. We don't see a change, because this is the so called identity matrix, which doesn't transform the object (or it transforms all points in place).
However, we can do simple transformation with a matrix via python code. Here are three examples (for translation, rotation and scale) which you can replace in the above code snippet. The results are seen in the image below.

# translate x by -1
mat = mathutils.Matrix( [ [1, 0, 0, -1],
                          [0, 1, 0, 0],
                          [0, 0, 1, 0],
                          [0, 0, 0, 1] ])
# rotate z by pi/8 (~22.5°)
mat = mathutils.Matrix( [ [cos(pi/8), -sin(pi/8), 0, 0],
                          [sin(pi/8), cos(pi/8), 0, 0],
                          [0, 0, 1, 0],
                          [0, 0, 0, 1] ])
# scale y by 2
mat = mathutils.Matrix( [ [1, 0, 0, 0],
                          [0, 2, 0, 0],
                          [0, 0, 1, 0],
                          [0, 0, 0, 1] ])

results

Shearing or Skewing

As far as I understand, the following matrix should shear the object along the objects X axis. You may be familiar with Shearing from Edit Mode, where you can shear points with ⎈ Ctrl⇧ Shift⎇ AltS.

[1, 1, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]

However, in the 3D view, we can see, that assigning this matrix with the code snippet from earlier will only scale and rotate the object. What happened? I guess, Blender is trying to show the transformation in the GUI, converted it to TRS and displayed it in the Transform panel. The other parts of the matrix were ignored and the matrix reconstructed without the shear/skew.
In fact, if we go into the python console and execute bpy.context.object.matrix_world it shows an entirely different matrix, than what we just inserted.

<Matrix 4x4 ( 0.9210,  0.5395, 0.1117, 0.0000)
            (-0.3815,  1.1907, 0.5395, 0.0000)
            ( 0.0790, -0.5395, 1.3025, 0.0000)
            ( 0.0000,  0.0000, 0.0000, 1.0000)>

What can happen when unparenting?

When unparenting an object, it can require a transformation matrix, which includes shear/skew and can't be expressed with just TRS.

Let's look at an example, where everything is fine first. The Empty is the parent of the Cube.
The empty is scaled to 0.5as can be seen in the Transform panel.
The Cube is scaled to 2 on the Y-Axis. parent-relation

Even before doing any unparenting, we can figure out, what's going to happen. The Cube must have a scale of 0.5, 1.0, 0.5 to remain in its current transformation.

We can also figure this out, by multiplying the matrices.

[ 0.5 0   0   0    ]         [ 1   0   0   0    ]       [ 0.5 0   0   0  ]
[ 0   0.5 0   0    ]    x    [ 0   2   0   0    ]   =   [ 0   1   0   0  ]
[ 0   0   0.5 0    ]         [ 0   0   1   0    ]       [ 0   0   0.5 0  ]
[ 0   0   0   1    ]         [ 0   0   0   1    ]       [ 0   0   0   1  ]

This is the same as what Blender would tell us when calling matrix_world of the child Cube.

>>> bpy.data.objects['Cube'].matrix_world
Matrix(((0.5, 0.0, 0.0, 0.0),
        (0.0, 1.0, 0.0, 0.0),
        (0.0, 0.0, 0.5, 0.0),
        (0.0, 0.0, 0.0, 1.0)))

In this second case the cube is rotated by an angle which is not a multiple of 90° and the empty has a non-uniform scale of 1, 0.5, 0.5.

transformations

[ 1   0   0   0    ]         [ 0.7 1.4 0   0    ]       [ 0.7 1.4 0   0  ]
[ 0   0.5 0   0    ]    x    [-0.7 1.4 0   0    ]   =   [-0.3 0.7 0   0  ]
[ 0   0   0.5 0    ]         [ 0   0   1   0    ]       [ 0   0   0.5 0  ]
[ 0   0   0   1    ]         [ 0   0   0   1    ]       [ 0   0   0   1  ]

Even though we can't see it in the numbers, we end up with a transformation matrix which is not representable with only translation, rotation and scale.
Unparenting the cube results in it's shear changing.
needed scale
And it's obvious, that we can't scale the right cube towards the shape of the left. We need to scale along the green arrow, but there is no axis of the cube which points in that direction. If the rotations were done at 90° angles, this would not be a problem. Again ...

Most transformations can not be reached using only location, rotation and scale.

But wait, right before unparenting, we were able to see the correct transformation matrix using matrix_world of the child Cube, right? Yes, and that is a way to "apply" the transformation to the points.

What happens to the matrix when unparenting?

How does the matrix change when unparenting since shear and skew are ignored?

matrix values

We can reproduce this process by extracting TRS from matrix_world with python and the mathutils module.

  1. Get the matrix_world of the child Cube.
  2. Extract the location, rotation and scale using the the matrix' decompose() function.
  3. Assemble a new matrix from the location, rotation and scale.

The result will be the same matrix as the one we receive, when we unparent the Cube. It is different from the original matrix.

import bpy
import mathutils

mat = bpy.context.object.matrix_world

loc, rot, sca = mat.decompose()

mat_loc = mathutils.Matrix.Translation(loc)
mat_rot = rot.to_matrix().to_4x4()
mat_sca = mathutils.Matrix.Identity(4)
mat_sca[0][0], mat_sca[1][1], mat_sca[2][2] = sca

mat_out = mat_loc @ mat_rot @ mat_sca
print(mat_out)

We want the points of the mesh to be transformed by the original mat, but we are left with the reduced mat_out after the transformation.

The Workaround

We want to apply the missing transformation directly to the points, so that the unparented resulting transformation matrix is "enough" to reproduce the original transformation.
Using the naming from the previous code snippet:

mat_out @ mat_h = mat

mat_h is the missing part of the transformation, which is needed to return to the original transformation (mat). We can get it's value with the following line:

mat_h = mat_out.inverted() @ mat

And we can apply this to the cube, before unparenting is with a short script.

import bpy
import mathutils

ob = bpy.context.object
mat = ob.matrix_world

loc, rot, sca = mat.decompose()

mat_loc = mathutils.Matrix.Translation(loc)
mat_rot = rot.to_matrix().to_4x4()
mat_sca = mathutils.Matrix.Identity(4)
mat_sca[0][0], mat_sca[1][1], mat_sca[2][2] = sca

mat_out = mat_loc @ mat_rot @ mat_sca
mat_h = mat_out.inverted() @ mat

# Unparent the object.
bpy.ops.object.parent_clear(type='CLEAR_KEEP_TRANSFORM')

# Move the vertices to their original position,
# which the mat_out can't represent.
for v in ob.data.vertices:
    v.co = mat_h @ v.co

Paste this script in Blenders script editor and press Run Script or ⎇ AltP. It will unparent the mesh and apply the transformations, which can no longer be represented by location, rotation and scale, directly to the points.

$\endgroup$
  • $\begingroup$ Killing answer !!! :) You are number one, I hope you are in touch with devs, so there is a chance to get the result natively without workarrounds. Works in glance. Thank you for your time. $\endgroup$ – vklidu Oct 9 '19 at 13:49
  • $\begingroup$ Thank you for your kind words! While this would probably be a cool feature, I think that it wouldn't be intuitive if unparenting changed your vertices (at geometry level). $\endgroup$ – Leander Oct 9 '19 at 18:48
  • $\begingroup$ So what about to rename it to “Clear Parent and Apply Transformation”? Like that transformation can be applied on object level as well (now it is a mix). Seems to me clear, right? Or add it as a new operator, because like now - end user primarily needs object in specific shape. Current blenders posibilities does not fits to this. $\endgroup$ – vklidu Oct 10 '19 at 8:42

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.