I want to align an empty (Arrows) to a selected face normal with the empty's Z axis aligning to the normal direction (and location which I have working). When the target objects rotation is 0 it's fine... but if I rotate the target object and then select a face I can't work out how to add the object's matrix_world rotation to the empty's matrix_world rotation (I'm thinking this should be easy but can't solve it atm)... here's the code so far. (Add an Arrows Empty named 'Empty' and select one face on an object). any help greatly appreciated.

import mathutils
import bmesh

obj = bpy.context.edit_object
mw = obj.matrix_world.copy()
loc, rot, scale = obj.matrix_world.decompose()

bm = bmesh.from_edit_mesh(obj.data)

# select one face - testing    
for f in bm.faces: 
    if f.select:
        f_location = mw @ f.calc_center_median()
        print("location: ", f_location) #correct
        DirectionVector = f.normal 

bpy.data.objects['Empty'].rotation_mode = 'QUATERNION'
bpy.data.objects['Empty'].rotation_quaternion = DirectionVector.to_track_quat('Z','Y')
bpy.data.objects['Empty'].location = f_location

#bpy.data.objects['Empty'].matrix_world.to_euler = rot
import bpy
from bpy import context
from mathutils import Matrix, Vector
import bmesh

mw = obj.matrix_world
m = mw.copy()
mi = m.inverted()

def create_rotation_matrix_from_face(mx, face):
    normal = (mx.to_3x3() @ face.normal)
    tangent = (mx.to_3x3() @ face.calc_tangent_edge_pair())
    binormal = normal.cross(tangent)
    rot = Matrix()
    rot[0].xyz = binormal
    rot[1].xyz = normal
    rot[2].xyz = tangent
    return rot.transposed()

#apply all transforms
me.transform(mw) #Transform mesh vertices by a matrix
mw = Matrix()

bm = bmesh.from_edit_mesh(obj.data)
for face in bm.faces: 
    if face.select:
        mat_rot=create_rotation_matrix_from_face(mw, face)
        loc=mw @ face.calc_center_median() # > vector
        mat_trans = Matrix.Translation(loc)

mat=mat_trans @ mat_rot
empty=bpy.ops.object.empty_add(type='ARROWS', radius=1)

scale_empty=context.object.scale.copy() #scale will change after the matrix modification (next line)
context.object.matrix_world @= mat
#empty size
context.object.scale=scale_empty #original size
#context.object.scale=obj.scale #size of the object x,y,z 

#apply all modif back
me.transform(mi) #all zero loc rot scale
mw = m #back to start matrix
  • $\begingroup$ Thanks for this answer... but the final rotation of the empty doesn't match a selected face rotation if the object is scaled non-uniformly. The object scale is applied to the empty, but the empty's orientation is already aligned with the face so it effects it incorrectly... perhaps applying the object scale to the empty before matching the rotation would work? $\endgroup$ – Dan Feb 5 at 10:49
  • $\begingroup$ @Dan I finally modified it to correct this problem $\endgroup$ – Uneconscience UneSource Mar 15 at 17:34
  • $\begingroup$ you can choose an empty proportional to the object scale uncommenting one line $\endgroup$ – Uneconscience UneSource Mar 15 at 17:41
  • $\begingroup$ finally I inverted axis order again to be sure to have right alignement of empty with object axis. z and y were inverted... $\endgroup$ – Uneconscience UneSource Mar 15 at 18:48

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.