www.xbdev.net xbdev - software development
Wednesday November 20, 2019
home | about | contact | Donations

     
 

3D File Formats

The bits and bytes...

 

Quake 3 BSP [Reading in File Information]
Author bkenwright@xbdev.net

 

Going from theory to code!  Its not so hard, as the Quake 3 file format is pretty well layed out.  Its mostly just breaking it down and analysing the data.

Download Code

#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.

Download Source Code

#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.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
 Visitor: 9534626  { 209.237.238.175 } Copyright (c) 2002-2017 xbdev.net - All rights reserved.
Designated tutorial and software are the property of their respective owners.