osg osgQt模块 基于 QGLWidget 源码分析

news2024/11/25 23:25:33

        osgQt基于QGLWidget实现了在Qt窗口内OSG渲染操作。Qt以其开源、跨平台、方便快捷、现代化的界面风格等优点,已经成为了目前桌面版CAD/CAE/CAM等软件开发的首选组件。因此,非常有必要在OSG的基础之上,研究Qt桌面系统内集成OSG渲染功能的相关技术。

 系统配置


一、编译、安装
在进行osgQt编译安装之前,需要完成OSG的编译安装,可以参照笔者前面的博客。完成编译安装之后,便可编译安装osgQt。

 

    1.1 下载代码
首先,需要从GitHub上下载osgQt,

git clone https://github.com/openscenegraph/osgQt.git
git checkout -b 3.5.7 3.5.7
    1.2 构建项目
打开CMake,按照下表构建项目配置,

CMAKE_INSTALL_PREFIX    D:/YouQuan/CaeFrameworks/OpenSceneGraph/INSTALL    
QT_QMAKE_EXECUTABLE    D:/YouQuan/CaeFrameworks/FreeCAD/FreeCADLibs_12.1.6_x64_VC15/bin/qmake.exe    
QT5Widgets_DIR    D:/YouQuan/CaeFrameworks/FreeCAD/FreeCADLibs_12.1.6_x64_VC15/lib/cmake/Qt5Widgets    
OPENTHREADS_INCLUDE_DIR    D:/YouQuan/CaeFrameworks/OpenSceneGraph/INSTALL/lib/include    
OPENTHREADS_LIBRARY_DEBUG    D:/YouQuan/CaeFrameworks/OpenSceneGraph/INSTALL/lib/OpenThreadsd.lib    
OPENTHREADS_LIBRARY_RELEASE    D:/YouQuan/CaeFrameworks/OpenSceneGraph/INSTALL/lib/OpenThreads.lib    
OSG_INCLUDE_DIR    D:/YouQuan/CaeFrameworks/OpenSceneGraph/INSTALL/include    
OSG_LIBRARY_DEBUG    D:/YouQuan/CaeFrameworks/OpenSceneGraph/INSTALL/lib/osgd.lib    
OSG_LIBRARY_RELEASE    D:/YouQuan/CaeFrameworks/OpenSceneGraph/INSTALL/lib/osg.lib    
OSGDB_INCLUDE_DIR    D:/YouQuan/CaeFrameworks/OpenSceneGraph/INSTALL/include    
OSGDB_LIBRARY_DEBUG    D:/YouQuan/CaeFrameworks/OpenSceneGraph/INSTALL/lib/osgDBd.lib    
OSGDB_LIBRARY_RELEASE    D:/YouQuan/CaeFrameworks/OpenSceneGraph/INSTALL/lib/osgDB.lib    
OSGGA_INCLUDE_DIR    D:/YouQuan/CaeFrameworks/OpenSceneGraph/INSTALL/include    
OSGGA_LIBRARY_DEBUG    D:/YouQuan/CaeFrameworks/OpenSceneGraph/INSTALL/lib/osgGAd.lib    
OSGGA_LIBRARY_RELEASE    D:/YouQuan/CaeFrameworks/OpenSceneGraph/INSTALL/lib/osgGA.lib    
OSGTEXT_INCLUDE_DIR    D:/YouQuan/CaeFrameworks/OpenSceneGraph/INSTALL/include    
OSGTEXT_LIBRARY_DEBUG    D:/YouQuan/CaeFrameworks/OpenSceneGraph/INSTALL/lib/osgTextd.lib    
OSGTEXT_LIBRARY_RELEASE    D:/YouQuan/CaeFrameworks/OpenSceneGraph/INSTALL/lib/osgText.lib    
OSGUTIL_INCLUDE_DIR    D:/YouQuan/CaeFrameworks/OpenSceneGraph/INSTALL/include    
OSGUTIL_LIBRARY_DEBUG    D:/YouQuan/CaeFrameworks/OpenSceneGraph/INSTALL/lib/osgUtild.lib    
OSGUTIL_LIBRARY_RELEASE    D:/YouQuan/CaeFrameworks/OpenSceneGraph/INSTALL/lib/osgUtil.lib    
OSGVIEWER_INCLUDE_DIR    D:/YouQuan/CaeFrameworks/OpenSceneGraph/INSTALL/include    
OSGVIEWER_LIBRARY_DEBUG    D:/YouQuan/CaeFrameworks/OpenSceneGraph/INSTALL/lib/osgViewerd.lib    
OSGVIEWER_LIBRARY_RELEASE    D:/YouQuan/CaeFrameworks/OpenSceneGraph/INSTALL/lib/osgViewer.lib    
OSGWIDGET_INCLUDE_DIR    D:/YouQuan/CaeFrameworks/OpenSceneGraph/INSTALL/include    
OSGWIDGET_LIBRARY_DEBUG    D:/YouQuan/CaeFrameworks/OpenSceneGraph/INSTALL/lib/osgWidgetd.lib    
OSGWIDGET_LIBRARY_RELEASE    D:/YouQuan/CaeFrameworks/OpenSceneGraph/INSTALL/lib/osgWidget.lib    


    1.3 编译安装
