252 lines
7.7 KiB
C++
252 lines
7.7 KiB
C++
/* -*-c++-*-
|
|
* Copyright (C) 2008 Cedric Pinson <cedric.pinson@plopbyte.net>
|
|
*
|
|
* 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 <osgAnimation/Timeline>
|
|
#include <osgAnimation/StatsVisitor>
|
|
#include <limits.h>
|
|
|
|
using namespace osgAnimation;
|
|
|
|
|
|
osgAnimation::Timeline::Timeline()
|
|
{
|
|
_lastUpdate = 0;
|
|
_currentFrame = 0;
|
|
_fps = 25;
|
|
_speed = 1.0;
|
|
_state = Stop;
|
|
_initFirstFrame = false;
|
|
_previousFrameEvaluated = 0;
|
|
_evaluating = 0;
|
|
_numberFrame = UINT_MAX; // something like infinity
|
|
_collectStats = false;
|
|
_stats = new osg::Stats("Timeline");
|
|
setName("Timeline");
|
|
}
|
|
|
|
osgAnimation::Timeline::Timeline(const Timeline& nc,const osg::CopyOp& op)
|
|
: Action(nc, op),
|
|
_actions(nc._actions)
|
|
{
|
|
_lastUpdate = 0;
|
|
_currentFrame = 0;
|
|
_fps = 25;
|
|
_speed = 1.0;
|
|
_state = Stop;
|
|
_initFirstFrame = false;
|
|
_previousFrameEvaluated = 0;
|
|
_evaluating = 0;
|
|
_numberFrame = UINT_MAX; // something like infinity
|
|
_collectStats = false;
|
|
_stats = new osg::Stats("Timeline");
|
|
setName("Timeline");
|
|
}
|
|
|
|
void Timeline::setAnimationManager(AnimationManagerBase* manager)
|
|
{
|
|
_animationManager = manager;
|
|
}
|
|
|
|
void osgAnimation::Timeline::traverse(ActionVisitor& visitor)
|
|
{
|
|
int layer = visitor.getCurrentLayer();
|
|
visitor.pushTimelineOnStack(this);
|
|
// update from high priority to low priority
|
|
for( ActionLayers::reverse_iterator iterAnim = _actions.rbegin(); iterAnim != _actions.rend(); ++iterAnim )
|
|
{
|
|
visitor.setCurrentLayer(iterAnim->first);
|
|
ActionList& list = iterAnim->second;
|
|
for (unsigned int i = 0; i < list.size(); i++)
|
|
{
|
|
visitor.pushFrameActionOnStack(list[i]);
|
|
if (list[i].second) list[i].second->accept(visitor);
|
|
visitor.popFrameAction();
|
|
}
|
|
}
|
|
visitor.popTimeline();
|
|
visitor.setCurrentLayer(layer);
|
|
}
|
|
|
|
|
|
void osgAnimation::Timeline::setStats(osg::Stats* stats) { _stats = stats;}
|
|
osg::Stats* osgAnimation::Timeline::getStats() { return _stats.get();}
|
|
void osgAnimation::Timeline::collectStats(bool state) { _collectStats = state;}
|
|
osgAnimation::StatsActionVisitor* osgAnimation::Timeline::getStatsVisitor() { return _statsVisitor.get(); }
|
|
|
|
void osgAnimation::Timeline::clearActions()
|
|
{
|
|
_actions.clear();
|
|
_addActionOperations.clear();
|
|
_removeActionOperations.clear();
|
|
}
|
|
|
|
void osgAnimation::Timeline::update(double simulationTime)
|
|
{
|
|
// first time we call update we generate one frame
|
|
UpdateActionVisitor updateTimeline;
|
|
if (!_initFirstFrame)
|
|
{
|
|
_lastUpdate = simulationTime;
|
|
_initFirstFrame = true;
|
|
|
|
|
|
_animationManager->clearTargets();
|
|
updateTimeline.setFrame(_currentFrame);
|
|
accept(updateTimeline);
|
|
|
|
if (_collectStats)
|
|
{
|
|
if (!_statsVisitor)
|
|
_statsVisitor = new osgAnimation::StatsActionVisitor();
|
|
_statsVisitor->setStats(_stats.get());
|
|
_statsVisitor->setFrame(_currentFrame);
|
|
_statsVisitor->reset();
|
|
accept(*_statsVisitor);
|
|
}
|
|
|
|
processPendingOperation();
|
|
}
|
|
|
|
// find the number of frame pass since the last update
|
|
double delta = (simulationTime - _lastUpdate);
|
|
double nbframes = delta * _fps * _speed;
|
|
unsigned int nb = static_cast<unsigned int>(floor(nbframes));
|
|
|
|
for (unsigned int i = 0; i < nb; i++)
|
|
{
|
|
if (_state == Play)
|
|
_currentFrame++;
|
|
|
|
_animationManager->clearTargets();
|
|
updateTimeline.setFrame(_currentFrame);
|
|
accept(updateTimeline);
|
|
if (_collectStats)
|
|
{
|
|
if (!_statsVisitor)
|
|
_statsVisitor = new StatsActionVisitor;
|
|
_statsVisitor->setStats(_stats.get());
|
|
_statsVisitor->setFrame(_currentFrame);
|
|
_statsVisitor->reset();
|
|
accept(*_statsVisitor);
|
|
}
|
|
|
|
processPendingOperation();
|
|
}
|
|
if (nb)
|
|
{
|
|
_lastUpdate += ((double)nb) / _fps;
|
|
}
|
|
}
|
|
|
|
void osgAnimation::Timeline::removeAction(Action* action)
|
|
{
|
|
if (getEvaluating())
|
|
_removeActionOperations.push_back(FrameAction(0, action));
|
|
else
|
|
internalRemoveAction(action);
|
|
}
|
|
|
|
void osgAnimation::Timeline::addActionAt(unsigned int frame, Action* action, int priority)
|
|
{
|
|
// skip if this action has already been added this frame
|
|
for (CommandList::iterator it = _addActionOperations.begin(); it != _addActionOperations.end(); ++it)
|
|
{
|
|
Command& command = *it;
|
|
if (command._action.second.get() == action) {
|
|
osg::notify(osg::INFO) << "Timeline::addActionAt command " << action->getName() << " already added this frame, declined" << std::endl;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (isActive(action))
|
|
{
|
|
osg::notify(osg::INFO) << "Timeline::addActionAt command " << action->getName() << " already active, remove the old" << std::endl;
|
|
removeAction(action);
|
|
}
|
|
|
|
if (getEvaluating())
|
|
_addActionOperations.push_back(Command(priority,FrameAction(frame, action)));
|
|
else
|
|
internalAddAction(priority, FrameAction(frame, action));
|
|
}
|
|
void osgAnimation::Timeline::addActionAt(double t, Action* action, int priority)
|
|
{
|
|
unsigned int frame = static_cast<unsigned int>(floor(t * _fps));
|
|
addActionAt(frame, action, priority);
|
|
}
|
|
|
|
void osgAnimation::Timeline::addActionNow(Action* action, int priority)
|
|
{
|
|
addActionAt(getCurrentFrame()+1, action, priority);
|
|
}
|
|
|
|
void osgAnimation::Timeline::processPendingOperation()
|
|
{
|
|
// process all pending add action operation
|
|
while( !_addActionOperations.empty())
|
|
{
|
|
internalAddAction(_addActionOperations.back()._priority, _addActionOperations.back()._action);
|
|
_addActionOperations.pop_back();
|
|
}
|
|
|
|
// process all pending remove action operation
|
|
while( !_removeActionOperations.empty())
|
|
{
|
|
internalRemoveAction(_removeActionOperations.back().second.get());
|
|
_removeActionOperations.pop_back();
|
|
}
|
|
}
|
|
|
|
void osgAnimation::Timeline::internalRemoveAction(Action* action)
|
|
{
|
|
for (ActionLayers::iterator it = _actions.begin(); it != _actions.end(); it++)
|
|
{
|
|
ActionList& fa = it->second;
|
|
for (unsigned int i = 0; i < fa.size(); i++)
|
|
if (fa[i].second.get() == action)
|
|
{
|
|
fa.erase(fa.begin() + i);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void osgAnimation::Timeline::internalAddAction(int priority, const FrameAction& ftl)
|
|
{
|
|
_actions[priority].insert(_actions[priority].begin(), ftl);
|
|
}
|
|
|
|
bool osgAnimation::Timeline::isActive(Action* activeAction)
|
|
{
|
|
// update from high priority to low priority
|
|
for( ActionLayers::iterator iterAnim = _actions.begin(); iterAnim != _actions.end(); ++iterAnim )
|
|
{
|
|
// update all animation
|
|
ActionList& list = iterAnim->second;
|
|
for (unsigned int i = 0; i < list.size(); i++)
|
|
{
|
|
Action* action = list[i].second.get();
|
|
if (action == activeAction)
|
|
{
|
|
unsigned int firstFrame = list[i].first;
|
|
// check if current frame of timeline hit an action interval
|
|
if (_currentFrame >= firstFrame &&
|
|
_currentFrame < (firstFrame + action->getNumFrames()) )
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|