1

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.

ttsesm
  • 409
  • 3
  • 10
  • 3
    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 ? – batFINGER Sep 06 '21 at 10:20
  • 2
    Tangential note: the roughness on Principled and the roughness on Diffuse are not at all the same thing. – scurest Sep 06 '21 at 10:25
  • @batFINGER if apply these changes through the gui it works fine. Thus, why through the script complaints about being read only. Except if under the hood does what you suggest. @scurest well roughness isn't that much of a concern, the basic task is to covert it to Diffuse BSDF type and keep the texture. – ttsesm Sep 06 '21 at 11:10
  • 2
    Agree with @batFINGER, you'd have to replace the node (recently updated) -> Update all materials, but keep their color and textures – brockmann Sep 06 '21 at 11:34
  • 1
    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 https://blender.stackexchange.com/questions/215897/how-to-change-texture-properties-via-python – batFINGER Sep 06 '21 at 11:51
  • Thanks @brockmann, batFINGER let me have a look on the links and the code there and see how I can use them or not. – ttsesm Sep 06 '21 at 12:08
  • 2
    Glad I could help, just let us know whether that works for you. – brockmann Sep 06 '21 at 12:37

1 Answers1

1

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.

ttsesm
  • 409
  • 3
  • 10