如果你自己都不清楚所谈论的东西,就根本不可能精确的描述它——冯诺依曼
今天我就试着来表述一件众人皆知的事情,以测试自己到底有没有明白这件事情。
OGRE是著名的设计模式大师,这已是不争的事实。可以说OGRE里将设计模式用得淋漓尽致。 在这里我就不批判设计模式该不该用了。反正OGRE已经用了,并且没有出现什么不好的结果。适合的就是最好的,OGRE证明了这一点。
随着OGRE 1.7的发布,大家熟悉的DEMO程序不见了,换来的是一个个的DLL库。 而这些库,就是作为OGRE的一个插件而存在。 拿SkyBox为例,(不要问为什么拿SkyBox,如果真要知道 ,我只能说,我刚好看上它了。)我们可以在SkyBox.cpp里发现如下代码。
SamplePlugin* sp;
Sample* s;
extern "C" _OgreSampleExport void dllStartPlugin()
{
s = new Sample_SkyBox;
sp = OGRE_NEW SamplePlugin(s->getInfo()["Title"] + " Sample");
sp->addSample(s);
Root::getSingleton().installPlugin(sp);
}
extern "C" _OgreSampleExport void dllStopPlugin()
{
Root::getSingleton().uninstallPlugin(sp);
OGRE_DELETE sp;
delete s;
}
dllStartPlugin 和 dllStopPlugin 是插件的加载和卸载接口。 可以看到,当调用dllStartPlugin 时,它先新建了一个Sample_SkyBox实例,这就是我们真正的示例程序。紧接着,它又新建了一个插件。插件的名字则以实例的Title信息加上Sample来标志。 随后,这个示例程序的实例被加入插件中,然后调用Root::getSingleton().installPlugin(sp);函数初始化我们的插件。
显然,我们需要看看installPlugin干了些什么。
void Root::installPlugin(Plugin* plugin)
{
LogManager::getSingleton().logMessage("Installing plugin: " + plugin->getName());
mPlugins.push_back(plugin);
plugin->install();
// if rendersystem is already initialised, call rendersystem init too
if (mIsInitialised)
{
plugin->initialise();
}
LogManager::getSingleton().logMessage("Plugin successfully installed");
}
不难看出,OGRE在 这个函数中将插件加入了自己的插件容器中,并调用插件的初始化接口。以及输出相关LOG信息。
而又是在何时调用这个dllStartPlugin来加载插件的呢。 我们打开SampleBrowser.h找到virtual Sample* loadSamples()函数。在这个函数中的前几句便 反应了它所做的工作。
Sample* startupSample = 0;
Ogre::StringVector unloadedSamplePlugins;
Ogre::ConfigFile cfg;
cfg.load(mFSLayer->getConfigFilePath("samples.cfg"));
Ogre::String sampleDir = cfg.getSetting("SampleFolder"); // Mac OS X just uses Resources/ directory
Ogre::StringVector sampleList = cfg.getMultiSetting("SamplePlugin");
Ogre::String startupSampleTitle = cfg.getSetting("StartupSample");
在这里,例子浏览器加载了samples.cfg文件,并读取相关内容。我们看看samples.cfg 里装了些什么便一切明了了。
SampleFolder=.
SamplePlugin=Sample_BezierPatch_d
SamplePlugin=Sample_BSP_d
SamplePlugin=Sample_CameraTrack_d
SamplePlugin=Sample_CelShading_d
SamplePlugin=Sample_Character_d
SamplePlugin=Sample_Compositor_d
SamplePlugin=Sample_CubeMapping_d
SamplePlugin=Sample_DeferredShading_d
。。。。
这些正好是我们的例子插件的DLL文件名。 loadSamples函数在读取了这些信息后,将其放入 StringVector sampleList 中,然后依次遍历这个容器,并调用插件加载函数。 代码如下
// loop through all sample plugins...
for (Ogre::StringVector::iterator i = sampleList.begin(); i != sampleList.end(); i++)
{
mRoot->loadPlugin(sampleDir + *i);
}
按照我们分析问题的方案(我们总是从程序的行为进行跟踪分析)。于是我们看看loadPlugin函数做了些什么。
void Root::loadPlugin(const String& pluginName)
{
//根据名字加载动态库
DynLib* lib = DynLibManager::getSingleton().load( pluginName );
//查找是否已经加载,如果没有存在,则加入其中。并且取得入口函数并执行。
if (std::find(mPluginLibs.begin(), mPluginLibs.end(), lib) == mPluginLibs.end())
{
mPluginLibs.push_back(lib);
DLL_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");
pFunc();//执行dllStartPlugin( )
}
}
由此,我们便可以知道整个程序的流程。。即例子浏览器在初始化时读取samples_d.cfg文件,然后根据文件内容加载所有的DLL并初始化相关内容。
此时我们会考虑,如果我们想要新增一个例子,应该如何去做? 于是,我们需要先看看Sample_SkyBox 以及SamplePlugin.
打开Sample_SkyBox.h 我们便会看到
class _OgreSampleClassExport Sample_SkyBox : public SdkSample
{
protected:
void setupContent()
{
//实现代码
}
};
上面代码说明了,Sample_SkyBox继承自SdkSample,并且实现了setupContent函数。
我们再打开SamplePlugin可以看到有些空函数,说明在我们的例子中,SamplePlugin并没有做太多的初始化工作。 于是,我们得到如下的关系
1、 Sample_SkyBox派生自SdkSample
2、 SamplePlugin派生自Plugin
3、 SamplePlugin持有Sample_SkyBox实例指针
4、 SamplePlugin会注册到Root的插件管理中
5、 Sample_SkyBox应该被加入到Samples_d.cfg中。
于是,我们可以看到,如果我们想实现一个简单的例子。则只需要自SdkSample派生一个实现类,并至少实现setupContent函数。然后学着Sample_SkyBox的样子写好dllStartPlugin和dllStopPlugin函数,并导出成DLL,然后将DLL名字添加到smaples_d.cfg中。
以上描述均是Debug版本下。如果是Release,则去掉后面的_d即可。
本文来自优快云博客,转载请标明出处:http://blog.youkuaiyun.com/BoYueJiang/archive/2010/07/15/5735923.aspx