- C 73.1%
- C++ 25.7%
- Python 0.4%
- Batchfile 0.3%
- GLSL 0.3%
- Other 0.2%
- Camera::perspective_inf: zfar -> infinity projection, so geometry never pops out of existence with distance (the x16 skybox terrain reaches ~130k units; the old 65536 far plane was visibly clipping it). Near plane still maps to -1; sky detection at depth 1.0 keeps working since real geometry converges strictly below it. Gate test pins the projection math. - 3D skybox tagging is now a box around sky_camera (16384 wide, 5120 tall) instead of an 8192 sphere - the sphere missed the sprawling hill displacements, leaving pieces of the miniature room floating in the real sky. 924 checks pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|---|---|---|
| include | ||
| shaders | ||
| src | ||
| tests | ||
| .gitignore | ||
| build.bat | ||
| build.sh | ||
| README.md | ||
| run_tests.bat | ||
| run_tests.sh | ||
| setup_deps.py | ||
bsp-viewer
A Source engine BSP and VMF map viewer written in C++ with OpenGL 3.3.
PRs are welcomed.
What it is
Parses and renders .bsp map files from Source engine games (Half-Life 2,
CS:GO, etc.) and .vmf Hammer/decompiled map sources. Mounts every VPK
archive found under the game folder for textures, resolves VMT materials
(including patch include chains), decodes VTF textures (7.0–7.5, DXT1/3/5
- uncompressed formats), renders lightmapped world geometry, and loads static props (MDL/VVD/VTX, up to MDL v49).
For .vmf maps the brush solids are rebuilt from their half-space planes
(huge base quad clipped against every other side), displacement terrain is
generated from dispinfo grids (power 2–4, offsets/elevation included),
func_detail/brush-entity solids render too, and face UVs come from
uaxis/vaxis (texel scale resolved against the real VTF texture size).
VMF has no lightmaps, so VMF maps render fullbright until real-time
lighting lands.
What it uses
- OpenGL 3.3 Core Profile via GLAD
- GLFW3 for windowing and input
- stb_image for image loading
- C++20
Building
Prerequisites
- MSYS2/MinGW-w64 with GCC (
ucrt64andmingw64are auto-detected) - Run
setup_deps.pyonce to fetch GLAD and stb_image headers - Install GLFW:
pacman -S mingw-w64-ucrt-x86_64-glfw # MSYS2 ucrt64 pacman -S mingw-w64-x86_64-glfw # MSYS2 mingw64
Compile
build.bat
Or on Linux/Mac:
./build.sh
The Windows build links -static, so the resulting bsp_viewer.exe has no
DLL dependencies.
Tests
run_tests.bat
(./run_tests.sh on Linux/Mac.) Builds and runs the gate test suite in
tests/test_parsers.cpp: 230+ deterministic checks against synthetic BSP,
VPK, VTF, VMT, and MDL/VVD/VTX fixtures built in memory — no GL context or
game assets needed, runs in well under a second. Every previously fixed
format bug (LDR/HDR lightmap pairing, sprp v10 stride, VTF 7.3+ resource
offsets, VTX v49 strip-group stride, VPK dir-embedded offsets, the camera
view matrix, prop model matrices, ...) is pinned by a test.
Usage
bsp_viewer.exe <map.bsp|map.vmf> [game_dir]
game_dir is the game folder; it is scanned recursively and all dir
VPKs found are mounted (pak01_dir.vpk, hl2_textures_dir.vpk, ...), so
both the flat CS:GO layout and the multi-VPK HL2 layout work. Loose
materials/ files on disk are used as fallback. For .vmf maps
game_dir defaults to the map's own folder (decompiles usually sit next
to loose materials/ + models/). Examples:
bsp_viewer.exe d1_canals_01.bsp "C:\Steam\steamapps\common\Half-Life 2"
bsp_viewer.exe de_dust2.bsp "C:\Steam\steamapps\common\Counter-Strike Global Offensive\game\csgo"
bsp_viewer.exe gm_construct.vmf
Controls
| Input | Action |
|---|---|
W A S D |
Move |
| Mouse | Look |
Space / E |
Move up |
Ctrl / Q |
Move down |
Shift |
3x speed boost |
[ / ] |
Halve / double speed |
| Scroll wheel | Adjust speed |
Escape |
Toggle mouse capture |
Current state
Working: world geometry with gamma-correct lightmaps, texture loading from VPK/disk (VMT → VTF, patch materials), static props with correct placement/orientation, skybox, HL2 and CS:GO era formats.
Known limitations:
- Specialty materials (water, 2-way blends
$basetexture2, refractive glass) render with their base texture or a gray fallback - Displacements (terrain) are not rendered
- Source skybox face orientation is approximate
- No BSP visibility culling — the whole map draws every frame (fine on any modern GPU for Source 1 maps)
Source files
| File | Purpose |
|---|---|
src/main.cpp |
Entry point, GLFW setup, VPK discovery, main loop |
src/bsp_parser.cpp |
Source BSP file parser |
src/vmf_parser.cpp |
VMF (Hammer) parser: KeyValues, brush clipping, displacements |
src/renderer.cpp |
OpenGL rendering, texture/material pipeline |
src/vpk_reader.cpp |
VPK archive reader (multi-archive) |
src/vmt_parser.cpp |
VMT material parser |
src/vtf_reader.cpp |
VTF texture reader |
src/mdl_loader.cpp |
MDL/VVD/VTX model loader |
src/glad.c |
OpenGL loader |
tests/test_parsers.cpp |
Gate test suite (synthetic fixtures) |