Browse Source

robustify edge conditions

David Rose 22 years ago
parent
commit
6e1cdc100a
1 changed files with 84 additions and 72 deletions
  1. 84 72
      direct/src/interval/ActorInterval.py

+ 84 - 72
direct/src/interval/ActorInterval.py

@@ -14,17 +14,27 @@ class ActorInterval(Interval.Interval):
     # Name counter
     animNum = 1
     # Class methods
-    # Interval used to play an animation.  If loop = 0, animation is
-    # played only once and the pose of the anim depends on t passed
-    # into the setT method every frame.  If loop = 1, the animation is
-    # started and plays on its own and stopped when t >= duration or
-    # playback stop event occurs.  If no duration is specified, interval
-    # duration defaults to be equal to the length of the animation.
-    # Note: if loop == 0 and duration > anim duration then the animation
-    # 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, playRate=1.0, name=None):
+
+    # Plays an animation on an Actor.  The subrange of the animation
+    # to be played may be specified via frames (startFrame up to and
+    # including endFrame) or seconds (startTime up to and including
+    # endTime).  If neither is specified, the default is the entire
+    # range of the animation.
+
+    # The duration may be implicit or explicit.  If it is omitted, it
+    # is taken to be endTime - startTime.  There's not much point in
+    # specifying otherwise unless you also specify loop=1, which will
+    # loop the animation over its frame range during the duration of
+    # the interval.
+    
+    # Note: if loop == 0 and duration > anim duration then the
+    # animation will play once and then hold its final pose for the
+    # remainder of the interval.
+    
+    def __init__(self, actor, animName, loop=0, duration=None,
+                 startTime=None, endTime=None,
+                 startFrame=None, endFrame=None,
+                 playRate=1.0, name=None):
         """__init__(name)
         """
         # Generate unique id
@@ -34,58 +44,67 @@ class ActorInterval(Interval.Interval):
         self.actor = actor
         self.animName = animName
         self.controls = self.actor.getAnimControls(self.animName)
-        assert(len(self.controls) > 0)
         self.loopAnim = loop
-        self.frameRate = self.controls[0].getAnim().getBaseFrameRate() * playRate
-        self.numFrames = self.controls[0].getNumFrames()
-        # Compute start time
-        self.startTime = startTime
+
         # If no name specified, use id as name
         if (name == None):
             name = id
-        # Compute duration if no duration specified
-        self.reverse = 0
-        if duration == 0.0:
-            if (endTime == None):
-                duration = max(self.actor.getDuration(self.animName) - \
-                                startTime, 0.0)
-            else:
-                duration = endTime - startTime
-                if (duration < 0.0):
-                    duration = -duration
-        if (endTime == None):
-            self.finishTime = self.startTime + duration
+
+        if len(self.controls) == 0:
+            self.notify.warning("Unknown animation for actor: %s" % (self.animName))
+            self.frameRate = 1.0
+            self.startFrame = 0
+            self.endFrame = 0
         else:
-            self.finishTime = endTime
-        if (self.startTime > self.finishTime):
+
+            self.frameRate = self.controls[0].getAnim().getBaseFrameRate() * abs(playRate)
+            # Compute start and end frames.
+            if startFrame != None:
+                self.startFrame = startFrame
+            elif startTime != None:
+                self.startFrame = int(math.floor(startTime * self.frameRate + 0.0001))
+            else:
+                self.startFrame = 0
+
+            if endFrame != None:
+                self.endFrame = endFrame
+            elif endTime != None:
+                self.endFrame = int(math.floor(endTime * self.frameRate + 0.0001))
+            else:
+                self.endFrame = self.controls[0].getNumFrames() - 1
+
+        # Must we play the animation backwards?
+        self.reverse = 0
+        if self.endFrame < self.startFrame:
             self.reverse = 1
+            t = self.endFrame
+            self.endFrame = self.startFrame
+            self.startFrame = t
+
+        self.numFrames = self.endFrame - self.startFrame + 1
+
+        # Compute duration if no duration specified
+        self.implicitDuration = 0
+        if duration == None:
+            self.implicitDuration = 1
+            duration = float(self.numFrames - 1) / self.frameRate
 
         # Initialize superclass
         Interval.Interval.__init__(self, name, duration)
 
-    def calcFrame(self, t):
-        segmentLength = abs(self.finishTime - self.startTime)
-        if segmentLength == 0:
-            offset = 0
+    def privStep(self, t):
+        # Calc integer frame number
+        frame = int(math.floor(t * self.frameRate + 0.0001))
+        if self.loopAnim:
+            frame = frame % self.numFrames
         else:
-            offset = t % segmentLength
-        # Handle boundary case where we want to set the final frame
-        if (t == self.getDuration() and offset < 0.0001):
-            offset = segmentLength
-        # Compute current frame based upon current time
-        if (self.reverse == 0):
-            floatFrame = self.frameRate * (self.startTime + offset)
+            frame = max(min(frame, self.numFrames - 1), 0)
+
+        if self.reverse:
+            frame = self.endFrame - frame
         else:
-            negOffset = (self.startTime - self.finishTime) - offset
-            floatFrame = self.frameRate * (self.finishTime + negOffset)
-        # Need max to avoid frame = -1 when t = 0
-        frame = max(0, int(math.ceil(floatFrame)) - 1)
-        # Modulo in case of looping anim
-        return frame % self.numFrames
-
-    def goToT(self, t):
-        # Calc integer frame number
-        frame = self.calcFrame(t)
+            frame = self.startFrame + frame
+
         # Pose anim
 
         # We use our pre-computed list of animControls for
@@ -94,33 +113,26 @@ class ActorInterval(Interval.Interval):
         for control in self.controls:
             control.pose(frame)
             
-        return frame
-
-    def privInitialize(self, t):
         self.state = CInterval.SStarted
-        self.goToT(t)
-        if self.loopAnim:
-            self.actor.loop(self.animName, restart = 0)
         self.currT = t
 
     def privFinalize(self):
-        if self.loopAnim:
-            self.actor.stop()
+        if self.implicitDuration:
+            # As a special case, we ensure we end up posed to the last
+            # frame of the animation if the original duration was
+            # implicit.  This is necessary only to guard against
+            # possible roundoff error in computing the final frame
+            # from the duration.
+            for control in self.controls:
+                control.pose(self.endFrame)
+
         else:
-            self.goToT(self.getDuration())
-        self.currT = self.getDuration()
-        self.state = CInterval.SFinal
+            # Otherwise, the user-specified duration determines which
+            # is our final frame.
+            self.privStep(self.getDuration())
             
-    def privStep(self, t):
-        if not self.loopAnim:
-            self.goToT(t)
-
-        self.state = CInterval.SStarted
-        self.currT = t
-
-    def privInterrupt(self):
-        if self.loopAnim:
-            self.actor.stop
+        self.state = CInterval.SFinal
+        self.intervalDone()
         
 
 class LerpAnimInterval(CLerpAnimEffectInterval):