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系统中的一个组织者。”

        在ExampleApplicationRoot对象是用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 RootAllocOgre的内存管理相关类,这里不深入研究。

        下面我写了一个简化的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的实例化方法。先写到这吧。

 

原创粉丝点击