Skip to main content
added 348 characters in body
Source Link
CodeManX
  • 29.7k
  • 3
  • 92
  • 131

Another variant:

        loc = mat_world * Vector(poly.center)
        # use loc as co for lamp_add operator here
        quat = poly.normal.to_track_quat('-Z', 'Y')
        quat.rotate(mat_world)
        bpy.context.object.matrix_world *= quat.to_matrix().to_4x4()

Another variant:

        loc = mat_world * Vector(poly.center)
        # use loc as co for lamp_add operator here
        quat = poly.normal.to_track_quat('-Z', 'Y')
        quat.rotate(mat_world)
        bpy.context.object.matrix_world *= quat.to_matrix().to_4x4()
added 348 characters in body
Source Link
CodeManX
  • 29.7k
  • 3
  • 92
  • 131

As ideasman_42, suggested, to_track_quat() can be used to do the job, too:

        quat = poly.normal.to_track_quat('-Z', 'Y')
        loc = Matrix.Translation(poly.center)
        mat = mat_world * loc * quat.to_matrix().to_4x4()
        bpy.context.object.matrix_world = mat

It's not much code less, but more understandable I hope.

As ideasman_42, suggested, to_track_quat() can be used to do the job, too:

        quat = poly.normal.to_track_quat('-Z', 'Y')
        loc = Matrix.Translation(poly.center)
        mat = mat_world * loc * quat.to_matrix().to_4x4()
        bpy.context.object.matrix_world = mat

It's not much code less, but more understandable I hope.

Source Link
CodeManX
  • 29.7k
  • 3
  • 92
  • 131

There's probably a cleaner solution, but for the time being, you can use:

import bpy
from math import radians
from mathutils import Vector, Matrix

obj = bpy.context.active_object
mesh = obj.data


def get_layers():
    """
    return: tuple of 20 booleans. True if layer is active layer,False if not
    Allows the mesh to be created on the active layer. There is only 1 active layer
    """
    active_layer = bpy.context.scene.active_layer
    layers = [False] * 20
    layers[active_layer] = True
    return tuple(layers)


def create_light_at_face():
    """
    layers: tuple. 20 booleans where exaclty one boolen is True
    return: None
    creates an area light the has the same orientation as the normal at the center of each face
    """
    mat_world = obj.matrix_world
    up = Vector((0,0,1))
    
    for poly in mesh.polygons:
        co = mat_world * Vector(poly.center)
        
        forward = poly.normal.copy()
        forward.rotate(mat_world)
        right = forward.cross(up).normalized() # Vector.length closer to 1.0
        up = right.cross(forward)
    
        rot = Matrix((right, up, -forward)).transposed().normalized().to_4x4()
        mat = Matrix.Translation(co) * rot
    
        bpy.ops.object.lamp_add(type='AREA', view_align=False, location=co, layers=get_layers())
        # scale lamp up a little fo we can see the area
        bpy.context.object.data.size = 3
        bpy.context.object.matrix_world = mat

create_light_at_face()

Also tried eulers, but did not work reliably (some lamps pointing inwards).