Prechádzať zdrojové kódy

new C++-based intervals

David Rose 23 rokov pred
rodič
commit
8143fa084c
33 zmenil súbory, kde vykonal 3805 pridanie a 741 odobranie
  1. 1 1
      direct/metalibs/direct/Sources.pp
  2. 16 49
      direct/src/interval/ActorInterval.py
  3. 1 1
      direct/src/interval/FunctionInterval.py
  4. 10 10
      direct/src/interval/Interval.py
  5. 1 3
      direct/src/interval/IntervalGlobal.py
  6. 256 336
      direct/src/interval/LerpInterval.py
  7. 0 74
      direct/src/interval/MultiTrack.py
  8. 20 0
      direct/src/interval/Sources.pp
  9. 0 267
      direct/src/interval/Track.py
  10. 127 0
      direct/src/interval/cInterval.I
  11. 362 0
      direct/src/interval/cInterval.cxx
  12. 135 0
      direct/src/interval/cInterval.h
  13. 62 0
      direct/src/interval/cLerpAnimEffectInterval.I
  14. 70 0
      direct/src/interval/cLerpAnimEffectInterval.cxx
  15. 87 0
      direct/src/interval/cLerpAnimEffectInterval.h
  16. 43 0
      direct/src/interval/cLerpInterval.I
  17. 85 0
      direct/src/interval/cLerpInterval.cxx
  18. 78 0
      direct/src/interval/cLerpInterval.h
  19. 219 0
      direct/src/interval/cLerpNodePathInterval.I
  20. 351 0
      direct/src/interval/cLerpNodePathInterval.cxx
  21. 109 0
      direct/src/interval/cLerpNodePathInterval.h
  22. 239 0
      direct/src/interval/cMetaInterval.I
  23. 877 0
      direct/src/interval/cMetaInterval.cxx
  24. 190 0
      direct/src/interval/cMetaInterval.h
  25. 65 0
      direct/src/interval/config_interval.cxx
  26. 32 0
      direct/src/interval/config_interval.h
  27. 18 0
      direct/src/interval/hideInterval.I
  28. 68 0
      direct/src/interval/hideInterval.cxx
  29. 61 0
      direct/src/interval/hideInterval.h
  30. 75 0
      direct/src/interval/lerp_helpers.h
  31. 18 0
      direct/src/interval/showInterval.I
  32. 68 0
      direct/src/interval/showInterval.cxx
  33. 61 0
      direct/src/interval/showInterval.h

+ 1 - 1
direct/metalibs/direct/Sources.pp

@@ -8,7 +8,7 @@
 #define BUILDING_DLL BUILDING_DIRECT
 
 #define COMPONENT_LIBS \
-   directbase dcparser showbase deadrec directd
+   directbase dcparser showbase deadrec directd interval
 
 #define OTHER_LIBS panda pandaexpress dtoolconfig dtool
 

+ 16 - 49
direct/src/interval/ActorInterval.py

@@ -122,7 +122,7 @@ class ActorInterval(Interval.Interval):
             self.goToT(t)
 
 
-class LerpAnimInterval(Interval.Interval):
+class LerpAnimInterval(CLerpAnimEffectInterval):
     # 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.
@@ -131,58 +131,25 @@ class LerpAnimInterval(Interval.Interval):
     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)
+        blendType = self.stringBlendType(blendType)
+        assert(blendType != self.BTInvalid)
 
         # 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.duration)
