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

forked from trevato/cs

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

shipped production is serving it opened this has it
opened this

Once a frame is plain data (#47), the renderer holds nothing Counter-Strike's except the meshes and the atlas it is handed, so it moves to src/engine/renderer.ts and the range in #13 can draw with the same 700 lines of WebGL2 instead of a second copy.

This is the other half of #5, and the last thing the direction's headline promises that nobody has started: rendering behind the door.

Done looks like

  • [ ] src/engine/renderer.ts imports only from src/engine/, which boundary.test.ts proves with no new rule: no world.ts, weapons.ts, map.ts, materials.ts, models.ts or physics.ts.
  • [ ] createRenderer(surface, { atlas: { bytes, size, layers }, meshes, sky }) uploads the atlas and every named mesh once at construction and keeps them by name; draw(scene, dt) takes the Scene from #47 with mesh as a name, and resize() and gl stay as they are. A name a scene asks for that was never handed in throws once at construction time, not per frame — a missing mesh is a programming error, and the message says which name.
  • [ ] src/engine/index.ts gains the door's renderer paragraph and exports createRenderer with Renderer, Scene, Camera, Instance and RendererAssets as types; the door stays cheap to import, so nothing at module scope touches a canvas or a WebGL2RenderingContext.
  • [ ] src/client/ hands its own assets in: materialTextures(), TEX_SIZE, LAYERS and the four meshes from models.ts are passed at construction from main.ts, and src/client/renderer.ts is gone rather than left as a re-export.
  • [ ] A test drives it without a browser: the engine's renderer returns null when the surface gives no webgl2 context, and a stub context — the same shape the range's Paint does for 2D — records that construction uploads one texture and one buffer set per mesh handed in and that a scene with two instances of one mesh binds that mesh's vertex array once. It must not need a GPU.
  • [ ] The game looks the same: the golden digest unchanged, the preview compared against production at 360px and 1920px, and #fps read before and after and stated in the pull.
  • [ ] bun test green, the bundle rebuilt with bun run scripts/build.ts, the new static/app.<hash>.js committed, the size ceiling from #39 still held, and README.md's engine list gains the renderer.

Files I expect to change

src/engine/renderer.ts (moved from src/client/renderer.ts), src/engine/renderer.test.ts (new), src/engine/index.ts, src/client/main.ts, src/client/scene.ts (the Scene type moves behind the door and this imports it), src/engine/boundary.test.ts (nothing but the file list it walks), README.md, static/app.<hash>.js.

Risks

The atlas is the seam that matters: the shooter's shaders assume TEX_SIZE 128 with LAYERS layers in a TEXTURE_2D_ARRAY and mipmaps generated at upload, so the engine must take size and layer count as data and not bake either, and a game that hands a bytes length other than size * size * 4 * layers should be told so at construction. Stubbing WebGL is the tedious part — keep the stub to the calls the module makes, in the module's own file, and do not grow it into a fake GPU. Do not tune anything while moving it: same shaders, same uniforms, same culling, same clear colour.

Suggested: Vesper — the renderer is theirs, and this is the move that makes the package a package.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs