nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include "psys.h" nuclear@0: nuclear@0: static int spawn(struct psys_emitter *em, struct psys_particle *p, void *cls); nuclear@0: static void update_particle(struct psys_emitter *em, struct psys_particle *p, float tm, float dt, void *cls); nuclear@0: nuclear@0: /* particle pool */ nuclear@0: static struct psys_particle *ppool; nuclear@0: static int ppool_size; nuclear@0: static pthread_mutex_t pool_lock = PTHREAD_MUTEX_INITIALIZER; nuclear@0: nuclear@0: static struct psys_particle *palloc(void); nuclear@0: static void pfree(struct psys_particle *p); nuclear@0: nuclear@0: /* --- constructors and shit --- */ nuclear@0: nuclear@0: struct psys_emitter *psys_create(void) nuclear@0: { nuclear@0: struct psys_emitter *em; nuclear@0: nuclear@0: if(!(em = malloc(sizeof *em))) { nuclear@0: return 0; nuclear@0: } nuclear@0: if(psys_init(em) == -1) { nuclear@0: free(em); nuclear@0: return 0; nuclear@0: } nuclear@0: return em; nuclear@0: } nuclear@0: nuclear@0: void psys_free(struct psys_emitter *em) nuclear@0: { nuclear@0: psys_destroy(em); nuclear@0: free(em); nuclear@0: } nuclear@0: nuclear@0: int psys_init(struct psys_emitter *em) nuclear@0: { nuclear@0: memset(em, 0, sizeof *em); nuclear@0: nuclear@0: if(anm_init_node(&em->prs) == -1) { nuclear@0: return -1; nuclear@0: } nuclear@0: if(psys_init_attr(&em->attr) == -1) { nuclear@0: anm_destroy_node(&em->prs); nuclear@0: return -1; nuclear@0: } nuclear@0: nuclear@0: em->spawn = spawn; nuclear@0: em->update = update_particle; nuclear@0: nuclear@0: em->draw = 0; nuclear@0: em->draw_start = 0; nuclear@0: em->draw_end = 0; nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: void psys_destroy(struct psys_emitter *em) nuclear@0: { nuclear@0: struct psys_particle *part; nuclear@0: nuclear@0: part = em->plist; nuclear@0: while(part) { nuclear@0: struct psys_particle *tmp = part; nuclear@0: part = part->next; nuclear@0: pfree(tmp); nuclear@0: } nuclear@0: nuclear@0: psys_destroy_attr(&em->attr); nuclear@0: } nuclear@0: nuclear@0: void psys_set_pos(struct psys_emitter *em, vec3_t pos, float tm) nuclear@0: { nuclear@0: anm_set_position(&em->prs, pos, ANM_SEC2TM(tm)); nuclear@0: } nuclear@0: nuclear@0: void psys_set_rot(struct psys_emitter *em, quat_t rot, float tm) nuclear@0: { nuclear@0: anm_set_rotation(&em->prs, rot, ANM_SEC2TM(tm)); nuclear@0: } nuclear@0: nuclear@0: void psys_set_pivot(struct psys_emitter *em, vec3_t pivot) nuclear@0: { nuclear@0: anm_set_pivot(&em->prs, pivot); nuclear@0: } nuclear@0: nuclear@0: vec3_t psys_get_pos(struct psys_emitter *em, float tm) nuclear@0: { nuclear@0: return anm_get_node_position(&em->prs, ANM_SEC2TM(tm)); nuclear@0: } nuclear@0: nuclear@0: quat_t psys_get_rot(struct psys_emitter *em, float tm) nuclear@0: { nuclear@0: return anm_get_node_rotation(&em->prs, ANM_SEC2TM(tm)); nuclear@0: } nuclear@0: nuclear@0: vec3_t psys_get_pivot(struct psys_emitter *em) nuclear@0: { nuclear@0: return anm_get_pivot(&em->prs); nuclear@0: } nuclear@0: nuclear@0: void psys_clear_collision_planes(struct psys_emitter *em) nuclear@0: { nuclear@0: struct psys_plane *plane; nuclear@0: nuclear@0: plane = em->planes; nuclear@0: while(plane) { nuclear@0: struct psys_plane *tmp = plane; nuclear@0: plane = plane->next; nuclear@0: free(tmp); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: int psys_add_collision_plane(struct psys_emitter *em, plane_t plane, float elast) nuclear@0: { nuclear@0: struct psys_plane *node; nuclear@0: nuclear@0: if(!(node = malloc(sizeof *node))) { nuclear@0: return -1; nuclear@0: } nuclear@0: node->p = plane; nuclear@0: node->elasticity = elast; nuclear@0: node->next = em->planes; nuclear@0: em->planes = node; nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: void psys_add_particle(struct psys_emitter *em, struct psys_particle *p) nuclear@0: { nuclear@0: p->next = em->plist; nuclear@0: em->plist = p; nuclear@0: nuclear@0: em->pcount++; nuclear@0: } nuclear@0: nuclear@0: void psys_spawn_func(struct psys_emitter *em, psys_spawn_func_t func, void *cls) nuclear@0: { nuclear@0: em->spawn = func; nuclear@0: em->spawn_cls = cls; nuclear@0: } nuclear@0: nuclear@0: void psys_update_func(struct psys_emitter *em, psys_update_func_t func, void *cls) nuclear@0: { nuclear@0: em->update = func; nuclear@0: em->upd_cls = cls; nuclear@0: } nuclear@0: nuclear@0: void psys_draw_func(struct psys_emitter *em, psys_draw_func_t draw, nuclear@0: psys_draw_start_func_t start, psys_draw_end_func_t end, void *cls) nuclear@0: { nuclear@0: em->draw = draw; nuclear@0: em->draw_start = start; nuclear@0: em->draw_end = end; nuclear@0: em->draw_cls = cls; nuclear@0: } nuclear@0: nuclear@0: /* --- update and render --- */ nuclear@0: nuclear@0: void psys_update(struct psys_emitter *em, float tm) nuclear@0: { nuclear@0: float dt, spawn_dt, spawn_tm; nuclear@0: int i, spawn_count; nuclear@0: struct psys_particle *p, pdummy; nuclear@0: anm_time_t atm = ANM_SEC2TM(tm); nuclear@0: nuclear@0: assert(em->spawn && em->update); nuclear@0: nuclear@0: dt = tm - em->last_update; nuclear@0: if(dt <= 0.0) { nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: psys_eval_attr(&em->attr, atm); nuclear@0: nuclear@0: /* how many particles to spawn for this interval ? */ nuclear@0: em->spawn_acc += psys_get_cur_value(&em->attr.rate) * dt; nuclear@0: if(em->spawn_acc >= 1.0) { nuclear@0: spawn_count = em->spawn_acc; nuclear@0: em->spawn_acc = fmod(em->spawn_acc, 1.0); nuclear@0: } else { nuclear@0: spawn_count = 0; nuclear@0: } nuclear@0: nuclear@0: spawn_dt = dt / (float)spawn_count; nuclear@0: spawn_tm = em->last_update; nuclear@0: for(i=0; iattr.max_particles >= 0 && em->pcount >= em->attr.max_particles) { nuclear@0: break; nuclear@0: } nuclear@0: nuclear@0: /* update emitter position for this spawning */ nuclear@0: em->cur_pos = anm_get_position(&em->prs, ANM_SEC2TM(spawn_tm)); nuclear@0: nuclear@0: if(!(p = palloc())) { nuclear@0: return; nuclear@0: } nuclear@0: if(em->spawn(em, p, em->spawn_cls) == -1) { nuclear@0: pfree(p); nuclear@0: } nuclear@0: spawn_tm += spawn_dt; nuclear@0: } nuclear@0: nuclear@0: /* update all particles */ nuclear@0: p = em->plist; nuclear@0: while(p) { nuclear@0: em->update(em, p, tm, dt, em->upd_cls); nuclear@0: p = p->next; nuclear@0: } nuclear@0: nuclear@0: /* cleanup dead particles */ nuclear@0: pdummy.next = em->plist; nuclear@0: p = &pdummy; nuclear@0: while(p->next) { nuclear@0: if(p->next->life <= 0) { nuclear@0: struct psys_particle *tmp = p->next; nuclear@0: p->next = p->next->next; nuclear@0: pfree(tmp); nuclear@0: em->pcount--; nuclear@0: } else { nuclear@0: p = p->next; nuclear@0: } nuclear@0: } nuclear@0: em->plist = pdummy.next; nuclear@0: nuclear@0: em->last_update = tm; nuclear@0: } nuclear@0: nuclear@0: void psys_draw(const struct psys_emitter *em) nuclear@0: { nuclear@0: struct psys_particle *p; nuclear@0: nuclear@0: assert(em->draw); nuclear@0: nuclear@0: if(em->draw_start) { nuclear@0: em->draw_start(em, em->draw_cls); nuclear@0: } nuclear@0: nuclear@0: p = em->plist; nuclear@0: while(p) { nuclear@0: em->draw(em, p, em->draw_cls); nuclear@0: p = p->next; nuclear@0: } nuclear@0: nuclear@0: if(em->draw_end) { nuclear@0: em->draw_end(em, em->draw_cls); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: static int spawn(struct psys_emitter *em, struct psys_particle *p, void *cls) nuclear@0: { nuclear@0: struct psys_rnd3 rpos; nuclear@0: rpos.value = em->cur_pos; nuclear@0: rpos.range = psys_get_cur_value3(&em->attr.spawn_range); nuclear@0: nuclear@0: p->pos = psys_eval_rnd3(&rpos); nuclear@0: p->vel = psys_eval_anm_rnd3(&em->attr.dir, PSYS_EVAL_CUR); nuclear@0: p->base_size = psys_eval_anm_rnd(&em->attr.size, PSYS_EVAL_CUR); nuclear@0: p->max_life = p->life = psys_eval_anm_rnd(&em->attr.life, PSYS_EVAL_CUR); nuclear@0: nuclear@0: p->pattr = &em->attr.part_attr; nuclear@0: nuclear@0: psys_add_particle(em, p); nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: static void update_particle(struct psys_emitter *em, struct psys_particle *p, float tm, float dt, void *cls) nuclear@0: { nuclear@0: vec3_t accel, grav; nuclear@0: anm_time_t t; nuclear@0: nuclear@0: grav = psys_get_cur_value3(&em->attr.grav); nuclear@0: nuclear@0: accel.x = grav.x - p->vel.x * em->attr.drag; nuclear@0: accel.y = grav.y - p->vel.y * em->attr.drag; nuclear@0: accel.z = grav.z - p->vel.z * em->attr.drag; nuclear@0: nuclear@0: p->vel.x += accel.x * dt; nuclear@0: p->vel.y += accel.y * dt; nuclear@0: p->vel.z += accel.z * dt; nuclear@0: nuclear@0: p->pos.x += p->vel.x * dt; nuclear@0: p->pos.y += p->vel.y * dt; nuclear@0: p->pos.z += p->vel.z * dt; nuclear@0: nuclear@0: /* update particle attributes */ nuclear@0: t = (anm_time_t)(1000.0 * (p->max_life - p->life) / p->max_life); nuclear@0: nuclear@0: p->color = psys_get_value3(&p->pattr->color, t); nuclear@0: p->alpha = psys_get_value(&p->pattr->alpha, t); nuclear@0: p->size = p->base_size * psys_get_value(&p->pattr->size, t); nuclear@0: nuclear@0: p->life -= dt; nuclear@0: } nuclear@0: nuclear@0: /* --- particle allocation pool --- */ nuclear@0: nuclear@0: static struct psys_particle *palloc(void) nuclear@0: { nuclear@0: struct psys_particle *p; nuclear@0: nuclear@0: pthread_mutex_lock(&pool_lock); nuclear@0: if(ppool) { nuclear@0: p = ppool; nuclear@0: ppool = ppool->next; nuclear@0: ppool_size--; nuclear@0: } else { nuclear@0: p = malloc(sizeof *p); nuclear@0: } nuclear@0: pthread_mutex_unlock(&pool_lock); nuclear@0: nuclear@0: return p; nuclear@0: } nuclear@0: nuclear@0: static void pfree(struct psys_particle *p) nuclear@0: { nuclear@0: pthread_mutex_lock(&pool_lock); nuclear@0: p->next = ppool; nuclear@0: ppool = p; nuclear@0: ppool_size++; nuclear@0: pthread_mutex_unlock(&pool_lock); nuclear@0: }