osg示例解析之osganimationtimeline(1)

  • 简介

本示例主要使用了osgAnimation中的timeline时间线的方式来管理场景中的动画。时间线的概念有点像把三维的渲染过程分成一帧一帧,打个比喻:timeline有点像在看动画片的时候用相机把正在播放的动画片场景按固定间隔拍摄成一张张的照片,最后把这一张张的照片管理起来,有点像反向模拟动画的过程。

  • 示例

这个示例只能用Nathan.osg这个模型来运行,因为代码中的Animation动画依赖这个文件的内容,我们可以打开Nathan.osg看看,可以发现这样几段内容:

  osgAnimation::Animation {
        UniqueID uniqid_Animation_698
        name "Idle_Main"
        num_channels 66
        Vec3LinearChannel {
          name "scale"
          target "Foot_IK.L"
          Keyframes 51 {
            key 0.00000 1.00000 1.00000 1.00000
            key 0.04000 1.00000 1.00000 1.00000
            key 0.08000 1.00000 1.00000 1.00000
            key 0.12000 1.00000 1.00000 1.00000
            key 0.16000 1.00000 1.00000 1.00000
            key 0.20000 1.00000 1.00000 1.00000
            key 0.24000 1.00000 1.00000 1.00000
            ......

可以看到这个Idle_Main正好是Nathan.osg中的一段关键帧动画,同样源码中的Idle_Head_Scratch.02以及Idle_Nose_Scratch.01也可以在Nathan.osg中找到它们对应的动画

  • Callback定义

struct NoseBegin : public osgAnimation::Action::Callback
{
    virtual void operator()(osgAnimation::Action* action, osgAnimation::ActionVisitor* nv)
    {
        std::cout << "sacrebleu, it scratches my nose, let me scratch it" << std::endl;
        std::cout << "process NoseBegin call back " << action->getName() << std::endl << std::endl;
    }
};

struct NoseEnd : public osgAnimation::Action::Callback
{
    virtual void operator()(osgAnimation::Action* action, osgAnimation::ActionVisitor* nv)
    {
        std::cout << "shhhrt shrrrrt shhhhhhrrrrt, haaa it's better"<< std::endl;
        std::cout << "process NoseEnd call back " << action->getName() << std::endl << std::endl;
    }
};


这两段Callback定义在Nose_Scratch Action下面,它们会在某一个时刻被调用。Callback定义在Action之中,它们为的是实现在某一个Action中某一时刻被调用。而这些Action会在场景动画的某一个时间线上被调用(有点像我们之前打的比喻,在某一张拍摄照片绑定了一个Action,在这一时间到来时这个Action会被触发来运行它所包含的动画和回调)

        _scratchNose->setCallback(0.0, new NoseBegin);
        _scratchNose->setCallback(_scratchNose->getNumFrames()-1, new NoseEnd);
可以知道它们在_scratchNose开始时(0.0)以及_scratchNose结束时被调用。

  • 定义Action

在代码中Action定义在NodeCallback(ExampleTimelineUsage)之中

        _releaseKey = false;
        _manager = manager;

        const osgAnimation::AnimationList& list = _manager->getAnimationList();
        osgAnimation::AnimationMap map;
        for (osgAnimation::AnimationList::const_iterator it = list.begin(); it != list.end(); it++)
            map[(*it)->getName()] = *it;

        _mainLoop = new osgAnimation::ActionStripAnimation(map["Idle_Main"].get(),0.0,0.0);
        _mainLoop->setLoop(0); // means forever

		map["Idle_Head_Scratch.02"]->setDuration(4.0);
        _scratchHead = new osgAnimation::ActionStripAnimation(map["Idle_Head_Scratch.02"].get(),0.2,0.3);
        _scratchHead->setLoop(1); // one time

        map["Idle_Nose_Scratch.01"]->setDuration(10.0); // set this animation duration to 10 seconds
        _scratchNose = new osgAnimation::ActionStripAnimation(map["Idle_Nose_Scratch.01"].get(),0.2,0.3);
        _scratchNose->setLoop(1); // one time

        // add the main loop at priority 0 at time 0.
        
        osgAnimation::Timeline* tml = _manager->getTimeline();
        tml->play();
        tml->addActionAt(0.0, _mainLoop.get(), 0);


        // add a scratch head priority 1 at 3.0 second.
        tml->addActionAt(5.0, _scratchHead.get(), 1);

        // populate time with scratch head
        for (int i = 1; i < 20; i++)
        {
            // we add a scratch head priority 1 each 10 second
            // note:
            //      it's possible to add the same instance more then once on the timeline
            //      the only things you need to take care is if you remove it. It will remove
            //      all instance that exist on the timeline. If you need to differtiate
            //      it's better to create a new instance
            tml->addActionAt(5.0 + 10.0 * i, _scratchHead.get(), 1);
        }

        // we will add the scratch nose action only when the player hit a key
        // in the operator()

        // now we will add callback at end and begin of animation of Idle_Nose_Scratch.02
        _scratchNose->setCallback(0.0, new NoseBegin);
        _scratchNose->setCallback(_scratchNose->getNumFrames()-1, new NoseEnd);


将它们添加到Timeline之中,设置在弹起键盘上按键的时候添加一个Action

        if (nv && nv->getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR)
        {
            if (_releaseKey) // we hit a key and release it execute an action
            {
                osgAnimation::Timeline* tml = _manager->getTimeline();
                // dont play if already playing
                if (!tml->isActive(_scratchNose.get()))
                {
                    // add this animation on top of two other
                    // we add one to evaluate the animation at the next frame, else we
                    // will miss the current frame
                    tml->addActionAt(tml->getCurrentFrame() + 1, _scratchNose.get(), 1);
                }
                _releaseKey = false;
            }
        }

  • 添加管理器

和其他管理器一样的使用方式,在场景中添加管理器到节点中

    osg::ref_ptr<osgAnimation::TimelineAnimationManager> tl = new osgAnimation::TimelineAnimationManager(*animationManager);
    root->setUpdateCallback(tl.get());
    
    ExampleTimelineUsage* callback = new ExampleTimelineUsage(tl.get());
    root->setEventCallback(callback);
    root->getUpdateCallback()->addNestedCallback(callback);

附:这个示例中存在一个Bug,Idle_Head_Scratch.02并不会被调用,需要我们添加该动作的时间,使用setDuration设置一个Action运行的时候就好了。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值