PROCITY/docs/LANES/LANE_D_CITIZENS.md
2026-07-14 10:46:40 +10:00

4.8 KiB
Raw Permalink Blame History

LANE D — CITIZENS (populate the town)

Prompt for Opus 4.8. You are working in /Users/jing/Documents/PROCITY. Read docs/CITY_SPEC.md and docs/RESEARCH.md first, then the source you are porting: 90sDJsim/web/world/index.html lines ~258393 (loadRig, spawnRig, _canon, _rotOnly, head-bone height normalization, upgradeStreetPeople) — this stack is proven, port it faithfully. You own web/js/citizens/*, web/citizens_test.html, and web/models/*. Do not touch other lanes' files. Plain ES modules, three.js from web/vendor/, randomness via core/prng.js. Commit in small, described steps on main (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-only walk.glb / idle.glb) into web/models/peds/. Byte-identical copies — the canonical source stays upstream (house law; see asset-pipeline conventions).
  • 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 in docs/LANES/LANE_D_NOTES.md, don't block.
  • Licensing: 🟢 web-ok only. No ActorCore.

Architecture

  • web/js/citizens/rigs.js — the ported stack: promise-cached loadRig; spawnRig() with SkeletonUtils.clone, mixamorig\d+ canonicalization (one walk clip drives all 19 peds), _rotOnly position-track stripping, head-bone height normalization (seeded 1.551.95m), feet planted by min bone Y, random clip start offsets. Primitive-figure placeholders that hot-swap when GLBs arrive (upgradeStreetPeople pattern) — 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 (2570m): 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.
  • web/js/citizens/keepers.js — shopkeepers: one keeper per open shop, standing at the counter slot (Lane C exposes counter position in its places — 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 0200 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.