Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

130 ay 6965 default output paths and support custom staging directories. #200

Open
wants to merge 19 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
f2e6c68
add `default_output_dir` general setting
MustafaJafar Dec 18, 2024
f00f13e
set `staging_dir` value in `apply_settings`
MustafaJafar Dec 18, 2024
9189c61
Update output paths leveraging `staging_dir`
MustafaJafar Dec 18, 2024
49b3790
Merge branch 'develop' into enhancement/130-ay-6965_rop-default-outpu…
MustafaJafar Jan 7, 2025
0af5133
Add `ROP Output Directory` setting to control setting output paths.
MustafaJafar Jan 8, 2025
4f7ecec
Merge branch 'enhancement/130-ay-6965_rop-default-output-path-update-…
MustafaJafar Jan 8, 2025
d93fe8b
support custom staging directories
MustafaJafar Jan 8, 2025
24183f6
Update `ROPOutputDirModel` doc string - Add custom staging dir settin…
MustafaJafar Jan 8, 2025
35b1c04
refactor variable name to `enable_staging_path_management`
MustafaJafar Jan 9, 2025
2a3bcc9
use the new variable name `enable_staging_path_management`
MustafaJafar Jan 9, 2025
e05a2be
add `context` argument to `get_custom_staging_dir`
MustafaJafar Jan 9, 2025
6e15604
Override `Creator.get_staging_dir` and use it inside all of the creat…
MustafaJafar Jan 9, 2025
285bdee
Get staging dir: Respect the context of the created instance
MustafaJafar Jan 15, 2025
1543271
Staging dir: Use always a forward slash and remove the last character…
MustafaJafar Jan 15, 2025
c6c0003
Merge branch 'develop' into enhancement/130-ay-6965_rop-default-outpu…
MustafaJafar Jan 15, 2025
09643ed
get staging dir: pass cached project_settings
MustafaJafar Jan 15, 2025
08b7958
Remove unused import
MustafaJafar Jan 16, 2025
a4ffbaf
refactor `get_staging_dir` -> `get_custom_staging_dir`
MustafaJafar Jan 16, 2025
3c2fe1a
Update client/ayon_houdini/api/lib.py
BigRoy Jan 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 58 additions & 1 deletion client/ayon_houdini/api/lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import re
import logging
import json
from typing import Optional
import clique
from functools import lru_cache
from contextlib import contextmanager
Expand All @@ -21,6 +22,7 @@
registered_host,
get_current_context,
get_current_host_name,
get_staging_dir_info,
)
from ayon_core.pipeline.create import CreateContext
from ayon_core.pipeline.template_data import get_template_data
Expand Down Expand Up @@ -1623,4 +1625,59 @@ def format_as_collections(files: list[str], pattern: str = "{head}{padding}{tail
collections, remainder = clique.assemble(files)
result = [collection.format(pattern) for collection in collections]
result.extend(remainder)
return result
return result


def get_custom_staging_dir(product_type, product_name, context=None, project_settings=None) -> "Optional[str]":
"""Get Custom Staging Directory

Retrieve a custom staging directory for the specified product type and name
within the specified or current AYON context.

This function is primarily used in creator plugins to obtain the custom
staging directory for the created instance.

Note:
This function is preferred over `ayon_core.pipeline.publish.get_instance_staging_dir`
because the `instance` object in creator plugins doesn't have `context` attribute.

Args:
product_type (str): The type of product.
product_name (str): The name of the product.
context (Optional[dict[str, Union[str, None]]]): A dictionary with AYON context.
it expects keys: `project_name`, `folder_path` and `task_name`
project_settings(Dict[str, Any]): Prepared project settings.

Returns:
Optional[str]: The computed staging directory path.
"""

context = context or get_current_context()
project_name = context["project_name"]
folder_path = context["folder_path"]
task_name = context["task_name"]
host_name = get_current_host_name()

project_entity = ayon_api.get_project(project_name)

folder_entity = ayon_api.get_folder_by_path(project_name, folder_path)
task_entity = ayon_api.get_task_by_name(
project_name, folder_entity["id"], task_name
)

staging_dir_info = get_staging_dir_info(
project_entity,
folder_entity,
task_entity,
product_type,
product_name,
host_name,
always_return_path=False,
project_settings=project_settings
)

staging_dir_path = None
if staging_dir_info:
staging_dir_path = staging_dir_info.directory

BigRoy marked this conversation as resolved.
Show resolved Hide resolved
return staging_dir_path
52 changes: 49 additions & 3 deletions client/ayon_houdini/api/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
)
from ayon_core.lib import BoolDef

