Skip to content

Instantly share code, notes, and snippets.

Created May 3, 2021 17:32
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save zewt/f1082ace0532a4dc89ff3f58635c2e57 to your computer and use it in GitHub Desktop.
Freeze bound joints
import pymel.core as pm
from zMayaTools import maya_helpers
# Imported skeletons often have ugly scaling. We should be able to just turn on move skinned
# joints and freeze transforms, but Maya tries to be helpful and prevents us from doing this.
def get_descendants_depth_first(node, depth=0):
if not isinstance(node, pm.nodetypes.Transform):
yield node, depth
for child in node.getChildren():
for result in get_descendants_depth_first(child, depth+1):
yield result
def copy_attr(src, dst, attr):
new_value = src.attr(attr).get()
old_value = dst.attr(attr).get()
# Don't set values if they already match. This avoids throwing errors for locked attributes
# that haven't changed.
if new_value == old_value:
print('Couldn\'t set %s' % dst)
def copy_hierarchy_transforms(to_node, from_node):
to_tree = list(get_descendants_depth_first(to_node))
from_tree = list(get_descendants_depth_first(from_node))
mapping = {}
# First, check that the trees match and make a mapping from source to target nodes.
for (to_node, depth), (from_node, unused) in zip(to_tree, from_tree):
# Don't match up the names for the top-level nodes, since they're usually siblings and
# can't have the same name.
if depth > 0 and to_node.nodeName() != from_node.nodeName():
print('Trees don\'t match (node %s doesn\'t match node %s)' % (to_node, from_node))
if to_node.__class__ != from_node.__class__:
print('Trees don\'t match (node %s is %s, node %s is %s)' % (to_node, to_node.__class__, from_node, from_node.__class__))
mapping[to_node] = from_node
for to_node, unused in to_tree:
from_node = mapping[to_node]
for attr in ('t', 'r', 's', 'rotateAxis', 'rotateOrder'):
copy_attr(from_node, to_node, attr)
if isinstance(to_node, pm.nodetypes.Joint):
for attr in ('jointOrient', ):
copy_attr(from_node, to_node, attr)
def freeze_skinned_joint(node):
with maya_helpers.restores() as restores:
# Enable global move skinned joints mode.
restores.append(maya_helpers.SetAndRestoreCmd(pm.zMoveSkinnedJoints, key='enable', value=True))
# Create a duplicate of the hierarchy that we can freeze transforms on.
node2 = pm.duplicate(node)[0]
# Freeze transforms on the transform, removing any scale and moving rotation into jointOrient. This
# will propagate changes to the children so the children don't change.
pm.makeIdentity(node2, apply=True, rotate=True, scale=True)
# Copy the resulting transforms from node2 to node.
copy_hierarchy_transforms(node, node2)
# Delete our temporary nodes.
freeze_skinned_joint(, type='transform')[0])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment