mirror of
https://github.com/ynput/ayon-core.git
synced 2026-01-01 16:34:53 +01:00
Unlock attributes only during the bake context, make sure attributes are relocked after to preserve the lock state of the original node being baked
This commit is contained in:
parent
a75ff0f71a
commit
1a39a5cc99
1 changed files with 87 additions and 84 deletions
|
|
@ -2566,68 +2566,36 @@ def bake_to_world_space(nodes,
|
|||
|
||||
"""
|
||||
@contextlib.contextmanager
|
||||
def _revert_lock_attributes(node, new_node):
|
||||
# Connect all attributes on the node except for transform
|
||||
# attributes
|
||||
attrs = _get_attrs(node)
|
||||
attrs = set(attrs) - transform_attrs if attrs else []
|
||||
original_attrs_lock = {}
|
||||
def _unlock_attr(attr):
|
||||
"""Unlock attribute during context if it is locked"""
|
||||
if not cmds.getAttr(attr, lock=True):
|
||||
# If not locked, do nothing
|
||||
yield
|
||||
return
|
||||
try:
|
||||
for attr in attrs:
|
||||
orig_node_attr = '{0}.{1}'.format(node, attr)
|
||||
new_node_attr = '{0}.{1}'.format(new_node, attr)
|
||||
original_attrs_lock[new_node_attr] = (
|
||||
cmds.getAttr(new_node_attr, lock=True)
|
||||
)
|
||||
|
||||
# unlock to avoid connection errors
|
||||
cmds.setAttr(new_node_attr, lock=False)
|
||||
|
||||
cmds.connectAttr(orig_node_attr,
|
||||
new_node_attr,
|
||||
force=True)
|
||||
cmds.setAttr(attr, lock=False)
|
||||
yield
|
||||
finally:
|
||||
for attr, lock_state in original_attrs_lock.items():
|
||||
cmds.setAttr(attr, lock=lock_state)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _revert_lock_shape_attributes(node, new_node, shape):
|
||||
# If shapes are also baked then connect those keyable attributes
|
||||
if shape:
|
||||
children_shapes = cmds.listRelatives(new_node,
|
||||
children=True,
|
||||
fullPath=True,
|
||||
shapes=True)
|
||||
if children_shapes:
|
||||
orig_children_shapes = cmds.listRelatives(node,
|
||||
children=True,
|
||||
fullPath=True,
|
||||
shapes=True)
|
||||
original_shape_lock_attrs = {}
|
||||
try:
|
||||
for orig_shape, new_shape in zip(orig_children_shapes,
|
||||
children_shapes):
|
||||
attrs = _get_attrs(orig_shape)
|
||||
for attr in attrs:
|
||||
orig_node_attr = '{0}.{1}'.format(orig_shape, attr)
|
||||
new_node_attr = '{0}.{1}'.format(new_shape, attr)
|
||||
original_shape_lock_attrs[new_node_attr] = (
|
||||
cmds.getAttr(new_node_attr, lock=True)
|
||||
)
|
||||
# unlock to avoid connection errors
|
||||
cmds.setAttr(new_node_attr, lock=False)
|
||||
|
||||
cmds.connectAttr(orig_node_attr,
|
||||
new_node_attr,
|
||||
force=True)
|
||||
yield
|
||||
finally:
|
||||
for attr, lock_state in original_shape_lock_attrs.items():
|
||||
cmds.setAttr(attr, lock=lock_state)
|
||||
cmds.setAttr(attr, lock=True)
|
||||
|
||||
def _get_attrs(node):
|
||||
"""Workaround for buggy shape attribute listing with listAttr"""
|
||||
"""Workaround for buggy shape attribute listing with listAttr
|
||||
|
||||
This will only return keyable settable attributes that have an
|
||||
incoming connections (those that have a reason to be baked).
|
||||
|
||||
Technically this *may* fail to return attributes driven by complex
|
||||
expressions for which maya makes no connections, e.g. doing actual
|
||||
`setAttr` calls in expressions.
|
||||
|
||||
Arguments:
|
||||
node (str): The node to list attributes for.
|
||||
|
||||
Returns:
|
||||
list: Keyable attributes with incoming connections.
|
||||
The attribute may be locked.
|
||||
|
||||
"""
|
||||
attrs = cmds.listAttr(node,
|
||||
write=True,
|
||||
scalar=True,
|
||||
|
|
@ -2652,13 +2620,14 @@ def bake_to_world_space(nodes,
|
|||
|
||||
return valid_attrs
|
||||
|
||||
transform_attrs = set(["t", "r", "s",
|
||||
"tx", "ty", "tz",
|
||||
"rx", "ry", "rz",
|
||||
"sx", "sy", "sz"])
|
||||
transform_attrs = {"t", "r", "s",
|
||||
"tx", "ty", "tz",
|
||||
"rx", "ry", "rz",
|
||||
"sx", "sy", "sz"}
|
||||
|
||||
world_space_nodes = []
|
||||
with delete_after() as delete_bin:
|
||||
with contextlib.ExitStack() as stack:
|
||||
delete_bin = stack.enter_context(delete_after())
|
||||
# Create the duplicate nodes that are in world-space connected to
|
||||
# the originals
|
||||
for node in nodes:
|
||||
|
|
@ -2669,32 +2638,66 @@ def bake_to_world_space(nodes,
|
|||
new_node = cmds.duplicate(node,
|
||||
name=new_name,
|
||||
renameChildren=True)[0] # noqa
|
||||
with _revert_lock_attributes(node, new_node):
|
||||
with _revert_lock_shape_attributes(node, new_node):
|
||||
# Parent to world
|
||||
if cmds.listRelatives(new_node, parent=True):
|
||||
new_node = cmds.parent(new_node, world=True)[0]
|
||||
|
||||
# Unlock transform attributes so constraint can be created
|
||||
for attr in transform_attrs:
|
||||
cmds.setAttr(
|
||||
'{0}.{1}'.format(new_node, attr), lock=False)
|
||||
# Parent new node to world
|
||||
if cmds.listRelatives(new_node, parent=True):
|
||||
new_node = cmds.parent(new_node, world=True)[0]
|
||||
|
||||
# Constraints
|
||||
delete_bin.extend(
|
||||
cmds.parentConstraint(node, new_node, mo=False))
|
||||
delete_bin.extend(
|
||||
cmds.scaleConstraint(node, new_node, mo=False))
|
||||
# Temporarily unlock and passthrough connect all attributes
|
||||
# so we can bake them over time
|
||||
# Skip transform attributes because we will constrain them later
|
||||
attrs = set(_get_attrs(node)) - transform_attrs
|
||||
for attr in attrs:
|
||||
orig_node_attr = "{}.{}".format(node, attr)
|
||||
new_node_attr = "{}.{}".format(new_node, attr)
|
||||
|
||||
world_space_nodes.append(new_node)
|
||||
# unlock during context to avoid connection errors
|
||||
stack.enter_context(_unlock_attr(new_node_attr))
|
||||
cmds.connectAttr(orig_node_attr,
|
||||
new_node_attr,
|
||||
force=True)
|
||||
|
||||
bake(world_space_nodes,
|
||||
frame_range=frame_range,
|
||||
step=step,
|
||||
simulation=simulation,
|
||||
preserve_outside_keys=preserve_outside_keys,
|
||||
disable_implicit_control=disable_implicit_control,
|
||||
shape=shape)
|
||||
# If shapes are also baked then also temporarily unlock and
|
||||
# passthrough connect all shape attributes for baking
|
||||
if shape:
|
||||
children_shapes = cmds.listRelatives(new_node,
|
||||
children=True,
|
||||
fullPath=True,
|
||||
shapes=True)
|
||||
if children_shapes:
|
||||
orig_children_shapes = cmds.listRelatives(node,
|
||||
children=True,
|
||||
fullPath=True,
|
||||
shapes=True)
|
||||
for orig_shape, new_shape in zip(orig_children_shapes,
|
||||
children_shapes):
|
||||
attrs = _get_attrs(orig_shape)
|
||||
for attr in attrs:
|
||||
orig_node_attr = "{}.{}".format(orig_shape, attr)
|
||||
new_node_attr = "{}.{}".format(new_shape, attr)
|
||||
|
||||
# unlock during context to avoid connection errors
|
||||
stack.enter_context(_unlock_attr(new_node_attr))
|
||||
cmds.connectAttr(orig_node_attr,
|
||||
new_node_attr,
|
||||
force=True)
|
||||
|
||||
# Constraint transforms
|
||||
for attr in transform_attrs:
|
||||
transform_attr = "{}.{}".format(new_node, attr)
|
||||
stack.enter_context(_unlock_attr(transform_attr))
|
||||
delete_bin.extend(cmds.parentConstraint(node, new_node, mo=False))
|
||||
delete_bin.extend(cmds.scaleConstraint(node, new_node, mo=False))
|
||||
|
||||
world_space_nodes.append(new_node)
|
||||
|
||||
bake(world_space_nodes,
|
||||
frame_range=frame_range,
|
||||
step=step,
|
||||
simulation=simulation,
|
||||
preserve_outside_keys=preserve_outside_keys,
|
||||
disable_implicit_control=disable_implicit_control,
|
||||
shape=shape)
|
||||
|
||||
return world_space_nodes
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue