mirror of
https://github.com/ynput/ayon-core.git
synced 2026-01-01 16:34:53 +01:00
252 lines
9 KiB
Python
252 lines
9 KiB
Python
import re
|
|
from maya import cmds
|
|
|
|
from openpype.client import get_representations
|
|
from openpype.pipeline import legacy_io
|
|
from openpype.pipeline.workfile.abstract_template_loader import (
|
|
AbstractPlaceholder,
|
|
AbstractTemplateLoader
|
|
)
|
|
from openpype.pipeline.workfile.build_template_exceptions import (
|
|
TemplateAlreadyImported
|
|
)
|
|
|
|
PLACEHOLDER_SET = 'PLACEHOLDERS_SET'
|
|
|
|
|
|
class MayaTemplateLoader(AbstractTemplateLoader):
|
|
"""Concrete implementation of AbstractTemplateLoader for maya
|
|
"""
|
|
|
|
def import_template(self, path):
|
|
"""Import template into current scene.
|
|
Block if a template is already loaded.
|
|
Args:
|
|
path (str): A path to current template (usually given by
|
|
get_template_path implementation)
|
|
Returns:
|
|
bool: Wether the template was succesfully imported or not
|
|
"""
|
|
if cmds.objExists(PLACEHOLDER_SET):
|
|
raise TemplateAlreadyImported(
|
|
"Build template already loaded\n"
|
|
"Clean scene if needed (File > New Scene)")
|
|
|
|
cmds.sets(name=PLACEHOLDER_SET, empty=True)
|
|
self.new_nodes = cmds.file(path, i=True, returnNewNodes=True)
|
|
cmds.setAttr(PLACEHOLDER_SET + '.hiddenInOutliner', True)
|
|
|
|
for set in cmds.listSets(allSets=True):
|
|
if (cmds.objExists(set) and
|
|
cmds.attributeQuery('id', node=set, exists=True) and
|
|
cmds.getAttr(set + '.id') == 'pyblish.avalon.instance'):
|
|
if cmds.attributeQuery('asset', node=set, exists=True):
|
|
cmds.setAttr(
|
|
set + '.asset',
|
|
legacy_io.Session['AVALON_ASSET'], type='string'
|
|
)
|
|
|
|
return True
|
|
|
|
def template_already_imported(self, err_msg):
|
|
clearButton = "Clear scene and build"
|
|
updateButton = "Update template"
|
|
abortButton = "Abort"
|
|
|
|
title = "Scene already builded"
|
|
message = (
|
|
"It's seems a template was already build for this scene.\n"
|
|
"Error message reveived :\n\n\"{}\"".format(err_msg))
|
|
buttons = [clearButton, updateButton, abortButton]
|
|
defaultButton = clearButton
|
|
cancelButton = abortButton
|
|
dismissString = abortButton
|
|
answer = cmds.confirmDialog(
|
|
t=title,
|
|
m=message,
|
|
b=buttons,
|
|
db=defaultButton,
|
|
cb=cancelButton,
|
|
ds=dismissString)
|
|
|
|
if answer == clearButton:
|
|
cmds.file(newFile=True, force=True)
|
|
self.import_template(self.template_path)
|
|
self.populate_template()
|
|
elif answer == updateButton:
|
|
self.update_missing_containers()
|
|
elif answer == abortButton:
|
|
return
|
|
|
|
@staticmethod
|
|
def get_template_nodes():
|
|
attributes = cmds.ls('*.builder_type', long=True)
|
|
return [attribute.rpartition('.')[0] for attribute in attributes]
|
|
|
|
def get_loaded_containers_by_id(self):
|
|
try:
|
|
containers = cmds.sets("AVALON_CONTAINERS", q=True)
|
|
except ValueError:
|
|
return None
|
|
|
|
return [
|
|
cmds.getAttr(container + '.representation')
|
|
for container in containers]
|
|
|
|
|
|
class MayaPlaceholder(AbstractPlaceholder):
|
|
"""Concrete implementation of AbstractPlaceholder for maya
|
|
"""
|
|
|
|
optional_keys = {'asset', 'subset', 'hierarchy'}
|
|
|
|
def get_data(self, node):
|
|
user_data = dict()
|
|
for attr in self.required_keys.union(self.optional_keys):
|
|
attribute_name = '{}.{}'.format(node, attr)
|
|
if not cmds.attributeQuery(attr, node=node, exists=True):
|
|
print("{} not found".format(attribute_name))
|
|
continue
|
|
user_data[attr] = cmds.getAttr(
|
|
attribute_name,
|
|
asString=True)
|
|
user_data['parent'] = (
|
|
cmds.getAttr(node + '.parent', asString=True)
|
|
or node.rpartition('|')[0]
|
|
or ""
|
|
)
|
|
user_data['node'] = node
|
|
if user_data['parent']:
|
|
siblings = cmds.listRelatives(user_data['parent'], children=True)
|
|
else:
|
|
siblings = cmds.ls(assemblies=True)
|
|
node_shortname = user_data['node'].rpartition('|')[2]
|
|
current_index = cmds.getAttr(node + '.index', asString=True)
|
|
user_data['index'] = (
|
|
current_index if current_index >= 0
|
|
else siblings.index(node_shortname))
|
|
|
|
self.data = user_data
|
|
|
|
def parent_in_hierarchy(self, containers):
|
|
"""Parent loaded container to placeholder's parent
|
|
ie : Set loaded content as placeholder's sibling
|
|
Args:
|
|
containers (String): Placeholder loaded containers
|
|
"""
|
|
if not containers:
|
|
return
|
|
|
|
roots = cmds.sets(containers, q=True)
|
|
nodes_to_parent = []
|
|
for root in roots:
|
|
if root.endswith("_RN"):
|
|
refRoot = cmds.referenceQuery(root, n=True)[0]
|
|
refRoot = cmds.listRelatives(refRoot, parent=True) or [refRoot]
|
|
nodes_to_parent.extend(refRoot)
|
|
elif root in cmds.listSets(allSets=True):
|
|
if not cmds.sets(root, q=True):
|
|
return
|
|
else:
|
|
continue
|
|
else:
|
|
nodes_to_parent.append(root)
|
|
|
|
if self.data['parent']:
|
|
cmds.parent(nodes_to_parent, self.data['parent'])
|
|
# Move loaded nodes to correct index in outliner hierarchy
|
|
placeholder_node = self.data['node']
|
|
placeholder_form = cmds.xform(
|
|
placeholder_node,
|
|
q=True,
|
|
matrix=True,
|
|
worldSpace=True
|
|
)
|
|
for node in set(nodes_to_parent):
|
|
cmds.reorder(node, front=True)
|
|
cmds.reorder(node, relative=self.data['index'])
|
|
cmds.xform(node, matrix=placeholder_form, ws=True)
|
|
|
|
holding_sets = cmds.listSets(object=placeholder_node)
|
|
if not holding_sets:
|
|
return
|
|
for holding_set in holding_sets:
|
|
cmds.sets(roots, forceElement=holding_set)
|
|
|
|
def clean(self):
|
|
"""Hide placeholder, parent them to root
|
|
add them to placeholder set and register placeholder's parent
|
|
to keep placeholder info available for future use
|
|
"""
|
|
node = self.data['node']
|
|
if self.data['parent']:
|
|
cmds.setAttr(node + '.parent', self.data['parent'], type='string')
|
|
if cmds.getAttr(node + '.index') < 0:
|
|
cmds.setAttr(node + '.index', self.data['index'])
|
|
|
|
holding_sets = cmds.listSets(object=node)
|
|
if holding_sets:
|
|
for set in holding_sets:
|
|
cmds.sets(node, remove=set)
|
|
|
|
if cmds.listRelatives(node, p=True):
|
|
node = cmds.parent(node, world=True)[0]
|
|
cmds.sets(node, addElement=PLACEHOLDER_SET)
|
|
cmds.hide(node)
|
|
cmds.setAttr(node + '.hiddenInOutliner', True)
|
|
|
|
def get_representations(self, current_asset_doc, linked_asset_docs):
|
|
project_name = legacy_io.active_project()
|
|
|
|
builder_type = self.data["builder_type"]
|
|
if builder_type == "context_asset":
|
|
context_filters = {
|
|
"asset": [current_asset_doc["name"]],
|
|
"subset": [re.compile(self.data["subset"])],
|
|
"hierarchy": [re.compile(self.data["hierarchy"])],
|
|
"representations": [self.data["representation"]],
|
|
"family": [self.data["family"]]
|
|
}
|
|
|
|
elif builder_type != "linked_asset":
|
|
context_filters = {
|
|
"asset": [re.compile(self.data["asset"])],
|
|
"subset": [re.compile(self.data["subset"])],
|
|
"hierarchy": [re.compile(self.data["hierarchy"])],
|
|
"representation": [self.data["representation"]],
|
|
"family": [self.data["family"]]
|
|
}
|
|
|
|
else:
|
|
asset_regex = re.compile(self.data["asset"])
|
|
linked_asset_names = []
|
|
for asset_doc in linked_asset_docs:
|
|
asset_name = asset_doc["name"]
|
|
if asset_regex.match(asset_name):
|
|
linked_asset_names.append(asset_name)
|
|
|
|
context_filters = {
|
|
"asset": linked_asset_names,
|
|
"subset": [re.compile(self.data["subset"])],
|
|
"hierarchy": [re.compile(self.data["hierarchy"])],
|
|
"representation": [self.data["representation"]],
|
|
"family": [self.data["family"]],
|
|
}
|
|
|
|
return list(get_representations(
|
|
project_name,
|
|
context_filters=context_filters
|
|
))
|
|
|
|
def err_message(self):
|
|
return (
|
|
"Error while trying to load a representation.\n"
|
|
"Either the subset wasn't published or the template is malformed."
|
|
"\n\n"
|
|
"Builder was looking for :\n{attributes}".format(
|
|
attributes="\n".join([
|
|
"{}: {}".format(key.title(), value)
|
|
for key, value in self.data.items()]
|
|
)
|
|
)
|
|
)
|