//
// This unit is part of the GLScene Engine https://github.com/glscene
//
{
   Simple Quake III BSP file loader.

    History :  
       16/10/08 - UweR - Compatibility fix for Delphi 2009
	    30/01/03 - Egg - Creation, based on Philip Stefou's document and
                           bits from Jan Horn's loader 
	 
}
unit Q3BSP;

interface

uses Classes, GLVectorTypes;

const FACE_POLYGON = 1;
const MAX_TEXTURES = 1000;

type

   TBSPHeader = record
      StrID : array [0..3] of AnsiChar;    // This should always be 'IBSP'
      Version : Integer;               // This should be 0x2e for Quake 3 files
   end;

   TBSPLump = record
      Offset : Integer;                // The offset into the file for the start of this lump
      Length : Integer;                // The length in bytes for this lump
   end;

   TBSPBBox = array [0..5] of Integer;

   TBSPNode = record
      Plane : Integer;                       // Space partitioning plane
      Children : array [0..1] of Integer;    // Back and front child nodes
      BBox : TBSPBBox;                       // Bounding box of node
   end;

   TBSPLeaf = record
      Cluster : Integer;                  // Visibility cluster number
      Area : Integer;                     // Volume of the leaf
      BBox : TBSPBBox;                    // Bounding box of leaf
      FirstFace, NumFaces : Integer;      // Lookup for the face list (indexes are for faces)
      FirstBrush, NumBrushes : Integer;   // Lookup for the brush list (indexes are for brushes)
   end;

   TBSPModel = record
      BBox : TBSPBBox;                    // Bounding box of model
      FirstFace, NumFaces : Integer;      // Lookup for the face list (indexes are for faces)
      FirstBrush, NumBrushes : Integer;   // Lookup for the brush list (indexes are for brushes)
   end;

   TBSPVertex = record
      Position      : TVector3f;             // (x, y, z) position.
      TextureCoord  : TVector2f;             // (u, v) texture coordinate
      LightmapCoord : TVector2f;             // (u, v) lightmap coordinate
      Normal        : TVector3f;             // (x, y, z) normal vector
      Color         : array [0..3] of Byte   // RGBA color for the vertex
   end;
   PBSPVertex = ^TBSPVertex;

   TBSPFace = record
      textureID : Integer;                   // The index into the texture array
      effect    : Integer;                   // The index for the effects (or -1 = n/a)
      FaceType  : Integer;                   // 1=polygon, 2=patch, 3=mesh, 4=billboard
      startVertIndex : Integer;              // The starting index into this face's first vertex
      numOfVerts     : Integer;              // The number of vertices for this face
      meshVertIndex  : Integer;              // The index into the first meshvertex
      numMeshVerts   : Integer;              // The number of mesh vertices
      lightmapID     : Integer;              // The texture index for the lightmap
      lMapCorner : array [0..1] of Integer;  // The face's lightmap corner in the image
      lMapSize   : array [0..1] of Integer;  // The size of the lightmap section
      lMapPos  : TVector3f;                  // The 3D origin of lightmap.
      lMapVecs : array [0..1] of TVector3f;  // The 3D space for s and t unit vectors.
      vNormal  : TVector3f;                  // The face normal.
      Size : array [0..1] of Integer;        // The bezier patch dimensions.
   end;
   PBSPFace = ^TBSPFace;

   TBSPTexture = record
      TextureName : array [0..63] of AnsiChar;   // The name of the texture w/o the extension
      flags    : Integer;                    // The surface flags (unknown)
      contents : Integer;                    // The content flags (unknown)
   end;
   PBSPTexture = ^TBSPTexture;

   TBSPLightmap = record
      imageBits : array [0..49151] of Byte;     // The RGB data in a 128x128 image
   end;
   PBSPLightmap = ^TBSPLightmap;
   
   TBSPVisData = record
      numOfClusters : Integer;
      bytesPerCluster : Integer;
      bitSets : array of Byte;
   end;

   // TQ3BSP
   //
   TQ3BSP = class (TObject)
      public
          
         Header         : TBSPHeader;
         Lumps          : array of TBSPLump;
         NumOfVerts     : Integer;
         NumOfNodes     : Integer;
         NumOfPlanes    : Integer;
         NumOfLeaves    : Integer;
         NumOfFaces     : Integer;
         NumOfTextures  : Integer;
         NumOfLightmaps : Integer;
         Vertices       : array of TBSPVertex;
         Nodes          : array of TBSPNode;
         Planes         : array of TVector4f;
         Leaves         : array of TBSPLeaf;
         Faces          : array of TBSPFace;
         Textures       : array of TBSPTexture; // texture names (without extension)
         Lightmaps      : array of TBSPLightmap;
         VisData        : TBSPVisData;

         constructor Create(bspStream : TStream);
   end;