打开生成的osgQt.sln,构建ALL_BUILD项目完成编译;构建“INSTALL”项目完成安装,

二、代码分析
从整体上来看,osgQt实际上是仿照osgViewer::run()函数,借助于QTimer、QGLWidget完成了在Qt窗口内渲染OSG场景。

    2.1 HeartBeat
HeartBeat是个单例类,借助于QTimer,触发OSG场景渲染。与osgViewer::run()实现相似,HeartBeat::timeEvent(QTimeEvent*)也实现了渲染帧率控制的功能(相当于控制”渲染频率“的节流阀)。

void HeartBeat::timerEvent( QTimerEvent * /*event*/ )
{
    osg::ref_ptr< osgViewer::ViewerBase > viewer;
    if( !_viewer.lock( viewer ) )
    {
        // viewer has been deleted -> stop timer
        stopTimer();
        return;
    }
 
    // limit the frame rate
    if( viewer->getRunMaxFrameRate() > 0.0)
    {
        double dt = _lastFrameStartTime.time_s();
        double minFrameTime = 1.0 / viewer->getRunMaxFrameRate();
        if (dt < minFrameTime)
            OpenThreads::Thread::microSleep(static_cast<unsigned int>(1000000.0*(minFrameTime-dt)));
    }
    else
    {
        // avoid excessive CPU loading when no frame is required in ON_DEMAND mode
        if( viewer->getRunFrameScheme() == osgViewer::ViewerBase::ON_DEMAND )
        {
            double dt = _lastFrameStartTime.time_s();
            if (dt < 0.01)
                OpenThreads::Thread::microSleep(static_cast<unsigned int>(1000000.0*(0.01-dt)));
        }
 
        // record start frame time
        _lastFrameStartTime.setStartTick();
 
        // make frame
        if( viewer->getRunFrameScheme() == osgViewer::ViewerBase::ON_DEMAND )
        {
            if( viewer->checkNeedToDoFrame() )
            {
                viewer->frame();
            }
        }
        else
        {
            viewer->frame();
        }
    }
}

为了触发OSG场景渲染,首先需要调用HeartBeat::init( osgViewer::ViewerBase *viewer ),这样便可将QTimer绑定到osgViewer::ViewerBase,进而不断的触发场景的渲染。

/// Initializes the loop for viewer. Must be called from main thread.
void HeartBeat::init( osgViewer::ViewerBase *viewer )
{
    if( _viewer == viewer )
        return;
 
    stopTimer();
 
    _viewer = viewer;
 
    if( viewer )
    {
        _timerId = startTimer( 0 );
        _lastFrameStartTime.setStartTick( 0 );
    }
}

    2.2 osgQt::GraphicsWindowQt
从运行原理上来看,OSG实际上是对OpenGL状态机采用C++进行了封装,而OpenGL在底层使用图形上下文(Graphics Context)来描述状态机相关信息。使用OpenGL必须要首先指定图形上下文。osgQt::GraphicsWindowQt就是用来提供QGLWidget的图形上下文。

Ref. from OpenGL Wiki =======================================================

An OpenGL context represents many things. A context stores all of the state associated with this instance of OpenGL. It represents the (potentially visible) default framebuffer that rendering commands will draw to when not drawing to a framebuffer object. Think of a context as an object that holds all of OpenGL; when a context is destroyed, OpenGL is destroyed.

Contexts are localized within a particular process of execution (an application, more or less) on an operating system. A process can create multiple OpenGL contexts. Each context can represent a separate viewable surface, like a window in an application.

Each context has its own set of OpenGL Objects, which are independent of those from other contexts. A context's objects can be shared with other contexts. Most OpenGL objects are sharable, including Sync Objects and GLSL Objects. Container Objects are not sharable, nor are Query Objects.

Any object sharing must be made explicitly, either as the context is created or before a newly created context creates any objects. However, contexts do not have to share objects; they can remain completely separate from one another.

