# 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.json` → `fittings` / `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__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, ≤$2.70) 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 (**25–77k tris, ~0.3–3.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 **~31k–56k 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` `weld` → `simplify` (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 1564–2805, all ≤3k), **36M → 5M** on disk. **Skinning fully preserved** — `JOINTS_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 25–77k 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 `counter` → `counter_till`); the balcao 4 m `counter` stays for long-bar use.