generated from ynput/ayon-addon-template
-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #163 from BigRoy/enhancement/147-ay-7047_publishin…
…g-component-builder-usd-while-maintaining-hierarchical-structure Publish component builder LOP usd while maintaining hierarchical structure
- Loading branch information
Showing
3 changed files
with
154 additions
and
2 deletions.
There are no files selected for viewing
70 changes: 70 additions & 0 deletions
70
client/ayon_houdini/plugins/create/create_usd_componentbuilder.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import inspect | ||
|
||
from ayon_houdini.api import plugin | ||
from ayon_core.pipeline import CreatedInstance, CreatorError | ||
|
||
import hou | ||
|
||
|
||
class CreateUSDComponentBuilder(plugin.HoudiniCreator): | ||
identifier = "io.ayon.creators.houdini.componentbuilder" | ||
label = "USD Component Builder LOPs" | ||
product_type = "usd" | ||
icon = "cubes" | ||
description = "Create USD from Component Builder LOPs" | ||
|
||
def get_detail_description(self): | ||
return inspect.cleandoc(""" | ||
Creates a USD publish from a Component Output LOP that is part of | ||
a solaris component builder network. | ||
The created USD will contain the component builder LOPs and all its | ||
dependencies inside the single product. | ||
To use it, select a Component Output LOP and click "Create" for | ||
this creator. It will generate an instance for each selected | ||
Component Output LOP. | ||
""") | ||
|
||
def create(self, product_name, instance_data, pre_create_data): | ||
nodes = hou.selectedNodes() | ||
builders = [ | ||
node for node in nodes | ||
if node.type().nameWithCategory() == "Lop/componentoutput" | ||
] | ||
for builder in builders: | ||
self.create_for_instance_node(product_name, instance_data, builder) | ||
|
||
def create_for_instance_node( | ||
self, product_name, instance_data, instance_node): | ||
|
||
try: | ||
self.customize_node_look(instance_node) | ||
instance_data["instance_node"] = instance_node.path() | ||
instance_data["instance_id"] = instance_node.path() | ||
instance_data["families"] = self.get_publish_families() | ||
instance = CreatedInstance( | ||
self.product_type, | ||
product_name, | ||
instance_data, | ||
self) | ||
self._add_instance_to_context(instance) | ||
self.imprint(instance_node, instance.data_to_store()) | ||
except hou.Error as er: | ||
raise CreatorError("Creator error: {}".format(er)) from er | ||
|
||
# Lock any parameters in this list | ||
to_lock = [ | ||
# Lock some AYON attributes | ||
"productType", | ||
"id", | ||
] | ||
self.lock_parameters(instance_node, to_lock) | ||
|
||
def get_network_categories(self): | ||
# Do not expose via tab menu because currently it does not create any | ||
# node, but only 'imprints' on an existing node. | ||
return [] | ||
|
||
def get_publish_families(self): | ||
return ["usd", "componentbuilder"] |
84 changes: 84 additions & 0 deletions
84
client/ayon_houdini/plugins/publish/collect_componentbuilder_lop.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import os | ||
from typing import List, Tuple | ||
from pathlib import Path | ||
|
||
import pyblish.api | ||
|
||
from ayon_core.pipeline import AYONPyblishPluginMixin, PublishError | ||
from ayon_houdini.api import plugin | ||
|
||
import hou | ||
from pxr import Sdf, UsdUtils | ||
|
||
|
||
def compute_all_dependencies( | ||
filepath: str) -> Tuple[list[Sdf.Layer], list[str], list[str]]: | ||
"""Compute all dependencies for the given USD file.""" | ||
# Only separated here for better type hints on returned values | ||
return UsdUtils.ComputeAllDependencies(filepath) | ||
|
||
|
||
class CollectComponentBuilderLOPs(plugin.HoudiniInstancePlugin, | ||
AYONPyblishPluginMixin): | ||
|
||
# Run after `CollectResourcesPath` | ||
order = pyblish.api.CollectorOrder + 0.496 | ||
families = ["componentbuilder"] | ||
label = "Collect Componentbuilder LOPs" | ||
|
||
def process(self, instance): | ||
|
||
node = hou.node(instance.data["instance_node"]) | ||
|
||
# Render the component builder LOPs | ||
# TODO: Do we want this? or use existing frames? Usually a Collector | ||
# should not 'extract' but in this case we need the resulting USD | ||
# file. | ||
node.cook(force=True) # required to clear existing errors | ||
node.parm("execute").pressButton() | ||
|
||
errors = node.errors() | ||
if errors: | ||
for error in errors: | ||
self.log.error(error) | ||
raise PublishError(f"Failed to save to disk '{node.path()}'") | ||
|
||
# Define the main asset usd file | ||
filepath = node.evalParm("lopoutput") | ||
representations = instance.data.setdefault("representations", []) | ||
representations.append({ | ||
"name": "usd", | ||
"ext": "usd", | ||
"files": os.path.basename(filepath), | ||
"stagingDir": os.path.dirname(filepath), | ||
}) | ||
|
||
# Get all its files and dependencies | ||
# TODO: Ignore any files that are not 'relative' to the USD file | ||
layers, assets, unresolved_paths = compute_all_dependencies(filepath) | ||
paths: List[str] = [] | ||
paths.extend(layer.realPath for layer in layers) | ||
paths.extend(assets) | ||
|
||
# Skip unresolved paths, but warn about them | ||
for unresolved in unresolved_paths: | ||
self.log.warning(f"Cannot be resolved: {unresolved}") | ||
|
||
self.log.debug(f"Collecting USD: {filepath}") | ||
src_root_dir = os.path.dirname(filepath) | ||
|
||
# Used to compare resolved paths against | ||
filepath = Path(filepath) | ||
|
||
# We keep the relative paths to the USD file | ||
transfers = instance.data.setdefault("transfers", []) | ||
publish_root = instance.data["publishDir"] | ||
for src in paths: | ||
|
||
if filepath == Path(src): | ||
continue | ||
|
||
relative_path = os.path.relpath(src, start=src_root_dir) | ||
self.log.debug(f"Collected dependency: {relative_path}") | ||
dest = os.path.normpath(os.path.join(publish_root, relative_path)) | ||
transfers.append((src, dest)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters