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