第4节 操作器和Trackball
文章發布之后,大家有很多疑問,我發現有些很基礎。要求我再聊細一些,我就再重頭來再比較細致的寫一寫。雖然不需要大家看代碼,但是可能在講代碼的時候大家還是希望能有對照,作者使用的是3.6.5版本,我已經上傳到了百度網盤里。大家可以通過如下方式下載:
【擊此打開網盤資源鏈接】
對于只有三行的最簡單的osg程序:
osgViewer::Viewer viewer;viewer.setSceneData(osgDB::readNodeFile("glider.osg"));return viewer.run();我們重新回到Viewer::run()這個函數里,這個函數的調用是如下這樣子的:
int Viewer::run() {if (!getCameraManipulator() && getCamera()->getAllowEventFocus()){setCameraManipulator(new osgGA::TrackballManipulator());}setReleaseContextAtEndOfFrameHint(false);return ViewerBase::run(); }從字面上我們很容易理解,首先判斷當前viewer是不是設置了操作器,然后再看相機是不是允許獲取焦點,假如不允許獲取焦點,那鼠標鍵盤都點不上去,是不需要操作器的。
那么getCameraManipulator()我們知道我們沒有調用過setCameraManipulator所以這個得到的是null。但是getCamera()不會返回空嗎?答案是不會的,這個camera的申請階段是在osg::View的構造函數里osgViewer::Viewer派生自osg::View因此自然構造。初始化的代碼在osg/view.cpp的第27~40行,如下:
View::View():Object(true) { ..._camera = new osg::Camera;_camera->setView(this);double height = osg::DisplaySettings::instance()->getScreenHeight();double width = osg::DisplaySettings::instance()->getScreenWidth();double distance = osg::DisplaySettings::instance()->getScreenDistance();double vfov = osg::RadiansToDegrees(atan2(height/2.0f,distance)*2.0);_camera->setProjectionMatrixAsPerspective( vfov, width/height, 1.0f,10000.0f);_camera->setClearColor(osg::Vec4f(0.2f, 0.2f, 0.4f, 1.0f));osg::StateSet* stateset = _camera->getOrCreateStateSet();stateset->setGlobalDefaults(); ... }沒有操作器的話就會調用setCameraManipulator(new osgGA::TrackballManipulator());設置Trackball了,那么操作器是如何在場景中起到拖拖拽拽起作用的呢?以及Trackball是怎么回事兒呢?怎么就場景上來就會在屏幕的中間,一拖就會走呢?下面來一一解答。
操作器怎么起作用的?
所有的操作器都派生自osgGA::CameraManipulator,而這個CameraManipulator又派生自osgGA::GUIEventHandler,可見其本質上是個事件處理的類。因此它首先會接收事件,比如鼠標一拖,場景就動。場景動與不動是受視點的位置、朝向來決定的,也就是觀察矩陣,因此CameraManipulator必有處理事件的接口和輸出矩陣的接口,處理事件的接口是函數bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter &aa);所以每個操作器必須要對這個函數大書特書來達到按自己的操作來達成接口的目的。這個handle的被調用的地方是在一幀繪制的事件遍歷階段,是在void Viewer::eventTraversal()中,viewer.cpp的第1126行:_cameraManipulator->handle( event, 0, _eventVisitor.get());雖然入參不一樣,但是跟蹤一下會發現會調用過去的。
事件處理之后,就要根據處理的事件改變視點的位置來達到操作場景的目的。這個是通過osgGA::CameraManipulator的函數virtual osg::Matrixd getInverseMatrix()來實現的,它的調用時機是在更新階段void Viewer::updateTraversal()中,第1213行:_cameraManipulator->updateCamera(*_camera);它會調用至:camera.setViewMatrix(getInverseMatrix());這個時候就有人不明白了,為什么要使用逆矩陣呢?原因這里有點繞,大家認真理解一下,世界坐標下,一個盒子在A點(xa,ya,za),視點在B點(xb,yb,zb),先不管朝向不朝向的。我們要輸出此時在B點的人看到A點的圖像,做法有很多,實際上圖形學界是這么做的,把A點先轉換為以B點為局部坐標的坐標系下,然后再進行一系列操作。這個變換的過程其實相當于需要求出把B點移回世界坐標原點的矩陣,A點再乘以這個矩陣就可以,這個移回的矩陣是這樣計算的,把人從世界坐標原點移到B點的變換是Matrix,而把B點移回世界坐標原點的變換就是InverseMatrix,現在我們要把A點放在以B點為局部坐標下,則需要變換的就是InverseMatrix。有點繞,但是明白這一點還是很重要的,屬于基本功。
Trackball是如何上來就看著物體的
Trackball的第一個特點是上來就會看著場景的物體,這是怎么實現的呢?首先setCameraManipulator其實有兩個參數:void setCameraManipulator(osgGA::CameraManipulator* manipulator, bool resetPosition = true);第二個參數默認是true,意思就是初始時要設置到home的位置,其實是調用home函數,看看這個函數的代碼:
而home是如何定位到正好看見物體的呢?不大也不小,不近也不遠的。在home這個函數中,它調用了virtual void computeHomePosition(const osg::Camera *camera = NULL, bool useBoundingBox = false);來計算了一下home的位置,其計算方法也很一目了然,先計算包圍盒,然后再調用setHomePosition根據包圍盒的中心點和半徑來設置視點的位置和朝向。virtual void setHomePosition(const osg::Vec3d& eye, const osg::Vec3d& center, const osg::Vec3d& up, bool autoComputeHomePosition=false)
Trackball是如何實現鼠標慣性操作的?
Trackball有兩個特征:
1、鼠標按下,拖動,停下,鼠標彈起。則場景轉動,在鼠標彈起時不轉了。
2、鼠標按下,甩出來,則場景一直轉動。
這兩個的差別關鍵在鼠標的彈起操作,一個是停下彈,一個是甩彈。這個是在這里判斷的,bool StandardManipulator::handleMouseRelease( const GUIEventAdapter& ea, GUIActionAdapter& us )是操作器的鼠標彈起事件:這里的關鍵是鼠標彈起的時候和上一個鼠標移動的時候的時間差>0.02秒,則認為是情況1,小于等于0.02秒認為是甩。
bool StandardManipulator::handleMouseRelease( const GUIEventAdapter& ea, GUIActionAdapter& us ) {//判斷是DRAG,而不是MOVE,DRAG是有鍵按下了,Mask=0if( ea.getButtonMask() == 0 ){double timeSinceLastRecordEvent = _ga_t0.valid() ? (ea.getTime() - _ga_t0->getTime()) : DBL_MAX;//判斷上一個事件和當前事件的時間差,小于0.02秒可認為是甩if( timeSinceLastRecordEvent > 0.02 )flushMouseEventStack();if( isMouseMoving() ){if( performMovement() && _allowThrow ){us.requestRedraw();us.requestContinuousUpdate( true );_thrown = true;}return true;}}flushMouseEventStack();addMouseEvent( ea );if( performMovement() )us.requestRedraw();us.requestContinuousUpdate( false );_thrown = false;return true; }總結
以上是生活随笔為你收集整理的第4节 操作器和Trackball的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 精准授时,GPS北斗卫星授时同步时钟系统
- 下一篇: 笔记本计算机配置型号,笔记本怎么看配置,