Panda3D
Loading...
Searching...
No Matches
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
20CIntervalManager *CIntervalManager::_global_ptr;
21
22/**
23 *
24 */
25CIntervalManager::
26CIntervalManager() {
27 _first_slot = 0;
28 _next_event_index = 0;
30}
31
32/**
33 *
34 */
35CIntervalManager::
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 */
54add_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 */
111find_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 */
125get_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 */
138remove_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 */
164interrupt() {
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 */
227get_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 */
240get_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 */
257step() {
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 */
353void CIntervalManager::
354output(std::ostream &out) const {
355 MutexHolder holder(_lock);
356
357 out << "CIntervalManager, " << (int)_name_index.size() << " intervals.";
358}
359
360/**
361 *
362 */
363void CIntervalManager::
364write(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 */
408void CIntervalManager::
409finish_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 */
428void CIntervalManager::
429remove_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:126
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 ...
virtual void priv_instant()
This is called in lieu of priv_initialize() .
This interval contains a list of nested intervals, each of which has its own begin and end times.
bool is_event_ready()
Returns true if a recent call to priv_initialize(), priv_step(), or priv_finalize() has left some ext...
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.