3
$\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
    Dec 1 '19 at 16:00
  • $\begingroup$ obj.data.transform(obj.matrix_world) obj.data.update() matrix = Matrix.Identity(4) obj.matrix_world = matrix $\endgroup$
    – barakooda
    Jun 14 at 14:20
9
$\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$
11
  • $\begingroup$ Exactly what I was missing. Thanks a lot. $\endgroup$
    – amaizel
    Dec 1 '19 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
    Dec 1 '19 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
    Jun 30 at 11:05
  • 1
    $\begingroup$ Apologies, fixed and fixed. Probably relates to comment above also. Edited this one re-opened other. $\endgroup$
    – batFINGER
    Jun 30 at 13:24
  • $\begingroup$ Nice one! Thanks for the edits! $\endgroup$
    – DrewTNBD
    Jun 30 at 14:28

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.