OSG开发笔记(三十):OSG加载动力学仿真K模型文件以及测试Demo

前言

Osg需要打开模型文件,但是遇到显示动力学仿真的K模型文件,.k文件是一种描述材料属性的文件,比如密度、弹性模量等,该模型文件不是常规中间开放格式,无法直接支持,需要自定义解析并且重建三维模型。

Demo

实际非常流程,因为视频转gif导致部分看起来不行:


  
  

交互流畅性测试

实际研发需要用不同的策略进行在不影响视觉观感的前提下,进行模型展示优化处理,本次测试直接copy多份模型加载:

  • 172万多个四边形,丝滑

  • 344万多个四边形,丝滑

  • 518万多个四边形,开始稍微有点卡顿了

  • 691万多个四边形,开始更不利索了

    此时翻倍就在加载场景崩溃,这个崩溃确认代码没问题,是运行内存较大了,加大运行时内存配置:

cpp 复制代码
QMAKE_LFLAGS_WINDOWS += /LARGEADDRESSAWARE

运行成功,交互更卡一点。


  更大又不行了:

注意

分析的是本文件解析,可能包含数据格式关键字不全。


  实际遇到的为:*TITLE,*PART,*ELEMENT_SHELL,*NODE, *END。

(PS:以后项目上遇到了再另起篇章补充。)

K文件

概览

.k文件是一种描述材料属性的文件,比如密度、弹性模量等。

打开其的软件LS-DYNA和LSPREPOST

  • LS-DYNA
      LS-DYNA程序(最新版本17.2版)是功能齐全的几何非线性(大位移、大转动和大应变)、材料非线性(140多种材料动态模型)和接触非线性(50多种)程序。它以Lagrange算法为主,兼有ALE和Euler算法;以显式求解为主,兼有隐式求解功能;以结构分析为主,兼有热分析、流体-结构耦合功能;以非线性动力分析为主,兼有静力分析功能(如动力分析前的预应力计算和薄板冲压成型后的回弹计算);军用和民用相结合的通用结构分析非线性有限元程序,是显式动力学程序的鼻祖和先驱。
  • LSPREPOST
      lsprepost是一套专为LS-DYNA开发的高级有限元前后处理软件,上线时间是1976年1月1日。
      文件内容概览
      文件整体概览如下:
      
      以上所知,分为六个关键字,分割为五个区域,归属关系如下:

关键字:*KEYWORK

关键字:*TITLE

关键字:*PART

PART关键字来定义部件及其相关特性


  第一行为title属性,为标题,没有实际意义。

关键字:*ELEMENT_SHELL

SECTION系列关键字指定所采用的单元算法、积分分规则及各种几何参数(壳单元的厚度、梁单元的截面信息参数等),对不同的单元类型,或者需要相应的 SECTION关键字段来定义单元的算法和参数。

*SECTION_SEHLL定义单壳体单元的特性。这些属性对于模拟如汽车碰撞、航空航天结构、以及任何涉及薄壳结构动态响应的模拟都至关重要。然而,具体到ELEMENT_SHELL属性在K文件中的详细内容和格式,可能因LS-DYNA的版本和具体应用而有所不同。


  

关键字:*NODE

*NODE关键字来定义结构模型的每一节点及其在总体直角坐标系中的坐标及受约束情况,该关键字段包含如下的变量信息:


  然后部件与node的对应关系,通过索引对应:


  解析文件即可。

关键字:*END

表示文件结束。

测试加载K文件


  

Demo关键源码

模型结构体

cpp 复制代码
// 模型结构体
    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; }; 

绘制部分

