OSG 飞机路径动画控制
注:摘自《三维渲染引擎编程指南》
本文实现一个飞机,在地图上空盘旋,读者可以根据自己需要进行更改;
代码如下:
//2017. 8 .29
#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>
#include <osg/Node>
#include <osg/Geode>
#include <osg/Group>
#include <osg/Math>
#include <osg/AnimationPath>
#include <osg/MatrixTransform>
#include <osg/PositionAttitudeTransform>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgUtil/Optimizer>
//路径动画控制事件
class AnimationEventHandler : public osgGA::GUIEventHandler
{
public:
AnimationEventHandler(osgViewer::Viewer &vr):viewer(vr){}
//事件处理
virtual bool handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &)
{
//创建动画更新回调对象
osg::ref_ptr<osg::AnimationPathCallback> animationPathCallback = new osg::AnimationPathCallback();
osg::ref_ptr<osg::Group> group = dynamic_cast<osg::Group*>(viewer.getSceneData());
//取得节点的动画属性
animationPathCallback = dynamic_cast<osg::AnimationPathCallback*>(group->getChild(0)->getUpdateCallback());
switch(ea.getEventType())
{
case (osgGA::GUIEventAdapter::KEYDOWN):
{
if (ea.getKey() == 'p')
{
//暂停
animationPathCallback->setPause(true);
return true;
}
if (ea.getKey() == 'k')
{
//开始
animationPathCallback->setPause(false);
return true;
}
if (ea.getKey() == 'r')
{
//重新开始
animationPathCallback->reset();
return true;
}
break;
}
default:
break;
}
return false;
}
public:
osgViewer::Viewer &viewer;
};
//创建路径
osg::ref_ptr<osg::AnimationPath> creatAnimationPath(osg::Vec3 ¢er, float radius, float looptime)
{
//创建一个Path对象
osg::ref_ptr<osg::AnimationPath> animationPath = new osg::AnimationPath();
//设置动画模式为循环(LOOP)
//LOOP:循环 SWING:单摆 NO_LOOPING:不循环
animationPath->setLoopMode(osg::AnimationPath::LOOP);
//关键点数
int numPoint = 60;
//每次偏移角度
float yaw = 0.0f;
float yaw_delta = 2.0 * osg::PI / ((float)(numPoint - 1.0f));
//倾斜角度
float roll = osg::inDegrees(45.0f);
//时间偏移
float time = 0.0f;
float time_delta = looptime / ((float)(numPoint));
for (int i = 0; i < numPoint; i++)
{
//关键点位置
osg::Vec3 position(center + osg::Vec3(sinf(yaw) * radius, cosf(yaw) * radius,0.0f));
//关键点角度
osg::Quat rotation(osg::Quat(roll, osg::Vec3(0.0, 1.0, 0.0)) * osg::Quat( -(yaw + osg::inDegrees(90.f)), osg::Vec3(0.0, 0.0, 1.0)));
//插入Path,把关键点与时间压入形成Path
animationPath->insert(time, osg::AnimationPath::ControlPoint(position, rotation));
yaw += yaw_delta;
time += time_delta;
}
//返回Path
return animationPath.get();
}
int main()
{
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
osg::ref_ptr<osg::Group> root = new osg::Group();
//读入飞机模型
osg::ref_ptr<osg::Node> cessna = osgDB::readNodeFile("cessna.osg");
//读入地形模型
osg::ref_ptr<osg::Node> node = osgDB::readNodeFile("lz.osg");
//得到包围盒,来确定动画旋转中心
const osg::BoundingSphere &bs = cessna->getBound();
osg::Vec3 position = bs.center() + osg::Vec3(0.0f, 0.0f, 200.f);
//缩放比例,如果比例不当,模型会看不见
float size = 100.0f / bs.radius() * 0.3f;
//创建路径
osg::ref_ptr<osg::AnimationPath> animationPath = new osg::AnimationPath();
animationPath = creatAnimationPath(position, 200.0f, 10.0f);
osg::ref_ptr<osg::MatrixTransform> mt = new osg::MatrixTransform();
//OSG确保只有STATIC数据可以进行图形渲染
mt->setMatrix(osg::Matrix::translate( -bs.center()) * osg::Matrix::scale(size, size, size) * osg::Matrix::rotate(osg::inDegrees(-180.0f), 0.0f,0.0f,1.0f));
mt->addChild(cessna.get());
osg::ref_ptr<osg::PositionAttitudeTransform> pat = new osg::PositionAttitudeTransform();
//设置更新回调
pat->setUpdateCallback(new osg::AnimationPathCallback(animationPath.get(), 0.0f, 1.0f));
pat->addChild(mt.get());
root->addChild(pat.get());
root->addChild(node.get());
//优化场景数据
osgUtil::Optimizer optimizer;
optimizer.optimize(root.get());
viewer->setSceneData(root.get());
//添加路径动画
viewer->addEventHandler(new AnimationEventHandler(*(viewer.get())));
viewer->realize();
viewer->run();
return 0;
}