v2 features are procedural (no forced GLB needs), so E2 is light per Fable's "no unconsumed entries": two synthetic depot props with clear homes — council-bin -> procity_street_bin_01 (Lane B street dressing) + milk-crate-stack -> procity_fit_crate_stack_01 (Lane C floor piles / dig). Both 3k tris, base-origin, re-normalized to procity names + thumbs, published, verified loading, validate --depot 0 err. Manifest 18 fittings + 10 furniture (28 GLBs). Also noted B3 resolved (novelty_record 26.5k->8k via E1 bake). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
20 KiB
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_01was native-metres), up-axis varies, tri counts span 12 → 960k, textures reach 4096px, and several assets carry branded/trademarked designs (quarantined). pipeline/normalize.pyfixes 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.mdor 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 themsignboard:"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.pypushes 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 ~14–17¢/gen: ~$0.28–0.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 sheetdocs/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_normalizedset).
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 viapipeline/publish.pyonceGOD3_PWis 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.glbre-export (fixed). The R3 normalize left a sourceless texture (packed AO/roughness map didn't survive the WebP export; the material'smetallicRoughnessTexture+occlusionTexturepointed at it), which crashed three.js GLTFLoader withundefined 'uri'.pipeline/fix_glb_textures.pystrips material slots that reference a sourceless texture (base-colour + normal kept; the orphan is never loaded). Verified it loads in the vendoredGLTFLoader(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_localconcept (3 rolls for the ~2:1 aspect) →trellis_macmesh →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 mapscounter→counter_till); the balcao 4 mcounterstays for long-bar use.
Round 5 — E1: hero-prop bake-to-lowpoly (un-parked)
The 6 heavy TRELLIS hero props were 25k–77k 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.