探索未知種族之osg類生物---呼吸分解之事件循環一

探索未知種族之osg類生物---呼吸分解之事件循環一
事件循環和更新循環**
終於到了我們嘴裏經常唸叨的事件循環、更新循環以及渲染循環了。首先我們來區分一下事件循環和渲染循環,他們兩個首先是兩個不同順序執行的過程,我們有時候會用到任意node的updateCallback函數,這個就是在更新循環的時候遍歷所有的node來調用updateCallback函數的;而事件循環是與用戶操作和操作系統事件想關聯的,以及調用我們設置的事件回調(EventCallback)函數。而事件循環函數(viewer::eventTraversal())是我們現在要探究的內容。

osgViewer::Viewer::eventTraversal()

if (_done) return;
double cutOffTime = _frameStamp->getReferenceTime();
double beginEventTraversal = osg::Timer::instance()->delta_s(_startTick, osg::Timer::instance()->tick());
// OSG_NOTICE<<"Viewer::frameEventTraversal()."<<std::endl;
// need to copy events from the GraphicsWindow's into local EventQueue;
osgGA::EventQueue::Events events;

Contexts contexts;
getContexts(contexts);

// set done if there are no windows
checkWindowStatus(contexts);
if (_done) return;<br><br>
進入這個函數,我們發現前幾行都是我們以前介紹過的osg器官。首先記錄了事件循環的開始時間,這樣做的目的是:與這個函數最後記錄的時間進行比較,然後記錄在_stats記錄器中,這樣可以幫助開發者瞭解每一幀當中事件遍歷,更新遍歷和渲染遍歷運行所佔用的時間比例,以便對整個程序進行調優工作。然後得到所有的GraphicsContext,保存到contexts中,當contexts爲空時,意味着沒有最終的畫布,osg會結束運行,通過設置_done=true;

然後的主要工作是:事件循環會得到已經發生的所有事件,並進行一定的篩選工作,最後全部都傳給各自的事件處理器。所以我們首先對其中一些新成員進行簡單的介紹:

eventState事件隊列的目前的狀態事件,eventState的設置是通過osgGA::EventQuene::setCurrentEventState函數進行設置的。
_eventSources 實在osgViewer::View下的成員變量,通過View::addDevice()函數來添加新的設備,他的主要作用就是在每一個幀的事件循環中便利所有的設備,然後得到通過Device :: getEventQueue收集生成的所有的事件。
搬掉了幾塊絆腳石,那麼現在我們就可以繼續前行了。osgViewer::viewer::eventTraversal()中第一個for循環的目的就是遍歷所有的設備所發生的事件,並保存到viewer::events中。這些設備就包含鼠標,鍵盤等發生的事件。

for(Devices::iterator eitr = _eventSources.begin();
eitr != _eventSources.end();
++eitr)
{
osgGA::Device* es = eitr->get();
if (es->getCapabilities() & osgGA::Device::RECEIVE_EVENTS)
es->checkEvents();

    // open question, will we need to reproject mouse coordinates into current view's coordinate frame as is down for GraphicsWindow provided events?
    // for now assume now and just get the events directly without any reprojection.
    es->getEventQueue()->takeEvents(events, cutOffTime);
}

然後再一個for循環,得到所有的GraphicsContext中的event並插入到eventQuene鏈表中,也就是諸如鼠標的移動,鍵盤上的按鍵被按下,窗口的尺寸被改變等動作,都會作爲一個新的 GUIEventAdapter 對象插入到鏈表中,插入事件的方法是由圖形窗口GraphicsWindow執行EventQueue類的成員函數 mouseMotion,keyPress和 windowResize,並間接地調用 EventQueue::addEvent 函數。而這些事件之間可能共通的參數和狀態就從“狀態事件”中讀取。然後我們再會對窗口上發生的點擊,釋放,拖拽,雙擊和移動事件中的鼠標座標進行統一的投影變換,使鼠標座標重新投影到當前視圖的座標系中。

現在我們就主要來講解一下鼠標座標到視圖座標系的轉換。
當鼠標只是進行單點操作,或者當然的事件的GraphicsContext不是主GraphicsContext時,需要調用generatePointerData函數來對鼠標的座標進行轉換。Viewer::generatePointerData()函數中,在這裏我們要普及一點知識osg或者說opengl中屏幕座標的原點在左下角,而windows的座標原點在右上角,所以在這個函數中我們首先需要把判斷我們所使用的平臺的原點和osg的原點是否相同,如果不同則需要把鼠標座標的y取反一下(gw->getTraits()->height - y)。然後把新的到的座標點設置回事件信息中,並把Y軸模式改爲向上增長(Y_INCREASING_UPWARDS).然後我們得到此時這個GraphicsContext下的所有的正在使用到的相機,並選出目前鼠標事件中的x,y所處在那幾個相機的視口中,得到這幾個相機作爲活動相機。然後根據相機的先後渲染順序進行排序,因爲我們最後渲染的肯定會覆蓋先前的,所以我們只需把鼠標座標投影到最後渲染的相機的視圖上就可以了。因爲視口的座標都是以0到1間的數字來表示的,所以鼠標的座標通過一定的線性變換就可以變換到視口座標系內

event.addPointerData(new osgGA::PointerData(camera, (x-viewport->x())/viewport->width()2.0f-1.0f, -1.0, 1.0,(y-viewport->y())/viewport->height()2.0f-1.0f, -1.0, 1.0));<br>
當然上面所說的適口座標肯定是主攝像機的視口座標,如果是目前鼠標是在從相機中移動的,那麼再轉換到主攝像機座標系中。這個過程大概可以理解成這樣,我們首先要把鼠標的座標按照從相機的MVP矩陣轉換到世界座標系中,再根據主相機的MVP矩陣把剛剛得到的世界座標轉換到主攝像機的視口中,最後完成了從相機到主相機的座標轉換。
歡迎大家來我的新家看一看 www.3wwang.cn

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