Ogre 场景管理
每个3D引擎都会用scene graph 来组织它的可渲染对象。scene graph 总是会为了更快地搜索与查询做
优化,提供给用户查找目标对象附近特定对象的功能,允许查找,排序,剔除多边形,以实现更高效的渲染。偶尔,scene graph也用于碰撞检测。有时,一个单独的scene graph可被用于程序中的所有子系统,包括
音效与物理。
Ogre使用插件机制来实现场景管理功能。ScenceManager只是接口,他可以有很多具体的实现。Ogre允许
在同一时刻同一场景中使用多个Scene Manager,这样在不同的场景类型切换时带来好处。
场景管理器的责任
1,创建,放置场景中的可移动对象,light,camera,并可以在图形遍历中有效地访问它们。
2,加载,装配world geometry(它通常很大,向四处延伸,不可移动)
3,完成场景查询,例如可以回答这样的问题:在世界空间的特定点画一个球体,它会包含哪些对象?
4,剔除不可见对象,把可见对象放入渲染队列进行渲染
5,从当前可渲染的透视图中组织,拣选各方向光照
6,设置,渲染场景中的所有阴影
7 设置,渲染场景中的其他对象(如背景,天空盒)
8 传递组织良好的内容到渲染系统进行渲染
场景管理器类型
以分析源码的方式讨论一下插件的加载机制与特定场景管理器是如何进行运用的。
上一章提到了以手工的方式初始化ogre,包括手工加载场景管理器:
root->loadPlugin("Plugin_OctreeSceneManager");
其实所谓的自动方式下,Root:Root()中也会间接调用到loadPlugin()方法,这已在前面的笔记
(配置文件Plugins.cfg )中提到过。既然特定管理器以插件的形式(dll文件)给出,下面先看如何
加载dll. 进入源码:标号表明执行顺序。
void Root::loadPlugin(const String& pluginName)
{
// Load plugin library
DynLib* lib = DynLibManager::getSingleton().load( pluginName ); //(1)
// Store for later unload
mPluginLibs.push_back(lib); //(4)
// Call startup function
DLL_START_PLUGIN pFunc = (DLL_START_PLUGIN)lib->getSymbol("dllStartPlugin"); //(5)
// This must call installPlugin
pFunc(); //(6)
}
DynLib* DynLibManager::load( const String& filename) //(2)
{
DynLib* pLib = new DynLib(filename);
pLib->load();
mLibList[filename] = pLib;
return pLib;
}
void DynLib::load() //(3)
{
m_hInst = (DYNLIB_HANDLE)DYNLIB_LOAD( name.c_str() );
}
第(3)中的宏定义如下:
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
# define DYNLIB_HANDLE hInstance
# define DYNLIB_LOAD( a ) LoadLibrary( a )
到此,DLL被加载到内存,第(4)步,mPluginLibs是个STL容器,它存放动态库指针。
第(5)步,进入源码可以看到
void* DynLib::getSymbol( const String& strName ) const throw()
{
return (void*)DYNLIB_GETSYM( m_hInst, strName.c_str() );
}
其中宏定义:define DYNLIB_GETSYM( a, b ) GetProcAddress( a, b ),很显然,它取得一个名为
dllStartPlugin的函数指针:不防再看看宏定义: typedef void (*DLL_START_PLUGIN)(void);
说明DLL_START_PLUGIN为参数为空,返回值为空的函数指针。
每个注册到ogre的dll都实现了这个约定函数。对于我们当前讨论的场景管理器Plugin_OctreeSceneManager
来讲,我们可以找到其相应的定义:它在第(6)步中执行
OctreePlugin* octreePlugin;
extern "C" void _OgreOctreePluginExport dllStartPlugin( void )
{
// Create new scene manager
octreePlugin = new OctreePlugin(); //(6-1)
// Register
Root::getSingleton().installPlugin(octreePlugin); //(6-2)
}
程序执行到(6-1)步,new 来一个OctreePlugin,我们看一下它的定义:
class OctreePlugin : public Plugin
{
public:
OctreePlugin();
const String& getName() const;
void install();
void initialise();
void shutdown();
void uninstall();
protected:
OctreeSceneManagerFactory* mOctreeSMFactory;
TerrainSceneManagerFactory* mTerrainSMFactory;
TerrainPageSourceListenerManager* mTerrainPSListenerManager;
};
我们会注意到它包含两个工厂类指针:OctreeSceneManagerFactory,TerrainSceneManagerFactory
他们就是用来生产特定ScenManager的,稍后讨论。先看(6-2)步:
void Root::installPlugin(Plugin* plugin)
{
mPlugins.push_back(plugin); //(6-2-1)
plugin->install(); //(6-2-2)
// if rendersystem is already initialised, call rendersystem init too
if (mIsInitialised)
{
plugin->initialise(); //(6-2-3)
}
}
//(6-2-1)步把插件放到容器中。看看(6-2-2)做了什么:
void OctreePlugin::install()
{
// Create objects
mOctreeSMFactory = new OctreeSceneManagerFactory();
mTerrainSMFactory = new TerrainSceneManagerFactory();
mTerrainPSListenerManager = new TerrainPageSourceListenerManager();
}
呵,刚才还说两个工厂类指针,现在把两个工厂建起来,以后可以用来生产东西了。
继续看看(6-2-3):
void OctreePlugin::initialise()
{
// Register
Root::getSingleton().addSceneManagerFactory(mOctreeSMFactory);
Root::getSingleton().addSceneManagerFactory(mTerrainSMFactory);
}
哦,把这两个工厂注册到Root中,让Root可以使用它们。啊这句话有点问题,Root只是间接的用到,
直接雇主是 SceneManagerEnumerator* mSceneManagerEnum;它被包含在Root中。于是我们可以看到
void Root::addSceneManagerFactory(SceneManagerFactory* fact) //(6-2-3-1)
{
mSceneManagerEnum->addFactory(fact);
}
工厂已经有了,我们如何利用这个工厂生产出我们想到的东西(SceneManager)呢?
回忆上一章的手工初始化过程中,我们一般用以下语句来创建SceneManager:
SceneManager *sceneMgr = root->createSceneManager(ST_GENERIC); //(A)
也可以这样用
sceneMgr = ogre->createSceneManager("OctreeSceneManager"); //(B)
每个工厂类都用一个字符串表示其类型。上面说的两个工厂分别使用的字符串为:“TerrainSceneManager”,"OctreeSceneManager"
(A)语句肯定会调用工厂类的方法来产生实际的SceneManager,下面看源码验证一下:
SceneManager* Root::createSceneManager(const String& typeName,
const String& instanceName)
{
return mSceneManagerEnum->createSceneManager(typeName, instanceName);
}
继续挖:
SceneManager* SceneManagerEnumerator::createSceneManager(
const String& typeName, const String& instanceName)
{
SceneManager* inst = 0;
for(Factories::iterator i = mFactories.begin(); i != mFactories.end(); ++i)
{
if ((*i)->getMetaData().typeName == typeName)
{
if (instanceName.empty())
{
// generate a name
StringUtil::StrStreamType s;
s << "SceneManagerInstance" << ++mInstanceCreateCount;
inst = (*i)->createInstance(s.str());
}
else
{
inst = (*i)->createInstance(instanceName);
}
break;
}
}
}
上述代码很简单:因为执行(6-2-3-1)已经过实际工厂类实例进行了注册。于是遍历每个工厂实例,
找到类型相符的。找到之后,如果没有传场景管理器实例的名字,就起个名字。然后用这个名字
生产出一个实例来。CreateInstance没干什么,new 呗。
SceneManager* OctreeSceneManagerFactory::createInstance(
const String& instanceName)
{
return new OctreeSceneManager(instanceName);
}
就是这样。OctreeSceneManager and TerrainSceneManager 的功能都是由一个DLL提供的。它们的关系是:class _OgreOctreePluginExport TerrainSceneManager : public OctreeSceneManager,按照一般的类关系逻辑,
我们知道派生类一般功能都比父类强大,父类应用于一般场景,子类针对特定场景。这个逻辑在这里是对的。本来是写读书笔记,跑题了,打住。
posted on 2007-03-08 15:08
清源游民 阅读(2329)
评论(0) 编辑 收藏 引用 所属分类:
OGRE