RECORDANDRONCO/tools/mbsprites.py
2026-07-20 04:51:31 +10:00

79 lines
4.7 KiB
Python

#!/usr/bin/env python3
"""Batch-generate real sprites for every actor: flux_local -> bg_remove_local ->
trim + downscale (Pillow). Overwrites the placeholder PNGs in sequences/assets.
Full-res renders kept in assets_src/renders. Prompts appended to assets_src/PROMPTS.md.
Single facing per actor — the Blender 16-facing turntable pipeline is the Phase D
upgrade path in MEGAPLAN.md.
"""
import os, sys, datetime
sys.path.insert(0, os.path.dirname(__file__))
from mbgen import run, upload # noqa: E402
from PIL import Image # noqa: E402
ROOT = os.path.join(os.path.dirname(__file__), '..')
RENDERS = os.path.join(ROOT, 'assets_src', 'renders')
ASSETS = os.path.join(ROOT, 'mods', 'vinylgod', 'sequences', 'assets')
os.makedirs(RENDERS, exist_ok=True)
MRP = 'rusty junkyard robot aesthetic, rust metal with hot pink trim, analog dials and speaker parts, '
STR = 'glossy seamless white tech aesthetic, soft blue ring lights, smooth rounded minimal design, '
SUF = ', isometric three-quarter view from above, single object centered, video game asset, plain flat grey background, no text'
ACTORS = [
('popupvan', 24, MRP + 'small delivery truck covered in band stickers and a roof-mounted loudspeaker'),
('popupshop', 64, MRP + 'record store shack built from shipping containers with neon OPEN sign and crane arm'),
('pressplant', 64, MRP + 'industrial hydraulic vinyl record pressing plant building with steam vents and conveyor of records'),
('ampstack', 32, MRP + 'wall of stacked guitar amplifiers crackling with pink energy'),
('vault', 32, MRP + 'squat bunker with a huge round bank vault door and stacked record crates'),
('merchtable', 32, MRP + 'folding merch table under a small tent with t-shirts and records'),
('garage', 64, MRP + 'corrugated iron vehicle workshop with engine hoist and band posters'),
('mastering', 64, MRP + 'pristine acoustic-panelled recording studio grafted onto a junk tower with satellite dish'),
('digger', 24, MRP + 'forklift monster robot with crate claw carrying milk crates of vinyl records'),
('spinner', 20, MRP + 'small agile one-wheeled robot whose wheel is a spinning vinyl record with blade tonearms'),
('stacktank', 28, MRP + 'two-legged walker mech made of stacked speaker cabinets with subwoofer chest'),
('basscannon', 26, MRP + 'six-wheeled flatbed truck hauling one colossal subwoofer cannon with fold-out legs'),
('djgrunt', 16, MRP + 'small hooded robot with a portable turntable chest rig holding a razor-sharp record'),
('roadie', 16, MRP + 'four-armed robot with gaffer tape bandoliers and a flight case backpack'),
('streamvan', 24, STR + 'autonomous white delivery van with a glowing blue play-button logo'),
('streamhub', 64, STR + 'white monolithic server hub building with one huge blue status ring'),
('datacentre', 32, STR + 'humming white server monolith with a single blue LED strip'),
('transcoder', 64, STR + 'white cube building that swallows records on a conveyor, receipts coming out'),
('podfarm', 32, STR + 'vending machine wall dispensing small white sphere robots'),
('printfarm', 64, STR + 'white 3d-printer factory building with robotic arms assembling drones'),
('scraper', 22, STR + 'autonomous white street sweeper vehicle inhaling black vinyl through a glowing blue intake'),
('bufferbot', 14, STR + 'knee-high white sphere robot with a loading spinner face'),
('drmwalker', 28, STR + 'tall faceless white mech with one blue ring-light eye and a glowing paywall screen chest'),
]
def fit(im, w, h):
im = im.crop(im.getbbox() or (0, 0, im.width, im.height))
im.thumbnail((w, h), Image.LANCZOS)
out = Image.new('RGBA', (w, h), (0, 0, 0, 0))
out.paste(im, ((w - im.width) // 2, (h - im.height) // 2))
return out
log = open(os.path.join(ROOT, 'assets_src', 'PROMPTS.md'), 'a')
today = datetime.date.today().isoformat()
only = sys.argv[1:]
for name, size, prompt in ACTORS:
if only and name not in only:
continue
raw = os.path.join(RENDERS, f'{name}_raw.png')
cut = os.path.join(RENDERS, f'{name}.png')
if not os.path.exists(cut):
run('flux_local', {'prompt': prompt + SUF}, raw)
run('bg_remove_local', {}, cut, asset_id=upload(raw))
log.write(f'| renders/{name}.png | {today} | {prompt + SUF} |\n')
log.flush()
im = Image.open(cut).convert('RGBA')
fit(im, size, size).save(os.path.join(ASSETS, f'{name}.png'))
fit(im, 56, 40).resize((56, 40)).save(os.path.join(ASSETS, f'_icon_tmp.png'))
ic = Image.new('RGBA', (64, 48), (18, 16, 20, 255))
ic.paste(Image.open(os.path.join(ASSETS, '_icon_tmp.png')), (4, 4))
ic.save(os.path.join(ASSETS, f'icon_{name}.png'))
print(f'[sprite] {name} done', flush=True)
os.remove(os.path.join(ASSETS, '_icon_tmp.png'))
print('ALL DONE')