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

Load USD loaders with filepath from entity URI #26

Merged
merged 17 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
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
26 changes: 19 additions & 7 deletions client/ayon_houdini/api/hda_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from ayon_core.style import load_stylesheet

from ayon_houdini.api import lib
from .usd import get_ayon_entity_uri_from_representation_context

from qtpy import QtCore, QtWidgets, QtGui
import hou
Expand Down Expand Up @@ -179,11 +180,19 @@ def set_representation(node, representation_id: str):

context = get_representation_context(project_name, repre_entity)
update_info(node, context)
path = get_representation_path_from_context(context)
# Load fails on UNC paths with backslashes and also
# fails to resolve @sourcename var with backslashed
# paths correctly. So we force forward slashes
path = path.replace("\\", "/")

if node.parm("use_ayon_entity_uri"):
use_ayon_entity_uri = node.evalParm("use_ayon_entity_uri")
else:
use_ayon_entity_uri = False
if use_ayon_entity_uri:
path = get_ayon_entity_uri_from_representation_context(context)
else:
path = get_representation_path_from_context(context)
# Load fails on UNC paths with backslashes and also
# fails to resolve @sourcename var with backslashed
# paths correctly. So we force forward slashes
path = path.replace("\\", "/")
with _unlocked_parm(file_parm):
file_parm.set(path)

Expand Down Expand Up @@ -255,14 +264,17 @@ def on_representation_id_changed(node):
set_representation(node, repre_id)


