2 libpsys - reusable particle system library.
3 Copyright (C) 2011-2018 John Tsiombikas <nuclear@member.fsf.org>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #ifdef PSYS_MULTITHREADED
28 static int spawn_particle(struct psys_emitter *em, struct psys_particle *p);
29 static void update_particle(struct psys_emitter *em, struct psys_particle *p, long tm, float dt, void *cls);
32 static struct psys_particle *ppool;
33 static int ppool_size;
34 #ifdef PSYS_MULTITHREADED
35 static pthread_mutex_t pool_lock = PTHREAD_MUTEX_INITIALIZER;
38 static struct psys_particle *palloc(void);
39 static void pfree(struct psys_particle *p);
41 /* --- constructors and shit --- */
43 struct psys_emitter *psys_create(void)
45 struct psys_emitter *em;
47 if(!(em = malloc(sizeof *em))) {
50 if(psys_init(em) == -1) {
57 void psys_free(struct psys_emitter *em)
63 int psys_init(struct psys_emitter *em)
65 memset(em, 0, sizeof *em);
67 if(anm_init_node(&em->prs) == -1) {
70 if(psys_init_attr(&em->attr) == -1) {
71 anm_destroy_node(&em->prs);
75 em->spawn = 0; /* no custom spawning, just the defaults */
76 em->update = update_particle;
80 void psys_destroy(struct psys_emitter *em)
82 struct psys_particle *part;
86 struct psys_particle *tmp = part;
91 psys_destroy_attr(&em->attr);
94 void psys_set_pos(struct psys_emitter *em, const float *pos, long tm)
96 anm_set_position(&em->prs, pos, ANM_MSEC2TM(tm));
99 void psys_set_pos3f(struct psys_emitter *em, float x, float y, float z, long tm)
101 anm_set_position3f(&em->prs, x, y, z, ANM_MSEC2TM(tm));
104 void psys_set_rot(struct psys_emitter *em, const float *qrot, long tm)
106 anm_set_rotation(&em->prs, qrot, ANM_MSEC2TM(tm));
109 void psys_set_pivot(struct psys_emitter *em, const float *pivot)
111 anm_set_pivot(&em->prs, pivot[0], pivot[1], pivot[2]);
114 void psys_set_pivot3f(struct psys_emitter *em, float x, float y, float z)
116 anm_set_pivot(&em->prs, x, y, z);
119 void psys_get_pos(struct psys_emitter *em, float *pos, long tm)
121 anm_get_node_position(&em->prs, pos, ANM_MSEC2TM(tm));
124 void psys_get_rot(struct psys_emitter *em, float *qrot, long tm)
126 anm_get_node_rotation(&em->prs, qrot, ANM_MSEC2TM(tm));
129 void psys_get_pivot(struct psys_emitter *em, float *pivot)
131 anm_get_pivot(&em->prs, pivot, pivot + 1, pivot + 2);
134 void psys_clear_collision_planes(struct psys_emitter *em)
136 struct psys_plane *plane;
140 struct psys_plane *tmp = plane;
146 int psys_add_collision_plane(struct psys_emitter *em, const float *plane, float elast)
148 struct psys_plane *node;
150 if(!(node = malloc(sizeof *node))) {
157 node->elasticity = elast;
158 node->next = em->planes;
163 void psys_add_particle(struct psys_emitter *em, struct psys_particle *p)
171 void psys_spawn_func(struct psys_emitter *em, psys_spawn_func_t func, void *cls)
177 void psys_update_func(struct psys_emitter *em, psys_update_func_t func, void *cls)
183 void psys_draw_func(struct psys_emitter *em, psys_draw_func_t draw,
184 psys_draw_start_func_t start, psys_draw_end_func_t end, void *cls)
187 em->draw_start = start;
192 /* --- update and render --- */
194 void psys_update(struct psys_emitter *em, long tm)
196 long delta_ms, spawn_tm, spawn_dt;
199 struct psys_particle *p, pdummy;
200 anm_time_t atm = ANM_MSEC2TM(tm);
206 fprintf(stderr, "psys_update called without an update callback\n");
210 delta_ms = tm - em->last_update;
214 dt = (float)delta_ms / 1000.0f;
216 psys_eval_attr(&em->attr, atm);
218 /* how many particles to spawn for this interval ? */
219 em->spawn_acc += psys_get_cur_value(&em->attr.rate) * delta_ms;
220 if(em->spawn_acc >= 1000) {
221 spawn_count = em->spawn_acc / 1000;
222 em->spawn_acc %= 1000;
228 spawn_dt = delta_ms / spawn_count;
230 spawn_tm = em->last_update;
231 for(i=0; i<spawn_count; i++) {
232 if(em->attr.max_particles >= 0 && em->pcount >= em->attr.max_particles) {
236 /* update emitter position for this spawning */
237 anm_get_position(&em->prs, em->cur_pos, ANM_MSEC2TM(spawn_tm));
239 if(!(p = palloc())) {
242 if(spawn_particle(em, p) == -1) {
245 spawn_tm += spawn_dt;
248 /* update all particles */
251 em->update(em, p, tm, dt, em->upd_cls);
255 /* cleanup dead particles */
256 pdummy.next = em->plist;
259 if(p->next->life <= 0) {
260 struct psys_particle *tmp = p->next;
261 p->next = p->next->next;
268 em->plist = pdummy.next;
270 em->last_update = tm;
273 void psys_draw(const struct psys_emitter *em)
275 struct psys_particle *p;
280 em->draw_start(em, em->draw_cls);
285 em->draw(em, p, em->draw_cls);
290 em->draw_end(em, em->draw_cls);
294 static int spawn_particle(struct psys_emitter *em, struct psys_particle *p)
297 struct psys_rnd3 rpos;
300 rpos.value[i] = em->cur_pos[i];
302 psys_get_cur_value3(&em->attr.spawn_range, rpos.range);
304 psys_eval_rnd3(&rpos, &p->x);
305 psys_eval_anm_rnd3(&em->attr.dir, PSYS_EVAL_CUR, &p->vx);
306 p->base_size = psys_eval_anm_rnd(&em->attr.size, PSYS_EVAL_CUR);
307 p->max_life = p->life = psys_eval_anm_rnd(&em->attr.life, PSYS_EVAL_CUR);
309 p->pattr = &em->attr.part_attr;
311 if(em->spawn && em->spawn(em, p, em->spawn_cls) == -1) {
315 psys_add_particle(em, p);
319 static void update_particle(struct psys_emitter *em, struct psys_particle *p, long tm, float dt, void *cls)
321 float accel[3], grav[3];
324 psys_get_cur_value3(&em->attr.grav, grav);
326 accel[0] = grav[0] - p->vx * em->attr.drag;
327 accel[1] = grav[1] - p->vy * em->attr.drag;
328 accel[2] = grav[2] - p->vz * em->attr.drag;
330 p->vx += accel[0] * dt;
331 p->vy += accel[1] * dt;
332 p->vz += accel[2] * dt;
338 /* update particle attributes */
339 t = (anm_time_t)(1000.0f * (p->max_life - p->life) / p->max_life);
341 psys_get_value3(&p->pattr->color, t, &p->r);
342 p->alpha = psys_get_value(&p->pattr->alpha, t);
343 p->size = p->base_size * psys_get_value(&p->pattr->size, t);
348 /* --- particle allocation pool --- */
350 static struct psys_particle *palloc(void)
352 struct psys_particle *p;
354 #ifdef PSYS_MULTITHREADED
355 pthread_mutex_lock(&pool_lock);
362 p = malloc(sizeof *p);
364 #ifdef PSYS_MULTITHREADED
365 pthread_mutex_unlock(&pool_lock);
371 static void pfree(struct psys_particle *p)
373 #ifdef PSYS_MULTITHREADED
374 pthread_mutex_lock(&pool_lock);
379 #ifdef PSYS_MULTITHREADED
380 pthread_mutex_unlock(&pool_lock);