wmo(wow map object) research
The wmo是一个非常有趣的设计,wow中比较小的物体使用doodad,而building使用wmo,
这里的building可以是桥梁、了望台、简单的小房子、复杂点的旅馆这样的房屋、非常
复杂的建筑群(例如地下城场景),本文对wmo场景文件进行简单的介绍,关于wmo文件的具
体信息请参考wowmapview的source code,这里非常感谢ufoz所做的贡献。
1、命名规则
wmo保存在以.wmo结尾的文件中,这个文件使用数据块来保存数据。一个wmo通常由一个或多个
group组成,而group数据也保存在以.wmo结尾的文件中,不过文件名称存在不同,例如一个wmo
保存在name.wmo文件中,那么group文件名就为name_001.wmo name_002.wmo......
2、结构
wmo就组织结构来说包含两个层次,group和batch。一个group通常包含多个batch,其中group包含
一个AABB。batch是wmo最小的渲染单元,它保存了顶点索引列表,可以直接调用DP进行渲染。group
内保存一个标志位,可以将group分为indoor/outdoor两类,这一个信息非常重要,通过它wmo就将
building分成了内外两部分,outdoor group就是building的外壳,他通过portal与室内场景连接
在一起。
3、portal
wmo文件中保存了portal信息,在wmo中规定group必须通过portal进行连接,portal由PVS和PRS
两部分组成,PVS记录portal顶点信息,PRS记录portal和group的连接信息,PRS结构如下:
struct WMOPR {
int portal;
int group;
int dir;
};
需要注意的是dir,这个成员只有两个值-1或1,由于portal的顶点信息按照顺时针记录,因此group
位于portal的正面时dir为1,否则为-1,通过dir可以快速确定group到底位于portal的哪一侧。
通过wmo中记录的portal信息可以使用portal culling来检查group的可见性,但是这里还是有一些
难度,主要是指portal的记录方式。由于一个group可能有多个portal,而查找连接的portal只能
通过PRS,这样在大的场景中非常不方便。而且在wmo中竟然没有记录portal的plane信息,如何确定
camera到底是位于portal的正面还是反面呢?(现在wmo文件由于没有完全破解,存在一些wowmapview
未读入的数据块,例如MVER、MOPT、MOVV、MOVB等,其中MVER应该是wmo文件的版本号,MOPT怀疑是保
存所有plane信息,而MOVV可能是保存包围体顶点信息,而MOVB保存包围体信息,MOVV、MOVB应当用于
碰撞检测,这些暂时没有验证)我的做法是在载入时计算portal的plane信息,并将PRS信息转换为类似
Q3 BSP中portal的结构。
struct portal_t {
int othergroup;
int pvs;//pvs index
int dir;
};
struct group_t {
int firstportal;
int numportals;
};
4、碰撞检测
在wmo中并没有使用BSP、OC TREE这样的结构来进行场景管理,可能所有人都感觉非常困惑。
场景管理的功能主要是为了加速渲染和方便碰撞检测,由于存在portal,这样第一个功能已经完成。
而对于碰撞检测,我的想法应当是AABB TREE。仔细观察WOW的场景可以发现在indoor场景中曲面、斜面
这样的几何物体非常少,大多数是规则物体,因此可以判断在wmo中所有的物体都是严格按照轴对齐
方式进行建模,也就是对规则性物体AABB=OBB。由于MOVV和MOVB信息并没有完全研究透彻,因此关于
这一部分只能是我的猜测。
5、渲染
对wmo的渲染由于batch的存在从而变的简单化,但还有可以优化的地方。由于wmo中使用portal将其分割
成group,因此有大量的材质相同的model被分割成不同的batch,在渲染时将材质相同的batch合并到一起
渲染可以避免一些无谓的DP调用。wmo一个令人诟病的地方是使用vertex light,为了减少图元数量从而
使顶点数量降低,造成渲染的时候出现色带效果,应当加入lightmap,由于wmo的场景通常不大,预处理
时做radiosity的时间也不会太长。
6、动态载入
对于只包含几个group的小场景的wmo,由于载入时间不是太长,在动态载入时一次性载入对程序影响
并不会太大。但是对于超大场景的wmo就需要考虑载入策略,这样场景典型的就是wow中的地下城场景,
它一个wmo中包含了几百个group,一次性载入时间非常长,需要分段进行载入。此时就显示出wmo分
文件保存group的优势了,为了实现动态载入wmo场景,一种可能的做法是在载入wmo后需要根据camera
所在的group快速的建立group连接层次图,这个图通过PRS数据建立,建立流程如下:
一、将camera所在group作为当前group,获得所有相连的protal;
二、将protal连接的group保存到第一层列表中,遍历第一层列表中所有的group;
三、获得第一层列表中group的portal,检查portal所连接的group是否保存在第一层列表中,如果没有
将其保存到第二层列表中;
四、重复上述过程,直到整个层次图建立。
这个层次图可以预先建立然后保存到文件中运行时载入,这样wmo就是分层载入而不需要一次性载入。
(这里我考虑是否在wmo中也可以建立类似bsp的pvs数据呢?虽然pvs现在已经开始淘汰,但是如果
存在pvs就可以方便确定哪些group需要立即载入,只是不知被portal分割后的group到底是不是convex
hull,如果是的话可以建立pvs,但对建模时限制更加明显,两难的选择!!!)
7、建模
由于wmo是按照group对场景进行保存,因此为了建立wmo需要设计一个强力的模型构建工具,这个工具
主要功能就是对从建模工具(3DS MAX)中建立的场景模型进行分组和处理。美工在制作模型时需要非常
小心,所有的模型要严格的轴对齐(轴对齐的原因是需要模型的AABB=OBB),然后将模型导入工具中。
模型构建工具有以下功能:分组(group)功能、group选择、group显示/隐藏、指定portal,portal对
齐(考虑门、窗户这样天然的portal,手动指定portal时肯定无法与外表墙壁对齐,需要程序自动对齐)
、batch操作(分割、选择、显示/隐藏等)、图元级操作(triangle拣选,用于batch分割)、光照运算
(产生vertex light数据)、放置光源、放置doodad(场景中的道具,如桌子、椅子等)。可能还需要其
他一些功能,但是对比其他引擎的场景建模工具(hammer、sandbox)明显简单化许多。
8、优势及不足
当前处理室内场景的主流技术依然是bsp,但是随着硬件的发展bsp的优势在慢慢地丧失,bsp赖以生存的
预处理PVS现在已经完全被实时的portal culling所取代,bsp优势只剩下对图元排序(用于透明物体的
渲染)和基于brush的快速碰撞检测上,但是对比建模工具的复杂化和场景的限制,采用bsp的开销确实
显得太大。而基于纯portal引擎的结构开始流行,例如cryengine中处理室内场景时就完全抛弃bsp,
场景完全由一块块固定大小的墙壁组成,一块墙壁基本和bsp中brush类似,这样做的好处是建模工具变
的简单(不需要进行CSG运算),而且非常容易的产生portal,同时由于场景使用brush构成也兼具了
bsp方便进行碰撞检测的优势。wmo有些类似cryengine,但是在某些方面更具优势。
首先在建模方面,wmo的场景完全可以通过成熟的建模工具来构建,这样对于美工不需要重新学习新的
建模工具,可以节约大量的时间。其次模型构建工具需要的功能非常少,减少了程序的复杂性,缩短了
编写相关工具的时间。再次,场景管理简单化,相应代码量大幅度减少,同时由于portal的存在,可以方便
的与其他引擎相对接。可以说wmo是一种可以进行快速开发的场景结构。