#include <osg/Notify>
#include <osg/MatrixTransform>
#include <osg/PositionAttitudeTransform>
#include <osg/Geometry>
#include <osg/Geode>
#include <osgUtil/Optimizer>
#include <osgDB/Registry>
#include <osgDB/ReadFile>
#include <osgGA/TrackballManipulator>
#include <osgGA/FlightManipulator>
#include <osgGA/DriveManipulator>
#include <osgSim/OverlayNode>
#include <osgViewer/Viewer>
#include <iostream>
// 创建动画路径,参数有中心点,半径,以及循环时间
osg::AnimationPath* createAnimationPath(const osg::Vec3& center,float radius,double looptime)
{
// 实例化动画路径类
// set up the animation path
osg::AnimationPath* animationPath = new osg::AnimationPath;
// 设置动画路径的播放模式,当前使用循环模式(SWING LOOP NO_LOOPING )SWING就是在一个区间内顺逆方向循环,LOOP是顺一个方向循环
animationPath->setLoopMode(osg::AnimationPath::LOOP);
// 分40个阶段 这个意思就是分为四十个控制点,犹如四十条边的多边形,沿着这个路线飞行
int numSamples = 40;
float yaw = 0.0f;
// 偏航的分量,这是一个角度分量,算出来每一个多边形的边对应的角度分量2*PI/(n-1)
float yaw_delta = 2.0f*osg::PI/((float)numSamples-1.0f);
// 旋转角度为30度
float roll = osg::inDegrees(30.0f);
// 时间分量
double time=0.0f;
//平均的两个控制点之间的时间
double time_delta = looptime/(double)numSamples;
// 将时间控制点与位置坐标插入到动画路径中
for(int i=0;i<numSamples;++i)
{
//这儿是计算出来每个多边形的顶点的坐标,(sinx*r,cosx*r,0.0)是相当于中心点的增量
osg::Vec3 position(center+osg::Vec3(sinf(yaw)*radius,cosf(yaw)*radius,0.0f));
//四元数表示方向,表示在3D空间中的旋转方向**四元数很复杂**这个表示,绕x轴旋转roll,y轴旋转-(yaw+90),z轴0
osg::Quat rotation(osg::Quat(roll,osg::Vec3(0.0,1.0,0.0))*osg::Quat(-(yaw+osg::inDegrees(90.0f)),osg::Vec3(0.0,0.0,1.0)));
//和flash动画类似,控制点有位置和转动角度,这样控制点之间是均匀运动
animationPath->insert(time,osg::AnimationPath::ControlPoint(position,rotation));
//递增yaw和time
yaw += yaw_delta;
time += time_delta;
}
return animationPath;
}
// 创建底板,中心点位置在center,半径为radius
osg::Node* createBase(const osg::Vec3& center,float radius)
{
// 一个10x10的底板
int numTilesX = 10;
int numTilesY = 10;
// 长度与宽度的尺寸
float width = 2*radius;
float height = 2*radius;
// 计算初始位置与x、y的分量
osg::Vec3 v000(center - osg::Vec3(width*0.5f,height*0.5f,0.0f));
osg::Vec3 dx(osg::Vec3(width/((float)numTilesX),0.0,0.0f));
osg::Vec3 dy(osg::Vec3(0.0f,height/((float)numTilesY),0.0f));
// 计算每个小格子的顶点坐标并压入数组中
// fill in vertices for grid, note numTilesX+1 * numTilesY+1...
osg::Vec3Array* coords = new osg::Vec3Array;
int iy;
for(iy=0;iy<=numTilesY;++iy)
{
for(int ix=0;ix<=numTilesX;++ix)
{
coords->push_back(v000+dx*(float)ix+dy*(float)iy);
}
}
// 设置颜色的数组,当前为黑白色两种颜色
//Just two colours - black and white.
osg::Vec4Array* colors = new osg::Vec4Array;
colors->push_back(osg::Vec4(1.0f,1.0f,1.0f,1.0f)); // white
colors->push_back(osg::Vec4(0.0f,0.0f,0.0f,1.0f)); // black
int numColors=colors->size();
// 设置绘制四边形的顶点索引与每个四边形的颜色
int numIndicesPerRow=numTilesX+1;
osg::UByteArray* coordIndices = new osg::UByteArray; // assumes we are using less than 256 points...
osg::UByteArray* colorIndices = new osg::UByteArray;
for(iy=0;iy<numTilesY;++iy)
{
for(int ix=0;ix<numTilesX;++ix)
{
// four vertices per quad.
coordIndices->push_back(ix +(iy+1)*numIndicesPerRow);
coordIndices->push_back(ix +iy*numIndicesPerRow);
coordIndices->push_back((ix+1)+iy*numIndicesPerRow);
coordIndices->push_back((ix+1)+(iy+1)*numIndicesPerRow);
// one color per quad
colorIndices->push_back((ix+iy)%numColors);
}
}
// 设置法线向量
// set up a single normal
osg::Vec3Array* normals = new osg::Vec3Array;
normals->push_back(osg::Vec3(0.0f,0.0f,1.0f));
// 设置顶点坐标数组
osg::Geometry* geom = new osg::Geometry;
geom->setVertexArray(coords);
geom->setVertexIndices(coordIndices);
// 设置颜色数组
geom->setColorArray(colors);
geom->setColorIndices(colorIndices);
geom->setColorBinding(osg::Geometry::BIND_PER_PRIMITIVE);
// 设置法线数组
geom->setNormalArray(normals);
geom->setNormalBinding(osg::Geometry::BIND_OVERALL);
// 需要绘制什么形状的图形,当前为四边形
geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,coordIndices->size()));
// 将绘制的的图形添加到osg::Geode中
osg::Geode* geode = new osg::Geode;
geode->addDrawable(geom);
return geode;
}
// 创建移动的模型,中心点在center,半径为radius
osg::Node* createMovingModel(const osg::Vec3& center, float radius)
{
float animationLength = 10.0f;
// 创建动画路径,中心点在center,半径为radius,循环时间为10.0f
osg::AnimationPath* animationPath = createAnimationPath(center,radius,animationLength);
osg::Group* model = new osg::Group;
// 从外部读取一个模型glider.osg作为飞行的模型
osg::Node* glider = osgDB::readNodeFile("glider.osg");
if (glider)
{
// 根据模型的包围球来计算矩阵
// 平移到原点,缩放模型,然后沿z轴旋转-90度
const osg::BoundingSphere& bs = glider->getBound();
float size = radius/bs.radius()*0.3f;
osg::MatrixTransform* positioned = new osg::MatrixTransform;
//设置这个值在对象生命周期内为静态的不可改变的数值,或者动态的在对象生命周期内变化的值
positioned->setDataVariance(osg::Object::STATIC);
//设置转移矩阵参数
positioned->setMatrix(osg::Matrix::translate(-bs.center())* /*表示平移物体,注意osg的坐标系是z轴是向上的*/
osg::Matrix::scale(size,size,size)* /*x,y,z轴的放缩比例*/
osg::Matrix::rotate(osg::inDegrees(-90.0f),0.0f,0.0f,1.0f)); /*x,y,z轴的旋转角度*/
positioned->addChild(glider);
// 设置动画路径的回调函数,使在渲染循环中不停的沿动画路径移动,设置坐标系变换
osg::PositionAttitudeTransform* xform = new osg::PositionAttitudeTransform;
xform->setUpdateCallback(new osg::AnimationPathCallback(animationPath,0.0,1.0));
xform->addChild(positioned);
model->addChild(xform);
}
// 从外部读取一个模型cessna.osg
// 操作同上,设置动画路径的回调函数,使在渲染的循环中不停的沿动画路径移动
osg::Node* cessna = osgDB::readNodeFile("cessna.osg");
if (cessna)
{
const osg::BoundingSphere& bs = cessna->getBound();
float size = radius/bs.radius()*0.3f;
osg::MatrixTransform* positioned = new osg::MatrixTransform;
positioned->setDataVariance(osg::Object::STATIC);
positioned->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));
positioned->addChild(cessna);
osg::MatrixTransform* xform = new osg::MatrixTransform;
xform->setUpdateCallback(new osg::AnimationPathCallback(animationPath,0.0f,2.0));
xform->addChild(positioned);
model->addChild(xform);
}
return model;
}
// 创建覆盖图,根据覆盖的实现技术来设置
osg::Node* createModel(bool overlay, osgSim::OverlayNode::OverlayTechnique technique)
{
osg::Vec3 center(0.0f,0.0f,0.0f);
float radius = 100.0f;
osg::Group* root = new osg::Group;
// 创建底板与飞行的模型
float baseHeight = center.z()-radius*0.5;
osg::Node* baseModel = createBase(osg::Vec3(center.x(), center.y(), baseHeight),radius);
osg::Node* movingModel = createMovingModel(center,radius*0.8f);
// 是否设置覆盖图
if (overlay)
{
// 根据命令行传入的参数来设置
osgSim::OverlayNode* overlayNode = new osgSim::OverlayNode(technique);
overlayNode->setContinuousUpdate(true);
// 需要设置的覆盖的图的模型为飞行的模型,当前为从外部加载的模型
overlayNode->setOverlaySubgraph(movingModel);
// 设置覆盖图距离底板的高度
overlayNode->setOverlayBaseHeight(baseHeight-0.01);
overlayNode->addChild(baseModel);
// 将覆盖图的节点加入到根节点中
root->addChild(overlayNode);
}
else
{
// 不设置覆盖图
root->addChild(baseModel);
}
root->addChild(movingModel);
return root;
}
int main( int argc, char **argv )
{
// 是否使用覆盖模拟
bool overlay = false;
// 使用命令行参数实例化osg::ArgumentParset类,方便以后的操作
osg::ArgumentParser arguments(&argc,argv);
while (arguments.read("--overlay")) overlay = true;
// 获得覆盖节点采用哪种技术实现,提供三种实现技术
osgSim::OverlayNode::OverlayTechnique technique = osgSim::OverlayNode::OBJECT_DEPENDENT_WITH_ORTHOGRAPHIC_OVERLAY;
while (arguments.read("--object")) { technique = osgSim::OverlayNode::OBJECT_DEPENDENT_WITH_ORTHOGRAPHIC_OVERLAY; overlay=true; }
while (arguments.read("--ortho") || arguments.read("--orthographic")) { technique = osgSim::OverlayNode::VIEW_DEPENDENT_WITH_ORTHOGRAPHIC_OVERLAY; overlay=true; }
while (arguments.read("--persp") || arguments.read("--perspective")) { technique = osgSim::OverlayNode::VIEW_DEPENDENT_WITH_PERSPECTIVE_OVERLAY; overlay=true; }
// initialize the viewer.
osgViewer::Viewer viewer;
// 创建底板与飞行的物体,飞行物体通过外部读取
// load the nodes from the commandline arguments.
osg::Node* model = createModel(overlay, technique);
if (!model)
{
return 1;
}
// 创建一个osg::MatrixTransform并设置场景沿x轴旋转30度
// tilt the scene so the default eye position is looking down on the model.
osg::MatrixTransform* rootnode = new osg::MatrixTransform;
rootnode->setMatrix(osg::Matrix::rotate(osg::inDegrees(30.0f),1.0f,0.0f,0.0f));
rootnode->addChild(model);
/// 对整个场景进行优化
// run optimization over the scene graph
osgUtil::Optimizer optimzer;
optimzer.optimize(rootnode);
// 将整个节点设置到场景中进行渲染
// set the scene to render
viewer.setSceneData(rootnode);
// 设置相机的操作器,当前使用跟踪球的模式进行操作
viewer.setCameraManipulator(new osgGA::TrackballManipulator());
// viewer.setUpViewOnSingleScreen(1);
// 提供两中渲染的循环模式
// 第一中采用老式的模式
// 第二中采用封装模式
#if 0
// use of custom simulation time.
viewer.realize();
double simulationTime = 0.0;
while (!viewer.done())
{
viewer.frame(simulationTime);
simulationTime += 0.001;
}
return 0;
#else
// normal viewer usage.
return viewer.run();
#endif
}
转载请注明出处,有技术问题,欢迎互相交流,或者留言.