improved check to validate if IDs are related to original use shape

This commit is contained in:
aardschok 2017-10-31 16:42:57 +01:00
parent 3430b6660b
commit dee1d504bf

View file

@ -3,11 +3,10 @@ import maya.cmds as cmds
import pyblish.api import pyblish.api
import colorbleed.api import colorbleed.api
import colorbleed.maya.lib as lib import colorbleed.maya.lib as lib
import avalon.io as io
class ValidateAnimationOutSetRelatedNodeIds(pyblish.api.InstancePlugin): class ValidateAnimationOutSetRelatedNodeIds(pyblish.api.InstancePlugin):
"""Validate rig out_SET nodes have related ids to current context """Validate if nodes have related IDs to the source (original shapes)
An ID is 'related' if its built in the current Item. An ID is 'related' if its built in the current Item.
@ -18,10 +17,10 @@ class ValidateAnimationOutSetRelatedNodeIds(pyblish.api.InstancePlugin):
""" """
order = colorbleed.api.ValidateContentsOrder order = colorbleed.api.ValidateContentsOrder
families = ['colorbleed.animation'] families = ['colorbleed.animation', "colorbleed.pointcache"]
hosts = ['maya'] hosts = ['maya']
label = 'Animation Out Set Related Node Ids' label = 'Animation Out Set Related Node Ids'
actions = [colorbleed.api.SelectInvalidAction] actions = [colorbleed.api.SelectInvalidAction, colorbleed.api.RepairAction]
optional = True optional = True
ignore_types = ("constraint",) ignore_types = ("constraint",)
@ -29,61 +28,105 @@ class ValidateAnimationOutSetRelatedNodeIds(pyblish.api.InstancePlugin):
def process(self, instance): def process(self, instance):
"""Process all meshes""" """Process all meshes"""
# Ensure all nodes have a cbId # Ensure all nodes have a cbId and a related ID to the original shapes
# if a deformer has been created on the shape
invalid = self.get_invalid(instance) invalid = self.get_invalid(instance)
if invalid: if invalid:
raise RuntimeError("Nodes found with non-related " raise RuntimeError("Nodes found with non-related "
"asset IDs: {0}".format(invalid)) "asset IDs: {0}".format(invalid))
@classmethod
def get_pointcache_nodes(cls, instance):
# Get out_SET
sets = cmds.ls(instance, type='objectSet')
pointcache_sets = [x for x in sets if x == 'out_SET']
nodes = list()
for s in pointcache_sets:
# Ensure long names
members = cmds.ls(cmds.sets(s, query=True), long=True)
descendants = cmds.listRelatives(members,
allDescendents=True,
fullPath=True) or []
descendants = cmds.ls(descendants,
noIntermediate=True,
long=True)
hierarchy = members + descendants
nodes.extend(hierarchy)
# ignore certain node types (e.g. constraints)
ignore = cmds.ls(nodes, type=cls.ignore_types, long=True)
if ignore:
ignore = set(ignore)
nodes = [node for node in nodes if node not in ignore]
return nodes
@classmethod @classmethod
def get_invalid(cls, instance): def get_invalid(cls, instance):
invalid_items = [] """Get all nodes which do not match the criteria"""
invalid = []
# get asset id # get asset id
nodes = cls.get_pointcache_nodes(instance) nodes = instance.data["pointcache_data"]
for node in nodes: for node in nodes:
node_type = cmds.nodeType(node)
node_id = lib.get_id(node) node_id = lib.get_id(node)
if node_type == "mesh" and not node_id:
invalid.append(node)
continue
if not node_id: if not node_id:
invalid_items.append(node) continue
# TODO: Should we check whether the ids are related to the rig's asset? root_id = cls.get_history_root_id(node=node)
if root_id is not None:
asset_id = cls.to_item(node_id)
if root_id != asset_id:
invalid.append(node)
# Log invalid item ids return invalid
if invalid_items:
for item_id in sorted(invalid_items):
cls.log.warning("Found invalid item id: {0}".format(item_id))
return invalid_items @classmethod
def get_history_root_id(cls, node):
"""
Get the original node ID when a node has been deformed
Args:
node (str): node to retrieve the
Returns:
str: the asset ID as found in the database
"""
asset_id = None
node = cmds.ls(node, long=True)[0]
# We only check when the node is *not* referenced
if cmds.referenceQuery(node, isNodeReferenced=True):
return
# Find all similar nodes in history
history = cmds.listHistory(node)
node_type = cmds.nodeType(node)
similar_nodes = cmds.ls(history, exactType=node_type, long=True)
# Exclude itself
similar_nodes = [x for x in similar_nodes if x != node]
# The node *must be* under the same parent
parent = cls.get_parent(node)
similar_nodes = [i for i in similar_nodes if
cls.get_parent(i) == parent]
# Check all of the remaining similar nodes and take the first one
# with an id and assume it's the original.
for similar_node in similar_nodes:
_id = lib.get_id(similar_node)
if not _id:
continue
asset_id = cls.to_item(_id)
break
return asset_id
@classmethod
def repair(cls, instance):
for node in cls.get_invalid(instance):
# Get node ID and the asset ID part
node_id = lib.get_id(node)
asset_id = cls.to_item(node_id)
# Get root asset ID
root_id = cls.get_history_root_id(node=node)
# Replace errored ID with good ID
new_id = node_id.replace(asset_id, root_id)
cmds.setAttr("%s.cbId" % node, new_id, type="string")
@staticmethod @staticmethod
def to_item(_id): def to_item(_id):
"""Split the item id part from a node id""" """Split the item id part from a node id"""
return _id.rsplit(":", 1)[0] return _id.split(":", 1)[0]
@staticmethod
def get_parent(node):
return cmds.listRelatives(node, parent=True, fullPath=True)