OSG嵌入Qt解决方案一

OSG与Qt结合,国内网站上的资料非常少,最有助于理解OSG的资料我可以推荐一下:王锐老师的《最长的一帧》

要想驾驭一个SDK,首先就得了解其工作原理。王老师那本电子书里面就介绍了一帧的画面,OSG所做的全部内容,几乎可以这么说:你如果弄明白了一帧中OSG所做的东西,你就几乎掌握了OSG的全部,因为OSG本来就是个图形渲染引擎,学好OSG,只需要搞清楚它一帧做的事情就足够了。

这里我来记录一个嵌入Qt的OSG渲染窗口是怎么做到的,首先你要相信,这很简单,因为看完这篇文章,你一定可以模仿或者做出更好的(直接拷贝所有代码进你的main.cpp也行~)。没错,这里我用到的例子就是OSG的example里面的。

此处需要申明的是,我的Qt的版本是4.6.2,如果是其他版本的话,这个程序很明显不一定能成功运行~

下面是需要包含的头文件:(有点乱,有几个好像是不需要的,因为写重复了)

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using Qt::WindowFlags;
#include

=================================================================

=================================================================

头文件包括完毕这后,我们开始思考,到底去继承什么样的Qt窗口来容纳OSG呢?首先,学过windowsSDK编程的人都知道,MFC是一个死的,会windowsSDK编程的都不会在做东西的时候首先考虑MFC。然后这里我要说的是Qt实际上就仅仅是一个漂亮点的MFC,或许它的信号机制在反应速度上不如MFC,但是它从外观和安全和可移植性都比MFC好。而且,我要说明的是,Qt看起来仅仅是MFC里面的基于对话框的编程,乍一看是挺恶心的,那么死板的对话框,所有的创意都不可能实现了。(但是那只是乍一看~~);

对程序员帮助最大的东西莫过于一个帮助手册,阅读Qt的帮助手册,发现,其实这种开源的东西是需要靠自己去继承它并发展新的东西的。扯远了~~

我们的目的是在Qt的窗口上嵌套OSG,最简单的方法其实就是用多进程,以一种外部控制的方式使两个窗口看起来像是父子关系,但是不是真正意义上的父子关系。于是这种方案抛弃了。

接着分析,要潜入一个Qt类型的主窗口,就必须是Qt类型的窗口。于是就需要从众多Q系列的窗口里需要找到一个支撑OSG画面的窗口类型。OSG是基于OpenGL的渲染引擎吧~而恰好,Qt也有对OpenGL窗口支持的Q类型窗口,这个窗口类型的名字就是QGLWidget,于是乎,基于这个类派生自己的OSG窗口似乎是个不错的想法(嗯~确实是个不错的想法)。

QGLWidget是多继承了QWidget(使这种窗口具有了Q系列的血统)和QGL(又让此窗口有了三维视觉效果),是个不错的想法。于是,下面就是最原始的嵌入式OSG的窗口类别~,以及实现方法。

class AdapterWidget : public QGLWidget
{
public:

 AdapterWidget( QWidget * parent = 0, const char * name = 0, const QGLWidget * shareWidget = 0, WindowFlags f = 0 );

        virtual ~AdapterWidget()
  {
  }

        osgViewer::GraphicsWindow* getGraphicsWindow() { return _gw.get(); }
        const osgViewer::GraphicsWindow* getGraphicsWindow() const { return _gw.get(); }

    protected:

        void init();

        virtual void resizeGL( int width, int height );
        virtual void keyPressEvent( QKeyEvent* event );
        virtual void keyReleaseEvent( QKeyEvent* event );
        virtual void mousePressEvent( QMouseEvent* event );
        virtual void mouseReleaseEvent( QMouseEvent* event );
        virtual void mouseMoveEvent( QMouseEvent* event );

        osg::ref_ptr _gw;
};
AdapterWidget::AdapterWidget( QWidget * parent, const char * name, const QGLWidget * shareWidget, WindowFlags f):
    QGLWidget(parent, shareWidget, f)
{
    _gw = new osgViewer::GraphicsWindowEmbedded(0,0,width(),height());
    setFocusPolicy(Qt::ClickFocus);

}
void AdapterWidget::resizeGL( int width, int height )
{
    _gw->getEventQueue()->windowResize(0, 0, width, height );
    _gw->resized(0,0,width,height);
}

