# gameframe-voodoo

A software model of the 3dfx Voodoo Graphics pixel pipeline as Glide 2.x drives it. A port
that has traced which triangles and states a game's 3dfx build hands to Glide can draw them
here, at any resolution, with no Glide library and no GPU, natively and in WebAssembly.

What it models, in the order of the Glide 2.x reference manual's pipeline:

- one TMU: texture sampling, point or bilinear, wrapped or clamped, perspective-correct
  (`sow`, `tow` over `oow`); `guTexCombineFunction` decal, zero and one;
- the colour and alpha combine units (`grColorCombine`, `grAlphaCombine`, and
  `guColorCombineFunction` as the calls it stands for);
- the chroma key, the alpha test, iterated-alpha and table fog;
- the Z buffer (`ooz`) and the W buffer (`1/oow`), with every compare function and the
  depth mask;
- alpha blending against the pixel already in the buffer;
- the 16-bit RGB565 frame buffer with its 4 × 4 ordered dither, and the gamma curve on the
  way out;
- `Glide`, a software Glide 2.x context: the calls by their Glide names (`grColorCombine`,
  `guColorCombineFunction`, `grTexDownloadMipMap`, `grTexSource`, `grFogTable`,
  `grDrawTriangle` and the rest), drawn at any multiple of the game's resolution.

It follows 3dfx's own SST-1 (Voodoo Graphics) specification, revision 1.61: the combine
units' integer arithmetic (a 0–256 factor, a truncating product, a clamp), the 12.4 vertex
coordinates, the W buffer's 4.12 floating format, the fog table indexed and blended by that
value, bit-truncation wrapping, and the 4 × 4 dither matrix (from the Voodoo2 specification).
Each source is cited where it is used. Where the specification leaves a detail open (the
iterators' rounding, the bilinear weights' precision, how the dither is added) the code says
so in a `Deviation:` comment. Nothing has been compared with a real card yet.

- `grDrawPolygon`, `grDrawPoint` and `grDrawLine` through the triangle engine;
- `grColorMask` and `grBufferClear` as the FASTFILL command (clip window and write masks);
- `grLfbWriteRegion` with the pixel pipeline bypassed (565, 555, 1555, 888, 8888);
- Glide's texture memory manager (`guTexAllocateMemory`, `guTexDownloadMipMapLevel`,
  `guTexSource`, `guTexMemReset`, `guTexMemQueryAvail`) with a memory budget, so a game that
  allocates until it fails sees the card fill up; `guFogGenerateLinear`;
- the palette formats looked up as the TMU samples, so a new palette recolours `P_8` and
  `AP_88` textures already downloaded.

What it does not model: two TMUs, mipmaps, LFB reads and locks, the alpha planes, alpha and
depth bias, stipple.

The constants are the values of the Glide 2.x SDK's `glide.h`, as in GameFrame's
`wrappers/glide`, whose OpenGL shader follows the same combine rules. Glide's texture formats
(`formats`) and NCC tables (`ncc`) are here too; texel decoding is `gameframe-texel`'s.

It comes from the POD: Planet of Death (1997) project, whose 3dfx build (`WPOD3DFX.EXE`)
Un-POD draws with it.

## Use it

```toml
[dependencies]
gameframe-voodoo = { git = "https://github.com/tanepiper/game-frame" }
```

```rust
use gameframe_voodoo::{consts::*, Combine, Frame, State, Vertex, triangle};

let mut frame = Frame::new(640, 480);
let mut state = State::default();
state.depth_mode = DEPTHBUFFER_WBUFFER;
state.depth_mask = true;
state.colour = Combine::colour_function(CC_ITRGB).unwrap();
frame.clear(0, 0xffff);
let v = |x, y| Vertex { x, y, oow: 1.0, r: 255.0, g: 128.0, b: 0.0, a: 255.0, ..Vertex::default() };
triangle(&mut frame, &state, None, [&v(10.0, 10.0), &v(200.0, 20.0), &v(50.0, 300.0)]);
let rgba = frame.rgba(1.0);
assert_eq!(rgba.len(), 640 * 480 * 4);
```

## Build and test

```bash
cargo test --manifest-path crates/Cargo.toml -p gameframe-voodoo
```

Licence: Apache 2.0.
