aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkartofen <mladenovnasko0@gmail.com>2022-08-11 21:45:03 +0300
committerkartofen <mladenovnasko0@gmail.com>2022-08-11 21:45:03 +0300
commit30d1c9b30b5642efcba66bdc7956f4d7e321fc97 (patch)
treeae3cf3aeb4b01f1cb70ae84cc84d453e08da35a5
parent57315da56fa3b036f8e3e2d32a5f90a11ae7c3de (diff)
more used friendly
-rw-r--r--README.md51
-rw-r--r--src/config.h26
-rw-r--r--src/gen_tiles.c2
-rw-r--r--src/main.c70
-rw-r--r--src/ppm.c6
-rw-r--r--src/ppm.h4
-rw-r--r--src/tilemap.c58
-rw-r--r--src/tilemap.h23
-rw-r--r--src/tiles.c6
-rw-r--r--src/tiles.h2
-rw-r--r--src/typedef.h4
11 files changed, 181 insertions, 71 deletions
diff --git a/README.md b/README.md
index 47cd43e..424e9c6 100644
--- a/README.md
+++ b/README.md
@@ -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);
diff --git a/src/main.c b/src/main.c
index 6bec32e..5c32c13 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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);
diff --git a/src/ppm.c b/src/ppm.c
index 7952305..57a402b 100644
--- a/src/ppm.c
+++ b/src/ppm.c
@@ -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);
}
diff --git a/src/ppm.h b/src/ppm.h
index 70bd6f0..f166867 100644
--- a/src/ppm.h
+++ b/src/ppm.h
@@ -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