基于Qt5+osg3.4+opencascade7.0开发的三维CAD,目前软件支持主流的3D格式文件(比如igs,stop,stl,obj,3ds等),文件可以另存为obj,stl,osg等,软件整体采用Qt MDI多文档结构,支持模型放大缩小,拖动,旋转,渲染!支持视图切换,已经视觉样式切换(目前实现四种显示样式)!
最近在搞一个三维CAD软件,用到了Qt5、OpenSceneGraph和OpenCASCADE这三个重量级库。这玩意儿能同时打开十几个模型文件不卡顿,全靠Qt的MDI多文档框架撑着。新建子窗口的代码其实特简单:
QMdiSubWindow* subWindow = mdiArea->addSubWindow(new ModelViewer); subWindow->setWindowTitle("Untitled"); connect(subWindow->widget(), &ModelViewer::modelLoaded, [=](const QString& name){ subWindow->setWindowTitle(name); });这段代码最有意思的是信号槽连接,模型加载完成后自动更新窗口标题。不过实际开发时被OSG的线程同步坑惨了——当多个子窗口同时加载大模型时,得用QMutex锁住OSG的渲染操作,不然分分钟崩溃给你看。
模型操作是三维软件的核心,这里用OSG的事件处理器实现得挺巧妙。旋转功能的核心代码长这样:
void RotateHandler::handleMovement(osgGA::GUIEventAdapter& ea) { if(_dragging) { float dx = ea.getXnormalized() - _lastX; float dy = ea.getYnormalized() - _lastY; _camera->rotate(-dx*2.0, dy*2.0, 0.0); // 这个系数2.0调了三天才找到手感最佳的数值... } }为了让操作更顺滑,给旋转加了惯性效果。用osg::AnimationPathCallback实现的缓动动画,结果测试时被吐槽像在玩旋转寿司游戏,最后改成了线性插值才正常。
基于Qt5+osg3.4+opencascade7.0开发的三维CAD,目前软件支持主流的3D格式文件(比如igs,stop,stl,obj,3ds等),文件可以另存为obj,stl,osg等,软件整体采用Qt MDI多文档结构,支持模型放大缩小,拖动,旋转,渲染!支持视图切换,已经视觉样式切换(目前实现四种显示样式)!
说到文件格式支持,OpenCASCADE处理STEP文件时有个坑点:必须显式初始化字体库,否则中文注释全变方块。正确姿势是加载前执行:
Handle(Resource_Manager) res = new Resource_Manager(""); res->SetValue("font.name", "SimSun"); res->SetValue("font.file", "/usr/share/fonts/windows/");渲染样式切换倒是简单粗暴,直接改OSG的状态集。比如线框模式就是:
osg::PolygonMode* pm = new osg::PolygonMode; pm->setMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE); _stateset->setAttributeAndModes(pm, osg::StateAttribute::OVERRIDE);不过实现半透明效果时栽了个跟头——必须按深度排序绘制,否则透明物体渲染顺序错乱。最后在场景根节点加了osg::Depth节点才解决,这招是从老外的论坛里扒出来的偏方。
导出功能最头疼的是STL文件生成,用OpenCASCADE的API导出的二进制STL总有几个面方向错误。后来换成手动遍历三角面片,边导出边检查法线方向才搞定。代码里现在还有这么一段:
TopExp_Explorer exp(shape, TopAbs_FACE); while (exp.More()) { TopoDS_Face face = TopoDS::Face(exp.Current()); // 手动计算法线方向的代码块 if (normal.Z() < 0) normal.Reverse(); // YY出来的修复方案 // ...写入STL }现在软件能流畅处理10万+三角面片的模型,不过遇到复杂的NURBS曲面还是有点吃力。下一步打算上LevelOfDetail优化,但OSG的LOD节点和Qt的视图缩放事件还没打通,估计又得掉不少头发。