495 lines
13 KiB
C++
495 lines
13 KiB
C++
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
|
|
*
|
|
* This library is open source and may be redistributed and/or modified under
|
|
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
|
|
* (at your option) any later version. The full license is in LICENSE file
|
|
* included with this distribution, and on the openscenegraph.org website.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* 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 <osg/Sequence>
|
|
#include <osg/Notify>
|
|
|
|
using namespace osg;
|
|
|
|
/**
|
|
* Sequence constructor.
|
|
*/
|
|
Sequence::Sequence() :
|
|
Group(),
|
|
_value(-1),
|
|
_now(0.0),
|
|
_start(-1.0),
|
|
_totalTime(0.),
|
|
_resetTotalTime(true),
|
|
_loopMode(LOOP),
|
|
_begin(0),
|
|
_end(-1),
|
|
_speed(0),
|
|
_nreps(-1),
|
|
_nrepsRemain(-1),
|
|
_step(0),
|
|
_defaultTime(1.),
|
|
_lastFrameTime(0.),
|
|
_saveRealLastFrameTime(-1.),
|
|
_mode(STOP),
|
|
_sync(false),
|
|
_clearOnStop(false)
|
|
|
|
{
|
|
setNumChildrenRequiringUpdateTraversal(1);
|
|
}
|
|
|
|
Sequence::Sequence(const Sequence& seq, const CopyOp& copyop) :
|
|
Group(seq, copyop),
|
|
_value(seq._value),
|
|
_now(seq._now),
|
|
_start(seq._start),
|
|
_frameTime(seq._frameTime),
|
|
_totalTime(seq._totalTime),
|
|
_resetTotalTime(seq._resetTotalTime),
|
|
_loopMode(seq._loopMode),
|
|
_begin(seq._begin),
|
|
_end(seq._end),
|
|
_speed(seq._speed),
|
|
_nreps(seq._nreps),
|
|
_nrepsRemain(seq._nrepsRemain),
|
|
_step(seq._step),
|
|
_defaultTime(seq._defaultTime),
|
|
_lastFrameTime(seq._lastFrameTime),
|
|
_saveRealLastFrameTime(seq._saveRealLastFrameTime),
|
|
_mode(seq._mode),
|
|
_sync(seq._sync),
|
|
_clearOnStop(seq._clearOnStop)
|
|
{
|
|
setNumChildrenRequiringUpdateTraversal(getNumChildrenRequiringUpdateTraversal()+1);
|
|
}
|
|
|
|
bool Sequence::addChild( Node *child)
|
|
{
|
|
return Sequence::insertChild( _children.size(), child, _defaultTime);
|
|
}
|
|
|
|
bool Sequence::addChild( Node *child, double t)
|
|
{
|
|
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;
|
|
}
|
|
|
|
void Sequence::setInterval(LoopMode mode, int begin, int end)
|
|
{
|
|
_loopMode = mode;
|
|
_end = end;
|
|
_begin = begin;
|
|
|
|
// _value based on _begin & _end
|
|
_value = -1;
|
|
|
|
_resetTotalTime = true;
|
|
|
|
}
|
|
|
|
void Sequence::setDuration(float speed, int 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)
|
|
{
|
|
int ubegin, uend;
|
|
|
|
switch (mode)
|
|
{
|
|
case START:
|
|
// restarts sequence from beginning
|
|
_value = -1;
|
|
|
|
// Figure out which direction to start stepping the sequence
|
|
ubegin = (_begin < 0 ? (int)_frameTime.size()-1: _begin);
|
|
uend = (_end < 0 ? (int)_frameTime.size()-1: _end);
|
|
_step = (ubegin > uend ? -1 : 1);
|
|
|
|
_start = -1.0;
|
|
_mode = mode;
|
|
if (_saveRealLastFrameTime>=0.)
|
|
{
|
|
_frameTime[_saveRealLastFrameValue] = _saveRealLastFrameTime;
|
|
_saveRealLastFrameTime = -1.;
|
|
}
|
|
break;
|
|
case STOP:
|
|
_mode = mode;
|
|
break;
|
|
case PAUSE:
|
|
if (_mode == START)
|
|
_mode = PAUSE;
|
|
break;
|
|
case RESUME:
|
|
if (_mode == PAUSE)
|
|
_mode = START;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Sequence::traverse(NodeVisitor& nv)
|
|
{
|
|
if (getNumChildren()==0) return;
|
|
|
|
const FrameStamp* framestamp = nv.getFrameStamp();
|
|
if (framestamp)
|
|
{
|
|
_now = framestamp->getSimulationTime();
|
|
}
|
|
|
|
|
|
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);
|
|
|
|
int _sbegin = osg::minimum(_ubegin,_uend);
|
|
int _send = osg::maximum(_ubegin,_uend);
|
|
|
|
if (framestamp)
|
|
{
|
|
// hack for last frame time
|
|
if (_lastFrameTime>0. && _nrepsRemain==1 && _saveRealLastFrameTime<0.)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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]*osg::absolute(_speed);
|
|
// 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_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_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 ( !((_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);
|
|
|
|
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;
|
|
}
|
|
|
|
}
|