探索未知种族之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

原文地址:https://blog.51cto.com/9302896/2356337

时间: 2024-07-31 02:57:44

探索未知种族之osg类生物---呼吸分解之事件循环一的相关文章

探索未知种族之osg类生物---呼吸分解之事件循环三

那我们就开始处理这些事件中得到的所有的交互事件,首先我们要判断这些事件是否包含osg的退出事件,那什么情况下会触发这个退出事件呢?如果您运行过osg中example中的小例子的,聪明的你一定就会发现当按下esc时就会退出osg.所以osg中默认的退出事件就是由esc触发的.当然我们也可以通过ViewerBase::setQuitEventSetsDone 设置是否允许按下某个键之后直接退出这种做法, 同时还可以使用另一个函数 ViewerBase::setKeyEventSetsDone 来设置

探索未知种族之osg类生物---呼吸分解之事件循环二

VPM矩阵1.V 表示摄像机的观察矩阵(View Matrix),它的作用是把对象从世界坐标系变换到摄像机坐标系.因此,对于世界坐标系下的坐标值 worldCoord(x0, y0, z0),如果希望使用观察矩阵 VM 将其变换为摄像机相对坐标系下的坐标值 localCoord(x', y', z'),则有: localCoord = worldCoord * VM 此外,观察矩阵可以理解为"摄像机在世界坐标系下的变换矩阵的逆矩阵",因此 Camera类也专门提供了 getInvers

探索未知种族之osg类生物---呼吸分解之更新循环二

_scene->updateSceneGraph(*_updateVisitor); 我们用了前面4节才刚刚算是完成对DatabasePager::DatabaseThread::run()函数的探究,也就是了解了osg究竟是怎么完成对数据的加载的.那么我们现在要回到DatabasePager::updateSceneGraph的工作中,它是在osgViewer::Viewer:: updateTraversal()函数中遇到的 _scene->updateSceneGraph(*_updat

探索未知种族之osg类生物---呼吸分解之advance

回顾我们用了两节的内容才堪堪讲解完ViewerBase::frame()函数中调用的realize()---Viewer:: realize()函数.我们简单的总结就是Viewer:: realize()主要是使GraphicsContext处于可用状态,并且启动相关的图形线程. ViewerBase::frame()函数解读到这里,我们完成了osg生物第一次尝试呼吸所需要的所有器官的初始化工作.下面就真正的开始进入osg呼吸动作的研究了.也就意味着我们真是进入osg的仿真循环的研究当中.那我们

[转][osg]探索未知种族之osg类生物【目录】

作者:3wwang 原文链接:http://www.3wwang.cn/html/article_58.html 前序 探索未知种族之osg类生物---起源 ViewBase::frame函数中的ViewerInit()及realize() 探索未知种族之osg类生物---器官初始化一 探索未知种族之osg类生物---器官初始化二 探索未知种族之osg类生物---器官初始化三 探索未知种族之osg类生物---器官初始化四 ViewBase::frame函数中的advance() 探索未知种族之o

探索未知种族之osg类生物---状态树与渲染树以及节点树之间的关系

节点树 首先我们来看一个场景构建的实例,并通过它来了解一下“状态节点”StateGraph 和“渲染叶”RenderLeaf 所构成的状态树,“渲染台”RenderStage 和“渲染元”RenderBin 所构成的渲染树,进一步了解这两棵树之间错综复杂的关系,以及理解它们与场景节点树之间更加复杂的关系. 上面是一个虚构的场景结构图,其中叶节点_geode3,以及所有六个几何对象均设置了关联的渲染状 态集(StateSet),且几何体 1 和几何体 2 共享了同一个 StateSet(ss11(

探索未知种族之osg类生物---起源

任何程序都是有生命的,是生命就需要呼吸.例如普通的windows程序,当运行完main()函数后,就需要进入消息循环,来监听用户的各种操作,以便做出及时的回应.这样的每次循环就像生命的每次呼吸,来维持生命体征. osg的程序不仅仅需要消息循环来监听用户的鼠标.键盘等操作,同时也得具备了渲染循环.当然随着我们的对osg的深入了解会发现,osg的事件监听和渲染循环是串行的.但是当我们把osg与MFC(QT)等结合时,相应UI上的鼠标,键盘事件的同时也要兼顾可能发生在osg中的效果,所以一般的osg程

探索未知种族之osg类生物---器官初始化一

我们把ViewerBase::frame()比作osg这类生物的肺,首先我们先来大概的看一下'肺'长什么样子,有哪几部分组成.在这之前得对一些固定的零件进行说明,例如_done代表osg的viewer是否被删除释放内存:_firstFrame代表是否是第一次进入frame函数.那么接下来我们会发现frame函数表面上组成结构非常简单,逻辑上也非常的清晰---先判断当前的viewer是否被删除,也就是判断是否died,如果已经died,那么肺的功能就不会进行.然后判断这个osg小孩是否刚刚出生,是

探索未知种族之osg类生物---器官初始化二

那我们回到ViewerBase::frame函数中来,继续看看为什么osg生命刚刚出生的时候会大哭,除了初始化了eventQuene和cameraManipulator之外还对那些器官进行了初始化.在这之前我们先介绍一下上一节说到的osg的肢体或者器官但是没有展开介绍的. 前言osgGA::GUIEventAdapter,GUI事件适配器.它就是对所有平台windows linux mac平台上的鼠标.键盘.以及其他的窗口事件进行了封装,目的是使接口统一,用户在使用osg库的时候不用再自己区分平