简介
BSP文件格式是QUAKE 2用于存储地图的一种文件格式,说得具体点,就是用于渲染Q2世界的。尽管有其他的信息包含在BSP文件中,用于其他游戏部分(如敌人AI,等等),在这篇文章中,我不将讨论他们。如果你有这方面的知识,请告诉我,我的E-MAIL在下面。在这篇文章中难免有错误之处,请告诉我。
为了更清楚的描述BSP文件格式,在这篇文章中将试图对Q2渲染引擎进行技术方面的描述。现在让我们假想这个渲染器使用的是基本的3D图形技术,包含BSP树结构。
我们在这篇文章中描述的是Q2的BSP文件格式,这个文件格式由Kingpin使用(他建立了Q2引擎)。对于Q1和Q3的文件格式,虽然他们很相同,但还是有点不兼容。不管怎样,BSP的文件结构和技术不是很难,这篇文章将对于那些对这些格式赶兴趣的人。
法律方面的问题
协定
在这篇文章中,我用C语言结构来描述这些问题。为了使描述简单并规范意义,我用int32 and uint32来表示有符号和无符号32位整形数;同样的符号将用于16位和32位整形数。下面为用于存储BSP文件格式的顶点和矢量的结构:
struct point3f
{
float x;
float y;
float z;
};
struct point3s
{
int16 x;
int16 y;
int16 z;
};
第二版中一些地方,如保存内存,就不需要很高的精度。相比于12位的版本,他只需要6位。通常情况下,都是用整形,而非浮点。
渲染
在这一节中,我们将介绍数据结构中的一些术语,并描述一下在Q2中进行渲染的过程。
Q2地图中划分成很多的凸区域,这些区域用BSP树页来表示。任何时候摄象机的位置随这些凸区域的水平方向改变。那一些树叶都相互的簇拥在一起,成一丛一丛的。这些丛如何构成是由建立BSP文件的工具决定。对于每个丛,他们都是以列表形式潜在,直观地存储。我们把这称为potentially visible set(PVS).
要渲染Q2地图,首先篇历BSP树,以决定摄象机的导入。一旦知道摄象机导入的叶,相应的丛也导入了(记住一片叶包含一个丛)。对应于丛的PVS然后解压出摄象机对应的那些PVS的列表。叶然后存储根据视角平截面裁减的叶的范围。
BSP树
许多人在通过用Q2和相似的引擎时,没有把BSP树和相应的算法联系起来。通过上面的叙述,我们知道可视的界面是由PVS决定。BSP树用于分割地图并快速地决定哪一块应该显示。结果,在Q2中没有好的渲染算法,而不得不用其他数据结构代替(如OCTREE OR K-D TREE)。BSP树不管怎样是非常简单的,他也被用在Q2引擎中其他不用渲染的任务中。
当讨论BSP树中存储叶结点的BSP树(leaf-based BSP trees)和存储内部结点的BSP树(node-based BSP trees)时。用在Q2中的BSP树的变化实际上是两方面的。这两方面是渲染和冲突检测。用于渲染时,他直接把存储在叶结点中的面渲染出来,因为他们是以PVS形式存储的。如果你没有执行冲突检测,存储在内部结点中的面将完全被忽约。
文件头
就我所知,所有的BSP文件是以字节的形式存储的,当用在大的终端平台上时,随着导入而变换。这使的相同的地图能够被使用不同平台的Q2用户共同使用。如果你在大的终端中导入BSP文件-------如Macintosh ,UNIX---你将不得不仔细的变换字节顺序。
BSP文件格式是用目录结构组织的,文件中所有的数据包含“自由的浮动”(free floating)团。在文件开始处,有一个目录告诉团的开始的偏移和长度。这个目录后的文件开始的8个字节是用于描述这些团的位置和长度。
BSP文件头的格式描述如下:
struct
bsp_lump
{
uint32 offset;
//从文件开始的数据的偏移(用字节表示)
uint32 length;
//数据的长度(用字节表示)
};
struct bsp_header
{
uint32 magic;
//magic号(“IBSP”)
uint32 version;
//BSP格式的版本(38)
bsp_lump lump[19];
//团的目录
};
BSP文件开始的4个字节是一个标记,用于标识文件是否一个软件BSP文件。这些字节被拼为“IBSP”。接下来是32位无符号整形数,用于指定版本号。正如上面说的一样,本文件的版本号为38。
接下来的表显示BSP文件中包含的不同的团,并且这些团的索引排列在文件头中。这些团的目的是用于标识不知道的问题标记(这个名字来源于QBSP源代码),不管怎样,他们是不用建构简单的Q2标准渲染器。
索引名
描述
0 Entities
MAP实体文本缓冲器
1 Planes
平面队列
2 Vertices
顶点队列
3 Visibility
压缩的PVS数据和所有的丛目录
4 Nodes
BSP树的内部结点数组
5 Texture Information
表面材质运用队列
6 Faces
表面队列
7 Lightmaps
光图
8 Leaves
BSP树的内部叶队列
9 Leaf Face Table 索引查找表,用于参考叶中的表面队列
10 Leaf Brush Table
?
11 Edges
边队列
12 Face Edge Table
索引查找表,用于参考叶中的边队列
13 Models
?
14 Brushes
?
15 Brush Sides
?
16 Pop
?
17 Areas
?
18 Area Portals
?
许多团是用结构队列的形式存储的,这些结构都是固定大小。例如,顶点团是一个POINT3F结构队列。由于每个POINT3F是12位,顶点值被12个顶点团分开进行计算。相似的计算也被用于面,结点,材质信息,表面,叶和边团。
在下一节,我们将讨论团的结构和作用。
顶点团
顶点团是一列世界的所有的顶点。每个结点是3个float浮点值,相当于12个字节。你能计算这些顶点值通过把这些顶点团的长度分为12来计算。(You can compute the numbers of vertices by dividing the length of the vertex lump by 12. ^_^不好意思,可能翻译错了)
Q2用一个协调系统通过Z-轴调整为向上。记住,就是你用其他的系统进行调节,你可能需要改变束缚盒和面方程式。
边团
在表面的顶点要共享,而且边也要共享。每一条边是存储在顶点队列中。存储量是两个16位的整形数,因此,在边队列中的边数是由边团的值除以4。这里要说的是可能有一条边被有卷绕着的线的两个面共享,因此对于一条边,没有特殊的方向。这一点将在表面团中讨论。
表面团
bsp_face结构队列中表面团的格式如下:
struct bsp_face
{
uint16 plane;
//表面平行的面的索引
uint16 plane_side;
//设置表面是否与面平行
uint32 first_edge;
//第一条边的索引(在表面边队列中)
uint16 num_edges;
//连续的边的数量(在表面边队列中)
uint16 texture_info;
//材质信息结构的索引
uint8
lightmap_syles[4];
//光图的类型
uint32 lightmap_offset;
//光图在光图团中的偏移
};
这个bsp_face结构的长度是20字节,面的数量可以由表面团除以20得到。