I am using the Blender Internal render engine. I have an material slot with no material assigned to it. How can I assign a material to a slot using python? This should work in edit mode as well. Basically I would like to assign materials to specific faces.
6 Answers
This code creates 10 materials and appends it to the active object (should be a mesh). The diffuse color of every material is random. The .material_index
poperty is set to the new materials by index (even if the object has materials assigned before the operation).
import bpy
from random import random
ob = bpy.context.object
me = ob.data
mat_offset = len(me.materials)
mat_count = 10
mats = []
for i in range(mat_count):
mat = bpy.data.materials.new("Mat_%i" % i)
mat.diffuse_color = random(), random(), random()
me.materials.append(mat)
# Can't assign materials in editmode
bpy.ops.object.mode_set(mode='OBJECT')
i = 0
for poly in me.polygons:
if poly.select:
poly.material_index = i % mat_count + mat_offset
i += 1
If you want to use UV mapped textures, you'll also have to deal with me.uv_layers
and me.uv_textures
. It's not enough to assign materials to faces, you also need to set the texture image for every face.
-
$\begingroup$ What if I want to use the face selection from the select random tool? $\endgroup$– VaderCommented Apr 13, 2014 at 14:03
-
$\begingroup$ Updated my example to only assign new materials to selected polygons. Note that if there's no material before the operation, unselected polygons will use the first material (
.material_index
has a default of 0, which is automatically the first material). $\endgroup$– CodeManXCommented Apr 13, 2014 at 21:27 -
$\begingroup$ @CoDEmanX I want to go over all of my 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$– AmirCommented Mar 17, 2018 at 4:59
That would be -
import bpy
D = bpy.data
if len(D.objects['Cube'].material_slots) < 1:
# if there is no slot then we append to create the slot and assign
D.objects['Cube'].data.materials.append(D.materials['Material'])
else:
# we always want the material in slot[0]
D.objects['Cube'].material_slots[0].material = D.materials['Material']
-
$\begingroup$ This doesn't work when I want to assign random faces a material in edit mode. $\endgroup$– VaderCommented Apr 11, 2014 at 13:02
-
$\begingroup$ @Vader Perhaps the source of this addon has some clues. $\endgroup$– gandalf3Commented Apr 12, 2014 at 21:19
-
$\begingroup$ @gandalf3 I already know how to do this on object level. I need to do it in edit mode though. This tut only does objects. $\endgroup$– VaderCommented Apr 12, 2014 at 21:26
-
$\begingroup$ @Vader Your question is assigning a material to a slot not assigning a material to faces. $\endgroup$– samblerCommented Apr 13, 2014 at 8:39
-
Abandon the approach of edit mode/non-edit mode and just focus on the datablock. Materials for faces are specified via material_index inside the datablock of a mesh type object.
Add a Suzanne mesh to the scene and try this code:
import bpy, random
ob = bpy.data.objects.get("Suzanne")
if ob != None:
# Create a materials.
mat_one = bpy.data.materials.get("mat_one")
if mat_one == None:
mat_one = bpy.data.materials.new("mat_one")
mat_one.diffuse_color = (random.random(),random.random(),random.random())
mat_two = bpy.data.materials.get("mat_two")
if mat_two == None:
mat_two = bpy.data.materials.new("mat_two")
mat_two.diffuse_color = (random.random(),random.random(),random.random())
# Add materials to slots.
ob.data.materials.append(mat_one)
ob.data.materials.append(mat_two)
# Determine random count and poly count.
rnd_face_count = 12
l = len(ob.data.polygons)
# Note: By default all faces have a material index of 0.
for i in range(rnd_face_count):
# Generate a random face index.
rnd_face_index = random.randint(0,(l-1))
ob.data.polygons[rnd_face_index].material_index = 1
This is not the cleanest code but it shows one way to accomplish the task mentioned. If you run this code twice, however, you will discover that you are building up a list of new materials on the Suzanne object.
This sequence would assign the currently selected material to the first slot:
bpy.context.object.active_material_index = 0
bpy.ops.object.material_slot_assign()
The material-utils addon makes heavy use of the API methods for materials, there you should find further details. SVN space_view3d_materials_utils.py
-
$\begingroup$ I need to specify the material that is assigned though $\endgroup$– VaderCommented Apr 10, 2014 at 21:29
I used sambler's answer for the .append() idea,
but I referenced context, instead of name:
(R, G, B are standard 0-255 values)
obj = bpy.context.object
colorstring = 'r' + str(R) + ' g' + str(G) + ' b' + str(B)
if colorstring in bpy.data.materials.keys():
obj.data.materials.append(bpy.data.materials[colorstring])
else:
material = bpy.data.materials.new(colorstring)
obj.data.materials.append(material)
material.diffuse_color = (R / 255, G / 255, B / 255)
bpy.ops.object.material_slot_assign()
Just to put in my two cents:
I have created my object, and I have a list that contains the index to what material slot each face should be assigned to. Running this code:
i = 0
for poly in bpy.context.object.data.polygons:
poly.material_index = MyTextures[i]
i += 1
did the trick for what I needed it to do.
Now if I can only find the simple solution for assigning the UV's I will be almost done with my project.