Public: anyone can read this; the crew can change it.

forked from trevato/cs · proposes · 4 behind

The renderer moves behind the door: src/engine/renderer.ts takes an atlas and named meshes #65

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

Pushed. Both commits are on the branch and `bun test` is green (415 pass).

shipped production is serving it deploy ae07c2d

opened this

Pushed. Both commits are on the branch and bun test is green (415 pass).

---

The WebGL2 moves to src/engine/renderer.ts unchanged — same two programs, same uniforms, same culling, same near/far planes, same draw order — and takes what it used to reach for as data: createRenderer(canvas, { atlas: { bytes, size, layers }, meshes, sky, devicePixelRatio }) uploads the array texture and every named mesh once at construction and keeps them by name, so a frame does no upload at all. Scene, Camera and Instance move behind the door with it; src/client/scene.ts is now the engine's frame plus the two things this game remembers between frames, src/client/main.ts hands in materialTextures()/TEX_SIZE/LAYERS and all eight meshes from models.ts, and src/client/renderer.ts is gone rather than left as a re-export. The diff worth reading is git show 8cc8a81 -- src/engine/renderer.ts against the deleted file: outside the types and the asset plumbing, nothing in the GL sequence moved.

src/engine/renderer.test.ts drives the module with a stub context that only writes down what it was asked for — the same trick overlay.test.ts plays with Paint — and checks one texture and one buffer set per mesh handed in, no bufferData/texImage3D in a frame, one vertex-array bind for two instances of one mesh and two for two meshes, the clamped device pixel ratio, lines drawn in pairs, the view model's own depth clear, and null when the surface gives no webgl2. main.test.ts gains a test on gameMeshes(), because the renderer resolving nothing means a name that table misses is an exception on the frame a player first holds that gun.

Three places where the move is not literal, all deliberate: the device pixel ratio arrives as a function instead of the engine reading window (same Math.min(dpr, 2) clamp, so the same pixels); sky is optional on both the assets and the Scene, so a game whose horizon never changes says it once — the shooter still writes it every frame and also passes it at construction, so nothing about its colour changed; and a mesh name nobody handed in throws on the first frame that asks for it rather than at construction, since only a scene knows which names it will use — construction is where the atlas is checked (size * size * 4 * layers, refused before a context is even requested, with both numbers in the message).

What I could not do here: there is no browser or GPU in this sandbox, so the #fps reading before and after and the 360px/1920px comparison against production are a reviewer's checks on the preview, not claims I am making — please do them. What I can measure: the golden digest and the range's pin are untouched and green, the eight meshes cost about 2 ms to build at start-up (the atlas beside them is ~120 ms, exactly as before), and the app bundle is 70,558 raw / 32,850 gzipped against a 75,400 / 34,900 ceiling, while static/range.*.js is unchanged at 12,548 bytes — the range imports the door and none of the WebGL2 behind it, which is the tree-shaking doing its job. GUIDE.md gains the renderer's paragraph and two quoted lines, and the README's engine list gains the module.

🤖 Generated with Claude Code

Closes #48

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs