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@15: static int get_cfg_opt(const char *line, 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@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@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@13: nuclear@13: psys_init_attr(attr); nuclear@13: nuclear@13: while(fgets(buf, sizeof buf, fp)) { nuclear@14: struct cfgopt opt; nuclear@13: nuclear@13: lineno++; nuclear@13: nuclear@14: if(get_cfg_opt(buf, &opt) == -1) { nuclear@14: goto err; nuclear@13: } nuclear@13: nuclear@14: if(strcmp(opt.name, "texture")) { nuclear@14: if(opt.type != OPT_STR) { nuclear@14: goto err; nuclear@14: } nuclear@15: if(!(attr->tex = load_texture(opt.valstr, tex_cls))) { nuclear@15: fprintf(stderr, "failed to load texture: %s\n", opt.valstr); nuclear@15: goto err; nuclear@15: } nuclear@15: } else if(opt.type == OPT_STR) { nuclear@15: fprintf(stderr, "invalid particle config: %s\n", opt.name); nuclear@15: goto err; nuclear@15: } nuclear@15: nuclear@15: if(strcmp(opt.name, "spawn_range") == 0) { nuclear@15: psys_set_value3(&attr->spawn_range, opt.tm, opt.val); nuclear@15: } else if(strcmp(opt.name, "rate") == 0) { nuclear@15: psys_set_value(&attr->rate, opt.tm, opt.val.x); nuclear@15: } else if(strcmp(opt.name, "life") == 0) { nuclear@15: psys_set_anm_rnd(&attr->life, opt.tm, opt.val.x, opt.valrng.x); nuclear@15: } else if(strcmp(opt.name, "size") == 0) { nuclear@15: psys_set_anm_rnd(&attr->size, opt.tm, opt.val.x, opt.valrng.x); nuclear@15: } else if(strcmp(opt.name, "dir") == 0) { nuclear@15: psys_set_anm_rnd3(&attr->dir, opt.tm, opt.val, opt.valrng); nuclear@15: } else if(strcmp(opt.name, "grav") == 0) { nuclear@15: psys_set_value3(&attr->grav, opt.tm, opt.val); nuclear@15: } else if(strcmp(opt.name, "drag") == 0) { nuclear@15: attr->drag = opt.val.x; nuclear@15: } else if(strcmp(opt.name, "pcolor") == 0) { nuclear@15: psys_set_value3(&attr->part_attr.color, opt.tm, opt.val); nuclear@15: } else if(strcmp(opt.name, "palpha") == 0) { nuclear@15: psys_set_value(&attr->part_attr.alpha, opt.tm, opt.val.x); nuclear@15: } else if(strcmp(opt.name, "psize") == 0) { nuclear@15: psys_set_value(&attr->part_attr.size, opt.tm, opt.val.x); nuclear@15: } else { nuclear@15: fprintf(stderr, "unrecognized particle config option: %s\n", opt.name); nuclear@15: goto err; nuclear@13: } 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@14: psys_destroy_attr(attr); nuclear@14: return -1; nuclear@14: } nuclear@13: nuclear@14: /* strdup on the stack with alloca */ nuclear@15: #define strdup_stack(s) strcpy(alloca(strlen(s) + 1), s) nuclear@14: nuclear@14: static int get_cfg_opt(const char *line, struct cfgopt *opt) nuclear@14: { nuclear@14: char *buf; nuclear@14: float tmsec; 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@14: if(!(opt->valstr = strchr(line, '='))) { nuclear@14: return -1; 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@14: nuclear@14: if(sscanf(line, "%s(%fs)", buf, &tmsec) == 2) { nuclear@14: opt->tm = (long)(tmsec * 1000.0f); nuclear@14: opt->name = strdup_stack(buf); nuclear@14: } else if(sscanf(line, "%s(%ld)", buf, &opt->tm) == 2) { nuclear@14: opt->name = strdup_stack(buf); nuclear@14: } else { nuclear@14: opt->name = strdup_stack(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@14: opt->type = OPT_STR; nuclear@14: opt->valstr = strdup_stack(buf); nuclear@14: } else { nuclear@14: /* fuck it ... */ nuclear@14: return -1; nuclear@14: } nuclear@14: nuclear@14: return 0; nuclear@6: } nuclear@6: 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@13: end = str + strlen(str); nuclear@13: while(end >= str && isspace(*end)) { nuclear@13: *end-- = 0; nuclear@13: } nuclear@13: return str; nuclear@13: }