"""Base Hook for other Houdini renderer node handlers.
These include:
- ``arnold_handler``
- ``ifd_handler``
This hook is designed to inherit from ``base_export_handler`` hook.
"""
import sgtk
import hou
[docs]HookBaseClass = sgtk.get_hook_baseclass()
[docs]class BaseRenderNodeHandler(HookBaseClass):
"""
Base class for all render output nodes.
"""
[docs] SGTK_PASS_NAME = "sgtk_pass_name"
# aovs
[docs] AOV_USE_FILE_TMPL = "{}"
# archives
# templates
[docs] AOV_WORK_TEMPLATE = "aov_work_template"
[docs] AOV_PUBLISH_TEMPLATE = "aov_publish_template"
[docs] ARCHIVE_WORK_TEMPLATE = ""
[docs] ARCHIVE_PUBLISH_TEMPLATE = ""
# strings
[docs] def _get_template_fields_from(self, node, additional_fields=None):
"""
Get shotgun template fields from the given node and update with any additional
fields.
:param node: A :class:`hou.Node` instance.
:param dict additional_fields: Any fields to override with.
:returns: The template fields updated with the additional_fields
"""
output_parm = node.parm(self.OUTPUT_PARM)
file_path = output_parm.unexpandedString()
fields = self.get_work_template(node).validate_and_get_fields(file_path)
if not fields:
mesage = 'Can not extract Shotgun fields from "{}": "{}"'
raise sgtk.TankError(mesage.format(output_parm.path(), file_path))
if "SEQ" in fields:
fields["SEQ"] = "FORMAT: $F"
fields.update(additional_fields or {})
return fields
[docs] def generate_aov_path(self, node, channel, template_name):
"""
Generate the file path for the given aov name.
:param node: A :class:`hou.Node` instance.
:param str channel: The aov name.
:param str template_name: The name of the shotgun template to use.
:returns: The file path for the aov output.
"""
try:
fields = self._get_template_fields_from(node)
template = self._get_template(template_name)
except sgtk.TankError as error:
return str(error)
if channel:
fields["channel"] = channel
try:
file_path = template.apply_fields(fields)
except sgtk.TankError:
return self.AOV_ERROR
return file_path
[docs] def _lock_parms(self, node, lock):
"""
Lock parms on the node is shotgun is enabled.
:param node: A :class:`hou.Node` instance.
:param bool lock: Lock parms if True.
"""
self._lock_aov_parms(node, lock)
archive_output = node.parm(self.ARCHIVE_OUTPUT)
archive_output.lock(lock)
[docs] def _update_aov_paths(self, node):
"""
Update all the aov output paths on the node.
:param node: A :class:`hou.Node` instance.
"""
aov_count = node.parm(self.AOV_COUNT)
count = aov_count.eval() + 1
for index in range(1, count):
aov_name = node.parm(self.AOV_NAME_TMPL.format(index))
sgtk_aov_name = node.parm(self.SGTK_AOV_NAME_TMPL.format(index))
sgtk_aov_name.set(aov_name.unexpandedString())
self._update_aov_path(node, index)
[docs] def update_file_path(self, node, parm_name, template_name, additional_fields=None):
"""
Update the file path for the given parameter.
:param node: A :class:`hou.Node` instance.
:param str parm_name: The name of the parm to update.
:param str template_name: The name of the shotgun template to use.
:param dict additional_fields: Any fields to override the template fields with.
"""
try:
fields = self._get_template_fields_from(
node, additional_fields=additional_fields
)
template = self._get_template(template_name)
file_path = template.apply_fields(fields)
except sgtk.TankError as error:
file_path = str(error)
parm = node.parm(parm_name)
parm.lock(False)
parm.set(file_path)
parm.lock(True)
#############################################################################################
# Overriden methods
#############################################################################################
[docs] def _create_sgtk_parms(self, node):
"""
Create the parameters that are going to live within the sgtk folder.
:param node: A :class:`hou.Node` instance.
:rtype: list(:class:`hou.ParmTemplate`)
"""
templates = super(BaseRenderNodeHandler, self)._create_sgtk_parms(node)
templates[-1].setJoinWithNext(True)
sgtk_pass_name = hou.StringParmTemplate(
self.SGTK_PASS_NAME,
"Render Pass",
1,
default_value=("beauty",),
script_callback=self.generate_callback_script_str(
"validate_parm_and_refresh_path"
),
script_callback_language=hou.scriptLanguage.Python,
)
sgtk_pass_name.setConditional(hou.parmCondType.DisableWhen, "{ use_sgtk != 1 }")
templates.append(sgtk_pass_name)
return templates
[docs] def _update_template_fields(self, node, fields):
"""
Update template fields from the node's parameter values.
:param node: A :class:`hou.Node` instance.
:param dict fields: Template fields.
"""
super(BaseRenderNodeHandler, self)._update_template_fields(node, fields)
sgtk_pass_name = node.parm(self.SGTK_PASS_NAME)
pass_name = sgtk_pass_name.evalAsString().strip()
if pass_name:
fields["identifier"] = pass_name
[docs] def _set_up_parms(self, node):
"""
Set up the given node's parameters.
:param node: A :class:`hou.Node` instance.
"""
super(BaseRenderNodeHandler, self)._set_up_parms(node)
self._lock_parms(node, True)
[docs] def _enable_sgtk(self, node, sgtk_enabled):
"""
Enable/disable the sgtk parameters.
:param node: A :class:`hou.Node` instance.
:param bool sgtk_enabled: The state to set the parameters to.
"""
super(BaseRenderNodeHandler, self)._enable_sgtk(node, sgtk_enabled)
self._lock_parms(node, sgtk_enabled)
if sgtk_enabled:
self._update_aov_paths(node)
[docs] def _refresh_file_path(self, node):
"""
Refresh the file paths generated by the node handler.
:param node: A :class:`hou.Node` instance.
"""
super(BaseRenderNodeHandler, self)._refresh_file_path(node)
aov_count = node.parm(self.AOV_COUNT)
count = aov_count.eval() + 1
for index in range(1, count):
self._update_aov_path(node, index)
self.update_file_path(node, self.ARCHIVE_OUTPUT, self.ARCHIVE_WORK_TEMPLATE)
#############################################################################################
# AOVs
#############################################################################################
[docs] def _update_aov_path(self, node, index):
"""
Update the aov output path for the given index.
:param node: A :class:`hou.Node` instance.
:param int index: The index of the aov parm.
:raises: :class:`FieldInputError` on invalid input.
"""
parm = node.parm(self.SGTK_AOV_NAME_TMPL.format(index))
aov_name = node.parm(self.AOV_NAME_TMPL.format(index))
aov_file_path = node.parm(self.AOV_FILE_TMPL.format(index))
try:
self._validate_parm(parm)
except Exception:
aov_name.set("")
aov_file_path.set(self.AOV_ERROR)
raise
aov = parm.evalAsString()
aov_name.set(parm.unexpandedString())
aov_path = self.generate_aov_path(node, aov, self.AOV_WORK_TEMPLATE)
aov_file_path.lock(False)
aov_file_path.set(aov_path)
aov_file_path.lock(True)
[docs] def update_aov_path(self, kwargs):
"""
Callback to update the aov output path for the given index.
"""
node = kwargs["node"]
index = kwargs["script_multiparm_index"]
self._update_aov_path(node, index)
[docs] def _lock_aov_parms(self, node, lock):
"""
Lock aov output path parms.
:param node: A :class:`hou.Node` instance.
:param bool lock: Lock parms if True.
"""
aov_count = node.parm(self.AOV_COUNT)
count = aov_count.eval() + 1
for index in range(1, count):
aov_file_path = node.parm(self.AOV_FILE_TMPL.format(index))
aov_file_path.lock(lock)
[docs] def lock_aov_parms(self, kwargs):
"""
Callback to lock aov output path parms.
"""
node = kwargs["node"]
self._lock_aov_parms(node, True)
#############################################################################################
# Utilities
#############################################################################################
[docs] def _populate_aov_names(
self, node, parent_parm_name, src_parm_name, dest_parm_name
):
"""
Populate sgtk aov names from the original aov names.
:param node: A :class:`hou.Node` instance.
:param str parent_parm_name: The parent parm name.
:param str src_parm_name: The source parm name.
:param str dest_parm_name: The sgtk parm name.
"""
parent_parm = node.parm(parent_parm_name)
count = parent_parm.eval() + 1
for index in range(1, count):
src_parm = node.parm(src_parm_name.format(index))
channel_name = src_parm.evalAsString()
dest_parm = node.parm(dest_parm_name.format(index))
dest_parm.set(channel_name)
[docs] def _populate_from_fields(self, node, fields):
"""
Populate the node from template fields.
:param node: A :class:`hou.Node` instance.
:param dict fields: The template fields.
"""
super(BaseRenderNodeHandler, self)._populate_from_fields(node, fields)
sgtk_pass_name = node.parm(self.SGTK_PASS_NAME)
sgtk_pass_name.set(fields.get("identifier", ""))
self._populate_aov_names(
node, self.AOV_COUNT, self.AOV_NAME_TMPL, self.SGTK_AOV_NAME_TMPL
)
[docs] def _get_multi_parm_output_paths_and_templates(
self,
node,
count_parm_name,
use_file_parm_tmpl,
file_parm_tmpl,
work_template_name,
publish_template_name,
paths_and_templates,
):
"""
Get the output path and the templates used for the given multi parms.
:param node: A :class:`hou.Node` instance.
:param str count_parm_name: The name of the count parameter to query.
:param str use_file_parm_tmpl: The string template for the use file parm name.
:param str file_parm_tmpl: The string template for the file parm name.
:param work_template: The work :class:`sgtk.Template` for this parm.
:param publish_template: The publish :class:`sgtk.Template` for this parm.
:param list paths_and_templates: The current list of paths and templates
to append to.
"""
aov_work_template = self._get_template(work_template_name)
aov_publish_template = self._get_template(publish_template_name)
aov_count = node.parm(count_parm_name)
count = aov_count.eval() + 1
for index in range(1, count):
aov_enabled = node.parm(use_file_parm_tmpl.format(index))
if aov_enabled.eval():
self._get_output_path_and_templates_for_parm(
node,
file_parm_tmpl.format(index),
aov_work_template,
aov_publish_template,
paths_and_templates,
)
[docs] def _get_output_paths_and_templates(self, node):
"""
Go through the node's specified parameters and get the output paths,
work and publish templates.
Returns a list of dictionaries, each containing, at least:
- work template
- publish template
- file name
and optionally:
- any sequence paths
- whether the output is a deep image
:param node: A :class:`hou.Node` instance.
:rtype: list(dict)
"""
paths_and_templates = super(
BaseRenderNodeHandler, self
)._get_output_paths_and_templates(node)
# get extra image planes
self._get_multi_parm_output_paths_and_templates(
node,
self.AOV_COUNT,
self.AOV_USE_FILE_TMPL,
self.AOV_FILE_TMPL,
self.AOV_WORK_TEMPLATE,
self.AOV_PUBLISH_TEMPLATE,
paths_and_templates,
)
return paths_and_templates