探索未知種族之osg類生物---起源

探索未知種族之osg類生物---起源
任何程序都是有生命的,是生命就需要呼吸。例如普通的windows程序,當運行完main()函數後,就需要進入消息循環,來監聽用戶的各種操作,以便做出及時的迴應。這樣的每次循環就像生命的每次呼吸,來維持生命體徵。

osg的程序不僅僅需要消息循環來監聽用戶的鼠標、鍵盤等操作,同時也得具備了渲染循環。當然隨着我們的對osg的深入瞭解會發現,osg的事件監聽和渲染循環是串行的。但是當我們把osg與MFC(QT)等結合時,相應UI上的鼠標,鍵盤事件的同時也要兼顧可能發生在osg中的效果,所以一般的osg程序起碼需要兩個並行的線程(例如osg與qt結合使用,爲了保持足夠靈敏的相應速度就需要把QTUI和osg渲染看成兩種生命,分爲兩個線程)來維持它的正常運行。我們今天就是要解讀osg程序賴以生存的每次呼吸。

首先我們得找到osg是用什麼呼吸的,就想地球上的一般生物都是用鼻子呼吸,我們又大概得知道鼻子長在生物的那個位置。這樣我們纔可以開始我們的研究。當然我們肯定得有osg的源碼,就像我們研究生物的呼吸先得有這種生物的身體。有了身體我們還得持續的觀察一個有生命的生物,所以我們最先得到osg的可運行的程序就是example中的各種程序。其中大部分的程序main()函數的最後部分都是調用一下viewer.run()。

探索未知種族之osg類生物---起源

所以我們可以有一個模糊的判斷這一類的通過調用viewer類的run函數的程序,他的呼吸系統可能是通過run完成的。但是run()函數是一個單獨的一行,按說他執行完畢以後程序就會結束了,所以我們有了新的判斷osg的每一幀的調用的入口是在run()函數中的。這是osg程序存在的一種形式(或者叫獨立運行模式)。Osg還有另一種存在形式,就是和各種UI混合使用,例如qt與osg結合使用,MFC與osg結合使用等等。我們可以從examples/osgviewerQt 的例子,可以根據上一個的思路,呼吸不是一次性的動作,是隻要存活就會一直存在的。所以從osgviewerQt.cpp中根據以前的經驗定位到timer (計時器),他每次timeout觸發時調用的函數update()中一定包含了osg的每一幀的調用的入口。

探索未知種族之osg類生物---起源

根據上面兩種osg的存活形式,可以進行進一步的確認,究竟哪裏纔是維持osg生命體徵的位置。Viewer.run()函數(OSG Core/osgViewer/Viewer.cpp)最後會繼續調用ViewerBase::run()函數(OSG Core/osgViewer/ViewerBase.cpp),

int Viewer::run()
{
if (!getCameraManipulator() && getCamera()->getAllowEventFocus())
{
setCameraManipulator(new osgGA::TrackballManipulator());
}

setReleaseContextAtEndOfFrameHint(false);

return ViewerBase::run();

}

int ViewerBase::run()
{
if (!isRealized())
{
realize();
}

const char* run_frame_count_str = getenv("OSG_RUN_FRAME_COUNT");
unsigned int runTillFrameNumber = run_frame_count_str==0 ? osg::UNINITIALIZED_FRAME_NUMBER : atoi(run_frame_count_str);

while(!done() && (run_frame_count_str==0 || getViewerFrameStamp()->getFrameNumber()<runTillFrameNumber))
{
    double minFrameTime = _runMaxFrameRate>0.0 ? 1.0/_runMaxFrameRate : 0.0;
    osg::Timer_t startFrameTick = osg::Timer::instance()->tick();
    if (_runFrameScheme==ON_DEMAND)
    {
        if (checkNeedToDoFrame())
        {
            frame();
        }
        else
        {
            // we don't need to render a frame but we don't want to spin the run loop so make sure the minimum
            // loop time is 1/100th of second, if not otherwise set, so enabling the frame microSleep below to
            // avoid consume excessive CPU resources.
            if (minFrameTime==0.0) minFrameTime=0.01;
        }
    }
    else
    {
        frame();
    }

    // work out if we need to force a sleep to hold back the frame rate
    osg::Timer_t endFrameTick = osg::Timer::instance()->tick();
    double frameTime = osg::Timer::instance()->delta_s(startFrameTick, endFrameTick);
    if (frameTime < minFrameTime) OpenThreads::Thread::microSleep(static_cast(1000000.0*(minFrameTime-frameTime)));
}

return 0;

}
我們在ViewerBase::run()中繼續耐心的尋找就會發現有一個特殊的函數frame(),爲什麼特殊呢?因爲frame的英文的意思就是’幀’,而我們學渲染都知道’幀’代表屏幕上一幅畫,這和osg庫的本質就聯繫在了一起。Osg就是一個庫,一個在計算機屏幕上作畫的庫。所以ViewerBase::frame()就是我們要找的osg中會呼吸的地方。同樣我們在examples/osgviewerQt中也會發現,timer每到設定事件就會調用update()函數,而qt的update()函數在內部就會調用paintEvent()函數,我們在osgviewerQt.cpp的paintEvent()函數中也會發現osg::CompositeViewer的update函數,而osg::CompositeViewer繼承自ViewerBase,所以最後也會定位到ViewerBase::frame()。這樣我們就可以確定osg這類生物的呼吸的入口是ViewerBase::frame()函數。終於我們打開了通往新世界的大門,下一步就是經歷輪迴,看看osg這類生物是怎麼生存的。

歡迎大家來我的新家看一看 http://www.3wwang.cn

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章