osg窗口的创建(二)

本文接着osg窗口的创建(一)中关于GraphicsContext的内容继续阅读。

还是先看看GraphicsContext中的继承图


由于是在Windows平台下分析窗口的创建,因此本文选取Win32中的实现来探讨,其他平台的原理类似,只是在细节上稍有不同。

前文说到了 isRealized的实现,我们在看看实现过程:

GraphicsContext的isRealized实现如下:

 inline bool isRealized() const 
 { 
 	return isRealizedImplementation();
 } 

isRealizedImplementation函数在GraphicsContext中是一个纯虚函数,在GraphicsWindow中的实现仅仅打印出一个提示信息(等同于没实现),真正的实现在GraphicsWindowWin32之中,实现如下:

virtual bool GraphicsWindowWin32::isRealizedImplementation() const 
{ 
	return _realized; 
}
仅仅是返回一个标识,于是我们有理由认为在 realize函数中设置了该标识。(实际上确实是这样的,后续再realize代码中再分析)

继续看frame中的实现:

        if (!isRealized())
        {
            realize();
        }
可以看到如果没有设置好窗口(isRealized返回值为false),那么就调用 realize函数来设置窗口,事实上osg的窗口创建过程就是在realize函数中完成的,在代码调试中到断点越过realize函数体,可以在任务栏上看到新的窗口出现。realize的详细实现如下:

1. realize在ViewerBase中是纯虚函数

2. realize的具体实现在osgViewer::Viewer类中实现:

void Viewer::realize()
{
    Contexts contexts;
    getContexts(contexts);

    if (contexts.empty())
    {
        OSG_INFO<<"Viewer::realize() - No valid contexts found, setting up view across all screens."<<std::endl;
        const char* ptr = 0;
        if ((ptr = getenv("OSG_CONFIG_FILE")) != 0)
        {
            readConfiguration(ptr);
        }
        else
        {
            int screenNum = -1;
            if ((ptr = getenv("OSG_SCREEN")) != 0)
            {
                if (strlen(ptr)!=0) screenNum = atoi(ptr);
                else screenNum = -1;
            }

            int x = -1, y = -1, width = -1, height = -1;
            if ((ptr = getenv("OSG_WINDOW")) != 0)
            {
                std::istringstream iss(ptr);
                iss >> x >> y >> width >> height;
            }

            if (width>0 && height>0)
            {
                if (screenNum>=0) setUpViewInWindow(x, y, width, height, screenNum);
                else setUpViewInWindow(x,y,width,height);
            }
            else if (screenNum>=0)
            {
                setUpViewOnSingleScreen(screenNum);
            }
            else
            {
                setUpViewAcrossAllScreens();
            }
        }

        getContexts(contexts);
    }

    if (contexts.empty())
    {
        OSG_NOTICE<<"Viewer::realize() - failed to set up any windows"<<std::endl;
        _done = true;
        return;
    }

    unsigned int maxTexturePoolSize = osg::DisplaySettings::instance()->getMaxTexturePoolSize();
    if (_camera->getDisplaySettings()) maxTexturePoolSize = std::max(maxTexturePoolSize, _camera->getDisplaySettings()->getMaxTexturePoolSize());
    if (_displaySettings.valid()) maxTexturePoolSize = std::max(maxTexturePoolSize, _displaySettings->getMaxTexturePoolSize());

    unsigned int maxBufferObjectPoolSize = osg::DisplaySettings::instance()->getMaxBufferObjectPoolSize();
    if (_displaySettings.valid()) maxBufferObjectPoolSize = std::max(maxBufferObjectPoolSize, _displaySettings->getMaxBufferObjectPoolSize());
    if (_camera->getDisplaySettings()) maxBufferObjectPoolSize = std::max(maxBufferObjectPoolSize, _camera->getDisplaySettings()->getMaxBufferObjectPoolSize());

    for(Contexts::iterator citr = contexts.begin();
        citr != contexts.end();
        ++citr)
    {
        osg::GraphicsContext* gc = *citr;

        // set the pool sizes, 0 the default will result in no GL object pools.
        gc->getState()->setMaxTexturePoolSize(maxTexturePoolSize);
        gc->getState()->setMaxBufferObjectPoolSize(maxBufferObjectPoolSize);

        gc->realize();

        if (_realizeOperation.valid() && gc->valid())
        {
            gc->makeCurrent();

            (*_realizeOperation)(gc);

            gc->releaseContext();
        }
    }

    // attach contexts to _incrementalCompileOperation if attached.
    if (_incrementalCompileOperation) _incrementalCompileOperation->assignContexts(contexts);

    bool grabFocus = true;
    if (grabFocus)
    {
        for(Contexts::iterator citr = contexts.begin();
            citr != contexts.end();
            ++citr)
        {
            osgViewer::GraphicsWindow* gw = dynamic_cast<osgViewer::GraphicsWindow*>(*citr);
            if (gw)
            {
                gw->grabFocusIfPointerInWindow();
            }
        }
    }

    // initialize the global timer to be relative to the current time.
    osg::Timer::instance()->setStartTick();

    // pass on the start tick to all the associated event queues
    setStartTick(osg::Timer::instance()->getStartTick());

    setUpThreading();

    if (osg::DisplaySettings::instance()->getCompileContextsHint())
    {
        int numProcessors = OpenThreads::GetNumberOfProcessors();
        int processNum = 0;

        for(unsigned int i=0; i<= osg::GraphicsContext::getMaxContextID(); ++i)
        {
            osg::GraphicsContext* gc = osg::GraphicsContext::getOrCreateCompileContext(i);

            if (gc)
            {
                gc->createGraphicsThread();
                gc->getGraphicsThread()->setProcessorAffinity(processNum % numProcessors);
                gc->getGraphicsThread()->startThread();

                ++processNum;
            }
        }
    }
}
函数体的实现代码相对较长,分段进行分析:

