歡迎技術交流和幫助,提供IT相關服務,索要源碼請聯繫博主QQ: 21497936,若該文爲原創文章,未經允許不得轉載
原博主博客地址:https://blog.csdn.net/qq21497936
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/99816214
目錄
飛行漫遊器代碼FlyCameraMainpulator.cpp
OSG三維開發專欄
《OSG開發筆記(三):OSG使用osgQt嵌入Qt應用程序》
《OSG開發筆記(四):OSG不使用osgQt重寫類嵌入Qt應用程序》:
《OSG開發筆記(七):OSG復現OpenGL入門示例和OSG座標系》
《OSG開發筆記(九):OSG模型的基本操作之添加/刪除、顯示/隱藏、開關節點開/關》:
《OSG開發筆記(十):OSG模型的變換之平移、旋轉和縮放》
《OSG開發筆記(十二):OSG基本幾何圖形、內置幾何類型》
《OSG開發筆記(十八):OSG鼠標拾取pick、拽託球體以及多光源》
《OSG開發筆記(二十一):OSG使用HUD繪製圖形以及紋理混合模式》
《OSG開發筆記(二十三):Qt使用QOpenGLWidget渲染OSG和地球儀》
《OSG開發筆記(二十四):OSG漫遊之平移、轉向和低擡頭》
《OSG開發筆記(二十五):OSG漫遊之CS移動、碰撞檢測與跳躍》
《OSG開發筆記(二十七):OSG路徑漫遊之錄製播放固定路徑動畫》
持續補充中…
OSG開發筆記(二十八):OSG模型固定路徑動畫
前言
動畫路徑中,物體本身的動畫移動也是一種動畫路徑,本篇實現飛機在上空盤旋。
Demo效果
路徑動畫
按照預先的路徑進行移動,是動畫路徑,動畫路徑有幾種:
- 視口路徑漫遊:按照預定的路線進行漫遊,需要設置動畫漫遊器;
- 模型路徑動畫:按照預定的路線進行移動。
動畫關鍵點
路徑動畫就是按照固定路徑進行移動的,路徑是預先固定的設計好的,所以是固定路徑的。
記錄A,B,C,D四個點,每個點的信息至少包含位置、朝向、時間等關鍵信息,形成一條動畫路徑。
動畫路徑類AnimationPath
在OSG中形成路徑的AnimationPath類,使用該類是可以插入關鍵點,函數如下:
void AnimationPath::insert(double time, const ControlPoint &controlPoint);
依據路徑漫遊的原因,可以知道參數的意義:
- time:記錄點的時間(類型double,單位秒)
- 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