[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 <noreply@anthropic.com>
This commit is contained in:
type-two 2026-07-17 17:36:09 +10:00
parent 56c29eaba6
commit 30deed10dc

View File

@ -150,3 +150,111 @@ SHIPPED: / DECISIONS: / BLOCKED/BROKEN: / CONTRACT REQUEST: / PROPOSAL: / NEXT:
- AnimationMixer for rigged GLBs; per-era skinning hooks; splitter/lane visuals; heat - AnimationMixer for rigged GLBs; per-era skinning hooks; splitter/lane visuals; heat
shimmer + throttle read-out on the software decoder; belt corner/junction pieces shimmer + throttle read-out on the software decoder; belt corner/junction pieces
(currently every belt is a straight plate regardless of neighbours). (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.0890.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.