Mi2dRPGamEng/scripts/export_blank_textures.py

100 lines
3.6 KiB
Python

#!/usr/bin/env python3
"""Generates a blank, correctly-sized, labeled PNG for every game-art texture slot that hasn't
been staged for an artist yet - see reimport_textures.py for bringing finished art back in.
Only covers actual hand-drawn game art (tile layers, items, body parts) - not the
auto-generated UI font/panel textures (engine/ui_assets.py), which regenerate from code and
aren't meant to be hand-painted.
Existing files in the output directory are left untouched, so re-running this after an artist
has started replacing some of them won't blank out their work - it only fills in the gaps, the
same "only write if missing" idiom engine/placeholder_textures.py already uses.
Usage:
python scripts/export_blank_textures.py [--out art_wip]
"""
from __future__ import annotations
import argparse
from pathlib import Path
from typing import Iterator
from PIL import Image, ImageDraw, ImageFont
from engine.defs import DefRegistry
from engine.placeholder_textures import TEXTURE_SIZE
ROOT_DIR = Path(__file__).resolve().parent.parent
DEFS_DIR = ROOT_DIR / "resources" / "defs"
DEFAULT_OUT_DIR = ROOT_DIR / "art_wip"
LABEL_FONT_SIZE = 7
LABEL_COLOR = (130, 130, 130, 220)
_label_font = None
def _font():
global _label_font
if _label_font is None:
_label_font = ImageFont.load_default(size=LABEL_FONT_SIZE)
return _label_font
def _blank_labeled_image(label: str, size: int) -> Image.Image:
"""A fully transparent canvas with just an identifying label drawn on it - a tile artist
filling the whole square in opaque paint will naturally cover it; an icon artist working on
a transparent background will still see it as a guide (and can paint over/around it).
"""
img = Image.new("RGBA", (size, size), (0, 0, 0, 0))
ImageDraw.Draw(img).text((1, 1), label, font=_font(), fill=LABEL_COLOR)
return img
def _texture_slots(registry: DefRegistry) -> Iterator[tuple[str, str, int]]:
"""Yields (relative_path, label, size) for every def with a texture path - the same set
engine/placeholder_textures.py::generate_placeholder_textures covers.
"""
for layer_def in registry.tile_layer_defs.values():
if layer_def.texture:
yield layer_def.texture, layer_def.id, TEXTURE_SIZE
for item_def in registry.item_defs.values():
if item_def.texture:
yield item_def.texture, item_def.id, TEXTURE_SIZE
for part_def in registry.body_part_defs.values():
if part_def.texture:
yield part_def.texture, part_def.id, TEXTURE_SIZE
def export_blank_textures(registry: DefRegistry, out_dir: Path) -> tuple[int, int]:
"""Writes a blank labeled PNG for every texture slot not already present in out_dir.
Returns (written_count, skipped_count).
"""
written = skipped = 0
for relative_path, label, size in _texture_slots(registry):
path = out_dir / relative_path
if path.exists():
skipped += 1
continue
path.parent.mkdir(parents=True, exist_ok=True)
_blank_labeled_image(label, size).save(path)
written += 1
return written, skipped
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--out", type=Path, default=DEFAULT_OUT_DIR, help=f"staging directory for artists (default: {DEFAULT_OUT_DIR})"
)
args = parser.parse_args()
registry = DefRegistry.load(DEFS_DIR)
written, skipped = export_blank_textures(registry, args.out)
print(f"Wrote {written} blank texture template(s) to {args.out}")
if skipped:
print(f"Skipped {skipped} file(s) already staged there (not overwritten).")
if __name__ == "__main__":
main()