array 发表于 2008-8-21 13:20:07

OSG原创教程:最长的一帧(8)

当前位置: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,根据:视口坐标 = 世界坐标 * VPW并结合上面的代码可以判断出,new_coord实际上是焦点摄像机中的(x, y)经过转换之后,在主摄像机视口中的坐标位置,而这个位置被重新写入所有事件的setX,setY函数。也就是说,当我们在重写后的GUIEventHandler::handle函数中使用ea.getX和ea.getY获取鼠标位置时,这个位置很可能已经经过了一次换算,并非实际的像素位置!

感兴趣的读者不妨改写一下osgcamera例子,派生一个新的GUIEventHandler事件处理器并在其中读取鼠标坐标值。当我们使用以下参数启动osgcamera时:osgcamera.exe -1可以颇为惊异地发现,所得到的坐标范围在(-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 编辑 ]

hahahaha 发表于 2008-8-21 19:00:18

继续支持

blindpoint 发表于 2008-8-22 08:41:57

学习

hcg_me 发表于 2008-8-28 15:30:51

为什么关闭渲染窗口之后( stopThreading() ),还要再开启所有的渲染线程?

array 发表于 2008-8-28 15:50:33

原帖由 hcg_me 于 2008-8-28 15:30 发表 http://bbs.osgchina.org/images/common/back.gif
为什么关闭渲染窗口之后( stopThreading() ),还要再开启所有的渲染线程?

stopThreading是暂停所有的线程,而非关闭渲染窗口。
startThreading是重新开启渲染线程。

如果没有暂停线程就添加/删除窗口、视图或摄像机的话,对于多线程方式的渲染遍历将可能造成崩溃。但是不重新开启渲染线程的话渲染工作就没办法进行

shengrendan2 发表于 2008-9-27 13:51:46

好;
赞一个;

forest37 发表于 2008-12-2 16:26:47

请问在窗口最小化时,OSG的线程被阻塞了没啊

to myself:线程没有被阻塞,只是场景里没有要绘制的内容,从而性能上不会影响其他窗口

[ 本帖最后由 forest37 于 2008-12-2 17:26 编辑 ]
页: [1]
查看完整版本: OSG原创教程:最长的一帧(8)