Browse Source

feat: procedural bushy vegetation autotiles

master
Matteo Benedetto 3 days ago
parent
commit
5d80f598f1
  1. 214
      build_bush_tiles.py
  2. 42
      src/tiles.c

214
build_bush_tiles.py

@ -0,0 +1,214 @@
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!")

42
src/tiles.c

@ -4,45 +4,45 @@ const unsigned char TileData[352] = {
// Floor variant 0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Floor variant 1
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
// Floor variant 2
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// Floor variant 3
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
// Floor variant 4
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x18, 0x00, 0x04, 0x00, 0x00, 0x00,
// Floor variant 5
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
// Wall 0
0x7E, 0x7E, 0x7C, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7E, 0x7E, 0x7C, 0x7C, 0x00, 0x00,
0x3C, 0x00, 0x7E, 0x00, 0xA5, 0x7A, 0x90, 0xEE, 0x48, 0x3F, 0x1E, 0xFE, 0x7E, 0x7E, 0x30, 0x30,
// Wall 1
0x00, 0x02, 0xFC, 0x02, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xFE, 0xFC, 0xFE, 0x00, 0x02,
0x6C, 0x00, 0xFE, 0x00, 0x93, 0x6E, 0x24, 0xFB, 0x90, 0xEE, 0x6A, 0xFF, 0xFE, 0xFE, 0x9C, 0x9C,
// Wall 2
0x00, 0x42, 0x3C, 0x42, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x7E,
0x0C, 0x00, 0x7E, 0x00, 0x36, 0x4D, 0x48, 0xB6, 0x12, 0x7D, 0x48, 0x76, 0x24, 0x5E, 0x0A, 0x7C,
// Wall 3
0x00, 0x02, 0xFC, 0x02, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xFE,
0x64, 0x00, 0xFE, 0x00, 0xDB, 0x26, 0xA4, 0x5E, 0x49, 0xB7, 0x12, 0xFD, 0xC8, 0xF6, 0x24, 0xDF,
// Wall 4
0x00, 0x40, 0x3F, 0x40, 0x00, 0x7F, 0x00, 0x7F, 0x00, 0x7F, 0x00, 0x7F, 0x3F, 0x7F, 0x00, 0x40,
0x36, 0x00, 0x7F, 0x00, 0xD9, 0x36, 0x25, 0xDB, 0x52, 0x2F, 0xBD, 0xDF, 0x7F, 0x7F, 0x22, 0x22,
// Wall 5
0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
0x5A, 0x00, 0xFF, 0x00, 0xA5, 0x5A, 0x90, 0x7F, 0x25, 0xDB, 0x72, 0xFF, 0xFF, 0xFF, 0x8F, 0x8F,
// Wall 6
0x00, 0x40, 0x3F, 0x40, 0x00, 0x7F, 0x00, 0x7F, 0x00, 0x7F, 0x00, 0x7F, 0x00, 0x7F, 0x00, 0x7F,
0x1F, 0x00, 0x7F, 0x00, 0x29, 0x57, 0x02, 0x7F, 0x48, 0x3F, 0x92, 0xED, 0x29, 0x57, 0x52, 0x6D,
// Wall 7
0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x18, 0x00, 0xFF, 0x00, 0xF7, 0x18, 0x09, 0xF7, 0x82, 0x7F, 0x48, 0xBF, 0x92, 0xED, 0x29, 0xD7,
// Wall 8
0x00, 0x7E, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x7E, 0x3C, 0x7E, 0x00, 0x42,
0x94, 0xEB, 0x29, 0x76, 0x42, 0x3E, 0x18, 0xFE, 0x04, 0x7A, 0xDA, 0xFE, 0x7E, 0x7E, 0x24, 0x24,
// Wall 9
0x00, 0xFE, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xFE, 0xFC, 0xFE, 0x00, 0x02,
0x48, 0xF6, 0x94, 0xEA, 0x29, 0xF6, 0x42, 0xBE, 0x19, 0xFE, 0xDC, 0xFF, 0xFE, 0xFE, 0x20, 0x20,
// Wall 10
0x00, 0x7E, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x7E,
0x12, 0xEF, 0xA4, 0x7A, 0x4A, 0x75, 0x14, 0x7B, 0x20, 0x5E, 0x0C, 0x7E, 0x42, 0x7C, 0x20, 0x5E,
// Wall 11
0x00, 0xFE, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xFE,
0xA0, 0xDE, 0x12, 0xEF, 0xA4, 0x7B, 0x4A, 0xF5, 0x94, 0x7A, 0x20, 0xDE, 0x8C, 0x7F, 0x42, 0xFD,
// Wall 12
0x00, 0x7F, 0x00, 0x7F, 0x00, 0x7F, 0x00, 0x7F, 0x00, 0x7F, 0x00, 0x7F, 0x3F, 0x7F, 0x00, 0x40,
0x21, 0xFE, 0x50, 0x6F, 0x09, 0x77, 0x52, 0x3D, 0x25, 0xFA, 0x4F, 0x3F, 0x7F, 0x7F, 0x38, 0x38,
// Wall 13
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
0x46, 0xBF, 0x21, 0xFE, 0x50, 0xEF, 0x09, 0xF7, 0x52, 0xBD, 0xBD, 0xFA, 0xFF, 0xFF, 0x67, 0x67,
// Wall 14
0x00, 0x7F, 0x00, 0x7F, 0x00, 0x7F, 0x00, 0x7F, 0x00, 0x7F, 0x00, 0x7F, 0x00, 0x7F, 0x00, 0x7F,
0x48, 0xF7, 0x23, 0xDF, 0x10, 0x7F, 0x28, 0x77, 0x84, 0xFB, 0x29, 0x5E, 0x92, 0x7D, 0x25, 0x5E,
// Wall 15
0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF,
0x25, 0xDE, 0x48, 0xF7, 0x23, 0xDF, 0x90, 0x7F, 0x28, 0xF7, 0x84, 0xFB, 0x29, 0xDE, 0x92, 0x7D,
};

Loading…
Cancel
Save