terrain/terrain.py

149 lines
4.6 KiB
Python
Raw Normal View History

2014-05-20 17:02:35 +02:00
import random
import numpy
import pyglet
from pyglet.gl import *
from trackball_camera import TrackballCamera
class Terrain():
def __init__(self, details):
self.size = 2 ** details + 1
self.map = numpy.zeros((self.size, self.size))
def _square(self, x, y, size, offset):
average = sum([
self.map[x - size][y - size],
self.map[x + size][y - size],
self.map[x + size][y + size],
self.map[x - size][y + size]]) / 4
self.map[x][y] = average + offset
def _diamond(self, x, y, size, offset):
average = sum([
self.map[x][y - size],
self.map[x + size][y],
self.map[x][y + size],
self.map[x - size][y]]) / 4
self.map[x][y] = average + offset
def generate(self, high):
i, half = self.size - 1, (self.size - 1) / 2
scale = high * self.size
while i > 1:
for y in numpy.arange(half, self.size - 1, i):
for x in numpy.arange(half, self.size - 1, i):
self._square(x, y, half, scale / 2 * random.random())
for y in numpy.arange(0, self.size - 1, half):
for x in numpy.arange((y + half) % i, self.size - 1, i):
self._diamond(x, y, half, scale / 2 * random.random())
i /= 2
half = i / 2
scale = high * half * 2
class Window(pyglet.window.Window):
def __init__(self, *args, **kwargs):
self.terrain = None
self.camera = kwargs['camera']
del kwargs['camera']
super().__init__(*args, **kwargs)
# Initialize OpenGL
glEnable(GL_DEPTH_TEST)
glDisable(GL_CULL_FACE)
def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers):
"""Move camera/zoom on mouse drag"""
if buttons & pyglet.window.mouse.LEFT:
self.camera.mouse_roll(
self._norm(x, self.width),
self._norm(y, self.height))
elif buttons & pyglet.window.mouse.RIGHT:
self.camera.mouse_zoom(
self._norm(x * 2, self.width),
self._norm(y * 2, self.height))
def on_mouse_press(self, x, y, button, modifiers):
"""Move camera/zoom on mouse drag"""
if button == pyglet.window.mouse.LEFT:
self.camera.mouse_roll(
self._norm(x, self.width),
self._norm(y, self.height),
False)
elif button == pyglet.window.mouse.RIGHT:
self.camera.mouse_zoom(
self._norm(x * 2, self.width),
self._norm(y * 2, self.height),
False)
def on_resize(self, width, height):
"""Adjust drawing after window is resized"""
self.width = width
self.height = height
glViewport(0, 0, self.width, self.height)
self.on_show()
def on_show(self):
"""Set OpenGl config"""
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(40, self.width / self.height, 1, 400)
self.camera.update_modelview()
def on_draw(self):
"""Draw the current frame"""
if self.terrain is None:
return
self.clear()
map, size = self.terrain.map, self.terrain.size
glPushMatrix()
glTranslatef(-size / 2, 0, -size / 2)
# Draw terrain
for x in numpy.arange(size - 1):
for y in numpy.arange(size - 1):
glBegin(GL_TRIANGLE_STRIP)
glColor3f(map[x][y] / 20, map[x][y] / 20, map[x][y] / 20)
glVertex3f(x, map[x][y], y)
glVertex3f(x + 1, map[x + 1][y], y)
glVertex3f(x, map[x][y + 1], y + 1)
glVertex3f(x + 1, map[x + 1][y + 1], y + 1)
glEnd()
glPopMatrix()
# Draw axis
glBegin(GL_LINES)
glColor3f(1, 0, 0)
glVertex3f(-size, 0, 0)
glVertex3f(size, 0, 0)
glColor3f(0, 1, 0)
glVertex3f(0, -size, 0)
glVertex3f(0, size, 0)
glColor3f(0, 0, 1)
glVertex3f(0, 0, -size)
glVertex3f(0, 0, size)
glEnd()
def _norm(self, x, max_x):
"""given x within [0,max_x], scale to a range [-1,1]"""
return (2 * x - float(max_x)) / float(max_x)
def draw(self, terrain):
"""Render the height map"""
self.terrain = terrain
def main():
terrain = Terrain(5)
camera = TrackballCamera(150)
window = Window(caption="Terrain", resizable=True,
width=800, height=600, camera=camera)
terrain.generate(0.7)
window.draw(terrain)
pyglet.app.run()
if __name__ == '__main__':
main()