37#define N_FLUID_U_FIELD 0
39#define N_FLUID_V_FIELD 1
41#define N_FLUID_S_FIELD 2
50void n_memset(
void* dst,
const void* val,
size_t size,
size_t count) {
51 char* ptr = (
char*)dst;
53 memcpy(ptr, val, size);
67 list_destroy(&(*fluid)->solveIncompressibility_chunk_list);
95N_FLUID*
new_n_fluid(
double density,
double gravity,
size_t numIters,
double dt,
double overRelaxation,
size_t sx,
size_t sy) {
105 fluid->
h = 1.0 / 100.0;
107 fluid->
numX = sx + 2;
108 fluid->
numY = sy + 2;
113 if (!fluid->
u)
goto cleanup_fluid;
115 if (!fluid->
newU)
goto cleanup_fluid;
117 if (!fluid->
v)
goto cleanup_fluid;
119 if (!fluid->
newV)
goto cleanup_fluid;
121 if (!fluid->
p)
goto cleanup_fluid;
123 if (!fluid->
s)
goto cleanup_fluid;
125 if (!fluid->
m)
goto cleanup_fluid;
127 if (!fluid->
newM)
goto cleanup_fluid;
142 if (nb_cores_l <= 0) {
145 size_t nb_cores = (size_t)nb_cores_l;
153 size_t steps = (fluid->
numX / nb_cores > 0) ? fluid->
numX / nb_cores : 1;
157 for (
size_t i = 1; i < fluid->
numX - 1; i += steps) {
161 params->
x_end = i + steps;
173 for (
size_t i = 1; i < fluid->
numX - 1; i += steps) {
177 params->
x_end = i + steps;
189 for (
size_t i = 1; i < fluid->
numX - 1; i += steps) {
193 params->
x_end = i + steps;
205 for (
size_t i = 1; i < fluid->
numX - 1; i += steps) {
209 params->
x_end = i + steps;
244 size_t n = fluid->
numY;
245 for (
size_t i = params->
x_start; i < params->
x_end; i++) {
246 for (
size_t j = params->
y_start; j < params->
y_end; j++) {
247 if (!
_z(fluid, s[i * n + j]) && !
_z(fluid, s[i * n + j - 1]))
248 fluid->
v[i * n + j] += fluid->
gravity * fluid->
dt;
262 size_t n = fluid->
numY;
263 for (
size_t i = 1; i < fluid->
numX; i++) {
264 for (
size_t j = 1; j < fluid->
numY - 1; j++) {
265 if (!
_z(fluid, s[i * n + j]) && !
_z(fluid, s[i * n + j - 1]))
266 fluid->
v[i * n + j] += fluid->
gravity * fluid->
dt;
281 double cp = (fluid->
density * fluid->
h) / fluid->
dt;
283 size_t n = fluid->
numY;
284 for (
size_t i = params->
x_start; i < params->
x_end; i++) {
285 for (
size_t j = params->
y_start; j < params->
y_end; j++) {
286 if (
_z(fluid, s[i * n + j]))
289 double sx0 = fluid->
s[(i - 1) * n + j];
290 double sx1 = fluid->
s[(i + 1) * n + j];
291 double sy0 = fluid->
s[i * n + j - 1];
292 double sy1 = fluid->
s[i * n + j + 1];
293 double s = sx0 + sx1 + sy0 + sy1;
297 double div = fluid->
u[(i + 1) * n + j] - fluid->
u[i * n + j] + fluid->
v[i * n + j + 1] - fluid->
v[i * n + j];
299 fluid->
p[i * n + j] += cp * p;
300 fluid->
u[i * n + j] -= sx0 * p;
301 fluid->
u[(i + 1) * n + j] += sx1 * p;
302 fluid->
v[i * n + j] -= sy0 * p;
303 fluid->
v[i * n + j + 1] += sy1 * p;
316 size_t n = fluid->
numY;
318 double cp = (fluid->
density * fluid->
h) / fluid->
dt;
320 for (
size_t iter = 0; iter < fluid->
numIters; iter++) {
321 for (
size_t i = 1; i < fluid->
numX - 1; i++) {
322 for (
size_t j = 1; j < fluid->
numY - 1; j++) {
323 if (
_z(fluid, s[i * n + j]))
326 double sx0 = fluid->
s[(i - 1) * n + j];
327 double sx1 = fluid->
s[(i + 1) * n + j];
328 double sy0 = fluid->
s[i * n + j - 1];
329 double sy1 = fluid->
s[i * n + j + 1];
330 double s = sx0 + sx1 + sy0 + sy1;
334 double div = fluid->
u[(i + 1) * n + j] - fluid->
u[i * n + j] + fluid->
v[i * n + j + 1] - fluid->
v[i * n + j];
336 fluid->
p[i * n + j] += cp * p;
337 fluid->
u[i * n + j] -= sx0 * p;
338 fluid->
u[(i + 1) * n + j] += sx1 * p;
339 fluid->
v[i * n + j] -= sy0 * p;
340 fluid->
v[i * n + j + 1] += sy1 * p;
354 size_t n = fluid->
numY;
355 for (
size_t i = 0; i < fluid->
numX; i++) {
356 fluid->
u[i * n + 0] = fluid->
u[i * n + 1];
357 fluid->
u[i * n + fluid->
numY - 1] = fluid->
u[i * n + fluid->
numY - 2];
359 for (
size_t j = 0; j < fluid->
numY; j++) {
360 fluid->
v[0 * n + j] = fluid->
v[1 * n + j];
361 fluid->
v[(fluid->
numX - 1) * n + j] = fluid->
v[(fluid->
numX - 2) * n + j];
376 size_t n = fluid->
numY;
377 double h1 = 1.0 / fluid->
h;
378 double h2 = 0.5 * fluid->
h;
380 x = MAX(MIN(x, (
double)fluid->
numX * fluid->
h), fluid->
h);
381 y = MAX(MIN(y, (
double)fluid->
numY * fluid->
h), fluid->
h);
386 const double* f = NULL;
408 double x0 = MIN(floor((x - dx) * h1), (
double)(fluid->
numX - 1));
409 double tx = ((x - dx) - x0 * fluid->
h) * h1;
410 double x1 = MIN(x0 + 1, (
double)(fluid->
numX - 1));
412 double y0 = MIN(floor((y - dy) * h1), (
double)(fluid->
numY - 1));
413 double ty = ((y - dy) - y0 * fluid->
h) * h1;
414 double y1 = MIN(y0 + 1, (
double)(fluid->
numY - 1));
416 double sx = 1.0 - tx;
417 double sy = 1.0 - ty;
419 double val = sx * sy * f[(size_t)(x0 * (
double)n + y0)] +
420 tx * sy * f[(
size_t)(x1 * (double)n + y0)] +
421 tx * ty * f[(size_t)(x1 * (
double)n + y1)] +
422 sx * ty * f[(
size_t)(x0 * (double)n + y1)];
436 if (j == 0)
return 0.0;
437 size_t n = fluid->
numY;
438 double u = (fluid->
u[i * n + j - 1] + fluid->
u[i * n + j] +
439 fluid->
u[(i + 1) * n + j - 1] + fluid->
u[(i + 1) * n + j]) *
454 if (i == 0)
return 0.0;
455 size_t n = fluid->
numY;
456 double v = (fluid->
v[(i - 1) * n + j] + fluid->
v[i * n + j] +
457 fluid->
v[(i - 1) * n + j + 1] + fluid->
v[i * n + j + 1]) *
471 size_t n = fluid->
numY;
472 double h2 = 0.5 * fluid->
h;
473 for (
size_t i = params->
x_start; i < params->
x_end; i++) {
474 for (
size_t j = params->
y_start; j < params->
y_end; j++) {
475 size_t index = i * n + j;
477 if (!
_z(fluid, s[index]) && !
_z(fluid, s[(i - 1) * n + j]) && j < fluid->
numY - 1) {
478 double x = (double)i * fluid->
h;
479 double y = (double)j * fluid->
h + h2;
480 double u = fluid->
u[index];
483 x = x - fluid->
dt * u;
484 y = y - fluid->
dt * v;
486 fluid->
newU[index] = u;
489 if (!
_z(fluid, s[index]) && !
_z(fluid, s[index - 1]) && i < fluid->numX - 1) {
490 double x = (double)i * fluid->
h + h2;
491 double y = (double)j * fluid->
h;
494 double v = fluid->
v[index];
495 x = x - fluid->
dt * u;
496 y = y - fluid->
dt * v;
498 fluid->
newV[index] = v;
513 memcpy(fluid->
newU, fluid->
u, fluid->
numCells *
sizeof(
double));
514 memcpy(fluid->
newV, fluid->
v, fluid->
numCells *
sizeof(
double));
516 size_t n = fluid->
numY;
517 double h2 = 0.5 * fluid->
h;
518 for (
size_t i = 1; i < fluid->
numX; i++) {
519 for (
size_t j = 1; j < fluid->
numY; j++) {
520 size_t index = i * n + j;
522 if (!
_z(fluid, s[index]) && !
_z(fluid, s[(i - 1) * n + j]) && j < fluid->numY - 1) {
523 double x = (double)i * fluid->
h;
524 double y = (double)j * fluid->
h + h2;
525 double u = fluid->
u[index];
528 x = x - fluid->
dt * u;
529 y = y - fluid->
dt * v;
531 fluid->
newU[index] = u;
534 if (!
_z(fluid, s[index]) && !
_z(fluid, s[index - 1]) && i < fluid->numX - 1) {
535 double x = (double)i * fluid->
h + h2;
536 double y = (double)j * fluid->
h;
539 double v = fluid->
v[index];
540 x = x - fluid->
dt * u;
541 y = y - fluid->
dt * v;
543 fluid->
newV[index] = v;
547 double* ptr = fluid->
u;
548 fluid->
u = fluid->
newU;
552 fluid->
v = fluid->
newV;
567 size_t n = fluid->
numY;
568 double h2 = 0.5 * fluid->
h;
569 for (
size_t i = params->
x_start; i < params->
x_end; i++) {
570 for (
size_t j = params->
y_start; j < params->
y_end; j++) {
571 size_t index = i * n + j;
572 if (!
_z(fluid, s[index])) {
573 double u = (fluid->
u[index] + fluid->
u[(i + 1) * n + j]) * 0.5;
574 double v = (fluid->
v[index] + fluid->
v[index + 1]) * 0.5;
575 double x = (double)i * fluid->
h + h2 - fluid->
dt * u;
576 double y = (double)j * fluid->
h + h2 - fluid->
dt * v;
593 size_t n = fluid->
numY;
594 double h2 = 0.5 * fluid->
h;
596 memcpy(fluid->
newM, fluid->
m, fluid->
numCells *
sizeof(
double));
598 for (
size_t i = 1; i < fluid->
numX - 1; i++) {
599 for (
size_t j = 1; j < fluid->
numY - 1; j++) {
600 size_t index = i * n + j;
601 if (!
_z(fluid, s[index])) {
602 double u = (fluid->
u[index] + fluid->
u[(i + 1) * n + j]) * 0.5;
603 double v = (fluid->
v[index] + fluid->
v[index + 1]) * 0.5;
604 double x = (double)i * fluid->
h + h2 - fluid->
dt * u;
605 double y = (double)j * fluid->
h + h2 - fluid->
dt * v;
611 double* ptr = fluid->
m;
612 fluid->
m = fluid->
newM;
626 memset(fluid->
p, 0, fluid->
numCells *
sizeof(
double));
652 memset(fluid->
p, 0, fluid->
numCells *
sizeof(
double));
655 for (
size_t iter = 0; iter < fluid->
numIters; iter++) {
668 memcpy(fluid->
newU, fluid->
u, fluid->
numCells *
sizeof(
double));
669 memcpy(fluid->
newV, fluid->
v, fluid->
numCells *
sizeof(
double));
678 double* ptr = fluid->
u;
679 fluid->
u = fluid->
newU;
683 fluid->
v = fluid->
newV;
687 memcpy(fluid->
newM, fluid->
m, fluid->
numCells *
sizeof(
double));
696 fluid->
m = fluid->
newM;
715 size_t n = fluid->
numY;
716 for (
size_t i = 1; i < fluid->
numX - 2; i++) {
717 for (
size_t j = 1; j < fluid->
numY - 2; j++) {
718 double dx = ((double)i + 0.5) - x;
719 double dy = ((double)j + 0.5) - y;
721 if (i > 7 && (dx * dx + dy * dy < r * r)) {
722 fluid->
s[i * n + j] = 0.0;
723 fluid->
m[i * n + j] = 1.0;
724 fluid->
u[i * n + j] = vx;
725 fluid->
u[(i + 1) * n + j] = vx;
726 fluid->
v[i * n + j] = vy;
727 fluid->
v[i * n + j + 1] = vy;
742 size_t n = fluid->
numY;
743 for (
size_t i = 1; i < fluid->
numX - 2; i++) {
744 for (
size_t j = 1; j < fluid->
numY - 2; j++) {
745 fluid->
s[i * n + j] = 1.0;
766 al_lock_bitmap(bitmap, al_get_bitmap_format(bitmap), ALLEGRO_LOCK_READONLY);
768 size_t n = fluid->
numY;
769 for (
size_t i = 1; i < fluid->
numX - 2; i++) {
770 for (
size_t j = 1; j < fluid->
numY - 2; j++) {
771 double dx = ((double)i + 0.5) - x;
772 double dy = ((double)j + 0.5) - y;
774 if (i > 7 && (dx * dx + dy * dy < r * r)) {
775 fluid->
s[i * n + j] = 0.0;
776 fluid->
m[i * n + j] = 1.0;
777 fluid->
u[i * n + j] = vx;
778 fluid->
v[i * n + j] = vy;
780 fluid->
u[(i + 1) * n + j] = vx;
781 fluid->
v[i * n + j + 1] = vy;
786 al_unlock_bitmap(bitmap);
800 val = MIN(MAX(val, minVal), maxVal - 0.0001);
801 double d = maxVal - minVal;
805 val = (val - minVal) / d;
808 size_t num = (size_t)floor(val / m);
809 double s = (val - (double)num * m) / m;
810 double r = 0.0, g = 0.0, b = 0.0;
834 return al_map_rgb_f((
float)r, (
float)g, (
float)b);
845 size_t n = fluid->
numY;
847 double minP = fluid->
p[0];
848 double maxP = fluid->
p[0];
851 for (
size_t i = 0; i < fluid->
numCells; i++) {
852 minP = MIN(minP, fluid->
p[i]);
853 maxP = MAX(maxP, fluid->
p[i]);
858 double cScale = fluid->
cScale;
860 for (
size_t i = 0; i < fluid->
numX; i++) {
861 for (
size_t j = 0; j < fluid->
numY; j++) {
862 double xd = (double)i * cScale;
863 double yd = (double)j * cScale;
864 double cxd = xd + cScale;
865 double cyd = yd + cScale;
867 double s = fluid->
m[i * n + j];
872 double p = fluid->
p[i * n + j];
875 float color_vec_f[3] = {0.0, 0.0, 0.0};
876 al_unmap_rgb_f(color, color_vec_f, color_vec_f + 1, color_vec_f + 2);
877 color = al_map_rgb_f((
float)MAX(0.0, color_vec_f[0] - s), (
float)MAX(0.0, color_vec_f[1] - s), (
float)MAX(0.0, color_vec_f[2] - s));
880 color = al_map_rgb_f((
float)(1.0 - s), 0.0f, 0.0f);
882 color = al_map_rgb_f((
float)s, (
float)s, (
float)s);
884 al_draw_filled_rectangle((
float)xd, (
float)yd, (
float)cxd, (
float)cyd, color);
THREAD_POOL * thread_pool
#define FreeNoLog(__ptr)
Free Handler without log.
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
#define __n_assert(__ptr, __ret)
macro to assert things
LIST_NODE * end
pointer to the end of the list
void * ptr
void pointer to store
int list_push(LIST *list, void *ptr, void(*destructor)(void *ptr))
Add a pointer to the end of the list.
#define list_foreach(__ITEM_, __LIST_)
ForEach macro helper, safe for node removal during iteration.
int list_destroy(LIST **list)
Empty and Free a list container.
LIST * new_generic_list(size_t max_items)
Initialiaze a generic list container to max_items pointers.
double * newU
holder for newU arrays
size_t numY
number of cells in Y
size_t y_start
y start point
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
LIST * advectSmoke_chunk_list
preprocessed list of threaded procs parameters, for n_fluid_advectSmoke
double overRelaxation
over relaxation
bool showPressure
display fluids pressures as color variations, can be combined with showSmoke
void * ptr
pointer to data which will be used in the proc
double positive_float_tolerance
fluid double positive precision setting
LIST * solveIncompressibility_chunk_list
preprocessed list of threaded procs parameters, for n_fluid_solveIncompressibility
bool showPaint
activate a fonky palette, override smoke and pressure display
double * u
holder for u arrays
LIST * integrate_chunk_list
preprocessed list of threaded procs parameters, for n_fluid_integrate
double * newV
holder for newV arrays
double dt
time between frames
double * s
holder for s arrays
double * v
holder for v arrays
size_t numCells
total number of cells
size_t numIters
number of fluid processing iterations for each frame
double density
density of the fluid (not working ?)
LIST * advectVel_chunk_list
preprocessed list of threaded procs parameters, for n_fluid_advectVel
double * m
holder for m arrays
double cScale
scale used to deduce cellX and cellY from screen/window width and height
double * newM
holder for newM arrays
size_t numZ
number of cells in Z
double * p
holder for p arrays
size_t x_start
x start point
double gravity
gravity on Y
double negative_float_tolerance
fluid double negative precision setting
double n_fluid_avgV(N_FLUID *fluid, size_t i, size_t j)
compute the average V value at a fluid position using it's surrounding
int n_fluid_draw(N_FLUID *fluid)
draw a N_FLUID on screen / targert bitmap
#define _z(__fluid, __component)
test if component is near zero, according to fluid's precision
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
double n_fluid_avgU(N_FLUID *fluid, size_t i, size_t j)
compute the average U value at a fluid position using it's surrounding
double n_fluid_sampleField(N_FLUID *fluid, double x, double y, uint32_t field)
compute a sample value at a field position
int n_fluid_extrapolate(N_FLUID *fluid)
non threaded extrapolation function
int n_fluid_advectSmoke(N_FLUID *fluid)
non threaded version of add smoke function
int n_fluid_solveIncompressibility(N_FLUID *fluid)
non threaded version of incompressibility solving function
ALLEGRO_COLOR n_fluid_getSciColor(const N_FLUID *fluid, double val, double minVal, double maxVal)
get funky colors for the fluid
int n_fluid_resetObstacles(N_FLUID *fluid)
reset the obstacles set in a N_FLUID
#define _zd(__fluid, __value)
test if value is near zero, according to fluid's precision
int n_fluid_integrate(N_FLUID *fluid)
non threaded version of integration function
int n_fluid_setObstacle(N_FLUID *fluid, double x, double y, double vx, double vy, double r)
set an obstacle in the fluid grid
int destroy_n_fluid(N_FLUID **fluid)
destroy a fluid structure
int n_fluid_advectVel(N_FLUID *fluid)
non threaded version of add velocities function
N_FLUID * new_n_fluid(double density, double gravity, size_t numIters, double dt, double overRelaxation, size_t sx, size_t sy)
return a newly allocated fluid
structure passed to a threaded fluid process
int start_threaded_pool(THREAD_POOL *thread_pool)
Launch the process waiting for execution in the thread pool.
#define SYNCED_PROC
processing mode for added func, synced start, not queued
int add_threaded_process(THREAD_POOL *thread_pool, void *(*func_ptr)(void *param), void *param, int mode)
add a function and params to a thread pool
int refresh_thread_pool(THREAD_POOL *thread_pool)
try to add some waiting DIRECT_PROCs on some free thread slots, else do nothing
int wait_for_synced_threaded_pool(THREAD_POOL *thread_pool)
wait for all the launched process, blocking but light on the CPU as there is no polling
long int get_nb_cpu_cores()
get number of core of current system
Structure of a thread pool.
Common headers and low-level functions & define.
#define N_FLUID_S_FIELD
array number for s
#define N_FLUID_U_FIELD
array number for u
void * n_fluid_integrate_proc(void *ptr)
ready to be threaded integration function
void * n_fluid_advectVel_proc(void *ptr)
ready to be threaded add velocities function
void * n_fluid_advectSmoke_proc(void *ptr)
ready to be threaded add smoke function
int n_fluid_setObstacleFromBitmap(N_FLUID *fluid, ALLEGRO_BITMAP *bitmap, double x, double y, double vx, double vy, double r)
set an obstacle in the fluid grid from a bitmap mask
#define N_FLUID_V_FIELD
array number for v
void * n_fluid_solveIncompressibility_proc(void *ptr)
ready to be threaded incompressibility solving function
void n_memset(void *dst, const void *val, size_t size, size_t count)
memset bytes to a custom value
Fluid management port from "How to write an Eulerian fluid simulator with 200 lines of code",...