8#define ALLEGRO_UNSTABLE 1
19#include <allegro5/allegro.h>
20#include <allegro5/allegro_ttf.h>
21#include "allegro5/allegro_audio.h"
22#include "allegro5/allegro_acodec.h"
23#include <allegro5/allegro_font.h>
24#include <allegro5/allegro_image.h>
25#include <allegro5/allegro_primitives.h>
27#include "ex_fluid_config.h"
30#define RESERVED_SAMPLES 16
32#define MAX_SAMPLE_DATA 10
34ALLEGRO_DISPLAY* display = NULL;
45double logicFPS = 120.0;
47ALLEGRO_TIMER* fps_timer = NULL;
48ALLEGRO_TIMER* logic_timer = NULL;
60int main(
int argc,
char* argv[]) {
62 setlocale(LC_ALL,
"POSIX");
71 char ver_str[128] =
"";
73 while ((getoptret = getopt(argc, argv,
"hvV:L:")) != EOF) {
76 n_log(
LOG_NOTICE,
"\n %s -h help -v version -V DEBUGLEVEL (NOLOG,VERBOSE,NOTICE,ERROR,DEBUG)\n", argv[0]);
79 sprintf(ver_str,
"%s %s", __DATE__, __TIME__);
83 if (!strncmp(
"INFO", optarg, 6)) {
86 if (!strncmp(
"NOTICE", optarg, 6)) {
89 if (!strncmp(
"VERBOSE", optarg, 7)) {
92 if (!strncmp(
"ERROR", optarg, 5)) {
95 if (!strncmp(
"DEBUG", optarg, 5)) {
115 n_log(
LOG_ERR,
"\nPlease specify a log level after -V. \nAvailable values: NOLOG,VERBOSE,NOTICE,ERROR,DEBUG");
123 __attribute__((fallthrough));
125 n_log(
LOG_ERR,
"\n %s -h help -v version -V DEBUGLEVEL (NOLOG,VERBOSE,NOTICE,ERROR,DEBUG) -L logfile", argv[0]);
132 int threadedProcessing = 0;
135 if (load_app_state(
"CONFIGS/ex_fluid.cfg", &WIDTH, &HEIGHT, &fullscreen, &bgmusic, &drawFPS, &logicFPS, fluid_data, &threadedProcessing) != TRUE) {
139 double fluid_factor = fluid_data->
cScale;
140 n_log(
LOG_DEBUG,
"%s starting with params: %dx%d fullscreen(%d), music: %s", argv[0], WIDTH, HEIGHT, fullscreen,
_str(bgmusic));
144 n_abort(
"Could not init Allegro.\n");
146 if (!al_init_acodec_addon()) {
147 n_abort(
"Could not register addons.\n");
149 if (!al_install_audio()) {
150 n_log(
LOG_ERR,
"Unable to initialize audio addon, disabling bgmusic\n");
153 if (!al_init_acodec_addon()) {
154 n_abort(
"Unable to initialize acoded addon\n");
156 if (!al_init_image_addon()) {
157 n_abort(
"Unable to initialize image addon\n");
159 if (!al_init_primitives_addon()) {
160 n_abort(
"Unable to initialize primitives addon\n");
162 if (!al_init_font_addon()) {
163 n_abort(
"Unable to initialize font addon\n");
165 if (!al_init_ttf_addon()) {
166 n_abort(
"Unable to initialize ttf_font addon\n");
168 if (!al_install_keyboard()) {
169 n_abort(
"Unable to initialize keyboard handler\n");
171 if (!al_install_mouse()) {
172 n_abort(
"Unable to initialize mouse handler\n");
175 if (bgmusic && !al_reserve_samples(RESERVED_SAMPLES)) {
176 n_abort(
"Could not set up voice and mixer.\n");
179 ALLEGRO_SAMPLE* sample_data[MAX_SAMPLE_DATA] = {NULL};
180 memset(sample_data, 0,
sizeof(sample_data));
182 ALLEGRO_EVENT_QUEUE* event_queue = NULL;
184 event_queue = al_create_event_queue();
186 fprintf(stderr,
"failed to create event_queue!\n");
191 al_set_new_display_flags(ALLEGRO_OPENGL | ALLEGRO_FULLSCREEN_WINDOW);
193 al_set_new_display_flags(ALLEGRO_OPENGL | ALLEGRO_WINDOWED);
197 al_set_new_bitmap_flags(ALLEGRO_VIDEO_BITMAP | ALLEGRO_NO_PRESERVE_TEXTURE);
200 display = al_create_display(WIDTH, HEIGHT);
202 n_abort(
"Unable to create display\n");
205 al_set_window_title(display, argv[0]);
207 ALLEGRO_FONT* font = al_load_font(
"DATAS/2Dumb.ttf", 18, 0);
209 n_abort(
"Unable to load font DATAS/2Dumb.ttf\n");
213 fps_timer = al_create_timer(1.0 / drawFPS);
214 logic_timer = al_create_timer(1.0 / logicFPS);
216 al_register_event_source(event_queue, al_get_display_event_source(display));
217 al_start_timer(fps_timer);
218 al_start_timer(logic_timer);
219 al_register_event_source(event_queue, al_get_timer_event_source(fps_timer));
220 al_register_event_source(event_queue, al_get_timer_event_source(logic_timer));
222 al_register_event_source(event_queue, al_get_keyboard_event_source());
223 al_register_event_source(event_queue, al_get_mouse_event_source());
226 ALLEGRO_BITMAP* scrbuf = NULL;
227 ALLEGRO_BITMAP* bitmap = al_create_bitmap(WIDTH, HEIGHT);
229 al_hide_mouse_cursor(display);
252 int key[19] = {
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false};
255 if (!(sample_data[0] = al_load_sample(bgmusic))) {
259 al_play_sample(sample_data[0], 1, 0, 1, ALLEGRO_PLAYMODE_LOOP, NULL);
275 fluid_sim->
dt = 1.0 / logicFPS;
277 size_t n = fluid_sim->
numY;
279 for (
size_t i = 0; i < fluid_sim->
numX; i++) {
280 for (
size_t j = 0; j < fluid_sim->
numY; j++) {
282 if (i == 0 || j == 0 || j == fluid_sim->
numY - 1)
285 fluid_sim->
s[i * n + j] = s;
288 fluid_sim->
u[i * n + j] = inVel;
293 size_t minJ = floor(0.5 * fluid_sim->
numY - 0.5 * pipeH);
294 size_t maxJ = floor(0.5 * fluid_sim->
numY + 0.5 * pipeH);
295 for (
size_t j = 0; j < minJ; j++)
296 fluid_sim->
m[j] = 1.0;
297 for (
size_t j = minJ; j < maxJ; j++)
298 fluid_sim->
m[j] = 0.0;
299 for (
size_t j = maxJ; j < fluid_sim->
numY; j++)
300 fluid_sim->
m[j] = 1.0;
302 bool do_draw = 1, do_logic = 1;
303 int mx = WIDTH / 3, my = HEIGHT / 2, mouse_button = 0, mouse_b1 = 0, mouse_b2 = 0;
307 al_flush_event_queue(event_queue);
308 al_set_mouse_xy(display, WIDTH / 3, HEIGHT / 2);
310 int w = al_get_display_width(display);
311 int h = al_get_display_height(display);
313 bitmap = al_create_bitmap(WIDTH, HEIGHT);
315 size_t logic_duration = 0;
316 size_t drawing_duration = 0;
321 al_wait_for_event(event_queue, &ev);
323 if (ev.type == ALLEGRO_EVENT_KEY_DOWN) {
324 switch (ev.keyboard.keycode) {
328 case ALLEGRO_KEY_DOWN:
331 case ALLEGRO_KEY_LEFT:
334 case ALLEGRO_KEY_RIGHT:
337 case ALLEGRO_KEY_ESCAPE:
340 case ALLEGRO_KEY_SPACE:
343 case ALLEGRO_KEY_LSHIFT:
344 case ALLEGRO_KEY_RSHIFT:
347 case ALLEGRO_KEY_PAD_MINUS:
348 key[KEY_PAD_MINUS] = 1;
350 case ALLEGRO_KEY_PAD_PLUS:
351 key[KEY_PAD_PLUS] = 1;
353 case ALLEGRO_KEY_PAD_ENTER:
354 key[KEY_PAD_ENTER] = 1;
362 case ALLEGRO_KEY_LCTRL:
363 case ALLEGRO_KEY_RCTRL:
387 }
else if (ev.type == ALLEGRO_EVENT_KEY_UP) {
388 switch (ev.keyboard.keycode) {
392 case ALLEGRO_KEY_DOWN:
395 case ALLEGRO_KEY_LEFT:
398 case ALLEGRO_KEY_RIGHT:
401 case ALLEGRO_KEY_ESCAPE:
404 case ALLEGRO_KEY_SPACE:
407 case ALLEGRO_KEY_LSHIFT:
408 case ALLEGRO_KEY_RSHIFT:
411 case ALLEGRO_KEY_PAD_MINUS:
412 key[KEY_PAD_MINUS] = 0;
414 case ALLEGRO_KEY_PAD_PLUS:
415 key[KEY_PAD_PLUS] = 0;
417 case ALLEGRO_KEY_PAD_ENTER:
418 key[KEY_PAD_ENTER] = 0;
426 case ALLEGRO_KEY_LCTRL:
427 case ALLEGRO_KEY_RCTRL:
452 }
else if (ev.type == ALLEGRO_EVENT_TIMER) {
453 if (al_get_timer_event_source(fps_timer) == ev.any.source) {
455 }
else if (al_get_timer_event_source(logic_timer) == ev.any.source) {
458 }
else if (ev.type == ALLEGRO_EVENT_MOUSE_AXES) {
461 }
else if (ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN) {
462 if (ev.mouse.button == 1)
464 if (ev.mouse.button == 2)
466 }
else if (ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_UP) {
467 if (ev.mouse.button == 1)
469 if (ev.mouse.button == 2)
479 else if (ev.type == ALLEGRO_EVENT_DISPLAY_SWITCH_IN || ev.type == ALLEGRO_EVENT_DISPLAY_SWITCH_OUT) {
480 al_clear_keyboard_state(display);
481 al_flush_event_queue(event_queue);
513 if (key[KEY_RIGHT]) {
517 if (key[KEY_PAD_PLUS]) {
521 if (key[KEY_PAD_MINUS]) {
525 if (mouse_button != -1) {
529 }
while (!al_is_event_queue_empty(event_queue));
533 static int old_mx = -1, old_my = -1;
534 double vx = 0.0, vy = 0.0;
535 if (old_mx != mx || old_my != my) {
536 if (old_mx != -1 && old_my != -1) {
537 vx = (old_mx - mx) / logicFPS;
538 vy = (old_my - my) / logicFPS;
543 n_fluid_setObstacle(fluid_sim, mx / fluid_factor, (my / fluid_factor) - 20.0, vx, vy, fluid_factor / 2);
544 n_fluid_setObstacle(fluid_sim, (mx / fluid_factor) - 15, my / fluid_factor, vx, vy, fluid_factor / 2 + fluid_factor / 3);
545 n_fluid_setObstacle(fluid_sim, (mx / fluid_factor) + 15, my / fluid_factor - 10.0, vx, vy, fluid_factor / 2 + fluid_factor / 2);
546 n_fluid_setObstacle(fluid_sim, (mx / fluid_factor) + 15, my / fluid_factor + 10.0, vx, vy, fluid_factor / 2 + fluid_factor / 2);
547 n_fluid_setObstacle(fluid_sim, mx / fluid_factor, (my / fluid_factor) + 20.0, vx, vy, fluid_factor / 2);
551 minJ = floor(0.5 * fluid_sim->
numY - 0.5 * pipeH);
552 maxJ = floor(0.5 * fluid_sim->
numY + 0.5 * pipeH);
553 for (
size_t j = 0; j < minJ; j++)
554 fluid_sim->
m[j] = 1.0;
555 for (
size_t j = minJ; j < maxJ; j++)
556 fluid_sim->
m[j] = 0.0;
557 for (
size_t j = maxJ; j < fluid_sim->
numY; j++)
558 fluid_sim->
m[j] = 1.0;
559 for (
size_t j = minJ; j < maxJ; j++)
560 fluid_sim->
m[j] = 0.0;
562 if (threadedProcessing == 1)
567 logic_duration = (logic_duration +
get_usec(&logic_chrono)) / 2;
574 scrbuf = al_get_backbuffer(display);
578 al_set_target_bitmap(scrbuf);
581 al_lock_bitmap(scrbuf, al_get_bitmap_format(scrbuf), ALLEGRO_LOCK_READWRITE);
585 al_draw_circle(mx, my - 20 * fluid_factor, fluid_factor * fluid_factor / 2, al_map_rgb(255, 0, 0), 2.0);
586 al_draw_circle(mx - 15 * fluid_factor, my, fluid_factor * fluid_factor / 2 + (fluid_factor * fluid_factor) / 3, al_map_rgb(255, 0, 0), 2.0);
587 al_draw_circle(mx + 15 * fluid_factor, my + 10.0 * fluid_factor, fluid_factor * fluid_factor / 2 + (fluid_factor * fluid_factor) / 2, al_map_rgb(255, 0, 0), 2.0);
588 al_draw_circle(mx + 15 * fluid_factor, my - 10.0 * fluid_factor, fluid_factor * fluid_factor / 2 + (fluid_factor * fluid_factor) / 2, al_map_rgb(255, 0, 0), 2.0);
589 al_draw_circle(mx, my + 20 * fluid_factor, fluid_factor * fluid_factor / 2, al_map_rgb(255, 0, 0), 2.0);
591 static N_STR* textout = NULL;
593 al_draw_text(font, al_map_rgb(0, 0, 255), WIDTH, 10, ALLEGRO_ALIGN_RIGHT,
_nstr(textout));
596 al_draw_text(font, al_map_rgb(0, 0, 255), WIDTH, 25, ALLEGRO_ALIGN_RIGHT,
_nstr(textout));
598 nstrprintf(textout,
"logic(max %zd): %zd usecs", (
size_t)(1000000.0 / logicFPS), logic_duration);
599 al_draw_text(font, al_map_rgb(0, 0, 255), 5, 10, ALLEGRO_ALIGN_LEFT,
_nstr(textout));
601 nstrprintf(textout,
"drawing(max %zd): %zd usecs", (
size_t)(1000000.0 / drawFPS), drawing_duration);
602 al_draw_text(font, al_map_rgb(0, 0, 255), 5, 30, ALLEGRO_ALIGN_LEFT,
_nstr(textout));
605 al_unlock_bitmap(scrbuf);
606 al_set_target_bitmap(al_get_backbuffer(display));
607 al_draw_bitmap(scrbuf, w / 2 - al_get_bitmap_width(scrbuf) / 2, h / 2 - al_get_bitmap_height(scrbuf) / 2, 0);
610 drawing_duration = (drawing_duration +
get_usec(&drawing_chrono)) / 2;
617 }
while (!key[KEY_ESC] && !DONE);
619 al_uninstall_system();
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
#define _str(__PTR)
define true
void n_abort(char const *format,...)
abort program with a text
#define Free(__ptr)
Free Handler to get errors.
#define _nstr(__PTR)
N_STR or "NULL" string for logging purposes.
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
#define LOG_DEBUG
debug-level messages
#define LOG_ERR
error conditions
int set_log_file(char *file)
Set the logging to a file instead of stderr.
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL )
#define LOG_NOTICE
normal but significant condition
#define LOG_INFO
informational
size_t numY
number of cells in Y
double fluid_production_percentage
size of the produced fluid
bool showSmoke
display fluid as a colored cloud instead of black and white, can be combined with showPressure
size_t numX
number of cells in X
double overRelaxation
over relaxation
bool showPressure
display fluids pressures as color variations, can be combined with showSmoke
bool showPaint
activate a fonky palette, override smoke and pressure display
double * u
holder for u arrays
double dt
time between frames
double * s
holder for s arrays
size_t numIters
number of fluid processing iterations for each frame
double density
density of the fluid (not working ?)
double * m
holder for m arrays
double cScale
scale used to deduce cellX and cellY from screen/window width and height
double gravity
gravity on Y
int n_fluid_draw(N_FLUID *fluid)
draw a N_FLUID on screen / targert bitmap
int n_fluid_simulate_threaded(N_FLUID *fluid, THREAD_POOL *thread_pool)
a threaded version of N_FLUID global processing function
int n_fluid_simulate(N_FLUID *fluid)
non threaded version of N_FLUID global processing function
int n_fluid_resetObstacles(N_FLUID *fluid)
reset the obstacles set in a N_FLUID
int n_fluid_setObstacle(N_FLUID *fluid, double x, double y, double vx, double vy, double r)
set an obstacle in the fluid grid
N_FLUID * new_n_fluid(double density, double gravity, size_t numIters, double dt, double overRelaxation, size_t sx, size_t sy)
!
#define free_nstr(__ptr)
free a N_STR structure and set the pointer to NULL
#define nstrprintf(__nstr_var, __format,...)
Macro to quickly allocate and sprintf to N_STR.
A box including a string and his lenght.
int start_HiTimer(N_TIME *timer)
Initialize or restart from zero any N_TIME HiTimer.
time_t get_usec(N_TIME *timer)
Poll any N_TIME HiTimer, returning usec, and moving currentTime to startTime.
THREAD_POOL * new_thread_pool(size_t nbmaxthr, size_t nb_max_waiting)
Create a new pool of nbmaxthr threads.
long int get_nb_cpu_cores()
get number of core of current system
Structure of a trhead pool.
Common headers and low-level functions & define.
fluid management port from "How to write an Eulerian fluid simulator with 200 lines of code",...
List structures and definitions.
static FILE * log_file
static FILE handling if logging to file is enabled
N_STR and string function declaration.