0

i have a Material called "Dust" that has 4 nodes. I now need a script that copies all nodes and paste it to all materials of objects in all activated collections.

enter image description here

Then the Output of the Map Range Node shall be connected to the Roughness Input of the Sheen Tab on the materials like in this example. Also the Weight shall be set to 0.25 and the Tint Color Value to 0.5 for every material where the nodes are copied.

enter image description here

Is that somehow possible? I really need it and would appreciate any help!!

VICUBE Animation
  • 501
  • 3
  • 14
  • 1
    The first thing to consider is to make the 4-node additions into a group. If you don't have too many materials to apply this two, you can then add the Group node into materials, and hook that up. – james_t Jan 11 '24 at 21:20
  • Can you make dust with the Sheen option? -- and, yes, you can do this with a Python script. I also would recommend to use a node group. That's a good idea because it's easier to modify, remove and check if it already exists in the material so you don't add it again accidentally. – Blunder Jan 12 '24 at 00:49
  • Thank you for your comments! Yes that´s true, putting thiese node into a group is a good idea. And yeah, i want to make the dust over the sheen input. It is a very complicated scene with over 1000 materials so doing this manually would be horrible. Could anybody write the pyton script for that? – VICUBE Animation Jan 12 '24 at 11:01
  • I made it into a node goup called "Dust" with the Output "Result". – VICUBE Animation Jan 12 '24 at 12:06

1 Answers1

1

Here is a little script I wrote to add a node group to many materials. The script ...

  • it loops over all objects in the blend file
  • checks the material slots of the objects
  • and adds a node group ("Dust") to the material's node tree if the node group is not already present.
  • it also connects the first output of the group to the Principled BSDF's Sheen input and sets a few values.

If you want to modify only objects that are in a certain collection you need to change line 5 to fill the objects set (How to list all collections and their objects?). Note, that checking if a collection is active/enabled in the outliner is a bit more complicated because the View Layer controls this (see How can I check which of my collections are hidden in viewport?). So I kept it simple.

Don't forget to make a backup before you run the script. In the system console (main menu Window > Toggle System Console), you can check what the script is doing. Ctrl+C in the system console window will stop it.

import bpy
from mathutils import Vector

'list' (set) of objects to check

objects = set(obj for obj in bpy.data.objects)

keep track of the materials done

materials_done = set()

def material_uses_group(material, node_group_to_check) -> bool:

for node in material.node_tree.nodes:
    # check whether the node is a node group and has the same name as the group to be added.
    # Note: node.name is an internal name (e.g. 'Group') as shown in the side panel. 
    #   It's not the name of the node as shown on the node, it's node.node_tree.name
    #   (e.g. 'Dust'). It can be overwritten by node.label. 
    #   Only groups have node.node_tree attribute, can be checked with hasattr()
    if node.bl_idname == 'ShaderNodeGroup':
        #print(f"DEBUG: node.bl_idname = {node.bl_idname}, node.node_tree.name = {node.node_tree.name}, node_group.name = {node_group_to_check.name}")
        if hasattr(node, 'node_tree') and node.node_tree.name == node_group_to_check.name:
            return True
return False


def add_node_group_to_material(material, node_group_to_add) -> bool:

# find the Principled BSDF
print(f"  - looking for Principled BSDF node in material {material.name}")
pbsdf = None
for node in material.node_tree.nodes:
    if node.bl_idname == 'ShaderNodeBsdfPrincipled':
        pbsdf = node
        break

# add node group if found        
if pbsdf != None:
    print(f"  + found it, adding group to material {material.name}")
    new_group = material.node_tree.nodes.new('ShaderNodeGroup')
    new_group.node_tree = node_group_to_add
    new_group.location = pbsdf.location - Vector([240, 200])  # <----- position, left of the P.BSDF

    # linking and values
    material.node_tree.links.new(pbsdf.inputs['Sheen Roughness'], new_group.outputs[0])
    pbsdf.inputs['Sheen Weight'].default_value = 0.250       # <------- value
    pbsdf.inputs['Sheen Tint'].default_value = [0.5, 0.5, 0.5, 1.0] # <---- RGBA   
    return True
else:
    print(f"  - no Principled BSDF node found in material {material.name}, ignored")
    return False


def main() -> int:

# actually, this is a node tree (my_node_group.bl_idname == 'ShaderNodeTree').
# The group used in the material is 'ShaderNodeGroup'.
my_node_group = bpy.data.node_groups['Dust']    # <---- the node group to add to the materials

# main loop
for obj in objects:
    if obj.type != 'MESH':
        print(f"Skipping object {obj.name} (type {obj.type})")
        continue

    print(f"Checking materials of {obj.name} ")
    # check material slots
    for idx, slot in enumerate(obj.material_slots):
        if slot.material is None:
            print(f"- WARN: found no material {obj.name}, empty slot #{idx}, skip")
            continue
        if  slot.material.name in materials_done:              # already done?
            print(f"- found again material {slot.material.name} in slot #{idx}, skip")
            continue
        if not slot.material.use_nodes:
            print(f"- found material {slot.material.name} in slot #{idx} but it does not use nodes, skip")        
            continue

        print(f"+ found material {slot.material.name} in slot #{idx}, trying to add node group")
        if material_uses_group(slot.material, my_node_group):
            print(f"   - no, node group is already in material, skip")
            materials_done.add(slot.material.name)
            continue

        if add_node_group_to_material(slot.material, my_node_group):
            print("  OK, node group added")                
        materials_done.add(slot.material.name)


if name == "main": main() print("-- DONE --")

Related questions:

Blunder
  • 13,925
  • 4
  • 13
  • 36