Procedural Terrain - Diamond Square Algorithm

Hi,

I’ve been trying for the last 5 days to produce a simple procedural terrain in unity using the diamond square algorithm.

Unfortunately every example (12 at my last count) that I’ve found online is too complicated or has some kind of error in Unity. (i note that apparently the ‘Java’ in Unity is not the same as actual Java code).

My last attempt is from scratch and tries to emulate the example here
My code below starts with 4 corners, then finds the middle point and height, followed by the ‘sides’ (north, east, south, west).
After the first iteration it is supposed to ‘reset’ and find the middle & sides of 4 smaller squares.

As you can see in the picture, the first iteration works ok.

But, I can’t get the ‘reset’ (which moves to the next level of detail) or ‘next’ (which moves to the next square) to actually change their values and start the iteration again.

Can anyone please help or advise?

Code is in JAVA.

function Start () {
    // map size
    var res = 65;
    var step = (res-1);
    var tData = GetComponent(Terrain).terrainData;
    tData.heightmapResolution = res;
     
    var heights = new float[res, res];
    // bumpyness
    var bumpyness = 0.03;
    
    var width = res;
    var length = res;
    var reset = 0;
    var next = 1;

      //square
    
    	
			//if(reset == 0){
     		//heights[x,y] = Random.Range(0.1-bumpyness, 0.1+bumpyness);
     		//}
     		if(reset == 0){
     		heights[0,0] = Random.Range(0.1-bumpyness, 0.1+bumpyness);
     		heights[step,0] = Random.Range(0.1-bumpyness, 0.1+bumpyness);
     		heights[0,step] = Random.Range(0.1-bumpyness, 0.1+bumpyness);
     		heights[step,step] = Random.Range(0.1-bumpyness, 0.1+bumpyness);
     		}
     		
     	
     	for(next = 1; step*next < res; next++)
     	{
     		
     		
     		
 				var x = step*next;
 				var y = step*next;
 				
     			
     			var A = heights[x-step, y-step];
     			var B = heights[x, y-step];
     			var C = heights[x-step, y];
     			var D = heights[x, y];
     		
     			var Middle = (A+B+C+D)/4 + Random.Range(-bumpyness,bumpyness);
     			heights[x-step/2, y-step/2] = Middle;
     		
    		
     			var N = (A+B)/2 + Random.Range(-bumpyness,bumpyness);
     			heights[x-step/2, y-step] = N;
     			var E = (B+D)/2 + Random.Range(-bumpyness,bumpyness);
     			heights[x, y-step/2] = E;
     			var S = (C+D)/2 + Random.Range(-bumpyness,bumpyness);
     			heights[x-step/2, y] = S;
     			var W = (A+C)/2 + Random.Range(-bumpyness,bumpyness);
     			heights[x-step, y-step/2] = W;
     		    
     			
     			
     			print(next);
     		

     		
    	}
    	
    	for(reset = 1; step*next >= res && reset < 3; reset++){
     		
      	print(reset); step = step/2; x = 0; y = 0; next = 1;
    	
     	
     	}
     	
         
                                              
     tData.SetHeights (0, 0, heights);

   }

I had a look at the code derived from Notchs Ludum Dare entry, but could not get it to work, so am unable to help with your current code :frowning:

However, here is an implementation using code derived from : java - Diamond square algorithm - Stack Overflow

This generates a new terrain every time it is run, but is really hard to control what type of terrain will be generated as it uses a random value for ‘bumpiness’. Press space while running to generate a new terrain :

#pragma strict


public var terrain : Terrain;
public var size : int = 513;

var dataArray : float[,];


function Start() 
{
	DiamondSquareDataArray();
}


function Update() 
{
	if ( Input.GetKeyDown( KeyCode.Space ) )
		DiamondSquareDataArray();
}


// http://stackoverflow.com/questions/2755750/diamond-square-algorithm

