support blending animations

This commit is contained in:
David Rose 2002-07-02 04:48:51 +00:00
parent 824df2dd87
commit 013c3cbc96
2 changed files with 142 additions and 4 deletions

View File

@ -1031,7 +1031,79 @@ class Actor(PandaObject, NodePath):
thisLod)
if (animControl != None):
animControl.pose(frame)
def enableBlend(self, partName = None):
"""Enables blending of multiple animations simultaneously.
After this is called, you may call play(), loop(), or pose()
on multiple animations and have all of them contribute to the
final pose each frame.
With blending in effect, starting a particular animation with
play(), loop(), or pose() does not implicitly make the
animation visible; you must also call setControlEffect() for
each animation you wish to use to indicate how much each
animation contributes to the final pose.
"""
for lodName, bundleDict in self.__partBundleDict.items():
if partName == None:
for partBundle in bundleDict.values():
partBundle.node().getBundle().setBlendType(PartBundle.BTNormalizedLinear)
else:
partBundle = bundleDict.get(partName)
if partBundle != None:
partBundle.node().getBundle().setBlendType(PartBundle.BTNormalizedLinear)
else:
Actor.notify.warning("Couldn't find part: %s" % (partName))
def disableBlend(self, partName = None):
""" Restores normal one-animation-at-a-time operation after a
previous call to enableBlend().
"""
for lodName, bundleDict in self.__partBundleDict.items():
if partName == None:
for partBundle in bundleDict.values():
partBundle.node().getBundle().setBlendType(PartBundle.BTSingle)
else:
partBundle = bundleDict.get(partName)
if partBundle != None:
partBundle.node().getBundle().setBlendType(PartBundle.BTSingle)
else:
Actor.notify.warning("Couldn't find part: %s" % (partName))
def setControlEffect(self, animName, effect,
partName = None, lodName = None):
""" Sets the amount by which the named animation contributes to
the overall pose. This controls blending of multiple
animations; it only makes sense to call this after a previous
call to enableBlend().
"""
if lodName == None:
for lodName, controlDict in self.__animControlDict.items():
if partName == None:
for part in controlDict.keys():
ac = self.getAnimControl(animName, part, lodName)
if ac != None:
ac.getPart().setControlEffect(ac, effect)
else:
ac = self.getAnimControl(animName, partName, lodName)
if ac != None:
ac.getPart().setControlEffect(ac, effect)
else:
if partName == None:
controlDict = self.__animControlDict.get(lodName)
if controlDict != None:
for part in controlDict.keys():
ac = self.getAnimControl(animName, part, lodName)
if ac != None:
ac.getPart().setControlEffect(ac, effect)
else:
Actor.notify.warning("couldn't find lod: %s" % (lodName))
else:
ac = self.getAnimControl(animName, partName, lodName)
if ac != None:
ac.getPart().setControlEffect(ac, effect)
def getAnimControl(self, animName, partName, lodName="lodRoot"):
"""getAnimControl(self, string, string, string="lodRoot")
Search the animControl dictionary indicated by lodName for

View File

@ -3,6 +3,7 @@
from PandaModules import *
import Interval
import math
import LerpBlendHelpers
class ActorInterval(Interval.Interval):
@ -22,7 +23,7 @@ class ActorInterval(Interval.Interval):
# will play once and then nothing will happen for the remainder of the
# interval
def __init__(self, actor, animName, loop=0, duration=0.0,
startTime=0.0, endTime=None, name=None):
startTime=0.0, endTime=None, playRate=1.0, name=None):
"""__init__(name)
"""
# Generate unique id
@ -32,7 +33,7 @@ class ActorInterval(Interval.Interval):
self.actor = actor
self.animName = animName
self.loopAnim = loop
self.frameRate = self.actor.getFrameRate(self.animName)
self.frameRate = self.actor.getFrameRate(self.animName) * playRate
self.numFrames = self.actor.getNumFrames(self.animName)
# Compute start time
self.startTime = startTime
@ -100,7 +101,7 @@ class ActorInterval(Interval.Interval):
# Update animation based upon current time
# Pose or stop anim
if (t >= self.getDuration()):
self.actor.stop()
self.actor.stop(self.animName)
frame = self.goToT(self.getDuration())
if self.loopAnim:
self.ignore(self.stopEvent)
@ -123,3 +124,68 @@ class ActorInterval(Interval.Interval):
# Pose anim
self.goToT(t)
class LerpAnimInterval(Interval.Interval):
# Blends between two anims. Start both anims first (or use
# parallel ActorIntervals), then invoke LerpAnimInterval to
# smoothly blend the control effect from the first to the second.
lerpAnimNum = 1
def __init__(self, actor, duration, startAnim, endAnim,
startWeight = 0.0, endWeight = 1.0,
blendType = 'noBlend', name = None):
""" __init__(actor, duration, pos, startPos, other, blendType, name)
"""
# Generate unique name if necessary
if (name == None):
name = 'LerpAnimInterval-%d' % LerpAnimInterval.lerpAnimNum
LerpAnimInterval.lerpAnimNum += 1
# Record class specific variables
self.actor = actor
self.startAnim = startAnim
self.endAnim = endAnim
self.startWeight = startWeight
self.deltaWeight = endWeight - startWeight
self.blendType = self.getBlend(blendType)
# Initialize superclass
Interval.Interval.__init__(self, name, duration)
def updateFunc(self, t, event=Interval.IVAL_NONE):
""" updateFunc(t, event)
Go to time t
"""
if (self.actor.isEmpty()):
self.notify.warning('updateFunc() - %s empty actor!' % self.name)
return
# First, normalize t into the range 0 .. 1, and apply the blendType.
t = self.blendType(float(t) / self.getDuration())
# Then compute the current weight based on the time elapsed so far.
w = self.startWeight + t * self.deltaWeight
print "t = %f, w = %f" % (t, w)
# Apply that weight to the two anims.
self.actor.setControlEffect(self.endAnim, w)
self.actor.setControlEffect(self.startAnim, 1.0 - w)
def getBlend(self, blendType):
"""__getBlend(self, string)
Return the C++ blend class corresponding to blendType string
"""
if (blendType == "easeIn"):
return LerpBlendHelpers.easeIn
elif (blendType == "easeOut"):
return LerpBlendHelpers.easeOut
elif (blendType == "easeInOut"):
return LerpBlendHelpers.easeInOut
elif (blendType == "noBlend"):
return LerpBlendHelpers.noBlend
else:
raise Exception(
'Error: LerpAnimInterval.getBlend: Unknown blend type')