136 lines
3.6 KiB
Python
136 lines
3.6 KiB
Python
|
|
from PIL import Image
|
|||
|
|
import csv
|
|||
|
|
import sys
|
|||
|
|
import os
|
|||
|
|
|
|||
|
|
TILE_W = 8
|
|||
|
|
TILE_H = 8
|
|||
|
|
TILES_PER_ROW = 24 # because 24 tiles * 8 px = 192 px rows in the map
|
|||
|
|
|
|||
|
|
|
|||
|
|
def load_tileset(path):
|
|||
|
|
tileset = Image.open(path).convert("RGBA")
|
|||
|
|
ts_w, ts_h = tileset.size
|
|||
|
|
|
|||
|
|
tiles_x = ts_w // TILE_W
|
|||
|
|
tiles_y = ts_h // TILE_H
|
|||
|
|
|
|||
|
|
tiles = []
|
|||
|
|
for ty in range(tiles_y):
|
|||
|
|
for tx in range(tiles_x):
|
|||
|
|
tile = tileset.crop((
|
|||
|
|
tx * TILE_W,
|
|||
|
|
ty * TILE_H,
|
|||
|
|
tx * TILE_W + TILE_W,
|
|||
|
|
ty * TILE_H + TILE_H
|
|||
|
|
))
|
|||
|
|
tiles.append(tile)
|
|||
|
|
|
|||
|
|
print(f"Tileset loaded: {len(tiles)} tiles ({tiles_x}×{tiles_y})")
|
|||
|
|
return tiles
|
|||
|
|
|
|||
|
|
|
|||
|
|
def find_tile_index(block, tiles):
|
|||
|
|
"""Return index of tile in tiles[] or raise error if not found."""
|
|||
|
|
for i, t in enumerate(tiles):
|
|||
|
|
if list(block.getdata()) == list(t.getdata()):
|
|||
|
|
print(f"Made {i}, {t}")
|
|||
|
|
return i
|
|||
|
|
raise ValueError("Tile not found in tileset!")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def build_csv_from_image(src_path, tileset_path, csv_out):
|
|||
|
|
img = Image.open(src_path).convert("RGBA")
|
|||
|
|
w, h = img.size
|
|||
|
|
|
|||
|
|
tiles = load_tileset(tileset_path)
|
|||
|
|
|
|||
|
|
# Must be aligned
|
|||
|
|
if w % TILE_W != 0 or h % TILE_H != 0:
|
|||
|
|
raise ValueError("Image dimensions must be multiples of 8.")
|
|||
|
|
|
|||
|
|
map_w = w // TILE_W
|
|||
|
|
map_h = h // TILE_H
|
|||
|
|
|
|||
|
|
print(f"Building CSV: {map_w}×{map_h} tiles")
|
|||
|
|
|
|||
|
|
rows = []
|
|||
|
|
tile_indices = []
|
|||
|
|
|
|||
|
|
# Scan image in 8×8 blocks
|
|||
|
|
for ty in range(map_h):
|
|||
|
|
row_tiles = []
|
|||
|
|
for tx in range(map_w):
|
|||
|
|
block = img.crop((
|
|||
|
|
tx * TILE_W,
|
|||
|
|
ty * TILE_H,
|
|||
|
|
tx * TILE_W + TILE_W,
|
|||
|
|
ty * TILE_H + TILE_H
|
|||
|
|
))
|
|||
|
|
|
|||
|
|
idx = find_tile_index(block, tiles)
|
|||
|
|
row_tiles.append(idx)
|
|||
|
|
tile_indices.append(row_tiles)
|
|||
|
|
|
|||
|
|
# Output CSV with line breaks after 24 tiles
|
|||
|
|
with open(csv_out, "w", newline="") as f:
|
|||
|
|
writer = csv.writer(f)
|
|||
|
|
for row in tile_indices:
|
|||
|
|
# 24-tile chunks
|
|||
|
|
for i in range(0, len(row), TILES_PER_ROW):
|
|||
|
|
writer.writerow(row[i:i + TILES_PER_ROW])
|
|||
|
|
|
|||
|
|
print(f"CSV written to {csv_out}")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def build_png_from_csv(csv_path, tileset_path, out_path):
|
|||
|
|
tiles = load_tileset(tileset_path)
|
|||
|
|
|
|||
|
|
# Read CSV
|
|||
|
|
tile_rows = []
|
|||
|
|
with open(csv_path, newline="") as f:
|
|||
|
|
reader = csv.reader(f)
|
|||
|
|
for row in reader:
|
|||
|
|
if row:
|
|||
|
|
tile_rows.append([int(v) for v in row])
|
|||
|
|
|
|||
|
|
height_blocks = len(tile_rows)
|
|||
|
|
width_blocks = len(tile_rows[0])
|
|||
|
|
|
|||
|
|
out_w = width_blocks * TILE_W
|
|||
|
|
out_h = height_blocks * TILE_H
|
|||
|
|
out_img = Image.new("RGBA", (out_w, out_h))
|
|||
|
|
|
|||
|
|
print(f"Reconstructing {out_w}×{out_h} image")
|
|||
|
|
|
|||
|
|
for ty, row in enumerate(tile_rows):
|
|||
|
|
for tx, tile_index in enumerate(row):
|
|||
|
|
tile = tiles[tile_index]
|
|||
|
|
out_img.paste(tile, (tx * TILE_W, ty * TILE_H))
|
|||
|
|
|
|||
|
|
out_img.save(out_path)
|
|||
|
|
print(f"Reconstructed PNG saved to {out_path}")
|
|||
|
|
|
|||
|
|
|
|||
|
|
# -----------------------------------------------------------------------
|
|||
|
|
# Command-line wrapper
|
|||
|
|
# -----------------------------------------------------------------------
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
if len(sys.argv) < 5:
|
|||
|
|
print("Usage:")
|
|||
|
|
print(" python build_tilemap.py makemap <tileset.png> <image.png> <out.csv>")
|
|||
|
|
print(" python build_tilemap.py makepng <tileset.png> <in.csv> <out.png>")
|
|||
|
|
sys.exit(1)
|
|||
|
|
|
|||
|
|
mode = sys.argv[1].lower()
|
|||
|
|
tile = sys.argv[2]
|
|||
|
|
a = sys.argv[3]
|
|||
|
|
b = sys.argv[4]
|
|||
|
|
|
|||
|
|
if mode == "makemap":
|
|||
|
|
build_csv_from_image(a, tile, b)
|
|||
|
|
elif mode == "makepng":
|
|||
|
|
build_png_from_csv(a, tile, b)
|
|||
|
|
else:
|
|||
|
|
print("Unknown mode.")
|