This document was created as an aid to the Quake3 BSP  tutorial series featured on      The information is what I have found, and it's possibly that it's incorrect or just blatently wrong.  I suggest you use this as a reference and a guide, not the end all file format doc for the Quake3 .bsp file format.  With that out of the way, let's load some sweet levels!

这篇文章是为GAMETURORIALSCOMQUAK3 BSP系列教程提供指导而写的。文章内提供的信息都是我发现的,而且很可能是不准确或完全错误的。所以我建议你把它作为一个参考或指导,而不是QUAKE3 BSP文件格式标准指南。扯远了,还是让我们开始载入美丽的关卡吧。

The Quake3 level format, .bsp, stores most of the information about the level.  There are other files such as .shader, .arena and .aas, which store bot and texture shader information.  The .bsp file is stored in what is called a IBSP format.  That means that the length and offsets of different sections in the file are stored in what's know as lumps.  The older version of Quake use this same lump format, but different information is stored in each version of Quake.  If you can read in Quake 3 levels, it's not a lot of changes to write a Quake 2 level loader.


If you don't know what BSP stands for yet, it means Binary Space Partition(ing).  You would create a BSP tree.  That means that there is a parent node, and at most, 2 children attached to each parent.  These children are called the front and back children.  I won't attempt to teach you how to create or manage a BSP tree here, but there is a BSP FAQ that SGI put out floating around the internet somewhere that has a ton of information.  Better yet, I suggest you take the BSP class at  I personally took this class and was quite satisfied.  It teaches all you need to know about BSP trees.

如果你还不知道BSP代表了什么,那么让我来告诉你。他是二叉数空间分割的简写。你要建一个BSP树,那么就有了一个父节点,而且通常每个父节点都是连接着两个子节点。这些子节点分别叫做前子节点和后子节点。在这里,我不想教会你如果创建或管理一棵二叉树,但是你可以在SGI在互联网上创建的BSP FAQ中的成千上万的信息中找到你所需要的。如果还不够,我建议你去GAMEINSTITUTECOM看一下BSP教程。我个人认为这个教程还是很不错的,它能教会你掌握BSP树所需要所有知识。


Like we mentioned before, lumps hold the length in bytes and offset into the file for a given section.  Below is an enum eLumps that holds all the lumps and their order in the file:


enum eLumps
    kEntities = 0,     // Stores player/object positions, etc...
    kTextures,         // Stores texture information
    kPlanes,           // Stores the splitting planes
    kNodes,            // Stores the BSP nodes
    kLeafs,            // Stores the leafs of the nodes
    kLeafFaces,        // Stores the leaf's indices into the faces
    kLeafBrushes,      // Stores the leaf's indices into the brushes
    kModels,           // Stores the info of world models
    kBrushes,          // Stores the brushes info (for collision)
    kBrushSides,       // Stores the brush surfaces info
    kVertices,         // Stores the level vertices
    kMeshVerts,        // Stores the model vertices offsets
    kShaders,          // Stores the shader files (blending, anims..)
    kFaces,            // Stores the faces for the level
    kLightmaps,        // Stores the lightmaps for the level
    kLightVolumes,     // Stores extra world lighting information
    kVisData,          // Stores PVS and cluster info (visibility)
    kMaxLumps          // A constant to store the number of lumps

Each on of these sections has a offset and a size in bytes that need to be read in.  In the next sections we will examine the structures needed to read in each lump.


Here is a lump structure.  The offset is the position into the file that is the starting point of the current section.  The length is the number of bytes that this lump stores.

struct tBSPLump
    int offset;
    int length;

Let's give an example of reading in the vertices (kVertices) for the level.  Once the lumps are read in, to find the number of vertices in the level we do this:


numOfVerts = lumps[kVertices].length / sizeof(tBSPVertex);

