# How to add an image texture to a single face?

I'm expecting to see the face selected for adding it into a vertex group later on and the image texture mapped to the front face in order to map the image onto a road sign.

I've tried assigning the texture via material_index and I get the selected face but the image is not mapped properly. I guess something has been changed as of 2.8+, What am I missing here?

import bpy, bmesh, os

bpy.ops.object.mode_set(mode = 'OBJECT')
bpy.ops.transform.resize(use_proportional_edit=True, value=(1.5, 0.005, 2))
bpy.context.object.location = (0, 0, 4)
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True, properties=True)
bpy.context.object.name = "sign1"

bpy.ops.object.mode_set(mode = 'EDIT')
ob = bpy.context.object

mat = bpy.data.materials.new(name="matsign")
mat.use_nodes = True
bsdf = mat.node_tree.nodes["Principled BSDF"]

if ob.data.materials:
ob.data.materials[0] = mat
else:
ob.data.materials.append(mat)

bm = bmesh.from_edit_mesh(ob.data)

bm.select_mode = {'FACE'}
bpy.ops.mesh.select_all(action='DESELECT')
bm.faces.ensure_lookup_table()
bm.faces[3].select_set(True)       # front face selected
bm.faces[3].material_index = ob.data.materials["matsign"].pass_index    # why can't I see it?


### The image node must be connected

You create an image node but don't connect it to anything. A node needs to ultimately flow into the output node to have an effect. You need to connect the image node's Color socket to the Principled node's Base Color socket. You can do that with (I also moved the node so its easier to see)

bsdf = mat.node_tree.nodes["Principled BSDF"]
texImage.location = -300, 120


### The poly's material_index is an index into the object's material slots

You have

bm.faces[3].material_index = ob.data.materials["matsign"].pass_index


The pass_index has nothing to do with this and this only works by coincidence because it's 0 by default.

The material_index is an index into the objects' material slots. So since here

if ob.data.materials:
ob.data.materials[0] = mat
else:
ob.data.materials.append(mat)


you are ensuring the material is in the 0th slot, all polys with material_index 0 will use that material.

And since 0 is the default value for material_index, all polys will use that material without you having to do anything.

• mat.node_tree.links was the missing link for me, now it all starts to make sense. My intent was to only make the front face pick up the texture though. Now everything has the texture; I am guessing this is because everything is assigned to the "0" index, not just the intended face. Programmatically, what is the correct way to reference an index by material name so I can use several? Do I just manually add one to a counter every time I do a data.materials.append or can I interrogate this? Mar 15, 2021 at 10:23
• You can do mesh.materials.find(material_name) to get the material_index if that's what you're asking. -1 is "not found". Mar 15, 2021 at 10:36
• I tried that, but mesh was not defined. However, bpy.data.materials.find(material_name) seemed to do the trick - was this the right one in this context? Mar 15, 2021 at 20:34
• No. In your code above, mesh is ob.data. Mar 15, 2021 at 21:09