From 24f194bd45ce6daebc832ae4d66f64f8bde52d1a Mon Sep 17 00:00:00 2001 From: David Rose Date: Wed, 11 Sep 2002 15:59:22 +0000 Subject: [PATCH] define ivalMgr --- direct/src/extensions/CInterval-extensions.py | 85 +--- direct/src/ffi/FFIRename.py | 1 + direct/src/interval/ActorInterval.py | 1 + direct/src/interval/FunctionInterval.py | 1 + direct/src/interval/Interval.py | 117 +++-- direct/src/interval/IntervalGlobal.py | 1 + direct/src/interval/MetaInterval.py | 71 ++- direct/src/interval/MopathInterval.py | 1 + direct/src/interval/ParticleInterval.py | 1 + direct/src/interval/SoundInterval.py | 4 + direct/src/interval/Sources.pp | 1 + direct/src/interval/cInterval.I | 80 ++- direct/src/interval/cInterval.cxx | 466 ++++++++++++------ direct/src/interval/cInterval.h | 33 +- direct/src/interval/cIntervalManager.I | 25 + direct/src/interval/cIntervalManager.cxx | 417 ++++++++++++++++ direct/src/interval/cIntervalManager.h | 101 ++++ direct/src/interval/cMetaInterval.cxx | 8 + direct/src/showbase/ShowBase.py | 10 + 19 files changed, 1093 insertions(+), 331 deletions(-) create mode 100644 direct/src/interval/cIntervalManager.I create mode 100644 direct/src/interval/cIntervalManager.cxx create mode 100644 direct/src/interval/cIntervalManager.h diff --git a/direct/src/extensions/CInterval-extensions.py b/direct/src/extensions/CInterval-extensions.py index 22577f1fbf..34a3ceb999 100644 --- a/direct/src/extensions/CInterval-extensions.py +++ b/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. diff --git a/direct/src/ffi/FFIRename.py b/direct/src/ffi/FFIRename.py index a04b6fb99d..b058a7344f 100644 --- a/direct/src/ffi/FFIRename.py +++ b/direct/src/ffi/FFIRename.py @@ -44,6 +44,7 @@ methodRenameDictionary = { 'operator<<=' : '__ilshift__', 'operator>>=' : '__irshift__', 'print' : 'Cprint', + 'CInterval.setT' : '__cSetT', } classRenameDictionary = { diff --git a/direct/src/interval/ActorInterval.py b/direct/src/interval/ActorInterval.py index d2dbca23fb..1f2eff6a34 100644 --- a/direct/src/interval/ActorInterval.py +++ b/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 diff --git a/direct/src/interval/FunctionInterval.py b/direct/src/interval/FunctionInterval.py index 3f46964e61..ebb3ffc7fa 100644 --- a/direct/src/interval/FunctionInterval.py +++ b/direct/src/interval/FunctionInterval.py @@ -2,6 +2,7 @@ from PandaModules import * from MessengerGlobal import * +from DirectNotifyGlobal import * import Interval import types diff --git a/direct/src/interval/Interval.py b/direct/src/interval/Interval.py index c552f2158a..a82c8701d9 100644 --- a/direct/src/interval/Interval.py +++ b/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 diff --git a/direct/src/interval/IntervalGlobal.py b/direct/src/interval/IntervalGlobal.py index 453f317b04..848366ce62 100644 --- a/direct/src/interval/IntervalGlobal.py +++ b/direct/src/interval/IntervalGlobal.py @@ -10,3 +10,4 @@ from ParticleInterval import * from SoundInterval import * from WaitInterval import * from MetaInterval import * +from IntervalManager import * diff --git a/direct/src/interval/MetaInterval.py b/direct/src/interval/MetaInterval.py index 1de7573f9a..9ac057c93d 100644 --- a/direct/src/interval/MetaInterval.py +++ b/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. diff --git a/direct/src/interval/MopathInterval.py b/direct/src/interval/MopathInterval.py index 49eb0e6a96..82fba9c325 100644 --- a/direct/src/interval/MopathInterval.py +++ b/direct/src/interval/MopathInterval.py @@ -2,6 +2,7 @@ import Interval from PandaModules import * +from DirectNotifyGlobal import * # import Mopath diff --git a/direct/src/interval/ParticleInterval.py b/direct/src/interval/ParticleInterval.py index e7112443e8..84746292ca 100644 --- a/direct/src/interval/ParticleInterval.py +++ b/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 diff --git a/direct/src/interval/SoundInterval.py b/direct/src/interval/SoundInterval.py index 105746036f..5b8d68513c 100644 --- a/direct/src/interval/SoundInterval.py +++ b/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 diff --git a/direct/src/interval/Sources.pp b/direct/src/interval/Sources.pp index 678662e9fa..17e3aa2e1a 100644 --- a/direct/src/interval/Sources.pp +++ b/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 \ diff --git a/direct/src/interval/cInterval.I b/direct/src/interval/cInterval.I index 22b8d950fd..652774e332 100644 --- a/direct/src/interval/cInterval.I +++ b/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 diff --git a/direct/src/interval/cInterval.cxx b/direct/src/interval/cInterval.cxx index ea41c606e4..cd1e00ec46 100644 --- a/direct/src/interval/cInterval.cxx +++ b/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; + default: + priv_step(t); } - 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; } //////////////////////////////////////////////////////////////////// -// Function: CInterval::setup_resume +// Function: CInterval::start // 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 to the end and stop. +// +// 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); - - } else if (_play_rate < 0.0) { - _clock_start = now - ((get_t() - _end_t) / _play_rate); - } - _loop_count = 0; +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::step_play +// Function: CInterval::loop // 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: 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. //////////////////////////////////////////////////////////////////// -int CInterval:: -step_play() { - double now = ClockObject::get_global_clock()->get_frame_time(); +void CInterval:: +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); +} - if (_play_rate >= 0.0) { - double t = (now - _clock_start) * _play_rate + _start_t; +//////////////////////////////////////////////////////////////////// +// 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(); + } + int index = _manager->find_c_interval(this->get_name()); + if (index >= 0) { + _manager->remove_c_interval(index); + } + return get_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; - } - } +//////////////////////////////////////////////////////////////////// +// Function: CInterval::resume +// Access: Published +// Description: Restarts the interval from its current point after a +// previous call to pause(). +//////////////////////////////////////////////////////////////////// +void CInterval:: +resume() { + setup_resume(); + _manager->add_c_interval(this, false); +} - } 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; - } - } +//////////////////////////////////////////////////////////////////// +// 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); +} + +//////////////////////////////////////////////////////////////////// +// 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; + + case S_final: + break; + + default: + priv_finalize(); } - return _loop_count; + int index = _manager->find_c_interval(this->get_name()); + if (index >= 0) { + _manager->remove_c_interval(index); + } +} + +//////////////////////////////////////////////////////////////////// +// 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 diff --git a/direct/src/interval/cInterval.h b/direct/src/interval/cInterval.h index da27a8e9a0..e9cc9e0602 100644 --- a/direct/src/interval/cInterval.h +++ b/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; diff --git a/direct/src/interval/cIntervalManager.I b/direct/src/interval/cIntervalManager.I new file mode 100644 index 0000000000..610797ef62 --- /dev/null +++ b/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 +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + + +INLINE ostream & +operator << (ostream &out, const CIntervalManager &ival_mgr) { + ival_mgr.output(out); + return out; +} + diff --git a/direct/src/interval/cIntervalManager.cxx b/direct/src/interval/cIntervalManager.cxx new file mode 100644 index 0000000000..0e76041eb7 --- /dev/null +++ b/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 +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#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; + } +} diff --git a/direct/src/interval/cIntervalManager.h b/direct/src/interval/cIntervalManager.h new file mode 100644 index 0000000000..8f9b60816e --- /dev/null +++ b/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 +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#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 Intervals; + Intervals _intervals; + typedef pmap 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 + + + diff --git a/direct/src/interval/cMetaInterval.cxx b/direct/src/interval/cMetaInterval.cxx index 97c70db7a2..c36c1cc240 100644 --- a/direct/src/interval/cMetaInterval.cxx +++ b/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. diff --git a/direct/src/showbase/ShowBase.py b/direct/src/showbase/ShowBase.py index 7c79742763..3aa55637fb 100644 --- a/direct/src/showbase/ShowBase.py +++ b/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):