nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include "psys_impl.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: static int init_v3track(struct v3track *v3t); nuclear@0: static void destroy_v3track(struct v3track *v3t); nuclear@0: static void set_v3value(struct v3track *v3t, anm_time_t tm, vec3_t v); nuclear@0: static vec3_t get_v3value(struct v3track *v3t, anm_time_t tm); 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@1: psys_destroy(em); nuclear@0: return -1; nuclear@0: } nuclear@0: if(anm_init_track(&em->rate) == -1) { nuclear@1: psys_destroy(em); nuclear@1: return -1; nuclear@1: } nuclear@1: if(anm_init_track(&em->life) == -1) { nuclear@1: psys_destroy(em); nuclear@0: return -1; nuclear@0: } nuclear@0: if(init_v3track(&em->dir) == -1) { nuclear@1: psys_destroy(em); nuclear@1: return -1; nuclear@1: } nuclear@1: if(init_v3track(&em->grav) == -1) { nuclear@1: psys_destroy(em); 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 = psys_gl_draw; nuclear@0: em->draw_start = psys_gl_draw_start; nuclear@0: em->draw_end = psys_gl_draw_end; nuclear@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: anm_destroy_node(&em->prs); nuclear@0: anm_destroy_track(&em->rate); nuclear@0: destroy_v3track(&em->dir); 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: void psys_set_rate(struct psys_emitter *em, float rate, float tm) nuclear@0: { nuclear@0: anm_set_value(&em->rate, ANM_SEC2TM(tm), rate); nuclear@0: } nuclear@0: nuclear@1: void psys_set_life(struct psys_emitter *em, float life, float tm) nuclear@1: { nuclear@1: anm_set_value(&em->life, ANM_SEC2TM(tm), life); nuclear@1: } nuclear@1: nuclear@0: void psys_set_dir(struct psys_emitter *em, vec3_t dir, float tm) nuclear@0: { nuclear@0: set_v3value(&em->dir, ANM_SEC2TM(tm), dir); nuclear@0: } nuclear@0: nuclear@1: void psys_set_grav(struct psys_emitter *em, vec3_t grav, float tm) nuclear@1: { nuclear@1: set_v3value(&em->grav, ANM_SEC2TM(tm), grav); nuclear@1: } nuclear@1: nuclear@0: nuclear@0: void psys_clear_collision_planes(struct psys_emitter *em) nuclear@0: { nuclear@0: struct col_plane *plane; nuclear@0: nuclear@0: plane = em->planes; nuclear@0: while(plane) { nuclear@0: struct col_plane *tmp = plane; nuclear@0: plane = plane->next; nuclear@1: 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 col_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: 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: /* --- query current state --- */ nuclear@0: vec3_t psys_get_pos(struct psys_emitter *em) nuclear@0: { nuclear@0: return em->cur_pos; nuclear@0: } nuclear@0: nuclear@0: quat_t psys_get_rot(struct psys_emitter *em) nuclear@0: { nuclear@0: return em->cur_rot; nuclear@0: } nuclear@0: nuclear@0: float psys_get_rate(struct psys_emitter *em) nuclear@0: { nuclear@0: return em->cur_rate; nuclear@0: } nuclear@0: nuclear@0: float psys_get_life(struct psys_emitter *em) nuclear@0: { nuclear@0: return em->cur_life; nuclear@0: } nuclear@0: nuclear@0: vec3_t psys_get_dir(struct psys_emitter *em) nuclear@0: { nuclear@0: return em->cur_dir; nuclear@0: } nuclear@0: nuclear@1: vec3_t psys_get_grav(struct psys_emitter *em) nuclear@1: { nuclear@1: return em->cur_grav; nuclear@1: } 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; nuclear@0: int i, spawn_count; nuclear@0: struct psys_particle *p, pdummy; nuclear@0: anm_time_t atm; nuclear@0: nuclear@0: assert(em->spawn && em->update); nuclear@0: nuclear@0: atm = ANM_SEC2TM(tm); nuclear@0: nuclear@1: em->cur_rate = anm_get_value(&em->rate, atm); nuclear@0: dt = tm - em->last_update; nuclear@0: nuclear@0: /* how many particles to spawn for this interval ? */ nuclear@0: spawn_count = em->cur_rate * dt; nuclear@0: nuclear@0: #ifndef SUB_UPDATE_POS nuclear@1: em->cur_pos = anm_get_position(&em->prs, atm); nuclear@0: #endif nuclear@0: em->cur_dir = get_v3value(&em->dir, atm); nuclear@0: em->cur_life = anm_get_value(&em->life, atm); nuclear@1: em->cur_grav = get_v3value(&em->grav, atm); nuclear@0: nuclear@0: spawn_dt = dt / (float)spawn_count; nuclear@0: for(i=0; icur_pos = anm_get_position(&em->prs, ANM_SEC2TM(em->last_update + spawn_dt)); nuclear@0: #endif 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: } nuclear@0: nuclear@0: /* update all particles */ nuclear@0: p = em->plist; nuclear@0: while(p) { nuclear@1: 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: } else { nuclear@0: p = p->next; nuclear@0: } nuclear@0: } nuclear@0: em->plist = pdummy.next; nuclear@0: } nuclear@0: nuclear@0: void psys_draw(struct psys_emitter *em) nuclear@0: { nuclear@0: struct psys_particle *p; nuclear@0: nuclear@0: if(em->draw_start) { nuclear@1: em->draw_start(em, em->draw_cls); nuclear@0: } nuclear@0: nuclear@0: p = em->plist; nuclear@0: while(p) { nuclear@1: em->draw(em, p, em->draw_cls); nuclear@0: p = p->next; nuclear@0: } nuclear@0: nuclear@0: if(em->draw_end) { nuclear@1: 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: p->pos = em->cur_pos; nuclear@0: p->vel = em->cur_dir; nuclear@0: p->size = 1.0; nuclear@0: p->life = em->cur_life; 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@1: vec3_t accel; nuclear@0: nuclear@1: accel.x = em->cur_grav.x - p->vel.x * em->drag; nuclear@1: accel.y = em->cur_grav.y - p->vel.y * em->drag; nuclear@1: accel.z = em->cur_grav.z - p->vel.z * em->drag; nuclear@1: nuclear@1: p->vel.x += accel.x * dt; nuclear@1: p->vel.y += accel.y * dt; nuclear@1: 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: nuclear@0: /* --- v3track helper --- */ nuclear@0: nuclear@0: int init_v3track(struct v3track *v3t) nuclear@0: { nuclear@0: if(anm_init_track(&v3t->x) == -1) { nuclear@0: return -1; nuclear@0: } nuclear@0: if(anm_init_track(&v3t->y) == -1) { nuclear@0: anm_destroy_track(&v3t->x); nuclear@0: return -1; nuclear@0: } nuclear@0: if(anm_init_track(&v3t->z) == -1) { nuclear@0: anm_destroy_track(&v3t->x); nuclear@0: anm_destroy_track(&v3t->y); nuclear@0: return -1; nuclear@0: } nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: static void destroy_v3track(struct v3track *v3t) nuclear@0: { nuclear@0: anm_destroy_track(&v3t->x); nuclear@0: anm_destroy_track(&v3t->y); nuclear@0: anm_destroy_track(&v3t->z); nuclear@0: } nuclear@0: nuclear@0: static void set_v3value(struct v3track *v3t, anm_time_t tm, vec3_t v) nuclear@0: { nuclear@0: anm_set_value(&v3t->x, tm, v.x); nuclear@0: anm_set_value(&v3t->y, tm, v.y); nuclear@0: anm_set_value(&v3t->z, tm, v.z); nuclear@0: } nuclear@0: nuclear@0: static vec3_t get_v3value(struct v3track *v3t, anm_time_t tm) nuclear@0: { nuclear@0: vec3_t v; nuclear@0: v.x = anm_get_value(&v3t->x, tm); nuclear@0: v.y = anm_get_value(&v3t->y, tm); nuclear@0: v.z = anm_get_value(&v3t->z, tm); nuclear@0: return v; 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: if(p) { nuclear@0: memset(p, 0, sizeof *p); nuclear@1: /*reset_pattr(&p->attr);*/ 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: }