事件循环和更新循环

终于到了我们嘴里经常念叨的事件循环、更新循环以及渲染循环了。首先我们来区分一下事件循环和渲染循环,他们两个首先是两个不同顺序执行的过程,我们有时候会用到任意node的updateCallback函数,这个就是在更新循环的时候遍历所有的node来调用updateCallback函数的;而事件循环是与用户操作和操作系统事件想关联的,以及调用我们设置的事件回调(EventCallback)函数。而事件循环函数(viewer::eventTraversal())是我们现在要探究的内容。

osgViewer::Viewer::eventTraversal()

1
2
3
4
5
6
7
8
9
10
11
12
13
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;

然后的主要工作是:事件循环会得到已经发生的所有事件,并进行一定的筛选工作,最后全部都传给各自的事件处理器。所以我们首先对其中一些新成员进行简单的介绍:

      1. eventState事件队列的目前的状态事件,eventState的设置是通过osgGA::EventQuene::setCurrentEventState函数进行设置的。
      2. _eventSources 实在osgViewer::View下的成员变量,通过View::addDevice()函数来添加新的设备,他的主要作用就是在每一个帧的事件循环中便利所有的设备,然后得到通过Device :: getEventQueue收集生成的所有的事件。

搬掉了几块绊脚石,那么现在我们就可以继续前行了。osgViewer::viewer::eventTraversal()中第一个for循环的目的就是遍历所有的设备所发生的事件,并保存到viewer::events中。这些设备就包含鼠标,键盘等发生的事件。

1
2
3
4
5
6
7
8
9
10
11
12
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间的数字来表示的,所以鼠标的坐标通过一定的线性变换就可以变换到视口坐标系内

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矩阵把刚刚得到的世界坐标转换到主摄像机的视口中,最后完成了从相机到主相机的坐标转换。

欢迎大家来我的新家看一看 3wwang个人博客-记录走过的技术之路

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

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

    那我们就开始处理这些事件中得到的所有的交互事件,首先我们要判断这些事件是否包含osg的退出事件,那什么情况下会触发这个退出事件呢?如果您运行过osg中example中的小例子的,聪明的你一定就会发现当 ...

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

    VPM矩阵 1.V 表示摄像机的观察矩阵(View Matrix),它的作用是把对象从世界坐标系变换到摄像机坐标系.因此,对于世界坐标系下的坐标值 worldCoord(x0, y0, z0),如果希 ...

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

    上节总结 前几天我们大体上介绍完成了osg的事件循环的介绍,总结一下osg的时间循环主要就是得到平台(windows)的所有消息,并遍历所有的node的eventCallback,并对他们进行处理.接 ...

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

    _scene->updateSceneGraph(*_updateVisitor); 我们用了前面4节才刚刚算是完成对DatabasePager::DatabaseThread::run()函数 ...

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

    补充 当然细心的你会发现,_scene->updateSceneGraph(*_updateVisitor)中还有一个imagePager::UpdateSceneGraph()还没有进行讲解, ...

  6. 探索未知种族之osg类生物---呼吸分解之渲染遍历二

    那么今天我们就正式进入osg整个呼吸动作之中最复杂的一个动作,ViewerBase::renderingTraversals(),我们先介绍renderingTraversals的开头的简单的几步操作 ...

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

    回顾 我们用了两节的内容才堪堪讲解完ViewerBase::frame()函数中调用的realize()---Viewer:: realize()函数.我们简单的总结就是Viewer:: realiz ...

  8. 探索未知种族之osg类生物---呼吸分解之渲染遍历一

    总结 前面我们基本上已经完成对ViewerBase::frame()函数的探究,只剩下renderingTraversals()渲染遍历的探究,虽然就剩下了一个函数,但是这却是最重要的,不可少的一个步 ...

  9. 《探索未知种族之osg类生物》目录

    精力有限,博客园不在更新<探索未知种族之osg类生物>.在这里列出所有文章目录(持续更新)有兴趣的同学可以看看. 探索未知种族之osg类生物[目录] 前序 探索未知种族之osg类生物--- ...

随机推荐

  1. Java 中 == 和 equals 的区别

    有一段时间,== 和 equals 的区别一直困扰着我.因为涉及到Java的内存机制,然而Java的内存机制又是比较抽象的东西,所以对那时候的我来说,实在是很难理解. == 和 equals 最大的区 ...

  2. python基础学习 Day19 面向对象的三大特性之多态、封装 property的用法(1)

    一.课前内容回顾 继承作用:提高代码的重用性(要继承父类的子类都实现相同的方法:抽象类.接口) 继承解释:当你开始编写两个类的时候,出现了重复的代码,通过继承来简化代码,把重复的代码放在父类中. 单继 ...

  3. 关于CSS中的浮动

    在页面布局中我们经常会用到浮动的布局,浮动元素会影响到后面页面的布局效果.例如下面: .box{ background: yellow; float: left; } .box2{ width: 20 ...

  4. js高级-数组的map foreach 方法

    函数式编程  sort map forEach  ....  jQuery() 数组的sort 方法 传入一个匿名函数 就是函数式编程 ie9 以上的浏览器支持 map 方法 map方法 返回一个新数 ...

  5. vuex的几个细节

    vuex中的state值一般是不能再外面修改的,如果开发者外面修改store里面的值就失去其存在的意义了,这里需要其加属性如下所示: const isDev = process.env.NODE_EN ...

  6. Activty左出右进动画

    [Activty左出右进动画] public void overridePendingTransition (int enterAnim, int exitAnim) 其中: enterAnim 定义 ...

  7. fs.watchFile

    [fs.watchFile] fs.watchFile(filename[, options], listener) Watch for changes on filename. The callba ...

  8. lvm磁盘分区

    初始分区情况见下: 创建lvm类型磁盘 创建卷pv 添加pv到vg中,vg名vgroup0 创建lv lvcreate -L 2g -n zookeeper vgroup0 在vg vgroup0中创 ...

  9. Java 读写Properties配置文件(转)

    转自:http://www.cnblogs.com/xudong-bupt/p/3758136.html

  10. 【scrapy】其他问题

    今天看<python爬虫开发与项目实践>的17章写代码的时候发现,一个方法的结尾带了红色波浪线: def _process_booklist_item(self,item): ''' 处理 ...