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@3: static float random_val(float x, float range); nuclear@3: static vec3_t random_vec3(vec3_t v, vec3_t range); nuclear@3: 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@3: if(anm_init_node(&em->prs) == -1) nuclear@3: goto err; nuclear@3: if(init_v3track(&em->pos_range) == -1) nuclear@3: goto err; nuclear@3: if(anm_init_track(&em->rate) == -1) nuclear@3: goto err; nuclear@3: if(anm_init_track(&em->life) == -1) nuclear@3: goto err; nuclear@3: if(anm_init_track(&em->life_range) == -1) nuclear@3: goto err; nuclear@3: if(anm_init_track(&em->size) == -1) nuclear@3: goto err; nuclear@3: if(anm_init_track(&em->size_range) == -1) nuclear@3: goto err; nuclear@3: if(init_v3track(&em->dir) == -1) nuclear@3: goto err; nuclear@3: if(init_v3track(&em->dir_range) == -1) nuclear@3: goto err; nuclear@3: if(init_v3track(&em->grav) == -1) nuclear@3: goto err; 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@3: err: nuclear@3: psys_destroy(em); nuclear@3: return -1; 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@3: destroy_v3track(&em->pos_range); nuclear@0: anm_destroy_track(&em->rate); nuclear@3: anm_destroy_track(&em->life); nuclear@3: anm_destroy_track(&em->size); nuclear@3: anm_destroy_track(&em->size_range); nuclear@0: destroy_v3track(&em->dir); nuclear@3: destroy_v3track(&em->dir_range); nuclear@3: destroy_v3track(&em->grav); nuclear@0: } nuclear@0: nuclear@2: void psys_set_texture(struct psys_emitter *em, unsigned int tex) nuclear@2: { nuclear@2: em->tex = tex; nuclear@2: } nuclear@2: nuclear@3: void psys_set_pos(struct psys_emitter *em, vec3_t pos, vec3_t range, float tm) nuclear@0: { nuclear@0: anm_set_position(&em->prs, pos, ANM_SEC2TM(tm)); nuclear@3: set_v3value(&em->pos_range, ANM_SEC2TM(tm), range); 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@3: void psys_set_life(struct psys_emitter *em, float life, float range, float tm) nuclear@1: { nuclear@1: anm_set_value(&em->life, ANM_SEC2TM(tm), life); nuclear@3: anm_set_value(&em->life_range, ANM_SEC2TM(tm), range); nuclear@1: } nuclear@1: nuclear@3: void psys_set_size(struct psys_emitter *em, float size, float range, float tm) nuclear@3: { nuclear@3: anm_set_value(&em->size, ANM_SEC2TM(tm), size); nuclear@3: anm_set_value(&em->size_range, ANM_SEC2TM(tm), range); nuclear@3: } nuclear@3: nuclear@3: void psys_set_dir(struct psys_emitter *em, vec3_t dir, vec3_t range, float tm) nuclear@0: { nuclear@0: set_v3value(&em->dir, ANM_SEC2TM(tm), dir); nuclear@3: set_v3value(&em->dir_range, ANM_SEC2TM(tm), range); 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@2: unsigned int psys_get_texture(struct psys_emitter *em) nuclear@2: { nuclear@2: return em->tex; nuclear@2: } nuclear@2: 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@2: 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; 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@2: em->spawn_acc += em->cur_rate * dt; nuclear@2: if(em->spawn_acc >= 1.0) { nuclear@2: spawn_count = em->spawn_acc; nuclear@2: em->spawn_acc = fmod(em->spawn_acc, 1.0); nuclear@2: } else { nuclear@2: spawn_count = 0; nuclear@2: } nuclear@0: 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@2: spawn_tm = em->last_update; nuclear@0: for(i=0; icur_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@2: spawn_tm += spawn_dt; 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@2: nuclear@2: em->last_update = tm; 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@3: p->pos = random_vec3(em->cur_pos, em->cur_pos_range); nuclear@3: p->vel = random_vec3(em->cur_dir, em->dir_range); nuclear@3: p->size = random_val(em->cur_size, em->cur_size_range); 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@2: nuclear@2: p->life -= 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: } nuclear@3: nuclear@3: static float random_val(float x, float range) nuclear@3: { nuclear@3: return x + range * (float)rand() / (float)RAND_MAX - 0.5 * range; nuclear@3: } nuclear@3: nuclear@3: static vec3_t random_vec3(vec3_t v, vec3_t range) nuclear@3: { nuclear@3: vec3_t res; nuclear@3: res.x = random_val(v.x, range.x); nuclear@3: res.y = random_val(v.y, range.y); nuclear@3: res.z = random_val(v.z, range.z); nuclear@3: return res; nuclear@3: }