100 lines
3.6 KiB
Python
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()
|