PHP script (and eventually Construct 2) that generates fractal terrain using Diamond Square Algorithm. The three links below show (in order), implementation inside Construct 2, Fractal Terrain with Gradient, Raw Fractal
In Action Inside Construct
Terrain Example
Raw Example
<?
$DATA_SIZE = 513;
$SEED = 0.0;
$data = array();
$maxY = -1E32;
$minY = 1E32;
mt_srand($DATA_SIZE);
$RAND_MAX = mt_getrandmax();
$data[0][0] = $data[0][$DATA_SIZE-1] = $data[$DATA_SIZE-1][0] = $data[$DATA_SIZE-1][$DATA_SIZE-1] = $SEED;
$h = 200.0;
for ($sideLength = $DATA_SIZE-1; $sideLength >= 2; $sideLength /= 2, $h /= 2.0) {
$halfSide = $sideLength/2;
for($x=0; $x<$DATA_SIZE-1;$x+=$sideLength) {
?for($y=0; $y<$DATA_SIZE-1; $y+=$sideLength) {
//x,y is upper left corner of the square
//calculate average of existing corners
$avg = $data[$x][$y] + //top left
$data[$x+$sideLength][$y] + //top right
$data[$x][$y+$sideLength] + //lower left
$data[$x+$sideLength][$y+$sideLength]; //lower right
$avg = $avg / 4.0;
//center is average plus random offset in the range (-h, h)
$offset = (-$h) + rand() * ($h - (-$h)) / $RAND_MAX;
$data[$x+$halfSide][$y+$halfSide] = $avg + $offset;
?} //for y
} // for x
//Generate the diamond values
//Since diamonds are staggered, we only move x by half side
//NOTE: if the data shouldn't wrap the x < DATA_SIZE and y < DATA_SIZE
for ($x=0; $x<$DATA_SIZE; $x+=$halfSide) {
for ($y=($x+$halfSide)%$sideLength; $y<$DATA_SIZE; $y+=$sideLength) {
//x,y is center of diamond
//we must use mod and add DATA_SIZE for subtraction
//so that we can wrap around the array to find the corners
$avg =
$data[($x-$halfSide+$DATA_SIZE)%$DATA_SIZE][$y] + //left of center
$data[($x+$halfSide)%$DATA_SIZE][$y] + //right of center
$data[$x][($y+$halfSide)%$DATA_SIZE] + //below center
$data[$x][($y-$halfSide+$DATA_SIZE)%$DATA_SIZE]; //above center
$avg = $avg / 4.0;
//new value = average plus random offset
//calc random value in the range (-h,+h)
$offset = (-$h) + rand() * ($h - (-$h)) / $RAND_MAX;
$avg = $avg + $offset;
//update value for center of diamond
$data[$x][$y] = $avg;
//wrap values on the edges
//remove this and adjust loop condition above
//for non-wrapping values
//if ($x == 0) $data[$DATA_SIZE-1][$y] = $avg;
//if ($y == 0) $data[$x][$DATA_SIZE-1] = $avg;
} //for y
?} //for x
} //for sideLength
//Calculate minY and maxY values
for ($i = 0; $i<$DATA_SIZE-1; $i++){
for($j=0; $j<$DATA_SIZE-1; $j++) {
?if ($data[$i][$j] > $maxY){
$maxY = $data[$i][$j];
?}
?if ($data[$i][$j] < $minY){
$minY = $data[$i][$j];
?}
}
}
createTerrain();
// start of functions
function createTerrain(){
global $DATA_SIZE, $data, $maxY, $minY;
$px = 0;
$py = 0;
$pz = 0;
$pallete = array();
// I use a 256px wide image gradient here to represent the colors for elevation
$im = imagecreatefrompng("map.png");
for($a = 0;$a<= 256;$a++){
?$rgb = imagecolorat($im, $a+1, 1);
?$r = ($rgb >> 16) & 0xFF;
?$g = ($rgb >> 8) & 0xFF;
?$b = $rgb & 0xFF;
?$pallete[$a][r] = $r;
?$pallete[$a][g] = $g;
?$pallete[$a][b] = $b;
}
$gd = imagecreatetruecolor($DATA_SIZE, $DATA_SIZE);
for($x=0; $x < $DATA_SIZE-1; $x++){
for($y=0; $y < $DATA_SIZE-1; $y++){
??//populate the point struct
$px = $x;
$py = $data[$x][$y]; // color || height
$pz = $y;
??//change range to 0..1
??$py = ($py - $minY) / ($maxY - $minY);
??$py = floor($py * 255);
$c[0] = $pallete[$py][r];
$c[1] = $pallete[$py][g];
$c[2] = $pallete[$py][b];
??$color = imagecolorallocate($gd, $c[0], $c[1], $c[2]);
??imagesetpixel($gd, round($y),round($x), $color);
}
}
header('Content-Type: image/png');
imagepng($gd);
imagedestroy($gd);
}
function html2rgb($color){
if ($color[0] == '#')
$color = substr($color, 1);
if (strlen($color) == 6)
list($r, $g, $b) = array($color[0].$color[1],
$color[2].$color[3],
$color[4].$color[5]);
elseif (strlen($color) == 3)
list($r, $g, $b) = array($color[0].$color[0], $color[1].$color[1], $color[2].$color[2]);
else
return false;
$r = hexdec($r); $g = hexdec($g); $b = hexdec($b);
return array($r, $g, $b);
}
?>