add template in create output path base node
templates for slot are in code but disabled, not useful in the end
This commit is contained in:
@@ -4,6 +4,7 @@ import re
|
||||
import json
|
||||
|
||||
from .constant import TECH_PASS_KEYWORDS
|
||||
from pathlib import Path
|
||||
|
||||
# region Manage nodes
|
||||
|
||||
@@ -62,9 +63,11 @@ def recursive_node_connect_check(l, target_node):
|
||||
return False
|
||||
|
||||
|
||||
# region create and connect file output
|
||||
|
||||
def create_and_connect_file_output(node, outputs, file_out, out_name, base_path, out_base,
|
||||
scene, remap_names, file_format, suffix='', color_depth=None,
|
||||
location_offset=(500, 50)):
|
||||
location_offset=(500, 50), base_path_template=None, use_slot_template=False):
|
||||
"""Helper function to create file output node and connect it to outputs
|
||||
|
||||
Args:
|
||||
@@ -112,13 +115,31 @@ def create_and_connect_file_output(node, outputs, file_out, out_name, base_path,
|
||||
if node.parent:
|
||||
fo.parent = node.parent
|
||||
|
||||
if base_path:
|
||||
fo.base_path = base_path
|
||||
if base_path_template is not None:
|
||||
fo.base_path = build_path_from_template(base_path_template,
|
||||
node_name=node.name.strip(),
|
||||
node_label=node.label.strip(),
|
||||
scene_name=node.scene.name if node.type == 'R_LAYERS' else None,
|
||||
viewlayer_name=node.layer if node.type == 'R_LAYERS' else None,
|
||||
)
|
||||
if suffix:
|
||||
if fo.format.file_format == 'OPEN_EXR_MULTILAYER':
|
||||
## insert suffix right after last slash as folder
|
||||
path_list = re.split('(\\/)', fo.base_path)
|
||||
path_list.insert(-1, suffix)
|
||||
fo.base_path = ''.join(path_list)
|
||||
else:
|
||||
## Ensure slash separator then add suffix
|
||||
fo.base_path = fo.base_path.rstrip('\\/') + '/' + suffix
|
||||
else:
|
||||
if fo.format.file_format == 'OPEN_EXR_MULTILAYER':
|
||||
fo.base_path = f'//render/{out_base}/{suffix}{out_base}_'
|
||||
# default behavior without template
|
||||
if base_path:
|
||||
fo.base_path = base_path
|
||||
else:
|
||||
fo.base_path = f'//render/{out_base}/{suffix}'
|
||||
if fo.format.file_format == 'OPEN_EXR_MULTILAYER':
|
||||
fo.base_path = f'//render/{out_base}/{suffix}{out_base}_'
|
||||
else:
|
||||
fo.base_path = f'//render/{out_base}/{suffix}'
|
||||
|
||||
for o in outputs:
|
||||
if next((l for l in o.links if recursive_node_connect_check(l, fo)), None):
|
||||
@@ -134,9 +155,11 @@ def create_and_connect_file_output(node, outputs, file_out, out_name, base_path,
|
||||
|
||||
ls = fo.layer_slots[-1]
|
||||
ls.name = slot_name
|
||||
|
||||
fs = fo.file_slots[-1]
|
||||
fs.path = f'{slot_name}/{slot_name}_'
|
||||
if use_slot_template:
|
||||
## Always False (currently never use slot template)
|
||||
fs.path = slot_name
|
||||
|
||||
out_input = fo.inputs[-1]
|
||||
links.new(o, out_input)
|
||||
@@ -147,7 +170,7 @@ def create_and_connect_file_output(node, outputs, file_out, out_name, base_path,
|
||||
|
||||
|
||||
def connect_to_file_output(node_list, file_out=None, base_path='', excludes=None, remap_names=None,
|
||||
file_format=None, split_tech_passes=False):
|
||||
file_format=None, split_tech_passes=False, template=None):
|
||||
"""Connect selected nodes output to file output(s)
|
||||
if a file output is selected, add intputs on it
|
||||
|
||||
@@ -234,7 +257,8 @@ def connect_to_file_output(node_list, file_out=None, base_path='', excludes=None
|
||||
fo_regular = create_and_connect_file_output(
|
||||
node, regular_outputs, file_out, out_name, base_path,
|
||||
out_base, scene, remap_names, file_format,
|
||||
location_offset=(500, y_offset)
|
||||
location_offset=(500, y_offset),
|
||||
base_path_template=template
|
||||
)
|
||||
if fo_regular and fo_regular not in created_nodes:
|
||||
created_nodes.append(fo_regular)
|
||||
@@ -249,7 +273,8 @@ def connect_to_file_output(node_list, file_out=None, base_path='', excludes=None
|
||||
node, tech_outputs, None, out_name, base_path,
|
||||
out_base, scene, remap_names, tech_file_format,
|
||||
suffix='tech/', color_depth='32',
|
||||
location_offset=(500, y_offset)
|
||||
location_offset=(500, y_offset),
|
||||
base_path_template=template
|
||||
)
|
||||
|
||||
if fo_tech and fo_tech not in created_nodes:
|
||||
@@ -266,17 +291,19 @@ def connect_to_file_output(node_list, file_out=None, base_path='', excludes=None
|
||||
node, crypto_outputs, None, out_name, base_path,
|
||||
out_base, scene, remap_names, tech_file_format,
|
||||
suffix='cryptos/', color_depth='32',
|
||||
location_offset=(500, y_offset)
|
||||
location_offset=(500, y_offset),
|
||||
base_path_template=template
|
||||
)
|
||||
|
||||
if fo_crypto and fo_crypto not in created_nodes:
|
||||
created_nodes.append(fo_crypto)
|
||||
|
||||
|
||||
return created_nodes
|
||||
|
||||
# endregion
|
||||
|
||||
# endregion
|
||||
|
||||
|
||||
# region Utilities
|
||||
|
||||
@@ -365,3 +392,98 @@ def show_message_box(_message = "", _title = "Message Box", _icon = 'INFO'):
|
||||
if isinstance(_message, str):
|
||||
_message = [_message]
|
||||
bpy.context.window_manager.popup_menu(draw, title = _title, icon = _icon)
|
||||
|
||||
|
||||
def get_rightmost_number_in_string(string) -> str:
|
||||
"""Get the rightmost number passed string
|
||||
return the number as string, empty string if no number found
|
||||
"""
|
||||
res = re.search(r'(\d+)(?!.*\d)', string)
|
||||
if not res:
|
||||
return ''
|
||||
return res.group(1)
|
||||
|
||||
|
||||
def build_path_from_template(template: str,
|
||||
node_name: str=None,
|
||||
node_label: str=None,
|
||||
scene_name: str=None,
|
||||
viewlayer_name: str=None,
|
||||
socket_name: str=None,
|
||||
socket_index: str=None,
|
||||
) -> str:
|
||||
"""Get a name from a template string
|
||||
Possible keyword:
|
||||
{node_name} : name of the node
|
||||
{node_label} : label of the node (fallback to node_name if not set)
|
||||
{scene_name} : name of the source render layer node scene
|
||||
{viewlayer_name} : name of the source render layer viewlayer
|
||||
{socket_name} : name of source the node socket
|
||||
{socket_index} : index of the node in the node tree (010, 020, etc.)
|
||||
|
||||
## Info from Blend
|
||||
{blend_name} : name of the blend file
|
||||
{blend_version} : version of the blend file
|
||||
{date} : current date (YYYYMMDD format)
|
||||
{time} : current time (HHMMSS format)
|
||||
"""
|
||||
|
||||
## note: socket index is a bad idea because the final order is not decided before execute
|
||||
if not template:
|
||||
return ''
|
||||
|
||||
# Get blend file info
|
||||
blend_path = bpy.data.filepath
|
||||
blend_name = ''
|
||||
blend_version = ''
|
||||
|
||||
if blend_path:
|
||||
blend_name = Path(blend_path).stem
|
||||
blend_version = get_rightmost_number_in_string(blend_name)
|
||||
|
||||
# Get current date and time
|
||||
import datetime
|
||||
now = datetime.datetime.now()
|
||||
current_date = now.strftime("%Y%m%d")
|
||||
current_time = now.strftime("%H%M%S")
|
||||
|
||||
# Handle node_label to name fallback
|
||||
if "{node_label}" in template:
|
||||
if node_label is None or not node_label:
|
||||
node_label = node_name
|
||||
|
||||
if socket_index:
|
||||
# Ensure socket_index is formatted as a zero-padded string and increment on base 10 (allow to insert inbetween renders)
|
||||
socket_index = f"{int(socket_index)*10:03d}"
|
||||
|
||||
def clean_name(name):
|
||||
# Clean names to be file-system safe
|
||||
if name is None:
|
||||
return ''
|
||||
return bpy.path.clean_name(name)
|
||||
|
||||
# Create a dictionary of all available variables
|
||||
template_vars = {
|
||||
'node_name': clean_name(node_name),
|
||||
'node_label': clean_name(node_label),
|
||||
'socket_name': clean_name(socket_name),
|
||||
'scene_name': clean_name(scene_name),
|
||||
'viewlayer_name': clean_name(viewlayer_name),
|
||||
'socket_index': socket_index or '',
|
||||
'blend_name': clean_name(blend_name),
|
||||
'blend_version': blend_version,
|
||||
'date': current_date,
|
||||
'time': current_time,
|
||||
}
|
||||
|
||||
# Apply template using format_map to handle missing keys gracefully
|
||||
try:
|
||||
applied_template = template.format_map(template_vars)
|
||||
except KeyError as e:
|
||||
# If a key is missing, return template with error message
|
||||
# print(f"Template error: Unknown variable {e}")
|
||||
return f"Template error: Unknown {e}"
|
||||
|
||||
return applied_template
|
||||
|
||||
# endregion
|
||||
Reference in New Issue
Block a user