Browse Source

define ivalMgr

David Rose 23 years ago
parent
commit
24f194bd45

+ 7 - 78
direct/src/extensions/CInterval-extensions.py

@@ -5,59 +5,17 @@
     """
 
     def setT(self, t):
-        t = min(max(t, 0.0), self.getDuration())
-        state = self.getState()
-        if state == CInterval.SInitial:
-            self.privInitialize(t)
-        elif state == CInterval.SFinal:
-            self.privReverseInitialize(t)
-        else:
-            self.privStep(t)
+        # Overridden from the C++ function to call privPostEvent
+        # afterward.  We do this by renaming the C++ function in
+        # FFIRename.
+        self.__cSetT(t)
         self.privPostEvent()
 
-    def start(self, t0 = 0.0, duration = None, scale = 1.0):
-        if self.isPlaying():
-            self.finish()
+    def play(self, t0 = 0.0, duration = None, scale = 1.0):
         if duration:  # None or 0 implies full length
-            self.setupPlay(t0, t0 + duration, scale)
+            self.start(t0, t0 + duration, scale)
         else:
-            self.setupPlay(t0, -1, scale)
-        self.privPostEvent()
-        self.__loop = 0
-        self.__spawnTask()
-
-    def loop(self, t0 = 0.0, duration = None, scale = 1.0):
-        self.start(t0, duration, scale)
-        self.__loop = 1
-        return
-
-    def pause(self):
-        if self.getState() == CInterval.SStarted:
-            self.privInterrupt()
-        self.privPostEvent()
-        self.__removeTask()
-        return self.getT()
-
-    def resume(self, t0 = None):
-        if not hasattr(self, "_CInterval__loop"):
-            self.__loop = 0
-        if t0 != None:
-            self.setT(t0)
-        self.setupResume()
-        if not self.isPlaying():
-            self.__spawnTask()
-        
-    def finish(self):
-        state = self.getState()
-        if state == CInterval.SInitial:
-            self.privInstant()
-        elif state != CInterval.SFinal:
-            self.privFinalize()
-        self.privPostEvent()
-        self.__removeTask()
-
-    def play(self, *args, **kw):
-        self.start(*args, **kw)
+            self.start(t0, -1, scale)
 
     def stop(self):
         self.finish()
@@ -65,9 +23,6 @@
     def setFinalT(self):
         self.finish()
 
-    def isPlaying(self):
-        return taskMgr.hasTaskNamed(self.getName() + '-play')
-
     def privPostEvent(self):
         # Call after calling any of the priv* methods to do any required
         # Python finishing steps.
@@ -76,32 +31,6 @@
             for func in self.setTHooks:
                 func(t)
 
-    def __spawnTask(self):
-        # Spawn task
-        import Task
-        taskName = self.getName() + '-play'
-        task = Task.Task(self.__playTask)
-        task.interval = self
-        taskMgr.add(task, taskName)
-
-    def __removeTask(self):
-        # Kill old task(s), including those from a similarly-named but
-        # different interval.
-        taskName = self.getName() + '-play'
-        oldTasks = taskMgr.getTasksNamed(taskName)
-        for task in oldTasks:
-            if hasattr(task, "interval"):
-                taskMgr.remove(task)
-
-    def __playTask(self, task):
-        import Task
-        loopCount = self.stepPlay()
-        self.privPostEvent()
-        if loopCount == 0 or self.__loop:
-            return Task.cont
-        else:
-            return Task.done
-
     def popupControls(self, tl = None):
         """ popupControls()
             Popup control panel for interval.

+ 1 - 0
direct/src/ffi/FFIRename.py

