Quake 3 Level Format

and fun with culling!

Most information taken from

http://www.mralligator.com/q3/

Q3Radiant - Quake 3 level editor

Intermediate "editing" format: .map

.map files are strings, human-readable files

 

Then they're finalized and "baked" into a .bsp file, which the game executable reads

BSP

IBSP, or "Id BSP"

Binary

Search

Partitioning

Split geometry into subsections, mapping physical space in a tree data structure

 

This storage allows for some rendering optimizations to be made at runtime

Header

char[4] - always reads "IBSP"

int32 - indicates version of .bsp file format

               - Quake 3 will always read '46'

lump[17] - offsets and lengths to arrays of data

               - Lumps and version numbers differ from game

               to game that uses the Quake engine

struct lump
{
    int32 offset; //in bytes
    int32 length; //in bytes
}

17 kinds of lumps in version 46

[0] Entities

[1] Textures

[2] Planes

[3] Nodes

[4] Leaves

[5] Leaf Faces

[6] Leaf Brushes

[7] Models

[8] Brushes

[9] Brush Sides

[10] Vertices

[11] Indices

[12] Effects

[13] Faces

[14] Lightmaps

[15] Light Volumes

[16] Visibility Data

We'll just look at a few:

[1] Textures

[3] Nodes

[4] Leaves

[5] Leaf Faces

[7] Models

[10] Vertices

[11] Indices

[13] Faces

[14] Lightmaps

[16] Visibility Data

Textures

List of filenames (without extension)

Meant to point to a "material," which is really a texture + some shading logic

 

Most textures are just "use this image," though

struct texture
{
    char[64] name;
    int32 flags;
    int32 contents;
}

Vertices, Indices, and Faces

Topics for a different talk, but important

Basically a collection of 3D geometry to feed into the GPU

 

(Make your VBO and IBO from these)

struct vertex
{
    float[3] position;
    float[2] diffuseTexCoords;
    float[2] lightmapTexCoords;
    float[3] normal;
    ubyte[4] color;
}
//indices are just int32s
struct face
{
    int diffuseTexture;       
    int effect;
    int type;
    int vertexOffset;
    int numVertices;
    int indexOffset;
    int numIndices;
    int lightmapTexture;
    //... and a whole bunch more
}

Nodes and Leaves

BSP data is stored in a perfectly balanced tree

 

Leaf

Leaf

Leaf

Leaf

Node

Node

Node

struct node
{
    int32 plane;
    int32[2] children;
    int32[3] boundingBoxMins;
    int32[3] boundingBoxMaxes;
}
struct leaf
{
    int32 cluster;
    int32 area;
    int32[3] boundingBoxMins;
    int32[3] boundingBoxMaxes;
    int32 firstLeafFace;
    int32 numLeafFaces;
    int32 firstLeafBrush;
    int32 numLeafBrushes;
}

Main use for this BSP structure:

Frustum culling

Start at root and iterate:

Does frustum intersect with node bounding box?

If yes, render this node

    Nodes continue algorithm to children

    Leaves draw all faces in leaf

If not, return and don't render any children

Visibility Data

Potential Visible Set (PVS) Culling

On top of BSP, the level also has PVS data

Leaves are assigned to clusters

 

Clusters have list of what other clusters are visible from this cluster

 

If BSP solves outside-frustum visibility,

PVS solves occlusion visibility

"Cluster x is visible from cluster y if the

(1 << y % 8)

bit of 

visibilityData[x * sizeOfRows + y / 8]

is set."

struct visibilityDataHeader
{
    int32 numRows;
    int32 sizeOfRows;
}

ubyte visibilityData[numRows * sizeOfRows];

Live demo!

q3

By tdhoward