Files
TeslaRel410/dpl3-revive/spec/SVT_FORMAT.md
T
CydandClaude Fable 5 afc3fd839e Vendor dpl3-revive: the Division/DPL3 renderer, now ours
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>
2026-07-05 22:06:25 -05:00

128 lines
5.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# `.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 | 2023 |
| 1 | 4-bit mono | 1619 |
| 2 | 4-bit mono | 1215 |
| 3 | 4-bit mono | 811 |
| 4 | 8-bit mono | 815 |
| 6 | 8-bit colour (3-3-2: `r \| g<<3 \| b<<6`) | 07 |
| 7 | 12-bit colour (4-4-4: `r \| g<<4 \| b<<8`) | 011 |
`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 831 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 01) for an optional 8-bit colour plane. So slices 05 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/`
(`STEH16.TGA`, `KLNG16.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 `7k` 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.