PROCITY/pipeline/AUDIT.md
m3ultra 447188a516 Lane E round 7 (E1): merge ped rigs to ONE draw each (gates roster flip)
Each of the 19 ped rigs carried 1-9 primitives / 1-3 materials (fleet 92 draws,
~116 at the 24-rig near cap). merge_ped.py (Blender) merges each to 1 primitive +
1 atlased material: join meshes -> pack material UVs into a non-overlapping atlas UV
-> EMIT-bake base-colours into one 1024 atlas -> single material. Skinning + skeleton
preserved (65 joints, walk/idle untouched, clips retarget unchanged). Fleet 92->19
draws (1/ped); near-cap 116->24 (79% cut). Verified in vendored GLTFLoader: all 19
load as SkinnedMesh + bind walk; man_suit poses byte-identical to original. Hands to
Lane D (D1) for full validation + full-density numbers, then F flips the roster.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 00:33:03 +10:00

22 KiB
Raw Blame History

PROCITY Asset Audit — Lane E, Step 1

Generated 2026-07-14 from a live inspection of johnking@100.91.239.7:~/Documents/3D=models/ (the sorted house library) + the 3GOD depot GET /api/list. Tri counts / bounding boxes from pipeline/glb_stat.py (dependency-free GLB parser); orientation & final scale verified by rendering every candidate through Blender and looking at the thumbnails (never "should look right").

TL;DR

  • The library is a raw dump: almost nothing obeys the house GLB law. Scale is arbitrary (only record_crate_plastic_01 was native-metres), up-axis varies, tri counts span 12 → 960k, textures reach 4096px, and several assets carry branded/trademarked designs (quarantined).
  • pipeline/normalize.py fixes scale/orientation/tris/textures uniformly. 16 assets normalized clean, verified upright & metre-scaled, WebP ≤1024, ≤5k tris, +Y up, base origin — ready to publish.
  • Gaps with no usable source (→ MESHGOD_BATCH.md or in-engine primitives): glass display case, fridge, till/register, spinner rack, magazine rack, market stall, escalator, VHS shelving.
  • Depot already holds a few reusable house props (street-bench, council-bin, milk-crate-stack) and 138 assets total (mostly TRELLIS *-views-3d-* / image-3d-* MeshGod outputs).
  • Skin caveat: 5 of the 25 facades carry baked-in sign text (djsim-milkbar "MILK BAR", djsim-video, djsim-pub, market, grimy) — they break the blank-signboard convention, so the manifest tags them signboard:"baked" and Lane B must not overlay a name on them. The other 20 are genuinely blank.

Toolchain reality (differs from the lane brief)

thing brief said actual
Blender on ultra "runs on ultra against Blender" present but not on PATH/Applications/Blender.app/Contents/MacOS/Blender (v5.0.1). Renders 4 angles in ~0.9s.
coreutils / timeout none confirmed — used ssh ConnectTimeout, no timeout/setsid
python on ultra 3.9 3.9.6 — glb_stat.py/normalize.py written to that floor
OpenRouter key thriftgod/.env absent; house convention ~/Documents/dealgod/lore/keys.env (auto-read by gen scripts)
depot upload cred GOD3_PW env, not set here → publish is gated on John (see publish.py)

Normalized set (16 — ready; manifest.jsonfittings / furniture)

house law enforced on every one: metres · +Y up · origin at base (minY=0, centred X/Z — verified node-aware) · ≤5k tris · WebP ≤1024 · no Draco. Facing is not auto-corrected — symmetric props are orientation-agnostic; the few directional ones (counter, food_cart) keep their native front and Lane B/C rotate when placing (per-asset yaw is available in batch.json).

