From aa600f88d45dcff4452d3f34de53168958cf5172 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 26 Apr 2024 23:20:45 +0300 Subject: [PATCH 001/129] Support HDA Publishing from non object level --- .../hosts/houdini/plugins/create/create_hda.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py index d399aa5e15..d747abc738 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py @@ -37,16 +37,24 @@ class CreateHDA(plugin.HoudiniCreator): self, folder_path, node_name, parent, node_type="geometry" ): - parent_node = hou.node("/obj") if self.selected_nodes: # if we have `use selection` enabled, and we have some # selected nodes ... + parent_node = self.selected_nodes[0].parent() subnet = parent_node.collapseIntoSubnet( self.selected_nodes, subnet_name="{}_subnet".format(node_name)) subnet.moveToGoodPosition() to_hda = subnet else: + # Use Obj as the default path + parent_node = hou.node("/obj") + # Find and return the NetworkEditor pane tab with the minimum index + pane = hou.ui.paneTabOfType(hou.paneTabType.NetworkEditor) + if isinstance(pane, hou.NetworkEditor): + # Use the NetworkEditor pane path as the parent path. + parent_node = pane.pwd() + to_hda = parent_node.createNode( "subnet", node_name="{}_subnet".format(node_name)) if not to_hda.type().definition(): From 1953a74ae1635c3e0b2159fcec7ca0ec2d6cfef3 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 26 Apr 2024 23:21:50 +0300 Subject: [PATCH 002/129] Allow users to specify maximum inputs of the HDA --- .../hosts/houdini/plugins/create/create_hda.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py index d747abc738..4538266bce 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py @@ -4,6 +4,7 @@ import ayon_api from ayon_core.pipeline import CreatorError from ayon_core.hosts.houdini.api import plugin +from ayon_core.lib import NumberDef import hou @@ -16,6 +17,8 @@ class CreateHDA(plugin.HoudiniCreator): icon = "gears" maintain_selection = False + max_num_inputs = 0 + def _check_existing(self, folder_path, product_name): # type: (str, str) -> bool """Check if existing product name versions already exists.""" @@ -66,7 +69,8 @@ class CreateHDA(plugin.HoudiniCreator): hda_node = to_hda.createDigitalAsset( name=node_name, - hda_file_name="$HIP/{}.hda".format(node_name) + hda_file_name="$HIP/{}.hda".format(node_name), + max_num_inputs=self.max_num_inputs ) hda_node.layoutChildren() elif self._check_existing(folder_path, node_name): @@ -83,6 +87,8 @@ class CreateHDA(plugin.HoudiniCreator): def create(self, product_name, instance_data, pre_create_data): instance_data.pop("active", None) + self.max_num_inputs = pre_create_data["max_num_inputs"] + instance = super(CreateHDA, self).create( product_name, instance_data, @@ -94,3 +100,12 @@ class CreateHDA(plugin.HoudiniCreator): return [ hou.objNodeTypeCategory() ] + + def get_pre_create_attr_defs(self): + attrs = super(CreateHDA, self).get_pre_create_attr_defs() + return attrs + [ + NumberDef("max_num_inputs", + label="Maximum Inputs", + default=self.max_num_inputs, + decimals=0) + ] From 079b332e48ad42f2a69d142e35521cf0d97d6610 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 26 Apr 2024 23:23:20 +0300 Subject: [PATCH 003/129] fix a bug with setName --- client/ayon_core/hosts/houdini/plugins/create/create_hda.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py index 4538266bce..1a4457d2d4 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py @@ -80,7 +80,11 @@ class CreateHDA(plugin.HoudiniCreator): else: hda_node = to_hda - hda_node.setName(node_name) + # If user tries to create the same HDA instance more than + # once, then all of them will have the same product name and + # point to the same hda_file_name. But, their node names will + # be incremented. + hda_node.setName(node_name, unique_name=True) self.customize_node_look(hda_node) return hda_node From b8dcd4cc315e311a90d7ccaf0c41964e53f2cdd4 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 26 Apr 2024 23:50:34 +0300 Subject: [PATCH 004/129] ignore_external_references when creating a digital asset --- client/ayon_core/hosts/houdini/plugins/create/create_hda.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py index 1a4457d2d4..119afd3edc 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py @@ -70,7 +70,8 @@ class CreateHDA(plugin.HoudiniCreator): hda_node = to_hda.createDigitalAsset( name=node_name, hda_file_name="$HIP/{}.hda".format(node_name), - max_num_inputs=self.max_num_inputs + max_num_inputs=self.max_num_inputs, + ignore_external_references=True ) hda_node.layoutChildren() elif self._check_existing(folder_path, node_name): From a41b564f2f5bfc5d18551a9fde7c49da8ccb55dd Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Mon, 29 Apr 2024 11:20:02 +0300 Subject: [PATCH 005/129] don't create a subnetwork if user seleted a subnetwork --- .../hosts/houdini/plugins/create/create_hda.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py index 119afd3edc..6a4ebc324d 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py @@ -43,12 +43,16 @@ class CreateHDA(plugin.HoudiniCreator): if self.selected_nodes: # if we have `use selection` enabled, and we have some # selected nodes ... - parent_node = self.selected_nodes[0].parent() - subnet = parent_node.collapseIntoSubnet( - self.selected_nodes, - subnet_name="{}_subnet".format(node_name)) - subnet.moveToGoodPosition() - to_hda = subnet + if self.selected_nodes[0].type().name() == "subnet": + to_hda = self.selected_nodes[0] + to_hda.setName("{}_subnet".format(node_name), unique_name=True) + else: + parent_node = self.selected_nodes[0].parent() + subnet = parent_node.collapseIntoSubnet( + self.selected_nodes, + subnet_name="{}_subnet".format(node_name)) + subnet.moveToGoodPosition() + to_hda = subnet else: # Use Obj as the default path parent_node = hou.node("/obj") From 8399a6832de451e97e5692408aed867a9f796d97 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Mon, 29 Apr 2024 11:24:35 +0300 Subject: [PATCH 006/129] Allow users to specify minimum inputs of the HDA --- .../ayon_core/hosts/houdini/plugins/create/create_hda.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py index 6a4ebc324d..f79b49fb2b 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py @@ -17,6 +17,7 @@ class CreateHDA(plugin.HoudiniCreator): icon = "gears" maintain_selection = False + min_num_inputs = 0 max_num_inputs = 0 def _check_existing(self, folder_path, product_name): @@ -74,6 +75,7 @@ class CreateHDA(plugin.HoudiniCreator): hda_node = to_hda.createDigitalAsset( name=node_name, hda_file_name="$HIP/{}.hda".format(node_name), + min_num_inputs=self.min_num_inputs, max_num_inputs=self.max_num_inputs, ignore_external_references=True ) @@ -96,6 +98,7 @@ class CreateHDA(plugin.HoudiniCreator): def create(self, product_name, instance_data, pre_create_data): instance_data.pop("active", None) + self.min_num_inputs = pre_create_data["min_num_inputs"] self.max_num_inputs = pre_create_data["max_num_inputs"] instance = super(CreateHDA, self).create( @@ -113,6 +116,10 @@ class CreateHDA(plugin.HoudiniCreator): def get_pre_create_attr_defs(self): attrs = super(CreateHDA, self).get_pre_create_attr_defs() return attrs + [ + NumberDef("min_num_inputs", + label="Minimum Inputs", + default=self.min_num_inputs, + decimals=0), NumberDef("max_num_inputs", label="Maximum Inputs", default=self.max_num_inputs, From 23583ab02243ee55cfe6b1424c71e05df90a0e91 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 30 Apr 2024 08:47:50 +0300 Subject: [PATCH 007/129] CreateHDA: update get_network_categories --- client/ayon_core/hosts/houdini/plugins/create/create_hda.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py index f79b49fb2b..772ff4e255 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py @@ -110,7 +110,11 @@ class CreateHDA(plugin.HoudiniCreator): def get_network_categories(self): return [ - hou.objNodeTypeCategory() + category for name, category in hou.nodeTypeCategories().items() + if name in { + "Chop", "Cop2", "Dop", "Driver", "Lop", + "Object", "Shop", "Sop", "Top", "Vop" + } ] def get_pre_create_attr_defs(self): From 05886e882a81fd02c01925ce423bc35dce2d883c Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 3 May 2024 11:51:20 +0200 Subject: [PATCH 008/129] Pass `pre_create_data` to `create_instance_node` --- client/ayon_core/hosts/houdini/api/plugin.py | 7 +++++- .../houdini/plugins/create/create_hda.py | 25 ++++++++++++------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/client/ayon_core/hosts/houdini/api/plugin.py b/client/ayon_core/hosts/houdini/api/plugin.py index a9c8c313b9..75527e0b3f 100644 --- a/client/ayon_core/hosts/houdini/api/plugin.py +++ b/client/ayon_core/hosts/houdini/api/plugin.py @@ -141,7 +141,11 @@ class HoudiniCreatorBase(object): @staticmethod def create_instance_node( - folder_path, node_name, parent, node_type="geometry" + folder_path, + node_name, + parent, + node_type="geometry", + pre_create_data=None ): """Create node representing instance. @@ -150,6 +154,7 @@ class HoudiniCreatorBase(object): node_name (str): Name of the new node. parent (str): Name of the parent node. node_type (str, optional): Type of the node. + pre_create_data (Optional[Dict]): Pre create data. Returns: hou.Node: Newly created instance node. diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py index 772ff4e255..bc4ad093bf 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py @@ -38,8 +38,20 @@ class CreateHDA(plugin.HoudiniCreator): return product_name.lower() in existing_product_names_low def create_instance_node( - self, folder_path, node_name, parent, node_type="geometry" + self, + folder_path, + node_name, + parent, + node_type="geometry", + pre_create_data=None ): + if pre_create_data is None: + pre_create_data = {} + + min_num_inputs = pre_create_data.get("min_num_inputs", + self.min_num_inputs) + max_num_inputs = pre_create_data.get("min_num_inputs", + self.max_num_inputs) if self.selected_nodes: # if we have `use selection` enabled, and we have some @@ -75,8 +87,8 @@ class CreateHDA(plugin.HoudiniCreator): hda_node = to_hda.createDigitalAsset( name=node_name, hda_file_name="$HIP/{}.hda".format(node_name), - min_num_inputs=self.min_num_inputs, - max_num_inputs=self.max_num_inputs, + min_num_inputs=min_num_inputs, + max_num_inputs=max_num_inputs, ignore_external_references=True ) hda_node.layoutChildren() @@ -98,16 +110,11 @@ class CreateHDA(plugin.HoudiniCreator): def create(self, product_name, instance_data, pre_create_data): instance_data.pop("active", None) - self.min_num_inputs = pre_create_data["min_num_inputs"] - self.max_num_inputs = pre_create_data["max_num_inputs"] - - instance = super(CreateHDA, self).create( + return super(CreateHDA, self).create( product_name, instance_data, pre_create_data) - return instance - def get_network_categories(self): return [ category for name, category in hou.nodeTypeCategories().items() From 2a654bcfe82f6e5df8a156823a7a85cc97957102 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 3 May 2024 19:38:18 +0300 Subject: [PATCH 009/129] pass pre_create_data argument to self.create_instance_node --- client/ayon_core/hosts/houdini/api/plugin.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/houdini/api/plugin.py b/client/ayon_core/hosts/houdini/api/plugin.py index 75527e0b3f..6c878867c0 100644 --- a/client/ayon_core/hosts/houdini/api/plugin.py +++ b/client/ayon_core/hosts/houdini/api/plugin.py @@ -189,7 +189,12 @@ class HoudiniCreator(NewCreator, HoudiniCreatorBase): folder_path = instance_data["folderPath"] instance_node = self.create_instance_node( - folder_path, product_name, "/out", node_type) + folder_path, + product_name, + "/out", + node_type, + pre_create_data + ) self.customize_node_look(instance_node) From 689c87b6d9ddf5605825767eef8f0446d1931c75 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 3 May 2024 19:38:58 +0300 Subject: [PATCH 010/129] fix pre_create_data key --- client/ayon_core/hosts/houdini/plugins/create/create_hda.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py index bc4ad093bf..9566a6c5f3 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py @@ -50,7 +50,7 @@ class CreateHDA(plugin.HoudiniCreator): min_num_inputs = pre_create_data.get("min_num_inputs", self.min_num_inputs) - max_num_inputs = pre_create_data.get("min_num_inputs", + max_num_inputs = pre_create_data.get("max_num_inputs", self.max_num_inputs) if self.selected_nodes: From 79139ce0dd4b31c32c98d26403a2f78c6e345f14 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 3 May 2024 20:09:02 +0300 Subject: [PATCH 011/129] use explicit network categories --- .../hosts/houdini/plugins/create/create_hda.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py index 9566a6c5f3..0eb2b5dd52 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py @@ -116,12 +116,20 @@ class CreateHDA(plugin.HoudiniCreator): pre_create_data) def get_network_categories(self): + # Houdini allows creating sub-network nodes inside + # these categories. + # Therefore this plugin can work in these categories. return [ - category for name, category in hou.nodeTypeCategories().items() - if name in { - "Chop", "Cop2", "Dop", "Driver", "Lop", - "Object", "Shop", "Sop", "Top", "Vop" - } + hou.chopNodeTypeCategory(), + hou.cop2NodeTypeCategory(), + hou.dopNodeTypeCategory(), + hou.ropNodeTypeCategory(), + hou.lopNodeTypeCategory(), + hou.objNodeTypeCategory(), + hou.shopNodeTypeCategory(), + hou.sopNodeTypeCategory(), + hou.topNodeTypeCategory(), + hou.vopNodeTypeCategory() ] def get_pre_create_attr_defs(self): From 618bab2c281936a9a2ec9982f98f96513d7b2e8c Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 7 May 2024 01:30:49 +0300 Subject: [PATCH 012/129] add more options for HDA creator --- .../houdini/plugins/create/create_hda.py | 47 ++++++++++++++++--- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py index 0eb2b5dd52..ff56a63132 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- """Creator plugin for creating publishable Houdini Digital Assets.""" import ayon_api - +import getpass from ayon_core.pipeline import CreatorError from ayon_core.hosts.houdini.api import plugin -from ayon_core.lib import NumberDef +from ayon_core.lib import NumberDef, BoolDef import hou +from ayon_core.resources import get_ayon_icon_filepath class CreateHDA(plugin.HoudiniCreator): @@ -19,6 +20,7 @@ class CreateHDA(plugin.HoudiniCreator): min_num_inputs = 0 max_num_inputs = 0 + max_num_outputs = 1 def _check_existing(self, folder_path, product_name): # type: (str, str) -> bool @@ -52,7 +54,8 @@ class CreateHDA(plugin.HoudiniCreator): self.min_num_inputs) max_num_inputs = pre_create_data.get("max_num_inputs", self.max_num_inputs) - + max_num_outputs = pre_create_data.get("max_num_outputs", + self.max_num_outputs) if self.selected_nodes: # if we have `use selection` enabled, and we have some # selected nodes ... @@ -87,8 +90,6 @@ class CreateHDA(plugin.HoudiniCreator): hda_node = to_hda.createDigitalAsset( name=node_name, hda_file_name="$HIP/{}.hda".format(node_name), - min_num_inputs=min_num_inputs, - max_num_inputs=max_num_inputs, ignore_external_references=True ) hda_node.layoutChildren() @@ -105,6 +106,24 @@ class CreateHDA(plugin.HoudiniCreator): # be incremented. hda_node.setName(node_name, unique_name=True) self.customize_node_look(hda_node) + + # Set Custom settings. + hda_def = hda_node.type().definition() + hda_def.setMinNumInputs(min_num_inputs) + hda_def.setMaxNumInputs(max_num_inputs) + hda_def.setMaxNumOutputs(max_num_outputs) + + if pre_create_data.get("use_ayon_icon"): + hda_def.setIcon(get_ayon_icon_filepath()) + + if pre_create_data.get("set_user"): + hda_def.setUserInfo(getpass.getuser()) + + if pre_create_data.get("use_project"): + tool_name = hou.shelves.defaultToolName( + hda_def.nodeTypeCategory().name(), hda_def.nodeTypeName()) + hou.shelves.tool(tool_name).setToolLocations((self.project_name,)) + return hda_node def create(self, product_name, instance_data, pre_create_data): @@ -142,5 +161,21 @@ class CreateHDA(plugin.HoudiniCreator): NumberDef("max_num_inputs", label="Maximum Inputs", default=self.max_num_inputs, - decimals=0) + decimals=0), + NumberDef("max_num_outputs", + label="Maximum Outputs", + default=self.max_num_outputs, + decimals=0), + BoolDef("use_ayon_icon", + tooltip="Use Ayon icon for the digital asset.", + default=True, + label="Use AYON Icon"), + BoolDef("set_user", + tooltip="Set current user as the author of the HDA", + default=True, + label="Set Current User"), + BoolDef("use_project", + tooltip="Use project name as tab submenu path", + default=True, + label="Use Project as menu entry"), ] From 2ab0e240f8ca0e119bbb317452e8d78247e7f296 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 7 May 2024 16:09:27 +0300 Subject: [PATCH 013/129] remove shop from network categories --- client/ayon_core/hosts/houdini/plugins/create/create_hda.py | 1 - 1 file changed, 1 deletion(-) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py index ff56a63132..d4dcd245ec 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py @@ -145,7 +145,6 @@ class CreateHDA(plugin.HoudiniCreator): hou.ropNodeTypeCategory(), hou.lopNodeTypeCategory(), hou.objNodeTypeCategory(), - hou.shopNodeTypeCategory(), hou.sopNodeTypeCategory(), hou.topNodeTypeCategory(), hou.vopNodeTypeCategory() From 7d37953011078a0d07b3a0c086507891329ec1d3 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 7 May 2024 16:15:44 +0300 Subject: [PATCH 014/129] remove setting number of inputs and outputs --- .../houdini/plugins/create/create_hda.py | 27 +------------------ 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py index d4dcd245ec..0e0436d981 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py @@ -4,7 +4,7 @@ import ayon_api import getpass from ayon_core.pipeline import CreatorError from ayon_core.hosts.houdini.api import plugin -from ayon_core.lib import NumberDef, BoolDef +from ayon_core.lib import BoolDef import hou from ayon_core.resources import get_ayon_icon_filepath @@ -18,10 +18,6 @@ class CreateHDA(plugin.HoudiniCreator): icon = "gears" maintain_selection = False - min_num_inputs = 0 - max_num_inputs = 0 - max_num_outputs = 1 - def _check_existing(self, folder_path, product_name): # type: (str, str) -> bool """Check if existing product name versions already exists.""" @@ -50,12 +46,6 @@ class CreateHDA(plugin.HoudiniCreator): if pre_create_data is None: pre_create_data = {} - min_num_inputs = pre_create_data.get("min_num_inputs", - self.min_num_inputs) - max_num_inputs = pre_create_data.get("max_num_inputs", - self.max_num_inputs) - max_num_outputs = pre_create_data.get("max_num_outputs", - self.max_num_outputs) if self.selected_nodes: # if we have `use selection` enabled, and we have some # selected nodes ... @@ -109,9 +99,6 @@ class CreateHDA(plugin.HoudiniCreator): # Set Custom settings. hda_def = hda_node.type().definition() - hda_def.setMinNumInputs(min_num_inputs) - hda_def.setMaxNumInputs(max_num_inputs) - hda_def.setMaxNumOutputs(max_num_outputs) if pre_create_data.get("use_ayon_icon"): hda_def.setIcon(get_ayon_icon_filepath()) @@ -153,18 +140,6 @@ class CreateHDA(plugin.HoudiniCreator): def get_pre_create_attr_defs(self): attrs = super(CreateHDA, self).get_pre_create_attr_defs() return attrs + [ - NumberDef("min_num_inputs", - label="Minimum Inputs", - default=self.min_num_inputs, - decimals=0), - NumberDef("max_num_inputs", - label="Maximum Inputs", - default=self.max_num_inputs, - decimals=0), - NumberDef("max_num_outputs", - label="Maximum Outputs", - default=self.max_num_outputs, - decimals=0), BoolDef("use_ayon_icon", tooltip="Use Ayon icon for the digital asset.", default=True, From 99179cc6453e968741313dd946671ff5aa56939f Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 7 May 2024 16:21:07 +0300 Subject: [PATCH 015/129] update default values of attr defs --- client/ayon_core/hosts/houdini/plugins/create/create_hda.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py index 0e0436d981..a810f06283 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py @@ -142,11 +142,11 @@ class CreateHDA(plugin.HoudiniCreator): return attrs + [ BoolDef("use_ayon_icon", tooltip="Use Ayon icon for the digital asset.", - default=True, + default=False, label="Use AYON Icon"), BoolDef("set_user", tooltip="Set current user as the author of the HDA", - default=True, + default=False, label="Set Current User"), BoolDef("use_project", tooltip="Use project name as tab submenu path", From 8f1dd6acf6f16f2f1f608fced6be7e416de747ee Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Wed, 8 May 2024 22:50:06 +0300 Subject: [PATCH 016/129] support loading HDAs from non object level --- .../hosts/houdini/plugins/load/load_hda.py | 131 +++++++++++++++--- 1 file changed, 115 insertions(+), 16 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_hda.py b/client/ayon_core/hosts/houdini/plugins/load/load_hda.py index 10fc03be03..b2d2337266 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_hda.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_hda.py @@ -3,10 +3,28 @@ import os from ayon_core.pipeline import ( load, get_representation_path, + AVALON_CONTAINER_ID ) -from ayon_core.hosts.houdini.api import pipeline +from ayon_core.hosts.houdini.api import lib, pipeline +import hou +def get_avalon_container(): + path = pipeline.AVALON_CONTAINERS + avalon_container = hou.node(path) + if not avalon_container: + # Let's create avalon container secretly + # but make sure the pipeline still is built the + # way we anticipate it was built, asserting it. + assert path == "/obj/AVALON_CONTAINERS" + + parent = hou.node("/obj") + avalon_container = parent.createNode( + "subnet", node_name="AVALON_CONTAINERS" + ) + + return avalon_container + class HdaLoader(load.LoaderPlugin): """Load Houdini Digital Asset file.""" @@ -18,38 +36,44 @@ class HdaLoader(load.LoaderPlugin): color = "orange" 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 - obj = hou.node("/obj") - # Create a unique name counter = 1 namespace = namespace or context["folder"]["name"] formatted = "{}_{}".format(namespace, name) if namespace else name node_name = "{0}_{1:03d}".format(formatted, counter) + hda_defs = hou.hda.definitionsInFile(file_path) + if not hda_defs: + raise RuntimeError ("No HDA definitions found!") + + hda_def = hda_defs[0] + parent_node = self._create_dedicated_parent_node(hda_def) + hou.hda.installFile(file_path) - hda_node = obj.createNode(name, node_name) + hda_node = parent_node.createNode(name, node_name) + hda_node.moveToGoodPosition() - self[:] = [hda_node] + # Imprint it manually + data = { + "schema": "openpype:container-2.0", + "id": AVALON_CONTAINER_ID, + "name": node_name, + "namespace": namespace, + "loader": str(self.__class__.__name__), + "representation": context["representation"]["id"], + } - return pipeline.containerise( - node_name, - namespace, - [hda_node], - context, - self.__class__.__name__, - suffix="", - ) + lib.imprint(hda_node, data) + + return hda_node def update(self, container, context): - import hou repre_entity = context["representation"] hda_node = container["node"] @@ -66,4 +90,79 @@ class HdaLoader(load.LoaderPlugin): def remove(self, container): node = container["node"] + parent = node.parent() node.destroy() + + if parent.type().category() == hou.objNodeTypeCategory(): + return + + # Remove parent if empty. + if not parent.children(): + parent.destroy() + + def _create_dedicated_parent_node(self, hda_def): + + # Get the root node + parent_node = get_avalon_container() + node = None + node_type = None + if hda_def.nodeTypeCategory() == hou.objNodeTypeCategory(): + return parent_node + elif hda_def.nodeTypeCategory() == hou.chopNodeTypeCategory(): + node_type, node_name = "chopnet", "MOTION" + elif hda_def.nodeTypeCategory() == hou.cop2NodeTypeCategory(): + node_type, node_name = "cop2net", "IMAGES" + elif hda_def.nodeTypeCategory() == hou.dopNodeTypeCategory(): + node_type, node_name = "dopnet", "DOPS" + elif hda_def.nodeTypeCategory() == hou.ropNodeTypeCategory(): + node_type, node_name = "ropnet", "ROPS" + elif hda_def.nodeTypeCategory() == hou.lopNodeTypeCategory(): + node_type, node_name = "lopnet", "LOPS" + elif hda_def.nodeTypeCategory() == hou.sopNodeTypeCategory(): + node_type, node_name = "geo", "SOPS" + elif hda_def.nodeTypeCategory() == hou.topNodeTypeCategory(): + node_type, node_name = "topnet", "TOPS" + # TODO: Create a dedicated parent node based on Vop Node vex context. + elif hda_def.nodeTypeCategory() == hou.vopNodeTypeCategory(): + parent_node, node_type, node_name = self._get_dedicated_parent_for_vop(parent_node, hda_def) + + node = parent_node.node(node_name) + if not node: + node = parent_node.createNode(node_type, node_name) + + node.moveToGoodPosition() + return node + + def _get_dedicated_parent_for_vop(self, parent_node, hda_def): + """create_dedicated_parent_for_vop + + VOPs are a little special because they can exist + in different contexts. + e.g. + - Material Network + - SOPs > VOP nodes + - Chop > VOP nodes + - Cop2 > VOP nodes + + Get the vex context of the HDA and return + dedicated node_type and node_name. + + It creates intermediate nodes if needed. + """ + vex_context = hou.vexContextForNodeTypeCategory(hda_def.nodeTypeCategory()) + + if vex_context: + new_parent = parent_node.node("VOPS") + if not new_parent: + new_parent = parent_node.createNode("vopnet", "VOPS") + new_parent.moveToGoodPosition() + + if vex_context.name() == "Cop2": + return new_parent, "cop2filter", "COP_VOP" + elif vex_context.name() == "Chop": + return new_parent, "chop", "CHOP_VOP" + elif vex_context.name() == "Sop": + return new_parent, "sop", "SOP_VOP" + + # Fall to material net if no vex context found + return parent_node, "matnet", "MATS" From 01906888d959df4d3445b78f6e3bd29276bc5704 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Thu, 9 May 2024 16:28:14 +0300 Subject: [PATCH 017/129] update ToolLocation --- client/ayon_core/hosts/houdini/plugins/create/create_hda.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py index a810f06283..e6aeba270c 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py @@ -109,7 +109,8 @@ class CreateHDA(plugin.HoudiniCreator): if pre_create_data.get("use_project"): tool_name = hou.shelves.defaultToolName( hda_def.nodeTypeCategory().name(), hda_def.nodeTypeName()) - hou.shelves.tool(tool_name).setToolLocations((self.project_name,)) + hou.shelves.tool(tool_name).setToolLocations( + ("AYON/{}".format(self.project_name),)) return hda_node From f8d07c0045d8437838f2e4d56b0e0e6ad1bc1c74 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Thu, 9 May 2024 16:32:15 +0300 Subject: [PATCH 018/129] update Tooltip --- client/ayon_core/hosts/houdini/plugins/create/create_hda.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py index e6aeba270c..3ca82ccb60 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py @@ -150,7 +150,9 @@ class CreateHDA(plugin.HoudiniCreator): default=False, label="Set Current User"), BoolDef("use_project", - tooltip="Use project name as tab submenu path", + tooltip="Use project name as tab submenu path.\n" + "The location in TAB Menu will be\n" + "'AYON/project_name/your_HDA_name'", default=True, label="Use Project as menu entry"), ] From 4f532560f3c958fb6431e2a7cad22340ece7700b Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Thu, 9 May 2024 18:09:23 +0300 Subject: [PATCH 019/129] add use_project to creator_attributes --- .../ayon_core/hosts/houdini/plugins/create/create_hda.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py index 3ca82ccb60..462ae5eb7c 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py @@ -117,6 +117,13 @@ class CreateHDA(plugin.HoudiniCreator): def create(self, product_name, instance_data, pre_create_data): instance_data.pop("active", None) + # Transfer settings from pre create to instance + creator_attributes = instance_data.setdefault( + "creator_attributes", dict()) + for key in {"use_project"}: + if key in pre_create_data: + creator_attributes[key] = pre_create_data[key] + return super(CreateHDA, self).create( product_name, instance_data, From 9b4d36eee848b9dc8704b03f263a6c70c4d1e7a2 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Thu, 9 May 2024 18:09:50 +0300 Subject: [PATCH 020/129] set HDA TAB menu loaction interactively --- client/ayon_core/hosts/houdini/plugins/load/load_hda.py | 7 +++++++ .../hosts/houdini/plugins/publish/extract_hda.py | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_hda.py b/client/ayon_core/hosts/houdini/plugins/load/load_hda.py index b2d2337266..2faa403858 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_hda.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_hda.py @@ -59,6 +59,13 @@ class HdaLoader(load.LoaderPlugin): hda_node = parent_node.createNode(name, node_name) hda_node.moveToGoodPosition() + # Set TAB Menu location interactively + # This shouldn't be needed if the Tool Location is saved in the HDA. + tool_name = hou.shelves.defaultToolName( + hda_def.nodeTypeCategory().name(), hda_def.nodeTypeName()) + hou.shelves.tool(tool_name).setToolLocations( + ("AYON/{}".format(context["project"]["name"]),)) + # Imprint it manually data = { "schema": "openpype:container-2.0", diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_hda.py b/client/ayon_core/hosts/houdini/plugins/publish/extract_hda.py index 5fe83e0dcf..5ac102332a 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/extract_hda.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/extract_hda.py @@ -26,6 +26,14 @@ class ExtractHDA(publish.Extractor): hda_def.setOptions(hda_options) hda_def.save(hda_def.libraryFilePath(), hda_node, hda_options) + if instance.data["creator_attributes"].get("use_project"): + # Set TAB Menu location interactively + # This shouldn't be needed if the Tool Location is saved in the HDA. + tool_name = hou.shelves.defaultToolName( + hda_def.nodeTypeCategory().name(), hda_def.nodeTypeName()) + hou.shelves.tool(tool_name).setToolLocations( + ("AYON/{}".format(self.project_name),)) + if "representations" not in instance.data: instance.data["representations"] = [] From a5e664619457c2a41f4c97abd16ad4d7f1e4d57e Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Thu, 9 May 2024 18:13:55 +0300 Subject: [PATCH 021/129] use matnet as the parent node to load VOP HDAs --- .../hosts/houdini/plugins/load/load_hda.py | 36 +------------------ 1 file changed, 1 insertion(+), 35 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/load/load_hda.py b/client/ayon_core/hosts/houdini/plugins/load/load_hda.py index 2faa403858..9368c8d2fb 100644 --- a/client/ayon_core/hosts/houdini/plugins/load/load_hda.py +++ b/client/ayon_core/hosts/houdini/plugins/load/load_hda.py @@ -131,7 +131,7 @@ class HdaLoader(load.LoaderPlugin): node_type, node_name = "topnet", "TOPS" # TODO: Create a dedicated parent node based on Vop Node vex context. elif hda_def.nodeTypeCategory() == hou.vopNodeTypeCategory(): - parent_node, node_type, node_name = self._get_dedicated_parent_for_vop(parent_node, hda_def) + node_type, node_name = "matnet", "MATSandVOPS" node = parent_node.node(node_name) if not node: @@ -139,37 +139,3 @@ class HdaLoader(load.LoaderPlugin): node.moveToGoodPosition() return node - - def _get_dedicated_parent_for_vop(self, parent_node, hda_def): - """create_dedicated_parent_for_vop - - VOPs are a little special because they can exist - in different contexts. - e.g. - - Material Network - - SOPs > VOP nodes - - Chop > VOP nodes - - Cop2 > VOP nodes - - Get the vex context of the HDA and return - dedicated node_type and node_name. - - It creates intermediate nodes if needed. - """ - vex_context = hou.vexContextForNodeTypeCategory(hda_def.nodeTypeCategory()) - - if vex_context: - new_parent = parent_node.node("VOPS") - if not new_parent: - new_parent = parent_node.createNode("vopnet", "VOPS") - new_parent.moveToGoodPosition() - - if vex_context.name() == "Cop2": - return new_parent, "cop2filter", "COP_VOP" - elif vex_context.name() == "Chop": - return new_parent, "chop", "CHOP_VOP" - elif vex_context.name() == "Sop": - return new_parent, "sop", "SOP_VOP" - - # Fall to material net if no vex context found - return parent_node, "matnet", "MATS" From 943a36130f56f54aba49ba3e48b887344fea3ba4 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Thu, 9 May 2024 22:04:54 +0300 Subject: [PATCH 022/129] Update 'creator>product_name_profiles': add Houdini HDA template to default values --- client/ayon_core/version.py | 2 +- package.py | 3 +-- server/settings/tools.py | 11 +++++++++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index a60de0493a..a0def120c4 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON core addon version.""" -__version__ = "0.3.1-dev.1" +__version__ = "0.3.1-dev.2" diff --git a/package.py b/package.py index 79450d029f..2a84c68f35 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "0.3.1-dev.1" +version = "0.3.1-dev.2" client_dir = "ayon_core" @@ -8,4 +8,3 @@ plugin_for = ["ayon_server"] requires = [ "~ayon_server-1.0.3+<2.0.0", ] - diff --git a/server/settings/tools.py b/server/settings/tools.py index fb8430a71c..ce3ceaea6e 100644 --- a/server/settings/tools.py +++ b/server/settings/tools.py @@ -403,6 +403,17 @@ DEFAULT_TOOLS_VALUES = { "task_types": [], "tasks": [], "template": "SK_{folder[name]}{variant}" + }, + { + "product_types": [ + "hda" + ], + "hosts": [ + "houdini" + ], + "task_types": [], + "tasks": [], + "template": "{folder[label]}_{variant}" } ] }, From 95f33b2da2d175e29198855322d1f876bed25d28 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Thu, 9 May 2024 22:06:22 +0300 Subject: [PATCH 023/129] add folder name and label to dynamic data --- .../houdini/plugins/create/create_hda.py | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py index 462ae5eb7c..957da8f230 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py @@ -51,12 +51,12 @@ class CreateHDA(plugin.HoudiniCreator): # selected nodes ... if self.selected_nodes[0].type().name() == "subnet": to_hda = self.selected_nodes[0] - to_hda.setName("{}_subnet".format(node_name), unique_name=True) + to_hda.setName("{}_HDA".format(node_name), unique_name=True) else: parent_node = self.selected_nodes[0].parent() subnet = parent_node.collapseIntoSubnet( self.selected_nodes, - subnet_name="{}_subnet".format(node_name)) + subnet_name="{}_HDA".format(node_name)) subnet.moveToGoodPosition() to_hda = subnet else: @@ -69,7 +69,7 @@ class CreateHDA(plugin.HoudiniCreator): parent_node = pane.pwd() to_hda = parent_node.createNode( - "subnet", node_name="{}_subnet".format(node_name)) + "subnet", node_name="{}_HDA".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. @@ -163,3 +163,30 @@ class CreateHDA(plugin.HoudiniCreator): default=True, label="Use Project as menu entry"), ] + + def get_dynamic_data( + self, + project_name, + folder_entity, + task_entity, + variant, + host_name, + instance + ): + """ + Pass product name from product name templates as dynamic data. + """ + dynamic_data = super(CreateHDA, self).get_dynamic_data( + project_name, + folder_entity, + task_entity, + variant, + host_name, + instance + ) + + dynamic_data["folder"] = { + "label": folder_entity["label"], + "name": folder_entity["name"] + } + return dynamic_data From 389288a50649526c1c438cea5b720e32a0c7f1d7 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Thu, 9 May 2024 22:06:40 +0300 Subject: [PATCH 024/129] add hda to families --- .../hosts/houdini/plugins/publish/validate_subset_name.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py index 0481929824..1bcd2dada2 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py @@ -25,7 +25,7 @@ class ValidateSubsetName(pyblish.api.InstancePlugin, """ - families = ["staticMesh"] + families = ["staticMesh", "hda"] hosts = ["houdini"] label = "Validate Product Name" order = ValidateContentsOrder + 0.1 From fa8b4e808b8f45a1f1672f015bf8d46ea61bd4a6 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 10 May 2024 12:04:33 +0300 Subject: [PATCH 025/129] fux bug, get project name from context --- client/ayon_core/hosts/houdini/plugins/publish/extract_hda.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_hda.py b/client/ayon_core/hosts/houdini/plugins/publish/extract_hda.py index 5ac102332a..1a3bcacc38 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/extract_hda.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/extract_hda.py @@ -32,7 +32,7 @@ class ExtractHDA(publish.Extractor): tool_name = hou.shelves.defaultToolName( hda_def.nodeTypeCategory().name(), hda_def.nodeTypeName()) hou.shelves.tool(tool_name).setToolLocations( - ("AYON/{}".format(self.project_name),)) + ("AYON/{}".format(instance.context.data["projectName"]),)) if "representations" not in instance.data: instance.data["representations"] = [] From bc2c9479d557464be6d8b001f73bc6d66e9a35cf Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 10 May 2024 12:05:17 +0300 Subject: [PATCH 026/129] add missing dynamic data keys --- .../hosts/houdini/plugins/publish/validate_subset_name.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py index 1bcd2dada2..6c79687d88 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/validate_subset_name.py @@ -67,7 +67,13 @@ class ValidateSubsetName(pyblish.api.InstancePlugin, instance.context.data["hostName"], instance.data["productType"], variant=instance.data["variant"], - dynamic_data={"asset": folder_entity["name"]} + dynamic_data={ + "asset": folder_entity["name"], + "folder": { + "label": folder_entity["label"], + "name": folder_entity["name"] + } + } ) if instance.data.get("productName") != product_name: From e95fcbe995ce018564cf235cf8e3da795bd384d6 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 10 May 2024 23:39:51 +0800 Subject: [PATCH 027/129] add joint into accepted_controllers --- .../hosts/maya/plugins/publish/validate_animated_reference.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_animated_reference.py b/client/ayon_core/hosts/maya/plugins/publish/validate_animated_reference.py index 2ba2bff6fc..c9dcc662af 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_animated_reference.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_animated_reference.py @@ -16,7 +16,7 @@ class ValidateAnimatedReferenceRig(pyblish.api.InstancePlugin, hosts = ["maya"] families = ["animation.fbx"] label = "Animated Reference Rig" - accepted_controllers = ["transform", "locator"] + accepted_controllers = ["transform", "locator", "joint"] actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction] optional = False From 2f98c006b2c80ab798b39cf5593cc9f27bc80b5d Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Fri, 10 May 2024 16:45:05 +0100 Subject: [PATCH 028/129] Implemented deadline file dependencies for Blender --- .../publish/collect_file_dependencies.py | 36 +++++++++++++++++++ .../publish/submit_blender_deadline.py | 13 ++++++- server_addon/deadline/package.py | 2 +- .../server/settings/publish_plugins.py | 2 ++ 4 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 client/ayon_core/hosts/blender/plugins/publish/collect_file_dependencies.py diff --git a/client/ayon_core/hosts/blender/plugins/publish/collect_file_dependencies.py b/client/ayon_core/hosts/blender/plugins/publish/collect_file_dependencies.py new file mode 100644 index 0000000000..ea36ab459c --- /dev/null +++ b/client/ayon_core/hosts/blender/plugins/publish/collect_file_dependencies.py @@ -0,0 +1,36 @@ +from pathlib import Path + +import pyblish.api + +import bpy + + +class CollectFileDependencies(pyblish.api.ContextPlugin): + """Gather all files referenced in this scene.""" + + label = "Collect File Dependencies" + order = pyblish.api.CollectorOrder - 0.49 + hosts = ["blender"] + families = ["render"] + + @classmethod + def apply_settings(cls, project_settings): + # Disable plug-in if not used for deadline submission anyway + settings = project_settings["deadline"]["publish"]["BlenderSubmitDeadline"] # noqa + cls.enabled = settings.get("asset_dependencies", True) + + def process(self, context): + dependencies = set() + + # Add alembic files as dependencies + for cache in bpy.data.cache_files: + dependencies.add( + Path(bpy.path.abspath(cache.filepath)).resolve().as_posix()) + + # Add image files as dependencies + for image in bpy.data.images: + if image.filepath: + dependencies.add(Path( + bpy.path.abspath(image.filepath)).resolve().as_posix()) + + context.data["fileDependencies"] = list(dependencies) diff --git a/client/ayon_core/modules/deadline/plugins/publish/submit_blender_deadline.py b/client/ayon_core/modules/deadline/plugins/publish/submit_blender_deadline.py index f5805beb5c..94fc8ed96d 100644 --- a/client/ayon_core/modules/deadline/plugins/publish/submit_blender_deadline.py +++ b/client/ayon_core/modules/deadline/plugins/publish/submit_blender_deadline.py @@ -40,6 +40,17 @@ class BlenderSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline, group = None job_delay = "00:00:00:00" + @classmethod + def apply_settings(cls, project_settings): + settings = project_settings["deadline"]["publish"]["BlenderSubmitDeadline"] # noqa + + # Take some defaults from settings + cls.asset_dependencies = settings.get("asset_dependencies", + cls.asset_dependencies) + cls.use_published = settings.get("use_published", cls.use_published) + cls.priority = settings.get("priority", cls.priority) + cls.group = settings.get("group", cls.group) + def get_job_info(self): job_info = DeadlineJobInfo(Plugin="Blender") @@ -161,7 +172,7 @@ class BlenderSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline, return plugin_payload - def process_submission(self): + def process_submission(self, auth=None): instance = self._instance expected_files = instance.data["expectedFiles"] diff --git a/server_addon/deadline/package.py b/server_addon/deadline/package.py index 25ba1c1166..e26734c813 100644 --- a/server_addon/deadline/package.py +++ b/server_addon/deadline/package.py @@ -1,3 +1,3 @@ name = "deadline" title = "Deadline" -version = "0.1.11" +version = "0.1.12" diff --git a/server_addon/deadline/server/settings/publish_plugins.py b/server_addon/deadline/server/settings/publish_plugins.py index 784ad2560b..85a93d49cd 100644 --- a/server_addon/deadline/server/settings/publish_plugins.py +++ b/server_addon/deadline/server/settings/publish_plugins.py @@ -270,6 +270,7 @@ class BlenderSubmitDeadlineModel(BaseSettingsModel): optional: bool = SettingsField(title="Optional") active: bool = SettingsField(title="Active") use_published: bool = SettingsField(title="Use Published scene") + asset_dependencies: bool = SettingsField(title="Use Asset dependencies") priority: int = SettingsField(title="Priority") chunk_size: int = SettingsField(title="Frame per Task") group: str = SettingsField("", title="Group Name") @@ -413,6 +414,7 @@ DEFAULT_DEADLINE_PLUGINS_SETTINGS = { "optional": False, "active": True, "use_published": True, + "asset_dependencies": True, "priority": 50, "chunk_size": 10, "group": "none", From c02d5522f3d6617c27fd4fe9da2e0c5de999f503 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 10 May 2024 23:58:34 +0800 Subject: [PATCH 029/129] make sure the animation doesnt get resample --- .../hosts/maya/plugins/publish/extract_fbx_animation.py | 1 + 1 file changed, 1 insertion(+) diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_fbx_animation.py b/client/ayon_core/hosts/maya/plugins/publish/extract_fbx_animation.py index ee66ed2fb7..36dc1b1544 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_fbx_animation.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_fbx_animation.py @@ -36,6 +36,7 @@ class ExtractFBXAnimation(publish.Extractor): out_members = instance.data.get("animated_skeleton", []) # Export instance.data["constraints"] = True + instance.data["bakeResampleAnimation"] = False instance.data["skeletonDefinitions"] = True instance.data["referencedAssetsContent"] = True fbx_exporter.set_options_from_instance(instance) From 1b70c0a6a4bd08815ab680549b7dd99f199c85ec Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 10 May 2024 23:59:19 +0800 Subject: [PATCH 030/129] revert commits --- .../hosts/maya/plugins/publish/extract_fbx_animation.py | 1 - .../hosts/maya/plugins/publish/validate_animated_reference.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/client/ayon_core/hosts/maya/plugins/publish/extract_fbx_animation.py b/client/ayon_core/hosts/maya/plugins/publish/extract_fbx_animation.py index 36dc1b1544..ee66ed2fb7 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/extract_fbx_animation.py +++ b/client/ayon_core/hosts/maya/plugins/publish/extract_fbx_animation.py @@ -36,7 +36,6 @@ class ExtractFBXAnimation(publish.Extractor): out_members = instance.data.get("animated_skeleton", []) # Export instance.data["constraints"] = True - instance.data["bakeResampleAnimation"] = False instance.data["skeletonDefinitions"] = True instance.data["referencedAssetsContent"] = True fbx_exporter.set_options_from_instance(instance) diff --git a/client/ayon_core/hosts/maya/plugins/publish/validate_animated_reference.py b/client/ayon_core/hosts/maya/plugins/publish/validate_animated_reference.py index c9dcc662af..2ba2bff6fc 100644 --- a/client/ayon_core/hosts/maya/plugins/publish/validate_animated_reference.py +++ b/client/ayon_core/hosts/maya/plugins/publish/validate_animated_reference.py @@ -16,7 +16,7 @@ class ValidateAnimatedReferenceRig(pyblish.api.InstancePlugin, hosts = ["maya"] families = ["animation.fbx"] label = "Animated Reference Rig" - accepted_controllers = ["transform", "locator", "joint"] + accepted_controllers = ["transform", "locator"] actions = [ayon_core.hosts.maya.api.action.SelectInvalidAction] optional = False From 08b9f3d3c05ea38279853bc9a4b7c464b412219a Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 17 May 2024 23:20:18 +0200 Subject: [PATCH 031/129] Fix #522: Validate workfile paths skip parms with keyframes --- .../houdini/plugins/publish/validate_workfile_paths.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/client/ayon_core/hosts/houdini/plugins/publish/validate_workfile_paths.py b/client/ayon_core/hosts/houdini/plugins/publish/validate_workfile_paths.py index 7984b7615c..cc10407df6 100644 --- a/client/ayon_core/hosts/houdini/plugins/publish/validate_workfile_paths.py +++ b/client/ayon_core/hosts/houdini/plugins/publish/validate_workfile_paths.py @@ -69,6 +69,12 @@ class ValidateWorkfilePaths( if param.node().type().name() not in cls.node_types: continue + if param.keyframes(): + # Calling `.unexpandedString()` below fails if param has + # keyframes - so for now we will skip those params. These are + # e.g. present in `filecache` nodes. + continue + if any( v for v in cls.prohibited_vars if v in param.unexpandedString()): From 888c4ff35434588fc28cc468753a1ac9ce05001f Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Thu, 23 May 2024 12:18:36 +0300 Subject: [PATCH 032/129] revert changes to ayon-core version --- client/ayon_core/version.py | 2 +- package.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index 28731bcb47..275e1b1dd6 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON core addon version.""" -__version__ = "0.3.2-dev.2" +__version__ = "0.3.2-dev.1" diff --git a/package.py b/package.py index f5d1ee3d03..b7b8d2dae6 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "0.3.2-dev.2" +version = "0.3.2-dev.1" client_dir = "ayon_core" From d54ddf2210d5f023837abf5ddcf26df760600158 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Thu, 23 May 2024 12:25:39 +0300 Subject: [PATCH 033/129] replace getpass.getuser() with get_ayon_username() --- .../ayon_core/hosts/houdini/plugins/create/create_hda.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py index 957da8f230..7f270b5cc2 100644 --- a/client/ayon_core/hosts/houdini/plugins/create/create_hda.py +++ b/client/ayon_core/hosts/houdini/plugins/create/create_hda.py @@ -1,10 +1,12 @@ # -*- coding: utf-8 -*- """Creator plugin for creating publishable Houdini Digital Assets.""" import ayon_api -import getpass from ayon_core.pipeline import CreatorError from ayon_core.hosts.houdini.api import plugin -from ayon_core.lib import BoolDef +from ayon_core.lib import ( + get_ayon_username, + BoolDef +) import hou from ayon_core.resources import get_ayon_icon_filepath @@ -104,7 +106,7 @@ class CreateHDA(plugin.HoudiniCreator): hda_def.setIcon(get_ayon_icon_filepath()) if pre_create_data.get("set_user"): - hda_def.setUserInfo(getpass.getuser()) + hda_def.setUserInfo(get_ayon_username()) if pre_create_data.get("use_project"): tool_name = hou.shelves.defaultToolName( From 0d49b83b33d172988808ab699fed4837ba61e6b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Fri, 31 May 2024 12:49:42 +0200 Subject: [PATCH 034/129] :recycle: check for invalid reference compatible with maya 2022 --- server_addon/maya/client/ayon_maya/api/lib.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/server_addon/maya/client/ayon_maya/api/lib.py b/server_addon/maya/client/ayon_maya/api/lib.py index 2b41ffc06c..b9b0795ebf 100644 --- a/server_addon/maya/client/ayon_maya/api/lib.py +++ b/server_addon/maya/client/ayon_maya/api/lib.py @@ -1721,7 +1721,7 @@ def is_valid_reference_node(reference_node): Reference node 'reference_node' is not associated with a reference file. Note that this does *not* check whether the reference node points to an - existing file. Instead it only returns whether maya considers it valid + existing file. Instead, it only returns whether maya considers it valid and thus is not an unassociated reference node Arguments: @@ -1734,6 +1734,15 @@ def is_valid_reference_node(reference_node): sel = OpenMaya.MSelectionList() sel.add(reference_node) depend_node = sel.getDependNode(0) + # maya 2022 is missing `isValidReference` so the check needs to be + # done in different way. + if cmds.about(version=True) < 2023: + try: + cmds.referenceQuery(reference_node, filename=True) + return True + except RuntimeError: + return False + return OpenMaya.MFnReference(depend_node).isValidReference() From f482c9e788364fc4f434be1dd2c0f3c8e4390cb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= <33513211+antirotor@users.noreply.github.com> Date: Fri, 31 May 2024 17:34:18 +0200 Subject: [PATCH 035/129] Update server_addon/maya/client/ayon_maya/api/lib.py Co-authored-by: Roy Nieterau --- server_addon/maya/client/ayon_maya/api/lib.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server_addon/maya/client/ayon_maya/api/lib.py b/server_addon/maya/client/ayon_maya/api/lib.py index b9b0795ebf..3b351ec1f0 100644 --- a/server_addon/maya/client/ayon_maya/api/lib.py +++ b/server_addon/maya/client/ayon_maya/api/lib.py @@ -1731,9 +1731,6 @@ def is_valid_reference_node(reference_node): bool: Whether reference node is a valid reference """ - sel = OpenMaya.MSelectionList() - sel.add(reference_node) - depend_node = sel.getDependNode(0) # maya 2022 is missing `isValidReference` so the check needs to be # done in different way. if cmds.about(version=True) < 2023: @@ -1742,6 +1739,9 @@ def is_valid_reference_node(reference_node): return True except RuntimeError: return False + sel = OpenMaya.MSelectionList() + sel.add(reference_node) + depend_node = sel.getDependNode(0) return OpenMaya.MFnReference(depend_node).isValidReference() From 8214147db157ee28815d3e0b69bf51240e694d9b Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 3 Jun 2024 17:22:17 +0200 Subject: [PATCH 036/129] Bump version --- server_addon/houdini/client/ayon_houdini/version.py | 2 +- server_addon/houdini/package.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/version.py b/server_addon/houdini/client/ayon_houdini/version.py index a30c770e1d..eff044feba 100644 --- a/server_addon/houdini/client/ayon_houdini/version.py +++ b/server_addon/houdini/client/ayon_houdini/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Houdini addon version.""" -__version__ = "0.3.0" +__version__ = "0.3.1" diff --git a/server_addon/houdini/package.py b/server_addon/houdini/package.py index 275d21c1bf..a8884ff60a 100644 --- a/server_addon/houdini/package.py +++ b/server_addon/houdini/package.py @@ -1,6 +1,6 @@ name = "houdini" title = "Houdini" -version = "0.3.0" +version = "0.3.1" client_dir = "ayon_houdini" From 0cccc9a7e8d52abcf7a03750a90d323c945d0d54 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Mon, 3 Jun 2024 21:02:43 +0300 Subject: [PATCH 037/129] add missing dynamic data keys in repair action --- .../ayon_houdini/plugins/publish/validate_subset_name.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_subset_name.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_subset_name.py index 999dcc3981..4f15f193fc 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_subset_name.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_subset_name.py @@ -103,7 +103,13 @@ class ValidateSubsetName(plugin.HoudiniInstancePlugin, instance.context.data["hostName"], instance.data["productType"], variant=instance.data["variant"], - dynamic_data={"asset": folder_entity["name"]} + dynamic_data={ + "asset": folder_entity["name"], + "folder": { + "label": folder_entity["label"], + "name": folder_entity["name"] + } + } ) instance.data["productName"] = product_name From 17ec19f5069860be184733254db79f4a86308de5 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Wed, 12 Jun 2024 22:24:37 +0300 Subject: [PATCH 038/129] set tab shelf location properly and revert unnecessary logic --- .../client/ayon_houdini/plugins/create/create_hda.py | 7 +++---- .../houdini/client/ayon_houdini/plugins/load/load_hda.py | 7 ------- .../client/ayon_houdini/plugins/publish/extract_hda.py | 8 -------- 3 files changed, 3 insertions(+), 19 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/create/create_hda.py b/server_addon/houdini/client/ayon_houdini/plugins/create/create_hda.py index c727fe0b09..e0929e16f7 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/create/create_hda.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/create/create_hda.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- """Creator plugin for creating publishable Houdini Digital Assets.""" import hou +from assettools import setToolSubmenu + import ayon_api from ayon_core.pipeline import CreatorError from ayon_core.lib import ( @@ -109,10 +111,7 @@ class CreateHDA(plugin.HoudiniCreator): hda_def.setUserInfo(get_ayon_username()) if pre_create_data.get("use_project"): - tool_name = hou.shelves.defaultToolName( - hda_def.nodeTypeCategory().name(), hda_def.nodeTypeName()) - hou.shelves.tool(tool_name).setToolLocations( - ("AYON/{}".format(self.project_name),)) + setToolSubmenu(hda_def, "AYON/{}".format(self.project_name)) return hda_node diff --git a/server_addon/houdini/client/ayon_houdini/plugins/load/load_hda.py b/server_addon/houdini/client/ayon_houdini/plugins/load/load_hda.py index 3ee487f496..85477965cd 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/load/load_hda.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/load/load_hda.py @@ -62,13 +62,6 @@ class HdaLoader(plugin.HoudiniLoader): hda_node = parent_node.createNode(name, node_name) hda_node.moveToGoodPosition() - # Set TAB Menu location interactively - # This shouldn't be needed if the Tool Location is saved in the HDA. - tool_name = hou.shelves.defaultToolName( - hda_def.nodeTypeCategory().name(), hda_def.nodeTypeName()) - hou.shelves.tool(tool_name).setToolLocations( - ("AYON/{}".format(context["project"]["name"]),)) - # Imprint it manually data = { "schema": "openpype:container-2.0", diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_hda.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_hda.py index 0ff7948a88..e4449d11f8 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_hda.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_hda.py @@ -25,14 +25,6 @@ class ExtractHDA(plugin.HoudiniExtractorPlugin): hda_def.setOptions(hda_options) hda_def.save(hda_def.libraryFilePath(), hda_node, hda_options) - if instance.data["creator_attributes"].get("use_project"): - # Set TAB Menu location interactively - # This shouldn't be needed if the Tool Location is saved in the HDA. - tool_name = hou.shelves.defaultToolName( - hda_def.nodeTypeCategory().name(), hda_def.nodeTypeName()) - hou.shelves.tool(tool_name).setToolLocations( - ("AYON/{}".format(instance.context.data["projectName"]),)) - if "representations" not in instance.data: instance.data["representations"] = [] From 45f72649464dc6071e6c92789de2176c411133cf Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Wed, 12 Jun 2024 22:47:36 +0300 Subject: [PATCH 039/129] revert changes in subnetwork naming, use postfix _subnet --- .../client/ayon_houdini/plugins/create/create_hda.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/create/create_hda.py b/server_addon/houdini/client/ayon_houdini/plugins/create/create_hda.py index e0929e16f7..18b26bb533 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/create/create_hda.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/create/create_hda.py @@ -55,12 +55,12 @@ class CreateHDA(plugin.HoudiniCreator): # selected nodes ... if self.selected_nodes[0].type().name() == "subnet": to_hda = self.selected_nodes[0] - to_hda.setName("{}_HDA".format(node_name), unique_name=True) + to_hda.setName("{}_subnet".format(node_name), unique_name=True) else: parent_node = self.selected_nodes[0].parent() subnet = parent_node.collapseIntoSubnet( self.selected_nodes, - subnet_name="{}_HDA".format(node_name)) + subnet_name="{}_subnet".format(node_name)) subnet.moveToGoodPosition() to_hda = subnet else: @@ -73,7 +73,7 @@ class CreateHDA(plugin.HoudiniCreator): parent_node = pane.pwd() to_hda = parent_node.createNode( - "subnet", node_name="{}_HDA".format(node_name)) + "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. From 65e8b0383ac4233d7d625ac7c6bfcae231e899f3 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Thu, 13 Jun 2024 22:29:52 +0300 Subject: [PATCH 040/129] update default hda product name profile --- server/settings/tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/settings/tools.py b/server/settings/tools.py index f73287eafd..3ed12d3d0a 100644 --- a/server/settings/tools.py +++ b/server/settings/tools.py @@ -458,7 +458,7 @@ DEFAULT_TOOLS_VALUES = { ], "task_types": [], "tasks": [], - "template": "{folder[label]}_{variant}" + "template": "{folder[name]}_{variant}" } ], "filter_creator_profiles": [] From 503e8bce3d30ea03d622bea1196e6ff3ff963cb3 Mon Sep 17 00:00:00 2001 From: Mustafa Taher Date: Thu, 13 Jun 2024 22:36:29 +0300 Subject: [PATCH 041/129] skip str casting Co-authored-by: Kayla Man <64118225+moonyuet@users.noreply.github.com> --- .../houdini/client/ayon_houdini/plugins/load/load_hda.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/load/load_hda.py b/server_addon/houdini/client/ayon_houdini/plugins/load/load_hda.py index 85477965cd..46fcb74b2c 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/load/load_hda.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/load/load_hda.py @@ -68,7 +68,7 @@ class HdaLoader(plugin.HoudiniLoader): "id": AVALON_CONTAINER_ID, "name": node_name, "namespace": namespace, - "loader": str(self.__class__.__name__), + "loader": self.__class__.__name__, "representation": context["representation"]["id"], } From 121fa8a98b1f204b0d20d004e4178c414c69d772 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 14 Jun 2024 16:50:19 +0300 Subject: [PATCH 042/129] revert 'use ayon icon as HDA icon' --- .../client/ayon_houdini/plugins/create/create_hda.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/create/create_hda.py b/server_addon/houdini/client/ayon_houdini/plugins/create/create_hda.py index 18b26bb533..1b9ffb1b5e 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/create/create_hda.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/create/create_hda.py @@ -9,7 +9,7 @@ from ayon_core.lib import ( get_ayon_username, BoolDef ) -from ayon_core.resources import get_ayon_icon_filepath + from ayon_houdini.api import plugin @@ -104,9 +104,6 @@ class CreateHDA(plugin.HoudiniCreator): # Set Custom settings. hda_def = hda_node.type().definition() - if pre_create_data.get("use_ayon_icon"): - hda_def.setIcon(get_ayon_icon_filepath()) - if pre_create_data.get("set_user"): hda_def.setUserInfo(get_ayon_username()) @@ -149,10 +146,6 @@ class CreateHDA(plugin.HoudiniCreator): def get_pre_create_attr_defs(self): attrs = super(CreateHDA, self).get_pre_create_attr_defs() return attrs + [ - BoolDef("use_ayon_icon", - tooltip="Use Ayon icon for the digital asset.", - default=False, - label="Use AYON Icon"), BoolDef("set_user", tooltip="Set current user as the author of the HDA", default=False, From eea56697995c664107eeeb9358c096021ef06e52 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 14 Jun 2024 17:31:10 +0300 Subject: [PATCH 043/129] don't transfere creator attribute --- .../client/ayon_houdini/plugins/create/create_hda.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/create/create_hda.py b/server_addon/houdini/client/ayon_houdini/plugins/create/create_hda.py index 72107bef72..e03f290ae8 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/create/create_hda.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/create/create_hda.py @@ -128,13 +128,6 @@ class CreateHDA(plugin.HoudiniCreator): def create(self, product_name, instance_data, pre_create_data): instance_data.pop("active", None) - # Transfer settings from pre create to instance - creator_attributes = instance_data.setdefault( - "creator_attributes", dict()) - for key in {"use_project"}: - if key in pre_create_data: - creator_attributes[key] = pre_create_data[key] - return super(CreateHDA, self).create( product_name, instance_data, From 71be4e99a7e04356ea3c3912ab9706dbf99b3333 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 14 Jun 2024 17:40:06 +0300 Subject: [PATCH 044/129] use the last HDA Def --- .../houdini/client/ayon_houdini/plugins/load/load_hda.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/load/load_hda.py b/server_addon/houdini/client/ayon_houdini/plugins/load/load_hda.py index 289b8beb69..eb9a74a7ad 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/load/load_hda.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/load/load_hda.py @@ -55,10 +55,10 @@ class HdaLoader(plugin.HoudiniLoader): if not hda_defs: raise LoadError(f"No HDA definitions found in file: {file_path}") - parent_node = self._create_dedicated_parent_node(hda_defs[0]) + parent_node = self._create_dedicated_parent_node(hda_defs[-1]) # Get the type name from the HDA definition. - type_name = hda_defs[0].nodeTypeName() + type_name = hda_defs[-1].nodeTypeName() hda_node = parent_node.createNode(type_name, node_name) hda_node.moveToGoodPosition() From 8166e4b8236676307951cd754c49c25b1792e321 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 17 Jun 2024 15:15:28 +0200 Subject: [PATCH 045/129] pass in sender --- client/ayon_core/tools/utils/tasks_widget.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/utils/tasks_widget.py b/client/ayon_core/tools/utils/tasks_widget.py index 314521b6b0..bba7b93925 100644 --- a/client/ayon_core/tools/utils/tasks_widget.py +++ b/client/ayon_core/tools/utils/tasks_widget.py @@ -191,12 +191,12 @@ class TasksQtModel(QtGui.QStandardItemModel): def _thread_getter(self, project_name, folder_id): task_items = self._controller.get_task_items( - project_name, folder_id + project_name, folder_id, sender=TASKS_MODEL_SENDER_NAME ) task_type_items = {} if hasattr(self._controller, "get_task_type_items"): task_type_items = self._controller.get_task_type_items( - project_name + project_name, sender=TASKS_MODEL_SENDER_NAME ) return task_items, task_type_items @@ -580,6 +580,7 @@ class TasksWidget(QtWidgets.QWidget): return if expected_data is None: expected_data = self._controller.get_expected_selection_data() + folder_data = expected_data.get("folder") task_data = expected_data.get("task") if ( From 50b4779ad35ec7cc2f793b467c4d5f2eedd0bcf3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 17 Jun 2024 15:15:41 +0200 Subject: [PATCH 046/129] wait for refresh to finish --- client/ayon_core/tools/common_models/hierarchy.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/common_models/hierarchy.py b/client/ayon_core/tools/common_models/hierarchy.py index a90e06a63b..f92563db20 100644 --- a/client/ayon_core/tools/common_models/hierarchy.py +++ b/client/ayon_core/tools/common_models/hierarchy.py @@ -1,3 +1,4 @@ +import time import collections import contextlib from abc import ABCMeta, abstractmethod @@ -535,13 +536,15 @@ class HierarchyModel(object): def _refresh_tasks_cache(self, project_name, folder_id, sender=None): if folder_id in self._tasks_refreshing: + while folder_id in self._tasks_refreshing: + time.sleep(0.01) return + cache = self._task_items[project_name][folder_id] with self._task_refresh_event_manager( project_name, folder_id, sender ): - task_items = self._query_tasks(project_name, folder_id) - self._task_items[project_name][folder_id] = task_items + cache.update_data(self._query_tasks(project_name, folder_id)) def _query_tasks(self, project_name, folder_id): tasks = list(ayon_api.get_tasks( From e6192095d4b47b41797a72402598d178cf1ff9ad Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 17 Jun 2024 17:32:08 +0200 Subject: [PATCH 047/129] AY-5750 - put order after collect_review This plugin depends on review in families logic which got put there only after this plugin (because of previous move order to accommodate deadline collection data). This plugin must run after collect_review BUT before collect DL credentials! --- .../plugins/publish/collect_render.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/server_addon/aftereffects/client/ayon_aftereffects/plugins/publish/collect_render.py b/server_addon/aftereffects/client/ayon_aftereffects/plugins/publish/collect_render.py index 3508512cba..9615154185 100644 --- a/server_addon/aftereffects/client/ayon_aftereffects/plugins/publish/collect_render.py +++ b/server_addon/aftereffects/client/ayon_aftereffects/plugins/publish/collect_render.py @@ -23,8 +23,16 @@ class AERenderInstance(RenderInstance): class CollectAERender(publish.AbstractCollectRender): + """Prepares RenderInstance. - order = pyblish.api.CollectorOrder + 0.100 + RenderInstance is meant to replace simple dictionaries to provide code + assist and typing. (Currently used only in AE, Harmony though.) + + This must run after `collect_review`, but before Deadline plugins (which + should be run only on renderable instances.) + """ + + order = pyblish.api.CollectorOrder + 0.125 label = "Collect After Effects Render Layers" hosts = ["aftereffects"] From fa747699c21018ed021d7930ad98ef46cec7febb Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 17 Jun 2024 17:32:38 +0200 Subject: [PATCH 048/129] AY-5750 - fix superfluous space --- .../client/ayon_aftereffects/plugins/create/create_render.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server_addon/aftereffects/client/ayon_aftereffects/plugins/create/create_render.py b/server_addon/aftereffects/client/ayon_aftereffects/plugins/create/create_render.py index eafe845247..8e44b51a1c 100644 --- a/server_addon/aftereffects/client/ayon_aftereffects/plugins/create/create_render.py +++ b/server_addon/aftereffects/client/ayon_aftereffects/plugins/create/create_render.py @@ -94,7 +94,7 @@ class RenderCreator(Creator): new_instance.creator_attributes["farm"] = use_farm review = pre_create_data["mark_for_review"] - new_instance. creator_attributes["mark_for_review"] = review + new_instance.creator_attributes["mark_for_review"] = review api.get_stub().imprint(new_instance.id, new_instance.data_to_store()) From f7e9e413fbd69fdc83b34b26d4fded69abcec2fe Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 17 Jun 2024 18:22:18 +0200 Subject: [PATCH 049/129] AY-5750 - fix single frame render Without it will render frame 0 which might result in black image instead of correct frame (1001). --- .../plugins/publish/submit_aftereffects_deadline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server_addon/deadline/client/ayon_deadline/plugins/publish/submit_aftereffects_deadline.py b/server_addon/deadline/client/ayon_deadline/plugins/publish/submit_aftereffects_deadline.py index 9ca077d162..c61e6cd35b 100644 --- a/server_addon/deadline/client/ayon_deadline/plugins/publish/submit_aftereffects_deadline.py +++ b/server_addon/deadline/client/ayon_deadline/plugins/publish/submit_aftereffects_deadline.py @@ -57,7 +57,7 @@ class AfterEffectsSubmitDeadline( dln_job_info.Plugin = "AfterEffects" dln_job_info.UserName = context.data.get( "deadlineUser", getpass.getuser()) - if self._instance.data["frameEnd"] > self._instance.data["frameStart"]: + if self._instance.data["frameEnd"] >= self._instance.data["frameStart"]: # noqa # Deadline requires integers in frame range frame_range = "{}-{}".format( int(round(self._instance.data["frameStart"])), From 50f39e4f5e4f006c046d8b7176c2313a7c5c45c7 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 17 Jun 2024 18:24:48 +0200 Subject: [PATCH 050/129] AY-5750 - removed folderPath from expected_files Folder path messed up os.path.join, resulting in wrong file path pointing to c:/file_name.png. Folder path is actually not needed here, it is already included in base_dir. --- .../plugins/publish/collect_render.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/server_addon/aftereffects/client/ayon_aftereffects/plugins/publish/collect_render.py b/server_addon/aftereffects/client/ayon_aftereffects/plugins/publish/collect_render.py index 9615154185..e0cf1b10d6 100644 --- a/server_addon/aftereffects/client/ayon_aftereffects/plugins/publish/collect_render.py +++ b/server_addon/aftereffects/client/ayon_aftereffects/plugins/publish/collect_render.py @@ -181,24 +181,25 @@ class CollectAERender(publish.AbstractCollectRender): _, ext = os.path.splitext(os.path.basename(file_name)) ext = ext.replace('.', '') version_str = "v{:03d}".format(render_instance.version) - if "#" not in file_name: # single frame (mov)W - path = os.path.join(base_dir, "{}_{}_{}.{}".format( - render_instance.folderPath, + if "#" not in file_name: # single frame (mov) + file_name = "{}_{}.{}".format( render_instance.productName, version_str, ext - )) - expected_files.append(path) + ) + file_path = os.path.join(base_dir, file_name) + expected_files.append(file_path) else: for frame in range(start, end + 1): - path = os.path.join(base_dir, "{}_{}_{}.{}.{}".format( - render_instance.folderPath, + file_name = "{}_{}.{}.{}".format( render_instance.productName, version_str, str(frame).zfill(self.padding_width), ext - )) - expected_files.append(path) + ) + + file_path = os.path.join(base_dir, file_name) + expected_files.append(file_path) return expected_files def _get_output_dir(self, render_instance): From 565188e772875203d9beb1df52b157798c06b5ac Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 17 Jun 2024 18:38:21 +0200 Subject: [PATCH 051/129] fix deprecated import in loader tool --- client/ayon_core/tools/loader/control.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/ayon_core/tools/loader/control.py b/client/ayon_core/tools/loader/control.py index ee0b2f4b9d..f4b00e985f 100644 --- a/client/ayon_core/tools/loader/control.py +++ b/client/ayon_core/tools/loader/control.py @@ -3,14 +3,13 @@ import uuid import ayon_api +from ayon_core.lib import NestedCacheItem, CacheItem from ayon_core.lib.events import QueuedEventSystem from ayon_core.pipeline import Anatomy, get_current_context from ayon_core.host import ILoadHost from ayon_core.tools.common_models import ( ProjectsModel, HierarchyModel, - NestedCacheItem, - CacheItem, ThumbnailsModel, ) From 3ee74b92b9bcd4b72f1cc06f8b91adb58aa48f1a Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 18 Jun 2024 10:22:23 +0200 Subject: [PATCH 052/129] bump version to 0.3.2 --- server_addon/houdini/client/ayon_houdini/version.py | 2 +- server_addon/houdini/package.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/version.py b/server_addon/houdini/client/ayon_houdini/version.py index 87d445d563..10d1478249 100644 --- a/server_addon/houdini/client/ayon_houdini/version.py +++ b/server_addon/houdini/client/ayon_houdini/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'houdini' version.""" -__version__ = "0.3.1" +__version__ = "0.3.2" diff --git a/server_addon/houdini/package.py b/server_addon/houdini/package.py index a8884ff60a..1f7879483e 100644 --- a/server_addon/houdini/package.py +++ b/server_addon/houdini/package.py @@ -1,6 +1,6 @@ name = "houdini" title = "Houdini" -version = "0.3.1" +version = "0.3.2" client_dir = "ayon_houdini" From b62d40ffd9da18a66873c78b2fd1280c15eed365 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 18 Jun 2024 11:11:48 +0200 Subject: [PATCH 053/129] AY-5750 - fix wrong looping --- .../ayon_aftereffects/plugins/publish/collect_render.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server_addon/aftereffects/client/ayon_aftereffects/plugins/publish/collect_render.py b/server_addon/aftereffects/client/ayon_aftereffects/plugins/publish/collect_render.py index e0cf1b10d6..e550e73709 100644 --- a/server_addon/aftereffects/client/ayon_aftereffects/plugins/publish/collect_render.py +++ b/server_addon/aftereffects/client/ayon_aftereffects/plugins/publish/collect_render.py @@ -198,8 +198,8 @@ class CollectAERender(publish.AbstractCollectRender): ext ) - file_path = os.path.join(base_dir, file_name) - expected_files.append(file_path) + file_path = os.path.join(base_dir, file_name) + expected_files.append(file_path) return expected_files def _get_output_dir(self, render_instance): From 5872d2b3ee786eaabfbd0d66c1d66860b18e713a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 18 Jun 2024 11:14:47 +0200 Subject: [PATCH 054/129] AY-5750 - remove unnecessary condition --- .../plugins/publish/submit_aftereffects_deadline.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/server_addon/deadline/client/ayon_deadline/plugins/publish/submit_aftereffects_deadline.py b/server_addon/deadline/client/ayon_deadline/plugins/publish/submit_aftereffects_deadline.py index c61e6cd35b..a7be38bf3b 100644 --- a/server_addon/deadline/client/ayon_deadline/plugins/publish/submit_aftereffects_deadline.py +++ b/server_addon/deadline/client/ayon_deadline/plugins/publish/submit_aftereffects_deadline.py @@ -57,12 +57,11 @@ class AfterEffectsSubmitDeadline( dln_job_info.Plugin = "AfterEffects" dln_job_info.UserName = context.data.get( "deadlineUser", getpass.getuser()) - if self._instance.data["frameEnd"] >= self._instance.data["frameStart"]: # noqa - # Deadline requires integers in frame range - frame_range = "{}-{}".format( - int(round(self._instance.data["frameStart"])), - int(round(self._instance.data["frameEnd"]))) - dln_job_info.Frames = frame_range + # Deadline requires integers in frame range + frame_range = "{}-{}".format( + int(round(self._instance.data["frameStart"])), + int(round(self._instance.data["frameEnd"]))) + dln_job_info.Frames = frame_range dln_job_info.Priority = self.priority dln_job_info.Pool = self._instance.data.get("primaryPool") From ce44538c2e608049f316aec0b9f475bc9accf800 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Tue, 18 Jun 2024 10:40:54 +0100 Subject: [PATCH 055/129] Removed unused function --- .../plugins/publish/submit_blender_deadline.py | 11 ----------- server_addon/deadline/client/ayon_deadline/version.py | 2 +- server_addon/deadline/package.py | 2 +- 3 files changed, 2 insertions(+), 13 deletions(-) diff --git a/server_addon/deadline/client/ayon_deadline/plugins/publish/submit_blender_deadline.py b/server_addon/deadline/client/ayon_deadline/plugins/publish/submit_blender_deadline.py index f12e6a0760..479dffdcc4 100644 --- a/server_addon/deadline/client/ayon_deadline/plugins/publish/submit_blender_deadline.py +++ b/server_addon/deadline/client/ayon_deadline/plugins/publish/submit_blender_deadline.py @@ -41,17 +41,6 @@ class BlenderSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline, group = None job_delay = "00:00:00:00" - @classmethod - def apply_settings(cls, project_settings): - settings = project_settings["deadline"]["publish"]["BlenderSubmitDeadline"] # noqa - - # Take some defaults from settings - cls.asset_dependencies = settings.get("asset_dependencies", - cls.asset_dependencies) - cls.use_published = settings.get("use_published", cls.use_published) - cls.priority = settings.get("priority", cls.priority) - cls.group = settings.get("group", cls.group) - def get_job_info(self): job_info = DeadlineJobInfo(Plugin="Blender") diff --git a/server_addon/deadline/client/ayon_deadline/version.py b/server_addon/deadline/client/ayon_deadline/version.py index 5baf5122fb..da619d2a42 100644 --- a/server_addon/deadline/client/ayon_deadline/version.py +++ b/server_addon/deadline/client/ayon_deadline/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'deadline' version.""" -__version__ = "0.2.0" +__version__ = "0.2.1" diff --git a/server_addon/deadline/package.py b/server_addon/deadline/package.py index 7e7bae8bd6..5696c60dc6 100644 --- a/server_addon/deadline/package.py +++ b/server_addon/deadline/package.py @@ -1,6 +1,6 @@ name = "deadline" title = "Deadline" -version = "0.2.0" +version = "0.2.1" client_dir = "ayon_deadline" From 7dc954fa8313439152b9fd83e913709d44d51219 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Tue, 18 Jun 2024 10:51:43 +0100 Subject: [PATCH 056/129] Bumped blender version --- server_addon/blender/client/ayon_blender/version.py | 2 +- server_addon/blender/package.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server_addon/blender/client/ayon_blender/version.py b/server_addon/blender/client/ayon_blender/version.py index 365c44e71b..c21b06a8de 100644 --- a/server_addon/blender/client/ayon_blender/version.py +++ b/server_addon/blender/client/ayon_blender/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'blender' version.""" -__version__ = "0.2.0" +__version__ = "0.2.1" diff --git a/server_addon/blender/package.py b/server_addon/blender/package.py index 4e0ac112e1..1b595e22da 100644 --- a/server_addon/blender/package.py +++ b/server_addon/blender/package.py @@ -1,6 +1,6 @@ name = "blender" title = "Blender" -version = "0.2.0" +version = "0.2.1" client_dir = "ayon_blender" From 2322d0f2ad1a88e00ae3febf4e16ba8d1ea6d90c Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 18 Jun 2024 12:01:51 +0200 Subject: [PATCH 057/129] safe access to shortName on folder type --- client/ayon_core/tools/common_models/projects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_core/tools/common_models/projects.py b/client/ayon_core/tools/common_models/projects.py index 90b75e15c1..4e8925388d 100644 --- a/client/ayon_core/tools/common_models/projects.py +++ b/client/ayon_core/tools/common_models/projects.py @@ -89,7 +89,7 @@ class FolderTypeItem: def from_project_item(cls, folder_type_data): return cls( name=folder_type_data["name"], - short=folder_type_data["shortName"], + short=folder_type_data.get("shortName", ""), icon=folder_type_data["icon"], ) From 59e49182be07bc5758349165cc07d18ae87c10f4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 18 Jun 2024 12:02:22 +0200 Subject: [PATCH 058/129] capture errors and add basic handle of the crash --- .../ayon_core/tools/utils/folders_widget.py | 6 ++++- client/ayon_core/tools/utils/lib.py | 22 ++++++++++++++++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/tools/utils/folders_widget.py b/client/ayon_core/tools/utils/folders_widget.py index 3b53266c9f..bb75f3b6e5 100644 --- a/client/ayon_core/tools/utils/folders_widget.py +++ b/client/ayon_core/tools/utils/folders_widget.py @@ -192,7 +192,11 @@ class FoldersQtModel(QtGui.QStandardItemModel): or thread_id != self._current_refresh_thread.id ): return - folder_items, folder_type_items = thread.get_result() + if thread.failed: + # TODO visualize that refresh failed + folder_items, folder_type_items = {}, {} + else: + folder_items, folder_type_items = thread.get_result() self._fill_items(folder_items, folder_type_items) self._current_refresh_thread = None diff --git a/client/ayon_core/tools/utils/lib.py b/client/ayon_core/tools/utils/lib.py index e0626ec1db..5a7feb05eb 100644 --- a/client/ayon_core/tools/utils/lib.py +++ b/client/ayon_core/tools/utils/lib.py @@ -2,7 +2,10 @@ import os import sys import contextlib import collections +import traceback from functools import partial +from typing import Union, Any + from qtpy import QtWidgets, QtCore, QtGui import qtawesome @@ -425,26 +428,39 @@ class RefreshThread(QtCore.QThread): self._id = thread_id self._callback = partial(func, *args, **kwargs) self._exception = None + self._traceback = None self._result = None self.finished.connect(self._on_finish_callback) @property - def id(self): + def id(self) -> str: return self._id @property - def failed(self): + def failed(self) -> bool: return self._exception is not None def run(self): try: self._result = self._callback() except Exception as exc: + exc_type, exc_value, exc_traceback = sys.exc_info() + err_traceback = "".join(traceback.format_exception( + exc_type, exc_value, exc_traceback + )) + print(err_traceback) + self._traceback = err_traceback self._exception = exc - def get_result(self): + def get_result(self) -> Any: return self._result + def get_exception(self) -> Union[BaseException, None]: + return self._exception + + def get_traceback(self) -> Union[str, None]: + return self._traceback + def _on_finish_callback(self): """Trigger custom signal with thread id. From 35e557671b37ad2168ffb65ac41c45a9165270a2 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 18 Jun 2024 12:09:26 +0200 Subject: [PATCH 059/129] Fix typo --- server_addon/deadline/client/ayon_deadline/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server_addon/deadline/client/ayon_deadline/lib.py b/server_addon/deadline/client/ayon_deadline/lib.py index 8822559134..7f07c350ec 100644 --- a/server_addon/deadline/client/ayon_deadline/lib.py +++ b/server_addon/deadline/client/ayon_deadline/lib.py @@ -6,5 +6,5 @@ FARM_FAMILIES = [ "vrayscene", "maxrender", "arnold_rop", "mantra_rop", "karma_rop", "vray_rop", "redshift_rop", - "renderFarm", "usrender", "publish.hou" + "renderFarm", "usdrender", "publish.hou" ] From 7d7e6c0297de8de88e63522d41bbb3f6f3328ed4 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 18 Jun 2024 12:09:58 +0200 Subject: [PATCH 060/129] Bump addon version --- server_addon/deadline/client/ayon_deadline/version.py | 2 +- server_addon/deadline/package.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server_addon/deadline/client/ayon_deadline/version.py b/server_addon/deadline/client/ayon_deadline/version.py index 5baf5122fb..da619d2a42 100644 --- a/server_addon/deadline/client/ayon_deadline/version.py +++ b/server_addon/deadline/client/ayon_deadline/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'deadline' version.""" -__version__ = "0.2.0" +__version__ = "0.2.1" diff --git a/server_addon/deadline/package.py b/server_addon/deadline/package.py index 7e7bae8bd6..5696c60dc6 100644 --- a/server_addon/deadline/package.py +++ b/server_addon/deadline/package.py @@ -1,6 +1,6 @@ name = "deadline" title = "Deadline" -version = "0.2.0" +version = "0.2.1" client_dir = "ayon_deadline" From 9d8a8a98918ac125d583cf789ce50340e938e371 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 18 Jun 2024 12:12:03 +0200 Subject: [PATCH 061/129] remove unnecessary line --- client/ayon_core/tools/utils/lib.py | 1 - 1 file changed, 1 deletion(-) diff --git a/client/ayon_core/tools/utils/lib.py b/client/ayon_core/tools/utils/lib.py index 5a7feb05eb..f31bb82e59 100644 --- a/client/ayon_core/tools/utils/lib.py +++ b/client/ayon_core/tools/utils/lib.py @@ -6,7 +6,6 @@ import traceback from functools import partial from typing import Union, Any - from qtpy import QtWidgets, QtCore, QtGui import qtawesome import qtmaterialsymbols From 8f4f990b22c6d79d1e1000ec69c5177ac011292e Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 18 Jun 2024 12:21:48 +0200 Subject: [PATCH 062/129] Bump addon version (again, because `develop` just got bumped) --- server_addon/deadline/client/ayon_deadline/version.py | 2 +- server_addon/deadline/package.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server_addon/deadline/client/ayon_deadline/version.py b/server_addon/deadline/client/ayon_deadline/version.py index da619d2a42..e131427f12 100644 --- a/server_addon/deadline/client/ayon_deadline/version.py +++ b/server_addon/deadline/client/ayon_deadline/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'deadline' version.""" -__version__ = "0.2.1" +__version__ = "0.2.2" diff --git a/server_addon/deadline/package.py b/server_addon/deadline/package.py index 5696c60dc6..dcc61e3d46 100644 --- a/server_addon/deadline/package.py +++ b/server_addon/deadline/package.py @@ -1,6 +1,6 @@ name = "deadline" title = "Deadline" -version = "0.2.1" +version = "0.2.2" client_dir = "ayon_deadline" From 0fab2e5edbdf29b1856468ea31c5b8c779a2caff Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 18 Jun 2024 13:44:07 +0200 Subject: [PATCH 063/129] bump versions --- server_addon/aftereffects/client/ayon_aftereffects/version.py | 2 +- server_addon/aftereffects/package.py | 2 +- server_addon/deadline/client/ayon_deadline/version.py | 2 +- server_addon/deadline/package.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/server_addon/aftereffects/client/ayon_aftereffects/version.py b/server_addon/aftereffects/client/ayon_aftereffects/version.py index 2faa06ba3a..4e371a2467 100644 --- a/server_addon/aftereffects/client/ayon_aftereffects/version.py +++ b/server_addon/aftereffects/client/ayon_aftereffects/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'aftereffects' version.""" -__version__ = "0.2.1" +__version__ = "0.2.2" diff --git a/server_addon/aftereffects/package.py b/server_addon/aftereffects/package.py index 37a54b4212..eabba27df1 100644 --- a/server_addon/aftereffects/package.py +++ b/server_addon/aftereffects/package.py @@ -1,6 +1,6 @@ name = "aftereffects" title = "AfterEffects" -version = "0.2.1" +version = "0.2.2" client_dir = "ayon_aftereffects" diff --git a/server_addon/deadline/client/ayon_deadline/version.py b/server_addon/deadline/client/ayon_deadline/version.py index 5baf5122fb..da619d2a42 100644 --- a/server_addon/deadline/client/ayon_deadline/version.py +++ b/server_addon/deadline/client/ayon_deadline/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'deadline' version.""" -__version__ = "0.2.0" +__version__ = "0.2.1" diff --git a/server_addon/deadline/package.py b/server_addon/deadline/package.py index 7e7bae8bd6..5696c60dc6 100644 --- a/server_addon/deadline/package.py +++ b/server_addon/deadline/package.py @@ -1,6 +1,6 @@ name = "deadline" title = "Deadline" -version = "0.2.0" +version = "0.2.1" client_dir = "ayon_deadline" From e646d2d33e2b61103cce15190e89d551cc663081 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 18 Jun 2024 15:59:29 +0200 Subject: [PATCH 064/129] bump version to '0.4.0' --- client/ayon_core/version.py | 2 +- package.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index e4297e2000..705c2e5594 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON core addon version.""" -__version__ = "0.3.3-dev.1" +__version__ = "0.4.0" diff --git a/package.py b/package.py index 73f7174b6f..871a4d39f1 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "0.3.3-dev.1" +version = "0.4.0" client_dir = "ayon_core" From e5cc5de1916694811716b92e7c27bb2c66720c07 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 18 Jun 2024 16:00:22 +0200 Subject: [PATCH 065/129] bump version to '0.4.1-dev.1' --- client/ayon_core/version.py | 2 +- package.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/version.py b/client/ayon_core/version.py index 705c2e5594..f0e4b9a10f 100644 --- a/client/ayon_core/version.py +++ b/client/ayon_core/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON core addon version.""" -__version__ = "0.4.0" +__version__ = "0.4.1-dev.1" diff --git a/package.py b/package.py index 871a4d39f1..e5e567b8e8 100644 --- a/package.py +++ b/package.py @@ -1,6 +1,6 @@ name = "core" title = "Core" -version = "0.4.0" +version = "0.4.1-dev.1" client_dir = "ayon_core" From e037e8c8d4c563991700e6953e1485481550e86b Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 19 Jun 2024 16:43:12 +0800 Subject: [PATCH 066/129] fix the TypeError found in the scriptmenu when launching maya 2025 --- client/ayon_core/vendor/python/scriptsmenu/launchformaya.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/vendor/python/scriptsmenu/launchformaya.py b/client/ayon_core/vendor/python/scriptsmenu/launchformaya.py index 01880b94d7..c8b0c777de 100644 --- a/client/ayon_core/vendor/python/scriptsmenu/launchformaya.py +++ b/client/ayon_core/vendor/python/scriptsmenu/launchformaya.py @@ -130,7 +130,10 @@ def main(title="Scripts", parent=None, objectName=None): # Register control + shift callback to add to shelf (maya behavior) modifiers = QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier - menu.register_callback(int(modifiers), to_shelf) + if int(cmds.about(version=True)) <= 2025: + modifiers = int(modifiers) + + menu.register_callback(modifiers, to_shelf) menu.register_callback(0, register_repeat_last) From 819664cf0561956a729accc531f81dc8b20bbf67 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 19 Jun 2024 22:26:02 +0800 Subject: [PATCH 067/129] add option to stripe shader assignment --- .../maya/client/ayon_maya/plugins/create/create_setdress.py | 3 +++ .../client/ayon_maya/plugins/publish/extract_maya_scene_raw.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/server_addon/maya/client/ayon_maya/plugins/create/create_setdress.py b/server_addon/maya/client/ayon_maya/plugins/create/create_setdress.py index 12532e0724..98d6de867f 100644 --- a/server_addon/maya/client/ayon_maya/plugins/create/create_setdress.py +++ b/server_addon/maya/client/ayon_maya/plugins/create/create_setdress.py @@ -15,5 +15,8 @@ class CreateSetDress(plugin.MayaCreator): return [ BoolDef("exactSetMembersOnly", label="Exact Set Members Only", + default=True), + BoolDef("shader", + label="Include shader", default=True) ] diff --git a/server_addon/maya/client/ayon_maya/plugins/publish/extract_maya_scene_raw.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_maya_scene_raw.py index 6e66353c7a..f0d0c70ae4 100644 --- a/server_addon/maya/client/ayon_maya/plugins/publish/extract_maya_scene_raw.py +++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_maya_scene_raw.py @@ -96,7 +96,7 @@ class ExtractMayaSceneRaw(plugin.MayaExtractorPlugin, AYONPyblishPluginMixin): "preserve_references" ], constructionHistory=True, - shader=True, + shader=True if instance.data.get("shader", False) else False, constraints=True, expressions=True) From 4265c92ceaaa0c0d3d60dd9123a5e792467f1439 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 19 Jun 2024 22:27:15 +0800 Subject: [PATCH 068/129] make sure shader is always true if it is not in the instance.data --- .../client/ayon_maya/plugins/publish/extract_maya_scene_raw.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server_addon/maya/client/ayon_maya/plugins/publish/extract_maya_scene_raw.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_maya_scene_raw.py index f0d0c70ae4..fde48afb8f 100644 --- a/server_addon/maya/client/ayon_maya/plugins/publish/extract_maya_scene_raw.py +++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_maya_scene_raw.py @@ -96,7 +96,7 @@ class ExtractMayaSceneRaw(plugin.MayaExtractorPlugin, AYONPyblishPluginMixin): "preserve_references" ], constructionHistory=True, - shader=True if instance.data.get("shader", False) else False, + shader=True if instance.data.get("shader", True) else False, constraints=True, expressions=True) From fbcd56eb0d2b1c5c4d3032b7737da34cf900b47e Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 19 Jun 2024 17:52:21 +0200 Subject: [PATCH 069/129] Support single frame publishes - fix #672 --- .../houdini/client/ayon_houdini/api/plugin.py | 20 +++++++++++++++++++ .../plugins/publish/extract_alembic.py | 11 ++-------- .../plugins/publish/extract_ass.py | 13 ++---------- .../plugins/publish/extract_bgeo.py | 10 ++++------ .../plugins/publish/extract_composite.py | 7 +++---- .../plugins/publish/extract_mantra_ifd.py | 14 ++----------- .../plugins/publish/extract_opengl.py | 4 +--- .../plugins/publish/extract_redshift_proxy.py | 4 +--- .../plugins/publish/extract_vdb_cache.py | 4 +--- 9 files changed, 36 insertions(+), 51 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/api/plugin.py b/server_addon/houdini/client/ayon_houdini/api/plugin.py index 9c6bba925a..0e2308e948 100644 --- a/server_addon/houdini/client/ayon_houdini/api/plugin.py +++ b/server_addon/houdini/client/ayon_houdini/api/plugin.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- """Houdini specific Avalon/Pyblish plugin definitions.""" +import os import sys from abc import ( ABCMeta @@ -392,3 +393,22 @@ class HoudiniExtractorPlugin(publish.Extractor): hosts = ["houdini"] settings_category = SETTINGS_CATEGORY + + def validate_expected_frames(self, instance, staging_dir): + """ + Validate all expected files in `instance.data["frames"]` exist in + the staging directory. + """ + filenames = instance.data["frames"] + if isinstance(filenames, str): + # Single frame + filenames = [filenames] + + missing_filenames = [] + for filename in filenames: + path = os.path.join(staging_dir, filename) + if not os.path.isfile(path): + missing_filenames.append(filename) + if missing_filenames: + raise RuntimeError(f"Missing frames: {missing_filenames}") + diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_alembic.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_alembic.py index e82f07284a..07216a491c 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_alembic.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_alembic.py @@ -26,15 +26,8 @@ class ExtractAlembic(plugin.HoudiniExtractorPlugin): staging_dir = os.path.dirname(output) instance.data["stagingDir"] = staging_dir - if instance.data.get("frames"): - # list of files - files = instance.data["frames"] - else: - # single file - files = os.path.basename(output) - # We run the render - self.log.info("Writing alembic '%s' to '%s'" % (files, + self.log.info("Writing alembic '%s' to '%s'" % (output, staging_dir)) render_rop(ropnode) @@ -45,7 +38,7 @@ class ExtractAlembic(plugin.HoudiniExtractorPlugin): representation = { 'name': 'abc', 'ext': 'abc', - 'files': files, + 'files': instance.data["frames"], "stagingDir": staging_dir, } instance.data["representations"].append(representation) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_ass.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_ass.py index a796bbf4b3..befa6d0d49 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_ass.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_ass.py @@ -35,16 +35,7 @@ class ExtractAss(plugin.HoudiniExtractorPlugin): # Unfortunately user interrupting the extraction does not raise an # error and thus still continues to the integrator. To capture that # we make sure all files exist - files = instance.data["frames"] - missing = [] - for file_name in files: - full_path = os.path.normpath(os.path.join(staging_dir, file_name)) - if not os.path.exists(full_path): - missing.append(full_path) - - if missing: - raise RuntimeError("Failed to complete Arnold ass extraction. " - "Missing output files: {}".format(missing)) + self.validate_expected_frames(instance, staging_dir) if "representations" not in instance.data: instance.data["representations"] = [] @@ -55,7 +46,7 @@ class ExtractAss(plugin.HoudiniExtractorPlugin): representation = { 'name': 'ass', 'ext': ext, - "files": files, + "files": instance.data["frames"], "stagingDir": staging_dir, "frameStart": instance.data["frameStartHandle"], "frameEnd": instance.data["frameEndHandle"], diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_bgeo.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_bgeo.py index ab8837065d..180812ab87 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_bgeo.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_bgeo.py @@ -19,8 +19,8 @@ class ExtractBGEO(plugin.HoudiniExtractorPlugin): ropnode = hou.node(instance.data["instance_node"]) # Get the filename from the filename parameter - output = ropnode.evalParm("sopoutput") - staging_dir, file_name = os.path.split(output) + sop_output = ropnode.evalParm("sopoutput") + staging_dir, file_name = os.path.split(sop_output) instance.data["stagingDir"] = staging_dir # We run the render @@ -30,10 +30,8 @@ class ExtractBGEO(plugin.HoudiniExtractorPlugin): # write files lib.render_rop(ropnode) - output = instance.data["frames"] - _, ext = lib.splitext( - output[0], allowed_multidot_extensions=[ + sop_output, allowed_multidot_extensions=[ ".ass.gz", ".bgeo.sc", ".bgeo.gz", ".bgeo.lzma", ".bgeo.bz2"]) @@ -43,7 +41,7 @@ class ExtractBGEO(plugin.HoudiniExtractorPlugin): representation = { "name": "bgeo", "ext": ext.lstrip("."), - "files": output, + "files": instance.data["frames"], "stagingDir": staging_dir, "frameStart": instance.data["frameStartHandle"], "frameEnd": instance.data["frameEndHandle"] diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_composite.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_composite.py index cab462aef6..84f03e5d1c 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_composite.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_composite.py @@ -20,17 +20,16 @@ class ExtractComposite(plugin.HoudiniExtractorPlugin, # Get the filename from the copoutput parameter # `.evalParm(parameter)` will make sure all tokens are resolved - output = ropnode.evalParm("copoutput") - staging_dir = os.path.dirname(output) + cop_output = ropnode.evalParm("copoutput") + staging_dir, file_name = os.path.split(cop_output) instance.data["stagingDir"] = staging_dir - file_name = os.path.basename(output) self.log.info("Writing comp '%s' to '%s'" % (file_name, staging_dir)) render_rop(ropnode) output = instance.data["frames"] - _, ext = splitext(output[0], []) + _, ext = splitext(file_name, []) ext = ext.lstrip(".") if "representations" not in instance.data: diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_mantra_ifd.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_mantra_ifd.py index b424f2e452..f0f402fa64 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_mantra_ifd.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_mantra_ifd.py @@ -23,17 +23,7 @@ class ExtractMantraIFD(plugin.HoudiniExtractorPlugin): staging_dir = os.path.dirname(output) instance.data["stagingDir"] = staging_dir - files = instance.data["frames"] - missing_frames = [ - frame - for frame in instance.data["frames"] - if not os.path.exists( - os.path.normpath(os.path.join(staging_dir, frame))) - ] - if missing_frames: - raise RuntimeError("Failed to complete Mantra ifd extraction. " - "Missing output files: {}".format( - missing_frames)) + self.validate_expected_frames(instance, staging_dir) if "representations" not in instance.data: instance.data["representations"] = [] @@ -41,7 +31,7 @@ class ExtractMantraIFD(plugin.HoudiniExtractorPlugin): representation = { 'name': 'ifd', 'ext': 'ifd', - 'files': files, + 'files': instance.data["frames"], "stagingDir": staging_dir, "frameStart": instance.data["frameStart"], "frameEnd": instance.data["frameEnd"], diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_opengl.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_opengl.py index bee1bf871f..934f98a9f3 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_opengl.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_opengl.py @@ -38,8 +38,6 @@ class ExtractOpenGL(plugin.HoudiniExtractorPlugin, render_rop(ropnode) - output = instance.data["frames"] - tags = ["review"] if not instance.data.get("keepImages"): tags.append("delete") @@ -47,7 +45,7 @@ class ExtractOpenGL(plugin.HoudiniExtractorPlugin, representation = { "name": instance.data["imageFormat"], "ext": instance.data["imageFormat"], - "files": output, + "files": instance.data["frames"], "stagingDir": staging_dir, "frameStart": instance.data["frameStartHandle"], "frameEnd": instance.data["frameEndHandle"], diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_redshift_proxy.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_redshift_proxy.py index 3e8a79df00..8c3cdd5ef9 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_redshift_proxy.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_redshift_proxy.py @@ -32,15 +32,13 @@ class ExtractRedshiftProxy(plugin.HoudiniExtractorPlugin): render_rop(ropnode) - output = instance.data["frames"] - if "representations" not in instance.data: instance.data["representations"] = [] representation = { "name": "rs", "ext": "rs", - "files": output, + "files": instance.data["frames"], "stagingDir": staging_dir, } diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_vdb_cache.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_vdb_cache.py index a944d81e9b..8f0c070ff1 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_vdb_cache.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_vdb_cache.py @@ -30,15 +30,13 @@ class ExtractVDBCache(plugin.HoudiniExtractorPlugin): render_rop(ropnode) - output = instance.data["frames"] - if "representations" not in instance.data: instance.data["representations"] = [] representation = { "name": "vdb", "ext": "vdb", - "files": output, + "files": instance.data["frames"], "stagingDir": staging_dir, "frameStart": instance.data["frameStartHandle"], "frameEnd": instance.data["frameEndHandle"], From 0bec3f0d2e2616471102b905785cc739724ee031 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 19 Jun 2024 17:52:38 +0200 Subject: [PATCH 070/129] Simplify setting instance data "frames" --- .../client/ayon_houdini/plugins/publish/collect_frames.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_frames.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_frames.py index 3378657bfd..82f986ee13 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_frames.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_frames.py @@ -60,7 +60,7 @@ class CollectFrames(plugin.HoudiniInstancePlugin): # todo: `frames` currently conflicts with "explicit frames" for a # for a custom frame list. So this should be refactored. - instance.data.update({"frames": result}) + instance.data["frames"] = result @staticmethod def create_file_list(match, start_frame, end_frame): From f56f0cc3d761c52330637dd2e2f0f0c10ca58a24 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 19 Jun 2024 18:17:00 +0200 Subject: [PATCH 071/129] Move variable closer to its usage --- .../client/ayon_houdini/plugins/publish/extract_composite.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_composite.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_composite.py index 84f03e5d1c..9830b2ea84 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_composite.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_composite.py @@ -28,7 +28,6 @@ class ExtractComposite(plugin.HoudiniExtractorPlugin, render_rop(ropnode) - output = instance.data["frames"] _, ext = splitext(file_name, []) ext = ext.lstrip(".") @@ -38,7 +37,7 @@ class ExtractComposite(plugin.HoudiniExtractorPlugin, representation = { "name": ext, "ext": ext, - "files": output, + "files": instance.data["frames"], "stagingDir": staging_dir, "frameStart": instance.data["frameStartHandle"], "frameEnd": instance.data["frameEndHandle"], From 320f97c9c68ff5acd220c5da8f632258f935c2dd Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Wed, 19 Jun 2024 20:32:07 +0300 Subject: [PATCH 072/129] be more explicit about which node to skip --- .../houdini/client/ayon_houdini/plugins/load/load_hda.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/load/load_hda.py b/server_addon/houdini/client/ayon_houdini/plugins/load/load_hda.py index eb9a74a7ad..2f8d6aae49 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/load/load_hda.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/load/load_hda.py @@ -96,7 +96,7 @@ class HdaLoader(plugin.HoudiniLoader): parent = node.parent() node.destroy() - if parent.type().category() == hou.objNodeTypeCategory(): + if parent.path() == pipeline.AVALON_CONTAINERS: return # Remove parent if empty. From 2ce50870377748a9a6e5206542c3d5326c419cd5 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 19 Jun 2024 19:39:16 +0200 Subject: [PATCH 073/129] Implement more generic ExtractROP extractor --- .../plugins/publish/extract_alembic.py | 44 ------ .../plugins/publish/extract_ass.py | 54 ------- .../plugins/publish/extract_bgeo.py | 49 ------- .../plugins/publish/extract_composite.py | 56 -------- .../plugins/publish/extract_fbx.py | 51 ------- .../plugins/publish/extract_mantra_ifd.py | 39 ----- .../plugins/publish/extract_opengl.py | 67 --------- .../plugins/publish/extract_redshift_proxy.py | 50 ------- .../plugins/publish/extract_rop.py | 134 ++++++++++++++++++ .../plugins/publish/extract_vdb_cache.py | 44 ------ 10 files changed, 134 insertions(+), 454 deletions(-) delete mode 100644 server_addon/houdini/client/ayon_houdini/plugins/publish/extract_alembic.py delete mode 100644 server_addon/houdini/client/ayon_houdini/plugins/publish/extract_ass.py delete mode 100644 server_addon/houdini/client/ayon_houdini/plugins/publish/extract_bgeo.py delete mode 100644 server_addon/houdini/client/ayon_houdini/plugins/publish/extract_composite.py delete mode 100644 server_addon/houdini/client/ayon_houdini/plugins/publish/extract_fbx.py delete mode 100644 server_addon/houdini/client/ayon_houdini/plugins/publish/extract_mantra_ifd.py delete mode 100644 server_addon/houdini/client/ayon_houdini/plugins/publish/extract_opengl.py delete mode 100644 server_addon/houdini/client/ayon_houdini/plugins/publish/extract_redshift_proxy.py create mode 100644 server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py delete mode 100644 server_addon/houdini/client/ayon_houdini/plugins/publish/extract_vdb_cache.py diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_alembic.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_alembic.py deleted file mode 100644 index 07216a491c..0000000000 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_alembic.py +++ /dev/null @@ -1,44 +0,0 @@ -import os -import hou - -import pyblish.api - -from ayon_houdini.api import plugin -from ayon_houdini.api.lib import render_rop - - -class ExtractAlembic(plugin.HoudiniExtractorPlugin): - - order = pyblish.api.ExtractorOrder - label = "Extract Alembic" - families = ["abc", "camera"] - targets = ["local", "remote"] - - def process(self, instance): - if instance.data.get("farm"): - self.log.debug("Should be processed on farm, skipping.") - return - - ropnode = hou.node(instance.data["instance_node"]) - - # Get the filename from the filename parameter - output = ropnode.evalParm("filename") - staging_dir = os.path.dirname(output) - instance.data["stagingDir"] = staging_dir - - # We run the render - self.log.info("Writing alembic '%s' to '%s'" % (output, - staging_dir)) - - render_rop(ropnode) - - if "representations" not in instance.data: - instance.data["representations"] = [] - - representation = { - 'name': 'abc', - 'ext': 'abc', - 'files': instance.data["frames"], - "stagingDir": staging_dir, - } - instance.data["representations"].append(representation) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_ass.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_ass.py deleted file mode 100644 index befa6d0d49..0000000000 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_ass.py +++ /dev/null @@ -1,54 +0,0 @@ -import os -import hou - -import pyblish.api - -from ayon_houdini.api import plugin -from ayon_houdini.api.lib import render_rop - - -class ExtractAss(plugin.HoudiniExtractorPlugin): - - order = pyblish.api.ExtractorOrder + 0.1 - label = "Extract Ass" - families = ["ass"] - targets = ["local", "remote"] - - def process(self, instance): - if instance.data.get("farm"): - self.log.debug("Should be processed on farm, skipping.") - return - ropnode = hou.node(instance.data["instance_node"]) - - # Get the filename from the filename parameter - # `.evalParm(parameter)` will make sure all tokens are resolved - output = ropnode.evalParm("ar_ass_file") - staging_dir = os.path.dirname(output) - instance.data["stagingDir"] = staging_dir - file_name = os.path.basename(output) - - # We run the render - self.log.info("Writing ASS '%s' to '%s'" % (file_name, staging_dir)) - - render_rop(ropnode) - - # Unfortunately user interrupting the extraction does not raise an - # error and thus still continues to the integrator. To capture that - # we make sure all files exist - self.validate_expected_frames(instance, staging_dir) - - if "representations" not in instance.data: - instance.data["representations"] = [] - - # Allow ass.gz extension as well - ext = "ass.gz" if file_name.endswith(".ass.gz") else "ass" - - representation = { - 'name': 'ass', - 'ext': ext, - "files": instance.data["frames"], - "stagingDir": staging_dir, - "frameStart": instance.data["frameStartHandle"], - "frameEnd": instance.data["frameEndHandle"], - } - instance.data["representations"].append(representation) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_bgeo.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_bgeo.py deleted file mode 100644 index 180812ab87..0000000000 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_bgeo.py +++ /dev/null @@ -1,49 +0,0 @@ -import os -import hou - -import pyblish.api - -from ayon_houdini.api import lib, plugin - - -class ExtractBGEO(plugin.HoudiniExtractorPlugin): - - order = pyblish.api.ExtractorOrder - label = "Extract BGEO" - families = ["bgeo"] - - def process(self, instance): - if instance.data.get("farm"): - self.log.debug("Should be processed on farm, skipping.") - return - ropnode = hou.node(instance.data["instance_node"]) - - # Get the filename from the filename parameter - sop_output = ropnode.evalParm("sopoutput") - staging_dir, file_name = os.path.split(sop_output) - instance.data["stagingDir"] = staging_dir - - # We run the render - self.log.info("Writing bgeo files '{}' to '{}'.".format( - file_name, staging_dir)) - - # write files - lib.render_rop(ropnode) - - _, ext = lib.splitext( - sop_output, allowed_multidot_extensions=[ - ".ass.gz", ".bgeo.sc", ".bgeo.gz", - ".bgeo.lzma", ".bgeo.bz2"]) - - if "representations" not in instance.data: - instance.data["representations"] = [] - - representation = { - "name": "bgeo", - "ext": ext.lstrip("."), - "files": instance.data["frames"], - "stagingDir": staging_dir, - "frameStart": instance.data["frameStartHandle"], - "frameEnd": instance.data["frameEndHandle"] - } - instance.data["representations"].append(representation) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_composite.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_composite.py deleted file mode 100644 index 9830b2ea84..0000000000 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_composite.py +++ /dev/null @@ -1,56 +0,0 @@ -import os -import hou -import pyblish.api - -from ayon_core.pipeline import publish -from ayon_houdini.api import plugin -from ayon_houdini.api.lib import render_rop, splitext - - -class ExtractComposite(plugin.HoudiniExtractorPlugin, - publish.ColormanagedPyblishPluginMixin): - - order = pyblish.api.ExtractorOrder - label = "Extract Composite (Image Sequence)" - families = ["imagesequence"] - - def process(self, instance): - - ropnode = hou.node(instance.data["instance_node"]) - - # Get the filename from the copoutput parameter - # `.evalParm(parameter)` will make sure all tokens are resolved - cop_output = ropnode.evalParm("copoutput") - staging_dir, file_name = os.path.split(cop_output) - instance.data["stagingDir"] = staging_dir - - self.log.info("Writing comp '%s' to '%s'" % (file_name, staging_dir)) - - render_rop(ropnode) - - _, ext = splitext(file_name, []) - ext = ext.lstrip(".") - - if "representations" not in instance.data: - instance.data["representations"] = [] - - representation = { - "name": ext, - "ext": ext, - "files": instance.data["frames"], - "stagingDir": staging_dir, - "frameStart": instance.data["frameStartHandle"], - "frameEnd": instance.data["frameEndHandle"], - } - - if ext.lower() == "exr": - # Inject colorspace with 'scene_linear' as that's the - # default Houdini working colorspace and all extracted - # OpenEXR images should be in that colorspace. - # https://www.sidefx.com/docs/houdini/render/linear.html#image-formats - self.set_representation_colorspace( - representation, instance.context, - colorspace="scene_linear" - ) - - instance.data["representations"].append(representation) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_fbx.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_fbx.py deleted file mode 100644 index 49b3fa07ca..0000000000 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_fbx.py +++ /dev/null @@ -1,51 +0,0 @@ -# -*- coding: utf-8 -*- -"""Fbx Extractor for houdini. """ - -import os -import hou -import pyblish.api -from ayon_houdini.api import plugin -from ayon_houdini.api.lib import render_rop - - -class ExtractFBX(plugin.HoudiniExtractorPlugin): - - label = "Extract FBX" - families = ["fbx"] - - order = pyblish.api.ExtractorOrder + 0.1 - - def process(self, instance): - - # get rop node - ropnode = hou.node(instance.data.get("instance_node")) - output_file = ropnode.evalParm("sopoutput") - - # get staging_dir and file_name - staging_dir = os.path.normpath(os.path.dirname(output_file)) - file_name = os.path.basename(output_file) - - # render rop - self.log.debug("Writing FBX '%s' to '%s'", file_name, staging_dir) - render_rop(ropnode) - - # prepare representation - representation = { - "name": "fbx", - "ext": "fbx", - "files": file_name, - "stagingDir": staging_dir - } - - # A single frame may also be rendered without start/end frame. - if "frameStartHandle" in instance.data and "frameEndHandle" in instance.data: # noqa - representation["frameStart"] = instance.data["frameStartHandle"] - representation["frameEnd"] = instance.data["frameEndHandle"] - - # set value type for 'representations' key to list - if "representations" not in instance.data: - instance.data["representations"] = [] - - # update instance data - instance.data["stagingDir"] = staging_dir - instance.data["representations"].append(representation) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_mantra_ifd.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_mantra_ifd.py deleted file mode 100644 index f0f402fa64..0000000000 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_mantra_ifd.py +++ /dev/null @@ -1,39 +0,0 @@ -import os -import hou - -import pyblish.api - -from ayon_houdini.api import plugin - - -class ExtractMantraIFD(plugin.HoudiniExtractorPlugin): - - order = pyblish.api.ExtractorOrder - label = "Extract Mantra ifd" - families = ["mantraifd"] - targets = ["local", "remote"] - - def process(self, instance): - if instance.data.get("farm"): - self.log.debug("Should be processed on farm, skipping.") - return - - ropnode = hou.node(instance.data.get("instance_node")) - output = ropnode.evalParm("soho_diskfile") - staging_dir = os.path.dirname(output) - instance.data["stagingDir"] = staging_dir - - self.validate_expected_frames(instance, staging_dir) - - if "representations" not in instance.data: - instance.data["representations"] = [] - - representation = { - 'name': 'ifd', - 'ext': 'ifd', - 'files': instance.data["frames"], - "stagingDir": staging_dir, - "frameStart": instance.data["frameStart"], - "frameEnd": instance.data["frameEnd"], - } - instance.data["representations"].append(representation) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_opengl.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_opengl.py deleted file mode 100644 index 934f98a9f3..0000000000 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_opengl.py +++ /dev/null @@ -1,67 +0,0 @@ -import os -import hou - -import pyblish.api - -from ayon_core.pipeline import publish -from ayon_houdini.api import plugin -from ayon_houdini.api.lib import render_rop - - -class ExtractOpenGL(plugin.HoudiniExtractorPlugin, - publish.ColormanagedPyblishPluginMixin): - - order = pyblish.api.ExtractorOrder - 0.01 - label = "Extract OpenGL" - families = ["review"] - - def process(self, instance): - ropnode = hou.node(instance.data.get("instance_node")) - - # This plugin is triggered when marking render as reviewable. - # Therefore, this plugin will run on over wrong instances. - # TODO: Don't run this plugin on wrong instances. - # This plugin should run only on review product type - # with instance node of opengl type. - if ropnode.type().name() != "opengl": - self.log.debug("Skipping OpenGl extraction. Rop node {} " - "is not an OpenGl node.".format(ropnode.path())) - return - - output = ropnode.evalParm("picture") - staging_dir = os.path.normpath(os.path.dirname(output)) - instance.data["stagingDir"] = staging_dir - file_name = os.path.basename(output) - - self.log.info("Extracting '%s' to '%s'" % (file_name, - staging_dir)) - - render_rop(ropnode) - - tags = ["review"] - if not instance.data.get("keepImages"): - tags.append("delete") - - representation = { - "name": instance.data["imageFormat"], - "ext": instance.data["imageFormat"], - "files": instance.data["frames"], - "stagingDir": staging_dir, - "frameStart": instance.data["frameStartHandle"], - "frameEnd": instance.data["frameEndHandle"], - "tags": tags, - "preview": True, - "camera_name": instance.data.get("review_camera") - } - - if ropnode.evalParm("colorcorrect") == 2: # OpenColorIO enabled - colorspace = ropnode.evalParm("ociocolorspace") - # inject colorspace data - self.set_representation_colorspace( - representation, instance.context, - colorspace=colorspace - ) - - if "representations" not in instance.data: - instance.data["representations"] = [] - instance.data["representations"].append(representation) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_redshift_proxy.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_redshift_proxy.py deleted file mode 100644 index 8c3cdd5ef9..0000000000 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_redshift_proxy.py +++ /dev/null @@ -1,50 +0,0 @@ -import os -import hou - -import pyblish.api - -from ayon_houdini.api import plugin -from ayon_houdini.api.lib import render_rop - - -class ExtractRedshiftProxy(plugin.HoudiniExtractorPlugin): - - order = pyblish.api.ExtractorOrder + 0.1 - label = "Extract Redshift Proxy" - families = ["redshiftproxy"] - targets = ["local", "remote"] - - def process(self, instance): - if instance.data.get("farm"): - self.log.debug("Should be processed on farm, skipping.") - return - ropnode = hou.node(instance.data.get("instance_node")) - - # Get the filename from the filename parameter - # `.evalParm(parameter)` will make sure all tokens are resolved - output = ropnode.evalParm("RS_archive_file") - staging_dir = os.path.normpath(os.path.dirname(output)) - instance.data["stagingDir"] = staging_dir - file_name = os.path.basename(output) - - self.log.info("Writing Redshift Proxy '%s' to '%s'" % (file_name, - staging_dir)) - - render_rop(ropnode) - - if "representations" not in instance.data: - instance.data["representations"] = [] - - representation = { - "name": "rs", - "ext": "rs", - "files": instance.data["frames"], - "stagingDir": staging_dir, - } - - # A single frame may also be rendered without start/end frame. - if "frameStartHandle" in instance.data and "frameEndHandle" in instance.data: # noqa - representation["frameStart"] = instance.data["frameStartHandle"] - representation["frameEnd"] = instance.data["frameEndHandle"] - - instance.data["representations"].append(representation) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py new file mode 100644 index 0000000000..7b9e389f79 --- /dev/null +++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py @@ -0,0 +1,134 @@ +import os +import hou + +import pyblish.api + +from ayon_core.pipeline import publish +from ayon_houdini.api import plugin +from ayon_houdini.api.lib import render_rop, get_output_parameter, splitext + + +class ExtractROP(plugin.HoudiniExtractorPlugin): + """Generic Extractor for any ROP node.""" + label = "Extract ROP" + order = pyblish.api.ExtractorOrder + + families = ["abc", "camera", "bgeo", "pointcache", "fbx", "imagesequence", + "vdbcache", "ass", "redshiftproxy", "mantraifd"] + targets = ["local", "remote"] + + def process(self, instance: pyblish.api.Instance): + if instance.data.get("farm"): + self.log.debug("Should be processed on farm, skipping.") + return + + rop_node = hou.node(instance.data["instance_node"]) + + parm = get_output_parameter(rop_node) + filepath = parm.eval() + staging_dir = os.path.dirname(filepath) + _, ext = splitext( + filepath, allowed_multidot_extensions=[ + ".ass.gz", ".bgeo.sc", ".bgeo.gz", + ".bgeo.lzma", ".bgeo.bz2"] + ) + + render_rop(rop_node) + self.validate_expected_frames(instance, staging_dir) + + # In some cases representation name is not the the extension + # TODO: Preferably we remove this very specific naming + product_type = instance.data["productType"] + name = { + "bgeo": "bgeo", + "rs": "rs", + "ass": "ass", + }.get(product_type, ext) + + representation = { + "name": name, + "ext": ext, + "files": instance.data["frames"], + "stagingDir": staging_dir, + "frameStart": instance.data["frameStartHandle"], + "frameEnd": instance.data["frameEndHandle"], + } + representation = self.update_representation_data(instance, + representation) + instance.data.setdefault("representations", []).append(representation) + instance.data["stagingDir"] = staging_dir + + def update_representation_data(self, + instance: pyblish.api.Instance, + representation: dict): + """Allow subclass to override the representation data in-place""" + pass + + +class ExtractOpenGL(ExtractROP, + publish.ColormanagedPyblishPluginMixin): + + order = pyblish.api.ExtractorOrder - 0.01 + label = "Extract OpenGL" + families = ["review"] + + def process(self, instance): + # This plugin is triggered when marking render as reviewable. + # Therefore, this plugin will run over wrong instances. + # TODO: Don't run this plugin on wrong instances. + # This plugin should run only on review product type + # with instance node of opengl type. + instance_node = instance.data.get("instance_node") + if not instance_node: + self.log.debug("Skipping instance without instance node.") + return + + rop_node = hou.node(instance_node) + if rop_node.type().name() != "opengl": + self.log.debug("Skipping OpenGl extraction. Rop node {} " + "is not an OpenGl node.".format(rop_node.path())) + return + + super(ExtractOpenGL, self).process(instance) + + def update_representation_data(self, + instance: pyblish.api.Instance, + representation: dict): + + tags = ["review"] + if not instance.data.get("keepImages"): + tags.append("delete") + + representation.update({ + # TODO: Avoid this override? + "name": instance.data["imageFormat"], + "ext": instance.data["imageFormat"], + + "tags": tags, + "preview": True, + "camera_name": instance.data.get("review_camera") + }) + return representation + + +class ExtractComposite(ExtractROP, + publish.ColormanagedPyblishPluginMixin): + + label = "Extract Composite (Image Sequence)" + families = ["imagesequence"] + + def update_representation_data(self, + instance: pyblish.api.Instance, + representation: dict): + + if representation["ext"].lower() != "exr": + return + + # Inject colorspace with 'scene_linear' as that's the + # default Houdini working colorspace and all extracted + # OpenEXR images should be in that colorspace. + # https://www.sidefx.com/docs/houdini/render/linear.html#image-formats + self.set_representation_colorspace( + representation, instance.context, + colorspace="scene_linear" + ) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_vdb_cache.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_vdb_cache.py deleted file mode 100644 index 8f0c070ff1..0000000000 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_vdb_cache.py +++ /dev/null @@ -1,44 +0,0 @@ -import os -import hou - -import pyblish.api - -from ayon_houdini.api import plugin -from ayon_houdini.api.lib import render_rop - - -class ExtractVDBCache(plugin.HoudiniExtractorPlugin): - - order = pyblish.api.ExtractorOrder + 0.1 - label = "Extract VDB Cache" - families = ["vdbcache"] - - def process(self, instance): - if instance.data.get("farm"): - self.log.debug("Should be processed on farm, skipping.") - return - ropnode = hou.node(instance.data["instance_node"]) - - # Get the filename from the filename parameter - # `.evalParm(parameter)` will make sure all tokens are resolved - sop_output = ropnode.evalParm("sopoutput") - staging_dir = os.path.normpath(os.path.dirname(sop_output)) - instance.data["stagingDir"] = staging_dir - file_name = os.path.basename(sop_output) - - self.log.info("Writing VDB '%s' to '%s'" % (file_name, staging_dir)) - - render_rop(ropnode) - - if "representations" not in instance.data: - instance.data["representations"] = [] - - representation = { - "name": "vdb", - "ext": "vdb", - "files": instance.data["frames"], - "stagingDir": staging_dir, - "frameStart": instance.data["frameStartHandle"], - "frameEnd": instance.data["frameEndHandle"], - } - instance.data["representations"].append(representation) From 640a409729f808a40bcec549a545fda060ed14b3 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 19 Jun 2024 19:44:29 +0200 Subject: [PATCH 074/129] Move method to where it's used --- .../houdini/client/ayon_houdini/api/plugin.py | 20 ------------------- .../plugins/publish/extract_rop.py | 18 +++++++++++++++++ 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/api/plugin.py b/server_addon/houdini/client/ayon_houdini/api/plugin.py index 0e2308e948..9c6bba925a 100644 --- a/server_addon/houdini/client/ayon_houdini/api/plugin.py +++ b/server_addon/houdini/client/ayon_houdini/api/plugin.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- """Houdini specific Avalon/Pyblish plugin definitions.""" -import os import sys from abc import ( ABCMeta @@ -393,22 +392,3 @@ class HoudiniExtractorPlugin(publish.Extractor): hosts = ["houdini"] settings_category = SETTINGS_CATEGORY - - def validate_expected_frames(self, instance, staging_dir): - """ - Validate all expected files in `instance.data["frames"]` exist in - the staging directory. - """ - filenames = instance.data["frames"] - if isinstance(filenames, str): - # Single frame - filenames = [filenames] - - missing_filenames = [] - for filename in filenames: - path = os.path.join(staging_dir, filename) - if not os.path.isfile(path): - missing_filenames.append(filename) - if missing_filenames: - raise RuntimeError(f"Missing frames: {missing_filenames}") - diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py index 7b9e389f79..ed0c3f9855 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py @@ -58,6 +58,24 @@ class ExtractROP(plugin.HoudiniExtractorPlugin): instance.data.setdefault("representations", []).append(representation) instance.data["stagingDir"] = staging_dir + def validate_expected_frames(self, instance, staging_dir): + """ + Validate all expected files in `instance.data["frames"]` exist in + the staging directory. + """ + filenames = instance.data["frames"] + if isinstance(filenames, str): + # Single frame + filenames = [filenames] + + missing_filenames = [] + for filename in filenames: + path = os.path.join(staging_dir, filename) + if not os.path.isfile(path): + missing_filenames.append(filename) + if missing_filenames: + raise RuntimeError(f"Missing frames: {missing_filenames}") + def update_representation_data(self, instance: pyblish.api.Instance, representation: dict): From c45144a1cf7631137187ed0d5a2b98f620b741d3 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 19 Jun 2024 19:46:02 +0200 Subject: [PATCH 075/129] Avoid redundant `return` --- .../houdini/client/ayon_houdini/plugins/publish/extract_rop.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py index ed0c3f9855..fdabe4c713 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py @@ -112,7 +112,6 @@ class ExtractOpenGL(ExtractROP, def update_representation_data(self, instance: pyblish.api.Instance, representation: dict): - tags = ["review"] if not instance.data.get("keepImages"): tags.append("delete") @@ -126,7 +125,6 @@ class ExtractOpenGL(ExtractROP, "preview": True, "camera_name": instance.data.get("review_camera") }) - return representation class ExtractComposite(ExtractROP, From cde63892a8efa184b908b551dc3924902a845773 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 19 Jun 2024 19:46:50 +0200 Subject: [PATCH 076/129] Do not use extension to base `usd` representation name on --- .../houdini/client/ayon_houdini/plugins/publish/extract_rop.py | 1 + 1 file changed, 1 insertion(+) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py index fdabe4c713..6b05b468d5 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py @@ -43,6 +43,7 @@ class ExtractROP(plugin.HoudiniExtractorPlugin): "bgeo": "bgeo", "rs": "rs", "ass": "ass", + "usd": "usd" }.get(product_type, ext) representation = { From 3de37d0507429a1b5a7d6b3544cd91cabefa349c Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 19 Jun 2024 19:47:34 +0200 Subject: [PATCH 077/129] Revert: Do not use extension to base `usd` representation name on --- .../houdini/client/ayon_houdini/plugins/publish/extract_rop.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py index 6b05b468d5..265f4c1538 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py @@ -42,8 +42,7 @@ class ExtractROP(plugin.HoudiniExtractorPlugin): name = { "bgeo": "bgeo", "rs": "rs", - "ass": "ass", - "usd": "usd" + "ass": "ass" }.get(product_type, ext) representation = { From 928115f25a64f98a83b7593fadfd7142e2d3e35b Mon Sep 17 00:00:00 2001 From: Mustafa Taher Date: Wed, 19 Jun 2024 20:53:05 +0300 Subject: [PATCH 078/129] remove redundant attribute Co-authored-by: Roy Nieterau --- .../client/ayon_houdini/plugins/publish/validate_subset_name.py | 1 - 1 file changed, 1 deletion(-) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_subset_name.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_subset_name.py index 4f15f193fc..a63a4f16c7 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_subset_name.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_subset_name.py @@ -26,7 +26,6 @@ class ValidateSubsetName(plugin.HoudiniInstancePlugin, """ families = ["staticMesh", "hda"] - hosts = ["houdini"] label = "Validate Product Name" order = ValidateContentsOrder + 0.1 actions = [FixProductNameAction, SelectInvalidAction] From 336d38b21835f02bd04b5a2a4e317d07e5bb94b1 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Wed, 19 Jun 2024 20:56:36 +0300 Subject: [PATCH 079/129] move get_avalon_container to ayon_houdini.api.pipeline --- .../client/ayon_houdini/api/pipeline.py | 25 ++++++++++++++----- .../ayon_houdini/plugins/load/load_hda.py | 18 +------------ 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/api/pipeline.py b/server_addon/houdini/client/ayon_houdini/api/pipeline.py index 6af4993d25..0ca7de23bd 100644 --- a/server_addon/houdini/client/ayon_houdini/api/pipeline.py +++ b/server_addon/houdini/client/ayon_houdini/api/pipeline.py @@ -221,12 +221,8 @@ def containerise(name, """ - # Ensure AVALON_CONTAINERS subnet exists - subnet = hou.node(AVALON_CONTAINERS) - if subnet is None: - obj_network = hou.node("/obj") - subnet = obj_network.createNode("subnet", - node_name="AVALON_CONTAINERS") + # Get AVALON_CONTAINERS subnet + subnet = get_avalon_container() # Create proper container name container_name = "{}_{}".format(name, suffix or "CON") @@ -401,6 +397,23 @@ def on_new(): _enforce_start_frame() +def get_avalon_container(): + path = AVALON_CONTAINERS + avalon_container = hou.node(path) + if not avalon_container: + # Let's create avalon container secretly + # but make sure the pipeline still is built the + # way we anticipate it was built, asserting it. + assert path == "/obj/AVALON_CONTAINERS" + + parent = hou.node("/obj") + avalon_container = parent.createNode( + "subnet", node_name="AVALON_CONTAINERS" + ) + + return avalon_container + + def _set_context_settings(): """Apply the project settings from the project definition diff --git a/server_addon/houdini/client/ayon_houdini/plugins/load/load_hda.py b/server_addon/houdini/client/ayon_houdini/plugins/load/load_hda.py index 2f8d6aae49..d7268da39e 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/load/load_hda.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/load/load_hda.py @@ -12,22 +12,6 @@ from ayon_houdini.api import ( plugin ) -def get_avalon_container(): - path = pipeline.AVALON_CONTAINERS - avalon_container = hou.node(path) - if not avalon_container: - # Let's create avalon container secretly - # but make sure the pipeline still is built the - # way we anticipate it was built, asserting it. - assert path == "/obj/AVALON_CONTAINERS" - - parent = hou.node("/obj") - avalon_container = parent.createNode( - "subnet", node_name="AVALON_CONTAINERS" - ) - - return avalon_container - class HdaLoader(plugin.HoudiniLoader): """Load Houdini Digital Asset file.""" @@ -106,7 +90,7 @@ class HdaLoader(plugin.HoudiniLoader): def _create_dedicated_parent_node(self, hda_def): # Get the root node - parent_node = get_avalon_container() + parent_node = pipeline.get_avalon_container() node = None node_type = None if hda_def.nodeTypeCategory() == hou.objNodeTypeCategory(): From 020abb84c678f068e5f57fd86fdffe3c63cd690f Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 19 Jun 2024 19:57:15 +0200 Subject: [PATCH 080/129] Mimic generic ExtractROP logic more from #542 --- .../plugins/publish/collect_frames.py | 5 +++- .../plugins/publish/extract_rop.py | 24 +++++++++---------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_frames.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_frames.py index 82f986ee13..94feb7532c 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_frames.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_frames.py @@ -60,7 +60,10 @@ class CollectFrames(plugin.HoudiniInstancePlugin): # todo: `frames` currently conflicts with "explicit frames" for a # for a custom frame list. So this should be refactored. - instance.data["frames"] = result + instance.data.update({ + "frames": result, + "stagingDir": os.path.dirname(output) + }) @staticmethod def create_file_list(match, start_frame, end_frame): diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py index 265f4c1538..4075db3a2c 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py @@ -24,17 +24,16 @@ class ExtractROP(plugin.HoudiniExtractorPlugin): rop_node = hou.node(instance.data["instance_node"]) - parm = get_output_parameter(rop_node) - filepath = parm.eval() - staging_dir = os.path.dirname(filepath) + files = instance.data["frames"] + first_file = files[0] if isinstance(files, (list, tuple)) else files _, ext = splitext( - filepath, allowed_multidot_extensions=[ + first_file, allowed_multidot_extensions=[ ".ass.gz", ".bgeo.sc", ".bgeo.gz", ".bgeo.lzma", ".bgeo.bz2"] ) render_rop(rop_node) - self.validate_expected_frames(instance, staging_dir) + self.validate_expected_frames(instance) # In some cases representation name is not the the extension # TODO: Preferably we remove this very specific naming @@ -49,30 +48,29 @@ class ExtractROP(plugin.HoudiniExtractorPlugin): "name": name, "ext": ext, "files": instance.data["frames"], - "stagingDir": staging_dir, + "stagingDir": instance.data["stagingDir"], "frameStart": instance.data["frameStartHandle"], "frameEnd": instance.data["frameEndHandle"], } representation = self.update_representation_data(instance, representation) instance.data.setdefault("representations", []).append(representation) - instance.data["stagingDir"] = staging_dir - def validate_expected_frames(self, instance, staging_dir): + def validate_expected_frames(self, instance: pyblish.api.Instance): """ Validate all expected files in `instance.data["frames"]` exist in the staging directory. """ filenames = instance.data["frames"] + staging_dir = instance.data["stagingDir"] if isinstance(filenames, str): # Single frame filenames = [filenames] - missing_filenames = [] - for filename in filenames: - path = os.path.join(staging_dir, filename) - if not os.path.isfile(path): - missing_filenames.append(filename) + missing_filenames = [ + filename for filename in filenames + if not os.path.isfile(os.path.join(staging_dir, filename)) + ] if missing_filenames: raise RuntimeError(f"Missing frames: {missing_filenames}") From a45b68f20fdf13bd64d3641abeddf989de646c9f Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 19 Jun 2024 20:28:57 +0200 Subject: [PATCH 081/129] Expect update in-place --- .../houdini/client/ayon_houdini/plugins/publish/extract_rop.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py index 4075db3a2c..37e45e68d5 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py @@ -52,8 +52,7 @@ class ExtractROP(plugin.HoudiniExtractorPlugin): "frameStart": instance.data["frameStartHandle"], "frameEnd": instance.data["frameEndHandle"], } - representation = self.update_representation_data(instance, - representation) + self.update_representation_data(instance, representation) instance.data.setdefault("representations", []).append(representation) def validate_expected_frames(self, instance: pyblish.api.Instance): From a6de293bb6ece60983c9f4277bd356e3e8a8cbb7 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 19 Jun 2024 20:29:28 +0200 Subject: [PATCH 082/129] Remove dot from `ext` --- .../houdini/client/ayon_houdini/plugins/publish/extract_rop.py | 1 + 1 file changed, 1 insertion(+) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py index 37e45e68d5..784a4f161d 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py @@ -31,6 +31,7 @@ class ExtractROP(plugin.HoudiniExtractorPlugin): ".ass.gz", ".bgeo.sc", ".bgeo.gz", ".bgeo.lzma", ".bgeo.bz2"] ) + ext = ext.lstrip(".") render_rop(rop_node) self.validate_expected_frames(instance) From d0d45abfbad76a7dd24307441761cc5cfce1ddbe Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 19 Jun 2024 20:33:16 +0200 Subject: [PATCH 083/129] Add some debug logs --- .../houdini/client/ayon_houdini/plugins/publish/extract_rop.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py index 784a4f161d..215d4bf213 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py @@ -33,6 +33,8 @@ class ExtractROP(plugin.HoudiniExtractorPlugin): ) ext = ext.lstrip(".") + self.log.debug(f"Rendering {rop_node.path()} to {first_file}..") + render_rop(rop_node) self.validate_expected_frames(instance) From 836554d6f6b0d61b53f3dc543023613ba3877870 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 19 Jun 2024 22:11:24 +0200 Subject: [PATCH 084/129] Remove `imagesequence` because it has its own dedicated plug-in lower down in the file --- .../houdini/client/ayon_houdini/plugins/publish/extract_rop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py index 215d4bf213..23657b66c9 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py @@ -13,7 +13,7 @@ class ExtractROP(plugin.HoudiniExtractorPlugin): label = "Extract ROP" order = pyblish.api.ExtractorOrder - families = ["abc", "camera", "bgeo", "pointcache", "fbx", "imagesequence", + families = ["abc", "camera", "bgeo", "pointcache", "fbx", "vdbcache", "ass", "redshiftproxy", "mantraifd"] targets = ["local", "remote"] From 0ddfaa5b91a2a6d35dec5f6df70bb6094db582a7 Mon Sep 17 00:00:00 2001 From: Mustafa Taher Date: Thu, 20 Jun 2024 16:04:40 +0300 Subject: [PATCH 085/129] refactor `get_avalon_container` to `get_or_create_avalon_container` Co-authored-by: Roy Nieterau --- .../client/ayon_houdini/api/pipeline.py | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/api/pipeline.py b/server_addon/houdini/client/ayon_houdini/api/pipeline.py index 0ca7de23bd..ba7804cb02 100644 --- a/server_addon/houdini/client/ayon_houdini/api/pipeline.py +++ b/server_addon/houdini/client/ayon_houdini/api/pipeline.py @@ -397,21 +397,16 @@ def on_new(): _enforce_start_frame() -def get_avalon_container(): - path = AVALON_CONTAINERS - avalon_container = hou.node(path) - if not avalon_container: - # Let's create avalon container secretly - # but make sure the pipeline still is built the - # way we anticipate it was built, asserting it. - assert path == "/obj/AVALON_CONTAINERS" +def get_or_create_avalon_container() -> "hou.OpNode": + avalon_container = hou.node(AVALON_CONTAINERS) + if avalon_container: + return avalon_container - parent = hou.node("/obj") - avalon_container = parent.createNode( - "subnet", node_name="AVALON_CONTAINERS" - ) - - return avalon_container + parent_path, name = AVALON_CONTAINERS.rsplit("/", 1) + parent = hou.node(parent_path) + return parent.createNode( + "subnet", node_name=name + ) def _set_context_settings(): From 9757f276a092a119453b21266ad7c34c3c8b66b9 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Thu, 20 Jun 2024 16:12:00 +0300 Subject: [PATCH 086/129] replace `get_avalon_container` calls with `get_or_create_avalon_container` --- server_addon/houdini/client/ayon_houdini/api/pipeline.py | 2 +- .../houdini/client/ayon_houdini/plugins/load/load_hda.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/api/pipeline.py b/server_addon/houdini/client/ayon_houdini/api/pipeline.py index ba7804cb02..be8901d1f9 100644 --- a/server_addon/houdini/client/ayon_houdini/api/pipeline.py +++ b/server_addon/houdini/client/ayon_houdini/api/pipeline.py @@ -222,7 +222,7 @@ def containerise(name, """ # Get AVALON_CONTAINERS subnet - subnet = get_avalon_container() + subnet = get_or_create_avalon_container() # Create proper container name container_name = "{}_{}".format(name, suffix or "CON") diff --git a/server_addon/houdini/client/ayon_houdini/plugins/load/load_hda.py b/server_addon/houdini/client/ayon_houdini/plugins/load/load_hda.py index d7268da39e..fcf0e834f8 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/load/load_hda.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/load/load_hda.py @@ -90,7 +90,7 @@ class HdaLoader(plugin.HoudiniLoader): def _create_dedicated_parent_node(self, hda_def): # Get the root node - parent_node = pipeline.get_avalon_container() + parent_node = pipeline.get_or_create_avalon_container() node = None node_type = None if hda_def.nodeTypeCategory() == hou.objNodeTypeCategory(): From 6c5fe9476162b174d294fe42069bcee1afaac372 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Thu, 20 Jun 2024 16:41:28 +0300 Subject: [PATCH 087/129] update dynamic data in CreateHDA --- .../ayon_houdini/plugins/create/create_hda.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/create/create_hda.py b/server_addon/houdini/client/ayon_houdini/plugins/create/create_hda.py index e03f290ae8..179a6c2b00 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/create/create_hda.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/create/create_hda.py @@ -185,8 +185,14 @@ class CreateHDA(plugin.HoudiniCreator): instance ) - dynamic_data["folder"] = { - "label": folder_entity["label"], - "name": folder_entity["name"] - } + dynamic_data.update( + { + "asset": folder_entity["name"], + "folder": { + "label": folder_entity["label"], + "name": folder_entity["name"] + } + } + ) + return dynamic_data From 64c57d4f6a07394dd7f4871ec731bd94abe29131 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 21 Jun 2024 00:56:26 +0200 Subject: [PATCH 088/129] Remove Houdini `Mantra IDF` product, fix #673 --- .../plugins/create/create_mantra_ifd.py | 55 ------------------- .../plugins/publish/collect_cache_farm.py | 4 +- .../plugins/publish/collect_chunk_size.py | 4 +- .../plugins/publish/collect_frames.py | 3 +- .../plugins/publish/extract_mantra_ifd.py | 49 ----------------- .../houdini/server/settings/create.py | 7 --- 6 files changed, 3 insertions(+), 119 deletions(-) delete mode 100644 server_addon/houdini/client/ayon_houdini/plugins/create/create_mantra_ifd.py delete mode 100644 server_addon/houdini/client/ayon_houdini/plugins/publish/extract_mantra_ifd.py diff --git a/server_addon/houdini/client/ayon_houdini/plugins/create/create_mantra_ifd.py b/server_addon/houdini/client/ayon_houdini/plugins/create/create_mantra_ifd.py deleted file mode 100644 index fc5c4819d0..0000000000 --- a/server_addon/houdini/client/ayon_houdini/plugins/create/create_mantra_ifd.py +++ /dev/null @@ -1,55 +0,0 @@ -# -*- coding: utf-8 -*- -"""Creator plugin for creating pointcache alembics.""" -from ayon_houdini.api import plugin -from ayon_core.lib import BoolDef - - -class CreateMantraIFD(plugin.HoudiniCreator): - """Mantra .ifd Archive""" - identifier = "io.openpype.creators.houdini.mantraifd" - label = "Mantra IFD" - product_type = "mantraifd" - icon = "gears" - - def create(self, product_name, instance_data, pre_create_data): - import hou - instance_data.pop("active", None) - instance_data.update({"node_type": "ifd"}) - creator_attributes = instance_data.setdefault( - "creator_attributes", dict()) - creator_attributes["farm"] = pre_create_data["farm"] - instance = super(CreateMantraIFD, self).create( - product_name, - instance_data, - pre_create_data) - - instance_node = hou.node(instance.get("instance_node")) - - filepath = "{}{}".format( - hou.text.expandString("$HIP/pyblish/"), - "{}.$F4.ifd".format(product_name)) - parms = { - # Render frame range - "trange": 1, - # Arnold ROP settings - "soho_diskfile": filepath, - "soho_outputmode": 1 - } - - instance_node.setParms(parms) - - # Lock any parameters in this list - to_lock = ["soho_outputmode", "productType", "id"] - self.lock_parameters(instance_node, to_lock) - - def get_instance_attr_defs(self): - return [ - BoolDef("farm", - label="Submitting to Farm", - default=False) - ] - - def get_pre_create_attr_defs(self): - attrs = super().get_pre_create_attr_defs() - # Use same attributes as for instance attributes - return attrs + self.get_instance_attr_defs() diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_cache_farm.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_cache_farm.py index ecfebccfef..c558f35208 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_cache_farm.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_cache_farm.py @@ -12,9 +12,7 @@ class CollectDataforCache(plugin.HoudiniInstancePlugin): # Run after Collect Frames order = pyblish.api.CollectorOrder + 0.11 - families = ["ass", "pointcache", - "mantraifd", "redshiftproxy", - "vdbcache", "model"] + families = ["ass", "pointcache", "redshiftproxy", "vdbcache", "model"] targets = ["local", "remote"] label = "Collect Data for Cache" diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_chunk_size.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_chunk_size.py index 6ff53b7695..cd94827ba7 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_chunk_size.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_chunk_size.py @@ -9,9 +9,7 @@ class CollectChunkSize(plugin.HoudiniInstancePlugin, """Collect chunk size for cache submission to Deadline.""" order = pyblish.api.CollectorOrder + 0.05 - families = ["ass", "pointcache", - "vdbcache", "mantraifd", - "redshiftproxy", "model"] + families = ["ass", "pointcache", "vdbcache", "redshiftproxy", "model"] targets = ["local", "remote"] label = "Collect Chunk Size" chunk_size = 999999 diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_frames.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_frames.py index 3378657bfd..5b85023123 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_frames.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_frames.py @@ -16,8 +16,7 @@ class CollectFrames(plugin.HoudiniInstancePlugin): order = pyblish.api.CollectorOrder + 0.1 label = "Collect Frames" families = ["vdbcache", "imagesequence", "ass", - "mantraifd", "redshiftproxy", "review", - "pointcache"] + "redshiftproxy", "review", "pointcache"] def process(self, instance): diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_mantra_ifd.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_mantra_ifd.py deleted file mode 100644 index b424f2e452..0000000000 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_mantra_ifd.py +++ /dev/null @@ -1,49 +0,0 @@ -import os -import hou - -import pyblish.api - -from ayon_houdini.api import plugin - - -class ExtractMantraIFD(plugin.HoudiniExtractorPlugin): - - order = pyblish.api.ExtractorOrder - label = "Extract Mantra ifd" - families = ["mantraifd"] - targets = ["local", "remote"] - - def process(self, instance): - if instance.data.get("farm"): - self.log.debug("Should be processed on farm, skipping.") - return - - ropnode = hou.node(instance.data.get("instance_node")) - output = ropnode.evalParm("soho_diskfile") - staging_dir = os.path.dirname(output) - instance.data["stagingDir"] = staging_dir - - files = instance.data["frames"] - missing_frames = [ - frame - for frame in instance.data["frames"] - if not os.path.exists( - os.path.normpath(os.path.join(staging_dir, frame))) - ] - if missing_frames: - raise RuntimeError("Failed to complete Mantra ifd extraction. " - "Missing output files: {}".format( - missing_frames)) - - if "representations" not in instance.data: - instance.data["representations"] = [] - - representation = { - 'name': 'ifd', - 'ext': 'ifd', - 'files': files, - "stagingDir": staging_dir, - "frameStart": instance.data["frameStart"], - "frameEnd": instance.data["frameEnd"], - } - instance.data["representations"].append(representation) diff --git a/server_addon/houdini/server/settings/create.py b/server_addon/houdini/server/settings/create.py index cd1e110c23..02fb052a36 100644 --- a/server_addon/houdini/server/settings/create.py +++ b/server_addon/houdini/server/settings/create.py @@ -51,9 +51,6 @@ class CreatePluginsModel(BaseSettingsModel): CreateKarmaROP: CreatorModel = SettingsField( default_factory=CreatorModel, title="Create Karma ROP") - CreateMantraIFD: CreatorModel = SettingsField( - default_factory=CreatorModel, - title="Create Mantra IFD") CreateMantraROP: CreatorModel = SettingsField( default_factory=CreatorModel, title="Create Mantra ROP") @@ -119,10 +116,6 @@ DEFAULT_HOUDINI_CREATE_SETTINGS = { "enabled": True, "default_variants": ["Main"] }, - "CreateMantraIFD": { - "enabled": True, - "default_variants": ["Main"] - }, "CreateMantraROP": { "enabled": True, "default_variants": ["Main"] From 2a847b1e55750b64eea69da6eb11e07a743ad1d7 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 21 Jun 2024 15:37:11 +0200 Subject: [PATCH 089/129] Bump houdini addon version --- server_addon/houdini/client/ayon_houdini/version.py | 2 +- server_addon/houdini/package.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/version.py b/server_addon/houdini/client/ayon_houdini/version.py index 10d1478249..af2c4557db 100644 --- a/server_addon/houdini/client/ayon_houdini/version.py +++ b/server_addon/houdini/client/ayon_houdini/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'houdini' version.""" -__version__ = "0.3.2" +__version__ = "0.3.3" diff --git a/server_addon/houdini/package.py b/server_addon/houdini/package.py index 1f7879483e..da13bee9c7 100644 --- a/server_addon/houdini/package.py +++ b/server_addon/houdini/package.py @@ -1,6 +1,6 @@ name = "houdini" title = "Houdini" -version = "0.3.2" +version = "0.3.3" client_dir = "ayon_houdini" From 8336a0ff1843c14beffe3ff405aa31591f8be8cd Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 21 Jun 2024 21:42:41 +0800 Subject: [PATCH 090/129] add the setdress setting into ayon setting --- .../ayon_maya/plugins/create/create_setdress.py | 6 ++++-- server_addon/maya/client/ayon_maya/version.py | 2 +- server_addon/maya/package.py | 2 +- server_addon/maya/server/settings/creators.py | 14 ++++++++++++-- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/server_addon/maya/client/ayon_maya/plugins/create/create_setdress.py b/server_addon/maya/client/ayon_maya/plugins/create/create_setdress.py index 98d6de867f..6e1c4e1c4f 100644 --- a/server_addon/maya/client/ayon_maya/plugins/create/create_setdress.py +++ b/server_addon/maya/client/ayon_maya/plugins/create/create_setdress.py @@ -9,14 +9,16 @@ class CreateSetDress(plugin.MayaCreator): label = "Set Dress" product_type = "setdress" icon = "cubes" + exactSetMembersOnly = True + shader = True default_variants = ["Main", "Anim"] def get_instance_attr_defs(self): return [ BoolDef("exactSetMembersOnly", label="Exact Set Members Only", - default=True), + default=self.exactSetMembersOnly), BoolDef("shader", label="Include shader", - default=True) + default=self.shader) ] diff --git a/server_addon/maya/client/ayon_maya/version.py b/server_addon/maya/client/ayon_maya/version.py index 37f9026945..df66e3f399 100644 --- a/server_addon/maya/client/ayon_maya/version.py +++ b/server_addon/maya/client/ayon_maya/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'maya' version.""" -__version__ = "0.2.4" +__version__ = "0.2.5" diff --git a/server_addon/maya/package.py b/server_addon/maya/package.py index 17614ed9c1..3dd863a1b3 100644 --- a/server_addon/maya/package.py +++ b/server_addon/maya/package.py @@ -1,6 +1,6 @@ name = "maya" title = "Maya" -version = "0.2.4" +version = "0.2.5" client_dir = "ayon_maya" ayon_required_addons = { diff --git a/server_addon/maya/server/settings/creators.py b/server_addon/maya/server/settings/creators.py index 5f3b850a1f..ede33b6eec 100644 --- a/server_addon/maya/server/settings/creators.py +++ b/server_addon/maya/server/settings/creators.py @@ -124,6 +124,14 @@ class CreateVrayProxyModel(BaseSettingsModel): default_factory=list, title="Default Products") +class CreateSetDressModel(BaseSettingsModel): + enabled: bool = SettingsField(True) + exactSetMembersOnly: bool = SettingsField(title="Exact Set Members Only") + shader: bool = SettingsField(title="Include shader") + default_variants: list[str] = SettingsField( + default_factory=list, title="Default Products") + + class CreateMultishotLayout(BasicCreatorModel): shotParent: str = SettingsField(title="Shot Parent Folder") groupLoadedAssets: bool = SettingsField(title="Group Loaded Assets") @@ -217,8 +225,8 @@ class CreatorsModel(BaseSettingsModel): default_factory=BasicCreatorModel, title="Create Rig" ) - CreateSetDress: BasicCreatorModel = SettingsField( - default_factory=BasicCreatorModel, + CreateSetDress: CreateSetDressModel = SettingsField( + default_factory=CreateSetDressModel, title="Create Set Dress" ) CreateVrayProxy: CreateVrayProxyModel = SettingsField( @@ -396,6 +404,8 @@ DEFAULT_CREATORS_SETTINGS = { }, "CreateSetDress": { "enabled": True, + "exactSetMembersOnly": True, + "shader": True, "default_variants": [ "Main", "Anim" From f6c7d508d9fe62976f3027bad9311f374b2e1874 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 21 Jun 2024 21:49:11 +0800 Subject: [PATCH 091/129] add the setdress setting into ayon setting --- .../client/ayon_maya/plugins/publish/extract_maya_scene_raw.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server_addon/maya/client/ayon_maya/plugins/publish/extract_maya_scene_raw.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_maya_scene_raw.py index fde48afb8f..2052abe648 100644 --- a/server_addon/maya/client/ayon_maya/plugins/publish/extract_maya_scene_raw.py +++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_maya_scene_raw.py @@ -96,7 +96,7 @@ class ExtractMayaSceneRaw(plugin.MayaExtractorPlugin, AYONPyblishPluginMixin): "preserve_references" ], constructionHistory=True, - shader=True if instance.data.get("shader", True) else False, + shader=instance.data.get("shader", True), constraints=True, expressions=True) From 187b2e98c53e49819c807eab9829f436b16af676 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 21 Jun 2024 16:23:50 +0200 Subject: [PATCH 092/129] Fix FBX export --- .../client/ayon_houdini/plugins/publish/collect_frames.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_frames.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_frames.py index 94feb7532c..64bf0c8f46 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_frames.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_frames.py @@ -17,7 +17,7 @@ class CollectFrames(plugin.HoudiniInstancePlugin): label = "Collect Frames" families = ["vdbcache", "imagesequence", "ass", "mantraifd", "redshiftproxy", "review", - "pointcache"] + "pointcache", "fbx"] def process(self, instance): From 89eb86a140a8195b45bce495244d7cfd21ebc5f6 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 21 Jun 2024 22:34:41 +0800 Subject: [PATCH 093/129] add support to assign default shaders to the published setdress --- .../plugins/publish/extract_maya_scene_raw.py | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/server_addon/maya/client/ayon_maya/plugins/publish/extract_maya_scene_raw.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_maya_scene_raw.py index 2052abe648..7253dc38c2 100644 --- a/server_addon/maya/client/ayon_maya/plugins/publish/extract_maya_scene_raw.py +++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_maya_scene_raw.py @@ -5,7 +5,7 @@ import os from ayon_core.lib import BoolDef from ayon_core.pipeline import AVALON_CONTAINER_ID, AYON_CONTAINER_ID from ayon_core.pipeline.publish import AYONPyblishPluginMixin -from ayon_maya.api.lib import maintained_selection +from ayon_maya.api.lib import maintained_selection, shader from ayon_maya.api import plugin from maya import cmds @@ -88,17 +88,30 @@ class ExtractMayaSceneRaw(plugin.MayaExtractorPlugin, AYONPyblishPluginMixin): ) with maintained_selection(): cmds.select(selection, noExpand=True) - cmds.file(path, - force=True, - typ="mayaAscii" if self.scene_type == "ma" else "mayaBinary", # noqa: E501 - exportSelected=True, - preserveReferences=attribute_values[ - "preserve_references" - ], - constructionHistory=True, - shader=instance.data.get("shader", True), - constraints=True, - expressions=True) + if instance.data.get("shader", True): + with shader(selection, shadingEngine="initialShadingGroup"): + cmds.file(path, + force=True, + typ="mayaAscii" if self.scene_type == "ma" else "mayaBinary", # noqa: E501 + exportSelected=True, + preserveReferences=attribute_values[ + "preserve_references" + ], + constructionHistory=True, + shader=instance.data.get("shader", True), + expressions=True) + else: + cmds.file(path, + force=True, + typ="mayaAscii" if self.scene_type == "ma" else "mayaBinary", # noqa: E501 + exportSelected=True, + preserveReferences=attribute_values[ + "preserve_references" + ], + constructionHistory=True, + shader=True, + constraints=True, + expressions=True) if "representations" not in instance.data: instance.data["representations"] = [] From 32ebf9c34f91e8f4cc88ed6f98d97c2d6d600fd5 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 21 Jun 2024 17:08:05 +0200 Subject: [PATCH 094/129] Fix `camera` product type export --- .../client/ayon_houdini/plugins/publish/collect_frames.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_frames.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_frames.py index 64bf0c8f46..9aceb5a1d0 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_frames.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_frames.py @@ -15,9 +15,8 @@ class CollectFrames(plugin.HoudiniInstancePlugin): # this plugin runs after CollectRopFrameRange order = pyblish.api.CollectorOrder + 0.1 label = "Collect Frames" - families = ["vdbcache", "imagesequence", "ass", - "mantraifd", "redshiftproxy", "review", - "pointcache", "fbx"] + families = ["camera", "vdbcache", "imagesequence", "ass", "mantraifd", + "redshiftproxy", "review", "pointcache", "fbx"] def process(self, instance): From 9b02eed4c6c4620f3b1c08944c0e41f4b8e0fe89 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 21 Jun 2024 23:08:30 +0800 Subject: [PATCH 095/129] add exit stack contextlib for default shader assignment for meshes in the published scene --- .../plugins/publish/extract_maya_scene_raw.py | 29 +++++++------------ 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/server_addon/maya/client/ayon_maya/plugins/publish/extract_maya_scene_raw.py b/server_addon/maya/client/ayon_maya/plugins/publish/extract_maya_scene_raw.py index 7253dc38c2..047b7f6e6c 100644 --- a/server_addon/maya/client/ayon_maya/plugins/publish/extract_maya_scene_raw.py +++ b/server_addon/maya/client/ayon_maya/plugins/publish/extract_maya_scene_raw.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """Extract data as Maya scene (raw).""" import os - +import contextlib from ayon_core.lib import BoolDef from ayon_core.pipeline import AVALON_CONTAINER_ID, AYON_CONTAINER_ID from ayon_core.pipeline.publish import AYONPyblishPluginMixin @@ -88,28 +88,19 @@ class ExtractMayaSceneRaw(plugin.MayaExtractorPlugin, AYONPyblishPluginMixin): ) with maintained_selection(): cmds.select(selection, noExpand=True) - if instance.data.get("shader", True): - with shader(selection, shadingEngine="initialShadingGroup"): - cmds.file(path, - force=True, - typ="mayaAscii" if self.scene_type == "ma" else "mayaBinary", # noqa: E501 - exportSelected=True, - preserveReferences=attribute_values[ - "preserve_references" - ], - constructionHistory=True, - shader=instance.data.get("shader", True), - expressions=True) - else: + with contextlib.ExitStack() as stack: + if not instance.data.get("shader", True): + # Fix bug where export without shader may import the geometry 'green' + # due to the lack of any shader on import. + stack.enter_context(shader(selection, shadingEngine="initialShadingGroup")) + cmds.file(path, force=True, - typ="mayaAscii" if self.scene_type == "ma" else "mayaBinary", # noqa: E501 + typ="mayaAscii" if self.scene_type == "ma" else "mayaBinary", exportSelected=True, - preserveReferences=attribute_values[ - "preserve_references" - ], + preserveReferences=attribute_values["preserve_references"], constructionHistory=True, - shader=True, + shader=instance.data.get("shader", True), constraints=True, expressions=True) From b61bb2dfc8aaf0452e0548ba8559ef6378d0c93b Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 21 Jun 2024 18:00:08 +0200 Subject: [PATCH 096/129] Bump houdini addon version --- server_addon/houdini/client/ayon_houdini/version.py | 2 +- server_addon/houdini/package.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/version.py b/server_addon/houdini/client/ayon_houdini/version.py index 10d1478249..af2c4557db 100644 --- a/server_addon/houdini/client/ayon_houdini/version.py +++ b/server_addon/houdini/client/ayon_houdini/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'houdini' version.""" -__version__ = "0.3.2" +__version__ = "0.3.3" diff --git a/server_addon/houdini/package.py b/server_addon/houdini/package.py index 1f7879483e..da13bee9c7 100644 --- a/server_addon/houdini/package.py +++ b/server_addon/houdini/package.py @@ -1,6 +1,6 @@ name = "houdini" title = "Houdini" -version = "0.3.2" +version = "0.3.3" client_dir = "ayon_houdini" From 24a508a7040af06c7ded60d0dfae76e6482ffda8 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Sat, 22 Jun 2024 00:23:28 +0800 Subject: [PATCH 097/129] remove unused function in extract rop --- .../houdini/client/ayon_houdini/plugins/publish/extract_rop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py index 23657b66c9..62a38c0b93 100644 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py +++ b/server_addon/houdini/client/ayon_houdini/plugins/publish/extract_rop.py @@ -5,7 +5,7 @@ import pyblish.api from ayon_core.pipeline import publish from ayon_houdini.api import plugin -from ayon_houdini.api.lib import render_rop, get_output_parameter, splitext +from ayon_houdini.api.lib import render_rop, splitext class ExtractROP(plugin.HoudiniExtractorPlugin): From 7e6af882bd42a24cd8b438f7d091a4e2c2c080fe Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 21 Jun 2024 22:13:06 +0300 Subject: [PATCH 098/129] support_opening_workfile_on_launching_houdini --- .../houdini/client/ayon_houdini/api/lib.py | 50 ++++++++++++++++++- .../client/ayon_houdini/api/pipeline.py | 5 +- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/api/lib.py b/server_addon/houdini/client/ayon_houdini/api/lib.py index 671265fae9..eefe895b8f 100644 --- a/server_addon/houdini/client/ayon_houdini/api/lib.py +++ b/server_addon/houdini/client/ayon_houdini/api/lib.py @@ -8,6 +8,7 @@ import json from contextlib import contextmanager import six +from qtpy import QtCore, QtWidgets import ayon_api from ayon_core.lib import StringTemplate @@ -23,7 +24,11 @@ from ayon_core.pipeline import ( from ayon_core.pipeline.create import CreateContext from ayon_core.pipeline.template_data import get_template_data from ayon_core.pipeline.context_tools import get_current_folder_entity -from ayon_core.tools.utils import PopupUpdateKeys, SimplePopup +from ayon_core.tools.utils import ( + PopupUpdateKeys, + SimplePopup, + host_tools +) from ayon_core.tools.utils.host_tools import get_tool_by_name import hou @@ -1193,3 +1198,46 @@ def prompt_reset_context(): update_content_on_context_change() dialog.deleteLater() + + +def wait_startup_launch_workfiles_app(): + """Show workfiles tool on Houdini launch. + + Trigger to show workfiles tool on application launch. Can be executed only + once all other calls are ignored. + + Workfiles tool show is deferred after application initialization using + QTimer. + + Basically, it should wait till the app finish starting up. + """ + + # Show workfiles tool using timer + # - this will be probably triggered during initialization in that case + # the application is not be able to show uis so it must be + # deferred using timer + # - timer should be processed when initialization ends + # When applications starts to process events. + timer = QtCore.QTimer() + timer.timeout.connect(lambda: _launch_workfile_app(timer)) + timer.setInterval(100) + timer.start() + + +def _launch_workfile_app(timer): + # Safeguard to not show window when application is still starting up + # or is already closing down. + closing_down = QtWidgets.QApplication.closingDown() + starting_up = QtWidgets.QApplication.startingUp() + + # Stop the timer if application finished start up of is closing down + if closing_down or not starting_up: + timer.stop() + + # Skip if application is starting up or closing down + if starting_up or closing_down: + return + + # Make sure on top is enabled on first show so the window is not hidden + # under main nuke window + host_tools.show_workfiles(parent=hou.qt.mainWindow(), on_top=True) diff --git a/server_addon/houdini/client/ayon_houdini/api/pipeline.py b/server_addon/houdini/client/ayon_houdini/api/pipeline.py index 6af4993d25..9d420a92d3 100644 --- a/server_addon/houdini/client/ayon_houdini/api/pipeline.py +++ b/server_addon/houdini/client/ayon_houdini/api/pipeline.py @@ -85,10 +85,9 @@ class HoudiniHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): # initialization during start up delays Houdini UI by minutes # making it extremely slow to launch. hdefereval.executeDeferred(shelves.generate_shelves) - - if not IS_HEADLESS: - import hdefereval # noqa, hdefereval is only available in ui mode hdefereval.executeDeferred(creator_node_shelves.install) + if os.environ.get("AYON_WORKFILE_TOOL_ON_START"): + hdefereval.executeDeferred(lib.wait_startup_launch_workfiles_app) def workfile_has_unsaved_changes(self): return hou.hipFile.hasUnsavedChanges() From e6bb56b088ba001859ff02b61d577638f8d61ce1 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 21 Jun 2024 22:56:53 +0300 Subject: [PATCH 099/129] refactor launching the workfile - remove redundant code --- .../houdini/client/ayon_houdini/api/lib.py | 50 +------------------ .../client/ayon_houdini/api/pipeline.py | 11 +++- 2 files changed, 10 insertions(+), 51 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/api/lib.py b/server_addon/houdini/client/ayon_houdini/api/lib.py index eefe895b8f..671265fae9 100644 --- a/server_addon/houdini/client/ayon_houdini/api/lib.py +++ b/server_addon/houdini/client/ayon_houdini/api/lib.py @@ -8,7 +8,6 @@ import json from contextlib import contextmanager import six -from qtpy import QtCore, QtWidgets import ayon_api from ayon_core.lib import StringTemplate @@ -24,11 +23,7 @@ from ayon_core.pipeline import ( from ayon_core.pipeline.create import CreateContext from ayon_core.pipeline.template_data import get_template_data from ayon_core.pipeline.context_tools import get_current_folder_entity -from ayon_core.tools.utils import ( - PopupUpdateKeys, - SimplePopup, - host_tools -) +from ayon_core.tools.utils import PopupUpdateKeys, SimplePopup from ayon_core.tools.utils.host_tools import get_tool_by_name import hou @@ -1198,46 +1193,3 @@ def prompt_reset_context(): update_content_on_context_change() dialog.deleteLater() - - -def wait_startup_launch_workfiles_app(): - """Show workfiles tool on Houdini launch. - - Trigger to show workfiles tool on application launch. Can be executed only - once all other calls are ignored. - - Workfiles tool show is deferred after application initialization using - QTimer. - - Basically, it should wait till the app finish starting up. - """ - - # Show workfiles tool using timer - # - this will be probably triggered during initialization in that case - # the application is not be able to show uis so it must be - # deferred using timer - # - timer should be processed when initialization ends - # When applications starts to process events. - timer = QtCore.QTimer() - timer.timeout.connect(lambda: _launch_workfile_app(timer)) - timer.setInterval(100) - timer.start() - - -def _launch_workfile_app(timer): - # Safeguard to not show window when application is still starting up - # or is already closing down. - closing_down = QtWidgets.QApplication.closingDown() - starting_up = QtWidgets.QApplication.startingUp() - - # Stop the timer if application finished start up of is closing down - if closing_down or not starting_up: - timer.stop() - - # Skip if application is starting up or closing down - if starting_up or closing_down: - return - - # Make sure on top is enabled on first show so the window is not hidden - # under main nuke window - host_tools.show_workfiles(parent=hou.qt.mainWindow(), on_top=True) diff --git a/server_addon/houdini/client/ayon_houdini/api/pipeline.py b/server_addon/houdini/client/ayon_houdini/api/pipeline.py index 9d420a92d3..2c28e33929 100644 --- a/server_addon/houdini/client/ayon_houdini/api/pipeline.py +++ b/server_addon/houdini/client/ayon_houdini/api/pipeline.py @@ -6,7 +6,7 @@ import logging import hou # noqa from ayon_core.host import HostBase, IWorkfileHost, ILoadHost, IPublishHost - +from ayon_core.tools.utils import host_tools import pyblish.api from ayon_core.pipeline import ( @@ -25,6 +25,13 @@ from ayon_core.lib import ( emit_event, ) +def show_workfiles_tool(): + # Make sure on top is enabled on first show so the + # window is not hidden under main nuke window + print("showing workfiles tool..") + from ayon_core.tools.utils import host_tools + host_tools.show_workfiles(parent=hou.qt.mainWindow(), + on_top=True) log = logging.getLogger("ayon_houdini") @@ -87,7 +94,7 @@ class HoudiniHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): hdefereval.executeDeferred(shelves.generate_shelves) hdefereval.executeDeferred(creator_node_shelves.install) if os.environ.get("AYON_WORKFILE_TOOL_ON_START"): - hdefereval.executeDeferred(lib.wait_startup_launch_workfiles_app) + hdefereval.executeDeferred(lambda: host_tools.show_workfiles(parent=hou.qt.mainWindow())) def workfile_has_unsaved_changes(self): return hou.hipFile.hasUnsavedChanges() From d88c36b01ec50a706bb544f09574dadc963e762e Mon Sep 17 00:00:00 2001 From: Mustafa Taher Date: Fri, 21 Jun 2024 23:05:34 +0300 Subject: [PATCH 100/129] remove redundant code Co-authored-by: Roy Nieterau --- server_addon/houdini/client/ayon_houdini/api/pipeline.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/api/pipeline.py b/server_addon/houdini/client/ayon_houdini/api/pipeline.py index 2c28e33929..463191f787 100644 --- a/server_addon/houdini/client/ayon_houdini/api/pipeline.py +++ b/server_addon/houdini/client/ayon_houdini/api/pipeline.py @@ -25,13 +25,6 @@ from ayon_core.lib import ( emit_event, ) -def show_workfiles_tool(): - # Make sure on top is enabled on first show so the - # window is not hidden under main nuke window - print("showing workfiles tool..") - from ayon_core.tools.utils import host_tools - host_tools.show_workfiles(parent=hou.qt.mainWindow(), - on_top=True) log = logging.getLogger("ayon_houdini") From 583bc8f86b596ed0e6fc27b97e46b20d5b9929b5 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Fri, 21 Jun 2024 23:07:52 +0300 Subject: [PATCH 101/129] use env_value_to_bool instead of os.environ.get --- server_addon/houdini/client/ayon_houdini/api/pipeline.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server_addon/houdini/client/ayon_houdini/api/pipeline.py b/server_addon/houdini/client/ayon_houdini/api/pipeline.py index 463191f787..22a15605c7 100644 --- a/server_addon/houdini/client/ayon_houdini/api/pipeline.py +++ b/server_addon/houdini/client/ayon_houdini/api/pipeline.py @@ -23,6 +23,7 @@ from ayon_houdini.api import lib, shelves, creator_node_shelves from ayon_core.lib import ( register_event_callback, emit_event, + env_value_to_bool, ) @@ -86,7 +87,7 @@ class HoudiniHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost): # making it extremely slow to launch. hdefereval.executeDeferred(shelves.generate_shelves) hdefereval.executeDeferred(creator_node_shelves.install) - if os.environ.get("AYON_WORKFILE_TOOL_ON_START"): + if env_value_to_bool("AYON_WORKFILE_TOOL_ON_START"): hdefereval.executeDeferred(lambda: host_tools.show_workfiles(parent=hou.qt.mainWindow())) def workfile_has_unsaved_changes(self): From 51d63d849335257098d0791703ca0484db8f9ed6 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 24 Jun 2024 09:29:06 +0200 Subject: [PATCH 102/129] Houdini: Remove the legacy creator from before new publisher UI --- .../houdini/client/ayon_houdini/api/plugin.py | 82 ++----------------- 1 file changed, 5 insertions(+), 77 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/api/plugin.py b/server_addon/houdini/client/ayon_houdini/api/plugin.py index 9c6bba925a..c78d85924e 100644 --- a/server_addon/houdini/client/ayon_houdini/api/plugin.py +++ b/server_addon/houdini/client/ayon_houdini/api/plugin.py @@ -10,8 +10,7 @@ import hou import pyblish.api from ayon_core.pipeline import ( CreatorError, - LegacyCreator, - Creator as NewCreator, + Creator, CreatedInstance, AYON_INSTANCE_ID, AVALON_INSTANCE_ID, @@ -22,84 +21,13 @@ from ayon_core.lib import BoolDef from .lib import imprint, read, lsattr, add_self_publish_button +# Backwards compatibility +NewCreator = Creator + SETTINGS_CATEGORY = "houdini" -class Creator(LegacyCreator): - """Creator plugin to create instances in Houdini - - To support the wide range of node types for render output (Alembic, VDB, - Mantra) the Creator needs a node type to create the correct instance - - By default, if none is given, is `geometry`. An example of accepted node - types: geometry, alembic, ifd (mantra) - - Please check the Houdini documentation for more node types. - - Tip: to find the exact node type to create press the `i` left of the node - when hovering over a node. The information is visible under the name of - the node. - - Deprecated: - This creator is deprecated and will be removed in future version. - - """ - defaults = ['Main'] - - def __init__(self, *args, **kwargs): - super(Creator, self).__init__(*args, **kwargs) - self.nodes = [] - - def process(self): - """This is the base functionality to create instances in Houdini - - The selected nodes are stored in self to be used in an override method. - This is currently necessary in order to support the multiple output - types in Houdini which can only be rendered through their own node. - - Default node type if none is given is `geometry` - - It also makes it easier to apply custom settings per instance type - - Example of override method for Alembic: - - def process(self): - instance = super(CreateEpicNode, self, process() - # Set parameters for Alembic node - instance.setParms( - {"sop_path": "$HIP/%s.abc" % self.nodes[0]} - ) - - Returns: - hou.Node - - """ - try: - if (self.options or {}).get("useSelection"): - self.nodes = hou.selectedNodes() - - # Get the node type and remove it from the data, not needed - node_type = self.data.pop("node_type", None) - if node_type is None: - node_type = "geometry" - - # Get out node - out = hou.node("/out") - instance = out.createNode(node_type, node_name=self.name) - instance.moveToGoodPosition() - - imprint(instance, self.data) - - self._process(instance) - - except hou.Error as er: - six.reraise( - CreatorError, - CreatorError("Creator error: {}".format(er)), - sys.exc_info()[2]) - - class HoudiniCreatorBase(object): @staticmethod def cache_instance_data(shared_data): @@ -170,7 +98,7 @@ class HoudiniCreatorBase(object): @six.add_metaclass(ABCMeta) -class HoudiniCreator(NewCreator, HoudiniCreatorBase): +class HoudiniCreator(Creator, HoudiniCreatorBase): """Base class for most of the Houdini creator plugins.""" selected_nodes = [] settings_name = None From fc4df957e6351d120b8ac7cc6e566d762729a3e8 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 24 Jun 2024 09:59:35 +0200 Subject: [PATCH 103/129] Maya: Remove the legacy creator from before new publisher UI --- .../maya/client/ayon_maya/api/__init__.py | 2 -- .../maya/client/ayon_maya/api/plugin.py | 26 +++++-------------- 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/server_addon/maya/client/ayon_maya/api/__init__.py b/server_addon/maya/client/ayon_maya/api/__init__.py index 0948282f57..8783fbeeb7 100644 --- a/server_addon/maya/client/ayon_maya/api/__init__.py +++ b/server_addon/maya/client/ayon_maya/api/__init__.py @@ -12,7 +12,6 @@ from .pipeline import ( MayaHost, ) from .plugin import ( - Creator, Loader ) @@ -45,7 +44,6 @@ __all__ = [ "containerise", "MayaHost", - "Creator", "Loader", # Workfiles API diff --git a/server_addon/maya/client/ayon_maya/api/plugin.py b/server_addon/maya/client/ayon_maya/api/plugin.py index b8d9748ef1..6ff428567e 100644 --- a/server_addon/maya/client/ayon_maya/api/plugin.py +++ b/server_addon/maya/client/ayon_maya/api/plugin.py @@ -15,10 +15,9 @@ from ayon_core.pipeline import ( Anatomy, AutoCreator, CreatedInstance, - Creator as NewCreator, + Creator, CreatorError, HiddenCreator, - LegacyCreator, LoaderPlugin, get_current_project_name, get_representation_path, @@ -35,6 +34,9 @@ from . import lib from .lib import imprint, read from .pipeline import containerise +# Backwards compatibility +NewCreator = Creator + log = Logger.get_logger() SETTINGS_CATEGORY = "maya" @@ -70,22 +72,6 @@ def get_reference_node_parents(*args, **kwargs): return lib.get_reference_node_parents(*args, **kwargs) -class Creator(LegacyCreator): - defaults = ['Main'] - - def process(self): - nodes = list() - - with lib.undo_chunk(): - if (self.options or {}).get("useSelection"): - nodes = cmds.ls(selection=True) - - instance = cmds.sets(nodes, name=self.name) - lib.imprint(instance, self.data) - - return instance - - @six.add_metaclass(ABCMeta) class MayaCreatorBase(object): @@ -274,7 +260,7 @@ class MayaCreatorBase(object): @six.add_metaclass(ABCMeta) -class MayaCreator(NewCreator, MayaCreatorBase): +class MayaCreator(Creator, MayaCreatorBase): settings_category = "maya" @@ -381,7 +367,7 @@ def ensure_namespace(namespace): return cmds.namespace(add=namespace) -class RenderlayerCreator(NewCreator, MayaCreatorBase): +class RenderlayerCreator(Creator, MayaCreatorBase): """Creator which creates an instance per renderlayer in the workfile. Create and manages renderlayer product per renderLayer in workfile. From 61074623d498f6191f19934df546d4586239b133 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 24 Jun 2024 10:01:55 +0200 Subject: [PATCH 104/129] Remove import of `Creator` which isn't actually the Houdini specific Creator anyway --- server_addon/houdini/client/ayon_houdini/api/__init__.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/api/__init__.py b/server_addon/houdini/client/ayon_houdini/api/__init__.py index 2663a55f6f..358113a555 100644 --- a/server_addon/houdini/client/ayon_houdini/api/__init__.py +++ b/server_addon/houdini/client/ayon_houdini/api/__init__.py @@ -4,10 +4,6 @@ from .pipeline import ( containerise ) -from .plugin import ( - Creator, -) - from .lib import ( lsattr, lsattrs, @@ -23,8 +19,6 @@ __all__ = [ "ls", "containerise", - "Creator", - # Utility functions "lsattr", "lsattrs", From a01f047e2a6761ffb2cef86c499dbb8779cc584e Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 24 Jun 2024 10:42:11 +0200 Subject: [PATCH 105/129] convert version string to integer --- server_addon/maya/client/ayon_maya/api/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server_addon/maya/client/ayon_maya/api/lib.py b/server_addon/maya/client/ayon_maya/api/lib.py index 3b351ec1f0..0242dafc0b 100644 --- a/server_addon/maya/client/ayon_maya/api/lib.py +++ b/server_addon/maya/client/ayon_maya/api/lib.py @@ -1733,7 +1733,7 @@ def is_valid_reference_node(reference_node): """ # maya 2022 is missing `isValidReference` so the check needs to be # done in different way. - if cmds.about(version=True) < 2023: + if int(cmds.about(version=True)) < 2023: try: cmds.referenceQuery(reference_node, filename=True) return True From 0fdb63bc6779e3956c5b2f240a0d6fceb664b641 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 24 Jun 2024 11:01:15 +0200 Subject: [PATCH 106/129] bump version to '0.2.5' --- server_addon/maya/client/ayon_maya/version.py | 2 +- server_addon/maya/package.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server_addon/maya/client/ayon_maya/version.py b/server_addon/maya/client/ayon_maya/version.py index 37f9026945..df66e3f399 100644 --- a/server_addon/maya/client/ayon_maya/version.py +++ b/server_addon/maya/client/ayon_maya/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'maya' version.""" -__version__ = "0.2.4" +__version__ = "0.2.5" diff --git a/server_addon/maya/package.py b/server_addon/maya/package.py index 17614ed9c1..3dd863a1b3 100644 --- a/server_addon/maya/package.py +++ b/server_addon/maya/package.py @@ -1,6 +1,6 @@ name = "maya" title = "Maya" -version = "0.2.4" +version = "0.2.5" client_dir = "ayon_maya" ayon_required_addons = { From 2e8d578d8f4999fb3e58ec473470c77ba62a2d3f Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 24 Jun 2024 11:22:29 +0200 Subject: [PATCH 107/129] Update server_addon/maya/client/ayon_maya/api/plugin.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- server_addon/maya/client/ayon_maya/api/plugin.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/server_addon/maya/client/ayon_maya/api/plugin.py b/server_addon/maya/client/ayon_maya/api/plugin.py index 6ff428567e..45b2151e26 100644 --- a/server_addon/maya/client/ayon_maya/api/plugin.py +++ b/server_addon/maya/client/ayon_maya/api/plugin.py @@ -34,8 +34,6 @@ from . import lib from .lib import imprint, read from .pipeline import containerise -# Backwards compatibility -NewCreator = Creator log = Logger.get_logger() SETTINGS_CATEGORY = "maya" From b0701dc6d5b54178ebbeb7c8d38a792c96bde6f5 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 24 Jun 2024 11:23:36 +0200 Subject: [PATCH 108/129] Remove backwards compatibility --- server_addon/houdini/client/ayon_houdini/api/plugin.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/api/plugin.py b/server_addon/houdini/client/ayon_houdini/api/plugin.py index c78d85924e..b841b57617 100644 --- a/server_addon/houdini/client/ayon_houdini/api/plugin.py +++ b/server_addon/houdini/client/ayon_houdini/api/plugin.py @@ -21,9 +21,6 @@ from ayon_core.lib import BoolDef from .lib import imprint, read, lsattr, add_self_publish_button -# Backwards compatibility -NewCreator = Creator - SETTINGS_CATEGORY = "houdini" From 48a9077acb7c3ef5d5c4bc8912e2f5023823833d Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 24 Jun 2024 11:24:39 +0200 Subject: [PATCH 109/129] Bump Maya addon version --- server_addon/maya/client/ayon_maya/version.py | 2 +- server_addon/maya/package.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server_addon/maya/client/ayon_maya/version.py b/server_addon/maya/client/ayon_maya/version.py index df66e3f399..c5fbef58fe 100644 --- a/server_addon/maya/client/ayon_maya/version.py +++ b/server_addon/maya/client/ayon_maya/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'maya' version.""" -__version__ = "0.2.5" +__version__ = "0.2.6" diff --git a/server_addon/maya/package.py b/server_addon/maya/package.py index 3dd863a1b3..2f70b630d5 100644 --- a/server_addon/maya/package.py +++ b/server_addon/maya/package.py @@ -1,6 +1,6 @@ name = "maya" title = "Maya" -version = "0.2.5" +version = "0.2.6" client_dir = "ayon_maya" ayon_required_addons = { From 79ccd665944bc7546f40a8695b06671760d3b895 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 24 Jun 2024 11:25:39 +0200 Subject: [PATCH 110/129] Cosmetics --- server_addon/maya/client/ayon_maya/api/plugin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/server_addon/maya/client/ayon_maya/api/plugin.py b/server_addon/maya/client/ayon_maya/api/plugin.py index 45b2151e26..d2678e2100 100644 --- a/server_addon/maya/client/ayon_maya/api/plugin.py +++ b/server_addon/maya/client/ayon_maya/api/plugin.py @@ -34,7 +34,6 @@ from . import lib from .lib import imprint, read from .pipeline import containerise - log = Logger.get_logger() SETTINGS_CATEGORY = "maya" From 7daf688533e3db83cf32d1c138808d49fc45f58b Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 24 Jun 2024 11:28:20 +0200 Subject: [PATCH 111/129] Bump houdini addon version --- server_addon/houdini/client/ayon_houdini/version.py | 2 +- server_addon/houdini/package.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/version.py b/server_addon/houdini/client/ayon_houdini/version.py index af2c4557db..66f3ac59e7 100644 --- a/server_addon/houdini/client/ayon_houdini/version.py +++ b/server_addon/houdini/client/ayon_houdini/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'houdini' version.""" -__version__ = "0.3.3" +__version__ = "0.3.4" diff --git a/server_addon/houdini/package.py b/server_addon/houdini/package.py index da13bee9c7..0c1b1fcf9b 100644 --- a/server_addon/houdini/package.py +++ b/server_addon/houdini/package.py @@ -1,6 +1,6 @@ name = "houdini" title = "Houdini" -version = "0.3.3" +version = "0.3.4" client_dir = "ayon_houdini" From 2834c16dec124522bdd68914192823de61ff8675 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 24 Jun 2024 12:25:47 +0200 Subject: [PATCH 112/129] Skip viewers that are not currently visible --- .../houdini/client/ayon_houdini/api/lib.py | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/api/lib.py b/server_addon/houdini/client/ayon_houdini/api/lib.py index 671265fae9..29fb038de0 100644 --- a/server_addon/houdini/client/ayon_houdini/api/lib.py +++ b/server_addon/houdini/client/ayon_houdini/api/lib.py @@ -1038,17 +1038,25 @@ def add_self_publish_button(node): node.setParmTemplateGroup(template) -def get_scene_viewer(): +def get_scene_viewer(visible_only=True): """ Return an instance of a visible viewport. There may be many, some could be closed, any visible are current + Arguments: + visible_only (Optional[bool]): Only return viewers that currently + are the active tab (and hence are visible). + Returns: Optional[hou.SceneViewer]: A scene viewer, if any. """ panes = hou.ui.paneTabs() panes = [x for x in panes if x.type() == hou.paneTabType.SceneViewer] + + if visible_only: + return next((pane for pane in panes if pane.isCurrentTab()), None) + panes = sorted(panes, key=lambda x: x.isCurrentTab()) if panes: return panes[-1] @@ -1067,12 +1075,10 @@ def sceneview_snapshot( So, it's capable of generating snapshots image sequence. It works in different Houdini context e.g. Objects, Solaris - Example: - This is how the function can be used:: - - from ayon_houdini.api import lib - sceneview = hou.ui.paneTabOfType(hou.paneTabType.SceneViewer) - lib.sceneview_snapshot(sceneview) + Example:: + >>> from ayon_houdini.api import lib + >>> sceneview = hou.ui.paneTabOfType(hou.paneTabType.SceneViewer) + >>> lib.sceneview_snapshot(sceneview) Notes: .png output will render poorly, so use .jpg. From 201d36589935b92c137a2a4810e2a406ad0d9be5 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 24 Jun 2024 12:31:29 +0200 Subject: [PATCH 113/129] Cosmetics (fix docstring triple quotes) --- .../client/ayon_aftereffects/api/workfile_template_builder.py | 2 +- .../client/ayon_blender/plugins/publish/extract_thumbnail.py | 2 +- .../client/ayon_hiero/vendor/google/protobuf/text_format.py | 2 +- .../nuke/client/ayon_nuke/vendor/google/protobuf/text_format.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/server_addon/aftereffects/client/ayon_aftereffects/api/workfile_template_builder.py b/server_addon/aftereffects/client/ayon_aftereffects/api/workfile_template_builder.py index 77fd1059b5..a4d90be548 100644 --- a/server_addon/aftereffects/client/ayon_aftereffects/api/workfile_template_builder.py +++ b/server_addon/aftereffects/client/ayon_aftereffects/api/workfile_template_builder.py @@ -92,7 +92,7 @@ class AEPlaceholderPlugin(PlaceholderPlugin): return None, None def _collect_scene_placeholders(self): - """" Cache placeholder data to shared data. + """ Cache placeholder data to shared data. Returns: (list) of dicts """ diff --git a/server_addon/blender/client/ayon_blender/plugins/publish/extract_thumbnail.py b/server_addon/blender/client/ayon_blender/plugins/publish/extract_thumbnail.py index 40097aaa89..e3bce8bf73 100644 --- a/server_addon/blender/client/ayon_blender/plugins/publish/extract_thumbnail.py +++ b/server_addon/blender/client/ayon_blender/plugins/publish/extract_thumbnail.py @@ -83,7 +83,7 @@ class ExtractThumbnail(plugin.BlenderExtractor): instance.data["representations"].append(representation) def _fix_output_path(self, filepath): - """"Workaround to return correct filepath. + """Workaround to return correct filepath. To workaround this we just glob.glob() for any file extensions and assume the latest modified file is the correct file and return it. diff --git a/server_addon/hiero/client/ayon_hiero/vendor/google/protobuf/text_format.py b/server_addon/hiero/client/ayon_hiero/vendor/google/protobuf/text_format.py index 412385c26f..39739e21fb 100644 --- a/server_addon/hiero/client/ayon_hiero/vendor/google/protobuf/text_format.py +++ b/server_addon/hiero/client/ayon_hiero/vendor/google/protobuf/text_format.py @@ -548,7 +548,7 @@ class _Printer(object): self.out.write(' ' if self.as_one_line else '\n') def _PrintShortRepeatedPrimitivesValue(self, field, value): - """"Prints short repeated primitives value.""" + """Prints short repeated primitives value.""" # Note: this is called only when value has at least one element. self._PrintFieldName(field) self.out.write(' [') diff --git a/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/text_format.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/text_format.py index 412385c26f..39739e21fb 100644 --- a/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/text_format.py +++ b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/text_format.py @@ -548,7 +548,7 @@ class _Printer(object): self.out.write(' ' if self.as_one_line else '\n') def _PrintShortRepeatedPrimitivesValue(self, field, value): - """"Prints short repeated primitives value.""" + """Prints short repeated primitives value.""" # Note: this is called only when value has at least one element. self._PrintFieldName(field) self.out.write(' [') From 28dede482fedddebf272696c85b21db8fca909f3 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 24 Jun 2024 14:37:48 +0200 Subject: [PATCH 114/129] Update server_addon/aftereffects/client/ayon_aftereffects/api/workfile_template_builder.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- .../client/ayon_aftereffects/api/workfile_template_builder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server_addon/aftereffects/client/ayon_aftereffects/api/workfile_template_builder.py b/server_addon/aftereffects/client/ayon_aftereffects/api/workfile_template_builder.py index a4d90be548..7fbd469851 100644 --- a/server_addon/aftereffects/client/ayon_aftereffects/api/workfile_template_builder.py +++ b/server_addon/aftereffects/client/ayon_aftereffects/api/workfile_template_builder.py @@ -92,7 +92,7 @@ class AEPlaceholderPlugin(PlaceholderPlugin): return None, None def _collect_scene_placeholders(self): - """ Cache placeholder data to shared data. + """Cache placeholder data to shared data. Returns: (list) of dicts """ From e342f9fe0d6594e930f28ed83617ee61c33a7602 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 24 Jun 2024 14:49:23 +0200 Subject: [PATCH 115/129] Revert changes to vendorized files Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- .../client/ayon_hiero/vendor/google/protobuf/text_format.py | 2 +- .../nuke/client/ayon_nuke/vendor/google/protobuf/text_format.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server_addon/hiero/client/ayon_hiero/vendor/google/protobuf/text_format.py b/server_addon/hiero/client/ayon_hiero/vendor/google/protobuf/text_format.py index 39739e21fb..412385c26f 100644 --- a/server_addon/hiero/client/ayon_hiero/vendor/google/protobuf/text_format.py +++ b/server_addon/hiero/client/ayon_hiero/vendor/google/protobuf/text_format.py @@ -548,7 +548,7 @@ class _Printer(object): self.out.write(' ' if self.as_one_line else '\n') def _PrintShortRepeatedPrimitivesValue(self, field, value): - """Prints short repeated primitives value.""" + """"Prints short repeated primitives value.""" # Note: this is called only when value has at least one element. self._PrintFieldName(field) self.out.write(' [') diff --git a/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/text_format.py b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/text_format.py index 39739e21fb..412385c26f 100644 --- a/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/text_format.py +++ b/server_addon/nuke/client/ayon_nuke/vendor/google/protobuf/text_format.py @@ -548,7 +548,7 @@ class _Printer(object): self.out.write(' ' if self.as_one_line else '\n') def _PrintShortRepeatedPrimitivesValue(self, field, value): - """Prints short repeated primitives value.""" + """"Prints short repeated primitives value.""" # Note: this is called only when value has at least one element. self._PrintFieldName(field) self.out.write(' [') From 016ad0e0299ce8c411823c9f0b54aba31456f5b8 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 24 Jun 2024 15:43:13 +0200 Subject: [PATCH 116/129] Bump addon version --- server_addon/houdini/client/ayon_houdini/version.py | 2 +- server_addon/houdini/package.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/version.py b/server_addon/houdini/client/ayon_houdini/version.py index af2c4557db..66f3ac59e7 100644 --- a/server_addon/houdini/client/ayon_houdini/version.py +++ b/server_addon/houdini/client/ayon_houdini/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'houdini' version.""" -__version__ = "0.3.3" +__version__ = "0.3.4" diff --git a/server_addon/houdini/package.py b/server_addon/houdini/package.py index da13bee9c7..0c1b1fcf9b 100644 --- a/server_addon/houdini/package.py +++ b/server_addon/houdini/package.py @@ -1,6 +1,6 @@ name = "houdini" title = "Houdini" -version = "0.3.3" +version = "0.3.4" client_dir = "ayon_houdini" From cb80c8fa1a59da05cb131511f4575d7e22560830 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 24 Jun 2024 15:44:19 +0200 Subject: [PATCH 117/129] Bump addon version --- server_addon/houdini/client/ayon_houdini/version.py | 2 +- server_addon/houdini/package.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/version.py b/server_addon/houdini/client/ayon_houdini/version.py index 66f3ac59e7..b6b644f30e 100644 --- a/server_addon/houdini/client/ayon_houdini/version.py +++ b/server_addon/houdini/client/ayon_houdini/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'houdini' version.""" -__version__ = "0.3.4" +__version__ = "0.3.5" diff --git a/server_addon/houdini/package.py b/server_addon/houdini/package.py index 0c1b1fcf9b..5bdde038c2 100644 --- a/server_addon/houdini/package.py +++ b/server_addon/houdini/package.py @@ -1,6 +1,6 @@ name = "houdini" title = "Houdini" -version = "0.3.4" +version = "0.3.5" client_dir = "ayon_houdini" From 34bdab72ee28d27567fa81c0b21cb1eb58fedf95 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 24 Jun 2024 17:29:16 +0200 Subject: [PATCH 118/129] Don't use six to define ABC class --- client/ayon_core/tools/common_models/__init__.py | 4 ++++ client/ayon_core/tools/common_models/projects.py | 6 ++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/client/ayon_core/tools/common_models/__init__.py b/client/ayon_core/tools/common_models/__init__.py index f09edfeab2..40394c4732 100644 --- a/client/ayon_core/tools/common_models/__init__.py +++ b/client/ayon_core/tools/common_models/__init__.py @@ -2,6 +2,8 @@ from .cache import CacheItem, NestedCacheItem from .projects import ( + StatusItem, + StatusStates, ProjectItem, ProjectsModel, PROJECTS_MODEL_SENDER, @@ -21,6 +23,8 @@ __all__ = ( "CacheItem", "NestedCacheItem", + "StatusItem", + "StatusStates", "ProjectItem", "ProjectsModel", "PROJECTS_MODEL_SENDER", diff --git a/client/ayon_core/tools/common_models/projects.py b/client/ayon_core/tools/common_models/projects.py index 4e8925388d..1c455a9619 100644 --- a/client/ayon_core/tools/common_models/projects.py +++ b/client/ayon_core/tools/common_models/projects.py @@ -1,8 +1,7 @@ import contextlib -from abc import ABCMeta, abstractmethod +from abc import ABC, abstractmethod import ayon_api -import six from ayon_core.style import get_default_entity_icon_color from ayon_core.lib import CacheItem, NestedCacheItem @@ -10,8 +9,7 @@ from ayon_core.lib import CacheItem, NestedCacheItem PROJECTS_MODEL_SENDER = "projects.model" -@six.add_metaclass(ABCMeta) -class AbstractHierarchyController: +class AbstractHierarchyController(ABC): @abstractmethod def emit_event(self, topic, data, source): pass From 386a627abe9e71783d67929da23f076503940eb7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 24 Jun 2024 17:31:58 +0200 Subject: [PATCH 119/129] Added helper classes and hints for state --- .../ayon_core/tools/common_models/projects.py | 33 ++++++++++++++----- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/client/ayon_core/tools/common_models/projects.py b/client/ayon_core/tools/common_models/projects.py index 1c455a9619..17599c27f6 100644 --- a/client/ayon_core/tools/common_models/projects.py +++ b/client/ayon_core/tools/common_models/projects.py @@ -1,5 +1,6 @@ import contextlib from abc import ABC, abstractmethod +from typing import Literal, Dict, Any import ayon_api @@ -7,6 +8,14 @@ from ayon_core.style import get_default_entity_icon_color from ayon_core.lib import CacheItem, NestedCacheItem PROJECTS_MODEL_SENDER = "projects.model" +StatusStatesType = Literal["not_started", "in_progress", "done", "blocked"] + + +class StatusStates: + not_started = "not_started" + in_progress = "in_progress" + done = "done" + blocked = "blocked" class AbstractHierarchyController(ABC): @@ -23,18 +32,24 @@ class StatusItem: color (str): Status color in hex ("#434a56"). short (str): Short status name ("NRD"). icon (str): Icon name in MaterialIcons ("fiber_new"). - state (Literal["not_started", "in_progress", "done", "blocked"]): - Status state. + state (StatusStatesType): Status state. """ - def __init__(self, name, color, short, icon, state): - self.name = name - self.color = color - self.short = short - self.icon = icon - self.state = state + def __init__( + self, + name: str, + color: str, + short: str, + icon: str, + state: StatusStatesType + ): + self.name: str = name + self.color: str = color + self.short: str = short + self.icon: str = icon + self.state: StatusStatesType = state - def to_data(self): + def to_data(self) -> Dict[str, Any]: return { "name": self.name, "color": self.color, From 14fc4ae187eb48ab169bc94d8b66b3a475d2028f Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 24 Jun 2024 17:32:21 +0200 Subject: [PATCH 120/129] added 'is_last_approved' attribute to 'VersionItem' --- .../tools/sceneinventory/models/containers.py | 46 +++++++++++++++---- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/models/containers.py b/client/ayon_core/tools/sceneinventory/models/containers.py index 95c5322343..c3881ea40d 100644 --- a/client/ayon_core/tools/sceneinventory/models/containers.py +++ b/client/ayon_core/tools/sceneinventory/models/containers.py @@ -3,7 +3,9 @@ import collections import ayon_api from ayon_api.graphql import GraphQlQuery + from ayon_core.host import ILoadHost +from ayon_core.tools.common_models.projects import StatusStates # --- Implementation that should be in ayon-python-api --- @@ -149,26 +151,35 @@ class RepresentationInfo: class VersionItem: - def __init__(self, version_id, product_id, version, status, is_latest): - self.version = version - self.version_id = version_id - self.product_id = product_id - self.version = version - self.status = status - self.is_latest = is_latest + def __init__( + self, + version_id: str, + product_id: str, + version: int, + status: str, + is_latest: bool, + is_last_approved: bool, + ): + self.version_id: str = version_id + self.product_id: str = product_id + self.version: int = version + self.status: str = status + self.is_latest: bool = is_latest + self.is_last_approved: bool = is_last_approved @property def is_hero(self): return self.version < 0 @classmethod - def from_entity(cls, version_entity, is_latest): + def from_entity(cls, version_entity, is_latest, is_last_approved): return cls( version_id=version_entity["id"], product_id=version_entity["productId"], version=version_entity["version"], status=version_entity["status"], is_latest=is_latest, + is_last_approved=is_last_approved, ) @@ -275,6 +286,11 @@ class ContainersModel: if product_id not in self._version_items_by_product_id } if missing_ids: + status_items_by_name = { + status_item.name: status_item + for status_item in self._controller.get_project_status_items() + } + def version_sorted(entity): return entity["version"] @@ -300,9 +316,21 @@ class ContainersModel: version_entities_by_product_id.items() ): last_version = abs(version_entities[-1]["version"]) + last_approved_id = None + for version_entity in version_entities: + status_item = status_items_by_name.get( + version_entity["status"] + ) + if status_item is None: + continue + if status_item.state == StatusStates.done: + last_approved_id = version_entity["id"] + version_items_by_id = { entity["id"]: VersionItem.from_entity( - entity, abs(entity["version"]) == last_version + entity, + abs(entity["version"]) == last_version, + entity["id"] == last_approved_id ) for entity in version_entities } From db3f5c60c68e47b210dcd644ea69f754c554b8ee Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 24 Jun 2024 17:38:18 +0200 Subject: [PATCH 121/129] implemented logic to update to latest approved --- client/ayon_core/tools/sceneinventory/view.py | 84 ++++++++++++++++++- 1 file changed, 82 insertions(+), 2 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index c8cc3299a2..017a9597f1 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -233,10 +233,18 @@ class SceneInventoryView(QtWidgets.QTreeView): has_outdated = False has_loaded_hero_versions = False has_available_hero_version = False - for version_items_by_id in version_items_by_product_id.values(): + has_outdated_approved = False + last_version_by_product_id = {} + for product_id, version_items_by_id in ( + version_items_by_product_id.items() + ): + _has_outdated_approved = False + _last_approved_version_item = None for version_item in version_items_by_id.values(): if version_item.is_hero: has_available_hero_version = True + if version_item.is_last_approved: + _last_approved_version_item = version_item if version_item.version_id not in version_ids: continue @@ -245,6 +253,17 @@ class SceneInventoryView(QtWidgets.QTreeView): elif not version_item.is_latest: has_outdated = True + elif not version_item.is_last_approved: + _has_outdated_approved = True + + if ( + _has_outdated_approved + and _last_approved_version_item is not None + ): + last_version_by_product_id[product_id] = ( + _last_approved_version_item + ) + has_outdated_approved = True switch_to_versioned = None if has_loaded_hero_versions: @@ -261,6 +280,41 @@ class SceneInventoryView(QtWidgets.QTreeView): lambda: self._on_switch_to_versioned(item_ids) ) + update_to_last_approved_action = None + if has_outdated_approved: + approved_version_by_item_id = {} + for container_item in container_items_by_id.values(): + repre_id = container_item.representation_id + repre_info = repre_info_by_id.get(repre_id) + if not repre_info or not repre_info.is_valid: + continue + version_item = last_version_by_product_id.get( + repre_info.product_id + ) + if ( + version_item is None + or version_item.id == repre_info.version_id + ): + continue + approved_version_by_item_id[container_item.item_id] = ( + version_item.version + ) + + update_icon = qtawesome.icon( + "fa.angle-double-up", + color="#00f0b4" + ) + update_to_last_approved_action = QtWidgets.QAction( + update_icon, + "Update to last approved", + menu + ) + update_to_last_approved_action.triggered.connect( + lambda: self._update_containers_to_approved_versions( + approved_version_by_item_id + ) + ) + update_to_latest_action = None if has_outdated or has_loaded_hero_versions: update_icon = qtawesome.icon( @@ -299,7 +353,9 @@ class SceneInventoryView(QtWidgets.QTreeView): # set version set_version_action = None if active_repre_id is not None: - set_version_icon = qtawesome.icon("fa.hashtag", color=DEFAULT_COLOR) + set_version_icon = qtawesome.icon( + "fa.hashtag", color=DEFAULT_COLOR + ) set_version_action = QtWidgets.QAction( set_version_icon, "Set version", @@ -323,6 +379,9 @@ class SceneInventoryView(QtWidgets.QTreeView): if switch_to_versioned: menu.addAction(switch_to_versioned) + if update_to_last_approved_action: + menu.addAction(update_to_last_approved_action) + if update_to_latest_action: menu.addAction(update_to_latest_action) @@ -970,3 +1029,24 @@ class SceneInventoryView(QtWidgets.QTreeView): """ versions = [version for _ in range(len(item_ids))] self._update_containers(item_ids, versions) + + def _update_containers_to_approved_versions( + self, approved_version_by_item_id + ): + """Helper to update items to given version (or version per item) + + If at least one item is specified this will always try to refresh + the inventory even if errors occurred on any of the items. + + Arguments: + approved_version_by_item_id (Dict[str, int]): Version to set by + item id. + + """ + versions = [] + item_ids = [] + for item_id, version in approved_version_by_item_id.items(): + item_ids.append(item_id) + versions.append(version) + + self._update_containers(item_ids, versions) From b710af2662b2df77f672d4d8c84261433caeab5b Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 24 Jun 2024 18:00:54 +0200 Subject: [PATCH 122/129] removed 'Literal' --- client/ayon_core/tools/common_models/projects.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/client/ayon_core/tools/common_models/projects.py b/client/ayon_core/tools/common_models/projects.py index 17599c27f6..7ec941e6bd 100644 --- a/client/ayon_core/tools/common_models/projects.py +++ b/client/ayon_core/tools/common_models/projects.py @@ -1,6 +1,6 @@ import contextlib from abc import ABC, abstractmethod -from typing import Literal, Dict, Any +from typing import Dict, Any import ayon_api @@ -8,7 +8,6 @@ from ayon_core.style import get_default_entity_icon_color from ayon_core.lib import CacheItem, NestedCacheItem PROJECTS_MODEL_SENDER = "projects.model" -StatusStatesType = Literal["not_started", "in_progress", "done", "blocked"] class StatusStates: @@ -32,7 +31,7 @@ class StatusItem: color (str): Status color in hex ("#434a56"). short (str): Short status name ("NRD"). icon (str): Icon name in MaterialIcons ("fiber_new"). - state (StatusStatesType): Status state. + state (str): Status state. """ def __init__( @@ -41,13 +40,13 @@ class StatusItem: color: str, short: str, icon: str, - state: StatusStatesType + state: str ): self.name: str = name self.color: str = color self.short: str = short self.icon: str = icon - self.state: StatusStatesType = state + self.state: str = state def to_data(self) -> Dict[str, Any]: return { From 47fafbdc05f726d2d11ad2971ae393acbc28f6f7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 24 Jun 2024 18:15:04 +0200 Subject: [PATCH 123/129] fix logic of action discovery --- client/ayon_core/tools/sceneinventory/view.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/client/ayon_core/tools/sceneinventory/view.py b/client/ayon_core/tools/sceneinventory/view.py index 017a9597f1..22ba15fda8 100644 --- a/client/ayon_core/tools/sceneinventory/view.py +++ b/client/ayon_core/tools/sceneinventory/view.py @@ -243,18 +243,18 @@ class SceneInventoryView(QtWidgets.QTreeView): for version_item in version_items_by_id.values(): if version_item.is_hero: has_available_hero_version = True - if version_item.is_last_approved: + + elif version_item.is_last_approved: _last_approved_version_item = version_item + _has_outdated_approved = True if version_item.version_id not in version_ids: continue + if version_item.is_hero: has_loaded_hero_versions = True - elif not version_item.is_latest: has_outdated = True - elif not version_item.is_last_approved: - _has_outdated_approved = True if ( _has_outdated_approved @@ -281,8 +281,8 @@ class SceneInventoryView(QtWidgets.QTreeView): ) update_to_last_approved_action = None + approved_version_by_item_id = {} if has_outdated_approved: - approved_version_by_item_id = {} for container_item in container_items_by_id.values(): repre_id = container_item.representation_id repre_info = repre_info_by_id.get(repre_id) @@ -293,13 +293,14 @@ class SceneInventoryView(QtWidgets.QTreeView): ) if ( version_item is None - or version_item.id == repre_info.version_id + or version_item.version_id == repre_info.version_id ): continue approved_version_by_item_id[container_item.item_id] = ( version_item.version ) + if approved_version_by_item_id: update_icon = qtawesome.icon( "fa.angle-double-up", color="#00f0b4" From 403442b281b0645952fc3b9f6513e8c8c27ba88c Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 24 Jun 2024 18:20:40 +0200 Subject: [PATCH 124/129] fix color of loaded version --- client/ayon_core/tools/sceneinventory/model.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/ayon_core/tools/sceneinventory/model.py b/client/ayon_core/tools/sceneinventory/model.py index 3e0c361535..335df87b95 100644 --- a/client/ayon_core/tools/sceneinventory/model.py +++ b/client/ayon_core/tools/sceneinventory/model.py @@ -217,7 +217,9 @@ class InventoryModel(QtGui.QStandardItemModel): version_label = format_version(version_item.version) is_hero = version_item.version < 0 is_latest = version_item.is_latest - if not is_latest: + # TODO maybe use different colors for last approved and last + # version? Or don't care about color at all? + if not is_latest and not version_item.is_last_approved: version_color = self.OUTDATED_COLOR status_name = version_item.status From 4583a447776ed1af544255b024656aafaa8c9d43 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 24 Jun 2024 22:56:33 +0200 Subject: [PATCH 125/129] Remove unused "remote publish" workflow for Houdini This likely was once a 'straight' copy from `colorbleed` codebase back in the Avalon-era. --- .../houdini/client/ayon_houdini/api/lib.py | 83 ------------------- .../plugins/publish/collect_remote_publish.py | 29 ------- .../publish/validate_remote_publish.py | 50 ----------- .../validate_remote_publish_enabled.py | 41 --------- 4 files changed, 203 deletions(-) delete mode 100644 server_addon/houdini/client/ayon_houdini/plugins/publish/collect_remote_publish.py delete mode 100644 server_addon/houdini/client/ayon_houdini/plugins/publish/validate_remote_publish.py delete mode 100644 server_addon/houdini/client/ayon_houdini/plugins/publish/validate_remote_publish_enabled.py diff --git a/server_addon/houdini/client/ayon_houdini/api/lib.py b/server_addon/houdini/client/ayon_houdini/api/lib.py index 671265fae9..d23edcf1df 100644 --- a/server_addon/houdini/client/ayon_houdini/api/lib.py +++ b/server_addon/houdini/client/ayon_houdini/api/lib.py @@ -148,89 +148,6 @@ def validate_fps(): return True -def create_remote_publish_node(force=True): - """Function to create a remote publish node in /out - - This is a hacked "Shell" node that does *nothing* except for triggering - `colorbleed.lib.publish_remote()` as pre-render script. - - All default attributes of the Shell node are hidden to the Artist to - avoid confusion. - - Additionally some custom attributes are added that can be collected - by a Collector to set specific settings for the publish, e.g. whether - to separate the jobs per instance or process in one single job. - - """ - - cmd = "import colorbleed.lib; colorbleed.lib.publish_remote()" - - existing = hou.node("/out/REMOTE_PUBLISH") - if existing: - if force: - log.warning("Removing existing '/out/REMOTE_PUBLISH' node..") - existing.destroy() - else: - raise RuntimeError("Node already exists /out/REMOTE_PUBLISH. " - "Please remove manually or set `force` to " - "True.") - - # Create the shell node - out = hou.node("/out") - node = out.createNode("shell", node_name="REMOTE_PUBLISH") - node.moveToGoodPosition() - - # Set color make it stand out (avalon/pyblish color) - node.setColor(hou.Color(0.439, 0.709, 0.933)) - - # Set the pre-render script - node.setParms({ - "prerender": cmd, - "lprerender": "python" # command language - }) - - # Lock the attributes to ensure artists won't easily mess things up. - node.parm("prerender").lock(True) - node.parm("lprerender").lock(True) - - # Lock up the actual shell command - command_parm = node.parm("command") - command_parm.set("") - command_parm.lock(True) - shellexec_parm = node.parm("shellexec") - shellexec_parm.set(False) - shellexec_parm.lock(True) - - # Get the node's parm template group so we can customize it - template = node.parmTemplateGroup() - - # Hide default tabs - template.hideFolder("Shell", True) - template.hideFolder("Scripts", True) - - # Hide default settings - template.hide("execute", True) - template.hide("renderdialog", True) - template.hide("trange", True) - template.hide("f", True) - template.hide("take", True) - - # Add custom settings to this node. - parm_folder = hou.FolderParmTemplate("folder", "Submission Settings") - - # Separate Jobs per Instance - parm = hou.ToggleParmTemplate(name="separateJobPerInstance", - label="Separate Job per Instance", - default_value=False) - parm_folder.addParmTemplate(parm) - - # Add our custom Submission Settings folder - template.append(parm_folder) - - # Apply template back to the node - node.setParmTemplateGroup(template) - - def render_rop(ropnode): """Render ROP node utility for Publishing. diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_remote_publish.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_remote_publish.py deleted file mode 100644 index e695b57518..0000000000 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/collect_remote_publish.py +++ /dev/null @@ -1,29 +0,0 @@ -import hou -import pyblish.api - -from ayon_core.pipeline.publish import RepairAction -from ayon_houdini.api import lib, plugin - - -class CollectRemotePublishSettings(plugin.HoudiniContextPlugin): - """Collect custom settings of the Remote Publish node.""" - - order = pyblish.api.CollectorOrder - families = ["*"] - targets = ["deadline"] - label = "Remote Publish Submission Settings" - actions = [RepairAction] - - def process(self, context): - - node = hou.node("/out/REMOTE_PUBLISH") - if not node: - return - - attributes = lib.read(node) - - # Debug the settings we have collected - for key, value in sorted(attributes.items()): - self.log.debug("Collected %s: %s" % (key, value)) - - context.data.update(attributes) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_remote_publish.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_remote_publish.py deleted file mode 100644 index 08597c0a6f..0000000000 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_remote_publish.py +++ /dev/null @@ -1,50 +0,0 @@ -# -*-coding: utf-8 -*- -import hou - -import pyblish.api -from ayon_core.pipeline.publish import RepairContextAction -from ayon_core.pipeline import PublishValidationError - -from ayon_houdini.api import lib, plugin - - -class ValidateRemotePublishOutNode(plugin.HoudiniContextPlugin): - """Validate the remote publish out node exists for Deadline to trigger.""" - - order = pyblish.api.ValidatorOrder - 0.4 - families = ["*"] - targets = ["deadline"] - label = "Remote Publish ROP node" - actions = [RepairContextAction] - - def process(self, context): - - cmd = "import colorbleed.lib; colorbleed.lib.publish_remote()" - - node = hou.node("/out/REMOTE_PUBLISH") - if not node: - raise RuntimeError("Missing REMOTE_PUBLISH node.") - - # We ensure it's a shell node and that it has the pre-render script - # set correctly. Plus the shell script it will trigger should be - # completely empty (doing nothing) - if node.type().name() != "shell": - self.raise_error("Must be shell ROP node") - if node.parm("command").eval() != "": - self.raise_error("Must have no command") - if node.parm("shellexec").eval(): - self.raise_error("Must not execute in shell") - if node.parm("prerender").eval() != cmd: - self.raise_error("REMOTE_PUBLISH node does not have " - "correct prerender script.") - if node.parm("lprerender").eval() != "python": - self.raise_error("REMOTE_PUBLISH node prerender script " - "type not set to 'python'") - - @classmethod - def repair(cls, context): - """(Re)create the node if it fails to pass validation.""" - lib.create_remote_publish_node(force=True) - - def raise_error(self, message): - raise PublishValidationError(message) diff --git a/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_remote_publish_enabled.py b/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_remote_publish_enabled.py deleted file mode 100644 index dc5666609f..0000000000 --- a/server_addon/houdini/client/ayon_houdini/plugins/publish/validate_remote_publish_enabled.py +++ /dev/null @@ -1,41 +0,0 @@ -# -*- coding: utf-8 -*- -import hou - -import pyblish.api -from ayon_core.pipeline.publish import RepairContextAction -from ayon_core.pipeline import PublishValidationError - -from ayon_houdini.api import plugin - - -class ValidateRemotePublishEnabled(plugin.HoudiniContextPlugin): - """Validate the remote publish node is *not* bypassed.""" - - order = pyblish.api.ValidatorOrder - 0.39 - families = ["*"] - targets = ["deadline"] - label = "Remote Publish ROP enabled" - actions = [RepairContextAction] - - def process(self, context): - - node = hou.node("/out/REMOTE_PUBLISH") - if not node: - raise PublishValidationError( - "Missing REMOTE_PUBLISH node.", title=self.label) - - if node.isBypassed(): - raise PublishValidationError( - "REMOTE_PUBLISH must not be bypassed.", title=self.label) - - @classmethod - def repair(cls, context): - """(Re)create the node if it fails to pass validation.""" - - node = hou.node("/out/REMOTE_PUBLISH") - if not node: - raise PublishValidationError( - "Missing REMOTE_PUBLISH node.", title=cls.label) - - cls.log.info("Disabling bypass on /out/REMOTE_PUBLISH") - node.bypass(False) From ee0d3c91e2f8456bb34ab82258b4893af48bef54 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 25 Jun 2024 11:45:03 +0200 Subject: [PATCH 126/129] Bump Houdini addon version --- server_addon/houdini/client/ayon_houdini/version.py | 2 +- server_addon/houdini/package.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/version.py b/server_addon/houdini/client/ayon_houdini/version.py index b6b644f30e..5c32b4860e 100644 --- a/server_addon/houdini/client/ayon_houdini/version.py +++ b/server_addon/houdini/client/ayon_houdini/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'houdini' version.""" -__version__ = "0.3.5" +__version__ = "0.3.6" diff --git a/server_addon/houdini/package.py b/server_addon/houdini/package.py index 5bdde038c2..fb345dab51 100644 --- a/server_addon/houdini/package.py +++ b/server_addon/houdini/package.py @@ -1,6 +1,6 @@ name = "houdini" title = "Houdini" -version = "0.3.5" +version = "0.3.6" client_dir = "ayon_houdini" From 32f6d4941fb8106f0fb71cf3cb9f113d893198e1 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 25 Jun 2024 15:06:52 +0200 Subject: [PATCH 127/129] Bump houdini addon version --- server_addon/houdini/client/ayon_houdini/version.py | 2 +- server_addon/houdini/package.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server_addon/houdini/client/ayon_houdini/version.py b/server_addon/houdini/client/ayon_houdini/version.py index 5c32b4860e..3dbbb4c23e 100644 --- a/server_addon/houdini/client/ayon_houdini/version.py +++ b/server_addon/houdini/client/ayon_houdini/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'houdini' version.""" -__version__ = "0.3.6" +__version__ = "0.3.7" diff --git a/server_addon/houdini/package.py b/server_addon/houdini/package.py index fb345dab51..c01cc6044d 100644 --- a/server_addon/houdini/package.py +++ b/server_addon/houdini/package.py @@ -1,6 +1,6 @@ name = "houdini" title = "Houdini" -version = "0.3.6" +version = "0.3.7" client_dir = "ayon_houdini" From b6a4047efae94af8567a20407e4a8dad0b4704cf Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 25 Jun 2024 21:50:40 +0800 Subject: [PATCH 128/129] supports to export fbx in model family in Maya --- .../plugins/publish/collect_fbx_model.py | 29 +++++++++++++++++++ server_addon/maya/client/ayon_maya/version.py | 2 +- server_addon/maya/package.py | 2 +- .../maya/server/settings/publishers.py | 12 ++++++++ 4 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 server_addon/maya/client/ayon_maya/plugins/publish/collect_fbx_model.py diff --git a/server_addon/maya/client/ayon_maya/plugins/publish/collect_fbx_model.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_fbx_model.py new file mode 100644 index 0000000000..a5da0419a9 --- /dev/null +++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_fbx_model.py @@ -0,0 +1,29 @@ +import pyblish.api +from ayon_core.pipeline import OptionalPyblishPluginMixin +from ayon_maya.api import plugin + + + +class CollectFbxModel(plugin.MayaInstancePlugin, + OptionalPyblishPluginMixin): + """Collect Camera for FBX export.""" + + order = pyblish.api.CollectorOrder + 0.2 + label = "Collect Model for FBX export" + families = ["model"] + optional = True + + def process(self, instance): + if not self.is_active(instance.data): + return + + if not instance.data.get("families"): + instance.data["families"] = [] + + if "fbx" not in instance.data["families"]: + instance.data["families"].append("fbx") + + for key in { + "bakeComplexAnimation", "bakeResampleAnimation", + "skins", "constraints", "lights"}: + instance.data[key] = False diff --git a/server_addon/maya/client/ayon_maya/version.py b/server_addon/maya/client/ayon_maya/version.py index 1f53dfa492..80af287e97 100644 --- a/server_addon/maya/client/ayon_maya/version.py +++ b/server_addon/maya/client/ayon_maya/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring AYON addon 'maya' version.""" -__version__ = "0.2.7" +__version__ = "0.2.8" diff --git a/server_addon/maya/package.py b/server_addon/maya/package.py index 47aa8a4c0d..b2d0622493 100644 --- a/server_addon/maya/package.py +++ b/server_addon/maya/package.py @@ -1,6 +1,6 @@ name = "maya" title = "Maya" -version = "0.2.7" +version = "0.2.8" client_dir = "ayon_maya" ayon_required_addons = { diff --git a/server_addon/maya/server/settings/publishers.py b/server_addon/maya/server/settings/publishers.py index 9c552e17fa..4834ed839a 100644 --- a/server_addon/maya/server/settings/publishers.py +++ b/server_addon/maya/server/settings/publishers.py @@ -184,6 +184,11 @@ class CollectFbxCameraModel(BaseSettingsModel): enabled: bool = SettingsField(title="CollectFbxCamera") + +class CollectFbxModelModel(BaseSettingsModel): + enabled: bool = SettingsField(title="CollectFbxModel") + + class CollectGLTFModel(BaseSettingsModel): enabled: bool = SettingsField(title="CollectGLTF") @@ -625,6 +630,10 @@ class PublishersModel(BaseSettingsModel): default_factory=CollectFbxCameraModel, title="Collect Camera for FBX export", ) + CollectFbxModel: CollectFbxModelModel = SettingsField( + default_factory=CollectFbxModelModel, + title="Collect Model for FBX export", + ) CollectGLTF: CollectGLTFModel = SettingsField( default_factory=CollectGLTFModel, title="Collect Assets for GLB/GLTF export" @@ -1047,6 +1056,9 @@ DEFAULT_PUBLISH_SETTINGS = { "CollectFbxCamera": { "enabled": False }, + "CollectFbxModel": { + "enabled": False + }, "CollectGLTF": { "enabled": False }, From f866949669f302a837dc7f09ae23f96ee393d545 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 25 Jun 2024 22:37:58 +0800 Subject: [PATCH 129/129] add optional and enable settings for collect fbx model in ayon settings --- .../ayon_maya/plugins/publish/collect_fbx_model.py | 2 +- server_addon/maya/server/settings/publishers.py | 13 +++++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/server_addon/maya/client/ayon_maya/plugins/publish/collect_fbx_model.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_fbx_model.py index a5da0419a9..f3902a2868 100644 --- a/server_addon/maya/client/ayon_maya/plugins/publish/collect_fbx_model.py +++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_fbx_model.py @@ -9,7 +9,7 @@ class CollectFbxModel(plugin.MayaInstancePlugin, """Collect Camera for FBX export.""" order = pyblish.api.CollectorOrder + 0.2 - label = "Collect Model for FBX export" + label = "Collect Fbx Model" families = ["model"] optional = True diff --git a/server_addon/maya/server/settings/publishers.py b/server_addon/maya/server/settings/publishers.py index 4834ed839a..6a127cc998 100644 --- a/server_addon/maya/server/settings/publishers.py +++ b/server_addon/maya/server/settings/publishers.py @@ -184,11 +184,6 @@ class CollectFbxCameraModel(BaseSettingsModel): enabled: bool = SettingsField(title="CollectFbxCamera") - -class CollectFbxModelModel(BaseSettingsModel): - enabled: bool = SettingsField(title="CollectFbxModel") - - class CollectGLTFModel(BaseSettingsModel): enabled: bool = SettingsField(title="CollectGLTF") @@ -630,8 +625,8 @@ class PublishersModel(BaseSettingsModel): default_factory=CollectFbxCameraModel, title="Collect Camera for FBX export", ) - CollectFbxModel: CollectFbxModelModel = SettingsField( - default_factory=CollectFbxModelModel, + CollectFbxModel: BasicValidateModel = SettingsField( + default_factory=BasicValidateModel, title="Collect Model for FBX export", ) CollectGLTF: CollectGLTFModel = SettingsField( @@ -1057,7 +1052,9 @@ DEFAULT_PUBLISH_SETTINGS = { "enabled": False }, "CollectFbxModel": { - "enabled": False + "enabled": False, + "optional": True, + "active": True }, "CollectGLTF": { "enabled": False