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