1
$\begingroup$

I have some objects with their corresponding materials defined as "Principled BSDF" I am trying to write a python script that transforms these materials to "Diffuse BSDF" and applies the corresponding texture image on them. For example currently I have the following:

enter image description here

Then this should be transformed to the following:

enter image description here

My texture files are already loaded so once I select that Color should be Image Texture I can then select the corresponding image file from the drop down list which matches my material name in this case it is Tiles07 with a sufix _2K_Color.jpg

I've tried to loop through each object material and apply each change gradually:

import bpy

def map_correct_materials(obj):
    print("Obj name: {}".format(obj.name))
    for m in obj.material_slots:
        print("Material name: {}".format(m.name))
        
        mat = m.material
        if mat.use_nodes == True:
            
            for node in mat.node_tree.nodes:
                if node.type == 'BSDF_PRINCIPLED':
                    print("Node name: {}".format(node.name))
                    print("Node type: {}".format(node.type))
                    print("Node input Base Color: {}".format(node.inputs['Base Color']))
                    node.type = 'BSDF_DIFFUSE'

objs = [o for o in bpy.data.objects
    if o.type == 'MESH' and not "Light" in o.name]
    
for obj in objs:
    map_correct_materials(obj)

however I got a read only error when I tried to change the material type:

AttributeError: bpy_struct: attribute "type" from "ShaderNodeBsdfPrincipled" is read-only

Searching around I couldn't find a reason for that though. Thus, any help would be appreciated.

Thanks.

$\endgroup$
7
  • 3
    $\begingroup$ AFAIK if a type is read only can't re type it, as can with for instance textures. Eg can't context.object.type = 'LATTICE' to change an object into a lattice object. Hence will need to add a new BSDF_DIFFUSE node , make links to it using links of old, then remove old. Pretty sure there is a qa re exactly this. (or vice versa) @brockmann ? $\endgroup$
    – batFINGER
    Sep 6, 2021 at 10:20
  • 2
    $\begingroup$ Tangential note: the roughness on Principled and the roughness on Diffuse are not at all the same thing. $\endgroup$
    – scurest
    Sep 6, 2021 at 10:25
  • 2
    $\begingroup$ Agree with @batFINGER, you'd have to replace the node (recently updated) -> Update all materials, but keep their color and textures $\endgroup$
    – brockmann
    Sep 6, 2021 at 11:34
  • 1
    $\begingroup$ That's the one. @ttsesm confirm if that answers question and will close as dupe. If you are referring to PROPERTIES material CHeck out the UI code is layout.template_node_view(ntree, node, input) Not an enum of the node type property. Not aware of these being exposed to python Have asked re the magic done by template_constraint using instance panels. Re (aforementioned) textures and changing type blender.stackexchange.com/questions/215897/… $\endgroup$
    – batFINGER
    Sep 6, 2021 at 11:51
  • 2
    $\begingroup$ Glad I could help, just let us know whether that works for you. $\endgroup$
    – brockmann
    Sep 6, 2021 at 12:37

1 Answer 1

1
$\begingroup$

Ok, while @brockmann's solution suggested above looks good it might be an overkill for what I want to do. Thus, taking into consideration @batFINGER's suggestion to create a new node and remove the previous one I came up with the following solution which seems to do what I want:

import bpy

def map_correct_materials(obj):
    print("Obj name: {}".format(obj.name))
    for m in obj.material_slots:
        print("Material name: {}".format(m.name))
        
        mat = m.material
        if mat.use_nodes == True:
            
            image_texture = mat.node_tree.nodes.get('Image Texture')
            roughness = mat.node_tree.nodes.get('Image Texture.001')
            normal_map = mat.node_tree.nodes.get('Normal Map')
#            displacement = mat.node_tree.nodes.get('Displacement')
            material_output = mat.node_tree.nodes.get('Material Output')
            diffuse = mat.node_tree.nodes.new('ShaderNodeBsdfDiffuse')
            
            uv_map_node = mat.node_tree.nodes.new('ShaderNodeUVMap')
            uv_map_node.uv_map = "UVMap"
            normal_map.uv_map = "UVMap"


            # remap links and connect the diffuse shader to material
            mat.node_tree.links.new(uv_map_node.outputs['UV'], image_texture.inputs['Vector'])
            mat.node_tree.links.new(diffuse.inputs['Color'], image_texture.outputs['Color'])
            mat.node_tree.links.new(diffuse.inputs['Roughness'], roughness.outputs['Color'])
            mat.node_tree.links.new(diffuse.inputs['Normal'], normal_map.outputs['Normal'])
            mat.node_tree.links.new(material_output.inputs[0], diffuse.outputs[0])
            
            # Remove default
            mat.node_tree.nodes.remove(mat.node_tree.nodes.get('Principled BSDF')) #title of the existing node when materials.new

            # set activer material to your new material
            obj.active_material = mat
            

objs = [o for o in bpy.data.objects
    if o.type == 'MESH' and "Floor" in o.name and not "Light" in o.name]
    
for obj in objs:
    map_correct_materials(obj)

Which mainly what it does is to remap the links from the existing nodes to the newly created one and then remove the one that is not needed anymore.

$\endgroup$

You must log in to answer this question.

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