Bring the graphics-dev collaborator's dpl3-revive into the repo as first-class
project code (they've handed it off; it's ours now). This is the proven
Division renderer that our in-process rt_draw has been trying to be.
What's here:
- parser/ B2Z/V2Z/SVT/SCN/SPL/BGF/BMF/BSL decoders (pure Python).
- spec/ reverse-engineered format + the definitive VelociRender wire
protocol (from the original DIVISION source, matches our live
VPX node/action tables exactly).
- source-ref/ read-only copies of the original DIVISION C (BIZREAD.C,
DPLTYPES.H, DPL.H) that define the formats.
- patha/ the "virtual VelociRender board": vrboard.py (24-action protocol
server), vrview.py (numpy software rasterizer, the reference),
vrview_gl.py (moderngl GPU backend, 832x512@60Hz), plus the
run/replay/regress tooling and evidence renders. Drives FLYK/BLADE/
Star Trek demos AND our btl4opt/rpl4opt game binaries.
- viewer/ WebGL archive generators (.py); prebuilt HTML/data regeneratable.
- samples/ small test models/textures.
- bt*.raw.bin real BTL4OPT arena wire captures (kept for offline renderer
testing/regression against OUR game).
.gitignore keeps the multi-hundred-MB demo capture dumps + debug logs +
regeneratable viewer bundles out of history (they stay on disk).
Phase 0 of the integration is validated: their board decodes our bt8 capture
with zero errors (3748 nodes, 507 instances, 4 mechs) and renders our arena
(terrain/dome/sky, correct Division DAC gamma). Plan + status in memory;
integration continues in emulator/RENDERER-COLLAB.md.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
128 lines
5.3 KiB
Markdown
128 lines
5.3 KiB
Markdown
# `.SVT` texture format
|
||
|
||
Reverse-engineered from `DPL3/DPL_LOAD.C : load_svt()` and validated by decoding
|
||
real textures to PNG. `.SVT` is the texture-map format referenced by `.B2Z`
|
||
materials (via the texture "map filename", block 0x011 — see `B2Z_FORMAT.md` §7).
|
||
|
||
## Layout
|
||
|
||
**There is no header.** The entire file is a raw, row-major array of 32-bit
|
||
texels. The image is always **square**, and the edge length is inferred solely
|
||
from the file size (exactly as the original loader does):
|
||
|
||
| File size (bytes) | Dimensions |
|
||
|-------------------|------------|
|
||
| 16384 | 64 × 64 |
|
||
| 65536 | 128 × 128 |
|
||
| 262144 | 256 × 256 |
|
||
|
||
Any other size is rejected. `texel_count = edge²`, `file_size = edge² × 4`.
|
||
|
||
## Texel
|
||
|
||
Each texel is 4 bytes in file order:
|
||
|
||
```
|
||
byte0 = pad (always 0x00, unused)
|
||
byte1 = Blue
|
||
byte2 = Green
|
||
byte3 = Red
|
||
```
|
||
|
||
i.e. a `0BGR` layout (as a little-endian 32-bit word: `0xRRGGBB00`). There is no
|
||
usable alpha channel stored in the texel — byte0 is a pad byte, not opacity. The
|
||
loader hard-codes `bits_per_texel = 32`. Per-material transparency comes from the
|
||
material/texture `alpha` mode, not from the texels.
|
||
|
||
The channel order is not stated in the source (the i860/pxpl5 hardware consumed
|
||
the word directly); it was determined empirically — see Validation.
|
||
|
||
## V coordinate convention
|
||
|
||
**Do not flip V.** The renderer indexed texture rows directly: `v = 0` samples SVT
|
||
row 0, which is the image **top**. Verified empirically by A/B renders — the Mad Cat
|
||
cockpit canopy and missile-pod tube/vent faces, the MADCOL "08" insignia, and the
|
||
shark's back/belly shading are all correct with V passed through unchanged and all
|
||
invert if V is flipped (`1-v`). WebGL's `texImage2D` also places the first uploaded
|
||
row at `t = 0`, so no correction is needed anywhere in the modern pipeline.
|
||
(An earlier revision of this toolchain flipped V; that produced the "upside-down
|
||
cockpit / reversed pods" artefacts on the mechs.)
|
||
|
||
## GLOMM-packed ("ALL") textures
|
||
|
||
`GLOMM.EXE` (source: `DPL3/GLOMM.C`) packs several textures into the **bit-planes of
|
||
one 32-bit SVT** — a texture-memory-saving trick for the pxpl5 board. From the mode
|
||
switch in `load_svt()`:
|
||
|
||
| `/m:` mode | Format | Bits |
|
||
|---|---|---|
|
||
| 0 | 4-bit mono | 20–23 |
|
||
| 1 | 4-bit mono | 16–19 |
|
||
| 2 | 4-bit mono | 12–15 |
|
||
| 3 | 4-bit mono | 8–11 |
|
||
| 4 | 8-bit mono | 8–15 |
|
||
| 6 | 8-bit colour (3-3-2: `r \| g<<3 \| b<<6`) | 0–7 |
|
||
| 7 | 12-bit colour (4-4-4: `r \| g<<4 \| b<<8`) | 0–11 |
|
||
|
||
`save_svt()` **byte-reverses each 32-bit word** before writing. Files named
|
||
`*ALL.SVT` (`MADALL`, `VTVALL`, `GENALL`, …) are such packs (e.g. `GLOMMAD.BAT`:
|
||
`glomm madall.svt /m:0 mad1 /m:1 mad2 /m:2 mad3 /m:3 mad4 /m:6 madcol`) and decode
|
||
as coloured noise if read as plain RGBA. For modern rendering, **use the pre-glom
|
||
per-part source textures** (`MAD1..4.SVT`, `VTVCAB.SVT`, …), which sit alongside
|
||
them on the disk as ordinary full-colour SVTs.
|
||
|
||
## `.BSL` bit-slice packs (DIV-BSL2)
|
||
|
||
The formalised successor of the GLOMM packing, used by the game-side pipeline
|
||
(1996+). Header-ed, unlike `.SVT`:
|
||
|
||
```
|
||
"DIV-BSL2" 8-byte magic
|
||
u32 version (0x100), u32 (1)
|
||
u32 bits_per_slice (4)
|
||
u32 dir_bytes
|
||
u32 n_slices
|
||
n x { u32 type (10 = 4-bit mono), u32 slice_index, cstr name }
|
||
raw 32-bit texel words (edge inferred from remaining size, as .SVT)
|
||
```
|
||
|
||
Slice *k* occupies nibble **`(k+2) ^ 1`** of each little-endian 32-bit word:
|
||
slices fill bits 8–31 in ascending order, **pair-swapped within each byte** by the
|
||
writer's 32-bit byte reversal (the same `save_svt` reversal as GLOMM), leaving the
|
||
low byte (nibbles 0–1) for an optional 8-bit colour plane. So slices 0–5 land at
|
||
nibbles 3, 2, 5, 4, 7, 6. Materials select a slice via texture sub-block `0x018`
|
||
(binary) / `BITSLICE {k}` (text); the 4-bit mono plane is rendered as luminance
|
||
and coloured by the material's DIFFUSE.
|
||
|
||
*Verification:* the pre-pack source TGAs survive in `STDAVE/VIDEO/BUILD/`
|
||
(`STEH1–6.TGA`, `KLNG1–6.TGA`). Correlating each TGA against all eight nibble
|
||
planes of its pack gives r ≥ 0.98 for this mapping on **all twelve** slices, and
|
||
near-zero for every other nibble. (A first-guess mapping of `7−k` scrambled the
|
||
planes — e.g. the starship's deflector-dish glow landed on the saucer hull.)
|
||
|
||
## Notes
|
||
|
||
- Textures are addressed by name from the `.B2Z` material; the loader resolves the
|
||
name to a file via a search path (`dpl_FindTextureFile`). Files on the disk carry
|
||
the `.SVT` extension.
|
||
- `minify` / `magnify` modes in the material select point vs. bilinear sampling
|
||
(`magnify` 0 or 1 → point; else bilinear). `wrap_u` / `wrap_v` select clamp vs.
|
||
repeat. These live in the `.B2Z`, not the `.SVT`.
|
||
- A 1-byte `.SVT` appears on the disk as a placeholder/stub (e.g. `WPNMECH.SVT`);
|
||
treat non-conforming sizes as "no texture".
|
||
|
||
## Validation
|
||
|
||
`parser/svt.py png <file>` decodes with the `0BGR` order and produced correct,
|
||
recognizable images:
|
||
|
||
| File | Dims | Result |
|
||
|------|------|--------|
|
||
| FLAME64.SVT | 64² | orange/red flame (blue under the wrong order — the decisive test) |
|
||
| GROUPER.SVT | 128² | coral grouper: red body, blue spots |
|
||
| SHARK.SVT | 256² | great white shark on a blue ground |
|
||
| TANK.SVT | 128² | vehicle skin |
|
||
|
||
The flame is the disambiguator: warm colors only make sense under `0BGR`; the
|
||
alternative `0RGB` renders it blue.
|