@@ -44,6 +44,7 @@ methodRenameDictionary = {
     'operator<<=' : '__ilshift__',
     'operator>>=' : '__irshift__',
     'print'       : 'Cprint',
+    'CInterval.setT' : '__cSetT',
     }
     
 classRenameDictionary = {

+ 1 - 0
direct/src/interval/ActorInterval.py

@@ -1,6 +1,7 @@
 """ActorInterval module: contains the ActorInterval class"""
 
 from PandaModules import *
+from DirectNotifyGlobal import *
 import Interval
 import math
 import LerpBlendHelpers

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

@@ -2,6 +2,7 @@
 
 from PandaModules import *
 from MessengerGlobal import *
+from DirectNotifyGlobal import *
 import Interval
 import types
 

+ 56 - 61
direct/src/interval/Interval.py

@@ -27,13 +27,14 @@ class Interval(DirectObject):
         self.__startTAtStart = 1
         self.__endT = duration
         self.__endTAtEnd = 1
+        self.__playRate = 1.0
+        self.__doLoop = 0
+        self.__loopCount = 0
 
         # Set true if the interval should be invoked if it was
         # completely skipped over during initialize or finalize, false
         # if it should be ignored in this case.
         self.openEnded = openEnded
-        
-        self.__loop = 0
 
     def getName(self):
         return self.name
@@ -53,10 +54,55 @@ class Interval(DirectObject):
         # finish().
         return (self.getState() == CInterval.SInitial or \
                 self.getState() == CInterval.SFinal)
+
+    def setT(self, t):
+        t = min(max(t, 0.0), self.getDuration())
+        state = self.getState()
+        if state == CInterval.SInitial:
+            self.privInitialize(t)
+        elif state == CInterval.SFinal:
+            self.privReverseInitialize(t)
+        else:
+            self.privStep(t)
+        self.privPostEvent()
         
     def getT(self):
         return self.currT
 
+    def start(self, startT = 0.0, endT = -1.0, playRate = 1.0):
+        self.setupPlay(startT, endT, playRate, 0)
+        self.__spawnTask()
+
+    def loop(self, startT = 0.0, endT = -1.0, playRate = 1.0):
+        self.setupPlay(startT, endT, playRate, 1)
+        self.__spawnTask()
+
+    def pause(self):
+        if self.getState() == CInterval.SStarted:
+            self.privInterrupt()
+        self.privPostEvent()
+        self.__removeTask()
+        return self.getT()
+
+    def resume(self, t0 = None):
+        if t0 != None:
+            self.setT(t0)
+        self.setupResume()
+        if not self.isPlaying():
+            self.__spawnTask()
+        
+    def finish(self):
+        state = self.getState()
+        if state == CInterval.SInitial:
+            self.privInstant()
+        elif state != CInterval.SFinal:
+            self.privFinalize()
+        self.privPostEvent()
+        self.__removeTask()
+
+    def isPlaying(self):
+        return taskMgr.hasTaskNamed(self.getName() + '-play')
+
     def setDoneEvent(self, event):
         self.doneEvent = event
 
@@ -133,7 +179,7 @@ class Interval(DirectObject):
         if self.doneEvent:
             messenger.throw(self.doneEvent)
 
-    def setupPlay(self, startT, endT, playRate):
+    def setupPlay(self, startT, endT, playRate, doLoop):
         duration = self.getDuration()
         
         if startT <= 0:
@@ -155,6 +201,7 @@ class Interval(DirectObject):
 
         self.__clockStart = globalClock.getFrameTime()
         self.__playRate = playRate
+        self.__doLoop = doLoop
         self.__loopCount = 0
 
     def setupResume(self):
@@ -178,7 +225,7 @@ class Interval(DirectObject):
                 if self.isStopped():
                     self.privInitialize(t)
                 else:
-                    self.prevStep(t)
+                    self.privStep(t)
 
             else:
                 # Past the ending point; time to finalize.
@@ -214,6 +261,8 @@ class Interval(DirectObject):
             # Not supported at the moment.
             pass
 
+        return (self.__loopCount == 0 or self.__doLoop)
+
     def __repr__(self, indent=0):
         """ __repr__(indent)
         """
@@ -226,58 +275,6 @@ class Interval(DirectObject):
     # The rest of these methods are duplicates of functions defined
     # for the CInterval class via the file CInterval-extensions.py.
 
-    def setT(self, t):
-        t = min(max(t, 0.0), self.getDuration())
-        state = self.getState()
-        if state == CInterval.SInitial:
-            self.privInitialize(t)
-        elif state == CInterval.SFinal:
-            self.privReverseInitialize(t)
-        else:
-            self.privStep(t)
-        self.privPostEvent()
-
-    def start(self, t0 = 0.0, duration = None, scale = 1.0):
-        if self.isPlaying():
-            self.finish()
-        if duration:  # None or 0 implies full length
-            self.setupPlay(t0, t0 + duration, scale)
-        else:
-            self.setupPlay(t0, -1, scale)
-        self.privPostEvent()
-        self.__loop = 0
-        self.__spawnTask()
-
-    def loop(self, t0 = 0.0, duration = None, scale = 1.0):
-        self.start(t0, duration, scale)
-        self.__loop = 1
-        return
-
-    def pause(self):
-        if self.getState() == CInterval.SStarted:
-            self.privInterrupt()
-        self.privPostEvent()
-        self.__removeTask()
-        return self.getT()
-
-    def resume(self, t0 = None):
-        if not hasattr(self, "_CInterval__loop"):
-            self.__loop = 0
-        if t0 != None:
-            self.setT(t0)
-        self.setupResume()
-        if not self.isPlaying():
-            self.__spawnTask()
-        
-    def finish(self):
-        state = self.getState()
-        if state == CInterval.SInitial:
-            self.privInstant()
-        elif state != CInterval.SFinal:
-            self.privFinalize()
-        self.privPostEvent()
-        self.__removeTask()
-
     def play(self, *args, **kw):
         self.start(*args, **kw)
 
@@ -287,9 +284,6 @@ class Interval(DirectObject):
     def setFinalT(self):
         self.finish()
 
-    def isPlaying(self):
-        return taskMgr.hasTaskNamed(self.getName() + '-play')
-
     def privPostEvent(self):
         # Call after calling any of the priv* methods to do any required
         # Python finishing steps.
@@ -301,6 +295,7 @@ class Interval(DirectObject):
     def __spawnTask(self):
         # Spawn task
         import Task
+        self.__removeTask()
         taskName = self.getName() + '-play'
         task = Task.Task(self.__playTask)
         task.interval = self
@@ -318,9 +313,9 @@ class Interval(DirectObject):
 
     def __playTask(self, task):
         import Task
-        loopCount = self.stepPlay()
+        again = self.stepPlay()
         self.privPostEvent()
-        if loopCount == 0 or self.__loop:
+        if again:
             return Task.cont
         else:
             return Task.done

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

@@ -10,3 +10,4 @@ from ParticleInterval import *
 from SoundInterval import *
 from WaitInterval import *
 from MetaInterval import *
+from IntervalManager import *

+ 56 - 15
direct/src/interval/MetaInterval.py

@@ -1,5 +1,6 @@
 from PandaModules import *
 from DirectNotifyGlobal import *
+from IntervalManager import ivalMgr
 import Interval
 import Task
 import types
@@ -31,6 +32,11 @@ class MetaInterval(CMetaInterval):
                 name = kw['name']
                 del kw['name']
 
+        interruptible = 0
+        if kw.has_key('interruptible'):
+            interruptible = kw['interruptible']
+            del kw['interruptible']
+
         if kw:
             self.notify.error("Unexpected keyword parameters: %s" % (kw.keys()))
 
@@ -50,6 +56,8 @@ class MetaInterval(CMetaInterval):
             MetaInterval.SequenceNum += 1
 
         CMetaInterval.__init__(self, name)
+        self.__manager = ivalMgr
+        self.setInterruptible(interruptible)
 
         self.pythonIvals = []
 
@@ -113,6 +121,7 @@ class MetaInterval(CMetaInterval):
         self.__ivalsDirty = 1
         return self
 
+    # Functions to define sequence, parallel, and track behaviors:
     
     def addSequence(self, list, relTime, relTo):
         # Adds the given list of intervals to the MetaInterval to be
@@ -208,7 +217,53 @@ class MetaInterval(CMetaInterval):
 
         else:
             self.notify.error("Not an Interval: %s" % (ival,))
-            
+
+    # Functions to support automatic playback of MetaIntervals along
+    # with all of their associated Python callbacks:
+
+    def setManager(self, manager):
+        self.__manager = manager
+        CMetaInterval.setManager(self, manager)
+
+    def getManager(self):
+        return self.__manager
+
+    def start(self, startT = 0.0, endT = -1.0, playRate = 1.0):
+        self.__updateIvals()
+        self.setupPlay(startT, endT, playRate, 0)
+        self.__manager.addInterval(self)
+
+    def loop(self, startT = 0.0, endT = -1.0, playRate = 1.0):
+        self.__updateIvals()
+        self.setupPlay(startT, endT, playRate, 1)
+        self.__manager.addInterval(self)
+
+    def pause(self):
+        if self.getState() == CInterval.SStarted:
+            self.privInterrupt()
+        self.__manager.removeInterval(self)
+        self.privPostEvent()
+        return self.getT()
+
+    def resume(self, t0 = None):
+        self.__updateIvals()
+        if t0 != None:
+            self.setT(t0)
+        self.setupResume()
+        self.__manager.addInterval(self)
+        
+    def finish(self):
+        state = self.getState()
+        if state == CInterval.SInitial:
+            self.privInstant()
+        elif state != CInterval.SFinal:
+            self.privFinalize()
+        self.__manager.removeInterval(self)
+        self.privPostEvent()
+
+
+
+    # Internal functions:
 
     def __updateIvals(self):
         # The MetaInterval object does not create the C++ list of
@@ -312,20 +367,6 @@ class MetaInterval(CMetaInterval):
         self.__updateIvals()
         return CMetaInterval.getDuration(self)
 
-    def start(self, *args, **kw):
-        # This function overrides from the parent level to force it to
-        # update the interval list first, if necessary.
-
-        self.__updateIvals()
-        return CMetaInterval.start(self, *args, **kw)
-
-    def loop(self, *args, **kw):
-        # This function overrides from the parent level to force it to
-        # update the interval list first, if necessary.
-
-        self.__updateIvals()
-        return CMetaInterval.loop(self, *args, **kw)
-
     def __repr__(self, *args, **kw): 
         # This function overrides from the parent level to force it to
         # update the interval list first, if necessary.

+ 1 - 0
direct/src/interval/MopathInterval.py

@@ -2,6 +2,7 @@
 
 import Interval
 from PandaModules import *
+from DirectNotifyGlobal import *
 
 # import Mopath
 

+ 1 - 0
direct/src/interval/ParticleInterval.py

@@ -1,6 +1,7 @@
 """ParticleInterval module: contains the ParticleInterval class"""
 
 from PandaModules import *
+from DirectNotifyGlobal import *
 import Interval
 
 import ParticleEffect

+ 4 - 0
direct/src/interval/SoundInterval.py

@@ -1,6 +1,7 @@
 """SoundInterval module: contains the SoundInterval class"""
 
 from PandaModules import *
+from DirectNotifyGlobal import *
 import Interval
 
 class SoundInterval(Interval.Interval):
@@ -61,6 +62,9 @@ class SoundInterval(Interval.Interval):
     def privStep(self, t):
         if self.state == CInterval.SPaused:
             # Restarting from a pause.
+            self.sound.setVolume(self.volume)
+            self.sound.setTime(t)
+            self.sound.setLoop(self.loop)
             self.sound.play()
         self.state = CInterval.SStarted
         self.currT = t

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

@@ -8,6 +8,7 @@
   #define SOURCES \
     config_interval.cxx config_interval.h \
     cInterval.cxx cInterval.I cInterval.h \
+    cIntervalManager.cxx cIntervalManager.I cIntervalManager.h \
     cLerpInterval.cxx cLerpInterval.I cLerpInterval.h \
     cLerpNodePathInterval.cxx cLerpNodePathInterval.I cLerpNodePathInterval.h \
     cLerpAnimEffectInterval.cxx cLerpAnimEffectInterval.I cLerpAnimEffectInterval.h \

+ 68 - 12
direct/src/interval/cInterval.I

@@ -78,18 +78,6 @@ is_stopped() const {
   return (_state == S_initial || _state == S_final);
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: CInterval::get_t
-//       Access: Published
-//  Description: Returns the current time of the interval: the last
-//               value of t passed to priv_initialize(), priv_step(), or
-//               priv_finalize().
-////////////////////////////////////////////////////////////////////
-INLINE double CInterval::
-get_t() const {
-  return _curr_t;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: CInterval::set_done_event
 //       Access: Published
@@ -116,6 +104,74 @@ get_done_event() const {
   return _done_event;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: CInterval::get_t
+//       Access: Published
+//  Description: Returns the current time of the interval: the last
+//               value of t passed to priv_initialize(), priv_step(), or
+//               priv_finalize().
+////////////////////////////////////////////////////////////////////
+INLINE double CInterval::
+get_t() const {
+  return _curr_t;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CInterval::set_interruptible
+//       Access: Published
+//  Description: Changes the state of the 'interruptible' flag.  If
+//               this is true, the interval may be arbitrarily
+//               interrupted when the system needs to reset due to
+//               some external event by calling
+//               CIntervalManager::pause_all_interruptible().  If this
+//               is false (the default), the interval must always be
+//               explicitly finished or paused.
+////////////////////////////////////////////////////////////////////
+INLINE void CInterval::
+set_interruptible(bool interruptible) {
+  _interruptible = interruptible;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CInterval::get_interruptible
+//       Access: Published
+//  Description: Returns the state of the 'interruptible' flag.  See
+//               set_interruptible().
+////////////////////////////////////////////////////////////////////
+INLINE bool CInterval::
+get_interruptible() const {
+  return _interruptible;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CInterval::set_manager
+//       Access: Published
+//  Description: Indicates the CIntervalManager object which will be
+//               responsible for playing this interval.  This defaults
+//               to the global CIntervalManager; you should need to
+//               change this only if you have special requirements for
+//               playing this interval.
+////////////////////////////////////////////////////////////////////
+INLINE void CInterval::
+set_manager(CIntervalManager *manager) {
+  _manager = manager;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CInterval::get_manager
+//       Access: Published
+//  Description: Returns the CIntervalManager object which will be
+//               responsible for playing this interval.  Note that
+//               this can only return a C++ object; if the particular
+//               CIntervalManager object has been extended in the
+//               scripting language, this will return the encapsulated
+//               C++ object, not the full extended object.
+////////////////////////////////////////////////////////////////////
+INLINE CIntervalManager *CInterval::
+get_manager() const {
+  return _manager;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: CInterval::recompute
 //       Access: Protected

+ 303 - 157
direct/src/interval/cInterval.cxx

@@ -17,6 +17,7 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "cInterval.h"
+#include "cIntervalManager.h"
 #include "indent.h"
 #include "clockObject.h"
 #include "throw_event.h"
@@ -38,198 +39,157 @@ CInterval(const string &name, double duration, bool open_ended) :
   _open_ended(open_ended),
   _dirty(false)
 {
+  _interruptible = false;
+  _manager = CIntervalManager::get_global_ptr();
   _clock_start = 0.0;
   _start_t = 0.0;
   _end_t = _duration;
   _start_t_at_start = true;
   _end_t_at_end = true;
   _play_rate = 1.0;
+  _do_loop = false;
   _loop_count = 0;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: CInterval::setup_play
+//     Function: CInterval::set_t
 //       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.
+//  Description: Explicitly sets the time within the interval.
+//               Normally, you would use start() .. finish() to let
+//               the time play normally, but this may be used to set
+//               the time to some particular value.
 ////////////////////////////////////////////////////////////////////
 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);
+set_t(double t) {
+  t = min(max(t, 0.0), get_duration());
+  switch (get_state()) {
+  case S_initial:
+    priv_initialize(t);
+    break;
 
-  double duration = get_duration();
+  case S_final:
+    priv_reverse_initialize(t);
+    break;
 
-  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;
+  default:
+    priv_step(t);
   }
+}
 
-  _clock_start = ClockObject::get_global_clock()->get_frame_time();
-  _play_rate = play_rate;
-  _loop_count = 0;
+////////////////////////////////////////////////////////////////////
+//     Function: CInterval::start
+//       Access: Published
+//  Description: Starts the interval playing by registering it with
+//               the current CIntervalManager.  The interval will
+//               play to the end and stop.
+//
+//               If end_t is less than zero, it indicates the end of
+//               the interval.
+////////////////////////////////////////////////////////////////////
+void CInterval::
+start(double start_t, double end_t, double play_rate) {
+  setup_play(start_t, end_t, play_rate, false);
+  _manager->add_c_interval(this, false);
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: CInterval::setup_resume
+//     Function: CInterval::loop
 //       Access: Published
-//  Description: Called to prepare the interval for restarting at the
-//               current point within the interval after an
-//               interruption.
+//  Description: Starts the interval playing by registering it with
+//               the current CIntervalManager.  The interval will
+//               play until it is interrupted with finish() or
+//               pause(), looping back to start_t when it reaches
+//               end_t.
+//
+//               If end_t is less than zero, it indicates the end of
+//               the interval.
 ////////////////////////////////////////////////////////////////////
 void CInterval::
-setup_resume() {
-  double now = ClockObject::get_global_clock()->get_frame_time();
-  if (_play_rate > 0.0) {
-    _clock_start = now - ((get_t() - _start_t) / _play_rate);
+loop(double start_t, double end_t, double play_rate) {
+  setup_play(start_t, end_t, play_rate, true);
+  _manager->add_c_interval(this, false);
+}
 
-  } else if (_play_rate < 0.0) {
-    _clock_start = now - ((get_t() - _end_t) / _play_rate);
+////////////////////////////////////////////////////////////////////
+//     Function: CInterval::pause
+//       Access: Published
+//  Description: Stops the interval from playing but leaves it in its
+//               current state.  It may later be resumed from this
+//               point by calling resume().
+////////////////////////////////////////////////////////////////////
+double CInterval::
+pause() {
+  if (get_state() == S_started) {
+    priv_interrupt();
   }
-  _loop_count = 0;
+  int index = _manager->find_c_interval(this->get_name());
+  if (index >= 0) {
+    _manager->remove_c_interval(index);
+  }
+  return get_t();
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: CInterval::step_play
+//     Function: CInterval::resume
 //       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.
+//  Description: Restarts the interval from its current point after a
+//               previous call to pause().
 ////////////////////////////////////////////////////////////////////
-int CInterval::
-step_play() {
-  double now = ClockObject::get_global_clock()->get_frame_time();
+void CInterval::
+resume() {
+  setup_resume();
+  _manager->add_c_interval(this, false);
+}
 
-  if (_play_rate >= 0.0) {
-    double t = (now - _clock_start) * _play_rate + _start_t;
+////////////////////////////////////////////////////////////////////
+//     Function: CInterval::resume
+//       Access: Published
+//  Description: Restarts the interval from the indicated point after a
+//               previous call to pause().
+////////////////////////////////////////////////////////////////////
+void CInterval::
+resume(double start_t) {
+  set_t(start_t);
+  setup_resume();
+  _manager->add_c_interval(this, false);
+}
 
-    if (_end_t_at_end) {
-      _end_t = get_duration();
-    }
-    
-    if (t < _end_t) {
-      // In the middle of the interval, not a problem.
-      if (is_stopped()) {
-        priv_initialize(t);
-      } else {
-        priv_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 (is_stopped()) {
-          if (get_open_ended() || _loop_count != 0) {
-            priv_instant();
-          }
-        } else {
-          priv_finalize();
-        }
-      } else {
-        if (is_stopped()) {
-          priv_initialize(_end_t);
-        } else {
-          priv_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;
-      }
-    }
+////////////////////////////////////////////////////////////////////
+//     Function: CInterval::finish
+//       Access: Published
+//  Description: Stops the interval from playing and sets it to its
+//               final state.
+////////////////////////////////////////////////////////////////////
+void CInterval::
+finish() {
+  switch (get_state()) {
+  case S_initial:
+    priv_instant();
+    break;
 
-  } 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 (is_stopped()) {
-        priv_reverse_initialize(t);
-      } else {
-        priv_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 (is_stopped()) {
-          if (get_open_ended() || _loop_count != 0) {
-            priv_reverse_instant();
-          }
-        } else {
-          priv_reverse_finalize();
-        }
-      } else {
-        if (is_stopped()) {
-          priv_reverse_initialize(_start_t);
-        } else {
-          priv_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;
-      }
-    }
+  case S_final:
+    break;
+
+  default:
+    priv_finalize();
+  }
+
+  int index = _manager->find_c_interval(this->get_name());
+  if (index >= 0) {
+    _manager->remove_c_interval(index);
   }
+}
 
-  return _loop_count;
+////////////////////////////////////////////////////////////////////
+//     Function: CInterval::is_playing
+//       Access: Published
+//  Description: Returns true if the interval is currently playing,
+//               false otherwise.
+////////////////////////////////////////////////////////////////////
+bool CInterval::
+is_playing() const {
+  int index = _manager->find_c_interval(this->get_name());
+  return (index >= 0);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -434,6 +394,192 @@ write(ostream &out, int indent_level) const {
   indent(out, indent_level) << *this << "\n";
 }
 
+////////////////////////////////////////////////////////////////////
+//     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, bool do_loop) {
+  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;
+  _do_loop = do_loop;
+  _loop_count = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CInterval::setup_resume
+//       Access: Published
+//  Description: Called to prepare the interval for restarting at the
+//               current point within the interval after an
+//               interruption.
+////////////////////////////////////////////////////////////////////
+void CInterval::
+setup_resume() {
+  double now = ClockObject::get_global_clock()->get_frame_time();
+  if (_play_rate > 0.0) {
+    _clock_start = now - ((get_t() - _start_t) / _play_rate);
+
+  } else if (_play_rate < 0.0) {
+    _clock_start = now - ((get_t() - _end_t) / _play_rate);
+  }
+  _loop_count = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CInterval::step_play
+//       Access: Published
+//  Description: Should be called once per frame to execute the
+//               automatic timed playback begun with setup_play().
+//
+//               Returns true if the interval should continue, false
+//               if it is done and should stop.
+////////////////////////////////////////////////////////////////////
+bool 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 (_end_t_at_end) {
+      _end_t = get_duration();
+    }
+    
+    if (t < _end_t) {
+      // In the middle of the interval, not a problem.
+      if (is_stopped()) {
+        priv_initialize(t);
+      } else {
+        priv_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 (is_stopped()) {
+          if (get_open_ended() || _loop_count != 0) {
+            priv_instant();
+          }
+        } else {
+          priv_finalize();
+        }
+      } else {
+        if (is_stopped()) {
+          priv_initialize(_end_t);
+        } else {
+          priv_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 (is_stopped()) {
+        priv_reverse_initialize(t);
+      } else {
+        priv_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 (is_stopped()) {
+          if (get_open_ended() || _loop_count != 0) {
+            priv_reverse_instant();
+          }
+        } else {
+          priv_reverse_finalize();
+        }
+      } else {
+        if (is_stopped()) {
+          priv_reverse_initialize(_start_t);
+        } else {
+          priv_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 == 0 || _do_loop);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: CInterval::mark_dirty
 //       Access: Public

+ 28 - 5
direct/src/interval/cInterval.h

@@ -24,6 +24,8 @@
 #include "pvector.h"
 #include "config_interval.h"
 
+class CIntervalManager;
+
 ////////////////////////////////////////////////////////////////////
 //       Class : CInterval
 // Description : The base class for timeline components.  A CInterval
@@ -66,14 +68,26 @@ PUBLISHED:
 
   INLINE State get_state() const;
   INLINE bool is_stopped() const;
-  INLINE double get_t() const;
 
   INLINE void set_done_event(const string &event);
   INLINE const string &get_done_event() const;
 
-  void setup_play(double start_time, double end_time, double play_rate);
-  void setup_resume();
-  int step_play();
+  void set_t(double t);
+  INLINE double get_t() const;
+
+  INLINE void set_interruptible(bool interruptible);
+  INLINE bool get_interruptible() const;
+
+  INLINE void set_manager(CIntervalManager *manager);
+  INLINE CIntervalManager *get_manager() const;
+
+  void start(double start_t = 0.0, double end_t = -1.0, double play_rate = 1.0);
+  void loop(double start_t = 0.0, double end_t = -1.0, double play_rate = 1.0);
+  double pause();
+  void resume();
+  void resume(double start_t);
+  void finish();
+  bool is_playing() const;
 
   // These functions control the actual playback of the interval.
   // Don't call them directly; they're intended to be called from a
@@ -96,6 +110,11 @@ PUBLISHED:
   virtual void output(ostream &out) const;
   virtual void write(ostream &out, int indent_level) const;
 
+  void setup_play(double start_time, double end_time, double play_rate,
+                  bool do_loop);
+  void setup_resume();
+  bool step_play();
+
 public:
   void mark_dirty();
 
@@ -113,6 +132,9 @@ protected:
   string _done_event;
   double _duration;
 
+  bool _interruptible;
+  CIntervalManager *_manager;
+
   // For setup_play() and step_play().
   double _clock_start;
   double _start_t;
@@ -120,8 +142,9 @@ protected:
   bool _end_t_at_end;
   bool _start_t_at_start;
   double _play_rate;
+  bool _do_loop;
   int _loop_count;
-
+  
 private:
   bool _open_ended;
   bool _dirty;

+ 25 - 0
direct/src/interval/cIntervalManager.I

@@ -0,0 +1,25 @@
+// Filename: cIntervalManager.I
+// Created by:  drose (10Sep02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+INLINE ostream &
+operator << (ostream &out, const CIntervalManager &ival_mgr) {
+  ival_mgr.output(out);
+  return out;
+}
+

+ 417 - 0
direct/src/interval/cIntervalManager.cxx

@@ -0,0 +1,417 @@
+// Filename: cIntervalManager.cxx
+// Created by:  drose (10Sep02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "cIntervalManager.h"
+#include "cMetaInterval.h"
+#include "dcast.h"
+
+CIntervalManager *CIntervalManager::_global_ptr;
+
+////////////////////////////////////////////////////////////////////
+//     Function: CIntervalManager::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+CIntervalManager::
+CIntervalManager() {
+  _first_slot = 0;
+  _next_event_index = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CIntervalManager::Destructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+CIntervalManager::
+~CIntervalManager() {
+  nassertv(_name_index.empty());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CIntervalManager::add_c_interval
+//       Access: Published
+//  Description: Adds the interval to the manager, and returns a
+//               unique index for the interval.  This index will be
+//               unique among all the currently added intervals, but
+//               not unique across all intervals ever added to the
+//               manager.  The maximum index value will never exceed
+//               the maximum number of intervals added at any given
+//               time.
+//
+//               If the external flag is true, the interval is
+//               understood to also be stored in the scripting
+//               language data structures.  In this case, it will be
+//               available for information returned by
+//               get_next_event() and get_next_removal().  If external
+//               is false, the interval's index will never be returned
+//               by these two functions.
+////////////////////////////////////////////////////////////////////
+int CIntervalManager::
+add_c_interval(CInterval *interval, bool external) {
+  // First, check the name index.  If we already have an interval by
+  // this name, it gets finished and removed.
+  NameIndex::iterator ni = _name_index.find(interval->get_name());
+  if (ni != _name_index.end()) {
+    int old_index = (*ni).second;
+    nassertr(old_index >= 0 && old_index < (int)_intervals.size(), -1)
+    CInterval *old_interval = _intervals[old_index]._interval;
+    if (old_interval == interval) {
+      // No, it's the same interval that was already here.  In this
+      // case, don't finish the interval; just return it.
+      return old_index;
+    }
+    finish_interval(old_interval);
+    remove_index(old_index);
+    _name_index.erase(ni);
+  }
+
+  int slot;
+
+  if (_first_slot >= (int)_intervals.size()) {
+    // All the slots are filled; make a new slot.
+    nassertr(_first_slot == (int)_intervals.size(), -1);
+    slot = (int)_intervals.size();
+    _intervals.push_back(IntervalDef());
+    _first_slot = (int)_intervals.size();
+
+  } else {
+    // Some slot is available; use it.
+    slot = _first_slot;
+    nassertr(slot >= 0 && slot < (int)_intervals.size(), -1);
+    _first_slot = _intervals[slot]._next_slot;
+  }
+
+  IntervalDef &def = _intervals[slot];
+  def._interval = interval;
+  def._flags = 0;
+  if (external) {
+    def._flags |= F_external;
+  }
+  if (interval->is_of_type(CMetaInterval::get_class_type())) {
+    def._flags |= F_meta_interval;
+  }
+  def._next_slot = -1;
+    
+  _name_index[interval->get_name()] = slot;
+  nassertr(_first_slot >= 0, slot);
+  return slot;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CIntervalManager::find_c_interval
+//       Access: Published
+//  Description: Returns the index associated with the named interval,
+//               if there is such an interval, or -1 if there is not.
+////////////////////////////////////////////////////////////////////
+int CIntervalManager::
+find_c_interval(const string &name) const {
+  NameIndex::const_iterator ni = _name_index.find(name);
+  if (ni != _name_index.end()) {
+    return (*ni).second;
+  }
+  return -1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CIntervalManager::get_c_interval
+//       Access: Published
+//  Description: Returns the interval associated with the given index.
+////////////////////////////////////////////////////////////////////
+CInterval *CIntervalManager::
+get_c_interval(int index) const {
+  nassertr(index >= 0 && index < (int)_intervals.size(), NULL);
+  return _intervals[index]._interval;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CIntervalManager::remove_c_interval
+//       Access: Published
+//  Description: Removes the indicated interval from the queue
+//               immediately.  It will not be returned from
+//               get_next_removal(), and none of its pending events,
+//               if any, will be returned by get_next_event().
+////////////////////////////////////////////////////////////////////
+void CIntervalManager::
+remove_c_interval(int index) {
+  nassertv(index >= 0 && index < (int)_intervals.size());
+  IntervalDef &def = _intervals[index];
+  nassertv(def._interval != (CInterval *)NULL);
+
+  NameIndex::iterator ni = _name_index.find(def._interval->get_name());
+  nassertv(ni != _name_index.end());
+  nassertv((*ni).second == index);
+  _name_index.erase(ni);
+
+  def._interval = (CInterval *)NULL;
+  def._next_slot = _first_slot;
+  _first_slot = index;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CIntervalManager::pause_all_interruptible
+//       Access: Published
+//  Description: Pauses (removes from the active queue) all intervals
+//               tagged as "interruptible".  These are intervals that
+//               someone fired up but won't necessarily expect to
+//               clean up; they can be interrupted at will when
+//               necessary.
+//
+//               Returns the number of intervals affected.
+////////////////////////////////////////////////////////////////////
+int CIntervalManager::
+pause_all_interruptible() {
+  int num_paused = 0;
+
+  NameIndex::iterator ni;
+  ni = _name_index.begin();
+  while (ni != _name_index.end()) {
+    int index = (*ni).second;
+    const IntervalDef &def = _intervals[index];
+    nassertr(def._interval != (CInterval *)NULL, num_paused);
+    if (!def._interval->get_interruptible()) {
+      // This interval may be interrupted.
+      if (def._interval->get_state() == CInterval::S_started) {
+        def._interval->priv_interrupt();
+      }
+      NameIndex::iterator prev;
+      prev = ni;
+      ++ni;
+      _name_index.erase(prev);
+      remove_index(index);
+      num_paused++;
+
+    } else {
+      // The interval should remain on the active list.
+      ++ni;
+    }
+  }
+
+  return num_paused;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CIntervalManager::get_num_intervals
+//       Access: Published
+//  Description: Returns the number of currently active intervals.
+////////////////////////////////////////////////////////////////////
+int CIntervalManager::
+get_num_intervals() const {
+  return _name_index.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CIntervalManager::step
+//       Access: Published
+//  Description: This should be called every frame to do the
+//               processing for all the active intervals.  It will
+//               call step_play() for each interval that has been
+//               added and that has not yet been removed.
+//
+//               After each call to step(), the scripting language
+//               should call get_next_event() and get_next_removal()
+//               repeatedly to process all the high-level
+//               (e.g. Python-interval-based) events and to manage the
+//               high-level list of intervals.
+////////////////////////////////////////////////////////////////////
+void CIntervalManager::
+step() {
+  NameIndex::iterator ni;
+  ni = _name_index.begin();
+  while (ni != _name_index.end()) {
+    int index = (*ni).second;
+    const IntervalDef &def = _intervals[index];
+    nassertv(def._interval != (CInterval *)NULL);
+    if (!def._interval->step_play()) {
+      // This interval is finished and wants to be removed from the
+      // active list.
+      NameIndex::iterator prev;
+      prev = ni;
+      ++ni;
+      _name_index.erase(prev);
+      remove_index(index);
+
+    } else {
+      // The interval can remain on the active list.
+      ++ni;
+    }
+  }
+
+  _next_event_index = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CIntervalManager::get_next_event
+//       Access: Published
+//  Description: This should be called by the scripting language after
+//               each call to step().  It returns the index number of
+//               the next interval that has events requiring servicing
+//               by the scripting language, or -1 if no more intervals
+//               have any events pending.
+//
+//               If this function returns something other than -1, it
+//               is the scripting language's responsibility to query
+//               the indicated interval for its next event via
+//               get_event_index(), and eventually pop_event().
+//
+//               Then get_next_event() should be called again until it
+//               returns -1.
+////////////////////////////////////////////////////////////////////
+int CIntervalManager::
+get_next_event() {
+  while (_next_event_index < (int)_intervals.size()) {
+    IntervalDef &def = _intervals[_next_event_index];
+    if (def._interval != (CInterval *)NULL && 
+        (def._flags & F_meta_interval) != 0) {
+      CMetaInterval *meta_interval;
+      DCAST_INTO_R(meta_interval, def._interval, -1);
+      if (meta_interval->is_event_ready()) {
+        nassertr((def._flags & F_external) != 0, -1);
+        return _next_event_index;
+      }
+    }
+    _next_event_index++;
+  }
+
+  return -1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CIntervalManager::get_next_removal
+//       Access: Published
+//  Description: This should be called by the scripting language after
+//               each call to step().  It returns the index number of
+//               an interval that was recently removed, or -1 if no
+//               intervals were removed.
+//
+//               If this returns something other than -1, the
+//               scripting language should clean up its own data
+//               structures accordingly, and then call
+//               get_next_removal() again.
+////////////////////////////////////////////////////////////////////
+int CIntervalManager::
+get_next_removal() {
+  if (!_removed.empty()) {
+    int index = _removed.back();
+    _removed.pop_back();
+
+    nassertr(index >= 0 && index < (int)_intervals.size(), -1);
+    IntervalDef &def = _intervals[index];
+    def._interval = (CInterval *)NULL;
+    def._next_slot = _first_slot;
+    _first_slot = index;
+    return index;
+  }
+
+  return -1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CIntervalManager::output
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void CIntervalManager::
+output(ostream &out) const {
+  out << "CIntervalManager, " << (int)_name_index.size() << " intervals.";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CIntervalManager::write
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void CIntervalManager::
+write(ostream &out) const {
+  NameIndex::const_iterator ni;
+  for (ni = _name_index.begin(); ni != _name_index.end(); ++ni) {
+    int index = (*ni).second;
+    nassertv(index >= 0 && index < (int)_intervals.size());
+    const IntervalDef &def = _intervals[index];
+    nassertv(def._interval != (CInterval *)NULL);
+    out << *def._interval << "\n";
+  }
+
+  if (!_removed.empty()) {
+    out << "\nRemoved:\n";
+    Removed::const_iterator ri;
+    for (ri = _removed.begin(); ri != _removed.end(); ++ri) {
+      int index = (*ri);
+      nassertv(index >= 0 && index < (int)_intervals.size());
+      const IntervalDef &def = _intervals[index];
+      nassertv(def._interval != (CInterval *)NULL);
+      out << "(R)" << *def._interval << "\n";
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CIntervalManager::get_global_ptr
+//       Access: Published, Static
+//  Description: Returns the pointer to the one global
+//               CIntervalManager object.
+////////////////////////////////////////////////////////////////////
+CIntervalManager *CIntervalManager::
+get_global_ptr() {
+  if (_global_ptr == (CIntervalManager *)NULL) {
+    _global_ptr = new CIntervalManager;
+  }
+  return _global_ptr;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CIntervalManager::finish_interval
+//       Access: Private
+//  Description: Explicitly finishes the indicated interval in
+//               preparation for moving it to the removed queue.
+////////////////////////////////////////////////////////////////////
+void CIntervalManager::
+finish_interval(CInterval *interval) {
+  switch (interval->get_state()) {
+  case CInterval::S_initial:
+    interval->priv_instant();
+    break;
+
+  case CInterval::S_final:
+    break;
+
+  default:
+    interval->priv_finalize();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CIntervalManager::remove_index
+//       Access: Private
+//  Description: Removes the indicated index number from the active
+//               list, either by moving it to the removed queue if it
+//               is flagged external, or by simply making the slot
+//               available again if it is not.
+////////////////////////////////////////////////////////////////////
+void CIntervalManager::
+remove_index(int index) {
+  nassertv(index >= 0 && index < (int)_intervals.size());
+  IntervalDef &def = _intervals[index];
+  if ((def._flags & F_external) != 0) {
+    _removed.push_back(index);
+  } else {
+    def._interval = (CInterval *)NULL;
+    def._next_slot = _first_slot;
+    _first_slot = index;
+  }    
+}

+ 101 - 0
direct/src/interval/cIntervalManager.h

@@ -0,0 +1,101 @@
+// Filename: cIntervalManager.h
+// Created by:  drose (10Sep02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 CINTERVALMANAGER_H
+#define CINTERVALMANAGER_H
+
+#include "directbase.h"
+#include "cInterval.h"
+#include "pointerTo.h"
+#include "pvector.h"
+#include "pmap.h"
+#include "vector_int.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : CIntervalManager
+// Description : This object holds a number of currently-playing
+//               intervals and is responsible for advancing them each
+//               frame as needed.
+//
+//               There is normally only one IntervalManager object in
+//               the world, and it is the responsibility of the
+//               scripting language to call step() on this object once
+//               each frame, and to then process the events indicated by
+//               get_next_event().  
+//
+//               It is also possible to create multiple
+//               IntervalManager objects for special needs.
+////////////////////////////////////////////////////////////////////
+class EXPCL_DIRECT CIntervalManager {
+PUBLISHED:
+  CIntervalManager();
+  ~CIntervalManager();
+
+  int add_c_interval(CInterval *interval, bool external);
+  int find_c_interval(const string &name) const;
+
+  CInterval *get_c_interval(int index) const;
+  void remove_c_interval(int index);
+
+  int pause_all_interruptible();
+  int get_num_intervals() const;
+
+  void step();
+  int get_next_event();
+  int get_next_removal();
+
+  void output(ostream &out) const;
+  void write(ostream &out) const;
+
+  static CIntervalManager *get_global_ptr();
+
+private:
+  void finish_interval(CInterval *interval);
+  void remove_index(int index);
+
+  enum Flags {
+    F_external      = 0x0001,
+    F_meta_interval = 0x0002,
+  };
+  class IntervalDef {
+  public:
+    PT(CInterval) _interval;
+    int _flags;
+    int _next_slot;
+  };
+  typedef pvector<IntervalDef> Intervals;
+  Intervals _intervals;
+  typedef pmap<string, int> NameIndex;
+  NameIndex _name_index;
+  typedef vector_int Removed;
+  Removed _removed;
+
+  int _first_slot;
+  int _next_event_index;
+
+  static CIntervalManager *_global_ptr;
+};
+
+INLINE ostream &operator << (ostream &out, const CInterval &ival_mgr);
+
+#include "cIntervalManager.I"
+
+#endif
+
+
+

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

@@ -444,6 +444,14 @@ priv_step(double t) {
   check_started("priv_step");
   int now = double_to_int_time(t);
 
+  // One special case: if we step to t == 0.0, it really means to the
+  // very beginning of the interval, *before* any events that occurred
+  // at time 0.  (Most of the time, stepping to a particular time
+  // means *after* any events that occurred at that time.)
+  if (t == 0.0) {
+    now = -1;
+  }
+
   // Now look for events between the last time we ran and the current
   // time.
 

+ 10 - 0
direct/src/showbase/ShowBase.py

@@ -12,6 +12,7 @@ from EventManagerGlobal import *
 from PythonUtil import *
 from ParticleManagerGlobal import *
 from PhysicsManagerGlobal import *
+from IntervalManager import ivalMgr
 
 import Task
 import EventManager
@@ -543,6 +544,11 @@ class ShowBase:
         self.dgTrav.traverse(self.dataRootNode)
         return Task.cont
 
+    def ivalloop(self, state):
+        # Execute all intervals in the global ivalMgr.
+        ivalMgr.step()
+        return Task.cont
+
     def igloop(self, state):
         # run the collision traversal if we have a
         # CollisionTraverser set.
@@ -567,11 +573,15 @@ class ShowBase:
         # give the dataloop task a reasonably "early" priority,
         # so that it will get run before most tasks
         self.taskMgr.add(self.dataloop, 'dataloop', priority = -50)
+        # spawn the ivalloop with a later priority, so that it will
+        # run after most tasks, but before igloop.
+        self.taskMgr.add(self.ivalloop, 'ivalloop', priority = 10)
         self.eventMgr.restart()
 
     def shutdown(self):
         self.taskMgr.remove('igloop')
         self.taskMgr.remove('dataloop')
+        self.taskMgr.remove('ivalloop')
         self.eventMgr.shutdown()
 
     def getBackgroundColor(self):