657d8f16c6
Co-Authored-By: Codex <noreply@openai.com>
273 lines
10 KiB
Python
273 lines
10 KiB
Python
"""AirDef's deterministic model texture catalog and generated variants."""
|
|
import random
|
|
import zlib
|
|
|
|
from PIL import Image
|
|
import gen_tex as GT
|
|
|
|
# ---------------------------------------------------------------- текстуры
|
|
def clamp(v):
|
|
return max(0, min(255, int(v)))
|
|
|
|
|
|
def noise(base, amp, seed, size=16):
|
|
rnd = random.Random(seed)
|
|
im = Image.new("RGBA", (size, size))
|
|
for y in range(size):
|
|
for x in range(size):
|
|
d = rnd.randint(-amp, amp)
|
|
im.putpixel((x, y), tuple(clamp(c + d) for c in base) + (255,))
|
|
return im
|
|
|
|
|
|
def panels(base, seed, size=16, every=8):
|
|
im = noise(base, 4, seed, size)
|
|
line = tuple(clamp(c - 16) for c in base) + (255,)
|
|
lite = tuple(clamp(c + 10) for c in base) + (255,)
|
|
for i in range(size):
|
|
for k in range(0, size, every):
|
|
im.putpixel((i, k), line)
|
|
im.putpixel((k, i), line)
|
|
if k + 1 < size:
|
|
im.putpixel((i, k + 1), lite)
|
|
rnd = random.Random(seed + 99)
|
|
for _ in range(size // 2): # заклёпки
|
|
x, y = rnd.randrange(size), rnd.randrange(size)
|
|
im.putpixel((x, y), lite)
|
|
return im
|
|
|
|
|
|
def camo(colors, seed, size=32, blobs=7):
|
|
rnd = random.Random(seed)
|
|
im = noise(colors[0], 4, seed, size)
|
|
for col in colors[1:]:
|
|
for _ in range(blobs):
|
|
cx, cy = rnd.randrange(size), rnd.randrange(size)
|
|
rx, ry = rnd.randint(2, 6), rnd.randint(1, 4)
|
|
for y in range(cy - ry, cy + ry + 1):
|
|
for x in range(cx - rx, cx + rx + 1):
|
|
if ((x - cx) / rx) ** 2 + ((y - cy) / ry) ** 2 <= 1 + rnd.random() * 0.3:
|
|
im.putpixel((x % size, y % size), tuple(clamp(c + rnd.randint(-5, 5)) for c in col) + (255,))
|
|
return im
|
|
|
|
|
|
def stripes(base, line, step, seed, size=16):
|
|
im = noise(base, 3, seed, size)
|
|
for y in range(0, size, step):
|
|
for x in range(size):
|
|
im.putpixel((x, y), line + (255,))
|
|
return im
|
|
|
|
|
|
def gradient(top, bottom, size=16):
|
|
im = Image.new("RGBA", (size, size))
|
|
for y in range(size):
|
|
t = y / (size - 1)
|
|
c = tuple(clamp(top[i] + (bottom[i] - top[i]) * t) for i in range(3))
|
|
for x in range(size):
|
|
im.putpixel((x, y), c + (255,))
|
|
return im
|
|
|
|
|
|
def canister_front():
|
|
im = noise((92, 100, 72), 3, 11)
|
|
for cx in (4, 11):
|
|
for cy in (4, 11):
|
|
for y in range(16):
|
|
for x in range(16):
|
|
d = (x - cx + 0.5) ** 2 + (y - cy + 0.5) ** 2
|
|
if d <= 9:
|
|
im.putpixel((x, y), (36, 38, 30, 255))
|
|
elif d <= 12:
|
|
im.putpixel((x, y), (120, 124, 100, 255))
|
|
return im
|
|
|
|
|
|
def radar_face():
|
|
im = noise((70, 76, 56), 3, 17)
|
|
for y in range(1, 15):
|
|
for x in range(1, 15):
|
|
if (x + y) % 3 == 0:
|
|
im.putpixel((x, y), (54, 58, 44, 255))
|
|
return im
|
|
|
|
|
|
def glass():
|
|
im = noise((40, 58, 78), 5, 31)
|
|
for i in range(16):
|
|
im.putpixel((i, 15 - i), (130, 160, 180, 255))
|
|
if i < 15:
|
|
im.putpixel((i + 1, 15 - i), (100, 130, 150, 255))
|
|
return im
|
|
|
|
|
|
def missile():
|
|
im = noise((232, 232, 226), 3, 21)
|
|
for x in range(16):
|
|
for y in (3, 4):
|
|
im.putpixel((x, y), (40, 40, 40, 255))
|
|
return im
|
|
|
|
|
|
def target():
|
|
im = Image.new("RGBA", (16, 16))
|
|
for y in range(16):
|
|
for x in range(16):
|
|
d = abs(x - 7.5) + abs(y - 7.5)
|
|
im.putpixel((x, y), (255, clamp(40 + d * 12), 40, 255))
|
|
return im
|
|
|
|
|
|
def fire(seed):
|
|
rnd = random.Random(seed)
|
|
im = Image.new("RGBA", (16, 16))
|
|
for y in range(16):
|
|
for x in range(16):
|
|
t = rnd.random()
|
|
im.putpixel((x, y), (255, clamp(140 + 110 * t), clamp(30 + 120 * t * t), 255))
|
|
return im
|
|
|
|
|
|
def missile_ru():
|
|
im = noise((226, 228, 222), 3, 49)
|
|
for x in range(16):
|
|
im.putpixel((x, 5), (180, 40, 40, 255))
|
|
im.putpixel((x, 11), (40, 40, 40, 255))
|
|
return im
|
|
|
|
|
|
def tube_cap():
|
|
im = noise((80, 88, 60), 3, 50)
|
|
for y in range(16):
|
|
for x in range(16):
|
|
d = (x - 7.5) ** 2 + (y - 7.5) ** 2
|
|
if d <= 36:
|
|
im.putpixel((x, y), (44, 48, 36, 255))
|
|
elif d <= 48:
|
|
im.putpixel((x, y), (120, 128, 96, 255))
|
|
return im
|
|
|
|
|
|
def tubes6():
|
|
im = noise((82, 96, 60), 3, 51)
|
|
for cx in (3, 8, 13):
|
|
for cy in (4, 11):
|
|
for y in range(16):
|
|
for x in range(16):
|
|
if (x - cx) ** 2 + (y - cy) ** 2 <= 4:
|
|
im.putpixel((x, y), (30, 32, 26, 255))
|
|
return im
|
|
|
|
|
|
def radar_big():
|
|
im = noise((92, 100, 70), 3, 52)
|
|
for y in range(16):
|
|
for x in range(16):
|
|
if x % 2 == 0 and y % 2 == 0:
|
|
im.putpixel((x, y), (60, 66, 46, 255))
|
|
return im
|
|
|
|
|
|
TEXTURES = {
|
|
"shahed": lambda: panels((66, 68, 70), 1),
|
|
"shahed_nose": lambda: noise((104, 106, 108), 4, 2),
|
|
"geran": lambda: panels((28, 28, 31), 3),
|
|
"geran_engine": lambda: panels((60, 60, 64), 4),
|
|
"intake": lambda: noise((14, 14, 14), 2, 5),
|
|
"prop": lambda: noise((22, 22, 22), 3, 6),
|
|
"missile": missile,
|
|
"nozzle": lambda: noise((44, 42, 40), 4, 7),
|
|
"flame": lambda: gradient((255, 250, 210), (255, 120, 20)),
|
|
"fire": lambda: fire(8),
|
|
"nato_green": lambda: camo([(76, 86, 58), (58, 68, 44), (44, 48, 34)], 9),
|
|
"flecktarn": lambda: camo([(92, 100, 62), (62, 74, 44), (104, 84, 54), (30, 32, 26)], 10),
|
|
"track": lambda: stripes((36, 36, 34), (74, 74, 70), 3, 11),
|
|
"tire": lambda: stripes((24, 24, 24), (40, 40, 40), 4, 12),
|
|
"canister": lambda: panels((94, 102, 74), 13),
|
|
"canister_front": canister_front,
|
|
"radar_face": radar_face,
|
|
"glass": glass,
|
|
"metal": lambda: panels((118, 120, 122), 14),
|
|
"dark_metal": lambda: panels((52, 54, 52), 15),
|
|
"barrel": lambda: noise((40, 42, 38), 3, 16),
|
|
"dish": lambda: noise((176, 178, 174), 5, 18),
|
|
"target": target,
|
|
"foam": lambda: panels((196, 180, 150), 40),
|
|
"kh101": lambda: panels((84, 88, 92), 41),
|
|
"kh101_belly": lambda: noise((150, 152, 150), 4, 42),
|
|
"lancet": lambda: panels((70, 78, 66), 43),
|
|
"ru_green": lambda: camo([(82, 96, 60), (54, 70, 40), (110, 96, 64), (34, 36, 28)], 44, blobs=6),
|
|
"ru_khaki": lambda: camo([(108, 110, 80), (84, 90, 60), (60, 64, 44)], 45, blobs=5),
|
|
"missile_ru": missile_ru,
|
|
"missile_grey": lambda: panels((150, 154, 156), 46),
|
|
"booster": lambda: noise((60, 62, 58), 3, 47),
|
|
"tube": lambda: stripes((96, 106, 74), (70, 78, 54), 4, 48),
|
|
"tube_cap": tube_cap,
|
|
"tubes6": tubes6,
|
|
"radar_big": radar_big,
|
|
"tracer": lambda: noise((255, 40, 20), 6, 60),
|
|
"tracer_hot": lambda: noise((255, 190, 150), 6, 61),
|
|
"white": lambda: noise((250, 250, 246), 2, 19),
|
|
"nato_tan": lambda: camo([(150, 130, 96), (124, 106, 76), (96, 84, 62)], 62, blobs=5),
|
|
"crate": lambda: stripes((92, 78, 48), (70, 58, 34), 5, 63),
|
|
"concrete": lambda: panels((132, 130, 124), 64),
|
|
"lamp": lambda: gradient((255, 255, 240), (255, 230, 170)),
|
|
"beam": lambda: gradient((255, 250, 225), (255, 240, 200)),
|
|
"antenna": lambda: noise((70, 72, 70), 3, 65),
|
|
"nose_dark": lambda: noise((58, 60, 62), 3, 66),
|
|
}
|
|
|
|
|
|
RINGED = set()
|
|
|
|
|
|
def _ring(name, base, bands, seed, text=None):
|
|
RINGED.add(name)
|
|
TEXTURES[name] = lambda: GT.band_tex(base, bands, seed, text=text)
|
|
TEXTURES[name + "_t"] = lambda: GT.band_tex(base, bands, seed, transpose=True, text=text)
|
|
|
|
|
|
STRETCH_FN = {}
|
|
for _i, _k in enumerate(("nato_green", "flecktarn", "ru_green", "ru_khaki", "nato_tan")):
|
|
STRETCH_FN[_k] = (lambda sx, sy, k=_k, i=_i: GT.camo(k, 100 + i, 64, sx, sy))
|
|
TEXTURES[_k] = (lambda k=_k, i=_i: GT.camo(k, 100 + i, 64))
|
|
STRETCH_FN["track_side"] = lambda sx, sy: GT.track_side(7, 32, sx, sy)
|
|
PAL_RU, PAL_RU2 = GT.PAL["ru_green"][0], GT.PAL["ru_khaki"][0]
|
|
TEXTURES.update({
|
|
"track_side": lambda: GT.track_side(7),
|
|
"track": lambda: GT.track_tex(8),
|
|
"tire": lambda: GT.tire_tex(9),
|
|
"hub": lambda: GT.hub_tex(10),
|
|
"glass_cab": lambda: GT.glass_tex(11),
|
|
"bare_lamp": lambda: GT.glass_tex(12, tint=(200, 205, 190), refl=False),
|
|
"bare_barrel": lambda: GT.metal_tex((44, 46, 44), 13),
|
|
"bare_nozzle": lambda: GT.metal_tex((66, 58, 52), 14),
|
|
"bare_dish": lambda: GT.metal_tex((178, 180, 176), 15),
|
|
"bare_antenna": lambda: GT.metal_tex((112, 114, 112), 16),
|
|
"canister_front": lambda: GT.cap_tex(28),
|
|
"mk_ru": lambda: GT.marking("digits", PAL_RU, 40, "312"),
|
|
"mk_ru2": lambda: GT.marking("digits", PAL_RU2, 41, "507"),
|
|
"mk_us": lambda: GT.marking("digits", (76, 86, 58), 42, "1-7"),
|
|
"mk_star": lambda: GT.marking("star", PAL_RU, 43),
|
|
"mk_de": lambda: GT.marking("cross", (88, 98, 62), 44),
|
|
"glow_ember": lambda: GT.Canvas(16, 1, 1, (255, 170, 80), 17).out(),
|
|
"metal": lambda: GT.painted((118, 120, 122), 18, lift=1.15),
|
|
"dark_metal": lambda: GT.painted((52, 54, 52), 19, lift=1.1),
|
|
"canister": lambda: GT.painted((92, 102, 74), 20),
|
|
"shahed": lambda: GT.painted((92, 96, 98), 21, panel=(20, 10), lift=1.2),
|
|
"geran": lambda: GT.painted((28, 28, 31), 22, panel=(20, 10), lift=1.0),
|
|
"kh101": lambda: GT.painted((84, 88, 92), 23, panel=(18, 10)),
|
|
"lancet": lambda: GT.painted((74, 82, 70), 24),
|
|
"missile_grey": lambda: GT.painted((150, 154, 156), 25, lift=1.0),
|
|
"radar_face": lambda: GT.lattice(26, 32, (96, 104, 76), (170, 176, 150)),
|
|
"radar_big": lambda: GT.lattice(27, 32, (112, 124, 92), (190, 196, 168)),
|
|
})
|
|
_ring("rm_pac3", (228, 228, 222), [(0.12, 2, (30, 30, 30)), (0.78, 1.5, (200, 170, 40)), (0.9, 1, (30, 30, 30))], 30, [(8, 14, 10)])
|
|
_ring("rm_white", (232, 232, 226), [(0.1, 2, (200, 40, 36)), (0.82, 1.5, (40, 40, 44))], 31, [(10, 12, 8)])
|
|
_ring("rm_ru", (226, 228, 222), [(0.15, 2, (170, 40, 36)), (0.55, 1, (40, 40, 40)), (0.85, 2, (170, 40, 36))], 32, [(9, 20, 10)])
|
|
_ring("rm_grey", (148, 152, 154), [(0.2, 1.5, (210, 210, 205)), (0.8, 1.5, (210, 210, 205))], 33, [(6, 14, 12)])
|
|
_ring("rm_khaki", (110, 112, 84), [(0.25, 2, (60, 62, 48)), (0.7, 1.5, (200, 190, 150))], 34, [(8, 18, 8)])
|
|
_ring("rm_dark", (58, 60, 62), [(0.3, 1.5, (150, 150, 146)), (0.85, 1.5, (150, 150, 146))], 35)
|
|
|
|
|