phpterrainprocedural-generation

Algo. terrain generation - evaporation calculation issue


Creating procedural terrain I cannot get an evaporation matrix and find why my algorithm is wrong. How my program works:

Creation of chunk steps:

When generating the terrain I use the following constants:

private const chunkHeights = array( 'max' => 64, 'min' => -64 ); // min and max height
private const chunkSize = 129; // width = height for 1 chunk
private const chunkVariation = 1; // max variation from a position to its neighbor

Heightfield script and my rivers script are both working as expected. I try updating the evaporation file of a given chunk:

    private static function updateEvaporation( $pChunkX, $pChunkY ) {
        $chunks = array();
        $seaOrRiver = 0;
        
        for( $y = ( $pChunkY - 1 ); $y <= ( $pChunkY + 1 ); $y++ ) {
            for( $x = ( $pChunkX - 1 ); $x <= ( $pChunkX + 1 ); $x++ ) {
                $chunkFolder = 'terrain/chunk'.$x.'x'.$y.'/';
                $tmpChunkFolder = 'terrain/tmp_chunk'.$x.'x'.$y.'/';
                
                $chunkHf = null;
                $chunkRivers = null;
                $chunkEvaporation = null;
                
                if( file_exists( $chunkFolder ) ) {
                    $chunkHf = new HeightField();
                    $chunkHfData = file_get_contents( $chunkFolder.'heightfield.json' );
                    $chunkHf->fromString( $chunkHfData );
                    
                    $chunkRivers = json_decode( file_get_contents( $chunkFolder.'rivers.json' ), true );
                    
                    $chunkEvaporation = json_decode( file_get_contents( $chunkFolder.'evaporation.json' ), true );
                } elseif( file_exists( $tmpChunkFolder ) ) {
                    $chunkHf = new HeightField();
                    $chunkHfData = file_get_contents( $tmpChunkFolder.'heightfield.json' );
                    $chunkHf->fromString( $chunkHfData );
                    
                    $chunkRivers = json_decode( file_get_contents( $tmpChunkFolder.'rivers.json' ), true );
                    
                    $chunkEvaporation = json_decode( file_get_contents( $tmpChunkFolder.'evaporation.json' ), true );
                }
                
                if( ( $chunkHf != null ) && ( $chunkRivers != null ) && ( $chunkEvaporation != null ) ) {
                    for( $chunkY = 0; $chunkY < self::chunkSize; $chunkY++ ) {
                        for( $chunkX = 0; $chunkX < self::chunkSize; $chunkX++ ) {
                            if( ( $chunkHf->getHeight( $chunkX, $chunkY ) <= 0 ) || ( $chunkRivers[$chunkY][$chunkX] == 1 ) ) {
                                $chunkEvaporation[$chunkY][$chunkX] = 0;
                                $seaOrRiver++;
                            } else {
                                $chunkEvaporation[$chunkY][$chunkX] = null;
                            }
                        }
                    }
                    
                    $chunks[$x.'x'.$y] = array(
                        'hf' => $chunkHf,
                        'rivers' => $chunkRivers,
                        'evaporation' => $chunkEvaporation
                    );
                }
            }
        }
        
        echo '<br>0 => ['.$seaOrRiver.']<br>';
        
        $eLevel = 0;
        
        while( $eLevel <= max( 0, self::chunkHeights['max'] ) ) {
            $byLevel = 0;
            
            for( $y = 0; $y < ( 3 * self::chunkSize ); $y++ ) {
                for( $x = 0; $x < ( 3 * self::chunkSize ); $x++ ) {
                    $chunkX = floor( $x / self::chunkSize ) + $pChunkX - 1;
                    $chunkY = floor( $y / self::chunkSize ) + $pChunkY - 1;
                    $chunkXx = $x % self::chunkSize;
                    $chunkYy = $y % self::chunkSize;
                    
                    if( isset( $chunks[$chunkX.'x'.$chunkY] ) ) {
                        if( $chunks[$chunkX.'x'.$chunkY]['evaporation'][$chunkYy][$chunkXx] == $eLevel ) {
                            for( $yy = ( $y - 1 ); $yy <= ( $y + 1 ); $yy++ ) {
                                for( $xx = ( $x - 1 ); $xx <= ( $x + 1 ); $xx++ ) {
                                    $chunkXX = floor( $xx / self::chunkSize ) + $pChunkX - 1;
                                    $chunkYY = floor( $yy / self::chunkSize ) + $pChunkY - 1;
                                    $chunkXXx = $xx % self::chunkSize;
                                    $chunkYYy = $yy % self::chunkSize;
                                    
                                    if( isset( $chunks[$chunkXX.'x'.$chunkYY]['evaporation'][$chunkYYy][$chunkXXx] ) ) {
                                        if( $chunks[$chunkXX.'x'.$chunkYY]['evaporation'][$chunkYYy][$chunkXXx] == null ) {
                                            $chunks[$chunkXX.'x'.$chunkYY]['evaporation'][$chunkYYy][$chunkXXx] = ( $eLevel + 1 );
                                            
                                            $byLevel++;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            
            echo ( $eLevel + 1 ).' => ['.$byLevel.']<br>';
            $eLevel++;
        }
        
        if( file_exists( 'terrain/chunk'.$pChunkX.'x'.$pChunkY ) ) {
            file_put_contents( 'terrain/chunk'.$pChunkX.'x'.$pChunkY.'/evaporation.json', json_encode( $chunks[$pChunkX.'x'.$pChunkY]['evaporation'] ) );
        } else {
            file_put_contents( 'terrain/tmp_chunk'.$pChunkX.'x'.$pChunkY.'/evaporation.json', json_encode( $chunks[$pChunkX.'x'.$pChunkY]['evaporation'] ) );
        }
        
        return $chunks[$pChunkX.'x'.$pChunkY]['evaporation'];
    }

Here are some explanations:

This is the first main loop. After this I have a $chunks list with for all chunks a heightfield, rivers matrix and incomplete evaporation matrix (where evaporation values are 0 or null).

My second main loop :

Result once I load 1 chunk :

evaporation

Here white parts are positions where $pEvaporation[$y][$x] == null and the rest is the evaporation ratio from ( rgb( 0, 0, 0 ) to rgb( 255, 0, 0 ) ). Only the sea (and rivers) are generated (river is not visible because of the order I generate and get the data). Printed text corresponds to <level I'm checking> => [<number of null neighbors found>].

Others matrix previewing:

heights and water

Do you know what is going wrong with my script?


Solution

  • I found my mistake:

    PHP considers 0 == null but 0 !== null, I corrected this on my script (for evaporation testing).