第36.1节 动画-刚体动画控制

本节功能

本节后几个章节会介绍和动画有关的课程。

本节实现一个从3DMAX导出的地板破碎的动画的控制,这类动画叫做刚体动画。所谓刚体动画我理解就是仅物体的位置、方向、缩放这三者发生变化。

当我们点击P时,动画会暂停,当我们点击S时,动画会播放,点击R时动画会复位。点击+时,动画会快速播放2倍,点击 – 时,动画会慢速播放2倍。

在这里插入图片描述
本节的内容在网盘中,目录为/osgChina站长文集/文件中的附件/, 有附件的会根据节的编号存放在该目录:

请使用浏览器打开,平时遇到问题或加群也可以加我微信:13324598743:
【击此打开网盘资源链接】

具体实现

很容易,我们联想到,刚体动画涉及到的变量都是矩阵的变化,它的位置、朝向、缩放发生变化,其实大多数情况下是位置、朝向。而osg中控制矩阵的结点是osg::MatrixTransform,对其设置动画使用的是setUpdateCallback,而刚体动画是osg::AnimationPathCallback,因此我们只需要找到模型中所有结点里的UpdateCallback,判断其是否为AnimationPathCallback就可以了。

存放动画

为此我们申请了一个全局变量:

std::vector<osg::AnimationPathCallback*> g_animateCallback;

这个全局变量是一个向量,里面存放AnimationPathCallback,然后我们使用一个NodeVisitor,来对每一个osg::Transform(因为osg::MatrixTransform继承自osg::Transform,用基类更保险一点),来看它是否有UpdateCallback,如果有的话,是否是AnimationPathCallback,是的话就压入到全局变量中进行控制。现实如下

寻找动画

我们写的NodeVistor类叫做FindAnimate,实现如下:在这里要特别注意的是,在构造函数中我们调用了TRAVERSE_ALL_CHILDREN,代表我们要遍历结点的所有的孩子在apply中,我们调用了traverse(node)代表我们还要向下遍历。

class FindAnimate : public osg::NodeVisitor
{
public:
    FindAnimate():osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN){}
    void apply(osg::Transform& node)
    {
        if (node.getUpdateCallback())
        {
            osg::AnimationPathCallback* apc = dynamic_cast<osg::AnimationPathCallback*>(node.getUpdateCallback());
            if (NULL != apc)
            {
                g_animateCallback.push_back(apc);
            }
        }
        traverse(node);
    }
};

它的调用是这样的:

    FindAnimate fa;
    //diban是读取的ive
    diban->accept(fa);

自此动画都压入到了g_animateCallback中。然后我们再写几个简单的函数即可实现控制:

播放/暂停

AnimationPathCallback中有个setPause函数,直接就可以实现这个功能:

//true是播放动画,false是暂停动画
void play(bool isPlay)
{
    for (int i = 0; i < g_animateCallback.size(); i++)
    {
        g_animateCallback.at(i)->setPause(!isPlay);
    }
}

复位

AnimationPathCallback里有个reset可以实现这个功能:

void reset()
{
    for (int i = 0; i < g_animateCallback.size(); i++)
    {
        g_animateCallback.at(i)->reset();
        g_animateCallback.at(i)->setPause(false);
    }
}

加速/减速

AnimationPathCallback里有个setTimeMultiplier函数,下面是按+号加速2倍:

if (ea.getKey() == '+')
{
	for (int i = 0; i < g_animateCallback.size(); i++)
	{
		g_animateCallback.at(i)->setTimeMultiplier(g_animateCallback.at(i)->getTimeMultiplier()*2.0);
	}
}

最后用一个事件响应来联接这一切


class MyEvent : public osgGA::GUIEventHandler
{
public:
    MyEvent(){}
    virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
    {
        if (ea.getEventType() == ea.KEYDOWN)
        {
            if ((ea.getKey() == 'r') || (ea.getKey() == 'R'))
            {
                reset();
            }

            if ((ea.getKey() == 'p') || (ea.getKey() == 'P'))
            {
                play(false);
            }

            if ((ea.getKey() == 's') || (ea.getKey() == 'S'))
            {
                play(true);
            }

            if (ea.getKey() == '+')
            {
                for (int i = 0; i < g_animateCallback.size(); i++)
                {
                    g_animateCallback.at(i)->setTimeMultiplier(g_animateCallback.at(i)->getTimeMultiplier()*2.0);
                }
            }

            if (ea.getKey() == '-')
            {
                for (int i = 0; i < g_animateCallback.size(); i++)
                {
                    g_animateCallback.at(i)->setTimeMultiplier(g_animateCallback.at(i)->getTimeMultiplier() * 0.5);
                }
            }
        }

        return false;
    }
};

所有代码

#include <osgViewer/Viewer>
#include <osgDB/ReadFile>
#include <osg/NodeVisitor>
#include <osg/Transform>
#include <osg/AnimationPath>
#include <osgGA/GUIEventHandler>

std::vector<osg::AnimationPathCallback*> g_animateCallback;

class FindAnimate : public osg::NodeVisitor
{
public:
    FindAnimate():osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN){}
    void apply(osg::Transform& node)
    {
        if (node.getUpdateCallback())
        {
            osg::AnimationPathCallback* apc = dynamic_cast<osg::AnimationPathCallback*>(node.getUpdateCallback());
            if (NULL != apc)
            {
                g_animateCallback.push_back(apc);
            }
        }
        
        traverse(node);
    }
};

//true是播放动画,false是暂停动画
void play(bool isPlay)
{
    for (int i = 0; i < g_animateCallback.size(); i++)
    {
        g_animateCallback.at(i)->setPause(!isPlay);
    }
}

void reset()
{
    for (int i = 0; i < g_animateCallback.size(); i++)
    {
        g_animateCallback.at(i)->reset();
        g_animateCallback.at(i)->setPause(false);
    }
}

class MyEvent : public osgGA::GUIEventHandler
{
public:
    MyEvent(){}
    virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
    {
        if (ea.getEventType() == ea.KEYDOWN)
        {
            if ((ea.getKey() == 'r') || (ea.getKey() == 'R'))
            {
                reset();
            }

            if ((ea.getKey() == 'p') || (ea.getKey() == 'P'))
            {
                play(false);
            }

            if ((ea.getKey() == 's') || (ea.getKey() == 'S'))
            {
                play(true);
            }

            if (ea.getKey() == '+')
            {
                for (int i = 0; i < g_animateCallback.size(); i++)
                {
                    g_animateCallback.at(i)->setTimeMultiplier(g_animateCallback.at(i)->getTimeMultiplier()*2.0);
                }
            }

            if (ea.getKey() == '-')
            {
                for (int i = 0; i < g_animateCallback.size(); i++)
                {
                    g_animateCallback.at(i)->setTimeMultiplier(g_animateCallback.at(i)->getTimeMultiplier() * 0.5);
                }
            }
        }

        return false;
    }
};


int main()
{
    osgViewer::Viewer viewer;

    osg::Node* diban = osgDB::readNodeFile("diban.ive");

    FindAnimate fa;
    diban->accept(fa);

    viewer.setSceneData(diban);
    viewer.addEventHandler(new MyEvent);
    return viewer.run();
}

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片