[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