Has anyone successfully dealt with Maya’s animation layers with the API? I realize that animation layers have been implemented totally with scripts and there is no direct API functionality. However, my animation load/save app has been written directly accessing animCurves with the API. Now the animators have discovered they like animation layers and I need to write to them. If there isn’t already an existing curve on a layer when I’m going to apply keys, I’m having an issue creating a curve on the proper plug.
The animLayer -layeredPlug sounds like it should do what I want. But I can’t seem to give it what it likes as input. And grepping through the Maya others scripts, I don’t see anywhere that’s it’s actually being used so I can see an example.
Well, the “animLayer” command is a clusterf… ahem, I mean, is poorly implemented. Here is what I have discovered so far:
If an attribute has keyframes on an animation layer, I can get the name of the animation curve. Then I can query and set the values on the anim curve directly.
# Note that you must use "edit" mode in order to query the value from "findCurveForPlug".
# Because the Python version of the command is broken. In MEL either "query" mode or "edit"
# mode works.
cmds.animLayer('AnimLayer1', e=True, findCurveForPlug='pCube1.translateY')
# Result: [u'pCube1_translateY_AnimLayer1_inputB'] #
cmds.keyframe('pCube1_translateY_AnimLayer1_inputB', q=True, time=(10, 10), eval=True, valueChange=True)
# Result: [2.0] #
If an attribute does not have an animation curve on a specific layer, I can get the corresponding attribute on the blend node:
# mel.eval? Oh, yeah. The Python version of the command is broken.
mel.eval('animLayer -q -layeredPlug "pCube1.translateZ" "AnimLayer1"')
# Result: pCube1_translateZ_AnimLayer1.inputB #
cmds.getAttr('pCube1_translateZ_AnimLayer1.inputB')
# Result: 3.5679999211430515 #
My remaining problem is that the “layeredPlug” flag does not return a result if the animation layer specified is the base animation layer. So I still don’t know how to get a static value from the base animation layer.
I think I figured out how to get the attribute when the animation layer is the base animation layer. Here it is all put together:
def getAttrPlugForLayer(nodeName, attribute, animLayer):
""" Find the animBlendNode plug corresponding to the given node, attribute,
and animation layer.
"""
nodeAttr = '{0}.{1}'.format(nodeName, attribute)
if not isObjectInAnimLayer(nodeAttr, animLayer):
return None
plug = None
if animLayer == cmds.animLayer(q=True, root=True):
# For the base animation layer, traverse the chain of animBlendNodes all
# the way to the end. The plug will be "inputA" on that last node.
conns = cmds.listConnections(nodeAttr, type='animBlendNodeBase', s=True, d=False)
blendNode = None
while conns:
blendNode = conns[0]
conns = cmds.listConnections(blendNode, type='animBlendNodeBase', s=True, d=False)
plug = '{0}.inputA'.format(blendNode)
else:
# For every layer other than the base animation layer, we can just use
# the "animLayer" command. Unfortunately the "layeredPlug" flag is
# broken in Python in Maya 2016, so we have to use MEL.
cmd = 'animLayer -q -layeredPlug "{0}" "{1}"'.format(nodeAttr, animLayer)
plug = mel.eval(cmd)
return plug
def isObjectInAnimLayer(obj, animLayer):
""" Determine if the given object is in the given animation layer.
Parameters:
* obj - Can be either a node name, like "pCube1", or a node/attribute combo,
like "pCube1.translateX".
* animLayer - The name of an animation layer.
"""
objAnimLayers = cmds.animLayer([obj], q=True, affectedLayers=True) or []
if animLayer in objAnimLayers:
return True
return False