任何程序都是有生命的鹅髓,是生命就需要呼吸。例如普通的windows程序廓八,當運行完main()函數(shù)后奉芦,就需要進入消息循環(huán)赵抢,來監(jiān)聽用戶的各種操作,以便做出及時的回應声功。這樣的每次循環(huán)就像生命的每次呼吸烦却,來維持生命體征。
osg的程序不僅僅需要消息循環(huán)來監(jiān)聽用戶的鼠標先巴、鍵盤等操作其爵,同時也得具備了渲染循環(huán)。當然隨著我們的對osg的深入了解會發(fā)現(xiàn)伸蚯,osg的事件監(jiān)聽和渲染循環(huán)是串行的摩渺。但是當我們把osg與MFC(QT)等結合時,相應UI上的鼠標剂邮,鍵盤事件的同時也要兼顧可能發(fā)生在osg中的效果摇幻,所以一般的osg程序起碼需要兩個并行的線程(例如osg與qt結合使用,為了保持足夠靈敏的相應速度就需要把QTUI和osg渲染看成兩種生命挥萌,分為兩個線程)來維持它的正常運行绰姻。我們今天就是要解讀osg程序賴以生存的每次呼吸。
首先我們得找到osg是用什么呼吸的引瀑,就想地球上的一般生物都是用鼻子呼吸狂芋,我們又大概得知道鼻子長在生物的那個位置。這樣我們才可以開始我們的研究伤疙。當然我們肯定得有osg的源碼银酗,就像我們研究生物的呼吸先得有這種生物的身體。有了身體我們還得持續(xù)的觀察一個有生命的生物徒像,所以我們最先得到osg的可運行的程序就是example中的各種程序黍特。其中大部分的程序main()函數(shù)的最后部分都是調(diào)用一下viewer.run()。
所以我們可以有一個模糊的判斷這一類的通過調(diào)用viewer類的run函數(shù)的程序锯蛀,他的呼吸系統(tǒng)可能是通過run完成的灭衷。但是run()函數(shù)是一個單獨的一行,按說他執(zhí)行完畢以后程序就會結束了旁涤,所以我們有了新的判斷osg的每一幀的調(diào)用的入口是在run()函數(shù)中的翔曲。這是osg程序存在的一種形式(或者叫獨立運行模式)。Osg還有另一種存在形式劈愚,就是和各種UI混合使用瞳遍,例如qt與osg結合使用,MFC與osg結合使用等等菌羽。我們可以從examples/osgviewerQt 的例子掠械,可以根據(jù)上一個的思路,呼吸不是一次性的動作,是只要存活就會一直存在的猾蒂。所以從osgviewerQt.cpp中根據(jù)以前的經(jīng)驗定位到timer (計時器),他每次timeout觸發(fā)時調(diào)用的函數(shù)update()中一定包含了osg的每一幀的調(diào)用的入口均唉。
根據(jù)上面兩種osg的存活形式,可以進行進一步的確認肚菠,究竟哪里才是維持osg生命體征的位置舔箭。Viewer.run()函數(shù)(OSG Core/osgViewer/Viewer.cpp)最后會繼續(xù)調(diào)用ViewerBase::run()函數(shù)(OSG Core/osgViewer/ViewerBase.cpp),
//OSG Core/osgViewer/Viewer.cppintViewer::run(){if(!getCameraManipulator()&&getCamera()->getAllowEventFocus()){setCameraManipulator(newosgGA::TrackballManipulator());}setReleaseContextAtEndOfFrameHint(false);returnViewerBase::run();}
//OSG Core/osgViewer/ViewerBase.cppintViewerBase::run(){if(!isRealized()){realize();}constchar*run_frame_count_str=getenv("OSG_RUN_FRAME_COUNT");unsignedintrunTillFrameNumber=run_frame_count_str==0?osg::UNINITIALIZED_FRAME_NUMBER:atoi(run_frame_count_str);while(!done()&&(run_frame_count_str==0||getViewerFrameStamp()->getFrameNumber()<runTillFrameNumber)){doubleminFrameTime=_runMaxFrameRate>0.0?1.0/_runMaxFrameRate:0.0;osg::Timer_tstartFrameTick=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 rateosg::Timer_tendFrameTick=osg::Timer::instance()->tick();doubleframeTime=osg::Timer::instance()->delta_s(startFrameTick,endFrameTick);if(frameTime<minFrameTime)OpenThreads::Thread::microSleep(static_cast(1000000.0*(minFrameTime-frameTime)));}return0;}
我們在ViewerBase::run()中繼續(xù)耐心的尋找就會發(fā)現(xiàn)有一個特殊的函數(shù)frame()蚊逢,為什么特殊呢层扶?因為frame的英文的意思就是’幀’,而我們學渲染都知道’幀’代表屏幕上一幅畫时捌,這和osg庫的本質就聯(lián)系在了一起怒医。Osg就是一個庫,一個在計算機屏幕上作畫的庫奢讨。所以ViewerBase::frame()就是我們要找的osg中會呼吸的地方。同樣我們在examples/osgviewerQt中也會發(fā)現(xiàn)焰薄,timer每到設定事件就會調(diào)用update()函數(shù)拿诸,而qt的update()函數(shù)在內(nèi)部就會調(diào)用paintEvent()函數(shù),我們在osgviewerQt.cpp的paintEvent()函數(shù)中也會發(fā)現(xiàn)osg::CompositeViewer的update函數(shù)塞茅,而osg::CompositeViewer繼承自ViewerBase亩码,所以最后也會定位到ViewerBase::frame()。這樣我們就可以確定osg這類生物的呼吸的入口是ViewerBase::frame()函數(shù)野瘦。終于我們打開了通往新世界的大門描沟,下一步就是經(jīng)歷輪回,看看osg這類生物是怎么生存的鞭光。
歡迎大家來我的新家看一看?3wwang個人博客-記錄走過的技術之路