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

64 lines
2.3 KiB
Markdown

# `.SPL` camera-path spline format
Reverse-engineered from `DPL3/EXAMPLES/SPLINE.C`. A `.SPL` drives a camera (or a
`DYNAMIC` object) along a smooth path. It is **plain text**:
```
N number of control points
x y z ax ay az x N -- position + euler angles (degrees)
```
`ax ay az` are rotations about X, Y, Z (same convention as `STATIC`/`LIGHT` in the
scene format). The path is a **closed loop** (the last point links back to the
first).
## Interpolation
Each of the 6 channels (x, y, z, and the three angles) is splined independently as
a **cubic Hermite** curve with **Catmull-Rom tangents**:
```
tangent at knot i (position): vel = (pos[i+1] - pos[i-1]) / 2
tangent at knot i (angle): rot = wrap(ang[i+1] - ang[i]) / 2
```
Per segment `i -> i+1`, with endpoint values `v0,v1` and tangents `d0,d1`, the
cubic `v(t) = a t^3 + b t^2 + c t + d` (`t` in 0..1) is:
```
d = v0
c = d0
a = d1 + d0 - 2*v1 + 2*v0
b = v1 - v0 - d0 - a
```
(Angle segments additionally subtract 360 from a tangent > 360 before solving, as
in the original `solve_rot_cubic`.) `walk_spline` advances `t` by a step, rolling
over to the next/previous knot at the 0..1 boundaries — a constant-`dt` walk, so
speed follows the control-point spacing (closely spaced points = slower).
## From a path sample to a camera
At parameter `t` the sampled `(pos, ang)` becomes a camera basis. Build the DPL
rotation `R = Rz(az) . Rx(ax) . Ry(ay)` (row-vector; see `SCN_FORMAT.md`), then:
```
forward = (0,0,-1) . R # the camera looks down its local -Z
up = (0,1,0) . R
eye = pos
center = pos + forward
```
The `-Z` local-forward is confirmed by `CAMERA.SPL`: it starts at `(0,310,-500)`
with `ay = 180`, which rotates local `-Z` to world `+Z` — i.e. the camera looks
into the scene, which extends toward `+Z`.
## Validation
`parser/spl.py` evaluates `CAMERA.SPL` (20 control points) into a smooth 240-frame
loop, eye path `X[-260,230] Y[-15,842] Z[-759,58519]` — matching both the file's
control points and the `RAPTOR.SCN` extent (`Z[-30000,75000]`). Rendered frames
from the path (`viewer/flythru_*.png`) show a coherent first-person flight down the
Raptor canal, banking as the euler angles interpolate. In the viewer, the fly-
through animates the full loop; dragging drops back to manual orbit.