OSG开发笔记(二十八):OSG模型固定路径动画

若该文为原创文章,未经允许不得转载
原博主博客地址:https://blog.csdn.net/qq21497936
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/99816214

目录

前言

Demo效果

路径动画

动画关键点

动画路径类AnimationPath

飞机模型

飞机模型动画

模型路径动画构架图

关键代码

初始化场景代码

飞行漫游器代码FlyCameraMainpulator.h

飞行漫游器代码FlyCameraMainpulator.cpp

工程模板:对应版本号1.25.0


OSG三维开发专栏

OSG开发笔记(一):OSG介绍、编译

OSG开发笔记(二):OSG帮助文档编译

OSG开发笔记(三):OSG使用osgQt嵌入Qt应用程序

OSG开发笔记(四):OSG不使用osgQt重写类嵌入Qt应用程序》:

OSG开发笔记(五):OSG场景理解与基础类概述

OSG开发笔记(六):OSG内存管理

OSG开发笔记(七):OSG复现OpenGL入门示例和OSG坐标系

OSG开发笔记(八):OSG模型文件存储与读取

OSG开发笔记(九):OSG模型的基本操作之添加/删除、显示/隐藏、开关节点开/》:

OSG开发笔记(十):OSG模型的变换之平移、旋转和缩放

OSG开发笔记(十一):OSG渲染状态与2D纹理映射

OSG开发笔记(十二):OSG基本几何图形、内置几何类型

OSG开发笔记(十三):OSG三维纹理映射(体渲染)

OSG开发笔记(十四):OSG交互

OSG开发笔记(十五):OSG光照

OSG开发笔记(十六):OSG视口、相机和视点

OSG开发笔记(十七):OSG中的相机移动

OSG开发笔记(十八):OSG鼠标拾取pick、拽托球体以及多光源

OSG开发笔记(十九):OSG文字显示

OSG开发笔记(二十):OSG使用HUD显示文字

OSG开发笔记(二十一):OSG使用HUD绘制图形以及纹理混合模式

OSG开发笔记(二十二):OSG场景背景

OSG开发笔记(二十三):Qt使用QOpenGLWidget渲染OSG地球仪

OSG开发笔记(二十四):OSG漫游之平移、转向和低抬头

OSG开发笔记(二十五):OSG漫游之CS移动、碰撞检测与跳跃

OSG开发笔记(二十六):OSG漫游之上下楼梯

OSG开发笔记(二十七):OSG路径漫游之录制播放固定路径动画

OSG开发笔记(二十八):OSG模型固定路径动画

  持续补充中…

 

        OSG开发笔记(二十八):OSG模型固定路径动画

前言

       动画路径中,物体本身的动画移动也是一种动画路径,本篇实现飞机在上空盘旋。

 

Demo效果

        

 

路径动画

       按照预先的路径进行移动,是动画路径,动画路径有几种:

  • 视口路径漫游:按照预定的路线进行漫游,需要设置动画漫游器;
  • 模型路径动画:按照预定的路线进行移动。

 

动画关键点

       路径动画就是按照固定路径进行移动的,路径是预先固定的设计好的,所以是固定路径的。

       

       记录A,B,C,D四个点,每个点的信息至少包含位置、朝向、时间等关键信息,形成一条动画路径。

 

动画路径类AnimationPath

       在OSG中形成路径的AnimationPath类,使用该类是可以插入关键点,函数如下:

void AnimationPath::insert(double time, const ControlPoint &controlPoint);

       依据路径漫游的原因,可以知道参数的意义:

  1. time:记录点的时间(类型double,单位秒)
  2. controlPoint:控制点,其构函数包含位置和朝向;

飞机模型

       正常加载飞机模型,对飞机模型进行调整,因为飞机模型比较大:

       先缩小为原来的0.1,缩小后移动到边界-10,移动后发现没有移动多少,是因为先缩小了是模型坐标空间,所以移动了其实是0.1*-10=-1.0。

       将之前的变换调整操作顺序先移动-10,然后缩小为原来的0.1。

       但是此处对模型变换还是基于模型坐标空间的,所以需要将其转换为一个符合世界坐标比例的模型坐标,所以设置两层变换模型,代码如下:

         

 

