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

forked from trevato/cs

The scene becomes data: src/client/scene.ts builds a frame, and the renderer draws only that #47

shipped production is serving it opened this has it
opened this

src/client/renderer.ts takes a World and reaches into physics.ts and weapons.ts to decide what a frame looks like, so no second game can draw with it and nothing about a frame can be tested; the decisions move to src/client/scene.ts as plain data and the renderer draws only that.

This is the half of #5 that needs neither #3 nor the engine: the renderer stays in src/client/ here, keeps its models.ts and materials.ts imports, and #B moves it behind the door afterwards. None of the files below are among pull #7's seven, so this can be worked while physics waits.

Done looks like

  • [ ] src/client/scene.ts exports a Scene — { camera, sky, instances: { mesh, pos, yaw, scale, tint }[], instanceCount, lines: Float32Array, lineCount, viewModel: { mesh, offset } | null } — and buildScene(world, camera, into) fills a scene handed to it rather than returning a new one.
  • [ ] src/client/renderer.ts exports draw(scene, dt) and imports neither world.ts, physics.ts nor weapons.ts: the duck squash through hullHeight, the team tints, the bomb, the weapon kind behind the view model and the effect fades are all decided in scene.ts. Camera moves to scene.ts with it.
  • [ ] Draw order and look are untouched: level, other living players, the bomb, the lines, then the view model on a cleared depth buffer with its own 58° projection; the same fog, the same sky [0.66, 0.73, 0.82], the same two programs.
  • [ ] The effect rules keep their numbers exactly — a tracer for 0.08 s fading to nothing, an impact for 0.25 s as three sparks along the normal, blood for 0.3 s, the planted bomb's blink at sin(time * 8) — and LINE_CAPACITY stays 4096 vertices, enforced in scene.ts now.
  • [ ] src/client/scene.test.ts runs headlessly with no canvas and would fail without this change: a tracer 40 ms old yields two vertices and one 120 ms old none, a planted bomb adds the beam and a carried one does not, a ducking player's scale is hullHeight(duck)/stand and a standing one's is 1, the camera's own player is never an instance, a dead player is not drawn, a flood of 5,000 effects stops at the cap, and the view model's kick decays over 0.12 s.
  • [ ] Nothing is allocated in the frame: main.ts keeps one scene object and one Float32Array, and a test asserts two buildScene calls return the same object identity and the same array identity.
  • [ ] The golden digest in src/client/replay.test.ts is unchanged — this touches no simulation — bun test is green, bun run scripts/build.ts is rerun and the new static/app.<hash>.js committed, and the pull says what #fps read before and after on the preview.

Files I expect to change

src/client/scene.ts, src/client/scene.test.ts (new), src/client/renderer.ts, src/client/main.ts, README.md, static/app.<hash>.js.

Risks

This is the change that can quietly make the game look wrong and no test sees pixels, so move code rather than rewriting it: lift each block of draw into a scene decision in the same order, and diff the preview against production before pushing. The other trap is allocation — today's draw allocates nothing per frame, and a scene built fresh every frame would be a regression the fps counter shows on a phone, not here. Keep hullHeight imported from physics.ts in scene.ts as it is named today; if #7 lands first it becomes hullHeight(body, CS_HULL) and that is a one-line rebase, which is the reason this issue exists.

Suggested: Vesper — WebGL2 by hand with the frame time measured and stated is the brief.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs