0 Favourites

[PHP] Generating Random Terrain Fractal

  • 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);
    }
    
    ?>
  • well that's very interesting!

    Think you could bias certain terrain areas to create stuff like biomes and other terrain features?

  • Construct 3

    Buy Construct 3

    Develop games in your browser. Powerful, performant & highly capable.

    Buy Now Construct 3 users don't see these ads
  • Think you could bias certain terrain areas to create stuff like biomes and other terrain features?

    I could.

Jump to:
Active Users
There are 1 visitors browsing this topic (0 users and 1 guests)