generated from ynput/ayon-addon-template
-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'develop' into chore/cleanup_creator_attributes
- Loading branch information
Showing
6 changed files
with
263 additions
and
2 deletions.
There are no files selected for viewing
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,14 @@ | ||
# AYON Maya USD Chasers | ||
|
||
This folder contains AYON Maya USD python import and export chasers to be | ||
registered on Maya startup. These chasers have the ability to influence how | ||
USD data is imported and exported in Maya. | ||
|
||
For example, the Filter Properties export chaser allows to filter properties | ||
in the exported USD file to only those that match by the specified pattern | ||
using a SideFX Houdini style pattern matching. | ||
|
||
The chasers are registered in the `MayaHost.install` method on Maya launch. | ||
|
||
See also the [Maya USD Import Chaser documentation](https://github.com/Autodesk/maya-usd/blob/dev/lib/mayaUsd/commands/Readme.md#import-chasers) | ||
and [Maya USD Export Chaser documentation](https://github.com/Autodesk/maya-usd/blob/dev/lib/mayaUsd/commands/Readme.md#export-chasers-advanced). |
138 changes: 138 additions & 0 deletions
138
client/ayon_maya/api/chasers/export_filter_properties.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,138 @@ | ||
import re | ||
import fnmatch | ||
import logging | ||
from typing import List | ||
|
||
import mayaUsd.lib as mayaUsdLib | ||
from pxr import Sdf | ||
|
||
|
||
def log_errors(fn): | ||
"""Decorator to log errors on error""" | ||
|
||
def wrap(*args, **kwargs): | ||
|
||
try: | ||
return fn(*args, **kwargs) | ||
except Exception as exc: | ||
logging.error(exc, exc_info=True) | ||
raise | ||
|
||
return wrap | ||
|
||
|
||
def remove_spec(spec: Sdf.Spec): | ||
"""Remove Sdf.Spec authored opinion.""" | ||
if spec.expired: | ||
return | ||
|
||
if isinstance(spec, Sdf.PrimSpec): | ||
# PrimSpec | ||
parent = spec.nameParent | ||
if parent: | ||
view = parent.nameChildren | ||
else: | ||
# Assume PrimSpec is root prim | ||
view = spec.layer.rootPrims | ||
del view[spec.name] | ||
|
||
elif isinstance(spec, Sdf.PropertySpec): | ||
# Relationship and Attribute specs | ||
del spec.owner.properties[spec.name] | ||
|
||
elif isinstance(spec, Sdf.VariantSetSpec): | ||
# Owner is Sdf.PrimSpec (or can also be Sdf.VariantSpec) | ||
del spec.owner.variantSets[spec.name] | ||
|
||
elif isinstance(spec, Sdf.VariantSpec): | ||
# Owner is Sdf.VariantSetSpec | ||
spec.owner.RemoveVariant(spec) | ||
|
||
else: | ||
raise TypeError(f"Unsupported spec type: {spec}") | ||
|
||
|
||
def remove_layer_specs(layer: Sdf.Layer, spec_paths: List[Sdf.Path]): | ||
# Iterate in reverse so we iterate the highest paths | ||
# first, so when removing a spec the children specs | ||
# are already removed | ||
for spec_path in reversed(spec_paths): | ||
spec = layer.GetObjectAtPath(spec_path) | ||
if not spec or spec.expired: | ||
continue | ||
remove_spec(spec) | ||
|
||
|
||
def match_pattern(name: str, text_pattern: str) -> bool: | ||
"""SideFX Houdini like pattern matching""" | ||
patterns = text_pattern.split(" ") | ||
is_match = False | ||
for pattern in patterns: | ||
# * means any character | ||
# ? means any single character | ||
# [abc] means a, b, or c | ||
pattern = pattern.strip(" ") | ||
if not pattern: | ||
continue | ||
|
||
excludes = pattern[0] == "^" | ||
|
||
# If name is already matched against earlier pattern in the text | ||
# pattern, then we can skip the pattern if it is not an exclude pattern | ||
if is_match and not excludes: | ||
continue | ||
|
||
if excludes: | ||
pattern = pattern[1:] | ||
|
||
regex = fnmatch.translate(pattern) | ||
match = re.match(regex, name) | ||
if match: | ||
is_match = not excludes | ||
return is_match | ||
|
||
|
||
|
||
class FilterPropertiesExportChaser(mayaUsdLib.ExportChaser): | ||
"""Remove property specs based on pattern""" | ||
|
||
name = "AYON_filterProperties" | ||
|
||
def __init__(self, factoryContext, *args, **kwargs): | ||
super().__init__(factoryContext, *args, **kwargs) | ||
self.log = logging.getLogger(self.__class__.__name__) | ||
self.stage = factoryContext.GetStage() | ||
self.job_args = factoryContext.GetJobArgs() | ||
|
||
@log_errors | ||
def PostExport(self): | ||
|
||
chaser_args = self.job_args.allChaserArgs[self.name] | ||
# strip all or use user-specified pattern | ||
pattern = chaser_args.get("pattern", "*") | ||
for layer in self.stage.GetLayerStack(): | ||
|
||
specs_to_remove = [] | ||
|
||
def find_attribute_specs_to_remove(path: Sdf.Path): | ||
if not path.IsPropertyPath(): | ||
return | ||
|
||
spec = layer.GetObjectAtPath(path) | ||
if not spec: | ||
return | ||
|
||
if not isinstance(spec, Sdf.PropertySpec): | ||
return | ||
|
||
if not match_pattern(spec.name, pattern): | ||
self.log.debug("Removing spec: %s", path) | ||
specs_to_remove.append(path) | ||
else: | ||
self.log.debug("Keeping spec: %s", path) | ||
|
||
layer.Traverse("/", find_attribute_specs_to_remove) | ||
|
||
remove_layer_specs(layer, specs_to_remove) | ||
|
||
return True |
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
50 changes: 50 additions & 0 deletions
50
client/ayon_maya/plugins/publish/collect_maya_usd_export_filter_properties.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,50 @@ | ||
import pyblish.api | ||
|
||
from ayon_core.lib import TextDef | ||
from ayon_core.pipeline.publish import AYONPyblishPluginMixin | ||
from ayon_maya.api import plugin | ||
|
||
|
||
class CollectMayaUsdFilterProperties(plugin.MayaInstancePlugin, | ||
AYONPyblishPluginMixin): | ||
|
||
order = pyblish.api.CollectorOrder | ||
label = "Maya USD Export Chaser: Filter Properties" | ||
families = ["mayaUsd"] | ||
|
||
default_filter = "" | ||
|
||
@classmethod | ||
def get_attribute_defs(cls): | ||
return [ | ||
TextDef( | ||
"filter_properties", | ||
label="USD Filter Properties", | ||
tooltip=( | ||
"Filter USD properties using a pattern:\n" | ||
"- Only include xforms: xformOp*\n" | ||
"- All but xforms: * ^xformOp*\n" | ||
"- All but mesh point data: * ^extent ^points " | ||
"^faceVertex* ^primvars*\n\n" | ||
"The pattern matching is very similar to SideFX Houdini's " | ||
"Pattern Matching in Parameters." | ||
), | ||
placeholder="* ^xformOp* ^points", | ||
default=cls.default_filter | ||
) | ||
] | ||
|
||
def process(self, instance): | ||
attr_values = self.get_attr_values_from_data(instance.data) | ||
filter_pattern = attr_values.get("filter_properties") | ||
if not filter_pattern: | ||
return | ||
|
||
self.log.debug( | ||
"Enabling USD filter properties chaser " | ||
f"with pattern {filter_pattern}" | ||
) | ||
instance.data.setdefault("chaser", []).append("AYON_filterProperties") | ||
instance.data.setdefault("chaserArgs", []).append( | ||
("AYON_filterProperties", "pattern", filter_pattern) | ||
) |
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
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