from .lib import imprint, read, lsattr, add_self_publish_button, render_rop
from .lib import imprint, read, lsattr, add_self_publish_button, render_rop, get_custom_staging_dir
from .usd import get_ayon_entity_uri_from_representation_context


Expand Down Expand Up @@ -79,7 +79,8 @@ def create_instance_node(
node_name,
parent,
node_type="geometry",
pre_create_data=None
pre_create_data=None,
instance_data=None
):
"""Create node representing instance.

Expand All @@ -89,6 +90,7 @@ def create_instance_node(
parent (str): Name of the parent node.
node_type (str, optional): Type of the node.
pre_create_data (Optional[Dict]): Pre create data.
instance_data (Optional[Dict]): Instance data.

Returns:
hou.Node: Newly created instance node.
Expand All @@ -107,6 +109,8 @@ class HoudiniCreator(Creator, HoudiniCreatorBase):
selected_nodes = []
settings_name = None
add_publish_button = False
staging_dir = "$HIP/ayon"
enable_staging_path_management = True

settings_category = SETTINGS_CATEGORY

Expand All @@ -129,7 +133,8 @@ def create(self, product_name, instance_data, pre_create_data):
product_name,
"/out",
node_type,
pre_create_data
pre_create_data,
instance_data=instance_data
)

self.customize_node_look(instance_node)
Expand Down Expand Up @@ -295,6 +300,9 @@ def apply_settings(self, project_settings):
houdini_general_settings = project_settings["houdini"]["general"]
self.add_publish_button = houdini_general_settings.get(
"add_self_publish_button", False)

self.enable_staging_path_management = houdini_general_settings["rop_output"]["enabled"]
self.staging_dir = houdini_general_settings["rop_output"]["default_output_dir"] or self.staging_dir

# Apply Creator Settings
settings_name = self.settings_name
Expand All @@ -312,6 +320,44 @@ def apply_settings(self, project_settings):
for key, value in settings.items():
setattr(self, key, value)

def get_custom_staging_dir(self, product_type, product_name, instance_data=None):
""" Get Custom Staging Directory

Retrieve a custom staging directory for the specified product type and name
within the current AYON context.

This method falls back to the default output path defined in settings
`ayon+settings://houdini/general/rop_output/default_output_dir`

Note:
This method is preferred over `super().get_staging_dir(instance)`
because it doesn't fit in all creators where
1. Some creators like HDA doesn't access `instance` object
2. Some other creators like Karma render creator should actually
get the staging directory of the `render` product type not `karma_rop`.

One downside is that `version` key is not supported in staging dir templates.

Args:
product_type (str): The type of product.
product_name (str): The name of the product.
instance_data (Optional[dict[str, Union[str, None]]]): A dictionary with instance data.

