Skip to content

Commit

Permalink
Merge pull request #72 from monkeez/keying-pair-blend
Browse files Browse the repository at this point in the history
first pass of keying a plug with pair blend or anim layer
  • Loading branch information
mottosso authored Feb 28, 2022
2 parents 0ce4eae + f584173 commit 814c7d9
Showing 1 changed file with 177 additions and 5 deletions.
182 changes: 177 additions & 5 deletions cmdx.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,24 @@
Linear = 2
Smooth = 4

# Animation blend nodes
# Because type checking against MFn.kBlendNodeBase doesn't work :(
AnimBlendTypes = (
om.MFn.kBlendNodeAdditiveRotation,
om.MFn.kBlendNodeAdditiveScale,
om.MFn.kBlendNodeBoolean,
om.MFn.kBlendNodeDouble,
om.MFn.kBlendNodeDoubleAngle,
om.MFn.kBlendNodeDoubleLinear,
om.MFn.kBlendNodeEnum,
om.MFn.kBlendNodeFloat,
om.MFn.kBlendNodeFloatAngle,
om.MFn.kBlendNodeFloatLinear,
om.MFn.kBlendNodeInt16,
om.MFn.kBlendNodeInt32,
om.MFn.kBlendNodeTime,
)

history = dict()


Expand Down Expand Up @@ -2862,6 +2880,10 @@ def attribute(self):
def isArray(self):
return self._mplug.isArray

@property
def isElement(self):
return self._mplug.isElement

@property
def arrayIndices(self):
if not self._mplug.isArray:
Expand All @@ -2874,6 +2896,73 @@ def arrayIndices(self):
def isCompound(self):
return self._mplug.isCompound

@property
def isChild(self):
return self._mplug.isChild

@property
def parent(self):
"""Return the parent of this plug
Examples:
>>> node = createNode("transform", name="mynode")
>>> node["tz"].parent.plug() == node["translate"].plug()
True
>>> node["myArray"] = Double(array=True)
>>> node["myArray"][0].parent.plug() == node["myArray"].plug()
True
>>> node["visibility"].parent
Traceback (most recent call last):
...
TypeError: |mynode.visibility is not a child or element
"""
cls = self.__class__

if self._mplug.isElement:
return cls(self._node, self._mplug.array(), self._unit)

elif self._mplug.isChild:
return cls(self._node, self._mplug.parent(), self._unit)

else:
raise TypeError(
"%s is not a child or element" % self.path()
)

array = parent

def index(self):
"""Return the index of this plug
Examples:
>>> node = createNode("transform", name="mynode")
>>> node["tz"].index()
2
>>> node["myArray"] = Double(array=True)
>>> node["myArray"].extend([2.0, 3.0])
>>> node["myArray"][1].index()
1
>>> node["visibility"].index()
Traceback (most recent call last):
...
TypeError: |mynode.visibility is not a child or element
"""
if self._mplug.isElement:
return self._mplug.logicalIndex()

elif self._mplug.isChild:
for index, plug in enumerate(self.parent):
if plug._mplug == self._mplug:
return index

else:
raise TypeError(
"%s is not a child or element" % self.path()
)


def nextAvailableIndex(self, startIndex=0):
"""Find the next unconnected element in an array plug
Expand Down Expand Up @@ -3151,6 +3240,80 @@ def animated(self, recursive=True):

return False

def findAnimatedPlug(self):
"""Return the plug currently controlling the animation for this plug
Sometimes a plug isn't directly connected to an animCurve, for example
if the plug is constrained or in an animation layer. This method tracks
down where we should apply any new animation.
Example:
>>> _new()
>>> node = createNode("transform")
>>> node["tx"].findAnimatedPlug().path() == node["tx"].path()
True
# Constraint
>>> node["tx"] = {0:1} # Dummy keys so a pairBlend is created
>>> parent = createNode("transform")
>>> _ = cmds.parentConstraint(str(parent), str(node))
>>> blend = ls(type="pairBlend")[0]
>>> node["tx"].findAnimatedPlug().path() == blend["inTranslateX1"].path()
True
# Animation layers
>>> layer = encode(cmds.animLayer(at=node["tx"].path()))
>>> node["tx"].findAnimatedPlug().path() == blend["inTranslateX1"].path()
True
>>> cmds.animLayer(str(layer), e=True, preferred=True)
>>> animBlend = ls(type="animBlendNodeBase")[0]
>>> node["tx"].findAnimatedPlug().path() == animBlend["inputB"].path()
True
# Animation layer then constraint
>>> _new()
>>> node = createNode("transform")
>>> layer = encode(cmds.animLayer(at=node["tx"].path()))
>>> parent = createNode("transform")
>>> _ = cmds.parentConstraint(str(parent), str(node))
>>> animBlend = ls(type="animBlendNodeBase")[0]
>>> node["tx"].findAnimatedPlug().path() == animBlend["inputA"].path()
True
>>> cmds.animLayer(str(layer), e=True, preferred=True)
>>> node["tx"].findAnimatedPlug().path() == animBlend["inputB"].path()
True
"""

plug = None
con = self.input(type=AnimBlendTypes + (MFn.kPairBlend,), plug=True)

while con is not None:
node = con.node()

if node.isA("pairBlend"):
plug = node[con.name().replace("out", "in") + "1"]

else:
# If we find a preferred anim layer the plug we want is inputB
layer = node.input(type="animLayer")
if layer and layer["preferred"] and not layer["lock"]:
plug = node["inputB"]
plug = plug[self.index()] if plug.isCompound else plug
break

# Otherwise we want inputA
plug = node["inputA"]
plug = plug[self.index()] if plug.isCompound else plug

# Search for more pair blends or anim blends
con = plug.input(type=AnimBlendTypes + (MFn.kPairBlend,), plug=True)

# If no animation layers or pair blends then plug is self
plug = plug if plug is not None else self

return plug

def lock(self):
"""Convenience function for plug.locked = True
Expand Down Expand Up @@ -3742,14 +3905,15 @@ def animate(self, values, tangents=None):
"""

times, values = list(map(UiUnit(), values.keys())), values.values()
typ = _find_curve_type(self)
anim = self.input(type=typ)
plug = self.findAnimatedPlug()
typ = _find_curve_type(plug)
anim = plug.input(type=typ)

if not anim:
anim = createNode(typ)
anim["output"] >> self
anim["output"] >> plug

anim.keys(times, values, tangents=Linear)
anim.keys(times, values, tangents=tangents)

def write(self, value):
if isinstance(value, dict) and __maya_version__ > 2015:
Expand Down Expand Up @@ -3994,6 +4158,7 @@ def node(self):
array_indices = arrayIndices
type_class = typeClass
next_available_index = nextAvailableIndex
find_animated_plug = findAnimatedPlug


class TransformationMatrix(om.MTransformationMatrix):
Expand Down Expand Up @@ -4985,12 +5150,19 @@ def _python_to_mod(value, plug, mod):
>>> int(node["ty"].read())
10
# Support for animation keys
>>> mod.set_attr(node["ty"], {1: 0.0, 5: 2.0, 10: 0.0})
>>> mod.doIt()
>>> int(node["ty"].read(time=time(5)))
2
"""

assert isinstance(plug, Plug), "plug must be of type cmdx.Plug"

if isinstance(value, dict) and __maya_version__ > 2015:
times, values = map(UiUnit(), value.keys()), value.values()
times, values = list(map(time, value.keys())), value.values()
plug = plug.findAnimatedPlug()
curve_typ = _find_curve_type(plug)
curve = plug.input(type=curve_typ)

Expand Down

0 comments on commit 814c7d9

Please sign in to comment.