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