I want to import a set of 4 images. Upon import, I want to rename the image texture node that is created for each imported image to represent the type of image that is being loaded, i.e. (Diffuse, Normal, Spec, Roughness). How do I pass the filepath/filename of the image I am importing as a string, tell the script to look for certain characters in the passed string (COL, NRM, SPEC, ROUGH) and rename based upon the returned characters?
I would assume I can complete the rename function by using if-else statements. Any help or examples you could provide would be greatly appreciated.
The script below creates a new node category, places a custom node inside of it to import pbr textures, and allows you to import multiple import multiple images at one time into the node editor. I am looking to expand this script to include the functionality I have mentioned above.
Here is the working code:
import bpy
from bpy.types import NodeTree, Node, NodeSocket
import bpy, blf, bgl
from bpy.types import Operator, Panel, Menu
from bpy.props import FloatProperty, EnumProperty, BoolProperty, IntProperty, StringProperty, FloatVectorProperty, CollectionProperty
from bpy_extras.io_utils import ImportHelper
from mathutils import Vector
from math import cos, sin, pi, hypot
from os import path
from glob import glob
from copy import copy
def node_mid_pt(node, axis):
if axis == 'x':
d = node.location.x + (node.dimensions.x / 2)
elif axis == 'y':
d = node.location.y - (node.dimensions.y / 2)
else:
d = 0
return d
def get_nodes_links(context):
space = context.space_data
tree = space.node_tree
nodes = tree.nodes
links = tree.links
active = nodes.active
context_active = context.active_node
# check if we are working on regular node tree or node group is currently edited.
# if group is edited - active node of space_tree is the group
# if context.active_node != space active node - it means that the group is being edited.
# in such case we set "nodes" to be nodes of this group, "links" to be links of this group
# if context.active_node == space.active_node it means that we are not currently editing group
is_main_tree = True
if active:
is_main_tree = context_active == active
if not is_main_tree: # if group is currently edited
tree = active.node_tree
nodes = tree.nodes
links = tree.links
return nodes, links
# Implementation of custom nodes from Python
# Derived from the NodeTree base type, similar to Menu, Operator, Panel, etc.
class MyCustomTree(NodeTree):
bl_idname = "CustomTreeType"
bl_label = "Custom Node Tree"
bl_icon = "NODE"
# Defines a poll function to enable filtering for various node tree types.
class MyCustomTreeNode:
@classmethod
def poll(cls, ntree):
b = False
# Make your node appear in different node trees by adding their bl_idname type here.
if ntree.bl_idname == 'ShaderNodeTree': b = True
return b
# Derived from the Node base type.
class MyCustomNode(Node, MyCustomTreeNode):
'''A custom node'''
bl_idname = 'CustomNodeType'
bl_label = 'Import PBR Textures'
bl_icon = 'NODETREE'
# Additional buttons displayed on the node.
def draw_buttons(self, context, layout):
layout.operator("node.nw_add_multiple_images", text="Select Images")
# Optional: custom label
# Explicit user label overrides this, but here we can define a label dynamically.
def draw_label(self):
return "Import PBR Textures"
class NWAddMultipleImages(Operator, ImportHelper):
"""Add multiple images at once"""
bl_idname = "node.nw_add_multiple_images"
bl_icon = 'NODETREE'
bl_label = 'Open Selected Images'
bl_options = {'REGISTER', 'UNDO'}
directory = StringProperty(subtype="DIR_PATH")
files = CollectionProperty(type=bpy.types.OperatorFileListElement, options={'HIDDEN', 'SKIP_SAVE'})
def execute(self, context):
nodes, links = get_nodes_links(context)
nodes_list = [node for node in nodes]
if nodes_list:
nodes_list.sort(key=lambda k: k.location.x)
xloc = nodes_list[0].location.x - 220 # place new nodes at far left
yloc = 0
for node in nodes:
node.select = False
yloc += node_mid_pt(node, 'y')
yloc = yloc/len(nodes)
else:
xloc = 0
yloc = 0
if context.space_data.node_tree.type == 'SHADER':
node_type = "ShaderNodeTexImage"
elif context.space_data.node_tree.type == 'COMPOSITING':
node_type = "CompositorNodeImage"
else:
self.report({'ERROR'}, "Unsupported Node Tree type!")
return {'CANCELLED'}
new_nodes = []
for f in self.files:
fname = f.name
node = nodes.new(node_type)
new_nodes.append(node)
node.label = fname
node.hide = True
node.width_hidden = 100
node.location.x = xloc
node.location.y = yloc
yloc -= 40
img = bpy.data.images.load(self.directory+fname)
node.image = img
# shift new nodes up to center of tree
list_size = new_nodes[0].location.y - new_nodes[-1].location.y
for node in new_nodes:
node.select = True
node.location.y += (list_size/2)
return {'FINISHED'}
### Node Categories ###
import nodeitems_utils
from nodeitems_utils import NodeCategory, NodeItem
# our own base class with an appropriate poll function,
# so the categories only show up in our target tree type
class MyNodeCategory(NodeCategory):
@classmethod
def poll(cls, context):
b = False
# Make your node appear in different node trees by adding their bl_idname type here.
if context.space_data.tree_type == 'ShaderNodeTree': b = True
return b
# all categories in a list
node_categories = [
# identifier, label, items list
MyNodeCategory("SOMENODES", "Import", items=[
NodeItem("CustomNodeType"),
#NodeItem("node.nw_add_multiple_images"),
]),
]
#def multipleimages_menu_func(self, context):
#col = self.layout.column(align=True)
#col.operator(NWAddMultipleImages.bl_idname, text="Import PBR")
#col.separator()
def register():
bpy.utils.register_class(MyCustomNode)
nodeitems_utils.register_node_categories("CUSTOM_NODES", node_categories)
#bpy.types.NODE_MT_category_SH_NEW_TEXTURE.prepend(multipleimages_menu_func)
#bpy.types.NODE_PT_category_SH_NEW_TEXTURE.prepend(multipleimages_menu_func)
#bpy.types.NODE_MT_category_CMP_INPUT.prepend(multipleimages_menu_func)
#bpy.types.NODE_PT_category_CMP_INPUT.prepend(multipleimages_menu_func)
bpy.utils.register_module(__name__)
def unregister():
nodeitems_utils.unregister_node_categories("CUSTOM_NODES")
bpy.utils.unregister_class(MyCustomNode)
#bpy.types.NODE_MT_category_SH_NEW_TEXTURE.remove(multipleimages_menu_func)
#bpy.types.NODE_PT_category_SH_NEW_TEXTURE.remove(multipleimages_menu_func)
#bpy.types.NODE_MT_category_CMP_INPUT.remove(multipleimages_menu_func)
#bpy.types.NODE_PT_category_CMP_INPUT.remove(multipleimages_menu_func)
bpy.utils.unregister_module(__name__)
def pre_frame_change(scene):
if scene.render.engine == 'CYCLES':
# Scan materials to see if I have a custom node within any of the trees.
for m in bpy.data.materials:
if m.node_tree != None:
for n in m.node_tree.nodes:
if n.bl_idname == 'CustomNodeType':
print(n.bl_idname)
# One of our custom nodes, let's update it.
# When we set the value that will trigger an update inside the node.
# Even if we change it to the same value it was.
v = n.some_value
n.some_value = v
if __name__ == "__main__":
register()
bpy.app.handlers.frame_change_pre.append(pre_frame_change)