Lane D round-7: validate ped-merge → GO for roster flip + full-density defaults + escape hatch
Lane E's R7 merge (447188a) made each ped 1 mesh/1 material (~1 draw/near-rig, fleet 116->24 at
the cap) — the gate my R6 memo named. Validated + handed F the GO.
Merge validated: bind+animate/no-T-pose, skinning/skeleton preserved, silhouettes+atlas materials
crisp (hi-vis/suit/kid), impostor bake clean, identity variety + determinism intact. GPU memory
dropped (geom 205->160, tex 141->90).
Flip numbers (full density, perChunk 16, in-shell): worst street view 241 draws <=300 (was 356
unmerged), 65,899 tris <=200k, 17 near-rigs; 30-chunk soak (726 builds/disposes) leak-free, 0 GPU
delta, heap stable. => GO.
D2: enableStream default perChunk 8->16 (full density, the merge affords it); near-cap stays 24.
D3: test page + harness default to stream now; ?roster=v1 escape hatch restores the fixed roster
(verified streamMode false + determinism). No-flag boot -> streamMode true + determinism.
F's web/index.html flip wiring (invert to default-on + ?roster=v1 escape) documented in
LANE_D_NOTES; in-shell no-flag-URL boot completes when F lands F1.
Only web/js/citizens/sim.js + web/citizens_test.html changed (+ NOTES/progress).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
0c12dacb6d
commit
43edabfe56
@ -1,5 +1,29 @@
|
||||
# PROCITY-D — progress (Lane D · Citizens)
|
||||
|
||||
## Round 7 (the flip) — MERGE VALIDATED, GO given to F, defaults + escape hatch ready. qa GREEN.
|
||||
|
||||
Validated Lane E's ped-merge (the gate my R6 memo named) and handed F the **GO** for the roster flip.
|
||||
Full go/no-go numbers + F's two-line wiring at the top of `LANE_D_NOTES.md`.
|
||||
|
||||
- **Merge validated (D1)**: all 19 peds now **1 mesh / 1 material** (was 8/2) → ~1 draw/near-rig
|
||||
(fleet 116→24 at the cap); bind+animate/no-T-pose, silhouettes+atlas materials crisp (hi-vis/suit/
|
||||
kid), impostor bake clean, identity variety + determinism preserved. GPU memory dropped too.
|
||||
- **The flip numbers (full density, perChunk 16)**: worst street view **241 draws ≤300** (was 356
|
||||
unmerged), **65,899 tris ≤200k**, 17 near-rigs; 30-chunk soak (726 builds/disposes) **leak-free, 0
|
||||
GPU delta**, heap stable. → **GO.**
|
||||
- **Full-density defaults (D2)**: `enableStream` default flipped 8→**16** (the merge affords it);
|
||||
near-cap stays 24 (now 24 draws not 168).
|
||||
- **Post-flip + escape hatch (D3)**: test page + shell now default to stream; **`?roster=v1` restores
|
||||
the fixed roster** (verified: streamMode false, determinism ✓). No-flag boot → streamMode true,
|
||||
determinism ✓. F's `web/index.html` flip wiring (invert to default-on + `?roster=v1` escape)
|
||||
documented; the in-shell no-flag-URL boot completes when F lands F1 (roster proven running in-shell
|
||||
at the shipping default via the in-shell soak above).
|
||||
|
||||
Only `web/js/citizens/sim.js` (perChunk default) + `web/citizens_test.html` (default-on + escape
|
||||
hatch) changed. Committed atomically.
|
||||
|
||||
---
|
||||
|
||||
## Round 6 (harden streamed roster toward default-on) — DONE. qa GREEN. Flag still default-off.
|
||||
|
||||
Hardened `?roster=stream` and wrote the **default-on readiness memo** (top of `LANE_D_NOTES.md`) so
|
||||
|
||||
@ -6,6 +6,58 @@ _rotOnly/head-bone normalize/upgradeStreetPeople). Measurements on the M3 Ultra
|
||||
|
||||
---
|
||||
|
||||
## ROUND 7 — MERGE VALIDATED · **GO for the roster flip** (→ Lane F: flip on this) · escape hatch ready
|
||||
|
||||
**Verdict: GO. Lane E's R7 ped-merge closed the one gap the R6 memo named. Full-density streamed
|
||||
roster (perChunk 16) is now under budget in a real town. Flip it default-on.**
|
||||
`qa.sh --strict` GREEN. All numbers in-shell (real town "Boolarra Heads", seed 20261990).
|
||||
|
||||
### Merge validation (E1 — `pipeline/merge_ped.py`, `447188a`) ✅
|
||||
- **Every one of the 19 peds is now 1 mesh / 1 material** (was 8 meshes / 2 materials) → **~1 draw per
|
||||
near-rig**; fleet **116→24 draws at the 24-cap** (E's own count: 92→19). Bonus: GPU memory dropped
|
||||
too (geometries 205→160, textures 141→90 in-shell).
|
||||
- Rigs **bind + animate** (walk/idle) — mixer 0.2–0.3 ms; **no T-pose**. Skinning/skeleton preserved
|
||||
(65 joints), clips retarget unchanged.
|
||||
- **Silhouettes + atlas materials intact** — hi-vis worker (vest/helmet/boots), suit, business, comical
|
||||
kid all read crisp, no atlas seams/melting; **identity variety preserved** (`pickRig` unchanged).
|
||||
- **Impostor bake clean** (19 subjects, 16×5 atlas); **determinism holds** (507 chunk-keyed identities
|
||||
re-derive from seed).
|
||||
|
||||
### The flip numbers (full density, `perChunk 16` — no reduced-density concession)
|
||||
| metric | pre-merge (R6) | **post-merge (R7)** | budget |
|
||||
|---|---|---|---|
|
||||
| worst continuous-walk street view **draws** | 356 ✗ | **241** ✅ | ≤300 |
|
||||
| worst view **tris** | ~66k | **65,899** ✅ | ≤200k |
|
||||
| worst-view near-rigs | (throttled to 8) | **17** (full) | cap 24 |
|
||||
| draws / near-rig | ~7 | **~1** | — |
|
||||
| 30-chunk soak (3 walks = 726 builds/disposes) | leak-free | **leak-free, 0 GPU delta** | baseline |
|
||||
| heap over soak | 50→77→54 | 78→63 (GC, stable) | stable |
|
||||
|
||||
### Full-density shipping defaults (set this round)
|
||||
`enableStream` default is now **`perChunk 16`, `radius 2`** (was 8). Near-cap stays 24 (now 24 draws,
|
||||
not 168). `NIGHT_LIVELY_FLOOR 0.5` for the open-late block (R6). No other knobs change.
|
||||
|
||||
### → Lane F: the flip wiring in `web/index.html` (F owns the shell — decision #1, flip on my word)
|
||||
Invert the R5/R6 flag: **stream is the default; `?roster=v1` is the escape hatch.**
|
||||
```js
|
||||
const rosterV1 = params.get('roster') === 'v1'; // escape hatch → the old fixed roster
|
||||
const citizens = new CitizenSim({ renderer, scene, camera, citySeed: plan.citySeed,
|
||||
graph: plan.streets, fleet,
|
||||
chunkStream: rosterV1 ? null : { radius: 2 } }); // default-on
|
||||
```
|
||||
Hours-aware (optional, recommended): after construction, `citizens.setNightLivelyChunks(keys)` where
|
||||
`keys` = the openLate shop's lot→chunk + 1-ring (`sim.chunkKeyAt(x,z)`; openLate = `hours[1] >= 22`).
|
||||
Everything else (`setTimeOfDay`/`setExposure`/`update`/`setPaused`) is unchanged. The
|
||||
hook-driven window (`onChunkBuilt`/`onChunkDisposed` → `sim.onChunkBuilt/onChunkDisposed`) is still the
|
||||
preferred precise option; poll works with zero wiring.
|
||||
|
||||
**Verified in my harness (test page + runtime-enabled shell):** no-flag boot → `streamMode:true`,
|
||||
perChunk 16, determinism ✓; `?roster=v1` → `streamMode:false`, fixed roster, determinism ✓. The
|
||||
in-shell no-flag-URL boot completes once F lands the two-line flip above — the roster itself is proven
|
||||
to run correctly in the shell at the shipping default (the soak + flip numbers above are in-shell).
|
||||
|
||||
---
|
||||
|
||||
## ROUND 6 — streamed-roster "default-on readiness" memo (→ Fable + Lane F, for the R7 flip call)
|
||||
|
||||
**Verdict: functionally ready (deterministic, leak-free, hours-aware, composes) — but full-density
|
||||
|
||||
@ -83,7 +83,9 @@ import { KeeperManager } from './js/citizens/keepers.js';
|
||||
const qs = new URLSearchParams(location.search);
|
||||
const SEED = (parseInt(qs.get('seed'), 10) || 20261990) >>> 0;
|
||||
const NOASSETS = qs.get('noassets') === '1';
|
||||
const STREAM = qs.get('roster') === 'stream'; // v2: chunk-streamed roster (default off = v1)
|
||||
// R7 flip: the chunk-streamed roster is now the DEFAULT. `?roster=v1` is the escape hatch back to the
|
||||
// fixed v1 roster; `?roster=stream` is still accepted (explicit). Mirrors the shell wiring F lands in F1.
|
||||
const STREAM = qs.get('roster') !== 'v1';
|
||||
|
||||
// ---------- renderer / scene ----------
|
||||
const app = document.getElementById('app');
|
||||
|
||||
@ -147,11 +147,10 @@ export class CitizenSim {
|
||||
// downstream — LOD tiers, the 24 near-cap, mixer stagger, rig pool, impostor layer — is already
|
||||
// per-citizen and chunk-agnostic and runs GLOBAL across live chunks, unchanged.
|
||||
|
||||
// perChunk default 8 keeps the worst continuous-walk street view ≤300 draws in a real town TODAY.
|
||||
// Ceiling cause: each decimated ped is 8 sub-meshes but only 2 materials → ~7 draws/near-rig, so a
|
||||
// dense street of near-rigs blows the 300 budget. The clean unlock (LANE_D_NOTES R6 memo) is merging
|
||||
// each ped's sub-meshes by material (8→2) — then perChunk can go back to ~16 with headroom.
|
||||
enableStream({ radius = 2, perChunk = 8 } = {}) {
|
||||
// perChunk default 16 = full density. Affordable since Lane E's R7 merge made each ped ONE draw
|
||||
// (was 8 sub-meshes / ~7 draws): the worst continuous-walk street view now sits well under the 300
|
||||
// budget even with a dense street of near-rigs. This is the shipping default for the R7 roster flip.
|
||||
enableStream({ radius = 2, perChunk = 16 } = {}) {
|
||||
if (this.streamMode) return;
|
||||
this.roster.forEach(c => this._releaseActor(c)); this.roster.length = 0; // tear down the v1 roster
|
||||
if (!this.chunkEdges) this._buildChunkIndex();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user