# Set the color of faces in python efficiently

I have two cubes obj1 and obj2 and I have a list which has a pair of face numbers of each object. So for example, mylist looks like that [(1,5),(2,5),(2,3),(1,4),(2,1)], where (1,5) represents face number 1 in obj1 and face number 5 in obj2, and (2,5) represents face number 2 in obj1 and face number 5 in obj2 and so on.

I wonder how using python I can assign a material of color red to those faces in the list, while the rest of the faces not in the list will be assigned a metrial of color green.

I figured a way to do it in my below answer but it's very slow, so I wonder if it can be more efficient and done in a more efficient way as I run this function every frame.

• Please add more context. Cycles, Internal, Vertex Color...? One material or multiple materials? Related: blender.stackexchange.com/a/8549/3710 – p2or Oct 21 '16 at 8:45
• @poor thanks for your comment, I've posted an answer below, but it's slow, so anyway it can be faster? – Tak Oct 21 '16 at 8:47

This seems almost instantaneous and much faster than the original function above:

import bpy, time
from random import randint

start = time.time()

def paint( obj1, obj2, mat1, mat2, pairs ):
# First assign the 2nd material as the active, default material for all faces
obj1.active_material = mat2
obj2.active_material = mat2

# Add mat1 to both objects as the 2nd material slot (index=1) if not already included
if mat1.name not in obj1.data.materials:
obj1.data.materials.append( mat1 )
if mat1.name not in obj2.data.materials:
obj2.data.materials.append( mat1 )

# Now iterate over the pairs and set material 1 as the active material for the selected faces
for p in pairs:
i1, i2 = p
obj1.data.polygons[i1].material_index = 1
obj2.data.polygons[i2].material_index = 1

obj1polygons = [ p[0] for p in pairs ]
obj2polygons = [ p[1] for p in pairs ]

# Set mat1 as the material for all polygon indices not in the pairs list
for i in range( len( obj1.data.polygons ) ):
if i not in obj1polygons:
obj1.data.polygons[i].material_index = 0

for i in range( len( obj2.data.polygons ) ):
if i not in obj2polygons:
obj2.data.polygons[i].material_index = 0

faceCount = 512
redFaces  = 50

pairs = [ ( randint(0, faceCount - 1 ), randint(0, faceCount - 1 ) ) for i in range( redFaces ) ]

mat1 = bpy.data.materials['mat_one']
mat2 = bpy.data.materials['mat_two']
obj1 = bpy.data.objects['obj1']
obj2 = bpy.data.objects['obj2']

paint( obj1, obj2, mat1, mat2, pairs )
print( "Ran for: ", round( time.time() - start, 5 ) )

• thanks for your answer. But there are two problems with this solution if I run the script couple of times. First, more materials will be added to the objects (and this is why in my answer I included this check), second, if I run the script one time and some faces turns into red, then I run the script second time with zero pairs, I want all faces to be back to green, but here the ones from the previous run remains red. – Tak Oct 21 '16 at 10:19
• @Tak, changes the script accordingly. Run a test and let me know if it works as expected. – TLousky Oct 21 '16 at 16:08
• @TLousky I want to go over all materials and select (or get indices of) faces to which that specific material has been applied to. I haven't been able to find a good solution for doing that as of now. I would appreciate if you can take a look at my question here and see if you can offer a solution? – Amir Mar 17 '18 at 4:38

I figured out a way to do it as shown below, but it' very slow, would appreciate any other answers to make it more efficient as I'm sure there should be a way to make it more efficient as I run it every frame.

def paint(pairs):
#check if materials are already existing or not and add them if not
mat_one = bpy.data.materials.get("mat_one")
if mat_one == None:
mat_one = bpy.data.materials.new("mat_one")
mat_one.diffuse_color = (0,1,0)

mat_two = bpy.data.materials.get("mat_two")
if mat_two == None:
mat_two = bpy.data.materials.new("mat_two")
mat_two.diffuse_color = (1,0,0)

mat_exist = False

# check if any of the slots of the two objects has the same material
for obj in bpy.data.objects:
for slot in obj.material_slots:
if slot.material == mat_one or slot.material == mat_two:
mat_exist = True
if mat_exist == False:
bpy.data.objects['obj1'].data.materials.append(mat_one)
bpy.data.objects['obj1'].data.materials.append(mat_two)
bpy.data.objects['obj2'].data.materials.append(mat_one)
bpy.data.objects['obj2'].data.materials.append(mat_two)
#if no pairs I want the whole two meshes to be green, if pairs have value then color only the faces in the pair list with red
if pairs == []:
for poly in bpy.data.objects['obj1'].data.polygons:
poly.material_index = 0
for poly in bpy.data.objects['obj2'].data.polygons:
poly.material_index = 0
else:
for a, b in pairs:
bpy.data.objects['obj1'].data.polygons[a].material_index = 1
bpy.data.objects['obj2'].data.polygons[b].material_index = 1

return