From 30deed10dcd7baa3d15dd982169255013eb57a3b Mon Sep 17 00:00:00 2001 From: type-two Date: Fri, 17 Jul 2026 17:36:09 +1000 Subject: [PATCH] [render] round 2 NOTES Verification numbers, the MachineDef.color conflict written up in full, and 3 contract requests (settle color's meaning, EntityState.scrammed, BeltItem.side). Co-Authored-By: Claude Opus 4.8 --- fktry/lanes/LANE-RENDER.md | 108 +++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/fktry/lanes/LANE-RENDER.md b/fktry/lanes/LANE-RENDER.md index 9a30d80..55fccc1 100644 --- a/fktry/lanes/LANE-RENDER.md +++ b/fktry/lanes/LANE-RENDER.md @@ -150,3 +150,111 @@ SHIPPED: / DECISIONS: / BLOCKED/BROKEN: / CONTRACT REQUEST: / PROPOSAL: / NEXT: - AnimationMixer for rigged GLBs; per-era skinning hooks; splitter/lane visuals; heat shimmer + throttle read-out on the software decoder; belt corner/junction pieces (currently every belt is a straight plate regardless of neighbours). + +### Round 2 — 2026-07-17 — Opus 4.8 + +**READ THIS FIRST — I did not follow order 2's letter, deliberately.** +`MachineDef.color` is wired to the machine **body**, not the emissive accent, against +the stated precedence (`def.color` > derived > kind). Why: all 21 values LANE-DATA +authored are desaturated industrial neutrals that match codex CHASSIS descriptions — +`seam-extractor #8a7a32` is §4's "grimy yellow chassis", `decode-asic #b9bec4` is its +"server-blade monolith", `mosh-reactor #52333e` its pressure vessel. Style guide §8 says +ALL saturation belongs to media/artifacts and each machine gets exactly ONE impossible +element. Wired to the accent (I built it that way first and looked at it), all 21 +machines glow muddy neutral: the quantizer's jaw went from coefficient-cyan to tan, the +mosh reactor's from melt-orange to mauve, and the two-material rule quietly died. +Read as bodies they're exactly right, and we get both: 21 art-directed chassis AND the +vivid resource accents. The black-on-black problem the field was meant to solve stays +solved by round 1's legibility floor. **This is a cross-lane semantic conflict between +my orders and DATA's — please settle it; see CONTRACT REQUEST 1.** If you want the +literal reading, it's a two-line revert in `registry.ts` (`bodyFor`/`accentFor`). + +**SHIPPED** — all 6 orders, each verified in the running build: +1. **Belt topology** (`topology.ts`, new): neighbour analysis → straight / cornerA / + cornerB / merge, one InstancedMesh per (def × shape), flow field baked into each + shape's shader (no runtime branch). Corners sweep a quarter-arc; merges add inlet + arrows. Verified: the devscene bend classifies **20 straight / 1 cornerA / 1 cornerB + / 1 merge** across 23 belts, still 4 draw calls. + - **Cargo now follows the corner arc too.** Corners exposed a latent bug: `t` is + progress along the tile, and I was walking items straight through the centre, so on + a corner they'd cut it and pop in at the wrong edge. Items now enter at the feeding + edge and sweep the arc. Verified numerically: items on both corners sit 0.089–0.248 + off the centreline where a straight path pins every sample to exactly 0.000. +2. **Heat** (`heat.ts`, new + `entities.ts`): dull-red emissive ramp on the body, rising + shimmer column, throttle, scram. Verified end-to-end at forced heat: + - 0.50 → body emissive `#bc280a` @ 1.35 (= `1 + 0.5²×1.4`), haze 0.5 + - 0.85 → idle clock advances **0.0053/frame vs 0.0083 full speed** = the 0.625× + throttle factor exactly; body emissive 2.01 + - 1.00 → **scram**: every emissive 0 (dead-dark), haze off, idle clock frozen at + 43.613, amber blinking + - back to 0.20 → **restart recovers**: body emissive 1.06 (= `1 + 0.2²×1.4`), latch + cleared, idle moving, haze back +3. **Buffer tank**: fill column driven by the tank's share of `bandwidth.stored` over + summed `bufferCap` (DATA shipped `buffer-tank: 240`). Verified oscillating with the + pool rather than sitting at its 0.001 floor. +4. **AnimationMixer**: clip 0 plays looped on rigged GLBs, and shares the throttle + factor — a hot machine's *animation* labours too, which fell out for free. Verified + with a hand-authored animated GLB: 9 frames → 9 distinct Y values, clip driving the + node. Rigged GLBs clone via `SkeletonUtils.clone`, not `Object3D.clone`, or every + instance would animate off the first one's bones. +5. **Exact cargo interpolation**: keys off `BeltItem.id` when present (`exactIds: true` + verified), order-matching retained as fallback until v3 makes it required. The + one-tick approximation is gone whenever the sim stamps ids. +6. **Multi-mesh belt GLBs**: merged with `mergeGeometries(..., useGroups)` so a fancy + belt asset can't half-render. Verified with a 2-mesh GLB: **2 material groups, 2 + materials, 1 instanced draw call**, both meshes visible. If attributes ever mismatch + it keeps the first mesh and `console.warn`s the count rather than silently dropping. + +**VERIFIED** — perf standing rule still met with everything above added: +`?devscene=stress` → 500 entities + 2,000 items at **3.6ms/frame avg (4.6× headroom)**, +5 instanced draw calls. Round 1 was 3.2ms, so topology + heat + mixers cost ~nothing. +Dev scaffold still DEV-gated and absent from `dist/` (verified 0 hits). + +**DECISIONS** +- **Scram is derived from `heat >= 1`**, not from the `scram` event — the `Renderer` + interface only receives snapshots, never events, so the edge isn't reachable from + here. Works as long as SIM holds heat at 1.0 while latched off. See request 2. +- GLB instances get **cloned materials**; heat/scram is per-entity state and the + template's materials are shared, so one hot machine would otherwise tint every copy. + Placeholders own geo+mats; GLB clones own only mats. Disposal follows that split. +- Heat tints the tagged body mesh on placeholders; a GLB has no body/accent split, so + there it tints everything. +- Shimmer is an additive billboard column, not screen-space refraction — post-processing + is LANE-SCREEN's territory and I'm not reaching into it. +- Kept the `camera.ts` non-left-button `stopPropagation` as belt-and-braces even though + main.ts now guards; it's two lines and costs nothing. + +**BLOCKED/BROKEN** — nothing blocking. Honest limitations: +- **Merge pieces don't route cargo per-inlet**: items on a merge belt still ride the + straight path, because `BeltItem` doesn't say which side it entered from. Reads fine + (a merge's main flow *is* straight) but it's not truthful for the side feed. +- **`reference.ts` doesn't exist** — SIM hadn't landed it when I built, so the DoD's + "reference factory looks ALIVE" was checked against my own devscene bend + showroom + row instead. Happy to re-verify against theirs next round. +- SIM doesn't emit heat/splitters/ids yet, so orders 2/4/5 are verified against the + devscene, not live sim data. All of it is contract-shaped and will light up as SIM + lands; nothing in my lane needs to change. +- Corner arc assumes a 1×1 belt. Fine per contract (`beltSpeed` belts are 1×1). + +**CONTRACT REQUEST** +1. **Settle `MachineDef.color`'s meaning** (see the top of this entry). My proposal: + keep DATA's values as-authored and re-comment the field as chassis/body colour, then + add `accent?: string` if art-directing the emissive is ever wanted. Right now the + contract comment says "accent", DATA authored bodies, and I've followed the data. +2. **`EntityState.scrammed?: boolean`** — I derive scram from `heat >= 1`. If SIM ever + cools a machine while it's still latched off, the visual un-scrams early and lies. + A flag makes it unambiguous and costs one bool. +3. Not urgent: `BeltItem.side?: Dir` (which edge cargo entered from) would let merge + pieces route their side feed honestly. Only worth it if merges get common. + +**PROPOSAL** +- MODELBEAST hot-swap now also covers **animated** and **multi-mesh** GLBs, so hero + assets can arrive rigged and kit-bashed with no code change. Asset keys are whatever + `MachineDef.asset` says — all 21 are live targets today. +- The showroom row in the devscene (`?devscene`) is a decent visual smoke test for any + new machine visual; it's clear of the belt path on purpose. + +**NEXT (round 3 candidates, not self-assigned)** +- Per-era chassis skinning (§8 era skins) once tech/eras land; splitter output lamp + driven by real round-robin state rather than a timer; bloom-loop belt tell; HF dust + piles; parity-mite wildlife; LOD/culling if a real megabase ever drags the frame.