PROCITY id source GLB src tris → out footprint (m) height (m) notes
fit record_crate shop-fittings/record_crate_plastic_01 18320→5000 0.46 × 0.63 0.41 ★ native-metres, textured red milk crate; serves record bin too
fit wire_shelf shop-fittings/shelf_metal_wire_01 330→330 1.17 × 0.44 1.80 ★ clean upright wire shelving
fit clothes_rack shop-fittings/clothes_rack_01 3152→3152 1.34 × 0.43 1.50 ★ rolling clothes rack (opshop)
fit bookshelf shop-fittings/bookshelves 45128→5000 0.20 × 1.02 2.00 tall bookshelf w/ books (book/video/opshop)
fit cube_shelf_wide shop-fittings/shelf_cube_bookshelf_01 17734→4999 0.75 × 1.24 1.10 stocked cube shelving (toy/book)
fit counter shop-fittings/balcao 276→276 1.06 × 4.02 1.10 long bar counter — Lane C may want to scale to lot
fit coffee_table shop-fittings/coffeetable 495→495 0.67 × 1.49 0.45 low display/side table
fit work_table shop-fittings/computertable 1556→1556 0.44 × 1.04 0.75 work/trestle table w/ lower shelf
fit box_crate shop-fittings/warehouse_box 78→78 1.35 × 1.32 0.60 open bulk bin / bric-a-brac box
street bench street-furniture/bench 1942→1942 0.44 × 1.21 0.85 slatted seat
street bench_modern street-furniture/eb_hipster_bench 640→640 1.68 × 0.47 0.45 low modern bench
street bench_wood street-furniture/woodbench 11728→3999 1.34 × 0.59 0.85 timber park bench
street longbench street-furniture/longbench 1172→1172 5.86 × 1.08 0.85 long feature bench (arcade/market)
street park_bench street-furniture/park_bench_01 9884→5000 1.92 × 1.62 0.85 classic slatted park bench
street streetlight street-furniture/streetlight 6592→4999 1.64 × 1.82 5.00 ornate lamppost
street food_cart street-furniture/food_cart_bakso_01 30316→5000 3.00 × 2.07 2.00 textured food cart (~3MB — heavy, 28 tex; optimize later)

Rejected during normalize (needs-manual-fix — NOT in manifest)

source issue (verified by render) disposition
shop-fittings/cube_bookcase (cube_shelf) node-scale distortion → 6.3 × 8.4 m footprint dropped; cube_shelf_wide covers the need
shop-fittings/garage_shelves_metal_v2 imports lying flat (green pallet); baked node transform, up-fix didn't help needs manual re-origin in Blender; wire_shelf covers shelving
shop-fittings/table, saloon_table, table_02, desk_01, kitchen_table degenerate/flat or node-distorted (table → 55×55 m; desk → 17×17 m) rejected; work_table/coffee_table cover tables
shop-fittings/radiogram_furniture_v2_l1 Blender "Please select a file" on import (bad GLB) needs re-export
street-furniture/market_stall_01_CHECK import error (_CHECK = sorter-flagged) → MeshGod/primitive (see MESHGOD_BATCH.md)

QUARANTINE — licence unclear / branded (🔴 do NOT publish or ship)

The house rule is 🟢 web-ok only; anything branded or of unknown provenance is quarantined until cleared. These are excluded from the manifest:

asset reason
furniture/armchair-vitra-eames, ines-chair-calligaris protected designer marques (Vitra/Eames®, Calligaris®)
shop-fittings/ikea-cupboard IKEA® product design (+259k tris)
props-scenes/mk2_1210_turntable_-_old Technics SL-1210® industrial design (trademark)
props-scenes/tuborg_fest_scene Tuborg® brand scene
furniture/uploads_files_*, props-scenes/uploads_files_* (Couch William, Harland Office Chair, nv toufa, Backpack) CGTrader/free3d bulk dumps, licence not recorded
furniture/sofa-c, hug-armchair, dragonchest, kitty-bed-01, bean_bag_chair provenance unrecorded — verify before use

Blanket caveat (applies to the 16-asset publish set): the generic fittings we normalized (benches, shelves, counter, crate…) carry no explicit licence/provenance record — I cross- checked all 16 source filenames and none is a recognizable trademark, and they were pre-sorted into the house library by John for exactly this use, so they're treated as usable per the lane brief. But "unclear ⇒ quarantine" is the house rule: before publish.py pushes to the shared CDN, record a one-line source/licence per asset, and for any CC-BY source carry attribution. If provenance genuinely can't be established for one, pull it from the publish set. This is a reason the publish step is deliberately gated on John, not automated.

Provenance — round-3 hero props (for the depot publish sign-off)

Per the round-3 instruction ("record the per-asset provenance list in AUDIT.md first, then hand John the two-liner"). The hero props have the cleanest provenance in the whole set — they are 100% generated on-device, from no third-party source:

