[render] fix GLB body-scale regression: pop settles to baseScale, not 1
The placement scale-pop hardcoded the settled scale to 1, which stomped a normalised GLB's fit-to-footprint root scale (s~2-3.5) down to 1 on the first sync, collapsing the body to raw geometry size (a speck) while the beacon/jam child meshes kept their own local scale. Store baseScale per entity (1 for placeholders, the normalisation scale for GLBs) and make the pop a multiplier on it. Verified full-size bodies on fresh loads in ?showroom and ?uidemo. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
0d491f8a2f
commit
3fc043e504
@ -760,3 +760,45 @@ was added. Round 5's measured **4.4ms/frame** at full load stands as the referen
|
|||||||
`firewall.glb` when it lands; per-mite orientation toward its `target` belt; heat-shimmer
|
`firewall.glb` when it lands; per-mite orientation toward its `target` belt; heat-shimmer
|
||||||
*relief* inside a cooler rectangle; bloom-loop belt tell; save/load camera state;
|
*relief* inside a cooler rectangle; bloom-loop belt tell; save/load camera state;
|
||||||
LOD/culling if a real megabase ever drags the frame.
|
LOD/culling if a real megabase ever drags the frame.
|
||||||
|
|
||||||
|
### Round 6 ADDENDUM — 2026-07-20 — Opus 4.8 — GLB body-scale regression fix
|
||||||
|
|
||||||
|
**Orchestrator caught a deploy-blocking regression at 0d491f8: GLB machine BODIES collapsed
|
||||||
|
to specks (beacons + jam lights + labels still correct). Fixed in `entities.ts` — commit
|
||||||
|
below. My round-6 verification missed it because I checked on an HMR'd session and wrongly
|
||||||
|
attributed the small bodies to "asset normalisation"; on a FRESH load the specks are stark.**
|
||||||
|
|
||||||
|
**ROOT CAUSE — the placement-pop hardcoded the settled scale to 1, which is only true for
|
||||||
|
placeholders.** A normalised GLB roots at its fit-to-footprint scale `s` (measured live:
|
||||||
|
2.0–3.5 for the current wave). The pop's settle branch was
|
||||||
|
`else if (rec.obj.scale.x !== 1) rec.obj.scale.setScalar(1)` — so on the very first sync it
|
||||||
|
saw `2.08 !== 1` and stomped the root to 1, shrinking the body to raw geometry size (~0.63
|
||||||
|
vs the correct 1.84 for a 2×2). The beacon and jam meshes carry their own local scale on
|
||||||
|
child nodes, so they were untouched — which is exactly the "bodies vanished, beacons fine"
|
||||||
|
signature the orchestrator reported.
|
||||||
|
|
||||||
|
**Why it surfaced now, honestly:** the pop code is UNCHANGED by me and the bug is latent for
|
||||||
|
ANY non-placeholder settled scale ≠ 1. It bit this round because the live GLB wave normalises
|
||||||
|
to s≈2–3.5 (verified: fresh `entry.create()` returns root scale 2.08 + a correct 1.84-wide
|
||||||
|
body; the live post-pop root was scale 1 + a 0.63 speck). The pop only ever writes
|
||||||
|
`rec.obj.scale`, so it was provably the resetter. It is not the beacon — a fresh create is
|
||||||
|
correct; the pop destroys it one frame later.
|
||||||
|
|
||||||
|
**FIX:** the pop is now a MULTIPLIER on a stored `rec.baseScale` (captured from `obj.scale.x`
|
||||||
|
at build = 1 for placeholders, the normalisation scale for GLBs), never an absolute. Settle
|
||||||
|
compares against `baseScale`, not 1. Placeholders: identical behaviour (baseScale 1). GLBs:
|
||||||
|
body stays full-size, and the placement scale-pop still plays (relative overshoot).
|
||||||
|
|
||||||
|
**VERIFIED on FRESH loads (hard navigate, no HMR), before/after described:**
|
||||||
|
- `?showroom`: BEFORE = every GLB a speck on a full plinth; AFTER = all 27 render as full
|
||||||
|
sculpted bodies filling their footprints, lifted + beaconed. Scene-graph confirms live
|
||||||
|
roots now carry scale 2.0–3.5 and body world sizes match footprints (1.84 for 2×2, 2.76
|
||||||
|
for 3×3), where they read 1.0 / 0.63 before the fix.
|
||||||
|
- `?uidemo` (reference factory, default game zoom): BEFORE = specks; AFTER = full-size
|
||||||
|
machines throughout, belts + cargo flowing. No console errors. `npm run check` clean,
|
||||||
|
`npm test` 481/481.
|
||||||
|
|
||||||
|
**LESSON (for me and the next lane):** verify visual regressions on a cold load, not an
|
||||||
|
HMR'd session — an HMR'd renderer can hold meshes normalised in a prior module state and
|
||||||
|
mask a fresh-boot break. And a hardcoded `scale = 1` is a placeholder assumption that any
|
||||||
|
scaled asset (GLB, future LOD) will trip; base-relative is the correct shape.
|
||||||
|
|||||||
@ -60,6 +60,11 @@ interface Rec {
|
|||||||
wasScrammed: boolean;
|
wasScrammed: boolean;
|
||||||
/** Seconds into the placement scale-pop; >= POP_TIME means settled. */
|
/** Seconds into the placement scale-pop; >= POP_TIME means settled. */
|
||||||
pop: number;
|
pop: number;
|
||||||
|
/** The object's settled scale. 1 for placeholders; the GLB fit-to-footprint scale for a
|
||||||
|
* hot-swapped asset. The pop multiplies THIS, never resets to 1 — a normalised GLB roots
|
||||||
|
* at s≠1 and hardcoding 1 collapsed the body to a speck (the beacon/jam sit on their own
|
||||||
|
* local scale so they were unaffected, which is why only bodies vanished). */
|
||||||
|
baseScale: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const JAM_GEO = new THREE.SphereGeometry(0.09, 8, 6);
|
const JAM_GEO = new THREE.SphereGeometry(0.09, 8, 6);
|
||||||
@ -159,13 +164,15 @@ export class EntityLayer {
|
|||||||
if (alarm) anyAlarm = true;
|
if (alarm) anyAlarm = true;
|
||||||
|
|
||||||
// Placement thunk: overshoot then settle. Reads as weight landing on the floor.
|
// Placement thunk: overshoot then settle. Reads as weight landing on the floor.
|
||||||
|
// The pop is a MULTIPLIER on the settled scale, never an absolute — a normalised GLB
|
||||||
|
// settles at baseScale (s≠1), so resetting to 1 would collapse its body to a speck.
|
||||||
if (rec.pop < POP_TIME) {
|
if (rec.pop < POP_TIME) {
|
||||||
rec.pop += dt;
|
rec.pop += dt;
|
||||||
const k = Math.min(1, rec.pop / POP_TIME);
|
const k = Math.min(1, rec.pop / POP_TIME);
|
||||||
const s = k < 1 ? 1 + Math.sin(k * Math.PI) * 0.22 : 1;
|
const mult = k < 1 ? 1 + Math.sin(k * Math.PI) * 0.22 : 1;
|
||||||
rec.obj.scale.setScalar(k < 0.12 ? k / 0.12 : s);
|
rec.obj.scale.setScalar(rec.baseScale * (k < 0.12 ? k / 0.12 : mult));
|
||||||
} else if (rec.obj.scale.x !== 1) {
|
} else if (rec.obj.scale.x !== rec.baseScale) {
|
||||||
rec.obj.scale.setScalar(1);
|
rec.obj.scale.setScalar(rec.baseScale);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Harassed by a swarm: the machine stutters. Wrongness of motion is the tell (§8).
|
// Harassed by a swarm: the machine stutters. Wrongness of motion is the tell (§8).
|
||||||
@ -292,6 +299,7 @@ export class EntityLayer {
|
|||||||
all,
|
all,
|
||||||
wasScrammed: false,
|
wasScrammed: false,
|
||||||
pop: POP_TIME, // settled by default; a real placement rewinds it to 0
|
pop: POP_TIME, // settled by default; a real placement rewinds it to 0
|
||||||
|
baseScale: obj.scale.x, // 1 for placeholders; the GLB normalisation scale otherwise
|
||||||
};
|
};
|
||||||
this.live.set(id, rec);
|
this.live.set(id, rec);
|
||||||
return rec;
|
return rec;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user