mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-25 05:14:40 +01:00
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:
parent
44f935b24b
commit
c4daf66e55
4 changed files with 109 additions and 181 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue