Newer
Older
for node in selected:
for input in node.inputs:
if input.links:
src = input.links[0].from_node
node.label = src.label
break
elif option == 'FROM_SOCKET':
selected = [n for n in nodes if n.select]
for node in selected:
for input in node.inputs:
if input.links:
src = input.links[0].from_socket
node.label = src.name
break
return {'FINISHED'}
class NWClearLabel(Operator, NWBase):
bl_idname = "node.nw_clear_label"
bl_label = "Clear Label"
bl_options = {'REGISTER', 'UNDO'}
option = BoolProperty()
def execute(self, context):
nodes, links = get_nodes_links(context)
for node in [n for n in nodes if n.select]:
node.label = ''
return {'FINISHED'}
def invoke(self, context, event):
if self.option:
return self.execute(context)
else:
return context.window_manager.invoke_confirm(self, event)
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
class NWModifyLabels(Operator, NWBase):
"""Modify Labels of all selected nodes."""
bl_idname = "node.nw_modify_labels"
bl_label = "Modify Labels"
bl_options = {'REGISTER', 'UNDO'}
prepend = StringProperty(
name="Add to Beginning"
)
append = StringProperty(
name="Add to End"
)
replace_from = StringProperty(
name="Text to Replace"
)
replace_to = StringProperty(
name="Replace with"
)
def execute(self, context):
nodes, links = get_nodes_links(context)
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
return {'FINISHED'}
def invoke(self, context, event):
self.prepend = ""
self.append = ""
self.remove = ""
return context.window_manager.invoke_props_dialog(self)
class NWAddTextureSetup(Operator, NWBase):
bl_idname = "node.nw_add_texture"
bl_label = "Texture Setup"
bl_description = "Add Texture Node Setup to Selected Shaders"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
space = context.space_data
valid = False
if space.type == 'NODE_EDITOR':
if space.tree_type == 'ShaderNodeTree' and space.node_tree is not None:
valid = True
return valid
def execute(self, context):
nodes, links = get_nodes_links(context)
active = nodes.active
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]
valid = False
if active:
if active.select:
if active.type in shader_types or active.type in texture_types:
if not active.inputs[0].is_linked:
valid = True
if valid:
locx = active.location.x
locy = active.location.y
xoffset = [500.0, 700.0]
isshader = True
if active.type not in shader_types:
xoffset = [290.0, 500.0]
isshader = False
coordout = 2
image_type = 'ShaderNodeTexImage'
if (active.type in texture_types and active.type != 'TEX_IMAGE') or (active.type == 'BACKGROUND'):
coordout = 0 # image texture uses UVs, procedural textures and Background shader use Generated
if active.type == 'BACKGROUND':
image_type = 'ShaderNodeTexEnvironment'
if isshader:
tex = nodes.new(image_type)
tex.location = [locx - 200.0, locy + 28.0]
map = nodes.new('ShaderNodeMapping')
map.location = [locx - xoffset[0], locy + 80.0]
map.width = 240
coord = nodes.new('ShaderNodeTexCoord')
coord.location = [locx - xoffset[1], locy + 40.0]
active.select = False
if isshader:
nodes.active = tex
links.new(tex.outputs[0], active.inputs[0])
links.new(map.outputs[0], tex.inputs[0])
links.new(coord.outputs[coordout], map.inputs[0])
else:
nodes.active = map
links.new(map.outputs[0], active.inputs[0])
links.new(coord.outputs[coordout], map.inputs[0])
return {'FINISHED'}
class NWAddReroutes(Operator, NWBase):
"""Add Reroute Nodes and link them to outputs of selected nodes"""
bl_idname = "node.nw_add_reroutes"
bl_label = "Add Reroutes"
bl_description = "Add Reroutes to Outputs"
bl_options = {'REGISTER', 'UNDO'}
option = EnumProperty(
name="option",
items=[
('ALL', 'to all', 'Add to all outputs'),
('LOOSE', 'to loose', 'Add only to loose outputs'),
('LINKED', 'to linked', 'Add only to linked outputs'),
]
)
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
def execute(self, context):
tree_type = context.space_data.node_tree.type
option = self.option
nodes, links = get_nodes_links(context)
# output valid when option is 'all' or when 'loose' output has no links
valid = False
post_select = [] # nodes to be selected after execution
# create reroutes and recreate links
for node in [n for n in nodes if n.select]:
if node.outputs:
x = node.location.x
y = node.location.y
width = node.width
# unhide 'REROUTE' nodes to avoid issues with location.y
if node.type == 'REROUTE':
node.hide = False
# When node is hidden - width_hidden not usable.
# Hack needed to calculate real width
if node.hide:
bpy.ops.node.select_all(action='DESELECT')
helper = nodes.new('NodeReroute')
helper.select = True
node.select = True
# resize node and helper to zero. Then check locations to calculate width
bpy.ops.transform.resize(value=(0.0, 0.0, 0.0))
width = 2.0 * (helper.location.x - node.location.x)
# restore node location
node.location = x, y
# delete helper
node.select = False
# only helper is selected now
bpy.ops.node.delete()
x = node.location.x + width + 20.0
if node.type != 'REROUTE':
y -= 35.0
y_offset = -22.0
loc = x, y
reroutes_count = 0 # will be used when aligning reroutes added to hidden nodes
for out_i, output in enumerate(node.outputs):
pass_used = False # initial value to be analyzed if 'R_LAYERS'
# if node is not 'R_LAYERS' - "pass_used" not needed, so set it to True
if node.type != 'R_LAYERS':
pass_used = True
else: # if 'R_LAYERS' check if output represent used render pass
node_scene = node.scene
node_layer = node.layer
# If output - "Alpha" is analyzed - assume it's used. Not represented in passes.
if output.name == 'Alpha':
pass_used = True
else:
# check entries in global 'rl_outputs' variable
for render_pass, out_name, exr_name, in_internal, in_cycles in rl_outputs:
if output.name == out_name:
pass_used = getattr(node_scene.render.layers[node_layer], render_pass)
break
if pass_used:
valid = ((option == 'ALL') or
(option == 'LOOSE' and not output.links) or
(option == 'LINKED' and output.links))
# Add reroutes only if valid, but offset location in all cases.
if valid:
n = nodes.new('NodeReroute')
nodes.active = n
for link in output.links:
links.new(n.outputs[0], link.to_socket)
links.new(output, n.inputs[0])
n.location = loc
post_select.append(n)
reroutes_count += 1
y += y_offset
loc = x, y
# disselect the node so that after execution of script only newly created nodes are selected
node.select = False
# nicer reroutes distribution along y when node.hide
if node.hide:
y_translate = reroutes_count * y_offset / 2.0 - y_offset - 35.0
for reroute in [r for r in nodes if r.select]:
reroute.location.y -= y_translate
for node in post_select:
node.select = True
return {'FINISHED'}
class NWLinkActiveToSelected(Operator, NWBase):
"""Link active node to selected nodes basing on various criteria"""
bl_idname = "node.nw_link_active_to_selected"
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
bl_label = "Link Active Node to Selected"
bl_options = {'REGISTER', 'UNDO'}
replace = BoolProperty()
use_node_name = BoolProperty()
use_outputs_names = BoolProperty()
@classmethod
def poll(cls, context):
space = context.space_data
valid = False
if space.type == 'NODE_EDITOR':
if space.node_tree is not None and context.active_node is not None:
if context.active_node.select:
valid = True
return valid
def execute(self, context):
nodes, links = get_nodes_links(context)
replace = self.replace
use_node_name = self.use_node_name
use_outputs_names = self.use_outputs_names
active = nodes.active
selected = [node for node in nodes if node.select and node != active]
outputs = [] # Only usable outputs of active nodes will be stored here.
for out in active.outputs:
if active.type != 'R_LAYERS':
outputs.append(out)
else:
# 'R_LAYERS' node type needs special handling.
# outputs of 'R_LAYERS' are callable even if not seen in UI.
# Only outputs that represent used passes should be taken into account
# Check if pass represented by output is used.
# global 'rl_outputs' list will be used for that
for render_pass, out_name, exr_name, in_internal, in_cycles in rl_outputs:
pass_used = False # initial value. Will be set to True if pass is used
if out.name == 'Alpha':
# Alpha output is always present. Doesn't have representation in render pass. Assume it's used.
pass_used = True
elif out.name == out_name:
# example 'render_pass' entry: 'use_pass_uv' Check if True in scene render layers
pass_used = getattr(active.scene.render.layers[active.layer], render_pass)
break
if pass_used:
outputs.append(out)
doit = True # Will be changed to False when links successfully added to previous output.
for out in outputs:
if doit:
for node in selected:
dst_name = node.name # Will be compared with src_name if needed.
# When node has label - use it as dst_name
if node.label:
dst_name = node.label
valid = True # Initial value. Will be changed to False if names don't match.
src_name = dst_name # If names not used - this asignment will keep valid = True.
if use_node_name:
# Set src_name to source node name or label
src_name = active.name
if active.label:
src_name = active.label
elif use_outputs_names:
src_name = (out.name, )
for render_pass, out_name, exr_name, in_internal, in_cycles in rl_outputs:
if out.name in {out_name, exr_name}:
src_name = (out_name, exr_name)
if dst_name not in src_name:
valid = False
if valid:
for input in node.inputs:
if input.type == out.type or node.type == 'REROUTE':
if replace or not input.is_linked:
links.new(out, input)
if not use_node_name and not use_outputs_names:
doit = False
break
return {'FINISHED'}
class NWAlignNodes(Operator, NWBase):
bl_idname = "node.nw_align_nodes"
bl_label = "Align nodes"
bl_options = {'REGISTER', 'UNDO'}
# option: 'Vertically', 'Horizontally'
option = EnumProperty(
name="option",
description="Direction",
items=(
('AXIS_X', "Align Vertically", 'Align Vertically'),
('AXIS_Y', "Aligh Horizontally", 'Aligh Horizontally'),
)
)
def execute(self, context):
nodes, links = get_nodes_links(context)
selected = [] # entry = [index, loc.x, loc.y, width, height]
frames_reselect = [] # entry = frame node. will be used to reselect all selected frames
active = nodes.active
for i, node in enumerate(nodes):
total_w = 0.0 # total width of all nodes. Will be calculated later.
total_h = 0.0 # total height of all nodes. Will be calculated later
if node.select:
if node.type == 'FRAME':
node.select = False
frames_reselect.append(i)
else:
locx = node.location.x
locy = node.location.y
width = node.dimensions[0]
height = node.dimensions[1]
total_w += width # add nodes[i] width to total width of all nodes
total_h += height # add nodes[i] height to total height of all nodes
# calculate relative locations
parent = node.parent
while parent is not None:
locx += parent.location.x
locy += parent.location.y
parent = parent.parent
selected.append([i, locx, locy, width, height])
count = len(selected)
if count > 1: # aligning makes sense only if at least 2 nodes are selected
selected_sorted_x = sorted(selected, key=lambda k: (k[1], -k[2]))
selected_sorted_y = sorted(selected, key=lambda k: (-k[2], k[1]))
min_x = selected_sorted_x[0][1] # min loc.x
min_x_loc_y = selected_sorted_x[0][2] # loc y of node with min loc x
min_x_w = selected_sorted_x[0][3] # width of node with max loc x
max_x = selected_sorted_x[count - 1][1] # max loc.x
max_x_loc_y = selected_sorted_x[count - 1][2] # loc y of node with max loc.x
max_x_w = selected_sorted_x[count - 1][3] # width of node with max loc.x
min_y = selected_sorted_y[0][2] # min loc.y
min_y_loc_x = selected_sorted_y[0][1] # loc.x of node with min loc.y
min_y_h = selected_sorted_y[0][4] # height of node with min loc.y
min_y_w = selected_sorted_y[0][3] # width of node with min loc.y
max_y = selected_sorted_y[count - 1][2] # max loc.y
max_y_loc_x = selected_sorted_y[count - 1][1] # loc x of node with max loc.y
max_y_w = selected_sorted_y[count - 1][3] # width of node with max loc.y
max_y_h = selected_sorted_y[count - 1][4] # height of node with max loc.y
if self.option == 'AXIS_Y': # Horizontally. Equivelent of s -> x -> 0 with even spacing.
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
loc_x = min_x
#loc_y = (max_x_loc_y + min_x_loc_y) / 2.0
loc_y = (max_y - max_y_h / 2.0 + min_y - min_y_h / 2.0) / 2.0
offset_x = (max_x - min_x - total_w + max_x_w) / (count - 1)
for i, x, y, w, h in selected_sorted_x:
nodes[i].location.x = loc_x
nodes[i].location.y = loc_y + h / 2.0
parent = nodes[i].parent
while parent is not None:
nodes[i].location.x -= parent.location.x
nodes[i].location.y -= parent.location.y
parent = parent.parent
loc_x += offset_x + w
else: # if self.option == 'AXIS_Y'
loc_x = (max_x + max_x_w / 2.0 + min_x + min_x_w / 2.0) / 2.0
loc_y = min_y
offset_y = (max_y - min_y + total_h - min_y_h) / (count - 1)
for i, x, y, w, h in selected_sorted_y:
nodes[i].location.x = loc_x - w / 2.0
nodes[i].location.y = loc_y
parent = nodes[i].parent
while parent is not None:
nodes[i].location.x -= parent.location.x
nodes[i].location.y -= parent.location.y
parent = parent.parent
loc_y += offset_y - h
# reselect selected frames
for i in frames_reselect:
nodes[i].select = True
# restore active node
nodes.active = active
return {'FINISHED'}
class NWSelectParentChildren(Operator, NWBase):
bl_idname = "node.nw_select_parent_child"
bl_label = "Select Parent or Children"
bl_options = {'REGISTER', 'UNDO'}
option = EnumProperty(
name="option",
items=(
('PARENT', 'Select Parent', 'Select Parent Frame'),
('CHILD', 'Select Children', 'Select members of selected frame'),
)
)
def execute(self, context):
nodes, links = get_nodes_links(context)
option = self.option
selected = [node for node in nodes if node.select]
if option == 'PARENT':
for sel in selected:
parent = sel.parent
if parent:
parent.select = True
else: # option == 'CHILD'
for sel in selected:
children = [node for node in nodes if node.parent == sel]
for kid in children:
kid.select = True
return {'FINISHED'}
class NWDetachOutputs(Operator, NWBase):
"""Detach outputs of selected node leaving inluts liked"""
bl_idname = "node.nw_detach_outputs"
def execute(self, context):
nodes, links = get_nodes_links(context)
selected = context.selected_nodes
bpy.ops.node.duplicate_move_keep_inputs()
new_nodes = context.selected_nodes
bpy.ops.node.select_all(action="DESELECT")
for node in selected:
node.select = True
bpy.ops.node.delete_reconnect()
for new_node in new_nodes:
new_node.select = True
Bartek Skorupa
committed
bpy.ops.transform.translate('INVOKE_DEFAULT')
Bartek Skorupa
committed
class NWLinkToOutputNode(Operator, NWBase):
"""Link to Composite node or Material Output node"""
bl_idname = "node.nw_link_out"
bl_label = "Connect to Output"
Bartek Skorupa
committed
space = context.space_data
return (space.type == 'NODE_EDITOR' and space.node_tree is not None and context.active_node is not None)
def execute(self, context):
nodes, links = get_nodes_links(context)
active = nodes.active
output_node = None
tree_type = context.space_data.tree_type
output_types_shaders = [x[1] for x in shaders_output_nodes_props]
output_types_compo = ['COMPOSITE']
output_types = output_types_shaders + output_types_compo
if node.type in output_types:
output_node = node
break
if not output_node:
bpy.ops.node.select_all(action="DESELECT")
if tree_type == 'ShaderNodeTree':
output_node = nodes.new('ShaderNodeOutputMaterial')
elif tree_type == 'CompositorNodeTree':
output_node = nodes.new('CompositorNodeComposite')
output_node.location.x = active.location.x + active.dimensions.x + 80
output_node.location.y = active.location.y
if (output_node and active.outputs):
output_index = 0
for i, output in enumerate(active.outputs):
if output.type == output_node.inputs[0].type:
output_index = i
break
out_input_index = 0
if tree_type == 'ShaderNodeTree':
if active.outputs[output_index].type != 'SHADER': # connect to displacement if not a shader
out_input_index = 2
links.new(active.outputs[output_index], output_node.inputs[out_input_index])
hack_force_update(context, nodes) # viewport render does not update
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
class NWMakeLink(Operator, NWBase):
"""Make a link from one socket to another"""
bl_idname = 'node.nw_make_link'
bl_label = 'Make Link'
bl_options = {'REGISTER', 'UNDO'}
from_socket = IntProperty()
to_socket = IntProperty()
@classmethod
def poll(cls, context):
snode = context.space_data
return (snode.type == 'NODE_EDITOR' and snode.node_tree is not None)
def execute(self, context):
nodes, links = get_nodes_links(context)
n1 = nodes[context.scene.NWLazySource]
n2 = nodes[context.scene.NWLazyTarget]
links.new(n1.outputs[self.from_socket], n2.inputs[self.to_socket])
hack_force_update(context, nodes)
return {'FINISHED'}
class NWCallInputsMenu(Operator, NWBase):
"""Link from this output"""
bl_idname = 'node.nw_call_inputs_menu'
bl_label = 'Make Link'
bl_options = {'REGISTER', 'UNDO'}
from_socket = IntProperty()
@classmethod
def poll(cls, context):
snode = context.space_data
return (snode.type == 'NODE_EDITOR' and snode.node_tree is not None)
def execute(self, context):
nodes, links = get_nodes_links(context)
context.scene.NWSourceSocket = self.from_socket
n1 = nodes[context.scene.NWLazySource]
n2 = nodes[context.scene.NWLazyTarget]
if len(n2.inputs) > 1:
bpy.ops.wm.call_menu("INVOKE_DEFAULT", name=NWConnectionListInputs.bl_idname)
elif len(n2.inputs) == 1:
links.new(n1.outputs[self.from_socket], n2.inputs[0])
return {'FINISHED'}
#
# P A N E L
#
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
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()
col = layout.column(align=True)
col.menu(NWSwitchNodeTypeMenu.bl_idname, text="Switch Node Type")
col.separator()
if tree_type == 'ShaderNodeTree':
col = layout.column(align=True)
col.operator(NWAddTextureSetup.bl_idname, text="Add Texture Setup", icon='NODE_SEL')
col.separator()
col = layout.column(align=True)
col.operator(NWDetachOutputs.bl_idname, icon='UNLINKED')
col.operator(NWSwapOutputs.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')
col.operator(NWLinkToOutputNode.bl_idname, icon='DRIVER')
col.separator()
col = layout.column(align=True)
if mode == 'panel':
row = col.row(align=True)
row.operator(NWClearLabel.bl_idname).option = True
row.operator(NWModifyLabels.bl_idname)
else:
col.operator(NWClearLabel.bl_idname).option = True
col.operator(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(NWResetBG.bl_idname, icon='ZOOM_PREVIOUS')
col.operator(NWReloadImages.bl_idname, icon='FILE_REFRESH')
col.separator()
col = layout.column(align=True)
col.operator(NWFrameSelected.bl_idname, icon='STICKY_UVS_LOC')
col.separator()
col = layout.column(align=True)
col.operator(NWDeleteUnused.bl_idname, icon='CANCEL')
col.separator()
class NodeWranglerPanel(Panel, NWBase):
bl_idname = "NODE_PT_nw_node_wrangler"
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'UI'
bl_label = "Node Wrangler"
prepend = StringProperty(
name='prepend',
)
append = StringProperty()
remove = StringProperty()
def draw(self, context):
self.layout.label(text="(Quick access: Ctrl+Space)")
drawlayout(context, self.layout, mode='panel')
#
# M E N U S
#
class NodeWranglerMenu(Menu, NWBase):
bl_idname = "NODE_MT_nw_node_wrangler_menu"
bl_label = "Node Wrangler"
def draw(self, context):
drawlayout(context, self.layout)
class NWMergeNodesMenu(Menu, NWBase):
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")
layout.menu(NWMergeMixMenu.bl_idname, text="Use Mix Nodes")
layout.menu(NWMergeMathMenu.bl_idname, text="Use Math Nodes")
class NWMergeShadersMenu(Menu, NWBase):
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'):
props = layout.operator(NWMergeNodes.bl_idname, text=type)
props.mode = type
props.merge_type = 'SHADER'
class NWMergeMixMenu(Menu, NWBase):
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(NWMergeNodes.bl_idname, text=name)
props.mode = type
props.merge_type = 'MIX'
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
class NWConnectionListOutputs(Menu, NWBase):
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]
if n1.type == "R_LAYERS":
index=0
for o in n1.outputs:
if o.enabled: # Check which passes the render layer has enabled
layout.operator(NWCallInputsMenu.bl_idname, text=o.name, icon="RADIOBUT_OFF").from_socket=index
index+=1
else:
index=0
for o in n1.outputs:
layout.operator(NWCallInputsMenu.bl_idname, text=o.name, icon="RADIOBUT_OFF").from_socket=index
index+=1
class NWConnectionListInputs(Menu, NWBase):
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]
#print (self.from_socket)
index = 0
for i in n2.inputs:
op = layout.operator(NWMakeLink.bl_idname, text=i.name, icon="FORWARD")
op.from_socket = context.scene.NWSourceSocket
op.to_socket = index
index+=1
class NWMergeMathMenu(Menu, NWBase):
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(NWMergeNodes.bl_idname, text=name)
props.mode = type
props.merge_type = 'MATH'
class NWBatchChangeNodesMenu(Menu, NWBase):
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, NWBase):
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(NWBatchChangeNodes.bl_idname, text=name)
props.blend_type = type
props.operation = 'CURRENT'
class NWBatchChangeOperationMenu(Menu, NWBase):
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(NWBatchChangeNodes.bl_idname, text=name)
props.blend_type = 'CURRENT'
props.operation = type
class NWCopyToSelectedMenu(Menu, NWBase):
bl_idname = "NODE_MT_nw_copy_node_properties_menu"
bl_label = "Copy to Selected"
def draw(self, context):
layout = self.layout
layout.operator(NWCopySettings.bl_idname, text="Settings from Active")
layout.menu(NWCopyLabelMenu.bl_idname)
class NWCopyLabelMenu(Menu, NWBase):
bl_idname = "NODE_MT_nw_copy_label_menu"
bl_label = "Copy Label"
def draw(self, context):
layout = self.layout
layout.operator(NWCopyLabel.bl_idname, text="from Active Node's Label").option = 'FROM_ACTIVE'
layout.operator(NWCopyLabel.bl_idname, text="from Linked Node's Label").option = 'FROM_NODE'
layout.operator(NWCopyLabel.bl_idname, text="from Linked Output's Name").option = 'FROM_SOCKET'
class NWAddReroutesMenu(Menu, NWBase):
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(NWAddReroutes.bl_idname, text="to All Outputs").option = 'ALL'
layout.operator(NWAddReroutes.bl_idname, text="to Loose Outputs").option = 'LOOSE'
layout.operator(NWAddReroutes.bl_idname, text="to Linked Outputs").option = 'LINKED'
class NWLinkActiveToSelectedMenu(Menu, NWBase):
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, NWBase):
bl_idname = "NODE_MT_nw_link_standard_menu"
bl_label = "To All Selected"
def draw(self, context):
layout = self.layout
props = layout.operator(NWLinkActiveToSelected.bl_idname, text="Don't Replace Links")
props.replace = False
props.use_node_name = False
props.use_outputs_names = False
props = layout.operator(NWLinkActiveToSelected.bl_idname, text="Replace Links")
props.replace = True
props.use_node_name = False
props.use_outputs_names = False
class NWLinkUseNodeNameMenu(Menu, NWBase):
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(NWLinkActiveToSelected.bl_idname, text="Don't Replace Links")
props.replace = False
props.use_node_name = True
props.use_outputs_names = False
props = layout.operator(NWLinkActiveToSelected.bl_idname, text="Replace Links")
props.replace = True
props.use_node_name = True
props.use_outputs_names = False
class NWLinkUseOutputsNamesMenu(Menu, NWBase):
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(NWLinkActiveToSelected.bl_idname, text="Don't Replace Links")
props.replace = False
props.use_node_name = False
props.use_outputs_names = True
props = layout.operator(NWLinkActiveToSelected.bl_idname, text="Replace Links")
props.replace = True
props.use_node_name = False
props.use_outputs_names = True
class NWNodeAlignMenu(Menu, NWBase):
bl_idname = "NODE_MT_nw_node_align_menu"
bl_label = "Align Nodes"
def draw(self, context):
layout = self.layout
layout.operator(NWAlignNodes.bl_idname, text="Horizontally").option = 'AXIS_X'
layout.operator(NWAlignNodes.bl_idname, text="Vertically").option = 'AXIS_Y'
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
# TODO, add to toolbar panel
class NWUVMenu(bpy.types.Menu):
bl_idname = "NODE_MT_nw_node_uvs_menu"
bl_label = "UV Maps"
@classmethod
def poll(cls, context):
if context.area.spaces[0].node_tree:
if context.area.spaces[0].node_tree.type == 'SHADER':
return True
else:
return False
else:
return False
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)
uvs = []
for obj in objs:
if obj.data.uv_layers:
for uv in obj.data.uv_layers:
uvs.append(uv.name)
uvs = list(set(uvs)) # get a unique list
if uvs:
for uv in uvs:
l.operator(NWAddAttrNode.bl_idname, text=uv).attr_name = uv
else:
l.label("No UV layers on objects with this material")
class NWVertColMenu(bpy.types.Menu):
bl_idname = "NODE_MT_nw_node_vertex_color_menu"
bl_label = "Vertex Colors"
@classmethod
def poll(cls, context):
if context.area.spaces[0].node_tree:
if context.area.spaces[0].node_tree.type == 'SHADER':
return True
else:
return False
else:
return False
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)
vcols = []
for obj in objs:
if obj.data.vertex_colors:
for vcol in obj.data.vertex_colors:
vcols.append(vcol.name)
vcols = list(set(vcols)) # get a unique list
if vcols:
for vcol in vcols:
l.operator(NWAddAttrNode.bl_idname, text=vcol).attr_name = vcol
else:
l.label("No Vertex Color layers on objects with this material")
class NWSwitchNodeTypeMenu(Menu, NWBase):
bl_idname = "NODE_MT_nw_switch_node_type_menu"
bl_label = "Switch Type to..."
def draw(self, context):
layout = self.layout
tree = context.space_data.node_tree
if tree.type == 'SHADER':
layout.menu(NWSwitchShadersInputSubmenu.bl_idname)
layout.menu(NWSwitchShadersOutputSubmenu.bl_idname)
layout.menu(NWSwitchShadersShaderSubmenu.bl_idname)
layout.menu(NWSwitchShadersTextureSubmenu.bl_idname)
layout.menu(NWSwitchShadersColorSubmenu.bl_idname)
layout.menu(NWSwitchShadersVectorSubmenu.bl_idname)
layout.menu(NWSwitchShadersConverterSubmenu.bl_idname)
layout.menu(NWSwitchShadersLayoutSubmenu.bl_idname)
if tree.type == 'COMPOSITING':
layout.menu(NWSwitchCompoInputSubmenu.bl_idname)
layout.menu(NWSwitchCompoOutputSubmenu.bl_idname)
layout.menu(NWSwitchCompoColorSubmenu.bl_idname)
layout.menu(NWSwitchCompoConverterSubmenu.bl_idname)
layout.menu(NWSwitchCompoFilterSubmenu.bl_idname)
layout.menu(NWSwitchCompoVectorSubmenu.bl_idname)
layout.menu(NWSwitchCompoMatteSubmenu.bl_idname)
layout.menu(NWSwitchCompoDistortSubmenu.bl_idname)
layout.menu(NWSwitchCompoLayoutSubmenu.bl_idname)
class NWSwitchShadersInputSubmenu(Menu, NWBase):
bl_idname = "NODE_MT_nw_switch_shaders_input_submenu"
bl_label = "Input"
def draw(self, context):
layout = self.layout