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