We index the lumps[] array with the kVertices constant, then divide that lumps length by the size of the tBSPVertex structure in bytes, which we will define later on.  It just so happens it's 44 bytes.  If the length is 3388, then 3388 / 44 = 77.  We now know there is 77 vertices in the .bsp file.  We then need to position the file pointer to the lump's offset, and start reading in 77 tBSPVertex structures into our dynamically allocated vertex array.  I use fread() and fseek() for the file manipulation.  This is of course, ONLY if you are not reading from the .zip file.  I am strictly speaking of loading the .bsp file unzipped.

我们用KVERTICES常数来在LUMPS[]数组中索引,然后再用以字节为单位的TBSPVERTEX结构的大小去除。这个结构我们会在后边定义,它刚刚是44字节。如果我们得到长度数值是3388,那么3388/44 = 77。我们就知道了在BSP文件中一共有77个顶点。接着我们把文件的指针指向LUMPOFFSET的位置,然后在我们动态分配的顶点数组中载入这77TBSPVERTEX结构。我个人使用FREAD()和FSEEK()来进行文件操作。在这里补充一下,上面我所说的仅仅是装载未压缩的BSP文件。

Now that we understand the basics of lumps, let's move on to the header structure, along with the rest of the structures for each lump read in.


BSP Header


The very first thing that needs to be read in for the .bsp file is the header.  The header contains a 4 character ID, then an integer that holds the version.



struct tBSPHeader


    char strID[4];     // This should always be 'IBSP'

    int version;       // This should be 0x2e for Quake 3 files






This structure stores the vertex information.  There is a position, texture and lightmap coordinates, the vertex normal and color.  To calculate the number of vertices in the lump you divide the length of the lump by the.



struct tBSPVertex


    float vPosition[3];      // (x, y, z) position.

    float vTextureCoord[2];  // (u, v) texture coordinate

    float vLightmapCoord[2]; // (u, v) lightmap coordinate

    float vNormal[3];        // (x, y, z) normal vector

    byte color[4];           // RGBA color for the vertex





This structure holds the face information for each polygon of the level.  It mostly holds indices into all the vertex and texture arrays.  To calculate the number of faces in the lump you divide the length of the lump by the sizeof(tBSPFace).



struct tBSPFace


    int textureID;        // The index into the texture array

    int effect;           // The index for the effects (or -1 = n/a)

    int type;             // 1=polygon, 2=patch, 3=mesh, 4=billboard

    int vertexIndex;      // The index into this face's first vertex

    int numOfVerts;       // The number of vertices for this face

    int meshVertIndex;    // The index into the first meshvertex

    int numMeshVerts;     // The number of mesh vertices

    int lightmapID;       // The texture index for the lightmap

    int lMapCorner[2];    // The face's lightmap corner in the image

    int lMapSize[2];      // The size of the lightmap section

    float lMapPos[3];     // The 3D origin of lightmap.

    float lMapVecs[2][3]; // The 3D space for s and t unit vectors.

    float vNormal[3];     // The face normal.

    int size[2];          // The bezier patch dimensions.




If the face type is 1 (normal polygons), the vertexIndex and numOfVerts can be used to index into the vertex array to render triangle fans.



If the face type is 2 (bezier path), the vertexIndex and numOfVerts act as a 2D grid of control points, where the grid dimensions are described by the size[2] array.  You can render the bezier patches with just the vertices and not fill in the curve information, but it looks horrible and blocky. 


The point of the curved surfaces are to be able to create a more defined surface, depending on the specs of the computer that is running that application.  Some computers with horrible speed and video cards would make the smallest amount of polygons from the curve, where as the fast computers using Geforce cards could use the highest amount of polygons to form a perfect curve.


If the face type is 3 (mesh vertices), the vertexIndex and numOfVerts also work the same as if the type is 1


If the face type is 4, the vertexIndex is the position of the billboard.  The billboards are used for light effects such as flares, etc...


想对作者说点什么? 我来说一句


2018年01月23日 11.56MB 下载