简介
本文将介绍三维渲染引擎开源框架OSG(Open Scene Graph)的事件处理模块的Windows版的原理与源码分析。本文将介绍OSG事件适配器、OSG事件执行者、OSG事件处理器等技术概念和具体C++类型。本文还将介绍OSG事件处理模块Windows版的实现思路。本文最后还将介绍OSG事件处理模块的源码分析。本文还将对OSG事件处理模块源码阅读过程中可能产生的困惑进行答疑解惑。这篇OSG框架技术博客是调查研究调试OSG框架源码之后才创作的博客,如果对读者朋友们有一些帮助,则倍感欣慰。
正文
OSG事件处理模块的技术概念和数据类型
OSG事件(Event)
OSG框架中的事件,表示某个具体的操作系统事件或者框架自定义事件。OSG事件的祖宗类型是Event类型。从类型定义看,Event类型也是一个OSG对象类型(Object)。
class OSGGA_EXPORT Event : public osg::Object
{
public:
Event();
Event(const Event& rhs, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);
//稍后解释META_Object是什么。
//大型C++开源框架都喜欢玩这种宏定义的套路
META_Object(osgGA, Event);
//重新实现了asGuiEventAdapater()函数
//表示一个事件对象默认不是一个事件适配器对象;
//但是OSG中有一些事件适配器类型确实是事件类型的派生类。
virtual GUIEventAdapter* asGUIEventAdapter() {
return 0; }
virtual const GUIEventAdapter* asGUIEventAdapter() const {
return 0; }
//设置/获取当前事件对象表示的事件是否已经被处理过了
//具体是什么时间,稍后会介绍。
void setHandled(bool handled) const {
_handled = handled; }
bool getHandled() const {
return _handled; }
//设置/获取事件的时间
void setTime(double time) {
_time = time; }
double getTime() const {
return _time; }
protected:
//事件对象不能在对象外部进行销毁
virtual ~Event() {
}
mutable bool _handled;
double _time;
};
META_Object()宏定义的定义如下所示,定义了一系列的“元函数”,简化了具体事件类型的实现。
```cpp
#define META_Object(library,name) \
virtual osg::Object* cloneType() const {
return new name (); } \
virtual osg::Object* clone(const osg::CopyOp& copyop) const {
return new name (*this,copyop); } \
virtual bool isSameKindAs(const osg::Object* obj) const {
return dynamic_cast<const name *>(obj)!=NULL; } \
virtual const char* libraryName() const {
return #library; }\
virtual const char* className() const {
return #name; }
然后来讨论时间中包含的时间。在一个事件加入到OSG框架的事件队列时,事件会被设置为加入队列时所在时刻的时间。当一个事件从OSG框架事件队列取出来时,该事件的时间会被设置为取出时所在时刻的时间。
OSG事件适配器(EventAdapater)
OSG框架中的事件适配器,是用于表示各种事件类型。通常操作系统中支持的事件类型特别多,比如仅仅鼠标左键相关事件就有按下、松开、移动、单击、双击等具体事件类型。作为OSG框架,如何使用具体的C++类型来表达这些不同的事件呢?
这有两种思路。
OSG框架的思路:
将所有图形用户界面有关的事件综合到一个数据类型中,也就是GUI事件适配器GUIEventAdapter类型。GUIEventAdapter这个类型是从Event类型派生而来的。结果就是一个C++数据类型必须能够同时表达所有的GUI事件。
QT框架的思路:
为每一个事件设计一个具体的C++类型。结果就是设计出了太多的C++数据类型。
在这里不打算去评判哪一种设计思路更好,仅仅列出来供读者朋友们拓展复杂软件框架的设计思路。
class OSGGA_EXPORT GUIEventAdapter : public Event
{
public:
GUIEventAdapter();
GUIEventAdapter(const GUIEventAdapter& rhs, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);
META_Object(osgGA, GUIEventAdapter);
//这种GUI事件适配器类型本身就是一个GUIEventAdapter类型,
//显然可以直接转换为GUIEventAdapter类型
virtual GUIEventAdapter* asGUIEventAdapter() {
return this; }
virtual const GUIEventAdapter* asGUIEventAdapter() const {
return this; }
//获取累积的事件状态对象的指针
static osg::ref_ptr<GUIEventAdapter>& getAccumulatedEventState();
//设置/获取事件类型
//这样就可以识别不同的具体事件
void setEventType(EventType Type) {
_eventType = Type; }
virtual EventType getEventType() const {
return _eventType; }
//设置/获取图形上下文
void setGraphicsContext(osg::GraphicsContext* context) {
_context = context; }
osg::GraphicsContext* getGraphicsContext() {
return _context.get(); }
const osg::GraphicsContext* getGraphicsContext() const {
return _context.get(); }
//键盘事件
inline void setKey(int key) {
_key = key; }
virtual int getKey() const {
return _key; }
void setUnmodifiedKey(int key) {
_unmodifiedKey = key; }
int getUnmodifiedKey() const {
return _unmodifiedKey; }
//鼠标事件
void setButton(int button) {
_button = button; }
int getButton() const {
return _button; }
void setX(float x) {
_mx = x; }
float getX() const {
return _mx; }
void setY(float y) {
_my = y; }
float getY() const {
return _my; }
protected:
virtual ~GUIEventAdapter();
EventType _eventType;
osg::observer_ptr<osg::GraphicsContext> _context;
//窗口位置和大小事件信息
int _windowX;
int _windowY;
int _windowWidth;
int _windowHeight;
//键盘事件和鼠标事件信息
int _key;
int _unmodifiedKey;
int _button;
float _Xmin,_Xmax;
float _Ymin,_Ymax;
float _mx;
float _my;
int _buttonMask;
int _modKeyMask;
MouseYOrientation _mouseYOrientation;
Scrolling _scrolling;
//其它一些GUI事件信息
TabletPen _tabletPen;
osg::ref_ptr<TouchData> _touchData;
PointerDataList _pointerDataList;
};
OSG行为适配器(ActionAdapter)
OSG行为适配器用于在事件处理器的事件处理函数的执行过程中,对窗口界面实施某种特定的行为操作影响。
class GUIActionAdapter
{
public:
virtual ~GUIActionAdapter() {
}
virtual osg::View* asView() {
return 0; }
//请求重绘一次
virtual void requestRedraw()