osgAnimation 例子 注释

#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
}
 
 
 
原文转载地址
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值