[Bf-extensions-cvs] [dbb3c56b] master: NodeWrangler: Auto Texture Setup for Principled BSDF "Close D2736" This adds an Operator for automatic texture setup for the pricipled shader. Shortcut is ctrl-shift-T
florianfelix
noreply at git.blender.org
Tue Jul 11 11:19:56 CEST 2017
Commit: dbb3c56b2c6816a0df3e9bc7417e21499c57c660
Author: florianfelix
Date: Tue Jul 11 11:18:18 2017 +0200
Branches: master
https://developer.blender.org/rBAdbb3c56b2c6816a0df3e9bc7417e21499c57c660
NodeWrangler: Auto Texture Setup for Principled BSDF
"Close D2736"
This adds an Operator for automatic texture setup for the pricipled
shader.
Shortcut is ctrl-shift-T
===================================================================
M node_wrangler.py
===================================================================
diff --git a/node_wrangler.py b/node_wrangler.py
index 1e242f4b..ac30af1c 100644
--- a/node_wrangler.py
+++ b/node_wrangler.py
@@ -19,8 +19,8 @@
bl_info = {
"name": "Node Wrangler",
"author": "Bartek Skorupa, Greg Zaal, Sebastian Koenig, Christian Brinkmann",
- "version": (3, 34),
- "blender": (2, 78, 0),
+ "version": (3, 35),
+ "blender": (2, 79, 0),
"location": "Node Editor Toolbar or Ctrl-Space",
"description": "Various tools to enhance and speed up node-based workflow",
"warning": "",
@@ -39,6 +39,7 @@ from os import path
from glob import glob
from copy import copy
from itertools import chain
+import re
#################
# rl_outputs:
@@ -2560,6 +2561,253 @@ class NWAddTextureSetup(Operator, NWBase):
self.report({'WARNING'}, "No free inputs for node: "+t_node.name)
return {'FINISHED'}
+class NWAddPrincipledSetup(Operator, NWBase, ImportHelper):
+ bl_idname = "node.nw_add_textures_for_principled"
+ bl_label = "Principled Texture Setup"
+ bl_description = "Add Texture Node Setup for Principled BSDF"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ directory = StringProperty(
+ name='Directory',
+ subtype='DIR_PATH',
+ default='',
+ description='Folder to search in for image files')
+ files = CollectionProperty(
+ type=bpy.types.OperatorFileListElement,
+ options={'HIDDEN', 'SKIP_SAVE'})
+
+ order = [
+ "filepath",
+ "files",
+ ]
+
+ @classmethod
+ def poll(cls, context):
+ valid = False
+ if nw_check(context):
+ space = context.space_data
+ if space.tree_type == 'ShaderNodeTree' and context.scene.render.engine == 'CYCLES':
+ valid = True
+ return valid
+
+ def execute(self, context):
+ # Check if everything is ok
+ if not self.directory:
+ self.report({'INFO'}, 'No Folder Selected')
+ return {'CANCELLED'}
+ if not self.files[:]:
+ self.report({'INFO'}, 'No Files Selected')
+ return {'CANCELLED'}
+
+ nodes, links = get_nodes_links(context)
+ active_node = nodes.active
+ if not active_node.bl_idname == 'ShaderNodeBsdfPrincipled':
+ self.report({'INFO'}, 'Select Principled BSDF')
+ return {'CANCELLED'}
+
+ # Helper_functions
+ def split_into__components(fname):
+ # Split filename into components
+ # 'WallTexture_diff_2k.002.jpg' -> ['Wall', 'Texture', 'diff', 'k']
+ # Remove extension
+ fname = path.splitext(fname)[0]
+ # Remove digits
+ fname = ''.join(i for i in fname if not i.isdigit())
+ # Seperate CamelCase by space
+ fname = re.sub("([a-z])([A-Z])","\g<1> \g<2>",fname)
+ # Replace common separators with SPACE
+ seperators = ['_', '.', '-', '__', '--', '#']
+ for sep in seperators:
+ fname = fname.replace(sep, ' ')
+
+ components = fname.split(' ')
+ components = [c.lower() for c in components]
+ return components
+
+ # Filter textures names for texturetypes in filenames
+ # [Socket Name, [abbreviations and keyword list], Filename placeholder]
+ normal_abbr = ['normal', 'nor', 'nrm', 'nrml', 'norm']
+ bump_abbr = ['bump', 'bmp']
+ gloss_abbr = ['gloss', 'glossy', 'glossyness']
+ rough_abbr = ['roughness', 'rough', 'rgh']
+ socketnames = [
+ ['Displacement', ['displacement', 'disp', 'dsp'], None],
+ ['Base Color', ['diffuse', 'diff', 'albedo', 'base', 'col', 'color'], None],
+ ['Subsurface Color', ['sss', 'subsurface'], None],
+ ['Metallic', ['metalness', 'metal', 'mtl'], None],
+ ['Specular', ['specularity', 'specular', 'spec', 'spc'], None],
+ ['Roughness', rough_abbr + gloss_abbr, None],
+ ['Normal', normal_abbr + bump_abbr, None],
+ ]
+ # Look through texture_types and set value as filename of first matched file
+ def match_files_to_socket_names():
+ for sname in socketnames:
+ for file in self.files:
+ fname = file.name
+ filenamecomponents = split_into__components(fname)
+ matches = set(sname[1]).intersection(set(filenamecomponents))
+ if matches:
+ sname[2] = fname
+ break
+
+ match_files_to_socket_names()
+ # Remove socketnames without found files
+ socketnames = [s for s in socketnames if s[2]
+ and path.exists(self.directory+s[2])]
+ if not socketnames:
+ self.report({'INFO'}, 'No matching images found')
+ print('No matching images found')
+ return {'CANCELLED'}
+
+ # Add found images
+ print('\nMatched Textures:')
+ texture_nodes = []
+ disp_texture = None
+ normal_node = None
+ roughness_node = None
+ for i, sname in enumerate(socketnames):
+ print(i, sname[0], sname[2])
+
+ # DISPLACEMENT NODES
+ if sname[0] == 'Displacement':
+ disp_texture = nodes.new(type='ShaderNodeTexImage')
+ img = bpy.data.images.load(self.directory+sname[2])
+ disp_texture.image = img
+ disp_texture.label = 'Displacement'
+ disp_texture.color_space = 'NONE'
+
+ # Add displacement offset nodes
+ math_sub = nodes.new(type='ShaderNodeMath')
+ math_sub.operation = 'SUBTRACT'
+ math_sub.label = 'Offset'
+ math_sub.location = active_node.location + Vector((0, -560))
+ math_mul = nodes.new(type='ShaderNodeMath')
+ math_mul.operation = 'MULTIPLY'
+ math_mul.label = 'Strength'
+ math_mul.location = math_sub.location + Vector((200, 0))
+ link = links.new(math_mul.inputs[0], math_sub.outputs[0])
+ link = links.new(math_sub.inputs[0], disp_texture.outputs[0])
+
+ # Turn on true displacement in the material
+ # Too complicated for now
+
+ '''
+ # Frame. Does not update immediatly
+ # Seems to need an editor redraw
+ frame = nodes.new(type='NodeFrame')
+ frame.label = 'Displacement'
+ math_sub.parent = frame
+ math_mul.parent = frame
+ frame.update()
+ '''
+
+ #find ouput node
+ output_node = [n for n in nodes if n.bl_idname == 'ShaderNodeOutputMaterial']
+ if output_node:
+ if not output_node[0].inputs[2].is_linked:
+ link = links.new(output_node[0].inputs[2], math_mul.outputs[0])
+
+ continue
+
+ if not active_node.inputs[sname[0]].is_linked:
+ # No texture node connected -> add texture node with new image
+ texture_node = nodes.new(type='ShaderNodeTexImage')
+ img = bpy.data.images.load(self.directory+sname[2])
+ texture_node.image = img
+
+ # NORMAL NODES
+ if sname[0] == 'Normal':
+ # Test if new texture node is normal or bump map
+ fname_components = split_into__components(sname[2])
+ match_normal = set(normal_abbr).intersection(set(fname_components))
+ match_bump = set(bump_abbr).intersection(set(fname_components))
+ if match_normal:
+ # If Normal add normal node in between
+ normal_node = nodes.new(type='ShaderNodeNormalMap')
+ link = links.new(normal_node.inputs[1], texture_node.outputs[0])
+ elif match_bump:
+ # If Bump add bump node in between
+ normal_node = nodes.new(type='ShaderNodeBump')
+ link = links.new(normal_node.inputs[2], texture_node.outputs[0])
+
+ link = links.new(active_node.inputs[sname[0]], normal_node.outputs[0])
+ normal_node_texture = texture_node
+
+ elif sname[0] == 'Roughness':
+ # Test if glossy or roughness map
+ fname_components = split_into__components(sname[2])
+ match_rough = set(rough_abbr).intersection(set(fname_components))
+ match_gloss = set(gloss_abbr).intersection(set(fname_components))
+
+ if match_rough:
+ # If Roughness nothing to to
+ link = links.new(active_node.inputs[sname[0]], texture_node.outputs[0])
+
+ elif match_gloss:
+ # If Gloss Map add invert node
+ invert_node = nodes.new(type='ShaderNodeInvert')
+ link = links.new(invert_node.inputs[1], texture_node.outputs[0])
+
+ link = links.new(active_node.inputs[sname[0]], invert_node.outputs[0])
+ roughness_node = texture_node
+
+ else:
+ # This is a simple connection Texture --> Input slot
+ link = links.new(active_node.inputs[sname[0]], texture_node.outputs[0])
+
+ # Use non-color for all but 'Base Color' Textures
+ if not sname[0] in ['Base Color']:
+ texture_node.color_space = 'NONE'
+
+ else:
+ # If already texture connected. add to node list for alignment
+ texture_node = active_node.inputs[sname[0]].links[0].from_node
+
+ # This are all connected texture nodes
+ texture_nodes.append(texture_node)
+ texture_node.label = sname[0]
+
+ if disp_texture:
+ texture_nodes.append(disp_texture)
+
+ # Alignment
+
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-extensions-cvs
mailing list