Files
openttd-cmclient/grf/alpine/gen_sprites.py
2022-12-20 19:46:24 +04:00

359 lines
13 KiB
Python

from PIL import Image, ImageDraw
import numpy as np
import math
import os
import random
import spectra
import grf
SAFE_COLORS = list(range(1, 0xD7))
f = open("../ttd-newgrf-dos.gpl")
while f.readline().strip() != "#":
pass
colors = []
for _ in range(256):
try:
r, g, b, _, i = f.readline().split()
except ValueError:
break
c = spectra.rgb(float(r) / 255., float(g) / 255., float(b) / 255.)
# if c in SAFE_COLORS:
colors.append((int(i), c))
def color_distance(c1, c2):
rmean = (c1.rgb[0] + c2.rgb[0]) / 2.
r = c1.rgb[0] - c2.rgb[0]
g = c1.rgb[1] - c2.rgb[1]
b = c1.rgb[2] - c2.rgb[2]
return math.sqrt(
((2 + rmean) * r * r) +
4 * g * g +
(3 - rmean) * b * b)
def find_best_color(x):
mj, md = 0, 1e100
for j, c in colors:
if j not in SAFE_COLORS:
continue
d = color_distance(x, c)
if d < md:
mj, md = j, d
return mj
def gen_recolor(color_func):
out = np.arange(256, dtype=np.uint8)
for i, c in colors:
if i not in SAFE_COLORS:
continue
out[i] = find_best_color(color_func(c))
return out
def gen_tint(tint, ratio):
return lambda x: find_best_color(x.blend(tint, ratio=ratio))
def gen_brightness(level):
def func(x):
if level > 0:
return x.brighten(amount=2.56 * level)
else:
return x.darken(amount=-2.56 * level)
return gen_recolor(func)
# def gen_land_recolor():
# def func(x):
# if 2 * g > r + b:
# x = x.blend(spectra.rgb(0.7, 1, 0), ratio=0.2)
# else:
# x = x.saturate(20)
# return x
# return gen_recolor(func)
def gen_land_recolor():
def func(x):
r, g, b = x.rgb
if 2 * g > r + b:
x = x.blend(spectra.rgb(0.7, 1, 0), ratio=0.05)
x = x.saturate(10)
# elif 3 * b > r + g:
# x = x.blend(spectra.rgb(0, 0, 1), ratio=0.3)
# x = x.blend(spectra.rgb(1, 0, 1), ratio=0.5)
# x = x.saturate(40)
else:
x = x.blend(spectra.rgb(0.7, 1, 0), ratio=0.05)
x = x.saturate(5)
return x
return gen_recolor(func)
def gen_land_recolor2():
def func(x):
r, g, b = x.rgb
if 3 * g / 2 > r + b:
x = x.blend(spectra.rgb(0.7, 1, 0), ratio=0.05)
x = x.saturate(10)
return x
return gen_recolor(func)
def remap_file(f_in, f_out, palmap):
print(f"Converting {f_out}...")
im = grf.open_image(f_in)
data = np.array(im)
data = palmap[data]
im2 = Image.fromarray(data)
im2.putpalette(im.getpalette())
im2.save(f_out)
DEST_DIR = "gfx"
SOURCE_DIR = "/home/pavels/Builds/OpenGFX/sprites/png"
land_palmap = gen_land_recolor()
for fname in ("miscellaneous/hq.png",
):
remap_file(os.path.join(SOURCE_DIR, fname), os.path.join(DEST_DIR, fname), land_palmap)
SOURCE_DIR = "/home/pavels/Projects/cmclient/local/ogfx-landscape-1.1.2-source/src/gfx"
land_palmap = gen_land_recolor()
for fname in ("grass_grid_temperate.gimp.png",
"bare03_grid.gimp.png",
"bare13_grid_temperate.gimp.png",
"bare23_grid_temperate.gimp.png",
"rough_grid_temperate.gimp.png",
"rough_grid_temperate.gimp.png",
"rocks_grid_temperate.gimp.png",
"snow14_grid_alpine.gimp.png",
"snow24_grid_alpine.gimp.png",
"snow34_grid_alpine.gimp.png",
"snow_grid.gimp.png",
"water/seashore_grid_temperate.gimp.png",
"infrastructure/tunnel_rail_grid_temperate.gimp.png",
"infrastructure_road_tunnel_grid.png",
):
remap_file(os.path.join(SOURCE_DIR, fname), os.path.join(DEST_DIR, fname), land_palmap)
land_palmap = gen_land_recolor2()
for fname in ("infrastructure_road_tunnel_grid.png",
"infrastructure/road_grid_temperate.gimp.png",):
remap_file(os.path.join(SOURCE_DIR, fname), os.path.join(DEST_DIR, fname), land_palmap)
def meadow_recolor(x):
x = x.blend(spectra.rgb(0.7, 1, 0), ratio=0.2)
return x.blend(spectra.rgb(1, 1, 0), ratio=0.4)
def half_meadow_recolor(x):
x = x.blend(spectra.rgb(0.7, 1, 0), ratio=0.2)
return x.blend(spectra.rgb(1, 1, 0), ratio=0.2)
# remap_file(os.path.join(SOURCE_DIR, "grass_grid_temperate.gimp.png"), os.path.join(DEST_DIR, "meadow_grid_temperate.png"), gen_recolor(meadow_recolor))
# remap_file(os.path.join(SOURCE_DIR, "grass_grid_temperate.gimp.png"), os.path.join(DEST_DIR, "half_meadow_grid_temperate.png"), gen_recolor(half_meadow_recolor))
# Generate snow transition tiles
grass = grf.open_image(os.path.join(SOURCE_DIR, "grass_grid_temperate.gimp.png"))
snow = grf.open_image(os.path.join(SOURCE_DIR, "snow_grid.gimp.png"))
grass_data = land_palmap[np.array(grass)]
snow_data = land_palmap[np.array(snow)]
for i in range(1, 4):
ratio = [0, 0.15, 0.35, 0.7][i]
grass_desaturate = [0, 10, 30, 15][i]
grass_blend = [0, 0.1, 0.2, 0.2][i]
print(f'Generating snow transitions {i}/3 ... ', end='')
data = np.array(grass)
cache = {(0, 0): 0}
for y in range(grass.height):
for x in range(grass.width):
cache_key = grass_index, snow_index = snow_data[y, x], grass_data[y, x]
if cache_key in cache:
data[y, x] = cache[cache_key]
continue
snow_colour = grf.SPECTRA_PALETTE[snow_index]
grass_colour = grf.SPECTRA_PALETTE[grass_index]
grass_colour = grass_colour.blend(spectra.rgb(0.7, 0.7, 0), ratio=grass_blend)
grass_colour = grass_colour.desaturate(grass_desaturate)
data[y, x] = cache[cache_key] = grf.find_best_color(snow_colour.blend(grass_colour, ratio=ratio))
im = Image.fromarray(data)
im.putpalette(grf.PALETTE)
im.save(os.path.join(DEST_DIR, f'snow_transition_{i}.png'))
print('Done')
# Generate meadow transition tiles
im = Image.open(os.path.join(SOURCE_DIR, "grass_grid_temperate.gimp.png"))
din = np.array(im)
GROUND_SPRITES = [
# N E S W STEEP
[ 0 + 1, 1, 64, 31, -31, 0, 15, 15], #
[ 80 + 1, 1, 64, 31, -31, 0, 7, 15], # W
[ 160 + 1, 1, 64, 23, -31, 0, 15, 15], # S
[ 240 + 1, 1, 64, 23, -31, 0, 7, 15], # S W
[ 320 + 1, 1, 64, 31, -31, 0, 15, 7], # E
[ 398 + 1, 1, 64, 31, -31, 0, 7, 7], # E W
[ 478 + 1, 1, 64, 23, -31, 0, 15, 7], # E S
[ 558 + 1, 1, 64, 23, -31, 0, 7, 7], # E S W
[ 638 + 1, 1, 64, 39, -31, -8, 23, 23], # N
[ 718 + 1, 1, 64, 39, -31, -8, 15, 23], # N W
[ 798 + 1, 1, 64, 31, -31, -8, 23, 23], # N S
[ 878 + 1, 1, 64, 31, -31, -8, 15, 23], # N S W
[ 958 + 1, 1, 64, 39, -31, -8, 23, 15], # N E
[1038 + 1, 1, 64, 39, -31, -8, 15, 15], # N E W
[1118 + 1, 1, 64, 31, -31, -8, 23, 15], # N E S
[1196 + 1, 1, 64, 47, -31,-16, 23, 23], # N E W STEEP
[1276 + 1, 1, 64, 15, -31, 0, 7, 7], # E S W STEEP
[1356 + 1, 1, 64, 31, -31, -8, 7, 23], # N S W STEEP
[1436 + 1, 1, 64, 31, -31, -8, 23, 7], # N E S STEEP
]
# dout = np.zeros((64 * 8, 31 + 47 + 15), dtype=np.uint8)
# dout = np.zeros((31 + 47 + 15, 64 * 8), dtype=np.uint8)
# dout = np.zeros((64 * 16, im.width), dtype=np.uint8)
# for i in range (16):
# print(f'Generating sprite row {i}/16...')
# for j, (ox, oy, w, h, _ox, _oy, h1, h2) in enumerate(GROUND_SPRITES):
# hn1 = 2. * (h1 + .5 - h / 2.) / h
# hn2 = 2. * (h2 + .5 - h / 2.) / h
# iflags = [i & (1 << k) for k in range(4)]
# ut, uc, ub = i & 1, (i & 2) / 2, (i & 4) / 4
# for y in range(0, h):
# for x in range(0, w):
# c = din[y + oy, x + ox]
# if not c: continue
# xn = 2. * (x + .5 - w / 2.) / w
# yn = 2. * (y + .5 - h / 2.) / h
# dn = math.hypot(xn, yn + 1)
# ds = math.hypot(xn, yn - 1)
# de = math.hypot(xn - 1, yn - hn1)
# dw = math.hypot(xn + 1, yn - hn2)
# f = min(d if fl else 2. for d, fl in zip([dn, de, ds, dw], iflags))
# f = min(f * 1.44, 1.)
# bc = spectra.rgb(f, 1, 0)
# c = colors[c][1].blend(bc, ratio=0.2 + 0.1 * f)
# dout[oy + y + 64 * i, ox + x] = find_best_color(c)
# im2 = Image.fromarray(dout)
# im2.putpalette(im.getpalette())
# im2.save(os.path.join(DEST_DIR, "meadow_transitions.png"))
dmask = np.vectorize(lambda x: 0 if x and x != 0xFF else 0xFF)(din).astype('uint8')
immask = Image.fromarray(dmask, mode="L")
# immask.save(os.path.join(DEST_DIR, "mask.png"))
dout = np.zeros((64 * 81, im.width), dtype=np.uint8)
im2 = Image.fromarray(dout)
im2.putpalette(im.getpalette())
imd2 = ImageDraw.Draw(im2)
# draw.line((0, 0) + im.size, fill=128)
# draw.line((0, im.size[1], im.size[0], 0), fill=128)
def draw_bezier(imd, fill, width, a, b, c):
N, M = 11, 2
lerp = lambda x, y, t: t * x + (N - t) * y
lerp2m = lambda a, b, t: (lerp(a[0], (a[0] + b[0]) // 2, t), lerp(a[1], (a[1] + b[1]) // 2, t))
lerp2 = lambda a, b, t: (lerp(a[0], b[0], t), lerp(a[1], b[1], t))
for t in range(-M, N + M + 1):
p0 = lerp2m(a, b, t)
p1 = lerp2m(c, b, N - t)
pf = lerp2(p0, p1, t)
x = pf[0] // N // N
y = pf[1] // N // N
imd.ellipse((x - width, y - width, x + width, y + width), fill=fill)
for i in range (81):
print(f'Generating rivers sprite row {i + 1}/81...')
points = (i % 3, (i // 3) % 3, (i // 9) % 3, (i // 27))
inp = []
outp = []
for ii, p in enumerate(points):
if p == 2: outp.append(ii)
elif p == 1: inp.append(ii)
if not inp: inp = [4]
if not outp: outp = [4]
for j, (ox, oy, w, h, _ox, _oy, h1, h2) in enumerate(GROUND_SPRITES):
xx, yy = ox, oy + 64 * i
# # tile outline
# corners = ((0, h1), (w / 2, 0), (w, h2), (w / 2, h))
# for ii in range(len(corners)):
# x1, y1 = corners[ii]
# x2, y2 = corners[(ii + 1) % len(corners)]
# imd2.line(((x1 + xx, y1 + yy), (x2 + xx, y2 + yy)), fill=0xA9, width=1)
# hn1 = 2. * (h1 + .5 - h / 2.) / h
# hn2 = 2. * (h2 + .5 - h / 2.) / h
wc = w // 4
edges = ((wc, h1 / 2), (w - wc, h2 / 2), (w - wc, (h + h2) / 2), (wc, (h + h1) / 2), (w / 2, h / 2))
center = (w / 2 + xx, h / 2 + yy)
# if not inp:
# continue
for ii in inp:
for oo in outp:
xy = ((edges[ii][0] + xx, edges[ii][1] + yy), (edges[oo][0] + xx, edges[oo][1] + yy))
draw_bezier(imd2, 0x38, 5, xy[0], center, xy[1])
for ii in inp:
for oo in outp:
xy = ((edges[ii][0] + xx, edges[ii][1] + yy), (edges[oo][0] + xx, edges[oo][1] + yy))
draw_bezier(imd2, 0x42, 4, xy[0], center, xy[1])
for ii in inp:
for oo in outp:
xy = ((edges[ii][0] + xx, edges[ii][1] + yy), (edges[oo][0] + xx, edges[oo][1] + yy))
draw_bezier(imd2, 0xF5, 3, xy[0], center, xy[1])
# # mark out
# for oo in outp:
# x, y = edges[oo][0] + xx, edges[oo][1] + yy
# width = 3
# imd2.ellipse((x - width, y - width, x + width, y + width), fill=0x42)
imd2.bitmap((0, i * 64), immask, fill=0)
# iflags = [i & (1 << k) for k in range(4)]
# ut, uc, ub = i & 1, (i & 2) / 2, (i & 4) / 4
# for y in range(0, h):
# for x in range(0, w):
# c = din[y + oy, x + ox]
# if not c: continue
# xn = 2. * (x + .5 - w / 2.) / w
# yn = 2. * (y + .5 - h / 2.) / h
# dn = math.hypot(xn, yn + 1)
# ds = math.hypot(xn, yn - 1)
# de = math.hypot(xn - 1, yn - hn1)
# dw = math.hypot(xn + 1, yn - hn2)
# f = min(d if fl else 2. for d, fl in zip([dn, de, ds, dw], iflags))
# f = min(f * 1.44, 1.)
# bc = spectra.rgb(f, 1, 0)
# c = colors[c][1].blend(bc, ratio=0.2 + 0.1 * f)
# dout[oy + y + 64 * i, ox + x] = find_best_color(c)
# im2 = Image.fromarray(dout)
# im2.putpalette(im.getpalette())
#
px = im2.load()
for y in range(im2.height):
for x in range(im2.width):
if px[x, y] == 0xF5:
px[x, y] = random.randint(0xF5, 0xF9)
elif px[x, y] == 0x42:
px[x, y] = random.randint(0x10, 0x14)
elif px[x, y] == 0x38:
px[x, y] = random.randint(0x19, 0x1e)
im2.save(os.path.join(DEST_DIR, "rivers.png"))