飞机模型动画

       填入飞机模型时进行了变换,动画需要对模型结点进行操作,上面使用了两层变换节点,最外层的变换节点的中心,仍然是0,0,0,尝试对X轴缩小1/2试试,结果是在中间与地板边缘1/2处,如下图:

         

          

       所以要飞机来回盘旋,是绕Z轴转动就行了,创建动画路径,4个点,如下图:

       

        

       运行效果:

      

       飞机已经在旋转,但是发现飞机是平的旋转,所以需要向圆心倾斜,那么本地模型中,应该是绕Y轴旋转-90,修改代码如下:

       

 

模型路径动画构架图

       模型路径动画,看上面构架图,其实属性节点在未操作与世界坐标系是一致的,之前使用了两层矩阵变换节点,现在优化掉一层,结构如下:

     

 

关键代码

初始化场景代码

osg::ref_ptr<osg::Node> OsgWidget::getFlyAnimation()
{
    // 隐藏按键面板
    ui->groupBox_pannel->setVisible(false);
    osg::ref_ptr<osg::Group> pGroup = new osg::Group;
    // 首先画个地板
    {
        osg::ref_ptr<osg::Geode> pGeode = new osg::Geode;
        // 地板格子长、宽、行、列
        float dx = 1.0f;
        float dy = 1.0f;
        int rows = 20;
        int cols = 20;
        osg::Vec3 vec3center(0.0f - dx * rows / 2 - dx / 2, 
                             0.0f - dx * cols / 2 - dx / 2, 
                             0.0f);
        // 顶点
        osg::Vec3Array *pVec3Array = new osg::Vec3Array;
        for(int index = 0; index <= rows; index++)
        {
            for(int index2 = 0; index2 <= cols; index2++)
            {
                pVec3Array->push_back(osg::Vec3(index2 * dx, index * dy, 0) + vec3center);
            }
        }
        // 颜色
        osg::Vec4Array *pVec4ColorArray = new osg::Vec4Array;
        pVec4ColorArray->push_back(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f));
        pVec4ColorArray->push_back(osg::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
        // 索引(使用索引,之前的demo没有使用过,此源码第一次使用)之处
        osg::UIntArray * pCoordIndices = new osg::UIntArray;
        osg::UIntArray * pColorIndices = new osg::UIntArray;
        for(int index = 0; index < rows; index++)
        {
            for(int index2 = 0; index2 < cols; index2++)
            {
                // 顶点索引
                pCoordIndices->push_back((index2    ) + (index    ) * (cols + 1));
                pCoordIndices->push_back((index2 + 1) + (index    ) * (cols + 1));
                pCoordIndices->push_back((index2 + 1) + (index + 1) * (cols + 1));
                pCoordIndices->push_back((index2    ) + (index + 1) * (cols + 1));
                pColorIndices->push_back(((index2   ) + (index + 1) * (cols+1) ) 
                                                       % pVec4ColorArray->size());
            }
        }
        // 法线
        osg::Vec3Array * pNormal = new osg::Vec3Array;
        pNormal->push_back(osg::Vec3(0.0f, 0.0f, 1.0f));
        // 图形(支持索引的需要使用deprecated_osg: :Geometry)
        deprecated_osg::Geometry * pGeometry = new deprecated_osg::Geometry;

        pGeometry->setVertexArray(pVec3Array);
        pGeometry->setVertexIndices(pCoordIndices);

        pGeometry->setColorArray(pVec4ColorArray);
        pGeometry->setColorIndices(pColorIndices);
        pGeometry->setColorBinding(deprecated_osg::Geometry::BIND_PER_PRIMITIVE);

        pGeometry->setSecondaryColorArray(pVec4ColorArray);
        pGeometry->setSecondaryColorIndices(pColorIndices);
        pGeometry->setSecondaryColorBinding(deprecated_osg::Geometry::BIND_PER_PRIMITIVE);

        pGeometry->setNormalArray(pNormal);
        pGeometry->setNormalBinding(deprecated_osg::Geometry::BIND_OVERALL);

        pGeometry->addPrimitiveSet(
                new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, pCoordIndices->size()));
        osg::StateSet *pStateSet = pGeometry->getOrCreateStateSet();
        pStateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
        pStateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::ON);
        pGeode->addDrawable(pGeometry);
        pGroup->addChild(pGeode);
    }
    // 添加飞机模型
    {
        osg::Node *pNode = osgDB::readNodeFile("cessna.osg");
        osg::MatrixTransform *pMatTrans = new osg::MatrixTransform();
        pMatTrans->addChild(pNode);
        // 对模型进行调整
        osg::Matrix matrix = pMatTrans->getMatrix();
        // Z轴和Z轴偏移10
        matrix = osg::Matrix::translate(-10.0f, 0.0f, 5.0f) * matrix;
        // 绕Y轴旋转-90°
        matrix = osg::Matrix::rotate(-90.0f, osg::Vec3f(0.0f, 1.0f, 0.0f)) * matrix;
        // 所小为原来的1/10
        matrix = osg::Matrix::scale(0.1f, 0.1f, 0.1f) * matrix;
        pMatTrans->setMatrix(matrix);
        // 以上对模型进行了变换,动画需要对模型结点,直接操作上面的变换是基于模型本地坐标的
        // 如缩小了1/10,那么移动10,其实只有10*0.01=1
        // 再建立一个变换是将模型添加到变换结点中
        osg::MatrixTransform *pMatTrans2 = new osg::MatrixTransform();
        pMatTrans2->addChild(pMatTrans);
        // 创建路径
        osg::AnimationPath * pAnimationPath = new osg::AnimationPath();
        // point 1->4
        pAnimationPath->insert(0, osg::AnimationPath::ControlPoint(
                                   osg::Vec3f(0.0f, 0.0f, 0.0f),
                                   osg::Quat(90.0f, osg::Vec3f(0.0f, 0.0f, 1.0f))));
        pAnimationPath->insert(1, osg::AnimationPath::ControlPoint(
                                   osg::Vec3f(0.0f, 0.0f, 0.0f),
                                   osg::Quat(180, osg::Vec3f(0.0f, 0.0f, 1.0f))));
        pAnimationPath->insert(2, osg::AnimationPath::ControlPoint(
                                   osg::Vec3f(0.0f, 0.0f, 0.0f),
                                   osg::Quat(270, osg::Vec3f(0.0f, 0.0f, 1.0f))));
        pAnimationPath->insert(3, osg::AnimationPath::ControlPoint(
                                   osg::Vec3f(0.0f, 0.0f, 0.0f),
                                   osg::Quat(90, osg::Vec3f(0.0f, 0.0f, 1.0f))));
        osg::ref_ptr<osg::PositionAttitudeTransform> pPositionAttitudeTransform
                = new osg::PositionAttitudeTransform;
        pPositionAttitudeTransform->setUpdateCallback(
                    new osg::AnimationPathCallback(pAnimationPath, 0.0f, 1.0f));
        pPositionAttitudeTransform->addChild(pMatTrans);
        pGroup->addChild(pPositionAttitudeTransform);

    }
    // 设置漫游器:使用之前基础CS的漫游器
    {
        FlyCameraMainpulator *pFlyCameraMainpulator = new FlyCameraMainpulator;
        pFlyCameraMainpulator->setHitsNode(pGroup.get());
        pFlyCameraMainpulator->setOsgWidget(this);
        _pViewer->setCameraManipulator(pFlyCameraMainpulator);

    }
    return pGroup.get();
}

