getting objects to properly face towards normals

I wrote this little addon: https://gist.github.com/SuddenDevelopment/16df6602a2ecab06f56dc8a2c7e4c9e0

and at first I thought it was doing great, but have found a bunch of scenarios where the area lights face the wrong direction.

I try to get the normals for a face, move rotate the light to match that face and then face it towards the normal, but it works for roughly half of the faces when it come to facing the proper direction. If I play with which axis are in the rotation by quartnerion I get different ones that are correct, I also tried .inverted()

I dont even know if I'm going the right direction. help appreciated.

Matrix centric approach

Code to place a lamp object at center of each selected face of a mesh, with light local -Z axis pointing in direction of face normal.

Find the global direction of a face normal by premultiplying with the objects global rotation matrix. (No scale).

import bpy
import bmesh
context = bpy.context

ob = context.edit_object
mw = ob.matrix_world
me = ob.data
bm = bmesh.from_edit_mesh(me)

faces = [f for f in bm.faces if f.select]

while faces:
f = faces.pop()
light = bpy.data.lights.new(
f"Face{f.index}",
type='AREA',
)
light.size = 1
light_ob = bpy.data.objects.new(
f"Face{f.index}",
light,
)
M = mw.normalized() @ f.normal.to_track_quat('-Z', 'Y').to_matrix().to_4x4()
M.translation = mw @ f.calc_center_median()
light_ob.matrix_world = M


Local with parenting.

The above code adds the lights in the scene at the global orientation of the face. If the mesh is moved the lights do not go along with it.

Another option would be to use only local coordinates and make each light a child of the mesh.

Scaling to fit the face.

It appears you are also scaling the area light to fit the face. To do this when not axis aligned is a bit more in depth than finding axes minima / maxima. IMO quite possibly warrants another question

Some examples that use another vector to align. The face has some calc tangent methods, based on longest edge, edges. Can adjust by rotating light about its local z axis (the face normal) to match the tangent. This way have one known axis aligned with an edge and can scale to match.

https://blender.stackexchange.com/a/94047/15543

Align a face normal to Z axis.

https://blender.stackexchange.com/a/121227/15543

similarly in this one the poly interior angles maps the corner verts to 2d. Similarly here if the face normal is aligned to an axis the coords can be mapped to 2d.

https://blender.stackexchange.com/a/203355/15543

If the coords are in 2d there are some helper methods (primarily for UV) that fit rectangles.

Using mathutils.geometry.box_fit_2d

• WOW, thanks so much for taking this time. I'm going to apply all that you said here and give it the time it deserves, I know I'll be learning something no matter what. It'll be a few hours before I can test it. Commented Dec 16, 2020 at 17:26
• the orientation worked perfectly. I couldnt use a lot of the way you did things more concisely because it messed up the resize stuff I had going on. I learned a bunch and will dive deeper into the matrix centric stuff. I have a similar issue in another addon I think this may fix. thanks a lot. Commented Dec 17, 2020 at 16:28