虚拟仿真引擎消息机制



1、虚拟仿真引擎和游戏引擎在消息机制方面的异同

虚拟仿真引擎与游戏引擎极为相似,但又有其不同之处。

游戏引擎重在游戏体验。所以60-120HZ的画面刷新率和事件刷新率依旧足够。但虚拟仿真引擎不但须要仿真体验。更须要更快速度的消息机制。试想在动作捕捉的应用中,动作捕捉设备的刷新率的典型值是120HZ,或者是须要记录一个设备的运动轨迹,也须要更高的刷新率。可是在中等以上复杂度的渲染中根本无法达到120HZ,同一时候假设须要进行立体渲染,能够预见渲染速率是不会超过60HZ的,由于超过60HZ的立体渲染已经超过了显示设备的极限。

所以虚拟仿真系统须要更为快速的消息机制。这也就是为什么VRPN的标准server的刷新率是1000HZ。典型的渲染循环刷新率在30-60HZ之间。这就造成了一个巨大的不平衡:渲染的低速和操作的快速。正确的方式应该是操作消息可以以1000HZ以上的刷新率在引擎中存在,因此必须为操作消息提供一个新的线程。

而消息管理器之所以定义为1000HZ是由于可以简单的使用Sleep(1)来节省大量的CPU开销。这样消息线程的CPU消耗率会减少到单核的1%以内。假设须要更高的速度。仅仅须要取消等待,或者使用其它的等待方式。则几万、几十万的刷新率都是可实现的。

2、为什么须要一个通用的消息管理器

并非全部的模块都必须用到Delta3D的数据类型和各种对象。假设为我们的每个模块都增加这么一个限制是不好的。比方我须要我的矩阵能进行一个特别的变换。Delta3D中的矩阵并不具备这个功能。这个时候,我不应该去重写Delta3D

的矩阵类,由于这样会造成非常大的改变。以及须要传递的消息。不应该是复杂的已经定义的类型。而应该是简单的每个模块都能解析的数据。这样,每个模块才干更简单的获得所须要的数据。

还有一方面,在渲染一个场景时,假设已经显示了场景内的UI。则场景内的其它操作都应该被停止。

这时,假设没有消息管理器,就必须更改场景的状态,更新各个节点或者相机管理器告诉他们。你们临时不要动。而假设消息管理器具有优先级的概念。UI

当前获取了这个消息,而且已经处理。返回该消息已经被处理,则该消息不再继续传递,这样就避免了非常多无谓的状态更改。

3、怎样使用消息管理器获得数据

看到消息管理器有那么多的功能,必定会想到使用消息管理器会不会非常复杂,须要非常多的步骤。实际上使用消息管理器获得数据是非常easy的事情。仅仅须要继承一个接口,并实现两个函数就可以。

须要继承的接口是IEventListener,它具有两个方法须要被实现:OnEvent和GetListenerOption。以下是IEventListener的代码:

enum EPriority

{

RealTime,

High,

AboveNormal,

Normal,

BelowNormal,

Idle

};

struct SListenerOpt

{

int mRegMessages; 
// 侦听消息的类型

EPriority mPriority; 
// 消息优先级

};

struct IEventListener

{

IEventListener()

{ //
向消息管理器注冊此侦听器的代码 }

virtual SListenerOptGetListenerOption() = 0;

virtual boolOnEvent(EEventType event, void* eventDat) = 0;

//
多线程支持
内部在OnEvent之前会检查是否被Lock
假设Lock则跳过

void Lock(); 
// 当你须要读取消息相关数据时调用,则消息管理器暂不更新当前侦听器的数据

void Unlock(); 
// 当数据读取完成时调用,消息管理器能够更新其数据。

};

EEventType是返回的消息类型,将在下一节中实现。

典型的实现方式为:

class CCameraManager : public IEventListener

{

//Members

//Functions

SListenerOptmListenerOpt; 
//在构造函数中定义或实时改动获取消息的优先级以及获取的消息类型

virtual SListenerOptGetListenerOption() { return mListenerOpt; }

virtual boolOnEvent(EEventType event, void* eventDat)

{

switch(event)

//将数据放入正确的位置

return //假设消息不再传递返回true;

//接收事件的实质是阻止此事件的继续发送。需配合优先级慎重使用

}

}

4、怎样实现消息管理器

消息管理器管理各个消息侦听者所能获得的消息以及其权限。消息管理器中消息能够分为多类,典型的有:输入消息、UI消息、引擎消息和物理消息等。首先看一下消息类型的定义。消息类型以BitMask的形式定义:

#define MESSAGE_TYPE_INPUT           
0x00000001