-
-        # Then compute the current weight based on the time elapsed so far.
-        w = self.startWeight + t * self.deltaWeight
-
-        # Apply that weight to the two anims.
-        if self.startAnim != None:
-            self.actor.setControlEffect(self.startAnim, 1.0 - w)
-        if self.endAnim != None:
-            self.actor.setControlEffect(self.endAnim, 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')
+        CLerpAnimEffectInterval.__init__(self, name, duration, blendType)
+
+        if startAnim != None:
+            controls = actor.getAnimControls(startAnim)
+            for control in controls:
+                self.addControl(control, startAnim,
+                                1.0 - startWeight, 1.0 - endWeight)
+                
+        if endAnim != None:
+            controls = actor.getAnimControls(endAnim)
+            for control in controls:
+                self.addControl(control, endAnim,
+                                startWeight, endWeight)

+ 1 - 1
direct/src/interval/FunctionInterval.py

@@ -27,7 +27,7 @@ class FunctionInterval(Interval.Interval):
         self.function = function
         # Create a unique name for the interval if necessary
         if (name == None):
-            name = 'FunctionInterval-%d' % FunctionInterval.functionIntervalNum
+            name = 'FunctionInterval-%s-%d' % (function.__name__, FunctionInterval.functionIntervalNum)
             FunctionInterval.functionIntervalNum += 1
         assert(isinstance(name, types.StringType))
         # Record any arguments

+ 10 - 10
direct/src/interval/Interval.py

@@ -5,9 +5,9 @@ from PandaModules import *
 import Task
 
 # Interval events
-IVAL_NONE = 0
-IVAL_INIT = 1
-IVAL_DONE = 2
+IVAL_NONE = CInterval.ETStep
+IVAL_INIT = CInterval.ETInitialize
+IVAL_DONE = CInterval.ETFinalize
 
 class Interval(DirectObject):
     """Interval class: Base class for timeline functionality"""
@@ -75,7 +75,7 @@ class Interval(DirectObject):
     def setFinalT(self):
         """ setFinalT()
         """
-        self.setT(self.getDuration(), event=IVAL_DONE)
+        self.setT(self.getDuration(), IVAL_DONE)
 
     def play(self, t0=0.0, duration=0.0, scale=1.0):
         """ play(t0, duration)
@@ -133,7 +133,7 @@ class Interval(DirectObject):
         if (te < self.endTime):
             if (self.firstTime):
                 # If first call, init intervals
-                self.setT(te, event = IVAL_INIT)
+                self.setT(te, IVAL_INIT)
                 self.firstTime = 0
             else:
                 self.setT(te)
@@ -142,7 +142,7 @@ class Interval(DirectObject):
             te = self.endTime
             if (self.firstTime):
                 # If first call, init intervals
-                self.setT(te, event = IVAL_INIT)
+                self.setT(te, IVAL_INIT)
                 self.firstTime = 0
             else:
                 self.setT(te, IVAL_DONE)
@@ -181,23 +181,23 @@ class Interval(DirectObject):
             # Kill playback task
             taskMgr.remove(s.name + '-play')
             # INIT interval
-            s.setT(es.get(), event = IVAL_INIT)
+            s.setT(es.get(), IVAL_INIT)
         es.onPress = onPress
         # To make sure you stop free running intervals
         es.onRelease = lambda s=self: s.stop()
         # To update scale and execute intervals with IVAL_INIT
         def onReturn(s = self, es = es):
-            s.setT(es.get(), event = IVAL_INIT)
+            s.setT(es.get(), IVAL_INIT)
             s.stop()
         es.onReturnRelease = onReturn
         es.pack(expand = 1, fill = X)
         bf = Frame(outerFrame)
         # Jump to start and end
         def toStart(s=self, es=es):
-            s.setT(0.0, event = IVAL_INIT)
+            s.setT(0.0, IVAL_INIT)
             s.stop()
         def toEnd(s=self):
-            s.setT(s.getDuration(), event = IVAL_INIT)
+            s.setT(s.getDuration(), IVAL_INIT)
             s.stop()
         jumpToStart = Button(bf, text = '<<', command = toStart)
         # Stop/play buttons

+ 1 - 3
direct/src/interval/IntervalGlobal.py

@@ -9,6 +9,4 @@ from MopathInterval import *
 from ParticleInterval import *
 from SoundInterval import *
 from WaitInterval import *
-
-from Track import *
-from MultiTrack import *
+from MetaInterval import *

+ 256 - 336
direct/src/interval/LerpInterval.py

@@ -5,6 +5,262 @@ from DirectNotifyGlobal import *
 import Interval
 import LerpBlendHelpers
 
+#
+# Most of the intervals defined in this module--the group up here at
+# the front of the file--are now derived from a CInterval instead of
+# an Interval, so they can run in the low-level C++ code at high
+# performance.
+#
+
+class LerpNodePathInterval(CLerpNodePathInterval):
+    # This is the base class for all of the lerps, defined below, that
+    # affect a property on a NodePath, like pos or hpr.
+    lerpNodePathNum = 1
+
+    def __init__(self, name, duration, blendType, node, other):
+        if name == None:
+            name = '%s-%d' % (self.__class__.__name__, self.lerpNodePathNum)
+            LerpNodePathInterval.lerpNodePathNum += 1
+
+        blendType = self.stringBlendType(blendType)
+        assert(blendType != self.BTInvalid)
+
+        if other == None:
+            other = NodePath()
+
+        CLerpNodePathInterval.__init__(self, name, duration, blendType,
+                                       node, other)
+
+    def anyCallable(self, *params):
+        # Returns true if any of the parameters listed is a callable
+        # functor, false if none of them are.  This is used by derived
+        # classes to determine if a functor was passed in for a
+        # parameter.
+        
+        for param in params:
+            if callable(param):
+                return 1
+        return 0
+
+    def setupParam(self, func, param):
+        # Stores the indicated parameter by passing it to the given
+        # function (probably a C++ setter function).  If the param is
+        # a callable functor, calls it; otherwise, uses the param
+        # directly.
+        if param != None:
+            if callable(param):
+                func(param())
+            else:
+                func(param)
+
+#####################################################################
+##
+##  It is legal to pass in a functor for the any of the pos, hpr,
+##  or scale parameters in the intervals below.  These will be
+##  evaluated at the time the interval starts in order to determine
+##  the actual final position.  However, doing so forces the entire
+##  LerpInterval to be executed up in Python, instead of down in the
+##  low-level C++ code, at a significant performance cost.
+##
+#####################################################################
+
+class LerpPosInterval(LerpNodePathInterval):
+    def __init__(self, node, duration, pos, startPos = None,
+                 other = None, blendType = 'noBlend', name = None):
+        LerpNodePathInterval.__init__(self, name, duration, blendType,
+                                      node, other)
+
+        # Check for functors in the input parameters.
+        if self.anyCallable(pos, startPos):
+            self.endPos = pos
+            self.startPos = startPos
+            self.inPython = 1
+        else:
+            self.setEndPos(pos)
+            if startPos != None:
+                self.setStartPos(startPos)
+
+    def setT(self, t, event):
+        # This function is only used if Python functors were passed in
+        # for some of the input parameters.
+        if event == Interval.IVAL_INIT:
+            self.setupParam(self.setEndPos, self.endPos)
+            self.setupParam(self.setStartPos, self.startPos)
+        LerpNodePathInterval.setT(self, t, event)
+                
+
+class LerpHprInterval(LerpNodePathInterval):
+    def __init__(self, node, duration, hpr, startHpr = None,
+                 other = None, blendType = 'noBlend', name = None):
+        LerpNodePathInterval.__init__(self, name, duration, blendType,
+                                      node, other)
+
+        # Check for functors in the input parameters.
+        if self.anyCallable(hpr, startHpr):
+            self.endHpr = hpr
+            self.startHpr = startHpr
+            self.inPython = 1
+        else:
+            self.setEndHpr(hpr)
+            if startHpr != None:
+                self.setStartHpr(startHpr)
+
+    def setT(self, t, event):
+        # This function is only used if Python functors were passed in
+        # for some of the input parameters.
+        if event == Interval.IVAL_INIT:
+            self.setupParam(self.setEndHpr, self.endHpr)
+            self.setupParam(self.setStartHpr, self.startHpr)
+        LerpNodePathInterval.setT(self, t, event)
+
+class LerpScaleInterval(LerpNodePathInterval):
+    def __init__(self, node, duration, scale, startScale = None,
+                 other = None, blendType = 'noBlend', name = None):
+        LerpNodePathInterval.__init__(self, name, duration, blendType,
+                                      node, other)
+        # Check for functors in the input parameters.
+        if self.anyCallable(scale, startScale):
+            self.endScale = scale
+            self.startScale = startScale
+            self.inPython = 1
+        else:
+            self.setEndScale(scale)
+            if startScale != None:
+                self.setStartScale(startScale)
+
+    def setT(self, t, event):
+        # This function is only used if Python functors were passed in
+        # for some of the input parameters.
+        if event == Interval.IVAL_INIT:
+            self.setupParam(self.setEndScale, self.endScale)
+            self.setupParam(self.setStartScale, self.startScale)
+        LerpNodePathInterval.setT(self, t, event)
+
+class LerpPosHprInterval(LerpNodePathInterval):
+    def __init__(self, node, duration, pos, hpr,
+                 startPos = None, startHpr = None,
+                 other = None, blendType = 'noBlend', name = None):
+        LerpNodePathInterval.__init__(self, name, duration, blendType,
+                                      node, other)
+        # Check for functors in the input parameters.
+        if self.anyCallable(pos, startPos, hpr, startHpr):
+            self.endPos = pos
+            self.startPos = startPos
+            self.endHpr = hpr
+            self.startHpr = startHpr
+            self.inPython = 1
+        else:
+            self.setEndPos(pos)
+            if startPos != None:
+                self.setStartPos(startPos)
+            self.setEndHpr(hpr)
+            if startHpr != None:
+                self.setStartHpr(startHpr)
+
+    def setT(self, t, event):
+        # This function is only used if Python functors were passed in
+        # for some of the input parameters.
+        if event == Interval.IVAL_INIT:
+            self.setupParam(self.setEndPos, self.endPos)
+            self.setupParam(self.setStartPos, self.startPos)
+            self.setupParam(self.setEndHpr, self.endHpr)
+            self.setupParam(self.setStartHpr, self.startHpr)
+        LerpNodePathInterval.setT(self, t, event)
+
+class LerpHprScaleInterval(LerpNodePathInterval):
+    def __init__(self, node, duration, hpr, scale,
+                 startHpr = None, startScale = None,
+                 other = None, blendType = 'noBlend', name = None):
+        LerpNodePathInterval.__init__(self, name, duration, blendType,
+                                      node, other)
+
+        # Check for functors in the input parameters.
+        if self.anyCallable(hpr, startHpr, scale, startScale):
+            self.endHpr = hpr
+            self.startHpr = startHpr
+            self.endScale = scale
+            self.startScale = startScale
+            self.inPython = 1
+        else:
+            self.setEndHpr(hpr)
+            if startHpr != None:
+                self.setStartHpr(startHpr)
+            self.setEndScale(scale)
+            if startScale != None:
+                self.setStartScale(startScale)
+
+    def setT(self, t, event):
+        # This function is only used if Python functors were passed in
+        # for some of the input parameters.
+        if event == Interval.IVAL_INIT:
+            self.setupParam(self.setEndHpr, self.endHpr)
+            self.setupParam(self.setStartHpr, self.startHpr)
+            self.setupParam(self.setEndScale, self.endScale)
+            self.setupParam(self.setStartScale, self.startScale)
+        LerpNodePathInterval.setT(self, t, event)
+
+class LerpPosHprScaleInterval(LerpNodePathInterval):
+    def __init__(self, node, duration, pos, hpr, scale,
+                 startPos = None, startHpr = None, startScale = None,
+                 other = None, blendType = 'noBlend', name = None):
+        LerpNodePathInterval.__init__(self, name, duration, blendType,
+                                      node, other)
+        # Check for functors in the input parameters.
+        if self.anyCallable(pos, startPos, hpr, startHpr, scale, startScale):
+            self.endPos = pos
+            self.startPos = startPos
+            self.endHpr = hpr
+            self.startHpr = startHpr
+            self.endScale = scale
+            self.startScale = startScale
+            self.inPython = 1
+        else:
+            self.setEndPos(pos)
+            if startPos != None:
+                self.setStartPos(startPos)
+            self.setEndHpr(hpr)
+            if startHpr != None:
+                self.setStartHpr(startHpr)
+            self.setEndScale(scale)
+            if startScale != None:
+                self.setStartScale(startScale)
+
+    def setT(self, t, event):
+        # This function is only used if Python functors were passed in
+        # for some of the input parameters.
+        if event == Interval.IVAL_INIT:
+            self.setupParam(self.setEndPos, self.endPos)
+            self.setupParam(self.setStartPos, self.startPos)
+            self.setupParam(self.setEndHpr, self.endHpr)
+            self.setupParam(self.setStartHpr, self.startHpr)
+            self.setupParam(self.setEndScale, self.endScale)
+            self.setupParam(self.setStartScale, self.startScale)
+        LerpNodePathInterval.setT(self, t, event)
+
+class LerpColorScaleInterval(LerpNodePathInterval):
+    def __init__(self, node, duration, colorScale, startColorScale = None,
+                 other = None, blendType = 'noBlend', name = None):
+        LerpNodePathInterval.__init__(self, name, duration, blendType,
+                                      node, other)
+        self.setEndColorScale(colorScale)
+        if startColorScale != None:
+            self.setStartColorScale(startColorScale)
+
+class LerpColorInterval(LerpNodePathInterval):
+    def __init__(self, node, duration, color, startColor = None,
+                 other = None, blendType = 'noBlend', name = None):
+        LerpNodePathInterval.__init__(self, name, duration, blendType,
+                                      node, other)
+        self.setEndColor(color)
+        if startColor != None:
+            self.setStartColor(startColor)
+
+
+#
+# The remaining intervals defined in this module are the old-school
+# Python-based intervals.
+#
+
 class LerpInterval(Interval.Interval):
     # create LerpInterval DirectNotify category
     notify = directNotify.newCategory('LerpInterval')
@@ -51,342 +307,6 @@ class LerpInterval(Interval.Interval):
             raise Exception(
                 'Error: LerpInterval.__getBlend: Unknown blend type')
 
-class LerpPosInterval(LerpInterval):
-    # Name counter
-    lerpPosNum = 1
-    # Class methods
-    def __init__(self, node, duration, pos, startPos=None,
-                                other=None, blendType='noBlend', name=None):
-        """ __init__(node, duration, pos, startPos, other, blendType, name)
-        """
-        def functorFunc(node=node, pos=pos, startPos=startPos,
-                        other=other):
-            assert(not node.isEmpty())
-            if callable(pos):
-                # This may be a thunk that returns a point.
-                pos = pos()
-            # Make a our own copy of the parameters:
-            if (pos != None): pos=Point3(pos)
-            if (startPos != None): startPos=Point3(startPos)
-            if (other != None):
-                # lerp wrt other
-                if (startPos == None):
-                    startPos = node.getPos(other)
-                functor = PosLerpFunctor(node, startPos, pos, other)
-            else:
-                if (startPos == None):
-                    startPos = node.getPos()
-                functor = PosLerpFunctor(node, startPos, pos)
-            return functor
-
-        # Generate unique name if necessary
-        if (name == None):
-            name = 'LerpPosInterval-%d' % LerpPosInterval.lerpPosNum
-            LerpPosInterval.lerpPosNum += 1
-        # Initialize superclass
-        LerpInterval.__init__(self, name, duration, functorFunc, blendType) 
-
-class LerpHprInterval(LerpInterval):
-    # Name counter
-    lerpHprNum = 1
-    # Class methods
-    def __init__(self, node, duration, hpr, startHpr=None,
-                                other=None, blendType='noBlend', name=None):
-        """ __init__(node, duration, hpr, startHpr, other, blendType, name)
-        """
-        def functorFunc(node=node, hpr=hpr, startHpr=startHpr,
-                        other=other):
-            assert(not node.isEmpty())
-            if callable(hpr):
-                # This may be a thunk that returns a point.
-                hpr = hpr()
-            # Make a our own copy of the parameters:
-            if (hpr != None): hpr=VBase3(hpr)
-            if (startHpr != None): startHpr=VBase3(startHpr)
-            if (other != None):
-                # lerp wrt other
-                if (startHpr == None):
-                    startHpr = VBase3(node.getHpr(other))
-                functor = HprLerpFunctor(node, startHpr, hpr, other)
-            else:
-                if (startHpr == None):
-                    startHpr = node.getHpr()
-                functor = HprLerpFunctor(node, startHpr, hpr)
-            return functor
-
-        # Generate unique name if necessary
-        if (name == None):
-            name = 'LerpHprInterval-%d' % LerpHprInterval.lerpHprNum
-            LerpHprInterval.lerpHprNum += 1
-        # Initialize superclass
-        LerpInterval.__init__(self, name, duration, functorFunc, blendType)
-
-class LerpScaleInterval(LerpInterval):
-
-    # Interval counter
-    lerpScaleNum = 1
-    # Class methods
-    def __init__(self, node, duration, scale, startScale=None,
-                                other=None, blendType='noBlend', name=None):
-        """ __init__(node, duration, scale, startScale, other, blendType, name)
-        """
-        def functorFunc(node=node, scale=scale,
-                        startScale=startScale, other=other):
-            assert(not node.isEmpty())
-            if callable(scale):
-                # This may be a thunk that returns a point.
-                scale = scale()
-            # Make a our own copy of the parameters:
-            if (scale != None): scale=VBase3(scale)
-            if (startScale != None): startScale=VBase3(startScale)
-            if (other != None):
-                # lerp wrt other
-                if (startScale == None):
-                    startScale = node.getScale(other)
-                functor = ScaleLerpFunctor(node, startScale, scale, other)
-            else:
-                if (startScale == None):
-                    startScale = node.getScale()
-                functor = ScaleLerpFunctor(node, startScale, scale)
-            return functor
-
-        # Generate unique name if necessary
-        if (name == None):
-            name = 'LerpScaleInterval-%d' % LerpScaleInterval.lerpScaleNum
-            LerpScaleInterval.lerpScaleNum += 1
-        # Initialize superclass
-        LerpInterval.__init__(self, name, duration, functorFunc, blendType) 
-
-class LerpPosHprInterval(LerpInterval):
-    # Interval counter
-
-    lerpPosHprNum = 1
-
-    def __init__(self, node, duration, pos, hpr, startPos=None,
-                startHpr=None, other=None, blendType='noBlend', name=None): 
-        """ __init__(node, duration, pos, hpr, startPos, startHpr,
-                                                other, blendType, name)
-        """
-        def functorFunc(node=node, pos=pos, hpr=hpr, 
-                        startPos=startPos, startHpr=startHpr, other=other):
-            assert(not node.isEmpty())
-            if callable(pos):
-                # This may be a thunk that returns a point.
-                pos = pos()
-            if callable(hpr):
-                # This may be a thunk that returns a point.
-                hpr = hpr()
-            # Make a our own copy of the parameters:
-            if (pos != None): pos=Point3(pos)
-            if (hpr != None): hpr=VBase3(hpr)
-            if (startPos != None): startPos=Point3(startPos)
-            if (startHpr != None): startHpr=VBase3(startHpr)
-            if (other != None):
-                # lerp wrt other
-                if (startPos == None):
-                    startPos = node.getPos(other)
-                if (startHpr == None):
-                    startHpr = node.getHpr(other)
-                functor = PosHprLerpFunctor(
-                    node, startPos, pos,
-                    startHpr, hpr, other)
-            else:
-                if (startPos == None):
-                    startPos = node.getPos()
-                if (startHpr == None):
-                    startHpr = node.getHpr()
-                functor = PosHprLerpFunctor(
-                    node, startPos, pos,
-                    startHpr, hpr)
-            return functor
-
-        # Generate unique name if necessary
-        if (name == None):
-            name = 'LerpPosHpr-%d' % LerpPosHprInterval.lerpPosHprNum
-            LerpPosHprInterval.lerpPosHprNum += 1
-        # Initialize superclass
-        LerpInterval.__init__(self, name, duration, functorFunc, blendType)
-
-class LerpHprScaleInterval(LerpInterval):
-    # Interval counter
-    lerpHprScaleNum = 1
-    # Class methods
-    def __init__(self, node, duration, hpr, scale,
-                 startHpr=None, startScale=None,
-                 other=None, blendType='noBlend', name=None): 
-        """ __init__(node, duration, hpr, scale,
-                     startHpr, startScale, 
-                     other, blendType, name)
-        """
-        def functorFunc(node=node, hpr=hpr, scale=scale,
-                        startHpr=startHpr,
-                        startScale=startScale, other=other):
-            assert(not node.isEmpty())
-            if callable(hpr):
-                # This may be a thunk that returns a point.
-                hpr = hpr()
-            if callable(scale):
-                # This may be a thunk that returns a point.
-                scale = scale()
-            # Make a our own copy of the parameters:
-            if (hpr != None): hpr=VBase3(hpr)
-            if (scale != None): scale=VBase3(scale)
-            if (startHpr != None): startHpr=VBase3(startHpr)
-            if (startScale != None): startScale=VBase3(startScale)
-            if (other != None):
-                # lerp wrt other
-                if (startHpr == None):
-                    startHpr = node.getHpr(other)
-                if (startScale == None):
-                    startScale = node.getScale(other)
-                functor = HprScaleLerpFunctor(
-                    node, startHpr, hpr,
-                    startScale, scale, other)
-            else:
-                if (startHpr == None):
-                    startHpr = node.getHpr()
-                if (startScale == None):
-                    startScale = node.getScale()
-                functor = HprScaleLerpFunctor(
-                    node, startHpr, hpr, startScale, scale)
-            return functor
-
-        # Generate unique name if necessary
-        if (name == None):
-            name = ('LerpHprScale-%d' %
-                    LerpHprScaleInterval.lerpHprScaleNum)
-            LerpHprScaleInterval.lerpHprScaleNum += 1
-        # Initialize superclass
-        LerpInterval.__init__(self, name, duration, functorFunc, blendType)
-
-class LerpPosHprScaleInterval(LerpInterval):
-    # Interval counter
-    lerpPosHprScaleNum = 1
-    # Class methods
-    def __init__(self, node, duration, pos, hpr, scale,
-                 startPos=None, startHpr=None, startScale=None,
-                 other=None, blendType='noBlend', name=None): 
-        """ __init__(node, duration, pos, hpr, scale,
-                     startPos, startHpr, startScale, 
-                     other, blendType, name)
-        """
-        def functorFunc(node=node, pos=pos, hpr=hpr, scale=scale,
-                        startPos=startPos, startHpr=startHpr,
-                        startScale=startScale, other=other):
-            assert(not node.isEmpty())
-            if callable(pos):
-                # This may be a thunk that returns a point.
-                pos = pos()
-            if callable(hpr):
-                # This may be a thunk that returns a point.
-                hpr = hpr()
-            if callable(scale):
-                # This may be a thunk that returns a point.
-                scale = scale()
-            # Make a our own copy of the parameters:
-            if (pos != None): pos=Point3(pos)
-            if (hpr != None): hpr=VBase3(hpr)
-            if (scale != None): scale=VBase3(scale)
-            if (startPos != None): startPos=Point3(startPos)
-            if (startHpr != None): startHpr=VBase3(startHpr)
-            if (startScale != None): startScale=VBase3(startScale)
-            if (other != None):
-                # lerp wrt other
-                if (startPos == None):
-                    startPos = node.getPos(other)
-                if (startHpr == None):
-                    startHpr = node.getHpr(other)
-                if (startScale == None):
-                    startScale = node.getScale(other)
-                functor = PosHprScaleLerpFunctor(
-                    node, startPos, pos, startHpr, hpr,
-                    startScale, scale, other)
-            else:
-                if (startPos == None):
-                    startPos = node.getPos()
-                if (startHpr == None):
-                    startHpr = node.getHpr()
-                if (startScale == None):
-                    startScale = node.getScale()
-                functor = PosHprScaleLerpFunctor(
-                    node, startPos, pos, startHpr, hpr, startScale, scale)
-            return functor
-
-        # Generate unique name if necessary
-        if (name == None):
-            name = ('LerpPosHprScale-%d' %
-                    LerpPosHprScaleInterval.lerpPosHprScaleNum)
-            LerpPosHprScaleInterval.lerpPosHprScaleNum += 1
-        # Initialize superclass
-        LerpInterval.__init__(self, name, duration, functorFunc, blendType)
-
-
-class LerpColorScaleInterval(LerpInterval):
-    # Name counter
-    lerpColorScaleNum = 1
-    # Class methods
-    def __init__(self, node, duration, startColor, endColor,
-                 other=None, blendType='noBlend', name=None):
-
-        def functorFunc(node=node, startColor=startColor, endColor=endColor, other=other):
-            assert(not node.isEmpty())
-            if callable(endColor):
-                # This may be a thunk that returns a point.
-                endColor = endColor()
-            if callable(startColor):
-                # This may be a thunk that returns a point.
-                startColor = startColor()
-            # Make a our own copy of the parameters:
-            if (startColor != None): startColor=VBase4(startColor)
-            if (endColor != None): endColor=VBase4(endColor)
-            if (other != None):
-                functor = ColorScaleLerpFunctor(node, startColor, endColor, other)
-            else:
-                functor = ColorScaleLerpFunctor(node, startColor, endColor)
-            return functor
-
-        # Generate unique name if necessary
-        if (name == None):
-            name = 'LerpColorScaleInterval-%d' % LerpColorScaleInterval.lerpColorScaleNum
-            LerpColorScaleInterval.lerpColorScaleNum += 1
-        # Initialize superclass
-        LerpInterval.__init__(self, name, duration, functorFunc, blendType)
-
-
-
-class LerpColorInterval(LerpInterval):
-    # Name counter
-    lerpColorNum = 1
-    # Class methods
-    def __init__(self, node, duration, startColor, endColor,
-                 other=None, blendType='noBlend', name=None):
-
-        def functorFunc(node=node, startColor=startColor,
-                        endColor=endColor, other=other):
-            assert(not node.isEmpty())
-            if callable(endColor):
-                # This may be a thunk that returns a point.
-                endColor = endColor()
-            if callable(startColor):
-                # This may be a thunk that returns a point.
-                startColor = startColor()
-            # Make a our own copy of the parameters:
-            if (startColor != None): startColor=VBase4(startColor)
-            if (endColor != None): endColor=VBase4(endColor)
-            if (other != None):
-                functor = ColorLerpFunctor(node, startColor, endColor, other)
-            else:
-                functor = ColorLerpFunctor(node, startColor, endColor)
-            return functor
-
-        # Generate unique name if necessary
-        if (name == None):
-            name = 'LerpColorInterval-%d' % LerpColorInterval.lerpColorNum
-            LerpColorInterval.lerpColorNum += 1
-        # Initialize superclass
-        LerpInterval.__init__(self, name, duration, functorFunc, blendType)
-
-
 
 class LerpFunctionInterval(Interval.Interval):
     """

+ 0 - 74
direct/src/interval/MultiTrack.py

@@ -1,74 +0,0 @@
-"""MultiTrack module: contains the MultiTrack class"""
-
-import Interval
-
-
-class MultiTrack(Interval.Interval):
-    # Name counter
-    multiTrackNum = 1
-    # Class methods
-    def __init__(self, trackList, name=None):
-        """__init__(trackList, name)
-        """
-        # Record track list
-        self.tlist = trackList
-        # Generate name if necessary
-        if (name == None):
-            name = 'MultiTrack-%d' % MultiTrack.multiTrackNum
-            MultiTrack.multiTrackNum = MultiTrack.multiTrackNum + 1
-        # Duration is max of all track durations
-        duration = self.__computeDuration()
-        # Initialize superclass
-        Interval.Interval.__init__(self, name, duration)
-        # Update stopEventList after initialization
-        # It is the union of the stopEventLists of all tracks in the MultiTrack
-        for t in self.tlist:
-            self.stopEventList = self.stopEventList + t.stopEventList
-
-    # Access track at given index
-    def __getitem__(self, item):
-        return self.tlist[item]
-    
-    def __computeDuration(self):
-        """ __computeDuration()
-            Returns the duration of the longest Track 
-        """
-        duration = 0.0
-        for t in self.tlist:
-            dur = t.getDuration()
-            if (dur > duration):
-                duration = dur
-        return duration
-
-    def updateFunc(self, t, event = Interval.IVAL_NONE):
-        """ updateFunc(t, event)
-            Go to time t
-        """
-        
-        for track in self.tlist:
-            # We used to try to be smart about calling this only in
-            # certain cases, but in fact we just called it in every
-            # case anyway, so might as well eliminate all of the
-            # comparisons.
-            track.setT(t, event)
-
-    # Print out representation of MultiTrack
-    def __repr__(self, indent=0):
-        """ __repr__(indent)
-        """
-        str = Interval.Interval.__repr__(self, indent) + '\n'
-        for t in self.tlist:
-            str = str + t.__repr__(indent+1)
-        return str
-
-
-
-
-
-
-
-
-
-class Parallel(MultiTrack):
-    def __init__(self, *tracks, **kw):
-        MultiTrack.__init__(self, tracks, **kw)

+ 20 - 0
direct/src/interval/Sources.pp

@@ -0,0 +1,20 @@
+#begin lib_target
+  #define TARGET interval
+  #define LOCAL_LIBS \
+    directbase
+  #define OTHER_LIBS \
+    pgraph:c putil:c panda:m express:c pandaexpress:m dtoolconfig dtool
+
+  #define SOURCES \
+    config_interval.cxx config_interval.h \
+    cInterval.cxx cInterval.I cInterval.h \
+    cLerpInterval.cxx cLerpInterval.I cLerpInterval.h \
+    cLerpNodePathInterval.cxx cLerpNodePathInterval.I cLerpNodePathInterval.h \
+    cLerpAnimEffectInterval.cxx cLerpAnimEffectInterval.I cLerpAnimEffectInterval.h \
+    cMetaInterval.cxx cMetaInterval.I cMetaInterval.h \
+    showInterval.cxx showInterval.I showInterval.h \
+    hideInterval.cxx hideInterval.I hideInterval.h \
+    lerp_helpers.h
+
+  #define IGATESCAN all
+#end lib_target

+ 0 - 267
direct/src/interval/Track.py

@@ -1,267 +0,0 @@
-"""Track module: contains the Track class"""
-
-import Interval
-import types
-
-PREVIOUS_END = 1
-PREVIOUS_START = 2
-TRACK_START = 3
-
-IDATA_IVAL = 0
-IDATA_TIME = 1
-IDATA_TYPE = 2
-IDATA_START = 3
-IDATA_END = 4
-
-
-
-class Track(Interval.Interval):
-    # Name counter
-    trackNum = 1
-    # Class methods
-    def __init__(self, intervalList, name=None):
-        """__init__(intervalList, name)
-        intervalList: <Interval> | 
-                      '[' <delay>, 
-                          <Interval> 
-                          [ , PREVIOUS_END | PREVIOUS_START | TRACK_START ] ']'
-        """
-        # Record instance variables
-        self.currentInterval = None
-        # Build ilist (need to do this before computing duration)
-        self.__buildIlist(intervalList)
-        # Generate unique name if necessary
-        if (name == None):
-            name = 'Track-%d' % Track.trackNum
-            Track.trackNum = Track.trackNum + 1
-        # Compute duration
-        duration = self.__computeDuration()
-        # Initialize superclass
-        Interval.Interval.__init__(self, name, duration)
-        # Update stopEventList
-        for i in self.ilist:
-            self.stopEventList = self.stopEventList + i[0].stopEventList
-
-    # Access interval at given index
-    def __getitem__(self, item):
-        return self.ilist[item]
-
-    # Create a list of this track's intervals, recording time
-    # and time type (relative to track start, previous start, or previous end)
-    def __buildIlist(self, intervalList):
-        self.ilist = []
-        for i in intervalList:
-            if isinstance(i, Interval.Interval):
-                self.ilist.append([i, 0.0, PREVIOUS_END, 0.0, 0.0])
-            elif (isinstance(i, types.ListType) or
-                  isinstance(i, types.TupleType)):
-                itime = i[0]
-                ival = i[1]
-                try:
-                    type = i[2]
-                except IndexError:
-                    type = TRACK_START
-                self.ilist.append([ival, itime, type, 0.0, 0.0])
-            else:
-                print 'Track.__buildIlist: Invalid intervallist entry'
-
-    # Compute duration of the track and precompute start and end time of
-    # each interval
-    def __computeDuration(self):
-        """ __computeDuration()
-        """
-        duration = 0.0
-        prev = None
-        for idata in self.ilist:
-            ival = idata[IDATA_IVAL]
-            itime = idata[IDATA_TIME]
-            type = idata[IDATA_TYPE]
-            assert(itime >= 0.0)
-            # Compute fill time, time between end of last interval and
-            # start of this one.  Depend on interval type
-            fillTime = itime 
-            if (type == PREVIOUS_END):
-                pass
-            elif (type == PREVIOUS_START):
-                if (prev != None):
-                    fillTime = itime - prev.getDuration()
-            elif (type == TRACK_START):
-                fillTime = itime - duration
-            else:
-                self.notify.error(
-                        'Track.__computeDuration(): unknown type: %d' % type)
-            # Check for overlap
-            if (fillTime < 0.0):
-                self.notify.error(
-                        'Track.__computeDuration(): overlap detected')
-            # Compute start time of interval
-            idata[IDATA_START] = duration + fillTime
-            # Compute end time of interval
-            idata[IDATA_END] = idata[IDATA_START] + ival.getDuration()
-            # Keep track of cumulative duration
-            duration = idata[IDATA_END]
-            prev = ival
-        return duration
-
-    def setIntervalStartTime(self, name, itime, type=TRACK_START):
-        """ setIntervalStartTime(name, itime, type)
-        """
-        found = 0
-        # Check for interval in current interval list
-        for idata in self.ilist:
-            # If found, update time and type
-            if (idata[IDATA_IVAL].getName() == name):
-                idata[IDATA_TIME] = itime
-                idata[IDATA_TYPE] = type
-                found = 1
-                break
-        if (found):
-            # And recompute duration
-            self.duration = self.__computeDuration()    
-        else:
-            self.notify.warning(
-                'Track.setIntervalStartTime(): no Interval named: %s' % name)
-
-    def getIntervalStartTime(self, name):
-        """ getIntervalStartTime(name)
-        """
-        # Search for interval of given name
-        for idata in self.ilist:
-            if (idata[IDATA_IVAL].getName() == name):
-                return idata[IDATA_START]
-        self.notify.warning(
-                'Track.getIntervalStartTime(): no Interval named: %s' % name)
-        return None
-
-    def __getIntervalStartTime(self, interval):
-        """ __getIntervalStartTime(interval)
-        """
-        # Search for given interval
-        for idata in self.ilist:
-            if (idata[IDATA_IVAL] == interval):
-                return idata[IDATA_START]
-        self.notify.warning(
-                'Track.getIntervalStartTime(): Interval not found')
-        return None
-
-    def getIntervalEndTime(self, name):
-        """ getIntervalEndTime(name)
-        """
-        # Search for interval of given name
-        for idata in self.ilist:
-            if (idata[IDATA_IVAL].getName() == name):   
-                return idata[IDATA_END]
-        self.notify.warning(
-                'Track.getIntervalEndTime(): no Interval named: %s' % name)
-        return None
-
-    def updateFunc(self, t, event = Interval.IVAL_NONE):
-        """ updateFunc(t, event)
-            Go to time t
-        """
-        # Deterimine which interval, if any to evaluate
-        if (self.currentInterval != None and
-            event == Interval.IVAL_NONE and
-            t > self.currentStart and t < self.currentEnd):
-            # If nothing interesting happened--we're still somewhere
-            # within the same interval we were within last time--just
-            # run that one.  Trivial case.
-            self.currentInterval.setT(t - self.currentStart, event)
-            
-        elif (t < 0):
-            # Before start of track, do nothing
-            pass
-        
-        else:
-            # The more sophisticated, look-for-the-proper-interval case.
-            
-            # Make sure track actually contains some intervals
-            if not self.ilist:
-                self.notify.warning(
-                    'Track.updateFunc(): track has no intervals')
-                return
-
-            # Initialize local variables
-            currentInterval = None
-            currentStart = 0.0
-            currentEnd = 0.0
-            # First entry, re-init instance variables
-            if (event == Interval.IVAL_INIT):
-                # Initialize prev_t to 0.0
-                self.prev_t = 0.0
-                # Clear record of currentInterval
-                self.currentInterval = None
-            # Compare t with start and end of each interval to determine
-            # which interval(s) to execute.
-            # If t falls between the start and end of an interval, that
-            # becomes the current interval.  If we've crossed over the end
-            # of an interval ((prev_t < tEnd) and (t > tEnd)) then execute
-            # that interval at its final value.  If we've crossed over the
-            # start of an interval ((prev_t > tStart) and (t < tStart))
-            # then execute that interval at its start value
-            for ival, itime, itype, tStart, tEnd in self.ilist:
-                # Compare time with each ival's start/end times
-                if (t < tStart):
-                    if (event == Interval.IVAL_DONE):
-                        # This should only happen in cases of floating
-                        # point instability where t is very close to
-                        # but less than tStart
-                        ival.setT(ival.getDuration(), event)
-                    elif (self.prev_t > tStart):
-                        # We just crossed the start of this interval
-                        # going backwards (e.g. via the slider)
-                        # Execute this interval at its start time
-                        ival.setT(0.0, event)
-                    # Done checking intervals
-                    break
-                elif (t >= tStart) and (t <= tEnd):
-                    # Between start/end, record current interval
-                    # Make sure event == Interval.IVAL_INIT if entering new interval
-                    if ((event == Interval.IVAL_NONE) and
-                        ((self.prev_t < tStart) or
-                          (ival != self.currentInterval))):
-                        event = Interval.IVAL_INIT
-                    # Evaluate interval at interval relative time
-                    if (event == Interval.IVAL_DONE):
-                        ival.setT(ival.getDuration(), event)
-                    else:
-                        ival.setT(t - tStart, event)
-                    currentInterval = ival
-                    currentStart = tStart
-                    currentEnd = tEnd
-                elif (t > tEnd):
-                    # Crossing over interval end 
-                    if (((event == Interval.IVAL_NONE) or (event == Interval.IVAL_DONE)) and
-                        (self.prev_t < tEnd)):
-                        # We've just crossed the end of this interval,
-                        # execute the interval at its end time
-                        # and flag event as Interval.IVAL_DONE
-                        ival.setT(ival.getDuration(), Interval.IVAL_DONE)
-                    elif ((event == Interval.IVAL_INIT) and ival.getOpenEnded()):
-                        # or its an INIT event after the interval's end
-                        # and the interval is openended,
-                        # then execute the interval at its end time
-                        ival.setT(ival.getDuration(), Interval.IVAL_INIT)
-                    # May not be the last, keep checking other intervals
-            # Record current interval (may be None)
-            self.currentInterval = currentInterval
-            self.currentStart = currentStart
-            self.currentEnd = currentEnd
-
-    # Create a printable representation of the track
-    def __repr__(self, indent=0):
-        """ __repr__(indent)
-        """
-        str = Interval.Interval.__repr__(self, indent) + '\n'
-        for idata in self.ilist:
-            # Tack on start and end time for this interval
-            str = (str + idata[IDATA_IVAL].__repr__(indent+1) +
-                   (' start: %0.2f end: %0.2f' %
-                    (idata[IDATA_START], idata[IDATA_END])) + '\n'
-                   )
-        return str
-
-
-class Sequence(Track):
-    def __init__(self, *intervals, **kw):
-        Track.__init__(self, intervals, **kw)

+ 127 - 0
direct/src/interval/cInterval.I

@@ -0,0 +1,127 @@
+// Filename: cInterval.I
+// Created by:  drose (27Aug02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CInterval::get_name
+//       Access: Published
+//  Description: Returns the interval's name.
+////////////////////////////////////////////////////////////////////
+INLINE const string &CInterval::
+get_name() const {
+  return _name;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CInterval::get_duration
+//       Access: Published
+//  Description: Returns the duration of the interval in seconds.
+////////////////////////////////////////////////////////////////////
+INLINE double CInterval::
+get_duration() const {
+  recompute();
+  return _duration;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CInterval::get_open_ended
+//       Access: Published
+//  Description: Returns the state of the "open_ended" flag.  This is
+//               primarily intended for instantaneous intervals like
+//               FunctionIntervals; it indicates true if the interval
+//               has some lasting effect that should be applied even
+//               if the interval doesn't get started until after its
+//               finish time, or false if the interval is a transitive
+//               thing that doesn't need to be called late.
+////////////////////////////////////////////////////////////////////
+INLINE bool CInterval::
+get_open_ended() const {
+  return _open_ended;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CInterval::set_t
+//       Access: Published
+//  Description: Advances the interval.  This function is provided
+//               mainly to provide compatibility with the Python
+//               Interval class; you should use initialize(), step(),
+//               and finalize() instead.
+////////////////////////////////////////////////////////////////////
+INLINE void CInterval::
+set_t(double t, CInterval::EventType event) {
+  switch (event) {
+  case ET_initialize:
+    initialize(t);
+    break;
+
+  case ET_instant:
+    instant();
+    break;
+
+  case ET_step:
+    step(t);
+    break;
+
+  case ET_finalize:
+    finalize();
+    break;
+
+  case ET_reverse_initialize:
+    reverse_initialize(t);
+    break;
+
+  case ET_reverse_instant:
+    reverse_instant();
+    break;
+
+  case ET_reverse_finalize:
+    reverse_finalize();
+    break;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CInterval::get_t
+//       Access: Published
+//  Description: Returns the current time of the interval: the last
+//               value of t passed to initialize(), step(), or
+//               finalize() (or set_t()).
+////////////////////////////////////////////////////////////////////
+INLINE double CInterval::
+get_t() const {
+  return _curr_t;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CInterval::recompute
+//       Access: Protected
+//  Description: Calls do_recompute() if the dirty flag has been set.
+////////////////////////////////////////////////////////////////////
+INLINE void CInterval::
+recompute() const {
+  if (_dirty) {
+    ((CInterval *)this)->do_recompute();
+  }
+}
+
+INLINE ostream &
+operator << (ostream &out, const CInterval &ival) {
+  ival.output(out);
+  return out;
+}
+

+ 362 - 0
direct/src/interval/cInterval.cxx

@@ -0,0 +1,362 @@
+// Filename: cInterval.cxx
+// Created by:  drose (27Aug02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "cInterval.h"
+#include "indent.h"
+#include "clockObject.h"
+
+TypeHandle CInterval::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: CInterval::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+CInterval::
+CInterval(const string &name, double duration, bool open_ended) :
+  _curr_t(0.0),
+  _name(name),
+  _duration(duration),
+  _open_ended(open_ended),
+  _dirty(false)
+{
+  _clock_start = 0.0;
+  _start_t = 0.0;
+  _end_t = 0.0;
+  _start_t_at_start = false;
+  _end_t_at_end = false;
+  _play_rate = 1.0;
+  _loop_count = 0;
+  _restart = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CInterval::setup_play
+//       Access: Published
+//  Description: Called to prepare the interval for automatic timed
+//               playback, e.g. via a Python task.  The interval will
+//               be played from start_t to end_t, at a time factor
+//               specified by play_rate.  start_t must always be less
+//               than end_t (except for the exception for end_t == -1,
+//               below), but if play_rate is negative the interval
+//               will be played backwards.
+//
+//               Specify end_t of -1 to play the entire interval from
+//               start_t.
+//
+//               Call step_play() repeatedly to execute the interval.
+////////////////////////////////////////////////////////////////////
+void CInterval::
+setup_play(double start_t, double end_t, double play_rate) {
+  nassertv(start_t < end_t || end_t < 0.0);
+  nassertv(play_rate != 0.0);
+
+  double duration = get_duration();
+
+  if (start_t < 0.0) {
+    _start_t = 0.0;
+    _start_t_at_start = true;
+  } else if (start_t > duration) {
+    _start_t = duration;
+    _start_t_at_start = false;
+  } else {
+    _start_t = start_t;
+    _start_t_at_start = false;
+  }
+  if (end_t < 0.0 || end_t >= duration) {
+    _end_t = duration;
+    _end_t_at_end = true;
+  } else {
+    _end_t = end_t;
+    _end_t_at_end = false;
+  }
+
+  _clock_start = ClockObject::get_global_clock()->get_frame_time();
+  _play_rate = play_rate;
+  _loop_count = 0;
+  _restart = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CInterval::step_play
+//       Access: Published
+//  Description: Should be called once per frame to execute the
+//               automatic timed playback begun with setup_play().
+//               The return value is the number of times the interval
+//               is about to repeat; stop when this reaches one to
+//               play the interval through exactly once.
+////////////////////////////////////////////////////////////////////
+int CInterval::
+step_play() {
+  double now = ClockObject::get_global_clock()->get_frame_time();
+
+  if (_play_rate >= 0.0) {
+    double t = (now - _clock_start) * _play_rate + _start_t;
+    
+    if (t < _end_t) {
+      // In the middle of the interval, not a problem.
+      if (_restart) {
+        initialize(t);
+        _restart = false;
+      } else {
+        step(t);
+      }
+      
+    } else {
+      // Past the ending point; time to finalize.
+      if (_end_t_at_end) {
+        // Only finalize if the playback cycle includes the whole
+        // interval.
+        if (_restart) {
+          if (get_open_ended() || _loop_count != 0) {
+            instant();
+          }
+        } else {
+          finalize();
+          _restart = true;
+        }
+      } else {
+        if (_restart) {
+          initialize(_end_t);
+          _restart = false;
+        } else {
+          step(_end_t);
+        }
+      }
+      
+      // Advance the clock for the next loop cycle.  We might have to
+      // advance multiple times if we skipped several cycles in the past
+      // frame.
+      
+      if (_end_t == _start_t) {
+        // If the interval has no length, we loop exactly once each
+        // time.
+        _loop_count++;
+        
+      } else {
+        // Otherwise, figure out how many loops we need to skip.
+        double time_per_loop = (_end_t - _start_t) / _play_rate;
+        double num_loops = floor((now - _clock_start) / time_per_loop);
+        _loop_count += (int)num_loops;
+        _clock_start += num_loops * time_per_loop;
+      }
+    }
+
+  } else {
+    // Playing backwards.
+    double t = (now - _clock_start) * _play_rate + _end_t;
+    
+    if (t >= _start_t) {
+      // In the middle of the interval, not a problem.
+      if (_restart) {
+        reverse_initialize(t);
+        _restart = false;
+      } else {
+        step(t);
+      }
+      
+    } else {
+      // Past the ending point; time to finalize.
+      if (_start_t_at_start) {
+        // Only finalize if the playback cycle includes the whole
+        // interval.
+        if (_restart) {
+          if (get_open_ended() || _loop_count != 0) {
+            reverse_instant();
+          }
+        } else {
+          reverse_finalize();
+          _restart = true;
+        }
+      } else {
+        if (_restart) {
+          reverse_initialize(_start_t);
+          _restart = false;
+        } else {
+          step(_start_t);
+        }
+      }
+      
+      // Advance the clock for the next loop cycle.  We might have to
+      // advance multiple times if we skipped several cycles in the past
+      // frame.
+      
+      if (_end_t == _start_t) {
+        // If the interval has no length, we loop exactly once each
+        // time.
+        _loop_count++;
+        
+      } else {
+        // Otherwise, figure out how many loops we need to skip.
+        double time_per_loop = (_end_t - _start_t) / -_play_rate;
+        double num_loops = floor((now - _clock_start) / time_per_loop);
+        _loop_count += (int)num_loops;
+        _clock_start += num_loops * time_per_loop;
+      }
+    }
+  }
+
+  return _loop_count;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CInterval::initialize
+//       Access: Published, Virtual
+//  Description: This replaces the first call to step(), and indicates
+//               that the interval has just begun.  This may be
+//               overridden by derived classes that need to do some
+//               explicit initialization on the first call.
+////////////////////////////////////////////////////////////////////
+void CInterval::
+initialize(double t) {
+  recompute();
+  step(t);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CInterval::instant
+//       Access: Published, Virtual
+//  Description: This is called in lieu of initialize() .. step()
+//               .. finalize(), when everything is to happen within
+//               one frame.  The interval should initialize itself,
+//               then leave itself in the final state.
+////////////////////////////////////////////////////////////////////
+void CInterval::
+instant() {
+  recompute();
+  step(get_duration());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CInterval::step
+//       Access: Published, Virtual
+//  Description: Advances the time on the interval.  The time may
+//               either increase (the normal case) or decrease
+//               (e.g. if the interval is being played by a slider).
+////////////////////////////////////////////////////////////////////
+void CInterval::
+step(double t) {
+  _curr_t = t;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CInterval::finalize
+//       Access: Published, Virtual
+//  Description: This is called to stop an interval, forcing it to
+//               whatever state it would be after it played all the
+//               way through.  It's generally invoked by
+//               set_final_t().
+////////////////////////////////////////////////////////////////////
+void CInterval::
+finalize() {
+  step(get_duration());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CInterval::reverse_initialize
+//       Access: Published, Virtual
+//  Description: Similar to initialize(), but this is called when the
+//               interval is being played backwards; it indicates that
+//               the interval should start at the finishing state and
+//               undo any intervening intervals.
+////////////////////////////////////////////////////////////////////
+void CInterval::
+reverse_initialize(double t) {
+  recompute();
+  step(t);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CInterval::reverse_instant
+//       Access: Published, Virtual
+//  Description: This is called in lieu of reverse_initialize()
+//               .. step() .. reverse_finalize(), when everything is
+//               to happen within one frame.  The interval should
+//               initialize itself, then leave itself in the initial
+//               state.
+////////////////////////////////////////////////////////////////////
+void CInterval::
+reverse_instant() {
+  recompute();
+  step(0.0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CInterval::reverse_finalize
+//       Access: Published, Virtual
+//  Description: Called generally following a reverse_initialize(),
+//               this indicates the interval should set itself to the
+//               initial state.
+////////////////////////////////////////////////////////////////////
+void CInterval::
+reverse_finalize() {
+  step(0.0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CInterval::output
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void CInterval::
+output(ostream &out) const {
+  out << get_name();
+  if (get_duration() != 0.0) {
+    out << " dur " << get_duration();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CInterval::write
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void CInterval::
+write(ostream &out, int indent_level) const {
+  indent(out, indent_level) << *this << "\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CInterval::mark_dirty
+//       Access: Public
+//  Description: Called by a derived class to indicate the interval has
+//               been changed internally and must be recomputed before
+//               its duration may be returned.
+////////////////////////////////////////////////////////////////////
+void CInterval::
+mark_dirty() {
+  if (!_dirty) {
+    _dirty = true;
+    Parents::iterator pi;
+    for (pi = _parents.begin(); pi != _parents.end(); ++pi) {
+      (*pi)->mark_dirty();
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CInterval::do_recompute
+//       Access: Protected, Virtual
+//  Description: Does whatever processing is necessary to recompute
+//               the interval after a call to mark_dirty() has
+//               indicated a recomputation is necessary.
+////////////////////////////////////////////////////////////////////
+void CInterval::
+do_recompute() {
+  _dirty = false;
+}

+ 135 - 0
direct/src/interval/cInterval.h

@@ -0,0 +1,135 @@
+// Filename: cInterval.h
+// Created by:  drose (27Aug02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CINTERVAL_H
+#define CINTERVAL_H
+
+#include "directbase.h"
+#include "typedReferenceCount.h"
+#include "pvector.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : CInterval
+// Description : The base class for timeline components.  A CInterval
+//               represents a single action, event, or collection of
+//               nested intervals that will be performed at some
+//               specific time or over a period of time.
+//
+//               This is essentially similar to the Python "Interval"
+//               class, but it is implemented in C++ (hence the name).
+//               Intervals that may be implemented in C++ will inherit
+//               from this class; Intervals that must be implemented
+//               in Python will inherit from the similar Python class.
+////////////////////////////////////////////////////////////////////
+class EXPCL_DIRECT CInterval : public TypedReferenceCount {
+public:
+  CInterval(const string &name, double duration, bool open_ended);
+
+PUBLISHED:
+  INLINE const string &get_name() const;
+  INLINE double get_duration() const;
+  INLINE bool get_open_ended() const;
+
+  enum EventType {
+    ET_initialize,
+    ET_instant,
+    ET_step,
+    ET_finalize,
+    ET_reverse_initialize,
+    ET_reverse_instant,
+    ET_reverse_finalize
+  };
+
+  INLINE void set_t(double t, EventType event = ET_step);
+  INLINE double get_t() const;
+
+  void setup_play(double start_time, double end_time, double play_rate);
+  int step_play();
+
+  // These functions control the actual playback of the interval.
+  virtual void initialize(double t);
+  virtual void instant();
+  virtual void step(double t);
+  virtual void finalize();
+  virtual void reverse_initialize(double t);
+  virtual void reverse_instant();
+  virtual void reverse_finalize();
+
+  virtual void output(ostream &out) const;
+  virtual void write(ostream &out, int indent_level) const;
+
+public:
+  void mark_dirty();
+
+protected:
+  INLINE void recompute() const;
+  virtual void do_recompute();
+
+  double _curr_t;
+  string _name;
+  double _duration;
+
+  // For setup_play() and step_play().
+  double _clock_start;
+  double _start_t;
+  double _end_t;
+  bool _end_t_at_end;
+  bool _start_t_at_start;
+  double _play_rate;
+  int _loop_count;
+  bool _restart;
+
+private:
+  bool _open_ended;
+  bool _dirty;
+
+  // We keep a record of the "parent" intervals (that is, any
+  // CMetaInterval objects that keep a pointer to this one) strictly
+  // so we can mark all of our parents dirty when this interval gets
+  // dirty.
+  typedef pvector<CInterval *> Parents;
+  Parents _parents;
+  
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    TypedReferenceCount::init_type();
+    register_type(_type_handle, "CInterval",
+                  TypedReferenceCount::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+
+  friend class CMetaInterval;
+};
+
+INLINE ostream &operator << (ostream &out, const CInterval &ival);
+
+#include "cInterval.I"
+
+#endif
+
+
+

+ 62 - 0
direct/src/interval/cLerpAnimEffectInterval.I

@@ -0,0 +1,62 @@
+// Filename: cLerpAnimEffectInterval.I
+// Created by:  drose (27Aug02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLerpAnimEffectInterval::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CLerpAnimEffectInterval::
+CLerpAnimEffectInterval(const string &name, double duration, 
+                        CLerpInterval::BlendType blend_type) :
+  CLerpInterval(name, duration, blend_type)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLerpAnimEffectInterval::add_control
+//       Access: Published
+//  Description: Adds another AnimControl to the list of AnimControls
+//               affected by the lerp.  This control will be lerped
+//               from begin_effect to end_effect over the period of
+//               the lerp.
+//
+//               The AnimControl name parameter is only used when
+//               formatting the interval for output.
+////////////////////////////////////////////////////////////////////
+INLINE void CLerpAnimEffectInterval::
+add_control(AnimControl *control, const string &name,
+            float begin_effect, float end_effect) {
+  _controls.push_back(ControlDef(control, name, begin_effect, end_effect));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLerpAnimEffectInterval::ControlDef::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CLerpAnimEffectInterval::ControlDef::
+ControlDef(AnimControl *control, const string &name,
+           float begin_effect, float end_effect) :
+  _control(control),
+  _name(name),
+  _begin_effect(begin_effect),
+  _end_effect(end_effect)
+{
+}

+ 70 - 0
direct/src/interval/cLerpAnimEffectInterval.cxx

@@ -0,0 +1,70 @@
+// Filename: cLerpAnimEffectInterval.cxx
+// Created by:  drose (27Aug02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "cLerpAnimEffectInterval.h"
+#include "lerp_helpers.h"
+#include "partBundle.h"
+
+TypeHandle CLerpAnimEffectInterval::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLerpAnimEffectInterval::step
+//       Access: Published, Virtual
+//  Description: Advances the time on the interval.  The time may
+//               either increase (the normal case) or decrease
+//               (e.g. if the interval is being played by a slider).
+////////////////////////////////////////////////////////////////////
+void CLerpAnimEffectInterval::
+step(double t) {
+  double d = compute_delta(t);
+
+  Controls::iterator ci;
+  for (ci = _controls.begin(); ci != _controls.end(); ++ci) {
+    ControlDef &def = (*ci);
+    float effect;
+    lerp_value(effect, d, def._begin_effect, def._end_effect);
+    def._control->get_part()->set_control_effect(def._control, effect);
+  }
+
+  _curr_t = t;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLerpAnimEffectInterval::output
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void CLerpAnimEffectInterval::
+output(ostream &out) const {
+  out << get_name() << ": ";
+
+  if (_controls.empty()) {
+    out << "(no controls)";
+  } else {
+    Controls::const_iterator ci;
+    ci = _controls.begin();
+    out << (*ci)._name;
+    ++ci;
+    while (ci != _controls.end()) {
+      out << ", " << (*ci)._name;
+      ++ci;
+    }
+  }
+
+  out << " dur " << get_duration();
+}

+ 87 - 0
direct/src/interval/cLerpAnimEffectInterval.h

@@ -0,0 +1,87 @@
+// Filename: cLerpAnimEffectInterval.h
+// Created by:  drose (27Aug02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CLERPANIMEFFECTINTERVAL_H
+#define CLERPANIMEFFECTINTERVAL_H
+
+#include "directbase.h"
+#include "cLerpInterval.h"
+#include "animControl.h"
+#include "pointerTo.h"
+#include "pvector.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : CLerpAnimEffectInterval
+// Description : This interval lerps between different amounts of
+//               control effects for various AnimControls that might
+//               be playing on an actor.  It's used to change the
+//               blending amount between multiple animations.
+//
+//               The idea is to start all the animations playing
+//               first, then use a CLerpAnimEffectInterval to adjust
+//               the degree to which each animation affects the actor.
+////////////////////////////////////////////////////////////////////
+class EXPCL_DIRECT CLerpAnimEffectInterval : public CLerpInterval {
+PUBLISHED:
+  INLINE CLerpAnimEffectInterval(const string &name, double duration, 
+                                 BlendType blend_type);
+
+  INLINE void add_control(AnimControl *control, const string &name,
+                          float begin_effect, float end_effect);
+
+  virtual void step(double t);
+
+  virtual void output(ostream &out) const;
+
+private:
+  class ControlDef {
+  public:
+    INLINE ControlDef(AnimControl *control, const string &name,
+                      float begin_effect, float end_effect);
+    PT(AnimControl) _control;
+    string _name;
+    float _begin_effect;
+    float _end_effect;
+  };
+    
+  typedef pvector<ControlDef> Controls;
+  Controls _controls;
+  
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    CLerpInterval::init_type();
+    register_type(_type_handle, "CLerpAnimEffectInterval",
+                  CLerpInterval::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "cLerpAnimEffectInterval.I"
+
+#endif
+

+ 43 - 0
direct/src/interval/cLerpInterval.I

@@ -0,0 +1,43 @@
+// Filename: cLerpInterval.I
+// Created by:  drose (27Aug02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLerpInterval::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CLerpInterval::
+CLerpInterval(const string &name, double duration, 
+              CLerpInterval::BlendType blend_type) :
+  CInterval(name, duration, true),
+  _blend_type(blend_type)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLerpInterval::get_blend_type
+//       Access: Published
+//  Description: Returns the blend type specified for the interval.
+//               This controls how the linear interpolation behaves
+//               near the beginning and end of the lerp period.
+////////////////////////////////////////////////////////////////////
+INLINE CLerpInterval::BlendType CLerpInterval::
+get_blend_type() const {
+  return _blend_type;
+}

+ 85 - 0
direct/src/interval/cLerpInterval.cxx

@@ -0,0 +1,85 @@
+// Filename: cLerpInterval.cxx
+// Created by:  drose (27Aug02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "cLerpInterval.h"
+#include "string_utils.h"
+
+TypeHandle CLerpInterval::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLerpInterval::string_blend_type
+//       Access: Published, Static
+//  Description: Returns the BlendType enumerated value corresponding
+//               to the indicated string, or BT_invalid if the string
+//               doesn't match anything.
+////////////////////////////////////////////////////////////////////
+CLerpInterval::BlendType CLerpInterval::
+string_blend_type(const string &blend_type) {
+  if (blend_type == "easeIn") {
+    return BT_ease_in;
+  } else if (blend_type == "easeOut") {
+    return BT_ease_out;
+  } else if (blend_type == "easeInOut") {
+    return BT_ease_in_out;
+  } else if (blend_type == "noBlend") {
+    return BT_no_blend;
+  } else {
+    return BT_invalid;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLerpInterval::compute_delta
+//       Access: Protected
+//  Description: Given a t value in the range [0, get_duration()],
+//               returns the corresponding delta value clamped to the
+//               range [0, 1], after scaling by duration and applying
+//               the blend type.
+////////////////////////////////////////////////////////////////////
+double CLerpInterval::
+compute_delta(double t) const {
+  double duration = get_duration();
+  if (duration == 0.0) {
+    return 0.0;
+  }
+  t /= duration;
+  t = min(max(t, 0.0), 1.0);
+
+  switch (_blend_type) {
+  case BT_ease_in:
+    {
+      double t2 = t * t;
+      return ((3.0 * t2) - (t2 * t)) * 0.5;
+    }
+
+  case BT_ease_out:
+    {
+      double t2 = t * t;
+      return ((3.0 * t2) - (t2 * t)) * 0.5;
+    }
+
+  case BT_ease_in_out:
+    {
+      double t2 = t * t;
+      return (3.0 * t2) - (2.0 * t * t2);
+    }
+
+  default:
+    return t;
+  }
+}

+ 78 - 0
direct/src/interval/cLerpInterval.h

@@ -0,0 +1,78 @@
+// Filename: cLerpInterval.h
+// Created by:  drose (27Aug02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CLERPINTERVAL_H
+#define CLERPINTERVAL_H
+
+#include "directbase.h"
+#include "cInterval.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : CLerpInterval
+// Description : The base class for a family of intervals that
+//               linearly interpolate one or more numeric values over
+//               time.
+////////////////////////////////////////////////////////////////////
+class EXPCL_DIRECT CLerpInterval : public CInterval {
+PUBLISHED:
+  enum BlendType {
+    BT_no_blend,
+    BT_ease_in,
+    BT_ease_out,
+    BT_ease_in_out,
+    BT_invalid
+  };
+
+public:
+  INLINE CLerpInterval(const string &name, double duration, 
+                       BlendType blend_type);
+
+PUBLISHED:
+  INLINE BlendType get_blend_type() const;
+
+  static BlendType string_blend_type(const string &blend_type);
+
+protected:
+  double compute_delta(double t) const;
+
+private:
+  BlendType _blend_type;
+
+  
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    CInterval::init_type();
+    register_type(_type_handle, "CLerpInterval",
+                  CInterval::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "cLerpInterval.I"
+
+#endif
+

+ 219 - 0
direct/src/interval/cLerpNodePathInterval.I

@@ -0,0 +1,219 @@
+// Filename: cLerpNodePathInterval.I
+// Created by:  drose (27Aug02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLerpNodePathInterval::get_node
+//       Access: Published
+//  Description: Returns the node being lerped.
+////////////////////////////////////////////////////////////////////
+INLINE const NodePath &CLerpNodePathInterval::
+get_node() const {
+  return _node;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLerpNodePathInterval::get_other
+//       Access: Published
+//  Description: Returns the "other" node, which the lerped node is
+//               being moved relative to.  If this is an empty node
+//               path, the lerped node is being moved in its own
+//               coordinate system.
+////////////////////////////////////////////////////////////////////
+INLINE const NodePath &CLerpNodePathInterval::
+get_other() const {
+  return _other;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLerpNodePathInterval::set_start_pos
+//       Access: Published
+//  Description: Indicates the initial position of the lerped node.
+//               This is meaningful only if set_end_pos() is also
+//               called.  This parameter is optional; if unspecified,
+//               the value will be taken from the node's actual
+//               position at the time the lerp is performed.
+////////////////////////////////////////////////////////////////////
+INLINE void CLerpNodePathInterval::
+set_start_pos(const LVecBase3f &pos) {
+  _start_pos = pos;
+  _flags |= F_start_pos;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLerpNodePathInterval::set_end_pos
+//       Access: Published
+//  Description: Indicates that the position of the node should be
+//               lerped, and specifies the final position of the node.
+//               This should be called before initialize().  If this
+//               is not called, the node's position will not be
+//               affected by the lerp.
+////////////////////////////////////////////////////////////////////
+INLINE void CLerpNodePathInterval::
+set_end_pos(const LVecBase3f &pos) {
+  _end_pos = pos;
+  _flags |= F_end_pos;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLerpNodePathInterval::set_start_hpr
+//       Access: Published
+//  Description: Indicates the initial rotation of the lerped node.
+//               This is meaningful only if set_end_hpr() is also
+//               called.  This parameter is optional; if unspecified,
+//               the value will be taken from the node's actual
+//               rotation at the time the lerp is performed.
+////////////////////////////////////////////////////////////////////
+INLINE void CLerpNodePathInterval::
+set_start_hpr(const LVecBase3f &hpr) {
+  _start_hpr = hpr;
+  _flags |= F_start_hpr;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLerpNodePathInterval::set_end_hpr
+//       Access: Published
+//  Description: Indicates that the rotation of the node should be
+//               lerped, and specifies the final rotation of the node.
+//               This should be called before initialize().  If this
+//               is not called, the node's rotation will not be
+//               affected by the lerp.
+////////////////////////////////////////////////////////////////////
+INLINE void CLerpNodePathInterval::
+set_end_hpr(const LVecBase3f &hpr) {
+  _end_hpr = hpr;
+  _flags |= F_end_hpr;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLerpNodePathInterval::set_start_scale
+//       Access: Published
+//  Description: Indicates the initial scale of the lerped node.
+//               This is meaningful only if set_end_scale() is also
+//               called.  This parameter is optional; if unspecified,
+//               the value will be taken from the node's actual
+//               scale at the time the lerp is performed.
+////////////////////////////////////////////////////////////////////
+INLINE void CLerpNodePathInterval::
+set_start_scale(const LVecBase3f &scale) {
+  _start_scale = scale;
+  _flags |= F_start_scale;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLerpNodePathInterval::set_start_scale
+//       Access: Published
+//  Description: Indicates the initial scale of the lerped node.
+//               This is meaningful only if set_end_scale() is also
+//               called.  This parameter is optional; if unspecified,
+//               the value will be taken from the node's actual
+//               scale at the time the lerp is performed.
+////////////////////////////////////////////////////////////////////
+INLINE void CLerpNodePathInterval::
+set_start_scale(float scale) {
+  set_start_scale(LVecBase3f(scale, scale, scale));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLerpNodePathInterval::set_end_scale
+//       Access: Published
+//  Description: Indicates that the scale of the node should be
+//               lerped, and specifies the final scale of the node.
+//               This should be called before initialize().  If this
+//               is not called, the node's scale will not be
+//               affected by the lerp.
+////////////////////////////////////////////////////////////////////
+INLINE void CLerpNodePathInterval::
+set_end_scale(const LVecBase3f &scale) {
+  _end_scale = scale;
+  _flags |= F_end_scale;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLerpNodePathInterval::set_end_scale
+//       Access: Published
+//  Description: Indicates that the scale of the node should be
+//               lerped, and specifies the final scale of the node.
+//               This should be called before initialize().  If this
+//               is not called, the node's scale will not be
+//               affected by the lerp.
+////////////////////////////////////////////////////////////////////
+INLINE void CLerpNodePathInterval::
+set_end_scale(float scale) {
+  set_end_scale(LVecBase3f(scale, scale, scale));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLerpNodePathInterval::set_start_color
+//       Access: Published
+//  Description: Indicates the initial color of the lerped node.
+//               This is meaningful only if set_end_color() is also
+//               called.  This parameter is optional; if unspecified,
+//               the value will be taken from the node's actual
+//               color at the time the lerp is performed.
+////////////////////////////////////////////////////////////////////
+INLINE void CLerpNodePathInterval::
+set_start_color(const LVecBase4f &color) {
+  _start_color = color;
+  _flags |= F_start_color;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLerpNodePathInterval::set_end_color
+//       Access: Published
+//  Description: Indicates that the color of the node should be
+//               lerped, and specifies the final color of the node.
+//               This should be called before initialize().  If this
+//               is not called, the node's color will not be
+//               affected by the lerp.
+////////////////////////////////////////////////////////////////////
+INLINE void CLerpNodePathInterval::
+set_end_color(const LVecBase4f &color) {
+  _end_color = color;
+  _flags |= F_end_color;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLerpNodePathInterval::set_start_color_scale
+//       Access: Published
+//  Description: Indicates the initial color scale of the lerped node.
+//               This is meaningful only if set_end_color_scale() is also
+//               called.  This parameter is optional; if unspecified,
+//               the value will be taken from the node's actual
+//               color scale at the time the lerp is performed.
+////////////////////////////////////////////////////////////////////
+INLINE void CLerpNodePathInterval::
+set_start_color_scale(const LVecBase4f &color_scale) {
+  _start_color_scale = color_scale;
+  _flags |= F_start_color_scale;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLerpNodePathInterval::set_end_color_scale
+//       Access: Published
+//  Description: Indicates that the color scale of the node should be
+//               lerped, and specifies the final color scale of the node.
+//               This should be called before initialize().  If this
+//               is not called, the node's color scale will not be
+//               affected by the lerp.
+////////////////////////////////////////////////////////////////////
+INLINE void CLerpNodePathInterval::
+set_end_color_scale(const LVecBase4f &color_scale) {
+  _end_color_scale = color_scale;
+  _flags |= F_end_color_scale;
+}

+ 351 - 0
direct/src/interval/cLerpNodePathInterval.cxx

@@ -0,0 +1,351 @@
+// Filename: cLerpNodePathInterval.cxx
+// Created by:  drose (27Aug02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "cLerpNodePathInterval.h"
+#include "lerp_helpers.h"
+#include "transformState.h"
+#include "renderState.h"
+#include "colorAttrib.h"
+#include "colorScaleAttrib.h"
+#include "dcast.h"
+
+TypeHandle CLerpNodePathInterval::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLerpNodePathInterval::Constructor
+//       Access: Published
+//  Description: Constructs a lerp interval that will lerp some
+//               properties on the indicated node, possibly relative
+//               to the indicated other node (if other is nonempty).
+//
+//               You must call set_end_pos(), etc. for the various
+//               properties you wish to lerp before the first call to
+//               initialize().  If you want to set a starting value
+//               for any of the properties, you may call
+//               set_start_pos(), etc.; otherwise, the starting value
+//               is taken from the actual node's value at the time the
+//               lerp is performed.
+////////////////////////////////////////////////////////////////////
+CLerpNodePathInterval::
+CLerpNodePathInterval(const string &name, double duration, 
+                      CLerpInterval::BlendType blend_type,
+                      const NodePath &node, const NodePath &other) :
+  CLerpInterval(name, duration, blend_type),
+  _node(node),
+  _other(other),
+  _flags(0)
+{
+  _prev_d = 0.0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLerpNodePathInterval::initialize
+//       Access: Published, Virtual
+//  Description: This replaces the first call to step(), and indicates
+//               that the interval has just begun.  This may be
+//               overridden by derived classes that need to do some
+//               explicit initialization on the first call.
+////////////////////////////////////////////////////////////////////
+void CLerpNodePathInterval::
+initialize(double t) {
+  recompute();
+  _prev_d = 0.0;
+  step(t);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLerpNodePathInterval::instant
+//       Access: Published, Virtual
+//  Description: This is called in lieu of initialize() .. step()
+//               .. finalize(), when everything is to happen within
+//               one frame.  The interval should initialize itself,
+//               then leave itself in the final state.
+////////////////////////////////////////////////////////////////////
+void CLerpNodePathInterval::
+instant() {
+  recompute();
+  _prev_d = 0.0;
+  step(get_duration());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLerpNodePathInterval::step
+//       Access: Published, Virtual
+//  Description: Advances the time on the interval.  The time may
+//               either increase (the normal case) or decrease
+//               (e.g. if the interval is being played by a slider).
+////////////////////////////////////////////////////////////////////
+void CLerpNodePathInterval::
+step(double t) {
+  double d = compute_delta(t);
+
+  if ((_flags & (F_end_pos | F_end_hpr | F_end_scale)) != 0) {
+    // We have some transform lerp.
+    CPT(TransformState) transform;
+
+    if (_other.is_empty()) {
+      // If there is no other node, it's a local transform lerp.
+      transform = _node.get_transform();
+    } else {
+      // If there *is* another node, we get the transform relative to
+      // that node.
+      transform = _node.get_transform(_other);
+    }
+    
+    LPoint3f pos;
+    LVecBase3f hpr;
+    LVecBase3f scale;
+
+    if ((_flags & F_end_pos) != 0) {
+      if ((_flags & F_start_pos) != 0) {
+        lerp_value(pos, d, _start_pos, _end_pos);
+
+      } else {
+        pos = transform->get_pos();
+        lerp_value_from_prev(pos, d, _prev_d, pos, _end_pos);
+      }
+    }
+    if ((_flags & F_end_hpr) != 0) {
+      if ((_flags & F_start_hpr) != 0) {
+        lerp_value(hpr, d, _start_hpr, _end_hpr);
+
+      } else {
+        hpr = transform->get_hpr();
+        lerp_value_from_prev(hpr, d, _prev_d, hpr, _end_hpr);
+      }
+    }
+    if ((_flags & F_end_scale) != 0) {
+      if ((_flags & F_start_scale) != 0) {
+        lerp_value(scale, d, _start_scale, _end_scale);
+
+      } else {
+        scale = transform->get_scale();
+        lerp_value_from_prev(scale, d, _prev_d, scale, _end_scale);
+      }
+    }
+
+    // Now apply the modifications back to the transform.  We want to
+    // be a little careful here, because we don't want to assume the
+    // transform has hpr/scale components if they're not needed.  And
+    // in any case, we only want to apply the components that we
+    // computed, above.
+    switch (_flags & (F_end_pos | F_end_hpr | F_end_scale)) {
+    case F_end_pos:
+      transform = transform->set_pos(pos);
+      break;
+
+    case F_end_hpr:
+      transform = transform->set_hpr(hpr);
+      break;
+
+    case F_end_scale:
+      transform = transform->set_scale(scale);
+      break;
+
+    case F_end_hpr | F_end_scale:
+      transform = TransformState::make_pos_hpr_scale(transform->get_pos(), hpr, scale);
+      break;
+
+    case F_end_pos | F_end_hpr:
+      transform = TransformState::make_pos_hpr_scale(pos, hpr, transform->get_scale());
+      break;
+
+    case F_end_pos | F_end_scale:
+      if (transform->quat_given()) {
+        transform = TransformState::make_pos_quat_scale(pos, transform->get_quat(), scale);
+      } else {
+        transform = TransformState::make_pos_hpr_scale(pos, transform->get_hpr(), scale);
+      }
+      break;
+
+    case F_end_pos | F_end_hpr | F_end_scale:
+      transform = TransformState::make_pos_hpr_scale(pos, hpr, scale);
+      break;
+
+    default:
+      interval_cat.error()
+        << "Internal error in CLerpNodePathInterval::step().\n";
+    }
+
+    // Now apply the new transform back to the node.
+    if (_other.is_empty()) {
+      _node.set_transform(transform);
+    } else {
+      _node.set_transform(_other, transform);
+    }
+  }
+
+  if ((_flags & (F_end_color | F_end_color_scale)) != 0) {
+    // We have some render state lerp.
+    CPT(RenderState) state;
+
+    if (_other.is_empty()) {
+      // If there is no other node, it's a local state lerp.  This is
+      // most common.
+      state = _node.get_state();
+    } else {
+      // If there *is* another node, we get the state relative to that
+      // node.  This is weird, but you could lerp color (for instance)
+      // relative to some other node's color.
+      state = _node.get_state(_other);
+    }
+    
+    // Unlike in the transform case above, we can go ahead and modify
+    // the state immediately with each attribute change, since these
+    // attributes don't interrelate.
+
+    if ((_flags & F_end_color) != 0) {
+      Colorf color;
+
+      if ((_flags & F_start_color) != 0) {
+        lerp_value(color, d, _start_color, _end_color);
+
+      } else {
+        // Get the previous color.
+        color.set(1.0f, 1.0f, 1.0f, 1.0f);
+        const RenderAttrib *attrib =
+          state->get_attrib(ColorAttrib::get_class_type());
+        if (attrib != (const RenderAttrib *)NULL) {
+          const ColorAttrib *ca = DCAST(ColorAttrib, attrib);
+          if (ca->get_color_type() == ColorAttrib::T_flat) {
+            color = ca->get_color();
+          }
+        }
+
+        lerp_value_from_prev(color, d, _prev_d, color, _end_color);
+      }
+
+      state = state->add_attrib(ColorAttrib::make_flat(color));
+    }
+
+    if ((_flags & F_end_color_scale) != 0) {
+      LVecBase4f color_scale;
+
+      if ((_flags & F_start_color_scale) != 0) {
+        lerp_value(color_scale, d, _start_color_scale, _end_color_scale);
+
+      } else {
+        // Get the previous color scale.
+        color_scale.set(1.0f, 1.0f, 1.0f, 1.0f);
+        const RenderAttrib *attrib =
+          state->get_attrib(ColorScaleAttrib::get_class_type());
+        if (attrib != (const RenderAttrib *)NULL) {
+          const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, attrib);
+          color_scale = csa->get_scale();
+        }
+
+        lerp_value_from_prev(color_scale, d, _prev_d, color_scale, _end_color_scale);
+      }
+
+      state = state->add_attrib(ColorScaleAttrib::make(color_scale));
+    }    
+
+    // Now apply the new state back to the node.
+    if (_other.is_empty()) {
+      _node.set_state(state);
+    } else {
+      _node.set_state(_other, state);
+    }
+  }
+
+  _prev_d = d;
+  _curr_t = t;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLerpNodePathInterval::reverse_initialize
+//       Access: Published, Virtual
+//  Description: Similar to initialize(), but this is called when the
+//               interval is being played backwards; it indicates that
+//               the interval should start at the finishing state and
+//               undo any intervening intervals.
+////////////////////////////////////////////////////////////////////
+void CLerpNodePathInterval::
+reverse_initialize(double t) {
+  recompute();
+  _prev_d = 1.0;
+  step(t);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLerpNodePathInterval::reverse_instant
+//       Access: Published, Virtual
+//  Description: This is called in lieu of reverse_initialize()
+//               .. step() .. reverse_finalize(), when everything is
+//               to happen within one frame.  The interval should
+//               initialize itself, then leave itself in the initial
+//               state.
+////////////////////////////////////////////////////////////////////
+void CLerpNodePathInterval::
+reverse_instant() {
+  recompute();
+  _prev_d = 1.0;
+  step(0.0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLerpNodePathInterval::output
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void CLerpNodePathInterval::
+output(ostream &out) const {
+  out << get_name() << ":";
+
+  if ((_flags & F_end_pos) != 0) {
+    out << " pos";
+    if ((_flags & F_start_pos) != 0) {
+      out << " from " << _start_pos;
+    }
+    out << " to " << _end_pos;
+  }
+
+  if ((_flags & F_end_hpr) != 0) {
+    out << " hpr";
+    if ((_flags & F_start_hpr) != 0) {
+      out << " from " << _start_hpr;
+    }
+    out << " to " << _end_hpr;
+  }
+
+  if ((_flags & F_end_scale) != 0) {
+    out << " scale";
+    if ((_flags & F_start_scale) != 0) {
+      out << " from " << _start_scale;
+    }
+    out << " to " << _end_scale;
+  }
+
+  if ((_flags & F_end_color) != 0) {
+    out << " color";
+    if ((_flags & F_start_color) != 0) {
+      out << " from " << _start_color;
+    }
+    out << " to " << _end_color;
+  }
+
+  if ((_flags & F_end_color_scale) != 0) {
+    out << " color_scale";
+    if ((_flags & F_start_color_scale) != 0) {
+      out << " from " << _start_color_scale;
+    }
+    out << " to " << _end_color_scale;
+  }
+
+  out << " dur " << get_duration();
+}

+ 109 - 0
direct/src/interval/cLerpNodePathInterval.h

@@ -0,0 +1,109 @@
+// Filename: cLerpNodePathInterval.h
+// Created by:  drose (27Aug02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CLERPNODEPATHINTERVAL_H
+#define CLERPNODEPATHINTERVAL_H
+
+#include "directbase.h"
+#include "cLerpInterval.h"
+#include "nodePath.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : CLerpNodePathInterval
+// Description : An interval that lerps one or more properties (like
+//               pos, hpr, etc.) on a NodePath over time.
+////////////////////////////////////////////////////////////////////
+class EXPCL_DIRECT CLerpNodePathInterval : public CLerpInterval {
+PUBLISHED:
+  CLerpNodePathInterval(const string &name, double duration, 
+                        BlendType blend_type,
+                        const NodePath &node, const NodePath &other);
+
+  INLINE const NodePath &get_node() const;
+  INLINE const NodePath &get_other() const;
+
+  INLINE void set_start_pos(const LVecBase3f &pos);
+  INLINE void set_end_pos(const LVecBase3f &pos);
+  INLINE void set_start_hpr(const LVecBase3f &hpr);
+  INLINE void set_end_hpr(const LVecBase3f &hpr);
+  INLINE void set_start_scale(const LVecBase3f &scale);
+  INLINE void set_start_scale(float scale);
+  INLINE void set_end_scale(const LVecBase3f &scale);
+  INLINE void set_end_scale(float scale);
+  INLINE void set_start_color(const LVecBase4f &color);
+  INLINE void set_end_color(const LVecBase4f &color);
+  INLINE void set_start_color_scale(const LVecBase4f &color_scale);
+  INLINE void set_end_color_scale(const LVecBase4f &color_scale);
+
+  virtual void initialize(double t);
+  virtual void instant();
+  virtual void step(double t);
+  virtual void reverse_initialize(double t);
+  virtual void reverse_instant();
+
+  virtual void output(ostream &out) const;
+
+private:
+  NodePath _node;
+  NodePath _other;
+
+  enum Flags {
+    F_end_pos            = 0x0001,
+    F_end_hpr            = 0x0002,
+    F_end_scale          = 0x0004,
+    F_end_color          = 0x0008,
+    F_end_color_scale    = 0x0010,
+
+    F_start_pos          = 0x0100,
+    F_start_hpr          = 0x0200,
+    F_start_scale        = 0x0400,
+    F_start_color        = 0x0800,
+    F_start_color_scale  = 0x1000,
+  };
+  
+  int _flags;
+  LPoint3f _start_pos, _end_pos;
+  LVecBase3f _start_hpr, _end_hpr;
+  LVecBase3f _start_scale, _end_scale;
+  Colorf _start_color, _end_color;
+  LVecBase4f _start_color_scale, _end_color_scale;
+
+  double _prev_d;
+  
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    CLerpInterval::init_type();
+    register_type(_type_handle, "CLerpNodePathInterval",
+                  CLerpInterval::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "cLerpNodePathInterval.I"
+
+#endif
+

+ 239 - 0
direct/src/interval/cMetaInterval.I

@@ -0,0 +1,239 @@
+// Filename: cMetaInterval.I
+// Created by:  drose (27Aug02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::set_precision
+//       Access: Published
+//  Description: Indicates the precision with which time measurements
+//               are compared.  For numerical accuracy, all
+//               floating-point time values are converted to integer
+//               values internally by scaling by the precision factor.
+//               The larger the number given here, the smaller the
+//               delta of time that can be differentiated; the
+//               limit is the maximum integer that can be represented
+//               in the system.
+////////////////////////////////////////////////////////////////////
+INLINE void CMetaInterval::
+set_precision(double precision) {
+  _precision = precision;
+  mark_dirty();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::get_precision
+//       Access: Published
+//  Description: Returns the precision with which time measurements
+//               are compared.  See set_precision().
+////////////////////////////////////////////////////////////////////
+INLINE double CMetaInterval::
+get_precision() const {
+  return _precision;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::get_num_defs
+//       Access: Published
+//  Description: Returns the number of interval and push/pop
+//               definitions that have been added to the meta
+//               interval.
+////////////////////////////////////////////////////////////////////
+INLINE int CMetaInterval::
+get_num_defs() const {
+  return (int)_defs.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::get_def_type
+//       Access: Published
+//  Description: Returns the type of the nth interval definition that
+//               has been added.
+////////////////////////////////////////////////////////////////////
+INLINE CMetaInterval::DefType CMetaInterval::
+get_def_type(int n) const {
+  nassertr(n >= 0 && n < (int)_defs.size(), DT_c_interval);
+  return _defs[n]._type;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::get_c_interval
+//       Access: Published
+//  Description: Return the CInterval pointer associated with the nth
+//               interval definition.  It is only valid to call this
+//               if get_def_type(n) returns DT_c_interval.
+////////////////////////////////////////////////////////////////////
+INLINE CInterval *CMetaInterval::
+get_c_interval(int n) const {
+  nassertr(n >= 0 && n < (int)_defs.size(), NULL);
+  nassertr(_defs[n]._type == DT_c_interval, NULL);
+  return _defs[n]._c_interval;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::get_ext_index
+//       Access: Published
+//  Description: Return the external interval index number associated
+//               with the nth interval definition.  It is only valid
+//               to call this if get_def_type(n) returns DT_ext_index.
+////////////////////////////////////////////////////////////////////
+INLINE int CMetaInterval::
+get_ext_index(int n) const {
+  nassertr(n >= 0 && n < (int)_defs.size(), NULL);
+  nassertr(_defs[n]._type == DT_ext_index, NULL);
+  return _defs[n]._ext_index;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::is_event_ready
+//       Access: Published
+//  Description: Returns true if a recent call to initialize(),
+//               step(), or finalize() has left some external
+//               intervals ready to play.  If this returns true, call
+//               get_event_index(), get_event_t(), and pop_event() to
+//               retrieve the relevant information.
+////////////////////////////////////////////////////////////////////
+INLINE bool CMetaInterval::
+is_event_ready() {
+  return service_event_queue();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::get_event_index
+//       Access: Published
+//  Description: If a previous call to is_event_ready() returned
+//               true, this returns the index number (added via
+//               add_event_index()) of the external interval that needs
+//               to be played.
+////////////////////////////////////////////////////////////////////
+INLINE int CMetaInterval::
+get_event_index() const {
+  nassertr(!_event_queue.empty(), -1);
+  const EventQueueEntry &entry = _event_queue.front();
+  const IntervalDef &def = _defs[entry._n];
+  nassertr(def._type == DT_ext_index, -1);
+  return def._ext_index;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::get_event_t
+//       Access: Published
+//  Description: If a previous call to is_event_ready() returned
+//               true, this returns the t value that should be fed to
+//               the given interval.
+////////////////////////////////////////////////////////////////////
+INLINE double CMetaInterval::
+get_event_t() const {
+  nassertr(!_event_queue.empty(), 0.0f);
+  return int_to_double_time(_event_queue.front()._time);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::get_event_type
+//       Access: Published
+//  Description: If a previous call to is_event_ready() returned
+//               true, this returns the type of the event (initialize,
+//               step, finalize, etc.) for the given interval.
+////////////////////////////////////////////////////////////////////
+INLINE CInterval::EventType CMetaInterval::
+get_event_type() const {
+  nassertr(!_event_queue.empty(), ET_step);
+  return _event_queue.front()._event_type;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::pop_event
+//       Access: Published
+//  Description: Acknowledges that the external interval on the top of
+//               the queue has been extracted, and is about to be
+//               serviced by the scripting language.  This prepares
+//               the interval so the next call to is_event_ready()
+//               will return information about the next external
+//               interval on the queue, if any.
+////////////////////////////////////////////////////////////////////
+INLINE void CMetaInterval::
+pop_event() {
+#ifndef NDEBUG
+  nassertv(!_event_queue.empty());
+  const EventQueueEntry &entry = _event_queue.front();
+  const IntervalDef &def = _defs[entry._n];
+  nassertv(def._type == DT_ext_index);
+#endif
+  _event_queue.pop_front();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::double_to_int_time
+//       Access: Private
+//  Description: Converts from an external double time value or offset
+//               in seconds to an internal integer value or offset.
+////////////////////////////////////////////////////////////////////
+INLINE int CMetaInterval::
+double_to_int_time(double t) const {
+  // Use floor() just in case there are negative values involved.
+  return (int)floor(t * _precision + 0.5);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::int_to_double_time
+//       Access: Private
+//  Description: Converts from an internal integer time value or
+//               offset to an external double time value or offset in
+//               seconds.
+////////////////////////////////////////////////////////////////////
+INLINE double CMetaInterval::
+int_to_double_time(int time) const {
+  return (double)time / _precision;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::PlaybackEvent::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CMetaInterval::PlaybackEvent::
+PlaybackEvent(int time, int n, 
+              CMetaInterval::PlaybackEventType type) :
+  _time(time),
+  _n(n),
+  _type(type)
+{
+  _begin_event = this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::PlaybackEvent::Ordering operator
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE bool CMetaInterval::PlaybackEvent::
+operator < (const CMetaInterval::PlaybackEvent &other) const {
+  return _time < other._time;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::EventQueueEntry::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CMetaInterval::EventQueueEntry::
+EventQueueEntry(int n, CInterval::EventType event_type, int time) :
+  _n(n),
+  _event_type(event_type),
+  _time(time)
+{
+}

+ 877 - 0
direct/src/interval/cMetaInterval.cxx

@@ -0,0 +1,877 @@
+// Filename: cMetaInterval.cxx
+// Created by:  drose (27Aug02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "cMetaInterval.h"
+#include "config_interval.h"
+#include "indirectLess.h"
+#include <algorithm>
+#include <math.h>   // for log10()
+#include <stdio.h>  // for sprintf()
+
+TypeHandle CMetaInterval::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+CMetaInterval::
+CMetaInterval(const string &name) :
+  CInterval(name, 0.0, true)
+{
+  _precision = interval_precision;
+  _current_nesting_level = 0;
+  _next_event_index = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::Destructor
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+CMetaInterval::
+~CMetaInterval() {
+  clear_intervals();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::clear_intervals
+//       Access: Published
+//  Description: Resets the list of intervals and prepares for
+//               receiving a new list.
+////////////////////////////////////////////////////////////////////
+void CMetaInterval::
+clear_intervals() {
+  // Better not do this unless you have serviced all of the
+  // outstanding events!
+  nassertv(_event_queue.empty());
+
+  clear_events();
+
+  // Go through all of our nested intervals and remove ourselves as
+  // their parent.
+  Defs::iterator di;
+  for (di = _defs.begin(); di != _defs.end(); ++di) {
+    IntervalDef &def = (*di);
+    if (def._c_interval != (CInterval *)NULL) {
+      CInterval::Parents::iterator pi = 
+        find(def._c_interval->_parents.begin(),
+             def._c_interval->_parents.end(),
+             this);
+      nassertv(pi != def._c_interval->_parents.end());
+      def._c_interval->_parents.erase(pi);
+    }
+  }
+  _defs.clear();
+
+  _current_nesting_level = 0;
+  _next_event_index = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::push_level
+//       Access: Published
+//  Description: Marks the beginning of a nested level of child
+//               intervals.  Within the nested level, a RelativeStart
+//               time of RS_level_begin refers to the start of the
+//               level, and the first interval added within the level
+//               is always relative to the start of the level.
+//
+//               The return value is the index of the def entry
+//               created by this push.
+////////////////////////////////////////////////////////////////////
+int CMetaInterval::
+push_level(double rel_time, RelativeStart rel_to) {
+  _defs.push_back(IntervalDef());
+  IntervalDef &def = _defs.back();
+  def._type = DT_push_level;
+  def._rel_time = rel_time;
+  def._rel_to = rel_to;
+  _current_nesting_level++;
+  mark_dirty();
+
+  return (int)_defs.size() - 1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::add_c_interval
+//       Access: Published
+//  Description: Adds a new CInterval to the list.  The interval will
+//               be played when the indicated time (relative to the
+//               given point) has been reached.
+//
+//               The return value is the index of the def entry
+//               representing the new interval.
+////////////////////////////////////////////////////////////////////
+int CMetaInterval::
+add_c_interval(CInterval *c_interval, 
+               double rel_time, RelativeStart rel_to) {
+  nassertr(c_interval != (CInterval *)NULL, -1);
+
+  c_interval->_parents.push_back(this);
+  _defs.push_back(IntervalDef());
+  IntervalDef &def = _defs.back();
+  def._type = DT_c_interval;
+  def._c_interval = c_interval;
+  def._rel_time = rel_time;
+  def._rel_to = rel_to;
+  mark_dirty();
+
+  return (int)_defs.size() - 1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::add_ext_index
+//       Access: Published
+//  Description: Adds a new external interval to the list.  This
+//               represents some object in the external scripting
+//               language that has properties similar to a CInterval
+//               (for instance, a Python Interval object).
+//
+//               The CMetaInterval object cannot play this external
+//               interval directly, but it records a placeholder for
+//               it and will ask the scripting language to play it
+//               when it is time, via is_event_ready() and related
+//               methods.
+//
+//               The ext_index number itself is simply a handle that
+//               the scripting language makes up and associates with
+//               its interval object somehow.  The CMetaInterval
+//               object does not attempt to interpret this value.
+//
+//               The return value is the index of the def entry
+//               representing the new interval.
+////////////////////////////////////////////////////////////////////
+int CMetaInterval::
+add_ext_index(int ext_index, const string &name, double duration,
+              bool open_ended,
+              double rel_time, RelativeStart rel_to) {
+  _defs.push_back(IntervalDef());
+  IntervalDef &def = _defs.back();
+  def._type = DT_ext_index;
+  def._ext_index = ext_index;
+  def._ext_name = name;
+  def._ext_duration = duration;
+  def._ext_open_ended = open_ended;
+  def._rel_time = rel_time;
+  def._rel_to = rel_to;
+  mark_dirty();
+
+  return (int)_defs.size() - 1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::pop_level
+//       Access: Published
+//  Description: Finishes a level marked by a previous call to
+//               push_level(), and returns to the previous level.
+////////////////////////////////////////////////////////////////////
+int CMetaInterval::
+pop_level() {
+  nassertr(_current_nesting_level > 0, -1);
+
+  _defs.push_back(IntervalDef());
+  IntervalDef &def = _defs.back();
+  def._type = DT_pop_level;
+  _current_nesting_level--;
+  mark_dirty();
+
+  return (int)_defs.size() - 1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::initialize
+//       Access: Published, Virtual
+//  Description: This replaces the first call to step(), and indicates
+//               that the interval has just begun.  This may be
+//               overridden by derived classes that need to do some
+//               explicit initialization on the first call.
+////////////////////////////////////////////////////////////////////
+void CMetaInterval::
+initialize(double t) {
+  // It may be tempting to flush the event_queue here, but don't do
+  // it.  Those are events that must still be serviced from some
+  // previous interval operation.  Throwing them away would be a
+  // mistake.
+
+  recompute();
+  _next_event_index = 0;
+  _active.clear();
+
+  int now = double_to_int_time(t);
+
+  // Now look for events from the beginning up to the current time.
+  ActiveEvents new_active;
+  while (_next_event_index < _events.size() &&
+         _events[_next_event_index]->_time <= now) {
+    PlaybackEvent *event = _events[_next_event_index];
+    
+    // Do the indicated event.
+    do_event_forward(event, new_active, true);
+    _next_event_index++;
+  }
+  finish_events_forward(now, new_active);
+
+  _curr_t = t;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::instant
+//       Access: Published, Virtual
+//  Description: This is called in lieu of initialize() .. step()
+//               .. finalize(), when everything is to happen within
+//               one frame.  The interval should initialize itself,
+//               then leave itself in the final state.
+////////////////////////////////////////////////////////////////////
+void CMetaInterval::
+instant() {
+  recompute();
+  _active.clear();
+
+  // Apply all of the events.  This just means we invoke "instant" for
+  // any end or instant event, ignoring the begin events.
+  PlaybackEvents::iterator ei;
+  for (ei = _events.begin(); ei != _events.end(); ++ei) {
+    PlaybackEvent *event = (*ei);
+    if (event->_type != PET_begin) {
+      enqueue_event(event->_n, ET_instant, true, 0);
+    }
+  }
+
+  _next_event_index = _events.size();
+  _curr_t = get_duration();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::step
+//       Access: Published, Virtual
+//  Description: Advances the time on the interval.  The time may
+//               either increase (the normal case) or decrease
+//               (e.g. if the interval is being played by a slider).
+////////////////////////////////////////////////////////////////////
+void CMetaInterval::
+step(double t) {
+  int now = double_to_int_time(t);
+
+  // Now look for events between the last time we ran and the current
+  // time.
+
+  if (_next_event_index < _events.size() &&
+      _events[_next_event_index]->_time <= now) {
+    // The normal case: time is increasing.
+    ActiveEvents new_active;
+    while (_next_event_index < _events.size() &&
+           _events[_next_event_index]->_time <= now) {
+      PlaybackEvent *event = _events[_next_event_index];
+
+      // Do the indicated event.
+      do_event_forward(event, new_active, false);
+      _next_event_index++;
+    }
+
+    finish_events_forward(now, new_active);
+
+  } else {
+    // A less usual case: time is decreasing.
+    ActiveEvents new_active;
+    while (_next_event_index > 0 && 
+           _events[_next_event_index - 1]->_time > now) {
+      _next_event_index--;
+      PlaybackEvent *event = _events[_next_event_index];
+      do_event_reverse(event, new_active, false);
+    }
+
+    finish_events_reverse(now, new_active);
+  }
+
+  _curr_t = t;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::finalize
+//       Access: Published, Virtual
+//  Description: This is called when an interval is interrupted.  It
+//               should advance the time as if step() were called, and
+//               also perform whatever cleanup might be required.
+////////////////////////////////////////////////////////////////////
+void CMetaInterval::
+finalize() {
+  // Do all remaining events.
+  ActiveEvents new_active;
+  while (_next_event_index < _events.size()) {
+    PlaybackEvent *event = _events[_next_event_index];
+    // Do the indicated event.
+    do_event_forward(event, new_active, false);
+    _next_event_index++;
+  }
+
+  _curr_t = get_duration();
+  finish_events_forward(double_to_int_time(_curr_t), new_active);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::reverse_initialize
+//       Access: Published, Virtual
+//  Description: Similar to initialize(), but this is called when the
+//               interval is being played backwards; it indicates that
+//               the interval should start at the finishing state and
+//               undo any intervening intervals.
+////////////////////////////////////////////////////////////////////
+void CMetaInterval::
+reverse_initialize(double t) {
+  // It may be tempting to flush the event_queue here, but don't do
+  // it.  Those are events that must still be serviced from some
+  // previous interval operation.  Throwing them away would be a
+  // mistake.
+
+  recompute();
+  _next_event_index = _events.size();
+  _active.clear();
+
+  int now = double_to_int_time(t);
+
+  // Now look for events from the end down to the current time.
+  ActiveEvents new_active;
+  while (_next_event_index > 0 && 
+         _events[_next_event_index - 1]->_time > now) {
+    _next_event_index--;
+    PlaybackEvent *event = _events[_next_event_index];
+    
+    // Do the indicated event.
+    do_event_reverse(event, new_active, true);
+  }
+  finish_events_reverse(now, new_active);
+
+  _curr_t = t;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::reverse_instant
+//       Access: Published, Virtual
+//  Description: This is called in lieu of reverse_initialize()
+//               .. step() .. reverse_finalize(), when everything is
+//               to happen within one frame.  The interval should
+//               initialize itself, then leave itself in the initial
+//               state.
+////////////////////////////////////////////////////////////////////
+void CMetaInterval::
+reverse_instant() {
+  recompute();
+  _active.clear();
+
+  // Apply all of the events.  This just means we invoke "instant" for
+  // any end or instant event, ignoring the begin events.
+  PlaybackEvents::reverse_iterator ei;
+  for (ei = _events.rbegin(); ei != _events.rend(); ++ei) {
+    PlaybackEvent *event = (*ei);
+    if (event->_type != PET_begin) {
+      enqueue_event(event->_n, ET_reverse_instant, true, 0);
+    }
+  }
+
+  _next_event_index = 0;
+  _curr_t = 0.0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::reverse_finalize
+//       Access: Published, Virtual
+//  Description: Called generally following a reverse_initialize(),
+//               this indicates the interval should set itself to the
+//               initial state.
+////////////////////////////////////////////////////////////////////
+void CMetaInterval::
+reverse_finalize() {
+  // Do all remaining events at the beginning.
+  ActiveEvents new_active;
+
+  while (_next_event_index > 0) {
+    _next_event_index--;
+    PlaybackEvent *event = _events[_next_event_index];
+    do_event_reverse(event, new_active, false);
+  }
+
+  finish_events_reverse(0, new_active);
+  _curr_t = 0.0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::write
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void CMetaInterval::
+write(ostream &out, int indent_level) const {
+  recompute();
+
+  // How many digits of precision should we output for time?
+  int num_decimals = (int)ceil(log10(_precision));
+  int total_digits = num_decimals + 4;
+  static const int max_digits = 32;  // totally arbitrary
+  nassertv(total_digits <= max_digits);
+  char format_str[12];
+  sprintf(format_str, "%%%d.%df", total_digits, num_decimals);
+
+  indent(out, indent_level) << get_name() << ":\n";
+
+  int extra_indent_level = 1;
+  Defs::const_iterator di;
+  for (di = _defs.begin(); di != _defs.end(); ++di) {
+    const IntervalDef &def = (*di);
+    char time_str[max_digits + 1];
+    sprintf(time_str, format_str, int_to_double_time(def._actual_begin_time));
+    indent(out, indent_level) << time_str;
+
+    switch (def._type) {
+    case DT_c_interval:
+      indent(out, extra_indent_level)
+        << *def._c_interval;
+      if (!def._c_interval->get_open_ended()) {
+        out << " (!oe)";
+      }
+      out << "\n";
+      break;
+
+    case DT_ext_index:
+      indent(out, extra_indent_level)
+        << "*" << def._ext_name;
+      if (def._ext_duration != 0.0) {
+        out << " dur " << def._ext_duration;
+      }
+      if (!def._ext_open_ended) {
+        out << " (!oe)";
+      }
+      out<< "\n";
+      break;
+
+    case DT_push_level:
+      indent(out, extra_indent_level)
+        << "{\n";
+      extra_indent_level += 2;
+      break;
+
+    case DT_pop_level:
+      extra_indent_level -= 2;
+      indent(out, extra_indent_level)
+        << "}\n";
+      break;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::do_recompute
+//       Access: Protected, Virtual
+//  Description: Recomputes all of the events (and the duration)
+//               according to the set of interval defs.
+////////////////////////////////////////////////////////////////////
+void CMetaInterval::
+do_recompute() {
+  _dirty = false;
+  clear_events();
+
+  int n = recompute_level(0, 0, _end_time);
+
+  if (n != (int)_defs.size()) {
+    interval_cat.warning()
+      << "CMetaInterval pushes don't match pops.\n";
+  }
+
+  // We do a stable_sort() to guarantee ordering of events that have
+  // the same start time.  These must be invoked in the order in which
+  // they appear.
+  stable_sort(_events.begin(), _events.end(), IndirectLess<PlaybackEvent>());
+  _duration = int_to_double_time(_end_time);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::clear_events
+//       Access: Private
+//  Description: Removes all entries from the _events list.
+////////////////////////////////////////////////////////////////////
+void CMetaInterval::
+clear_events() {
+  PlaybackEvents::iterator ei;
+  for (ei = _events.begin(); ei != _events.end(); ++ei) {
+    PlaybackEvent *event = (*ei);
+    delete event;
+  }
+  _events.clear();
+  _active.clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::do_event_forward
+//       Access: Private
+//  Description: Process a single event in the interval, moving
+//               forwards in time.  If the event represents a new
+//               begin, adds it to the new_active list; if it is an
+//               end, finalizes it.
+//
+//               If is_initial is true, it is as if we are in
+//               initialize: instant events will be invoked only if
+//               they are marked open_ended.
+////////////////////////////////////////////////////////////////////
+void CMetaInterval::
+do_event_forward(CMetaInterval::PlaybackEvent *event, 
+                 CMetaInterval::ActiveEvents &new_active, bool is_initial) {
+  switch (event->_type) {
+  case PET_begin:
+    {
+      nassertv(event->_begin_event == event);
+      nassertv(_active.find(event) == _active.end());
+      bool okflag = new_active.insert(event).second;
+      nassertv(okflag);
+    }
+    break;
+    
+  case PET_end:
+    {
+      // Erase the event from either the new active or the current
+      // active lists.
+      int erase_count = new_active.erase(event->_begin_event);
+      if (erase_count != 0) {
+        // This interval was new this frame; we must invoke it as
+        // an instant event.
+        enqueue_event(event->_n, ET_instant, is_initial);
+      } else {
+        erase_count = _active.erase(event->_begin_event);
+        enqueue_event(event->_n, ET_finalize, is_initial);
+      }
+      nassertv(erase_count == 1);
+    }
+    break;
+    
+  case PET_instant:
+    nassertv(event->_begin_event == event);
+    nassertv(new_active.find(event) == new_active.end());
+    nassertv(_active.find(event) == _active.end());
+    enqueue_event(event->_n, ET_instant, is_initial);
+    break;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::finish_events_forward
+//       Access: Private
+//  Description: After walking through the event list and adding a
+//               bunch of new events to new_active, finished up by
+//               calling step() on all of the events still in _active
+//               and initialize() on all the events in new_active,
+//               then copying the events from new_active to active.
+////////////////////////////////////////////////////////////////////
+void CMetaInterval::
+finish_events_forward(int now, CMetaInterval::ActiveEvents &new_active) {
+  // Do whatever's still active.
+  ActiveEvents::iterator ai;
+  for (ai = _active.begin(); ai != _active.end(); ++ai) {
+    PlaybackEvent *event = (*ai);
+    enqueue_event(event->_n, ET_step, false, now - event->_time);
+  }
+  
+  // Initialize whatever new intervals we came across.
+  for (ai = new_active.begin(); ai != new_active.end(); ++ai) {
+    PlaybackEvent *event = (*ai);
+    enqueue_event(event->_n, ET_initialize, false, now - event->_time);
+    bool inserted = _active.insert(event).second;
+    nassertv(inserted);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::do_event_reverse
+//       Access: Private
+//  Description: Process a single event in the interval, moving
+//               backwards in time.  This undoes the indicated event.
+//               If the event represents a new begin, adds it to the
+//               new_active list; if it is an end, finalizes it.
+//
+//               If is_initial is true, it is as if we are in
+//               reverse_initialize: instant events will be invoked
+//               only if they are marked open_ended.
+////////////////////////////////////////////////////////////////////
+void CMetaInterval::
+do_event_reverse(CMetaInterval::PlaybackEvent *event, 
+                 CMetaInterval::ActiveEvents &new_active, bool is_initial) {
+  // Undo the indicated event.
+  switch (event->_type) {
+  case PET_begin:
+    {
+      nassertv(event->_begin_event == event);
+      // Erase the event from either the new active or the current
+      // active lists.
+      int erase_count = new_active.erase(event);
+      if (erase_count != 0) {
+        // This interval was new this frame; we invoke it as an
+        // instant event.
+        enqueue_event(event->_n, ET_reverse_instant, is_initial);
+      } else {
+        erase_count = _active.erase(event->_begin_event);
+        enqueue_event(event->_n, ET_reverse_finalize, is_initial);
+      }
+      nassertv(erase_count == 1);
+    }
+    break;
+    
+  case PET_end:
+    {
+      nassertv(new_active.find(event->_begin_event) == new_active.end());
+      bool okflag = new_active.insert(event->_begin_event).second;
+      nassertv(okflag);
+    }
+    break;
+    
+  case PET_instant:
+    nassertv(event->_begin_event == event);
+    nassertv(_active.find(event) == _active.end());
+    nassertv(new_active.find(event) == new_active.end());
+    enqueue_event(event->_n, ET_reverse_instant, is_initial);
+    break;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::finish_events_reverse
+//       Access: Private
+//  Description: After walking through the event list and adding a
+//               bunch of new events to new_active, finishes up by
+//               calling step() on all of the events still in _active
+//               and reverse_initialize() on all the events in
+//               new_active, then copying the events from new_active
+//               to active.
+////////////////////////////////////////////////////////////////////
+void CMetaInterval::
+finish_events_reverse(int now, CMetaInterval::ActiveEvents &new_active) {
+  // Do whatever's still active.
+  ActiveEvents::iterator ai;
+  for (ai = _active.begin(); ai != _active.end(); ++ai) {
+    PlaybackEvent *event = (*ai);
+    enqueue_event(event->_n, ET_step, false, now - event->_time);
+  }
+  
+  // Initialize whatever new intervals we came across.
+  for (ai = new_active.begin(); ai != new_active.end(); ++ai) {
+    PlaybackEvent *event = (*ai);
+    enqueue_event(event->_n, ET_reverse_initialize, false, now - event->_time);
+    bool inserted = _active.insert(event).second;
+    nassertv(inserted);
+  }
+}
+  
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::enqueue_event
+//       Access: Private
+//  Description: Enqueues the indicated interval for invocation after
+//               we have finished scanning for events that need
+//               processing this frame.
+//
+//               is_initial is only relevant for event types
+//               ET_instant or ET_reverse_instant, and indicates
+//               whether we are in the initialize() (or
+//               reverse_initialize()) call, and should therefore only
+//               invoke open-ended intervals.
+//
+//               time is only relevant for ET_initialize,
+//               ET_reverse_initialize, and ET_step.
+////////////////////////////////////////////////////////////////////
+void CMetaInterval::
+enqueue_event(int n, CInterval::EventType event_type, bool is_initial, int time) {
+  nassertv(n >= 0 && n < (int)_defs.size());
+  const IntervalDef &def = _defs[n];
+  switch (def._type) {
+  case DT_c_interval:
+    if (is_initial &&
+        (event_type == ET_instant || event_type == ET_reverse_instant) &&
+        !def._c_interval->get_open_ended()) {
+      // Ignore a non-open-ended interval that we skipped completely
+      // past on initialize().
+      return;
+    } else {
+      if (_event_queue.empty()) {
+        // if the event queue is empty, we can process this C++
+        // interval immediately.  We only need to defer it if there
+        // are external (e.g. Python) intervals in the queue that need
+        // to be processed first.
+        def._c_interval->set_t(int_to_double_time(time), event_type);
+        return;
+      }
+    }
+    break;
+
+  case DT_ext_index:
+    if (is_initial &&
+        (event_type == ET_instant || event_type == ET_reverse_instant) &&
+        !def._ext_open_ended) {
+      // Ignore a non-open-ended interval that we skipped completely
+      // past on initialize().
+      return;
+    }
+    break;
+
+  default:
+    nassertv(false);
+    return;
+  }
+
+  _event_queue.push_back(EventQueueEntry(n, event_type, time));
+}
+  
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::service_event_queue
+//       Access: Private
+//  Description: Invokes whatever C++ intervals might be at the head
+//               of the queue, and prepares for passing an external
+//               interval to the scripting language.
+//
+//               The return value is true if there remains at least
+//               one external event to be serviced, false if all
+//               events are handled.
+////////////////////////////////////////////////////////////////////
+bool CMetaInterval::
+service_event_queue() {
+  while (!_event_queue.empty()) {
+    const EventQueueEntry &entry = _event_queue.front();
+    nassertr(entry._n >= 0 && entry._n < (int)_defs.size(), false);
+    const IntervalDef &def = _defs[entry._n];
+    switch (def._type) {
+    case DT_c_interval:
+      // Handle the C++ event.
+      def._c_interval->set_t(int_to_double_time(entry._time), entry._event_type);
+      break;
+
+    case DT_ext_index:
+      // Here's an external event; leave it there and return.
+      return true;
+
+    default:
+      nassertr(false, false);
+      return false;
+    }
+    _event_queue.pop_front();
+  }
+
+  // No more events on the queue.
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::recompute_level
+//       Access: Private
+//  Description: Recursively recomputes a complete level (delimited by
+//               push/pop definitions).
+//
+//               The value n on entry refers to the first entry after
+//               the push; the return value will reference the
+//               matching pop, or an index greater than the last
+//               element in the array if there was no matching pop.
+//
+//               The level_begin value indicates the begin time of
+//               this level.  On return, level_end is filled with the
+//               end time of this level.
+////////////////////////////////////////////////////////////////////
+int CMetaInterval::
+recompute_level(int n, int level_begin, int &level_end) {
+  level_end = level_begin;
+  int previous_begin = level_begin;
+  int previous_end = level_begin;
+
+  while (n < (int)_defs.size() && _defs[n]._type != DT_pop_level) {
+    IntervalDef &def = _defs[n];
+    int begin_time, end_time;
+    switch (def._type) {
+    case DT_c_interval:
+      begin_time = get_begin_time(def, level_begin, previous_begin, previous_end);
+      def._actual_begin_time = begin_time;
+      end_time = begin_time + double_to_int_time(def._c_interval->get_duration());
+      if (begin_time == end_time) {
+        _events.push_back(new PlaybackEvent(begin_time, n, PET_instant));
+      } else {
+        PlaybackEvent *begin = new PlaybackEvent(begin_time, n, PET_begin);
+        PlaybackEvent *end = new PlaybackEvent(end_time, n, PET_end);
+        end->_begin_event = begin;
+        _events.push_back(begin);
+        _events.push_back(end);
+      }
+      break;
+
+    case DT_ext_index:
+      begin_time = get_begin_time(def, level_begin, previous_begin, previous_end);
+      def._actual_begin_time = begin_time;
+      end_time = begin_time + double_to_int_time(def._ext_duration);
+      if (begin_time == end_time) {
+        _events.push_back(new PlaybackEvent(begin_time, n, PET_instant));
+      } else {
+        PlaybackEvent *begin = new PlaybackEvent(begin_time, n, PET_begin);
+        PlaybackEvent *end = new PlaybackEvent(end_time, n, PET_end);
+        end->_begin_event = begin;
+        _events.push_back(begin);
+        _events.push_back(end);
+      }
+      break;
+
+    case DT_push_level:
+      begin_time = get_begin_time(def, level_begin, previous_begin, previous_end);
+      def._actual_begin_time = begin_time;
+      n = recompute_level(n + 1, begin_time, end_time);
+      break;
+
+    case DT_pop_level:
+      nassertr(false, _defs.size());
+      break;
+    }
+
+    previous_begin = begin_time;
+    previous_end = end_time;
+    level_end = max(level_end, end_time);
+    n++;
+  }
+
+  if (n < (int)_defs.size()) {
+    // The final pop "begins" at the level end time, just for clarity
+    // on output.
+    IntervalDef &def = _defs[n];
+    def._actual_begin_time = level_end;
+  }
+
+  return n;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CMetaInterval::get_begin_time
+//       Access: Private
+//  Description: Returns the integer begin time indicated by the given
+//               IntervalDef, given the indicated level begin,
+//               previous begin, and previous end times.
+////////////////////////////////////////////////////////////////////
+int CMetaInterval::
+get_begin_time(const CMetaInterval::IntervalDef &def, int level_begin,
+               int previous_begin, int previous_end) {
+  switch (def._rel_to) {
+  case RS_previous_end:
+    return previous_end + double_to_int_time(def._rel_time);
+
+  case RS_previous_begin:
+    return previous_begin + double_to_int_time(def._rel_time);
+
+  case RS_level_begin:
+    return level_begin + double_to_int_time(def._rel_time);
+  }
+
+  nassertr(false, previous_end);
+  return previous_end;
+}

+ 190 - 0
direct/src/interval/cMetaInterval.h

@@ -0,0 +1,190 @@
+// Filename: cMetaInterval.h
+// Created by:  drose (27Aug02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CMETAINTERVAL_H
+#define CMETAINTERVAL_H
+
+#include "directbase.h"
+#include "cInterval.h"
+
+#include <pdeque.h>
+#include <pvector.h>
+#include <pset.h>
+
+////////////////////////////////////////////////////////////////////
+//       Class : CMetaInterval
+// Description : This interval contains a list of nested intervals,
+//               each of which has its own begin and end times.  Some
+//               of them may overlap and some of them may not.
+////////////////////////////////////////////////////////////////////
+class EXPCL_DIRECT CMetaInterval : public CInterval {
+PUBLISHED:
+  CMetaInterval(const string &name);
+  virtual ~CMetaInterval();
+
+  enum RelativeStart {
+    RS_previous_end,
+    RS_previous_begin,
+    RS_level_begin,
+  };
+
+  INLINE void set_precision(double precision);
+  INLINE double get_precision() const;
+
+  void clear_intervals();
+  int push_level(double rel_time, RelativeStart rel_to);
+  int add_c_interval(CInterval *c_interval, 
+                     double rel_time, RelativeStart rel_to);
+  int add_ext_index(int ext_index, const string &name,
+                    double duration, bool open_ended,
+                    double rel_time, RelativeStart rel_to);
+  int pop_level();
+
+  enum DefType {
+    DT_c_interval,
+    DT_ext_index,
+    DT_push_level,
+    DT_pop_level
+  };
+
+  INLINE int get_num_defs() const;
+  INLINE DefType get_def_type(int n) const;
+  INLINE CInterval *get_c_interval(int n) const;
+  INLINE int get_ext_index(int n) const;
+
+  virtual void initialize(double t);
+  virtual void instant();
+  virtual void step(double t);
+  virtual void finalize();
+  virtual void reverse_initialize(double t);
+  virtual void reverse_instant();
+  virtual void reverse_finalize();
+
+  INLINE bool is_event_ready();
+  INLINE int get_event_index() const;
+  INLINE double get_event_t() const;
+  INLINE EventType get_event_type() const;
+  INLINE void pop_event();
+
+  virtual void write(ostream &out, int indent_level) const;
+
+protected:
+  virtual void do_recompute();
+
+private:
+  class IntervalDef {
+  public:
+    DefType _type;
+    PT(CInterval) _c_interval;
+    int _ext_index;
+    string _ext_name;
+    double _ext_duration;
+    bool _ext_open_ended;
+    double _rel_time;
+    RelativeStart _rel_to;
+    int _actual_begin_time;
+  };
+
+  enum PlaybackEventType {
+    PET_begin,
+    PET_end,
+    PET_instant
+  };
+
+  class PlaybackEvent {
+  public:
+    INLINE PlaybackEvent(int time, int n, PlaybackEventType type);
+    INLINE bool operator < (const PlaybackEvent &other) const;
+    int _time;
+    int _n;
+    PlaybackEventType _type;
+    PlaybackEvent *_begin_event;
+  };
+
+  class EventQueueEntry {
+  public:
+    INLINE EventQueueEntry(int n, EventType event_type, int time);
+    int _n;
+    EventType _event_type;
+    int _time;
+  };
+
+  typedef pvector<IntervalDef> Defs;
+  typedef pvector<PlaybackEvent *> PlaybackEvents;
+  typedef pset<PlaybackEvent *> ActiveEvents;
+  typedef pdeque<EventQueueEntry> EventQueue;
+
+  INLINE int double_to_int_time(double t) const;
+  INLINE double int_to_double_time(int time) const;
+
+  void clear_events();
+  void do_event_forward(PlaybackEvent *event, ActiveEvents &new_active,
+                        bool is_initial);
+  void finish_events_forward(int now, ActiveEvents &new_active);
+  void do_event_reverse(PlaybackEvent *event, ActiveEvents &new_active,
+                        bool is_initial);
+  void finish_events_reverse(int now, ActiveEvents &new_active);
+
+  void enqueue_event(int n, CInterval::EventType event_type, bool is_initial,
+                     int time = 0);
+  bool service_event_queue();
+
+  int recompute_level(int n, int level_begin, int &level_end);
+  int get_begin_time(const IntervalDef &def, int level_begin,
+                     int previous_begin, int previous_end);
+
+  double _precision;
+  Defs _defs;
+  int _current_nesting_level;
+
+  PlaybackEvents _events;
+  ActiveEvents _active;
+  int _end_time;
+
+  size_t _next_event_index;
+
+  // This is the queue of events that have occurred due to a recent
+  // initialize(), step(), etc., but have not yet been serviced, due
+  // to an embedded external (e.g. Python) interval that the scripting
+  // language must service.  This queue should be considered precious,
+  // and should never be arbitrarily flushed without servicing all of
+  // its events.
+  EventQueue _event_queue;
+  
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    CInterval::init_type();
+    register_type(_type_handle, "CMetaInterval",
+                  CInterval::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "cMetaInterval.I"
+
+#endif
+

+ 65 - 0
direct/src/interval/config_interval.cxx

@@ -0,0 +1,65 @@
+// Filename: config_interval.cxx
+// Created by:  drose (27Aug02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "config_interval.h"
+#include "cInterval.h"
+#include "cLerpInterval.h"
+#include "cLerpNodePathInterval.h"
+#include "cLerpAnimEffectInterval.h"
+#include "cMetaInterval.h"
+#include "showInterval.h"
+#include "hideInterval.h"
+
+#include "dconfig.h"
+
+Configure(config_interval);
+NotifyCategoryDef(interval, "");
+
+ConfigureFn(config_interval) {
+  init_libinterval();
+}
+
+// Set this to the default value for set_precision() for each
+// CMetaInterval created.
+const double interval_precision = config_interval.GetDouble("interval-precision", 1000.0);
+
+////////////////////////////////////////////////////////////////////
+//     Function: init_libinterval
+//  Description: Initializes the library.  This must be called at
+//               least once before any of the functions or classes in
+//               this library can be used.  Normally it will be
+//               called by the static initializers and need not be
+//               called explicitly, but special cases exist.
+////////////////////////////////////////////////////////////////////
+void
+init_libinterval() {
+  static bool initialized = false;
+  if (initialized) {
+    return;
+  }
+  initialized = true;
+
+  CInterval::init_type();
+  CLerpInterval::init_type();
+  CLerpNodePathInterval::init_type();
+  CLerpAnimEffectInterval::init_type();
+  CMetaInterval::init_type();
+  ShowInterval::init_type();
+  HideInterval::init_type();
+}
+

+ 32 - 0
direct/src/interval/config_interval.h

@@ -0,0 +1,32 @@
+// Filename: config_interval.h
+// Created by:  drose (27Aug02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CONFIG_INTERVAL_H
+#define CONFIG_INTERVAL_H
+
+#include "directbase.h"
+#include "notifyCategoryProxy.h"
+#include "dconfig.h"
+
+NotifyCategoryDecl(interval, EXPCL_DIRECT, EXPTP_DIRECT);
+
+extern const double interval_precision;
+
+extern EXPCL_DIRECT void init_libinterval();
+
+#endif

+ 18 - 0
direct/src/interval/hideInterval.I

@@ -0,0 +1,18 @@
+// Filename: hideInterval.I
+// Created by:  drose (27Aug02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+

+ 68 - 0
direct/src/interval/hideInterval.cxx

@@ -0,0 +1,68 @@
+// Filename: hideInterval.cxx
+// Created by:  drose (27Aug02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "hideInterval.h"
+
+int HideInterval::_unique_index;
+TypeHandle HideInterval::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: HideInterval::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+HideInterval::
+HideInterval(const NodePath &node, const string &name) :
+  CInterval(name, 0.0, true),
+  _node(node)
+{
+  nassertv(!node.is_empty());
+  if (_name.empty()) {
+    ostringstream name_strm;
+    name_strm
+      << "HideInterval-" << node.node()->get_name() << "-" << ++_unique_index;
+    _name = name_strm.str();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HideInterval::instant
+//       Access: Published, Virtual
+//  Description: This is called in lieu of initialize() .. step()
+//               .. finalize(), when everything is to happen within
+//               one frame.  The interval should initialize itself,
+//               then leave itself in the final state.
+////////////////////////////////////////////////////////////////////
+void HideInterval::
+instant() {
+  _node.hide();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HideInterval::reverse_instant
+//       Access: Published, Virtual
+//  Description: This is called in lieu of reverse_initialize()
+//               .. step() .. reverse_finalize(), when everything is
+//               to happen within one frame.  The interval should
+//               initialize itself, then leave itself in the initial
+//               state.
+////////////////////////////////////////////////////////////////////
+void HideInterval::
+reverse_instant() {
+  _node.show();
+}

+ 61 - 0
direct/src/interval/hideInterval.h

@@ -0,0 +1,61 @@
+// Filename: hideInterval.h
+// Created by:  drose (27Aug02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef HIDEINTERVAL_H
+#define HIDEINTERVAL_H
+
+#include "directbase.h"
+#include "cInterval.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : HideInterval
+// Description : An interval that calls NodePath::hide().
+////////////////////////////////////////////////////////////////////
+class EXPCL_DIRECT HideInterval : public CInterval {
+PUBLISHED:
+  HideInterval(const NodePath &node, const string &name = string());
+
+  virtual void instant();
+  virtual void reverse_instant();
+
+private:
+  NodePath _node;
+  static int _unique_index;
+  
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    CInterval::init_type();
+    register_type(_type_handle, "HideInterval",
+                  CInterval::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "hideInterval.I"
+
+#endif
+

+ 75 - 0
direct/src/interval/lerp_helpers.h

@@ -0,0 +1,75 @@
+// Filename: lerp_helpers.h
+// Created by:  drose (27Aug02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef LERP_HELPERS_H
+#define LERP_HELPERS_H
+
+#include "directbase.h"
+
+//
+// This header file is only included within .cxx files in this
+// directory.
+//
+
+//
+// The functions defined here include some trivial template functions
+// for handling basic lerp computations, common to several .cxx files
+// here.
+//
+
+////////////////////////////////////////////////////////////////////
+//     Function: lerp_value
+//  Description: Applies the linear lerp computation for a single
+//               parameter.
+////////////////////////////////////////////////////////////////////
+template<class NumericType>
+INLINE void
+lerp_value(NumericType &current_value,
+           double d,
+           const NumericType &starting_value,
+           const NumericType &ending_value) {
+  current_value = starting_value + d * (ending_value - starting_value);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: lerp_value_from_prev
+//  Description: Applies the linear lerp computation for a single
+//               parameter, when the starting value is implicit.
+//
+//               This computes the new value based on assuming the
+//               prev_value represents the value computed at delta
+//               prev_d.
+////////////////////////////////////////////////////////////////////
+template<class NumericType>
+INLINE void
+lerp_value_from_prev(NumericType &current_value,
+                     double d, double prev_d,
+                     const NumericType &prev_value,
+                     const NumericType &ending_value) {
+  if (prev_d == 1.0) {
+    current_value = ending_value;
+  } else {
+    NumericType starting_value = 
+      (prev_value - prev_d * ending_value) / (1.0 - prev_d);
+    current_value = starting_value + d * (ending_value - starting_value);
+  }
+}
+
+
+#endif
+

+ 18 - 0
direct/src/interval/showInterval.I

@@ -0,0 +1,18 @@
+// Filename: showInterval.I
+// Created by:  drose (27Aug02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+

+ 68 - 0
direct/src/interval/showInterval.cxx

@@ -0,0 +1,68 @@
+// Filename: showInterval.cxx
+// Created by:  drose (27Aug02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "showInterval.h"
+
+int ShowInterval::_unique_index;
+TypeHandle ShowInterval::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: ShowInterval::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+ShowInterval::
+ShowInterval(const NodePath &node, const string &name) :
+  CInterval(name, 0.0, true),
+  _node(node)
+{
+  nassertv(!node.is_empty());
+  if (_name.empty()) {
+    ostringstream name_strm;
+    name_strm
+      << "ShowInterval-" << node.node()->get_name() << "-" << ++_unique_index;
+    _name = name_strm.str();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ShowInterval::instant
+//       Access: Published, Virtual
+//  Description: This is called in lieu of initialize() .. step()
+//               .. finalize(), when everything is to happen within
+//               one frame.  The interval should initialize itself,
+//               then leave itself in the final state.
+////////////////////////////////////////////////////////////////////
+void ShowInterval::
+instant() {
+  _node.show();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ShowInterval::reverse_instant
+//       Access: Published, Virtual
+//  Description: This is called in lieu of reverse_initialize()
+//               .. step() .. reverse_finalize(), when everything is
+//               to happen within one frame.  The interval should
+//               initialize itself, then leave itself in the initial
+//               state.
+////////////////////////////////////////////////////////////////////
+void ShowInterval::
+reverse_instant() {
+  _node.hide();
+}

+ 61 - 0
direct/src/interval/showInterval.h

@@ -0,0 +1,61 @@
+// Filename: showInterval.h
+// Created by:  drose (27Aug02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef SHOWINTERVAL_H
+#define SHOWINTERVAL_H
+
+#include "directbase.h"
+#include "cInterval.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : ShowInterval
+// Description : An interval that calls NodePath::show().
+////////////////////////////////////////////////////////////////////
+class EXPCL_DIRECT ShowInterval : public CInterval {
+PUBLISHED:
+  ShowInterval(const NodePath &node, const string &name = string());
+
+  virtual void instant();
+  virtual void reverse_instant();
+
+private:
+  NodePath _node;
+  static int _unique_index;
+  
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    CInterval::init_type();
+    register_type(_type_handle, "ShowInterval",
+                  CInterval::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "showInterval.I"
+
+#endif
+