| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210 |
- /**
- * 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."
- *
- * @file cMetaInterval.cxx
- * @author drose
- * @date 2002-08-27
- */
- #include "cMetaInterval.h"
- #include "waitInterval.h"
- #include "config_interval.h"
- #include "indirectLess.h"
- #include "indent.h"
- #include <algorithm>
- #include <math.h> // for log10()
- #include <stdio.h> // for sprintf()
- using std::string;
- TypeHandle CMetaInterval::_type_handle;
- /**
- *
- */
- CMetaInterval::
- CMetaInterval(const string &name) :
- CInterval(name, 0.0, true)
- {
- _precision = interval_precision;
- _current_nesting_level = 0;
- _next_event_index = 0;
- _processing_events = false;
- }
- /**
- *
- */
- CMetaInterval::
- ~CMetaInterval() {
- clear_intervals();
- }
- /**
- * Resets the list of intervals and prepares for receiving a new list.
- */
- void CMetaInterval::
- clear_intervals() {
- // Better not do this unless you have serviced all of the outstanding
- // events!
- bool lost_events = false;
- if (!_event_queue.empty()) {
- interval_cat.warning()
- << "Losing outstanding events for " << *this << "\n";
- _event_queue.clear();
- lost_events = true;
- }
- clear_events();
- // Go through all of our nested intervals and remove ourselves as their
- // parent.
- Defs::iterator di;
- for (di = _defs.begin(); di != _defs.end(); ++di) {
- IntervalDef &def = (*di);
- if (def._c_interval != nullptr) {
- CInterval::Parents::iterator pi =
- std::find(def._c_interval->_parents.begin(),
- def._c_interval->_parents.end(),
- this);
- nassertv(pi != def._c_interval->_parents.end());
- def._c_interval->_parents.erase(pi);
- }
- }
- _defs.clear();
- _current_nesting_level = 0;
- _next_event_index = 0;
- #ifndef NDEBUG
- if (verify_intervals) {
- nassertv(!lost_events);
- }
- #endif
- }
- /**
- * Marks the beginning of a nested level of child intervals. Within the
- * nested level, a RelativeStart time of RS_level_begin refers to the start of
- * the level, and the first interval added within the level is always relative
- * to the start of the level.
- *
- * The return value is the index of the def entry created by this push.
- */
- int CMetaInterval::
- push_level(const string &name, double rel_time, RelativeStart rel_to) {
- nassertr(_event_queue.empty() && !_processing_events, -1);
- _defs.push_back(IntervalDef());
- IntervalDef &def = _defs.back();
- def._type = DT_push_level;
- def._ext_name = name;
- def._rel_time = rel_time;
- def._rel_to = rel_to;
- _current_nesting_level++;
- mark_dirty();
- return (int)_defs.size() - 1;
- }
- /**
- * Adds a new CInterval to the list. The interval will be played when the
- * indicated time (relative to the given point) has been reached.
- *
- * The return value is the index of the def entry representing the new
- * interval.
- */
- int CMetaInterval::
- add_c_interval(CInterval *c_interval,
- double rel_time, RelativeStart rel_to) {
- nassertr(_event_queue.empty() && !_processing_events, -1);
- nassertr(c_interval != nullptr, -1);
- c_interval->_parents.push_back(this);
- c_interval->_ival_pcollector = PStatCollector(_ival_pcollector, c_interval->_pname);
- _defs.push_back(IntervalDef());
- IntervalDef &def = _defs.back();
- def._type = DT_c_interval;
- def._c_interval = c_interval;
- def._rel_time = rel_time;
- def._rel_to = rel_to;
- mark_dirty();
- return (int)_defs.size() - 1;
- }
- /**
- * Adds a new external interval to the list. This represents some object in
- * the external scripting language that has properties similar to a CInterval
- * (for instance, a Python Interval object).
- *
- * The CMetaInterval object cannot play this external interval directly, but
- * it records a placeholder for it and will ask the scripting language to play
- * it when it is time, via is_event_ready() and related methods.
- *
- * The ext_index number itself is simply a handle that the scripting language
- * makes up and associates with its interval object somehow. The
- * CMetaInterval object does not attempt to interpret this value.
- *
- * The return value is the index of the def entry representing the new
- * interval.
- */
- int CMetaInterval::
- add_ext_index(int ext_index, const string &name, double duration,
- bool open_ended,
- double rel_time, RelativeStart rel_to) {
- nassertr(_event_queue.empty() && !_processing_events, -1);
- _defs.push_back(IntervalDef());
- IntervalDef &def = _defs.back();
- def._type = DT_ext_index;
- def._ext_index = ext_index;
- def._ext_name = name;
- def._ext_duration = duration;
- def._ext_open_ended = open_ended;
- def._rel_time = rel_time;
- def._rel_to = rel_to;
- mark_dirty();
- return (int)_defs.size() - 1;
- }
- /**
- * Finishes a level marked by a previous call to push_level(), and returns to
- * the previous level.
- *
- * If the duration is not negative, it represents a phony duration to assign
- * to the level, for the purposes of sequencing later intervals. Otherwise,
- * the level's duration is computed based on the intervals within the level.
- */
- int CMetaInterval::
- pop_level(double duration) {
- nassertr(_event_queue.empty() && !_processing_events, -1);
- nassertr(_current_nesting_level > 0, -1);
- _defs.push_back(IntervalDef());
- IntervalDef &def = _defs.back();
- def._type = DT_pop_level;
- def._ext_duration = duration;
- _current_nesting_level--;
- mark_dirty();
- return (int)_defs.size() - 1;
- }
- /**
- * Adjusts the start time of the child interval with the given name, if found.
- * This may be either a C++ interval added via add_c_interval(), or an
- * external interval added via add_ext_index(); the name must match exactly.
- *
- * If the interval is found, its start time is adjusted, and all subsequent
- * intervals are adjusting accordingly, and true is returned. If a matching
- * interval is not found, nothing is changed and false is returned.
- */
- bool CMetaInterval::
- set_interval_start_time(const string &name, double rel_time,
- CMetaInterval::RelativeStart rel_to) {
- nassertr(_event_queue.empty() && !_processing_events, false);
- Defs::iterator di;
- for (di = _defs.begin(); di != _defs.end(); ++di) {
- IntervalDef &def = (*di);
- bool match = false;
- switch (def._type) {
- case DT_c_interval:
- match = (def._c_interval->get_name() == name);
- break;
- case DT_ext_index:
- match = (def._ext_name == name);
- break;
- default:
- break;
- }
- if (match) {
- // Here's the interval.
- def._rel_time = rel_time;
- def._rel_to = rel_to;
- mark_dirty();
- return true;
- }
- }
- return false;
- }
- /**
- * Returns the actual start time, relative to the beginning of the interval,
- * of the child interval with the given name, if found, or -1 if the interval
- * is not found.
- */
- double CMetaInterval::
- get_interval_start_time(const string &name) const {
- recompute();
- Defs::const_iterator di;
- for (di = _defs.begin(); di != _defs.end(); ++di) {
- const IntervalDef &def = (*di);
- bool match = false;
- switch (def._type) {
- case DT_c_interval:
- match = (def._c_interval->get_name() == name);
- break;
- case DT_ext_index:
- match = (def._ext_name == name);
- break;
- default:
- break;
- }
- if (match) {
- // Here's the interval.
- return int_to_double_time(def._actual_begin_time);
- }
- }
- return -1.0;
- }
- /**
- * Returns the actual end time, relative to the beginning of the interval, of
- * the child interval with the given name, if found, or -1 if the interval is
- * not found.
- */
- double CMetaInterval::
- get_interval_end_time(const string &name) const {
- recompute();
- Defs::const_iterator di;
- for (di = _defs.begin(); di != _defs.end(); ++di) {
- const IntervalDef &def = (*di);
- bool match = false;
- double duration = 0.0;
- switch (def._type) {
- case DT_c_interval:
- duration = def._c_interval->get_duration();
- match = (def._c_interval->get_name() == name);
- break;
- case DT_ext_index:
- duration = def._ext_duration;
- match = (def._ext_name == name);
- break;
- default:
- break;
- }
- if (match) {
- // Here's the interval.
- return int_to_double_time(def._actual_begin_time) + duration;
- }
- }
- return -1.0;
- }
- /**
- * This replaces the first call to priv_step(), and indicates that the
- * interval has just begun. This may be overridden by derived classes that
- * need to do some explicit initialization on the first call.
- */
- void CMetaInterval::
- priv_initialize(double t) {
- if (_processing_events) {
- enqueue_self_event(ET_initialize, t);
- return;
- }
- check_stopped(get_class_type(), "priv_initialize");
- // It may be tempting to flush the event_queue here, but don't do it. Those
- // are events that must still be serviced from some previous interval
- // operation. Throwing them away would be a mistake.
- recompute();
- _next_event_index = 0;
- _active.clear();
- int now = double_to_int_time(t);
- /*
- // One special case: if we step to t == 0.0, it really means to the very
- // beginning of the interval, *before* any events that occurred at time 0.
- // (Most of the time, stepping to a particular time means *after* any events
- // that occurred at that time.)
- if (t == 0.0) {
- now = -1;
- }
- */
- // Now look for events from the beginning up to the current time.
- _processing_events = true;
- ActiveEvents new_active;
- while (_next_event_index < _events.size() &&
- _events[_next_event_index]->_time <= now) {
- PlaybackEvent *event = _events[_next_event_index];
- _next_event_index++;
- // Do the indicated event.
- do_event_forward(event, new_active, true);
- }
- finish_events_forward(now, new_active);
- _processing_events = false;
- _curr_t = t;
- _state = S_started;
- }
- /**
- * This is called in lieu of priv_initialize() .. priv_step() ..
- * priv_finalize(), when everything is to happen within one frame. The
- * interval should initialize itself, then leave itself in the final state.
- */
- void CMetaInterval::
- priv_instant() {
- if (_processing_events) {
- enqueue_self_event(ET_instant);
- return;
- }
- check_stopped(get_class_type(), "priv_instant");
- recompute();
- _active.clear();
- // Apply all of the events. This just means we invoke "instant" for any end
- // or instant event, ignoring the begin events.
- _processing_events = true;
- PlaybackEvents::iterator ei;
- for (ei = _events.begin(); ei != _events.end(); ++ei) {
- PlaybackEvent *event = (*ei);
- if (event->_type != PET_begin) {
- enqueue_event(event->_n, ET_instant, true, 0);
- }
- }
- _processing_events = false;
- _next_event_index = _events.size();
- _curr_t = get_duration();
- _state = S_final;
- if (_event_queue.empty()) {
- interval_done();
- } else {
- enqueue_done_event();
- }
- }
- /**
- * Advances the time on the interval. The time may either increase (the
- * normal case) or decrease (e.g. if the interval is being played by a
- * slider).
- */
- void CMetaInterval::
- priv_step(double t) {
- if (_processing_events) {
- enqueue_self_event(ET_step, t);
- return;
- }
- check_started(get_class_type(), "priv_step");
- int now = double_to_int_time(t);
- /*
- // One special case: if we step to t == 0.0, it really means to the very
- // beginning of the interval, *before* any events that occurred at time 0.
- // (Most of the time, stepping to a particular time means *after* any events
- // that occurred at that time.)
- if (t == 0.0) {
- now = -1;
- }
- */
- // Now look for events between the last time we ran and the current time.
- _processing_events = true;
- if (_next_event_index < _events.size() &&
- _events[_next_event_index]->_time <= now) {
- // The normal case: time is increasing.
- ActiveEvents new_active;
- while (_next_event_index < _events.size() &&
- _events[_next_event_index]->_time <= now) {
- PlaybackEvent *event = _events[_next_event_index];
- _next_event_index++;
- // Do the indicated event.
- do_event_forward(event, new_active, false);
- }
- finish_events_forward(now, new_active);
- } else {
- // A less usual case: time is decreasing.
- ActiveEvents new_active;
- while (_next_event_index > 0 &&
- _events[_next_event_index - 1]->_time > now) {
- _next_event_index--;
- PlaybackEvent *event = _events[_next_event_index];
- do_event_reverse(event, new_active, false);
- }
- finish_events_reverse(now, new_active);
- }
- _processing_events = false;
- _curr_t = t;
- _state = S_started;
- }
- /**
- * This is called when an interval is interrupted. It should advance the time
- * as if priv_step() were called, and also perform whatever cleanup might be
- * required.
- */
- void CMetaInterval::
- priv_finalize() {
- if (_processing_events) {
- enqueue_self_event(ET_finalize);
- return;
- }
- double duration = get_duration();
- if (_state == S_initial) {
- priv_initialize(duration);
- }
- // Do all remaining events.
- _processing_events = true;
- ActiveEvents new_active;
- while (_next_event_index < _events.size()) {
- PlaybackEvent *event = _events[_next_event_index];
- _next_event_index++;
- // Do the indicated event.
- do_event_forward(event, new_active, true);
- }
- finish_events_forward(double_to_int_time(duration), new_active);
- _processing_events = false;
- _curr_t = duration;
- _state = S_final;
- if (_event_queue.empty()) {
- interval_done();
- } else {
- enqueue_done_event();
- }
- }
- /**
- * Similar to priv_initialize(), but this is called when the interval is being
- * played backwards; it indicates that the interval should start at the
- * finishing state and undo any intervening intervals.
- */
- void CMetaInterval::
- priv_reverse_initialize(double t) {
- if (_processing_events) {
- enqueue_self_event(ET_reverse_initialize, t);
- return;
- }
- check_stopped(get_class_type(), "priv_reverse_initialize");
- // It may be tempting to flush the event_queue here, but don't do it. Those
- // are events that must still be serviced from some previous interval
- // operation. Throwing them away would be a mistake.
- recompute();
- _next_event_index = _events.size();
- _active.clear();
- int now = double_to_int_time(t);
- /*
- // One special case: if we step to t == 0.0, it really means to the very
- // beginning of the interval, *before* any events that occurred at time 0.
- // (Most of the time, stepping to a particular time means *after* any events
- // that occurred at that time.)
- if (t == 0.0) {
- now = -1;
- }
- */
- // Now look for events from the end down to the current time.
- _processing_events = true;
- ActiveEvents new_active;
- while (_next_event_index > 0 &&
- _events[_next_event_index - 1]->_time > now) {
- _next_event_index--;
- PlaybackEvent *event = _events[_next_event_index];
- // Do the indicated event.
- do_event_reverse(event, new_active, true);
- }
- finish_events_reverse(now, new_active);
- _processing_events = false;
- _curr_t = t;
- _state = S_started;
- }
- /**
- * This is called in lieu of priv_reverse_initialize() .. priv_step() ..
- * priv_reverse_finalize(), when everything is to happen within one frame.
- * The interval should initialize itself, then leave itself in the initial
- * state.
- */
- void CMetaInterval::
- priv_reverse_instant() {
- if (_processing_events) {
- enqueue_self_event(ET_reverse_instant);
- return;
- }
- check_stopped(get_class_type(), "priv_reverse_instant");
- recompute();
- _active.clear();
- // Apply all of the events. This just means we invoke "instant" for any end
- // or instant event, ignoring the begin events.
- _processing_events = true;
- PlaybackEvents::reverse_iterator ei;
- for (ei = _events.rbegin(); ei != _events.rend(); ++ei) {
- PlaybackEvent *event = (*ei);
- if (event->_type != PET_begin) {
- enqueue_event(event->_n, ET_reverse_instant, true, 0);
- }
- }
- _processing_events = false;
- _next_event_index = 0;
- _curr_t = 0.0;
- _state = S_initial;
- }
- /**
- * Called generally following a priv_reverse_initialize(), this indicates the
- * interval should set itself to the initial state.
- */
- void CMetaInterval::
- priv_reverse_finalize() {
- if (_processing_events) {
- enqueue_self_event(ET_reverse_finalize);
- return;
- }
- if (_state == S_initial) {
- priv_initialize(0.0);
- }
- // Do all remaining events at the beginning.
- _processing_events = true;
- ActiveEvents new_active;
- while (_next_event_index > 0) {
- _next_event_index--;
- PlaybackEvent *event = _events[_next_event_index];
- do_event_reverse(event, new_active, true);
- }
- finish_events_reverse(0, new_active);
- _processing_events = false;
- _curr_t = 0.0;
- _state = S_initial;
- }
- /**
- * This is called while the interval is playing to indicate that it is about
- * to be interrupted; that is, priv_step() will not be called for a length of
- * time. But the interval should remain in its current state in anticipation
- * of being eventually restarted when the calls to priv_step() eventually
- * resume.
- *
- * The purpose of this function is to allow self-running intervals like sound
- * intervals to stop the actual sound playback during the pause.
- */
- void CMetaInterval::
- priv_interrupt() {
- if (_processing_events) {
- enqueue_self_event(ET_interrupt);
- return;
- }
- _processing_events = true;
- ActiveEvents::iterator ai;
- for (ai = _active.begin(); ai != _active.end(); ++ai) {
- PlaybackEvent *event = (*ai);
- enqueue_event(event->_n, ET_interrupt, false);
- }
- _processing_events = false;
- if (_state == S_started) {
- _state = S_paused;
- }
- }
- /**
- * Acknowledges that the external interval on the top of the queue has been
- * extracted, and is about to be serviced by the scripting language. This
- * prepares the interval so the next call to is_event_ready() will return
- * information about the next external interval on the queue, if any.
- */
- void CMetaInterval::
- pop_event() {
- #ifndef NDEBUG
- nassertv(!_event_queue.empty());
- const EventQueueEntry &entry = _event_queue.front();
- const IntervalDef &def = _defs[entry._n];
- nassertv(def._type == DT_ext_index);
- #endif
- _event_queue.pop_front();
- }
- /**
- *
- */
- void CMetaInterval::
- write(std::ostream &out, int indent_level) const {
- recompute();
- // How many digits of precision should we output for time?
- int num_decimals = (int)ceil(log10(_precision));
- int total_digits = num_decimals + 4;
- static const int max_digits = 32; // totally arbitrary
- nassertv(total_digits <= max_digits);
- char format_str[26];
- sprintf(format_str, "%%%d.%df", total_digits, num_decimals);
- indent(out, indent_level) << get_name() << ":\n";
- int extra_indent_level = 1;
- Defs::const_iterator di;
- for (di = _defs.begin(); di != _defs.end(); ++di) {
- const IntervalDef &def = (*di);
- char time_str[max_digits + 1];
- sprintf(time_str, format_str, int_to_double_time(def._actual_begin_time));
- indent(out, indent_level) << time_str;
- write_event_desc(out, def, extra_indent_level);
- }
- }
- /**
- * Outputs a list of all events in the order in which they occur.
- */
- void CMetaInterval::
- timeline(std::ostream &out) const {
- recompute();
- // How many digits of precision should we output for time?
- int num_decimals = (int)ceil(log10(_precision));
- int total_digits = num_decimals + 4;
- static const int max_digits = 32; // totally arbitrary
- nassertv(total_digits <= max_digits);
- char format_str[26];
- sprintf(format_str, "%%%d.%df", total_digits, num_decimals);
- int extra_indent_level = 0;
- PlaybackEvents::const_iterator ei;
- for (ei = _events.begin(); ei != _events.end(); ++ei) {
- const PlaybackEvent *event = (*ei);
- char time_str[max_digits + 1];
- sprintf(time_str, format_str, int_to_double_time(event->_time));
- out << time_str;
- switch (event->_type) {
- case PET_begin:
- out << " begin ";
- break;
- case PET_end:
- out << " end ";
- break;
- case PET_instant:
- out << " instant ";
- break;
- }
- int n = event->_n;
- nassertv(n >= 0 && n < (int)_defs.size());
- const IntervalDef &def = _defs[n];
- write_event_desc(out, def, extra_indent_level);
- }
- }
- /**
- * Recomputes all of the events (and the duration) according to the set of
- * interval defs.
- */
- void CMetaInterval::
- do_recompute() {
- _dirty = false;
- clear_events();
- int n = recompute_level(0, 0, _end_time);
- if (n != (int)_defs.size()) {
- interval_cat.warning()
- << "CMetaInterval pushes don't match pops.\n";
- }
- // We do a stable_sort() to guarantee ordering of events that have the same
- // start time. These must be invoked in the order in which they appear.
- std::stable_sort(_events.begin(), _events.end(), IndirectLess<PlaybackEvent>());
- _duration = int_to_double_time(_end_time);
- }
- /**
- * Removes all entries from the _events list.
- */
- void CMetaInterval::
- clear_events() {
- PlaybackEvents::iterator ei;
- for (ei = _events.begin(); ei != _events.end(); ++ei) {
- PlaybackEvent *event = (*ei);
- delete event;
- }
- _events.clear();
- _active.clear();
- }
- /**
- * Process a single event in the interval, moving forwards in time. If the
- * event represents a new begin, adds it to the new_active list; if it is an
- * end, finalizes it.
- *
- * If is_initial is true, it is as if we are in initialize or finalize:
- * instant events will be invoked only if they are marked open_ended.
- */
- void CMetaInterval::
- do_event_forward(CMetaInterval::PlaybackEvent *event,
- CMetaInterval::ActiveEvents &new_active, bool is_initial) {
- switch (event->_type) {
- case PET_begin:
- nassertv(event->_begin_event == event);
- new_active.push_back(event);
- break;
- case PET_end:
- {
- // Erase the event from either the new active or the current active
- // lists.
- ActiveEvents::iterator ai;
- ai = std::find(new_active.begin(), new_active.end(), event->_begin_event);
- if (ai != new_active.end()) {
- new_active.erase(ai);
- // This interval was new this frame; we must invoke it as an instant
- // event.
- enqueue_event(event->_n, ET_instant, is_initial);
- } else {
- ai = std::find(_active.begin(), _active.end(), event->_begin_event);
- if (ai != _active.end()) {
- _active.erase(ai);
- enqueue_event(event->_n, ET_finalize, is_initial);
- } else {
- // Hmm, this event wasn't on either list. Maybe there was a start
- // event on the list whose time was less than 0.
- interval_cat.error()
- << "Event " << event->_begin_event->_n << " not on active list.\n";
- nassertv(false);
- }
- }
- }
- break;
- case PET_instant:
- nassertv(event->_begin_event == event);
- enqueue_event(event->_n, ET_instant, is_initial);
- break;
- }
- }
- /**
- * After walking through the event list and adding a bunch of new events to
- * new_active, finished up by calling priv_step() on all of the events still
- * in _active and priv_initialize() on all the events in new_active, then
- * copying the events from new_active to active.
- */
- void CMetaInterval::
- finish_events_forward(int now, CMetaInterval::ActiveEvents &new_active) {
- // Do whatever's still active.
- ActiveEvents::iterator ai;
- for (ai = _active.begin(); ai != _active.end(); ++ai) {
- PlaybackEvent *event = (*ai);
- enqueue_event(event->_n, ET_step, false, now - event->_time);
- }
- // Initialize whatever new intervals we came across.
- for (ai = new_active.begin(); ai != new_active.end(); ++ai) {
- PlaybackEvent *event = (*ai);
- enqueue_event(event->_n, ET_initialize, false, now - event->_time);
- _active.push_back(event);
- }
- }
- /**
- * Process a single event in the interval, moving backwards in time. This
- * undoes the indicated event. If the event represents a new begin, adds it
- * to the new_active list; if it is an end, finalizes it.
- *
- * If is_initial is true, it is as if we are in reverse_initialize or
- * reverse_finalize: instant events will be invoked only if they are marked
- * open_ended.
- */
- void CMetaInterval::
- do_event_reverse(CMetaInterval::PlaybackEvent *event,
- CMetaInterval::ActiveEvents &new_active, bool is_initial) {
- // Undo the indicated event.
- switch (event->_type) {
- case PET_begin:
- {
- nassertv(event->_begin_event == event);
- // Erase the event from either the new active or the current active
- // lists.
- ActiveEvents::iterator ai;
- ai = std::find(new_active.begin(), new_active.end(), event);
- if (ai != new_active.end()) {
- new_active.erase(ai);
- // This interval was new this frame; we invoke it as an instant event.
- enqueue_event(event->_n, ET_reverse_instant, is_initial);
- } else {
- ai = std::find(_active.begin(), _active.end(), event);
- if (ai != _active.end()) {
- _active.erase(ai);
- enqueue_event(event->_n, ET_reverse_finalize, is_initial);
- } else {
- // Hmm, this event wasn't on either list. Maybe there was a stop
- // event on the list whose time was greater than the total, somehow.
- interval_cat.error()
- << "Event " << event->_n << " not on active list.\n";
- nassertv(false);
- }
- }
- }
- break;
- case PET_end:
- new_active.push_front(event->_begin_event);
- break;
- case PET_instant:
- nassertv(event->_begin_event == event);
- enqueue_event(event->_n, ET_reverse_instant, is_initial);
- break;
- }
- }
- /**
- * After walking through the event list and adding a bunch of new events to
- * new_active, finishes up by calling priv_step() on all of the events still
- * in _active and priv_reverse_initialize() on all the events in new_active,
- * then copying the events from new_active to active.
- */
- void CMetaInterval::
- finish_events_reverse(int now, CMetaInterval::ActiveEvents &new_active) {
- // Do whatever's still active.
- ActiveEvents::iterator ai;
- for (ai = _active.begin(); ai != _active.end(); ++ai) {
- PlaybackEvent *event = (*ai);
- enqueue_event(event->_n, ET_step, false, now - event->_time);
- }
- // Initialize whatever new intervals we came across.
- for (ai = new_active.begin(); ai != new_active.end(); ++ai) {
- PlaybackEvent *event = (*ai);
- enqueue_event(event->_n, ET_reverse_initialize, false, now - event->_time);
- _active.push_front(event);
- }
- }
- /**
- * Enqueues the indicated interval for invocation after we have finished
- * scanning for events that need processing this frame.
- *
- * is_initial is only relevant for event types ET_instant or
- * ET_reverse_instant, and indicates whether we are in the priv_initialize()
- * (or priv_reverse_initialize()) call, and should therefore only invoke open-
- * ended intervals.
- *
- * time is only relevant for ET_initialize, ET_reverse_initialize, and
- * ET_step.
- */
- void CMetaInterval::
- enqueue_event(int n, CInterval::EventType event_type, bool is_initial, int time) {
- nassertv(n >= 0 && n < (int)_defs.size());
- const IntervalDef &def = _defs[n];
- switch (def._type) {
- case DT_c_interval:
- if (is_initial &&
- (event_type == ET_instant || event_type == ET_reverse_instant) &&
- !def._c_interval->get_open_ended()) {
- // Ignore a non-open-ended interval that we skipped completely past on
- // priv_initialize().
- return;
- } else {
- if (_event_queue.empty()) {
- // if the event queue is empty, we can process this C++ interval
- // immediately. We only need to defer it if there are external (e.g.
- // Python) intervals in the queue that need to be processed first.
- def._c_interval->priv_do_event(int_to_double_time(time), event_type);
- return;
- }
- }
- break;
- case DT_ext_index:
- if (is_initial &&
- (event_type == ET_instant || event_type == ET_reverse_instant) &&
- !def._ext_open_ended) {
- // Ignore a non-open-ended interval that we skipped completely past on
- // priv_initialize().
- return;
- }
- break;
- default:
- nassertv(false);
- return;
- }
- _event_queue.push_back(EventQueueEntry(n, event_type, time));
- }
- /**
- * Enqueues a reference to *this* interval. This is called only when the
- * interval is recursively re-entered; the request will be serviced when the
- * current request is done processing.
- *
- * time is only relevant for ET_initialize, ET_reverse_initialize, and
- * ET_step.
- */
- void CMetaInterval::
- enqueue_self_event(CInterval::EventType event_type, double t) {
- interval_cat.info()
- << "Recursive reentry detected into " << *this << "\n";
- int time = double_to_int_time(t);
- _event_queue.push_back(EventQueueEntry(-1, event_type, time));
- }
- /**
- * Enqueues a special "event" that simply marks the end of processing of the
- * interval; the interval's done event should be thrown now, if it is defined.
- */
- void CMetaInterval::
- enqueue_done_event() {
- _event_queue.push_back(EventQueueEntry(-2, ET_finalize, 0));
- }
- /**
- * Invokes whatever C++ intervals might be at the head of the queue, and
- * prepares for passing an external interval to the scripting language.
- *
- * The return value is true if there remains at least one external event to be
- * serviced, false if all events are handled.
- */
- bool CMetaInterval::
- service_event_queue() {
- while (!_event_queue.empty()) {
- nassertr(!_processing_events, true);
- const EventQueueEntry &entry = _event_queue.front();
- if (entry._n == -1) {
- // Index -1 is a special code for *this* interval.
- priv_do_event(int_to_double_time(entry._time), entry._event_type);
- } else if (entry._n == -2) {
- // Index -2 is a special code to indicate the interval is now done, and
- // its done event should be thrown.
- interval_done();
- } else {
- nassertr(entry._n >= 0 && entry._n < (int)_defs.size(), false);
- const IntervalDef &def = _defs[entry._n];
- switch (def._type) {
- case DT_c_interval:
- // Handle the C++ event.
- def._c_interval->priv_do_event(int_to_double_time(entry._time), entry._event_type);
- break;
- case DT_ext_index:
- // Here's an external event; leave it there and return.
- return true;
- default:
- nassertr(false, false);
- return false;
- }
- }
- _event_queue.pop_front();
- }
- // No more events on the queue.
- nassertr(!_processing_events, false);
- return false;
- }
- /**
- * Recursively recomputes a complete level (delimited by push/pop
- * definitions).
- *
- * The value n on entry refers to the first entry after the push; the return
- * value will reference the matching pop, or an index greater than the last
- * element in the array if there was no matching pop.
- *
- * The level_begin value indicates the begin time of this level. On return,
- * level_end is filled with the end time of this level.
- */
- int CMetaInterval::
- recompute_level(int n, int level_begin, int &level_end) {
- level_end = level_begin;
- int previous_begin = level_begin;
- int previous_end = level_begin;
- while (n < (int)_defs.size() && _defs[n]._type != DT_pop_level) {
- IntervalDef &def = _defs[n];
- int begin_time = previous_begin;
- int end_time = previous_end;
- switch (def._type) {
- case DT_c_interval:
- begin_time = get_begin_time(def, level_begin, previous_begin, previous_end);
- def._actual_begin_time = begin_time;
- end_time = begin_time + double_to_int_time(def._c_interval->get_duration());
- if (def._c_interval->is_exact_type(WaitInterval::get_class_type())) {
- // Don't bother enqueuing events for WaitIntervals; they're just there
- // to fill up time.
- } else {
- if (begin_time == end_time) {
- _events.push_back(new PlaybackEvent(begin_time, n, PET_instant));
- } else {
- PlaybackEvent *begin = new PlaybackEvent(begin_time, n, PET_begin);
- PlaybackEvent *end = new PlaybackEvent(end_time, n, PET_end);
- end->_begin_event = begin;
- _events.push_back(begin);
- _events.push_back(end);
- }
- }
- break;
- case DT_ext_index:
- begin_time = get_begin_time(def, level_begin, previous_begin, previous_end);
- def._actual_begin_time = begin_time;
- end_time = begin_time + double_to_int_time(def._ext_duration);
- if (begin_time == end_time) {
- _events.push_back(new PlaybackEvent(begin_time, n, PET_instant));
- } else {
- PlaybackEvent *begin = new PlaybackEvent(begin_time, n, PET_begin);
- PlaybackEvent *end = new PlaybackEvent(end_time, n, PET_end);
- end->_begin_event = begin;
- _events.push_back(begin);
- _events.push_back(end);
- }
- break;
- case DT_push_level:
- begin_time = get_begin_time(def, level_begin, previous_begin, previous_end);
- def._actual_begin_time = begin_time;
- n = recompute_level(n + 1, begin_time, end_time);
- break;
- case DT_pop_level:
- nassertr(false, _defs.size());
- break;
- }
- previous_begin = begin_time;
- previous_end = end_time;
- level_end = std::max(level_end, end_time);
- n++;
- }
- if (n < (int)_defs.size()) {
- IntervalDef &def = _defs[n];
- // If we have a pop record, check it for a phony duration.
- if (def._ext_duration >= 0.0) {
- level_end = level_begin + double_to_int_time(def._ext_duration);
- }
- // The final pop "begins" at the level end time, just for clarity on
- // output.
- def._actual_begin_time = level_end;
- }
- return n;
- }
- /**
- * Returns the integer begin time indicated by the given IntervalDef, given
- * the indicated level begin, previous begin, and previous end times.
- */
- int CMetaInterval::
- get_begin_time(const CMetaInterval::IntervalDef &def, int level_begin,
- int previous_begin, int previous_end) {
- switch (def._rel_to) {
- case RS_previous_end:
- return previous_end + double_to_int_time(def._rel_time);
- case RS_previous_begin:
- return previous_begin + double_to_int_time(def._rel_time);
- case RS_level_begin:
- return level_begin + double_to_int_time(def._rel_time);
- }
- nassertr(false, previous_end);
- return previous_end;
- }
- /**
- * Formats an event for output, for write() or timeline().
- */
- void CMetaInterval::
- write_event_desc(std::ostream &out, const CMetaInterval::IntervalDef &def,
- int &extra_indent_level) const {
- switch (def._type) {
- case DT_c_interval:
- indent(out, extra_indent_level)
- << *def._c_interval;
- if (!def._c_interval->get_open_ended()) {
- out << " (!oe)";
- }
- out << "\n";
- break;
- case DT_ext_index:
- indent(out, extra_indent_level)
- << "*" << def._ext_name;
- if (def._ext_duration != 0.0) {
- out << " dur " << def._ext_duration;
- }
- if (!def._ext_open_ended) {
- out << " (!oe)";
- }
- out<< "\n";
- break;
- case DT_push_level:
- indent(out, extra_indent_level)
- << def._ext_name << " {\n";
- extra_indent_level += 2;
- break;
- case DT_pop_level:
- extra_indent_level -= 2;
- indent(out, extra_indent_level)
- << "}\n";
- break;
- }
- }
|