diff --git a/client/ayon_houdini/plugins/create/create_hda.py b/client/ayon_houdini/plugins/create/create_hda.py
index 7613735f7b..c7ee04b0e6 100644
--- a/client/ayon_houdini/plugins/create/create_hda.py
+++ b/client/ayon_houdini/plugins/create/create_hda.py
@@ -3,14 +3,8 @@
import hou
import ayon_api
-from ayon_core.pipeline import (
- CreatorError,
- get_current_project_name
-)
-from ayon_core.lib import (
- get_ayon_username,
- BoolDef
-)
+from ayon_core.pipeline import CreatorError, get_current_project_name
+from ayon_core.lib import get_ayon_username, BoolDef
from ayon_houdini.api import plugin
@@ -31,15 +25,16 @@ def get_tool_submenus(hda_def):
"""
import xml.etree.ElementTree as ET
- if hda_def.hasSection('Tools.shelf'):
+
+ if hda_def.hasSection("Tools.shelf"):
sections = hda_def.sections()
- ts_section = sections['Tools.shelf'].contents()
+ ts_section = sections["Tools.shelf"].contents()
try:
root = ET.fromstring(ts_section)
except ET.ParseError:
return None
tool = root[0]
- submenus = tool.findall('toolSubmenu')
+ submenus = tool.findall("toolSubmenu")
if submenus:
tool_submenus = []
for submenu in submenus:
@@ -57,8 +52,7 @@ def get_tool_submenus(hda_def):
return None
-def set_tool_submenu(hda_def,
- new_submenu='Digital Assets'):
+def set_tool_submenu(hda_def, new_submenu="Digital Assets"):
"""Sets the tab menu entry for a node.
Arguments:
@@ -68,34 +62,36 @@ def set_tool_submenu(hda_def,
"""
context_dict = {
- 'Shop': 'SHOP',
- 'Cop2': 'COP2',
- 'Object': 'OBJ',
- 'Chop': 'CHOP',
- 'Sop': 'SOP',
- 'Vop': 'VOP',
- 'VopNet': 'VOPNET',
- 'Driver': 'ROP',
- 'TOP': 'TOP',
- 'Top': 'TOP',
- 'Lop': 'LOP',
- 'Dop': 'DOP'}
+ "Shop": "SHOP",
+ "Cop2": "COP2",
+ "Object": "OBJ",
+ "Chop": "CHOP",
+ "Sop": "SOP",
+ "Vop": "VOP",
+ "VopNet": "VOPNET",
+ "Driver": "ROP",
+ "TOP": "TOP",
+ "Top": "TOP",
+ "Lop": "LOP",
+ "Dop": "DOP",
+ }
utils_dict = {
- 'Shop': 'shoptoolutils',
- 'Cop2': 'cop2toolutils',
- 'Object': 'objecttoolutils',
- 'Chop': 'choptoolutils',
- 'Sop': 'soptoolutils',
- 'Vop': 'voptoolutils',
- 'VopNet': 'vopnettoolutils',
- 'Driver': 'drivertoolutils',
- 'TOP': 'toptoolutils',
- 'Top': 'toptoolutils',
- 'Lop': 'loptoolutils',
- 'Dop': 'doptoolutils'}
-
- if hda_def.hasSection('Tools.shelf'):
+ "Shop": "shoptoolutils",
+ "Cop2": "cop2toolutils",
+ "Object": "objecttoolutils",
+ "Chop": "choptoolutils",
+ "Sop": "soptoolutils",
+ "Vop": "voptoolutils",
+ "VopNet": "vopnettoolutils",
+ "Driver": "drivertoolutils",
+ "TOP": "toptoolutils",
+ "Top": "toptoolutils",
+ "Lop": "loptoolutils",
+ "Dop": "doptoolutils",
+ }
+
+ if hda_def.hasSection("Tools.shelf"):
old_submenu = get_tool_submenus(hda_def)[0]
else:
# Add default tools shelf section
@@ -118,26 +114,29 @@ def set_tool_submenu(hda_def,
"""
-
+
nodetype_category_name = hda_def.nodeType().category().name()
context = context_dict[nodetype_category_name]
util = utils_dict[nodetype_category_name]
content = content.replace(
"SOP",
- f"{context}")
- content = content.replace('soptoolutils', util)
- hda_def.addSection('Tools.shelf', content)
- old_submenu = 'Digital Assets'
+ f"{context}",
+ )
+ content = content.replace("soptoolutils", util)
+ hda_def.addSection("Tools.shelf", content)
+ old_submenu = "Digital Assets"
# Replace submenu
tools = hda_def.sections()["Tools.shelf"]
content = tools.contents()
content = content.replace(
f"{old_submenu}",
- f"{new_submenu}"
+ f"{new_submenu}",
)
- hda_def.addSection('Tools.shelf', content)
+ hda_def.addSection("Tools.shelf", content)
+
+
# endregion
@@ -162,18 +161,12 @@ def _check_existing(self, folder_path, product_name):
project_name, folder_ids={folder_entity["id"]}, fields={"name"}
)
existing_product_names_low = {
- product_entity["name"].lower()
- for product_entity in product_entities
+ product_entity["name"].lower() for product_entity in product_entities
}
return product_name.lower() in existing_product_names_low
def create_instance_node(
- self,
- folder_path,
- node_name,
- parent,
- node_type="geometry",
- pre_create_data=None
+ self, folder_path, node_name, parent, node_type="geometry", pre_create_data=None
):
if pre_create_data is None:
pre_create_data = {}
@@ -187,8 +180,8 @@ def create_instance_node(
else:
parent_node = self.selected_nodes[0].parent()
subnet = parent_node.collapseIntoSubnet(
- self.selected_nodes,
- subnet_name="{}_subnet".format(node_name))
+ self.selected_nodes, subnet_name="{}_subnet".format(node_name)
+ )
subnet.moveToGoodPosition()
to_hda = subnet
else:
@@ -201,34 +194,66 @@ def create_instance_node(
parent_node = pane.pwd()
to_hda = parent_node.createNode(
- "subnet", node_name="{}_subnet".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.
+ # If the node's type lacks a definition, it isn't an user-created HDA.
+ # We test whether an HDA can be generated from this node.
if not to_hda.canCreateDigitalAsset():
- raise CreatorError(
- "cannot create hda from node {}".format(to_hda))
+ raise CreatorError("cannot create hda from node {}".format(to_hda))
# Pick a unique type name for HDA product per folder path per project.
- type_name = (
- "{project_name}{folder_path}_{node_name}".format(
- project_name=get_current_project_name(),
- folder_path=folder_path.replace("/","_"),
- node_name=node_name
- )
+ type_name = "{project_name}{folder_path}_{node_name}".format(
+ project_name=get_current_project_name(),
+ folder_path=folder_path.replace("/", "_"),
+ node_name=node_name,
)
+ source_parm_template_group = to_hda.parmTemplateGroup()
+
+ # Identify all distinct output nodes within the initial layer of the chosen subnet
+ # to determine the total number of outputs required for the HDA.
+ output_indexes = set()
+ for node in [
+ node for node in to_hda.children() if node.type().name() == "output"
+ ]:
+ output_index = node.parm("outputidx").eval()
+ output_indexes.add(output_index)
+
+ # Find all inputs within the chosen subnet connected to anything to determine
+ # the input count required for the HDA.
+ subnet_inputs_with_connections = set()
+ for subnet_input in to_hda.indirectInputs():
+ if subnet_input.outputs():
+ subnet_inputs_with_connections.add(subnet_input)
+
hda_node = to_hda.createDigitalAsset(
name=type_name,
description=node_name,
hda_file_name="$HIP/{}.hda".format(node_name),
- ignore_external_references=True
+ ignore_external_references=True,
+ min_num_inputs=0,
+ max_num_inputs=max(len(subnet_inputs_with_connections), 1),
)
+
+ # add the parm template that we got from the source Subnet into the hda type definition
+ hda_def = hda_node.type().definition()
+ hda_def.setMaxNumOutputs(max(len(output_indexes), 1))
+
+ hda_parm_template_group = hda_def.parmTemplateGroup()
+
+ for parm_template in source_parm_template_group.entries():
+ if not hda_parm_template_group.find(parm_template.name()):
+ hda_parm_template_group.addParmTemplate(parm_template)
+
+ hda_def.setParmTemplateGroup(hda_parm_template_group)
+
hda_node.layoutChildren()
elif self._check_existing(folder_path, node_name):
raise CreatorError(
- ("product {} is already published with different HDA"
- "definition.").format(node_name))
+ f"product {node_name} is already published with different HDA definition."
+ )
else:
hda_node = to_hda
@@ -263,52 +288,45 @@ def get_network_categories(self):
hou.objNodeTypeCategory(),
hou.sopNodeTypeCategory(),
hou.topNodeTypeCategory(),
- hou.vopNodeTypeCategory()
+ hou.vopNodeTypeCategory(),
]
def get_pre_create_attr_defs(self):
attrs = super(CreateHDA, self).get_pre_create_attr_defs()
return attrs + [
- BoolDef("set_user",
- tooltip="Set current user as the author of the HDA",
- default=False,
- label="Set Current User"),
- BoolDef("use_project",
- 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"),
+ BoolDef(
+ "set_user",
+ tooltip="Set current user as the author of the HDA",
+ default=False,
+ label="Set Current User",
+ ),
+ BoolDef(
+ "use_project",
+ 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",
+ ),
]
def get_dynamic_data(
- self,
- project_name,
- folder_entity,
- task_entity,
- variant,
- host_name,
- instance
+ 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
+ project_name, folder_entity, task_entity, variant, host_name, instance
)
dynamic_data.update(
{
"asset": folder_entity["name"],
"folder": {
- "label": folder_entity["label"],
- "name": folder_entity["name"]
- }
+ "label": folder_entity["label"],
+ "name": folder_entity["name"],
+ },
}
)