Ogre源码剖析之一:初识Root类
来源:互联网 发布:网络设置 编辑:程序博客网 时间:2024/05/17 05:05
当走了很远后,我们常常忘了最初启程的目的。
当我们学习一门新的开发语言时,大都是以一个输出”Hello world”开始的。那么对于学习Ogre来说ExampleApplication类就是它的入门”Hello world”例子。这个文件可以在..\ogre_src_v1-8-0\Samples\Common\include中找到,还有一个ExampleApplication类用到的类ExampleFrameListener也在该目录下。
可以这么说ExampleApplication类实现了运行Ogre程序的最基本框架,麻雀虽小五脏俱全,通过分析它,能深入理解Ogre的基本思想,机制和原理。
这里我用的图形引擎是DirectX9.0,那么剖析的深度就会到调用DirectX9的方法为止。
在阅读本文之前,确保你已经大概了解了Ogre,并且已经利用继承ExampleApplication类的方法实现一个最简单Ogre渲染实例,比如说渲染出来Ogre头。如果你不清楚这个怎么做,或者不知道如何配置Ogre,就百度吧。这里重点在对Ogre源码的剖析,不会太多介绍怎么用Ogre,而是重点放在介绍Ogre做了些什么,怎么做的这些问题上。
首先来看一下ExampleApplication类都有哪些成员变量,如下:
Root *mRoot; Camera* mCamera; SceneManager* mSceneMgr; ExampleFrameListener* mFrameListener; RenderWindow* mWindow;
好,下面就开始剖析第一类:Root。下面是引用《Ogre帮助手册》对Root对象的解释:“根(Root)对象。这是你进入OGRE系统的“入口”,根(Root)对象用来创建OGRE系统中的所有的基础元素,比如:场景管理器(Scene Managers),绘制系统(Rendering Systems),绘制窗口(Render Windows)和插件加载器(Loading Plugins)。根(Root)对象是OGRE系统中一切的开始,它会提供给你实现具体功能的核心对象,根(Root)对象更像是OGRE系统中的一个组织者。”
在ExampleApplication中Root对象是用OGRE_NEW的方法创建的,“OGRE_NEW”是Ogre自带的内存管理机制方法。Root对象是唯一的,就是说在用Ogre作为引擎开发的整个系统中,Root的实例化有且只有一个。Ogre用单件的方式管理着该对象,查看OgreRoot.h可以看到如下Root类的定义,这里要注意的是单件的实现方式,如下面的代码:
class _OgreExport Root : public Singleton<Root>, public RootAlloc
容易看到Root继承了2个类 Singleton<Root>和RootAlloc, RootAlloc是Ogre的内存管理相关类,这里不深入研究。
下面我写了一个简化的Root类:
template <typename T> class Singleton{private:/** \brief Explicit private copy constructor. This is a forbidden operation.*/Singleton(const Singleton<T> &);/** \brief Private operator= . This is a forbidden operation. */Singleton& operator=(const Singleton<T> &);protected:static T* msSingleton;public:Singleton( void ){assert( !msSingleton );#if defined( _MSC_VER ) && _MSC_VER < 1200 int offset = (int)(T*)1 - (int)(Singleton <T>*)(T*)1;msSingleton = (T*)((int)this + offset);#elsemsSingleton = static_cast< T* >( this );#endif}~Singleton( void ){ assert( msSingleton ); msSingleton = 0; }static T& getSingleton( void ){assert( msSingleton ); return ( *msSingleton ); }static T* getSingletonPtr( void ){ return msSingleton; }};class Root : public Singleton<Root>{public:Root(){std::cout<<"create root!"<<"\n";}virtual ~Root(){}static Root& getSingleton(void);static Root* getSingletonPtr(void);};template<> Root* Singleton<Root>::msSingleton = 0;Root* Root::getSingletonPtr(void){return msSingleton;}Root& Root::getSingleton(void){assert( msSingleton ); return ( *msSingleton );}void main(){//创建Root对象Root* p = new Root();//得到Root对象指针Root* pb = Root::getSingletonPtr();//此句会报错,因为Singleton中的assert( !msSingleton );p = new Root();}
当执行Root* p = new Root()会调用基类的构造函数Singleton( void ),然后断言assert( !msSingleton ),关键的是这一句msSingleton = static_cast< T* >( this ),将msSingleton指针指向p。执行Root* pb = Root::getSingletonPtr()能得到pb == p,就是说以后再想得到Root指针时得用Root::getSingletonPtr()。如果再次执行new Root()则断言不通过,报出错误。
这种实现单件的方式不是很好,因为还得重写getSingletonPtr(),getSingletonPtr(void)方法。而且可以调用delete删除创建的Root,不安全。
在Root的构造函数中,初始化了一批管理器,比如材质管理器,Mesh管理器,骨骼管理器,粒子管理器等等。
Root对象另外的重要作用是帮助你配置系统,比如颜色深度,是否全屏,Opengl还是D3D的选择等等。下面我们就来看看它是如何做到的。
首先还是回到Root的构造函数中找到这一句:
if (!pluginFileName.empty()) loadPlugins(pluginFileName);
上面这一句的作用是加载Oger插件,plugins.cfg文件中列出了要加载的相关插件。查看loadPlugins方法,可以查看到它在调用loadPlugin方法,再次跟踪到loadPlugin里去,这里有相当关键的代码,展示了Oger灵活的插件模式是怎么实现的,代码如下:
// Load plugin library DynLib* lib = DynLibManager::getSingleton().load( pluginName );// Store for later unload// Check for existence, because if called 2+ times DynLibManager returns existing entryif (std::find(mPluginLibs.begin(), mPluginLibs.end(), lib) == mPluginLibs.end()){mPluginLibs.push_back(lib);// Call startup functionDLL_START_PLUGIN pFunc = (DLL_START_PLUGIN)lib->getSymbol("dllStartPlugin");if (!pFunc)OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "Cannot find symbol dllStartPlugin in library " + pluginName,"Root::loadPlugin");// This must call installPluginpFunc();}
接着跟踪这一句:DynLib* lib = DynLibManager::getSingleton().load( pluginName );在OgreDynLib.cpp中找到void DynLib::load()方法,关键的动态加载动态库的方法出来了: mInst = (DYNLIB_HANDLE)DYNLIB_LOAD( name.c_str() ); DYNLIB_LOAD是一个宏,在Windows平台下被定义为:# define DYNLIB_LOAD( a ) LoadLibraryEx( a, NULL, LOAD_WITH_ALTERED_SEARCH_PATH ),这个是动态加载动态库的方法,在运行时加载。好了搞明白了Ogre的插件是如何加载了,再看看加载后都做了些什么事情。
来看这一句代码:
DLL_START_PLUGIN pFunc = (DLL_START_PLUGIN)lib->getSymbol("dllStartPlugin");
感觉挺怪异的,DLL_START_PLUGIN 是一个函数指针,为void*()。getSymbol("dllStartPlugin") 方法的实现为 return (void*)DYNLIB_GETSYM( mInst, strName.c_str() ); DYNLIB_GETSYM宏定义为GetProcAddress( a, b )这是获取动态库的函数地址的,那获得那个函数的地址呢?显然是dllStartPlugin函数。这里看一个例子,找到RenderSystem_Direct3D9工程下的OgreD3D9EngineDll.cpp文件,哦~~~,看到了下面代码:
extern "C" void _OgreD3D9Export dllStartPlugin(void) throw(){plugin = OGRE_NEW D3D9Plugin();Root::getSingleton().installPlugin(plugin);}
第一句话是实例化了一个D3D9Plugin对象,第二句进行了初始化。当执行pFunc()时上面的函数将被调用。
跟踪Root::getSingleton().installPlugin(plugin);定位到plugin->install();再次跟踪,看到下面代码:
void D3D9Plugin::install(){// Create the DirectX 9 rendering api#ifdef OGRE_STATIC_LIBHINSTANCE hInst = GetModuleHandle( NULL );#else# if OGRE_DEBUG_MODE == 1HINSTANCE hInst = GetModuleHandle( "RenderSystem_Direct3D9_d.dll" );# elseHINSTANCE hInst = GetModuleHandle( "RenderSystem_Direct3D9.dll" );# endif#endifmRenderSystem = OGRE_NEW D3D9RenderSystem( hInst );// Register the render systemRoot::getSingleton().addRenderSystem( mRenderSystem );}
这里实例化了一个很重要的对象 mRenderSystem ,它是D3D9RenderSystem类型,该类继承自RenderSystem。
这里有必要引用《Ogre帮助手册》对“RenderSystem”隆重说明一下:
渲染系统(RenderSystem)对象事实上是一个抽象类,它定义了3D API的接口。它负责设置所有的渲染参数并且传送渲染指令给底层的API,该类是抽象类,因为其实现依赖于某个具体的渲染系统的API接口(比如Direct3D的D3DRenderSystem)。当系统经过Root::initialise初始化后,通过调用Root::getRenderSystem()方法,渲染系统(RenderSystem)对象产生了。
可以说RenderSystem是一个中间接口层,封装了D3D和Opengl的3D API.
然后mRenderSystem被加入到了Root对象的mRenderers 成员变量中,mRenderers就是一个typedef vector<RenderSystem*>::type RenderSystemList。之所以这个对象mRenderSystem很重要,是因为在Root中会大量使用它。明白了它是个什么东西,和它是怎么来的,对我们下一步的剖析是很重要的。
可以看到,在Root的构造函数中,就完成了个各种插件的加载初始化,这也是Ogre中的动态插件加载方式的具体实现方法。另外我们记住一个很重要的对象mRenderSystem。
好了,这里剖析了三点:1.Root对象的单件创建方式。2.Root对象加载各种插件的方法。3. RenderSystem的继承类D3D9RenderSystem的实例化方法。先写到这吧。
- Ogre源码剖析之一:初识Root类
- Ogre源码剖析 - 任意类型类Any
- Ogre源码剖析1
- Ogre源码剖析 -FrameListener.frameRenderingQueued
- Ogre源码剖析 -FrameListener.frameRenderingQueued
- Ogre源码剖析 -FrameListener.frameRenderingQueued
- Ogre源码分析(一)Root类,Facade模式
- Ogre源码分析之Root类、Facade模式
- TinyXML 源码剖析(之一)
- Ogre源码剖析 - 场景管理之Octree
- Ogre源码剖析 - 场景管理之Octree
- STL源码剖析之一:概论
- Ogre源码剖析:如何实现低耦合的类间消息传递机制
- Ogre源码剖析:如何实现低耦合的类间消息传递机制
- Android Root插件模式:Xposed源码剖析
- Ogre源码剖析3–可扩展性&插件机制
- Ogre源码剖析 – 可扩展性&插件机制
- Ogre源码剖析 – 可扩展性&插件机制
- debug privilege
- Placement new、operator new、new operator 完全释疑
- WIZnet 4月新闻报
- Nginx %00空字节执行任意代码(php)漏洞
- MPLAB X IDE调试代码
- Ogre源码剖析之一:初识Root类
- windows下Git pull、 push 操作无需输密码的方法
- Oracle中insert into select和select into的用法(异常0RA-00905:missing keyword的解决)
- Zend Studio 9.0 字体修改方法
- RabbitMQ介绍
- 给TextView添加边框
- 快速入门互联网协议
- 客户端内嵌应用内支付功能--支付宝嵌入
- Protobuf 的安装