Improve connect to file output feature with better name besed on source socket and node
1.8.13 1.8.13 - changed: improve `connect to fileoutput` feature: - possible to choose `node_name + socket name` or `node name` instead of `socket name` only - default is now `node_name_socket_name` - added some exception behavior: - if a node has single output : write the node name only - render layers use `scene_viewlayer` as node_name intead of "Render Layer" - interface shows source node label (with source node name in parenthesis) - interface shows render layer scene/viewlayer name (with name in parenthesis)main
parent
09a47413c3
commit
35a99ff6cd
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -14,6 +14,19 @@ Activate / deactivate layer opacity according to prefix
|
||||||
Activate / deactivate all masks using MA layers
|
Activate / deactivate all masks using MA layers
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
1.8.13
|
||||||
|
|
||||||
|
- changed: improve `connect to fileoutput` feature:
|
||||||
|
- possible to choose `node_name + socket name` or `node name` instead of `socket name` only
|
||||||
|
- default is now `node_name_socket_name`
|
||||||
|
- added some exception behavior:
|
||||||
|
- if a node has single output : write the node name only
|
||||||
|
- render layers use `scene_viewlayer` as node_name intead of "Render Layer"
|
||||||
|
- interface shows source node label (with source node name in parenthesis)
|
||||||
|
- interface shows render layer scene/viewlayer name (with name in parenthesis)
|
||||||
|
|
||||||
|
- fixed: a potential name overlapping bug in connect to fileoutput
|
||||||
|
|
||||||
1.8.12
|
1.8.12
|
||||||
|
|
||||||
- changed: Use GP_RENDER_FILE_FORMAT env var to set file output nodes
|
- changed: Use GP_RENDER_FILE_FORMAT env var to set file output nodes
|
||||||
|
|
|
@ -239,12 +239,17 @@ class GPEXP_OT_reset_render_settings(bpy.types.Operator):
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
class GPEXP_PG_selectable_prop(bpy.types.PropertyGroup):
|
class GPEXP_PG_selectable_prop(bpy.types.PropertyGroup):
|
||||||
node_name: bpy.props.StringProperty(name="Object Name")
|
node_name: bpy.props.StringProperty(name="Node Name")
|
||||||
|
node_label: bpy.props.StringProperty(name="Node Label")
|
||||||
name: bpy.props.StringProperty(name="Name or Path")
|
name: bpy.props.StringProperty(name="Name or Path")
|
||||||
socket_name: bpy.props.StringProperty(name="Source socket Name") # Source socket name as reference
|
socket_name: bpy.props.StringProperty(name="Source socket Name") # Source socket name as reference
|
||||||
select: bpy.props.BoolProperty(name="Selected", default=True)
|
select: bpy.props.BoolProperty(name="Selected", default=True)
|
||||||
is_linked: bpy.props.BoolProperty(name="Linked", default=False)
|
is_linked: bpy.props.BoolProperty(name="Linked", default=False)
|
||||||
# is_valid: bpy.props.BoolProperty(name="Valid", default=True)
|
# is_valid: bpy.props.BoolProperty(name="Valid", default=True)
|
||||||
|
|
||||||
|
## extra output naming options
|
||||||
|
name_from_node: bpy.props.StringProperty(name="Name From Node")
|
||||||
|
name_from_node_and_socket: bpy.props.StringProperty(name="Name From Node And Socket")
|
||||||
|
|
||||||
class GPEXP_OT_connect_selected_to_file_out(bpy.types.Operator):
|
class GPEXP_OT_connect_selected_to_file_out(bpy.types.Operator):
|
||||||
bl_idname = "gp.connect_selected_to_file_out"
|
bl_idname = "gp.connect_selected_to_file_out"
|
||||||
|
@ -259,6 +264,24 @@ class GPEXP_OT_connect_selected_to_file_out(bpy.types.Operator):
|
||||||
name='Settings',
|
name='Settings',
|
||||||
default=False)
|
default=False)
|
||||||
|
|
||||||
|
## enum choice for naming: socket_name, node_name, node_and_socket_name,
|
||||||
|
name_type : bpy.props.EnumProperty(
|
||||||
|
name='Output Name From',
|
||||||
|
description='Choose the output name\
|
||||||
|
\nNode name use Label (Use node name when there is no Label)',
|
||||||
|
default='node_and_socket_name',
|
||||||
|
items=(
|
||||||
|
('node_and_socket_name', 'Node_Socket Name', 'Use the node name prefix and socket name', 0),
|
||||||
|
('socket_name', 'Socket Name', 'Use the socket name as output name', 1),
|
||||||
|
('node_name', 'Node Name', 'Use the node name as output name', 2),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# prefix_with_node_name : bpy.props.BoolProperty(
|
||||||
|
# name='Prefix With Node Name',
|
||||||
|
# description='Add the node name as prefix to the output name',
|
||||||
|
# default=False)
|
||||||
|
|
||||||
base_path : bpy.props.StringProperty(
|
base_path : bpy.props.StringProperty(
|
||||||
name='Custom base path',
|
name='Custom base path',
|
||||||
default='',
|
default='',
|
||||||
|
@ -291,7 +314,6 @@ class GPEXP_OT_connect_selected_to_file_out(bpy.types.Operator):
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
color_depth : bpy.props.EnumProperty(
|
color_depth : bpy.props.EnumProperty(
|
||||||
name='Color Depth',
|
name='Color Depth',
|
||||||
default='16',
|
default='16',
|
||||||
|
@ -322,7 +344,27 @@ class GPEXP_OT_connect_selected_to_file_out(bpy.types.Operator):
|
||||||
continue
|
continue
|
||||||
item = self.socket_collection.add()
|
item = self.socket_collection.add()
|
||||||
item.node_name = n.name
|
item.node_name = n.name
|
||||||
item.socket_name = item.name = o.name
|
item.node_label = n.label.strip()
|
||||||
|
item.socket_name = o.name
|
||||||
|
|
||||||
|
## Set editable names
|
||||||
|
item.name = o.name
|
||||||
|
|
||||||
|
## Store other naming options (cleaned at exec with bpy.path.clean_name)
|
||||||
|
node_name = n.label.strip() if n.label.strip() else n.name
|
||||||
|
|
||||||
|
## Change node_name for render layers: scene_viewlayer_name
|
||||||
|
if n.type == 'R_LAYERS' and node_name != n.label: # skip if a label is set
|
||||||
|
node_name = f'{n.scene.name}_{n.layer}'
|
||||||
|
|
||||||
|
item.name_from_node = node_name
|
||||||
|
|
||||||
|
if len(n.outputs) == 1:
|
||||||
|
## Only one output, just pick node name, no need to add socket name
|
||||||
|
item.name_from_node_and_socket = node_name
|
||||||
|
else:
|
||||||
|
item.name_from_node_and_socket = f'{node_name}_{o.name}'
|
||||||
|
|
||||||
## TODO: rename item.name according to tamplate pairs in preferences (to add later)
|
## TODO: rename item.name according to tamplate pairs in preferences (to add later)
|
||||||
if o.is_linked:
|
if o.is_linked:
|
||||||
item.is_linked = True
|
item.is_linked = True
|
||||||
|
@ -332,13 +374,15 @@ class GPEXP_OT_connect_selected_to_file_out(bpy.types.Operator):
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
|
box = layout.box()
|
||||||
expand_icon = 'DISCLOSURE_TRI_DOWN' if self.show_custom_settings else 'DISCLOSURE_TRI_RIGHT'
|
expand_icon = 'DISCLOSURE_TRI_DOWN' if self.show_custom_settings else 'DISCLOSURE_TRI_RIGHT'
|
||||||
layout.prop(self, 'show_custom_settings', emboss=False, icon=expand_icon)
|
box.prop(self, 'show_custom_settings', emboss=False, icon=expand_icon)
|
||||||
## Settings
|
## Settings
|
||||||
if self.show_custom_settings:
|
if self.show_custom_settings:
|
||||||
layout.use_property_split = True
|
box.use_property_split = True
|
||||||
layout.prop(self, 'base_path')
|
box.row().prop(self, 'name_type', expand=False)
|
||||||
col = layout.column()
|
box.prop(self, 'base_path')
|
||||||
|
col = box.column()
|
||||||
col.prop(self, 'file_format')
|
col.prop(self, 'file_format')
|
||||||
col.prop(self, 'exr_codec')
|
col.prop(self, 'exr_codec')
|
||||||
col.row().prop(self, 'color_depth', expand=True)
|
col.row().prop(self, 'color_depth', expand=True)
|
||||||
|
@ -351,7 +395,16 @@ class GPEXP_OT_connect_selected_to_file_out(bpy.types.Operator):
|
||||||
if item.node_name != current_node_name:
|
if item.node_name != current_node_name:
|
||||||
current_node_name = item.node_name
|
current_node_name = item.node_name
|
||||||
col.separator()
|
col.separator()
|
||||||
col.label(text=item.node_name, icon='NODE_SEL')
|
## Display node label + name or name only
|
||||||
|
if item.node_label:
|
||||||
|
display_node_name = f'{item.node_label} ({item.node_name})'
|
||||||
|
else:
|
||||||
|
display_node_name = item.node_name
|
||||||
|
## A bit dirty: For render layer node, show render layer-node name.
|
||||||
|
if display_node_name.startswith('Render Layer'):
|
||||||
|
rln = context.scene.node_tree.nodes.get(item.node_name)
|
||||||
|
display_node_name = f'{rln.scene.name}/{rln.layer} ({display_node_name})'
|
||||||
|
col.label(text=display_node_name, icon='NODE_SEL')
|
||||||
|
|
||||||
row = col.row()
|
row = col.row()
|
||||||
if item.is_linked:
|
if item.is_linked:
|
||||||
|
@ -362,23 +415,46 @@ class GPEXP_OT_connect_selected_to_file_out(bpy.types.Operator):
|
||||||
|
|
||||||
display_name = item.socket_name
|
display_name = item.socket_name
|
||||||
if 'crypto' in display_name.lower():
|
if 'crypto' in display_name.lower():
|
||||||
display_name = f'{display_name} (-> separate 32bit output node)'
|
display_name = f'{display_name} -> 32bit output node'
|
||||||
row.label(text=display_name)
|
row.label(text=display_name)
|
||||||
row.label(text='', icon='RIGHTARROW')
|
row.label(text='', icon='RIGHTARROW')
|
||||||
row.prop(item, 'name', text='')
|
|
||||||
|
if self.name_type == 'socket_name':
|
||||||
|
row.prop(item, 'name', text='')
|
||||||
|
elif self.name_type == 'node_name':
|
||||||
|
row.prop(item, 'name_from_node', text='')
|
||||||
|
elif self.name_type == 'node_and_socket_name':
|
||||||
|
row.prop(item, 'name_from_node_and_socket', text='')
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
|
|
||||||
# Build exclude dict from selection
|
# Build exclude dict from selection
|
||||||
excludes = {}
|
excludes = {}
|
||||||
remap_names = {}
|
remap_names = {}
|
||||||
|
## Old system
|
||||||
|
# if len(self.socket_collection):
|
||||||
|
# for item in self.socket_collection:
|
||||||
|
# if not item.select:
|
||||||
|
# # All deselected goes to exclude with {node_name: [socket1, ...]}
|
||||||
|
# excludes.setdefault(item.node_name, []).append(item.name)
|
||||||
|
# elif item.socket_name != item.name:
|
||||||
|
# remap_names[item.socket_name] = item.name
|
||||||
|
|
||||||
if len(self.socket_collection):
|
if len(self.socket_collection):
|
||||||
for item in self.socket_collection:
|
for item in self.socket_collection:
|
||||||
|
final_name = item.name
|
||||||
|
## change name if other options were used
|
||||||
|
if self.name_type == 'node_name':
|
||||||
|
final_name = item.name_from_node
|
||||||
|
elif self.name_type == 'node_and_socket_name':
|
||||||
|
final_name = item.name_from_node_and_socket
|
||||||
|
|
||||||
if not item.select:
|
if not item.select:
|
||||||
# All deselected goes to exclude with {node_name: [socket1, ...]}
|
# All deselected goes to exclude with {node_name: [socket1, ...]}
|
||||||
excludes.setdefault(item.node_name, []).append(item.name)
|
excludes.setdefault(item.node_name, []).append(item.socket_name)
|
||||||
elif item.socket_name != item.name:
|
elif item.socket_name != final_name:
|
||||||
remap_names[item.socket_name] = item.name
|
remap_names.setdefault(item.node_name, {})[item.socket_name] = final_name
|
||||||
|
# remap_names[item.socket_name] = final_name
|
||||||
|
|
||||||
## Handle default file format
|
## Handle default file format
|
||||||
file_ext = self.file_format
|
file_ext = self.file_format
|
||||||
|
|
|
@ -2,7 +2,7 @@ bl_info = {
|
||||||
"name": "GP Render",
|
"name": "GP Render",
|
||||||
"description": "Organise export of gp layers through compositor output",
|
"description": "Organise export of gp layers through compositor output",
|
||||||
"author": "Samuel Bernou",
|
"author": "Samuel Bernou",
|
||||||
"version": (1, 8, 12),
|
"version": (1, 8, 13),
|
||||||
"blender": (3, 0, 0),
|
"blender": (3, 0, 0),
|
||||||
"location": "View3D",
|
"location": "View3D",
|
||||||
"warning": "",
|
"warning": "",
|
||||||
|
|
8
fn.py
8
fn.py
|
@ -1906,6 +1906,9 @@ def connect_to_file_output(node_list, file_out=None, base_path='', excludes=None
|
||||||
|
|
||||||
excludes (dict, optionnal): List of output names to exclude {node_name: [outputs,]}.
|
excludes (dict, optionnal): List of output names to exclude {node_name: [outputs,]}.
|
||||||
Defaults toNone.
|
Defaults toNone.
|
||||||
|
|
||||||
|
remap_names (dict, optionnal): List of output names to remap {node_name: {output_name: new_name}}.
|
||||||
|
|
||||||
frame (bpy.types.CompositorNode, optional): If given, create nodes into a frame.
|
frame (bpy.types.CompositorNode, optional): If given, create nodes into a frame.
|
||||||
Defaults to None.
|
Defaults to None.
|
||||||
|
|
||||||
|
@ -1969,7 +1972,7 @@ def connect_to_file_output(node_list, file_out=None, base_path='', excludes=None
|
||||||
if next((l for l in o.links if recursive_node_connect_check(l, fo)), None):
|
if next((l for l in o.links if recursive_node_connect_check(l, fo)), None):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if remap_names and (custom_name := remap_names.get(o.name)):
|
if (socket_remaps := remap_names.get(node.name)) and (custom_name := socket_remaps.get(o.name)):
|
||||||
slot_name = bpy.path.clean_name(custom_name) # clean name ?
|
slot_name = bpy.path.clean_name(custom_name) # clean name ?
|
||||||
else:
|
else:
|
||||||
slot_name = bpy.path.clean_name(o.name)
|
slot_name = bpy.path.clean_name(o.name)
|
||||||
|
@ -2032,7 +2035,8 @@ def connect_to_file_output(node_list, file_out=None, base_path='', excludes=None
|
||||||
if next((l for l in o.links if recursive_node_connect_check(l, fo)), None):
|
if next((l for l in o.links if recursive_node_connect_check(l, fo)), None):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if remap_names and (custom_name := remap_names.get(o.name)):
|
# if remap_names and (custom_name := remap_names.get(o.name)):
|
||||||
|
if (socket_remaps := remap_names.get(node.name)) and (custom_name := socket_remaps.get(o.name)):
|
||||||
slot_name = bpy.path.clean_name(custom_name) # clean name ?
|
slot_name = bpy.path.clean_name(custom_name) # clean name ?
|
||||||
else:
|
else:
|
||||||
slot_name = bpy.path.clean_name(o.name) # directly use name in multi layer exr
|
slot_name = bpy.path.clean_name(o.name) # directly use name in multi layer exr
|
||||||
|
|
Loading…
Reference in New Issue