def on_representation_parms_changed(node):
def on_representation_parms_changed(node, force=False):
"""
Usually used as callback to the project, folder, product, version and
representation parms which on change - would result in a different
representation id to be resolved.

Args:
node (hou.Node): Node to update.
force (Optional[bool]): Whether to force the callback to retrigger
even if the representation id already matches. For example, when
needing to resolve the filepath in a different way.
"""
project_name = node.evalParm("project_name") or get_current_project_name()
representation_id = get_representation_id(
Expand All @@ -278,7 +290,7 @@ def on_representation_parms_changed(node):
else:
representation_id = str(representation_id)

if node.evalParm("representation") != representation_id:
if force or node.evalParm("representation") != representation_id:
node.parm("representation").set(representation_id)
node.parm("representation").pressButton() # trigger callback

Expand Down
8 changes: 8 additions & 0 deletions client/ayon_houdini/api/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from ayon_core.lib import BoolDef

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


SETTINGS_CATEGORY = "houdini"
Expand Down Expand Up @@ -316,6 +317,13 @@ class HoudiniLoader(load.LoaderPlugin):

hosts = ["houdini"]
settings_category = SETTINGS_CATEGORY
use_ayon_entity_uri = False

def filepath_from_context(cls, context):
if cls.use_ayon_entity_uri:
return get_ayon_entity_uri_from_representation_context(context)

return super(HoudiniLoader, cls).filepath_from_context(context)


class HoudiniInstancePlugin(pyblish.api.InstancePlugin):
Expand Down
40 changes: 40 additions & 0 deletions client/ayon_houdini/api/usd.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from typing import List

import hou
import ayon_api
from pxr import Usd, Sdf, Tf, Vt, UsdRender

log = logging.getLogger(__name__)
Expand Down Expand Up @@ -377,3 +378,42 @@ def get_schema_type_names(type_name: str) -> List[str]:
results.append(schema_type_name)

return results


def get_ayon_entity_uri_from_representation_context(context: dict) -> str:
"""Resolve AYON Entity URI from representation context.

Note:
The representation context is the `get_representation_context` dict
containing the `project`, `folder, `representation` and so forth.
It is not the representation entity `context` key.

Arguments:
context (dict): The representation context.

Raises:
RuntimeError: Unable to resolve to a single valid URI.

Returns:
str: The AYON entity URI.

"""
project_name = context["project"]["name"]
representation_id = context["representation"]["id"]
response = ayon_api.post(
f"projects/{project_name}/uris",
entityType="representation",
ids=[representation_id])
if response.status_code != 200:
raise RuntimeError(
f"Unable to resolve AYON entity URI for '{project_name}' "
f"representation id '{representation_id}': {response.text}"
)
uris = response.data["uris"]
if len(uris) != 1:
raise RuntimeError(
f"Unable to resolve AYON entity URI for '{project_name}' "
f"representation id '{representation_id}' to single URI. "
f"Received data: {response.data}"
)
return uris[0]["uri"]
8 changes: 4 additions & 4 deletions client/ayon_houdini/plugins/load/load_usd_layer.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from ayon_core.pipeline import (
get_representation_path,
AVALON_CONTAINER_ID,
)
from ayon_houdini.api import (
Expand All @@ -22,6 +21,8 @@ class USDSublayerLoader(plugin.HoudiniLoader):
icon = "code-fork"
color = "orange"

use_ayon_entity_uri = False

def load(self, context, name=None, namespace=None, data=None):

import os
Expand Down Expand Up @@ -60,18 +61,17 @@ def load(self, context, name=None, namespace=None, data=None):
return container

def update(self, container, context):
repre_entity = context["representation"]
node = container["node"]

# Update the file path
file_path = get_representation_path(repre_entity)
file_path = self.filepath_from_context(context)
file_path = file_path.replace("\\", "/")

# Update attributes
node.setParms(
{
"filepath1": file_path,
"representation": repre_entity["id"],
"representation": context["representation"]["id"],
}
)

Expand Down
8 changes: 4 additions & 4 deletions client/ayon_houdini/plugins/load/load_usd_reference.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from ayon_core.pipeline import (
get_representation_path,
AVALON_CONTAINER_ID,
)
from ayon_houdini.api import (
Expand All @@ -22,6 +21,8 @@ class USDReferenceLoader(plugin.HoudiniLoader):
icon = "code-fork"
color = "orange"

use_ayon_entity_uri = False

def load(self, context, name=None, namespace=None, data=None):

import os
Expand Down Expand Up @@ -60,18 +61,17 @@ def load(self, context, name=None, namespace=None, data=None):
return container

def update(self, container, context):
repre_entity = context["representation"]
node = container["node"]

# Update the file path
file_path = get_representation_path(repre_entity)
file_path = self.filepath_from_context(context)
file_path = file_path.replace("\\", "/")

# Update attributes
node.setParms(
{
"filepath1": file_path,
"representation": repre_entity["id"],
"representation": context["representation"]["id"],
}
)

Expand Down
2 changes: 2 additions & 0 deletions client/ayon_houdini/plugins/load/load_usd_sop.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ class SopUsdImportLoader(plugin.HoudiniLoader):
icon = "code-fork"
color = "orange"

use_ayon_entity_uri = False

def load(self, context, name=None, namespace=None, data=None):
import hou

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,15 @@
default { "" }
parmtag { "script_callback_language" "python" }
}
parm {
name "use_ayon_entity_uri"
label "Use AYON Entity URI"
help "When enabled, loads the filepath using the AYON Entity URI instead of the resolved filepath."
type toggle
default { "0" }
parmtag { "script_callback" "hou.phm().on_representation_parms_changed(kwargs['node'], force=True)" }
parmtag { "script_callback_language" "python" }
}
parm {
name "primpath1"
label "Primitive Root"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,11 @@ hda_module = node.hdaModule()
hda_module.setup_flag_changed_callback(node)

node.parm("file").lock(True)

# Get attribute defaults from settings
# TODO: Clean this up and re-use more from HDA utils lib
from ayon_core.settings import get_current_project_settings
settings = get_current_project_settings()
load_settings = settings["houdini"].get("load", {}).get("LOPLoadAssetLoader", {})
use_ayon_entity_uri = load_settings.get("use_ayon_entity_uri", False)
node.parm("use_ayon_entity_uri").set(use_ayon_entity_uri)
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,15 @@
default { "" }
parmtag { "script_callback_language" "python" }
}
parm {
name "use_ayon_entity_uri"
label "Use AYON Entity URI"
help "When enabled, loads the filepath using the AYON Entity URI instead of the resolved filepath."
type toggle
default { "0" }
parmtag { "script_callback" "hou.phm().on_representation_parms_changed(kwargs['node'], force=True)" }
parmtag { "script_callback_language" "python" }
}
groupcollapsible {
name "extra_options"
label "Load Options"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,11 @@ hda_module = node.hdaModule()
hda_module.setup_flag_changed_callback(node)

node.parm("file").lock(True)

# Get attribute defaults from settings
# TODO: Clean this up and re-use more from HDA utils lib
from ayon_core.settings import get_current_project_settings
settings = get_current_project_settings()
load_settings = settings["houdini"].get("load", {}).get("LOPLoadShotLoader", {})
use_ayon_entity_uri = load_settings.get("use_ayon_entity_uri", False)
node.parm("use_ayon_entity_uri").set(use_ayon_entity_uri)
32 changes: 32 additions & 0 deletions server/settings/load.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from ayon_server.settings import BaseSettingsModel, SettingsField


# Load Plugins
class LoadUseAYONEntityURIModel(BaseSettingsModel):
use_ayon_entity_uri: bool = SettingsField(
False,
title="Use AYON Entity URI",
description=(
"Use the AYON Entity URI on load instead of the resolved filepath "
"so that the AYON USD Resolver will resovle the paths at runtime. "
"This should be enabled when using the AYON USD Resolver."
)
)


class LoadPluginsModel(BaseSettingsModel):
LOPLoadAssetLoader: LoadUseAYONEntityURIModel = SettingsField(
default_factory=LoadUseAYONEntityURIModel,
title="LOP Load Asset")
LOPLoadShotLoader: LoadUseAYONEntityURIModel = SettingsField(
default_factory=LoadUseAYONEntityURIModel,
title="LOP Load Shot")
USDSublayerLoader: LoadUseAYONEntityURIModel = SettingsField(
default_factory=LoadUseAYONEntityURIModel,
title="USD Sublayer Loader")
USDReferenceLoader: LoadUseAYONEntityURIModel = SettingsField(
default_factory=LoadUseAYONEntityURIModel,
title="USD Reference Loader")
SopUsdImportLoader: LoadUseAYONEntityURIModel = SettingsField(
default_factory=LoadUseAYONEntityURIModel,
title="USD SOP Import Loader")
7 changes: 7 additions & 0 deletions server/settings/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
PublishPluginsModel,
DEFAULT_HOUDINI_PUBLISH_SETTINGS,
)
from .load import (
LoadPluginsModel,
)
from .templated_workfile_build import (
TemplatedWorkfileBuildModel
)
Expand All @@ -42,6 +45,10 @@ class HoudiniSettings(BaseSettingsModel):
default_factory=PublishPluginsModel,
title="Publish Plugins",
)
load: LoadPluginsModel = SettingsField(
default_factory=LoadPluginsModel,
title="Loader Plugins",
)
templated_workfile_build: TemplatedWorkfileBuildModel = SettingsField(
title="Templated Workfile Build",
default_factory=TemplatedWorkfileBuildModel
Expand Down