osgViewer::View::setUpViewOnSingleScreen()

void ViewerBase::frame(double simulationTime)
{
    if (_done) return;

    // OSG_NOTICE<<std::endl<<"CompositeViewer::frame()"<<std::endl<<std::endl;

    if (_firstFrame)
    {
        viewerInit();

        if (!isRealized())
        {
            realize();
        }

        _firstFrame = false;
    }
    advance(simulationTime);

    eventTraversal();
    updateTraversal();
    renderingTraversals();
}

 

setUpViewOnSingleScreen 和 setUpViewAcrossAllScreens 函数的实现流程与上一日介绍的 setUpViewInWindow 区别不是很大。值得注意的是,setUpViewAcrossAllScreens 函数中调用 GraphicsContext::getWindowingSystemInterface 函数取得了与平台相关的视窗 API 接口类(其中的原理请参看上一日的文字),并进而使用 WindowingSystemInterface::getNumScreens函数取得了当前系统的显示屏幕数。

事实上,如果我们需要在自己的程序中获取屏幕分辨率,或者设置屏幕刷新率的话,也可以使用同样的方法,调用 getScreenResolution,setScreenResolution 和 setScreenRefreshRate等相关函数即可。具体的实现方法可以参见 GraphicsWindowWin32.cpp 的源代码。

setUpViewAcrossAllScreens 函数可以自行判断屏幕的数量,并且使用多个从摄像机来对应多个屏幕的显示(或者使用主摄像机_camera 来对应单一屏幕)。此外它还针对水平分割显示(HORIZONTAL_SPLIT)的情况,对摄像机的左/右眼设置自动做了处理,有兴趣的读者不妨仔细研究一下。

最后,本函数还执行了一个重要的工作,即 View::assignSceneDataToCameras,这其中包括以下几项工作:
1、对于场景漫游器_cameraManipulator,执行其 setNode 函数和 home 函数,也就是设置漫游器对应于场景图形根节点,并回到其原点位置。不过在我们使用 setCameraManipulator函数时也会自动执行同样的操作。
2、将场景图形赋予主摄像机_camera,同时设置它对应的渲染器(Renderer)的相关函数。这里的渲染器起到了什么作用?还是先放到悬疑列表中吧,不过依照我们的解读速度,这个问题可能会悬疑很久。
3、同样将场景图形赋予所有的从摄像机_slaves,并设置每个从摄像机的渲染器。终于可以回到 realize 函数的正轨了,还记得下一步要做什么吧?对,在尝试设置了缺省的 GraphicsContext 设备之后,我们需要再次使用 getContexts 来获取设备,如果还是不成功的话,则 OSG 不得不退出运行了(连图形窗口都建立不起来,还玩什么)。

void View::setUpViewOnSingleScreen(unsigned int screenNum)
{
    apply(new osgViewer::SingleScreen(screenNum));
}

sgViewer/Viewer.cpp 第 496 行,void Viewer::realize()

void Viewer::realize()
{
    //OSG_INFO<<"Viewer::realize()"<<std::endl;

    Contexts contexts;
    getContexts(contexts);

    if (contexts.empty())
    {
        OSG_INFO<<"Viewer::realize() - No valid contexts found, setting up view across all screens."<<std::endl;

        // no windows are already set up so set up a default view

        std::string value;
        if (osg::getEnvVar("OSG_CONFIG_FILE", value))
        {
            readConfiguration(value);
        }
        else
        {
            int screenNum = -1;
            osg::getEnvVar("OSG_SCREEN", screenNum);

            int x = -1, y = -1, width = -1, height = -1;
            osg::getEnvVar("OSG_WINDOW", x, y, width, height);

            if (osg::getEnvVar("OSG_BORDERLESS_WINDOW", x, y, width, height))
            {
                osg::ref_ptr<osgViewer::SingleWindow> sw = new osgViewer::SingleWindow(x, y, width, height, screenNum);
                sw->setWindowDecoration(false);
                apply(sw.get());
            }
            else 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;
    }

    // get the display settings that will be active for this viewer
    osg::DisplaySettings* ds = _displaySettings.valid() ? _displaySettings.get() : osg::DisplaySettings::instance().get();
    osg::GraphicsContext::WindowingSystemInterface* wsi = osg::GraphicsContext::getWindowingSystemInterface();

    // pass on the display settings to the WindowSystemInterface.
    if (wsi && wsi->getDisplaySettings()==0) wsi->setDisplaySettings(ds);

    unsigned int maxTexturePoolSize = ds->getMaxTexturePoolSize();
    unsigned int maxBufferObjectPoolSize = ds->getMaxBufferObjectPoolSize();

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

        if (ds->getSyncSwapBuffers()) gc->setSwapCallback(new osg::SyncSwapBuffersCallback);

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


        /*
        首先是 GraphicsContext::realize 函数,实际上也就是 GraphicsContext::realizeImplementation 函数。
        realizeImplementation 是纯虚函数吗?没错,回想一下第三日的内容,当我们尝试使用createGraphicsContext 来创建一个图形设备上下文时,系统返回的实际上是这个函数的值:
        而正如我们历经千辛万苦所分析的那样,wsref 所指向的是平台相关的 API 接口类,也就是 Win32 API 的接口,也就是 GraphicsWindowWin32.cpp 中对应类的实例。换句话说,此时 WindowingSystemInterface:: createGraphicsContext 函数返回的值,也应当是派生自GraphicsContext 的具体类的实例!
        正确,对于 Windows 用户来说,这个函数返回的恰恰是 GraphicsWindowWin32 的实例,而前文的 realizeImplementation 函数,正是 GraphicsWindowWin32::realizeImplementation。
        */
        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());

    // configure threading.
    setUpThreading();

    if (osg::DisplaySettings::instance()->getCompileContextsHint())
    {
        for(unsigned int i=0; i<= osg::GraphicsContext::getMaxContextID(); ++i)
        {
            osg::GraphicsContext* gc = osg::GraphicsContext::getOrCreateCompileContext(i);

            if (gc)
            {
                gc->createGraphicsThread();
                gc->getGraphicsThread()->startThread();
            }
        }
    }
#if 0
    osgGA::GUIEventAdapter* eventState = getEventQueue()->getCurrentEventState();
    if (getCamera()->getViewport())
    {
        osg::Viewport* viewport = getCamera()->getViewport();
        eventState->setInputRange( viewport->x(), viewport->y(), viewport->x() + viewport->width(), viewport->y() + viewport->height());
    }
    else
    {
        eventState->setInputRange(-1.0, -1.0, 1.0, 1.0);
    }
#endif
}

 

 

  1、视景器 Viewer 的主/从摄像机均需要使用 setGraphicsContext 设置对应的图形设备上下文,实际上也就是对应的显示窗口;
  2、GraphicsContext 的创建由平台相关的抽象接口类 WindowingSystemInterface 负责,对于 Win32 平台而言,这个类是由 GraphicsWindowWin32.cpp 的 Win32WindowingSystem 类具体实现的,它创建的显示窗口设备即 osgViewer::GraphicsWindowWin32 的实例。
  3、进一步深究的话,如果窗口特性(Traits)中开启了 pbuffer 选项,则 OSG 将尝试创建 osgViewer::PixelBufferWin32 设备,以实现离屏渲染(Offscreen Render),纹理烘焙(Render-To-Texture)等工作;否则只建立通常的 OpenGL 窗口。
  真是令人兴奋!没错,GraphicsContext::makeCurrent 和 GraphicsContext:: releaseContext函数也是用相同的方法来实现多态性的,而它们的工作也就是 OpenGL 开发者使用函数wglMakeCurrent 完成的工作,将渲染上下文 RC 对应到正确的窗口绘制句柄上。
如果您还想要深究具体的实现方法的话,就好好地阅读 GraphicsWindowWin32.cpp 中的相关内容吧,不过我们的旅程要继续了。
等等,刚才那段程序里面,_realizeOperation 是什么,它又执行了什么?嗯,简单说来,这个变量是通过 ViewerBase::setRealizeOperation 来设置的,其主要作用是在执行 realize 函数时,顺便完成用户指定的一些工作。您自己的工作内容可以通过继承 osg::Operation 类,并重载 operator()操作符来添加。osgcatch 这个妙趣横生的例子(一个傻娃娃接玩具的小游戏)中就使用了 setRealizeOperation,主要的作用是为场景中的 Drawable 几何对象立即编译显示列表(Display List)。有兴趣的话不妨细细把玩一下。

 

这一节和王锐老师当时解读的osg版本源码差异性较大

 

文字参考:王锐老师《最长的一帧》
代码参考:OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield    (osg3.4)
posted @ 2019-06-30 18:00  西北逍遥  阅读(596)  评论(0编辑  收藏  举报