事件循环和更新循环

终于到了我们嘴里经常念叨的事件循环、更新循环以及渲染循环了。首先我们来区分一下事件循环和渲染循环,他们两个首先是两个不同顺序执行的过程,我们有时候会用到任意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. ADO.Net创建数据模型和数据访问类及泛型集合

    数据模型和数据访问类:数据模型: 使用面向对象中的封装特性,将数据表中的行数据组成一个同样结构的对象,来单独使用: 数据访问类: 将某一个表的全部增删改查操作的方法写进去,方便统一管理和调用: 数据模 ...

  2. php 指定页面显示所有报错

    ini_set('display_errors',1); error_reporting(E_ALL);

  3. lcd 控制器

    1. 使用lcd 一般需要一个控制器和驱动器,控制器需要初始化以产生正确的时序,驱动器一般是和lcd基板制作在一起. LCD 控制器结构图: REGBANK 表示调色板 LCDDMA 表示DMA通道 ...

  4. input 文本框,对中文长度校验

    在项目中,经常会遇到,对文本框进行校验. eg.  要求姓名长度为20,中文为10,只能输入中英文. <input   maxlength="20" type="t ...

  5. vc for python2.7

    https://www.microsoft.com/en-us/download/confirmation.aspx?id=44266

  6. windows环境安装zabbix客户端

    安装windows版zabbix客户端 一.下载客户端 在数据盘新建一个zabbix_agent目录 浏览器打开 https://www.zabbix.com/download_agents 选择”z ...

  7. Log4j2配置及使用

    Log4j2:一个日志管理工具.Log4j的升级版,需要Java6以上   一.安装log4j2依赖包 1.通过maven的pom.xml直接引入jar: log4j-api和log4j-core & ...

  8. JPA in

    CriteriaBuilder.In in = criteriaBuilder.in(root.get("field1")); for (String str : arr) { i ...

  9. js keycode

    参考 https://www.cnblogs.com/daysme/p/6272570.html

  10. 【Nodejs】ExpressのRequestとResponseの内容

    Request 对象 - request 对象表示 HTTP 请求,包含了请求查询字符串,参数,内容,HTTP 头部等属性.常见属性有: req.app:当callback为外部文件时,用req.ap ...