void AdapterWidget::keyPressEvent( QKeyEvent* event )
{
    _gw->getEventQueue()->keyPress( (osgGA::GUIEventAdapter::KeySymbol) *(event->text().toAscii().data() ) );
}

void AdapterWidget::keyReleaseEvent( QKeyEvent* event )
{
    _gw->getEventQueue()->keyRelease( (osgGA::GUIEventAdapter::KeySymbol) *(event->text().toAscii().data() ) );
}

void AdapterWidget::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;
    }
    _gw->getEventQueue()->mouseButtonPress(event->x(), event->y(), button);
}

void AdapterWidget::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;
    }
    _gw->getEventQueue()->mouseButtonRelease(event->x(), event->y(), button);
}

void AdapterWidget::mouseMoveEvent( QMouseEvent* event )
{
    _gw->getEventQueue()->mouseMotion(event->x(), event->y());
}

根据我的了解,实际上,上面的那个窗口仅仅实现了一帧里面的viewerInit。其他的任何事情都没做。于是乎需要做realize吧~如果不知道OSG一帧大概需要做些什么东西的人,可以看我的某一篇日志,里面介绍了一帧里面OSG所做的五个大动作。而realize实际上只是属于第一个动作里面收尾的一个动作(也就是初始化视景器)。

还是让我们来看看初始化视景器的代码吧~

lass ViewerQT : public osgViewer::Viewer, public AdapterWidget
{
    public:

        ViewerQT(QWidget * parent = 0, const char * name = 0, const QGLWidget * shareWidget = 0, WindowFlags f = 0):
            AdapterWidget( parent, name, shareWidget, f )
        {
            getCamera()->setViewport(new osg::Viewport(0,0,width(),height()));
            getCamera()->setProjectionMatrixAsPerspective(30.0f, static_cast(width())/static_cast(height()), 1.0f, 10000.0f);
            getCamera()->setGraphicsContext(getGraphicsWindow());

            setThreadingModel(osgViewer::Viewer::SingleThreaded);

            connect(&_timer, SIGNAL(timeout()), this, SLOT(updateGL()));
            _timer.start(10);
        }

        virtual void paintGL()
        {
            frame();
        }
    
    protected:

        QTimer _timer;
};
既然场景已经初始化完毕了,这样简单的一帧就算是完成了。(实际上一帧中的五个大动作中,后面四个都采用的默认设置,那么这里也可以顺便总结一下,OSG一帧五个大动作中,第一帧实际上是准备整个场景,其中包含了第一次运行应用程序对OSG环境的初始化,而恰好,我们在做OSG嵌入其他GUI时做的就是这个初始化工作,因为默认的初始化工作满足不了我们的需求,所以我们需要自己去写。)

然后调用main函数,看看结果吧~

int main( int argc, char *argv[])
{
    QApplication a( argc, argv );

  osg::ref_ptr loadedModel = osgDB::readNodeFile("osgcool.osg");

        std::cout<<"Using ViewetQT MDI version"<          ViewerQT* viewerWindow = new ViewerQT;
         viewerWindow->setCameraManipulator(new osgGA::TrackballManipulator);
         viewerWindow->setSceneData(loadedModel.get());
         QMainWindow* mw = new QMainWindow();
         QMdiArea* mdiArea = new QMdiArea(mw);
         mw->setCentralWidget(mdiArea);

         QMdiSubWindow *subWindow = mdiArea->addSubWindow(viewerWindow);
         subWindow->showMaximized();
         subWindow->setWindowTitle("New Window");
         mw->show();
    a.connect( &a, SIGNAL(lastWindowClosed()), &a, SLOT(quit()) );
    return a.exec();
}

这里需要注意的是Qt是一个非常懒的家伙~你必须告诉它去刷新,它才肯动一下,所以那个设置QTimer的地方在所有需要有定时刷新机制的程序中必须用到。

你可能感兴趣的:(OSG嵌入Qt解决方案一)