diff options
-rw-r--r-- | README.md | 51 | ||||
-rw-r--r-- | src/config.h | 26 | ||||
-rw-r--r-- | src/gen_tiles.c | 2 | ||||
-rw-r--r-- | src/main.c | 70 | ||||
-rw-r--r-- | src/ppm.c | 6 | ||||
-rw-r--r-- | src/ppm.h | 4 | ||||
-rw-r--r-- | src/tilemap.c | 58 | ||||
-rw-r--r-- | src/tilemap.h | 23 | ||||
-rw-r--r-- | src/tiles.c | 6 | ||||
-rw-r--r-- | src/tiles.h | 2 | ||||
-rw-r--r-- | src/typedef.h | 4 |
11 files changed, 181 insertions, 71 deletions
@@ -1,37 +1,58 @@ -### Wave function collapse +### Wave Function Collapse This an implementation of very primitive tile-based wave function collapse. It uses tiles to generate an image, similar to this: -![Demo 0 of the program](demo/demo0.png) ![Demo 1 of the program](demo/demo1.png) +![Demo 0 of the program](demo/demo0.png) ![Demo 1 of the program](demo/demo1.png) -It generates the tiles as ppm, then generates the image, similar to the demo image as ppm. There is a script to turn the ppm files into png using imagemagick. +It can use colored tileset like the one included in the files directory, which is copied from +Mxgmn's Wave Function Collapse README. -All files including the tiles are saved in the files directory +There is a script to turn the generated images into png using imagemagick. +All files including the tiles are saved in the files directory. ### Build To build use `./build.sh` To build and run use `./build.sh run` +To clean use `./build.sh clean` + +Two files will be build in the bin directory, +`bin/gen_tiles` and `bin/wfc` + +I am pretty sure the `bin/wfc` should work on Windows, +`bin/gen_tiles` uses linux-only header to +create the directories for the files (files and files/tiles) + +It would be easy to make everything work on windows, +I just dont want to take my time and make it cross platform. + +And I dont want to use Makefile, because I dont like it, +the project is not too big, a shell script is enough. ### How it works -There are two programs, the first one generates the tiles and a special little file which includes the information -if a tile should connect to another tile. The tiles are specified as bitmaps, their symmetry type, and on which sides it connects. -And the program generates every tile and all of its rotations as ppm files. +Firstly `bin/wfc --help` can be used for some info on program arguments + +The main program `bin/wfc` uses the tiles in `files/tiles`, which are ppm files. +There is a file called `tiles.dat` in the same directory that holds information about the tiles, and it enables `bin/wfc` to generate the tile rotations + +The `bin/gen_tiles` generates the `tiles.dat` file and optionally the tiles themselves. +There is a file called `config.h` under the src directory which is used to specify things about +`tiles.dat` and basically configuring `bin/gen_tiles`. More info in the file as comments. + +To use a tileset, different from the one that can be generated from `bin/gen_tiles` +you need to specify the tile-types you are going to use in `src/config.h`, then copy-paste +the tiles into `files/tiles` directory and then name them `tile_<num>.ppm`, where +num is the index of the tile in the `tiles_to_load` array in `src/config.h` (Not the tile index in the table). -The other program reads the ppm tiles and the tile-info file (tiles.bin) and it creates the tilemap. -Using the tile-info file it internally creates a table of bit masks to apply to the neighbors of collapsed tiles. -Other than that it functions very similar to a normal a normal simple tiled model ### Limitations -1. For now i am using a second program to generate the tiles and their rotation - as explained in the previous section -2. No more than 63 tiles, because i am using a 64-bit integer to store the superpositions +1. No more than 63 tiles, because I am using a 64-bit integer to store the superpositions of tiles -3. Only tile based wave function collapse, i dont understand the other more complex one +2. Only tile based wave function collapse, I dont understand the other more complex one ### References -[wave function collapse readme](https://github.com/mxgmn/WaveFunctionCollapse) +[Mxgmn's Wave Function Collapse README](https://github.com/mxgmn/WaveFunctionCollapse) diff --git a/src/config.h b/src/config.h index c9743e1..8e2910b 100644 --- a/src/config.h +++ b/src/config.h @@ -1,5 +1,31 @@ +// comment this if you dont want to +// generate the tiles, only the +// tiles.dat file #define GENERATE_PPM_TILES +/* The TILES are + +----------------------------------------------- +|0: ... blank | 1: ... T-piece (down) | +| ... X symetry | ### T symetry | +| ... | .#. | +|-------------------+-------------------------| +|2: .#. cross | 3: ... bar | +| ### X symetry | ### I symetry | +| .#. | ... | +|-------------------+-------------------------| +|4: .#. corner | | +| .## T symetry | | +| ... | | +----------------------------------------------- + +The number are the tile indexes, the values that you +need to put in the tiles_to_load array + + */ + +// Array for which tiles to include the tiles.dat int tiles_to_load[] = { + 0, 1, }; diff --git a/src/gen_tiles.c b/src/gen_tiles.c index 53e1012..6a73872 100644 --- a/src/gen_tiles.c +++ b/src/gen_tiles.c @@ -93,7 +93,7 @@ void gen_rotations() char syms[TILES_CAP]; size_t sym_sz = 0; - for(int n = 0; n < tiles_sz; n++) + for(size_t n = 0; n < tiles_sz; n++) { int i = tiles_to_load[n]; save(tiles[i], tiles_connections[i], n); @@ -1,19 +1,27 @@ #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <time.h> #include "typedef.h" #include "ppm.h" #include "tiles.h" #include "tilemap.h" +// amount of tiles size_t TILES; +// width and height of a single tile size_t TILE_WIDTH; size_t TILE_HEIGHT; +// width and height of the tilemap +// to generate size_t SWIDTH = 30; size_t SHEIGHT = 30; +time_t SEED; +size_t SCALE = 9; + int get_least_entropy_index() { // array of indexes with the least entropy @@ -49,14 +57,13 @@ int get_least_entropy_index() void collapse_this(int i) { - int n = 0; - if(count_entropy(i) == 0) { fprintf(stderr, "ERROR: No possible tiles for this position: %d\n", i); exit(EXIT_FAILURE); } // this bad + int n = 0; do { n = rand() % TILES; if(!(is_set(i, n))) @@ -65,6 +72,7 @@ void collapse_this(int i) collapse(i, n); } while(!(has_collapsed(i))); + // apply a bitmask, based on the direction if(i / SWIDTH != 0) // up if(!(has_collapsed(i-SWIDTH))) mask(i-SWIDTH, n, 0); @@ -82,15 +90,56 @@ void collapse_this(int i) mask(i+SWIDTH, n, 3); } -int main(void) +void manage_arguments(int argc, char **argv) { - load_tiles(); - print_tiles(); + if(argc == 1) return; + + for(int n = 1; n < argc; n++) + { + char *arg = argv[n]; + + if((strcmp(arg, "-h") == 0) || (strcmp(argv[1], "--help") == 0)) { + puts("Usage: wfc [OPTION] [VALUE(S)]...\n"); + puts("The options are the following: (No option is mandatory)\n"); + puts("-h, --help Get help"); + puts("-d Change width and height of the tilemap, defaut is 30x30"); + puts(" args: 2 numbers; width and height seperated by space"); + puts("-s Change the seed for generating the tilemap, default is time(0)"); + puts(" args: 1 number; the seed"); + puts("-m Change the number, that the tilemap is scaled by"); + puts(" args: 1 number; scaler"); + exit(EXIT_SUCCESS); + } else if(strcmp(arg, "-d") == 0) { + if(!(n+2 < argc)) goto error; + SWIDTH = atoi(argv[++n]); + SHEIGHT = atoi(argv[++n]); + } else if(strcmp(arg, "-s") == 0) { + if(!(n+1 < argc)) goto error; + SEED = atoi(argv[++n]); + } else if(strcmp(arg, "-m") == 0) { + if(!(n+1 < argc)) goto error; + SCALE = atoi(argv[++n]); + } else goto error; - time_t seed = 69; - srand(seed); - printf("The Seed is %ld\n", seed); + } + return; +error: + fputs("ERROR: An error has accured with the given arguments", stderr); + exit(EXIT_FAILURE); +} + +int main(int argc, char **argv) +{ + // SEED = 69; + SEED = time(0); + + manage_arguments(argc, argv); + + srand(SEED); + printf("The Seed is %ld\n", SEED); + + load_tiles(); generate_tile_masks(get_tile_connections()); init_tilemap(); @@ -113,7 +162,6 @@ int main(void) for(size_t j = 0; j < SWIDTH; j++) { size_t t = get_collapsed_tile(i * SWIDTH + j); - if(t == TILES) exit(-124); for(size_t y = 0; y < TILE_HEIGHT; y++) for(size_t x = 0; x < TILE_WIDTH; x++) @@ -127,8 +175,8 @@ int main(void) char file_name[64] = {0}; - sprintf(file_name, "files/file_%ld.ppm", seed); - save_as_ppm(file_name, image, img_wdt, img_hgt, 9); + sprintf(file_name, "files/file_%ld.ppm", SEED); + save_as_ppm(file_name, image, img_wdt, img_hgt, SCALE); printf("Saved file with name: %s\n", file_name); free(image); @@ -29,7 +29,7 @@ void save_as_ppm(char* file_path, small_t *t, size_t width, size_t height, size_ fclose(fp); } -small_t *load_from_ppm(char *file_path, size_t *width, size_t *height) +char *load_from_ppm(char *file_path, size_t *width, size_t *height) { FILE *fp = fopen(file_path, "rb"); if(!fp) { @@ -55,7 +55,7 @@ small_t *load_from_ppm(char *file_path, size_t *width, size_t *height) exit(1); } - small_t *t = malloc((*width) * (*height ) * 3); + char *t = malloc((*width) * (*height ) * 3); fread(t, *width*3, *height, fp); @@ -64,7 +64,7 @@ small_t *load_from_ppm(char *file_path, size_t *width, size_t *height) return t; } -void free_ppm(small_t *t) +void free_ppm(char *t) { free(t); } @@ -3,8 +3,8 @@ void save_as_ppm(char* file_path, small_t *t, size_t width, size_t height, size_t scaler); -small_t *load_from_ppm(char *file_path, size_t *width, size_t *height); +char *load_from_ppm(char *file_path, size_t *width, size_t *height); -void free_ppm(small_t *img); +void free_ppm(char *img); #endif diff --git a/src/tilemap.c b/src/tilemap.c index 70d534f..85d76bd 100644 --- a/src/tilemap.c +++ b/src/tilemap.c @@ -14,58 +14,62 @@ big_t tilemap[TILEMAP_CAP]; // least significant bit is tile index 0 big_t tile_masks[TILES_CAP][4]; -void set(int i, int n) +void set(int t, int n) { - tilemap[i] |= (1 << n); + tilemap[t] |= (1 << n); } -int is_set(int i, int n) +int is_set(int t, int n) { - return (tilemap[i] >> n) & 1; + return (tilemap[t] >> n) & 1; } void init_tilemap() { + // this is for support of more than 63 different tiles in + // for(size_t i = 0; i < SWIDTH * SHEIGHT; i++) + // for(size_t n = 0; n < TILES; n++) + // set(i, n); + for(size_t i = 0; i < SWIDTH * SHEIGHT; i++) - for(size_t n = 0; n < TILES; n++) - set(i, n); + tilemap[i] = ((1 << TILES) - 1); + } -size_t count_entropy(int i) +int has_collapsed(int t) { - if(has_collapsed(i)) return TILES+1; - - size_t c = 0; - for (size_t j = 0; j < TILES; j++) - c += is_set(i, j); - - return c; + return is_set(t, TILES); } -void collapse(int i, int n) +int get_collapsed_tile(int t) { - tilemap[i] = 0; - set(i, n); - set(i, TILES); + for(size_t i = 0; i < TILES; i++) + if(is_set(t, i)) return i; + + return TILES; } -int has_collapsed(int i) +void collapse(int t, int n) { - return is_set(i, TILES); + tilemap[t] = 0; + set(t, n); + set(t, TILES); } -int get_collapsed_tile(int i) +size_t count_entropy(int t) { - for(size_t t = 0; t < TILES; t++) - if(is_set(i, t)) return t; + if(has_collapsed(t)) return TILES+1; - return TILES; -} + size_t c = 0; + for (size_t j = 0; j < TILES; j++) + c += is_set(t, j); + return c; +} -void mask(int i, int m, int r) +void mask(int t, int m, int r) { - tilemap[i] &= tile_masks[m][r]; + tilemap[t] &= tile_masks[m][r]; } void generate_tile_masks(small_t* tile_connections) diff --git a/src/tilemap.h b/src/tilemap.h index e945f06..74344dd 100644 --- a/src/tilemap.h +++ b/src/tilemap.h @@ -1,17 +1,24 @@ #ifndef TILEMAP_H #define TILEMAP_H -void set(int i, int n); -int is_set(int i, int n); +// set bit n in tile t in tilemap +void set(int t, int n); +// is bit n set in tile t in tilemap +int is_set(int t, int n); + +// self explanatory names +int has_collapsed(int t); +int get_collapsed_tile(int i); +void collapse(int t, int n); +size_t count_entropy(int t); +void init_tilemap(); -void mask(int i, int m, int r); void generate_tile_masks(small_t* tile_connections); -void init_tilemap(); +// applly a mask m, r (from tile_masks) +// to tile t in tilemap +void mask(int t, int m, int r); + -size_t count_entropy(int i); -void collapse(int i, int n); -int has_collapsed(int i); -int get_collapsed_tile(int i); #endif diff --git a/src/tiles.c b/src/tiles.c index cb135fa..28ec89c 100644 --- a/src/tiles.c +++ b/src/tiles.c @@ -28,7 +28,7 @@ static int rotate(int x, int y, int r, int w) return -1; } -static void rotate_tiles(small_t *t, int i, int r) +static void rotate_tiles(char *t, int i, int r) { tiles[i] = malloc(TILE_WIDTH * TILE_HEIGHT * 3); @@ -38,7 +38,7 @@ static void rotate_tiles(small_t *t, int i, int r) tiles[i][(y * TILE_WIDTH + x)*3 + k] = t[(rotate(x, y, r, TILE_WIDTH))*3 + k]; } -static void rotate_connections(small_t *tc, int i, int n, int r) +static void rotate_connections(char *tc, int i, int n, int r) { tile_connections[n] = 0; for(int y = 0; y < 2; y++) @@ -79,7 +79,7 @@ void load_tiles() { char file_path[32]; sprintf(file_path, "files/tiles/tile_%ld.ppm", i); - small_t *t = load_from_ppm(file_path, &TILE_WIDTH, &TILE_HEIGHT); + char *t = load_from_ppm(file_path, &TILE_WIDTH, &TILE_HEIGHT); switch(tile_sym[i]) { diff --git a/src/tiles.h b/src/tiles.h index 56bb831..d6dae58 100644 --- a/src/tiles.h +++ b/src/tiles.h @@ -3,7 +3,9 @@ void load_tiles(); void free_tiles(); + void print_tiles(); + int get_tile_pixel(size_t t, size_t x, size_t y, int k); small_t *get_tile_connections(); diff --git a/src/typedef.h b/src/typedef.h index 2e7c672..483c250 100644 --- a/src/typedef.h +++ b/src/typedef.h @@ -1,12 +1,14 @@ #ifndef TYPEDEF_H #define TYPEDEF_H +// useful definitions + #define TILES_CAP 63 #define TILEMAP_CAP 4096 #include <stdint.h> #include <stddef.h> typedef unsigned char small_t; -typedef size_t big_t; +typedef uint64_t big_t; #endif |