#!/usr/bin/env python3 """Top-down vehicle renders -> 16-facing rotation sheets. flux renders each vehicle facing NORTH from directly above; PIL rotates into 16 frames (OpenRA facing order: north first, counter-clockwise), packed into one horizontal strip with FrameSize metadata. Geometrically correct, unlike rotating a 3/4-view sprite. Also: terrain tile + wax crystal density sheet. """ import os, sys sys.path.insert(0, os.path.dirname(__file__)) from mbgen import run, upload # noqa: E402 from PIL import Image # noqa: E402 from PIL.PngImagePlugin import PngInfo # noqa: E402 ROOT = os.path.join(os.path.dirname(__file__), '..') RENDERS = os.path.join(ROOT, 'assets_src', 'renders_topdown') 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, ' STR = 'glossy seamless white tech aesthetic, soft blue ring lights, ' SUF = ', viewed from directly above, top-down aerial view, vehicle pointing straight up toward the top of the image, single object centered, video game sprite, plain flat grey background, no text, no shadow' VEHICLES = [ ('digger', 26, MRP + 'forklift harvester truck carrying crates of vinyl records'), ('popupvan', 26, MRP + 'delivery truck covered in band stickers with roof loudspeaker'), ('spinner', 20, MRP + 'small round robot riding a single vinyl record wheel with blade arms'), ('stacktank', 30, MRP + 'heavy tracked tank made of stacked speaker cabinets with subwoofer turret'), ('basscannon', 30, MRP + 'long six-wheeled flatbed truck with one colossal subwoofer cannon'), ('streamvan', 26, STR + 'sleek white autonomous delivery van with blue play-button roof logo'), ('scraper', 24, STR + 'white autonomous street sweeper vehicle with glowing blue front intake'), ('drmwalker', 30, STR + 'white quad-legged mech with one blue ring light, seen from above'), ] def sheet16(im, size, out): im = im.crop(im.getbbox() or (0, 0, im.width, im.height)) im.thumbnail((size * 4, size * 4), Image.LANCZOS) # rotate at 4x, downscale after strip = Image.new('RGBA', (size * 16, size), (0, 0, 0, 0)) for i in range(16): fr = im.rotate(i * 22.5, expand=False, resample=Image.BICUBIC) fr.thumbnail((size, size), Image.LANCZOS) strip.paste(fr, (i * size + (size - fr.width) // 2, (size - fr.height) // 2), fr) meta = PngInfo() meta.add_text('FrameSize', f'{size},{size}') strip.save(out, pnginfo=meta) print(f'[facings] {out}', flush=True) for name, size, prompt in VEHICLES: 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)) sheet16(Image.open(cut).convert('RGBA'), size, os.path.join(ASSETS, f'{name}.png')) # terrain tile: seamless ground texture -> 32x32 traw = os.path.join(RENDERS, 'terrain_raw.png') if not os.path.exists(traw): run('flux_local', {'prompt': 'seamless tileable texture, cracked dark asphalt with faint dust and tiny speckles, muted grey brown, flat even lighting, top down, no objects'}, traw) t = Image.open(traw).convert('RGBA').resize((64, 64), Image.LANCZOS).crop((16, 16, 48, 48)) t.save(os.path.join(ROOT, 'mods', 'vinylgod', 'tilesets', 'vinylgod', 'vinylgod.png')) print('[terrain] tile written', flush=True) # wax crystals: one cluster render -> 12 density frames wraw = os.path.join(RENDERS, 'wax_raw.png') wcut = os.path.join(RENDERS, 'wax.png') if not os.path.exists(wcut): run('flux_local', {'prompt': 'cluster of jagged black vinyl crystal shards glowing hot pink from the cracks, viewed from directly above, top down, video game resource sprite, single cluster centered, plain flat grey background'}, wraw) run('bg_remove_local', {}, wcut, asset_id=upload(wraw)) w = Image.open(wcut).convert('RGBA') w = w.crop(w.getbbox() or (0, 0, w.width, w.height)) strip = Image.new('RGBA', (32 * 12, 32), (0, 0, 0, 0)) spots = [(16, 16), (7, 9), (24, 10), (9, 24), (24, 24), (16, 6), (5, 17), (27, 18), (16, 26), (11, 13), (21, 15), (16, 20)] for f in range(12): for i in range(f + 1): s = 10 + (6 if i == 0 else 0) c = w.copy() c.thumbnail((s, s), Image.LANCZOS) c = c.rotate(i * 47, expand=False) x, y = spots[i] strip.paste(c, (f * 32 + x - c.width // 2, y - c.height // 2), c) meta = PngInfo() meta.add_text('FrameSize', '32,32') strip.save(os.path.join(ASSETS, 'wax.png'), pnginfo=meta) print('[wax] density sheet written', flush=True) print('ALL DONE')