I'm trying to Orient my gizmo according to the normals, but I can't do it exactly. Sometimes perfect, sometimes not. Can you tell me what's wrong? enter image description here enter image description here enter image description here

ob = position_matrix()
orig_loc, orig_rot, orig_scale = ob.decompose()

uniques = bpy.context.objects_in_mode_unique_data

# Selected Object(EDIT_MODE)  
bms = {}
for obj in uniques:
    bms[obj] = bmesh.from_edit_mesh(obj.data)

# Selected Vertex
verts = []
for obj in bms:
    verts.extend([v for v in bms[obj].verts if v.select]) 

# Get Faces Normal
faces_normal = []
for vert in verts:
    faces_normal.extend([f.normal for f in vert.link_faces]) 

# Delete Duplicate Vector
def del_duplicate(list):
    newList = []
    for i in list:
        if i not in newList:
    return newList

# Tangent 
faces_tang = []
for vert in verts:
    faces_tang.extend([f.calc_tangent_edge() for f in vert.link_faces])  

faces_normal_Sort = del_duplicate(faces_normal)
faces_tang_Sort = del_duplicate(faces_tang)

normal_vector = Vector(sum(faces_normal_Sort, Vector())).normalized()
tangent_vector = Vector(sum(faces_tang_Sort, Vector())).normalized()

axis_dst = Vector((0, 0, 1)) 
axis_dst2 = Vector((0, -1, 0))

vec1 = normal_vector @ ob.inverted()
matrix_normal = axis_dst.rotation_difference(vec1).to_matrix().to_4x4() #@ 

vec2 = tangent_vector @ ob.inverted()
matrix_tang = axis_dst2 @ matrix_normal.inverted()
mat_tmp = matrix_tang.rotation_difference(vec2).to_matrix().to_4x4()

matrix_rotate = mat_tmp @ matrix_normal

matrix_rotate = matrix_rotate.to_4x4() 

1 Answer 1


Track to Quaternion

To align to vertex normal.

The normal orientation with respect to vertices appear to be a track to quaternion, tracking Z in direction of normal, and using -Y as up. Since the only options for up in Vector.to_track_quat(to, up) are 'X', 'Y', 'Z' track with Y up and inverting the scale in X and Y.

enter image description here

Simple example. Run in object mode, adds an empty displayed as arrows, at each vertex aligned to normal.

import bpy
from mathutils import Matrix

context = bpy.context

ob = context.object
mw = ob.matrix_world
me = ob.data
Sx = Matrix.Scale(-1, 4, (1, 0, 0))
Sy = Matrix.Scale(-1, 4, (0, 1, 0))

S = Sx @ Sy

for v in me.vertices:
    R = v.normal.to_track_quat('Z', 'Y').to_matrix().to_4x4()
    R.translation = mw @ v.co

    mt = context.object
    mt.empty_display_type = 'ARROWS'
    mt.matrix_world = R @ S


Align to face normal vector

  • $\begingroup$ thank you for your answer, it helped me. But I do not quite understand how to get the correct calculation of the normal vector when I have selected several vertices? Now I do it like this matrix_normal = Vector(sum(listVertexNormal, Vector())).to_track_quat('Z', 'X').to_matrix().to_4x4(). Your approach works, but only for one selected vertex. $\endgroup$ Feb 21, 2020 at 16:31

You must log in to answer this question.

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