瀏覽代碼

support blending animations

David Rose 23 年之前
父節點
當前提交
013c3cbc96
共有 2 個文件被更改,包括 142 次插入4 次删除
  1. 73 1
      direct/src/actor/Actor.py
  2. 69 3
      direct/src/interval/ActorInterval.py

+ 73 - 1
direct/src/actor/Actor.py

@@ -1031,7 +1031,79 @@ class Actor(PandaObject, NodePath):
                                                     thisLod)
                                                     thisLod)
                 if (animControl != None):
                 if (animControl != None):
                     animControl.pose(frame)
                     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"):
     def getAnimControl(self, animName, partName, lodName="lodRoot"):
         """getAnimControl(self, string, string, string="lodRoot")
         """getAnimControl(self, string, string, string="lodRoot")
         Search the animControl dictionary indicated by lodName for
         Search the animControl dictionary indicated by lodName for

+ 69 - 3
direct/src/interval/ActorInterval.py

@@ -3,6 +3,7 @@
 from PandaModules import *
 from PandaModules import *
 import Interval
 import Interval
 import math
 import math
+import LerpBlendHelpers
 
 
 class ActorInterval(Interval.Interval):
 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
     # will play once and then nothing will happen for the remainder of the
     # interval
     # interval
     def __init__(self, actor, animName, loop=0, duration=0.0,
     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)
         """__init__(name)
         """
         """
         # Generate unique id
         # Generate unique id
@@ -32,7 +33,7 @@ class ActorInterval(Interval.Interval):
         self.actor = actor
         self.actor = actor
         self.animName = animName
         self.animName = animName
         self.loopAnim = loop
         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)
         self.numFrames = self.actor.getNumFrames(self.animName)
         # Compute start time
         # Compute start time
         self.startTime = startTime
         self.startTime = startTime
@@ -100,7 +101,7 @@ class ActorInterval(Interval.Interval):
         # Update animation based upon current time
         # Update animation based upon current time
         # Pose or stop anim
         # Pose or stop anim
         if (t >= self.getDuration()):
         if (t >= self.getDuration()):
-            self.actor.stop()
+            self.actor.stop(self.animName)
             frame = self.goToT(self.getDuration())
             frame = self.goToT(self.getDuration())
             if self.loopAnim:
             if self.loopAnim:
                 self.ignore(self.stopEvent)
                 self.ignore(self.stopEvent)
@@ -123,3 +124,68 @@ class ActorInterval(Interval.Interval):
             # Pose anim
             # Pose anim
             self.goToT(t)
             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')