You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
214 lines
5.6 KiB
214 lines
5.6 KiB
import random |
|
import numpy as np |
|
from PIL import Image |
|
|
|
# 0: Floor |
|
# 1: Highlight |
|
# 2: Midtone |
|
# 3: Shadow |
|
|
|
random.seed(1234) # deterministic |
|
|
|
# Base seamless vegetation pattern (8x8) |
|
base_veg = [ |
|
[2, 1, 2, 2, 3, 2, 2, 1], |
|
[1, 2, 2, 1, 2, 2, 3, 2], |
|
[2, 2, 3, 2, 2, 1, 2, 2], |
|
[3, 2, 2, 1, 2, 2, 2, 3], |
|
[2, 1, 2, 2, 3, 2, 2, 2], |
|
[2, 2, 2, 3, 2, 1, 2, 2], |
|
[2, 3, 2, 2, 2, 2, 1, 2], |
|
[1, 2, 2, 1, 2, 3, 2, 2] |
|
] |
|
base_veg = np.array(base_veg, dtype=np.uint8) |
|
|
|
# To avoid repeating exactly the same pattern, we can roll it slightly per mask |
|
# Or we just use it. |
|
walls = [] |
|
|
|
def make_bush(n, e, s, w, mask_idx): |
|
# Start with base vegetation |
|
t = np.roll(np.roll(base_veg, mask_idx % 8, axis=0), (mask_idx // 2) % 8, axis=1).copy() |
|
|
|
# Carve N |
|
if not n: |
|
# Jagged top edge: |
|
# col 0..7 randomly floor or highlight |
|
for c in range(8): |
|
depth = random.choice([0, 1]) |
|
for r in range(depth): |
|
t[r, c] = 0 |
|
t[depth, c] = 1 # highlight on the leaf tip |
|
t[depth+1, c] = 1 # more highlight |
|
|
|
# Carve S |
|
if not s: |
|
for c in range(8): |
|
depth = random.choice([0, 1]) |
|
for r in range(8 - depth, 8): |
|
t[r, c] = 0 |
|
t[7 - depth, c] = 3 # shadow on the bottom leaf |
|
t[6 - depth, c] = 3 |
|
|
|
# Carve W |
|
if not w: |
|
for r in range(8): |
|
depth = random.choice([0, 1]) |
|
for c in range(depth): |
|
t[r, c] = 0 |
|
# Ensure the edge isn't floor, make it a midtone/shadow |
|
if t[r, depth] == 0: t[r, depth] = 2 |
|
|
|
# Carve E |
|
if not e: |
|
for r in range(8): |
|
depth = random.choice([0, 1]) |
|
for c in range(8 - depth, 8): |
|
t[r, c] = 0 |
|
if t[r, 7 - depth] == 0: t[r, 7 - depth] = 2 |
|
|
|
# Fix Corners if isolated |
|
if not n and not w: |
|
t[0, 0] = t[0, 1] = t[1, 0] = 0 |
|
t[1, 1] = 1 |
|
if not n and not e: |
|
t[0, 7] = t[0, 6] = t[1, 7] = 0 |
|
t[1, 6] = 1 |
|
if not s and not w: |
|
t[7, 0] = t[7, 1] = t[6, 0] = 0 |
|
t[6, 1] = 3 |
|
if not s and not e: |
|
t[7, 7] = t[7, 6] = t[6, 7] = 0 |
|
t[6, 6] = 3 |
|
|
|
return t |
|
|
|
for mask in range(16): |
|
n = (mask & 8) != 0 |
|
e = (mask & 4) != 0 |
|
s = (mask & 2) != 0 |
|
w = (mask & 1) != 0 |
|
walls.append(make_bush(n, e, s, w, mask)) |
|
|
|
floors = [ |
|
# 0: Clean floor |
|
np.array([ |
|
[0,0,0,0,0,0,0,0], |
|
[0,0,0,0,0,0,0,0], |
|
[0,0,0,0,0,0,0,0], |
|
[0,0,0,0,0,0,0,0], |
|
[0,0,0,0,0,0,0,0], |
|
[0,0,0,0,0,0,0,0], |
|
[0,0,0,0,0,0,0,0], |
|
[0,0,0,0,0,0,0,0] |
|
], dtype=np.uint8), |
|
# 1: Tiny pebble/crack |
|
np.array([ |
|
[0,0,0,0,0,0,0,0], |
|
[0,0,1,0,0,0,0,0], |
|
[0,0,0,0,0,0,0,0], |
|
[0,0,0,0,0,0,0,0], |
|
[0,0,0,0,0,0,0,0], |
|
[0,0,0,0,0,0,0,1], |
|
[0,0,0,0,0,0,0,0], |
|
[0,0,0,0,0,0,0,0] |
|
], dtype=np.uint8), |
|
# 2: Subtle horizontal vein |
|
np.array([ |
|
[0,0,0,0,0,0,0,0], |
|
[0,0,0,0,0,0,0,0], |
|
[0,1,1,0,0,0,0,0], |
|
[0,0,0,1,0,0,0,0], |
|
[0,0,0,0,0,0,0,0], |
|
[0,0,0,0,0,0,0,0], |
|
[0,0,0,0,0,0,0,0], |
|
[0,0,0,0,0,0,0,0] |
|
], dtype=np.uint8), |
|
# 3: Diagonal crack |
|
np.array([ |
|
[0,0,0,0,0,0,0,0], |
|
[0,0,0,0,0,0,0,0], |
|
[0,0,0,0,0,0,0,0], |
|
[0,0,0,0,0,0,1,0], |
|
[0,0,0,0,0,1,0,0], |
|
[0,0,0,0,1,0,0,0], |
|
[0,0,0,0,0,0,0,0], |
|
[0,0,0,0,0,0,0,0] |
|
], dtype=np.uint8), |
|
# 4: Forked crack |
|
np.array([ |
|
[0,0,0,0,0,0,0,0], |
|
[0,0,0,0,0,0,0,0], |
|
[0,0,0,0,0,0,0,0], |
|
[0,0,0,0,0,0,0,0], |
|
[0,0,1,0,0,0,0,0], |
|
[0,0,0,1,1,0,0,0], |
|
[0,0,0,0,0,1,0,0], |
|
[0,0,0,0,0,0,0,0] |
|
], dtype=np.uint8), |
|
# 5: Dots / dust |
|
np.array([ |
|
[0,0,0,0,0,0,0,0], |
|
[0,0,0,0,0,0,0,0], |
|
[0,0,0,0,0,0,0,0], |
|
[0,0,0,1,0,0,0,0], |
|
[0,0,0,0,0,0,0,0], |
|
[0,0,0,0,1,0,0,0], |
|
[0,0,0,0,0,0,0,0], |
|
[0,0,0,0,0,0,0,0] |
|
], dtype=np.uint8) |
|
] |
|
|
|
def to_gb_format(tile_np): |
|
bytes_arr = [] |
|
for r in range(8): |
|
lo = hi = 0 |
|
for c in range(8): |
|
val = int(tile_np[r, c]) |
|
if val & 1: lo |= (1 << (7 - c)) |
|
if val & 2: hi |= (1 << (7 - c)) |
|
bytes_arr.append(lo) |
|
bytes_arr.append(hi) |
|
return bytes_arr |
|
|
|
with open('src/tiles.c', 'w') as f: |
|
f.write('#include "tiles.h"\n\n') |
|
f.write(f'const unsigned char TileData[{(6+16)*16}] = {{\n') |
|
|
|
for i, fl in enumerate(floors): |
|
f.write(f' // Floor variant {i}\n') |
|
b = to_gb_format(fl) |
|
f.write(' ' + ', '.join(f"0x{val:02X}" for val in b) + ',\n') |
|
|
|
for i, w in enumerate(walls): |
|
f.write(f' // Wall {i}\n') |
|
b = to_gb_format(w) |
|
f.write(' ' + ', '.join(f"0x{val:02X}" for val in b) + ',\n') |
|
|
|
f.write('};\n') |
|
|
|
colors = { |
|
0: (224, 248, 208), |
|
1: (136, 192, 112), |
|
2: (52, 104, 86), |
|
3: (8, 24, 32) |
|
} |
|
|
|
cols = 6 |
|
rows = 4 |
|
img = Image.new('RGB', (cols * 10, rows * 10), (255, 255, 255)) |
|
tiles = floors + walls |
|
for idx, t in enumerate(tiles): |
|
c = idx % cols |
|
r = idx // cols |
|
x_offset = c * 10 + 1 |
|
y_offset = r * 10 + 1 |
|
for ty in range(8): |
|
for tx in range(8): |
|
color = colors[int(t[ty, tx])] |
|
img.putpixel((x_offset + tx, y_offset + ty), color) |
|
|
|
img = img.resize((cols * 10 * 4, rows * 10 * 4), Image.Resampling.NEAREST) |
|
img.save('/home/enne2/.gemini/antigravity/brain/69a2b2a9-088f-47ee-9f3c-9ba7ac332992/bush_tiles_preview.png') |
|
|
|
print("tiles.c generated with bush tiles!")
|
|
|