Randomly generating terrains

… aka “When the Creator misplaces a parenthesis”. :)

So my first attempt to write a program to generate random but pleasing landforms for my Sim on a Stick did not go quite as expected.

When the Creator God misplaces a paren...

Bit spikey, for instance.

But then I found a misplaced parenthesis! And things got better.

Ah, much better

I think I am liking the algorithm…

Landforms from the RNG

For the curious, here is the basic (recursive, self-similar) routine, in everyone’s favorite language: Python. (Squished down to fit in the weblog here, not guaranteed to compile as-is.)

def fill_in_terrain(t,lowx,lowy,maxx,maxy):
   ''' Takes the input terrain, where the low-by-max '''
   ''' corners are already filled in, and fills in '''
   ''' everything inside those corners, by the '''
   ''' Magic of Recursion. '''
   if maxx-lowx<2: return t
   if maxy-lowy<2: return t
   xmid = int((lowx+maxx)/2)    
   ymid = int((lowy+maxy)/2)
   hscale = (maxx - lowx) * HSCALE
   # first the corners
   if t[lowx][ymid] is None:
     t[lowx][ymid] = (t[lowx][lowy] + t[lowx][maxy]) / 2 
       + hscale - math.floor(2*hscale*random.random())
   if t[maxx][ymid] is None:
     t[maxx][ymid] = (t[maxx][lowy] + t[maxx][maxy]) / 2 
       + hscale - math.floor(2*hscale*random.random())
   if t[xmid][lowy] is None:
     t[xmid][lowy] = (t[lowx][lowy] + t[maxx][lowy]) / 2 
       + hscale - math.floor(2*hscale*random.random())
   if t[xmid][maxy] is None:
     t[xmid][maxy] = (t[lowx][maxy] + t[maxx][maxy]) / 2 
       + hscale - math.floor(2*hscale*random.random())
   # then the center
   if t[xmid][ymid] is None:
     t[xmid][ymid] = (t[lowx][ymid]+t[maxx][ymid]
                     + t[xmid][lowy]+[xmid][maxy])/4 
                     + hscale - math.floor(2*hscale*random.random())
   # and recurse on the four quadrants
   t = fill_in_terrain(t,lowx,lowy,xmid,ymid)
   t = fill_in_terrain(t,lowx,ymid,xmid,maxy)
   t = fill_in_terrain(t,xmid,lowy,maxx,ymid)
   t = fill_in_terrain(t,xmid,ymid,maxx,maxy)
   return t

def get_terrain(x,y):
   ''' returns an [x][y] array of floats representing a terrain; '''
   ''' range unpredictable '''
   ''' x and y ought to be powers of two, for best results.  '''
   ''' oh, and equal I suppose '''
   answer = [ [None for a in range(y+1)] for b in range(x+1) ]
   answer[0][0] = 0
   answer[x][0] = 0
   answer[0][y] = 0
   answer[x][y] = 0
   answer = fill_in_terrain(answer,0,0,x,y)
   return answer

Fun that all these pretty hills and things come out of that little bit of (also pretty) code…


Making sculpties with code

I finally got around to doing this!

Sculpties, I make thems!

Here I am sitting in my place in Inworldz (where the texture uploads are free, heh heh), trying out the sculpt-maps made by this Python program that I wrote that makes sculpt maps.

My favorite so far is the pointy one on the right there, which resulted from a couple of bugs in the program. :)

(And yeah, the Linden trees are being mysteriously grey again.)

Here are a couple of the sculpt textures themselves:

The fun pointy one:

Another odd bug-generated one:

And the very symmetrical one on the left, once all the bugs were out:

And finally (Secrets of the Ages Revealed!) the Python code itself:

import sys
import math
from PIL import Image

X = 64
Y = X

fname = sys.argv[1] if len(sys.argv)>1 else "sculpt.png"

def getR(x,y):
  return(      math.sin( y * 2 * math.pi )  * 
                 (( math.sin( x * math.pi ) + 1 ) / 2)         )

def getG(x,y):
  return(      math.cos( y * 2 * math.pi )  * 
                 (( math.sin( x * math.pi ) + 1 ) / 2)        )

def getB(x,y):
  return( x*2 - 1  )

def gogogo():
  ary = []
  for x in range(X):
    for y in range(Y):
      ary.append( ( 
        127+int(127.0* getR(float(x)/X,float(y)/Y)), 
        127+int(127.0* getG(float(x)/X,float(y)/Y)), 
        127+int(127.0* getB(float(x)/X,float(y)/Y))  ) )
  mode = "RGB"
  size = (X,Y)
  im = Image.new(mode,size)


I trust it’s obvious how to use it.

Hahaha, sorry! Couldn’t resist. :)

To use it, well, first install Python and also the Python Imaging Library, then create a “mksc.py” or whatever you want to call it from the code above. Do “python mksc.py vase.png”, and it will create a file “vase.png” which you can upload and use as a sculpt texture to make the symmetrical thing on the left.

Then edit your mksc.py to change the getR, getG, and getB functions to something else, and do “python mksc.py somethingelse.png”, and upload somethingelse.png and use it as a sculpt texture, and see what happens!

For best results, getR, getG, and getB should all expect to get two floating point numbers between zero and one as input, and should return a single floating point number between negative one and one as output.

And there ya go! :)

Oh, and: you may find that the textures work best if uploaded with lossless compression (probably a checkbox on your image-upload panel), and that it’s more obvious what’s going on it you use the “torus” stitching type (in the edit panel right under where you put the sculpt texture) rather than the default “sphere” type. P’haps…