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:
+199
-11
@@ -1,6 +1,9 @@
|
||||
import bpy
|
||||
import os
|
||||
import re
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from .. import fn
|
||||
from bpy.props import (StringProperty,
|
||||
BoolProperty,
|
||||
@@ -120,11 +123,43 @@ class RT_PG_selectable_prop(bpy.types.PropertyGroup):
|
||||
select: BoolProperty(name="Selected", default=True)
|
||||
is_linked: BoolProperty(name="Linked", default=False)
|
||||
# is_valid: BoolProperty(name="Valid", default=True)
|
||||
|
||||
## Specific to render layer nodes
|
||||
scene_name : StringProperty(name="Scene Name", default='')
|
||||
viewlayer_name : StringProperty(name="ViewLayer Name", default='')
|
||||
|
||||
|
||||
## extra output naming options
|
||||
name_from_node: StringProperty(name="Name From Node")
|
||||
name_from_node_and_socket: StringProperty(name="Name From Node And Socket")
|
||||
name_from_node_and_socket_with_scene: StringProperty(name="Name From Node And Socket With Scene prefixed")
|
||||
name_from_node_and_socket_with_scene: StringProperty(name="Name From Node And Socket With Scene prefixed")
|
||||
name_from_template: StringProperty(name="Name From Templates")
|
||||
## Individual ? (not transmitted to assignation function currently, only used to build remap name)
|
||||
# name_from_template: StringProperty(name="Layer Name From Templates")
|
||||
# slot_from_template: StringProperty(name="File Slot From Templates")
|
||||
|
||||
def update_template_file_slot(self, context):
|
||||
for item in context.window_manager.rt_socket_collection:
|
||||
item.name_from_template = fn.build_path_from_template(
|
||||
self.template_file_slot,
|
||||
node_name=item.node_name,
|
||||
node_label=item.node_label,
|
||||
scene_name=item.scene_name,
|
||||
viewlayer_name=item.viewlayer_name,
|
||||
socket_name=item.socket_name,
|
||||
)
|
||||
|
||||
def update_template_multilayer_name(self, context):
|
||||
for item in context.window_manager.rt_socket_collection:
|
||||
item.name_from_template = fn.build_path_from_template(
|
||||
self.template_multilayer_name,
|
||||
node_name=item.node_name,
|
||||
node_label=item.node_label,
|
||||
scene_name=item.scene_name,
|
||||
viewlayer_name=item.viewlayer_name,
|
||||
socket_name=item.socket_name,
|
||||
)
|
||||
|
||||
class RT_OT_create_output_layers(bpy.types.Operator):
|
||||
bl_idname = "rt.create_output_layers"
|
||||
@@ -150,25 +185,21 @@ class RT_OT_create_output_layers(bpy.types.Operator):
|
||||
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',
|
||||
default='socket_name',
|
||||
items=(
|
||||
('node_and_socket_name', 'Node Socket Name', 'Use the node name prefix and socket name', 0),
|
||||
('node_and_socket_name_with_scene', 'Scene + Node Socket Name', 'Use the node name prefix and socket name, prefix scene with render layer nodes', 1),
|
||||
('socket_name', 'Socket Name', 'Use the socket name as output name', 2),
|
||||
('node_name', 'Node Name', 'Use the node name as output name', 3),
|
||||
# ('template', 'From Template', 'Use custom template for naming', 4), # Disable slot template for now
|
||||
)
|
||||
)
|
||||
|
||||
# prefix_with_node_name : BoolProperty(
|
||||
# name='Prefix With Node Name',
|
||||
# description='Add the node name as prefix to the output name',
|
||||
# default=False)
|
||||
|
||||
base_path : StringProperty(
|
||||
name='Custom base path',
|
||||
default='',
|
||||
description='Set the base path of created file_output (not if already exists)')
|
||||
|
||||
|
||||
file_format : EnumProperty(
|
||||
name='Output Format',
|
||||
default='NONE',
|
||||
@@ -209,6 +240,68 @@ class RT_OT_create_output_layers(bpy.types.Operator):
|
||||
),
|
||||
)
|
||||
|
||||
# --- templates
|
||||
|
||||
use_base_path_templates : BoolProperty(
|
||||
name='Use Base Path Template',
|
||||
default=True,
|
||||
description='Use template strings for base path formatting')
|
||||
|
||||
## -- base path
|
||||
template_base_path : StringProperty(
|
||||
name="Base Path Template",
|
||||
description="Template for file output base path\
|
||||
\nFolder containing subsequent filepath",
|
||||
default="//render/{node_name}/",
|
||||
# options={'SKIP_SAVE'}
|
||||
)
|
||||
|
||||
## Multilayer base path : output on file instead of folder
|
||||
template_multilayer_base_path : StringProperty(
|
||||
name="Base Path Template",
|
||||
description="Template for multilayer file output base paths\
|
||||
\nOutput of the file sequence",
|
||||
default="//render/{node_name}/{node_name}_",
|
||||
# options={'SKIP_SAVE'}
|
||||
)
|
||||
|
||||
|
||||
## -- slots/layer_names (UNUSED for now)
|
||||
template_file_slot : StringProperty(
|
||||
name="File Slot Template",
|
||||
description="Template for file output file slots\
|
||||
\nSubpath added to base path for file sequence",
|
||||
default="{socket_name}/{socket_name}_",
|
||||
update=update_template_file_slot,
|
||||
# options={'SKIP_SAVE'}
|
||||
)
|
||||
|
||||
## Multilayer's layer: layer name in EXR instead of subpath to file
|
||||
template_multilayer_name : StringProperty(
|
||||
name="Layer Name Template",
|
||||
description="Template for multilayer file output layer names\
|
||||
\nLayer name in EXR multilayer file",
|
||||
default="{socket_name}",
|
||||
update=update_template_multilayer_name,
|
||||
# options={'SKIP_SAVE'}
|
||||
)
|
||||
|
||||
## extra suffix passes (unused)
|
||||
# template_tech_pass_name : StringProperty(
|
||||
# name="Tech Pass Name Template",
|
||||
# description="Template for tech pass file output layer names\
|
||||
# \nLayer name in EXR multilayer file",
|
||||
# default="tech",
|
||||
# )
|
||||
|
||||
# template_crypto_name : StringProperty(
|
||||
# name="Crypto Pass Name Template",
|
||||
# description="Template for crypto pass file output layer names\
|
||||
# \nLayer name in EXR multilayer file",
|
||||
# default="crypto",
|
||||
# )
|
||||
|
||||
|
||||
def invoke(self, context, event):
|
||||
if not hasattr(context.window_manager, 'rt_socket_collection'):
|
||||
## Create collection prop on window manager if not existing
|
||||
@@ -222,6 +315,13 @@ class RT_OT_create_output_layers(bpy.types.Operator):
|
||||
self.base_path = ''
|
||||
return self.execute(context)
|
||||
|
||||
## Store variable usable in templates
|
||||
# self.blend_name = bpy.data.filepath
|
||||
# self.version = ''
|
||||
# if self.blend_name:
|
||||
# self.blend_name = Path(self.blend_name).stem
|
||||
# self.version = fn.get_rightmost_number_in_string(self.blend_name)
|
||||
|
||||
selected = [n for n in context.scene.node_tree.nodes if n.select and n.type != 'OUTPUT_FILE']
|
||||
if not selected:
|
||||
self.report({'ERROR'}, 'No render layer nodes selected')
|
||||
@@ -244,6 +344,8 @@ class RT_OT_create_output_layers(bpy.types.Operator):
|
||||
|
||||
if n.type == 'R_LAYERS':
|
||||
scene_node_name = f'{n.scene.name}_{n.layer}'
|
||||
item.scene_name = n.scene.name
|
||||
item.viewlayer_name = n.layer
|
||||
|
||||
## 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
|
||||
@@ -256,7 +358,7 @@ class RT_OT_create_output_layers(bpy.types.Operator):
|
||||
item.name_from_node_and_socket = node_name
|
||||
item.name_from_node_and_socket_with_scene = scene_node_name
|
||||
else:
|
||||
print(f'node_name: {node_name} VS {scene_node_name}')
|
||||
# print(f'node_name: {node_name} VS {scene_node_name}')
|
||||
item.name_from_node_and_socket = f'{node_name}_{o.name}'
|
||||
item.name_from_node_and_socket_with_scene = f'{scene_node_name}_{o.name}'
|
||||
|
||||
@@ -265,6 +367,25 @@ class RT_OT_create_output_layers(bpy.types.Operator):
|
||||
item.is_linked = True
|
||||
item.select = False
|
||||
|
||||
## Assign default template values (Disabled for now)
|
||||
## TRIGGER update to firstly fill template slot/names
|
||||
# self.template_file_slot = context.scene.render_toolbox.default_file_slot
|
||||
# self.template_multilayer_name = context.scene.render_toolbox.default_multilayer_name
|
||||
|
||||
## Replace self base path
|
||||
# self.template_base_path = context.scene.render_toolbox.default_base_path
|
||||
# self.template_multilayer_base_path = context.scene.render_toolbox.default_multilayer_base_path
|
||||
|
||||
## For template preview display, Store some infos
|
||||
ref = selected[0]
|
||||
self.node_name = ref.name.strip()
|
||||
self.node_label = ref.label.strip()
|
||||
self.scene_name = ''
|
||||
self.view_layer_name = ''
|
||||
if ref.type == 'R_LAYERS':
|
||||
self.scene_name = ref.scene.name
|
||||
self.view_layer_name = ref.layer
|
||||
self.final_base_path_template = ''
|
||||
return context.window_manager.invoke_props_dialog(self, width=500)
|
||||
|
||||
def draw(self, context):
|
||||
@@ -276,8 +397,68 @@ class RT_OT_create_output_layers(bpy.types.Operator):
|
||||
## Settings
|
||||
if self.show_custom_settings:
|
||||
box.use_property_split = True
|
||||
|
||||
# Template settings
|
||||
col = box.column()
|
||||
row = col.row(align=True)
|
||||
row.prop(self, 'use_base_path_templates')
|
||||
row.operator("rt.info_note", text='', icon='INFO').text = """Format the base path using templates.
|
||||
Possible variables:
|
||||
{node_name} : name of the node
|
||||
{node_label} : label of the node, fallback to node_name when nothing
|
||||
{scene_name} : name of the scene from render layer node
|
||||
{viewlayer_name} : name of the viewlayer from render layer node
|
||||
{blend_name} : stem of the blend
|
||||
{blend_version} : version (rightmost number in blend name)
|
||||
{date} : (YYYYMMDD format)
|
||||
{time} : (HHMMSS format)""" # {socket_name} : name of the socket (only availabe in slots)
|
||||
|
||||
final_format = self.file_format
|
||||
if final_format == 'NONE':
|
||||
final_format = os.environ.get('FILE_FORMAT', 'OPEN_EXR_MULTILAYER')
|
||||
|
||||
if self.use_base_path_templates:
|
||||
|
||||
if final_format == 'OPEN_EXR_MULTILAYER':
|
||||
## self local template
|
||||
# box.prop(self, 'template_multilayer_base_path')
|
||||
# self.final_base_path_template = self.template_multilayer_base_path
|
||||
|
||||
## scene prop
|
||||
box.prop(context.scene.render_toolbox, 'default_multilayer_base_path')
|
||||
self.final_base_path_template = context.scene.render_toolbox.default_multilayer_base_path
|
||||
else:
|
||||
## self local template
|
||||
# box.prop(self, 'template_base_path')
|
||||
# self.final_base_path_template = self.template_base_path
|
||||
|
||||
## scene prop
|
||||
box.prop(context.scene.render_toolbox, 'default_base_path')
|
||||
self.final_base_path_template = context.scene.render_toolbox.default_base_path
|
||||
## display the applied template
|
||||
box.label(text=fn.build_path_from_template(self.final_base_path_template,
|
||||
node_name = self.node_name,
|
||||
node_label=self.node_label,
|
||||
scene_name=self.scene_name,
|
||||
viewlayer_name=self.view_layer_name,
|
||||
),
|
||||
icon='INFO'
|
||||
)
|
||||
|
||||
else:
|
||||
box.prop(self, 'base_path')
|
||||
self.final_base_path_template = None
|
||||
|
||||
if self.name_type == 'template':
|
||||
row_single = box.row()
|
||||
row_single.prop(self, 'template_file_slot')
|
||||
row_single.active = final_format != 'OPEN_EXR_MULTILAYER'
|
||||
row_multi = box.row()
|
||||
row_multi.prop(self, 'template_multilayer_name')
|
||||
row_multi.active = final_format == 'OPEN_EXR_MULTILAYER'
|
||||
|
||||
## Todo: Add rt.info_note to explain templates
|
||||
box.row().prop(self, 'name_type', expand=False)
|
||||
box.prop(self, 'base_path')
|
||||
col = box.column()
|
||||
col.prop(self, 'file_format')
|
||||
col.prop(self, 'exr_codec')
|
||||
@@ -295,6 +476,8 @@ class RT_OT_create_output_layers(bpy.types.Operator):
|
||||
op.target_prop = 'name_from_node_and_socket'
|
||||
elif self.name_type == 'node_and_socket_name_with_scene':
|
||||
op.target_prop = 'name_from_node_and_socket_with_scene'
|
||||
elif self.name_type == 'template':
|
||||
op.target_prop = 'name_from_template'
|
||||
|
||||
## Node Sockets
|
||||
layout.use_property_split = False
|
||||
@@ -339,6 +522,8 @@ class RT_OT_create_output_layers(bpy.types.Operator):
|
||||
row.prop(item, 'name_from_node_and_socket', text='')
|
||||
elif self.name_type == 'node_and_socket_name_with_scene':
|
||||
row.prop(item, 'name_from_node_and_socket_with_scene', text='')
|
||||
elif self.name_type == 'template':
|
||||
row.prop(item, 'name_from_template', text='')
|
||||
|
||||
def execute(self, context):
|
||||
|
||||
@@ -364,7 +549,9 @@ class RT_OT_create_output_layers(bpy.types.Operator):
|
||||
final_name = item.name_from_node_and_socket
|
||||
elif self.name_type == 'node_and_socket_name_with_scene':
|
||||
final_name = item.name_from_node_and_socket_with_scene
|
||||
|
||||
elif self.name_type == 'name_from_template':
|
||||
final_name = item.name_from_template
|
||||
|
||||
if not item.select:
|
||||
# All deselected goes to exclude with {node_name: [socket1, ...]}
|
||||
excludes.setdefault(item.node_name, []).append(item.socket_name)
|
||||
@@ -400,7 +587,8 @@ class RT_OT_create_output_layers(bpy.types.Operator):
|
||||
excludes=excludes,
|
||||
remap_names=remap_names,
|
||||
file_format=file_format,
|
||||
split_tech_passes=self.split_tech_passes)
|
||||
split_tech_passes=self.split_tech_passes,
|
||||
template=self.final_base_path_template)
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
+39
-1
@@ -1,5 +1,6 @@
|
||||
import bpy
|
||||
|
||||
from bpy.types import Operator
|
||||
from bpy.props import (BoolProperty,
|
||||
EnumProperty,
|
||||
PointerProperty,
|
||||
@@ -8,7 +9,7 @@ from bpy.props import (BoolProperty,
|
||||
|
||||
from .. import fn
|
||||
|
||||
class RT_OT_select_object_by_name(bpy.types.Operator):
|
||||
class RT_OT_select_object_by_name(Operator):
|
||||
bl_idname = "rt.select_object_by_name"
|
||||
bl_label = "Select Object By name"
|
||||
bl_description = "Select object and modifier"
|
||||
@@ -41,6 +42,7 @@ class RT_OT_select_object_by_name(bpy.types.Operator):
|
||||
return {'CANCELLED'}
|
||||
|
||||
# Make it the active object
|
||||
## function to make active and selected
|
||||
# fn.show_and_active_object(context, target_object, make_active=True, )
|
||||
context.view_layer.objects.active = target_object
|
||||
|
||||
@@ -64,8 +66,44 @@ class RT_OT_select_object_by_name(bpy.types.Operator):
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class RT_OT_info_note(Operator):
|
||||
bl_idname = "rt.info_note"
|
||||
bl_label = "Info Note"
|
||||
bl_description = "Info Note"
|
||||
bl_options = {"REGISTER", "INTERNAL"}
|
||||
|
||||
text : bpy.props.StringProperty(default='', options={'SKIP_SAVE'})
|
||||
title : bpy.props.StringProperty(default='Help', options={'SKIP_SAVE'})
|
||||
icon : bpy.props.StringProperty(default='INFO', options={'SKIP_SAVE'})
|
||||
|
||||
@classmethod
|
||||
def description(self, context, properties):
|
||||
return properties.text
|
||||
|
||||
def execute(self, context):
|
||||
## Split text in list of lines to display
|
||||
lines = self.text.split('\n')
|
||||
fn.show_message_box(_message=lines, _title=self.title, _icon=self.icon)
|
||||
return {"FINISHED"}
|
||||
|
||||
# Not registered yet
|
||||
class RT_set_env_settings(bpy.types.Operator):
|
||||
"""manually reset environnement settings"""
|
||||
bl_idname = "rt.set_env_settings"
|
||||
bl_label = "Reset Templates From Env"
|
||||
bl_description = "Reset Render Toolbox templates from environment variables"
|
||||
bl_options = {"REGISTER", "UNDO", "INTERNAL"}
|
||||
|
||||
# mode : bpy.props.StringProperty(default='ALL', options={'SKIP_SAVE'}) # 'HIDDEN',
|
||||
|
||||
def execute(self, context):
|
||||
from ..properties import set_env_properties
|
||||
set_env_properties()
|
||||
return {'FINISHED'}
|
||||
|
||||
classes = (
|
||||
RT_OT_select_object_by_name,
|
||||
RT_OT_info_note
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user