// Filename: cIntervalManager.cxx // Created by: drose (10Sep02) // //////////////////////////////////////////////////////////////////// // // PANDA 3D SOFTWARE // Copyright (c) Carnegie Mellon University. All rights reserved. // // All use of this software is subject to the terms of the revised BSD // license. You should have received a copy of this license along // with this source code in a file named "LICENSE." // //////////////////////////////////////////////////////////////////// #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; } }