osgAnimation之动画管理器

  • 简介

在上一节osgAnimation之作用对象一文中的UpdateMatrixTransform示例里面,我们自己通过添加UpdateCallback来实现动画,事实上osgAnimation已经为我们提供了基本的动画处理类BasicAnimationManager,这个类继承自NodeCallback,负责多个动画对象的管理,现在我们看看它究竟是如何实现的。

  • 动画管理器

动画管理器中涉及到如下的几个类:LinkVisitor、AnimationManagerBase、BasicAnimationManager下面我们一一来看这些类的实现。

  • LinkVisitor

LinkVisitor使用在AnimationManagerBase中的一个内部辅助类,用来遍历子场景下面的作用对象(UpdateMatrixTransform等),我们在开发代码中不会直接用到这个类,下面我们重点看看这个类的作用:代码如下

void LinkVisitor::apply(osg::Node& node)
{
    osg::StateSet* st = node.getStateSet();
    if (st)
        handle_stateset(st);

    osg::NodeCallback* cb = node.getUpdateCallback();
    while (cb)
    {
        osgAnimation::AnimationUpdateCallbackBase* cba = dynamic_cast<osgAnimation::AnimationUpdateCallbackBase*>(cb);
        if (cba)
            link(cba);
        cb = cb->getNestedCallback();
    }
    traverse(node);
}

void LinkVisitor::apply(osg::Geode& node)
{
    for (unsigned int i = 0; i < node.getNumDrawables(); i++)
    {
        osg::Drawable* drawable = node.getDrawable(i);
        if (drawable && drawable->getStateSet())
            handle_stateset(drawable->getStateSet());
    }
    apply(static_cast<osg::Node&>(node));
}

LinkVisitor是一个NodeVisitor,看看它是实现我们知道它就是用来搜集作用对象的,并且将作用对象和Animation进行link(实质是将channel和作用对象进行了link)也就是将Channel中用来保存运算结果的Target对象设置为了作用对象中数组中元素的Target(即将StackedTransformElement中的target设置给了channel)

  • AnimationManagerBase

AnimationManagerBase是动画管理器的基类,它提供了动画管理器需要的基本功能,这个类是一个虚基类,需要派生类实现update成员函数。由于这个类是一个NodeCallback,它必然会重载operator()操作符,我们看一下它里面做了些什么:

void AnimationManagerBase::operator()(osg::Node* node, osg::NodeVisitor* nv)
{
    if (nv && nv->getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR)
    {
        if (needToLink())
        {
            link(node);
        }
        const osg::FrameStamp* fs = nv->getFrameStamp();
        update(fs->getSimulationTime());
    }
    traverse(node,nv);
}
对比一下我们在osgAnimation之作用对象中UpdateMatrixTransform的例子不难看出我们的PlayCallback与这个类很类似,这个update(fs->getSimulationTime())调用驱动了整个Animation动画,让整个动画一帧一帧的动起来。

此外我们在osgAnimation之作用对象中UpdateMatrixTransform的例子中将PlayCallback设置为动画子场景(球节点)的父节点,在场景遍历到动画子节点之前先通过PlayCallback来更新动画子节点之上MatrixTransform的矩阵值,从而达到遍历到动画子场景时变动子场景的位置实现动起来的效果。同理,这里AnimationManagerBase应该也是这样做到吧!没错,正是这样的。在上面代码中link(node)就是这么做的,我们看一看实现:

void AnimationManagerBase::link(osg::Node* subgraph)
{
    LinkVisitor* linker = getOrCreateLinkVisitor();
    linker->getAnimationList().clear();
    linker->getAnimationList() = _animations;

    subgraph->accept(*linker);
    _needToLink = false;
    buildTargetReference();
}
这里通过LinkVisitor将所有的StackedTransformElement的Target设置给了Channel,然后通过update(fs->getSimulationTime())来更新所有Channel中的Target的值。至于update(fs->getSimulationTime())究竟做了些什么,请接着往下阅读。

  • BasicAnimationManager

这个类正是集成了AnimationManagerBase类,同时重写了update方法,它也是我们在代码中最常使用的管理器类,现在看看在它的update中做了些什么:

void BasicAnimationManager::update (double time)
{
    _lastUpdate = time; 

    for (TargetSet::iterator it = _targets.begin(); it != _targets.end(); ++it)
        (*it).get()->reset();

// std::map键值从低到高,这里用了反向的迭代器,因此是按优先级从高到低进行操作的
    for( AnimationLayers::reverse_iterator iterAnim = _animationsPlaying.rbegin(); iterAnim != _animationsPlaying.rend(); ++iterAnim )
    {
        std::vector<int> toremove;
        int priority = iterAnim->first;
		
        AnimationList& list = iterAnim->second;
        for (unsigned int i = 0; i < list.size(); i++)
        {
            if (! list[i]->update(time, priority))
            {
                toremove.push_back(i);
            } 
        }
        while (!toremove.empty())
        {
            list.erase(list.begin() + toremove.back());
            toremove.pop_back();
        }
    }
}
这里的AnimationLayers是这样定义的:typedef std::map<int, AnimationList > AnimationLayers; 它是一个std::map,第一个元素代表了优先级,也就是在更新Target里面数据的时候是按照优先级从高到低进行的。在osgAnimation之作用对象中UpdateMatrixTransform的例子中我们仅仅是更新了一个Animation对象,这里更新了多个,但原理都是类似的。

  • 总结

结合上述的分析,我们知道了管理器做的工作实际上是使用一个更新回调驱动数据去完成更新。它需要这样几个过程:

1.将场景中的作用对象(UpdateMatrixTransform)中元素(也就是StackedTransformElement)的Target设置给Channel,这样建立起Target进行数据的传输纽带;这一过程通过LinkVisitor来完成。

2.更新回调中update的调用,这个update的调用起到一系列连锁反应,将Animation中所有Channel的update调用一遍,实质上是调用Channel里面所有Target的update,从而完成数据的更新,这一过程在BasicAnimationManager中update中完成。

本课内容的一个实例参看osg Examples 中osganimationsolid示例。



你可能感兴趣的:(OSG,osgAnimation)