asset (procity_fit/street_<id>_01.glb) origin licence zone
5 generated — listening_booth, drinks_fridge, arcade_cabinet, milkshake_mixer (fit) · novelty_record (street) fully synthetic, local: concept image = MODELBEAST flux_local (FLUX.2-klein-4B, Apache-2.0 model) → mesh = trellis_mac (TRELLIS.2, an original reconstruction of that concept image). No scanned/third-party geometry, no copyrighted source asset. 🟢 original work (CC0-equivalent)
cash_register (vintage-cash-register.glb) reuse of John's own thriftgod_hero asset, already live on 3GOD (round-3 reuse check) — not regenerated. 🟢 same as existing depot set
magazine_rack not shipped — TRELLIS wire rack floored ~112k tris (thin-wire gotcha) → in-engine primitive. n/a
glass_case, bus_shelter not shipped — TRELLIS failed on the transparent glass → fal.ai fallback list (John's go — Hunyuan v2 ~1417¢/gen: ~$0.280.34 for the pair, ~$1 worst case w/ retries) or primitive. n/a
  • Prompts were generic period objects with explicit no text, no brands, no people. Every kept prop was eyeballed for accidental baked branding (contact sheet docs/shots/laneE/props_gen_local.png). The only textual marks are the drinks-fridge's weathered faux-stickers — verified at full concept resolution to be garbled, illegible pseudo-lettering (no real trademark; e.g. a faux barcode + nonsense glyphs), which reads as period flavour and blurs out at mesh-texture resolution. 🟢 cleared.
  • Tri/size note: TRELLIS meshes are dense multi-shell scans; normalize.py's COLLAPSE decimate floors well above the ≤5k library budget (2577k tris, ~0.33.5 MB/prop; neither COLLAPSE nor meshopt can reduce shell-soup without a voxel-remesh that destroys the baked UVs). Accepted for focal, optional, LOD-able props (texture-intact) — flagged for a later texture-bake-to-lowpoly / gltfpack pass.
  • These GLBs stage locally (web/assets/models/, git-ignored, served by ?localdepot=1) and await John's depot publish; they are not committed binaries (same policy as the library _normalized set).

Coverage vs the registry fittings needs

shop type fittings need covered by gap →
record bins, crates, counter, listening corner record_crate, counter, wire_shelf listening booth → MeshGod
opshop clothes racks, bric-a-brac shelving, book wall clothes_rack, wire_shelf, bookshelf, box_crate
toy cube shelves, display tables, glass case cube_shelf_wide, coffee_table glass case → MeshGod
book bookshelves, spinner racks, armchair bookshelf, cube_shelf_wide spinner rack, armchair → MeshGod/primitive
video aisle shelves, returns slot, poster wall bookshelf, wire_shelf returns slot, poster wall → primitive
pawn glass cabinets, wall hooks, barred window counter glass cabinet → MeshGod; pegboard = tex-pegboard skin
milkbar counter, fridge, magazine rack counter fridge, magazine rack → MeshGod/primitive
dept mixed sections, escalator prop (all above) escalator → primitive
stall trestle tables, crates work_table, box_crate, record_crate market stall → MeshGod/primitive
street bench/streetlight/bin/tree bench×4, longbench, park_bench, streetlight, food_cart bin (council-bin on depot), tree → billboard (Lane B)

Gaps with no clean source are shortlisted in MESHGOD_BATCH.md (budget-gated) — everything has an in-engine primitive fallback, so none of these block the game.

Depot cross-reference (GET /api/list, 138 assets)

  • 90 named + 48 TRELLIS-hash. Reusable house props already live (verify visually + licence before wiring): street-bench.glb, council-bin.glb, milk-crate-stack.glb, cashbot.glb. ⚠️ akai-mpc-live-iii-* is a trademarked product design — same quarantine class as Technics/ IKEA; do not treat as clean web-ok reuse without a licence check.
  • No procity_* assets yet — this lane is the first to publish that namespace.
  • Our 16 normalized GLBs are staged in pipeline/_normalized/ and upload via pipeline/publish.py once GOD3_PW is provided (idempotent, reversible).

Full library inventory (dispositions)

shop-fittings/ (33): normalized 9 · rejected 8 (7 node-distorted/degenerate + radiogram bad-GLB) · quarantine 1 (ikea) · unused/dup 15 (heavier or duplicate tables/shelves — shelf_storage_rack_01 alone is 960k tris). street-furniture/ (9): normalized 7 · error 1 (market_stall) · building_shell_01 reserved for Lane B (38k-tri building shell — not a fitting). furniture/ (17): mostly domestic (beds/sofas/chairs) — out of scope for shop fittings; 6 quarantined. props-scenes/ (10): dj_roomv01 + turntable = record-shop dressing (turntable quarantined: Technics); rest branded/scene dumps.

Raw stats for every parsable .glb (59 of the ~69 library entries; the rest are .fbx/.obj or subdir sources not counted): pipeline/_audit_stats.json. glb_stat.py reports world-space dims (node-transform-aware) and WebP/PNG/JPEG texture sizes. Normalizer output: pipeline/_normalize_results.json.

Round 4 — ped-rig decimation (gate-3 blocker)

The 19 street ped rigs in web/models/peds/ were ~31k56k tris each (924k total) — the cause of gate 3 failing (~279k tris at the busiest intersection vs ≤200k). Decimated to ≤3k tris each (integrator decision #1) via gltf-transform weldsimplify (meshoptimizer):

gltf-transform weld  in.glb  _w.glb
gltf-transform simplify _w.glb out.glb --ratio 0.05 --error 0.12

meshoptimizer works cleanly here (proper manifold skinned character meshes, NOT the TRELLIS shell-soup of the R3 hero props). Result: 924,153 → 46,551 tris (48.6k → 2.45k avg; range 15642805, all ≤3k), 36M → 5M on disk. Skinning fully preservedJOINTS_0/WEIGHTS_0 intact, skeleton unchanged (nodes + 65 joints identical to source), so the shared walk.glb/ idle.glb clips retarget unchanged (both left untouched). Contact sheet: docs/shots/laneE/peds_decimated.png — silhouettes/textures read clean, no melted faces/hands (fine for street distance). Originals backed up (gitignored) at pipeline/.peds_orig/; per-ped before/after in pipeline/_peds_decim.json. Lane D validates rig binding + anims (task D2); Lane F re-measures gate 3 (~24-rig near cap × ≤3k ≈ ≤67k fleet contribution).

Round 4 — E2 asset re-exports (published)

  • procity_fit_record_crate_01.glb re-export (fixed). The R3 normalize left a sourceless texture (packed AO/roughness map didn't survive the WebP export; the material's metallicRoughnessTexture + occlusionTexture pointed at it), which crashed three.js GLTFLoader with undefined 'uri'. pipeline/fix_glb_textures.py strips material slots that reference a sourceless texture (base-colour + normal kept; the orphan is never loaded). Verified it loads in the vendored GLTFLoader (5000 tris, textures ok); re-published to the depot.
  • procity_fit_counter_till_01.glb (new). ~2 m timber shop counter with a period beige till — flux_local concept (3 rolls for the ~2:1 aspect) → trellis_mac mesh → normalize.py. 1.61 × 0.64 m, 1.15 m tall, 32k tris (TRELLIS shell-soup floor, within decision-#2's 2577k for focal props). Provenance: 100% synthetic/on-device (FLUX.2-klein-4B + TRELLIS.2 on the M3 Ultra), no third-party source — cleanest licence zone. Replaces the R3 4 m till-less counter for the interior counter slot (Lane C maps countercounter_till); the balcao 4 m counter stays for long-bar use.

Round 5 — E1: hero-prop bake-to-lowpoly (un-parked)

The 6 heavy TRELLIS hero props were 25k77k tris (shell-soup — simplify-only floors there, R3's lesson). Rebuilt topology + re-baked base colour via pipeline/bake_lowpoly.py (Blender headless): voxel-remesh → triangulate → decimate ≤8k → smart-UV → Cycles DIFFUSE(COLOR) bake HP→LP → WebP export. All ≤8k tris, base colour baked onto fresh UVs, self-illuminated a touch (matches normalize). Originals backed up (gitignored) at pipeline/.props_orig/; batch spec pipeline/batch_bake.json; results pipeline/_bake_results.json.

prop tris before → after voxel size on disk note
drinks_fridge 77047 → 8000 0.03 5.3M → 0.9M hollow glass-door box — needed a coarser voxel (0.012 grabbed the internal shelves into wireframe)
milkshake_mixer 73492 → 7991 0.005 4.7M → 0.9M small part, fine voxel
listening_booth 53502 → 7986 0.012 3.4M → 0.9M panel + stool
counter_till 32226 → 7999 0.012 1.9M → 0.9M timber counter + till
novelty_record 26486 → 8000 0.014 1.8M → 0.9M thick disc + base
arcade_cabinet 25142 → 8000 0.012 1.5M → 0.8M boxy cabinet

Fleet: 288k → 48k tris, 18M → 5.2M. Every one eyeballed (contact sheet docs/shots/laneE/props_baked.png — no melted labels/faces; the fridge re-bake was the only redo) and verified loading in the vendored GLTFLoader (tris ≤8k, baked texture present). Re-published to the depot (validate --depot 0 errors). Provenance unchanged: 100% synthetic/ on-device (FLUX.2-klein-4B + TRELLIS.2 + Blender bake, no third-party source). → Lane C re-measures the record interior (was ~273k tris).

Round 5 — E3: fal.ai fallback props (glass-case + bus-shelter + magazine-rack)

The two R3 TRELLIS glass rejects + the optional magazine-rack, generated on fal.ai Hunyuan3D v2 (John-authorized, decision #4). Pipeline: flux_local concept (frosted/opaque surfaces so the reconstructor has something to grab — clear glass is what defeated TRELLIS) → pipeline/gen_props_fal.py (fal fal-ai/hunyuan3d/v2, key from MODELBEAST settings, never hardcoded) → normalize.py (scale/ origin) → bake_lowpoly.py (voxel-remesh → ≤8k → EMIT albedo bake; Hunyuan materials need the base-colour→emission route, DIFFUSE(COLOR) baked them near-black).

Paid gen log (fal Hunyuan3D v2, ~$0.16/gen):

prop attempts cost outcome
glass-case 1 (frosted-glass concept) ~$0.16 solid case, timber base, items visible — TRELLIS couldn't; 40k→8k baked
bus-shelter 2 (att.1 dropped the thin perspex panels → att.2 with an opaque back wall) ~$0.32 solid shelter, back wall + roof + bench; 40k→8k
magazine-rack 1 (optional; solid-timber, not wire) ~$0.16 tiered timber rack w/ magazines — beat the R3 wire failure; 40k→8k

Total fal spend this round: ~$0.64 of the $22 balance (well under the <$1 estimate). Guardrail held — nothing else on fal. All 3 ≤8k tris, base-colour baked, verified loading in the vendored GLTFLoader, eyeballed (docs/shots/laneE/props_fal.png — no melted glass, no legible branding), published, validate --depot 0 err. Provenance: FLUX.2-klein-4B concept + fal Hunyuan3D v2 mesh + Blender bake — synthetic, no third-party source (Hunyuan output is model-generated geometry). Raw fal GLBs + concepts kept in gitignored pipeline/.genprops/. These retire the last two primitive-only fittings (glass_case, magazine_rack) + add a street bus_shelter.

Round 5 — E2: depot growth (consumer-driven)

The round-5 v2 features are procedural (dig.js riffles generated sleeve art; B's window shader is 100% procedural; D's roster is spawn logic) — so no forced GLB needs. Per Fable "don't add manifest entries no lane consumes", E2 is deliberately light: two already-synthetic depot props with clear consumers, re-normalized to procity names + thumbs (both already ≤5k tris, metre-scale, base-origin):

new id source footprint / h consumer
street/bin (procity_street_bin_01.glb) council-bin.glb (thriftgod_hero, on depot) 0.57×0.68 m, 1.0 m Lane B street dressing (towns need bins)
fit/crate_stack (procity_fit_crate_stack_01.glb) milk-crate-stack.glb (thriftgod_hero) 0.71×0.72 m, 0.9 m Lane C record-store floor piles / dig context

Both 3k tris, verified loading in the vendored GLTFLoader, published, validate --depot 0 err. Provenance: MeshGod-generated (thriftgod_hero) — synthetic, web-ok. Note: also resolved Lane B's B3 ask — novelty_record (was 26.5k, spiking the busy view) is now 8k via the E1 bake. No speculative props added; further depot growth is available on request once C's dig / D's roster name concrete needs.

Round 7 — E1: ped sub-mesh merge to one draw (gates the roster flip)

The 19 ped rigs carried 19 primitives / 13 materials each = 92 draws for the fleet (≈116 at the 24-rig near cap) — the optimisation D's memo gates full-density roster on. pipeline/merge_ped.py (headless Blender) merges each ped to ONE primitive + one atlased material: join meshes → pack every material's UV islands into a non-overlapping atlas UV → EMIT-bake each source material's base-colour into one 1024 atlas (route base-colour→emission via the ORIGINAL uv so the atlas UV doesn't hijack the lookup) → single material. Skinning + skeleton fully preserved (65 joints intact on all 19, JOINTS_0/WEIGHTS_0 kept; walk/idle untouched).

Result: fleet 92 → 19 draws (1/ped); at the 24-rig near cap 116 → 24 draws (79% cut). Size 5M → 4M. Verified in the vendored GLTFLoader: all 19 load with isSkinnedMesh + bind the shared walk.glb clip; man_suit (2-mat) poses to byte-identical dimensions vs the original (109.038 × 62.5) proving the skin deforms unchanged; man_athletic (3-mat, most complex atlas) eyeballed correct (skin/top/pants regions all right). Pre-merge decimated peds backed up (gitignored) in pipeline/.peds_r6/; per-ped in pipeline/_peds_merge.json. → Lane D validates (D1) — full silhouette/material/impostor/identity pass + full-density numbers — then F flips the roster.