summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Constants.h45
-rw-r--r--FileIO.h28
-rw-r--r--Makefile13
-rw-r--r--PPM.h156
-rw-r--r--main.c523
5 files changed, 765 insertions, 0 deletions
diff --git a/Constants.h b/Constants.h
new file mode 100644
index 0000000..8b968e5
--- /dev/null
+++ b/Constants.h
@@ -0,0 +1,45 @@
+#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
+#define ON_WINDOWS 1
+#else
+#define ON_WINDOWS 0
+#endif
+
+#define FUNCTION_LENGTH 40
+
+//Map modes
+#define BINARY 0
+#define GRADIENT 1
+#define EMPIRES 2
+
+//Settings modes
+#define DEFAULT 1
+#define NO_VIDEO 0
+#define BASIC 2
+#define ADVANCED 3
+
+//Cell types
+#define WATER 0
+#define LAND 1
+#define BLACK 2
+#define WHITE 3
+#define UNOCCUPIED 1
+#define FIRST_EMPIRE 4
+
+//Error codes
+#define FFMPEG_AVCONV_NOT_FOUND 1
+
+//PPM IO
+#define PPM_START 13 // Where the actual data starts for the letter PPM files.
+
+//Colors
+#define LANDR 0
+#define LANDG 150
+#define LANDB 0
+
+#define WATERR 0
+#define WATERG 0
+#define WATERB 150
+
+//Letters
+#define LETTER_WIDTH 20
+#define LETTER_HEIGHT 20
diff --git a/FileIO.h b/FileIO.h
new file mode 100644
index 0000000..5a4c445
--- /dev/null
+++ b/FileIO.h
@@ -0,0 +1,28 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void writeFile(char* filename, char* data)
+{
+ FILE* fp = fopen(filename, "w");
+ fwrite(data, 1, strlen(data), fp);
+ fclose(fp);
+}
+
+int fileSize(FILE* fp)
+{
+
+ fseek(fp, 0, SEEK_END);
+ int ret = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+ return ret;
+}
+
+char* readFile(char* filename)
+{
+ FILE* fp = fopen(filename, "r");
+ int sz = fileSize(fp);
+ char* buffer = malloc(sz);
+ fread(buffer, sz + 1, 1, fp);
+ return buffer;
+} \ No newline at end of file
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..6a9e6ed
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,13 @@
+CC=gcc
+CFLAGS=-Wall -lm
+
+default: AutoMap
+
+AutoMap: main.c PPM.h FileIO.h Constants.h
+ $(CC) $(CFLAGS) main.c -o AutoMap
+
+run: AutoMap
+ ./AutoMap
+
+clean:
+ rm AutoMap \ No newline at end of file
diff --git a/PPM.h b/PPM.h
new file mode 100644
index 0000000..dd7fd89
--- /dev/null
+++ b/PPM.h
@@ -0,0 +1,156 @@
+#include <string.h>
+#include "FileIO.h"
+#include "Constants.h"
+
+typedef struct
+{
+ double r; // 0 - 1
+ double g; // 0 - 1
+ double b; // 0 - 1
+} color;
+
+typedef struct
+{
+ //General settings
+ double** map;
+ double** newMap;
+ char* contents;
+ int width;
+ int height;
+ int numFrames;
+ int frameNumber;
+ int mode;
+ //BINARY settings
+ double expandCoast;
+ double retractCoast;
+ double island;
+ double lake;
+ int expandCoastDecay;
+ //GRADIENT settings
+ double base; //Base positive random number probability; default: 0.5
+ double factor; //Positive random number factor; default: 0.4
+ double change; //Change multiplier; default: 0.1
+ //EMPIRES settings
+ color* empireColors;
+ int numEmpires;
+ int showMapGen;
+} map_state;
+
+void overlay(double** dest, double** src, int src_x, int src_y, int src_width, int src_height)
+{
+ int x, y;
+ for (y = src_y; y < src_y + src_height; y++)
+ for (x = src_x; x < src_x + src_width; x++)
+ dest[y][x] = src[y-src_y][x-src_x];
+}
+
+double** getLetter(char letter)
+{
+ char* filePath;
+ if (letter == ' ')
+ {
+ filePath = "images/space.ppm";
+ }
+ else
+ {
+ filePath = malloc(16);
+ sprintf(filePath, "images/%c.ppm", letter);
+ }
+ char* data = readFile(filePath);
+ double** img = malloc(LETTER_HEIGHT * sizeof(double*));
+ int i;
+ for (i = 0; i < LETTER_HEIGHT; i++)
+ img[i] = malloc(LETTER_WIDTH * sizeof(double));
+ for (i = 0; i < LETTER_HEIGHT * LETTER_WIDTH; i++)
+ img[i / LETTER_HEIGHT][i % LETTER_HEIGHT] = (double)((unsigned char) data[i * 3 + PPM_START] / 255 + BLACK);
+
+ return img;
+}
+
+
+void writeChar(double** dest, char letter, int x, int y)
+{
+ double** l = getLetter(letter);
+ overlay(dest, l, x, y, LETTER_WIDTH, LETTER_HEIGHT);
+}
+
+void writeString(double** dest, char* s, int x, int y)
+{
+ int i;
+ for (i = 0; i < strlen(s); i++)
+ {
+ writeChar(dest, s[i], x+LETTER_WIDTH*i, y);
+ }
+}
+
+// Convert a map to a .ppm file
+char* writePPM(map_state* ms, char* start, int binaryModeOverride)
+{
+ if (ms->mode < 2 && !binaryModeOverride)
+ {
+ int x, y;
+ char r, g, b;
+ int string_pos = strlen(start);
+ double** map = ms->map;
+
+ memcpy(ms->contents, start, strlen(start));
+
+ for (y = 0; y < ms->height; y++)
+ {
+ for (x = 0; x < ms->width; x++)
+ {
+ if (map[y][x] == -2)
+ {
+ ms->contents[string_pos++] = 1;
+ ms->contents[string_pos++] = 1;
+ ms->contents[string_pos++] = 1;
+ }
+ else if (map[y][x] == -1)
+ {
+ ms->contents[string_pos++] = 255;
+ ms->contents[string_pos++] = 255;
+ ms->contents[string_pos++] = 255;
+ }
+ else
+ {
+ r = (char)(LANDR * map[y][x] + WATERR * (1-map[y][x]));
+ g = (char)((ms->mode == GRADIENT ? 255 : LANDG) * map[y][x] + WATERG * (1-map[y][x])); // For gradient mode, LANDG should be 255
+ b = (char)(LANDB * map[y][x] + WATERB * (1-map[y][x]));
+ ms->contents[string_pos++] = r == 0 ? 1: r;
+ ms->contents[string_pos++] = g == 0 ? 1 : g;
+ ms->contents[string_pos++] = b == 0 ? 1 : b;
+ }
+ }
+ }
+ ms->contents[string_pos] = 0;
+
+ return ms->contents;
+ }
+ else
+ {
+ int x, y;
+ color col;
+ char r, g, b;
+ int string_pos = strlen(start);
+
+ memcpy(ms->contents, start, strlen(start));
+ for (y = 0; y < ms->height; y++)
+ {
+ for (x = 0; x < ms->width; x++)
+ {
+ col = ms->empireColors[(int)ms->newMap[y][x]];
+
+ r = (char)(col.r * 255);
+ g = (char)(col.g * 255);
+ b = (char)(col.b * 255);
+ ms->contents[string_pos++] = r == 0 ? 1 : r;
+ ms->contents[string_pos++] = g == 0 ? 1 : g;
+ ms->contents[string_pos++] = b == 0 ? 1 : b;
+ }
+ }
+ ms->contents[string_pos] = 0;
+
+ return ms->contents;
+ }
+}
+
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..83c50cc
--- /dev/null
+++ b/main.c
@@ -0,0 +1,523 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <math.h>
+#include <sys/stat.h>
+#include "PPM.h" //.ppm file IO
+
+double rand01()
+{
+ return (double)rand() / RAND_MAX;
+}
+
+color* randColor()
+{
+ color* c = malloc(sizeof(color));
+ c->r = rand01();
+ c->g = rand01();
+ c->b = rand01();
+ return c;
+}
+
+color* mkColor(double r, double g, double b)
+{
+ color* c = malloc(sizeof(color));
+ c->r = r;
+ c->g = g;
+ c->b = b;
+ return c;
+}
+
+int randrange(int start, int end)
+{
+ return (int) (rand01() * (end - start) + start);
+}
+
+
+
+double changeScale(double val, double oldMin, double oldMax, double newMin, double newMax)
+{
+ double oldScale = oldMax - oldMin;
+ double newScale = newMax - newMin;
+ double normalized = (val - oldMin) / oldScale;
+ return normalized * newScale + newMin;
+}
+
+long long int year(map_state* ms)
+{
+ int n = ms->numFrames;
+ int t = ms->frameNumber;
+ if (ms->showMapGen)
+ {
+ if (t < n)
+ {
+ return (long long int) changeScale(t, 0, n, -1000000000, -1000);
+ }
+ else
+ {
+ return (long long int) changeScale(t, n, 2*n, -2000, 1500);
+ }
+ }
+ else
+ {
+ return (long long int) changeScale(t, 0, n, -1000, 1000);
+ }
+
+}
+
+char* showYear(map_state* ms)
+{
+ char* val = malloc(32);
+ long long int y = year(ms);
+ if (y < 0)
+ sprintf(val, "year %lld bce", -y);
+ else if (y > 0)
+ sprintf(val, "year %lld ce", y);
+ else
+ return "year 1 ce"; //0 CE wasn't a year.
+ return val;
+}
+
+double clamp(double value, double mn, double mx)
+{
+ if (value < mn)
+ return mn;
+ if (value > mx)
+ return mx;
+ return value;
+}
+
+double** blankMap(int width, int height)
+{
+ int i, j;
+
+ double** map = malloc(height*sizeof(double*));
+ for (i = 0; i < height; i++)
+ {
+ map[i] = malloc(width*sizeof(double));
+ for (j = 0; j < width; j++)
+ map[i][j] = 0;
+ }
+
+ return map;
+}
+
+
+void iteration(map_state* ms)
+{
+ double** map = ms->map;
+ int y, x, selection; //isLand NTBCW island
+ int i, j, importance, currentMax;
+ double landNeighbours, neighbourWeights, r;
+ double landScore, waterScore;
+
+ for (y = 0; y < ms->height; y++)
+ {
+ for (x = 0; x < ms->width; x++)
+ {
+ switch (ms->mode)
+ {
+ case BINARY:
+ landNeighbours = 0;
+ if (y > 0)
+ {
+ landNeighbours += map[y-1][x-1] + map[y-1][x] + map[y-1][x+1];
+ }
+ if (x > 0)
+ landNeighbours += map[y][x-1];
+ if (x < ms->width-1)
+ landNeighbours += map[y][x+1];
+ if (y < ms->height-1)
+ {
+ landNeighbours += map[y+1][x-1] + map[y+1][x] + map[y+1][x+1];
+ }
+ landScore = (double)landNeighbours/8;
+ waterScore = 1 - landScore;
+ waterScore = waterScore < 0.01 ? 0.1 : waterScore;
+ if (map[y][x])
+ {
+ if (landNeighbours == 8)
+ {
+ ms->newMap[y][x] = rand01() > ms->lake;
+ }
+ else
+ {
+ ms->newMap[y][x] = rand01() > waterScore * ms->retractCoast;
+ }
+ }
+ else
+ {
+ if (landNeighbours == 0)
+ {
+ ms->newMap[y][x] = rand01() < ms->island;
+ }
+ else
+ {
+ ms->newMap[y][x] = rand01() < landScore * ms->expandCoast;
+ }
+ }
+
+ break;
+ case GRADIENT:
+ neighbourWeights = 3;
+ landNeighbours = 3*map[y][x];
+ if (y > 0)
+ {
+ landNeighbours += map[y-1][x-1] + 2*map[y-1][x] + map[y-1][x+1];
+ neighbourWeights += 4;
+ }
+ if (x > 0)
+ {
+ landNeighbours += 2*map[y][x-1];
+ neighbourWeights += 2;
+ }
+ if (x < ms->width-1)
+ {
+ landNeighbours += 2*map[y][x+1];
+ neighbourWeights += 2;
+ }
+ if (y < ms->height-1)
+ {
+ landNeighbours += map[y+1][x-1] + 2*map[y+1][x] + map[y+1][x+1];
+ neighbourWeights += 4;
+ }
+ r = rand01();
+ ms->newMap[y][x] = clamp(landNeighbours / neighbourWeights + r * (rand01() < ms->base + ms->factor / ms->frameNumber ? 1 : -1) * ms->change, 0, 1);
+ break;
+ default: //EMPIRES
+
+ if (ms->map[y][x] == WATER)
+ {
+ ms->newMap[y][x] = WATER;
+ break;
+ }
+
+ currentMax = 15;
+ selection = randrange(0, currentMax);
+ for (i = -1; i <= 1; i++)
+ {
+ for (j = -1; j <= 1; j++)
+ {
+ if (y+i < 0 || x+j < 0 || y+i >= ms->height || x+j >= ms->width)
+ continue;
+ importance = 3 - (abs(i) + abs(j));
+ currentMax -= importance;
+ if ((selection -= importance) < 0)
+ {
+
+ if (map[y+i][x+j] <= UNOCCUPIED)
+ {
+ selection = randrange(0, currentMax);
+ }
+ else
+ {
+ ms->newMap[y][x] = ms->map[y+i][x+j];
+ goto done; // Sorry!
+ }
+ }
+ }
+ }
+ ms->newMap[y][x] = UNOCCUPIED;
+
+ done:
+ break;
+ }
+ }
+ }
+
+ if (ms->mode == BINARY)
+ {
+ // As the frame_number approaches infinity, expandCoast -> retractCoast
+ if (ms->frameNumber > 0 && ms->frameNumber % ms->expandCoastDecay == 0)
+ ms->expandCoast = (ms->retractCoast + ms->expandCoast) / 2;
+ }
+ double** tmp = ms->newMap;
+ ms->newMap = ms->map;
+ ms->map = tmp;
+}
+
+
+int getInt(char* msg, int default_value, int ask)
+{
+ //Get int from user. If it is invalid, or ask = 0, return the default_value.
+ if (!ask)
+ return default_value;
+ char* i;
+ char* ptr;
+ int val;
+ i = malloc(16);
+ printf(msg);
+ fgets(i, 16, stdin);
+ val = (int)strtol(i, &ptr, 10);
+ if (strlen(i) <= 1 || strlen(ptr) > 1)
+ return default_value;
+ else
+ return val;
+}
+
+
+double getDouble(char* msg, double default_value, int ask)
+{
+ //Get double from user. If it is invalid, or ask = 0, return the default_value.
+ if (!ask)
+ return default_value;
+ char* i;
+ char* ptr;
+ double val;
+ i = malloc(16);
+ printf(msg);
+ fgets(i, 16, stdin);
+ val = strtod(i, &ptr);
+ if (strlen(i) <= 1 || strlen(ptr) > 1)
+ return default_value;
+ else
+ return val;
+}
+
+int askYesNo(char* msg, int default_value, int ask)
+{
+ if (!ask)
+ return default_value;
+ char* yn;
+ yn = malloc(20);
+ printf(msg);
+ fgets(yn, 20, stdin);
+ switch (yn[0])
+ {
+ case 'y':
+ return 1;
+ case 'n':
+ return 0;
+ default:
+ return default_value;
+ }
+}
+
+
+void printMap(double** map, int width, int height)
+{
+ int y, x;
+ for (y = 0; y < height; y++)
+ {
+ for (x = 0; x < width; x++)
+ printf("%02d", (int)map[y][x]);
+ printf("\n");
+ }
+}
+
+
+int main()
+{
+ srand(time(NULL));
+ int i, frame_number, x, y, createVideo, frameRate, deleteFrames, counter, showCurrentYear, settings;
+ char* yr;
+ map_state* ms;
+
+
+ //AutoMap logo
+ printf("#### ##### ##### ############## ############\n### ### ### ### ## ## ##\n### ### ### ### ## ## ##\n########### ### ### ## ## ##\n### ### ### ### ## ## ##\n##### ##### ######### ###### ###########\n\n ### ### #### ############\n ##### ##### ### ### ## ##\n ####### ####### ### ### ###########\n ### ##### ### ########### ###\n ### ### ### ### ### ###\n ### ## ### ##### ##### #####\n");
+ printf("For more information, see https://pommicket.itch.io/automap\n");
+ printf("WARNING: AutoMap may temporarily use a lot of your disk space. The default settings should use about 300MB.\n");
+
+ printf("Please select a settings mode\n(%d) Default settings\n(%d) Default settings, but don't produce a video\n(%d) Basic settings\n(%d) Advanced settings\nMode (default=%d)? ", DEFAULT, NO_VIDEO, BASIC, ADVANCED, DEFAULT);
+ settings = getInt("", DEFAULT, 1);
+
+
+ ms = malloc(sizeof(map_state));
+ ms->showMapGen = 0;
+ ms->width = getInt("Width (default=1280)? ", 1280, settings > DEFAULT);
+ ms->height = getInt("Height (default=720)? ", 720, settings > DEFAULT);
+ if (settings == NO_VIDEO)
+ {
+ createVideo = 0;
+ }
+ else
+ {
+ createVideo = askYesNo("Would you like to make a video (ffmpeg/avconv is required. If you are on Windows, it must be on your PATH. default=yes)? ",
+ 1, settings > DEFAULT);
+ if (createVideo)
+ {
+ frameRate = getInt("Frame rate (default=20)? ", 20, settings > BASIC);
+ deleteFrames = askYesNo("Would you like to delete the frames after the video is created (default=yes, yes is recommended)? ", 1, settings > DEFAULT);
+ }
+ }
+
+ ms->mode = getInt("Select a mode.\n(0) Binary map (only land and water)\n(1) Gradient map (with elevation)\n(2) Map with empires\nMode (default=2)? ", 2, settings > DEFAULT);
+
+
+ if (ms->mode == BINARY || ms->mode == EMPIRES)
+ {
+ ms->expandCoast = getDouble("Expand coast probability (default=0.8)? ", 0.8, settings > BASIC);
+ ms->expandCoastDecay = getInt("Expand coast decay, the lower the number the higher the decay (default=100)? ", 100, settings > BASIC);
+ ms->retractCoast = getDouble("Retract coast probability (default=0.6)? ", 0.6, settings > BASIC);
+ ms->island = getDouble("Island probability (default=0.0000025)? ", 0.0000025, settings > BASIC);
+ ms->lake = getDouble("Lake probability (default=0.0005)? ", 0.0005, settings > BASIC);
+ }
+
+ switch(ms->mode)
+ {
+ case GRADIENT:
+ ms->base = getDouble("Base positive random number probability (default=0.5)? ", 0.5, settings > BASIC);
+ ms->factor = getDouble("Positive random number factor (default=0.4)? ", 0.4, settings > BASIC);
+ ms->change = getDouble("Change multiplier (default=0.1)? ", 0.1, settings > BASIC);
+ break;
+ case EMPIRES:
+ ms->numEmpires = getInt("Number of empires (Some will be confined to small islands, default=20)? ", 20, settings > DEFAULT);
+ ms->showMapGen = askYesNo("Show map generation (default=yes)? ", 1, settings > DEFAULT);
+ showCurrentYear = askYesNo("Show year (default=yes)? ", 1, settings > BASIC);
+ ms->empireColors = malloc((ms->numEmpires+FIRST_EMPIRE)*sizeof(color));
+ ms->empireColors[WATER] = *mkColor((double)WATERR/255, (double)WATERG/255, (double)WATERB/255);
+ ms->empireColors[UNOCCUPIED] = *mkColor((double)LANDR/255, (double)LANDG/255, (double)LANDB/255);
+ ms->empireColors[BLACK] = *mkColor(0, 0, 0);
+ ms->empireColors[WHITE] = *mkColor(1, 1, 1);
+ for (i = 0; i < ms->numEmpires; i++)
+ ms->empireColors[i+FIRST_EMPIRE] = *randColor();
+ break;
+ }
+
+ if (createVideo)
+ {
+ //Ask for length (s)
+ ms->numFrames = (int)(frameRate * getDouble("Length of video in seconds (default=100)? ", 100, settings > DEFAULT));
+ }
+ else
+ {
+ //Ask for # of frames (total)
+ ms->numFrames = getInt(ms->showMapGen ? "Number of frames (default=2000)? " : "Number of frames (default=1000)? ", ms->showMapGen ? 2000 : 1000, settings > DEFAULT);
+ }
+
+ if (ms->showMapGen)
+ ms->numFrames /= 2;
+
+ double** map = blankMap(ms->width, ms->height);
+ ms->map = map;
+
+ ms->newMap = blankMap(ms->width, ms->height);
+ ms->contents = malloc(ms->width * ms->height * 3 + ms->height + 90);
+ char* start = malloc(90);
+ sprintf(start, "P6\n# AutoMap (pommicket.itch.io/auto-map)\n%d %d\n255\n", ms->width, ms->height);
+
+
+
+ char* folder = malloc(32);
+ sprintf(folder, "map%d", rand());
+
+#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__) // Helps if you're trying to compile for Windows.
+ mkdir(folder);
+#else
+ mkdir(folder, 0777);
+#endif
+
+ char* command;
+ char* filename;
+ char* png_filename;
+ frame_number = 0;
+ if (ms->mode == EMPIRES)
+ {
+ ms->mode = BINARY;
+ for (frame_number = 0; frame_number < ms->numFrames; frame_number++)
+ {
+ printf("Preparing map %d/%d (%.2f%%)...\n", frame_number+1, ms->numFrames, 100*(double)(frame_number+1)/ms->numFrames);
+ if (ms->showMapGen)
+ {
+ ms->frameNumber = frame_number + 1;
+ filename = malloc(64);
+ png_filename = malloc(64);
+ command = malloc(200);
+ sprintf(filename, "%s/frame%09d.ppm", folder, frame_number);
+ sprintf(png_filename, "%s/frame%09d.png", folder, frame_number);
+ if (ON_WINDOWS)
+ sprintf(command, "convert %s %s && del %s", filename, png_filename, filename);
+ else
+ sprintf(command, "convert %s %s && rm %s", filename, png_filename, filename);
+ if (showCurrentYear)
+ {
+ yr = showYear(ms);
+ writeString(ms->newMap, yr, 0, 0);
+ }
+ writeFile(filename, writePPM(ms, start, 1));
+ system(command);
+ }
+
+ iteration(ms);
+ }
+ ms->mode = EMPIRES;
+ if (!ms->showMapGen)
+ frame_number = 0;
+ //Place empires
+ for (i = 0; i < ms->numEmpires; i++)
+ {
+ while (ms->map[(y=randrange(0, ms->height))][(x=randrange(0, ms->width))] == WATER);
+ ms->map[y][x] = i+FIRST_EMPIRE;
+ }
+ }
+
+ for (; frame_number < ms->numFrames + ms->showMapGen*ms->numFrames; frame_number++)
+ {
+ counter = frame_number+1;
+ i = counter - ms->showMapGen * ms->numFrames;
+ printf(ms->mode == EMPIRES ? "Expanding empires %d/%d (%.2f%%)...\n" : "Creating map %d/%d (%.2f%%)...\n", i, ms->numFrames, 100*(double)i/ms->numFrames);
+ if (ms->mode == EMPIRES && showCurrentYear)
+ {
+ yr = showYear(ms);
+ writeString(ms->newMap, yr, 0, 0);
+ }
+ filename = malloc(64);
+ png_filename = malloc(64);
+ command = malloc(200);
+ sprintf(filename, "%s/frame%09d.ppm", folder, frame_number);
+ sprintf(png_filename, "%s/frame%09d.png", folder, frame_number);
+ if (ON_WINDOWS)
+ sprintf(command, "convert %s %s && del %s", filename, png_filename, filename);
+ else
+ sprintf(command, "convert %s %s && rm %s", filename, png_filename, filename);
+ writeFile(filename, writePPM(ms, start, 0));
+ system(command);
+ ms->frameNumber = counter;
+ iteration(ms);
+ }
+
+ if (!createVideo)
+ {
+ printf("Done!\n");
+ return 0;
+ }
+
+ printf("Creating video...\n");
+
+ command = malloc(128);
+ sprintf(command, "ffmpeg -framerate %d -i '%s/frame%%09d.png' -c:v libx264 %s/video.mp4", frameRate, folder, folder);
+ printf("%s\n", command);
+ if (system(command))
+ {
+ // ffmpeg failed. Trying avconv.
+ command = malloc(128);
+ sprintf(command, "avconv -framerate %d -i '%s/frame%%09d.png' -c:v libx264 %s/video.mp4", frameRate, folder, folder);
+ printf("%s\n", command);
+ if (system(command))
+ {
+ printf("Error - You must have avconv/ffmpeg installed. If you are on Windows, one of them must be on your PATH.\n");
+ return FFMPEG_AVCONV_NOT_FOUND;
+ }
+ }
+ printf("%s/video.mp4 created.\n", folder);
+ if (!deleteFrames)
+ {
+ printf("Done!\n");
+ return 0;
+ }
+ command = malloc(128);
+
+
+ if (ON_WINDOWS)
+ sprintf(command, "del %s/frame*.png", folder);
+ else
+ sprintf(command, "rm %s/frame*.png", folder);
+ printf("%s\n", command);
+ system(command);
+ printf("Frames deleted.\nDone.\n");
+
+
+ return 0;
+}