cpp 复制代码
osg::ref_ptr<osg::Group> pGroup = new osg::Group; // 绘图 { for(int partIndex = 0; partIndex < kMode.listPart.size(); partIndex++) { // 创建一个用户保存几何信息的对象 osg::ref_ptr<osg::Geometry> pGeometry = new osg::Geometry; // 创建四个顶点的数组 osg::ref_ptr<osg::Vec3Array> pVec3Array = new osg::Vec3Array; // 添加四个顶点 pGeometry->setVertexArray(pVec3Array.get()); // 创建四种颜色的数据 osg::ref_ptr<osg::Vec4Array> pVec4Array = new osg::Vec4Array; // 添加四种颜色 pGeometry->setColorArray(pVec4Array.get()); // 绑定颜色 pGeometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX); double r, g, b; r = qrand() % 100 * 1.0f / 100; g = qrand() % 100 * 1.0f / 100; b = qrand() % 100 * 1.0f / 100; for(int elementShellIndex = 0; elementShellIndex < kMode.listPart.at(partIndex).listElementShell.size(); elementShellIndex++) { // x y z #if 0 pVec3Array->push_back(osg::Vec3( 1.0, 0.0, 0.0)); pVec3Array->push_back(osg::Vec3( 3.0, 0.0, 0.0)); pVec3Array->push_back(osg::Vec3( 3.0, 0.0, 1.0)); pVec3Array->push_back(osg::Vec3( 1.0, 0.0, 1.0)); #endif pVec3Array->push_back(osg::Vec3(kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n1).x, kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n1).y, kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n1).z)); pVec3Array->push_back(osg::Vec3(kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n2).x, kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n2).y, kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n2).z)); pVec3Array->push_back(osg::Vec3(kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n3).x, kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n3).y, kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n3).z)); pVec3Array->push_back(osg::Vec3(kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n4).x, kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n4).y, kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n4).z)); // r g b a(a设置无效,估计需要其他属性配合) pVec4Array->push_back(osg::Vec4(r, g, b, 1.0)); pVec4Array->push_back(osg::Vec4(r, g, b, 1.0)); pVec4Array->push_back(osg::Vec4(r, g, b, 1.0)); pVec4Array->push_back(osg::Vec4(r, g, b, 1.0)); } // 注意:此处若不绑定画笔,则表示使用之前绑定的画笔 // 为唯一的法线创建一个数组 法线: normal osg::ref_ptr<osg::Vec3Array> pVec3ArrayNormal = new osg::Vec3Array; pGeometry->setNormalArray(pVec3ArrayNormal.get()); pGeometry->setNormalBinding(osg::Geometry::BIND_OVERALL); pVec3ArrayNormal->push_back(osg::Vec3(0.0, -1.0, 0.0)); // 由保存的数据绘制四个顶点的多边形 pGeometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, kMode.listPart.at(partIndex).listElementShell.size() * 4)); // pGeometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 4)); // 向Geode类添加几何体(Drawable) osg::ref_ptr<osg::Geode> pGeode = new osg::Geode; pGeode->addDrawable(pGeometry.get()); { osg::ref_ptr<osg::StateSet> pStateSet = pGeometry->getOrCreateStateSet(); osg::ref_ptr<osg::PolygonMode> pPolygonMode = new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE); pStateSet->setAttribute(pPolygonMode); } pGroup->addChild(pGeode); } } 

工程模板:对应版本号v1.32.0

入坑

入坑一:绑定颜色不出来

问题

去掉纹理后颜色不出来,显示一直灰色。

原理

开启光照,就颜色不出来,需要关闭

解决

cpp 复制代码
// 始终是灰色,这里需要设置关闭光照:OFF
osg::StateSet *pStateSet = pGroup->getOrCreateStateSet(); pStateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF); //pStateSet->setMode(GL_LIGHTING, osg::StateAttribute::ON); 


  
  

入坑二:运行起来就崩溃

问题

数据都加载绘制时,运行就崩溃

解决过程

  • 步骤一:先减少面数量,发现小于一档数量可以没问题,初步怀疑绘制太多,但是osg本身做深度测试相关很不错,除非时内存爆了,所以应然需要继续。
  • 步骤二:使用绘制四边形的,绘制10000个也会崩,加重了内存怀疑。
  • 步骤三:由于之前对于顶点颜色没有进行全部点绑定,其他的看纹理的方式都是重复的,所以这块就直接只设置了4个点,但本次去掉颜色之后,发现是可以的。
  • 步骤四:将颜色给他赋值上,一个部件一个纯色,把对应绑定点都补上,发现没问题

原因

就是因为绑定颜色角点,只绑定了四个,按道理来说就算其越界也只是读取的乱值,颜色是乱的,但是点多了就不行了,所以这里还是需要一一对应,不偷懒。

解决

颜色单独,每一个部件一个颜色,然后循环内一一对应: