face.center in world coordinates

I want to create a point light for each face of a mesh with a specific material.

My first attempt looked something like this:

for face in obj.data.faces:
    mat = bpy.data.materials[face.material_index]
    if mat.name == 'Lamp'
        bpy.ops.object.lamp_add(location=face.center)

Besides the fact that the script did not create any point lights, the locations all would have been wrong anyway (i guess face.center is in local coordinates?). I actually found a small article about getting the world coordinates of a vertex on a bezier curve, and tried the proposed solution in my script:

wmtx = obj.matrix_world

for face in obj.data.faces:
    mat = bpy.data.materials[face.material_index]
    if mat.name == 'Lamp'
        loc = face.center * wmtx
        bpy.ops.object.lamp_add(location=loc)

Yet the calculated locations are still nowhere near any face of my mesh :confused:

Can you toss us your example .blend with the code you have so far (and instructions if the undesired/desired effects are not obvious)?
-rking

hi, try matrix * vector, I mean: loc = wmtx * face.center

edit: and for the script I think you are mixing indices and names…
this should do the trick:


import bpy
obj = bpy.context.object

i = obj.material_slots.find('Lamp')
if i != -1:

    wmtx = obj.matrix_world
    for face in obj.data.faces:
        if face.material_index == i:
            loc = wmtx * face.center
            bpy.ops.object.lamp_add(location=loc)

It works! :cool:

my code now looks like this:

for obj in bpy.data.objects:
    if obj.type == 'MESH':
        obj.name = obj.data.name
        wmtx = mathutils.Matrix(obj.matrix_world)
        wmtx.invert()
        
        face_list = [f for f in obj.data.faces if obj.material_slots[f.material_index].name == 'Torch']
        
        for face in face_list:
            norm = mathutils.Vector(face.normal)
            if norm.dot((0,0,1)) == 1:
                bpy.ops.object.add(type='LAMP',location=face.center*wmtx)

There were actually two problems:

  1. it seems that the world matrix needs to be inverted (i guess that’s because my objects are rotated around the Z axis!?)

  2. bpy.data.materials is not in the same order as obj.material_slots, so my script always selected faces with the wrong material. I also tried “material_slots.find()” but blender doesn’t know that method, so i came up with the solution above.

There is probably a much easier way to do this, but since those are my very first steps in python i’m just happy that it works.

ohoh, in the bmesh blender (the new one!) the obj.data.faces is empty( for the default cube) there are loops etc.
meaning the Api has changed considerably!!!
Maybe like this: obj.data.polygons[0].normal where the polygon replaces the normals?? At least the command works, and the cube
has 6 polygons … looks good the normal-vectors are indeed (rounding errors!) the six different normals :wink:

By the way, I would write wmtx = mathutils.Matrix(obj.matrix_world)
wmtx = obj.matrix_world.copy()
using the copy
and in old Blender face.normal is already a Vector so you could write at once
face.normal.dot((0,0,1))
:wink: