Hi,
It looks like osg::Sequence has a bug in the way it ends a run through its
frames. Instead of stopping at the last frame, it wraps around and displays
the first frame once before stopping. You can test this by loading a
sequence and setting it to play exactly once, with a long enough time per
frame that each is clearly visible. It looks like the culprit is an
inconsistency in two boolean tests that determine whether or not to do last
frame related things:
314 if ( (_step>0 && nextValue==_send) ||
315 (_step<0 && nextValue==_sbegin))
316 {
317 if (_nreps>0)
318 _nrepsRemain--;
319
320 // change direction
321 if (_loopMode == SWING)
322 _step = -_step;
323
324 }
325 _value = nextValue;
And
281 if ( (_loopMode == LOOP) &&
282 ( (_step>0 && _value!=_send) ||
283 (_step<0 && _value!=_sbegin)))
284 {
285 _mode = STOP;
286 }
The first piece of code is in the regular (_nrepsRemain > 0) portion, and
does something like "if the next frame is the last one, decrement
_nrepsRemain and go to the next frame". So far so good. The second snippet
is from the code that handles the _nrepsRemain == 0 case, and based on
comments and functionality appears intended to run when the current frame is
the last one. However, the tests _value!=_send and _value!=_sbegin won't
pass until the current frame is *not* the last frame, so the sequence will
wrap around to the first frame before stopping. The simple fix is to just
change != to ==. The entire sequence.cpp file with change is pasted below.
Note that I have not changed the != tests for the code handling the last
frame when _nrepsRemain > 0, because I haven't tested it. It might not
matter in that case, since the animation isn't stopping anyway.
Thanks,
Max Bandazian
virtual void traverse(osg::NodeVisitor& nv)
{
if (getNumChildren()==0) return;
const osg::FrameStamp* framestamp = nv.getFrameStamp();
if (framestamp)
{
_now = framestamp->getSimulationTime();
}
if (nv.getVisitorType()==osg::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::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()==osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN)
{
if ( !((_mode == STOP) && _clearOnStop) &&
(getValue()>=0 && getValue()<(int)_children.size()) )
{
_children[getValue()]->accept(nv);
}
}
else
{
osg::Group::traverse(nv);
}
}
_______________________________________________
osg-submissions mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org