天天看點

OSG嵌入Qt的第二種方式:使用QOpenGLWidget/QGLWidget

幾天前有朋友問OSG嵌入Qt的思路。我說整體思路就是用OpenGL的方式來進行OSG的繪制,而正好Qt已經整合了OpenGL的功能,我們可以以此為依托來進行OSG的渲染繪制工作。而在Qt中卻有QWidget和QGraphicsView兩種不同的體系架構,于是我們也就有了兩種不同的嵌入方式,關于QGraphicsView的嵌入方式我前面的部落格OSGEarth嵌入Qt的QGraphicsView架構已經完成了,本篇是增加對QWidget體系的講解。

Qt中具有OpenGL功能的是一個繼承自QWidget的widget,叫做QGLWidget,但是在Qt5.5的版本中此類已被廢棄,取而代之的是一個叫做QOpenGLWidget的類。在此,我用的是5.5版本的QOpenGLWidget,若是您用的之前的Qt版本請使用QGLWidget。

首先,我們需要處理一下按鍵、滑鼠事件,處理的方式與QGraphicsView體系中使用的方式是相同的,代碼也沒太大變化,可參看OSGEarth嵌入Qt的QGraphicsView架構變化的地方在于繼承的對象變了,在QGraphicsView體系中EventAdapter 繼承自QGraphicsView,現在我們需要EventAdapter 繼承自QOpenGLWidget。

完成事件處理後,我們來進行OSG的嵌入工作。

首先是頭檔案:

class Widget3D : public EventAdapter, public osgViewer::Viewer
{
    Q_OBJECT

public:
    Widget3D(QWidget *parent = );
    ~Widget3D();

    osgViewer::Viewer* getOSGViewer(){ return this; }
    osg::Group* getRoot(){ return m_pRoot; }

protected:
    // 看這裡,這是關鍵,即每幀重新整理的操作有這裡來完成,使用的思路和QGraphicsView體系中是一樣的,控制定時器進行更新
    virtual void paintGL(){ frame(); }
    virtual void timerEvent(QTimerEvent *event){ update(); }

private:
    void init3D();
    osg::ref_ptr<osg::Camera> createCamera(int x, int y, int w, int h);

private:
    osg::ref_ptr<osg::Group> m_pRoot;
};
           

注意我們的Widget3D直接從osgViewer::Viewer繼承了,而在QGraphicsView體系中是設定的視口(setViewPort)。

Widget3D::Widget3D(QWidget *parent)
    : EventAdapter(parent)
{
    init3D();
    this->setMouseTracking(true);
}

Widget3D::~Widget3D()
{

}

void Widget3D::init3D()
{
    m_pRoot = new osg::Group;
    m_pRoot->setName("Root");

    this->setCamera(createCamera(, , width(), height()));
    osg::ref_ptr<osgGA::TrackballManipulator> pManipulator =
        new osgGA::TrackballManipulator;
    this->setCameraManipulator(pManipulator);
    this->addEventHandler(new osgViewer::StatsHandler);
    this->addEventHandler(new osgViewer::ThreadingHandler());
    this->addEventHandler(new osgViewer::HelpHandler);
    this->addEventHandler(new osgGA::StateSetManipulator(this->getCamera()->getOrCreateStateSet()));
    this->setThreadingModel(osgViewer::Viewer::SingleThreaded);

    m_pRoot->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::ON);
    m_pRoot->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::ON);
    this->setSceneData(m_pRoot);

    // 添加cow.osg作為測試
    osg::ref_ptr<osg::Node> pNode = osgDB::readNodeFile("cow.osg");
    osg::ref_ptr<osg::MatrixTransform> matTrans = new osg::MatrixTransform;
    matTrans->addChild(pNode);
    matTrans->setMatrix(osg::Matrix::translate(osg::Vec3d(, -, )));
    m_pRoot->addChild(matTrans);

    startTimer();
}

osg::ref_ptr<osg::Camera> Widget3D::createCamera(int x, int y, int w, int h)
{
    m_pGraphicsWindow = new osgViewer::GraphicsWindowEmbedded(x, y, w, h);
    osg::DisplaySettings* ds = osg::DisplaySettings::instance().get();
    osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
    traits->windowDecoration = true;
    traits->x = x;
    traits->y = y;
    traits->width = w;
    traits->height = h;
    traits->doubleBuffer = true;
    traits->sharedContext = ;

    osg::ref_ptr<osg::Camera> camera = new osg::Camera;
    camera->setGraphicsContext(m_pGraphicsWindow);
    camera->setClearColor(osg::Vec4(, , , ));
    camera->setViewport(new osg::Viewport(, , traits->width, traits->height));
    camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    camera->setProjectionMatrixAsPerspective(
        , static_cast<double>(traits->width) / static_cast<double>(traits->height), , );

    return camera.release();
}