From 69d93987d3d14c061d297e8f288770c5ed34a648 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 5 Nov 2024 23:11:43 +0100 Subject: [PATCH 1/8] Remove `usd` because it should only care about `usdrop` and `usdrender` Also, remove duplicate entry of `usdrender` in the list. --- client/ayon_houdini/plugins/publish/collect_output_node.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/ayon_houdini/plugins/publish/collect_output_node.py b/client/ayon_houdini/plugins/publish/collect_output_node.py index c7af43ec6a..e2ae431a3a 100644 --- a/client/ayon_houdini/plugins/publish/collect_output_node.py +++ b/client/ayon_houdini/plugins/publish/collect_output_node.py @@ -12,8 +12,6 @@ class CollectOutputSOPPath(plugin.HoudiniInstancePlugin): "camera", "vdbcache", "imagesequence", - "usd", - "usdrender", "redshiftproxy", "staticMesh", "model", From 0cc329d9c4a5a145e47991d4f4d700217be63639 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 5 Nov 2024 23:13:33 +0100 Subject: [PATCH 2/8] Implement rudimentary draft implementation of a "Component Builder" publish --- .../create/create_usd_componentbuilder.py | 75 ++++++++++++++++ .../publish/collect_componentbuilder_lop.py | 86 +++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 client/ayon_houdini/plugins/create/create_usd_componentbuilder.py create mode 100644 client/ayon_houdini/plugins/publish/collect_componentbuilder_lop.py diff --git a/client/ayon_houdini/plugins/create/create_usd_componentbuilder.py b/client/ayon_houdini/plugins/create/create_usd_componentbuilder.py new file mode 100644 index 0000000000..566da9d263 --- /dev/null +++ b/client/ayon_houdini/plugins/create/create_usd_componentbuilder.py @@ -0,0 +1,75 @@ +import inspect + +from ayon_houdini.api import plugin +from ayon_core.pipeline import CreatedInstance, CreatorError + +import hou + + +class CreateUSDComponentBuilder(plugin.HoudiniCreator): + identifier = "io.ayon.creators.houdini.componentbuilders" + label = "USD Component Builder LOPs" + product_type = "usd" + icon = "cubes" + description = "Create USD from Component Builder LOPs" + + def get_detail_description(self): + return inspect.cleandoc(""" + Creates a USD publish from a Component Output LOP that is part of + a solaris component builder network. + + The created USD will contain the component builder LOPs and all its + dependencies inside the single product. + + To use it, select a Component Output LOP and click "Create" for + this creator. It will generate an instance for each selected + Component Output LOP. + """) + + def create(self, product_name, instance_data, pre_create_data): + + nodes = hou.selectedNodes() + + builders = [ + node for node in nodes if node.type().name() == "componentoutput" + ] + if not builders: + return + + for builder in builders: + self.create_for_instance_node(product_name, instance_data, builder) + + + def create_for_instance_node( + self, product_name, instance_data, instance_node): + + try: + self.customize_node_look(instance_node) + instance_data["instance_node"] = instance_node.path() + instance_data["instance_id"] = instance_node.path() + instance_data["families"] = self.get_publish_families() + instance = CreatedInstance( + self.product_type, + product_name, + instance_data, + self) + self._add_instance_to_context(instance) + self.imprint(instance_node, instance.data_to_store()) + except hou.Error as er: + raise CreatorError("Creator error: {}".format(er)) from er + + # Lock any parameters in this list + to_lock = [ + # Lock some AYON attributes + "productType", + "id", + ] + self.lock_parameters(instance_node, to_lock) + + def get_network_categories(self): + # Do not expose via tab menu because currently it does not create any + # node, but only 'imprints' on an existing node. + return [] + + def get_publish_families(self): + return ["usd", "componentbuilder"] diff --git a/client/ayon_houdini/plugins/publish/collect_componentbuilder_lop.py b/client/ayon_houdini/plugins/publish/collect_componentbuilder_lop.py new file mode 100644 index 0000000000..684ab3131c --- /dev/null +++ b/client/ayon_houdini/plugins/publish/collect_componentbuilder_lop.py @@ -0,0 +1,86 @@ +import os +from typing import List, Tuple +from pathlib import Path + +import pyblish.api + +from ayon_core.pipeline import AYONPyblishPluginMixin, PublishError +from ayon_houdini.api import plugin + +import hou +from pxr import Sdf, Usd, UsdUtils + + +def compute_all_dependencies( + filepath: str) -> Tuple[list[Sdf.Layer], list[str], list[str]]: + """Compute all dependencies for the given USD file.""" + # Only separated here for better type hints on returned values + return UsdUtils.ComputeAllDependencies(filepath) + + +class CollectComponentBuilderLOPs(plugin.HoudiniInstancePlugin, + AYONPyblishPluginMixin): + + # Run after `CollectResourcesPath` + order = pyblish.api.CollectorOrder + 0.496 + families = ["componentbuilder"] + label = "Collect Componentbuilder LOPs" + + def process(self, instance): + + node = hou.node(instance.data["instance_node"]) + + # Use existing files for now + filepath = node.evalParm("lopoutput") + + # Render the component builder LOPs + # TODO: Do we want this? or use existing frames? Usually a Collector + # should not 'extract' but in this case we need the resulting USD + # file. + node.cook(force=True) # required to clear existing errors + node.parm("execute").pressButton() + + errors = node.errors() + if errors: + for error in errors: + self.log.error(error) + raise PublishError(f"Failed to save to disk '{node.path()}'") + + # Define the main asset usd file + representations = instance.data.setdefault("representations", []) + representations.append({ + "name": "usd", + "ext": "usd", + "files": os.path.basename(filepath), + "stagingDir": os.path.dirname(filepath), + }) + + # Get all its files and dependencies + # TODO: Ignore any files that are not 'relative' to the USD file + layers, assets, unresolved_paths = compute_all_dependencies(filepath) + paths: List[str] = [] + paths.extend(layer.realPath for layer in layers) + paths.extend(assets) + + # Skip unresolved paths, but warn about them + for unresolved in unresolved_paths: + self.log.warning(f"Cannot be resolved: {unresolved}") + + self.log.debug(f"Collecting USD: {filepath}") + src_root_dir = os.path.dirname(filepath) + + # Used to compare resolved paths against + filepath = Path(filepath) + + # We keep the relative paths to the USD file + transfers = instance.data.setdefault("transfers", []) + publish_root = instance.data["publishDir"] + for src in paths: + + if filepath == Path(src): + continue + + relative_path = os.path.relpath(src, start=src_root_dir) + self.log.debug(f"Collected dependency: {relative_path}") + dest = os.path.normpath(os.path.join(publish_root, relative_path)) + transfers.append((src, dest)) From 1761d8d3da789d9cb04eefcbf812f5ec1d76fd4c Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 5 Nov 2024 23:26:39 +0100 Subject: [PATCH 3/8] Remove trailing `s` from creator identifier because it's odd --- .../ayon_houdini/plugins/create/create_usd_componentbuilder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_houdini/plugins/create/create_usd_componentbuilder.py b/client/ayon_houdini/plugins/create/create_usd_componentbuilder.py index 566da9d263..5aa15af716 100644 --- a/client/ayon_houdini/plugins/create/create_usd_componentbuilder.py +++ b/client/ayon_houdini/plugins/create/create_usd_componentbuilder.py @@ -7,7 +7,7 @@ class CreateUSDComponentBuilder(plugin.HoudiniCreator): - identifier = "io.ayon.creators.houdini.componentbuilders" + identifier = "io.ayon.creators.houdini.componentbuilder" label = "USD Component Builder LOPs" product_type = "usd" icon = "cubes" From b1ffc35b59636b008bdcc24cc1caccd6d2e2783f Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 5 Nov 2024 23:30:35 +0100 Subject: [PATCH 4/8] Simplify code --- .../plugins/create/create_usd_componentbuilder.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/client/ayon_houdini/plugins/create/create_usd_componentbuilder.py b/client/ayon_houdini/plugins/create/create_usd_componentbuilder.py index 5aa15af716..afa0f568ac 100644 --- a/client/ayon_houdini/plugins/create/create_usd_componentbuilder.py +++ b/client/ayon_houdini/plugins/create/create_usd_componentbuilder.py @@ -27,19 +27,13 @@ def get_detail_description(self): """) def create(self, product_name, instance_data, pre_create_data): - nodes = hou.selectedNodes() - builders = [ node for node in nodes if node.type().name() == "componentoutput" ] - if not builders: - return - for builder in builders: self.create_for_instance_node(product_name, instance_data, builder) - def create_for_instance_node( self, product_name, instance_data, instance_node): From 54a25de116f5be4f3e0b6822cfcd80ecc1eae4d1 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 5 Nov 2024 23:31:47 +0100 Subject: [PATCH 5/8] Cleanup code --- .../plugins/publish/collect_componentbuilder_lop.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/client/ayon_houdini/plugins/publish/collect_componentbuilder_lop.py b/client/ayon_houdini/plugins/publish/collect_componentbuilder_lop.py index 684ab3131c..591e37f9d6 100644 --- a/client/ayon_houdini/plugins/publish/collect_componentbuilder_lop.py +++ b/client/ayon_houdini/plugins/publish/collect_componentbuilder_lop.py @@ -30,9 +30,6 @@ def process(self, instance): node = hou.node(instance.data["instance_node"]) - # Use existing files for now - filepath = node.evalParm("lopoutput") - # Render the component builder LOPs # TODO: Do we want this? or use existing frames? Usually a Collector # should not 'extract' but in this case we need the resulting USD @@ -47,6 +44,7 @@ def process(self, instance): raise PublishError(f"Failed to save to disk '{node.path()}'") # Define the main asset usd file + filepath = node.evalParm("lopoutput") representations = instance.data.setdefault("representations", []) representations.append({ "name": "usd", From c3b785949cbfb6bec8583c6359ed72d94790e418 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 6 Nov 2024 00:44:09 +0100 Subject: [PATCH 6/8] Remove unused import --- .../plugins/publish/collect_componentbuilder_lop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_houdini/plugins/publish/collect_componentbuilder_lop.py b/client/ayon_houdini/plugins/publish/collect_componentbuilder_lop.py index 591e37f9d6..b7a771f693 100644 --- a/client/ayon_houdini/plugins/publish/collect_componentbuilder_lop.py +++ b/client/ayon_houdini/plugins/publish/collect_componentbuilder_lop.py @@ -8,7 +8,7 @@ from ayon_houdini.api import plugin import hou -from pxr import Sdf, Usd, UsdUtils +from pxr import Sdf, UsdUtils def compute_all_dependencies( From 0dcb84ed9b78955ca0b9f3e352520e126841c2bc Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 11 Nov 2024 14:11:29 +0100 Subject: [PATCH 7/8] Update client/ayon_houdini/plugins/create/create_usd_componentbuilder.py Co-authored-by: Mustafa Jafar --- .../ayon_houdini/plugins/create/create_usd_componentbuilder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_houdini/plugins/create/create_usd_componentbuilder.py b/client/ayon_houdini/plugins/create/create_usd_componentbuilder.py index afa0f568ac..977334bc34 100644 --- a/client/ayon_houdini/plugins/create/create_usd_componentbuilder.py +++ b/client/ayon_houdini/plugins/create/create_usd_componentbuilder.py @@ -29,7 +29,7 @@ def get_detail_description(self): def create(self, product_name, instance_data, pre_create_data): nodes = hou.selectedNodes() builders = [ - node for node in nodes if node.type().name() == "componentoutput" + node for node in nodes if node.type().nameWithCategory() == "Lop/componentoutput" ] for builder in builders: self.create_for_instance_node(product_name, instance_data, builder) From c52e956c04a8209b110b21f4966869912832a091 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 11 Nov 2024 14:12:00 +0100 Subject: [PATCH 8/8] Update client/ayon_houdini/plugins/create/create_usd_componentbuilder.py --- .../ayon_houdini/plugins/create/create_usd_componentbuilder.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_houdini/plugins/create/create_usd_componentbuilder.py b/client/ayon_houdini/plugins/create/create_usd_componentbuilder.py index 977334bc34..67b21d1a49 100644 --- a/client/ayon_houdini/plugins/create/create_usd_componentbuilder.py +++ b/client/ayon_houdini/plugins/create/create_usd_componentbuilder.py @@ -29,7 +29,8 @@ def get_detail_description(self): def create(self, product_name, instance_data, pre_create_data): nodes = hou.selectedNodes() builders = [ - node for node in nodes if node.type().nameWithCategory() == "Lop/componentoutput" + node for node in nodes + if node.type().nameWithCategory() == "Lop/componentoutput" ] for builder in builders: self.create_for_instance_node(product_name, instance_data, builder)