mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-27 06:12:19 +01:00
Replace legacy instance collector
This commit is contained in:
parent
9976d7bc95
commit
2f8b45acdc
2 changed files with 30 additions and 241 deletions
|
|
@ -2,7 +2,6 @@ from maya import cmds
|
|||
import maya.api.OpenMaya as om
|
||||
|
||||
import pyblish.api
|
||||
import json
|
||||
|
||||
|
||||
def get_all_children(nodes):
|
||||
|
|
@ -45,8 +44,8 @@ def get_all_children(nodes):
|
|||
return list(traversed)
|
||||
|
||||
|
||||
class CollectInstances(pyblish.api.ContextPlugin):
|
||||
"""Gather instances by objectSet and pre-defined attribute
|
||||
class CollectNewInstances(pyblish.api.InstancePlugin):
|
||||
"""Gather members for instances and pre-defined attribute
|
||||
|
||||
This collector takes into account assets that are associated with
|
||||
an objectSet and marked with a unique identifier;
|
||||
|
|
@ -65,132 +64,55 @@ class CollectInstances(pyblish.api.ContextPlugin):
|
|||
|
||||
"""
|
||||
|
||||
label = "Collect Instances"
|
||||
label = "Collect New Instance Data"
|
||||
order = pyblish.api.CollectorOrder
|
||||
hosts = ["maya"]
|
||||
|
||||
def process(self, context):
|
||||
def process(self, instance):
|
||||
|
||||
objectset = cmds.ls("*.id", long=True, type="objectSet",
|
||||
recursive=True, objectsOnly=True)
|
||||
objset = instance.data.get("instance_node")
|
||||
if not objset:
|
||||
self.log.debug("Instance has no `instance_node` data")
|
||||
|
||||
context.data['objectsets'] = objectset
|
||||
for objset in objectset:
|
||||
|
||||
if cmds.attributeQuery("creator_identifier",
|
||||
node=objset,
|
||||
exists=True):
|
||||
# Ignore new style instances
|
||||
continue
|
||||
|
||||
if not cmds.attributeQuery("id", node=objset, exists=True):
|
||||
continue
|
||||
|
||||
id_attr = "{}.id".format(objset)
|
||||
if cmds.getAttr(id_attr) != "pyblish.avalon.instance":
|
||||
continue
|
||||
|
||||
# The developer is responsible for specifying
|
||||
# the family of each instance.
|
||||
has_family = cmds.attributeQuery("family",
|
||||
node=objset,
|
||||
exists=True)
|
||||
assert has_family, "\"%s\" was missing a family" % objset
|
||||
|
||||
members = cmds.sets(objset, query=True)
|
||||
if members is None:
|
||||
self.log.warning("Skipped empty instance: \"%s\" " % objset)
|
||||
continue
|
||||
|
||||
self.log.info("Creating instance for {}".format(objset))
|
||||
|
||||
data = dict()
|
||||
|
||||
# Apply each user defined attribute as data
|
||||
for attr in cmds.listAttr(objset, userDefined=True) or list():
|
||||
try:
|
||||
value = cmds.getAttr("%s.%s" % (objset, attr))
|
||||
except Exception:
|
||||
# Some attributes cannot be read directly,
|
||||
# such as mesh and color attributes. These
|
||||
# are considered non-essential to this
|
||||
# particular publishing pipeline.
|
||||
value = None
|
||||
data[attr] = value
|
||||
|
||||
# temporarily translation of `active` to `publish` till issue has
|
||||
# been resolved, https://github.com/pyblish/pyblish-base/issues/307
|
||||
if "active" in data:
|
||||
data["publish"] = data["active"]
|
||||
# TODO: We might not want to do this in the future
|
||||
# Merge creator attributes into instance.data just backwards compatible
|
||||
# code still runs as expected
|
||||
creator_attributes = instance.data.get("creator_attributes", {})
|
||||
if creator_attributes:
|
||||
instance.data.update(creator_attributes)
|
||||
|
||||
members = cmds.sets(objset, query=True) or []
|
||||
if not members:
|
||||
self.log.warning("Empty instance: \"%s\" " % objset)
|
||||
else:
|
||||
# Collect members
|
||||
members = cmds.ls(members, long=True) or []
|
||||
|
||||
dag_members = cmds.ls(members, type="dagNode", long=True)
|
||||
children = get_all_children(dag_members)
|
||||
children = cmds.ls(children, noIntermediate=True, long=True)
|
||||
|
||||
parents = []
|
||||
if data.get("includeParentHierarchy", True):
|
||||
if creator_attributes.get("includeParentHierarchy", True):
|
||||
# If `includeParentHierarchy` then include the parents
|
||||
# so they will also be picked up in the instance by validators
|
||||
parents = self.get_all_parents(members)
|
||||
members_hierarchy = list(set(members + children + parents))
|
||||
|
||||
if 'families' not in data:
|
||||
data['families'] = [data.get('family')]
|
||||
|
||||
# Create the instance
|
||||
instance = context.create_instance(objset)
|
||||
instance[:] = members_hierarchy
|
||||
instance.data["objset"] = objset
|
||||
|
||||
# Store the exact members of the object set
|
||||
instance.data["setMembers"] = members
|
||||
# Store the exact members of the object set
|
||||
instance.data["setMembers"] = members
|
||||
|
||||
# Define nice label
|
||||
name = cmds.ls(objset, long=False)[0] # use short name
|
||||
label = "{0} ({1})".format(name,
|
||||
data["asset"])
|
||||
|
||||
# Append start frame and end frame to label if present
|
||||
if "frameStart" and "frameEnd" in data:
|
||||
|
||||
# Backwards compatibility for 'handles' data
|
||||
if "handles" in data:
|
||||
data["handleStart"] = data["handles"]
|
||||
data["handleEnd"] = data["handles"]
|
||||
data.pop('handles')
|
||||
|
||||
# Take handles from context if not set locally on the instance
|
||||
for key in ["handleStart", "handleEnd"]:
|
||||
if key not in data:
|
||||
data[key] = context.data[key]
|
||||
|
||||
data["frameStartHandle"] = data["frameStart"] - data["handleStart"] # noqa: E501
|
||||
data["frameEndHandle"] = data["frameEnd"] + data["handleEnd"] # noqa: E501
|
||||
|
||||
label += " [{0}-{1}]".format(int(data["frameStartHandle"]),
|
||||
int(data["frameEndHandle"]))
|
||||
|
||||
instance.data["label"] = label
|
||||
|
||||
instance.data.update(data)
|
||||
|
||||
# Produce diagnostic message for any graphical
|
||||
# user interface interested in visualising it.
|
||||
self.log.info("Found: \"%s\" " % instance.data["name"])
|
||||
self.log.debug(
|
||||
"DATA: {} ".format(json.dumps(instance.data, indent=4)))
|
||||
|
||||
def sort_by_family(instance):
|
||||
"""Sort by family"""
|
||||
return instance.data.get("families", instance.data.get("family"))
|
||||
|
||||
# Sort/grouped by family (preserving local index)
|
||||
context[:] = sorted(context, key=sort_by_family)
|
||||
|
||||
return context
|
||||
# TODO: This might make more sense as a separate collector
|
||||
# Collect frameStartHandle and frameEndHandle if frames present
|
||||
if "frameStart" in instance.data:
|
||||
handle_start = instance.data.get("handleStart", 0)
|
||||
frame_start_handle = instance.data["frameStart"] - handle_start
|
||||
instance.data["frameStartHandle"] = frame_start_handle
|
||||
if "frameEnd" in instance.data:
|
||||
handle_end = instance.data.get("handleEnd", 0)
|
||||
frame_end_handle = instance.data["frameEnd"] + handle_end
|
||||
instance.data["frameEndHandle"] = frame_end_handle
|
||||
|
||||
def get_all_parents(self, nodes):
|
||||
"""Get all parents by using string operations (optimization)
|
||||
|
|
|
|||
|
|
@ -1,133 +0,0 @@
|
|||
from maya import cmds
|
||||
import maya.api.OpenMaya as om
|
||||
|
||||
import pyblish.api
|
||||
|
||||
|
||||
def get_all_children(nodes):
|
||||
"""Return all children of `nodes` including each instanced child.
|
||||
Using maya.cmds.listRelatives(allDescendents=True) includes only the first
|
||||
instance. As such, this function acts as an optimal replacement with a
|
||||
focus on a fast query.
|
||||
|
||||
"""
|
||||
|
||||
sel = om.MSelectionList()
|
||||
traversed = set()
|
||||
iterator = om.MItDag(om.MItDag.kDepthFirst)
|
||||
for node in nodes:
|
||||
|
||||
if node in traversed:
|
||||
# Ignore if already processed as a child
|
||||
# before
|
||||
continue
|
||||
|
||||
sel.clear()
|
||||
sel.add(node)
|
||||
dag = sel.getDagPath(0)
|
||||
|
||||
iterator.reset(dag)
|
||||
# ignore self
|
||||
iterator.next() # noqa: B305
|
||||
while not iterator.isDone():
|
||||
|
||||
path = iterator.fullPathName()
|
||||
|
||||
if path in traversed:
|
||||
iterator.prune()
|
||||
iterator.next() # noqa: B305
|
||||
continue
|
||||
|
||||
traversed.add(path)
|
||||
iterator.next() # noqa: B305
|
||||
|
||||
return list(traversed)
|
||||
|
||||
|
||||
class CollectNewInstances(pyblish.api.InstancePlugin):
|
||||
"""Gather members for instances and pre-defined attribute
|
||||
|
||||
This collector takes into account assets that are associated with
|
||||
an objectSet and marked with a unique identifier;
|
||||
|
||||
Identifier:
|
||||
id (str): "pyblish.avalon.instance"
|
||||
|
||||
Limitations:
|
||||
- Does not take into account nodes connected to those
|
||||
within an objectSet. Extractors are assumed to export
|
||||
with history preserved, but this limits what they will
|
||||
be able to achieve and the amount of data available
|
||||
to validators. An additional collector could also
|
||||
append this input data into the instance, as we do
|
||||
for `pype.rig` with collect_history.
|
||||
|
||||
"""
|
||||
|
||||
label = "Collect New Instance Data"
|
||||
order = pyblish.api.CollectorOrder
|
||||
hosts = ["maya"]
|
||||
|
||||
def process(self, instance):
|
||||
|
||||
objset = instance.data.get("instance_node")
|
||||
if not objset:
|
||||
self.log.debug("Instance has no `instance_node` data")
|
||||
|
||||
# TODO: We might not want to do this in the future
|
||||
# Merge creator attributes into instance.data just backwards compatible
|
||||
# code still runs as expected
|
||||
creator_attributes = instance.data.get("creator_attributes", {})
|
||||
if creator_attributes:
|
||||
instance.data.update(creator_attributes)
|
||||
|
||||
members = cmds.sets(objset, query=True) or []
|
||||
if not members:
|
||||
self.log.warning("Empty instance: \"%s\" " % objset)
|
||||
else:
|
||||
# Collect members
|
||||
members = cmds.ls(members, long=True) or []
|
||||
|
||||
dag_members = cmds.ls(members, type="dagNode", long=True)
|
||||
children = get_all_children(dag_members)
|
||||
children = cmds.ls(children, noIntermediate=True, long=True)
|
||||
parents = []
|
||||
if creator_attributes.get("includeParentHierarchy", True):
|
||||
# If `includeParentHierarchy` then include the parents
|
||||
# so they will also be picked up in the instance by validators
|
||||
parents = self.get_all_parents(members)
|
||||
members_hierarchy = list(set(members + children + parents))
|
||||
|
||||
instance[:] = members_hierarchy
|
||||
|
||||
# Store the exact members of the object set
|
||||
instance.data["setMembers"] = members
|
||||
|
||||
# TODO: This might make more sense as a separate collector
|
||||
# Collect frameStartHandle and frameEndHandle if frames present
|
||||
if "frameStart" in instance.data:
|
||||
handle_start = instance.data.get("handleStart", 0)
|
||||
frame_start_handle = instance.data["frameStart"] - handle_start
|
||||
instance.data["frameStartHandle"] = frame_start_handle
|
||||
if "frameEnd" in instance.data:
|
||||
handle_end = instance.data.get("handleEnd", 0)
|
||||
frame_end_handle = instance.data["frameEnd"] + handle_end
|
||||
instance.data["frameEndHandle"] = frame_end_handle
|
||||
|
||||
def get_all_parents(self, nodes):
|
||||
"""Get all parents by using string operations (optimization)
|
||||
|
||||
Args:
|
||||
nodes (list): the nodes which are found in the objectSet
|
||||
|
||||
Returns:
|
||||
list
|
||||
"""
|
||||
|
||||
parents = []
|
||||
for node in nodes:
|
||||
splitted = node.split("|")
|
||||
items = ["|".join(splitted[0:i]) for i in range(2, len(splitted))]
|
||||
parents.extend(items)
|
||||
|
||||
return list(set(parents))
|
||||
Loading…
Add table
Add a link
Reference in a new issue