Módulo osg osgQt basado en el análisis del código fuente QGLWidget

        osgQt implementa la operación de representación OSG en la ventana Qt basada en QGLWidget. Con sus ventajas de código abierto, multiplataforma, conveniencia y estilo de interfaz moderno, Qt se ha convertido en el componente preferido para el desarrollo de software como CAD/CAE/CAM de escritorio. Por lo tanto, es muy necesario estudiar las tecnologías relacionadas con la integración de la función de representación OSG en el sistema de escritorio Qt sobre la base de OSG.

 Configuración del sistema


1. Compilación e instalación
Antes de compilar e instalar osgQt, debe completar la compilación e instalación de OSG. Puede consultar el blog anterior del autor. Después de compilar e instalar, puede compilar e instalar osgQt.

 

    1.1 Descargar código
Primero, debe descargar osgQt de GitHub,

git clone https://github.com/openscenegraph/osgQt.git
git checkout -b 3.5.7 3.5.7
    1.2 Cree el proyecto
Abra CMake, cree la configuración del proyecto de acuerdo con la siguiente tabla,

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 Compilar e instalar
Abra el osgQt.sln generado, cree el proyecto ALL_BUILD para completar la compilación, cree el proyecto "INSTALL" para completar la instalación,

2. Análisis de código
En general, osgQt se basa en la función osgViewer::run(), con la ayuda de QTimer y QGLWidget para completar la representación de la escena OSG en la ventana de Qt.

    2.1 HeartBeat
HeartBeat es una clase singleton que activa la representación de escenas OSG con la ayuda de QTimer. De forma similar a la implementación de osgViewer::run(), HeartBeat::timeEvent(QTimeEvent*) también implementa la función de control de velocidad de fotogramas de representación (equivalente a controlar el acelerador de la "frecuencia de representación").

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();
        }
    }
}

Para activar la representación de la escena OSG, primero debe llamar a HeartBeat::init( osgViewer::ViewerBase *viewer ), de modo que pueda vincular el QTimer a osgViewer::ViewerBase y luego activar continuamente la representación de la escena.

/// 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
Desde la perspectiva del principio operativo, OSG realmente encapsula la máquina de estado de OpenGL en C++, y OpenGL usa el contexto de gráficos (contexto de gráficos) en la capa inferior para describir la información relacionada con la máquina de estado. Para usar OpenGL, primero debe especificar un contexto de gráficos. osgQt::GraphicsWindowQt se utiliza para proporcionar el contexto gráfico de 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
Basado en la función de representación de QWidget, osg::GLWidget convierte el teclado, el mouse y otros eventos Qt en eventos OSG y activa la llamada de funciones de eventos relacionadas.

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) );
}


Apéndice A: Preguntas frecuentes
P 1. Bajo la configuración de la versión, la compilación de osgQt puede generar las siguientes preguntas:

1>------ Compilación iniciada: Proyecto: osgQt5, Configuración: Versión x64 ------
1>ENLACE: error fatal LNK1181: no se puede abrir el archivo de entrada 'optimized.lib'
1>Terminó el proyecto de construcción "osgQt5. vcxproj" -- FALLIDO.
A1. En las propiedades del proyecto osgQt, elimine las referencias de la biblioteca como "debug.lib" y "optimized.lib", y la compilación se puede completar sin problemas.

Guess you like

Origin blog.csdn.net/vcit102/article/details/131803381