# Division Renderer Collaboration Log Shared working doc for the two renderer efforts against the VPX/Division hardware interface: - **In-process renderer** — GL HLE inside the DOSBox-X fork's VPX device (`src/src/hardware/vpxlog.cpp`, commit-copy `vpx-device/vpxlog.cpp`). Maintained by Cyd + Claude (this repo's sessions). - **External renderer** — separate approach by the graphics developer + their Claude instance. *(Fill in your section below.)* **How to use this file:** each session (human or Claude) reads it at start, appends to its own section and the Log, and commits. Don't rewrite the other team's sections — append questions/answers instead. Commit messages prefixed `collab:` so both sides can `git log --grep=collab:` for changes. --- ## Ground-truth sources (in priority order) 1. `sda4/DPL3/LIBDPL/DPLTYPES.H` + `DPL.H` — the dVS/DPL node structs and API. 2. `sda4/DPL3/VRENDER/` — Division's own i860 renderer source (rasterizer semantics; the ramp/texture/light questions below should be settled here). 3. `ALPHA_1/REL410/BT/BTDPL.INI` — per-arena ground truth the game actually loads: `fog=start end r g b`, `light=r g b pitch yaw roll`, `ambient=`, `clip=`, `objectpath=` (arena content dirs), keyed by egg map/time/weather. 4. `restoration/` — WebGL re-render of the on-disk scene formats (`divformats.py` parsers, `gallery_template.html` shading model). Proven against 26 recovered scenes; its mesh shader is the reference shading model (see below). 5. Live wire captures: `VPXLOG=` logs every port I/O; scratch tooling (`dump_nodes.py`-style parsers) has decoded node flushes from fifodumps. ## VPX board interface — verified on live captures Register offsets (host side of the i860 board FIFO link): `0` inputData (board→host), `1` outputData (host→board), `2` inputStatus (bit0 = byte available), `3` outputStatus (always ready), `4/5` FIFO data port link B low/high bytes, `16` resetRoot, `17` analyseRoot. ### Action codes (FIFO message stream) | action | meaning | |---|---| | 1 | create `[type][name]` | | 3 | flush `[name][type][struct]` — struct offsets below are into the payload `d`, where `d+8` = struct offset 0 | | 7 | dcs_link `[parent][child]` — articulation tree | | 9 | draw_scene — frame commit | | 11 | list_add `[parent][child]` | | 23 | set_geom_verts header `[name][0][n_verts][stride_floats]` + vertex bursts; stride 3..16 floats/vert: xyz, +uv at 3 (stride 5) or 6 (stride ≥8), +normal at 3 when stride ≥8 | | 25 | set_geom_conns header `[name][n_polys][loop_len][0]` + index words (closing duplicate dropped) | | 26 | texel upload header `[node][nbytes][w][h]` + texel bursts (4/8/16 bpp, bpp inferred from byte/texel ratio) | | 31 | per-frame camera `[?][view][3x3 rows][eye]` | ### Node types (create/flush `type` field) | type | node | verified layout (flush payload `d`) | |---|---|---| | 2 | zone | raw-logged only | | 3 | view | win l/b/r/t/dist `d+24..40`, vw/vh `d+44/48`, near/far `d+52/56`, back_color `d+60..68`, **fog enable/mode `d+72`, near `d+76`, far `d+80`, rgb `d+84..92`** (mode 5 = linear; game ANIMATES fog and re-flushes ~27×; boot flushes carry heap garbage — sanity-check floats) | | 4 | instance | object ref scanned at `d+8..` (resolves to a type-7 node) | | 5 | dcs | 4×4 row-major at floats 4..19 (`d+16..`); row 3 = translation; world = local × parent (row-vector convention) | | 6 | lmodel | raw-logged only | | 7 | **object** (instance target in the scene-graph walker) — **CONFLICT:** the flush handler currently raw-dumps `t==7` as a *light* candidate ("t7 light=" lines in the tex log). Both can't be right; the real dpl_LIGHT wire type is still unidentified. Reconcile against DPLTYPES.H + a live dump. | | 9 | geogroup | material ref at `d+64` | | 11 | material | texture ref `d+8`, ramp ref `d+20`, emissive `d+24..32`, ambient `d+36..44`, diffuse `d+48..56`, opacity `d+60`, specular `d+72..80`, shininess `d+84` (84-byte struct; refs resolve lazily — a ref may precede its create) | | 12 | dpl_TEXTURE params | texmap ref `d+8`, then minify/magnify/alpha/wrap_u/wrap_v/detail (u32s) + u0/v0/du/dv/anim_time (floats) + anim_bhv — logged (`t12` lines), **semantics not yet applied** | | 13 | texmap header | texels ptr, u_size, v_size, bits_per_texel (+ hwareSize/hwareOffs/bilinear) | | 14 | ramp | lo RGB `d+8..16`, hi RGB `d+20..28`; texels are intensities colorized through the material's ramp | ### Scene assembly & conventions - Draw hierarchy: DCS (type 5) children (via list_add) → instance (4) → object (7) → **first LOD child** (host keeps active LOD at list head; the wire carries no switch distances) → geogroups (9) → geometry. - World is **y-down** (DCS matrices carry reflections); Division screen x is mirrored vs GL eye x (flip x after projection). - Vertices reach the renderer already in world space; the modelview holds only the camera (action 31: row-major world→eye rotation + eye pos, `eye_p = R·(p − e)`). - Frustum from view win[]: `glFrustum(win[l/r/b/t] · near / win[4], …)`. - **The camera only goes live after the first analog (throttle/stick) input** — until then the game submits no worklist and the screen is the back_color clear. Hands-off runs look like "solid sky"; check the radar head (win0 BMP dump) to confirm the mission actually loaded. ## In-process renderer — status (2026-07-04) The device now runs the restoration gallery's shading model verbatim as a GLSL 1.10 program (fixed-function fallback; stderr prints `VPX render: gallery shading model active (GLSL)`): ``` acc = Σ_i lightCol_i · max(dot(N, −lightDir_i), 0) (world space, 2 dir lights) lit = mix(ramp.lo, ramp.hi, clamp(acc, 0, 1)) c = (matAmb·sceneAmb + matDiff·lit) · tex + matEmis out = mix(fogColor, c, max(clamp((fogEnd−d)/(fogEnd−fogStart), 0, 1), immune)) ``` Interim until the light node is decoded: hardcoded sun + sceneAmb 0.45, overridable via `VPX_AMBIENT="r,g,b"` / `VPX_SUN="r,g,b,pitch,yaw"` (deg, y-down). Fog comes from the type-3 view flush. Opacity/specular parsed but not applied. Polys without wire normals get Newell flat face normals. **Ramp interpretation (open):** wire textures are intensity texels baked through the material ramp at upload; the gallery applies the ramp to *light* (disk textures are RGB). The device currently avoids double-application by using an identity light-ramp on textured polys. VRENDER source should settle whether Division ramps texels, light, or both. ## Data taps available to an external renderer (today) - `VPXLOG=` (host env): full port-I/O log incl. every FIFO byte — replayable; parse with the action/type tables above. - `VPX_DUMPDIR=` + touch `/DUMP`: per-head BMP dumps (`win.bmp`, g=0 radar, 3/4 MFD heads, 5-9 exploded singles). - Device tex log: `t7`/`t12`/`t13` raw flush lines for undecoded nodes. - `RIO_TAP=`: serial wire tap (not renderer-relevant, but proves rig liveness). - **Offer:** if a structured per-frame scene export (JSON/binary worklist after graph assembly, i.e. post-DCS world-space polys + materials) would feed your renderer better than raw FIFO, ask here — straightforward to add behind an env var. ## Open questions (either side, answer in place with date) 1. Real dpl_LIGHT wire type + layout (type 7 conflict above). BTDPL.INI `light=` lines give expected values to match against. 2. Type-12 texture param semantics (wrap/clamp/uv-window/animate) — cause of smeared wall textures? 3. Ramp on texel vs ramp on light (VRENDER). 4. Terrain + sky in arena missions: what content/nodes carry them? (Live arena1 renders structures but almost no ground; sky is back_color only.) 5. Opacity blending order/mode (glass cockpits, effects). 6. Scene ambient: which node carries it at runtime (lmodel type 6?). ## External renderer — approach & status *(Yours — please fill in: approach, inputs consumed, what you need from the device side, decisions taken. Append, date-stamp.)* ## Log - **2026-07-04 (Claude, in-process side):** doc created. Gallery shading model ported into the device and verified live in arena1 (lit faces, plausible wire fog). Full material parse landed. Next in-process steps: light node decode, type-12 params, restoration-toolkit reference render of arena1 content.