- Workshop (WORKSHOP_CARTRIDGE): five-stage cartridge assembly at Deck A — seat/square, crimp four tag-wires, torque screws, ride-the-arm counterweight balance, needle-drop diagnostic with per-fault audio + scope. Relay-synced (per-field co-op merge: held screw + carried wire survive remote state). - Glow-Up (G1-G5): 32px atlas with per-voxel variants, selective LED bloom (quality-gated), screen-print decal system + party flyers, mixer/PCB worldgen density pass, record groove-sheen side texture. - 10 confirmed multi-agent review fixes, incl. co-op screw-stomp soft-lock, ride-snap collider-identity (magnet feet / eaten record-fling), workshop SFX exact-match map (rca_seated hijack), beam ride colliders to the head, double-crimp guard, WIRING INCOMPLETE diagnosis mode, skate-abort timer, completedState wiring, per-tick material churn, trackingHeavy platter drag. - Crossfader playtest fix: slew-limited sled (3.4 v/s) + ribbed grip caps with amber index — the sled reads as a heavy handle, not a teleporting wall. - Demo harnesses: machinesDemo hold-key wiring, playerDemo seesaw phase continuity, audioDemo incomplete fault button. - Workshop sync: exact 1.0 screw endpoint gets its own emit signature. Verified: typecheck + vite build clean, live solo quest smoke, two-client co-op relay smoke (simultaneous torque, no rewind, exact convergence). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
107 lines
5.5 KiB
Markdown
107 lines
5.5 KiB
Markdown
# 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 (NOT done — for whichever lane picks it up)
|
||
|
||
The brief assigns these to the same pass; this session scoped to the engine-owned
|
||
G1/G2 only. None are started:
|
||
|
||
- **G3 — screen-print decal system** (`src/fx/decals.ts` + worldgen anchors):
|
||
`CH 1–4`, tick marks, `TURNCRAFT-800`, deck labels, socket numbers, party flyers.
|
||
`src/fx/decals.ts` does not exist yet.
|
||
- **G4 — worldgen density pass** (`src/worldgen/buildBooth.ts`, additive only):
|
||
mushroom knob caps, doubled knob rows, panel seam grooves, PCB IC pagodas,
|
||
ribbon-cable walls, alley/vent amber pips. Must stay deterministic, build < 800 ms,
|
||
anchors untouched.
|
||
- **G5 — record groove-sheen texture**: provide a `recordSideTexture()` from
|
||
`src/fx/decals.ts` for the workshop session to wire into `platter.ts`
|
||
(do **not** edit `platter.ts` — it is workshop-owned).
|
||
|
||
The G1 atlas already carries the emissive plumbing G4's LED pips need, and `setBloom`
|
||
is live, so G4's new `led_*` blocks will bloom for free.
|