#define MESSAGE_TYPE_UI                  
0x00000002

#define MESSAGE_TYPE_ENGINE         
0x00000004

#define MESSAGE_TYPE_PHYSICS        
0x00000008

#define MESSAGE_TYPE_COUNT         
0x00000004

假设须要接受特定类型的数据则在消息侦听器的mListenerOpt中的mRegMessages中使用这些定义如:mListenerOpt.
mRegMessages= MESSAGE_TYPE_INPUT | MESSAGE_TYPE_UI;这样。这个侦听器就能够侦听输入消息和UI的消息了。

相同,假设改动了mListenerOpt.
mPriority则消息的接收权限会更改。这两个侦听器的属性能够配置为固定或者实时更改,能够依据须要进行配置。

消息管理器是一个引擎中很重要的部分。它就像一个交通枢纽。让各个部分可以高速的交换数据。

以下就来描写叙述一下消息管理器的实现和使用。

它须要保存2个列表:

  1. 全部已经注冊的侦听器的列表
  2. 每一个优先级的侦听器的列表

已经注冊的侦听器列表能够方便的便利全部的侦听器查看他们的状态改变。优先级列表能够保证高优先级的侦听器领先被赋予数据。

以下是消息管理器的简单实现:

class CMessageManager

{

public:

CMessageManager() { //
初始化并启动消息管理器线程 }

~ CMessageManager() { //
销毁列表并结束消息管理线程 }

void MainLoop(); 
// 检查输入设备状态
发送消息

void CheckOpt(); 
// 检查每一个侦听器的状态
优先级改变后改变所在的列表

voidPostMessage(EEventType event, void* eventDat); //
其它模块发送的消息,多线程支持

}

这样就实现了一个简单的消息管理器。仅仅须要在引擎初始化的最開始建立一个它的新实例,而且在引擎结束时销毁它就能够了。侦听器会在被创建时自己主动注冊到这个消息管理器。消息管理器内部以1000HZ或更高的速度刷新数据,并发送到各个须要这些消息的模块。典型的消息处理流程将在第七节进行描写叙述。

以下说一下消息的类型,一个引擎中有非常多种的消息类型。但能够预见的是总消息类型基本不会超过一个32位整数所表达的最大数值。消息的分类应该不会超过1024种,单种类型的消息其细分量不会大于32
* 65536 = 2097152种,所以消息类型的定义例如以下:

#define EVENT_TYPE_BASE_INPUT
     0x00000000

#define EVENT_TYPE_BASE_UI            
0x00200000

#define EVENT_TYPE_BASE_ENGINE   
0x00400000

#define EVENT_TYPE_BASE_ PHYSICS  
0x00600000

enum EEventType

{

EVENT_INPUT_MENU_BUTTON= EVENT_TYPE_BASE_INPUT,

EVENT_INPUT_XX_XX,

EVENT_UI_OPEN_MAINPAGE=EVENT_TYPE_BASE_UI,

EVENT_UI_XX_XX,

EVENT_ENGINE_INIT_BEGIN= EVENT_TYPE_BASE_ENGINE,

EVENT_ENGINE_XX_XX,

EVENT_ PHYSICS_CREATE_BOX= EVENT_TYPE_BASE_ PHYSICS,

EVENT_ PHYSICS_XX_XX,

}

消息数据定义为何使用void*。首先它能够传递不论什么指针信息,所以使用它不会出现无法传送的消息。假设使用实例来进行传输如:VEC3、MAT4之类,每次的消息发送都会调用其构造函数并进行赋值。这样对性能是巨大的损失,所以选择使用指针方式进行传输数据,假设此数据是实用的。则由OnEvent中实现的代码来解析出对本模块实用的数据并保存。

5、怎样向消息管理器发送消息

无论当前的代码处于哪个线程。哪个模块,都能随时向消息管理器发送消息,这些消息将实时的被传送到须要这些消息的各个模块其中去。

当一个模块想要发送一个消息时,仅须要调用消息管理器的PostMessage方法就可以,标注好消息类型。构造好消息数据,就行将数据发送出去了。

6、典型的消息处理流程

本节将描写叙述一个按键从按下到触发UI或引起场景内改变的一系列过程。首先从按键按下開始说起。

当键盘、鼠标或者不论什么VR设备的button按下时,VRPN的server将获得这些数据并将其广播出来,VRPN的client接受到这些消息后,调用其按键回调来将这些数据发出。每个VRPN的client都是一个设备的实例,所以须要定义一个设备的类来完毕这个步骤,当然还须要一个设备管理器来刷新这些设备。这些类都已经实现,临时仅仅讲述设备类:

class CDevice

