7
$\begingroup$

I am new to Blender (and to some extent to python). I have pieced together this function that rotates and scales an Object at low level by modifying the matrices:

def rotate_scale_object(inObj, angle, axis, scaling):
        rot_mat = Matrix.Rotation(radians(angle), 4, axis)
        orig_loc, orig_rot, orig_scale = inObj.matrix_world.decompose()
        orig_loc_mat   = Matrix.Translation(orig_loc)
        orig_rot_mat   = orig_rot.to_matrix().to_4x4()
        orig_scale_mat = (Matrix.Scale(scaling,4,(1,0,0)) @ Matrix.Scale(scaling,4,(0,1,0)) @ Matrix.Scale(scaling,4,(0,0,1)))
        # assemble the new matrix
        inObj.matrix_world = orig_loc_mat @ rot_mat @ orig_rot_mat @ orig_scale_mat
        #update_matrices(inObj)
        return inObj

I call it in my script as follow:

obj = bpy.context.active_object
obj = scale_rotate_smooth_color(obj, angle = -90, axis = "X", scaling = 0.01)

It works very well: the object is rotated & scaled as expected. Yet, I am missing a way to "apply" these transformations to the object, as one would do by pressing Ctrl + A and select Apply all transformations.

Is there a way to do this "low level", without resorting to bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)?

$\endgroup$
2
  • $\begingroup$ Thanks for the suggestion. I will definitively implement it this way. $\endgroup$
    – amaizel
    Commented Dec 1, 2019 at 16:00
  • $\begingroup$ obj.data.transform(obj.matrix_world) obj.data.update() matrix = Matrix.Identity(4) obj.matrix_world = matrix $\endgroup$
    – barakooda
    Commented Jun 14, 2021 at 14:20

1 Answer 1

21
$\begingroup$

Mesh transform

Does a child object inherit the matrix from the parent?

Applying the transform.

  • Set the basis matrix value to identity for any or all of location, rotation scale.

  • Transform the mesh and or child objects to reflect the change

For all transforms.

import bpy

context = bpy.context

ob = context.object
mb = ob.matrix_basis
if hasattr(ob.data, "transform"):
    ob.data.transform(mb)
for c in ob.children:
    c.matrix_local = mb @ c.matrix_local
    
ob.matrix_basis.identity()

To apply only rotation.

Similarly will show the process of only applying rotation. Transform the data and children such that the rotation part of matrix basis is identity.

Method below uses the Matrix.decompose to reduce the transform into its three components. The rotation is defined as a quaternion. Another way to decompose is discussed in

How to Decompose and Compose the local transform matrix

import bpy
context = bpy.context
from mathutils import Matrix

ob = context.object
mw = ob.matrix_world
mb = ob.matrix_basis

loc, rot, scale = mb.decompose()

# rotation
T = Matrix.Translation(loc)
R = rot.to_matrix().to_4x4()
S = Matrix.Diagonal(scale).to_4x4()

if hasattr(ob.data, "transform"):
    ob.data.transform(R)

for c in ob.children:
    c.matrix_local = R @ c.matrix_local
    
ob.matrix_basis = T @ S

For rotation and scale

...:
    ob.data.transform(R @ S)

for c in ob.children:
    c.matrix_local = (R @ S) @ c.matrix_local
    
ob.matrix_basis = T

ie set the basis to make identity of applied property, transform mesh and children by the jconverse. eg applying all is transforming by T @ R @ S and setting to I @ I @ I transform & scale T @ I @ S --> I @ R @ S (where I is identity matrix)

Method for all or any

import bpy
from mathutils import Matrix

def apply_transfrom(ob, use_location=False, use_rotation=False, use_scale=False):
    mb = ob.matrix_basis
    I = Matrix()
    loc, rot, scale = mb.decompose()

    # rotation
    T = Matrix.Translation(loc)
    #R = rot.to_matrix().to_4x4()
    R = mb.to_3x3().normalized().to_4x4()
    S = Matrix.Diagonal(scale).to_4x4()

    transform = [I, I, I]
    basis = [T, R, S]

    def swap(i):
        transform[i], basis[i] = basis[i], transform[i]

    if use_location:
        swap(0)
    if use_rotation:
        swap(1)
    if use_scale:
        swap(2)
        
    M = transform[0] @ transform[1] @ transform[2]
    if hasattr(ob.data, "transform"):
        ob.data.transform(M)
    for c in ob.children:
        c.matrix_local = M @ c.matrix_local
        
    ob.matrix_basis = basis[0] @ basis[1] @ basis[2]


# test call
apply_transfrom(bpy.context.object, use_rotation=True)

Recent answer using Mesh.transform Problem with rotating objects by script

$\endgroup$
12
  • $\begingroup$ Exactly what I was missing. Thanks a lot. $\endgroup$
    – amaizel
    Commented Dec 1, 2019 at 16:07
  • $\begingroup$ cheers, prob should have used mult by inverse instead of setting to identity. eg to apply rotation make a rotation matrix, R apply it to mesh me.transform(R), then mult matrix_world by R.inverted() $\endgroup$
    – batFINGER
    Commented Dec 1, 2019 at 16:15
  • $\begingroup$ This didn't work for me, which is why I asked another question. But it got closed. I wanted to apply rotations to my meshes after adding rotation eulers. In the end I rewrote my code to perform the rotation directly on the meshes instead of using bpy.ops. $\endgroup$
    – DrewTNBD
    Commented Jun 30, 2021 at 11:05
  • 1
    $\begingroup$ Apologies, fixed and fixed. Probably relates to comment above also. Edited this one re-opened other. $\endgroup$
    – batFINGER
    Commented Jun 30, 2021 at 13:24
  • $\begingroup$ Nice one! Thanks for the edits! $\endgroup$
    – DrewTNBD
    Commented Jun 30, 2021 at 14:28

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .