Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
n_particles.c
Go to the documentation of this file.
1/*
2 * Nilorea Library
3 * Copyright (C) 2005-2026 Castagnier Mickael
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * permissions and limitations under the License.
16 *
17 * SPDX-License-Identifier: Apache-2.0
18 */
19
28#include "nilorea/n_particles.h"
29#include "math.h"
30
41int init_particle_system(PARTICLE_SYSTEM** psys, int max, double x, double y, double z, int max_sprites) {
42 __n_assert(!(*psys), n_log(LOG_ERR, "particle system %p already initialized", (*psys)); return FALSE);
43
44 Malloc((*psys), PARTICLE_SYSTEM, 1);
45 __n_assert((*psys), return FALSE);
46
47 start_HiTimer(&(*psys)->timer);
48
49 size_t list_max = (max <= 0) ? UNLIMITED_LIST_ITEMS : (size_t)max;
50 (*psys)->list = new_generic_list(list_max);
51
52 (*psys)->source[0] = x;
53 (*psys)->source[1] = y;
54 (*psys)->source[2] = z;
55
56 if (max_sprites > 0)
57 (*psys)->sprites = (ALLEGRO_BITMAP**)calloc((size_t)max_sprites, sizeof(ALLEGRO_BITMAP*));
58 else
59 (*psys)->sprites = NULL;
60
61 (*psys)->max_sprites = max_sprites;
62
63 (*psys)->emitters = new_generic_list(UNLIMITED_LIST_ITEMS);
64
65 return TRUE;
66} /* init_particle_system() */
67
79int add_particle(PARTICLE_SYSTEM* psys, int spr, int mode, int lifetime, int size, ALLEGRO_COLOR color, PHYSICS object) {
80 int it = 0;
81
82 PARTICLE* new_p = NULL;
83
84 /* nb_max_items == 0 means unlimited (matches list_push behavior) */
85 if (psys->list->nb_max_items > 0 && psys->list->nb_items >= psys->list->nb_max_items)
86 return FALSE;
87
88 for (it = 0; it < 3; it++) {
89 object.position[it] += psys->source[it];
90 }
91
92 Malloc(new_p, PARTICLE, 1);
93 __n_assert(new_p, return FALSE);
94
95 new_p->depth_sort = 1; /* default: depth-sorted */
96 new_p->spr_id = spr;
97 new_p->mode = mode;
98 new_p->lifetime = lifetime;
99 new_p->color = color;
100 new_p->size = size;
101
102 /* Initialize extended fields for backward compatibility:
103 * color_start == color_end means no lerp, age starts at 0 */
104 new_p->color_start = color;
105 new_p->color_end = color;
106 new_p->age = 0;
107 new_p->lifetime_max = lifetime;
108 new_p->size_start = size;
109 new_p->size_end = size;
110
111 memcpy(&new_p->object, &object, sizeof(PHYSICS));
112
113 return list_push(psys->list, new_p, &free);
114} /* add_particle() */
115
134int add_particle_ex(PARTICLE_SYSTEM* psys, int spr, int mode, int off_x, int off_y, int lifetime, int size, ALLEGRO_COLOR color, double vx, double vy, double vz, double ax, double ay, double az) {
135 PHYSICS object;
136 memset(&object, 0, sizeof(PHYSICS));
137 VECTOR3D_SET(object.position, off_x, off_y, 0.0);
138 VECTOR3D_SET(object.speed, vx, vy, vz);
139 VECTOR3D_SET(object.acceleration, ax, ay, az);
140 VECTOR3D_SET(object.orientation, 0.0, 0.0, 0.0);
141 VECTOR3D_SET(object.angular_speed, 0.0, 0.0, 0.0);
142 VECTOR3D_SET(object.angular_acceleration, 0.0, 0.0, 0.0);
143
144 return add_particle(psys, spr, mode, lifetime, size, color, object);
145} /* add_particle_ex () */
146
147/* NOTE: manage_particle_ex delta_t is in MICROSECONDS, matching
148 * update_physics_position() and the internal get_usec() timer.
149 * The header doc says "msecs" but the actual unit is microseconds.
150 * Lifetime and age fields on PARTICLE are in milliseconds. */
151
158static void _emitter_shape_offset(const PARTICLE_EMITTER* em, double* out_x, double* out_y) {
159 double ox = 0.0, oy = 0.0;
160 switch (em->shape) {
161 case 1: { /* line: random point along line of shape_w length */
162 double t = ((double)rand() / (double)RAND_MAX) - 0.5; /* -0.5..0.5 */
163 ox = t * em->shape_w;
164 oy = 0.0;
165 break;
166 }
167 case 2: { /* rect: random point in rectangle shape_w x shape_h */
168 double tx = ((double)rand() / (double)RAND_MAX) - 0.5;
169 double ty = ((double)rand() / (double)RAND_MAX) - 0.5;
170 ox = tx * em->shape_w;
171 oy = ty * em->shape_h;
172 break;
173 }
174 case 3: { /* circle: random point in circle of radius shape_w */
175 double angle = ((double)rand() / (double)RAND_MAX) * 2.0 * 3.14159265358979;
176 double r = sqrt((double)rand() / (double)RAND_MAX) * em->shape_w;
177 ox = cos(angle) * r;
178 oy = sin(angle) * r;
179 break;
180 }
181 default: /* point: no offset */
182 break;
183 }
184 /* Apply rotation */
185 if (em->shape_rotation != 0.0) {
186 double cr = cos(em->shape_rotation);
187 double sr = sin(em->shape_rotation);
188 double rx = ox * cr - oy * sr;
189 double ry = ox * sr + oy * cr;
190 ox = rx;
191 oy = ry;
192 }
193 *out_x = ox;
194 *out_y = oy;
195}
196
203static double _particle_rand_range(double vmin, double vmax) {
204 if (vmin >= vmax) return vmin;
205 return vmin + ((double)rand() / (double)RAND_MAX) * (vmax - vmin);
206}
207
214static int _particle_rand_range_i(int imin, int imax) {
215 if (imin >= imax) return imin;
216 return imin + (rand() % (imax - imin + 1));
217}
218
226static ALLEGRO_COLOR _color_lerp(ALLEGRO_COLOR a, ALLEGRO_COLOR b, float t) {
227 float ar, ag, ab, aa, br, bg, bb, ba;
228 al_unmap_rgba_f(a, &ar, &ag, &ab, &aa);
229 al_unmap_rgba_f(b, &br, &bg, &bb, &ba);
230 return al_map_rgba_f(ar + (br - ar) * t,
231 ag + (bg - ag) * t,
232 ab + (bb - ab) * t,
233 aa + (ba - aa) * t);
234}
235
242int manage_particle_ex(PARTICLE_SYSTEM* psys, double delta_t) {
243 __n_assert(psys, return FALSE);
244
245 /* Emitter spawning phase */
246 if (psys->emitters) {
247 LIST_NODE* em_node = psys->emitters->start;
248 while (em_node) {
249 PARTICLE_EMITTER* em = (PARTICLE_EMITTER*)em_node->ptr;
250 if (!em || !em->active) {
251 em_node = em_node->next;
252 continue;
253 }
254
255 if (em->burst_count > 0) {
256 /* Burst mode: emit all at once then deactivate */
257 for (int bi = 0; bi < em->burst_count; bi++) {
258 /* Enforce max_particles limit */
259 if (em->max_particles > 0 && em->live_count >= em->max_particles) break;
261 double vx = _particle_rand_range(em->velocity_min[0], em->velocity_max[0]);
262 double vy = _particle_rand_range(em->velocity_min[1], em->velocity_max[1]);
263 double vz = _particle_rand_range(em->velocity_min[2], em->velocity_max[2]);
264 double sox = 0.0, soy = 0.0;
265 _emitter_shape_offset(em, &sox, &soy);
266 add_particle_at(psys, em->spr_id, em->particle_mode,
267 em->position[0] + sox, em->position[1] + soy, em->position[2],
268 lt, em->size_start, em->size_end,
269 em->color_start, em->color_end,
270 vx, vy, vz,
271 em->acceleration[0], em->acceleration[1], em->acceleration[2]);
272 /* Set per-particle flags and back-pointer from emitter */
273 if (psys->list->end) {
274 PARTICLE* np = (PARTICLE*)psys->list->end->ptr;
275 if (np) {
276 np->additive = em->additive_blend;
277 np->depth_sort = em->depth_sort;
278 np->emitter = em;
279 em->live_count++;
280 }
281 }
282 }
283 em->active = 0;
284 } else {
285 /* Continuous mode: accumulate particles over time.
286 * delta_t is in microseconds, emit_rate is particles/second */
287 em->emit_accumulator += em->emit_rate * delta_t / 1000000.0;
288 while (em->emit_accumulator >= 1.0) {
289 /* Enforce max_particles limit */
290 if (em->max_particles > 0 && em->live_count >= em->max_particles) {
291 em->emit_accumulator = 0.0;
292 break;
293 }
294 em->emit_accumulator -= 1.0;
296 double vx = _particle_rand_range(em->velocity_min[0], em->velocity_max[0]);
297 double vy = _particle_rand_range(em->velocity_min[1], em->velocity_max[1]);
298 double vz = _particle_rand_range(em->velocity_min[2], em->velocity_max[2]);
299 double sox = 0.0, soy = 0.0;
300 _emitter_shape_offset(em, &sox, &soy);
301 add_particle_at(psys, em->spr_id, em->particle_mode,
302 em->position[0] + sox, em->position[1] + soy, em->position[2],
303 lt, em->size_start, em->size_end,
304 em->color_start, em->color_end,
305 vx, vy, vz,
306 em->acceleration[0], em->acceleration[1], em->acceleration[2]);
307 if (psys->list->end) {
308 PARTICLE* np = (PARTICLE*)psys->list->end->ptr;
309 if (np) {
310 np->additive = em->additive_blend;
311 np->depth_sort = em->depth_sort;
312 np->emitter = em;
313 em->live_count++;
314 }
315 }
316 }
317 }
318 em_node = em_node->next;
319 }
320 }
321
322 /* Particle tick phase */
323 LIST_NODE* node = psys->list->start;
324
325 while (node) {
326 PARTICLE* ptr = (PARTICLE*)node->ptr;
327 if (ptr->lifetime == -1) {
328 /* infinite lifetime: just update position */
329 update_physics_position(&ptr->object, delta_t);
330 node = node->next;
331 } else {
332 ptr->lifetime = (int)(ptr->lifetime - (delta_t / 1000.0));
333 if (ptr->lifetime > 0) {
334 update_physics_position(&ptr->object, delta_t);
335
336 /* Update age and lerp color/size.
337 * delta_t is in microseconds; age/lifetime are in milliseconds */
338 ptr->age += (int)(delta_t / 1000.0);
339 if (ptr->lifetime_max > 0) {
340 double ratio = (double)ptr->age / (double)ptr->lifetime_max;
341 if (ratio > 1.0) ratio = 1.0;
342 ptr->color = _color_lerp(ptr->color_start, ptr->color_end, (float)ratio);
343 ptr->size = ptr->size_start + (int)((double)(ptr->size_end - ptr->size_start) * ratio);
344 }
345
346 node = node->next;
347 } else {
348 LIST_NODE* node_to_kill = node;
349 node = node->next;
350 /* Decrement owning emitter's live count */
351 if (ptr->emitter) {
352 ptr->emitter->live_count--;
353 if (ptr->emitter->live_count < 0) ptr->emitter->live_count = 0;
354 }
355 ptr = remove_list_node(psys->list, node_to_kill, PARTICLE);
356 Free(ptr);
357 }
358 }
359 }
360
361 return TRUE;
362} /* manage_particle_ex() */
363
370 __n_assert(psys, return FALSE);
371
372 double delta_t = (double)get_usec(&psys->timer);
373 return manage_particle_ex(psys, delta_t);
374} /* manage_particle() */
375
386int draw_particle(PARTICLE_SYSTEM* psys, double xpos, double ypos, int w, int h, double range) {
387 __n_assert(psys, return FALSE);
388
389 LIST_NODE* node = NULL;
390 PARTICLE* ptr = NULL;
391
392 node = psys->list->start;
393
394 while (node) {
395 double x = 0, y = 0;
396
397 ptr = (PARTICLE*)node->ptr;
398 x = ptr->object.position[0] - xpos;
399 y = ptr->object.position[1] - ypos;
400
401 if ((x < -range) || (x > (w + range)) || (y < -range) || (y > (h + range))) {
402 node = node->next;
403 continue;
404 }
405
406 for (int it = 0; it < 3; it++) {
407 while (ptr->object.orientation[it] < 0.0)
408 ptr->object.orientation[it] += 256.0;
409
410 if (ptr->object.orientation[it] >= 256.0)
411 ptr->object.orientation[it] = fmod(ptr->object.orientation[it], 256.0);
412 }
413
414 if (ptr->mode == SINUS_PART) {
415 if (ptr->object.speed[0] != 0)
416 x = x + ptr->object.speed[0] * sin((ptr->object.position[0] / ptr->object.speed[0]));
417 else
418 x = x + ptr->object.speed[0] * sin(ptr->object.position[0]);
419
420 if (ptr->object.speed[1] != 0)
421 y = y + ptr->object.speed[1] * cos((ptr->object.position[1] / ptr->object.speed[1]));
422 else
423 y = y + ptr->object.speed[1] * sin(ptr->object.position[1]);
424
425 if (ptr->spr_id >= 0 && ptr->spr_id < psys->max_sprites && psys->sprites[ptr->spr_id]) {
426 int spr_w = al_get_bitmap_width(psys->sprites[ptr->spr_id]);
427 int spr_h = al_get_bitmap_height(psys->sprites[ptr->spr_id]);
428
429 al_draw_rotated_bitmap(psys->sprites[ptr->spr_id], (float)(spr_w / 2), (float)(spr_h / 2), (float)(x - spr_w / 2), (float)(y - spr_h / 2), (float)(ptr->object.orientation[2] * (2.0 * M_PI / 256.0)), 0);
430 } else
431 al_draw_circle((float)x, (float)y, (float)ptr->size, ptr->color, 1);
432 }
433
434 if (ptr->mode & NORMAL_PART) {
435 if (ptr->spr_id >= 0 && ptr->spr_id < psys->max_sprites && psys->sprites[ptr->spr_id]) {
436 int bmp_w = al_get_bitmap_width(psys->sprites[ptr->spr_id]);
437 int bmp_h = al_get_bitmap_height(psys->sprites[ptr->spr_id]);
438
439 al_draw_rotated_bitmap(psys->sprites[ptr->spr_id], (float)(bmp_w / 2), (float)(bmp_h / 2), (float)(x - bmp_w / 2), (float)(y - bmp_h / 2), (float)(ptr->object.orientation[2] * (2.0 * M_PI / 256.0)), 0);
440 } else
441 al_draw_circle((float)x, (float)y, (float)ptr->size, ptr->color, 1);
442 } else if (ptr->mode & PIXEL_PART) {
443 al_draw_filled_rectangle((float)(x - ptr->size), (float)(y - ptr->size), (float)(x + ptr->size), (float)(y + ptr->size), ptr->color);
444 } else
445 al_draw_circle((float)x, (float)y, (float)ptr->size, ptr->color, 1);
446 node = node->next;
447 }
448
449 return TRUE;
450} /* draw_particle() */
451
458 __n_assert((*psys), return FALSE);
459
460 while ((*psys)->list->start) {
461 PARTICLE* particle = remove_list_node((*psys)->list, (*psys)->list->start, PARTICLE);
462 Free(particle);
463 }
464 list_destroy(&(*psys)->list);
465 free_emitters((*psys));
466 list_destroy(&(*psys)->emitters);
467 FreeNoLog((*psys)->sprites);
468 Free((*psys));
469
470 return TRUE;
471} /* free_particle_system() */
472
481int move_particles(PARTICLE_SYSTEM* psys, double vx, double vy, double vz) {
482 __n_assert(psys, return FALSE);
483
484 LIST_NODE* node = NULL;
485 PARTICLE* ptr = NULL;
486
487 node = psys->list->start;
488
489 while (node) {
490 ptr = (PARTICLE*)node->ptr;
491 ptr->object.position[0] = ptr->object.position[0] + vx;
492 ptr->object.position[1] = ptr->object.position[1] + vy;
493 ptr->object.position[2] = ptr->object.position[2] + vz;
494 node = node->next;
495 }
496 return TRUE;
497} /* move_particles() */
498
505 __n_assert(psys, return NULL);
506 __n_assert(psys->emitters, return NULL);
507
508 PARTICLE_EMITTER* em = NULL;
509 Malloc(em, PARTICLE_EMITTER, 1);
510 __n_assert(em, return NULL);
511
512 memset(em, 0, sizeof(PARTICLE_EMITTER));
513 em->attached_entity_id = -1;
514 em->spr_id = -1;
515 em->depth_sort = 1; /* default: depth-sorted (caller can override to 0 for overlay) */
516
517 if (!list_push(psys->emitters, em, &free)) {
518 Free(em);
519 return NULL;
520 }
521
522 return em;
523} /* add_emitter() */
524
532 __n_assert(psys, return FALSE);
533 __n_assert(psys->emitters, return FALSE);
534 __n_assert(em, return FALSE);
535
536 /* Clear back-pointers on any particles owned by this emitter */
537 LIST_NODE* pnode = psys->list ? psys->list->start : NULL;
538 while (pnode) {
539 PARTICLE* p = (PARTICLE*)pnode->ptr;
540 if (p && p->emitter == em) {
541 p->emitter = NULL;
542 }
543 pnode = pnode->next;
544 }
545
546 LIST_NODE* node = psys->emitters->start;
547 while (node) {
548 if (node->ptr == em) {
550 Free(removed);
551 return TRUE;
552 }
553 node = node->next;
554 }
555 return FALSE;
556} /* remove_emitter() */
557
564 __n_assert(psys, return FALSE);
565 if (!psys->emitters) return TRUE;
566
567 while (psys->emitters->start) {
569 Free(em);
570 }
571 return TRUE;
572} /* free_emitters() */
573
595int add_particle_at(PARTICLE_SYSTEM* psys, int spr, int mode, double px, double py, double pz, int lifetime, int size_start, int size_end, ALLEGRO_COLOR color_start, ALLEGRO_COLOR color_end, double vx, double vy, double vz, double ax, double ay, double az) {
596 __n_assert(psys, return FALSE);
597
598 /* nb_max_items == 0 means unlimited (matches list_push behavior) */
599 if (psys->list->nb_max_items > 0 && psys->list->nb_items >= psys->list->nb_max_items)
600 return FALSE;
601
602 PARTICLE* new_p = NULL;
603 Malloc(new_p, PARTICLE, 1);
604 __n_assert(new_p, return FALSE);
605
606 new_p->depth_sort = 1;
607 new_p->spr_id = spr;
608 new_p->mode = mode;
609 new_p->lifetime = lifetime;
610 new_p->lifetime_max = lifetime;
611 new_p->age = 0;
612 new_p->color = color_start;
613 new_p->color_start = color_start;
614 new_p->color_end = color_end;
615 new_p->size = size_start;
616 new_p->size_start = size_start;
617 new_p->size_end = size_end;
618
619 VECTOR3D_SET(new_p->object.position, px, py, pz);
620 VECTOR3D_SET(new_p->object.speed, vx, vy, vz);
621 VECTOR3D_SET(new_p->object.acceleration, ax, ay, az);
622 VECTOR3D_SET(new_p->object.orientation, 0.0, 0.0, 0.0);
623 VECTOR3D_SET(new_p->object.angular_speed, 0.0, 0.0, 0.0);
624 VECTOR3D_SET(new_p->object.angular_acceleration, 0.0, 0.0, 0.0);
625
626 return list_push(psys->list, new_p, &free);
627} /* add_particle_at() */
static int mode
#define M_PI
#define FreeNoLog(__ptr)
Free Handler without log.
Definition n_common.h:272
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
Definition n_common.h:204
#define __n_assert(__ptr, __ret)
macro to assert things
Definition n_common.h:279
#define Free(__ptr)
Free Handler to get errors.
Definition n_common.h:263
LIST_NODE * end
pointer to the end of the list
Definition n_list.h:68
void * ptr
void pointer to store
Definition n_list.h:46
size_t nb_max_items
Maximum number of items in the list.
Definition n_list.h:63
LIST_NODE * start
pointer to the start of the list
Definition n_list.h:66
size_t nb_items
number of item currently in the list
Definition n_list.h:61
struct LIST_NODE * next
pointer to the next node
Definition n_list.h:52
#define UNLIMITED_LIST_ITEMS
flag to pass to new_generic_list for an unlimited number of item in the list.
Definition n_list.h:73
int list_push(LIST *list, void *ptr, void(*destructor)(void *ptr))
Add a pointer to the end of the list.
Definition n_list.c:228
#define remove_list_node(__LIST_, __NODE_, __TYPE_)
Remove macro helper for void pointer casting.
Definition n_list.h:98
int list_destroy(LIST **list)
Empty and Free a list container.
Definition n_list.c:548
LIST * new_generic_list(size_t max_items)
Initialiaze a generic list container to max_items pointers.
Definition n_list.c:37
Structure of a generic list node.
Definition n_list.h:44
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:89
#define LOG_ERR
error conditions
Definition n_log.h:76
int start_HiTimer(N_TIME *timer)
Initialize or restart from zero any N_TIME HiTimer.
Definition n_time.c:85
time_t get_usec(N_TIME *timer)
Poll any N_TIME HiTimer, returning usec, and moving currentTime to startTime.
Definition n_time.c:107
double shape_h
shape dimension 2: rect height (world units)
int size_start
particle size at birth
int particle_mode
particle draw mode (NORMAL_PART, CIRCLE_PART, PIXEL_PART, FIRE_PART, etc.)
N_TIME timer
Internal: particle system timer.
LIST * emitters
list of PARTICLE_EMITTER pointers
int size
current size of particle
Definition n_particles.h:83
int max_sprites
size of the picture library
VECTOR3D velocity_max
random velocity range max
ALLEGRO_COLOR color
color of the particle
Definition n_particles.h:86
ALLEGRO_COLOR color_end
end color for lerp
Definition n_particles.h:94
int additive
1=additive blend, 0=normal alpha
int depth_sort
1=depth-sorted with terrain, 0=flat overlay after iso pass
int active
1=emitting, 0=paused
PHYSICS object
particle physical properties
Definition n_particles.h:89
int spr_id
sprite id in library
Definition n_particles.h:81
int size_end
particle size at death
int lifetime
lifetime (counts down, msecs)
Definition n_particles.h:79
ALLEGRO_COLOR color_start
particle start color
ALLEGRO_COLOR color_end
particle end color (lerp over lifetime)
int shape
emission shape: 0=point, 1=line, 2=rect, 3=circle
VECTOR3D velocity_min
random velocity range min
int live_count
current live particle count for this emitter (managed by system)
VECTOR3D source
Coordinate of emitting point (used by add_particle/add_particle_ex)
double emit_accumulator
internal: fractional particle accumulator
VECTOR3D position
emitter world position
int size_start
particle size at birth
LIST * list
list of PARTICLE pointers
int attached_entity_id
-1=not attached, >=0=entity id
int spr_id
sprite id or -1
struct PARTICLE_EMITTER * emitter
back-pointer to owning emitter (NULL if spawned manually)
int mode
particle mode: NORMAL_PART,SINUS_PART,PIXEL_PART
Definition n_particles.h:77
double emit_rate
particles per second (continuous mode)
int max_particles
per-emitter particle cap (0=unlimited)
ALLEGRO_BITMAP ** sprites
Library of picture for the particles.
int lifetime_min
particle lifetime range min (msecs)
int lifetime_max
original lifetime at birth (needed for lerp ratio = age/lifetime_max)
Definition n_particles.h:98
int size_end
particle size at death
double shape_rotation
shape rotation in radians
int depth_sort
1=depth-sorted with terrain, 0=flat overlay
double shape_w
shape dimension 1: line length, rect width, or circle radius (world units)
int lifetime_max
particle lifetime range max (msecs)
int burst_count
if >0, emit this many instantly then deactivate
ALLEGRO_COLOR color_start
start color for lerp (set by emitter, ignored if color_start == color_end)
Definition n_particles.h:92
VECTOR3D acceleration
constant acceleration (gravity etc.)
int additive_blend
1=additive blend, 0=normal alpha blend
int age
current age in msecs (incremented by manage_particle_ex)
Definition n_particles.h:96
int draw_particle(PARTICLE_SYSTEM *psys, double xpos, double ypos, int w, int h, double range)
draw particles of a particle system
#define NORMAL_PART
classic moving particle
Definition n_particles.h:54
int add_particle_at(PARTICLE_SYSTEM *psys, int spr, int mode, double px, double py, double pz, int lifetime, int size_start, int size_end, ALLEGRO_COLOR color_start, ALLEGRO_COLOR color_end, double vx, double vy, double vz, double ax, double ay, double az)
add a particle at an absolute world position (not relative to psys->source)
int move_particles(PARTICLE_SYSTEM *psys, double vx, double vy, double vz)
move all particles of a particle system by a given offset
PARTICLE_EMITTER * add_emitter(PARTICLE_SYSTEM *psys)
add a new emitter to the particle system
#define PIXEL_PART
pixel particle
Definition n_particles.h:68
int free_emitters(PARTICLE_SYSTEM *psys)
free all emitters in the particle system
int manage_particle_ex(PARTICLE_SYSTEM *psys, double delta_t)
update particles positions using provided delta time
int remove_emitter(PARTICLE_SYSTEM *psys, const PARTICLE_EMITTER *em)
remove an emitter from the particle system and free it
int free_particle_system(PARTICLE_SYSTEM **psys)
destroy and free a particle system
int manage_particle(PARTICLE_SYSTEM *psys)
update particles positions using particle system internal timer
#define SINUS_PART
sinus based moving particle
Definition n_particles.h:56
int add_particle_ex(PARTICLE_SYSTEM *psys, int spr, int mode, int off_x, int off_y, int lifetime, int size, ALLEGRO_COLOR color, double vx, double vy, double vz, double ax, double ay, double az)
add a particle to a particle system, all in line version (you have to set the PHYSICS object paramete...
int add_particle(PARTICLE_SYSTEM *psys, int spr, int mode, int lifetime, int size, ALLEGRO_COLOR color, PHYSICS object)
add a particle to a particle system
Definition n_particles.c:79
int init_particle_system(PARTICLE_SYSTEM **psys, int max, double x, double y, double z, int max_sprites)
initialize a particle system
Definition n_particles.c:41
Structure of a single particle.
Definition n_particles.h:75
Structure of a particle emitter.
Structure of a particle system.
VECTOR3D speed
vx,vy,vz actual speed
Definition n_3d.h:76
VECTOR3D orientation
ax,ay,az actual rotation position
Definition n_3d.h:80
VECTOR3D position
x,y,z actual position
Definition n_3d.h:74
VECTOR3D acceleration
ax,ay,az actual acceleration
Definition n_3d.h:78
VECTOR3D angular_acceleration
rax,ray,raz actual angular acceleration
Definition n_3d.h:84
VECTOR3D angular_speed
rvx,rvy,rvz actual angular speed
Definition n_3d.h:82
#define VECTOR3D_SET(VECTOR, X, Y, Z)
helper to set a VECTOR3D position
Definition n_3d.h:62
int update_physics_position(PHYSICS *object, double delta_t)
Update object position.
Definition n_3d.c:106
structure of the physics of an object
Definition n_3d.h:70
static double _particle_rand_range(double vmin, double vmax)
helper to generate a random double between min and max
static ALLEGRO_COLOR _color_lerp(ALLEGRO_COLOR a, ALLEGRO_COLOR b, float t)
helper to linearly interpolate a color
static void _emitter_shape_offset(const PARTICLE_EMITTER *em, double *out_x, double *out_y)
compute shape-offset spawn position for an emitter
static int _particle_rand_range_i(int imin, int imax)
helper to generate a random int between min and max (inclusive)
Particles management.