飞行漫游器代码FlyCameraMainpulator.h

#ifndef FLYCAMERAMAINPULATOR_H
#define FLYCAMERAMAINPULATOR_H

#include <osgGA/CameraManipulator>
#include <osgGA/GUIActionAdapter>
#include <osgGA/GUIEventAdapter>
#include <QObject>

class OsgWidget;

class FlyCameraMainpulator : public osgGA::CameraManipulator, public QObject
{
public:
    FlyCameraMainpulator();
    ~FlyCameraMainpulator();

public:
    virtual void setByMatrix(const osg::Matrixd& matrix);         // 设置相机的位置姿态矩阵
    virtual void setByInverseMatrix(const osg::Matrixd& matrix);  // 设置相机的视图矩阵
    virtual osg::Matrixd getMatrix() const;                       // 获取相机的姿态矩阵
    virtual osg::Matrixd getInverseMatrix() const;                // 获取相机的视图矩阵

    virtual bool handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &us);

public:
    void setHitsNode(osg::Node *pNode);

    void setOsgWidget(OsgWidget *pOsgWidget);

protected:
    bool isHits(osg::Vec3f oldPos, osg::Vec3 newPos);

protected:

private:
    osg::Vec3 _position;        // 视点当前位置

    osg::Vec3 _rotation;        // 朝向
    float _moveStep;            // 移动步长5
    float _rotateStep;          // 旋转步长

    float _originAngleX;        // X轴初始化时偏移角度
    float _originAngleY;        // Y轴初始化时偏移角度
    float _originAngleZ;        // Z轴初始化时偏移角度
    float _offsetAngleX;        // X轴当前旋转角度
    float _offsetAngleY;        // Y轴当前旋转角度
    float _offsetAngleZ;        // Z轴当前旋转角度

    osg::Node *_pNode;

    OsgWidget *_pOsgWidget;
};