In order for any OpenGL commands to work, a context must be current; all OpenGL commands affect the state of whichever context is current. The current context is a thread-local variable, so a single process can have several threads, each of which has its own current context. However, a single context cannot be current in multiple threads at the same time.

=======================================================Ref. from OpenGL Wiki


    2.3 osg::GLWidget
osg::GLWidget在QWidget渲染功能基础之上,将Qt键盘、鼠标等事件转化为OSG事件,并触发相关事件函数的调用。

void GLWidget::setKeyboardModifiers( QInputEvent* event )
{
    int modkey = event->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier);
    unsigned int mask = 0;
    if ( modkey & Qt::ShiftModifier ) mask |= osgGA::GUIEventAdapter::MODKEY_SHIFT;
    if ( modkey & Qt::ControlModifier ) mask |= osgGA::GUIEventAdapter::MODKEY_CTRL;
    if ( modkey & Qt::AltModifier ) mask |= osgGA::GUIEventAdapter::MODKEY_ALT;
    _gw->getEventQueue()->getCurrentEventState()->setModKeyMask( mask );
}
 
void GLWidget::resizeEvent( QResizeEvent* event )
{
    const QSize& size = event->size();
 
    int scaled_width = static_cast<int>(size.width()*_devicePixelRatio);
    int scaled_height = static_cast<int>(size.height()*_devicePixelRatio);
    _gw->resized( x(), y(), scaled_width,  scaled_height);
    _gw->getEventQueue()->windowResize( x(), y(), scaled_width, scaled_height );
    _gw->requestRedraw();
}
 
void GLWidget::moveEvent( QMoveEvent* event )
{
    const QPoint& pos = event->pos();
    int scaled_width = static_cast<int>(width()*_devicePixelRatio);
    int scaled_height = static_cast<int>(height()*_devicePixelRatio);
    _gw->resized( pos.x(), pos.y(), scaled_width,  scaled_height );
    _gw->getEventQueue()->windowResize( pos.x(), pos.y(), scaled_width,  scaled_height );
}
 
void GLWidget::keyPressEvent( QKeyEvent* event )
{
    setKeyboardModifiers( event );
    int value = s_QtKeyboardMap.remapKey( event );
    _gw->getEventQueue()->keyPress( value );
 
    // this passes the event to the regular Qt key event processing,
    // among others, it closes popup windows on ESC and forwards the event to the parent widgets
    if( _forwardKeyEvents )
        inherited::keyPressEvent( event );
}
 
void GLWidget::keyReleaseEvent( QKeyEvent* event )
{
    if( event->isAutoRepeat() )
    {
        event->ignore();
    }
    else
    {
        setKeyboardModifiers( event );
        int value = s_QtKeyboardMap.remapKey( event );
        _gw->getEventQueue()->keyRelease( value );
    }
 
    // this passes the event to the regular Qt key event processing,
    // among others, it closes popup windows on ESC and forwards the event to the parent widgets
    if( _forwardKeyEvents )
        inherited::keyReleaseEvent( event );
}
 
void GLWidget::mousePressEvent( QMouseEvent* event )
{
    int button = 0;
    switch ( event->button() )
    {
        case Qt::LeftButton: button = 1; break;
        case Qt::MidButton: button = 2; break;
        case Qt::RightButton: button = 3; break;
        case Qt::NoButton: button = 0; break;
        default: button = 0; break;
    }
    setKeyboardModifiers( event );
    _gw->getEventQueue()->mouseButtonPress( event->x()*_devicePixelRatio, event->y()*_devicePixelRatio, button );
}
 
void GLWidget::mouseReleaseEvent( QMouseEvent* event )
{
    int button = 0;
    switch ( event->button() )
    {
        case Qt::LeftButton: button = 1; break;
        case Qt::MidButton: button = 2; break;
        case Qt::RightButton: button = 3; break;
        case Qt::NoButton: button = 0; break;
        default: button = 0; break;
    }
    setKeyboardModifiers( event );
    _gw->getEventQueue()->mouseButtonRelease( event->x()*_devicePixelRatio, event->y()*_devicePixelRatio, button );
}
 
void GLWidget::mouseDoubleClickEvent( QMouseEvent* event )
{
    int button = 0;
    switch ( event->button() )
    {
        case Qt::LeftButton: button = 1; break;
        case Qt::MidButton: button = 2; break;
        case Qt::RightButton: button = 3; break;
        case Qt::NoButton: button = 0; break;
        default: button = 0; break;
    }
    setKeyboardModifiers( event );
    _gw->getEventQueue()->mouseDoubleButtonPress( event->x()*_devicePixelRatio, event->y()*_devicePixelRatio, button );
}
 
