0
$\begingroup$

Is it possible to assign a new Material(Principled BSDF shader) to each mesh or a selection of meshes in my scene using Python?

The script should use the individual mesh name to name the material and delete the existing material if there was a material before.

How can I approach this problem?

$\endgroup$
1
  • $\begingroup$ I opened a new Blender session and created a new principled Material called "Material.001" then I created a new principled Material called "Material.002" the following code allowed me to set the material I wanted to the cube and should give you a jumping off point. bpy.data.objects["Cube"].material_slots[0].material = bpy.data.materials["Material.002"] $\endgroup$
    – rob
    Commented Sep 26, 2018 at 10:23

2 Answers 2

3
$\begingroup$

Run this with your objects selected:

import bpy

# I separated the creation of nodes to a function so it's easier 
# to edit later if needed
def create_nodes(mat): 
    mat.use_nodes = True
    nodes = mat.node_tree.nodes
    for every_node in nodes: # this removes all existing nodes
        nodes.remove(every_node)

    # creating Principled node and moving it:
    node = nodes.new('ShaderNodeBsdfPrincipled')
    node.location = (-190,100)

    # creating Output node and moving it:
    output_node = nodes.new('ShaderNodeOutputMaterial')
    output_node.location = (40,100)

    # creating the link between the two nodes:
    links = mat.node_tree.links
    link = links.new(node.outputs[0], output_node.inputs[0])

# this saves the currently active object so it can be restored later
active = bpy.context.object 

# let's loop through all selected objects
for every_object in bpy.context.selected_objects: 
    # I only want to work with objects capable of having a material
    if every_object.type in {'MESH','CURVE', 'SURFACE','META', 'FONT'}: 
        if every_object.name not in bpy.data.materials:
        # if there is no material named after the object yet let's make one
            mat = bpy.data.materials.new(every_object.name)
            # and let's create the nodes for it
            create_nodes(mat)
        else:
            # if the material already exists let's just use it
            mat = bpy.data.materials.get(every_object.name)
        if len(every_object.material_slots) == 0: # if there are no material slots
            every_object.data.materials.append(mat)

        # The only thing left now is to assign the material to 
        # all material slots. We probably do not want to loose the info
        # about how the object is divided into separate materials
        for every_slot in every_object.material_slots:
            every_slot.material = mat  
$\endgroup$
6
  • $\begingroup$ Thanks Martin, this does exactly what i was asking for! great! $\endgroup$
    – user62799
    Commented Sep 26, 2018 at 13:47
  • $\begingroup$ Would you mind explaining the steps- so i can learn from it :) ? $\endgroup$
    – user62799
    Commented Sep 26, 2018 at 13:48
  • $\begingroup$ I tried to add comments. If you have any questions, let me know. $\endgroup$ Commented Sep 26, 2018 at 14:07
  • $\begingroup$ line 40, in <module> AttributeError: bpy_prop_collection: attribute "active" not found How to do it in 2.9 $\endgroup$
    – 6by3
    Commented Oct 29, 2020 at 15:00
  • $\begingroup$ blender.stackexchange.com/a/124259/43889 found the answer replace line 40 and 43 with bpy.context.view_layer.objects.active = every_object $\endgroup$
    – 6by3
    Commented Oct 29, 2020 at 15:03
1
$\begingroup$

Sorry for my English, I'm Brazilian and new to blender.

I've been looking for this script (first of all thank you very much Martynas Žiemys was very utiul for me) but it was out of date, so I'm putting it here with modifications to help someone who needs it like me, but the credit is all from the friend.

again, very grateful, helped me a lot ...

import bpy


# Separei a criação de nós em uma função para facilitar 

# para editar mais tarde, se necessário
def create_nodes(mat): 
    mat.use_nodes = True
    nodes = mat.node_tree.nodes
    for every_node in nodes: # isso remove todos os nós existentes
        nodes.remove(every_node)

    # criando nó Principled e movendo-o:
    node = nodes.new('ShaderNodeBsdfPrincipled')
    node.location = (-190,100)

    # criando nó de saída e movendo-o:
    output_node = nodes.new('ShaderNodeOutputMaterial')
    output_node.location = (40,100)

    # criando o link entre os dois nós
    links = mat.node_tree.links
    link = links.new(node.outputs[0], output_node.inputs[0])

# Isso salva o objeto ativo no momento para que possa ser restaurado mais tarde
active = bpy.context.object 

# vamos percorrer todos os objetos selecionados
for every_object in bpy.context.selected_objects: 
    # Eu só quero trabalhar com objetos capazes de ter um material
    if every_object.type in {'MESH','CURVE', 'SURFACE','META', 'FONT'}: 
        if every_object.name not in bpy.data.materials:
        # se ainda não houver material com o nome do objeto, vamos fazer um 
            mat = bpy.data.materials.new(every_object.name)
            # e vamos criar os nós para ele
            create_nodes(mat)
        else:
            # se o material já existir, vamos usá-lo
            mat = bpy.data.materials.get(every_object.name)
        if len(every_object.material_slots) == 0: # se não houver slots de material
            # vamos ativar o objeto atual e criar um slot de material
            bpy.context.view_layer.objects.active = every_object
            bpy.ops.object.material_slot_add() 
            # vamos deixar o objeto ativo como estava
            bpy.context.view_layer.objects.active = active

        # A única coisa que resta agora é atribuir o material a
        # todos os slots de material. Provavelmente não queremos perder a informação
        # sobre como o objeto é dividido em materiais separados
        for every_slot in every_object.material_slots:
            every_slot.material = mat 

Note: I didn't comment on his answer because I'm new to the community and I don't have enough reputation :P

$\endgroup$
1
  • $\begingroup$ Apart from language of comments, what is the difference here? Btw would remove every instance of every_ eg for slot in ob.material_slots: IMO reads a lot better since every_slot is not "every slot" it is one item. $\endgroup$
    – batFINGER
    Commented Apr 3, 2020 at 19:10

You must log in to answer this question.

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