BSP level rendering

Hi, don't think that I'm insane but I'm in love with the glorious 1996 quake first person shooter. I'm trying to write a program in C that loads a classic quake .bsp file and renders it using glut, I already understand how bsp works and I have read it's specification here http://www.gamers.org/dEngine/quake/spec/quake-spec31.html#CBSPG but I don't know how to write the code that'll load the bsp and render it, if you have some time, please help!
Last edited on
The question is too broad to get many useful answers. If you just need help in general, maybe set-up a public source control repository and ask for specific contributions or code review. I'd be happy to take a look if you did that.
Last edited on
It might help if you provide an example BSP file for someone to look at.

You'll need to figure out some way to test small bits of your process at a time so that you know you're heading in the right direction. Separate out loading the BSP from actually rendering it, since it's two different things. You'd want to make sure the data is at least mostly right before you attempt to render it.

The link you posted says that the first entry in the BSP file header is a long, but I don't know if that's Big Endian, Little Endian, or if it's a 4-byte long or an 8-byte long.
The Quake Wiki says that the version is 4 bytes long (0x1d, 0x00, 0x00, 0x00), which makes sense, so therefore I would use std::uint32 to store the version.
Furthermore, since the least-significant byte is at the beginning, that means it's little-endian.
https://quakewiki.org/wiki/Quake_BSP_Format

HOWEVER 0x1d is 29, not 17... so clearly the Quake Wiki is using a different version number.

Either way, you're going to want to open your BSP file in binary mode.
There's a short tutorial by Disch you can read here: http://www.cplusplus.com/articles/DzywvCM9/

Since it's in little endian, you don't necessarily have to do the bit shifting yourself, but this should be valid nonetheless:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <cstdint>
#include <iostream>
#include <fstream>

using u32 = std::uint32_t;
using u8 = std::uint8_t;

typedef struct                 // A Directory entry
{ u32   offset;                // Offset to entry, in bytes, from start of file
  u32   size;                  // Size of entry in file, in bytes
} dentry_t;

typedef struct                 // The BSP file header
{ u32  version;               // Model version, must be 0x17 (23).
  dentry_t entities;           // List of Entities.
  dentry_t planes;             // Map Planes.
                               // numplanes = size/sizeof(plane_t)
  dentry_t miptex;             // Wall Textures.
  dentry_t vertices;           // Map Vertices.
                               // numvertices = size/sizeof(vertex_t)
  dentry_t visilist;           // Leaves Visibility lists.
  dentry_t nodes;              // BSP Nodes.
                               // numnodes = size/sizeof(node_t)
  dentry_t surfaces;           // Map Surfaces.
                               // numsurfaces = size/sizeof(surface_t)
  dentry_t lightmaps;          // Wall Light Maps.
  dentry_t boundnodes;         // BSP bound nodes, for Hulls.
                               // numbounds = size/sizeof(hullbound_t)
  dentry_t leaves;             // BSP Leaves.
                               // numlaves = size/sizeof(leaf_t)
  dentry_t lstsurf;            // List of Surfaces.
  dentry_t edges;              // Original surface Edges.
                               // numedges = Size/sizeof(edge_t)
  dentry_t lstedges;           // List of surfaces Edges.
  dentry_t hulls;              // List of Hulls.
                               // numhulls = Size/sizeof(hull_t)
} dheader_t;

u32 read_u32_le(std::istream& file)
{
  u32 val;
  u8 bytes[4];

  // read 4 bytes from the file
  file.read((char*)bytes, 4);
  
  // construct the 32-bit value from those bytes
  val = bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24); 

  return val;
}

int main()
{
	std::ifstream fin("test.bsp");
	
	dheader_t bsp_header;
	bsp_header.version = read_u32_le(fin);
	
	std::cout << bsp_header.version << '\n'; // Does this print 17 for you?
}

Test it yourself to see if it prints 17. I don't have a file to test.
Last edited on
Topic archived. No new replies allowed.