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

Reply via email to