Files
TeslaRel410/dpl3-revive/spec/B2Z_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

189 lines
7.6 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.
# DIV-BIZ2 (`.B2Z` / `.V2Z`) binary geometry format
> **The format family is wider than its extensions.** The game-side pipeline uses
> the same two encodings under different names:
> `.BGF` = binary geometry (magic `DIV-BIZ2`), `.VGF` = its text twin (`DIV-VIZ2`),
> `.BMF` = binary **material library** (a BIZ2 file with only material/texture/ramp
> blocks, no geometry), `.VMF` = its text twin. Everything below applies to all of
> them. Game-side material references are **namespaced**: `stships:eblu4_mtl` means
> material `eblu4_mtl` in library `STSHIPS.VMF`/`.BMF`.
>
> **Texture sub-block `0x018` (1996+): BITSLICE** — u8 plane index into a `.BSL`
> bit-slice texture pack (see `SVT_FORMAT.md`). Absent = slice 0.
Reverse-engineered from `DPL3/BIZREAD.C` (© DIVISION Ltd 1994, author PJA) and
validated against real models on drive SDA4. This is the object/geometry format
consumed by the DPL3 renderer used in Virtual World Entertainment's BattleTech /
Red Planet pods. `.B2Z` and `.V2Z` share this format.
All multi-byte values are **little-endian**. Floats are IEEE-754 single precision
(the header rejects double-precision files). There is no compression — the stream
is a flat sequence of length-delimited, tagged blocks.
## 1. File layout
```
"DIV-BIZ2" 8-byte ASCII magic
<block>* sequence of top-level blocks
```
Parsing stops at the **trailer** block (type `0x005`).
## 2. Block header
Every block begins with a 16-bit header, followed by a length field whose **width
is encoded in the top nibble of the header**:
```
hdr = u16
sel = (hdr >> 12) & 0xC
sel == 0x0 -> length = u8 (1 byte) top nibble 0,1,2,3
sel == 0x4 -> length = u16 (2 bytes) top nibble 4,5,6,7
sel == 0x8 -> length = u32 (4 bytes) top nibble 8,9,A,B
```
`length` counts the block **body** only (everything after the length field). The
record **type** is `hdr & 0xFFF`; the top nibble is purely the length-encoding
flag. That is why the source lists each type three times (`0x0NNN` / `0x4NNN` /
`0x8NNN`) — same record, three body sizes.
## 3. Top-level block types (`hdr & 0xFFF`)
| Type | Meaning | Body |
|-------|----------|------|
| 0x003 | Header | sub-blocks (§4) |
| 0x004 | Comment | opaque, skip |
| 0x005 | Trailer | end of file |
| 0x010 | Texture | sub-blocks (§7) |
| 0x020 | Material | sub-blocks (§7) |
| 0x030 | Ramp | sub-blocks (§7) |
| 0x040 | Object | sub-blocks (§5) |
## 4. Header sub-blocks
| Type | Name | Body |
|-------|-----------|------|
| 0x002 | version | skip |
| 0x003 | date | skip |
| 0x004 | time | skip |
| 0x005 | scale | f32 |
| 0x006 | precision | u8 (must be 0 = single) |
| 0x007 | filetype | skip |
| 0x009 | unit | u8: 0 = model units, 1 = inches (scale by 1/25.4) |
## 5. Object sub-blocks
| Type | Name | Body |
|-------|----------------|------|
| 0x008 | object name | NUL-terminated string |
| 0x030 | front material | material-ref (§6) |
| 0x031 | back material | material-ref (§6) |
| 0x036 | vertex format | u8 draw_mode (inherited by patches) |
| 0x041 | LOD | payload skipped by original loader |
| 0x042 | patch | sub-blocks (§8) — one geogroup |
| 0x032/33/34/35/37 | plane / draw-mode / decal / facet / voodoo | skip |
| 0x000/001 (+comment tags) | comment | skip |
## 6. Material reference (used by 0x030 / 0x031)
```
type = u8
0 -> none (NULL)
1 -> named: NUL-terminated material name follows
2 -> DEFAULT material
3 -> (back only) same as front
```
## 7. Material / Texture / Ramp sub-blocks
**Material** (top-level 0x020):
| Type | Name | Body |
|-------|----------|------|
| 0x008 | name | string |
| 0x00a | scope | u8 |
| 0x021 | texture | u8 mode; if mode==2, NUL-terminated texture name |
| 0x023 | ambient | 3×f32 |
| 0x024 | diffuse | 3×f32 |
| 0x025 | specular | 4×f32 |
| 0x026 | emissive | 3×f32 |
| 0x027 | opacity | 3×f32 |
| 0x028 | ramp | NUL-terminated ramp name |
**Texture** (top-level 0x010): 0x008 name, 0x00a scope, 0x011 map filename
(string), 0x012 minify, 0x013 magnify, 0x014 alpha, 0x015 wrap_u, 0x016 wrap_v,
0x017 detail — the mode fields are each a u8. The bitmap itself lives in an
external `.SVT` file named by 0x011. 0x037 is the SPECIAL hook, a NUL-terminated
free-text string mirroring the text source's `SPECIAL="..."` header field — in
practice `" SCROLL u0 v0 du dv "` UV-drift rates (see SCN_FORMAT, Scrolling
textures).
**Ramp** (top-level 0x030): 0x008 name, 0x00a scope, 0x031 ramp data
(color0 = 3×f32, color1 = 3×f32).
## 8. Patch (geogroup) sub-blocks
| Type | Name | Body |
|-------|----------------|------|
| 0x008 | patch name | string |
| 0x030 | front material | material-ref (§6) |
| 0x031 | back material | material-ref (§6) |
| 0x036 | vertex format | u8 draw_mode |
| 0x043 | polygon | vertex-list (§9), type = polystrip |
| 0x044 | tristrip | vertex-list (§9), type = tristrip |
| 0x045 | polystrip | vertex-list (§9), type = polystrip |
| 0x046 | pmesh | vertex-list (§9), type = pmesh |
| 0x048/04a/04b | sphere / line / text | not implemented in original; skip |
| 0x032/33/34/35/37 | plane / draw-mode / decal / facet / voodoo | skip |
`draw_mode` bits (from `dpltypes.h`): 0x01 smooth, 0x02 colored, 0x04 luminance,
0x08 textured, 0x10 3d-textured, 0x200 front, 0x400 back.
## 9. Vertex-list sub-blocks
A primitive's body is itself a sequence of blocks: zero or more **connectivity**
blocks (pmesh only) plus exactly one **vertex** block. A vertex block's `length`
gives `n_floats = length / 4`; `n_vertices = n_floats / floats_per_vertex`.
Each vertex is `floats_per_vertex` consecutive f32s; components live at fixed
offsets by format:
| Type | f/vert | position | normal | rgba | lum(la) | uv(2) | uvw(3) |
|-------|:-----:|:--------:|:------:|:----:|:-------:|:-----:|:------:|
| 0x080 | 3 | 0 | | | | | |
| 0x081 | 6 | 0 | 3 | | | | |
| 0x082 | 7 | 0 | | 3 | | | |
| 0x083 | 10 | 0 | 3 | 6 | | | |
| 0x084 | 5 | 0 | | | 3 | | |
| 0x085 | 8 | 0 | 3 | | 6 | | |
| 0x088 | 5 | 0 | | | | 3 | |
| 0x089 | 8 | 0 | 3 | | | 6 | |
| 0x08a | 9 | 0 | | 3 | | 7 | |
| 0x08c | 7 | 0 | | | 3 | 5 | |
| 0x090 | 6 | 0 | | | | | 3 |
| 0x091 | 9 | 0 | 3 | | | | 6 |
| 0x092 | 10 | 0 | | 3 | | | 7 |
| 0x094 | 8 | 0 | | | 3 | | 5 |
**Connectivity** (pmesh only):
- `0x047` triangle connectivity — `length/12` triangles, each = 3× i32 vertex indices.
- `0x04d` polygon connectivity — u8 `verts_per_poly`, then `(length-1)/(vpp*4)`
polygons, each = `vpp`× i32 indices.
**Triangulation for export:**
- `tristrip`: triangles `(i, i+1, i+2)`, flipping winding on odd `i`.
- `pmesh`: fan each face `(f0, fk, fk+1)`.
- `polygon` / `polystrip`: current parser fans the block's vertices — adequate for
simple patches; a faithful polystrip walk is a known refinement (see README).
## 10. Validation
`parser/b2z.py` decodes the samples with no unrecognized blocks and clean
trailers. Semantic checks that confirm correctness:
| File | Result |
|------|--------|
| PT_RED.B2Z | material diffuse = (1,0,0) → red; no geometry (a colored point) |
| PT_WHITE.B2Z | material diffuse = (1,1,1) → white |
| BALL.B2Z | 50 verts / 40 tris, bounds ≈ unit sphere at origin, has normals |
| TESTBOX.B2Z | 5 pmesh parts (blue/red/black/green/yellow), 202 tris, boxy bounds |
| CANDA_S1.B2Z | Red Planet terrain tile: redrock/water materials, inch units, UV coords |