I have a folder with 750 texture folders, within each texture folder there are four files: jpg, png, exr, and x2. I can import these textures manually by going to the shader tab and pressing Ctrl+Shift+T with Node Wrangler enabled and then select all the files within a given texture folder. My goal with this post is to automate this so that I don't have to manually do this for all 750 texture folders.
Here is the code I have so far:
import bpy
import os
FOLDER_PATH = "/Users/torrinleonard/Desktop/Texture_Importer/Textures_Folder"
MATERIAL_OBJECT = "Material_Object" # The name of an object used to only add materials to the scene and nothing else.
for texture_folder in os.listdir(FOLDER_PATH):
if texture_folder != '.DS_Store': # Make sure Blender is not importing MacOs .DS_Store files
# Get folder path for each texture
texture_path = os.path.join(FOLDER_PATH, texture_folder)
# Create new material slot in material list and create new material:
material_object = bpy.data.objects[MATERIAL_OBJECT] # Set active object to variable
material = bpy.data.materials.new(name=texture_folder) # Set new material to variable
material_object.data.materials.append(material) # Add material to the object
bpy.context.area.type = 'NODE_EDITOR'
bpy.context.area.ui_type = 'ShaderNodeTree'
# Prepare node setup of material:
material.use_nodes = True
material.node_tree.nodes.get("Principled BSDF").select = True # Set nodes in material node tree to active
textures = os.listdir(texture_path)
first_jpg = ""
files = []
for texture in textures:
if texture != '.DS_Store': # Make sure Blender is not importing MacOs .DS_Store files
if texture.endswith(".jpg"):
first_jpg = texture
files.append(
{
"name": texture,
"name": texture
}
)
bpy.ops.node.nw_add_textures_for_principled(
filepath=f"//Textures_Folder/metal_plate/{first_jpg}",
directory=os.path.join(FOLDER_PATH, texture_folder),
files=files
)
It's pretty self explanatory but here is the recap: We have an object in our scene, "Material_Object", for each texture located in our Texture folder, we add a new material to that object's material list, and create a new material. This creates a Principled BSDF node in the Shader Editor.
When running this script inside Blender, the materials are created, the names are set, but for some reason the files are not applied to the Principled BSDF node as if I were to do it manually:
Why is this happening?
I believe it is due to the way Blender is selecting the Principled BSDF node. In the console it prints it successfully Selected that node:
However going to that materials node group we can see it is highlighted in orange, not white, which is the selection type we need to activate Ctrl+Shift+T, or in this case, bpy.ops.node.nw_add_textures_for_principled
with Python. REMEMBER: This was selected with the above function material.node_tree.nodes.get("Principled BSDF").select = True
:
To contrast that, here is what the selection looks like when you do it manually:
Below I've highlighted the code for nw_add_textures_for_principled
in Node Wranglers, it appears to be looking for nodes.active
:
I have spent hours trying to find out how to get this hidden 'active' state on a node with python, but with no luck. I've poured through the documentation and node.select
is the only thing close to what I can find, but that's not selecting the Principled BSDF like you would do it manually to import textures with Ctrl+Shift+T.
My question: How do I apply this hidden 'active' state to a node so that I can import images to a materials Principled BSDF node using nw_add_textures_for_principled
from Node Wrangler?
Is this the only way to bulk import textures? Am I missing some much better solution?
I'm using Blender 3.1.2 on MacOS 11.6.7 Big Sur to code/test this and Blender 3.2 on Windows 10 to execute it if that matters
material.node_tree.nodes.active = material.node_tree.nodes.get("Principled BSDF")
? docs.blender.org/api/current/… active is a property of the node tree's nodes construct, it's not tied to each and every node (Every node can be selected, but only one node at most can be active per tree at any given time) $\endgroup$RuntimeError: Operator bpy.ops.node.nw_add_textures_for_principled.poll() failed, context is incorrect
$\endgroup$3.2\scripts\addons\node_wrangler.py
NWAddPrincipledSetup
. $\endgroup$print(f"AREA TYPE: {bpy.context.area.ui_type}")
before the node wrangler line, the console prints the area asAREA TYPE: ShaderNodeTree
$\endgroup$