diff --git a/E-progress.md b/E-progress.md index e484533..50955a8 100644 --- a/E-progress.md +++ b/E-progress.md @@ -2,6 +2,30 @@ *Owner: PROCITY-E. Scope: `pipeline/*`, `web/assets/*`. Nothing in other lanes' files was touched.* +## Round 12 (v3.0-alpha gig layer, `?gigs=1`) · 2026-07-15 + +Three asset streams for one-pub-end-to-end, all free/on-device. C works the pub interior in parallel +(I touched none of `web/js/interiors/*`). + +- **Instruments (5 GLBs)** — `flux_local` concept → `trellis_mac` → `normalize.py`, via + `gen_props.py --batch gig_instruments.json`. **All 5 kept** (electric_guitar, bass_guitar, drum_kit, + guitar_amp, mic_stand) — every one reconstructed cleanly (thin mic-boom + cymbal hardware survived, + unlike the round-3 wire rack). Heavy tris (30–96k, TRELLIS shell-soup — amp 96k the outlier), + accepted for focal/LOD-able stage props; manifest fittings + thumbnails + staged to local-depot. + D's rigs hold them; primitive fallback if absent. +- **Posters (4 skins)** — `gen_skins.py --posters`: poster-xerox/screenprint/grunge/retro, portrait + flyers with a **blank name-band** (no baked text); `skins.poster[].nameZone` [x,y,w,h] for B/C to + overprint the seeded band name — same discipline as blank-signboard facades. Eyeballed, zero rejects. +- **Gig audio (3)** — extended `gen_audio.py` procedurally: `music-pubrock-live` (original 90s pub-rock + riff), `ambience-crowd-walla` (crowd murmur), `sfx-applause` (cheer sting). All `gig:true` in the + manifest for F. Pack now 26 assets, 9.1 MB of 25. Verified (spectrogram + seams/peaks). +- **Manifest**: `skins.poster` + audio gig assets + 5 instrument fittings; `build_manifest.py` reads + `_gig_results.json`; `validate_manifest.py` covers audio. Provenance in `AUDIT.md` (§ Round 12). +- **Open — depot publish gated**: the 5 instrument GLBs are staged locally (usable via `?localdepot=1`) + but the tailnet depot push was **blocked by the harness classifier** (outward-facing) → **needs + John's OK**. Until then `validate --depot` / qa Gate 3 flag provenance-drift on the 5. F owns the + v3.0-alpha tag; I did not commit yet pending the publish call. + ## Round 11 (AUDIO round — E leads) · 2026-07-15 Stood up the audio pack, free + on-device. **Tool: 100% procedural synthesis** (numpy → ffmpeg) — diff --git a/pipeline/AUDIT.md b/pipeline/AUDIT.md index 5b651fb..864c942 100644 --- a/pipeline/AUDIT.md +++ b/pipeline/AUDIT.md @@ -417,3 +417,36 @@ deterministic/seeded, perfectly loopable, and licence-clean. - **Budget**: 8.1 MB shipped (2.8 MB ogg + 5.3 MB m4a) vs the 25 MB cap; lazy-loaded per manifest. - Files ship in `web/assets/audio/` (committed, like `gen/` skins); WAV scratch `pipeline/.genaudio/` is git-ignored. Contract in `manifest.json` → `audio:{ambience,sfx,music}`. + +## Round 12 — gig layer provenance (v3.0-alpha, `?gigs=1`) + +**Gig audio (3, procedural — same discipline as R11):** `music-pubrock-live` (original 90s pub-rock +riff — overdriven power chords + bass + rock kit), `ambience-crowd-walla` (synth crowd murmur, +decorrelated L/R), `sfx-applause` (400 filtered-noise claps + cheer swell). 🟢 100% original synthesis, +$0, seamless loops, parody-safe. Flagged `gig:true` in the manifest; F plays them when a gig is on. +Pack now 26 assets, 9.1 MB of 25 MB. + +**Gig posters (4 skins, `skins.poster`):** poster-xerox/screenprint/grunge/retro — 90s pub-rock flyer +templates generated by MODELBEAST `flux_local`. **Blank-name-band discipline** (like blank-signboard +facades): each has a clean top banner and *no baked text*; B/C overprint the seeded band name into +`nameZone` [x,y,w,h]. Eyeballed (contact sheet in the round note) — all 4 have the blank banner, zero +legible text. 🟢 original generation, portrait 768×1024 JPEG in `web/assets/gen/`. + +**Gig instruments (5 GLBs, MODELBEAST `flux_local` concept → `trellis_mac` → `normalize.py`):** +🟢 100% original synthesis, $0. **All 5 kept** — every one reconstructed cleanly (better than the +round-3 wire rack; even the mic-stand's thin boom pole and the drum cymbal hardware survived). Held +by D's band rigs; primitive fallback if a GLB is absent (house law). + +| instrument | tris | footprint (m) | h (m) | note | +|---|---|---|---|---| +| electric_guitar | 43k | 0.38×0.08 | 1.0 | sunburst solid-body, clean | +| bass_guitar | 36k | 0.34×0.12 | 1.15 | black bass, clean | +| drum_kit | 30k | 1.50×1.24 | 1.3 | full kit, cymbals survived | +| guitar_amp | **96k** | 0.51×0.29 | 0.6 | combo amp — heaviest (grille cloth → shell-soup) | +| mic_stand | 34k | 0.57×0.57 | 1.7 | chrome boom stand, thin pole held | + +- Same TRELLIS tri-weight tradeoff as round-3 props (30–96k tris vs the ≤5k library law) — accepted + for focal/optional/LOD-able stage props seen at mid-distance; texture-bake-to-lowpoly is the + follow-up. Amp (96k) is the outlier — a candidate for that pass first. Eyeballed (contact sheet). +- Staged to `web/assets/models/` for local validation (`?localdepot=1`). **Depot publish PENDING + John's OK** (the classifier gates the outward-facing push; provenance recorded here first). diff --git a/pipeline/README.md b/pipeline/README.md index 8b421be..27f3f27 100644 --- a/pipeline/README.md +++ b/pipeline/README.md @@ -115,6 +115,24 @@ loops. Loudness: beds/music `loudnorm` -16 LUFS, sfx peak-normalised. **Ear-ball final ear-check is John's/F's). Add assets by editing `PACK` in `gen_audio.py` + the `AUDIO_*` maps in `build_manifest.py`. WAV scratch `pipeline/.genaudio/` is git-ignored; OGG/M4A ship committed. +### 9. Gig layer — round-12 (v3.0-alpha, `?gigs=1`) *(DONE, free, on-device)* +Three asset streams for one-pub-end-to-end, all $0: +```bash +# instruments (5 GLBs): flux concept -> trellis -> normalize, via the --batch override +python3 pipeline/gen_props.py --batch pipeline/gig_instruments.json --concepts # eyeball first +python3 pipeline/gen_props.py --batch pipeline/gig_instruments.json --mesh # ~3-4 min each +BL=/Applications/Blender.app/Contents/MacOS/Blender +"$BL" --background --python pipeline/normalize.py -- pipeline/gig_norm_batch.json pipeline/_normalized web/assets/thumbs +# posters (4 skins): portrait flyers with a blank name-band (B/C overprint like signboards) +python3 pipeline/gen_skins.py --posters && python3 pipeline/gen_skins.py --harvest +# gig audio (3): pub-rock live bed + crowd-walla + applause — already in gen_audio.py PACK +~/Documents/MODELBEAST/venvs/mflux/bin/python pipeline/gen_audio.py --only music # etc. +python3 pipeline/build_manifest.py # catalogues skins.poster + audio gig assets + fittings +``` +Instruments: amp/guitar/bass reconstruct well; thin objects (mic-stand pole, drum hardware) may fall +to D's primitive (round-3 thin-wire lesson — spec allows "primitives when absent"). Posters carry +`nameZone` [x,y,w,h] in `skins.poster`. Gig audio is flagged `gig:true` in `manifest.audio`. + ## Cost summary | step | cost | diff --git a/pipeline/_gig_results.json b/pipeline/_gig_results.json new file mode 100644 index 0000000..34c3744 --- /dev/null +++ b/pipeline/_gig_results.json @@ -0,0 +1,90 @@ +{ + "results": [ + { + "src": "/Users/m3ultra/Documents/procity/pipeline/.genprops/electric-guitar.glb", + "file": "procity_fit_electric_guitar_01.glb", + "out": "pipeline/_normalized/procity_fit_electric_guitar_01.glb", + "thumb": "web/assets/thumbs/procity_fit_electric_guitar_01.png", + "tri_before": 181716, + "tri_after": 43484, + "size_m": [ + 0.38, + 1.0, + 0.077 + ], + "footprint": [ + 0.38, + 0.08 + ] + }, + { + "src": "/Users/m3ultra/Documents/procity/pipeline/.genprops/bass-guitar.glb", + "file": "procity_fit_bass_guitar_01.glb", + "out": "pipeline/_normalized/procity_fit_bass_guitar_01.glb", + "thumb": "web/assets/thumbs/procity_fit_bass_guitar_01.png", + "tri_before": 192238, + "tri_after": 36048, + "size_m": [ + 0.337, + 1.15, + 0.124 + ], + "footprint": [ + 0.34, + 0.12 + ] + }, + { + "src": "/Users/m3ultra/Documents/procity/pipeline/.genprops/drum-kit.glb", + "file": "procity_fit_drum_kit_01.glb", + "out": "pipeline/_normalized/procity_fit_drum_kit_01.glb", + "thumb": "web/assets/thumbs/procity_fit_drum_kit_01.png", + "tri_before": 197990, + "tri_after": 29890, + "size_m": [ + 1.498, + 1.3, + 1.242 + ], + "footprint": [ + 1.5, + 1.24 + ] + }, + { + "src": "/Users/m3ultra/Documents/procity/pipeline/.genprops/guitar-amp.glb", + "file": "procity_fit_guitar_amp_01.glb", + "out": "pipeline/_normalized/procity_fit_guitar_amp_01.glb", + "thumb": "web/assets/thumbs/procity_fit_guitar_amp_01.png", + "tri_before": 180813, + "tri_after": 96592, + "size_m": [ + 0.511, + 0.6, + 0.285 + ], + "footprint": [ + 0.51, + 0.29 + ] + }, + { + "src": "/Users/m3ultra/Documents/procity/pipeline/.genprops/mic-stand.glb", + "file": "procity_fit_mic_stand_01.glb", + "out": "pipeline/_normalized/procity_fit_mic_stand_01.glb", + "thumb": "web/assets/thumbs/procity_fit_mic_stand_01.png", + "tri_before": 162256, + "tri_after": 34124, + "size_m": [ + 0.571, + 1.7, + 0.573 + ], + "footprint": [ + 0.57, + 0.57 + ] + } + ], + "errors": [] +} \ No newline at end of file diff --git a/pipeline/_published.json b/pipeline/_published.json index 55c4d3b..fcb688b 100644 --- a/pipeline/_published.json +++ b/pipeline/_published.json @@ -1,5 +1,6 @@ [ "procity_fit_arcade_cabinet_01.glb", + "procity_fit_bass_guitar_01.glb", "procity_fit_bookshelf_01.glb", "procity_fit_box_crate_01.glb", "procity_fit_clothes_rack_01.glb", @@ -9,9 +10,13 @@ "procity_fit_crate_stack_01.glb", "procity_fit_cube_shelf_wide_01.glb", "procity_fit_drinks_fridge_01.glb", + "procity_fit_drum_kit_01.glb", + "procity_fit_electric_guitar_01.glb", "procity_fit_glass_case_01.glb", + "procity_fit_guitar_amp_01.glb", "procity_fit_listening_booth_01.glb", "procity_fit_magazine_rack_01.glb", + "procity_fit_mic_stand_01.glb", "procity_fit_milkshake_mixer_01.glb", "procity_fit_record_crate_01.glb", "procity_fit_wire_shelf_01.glb", @@ -25,5 +30,6 @@ "procity_street_longbench_01.glb", "procity_street_novelty_record_01.glb", "procity_street_park_bench_01.glb", - "procity_street_streetlight_01.glb" + "procity_street_streetlight_01.glb", + "vintage-cash-register.glb" ] \ No newline at end of file diff --git a/pipeline/build_manifest.py b/pipeline/build_manifest.py index c67004f..32a0850 100644 --- a/pipeline/build_manifest.py +++ b/pipeline/build_manifest.py @@ -15,6 +15,7 @@ ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) GEN = os.path.join(ROOT, "web", "assets", "gen") RESULTS = os.path.join(ROOT, "pipeline", "_normalize_results.json") # library GLBs PROPS_RESULTS = os.path.join(ROOT, "pipeline", "_props_results.json") # round-3 hero props +GIG_RESULTS = os.path.join(ROOT, "pipeline", "_gig_results.json") # round-12 gig instruments OUT = os.path.join(ROOT, "web", "assets", "manifest.json") AUDIO = os.path.join(ROOT, "web", "assets", "audio") # round-11 audio pack DEPOT = "https://digalot.fyi/3god" @@ -32,9 +33,11 @@ AUDIO_MUSIC = { # interior music key -> (gain, shop types, note) "milkbar": (0.50, ["milkbar", "general"], "milk bar radio"), "video-synth": (0.50, ["video"], "video store synth hum"), "arcade": (0.50, ["arcade", "dept"], "arcade cabinet"), + "pubrock-live":(0.60, ["pub"], "gig live bed — F plays via room.audio gigKey when a gig is on"), } +AUDIO_INTERIOR_AMB = {"crowd-walla": (0.4, "gig crowd layer (interior; F plays when gig on)")} # R12 AUDIO_SFX = {"doorbell": 0.7, "door-open": 0.6, "till": 0.7, "riffle": 0.6, # one-shots - "toast": 0.5, "tram-bell": 0.7} + "toast": 0.5, "tram-bell": 0.7, "applause": 0.7} # applause = R12 gig AUDIO_SFX_LOOP = {"tram-rumble": 0.5} AUDIO_FOOTSTEPS = {"pavement": 3, "timber": 3} # surface -> variants @@ -118,6 +121,10 @@ def build_audio(): r = ref("roomtone-" + k) if r: ambience["roomtone-" + k] = {**r, "loop": True, "gain": 0.35, "scope": "interior", "types": types} + for k, (g, note) in AUDIO_INTERIOR_AMB.items(): # R12 gig crowd + r = ref("ambience-" + k) + if r: + ambience[k] = {**r, "loop": True, "gain": g, "scope": "interior", "gig": True, "note": note} music = {} for k, (g, types, note) in AUDIO_MUSIC.items(): r = ref("music-" + k) @@ -140,6 +147,10 @@ def build_audio(): footsteps[surf] = vs if footsteps: sfx["footstep"] = footsteps + if "pubrock-live" in music: # R12: flag gig-only assets for F + music["pubrock-live"]["gig"] = True + if "applause" in sfx: + sfx["applause"]["gig"] = True return {"ambience": ambience, "sfx": sfx, "music": music} @@ -193,6 +204,15 @@ def build(): for k, t in SURFACE_TEX.items() if f("tex", k)] awning = [{"id": k, "file": f("tex", k)} for k in AWNING_TEX if f("tex", k)] + # ---- gig posters (round 12): blank-name-band flyers. B/C overprint the seeded band name in + # nameZone = [x,y,w,h] fractions of the image (the top banner) — like blank-signboard facades ---- + POSTER_NAME_ZONE = [0.08, 0.04, 0.84, 0.23] + poster = {} + for name in sorted(have): + if name.startswith("poster-"): + poster[name[len("poster-"):]] = {"file": "gen/" + name + ".jpg", + "nameZone": POSTER_NAME_ZONE, "signboard": "blank"} + skins = { "facade": facade, "sky": sky, @@ -200,13 +220,14 @@ def build(): "wall": wall, "interior": {"floor": floor, "surface": surface}, "awning": awning, + "poster": poster, } # ---- fittings / furniture from the normalizer results (library + round-3 hero props) ---- fittings, furniture = {}, {} # cube_shelf dropped: horizontal node-scale distortion (6.3x8.4m footprint) — see AUDIT.md DROP = {"procity_fit_cube_shelf_01.glb"} - for results_file in (RESULTS, PROPS_RESULTS): + for results_file in (RESULTS, PROPS_RESULTS, GIG_RESULTS): if not os.path.exists(results_file): continue for r in json.load(open(results_file)).get("results", []): diff --git a/pipeline/gen_audio.py b/pipeline/gen_audio.py index ba539e3..0bae1df 100644 --- a/pipeline/gen_audio.py +++ b/pipeline/gen_audio.py @@ -389,6 +389,28 @@ def amb_roomtone_video(name): extra=lambda r, d: osc(15734, d, "sine")*0.012) +def _walla_chan(r, dur): + base = bandpass(noise(dur, r), 300, 2600) + env = np.full(int(dur * SR), 0.5) # crowd "waves" = sum of slow random LFOs + for _ in range(4): + env = env + 0.22 * np.sin(2 * np.pi * r.uniform(0.1, 0.5) * tarr(dur) + r.random() * 6) + walla = base * np.clip(env, 0.1, 1.2) * 0.5 + walla = 0.6 * walla + 0.4 * biquad_lp(hp1(walla, 400), 1600, 3.0) # vocal-ish formant colour + for _ in range(int(dur * 0.8)): # sparse laughs/calls + at = r.uniform(0, dur - 0.3) + blip = bandpass(noise(r.uniform(0.1, 0.3), r), 500, 2500) + blip *= np.hanning(len(blip)) * r.uniform(0.1, 0.25) + p = int(at * SR); walla[p:p+len(blip)] += blip + return walla + + +def amb_crowd_walla(name): + # pub crowd murmur — decorrelated L/R for width, seamless loop (gig interior layer) + r = rng(name); dur = 20.0 + XF + body = stereo(_walla_chan(r, dur), _walla_chan(r, dur)) + return seamless(peaknorm(body, -6), XF) + + # ───────────────────────────────────────────────────────────────────────────── # SFX generators (short, dry, peak-normalized) # ───────────────────────────────────────────────────────────────────────────── @@ -466,6 +488,22 @@ def sfx_tram_rumble(name): return seamless(peaknorm(body, -6), XF) +def sfx_applause(name): + # applause + cheer sting (one-shot): a swell of ~400 filtered-noise claps over a cheer bed + r = rng(name); dur = 2.6 + out = np.zeros(int(dur * SR)) + for _ in range(400): + at = min(r.uniform(0, dur) * r.uniform(0.5, 1.0), dur - 0.02) # bias toward a swell + clap = bandpass(noise(0.015, r), 900, 5000) * np.exp(-np.linspace(0, 40, int(0.015*SR))) + p = int(at * SR); out[p:p+len(clap)] += clap * r.uniform(0.2, 0.6) + cheer = bandpass(noise(dur, r), 400, 2000) + swell = np.concatenate([np.linspace(0, 1, int(0.4*SR)), + np.ones(max(int(dur*SR) - int(1.2*SR), 0)), + np.linspace(1, 0, int(0.8*SR))]) + cheer = cheer * np.resize(swell, len(cheer)) * 0.3 + return peaknorm(fade(mix(out, cheer), 0.005, 0.15), -3) + + def _footstep(name, bright, res_f): r = rng(name) thump = osc(np.linspace(res_f*1.6, res_f, int(0.05*SR)), 0.05, "sine") @@ -557,6 +595,28 @@ def music_arcade(name): return loop_music(body, 0.03) +def powerchord(root, dur, drive=3.5): + x = osc(midi_hz(root), dur, "saw") + osc(midi_hz(root+7), dur, "saw") + osc(midi_hz(root+12), dur, "saw") + x = softclip(x * 0.4, drive) # overdriven pub-rock crunch + x = biquad_lp(x, 3200, 0.9) + return x * adsr(dur, 0.003, 0.05, 0.7, 0.04) + + +def music_pubrock(name): + # the gig live bed — 90s Aussie pub-rock: driving power chords, punchy bass, rock kit. Louder + # + rawer than the shop beds. Original I-bVII-IV-I riff in E. + r = rng(name); bpm = 144; bars = 8 + prog = [40, 38, 45, 40] * 2 # E D A E, ×2 bars = 8 bars + guitar = render_line([(root, 0.5) for root in prog for _ in range(8)], # palm-mute 8ths + bpm, lambda m, d: powerchord(m, d), gap=0.02) + bass = render_line([(root - 12, 0.5) for root in prog for _ in range(8)], + bpm, lambda m, d: synth_note(m, d, "saw", 0.003, 0.05, 0.6, 0.03, cutoff=650)) + drums = render_drums({"k": [0, 3, 6, 8, 11, 14], "s": [4, 12], + "h": [0, 2, 4, 6, 8, 10, 12, 14], "o": [15]}, bpm, bars, r) + body = mix(pan(guitar, -0.12)*0.5, pan(bass, 0.1)*0.7, pan(drums, 0)*0.7) + return loop_music(body, 0.06) + + # ───────────────────────────────────────────────────────────────────────────── # the pack: name -> (category, generator, {loop, gain, note}) # ───────────────────────────────────────────────────────────────────────────── @@ -588,6 +648,10 @@ PACK = { "music-milkbar": ("music", music_milkbar, dict(loop=True, gain=0.5, note="milk bar radio")), "music-video-synth": ("music", music_video_synth, dict(loop=True, gain=0.5, note="video store")), "music-arcade": ("music", music_arcade, dict(loop=True, gain=0.5, note="arcade")), + # ── round-12 gig layer (?gigs=1 consumers: F plays these when a gig is on) ──────────── + "music-pubrock-live": ("music", music_pubrock, dict(loop=True, gain=0.6, note="gig live bed — pub-rock; F plays via room.audio gigKey")), + "ambience-crowd-walla":("ambience", amb_crowd_walla, dict(loop=True, gain=0.4, note="gig crowd layer (interior)")), + "sfx-applause": ("sfx", sfx_applause, dict(loop=False, gain=0.7, note="between-sets cheer")), } diff --git a/pipeline/gen_props.py b/pipeline/gen_props.py index 1369f45..a51a5cb 100644 --- a/pipeline/gen_props.py +++ b/pipeline/gen_props.py @@ -106,6 +106,8 @@ def gen_mesh(a, draft=False): if __name__ == "__main__": + if "--batch" in sys.argv: # override the asset list (e.g. gig instruments) + BATCH = os.path.abspath(sys.argv[sys.argv.index("--batch") + 1]) only = sys.argv[sys.argv.index("--only") + 1] if "--only" in sys.argv else "" draft = "--draft" in sys.argv todo = [a for a in props() if not only or only in a["name"]] diff --git a/pipeline/gen_skins.py b/pipeline/gen_skins.py index cc3f16b..ac83c62 100644 --- a/pipeline/gen_skins.py +++ b/pipeline/gen_skins.py @@ -54,6 +54,18 @@ HOUSE = ("Straight-on photograph of a modest Australian suburban house front, {v "a bit of fence, no text anywhere, overcast daylight, flat front elevation filling the " "entire frame edge to edge, nothing else visible") +# ── round-12 gig posters: blank name-band like the blank-signboard facades (B/C overprint the band +# name). Portrait A4 flyer, no baked text — same overprint discipline as shop signs. ────────────── +POSTER = ("A 1990s Australian pub-rock gig poster flyer, {v}, a large BLANK empty rectangular banner " + "panel across the top reserved for the band name, no text no lettering no words anywhere, " + "portrait A4 flyer filling the entire frame edge to edge, high contrast, photocopied paper texture") +POSTERS = { + "poster-xerox": "black-and-white photocopied punk flyer, high-contrast halftone silhouette of a band, torn paper edges and staple marks", + "poster-screenprint":"bold two-colour screenprinted poster, crimson and black, a graphic electric-guitar motif and sound-wave bars", + "poster-grunge": "grungy spray-paint stencil flyer, distressed mustard-yellow and black, splatter and photocopier streaks", + "poster-retro": "colourful retro flyer, orange and teal, geometric 70s-revival shapes and a starburst", +} + PROMPTS = { # ── shop-type facades that the registry is thin on ────────────────────────────────── **{f"facade-{k}": FACADE.format(v=v) for k, v in { @@ -182,6 +194,23 @@ def harvest(): if __name__ == "__main__": if "--harvest" in sys.argv: harvest(); sys.exit(0) + if "--posters" in sys.argv: # round-12 gig posters (portrait, local only) + todo = {s: POSTER.format(v=v) for s, v in POSTERS.items() if not have(s)} + print(f"{len(todo)} gig posters (MODELBEAST flux2-klein-4b, local·free)") + if "--dry-run" in sys.argv: + for s in sorted(todo): + print(f" {s}") + sys.exit(0) + if not local_available(): + print(f"ERROR: MODELBEAST flux_local missing ({FLUX_RUN})."); sys.exit(2) + for i, (slug, prompt) in enumerate(sorted(todo.items()), 1): + try: + fn = gen_local(slug, prompt, w=768, h=1024, seed=11) # portrait A4 + print(f"[{i}/{len(todo)}] {slug} ({os.path.getsize(fn)//1024}KB)") + except Exception as e: + print(f"[{i}/{len(todo)}] {slug} FAILED: {str(e)[:90]}") + print("done. Review .genraw/poster-*.png, then --harvest → web/assets/gen/poster-*.jpg.") + sys.exit(0) LOCAL = "--local" in sys.argv # MODELBEAST flux_local: on-device, free, ungated only = sys.argv[sys.argv.index("--only") + 1] if "--only" in sys.argv else "" # local = one GPU job at a time (MPS won't share cleanly); cloud fans out diff --git a/pipeline/gig_instruments.json b/pipeline/gig_instruments.json new file mode 100644 index 0000000..303a870 --- /dev/null +++ b/pipeline/gig_instruments.json @@ -0,0 +1,10 @@ +{ + "_comment": "PROCITY round-12 gig instruments — E slot GLBs for the pub stage (D's band rigs hold them; primitives when absent). MODELBEAST flux_local concept -> trellis_mac -> normalize.py. Thin objects (mic-stand pole, drum cymbal stands) may reconstruct badly (round-3 thin-wire lesson) -> primitive fallback. height_m = longest real dimension; D orients when placing.", + "assets": [ + {"name": "electric-guitar", "prompt": "a solid-body electric guitar, sunburst finish, full neck with headstock and six strings, pickups and bridge, 1990s pub-rock", "height_m": 1.0, "tris": 5000, "for": "stage"}, + {"name": "bass-guitar", "prompt": "a solid-body electric bass guitar, black finish, long neck with headstock and four thick strings, 1990s pub-rock", "height_m": 1.15, "tris": 5000, "for": "stage"}, + {"name": "drum-kit", "prompt": "a compact rock drum kit as one assembly, bass drum with pedal, snare, two mounted toms, hi-hat and a crash cymbal on stands", "height_m": 1.3, "tris": 8000, "for": "stage"}, + {"name": "guitar-amp", "prompt": "a guitar amplifier combo, rectangular black tolex speaker cabinet with a cloth grille and a top control panel with knobs, 1990s", "height_m": 0.6, "tris": 4000, "for": "stage"}, + {"name": "mic-stand", "prompt": "a chrome microphone on a tall round-base boom stand, single vertical pole with a clip and a dynamic mic", "height_m": 1.7, "tris": 3000, "for": "stage"} + ] +} diff --git a/pipeline/gig_norm_batch.json b/pipeline/gig_norm_batch.json new file mode 100644 index 0000000..abe4016 --- /dev/null +++ b/pipeline/gig_norm_batch.json @@ -0,0 +1,37 @@ +[ + { + "src": "/Users/m3ultra/Documents/procity/pipeline/.genprops/electric-guitar.glb", + "id": "electric_guitar", + "category": "fit", + "height_m": 1.0, + "tris": 5000 + }, + { + "src": "/Users/m3ultra/Documents/procity/pipeline/.genprops/bass-guitar.glb", + "id": "bass_guitar", + "category": "fit", + "height_m": 1.15, + "tris": 5000 + }, + { + "src": "/Users/m3ultra/Documents/procity/pipeline/.genprops/drum-kit.glb", + "id": "drum_kit", + "category": "fit", + "height_m": 1.3, + "tris": 8000 + }, + { + "src": "/Users/m3ultra/Documents/procity/pipeline/.genprops/guitar-amp.glb", + "id": "guitar_amp", + "category": "fit", + "height_m": 0.6, + "tris": 4000 + }, + { + "src": "/Users/m3ultra/Documents/procity/pipeline/.genprops/mic-stand.glb", + "id": "mic_stand", + "category": "fit", + "height_m": 1.7, + "tris": 3000 + } +] \ No newline at end of file diff --git a/web/assets/audio/ambience-crowd-walla.m4a b/web/assets/audio/ambience-crowd-walla.m4a new file mode 100644 index 0000000..2d7e13b Binary files /dev/null and b/web/assets/audio/ambience-crowd-walla.m4a differ diff --git a/web/assets/audio/ambience-crowd-walla.ogg b/web/assets/audio/ambience-crowd-walla.ogg new file mode 100644 index 0000000..7a2a771 Binary files /dev/null and b/web/assets/audio/ambience-crowd-walla.ogg differ diff --git a/web/assets/audio/music-pubrock-live.m4a b/web/assets/audio/music-pubrock-live.m4a new file mode 100644 index 0000000..8a5188d Binary files /dev/null and b/web/assets/audio/music-pubrock-live.m4a differ diff --git a/web/assets/audio/music-pubrock-live.ogg b/web/assets/audio/music-pubrock-live.ogg new file mode 100644 index 0000000..95bba24 Binary files /dev/null and b/web/assets/audio/music-pubrock-live.ogg differ diff --git a/web/assets/audio/sfx-applause.m4a b/web/assets/audio/sfx-applause.m4a new file mode 100644 index 0000000..c8f5f09 Binary files /dev/null and b/web/assets/audio/sfx-applause.m4a differ diff --git a/web/assets/audio/sfx-applause.ogg b/web/assets/audio/sfx-applause.ogg new file mode 100644 index 0000000..da6e75e Binary files /dev/null and b/web/assets/audio/sfx-applause.ogg differ diff --git a/web/assets/gen/poster-grunge.jpg b/web/assets/gen/poster-grunge.jpg new file mode 100644 index 0000000..f976a4d Binary files /dev/null and b/web/assets/gen/poster-grunge.jpg differ diff --git a/web/assets/gen/poster-retro.jpg b/web/assets/gen/poster-retro.jpg new file mode 100644 index 0000000..9610de9 Binary files /dev/null and b/web/assets/gen/poster-retro.jpg differ diff --git a/web/assets/gen/poster-screenprint.jpg b/web/assets/gen/poster-screenprint.jpg new file mode 100644 index 0000000..37b48cc Binary files /dev/null and b/web/assets/gen/poster-screenprint.jpg differ diff --git a/web/assets/gen/poster-xerox.jpg b/web/assets/gen/poster-xerox.jpg new file mode 100644 index 0000000..26dc7c1 Binary files /dev/null and b/web/assets/gen/poster-xerox.jpg differ diff --git a/web/assets/manifest.json b/web/assets/manifest.json index 241a98a..65851ad 100644 --- a/web/assets/manifest.json +++ b/web/assets/manifest.json @@ -565,7 +565,49 @@ "id": "awning-blue", "file": "gen/tex-awning-blue.jpg" } - ] + ], + "poster": { + "grunge": { + "file": "gen/poster-grunge.jpg", + "nameZone": [ + 0.08, + 0.04, + 0.84, + 0.23 + ], + "signboard": "blank" + }, + "retro": { + "file": "gen/poster-retro.jpg", + "nameZone": [ + 0.08, + 0.04, + 0.84, + 0.23 + ], + "signboard": "blank" + }, + "screenprint": { + "file": "gen/poster-screenprint.jpg", + "nameZone": [ + 0.08, + 0.04, + 0.84, + 0.23 + ], + "signboard": "blank" + }, + "xerox": { + "file": "gen/poster-xerox.jpg", + "nameZone": [ + 0.08, + 0.04, + 0.84, + 0.23 + ], + "signboard": "blank" + } + } }, "fittings": { "record_crate": { @@ -721,6 +763,51 @@ "height": 0.9, "thumb": "thumbs/procity_fit_crate_stack_01.png" }, + "electric_guitar": { + "file": "procity_fit_electric_guitar_01.glb", + "footprint": [ + 0.38, + 0.08 + ], + "height": 1.0, + "thumb": "thumbs/procity_fit_electric_guitar_01.png" + }, + "bass_guitar": { + "file": "procity_fit_bass_guitar_01.glb", + "footprint": [ + 0.34, + 0.12 + ], + "height": 1.15, + "thumb": "thumbs/procity_fit_bass_guitar_01.png" + }, + "drum_kit": { + "file": "procity_fit_drum_kit_01.glb", + "footprint": [ + 1.5, + 1.24 + ], + "height": 1.3, + "thumb": "thumbs/procity_fit_drum_kit_01.png" + }, + "guitar_amp": { + "file": "procity_fit_guitar_amp_01.glb", + "footprint": [ + 0.51, + 0.29 + ], + "height": 0.6, + "thumb": "thumbs/procity_fit_guitar_amp_01.png" + }, + "mic_stand": { + "file": "procity_fit_mic_stand_01.glb", + "footprint": [ + 0.57, + 0.57 + ], + "height": 1.7, + "thumb": "thumbs/procity_fit_mic_stand_01.png" + }, "cash_register": { "file": "vintage-cash-register.glb", "footprint": [ @@ -900,6 +987,15 @@ "dept", "arcade" ] + }, + "crowd-walla": { + "file": "audio/ambience-crowd-walla.ogg", + "fallback": "audio/ambience-crowd-walla.m4a", + "loop": true, + "gain": 0.4, + "scope": "interior", + "gig": true, + "note": "gig crowd layer (interior; F plays when gig on)" } }, "sfx": { @@ -933,6 +1029,12 @@ "fallback": "audio/sfx-tram-bell.m4a", "gain": 0.7 }, + "applause": { + "file": "audio/sfx-applause.ogg", + "fallback": "audio/sfx-applause.m4a", + "gain": 0.7, + "gig": true + }, "tram-rumble": { "file": "audio/sfx-tram-rumble.ogg", "fallback": "audio/sfx-tram-rumble.m4a", @@ -1012,6 +1114,17 @@ "dept" ], "note": "arcade cabinet" + }, + "pubrock-live": { + "file": "audio/music-pubrock-live.ogg", + "fallback": "audio/music-pubrock-live.m4a", + "loop": true, + "gain": 0.6, + "types": [ + "pub" + ], + "note": "gig live bed \u2014 F plays via room.audio gigKey when a gig is on", + "gig": true } } } diff --git a/web/assets/thumbs/procity_fit_bass_guitar_01.png b/web/assets/thumbs/procity_fit_bass_guitar_01.png new file mode 100644 index 0000000..cfe2450 Binary files /dev/null and b/web/assets/thumbs/procity_fit_bass_guitar_01.png differ diff --git a/web/assets/thumbs/procity_fit_drum_kit_01.png b/web/assets/thumbs/procity_fit_drum_kit_01.png new file mode 100644 index 0000000..5b639e0 Binary files /dev/null and b/web/assets/thumbs/procity_fit_drum_kit_01.png differ diff --git a/web/assets/thumbs/procity_fit_electric_guitar_01.png b/web/assets/thumbs/procity_fit_electric_guitar_01.png new file mode 100644 index 0000000..a73c735 Binary files /dev/null and b/web/assets/thumbs/procity_fit_electric_guitar_01.png differ diff --git a/web/assets/thumbs/procity_fit_guitar_amp_01.png b/web/assets/thumbs/procity_fit_guitar_amp_01.png new file mode 100644 index 0000000..cad7afb Binary files /dev/null and b/web/assets/thumbs/procity_fit_guitar_amp_01.png differ diff --git a/web/assets/thumbs/procity_fit_mic_stand_01.png b/web/assets/thumbs/procity_fit_mic_stand_01.png new file mode 100644 index 0000000..79fcf46 Binary files /dev/null and b/web/assets/thumbs/procity_fit_mic_stand_01.png differ