diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..eda70ff --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +LDLIBS = -lm + +all: CFLAGS = -O3 -ansi -pedantic +all: ds + +debug: CFLAGS = -g -ansi -pedantic +debug: ds + +ds: main.o ds.o + +main.o: main.c ds.h + +ds.o: ds.c ds.h + +clean: + $(RM) *.o ds diff --git a/README.md b/README.md index f205850..3747930 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,17 @@ DS-Island-generator =================== A procedural island generator based on the diamond-square mid point displacement and the particle deposition algorithms. + +Compile with: + + make + +Execute as: + + ./ds N PREFIX + +Where N is a positive integer greater than zero and PREFIX is a string to append to the output file names. + +The diamond-square mid point displacement implementation is a port of the code outlined in [this post] [1] at Stack Overflow. + + [1]: http://stackoverflow.com/a/2773032 diff --git a/ds.c b/ds.c new file mode 100644 index 0000000..ddf21b1 --- /dev/null +++ b/ds.c @@ -0,0 +1,209 @@ +#ifndef __cplusplus + +#include +#include +#include +#include +#include +#include + +#else + +#include +#include +#include +#include +#include +#include + +#endif + +#include "ds.h" + +static const float SEED = 1000.0; + +static float randomFloat (); + +/** + * Port of the java implementation outlined here http://stackoverflow.com/a/2773032 + */ +void ds ( float *** map, const unsigned int DATA_SIZE ) { + float avg, r, h = SEED / 2.0; + int sideLength, halfSide, x, y; + + srand( time( NULL ) ); + + ( * map )[ 0 ][ 0 ] = ( * map )[ 0 ][ DATA_SIZE - 1 ] = ( * map )[ DATA_SIZE - 1 ][ 0 ] = ( * map )[ DATA_SIZE - 1 ][ DATA_SIZE - 1 ] = h; + + 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 ) { + avg = ( * map )[ x ][ y ] + + ( * map )[ x + sideLength ][ y ] + + ( * map )[ x ][ y + sideLength ] + + ( * map )[ x + sideLength ][ y + sideLength ]; + avg /= 4.0; + + r = randomFloat(); + + ( * map )[ x + halfSide ][ y + halfSide ] = avg + ( r * 2.0 * h ) - h; + } + } + + for ( x = 0; x < DATA_SIZE - 1; x += halfSide ) { + for ( y = ( x + halfSide ) % sideLength; y < DATA_SIZE - 1; y += sideLength ) { + avg = ( * map )[ ( x - halfSide + DATA_SIZE ) % DATA_SIZE ][ y ] + + ( * map )[ ( x + halfSide ) % DATA_SIZE ][ y ] + + ( * map )[ x ][ ( y + halfSide ) % DATA_SIZE ] + + ( * map )[ x ][ ( y - halfSide + DATA_SIZE ) % DATA_SIZE ]; + avg /= 4.0; + + r = randomFloat(); + + avg = avg + ( r * 2.0 * h ) - h; + + ( * map )[ x ][ y ] = avg; + + if ( x == 0 ) ( * map )[ DATA_SIZE - 1 ][ y ] = avg; + if ( y == 0 ) ( * map )[ x ][ DATA_SIZE - 1 ] = avg; + } + } + } +} + +void island ( int ***imap, unsigned int MAP_SIZE ) { + const int part = ( MAP_SIZE * 3000 ) / 513; + const int life = ( MAP_SIZE * 5000 ) / 513; + int x, y, dx, dy, i, c, n, nx, ny, l; + + srand( time( NULL ) ); + + for ( i = 0; i < part; ++i ) { + x = ( MAP_SIZE / 4 ) + ( random() % ( MAP_SIZE / 2 ) ); + y = ( MAP_SIZE / 4 ) + ( random() % ( MAP_SIZE / 2 ) ); + + for ( l = life; l >= 0; l-- ) { + dx = ( random() % 3 ) - 1; + dy = ( random() % 3 ) - 1; + + ( * imap )[ x ][ y ] += 1; + + c = ( * imap )[ x ][ y ]; + + nx = ( x + dx ); + ny = ( y + dy ); + + if ( nx < 0 || nx >= MAP_SIZE || ny < 0 || ny >= MAP_SIZE ) { + l--; + continue; + } + + if ( ( * imap )[ nx ][ ny ] <= c ) { + ( * imap )[ nx ][ ny ] += 1; + } + + x = nx; + y = ny; + } + } +} + +void norm ( float *** fmap, unsigned int MAP_SIZE ) { + int i, j; + float max = -FLT_MAX; + + for ( i = 0; i < MAP_SIZE; ++i ) { + for ( j = 0; j < MAP_SIZE; ++j ) { + max = abs( ( * fmap )[ i ][ j ] ) > max ? abs ( ( * fmap )[ i ][ j ] ) : max; + } + } + + for ( i = 0; i < MAP_SIZE; ++i ) { + for ( j = 0; j < MAP_SIZE; ++j ) { + ( * fmap )[ i ][ j ] += max; + } + } + + for ( i = 0; i < MAP_SIZE; ++i ) { + for ( j = 0; j < MAP_SIZE; ++j ) { + max = abs( ( * fmap )[ i ][ j ] ) > max ? abs ( ( * fmap )[ i ][ j ] ) : max; + } + } + + for ( i = 0; i < MAP_SIZE; ++i ) { + for ( j = 0; j < MAP_SIZE; ++j ) { + ( * fmap )[ i ][ j ] /= max; + } + } +} + +void normInt ( int *** map, unsigned int MAP_SIZE ) { + int i, j, max = -1; + + for ( i = 0; i < MAP_SIZE; ++i ) { + for ( j = 0; j < MAP_SIZE; ++j ) { + max = max < ( * map )[ i ][ j ] ? ( * map )[ i ][ j ] : max; + } + } + + for ( i = 0; i < MAP_SIZE; ++i ) { + for ( j = 0; j < MAP_SIZE; ++j ) { + ( * map )[ i ][ j ] = ( int ) ( ( ( float ) ( * map )[ i ][ j ] / ( float ) max ) * 255.0 ); + } + } +} + +extern void mult ( float *** fmap, int *** imap, unsigned int MAP_SIZE ) { + int i, j; + + for ( i = 0; i < MAP_SIZE; ++i ) { + for ( j = 0; j < MAP_SIZE; ++j ) { + /*fprintf ( stderr, "%f %d\n", ( * fmap )[ i ][ j ], ( * imap )[ i ][ j ] );*/ + ( * imap )[ i ][ j ] = ( int )( ( ( * fmap )[ i ][ j ] * ( ( float ) ( * imap )[ i ][ j ] / 255.0f ) ) * 250.0f ); + } + } + +} + +void smooth ( int *** map, unsigned int MAP_SIZE) { + int i, j, avg, **temp; + + temp = ( int ** ) malloc ( sizeof ( int * ) * MAP_SIZE ); + for ( i = 0; i < MAP_SIZE; ++i ){ + temp[ i ] = ( int * ) malloc ( sizeof ( int ) * MAP_SIZE ); + } + + for ( i = 0; i < MAP_SIZE; ++i ) { + for ( j = 0; j < MAP_SIZE; ++j ) { + avg = 0; + + avg += ( * map )[ i ][ j ]; + avg += ( * map )[ ( i + 1 ) % MAP_SIZE ][ j ]; + avg += ( * map )[ ( i - 1 ) < 0 ? MAP_SIZE - 1 : i - 1 ][ j ]; + avg += ( * map )[ i ][ ( j + 1 ) % MAP_SIZE ]; + avg += ( * map )[ i ][ ( j - 1 ) < 0 ? MAP_SIZE - 1 : j - 1 ]; + avg += ( * map )[ ( i + 1 ) % MAP_SIZE ][ ( j + 1 ) % MAP_SIZE ]; + avg += ( * map )[ ( i + 1 ) % MAP_SIZE ][ ( j - 1 ) < 0 ? MAP_SIZE - 1 : j - 1 ]; + avg += ( * map )[ ( i - 1 ) < 0 ? MAP_SIZE - 1 : i - 1 ][ ( j + 1 ) % MAP_SIZE ]; + avg += ( * map )[ ( i - 1 ) < 0 ? MAP_SIZE - 1 : i - 1 ][ ( j - 1 ) < 0 ? MAP_SIZE - 1 : j - 1 ]; + + avg /= 9; + + temp[ i ][ j ] = avg; + } + } + + for ( i = 0; i < MAP_SIZE; ++i ){ + memcpy ( ( * map )[ i ], temp[ i ], MAP_SIZE * sizeof ( int ) ); + } + + free ( temp ); +} + +static float randomFloat() { + float r = ( float ) random() / ( float ) RAND_MAX; + r = ( r * 2.0 ) - 1.0; + return r; +} diff --git a/ds.h b/ds.h new file mode 100644 index 0000000..d4eecc2 --- /dev/null +++ b/ds.h @@ -0,0 +1,43 @@ +#ifndef DS_H +#define DS_H + +#ifdef __cplusplus +extern "C" { +#endif + + /** + * Generate a diamond-square fractal map. + */ + extern void ds ( float ***, const unsigned int ); + + /** + * Generate a mask using particle deposition. + */ + extern void island ( int ***, unsigned int ); + + /** + * Normalize a float matrix between 0 and 1. + */ + extern void norm ( float ***, unsigned int ); + + /** + * Normalize an int matrix between 0 and 255. + */ + extern void normInt ( int ***, unsigned int ); + + /** + * Perform a 3x3 average blur. + */ + extern void smooth ( int ***, unsigned int ); + + /** + * Multiply the diamond square map with the island mask. + * Both matrices must have been normalized before. + */ + extern void mult ( float ***, int *** , unsigned int); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/main.c b/main.c new file mode 100644 index 0000000..e3e9d30 --- /dev/null +++ b/main.c @@ -0,0 +1,223 @@ +/** + * Test main for the Diamond-Square island generator. + */ + +#include +#include +#include +#include +#include + +#include "ds.h" + +struct COLOR { + unsigned char r; + unsigned char g; + unsigned char b; +}; + +static void printMapAsPgm ( int ***, const unsigned int, char * ); +static void printMapAsPgm2 ( int ***, const unsigned int, char * ); +static void printFloatMapAsPgm ( float ***, const unsigned int, char * ); +static void printMapAsPpm ( int ***, const unsigned int, char * ); +static struct COLOR getTerrainType( int ); + +int main ( int argc, char **argv ) { + int i, m, ** imap; + unsigned int n = 0; + float ** map; + + if ( argc < 3 ) { + fprintf ( stderr, "Usage: %s N \nN is an integer greater than 0.\n", argv[0] ); + return EXIT_FAILURE; + } + + m = atoi( argv[1] ); + + if ( m <= 0 ) { + fprintf ( stderr, "N must be an integer greater than zero: %d\n", m ); + return EXIT_FAILURE; + } + + n = (unsigned int)( pow ( 2.0, m ) ) + 1; + + map = ( float ** ) malloc ( sizeof ( float * ) * n); + for ( i = 0; i < n; ++i ) { + map[ i ] = ( float * ) calloc ( n, sizeof ( float ) ); + } + + imap = ( int ** ) malloc ( sizeof ( int * ) * n); + for ( i = 0; i < n; ++i ) { + imap[ i ] = ( int * ) calloc ( n, sizeof ( int ) ); + } + + ds ( &map, n ); + island ( &imap, n ); + normInt ( &imap, n ); + printMapAsPgm2 ( &imap, n, argv[2] ); + norm ( &map, n ); + printFloatMapAsPgm ( &map, n, argv[2] ); + mult ( &map, &imap, n ); + smooth( &imap, n ); + normInt ( &imap, n ); + printMapAsPgm ( &imap, n, argv[2] ); + printMapAsPpm ( &imap, n, argv[2] ); + + for ( i = 0; i < n; ++i ) { + free ( map[ i ] ); + } + free ( map ); + + for ( i = 0; i < n; ++i ) { + free ( imap[ i ] ); + } + free ( imap ); + + return EXIT_SUCCESS; +} + +void printFloatMapAsPgm ( float *** map, const unsigned int MAP_SIZE, char * fn ) { + int i, j, max = -1; + FILE * f; + char * filename = ( char * ) malloc ( ( strlen ( "ds_map.pgm" ) + strlen ( fn ) ) * sizeof ( char ) + 1); + + strcpy ( filename, fn ); + strcat ( filename, "ds_map.pgm" ); + + f = fopen ( filename , "w" ); + + fprintf ( f, "P2\n" ); + fprintf ( f, "%u %u\n", MAP_SIZE, MAP_SIZE ); + fprintf ( f, "255\n" ); + + for ( i = 0; i < MAP_SIZE; ++i ) { + for ( j = 0; j < MAP_SIZE; ++j ) { + fprintf( f, "%d ", ( int )( ( * map )[ i ][ j ] * 255.0f ) ); + } + fprintf( f, "\n" ); + } + + fclose ( f ); + free ( filename ); +} + +void printMapAsPgm ( int *** map, const unsigned int MAP_SIZE, char * fn ) { + int i, j, max = -1; + FILE * f; + char * filename = ( char * ) malloc ( ( strlen ( "hmmap.pgm" ) + strlen ( fn ) ) * sizeof ( char ) + 1); + + strcpy ( filename, fn ); + strcat ( filename, "hmmap.pgm" ); + + f = fopen ( filename , "w" ); + + f = fopen ( filename, "w" ); + + for ( i = 0; i < MAP_SIZE; ++i ) { + for ( j = 0; j < MAP_SIZE; ++j ) { + max = max < ( * map )[ i ][ j ] ? ( * map )[ i ][ j ] : max; + } + } + + fprintf ( f, "P2\n" ); + fprintf ( f, "%u %u\n", MAP_SIZE, MAP_SIZE ); + fprintf ( f, "%d\n", max ); + + for ( i = 0; i < MAP_SIZE; ++i ) { + for ( j = 0; j < MAP_SIZE; ++j ) { + fprintf( f, "%d ", ( * map )[ i ][ j ] ); + } + fprintf( f, "\n" ); + } + + fclose ( f ); +} + +void printMapAsPgm2 ( int *** map, const unsigned int MAP_SIZE, char * fn ) { + int i, j, max = -1; + FILE * f; + char * filename = ( char * ) malloc ( ( strlen ( "is.pgm" ) + strlen ( fn ) ) * sizeof ( char ) + 1); + + strcpy ( filename, fn ); + strcat ( filename, "is.pgm" ); + + f = fopen ( filename , "w" ); + + for ( i = 0; i < MAP_SIZE; ++i ) { + for ( j = 0; j < MAP_SIZE; ++j ) { + max = max < ( * map )[ i ][ j ] ? ( * map )[ i ][ j ] : max; + } + } + + fprintf ( f, "P2\n" ); + fprintf ( f, "%u %u\n", MAP_SIZE, MAP_SIZE ); + fprintf ( f, "%d\n", max ); + + for ( i = 0; i < MAP_SIZE; ++i ) { + for ( j = 0; j < MAP_SIZE; ++j ) { + fprintf( f, "%d ", ( * map )[ i ][ j ] ); + } + fprintf( f, "\n" ); + } + + fclose ( f ); + free ( filename ); +} + +void printMapAsPpm ( int *** map, const unsigned int MAP_SIZE, char * fn ) { + struct COLOR c; + int i, j; + FILE * f; + char * filename = ( char * ) malloc ( ( strlen ( "map.ppm" ) + strlen ( fn ) ) * sizeof ( char ) + 1); + + strcpy ( filename, fn ); + strcat ( filename, "map.ppm" ); + + f = fopen ( filename, "w" ); + + fprintf ( f, "P3\n" ); + fprintf ( f, "%u %u\n", MAP_SIZE, MAP_SIZE ); + fprintf ( f, "255\n" ); + + for ( i = 0; i < MAP_SIZE; ++i ) { + for ( j = 0; j < MAP_SIZE; ++j ) { + c = getTerrainType( ( * map )[ i ][ j ] ); + fprintf( f, "%d %d %d\t", c.r, c.g, c.b ); + } + fprintf( f, "\n" ); + } + + fclose ( f ); + free ( filename ); +} + +struct COLOR getTerrainType( int h) { + struct COLOR c; + + if ( h < 30 ) { + if ( h < 15 ) { + c.r = 64; c.g = 64; c.b = 200; + return c; + } else { + c.r = 128; c.g = 128; c.b = 255; + return c; + } + } else { + if ( h < 40 ) { + c.r = 255; c.g = 234; c.b = 150; + return c; + } else if ( h >= 40 && h < 100 ) { + c.r = 16; c.g = 180; c.b = 40; + return c; + } else if ( h >= 100 && h < 150 ) { + c.r = 70; c.g = 164; c.b = 25; + return c; + } else if ( h >= 180 ) { + c.r = 200; c.g = 200; c.b = 200; + return c; + } else { + c.r = 143; c.g = 111; c.b = 81; + return c; + } + } +}