# Rotate origin only to match other object rotation

import bpy, bmesh
from mathutils import Euler
import math
import mathutils

active = bpy.context.scene.objects['Suzanne']
obj = active.rotation_euler

for o_sel in bpy.context.selected_objects:
selected = o_sel.rotation_euler

for o in bpy.context.selected_objects:

if obj[-1] == selected[-1]:
print('same angle')
pass

else:
o.rotation_euler = (0, 0, obj[-1])
mesh = bpy.context.object.data

bpy.ops.object.editmode_toggle()

bm = bmesh.from_edit_mesh(mesh)
bmesh.ops.rotate(bm, verts=bm.verts, cent=(0.0, 0.0, 0.0), matrix=mathutils.Matrix.Rotation(-obj[-1], 4, 'Z'))
bmesh.update_edit_mesh(mesh)
bm.free()

bpy.ops.object.editmode_toggle()


In Blender 2.93, let's say that I have one active object and I want all the selected objects rotate only their origin to match the rotation of the active one. After couple of hours I have faced a roadblock, where I can get the rotation of the active object only and then loop through the selected ones leaving the active out from the loop, but after that I can't find the way for the selected ones to rotate their origins only.

UPDATE: I manage to make it work somehow, if I select on object and ru the script, it will work as it should be, but if I have more than one object selected it doesn't work. I have tried different loops combination without avail. The second issue is if I replace the scene object for any active one, even the active will rotate which is not right.

UPDATE 2: Here is a gif showing the script in action and the problem when selecting multiple objects. As you can see, the monkey has the axis angle that I want the other objects to have, when selecting the Torus and run the script it works, the same goes when I select the Cube, but if I select both, the script is not working properly.

In the second video if I have an active object the selected ones will not rotate.

• This can be done via the UI.. or do you want this as part of a script? Jul 12 '21 at 11:41
• I'm looking to make it part of a script. Jul 12 '21 at 11:48
• To clarify, is this is akin to adding a copy rotation constraint to each of selected objects targeting active?. Setting constraint space to GLOBAL will make all axes appear oriented the same. Setting the space to LOCAL is more akin to what you are attempting above, but may not be the desired result, depending on parenting etc. Jul 12 '21 at 12:55
• When using a constrain it doesn't change the origin only, it rotates the whole mesh. For my case I'm not looking to create an interactive tool, just a script where selected objects will rotate their origins (not mesh) to the same angle than the active one. The idea behind this is that receiving files (from archicad) the origin orientation of many objects are different and I'm looking for a way to make them rotate to the same angle to be able to link the object data. Jul 13 '21 at 4:59
• Some constructive criit (and possibly answers to observations in q) re multiple issues with script in question. mesh is always that of the context object, IMO never use if float == float test. (abs(float - float) < TOL) It Requires objects to be in Euler rot mode, using obj for a rotation variable is somewhat confusing, if rot = Euler() then rot.z is z rotation. The bmesh rotation is only ever around Z axis. No need for edit mode. +1 for illustration lol. Jul 14 '21 at 19:40

Match local axes.

Ok it appears you wish to match the local axes of selected to active object.

• The local X, Y, Z axes in global coordianates are the columns of the rotation part of the world matrix

 R = ob.matrix_world.to_3x3().normalized()

• Note Have not allowed for non uniform scale, so apply scale first. Could fix, was noticed after posting.

• Use the rotation part of the matrix world for all other objects. But keep there translation, this will align all local axes, but will noticeably rotate them.

• Transform each selected objects mesh by rotating back the local space equivalent of the global rotation. (mw here is the world matrix of the selected object)

 Rloc = mw.to_3x3().normalized().to_4x4().inverted() @ R

• Since the mesh is being transformed, ensure that each object has a unique mesh.

Test script. Run in object mode, changes local rotation of selected to active.

import bpy
from mathutils import Matrix
from bpy import context

ob = context.object
mw = ob.matrix_world
# rotational part
R = mw.to_3x3().normalized().to_4x4()

for o in context.selected_objects:
if o is ob:
continue
mw = o.matrix_world
Rloc = mw.to_3x3().normalized().to_4x4().inverted() @ R
o.matrix_world = (
Matrix.Translation(mw.translation) @
R @
Matrix.Diagonal(mw.to_scale()).to_4x4()
)

o.data.transform(Rloc.inverted())


Related How to properly use ExportHelper's 'axis_conversion' method? except using the active object as a basis.