《OGRE分析之设计模式.pdf》由会员分享,可在线阅读,更多相关《OGRE分析之设计模式.pdf(22页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、OGRE 分析之设计模式-Mythma OGRE 分析之设计模式(一)分析之设计模式(一)Mythma http:/ Email: OGRE 的设计结构十分清晰,这得归功于设计模式的成功运用。一、一、Singleton Singleton 模式是 OGRE 中用的最广泛的设计模式,若干类都使用了该模式。Singleton 模式本身很简单,实现方式多种多样,简单的如 GoF 提到的实现方式,复杂的有 Loki 库中解决方案,而 OGRE 的实现方法也是十分简单。OGRE 提供了一个模板类Ogre:Singleton,作为所有 singleton 类的基类:template class Singl
2、eton protected:static T*ms_Singleton;public:Singleton(void)assert(!ms_Singleton);ms_Singleton=static_cast(this);Singleton(void)assert(ms_Singleton);ms_Singleton=0;static T&getSingleton(void)assert(ms_Singleton);return(*ms_Singleton);static T*getSingletonPtr(void)return ms_Singleton;需要使用 Singleton 的类
3、,只需要从它继承即可:class MyClass:public Singleton/.但在 OGRE的源码中我们可以发现,从Singleton 继承的类都 Override了 Singleton的成员 getSingleton 和 getSingletonPtr。OGRE 中是这样解释的:Singleton的 实 现实 现 都 在OgreSingleton.h文 件 中,这 意 味 着 任 何 包 含OgreSingleton.h 头文件的文件编译后都有 Singleton 的一份实现,这符合模板机制。但是从别的 dll 中使用基于 Singleton 的类时,就会产生连接错误。子类 Over
4、ride 一下,把实现放在.cpp 中,就会避免该问题。第 1 页 共 4 页 OGRE 分析之设计模式-Mythma 二、二、Factory Method Factory Method本质上代理某个类的实例化过程,封装了一个new的语句而已。C+中new和delete通常都要成对出现,因此Factory Method也应该负责一个对象的delete(GoF 没有提到由 Factory Method 构造的对象的析构问题)。另外,当一个类的构造函数的参数很多,其中有可以提供默认值的时候,用 Factory Method 还可以简化对象的创建。OGRE 中用到的 Factory Method 的
5、地方很多。为满足最普遍的情况,OGRE 有一个模板化的 Factory MethodOgre:FactoryObj。因为具体类的构造函数的参数并不都相同,因此 Ogre:FactoryObj并不是 OGRE 所有工厂方法的基类。template class FactoryObj public:virtual FactoryObj();virtual const String&getType()const=0;virtual T*createInstance(const String&name)=0;virtual void destroyInstance(T*)=0;OGRE 大部分 Fact
6、ory Method 的使用效果都是连接平行的类层次,如:不使用Ogre:FactoryObj的 Factory Method 很多,如:还有 ParticleEmitter 和 ParticleEmitterFactory 两个比较大的类层次等。第 2 页 共 4 页 OGRE 分析之设计模式-Mythma 三、三、Abstract Factory Abstract Factory 提供了用来创建一系列相关或相互依赖对象的接口。对使用者而言,无需关心使用的是何种具体的对象。OGRE 中,该模式体现在它的 Plug-in 机制。OGRE 提供了 Plug-in 机制。利用 Plug-in,一方
7、面把与平台相关的部分和与平台无关的部分分离开来,如底层渲染系统既可以使用 OpenGL,又可以使用 DirectX;另一方面便于系统功能的扩充,如场景既可以使用 BSP 方式管理,又可以用 Octree 管理。OGRE 的 Plug-in 实现方式有两种,自此只讨论一下用 Abstract Factory 模式实现的Plug-in。以 此 方 式 实 现 的 主 要 有 两 大 类Plug-in:RenderSystem_x和Plugin_xSceneManager。1、RenderSystem RenderSystem 是与渲染引擎相关的部分,如 RenderSystem_Direct3D7
8、.dll、RenderSystem_Direct3D9.dll、RenderSystem_GL.dll:与渲染相关的对象都是通过具体的子 RenderSystem 创建的,这样就形成了不同类别的Render 产品。在使用 OGRE 的时候,只需关心 RenderSystem,而无需关心究竟是那种具体的 RenderSystem。RenderSystem 创建的产品主要有:1)RenderWindow 2)RenderTexture 3)HardwareOcclusionQuery 第 3 页 共 4 页 OGRE 分析之设计模式-Mythma 2、SceneManager SceneManag
9、er是 与 场 景 内 数 据 的 组 织 方 式 相 关 的 部 分,如:Plugin_OctreeSceneManager.dll、Plugin_BSPSceneManager.dll 等:同样,SceneManager 只是个接口,具体的场景管理是由子类完成的。它的产品系主要有:1)SceneNode 2)RaySceneQuery SceneManager 中的很多 Products 是相同的,因此由 SceneManager 就完成创建过程。所以说 SceneManager 不是个纯粹的 Abstract Factory。第 4 页 共 4 页 OGRE 分析之设计模式-Mythma
10、 OGRE 分析之设计模式(二)分析之设计模式(二)Mythma http:/ Email: OGRE 的设计结构十分清晰,这得归功于设计模式的成功运用。四、四、Adapter Apdater 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。当使用第三方工具包时候,这种模式经常用到。OGRE 使用了其他的几个开源项目,如 Cg、freetype、ode、zzip 等,与其他接口肯定会有不兼容的情况,这就需要 Adapter。看一下 OGRE 的文件系统与 zzip:OGRE 是面向对象的,zzip 提供的都是结构体和函数。为了处理压缩文件和普通文件使用相同的接口,就需要 Adap
11、ter。OGRE 中处理文件目录使用 Ogre:Archive,zzip 用 ZZIP_DIR 来表示压缩文件目录。因此在Ogre:ZipArchive中加入一个ZZIP_DIR对象,以使zzip的接口适应Archive的接口使用的是对象适配器。文件的处理,OGRE 使用 Ogre:DataStream 接口,对应 zzip 的 ZZIP_FILE。同样,OGRE 使用的是对象适配器。可以看出,Adapter 模式与 Abstract Factory 模式可以实现相同的效果让别的接口兼容(回顾一下用 Abstract Factory“适配”不同的 RenderSystem),但是两者的区别在于
12、 Abstract Factory 是用于创建不同类别的对象;而 Adapter 没有创建别的对象的功能。所以 GoF 把 Abstract Factory 分在创建型模式类别中,而 Adapter 在结构型模式中。可以发现,这两种模式,OGRE 的文件系统都使用了。五、五、Bridge Bridge 的意图是使得抽象部分与它的实现部分分离,这样就使得两部分都可以独立变化提高可扩充性。该模式可以在多种情况下使用,如实现跨平台、程序换肤、文件版本演化等。当然实现这些功能也可以使用别的设计模式,同一个问题有不同的解决方案,模式的使用也是仁者见仁,智者见智。在分析 OGRE 二进制文件的序列化时,提
13、到了 Ogre:Serializer 类层次,如下图。第 1 页 共 6 页 OGRE 分析之设计模式-Mythma 可以看到序列化 Mesh 文件的 Serializer 既有 Ogre:MeshSerializer 又有Ogre:MeshSerializerImpl。OGRE在 读 写Mesh文 件 的 时 候 使 用 的 是Ogre:MeshSerializerImpl以 及 它 的 子 类,而Ogre:MeshSerializer是Ogre:MeshSerializerImpl 系列对外的“接口”,但二者是平辈关系。再看一下MeshSerializer 的实现一切将会真相大白:Mesh
14、Serializer:MeshSerializer()/Set up map mImplementations.insert(MeshSerializerImplMap:value_type(MeshSerializer_v1.10,new MeshSerializerImpl_v1_1();mImplementations.insert(MeshSerializerImplMap:value_type(MeshSerializer_v1.20,new MeshSerializerImpl_v1_2();mImplementations.insert(MeshSerializerImplMap
15、:value_type(msCurrentVersion,new MeshSerializerImpl();/-void MeshSerializer:exportMesh(const Mesh*pMesh,const String&filename)MeshSerializerImplMap:iterator impl=mImplementations.find(msCurrentVersion);if(impl=mImplementations.end()OGRE_EXCEPT();impl-second-exportMesh(pMesh,filename);可见具体的 Mesh 文件读取
16、 MeshSerializer 并未实现,而是由合适的 Implementor实现的,其他组件若要使用序列化功能,只需调用 MeshSerializer 即可。从实现上,还可以发现,与 GoF 提到的实现有很大的差别,这说明模式不是“死的”,同一模式的实现也多种多样,只要能满足需求就可以了。第 2 页 共 6 页 OGRE 分析之设计模式-Mythma 六、六、Proxy Proxy 为其他对象提供一种代理以控制对该对象的访问。GoF 提到四种常见的情况:Romote Proxy,Virtual Proxy、Protection Proxy 以及 Smart Reference。在这里我只分析
17、一下 Smart Reference。Smart Pointer 在 STL 中有 std:auto_ptr,在 BOOST 中有 boost:shared_ptr、boost:shared_array、boost:scoped_ptr、boost:scoped_array、boost:weak_ptr、boost:intrusive_ptr,在 Loki 中则有 Loki:SmartPtr。各种 Smart Pointer 都有不同的功能,适用的地方又各不相同。加上有的 Smart Pointer 的行为又有点诡异,尤其是 std:auto_ptr,所以实际应用中一向对之退而却步。OGRE
18、虽不是模板库,却也有个 Smart PointerOgre:SharedPtr。SharedPtr是一个引用计数的共享指针。下面是 OGRE 对 SharedPtr 作的使用说明:This is a standard shared pointer implementation which uses a reference count to work out when to delete the object.OGRE does not use this class very often,because it is usually more efficient to make the destr
19、uction of objects more intentional(in blocks,say).However in some cases you really cannot tell how many people are using an object,and this approach is worthwhile(e.g.ControllerValue)除了加上 mutex 外,其实现手法没有什么特别之处。附上 Ogre:SharedPtr 的实现:template class SharedPtr protected:T*pRep;unsigned int*pUseCount;pub
20、lic:OGRE_AUTO_SHARED_MUTEX/public to allow external locking /*Constructor,does not initialise the SharedPtr.remarks Dangerous!You have to call bind()before using the SharedPtr.*/SharedPtr():pRep(0),pUseCount(0)explicit SharedPtr(T*rep):pRep(rep),pUseCount(new unsigned int(1)OGRE_NEW_AUTO_SHARED_MUTE
21、X SharedPtr(const SharedPtr&r)/lock© other mutex pointer OGRE_LOCK_MUTEX(*r.OGRE_AUTO_MUTEX_NAME)OGRE_COPY_AUTO_SHARED_MUTEX(r.OGRE_AUTO_MUTEX_NAME)pRep=r.pRep;pUseCount=r.pUseCount;第 3 页 共 6 页 OGRE 分析之设计模式-Mythma /Handle zero pointer gracefully to manage STL containers if(pUseCount)+(*pUseCount
22、);SharedPtr&operator=(const SharedPtr&r)if(pRep=r.pRep)return*this;release();/lock© other mutex pointer OGRE_LOCK_MUTEX(*r.OGRE_AUTO_MUTEX_NAME)OGRE_COPY_AUTO_SHARED_MUTEX(r.OGRE_AUTO_MUTEX_NAME)pRep=r.pRep;pUseCount=r.pUseCount;if(pUseCount)+(*pUseCount);return*this;virtual SharedPtr()release()
23、;inline T&operator*()const assert(pRep);return*pRep;inline T*operator-()const assert(pRep);return pRep;inline T*get()const return pRep;/*Binds rep to the SharedPtr.remarks Assumes that the SharedPtr is uninitialised!*/void bind(T*rep)assert(!pRep&!pUseCount);OGRE_NEW_AUTO_SHARED_MUTEX OGRE_LOCK_AUTO
24、_SHARED_MUTEX pUseCount=new unsigned int(1);pRep=rep;inline bool unique()const assert(pUseCount);OGRE_LOCK_AUTO_SHARED_MUTEX return*pUseCount=1;第 4 页 共 6 页 OGRE 分析之设计模式-Mythma inline unsigned int useCount()const assert(pUseCount);OGRE_LOCK_AUTO_SHARED_MUTEX return*pUseCount;inline unsigned int*useCo
25、untPointer()const return pUseCount;inline T*getPointer()const return pRep;inline bool isNull(void)const return pRep=0;inline void setNull(void)if(pRep)/cant scope lock mutex before release incase deleted release();pRep=0;pUseCount=0;OGRE_COPY_AUTO_SHARED_MUTEX(0)protected:inline void release(void)bo
26、ol destroyThis=false;/lock own mutex in limited scope(must unlock before destroy)OGRE_LOCK_AUTO_SHARED_MUTEX if(pUseCount)if(-(*pUseCount)=0)destroyThis=true;if(destroyThis)destroy();virtual void destroy(void)/IF YOU GET A CRASH HERE,YOU FORGOT TO FREE UP POINTERS /BEFORE SHUTTING OGRE DOWN /Use set
27、Null()before shutdown or make sure your pointer goes /out of scope before OGRE shuts down to avoid this.第 5 页 共 6 页 OGRE 分析之设计模式-Mythma delete pRep;delete pUseCount;OGRE_DELETE_AUTO_SHARED_MUTEX ;template inline bool operator=(SharedPtr const&a,SharedPtr const&b)return a.get()=b.get();template inlin
28、e bool operator!=(SharedPtr const&a,SharedPtr const&b)return a.get()!=b.get();第 6 页 共 6 页 OGRE 分析之设计模式-Mythma OGRE 分析之设计模式(三)分析之设计模式(三)Mythma http:/ Email: OGRE 的设计结构十分清晰,这得归功于设计模式的成功运用。七、七、Chain of Responsibility Chain of Responsibility 是对象行为型模式,它把请求或消息以链的方式传送给对象处理者,避免了请求的发送者和接收者之间的耦合关系。该模式普遍用于处理用户
29、事件和处理图形的更新。OGRE 的消息传递也是使用 Chain of Responsibility 模式,体现在处理用户事件(鼠标消息和键盘消息)和图形的更新。首先看 OGRE 是如何传递处理用户事件的消息。1、用户事件的消息、用户事件的消息 在OGRE 分析之消息机制中分析了 OGRE 中消息的产生、处理和传递,得到如下的传递顺序:InputReader?EventProcessor?EventDispatcher?EventTarget?EventListener 这只是一个总体的过程,现在从代码上看一下是如何使用 Chain of Responsibility 的。1)消息的获取)消息的
30、获取 InputReader 产生的用户消息是如何进入消息传递链的?从代码上分析:OGRE 的入口是 Root:startRendering,然后进入系统循环:void Root:startRendering(void)/.mQueuedEnd=false;while(!mQueuedEnd)MSG msg;while(PeekMessage(&msg,NULL,0U,0U,PM_REMOVE)TranslateMessage(&msg);DispatchMessage(&msg);if(!renderOneFrame()break;然后进入 Root:renderOneFrame():boo
31、l Root:renderOneFrame(void)第 1 页 共 8 页 OGRE 分析之设计模式-Mythma if(!_fireFrameStarted()return false;_updateAllRenderTargets();/进行图形重画进行图形重画 return _fireFrameEnded();bool Root:_fireFrameStarted()unsigned long now=mTimer-getMilliseconds();FrameEvent evt;evt.timeSinceLastEvent=calculateEventTime(now,FETT_AN
32、Y);evt.timeSinceLastFrame=calculateEventTime(now,FETT_STARTED);return _fireFrameStarted(evt);从上面可以看出,构造出了 FrameEvent 消息并传递,但这并不是我们需要的 InputEvent,继续:bool Root:_fireFrameStarted(FrameEvent&evt)/略 /Tell all listeners for(i=mFrameListeners.begin();i!=mFrameListeners.end();+i)if(!(*i)-frameStarted(evt)/F
33、rameListener:frameStated(evt)return false;return true;可见 FrameEvent 消息被送往 FrameListener 去了。在初始化在初始化 OGRE 的时候,都需要创建一个的时候,都需要创建一个 EventProcessor 并自动注册到并自动注册到 Root:mFrameListeners 中中,而EventProcessor 属于 FrameLIstener 的子类,所以消息必定必定会传递给 EventProcessor:bool EventProcessor:frameStarted(const FrameEvent&evt)m
34、InputDevice-capture();while(mEventQueue-getSize()0)InputEvent*e=mEventQueue-pop();processEvent(e);delete e;return true;第 2 页 共 8 页 OGRE 分析之设计模式-Mythma 现在终于出现 InputEvent(OGRE 中的鼠标消息封装在 MouseEvent,键盘消息封装在 KeyEvent 中,它们都是 InputEvent 的子类)。开始进入消息的传递阶段。2)消息的传递 void EventProcessor:processEvent(InputEvent*e
35、)/try the event dispatcher list for(DispatcherList:iterator i=mDispatcherList.begin();i!=mDispatcherList.end();+i)(*i)-dispatchEvent(e);/EventDispatcher:dispatchEvent(e)/try the event target list if(!e-isConsumed()/自己处理,略自己处理,略 可见,InputEvent 先传递给 EventDispatcher 处理,若仍未被处理,就由EventProcessor 自己处理(Event
36、Processor 本身也是个 Target,有处理用户消息的能力)。看消息在 EventDispatcher 中是如何处理的:bool EventDispatcher:dispatchEvent(InputEvent*e)bool ret=false;if(e-isEventBetween(MouseEvent:ME_FIRST_EVENT,MouseEvent:ME_LAST_EVENT)MouseEvent*me=static_cast(e);ret=processMouseEvent(me);else if(e-isEventBetween(KeyEvent:KE_FIRST_EVEN
37、T,KeyEvent:KE_LAST_EVENT)KeyEvent*ke=static_cast(e);ret=processKeyEvent(ke);return ret;至此,EventDispatcher 把 InputEvent 消息分成 MouseEvent 和 KeyEvent 分别处理。第 3 页 共 8 页 OGRE 分析之设计模式-Mythma 先看键盘消息:bool EventDispatcher:processKeyEvent(KeyEvent*e)if(mKeyCursorOn!=0)mKeyCursorOn-processEvent(e);/PositionTarge
38、t:PositionTarget:processEvent(e)return e-isConsumed();键盘消息被分发给 Cursor 当前所在的 PositionTarget,由 PositionTarget 处理。而PositionTarget 是个抽象类,需要其子类才能处理消息:再看鼠标消息:bool EventDispatcher:processMouseEvent(MouseEvent*e)PositionTarget*targetOver;mMouseX=e-getX();mMouseY=e-getY();targetOver=mTargetManager-getPositio
39、nTargetAt(e-getX(),e-getY();trackMouseEnterExit(targetOver,e);switch(e-getID()case MouseEvent:ME_MOUSE_PRESSED:mDragging=true;if(mDragDropOn)mDragDropActive=true;mMouseDragSource=targetOver;retargetMouseEvent(targetOver,e);trackKeyEnterExit(targetOver,e);break;case MouseEvent:ME_MOUSE_RELEASED:第 4 页
40、 共 8 页 OGRE 分析之设计模式-Mythma if(targetOver!=0)if(targetOver=mMouseDragSource)retargetMouseEvent(mMouseDragSource,MouseEvent:ME_MOUSE_CLICKED,e);retargetMouseEvent(mMouseDragSource,e);else/i.e.targetOver!=mMouseDragSource if(mDragDropActive)retargetMouseEvent(targetOver,MouseEvent:ME_MOUSE_DRAGDROPPED,
41、e);retargetMouseEvent(mMouseDragSource,e);retargetMouseEvent(targetOver,MouseEvent:ME_MOUSE_ENTERED,e);else retargetMouseEvent(mMouseDragSource,e);mDragging=false;mDragDropActive=false;mMouseDragSource=0;break;case MouseEvent:ME_MOUSE_MOVED:case MouseEvent:ME_MOUSE_DRAGGED:if(!mDragging|targetOver=m
42、MouseDragSource)retargetMouseEvent(targetOver,e);else/i.e.mDragging&targetOver!=mMouseDragSource retargetMouseEvent(mMouseDragSource,MouseEvent:ME_MOUSE_DRAGGED,e,true);if(mDragDropActive)retargetMouseEvent(targetOver,MouseEvent:ME_MOUSE_DRAGMOVED,e);break;return e-isConsumed();第 5 页 共 8 页 OGRE 分析之设
43、计模式-Mythma void EventDispatcher:retargetMouseEvent(PositionTarget*target,MouseEvent*e)if(target=NULL)return;MouseEvent*retargeted=new MouseEvent(target,e-getID(),e-getButtonID(),e-getWhen(),e-getModifiers(),e-getX(),e-getY(),e-getZ(),e-getClickCount();target-processEvent(retargeted);delete retargete
44、d;e-consume();可见所有的消息经过 EventDispatcher 分发后都是由各种 target 处理的。若 target 处理不了,则由 EvenetProcessor 自行处理。而 EvenetProcessor 本身是也是 Target 的一种:所有,用户消息默认的情况下(在没有其他的 Target 的时候),是由 EvenetProcessor处理的,再看EventProcessor:processEvent(InputEvent*e):void EventProcessor:processEvent(InputEvent*e)/try the event dispatc
45、her list for(DispatcherList:iterator i=mDispatcherList.begin();i!=mDispatcherList.end();+i)(*i)-dispatchEvent(e);第 6 页 共 8 页 OGRE 分析之设计模式-Mythma /try the event target list if(!e-isConsumed()EventTargetList:iterator i,iEnd;iEnd=mEventTargetList.end();for(i=mEventTargetList.begin();i!=iEnd;+i)(*i)-pro
46、cessEvent(e);if(!e-isConsumed()switch(e-getID()case MouseEvent:ME_MOUSE_PRESSED:case MouseEvent:ME_MOUSE_RELEASED:case MouseEvent:ME_MOUSE_CLICKED:case MouseEvent:ME_MOUSE_ENTERED:case MouseEvent:ME_MOUSE_EXITED:case MouseEvent:ME_MOUSE_DRAGENTERED:case MouseEvent:ME_MOUSE_DRAGEXITED:case MouseEvent
47、:ME_MOUSE_DRAGDROPPED:processMouseEvent(static_cast(e);break;case MouseEvent:ME_MOUSE_MOVED:case MouseEvent:ME_MOUSE_DRAGGED:case MouseEvent:ME_MOUSE_DRAGMOVED:processMouseMotionEvent(static_cast(e);break;case KeyEvent:KE_KEY_PRESSED:case KeyEvent:KE_KEY_RELEASED:case KeyEvent:KE_KEY_CLICKED:process
48、KeyEvent(static_cast(e);break;可见,对不同的消息,EventProcessor 分别进行了处理。而上面三个高亮的函数,是EventProcessor 继承其父类而来的。以 processKeyEvent 为例(其他的两个类似),它是由 KeyTarget 处理的:第 7 页 共 8 页 OGRE 分析之设计模式-Mythma void KeyTarget:processKeyEvent(KeyEvent*e)/Remove all marked listeners std:set:iterator i;for(i=mRemovedListeners.begin()
49、;i!=mRemovedListeners.end();i+)mKeyListeners.erase(*i);mRemovedListeners.clear();/Tell all listeners for(i=mKeyListeners.begin();i!=mKeyListeners.end();i+)KeyListener*listener=*i;if(listener!=0)int id=e-getID();switch(id)case KeyEvent:KE_KEY_PRESSED:listener-keyPressed(e);break;case KeyEvent:KE_KEY_
50、RELEASED:listener-keyReleased(e);break;case KeyEvent:KE_KEY_CLICKED:listener-keyClicked(e);break;由此可见,经过长途跋涉用户消息最终分发给了 Listener。若要处理键盘和鼠标消息,就得向 EventProcessor 注册相应的 Listeners。2、图形更新消息、图形更新消息 在OGRE 分析之场景渲染中,已经分析过,得到更新消息是按如下路径传递的:Root?RenderSystem?RenderTarget?Viewport?Camera?SceneManager?RenderSystem