OSG開發筆記(二十八):OSG模型固定路徑動畫

歡迎技術交流和幫助,提供IT相關服務,索要源碼請聯繫博主QQ: 21497936,若該文爲原創文章,未經允許不得轉載
原博主博客地址: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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章