【1. 概览】 Ogre支持Quake3的bsp格式。相关的代码在“Plugin_BSPSceneManager”工程中。主要的类有以下几个: Class BspSceneNode: BspSceneNode是SceneNode的派生类,是专门提供给BSPSceneManager使用的。主要是提供针对于BSP tree的可见性判断。这个类并不是BSP tree的node,BSP tree中的node使用BspNode。BspSceneNode会放入BSP tree的leaf节点中。由于SceneNode使用包裹盒的方法,不可分割,所以一个BspSceneNode可能放入多个Bsp tree的leaf节点中。 从类的定义看,BspSceneNode并没有额外的保存什么数据。重写的几个虚函数主要是用来通知BspSceneMapager, BspSceneNode::_update()会调用BspSceneManager::_notifyObjectMoved(),detach objcect会调用BspSceneManager::_notifyObjectDetached()。 Class BspSceneManager: 粗略的看BspSceneManager与OctreeSceneManager类似。首先保存了一个BspLevel的指针,然后使用一个 walkTree()函数,用来遍历tree结构。由于Quake使用BSP leaf tree,所以多了一个processVisibleLeaf()函数。另外一个明显的不同是有一个renderStaticGeometry()函数, “Renders the static level geometry tagged in walkTree”。此函数渲染“mMatFaceGroupMap”中的所有数据。BSP一个好处是不透明面可以front-back的顺序来渲染,而透明面back-front来渲染,OGRE是如何将此特性保存到MaterialFaceGroupMap的呢? Class BspLevel: 这是一个核心的class。他存储了BSP的所有数据,关键的数据有: <!–[if !supportLists]–>1. <!–[endif]–>“BspNode* mRootNode;”――BSP tree的根节点 <!–[if !supportLists]–>2. <!–[endif]–>“VertexData* mVertexData;”――整个level的所有顶点; <!–[if !supportLists]–>3. <!–[endif]–>“StaticFaceGroup* mFaceGroups;”――faces <!–[if !supportLists]–>4. <!–[endif]–>“BspNode::Brush *mBrushes;”――用来做碰撞检测的Brush,是QuakeBSP除了渲染以外的另外一个精华!Brush的名字有点怪,其实就是一个convex volume,可以减少CD的运算量。 <!–[if !supportLists]–>5. <!–[endif]–>“VisData mVisData;”――PVS数据,是又一个Quake中的精华!当初Carmark在设计Quake的时候还使用软件渲染,hiden surface removal和减少over draw是最另他头痛的问题。BSP的思想应该是他从网上看来的,不过PVS应该是他所创。PVS大大减少了over draw。(见《Michael Abrash’s Graphics Programming Black Book》) <!–[if !supportLists]–>6. <!–[endif]–>“PatchMap mPatches;”――Quake3支持贝赛尔曲面 关键的函数: <!–[if !supportLists]–>1. <!–[endif]–>bool isLeafVisible(const BspNode* from, const BspNode* to) const;使用PVS来检测可见性。 <!–[if !supportLists]–>2. <!–[endif]–>void _notifyObjectMoved(const MovableObject* mov, const Vector3& pos); Class BspNode: 这是Bsp中的另外一个重要的类了。Node和Leaf都使用这个类。 重要数据: <!–[if !supportLists]–>1. <!–[endif]–>Plane mSplitPlane; BspNode* mFront; BspNode* mBack; <!–[if !supportLists]–>2. <!–[endif]–>int mVisCluster; <!–[if !supportLists]–>3. <!–[endif]–>int mNumFaceGroups; int mFaceGroupStart; <!–[if !supportLists]–>4. <!–[endif]–>IntersectingObjectSet mMovables; <!–[if !supportLists]–>5. <!–[endif]–>NodeBrushList mSolidBrushes; 另外剩下的OgreQuake3Level.h、OgreQuake3Shader.h、OgreQuake3ShaderManager.h、 OgreQuake3Type.h主要是为了把Quake3格式的bsp,shader信息读入,并转换成Ogre本地的bsp定义以及 Material。现在quake3的源码已经公开(非常感谢id software以及carmark),可以结合quake3的源码来看。 【2. Quake3 bsp的加载】 OGRE使用BspLevel来存储Bsp场景信息,这个类是与文件格式无关的。所以需要另外一个类来把Quake3的bsp文件读入。 Quake3Level的读盘的主要由两个函数完成: 1、“void Quake3Level::loadHeaderFromStream()”。调用的流程是: BspApplication::loadResources() à ResourceGroupManager::loadResourceGroup()【A】 à BspSceneManager::estimateWorldGeometry() à BspLevel::calculateLoadingStages() àQuake3Level::loadHeaderFromStream() Quake3 BSP的文件格式很简单明了,前面是一个文件头,后面是几个数据块。文件头主要存储了几个lump,包含数据块的起始位置和大小,通过lump,可以直接seek到对于的数据块。 2、第二个函数“void Quake3Level::loadFromStream()”。调用的过程是: ResourceGroupManager::loadResourceGroup()【A】 àBspSceneManager::setWorldGeometry() àBspResourceManager::load() àResourceManager::load() àResource::load() àBspLevel::loadImpl() àQuake3Level::loadFromStream() 在这地方OGRE延续了他罗嗦的风格,BspSceneManager要通过BspResourceManager来加载场景,BspLevel实现为一种 Resource,BspResourceManager通过标准的ResourceManager――》Resource来找到BspLevel,然后调用其加载函数。 此函数首先构造了一个“MemoryDataStream”对象,在 MemoryDataStream的构造函数中把文件数据全部读入其缓冲中(Quake也是这样干的),然后调用“void Quake3Level::initialisePointers(void)”函数,得到所有lump索引的对象的指针。 Quake3Level 把文件读入并明确了所有数据的指针之后,在void BspLevel::loadImpl()中调用“BspLevel::loadQuake3Level()”函数讲Quake3level中的数据拷贝到自己的数据对象中。主要执行了以下几个操作: <!–[if !supportLists]–>1. <!–[endif]–>“BspLevel::loadEntities()”,这个lump存的是一个字符串,用来描述一些游戏信息,Ogre的这个函数只读取了Player start信息(位置和角度)。 <!–[if !supportLists]–>2. <!–[endif]–>“Quake3Level:: extractLightmaps()”。Quake3 BSP的每个light map都是128×128大,此函数将Light map lump中的数据逐个调用“TextureManager::loadImage()”创建成Texture对象(class D3D9Texture for D3D9 RenderSystem)。 <!–[if !supportLists]–>3. <!–[endif]–>创建VertexData: <!–[if !supportLists]–>4. <!–[endif]–>创建Faces:创建BspLevel:: mLeafFaceGroups数组;创建BspLevel:: mFaceGroups数组,此数组的数据在后面一步中填充;创建indexbuffer,并将Quake3Level::mElements拷贝进来; <!–[if !supportLists]–>5. <!–[endif]–>Create materials for shaders:对于Quake3Level::mFaces每一个bsp_face_t,找到它索引的Quake3Shader,并创建 Material,如果没有找到Quake3Shader的话则使用shader name去查找贴图文件; <!–[if !supportLists]–>6. <!–[endif]–>Nodes:创建BspLevel:: mRootNode数组,并将数据拷贝进来。 <!–[if !supportLists]–>7. <!–[endif]–>Brushes:将数据拷贝到BspLevel:: mBrushes中; <!–[if !supportLists]–>8. <!–[endif]–>Leaves:设置每个leaf节点的数据,主要包括包裹盒,mFaceGroupStart,mNumFaceGroups,mVisCluster,mSolidBrushes。参见BspNode类; <!–[if !supportLists]–>9. <!–[endif]–>Vis data:将数据拷贝到BspLevel:: mVisData中。 Quake3 BSP的load流程基本上就是这些了。 【3. Bsp tree scene的渲染】 然后是BspSceneManager重写了另外一个重要的函数_renderVisibleObjects()。此函数包含两个操作,一个是 renderStaticGeometry(),另外一个是调用父类的SceneManager::_renderVisibleObjects()。前者循环遍历mMatFaceGroupMap,然后调用RenderSystem::_rendr();后者已经在“Ogre学习笔记(3):Mesh的渲染流程”中详细分析过了。 |
- 本文固定链接: http://jingyan.idoubi.net/2364.html
- 转载请注明: 游戏创作者大陆 于 逗分享开发经验 发表