{

std::stringmDeviceName;

int mDeviceID;

intmTrackCount, mButtonCount, mAnalogCount;

vrpn_Tracker_Remote*mTrackers;

vrpn_Button_Remote*mButtons;

vrpn_Analog_Remot*mAnalogs;

// 构造和解析函数

voidUpdate(); // 刷新VRPNclient。向消息管理器发出消息,内容有设备ID、改变的类型、改变的数据。

}

如今通过调用每一个设备的刷新函数,按键消息已经发送到了消息管理器,发送的消息类型是MESSAGE_TYPE_INPUT。如今如果消息管理器中有三维场景中的UI和Engine两个模块在侦听Input类型的数据,而且UI的优先级较高。

消息管理器刷新时。发现有一个按键的状态改变的事件,这时首先遍历全部的侦听器,查看是否有侦听器的优先级、侦听事件发生了改变。假设改变了。则移动到新的优先级列表中。然后依据优先级,逐一向侦听器发送此消息,一旦某个侦听器返回了消息已经被处理,则跳出此消息的发送。

void CMessageManager::MainLoop()

{

// 检查优先级并改变列表

void CheckOpt();

for (每一个优先级)

for(每一个优先级的每一个侦听器)

侦听器. OnEvent

假设返回true,则处理下一条消息

}

此时在较高的优先级将消息发送给了UI,如果UI处于激活状态,则UI会在OnEvent处返回true,接受了这个按键按下的事件。反之。UI不接收这个事件,稍后会被Engine来接收到,并在场景中做出对应的改变。

继续说UI激活的状态,UI获得了这个消息。并将其存储在侦听器的某个变量内。UI更新线程发现此变量的改变,然后将查看它详细改变了哪些内容。

如果这个button是在场景中添加一个球体,则UI处理后,会使用PostMessage向消息管理器发送添加一个球体的消息,这次消息管理器的循环中。发现UI不接收这个消息。所以消息被传送给了Engine,然后Engine接收这个消息,而且在三维场景中添加一个球体。

这样一个典型的消息处理流程就显现出来了。不论什么模块都可以使用这个模型来接收、处理、发出消息,而且是在多线程的情况下,互不干扰的完毕。

7、从消息管理器看到的

一个功能完备的引擎就像是一台汽车或者是一个团队。每一个部件或者成员必须通力合作才干真正的完毕想要完毕的工作。

试想假设每一个人接到信息就先揣在兜里。然后在想告诉别人的时候再去告诉别人,这应该不是一个正确的信息通路。由于信息的传递可能会由于主观因素而变了味道。

引擎中就是使用一个部件接受了这些数据。然后在传递给别的部件,这样有可能会由于部件设计中没有完备的考虑到数据的全部情况而导致数据传递失真。相同会导致其它部件出现不应出现的毛病。而这种毛病是非常难定位的。

当前是信息社会,信息的传递被觉得是非常重要的因素,而引擎也可以看作一个团体,花费不多的时间为引擎加入一个可靠的消息传导机制我觉得是非常有必要的。就像一个团队也须要一个人可以把消息准确的传达给团队里的每个人,而不是感觉须要的时候再告诉别人。

二来。守旧思想实在是阻碍新技术的绊脚石。前段时间推行CMake管理项目的时候,我也感觉不习惯,不想要去更改如今已经习惯的东西。只是随着对它的使用,慢慢发现这是个非常好的东西。如今大家年纪都还不大。还在学习和积累的阶段,接触的事情还非常少。假设如今就有守旧的思想,实在是会影响自己的进步。所以在接触一个新事物时。一定要注意不要让守旧思想、经验主义束缚了自己的思想,而不去接受新事物。

版权声明:本文博客原创文章,博客,未经同意,不得转载。

时间: 2024-08-10 13:40:29

虚拟仿真引擎消息机制的相关文章

虚拟仿真引擎中的消息机制

 1.虚拟仿真引擎和游戏引擎在消息机制方面的异同 虚拟仿真引擎与游戏引擎极为相似,但又有其不同之处.游戏引擎重在游戏体验,所以60-120HZ的画面刷新率和事件刷新率依然足够.但虚拟仿真引擎不但需要仿真体验,更需要更高速度的消息机制.试想在动作捕捉的应用中,动作捕捉设备的刷新率的典型值是120HZ,或者是需要记录一个设备的运动轨迹,也需要更高的刷新率.但是在中等以上复杂度的渲染中根本无法达到120HZ,同时如果需要进行立体渲染,可以预见渲染速率是不会超过60HZ的,因为超过60HZ的立体渲染

CCBPM工作流引擎的消息机制与设计

