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  */
164 interrupt() {
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  */
227 get_num_intervals() const {
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  */
297 get_next_event() {
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  */
397 get_global_ptr() {
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 }
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This object holds a number of currently-playing intervals and is responsible for advancing them each ...
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_next_event()
This should be called by the scripting language after each call to step().
void remove_c_interval(int index)
Removes the indicated interval from the queue immediately.
int get_num_intervals() const
Returns the number of currently active intervals.
void step()
This should be called every frame to do the processing for all the active intervals.
int interrupt()
Pauses or finishes (removes from the active queue) all intervals tagged with auto_pause or auto_finis...
static CIntervalManager * get_global_ptr()
Returns the pointer to the one global CIntervalManager object.
CInterval * get_c_interval(int index) const
Returns the interval associated with the given index.
int get_max_index() const
Returns one more than the largest interval index number in the manager.
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.
The base class for timeline components.
Definition: cInterval.h:36
get_name
Returns the interval's name.
Definition: cInterval.h:124
get_state
Indicates the state the interval believes it is in: whether it has been started, is currently in the ...
Definition: cInterval.h:129
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
virtual void priv_instant()
This is called in lieu of priv_initialize() .
Definition: cInterval.cxx:342
This interval contains a list of nested intervals, each of which has its own begin and end times.
Definition: cMetaInterval.h:32
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
static EventQueue * get_global_event_queue()
Returns a pointer to the one global EventQueue object.
Definition: eventQueue.I:19
bool debug_is_locked() const
Returns true if the current thread has locked the Mutex, false otherwise.
Definition: mutexDirect.I:90
A lightweight C++ object whose constructor calls acquire() and whose destructor calls release() on a ...
Definition: mutexHolder.h:25
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.