Node Wrangler: Remove Switch Type operator #104957
@ -20,10 +20,6 @@ def drawlayout(context, layout, mode='non-panel'):
|
|||||||
col.menu(NWMergeNodesMenu.bl_idname)
|
col.menu(NWMergeNodesMenu.bl_idname)
|
||||||
col.separator()
|
col.separator()
|
||||||
|
|
||||||
col = layout.column(align=True)
|
|
||||||
col.menu(NWSwitchNodeTypeMenu.bl_idname, text="Switch Node Type")
|
|
||||||
col.separator()
|
|
||||||
|
|
||||||
if tree_type == 'ShaderNodeTree':
|
if tree_type == 'ShaderNodeTree':
|
||||||
col = layout.column(align=True)
|
col = layout.column(align=True)
|
||||||
col.operator(operators.NWAddTextureSetup.bl_idname, text="Add Texture Setup", icon='NODE_SEL')
|
col.operator(operators.NWAddTextureSetup.bl_idname, text="Add Texture Setup", icon='NODE_SEL')
|
||||||
@ -385,32 +381,8 @@ class NWSwitchNodeTypeMenu(Menu, NWBase):
|
|||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
categories = [c for c in node_categories_iter(context)
|
layout.label(text="This operator is removed due to the changes of node menus.", icon='ERROR')
|
||||||
if c.name not in ['Group', 'Script']]
|
layout.label(text="A native implementation of the function is expected in the future.")
|
||||||
for cat in categories:
|
|
||||||
idname = f"NODE_MT_nw_switch_{cat.identifier}_submenu"
|
|
||||||
if hasattr(bpy.types, idname):
|
|
||||||
layout.menu(idname)
|
|
||||||
else:
|
|
||||||
layout.label(text="Unable to load altered node lists.")
|
|
||||||
layout.label(text="Please re-enable Node Wrangler.")
|
|
||||||
break
|
|
||||||
|
|
||||||
|
|
||||||
def draw_switch_category_submenu(self, context):
|
|
||||||
layout = self.layout
|
|
||||||
if self.category.name == 'Layout':
|
|
||||||
for node in self.category.items(context):
|
|
||||||
if node.nodetype != 'NodeFrame':
|
|
||||||
props = layout.operator(operators.NWSwitchNodeType.bl_idname, text=node.label)
|
|
||||||
props.to_type = node.nodetype
|
|
||||||
else:
|
|
||||||
for node in self.category.items(context):
|
|
||||||
if isinstance(node, NodeItemCustom):
|
|
||||||
node.draw(self, layout, context)
|
|
||||||
continue
|
|
||||||
props = layout.operator(operators.NWSwitchNodeType.bl_idname, text=node.label)
|
|
||||||
props.to_type = node.nodetype
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# APPENDAGES TO EXISTING UI
|
# APPENDAGES TO EXISTING UI
|
||||||
|
@ -914,195 +914,6 @@ class NWReloadImages(Operator):
|
|||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
|
||||||
class NWSwitchNodeType(Operator, NWBase):
|
|
||||||
"""Switch type of selected nodes """
|
|
||||||
bl_idname = "node.nw_swtch_node_type"
|
|
||||||
bl_label = "Switch Node Type"
|
|
||||||
bl_options = {'REGISTER', 'UNDO'}
|
|
||||||
|
|
||||||
to_type: StringProperty(
|
|
||||||
name="Switch to type",
|
|
||||||
default='',
|
|
||||||
)
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
to_type = self.to_type
|
|
||||||
if len(to_type) == 0:
|
|
||||||
return {'CANCELLED'}
|
|
||||||
|
|
||||||
nodes, links = get_nodes_links(context)
|
|
||||||
# Those types of nodes will not swap.
|
|
||||||
src_excludes = ('NodeFrame')
|
|
||||||
# Those attributes of nodes will be copied if possible
|
|
||||||
attrs_to_pass = ('color', 'hide', 'label', 'mute', 'parent',
|
|
||||||
'show_options', 'show_preview', 'show_texture',
|
|
||||||
'use_alpha', 'use_clamp', 'use_custom_color', 'location'
|
|
||||||
)
|
|
||||||
selected = [n for n in nodes if n.select]
|
|
||||||
reselect = []
|
|
||||||
for node in [n for n in selected if
|
|
||||||
n.rna_type.identifier not in src_excludes and
|
|
||||||
n.rna_type.identifier != to_type]:
|
|
||||||
new_node = nodes.new(to_type)
|
|
||||||
for attr in attrs_to_pass:
|
|
||||||
if hasattr(node, attr) and hasattr(new_node, attr):
|
|
||||||
setattr(new_node, attr, getattr(node, attr))
|
|
||||||
# set image datablock of dst to image of src
|
|
||||||
if hasattr(node, 'image') and hasattr(new_node, 'image'):
|
|
||||||
if node.image:
|
|
||||||
new_node.image = node.image
|
|
||||||
# Special cases
|
|
||||||
if new_node.type == 'SWITCH':
|
|
||||||
new_node.hide = True
|
|
||||||
# Dictionaries: src_sockets and dst_sockets:
|
|
||||||
# 'INPUTS': input sockets ordered by type (entry 'MAIN' main type of inputs).
|
|
||||||
# 'OUTPUTS': output sockets ordered by type (entry 'MAIN' main type of outputs).
|
|
||||||
# in 'INPUTS' and 'OUTPUTS':
|
|
||||||
# 'SHADER', 'RGBA', 'VECTOR', 'VALUE' - sockets of those types.
|
|
||||||
# socket entry:
|
|
||||||
# (index_in_type, socket_index, socket_name, socket_default_value, socket_links)
|
|
||||||
src_sockets = {
|
|
||||||
'INPUTS': {'SHADER': [], 'RGBA': [], 'VECTOR': [], 'VALUE': [], 'MAIN': None},
|
|
||||||
'OUTPUTS': {'SHADER': [], 'RGBA': [], 'VECTOR': [], 'VALUE': [], 'MAIN': None},
|
|
||||||
}
|
|
||||||
dst_sockets = {
|
|
||||||
'INPUTS': {'SHADER': [], 'RGBA': [], 'VECTOR': [], 'VALUE': [], 'MAIN': None},
|
|
||||||
'OUTPUTS': {'SHADER': [], 'RGBA': [], 'VECTOR': [], 'VALUE': [], 'MAIN': None},
|
|
||||||
}
|
|
||||||
types_order_one = 'SHADER', 'RGBA', 'VECTOR', 'VALUE'
|
|
||||||
types_order_two = 'SHADER', 'VECTOR', 'RGBA', 'VALUE'
|
|
||||||
# check src node to set src_sockets values and dst node to set dst_sockets dict values
|
|
||||||
for sockets, nd in ((src_sockets, node), (dst_sockets, new_node)):
|
|
||||||
# Check node's inputs and outputs and fill proper entries in "sockets" dict
|
|
||||||
for in_out, in_out_name in ((nd.inputs, 'INPUTS'), (nd.outputs, 'OUTPUTS')):
|
|
||||||
# enumerate in inputs, then in outputs
|
|
||||||
# find name, default value and links of socket
|
|
||||||
for i, socket in enumerate(in_out):
|
|
||||||
the_name = socket.name
|
|
||||||
dval = None
|
|
||||||
# Not every socket, especially in outputs has "default_value"
|
|
||||||
if hasattr(socket, 'default_value'):
|
|
||||||
dval = socket.default_value
|
|
||||||
socket_links = []
|
|
||||||
for lnk in socket.links:
|
|
||||||
socket_links.append(lnk)
|
|
||||||
# check type of socket to fill proper keys.
|
|
||||||
for the_type in types_order_one:
|
|
||||||
if socket.type == the_type:
|
|
||||||
# create values for sockets['INPUTS'][the_type] and sockets['OUTPUTS'][the_type]
|
|
||||||
# entry structure: (index_in_type, socket_index, socket_name,
|
|
||||||
# socket_default_value, socket_links)
|
|
||||||
sockets[in_out_name][the_type].append(
|
|
||||||
(len(sockets[in_out_name][the_type]), i, the_name, dval, socket_links))
|
|
||||||
# Check which of the types in inputs/outputs is considered to be "main".
|
|
||||||
# Set values of sockets['INPUTS']['MAIN'] and sockets['OUTPUTS']['MAIN']
|
|
||||||
for type_check in types_order_one:
|
|
||||||
if sockets[in_out_name][type_check]:
|
|
||||||
sockets[in_out_name]['MAIN'] = type_check
|
|
||||||
break
|
|
||||||
|
|
||||||
matches = {
|
|
||||||
'INPUTS': {'SHADER': [], 'RGBA': [], 'VECTOR': [], 'VALUE_NAME': [], 'VALUE': [], 'MAIN': []},
|
|
||||||
'OUTPUTS': {'SHADER': [], 'RGBA': [], 'VECTOR': [], 'VALUE_NAME': [], 'VALUE': [], 'MAIN': []},
|
|
||||||
}
|
|
||||||
|
|
||||||
for inout, soctype in (
|
|
||||||
('INPUTS', 'MAIN',),
|
|
||||||
('INPUTS', 'SHADER',),
|
|
||||||
('INPUTS', 'RGBA',),
|
|
||||||
('INPUTS', 'VECTOR',),
|
|
||||||
('INPUTS', 'VALUE',),
|
|
||||||
('OUTPUTS', 'MAIN',),
|
|
||||||
('OUTPUTS', 'SHADER',),
|
|
||||||
('OUTPUTS', 'RGBA',),
|
|
||||||
('OUTPUTS', 'VECTOR',),
|
|
||||||
('OUTPUTS', 'VALUE',),
|
|
||||||
):
|
|
||||||
if src_sockets[inout][soctype] and dst_sockets[inout][soctype]:
|
|
||||||
if soctype == 'MAIN':
|
|
||||||
sc = src_sockets[inout][src_sockets[inout]['MAIN']]
|
|
||||||
dt = dst_sockets[inout][dst_sockets[inout]['MAIN']]
|
|
||||||
else:
|
|
||||||
sc = src_sockets[inout][soctype]
|
|
||||||
dt = dst_sockets[inout][soctype]
|
|
||||||
# start with 'dt' to determine number of possibilities.
|
|
||||||
for i, soc in enumerate(dt):
|
|
||||||
# if src main has enough entries - match them with dst main sockets by indexes.
|
|
||||||
if len(sc) > i:
|
|
||||||
matches[inout][soctype].append(((sc[i][1], sc[i][3]), (soc[1], soc[3])))
|
|
||||||
# add 'VALUE_NAME' criterion to inputs.
|
|
||||||
if inout == 'INPUTS' and soctype == 'VALUE':
|
|
||||||
for s in sc:
|
|
||||||
if s[2] == soc[2]: # if names match
|
|
||||||
# append src (index, dval), dst (index, dval)
|
|
||||||
matches['INPUTS']['VALUE_NAME'].append(((s[1], s[3]), (soc[1], soc[3])))
|
|
||||||
|
|
||||||
# When src ['INPUTS']['MAIN'] is 'VECTOR' replace 'MAIN' with matches VECTOR if possible.
|
|
||||||
# This creates better links when relinking textures.
|
|
||||||
if src_sockets['INPUTS']['MAIN'] == 'VECTOR' and matches['INPUTS']['VECTOR']:
|
|
||||||
matches['INPUTS']['MAIN'] = matches['INPUTS']['VECTOR']
|
|
||||||
|
|
||||||
# Pass default values and RELINK:
|
|
||||||
for tp in ('MAIN', 'SHADER', 'RGBA', 'VECTOR', 'VALUE_NAME', 'VALUE'):
|
|
||||||
# INPUTS: Base on matches in proper order.
|
|
||||||
for (src_i, src_dval), (dst_i, dst_dval) in matches['INPUTS'][tp]:
|
|
||||||
# pass dvals
|
|
||||||
if src_dval and dst_dval and tp in {'RGBA', 'VALUE_NAME'}:
|
|
||||||
new_node.inputs[dst_i].default_value = src_dval
|
|
||||||
# Special case: switch to math
|
|
||||||
if node.type in {'MIX_RGB', 'ALPHAOVER', 'ZCOMBINE'} and\
|
|
||||||
new_node.type == 'MATH' and\
|
|
||||||
tp == 'MAIN':
|
|
||||||
new_dst_dval = max(src_dval[0], src_dval[1], src_dval[2])
|
|
||||||
new_node.inputs[dst_i].default_value = new_dst_dval
|
|
||||||
if node.type == 'MIX_RGB':
|
|
||||||
if node.blend_type in [o[0] for o in operations]:
|
|
||||||
new_node.operation = node.blend_type
|
|
||||||
# Special case: switch from math to some types
|
|
||||||
if node.type == 'MATH' and\
|
|
||||||
new_node.type in {'MIX_RGB', 'ALPHAOVER', 'ZCOMBINE'} and\
|
|
||||||
tp == 'MAIN':
|
|
||||||
for i in range(3):
|
|
||||||
new_node.inputs[dst_i].default_value[i] = src_dval
|
|
||||||
if new_node.type == 'MIX_RGB':
|
|
||||||
if node.operation in [t[0] for t in blend_types]:
|
|
||||||
new_node.blend_type = node.operation
|
|
||||||
# Set Fac of MIX_RGB to 1.0
|
|
||||||
new_node.inputs[0].default_value = 1.0
|
|
||||||
# make link only when dst matching input is not linked already.
|
|
||||||
if node.inputs[src_i].links and not new_node.inputs[dst_i].links:
|
|
||||||
in_src_link = node.inputs[src_i].links[0]
|
|
||||||
in_dst_socket = new_node.inputs[dst_i]
|
|
||||||
connect_sockets(in_src_link.from_socket, in_dst_socket)
|
|
||||||
links.remove(in_src_link)
|
|
||||||
# OUTPUTS: Base on matches in proper order.
|
|
||||||
for (src_i, src_dval), (dst_i, dst_dval) in matches['OUTPUTS'][tp]:
|
|
||||||
for out_src_link in node.outputs[src_i].links:
|
|
||||||
out_dst_socket = new_node.outputs[dst_i]
|
|
||||||
connect_sockets(out_dst_socket, out_src_link.to_socket)
|
|
||||||
# relink rest inputs if possible, no criteria
|
|
||||||
for src_inp in node.inputs:
|
|
||||||
for dst_inp in new_node.inputs:
|
|
||||||
if src_inp.links and not dst_inp.links:
|
|
||||||
src_link = src_inp.links[0]
|
|
||||||
connect_sockets(src_link.from_socket, dst_inp)
|
|
||||||
links.remove(src_link)
|
|
||||||
# relink rest outputs if possible, base on node kind if any left.
|
|
||||||
for src_o in node.outputs:
|
|
||||||
for out_src_link in src_o.links:
|
|
||||||
for dst_o in new_node.outputs:
|
|
||||||
if src_o.type == dst_o.type:
|
|
||||||
connect_sockets(dst_o, out_src_link.to_socket)
|
|
||||||
# relink rest outputs no criteria if any left. Link all from first output.
|
|
||||||
for src_o in node.outputs:
|
|
||||||
for out_src_link in src_o.links:
|
|
||||||
if new_node.outputs:
|
|
||||||
connect_sockets(new_node.outputs[0], out_src_link.to_socket)
|
|
||||||
nodes.remove(node)
|
|
||||||
force_update(context)
|
|
||||||
return {'FINISHED'}
|
|
||||||
|
|
||||||
|
|
||||||
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"
|
||||||
@ -2976,7 +2787,6 @@ classes = (
|
|||||||
NWPreviewNode,
|
NWPreviewNode,
|
||||||
NWFrameSelected,
|
NWFrameSelected,
|
||||||
NWReloadImages,
|
NWReloadImages,
|
||||||
NWSwitchNodeType,
|
|
||||||
NWMergeNodes,
|
NWMergeNodes,
|
||||||
NWBatchChangeNodes,
|
NWBatchChangeNodes,
|
||||||
NWChangeMixFactor,
|
NWChangeMixFactor,
|
||||||
|
@ -162,7 +162,6 @@ class NWNodeWrangler(bpy.types.AddonPreferences):
|
|||||||
#
|
#
|
||||||
# REGISTER/UNREGISTER CLASSES AND KEYMAP ITEMS
|
# REGISTER/UNREGISTER CLASSES AND KEYMAP ITEMS
|
||||||
#
|
#
|
||||||
switch_category_menus = []
|
|
||||||
addon_keymaps = []
|
addon_keymaps = []
|
||||||
# kmi_defs entry: (identifier, key, action, CTRL, SHIFT, ALT, props, nice name)
|
# kmi_defs entry: (identifier, key, action, CTRL, SHIFT, ALT, props, nice name)
|
||||||
# props entry: (property name, property value)
|
# props entry: (property name, property value)
|
||||||
@ -392,28 +391,8 @@ def register():
|
|||||||
setattr(kmi.properties, prop, value)
|
setattr(kmi.properties, prop, value)
|
||||||
addon_keymaps.append((km, kmi))
|
addon_keymaps.append((km, kmi))
|
||||||
|
|
||||||
# switch submenus
|
|
||||||
switch_category_menus.clear()
|
|
||||||
for cat in node_categories_iter(None):
|
|
||||||
if cat.name not in ['Group', 'Script']:
|
|
||||||
idname = f"NODE_MT_nw_switch_{cat.identifier}_submenu"
|
|
||||||
switch_category_type = type(idname, (bpy.types.Menu,), {
|
|
||||||
"bl_space_type": 'NODE_EDITOR',
|
|
||||||
"bl_label": cat.name,
|
|
||||||
"category": cat,
|
|
||||||
"poll": cat.poll,
|
|
||||||
"draw": interface.draw_switch_category_submenu,
|
|
||||||
})
|
|
||||||
|
|
||||||
switch_category_menus.append(switch_category_type)
|
|
||||||
|
|
||||||
bpy.utils.register_class(switch_category_type)
|
|
||||||
|
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
for cat_types in switch_category_menus:
|
|
||||||
bpy.utils.unregister_class(cat_types)
|
|
||||||
switch_category_menus.clear()
|
|
||||||
|
|
||||||
# keymaps
|
# keymaps
|
||||||
for km, kmi in addon_keymaps:
|
for km, kmi in addon_keymaps:
|
||||||
|
Loading…
Reference in New Issue
Block a user