From d8ee1987353fa3c6786afc3d0369fb8c87bf8149 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Wed, 9 May 2007 11:11:19 +0000 Subject: [PATCH] From John Kelso, "Attached are updates of src/osg/Sequence.spp and include/osg/Sequence. I've taken _sbegin/_send/_ubegin/_uend and _step our of the include file and made them local variables in whatever method might need them. I got rid of the _recalculate method as it was only getting used in one place. I also found a cut/paste bug in setMode's START case." Note from Robert Osfield, Also includes some guards against crashes that was occuring in this new code when handling empty Sequences. --- include/osg/Sequence | 240 ++++++++++++++------- src/osg/Sequence.cpp | 485 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 547 insertions(+), 178 deletions(-) diff --git a/include/osg/Sequence b/include/osg/Sequence index 2e1cb02f5..b1d9038c5 100644 --- a/include/osg/Sequence +++ b/include/osg/Sequence @@ -9,115 +9,205 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * OpenSceneGraph Public License for more details. -*/ + */ #ifndef OSG_SEQUENCE #define OSG_SEQUENCE 1 -#include - -namespace osg { +#include +namespace osg +{ + /** Sequence is a Group node which allows automatic, time based - switching between children. +switching between children. */ class OSG_EXPORT Sequence : public Group { -public : - - Sequence(); + public : - /** Copy constructor using CopyOp to manage deep vs shallow copy.*/ - Sequence(const Sequence&, const CopyOp& copyop=CopyOp::SHALLOW_COPY); + Sequence(); - META_Node(osg, Sequence); + /** Copy constructor using CopyOp to manage deep vs shallow copy.*/ + Sequence(const Sequence&, const CopyOp& copyop=CopyOp::SHALLOW_COPY); - virtual void traverse(NodeVisitor& nv); - - - void setValue(int value) { _value = value; } - int getValue() const { return _value; } + META_Node(osg, Sequence); - /** Set time in seconds for child. */ - void setTime(int frame, float t); + virtual void traverse(NodeVisitor& nv); - /** Get time for child. */ - float getTime(int frame) const; + // the relationship between the _frameTime vector and the _children + // vector is a bit of a mess. This is how it was in previous versions, + // and there's no way out of it if upward compatability needs to be + // maintained. New code should set defaultTime and use addChild, and + // not mess with the setTime method - /** Set default time in seconds for new child. */ - void setDefaultTime(float t) {_defaultTime = t;} + virtual bool addChild( Node *child); - /** Get default time in seconds for new child. */ - float getDefaultTime(void) const {return _defaultTime;}; + virtual bool addChild( Node *child, double t); - /** Get number of frames */ - inline unsigned int getNumFrames() const { return _frameTime.size(); } + virtual bool insertChild( unsigned int index, Node *child); - /** Interval modes. 'Loop' repeats frames 1-N; 'swing' repeats 1->N, (N-1)->1. */ - enum LoopMode - { - LOOP, - SWING - }; + virtual bool insertChild( unsigned int index, Node *child, double t); - /** Set sequence mode & interval (range of children to be displayed). */ - void setInterval(LoopMode mode, int begin, int end); + virtual bool removeChild( Node *child ); - /** Get sequence mode & interval. */ - inline void getInterval(LoopMode& mode, int& begin, int& end) const - { - mode = _loopMode; - begin = _begin; - end = _end; - } + virtual bool removeChildren(unsigned int pos,unsigned int numChildrenToRemove); - /** Set duration: speed-up & number of repeats */ - void setDuration(float speed, int nreps = -1); - /** Get duration & number of repeats. */ - inline void getDuration(float& speed, int& nreps) const - { - speed = _speed; - nreps = _nreps; - } + /** value is which child node is to be displayed */ + void setValue(int value) { _value = value ; } + int getValue() const { return _value; } - /** Sequence modes. */ - enum SequenceMode - { - START, - STOP, - PAUSE, - RESUME - }; + /** Set time in seconds for child. */ + void setTime(unsigned int frame, double t); - /** Set sequence mode. Start/stop & pause/resume. */ - void setMode(SequenceMode mode); + /** Get time for child. */ + double getTime(unsigned int frame) const; - /** Get sequence mode. */ - inline SequenceMode getMode() const { return _mode; } + /** Set default time in seconds for new child. + if t<0, t=0 */ + void setDefaultTime(double t) {_defaultTime = (t<0.?0.:t);} -protected : - - virtual ~Sequence() {} + /** Get default time in seconds for new child. */ + double getDefaultTime(void) const {return _defaultTime;}; - int _value; + /** Set time of last frame of last loop, in seconds. + if t<= 0, then ignored */ + void setLastFrameTime(double t) {_lastFrameTime = (t<0.?0.:t);} - float _last; - std::vector _frameTime; + /** Get last frame time in seconds */ + double getLastFrameTime(void) const {return _lastFrameTime;}; - int _step; + /** Get number of frames */ + inline unsigned int getNumFrames() const { return _frameTime.size(); } - LoopMode _loopMode; - int _begin, _end; + /** Interval modes. 'Loop' repeats frames 1-N; 'swing' repeats 1->N, (N-1)->1. */ + enum LoopMode + { + LOOP, + SWING + }; - float _speed; - int _nreps, _nrepsremain; + /** Set sequence mode & interval (range of children to be displayed). */ + void setInterval(LoopMode mode, int begin, int end); - float _defaultTime ; + /** Get sequence mode & interval. */ + inline void getInterval(LoopMode& mode, int& begin, int& end) const + { + mode = _loopMode; + begin = _begin; + end = _end; + } + + /** Set duration: speed-up & number of repeats */ + void setDuration(float speed, int nreps = -1); + + /** Get duration & number of repeats. */ + inline void getDuration(float& speed, int& nreps) const + { + speed = _speed; + nreps = _nreps; + } + + /** Sequence modes. */ + enum SequenceMode + { + START, + STOP, + PAUSE, + RESUME + }; + + /** Set sequence mode. Start/stop & pause/resume. */ + void setMode(SequenceMode mode); + + /** Get sequence mode. */ + inline SequenceMode getMode() const { return _mode; } + + /** If false (default), frames will not be sync'd to frameTime. If + true, frames will be sync'd to frameTime. */ + void setSync(bool sync) { _sync = sync ; } ; + + /** Get sync value */ + inline void getSync(bool& sync) const { sync = _sync ; } ; + + /** If true, show no child nodes after stopping */ + void setClearOnStop(bool clearOnStop) { _clearOnStop = clearOnStop ; } ; + + /** If true, show no child nodes after stopping */ + inline void getClearOnStop(bool& clearOnStop) const { clearOnStop = _clearOnStop ; } ; + + protected : + + virtual ~Sequence() {} + + // get next _value in sequence + int _getNextValue(void) ; + + // update local variables + void _update(void) ; + + // init to -1 to mean "restart" + int _value; + + // current time, set by traverse + double _now ; + + // time this frame started. init to -1.0f- means get current time + double _start; + + // a vector of frame times, one per value + std::vector _frameTime; + + // the total time for one sequence, from BEGIN to END + double _totalTime ; + + // true if _totalTime needs to be recalculated because setTime or + // setInterval was invoked, or a new child was added + bool _resetTotalTime ; + + // store "loop mde", either LOOP or SWING + // init to LOOP- set by setInterval + LoopMode _loopMode; + + // first and last "values" to sequence through + // begin inits to 0 + // end inits to -1- means to init to number of values + int _begin, _end; + + // multiplier of real-time clock- set to N to go N times faster + // init to 0- going nowhere + float _speed; + + // _nreps: how many times to repeat- default param is -1, repeat forever + // init to 0, no repetitions + // _nrepsRemain: set to nreps and counts down every traversal, + // stopping when it gets to zero. init to 0 + int _nreps, _nrepsRemain; + + // default frame time for newly created frames or children- default is 1. + // set by setDefaultTime + double _defaultTime ; + + // special time to display last frame of last loop + // <= zero means to not do anything special + double _lastFrameTime ; + + // save the actual time of the last frame, and what value was stored + double _saveRealLastFrameTime ; + unsigned int _saveRealLastFrameValue ; + + // the current mode + SequenceMode _mode; + + // the current sync value + bool _sync ; + + // the current clearOnStop value + bool _clearOnStop ; - SequenceMode _mode; }; - + } #endif diff --git a/src/osg/Sequence.cpp b/src/osg/Sequence.cpp index cfcf55116..bc8bc74bc 100644 --- a/src/osg/Sequence.cpp +++ b/src/osg/Sequence.cpp @@ -9,7 +9,7 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * OpenSceneGraph Public License for more details. -*/ + */ #include #include @@ -22,16 +22,23 @@ using namespace osg; Sequence::Sequence() : Group(), _value(-1), - _last(-1.0f), - _step(1), + _now(0.0), + _start(-1.0), + _totalTime(0.), + _resetTotalTime(true), _loopMode(LOOP), _begin(0), _end(-1), _speed(0), - _nreps(0), - _nrepsremain(0), - _defaultTime(1.f), - _mode(STOP) + _nreps(-1), + _nrepsRemain(0), + _defaultTime(1.), + _lastFrameTime(0.), + _saveRealLastFrameTime(-1.), + _mode(STOP), + _sync(false), + _clearOnStop(false) + { setNumChildrenRequiringUpdateTraversal(1); } @@ -39,36 +46,105 @@ Sequence::Sequence() : Sequence::Sequence(const Sequence& seq, const CopyOp& copyop) : Group(seq, copyop), _value(seq._value), - _last(seq._last), + _now(seq._now), + _start(seq._start), _frameTime(seq._frameTime), - _step(seq._step), + _totalTime(seq._totalTime), + _resetTotalTime(seq._resetTotalTime), _loopMode(seq._loopMode), _begin(seq._begin), _end(seq._end), _speed(seq._speed), _nreps(seq._nreps), - _nrepsremain(seq._nrepsremain), + _nrepsRemain(seq._nrepsRemain), _defaultTime(seq._defaultTime), - _mode(seq._mode) + _lastFrameTime(seq._lastFrameTime), + _saveRealLastFrameTime(seq._saveRealLastFrameTime), + _mode(seq._mode), + _sync(seq._sync), + _clearOnStop(seq._clearOnStop) { setNumChildrenRequiringUpdateTraversal(getNumChildrenRequiringUpdateTraversal()+1); } -void Sequence::setTime(int frame, float t) +bool Sequence::addChild( Node *child) { - int sz = _frameTime.size(); - //cerr << "sz=" << sz << " frame=" << frame << endl; - if (frame < sz) - _frameTime[frame] = t; - else - for (int i = sz; i < (frame+1); i++) { - _frameTime.push_back(t); - } + return Sequence::insertChild( _children.size(), child, _defaultTime); } -float Sequence::getTime(int frame) const +bool Sequence::addChild( Node *child, double t) { - if (frame >= 0 && frame < (int) _frameTime.size()) + return Sequence::insertChild( _children.size(), child, t); +} + +bool Sequence::insertChild( unsigned int index, Node *child) +{ + return Sequence::insertChild(index, child, _defaultTime); +} + +bool Sequence::insertChild( unsigned int index, Node *child, double t) +{ + if (Group::insertChild(index,child)) + { + if (index>=_frameTime.size()) + { + Sequence::setTime(index, t); + } + _resetTotalTime = true; + return true; + } + return false; +} + +bool Sequence::removeChild( Node *child ) +{ + if (Group::removeChild(child )) + { + unsigned int pos = getChildIndex(child); + if (pos < _children.size()) + return Sequence::removeChildren(pos,1); + else + return false; + } + else + return false; +} + +bool Sequence::removeChildren(unsigned int pos,unsigned int numChildrenToRemove) +{ + if (pos<_frameTime.size()) + _frameTime.erase(_frameTime.begin()+pos, + osg::minimum(_frameTime.begin()+(pos+numChildrenToRemove), + _frameTime.end()) ); + _resetTotalTime = true; + return Group::removeChildren(pos,numChildrenToRemove); +} + + +// if frame >= _frameTime.size() then extend _frameTime to have frame-1 items +// a time <0 will get set to 0 +void Sequence::setTime(unsigned int frame, double t) +{ + if (t<0.) t = 0.0; + unsigned int sz = _frameTime.size(); + if (frame < sz) + { + _frameTime[frame] = t; + } + else + { + for (unsigned int i = sz; i <= frame; i++) + { + _frameTime.push_back(t); + } + } + +} + +// returns a frame time of -1 if frame is out of range +double Sequence::getTime(unsigned int frame) const +{ + if (frame < _frameTime.size()) return _frameTime[frame]; else return -1.0f; @@ -77,32 +153,39 @@ float Sequence::getTime(int frame) const void Sequence::setInterval(LoopMode mode, int begin, int end) { _loopMode = mode; - _begin = begin; _end = end; + _begin = begin; - // switch to beginning of interval - unsigned int nch = getNumChildren(); - begin = (_begin < 0 ? nch + _begin : _begin); - end = (_end < 0 ? nch + _end : _end); + // _value based on _begin & _end + _value = -1; + + _resetTotalTime = true; - setValue(begin); - _step = (begin < end ? 1 : -1); } void Sequence::setDuration(float speed, int nreps) { - _speed = (speed <= 0.0f ? 0.0f : speed); - _nreps = (nreps < 0 ? -1 : nreps); - _nrepsremain = _nreps; + _speed = speed; + // -1 means loop forever + _nreps = (nreps < 0 ? -1:nreps); + // countdown of laps around the track + _nrepsRemain = _nreps; } void Sequence::setMode(SequenceMode mode) { - switch (mode) { + switch (mode) + { case START: - // restarts sequence in 'traverse' - setValue(-1); + // restarts sequence from beginning + _value = -1; + _start = _now; _mode = mode; + if (_saveRealLastFrameTime>=0.) + { + _frameTime[_saveRealLastFrameValue] = _saveRealLastFrameTime; + _saveRealLastFrameTime = -1.; + } break; case STOP: _mode = mode; @@ -120,93 +203,289 @@ void Sequence::setMode(SequenceMode mode) void Sequence::traverse(NodeVisitor& nv) { - // if app traversal update the frame count. - if (nv.getVisitorType()==NodeVisitor::UPDATE_VISITOR && _mode == START && _nrepsremain) + if (getNumChildren()==0) return; + + if (nv.getVisitorType()==NodeVisitor::UPDATE_VISITOR && + _mode == START && + !_frameTime.empty() && getNumChildren()!=0) { + + // if begin or end < 0, make it last frame + int _ubegin = (_begin < 0 ? (int)_frameTime.size()-1: _begin); + int _uend = (_end < 0 ? (int)_frameTime.size()-1: _end); + + // are we stepping forward or backward? + int _step; + _step = (_ubegin < _uend ? 1 : -1); + _step = (_speed <0. ? -_step : _step); + + int _sbegin = osg::minimum(_ubegin,_uend); + int _send = osg::maximum(_ubegin,_uend); + const FrameStamp* framestamp = nv.getFrameStamp(); if (framestamp) { - - double t = framestamp->getSimulationTime(); - if (_last == -1.0) - _last = t; + + _now = framestamp->getSimulationTime(); - // first and last frame of interval - unsigned int nch = getNumChildren(); - int begin = (_begin < 0 ? nch + _begin : _begin); - int end = (_end < 0 ? nch + _end : _end); - - int sw = getValue(); - if (sw<0) + // hack for last frame time + if (_lastFrameTime>0. && _nrepsRemain==1 && _saveRealLastFrameTime<0.) { - sw = begin; - _step = (begin < end ? 1 : -1); - } - - // default timeout for unset values - if (sw >= (int) _frameTime.size()) - { - setTime(sw, _defaultTime); - } - - // frame time-out? - float dur = _frameTime[sw] * _speed; - if ((t - _last) > dur) { - - sw += _step; - - // check interval - int ibegin = (begin < end ? begin : end); - int iend = (end > begin ? end : begin); - //cerr << this << " interval " << ibegin << "," << iend << endl; - - if (sw < ibegin || sw > iend) { - // stop at last frame - if (sw < ibegin) - sw = ibegin; - else - sw = iend; - - // repeat counter - if (_nrepsremain > 0) - _nrepsremain--; - - if (_nrepsremain == 0) { - // stop - setMode(STOP); - } - else { - // wrap around - switch (_loopMode) { - case LOOP: - //cerr << this << " loop" << endl; - sw = begin; - break; - case SWING: - //cerr << this << " swing" << endl; - _step = -_step; - break; - } + if ( _loopMode == LOOP) + { + if ((_step>0 && _value!=_send) || (_step<0 && _value!=_sbegin)) + { + _saveRealLastFrameTime=_frameTime[_uend]; + _saveRealLastFrameValue = _uend; + _frameTime[_uend] = _lastFrameTime; + _resetTotalTime = true; + } + } + else + { + if (_step>0 && _value!=_sbegin) + { + _saveRealLastFrameTime=_frameTime[_send]; + _saveRealLastFrameValue = _send; + _frameTime[_send] = _lastFrameTime; + _resetTotalTime = true; + } + else if (_step<0 && _value!=_send) + { + _saveRealLastFrameTime=_frameTime[_sbegin]; + _saveRealLastFrameValue = _sbegin; + _frameTime[_sbegin] = _lastFrameTime; + _resetTotalTime = true; } } - - _last = t; } - setValue(sw); + + // I never know when to stop! + // more fun for last frame time + if (_nrepsRemain==0) + { + if (!_clearOnStop) + { + _mode = STOP; + } + else + { + if ( (_loopMode == LOOP) && + ( (_step>0 && _value!=_send) || + (_step<0 && _value!=_sbegin))) + { + _mode = STOP; + } + else if ( (_loopMode == SWING) && + ( (_step<0 && _value!=_send) || + (_step>0 && _value!=_sbegin))) + { + _mode = STOP; + } + + } + } + + // update local variables + _update(); + + + // now for the heavy lifting! three options + // 1) still in the same frame, so have nothing to do + // 2) just in the next frame + // 3) need to calculate everything based on elapsed time + if ((_now - _start) > _frameTime[_value]*osg::absolute(_speed)) + { // case 2 or case 3 + // most of the time it's just the next frame in the sequence + int nextValue = _getNextValue(); + if (!_sync || + ((_now - _start) <= (_frameTime[_value]+_frameTime[nextValue])*osg::absolute(_speed)) ) + { + _start += _frameTime[_value]; + // repeat or change directions? + if ( (_step>0 && nextValue==_send) || + (_step<0 && nextValue==_sbegin)) + { + if (_nreps>0) + _nrepsRemain--; + + // change direction + if (_loopMode == SWING) + _step = -_step; + + } + _value = nextValue; + } + else // case 3 + { + // recalculate everything based on elapsed time + + // elapsed time from start of the frame + double deltaT = _now - _start; + + // factors _speed into account + double adjTotalTime = _totalTime*osg::absolute(_speed); + + // how many laps? + int loops = (int)(deltaT/adjTotalTime); + + + // adjust reps & quick check to see if done becuase reps used up + + if (_nreps>0) + { + if (_loopMode == LOOP) + _nrepsRemain -= loops; + else + _nrepsRemain -= 2*loops; + + if (_nrepsRemain<=0) + { + _nrepsRemain = 0; + _mode = STOP; + osg::notify(osg::WARN) << "stopping because elapsed time greater or equal to time remaining to repeat the sequence\n"; + } + } + + // deduct off time for laps- _value shouldn't change as it's modulo the total time + double jumpStart = ((double)loops * adjTotalTime); + + // step through frames one at a time until caught up + while (deltaT-jumpStart > _frameTime[_value]*osg::absolute(_speed)) + { + jumpStart += _frameTime[_value]*osg::absolute(_speed ); + _value = _getNextValue(); + } + + // set start time + _start += jumpStart; + } + } } else - { osg::notify(osg::WARN) << "osg::Sequence::traverse(NodeVisitor&) requires a valid FrameStamp to function, sequence not updated.\n"; - } + } - + // now do the traversal if (nv.getTraversalMode()==NodeVisitor::TRAVERSE_ACTIVE_CHILDREN) { - if (getValue()>=0 && getValue()<(int)_children.size()) _children[getValue()]->accept(nv); + if ( !((_mode == STOP) && _clearOnStop) && + (getValue()>=0 && getValue()<(int)_children.size()) ) + { + _children[getValue()]->accept(nv); + } } else { Group::traverse(nv); } + +} + +int Sequence::_getNextValue() +{ + if (_frameTime.empty() || getNumChildren()==0) return 0; + + // if begin or end < 0, make it last frame + int _ubegin = (_begin < 0 ? (int)_frameTime.size()-1: _begin); + int _uend = (_end < 0 ? (int)_frameTime.size()-1: _end); + + // are we stepping forward or backward? + int _step; + _step = (_ubegin < _uend ? 1 : -1); + _step = (_speed <0. ? -_step : _step); + + int _sbegin = osg::minimum(_ubegin,_uend); + int _send = osg::maximum(_ubegin,_uend); + + int v = _value + _step; + + if (_sbegin==_send) + { + return _sbegin; + } + else if (v<=_send && v>=_sbegin) + { + return v; + } + else + { + int vs = _send - _sbegin + 1; + if (_loopMode == LOOP) + { + v = ((v-_sbegin)%vs) + _sbegin; + if (v<_sbegin) + { + v+=vs; + } + + return v; + } + else // SWING + { + if (v>_send) + return (2*_send-v); + else + return (2*_sbegin-v); + + } + } + +} + +void Sequence::_update() +{ + if (_frameTime.empty()) return; + + // if begin or end < 0, make it last frame + int _ubegin = (_begin < 0 ? (int)_frameTime.size()-1: _begin); + int _uend = (_end < 0 ? (int)_frameTime.size()-1: _end); + + int _sbegin = osg::minimum(_ubegin,_uend); + int _send = osg::maximum(_ubegin,_uend); + + // if _value<0, new or restarted + if (_value<0) + { + _value = (_begin < 0 ? (int)_frameTime.size()-1: _begin); + _resetTotalTime = true; + } + + // if _start<0, new or restarted + if (_start<0) + { + _start = _now; + _resetTotalTime = true; + } + + // need to calculate time of a complete sequence? + // time is different depending on loop mode + if (_resetTotalTime) + { + if (_loopMode == LOOP) + { + _totalTime = 0.0; + for (int i=_sbegin; i<=_send; i++) + { + _totalTime += _frameTime[i]; + } + } + else //SWING + { + _totalTime = _frameTime[_sbegin]; + // ones in the middle get counted twice: 0 1 2 3 4 3 2 1 0 + for (int i=_sbegin+1; i<_send; i++) + { + _totalTime += 2*_frameTime[i]; + } + if (_sbegin != _send) + { + _totalTime += _frameTime[_send]; + } + } + + _resetTotalTime = false; + } + }