Skip to content

Commit

Permalink
Merge pull request #26 from BigRoy/enhancement/usd_load_ayon_uri
Browse files Browse the repository at this point in the history
Load USD loaders with filepath from entity URI
  • Loading branch information
antirotor authored Aug 30, 2024
2 parents f2b12a7 + 2aed406 commit 9eec28e
Show file tree
Hide file tree
Showing 12 changed files with 150 additions and 24 deletions.
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"]
11 changes: 4 additions & 7 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,14 +21,13 @@ class USDSublayerLoader(plugin.HoudiniLoader):
icon = "code-fork"
color = "orange"

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

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

# Format file name, Houdini only wants forward slashes
file_path = self.filepath_from_context(context)
file_path = os.path.normpath(file_path)
file_path = file_path.replace("\\", "/")

# Get the root node
Expand Down Expand Up @@ -60,18 +58,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
11 changes: 4 additions & 7 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,14 +21,13 @@ class USDReferenceLoader(plugin.HoudiniLoader):
icon = "code-fork"
color = "orange"

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

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

# Format file name, Houdini only wants forward slashes
file_path = self.filepath_from_context(context)
file_path = os.path.normpath(file_path)
file_path = file_path.replace("\\", "/")

# Get the root node
Expand Down Expand Up @@ -60,18 +58,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
5 changes: 2 additions & 3 deletions client/ayon_houdini/plugins/load/load_usd_sop.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import os

from ayon_houdini.api import (
pipeline,
plugin
Expand All @@ -16,12 +14,13 @@ 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

# Format file name, Houdini only wants forward slashes
file_path = self.filepath_from_context(context)
file_path = os.path.normpath(file_path)
file_path = file_path.replace("\\", "/")

# Get the root node
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

0 comments on commit 9eec28e

Please sign in to comment.