151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374 | class CreateHDA(plugin.HoudiniCreator):
"""Publish Houdini Digital Asset file."""
identifier = "io.openpype.creators.houdini.hda"
label = "Houdini Digital Asset (Hda)"
description = "Create a Houdini Digital Asset (HDA) product"
product_type = "hda"
product_base_type = "hda"
icon = "gears"
def _check_existing(self, folder_path, product_name):
# type: (str, str) -> bool
"""Check if existing product name versions already exists."""
# Get all products of the current folder
project_name = self.project_name
folder_entity = ayon_api.get_folder_by_path(
project_name, folder_path, fields={"id"}
)
product_entities = ayon_api.get_products(
project_name, folder_ids={folder_entity["id"]}, fields={"name"}
)
existing_product_names_low = {
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,
instance_data=None
):
if pre_create_data is None:
pre_create_data = {}
use_promote_spare_parameters = pre_create_data.get(
"use_promote_spare_parameters", True)
if self.selected_nodes:
# if we have `use selection` enabled, and we have some
# selected nodes ...
one_node_selected = len(self.selected_nodes) == 1
first_selected_node = self.selected_nodes[0]
# If only an HDA is selected, publish just that node as HDA.
if one_node_selected and first_selected_node.type().definition():
to_hda = first_selected_node
use_promote_spare_parameters = False
# If only a single subnet is selected, turn that into the HDA.
elif (
one_node_selected
and first_selected_node.type().name() == "subnet"
):
to_hda = first_selected_node
to_hda.setName("{}_subnet".format(node_name), unique_name=True)
# Collapse all selected nodes into a subnet and turn that into
# the HDA.
else:
parent_node = self.selected_nodes[0].parent()
subnet = parent_node.collapseIntoSubnet(
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():
# if node type has not its definition, it is not user
# created hda. We test if hda can be created from the node.
if not to_hda.canCreateDigitalAsset():
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
)
)
hda_node = to_hda.createDigitalAsset(
name=type_name,
description=node_name,
ignore_external_references=True,
min_num_inputs=0,
max_num_inputs=len(to_hda.inputs()) or 1,
save_as_embedded=True,
)
if use_promote_spare_parameters:
# Similar to Houdini default behavior, when enabled this will
# promote spare parameters to type properties on initial
# creation of the HDA.
promote_spare_parameters(hda_node)
hda_node.layoutChildren()
elif self._check_existing(folder_path, node_name):
raise CreatorError(
("product {} is already published with different HDA"
"definition.").format(node_name))
else:
hda_node = to_hda
# 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)
# Set Custom settings.
hda_def = hda_node.type().definition()
if pre_create_data.get("set_user"):
hda_def.setUserInfo(get_ayon_username())
if pre_create_data.get("use_project"):
set_tool_submenu(hda_def, "AYON/{}".format(self.project_name))
return hda_node
def set_node_staging_dir(
self, node, staging_dir, instance, pre_create_data):
with hou.ScriptEvalContext(node):
staging_dir = expand_houdini_string(staging_dir, r"`[^`]+`")
hda_file_name = f"{staging_dir}/{node.name()}.hda"
hda_def = node.type().definition()
hda_def.save(hda_file_name, node)
hou.hda.installFile(hda_file_name)
# Remove the embedded HDA.
hda_def.destroy()
def get_network_categories(self):
# Houdini allows creating sub-network nodes inside
# these categories.
# Therefore this plugin can work in these categories.
return [
hou.chopNodeTypeCategory(),
hou.cop2NodeTypeCategory(),
hou.dopNodeTypeCategory(),
hou.ropNodeTypeCategory(),
hou.lopNodeTypeCategory(),
hou.objNodeTypeCategory(),
hou.sopNodeTypeCategory(),
hou.topNodeTypeCategory(),
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("use_promote_spare_parameters",
tooltip="Similar to Houdini default behavior, when "
"enabled this will promote spare parameters to "
"type properties on initial creation of the HDA.",
default=True,
label="Promote Spare Parameters"),
]
def get_dynamic_data(
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.update(
{
"asset": folder_entity["name"],
"folder": {
"label": folder_entity["label"],
"name": folder_entity["name"]
}
}
)
return dynamic_data
|