From 69d93987d3d14c061d297e8f288770c5ed34a648 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 5 Nov 2024 23:11:43 +0100 Subject: [PATCH 01/32] 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 02/32] 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 03/32] 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 04/32] 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 05/32] 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 06/32] 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 07/32] 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 08/32] 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) From 27b4025fe244ecf4f327da9ab27c1c816dfda249 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 15 Nov 2024 17:34:42 +0200 Subject: [PATCH 09/32] propagate the number of inputs of the subnetwork to the HDA --- client/ayon_houdini/plugins/create/create_hda.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/ayon_houdini/plugins/create/create_hda.py b/client/ayon_houdini/plugins/create/create_hda.py index 7613735f7b..27ac1ba72b 100644 --- a/client/ayon_houdini/plugins/create/create_hda.py +++ b/client/ayon_houdini/plugins/create/create_hda.py @@ -222,7 +222,9 @@ def create_instance_node( name=type_name, description=node_name, hda_file_name="$HIP/{}.hda".format(node_name), - ignore_external_references=True + ignore_external_references=True, + min_num_inputs=0, + max_num_inputs=len(to_hda.inputs()) or 1, ) hda_node.layoutChildren() elif self._check_existing(folder_path, node_name): From 4ab72b90ab62ba644166e91740d6a81f596b5591 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 15 Nov 2024 23:29:12 +0200 Subject: [PATCH 10/32] keep user spare parameters when publishing --- .../plugins/publish/extract_hda.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/client/ayon_houdini/plugins/publish/extract_hda.py b/client/ayon_houdini/plugins/publish/extract_hda.py index e4449d11f8..d0651b5a99 100644 --- a/client/ayon_houdini/plugins/publish/extract_hda.py +++ b/client/ayon_houdini/plugins/publish/extract_hda.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- import os -from pprint import pformat import hou import pyblish.api from ayon_houdini.api import plugin @@ -13,17 +12,33 @@ class ExtractHDA(plugin.HoudiniExtractorPlugin): families = ["hda"] def process(self, instance): - self.log.info(pformat(instance.data)) + hda_node = hou.node(instance.data.get("instance_node")) hda_def = hda_node.type().definition() hda_options = hda_def.options() hda_options.setSaveInitialParmsAndContents(True) + hda_options.setSaveSpareParms(True) next_version = instance.data["anatomyData"]["version"] self.log.info("setting version: {}".format(next_version)) hda_def.setVersion(str(next_version)) hda_def.setOptions(hda_options) + + # Get `Extra` AYON parameters + parm_group = hda_node.parmTemplateGroup() + parm_folder = parm_group.findFolder("Extra") # This name is a hard coded name in AYON. + assert parm_folder, f"Extra parm folder does not exist: {hda_node.path()}" + + # Remove `Extra` AYON parameters + parm_group.remove(parm_folder.name()) + hda_node.setParmTemplateGroup(parm_group) + + # Save the HDA file hda_def.save(hda_def.libraryFilePath(), hda_node, hda_options) + + # Add `Extra` AYON parameters back + parm_group.append(parm_folder) + hda_node.setParmTemplateGroup(parm_group) if "representations" not in instance.data: instance.data["representations"] = [] From cf7f23c9843bcf74db85da049b227df1e9a32594 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Wed, 20 Nov 2024 17:39:58 +0200 Subject: [PATCH 11/32] support render targets for bgeo instnaces --- .../ayon_houdini/plugins/create/create_bgeo.py | 18 ++++++++++++++---- .../plugins/publish/collect_cache_farm.py | 8 +++++--- .../plugins/publish/collect_farm_instances.py | 5 +++-- .../plugins/publish/extract_rop.py | 4 +++- 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/client/ayon_houdini/plugins/create/create_bgeo.py b/client/ayon_houdini/plugins/create/create_bgeo.py index be51eda411..57037d29d0 100644 --- a/client/ayon_houdini/plugins/create/create_bgeo.py +++ b/client/ayon_houdini/plugins/create/create_bgeo.py @@ -13,6 +13,9 @@ class CreateBGEO(plugin.HoudiniCreator): product_type = "pointcache" icon = "gears" + # Default render target + render_target = "local" + def get_publish_families(self): return ["pointcache", "bgeo"] @@ -21,7 +24,7 @@ def create(self, product_name, instance_data, pre_create_data): instance_data.update({"node_type": "geometry"}) creator_attributes = instance_data.setdefault( "creator_attributes", dict()) - creator_attributes["farm"] = pre_create_data["farm"] + creator_attributes["render_target"] = pre_create_data["render_target"] instance = super(CreateBGEO, self).create( product_name, @@ -63,10 +66,17 @@ def create(self, product_name, instance_data, pre_create_data): instance_node.setParms(parms) def get_instance_attr_defs(self): + render_target_items = { + "local": "Local machine rendering", + "local_no_render": "Use existing frames (local)", + "farm": "Farm Rendering", + } + return [ - BoolDef("farm", - label="Submitting to Farm", - default=False) + EnumDef("render_target", + items=render_target_items, + label="Render target", + default=self.render_target) ] def get_pre_create_attr_defs(self): diff --git a/client/ayon_houdini/plugins/publish/collect_cache_farm.py b/client/ayon_houdini/plugins/publish/collect_cache_farm.py index 68053cf8fb..de977faed2 100644 --- a/client/ayon_houdini/plugins/publish/collect_cache_farm.py +++ b/client/ayon_houdini/plugins/publish/collect_cache_farm.py @@ -16,9 +16,11 @@ class CollectFarmCacheFamily(plugin.HoudiniInstancePlugin): def process(self, instance): creator_attribute = instance.data["creator_attributes"] - farm_enabled = creator_attribute["farm"] - instance.data["farm"] = farm_enabled - if not farm_enabled: + farm_enabled = creator_attribute.get("farm") + if farm_enabled is not None: + instance.data["farm"] = farm_enabled + + if not instance.data["farm"]: self.log.debug("Caching on farm is disabled. " "Skipping farm collecting.") return diff --git a/client/ayon_houdini/plugins/publish/collect_farm_instances.py b/client/ayon_houdini/plugins/publish/collect_farm_instances.py index f14ff65518..9953dce70d 100644 --- a/client/ayon_houdini/plugins/publish/collect_farm_instances.py +++ b/client/ayon_houdini/plugins/publish/collect_farm_instances.py @@ -5,13 +5,14 @@ class CollectFarmInstances(plugin.HoudiniInstancePlugin): """Collect instances for farm render.""" - order = pyblish.api.CollectorOrder + order = pyblish.api.CollectorOrder - 0.49 families = ["mantra_rop", "karma_rop", "redshift_rop", "arnold_rop", "vray_rop", - "usdrender"] + "usdrender", + "bgeo"] targets = ["local", "remote"] label = "Collect farm instances" diff --git a/client/ayon_houdini/plugins/publish/extract_rop.py b/client/ayon_houdini/plugins/publish/extract_rop.py index 74a5f52154..bb1bb8a253 100644 --- a/client/ayon_houdini/plugins/publish/extract_rop.py +++ b/client/ayon_houdini/plugins/publish/extract_rop.py @@ -19,6 +19,7 @@ def process(self, instance: pyblish.api.Instance): if instance.data.get("farm"): self.log.debug("Should be processed on farm, skipping.") return + creator_attribute = instance.data["creator_attributes"] files = instance.data["frames"] first_file = files[0] if isinstance(files, (list, tuple)) else files @@ -29,7 +30,8 @@ def process(self, instance: pyblish.api.Instance): ) ext = ext.lstrip(".") - self.render_rop(instance) + if creator_attribute.get("render_target") in {None, "local"}: + self.render_rop(instance) self.validate_expected_frames(instance) # In some cases representation name is not the the extension From c51710528da1afd7175446347bd07c2b69a79332 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Thu, 21 Nov 2024 13:08:56 +0200 Subject: [PATCH 12/32] remove unused import --- client/ayon_houdini/plugins/create/create_bgeo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_houdini/plugins/create/create_bgeo.py b/client/ayon_houdini/plugins/create/create_bgeo.py index 57037d29d0..884f0d92b5 100644 --- a/client/ayon_houdini/plugins/create/create_bgeo.py +++ b/client/ayon_houdini/plugins/create/create_bgeo.py @@ -3,7 +3,7 @@ from ayon_houdini.api import plugin from ayon_core.pipeline import CreatorError import hou -from ayon_core.lib import EnumDef, BoolDef +from ayon_core.lib import EnumDef class CreateBGEO(plugin.HoudiniCreator): From 6ae167ad0513d113ad90db082fd689af1c14f4c9 Mon Sep 17 00:00:00 2001 From: Mustafa Jafar Date: Thu, 21 Nov 2024 23:05:40 +0200 Subject: [PATCH 13/32] Better condition Co-authored-by: Roy Nieterau --- client/ayon_houdini/plugins/publish/extract_rop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_houdini/plugins/publish/extract_rop.py b/client/ayon_houdini/plugins/publish/extract_rop.py index bb1bb8a253..2e7326db8d 100644 --- a/client/ayon_houdini/plugins/publish/extract_rop.py +++ b/client/ayon_houdini/plugins/publish/extract_rop.py @@ -30,7 +30,7 @@ def process(self, instance: pyblish.api.Instance): ) ext = ext.lstrip(".") - if creator_attribute.get("render_target") in {None, "local"}: + if creator_attribute.get("render_target", "local") == "local": self.render_rop(instance) self.validate_expected_frames(instance) From 1124f995898efcc10741a44e00ce24f278093556 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 22 Nov 2024 17:43:48 +0200 Subject: [PATCH 14/32] Support render targets for other cache instances --- .../plugins/create/create_alembic_camera.py | 25 ++++++++++++++++++ .../plugins/create/create_arnold_ass.py | 20 ++++++++++---- .../plugins/create/create_composite.py | 25 ++++++++++++++++++ .../plugins/create/create_model.py | 20 ++++++++++---- .../plugins/create/create_pointcache.py | 20 ++++++++++---- .../plugins/create/create_redshift_proxy.py | 20 ++++++++++---- .../plugins/create/create_review.py | 24 +++++++++++++++-- .../plugins/create/create_staticmesh.py | 26 +++++++++++++++++-- .../ayon_houdini/plugins/create/create_usd.py | 25 ++++++++++++++++++ .../plugins/create/create_usd_look.py | 25 ++++++++++++++++++ .../plugins/create/create_vbd_cache.py | 20 ++++++++++---- .../plugins/publish/collect_cache_farm.py | 8 +++--- .../plugins/publish/collect_farm_instances.py | 4 ++- 13 files changed, 227 insertions(+), 35 deletions(-) diff --git a/client/ayon_houdini/plugins/create/create_alembic_camera.py b/client/ayon_houdini/plugins/create/create_alembic_camera.py index 5593452d77..fee86e91f1 100644 --- a/client/ayon_houdini/plugins/create/create_alembic_camera.py +++ b/client/ayon_houdini/plugins/create/create_alembic_camera.py @@ -2,6 +2,7 @@ """Creator plugin for creating alembic camera products.""" from ayon_houdini.api import plugin from ayon_core.pipeline import CreatorError +from ayon_core.lib import EnumDef import hou @@ -14,10 +15,16 @@ class CreateAlembicCamera(plugin.HoudiniCreator): product_type = "camera" icon = "camera" + # Default render target + render_target = "local" + def create(self, product_name, instance_data, pre_create_data): import hou instance_data.update({"node_type": "alembic"}) + creator_attributes = instance_data.setdefault( + "creator_attributes", dict()) + creator_attributes["render_target"] = pre_create_data["render_target"] instance = super(CreateAlembicCamera, self).create( product_name, @@ -54,3 +61,21 @@ def get_network_categories(self): hou.ropNodeTypeCategory(), hou.objNodeTypeCategory() ] + + def get_instance_attr_defs(self): + render_target_items = { + "local": "Local machine rendering", + "local_no_render": "Use existing frames (local)", + "farm": "Farm Rendering", + } + + return [ + EnumDef("render_target", + items=render_target_items, + label="Render target", + default=self.render_target) + ] + + def get_pre_create_attr_defs(self): + attrs = super().get_pre_create_attr_defs() + return attrs + self.get_instance_attr_defs() diff --git a/client/ayon_houdini/plugins/create/create_arnold_ass.py b/client/ayon_houdini/plugins/create/create_arnold_ass.py index ec7ba508e0..9adb9a2d28 100644 --- a/client/ayon_houdini/plugins/create/create_arnold_ass.py +++ b/client/ayon_houdini/plugins/create/create_arnold_ass.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """Creator plugin for creating Arnold ASS files.""" from ayon_houdini.api import plugin -from ayon_core.lib import BoolDef +from ayon_core.lib import EnumDef class CreateArnoldAss(plugin.HoudiniCreator): @@ -17,13 +17,16 @@ class CreateArnoldAss(plugin.HoudiniCreator): # will override it by the value in the project settings ext = ".ass" + # Default render target + render_target = "local" + def create(self, product_name, instance_data, pre_create_data): import hou instance_data.update({"node_type": "arnold"}) creator_attributes = instance_data.setdefault( "creator_attributes", dict()) - creator_attributes["farm"] = pre_create_data["farm"] + creator_attributes["render_target"] = pre_create_data["render_target"] instance = super(CreateArnoldAss, self).create( product_name, @@ -57,10 +60,17 @@ def create(self, product_name, instance_data, pre_create_data): self.lock_parameters(instance_node, to_lock) def get_instance_attr_defs(self): + render_target_items = { + "local": "Local machine rendering", + "local_no_render": "Use existing frames (local)", + "farm": "Farm Rendering", + } + return [ - BoolDef("farm", - label="Submitting to Farm", - default=False) + EnumDef("render_target", + items=render_target_items, + label="Render target", + default=self.render_target) ] def get_pre_create_attr_defs(self): diff --git a/client/ayon_houdini/plugins/create/create_composite.py b/client/ayon_houdini/plugins/create/create_composite.py index 50fcab5875..ee7ff0e9a6 100644 --- a/client/ayon_houdini/plugins/create/create_composite.py +++ b/client/ayon_houdini/plugins/create/create_composite.py @@ -2,6 +2,7 @@ """Creator plugin for creating composite sequences.""" from ayon_houdini.api import plugin from ayon_core.pipeline import CreatorError +from ayon_core.lib import EnumDef import hou @@ -16,10 +17,16 @@ class CreateCompositeSequence(plugin.HoudiniCreator): ext = ".exr" + # Default render target + render_target = "local" + def create(self, product_name, instance_data, pre_create_data): import hou # noqa instance_data.update({"node_type": "comp"}) + creator_attributes = instance_data.setdefault( + "creator_attributes", dict()) + creator_attributes["render_target"] = pre_create_data["render_target"] instance = super(CreateCompositeSequence, self).create( product_name, @@ -58,3 +65,21 @@ def get_network_categories(self): hou.ropNodeTypeCategory(), hou.cop2NodeTypeCategory() ] + + def get_instance_attr_defs(self): + render_target_items = { + "local": "Local machine rendering", + "local_no_render": "Use existing frames (local)", + "farm": "Farm Rendering", + } + + return [ + EnumDef("render_target", + items=render_target_items, + label="Render target", + default=self.render_target) + ] + + def get_pre_create_attr_defs(self): + attrs = super().get_pre_create_attr_defs() + return attrs + self.get_instance_attr_defs() \ No newline at end of file diff --git a/client/ayon_houdini/plugins/create/create_model.py b/client/ayon_houdini/plugins/create/create_model.py index 92eee306ae..8c5e5aa89a 100644 --- a/client/ayon_houdini/plugins/create/create_model.py +++ b/client/ayon_houdini/plugins/create/create_model.py @@ -12,7 +12,7 @@ """ from ayon_houdini.api import plugin -from ayon_core.lib import BoolDef +from ayon_core.lib import EnumDef import hou @@ -24,6 +24,9 @@ class CreateModel(plugin.HoudiniCreator): product_type = "model" icon = "cube" + # Default render target + render_target = "local" + def get_publish_families(self): return ["model", "abc"] @@ -31,7 +34,7 @@ def create(self, product_name, instance_data, pre_create_data): instance_data.update({"node_type": "alembic"}) creator_attributes = instance_data.setdefault( "creator_attributes", dict()) - creator_attributes["farm"] = pre_create_data["farm"] + creator_attributes["render_target"] = pre_create_data["render_target"] instance = super(CreateModel, self).create( product_name, @@ -130,10 +133,17 @@ def get_obj_output(self, obj_node): key=lambda node: node.evalParm('outputidx')) def get_instance_attr_defs(self): + render_target_items = { + "local": "Local machine rendering", + "local_no_render": "Use existing frames (local)", + "farm": "Farm Rendering", + } + return [ - BoolDef("farm", - label="Submitting to Farm", - default=False) + EnumDef("render_target", + items=render_target_items, + label="Render target", + default=self.render_target) ] def get_pre_create_attr_defs(self): diff --git a/client/ayon_houdini/plugins/create/create_pointcache.py b/client/ayon_houdini/plugins/create/create_pointcache.py index 969e2a65e7..3288c2655b 100644 --- a/client/ayon_houdini/plugins/create/create_pointcache.py +++ b/client/ayon_houdini/plugins/create/create_pointcache.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """Creator plugin for creating pointcache alembics.""" from ayon_houdini.api import plugin -from ayon_core.lib import BoolDef +from ayon_core.lib import EnumDef import hou @@ -14,6 +14,9 @@ class CreatePointCache(plugin.HoudiniCreator): product_type = "pointcache" icon = "gears" + # Default render target + render_target = "local" + def get_publish_families(self): return ["pointcache", "abc"] @@ -21,7 +24,7 @@ def create(self, product_name, instance_data, pre_create_data): instance_data.update({"node_type": "alembic"}) creator_attributes = instance_data.setdefault( "creator_attributes", dict()) - creator_attributes["farm"] = pre_create_data["farm"] + creator_attributes["render_target"] = pre_create_data["render_target"] instance = super(CreatePointCache, self).create( product_name, @@ -114,10 +117,17 @@ def get_obj_output(self, obj_node): key=lambda node: node.evalParm('outputidx')) def get_instance_attr_defs(self): + render_target_items = { + "local": "Local machine rendering", + "local_no_render": "Use existing frames (local)", + "farm": "Farm Rendering", + } + return [ - BoolDef("farm", - label="Submitting to Farm", - default=False) + EnumDef("render_target", + items=render_target_items, + label="Render target", + default=self.render_target) ] def get_pre_create_attr_defs(self): diff --git a/client/ayon_houdini/plugins/create/create_redshift_proxy.py b/client/ayon_houdini/plugins/create/create_redshift_proxy.py index d9bfe6cdd2..12dc7fb520 100644 --- a/client/ayon_houdini/plugins/create/create_redshift_proxy.py +++ b/client/ayon_houdini/plugins/create/create_redshift_proxy.py @@ -2,7 +2,7 @@ """Creator plugin for creating Redshift proxies.""" from ayon_houdini.api import plugin import hou -from ayon_core.lib import BoolDef +from ayon_core.lib import EnumDef class CreateRedshiftProxy(plugin.HoudiniCreator): @@ -12,6 +12,9 @@ class CreateRedshiftProxy(plugin.HoudiniCreator): product_type = "redshiftproxy" icon = "magic" + # Default render target + render_target = "local" + def create(self, product_name, instance_data, pre_create_data): # Redshift provides a `Redshift_Proxy_Output` node type which shows @@ -24,7 +27,7 @@ def create(self, product_name, instance_data, pre_create_data): instance_data.update({"node_type": "Redshift_Proxy_Output"}) creator_attributes = instance_data.setdefault( "creator_attributes", dict()) - creator_attributes["farm"] = pre_create_data["farm"] + creator_attributes["render_target"] = pre_create_data["render_target"] instance = super(CreateRedshiftProxy, self).create( product_name, @@ -53,10 +56,17 @@ def get_network_categories(self): ] def get_instance_attr_defs(self): + render_target_items = { + "local": "Local machine rendering", + "local_no_render": "Use existing frames (local)", + "farm": "Farm Rendering", + } + return [ - BoolDef("farm", - label="Submitting to Farm", - default=False) + EnumDef("render_target", + items=render_target_items, + label="Render target", + default=self.render_target) ] def get_pre_create_attr_defs(self): diff --git a/client/ayon_houdini/plugins/create/create_review.py b/client/ayon_houdini/plugins/create/create_review.py index 601349e3ad..cf98294ab0 100644 --- a/client/ayon_houdini/plugins/create/create_review.py +++ b/client/ayon_houdini/plugins/create/create_review.py @@ -16,6 +16,9 @@ class CreateReview(plugin.HoudiniCreator): icon = "video-camera" review_color_space = "" node_type = "opengl" + + # Default render target + render_target = "local" # TODO: Publish families should reflect the node type, # such as `rop.flipbook` for flipbook nodes @@ -35,6 +38,9 @@ def apply_settings(self, project_settings): def create(self, product_name, instance_data, pre_create_data): self.node_type = pre_create_data.get("node_type") + creator_attributes = instance_data.setdefault( + "creator_attributes", dict()) + creator_attributes["render_target"] = pre_create_data["render_target"] instance_data.update({"node_type": self.node_type}) instance_data["imageFormat"] = pre_create_data.get("imageFormat") @@ -121,9 +127,23 @@ def create(self, product_name, instance_data, pre_create_data): to_lock = ["id", "productType"] self.lock_parameters(instance_node, to_lock) + + def get_instance_attr_defs(self): + render_target_items = { + "local": "Local machine rendering", + "local_no_render": "Use existing frames (local)", + "farm": "Farm Rendering", + } + return [ + EnumDef("render_target", + items=render_target_items, + label="Render target", + default=self.render_target) + ] + def get_pre_create_attr_defs(self): - attrs = super(CreateReview, self).get_pre_create_attr_defs() + attrs = super().get_pre_create_attr_defs() image_format_enum = [ "bmp", "cin", "exr", "jpg", "pic", "pic.gz", "png", @@ -165,4 +185,4 @@ def get_pre_create_attr_defs(self): default=1.0, minimum=0.0001, decimals=3) - ] + ] + self.get_instance_attr_defs() diff --git a/client/ayon_houdini/plugins/create/create_staticmesh.py b/client/ayon_houdini/plugins/create/create_staticmesh.py index 17b646040c..6a9fef08ed 100644 --- a/client/ayon_houdini/plugins/create/create_staticmesh.py +++ b/client/ayon_houdini/plugins/create/create_staticmesh.py @@ -16,9 +16,15 @@ class CreateStaticMesh(plugin.HoudiniCreator): default_variants = ["Main"] + # Default render target + render_target = "local" + def create(self, product_name, instance_data, pre_create_data): instance_data.update({"node_type": "filmboxfbx"}) + creator_attributes = instance_data.setdefault( + "creator_attributes", dict()) + creator_attributes["render_target"] = pre_create_data["render_target"] instance = super(CreateStaticMesh, self).create( product_name, @@ -57,11 +63,25 @@ def get_network_categories(self): hou.objNodeTypeCategory(), hou.sopNodeTypeCategory() ] + + def get_instance_attr_defs(self): + render_target_items = { + "local": "Local machine rendering", + "local_no_render": "Use existing frames (local)", + "farm": "Farm Rendering", + } + + return [ + EnumDef("render_target", + items=render_target_items, + label="Render target", + default=self.render_target) + ] def get_pre_create_attr_defs(self): """Add settings for users. """ - attrs = super(CreateStaticMesh, self).get_pre_create_attr_defs() + attrs = super().get_pre_create_attr_defs() createsubnetroot = BoolDef("createsubnetroot", tooltip="Create an extra root for the " "Export node when it's a " @@ -85,7 +105,9 @@ def get_pre_create_attr_defs(self): default=False, label="Convert Units") - return attrs + [createsubnetroot, vcformat, convert_units] + return attrs + [ + createsubnetroot, vcformat, convert_units + ] + self.get_instance_attr_defs() def get_dynamic_data( self, diff --git a/client/ayon_houdini/plugins/create/create_usd.py b/client/ayon_houdini/plugins/create/create_usd.py index 585ba667de..3a0e743bdf 100644 --- a/client/ayon_houdini/plugins/create/create_usd.py +++ b/client/ayon_houdini/plugins/create/create_usd.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- """Creator plugin for creating USDs.""" from ayon_houdini.api import plugin +from ayon_core.lib import EnumDef import hou @@ -14,9 +15,15 @@ class CreateUSD(plugin.HoudiniCreator): enabled = False description = "Create USD" + # Default render target + render_target = "local" + def create(self, product_name, instance_data, pre_create_data): instance_data.update({"node_type": "usd"}) + creator_attributes = instance_data.setdefault( + "creator_attributes", dict()) + creator_attributes["render_target"] = pre_create_data["render_target"] instance = super(CreateUSD, self).create( product_name, @@ -52,3 +59,21 @@ def get_network_categories(self): def get_publish_families(self): return ["usd", "usdrop"] + + def get_instance_attr_defs(self): + render_target_items = { + "local": "Local machine rendering", + "local_no_render": "Use existing frames (local)", + "farm": "Farm Rendering", + } + + return [ + EnumDef("render_target", + items=render_target_items, + label="Render target", + default=self.render_target) + ] + + def get_pre_create_attr_defs(self): + attrs = super().get_pre_create_attr_defs() + return attrs + self.get_instance_attr_defs() \ No newline at end of file diff --git a/client/ayon_houdini/plugins/create/create_usd_look.py b/client/ayon_houdini/plugins/create/create_usd_look.py index 93aa486abb..2e621a0037 100644 --- a/client/ayon_houdini/plugins/create/create_usd_look.py +++ b/client/ayon_houdini/plugins/create/create_usd_look.py @@ -3,6 +3,7 @@ import inspect from ayon_houdini.api import plugin +from ayon_core.lib import EnumDef import hou @@ -17,9 +18,15 @@ class CreateUSDLook(plugin.HoudiniCreator): enabled = True description = "Create USD Look" + # Default render target + render_target = "local" + def create(self, product_name, instance_data, pre_create_data): instance_data.update({"node_type": "usd"}) + creator_attributes = instance_data.setdefault( + "creator_attributes", dict()) + creator_attributes["render_target"] = pre_create_data["render_target"] instance = super(CreateUSDLook, self).create( product_name, @@ -70,3 +77,21 @@ def get_network_categories(self): def get_publish_families(self): return ["usd", "look", "usdrop"] + + def get_instance_attr_defs(self): + render_target_items = { + "local": "Local machine rendering", + "local_no_render": "Use existing frames (local)", + "farm": "Farm Rendering", + } + + return [ + EnumDef("render_target", + items=render_target_items, + label="Render target", + default=self.render_target) + ] + + def get_pre_create_attr_defs(self): + attrs = super().get_pre_create_attr_defs() + return attrs + self.get_instance_attr_defs() \ No newline at end of file diff --git a/client/ayon_houdini/plugins/create/create_vbd_cache.py b/client/ayon_houdini/plugins/create/create_vbd_cache.py index 804865860e..3870f403b5 100644 --- a/client/ayon_houdini/plugins/create/create_vbd_cache.py +++ b/client/ayon_houdini/plugins/create/create_vbd_cache.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """Creator plugin for creating VDB Caches.""" from ayon_houdini.api import plugin -from ayon_core.lib import BoolDef +from ayon_core.lib import EnumDef import hou @@ -14,13 +14,16 @@ class CreateVDBCache(plugin.HoudiniCreator): product_type = "vdbcache" icon = "cloud" + # Default render target + render_target = "local" + def create(self, product_name, instance_data, pre_create_data): import hou instance_data.update({"node_type": "geometry"}) creator_attributes = instance_data.setdefault( "creator_attributes", dict()) - creator_attributes["farm"] = pre_create_data["farm"] + creator_attributes["render_target"] = pre_create_data["render_target"] instance = super(CreateVDBCache, self).create( product_name, instance_data, @@ -109,10 +112,17 @@ def get_obj_output(self, obj_node): key=lambda node: node.evalParm('outputidx')) def get_instance_attr_defs(self): + render_target_items = { + "local": "Local machine rendering", + "local_no_render": "Use existing frames (local)", + "farm": "Farm Rendering", + } + return [ - BoolDef("farm", - label="Submitting to Farm", - default=False) + EnumDef("render_target", + items=render_target_items, + label="Render target", + default=self.render_target) ] def get_pre_create_attr_defs(self): diff --git a/client/ayon_houdini/plugins/publish/collect_cache_farm.py b/client/ayon_houdini/plugins/publish/collect_cache_farm.py index de977faed2..53f13af366 100644 --- a/client/ayon_houdini/plugins/publish/collect_cache_farm.py +++ b/client/ayon_houdini/plugins/publish/collect_cache_farm.py @@ -10,15 +10,13 @@ class CollectFarmCacheFamily(plugin.HoudiniInstancePlugin): """Collect publish.hou family for caching on farm as early as possible.""" order = pyblish.api.CollectorOrder - 0.45 - families = ["ass", "pointcache", "redshiftproxy", "vdbcache", "model"] + families = ["ass", "pointcache", "redshiftproxy", + "vdbcache", "model", "staticMesh", + "rop.opengl", "usdrop", "camera"] targets = ["local", "remote"] label = "Collect Data for Cache" def process(self, instance): - creator_attribute = instance.data["creator_attributes"] - farm_enabled = creator_attribute.get("farm") - if farm_enabled is not None: - instance.data["farm"] = farm_enabled if not instance.data["farm"]: self.log.debug("Caching on farm is disabled. " diff --git a/client/ayon_houdini/plugins/publish/collect_farm_instances.py b/client/ayon_houdini/plugins/publish/collect_farm_instances.py index 9953dce70d..d2b39beece 100644 --- a/client/ayon_houdini/plugins/publish/collect_farm_instances.py +++ b/client/ayon_houdini/plugins/publish/collect_farm_instances.py @@ -12,7 +12,9 @@ class CollectFarmInstances(plugin.HoudiniInstancePlugin): "arnold_rop", "vray_rop", "usdrender", - "bgeo"] + "ass","pointcache", "redshiftproxy", + "vdbcache", "model", "staticMesh", + "rop.opengl", "usdrop", "camera"] targets = ["local", "remote"] label = "Collect farm instances" From 5292f6484783f67e4d114f10d1931cb1dcdb4da5 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 22 Nov 2024 17:58:47 +0200 Subject: [PATCH 15/32] revert changes to CreateCompositeSequence --- .../plugins/create/create_composite.py | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/client/ayon_houdini/plugins/create/create_composite.py b/client/ayon_houdini/plugins/create/create_composite.py index ee7ff0e9a6..50fcab5875 100644 --- a/client/ayon_houdini/plugins/create/create_composite.py +++ b/client/ayon_houdini/plugins/create/create_composite.py @@ -2,7 +2,6 @@ """Creator plugin for creating composite sequences.""" from ayon_houdini.api import plugin from ayon_core.pipeline import CreatorError -from ayon_core.lib import EnumDef import hou @@ -17,16 +16,10 @@ class CreateCompositeSequence(plugin.HoudiniCreator): ext = ".exr" - # Default render target - render_target = "local" - def create(self, product_name, instance_data, pre_create_data): import hou # noqa instance_data.update({"node_type": "comp"}) - creator_attributes = instance_data.setdefault( - "creator_attributes", dict()) - creator_attributes["render_target"] = pre_create_data["render_target"] instance = super(CreateCompositeSequence, self).create( product_name, @@ -65,21 +58,3 @@ def get_network_categories(self): hou.ropNodeTypeCategory(), hou.cop2NodeTypeCategory() ] - - def get_instance_attr_defs(self): - render_target_items = { - "local": "Local machine rendering", - "local_no_render": "Use existing frames (local)", - "farm": "Farm Rendering", - } - - return [ - EnumDef("render_target", - items=render_target_items, - label="Render target", - default=self.render_target) - ] - - def get_pre_create_attr_defs(self): - attrs = super().get_pre_create_attr_defs() - return attrs + self.get_instance_attr_defs() \ No newline at end of file From dbcce0b947f15ec002fc8b23031dd888ebbdb89b Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Mon, 25 Nov 2024 12:35:39 +0200 Subject: [PATCH 16/32] add a comment about using `local` as a fallback --- client/ayon_houdini/plugins/publish/extract_rop.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/ayon_houdini/plugins/publish/extract_rop.py b/client/ayon_houdini/plugins/publish/extract_rop.py index 2e7326db8d..7e5c5798cd 100644 --- a/client/ayon_houdini/plugins/publish/extract_rop.py +++ b/client/ayon_houdini/plugins/publish/extract_rop.py @@ -30,6 +30,9 @@ def process(self, instance: pyblish.api.Instance): ) ext = ext.lstrip(".") + # Value `local` is used as a fallback if the `render_target` key is missing. + # This key might be absent because render targets are not yet implemented + # for all product types that use this plugin. if creator_attribute.get("render_target", "local") == "local": self.render_rop(instance) self.validate_expected_frames(instance) From cff27b078e9340e99447fc35735097f2b127137a Mon Sep 17 00:00:00 2001 From: Ynbot Date: Mon, 25 Nov 2024 12:30:27 +0000 Subject: [PATCH 17/32] [Automated] Add generated package files to main --- client/ayon_houdini/version.py | 2 +- package.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_houdini/version.py b/client/ayon_houdini/version.py index bc4015c5c2..bd6f7839ec 100644 --- a/client/ayon_houdini/version.py +++ b/client/ayon_houdini/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'houdini' version.""" -__version__ = "0.3.17+dev" +__version__ = "0.4.0" diff --git a/package.py b/package.py index f4663525bf..0c62aea649 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "houdini" title = "Houdini" -version = "0.3.17+dev" +version = "0.4.0" app_host_name = "houdini" client_dir = "ayon_houdini" From a5368006d3b2c1cb3404670e4226f86db647b50b Mon Sep 17 00:00:00 2001 From: Ynbot Date: Mon, 25 Nov 2024 12:31:01 +0000 Subject: [PATCH 18/32] [Automated] Update version in package.py for develop --- client/ayon_houdini/version.py | 2 +- package.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_houdini/version.py b/client/ayon_houdini/version.py index bd6f7839ec..5498259afd 100644 --- a/client/ayon_houdini/version.py +++ b/client/ayon_houdini/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'houdini' version.""" -__version__ = "0.4.0" +__version__ = "0.4.0+dev" diff --git a/package.py b/package.py index 0c62aea649..3c9d30689e 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "houdini" title = "Houdini" -version = "0.4.0" +version = "0.4.0+dev" app_host_name = "houdini" client_dir = "ayon_houdini" From 70680ad37826c199545d027e2a7861620ba06a8a Mon Sep 17 00:00:00 2001 From: ynbot Date: Mon, 25 Nov 2024 13:57:23 +0000 Subject: [PATCH 19/32] [Automated] Update assign_pr_to_project caller workflow --- .github/workflows/assign_pr_to_project.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .github/workflows/assign_pr_to_project.yml diff --git a/.github/workflows/assign_pr_to_project.yml b/.github/workflows/assign_pr_to_project.yml new file mode 100644 index 0000000000..86707fc9da --- /dev/null +++ b/.github/workflows/assign_pr_to_project.yml @@ -0,0 +1,15 @@ +name: 🔸Auto assign pr +on: + pull_request: + types: + - opened + +jobs: + auto-assign-pr: + uses: ynput/ops-repo-automation/.github/workflows/pr_to_project.yml@develop + with: + repo: "${{ github.repository }}" + project_id: 16 + pull_request_number: ${{ github.event.pull_request.number }} + secrets: + token: ${{ secrets.YNPUT_BOT_TOKEN }} From 64596e523e93379fa304fb650e1d39cd21e85652 Mon Sep 17 00:00:00 2001 From: ynbot Date: Mon, 25 Nov 2024 14:08:00 +0000 Subject: [PATCH 20/32] [Automated] Update validate_pr_labels caller workflow --- .github/workflows/validate_pr_labels.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/workflows/validate_pr_labels.yml diff --git a/.github/workflows/validate_pr_labels.yml b/.github/workflows/validate_pr_labels.yml new file mode 100644 index 0000000000..00e5742afe --- /dev/null +++ b/.github/workflows/validate_pr_labels.yml @@ -0,0 +1,18 @@ +name: 🔎 Validate PR Labels +on: + pull_request: + types: + - opened + - edited + - labeled + - unlabeled + +jobs: + validate-type-label: + uses: ynput/ops-repo-automation/.github/workflows/validate_pr_labels.yml@develop + with: + repo: "${{ github.repository }}" + pull_request_number: ${{ github.event.pull_request.number }} + query_prefix: "type: " + secrets: + token: ${{ secrets.YNPUT_BOT_TOKEN }} From 8b76f1e50ea8d4578ea3caeab26c2035009d06f9 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 26 Nov 2024 11:04:48 +0100 Subject: [PATCH 21/32] Containerise and parse container with `project_name` parm --- client/ayon_houdini/api/pipeline.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/client/ayon_houdini/api/pipeline.py b/client/ayon_houdini/api/pipeline.py index 337e24c4a4..f0f4d3db99 100644 --- a/client/ayon_houdini/api/pipeline.py +++ b/client/ayon_houdini/api/pipeline.py @@ -243,6 +243,7 @@ def containerise(name, "namespace": namespace, "loader": str(loader), "representation": context["representation"]["id"], + "project_name": context["project"]["name"] } lib.imprint(container, data) @@ -285,6 +286,13 @@ def parse_container(container): pass data[name] = value + # Support project name in container as optional attribute + for name in ["project_name"]: + parm = container.parm(name) + if not parm: + continue + data[name] = parm.eval() + # Backwards compatibility pre-schemas for containers data["schema"] = data.get("schema", "openpype:container-1.0") From 8004afb8d2b5416b57d01ab4182a423af3466fe2 Mon Sep 17 00:00:00 2001 From: ynbot Date: Tue, 26 Nov 2024 11:46:43 +0000 Subject: [PATCH 22/32] [Automated] Update assign_pr_to_project caller workflow --- .github/workflows/assign_pr_to_project.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/assign_pr_to_project.yml b/.github/workflows/assign_pr_to_project.yml index 86707fc9da..4bb3d1742c 100644 --- a/.github/workflows/assign_pr_to_project.yml +++ b/.github/workflows/assign_pr_to_project.yml @@ -6,7 +6,7 @@ on: jobs: auto-assign-pr: - uses: ynput/ops-repo-automation/.github/workflows/pr_to_project.yml@develop + uses: ynput/ops-repo-automation/.github/workflows/pr_to_project.yml@main with: repo: "${{ github.repository }}" project_id: 16 From b45cbe354b848fb7f7b6ab425721d5b261721ec6 Mon Sep 17 00:00:00 2001 From: ynbot Date: Tue, 26 Nov 2024 11:52:09 +0000 Subject: [PATCH 23/32] [Automated] Update validate_pr_labels caller workflow --- .github/workflows/validate_pr_labels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/validate_pr_labels.yml b/.github/workflows/validate_pr_labels.yml index 00e5742afe..f25e263c98 100644 --- a/.github/workflows/validate_pr_labels.yml +++ b/.github/workflows/validate_pr_labels.yml @@ -9,7 +9,7 @@ on: jobs: validate-type-label: - uses: ynput/ops-repo-automation/.github/workflows/validate_pr_labels.yml@develop + uses: ynput/ops-repo-automation/.github/workflows/validate_pr_labels.yml@main with: repo: "${{ github.repository }}" pull_request_number: ${{ github.event.pull_request.number }} From e5caf2fa58ad4fea48d7a4e5ea47213128e44133 Mon Sep 17 00:00:00 2001 From: ynbot Date: Tue, 26 Nov 2024 15:17:29 +0000 Subject: [PATCH 24/32] [Automated] Update assign_pr_to_project caller workflow --- .github/workflows/assign_pr_to_project.yml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/.github/workflows/assign_pr_to_project.yml b/.github/workflows/assign_pr_to_project.yml index 4bb3d1742c..92d2ff2916 100644 --- a/.github/workflows/assign_pr_to_project.yml +++ b/.github/workflows/assign_pr_to_project.yml @@ -1,5 +1,16 @@ name: 🔸Auto assign pr on: + workflow_dispatch: + inputs: + pr_number: + type: number + description: "Run workflow for this PR number" + required: true + project_id: + type: number + description: "Github Project Number" + required: true + default: 16 pull_request: types: - opened @@ -9,7 +20,7 @@ jobs: uses: ynput/ops-repo-automation/.github/workflows/pr_to_project.yml@main with: repo: "${{ github.repository }}" - project_id: 16 - pull_request_number: ${{ github.event.pull_request.number }} + project_id: ${{ inputs.project_id || 16 }} + pull_request_number: ${{ github.event.pull_request.number || inputs.pr_number }} secrets: token: ${{ secrets.YNPUT_BOT_TOKEN }} From 2d515a4f1b0b474146e4328c223742ae16b53861 Mon Sep 17 00:00:00 2001 From: ynbot Date: Fri, 29 Nov 2024 08:16:59 +0000 Subject: [PATCH 25/32] [Automated] Update assign_pr_to_project caller workflow --- .github/workflows/assign_pr_to_project.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/assign_pr_to_project.yml b/.github/workflows/assign_pr_to_project.yml index 92d2ff2916..14e1a02075 100644 --- a/.github/workflows/assign_pr_to_project.yml +++ b/.github/workflows/assign_pr_to_project.yml @@ -17,10 +17,11 @@ on: jobs: auto-assign-pr: + if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} uses: ynput/ops-repo-automation/.github/workflows/pr_to_project.yml@main with: repo: "${{ github.repository }}" - project_id: ${{ inputs.project_id || 16 }} - pull_request_number: ${{ github.event.pull_request.number || inputs.pr_number }} + project_id: "${{ inputs.project_id }}" + pull_request_number: "${{ github.event.pull_request.number || inputs.pr_number }}" secrets: token: ${{ secrets.YNPUT_BOT_TOKEN }} From cacbe68dbaf2297575e5620ff1b1494bfe958763 Mon Sep 17 00:00:00 2001 From: ynbot Date: Sat, 30 Nov 2024 14:31:10 +0000 Subject: [PATCH 26/32] [Automated] Update assign_pr_to_project caller workflow --- .github/workflows/assign_pr_to_project.yml | 35 +++++++++++++++++----- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/.github/workflows/assign_pr_to_project.yml b/.github/workflows/assign_pr_to_project.yml index 14e1a02075..e61d281c2a 100644 --- a/.github/workflows/assign_pr_to_project.yml +++ b/.github/workflows/assign_pr_to_project.yml @@ -3,25 +3,46 @@ on: workflow_dispatch: inputs: pr_number: - type: number + type: string description: "Run workflow for this PR number" required: true project_id: - type: number + type: string description: "Github Project Number" required: true - default: 16 + default: "16" pull_request: types: - opened +env: + GH_TOKEN: ${{ github.token }} + jobs: + get-pr-repo: + runs-on: ubuntu-latest + outputs: + pr_repo_name: ${{ steps.get-repo-name.outputs.repo_name || github.event.pull_request.head.repo.full_name }} + + # INFO `github.event.pull_request.head.repo.full_name` is not available on manual triggered (dispatched) runs + steps: + - name: Get PR repo name + if: ${{ github.event_name == 'workflow_dispatch' }} + id: get-repo-name + run: | + repo_name=$(gh pr view ${{ inputs.pr_number }} --json headRepository,headRepositoryOwner --repo ${{ github.repository }} | jq -r '.headRepositoryOwner.login + "/" + .headRepository.name') + echo "repo_name=$repo_name" >> $GITHUB_OUTPUT + auto-assign-pr: - if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} + needs: + - get-pr-repo + if: ${{ needs.get-pr-repo.outputs.pr_repo_name == github.repository }} uses: ynput/ops-repo-automation/.github/workflows/pr_to_project.yml@main with: repo: "${{ github.repository }}" - project_id: "${{ inputs.project_id }}" - pull_request_number: "${{ github.event.pull_request.number || inputs.pr_number }}" + project_id: ${{ inputs.project_id != '' && fromJSON(inputs.project_id) || 16 }} + pull_request_number: ${{ github.event.pull_request.number || fromJSON(inputs.pr_number) }} secrets: - token: ${{ secrets.YNPUT_BOT_TOKEN }} + # INFO fallback to default `github.token` is required for PRs from forks + # INFO organization secrets won't be available to forks + token: ${{ secrets.YNPUT_BOT_TOKEN || github.token}} From 06456af59d7e34f6c2a689c1ddbd8327fec87d6e Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 5 Dec 2024 11:49:35 +0100 Subject: [PATCH 27/32] Use context manager to revert the original parameter template group --- .../plugins/publish/extract_hda.py | 49 +++++++++++++------ 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/client/ayon_houdini/plugins/publish/extract_hda.py b/client/ayon_houdini/plugins/publish/extract_hda.py index d0651b5a99..a2bd8cf1c2 100644 --- a/client/ayon_houdini/plugins/publish/extract_hda.py +++ b/client/ayon_houdini/plugins/publish/extract_hda.py @@ -1,10 +1,25 @@ # -*- coding: utf-8 -*- import os +import contextlib + import hou import pyblish.api + +from ayon_core.pipeline import PublishError from ayon_houdini.api import plugin +@contextlib.contextmanager +def revert_original_parm_template_group(node: "hou.OpNode"): + """Restore parm template group after the context""" + parm_group = node.parmTemplateGroup() + try: + yield + finally: + # Set the original + node.setParmTemplateGroup(parm_group) + + class ExtractHDA(plugin.HoudiniExtractorPlugin): order = pyblish.api.ExtractorOrder @@ -24,21 +39,25 @@ def process(self, instance): hda_def.setVersion(str(next_version)) hda_def.setOptions(hda_options) - # Get `Extra` AYON parameters - parm_group = hda_node.parmTemplateGroup() - parm_folder = parm_group.findFolder("Extra") # This name is a hard coded name in AYON. - assert parm_folder, f"Extra parm folder does not exist: {hda_node.path()}" - - # Remove `Extra` AYON parameters - parm_group.remove(parm_folder.name()) - hda_node.setParmTemplateGroup(parm_group) - - # Save the HDA file - hda_def.save(hda_def.libraryFilePath(), hda_node, hda_options) - - # Add `Extra` AYON parameters back - parm_group.append(parm_folder) - hda_node.setParmTemplateGroup(parm_group) + with revert_original_parm_template_group(hda_node): + # Remove our own custom parameters so that if the HDA definition + # has "Save Spare Parameters" enabled, we don't save our custom + # attributes + # Get our custom `Extra` AYON parameters + parm_group = hda_node.parmTemplateGroup() + # The name 'Extra' is a hard coded name in AYON. + parm_folder = parm_group.findFolder("Extra") + if not parm_folder: + raise PublishError( + f"Extra parm folder does not exist: {hda_node.path()}" + ) + + # Remove `Extra` AYON parameters + parm_group.remove(parm_folder.name()) + hda_node.setParmTemplateGroup(parm_group) + + # Save the HDA file + hda_def.save(hda_def.libraryFilePath(), hda_node, hda_options) if "representations" not in instance.data: instance.data["representations"] = [] From 7f4ac93273b7cdacf52205be8a36127f6a763b92 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 5 Dec 2024 11:50:13 +0100 Subject: [PATCH 28/32] Do not enforce `setSaveSpareParms` to be True - but allow user to enable it manually in the HDA definition if they want --- client/ayon_houdini/plugins/publish/extract_hda.py | 1 - 1 file changed, 1 deletion(-) diff --git a/client/ayon_houdini/plugins/publish/extract_hda.py b/client/ayon_houdini/plugins/publish/extract_hda.py index a2bd8cf1c2..3d49c3332f 100644 --- a/client/ayon_houdini/plugins/publish/extract_hda.py +++ b/client/ayon_houdini/plugins/publish/extract_hda.py @@ -32,7 +32,6 @@ def process(self, instance): hda_def = hda_node.type().definition() hda_options = hda_def.options() hda_options.setSaveInitialParmsAndContents(True) - hda_options.setSaveSpareParms(True) next_version = instance.data["anatomyData"]["version"] self.log.info("setting version: {}".format(next_version)) From 34ed4197dc14bd07dc8614b214bffe436c4320a5 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 5 Dec 2024 11:51:06 +0100 Subject: [PATCH 29/32] Add support to promote spare parameters on Creation (like Houdini default behavior) + fix having just a HDA selected to make that publishable instead of collapsing it. --- .../ayon_houdini/plugins/create/create_hda.py | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/client/ayon_houdini/plugins/create/create_hda.py b/client/ayon_houdini/plugins/create/create_hda.py index 27ac1ba72b..0f411bb01f 100644 --- a/client/ayon_houdini/plugins/create/create_hda.py +++ b/client/ayon_houdini/plugins/create/create_hda.py @@ -141,6 +141,12 @@ def set_tool_submenu(hda_def, # endregion +def promote_spare_parameters(node): + """Promote spare parameters to HDA node type definition""" + ptg = node.parmTemplateGroup() + node.type().definition().setParmTemplateGroup(ptg) + + class CreateHDA(plugin.HoudiniCreator): """Publish Houdini Digital Asset file.""" @@ -178,12 +184,27 @@ def create_instance_node( if pre_create_data is None: pre_create_data = {} + use_promote_spare_parameters = pre_create_data.get( + "use_promote_spare_parameters", True) + if self.selected_nodes: # if we have `use selection` enabled, and we have some # selected nodes ... - if self.selected_nodes[0].type().name() == "subnet": - to_hda = self.selected_nodes[0] + one_node_selected = len(self.selected_nodes) == 1 + first_selected_node = self.selected_nodes[0] + + # If only an HDA is selected, publish just that node as HDA. + if one_node_selected and first_selected_node.type().definition(): + to_hda = first_selected_node + use_promote_spare_parameters = False + + # If only a single subnet is selected, turn that into the HDA. + elif one_node_selected and first_selected_node.type().name() == "subnet": + to_hda = first_selected_node to_hda.setName("{}_subnet".format(node_name), unique_name=True) + + # Collapse all selected nodes into a subnet and turn that into + # the HDA. else: parent_node = self.selected_nodes[0].parent() subnet = parent_node.collapseIntoSubnet( @@ -202,6 +223,7 @@ def create_instance_node( to_hda = parent_node.createNode( "subnet", node_name="{}_subnet".format(node_name)) + if not to_hda.type().definition(): # if node type has not its definition, it is not user # created hda. We test if hda can be created from the node. @@ -226,6 +248,13 @@ def create_instance_node( min_num_inputs=0, max_num_inputs=len(to_hda.inputs()) or 1, ) + + if use_promote_spare_parameters: + # Similar to Houdini default behavior, when enabled this will + # promote spare parameters to type properties on initial + # creation of the HDA. + promote_spare_parameters(hda_node) + hda_node.layoutChildren() elif self._check_existing(folder_path, node_name): raise CreatorError( @@ -281,6 +310,12 @@ def get_pre_create_attr_defs(self): "'AYON/project_name/your_HDA_name'", default=True, label="Use Project as menu entry"), + BoolDef("use_promote_spare_parameters", + tooltip="Similar to Houdini default behavior, when " + "enabled this will promote spare parameters to " + "type properties on initial creation of the HDA.", + default=True, + label="Promote Spare Parameters"), ] def get_dynamic_data( From 72308a4a4debfea3776a79b8a67ca0a80643872e Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Thu, 5 Dec 2024 14:40:53 +0200 Subject: [PATCH 30/32] Move `Extra` parameters folder to the back on updating HDA from scene inventory --- client/ayon_houdini/plugins/load/load_hda.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/client/ayon_houdini/plugins/load/load_hda.py b/client/ayon_houdini/plugins/load/load_hda.py index fcf0e834f8..41ad6aef42 100644 --- a/client/ayon_houdini/plugins/load/load_hda.py +++ b/client/ayon_houdini/plugins/load/load_hda.py @@ -75,6 +75,16 @@ def update(self, container, context): "representation": repre_entity["id"] }) + # Move the Extra parameter folder to the back. + parm_group = hda_node.parmTemplateGroup() + # The name 'Extra' is a hard coded name in AYON. + parm_folder = parm_group.findFolder("Extra") + # Remove `Extra` AYON parameters + parm_group.remove(parm_folder.name()) + # Add `Extra` AYON parameters back + parm_group.append(parm_folder) + hda_node.setParmTemplateGroup(parm_group) + def remove(self, container): node = container["node"] parent = node.parent() From 2c3dc63ccd9e64c3594e3c240e1c057a134f4890 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Tue, 10 Dec 2024 11:33:05 +0000 Subject: [PATCH 31/32] [Automated] Add generated package files from main --- client/ayon_houdini/version.py | 2 +- package.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_houdini/version.py b/client/ayon_houdini/version.py index 5498259afd..1affb2cf45 100644 --- a/client/ayon_houdini/version.py +++ b/client/ayon_houdini/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'houdini' version.""" -__version__ = "0.4.0+dev" +__version__ = "0.4.1" diff --git a/package.py b/package.py index 3c9d30689e..1491c85e75 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "houdini" title = "Houdini" -version = "0.4.0+dev" +version = "0.4.1" app_host_name = "houdini" client_dir = "ayon_houdini" From 0d64612fffbd028034674f0127285b352baa4e34 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Tue, 10 Dec 2024 11:33:40 +0000 Subject: [PATCH 32/32] [Automated] Update version in package.py for develop --- client/ayon_houdini/version.py | 2 +- package.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_houdini/version.py b/client/ayon_houdini/version.py index 1affb2cf45..a08a5c36c6 100644 --- a/client/ayon_houdini/version.py +++ b/client/ayon_houdini/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'houdini' version.""" -__version__ = "0.4.1" +__version__ = "0.4.1+dev" diff --git a/package.py b/package.py index 1491c85e75..5304cac495 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "houdini" title = "Houdini" -version = "0.4.1" +version = "0.4.1+dev" app_host_name = "houdini" client_dir = "ayon_houdini"