terrain.py (14 lines of code) (raw):
from pdb import set_trace as T
from opensimplex import OpenSimplex
gen = OpenSimplex()
from forge.blade.lib import enums
from matplotlib import pyplot as plt
from scipy.misc import imread, imsave
from shutil import copyfile
from copy import deepcopy
import numpy as np
import os
template_tiled = """<?xml version="1.0" encoding="UTF-8"?>
<map version="1.0" tiledversion="1.1.5" orientation="orthogonal" renderorder="right-down" width="{0}" height="{1}" tilewidth="128" tileheight="128" infinite="0" nextobjectid="1">
<tileset firstgid="0" source="../../tiles.tsx"/>
<layer name="Tile Layer 1" width="{0}" height="{1}">
<data encoding="csv">
{2}
</data>
</layer>
</map>"""
def saveTiled(dat, path):
""""Saved a map into into a tiled compatiable file given a save_path,
width and hieght of the map, and 2D numpy array specifiying enums for the array"""
height, width = dat.shape
dat = str(dat.ravel().tolist())
dat = dat.strip('[').strip(']')
with open(path + 'map.tmx', "w") as f:
f.write(template_tiled.format(width, height, dat))
#Bounds checker
def inBounds(r, c, shape, border=0):
R, C = shape
return (
r > border and
c > border and
r < R - border - 1 and
c < C - border - 1
)
def noise(nx, ny):
# Rescale from -1.0:+1.0 to 0.0:1.0
return gen.noise2d(nx, ny) / 2.0 + 0.5
def sharp(nx, ny):
return 2 * (0.5 - abs(0.5 - noise(nx, ny)));
def perlin(nx, ny, octaves, scale=1):
val = 0
for mag, freq in octaves:
val += mag * noise(scale*freq*nx, scale*freq*ny)
return
def ridge(nx, ny, octaves, scale=1):
val = []
for idx, octave in enumerate(octaves):
mag, freq = octave
v = mag*sharp(scale*freq*nx, scale*freq*ny)
if idx > 0:
v *= sum(val)
val.append(v)
return sum(val)
def expParams(n):
return [(0.5**i, 2**i) for i in range(n)]
def norm(x, X):
return x/X - 0.5
def grid(X, Y, n=8, scale=1, seed=0):
terrain = np.zeros((Y, X))
for y in range(Y):
for x in range(X):
octaves = expParams(n)
nx, ny = norm(x, X), norm(y, Y)
terrain[x, y] = ridge(seed+nx, seed+ny, octaves, scale)
return terrain / np.max(terrain)
def textures():
lookup = {}
for mat in enums.Material:
mat = mat.value
tex = imread(
'resource/assets/tiles/' + mat.tex + '.png')
key = mat.tex
mat.tex = tex[:, :, :3][::4, ::4]
lookup[key] = mat
return lookup
def tile(val, tex):
if val == 0:
return tex['lava']
elif val < 0.3:
return tex['water']
elif val < 0.57:
return tex['grass']
elif val < 0.715:
return tex['forest']
else:
return tex['stone']
def material(terrain, tex, X, Y, border=9):
terrain = deepcopy(terrain).astype(object)
for y in range(Y):
for x in range(X):
if not inBounds(y, x, (Y, X), border-1):
terrain[y, x] = tex['lava']
elif not inBounds(y, x, (Y, X), border):
terrain[y, x] = tex['grass']
else:
val = float(terrain[y, x])
terrain[y, x] = tile(val, tex)
return terrain
def render(mats, path):
images = [[e.tex for e in l] for l in mats]
image = np.vstack([np.hstack(e) for e in images])
imsave(path, image)
def index(mats, path):
inds = np.array([[e.index+1 for e in l] for l in mats])
saveTiled(inds, path)
def fractal(terrain, path):
frac = (256*terrain).astype(np.uint8)
imsave(path, terrain)
nMaps, sz = 200, 64 + 16
#nMaps, sz = 1, 512 + 16
seeds = np.linspace(0, 2**32, nMaps)
scale = int(sz / 5)
root = 'resource/maps/'
tex = textures()
for i, seed in enumerate(seeds):
print('Generating map ' + str(i))
path = root + 'procedural/map' + str(i) + '/'
try:
os.mkdir(path)
except:
pass
terrain = grid(sz, sz, scale=scale, seed=seed)
tiles = material(terrain, tex, sz, sz)
fractal(terrain, path+'fractal.png')
render(tiles, path+'map.png')
index(tiles, path)