#endif // MYCAMERAMAINPULATOR_H

飞行漫游器代码FlyCameraMainpulator.cpp

#include "FlyCameraMainpulator.h"
#include <QDebug>
#include "osg/Math"
#include "osg/LineSegment"
#include "OsgWidget.h"

FlyCameraMainpulator::FlyCameraMainpulator()
    :  QObject(0),
      _originAngleX(90.0f),
      _originAngleY(0.0f),
      _originAngleZ(0.0f),
      _offsetAngleX(-25.0f),
      _offsetAngleY(0.0f),
      _offsetAngleZ(90.0f),
      _pNode(0)
{
    // 使用漫游器的初始化位置
    _position = osg::Vec3(30.0f, 0.0f, 15.0f);
    _rotation = osg::Vec3( osg::DegreesToRadians(_originAngleX + _offsetAngleX),
                           osg::DegreesToRadians(_originAngleY + _offsetAngleY),
                           osg::DegreesToRadians(_originAngleZ + _offsetAngleZ));
    _moveStep = 0.1f;
    _rotateStep = 1.5f;

}

FlyCameraMainpulator::~FlyCameraMainpulator()
{

}

void FlyCameraMainpulator::setByMatrix(const osg::Matrixd &matrix)
{

}

void FlyCameraMainpulator::setByInverseMatrix(const osg::Matrixd &matrix)
{
    computeHomePosition();
}

osg::Matrixd FlyCameraMainpulator::getMatrix() const
{
    osg::Matrixd mat;
    mat.makeTranslate(_position);
    return osg::Matrixd::rotate(_rotation.x(), osg::X_AXIS,
                                _rotation.y(), osg::Y_AXIS,
                                _rotation.z(), osg::Z_AXIS) * mat;
}

osg::Matrixd FlyCameraMainpulator::getInverseMatrix() const
{
    osg::Matrixd mat;
    mat.makeTranslate(_position);
    return osg::Matrixd::inverse(osg::Matrixd::rotate(_rotation.x(), osg::X_AXIS,
                                                      _rotation.y(), osg::Y_AXIS,
                                                      _rotation.z(), osg::Z_AXIS) * mat);
}

bool FlyCameraMainpulator::handle(const osgGA::GUIEventAdapter &ea, 
                                  osgGA::GUIActionAdapter &us)
{
    float offsetX = 0.0f;
    float offsetY = 0.0f;
    float offsetZ = 0.0f;
    osg::Vec3f newPosition;
    switch (ea.getEventType())
    {
    case osgGA::GUIEventAdapter::KEYDOWN:
        switch (ea.getKey()) {
        case osgGA::GUIEventAdapter::KEY_E:
        case osgGA::GUIEventAdapter::KEY_Right:
            _offsetAngleZ = _offsetAngleZ - _rotateStep;
            _rotation[2] = osg::DegreesToRadians(_originAngleZ + _offsetAngleZ);
            break;
        case osgGA::GUIEventAdapter::KEY_Q:
        case osgGA::GUIEventAdapter::KEY_Left:
            _offsetAngleZ = _offsetAngleZ + _rotateStep;
            _rotation[2] = osg::DegreesToRadians(_originAngleZ + _offsetAngleZ);
            break;
        case osgGA::GUIEventAdapter::KEY_W:
            offsetY = _moveStep * cos(osg::DegreesToRadians(-_offsetAngleZ));
            offsetX = _moveStep * sin(osg::DegreesToRadians(-_offsetAngleZ));
            newPosition = _position + osg::Vec3f(offsetX, offsetY, offsetZ);
            if(!isHits(_position, newPosition))
            {
                _position = newPosition;
            }
            break;
        case osgGA::GUIEventAdapter::KEY_S:
            offsetY = _moveStep * cos(osg::DegreesToRadians(-_offsetAngleZ));
            offsetX = _moveStep * sin(osg::DegreesToRadians(-_offsetAngleZ));
            newPosition = _position - osg::Vec3f(offsetX, offsetY, offsetZ);
            if(!isHits(_position, newPosition))
            {
                _position = newPosition;
            }
            break;
        case osgGA::GUIEventAdapter::KEY_A:
            offsetX = -_moveStep * cos(osg::DegreesToRadians(-_offsetAngleZ));
            offsetY =  _moveStep * sin(osg::DegreesToRadians(-_offsetAngleZ));
            newPosition = _position + osg::Vec3f(offsetX, offsetY, offsetZ);
            if(!isHits(_position, newPosition))
            {
                _position = newPosition;
            }
            break;
        case osgGA::GUIEventAdapter::KEY_D:
            offsetX =  _moveStep * cos(osg::DegreesToRadians(-_offsetAngleZ));
            offsetY = -_moveStep * sin(osg::DegreesToRadians(-_offsetAngleZ));
            newPosition = _position + osg::Vec3f(offsetX, offsetY, offsetZ);
            if(!isHits(_position, newPosition))
            {
                _position = newPosition;
            }
            break;
        case osgGA::GUIEventAdapter::KEY_Up:
            if(_offsetAngleX > 90)
            {
                break;
            }
            _offsetAngleX += _rotateStep;
            _rotation[0] = osg::DegreesToRadians(_originAngleX + _offsetAngleX);
            break;
        case osgGA::GUIEventAdapter::KEY_Down:
            if(_offsetAngleX < -90)
            {
                break;
            }
            _offsetAngleX -= _rotateStep;
            _rotation[0] = osg::DegreesToRadians(_originAngleX + _offsetAngleX);
            break;
        case osgGA::GUIEventAdapter::KEY_Space:
            _position[2] += 0.1f;
            break;
        case osgGA::GUIEventAdapter::KEY_Control_L:
            _position[2] -= 0.1f;
            break;
        default:
            break;
        }
        break;
    default:
        break;
    }
    return true;
}

void FlyCameraMainpulator::setHitsNode(osg::Node *pNode)
{
    _pNode = pNode;
}

bool FlyCameraMainpulator::isHits(osg::Vec3f oldPos, osg::Vec3 newPos)
{
    bool ret = false;
    if(_pNode == 0)
    {
        return ret;
    }
    // osg3.4.0 类 osgUtil::IntersectionVisitor已经细化,此处用
    osg::ref_ptr<osgUtil::LineSegmentIntersector> pLs = 
                    new osgUtil::LineSegmentIntersector(oldPos, newPos);
    osg::ref_ptr<osgUtil::IntersectionVisitor> pIv = 
                    new osgUtil::IntersectionVisitor(pLs);
    // 碰撞检测
    _pNode->accept(*pIv);
    if(pLs->containsIntersections())
    {
//        qDebug() << "hit";
        ret = true;
    }else{
        ret = false;
    }
    return ret;
}

void FlyCameraMainpulator::setOsgWidget(OsgWidget *pOsgWidget)
{
    _pOsgWidget = pOsgWidget;
}

 

OSG:目前测试Bug

       动画初始化路径,上一篇章中漫游的起点是随机的,本章节使用了模型,发现模型没有此问题,所以目前发现,只有上一篇章《OSG开发笔记(二十七):OSG路径漫游之录制播放固定路径动画》中的固定路径漫游存在此问题。

       

 

工程模板:对应版本号1.25.0

        对应版本号1.25.0


原博主博客地址:https://blog.csdn.net/qq21497936
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/99816214

发布了227 篇原创文章 · 获赞 237 · 访问量 40万+

猜你喜欢

转载自blog.csdn.net/qq21497936/article/details/99816214