scene: voxel terrain as painter's-order strips, the default simple background #13

Open
nibu wants to merge 7 commits from nibu/voxel-scene into main

2026-11-05

Nibu
scene: rewrap the over-long comment line in index.ts
Some checks failed
Check PR title / lint (pull_request_target) Has been cancelled
CLA Assistant / CLAssistant (pull_request_target) Has been cancelled
Build pull request / Build pull request (pull_request) Has been cancelled
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-05 05:38:51 +00:00
Nibu
scene: strips in one plain draw, not instanced
On SwiftShader the instanced draw (one instance per strip, four vertices) costs about 1.9 s per frame at 320x180 against 11.6 ms for the old march, nearly all of it per-instance overhead: the same strips as six vertices decoded from gl_VertexID in one drawArrays take about 11 ms. The vertex shader now reads strip and corner from gl_VertexID; no buffers, no instancing, same quads, same painter's order. VERTICES_PER_STRIP and LOOKUPS_PER_STRIP follow the new corner layout.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-05 05:38:51 +00:00
Nibu
scene: voxel terrain drawn as painter's-order instanced strips
The fullscreen fragment march is replaced by one instance per screen column and depth step, drawn far to near with no depth test. The vertex shader fetches the strip's height and its nearer neighbour's from the RG8 ring (vertex texture fetch, WebGL2 core) and emits a quad from the projected top down to the nearer top, or the screen bottom for the first strip; the fragment is a flat colour by height, slope and fog. Strips nearer than any terrain could reach the screen are not drawn, and strips past a column's far plane are clipped.

The drawing buffer is a fixed pixel budget in the canvas's aspect, 320x180 by default, with the ladder below it. The ring, its generation pass, the baked sky, the createScene contract and the flag are unchanged; the sky is now a small backdrop pass. FrameStats gains vertices and ring lookups per frame; the measuring pass counts fragments per pixel (overdraw) instead of march lookups. Harness: drive.cjs voxel takes SIZES, a voxelshots mode takes screenshots.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-05 05:38:51 +00:00
Nibu
scene README: the voxel scene
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-05 05:38:51 +00:00
Nibu
scene: voxel terrain scene, the default simple background
createVoxelScene: same contract as createScene, WebGL2 core only (no extension, no float
target). A 256x256 wrapping ring of cell heights (RG8, 16 bit) regenerated one column or
row at a time as the camera crosses cells (Phacelle terrain in world space, a pure function of
the cell); one fullscreen pass marching each screen column near to far, at most 128 lookups,
none for sky; baked sky texture with cloud bands; free-yaw rail camera; drawing-buffer scale
ladder on frame cadence. The door's style defaults to voxel; raymarch stays selectable.
Harness: drive.cjs voxel mode, standalone page with lookups/pixel, frame ms, device panel.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-05 05:38:51 +00:00
Nibu
scene: shader linking in link.ts, BudgetLadder takes its levels, test GL fake shared, SceneStyle in the config
Groundwork for a second scene behind the same createScene contract: the compile/link path
and ShaderCompileError leave Scene.ts so a scene need not import the other; BudgetLadder is
generic over its levels (default unchanged); the fake GL moves out of Scene.test.ts;
backgroundScene.style ('voxel' | 'raymarch') is typed; SceneOptions gains onFrameStats and
clock (frame cadence without timer queries; a camera clock for tests and measurement).
No behaviour change.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-05 05:38:51 +00:00
Nibu
scene: frame cadence moves to frameLoop.ts so a second scene can share it
Cap, visibility pause, reduced-motion bound, still, wake on resize and context loss were
inline in createScene. Moved verbatim into startFrameLoop; Scene.ts keeps only what it owns
(history reset on resume, ladder step on a missed cap). No behaviour change: the scene tests
pass unchanged.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-05 05:38:51 +00:00