function DiamondSquareDataArray() 
{
	// declare the data array
	dataArray = new float[ size, size ];
	
	// set the 4 corners
	dataArray[ 0, 0 ] = 1;
	dataArray[ size - 1, 0 ] = 1;
	dataArray[ 0, size - 1 ] = 1;
	dataArray[ size - 1, size - 1 ] = 1;
	
	
	var val : float;
	var rnd : float;
	
	var h : float = 0.5;
	
	var sideLength : int;
	var x : int;
	var y : int;
	
	var halfSide : int;
	
	
	for ( sideLength = size - 1; sideLength >= 2; sideLength /= 2 )
	{
		halfSide = sideLength / 2;
		
		// square values
		for ( x = 0; x < size - 1; x += sideLength )
		{
			for ( y = 0; y < size - 1; y += sideLength )
			{
				val = dataArray[ x, y ];
				val += dataArray[ x + sideLength, y ];
				val += dataArray[ x, y + sideLength ];
				val += dataArray[ x + sideLength, y + sideLength ];
				
				val /= 4.0;
				
				// add random
				rnd = ( Random.value * 2.0 * h ) - h;
				val = Mathf.Clamp01( val + rnd );
				
				dataArray[ x + halfSide, y + halfSide ] = val;
			}
		}
		
		// diamond values
		for ( x = 0; x < size - 1; x += halfSide )
		{
			for ( y = ( x + halfSide ) % sideLength; y < size - 1; y += sideLength )
			{
				val = dataArray[ ( x - halfSide + size - 1 ) % ( size - 1 ), y ];
				val += dataArray[ ( x + halfSide ) % ( size - 1 ), y ];
				val += dataArray[ x, ( y + halfSide ) % ( size - 1 ) ];
				val += dataArray[ x, ( y - halfSide + size - 1 ) % ( size - 1 ) ];
				
				val /= 4.0;
				
				// add random
				rnd = ( Random.value * 2.0 * h ) - h;
				val = Mathf.Clamp01( val + rnd );
				
				dataArray[ x, y ] = val;
				
				if ( x == 0 ) dataArray[ size - 1, y ] = val;
      			if ( y == 0 ) dataArray[ x, size - 1 ] = val;
			}
		}
		
		
		h /= 2.0; // cannot include this in for loop (dont know how in uJS)
	}
	
	
	Debug.Log( "DiamondSquareDataArray completed" );
	
	// Generate Terrain using dataArray as height values
	GenerateTerrain();
}


function GenerateTerrain() 
{
	if ( !terrain )
		return;
	
	if ( terrain.terrainData.heightmapResolution != size )
		terrain.terrainData.heightmapResolution = size;
	
	terrain.terrainData.SetHeights( 0, 0, dataArray );
}

Alternative :

If you are looking at creating fractal looking terrain, have you considered using Perlin Noise? You have much more control with the terrain generated. Modify the variables in the inspector while running then press space to generate a new terrain :

#pragma strict


public var terrain : Terrain;
public var size : int = 513;

public var offset : Vector2 = Vector2.zero; // where does the sample start?
public var octaves : int = 8; // how many iteration for fractal?
public var frq : float = 100.0; // how smooth/rough? (low = rough, high = smooth)
public var amp : float = 0.5; // how strong? (0.5 is ideal)

var dataArray : float[,];


function Start() 
{
	PerlinFractalDataArray();
}


function Update() 
{
	if ( Input.GetKeyDown( KeyCode.Space ) )
		PerlinFractalDataArray();
}


function PerlinFractalDataArray() 
{
	// declare the data array
	dataArray = new float[ size, size ];
	
	// variables used in calculations
	var noise : float;
	var gain : float;
	
	var x : int;
	var y : int;
	var i : int;
	
	var sample : Vector2;
	
	// generate noise
	for ( y = 0; y < size; y ++ )
	{
		for ( x = 0; x < size; x ++ )
		{
			noise = 0.0;
			gain = 1.0;
			
			for ( i = 0; i < octaves; i ++ )
			{
				sample.x = offset.x + ( parseFloat( x ) * ( gain / frq ) );
				sample.y = offset.y + ( parseFloat( y ) * ( gain / frq ) );
				
				noise += Mathf.PerlinNoise( sample.x, sample.y ) * ( amp / gain );
				gain *= 2.0;
			}

			dataArray[ x, y ] = noise;
		}
	}
	
	
	Debug.Log( "PerlinFractalDataArray completed" );
	
	// Generate Terrain using dataArray as height values
	GenerateTerrain();
}


function GenerateTerrain() 
{
	if ( !terrain )
		return;
	
	if ( terrain.terrainData.heightmapResolution != size )
		terrain.terrainData.heightmapResolution = size;
	
	terrain.terrainData.SetHeights( 0, 0, dataArray );
}