本节功能
本节后几个章节会介绍和动画有关的课程。
本节实现一个从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();
}
暂无评论内容