Panda3D
cIntervalManager.cxx
1 // Filename: cIntervalManager.cxx
2 // Created by: drose (10Sep02)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "cIntervalManager.h"
16 #include "cMetaInterval.h"
17 #include "dcast.h"
18 #include "eventQueue.h"
19 #include "mutexHolder.h"
20 
21 CIntervalManager *CIntervalManager::_global_ptr;
22 
23 ////////////////////////////////////////////////////////////////////
24 // Function: CIntervalManager::Constructor
25 // Access: Published
26 // Description:
27 ////////////////////////////////////////////////////////////////////
28 CIntervalManager::
29 CIntervalManager() {
30  _first_slot = 0;
31  _next_event_index = 0;
32  _event_queue = EventQueue::get_global_event_queue();
33 }
34 
35 ////////////////////////////////////////////////////////////////////
36 // Function: CIntervalManager::Destructor
37 // Access: Published
38 // Description:
39 ////////////////////////////////////////////////////////////////////
40 CIntervalManager::
41 ~CIntervalManager() {
42  nassertv(_name_index.empty());
43 }
44 
45 ////////////////////////////////////////////////////////////////////
46 // Function: CIntervalManager::add_c_interval
47 // Access: Published
48 // Description: Adds the interval to the manager, and returns a
49 // unique index for the interval. This index will be
50 // unique among all the currently added intervals, but
51 // not unique across all intervals ever added to the
52 // manager. The maximum index value will never exceed
53 // the maximum number of intervals added at any given
54 // time.
55 //
56 // If the external flag is true, the interval is
57 // understood to also be stored in the scripting
58 // language data structures. In this case, it will be
59 // available for information returned by
60 // get_next_event() and get_next_removal(). If external
61 // is false, the interval's index will never be returned
62 // by these two functions.
63 ////////////////////////////////////////////////////////////////////
65 add_c_interval(CInterval *interval, bool external) {
66  MutexHolder holder(_lock);
67 
68  // First, check the name index. If we already have an interval by
69  // this name, it gets finished and removed.
70  NameIndex::iterator ni = _name_index.find(interval->get_name());
71  if (ni != _name_index.end()) {
72  int old_index = (*ni).second;
73  nassertr(old_index >= 0 && old_index < (int)_intervals.size(), -1)
74  CInterval *old_interval = _intervals[old_index]._interval;
75  if (old_interval == interval) {
76  // No, it's the same interval that was already here. In this
77  // case, don't finish the interval; just return it.
78  return old_index;
79  }
80  finish_interval(old_interval);
81  remove_index(old_index);
82  _name_index.erase(ni);
83  }
84 
85  int slot;
86 
87  if (_first_slot >= (int)_intervals.size()) {
88  // All the slots are filled; make a new slot.
89  nassertr(_first_slot == (int)_intervals.size(), -1);
90  slot = (int)_intervals.size();
91  _intervals.push_back(IntervalDef());
92  _first_slot = (int)_intervals.size();
93 
94  } else {
95  // Some slot is available; use it.
96  slot = _first_slot;
97  nassertr(slot >= 0 && slot < (int)_intervals.size(), -1);
98  _first_slot = _intervals[slot]._next_slot;
99  }
100 
101  IntervalDef &def = _intervals[slot];
102  def._interval = interval;
103  def._flags = 0;
104  if (external) {
105  def._flags |= F_external;
106  }
107  if (interval->is_of_type(CMetaInterval::get_class_type())) {
108  def._flags |= F_meta_interval;
109  }
110  def._next_slot = -1;
111 
112  _name_index[interval->get_name()] = slot;
113  nassertr(_first_slot >= 0, slot);
114  return slot;
115 }
116 
117 ////////////////////////////////////////////////////////////////////
118 // Function: CIntervalManager::find_c_interval
119 // Access: Published
120 // Description: Returns the index associated with the named interval,
121 // if there is such an interval, or -1 if there is not.
122 ////////////////////////////////////////////////////////////////////
124 find_c_interval(const string &name) const {
125  MutexHolder holder(_lock);
126 
127  NameIndex::const_iterator ni = _name_index.find(name);
128  if (ni != _name_index.end()) {
129  return (*ni).second;
130  }
131  return -1;
132 }
133 
134 ////////////////////////////////////////////////////////////////////
135 // Function: CIntervalManager::get_c_interval
136 // Access: Published
137 // Description: Returns the interval associated with the given index.
138 ////////////////////////////////////////////////////////////////////
140 get_c_interval(int index) const {
141  MutexHolder holder(_lock);
142 
143  nassertr(index >= 0 && index < (int)_intervals.size(), NULL);
144  return _intervals[index]._interval;
145 }
146 
147 ////////////////////////////////////////////////////////////////////
148 // Function: CIntervalManager::remove_c_interval
149 // Access: Published
150 // Description: Removes the indicated interval from the queue
151 // immediately. It will not be returned from
152 // get_next_removal(), and none of its pending events,
153 // if any, will be returned by get_next_event().
154 ////////////////////////////////////////////////////////////////////
156 remove_c_interval(int index) {
157  MutexHolder holder(_lock);
158 
159  nassertv(index >= 0 && index < (int)_intervals.size());
160  IntervalDef &def = _intervals[index];
161  nassertv(def._interval != (CInterval *)NULL);
162 
163  NameIndex::iterator ni = _name_index.find(def._interval->get_name());
164  nassertv(ni != _name_index.end());
165  nassertv((*ni).second == index);
166  _name_index.erase(ni);
167 
168  def._interval = (CInterval *)NULL;
169  def._next_slot = _first_slot;
170  _first_slot = index;
171 }
172 
173 ////////////////////////////////////////////////////////////////////
174 // Function: CIntervalManager::interrupt
175 // Access: Published
176 // Description: Pauses or finishes (removes from the active queue)
177 // all intervals tagged with auto_pause or auto_finish
178 // set to true. These are intervals that someone fired
179 // up but won't necessarily expect to clean up; they can
180 // be interrupted at will when necessary.
181 //
182 // Returns the number of intervals affected.
183 ////////////////////////////////////////////////////////////////////
186  MutexHolder holder(_lock);
187 
188  int num_paused = 0;
189 
190  NameIndex::iterator ni;
191  ni = _name_index.begin();
192  while (ni != _name_index.end()) {
193  int index = (*ni).second;
194  const IntervalDef &def = _intervals[index];
195  nassertr(def._interval != (CInterval *)NULL, num_paused);
196  if (def._interval->get_auto_pause() || def._interval->get_auto_finish()) {
197  // This interval may be interrupted.
198  if (def._interval->get_auto_pause()) {
199  // It may be interrupted simply by pausing it.
200  if (interval_cat.is_debug()) {
201  interval_cat.debug()
202  << "Auto-pausing " << def._interval->get_name() << "\n";
203  }
204  if (def._interval->get_state() == CInterval::S_started) {
205  def._interval->priv_interrupt();
206  }
207 
208  } else {
209  // It should be interrupted by finishing it.
210  if (interval_cat.is_debug()) {
211  interval_cat.debug()
212  << "Auto-finishing " << def._interval->get_name() << "\n";
213  }
214  switch (def._interval->get_state()) {
215  case CInterval::S_initial:
216  def._interval->priv_instant();
217  break;
218 
219  case CInterval::S_final:
220  break;
221 
222  default:
223  def._interval->priv_finalize();
224  }
225  }
226 
227  // Now carefully remove it from the active list.
228  NameIndex::iterator prev;
229  prev = ni;
230  ++ni;
231  _name_index.erase(prev);
232  remove_index(index);
233  num_paused++;
234 
235  } else {
236  // The interval should remain on the active list.
237  ++ni;
238  }
239  }
240 
241  return num_paused;
242 }
243 
244 ////////////////////////////////////////////////////////////////////
245 // Function: CIntervalManager::get_num_intervals
246 // Access: Published
247 // Description: Returns the number of currently active intervals.
248 ////////////////////////////////////////////////////////////////////
251  MutexHolder holder(_lock);
252 
253  return _name_index.size();
254 }
255 
256 ////////////////////////////////////////////////////////////////////
257 // Function: CIntervalManager::get_max_index
258 // Access: Published
259 // Description: Returns one more than the largest interval index
260 // number in the manager. If you walk through all the
261 // values between (0, get_max_index()] and call
262 // get_c_interval() on each number, you will retrieve
263 // all of the managed intervals (and possibly a number
264 // of NULL pointers as well).
265 ////////////////////////////////////////////////////////////////////
267 get_max_index() const {
268  MutexHolder holder(_lock);
269 
270  return _intervals.size();
271 }
272 
273 ////////////////////////////////////////////////////////////////////
274 // Function: CIntervalManager::step
275 // Access: Published
276 // Description: This should be called every frame to do the
277 // processing for all the active intervals. It will
278 // call step_play() for each interval that has been
279 // added and that has not yet been removed.
280 //
281 // After each call to step(), the scripting language
282 // should call get_next_event() and get_next_removal()
283 // repeatedly to process all the high-level
284 // (e.g. Python-interval-based) events and to manage the
285 // high-level list of intervals.
286 ////////////////////////////////////////////////////////////////////
288 step() {
289  MutexHolder holder(_lock);
290 
291  NameIndex::iterator ni;
292  ni = _name_index.begin();
293  while (ni != _name_index.end()) {
294  int index = (*ni).second;
295  const IntervalDef &def = _intervals[index];
296  nassertv(def._interval != (CInterval *)NULL);
297  if (!def._interval->step_play()) {
298  // This interval is finished and wants to be removed from the
299  // active list.
300  NameIndex::iterator prev;
301  prev = ni;
302  ++ni;
303  _name_index.erase(prev);
304  remove_index(index);
305 
306  } else {
307  // The interval can remain on the active list.
308  ++ni;
309  }
310  }
311 
312  _next_event_index = 0;
313 }
314 
315 ////////////////////////////////////////////////////////////////////
316 // Function: CIntervalManager::get_next_event
317 // Access: Published
318 // Description: This should be called by the scripting language after
319 // each call to step(). It returns the index number of
320 // the next interval that has events requiring servicing
321 // by the scripting language, or -1 if no more intervals
322 // have any events pending.
323 //
324 // If this function returns something other than -1, it
325 // is the scripting language's responsibility to query
326 // the indicated interval for its next event via
327 // get_event_index(), and eventually pop_event().
328 //
329 // Then get_next_event() should be called again until it
330 // returns -1.
331 ////////////////////////////////////////////////////////////////////
334  MutexHolder holder(_lock);
335 
336  while (_next_event_index < (int)_intervals.size()) {
337  IntervalDef &def = _intervals[_next_event_index];
338  if (def._interval != (CInterval *)NULL) {
339  if ((def._flags & F_external) != 0 &&
340  def._interval->check_t_callback()) {
341  return _next_event_index;
342  }
343  if ((def._flags & F_meta_interval) != 0) {
344  CMetaInterval *meta_interval;
345  DCAST_INTO_R(meta_interval, def._interval, -1);
346  if (meta_interval->is_event_ready()) {
347  nassertr((def._flags & F_external) != 0, -1);
348  return _next_event_index;
349  }
350  }
351  }
352  _next_event_index++;
353  }
354 
355  return -1;
356 }
357 
358 ////////////////////////////////////////////////////////////////////
359 // Function: CIntervalManager::get_next_removal
360 // Access: Published
361 // Description: This should be called by the scripting language after
362 // each call to step(). It returns the index number of
363 // an interval that was recently removed, or -1 if no
364 // intervals were removed.
365 //
366 // If this returns something other than -1, the
367 // scripting language should clean up its own data
368 // structures accordingly, and then call
369 // get_next_removal() again.
370 ////////////////////////////////////////////////////////////////////
373  MutexHolder holder(_lock);
374 
375  if (!_removed.empty()) {
376  int index = _removed.back();
377  _removed.pop_back();
378 
379  nassertr(index >= 0 && index < (int)_intervals.size(), -1);
380  IntervalDef &def = _intervals[index];
381  def._interval = (CInterval *)NULL;
382  def._next_slot = _first_slot;
383  _first_slot = index;
384  return index;
385  }
386 
387  return -1;
388 }
389 
390 ////////////////////////////////////////////////////////////////////
391 // Function: CIntervalManager::output
392 // Access: Published
393 // Description:
394 ////////////////////////////////////////////////////////////////////
395 void CIntervalManager::
396 output(ostream &out) const {
397  MutexHolder holder(_lock);
398 
399  out << "CIntervalManager, " << (int)_name_index.size() << " intervals.";
400 }
401 
402 ////////////////////////////////////////////////////////////////////
403 // Function: CIntervalManager::write
404 // Access: Published
405 // Description:
406 ////////////////////////////////////////////////////////////////////
407 void CIntervalManager::
408 write(ostream &out) const {
409  MutexHolder holder(_lock);
410 
411  // We need to write this line so that it's clear what's going on
412  // when there are no intervals in the list.
413  out << (int)_name_index.size() << " intervals.\n";
414 
415  NameIndex::const_iterator ni;
416  for (ni = _name_index.begin(); ni != _name_index.end(); ++ni) {
417  int index = (*ni).second;
418  nassertv(index >= 0 && index < (int)_intervals.size());
419  const IntervalDef &def = _intervals[index];
420  nassertv(def._interval != (CInterval *)NULL);
421  out << *def._interval << "\n";
422  }
423 
424  if (!_removed.empty()) {
425  out << "\nRemoved:\n";
426  Removed::const_iterator ri;
427  for (ri = _removed.begin(); ri != _removed.end(); ++ri) {
428  int index = (*ri);
429  nassertv(index >= 0 && index < (int)_intervals.size());
430  const IntervalDef &def = _intervals[index];
431  nassertv(def._interval != (CInterval *)NULL);
432  out << "(R)" << *def._interval << "\n";
433  }
434  }
435 }
436 
437 ////////////////////////////////////////////////////////////////////
438 // Function: CIntervalManager::get_global_ptr
439 // Access: Published, Static
440 // Description: Returns the pointer to the one global
441 // CIntervalManager object.
442 ////////////////////////////////////////////////////////////////////
445  if (_global_ptr == (CIntervalManager *)NULL) {
446  _global_ptr = new CIntervalManager;
447  }
448  return _global_ptr;
449 }
450 
451 ////////////////////////////////////////////////////////////////////
452 // Function: CIntervalManager::finish_interval
453 // Access: Private
454 // Description: Explicitly finishes the indicated interval in
455 // preparation for moving it to the removed queue.
456 ////////////////////////////////////////////////////////////////////
457 void CIntervalManager::
458 finish_interval(CInterval *interval) {
459  switch (interval->get_state()) {
460  case CInterval::S_initial:
461  interval->priv_instant();
462  break;
463 
464  case CInterval::S_final:
465  break;
466 
467  default:
468  interval->priv_finalize();
469  }
470 }
471 
472 ////////////////////////////////////////////////////////////////////
473 // Function: CIntervalManager::remove_index
474 // Access: Private
475 // Description: Removes the indicated index number from the active
476 // list, either by moving it to the removed queue if it
477 // is flagged external, or by simply making the slot
478 // available again if it is not. Assumes the lock is
479 // already held.
480 ////////////////////////////////////////////////////////////////////
481 void CIntervalManager::
482 remove_index(int index) {
483  nassertv(_lock.debug_is_locked());
484  nassertv(index >= 0 && index < (int)_intervals.size());
485  IntervalDef &def = _intervals[index];
486  if ((def._flags & F_external) != 0) {
487  _removed.push_back(index);
488  } else {
489  def._interval = (CInterval *)NULL;
490  def._next_slot = _first_slot;
491  _first_slot = index;
492  }
493 }
int interrupt()
Pauses or finishes (removes from the active queue) all intervals tagged with auto_pause or auto_finis...
virtual void priv_instant()
This is called in lieu of priv_initialize() .
Definition: cInterval.cxx:390
State get_state() const
Indicates the state the interval believes it is in: whether it has been started, is currently in the ...
Definition: cInterval.I:61
int get_max_index() const
Returns one more than the largest interval index number in the manager.
The base class for timeline components.
Definition: cInterval.h:39
int get_next_event()
This should be called by the scripting language after each call to step().
A lightweight C++ object whose constructor calls acquire() and whose destructor calls release() on a ...
Definition: mutexHolder.h:29
static EventQueue * get_global_event_queue()
Returns a pointer to the one global EventQueue object.
Definition: eventQueue.I:24
virtual void priv_finalize()
This is called to stop an interval, forcing it to whatever state it would be after it played all the ...
Definition: cInterval.cxx:422
int find_c_interval(const string &name) const
Returns the index associated with the named interval, if there is such an interval, or -1 if there is not.
static CIntervalManager * get_global_ptr()
Returns the pointer to the one global CIntervalManager object.
This interval contains a list of nested intervals, each of which has its own begin and end times...
Definition: cMetaInterval.h:34
void remove_c_interval(int index)
Removes the indicated interval from the queue immediately.
const string & get_name() const
Returns the interval&#39;s name.
Definition: cInterval.I:22
bool debug_is_locked() const
Returns true if the current thread has locked the Mutex, false otherwise.
Definition: mutexDirect.I:115
bool is_event_ready()
Returns true if a recent call to priv_initialize(), priv_step(), or priv_finalize() has left some ext...
CInterval * get_c_interval(int index) const
Returns the interval associated with the given index.
This object holds a number of currently-playing intervals and is responsible for advancing them each ...
int get_num_intervals() const
Returns the number of currently active intervals.
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:63
int get_next_removal()
This should be called by the scripting language after each call to step().
int add_c_interval(CInterval *interval, bool external)
Adds the interval to the manager, and returns a unique index for the interval.
void step()
This should be called every frame to do the processing for all the active intervals.