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>
This commit is contained in:
parent
5671a06aef
commit
8644d3f7ac
@ -14,10 +14,16 @@ other lanes' files was touched.*
|
||||
eyeballed (`docs/shots/laneE/props_baked.png` — fridge needed a coarser voxel re-bake, rest clean)
|
||||
+ verified loading in the vendored GLTFLoader. Re-published; `validate --depot` 0 err. Originals
|
||||
gitignored in `pipeline/.props_orig/`. → Lane C re-measures the record interior (was ~273k).
|
||||
- **E3 — fal.ai fallbacks (authorized, decision #4):** glass-case + bus-shelter (+ optional
|
||||
magazine-rack) via fal Hunyuan v2, ≤2 attempts each, costs logged in AUDIT. *(in progress)*
|
||||
- **E3 — fal.ai fallbacks: DONE.** glass_case + bus_shelter + magazine_rack via fal Hunyuan3D v2
|
||||
(`pipeline/gen_props_fal.py`, key from MODELBEAST settings). Frosted/opaque concepts (`flux_local`)
|
||||
gave the reconstructor a surface — all 3 succeeded where TRELLIS failed. Baked to ≤8k (EMIT albedo
|
||||
bake — Hunyuan materials need base-colour→emission or DIFFUSE bakes near-black), verified loading,
|
||||
published, `validate --depot` 0 err. **Total fal spend ~$0.64** (glass 1 att, bus 2 att, mag 1
|
||||
att × ~$0.16), well under the <$1 estimate; guardrail held. Costs logged in AUDIT R5. Last two
|
||||
primitive-only fittings (glass_case, magazine_rack) retired; new street `bus_shelter`. Manifest
|
||||
now 17 fittings + 9 furniture (26 GLBs). Contact sheet `docs/shots/laneE/props_fal.png`.
|
||||
- **E2 — depot growth for v2:** ~8–12 library props serving C's dig / D's roster / B's windows.
|
||||
*(in progress)*
|
||||
*(next)*
|
||||
|
||||
## Round 4 (Fable) — ped decimation (gate-3 critical path) + asset fixes
|
||||
|
||||
|
||||
@ -21,9 +21,27 @@ baked (no separate metallic/normal maps → also dodges the record_crate danglin
|
||||
→ **Please re-measure the record interior** (was ~273k tris in your R4 note) and record the new
|
||||
number in LANE_C_GLB_VALIDATION.md — it should drop by ~most of the prop weight.
|
||||
|
||||
### → Fable: E1 (bake) status
|
||||
DONE + committed. 6 props ≤8k, published, `validate --depot` 0 err, qa gate green. Feeds C's
|
||||
re-measure + your v1.1 tag. E3 (fal glass-case/bus-shelter) + E2 (depot growth) next.
|
||||
### → Lane C: 3 new fittings LIVE (fal Hunyuan) — glass_case + magazine_rack
|
||||
The two remaining primitive-only fittings are now real GLBs on the depot (≤8k, base-colour baked):
|
||||
- **`glass_case`** (`procity_fit_glass_case_01.glb`, 1.08×1.42 m, 1.0 m tall) — timber base + glass
|
||||
panels + items inside. Registry ids `glass_case` (toy/dept) + `glass_cabinets` (pawn) both alias
|
||||
to it. → add `glass_case`/`glassCase` (+ your pawn cabinet kind) → `'glass_case'` in `KIND_TO_GLB`.
|
||||
- **`magazine_rack`** (`procity_fit_magazine_rack_01.glb`, 0.54×0.44 m, 1.2 m tall) — tiered timber
|
||||
rack w/ magazines (milkbar/pawn). Registry `magazine_rack` aliases to it. → map your kind → `'magazine_rack'`.
|
||||
- Both base-origin, verified loading in the vendored GLTFLoader. `manifest.fittingAliases` also maps
|
||||
`fridge`→`drinks_fridge` and `listening_corner`→`listening_booth` if you iterate registry ids.
|
||||
|
||||
### → Lane B: new street prop — `bus_shelter`
|
||||
`procity_street_bus_shelter_01.glb` (manifest `furniture.bus_shelter`, 2.33×1.41 m, 2.4 m tall, ≤8k,
|
||||
base-origin). Solid painted-steel shelter (back wall + roof + bench) for street dressing / D's roster
|
||||
context — place at bus-stop-ish spots along the footpath if useful. Primitive-free; fail-soft as usual.
|
||||
|
||||
### → Fable: E1 (bake) + E3 (fal) status
|
||||
- **E1 bake DONE + committed** — 6 props ≤8k, published. Feeds C's re-measure + your v1.1 tag.
|
||||
- **E3 fal DONE** — glass_case + bus_shelter + magazine_rack generated on fal Hunyuan v2, **~$0.64
|
||||
total** (log in AUDIT R5), ≤2 attempts each, all baked ≤8k + published. Guardrail held (nothing
|
||||
else on fal). `validate --depot` 0 err. Last two primitive-only fittings retired.
|
||||
- **E2 depot growth** next (v2-serving library props).
|
||||
|
||||
---
|
||||
|
||||
|
||||
BIN
docs/shots/laneE/props_fal.png
Normal file
BIN
docs/shots/laneE/props_fal.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
@ -219,3 +219,28 @@ and **verified loading in the vendored `GLTFLoader`** (tris ≤8k, baked texture
|
||||
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.
|
||||
|
||||
@ -101,6 +101,54 @@
|
||||
1.61,
|
||||
0.61
|
||||
]
|
||||
},
|
||||
{
|
||||
"file": "procity_fit_glass_case_01.glb",
|
||||
"tri_before": 40000,
|
||||
"tri_after": 7999,
|
||||
"size_m": [
|
||||
1.085,
|
||||
0.997,
|
||||
1.418
|
||||
],
|
||||
"footprint": [
|
||||
1.08,
|
||||
1.42
|
||||
],
|
||||
"thumb": "/tmp/fal_bake/thumbs/procity_fit_glass_case_01.png",
|
||||
"out": "/tmp/fal_bake/procity_fit_glass_case_01.glb"
|
||||
},
|
||||
{
|
||||
"file": "procity_street_bus_shelter_01.glb",
|
||||
"tri_before": 40000,
|
||||
"tri_after": 7998,
|
||||
"size_m": [
|
||||
2.333,
|
||||
2.4,
|
||||
1.414
|
||||
],
|
||||
"footprint": [
|
||||
2.33,
|
||||
1.41
|
||||
],
|
||||
"thumb": "/tmp/fal_bake/thumbs/procity_street_bus_shelter_01.png",
|
||||
"out": "/tmp/fal_bake/procity_street_bus_shelter_01.glb"
|
||||
},
|
||||
{
|
||||
"file": "procity_fit_magazine_rack_01.glb",
|
||||
"tri_before": 40000,
|
||||
"tri_after": 8000,
|
||||
"size_m": [
|
||||
0.545,
|
||||
1.2,
|
||||
0.443
|
||||
],
|
||||
"footprint": [
|
||||
0.54,
|
||||
0.44
|
||||
],
|
||||
"thumb": "/tmp/fal_bake/thumbs/procity_fit_magazine_rack_01.png",
|
||||
"out": "/tmp/fal_bake/procity_fit_magazine_rack_01.glb"
|
||||
}
|
||||
],
|
||||
"errors": []
|
||||
|
||||
@ -88,7 +88,31 @@ def make_lowpoly(hp, voxel, tris):
|
||||
return lp
|
||||
|
||||
|
||||
def _base_color_to_emission(hp):
|
||||
"""Route every HP material's base-colour (texture or flat value) into its Emission and mute the
|
||||
BSDF, so a bake_type='EMIT' pass reads PURE ALBEDO regardless of the source material setup
|
||||
(TRELLIS and fal-Hunyuan wire their materials differently; DIFFUSE(COLOR) mis-reads fal's)."""
|
||||
for m in hp.data.materials:
|
||||
if not m or not m.use_nodes:
|
||||
continue
|
||||
nt = m.node_tree
|
||||
b = nt.nodes.get("Principled BSDF")
|
||||
if not b:
|
||||
continue
|
||||
bc, ec = b.inputs.get("Base Color"), (b.inputs.get("Emission Color") or b.inputs.get("Emission"))
|
||||
es = b.inputs.get("Emission Strength")
|
||||
if ec is None:
|
||||
continue
|
||||
if bc and bc.is_linked:
|
||||
nt.links.new(bc.links[0].from_socket, ec)
|
||||
elif bc:
|
||||
ec.default_value = bc.default_value
|
||||
if es is not None:
|
||||
es.default_value = 1.0
|
||||
|
||||
|
||||
def bake_color(hp, lp, img_size):
|
||||
_base_color_to_emission(hp)
|
||||
img = bpy.data.images.new("baked", img_size, img_size)
|
||||
mat = bpy.data.materials.new("baked_mat"); mat.use_nodes = True
|
||||
nt = mat.node_tree
|
||||
@ -113,7 +137,7 @@ def bake_color(hp, lp, img_size):
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
hp.select_set(True); lp.select_set(True)
|
||||
bpy.context.view_layer.objects.active = lp # active = bake TARGET
|
||||
bpy.ops.object.bake(type='DIFFUSE', pass_filter={'COLOR'}, use_clear=True)
|
||||
bpy.ops.object.bake(type='EMIT', use_clear=True) # HP base-colour routed to emission = albedo
|
||||
|
||||
# self-illuminate a touch so it reads in any viewer (matches normalize.py bright)
|
||||
ec = bsdf.inputs.get("Emission Color") or bsdf.inputs.get("Emission")
|
||||
|
||||
5
pipeline/batch_fal.json
Normal file
5
pipeline/batch_fal.json
Normal file
@ -0,0 +1,5 @@
|
||||
[
|
||||
{"src": "/Users/m3ultra/Documents/procity/pipeline/.genprops/glass_case_fal1.glb", "id": "glass_case", "category": "fit", "height_m": 1.0, "tris": 60000},
|
||||
{"src": "/Users/m3ultra/Documents/procity/pipeline/.genprops/bus_shelter_fal2.glb", "id": "bus_shelter", "category": "street", "height_m": 2.4, "tris": 60000},
|
||||
{"src": "/Users/m3ultra/Documents/procity/pipeline/.genprops/magazine_rack_fal1.glb", "id": "magazine_rack", "category": "fit", "height_m": 1.2, "tris": 60000}
|
||||
]
|
||||
@ -173,6 +173,9 @@ def build():
|
||||
"clothes_racks": "clothes_rack", "bricabrac_shelving": "wire_shelf", "book_wall": "bookshelf",
|
||||
"cube_shelves": "cube_shelf_wide", "display_tables": "coffee_table",
|
||||
"bookshelves": "bookshelf", "vhs_shelving": "wire_shelf", "trestle_tables": "work_table",
|
||||
# round-5 fal/MODELBEAST hero props → their registry semantic ids
|
||||
"glass_case": "glass_case", "glass_cabinets": "glass_case", "magazine_rack": "magazine_rack",
|
||||
"fridge": "drinks_fridge", "listening_corner": "listening_booth",
|
||||
}
|
||||
fitting_aliases = {k: v for k, v in fitting_aliases.items() if v in fittings}
|
||||
|
||||
|
||||
48
pipeline/gen_props_fal.py
Normal file
48
pipeline/gen_props_fal.py
Normal file
@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env python3
|
||||
"""fal.ai Hunyuan3D v2 image->GLB for the two TRELLIS glass rejects (glass-case, bus-shelter).
|
||||
|
||||
Paid — John-authorized (ROUND5 decision #4; account topped up). ~14-17c/gen, <=2 attempts each,
|
||||
every gen logged to AUDIT.md by the caller. Uses the owner's FAL_KEY configured in MODELBEAST
|
||||
settings (never hardcoded here) via MODELBEAST's own settings loader — the same env-injection the
|
||||
server does. MUST run with MODELBEAST's server venv python (it has fal_client):
|
||||
|
||||
MBPY=~/Documents/MODELBEAST/.venv/bin/python
|
||||
$MBPY pipeline/gen_props_fal.py CONCEPT.png OUT.glb
|
||||
|
||||
Prints `FAL_OK <glb> ~$0.16` on success (cost is the caller's to record), `FAL_ERR ...` on failure.
|
||||
"""
|
||||
import sys, os, subprocess, json, glob, shutil
|
||||
|
||||
MB = os.path.expanduser("~/Documents/MODELBEAST")
|
||||
sys.path.insert(0, MB)
|
||||
from server import db, settings as S # noqa: E402
|
||||
|
||||
ENDPOINT = "fal-ai/hunyuan3d/v2" # ~$0.16 geo (+ texture); see MESHGOD_BATCH.md pricing
|
||||
COST = 0.16
|
||||
|
||||
|
||||
def main():
|
||||
concept, out = sys.argv[1], sys.argv[2]
|
||||
con = db.connect()
|
||||
env = os.environ.copy()
|
||||
env.update(S.env_for_operator(S.get_all(con), ["FAL_KEY"]))
|
||||
if not env.get("FAL_KEY"):
|
||||
print("FAL_ERR FAL_KEY not configured in MODELBEAST settings"); sys.exit(2)
|
||||
|
||||
opdir = os.path.join(MB, "server/operators/fal_hunyuan3d")
|
||||
outdir = os.path.join(os.path.dirname(out) or ".", "_fal_" + os.path.basename(out).replace(".glb", ""))
|
||||
os.makedirs(outdir, exist_ok=True)
|
||||
params = json.dumps({"seed": 0, "textured_mesh": True})
|
||||
r = subprocess.run([sys.executable, os.path.join(opdir, "run.py"),
|
||||
"--input", concept, "--outdir", outdir, "--params", params],
|
||||
env=env, capture_output=True, text=True, timeout=600)
|
||||
glbs = glob.glob(os.path.join(outdir, "**", "*.glb"), recursive=True)
|
||||
if not glbs:
|
||||
print("FAL_ERR " + (r.stderr or r.stdout or "no glb")[-300:]); sys.exit(1)
|
||||
shutil.move(sorted(glbs)[0], out)
|
||||
shutil.rmtree(outdir, ignore_errors=True)
|
||||
print(f"FAL_OK {out} ~${COST:.2f} ({ENDPOINT})")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -8,6 +8,12 @@
|
||||
"fal_fallback": {"_comment": "TRELLIS failed on these transparent-glass subjects (no GLB); sf3d gives a featureless blob. Local 2-attempt budget spent -> fal.ai, gated on John (<=$2.70). Both have in-engine primitive fallbacks so nothing blocks.", "glass-case": "transparent glass top defeats TRELLIS segmentation", "bus-shelter": "thin steel frame + perspex panels"},
|
||||
"note_tris": "TRELLIS meshes are dense multi-shell scans; normalize.py decimate floors 25-77k tris (>library <=5k law). Accepted for focal/optional/LOD-able props (texture-intact); a texture-bake-to-lowpoly / gltfpack pass is a follow-up."
|
||||
},
|
||||
"_round5_outcome": {
|
||||
"_comment": "Round 5 (Fable): fal.ai authorized ($22 balance). The R3 fal_fallback trio is now DONE + live on the depot. Pipeline: flux_local concept (frosted/opaque surfaces) -> pipeline/gen_props_fal.py (fal-ai/hunyuan3d/v2) -> normalize -> bake_lowpoly (<=8k, EMIT albedo bake). ALSO: all 6 R3/R4 TRELLIS hero props baked to <=8k (pipeline/bake_lowpoly.py). Costs logged in AUDIT.md R5.",
|
||||
"fal_done": {"glass-case": "1 attempt ~$0.16", "bus-shelter": "2 attempts ~$0.32 (att.2 opaque back wall)", "magazine-rack": "1 attempt ~$0.16 (optional, solid timber not wire)"},
|
||||
"fal_total": "~$0.64 of $22; guardrail held (nothing else on fal)",
|
||||
"baked_to_8k": ["drinks_fridge", "milkshake_mixer", "arcade_cabinet", "listening_booth", "novelty_record", "counter_till", "glass_case", "bus_shelter", "magazine_rack"]
|
||||
},
|
||||
"assets": [
|
||||
{"name": "listening-booth", "prompt": "a record shop listening station booth, angled timber panel with a shelf and hanging headphones, single stool", "height_m": 1.6, "tris": 6000, "for": "record"},
|
||||
{"name": "cash-register", "prompt": "an ornate antique brass cash register with round number keys and a bell", "height_m": 0.35, "tris": 6000, "for": "all", "reuse": "thriftgod_hero:vintage-cash-register — check depot first"},
|
||||
|
||||
@ -693,6 +693,24 @@
|
||||
"height": 1.146,
|
||||
"thumb": "thumbs/procity_fit_counter_till_01.png"
|
||||
},
|
||||
"glass_case": {
|
||||
"file": "procity_fit_glass_case_01.glb",
|
||||
"footprint": [
|
||||
1.08,
|
||||
1.42
|
||||
],
|
||||
"height": 0.997,
|
||||
"thumb": "thumbs/procity_fit_glass_case_01.png"
|
||||
},
|
||||
"magazine_rack": {
|
||||
"file": "procity_fit_magazine_rack_01.glb",
|
||||
"footprint": [
|
||||
0.54,
|
||||
0.44
|
||||
],
|
||||
"height": 1.2,
|
||||
"thumb": "thumbs/procity_fit_magazine_rack_01.png"
|
||||
},
|
||||
"cash_register": {
|
||||
"file": "vintage-cash-register.glb",
|
||||
"footprint": [
|
||||
@ -774,6 +792,15 @@
|
||||
],
|
||||
"height": 1.992,
|
||||
"thumb": "thumbs/procity_street_novelty_record_01.png"
|
||||
},
|
||||
"bus_shelter": {
|
||||
"file": "procity_street_bus_shelter_01.glb",
|
||||
"footprint": [
|
||||
2.33,
|
||||
1.41
|
||||
],
|
||||
"height": 2.4,
|
||||
"thumb": "thumbs/procity_street_bus_shelter_01.png"
|
||||
}
|
||||
},
|
||||
"fittingAliases": {
|
||||
@ -787,6 +814,11 @@
|
||||
"display_tables": "coffee_table",
|
||||
"bookshelves": "bookshelf",
|
||||
"vhs_shelving": "wire_shelf",
|
||||
"trestle_tables": "work_table"
|
||||
"trestle_tables": "work_table",
|
||||
"glass_case": "glass_case",
|
||||
"glass_cabinets": "glass_case",
|
||||
"magazine_rack": "magazine_rack",
|
||||
"fridge": "drinks_fridge",
|
||||
"listening_corner": "listening_booth"
|
||||
}
|
||||
}
|
||||
BIN
web/assets/thumbs/procity_fit_glass_case_01.png
Normal file
BIN
web/assets/thumbs/procity_fit_glass_case_01.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
BIN
web/assets/thumbs/procity_fit_magazine_rack_01.png
Normal file
BIN
web/assets/thumbs/procity_fit_magazine_rack_01.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
BIN
web/assets/thumbs/procity_street_bus_shelter_01.png
Normal file
BIN
web/assets/thumbs/procity_street_bus_shelter_01.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
Loading…
Reference in New Issue
Block a user