|
Quake 3 BSP [Reading in File Information]
Author bkenwright@xbdev.net
 | Quake 3 BSP |  |
Let's get our hands dirty and jump into the code. Implementing an Q3 BSP reader/parser isn't as hard as you'd think.
As you'll see by looking at the code below the Quake 3 file format is pretty well layed out.
It's mostly just breaking it down and analysing the data.
#include <stdio.h> // fopen(..), fclose(..)
#define SZ_FILE_BSP "test_level.bsp" // Quake 3 BSP Level File
//------------------------- Various Structure Definitions ------------------------//
struct stLump
{
int fileofs;
int filelen;
};
struct stHeader
{
char id[4];
int ver;
stLump lumps[17];
};
//-----------------------Write Debug Feedback Information-------------------------//
void abc(char *str)
{
FILE *fp = fopen("dbg.txt", "a+");
fprintf(fp, "%s", str);
fclose(fp);
}
//-------------------------- Program Entry Point ---------------------------------//
void main()
{
abc("<->Opening File\n");
// Open BSP File
FILE * fp = fopen(SZ_FILE_BSP, "rb");
// Temporary Buffer
stHeader Data;
fread(&Data, // Buffer
sizeof(stHeader), // Size
1, // Number of times
fp); // File Pointer
// Temp string buffer
char buf[100];
sprintf(buf, "<?>stHeader:id = %c%c%c%c\n",
Data.id[0], // 'I'
Data.id[1], // 'B'
Data.id[2], // 'S'
Data.id[3]);// 'P'
abc(buf);
sprintf(buf, "<?>stHeader:ver = 0x%X\n",
Data.ver); // 0x2E for Quake3
abc(buf);
abc("<->Closing File\n");
// Close BSP File
fclose(fp);
}//End main()
Output File:
<->Opening File
<?>stHeader:id = IBSP
<?>stHeader:ver = 0x2E
<->Closing File
Taking it a stage further we can get now dump the location in our file of the various lump data!
Download Source Code
#include <stdio.h> // fopen(..), fclose(..)
#define SZ_FILE_BSP "test_level.bsp" // Quake 3 BSP Level File
//----------------------- Various BSP Lump Data Types-----------------------------//
enum BSP_TYPES
{
Entities=0,
Textures,
Planes,
Nodes,
Leaves,
LeafFaces,
LeafBrushes,
Models,
Brushes,
BrushSides,
Vertices,
MeshIndices,
Effect,
Faces,
Lightmaps,
LightVols,
VisData
};
char szBSPTYPES [17][20] = {
"Entities",
"Textures",
"Planes",
"Nodes",
"Leaves",
"LeafFaces",
"LeafBrushes",
"Models",
"Brushes",
"BrushSides",
"Vertices",
"MeshIndices",
"Effect",
"Faces",
"Lightmaps",
"LightVols",
"VisData" };
//------------------------- Various Structure Definitions ------------------------//
struct stLump
{
int nFileofs;
int nFileLen;
};
struct stHeader
{
char cMagic[4];
int nVersion;
stLump Lumps[17];
};
//-----------------------Write Debug Feedback Information-------------------------//
void abc(char *str)
{
FILE *fp = fopen("dbg.txt", "a+");
fprintf(fp, "%s", str);
fclose(fp);
}
//-------------------------- Program Entry Point ---------------------------------//
void main()
{
abc("<->Opening File\n\n");
// Open BSP File
FILE * fp = fopen(SZ_FILE_BSP, "rb");
// Temporary Buffer
stHeader Data;
fread(&Data, // Buffer
sizeof(stHeader), // Size
1, // Number of times
fp); // File Pointer
// Temp string buffer
char buf[100];
sprintf(buf, "<?>stHeader:cMagic = %c%c%c%c\n",
Data.cMagic[0], // 'I'
Data.cMagic[1], // 'B'
Data.cMagic[2], // 'S'
Data.cMagic[3]); // 'P'
abc(buf);
sprintf(buf, "<?>stHeader:ver = 0x%X\n\n",
Data.nVersion); // 0x2E for Quake3
abc(buf);
// Get the pointer to our array of lumps
stLump * pLumps = Data.Lumps;
// Display all the different types of Lumps
for(int i=0; i<17; i++)
{
sprintf(buf, "<?>\tLump:(%d):%s"
"FileOffset: 0x%X"
"Length: 0x%X\n",
i+1,
szBSPTYPES[i],
pLumps[i].nFileofs,
pLumps[i].nFileLen);
abc(buf);
}
abc("\n<->Closing File\n");
// Close BSP File
fclose(fp);
}//End main()
Debug File Output:
<->Opening File
<?>stHeader:cMagic = IBSP
<?>stHeader:ver = 0x2E
<?> Lump:(1): EntitiesFileOffset: 0x355FCLength: 0x948
<?> Lump:(2): TexturesFileOffset: 0x90Length: 0x318
<?> Lump:(3): PlanesFileOffset: 0x3A8Length: 0xD00
<?> Lump:(4): NodesFileOffset: 0x2308Length: 0xD80
<?> Lump:(5): LeavesFileOffset: 0x10A8Length: 0x1260
<?> Lump:(6): LeafFacesFileOffset: 0x49D8Length: 0x4F4
<?> Lump:(7): LeafBrushesFileOffset: 0x4ECCLength: 0x364
<?> Lump:(8): ModelsFileOffset: 0x5230Length: 0x28
<?> Lump:(9): BrushesFileOffset: 0x3088Length: 0x450
<?> Lump:(10): BrushSidesFileOffset: 0x34D8Length: 0x1500
<?> Lump:(11): VerticesFileOffset: 0x5258Length: 0x11D24
<?> Lump:(12): MeshIndicesFileOffset: 0x35F44Length: 0x34E0
<?> Lump:(13): EffectFileOffset: 0x35F44Length: 0x0
<?> Lump:(14): FacesFileOffset: 0x16F7CLength: 0x3AE8
<?> Lump:(15): LightmapsFileOffset: 0x1ABBCLength: 0x18000
<?> Lump:(16): LightVolsFileOffset: 0x32BBCLength: 0x2A40
<?> Lump:(17): VisDataFileOffset: 0x1AA64Length: 0x158
<->Closing File
We have a considerable amount of information, but nothing we can really get our teeth into and use. We'll put together a load of structures so we can actually read in all our data. Probably the most important information is the vertices and mesh indices. So we'll dump them to our file and make sure they look write.
#include <stdio.h> // fopen(..), fclose(..)
#include <assert.h> // assert(..)
#define SZ_FILE_BSP "test_level.bsp" // Quake 3 BSP Level File
//----------------------- Various BSP Lump Data Types-----------------------------//
enum BSP_TYPES
{
enEntities=0,
enTextures,
enPlanes,
enNodes,
enLeaves,
enLeafFaces,
enLeafBrushes,
enModels,
enBrushes,
enBrushSides,
enVertices,
enMeshIndices,
enEffect,
enFaces,
enLightmaps,
enLightVols,
enVisData
};
char szBSPTYPES [17][20] = {
"Entities",
"Textures",
"Planes",
"Nodes",
"Leaves",
"LeafFaces",
"LeafBrushes",
"Models",
"Brushes",
"BrushSides",
"Vertices",
"MeshIndices",
"Effect",
"Faces",
"Lightmaps",
"LightVols",
"VisData" };
//------------------------- Various Structure Definitions ------------------------//
struct stLump
{
int nFileofs; // Offset to start of lump, relative to beginning of file.
int nFileLen; // Length of lump. Always a multiple of 4.
};
struct stHeader
{
char cMagic[4];
int nVersion;
stLump Lumps[17];
};
//----------------------------- Lump Definitions ---------------------------------//
typedef float Vector2[2];
typedef float Vector3[3];
typedef float Vector4[4];
typedef float TexCoord[2];
typedef int nBBox[6]; // Integer bounding box (mins, maxs)
typedef float fBBox[6]; // Float bounding box
//Lump 0
//Entities
char *entities; // A pointer to text
//Lump 1
//Shader Texture Info
struct stShaderRef
{
char Name[64]; // Texture name
int nSurfaceFlags; // Type of surface (See Surface Flags below)
int nContentFlags; // Leaf content (See Content Flags below)
};
//Lump 2
//Planes
struct stPlane
{
Vector3 Normal; // Normal vector for plane
float fDist; // Distance from plane to origin
};
//Lump 3
//Nodes
struct stNode
{
int nPlane; // Space partitioning plane
int nChildren[2]; // Back and front child nodes
nBBox BBoxI; // Bounding box of node
};
//Lump 4
//Leaves
struct stLeaf
{
int nCluster; // Visibility cluster number
int nArea; // Volume of the leaf
nBBox BBoxI; // Bounding box of leaf
int nFirstFace,
NumFaces; // Lookup for the face list (indexes
// are for faces)
int nFirstBrush,
NumBrushes; // Lookup for the brush list (indexes
// are for brushes)
};
//Lump 5
//Leaf Faces
int *pFaces; // a pointer to a series of indexes to
// a face list
//Lump 6
//Leaf Brushes
int *pBrushes; // a pointer to a series of indexes to
// a brush list
//Lump 7
//Models
struct stModel
{
fBBox BBoxF; // Bounding box of model
int nFirstFace, // First face for model
NumFaces; // Number of faces for model
int nFirstBrush, // First brush for model
NumBrushes; // Number of brushes for model
};
//Lump 8
//Brushes
struct stBrush
{
int nFirstSide, // First brushside for brush
NumSides; // Number of brushsides for brush
int nIndex; // Texture index
};
//Lump 9
//Brush Sides
struct stBrushSide
{
int PlaneNum; // Lookup for plane
int nIndex; // Texture index
};
//Lump 10
//Vertices
struct stVertex
{
Vector3 vPoint; // Vertex Position
TexCoord Tex; // Texture coordinates
TexCoord LightTexCoord; // Light Map texture coordinates
Vector3 vNormal; // Normal vector (used for lighting ?)
unsigned int RGBA; // Vertex color. RGBA
};
//Lump 11
//MeshVert
int nOffset; // Vertex index offset, relative to first
// vertex of corresponding face.
//Lump 12
//Effect
struct stEffect
{
char cName[64]; // Effect shader.
int nBrush; // Brush that generated this effect.
int Unknown; // Always 5, except in q3dm8, which has
// one effect with -1.
};
//Lump 13
//Faces
#pragma pack(1)
struct stFace // size 24*4
{
int nShader; // Refers to a shader
int nEffect; // Index into lump 12 (Effects), or -1
int nFaceType; // Face type. 1=polygon, 2=patch, 3=mesh, 4=billboard
int FirstVert,
NumVerts; // Reference to vertices
int nFirstMeshVerts, // Index of first meshvert
// Every three meshverts describe a triangle
NumMeshVerts; // Number of meshverts
int LMIndex; // Lightmap index.
int LMStart[2]; // X,Y Corner of this face's lightmap image in lightmap.
int LMSize[2]; // Size of lightmap
float LMOrigin[3]; // World space origin of lightmap.
float LMVects[2][3]; // World space lightmap s and t unit vectors.
Vector3 vNormal; // Face normal
int nSize[2]; // Patch dimensions.
};
#pragma pack()
//Lump 14
//Lightmaps
unsigned char pMap[128][128][3]; // Lightmap color data. RGB.
//Lump 15
//Light Grid
//Unknown
//Lump 16
//Visibility Lists
struct stVisibility
{
int NumVectors; // Number of vectors.
int nSizeVector; // Size of each vector, in bytes
unsigned char *pData; // [NumVectors * nSizeVector];
// Visibility data. One bit per cluster per vector
};
//Lump 17
//Number of Lumps
//Unknown
//-----------------------Write Debug Feedback Information-------------------------//
void abc(char *str)
{
FILE *fp = fopen("dbg.txt", "a+");
fprintf(fp, "%s", str);
fclose(fp);
}
//-------------------------- Program Entry Point ---------------------------------//
void main()
{
abc("<->Opening File\n\n");
// Open BSP File
FILE * fp = fopen(SZ_FILE_BSP, "rb");
// Temporary Buffer
stHeader Data;
fread(&Data, // Buffer
sizeof(stHeader), // Size
1, // Number of times
fp); // File Pointer
// Temp string buffer
char buf[100];
sprintf(buf, "<?>stHeader:cMagic = %c%c%c%c\n",
Data.cMagic[0], // 'I'
Data.cMagic[1], // 'B'
Data.cMagic[2], // 'S'
Data.cMagic[3]); // 'P'
abc(buf);
sprintf(buf, "<?>stHeader:ver = 0x%X\n\n",
Data.nVersion); // 0x2E for Quake3
abc(buf);
// Get the pointer to our array of lumps
stLump * pLumps = Data.Lumps;
// Display all the different types of Lumps
for(int i=0; i<17; i++)
{
sprintf(buf, "<?>\tLump:(%d):%s"
"FileOffset: 0x%X"
"Length: 0x%X\n",
i+1,
szBSPTYPES[i],
pLumps[i].nFileofs,
pLumps[i].nFileLen);
abc(buf);
}
//------------------------------------Vertices------------------------------------//
stLump * pLump = &pLumps[enVertices];
fseek( fp,
pLump->nFileofs,
SEEK_SET );
char * pVData = new char[pLump->nFileLen];
assert(pVData);
fread( pVData, // Buffer
1, // Size
pLump->nFileLen, // Number of times
fp ); // File Pointer
stVertex * pVerts = 0;
pVerts = (stVertex*)pVData;
int nNumVerts = pLump->nFileLen / sizeof(stVertex);
sprintf(buf, "\n<?>Num Vertices: %d\n", nNumVerts);
abc(buf);
for(i=0; i<nNumVerts; i++)
{
// We will only display some of the information
sprintf(buf,"<?>Point:(%.2f,%.2f,%.2f) "
"Normal(%.2f,%.2f,%.2f) "
"RGBA(0x%X)\n",
pVerts[i].vPoint[0], // x
pVerts[i].vPoint[1], // y
pVerts[i].vPoint[2], // z
pVerts[i].vNormal[0],// nx
pVerts[i].vNormal[1],// ny
pVerts[i].vNormal[2],// nz
pVerts[i].RGBA ); // rgba
abc(buf);
}//End for i
delete[] pVData;
//------------------------------------ Faces -------------------------------------//
pLump = &pLumps[enFaces];
fseek( fp,
pLump->nFileofs,
SEEK_SET );
char * pFData = new char[pLump->nFileLen];
assert(pFData);
fread( pFData, // Buffer
pLump->nFileLen, // Size
1, // Number of times
fp ); // File Pointer
stFace * pFaces = 0;
pFaces = (stFace*)pFData;
int nNumFaces = pLump->nFileLen / sizeof(stFace);
// Display Information
sprintf(buf, "\n<?>Num Faces: %d\n", nNumFaces);
abc(buf);
for(i=0; i<nNumFaces; i++)
{
// We will only display some of the information
sprintf(buf,"<?>FirstVert(%d) "
"NumVerts(%d) "
"FirstMeshVert(%d) "
"NumMeshVerts(%d) \n",
pFaces[i].FirstVert,
pFaces[i].NumVerts,
pFaces[i].nFirstMeshVerts,
pFaces[i].NumMeshVerts);
abc(buf);
}//End for i
delete[] pFData;
abc("\n<->Closing File\n");
// Close BSP File
fclose(fp);
}//End main()
Output Information (well most of it):
<->Opening File
<?>stHeader:cMagic = IBSP
<?>stHeader:ver = 0x2E
<?> Lump:(1):EntitiesFileOffset: 0x355FCLength: 0x948
<?> Lump:(2):TexturesFileOffset: 0x90Length: 0x318
<?> Lump:(3):PlanesFileOffset: 0x3A8Length: 0xD00
<?> Lump:(4):NodesFileOffset: 0x2308Length: 0xD80
<?> Lump:(5):LeavesFileOffset: 0x10A8Length: 0x1260
<?> Lump:(6):LeafFacesFileOffset: 0x49D8Length: 0x4F4
<?> Lump:(7):LeafBrushesFileOffset: 0x4ECCLength: 0x364
<?> Lump:(8):ModelsFileOffset: 0x5230Length: 0x28
<?> Lump:(9):BrushesFileOffset: 0x3088Length: 0x450
<?> Lump:(10):BrushSidesFileOffset: 0x34D8Length: 0x1500
<?> Lump:(11):VerticesFileOffset: 0x5258Length: 0x11D24
<?> Lump:(12):MeshIndicesFileOffset: 0x35F44Length: 0x34E0
<?> Lump:(13):EffectFileOffset: 0x35F44Length: 0x0
<?> Lump:(14):FacesFileOffset: 0x16F7CLength: 0x3AE8
<?> Lump:(15):LightmapsFileOffset: 0x1ABBCLength: 0x18000
<?> Lump:(16):LightVolsFileOffset: 0x32BBCLength: 0x2A40
<?> Lump:(17):VisDataFileOffset: 0x1AA64Length: 0x158
<?>Num Vertices: 1659
<?>Point:(-56.00,528.00,64.00) Normal(1.00,0.00,0.00) RGBA(0xFF020603)
<?>Point:(-56.00,528.00,128.00) Normal(0.71,0.00,-0.71) RGBA(0xFF030808)
<?>Point:(0.00,528.00,128.00) Normal(0.00,0.00,-1.00) RGBA(0xFF020000)
<?>Point:(56.00,528.00,128.00) Normal(-0.71,0.00,-0.71) RGBA(0xFF0D0304)
<?>Point:(56.00,528.00,64.00) Normal(-1.00,0.00,0.00) RGBA(0xFF030504)
<?>Point:(-56.00,456.00,64.00) Normal(1.00,0.00,0.00) RGBA(0xFF050D0E)
<?>Point:(-56.00,456.00,128.00) Normal(0.71,0.00,-0.71) RGBA(0xFF050D0E)
<?>Point:(0.00,456.00,128.00) Normal(0.00,0.00,-1.00) RGBA(0xFF090101)
<?>Point:(56.00,456.00,128.00) Normal(-0.71,0.00,-0.71) RGBA(0xFF0D0304)
<?>Point:(56.00,456.00,64.00) Normal(-1.00,0.00,0.00) RGBA(0xFF0E0505)
<?>Point:(-56.00,384.00,64.00) Normal(1.00,0.00,0.00) RGBA(0xFF071113)
<?>Point:(-56.00,384.00,128.00) Normal(0.71,0.00,-0.71) RGBA(0xFF050B0E)
<?>Point:(0.00,384.00,128.00) Normal(0.00,0.00,-1.00) RGBA(0xFF020608)
....
<?>Num Faces: 145
<?>FirstVert(0) NumVerts(15) FirstMeshVert(0) NumMeshVerts(0)
<?>FirstVert(15) NumVerts(15) FirstMeshVert(0) NumMeshVerts(6)
<?>FirstVert(30) NumVerts(15) FirstMeshVert(6) NumMeshVerts(6)
<?>FirstVert(45) NumVerts(15) FirstMeshVert(12) NumMeshVerts(6)
<?>FirstVert(60) NumVerts(15) FirstMeshVert(18) NumMeshVerts(6)
<?>FirstVert(75) NumVerts(15) FirstMeshVert(24) NumMeshVerts(0)
<?>FirstVert(90) NumVerts(15) FirstMeshVert(24) NumMeshVerts(0)
<?>FirstVert(105) NumVerts(15) FirstMeshVert(24) NumMeshVerts(6)
<?>FirstVert(120) NumVerts(15) FirstMeshVert(30) NumMeshVerts(6)
<?>FirstVert(135) NumVerts(15) FirstMeshVert(36) NumMeshVerts(0)
<?>FirstVert(150) NumVerts(15) FirstMeshVert(36) NumMeshVerts(6)
...
<->Closing File
Didn't want to put all the information here...as theres over a thousand vertices...so I just put '...' to represent thats theres more of it.
|