项目实战:Qt+OSG爆破动力学仿真三维引擎测试工具v1.1.0(加载.K模型,子弹轨迹模拟动画,支持windows、linux、国产麒麟系统)
需求
1. 使用 osg 三维引擎进行动力学模型仿真性能测试;
2. 打开动力学仿真模型文件,.k 后缀的模型文件,测试加载解析过程;
3. 解决第三方 company 的 opengl 制作的三维引擎,绘制面较多与弹丸路径模拟较卡顿的问题;
4. 测试时,使用的模型为公开模型,基础面数量达到 160 多万个;
5. 测试时,模拟动画使用的时 100 万条弹丸路径平行飞射出去;
1. 新增打开双模型,第一个模型在原来的位置,第二个模型在偏移后的位置
2. 优化打开关闭重新打开模型的过程
3. 对于 100 万线动画射击,用于测试性能
4. 当前模型为 160 万个面,双模型的时候为 320 多万个元素基础面
测试交互流畅性,交互无延迟!!!
#ifndef OSGWIDGET_H
#define OSGWIDGET_H
#include <QWidget>
#include "OsgViewerWidget.h"
#include "MyManipulator.h"
#include "osg/PolygonMode"
class AnimationPathCameraMainpulator;
namespace Ui {
class OsgWidget;
}
class OsgWidget : public QWidget
{
Q_OBJECT
public:
// 模型结构体
struct Element_Shell // ELEMENT_SHELL
{
Element_Shell() {
}
qint64 eid; // 单元 id
qint64 pid; // 材料 id
qint64 n1; // 节点 1,定义几何形状
qint64 n2; // 节点 2,定义几何形状
qint64 n3; // 节点 3,定义几何形状
qint64 n4; // 节点 4,定义几何形状
qint64 n5; // 厚度,额外的节点在标准的 LS-DYNA 四边形壳单元定义中是没有意义的。
qint64 n6; // 积分点数,额外的节点在标准的 LS-DYNA 四边形壳单元定义中是没有意义的。
qint64 n7; // 额外的节点在标准的 LS-DYNA 四边形壳单元定义中是没有意义的。
qint64 n8; // 额外的节点在标准的 LS-DYNA 四边形壳单元定义中是没有意义的。
};
struct Part // PART
{
Part() {
}
qint64 pid; // 部件的 id 号,唯一
qint64 secid; // 有section 关键字定义的 section 的 id 号
QList<Element_Shell> listElementShell; // 部件片元
qint64 mid; // 部件的材料号
qint64 eosid; // 部件所属材料涉及的状态方程号,由EOS 关键字定义
qint64 hgid; // 沙漏或体积粘性参数编号,由 *HOURGLASS 关键字定义,取 0 表示将采用默认的数值:
qint64 grav; // 仅对实体单元有效,取 0 表示对所有 PART 进行重力初始化,取 1 表示仅对当前材料初始化
qint64 adpopt; // 标识该部件是否采用自适应网格划分,取 0 表示不采用
qint64 tmid; // 标识该部件是否采用自适应网格划分,取 0 表示不采用
};
struct Node {
Node() {
}
qint64 nid; // 结点号,唯一
double x; // 三维 x 坐标(全局)
double y; // 三维 y 坐标(全局)
double z; // 三维 z 坐标(全局)
int tc; // 平动自由度受约束状态,枚举值:0- 无平动约束,1-X 方向平动约束,2-Y 方向平动约束
int rc; // 转动自由度收约束状态,枚举值:0- 无转动约束,1-X 方向转动约束,2-Y 方向转动约束
};
struct K_Mode
{
K_Mode(){}
QList<Part> listPart;
QList<Node> listNode;
QHash<int, Node> hashNid2Node;
};
<span class="hljs-comment">// 添加模型</span>
K_Mode kMode;
public:
explicit OsgWidget(QWidget *parent = 0);
~OsgWidget();
public:
bool getFixXAxis() const; // 获取 X 轴固定状态
bool getFixYAxis() const; // 获取 Y 轴固定状态
bool getFixZAxis() const; // 获取 Z 轴固定状态
void getCenter(double &x, double &y, double &z);
// 获取引擎中心点坐标
void getPersonPoint(double &x, double &y, double &z);
// 获取初始化人眼的角度(看向引擎中心点)
public:
void setFixXAxis(bool fixXAxis); // 设置固定 X 轴
void setFixYAxis(bool fixYAxis); // 设置固定 Y 轴
void setFixZAxis(bool fixZAxis); // 设置固定 Z 轴
void setCenter(double x, double y, double z);
// 设置引擎中心点坐标
void setPersonPoint(double x, double y, double z);
void setEnablePolygonMode(bool enable);
<span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">startAnimation</span><span class="hljs-params">()</span></span>;
<span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">pauseAnimation</span><span class="hljs-params">()</span></span>;
<span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">stopAnimation</span><span class="hljs-params">()</span></span>;
public:
bool loadKFile(QString filePath);
bool loadK2File(QString filePath, int num, int x, int y, int z);
<span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">clear</span><span class="hljs-params">()</span></span>;
<span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">resetCoordinate</span><span class="hljs-params">()</span></span>;
protected:
void initOsg(); // osg 初始化
void loadNode(osg::ref_ptr<osg::Node> pNode);
// 加载场景根节点
protected:
osg::ref_ptr<osg::Node> createScene(); // 创建总场景
osg::ref_ptr<osg::Node> createAnimation(); // 创建动画
protected:
void resizeEvent(QResizeEvent event);
void keyPressEvent(QKeyEvent event);
void keyReleaseEvent(QKeyEvent* event);
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
void mouseDoubleClickEvent(QMouseEvent* event);
void mouseMoveEvent(QMouseEvent* event);
void wheelEvent(QWheelEvent* event);
void timerEvent(QTimerEvent *event);
private:
Ui::OsgWidget *ui;
private:
OsgViewerWidget *_pViewer; // osg 场景嵌入 Qt 核心类
osg::ref_ptr<osg::MatrixTransform> _pRoot; // osg 场景根节点
private:
float _xDistance; // x 轴单个 tick 间距
int _xTickNumber; // x 轴 tick 数 (例如:5 的时候,是 6 个,0~5)
<span class="hljs-type">float</span> _yDistance; <span class="hljs-comment">// y轴单个tick间距</span>
<span class="hljs-type">int</span> _yTickNumber; <span class="hljs-comment">// y轴tick数(例如:5的时候,是6个,0~5)</span>
<span class="hljs-type">float</span> _zDistance; <span class="hljs-comment">// z轴单个tick间距</span>
<span class="hljs-type">int</span> _zTickNumber; <span class="hljs-comment">// z轴tick数(例如:5的时候,是6个,0~5)</span>
QString _zUnit; <span class="hljs-comment">// z轴单位</span>
<span class="hljs-type">float</span> _zTickLabelOffset; <span class="hljs-comment">// z轴坐标偏移</span>
QString _yUnit; <span class="hljs-comment">// y轴单位</span>
<span class="hljs-type">float</span> _zTickUnitLabelOffset; <span class="hljs-comment">// z轴坐标偏移</span>
QColor _gridColor; <span class="hljs-comment">// 轴颜色</span>
QColor _labelColor; <span class="hljs-comment">// 轴tickLabel的颜色</span>
osg::ref_ptr<osg::Node> _pNode; <span class="hljs-comment">// 模型</span>
osg::ref_ptr<osg::Node> _pNode2; <span class="hljs-comment">// 子弹</span>
osg::ref_ptr<MyManipulator> _pManipulator; <span class="hljs-comment">// 自定义漫游器</span>
osg::Vec3d _eyeVect3D; <span class="hljs-comment">// 原始坐标,用于复位原始视角</span>
osg::Vec3d _centerVect3D; <span class="hljs-comment">// 原始坐标,用于复位原始视角</span>
osg::Vec3d _upVect3D; <span class="hljs-comment">// 原始坐标,用于复位原始视角</span>
K_Mode _kMode;
<span class="hljs-type">int</span> _timerId;
osg::ref_ptr<osg::StateSet> _pStateSet;
osg::ref_ptr<osg::PolygonMode> _pPolygonMode;
osg::ref_ptr<osg::Vec3Array> _pVec3Array; <span class="hljs-comment">// 炮弹</span>
<span class="hljs-type">bool</span> _animationPausing;
};
#endif // OSGWIDGET_H
bool OsgWidget::loadK2File(QString filePath, int num, int x, int y, int z)
{
if(!QFile::exists(filePath))
{
LOG << "Not exist file:" << filePath;
QMessageBox::information(0, "错误", QString("Not exist file: %1").arg(filePath));
return false;
}
QFile file(filePath);
if(!file.open(QIODevice::ReadOnly))
{
LOG << "Failed to open file:" << filePath;
QMessageBox::information(0, "错误", QString("Failed to open file: %1").arg(filePath));
return false;
}
kMode = <span class="hljs-built_in">K_Mode</span>();
<span class="hljs-function">QTextStream <span class="hljs-title">textStream</span><span class="hljs-params">(&file)</span></span>;
QString context;
qint64 rowIndex = <span class="hljs-number">-1</span>;
context = textStream.<span class="hljs-built_in">readLine</span>();
rowIndex++;
LOG;
...
file.<span class="hljs-built_in">close</span>();
LOG;
osg::ref_ptr<osg::Group> pGroup = <span class="hljs-keyword">new</span> osg::Group;
<span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> index = <span class="hljs-number">0</span>; index < num; index++)
{
LOG << index;
<span class="hljs-comment">// 绘图</span>
{
<span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> partIndex = <span class="hljs-number">0</span>; partIndex < kMode.listPart.<span class="hljs-built_in">size</span>(); partIndex++)
{
<span class="hljs-comment">// 创建一个用户保存几何信息的对象</span>
osg::ref_ptr<osg::Geometry> pGeometry = <span class="hljs-keyword">new</span> osg::Geometry;
<span class="hljs-comment">// 创建四个顶点的数组</span>
osg::ref_ptr<osg::Vec3Array> pVec3Array = <span class="hljs-keyword">new</span> osg::Vec3Array;
<span class="hljs-comment">// 添加四个顶点</span>
pGeometry-><span class="hljs-built_in">setVertexArray</span>(pVec3Array.<span class="hljs-built_in">get</span>());
<span class="hljs-comment">// 创建四种颜色的数据</span>
osg::ref_ptr<osg::Vec4Array> pVec4Array = <span class="hljs-keyword">new</span> osg::Vec4Array;
<span class="hljs-comment">// 添加四种颜色</span>
pGeometry-><span class="hljs-built_in">setColorArray</span>(pVec4Array.<span class="hljs-built_in">get</span>());
<span class="hljs-comment">// 绑定颜色</span>
pGeometry-><span class="hljs-built_in">setColorBinding</span>(osg::Geometry::BIND_PER_VERTEX);
<span class="hljs-type">double</span> r, g, b;
r = <span class="hljs-built_in">qrand</span>() % <span class="hljs-number">100</span> * <span class="hljs-number">1.0f</span> / <span class="hljs-number">100</span>;
g = <span class="hljs-built_in">qrand</span>() % <span class="hljs-number">100</span> * <span class="hljs-number">1.0f</span> / <span class="hljs-number">100</span>;
b = <span class="hljs-built_in">qrand</span>() % <span class="hljs-number">100</span> * <span class="hljs-number">1.0f</span> / <span class="hljs-number">100</span>;
<span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> elementShellIndex = <span class="hljs-number">0</span>; elementShellIndex < kMode.listPart.<span class="hljs-built_in">at</span>(partIndex).listElementShell.<span class="hljs-built_in">size</span>(); elementShellIndex++)
{
<span class="hljs-comment">// x y z</span>
pVec3Array-><span class="hljs-built_in">push_back</span>(osg::<span class="hljs-built_in">Vec3</span>(kMode.hashNid2Node.<span class="hljs-built_in">value</span>(kMode.listPart.<span class="hljs-built_in">at</span>(partIndex).listElementShell.<span class="hljs-built_in">at</span>(elementShellIndex).n1).x + index * x,
kMode.hashNid2Node.<span class="hljs-built_in">value</span>(kMode.listPart.<span class="hljs-built_in">at</span>(partIndex).listElementShell.<span class="hljs-built_in">at</span>(elementShellIndex).n1).y + index * y,
kMode.hashNid2Node.<span class="hljs-built_in">value</span>(kMode.listPart.<span class="hljs-built_in">at</span>(partIndex).listElementShell.<span class="hljs-built_in">at</span>(elementShellIndex).n1).z + index * z));
pVec3Array-><span class="hljs-built_in">push_back</span>(osg::<span class="hljs-built_in">Vec3</span>(kMode.hashNid2Node.<span class="hljs-built_in">value</span>(kMode.listPart.<span class="hljs-built_in">at</span>(partIndex).listElementShell.<span class="hljs-built_in">at</span>(elementShellIndex).n2).x + index * x,
kMode.hashNid2Node.<span class="hljs-built_in">value</span>(kMode.listPart.<span class="hljs-built_in">at</span>(partIndex).listElementShell.<span class="hljs-built_in">at</span>(elementShellIndex).n2).y + index * y,
kMode.hashNid2Node.<span class="hljs-built_in">value</span>(kMode.listPart.<span class="hljs-built_in">at</span>(partIndex).listElementShell.<span class="hljs-built_in">at</span>(elementShellIndex).n2).z + index * z));
pVec3Array-><span class="hljs-built_in">push_back</span>(osg::<span class="hljs-built_in">Vec3</span>(kMode.hashNid2Node.<span class="hljs-built_in">value</span>(kMode.listPart.<span class="hljs-built_in">at</span>(partIndex).listElementShell.<span class="hljs-built_in">at</span>(elementShellIndex).n3).x + index * x,
kMode.hashNid2Node.<span class="hljs-built_in">value</span>(kMode.listPart.<span class="hljs-built_in">at</span>(partIndex).listElementShell.<span class="hljs-built_in">at</span>(elementShellIndex).n3).y + index * y,
kMode.hashNid2Node.<span class="hljs-built_in">value</span>(kMode.listPart.<span class="hljs-built_in">at</span>(partIndex).listElementShell.<span class="hljs-built_in">at</span>(elementShellIndex).n3).z + index * z));
pVec3Array-><span class="hljs-built_in">push_back</span>(osg::<span class="hljs-built_in">Vec3</span>(kMode.hashNid2Node.<span class="hljs-built_in">value</span>(kMode.listPart.<span class="hljs-built_in">at</span>(partIndex).listElementShell.<span class="hljs-built_in">at</span>(elementShellIndex).n4).x + index * x,
kMode.hashNid2Node.<span class="hljs-built_in">value</span>(kMode.listPart.<span class="hljs-built_in">at</span>(partIndex).listElementShell.<span class="hljs-built_in">at</span>(elementShellIndex).n4).y + index * y,
kMode.hashNid2Node.<span class="hljs-built_in">value</span>(kMode.listPart.<span class="hljs-built_in">at</span>(partIndex).listElementShell.<span class="hljs-built_in">at</span>(elementShellIndex).n4).z + index * z));
<span class="hljs-comment">// r g b a(a设置无效,估计需要其他属性配合)</span>
pVec4Array-><span class="hljs-built_in">push_back</span>(osg::<span class="hljs-built_in">Vec4</span>(r, g, b, <span class="hljs-number">1.0</span>));
pVec4Array-><span class="hljs-built_in">push_back</span>(osg::<span class="hljs-built_in">Vec4</span>(r, g, b, <span class="hljs-number">1.0</span>));
pVec4Array-><span class="hljs-built_in">push_back</span>(osg::<span class="hljs-built_in">Vec4</span>(r, g, b, <span class="hljs-number">1.0</span>));
pVec4Array-><span class="hljs-built_in">push_back</span>(osg::<span class="hljs-built_in">Vec4</span>(r, g, b, <span class="hljs-number">1.0</span>));
}
<span class="hljs-comment">// 注意:此处若不绑定画笔,则表示使用之前绑定的画笔</span>
<span class="hljs-comment">// 为唯一的法线创建一个数组 法线: normal</span>
osg::ref_ptr<osg::Vec3Array> pVec3ArrayNormal = <span class="hljs-keyword">new</span> osg::Vec3Array;
pGeometry-><span class="hljs-built_in">setNormalArray</span>(pVec3ArrayNormal.<span class="hljs-built_in">get</span>());
pGeometry-><span class="hljs-built_in">setNormalBinding</span>(osg::Geometry::BIND_OVERALL);
pVec3ArrayNormal-><span class="hljs-built_in">push_back</span>(osg::<span class="hljs-built_in">Vec3</span>(<span class="hljs-number">0.0</span>, <span class="hljs-number">-1.0</span>, <span class="hljs-number">0.0</span>));
<span class="hljs-comment">// 由保存的数据绘制四个顶点的多边形</span>
pGeometry-><span class="hljs-built_in">addPrimitiveSet</span>(<span class="hljs-keyword">new</span> osg::<span class="hljs-built_in">DrawArrays</span>(osg::PrimitiveSet::QUADS, <span class="hljs-number">0</span>, kMode.listPart.<span class="hljs-built_in">at</span>(partIndex).listElementShell.<span class="hljs-built_in">size</span>() * <span class="hljs-number">4</span>));
<span class="hljs-comment">// pGeometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 4));</span>
<span class="hljs-comment">// 向Geode类添加几何体(Drawable)</span>
osg::ref_ptr<osg::Geode> pGeode = <span class="hljs-keyword">new</span> osg::Geode;
pGeode-><span class="hljs-built_in">addDrawable</span>(pGeometry.<span class="hljs-built_in">get</span>());
#if 0
{
_pStateSet = pGeometry->getOrCreateStateSet();
// _pPolygonMode = new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE);
_pPolygonMode = new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::FILL);
_pStateSet->setAttribute(_pPolygonMode, osg::StateAttribute::ON);
}
#endif
pGroup->addChild(pGeode.get());
}
}
}
// 始终是灰色,这里需要设置关闭光照:OFF,同时旋转都能看到了(光照关闭,法向量不起作用)
{
osg::StateSet *pStateSet = pGroup->getOrCreateStateSet();
// pStateSet->setMode(GL_LIGHTING, osg::StateAttribute::ON);
pStateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
}
_pNode = pGroup.<span class="hljs-built_in">get</span>();
<span class="hljs-keyword">if</span>(_pNode.<span class="hljs-built_in">get</span>() == <span class="hljs-number">0</span>)
{
<span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
}
_pRoot-><span class="hljs-built_in">addChild</span>(_pNode);
<span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
}