X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?a=blobdiff_plain;ds=inline;f=libs%2Fpsys%2Fpsys.c;fp=libs%2Fpsys%2Fpsys.c;h=6825ae7eff0ce0089495707b613db1e986f52cc3;hb=fca3f24e31b3bbbe81ce0ef00da901480a2a92cc;hp=0000000000000000000000000000000000000000;hpb=e808f24e718ba3eae01b20dc3dc9e1526fd20871;p=andemo diff --git a/libs/psys/psys.c b/libs/psys/psys.c new file mode 100644 index 0000000..6825ae7 --- /dev/null +++ b/libs/psys/psys.c @@ -0,0 +1,370 @@ +/* +libpsys - reusable particle system library. +Copyright (C) 2011-2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ +#include +#include +#include +#include +#include +#include "psys.h" +#include + +static int spawn_particle(struct psys_emitter *em, struct psys_particle *p); +static void update_particle(struct psys_emitter *em, struct psys_particle *p, long tm, float dt, void *cls); + +/* particle pool */ +static struct psys_particle *ppool; +static int ppool_size; +static pthread_mutex_t pool_lock = PTHREAD_MUTEX_INITIALIZER; + +static struct psys_particle *palloc(void); +static void pfree(struct psys_particle *p); + +/* --- constructors and shit --- */ + +struct psys_emitter *psys_create(void) +{ + struct psys_emitter *em; + + if(!(em = malloc(sizeof *em))) { + return 0; + } + if(psys_init(em) == -1) { + free(em); + return 0; + } + return em; +} + +void psys_free(struct psys_emitter *em) +{ + psys_destroy(em); + free(em); +} + +int psys_init(struct psys_emitter *em) +{ + memset(em, 0, sizeof *em); + + if(anm_init_node(&em->prs) == -1) { + return -1; + } + if(psys_init_attr(&em->attr) == -1) { + anm_destroy_node(&em->prs); + return -1; + } + + em->spawn = 0; /* no custom spawning, just the defaults */ + em->update = update_particle; + return 0; +} + +void psys_destroy(struct psys_emitter *em) +{ + struct psys_particle *part; + + part = em->plist; + while(part) { + struct psys_particle *tmp = part; + part = part->next; + pfree(tmp); + } + + psys_destroy_attr(&em->attr); +} + +void psys_set_pos(struct psys_emitter *em, const float *pos, long tm) +{ + anm_set_position(&em->prs, pos, ANM_MSEC2TM(tm)); +} + +void psys_set_pos3f(struct psys_emitter *em, float x, float y, float z, long tm) +{ + anm_set_position3f(&em->prs, x, y, z, ANM_MSEC2TM(tm)); +} + +void psys_set_rot(struct psys_emitter *em, const float *qrot, long tm) +{ + anm_set_rotation(&em->prs, qrot, ANM_MSEC2TM(tm)); +} + +void psys_set_pivot(struct psys_emitter *em, const float *pivot) +{ + anm_set_pivot(&em->prs, pivot[0], pivot[1], pivot[2]); +} + +void psys_set_pivot3f(struct psys_emitter *em, float x, float y, float z) +{ + anm_set_pivot(&em->prs, x, y, z); +} + +void psys_get_pos(struct psys_emitter *em, float *pos, long tm) +{ + anm_get_node_position(&em->prs, pos, ANM_MSEC2TM(tm)); +} + +void psys_get_rot(struct psys_emitter *em, float *qrot, long tm) +{ + anm_get_node_rotation(&em->prs, qrot, ANM_MSEC2TM(tm)); +} + +void psys_get_pivot(struct psys_emitter *em, float *pivot) +{ + anm_get_pivot(&em->prs, pivot, pivot + 1, pivot + 2); +} + +void psys_clear_collision_planes(struct psys_emitter *em) +{ + struct psys_plane *plane; + + plane = em->planes; + while(plane) { + struct psys_plane *tmp = plane; + plane = plane->next; + free(tmp); + } +} + +int psys_add_collision_plane(struct psys_emitter *em, const float *plane, float elast) +{ + struct psys_plane *node; + + if(!(node = malloc(sizeof *node))) { + return -1; + } + node->nx = plane[0]; + node->ny = plane[1]; + node->nz = plane[2]; + node->d = plane[3]; + node->elasticity = elast; + node->next = em->planes; + em->planes = node; + return 0; +} + +void psys_add_particle(struct psys_emitter *em, struct psys_particle *p) +{ + p->next = em->plist; + em->plist = p; + + em->pcount++; +} + +void psys_spawn_func(struct psys_emitter *em, psys_spawn_func_t func, void *cls) +{ + em->spawn = func; + em->spawn_cls = cls; +} + +void psys_update_func(struct psys_emitter *em, psys_update_func_t func, void *cls) +{ + em->update = func; + em->upd_cls = cls; +} + +void psys_draw_func(struct psys_emitter *em, psys_draw_func_t draw, + psys_draw_start_func_t start, psys_draw_end_func_t end, void *cls) +{ + em->draw = draw; + em->draw_start = start; + em->draw_end = end; + em->draw_cls = cls; +} + +/* --- update and render --- */ + +void psys_update(struct psys_emitter *em, long tm) +{ + long delta_ms, spawn_tm, spawn_dt; + float dt; + int i, spawn_count; + struct psys_particle *p, pdummy; + anm_time_t atm = ANM_MSEC2TM(tm); + + if(!em->update) { + static int once; + if(!once) { + once = 1; + fprintf(stderr, "psys_update called without an update callback\n"); + } + } + + delta_ms = tm - em->last_update; + if(delta_ms <= 0) { + return; + } + dt = (float)delta_ms / 1000.0f; + + psys_eval_attr(&em->attr, atm); + + /* how many particles to spawn for this interval ? */ + em->spawn_acc += psys_get_cur_value(&em->attr.rate) * delta_ms; + if(em->spawn_acc >= 1000) { + spawn_count = em->spawn_acc / 1000; + em->spawn_acc %= 1000; + } else { + spawn_count = 0; + } + + if(spawn_count) { + spawn_dt = delta_ms / spawn_count; + } + spawn_tm = em->last_update; + for(i=0; iattr.max_particles >= 0 && em->pcount >= em->attr.max_particles) { + break; + } + + /* update emitter position for this spawning */ + anm_get_position(&em->prs, em->cur_pos, ANM_MSEC2TM(spawn_tm)); + + if(!(p = palloc())) { + return; + } + if(spawn_particle(em, p) == -1) { + pfree(p); + } + spawn_tm += spawn_dt; + } + + /* update all particles */ + p = em->plist; + while(p) { + em->update(em, p, tm, dt, em->upd_cls); + p = p->next; + } + + /* cleanup dead particles */ + pdummy.next = em->plist; + p = &pdummy; + while(p->next) { + if(p->next->life <= 0) { + struct psys_particle *tmp = p->next; + p->next = p->next->next; + pfree(tmp); + em->pcount--; + } else { + p = p->next; + } + } + em->plist = pdummy.next; + + em->last_update = tm; +} + +void psys_draw(const struct psys_emitter *em) +{ + struct psys_particle *p; + + assert(em->draw); + + if(em->draw_start) { + em->draw_start(em, em->draw_cls); + } + + p = em->plist; + while(p) { + em->draw(em, p, em->draw_cls); + p = p->next; + } + + if(em->draw_end) { + em->draw_end(em, em->draw_cls); + } +} + +static int spawn_particle(struct psys_emitter *em, struct psys_particle *p) +{ + int i; + struct psys_rnd3 rpos; + + for(i=0; i<3; i++) { + rpos.value[i] = em->cur_pos[i]; + } + psys_get_cur_value3(&em->attr.spawn_range, rpos.range); + + psys_eval_rnd3(&rpos, &p->x); + psys_eval_anm_rnd3(&em->attr.dir, PSYS_EVAL_CUR, &p->vx); + p->base_size = psys_eval_anm_rnd(&em->attr.size, PSYS_EVAL_CUR); + p->max_life = p->life = psys_eval_anm_rnd(&em->attr.life, PSYS_EVAL_CUR); + + p->pattr = &em->attr.part_attr; + + if(em->spawn && em->spawn(em, p, em->spawn_cls) == -1) { + return -1; + } + + psys_add_particle(em, p); + return 0; +} + +static void update_particle(struct psys_emitter *em, struct psys_particle *p, long tm, float dt, void *cls) +{ + float accel[3], grav[3]; + anm_time_t t; + + psys_get_cur_value3(&em->attr.grav, grav); + + accel[0] = grav[0] - p->vx * em->attr.drag; + accel[1] = grav[1] - p->vy * em->attr.drag; + accel[2] = grav[2] - p->vz * em->attr.drag; + + p->vx += accel[0] * dt; + p->vy += accel[1] * dt; + p->vz += accel[2] * dt; + + p->x += p->vx * dt; + p->y += p->vy * dt; + p->z += p->vz * dt; + + /* update particle attributes */ + t = (anm_time_t)(1000.0f * (p->max_life - p->life) / p->max_life); + + psys_get_value3(&p->pattr->color, t, &p->r); + p->alpha = psys_get_value(&p->pattr->alpha, t); + p->size = p->base_size * psys_get_value(&p->pattr->size, t); + + p->life -= dt; +} + +/* --- particle allocation pool --- */ + +static struct psys_particle *palloc(void) +{ + struct psys_particle *p; + + pthread_mutex_lock(&pool_lock); + if(ppool) { + p = ppool; + ppool = ppool->next; + ppool_size--; + } else { + p = malloc(sizeof *p); + } + pthread_mutex_unlock(&pool_lock); + + return p; +} + +static void pfree(struct psys_particle *p) +{ + pthread_mutex_lock(&pool_lock); + p->next = ppool; + ppool = p; + ppool_size++; + pthread_mutex_unlock(&pool_lock); +}