(Yet another) Terrain Algorithm

Ralph is roaming again, and this time over SOARX terrain. I have updated the code and written a new demo, this time featuring Ralph (who else).

Some bugs have been fixed, and the API has been re-designed, and it is possible to split terrain into chunks. Also, some minor changes in the algorithm increase the performance. The new download (source complete with windows binaries) is:

http://www.dachau.net/users/pfrogner/soarx.zip

Two screenshots, one with pudget sound data, and one in a random noise land:

http://www.dachau.net/users/pfrogner/soarx1.jpg
http://www.dachau.net/users/pfrogner/soarx2.jpg

The module now provides two objects, TerrainBuilder and TerrainRenderer. Using the TerrainBuilder works like this:

import soarx

builder = soarx.TerrainBuilder( )
builder.setHorizontalResolution( hscale )
builder.setVerticalResolution( vscale )
builder.setVerticalBias( 0.0 )
builder.setMapBits( bits )
builder.buildTerrain( filenameSource, filenameDest )

#utils
builder.buildNoiseland( filenameDest, width, height )
builder.buildGradient( filenameSource, filenameDest, scale )

Setting horizontal scale, vertical scale and vertical bias just scales the terrain and shifts it up/down. Map bits tells the builder the size of the heightmap, which must be (2^bits) + 1, for example 1025 x 1025. Finally, buildTerrain reads the heightmap and pre-computes binary data. Arguments are the source filename (an 32bit grayscale image) and the filename of the binary data to create. Two utils are available: buildNoiseland create a perlin noise image of dimensions width x height, and saves it as filenameDest. buildGradient computes a normalmap for the heightfield.

Using the TerrainRenderer isn’t too complex either. Just make sure you set the same parameters for resolution and map bits that you have used to build the terrain:

import soarx

# initialize
o = soarx.TerrainRenderer( )
o.setMapBits( bits )
o.setHorizontalResolution( hscale )
o.setCameraNP( self.camera )
o.setLens( self.lens )
o.setMagic( 60.0 )
o.setNP( chunkNP )
o.load( filename )

# once a frame
o.update( )

It is now possible to have several terrain chunks at the same time. Just edit the demo (both build.py and render.py) and change nx,ny and chunkbits. For example nx=3, ny=2, chunkbits=9. This will create 3x2 chunks, each 2^9+1 -> 513x513 pixels in size. The chunks will fit together without cracks :slight_smile: This way you could implement a paging landscape (The demo just loads the chunks at startup, it doesn’t load/unload them depending on Ralph’s position). Loading terrain is quite fast, since no terrain data is loaded actually, only a filemapping is created.

Now the bad news:
The demo runs at 130fps on my PC, which is above average. Not really hight-end, but certainly above average. This is nice, but it is just the terrain, without vegetation, objects, actors, and so on. The built-in HeightfieldTesselator is much slower when updating the terrain every frame, but if you use it is a sensible way (update only if the camera position has changed significant) then it is about twice as fast for medium sized terrains. And HeightfieldTesselator has normals and texture coordinates assigned to the vertices, and collision detection is fast.

So feel free to try out the SOARX extension, especially for huge terrains (4k x 4k), or have a look at how the wrapping is done. But keep in mind that the HeightfieldTesselator that comes with Panda3d might be a better choice.

enn0x