void GLWidget::mouseMoveEvent( QMouseEvent* event )
{
    setKeyboardModifiers( event );
    _gw->getEventQueue()->mouseMotion( event->x()*_devicePixelRatio, event->y()*_devicePixelRatio );
}
 
void GLWidget::wheelEvent( QWheelEvent* event )
{
    setKeyboardModifiers( event );
    _gw->getEventQueue()->mouseScroll(
        event->orientation() == Qt::Vertical ?
            (event->delta()>0 ? osgGA::GUIEventAdapter::SCROLL_UP : osgGA::GUIEventAdapter::SCROLL_DOWN) :
            (event->delta()>0 ? osgGA::GUIEventAdapter::SCROLL_LEFT : osgGA::GUIEventAdapter::SCROLL_RIGHT) );
}


附录A:常见问题
Q1. 在Release配置下,编译osgQt可能会报以下问题,

1>------ Build started: Project: osgQt5, Configuration: Release x64 ------
1>LINK : fatal error LNK1181: cannot open input file 'optimized.lib'
1>Done building project "osgQt5.vcxproj" -- FAILED.
A1. 在osgQt项目属性中,去掉"debug.lib"、“optimized.lib"等库引用,即可顺利完成编译。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/774180.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux查看机器内存空间

执行 fdisk命令查看磁盘空间 fdisk -l更多方法参考&#xff1a; Linux检查磁盘空间

一种工业机器人绝对精度的提升方法

摘要&#xff1a;一种新的校准方法&#xff0c;使用动作捕捉作为测量工具&#xff0c;利用ELM神经网络作为非几何误差源补偿&#xff0c;提升工业机器人的绝对精度。 工业机器人的绝对精度是评估其综合性能的重要指标之一。然而&#xff0c;由于机器人受到多种因素的影响&#…

概率论的学习和整理19:条件概率我知道,但什么是条件期望?---用来解决递归问题(草稿)

目录 1 目标问题&#xff1a; 什么是条件期望&#xff1f; 条件期望有什么用&#xff1f; 2 条件期望&#xff0c;全期望公式 3 条件期望&#xff0c;全期望公式 和 条件概率&#xff0c;全概率公式的区别和联系 3.1 公式如下 3.2 区别和联系 3.3 概率和随机过程 4 有什…

8年软件测试工程师感悟 —— 写给还在迷茫中的朋友

去年还在全网声讨互联网企业996呢&#xff0c;今年突然没声音了&#xff0c;也不用讨论在哪个路灯上吊死互联网资本家了&#xff0c;因为都被裁了。 继教育培训领域大幅度裁员之后&#xff0c;大厂裁员消息也开始陆续传出&#xff0c;百度AIG,MEG多条业务线进行精简&#xff0…

线程系列 3 - 关于 CompletableFuture

线程系列3-关于 CompletableFuture 1、从 Future 接口说起2、CompletableFuture 对 Future 的改进2.1、CompletionStage 接口类2.2、runAsync 和 supplyAsync 创建子任务2.3、 whenComplete 和 exceptionally 异步任务回调钩子2.4、调用 handle() 方法统一处理异常和结果2.5、异…

重温黑盒、白盒与灰盒测试方法

黑盒、白盒和灰盒测试方法是软件测试中常用的测试策略&#xff0c;用于评估系统的功能和质量。 对于黑盒、白盒与灰盒测试方法的理解&#xff0c;几年前我在某乎做过一个概念性的回答&#xff0c;当时提问者询问&#xff1a;如何跟非技术人员解释黑盒、白盒、灰盒测试的区别&a…

UG\NX二次开发 使用exception类,异常处理的方法

文章作者:里海 来源网站:https://blog.csdn.net/WangPaiFeiXingYuan 简介 异常处理是一种编程中常见的错误处理机制,它允许程序在遇到错误或异常情况时优雅地处理。在C++中,异常类是一种重要的异常处理工具。 当程序发生错误或异常时,我们可以使用excepti…

查看maven包依赖关系,一行命令搞定。

1&#xff1a;以logback-classic包为例&#xff0c;在命令端运行&#xff0c;下面命令 mvn dependency:tree -Dincludesch.qos.logback:logback-classic 2.会出现以下日志&#xff0c;就可以清楚的知道这个jar包&#xff0c;是在谁的下面。

python 批量下载图片(协程)

要下载的图片网站 1、总共多少页&#xff0c;得到每页的 url 列表 2、每页的图片详情的 ulr 列表&#xff08;因为该高清大图在图片详情页&#xff0c;因此需要去图片详情页拿图片的url) ​​​​​​​ 3、进入图片详情页&#xff0c;获取到图片url 然后下载。 完整代码如下&…

