mirror of
https://github.com/DramaticShape/DramaticShapeVoxelMod.git
synced 2026-08-12 06:50:51 +02:00
113 lines
4.1 KiB
Python
113 lines
4.1 KiB
Python
"""Composite the reference PNGs that
|
|
assets/docs/buildings/B##-unnamed-building.md files link to (img/B##.png,
|
|
img/B##_x{scale}.png, img/B##_atlas.png) but the repo does not ship, since
|
|
they land in the gitignored .scratchpad/ -- run this to regenerate them
|
|
locally.
|
|
|
|
Parses each doc's raw tile-id grid (the `local rows = {...}` Lua array), its
|
|
named tileset, and its scale factor straight out of the markdown, then
|
|
composites the building out of the tileset atlas the same way
|
|
building_voxels.py's sprite() does: paste each tile's 8x8 block, 16 tiles
|
|
per row. Emits, per building:
|
|
- B##.png native resolution
|
|
- B##_x{N}.png nearest-neighbor upscale, at the scale the doc references
|
|
- B##_atlas.png a strip of the building's distinct tiles, first-appearance
|
|
order (matches the doc's legend lettering)
|
|
|
|
python mods/DRAMATIC_SHAPE/tools/building_images.py [outdir]
|
|
|
|
outdir defaults to .scratchpad/img at the repo root.
|
|
"""
|
|
import os
|
|
import re
|
|
import sys
|
|
import glob
|
|
from PIL import Image
|
|
|
|
MOD_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
ROOT = os.path.dirname(os.path.dirname(MOD_ROOT))
|
|
DOCS = os.path.join(MOD_ROOT, "assets", "docs", "buildings")
|
|
TILESETS = os.path.join(ROOT, "assets", "generated", "tilesets")
|
|
PER_ROW = 16
|
|
|
|
|
|
def parse_doc(path):
|
|
text = open(path, encoding="utf-8").read()
|
|
bid = re.search(r"^# (B\d+)", text, re.M).group(1)
|
|
scale = int(re.search(r"img/B\d+_x(\d+)\.png", text).group(1))
|
|
tileset = re.search(r"assets/generated/tilesets/(\w+)\.png", text).group(1)
|
|
lua = re.search(r"local rows = \{(.*?)\n\}", text, re.S).group(1)
|
|
rows = []
|
|
for line in lua.strip().splitlines():
|
|
code = line.split("--")[0]
|
|
nums = re.findall(r"\d+", code)
|
|
if nums:
|
|
rows.append([int(n) for n in nums])
|
|
return bid, scale, tileset, rows
|
|
|
|
|
|
def composite(rows, atlas):
|
|
h, w = len(rows), len(rows[0])
|
|
im = Image.new("RGB", (w * 8, h * 8))
|
|
for r, row in enumerate(rows):
|
|
for c, t in enumerate(row):
|
|
ax, ay = (t % PER_ROW) * 8, (t // PER_ROW) * 8
|
|
im.paste(atlas.crop((ax, ay, ax + 8, ay + 8)), (c * 8, r * 8))
|
|
return im
|
|
|
|
|
|
def atlas_strip(rows, atlas, cell=32, pad=2, per_row=12):
|
|
"""Every distinct tile the building uses, in first-appearance (row-major)
|
|
order -- the same order the doc's legend assigns A, B, C, ... to."""
|
|
seen, seenset = [], set()
|
|
for row in rows:
|
|
for t in row:
|
|
if t not in seenset:
|
|
seenset.add(t)
|
|
seen.append(t)
|
|
n = len(seen)
|
|
cols = min(n, per_row)
|
|
lines = (n + per_row - 1) // per_row
|
|
W = cols * cell + (cols + 1) * pad
|
|
H = lines * cell + (lines + 1) * pad
|
|
img = Image.new("RGB", (W, H), (255, 255, 255))
|
|
for i, t in enumerate(seen):
|
|
ax, ay = (t % PER_ROW) * 8, (t // PER_ROW) * 8
|
|
tile = atlas.crop((ax, ay, ax + 8, ay + 8)).resize((cell, cell), Image.NEAREST)
|
|
col, line = i % per_row, i // per_row
|
|
img.paste(tile, (pad + col * (cell + pad), pad + line * (cell + pad)))
|
|
return img
|
|
|
|
|
|
def main():
|
|
out = sys.argv[1] if len(sys.argv) > 1 else os.path.join(ROOT, ".scratchpad", "img")
|
|
os.makedirs(out, exist_ok=True)
|
|
|
|
atlas_cache = {}
|
|
docs = sorted(
|
|
glob.glob(os.path.join(DOCS, "B*-unnamed-building.md")),
|
|
key=lambda p: int(re.search(r"B(\d+)", os.path.basename(p)).group(1)),
|
|
)
|
|
for path in docs:
|
|
bid, scale, tileset, rows = parse_doc(path)
|
|
if tileset not in atlas_cache:
|
|
atlas_cache[tileset] = Image.open(
|
|
os.path.join(TILESETS, tileset + ".png")
|
|
).convert("RGB")
|
|
atlas = atlas_cache[tileset]
|
|
|
|
native = composite(rows, atlas)
|
|
native.save(os.path.join(out, f"{bid}.png"))
|
|
|
|
big = native.resize((native.width * scale, native.height * scale), Image.NEAREST)
|
|
big.save(os.path.join(out, f"{bid}_x{scale}.png"))
|
|
|
|
atl = atlas_strip(rows, atlas)
|
|
atl.save(os.path.join(out, f"{bid}_atlas.png"))
|
|
|
|
print(f"{bid}: {tileset} {native.size} -> x{scale} {big.size}, atlas {atl.size}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|