天天看點

osg自學筆記2——《最長的一幀》第1、2日0 概況第一日第二日

最長的一幀 作者:王銳(array)

參考的osg源碼版本為 3.6.3

osg自學筆記2——《最長的一幀》

  • 0 概況
  • 第一日
    • 具體内容
      • osgViewer::ViewerBase::frame()函數
      • osgViewer::View::init()函數
      • 總結
  • 第二日
    • 具體内容
      • 一種常用的嵌入方式也許是這樣實作的:
      • osgViewer::Viewer::getContexts()函數
      • 建立預設 GraphicsContext 裝置的方法有以下幾種:
      • 總結

0 概況

這裡隻研究一幀的渲染。

while (!viewer.done()) 
 viewer.frame();
           

第一日

具體内容

osgViewer::ViewerBase::frame()函數

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();//事件周遊 滑鼠鍵盤視窗
    //周遊更新回調,負責更新 DatabasePager 與 ImagePager 分頁資料處理元件。
	updateTraversal();
	//周遊渲染,線程處理方法,完成場景的篩選(cull)和繪制(draw)工作。
	renderingTraversals();
}
           

osgViewer::View::init()函數

//完成視景器的初始化工作
void View::init()
{
    OSG_INFO<<"View::init()"<<std::endl;

	//_eventQueue 用于儲存該視景器的事件隊列。儲存一個 GUIEventAdapter 的連結清單.
	//createEvent 函數的作用是配置設定和傳回一個新的 GUIEventAdapter事件的指針。
	//osgGA::GUIEventAdapter,用于表達各種類型的滑鼠、鍵盤、觸壓筆和視窗事件。
	//initEvent 新事件的類型被指定為 FRAME 事件,即每幀都會觸發的一個事件。
    osg::ref_ptr<osgGA::GUIEventAdapter> initEvent = 
                       _eventQueue->createEvent();
    initEvent->setEventType(osgGA::GUIEventAdapter::FRAME);

	//_cameraManipulator 是視景器中所用的場景漫遊器,
	//不同的漫遊器初始化函數是不同的。
    if (_cameraManipulator.valid())
    {
        _cameraManipulator->init(*initEvent, *this);
    }
}
           

上面的代碼将新建立的 FRAME 事件和 Viewer 對象本身傳遞給_cameraManipulator 的init 函數,不同的漫遊器(如 TrackballManipulator、DriveManipulator)會重寫各自的 init 函數,實作自己所需的初始化工作。如果讀者希望自己編寫一個場景的漫遊器,那麼覆寫并使用 osgGA::MatrixManipulator::init 就可以靈活地初始化自定義漫遊器的功能了,它的調用時機就在這裡。

總結

  • 解讀成果:

    osgGA::EventQueue::createEvent,

    osgGA::MatrixManipulator::init,

    osgViewer::View::init,

    osgViewer::Viewer::viewerInit。

  • 懸疑清單:

第二日

具體内容

一種常用的嵌入方式也許是這樣實作的:

對于需要将 OSG 嵌合到各式各樣的 GUI 系統(如 MFC,Qt,wxWidgets 等)的朋友來說,osg::GraphicsContext 類是經常要打交道的對象之一。

實作如下:

osg::ref_ptr<osg::GraphicsContext::Traits> traits = 
                              new osg::GraphicsContext::Traits; 
osg::ref_ptr<osg::Referenced> windata = 
 new osgViewer::GraphicsWindowWin32::WindowData(hWnd); 
traits->x = 0; 
traits->y = 0; 
……
traits->inheritedWindowData = windata; 
osg::GraphicsContext* gc =
     osg::GraphicsContext::createGraphicsContext(traits.get()); 
Camera* camera = viewer.getCamera(); 
camera->setGraphicsContext(gc); 
……
viewer.setCamera(camera);
           

這個過程雖然比較繁雜,但是順序還是十厘清楚的:

  • 首先設定嵌入視窗的特性(Traits),例如 X、Y 位置,寬度和高度,以及父視窗的句柄(inheritedWindowData);
  • 然後根據特性的設定建立一個新的圖形裝置上下文(GraphicsContext),将其賦予場景所用的錄影機。

osgViewer::Viewer::getContexts()函數

//主錄影機沒有建立圖形上下文,是以也就得不到裝置的指針。?這裡添加了裝置上下文啊?
//contextSet這個局部變量是判斷GraphicsContext是否重複。
void Viewer::getContexts(Contexts& contexts, bool onlyValid)
{
    typedef std::set<osg::GraphicsContext*> ContextSet;
    ContextSet contextSet;

    contexts.clear();
	//首先判斷場景的主錄影機_camera 是否包含了一個有效的GraphicsContext 裝置
    if (_camera.valid() &&
        _camera->getGraphicsContext() &&
        (_camera->getGraphicsContext()->valid() || !onlyValid))
    {
        contextSet.insert(_camera->getGraphicsContext());
        contexts.push_back(_camera->getGraphicsContext());
    }
	//然後再周遊所有的從錄影機_slaves,
	//将所有找到的 GraphicsContext 圖形上下文裝置記錄下來。
	//随後,将這些 GraphicsContext 的指針追加到傳入參數(contexts 向量組)中,
	//并使用std::sort 執行了一步排序的工作,所
    for(unsigned int i=0; i<getNumSlaves(); ++i)
    {
        Slave& slave = getSlave(i);
        osg::GraphicsContext* sgc = slave._camera.valid() ?
                  slave._camera->getGraphicsContext() : 0;
        if (sgc && (sgc->valid() || !onlyValid))
        {
            if (contextSet.count(sgc)==0)//不重複記錄
            {
                contextSet.insert(sgc);
                contexts.push_back(sgc);
            }
        }
    }
}
           

建立預設 GraphicsContext 裝置的方法有以下幾種:

1、讀取 OSG_CONFIG_FILE 環境變量的内容:如果使用者在這個環境變量中定義了一個檔案路徑的話,那麼系統會嘗試用 osgDB::readObjectFile 函數讀入這個檔案,使用 cfg 插件進行解析;如果成功的話,則調用 osgViewer::Viewer::take 函數,使用配置資訊設定目前的視景器。這些工作在 osgViewer::Viewer::readConfiguration 函數中實作。

2、讀取 OSG_WINDOW 環境變量的内容:如果使用者以“x y w h”的格式在其中定義了視窗的左上角坐标(x,y)和尺寸(w,h)的話(注意要以空格為分隔符),系統會嘗試使用 osgViewer::View::setUpViewInWindow 函數來建立裝置。

3、讀取 OSG_SCREEN 環境變量的内容:如果使用者在其中定義了所用螢幕的數量的話,系統會嘗試用 osgViewer::View::setUpViewOnSingleScreen 函數,為每一個顯示屏建立一個全螢幕的圖形視窗;如果同時還設定了 OSG_WINDOW,那麼這兩個環境變量都可以起到作用,此時将調用 setUpViewInWindow 函數。

4、如果上述環境變量都沒有設定的話(事實上這也是最常見的情況),那麼系統将調用osgViewer::View::setUpViewAcrossAllScreens 函數,嘗試建立一個全屏顯示的圖形裝置。

那麼,下文就從這幾種圖形裝置建立的方法開始。至于後面的路,果然遙遙無期呢。

總結

  • 解讀成果:

    osgViewer::Viewer::getContexts,

    osgViewer::Viewer::readConfiguration。

  • 懸疑清單:

    類變量_cameraWithFocus 的意義是什麼?

    ————完結————

上一篇: Swing基礎

繼續閱讀