玩转代码|详细盘点JavaScript 数据类型

目录 什么是JavaScript JavaScript 拥有动态类型 JavaScript 字符串 JavaScript 数字 JavaScript 布尔 JavaScript 数组 JavaScript 对象 Undefined 和 Null JS 中如何判断 undefined JS 中如何判断 null 声明变量类型 什么是JavaScript JavaScript&#xff08;简称…

“nacos is starting with cluster”之nacos启动报错问题

下载并解压nacos后&#xff0c;通过点击startup.cmd启动nacos&#xff0c;出现nacos is starting with cluster的错误&#xff0c;导致nacos未能启动成功。 这是因为&#xff0c;通过startup.cmd命令启动nacos&#xff0c;默认是以集群的方式进行启动的&#xff0c;我们可以改…

为3.7亿用户提供优质服务的微众银行,如何保障应用安全、及时上线

微众银行成立于2014年&#xff0c;是国内首家数字银行。作为银行业改革创新的产物&#xff0c;开业八年多来&#xff0c;微众银行积极把握数字经济时代发展新机遇&#xff0c;运用科技手段为小微企业及普罗大众提供特色化、差异化的优质金融服务&#xff0c;在以数字普惠金融服…

地下供水管漏水监测-供水管道漏水监测设备

地下供水管道作为城市供水系统的重要组成部分&#xff0c;承载着为居民和企业提供清洁饮用水的重要使命。然而&#xff0c;由于管道老化、施工质量、外力损伤等因素&#xff0c;地下供水管道泄漏问题时有发生&#xff0c;这不仅造成了宝贵的水资源浪费&#xff0c;还会导致供水…

流程编排及可视化

写在前面 这里只介绍liteflow的简单基础使用以及作者对liteflow进行可视化扩展的相关阐述 一、背景及意义 背景&#xff1a;对于拥有复杂业务逻辑的系统承载着核心业务逻辑&#xff0c;这些核心业务逻辑涉及内部逻辑运算&#xff0c;缓存操作&#xff0c;持久化操作&#xf…

LiveQing视频点播RTMP推流直播功能-点播拉转在线资源拉转转推到鉴权直播间云端录像集中录像存储

LiveQing点播拉转在线资源拉转转推到鉴权直播间云端录像集中录像存储 1、基本功能2、拉转直播2.1、点播资源拉转2.2、在线资源拉转2.3、服务器本地文件拉转 3、拉转直播如何录像&#xff1f;4、RTMP推流视频直播和点播流媒体服务 1、基本功能 LiveQing RTMP直播点播流媒体服务…

Electron运行报错: Failed to fetch extension, trying ...

Script: "electron:serve": "vue-cli-service electron:serve", 运行 npm run electron:serve 时报错&#xff1a; 解决方法&#xff1a; 检查你的electron配置文件也就是 vue.config.js 中的 mian 的文件 注释其中关于开发工具安装的部分&#xff1a;…

搭建zyplayer-doc个人WIKI文档管理工具,问题记录及简单使用

目录 项目简介各模块介绍项目部署准备工作修改配置及数据库初始化 编译部署编译后文件前后端在同一个部署包当中&#xff08;无需单独部署前端&#xff09; 环境部署目录规划启动脚本编写登录 部署问题记录错误: 找不到或无法加载主类Failed to instantiate [javax.sql.DataSou…

Linux--标记位:flag

我们知道&#xff0c;标记位赋予的值不同&#xff0c;就会生成不同的选项。那么如何给一个变量的位置赋予多个值呢&#xff1f; int整型有32个比特位&#xff0c;故我们可以通过改变位的方式改变值的大小 示例&#xff1a; #include <stdio.h> #include <unistd.h&…

Pandas数据分析库详解

概要 Pandas是一个基于 NumPy 的非常强大的开源数据处理库&#xff0c;它提供了高效、灵活和丰富的数据结构和数据分析工具&#xff0c;当涉及到数据分析和处理时&#xff0c;使得数据清洗、转换、分析和可视化变得更加简单和高效。本文中&#xff0c;我们将学习如何使用Panda…

ABeam News | 聚智同行,制胜未来,ABeam Consulting出席2023思爱普中国峰会

News 6月14日&#xff0c;2023思爱普中国峰会在北京盛大召开。本届峰会以“创新赋能高质量发展”为主题&#xff0c;现场汇聚了业界大咖、行业专家和众多客户伙伴&#xff0c;聚焦数字化加速、全球化出海、可持续发展等主题。ABeam Consulting作为SAP金牌合作伙伴及本次峰会的…