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
    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
    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
    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
    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
    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$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.