Ogre学习笔记(6):BspSceneManager

本文深入探讨Ogre引擎如何支持Quake3的BSP格式,重点介绍了BspSceneNode、BspSceneManager、BspLevel等关键类的作用和数据结构,以及BSP树的加载和渲染流程。通过解析Quake3 BSP文件格式,展示了从文件加载到场景渲染的详细步骤。
摘要由CSDN通过智能技术生成

1. 概览】

       Ogre支持Quake3bsp格式。相关的代码在“Plugin_BSPSceneManager”工程中。主要的类有以下几个:

 

Class BspSceneNode

       BspSceneNodeSceneNode的派生类,是专门提供给BSPSceneManager使用的。主要是提供针对于BSP tree的可见性判断。这个类并不是BSP treenodeBSP tree中的node使用BspNodeBspSceneNode会放入BSP treeleaf节点中。由于SceneNode使用包裹盒的方法,不可分割,所以一个BspSceneNode可能放入多个Bsp treeleaf节点中。

       从类的定义看,BspSceneNode并没有额外的保存什么数据。重写的几个虚函数主要是用来通知BspSceneMapagerBspSceneNode::_update()会调用BspSceneManager::_notifyObjectMoved()detach objcect会调用BspSceneManager::_notifyObjectDetached()

 

Class BspSceneManager

       粗略的看BspSceneManagerOctreeSceneManager类似。首先保存了一个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的所有数据,关键的数据有:

1.         BspNode* mRootNode;”――BSP tree的根节点

2.         VertexData* mVertexData;”――整个level的所有顶点;

3.         StaticFaceGroup* mFaceGroups;”――faces

4.         BspNode::Brush *mBrushes;”――用来做碰撞检测的Brush,是QuakeBSP除了渲染以外的另外一个精华!Brush的名字有点怪,其实就是一个convex volume,可以减少CD的运算量。

5.         VisData mVisData;”――PVS数据,是又一个Quake中的精华!当初Carmark在设计Quake的时候还使用软件渲染,hiden surface removal和减少over draw是最另他头痛的问题。BSP的思想应该是他从网上看来的,不过PVS应该是他所创。PVS大大减少了over draw(见《Michael Abrash's Graphics Programming Black Book)

6.         PatchMap mPatches;”――Quake3支持贝赛尔曲面

关键的函数:

1.  bool isLeafVisible(const BspNode* from, const BspNode* to) const;使用PVS来检测可见性。

2.  void _notifyObjectMoved(const MovableObject* mov, const Vector3& pos);
void _notifyObjectDetached(const MovableObject* mov);
à void tagNodesWithMovable(BspNode* node, const MovableObject* mov, const Vector3& pos);
MovableObject(注意:不是SceneNode)挂到BSPleaves上。

 

Class BspNode

       这是Bsp中的另外一个重要的类了。NodeLeaf都使用这个类。

       重要数据:

1.  Plane mSplitPlane;     BspNode* mFront;     BspNode* mBack;
分割平面和前后节点;

2.  int mVisCluster;
每个clusterpvs的一个bit,这是为了减少pvs占用的内存。

3.  int mNumFaceGroups;    int mFaceGroupStart;
用来找到BspLevel中哪些face group是属于我这个leaf的,这样做也是为了优化存储;

4.  IntersectingObjectSet mMovables;
和本节点相交的movable对象

5.  NodeBrushList mSolidBrushes;
本节点包含的brush

另外剩下的OgreQuake3Level.hOgreQuake3Shader.hOgreQuake3ShaderManager.hOgreQuake3Type.h主要是为了把Quake3格式的bspshader信息读入,并转换成Ogre本地的bsp定义以及Material。现在quake3的源码已经公开(非常感谢id software以及carmark),可以结合quake3的源码来看。

 

2. Quake3 bsp的加载】

       Demo_BSP为例,首先需要修改“quake3settings.cfg”,两个参数,“Pak0Location”是pk包的路径(是一个zip文件),“Map”为想要加载的地图。

       OGRE使用BspLevel来存储Bsp场景信息,这个类是与文件格式无关的。所以需要另外一个类来把Quake3bsp文件读入。

       Quake3Level的读盘的主要由两个函数完成:

1、“void Quake3Level::loadHeaderFromStream()”。调用的流程是:

BspApplication::loadResources()

à ResourceGroupManager::loadResourceGroup()A

à BspSceneManager::estimateWorldGeometry()

à BspLevel::calculateLoadingStages()

àQuake3Level::loadHeaderFromStream()

       Quake3 BSP的文件格式很简单明了,前面是一个文件头,后面是几个数据块。文件头主要存储了几个lump,包含数据块的起始位置和大小,通过lump,可以直接seek到对于的数据块。
      
此函数在加载了文件头之后,调用了Quake3Level:: initialiseCounts ()函数,主要是计算了每个lump包含的对象的个数,例如facevertexbursh等等。

       2、第二个函数“void Quake3Level::loadFromStream()”。调用的过程是:

ResourceGroupManager::loadResourceGroup()【A

àBspSceneManager::setWorldGeometry(

àBspResourceManager::load(

àResourceManager::load(

àResource::load()

àBspLevel::loadImpl()

àQuake3Level::loadFromStream(

在这地方OGRE延续了他罗嗦的风格,BspSceneManager要通过BspResourceManager来加载场景,BspLevel实现为一种ResourceBspResourceManager通过标准的ResourceManager――》Resource来找到BspLevel,然后调用其加载函数。

 

此函数首先构造了一个“MemoryDataStream”对象,在MemoryDataStream的构造函数中把文件数据全部读入其缓冲中(Quake也是这样干的),然后调用“void Quake3Level::initialisePointers(void)”函数,得到所有lump索引的对象的指针。

 

Quake3Level把文件读入并明确了所有数据的指针之后,在void BspLevel::loadImpl()中调用“BspLevel::loadQuake3Level()”函数讲Quake3level中的数据拷贝到自己的数据对象中。主要执行了以下几个操作:

1.         BspLevel::loadEntities()”,这个lump存的是一个字符串,用来描述一些游戏信息,Ogre的这个函数只读取了Player start信息(位置和角度)。

2.         Quake3Level::extractLightmaps()”。Quake3 BSP的每个light map都是128×128大,此函数将Light map lump中的数据逐个调用“TextureManager::loadImage()”创建成Texture对象(class D3D9Texture for D3D9 RenderSystem)。

3.         创建VertexData:
[Create vertex declaration] OGRE BspLevel
使用的顶点格式为:Position3Normal3Diffuseuv0uv1
[Build initial patches]
调用BspLevel::initQuake3Patches()。此函数遍历Quake3Level中的所有faces,对于每个face type为“BSP_FACETYPE_PATCH”的face,创建一个PatchSurface对象,并调用PatchSurface:: defineSurface()函数进行,然后保存到BspLevel:: mPatches数组中。此函数还计算了BspLevel:: mPatchVertexCountBspLevel:: mPatchIndexCount
[
硬件顶点缓冲] 调用HardwareBufferManager创建HardwareVertexBuffer对象;使用“BspLevel::quakeVertexToBspVertex()”函数把q3 bsp顶点格式转换为Ogre BSPLevel的顶点格式。然后绑定到BspLevel::mVertexData

4.         创建Faces:创建BspLevel:: mLeafFaceGroups数组;创建BspLevel:: mFaceGroups数组,此数组的数据在后面一步中填充;创建indexbuffer,并将Quake3Level::mElements拷贝进来;

5.         Create materials for shaders:对于Quake3Level::mFaces每一个bsp_face_t,找到它索引的Quake3Shader,并创建Material,如果没有找到Quake3Shader的话则使用shader name去查找贴图文件;
在此循环中还进行了“Copy face data”的操作,填充BspLevel:: mFaceGroups中的数据;

6.         Nodes:创建BspLevel:: mRootNode数组,并将数据拷贝进来。

7.         Brushes:将数据拷贝到BspLevel:: mBrushes中;

8.         Leaves:设置每个leaf节点的数据,主要包括包裹盒,mFaceGroupStartmNumFaceGroupsmVisClustermSolidBrushes。参见BspNode类;

9.         Vis data:将数据拷贝到BspLevel:: mVisData中。

Quake3 BSPload流程基本上就是这些了。

3. Bsp tree scene的渲染】

       仍然以Demo_BSP为例来分析。渲染的核心操作流程从SceneManager::_renderScene()开始(参见“Ogre学习笔记(3):Mesh的渲染流程”),接下来还有SceneManager:: _updateSceneGraph()SceneManager::prepareRenderQueue()BspSceneManager没有重写这几个函数。不过,有一点需要注意,SceneManager:: _updateSceneGraph()调用了BspSceneNode::_update()OctreeSceneManager类似的,如果有必要的话,会调用BspSceneManager:: _notifyObjectMoved()--》BspLevel:: _notifyObjectMoved(),将SceneNode中的MovableObject attach到正确的leaf node中。
      
接下来是BspSceneManager:: findVisibleObjects(),这是一个从SceneManager重写的函数。顺理成章的,这个函数调用了BspSceneManager::walkTree()。在这个函数中,首先找到了camera所在的leaf node(通过BspLevel::findleaf()函数);然后遍历BspLevel中的每个leaf node,先使用PVS检测可见性(通过BspLevel::isLeafVisible()函数),如果可见再使用camera――bounding box检测,如果还是可见的,则对此leaf node调用BspSceneManager::processVisibleLeaf()函数。此函数主要执行两个操作,一个是把World Geometry加入到渲染数据表中(mFaceGroupSetmMatFaceGroupMap),另外一个是把与此leaf node相交的MoveableObject加到渲染队列中(mMovablesForRendering)。一件比较有疑问的事情是,walkTree是循环遍历所有leaf node,而没有按照BSP tree递归遍历,这大大削弱了BSP的提前排序的优势。

       然后是BspSceneManager重写了另外一个重要的函数_renderVisibleObjects()。此函数包含两个操作,一个是renderStaticGeometry(),另外一个是调用父类的SceneManager::_renderVisibleObjects()。前者循环遍历mMatFaceGroupMap,然后调用RenderSystem::_rendr();后者已经在“Ogre学习笔记(3):Mesh的渲染流程”中详细分析过了。

 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值