1. 首先收集当前视景器Viewer中相机的渲染上下文,代码和isRealized中的实现一样

2. 如果不存在渲染上下文,我们进入第一段if判断中,可以看到主要进行的工作如下:

  • 读取环境变量OSG_CONFIG_FILE,如果设置了OSG_CONFIG_FILE指向一个.view后缀的文件,可以创建该文件描述的窗口,格式如下:(在openscenegraph-data目录中的configuration目录下有.view类型的文件)
osgViewer::Viewer
{
    setUpViewInWindow 100 200 600 400 0
}
如果设置了OSG_CONFIG_FILE环境变量,但是环境变量所对应的文件解析有错,那么整个程序就会退出。

  • 如果没有设置OSG_CONFIG_FILE环境变量,那么程序会检查 OSG_SCREEN和OSG_WINDOW这两个环境变量的值,OSG_SCREEN对应窗口的个数(值是一个整型数)OSG_WINDOW对应窗口的大小和位置,格式是(x, y, w, h) 分别是窗口左上角点坐标(x, y)以及窗口的长和宽(w, h),具体来说是以下情况:
Screen取值     Window中width和height取值窗口设置函数
大于或等于0长和宽都大于0setUpViewInWindow(x, y, width, height, screenNum)
小于0长和宽都大于0setUpViewInWindow(x, y, width, height)
大于0长和宽为0或者负值setUpViewOnSingleScreen(screenNum)

其他情况下窗口设置函数使用setUpViewAcrossAllScreens()

当窗口设置完成之后,osg会再次调用getContexts(contexts) 搜集目前可用的设备渲染上下文,如果这时候还没有窗口产生,也就是说设置窗口的各种方式都失败了,那么程序就会退出,如果成功,那么会针对已经常见的窗口进行一些设置,完成后续的操作

这三个函数的实现如下所示:

void View::setUpViewAcrossAllScreens()
{
    apply(new osgViewer::AcrossAllScreens());
}

void View::setUpViewInWindow(int x, int y, int width, int height, unsigned int screenNum)
{
    apply(new osgViewer::SingleWindow(x, y, width, height, screenNum));
}

void View::setUpViewOnSingleScreen(unsigned int screenNum)
{
    apply(new osgViewer::SingleScreen(screenNum));
}
这桑函数调用了osgViewer命名空间中的三个窗口配置类,它们的关系如下图所示:



基类osgViewer::ViewConfig提供了一个虚函数 virtual void  configure (osgViewer::View &) const,在子类中通过实现该函数来配置渲染的窗口大小和位置。

SingleScreen在实现过程中会调用SingleWindow来完成设置,AcrossAllScreen在未设置窗口的大小和显示屏数目的时候会被调用(一般就是我们默认使用osg的场景),那么它会调用SingleWindow来设置,在设置过程中会读取外接显示器的分辨率,产生一个全屏的窗口,代码如下:

//来自于osgViewer::SingleWindow::configure
    if (traits->width<=0 || traits->height<=0 ) 
    {
        osg::GraphicsContext::ScreenIdentifier si;
        si.readDISPLAY();

        // displayNum has not been set so reset it to 0.
        if (si.displayNum<0) si.displayNum = 0;

        si.screenNum = _screenNum;

        unsigned int width, height;
        wsi->getScreenResolution(si, width, height);
        if (traits->width<=0) traits->width = width;
        if (traits->height<=0) traits->height = height;
    }

下面继续realize后面的代码,在完成窗口参数的设置之后,后面的代码无非是一些关于OpenGL缓冲区对象的设置等,暂时与窗口无关。在此之后进入到for循环之中,这里面就是窗口设置的内容了,实现的内容是对所有的相机中的设备上下文进行检查,并真正的产生窗口,窗口的产生调用在

gc->realize();

我们查看一下它的实现:

bool GraphicsContext::realize()
{
    if (realizeImplementation())
    {
        return true;
    }
    else
    {
        return false;
    }
}
实现中调用了另一个函数,继续往下看,它与isRealizedImplementation函数类似,在GraphicsContext中是一个纯虚函数,在窗口系统中实现相应的代码:

bool GraphicsWindowWin32::realizeImplementation()
{
    if (_realized) return true;

    if (!_initialized)
    {
        init();
        if (!_initialized) return false;
    }

    if (_traits.valid() && (_traits->sharedContext.valid() || _traits->vsync || _traits->swapGroupEnabled))
    {
        // make context current so we can test capabilities and set up context sharing
        struct RestoreContext
        {
            RestoreContext()
            {
                _hdc = wglGetCurrentDC();
                _hglrc = wglGetCurrentContext();
            }
            ~RestoreContext()
            {
                wglMakeCurrent(_hdc,_hglrc);
            }
        protected:
            HDC      _hdc;
            HGLRC    _hglrc;
        } restoreContext;

        _realized = true;
        bool result = makeCurrent();
        _realized = false;

        if (!result)
        {
            return false;
        }

        // set up sharing of contexts if required
        GraphicsHandleWin32* graphicsHandleWin32 = dynamic_cast<GraphicsHandleWin32*>(_traits->sharedContext.get());
        if (graphicsHandleWin32)
        {
            if (!wglShareLists(graphicsHandleWin32->getWGLContext(), getWGLContext()))
            {
                reportErrorForScreen("GraphicsWindowWin32::realizeImplementation() - Unable to share OpenGL context", _traits->screenNum, ::GetLastError());
                return false;
            }
        }

        // if vysnc should be on then enable it.
        if (_traits->vsync)
        {
            setSyncToVBlank(_traits->vsync);
        }

        // If the swap group is active then enable it.
        if (_traits->swapGroupEnabled)
        {
            setSwapGroup(_traits->swapGroupEnabled, _traits->swapGroup, _traits->swapBarrier);
        }
    }

    if (_ownsWindow)
    {
        //
        // Bring the window on top of other ones (including the taskbar if it covers it completely)
        //
        // NOTE: To cover the taskbar with a window that does not completely cover it, the HWND_TOPMOST
        // Z-order must be used in the code below instead of HWND_TOP.
        // @todo: This should be controlled through a flag in the traits (topMostWindow)
        //

        if (!::SetWindowPos(_hwnd,
                            HWND_TOP,
                            _windowOriginXToRealize,
                            _windowOriginYToRealize,
                            _windowWidthToRealize,
                            _windowHeightToRealize,
                            SWP_SHOWWINDOW))
        {
            reportErrorForScreen("GraphicsWindowWin32::realizeImplementation() - Unable to show window", _traits->screenNum, ::GetLastError());
            return false;
        }

        if (!::UpdateWindow(_hwnd))
        {
            reportErrorForScreen("GraphicsWindowWin32::realizeImplementation() - Unable to update window", _traits->screenNum, ::GetLastError());
            return false;
        }
    }

    _realized = true;

    // make sure the event queue has the correct window rectangle size and input range
    getEventQueue()->syncWindowRectangleWithGraphcisContext();

    return true;
}
可以看到窗口的产生是在这个函数中,在Win32程序设计中的常用函数UpdateWindow与SetWindowPos就是在这里调用的,SetWindowPos最终将窗口Show出来,让我们在系统中可以看到创建好的场景。


要将OSG模型嵌入到Qt窗口中,可以使用Qt的OpenGL模块和OSG的Qt插件。具体步骤如下: 1. 创建一个Qt窗口,继承自QOpenGLWidget。 2. 在窗口的初始化函数中,创建一个OSG Viewer对象,并将其设置为OpenGL上下文。 3. 在窗口的paintGL()函数中,调用OSG渲染函数进行渲染。 4. 在窗口的resizeGL()函数中,更新OSG Viewer的视口大小。 5. 在Qt的主函数中,加载OSG的Qt插件,以便在Qt应用程序中使用OSG。 下面是一个简单的示例代码: ```cpp #include <osgViewer/Viewer> #include <osgQt/GraphicsWindowQt> class OSGWidget : public QOpenGLWidget { public: OSGWidget(QWidget* parent = nullptr) : QOpenGLWidget(parent) { osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits(); traits->windowName = "OSG Widget"; traits->x = x(); traits->y = y(); traits->width = width(); traits->height = height(); traits->windowDecoration = false; traits->doubleBuffer = true; traits->sharedContext = 0; osgQt::GraphicsWindowQt* gw = new osgQt::GraphicsWindowQt(traits.get()); osg::Camera* camera = new osg::Camera; camera->setGraphicsContext(gw); camera->setViewport(new osg::Viewport(0, 0, width(), height())); setCameraManipulator(new osgGA::TrackballManipulator); viewer.setCamera(camera); viewer.setThreadingModel(osgViewer::Viewer::SingleThreaded); } protected: void paintGL() override { viewer.frame(); } void resizeGL(int w, int h) override { viewer.getCamera()->setViewport(new osg::Viewport(0, 0, w, h)); } private: osgViewer::Viewer viewer; }; int main(int argc, char** argv) { QApplication app(argc, argv); osgDB::Registry::instance()->loadLibrary("osgdb_qt.so"); OSGWidget widget; widget.resize(640, 480); widget.show(); return app.exec(); } ``` 这里假设OSG已经正确安装,并且Qt应用程序已经配置好了OpenGL环境。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值