Skip to content
This repository has been archived by the owner on Sep 20, 2024. It is now read-only.

Maya: Deformer node ids validation plugin #2826

Merged
merged 15 commits into from
Mar 17, 2022
55 changes: 51 additions & 4 deletions openpype/hosts/maya/api/lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -1937,18 +1937,26 @@ def remove_other_uv_sets(mesh):
cmds.removeMultiInstance(attr, b=True)


def get_id_from_history(node):
def get_id_from_sibling(node, history_only=True):
"""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.

Optionally, if no matching node is found from the history, all the
siblings of the node that are of the same type are checked.
Additionally to having the same parent, the sibling must be marked as
'intermediate object'.

Args:
node (str): node to retrieve the
node (str): node to retrieve the history from
history_only (bool): if True and if nothing found in history,
look for an 'intermediate object' in all the node's siblings
of same type

Returns:
str or None: The id from the node in history or None when no id found
on any valid nodes in the history.
str or None: The id from the sibling node or None when no id found
on any valid nodes in the history or siblings.

"""

Expand Down Expand Up @@ -1977,6 +1985,45 @@ def _get_parent(node):
if _id:
return _id

if not history_only:
# Get siblings of same type
similar_nodes = cmds.listRelatives(parent,
type=node_type,
BigRoy marked this conversation as resolved.
Show resolved Hide resolved
fullPath=True)
similar_nodes = cmds.ls(similar_nodes, exactType=node_type, long=True)

# Exclude itself
similar_nodes = [x for x in similar_nodes if x != node]

# Get all unique ids from siblings in order since
# we consistently take the first one found
sibling_ids = OrderedDict()
for similar_node in similar_nodes:
# Check if "intermediate object"
if not cmds.getAttr(similar_node + ".intermediateObject"):
continue

_id = get_id(similar_node)
if not _id:
continue

if _id in sibling_ids:
sibling_ids[_id].append(similar_node)
else:
sibling_ids[_id] = [similar_node]

if sibling_ids:
first_id, found_nodes = next(iter(sibling_ids.items()))

# Log a warning if we've found multiple unique ids
if len(sibling_ids) > 1:
log.warning(("Found more than 1 intermediate shape with"
" unique id for '{}'. Using id of first"
" found: '{}'".format(node, found_nodes[0])))

return first_id



# Project settings
def set_scene_fps(fps, update=True):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ def process(self, instance):
# if a deformer has been created on the shape
invalid = self.get_invalid(instance)
if invalid:
raise RuntimeError("Nodes found with non-related "
"asset IDs: {0}".format(invalid))
raise RuntimeError("Nodes found with mismatching "
"IDs: {0}".format(invalid))

@classmethod
def get_invalid(cls, instance):
Expand Down Expand Up @@ -65,7 +65,7 @@ def get_invalid(cls, instance):
invalid.append(node)
continue

history_id = lib.get_id_from_history(node)
history_id = lib.get_id_from_sibling(node)
if history_id is not None and node_id != history_id:
invalid.append(node)

Expand All @@ -76,7 +76,7 @@ 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)
history_id = lib.get_id_from_sibling(node)
if not history_id:
cls.log.error("Could not find ID in history for '%s'", node)
continue
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def get_invalid(cls, instance):

invalid = []
for shape in shapes:
history_id = lib.get_id_from_history(shape)
history_id = lib.get_id_from_sibling(shape)
if history_id:
current_id = lib.get_id(shape)
if current_id != history_id:
Expand All @@ -61,7 +61,7 @@ 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)
history_id = lib.get_id_from_sibling(node)
if not history_id:
cls.log.error("Could not find ID in history for '%s'", node)
continue
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class ValidateRigOutSetNodeIds(pyblish.api.InstancePlugin):
openpype.hosts.maya.api.action.SelectInvalidAction,
openpype.api.RepairAction
]
allow_history_only = False
BigRoy marked this conversation as resolved.
Show resolved Hide resolved

def process(self, instance):
"""Process all meshes"""
Expand All @@ -32,8 +33,8 @@ def process(self, instance):
# if a deformer has been created on the shape
invalid = self.get_invalid(instance)
if invalid:
raise RuntimeError("Nodes found with non-related "
"asset IDs: {0}".format(invalid))
raise RuntimeError("Nodes found with mismatching "
"IDs: {0}".format(invalid))

@classmethod
def get_invalid(cls, instance):
Expand All @@ -51,10 +52,13 @@ def get_invalid(cls, instance):
noIntermediate=True)

for shape in shapes:
history_id = lib.get_id_from_history(shape)
if history_id:
sibling_id = lib.get_id_from_sibling(
shape,
history_only=cls.allow_history_only
)
if sibling_id:
current_id = lib.get_id(shape)
if current_id != history_id:
if current_id != sibling_id:
invalid.append(shape)

return invalid
Expand All @@ -63,10 +67,13 @@ def get_invalid(cls, instance):
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)
# Get the original id from sibling
sibling_id = lib.get_id_from_sibling(
node,
history_only=cls.allow_history_only
)
if not sibling_id:
cls.log.error("Could not find ID in siblings for '%s'", node)
continue

lib.set_id(node, history_id, overwrite=True)
lib.set_id(node, sibling_id, overwrite=True)
4 changes: 4 additions & 0 deletions openpype/settings/defaults/project_settings/maya.json
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,10 @@
"optional": true,
"active": true
},
"ValidateRigOutSetNodeIds": {
"enabled": true,
"allow_history_only": false
},
"ValidateCameraAttributes": {
"enabled": false,
"optional": true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,26 @@
"label": "Validate Rig Controllers"
}
]
},
{
"type": "dict",
"collapsible": true,
"checkbox_key": "enabled",
"key": "ValidateRigOutSetNodeIds",
"label": "Validate Rig Out Set Node Ids",
"is_group": true,
"children": [
{
"type": "boolean",
"key": "enabled",
"label": "Enabled"
},
{
"type": "boolean",
"key": "allow_history_only",
"label": "Allow history only"
}
]
}
]
},
Expand Down