1
$\begingroup$

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.

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

2 Answers 2

2
$\begingroup$

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 ) )
$\endgroup$
3
  • $\begingroup$ 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. $\endgroup$
    – Tak
    Commented Oct 21, 2016 at 10:19
  • $\begingroup$ @Tak, changes the script accordingly. Run a test and let me know if it works as expected. $\endgroup$
    – TLousky
    Commented Oct 21, 2016 at 16:08
  • $\begingroup$ @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? $\endgroup$
    – Amir
    Commented Mar 17, 2018 at 4:38
0
$\begingroup$

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
    #Add materials to slots       
    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
$\endgroup$

You must log in to answer this question.

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