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>
189 lines
7.6 KiB
Markdown
189 lines
7.6 KiB
Markdown
# 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 |
|