diff --git a/node_wrangler/__init__.py b/node_wrangler/__init__.py index 757c80ba3..ec93a2be9 100644 --- a/node_wrangler/__init__.py +++ b/node_wrangler/__init__.py @@ -71,7 +71,7 @@ rl_outputs = ( RL_entry('use_pass_uv', 'UV', 'UV', True, True), RL_entry('use_pass_vector', 'Speed', 'Vector', False, True), RL_entry('use_pass_z', 'Z', 'Depth', True, True), - ) +) # list of blend types of "Mix" nodes in a form that can be used as 'items' for EnumProperty. # used list, not tuple for easy merging with other lists. @@ -192,11 +192,13 @@ draw_color_sets = { viewer_socket_name = "tmp_viewer" + def get_nodes_from_category(category_name, context): for category in node_categories_iter(context): if category.name == category_name: return sorted(category.items(context), key=lambda node: node.label) + def get_first_enabled_output(node): for output in node.outputs: if output.enabled: @@ -204,9 +206,11 @@ def get_first_enabled_output(node): else: return node.outputs[0] + def is_visible_socket(socket): return not socket.hide and socket.enabled and socket.type != 'CUSTOM' + def nice_hotkey_name(punc): # convert the ugly string name into the actual character nice_name = { @@ -329,12 +333,14 @@ def autolink(node1, node2, links): print("Could not make a link from " + node1.name + " to " + node2.name) return link_made + def abs_node_location(node): abs_location = node.location if node.parent is None: return abs_location return abs_location + abs_node_location(node.parent) + def node_at_pos(nodes, context, event): nodes_under_mouse = [] target_node = None @@ -395,17 +401,18 @@ def store_mouse_cursor(context, event): else: space.cursor_location = tree.view_center + def draw_line(x1, y1, x2, y2, size, colour=(1.0, 1.0, 1.0, 0.7)): shader = gpu.shader.from_builtin('POLYLINE_SMOOTH_COLOR') shader.uniform_float("viewportSize", gpu.state.viewport_get()[2:]) shader.uniform_float("lineWidth", size * prefs_line_width()) vertices = ((x1, y1), (x2, y2)) - vertex_colors = ((colour[0]+(1.0-colour[0])/4, - colour[1]+(1.0-colour[1])/4, - colour[2]+(1.0-colour[2])/4, - colour[3]+(1.0-colour[3])/4), - colour) + vertex_colors = ((colour[0] + (1.0 - colour[0]) / 4, + colour[1] + (1.0 - colour[1]) / 4, + colour[2] + (1.0 - colour[2]) / 4, + colour[3] + (1.0 - colour[3]) / 4), + colour) batch = batch_for_shader(shader, 'LINE_STRIP', {"pos": vertices, "color": vertex_colors}) batch.draw(shader) @@ -416,7 +423,7 @@ def draw_circle_2d_filled(mx, my, radius, colour=(1.0, 1.0, 1.0, 0.7)): sides = 12 vertices = [(radius * cos(i * 2 * pi / sides) + mx, radius * sin(i * 2 * pi / sides) + my) - for i in range(sides + 1)] + for i in range(sides + 1)] shader = gpu.shader.from_builtin('UNIFORM_COLOR') shader.uniform_float("color", colour) @@ -431,8 +438,8 @@ def draw_rounded_node_border(node, radius=8, colour=(1.0, 1.0, 1.0, 0.7)): nlocx, nlocy = abs_node_location(node) - nlocx = (nlocx+1) * dpi_fac() - nlocy = (nlocy+1) * dpi_fac() + nlocx = (nlocx + 1) * dpi_fac() + nlocy = (nlocy + 1) * dpi_fac() ndimx = node.dimensions.x ndimy = node.dimensions.y @@ -451,52 +458,52 @@ def draw_rounded_node_border(node, radius=8, colour=(1.0, 1.0, 1.0, 0.7)): # Top left corner mx, my = bpy.context.region.view2d.view_to_region(nlocx, nlocy, clip=False) - vertices = [(mx,my)] - for i in range(sides+1): - if (4<=i<=8): + vertices = [(mx, my)] + for i in range(sides + 1): + if (4 <= i <= 8): if mx < area_width: cosine = radius * cos(i * 2 * pi / sides) + mx sine = radius * sin(i * 2 * pi / sides) + my - vertices.append((cosine,sine)) + vertices.append((cosine, sine)) batch = batch_for_shader(shader, 'TRI_FAN', {"pos": vertices}) batch.draw(shader) # Top right corner mx, my = bpy.context.region.view2d.view_to_region(nlocx + ndimx, nlocy, clip=False) - vertices = [(mx,my)] - for i in range(sides+1): - if (0<=i<=4): + vertices = [(mx, my)] + for i in range(sides + 1): + if (0 <= i <= 4): if mx < area_width: cosine = radius * cos(i * 2 * pi / sides) + mx sine = radius * sin(i * 2 * pi / sides) + my - vertices.append((cosine,sine)) + vertices.append((cosine, sine)) batch = batch_for_shader(shader, 'TRI_FAN', {"pos": vertices}) batch.draw(shader) # Bottom left corner mx, my = bpy.context.region.view2d.view_to_region(nlocx, nlocy - ndimy, clip=False) - vertices = [(mx,my)] - for i in range(sides+1): - if (8<=i<=12): + vertices = [(mx, my)] + for i in range(sides + 1): + if (8 <= i <= 12): if mx < area_width: cosine = radius * cos(i * 2 * pi / sides) + mx sine = radius * sin(i * 2 * pi / sides) + my - vertices.append((cosine,sine)) + vertices.append((cosine, sine)) batch = batch_for_shader(shader, 'TRI_FAN', {"pos": vertices}) batch.draw(shader) # Bottom right corner mx, my = bpy.context.region.view2d.view_to_region(nlocx + ndimx, nlocy - ndimy, clip=False) - vertices = [(mx,my)] - for i in range(sides+1): - if (12<=i<=16): + vertices = [(mx, my)] + for i in range(sides + 1): + if (12 <= i <= 16): if mx < area_width: cosine = radius * cos(i * 2 * pi / sides) + mx sine = radius * sin(i * 2 * pi / sides) + my - vertices.append((cosine,sine)) + vertices.append((cosine, sine)) batch = batch_for_shader(shader, 'TRI_FAN', {"pos": vertices}) batch.draw(shader) @@ -510,10 +517,10 @@ def draw_rounded_node_border(node, radius=8, colour=(1.0, 1.0, 1.0, 0.7)): m1x, m1y = bpy.context.region.view2d.view_to_region(nlocx, nlocy, clip=False) m2x, m2y = bpy.context.region.view2d.view_to_region(nlocx, nlocy - ndimy, clip=False) if m1x < area_width and m2x < area_width: - vertices.extend([(m2x-radius,m2y), (m2x,m2y), - (m1x,m1y), (m1x-radius,m1y)]) - indices.extend([(id_last, id_last+1, id_last+3), - (id_last+3, id_last+1, id_last+2)]) + vertices.extend([(m2x - radius, m2y), (m2x, m2y), + (m1x, m1y), (m1x - radius, m1y)]) + indices.extend([(id_last, id_last + 1, id_last + 3), + (id_last + 3, id_last + 1, id_last + 2)]) id_last += 4 # Top edge @@ -521,37 +528,38 @@ def draw_rounded_node_border(node, radius=8, colour=(1.0, 1.0, 1.0, 0.7)): m2x, m2y = bpy.context.region.view2d.view_to_region(nlocx + ndimx, nlocy, clip=False) m1x = min(m1x, area_width) m2x = min(m2x, area_width) - vertices.extend([(m1x,m1y), (m2x,m1y), - (m2x,m1y+radius), (m1x,m1y+radius)]) - indices.extend([(id_last, id_last+1, id_last+3), - (id_last+3, id_last+1, id_last+2)]) + vertices.extend([(m1x, m1y), (m2x, m1y), + (m2x, m1y + radius), (m1x, m1y + radius)]) + indices.extend([(id_last, id_last + 1, id_last + 3), + (id_last + 3, id_last + 1, id_last + 2)]) id_last += 4 # Right edge m1x, m1y = bpy.context.region.view2d.view_to_region(nlocx + ndimx, nlocy, clip=False) m2x, m2y = bpy.context.region.view2d.view_to_region(nlocx + ndimx, nlocy - ndimy, clip=False) if m1x < area_width and m2x < area_width: - vertices.extend([(m1x,m2y), (m1x+radius,m2y), - (m1x+radius,m1y), (m1x,m1y)]) - indices.extend([(id_last, id_last+1, id_last+3), - (id_last+3, id_last+1, id_last+2)]) + vertices.extend([(m1x, m2y), (m1x + radius, m2y), + (m1x + radius, m1y), (m1x, m1y)]) + indices.extend([(id_last, id_last + 1, id_last + 3), + (id_last + 3, id_last + 1, id_last + 2)]) id_last += 4 # Bottom edge - m1x, m1y = bpy.context.region.view2d.view_to_region(nlocx, nlocy-ndimy, clip=False) - m2x, m2y = bpy.context.region.view2d.view_to_region(nlocx + ndimx, nlocy-ndimy, clip=False) + m1x, m1y = bpy.context.region.view2d.view_to_region(nlocx, nlocy - ndimy, clip=False) + m2x, m2y = bpy.context.region.view2d.view_to_region(nlocx + ndimx, nlocy - ndimy, clip=False) m1x = min(m1x, area_width) m2x = min(m2x, area_width) - vertices.extend([(m1x,m2y), (m2x,m2y), - (m2x,m1y-radius), (m1x,m1y-radius)]) - indices.extend([(id_last, id_last+1, id_last+3), - (id_last+3, id_last+1, id_last+2)]) + vertices.extend([(m1x, m2y), (m2x, m2y), + (m2x, m1y - radius), (m1x, m1y - radius)]) + indices.extend([(id_last, id_last + 1, id_last + 3), + (id_last + 3, id_last + 1, id_last + 2)]) # now draw all edges in one batch if len(vertices) != 0: batch = batch_for_shader(shader, 'TRIS', {"pos": vertices}, indices=indices) batch.draw(shader) + def draw_callback_nodeoutline(self, context, mode): if self.mouse_path: gpu.state.blend_set('ALPHA') @@ -602,6 +610,7 @@ def draw_callback_nodeoutline(self, context, mode): gpu.state.blend_set('NONE') + def get_active_tree(context): tree = context.space_data.node_tree path = [] @@ -616,16 +625,19 @@ def get_active_tree(context): path.append(tree) return tree, path + def get_nodes_links(context): tree, path = get_active_tree(context) return tree.nodes, tree.links + def is_viewer_socket(socket): # checks if a internal socket is a valid viewer socket return socket.name == viewer_socket_name and socket.NWViewerSocket + def get_internal_socket(socket): - #get the internal socket from a socket inside or outside the group + # get the internal socket from a socket inside or outside the group node = socket.node if node.type == 'GROUP_OUTPUT': source_iterator = node.inputs @@ -648,6 +660,7 @@ def get_internal_socket(socket): break return iterator[i] + def is_viewer_link(link, output_node): if link.to_node == output_node and link.to_socket == output_node.inputs[0]: return True @@ -657,11 +670,13 @@ def is_viewer_link(link, output_node): return True return False + def get_group_output_node(tree): for node in tree.nodes: - if node.type == 'GROUP_OUTPUT' and node.is_active_output == True: + if node.type == 'GROUP_OUTPUT' and node.is_active_output: return node + def get_output_location(tree): # get right-most location sorted_by_xloc = (sorted(tree.nodes, key=lambda x: x.location.x)) @@ -677,6 +692,8 @@ def get_output_location(tree): return loc_x, loc_y # Principled prefs + + class NWPrincipledPreferences(bpy.types.PropertyGroup): base_color: StringProperty( name='Base Color', @@ -732,6 +749,8 @@ class NWPrincipledPreferences(bpy.types.PropertyGroup): description='Naming Components for AO maps') # Addon prefs + + class NWNodeWrangler(bpy.types.AddonPreferences): bl_idname = __name__ @@ -779,7 +798,11 @@ class NWNodeWrangler(bpy.types.AddonPreferences): box = layout.box() col = box.column(align=True) - col.prop(self, "show_principled_lists", text='Edit tags for auto texture detection in Principled BSDF setup', toggle=True) + col.prop( + self, + "show_principled_lists", + text='Edit tags for auto texture detection in Principled BSDF setup', + toggle=True) if self.show_principled_lists: tags = self.principled_tags @@ -823,7 +846,6 @@ class NWNodeWrangler(bpy.types.AddonPreferences): row.label(text=keystr) - def nw_check(context): space = context.space_data valid_trees = ["ShaderNodeTree", "CompositorNodeTree", "TextureNodeTree", "GeometryNodeTree"] @@ -836,6 +858,7 @@ def nw_check(context): return False + class NWBase: @classmethod def poll(cls, context): @@ -908,7 +931,8 @@ class NWLazyMix(Operator, NWBase): args = (self, context, 'MIX') # Add the region OpenGL drawing callback # draw in view space with 'POST_VIEW' and 'PRE_VIEW' - self._handle = bpy.types.SpaceNodeEditor.draw_handler_add(draw_callback_nodeoutline, args, 'WINDOW', 'POST_PIXEL') + self._handle = bpy.types.SpaceNodeEditor.draw_handler_add( + draw_callback_nodeoutline, args, 'WINDOW', 'POST_PIXEL') self.mouse_path = [] @@ -966,7 +990,7 @@ class NWLazyConnect(Operator, NWBase): original_sel = [] original_unsel = [] for node in nodes: - if node.select == True: + if node.select: node.select = False original_sel.append(node) else: @@ -1013,7 +1037,8 @@ class NWLazyConnect(Operator, NWBase): args = (self, context, mode) # Add the region OpenGL drawing callback # draw in view space with 'POST_VIEW' and 'PRE_VIEW' - self._handle = bpy.types.SpaceNodeEditor.draw_handler_add(draw_callback_nodeoutline, args, 'WINDOW', 'POST_PIXEL') + self._handle = bpy.types.SpaceNodeEditor.draw_handler_add( + draw_callback_nodeoutline, args, 'WINDOW', 'POST_PIXEL') self.mouse_path = [] @@ -1030,13 +1055,19 @@ class NWDeleteUnused(Operator, NWBase): bl_label = 'Delete Unused Nodes' bl_options = {'REGISTER', 'UNDO'} - delete_muted: BoolProperty(name="Delete Muted", description="Delete (but reconnect, like Ctrl-X) all muted nodes", default=True) - delete_frames: BoolProperty(name="Delete Empty Frames", description="Delete all frames that have no nodes inside them", default=True) + delete_muted: BoolProperty( + name="Delete Muted", + description="Delete (but reconnect, like Ctrl-X) all muted nodes", + default=True) + delete_frames: BoolProperty( + name="Delete Empty Frames", + description="Delete all frames that have no nodes inside them", + default=True) def is_unused_node(self, node): - end_types = ['OUTPUT_MATERIAL', 'OUTPUT', 'VIEWER', 'COMPOSITE', \ - 'SPLITVIEWER', 'OUTPUT_FILE', 'LEVELS', 'OUTPUT_LIGHT', \ - 'OUTPUT_WORLD', 'GROUP_INPUT', 'GROUP_OUTPUT', 'FRAME'] + end_types = ['OUTPUT_MATERIAL', 'OUTPUT', 'VIEWER', 'COMPOSITE', + 'SPLITVIEWER', 'OUTPUT_FILE', 'LEVELS', 'OUTPUT_LIGHT', + 'OUTPUT_WORLD', 'GROUP_INPUT', 'GROUP_OUTPUT', 'FRAME'] if node.type in end_types: return False @@ -1059,7 +1090,7 @@ class NWDeleteUnused(Operator, NWBase): # Store selection selection = [] for node in nodes: - if node.select == True: + if node.select: selection.append(node.name) for node in nodes: @@ -1176,12 +1207,14 @@ class NWSwapLinks(Operator, NWBase): try: links.new(n2.outputs[connection[0]], connection[1]) except: - self.report({'WARNING'}, "Some connections have been lost due to differing numbers of output sockets") + self.report({'WARNING'}, + "Some connections have been lost due to differing numbers of output sockets") for connection in n2_outputs: try: links.new(n1.outputs[connection[0]], connection[1]) except: - self.report({'WARNING'}, "Some connections have been lost due to differing numbers of output sockets") + self.report({'WARNING'}, + "Some connections have been lost due to differing numbers of output sockets") else: if n1.outputs or n2.outputs: self.report({'WARNING'}, "One of the nodes has no outputs!") @@ -1195,14 +1228,14 @@ class NWSwapLinks(Operator, NWBase): return {'FINISHED'} if n1.inputs: types = [] - i=0 + i = 0 for i1 in n1.inputs: if i1.is_linked and not i1.is_multi_input: similar_types = 0 for i2 in n1.inputs: if i1.type == i2.type and i2.is_linked and not i2.is_multi_input: similar_types += 1 - types.append ([i1, similar_types, i]) + types.append([i1, similar_types, i]) i += 1 types.sort(key=lambda k: k[1], reverse=True) @@ -1223,7 +1256,7 @@ class NWSwapLinks(Operator, NWBase): fs = t[0].links[0].from_socket i = t[2] links.remove(t[0].links[0]) - if i+1 == len(n1.inputs): + if i + 1 == len(n1.inputs): i = -1 i += 1 while n1.inputs[i].is_linked: @@ -1281,6 +1314,7 @@ class NWAddAttrNode(Operator, NWBase): nodes.active.attribute_name = self.attr_name return {'FINISHED'} + class NWPreviewNode(Operator, NWBase): bl_idname = "node.nw_preview_node" bl_label = "Preview Node" @@ -1308,24 +1342,24 @@ class NWPreviewNode(Operator, NWBase): return False def ensure_viewer_socket(self, node, socket_type, connect_socket=None): - #check if a viewer output already exists in a node group otherwise create + # check if a viewer output already exists in a node group otherwise create if hasattr(node, "node_tree"): index = None if len(node.node_tree.outputs): free_socket = None for i, socket in enumerate(node.node_tree.outputs): if is_viewer_socket(socket) and is_visible_socket(node.outputs[i]) and socket.type == socket_type: - #if viewer output is already used but leads to the same socket we can still use it + # if viewer output is already used but leads to the same socket we can still use it is_used = self.is_socket_used_other_mats(socket) if is_used: - if connect_socket == None: + if connect_socket is None: continue groupout = get_group_output_node(node.node_tree) groupout_input = groupout.inputs[i] links = groupout_input.links if connect_socket not in [link.from_socket for link in links]: continue - index=i + index = i break if not free_socket: free_socket = i @@ -1333,7 +1367,7 @@ class NWPreviewNode(Operator, NWBase): index = free_socket if not index: - #create viewer socket + # create viewer socket node.node_tree.outputs.new(socket_type, viewer_socket_name) index = len(node.node_tree.outputs) - 1 node.node_tree.outputs[index].NWViewerSocket = True @@ -1354,12 +1388,12 @@ class NWPreviewNode(Operator, NWBase): def get_shader_output_node(self, tree): for node in tree.nodes: - if node.type == self.shader_output_type and node.is_active_output == True: + if node.type == self.shader_output_type and node.is_active_output: return node @classmethod def ensure_group_output(cls, tree): - #check if a group output node exists otherwise create + # check if a group output node exists otherwise create groupout = get_group_output_node(tree) if not groupout: groupout = tree.nodes.new('NodeGroupOutput') @@ -1388,7 +1422,7 @@ class NWPreviewNode(Operator, NWBase): socket = next_node.node_tree.outputs[socket_index] if is_viewer_socket(socket) and socket not in sockets: sockets.append(socket) - #continue search inside of node group but restrict socket to where we came from + # continue search inside of node group but restrict socket to where we came from groupout = get_group_output_node(next_node.node_tree) cls.search_sockets(groupout, sockets, index=socket_index) @@ -1405,12 +1439,12 @@ class NWPreviewNode(Operator, NWBase): cls.scan_nodes(node.node_tree, sockets) def link_leads_to_used_socket(self, link): - #return True if link leads to a socket that is already used in this material + # return True if link leads to a socket that is already used in this material socket = get_internal_socket(link.to_socket) return (socket and self.is_socket_used_active_mat(socket)) def is_socket_used_active_mat(self, socket): - #ensure used sockets in active material is calculated and check given socket + # ensure used sockets in active material is calculated and check given socket if not hasattr(self, "used_viewer_sockets_active_mat"): self.used_viewer_sockets_active_mat = [] materialout = self.get_shader_output_node(bpy.context.space_data.node_tree) @@ -1419,7 +1453,7 @@ class NWPreviewNode(Operator, NWBase): return socket in self.used_viewer_sockets_active_mat def is_socket_used_other_mats(self, socket): - #ensure used sockets in other materials are calculated and check given socket + # ensure used sockets in other materials are calculated and check given socket if not hasattr(self, "used_viewer_sockets_other_mats"): self.used_viewer_sockets_other_mats = [] for mat in bpy.data.materials: @@ -1493,7 +1527,7 @@ class NWPreviewNode(Operator, NWBase): socket_type = 'GEOMETRY' # Find an input socket of the output of type geometry geometryoutindex = None - for i,inp in enumerate(geometryoutput.inputs): + for i, inp in enumerate(geometryoutput.inputs): if inp.type == socket_type: geometryoutindex = i break @@ -1510,7 +1544,8 @@ class NWPreviewNode(Operator, NWBase): link_end = output_socket while tree.nodes.active != active: node = tree.nodes.active - index = self.ensure_viewer_socket(node,'NodeSocketGeometry', connect_socket=active.outputs[out_i] if node.node_tree.nodes.active == active else None) + index = self.ensure_viewer_socket( + node, 'NodeSocketGeometry', connect_socket=active.outputs[out_i] if node.node_tree.nodes.active == active else None) link_start = node.outputs[index] node_socket = node.node_tree.outputs[index] if node_socket in delete_sockets: @@ -1531,7 +1566,6 @@ class NWPreviewNode(Operator, NWBase): force_update(context) return {'FINISHED'} - # What follows is code for the shader editor output_types = [x.nodetype for x in get_nodes_from_category('Output', context)] @@ -1547,7 +1581,7 @@ class NWPreviewNode(Operator, NWBase): materialout = None # placeholder node delete_sockets = [] - #scan through all nodes in tree including nodes inside of groups to find viewer sockets + # scan through all nodes in tree including nodes inside of groups to find viewer sockets self.scan_nodes(base_node_tree, delete_sockets) materialout = self.get_shader_output_node(base_node_tree) @@ -1586,7 +1620,8 @@ class NWPreviewNode(Operator, NWBase): link_end = output_socket while tree.nodes.active != active: node = tree.nodes.active - index = self.ensure_viewer_socket(node, socket_type, connect_socket=active.outputs[out_i] if node.node_tree.nodes.active == active else None) + index = self.ensure_viewer_socket( + node, socket_type, connect_socket=active.outputs[out_i] if node.node_tree.nodes.active == active else None) link_start = node.outputs[index] node_socket = node.node_tree.outputs[index] if node_socket in delete_sockets: @@ -1649,7 +1684,7 @@ class NWFrameSelected(Operator, NWBase): nodes, links = get_nodes_links(context) selected = [] for node in nodes: - if node.select == True: + if node.select: selected.append(node) bpy.ops.node.add_node(type='NodeFrame') @@ -1715,7 +1750,7 @@ class NWSwitchNodeType(Operator, NWBase): to_type: StringProperty( name="Switch to type", - default = '', + default='', ) def execute(self, context): @@ -1783,8 +1818,10 @@ class NWSwitchNodeType(Operator, NWBase): for the_type in types_order_one: if socket.type == the_type: # create values for sockets['INPUTS'][the_type] and sockets['OUTPUTS'][the_type] - # entry structure: (index_in_type, socket_index, socket_name, socket_default_value, socket_links) - sockets[in_out_name][the_type].append((len(sockets[in_out_name][the_type]), i, the_name, dval, socket_links)) + # entry structure: (index_in_type, socket_index, socket_name, + # socket_default_value, socket_links) + sockets[in_out_name][the_type].append( + (len(sockets[in_out_name][the_type]), i, the_name, dval, socket_links)) # Check which of the types in inputs/outputs is considered to be "main". # Set values of sockets['INPUTS']['MAIN'] and sockets['OUTPUTS']['MAIN'] for type_check in types_order_one: @@ -1903,7 +1940,7 @@ class NWMergeNodes(Operator, NWBase): mode: EnumProperty( name="mode", description="All possible blend types, boolean operations and math operations", - items= blend_types + [op for op in geo_combine_operations if op not in blend_types] + [op for op in operations if op not in blend_types], + items=blend_types + [op for op in geo_combine_operations if op not in blend_types] + [op for op in operations if op not in blend_types], ) merge_type: EnumProperty( name="merge type", @@ -1925,7 +1962,7 @@ class NWMergeNodes(Operator, NWBase): # in selected_nodes, it returns False. The depth is used to prevent # getting stuck in a loop because of an already present cycle. @staticmethod - def link_creates_cycle(link, selected_nodes, depth=0)->bool: + def link_creates_cycle(link, selected_nodes, depth=0) -> bool: if depth > 255: # We're stuck in a cycle, but that cycle was already present, # so we return False. @@ -1939,7 +1976,7 @@ class NWMergeNodes(Operator, NWBase): for output in node.outputs: if output.is_linked: for olink in output.links: - if NWMergeNodes.link_creates_cycle(olink, selected_nodes, depth+1): + if NWMergeNodes.link_creates_cycle(olink, selected_nodes, depth + 1): return True # None of the outputs found a node in selected_nodes, so there is no cycle. return False @@ -1949,14 +1986,14 @@ class NWMergeNodes(Operator, NWBase): # be connected. The last one is assumed to be a multi input socket. # For convenience the node is returned. @staticmethod - def merge_with_multi_input(nodes_list, merge_position,do_hide, loc_x, links, nodes, node_name, socket_indices): + def merge_with_multi_input(nodes_list, merge_position, do_hide, loc_x, links, nodes, node_name, socket_indices): # The y-location of the last node loc_y = nodes_list[-1][2] if merge_position == 'CENTER': # Average the y-location - for i in range(len(nodes_list)-1): + for i in range(len(nodes_list) - 1): loc_y += nodes_list[i][2] - loc_y = loc_y/len(nodes_list) + loc_y = loc_y / len(nodes_list) new_node = nodes.new(node_name) new_node.hide = do_hide new_node.location.x = loc_x @@ -1964,12 +2001,14 @@ class NWMergeNodes(Operator, NWBase): selected_nodes = [nodes[node_info[0]] for node_info in nodes_list] prev_links = [] outputs_for_multi_input = [] - for i,node in enumerate(selected_nodes): + for i, node in enumerate(selected_nodes): node.select = False # Search for the first node which had output links that do not create # a cycle, which we can then reconnect afterwards. if prev_links == [] and node.outputs[0].is_linked: - prev_links = [link for link in node.outputs[0].links if not NWMergeNodes.link_creates_cycle(link, selected_nodes)] + prev_links = [ + link for link in node.outputs[0].links if not NWMergeNodes.link_creates_cycle( + link, selected_nodes)] # Get the index of the socket, the last one is a multi input, and is thus used repeatedly # To get the placement to look right we need to reverse the order in which we connect the # outputs to the multi input socket. @@ -2025,9 +2064,9 @@ class NWMergeNodes(Operator, NWBase): node_type = 'ShaderNode' selected_mix = [] # entry = [index, loc] selected_shader = [] # entry = [index, loc] - selected_geometry = [] # entry = [index, loc] + selected_geometry = [] # entry = [index, loc] selected_math = [] # entry = [index, loc] - selected_vector = [] # entry = [index, loc] + selected_vector = [] # entry = [index, loc] selected_z = [] # entry = [index, loc] selected_alphaover = [] # entry = [index, loc] @@ -2081,7 +2120,14 @@ class NWMergeNodes(Operator, NWBase): if selected_mix and selected_math and merge_type == 'AUTO': selected_mix += selected_math selected_math = [] - for nodes_list in [selected_mix, selected_shader, selected_geometry, selected_math, selected_vector, selected_z, selected_alphaover]: + for nodes_list in [ + selected_mix, + selected_shader, + selected_geometry, + selected_math, + selected_vector, + selected_z, + selected_alphaover]: if not nodes_list: continue count_before = len(nodes) @@ -2100,8 +2146,9 @@ class NWMergeNodes(Operator, NWBase): else: node_type = 'GeometryNode' if merge_position == 'CENTER': - loc_y = ((nodes_list[len(nodes_list) - 1][2]) + (nodes_list[len(nodes_list) - 2][2])) / 2 # average yloc of last two nodes (lowest two) - if nodes_list[len(nodes_list) - 1][-1] == True: # if last node is hidden, mix should be shifted up a bit + # average yloc of last two nodes (lowest two) + loc_y = ((nodes_list[len(nodes_list) - 1][2]) + (nodes_list[len(nodes_list) - 2][2])) / 2 + if nodes_list[len(nodes_list) - 1][-1]: # if last node is hidden, mix should be shifted up a bit if do_hide: loc_y += 40 else: @@ -2171,11 +2218,13 @@ class NWMergeNodes(Operator, NWBase): elif nodes_list == selected_geometry: if mode in ('JOIN', 'MIX'): add_type = node_type + 'JoinGeometry' - add = self.merge_with_multi_input(nodes_list, merge_position, do_hide, loc_x, links, nodes, add_type,[0]) + add = self.merge_with_multi_input( + nodes_list, merge_position, do_hide, loc_x, links, nodes, add_type, [0]) else: add_type = node_type + 'MeshBoolean' - indices = [0,1] if mode == 'DIFFERENCE' else [1] - add = self.merge_with_multi_input(nodes_list, merge_position, do_hide, loc_x, links, nodes, add_type,indices) + indices = [0, 1] if mode == 'DIFFERENCE' else [1] + add = self.merge_with_multi_input( + nodes_list, merge_position, do_hide, loc_x, links, nodes, add_type, indices) add.operation = mode was_multi = True break @@ -2221,7 +2270,8 @@ class NWMergeNodes(Operator, NWBase): # "last" node has been added as first, so its index is count_before. last_add = nodes[count_before] # Create list of invalid indexes. - invalid_nodes = [nodes[n[0]] for n in (selected_mix + selected_math + selected_shader + selected_z + selected_geometry)] + invalid_nodes = [nodes[n[0]] + for n in (selected_mix + selected_math + selected_shader + selected_z + selected_geometry)] # Special case: # Two nodes were selected and first selected has no output links, second selected has output links. @@ -2299,7 +2349,7 @@ class NWBatchChangeNodes(Operator, NWBase): operation = self.operation for node in context.selected_nodes: if node.type == 'MIX_RGB' or (node.bl_idname == 'ShaderNodeMix' and node.data_type == 'RGBA'): - if not blend_type in [nav[0] for nav in navs]: + if blend_type not in [nav[0] for nav in navs]: node.blend_type = blend_type else: if blend_type == 'NEXT': @@ -2318,7 +2368,7 @@ class NWBatchChangeNodes(Operator, NWBase): node.blend_type = blend_types[index - 1][0] if node.type == 'MATH' or node.bl_idname == 'ShaderNodeMath': - if not operation in [nav[0] for nav in navs]: + if operation not in [nav[0] for nav in navs]: node.operation = operation else: if operation == 'NEXT': @@ -2414,7 +2464,11 @@ class NWCopySettings(Operator, NWBase): # Report nodes that are not valid valid_node_names = [n.name for n in valid_nodes] not_valid_names = list(set(selected_node_names) - set(valid_node_names)) - self.report({'INFO'}, "Ignored {} (not of the same type as {})".format(", ".join(not_valid_names), node_active.name)) + self.report( + {'INFO'}, + "Ignored {} (not of the same type as {})".format( + ", ".join(not_valid_names), + node_active.name)) # Reference original orig = node_active @@ -2479,7 +2533,11 @@ class NWCopySettings(Operator, NWBase): orig.select = True node_tree.nodes.active = orig - self.report({'INFO'}, "Successfully copied attributes from {} to: {}".format(orig.name, ", ".join(success_names))) + self.report( + {'INFO'}, + "Successfully copied attributes from {} to: {}".format( + orig.name, + ", ".join(success_names))) return {'FINISHED'} @@ -2587,7 +2645,10 @@ class NWAddTextureSetup(Operator, NWBase): bl_description = "Add Texture Node Setup to Selected Shaders" bl_options = {'REGISTER', 'UNDO'} - add_mapping: BoolProperty(name="Add Mapping Nodes", description="Create coordinate and mapping nodes for the texture (ignored for selected texture nodes)", default=True) + add_mapping: BoolProperty( + name="Add Mapping Nodes", + description="Create coordinate and mapping nodes for the texture (ignored for selected texture nodes)", + default=True) @classmethod def poll(cls, context): @@ -2728,23 +2789,23 @@ class NWAddPrincipledSetup(Operator, NWBase, ImportHelper): gloss_abbr = tags.gloss.split(' ') rough_abbr = tags.rough.split(' ') socketnames = [ - ['Displacement', tags.displacement.split(' '), None], - ['Base Color', tags.base_color.split(' '), None], - ['Subsurface Color', tags.sss_color.split(' '), None], - ['Metallic', tags.metallic.split(' '), None], - ['Specular', tags.specular.split(' '), None], - ['Roughness', rough_abbr + gloss_abbr, None], - ['Normal', normal_abbr + bump_abbr, None], - ['Transmission', tags.transmission.split(' '), None], - ['Emission', tags.emission.split(' '), None], - ['Alpha', tags.alpha.split(' '), None], - ['Ambient Occlusion', tags.ambient_occlusion.split(' '), None], + ['Displacement', tags.displacement.split(' '), None], + ['Base Color', tags.base_color.split(' '), None], + ['Subsurface Color', tags.sss_color.split(' '), None], + ['Metallic', tags.metallic.split(' '), None], + ['Specular', tags.specular.split(' '), None], + ['Roughness', rough_abbr + gloss_abbr, None], + ['Normal', normal_abbr + bump_abbr, None], + ['Transmission', tags.transmission.split(' '), None], + ['Emission', tags.emission.split(' '), None], + ['Alpha', tags.alpha.split(' '), None], + ['Ambient Occlusion', tags.ambient_occlusion.split(' '), None], ] match_files_to_socket_names(self.files, socketnames) # Remove socketnames without found files socketnames = [s for s in socketnames if s[2] - and path.exists(self.directory+s[2])] + and path.exists(self.directory + s[2])] if not socketnames: self.report({'INFO'}, 'No matching images found') print('No matching images found') @@ -2891,7 +2952,8 @@ class NWAddPrincipledSetup(Operator, NWBase, ImportHelper): # If more than one texture add reroute node in between reroute = nodes.new(type='NodeReroute') texture_nodes.append(reroute) - tex_coords = Vector((texture_nodes[0].location.x, sum(n.location.y for n in texture_nodes)/len(texture_nodes))) + tex_coords = Vector((texture_nodes[0].location.x, + sum(n.location.y for n in texture_nodes) / len(texture_nodes))) reroute.location = tex_coords + Vector((-50, -120)) for texture_node in texture_nodes: link = links.new(texture_node.inputs[0], reroute.outputs[0]) @@ -3131,7 +3193,8 @@ class NWAlignNodes(Operator, NWBase): active_loc = copy(nodes.active.location) # make a copy, not a reference # Check if nodes should be laid out horizontally or vertically - x_locs = [n.location.x + (n.dimensions.x / 2) for n in selection] # use dimension to get center of node, not corner + # use dimension to get center of node, not corner + x_locs = [n.location.x + (n.dimensions.x / 2) for n in selection] y_locs = [n.location.y - (n.dimensions.y / 2) for n in selection] x_range = max(x_locs) - min(x_locs) y_range = max(y_locs) - min(y_locs) @@ -3166,7 +3229,8 @@ class NWAlignNodes(Operator, NWBase): for node in selection: node.location += active_loc_diff else: # Position nodes centered around where they used to be - locs = ([n.location.x + (n.dimensions.x / 2) for n in selection]) if horizontal else ([n.location.y - (n.dimensions.y / 2) for n in selection]) + locs = ([n.location.x + (n.dimensions.x / 2) for n in selection] + ) if horizontal else ([n.location.y - (n.dimensions.y / 2) for n in selection]) new_mid = (max(locs) + min(locs)) / 2 for node in selection: if horizontal: @@ -3252,8 +3316,8 @@ class NWLinkToOutputNode(Operator): active = nodes.active output_index = None tree_type = context.space_data.tree_type - shader_outputs = {'OBJECT': 'ShaderNodeOutputMaterial', - 'WORLD': 'ShaderNodeOutputWorld', + shader_outputs = {'OBJECT': 'ShaderNodeOutputMaterial', + 'WORLD': 'ShaderNodeOutputWorld', 'LINESTYLE': 'ShaderNodeOutputLineStyle'} output_type = { 'ShaderNodeTree': shader_outputs[context.space_data.shader_type], @@ -3394,24 +3458,23 @@ class NWAddSequence(Operator, NWBase, ImportHelper): if not files[0].name and not filename: self.report({'ERROR'}, "No file chosen") return {'CANCELLED'} - elif files[0].name and (not filename or not path.exists(directory+filename)): + elif files[0].name and (not filename or not path.exists(directory + filename)): # User has selected multiple files without an active one, or the active one is non-existent filename = files[0].name - if not path.exists(directory+filename): - self.report({'ERROR'}, filename+" does not exist!") + if not path.exists(directory + filename): + self.report({'ERROR'}, filename + " does not exist!") return {'CANCELLED'} without_ext = '.'.join(filename.split('.')[:-1]) # if last digit isn't a number, it's not a sequence if not without_ext[-1].isdigit(): - self.report({'ERROR'}, filename+" does not seem to be part of a sequence") + self.report({'ERROR'}, filename + " does not seem to be part of a sequence") return {'CANCELLED'} - extension = filename.split('.')[-1] - reverse = without_ext[::-1] # reverse string + reverse = without_ext[::-1] # reverse string count_numbers = 0 for char in reverse: @@ -3420,9 +3483,9 @@ class NWAddSequence(Operator, NWBase, ImportHelper): else: break - without_num = without_ext[:count_numbers*-1] + without_num = without_ext[:count_numbers * -1] - files = sorted(glob(directory + without_num + "[0-9]"*count_numbers + "." + extension)) + files = sorted(glob(directory + without_num + "[0-9]" * count_numbers + "." + extension)) num_frames = len(files) @@ -3434,18 +3497,18 @@ class NWAddSequence(Operator, NWBase, ImportHelper): for node in nodes: node.select = False yloc += node_mid_pt(node, 'y') - yloc = yloc/len(nodes) + yloc = yloc / len(nodes) else: xloc = 0 yloc = 0 - name_with_hashes = without_num + "#"*count_numbers + '.' + extension + name_with_hashes = without_num + "#" * count_numbers + '.' + extension bpy.ops.node.add_node('INVOKE_DEFAULT', use_transform=True, type=node_type) node = nodes.active node.label = name_with_hashes - filepath = directory+(without_ext+'.'+extension) + filepath = directory + (without_ext + '.' + extension) if self.relative_path: if bpy.data.filepath: try: @@ -3458,7 +3521,8 @@ class NWAddSequence(Operator, NWBase, ImportHelper): img.name = name_with_hashes node.image = img image_user = node.image_user if tree.type == 'SHADER' else node - image_user.frame_offset = int(files[0][len(without_num)+len(directory):-1*(len(extension)+1)]) - 1 # separate the number from the file name of the first file + # separate the number from the file name of the first file + image_user.frame_offset = int(files[0][len(without_num) + len(directory):-1 * (len(extension) + 1)]) - 1 image_user.frame_duration = num_frames return {'FINISHED'} @@ -3480,7 +3544,7 @@ class NWAddMultipleImages(Operator, NWBase, ImportHelper): def execute(self, context): nodes, links = get_nodes_links(context) - xloc, yloc = context.region.view2d.region_to_view(context.area.width/2, context.area.height/2) + xloc, yloc = context.region.view2d.region_to_view(context.area.width / 2, context.area.height / 2) if context.space_data.node_tree.type == 'SHADER': node_type = "ShaderNodeTexImage" @@ -3503,7 +3567,7 @@ class NWAddMultipleImages(Operator, NWBase, ImportHelper): node.location.y = yloc yloc -= 40 - img = bpy.data.images.load(self.directory+fname) + img = bpy.data.images.load(self.directory + fname) node.image = img # shift new nodes up to center of tree @@ -3511,7 +3575,7 @@ class NWAddMultipleImages(Operator, NWBase, ImportHelper): for node in nodes: if node in new_nodes: node.select = True - node.location.y += (list_size/2) + node.location.y += (list_size / 2) else: node.select = False return {'FINISHED'} @@ -3535,7 +3599,7 @@ class NWViewerFocus(bpy.types.Operator): def invoke(self, context, event): render = context.scene.render space = context.space_data - percent = render.resolution_percentage*0.01 + percent = render.resolution_percentage * 0.01 nodes, links = get_nodes_links(context) viewers = [n for n in nodes if n.type == 'VIEWER'] @@ -3545,11 +3609,11 @@ class NWViewerFocus(bpy.types.Operator): mlocy = event.mouse_region_y select_node = bpy.ops.node.select(location=(mlocx, mlocy), extend=False) - if not 'FINISHED' in select_node: # only run if we're not clicking on a node + if 'FINISHED' not in select_node: # only run if we're not clicking on a node region_x = context.region.width region_y = context.region.height - region_center_x = context.region.width / 2 + region_center_x = context.region.width / 2 region_center_y = context.region.height / 2 bd_x = render.resolution_x * percent * space.backdrop_zoom @@ -3579,21 +3643,21 @@ class NWSaveViewer(bpy.types.Operator, ExportHelper): bl_label = "Save This Image" filepath: StringProperty(subtype="FILE_PATH") filename_ext: EnumProperty( - name="Format", - description="Choose the file format to save to", - items=(('.bmp', "BMP", ""), - ('.rgb', 'IRIS', ""), - ('.png', 'PNG', ""), - ('.jpg', 'JPEG', ""), - ('.jp2', 'JPEG2000', ""), - ('.tga', 'TARGA', ""), - ('.cin', 'CINEON', ""), - ('.dpx', 'DPX', ""), - ('.exr', 'OPEN_EXR', ""), - ('.hdr', 'HDR', ""), - ('.tif', 'TIFF', "")), - default='.png', - ) + name="Format", + description="Choose the file format to save to", + items=(('.bmp', "BMP", ""), + ('.rgb', 'IRIS', ""), + ('.png', 'PNG', ""), + ('.jpg', 'JPEG', ""), + ('.jp2', 'JPEG2000', ""), + ('.tga', 'TARGA', ""), + ('.cin', 'CINEON', ""), + ('.dpx', 'DPX', ""), + ('.exr', 'OPEN_EXR', ""), + ('.hdr', 'HDR', ""), + ('.tif', 'TIFF', "")), + default='.png', + ) @classmethod def poll(cls, context): @@ -3609,19 +3673,19 @@ class NWSaveViewer(bpy.types.Operator, ExportHelper): fp = self.filepath if fp: formats = { - '.bmp': 'BMP', - '.rgb': 'IRIS', - '.png': 'PNG', - '.jpg': 'JPEG', - '.jpeg': 'JPEG', - '.jp2': 'JPEG2000', - '.tga': 'TARGA', - '.cin': 'CINEON', - '.dpx': 'DPX', - '.exr': 'OPEN_EXR', - '.hdr': 'HDR', - '.tiff': 'TIFF', - '.tif': 'TIFF'} + '.bmp': 'BMP', + '.rgb': 'IRIS', + '.png': 'PNG', + '.jpg': 'JPEG', + '.jpeg': 'JPEG', + '.jp2': 'JPEG2000', + '.tga': 'TARGA', + '.cin': 'CINEON', + '.dpx': 'DPX', + '.exr': 'OPEN_EXR', + '.hdr': 'HDR', + '.tiff': 'TIFF', + '.tif': 'TIFF'} basename, ext = path.splitext(fp) old_render_format = context.scene.render.image_settings.file_format context.scene.render.image_settings.file_format = formats[self.filename_ext] @@ -3647,7 +3711,7 @@ class NWResetNodes(bpy.types.Operator): def execute(self, context): node_active = context.active_node node_selected = context.selected_nodes - node_ignore = ["FRAME","REROUTE", "GROUP"] + node_ignore = ["FRAME", "REROUTE", "GROUP"] # Check if one node is selected at least if not (len(node_selected) > 0): @@ -3859,9 +3923,11 @@ class NWMergeNodesMenu(Menu, NWBase): props.mode = 'MIX' props.merge_type = 'ALPHAOVER' + class NWMergeGeometryMenu(Menu, NWBase): bl_idname = "NODE_MT_nw_merge_geometry_menu" bl_label = "Merge Selected Nodes using Geometry Nodes" + def draw(self, context): layout = self.layout # The boolean node + Join Geometry node @@ -3870,6 +3936,7 @@ class NWMergeGeometryMenu(Menu, NWBase): props.mode = type props.merge_type = 'GEOMETRY' + class NWMergeShadersMenu(Menu, NWBase): bl_idname = "NODE_MT_nw_merge_shaders_menu" bl_label = "Merge Selected Nodes using Shaders" @@ -3906,7 +3973,7 @@ class NWConnectionListOutputs(Menu, NWBase): for index, output in enumerate(n1.outputs): # Only show sockets that are exposed. if output.enabled: - layout.operator(NWCallInputsMenu.bl_idname, text=output.name, icon="RADIOBUT_OFF").from_socket=index + layout.operator(NWCallInputsMenu.bl_idname, text=output.name, icon="RADIOBUT_OFF").from_socket = index class NWConnectionListInputs(Menu, NWBase): @@ -4176,7 +4243,7 @@ def save_viewer_menu_func(self, context): def reset_nodes_button(self, context): node_active = context.active_node node_selected = context.selected_nodes - node_ignore = ["FRAME","REROUTE", "GROUP"] + node_ignore = ["FRAME", "REROUTE", "GROUP"] # Check if active node is in the selection and respective type if (len(node_selected) == 1) and node_active and node_active.select and node_active.type not in node_ignore: @@ -4318,10 +4385,14 @@ kmi_defs = ( (NWLinkActiveToSelected.bl_idname, 'SEMI_COLON', 'PRESS', False, True, False, (('replace', True), ('use_node_name', False), ('use_outputs_names', True),), "Link active to selected (Replace links, output names)"), # CHANGE MIX FACTOR - (NWChangeMixFactor.bl_idname, 'LEFT_ARROW', 'PRESS', False, False, True, (('option', -0.1),), "Reduce Mix Factor by 0.1"), - (NWChangeMixFactor.bl_idname, 'RIGHT_ARROW', 'PRESS', False, False, True, (('option', 0.1),), "Increase Mix Factor by 0.1"), - (NWChangeMixFactor.bl_idname, 'LEFT_ARROW', 'PRESS', False, True, True, (('option', -0.01),), "Reduce Mix Factor by 0.01"), - (NWChangeMixFactor.bl_idname, 'RIGHT_ARROW', 'PRESS', False, True, True, (('option', 0.01),), "Increase Mix Factor by 0.01"), + (NWChangeMixFactor.bl_idname, 'LEFT_ARROW', 'PRESS', False, + False, True, (('option', -0.1),), "Reduce Mix Factor by 0.1"), + (NWChangeMixFactor.bl_idname, 'RIGHT_ARROW', 'PRESS', False, + False, True, (('option', 0.1),), "Increase Mix Factor by 0.1"), + (NWChangeMixFactor.bl_idname, 'LEFT_ARROW', 'PRESS', False, + True, True, (('option', -0.01),), "Reduce Mix Factor by 0.01"), + (NWChangeMixFactor.bl_idname, 'RIGHT_ARROW', 'PRESS', False, + True, True, (('option', 0.01),), "Increase Mix Factor by 0.01"), (NWChangeMixFactor.bl_idname, 'LEFT_ARROW', 'PRESS', True, True, True, (('option', 0.0),), "Set Mix Factor to 0.0"), (NWChangeMixFactor.bl_idname, 'RIGHT_ARROW', 'PRESS', True, True, True, (('option', 1.0),), "Set Mix Factor to 1.0"), (NWChangeMixFactor.bl_idname, 'NUMPAD_0', 'PRESS', True, True, True, (('option', 0.0),), "Set Mix Factor to 0.0"), @@ -4333,16 +4404,19 @@ kmi_defs = ( # MODIFY LABEL (Alt Shift L) (NWModifyLabels.bl_idname, 'L', 'PRESS', False, True, True, None, "Modify node labels"), # Copy Label from active to selected - (NWCopyLabel.bl_idname, 'V', 'PRESS', False, True, False, (('option', 'FROM_ACTIVE'),), "Copy label from active to selected"), + (NWCopyLabel.bl_idname, 'V', 'PRESS', False, True, False, + (('option', 'FROM_ACTIVE'),), "Copy label from active to selected"), # DETACH OUTPUTS (Alt Shift D) (NWDetachOutputs.bl_idname, 'D', 'PRESS', False, True, True, None, "Detach outputs"), # LINK TO OUTPUT NODE (O) (NWLinkToOutputNode.bl_idname, 'O', 'PRESS', False, False, False, None, "Link to output node"), # SELECT PARENT/CHILDREN # Select Children - (NWSelectParentChildren.bl_idname, 'RIGHT_BRACKET', 'PRESS', False, False, False, (('option', 'CHILD'),), "Select children"), + (NWSelectParentChildren.bl_idname, 'RIGHT_BRACKET', 'PRESS', + False, False, False, (('option', 'CHILD'),), "Select children"), # Select Parent - (NWSelectParentChildren.bl_idname, 'LEFT_BRACKET', 'PRESS', False, False, False, (('option', 'PARENT'),), "Select Parent"), + (NWSelectParentChildren.bl_idname, 'LEFT_BRACKET', 'PRESS', + False, False, False, (('option', 'PARENT'),), "Select Parent"), # Add Texture Setup (NWAddTextureSetup.bl_idname, 'T', 'PRESS', True, False, False, None, "Add texture setup"), # Add Principled BSDF Texture Setup @@ -4356,8 +4430,10 @@ kmi_defs = ( # Swap Links (NWSwapLinks.bl_idname, 'S', 'PRESS', False, False, True, None, "Swap Links"), # Preview Node - (NWPreviewNode.bl_idname, 'LEFTMOUSE', 'PRESS', True, True, False, (('run_in_geometry_nodes', False),), "Preview node output"), - (NWPreviewNode.bl_idname, 'LEFTMOUSE', 'PRESS', False, True, True, (('run_in_geometry_nodes', True),), "Preview node output"), + (NWPreviewNode.bl_idname, 'LEFTMOUSE', 'PRESS', True, True, False, + (('run_in_geometry_nodes', False),), "Preview node output"), + (NWPreviewNode.bl_idname, 'LEFTMOUSE', 'PRESS', False, True, + True, (('run_in_geometry_nodes', True),), "Preview node output"), # Reload Images (NWReloadImages.bl_idname, 'R', 'PRESS', False, False, True, None, "Reload images"), # Lazy Mix @@ -4365,18 +4441,22 @@ kmi_defs = ( # Lazy Connect (NWLazyConnect.bl_idname, 'RIGHTMOUSE', 'PRESS', False, False, True, (('with_menu', False),), "Lazy Connect"), # Lazy Connect with Menu - (NWLazyConnect.bl_idname, 'RIGHTMOUSE', 'PRESS', False, True, True, (('with_menu', True),), "Lazy Connect with Socket Menu"), + (NWLazyConnect.bl_idname, 'RIGHTMOUSE', 'PRESS', False, True, + True, (('with_menu', True),), "Lazy Connect with Socket Menu"), # Viewer Tile Center (NWViewerFocus.bl_idname, 'LEFTMOUSE', 'DOUBLE_CLICK', False, False, False, None, "Set Viewers Tile Center"), # Align Nodes (NWAlignNodes.bl_idname, 'EQUAL', 'PRESS', False, True, False, None, "Align selected nodes neatly in a row/column"), # Reset Nodes (Back Space) - (NWResetNodes.bl_idname, 'BACK_SPACE', 'PRESS', False, False, False, None, "Revert node back to default state, but keep connections"), + (NWResetNodes.bl_idname, 'BACK_SPACE', 'PRESS', False, False, False, + None, "Revert node back to default state, but keep connections"), # MENUS ('wm.call_menu', 'W', 'PRESS', False, True, False, (('name', NodeWranglerMenu.bl_idname),), "Node Wrangler menu"), ('wm.call_menu', 'SLASH', 'PRESS', False, False, False, (('name', NWAddReroutesMenu.bl_idname),), "Add Reroutes menu"), - ('wm.call_menu', 'NUMPAD_SLASH', 'PRESS', False, False, False, (('name', NWAddReroutesMenu.bl_idname),), "Add Reroutes menu"), - ('wm.call_menu', 'BACK_SLASH', 'PRESS', False, False, False, (('name', NWLinkActiveToSelectedMenu.bl_idname),), "Link active to selected (menu)"), + ('wm.call_menu', 'NUMPAD_SLASH', 'PRESS', False, False, False, + (('name', NWAddReroutesMenu.bl_idname),), "Add Reroutes menu"), + ('wm.call_menu', 'BACK_SLASH', 'PRESS', False, False, False, + (('name', NWLinkActiveToSelectedMenu.bl_idname),), "Link active to selected (menu)"), ('wm.call_menu', 'C', 'PRESS', False, True, False, (('name', NWCopyToSelectedMenu.bl_idname),), "Copy to selected (menu)"), ('wm.call_menu', 'S', 'PRESS', False, True, False, (('name', NWSwitchNodeTypeMenu.bl_idname),), "Switch node type menu"), ) @@ -4440,6 +4520,7 @@ classes = ( NWSwitchNodeTypeMenu, ) + def register(): from bpy.utils import register_class @@ -4541,5 +4622,6 @@ def unregister(): for cls in classes: unregister_class(cls) + if __name__ == "__main__": register()