498 lines
17 KiB
C++
498 lines
17 KiB
C++
// Filename: cIntervalManager.cxx
|
|
// Created by: drose (10Sep02)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// PANDA 3D SOFTWARE
|
|
// Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ .
|
|
//
|
|
// To contact the maintainers of this program write to
|
|
// panda3d-general@lists.sourceforge.net .
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
#include "cIntervalManager.h"
|
|
#include "cMetaInterval.h"
|
|
#include "dcast.h"
|
|
#include "eventQueue.h"
|
|
#include "mutexHolder.h"
|
|
|
|
CIntervalManager *CIntervalManager::_global_ptr;
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CIntervalManager::Constructor
|
|
// Access: Published
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
CIntervalManager::
|
|
CIntervalManager() {
|
|
_first_slot = 0;
|
|
_next_event_index = 0;
|
|
_event_queue = EventQueue::get_global_event_queue();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// 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) {
|
|
MutexHolder holder(_lock);
|
|
|
|
// 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 {
|
|
MutexHolder holder(_lock);
|
|
|
|
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 {
|
|
MutexHolder holder(_lock);
|
|
|
|
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) {
|
|
MutexHolder holder(_lock);
|
|
|
|
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::interrupt
|
|
// Access: Published
|
|
// Description: Pauses or finishes (removes from the active queue)
|
|
// all intervals tagged with auto_pause or auto_finish
|
|
// set to true. 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::
|
|
interrupt() {
|
|
MutexHolder holder(_lock);
|
|
|
|
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_auto_pause() || def._interval->get_auto_finish()) {
|
|
// This interval may be interrupted.
|
|
if (def._interval->get_auto_pause()) {
|
|
// It may be interrupted simply by pausing it.
|
|
if (interval_cat.is_debug()) {
|
|
interval_cat.debug()
|
|
<< "Auto-pausing " << def._interval->get_name() << "\n";
|
|
}
|
|
if (def._interval->get_state() == CInterval::S_started) {
|
|
def._interval->priv_interrupt();
|
|
}
|
|
|
|
} else {
|
|
// It should be interrupted by finishing it.
|
|
if (interval_cat.is_debug()) {
|
|
interval_cat.debug()
|
|
<< "Auto-finishing " << def._interval->get_name() << "\n";
|
|
}
|
|
switch (def._interval->get_state()) {
|
|
case CInterval::S_initial:
|
|
def._interval->priv_instant();
|
|
break;
|
|
|
|
case CInterval::S_final:
|
|
break;
|
|
|
|
default:
|
|
def._interval->priv_finalize();
|
|
}
|
|
}
|
|
|
|
// Now carefully remove it from the active list.
|
|
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 {
|
|
MutexHolder holder(_lock);
|
|
|
|
return _name_index.size();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CIntervalManager::get_max_index
|
|
// Access: Published
|
|
// Description: Returns one more than the largest interval index
|
|
// number in the manager. If you walk through all the
|
|
// values between (0, get_max_index()] and call
|
|
// get_c_interval() on each number, you will retrieve
|
|
// all of the managed intervals (and possibly a number
|
|
// of NULL pointers as well).
|
|
////////////////////////////////////////////////////////////////////
|
|
int CIntervalManager::
|
|
get_max_index() const {
|
|
MutexHolder holder(_lock);
|
|
|
|
return _intervals.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() {
|
|
MutexHolder holder(_lock);
|
|
|
|
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() {
|
|
MutexHolder holder(_lock);
|
|
|
|
while (_next_event_index < (int)_intervals.size()) {
|
|
IntervalDef &def = _intervals[_next_event_index];
|
|
if (def._interval != (CInterval *)NULL) {
|
|
if ((def._flags & F_external) != 0 &&
|
|
def._interval->check_t_callback()) {
|
|
return _next_event_index;
|
|
}
|
|
if ((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() {
|
|
MutexHolder holder(_lock);
|
|
|
|
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 {
|
|
MutexHolder holder(_lock);
|
|
|
|
out << "CIntervalManager, " << (int)_name_index.size() << " intervals.";
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CIntervalManager::write
|
|
// Access: Published
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
void CIntervalManager::
|
|
write(ostream &out) const {
|
|
MutexHolder holder(_lock);
|
|
|
|
// We need to write this line so that it's clear what's going on
|
|
// when there are no intervals in the list.
|
|
out << (int)_name_index.size() << " intervals.\n";
|
|
|
|
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. Assumes the lock is
|
|
// already held.
|
|
////////////////////////////////////////////////////////////////////
|
|
void CIntervalManager::
|
|
remove_index(int index) {
|
|
nassertv(_lock.debug_is_locked());
|
|
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;
|
|
}
|
|
}
|