|
当前位置:osgViewer/Viewer.cpp第577行,osgViewer::Viewer::eventTraversal()
新的工作开展了,首先我们要使用Viewer::getContexts函数找到视景器中所有已有的GraphicsWindow图形窗口,然后执行GraphicsWindowWin32::checkEvents函数(为什么直接指向了GraphicsWindowWin32的实例,请参阅第四日的内容,还有我们假设是在Win32平台下进行分析),看看这个函数的内容……熟悉Win32 SDK编程的朋友一定又大喜过望了,TranslateMessage,DispatchMessage!多么熟悉的消息传递函数啊,而它们的工作当然也很明确:通知Windows执行窗口的消息回调函数,进而执行用户交互和系统消息的检查函数GraphicsWindowWin32::handleNativeWindowingEvent。而这个函数的作用在第六日就已经提过,它负责把WM_*消息转化并传递给osgGA::EventQueue消息队列。怎么样,其实OSG与操作系统API还真是“千里姻缘一线牵”呢,如下图所示:
附图1
之后,使用EventQueue::takeEvents函数,把当前GraphicsWindow图形窗口对象gw的事件队列保存到指定的变量gw_events中,代码如下:- osgGA::EventQueue::Events gw_events;
- gw->getEventQueue()->takeEvents(gw_events);
复制代码 注意这个takeEvents函数除了将所有的事件取出之外,还负责清空事件队列,这样下一帧到来后我们就不必再重复处理旧的事件了。
下一步,遍历刚刚所得的所有事件(天啊,到底要做什么),对于每一个GUIEventAdapter事件对象event:
1、首先处理Y轴的方向问题,通常的GUI窗口系统都会将屏幕左上角定义为(0, 0),右下角定义为(Xmax, Ymax),即GUIEventAdapter::Y_INCREASING_DOWNWARDS宏的含义,此时由GUI系统传回到OSG的坐标位置也是符合这一坐标系的;但是OSG的视口坐标系定义为左下角(0, 0),右上角(Xmax, Ymax),也就是Y_INCREASING_DOWNWARDS。此时,有必要对每个event对象的鼠标坐标值做一步转换。
2、对于符合下列条件的摄像机,设置为“焦点摄像机”(Camera with Focus),即第二日悬疑列表中的_cameraWithFocus:
(1)鼠标移动(而非拖动)时,鼠标进入了摄像机的视口范围之内;
(2)摄像机是对应该视口的,允许接收事件(Camera::getAllowEventFocus),且“渲染目标实现方式”(Render Target Implementation)为Camera::FRAME_BUFFER……
又有新问题了,什么是“渲染目标实现方式”?先放到这里,以后我们再深究。
现在我们终于有了“焦点摄像机”,也就是鼠标当前位置所激活的摄像机窗口。不过这并未打消我们的疑问:这个焦点摄像机可以用来做什么呢?继续看下面的代码:- osg::Viewport* viewport = getCameraWithFocus()->getViewport();
- osg::Matrix localCameraVPW = getCameraWithFocus()->getViewMatrix() *
- getCameraWithFocus()->getProjectionMatrix();
- if (viewport) localCameraVPW *= viewport->computeWindowMatrix();
- osg::Matrix matrix( osg::Matrix::inverse(localCameraVPW) * masterCameraVPW );
- osg::Vec3d new_coord = osg::Vec3d(x,y,0.0) * matrix;
- x = new_coord.x();
- y = new_coord.y();
- ……
复制代码 有了上一日的经验,现在我们不再惧怕那个VPW矩阵了。毫无疑问,这段代码中首先得到了焦点摄像机的VPW矩阵localCameraVPW。由于已知鼠标在屏幕坐标中的位置(x, y),又已知主摄像机的masterCameraVPW和焦点摄像机的localCameraVPW,根据:并结合上面的代码可以判断出,new_coord实际上是焦点摄像机中的(x, y)经过转换之后,在主摄像机视口中的坐标位置,而这个位置被重新写入所有事件的setX,setY函数。也就是说,当我们在重写后的GUIEventHandler::handle函数中使用ea.getX和ea.getY获取鼠标位置时,这个位置很可能已经经过了一次换算,并非实际的像素位置!
感兴趣的读者不妨改写一下osgcamera例子,派生一个新的GUIEventHandler事件处理器并在其中读取鼠标坐标值。当我们使用以下参数启动osgcamera时:可以颇为惊异地发现,所得到的坐标范围在(-1, -1)到(1, 1)之间!因为osgcamera有可能不建立主摄像机视口(参见第二日的内容),这无疑造成了我们不希望看到的结果。也许这个让人有些困惑(也可能对您来说正好提供了方便)的转换会在以后得到更合理的改进;当然,您也可以自己即刻手动重写eventTraversal函数,获取您真正想要的坐标值。
当前位置:osgViewer/Viewer.cpp第697行,osgViewer::Viewer::eventTraversal()
在结束一日的征程之前,我们再看一看OSG针对单一窗口的关闭做了什么处理:- bool wasThreading = areThreadsRunning();
- if (wasThreading) stopThreading();
- gw->close();
- if (wasThreading) startThreading();
复制代码 当我们选择关闭一个GraphicsWindow窗口gw时,OSG系统必须首先尝试终止所有的渲染线程(包括各个图形设备开启的线程和渲染器开启的线程,参见第五日),,关闭窗口之后再打开所有的渲染线程。事实上,当我们试图在运行时开启一个新的OSG图形窗口时,也必须使用相同的线程控制步骤,即,关闭线程,创建新渲染窗口,开启线程。否则很可能造成系统的崩溃(这同样涉及到OSG的多线程机制,目前它还在悬疑列表中沉睡)。
在这个大型循环体(577-724行)的最后,所有来自图形窗口和视景器的事件都被添加到一个std::list链表当中,下一步我们可以统一处理这些交互事件了。
解读成果:
osgViewer::GraphicsWindowWin32::checkEvents,osgGA::EventQueue::takeEvents。 悬疑列表:
如何调度和实现OSG的多线程机制?什么是“渲染目标实现方式”?
[ 本帖最后由 array 于 2008-8-21 13:21 编辑 ] |
-
附图1
|