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>
7.3 KiB
.SCN scene format
Reverse-engineered from DPL3/EXAMPLES/FLYK.C (the flythrough viewer) and
DPL3/MATRIX.C. .SCN is plain text, one command per line, whitespace-
delimited. Comments start with // or #. It assembles a world by referencing
models (.B2Z) and textures (.SVT) by name — it embeds no geometry itself.
Core commands (the FLYK subset)
These cover the overwhelming majority of every scene on the disk (STATIC alone
is ~5,000 of ~9,000 command lines):
| Command | Arguments | Meaning |
|---|---|---|
FOG |
z0 z1 r g b |
linear fog: start/end distance + colour |
BACKGND |
r g b |
background / clear colour |
AMBIENT |
r g b |
ambient light |
LIGHT |
r g b ax ay az |
directional light: colour + orientation angles (deg) |
STATIC |
name scale x y z ax ay az |
place a model instance |
DYNAMIC |
name scale splinefile t0 dt |
model that follows a camera-path spline |
SSTATIC |
(as STATIC) |
static variant |
SCROLL |
texname u0 v0 du dv |
UV drift on a TEXTURE entity (see below) |
Colours are floats in 0..1. Distances/positions are world units. Angles are in
degrees. name is resolved to a geometry file by search path (see Resolution).
Instance transform
From FLYK.C + MATRIX.C. DPL uses a row-vector convention — a point is a row
and transforms as p' = p · M, with translation in the bottom row of the
matrix and each op post-multiplied (M = M · op). STATIC builds:
M = Rz(az) · Rx(ax) · Ry(ay) · Scale(s) · Translate(x, y, z)
p_world = [x y z 1] · M
so a point is rotated (Z, then X, then Y), scaled, then translated into place.
Rotation matrices (row-vector form, c=cos, s=sin):
Rz = [ c s 0 0] Rx = [1 0 0 0] Ry = [c 0 -s 0]
[-s c 0 0] [0 c s 0] [0 1 0 0]
[ 0 0 1 0] [0 -s c 0] [s 0 c 0]
[ 0 0 0 1] [0 0 0 1] [0 0 0 1]
LIGHT's three angles orient a directional light the same way (Rz·Rx·Ry).
Asset resolution
-
Models:
name→<name>.B2Z(also.BIZ/.V2Z), found on a search path (originally theGEOMETRYenv var; here: index of the archive by basename). -
Materials & textures (authoritative): a geogroup carries a material name (e.g.
redrock_mtl). The real definition lives in the model's.V2Zsource (the verbose text twin of the binary.B2Z— magicDIV-VIZ2), which inlines its material library:TEXTURE(NAME=redrock_tex) { MAP {"rocknoiz"} ... } MATERIAL(NAME=redrock_mtl) { TEXTURE {redrock_tex} DIFFUSE {0.743, 0.216, 0.202} }So a material = a texture (→
<map>.SVT) modulated by a DIFFUSE colour. The final surface colour istexel × diffuse × lighting. Material names are scoped per model (the same name can differ between themes), so each model's own.V2Zis the source of truth —parser/bundleparse it per model.- Two kinds of texture, handled differently:
- Full-colour painted skins (vehicle textures like
VTVCAB) are shown as-is (tint = white); their colour lives in the texels. - Greyscale / intensity "noise" maps (
CANAL,ROCKNOIZ) are tinted by the material diffuse —techy(white) × CANAL = grey concrete,rednoise(red) × CANAL = red pipes. Some (e.g.CANAL,VTVALL) have a dead colour channel and are rebuilt as luminance first. The loader auto-classifies by saturation.
- Full-colour painted skins (vehicle textures like
- Prefer the material's per-part skin
<stem>.SVT(e.g.vtvcab_mtl→VTVCAB.SVT) over any atlas the material names (vtvall) — the per-part skins match the model UVs; the atlas tinted by diffuse does not. - Pair geometry and materials from the same source directory. The same asset
exists in several theme folders (
CONVERT/THOR/,CONVERT/CANALS/, …) and the same material name is defined with different colours in each (THORrednoise= vivid red0.83,0.006,0.006; CANALS = salmon0.69,0.41,0.40). Loading.B2Zfrom one folder but.V2Zfrom another mixes them and yields the wrong colour.bundle.v2z_for()resolves the sibling.V2Zfirst. - Fallback: only when a model has no
.V2Zdo we fall back to the old stem/keyword heuristics.
- Two kinds of texture, handled differently:
Output gamma (the Division DAC)
Material/diffuse values are linear. The Division i860/pxpl5 card output through a
10-bit DAC with a gamma-1.7 encode — from DPL3/GAMMA.C, which builds a
256→1023 table out = 1023 · (i/255)^(1/1.7). This brightens midtones and gives the
characteristic bright/pastel look. The viewer reproduces it by applying
colour^(1/1.7) as the final fragment step (and to the background clear). Without
it, linear output on an sRGB display looks too dark and over-saturated.
Surface merging (a bug worth noting)
When baking a scene, geometry is merged into surfaces to cut draw calls. Surfaces
must be keyed on (texture, diffuse-colour), not texture alone — otherwise two
materials that share a texture but differ in tint (grey columns vs red pipes, both
on CANAL.SVT) merge into one and the second colour is lost. This was the cause of
"the pipes render grey".
Scrolling textures (SCROLL)
SCROLL <texname> u0 v0 du dv e.g. SCROLL btfx:firesmoke1_scr_tex 0 0 0.05 -0.331
Animates a texture's UV coordinates — flowing water, drifting smoke, rising
fire, laser-beam flow (287 uses across the disk). texname addresses a
TEXTURE entity (not a bitmap): either lib:name in a named material
library (.VMF/.BMF) or a plain name from the model's own .V2Z/.VGF
source. u0 v0 is the initial offset, du dv the drift rate — the viewer
plays it as UV/second (rate units are inferred; the engine source for this
path is not on the disk).
The scene command is actually an override: the same data is baked into the
texture entity itself as a SPECIAL hook in the material source —
TEXTURE(NAME="firesmoke1_scr_tex";SPECIAL=" SCROLL 0 0 0.05 -0.331 \0x00")
{ MAP {"bintA"} }
so a texture scrolls even in scenes/maps with no SCROLL line, and several
entities can share one bitmap (bintA) at different rates. In the binary
.BMF form the SPECIAL string is texture sub-block tag 0x037 (see
B2Z_FORMAT §7). Scroll is a per-texture-entity property, so the baker keys
animated surfaces on the entity, not the bitmap.
Extended commands (game-tool scenes)
Larger scenes authored by the game tools use more keywords. The current loader recognises and skips these; they are documented here for completeness:
MORPH (animated geometry) · SPECIALFX (particle/effect hooks) ·
TEXTURE / GEOMETRY / MATERIAL / RAMP
(explicit asset declarations) · ZONE / START / END (zone blocks) · CLIP
(near/far clip) · VIEWANGLE (fov) · TRACKER / RETRACE / KEYRATE /
GEOMETRIZE / IMMUNE / DRAWLAST / NOVIEWMATRIX / BILLBOARD.
Validation
parser/scn.py parses CANAL.SCN → fog, background, ambient, 1 light, 47
instances (5 unique models, all resolved). viewer/bundle.py assembles it into
world space and renders it: a Red Planet canal — 22 red-rock cliff segments
lining a channel, 22 struts, a VTV "mule" vehicle, rock and tech-junk — with
reddish fog. See viewer/preview_scene.png.