nuclear@0: #define _GNU_SOURCE nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: nuclear@0: #ifdef _MSC_VER nuclear@0: #include nuclear@0: #else nuclear@0: #include nuclear@0: #endif nuclear@0: nuclear@0: #include "pattr.h" nuclear@0: nuclear@0: enum { nuclear@0: OPT_STR, nuclear@0: OPT_NUM, nuclear@0: OPT_NUM_RANGE, nuclear@0: OPT_VEC, nuclear@0: OPT_VEC_RANGE nuclear@0: }; nuclear@0: nuclear@0: struct cfgopt { nuclear@0: char *name; nuclear@0: int type; nuclear@0: long tm; nuclear@0: char *valstr; nuclear@0: vec3_t val, valrng; nuclear@0: }; nuclear@0: nuclear@0: static int init_particle_attr(struct psys_particle_attributes *pattr); nuclear@0: static void destroy_particle_attr(struct psys_particle_attributes *pattr); nuclear@0: static struct cfgopt *get_cfg_opt(const char *line); nuclear@0: static void release_cfg_opt(struct cfgopt *opt); nuclear@0: static char *stripspace(char *str); nuclear@0: nuclear@0: static void *tex_cls; nuclear@0: static unsigned int (*load_texture)(const char*, void*); nuclear@0: static void (*unload_texture)(unsigned int, void*); nuclear@0: nuclear@0: nuclear@0: void psys_texture_loader(unsigned int (*load)(const char*, void*), void (*unload)(unsigned int, void*), void *cls) nuclear@0: { nuclear@0: load_texture = load; nuclear@0: unload_texture = unload; nuclear@0: tex_cls = cls; nuclear@0: } nuclear@0: nuclear@0: struct psys_attributes *psys_create_attr(void) nuclear@0: { nuclear@0: struct psys_attributes *attr = malloc(sizeof *attr); nuclear@0: if(attr) { nuclear@0: if(psys_init_attr(attr) == -1) { nuclear@0: free(attr); nuclear@0: attr = 0; nuclear@0: } nuclear@0: } nuclear@0: return attr; nuclear@0: } nuclear@0: nuclear@0: void psys_free_attr(struct psys_attributes *attr) nuclear@0: { nuclear@0: psys_destroy_attr(attr); nuclear@0: free(attr); nuclear@0: } nuclear@0: nuclear@0: int psys_init_attr(struct psys_attributes *attr) nuclear@0: { nuclear@0: memset(attr, 0, sizeof *attr); nuclear@0: nuclear@0: if(psys_init_track3(&attr->spawn_range) == -1) nuclear@0: goto err; nuclear@0: if(psys_init_track(&attr->rate) == -1) nuclear@0: goto err; nuclear@0: if(psys_init_anm_rnd(&attr->life) == -1) nuclear@0: goto err; nuclear@0: if(psys_init_anm_rnd(&attr->size) == -1) nuclear@0: goto err; nuclear@0: if(psys_init_anm_rnd3(&attr->dir) == -1) nuclear@0: goto err; nuclear@0: if(psys_init_track3(&attr->grav) == -1) nuclear@0: goto err; nuclear@0: nuclear@0: if(init_particle_attr(&attr->part_attr) == -1) nuclear@0: goto err; nuclear@0: nuclear@0: attr->max_particles = -1; nuclear@0: nuclear@0: anm_set_track_default(&attr->size.value.trk, 1.0); nuclear@0: anm_set_track_default(&attr->life.value.trk, 1.0); nuclear@0: nuclear@0: return 0; nuclear@0: nuclear@0: err: nuclear@0: psys_destroy_attr(attr); nuclear@0: return -1; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: static int init_particle_attr(struct psys_particle_attributes *pattr) nuclear@0: { nuclear@0: if(psys_init_track3(&pattr->color) == -1) { nuclear@0: return -1; nuclear@0: } nuclear@0: if(psys_init_track(&pattr->alpha) == -1) { nuclear@0: psys_destroy_track3(&pattr->color); nuclear@0: return -1; nuclear@0: } nuclear@0: if(psys_init_track(&pattr->size) == -1) { nuclear@0: psys_destroy_track3(&pattr->color); nuclear@0: psys_destroy_track(&pattr->alpha); nuclear@0: return -1; nuclear@0: } nuclear@0: nuclear@0: anm_set_track_default(&pattr->color.x, 1.0); nuclear@0: anm_set_track_default(&pattr->color.y, 1.0); nuclear@0: anm_set_track_default(&pattr->color.z, 1.0); nuclear@0: anm_set_track_default(&pattr->alpha.trk, 1.0); nuclear@0: anm_set_track_default(&pattr->size.trk, 1.0); nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: void psys_destroy_attr(struct psys_attributes *attr) nuclear@0: { nuclear@0: psys_destroy_track3(&attr->spawn_range); nuclear@0: psys_destroy_track(&attr->rate); nuclear@0: psys_destroy_anm_rnd(&attr->life); nuclear@0: psys_destroy_anm_rnd(&attr->size); nuclear@0: psys_destroy_anm_rnd3(&attr->dir); nuclear@0: psys_destroy_track3(&attr->grav); nuclear@0: nuclear@0: destroy_particle_attr(&attr->part_attr); nuclear@0: nuclear@0: if(attr->tex && unload_texture) { nuclear@0: unload_texture(attr->tex, tex_cls); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: static void destroy_particle_attr(struct psys_particle_attributes *pattr) nuclear@0: { nuclear@0: psys_destroy_track3(&pattr->color); nuclear@0: psys_destroy_track(&pattr->alpha); nuclear@0: psys_destroy_track(&pattr->size); nuclear@0: } nuclear@0: nuclear@0: void psys_copy_attr(struct psys_attributes *dest, const struct psys_attributes *src) nuclear@0: { nuclear@0: dest->tex = src->tex; nuclear@0: nuclear@0: psys_copy_track3(&dest->spawn_range, &src->spawn_range); nuclear@0: psys_copy_track(&dest->rate, &src->rate); nuclear@0: nuclear@0: psys_copy_anm_rnd(&dest->life, &src->life); nuclear@0: psys_copy_anm_rnd(&dest->size, &src->size); nuclear@0: psys_copy_anm_rnd3(&dest->dir, &src->dir); nuclear@0: nuclear@0: psys_copy_track3(&dest->grav, &src->grav); nuclear@0: nuclear@0: dest->drag = src->drag; nuclear@0: dest->max_particles = src->max_particles; nuclear@0: nuclear@0: /* also copy the particle attributes */ nuclear@0: psys_copy_track3(&dest->part_attr.color, &src->part_attr.color); nuclear@0: psys_copy_track(&dest->part_attr.alpha, &src->part_attr.alpha); nuclear@0: psys_copy_track(&dest->part_attr.size, &src->part_attr.size); nuclear@0: } nuclear@0: nuclear@0: void psys_eval_attr(struct psys_attributes *attr, anm_time_t tm) nuclear@0: { nuclear@0: psys_eval_track3(&attr->spawn_range, tm); nuclear@0: psys_eval_track(&attr->rate, tm); nuclear@0: psys_eval_anm_rnd(&attr->life, tm); nuclear@0: psys_eval_anm_rnd(&attr->size, tm); nuclear@0: psys_eval_anm_rnd3(&attr->dir, tm); nuclear@0: psys_eval_track3(&attr->grav, tm); nuclear@0: } nuclear@0: nuclear@0: int psys_load_attr(struct psys_attributes *attr, const char *fname) nuclear@0: { nuclear@0: FILE *fp; nuclear@0: int res; nuclear@0: nuclear@0: if(!fname) { nuclear@0: return -1; nuclear@0: } nuclear@0: nuclear@0: if(!(fp = fopen(fname, "r"))) { nuclear@0: fprintf(stderr, "%s: failed to read file: %s: %s\n", __FUNCTION__, fname, strerror(errno)); nuclear@0: return -1; nuclear@0: } nuclear@0: res = psys_load_attr_stream(attr, fp); nuclear@0: fclose(fp); nuclear@0: return res; nuclear@0: } nuclear@0: nuclear@0: int psys_load_attr_stream(struct psys_attributes *attr, FILE *fp) nuclear@0: { nuclear@0: int lineno = 0; nuclear@0: char buf[512]; nuclear@0: struct cfgopt *opt = 0; nuclear@0: nuclear@0: psys_init_attr(attr); nuclear@0: nuclear@0: while(fgets(buf, sizeof buf, fp)) { nuclear@0: nuclear@0: lineno++; nuclear@0: nuclear@0: if(!(opt = get_cfg_opt(buf))) { nuclear@0: continue; nuclear@0: } nuclear@0: nuclear@0: if(strcmp(opt->name, "texture") == 0) { nuclear@0: if(opt->type != OPT_STR) { nuclear@0: goto err; nuclear@0: } nuclear@0: if(!load_texture) { nuclear@0: fprintf(stderr, "particle system requests a texture, but no texture loader available!\n"); nuclear@0: goto err; nuclear@0: } nuclear@0: if(!(attr->tex = load_texture(opt->valstr, tex_cls))) { nuclear@0: fprintf(stderr, "failed to load texture: %s\n", opt->valstr); nuclear@0: goto err; nuclear@0: } nuclear@0: nuclear@0: release_cfg_opt(opt); nuclear@0: continue; nuclear@0: } else if(opt->type == OPT_STR) { nuclear@0: fprintf(stderr, "invalid particle config: '%s'\n", opt->name); nuclear@0: goto err; nuclear@0: } nuclear@0: nuclear@0: if(strcmp(opt->name, "spawn_range") == 0) { nuclear@0: psys_set_value3(&attr->spawn_range, opt->tm, opt->val); nuclear@0: } else if(strcmp(opt->name, "rate") == 0) { nuclear@0: psys_set_value(&attr->rate, opt->tm, opt->val.x); nuclear@0: } else if(strcmp(opt->name, "life") == 0) { nuclear@0: psys_set_anm_rnd(&attr->life, opt->tm, opt->val.x, opt->valrng.x); nuclear@0: } else if(strcmp(opt->name, "size") == 0) { nuclear@0: psys_set_anm_rnd(&attr->size, opt->tm, opt->val.x, opt->valrng.x); nuclear@0: } else if(strcmp(opt->name, "dir") == 0) { nuclear@0: psys_set_anm_rnd3(&attr->dir, opt->tm, opt->val, opt->valrng); nuclear@0: } else if(strcmp(opt->name, "grav") == 0) { nuclear@0: psys_set_value3(&attr->grav, opt->tm, opt->val); nuclear@0: } else if(strcmp(opt->name, "drag") == 0) { nuclear@0: attr->drag = opt->val.x; nuclear@0: } else if(strcmp(opt->name, "pcolor") == 0) { nuclear@0: psys_set_value3(&attr->part_attr.color, opt->tm, opt->val); nuclear@0: } else if(strcmp(opt->name, "palpha") == 0) { nuclear@0: psys_set_value(&attr->part_attr.alpha, opt->tm, opt->val.x); nuclear@0: } else if(strcmp(opt->name, "psize") == 0) { nuclear@0: psys_set_value(&attr->part_attr.size, opt->tm, opt->val.x); nuclear@0: } else { nuclear@0: fprintf(stderr, "unrecognized particle config option: %s\n", opt->name); nuclear@0: goto err; nuclear@0: } nuclear@0: nuclear@0: release_cfg_opt(opt); nuclear@0: } nuclear@0: nuclear@0: return 0; nuclear@0: nuclear@0: err: nuclear@0: fprintf(stderr, "Line %d: error parsing particle definition\n", lineno); nuclear@0: release_cfg_opt(opt); nuclear@0: return -1; nuclear@0: } nuclear@0: nuclear@0: static struct cfgopt *get_cfg_opt(const char *line) nuclear@0: { nuclear@0: char *buf, *tmp; nuclear@0: struct cfgopt *opt; nuclear@0: nuclear@0: /* allocate a working buffer on the stack that could fit the current line */ nuclear@0: buf = alloca(strlen(line) + 1); nuclear@0: nuclear@0: line = stripspace((char*)line); nuclear@0: if(line[0] == '#' || !line[0]) { nuclear@0: return 0; /* skip empty lines and comments */ nuclear@0: } nuclear@0: nuclear@0: if(!(opt = malloc(sizeof *opt))) { nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: if(!(opt->valstr = strchr(line, '='))) { nuclear@0: release_cfg_opt(opt); nuclear@0: return 0; nuclear@0: } nuclear@0: *opt->valstr++ = 0; nuclear@0: opt->valstr = stripspace(opt->valstr); nuclear@0: nuclear@0: strcpy(buf, line); nuclear@0: buf = stripspace(buf); nuclear@0: nuclear@0: /* parse the keyframe time specifier if it exists */ nuclear@0: if((tmp = strchr(buf, '('))) { nuclear@0: char *endp; nuclear@0: float tval; nuclear@0: nuclear@0: *tmp++ = 0; nuclear@0: opt->name = malloc(strlen(buf) + 1); nuclear@0: strcpy(opt->name, buf); nuclear@0: nuclear@0: tval = strtod(tmp, &endp); nuclear@0: if(endp == tmp) { /* nada ... */ nuclear@0: opt->tm = 0; nuclear@0: } else if(*endp == 's') { /* seconds suffix */ nuclear@0: opt->tm = (long)(tval * 1000.0f); nuclear@0: } else { nuclear@0: opt->tm = (long)tval; nuclear@0: } nuclear@0: } else { nuclear@0: opt->name = malloc(strlen(buf) + 1); nuclear@0: strcpy(opt->name, buf); nuclear@0: opt->tm = 0; nuclear@0: } nuclear@0: nuclear@0: if(sscanf(opt->valstr, "[%f %f %f] ~ [%f %f %f]", &opt->val.x, &opt->val.y, &opt->val.z, nuclear@0: &opt->valrng.x, &opt->valrng.y, &opt->valrng.z) == 6) { nuclear@0: /* value is a vector range */ nuclear@0: opt->type = OPT_VEC_RANGE; nuclear@0: nuclear@0: } else if(sscanf(opt->valstr, "%f ~ %f", &opt->val.x, &opt->valrng.x) == 2) { nuclear@0: /* value is a number range */ nuclear@0: opt->type = OPT_NUM_RANGE; nuclear@0: opt->val.y = opt->val.z = opt->val.x; nuclear@0: opt->valrng.y = opt->valrng.z = opt->valrng.x; nuclear@0: nuclear@0: } else if(sscanf(opt->valstr, "[%f %f %f]", &opt->val.x, &opt->val.y, &opt->val.z) == 3) { nuclear@0: /* value is a vector */ nuclear@0: opt->type = OPT_VEC; nuclear@0: opt->valrng.x = opt->valrng.y = opt->valrng.z = 0.0f; nuclear@0: nuclear@0: } else if(sscanf(opt->valstr, "%f", &opt->val.x) == 1) { nuclear@0: /* value is a number */ nuclear@0: opt->type = OPT_NUM; nuclear@0: opt->val.y = opt->val.z = opt->val.x; nuclear@0: opt->valrng.x = opt->valrng.y = opt->valrng.z = 0.0f; nuclear@0: nuclear@0: } else if(sscanf(opt->valstr, "\"%s\"", buf) == 1) { nuclear@0: /* just a string... strip the quotes */ nuclear@0: if(buf[strlen(buf) - 1] == '\"') { nuclear@0: buf[strlen(buf) - 1] = 0; nuclear@0: } nuclear@0: opt->type = OPT_STR; nuclear@0: opt->valstr = strdup(buf); nuclear@0: } else { nuclear@0: /* fuck it ... */ nuclear@0: release_cfg_opt(opt); nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: return opt; nuclear@0: } nuclear@0: nuclear@0: static void release_cfg_opt(struct cfgopt *opt) nuclear@0: { nuclear@0: if(opt) { nuclear@0: free(opt->name); nuclear@0: opt->name = 0; nuclear@0: } nuclear@0: opt = 0; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: int psys_save_attr(const struct psys_attributes *attr, const char *fname) nuclear@0: { nuclear@0: FILE *fp; nuclear@0: int res; nuclear@0: nuclear@0: if(!(fp = fopen(fname, "w"))) { nuclear@0: fprintf(stderr, "%s: failed to write file: %s: %s\n", __FUNCTION__, fname, strerror(errno)); nuclear@0: return -1; nuclear@0: } nuclear@0: res = psys_save_attr_stream(attr, fp); nuclear@0: fclose(fp); nuclear@0: return res; nuclear@0: } nuclear@0: nuclear@0: int psys_save_attr_stream(const struct psys_attributes *attr, FILE *fp) nuclear@0: { nuclear@0: return -1; /* TODO */ nuclear@0: } nuclear@0: nuclear@0: nuclear@0: static char *stripspace(char *str) nuclear@0: { nuclear@0: char *end; nuclear@0: nuclear@0: while(*str && isspace(*str)) { nuclear@0: str++; nuclear@0: } nuclear@0: nuclear@0: end = str + strlen(str) - 1; nuclear@0: while(end >= str && isspace(*end)) { nuclear@0: *end-- = 0; nuclear@0: } nuclear@0: return str; nuclear@0: }