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/>.
26 static int spawn_particle(struct psys_emitter *em, struct psys_particle *p);
27 static void update_particle(struct psys_emitter *em, struct psys_particle *p, long tm, float dt, void *cls);
30 static struct psys_particle *ppool;
31 static int ppool_size;
32 static pthread_mutex_t pool_lock = PTHREAD_MUTEX_INITIALIZER;
34 static struct psys_particle *palloc(void);
35 static void pfree(struct psys_particle *p);
37 /* --- constructors and shit --- */
39 struct psys_emitter *psys_create(void)
41 struct psys_emitter *em;
43 if(!(em = malloc(sizeof *em))) {
46 if(psys_init(em) == -1) {
53 void psys_free(struct psys_emitter *em)
59 int psys_init(struct psys_emitter *em)
61 memset(em, 0, sizeof *em);
63 if(anm_init_node(&em->prs) == -1) {
66 if(psys_init_attr(&em->attr) == -1) {
67 anm_destroy_node(&em->prs);
71 em->spawn = 0; /* no custom spawning, just the defaults */
72 em->update = update_particle;
76 void psys_destroy(struct psys_emitter *em)
78 struct psys_particle *part;
82 struct psys_particle *tmp = part;
87 psys_destroy_attr(&em->attr);
90 void psys_set_pos(struct psys_emitter *em, const float *pos, long tm)
92 anm_set_position(&em->prs, pos, ANM_MSEC2TM(tm));
95 void psys_set_pos3f(struct psys_emitter *em, float x, float y, float z, long tm)
97 anm_set_position3f(&em->prs, x, y, z, ANM_MSEC2TM(tm));
100 void psys_set_rot(struct psys_emitter *em, const float *qrot, long tm)
102 anm_set_rotation(&em->prs, qrot, ANM_MSEC2TM(tm));
105 void psys_set_pivot(struct psys_emitter *em, const float *pivot)
107 anm_set_pivot(&em->prs, pivot[0], pivot[1], pivot[2]);
110 void psys_set_pivot3f(struct psys_emitter *em, float x, float y, float z)
112 anm_set_pivot(&em->prs, x, y, z);
115 void psys_get_pos(struct psys_emitter *em, float *pos, long tm)
117 anm_get_node_position(&em->prs, pos, ANM_MSEC2TM(tm));
120 void psys_get_rot(struct psys_emitter *em, float *qrot, long tm)
122 anm_get_node_rotation(&em->prs, qrot, ANM_MSEC2TM(tm));
125 void psys_get_pivot(struct psys_emitter *em, float *pivot)
127 anm_get_pivot(&em->prs, pivot, pivot + 1, pivot + 2);
130 void psys_clear_collision_planes(struct psys_emitter *em)
132 struct psys_plane *plane;
136 struct psys_plane *tmp = plane;
142 int psys_add_collision_plane(struct psys_emitter *em, const float *plane, float elast)
144 struct psys_plane *node;
146 if(!(node = malloc(sizeof *node))) {
153 node->elasticity = elast;
154 node->next = em->planes;
159 void psys_add_particle(struct psys_emitter *em, struct psys_particle *p)
167 void psys_spawn_func(struct psys_emitter *em, psys_spawn_func_t func, void *cls)
173 void psys_update_func(struct psys_emitter *em, psys_update_func_t func, void *cls)
179 void psys_draw_func(struct psys_emitter *em, psys_draw_func_t draw,
180 psys_draw_start_func_t start, psys_draw_end_func_t end, void *cls)
183 em->draw_start = start;
188 /* --- update and render --- */
190 void psys_update(struct psys_emitter *em, long tm)
192 long delta_ms, spawn_tm, spawn_dt;
195 struct psys_particle *p, pdummy;
196 anm_time_t atm = ANM_MSEC2TM(tm);
202 fprintf(stderr, "psys_update called without an update callback\n");
206 delta_ms = tm - em->last_update;
210 dt = (float)delta_ms / 1000.0f;
212 psys_eval_attr(&em->attr, atm);
214 /* how many particles to spawn for this interval ? */
215 em->spawn_acc += psys_get_cur_value(&em->attr.rate) * delta_ms;
216 if(em->spawn_acc >= 1000) {
217 spawn_count = em->spawn_acc / 1000;
218 em->spawn_acc %= 1000;
224 spawn_dt = delta_ms / spawn_count;
226 spawn_tm = em->last_update;
227 for(i=0; i<spawn_count; i++) {
228 if(em->attr.max_particles >= 0 && em->pcount >= em->attr.max_particles) {
232 /* update emitter position for this spawning */
233 anm_get_position(&em->prs, em->cur_pos, ANM_MSEC2TM(spawn_tm));
235 if(!(p = palloc())) {
238 if(spawn_particle(em, p) == -1) {
241 spawn_tm += spawn_dt;
244 /* update all particles */
247 em->update(em, p, tm, dt, em->upd_cls);
251 /* cleanup dead particles */
252 pdummy.next = em->plist;
255 if(p->next->life <= 0) {
256 struct psys_particle *tmp = p->next;
257 p->next = p->next->next;
264 em->plist = pdummy.next;
266 em->last_update = tm;
269 void psys_draw(const struct psys_emitter *em)
271 struct psys_particle *p;
276 em->draw_start(em, em->draw_cls);
281 em->draw(em, p, em->draw_cls);
286 em->draw_end(em, em->draw_cls);
290 static int spawn_particle(struct psys_emitter *em, struct psys_particle *p)
293 struct psys_rnd3 rpos;
296 rpos.value[i] = em->cur_pos[i];
298 psys_get_cur_value3(&em->attr.spawn_range, rpos.range);
300 psys_eval_rnd3(&rpos, &p->x);
301 psys_eval_anm_rnd3(&em->attr.dir, PSYS_EVAL_CUR, &p->vx);
302 p->base_size = psys_eval_anm_rnd(&em->attr.size, PSYS_EVAL_CUR);
303 p->max_life = p->life = psys_eval_anm_rnd(&em->attr.life, PSYS_EVAL_CUR);
305 p->pattr = &em->attr.part_attr;
307 if(em->spawn && em->spawn(em, p, em->spawn_cls) == -1) {
311 psys_add_particle(em, p);
315 static void update_particle(struct psys_emitter *em, struct psys_particle *p, long tm, float dt, void *cls)
317 float accel[3], grav[3];
320 psys_get_cur_value3(&em->attr.grav, grav);
322 accel[0] = grav[0] - p->vx * em->attr.drag;
323 accel[1] = grav[1] - p->vy * em->attr.drag;
324 accel[2] = grav[2] - p->vz * em->attr.drag;
326 p->vx += accel[0] * dt;
327 p->vy += accel[1] * dt;
328 p->vz += accel[2] * dt;
334 /* update particle attributes */
335 t = (anm_time_t)(1000.0f * (p->max_life - p->life) / p->max_life);
337 psys_get_value3(&p->pattr->color, t, &p->r);
338 p->alpha = psys_get_value(&p->pattr->alpha, t);
339 p->size = p->base_size * psys_get_value(&p->pattr->size, t);
344 /* --- particle allocation pool --- */
346 static struct psys_particle *palloc(void)
348 struct psys_particle *p;
350 pthread_mutex_lock(&pool_lock);
356 p = malloc(sizeof *p);
358 pthread_mutex_unlock(&pool_lock);
363 static void pfree(struct psys_particle *p)
365 pthread_mutex_lock(&pool_lock);
369 pthread_mutex_unlock(&pool_lock);