4.8 KiB
LANE D — CITIZENS (populate the town)
Prompt for Opus 4.8. You are working in
/Users/jing/Documents/PROCITY. Readdocs/CITY_SPEC.mdanddocs/RESEARCH.mdfirst, then the source you are porting:90sDJsim/web/world/index.htmllines ~258–393 (loadRig,spawnRig,_canon,_rotOnly, head-bone height normalization,upgradeStreetPeople) — this stack is proven, port it faithfully. You ownweb/js/citizens/*,web/citizens_test.html, andweb/models/*. Do not touch other lanes' files. Plain ES modules, three.js fromweb/vendor/, randomness viacore/prng.js. Commit in small, described steps onmain(GLBs are committed — keep the set curated, not hoarded).
Mission
Streets that feel lived-in and shops with keepers, at city scale, on budget. Rigged characters near, cheap impostors far, deterministic per-citizen identity (same seed → same person walks the same beat), all built on the house fleet rule: shared base meshes + one canonical clip bank, never per-character rigs.
Assets (copy, never edit locally)
- Copy the ped fleet from
90sDJsim/web/world/models/peds/(19 rigged GLBs + clip-onlywalk.glb/idle.glb) intoweb/models/peds/. Byte-identical copies — the canonical source stays upstream (house law; seeasset-pipelineconventions). - Canonical hero rigs if needed:
~/Documents/character_kit/(female + male). Clip bank:~/Documents/mixamo-fetch/out/(34 clips — sit, lean, look-around, phone etc. may exist; check before wanting new ones; never re-download an existing clip). New clips need John to run the mixamo-fetch flow — file wishes indocs/LANES/LANE_D_NOTES.md, don't block. - Licensing: 🟢 web-ok only. No ActorCore.
Architecture
web/js/citizens/rigs.js— the ported stack: promise-cachedloadRig;spawnRig()with SkeletonUtils.clone,mixamorig\d+canonicalization (one walk clip drives all 19 peds),_rotOnlyposition-track stripping, head-bone height normalization (seeded 1.55–1.95m), feet planted by min bone Y, random clip start offsets. Primitive-figure placeholders that hot-swap when GLBs arrive (upgradeStreetPeoplepattern) — the town is populated from frame one.web/js/citizens/sim.js— the citizen simulation (Vuntra's layered idea, hamlet-scale):- Deterministic roster per chunk:
rng(citySeed,'citizen',chunkKey+i)→ identity (ped index, height, walk speed, home/goal habits). Density by district (main street busy at midday, residential sparse, market square clumps). - Near tier (<25m): full rig + mixer, walking footpath lanes (offset polylines along street edges from the CityPlan graph), turning at nodes with seeded choice, idle/loiter stops at shop windows and benches (bench-sit if a sit clip exists, else stand-loiter).
- Mid tier (25–70m): no mixers ticking — instanced/billboard impostors. Generate impostor sprites at runtime: render each ped GLB once to a small offscreen RT from 4 yaw angles → one sprite atlas → InstancedMesh quads picking the nearest angle. (This keeps impostors automatically in sync with the fleet; cache the atlas in-session.)
- Far: culled entirely. Promotion/demotion by distance with hysteresis so nobody pops while you watch (swap only when off-screen or beyond a fade).
- Time-of-day modulation from the shell's day segment (event or global): morning trickle, lunch rush, night = near-empty streets + the odd wanderer.
- Deterministic roster per chunk:
web/js/citizens/keepers.js— shopkeepers: one keeper per open shop, standing at the counter slot (Lane C exposes counter position in itsplaces— coordinate via the event payload; until Lane C lands, test with a mock). Idle clip + a "greet" head-turn toward the player if a clip allows. Keepers are spawned when an interior builds, disposed with it.- Budgets: ≤ 24 rigged actives, ≤ 4 mixers updated per frame beyond the nearest 8 (stagger updates), impostor instancing = 1 draw call per atlas. All mixers pause when tab hidden.
Test page (web/citizens_test.html)
Standalone: a fixture street loop (hard-code a small street graph — do NOT import Lane A/B files; keep the fixture local), spawn slider 0–200 citizens, tier-visualization toggle (colour by tier), live HUD (rigged count, mixer ms, draw calls, fps), time-of-day slider, determinism check button (respawn roster, assert identical identities).
Acceptance
- 200-citizen fixture scene holds 60fps on an M-series MacBook; mixer update <2ms/frame.
- No T-pose ever visible (placeholder → rig swap is seamless); no giants/ants (height normalization verified across all 19 peds); one shared walk clip animates every ped.
- Same seed → same crowd, twice (assert).
- Screenshots of near/mid tiers + a busy main-street shot into
docs/shots/laneD/. docs/LANES/LANE_D_NOTES.md: measured budgets + clip wishlist for the mixamo-fetch run.