const
   kEntities    = 0;            // Stores player/object positions, etc...
   kTextures    = 1;            // Stores texture information
   kPlanes      = 2;            // Stores the splitting planes
   kNodes       = 3;            // Stores the BSP nodes
   kLeafs       = 4;            // Stores the leafs of the nodes
   kLeafFaces   = 5;            // Stores the leaf's indices into the faces
   kLeafBrushes = 6;            // Stores the leaf's indices into the brushes
   kModels      = 7;            // Stores the info of world models
   kBrushes     = 8;            // Stores the brushes info (for collision)
   kBrushSides  = 9;            // Stores the brush surfaces info
   kVertices    = 10;           // Stores the level vertices
   kMeshVerts   = 11;           // Stores the model vertices offsets
   kShaders     = 12;           // Stores the shader files (blending, anims..)
   kFaces       = 13;           // Stores the faces for the level
   kLightmaps   = 14;           // Stores the lightmaps for the level
   kLightVolumes= 15;           // Stores extra world lighting information
   kVisData     = 16;           // Stores PVS and cluster info (visibility)
   kMaxLumps    = 17;           // A constant to store the number of lumps

// ------------------------------------------------------------------
// ------------------------------------------------------------------
// ------------------------------------------------------------------
implementation
// ------------------------------------------------------------------
// ------------------------------------------------------------------
// ------------------------------------------------------------------

uses SysUtils;

// ------------------
// ------------------ TQ3BSP ------------------
// ------------------

// Create
//
constructor TQ3BSP.Create(bspStream : TStream);
begin
   SetLength(Lumps, kMaxLumps);

   // Read in the header and lump data
   bspStream.Read(Header, SizeOf(Header));
   bspStream.Read(Lumps[0], kMaxLumps*SizeOf(TBSPLump));

   NumOfNodes:=Round(lumps[kNodes].length/SizeOf(TBSPNode));
   SetLength(Nodes, NumOfNodes);
   bspStream.Position:=lumps[kNodes].offset;
   bspStream.Read(Nodes[0], NumOfNodes*SizeOf(TBSPNode));

   NumOfPlanes:=Round(lumps[kPlanes].length/SizeOf(TVector4f));
   SetLength(Planes, NumOfPlanes);
   bspStream.Position:=lumps[kPlanes].offset;
   bspStream.Read(Planes[0], NumOfPlanes*SizeOf(TVector4f));

   NumOfLeaves:=Round(lumps[kLeafs].length/SizeOf(TBSPLeaf));
   SetLength(Leaves, NumOfLeaves);
   bspStream.Position:=lumps[kLeafs].offset;
   bspStream.Read(Leaves[0], NumOfLeaves*SizeOf(TBSPLeaf));

   NumOfVerts:=Round(lumps[kVertices].length/SizeOf(TBSPVertex));
   SetLength(Vertices, numOfVerts);
   bspStream.Position:=lumps[kVertices].offset;
   bspStream.Read(Vertices[0], numOfVerts*SizeOf(TBSPVertex));

   numOfFaces:=Round(lumps[kFaces].length/SizeOf(TBSPFace));
   SetLength(Faces, numOfFaces);
   bspStream.Position:=lumps[kFaces].offset;
   bspStream.Read(Faces[0], numOfFaces*SizeOf(TBSPFace));

   NumOfTextures:=Round(lumps[kTextures].length/SizeOf(TBSPTexture));
   SetLength(Textures, NumOfTextures);
   bspStream.Position:=lumps[kTextures].offset;
   bspStream.Read(Textures[0], NumOfTextures*SizeOf(TBSPTexture));

   NumOfLightmaps:=Round(lumps[kLightmaps].length/SizeOf(TBSPLightmap));
   SetLength(Lightmaps, NumOfLightmaps);
   bspStream.Position:=lumps[kLightmaps].offset;
   bspStream.Read(Lightmaps[0], NumOfLightmaps*SizeOf(TBSPLightmap));

   bspStream.Position:=lumps[kVisData].offset;
   bspStream.Read(VisData.numOfClusters, SizeOf(Integer));
   bspStream.Read(VisData.bytesPerCluster, SizeOf(Integer));
   if VisData.numOfClusters*VisData.bytesPerCluster>0 then begin
      SetLength(VisData.bitSets, VisData.numOfClusters*VisData.bytesPerCluster);
      bspStream.Read(VisData.bitSets[0], VisData.numOfClusters*VisData.bytesPerCluster);
   end;
end;

end.
