High-res tile textures: floor, claimed floor, earth, rock, gold, churning water and lava
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
69a6b13006
commit
8197b07ac5
33 files changed
+256
-7
No files matched your search
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Turn a generated texture into a seamless pixel-art tile sheet for the high-res atlas.
|
||||
|
||||
Usage:
|
||||
make_tile.py raw.png out_prefix [--size 128] [--colors 32] [--band 14]
|
||||
|
||||
The picture is scaled down to size x size (default 128: a 2x2 block of 64x64 tiles; the game
|
||||
lays the four quarters out by the tiles' positions, so the floor repeats only every two tiles),
|
||||
made seamless (near the edges it fades into a copy shifted by half the size, whose edges wrap
|
||||
around without a seam; `--band` pixels wide) and reduced to a palette without dithering.
|
||||
|
||||
Writes out_prefix-<size>.png and out_prefix-<size>-preview.png (3x3 copies scaled up 3x, to
|
||||
check the seams). Needs Pillow.
|
||||
"""
|
||||
import argparse
|
||||
|
||||
from PIL import Image, ImageChops
|
||||
|
||||
|
||||
def seamless(img, band):
|
||||
n = img.width
|
||||
shifted = ImageChops.offset(img, n // 2, n // 2)
|
||||
mask = Image.new("L", (n, n))
|
||||
for y in range(n):
|
||||
for x in range(n):
|
||||
# 0 at the edges (the shifted copy, continuous across the wrap), 255 inside
|
||||
d = min(x, n - 1 - x, y, n - 1 - y)
|
||||
mask.putpixel((x, y), int(255 * min(1.0, d / band)))
|
||||
return Image.composite(img, shifted, mask)
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("raw")
|
||||
ap.add_argument("out")
|
||||
ap.add_argument("--size", type=int, default=128)
|
||||
ap.add_argument("--colors", type=int, default=32)
|
||||
ap.add_argument("--band", type=int, default=14)
|
||||
a = ap.parse_args()
|
||||
|
||||
img = Image.open(a.raw).convert("RGB")
|
||||
s = min(img.size)
|
||||
img = img.crop(((img.width - s) // 2, (img.height - s) // 2, (img.width + s) // 2, (img.height + s) // 2))
|
||||
img = img.resize((a.size, a.size), Image.LANCZOS)
|
||||
img = seamless(img, a.band)
|
||||
img = img.quantize(colors=a.colors, method=Image.Quantize.MEDIANCUT, dither=Image.Dither.NONE).convert("RGB")
|
||||
img.save(f"{a.out}-{a.size}.png")
|
||||
prev = Image.new("RGB", (a.size * 3, a.size * 3))
|
||||
for k in range(9):
|
||||
prev.paste(img, ((k % 3) * a.size, (k // 3) * a.size))
|
||||
prev.resize((prev.width * 2, prev.height * 2), Image.NEAREST).save(f"{a.out}-{a.size}-preview.png")
|
||||
print(f"{a.out}-{a.size}.png")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user