Add-ons: make node wrangler a core add-on #122557
5
scripts/addons_core/node_wrangler/README.md
Normal file
5
scripts/addons_core/node_wrangler/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Running Tests
|
||||
|
||||
```
|
||||
./utils/paths_test.py
|
||||
```
|
62
scripts/addons_core/node_wrangler/__init__.py
Normal file
62
scripts/addons_core/node_wrangler/__init__.py
Normal file
@ -0,0 +1,62 @@
|
||||
# SPDX-FileCopyrightText: 2013-2023 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
bl_info = {
|
||||
"name": "Node Wrangler",
|
||||
"author": "Bartek Skorupa, Greg Zaal, Sebastian Koenig, Christian Brinkmann, Florian Meyer",
|
||||
"version": (3, 54),
|
||||
"blender": (4, 2, 0),
|
||||
"location": "Node Editor Toolbar or Shift-W",
|
||||
"description": "Various tools to enhance and speed up node-based workflow",
|
||||
"warning": "",
|
||||
"doc_url": "{BLENDER_MANUAL_URL}/addons/node/node_wrangler.html",
|
||||
"category": "Node",
|
||||
}
|
||||
|
||||
import bpy
|
||||
from bpy.props import (
|
||||
BoolProperty,
|
||||
IntProperty,
|
||||
StringProperty,
|
||||
)
|
||||
|
||||
from . import operators
|
||||
from . import preferences
|
||||
from . import interface
|
||||
|
||||
|
||||
def register():
|
||||
# props
|
||||
bpy.types.Scene.NWBusyDrawing = StringProperty(
|
||||
name="Busy Drawing!",
|
||||
default="",
|
||||
description="An internal property used to store only the first mouse position")
|
||||
bpy.types.Scene.NWLazySource = StringProperty(
|
||||
name="Lazy Source!",
|
||||
default="x",
|
||||
description="An internal property used to store the first node in a Lazy Connect operation")
|
||||
bpy.types.Scene.NWLazyTarget = StringProperty(
|
||||
name="Lazy Target!",
|
||||
default="x",
|
||||
description="An internal property used to store the last node in a Lazy Connect operation")
|
||||
bpy.types.Scene.NWSourceSocket = IntProperty(
|
||||
name="Source Socket!",
|
||||
default=0,
|
||||
description="An internal property used to store the source socket in a Lazy Connect operation")
|
||||
|
||||
operators.register()
|
||||
interface.register()
|
||||
preferences.register()
|
||||
|
||||
|
||||
def unregister():
|
||||
preferences.unregister()
|
||||
interface.unregister()
|
||||
operators.unregister()
|
||||
|
||||
# props
|
||||
del bpy.types.Scene.NWBusyDrawing
|
||||
del bpy.types.Scene.NWLazySource
|
||||
del bpy.types.Scene.NWLazyTarget
|
||||
del bpy.types.Scene.NWSourceSocket
|
503
scripts/addons_core/node_wrangler/interface.py
Normal file
503
scripts/addons_core/node_wrangler/interface.py
Normal file
@ -0,0 +1,503 @@
|
||||
# SPDX-FileCopyrightText: 2023 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import bpy
|
||||
from bpy.types import Panel, Menu
|
||||
from bpy.props import StringProperty
|
||||
from nodeitems_utils import node_categories_iter, NodeItemCustom
|
||||
|
||||
from . import operators
|
||||
|
||||
from .utils.constants import blend_types, geo_combine_operations, operations
|
||||
from .utils.nodes import get_nodes_links, NWBaseMenu
|
||||
|
||||
|
||||
def drawlayout(context, layout, mode='non-panel'):
|
||||
tree_type = context.space_data.tree_type
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.menu(NWMergeNodesMenu.bl_idname)
|
||||
col.separator()
|
||||
|
||||
if tree_type == 'ShaderNodeTree':
|
||||
col = layout.column(align=True)
|
||||
col.operator(operators.NWAddTextureSetup.bl_idname, text="Add Texture Setup", icon='NODE_SEL')
|
||||
col.operator(operators.NWAddPrincipledSetup.bl_idname, text="Add Principled Setup", icon='NODE_SEL')
|
||||
col.separator()
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.operator(operators.NWDetachOutputs.bl_idname, icon='UNLINKED')
|
||||
col.operator(operators.NWSwapLinks.bl_idname)
|
||||
col.menu(NWAddReroutesMenu.bl_idname, text="Add Reroutes", icon='LAYER_USED')
|
||||
col.separator()
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.menu(NWLinkActiveToSelectedMenu.bl_idname, text="Link Active To Selected", icon='LINKED')
|
||||
if tree_type != 'GeometryNodeTree':
|
||||
col.operator(operators.NWLinkToOutputNode.bl_idname, icon='DRIVER')
|
||||
col.separator()
|
||||
|
||||
col = layout.column(align=True)
|
||||
if mode == 'panel':
|
||||
row = col.row(align=True)
|
||||
row.operator(operators.NWClearLabel.bl_idname).option = True
|
||||
row.operator(operators.NWModifyLabels.bl_idname)
|
||||
else:
|
||||
col.operator(operators.NWClearLabel.bl_idname).option = True
|
||||
col.operator(operators.NWModifyLabels.bl_idname)
|
||||
col.menu(NWBatchChangeNodesMenu.bl_idname, text="Batch Change")
|
||||
col.separator()
|
||||
col.menu(NWCopyToSelectedMenu.bl_idname, text="Copy to Selected")
|
||||
col.separator()
|
||||
|
||||
col = layout.column(align=True)
|
||||
if tree_type == 'CompositorNodeTree':
|
||||
col.operator(operators.NWResetBG.bl_idname, icon='ZOOM_PREVIOUS')
|
||||
if tree_type != 'GeometryNodeTree':
|
||||
col.operator(operators.NWReloadImages.bl_idname, icon='FILE_REFRESH')
|
||||
col.separator()
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.operator(operators.NWFrameSelected.bl_idname, icon='STICKY_UVS_LOC')
|
||||
col.separator()
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.operator(operators.NWAlignNodes.bl_idname, icon='CENTER_ONLY')
|
||||
col.separator()
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.operator(operators.NWDeleteUnused.bl_idname, icon='CANCEL')
|
||||
col.separator()
|
||||
|
||||
|
||||
class NodeWranglerPanel(Panel, NWBaseMenu):
|
||||
bl_idname = "NODE_PT_nw_node_wrangler"
|
||||
bl_space_type = 'NODE_EDITOR'
|
||||
bl_label = "Node Wrangler"
|
||||
bl_region_type = "UI"
|
||||
bl_category = "Node Wrangler"
|
||||
|
||||
prepend: StringProperty(
|
||||
name='prepend',
|
||||
)
|
||||
append: StringProperty()
|
||||
remove: StringProperty()
|
||||
|
||||
def draw(self, context):
|
||||
self.layout.label(text="(Quick access: Shift+W)")
|
||||
drawlayout(context, self.layout, mode='panel')
|
||||
|
||||
|
||||
#
|
||||
# M E N U S
|
||||
#
|
||||
class NodeWranglerMenu(Menu, NWBaseMenu):
|
||||
bl_idname = "NODE_MT_nw_node_wrangler_menu"
|
||||
bl_label = "Node Wrangler"
|
||||
|
||||
def draw(self, context):
|
||||
self.layout.operator_context = 'INVOKE_DEFAULT'
|
||||
drawlayout(context, self.layout)
|
||||
|
||||
|
||||
class NWMergeNodesMenu(Menu, NWBaseMenu):
|
||||
bl_idname = "NODE_MT_nw_merge_nodes_menu"
|
||||
bl_label = "Merge Selected Nodes"
|
||||
|
||||
def draw(self, context):
|
||||
type = context.space_data.tree_type
|
||||
layout = self.layout
|
||||
if type == 'ShaderNodeTree':
|
||||
layout.menu(NWMergeShadersMenu.bl_idname, text="Use Shaders")
|
||||
if type == 'GeometryNodeTree':
|
||||
layout.menu(NWMergeGeometryMenu.bl_idname, text="Use Geometry Nodes")
|
||||
layout.menu(NWMergeMathMenu.bl_idname, text="Use Math Nodes")
|
||||
else:
|
||||
layout.menu(NWMergeMixMenu.bl_idname, text="Use Mix Nodes")
|
||||
layout.menu(NWMergeMathMenu.bl_idname, text="Use Math Nodes")
|
||||
props = layout.operator(operators.NWMergeNodes.bl_idname, text="Use Z-Combine Nodes")
|
||||
props.mode = 'MIX'
|
||||
props.merge_type = 'ZCOMBINE'
|
||||
props = layout.operator(operators.NWMergeNodes.bl_idname, text="Use Alpha Over Nodes")
|
||||
props.mode = 'MIX'
|
||||
props.merge_type = 'ALPHAOVER'
|
||||
|
||||
|
||||
class NWMergeGeometryMenu(Menu, NWBaseMenu):
|
||||
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
|
||||
for type, name, description in geo_combine_operations:
|
||||
props = layout.operator(operators.NWMergeNodes.bl_idname, text=name)
|
||||
props.mode = type
|
||||
props.merge_type = 'GEOMETRY'
|
||||
|
||||
|
||||
class NWMergeShadersMenu(Menu, NWBaseMenu):
|
||||
bl_idname = "NODE_MT_nw_merge_shaders_menu"
|
||||
bl_label = "Merge Selected Nodes using Shaders"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
for type in ('MIX', 'ADD'):
|
||||
name = f'{type.capitalize()} Shader'
|
||||
props = layout.operator(operators.NWMergeNodes.bl_idname, text=name)
|
||||
props.mode = type
|
||||
props.merge_type = 'SHADER'
|
||||
|
||||
|
||||
class NWMergeMixMenu(Menu, NWBaseMenu):
|
||||
bl_idname = "NODE_MT_nw_merge_mix_menu"
|
||||
bl_label = "Merge Selected Nodes using Mix"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
for type, name, description in blend_types:
|
||||
props = layout.operator(operators.NWMergeNodes.bl_idname, text=name)
|
||||
props.mode = type
|
||||
props.merge_type = 'MIX'
|
||||
|
||||
|
||||
class NWConnectionListOutputs(Menu, NWBaseMenu):
|
||||
bl_idname = "NODE_MT_nw_connection_list_out"
|
||||
bl_label = "From:"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
nodes, links = get_nodes_links(context)
|
||||
|
||||
n1 = nodes[context.scene.NWLazySource]
|
||||
for index, output in enumerate(n1.outputs):
|
||||
# Only show sockets that are exposed.
|
||||
if output.enabled:
|
||||
layout.operator(
|
||||
operators.NWCallInputsMenu.bl_idname,
|
||||
text=output.name,
|
||||
icon="RADIOBUT_OFF").from_socket = index
|
||||
|
||||
|
||||
class NWConnectionListInputs(Menu, NWBaseMenu):
|
||||
bl_idname = "NODE_MT_nw_connection_list_in"
|
||||
bl_label = "To:"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
nodes, links = get_nodes_links(context)
|
||||
|
||||
n2 = nodes[context.scene.NWLazyTarget]
|
||||
|
||||
for index, input in enumerate(n2.inputs):
|
||||
# Only show sockets that are exposed.
|
||||
# This prevents, for example, the scale value socket
|
||||
# of the vector math node being added to the list when
|
||||
# the mode is not 'SCALE'.
|
||||
if input.enabled:
|
||||
op = layout.operator(operators.NWMakeLink.bl_idname, text=input.name, icon="FORWARD")
|
||||
op.from_socket = context.scene.NWSourceSocket
|
||||
op.to_socket = index
|
||||
|
||||
|
||||
class NWMergeMathMenu(Menu, NWBaseMenu):
|
||||
bl_idname = "NODE_MT_nw_merge_math_menu"
|
||||
bl_label = "Merge Selected Nodes using Math"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
for type, name, description in operations:
|
||||
props = layout.operator(operators.NWMergeNodes.bl_idname, text=name)
|
||||
props.mode = type
|
||||
props.merge_type = 'MATH'
|
||||
|
||||
|
||||
class NWBatchChangeNodesMenu(Menu, NWBaseMenu):
|
||||
bl_idname = "NODE_MT_nw_batch_change_nodes_menu"
|
||||
bl_label = "Batch Change Selected Nodes"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.menu(NWBatchChangeBlendTypeMenu.bl_idname)
|
||||
layout.menu(NWBatchChangeOperationMenu.bl_idname)
|
||||
|
||||
|
||||
class NWBatchChangeBlendTypeMenu(Menu, NWBaseMenu):
|
||||
bl_idname = "NODE_MT_nw_batch_change_blend_type_menu"
|
||||
bl_label = "Batch Change Blend Type"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
for type, name, description in blend_types:
|
||||
props = layout.operator(operators.NWBatchChangeNodes.bl_idname, text=name)
|
||||
props.blend_type = type
|
||||
props.operation = 'CURRENT'
|
||||
|
||||
|
||||
class NWBatchChangeOperationMenu(Menu, NWBaseMenu):
|
||||
bl_idname = "NODE_MT_nw_batch_change_operation_menu"
|
||||
bl_label = "Batch Change Math Operation"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
for type, name, description in operations:
|
||||
props = layout.operator(operators.NWBatchChangeNodes.bl_idname, text=name)
|
||||
props.blend_type = 'CURRENT'
|
||||
props.operation = type
|
||||
|
||||
|
||||
class NWCopyToSelectedMenu(Menu, NWBaseMenu):
|
||||
bl_idname = "NODE_MT_nw_copy_node_properties_menu"
|
||||
bl_label = "Copy to Selected"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.operator(operators.NWCopySettings.bl_idname, text="Settings from Active")
|
||||
layout.menu(NWCopyLabelMenu.bl_idname)
|
||||
|
||||
|
||||
class NWCopyLabelMenu(Menu, NWBaseMenu):
|
||||
bl_idname = "NODE_MT_nw_copy_label_menu"
|
||||
bl_label = "Copy Label"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.operator(operators.NWCopyLabel.bl_idname, text="from Active Node's Label").option = 'FROM_ACTIVE'
|
||||
layout.operator(operators.NWCopyLabel.bl_idname, text="from Linked Node's Label").option = 'FROM_NODE'
|
||||
layout.operator(operators.NWCopyLabel.bl_idname, text="from Linked Output's Name").option = 'FROM_SOCKET'
|
||||
|
||||
|
||||
class NWAddReroutesMenu(Menu, NWBaseMenu):
|
||||
bl_idname = "NODE_MT_nw_add_reroutes_menu"
|
||||
bl_label = "Add Reroutes"
|
||||
bl_description = "Add Reroute Nodes to Selected Nodes' Outputs"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.operator(operators.NWAddReroutes.bl_idname, text="to All Outputs").option = 'ALL'
|
||||
layout.operator(operators.NWAddReroutes.bl_idname, text="to Loose Outputs").option = 'LOOSE'
|
||||
layout.operator(operators.NWAddReroutes.bl_idname, text="to Linked Outputs").option = 'LINKED'
|
||||
|
||||
|
||||
class NWLinkActiveToSelectedMenu(Menu, NWBaseMenu):
|
||||
bl_idname = "NODE_MT_nw_link_active_to_selected_menu"
|
||||
bl_label = "Link Active to Selected"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.menu(NWLinkStandardMenu.bl_idname)
|
||||
layout.menu(NWLinkUseNodeNameMenu.bl_idname)
|
||||
layout.menu(NWLinkUseOutputsNamesMenu.bl_idname)
|
||||
|
||||
|
||||
class NWLinkStandardMenu(Menu, NWBaseMenu):
|
||||
bl_idname = "NODE_MT_nw_link_standard_menu"
|
||||
bl_label = "To All Selected"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
props = layout.operator(operators.NWLinkActiveToSelected.bl_idname, text="Don't Replace Links")
|
||||
props.replace = False
|
||||
props.use_node_name = False
|
||||
props.use_outputs_names = False
|
||||
props = layout.operator(operators.NWLinkActiveToSelected.bl_idname, text="Replace Links")
|
||||
props.replace = True
|
||||
props.use_node_name = False
|
||||
props.use_outputs_names = False
|
||||
|
||||
|
||||
class NWLinkUseNodeNameMenu(Menu, NWBaseMenu):
|
||||
bl_idname = "NODE_MT_nw_link_use_node_name_menu"
|
||||
bl_label = "Use Node Name/Label"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
props = layout.operator(operators.NWLinkActiveToSelected.bl_idname, text="Don't Replace Links")
|
||||
props.replace = False
|
||||
props.use_node_name = True
|
||||
props.use_outputs_names = False
|
||||
props = layout.operator(operators.NWLinkActiveToSelected.bl_idname, text="Replace Links")
|
||||
props.replace = True
|
||||
props.use_node_name = True
|
||||
props.use_outputs_names = False
|
||||
|
||||
|
||||
class NWLinkUseOutputsNamesMenu(Menu, NWBaseMenu):
|
||||
bl_idname = "NODE_MT_nw_link_use_outputs_names_menu"
|
||||
bl_label = "Use Outputs Names"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
props = layout.operator(operators.NWLinkActiveToSelected.bl_idname, text="Don't Replace Links")
|
||||
props.replace = False
|
||||
props.use_node_name = False
|
||||
props.use_outputs_names = True
|
||||
props = layout.operator(operators.NWLinkActiveToSelected.bl_idname, text="Replace Links")
|
||||
props.replace = True
|
||||
props.use_node_name = False
|
||||
props.use_outputs_names = True
|
||||
|
||||
|
||||
class NWAttributeMenu(bpy.types.Menu):
|
||||
bl_idname = "NODE_MT_nw_node_attribute_menu"
|
||||
bl_label = "Attributes"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
space = context.space_data
|
||||
return (space.type == 'NODE_EDITOR'
|
||||
and space.node_tree is not None
|
||||
and space.node_tree.library is None
|
||||
and space.tree_type == 'ShaderNodeTree')
|
||||
|
||||
def draw(self, context):
|
||||
l = self.layout
|
||||
nodes, links = get_nodes_links(context)
|
||||
mat = context.object.active_material
|
||||
|
||||
objs = []
|
||||
for obj in bpy.data.objects:
|
||||
for slot in obj.material_slots:
|
||||
if slot.material == mat:
|
||||
objs.append(obj)
|
||||
attrs = []
|
||||
for obj in objs:
|
||||
if obj.data.attributes:
|
||||
for attr in obj.data.attributes:
|
||||
if not attr.is_internal:
|
||||
attrs.append(attr.name)
|
||||
attrs = list(set(attrs)) # get a unique list
|
||||
|
||||
if attrs:
|
||||
for attr in attrs:
|
||||
l.operator(operators.NWAddAttrNode.bl_idname, text=attr).attr_name = attr
|
||||
else:
|
||||
l.label(text="No attributes on objects with this material")
|
||||
|
||||
|
||||
class NWSwitchNodeTypeMenu(Menu, NWBaseMenu):
|
||||
bl_idname = "NODE_MT_nw_switch_node_type_menu"
|
||||
bl_label = "Switch Type to..."
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.label(text="This operator is removed due to the changes of node menus.", icon='ERROR')
|
||||
layout.label(text="A native implementation of the function is expected in the future.")
|
||||
|
||||
#
|
||||
# APPENDAGES TO EXISTING UI
|
||||
#
|
||||
|
||||
|
||||
def select_parent_children_buttons(self, context):
|
||||
layout = self.layout
|
||||
layout.operator(operators.NWSelectParentChildren.bl_idname,
|
||||
text="Select frame's members (children)").option = 'CHILD'
|
||||
layout.operator(operators.NWSelectParentChildren.bl_idname, text="Select parent frame").option = 'PARENT'
|
||||
|
||||
|
||||
def attr_nodes_menu_func(self, context):
|
||||
col = self.layout.column(align=True)
|
||||
col.menu("NODE_MT_nw_node_attribute_menu")
|
||||
col.separator()
|
||||
|
||||
|
||||
def multipleimages_menu_func(self, context):
|
||||
col = self.layout.column(align=True)
|
||||
col.operator(operators.NWAddMultipleImages.bl_idname, text="Multiple Images")
|
||||
col.operator(operators.NWAddSequence.bl_idname, text="Image Sequence")
|
||||
col.separator()
|
||||
|
||||
|
||||
def bgreset_menu_func(self, context):
|
||||
self.layout.operator(operators.NWResetBG.bl_idname)
|
||||
|
||||
|
||||
def save_viewer_menu_func(self, context):
|
||||
space = context.space_data
|
||||
if (space.type == 'NODE_EDITOR'
|
||||
and space.node_tree is not None
|
||||
and space.node_tree.library is None
|
||||
and space.tree_type == 'CompositorNodeTree'
|
||||
and context.scene.node_tree.nodes.active
|
||||
and context.scene.node_tree.nodes.active.type == "VIEWER"):
|
||||
self.layout.operator(operators.NWSaveViewer.bl_idname, icon='FILE_IMAGE')
|
||||
|
||||
|
||||
def reset_nodes_button(self, context):
|
||||
node_active = context.active_node
|
||||
node_selected = context.selected_nodes
|
||||
|
||||
# Check if active node is in the selection, ignore some node types
|
||||
if (len(node_selected) != 1
|
||||
or node_active is None
|
||||
or not node_active.select
|
||||
or node_active.type in {"REROUTE", "GROUP"}):
|
||||
return
|
||||
|
||||
row = self.layout.row()
|
||||
|
||||
if node_active.type == "FRAME":
|
||||
row.operator(operators.NWResetNodes.bl_idname, text="Reset Nodes in Frame", icon="FILE_REFRESH")
|
||||
else:
|
||||
row.operator(operators.NWResetNodes.bl_idname, text="Reset Node", icon="FILE_REFRESH")
|
||||
|
||||
self.layout.separator()
|
||||
|
||||
|
||||
classes = (
|
||||
NodeWranglerPanel,
|
||||
NodeWranglerMenu,
|
||||
NWMergeNodesMenu,
|
||||
NWMergeGeometryMenu,
|
||||
NWMergeShadersMenu,
|
||||
NWMergeMixMenu,
|
||||
NWConnectionListOutputs,
|
||||
NWConnectionListInputs,
|
||||
NWMergeMathMenu,
|
||||
NWBatchChangeNodesMenu,
|
||||
NWBatchChangeBlendTypeMenu,
|
||||
NWBatchChangeOperationMenu,
|
||||
NWCopyToSelectedMenu,
|
||||
NWCopyLabelMenu,
|
||||
NWAddReroutesMenu,
|
||||
NWLinkActiveToSelectedMenu,
|
||||
NWLinkStandardMenu,
|
||||
NWLinkUseNodeNameMenu,
|
||||
NWLinkUseOutputsNamesMenu,
|
||||
NWAttributeMenu,
|
||||
NWSwitchNodeTypeMenu,
|
||||
)
|
||||
|
||||
|
||||
def register():
|
||||
from bpy.utils import register_class
|
||||
for cls in classes:
|
||||
register_class(cls)
|
||||
|
||||
# menu items
|
||||
bpy.types.NODE_MT_select.append(select_parent_children_buttons)
|
||||
bpy.types.NODE_MT_category_shader_input.prepend(attr_nodes_menu_func)
|
||||
bpy.types.NODE_PT_backdrop.append(bgreset_menu_func)
|
||||
bpy.types.NODE_PT_active_node_generic.append(save_viewer_menu_func)
|
||||
bpy.types.NODE_MT_category_shader_texture.prepend(multipleimages_menu_func)
|
||||
bpy.types.NODE_MT_category_compositor_input.prepend(multipleimages_menu_func)
|
||||
bpy.types.NODE_PT_active_node_generic.prepend(reset_nodes_button)
|
||||
bpy.types.NODE_MT_node.prepend(reset_nodes_button)
|
||||
|
||||
|
||||
def unregister():
|
||||
# menu items
|
||||
bpy.types.NODE_MT_select.remove(select_parent_children_buttons)
|
||||
bpy.types.NODE_MT_category_shader_input.remove(attr_nodes_menu_func)
|
||||
bpy.types.NODE_PT_backdrop.remove(bgreset_menu_func)
|
||||
bpy.types.NODE_PT_active_node_generic.remove(save_viewer_menu_func)
|
||||
bpy.types.NODE_MT_category_shader_texture.remove(multipleimages_menu_func)
|
||||
bpy.types.NODE_MT_category_compositor_input.remove(multipleimages_menu_func)
|
||||
bpy.types.NODE_PT_active_node_generic.remove(reset_nodes_button)
|
||||
bpy.types.NODE_MT_node.remove(reset_nodes_button)
|
||||
|
||||
from bpy.utils import unregister_class
|
||||
for cls in classes:
|
||||
unregister_class(cls)
|
2481
scripts/addons_core/node_wrangler/operators.py
Normal file
2481
scripts/addons_core/node_wrangler/operators.py
Normal file
File diff suppressed because it is too large
Load Diff
397
scripts/addons_core/node_wrangler/preferences.py
Normal file
397
scripts/addons_core/node_wrangler/preferences.py
Normal file
@ -0,0 +1,397 @@
|
||||
# SPDX-FileCopyrightText: 2023 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import bpy
|
||||
from bpy.props import EnumProperty, BoolProperty, StringProperty
|
||||
from nodeitems_utils import node_categories_iter
|
||||
|
||||
from . import operators
|
||||
from . import interface
|
||||
|
||||
from .utils.constants import nice_hotkey_name
|
||||
|
||||
|
||||
# Principled prefs
|
||||
class NWPrincipledPreferences(bpy.types.PropertyGroup):
|
||||
base_color: StringProperty(
|
||||
name='Base Color',
|
||||
default='diffuse diff albedo base col color basecolor',
|
||||
description='Naming Components for Base Color maps')
|
||||
metallic: StringProperty(
|
||||
name='Metallic',
|
||||
default='metallic metalness metal mtl',
|
||||
description='Naming Components for metallness maps')
|
||||
specular: StringProperty(
|
||||
name='Specular',
|
||||
default='specularity specular spec spc',
|
||||
description='Naming Components for Specular maps')
|
||||
normal: StringProperty(
|
||||
name='Normal',
|
||||
default='normal nor nrm nrml norm',
|
||||
description='Naming Components for Normal maps')
|
||||
bump: StringProperty(
|
||||
name='Bump',
|
||||
default='bump bmp',
|
||||
description='Naming Components for bump maps')
|
||||
rough: StringProperty(
|
||||
name='Roughness',
|
||||
default='roughness rough rgh',
|
||||
description='Naming Components for roughness maps')
|
||||
gloss: StringProperty(
|
||||
name='Gloss',
|
||||
default='gloss glossy glossiness',
|
||||
description='Naming Components for glossy maps')
|
||||
displacement: StringProperty(
|
||||
name='Displacement',
|
||||
default='displacement displace disp dsp height heightmap',
|
||||
description='Naming Components for displacement maps')
|
||||
transmission: StringProperty(
|
||||
name='Transmission',
|
||||
default='transmission transparency',
|
||||
description='Naming Components for transmission maps')
|
||||
emission: StringProperty(
|
||||
name='Emission',
|
||||
default='emission emissive emit',
|
||||
description='Naming Components for emission maps')
|
||||
alpha: StringProperty(
|
||||
name='Alpha',
|
||||
default='alpha opacity',
|
||||
description='Naming Components for alpha maps')
|
||||
ambient_occlusion: StringProperty(
|
||||
name='Ambient Occlusion',
|
||||
default='ao ambient occlusion',
|
||||
description='Naming Components for AO maps')
|
||||
|
||||
|
||||
# Addon prefs
|
||||
class NWNodeWrangler(bpy.types.AddonPreferences):
|
||||
bl_idname = __package__
|
||||
|
||||
merge_hide: EnumProperty(
|
||||
name="Hide Mix nodes",
|
||||
items=(
|
||||
("ALWAYS", "Always", "Always collapse the new merge nodes"),
|
||||
("NON_SHADER", "Non-Shader", "Collapse in all cases except for shaders"),
|
||||
("NEVER", "Never", "Never collapse the new merge nodes")
|
||||
),
|
||||
default='NON_SHADER',
|
||||
description="When merging nodes with the Ctrl+Numpad0 hotkey (and similar) specify whether to collapse them or show the full node with options expanded")
|
||||
merge_position: EnumProperty(
|
||||
name="Mix Node Position",
|
||||
items=(
|
||||
("CENTER", "Center", "Place the Mix node between the two nodes"),
|
||||
("BOTTOM", "Bottom", "Place the Mix node at the same height as the lowest node")
|
||||
),
|
||||
default='CENTER',
|
||||
description="When merging nodes with the Ctrl+Numpad0 hotkey (and similar) specify the position of the new nodes")
|
||||
|
||||
show_hotkey_list: BoolProperty(
|
||||
name="Show Hotkey List",
|
||||
default=False,
|
||||
description="Expand this box into a list of all the hotkeys for functions in this addon"
|
||||
)
|
||||
hotkey_list_filter: StringProperty(
|
||||
name=" Filter by Name",
|
||||
default="",
|
||||
description="Show only hotkeys that have this text in their name",
|
||||
options={'TEXTEDIT_UPDATE'}
|
||||
)
|
||||
show_principled_lists: BoolProperty(
|
||||
name="Show Principled naming tags",
|
||||
default=False,
|
||||
description="Expand this box into a list of all naming tags for principled texture setup"
|
||||
)
|
||||
principled_tags: bpy.props.PointerProperty(type=NWPrincipledPreferences)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
col = layout.column()
|
||||
col.prop(self, "merge_position")
|
||||
col.prop(self, "merge_hide")
|
||||
|
||||
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)
|
||||
if self.show_principled_lists:
|
||||
tags = self.principled_tags
|
||||
|
||||
col.prop(tags, "base_color")
|
||||
col.prop(tags, "metallic")
|
||||
col.prop(tags, "specular")
|
||||
col.prop(tags, "rough")
|
||||
col.prop(tags, "gloss")
|
||||
col.prop(tags, "normal")
|
||||
col.prop(tags, "bump")
|
||||
col.prop(tags, "displacement")
|
||||
col.prop(tags, "transmission")
|
||||
col.prop(tags, "emission")
|
||||
col.prop(tags, "alpha")
|
||||
col.prop(tags, "ambient_occlusion")
|
||||
|
||||
box = layout.box()
|
||||
col = box.column(align=True)
|
||||
hotkey_button_name = "Show Hotkey List"
|
||||
if self.show_hotkey_list:
|
||||
hotkey_button_name = "Hide Hotkey List"
|
||||
col.prop(self, "show_hotkey_list", text=hotkey_button_name, toggle=True)
|
||||
if self.show_hotkey_list:
|
||||
col.prop(self, "hotkey_list_filter", icon="VIEWZOOM")
|
||||
col.separator()
|
||||
for hotkey in kmi_defs:
|
||||
if hotkey[7]:
|
||||
hotkey_name = hotkey[7]
|
||||
|
||||
if self.hotkey_list_filter.lower() in hotkey_name.lower():
|
||||
row = col.row(align=True)
|
||||
row.label(text=hotkey_name)
|
||||
keystr = nice_hotkey_name(hotkey[1])
|
||||
if hotkey[4]:
|
||||
keystr = "Shift " + keystr
|
||||
if hotkey[5]:
|
||||
keystr = "Alt " + keystr
|
||||
if hotkey[3]:
|
||||
keystr = "Ctrl " + keystr
|
||||
row.label(text=keystr)
|
||||
|
||||
|
||||
#
|
||||
# REGISTER/UNREGISTER CLASSES AND KEYMAP ITEMS
|
||||
#
|
||||
addon_keymaps = []
|
||||
# kmi_defs entry: (identifier, key, action, CTRL, SHIFT, ALT, props, nice name)
|
||||
# props entry: (property name, property value)
|
||||
kmi_defs = (
|
||||
# MERGE NODES
|
||||
# NWMergeNodes with Ctrl (AUTO).
|
||||
(operators.NWMergeNodes.bl_idname, 'NUMPAD_0', 'PRESS', True, False, False,
|
||||
(('mode', 'MIX'), ('merge_type', 'AUTO'),), "Merge Nodes (Automatic)"),
|
||||
(operators.NWMergeNodes.bl_idname, 'ZERO', 'PRESS', True, False, False,
|
||||
(('mode', 'MIX'), ('merge_type', 'AUTO'),), "Merge Nodes (Automatic)"),
|
||||
(operators.NWMergeNodes.bl_idname, 'NUMPAD_PLUS', 'PRESS', True, False, False,
|
||||
(('mode', 'ADD'), ('merge_type', 'AUTO'),), "Merge Nodes (Add)"),
|
||||
(operators.NWMergeNodes.bl_idname, 'EQUAL', 'PRESS', True, False, False,
|
||||
(('mode', 'ADD'), ('merge_type', 'AUTO'),), "Merge Nodes (Add)"),
|
||||
(operators.NWMergeNodes.bl_idname, 'NUMPAD_ASTERIX', 'PRESS', True, False, False,
|
||||
(('mode', 'MULTIPLY'), ('merge_type', 'AUTO'),), "Merge Nodes (Multiply)"),
|
||||
(operators.NWMergeNodes.bl_idname, 'EIGHT', 'PRESS', True, False, False,
|
||||
(('mode', 'MULTIPLY'), ('merge_type', 'AUTO'),), "Merge Nodes (Multiply)"),
|
||||
(operators.NWMergeNodes.bl_idname, 'NUMPAD_MINUS', 'PRESS', True, False, False,
|
||||
(('mode', 'SUBTRACT'), ('merge_type', 'AUTO'),), "Merge Nodes (Subtract)"),
|
||||
(operators.NWMergeNodes.bl_idname, 'MINUS', 'PRESS', True, False, False,
|
||||
(('mode', 'SUBTRACT'), ('merge_type', 'AUTO'),), "Merge Nodes (Subtract)"),
|
||||
(operators.NWMergeNodes.bl_idname, 'NUMPAD_SLASH', 'PRESS', True, False, False,
|
||||
(('mode', 'DIVIDE'), ('merge_type', 'AUTO'),), "Merge Nodes (Divide)"),
|
||||
(operators.NWMergeNodes.bl_idname, 'SLASH', 'PRESS', True, False, False,
|
||||
(('mode', 'DIVIDE'), ('merge_type', 'AUTO'),), "Merge Nodes (Divide)"),
|
||||
(operators.NWMergeNodes.bl_idname, 'COMMA', 'PRESS', True, False, False,
|
||||
(('mode', 'LESS_THAN'), ('merge_type', 'MATH'),), "Merge Nodes (Less than)"),
|
||||
(operators.NWMergeNodes.bl_idname, 'PERIOD', 'PRESS', True, False, False,
|
||||
(('mode', 'GREATER_THAN'), ('merge_type', 'MATH'),), "Merge Nodes (Greater than)"),
|
||||
(operators.NWMergeNodes.bl_idname, 'NUMPAD_PERIOD', 'PRESS', True, False, False,
|
||||
(('mode', 'MIX'), ('merge_type', 'ZCOMBINE'),), "Merge Nodes (Z-Combine)"),
|
||||
# NWMergeNodes with Ctrl Alt (MIX or ALPHAOVER)
|
||||
(operators.NWMergeNodes.bl_idname, 'NUMPAD_0', 'PRESS', True, False, True,
|
||||
(('mode', 'MIX'), ('merge_type', 'ALPHAOVER'),), "Merge Nodes (Alpha Over)"),
|
||||
(operators.NWMergeNodes.bl_idname, 'ZERO', 'PRESS', True, False, True,
|
||||
(('mode', 'MIX'), ('merge_type', 'ALPHAOVER'),), "Merge Nodes (Alpha Over)"),
|
||||
(operators.NWMergeNodes.bl_idname, 'NUMPAD_PLUS', 'PRESS', True, False, True,
|
||||
(('mode', 'ADD'), ('merge_type', 'MIX'),), "Merge Nodes (Color, Add)"),
|
||||
(operators.NWMergeNodes.bl_idname, 'EQUAL', 'PRESS', True, False, True,
|
||||
(('mode', 'ADD'), ('merge_type', 'MIX'),), "Merge Nodes (Color, Add)"),
|
||||
(operators.NWMergeNodes.bl_idname, 'NUMPAD_ASTERIX', 'PRESS', True, False, True,
|
||||
(('mode', 'MULTIPLY'), ('merge_type', 'MIX'),), "Merge Nodes (Color, Multiply)"),
|
||||
(operators.NWMergeNodes.bl_idname, 'EIGHT', 'PRESS', True, False, True,
|
||||
(('mode', 'MULTIPLY'), ('merge_type', 'MIX'),), "Merge Nodes (Color, Multiply)"),
|
||||
(operators.NWMergeNodes.bl_idname, 'NUMPAD_MINUS', 'PRESS', True, False, True,
|
||||
(('mode', 'SUBTRACT'), ('merge_type', 'MIX'),), "Merge Nodes (Color, Subtract)"),
|
||||
(operators.NWMergeNodes.bl_idname, 'MINUS', 'PRESS', True, False, True,
|
||||
(('mode', 'SUBTRACT'), ('merge_type', 'MIX'),), "Merge Nodes (Color, Subtract)"),
|
||||
(operators.NWMergeNodes.bl_idname, 'NUMPAD_SLASH', 'PRESS', True, False, True,
|
||||
(('mode', 'DIVIDE'), ('merge_type', 'MIX'),), "Merge Nodes (Color, Divide)"),
|
||||
(operators.NWMergeNodes.bl_idname, 'SLASH', 'PRESS', True, False, True,
|
||||
(('mode', 'DIVIDE'), ('merge_type', 'MIX'),), "Merge Nodes (Color, Divide)"),
|
||||
# NWMergeNodes with Ctrl Shift (MATH)
|
||||
(operators.NWMergeNodes.bl_idname, 'NUMPAD_PLUS', 'PRESS', True, True, False,
|
||||
(('mode', 'ADD'), ('merge_type', 'MATH'),), "Merge Nodes (Math, Add)"),
|
||||
(operators.NWMergeNodes.bl_idname, 'EQUAL', 'PRESS', True, True, False,
|
||||
(('mode', 'ADD'), ('merge_type', 'MATH'),), "Merge Nodes (Math, Add)"),
|
||||
(operators.NWMergeNodes.bl_idname, 'NUMPAD_ASTERIX', 'PRESS', True, True, False,
|
||||
(('mode', 'MULTIPLY'), ('merge_type', 'MATH'),), "Merge Nodes (Math, Multiply)"),
|
||||
(operators.NWMergeNodes.bl_idname, 'EIGHT', 'PRESS', True, True, False,
|
||||
(('mode', 'MULTIPLY'), ('merge_type', 'MATH'),), "Merge Nodes (Math, Multiply)"),
|
||||
(operators.NWMergeNodes.bl_idname, 'NUMPAD_MINUS', 'PRESS', True, True, False,
|
||||
(('mode', 'SUBTRACT'), ('merge_type', 'MATH'),), "Merge Nodes (Math, Subtract)"),
|
||||
(operators.NWMergeNodes.bl_idname, 'MINUS', 'PRESS', True, True, False,
|
||||
(('mode', 'SUBTRACT'), ('merge_type', 'MATH'),), "Merge Nodes (Math, Subtract)"),
|
||||
(operators.NWMergeNodes.bl_idname, 'NUMPAD_SLASH', 'PRESS', True, True, False,
|
||||
(('mode', 'DIVIDE'), ('merge_type', 'MATH'),), "Merge Nodes (Math, Divide)"),
|
||||
(operators.NWMergeNodes.bl_idname, 'SLASH', 'PRESS', True, True, False,
|
||||
(('mode', 'DIVIDE'), ('merge_type', 'MATH'),), "Merge Nodes (Math, Divide)"),
|
||||
(operators.NWMergeNodes.bl_idname, 'COMMA', 'PRESS', True, True, False,
|
||||
(('mode', 'LESS_THAN'), ('merge_type', 'MATH'),), "Merge Nodes (Math, Less than)"),
|
||||
(operators.NWMergeNodes.bl_idname, 'PERIOD', 'PRESS', True, True, False,
|
||||
(('mode', 'GREATER_THAN'), ('merge_type', 'MATH'),), "Merge Nodes (Math, Greater than)"),
|
||||
# BATCH CHANGE NODES
|
||||
# NWBatchChangeNodes with Alt
|
||||
(operators.NWBatchChangeNodes.bl_idname, 'NUMPAD_0', 'PRESS', False, False, True,
|
||||
(('blend_type', 'MIX'), ('operation', 'CURRENT'),), "Batch change blend type (Mix)"),
|
||||
(operators.NWBatchChangeNodes.bl_idname, 'ZERO', 'PRESS', False, False, True,
|
||||
(('blend_type', 'MIX'), ('operation', 'CURRENT'),), "Batch change blend type (Mix)"),
|
||||
(operators.NWBatchChangeNodes.bl_idname, 'NUMPAD_PLUS', 'PRESS', False, False, True,
|
||||
(('blend_type', 'ADD'), ('operation', 'ADD'),), "Batch change blend type (Add)"),
|
||||
(operators.NWBatchChangeNodes.bl_idname, 'EQUAL', 'PRESS', False, False, True,
|
||||
(('blend_type', 'ADD'), ('operation', 'ADD'),), "Batch change blend type (Add)"),
|
||||
(operators.NWBatchChangeNodes.bl_idname, 'NUMPAD_ASTERIX', 'PRESS', False, False, True,
|
||||
(('blend_type', 'MULTIPLY'), ('operation', 'MULTIPLY'),), "Batch change blend type (Multiply)"),
|
||||
(operators.NWBatchChangeNodes.bl_idname, 'EIGHT', 'PRESS', False, False, True,
|
||||
(('blend_type', 'MULTIPLY'), ('operation', 'MULTIPLY'),), "Batch change blend type (Multiply)"),
|
||||
(operators.NWBatchChangeNodes.bl_idname, 'NUMPAD_MINUS', 'PRESS', False, False, True,
|
||||
(('blend_type', 'SUBTRACT'), ('operation', 'SUBTRACT'),), "Batch change blend type (Subtract)"),
|
||||
(operators.NWBatchChangeNodes.bl_idname, 'MINUS', 'PRESS', False, False, True,
|
||||
(('blend_type', 'SUBTRACT'), ('operation', 'SUBTRACT'),), "Batch change blend type (Subtract)"),
|
||||
(operators.NWBatchChangeNodes.bl_idname, 'NUMPAD_SLASH', 'PRESS', False, False, True,
|
||||
(('blend_type', 'DIVIDE'), ('operation', 'DIVIDE'),), "Batch change blend type (Divide)"),
|
||||
(operators.NWBatchChangeNodes.bl_idname, 'SLASH', 'PRESS', False, False, True,
|
||||
(('blend_type', 'DIVIDE'), ('operation', 'DIVIDE'),), "Batch change blend type (Divide)"),
|
||||
(operators.NWBatchChangeNodes.bl_idname, 'COMMA', 'PRESS', False, False, True,
|
||||
(('blend_type', 'CURRENT'), ('operation', 'LESS_THAN'),), "Batch change blend type (Current)"),
|
||||
(operators.NWBatchChangeNodes.bl_idname, 'PERIOD', 'PRESS', False, False, True,
|
||||
(('blend_type', 'CURRENT'), ('operation', 'GREATER_THAN'),), "Batch change blend type (Current)"),
|
||||
(operators.NWBatchChangeNodes.bl_idname, 'DOWN_ARROW', 'PRESS', False, False, True,
|
||||
(('blend_type', 'NEXT'), ('operation', 'NEXT'),), "Batch change blend type (Next)"),
|
||||
(operators.NWBatchChangeNodes.bl_idname, 'UP_ARROW', 'PRESS', False, False, True,
|
||||
(('blend_type', 'PREV'), ('operation', 'PREV'),), "Batch change blend type (Previous)"),
|
||||
# LINK ACTIVE TO SELECTED
|
||||
# Don't use names, don't replace links (K)
|
||||
(operators.NWLinkActiveToSelected.bl_idname, 'K', 'PRESS', False, False, False,
|
||||
(('replace', False), ('use_node_name', False), ('use_outputs_names', False),), "Link active to selected (Don't replace links)"),
|
||||
# Don't use names, replace links (Shift K)
|
||||
(operators.NWLinkActiveToSelected.bl_idname, 'K', 'PRESS', False, True, False,
|
||||
(('replace', True), ('use_node_name', False), ('use_outputs_names', False),), "Link active to selected (Replace links)"),
|
||||
# Use node name, don't replace links (')
|
||||
(operators.NWLinkActiveToSelected.bl_idname, 'QUOTE', 'PRESS', False, False, False,
|
||||
(('replace', False), ('use_node_name', True), ('use_outputs_names', False),), "Link active to selected (Don't replace links, node names)"),
|
||||
# Use node name, replace links (Shift ')
|
||||
(operators.NWLinkActiveToSelected.bl_idname, 'QUOTE', 'PRESS', False, True, False,
|
||||
(('replace', True), ('use_node_name', True), ('use_outputs_names', False),), "Link active to selected (Replace links, node names)"),
|
||||
# Don't use names, don't replace links (;)
|
||||
(operators.NWLinkActiveToSelected.bl_idname, 'SEMI_COLON', 'PRESS', False, False, False,
|
||||
(('replace', False), ('use_node_name', False), ('use_outputs_names', True),), "Link active to selected (Don't replace links, output names)"),
|
||||
# Don't use names, replace links (')
|
||||
(operators.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
|
||||
(operators.NWChangeMixFactor.bl_idname, 'LEFT_ARROW', 'PRESS', False,
|
||||
False, True, (('option', -0.1),), "Reduce Mix Factor by 0.1"),
|
||||
(operators.NWChangeMixFactor.bl_idname, 'RIGHT_ARROW', 'PRESS', False,
|
||||
False, True, (('option', 0.1),), "Increase Mix Factor by 0.1"),
|
||||
(operators.NWChangeMixFactor.bl_idname, 'LEFT_ARROW', 'PRESS', False,
|
||||
True, True, (('option', -0.01),), "Reduce Mix Factor by 0.01"),
|
||||
(operators.NWChangeMixFactor.bl_idname, 'RIGHT_ARROW', 'PRESS', False,
|
||||
True, True, (('option', 0.01),), "Increase Mix Factor by 0.01"),
|
||||
(operators.NWChangeMixFactor.bl_idname, 'LEFT_ARROW', 'PRESS',
|
||||
True, True, True, (('option', 0.0),), "Set Mix Factor to 0.0"),
|
||||
(operators.NWChangeMixFactor.bl_idname, 'RIGHT_ARROW', 'PRESS',
|
||||
True, True, True, (('option', 1.0),), "Set Mix Factor to 1.0"),
|
||||
(operators.NWChangeMixFactor.bl_idname, 'NUMPAD_0', 'PRESS',
|
||||
True, True, True, (('option', 0.0),), "Set Mix Factor to 0.0"),
|
||||
(operators.NWChangeMixFactor.bl_idname, 'ZERO', 'PRESS', True, True, True, (('option', 0.0),), "Set Mix Factor to 0.0"),
|
||||
(operators.NWChangeMixFactor.bl_idname, 'NUMPAD_1', 'PRESS', True, True, True, (('option', 1.0),), "Mix Factor to 1.0"),
|
||||
(operators.NWChangeMixFactor.bl_idname, 'ONE', 'PRESS', True, True, True, (('option', 1.0),), "Set Mix Factor to 1.0"),
|
||||
# CLEAR LABEL (Alt L)
|
||||
(operators.NWClearLabel.bl_idname, 'L', 'PRESS', False, False, True, (('option', False),), "Clear node labels"),
|
||||
# MODIFY LABEL (Alt Shift L)
|
||||
(operators.NWModifyLabels.bl_idname, 'L', 'PRESS', False, True, True, None, "Modify node labels"),
|
||||
# Copy Label from active to selected
|
||||
(operators.NWCopyLabel.bl_idname, 'V', 'PRESS', False, True, False,
|
||||
(('option', 'FROM_ACTIVE'),), "Copy label from active to selected"),
|
||||
# DETACH OUTPUTS (Alt Shift D)
|
||||
(operators.NWDetachOutputs.bl_idname, 'D', 'PRESS', False, True, True, None, "Detach outputs"),
|
||||
# LINK TO OUTPUT NODE (O)
|
||||
(operators.NWLinkToOutputNode.bl_idname, 'O', 'PRESS', False, False, False, None, "Link to output node"),
|
||||
# SELECT PARENT/CHILDREN
|
||||
# Select Children
|
||||
(operators.NWSelectParentChildren.bl_idname, 'RIGHT_BRACKET', 'PRESS',
|
||||
False, False, False, (('option', 'CHILD'),), "Select children"),
|
||||
# Select Parent
|
||||
(operators.NWSelectParentChildren.bl_idname, 'LEFT_BRACKET', 'PRESS',
|
||||
False, False, False, (('option', 'PARENT'),), "Select Parent"),
|
||||
# Add Texture Setup
|
||||
(operators.NWAddTextureSetup.bl_idname, 'T', 'PRESS', True, False, False, None, "Add texture setup"),
|
||||
# Add Principled BSDF Texture Setup
|
||||
(operators.NWAddPrincipledSetup.bl_idname, 'T', 'PRESS', True, True, False, None, "Add Principled texture setup"),
|
||||
# Reset backdrop
|
||||
(operators.NWResetBG.bl_idname, 'Z', 'PRESS', False, False, False, None, "Reset backdrop image zoom"),
|
||||
# Delete unused
|
||||
(operators.NWDeleteUnused.bl_idname, 'X', 'PRESS', False, False, True, None, "Delete unused nodes"),
|
||||
# Frame Selected
|
||||
(operators.NWFrameSelected.bl_idname, 'P', 'PRESS', False, True, False, None, "Frame selected nodes"),
|
||||
# Swap Links
|
||||
(operators.NWSwapLinks.bl_idname, 'S', 'PRESS', False, False, True, None, "Swap Links"),
|
||||
# Reload Images
|
||||
(operators.NWReloadImages.bl_idname, 'R', 'PRESS', False, False, True, None, "Reload images"),
|
||||
# Lazy Mix
|
||||
(operators.NWLazyMix.bl_idname, 'RIGHTMOUSE', 'PRESS', True, True, False, None, "Lazy Mix"),
|
||||
# Lazy Connect
|
||||
(operators.NWLazyConnect.bl_idname, 'RIGHTMOUSE', 'PRESS', False, False, True, (('with_menu', False),), "Lazy Connect"),
|
||||
# Lazy Connect with Menu
|
||||
(operators.NWLazyConnect.bl_idname, 'RIGHTMOUSE', 'PRESS', False,
|
||||
True, True, (('with_menu', True),), "Lazy Connect with Socket Menu"),
|
||||
# Align Nodes
|
||||
(operators.NWAlignNodes.bl_idname, 'EQUAL', 'PRESS', False, True,
|
||||
False, None, "Align selected nodes neatly in a row/column"),
|
||||
# Reset Nodes (Back Space)
|
||||
(operators.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', interface.NodeWranglerMenu.bl_idname),), "Node Wrangler menu"),
|
||||
('wm.call_menu', 'SLASH', 'PRESS', False, False, False,
|
||||
(('name', interface.NWAddReroutesMenu.bl_idname),), "Add Reroutes menu"),
|
||||
('wm.call_menu', 'NUMPAD_SLASH', 'PRESS', False, False, False,
|
||||
(('name', interface.NWAddReroutesMenu.bl_idname),), "Add Reroutes menu"),
|
||||
('wm.call_menu', 'BACK_SLASH', 'PRESS', False, False, False,
|
||||
(('name', interface.NWLinkActiveToSelectedMenu.bl_idname),), "Link active to selected (menu)"),
|
||||
('wm.call_menu', 'C', 'PRESS', False, True, False,
|
||||
(('name', interface.NWCopyToSelectedMenu.bl_idname),), "Copy to selected (menu)"),
|
||||
('wm.call_menu', 'S', 'PRESS', False, True, False,
|
||||
(('name', interface.NWSwitchNodeTypeMenu.bl_idname),), "Switch node type menu"),
|
||||
)
|
||||
|
||||
classes = (
|
||||
NWPrincipledPreferences, NWNodeWrangler
|
||||
)
|
||||
|
||||
|
||||
def register():
|
||||
from bpy.utils import register_class
|
||||
for cls in classes:
|
||||
register_class(cls)
|
||||
|
||||
# keymaps
|
||||
addon_keymaps.clear()
|
||||
kc = bpy.context.window_manager.keyconfigs.addon
|
||||
if kc:
|
||||
km = kc.keymaps.new(name='Node Editor', space_type="NODE_EDITOR")
|
||||
for (identifier, key, action, CTRL, SHIFT, ALT, props, nicename) in kmi_defs:
|
||||
kmi = km.keymap_items.new(identifier, key, action, ctrl=CTRL, shift=SHIFT, alt=ALT)
|
||||
if props:
|
||||
for prop, value in props:
|
||||
setattr(kmi.properties, prop, value)
|
||||
addon_keymaps.append((km, kmi))
|
||||
|
||||
|
||||
def unregister():
|
||||
|
||||
# keymaps
|
||||
for km, kmi in addon_keymaps:
|
||||
km.keymap_items.remove(kmi)
|
||||
addon_keymaps.clear()
|
||||
|
||||
from bpy.utils import unregister_class
|
||||
for cls in classes:
|
||||
unregister_class(cls)
|
232
scripts/addons_core/node_wrangler/utils/constants.py
Normal file
232
scripts/addons_core/node_wrangler/utils/constants.py
Normal file
@ -0,0 +1,232 @@
|
||||
# SPDX-FileCopyrightText: 2023 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
from collections import namedtuple
|
||||
|
||||
|
||||
#################
|
||||
# rl_outputs:
|
||||
# list of outputs of Input Render Layer
|
||||
# with attributes determining if pass is used,
|
||||
# and MultiLayer EXR outputs names and corresponding render engines
|
||||
#
|
||||
# rl_outputs entry = (render_pass, rl_output_name, exr_output_name, in_eevee, in_cycles)
|
||||
RL_entry = namedtuple('RL_Entry', ['render_pass', 'output_name', 'exr_output_name', 'in_eevee', 'in_cycles'])
|
||||
rl_outputs = (
|
||||
RL_entry('use_pass_ambient_occlusion', 'AO', 'AO', True, True),
|
||||
RL_entry('use_pass_combined', 'Image', 'Combined', True, True),
|
||||
RL_entry('use_pass_diffuse_color', 'Diffuse Color', 'DiffCol', False, True),
|
||||
RL_entry('use_pass_diffuse_direct', 'Diffuse Direct', 'DiffDir', False, True),
|
||||
RL_entry('use_pass_diffuse_indirect', 'Diffuse Indirect', 'DiffInd', False, True),
|
||||
RL_entry('use_pass_emit', 'Emit', 'Emit', False, True),
|
||||
RL_entry('use_pass_environment', 'Environment', 'Env', False, False),
|
||||
RL_entry('use_pass_glossy_color', 'Glossy Color', 'GlossCol', False, True),
|
||||
RL_entry('use_pass_glossy_direct', 'Glossy Direct', 'GlossDir', False, True),
|
||||
RL_entry('use_pass_glossy_indirect', 'Glossy Indirect', 'GlossInd', False, True),
|
||||
RL_entry('use_pass_indirect', 'Indirect', 'Indirect', False, False),
|
||||
RL_entry('use_pass_material_index', 'IndexMA', 'IndexMA', False, True),
|
||||
RL_entry('use_pass_mist', 'Mist', 'Mist', True, True),
|
||||
RL_entry('use_pass_normal', 'Normal', 'Normal', True, True),
|
||||
RL_entry('use_pass_object_index', 'IndexOB', 'IndexOB', False, True),
|
||||
RL_entry('use_pass_shadow', 'Shadow', 'Shadow', False, True),
|
||||
RL_entry('use_pass_subsurface_color', 'Subsurface Color', 'SubsurfaceCol', True, True),
|
||||
RL_entry('use_pass_subsurface_direct', 'Subsurface Direct', 'SubsurfaceDir', True, True),
|
||||
RL_entry('use_pass_subsurface_indirect', 'Subsurface Indirect', 'SubsurfaceInd', False, True),
|
||||
RL_entry('use_pass_transmission_color', 'Transmission Color', 'TransCol', False, True),
|
||||
RL_entry('use_pass_transmission_direct', 'Transmission Direct', 'TransDir', False, True),
|
||||
RL_entry('use_pass_transmission_indirect', 'Transmission Indirect', 'TransInd', False, True),
|
||||
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.
|
||||
blend_types = [
|
||||
('MIX', 'Mix', 'Mix Mode'),
|
||||
('ADD', 'Add', 'Add Mode'),
|
||||
('MULTIPLY', 'Multiply', 'Multiply Mode'),
|
||||
('SUBTRACT', 'Subtract', 'Subtract Mode'),
|
||||
('SCREEN', 'Screen', 'Screen Mode'),
|
||||
('DIVIDE', 'Divide', 'Divide Mode'),
|
||||
('DIFFERENCE', 'Difference', 'Difference Mode'),
|
||||
('DARKEN', 'Darken', 'Darken Mode'),
|
||||
('LIGHTEN', 'Lighten', 'Lighten Mode'),
|
||||
('OVERLAY', 'Overlay', 'Overlay Mode'),
|
||||
('DODGE', 'Dodge', 'Dodge Mode'),
|
||||
('BURN', 'Burn', 'Burn Mode'),
|
||||
('HUE', 'Hue', 'Hue Mode'),
|
||||
('SATURATION', 'Saturation', 'Saturation Mode'),
|
||||
('VALUE', 'Value', 'Value Mode'),
|
||||
('COLOR', 'Color', 'Color Mode'),
|
||||
('SOFT_LIGHT', 'Soft Light', 'Soft Light Mode'),
|
||||
('LINEAR_LIGHT', 'Linear Light', 'Linear Light Mode'),
|
||||
]
|
||||
|
||||
# list of operations of "Math" nodes in a form that can be used as 'items' for EnumProperty.
|
||||
# used list, not tuple for easy merging with other lists.
|
||||
operations = [
|
||||
('ADD', 'Add', 'Add Mode'),
|
||||
('SUBTRACT', 'Subtract', 'Subtract Mode'),
|
||||
('MULTIPLY', 'Multiply', 'Multiply Mode'),
|
||||
('DIVIDE', 'Divide', 'Divide Mode'),
|
||||
('MULTIPLY_ADD', 'Multiply Add', 'Multiply Add Mode'),
|
||||
('SINE', 'Sine', 'Sine Mode'),
|
||||
('COSINE', 'Cosine', 'Cosine Mode'),
|
||||
('TANGENT', 'Tangent', 'Tangent Mode'),
|
||||
('ARCSINE', 'Arcsine', 'Arcsine Mode'),
|
||||
('ARCCOSINE', 'Arccosine', 'Arccosine Mode'),
|
||||
('ARCTANGENT', 'Arctangent', 'Arctangent Mode'),
|
||||
('ARCTAN2', 'Arctan2', 'Arctan2 Mode'),
|
||||
('SINH', 'Hyperbolic Sine', 'Hyperbolic Sine Mode'),
|
||||
('COSH', 'Hyperbolic Cosine', 'Hyperbolic Cosine Mode'),
|
||||
('TANH', 'Hyperbolic Tangent', 'Hyperbolic Tangent Mode'),
|
||||
('POWER', 'Power', 'Power Mode'),
|
||||
('LOGARITHM', 'Logarithm', 'Logarithm Mode'),
|
||||
('SQRT', 'Square Root', 'Square Root Mode'),
|
||||
('INVERSE_SQRT', 'Inverse Square Root', 'Inverse Square Root Mode'),
|
||||
('EXPONENT', 'Exponent', 'Exponent Mode'),
|
||||
('MINIMUM', 'Minimum', 'Minimum Mode'),
|
||||
('MAXIMUM', 'Maximum', 'Maximum Mode'),
|
||||
('LESS_THAN', 'Less Than', 'Less Than Mode'),
|
||||
('GREATER_THAN', 'Greater Than', 'Greater Than Mode'),
|
||||
('SIGN', 'Sign', 'Sign Mode'),
|
||||
('COMPARE', 'Compare', 'Compare Mode'),
|
||||
('SMOOTH_MIN', 'Smooth Minimum', 'Smooth Minimum Mode'),
|
||||
('SMOOTH_MAX', 'Smooth Maximum', 'Smooth Maximum Mode'),
|
||||
('FRACT', 'Fraction', 'Fraction Mode'),
|
||||
('MODULO', 'Modulo', 'Modulo Mode'),
|
||||
('SNAP', 'Snap', 'Snap Mode'),
|
||||
('WRAP', 'Wrap', 'Wrap Mode'),
|
||||
('PINGPONG', 'Pingpong', 'Pingpong Mode'),
|
||||
('ABSOLUTE', 'Absolute', 'Absolute Mode'),
|
||||
('ROUND', 'Round', 'Round Mode'),
|
||||
('FLOOR', 'Floor', 'Floor Mode'),
|
||||
('CEIL', 'Ceil', 'Ceil Mode'),
|
||||
('TRUNCATE', 'Truncate', 'Truncate Mode'),
|
||||
('RADIANS', 'To Radians', 'To Radians Mode'),
|
||||
('DEGREES', 'To Degrees', 'To Degrees Mode'),
|
||||
]
|
||||
|
||||
# Operations used by the geometry boolean node and join geometry node
|
||||
geo_combine_operations = [
|
||||
('JOIN', 'Join Geometry', 'Join Geometry Mode'),
|
||||
('INTERSECT', 'Intersect', 'Intersect Mode'),
|
||||
('UNION', 'Union', 'Union Mode'),
|
||||
('DIFFERENCE', 'Difference', 'Difference Mode'),
|
||||
]
|
||||
|
||||
# in NWBatchChangeNodes additional types/operations. Can be used as 'items' for EnumProperty.
|
||||
# used list, not tuple for easy merging with other lists.
|
||||
navs = [
|
||||
('CURRENT', 'Current', 'Leave at current state'),
|
||||
('NEXT', 'Next', 'Next blend type/operation'),
|
||||
('PREV', 'Prev', 'Previous blend type/operation'),
|
||||
]
|
||||
|
||||
draw_color_sets = {
|
||||
"red_white": (
|
||||
(1.0, 1.0, 1.0, 0.7),
|
||||
(1.0, 0.0, 0.0, 0.7),
|
||||
(0.8, 0.2, 0.2, 1.0)
|
||||
),
|
||||
"green": (
|
||||
(0.0, 0.0, 0.0, 1.0),
|
||||
(0.38, 0.77, 0.38, 1.0),
|
||||
(0.38, 0.77, 0.38, 1.0)
|
||||
),
|
||||
"yellow": (
|
||||
(0.0, 0.0, 0.0, 1.0),
|
||||
(0.77, 0.77, 0.16, 1.0),
|
||||
(0.77, 0.77, 0.16, 1.0)
|
||||
),
|
||||
"purple": (
|
||||
(0.0, 0.0, 0.0, 1.0),
|
||||
(0.38, 0.38, 0.77, 1.0),
|
||||
(0.38, 0.38, 0.77, 1.0)
|
||||
),
|
||||
"grey": (
|
||||
(0.0, 0.0, 0.0, 1.0),
|
||||
(0.63, 0.63, 0.63, 1.0),
|
||||
(0.63, 0.63, 0.63, 1.0)
|
||||
),
|
||||
"black": (
|
||||
(1.0, 1.0, 1.0, 0.7),
|
||||
(0.0, 0.0, 0.0, 0.7),
|
||||
(0.2, 0.2, 0.2, 1.0)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
def get_texture_node_types():
|
||||
return [
|
||||
"ShaderNodeTexBrick",
|
||||
"ShaderNodeTexChecker",
|
||||
"ShaderNodeTexEnvironment",
|
||||
"ShaderNodeTexGradient",
|
||||
"ShaderNodeTexIES",
|
||||
"ShaderNodeTexImage",
|
||||
"ShaderNodeTexMagic",
|
||||
"ShaderNodeTexMusgrave",
|
||||
"ShaderNodeTexNoise",
|
||||
"ShaderNodeTexPointDensity",
|
||||
"ShaderNodeTexSky",
|
||||
"ShaderNodeTexVoronoi",
|
||||
"ShaderNodeTexWave",
|
||||
"ShaderNodeTexWhiteNoise"
|
||||
]
|
||||
|
||||
|
||||
def nice_hotkey_name(punc):
|
||||
# convert the ugly string name into the actual character
|
||||
nice_name = {
|
||||
'LEFTMOUSE': "LMB",
|
||||
'MIDDLEMOUSE': "MMB",
|
||||
'RIGHTMOUSE': "RMB",
|
||||
'WHEELUPMOUSE': "Wheel Up",
|
||||
'WHEELDOWNMOUSE': "Wheel Down",
|
||||
'WHEELINMOUSE': "Wheel In",
|
||||
'WHEELOUTMOUSE': "Wheel Out",
|
||||
'ZERO': "0",
|
||||
'ONE': "1",
|
||||
'TWO': "2",
|
||||
'THREE': "3",
|
||||
'FOUR': "4",
|
||||
'FIVE': "5",
|
||||
'SIX': "6",
|
||||
'SEVEN': "7",
|
||||
'EIGHT': "8",
|
||||
'NINE': "9",
|
||||
'OSKEY': "Super",
|
||||
'RET': "Enter",
|
||||
'LINE_FEED': "Enter",
|
||||
'SEMI_COLON': ";",
|
||||
'PERIOD': ".",
|
||||
'COMMA': ",",
|
||||
'QUOTE': '"',
|
||||
'MINUS': "-",
|
||||
'SLASH': "/",
|
||||
'BACK_SLASH': "\\",
|
||||
'EQUAL': "=",
|
||||
'NUMPAD_1': "Numpad 1",
|
||||
'NUMPAD_2': "Numpad 2",
|
||||