keyword:ccflowjflow 消息机制流程引擎 自己主动发送短信 发送邮件 发送消息 流程引擎微信连接 消息接口 关于ccbpm: 我们把ccflow jflow两个版本号的工作流引擎统称为ccbpm. 工作流引擎的消息产生:在发送.抄送.退回.转发.加签.删除等等操作过程中.须要对当时人进行提醒,而且在设置提醒的情况下,就会产生消息. Ccbpm的消息删除机制:对于已经过期无意义的提示.ccbpm就会删除,比方:a节点发送到b节点有n(n>=1)个人能够处理,这个时间假设设置了消息提

MFC消息机制

何谓消息.消息处理函数.消息映射?消息简单的说就是指通过输入设备向程序发出指令要执行某个操作.具体的某个操作是你的一系列代码.称为消息处理函数. 在SDK中消息其实非常容易理解,当窗口建立后便会有一个函数(窗口处理函数)开始执行一个消息循环,我们还可以清楚的看到消息处理的脉络.一个switch case语句就可以搞定,消息循环直到遇到WM_QUIT消息才会结束,其余的消息均被拦截后调用相应的处理函数. 但在封装了API的MFC中,消息似乎变的有些复杂了,我们看不到熟悉的switch case语句

【win32】day04-Win32消息机制

消息机制 过程驱动:程序是按照我们预先定义好的顺序 执行,每执行一步,下一步都已经按照预定的顺序继续执行,直到程序结束. 事件驱动:程序的执行顺序是无序的.某个时间点所执行的代码,是由外界通知.由于我们无法决定用户执行顺序,所以代码的执行也是无序. Win32的消息机制 -事件驱动. Win32消息程序 2.1 Win32窗口注册 2.2 Win32窗口创建 2.3 WIn32消息循环 2.3.1 GetMessage BOOL GetMessage( LPMSG lpMsg,//存放获取到的消

C#.NET 消息机制

一.消息概述 众人周知,window系统是一个消息驱动的系统, windows操作系统本身有自己的消息队列,消息循环,它捕捉键盘,鼠标的动作生成消息,并将这个消息传给应用程序的消息队列. 余下的工作有应用程序处理消息, windows 消息机制在这儿就不再讲述,我们重点讲述应用程序的消息机制. 大家只要明白消息是由操作系统传递给应用程序的. 一副图更能详细说明: 应用程序的执行是通过消息驱动的.消息是整个应用程序的工作引擎,我们需要理解掌握我们使用的编程语言是如何封装消息的原理.1 什么是消息(

windows消息机制

一. windows消息机制处理流程 (1)windows会为每一个正在执行的windows应用程序建立一个消息队列,即应用程序队列,用来存放该程序可能创建的各种窗口的消息. 当应用程序发生事件后,windows将事件转化为消息并将消息放入应用程序的消息队列中. (2)应用程序通过GetMessage从消息队列中检索事件消息并把他们分发到相应窗口的消息处理函数中. while(GetMessage(&msg, NULL, 0, 0)) { // 将虚拟键消息转换为字符消息 TranslateMe

Windows消息机制(转)1

Windows的应用程序一般包含窗口(Window),它主要为用户提供一种可视化的交互方式,窗口是总是在某个线程(Thread)内创建的.Windows系统通过消息机制来管理交互,消息(Message)被发送,保存,处理,一个线程会维护自己的一套消息队列(Message Queue),以保持线程间的独占性.队列的特点无非是先进先出,这种机制可以实现一种异步的需求响应过程. PS 常见的错误的理解: 1) 每个窗口有自己的消息队列 (我加的) 消息的是什么样子的? 消息由一个叫MSG的结构体定义,

windows消息机制与实例

windows发送窗口消息 所需工具:spy++,visual studio 2017,c#语言 技术路线:首先通过spy++获得所要操纵的窗口的句柄,函数的原型声明为: [DllImport("user32.dll")]       public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); 此函数获得目标窗口的句柄,如果要获得某个子窗口的句柄,通过以下函数可获得: [DllImpo

Windows消息机制详解

消息是指什么?      消息系统对于一个win32程序来说十分重要,它是一个程序运行的动力源泉.一个消息,是系统定义的一个32位的值,他唯一的定义了一个事件,向 Windows发出一个通知,告诉应用程序某个事情发生了.例如,单击鼠标.改变窗口尺寸.按下键盘上的一个键都会使Windows发送一个消息给应用程序. 消息本身是作为一个记录传递给应用程序的,这个记录中包含了消息的类型以及其他信息.例如,对于单击鼠标所产生的消息来说,这个记录中包含了单击鼠标时的坐标.这个记录类型叫做MSG,MSG含有来