quake-rb

A port of the Quake (1996) engine to Ruby. Reads original .pak/.bsp/.mdl assets and renders them via OpenGL + SDL2.

quake-rb running e1m1

Why

Mostly as a counterpart to doom — to see how far you can take id Software's classic engines in plain Ruby, and to learn the BSP/PVS/lightmap pipeline by writing it from the spec rather than reading the C.

Quick start

bundle install
bundle exec bin/quake

On first run, with no data/id1/pak0.pak present, you'll be asked to download the freely-redistributable shareware Quake (Episode 1, ~17 MB). It's pulled from archive.org/quakeshareware/QUAKE_SW.zip and extracted into ./data/id1/pak0.pak. You can also drop your own registered pak0.pak (and pak1.pak) in there manually.

System requirements:

  • Ruby 3.1+
  • SDL2 (brew install sdl2 / apt install libsdl2-dev)
  • OpenGL 2.1
  • unzip (used by the shareware fetcher)

Controls

Key Action
WASD Move
Mouse Look
Space Jump
C Crouch
1–8 / scroll Weapon select
N Toggle noclip
L Dump player pose + save a screenshot to debug/shots/ (debug aid)
Esc Quit

What works

  • BSP loading + face extraction with texture coordinates
  • Texture mapping with the original 256-colour palette
  • Static lightmaps from the lighting lump
  • PVS culling, including:
    - leaf 0 fallback (treat solid leaf as all-visible, à la TyrQuake)
    - FatPVS expansion when the camera leaf touches a liquid surface
    - **vis-through-water**: liquid faces in PVS bring the leaves on the other side of their plane into view, so translucent water shows the cave bed instead of clear-color void
    
  • Sky rendering: two-layer scrolling cloud + back layer, PVS-culled
  • Water/slime/lava: turbsin-warped texture, alpha-blended translucency, backface culling for the duplicate front/back face pairs Quake's BSP compiler emits
  • Brush entities (doors, plats, buttons, triggers) with their own transforms
  • MDL alias models with frame interpolation
  • Particle effects + viewmodel + HUD
  • Player physics: walk, crouch, jump, friction, water levels, platform riding
  • Item pickups, weapon switching, Quake-style player weapon firing, shareware audio mixer
  • Common QuakeC trigger behavior at engine level, including target firing, killtarget, counters, hurt/push/teleport triggers, and changelevel requests

What doesn't (yet)

  • Multiplayer / netcode
  • QuakeC server program — game logic is faked at engine level
  • Full savegame/spawn parameter preservation across episode/level transitions
  • Mirrors, particle warp, dynamic lights
  • Sky cube / skybox replacement
  • Demo playback

Maps

The shareware PAK ships:

start, e1m1, e1m2, e1m3, e1m4, e1m5, e1m6, e1m7, e1m8

Pass -map maps/e1m3.bsp to load a different one.

Architecture

lib/quake/
  bsp/             reader, types, face vertices, PVS / FatPVS / vis-through-water
  pak/             pak0.pak reader
  mdl/             alias model reader
  wad/             gfx.wad (HUD/menu graphics) reader
  palette.rb       256-colour palette → RGBA
  math/vec3.rb     plain Ruby vector
  physics/         hull trace + Player (walk/swim/jump/noclip)
  game/
    engine.rb      orchestrates renderers + physics + entity simulation
    brush_entities entity-driven door/plat/button state machines
    item_pickups   touchable items
    player_state   weapons, ammo, health, armor
  renderer/
    gl_textured       opaque world brushes (with lightmap pass)
    gl_brush_model    sub-model brushes (doors etc.) at entity transforms
    gl_alias_model    .mdl monsters + items
    gl_water          *water0/*slime0/*lava0/*teleport with turbsin warp
    gl_sky            two-layer scrolling sky
    gl_lightmap       lightmap atlas builder
    gl_texture_manager mipmapped diffuse texture cache
    gl_particles      explosion/spark sprites
    gl_viewmodel      first-person weapon
    gl_hud            statusbar + numbers
  sound/           SDL_mixer-backed sound effects
  pak_downloader.rb shareware fetcher
  version.rb        VERSION = "0.1.0"
bin/
  quake             interactive game
  quake-debug       headless DSL runner (`teleport`, `screenshot`, `dump` etc.)
test/              minitest suite
debug/scripts/     ad-hoc investigation scripts (gitignored)

The headless runner is genuinely useful for working on the engine without a window — see debug/scripts/* for examples (water visibility traces, leaf inspection, BSP dumps).

Development

# Run the tests
bundle exec ruby -Itest -Ilib -e 'Dir["test/**/*_test.rb"].each { |f| require File.expand_path(f) }'

# Run a headless debug script (renders to a hidden window)
bundle exec bin/quake-debug debug/scripts/water_real.rb

# Run the game with YJIT
bundle exec bin/quake --yjit

Status

This is a hobby project. It can comfortably walk you through e1m1, fire Quake-style player weapons, pick up items, and use a growing subset of QuakeC brush/trigger behavior. The QuakeC server program is still not implemented, so game logic is ported incrementally at engine level and monsters don't fight back yet.

License

GPL-2.0-only, matching the original Quake source release.

The shareware Quake data is © 1996 id Software, freely redistributable. The registered Quake data (if you supply your own pak0.pak + pak1.pak) is not redistributable; you must own a copy.