Fix LKD-15: Ensure deformed shapes in look have ids from history (reuse logic from validate_rig_out_set_node_ids.py)

This commit is contained in:
Roy Nieterau 2018-02-27 14:58:05 +01:00
parent 44f935b24b
commit c4daf66e55
4 changed files with 109 additions and 181 deletions

View file

@ -1247,3 +1247,44 @@ def remove_other_uv_sets(mesh):
for i in indices:
attr = '{0}.uvSet[{1}]'.format(mesh, i)
cmds.removeMultiInstance(attr, b=True)
def get_id_from_history(node):
"""Return first node id in the history chain that matches this node.
The nodes in history must be of the exact same node type and must be
parented under the same parent.
Args:
node (str): node to retrieve the
Returns:
str or None: The id from the node in history or None when no id found
on any valid nodes in the history.
"""
def _get_parent(node):
"""Return full path name for parent of node"""
return cmds.listRelatives(node, parent=True, fullPath=True)
node = cmds.ls(node, long=True)[0]
# 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 = _get_parent(node)
similar_nodes = [i for i in similar_nodes if _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 = get_id(similar_node)
if _id:
return _id

View file

@ -1,130 +0,0 @@
from maya import cmds
import pyblish.api
import colorbleed.api
import colorbleed.maya.lib as lib
# from cbra.utils.maya.node_uuid import add_ids
def get_deformed_history_id_mapping(shapes):
"""Return the id from history for nodes that are "Deformed".
When shapes are referenced and get deformed by a deformer
the shape is duplicated *without its attributes* as such
the new shape misses object ids. This method will try to
trace back in the history to find the first shape with
ids to identify the possible best match.
Args:
shapes (list): The shapes that are deformed.
Returns:
dict: Mapping of deformed shape to history shape.
"""
shapes = cmds.ls(shapes, shapes=True, long=True)
# Possibly deformed shapes
deformed_shapes = [x for x in shapes if "Deformed" in x.rsplit("|", 1)[-1]]
# The deformed shape should not be referenced
is_referenced = lambda n: cmds.referenceQuery(n, isNodeReferenced=True)
deformed_shapes = [x for x in deformed_shapes if not is_referenced(x)]
# Shapes without id
deformed_shapes_without_id = [x for x in deformed_shapes
if not lib.get_id(x)]
mapping = {}
for shape in deformed_shapes_without_id:
node_type = cmds.objectType(shape)
history = cmds.listHistory(shape)[1:] # history, skipping itself
history_shapes = cmds.ls(history, exactType=node_type, long=True)
if not history_shapes:
continue
for history_shape in history_shapes:
id = lib.get_id(history_shape)
if not id:
continue
mapping[shape] = history_shape
break
return mapping
class CopyUUIDsFromHistory(pyblish.api.Action):
"""Repairs the action
To retrieve the invalid nodes this assumes a static `get_invalid(instance)`
method is available on the plugin.
"""
label = "Copy UUIDs from History"
on = "failed" # This action is only available on a failed plug-in
icon = "wrench" # Icon from Awesome Icon
def process(self, context, plugin):
# Get the errored instances
self.log.info("Finding failed instances..")
errored = colorbleed.api.get_errored_instances_from_context(context)
# Apply pyblish.logic to get the instances for the plug-in
instances = pyblish.api.instances_by_plugin(errored, plugin)
ids_map = dict()
for instance in instances:
invalid = plugin.get_invalid(instance)
mapping = get_deformed_history_id_mapping(invalid)
for destination, source in mapping.items():
ids_map[destination] = lib.get_id(source)
if not ids_map:
return
self.log.info(ids_map)
class ValidateLookDeformedShapes(pyblish.api.InstancePlugin):
"""Validate look textures are set to ignore color space when set to RAW
Whenever the format is NOT set to sRGB for a file texture it must have
its ignore color space file rules checkbox enabled to avoid unwanted
reverting to sRGB settings upon file relinking.
To fix this use the select invalid action to find the invalid file nodes
and then check the "Ignore Color Space File Rules" checkbox under the
Color Space settings.
"""
order = colorbleed.api.ValidateContentsOrder
families = ['colorbleed.look']
hosts = ['maya']
label = 'Look deformed shapes'
actions = [colorbleed.api.SelectInvalidAction, CopyUUIDsFromHistory]
@classmethod
def get_invalid(cls, instance):
context = instance.context
nodes = context.data.get("instancePerItemNodesWithoutId", None)
if not nodes:
return list()
mapping = get_deformed_history_id_mapping(nodes)
return mapping.keys()
def process(self, instance):
"""Process all the nodes in the instance"""
invalid = self.get_invalid(instance)
if invalid:
raise RuntimeError("Shapes found that are considered 'Deformed'"
"without object ids: {0}".format(invalid))

View file

@ -0,0 +1,66 @@
from maya import cmds
import pyblish.api
import colorbleed.api
import colorbleed.maya.lib as lib
class ValidateNodeIdsDeformedShape(pyblish.api.InstancePlugin):
"""Validate if deformed shapes have related IDs to the original shapes.
When a deformer is applied in the scene on a referenced mesh that already
had deformers then Maya will create a new shape node for the mesh that
does not have the original id. This validator checks whether the ids are
valid on all the shape nodes in the instance.
"""
order = colorbleed.api.ValidateContentsOrder
families = ['colorbleed.look']
hosts = ['maya']
label = 'Deformed shape ids'
actions = [colorbleed.api.SelectInvalidAction, colorbleed.api.RepairAction]
def process(self, instance):
"""Process all the nodes in the instance"""
# 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)
if invalid:
raise RuntimeError("Shapes found that are considered 'Deformed'"
"without object ids: {0}".format(invalid))
@classmethod
def get_invalid(cls, instance):
"""Get all nodes which do not match the criteria"""
shapes = cmds.ls(instance[:],
dag=True,
leaf=True,
shapes=True,
long=True,
noIntermediate=True)
invalid = []
for shape in shapes:
history_id = lib.get_id_from_history(shape)
if history_id:
current_id = lib.get_id(shape)
if current_id != history_id:
invalid.append(shape)
return invalid
@classmethod
def repair(cls, instance):
for node in cls.get_invalid(instance):
# Get the original id from history
history_id = lib.get_id_from_history(node)
if not history_id:
cls.log.error("Could not find ID in history for '%s'", node)
continue
lib.set_id(node, history_id, overwrite=True)

View file

@ -5,55 +5,6 @@ import colorbleed.api
import colorbleed.maya.lib as lib
def get_id_from_history(node):
"""Return first node id in the history chain that matches this node.
The nodes in history must be of the exact same node type and must be
parented under the same parent.
Args:
node (str): node to retrieve the
Returns:
str or None: The id from the node in history or None when no id found
on any valid nodes in the history.
"""
node = cmds.ls(node, long=True)[0]
# 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 = get_parent(node)
similar_nodes = [i for i in similar_nodes if
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 _id:
return _id
def get_parent(node):
"""Get the parent node of the given node
Args:
node (str): full path of the node
Returns:
str, full path if parent node
"""
return cmds.listRelatives(node, parent=True, fullPath=True)
class ValidateRigOutSetNodeIds(pyblish.api.InstancePlugin):
"""Validate if deformed shapes have related IDs to the original shapes.
@ -96,7 +47,7 @@ class ValidateRigOutSetNodeIds(pyblish.api.InstancePlugin):
noIntermediate=True)
for shape in shapes:
history_id = get_id_from_history(shape)
history_id = lib.get_id_from_history(shape)
if history_id:
current_id = lib.get_id(shape)
if current_id != history_id:
@ -109,7 +60,7 @@ class ValidateRigOutSetNodeIds(pyblish.api.InstancePlugin):
for node in cls.get_invalid(instance):
# Get the original id from history
history_id = get_id_from_history(node)
history_id = lib.get_id_from_history(node)
if not history_id:
cls.log.error("Could not find ID in history for '%s'", node)
continue