Returns:
Optional[str]: The computed staging directory path.
"""

context = {
"project_name": self.project_name,
"folder_path": instance_data["folderPath"],
"task_name": instance_data["task"]
}

staging_dir = get_custom_staging_dir(
product_type, product_name, context, self.project_settings
) or self.staging_dir

return staging_dir.replace("\\", "/").rstrip("/")

class HoudiniLoader(load.LoaderPlugin):
"""Base class for Houdini load plugins."""
Expand Down
9 changes: 7 additions & 2 deletions client/ayon_houdini/plugins/create/create_alembic_camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,15 @@ def create(self, product_name, instance_data, pre_create_data):

instance_node = hou.node(instance.get("instance_node"))
parms = {
"filename": hou.text.expandString(
"$HIP/pyblish/{}.abc".format(product_name)),
"use_sop_path": False,
}

if self.enable_staging_path_management:
# keep dynamic link to product name in file path.
staging_dir = self.get_custom_staging_dir(self.product_type, product_name, instance_data)
parms["filename"] = "{root}/`chs('AYON_productName')`/$OS.abc".format(
root=hou.text.expandString(staging_dir)
)

if self.selected_nodes:
if len(self.selected_nodes) > 1:
Expand Down
13 changes: 8 additions & 5 deletions client/ayon_houdini/plugins/create/create_arnold_ass.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,20 @@ def create(self, product_name, instance_data, pre_create_data):
parm_template_group.hideFolder("Properties", True)
instance_node.setParmTemplateGroup(parm_template_group)

filepath = "{}{}".format(
hou.text.expandString("$HIP/pyblish/"),
"{}.$F4{}".format(product_name, self.ext)
)
parms = {
# Render frame range
"trange": 1,
# Arnold ROP settings
"ar_ass_file": filepath,
"ar_ass_export_enable": 1
}

if self.enable_staging_path_management:
# keep dynamic link to product name in file path.
staging_dir = self.get_custom_staging_dir(self.product_type, product_name, instance_data)
parms["ar_ass_file"] = "{root}/`chs('AYON_productName')`/$OS.$F4{ext}".format(
root=hou.text.expandString(staging_dir),
ext=self.ext
)

instance_node.setParms(parms)

Expand Down
32 changes: 15 additions & 17 deletions client/ayon_houdini/plugins/create/create_arnold_rop.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,31 +35,29 @@ def create(self, product_name, instance_data, pre_create_data):

instance_node = hou.node(instance.get("instance_node"))

ext = pre_create_data.get("image_format")

filepath = "{renders_dir}{product_name}/{product_name}.$F4.{ext}".format(
renders_dir=hou.text.expandString("$HIP/pyblish/renders/"),
product_name=product_name,
ext=ext,
)
parms = {
# Render frame range
"trange": 1,

# Arnold ROP settings
"ar_picture": filepath,
# Arnold ROP settings
"ar_exr_half_precision": 1 # half precision
}

if self.enable_staging_path_management:
# keep dynamic link to product name in file path.
staging_dir = self.get_custom_staging_dir("render", product_name, instance_data)

parms["ar_picture"] = "{root}/`chs('AYON_productName')`/$OS.$F4.{ext}".format(
root=hou.text.expandString(staging_dir),
ext=pre_create_data.get("image_format")
)

parms["ar_ass_file"] = "{root}/`chs('AYON_productName')`/ass/$OS.$F4.ass".format(
root=hou.text.expandString(staging_dir)
)

if pre_create_data.get("render_target") == "farm_split":
ass_filepath = \
"{export_dir}{product_name}/{product_name}.$F4.ass".format(
export_dir=hou.text.expandString("$HIP/pyblish/ass/"),
product_name=product_name,
)
parms["ar_ass_export_enable"] = 1
parms["ar_ass_file"] = ass_filepath


instance_node.setParms(parms)

# Lock any parameters in this list
Expand Down
20 changes: 10 additions & 10 deletions client/ayon_houdini/plugins/create/create_bgeo.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,16 @@ def create(self, product_name, instance_data, pre_create_data):

instance_node = hou.node(instance.get("instance_node"))

file_path = "{}{}".format(
hou.text.expandString("$HIP/pyblish/"),
"{}.$F4.{}".format(
product_name,
pre_create_data.get("bgeo_type") or "bgeo.sc")
)
parms = {
"sopoutput": file_path
}

parms = {}

if self.enable_staging_path_management:
# keep dynamic link to product name in file path.
staging_dir = self.get_custom_staging_dir(self.product_type, product_name, instance_data)
parms["sopoutput"] = "{root}/`chs('AYON_productName')`/$OS.$F4.{ext}".format(
root=hou.text.expandString(staging_dir),
ext=pre_create_data.get("bgeo_type") or "bgeo.sc"
)
instance_node.parm("trange").set(1)
if self.selected_nodes:
# if selection is on SOP level, use it
Expand Down
12 changes: 7 additions & 5 deletions client/ayon_houdini/plugins/create/create_composite.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,16 @@ def create(self, product_name, instance_data, pre_create_data):
pre_create_data)

instance_node = hou.node(instance.get("instance_node"))
filepath = "{}{}".format(
hou.text.expandString("$HIP/pyblish/"),
"{}.$F4{}".format(product_name, self.ext)
)
parms = {
"trange": 1,
"copoutput": filepath
}
if self.enable_staging_path_management:
# keep dynamic link to product name in file path.
staging_dir = self.get_custom_staging_dir(self.product_type, product_name, instance_data)
parms["copoutput"] = "{root}/`chs('AYON_productName')`/$OS.$F4{ext}".format(
root=hou.text.expandString(staging_dir),
ext=self.ext
)

if self.selected_nodes:
if len(self.selected_nodes) > 1:
Expand Down
10 changes: 8 additions & 2 deletions client/ayon_houdini/plugins/create/create_hda.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,8 @@ def create_instance_node(
node_name,
parent,
node_type="geometry",
pre_create_data=None
pre_create_data=None,
instance_data=None
):
if pre_create_data is None:
pre_create_data = {}
Expand Down Expand Up @@ -240,10 +241,15 @@ def create_instance_node(
)
)

hda_file_name = None
if self.enable_staging_path_management:
staging_dir = self.get_custom_staging_dir(self.product_type, node_name, instance_data)
hda_file_name = "{}/HDAs/{}.hda".format(staging_dir, node_name)

hda_node = to_hda.createDigitalAsset(
name=type_name,
description=node_name,
hda_file_name="$HIP/{}.hda".format(node_name),
hda_file_name=hda_file_name,
ignore_external_references=True,
min_num_inputs=0,
max_num_inputs=len(to_hda.inputs()) or 1,
Expand Down
Loading
Loading