0
$\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 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 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 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 at 11:51
  • 2
    $\begingroup$ Glad I could help, just let us know whether that works for you. $\endgroup$
    – brockmann
    Sep 6 at 12:37
0
$\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$

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.