# Glow-Up (Lane A / engine) — Handoff **Brief:** [docs/briefs/GLOWUP_ART.md](briefs/GLOWUP_ART.md) **Scope done here:** **G1 (Atlas v2)** and **G2 (selective LED bloom)** — the two engine-owned tasks. **G3–G5 are NOT started** (see "Remaining" below). **Status:** ✅ G1 + G2 complete, browser-verified in the real game, typecheck clean, zero console errors. --- ## What shipped ### G1 — Atlas v2 ([src/engine/atlas.ts](../src/engine/atlas.ts)) - Tiles **16 → 32 px**. `NearestFilter` chunkiness preserved (fract keeps every sample inside its cell — no bleed, no mip seams). - **Per-voxel variant system, zero mesher changes.** 4 painted variants per block laid out in extra atlas columns (`GRID_W = COLS*NVAR = 32`). The material shader patch hashes each voxel's baked world position (`floor(position - 0.02*normal)`) → picks a variant → expands the base cell column. A repeated surface (PCB floor, plywood wall, brushed metal) breaks up instead of tiling like wallpaper. The mesher still writes only the base cell `aTile = (id%8, id/8)` — its verified winding/AO path is untouched. (This is the brief's "shader-patch variant" fallback, applied to **all** patterns, not just pcb.) - Pattern rewrites: `brushed` = long directional strokes + scratches (not noise); `plywood` = long wavy grain, concentric end-grain rings for `ply_edge`; `pcb` = 4 variants that tile into routed traces + via dots + silkscreen flecks; `grooves` = concentric arcs + specular sheen line; `led` = smooth radial glow. - Material polish via roughness only: `0.82 → 0.78` (no env maps, per brief). ### G2 — Selective LED bloom ([src/engine/renderer.ts](../src/engine/renderer.ts)) - `EffectComposer` + `RenderPass` + `UnrealBloomPass` + `OutputPass`. Tuned `{ strength 0.9, radius 0.5, threshold 0.85 }` so **only emissive LEDs/strobe/lamps cross the threshold** — lit plywood/metal stays matte. - **Lazy:** the composer is created only on first `setBloom(true)`, so `fast` quality never allocates it. `render()` picks composer-vs-direct internally; callers keep calling `render()` unchanged. - New API on the renderer: `setBloom(enabled: boolean)`. Composer size/pixelRatio re-sync on resize and on quality (pixelRatio) changes. ### Wiring (merge points — kept additive) - **[src/main.ts](../src/main.ts)** — destructure `setBloom`; in the Menu `onApply`: `setBloom(s.bloom && s.quality !== 'fast')`; added `setBloom` + `setEmissiveBoost` to the `window.TURNCRAFT` dev handle (used for cine screenshots and fps benchmarking). *This file is shared with the Workshop brief, which added its own `interaction.*` / `jumpScale` lines — the edits are disjoint.* - **[src/ui/Menu.ts](../src/ui/Menu.ts)** — `bloom: boolean` in `Settings` (default `true`), one checkbox row ("LED bloom (glow — off automatically on Fast)"). --- ## Files touched | file | task | note | |---|---|---| | `src/engine/atlas.ts` | G1 | rewritten — 32px, variants, patterns | | `src/engine/renderer.ts` | G2 | +bloom composer, `setBloom()` | | `src/ui/Menu.ts` | G2 | +`bloom` setting + checkbox | | `src/main.ts` | merge | additive: `setBloom` wiring + dev handle | **Merge points:** `src/main.ts` (shared with Workshop brief), `src/ui/Menu.ts` (one settings row). `src/worldgen/buildBooth.ts` is listed as a merge point in the brief but G1/G2 did **not** need to touch it (that's G4). --- ## Verification (ran it in the real game, port 5173) - Boot: **739 ms build, 467 chunk meshes** (< 600 ✓), zero console errors. - **Bloom A/B at the mixer** (retina 2560×1440): LEDs (knob caps, VU towers, fader-alley pips) glow with a soft halo when ON, flat when OFF; plywood floor, matte-black body, and walls do **not** glow either way → **selective confirmed**. - **PCB Depths**: the `pcb` floor now reads as routed circuitry (traces + vias + silkscreen), not "green wallpaper"; variant hashing tiles it into a connected grid. - **Deck platter**: record shows concentric groove rings; brushed-metal plinth reads brushed; amber spindle lamp + orange strobe rim-pips bloom while the plinth stays matte. - **fps** (real, `readPixels`-forced GPU sync at 2560×1440, two runs): - Bloom **OFF**: ~234 / ~249 fps - Bloom **ON**: ~174 / ~220 fps (bloom overhead ≈ 1.5 ms/frame) - Both comfortably clear the **≥ 100 fps** budget. ✓ - (rAF is throttled in the headless preview pane, so frame-count fps reads 0 there; the synchronous per-frame render timing above is the trustworthy measure.) - `npm run typecheck`: **clean** (exit 0), including the concurrent Workshop edits. --- ## Remaining Glow-Up work — UPDATE (re-checked after G3/G4 landed) This session scoped to the engine-owned **G1/G2** only. At the time of writing, G3–G5 were not started. **They have since been implemented by another session** — re-verified against the live build: - **G3 — screen-print decals**: ✅ landed. `src/fx/decals.ts` exists; the booth logs `screen-print decals: 13 draw calls (budget < 25)` on boot. `CH 1–4`, tick marks, `TURNCRAFT-800`, crossfader `A`/`B`, `PATCH BAY`, and wall party flyers all render. - **G4 — worldgen density**: ✅ appears landed (denser/mixed knob clusters + lit fader-alley pips visible at the mixer face; build still ~752 ms, 467 chunk meshes). - **G5 — record groove-sheen**: ✅ `recordSideTexture()` is exported from `src/fx/decals.ts:348` for the workshop session to wire into `platter.ts`. Nothing here changes G1/G2. Confirmed on the integrated build: `npm run typecheck` clean (exit 0) with the Workshop + decal code merged; booth boots with **zero console errors**; at the mixer the alley pips / VU towers bloom while plywood and the matte-black body stay matte (**selective bloom still correct** next to the new decals); Atlas v2's grain/knob detail reads correctly under the new screen-print. As predicted, G4's `led_*` pips bloom for free through the existing emissive plumbing.