若该文为原创文章,未经允许不得转载
原博主博客地址: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使用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模型固定路径动画》
持续补充中…
动画路径中,物体本身的动画移动也是一种动画路径,本篇实现飞机在上空盘旋。
按照预先的路径进行移动,是动画路径,动画路径有几种:
路径动画就是按照固定路径进行移动的,路径是预先固定的设计好的,所以是固定路径的。
记录A,B,C,D四个点,每个点的信息至少包含位置、朝向、时间等关键信息,形成一条动画路径。
在OSG中形成路径的AnimationPath类,使用该类是可以插入关键点,函数如下:
void AnimationPath::insert(double time, const ControlPoint &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 OsgWidget::getFlyAnimation()
{
// 隐藏按键面板
ui->groupBox_pannel->setVisible(false);
osg::ref_ptr pGroup = new osg::Group;
// 首先画个地板
{
osg::ref_ptr 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 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();
}
#ifndef FLYCAMERAMAINPULATOR_H
#define FLYCAMERAMAINPULATOR_H
#include
#include
#include
#include
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
#include "FlyCameraMainpulator.h"
#include
#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 pLs =
new osgUtil::LineSegmentIntersector(oldPos, newPos);
osg::ref_ptr 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开发笔记(二十七):OSG路径漫游之录制播放固定路径动画》中的固定路径漫游存在此问题。
对应版本号1.25.0
原博主博客地址:https://blog.csdn.net/qq21497936
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/99816214