define ivalMgr

This commit is contained in:
David Rose 2002-09-11 15:59:22 +00:00
parent 707fd440c9
commit 24f194bd45
19 changed files with 1093 additions and 331 deletions

View File

@ -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.

View File

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

View File

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

View File

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

View File

@ -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

View File

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

View File

@ -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.

View File

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

View File

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

View File

@ -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

View File

@ -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 \

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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<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

View File

@ -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.

View File

@ -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):