diff --git a/direct/src/extensions/CInterval-extensions.py b/direct/src/extensions/CInterval-extensions.py index 87b05c53de..d0794dd085 100644 --- a/direct/src/extensions/CInterval-extensions.py +++ b/direct/src/extensions/CInterval-extensions.py @@ -4,11 +4,9 @@ of the CInterval class """ - def play(self, t0 = 0.0, duration = None, scale = 1.0): - """ play(t0, duration) - """ + def start(self, t0 = 0.0, duration = None, scale = 1.0): if self.isPlaying(): - self.stop() + self.finish() if duration: # None or 0 implies full length self.setupPlay(t0, t0 + duration, scale) else: @@ -17,7 +15,7 @@ self.resume() def loop(self, t0 = 0.0, duration = None, scale = 1.0): - self.play(t0, duration, scale) + self.start(t0, duration, scale) self.__loop = 1 return @@ -31,8 +29,8 @@ # Spawn task taskMgr.add(self.__playTask, self.getName() + '-play') - def stop(self): - # Nowadays, stop() will implicitly set the interval to its + def finish(self): + # Nowadays, finish() will implicitly set the interval to its # terminal state, like setFinalT() used to. Use pause() # instead if you just want to leave the interval in its # current state, whatever that may be. @@ -42,8 +40,14 @@ for func in self.setTHooks: func(self.getT()) + def play(self, *args, **kw): + self.start(*args, **kw) + + def stop(self): + self.finish() + def setFinalT(self): - self.stop() + self.finish() def setT(self, t, event = ETStep): # Overridden from the C++ layer. We rename the C++ function @@ -115,7 +119,7 @@ command = lambda s=self: s.pause()) play = Button( bf, text = 'Play', - command = lambda s=self, es=es: s.play(es.get())) + command = lambda s=self, es=es: s.start(es.get())) jumpToEnd = Button(bf, text = '>>', command = toEnd) jumpToStart.pack(side = LEFT, expand = 1, fill = X) play.pack(side = LEFT, expand = 1, fill = X) diff --git a/direct/src/interval/Interval.py b/direct/src/interval/Interval.py index c080596d0e..c89c565f63 100644 --- a/direct/src/interval/Interval.py +++ b/direct/src/interval/Interval.py @@ -75,22 +75,22 @@ class Interval(DirectObject): # Used by control panel to update scale pass - def play(self, t0=0.0, duration=0.0, scale=1.0): - """ play(t0, duration) - """ + def start(self, t0=0.0, duration=0.0, scale=1.0): + # Starts playing the interval from the beginning, or at the + # indicated time if specified. + # Make sure the start time is sensible. if t0 > self.duration: t0 = self.duration # Kill ongoing play task if self.isPlaying(): - self.stop() + self.finish() # Start new one self.offset = t0 self.startT = globalClock.getFrameTime() assert(scale > 0.0) self.scale = scale - self.vernier = 0.0 self.firstTime = 1 if (duration == 0.0): # If no play duration specified, use duration of Interval @@ -105,7 +105,7 @@ class Interval(DirectObject): def loop(self, t0=0.0, duration=0.0, scale=1.0): self.accept(self.name + '-loop', self.play, extraArgs=[t0, duration, scale]) - self.play(t0, duration, scale) + self.start(t0, duration, scale) return def pause(self): @@ -121,16 +121,22 @@ class Interval(DirectObject): # Spawn task taskMgr.add(self.__playTask, self.getName() + '-play') - def stop(self): - # Nowadays, stop() will implicitly set the interval to its + def finish(self): + # Nowadays, finish() will implicitly set the interval to its # terminal state, like setFinalT() used to. Use pause() # instead if you just want to leave the interval in its # current state, whatever that may be. self.pause() self.setT(self.getDuration(), IVAL_DONE) + def play(self, *args, **kw): + self.start(*args, **kw) + + def stop(self): + self.finish() + def setFinalT(self): - self.stop() + self.finish() def isPlaying(self): return taskMgr.hasTaskNamed(self.name + '-play') @@ -139,7 +145,7 @@ class Interval(DirectObject): """ __playTask(task) """ t = globalClock.getFrameTime() - te = self.offset + ((t - self.startT) * self.scale + self.vernier) + te = self.offset + ((t - self.startT) * self.scale) if (te < self.endTime): if (self.firstTime): # If first call, init intervals @@ -215,7 +221,7 @@ class Interval(DirectObject): command = lambda s=self: s.pause()) play = Button( bf, text = 'Play', - command = lambda s=self, es=es: s.play(es.get())) + command = lambda s=self, es=es: s.start(es.get())) jumpToEnd = Button(bf, text = '>>', command = toEnd) jumpToStart.pack(side = LEFT, expand = 1, fill = X) play.pack(side = LEFT, expand = 1, fill = X) diff --git a/direct/src/interval/MetaInterval.py b/direct/src/interval/MetaInterval.py index b759d1c413..f41719e69a 100644 --- a/direct/src/interval/MetaInterval.py +++ b/direct/src/interval/MetaInterval.py @@ -319,11 +319,11 @@ class MetaInterval(CMetaInterval): CMetaInterval.interrupt(self) self.__doPythonCallbacks() - def stop(self): + def finish(self): # This function overrides from the parent level to check for Python # callbacks afterwards. self.__updateIvals() - CMetaInterval.stop(self) + CMetaInterval.finish(self) self.__doPythonCallbacks() def setFinalT(self): @@ -360,12 +360,12 @@ class MetaInterval(CMetaInterval): self.__updateIvals() return CMetaInterval.getDuration(self) - def play(self, *args, **kw): + 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.play(self, *args, **kw) + return CMetaInterval.start(self, *args, **kw) def loop(self, *args, **kw): # This function overrides from the parent level to force it to diff --git a/direct/src/interval/cInterval.I b/direct/src/interval/cInterval.I index de821fe123..58b58ce118 100644 --- a/direct/src/interval/cInterval.I +++ b/direct/src/interval/cInterval.I @@ -54,6 +54,18 @@ get_open_ended() const { return _open_ended; } +//////////////////////////////////////////////////////////////////// +// Function: CInterval::get_state +// Access: Published +// Description: Indicates the state the interval believes it is in: +// whether it has been started, is currently in the +// middle, or has been finalized. +//////////////////////////////////////////////////////////////////// +INLINE CInterval::State CInterval:: +get_state() const { + return _state; +} + //////////////////////////////////////////////////////////////////// // Function: CInterval::set_t // Access: Published @@ -123,6 +135,38 @@ recompute() const { } } +//////////////////////////////////////////////////////////////////// +// Function: CInterval::check_stopped +// Access: Protected +// Description: Issues a warning if our internal state is not in +// one of the stopped states. +//////////////////////////////////////////////////////////////////// +INLINE void CInterval:: +check_stopped(const char *method_name) const { + if (_state != S_initial && _state != S_final) { + interval_cat.warning() + << get_name() << "." << method_name << "() called in state " + << _state << ".\n"; + nassertv(!verify_intervals); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CInterval::check_started +// Access: Protected +// Description: Issues a warning if our internal state is not in +// one of the started states. +//////////////////////////////////////////////////////////////////// +INLINE void CInterval:: +check_started(const char *method_name) const { + if (_state != S_started && _state != S_paused) { + interval_cat.warning() + << get_name() << "." << method_name << "() called in state " + << _state << ".\n"; + nassertv(!verify_intervals); + } +} + INLINE ostream & operator << (ostream &out, const CInterval &ival) { ival.output(out); diff --git a/direct/src/interval/cInterval.cxx b/direct/src/interval/cInterval.cxx index 2f395a7744..e9cae12b7f 100644 --- a/direct/src/interval/cInterval.cxx +++ b/direct/src/interval/cInterval.cxx @@ -30,6 +30,7 @@ TypeHandle CInterval::_type_handle; //////////////////////////////////////////////////////////////////// CInterval:: CInterval(const string &name, double duration, bool open_ended) : + _state(S_initial), _curr_t(0.0), _name(name), _duration(duration), @@ -225,7 +226,9 @@ step_play() { //////////////////////////////////////////////////////////////////// void CInterval:: initialize(double t) { + check_stopped("initialize"); recompute(); + _state = S_started; step(t); } @@ -239,8 +242,11 @@ initialize(double t) { //////////////////////////////////////////////////////////////////// void CInterval:: instant() { + check_stopped("instant"); recompute(); + _state = S_started; step(get_duration()); + _state = S_final; } //////////////////////////////////////////////////////////////////// @@ -252,6 +258,8 @@ instant() { //////////////////////////////////////////////////////////////////// void CInterval:: step(double t) { + check_started("step"); + _state = S_started; _curr_t = t; } @@ -265,7 +273,13 @@ step(double t) { //////////////////////////////////////////////////////////////////// void CInterval:: finalize() { - step(get_duration()); + double duration = get_duration(); + if (_state == S_initial) { + initialize(duration); + } + + step(duration); + _state = S_final; } //////////////////////////////////////////////////////////////////// @@ -278,7 +292,9 @@ finalize() { //////////////////////////////////////////////////////////////////// void CInterval:: reverse_initialize(double t) { + check_stopped("reverse_initialize"); recompute(); + _state = S_started; step(t); } @@ -293,8 +309,11 @@ reverse_initialize(double t) { //////////////////////////////////////////////////////////////////// void CInterval:: reverse_instant() { + check_stopped("reverse_instant"); recompute(); + _state = S_started; step(0.0); + _state = S_initial; } //////////////////////////////////////////////////////////////////// @@ -306,7 +325,12 @@ reverse_instant() { //////////////////////////////////////////////////////////////////// void CInterval:: reverse_finalize() { + if (_state == S_initial) { + initialize(0.0); + } + step(0.0); + _state = S_initial; } //////////////////////////////////////////////////////////////////// @@ -325,6 +349,9 @@ reverse_finalize() { //////////////////////////////////////////////////////////////////// void CInterval:: interrupt() { + if (_state == S_started) { + _state = S_paused; + } } //////////////////////////////////////////////////////////////////// @@ -379,3 +406,23 @@ void CInterval:: do_recompute() { _dirty = false; } + +ostream & +operator << (ostream &out, CInterval::State state) { + switch (state) { + case CInterval::S_initial: + return out << "initial"; + + case CInterval::S_started: + return out << "started"; + + case CInterval::S_paused: + return out << "paused"; + + case CInterval::S_final: + return out << "final"; + } + + return out << "**invalid state(" << (int)state << ")**"; +} + diff --git a/direct/src/interval/cInterval.h b/direct/src/interval/cInterval.h index 15b6b5dc67..dd13b98dc5 100644 --- a/direct/src/interval/cInterval.h +++ b/direct/src/interval/cInterval.h @@ -22,6 +22,7 @@ #include "directbase.h" #include "typedReferenceCount.h" #include "pvector.h" +#include "config_interval.h" //////////////////////////////////////////////////////////////////// // Class : CInterval @@ -56,6 +57,15 @@ PUBLISHED: ET_interrupt }; + enum State { + S_initial, + S_started, + S_paused, + S_final + }; + + INLINE State get_state() const; + INLINE void set_t(double t, EventType event = ET_step); INLINE double get_t() const; @@ -63,6 +73,9 @@ PUBLISHED: int step_play(); // These functions control the actual playback of the interval. + // Don't call them directly; they're intended to be called from a + // supervising object, e.g. the Python start() .. finish() + // interface. virtual void initialize(double t); virtual void instant(); virtual void step(double t); @@ -81,7 +94,10 @@ public: protected: INLINE void recompute() const; virtual void do_recompute(); + INLINE void check_stopped(const char *method_name) const; + INLINE void check_started(const char *method_name) const; + State _state; double _curr_t; string _name; double _duration; @@ -128,6 +144,7 @@ private: }; INLINE ostream &operator << (ostream &out, const CInterval &ival); +ostream &operator << (ostream &out, CInterval::State state); #include "cInterval.I" diff --git a/direct/src/interval/cLerpAnimEffectInterval.cxx b/direct/src/interval/cLerpAnimEffectInterval.cxx index 4744be4518..9f862fbba0 100644 --- a/direct/src/interval/cLerpAnimEffectInterval.cxx +++ b/direct/src/interval/cLerpAnimEffectInterval.cxx @@ -31,6 +31,8 @@ TypeHandle CLerpAnimEffectInterval::_type_handle; //////////////////////////////////////////////////////////////////// void CLerpAnimEffectInterval:: step(double t) { + check_started("step"); + _state = S_started; double d = compute_delta(t); Controls::iterator ci; diff --git a/direct/src/interval/cLerpNodePathInterval.cxx b/direct/src/interval/cLerpNodePathInterval.cxx index e7e6cb4853..3c0bb3f71a 100644 --- a/direct/src/interval/cLerpNodePathInterval.cxx +++ b/direct/src/interval/cLerpNodePathInterval.cxx @@ -64,8 +64,10 @@ CLerpNodePathInterval(const string &name, double duration, //////////////////////////////////////////////////////////////////// void CLerpNodePathInterval:: initialize(double t) { + check_stopped("initialize"); recompute(); _prev_d = 0.0; + _state = S_started; step(t); } @@ -79,9 +81,12 @@ initialize(double t) { //////////////////////////////////////////////////////////////////// void CLerpNodePathInterval:: instant() { + check_stopped("instant"); recompute(); _prev_d = 0.0; + _state = S_started; step(get_duration()); + _state = S_final; } //////////////////////////////////////////////////////////////////// @@ -93,6 +98,8 @@ instant() { //////////////////////////////////////////////////////////////////// void CLerpNodePathInterval:: step(double t) { + check_started("step"); + _state = S_started; double d = compute_delta(t); if ((_flags & (F_end_pos | F_end_hpr | F_end_scale)) != 0) { @@ -278,7 +285,9 @@ step(double t) { //////////////////////////////////////////////////////////////////// void CLerpNodePathInterval:: reverse_initialize(double t) { + check_stopped("reverse_initialize"); recompute(); + _state = S_started; _prev_d = 1.0; step(t); } @@ -294,9 +303,12 @@ reverse_initialize(double t) { //////////////////////////////////////////////////////////////////// void CLerpNodePathInterval:: reverse_instant() { + check_stopped("reverse_initialize"); recompute(); + _state = S_started; _prev_d = 1.0; step(0.0); + _state = S_initial; } //////////////////////////////////////////////////////////////////// diff --git a/direct/src/interval/cMetaInterval.cxx b/direct/src/interval/cMetaInterval.cxx index 2d2f16eb58..d0ce437e9d 100644 --- a/direct/src/interval/cMetaInterval.cxx +++ b/direct/src/interval/cMetaInterval.cxx @@ -330,6 +330,7 @@ get_interval_end_time(const string &name) const { //////////////////////////////////////////////////////////////////// void CMetaInterval:: initialize(double t) { + check_stopped("initialize"); // It may be tempting to flush the event_queue here, but don't do // it. Those are events that must still be serviced from some // previous interval operation. Throwing them away would be a @@ -354,6 +355,7 @@ initialize(double t) { finish_events_forward(now, new_active); _curr_t = t; + _state = S_started; } //////////////////////////////////////////////////////////////////// @@ -366,6 +368,7 @@ initialize(double t) { //////////////////////////////////////////////////////////////////// void CMetaInterval:: instant() { + check_stopped("instant"); recompute(); _active.clear(); @@ -381,6 +384,7 @@ instant() { _next_event_index = _events.size(); _curr_t = get_duration(); + _state = S_final; } //////////////////////////////////////////////////////////////////// @@ -392,6 +396,7 @@ instant() { //////////////////////////////////////////////////////////////////// void CMetaInterval:: step(double t) { + check_started("step"); int now = double_to_int_time(t); // Now look for events between the last time we ran and the current @@ -426,6 +431,7 @@ step(double t) { } _curr_t = t; + _state = S_started; } //////////////////////////////////////////////////////////////////// @@ -437,6 +443,11 @@ step(double t) { //////////////////////////////////////////////////////////////////// void CMetaInterval:: finalize() { + double duration = get_duration(); + if (_state == S_initial) { + initialize(duration); + } + // Do all remaining events. ActiveEvents new_active; while (_next_event_index < _events.size()) { @@ -446,8 +457,9 @@ finalize() { _next_event_index++; } - _curr_t = get_duration(); - finish_events_forward(double_to_int_time(_curr_t), new_active); + finish_events_forward(double_to_int_time(duration), new_active); + _curr_t = duration; + _state = S_final; } //////////////////////////////////////////////////////////////////// @@ -460,6 +472,7 @@ finalize() { //////////////////////////////////////////////////////////////////// void CMetaInterval:: reverse_initialize(double t) { + check_stopped("reverse_initialize"); // It may be tempting to flush the event_queue here, but don't do // it. Those are events that must still be serviced from some // previous interval operation. Throwing them away would be a @@ -484,6 +497,7 @@ reverse_initialize(double t) { finish_events_reverse(now, new_active); _curr_t = t; + _state = S_started; } //////////////////////////////////////////////////////////////////// @@ -497,6 +511,7 @@ reverse_initialize(double t) { //////////////////////////////////////////////////////////////////// void CMetaInterval:: reverse_instant() { + check_stopped("reverse_instant"); recompute(); _active.clear(); @@ -512,6 +527,7 @@ reverse_instant() { _next_event_index = 0; _curr_t = 0.0; + _state = S_initial; } //////////////////////////////////////////////////////////////////// @@ -523,6 +539,10 @@ reverse_instant() { //////////////////////////////////////////////////////////////////// void CMetaInterval:: reverse_finalize() { + if (_state == S_initial) { + initialize(0.0); + } + // Do all remaining events at the beginning. ActiveEvents new_active; @@ -534,6 +554,7 @@ reverse_finalize() { finish_events_reverse(0, new_active); _curr_t = 0.0; + _state = S_initial; } //////////////////////////////////////////////////////////////////// @@ -557,6 +578,9 @@ interrupt() { PlaybackEvent *event = (*ai); enqueue_event(event->_n, ET_interrupt, false); } + if (_state == S_started) { + _state = S_paused; + } } //////////////////////////////////////////////////////////////////// diff --git a/direct/src/interval/config_interval.cxx b/direct/src/interval/config_interval.cxx index cd15656dca..cc8806590a 100644 --- a/direct/src/interval/config_interval.cxx +++ b/direct/src/interval/config_interval.cxx @@ -38,6 +38,10 @@ ConfigureFn(config_interval) { // CMetaInterval created. const double interval_precision = config_interval.GetDouble("interval-precision", 1000.0); +// Set this true to generate an assertion failure if interval +// functions are called out-of-order. +const bool verify_intervals = config_interval.GetBool("verify-intervals", false); + //////////////////////////////////////////////////////////////////// // Function: init_libinterval // Description: Initializes the library. This must be called at diff --git a/direct/src/interval/config_interval.h b/direct/src/interval/config_interval.h index 0670b57b0c..31f00c2ca2 100644 --- a/direct/src/interval/config_interval.h +++ b/direct/src/interval/config_interval.h @@ -26,6 +26,7 @@ NotifyCategoryDecl(interval, EXPCL_DIRECT, EXPTP_DIRECT); extern const double interval_precision; +extern const bool verify_intervals; extern EXPCL_DIRECT void init_libinterval(); diff --git a/direct/src/interval/showInterval.cxx b/direct/src/interval/showInterval.cxx index 6472ac12c9..185e3e9246 100644 --- a/direct/src/interval/showInterval.cxx +++ b/direct/src/interval/showInterval.cxx @@ -50,7 +50,9 @@ ShowInterval(const NodePath &node, const string &name) : //////////////////////////////////////////////////////////////////// void ShowInterval:: instant() { + check_stopped("instant"); _node.show(); + _state = S_final; } //////////////////////////////////////////////////////////////////// @@ -64,5 +66,7 @@ instant() { //////////////////////////////////////////////////////////////////// void ShowInterval:: reverse_instant() { + check_stopped("instant"); _node.hide(); + _state = S_initial; }