PROCITY/pipeline/AUDIT.md
m3ultra 8644d3f7ac Lane E round 5 (E3): fal Hunyuan glass_case + bus_shelter + magazine_rack (~$0.64)
The 2 R3 TRELLIS glass rejects + optional magazine-rack, generated on fal.ai
Hunyuan3D v2 (John-authorized, $22 balance). pipeline/gen_props_fal.py drives the
fal operator using the FAL_KEY from MODELBEAST settings (never hardcoded). Frosted/
opaque flux_local concepts gave the reconstructor a surface (clear glass defeated
TRELLIS). glass-case 1 att, bus-shelter 2 att (opaque back wall), magazine-rack 1 att
= ~$0.64 total, guardrail held. All baked to <=8k via bake_lowpoly.py (switched to
EMIT albedo bake — Hunyuan materials bake near-black under DIFFUSE). Verified loading
in vendored GLTFLoader, published, validate --depot 0 err. Manifest 17 fittings + 9
furniture; aliases wire glass_case/glass_cabinets/magazine_rack/fridge/listening_corner.
Costs logged in AUDIT R5. Last 2 primitive-only fittings retired.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 21:20:44 +10:00

247 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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_<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` `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
15642805, 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 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 `counter``counter_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.