I have a default cube ‘Cube’ which has 1 default material (active_material_index = 0
). I’m adding another material dynamically via script that adds a Principled BSDF texture setup (CTRL+SHIFT+T) for this second material at active_material_index = 1
using bpy.ops.node.nw_add_textures_for_principled()
as in the code below. The weird thing is that if i use active_material_index = 0
or any index for a material that already exists and have its Principled BSDF node already selected, the script actually works and properly adds the texture setup. But it doesn't work if you dynamically create the material and dynamically select the Principled BSDF node (white outline). It looks like something asynchronous is happening where the creation of the material is not ready yet when nw_add_textures_for_principled
executes. Any idea? If the problem was the context, then how was it possible to work for the existing material?
import bpy
mat = bpy.data.materials.new(name='dynamically_created')
mat.use_nodes = True
bpy.data.objects['Cube'].data.materials.append(mat)
bpy.context.object.active_material_index = 1
bpy.context.area.ui_type = 'ShaderNodeTree'
bpy.context.area.type = 'NODE_EDITOR'
m = bpy.context.object.active_material
m.node_tree.nodes['Material Output'].select = False
m.node_tree.nodes['Principled BSDF'].select = True
m.node_tree.nodes.active = m.node_tree.nodes.get("Principled BSDF")
r = bpy.ops.node.nw_add_textures_for_principled(filepath="E:\\ZZZZ\\Projects\\blender\\textures\\metal\\Metal021_1K-JPG\\", directory="E:\\ZZZZ\\Projects\\blender\\textures\\metal\\Metal021_1K-JPG\\", files=[ {"name":"Metal021_1K_Color.jpg", "name":"Metal021_1K_Color.jpg"}, {"name":"Metal021_1K_Displacement.jpg", "name":"Metal021_1K_Displacement.jpg"}, {"name":"Metal021_1K_Metalness.jpg", "name":"Metal021_1K_Metalness.jpg"}, {"name":"Metal021_1K_NormalDX.jpg", "name":"Metal021_1K_NormalDX.jpg"}, {"name":"Metal021_1K_NormalGL.jpg", "name":"Metal021_1K_NormalGL.jpg"}, {"name":"Metal021_1K_Roughness.jpg", "name":"Metal021_1K_Roughness.jpg"}, {"name":"Metal021_PREVIEW.jpg", "name":"Metal021_PREVIEW.jpg"}], relative_path=True)
print("result ==== ", r) #{'CANCELLED'}
UPDATE w/ Marty Fouts' Answer:
Thanks to Marty Fouts the context usage is properly implemented now but:
When I manually select the Principled BSDF node of material at index 0 (aka the already-existing material), the script does add the texture node setup but to material of index 0:
When NO Principled BSDF shader node is selected and i want the script to select this node from material index 1 (aka the dynamically generated material) it selects the node but unfortunately does NOT add the texture setup as I would have expected. The result is:
Hence the issue as described in the question title of this thread still persists
Here's the new complete script (make sure to use the default cube that already has 1 material on slot index 0):
import bpy
object = bpy.context.active_object
material = bpy.data.materials.new(name='dynamically_created')
object.data.materials.append(material)
object.active_material_index = len(object.material_slots) - 1
material.use_nodes = True
tree = material.node_tree
nodes = tree.nodes
material.node_tree.nodes['Material Output'].select = False
material.node_tree.nodes['Principled BSDF'].select = True
material.node_tree.nodes.active = material.node_tree.nodes.get("Principled BSDF")
filepath="E:\\ZZZZ\\Projects\\blender\\textures\\metal\\Metal021_1K-JPG\\"
directory="E:\\ZZZZ\\Projects\\blender\\textures\\metal\\Metal021_1K-JPG\\"
files=[
{"name":"Metal021_1K-JPG.usda", "name":"Metal021_1K-JPG.usda"},
{"name":"Metal021_1K-JPG.usdc", "name":"Metal021_1K-JPG.usdc"},
{"name":"Metal021_1K_Color.jpg", "name":"Metal021_1K_Color.jpg"},
{"name":"Metal021_1K_Displacement.jpg", "name":"Metal021_1K_Displacement.jpg"},
{"name":"Metal021_1K_Metalness.jpg", "name":"Metal021_1K_Metalness.jpg"},
{"name":"Metal021_1K_NormalDX.jpg", "name":"Metal021_1K_NormalDX.jpg"},
{"name":"Metal021_1K_NormalGL.jpg", "name":"Metal021_1K_NormalGL.jpg"},
{"name":"Metal021_1K_Roughness.jpg", "name":"Metal021_1K_Roughness.jpg"},
{"name":"Metal021_PREVIEW.jpg", "name":"Metal021_PREVIEW.jpg"},
]
relative_path=True
#-----------------------------------------------------------------------------
#
# Starting in 3.2 context overrides are deprecated in favor of temp_override
# https://docs.blender.org/api/3.2/bpy.types.Context.html#bpy.types.Context.temp_override
#
# They are scheduled to be removed in 3.3
#
def use_temp_override():
''' Determine whether Blender is 3.2 or newer and requires
the temp_override function, or is older and requires
the context override dictionary
'''
version = bpy.app.version
major = version[0]
minor = version[1]
if major < 3 or (major == 3 and minor < 2):
return False
else:
return True
win = bpy.context.window
scr = win.screen
areas = [area for area in scr.areas if area.type == 'NODE_EDITOR']
regions = [region for region in areas[0].regions if region.type == 'WINDOW']
if use_temp_override():
with bpy.context.temp_override(window=win, area=areas[0], region=regions[0],
screen=scr):
bpy.ops.node.nw_add_textures_for_principled(
filepath=filepath,
directory=directory,
files=files,
relative_path=relative_path
)
else:
override = {
'window': win,
'screen': scr,
'area': areas[0],
'region': regions[0],
}
bpy.ops.node.nw_add_textures_for_principled(
override,
filepath=filepath,
directory=directory,
files=files,
relative_path=relative_path
)
Info: No Folder Selected
. Do you want to set the folder programmatically or let the user pick (display the file browser)? $\endgroup${"FINISHED"}
but no textures were added. I want to set it automatically. I have a list of folders through which i will iterate each folder to one material. $\endgroup$bpy.ops.node.nw_add_textures_for_principled()
on an already created material. $\endgroup$