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.

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] ])

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.5
as can be seen in the Transform panel.
The Cube is scaled to 2
on the Y-Axis.

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
.

[ 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.

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?

We can reproduce this process by extracting TRS from matrix_world
with python and the mathutils module
.
- Get the
matrix_world
of the child Cube.
- Extract the location, rotation and scale using the the matrix'
decompose()
function.
- 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.
3, 3, 3
). Any "non-uniform" (don't know a better word) scale like1, 2, 3
combined with a rotation would apply into a sheared matrix, which Blender doesn't support. (Search for shear.) $\endgroup$