Public: anyone can read this; the crew can change it.
forked from trevato/cs · proposes · 4 behind
The scene becomes data: src/client/scene.ts builds a frame, and the renderer draws only that #56
Check · Review · Land · Live done — Landed and live in production.
`src/client/scene.ts` is new and holds every decision a frame used to make inside `draw`: the camera and the sky, the level, each living other player with its team tint and `hullHeight(p)/HULL.stand` squash, the bomb, every tracer/spark/blood/beam as line vertices under the 4096 cap, and the view model's weapon kind, bob and kick. `renderer.ts` now takes `draw(scene, dt)` and imports neither `worl…
shipped production is serving it deploy ae07c2d
Sign in to join this conversation.
new above
offline
src/client/scene.tsis new and holds every decision a frame used to make insidedraw: the camera and the sky, the level, each living other player with its team tint andhullHeight(p)/HULL.standsquash, the bomb, every tracer/spark/blood/beam as line vertices under the 4096 cap, and the view model's weapon kind, bob and kick.renderer.tsnow takesdraw(scene, dt)and imports neitherworld.ts,physics.tsnorweapons.ts— it resolves mesh names throughmodels.ts(meshFor), keeps one upload per name, and grows its line buffer to whatever the scene brings.Cameramoved toscene.ts;main.tskeeps one scene, one camera and oneFloat32Arrayand fills them in place, andbootnow hands back the running frame somain.test.tscan still see and steer the world the renderer no longer carries.What to look at:
scene.test.tsruns headlessly and pins the numbers — a tracer at 40 ms is two vertices and at 120 ms none, an impact is six and dies at 0.25 s, blood at 0.3 s, a planted bomb adds the blinking beam and a carried one adds nothing, a ducking player is54/72and a standing one 1, the camera's own player and a dead one are never instances, 5,000 effects stop at 4,096 vertices, the kick decays to zero over 0.12 s, and two builds return the same object, the same array and the same instance objects. The last test is the boundary itself, read off the renderer's source.replay.test.ts's golden digest is untouched — no simulation changed —bun testis 373 green, andstatic/app.de0b8ba773.jsis the rebuilt bundle (69,711 raw / 32,142 gzip, up 1,254 bytes, under the 75,400/34,900 ceiling, which did not move).On the risk the issue names: I could not diff pixels, so I diffed calls. I ran the previous renderer and this one side by side against a recording WebGL stub over eleven frames — level, both teams, a duck, a death, the bomb planted, tracer and impact and blood together, the awp, scoped, dead, and a walking bob — and the two issued identical draw calls: same meshes, model matrices, tints, view-projections, line buffers and line draws. Two deliberate differences: the clear colour is now set per frame from
scene.sky(same[0.66, 0.73, 0.82]), and the walk bob rides the world's clock instead of the frame'sdt, so its starting phase differs by up to 0.1 s of sway and nothing else — with the clocks lined up the view model matrices match exactly. That harness was scratch and is not committed.What I did not do: I have no browser or GPU here, so I cannot report
#fpsbefore and after — that number needs the preview, and the comparison is worth making there. What I can measure is the work that moved:buildScenecosts 1.57 µs per frame for ten players and 120 line vertices (200,000 builds, Bun 1.3.14 on the forge runner), about a ten-thousandth of a 16.7 ms budget, and it allocates nothing after the first frame.draw(scene, dt)keeps itsdtparameter unused — the bob was the only thing using it — because a renderer's own time-based effects belong there rather than in a scene; say the word and it goes.🤖 Generated with Claude Code
Closes #47