Skip to content

Instantly share code, notes, and snippets.

@sixthgear
Created June 3, 2013 22:22
Show Gist options
  • Save sixthgear/5701991 to your computer and use it in GitHub Desktop.
Save sixthgear/5701991 to your computer and use it in GitHub Desktop.
Mad Science map class
from noise import perlin_noise as noise
from objects import building
from objects import tree
from pyglet.gl import *
import math
import random
import vector
tile_size = 32
tile_height = 24
tile_levels = 16
tile_colors = [
[0.0, 0.0, 0.5], # water
[0.0, 0.0, 0.7], # water
[0.1, 0.1, 0.9], # water
[0.1, 0.1, 0.9], # water
[1.0, 1.0, 0.9], # beach
[1.0, 1.0, 0.9], # beach
[0.1, 0.4, 0.1], # grass
[0.1, 0.4, 0.1], # grass
[0.1, 0.4, 0.1], # grass
[0.1, 0.4, 0.1], # grass
[0.1, 0.4, 0.1], # grass
[0.3, 0.3, 0.3], # rock
[0.3, 0.3, 0.3], # rock
[0.3, 0.3, 0.3], # rock
[0.3, 0.3, 0.3], # rock
[1.0, 1.0, 1.0], # snow
]
class Tile(object):
def __init__(self, value, height=None):
self.value = value
self.type = 0
if height:
self.height = height
else:
self.calc_height()
def calc_height(self):
self.height = max(0, self.value-1) * tile_height * (self.value / float(tile_levels)) ** 2
class Map(object):
"""
Stores the landscape mesh
"""
def __init__(self, width, height):
self.width = width
self.height = height
self.tiles = [Tile(0,0) for i in range(width*height)]
self.buildings = []
self.vbo = None
self.vbo_ol = None
@classmethod
def generate(cls, width, height):
"""
generate a random map
"""
m = cls(width, height)
ngrid = noise.SmoothNoise(width, height, interpolation='linear').generate(3)
for i, n in enumerate(ngrid.cell_iter()):
m.tiles[i] = Tile(int(n * tile_levels))
# flatten area to build city
# m.flatten(width/2-20,height/2-20,40,40)
# # construct buildings
# for b in range(4):
# x = random.randrange(width/2-17, width/2+17)
# z = random.randrange(width/2-17, height/2+17)
# t = m.get(x, z)
# if t.type == 1:
# bx = x * tile_size # tile_size/2 +
# bz = z * tile_size # tile_size/2 +
# by = t.height
# m.buildings.append(building.Building(bx, by, bz))
# construct trees
for b in range(8000):
x = random.randrange(1, width-1)
z = random.randrange(1, height-1)
t = m.get(x, z)
if t.value > 6 and t.value < 10 and t.type == 0:
bx = x * tile_size # tile_size/2 +
bz = z * tile_size # tile_size/2 +
by = t.height
m.buildings.append(tree.Tree(bx, by, bz))
m.build_vbo()
return m
def flatten(self, x, z, width, height, recurse=False):
heights = []
for i in range(width*height):
xx = x + i % width
zz = z + i / width
heights.append(self.get(xx,zz).value)
avg = sum(heights) / len(heights)
for i in range(width*height):
xx = x + i % width
zz = z + i / width
self.get(xx,zz).value = avg
self.get(xx,zz).type = 1
self.get(xx,zz).calc_height()
if recurse == False:
self.flatten(x-1,z-1,3,height+2,recurse=True)
self.flatten(x-1,z-1,width+2,3,recurse=True)
self.flatten(x+width-1,z-1,3,height+2,recurse=True)
self.flatten(x-1,z+height-1,width+2,3,recurse=True)
def get(self, x, z):
return self.tiles[z * self.width + x]
def build_vbo(self):
def get_color(x, z):
t = self.get(x, z)
if t.type == 0:
c = tile_colors[t.value]
else:
c = [0.1,0.1,0.1]
jitter = (random.random()*0.05-0.025)
return [c + jitter for c in c]
def get_normal(v, x, z):
s = tile_size
if normal_cache.has_key((x,z)):
return normal_cache[(x,z)]
neighbors = []
if x > 0:
neighbors.append(vector.Vec3d((x-1)*s, self.get(x-1,z).height, z*s))
if z > 0:
neighbors.append(vector.Vec3d(x*s, self.get(x,z-1).height, (z-1)*s))
if x < self.width-1:
neighbors.append(vector.Vec3d((x+1)*s, self.get(x+1,z).height, z*s))
if z < self.height-1:
neighbors.append(vector.Vec3d(x*s, self.get(x,z+1).height, (z+1)*s))
deltas = [(n - v) for n in neighbors]
sum = vector.Vec3d(0,0,0)
for d in range(len(deltas)):
sum += deltas[d].cross(deltas[(d+1)%len(deltas)])
normal = (sum / len(deltas)).normal
if normal.y < 0:
normal.y *= -1
normal_cache[(x,z)] = normal
return normal
s = tile_size
v_list = []
c_list = []
n_list = []
normal_cache = {}
# calculate vertices and colors
for i in range((self.height-1) * (self.width)):
z0 = i / (self.width)
z1 = z0 + 1
if z0 % 2 == 0: # even rows go right
x0 = i % (self.width )
skip = x0 == 0 and z0 != 0
else: # odd rows go left
x0 = (self.width - 1) - (i % (self.width))
skip = x0 == self.width-1
# print v, n
if not skip:
v0 = vector.Vec3d(x0*s, self.get(x0,z0).height, z0*s)
n0 = get_normal(v0, x0, z0)
v_list += [v0.x, v0.y, v0.z]
n_list += [n0.x, n0.y, n0.z]
jitter = (random.random()*0.05-0.025)
c_list += get_color(x0, z0)
v1 = vector.Vec3d(x0*s, self.get(x0,z1).height, z1*s)
n1 = get_normal(v1, x0, z1)
v_list += [v1.x, v1.y, v1.z]
n_list += [n1.x, n1.y, n1.z]
c_list += get_color(x0, z1)
self.vbo = pyglet.graphics.vertex_list(len(v_list)/3, 'v3f', 'c3f', 'n3f')
self.vbo_ol = pyglet.graphics.vertex_list(len(v_list)/3, 'v3f')
self.vbo.vertices = v_list[:]
self.vbo.normals = n_list[:]
self.vbo.colors = c_list[:]
self.vbo_ol.vertices = v_list[:]
def update(self, dt):
pass
def draw(self):
glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT | GL_LIGHTING_BIT | GL_POLYGON_BIT | GL_LINE_BIT)
# set wireframe details
glLineWidth(0.4)
glColor4f(0.1,0.1,0.1,0.2)
# draw solids
glEnable(GL_COLOR_MATERIAL)
self.vbo.draw(GL_TRIANGLE_STRIP)
#draw wireframe
glPolygonMode (GL_FRONT_AND_BACK, GL_LINE)
self.vbo_ol.draw(GL_TRIANGLE_STRIP)
glPopAttrib()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment