Rail3D*


 

Random Terrain Mesh



The best way to build realistic terrain in Rail3D is probably to take real world data from a dem. See UsingSRTM Terrain Data. However, sometimes it’s fun to try to build a railway on some completely imaginary terrain. In the world of computer games, a lot of work has been done on terrain generation, using various mathematical tools to generate something that looks convincing starting from random data. We might as well profit from this knowledge for Rail3D.

1 Fractal Terrain for Rail3D

R3dFracTerrain is a small utility that demonstrates some of the simpler fractal terrain generation techniques, producing output you can paste directly into Rail3D.

Random Terrain Mesh
Screenshot of the R3dFracTerrain program in action

1.1 Using R3dFracTerrain

To use the program, you need to select a few parameters.

North, South, East, West
These parameters define the bounds of the terrain area produced. The default settings will produce a 10×10km area centred on the default Home position in Rail3D
Ht N W, Ht N E, Ht S W, Ht S E
These are the starting elevations for the four corners of the layout. If you want high country sloping down towards a South coast, set the ne and nw values higher than se and sw. In most cases you will just leave these set to zero.
Recursion depth
This specifies the number of levels of recursion that are used, and hence the number of points produced. For most purposes level 6 (64×64) or level 7 (128×128) should be plenty. You only need level 8 for very big layouts.
Vertical Range
This defines the amount by which terrain points should be perturbed from the initial elevation. If you keep the default values, you can expect to find hills up to 200m high in the output.
Sea level
This is an arbitrary lower cut-off height for the terrain. This is a bit of a misnomer — its purpose is to cut off and fill the deepest valleys. If you are using the Rail3D sea-plane, you don’t need this, and you can set it to any negative value you like. If you don’t use the Rail3D sea-plane, setting a “sea level” somewhere about 0 gives you something like an alluvial plain at the bottom of your hills.
Roughness
This is the index of the noise filtering function 1/fn — by default it is set to 2, which gives you reasonably rough terrain. If you choose a higher value, the terrain gets progressively smoother (3 is about the limit); if you reduce it towards 1 you will end up with white noise.

R3dFracTerrain generates a terrain mesh using one of two techniques:

Fractal Subdivision
This is a simple quad tree fractal algorithm, as used in many computer games. What it does is take a square piece of terrain and add a new point in the middle of the square. The new point is randomly shifted up or down relative to its “normal” (interpolated) position. This divides the original square into four new squares, and we can repeat the process for each square, interpolating midpoints, shifting them randomly, and subdividing, until we have subdivided to the level we want. (See more detailed explanations by
Spatial filtering
This sounds fancier, but is actually quite simple — the program takes a set of random points, does a 2d Fourier transform to convert them into frequency space, applies a 1/fn filter function to get rid of the high frequency (jaggy) components, and then transforms them back into x,y space with an inverse fourier transform. (See Paul Bourke’s site for a more detailed explanation)

In theory, both techniques should give you the same results. In practice, the way I’ve implemented them they treat the edges of the pattern differently, so the spatial filtering technique gives a tilable pattern, while the subdivision technique doesn’t.

When you press the Generate button, you will get a dialogue telling you the point spacing you have selected, and the number of points. Remember that Rail3D will not triangulate points mre than 1km apart.

The program generates the terrain points. The output in Rail3D format appears in the text box, from where you can copy and paste it into Rail3D. There is a coloured 2d preview of the data to give you a rough idea of how the terrain will look. In the default Blue/Green mode, The “sea-level” points are coloured blue, everything else in varying shades of green, getting lighter as it gets higher. You can also use Greyscale or 24-bit colour visualisation.

If the terrain doesn’t look suitable for your purpose, press the button again to have another go.

You can change the scaling parameters (everything except Roughness and Recursion Depth) and regenerate the output text and preview for the same piece of random terrain by pressing refresh — this is useful if the random terrain is basically ok, but the mountains are too high, or the sea level is wrong.


Terrain preview image

1.2 Importing heightfields

You can also use an external application to generate a heightfield bitmap and use R3dFracTerrain to convert it into Rail3D format. Press Load Image to do this. The heightfield should be a square bitmap, preferably not more than 256×256 pixels, with the heights encoded as 8-bit greyscale or 24-bit colour with the most significant byte as the red channel (this is the format you get when exporting from Leveller).

The imported values are normalised before applying the scaling factors set in R3dFracTerrain — any scaling information you set in the source application is ignored.

Some programs you could use for generating heightfields:

1.3 Exporting

You can save the terrain preview if you want to use it in the Digitiser as a guide for tracklaying. It will be saved in the folder where you have the R3dFracTerrain program, with a filename like frac_20051028_223523.bmp. If you want to reimport the heightfield into R3dFracTerrain, then use the “Greyscale” colour mode.

Random Terrain Mesh
Layout using random terrain

1.4 Resources

2 Simple Mesh Generation

For the Transrapid Munich Airport project, I tried the experiment of using a program to generate a mesh of terrain points rather than adding points myself by hand according to the map. It turned out not to be the best solution for that particular layout, but it did work, and it’s a quick way to make natural-looking terrain without any nasty artefacts.

I found that the most efficient mesh that will give reliable triangulation is a lattice pattern, essentially two 1 km grids with the second one shifted 500m east and north relative to the first. Rail3D will triangulate points a maximum of 1km apart.

In theory, a pattern of equilateral triangles should give the most efficient terrain mesh, but I found that the lattice-mesh triangulated more reliably in practice. (Another thing that should work, but for some reason doesn’t, is a rectangular mesh with a spacing of 1/[sqrt(2)] km between the points.)

Adding in a small amount of randomness in the elevation gives the terrain a more natural look. I also experimented with adding randomness to the x,y coordinates, but this didn’t seem to have any useful effect.

I wrote the following script to generate this mesh using Calcit (you could use any suitable scripting tool).

 
 function scatter(value, range);
   //returns a random value in the range (value +/- range)

   range:=int(100*range);
   value:=value+RANDOM(-range,range)/100;
   value;
 end;

 //set up bounds of terrain area
 west:=9000;
 east:=40000;
 south:=18000;
 north:=46000;
 zsouth:=520;
 znorth:=444;

 //set up step size (1km is the maximum that will triangulate in Rail3d) 
 step:=1000;
 points:=0;   //counter for debugging

 xinc:=step;  //change these if you want an asymmetric grid
 yinc:=step;
 dr:=0;       //range for x,y scatter, if required

 RANDOMIZE;   //initialize random number generator

 //main loop
 for(y,south,north,yinc);
    z:=zsouth + (znorth-zsouth)*(y-south)/(north-south);

    for(x,west,east,xinc);
        print('TERRAIN ',scatter(x,dr):'.2',' ',y:'.2',' ',scatter(z,2):'.2');
        print('TERRAIN ',scatter(x+xinc/2,dr):'.2',' ',scatter(y+yinc/2,dr):'.2',' ',scatter(z,2):'.2');

        points:=points+1;
    END;

 END;
 

The first few lines of the output look like this:

 terrain 9000.00 18000.00 520.72
 terrain 9500.00 18500.00 518.44
 terrain 10000.00 18000.00 520.88
 terrain 10500.00 18500.00 520.54
 terrain 11000.00 18000.00 518.99
 terrain 11500.00 18500.00 519.76
 terrain 12000.00 18000.00 518.12
 terrain 12500.00 18500.00 521.41
 terrain 13000.00 18000.00 521.52
 terrain 13500.00 18500.00 520.20
 …

Always make a backup of your layout before pasting in large amounts of terrain - it’s all too easy to make a small error and mess everything up!

Once the script has run, all you have to do is copy the output to the clipboard and paste it into Rail3D.

Mark Hodson



import