The process is simply: 1. Take a plane 2. Cut out a shape 3. Make it a little taller 4. Repeat
- Similar to the spherical landscape algorithm by Hugo Elias.
- I found a combination of Ovals and Triangles to produce the best results.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | #!/usr/bin/env python
# -*- coding: utf-8 -*-
import Image, ImageDraw, ImageChops, ImageOps, ImageFilter
import pygame.display, pygame.image
import random
import sys
from math import ceil
percentWater = .70
mapSize = 600 #in pixels
maxSize = 1.40 # 1 to 5 How big should the slices be cut, smaller slices create more islands, larger slices create less
shape = 1.40 # 1 mean roundish continents .5 is twice as tall as it is wide, 2.0 is twice as wide as tall
driftRate = .70 # As the world ages how much slower does it drift. 1 creates a more textured world but takes longer
roughness = 1 #High numbers make a world faster, with more "ridges", but also makes things less "smooth"
filename = 'heightmap.bmp'
xrand = lambda ms = mapSize*3: int(randType(0, ms))
yrand = lambda ms = mapSize*2: int(randType(0-(ms/2), ms))
randType = random.uniform #change to alter variability
def normalize(image):
image = image.filter(ImageFilter.BLUR)
picture = ImageChops.blend(ImageOps.equalize(image), image, .5)
return ImageChops.multiply(picture, picture)
def finish(image): #called when window is closed, or iterations stop
picture = normalize(image)
picture.save(filename)
pygame.quit()
sys.exit();
def drawPieSlices(oval, orig, action):
fl = action[1]
img = Image.new('L', (mapSize*2,mapSize))
draw = ImageDraw.Draw(img)
draw.pieslice([oval[0],oval[1],mapSize*4,oval[3]], 90, 270, fill=fl)
del draw
orig = action[0](orig, img)
img = Image.new('L', (mapSize*2,mapSize))
draw = ImageDraw.Draw(img)
draw.pieslice([0-oval[0],oval[1],oval[2]-mapSize*2,oval[3]], 270, 90, fill=fl)
del draw
return action[0](orig, img)
def drawOval(oval, orig, action):
img = Image.new('L', (mapSize*2,mapSize))
draw = ImageDraw.Draw(img)
draw.ellipse(oval, fill=action[1])
del draw
return action[0](orig, img)
def cutOval(orig, smallness=1):
smallness = smallness ** driftRate
landAction = lambda: (
ImageChops.add,
ceil(randType(1,roughness*smallness*(percentWater)))
)
seaAction = lambda: (
ImageChops.subtract,
ceil(randType(1,roughness*smallness*(1.0-percentWater)))
)
action = seaAction() if random.random() < percentWater else landAction()
oval = [xrand(mapSize*2),yrand(mapSize),1,1] #x,y,x,y
oval[2] = int(oval[0]+(mapSize*maxSize*shape)*smallness)
oval[3] = int(oval[1]+(mapSize*maxSize)*smallness)
if oval[2] > mapSize*2: #if x values cross our border, we needto wrap
ret = drawPieSlices(oval, orig, action)
else:
ret = drawOval(oval, orig, action)
return ret
imageToPygame = lambda i: pygame.image.fromstring(i.tostring(), i.size, i.mode)
def highestPointOnSphere(sphere):
extremes = sphere.getextrema()
return extremes[0]/255.0 if percentWater > .5 else 1-(extremes[1]/255.0)
def createSphere():
pygame.init() #Need this to render the output
sphere = Image.new('L', (mapSize*2,mapSize))
img = ImageDraw.Draw(sphere)
baseline = (256*(1.0-(percentWater)))
img.rectangle([0-mapSize,0,mapSize*4,mapSize], fill=baseline)
del img
return sphere
def sphereToPicture(sphere):
picture = normalize(sphere)
picture = ImageOps.colorize(picture, (10,0,100), (0,256,0))
picture = imageToPygame(picture)
return picture
def generatePlanet(sphere, displayInterval = 50):
extrema = highestPointOnSphere(sphere)
i = 0
picture = sphereToPicture(sphere)
pygame.display.set_mode(picture.get_size())
main_surface = pygame.display.get_surface()
del picture
while extrema > driftRate/(roughness*10*maxSize):
sphere = cutOval(sphere, extrema)
i = i+1
if displayInterval > 0 and i%displayInterval == 0:
picture = sphereToPicture(sphere)
main_surface.blit(picture, (0, 0))
pygame.display.update()
del picture
for event in pygame.event.get(): #When people close the window
if event.type == pygame.QUIT:
return image
extrema = highestPointOnSphere(sphere)
return sphere
if __name__ == '__main__':
finish(generatePlanet(createSphere(), 50))
|
Edit: Recently improved the readability, efficiency, and effectivness of this algorithm. Try adjusting the variables at the top.