Changeset View
Changeset View
Standalone View
Standalone View
node_wrangler.py
| Show All 14 Lines | |||||
| # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
| # | # | ||||
| # ##### END GPL LICENSE BLOCK ##### | # ##### END GPL LICENSE BLOCK ##### | ||||
| bl_info = { | bl_info = { | ||||
| "name": "Node Wrangler", | "name": "Node Wrangler", | ||||
| "author": "Bartek Skorupa, Greg Zaal, Sebastian Koenig, Christian Brinkmann, Florian Meyer", | "author": "Bartek Skorupa, Greg Zaal, Sebastian Koenig, Christian Brinkmann, Florian Meyer", | ||||
| "version": (3, 35), | "version": (3, 35), | ||||
| "blender": (2, 78, 0), | "blender": (2, 80, 0), | ||||
| "location": "Node Editor Toolbar or Ctrl-Space", | "location": "Node Editor Toolbar or Ctrl-Space", | ||||
| "description": "Various tools to enhance and speed up node-based workflow", | "description": "Various tools to enhance and speed up node-based workflow", | ||||
| "warning": "", | "warning": "", | ||||
| "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/" | "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/" | ||||
| "Scripts/Nodes/Nodes_Efficiency_Tools", | "Scripts/Nodes/Nodes_Efficiency_Tools", | ||||
| "category": "Node", | "category": "Node", | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 78 Lines • ▼ Show 20 Lines | shaders_input_nodes_props = ( | ||||
| ('ShaderNodeParticleInfo', 'PARTICLE_INFO', 'Particle Info'), | ('ShaderNodeParticleInfo', 'PARTICLE_INFO', 'Particle Info'), | ||||
| ('ShaderNodeCameraData', 'CAMERA', 'Camera Data'), | ('ShaderNodeCameraData', 'CAMERA', 'Camera Data'), | ||||
| ('ShaderNodeUVMap', 'UVMAP', 'UV Map'), | ('ShaderNodeUVMap', 'UVMAP', 'UV Map'), | ||||
| ) | ) | ||||
| # (rna_type.identifier, type, rna_type.name) | # (rna_type.identifier, type, rna_type.name) | ||||
| # Keeping mixed case to avoid having to translate entries when adding new nodes in operators. | # Keeping mixed case to avoid having to translate entries when adding new nodes in operators. | ||||
| shaders_output_nodes_props = ( | shaders_output_nodes_props = ( | ||||
| ('ShaderNodeOutputMaterial', 'OUTPUT_MATERIAL', 'Material Output'), | ('ShaderNodeOutputMaterial', 'OUTPUT_MATERIAL', 'Material Output'), | ||||
| ('ShaderNodeOutputLamp', 'OUTPUT_LAMP', 'Lamp Output'), | ('ShaderNodeOutputLight', 'OUTPUT_LIGHT', 'Light Output'), | ||||
| ('ShaderNodeOutputWorld', 'OUTPUT_WORLD', 'World Output'), | ('ShaderNodeOutputWorld', 'OUTPUT_WORLD', 'World Output'), | ||||
| ) | ) | ||||
| # (rna_type.identifier, type, rna_type.name) | # (rna_type.identifier, type, rna_type.name) | ||||
| # Keeping mixed case to avoid having to translate entries when adding new nodes in operators. | # Keeping mixed case to avoid having to translate entries when adding new nodes in operators. | ||||
| shaders_shader_nodes_props = ( | shaders_shader_nodes_props = ( | ||||
| ('ShaderNodeMixShader', 'MIX_SHADER', 'Mix Shader'), | ('ShaderNodeMixShader', 'MIX_SHADER', 'Mix Shader'), | ||||
| ('ShaderNodeAddShader', 'ADD_SHADER', 'Add Shader'), | ('ShaderNodeAddShader', 'ADD_SHADER', 'Add Shader'), | ||||
| ('ShaderNodeBsdfDiffuse', 'BSDF_DIFFUSE', 'Diffuse BSDF'), | ('ShaderNodeBsdfDiffuse', 'BSDF_DIFFUSE', 'Diffuse BSDF'), | ||||
| ▲ Show 20 Lines • Show All 200 Lines • ▼ Show 20 Lines | compo_layout_nodes_props = ( | ||||
| ('CompositorNodeSwitch', 'SWITCH', 'Switch'), | ('CompositorNodeSwitch', 'SWITCH', 'Switch'), | ||||
| ) | ) | ||||
| # Blender Render material nodes | # Blender Render material nodes | ||||
| # (rna_type.identifier, type, rna_type.name) | # (rna_type.identifier, type, rna_type.name) | ||||
| # Keeping mixed case to avoid having to translate entries when adding new nodes in operators. | # Keeping mixed case to avoid having to translate entries when adding new nodes in operators. | ||||
| blender_mat_input_nodes_props = ( | blender_mat_input_nodes_props = ( | ||||
| ('ShaderNodeMaterial', 'MATERIAL', 'Material'), | ('ShaderNodeMaterial', 'MATERIAL', 'Material'), | ||||
| ('ShaderNodeCameraData', 'CAMERA', 'Camera Data'), | ('ShaderNodeCameraData', 'CAMERA', 'Camera Data'), | ||||
| ('ShaderNodeLampData', 'LAMP', 'Lamp Data'), | ('ShaderNodeLightData', 'LIGHT', 'Light Data'), | ||||
| ('ShaderNodeValue', 'VALUE', 'Value'), | ('ShaderNodeValue', 'VALUE', 'Value'), | ||||
| ('ShaderNodeRGB', 'RGB', 'RGB'), | ('ShaderNodeRGB', 'RGB', 'RGB'), | ||||
| ('ShaderNodeTexture', 'TEXTURE', 'Texture'), | ('ShaderNodeTexture', 'TEXTURE', 'Texture'), | ||||
| ('ShaderNodeGeometry', 'GEOMETRY', 'Geometry'), | ('ShaderNodeGeometry', 'GEOMETRY', 'Geometry'), | ||||
| ('ShaderNodeExtendedMaterial', 'MATERIAL_EXT', 'Extended Material'), | ('ShaderNodeExtendedMaterial', 'MATERIAL_EXT', 'Extended Material'), | ||||
| ) | ) | ||||
| # (rna_type.identifier, type, rna_type.name) | # (rna_type.identifier, type, rna_type.name) | ||||
| ▲ Show 20 Lines • Show All 198 Lines • ▼ Show 20 Lines | draw_color_sets = { | ||||
| "black": ( | "black": ( | ||||
| (1.0, 1.0, 1.0, 0.7), | (1.0, 1.0, 1.0, 0.7), | ||||
| (0.0, 0.0, 0.0, 0.7), | (0.0, 0.0, 0.0, 0.7), | ||||
| (0.2, 0.2, 0.2, 1.0) | (0.2, 0.2, 0.2, 1.0) | ||||
| ) | ) | ||||
| } | } | ||||
| def is_cycles_or_eevee(context): | |||||
| return context.scene.render.engine in {'CYCLES', 'BLENDER_EEVEE'} | |||||
| def nice_hotkey_name(punc): | def nice_hotkey_name(punc): | ||||
| # convert the ugly string name into the actual character | # convert the ugly string name into the actual character | ||||
| pairs = ( | pairs = ( | ||||
| ('LEFTMOUSE', "LMB"), | ('LEFTMOUSE', "LMB"), | ||||
| ('MIDDLEMOUSE', "MMB"), | ('MIDDLEMOUSE', "MMB"), | ||||
| ('RIGHTMOUSE', "RMB"), | ('RIGHTMOUSE', "RMB"), | ||||
| ('SELECTMOUSE', "Select"), | ('SELECTMOUSE', "Select"), | ||||
| ('WHEELUPMOUSE', "Wheel Up"), | ('WHEELUPMOUSE', "Wheel Up"), | ||||
| ▲ Show 20 Lines • Show All 437 Lines • ▼ Show 20 Lines | def get_nodes_links(context): | ||||
| if tree.nodes.active: | if tree.nodes.active: | ||||
| while tree.nodes.active != context.active_node: | while tree.nodes.active != context.active_node: | ||||
| tree = tree.nodes.active.node_tree | tree = tree.nodes.active.node_tree | ||||
| return tree.nodes, tree.links | return tree.nodes, tree.links | ||||
| # Principled prefs | # Principled prefs | ||||
| class NWPrincipledPreferences(bpy.types.PropertyGroup): | class NWPrincipledPreferences(bpy.types.PropertyGroup): | ||||
| base_color = StringProperty( | base_color: StringProperty( | ||||
| name='Base Color', | name='Base Color', | ||||
| default='diffuse diff albedo base col color', | default='diffuse diff albedo base col color', | ||||
| description='Naming Components for Base Color maps') | description='Naming Components for Base Color maps') | ||||
| sss_color = StringProperty( | sss_color: StringProperty( | ||||
| name='Subsurface Color', | name='Subsurface Color', | ||||
| default='sss subsurface', | default='sss subsurface', | ||||
| description='Naming Components for Subsurface Color maps') | description='Naming Components for Subsurface Color maps') | ||||
| metallic = StringProperty( | metallic: StringProperty( | ||||
| name='Metallic', | name='Metallic', | ||||
| default='metallic metalness metal mtl', | default='metallic metalness metal mtl', | ||||
| description='Naming Components for metallness maps') | description='Naming Components for metallness maps') | ||||
| specular = StringProperty( | specular: StringProperty( | ||||
| name='Specular', | name='Specular', | ||||
| default='specularity specular spec spc', | default='specularity specular spec spc', | ||||
| description='Naming Components for Specular maps') | description='Naming Components for Specular maps') | ||||
| normal = StringProperty( | normal: StringProperty( | ||||
| name='Normal', | name='Normal', | ||||
| default='normal nor nrm nrml norm', | default='normal nor nrm nrml norm', | ||||
| description='Naming Components for Normal maps') | description='Naming Components for Normal maps') | ||||
| bump = StringProperty( | bump: StringProperty( | ||||
| name='Bump', | name='Bump', | ||||
| default='bump bmp', | default='bump bmp', | ||||
| description='Naming Components for bump maps') | description='Naming Components for bump maps') | ||||
| rough = StringProperty( | rough: StringProperty( | ||||
| name='Roughness', | name='Roughness', | ||||
| default='roughness rough rgh', | default='roughness rough rgh', | ||||
| description='Naming Components for roughness maps') | description='Naming Components for roughness maps') | ||||
| gloss = StringProperty( | gloss: StringProperty( | ||||
| name='Gloss', | name='Gloss', | ||||
| default='gloss glossy glossyness', | default='gloss glossy glossyness', | ||||
| description='Naming Components for glossy maps') | description='Naming Components for glossy maps') | ||||
| displacement = StringProperty( | displacement: StringProperty( | ||||
| name='Displacement', | name='Displacement', | ||||
| default='displacement displace disp dsp height heightmap', | default='displacement displace disp dsp height heightmap', | ||||
| description='Naming Components for displacement maps') | description='Naming Components for displacement maps') | ||||
| # Addon prefs | # Addon prefs | ||||
| class NWNodeWrangler(bpy.types.AddonPreferences): | class NWNodeWrangler(bpy.types.AddonPreferences): | ||||
| bl_idname = __name__ | bl_idname = __name__ | ||||
| merge_hide = EnumProperty( | merge_hide: EnumProperty( | ||||
| name="Hide Mix nodes", | name="Hide Mix nodes", | ||||
| items=( | items=( | ||||
| ("ALWAYS", "Always", "Always collapse the new merge nodes"), | ("ALWAYS", "Always", "Always collapse the new merge nodes"), | ||||
| ("NON_SHADER", "Non-Shader", "Collapse in all cases except for shaders"), | ("NON_SHADER", "Non-Shader", "Collapse in all cases except for shaders"), | ||||
| ("NEVER", "Never", "Never collapse the new merge nodes") | ("NEVER", "Never", "Never collapse the new merge nodes") | ||||
| ), | ), | ||||
| default='NON_SHADER', | default='NON_SHADER', | ||||
| description="When merging nodes with the Ctrl+Numpad0 hotkey (and similar) specifiy whether to collapse them or show the full node with options expanded") | description="When merging nodes with the Ctrl+Numpad0 hotkey (and similar) specifiy whether to collapse them or show the full node with options expanded") | ||||
| merge_position = EnumProperty( | merge_position: EnumProperty( | ||||
| name="Mix Node Position", | name="Mix Node Position", | ||||
| items=( | items=( | ||||
| ("CENTER", "Center", "Place the Mix node between the two nodes"), | ("CENTER", "Center", "Place the Mix node between the two nodes"), | ||||
| ("BOTTOM", "Bottom", "Place the Mix node at the same height as the lowest node") | ("BOTTOM", "Bottom", "Place the Mix node at the same height as the lowest node") | ||||
| ), | ), | ||||
| default='CENTER', | default='CENTER', | ||||
| description="When merging nodes with the Ctrl+Numpad0 hotkey (and similar) specifiy the position of the new nodes") | description="When merging nodes with the Ctrl+Numpad0 hotkey (and similar) specifiy the position of the new nodes") | ||||
| show_hotkey_list = BoolProperty( | show_hotkey_list: BoolProperty( | ||||
| name="Show Hotkey List", | name="Show Hotkey List", | ||||
| default=False, | default=False, | ||||
| description="Expand this box into a list of all the hotkeys for functions in this addon" | description="Expand this box into a list of all the hotkeys for functions in this addon" | ||||
| ) | ) | ||||
| hotkey_list_filter = StringProperty( | hotkey_list_filter: StringProperty( | ||||
| name=" Filter by Name", | name=" Filter by Name", | ||||
| default="", | default="", | ||||
| description="Show only hotkeys that have this text in their name" | description="Show only hotkeys that have this text in their name" | ||||
| ) | ) | ||||
| show_principled_lists = BoolProperty( | show_principled_lists: BoolProperty( | ||||
| name="Show Principled naming tags", | name="Show Principled naming tags", | ||||
| default=False, | default=False, | ||||
| description="Expand this box into a list of all naming tags for principled texture setup" | description="Expand this box into a list of all naming tags for principled texture setup" | ||||
| ) | ) | ||||
| principled_tags = bpy.props.PointerProperty(type=NWPrincipledPreferences) | principled_tags: bpy.props.PointerProperty(type=NWPrincipledPreferences) | ||||
| def draw(self, context): | def draw(self, context): | ||||
| layout = self.layout | layout = self.layout | ||||
| col = layout.column() | col = layout.column() | ||||
| col.prop(self, "merge_position") | col.prop(self, "merge_position") | ||||
| col.prop(self, "merge_hide") | col.prop(self, "merge_hide") | ||||
| box = layout.box() | box = layout.box() | ||||
| ▲ Show 20 Lines • Show All 132 Lines • ▼ Show 20 Lines | def invoke(self, context, event): | ||||
| return {'CANCELLED'} | return {'CANCELLED'} | ||||
| class NWLazyConnect(Operator, NWBase): | class NWLazyConnect(Operator, NWBase): | ||||
| """Connect two nodes without clicking a specific socket (automatically determined""" | """Connect two nodes without clicking a specific socket (automatically determined""" | ||||
| bl_idname = "node.nw_lazy_connect" | bl_idname = "node.nw_lazy_connect" | ||||
| bl_label = "Lazy Connect" | bl_label = "Lazy Connect" | ||||
| bl_options = {'REGISTER', 'UNDO'} | bl_options = {'REGISTER', 'UNDO'} | ||||
| with_menu = BoolProperty() | with_menu: BoolProperty() | ||||
| def modal(self, context, event): | def modal(self, context, event): | ||||
| context.area.tag_redraw() | context.area.tag_redraw() | ||||
| nodes, links = get_nodes_links(context) | nodes, links = get_nodes_links(context) | ||||
| cont = True | cont = True | ||||
| start_pos = [event.mouse_region_x, event.mouse_region_y] | start_pos = [event.mouse_region_x, event.mouse_region_y] | ||||
| ▲ Show 20 Lines • Show All 89 Lines • ▼ Show 20 Lines | |||||
| class NWDeleteUnused(Operator, NWBase): | class NWDeleteUnused(Operator, NWBase): | ||||
| """Delete all nodes whose output is not used""" | """Delete all nodes whose output is not used""" | ||||
| bl_idname = 'node.nw_del_unused' | bl_idname = 'node.nw_del_unused' | ||||
| bl_label = 'Delete Unused Nodes' | bl_label = 'Delete Unused Nodes' | ||||
| bl_options = {'REGISTER', 'UNDO'} | bl_options = {'REGISTER', 'UNDO'} | ||||
| delete_muted = BoolProperty(name="Delete Muted", description="Delete (but reconnect, like Ctrl-X) all muted nodes", 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) | 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): | def is_unused_node(self, node): | ||||
| end_types = ['OUTPUT_MATERIAL', 'OUTPUT', 'VIEWER', 'COMPOSITE', \ | end_types = ['OUTPUT_MATERIAL', 'OUTPUT', 'VIEWER', 'COMPOSITE', \ | ||||
| 'SPLITVIEWER', 'OUTPUT_FILE', 'LEVELS', 'OUTPUT_LAMP', \ | 'SPLITVIEWER', 'OUTPUT_FILE', 'LEVELS', 'OUTPUT_LIGHT', \ | ||||
| 'OUTPUT_WORLD', 'GROUP_INPUT', 'GROUP_OUTPUT', 'FRAME'] | 'OUTPUT_WORLD', 'GROUP_INPUT', 'GROUP_OUTPUT', 'FRAME'] | ||||
| if node.type in end_types: | if node.type in end_types: | ||||
| return False | return False | ||||
| for output in node.outputs: | for output in node.outputs: | ||||
| if output.links: | if output.links: | ||||
| return False | return False | ||||
| return True | return True | ||||
| ▲ Show 20 Lines • Show All 207 Lines • ▼ Show 20 Lines | def poll(cls, context): | ||||
| valid = False | valid = False | ||||
| if nw_check(context): | if nw_check(context): | ||||
| snode = context.space_data | snode = context.space_data | ||||
| valid = snode.tree_type == 'CompositorNodeTree' | valid = snode.tree_type == 'CompositorNodeTree' | ||||
| return valid | return valid | ||||
| def execute(self, context): | def execute(self, context): | ||||
| context.space_data.backdrop_zoom = 1 | context.space_data.backdrop_zoom = 1 | ||||
| context.space_data.backdrop_x = 0 | context.space_data.backdrop_offset[0] = 0 | ||||
| context.space_data.backdrop_y = 0 | context.space_data.backdrop_offset[1] = 0 | ||||
| return {'FINISHED'} | return {'FINISHED'} | ||||
| class NWAddAttrNode(Operator, NWBase): | class NWAddAttrNode(Operator, NWBase): | ||||
| """Add an Attribute node with this name""" | """Add an Attribute node with this name""" | ||||
| bl_idname = 'node.nw_add_attr_node' | bl_idname = 'node.nw_add_attr_node' | ||||
| bl_label = 'Add UV map' | bl_label = 'Add UV map' | ||||
| attr_name = StringProperty() | |||||
| bl_options = {'REGISTER', 'UNDO'} | bl_options = {'REGISTER', 'UNDO'} | ||||
| attr_name: StringProperty() | |||||
| def execute(self, context): | def execute(self, context): | ||||
| bpy.ops.node.add_node('INVOKE_DEFAULT', use_transform=True, type="ShaderNodeAttribute") | bpy.ops.node.add_node('INVOKE_DEFAULT', use_transform=True, type="ShaderNodeAttribute") | ||||
| nodes, links = get_nodes_links(context) | nodes, links = get_nodes_links(context) | ||||
| nodes.active.attribute_name = self.attr_name | nodes.active.attribute_name = self.attr_name | ||||
| return {'FINISHED'} | return {'FINISHED'} | ||||
| class NWEmissionViewer(Operator, NWBase): | class NWEmissionViewer(Operator, NWBase): | ||||
| bl_idname = "node.nw_emission_viewer" | bl_idname = "node.nw_emission_viewer" | ||||
| bl_label = "Emission Viewer" | bl_label = "Emission Viewer" | ||||
| bl_description = "Connect active node to Emission Shader for shadeless previews" | bl_description = "Connect active node to Emission Shader for shadeless previews" | ||||
| bl_options = {'REGISTER', 'UNDO'} | bl_options = {'REGISTER', 'UNDO'} | ||||
| @classmethod | @classmethod | ||||
| def poll(cls, context): | def poll(cls, context): | ||||
| is_cycles = context.scene.render.engine == 'CYCLES' | is_cycles = is_cycles_or_eevee(context) | ||||
| if nw_check(context): | if nw_check(context): | ||||
| space = context.space_data | space = context.space_data | ||||
| if space.tree_type == 'ShaderNodeTree' and is_cycles: | if space.tree_type == 'ShaderNodeTree' and is_cycles: | ||||
| if context.active_node: | if context.active_node: | ||||
| if context.active_node.type != "OUTPUT_MATERIAL" or context.active_node.type != "OUTPUT_WORLD": | if context.active_node.type != "OUTPUT_MATERIAL" or context.active_node.type != "OUTPUT_WORLD": | ||||
| return True | return True | ||||
| else: | else: | ||||
| return True | return True | ||||
| return False | return False | ||||
| def invoke(self, context, event): | def invoke(self, context, event): | ||||
| space = context.space_data | space = context.space_data | ||||
| shader_type = space.shader_type | shader_type = space.shader_type | ||||
| if shader_type == 'OBJECT': | if shader_type == 'OBJECT': | ||||
| if space.id not in [lamp for lamp in bpy.data.lamps]: # cannot use bpy.data.lamps directly as iterable | if space.id not in [light for light in bpy.data.lights]: # cannot use bpy.data.lights directly as iterable | ||||
| shader_output_type = "OUTPUT_MATERIAL" | shader_output_type = "OUTPUT_MATERIAL" | ||||
| shader_output_ident = "ShaderNodeOutputMaterial" | shader_output_ident = "ShaderNodeOutputMaterial" | ||||
| shader_viewer_ident = "ShaderNodeEmission" | shader_viewer_ident = "ShaderNodeEmission" | ||||
| else: | else: | ||||
| shader_output_type = "OUTPUT_LAMP" | shader_output_type = "OUTPUT_LIGHT" | ||||
| shader_output_ident = "ShaderNodeOutputLamp" | shader_output_ident = "ShaderNodeOutputLight" | ||||
| shader_viewer_ident = "ShaderNodeEmission" | shader_viewer_ident = "ShaderNodeEmission" | ||||
| elif shader_type == 'WORLD': | elif shader_type == 'WORLD': | ||||
| shader_output_type = "OUTPUT_WORLD" | shader_output_type = "OUTPUT_WORLD" | ||||
| shader_output_ident = "ShaderNodeOutputWorld" | shader_output_ident = "ShaderNodeOutputWorld" | ||||
| shader_viewer_ident = "ShaderNodeBackground" | shader_viewer_ident = "ShaderNodeBackground" | ||||
| shader_types = [x[1] for x in shaders_shader_nodes_props] | shader_types = [x[1] for x in shaders_shader_nodes_props] | ||||
| mlocx = event.mouse_region_x | mlocx = event.mouse_region_x | ||||
| ▲ Show 20 Lines • Show All 111 Lines • ▼ Show 20 Lines | def invoke(self, context, event): | ||||
| return {'CANCELLED'} | return {'CANCELLED'} | ||||
| class NWFrameSelected(Operator, NWBase): | class NWFrameSelected(Operator, NWBase): | ||||
| bl_idname = "node.nw_frame_selected" | bl_idname = "node.nw_frame_selected" | ||||
| bl_label = "Frame Selected" | bl_label = "Frame Selected" | ||||
| bl_description = "Add a frame node and parent the selected nodes to it" | bl_description = "Add a frame node and parent the selected nodes to it" | ||||
| bl_options = {'REGISTER', 'UNDO'} | bl_options = {'REGISTER', 'UNDO'} | ||||
| label_prop = StringProperty(name='Label', default=' ', description='The visual name of the frame node') | |||||
| color_prop = FloatVectorProperty(name="Color", description="The color of the frame node", default=(0.6, 0.6, 0.6), | label_prop: StringProperty( | ||||
| min=0, max=1, step=1, precision=3, subtype='COLOR_GAMMA', size=3) | name='Label', | ||||
| description='The visual name of the frame node', | |||||
| default=' ' | |||||
| ) | |||||
| color_prop: FloatVectorProperty( | |||||
| name="Color", | |||||
| description="The color of the frame node", | |||||
| default=(0.6, 0.6, 0.6), | |||||
| min=0, max=1, step=1, precision=3, | |||||
| subtype='COLOR_GAMMA', size=3 | |||||
| ) | |||||
| def execute(self, context): | def execute(self, context): | ||||
| nodes, links = get_nodes_links(context) | nodes, links = get_nodes_links(context) | ||||
| selected = [] | selected = [] | ||||
| for node in nodes: | for node in nodes: | ||||
| if node.select == True: | if node.select == True: | ||||
| selected.append(node) | selected.append(node) | ||||
| ▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | |||||
| class NWSwitchNodeType(Operator, NWBase): | class NWSwitchNodeType(Operator, NWBase): | ||||
| """Switch type of selected nodes """ | """Switch type of selected nodes """ | ||||
| bl_idname = "node.nw_swtch_node_type" | bl_idname = "node.nw_swtch_node_type" | ||||
| bl_label = "Switch Node Type" | bl_label = "Switch Node Type" | ||||
| bl_options = {'REGISTER', 'UNDO'} | bl_options = {'REGISTER', 'UNDO'} | ||||
| to_type = EnumProperty( | to_type: EnumProperty( | ||||
| name="Switch to type", | name="Switch to type", | ||||
| items=list(shaders_input_nodes_props) + | items=list(shaders_input_nodes_props) + | ||||
| list(shaders_output_nodes_props) + | list(shaders_output_nodes_props) + | ||||
| list(shaders_shader_nodes_props) + | list(shaders_shader_nodes_props) + | ||||
| list(shaders_texture_nodes_props) + | list(shaders_texture_nodes_props) + | ||||
| list(shaders_color_nodes_props) + | list(shaders_color_nodes_props) + | ||||
| list(shaders_vector_nodes_props) + | list(shaders_vector_nodes_props) + | ||||
| list(shaders_converter_nodes_props) + | list(shaders_converter_nodes_props) + | ||||
| ▲ Show 20 Lines • Show All 197 Lines • ▼ Show 20 Lines | |||||
| class NWMergeNodes(Operator, NWBase): | class NWMergeNodes(Operator, NWBase): | ||||
| bl_idname = "node.nw_merge_nodes" | bl_idname = "node.nw_merge_nodes" | ||||
| bl_label = "Merge Nodes" | bl_label = "Merge Nodes" | ||||
| bl_description = "Merge Selected Nodes" | bl_description = "Merge Selected Nodes" | ||||
| bl_options = {'REGISTER', 'UNDO'} | bl_options = {'REGISTER', 'UNDO'} | ||||
| mode = EnumProperty( | mode: EnumProperty( | ||||
| name="mode", | name="mode", | ||||
| description="All possible blend types and math operations", | description="All possible blend types and math operations", | ||||
| items=blend_types + [op for op in operations if op not in blend_types], | items=blend_types + [op for op in operations if op not in blend_types], | ||||
| ) | ) | ||||
| merge_type = EnumProperty( | merge_type: EnumProperty( | ||||
| name="merge type", | name="merge type", | ||||
| description="Type of Merge to be used", | description="Type of Merge to be used", | ||||
| items=( | items=( | ||||
| ('AUTO', 'Auto', 'Automatic Output Type Detection'), | ('AUTO', 'Auto', 'Automatic Output Type Detection'), | ||||
| ('SHADER', 'Shader', 'Merge using ADD or MIX Shader'), | ('SHADER', 'Shader', 'Merge using ADD or MIX Shader'), | ||||
| ('MIX', 'Mix Node', 'Merge using Mix Nodes'), | ('MIX', 'Mix Node', 'Merge using Mix Nodes'), | ||||
| ('MATH', 'Math Node', 'Merge using Math Nodes'), | ('MATH', 'Math Node', 'Merge using Math Nodes'), | ||||
| ('ZCOMBINE', 'Z-Combine Node', 'Merge using Z-Combine Nodes'), | ('ZCOMBINE', 'Z-Combine Node', 'Merge using Z-Combine Nodes'), | ||||
| ▲ Show 20 Lines • Show All 229 Lines • ▼ Show 20 Lines | |||||
| class NWBatchChangeNodes(Operator, NWBase): | class NWBatchChangeNodes(Operator, NWBase): | ||||
| bl_idname = "node.nw_batch_change" | bl_idname = "node.nw_batch_change" | ||||
| bl_label = "Batch Change" | bl_label = "Batch Change" | ||||
| bl_description = "Batch Change Blend Type and Math Operation" | bl_description = "Batch Change Blend Type and Math Operation" | ||||
| bl_options = {'REGISTER', 'UNDO'} | bl_options = {'REGISTER', 'UNDO'} | ||||
| blend_type = EnumProperty( | blend_type: EnumProperty( | ||||
| name="Blend Type", | name="Blend Type", | ||||
| items=blend_types + navs, | items=blend_types + navs, | ||||
| ) | ) | ||||
| operation = EnumProperty( | operation: EnumProperty( | ||||
| name="Operation", | name="Operation", | ||||
| items=operations + navs, | items=operations + navs, | ||||
| ) | ) | ||||
| def execute(self, context): | def execute(self, context): | ||||
| nodes, links = get_nodes_links(context) | nodes, links = get_nodes_links(context) | ||||
| blend_type = self.blend_type | blend_type = self.blend_type | ||||
| ▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | class NWChangeMixFactor(Operator, NWBase): | ||||
| bl_idname = "node.nw_factor" | bl_idname = "node.nw_factor" | ||||
| bl_label = "Change Factor" | bl_label = "Change Factor" | ||||
| bl_description = "Change Factors of Mix Nodes and Mix Shader Nodes" | bl_description = "Change Factors of Mix Nodes and Mix Shader Nodes" | ||||
| bl_options = {'REGISTER', 'UNDO'} | bl_options = {'REGISTER', 'UNDO'} | ||||
| # option: Change factor. | # option: Change factor. | ||||
| # If option is 1.0 or 0.0 - set to 1.0 or 0.0 | # If option is 1.0 or 0.0 - set to 1.0 or 0.0 | ||||
| # Else - change factor by option value. | # Else - change factor by option value. | ||||
| option = FloatProperty() | option: FloatProperty() | ||||
| def execute(self, context): | def execute(self, context): | ||||
| nodes, links = get_nodes_links(context) | nodes, links = get_nodes_links(context) | ||||
| option = self.option | option = self.option | ||||
| selected = [] # entry = index | selected = [] # entry = index | ||||
| for si, node in enumerate(nodes): | for si, node in enumerate(nodes): | ||||
| if node.select: | if node.select: | ||||
| if node.type in {'MIX_RGB', 'MIX_SHADER'}: | if node.type in {'MIX_RGB', 'MIX_SHADER'}: | ||||
| ▲ Show 20 Lines • Show All 119 Lines • ▼ Show 20 Lines | def execute(self, context): | ||||
| return {'FINISHED'} | return {'FINISHED'} | ||||
| class NWCopyLabel(Operator, NWBase): | class NWCopyLabel(Operator, NWBase): | ||||
| bl_idname = "node.nw_copy_label" | bl_idname = "node.nw_copy_label" | ||||
| bl_label = "Copy Label" | bl_label = "Copy Label" | ||||
| bl_options = {'REGISTER', 'UNDO'} | bl_options = {'REGISTER', 'UNDO'} | ||||
| option = EnumProperty( | option: EnumProperty( | ||||
| name="option", | name="option", | ||||
| description="Source of name of label", | description="Source of name of label", | ||||
| items=( | items=( | ||||
| ('FROM_ACTIVE', 'from active', 'from active node',), | ('FROM_ACTIVE', 'from active', 'from active node',), | ||||
| ('FROM_NODE', 'from node', 'from node linked to selected node'), | ('FROM_NODE', 'from node', 'from node linked to selected node'), | ||||
| ('FROM_SOCKET', 'from socket', 'from socket linked to selected node'), | ('FROM_SOCKET', 'from socket', 'from socket linked to selected node'), | ||||
| ) | ) | ||||
| ) | ) | ||||
| Show All 27 Lines | def execute(self, context): | ||||
| return {'FINISHED'} | return {'FINISHED'} | ||||
| class NWClearLabel(Operator, NWBase): | class NWClearLabel(Operator, NWBase): | ||||
| bl_idname = "node.nw_clear_label" | bl_idname = "node.nw_clear_label" | ||||
| bl_label = "Clear Label" | bl_label = "Clear Label" | ||||
| bl_options = {'REGISTER', 'UNDO'} | bl_options = {'REGISTER', 'UNDO'} | ||||
| option = BoolProperty() | option: BoolProperty() | ||||
| def execute(self, context): | def execute(self, context): | ||||
| nodes, links = get_nodes_links(context) | nodes, links = get_nodes_links(context) | ||||
| for node in [n for n in nodes if n.select]: | for node in [n for n in nodes if n.select]: | ||||
| node.label = '' | node.label = '' | ||||
| return {'FINISHED'} | return {'FINISHED'} | ||||
| def invoke(self, context, event): | def invoke(self, context, event): | ||||
| if self.option: | if self.option: | ||||
| return self.execute(context) | return self.execute(context) | ||||
| else: | else: | ||||
| return context.window_manager.invoke_confirm(self, event) | return context.window_manager.invoke_confirm(self, event) | ||||
| class NWModifyLabels(Operator, NWBase): | class NWModifyLabels(Operator, NWBase): | ||||
| """Modify Labels of all selected nodes""" | """Modify Labels of all selected nodes""" | ||||
| bl_idname = "node.nw_modify_labels" | bl_idname = "node.nw_modify_labels" | ||||
| bl_label = "Modify Labels" | bl_label = "Modify Labels" | ||||
| bl_options = {'REGISTER', 'UNDO'} | bl_options = {'REGISTER', 'UNDO'} | ||||
| prepend = StringProperty( | prepend: StringProperty( | ||||
| name="Add to Beginning" | name="Add to Beginning" | ||||
| ) | ) | ||||
| append = StringProperty( | append: StringProperty( | ||||
| name="Add to End" | name="Add to End" | ||||
| ) | ) | ||||
| replace_from = StringProperty( | replace_from: StringProperty( | ||||
| name="Text to Replace" | name="Text to Replace" | ||||
| ) | ) | ||||
| replace_to = StringProperty( | replace_to: StringProperty( | ||||
| name="Replace with" | name="Replace with" | ||||
| ) | ) | ||||
| def execute(self, context): | def execute(self, context): | ||||
| nodes, links = get_nodes_links(context) | nodes, links = get_nodes_links(context) | ||||
| for node in [n for n in nodes if n.select]: | for node in [n for n in nodes if n.select]: | ||||
| node.label = self.prepend + node.label.replace(self.replace_from, self.replace_to) + self.append | node.label = self.prepend + node.label.replace(self.replace_from, self.replace_to) + self.append | ||||
| return {'FINISHED'} | return {'FINISHED'} | ||||
| def invoke(self, context, event): | def invoke(self, context, event): | ||||
| self.prepend = "" | self.prepend = "" | ||||
| self.append = "" | self.append = "" | ||||
| self.remove = "" | self.remove = "" | ||||
| return context.window_manager.invoke_props_dialog(self) | return context.window_manager.invoke_props_dialog(self) | ||||
| class NWAddTextureSetup(Operator, NWBase): | class NWAddTextureSetup(Operator, NWBase): | ||||
| bl_idname = "node.nw_add_texture" | bl_idname = "node.nw_add_texture" | ||||
| bl_label = "Texture Setup" | bl_label = "Texture Setup" | ||||
| bl_description = "Add Texture Node Setup to Selected Shaders" | bl_description = "Add Texture Node Setup to Selected Shaders" | ||||
| bl_options = {'REGISTER', 'UNDO'} | 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 | @classmethod | ||||
| def poll(cls, context): | def poll(cls, context): | ||||
| valid = False | valid = False | ||||
| if nw_check(context): | if nw_check(context): | ||||
| space = context.space_data | space = context.space_data | ||||
| if space.tree_type == 'ShaderNodeTree' and context.scene.render.engine == 'CYCLES': | if space.tree_type == 'ShaderNodeTree' and is_cycles_or_eevee(context): | ||||
| valid = True | valid = True | ||||
| return valid | return valid | ||||
| def execute(self, context): | def execute(self, context): | ||||
| nodes, links = get_nodes_links(context) | nodes, links = get_nodes_links(context) | ||||
| shader_types = [x[1] for x in shaders_shader_nodes_props if x[1] not in {'MIX_SHADER', 'ADD_SHADER'}] | shader_types = [x[1] for x in shaders_shader_nodes_props if x[1] not in {'MIX_SHADER', 'ADD_SHADER'}] | ||||
| texture_types = [x[1] for x in shaders_texture_nodes_props] | texture_types = [x[1] for x in shaders_texture_nodes_props] | ||||
| selected_nodes = [n for n in nodes if n.select] | selected_nodes = [n for n in nodes if n.select] | ||||
| ▲ Show 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | |||||
| class NWAddPrincipledSetup(Operator, NWBase, ImportHelper): | class NWAddPrincipledSetup(Operator, NWBase, ImportHelper): | ||||
| bl_idname = "node.nw_add_textures_for_principled" | bl_idname = "node.nw_add_textures_for_principled" | ||||
| bl_label = "Principled Texture Setup" | bl_label = "Principled Texture Setup" | ||||
| bl_description = "Add Texture Node Setup for Principled BSDF" | bl_description = "Add Texture Node Setup for Principled BSDF" | ||||
| bl_options = {'REGISTER', 'UNDO'} | bl_options = {'REGISTER', 'UNDO'} | ||||
| directory = StringProperty( | directory: StringProperty( | ||||
| name='Directory', | name='Directory', | ||||
| subtype='DIR_PATH', | subtype='DIR_PATH', | ||||
| default='', | default='', | ||||
| description='Folder to search in for image files') | description='Folder to search in for image files' | ||||
| files = CollectionProperty( | ) | ||||
| files: CollectionProperty( | |||||
| type=bpy.types.OperatorFileListElement, | type=bpy.types.OperatorFileListElement, | ||||
| options={'HIDDEN', 'SKIP_SAVE'}) | options={'HIDDEN', 'SKIP_SAVE'} | ||||
| ) | |||||
| order = [ | order = [ | ||||
| "filepath", | "filepath", | ||||
| "files", | "files", | ||||
| ] | ] | ||||
| @classmethod | @classmethod | ||||
| def poll(cls, context): | def poll(cls, context): | ||||
| valid = False | valid = False | ||||
| if nw_check(context): | if nw_check(context): | ||||
| space = context.space_data | space = context.space_data | ||||
| if space.tree_type == 'ShaderNodeTree' and context.scene.render.engine == 'CYCLES': | if space.tree_type == 'ShaderNodeTree' and is_cycles_or_eevee(context): | ||||
| valid = True | valid = True | ||||
| return valid | return valid | ||||
| def execute(self, context): | def execute(self, context): | ||||
| # Check if everything is ok | # Check if everything is ok | ||||
| if not self.directory: | if not self.directory: | ||||
| self.report({'INFO'}, 'No Folder Selected') | self.report({'INFO'}, 'No Folder Selected') | ||||
| return {'CANCELLED'} | return {'CANCELLED'} | ||||
| ▲ Show 20 Lines • Show All 217 Lines • ▼ Show 20 Lines | |||||
| class NWAddReroutes(Operator, NWBase): | class NWAddReroutes(Operator, NWBase): | ||||
| """Add Reroute Nodes and link them to outputs of selected nodes""" | """Add Reroute Nodes and link them to outputs of selected nodes""" | ||||
| bl_idname = "node.nw_add_reroutes" | bl_idname = "node.nw_add_reroutes" | ||||
| bl_label = "Add Reroutes" | bl_label = "Add Reroutes" | ||||
| bl_description = "Add Reroutes to Outputs" | bl_description = "Add Reroutes to Outputs" | ||||
| bl_options = {'REGISTER', 'UNDO'} | bl_options = {'REGISTER', 'UNDO'} | ||||
| option = EnumProperty( | option: EnumProperty( | ||||
| name="option", | name="option", | ||||
| items=[ | items=[ | ||||
| ('ALL', 'to all', 'Add to all outputs'), | ('ALL', 'to all', 'Add to all outputs'), | ||||
| ('LOOSE', 'to loose', 'Add only to loose outputs'), | ('LOOSE', 'to loose', 'Add only to loose outputs'), | ||||
| ('LINKED', 'to linked', 'Add only to linked outputs'), | ('LINKED', 'to linked', 'Add only to linked outputs'), | ||||
| ] | ] | ||||
| ) | ) | ||||
| ▲ Show 20 Lines • Show All 83 Lines • ▼ Show 20 Lines | |||||
| class NWLinkActiveToSelected(Operator, NWBase): | class NWLinkActiveToSelected(Operator, NWBase): | ||||
| """Link active node to selected nodes basing on various criteria""" | """Link active node to selected nodes basing on various criteria""" | ||||
| bl_idname = "node.nw_link_active_to_selected" | bl_idname = "node.nw_link_active_to_selected" | ||||
| bl_label = "Link Active Node to Selected" | bl_label = "Link Active Node to Selected" | ||||
| bl_options = {'REGISTER', 'UNDO'} | bl_options = {'REGISTER', 'UNDO'} | ||||
| replace = BoolProperty() | replace: BoolProperty() | ||||
| use_node_name = BoolProperty() | use_node_name: BoolProperty() | ||||
| use_outputs_names = BoolProperty() | use_outputs_names: BoolProperty() | ||||
| @classmethod | @classmethod | ||||
| def poll(cls, context): | def poll(cls, context): | ||||
| valid = False | valid = False | ||||
| if nw_check(context): | if nw_check(context): | ||||
| if context.active_node is not None: | if context.active_node is not None: | ||||
| if context.active_node.select: | if context.active_node.select: | ||||
| valid = True | valid = True | ||||
| ▲ Show 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | def execute(self, context): | ||||
| return {'FINISHED'} | return {'FINISHED'} | ||||
| class NWAlignNodes(Operator, NWBase): | class NWAlignNodes(Operator, NWBase): | ||||
| '''Align the selected nodes neatly in a row/column''' | '''Align the selected nodes neatly in a row/column''' | ||||
| bl_idname = "node.nw_align_nodes" | bl_idname = "node.nw_align_nodes" | ||||
| bl_label = "Align Nodes" | bl_label = "Align Nodes" | ||||
| bl_options = {'REGISTER', 'UNDO'} | bl_options = {'REGISTER', 'UNDO'} | ||||
| margin = IntProperty(name='Margin', default=50, description='The amount of space between nodes') | margin: IntProperty(name='Margin', default=50, description='The amount of space between nodes') | ||||
| def execute(self, context): | def execute(self, context): | ||||
| nodes, links = get_nodes_links(context) | nodes, links = get_nodes_links(context) | ||||
| margin = self.margin | margin = self.margin | ||||
| selection = [] | selection = [] | ||||
| for node in nodes: | for node in nodes: | ||||
| if node.select and node.type != 'FRAME': | if node.select and node.type != 'FRAME': | ||||
| ▲ Show 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | def execute(self, context): | ||||
| return {'FINISHED'} | return {'FINISHED'} | ||||
| class NWSelectParentChildren(Operator, NWBase): | class NWSelectParentChildren(Operator, NWBase): | ||||
| bl_idname = "node.nw_select_parent_child" | bl_idname = "node.nw_select_parent_child" | ||||
| bl_label = "Select Parent or Children" | bl_label = "Select Parent or Children" | ||||
| bl_options = {'REGISTER', 'UNDO'} | bl_options = {'REGISTER', 'UNDO'} | ||||
| option = EnumProperty( | option: EnumProperty( | ||||
| name="option", | name="option", | ||||
| items=( | items=( | ||||
| ('PARENT', 'Select Parent', 'Select Parent Frame'), | ('PARENT', 'Select Parent', 'Select Parent Frame'), | ||||
| ('CHILD', 'Select Children', 'Select members of selected frame'), | ('CHILD', 'Select Children', 'Select members of selected frame'), | ||||
| ) | ) | ||||
| ) | ) | ||||
| def execute(self, context): | def execute(self, context): | ||||
| ▲ Show 20 Lines • Show All 66 Lines • ▼ Show 20 Lines | def execute(self, context): | ||||
| output_types = output_types_shaders + output_types_compo + output_types_blender_mat | output_types = output_types_shaders + output_types_compo + output_types_blender_mat | ||||
| for node in nodes: | for node in nodes: | ||||
| if node.type in output_types: | if node.type in output_types: | ||||
| output_node = node | output_node = node | ||||
| break | break | ||||
| if not output_node: | if not output_node: | ||||
| bpy.ops.node.select_all(action="DESELECT") | bpy.ops.node.select_all(action="DESELECT") | ||||
| if tree_type == 'ShaderNodeTree': | if tree_type == 'ShaderNodeTree': | ||||
| if context.scene.render.engine == 'CYCLES': | if is_cycles_or_eevee(context): | ||||
| output_node = nodes.new('ShaderNodeOutputMaterial') | output_node = nodes.new('ShaderNodeOutputMaterial') | ||||
| else: | else: | ||||
| output_node = nodes.new('ShaderNodeOutput') | output_node = nodes.new('ShaderNodeOutput') | ||||
| elif tree_type == 'CompositorNodeTree': | elif tree_type == 'CompositorNodeTree': | ||||
| output_node = nodes.new('CompositorNodeComposite') | output_node = nodes.new('CompositorNodeComposite') | ||||
| elif tree_type == 'TextureNodeTree': | elif tree_type == 'TextureNodeTree': | ||||
| output_node = nodes.new('TextureNodeOutput') | output_node = nodes.new('TextureNodeOutput') | ||||
| output_node.location.x = active.location.x + active.dimensions.x + 80 | output_node.location.x = active.location.x + active.dimensions.x + 80 | ||||
| output_node.location.y = active.location.y | output_node.location.y = active.location.y | ||||
| if (output_node and active.outputs): | if (output_node and active.outputs): | ||||
| for i, output in enumerate(active.outputs): | for i, output in enumerate(active.outputs): | ||||
| if not output.hide: | if not output.hide: | ||||
| output_index = i | output_index = i | ||||
| break | break | ||||
| for i, output in enumerate(active.outputs): | for i, output in enumerate(active.outputs): | ||||
| if output.type == output_node.inputs[0].type and not output.hide: | if output.type == output_node.inputs[0].type and not output.hide: | ||||
| output_index = i | output_index = i | ||||
| break | break | ||||
| out_input_index = 0 | out_input_index = 0 | ||||
| if tree_type == 'ShaderNodeTree' and context.scene.render.engine == 'CYCLES': | if tree_type == 'ShaderNodeTree' and is_cycles_or_eevee(context): | ||||
| if active.outputs[output_index].name == 'Volume': | if active.outputs[output_index].name == 'Volume': | ||||
| out_input_index = 1 | out_input_index = 1 | ||||
| elif active.outputs[output_index].type != 'SHADER': # connect to displacement if not a shader | elif active.outputs[output_index].type != 'SHADER': # connect to displacement if not a shader | ||||
| out_input_index = 2 | out_input_index = 2 | ||||
| links.new(active.outputs[output_index], output_node.inputs[out_input_index]) | links.new(active.outputs[output_index], output_node.inputs[out_input_index]) | ||||
| force_update(context) # viewport render does not update | force_update(context) # viewport render does not update | ||||
| return {'FINISHED'} | return {'FINISHED'} | ||||
| class NWMakeLink(Operator, NWBase): | class NWMakeLink(Operator, NWBase): | ||||
| """Make a link from one socket to another""" | """Make a link from one socket to another""" | ||||
| bl_idname = 'node.nw_make_link' | bl_idname = 'node.nw_make_link' | ||||
| bl_label = 'Make Link' | bl_label = 'Make Link' | ||||
| bl_options = {'REGISTER', 'UNDO'} | bl_options = {'REGISTER', 'UNDO'} | ||||
| from_socket = IntProperty() | from_socket: IntProperty() | ||||
| to_socket = IntProperty() | to_socket: IntProperty() | ||||
| def execute(self, context): | def execute(self, context): | ||||
| nodes, links = get_nodes_links(context) | nodes, links = get_nodes_links(context) | ||||
| n1 = nodes[context.scene.NWLazySource] | n1 = nodes[context.scene.NWLazySource] | ||||
| n2 = nodes[context.scene.NWLazyTarget] | n2 = nodes[context.scene.NWLazyTarget] | ||||
| links.new(n1.outputs[self.from_socket], n2.inputs[self.to_socket]) | links.new(n1.outputs[self.from_socket], n2.inputs[self.to_socket]) | ||||
| force_update(context) | force_update(context) | ||||
| return {'FINISHED'} | return {'FINISHED'} | ||||
| class NWCallInputsMenu(Operator, NWBase): | class NWCallInputsMenu(Operator, NWBase): | ||||
| """Link from this output""" | """Link from this output""" | ||||
| bl_idname = 'node.nw_call_inputs_menu' | bl_idname = 'node.nw_call_inputs_menu' | ||||
| bl_label = 'Make Link' | bl_label = 'Make Link' | ||||
| bl_options = {'REGISTER', 'UNDO'} | bl_options = {'REGISTER', 'UNDO'} | ||||
| from_socket = IntProperty() | from_socket: IntProperty() | ||||
| def execute(self, context): | def execute(self, context): | ||||
| nodes, links = get_nodes_links(context) | nodes, links = get_nodes_links(context) | ||||
| context.scene.NWSourceSocket = self.from_socket | context.scene.NWSourceSocket = self.from_socket | ||||
| n1 = nodes[context.scene.NWLazySource] | n1 = nodes[context.scene.NWLazySource] | ||||
| n2 = nodes[context.scene.NWLazyTarget] | n2 = nodes[context.scene.NWLazyTarget] | ||||
| if len(n2.inputs) > 1: | if len(n2.inputs) > 1: | ||||
| bpy.ops.wm.call_menu("INVOKE_DEFAULT", name=NWConnectionListInputs.bl_idname) | bpy.ops.wm.call_menu("INVOKE_DEFAULT", name=NWConnectionListInputs.bl_idname) | ||||
| elif len(n2.inputs) == 1: | elif len(n2.inputs) == 1: | ||||
| links.new(n1.outputs[self.from_socket], n2.inputs[0]) | links.new(n1.outputs[self.from_socket], n2.inputs[0]) | ||||
| return {'FINISHED'} | return {'FINISHED'} | ||||
| class NWAddSequence(Operator, ImportHelper): | class NWAddSequence(Operator, ImportHelper): | ||||
| """Add an Image Sequence""" | """Add an Image Sequence""" | ||||
| bl_idname = 'node.nw_add_sequence' | bl_idname = 'node.nw_add_sequence' | ||||
| bl_label = 'Import Image Sequence' | bl_label = 'Import Image Sequence' | ||||
| bl_options = {'REGISTER', 'UNDO'} | bl_options = {'REGISTER', 'UNDO'} | ||||
| directory = StringProperty(subtype="DIR_PATH") | |||||
| filename = StringProperty(subtype="FILE_NAME") | directory: StringProperty( | ||||
| files = CollectionProperty(type=bpy.types.OperatorFileListElement, options={'HIDDEN', 'SKIP_SAVE'}) | subtype="DIR_PATH" | ||||
| ) | |||||
| filename: StringProperty( | |||||
| subtype="FILE_NAME" | |||||
| ) | |||||
| files: CollectionProperty( | |||||
| type=bpy.types.OperatorFileListElement, | |||||
| options={'HIDDEN', 'SKIP_SAVE'} | |||||
| ) | |||||
| def execute(self, context): | def execute(self, context): | ||||
| nodes, links = get_nodes_links(context) | nodes, links = get_nodes_links(context) | ||||
| directory = self.directory | directory = self.directory | ||||
| filename = self.filename | filename = self.filename | ||||
| files = self.files | files = self.files | ||||
| tree = context.space_data.node_tree | tree = context.space_data.node_tree | ||||
| ▲ Show 20 Lines • Show All 75 Lines • ▼ Show 20 Lines | def execute(self, context): | ||||
| return {'FINISHED'} | return {'FINISHED'} | ||||
| class NWAddMultipleImages(Operator, ImportHelper): | class NWAddMultipleImages(Operator, ImportHelper): | ||||
| """Add multiple images at once""" | """Add multiple images at once""" | ||||
| bl_idname = 'node.nw_add_multiple_images' | bl_idname = 'node.nw_add_multiple_images' | ||||
| bl_label = 'Open Selected Images' | bl_label = 'Open Selected Images' | ||||
| bl_options = {'REGISTER', 'UNDO'} | bl_options = {'REGISTER', 'UNDO'} | ||||
| directory = StringProperty(subtype="DIR_PATH") | directory: StringProperty( | ||||
| files = CollectionProperty(type=bpy.types.OperatorFileListElement, options={'HIDDEN', 'SKIP_SAVE'}) | subtype="DIR_PATH" | ||||
| ) | |||||
| files: CollectionProperty( | |||||
| type=bpy.types.OperatorFileListElement, | |||||
| options={'HIDDEN', 'SKIP_SAVE'} | |||||
| ) | |||||
| def execute(self, context): | def execute(self, context): | ||||
| nodes, links = get_nodes_links(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': | if context.space_data.node_tree.type == 'SHADER': | ||||
| node_type = "ShaderNodeTexImage" | node_type = "ShaderNodeTexImage" | ||||
| Show All 30 Lines | def execute(self, context): | ||||
| return {'FINISHED'} | return {'FINISHED'} | ||||
| class NWViewerFocus(bpy.types.Operator): | class NWViewerFocus(bpy.types.Operator): | ||||
| """Set the viewer tile center to the mouse position""" | """Set the viewer tile center to the mouse position""" | ||||
| bl_idname = "node.nw_viewer_focus" | bl_idname = "node.nw_viewer_focus" | ||||
| bl_label = "Viewer Focus" | bl_label = "Viewer Focus" | ||||
| x = bpy.props.IntProperty() | x: bpy.props.IntProperty() | ||||
| y = bpy.props.IntProperty() | y: bpy.props.IntProperty() | ||||
| @classmethod | @classmethod | ||||
| def poll(cls, context): | def poll(cls, context): | ||||
| return nw_check(context) and context.space_data.tree_type == 'CompositorNodeTree' | return nw_check(context) and context.space_data.tree_type == 'CompositorNodeTree' | ||||
| def execute(self, context): | def execute(self, context): | ||||
| return {'FINISHED'} | return {'FINISHED'} | ||||
| Show All 37 Lines | def invoke(self, context, event): | ||||
| return self.execute(context) | return self.execute(context) | ||||
| class NWSaveViewer(bpy.types.Operator, ExportHelper): | class NWSaveViewer(bpy.types.Operator, ExportHelper): | ||||
| """Save the current viewer node to an image file""" | """Save the current viewer node to an image file""" | ||||
| bl_idname = "node.nw_save_viewer" | bl_idname = "node.nw_save_viewer" | ||||
| bl_label = "Save This Image" | bl_label = "Save This Image" | ||||
| filepath = StringProperty(subtype="FILE_PATH") | filepath: StringProperty(subtype="FILE_PATH") | ||||
| filename_ext = EnumProperty( | filename_ext: EnumProperty( | ||||
| name="Format", | name="Format", | ||||
| description="Choose the file format to save to", | description="Choose the file format to save to", | ||||
| items=(('.bmp', "PNG", ""), | items=(('.bmp', "PNG", ""), | ||||
| ('.rgb', 'IRIS', ""), | ('.rgb', 'IRIS', ""), | ||||
| ('.png', 'PNG', ""), | ('.png', 'PNG', ""), | ||||
| ('.jpg', 'JPEG', ""), | ('.jpg', 'JPEG', ""), | ||||
| ('.jp2', 'JPEG2000', ""), | ('.jp2', 'JPEG2000', ""), | ||||
| ('.tga', 'TARGA', ""), | ('.tga', 'TARGA', ""), | ||||
| ▲ Show 20 Lines • Show All 161 Lines • ▼ Show 20 Lines | def drawlayout(context, layout, mode='non-panel'): | ||||
| col = layout.column(align=True) | col = layout.column(align=True) | ||||
| col.menu(NWMergeNodesMenu.bl_idname) | col.menu(NWMergeNodesMenu.bl_idname) | ||||
| col.separator() | col.separator() | ||||
| col = layout.column(align=True) | col = layout.column(align=True) | ||||
| col.menu(NWSwitchNodeTypeMenu.bl_idname, text="Switch Node Type") | col.menu(NWSwitchNodeTypeMenu.bl_idname, text="Switch Node Type") | ||||
| col.separator() | col.separator() | ||||
| if tree_type == 'ShaderNodeTree' and context.scene.render.engine == 'CYCLES': | if tree_type == 'ShaderNodeTree' and is_cycles_or_eevee(context): | ||||
| col = layout.column(align=True) | col = layout.column(align=True) | ||||
| col.operator(NWAddTextureSetup.bl_idname, text="Add Texture Setup", icon='NODE_SEL') | col.operator(NWAddTextureSetup.bl_idname, text="Add Texture Setup", icon='NODE_SEL') | ||||
| col.operator(NWAddPrincipledSetup.bl_idname, text="Add Principled Setup", icon='NODE_SEL') | col.operator(NWAddPrincipledSetup.bl_idname, text="Add Principled Setup", icon='NODE_SEL') | ||||
| col.separator() | col.separator() | ||||
| col = layout.column(align=True) | col = layout.column(align=True) | ||||
| col.operator(NWDetachOutputs.bl_idname, icon='UNLINKED') | col.operator(NWDetachOutputs.bl_idname, icon='UNLINKED') | ||||
| col.operator(NWSwapLinks.bl_idname) | col.operator(NWSwapLinks.bl_idname) | ||||
| Show All 24 Lines | def drawlayout(context, layout, mode='non-panel'): | ||||
| col.operator(NWReloadImages.bl_idname, icon='FILE_REFRESH') | col.operator(NWReloadImages.bl_idname, icon='FILE_REFRESH') | ||||
| col.separator() | col.separator() | ||||
| col = layout.column(align=True) | col = layout.column(align=True) | ||||
| col.operator(NWFrameSelected.bl_idname, icon='STICKY_UVS_LOC') | col.operator(NWFrameSelected.bl_idname, icon='STICKY_UVS_LOC') | ||||
| col.separator() | col.separator() | ||||
| col = layout.column(align=True) | col = layout.column(align=True) | ||||
| col.operator(NWAlignNodes.bl_idname, icon='ALIGN') | col.operator(NWAlignNodes.bl_idname, icon='CENTER_ONLY') | ||||
| col.separator() | col.separator() | ||||
| col = layout.column(align=True) | col = layout.column(align=True) | ||||
| col.operator(NWDeleteUnused.bl_idname, icon='CANCEL') | col.operator(NWDeleteUnused.bl_idname, icon='CANCEL') | ||||
| col.separator() | col.separator() | ||||
| class NodeWranglerPanel(Panel, NWBase): | class NodeWranglerPanel(Panel, NWBase): | ||||
| bl_idname = "NODE_PT_nw_node_wrangler" | bl_idname = "NODE_PT_nw_node_wrangler" | ||||
| bl_space_type = 'NODE_EDITOR' | bl_space_type = 'NODE_EDITOR' | ||||
| bl_label = "Node Wrangler" | bl_label = "Node Wrangler" | ||||
| bl_region_type = "TOOLS" | bl_region_type = "TOOLS" | ||||
| bl_category = "Node Wrangler" | bl_category = "Node Wrangler" | ||||
| prepend = StringProperty( | prepend: StringProperty( | ||||
| name='prepend', | name='prepend', | ||||
| ) | ) | ||||
| append = StringProperty() | append: StringProperty() | ||||
| remove = StringProperty() | remove: StringProperty() | ||||
| def draw(self, context): | def draw(self, context): | ||||
| self.layout.label(text="(Quick access: Ctrl+Space)") | self.layout.label(text="(Quick access: Ctrl+Space)") | ||||
| drawlayout(context, self.layout, mode='panel') | drawlayout(context, self.layout, mode='panel') | ||||
| # | # | ||||
| # M E N U S | # M E N U S | ||||
| # | # | ||||
| class NodeWranglerMenu(Menu, NWBase): | class NodeWranglerMenu(Menu, NWBase): | ||||
| bl_idname = "NODE_MT_nw_node_wrangler_menu" | bl_idname = "NODE_MT_nw_node_wrangler_menu" | ||||
| bl_label = "Node Wrangler" | bl_label = "Node Wrangler" | ||||
| def draw(self, context): | def draw(self, context): | ||||
| drawlayout(context, self.layout) | drawlayout(context, self.layout) | ||||
| class NWMergeNodesMenu(Menu, NWBase): | class NWMergeNodesMenu(Menu, NWBase): | ||||
| bl_idname = "NODE_MT_nw_merge_nodes_menu" | bl_idname = "NODE_MT_nw_merge_nodes_menu" | ||||
| bl_label = "Merge Selected Nodes" | bl_label = "Merge Selected Nodes" | ||||
| def draw(self, context): | def draw(self, context): | ||||
| type = context.space_data.tree_type | type = context.space_data.tree_type | ||||
| layout = self.layout | layout = self.layout | ||||
| if type == 'ShaderNodeTree' and context.scene.render.engine == 'CYCLES': | if type == 'ShaderNodeTree' and is_cycles_or_eevee(context): | ||||
| layout.menu(NWMergeShadersMenu.bl_idname, text="Use Shaders") | layout.menu(NWMergeShadersMenu.bl_idname, text="Use Shaders") | ||||
| layout.menu(NWMergeMixMenu.bl_idname, text="Use Mix Nodes") | layout.menu(NWMergeMixMenu.bl_idname, text="Use Mix Nodes") | ||||
| layout.menu(NWMergeMathMenu.bl_idname, text="Use Math Nodes") | layout.menu(NWMergeMathMenu.bl_idname, text="Use Math Nodes") | ||||
| props = layout.operator(NWMergeNodes.bl_idname, text="Use Z-Combine Nodes") | props = layout.operator(NWMergeNodes.bl_idname, text="Use Z-Combine Nodes") | ||||
| props.mode = 'MIX' | props.mode = 'MIX' | ||||
| props.merge_type = 'ZCOMBINE' | props.merge_type = 'ZCOMBINE' | ||||
| props = layout.operator(NWMergeNodes.bl_idname, text="Use Alpha Over Nodes") | props = layout.operator(NWMergeNodes.bl_idname, text="Use Alpha Over Nodes") | ||||
| props.mode = 'MIX' | props.mode = 'MIX' | ||||
| ▲ Show 20 Lines • Show All 207 Lines • ▼ Show 20 Lines | class NWVertColMenu(bpy.types.Menu): | ||||
| bl_idname = "NODE_MT_nw_node_vertex_color_menu" | bl_idname = "NODE_MT_nw_node_vertex_color_menu" | ||||
| bl_label = "Vertex Colors" | bl_label = "Vertex Colors" | ||||
| @classmethod | @classmethod | ||||
| def poll(cls, context): | def poll(cls, context): | ||||
| valid = False | valid = False | ||||
| if nw_check(context): | if nw_check(context): | ||||
| snode = context.space_data | snode = context.space_data | ||||
| valid = snode.tree_type == 'ShaderNodeTree' and context.scene.render.engine == 'CYCLES' | valid = snode.tree_type == 'ShaderNodeTree' and is_cycles_or_eevee(context) | ||||
| return valid | return valid | ||||
| def draw(self, context): | def draw(self, context): | ||||
| l = self.layout | l = self.layout | ||||
| nodes, links = get_nodes_links(context) | nodes, links = get_nodes_links(context) | ||||
| mat = context.object.active_material | mat = context.object.active_material | ||||
| objs = [] | objs = [] | ||||
| Show All 18 Lines | |||||
| class NWSwitchNodeTypeMenu(Menu, NWBase): | class NWSwitchNodeTypeMenu(Menu, NWBase): | ||||
| bl_idname = "NODE_MT_nw_switch_node_type_menu" | bl_idname = "NODE_MT_nw_switch_node_type_menu" | ||||
| bl_label = "Switch Type to..." | bl_label = "Switch Type to..." | ||||
| def draw(self, context): | def draw(self, context): | ||||
| layout = self.layout | layout = self.layout | ||||
| tree = context.space_data.node_tree | tree = context.space_data.node_tree | ||||
| if tree.type == 'SHADER': | if tree.type == 'SHADER': | ||||
| if context.scene.render.engine == 'CYCLES': | if is_cycles_or_eevee(context): | ||||
| layout.menu(NWSwitchShadersInputSubmenu.bl_idname) | layout.menu(NWSwitchShadersInputSubmenu.bl_idname) | ||||
| layout.menu(NWSwitchShadersOutputSubmenu.bl_idname) | layout.menu(NWSwitchShadersOutputSubmenu.bl_idname) | ||||
| layout.menu(NWSwitchShadersShaderSubmenu.bl_idname) | layout.menu(NWSwitchShadersShaderSubmenu.bl_idname) | ||||
| layout.menu(NWSwitchShadersTextureSubmenu.bl_idname) | layout.menu(NWSwitchShadersTextureSubmenu.bl_idname) | ||||
| layout.menu(NWSwitchShadersColorSubmenu.bl_idname) | layout.menu(NWSwitchShadersColorSubmenu.bl_idname) | ||||
| layout.menu(NWSwitchShadersVectorSubmenu.bl_idname) | layout.menu(NWSwitchShadersVectorSubmenu.bl_idname) | ||||
| layout.menu(NWSwitchShadersConverterSubmenu.bl_idname) | layout.menu(NWSwitchShadersConverterSubmenu.bl_idname) | ||||
| layout.menu(NWSwitchShadersLayoutSubmenu.bl_idname) | layout.menu(NWSwitchShadersLayoutSubmenu.bl_idname) | ||||
| if context.scene.render.engine != 'CYCLES': | else: | ||||
| layout.menu(NWSwitchMatInputSubmenu.bl_idname) | layout.menu(NWSwitchMatInputSubmenu.bl_idname) | ||||
| layout.menu(NWSwitchMatOutputSubmenu.bl_idname) | layout.menu(NWSwitchMatOutputSubmenu.bl_idname) | ||||
| layout.menu(NWSwitchMatColorSubmenu.bl_idname) | layout.menu(NWSwitchMatColorSubmenu.bl_idname) | ||||
| layout.menu(NWSwitchMatVectorSubmenu.bl_idname) | layout.menu(NWSwitchMatVectorSubmenu.bl_idname) | ||||
| layout.menu(NWSwitchMatConverterSubmenu.bl_idname) | layout.menu(NWSwitchMatConverterSubmenu.bl_idname) | ||||
| layout.menu(NWSwitchMatLayoutSubmenu.bl_idname) | layout.menu(NWSwitchMatLayoutSubmenu.bl_idname) | ||||
| if tree.type == 'COMPOSITING': | if tree.type == 'COMPOSITING': | ||||
| layout.menu(NWSwitchCompoInputSubmenu.bl_idname) | layout.menu(NWSwitchCompoInputSubmenu.bl_idname) | ||||
| ▲ Show 20 Lines • Show All 778 Lines • Show Last 20 Lines | |||||