vrshoot

annotate libs/psys/pattr.c @ 0:b2f14e535253

initial commit
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 01 Feb 2014 19:58:19 +0200
parents
children
rev   line source
nuclear@0 1 #define _GNU_SOURCE
nuclear@0 2 #include <stdio.h>
nuclear@0 3 #include <stdlib.h>
nuclear@0 4 #include <string.h>
nuclear@0 5 #include <errno.h>
nuclear@0 6 #include <ctype.h>
nuclear@0 7
nuclear@0 8 #ifdef _MSC_VER
nuclear@0 9 #include <malloc.h>
nuclear@0 10 #else
nuclear@0 11 #include <alloca.h>
nuclear@0 12 #endif
nuclear@0 13
nuclear@0 14 #include "pattr.h"
nuclear@0 15
nuclear@0 16 enum {
nuclear@0 17 OPT_STR,
nuclear@0 18 OPT_NUM,
nuclear@0 19 OPT_NUM_RANGE,
nuclear@0 20 OPT_VEC,
nuclear@0 21 OPT_VEC_RANGE
nuclear@0 22 };
nuclear@0 23
nuclear@0 24 struct cfgopt {
nuclear@0 25 char *name;
nuclear@0 26 int type;
nuclear@0 27 long tm;
nuclear@0 28 char *valstr;
nuclear@0 29 vec3_t val, valrng;
nuclear@0 30 };
nuclear@0 31
nuclear@0 32 static int init_particle_attr(struct psys_particle_attributes *pattr);
nuclear@0 33 static void destroy_particle_attr(struct psys_particle_attributes *pattr);
nuclear@0 34 static struct cfgopt *get_cfg_opt(const char *line);
nuclear@0 35 static void release_cfg_opt(struct cfgopt *opt);
nuclear@0 36 static char *stripspace(char *str);
nuclear@0 37
nuclear@0 38 static void *tex_cls;
nuclear@0 39 static unsigned int (*load_texture)(const char*, void*);
nuclear@0 40 static void (*unload_texture)(unsigned int, void*);
nuclear@0 41
nuclear@0 42
nuclear@0 43 void psys_texture_loader(unsigned int (*load)(const char*, void*), void (*unload)(unsigned int, void*), void *cls)
nuclear@0 44 {
nuclear@0 45 load_texture = load;
nuclear@0 46 unload_texture = unload;
nuclear@0 47 tex_cls = cls;
nuclear@0 48 }
nuclear@0 49
nuclear@0 50 struct psys_attributes *psys_create_attr(void)
nuclear@0 51 {
nuclear@0 52 struct psys_attributes *attr = malloc(sizeof *attr);
nuclear@0 53 if(attr) {
nuclear@0 54 if(psys_init_attr(attr) == -1) {
nuclear@0 55 free(attr);
nuclear@0 56 attr = 0;
nuclear@0 57 }
nuclear@0 58 }
nuclear@0 59 return attr;
nuclear@0 60 }
nuclear@0 61
nuclear@0 62 void psys_free_attr(struct psys_attributes *attr)
nuclear@0 63 {
nuclear@0 64 psys_destroy_attr(attr);
nuclear@0 65 free(attr);
nuclear@0 66 }
nuclear@0 67
nuclear@0 68 int psys_init_attr(struct psys_attributes *attr)
nuclear@0 69 {
nuclear@0 70 memset(attr, 0, sizeof *attr);
nuclear@0 71
nuclear@0 72 if(psys_init_track3(&attr->spawn_range) == -1)
nuclear@0 73 goto err;
nuclear@0 74 if(psys_init_track(&attr->rate) == -1)
nuclear@0 75 goto err;
nuclear@0 76 if(psys_init_anm_rnd(&attr->life) == -1)
nuclear@0 77 goto err;
nuclear@0 78 if(psys_init_anm_rnd(&attr->size) == -1)
nuclear@0 79 goto err;
nuclear@0 80 if(psys_init_anm_rnd3(&attr->dir) == -1)
nuclear@0 81 goto err;
nuclear@0 82 if(psys_init_track3(&attr->grav) == -1)
nuclear@0 83 goto err;
nuclear@0 84
nuclear@0 85 if(init_particle_attr(&attr->part_attr) == -1)
nuclear@0 86 goto err;
nuclear@0 87
nuclear@0 88 attr->max_particles = -1;
nuclear@0 89
nuclear@0 90 anm_set_track_default(&attr->size.value.trk, 1.0);
nuclear@0 91 anm_set_track_default(&attr->life.value.trk, 1.0);
nuclear@0 92
nuclear@0 93 return 0;
nuclear@0 94
nuclear@0 95 err:
nuclear@0 96 psys_destroy_attr(attr);
nuclear@0 97 return -1;
nuclear@0 98 }
nuclear@0 99
nuclear@0 100
nuclear@0 101 static int init_particle_attr(struct psys_particle_attributes *pattr)
nuclear@0 102 {
nuclear@0 103 if(psys_init_track3(&pattr->color) == -1) {
nuclear@0 104 return -1;
nuclear@0 105 }
nuclear@0 106 if(psys_init_track(&pattr->alpha) == -1) {
nuclear@0 107 psys_destroy_track3(&pattr->color);
nuclear@0 108 return -1;
nuclear@0 109 }
nuclear@0 110 if(psys_init_track(&pattr->size) == -1) {
nuclear@0 111 psys_destroy_track3(&pattr->color);
nuclear@0 112 psys_destroy_track(&pattr->alpha);
nuclear@0 113 return -1;
nuclear@0 114 }
nuclear@0 115
nuclear@0 116 anm_set_track_default(&pattr->color.x, 1.0);
nuclear@0 117 anm_set_track_default(&pattr->color.y, 1.0);
nuclear@0 118 anm_set_track_default(&pattr->color.z, 1.0);
nuclear@0 119 anm_set_track_default(&pattr->alpha.trk, 1.0);
nuclear@0 120 anm_set_track_default(&pattr->size.trk, 1.0);
nuclear@0 121 return 0;
nuclear@0 122 }
nuclear@0 123
nuclear@0 124
nuclear@0 125 void psys_destroy_attr(struct psys_attributes *attr)
nuclear@0 126 {
nuclear@0 127 psys_destroy_track3(&attr->spawn_range);
nuclear@0 128 psys_destroy_track(&attr->rate);
nuclear@0 129 psys_destroy_anm_rnd(&attr->life);
nuclear@0 130 psys_destroy_anm_rnd(&attr->size);
nuclear@0 131 psys_destroy_anm_rnd3(&attr->dir);
nuclear@0 132 psys_destroy_track3(&attr->grav);
nuclear@0 133
nuclear@0 134 destroy_particle_attr(&attr->part_attr);
nuclear@0 135
nuclear@0 136 if(attr->tex && unload_texture) {
nuclear@0 137 unload_texture(attr->tex, tex_cls);
nuclear@0 138 }
nuclear@0 139 }
nuclear@0 140
nuclear@0 141 static void destroy_particle_attr(struct psys_particle_attributes *pattr)
nuclear@0 142 {
nuclear@0 143 psys_destroy_track3(&pattr->color);
nuclear@0 144 psys_destroy_track(&pattr->alpha);
nuclear@0 145 psys_destroy_track(&pattr->size);
nuclear@0 146 }
nuclear@0 147
nuclear@0 148 void psys_copy_attr(struct psys_attributes *dest, const struct psys_attributes *src)
nuclear@0 149 {
nuclear@0 150 dest->tex = src->tex;
nuclear@0 151
nuclear@0 152 psys_copy_track3(&dest->spawn_range, &src->spawn_range);
nuclear@0 153 psys_copy_track(&dest->rate, &src->rate);
nuclear@0 154
nuclear@0 155 psys_copy_anm_rnd(&dest->life, &src->life);
nuclear@0 156 psys_copy_anm_rnd(&dest->size, &src->size);
nuclear@0 157 psys_copy_anm_rnd3(&dest->dir, &src->dir);
nuclear@0 158
nuclear@0 159 psys_copy_track3(&dest->grav, &src->grav);
nuclear@0 160
nuclear@0 161 dest->drag = src->drag;
nuclear@0 162 dest->max_particles = src->max_particles;
nuclear@0 163
nuclear@0 164 /* also copy the particle attributes */
nuclear@0 165 psys_copy_track3(&dest->part_attr.color, &src->part_attr.color);
nuclear@0 166 psys_copy_track(&dest->part_attr.alpha, &src->part_attr.alpha);
nuclear@0 167 psys_copy_track(&dest->part_attr.size, &src->part_attr.size);
nuclear@0 168 }
nuclear@0 169
nuclear@0 170 void psys_eval_attr(struct psys_attributes *attr, anm_time_t tm)
nuclear@0 171 {
nuclear@0 172 psys_eval_track3(&attr->spawn_range, tm);
nuclear@0 173 psys_eval_track(&attr->rate, tm);
nuclear@0 174 psys_eval_anm_rnd(&attr->life, tm);
nuclear@0 175 psys_eval_anm_rnd(&attr->size, tm);
nuclear@0 176 psys_eval_anm_rnd3(&attr->dir, tm);
nuclear@0 177 psys_eval_track3(&attr->grav, tm);
nuclear@0 178 }
nuclear@0 179
nuclear@0 180 int psys_load_attr(struct psys_attributes *attr, const char *fname)
nuclear@0 181 {
nuclear@0 182 FILE *fp;
nuclear@0 183 int res;
nuclear@0 184
nuclear@0 185 if(!fname) {
nuclear@0 186 return -1;
nuclear@0 187 }
nuclear@0 188
nuclear@0 189 if(!(fp = fopen(fname, "r"))) {
nuclear@0 190 fprintf(stderr, "%s: failed to read file: %s: %s\n", __FUNCTION__, fname, strerror(errno));
nuclear@0 191 return -1;
nuclear@0 192 }
nuclear@0 193 res = psys_load_attr_stream(attr, fp);
nuclear@0 194 fclose(fp);
nuclear@0 195 return res;
nuclear@0 196 }
nuclear@0 197
nuclear@0 198 int psys_load_attr_stream(struct psys_attributes *attr, FILE *fp)
nuclear@0 199 {
nuclear@0 200 int lineno = 0;
nuclear@0 201 char buf[512];
nuclear@0 202 struct cfgopt *opt = 0;
nuclear@0 203
nuclear@0 204 psys_init_attr(attr);
nuclear@0 205
nuclear@0 206 while(fgets(buf, sizeof buf, fp)) {
nuclear@0 207
nuclear@0 208 lineno++;
nuclear@0 209
nuclear@0 210 if(!(opt = get_cfg_opt(buf))) {
nuclear@0 211 continue;
nuclear@0 212 }
nuclear@0 213
nuclear@0 214 if(strcmp(opt->name, "texture") == 0) {
nuclear@0 215 if(opt->type != OPT_STR) {
nuclear@0 216 goto err;
nuclear@0 217 }
nuclear@0 218 if(!load_texture) {
nuclear@0 219 fprintf(stderr, "particle system requests a texture, but no texture loader available!\n");
nuclear@0 220 goto err;
nuclear@0 221 }
nuclear@0 222 if(!(attr->tex = load_texture(opt->valstr, tex_cls))) {
nuclear@0 223 fprintf(stderr, "failed to load texture: %s\n", opt->valstr);
nuclear@0 224 goto err;
nuclear@0 225 }
nuclear@0 226
nuclear@0 227 release_cfg_opt(opt);
nuclear@0 228 continue;
nuclear@0 229 } else if(opt->type == OPT_STR) {
nuclear@0 230 fprintf(stderr, "invalid particle config: '%s'\n", opt->name);
nuclear@0 231 goto err;
nuclear@0 232 }
nuclear@0 233
nuclear@0 234 if(strcmp(opt->name, "spawn_range") == 0) {
nuclear@0 235 psys_set_value3(&attr->spawn_range, opt->tm, opt->val);
nuclear@0 236 } else if(strcmp(opt->name, "rate") == 0) {
nuclear@0 237 psys_set_value(&attr->rate, opt->tm, opt->val.x);
nuclear@0 238 } else if(strcmp(opt->name, "life") == 0) {
nuclear@0 239 psys_set_anm_rnd(&attr->life, opt->tm, opt->val.x, opt->valrng.x);
nuclear@0 240 } else if(strcmp(opt->name, "size") == 0) {
nuclear@0 241 psys_set_anm_rnd(&attr->size, opt->tm, opt->val.x, opt->valrng.x);
nuclear@0 242 } else if(strcmp(opt->name, "dir") == 0) {
nuclear@0 243 psys_set_anm_rnd3(&attr->dir, opt->tm, opt->val, opt->valrng);
nuclear@0 244 } else if(strcmp(opt->name, "grav") == 0) {
nuclear@0 245 psys_set_value3(&attr->grav, opt->tm, opt->val);
nuclear@0 246 } else if(strcmp(opt->name, "drag") == 0) {
nuclear@0 247 attr->drag = opt->val.x;
nuclear@0 248 } else if(strcmp(opt->name, "pcolor") == 0) {
nuclear@0 249 psys_set_value3(&attr->part_attr.color, opt->tm, opt->val);
nuclear@0 250 } else if(strcmp(opt->name, "palpha") == 0) {
nuclear@0 251 psys_set_value(&attr->part_attr.alpha, opt->tm, opt->val.x);
nuclear@0 252 } else if(strcmp(opt->name, "psize") == 0) {
nuclear@0 253 psys_set_value(&attr->part_attr.size, opt->tm, opt->val.x);
nuclear@0 254 } else {
nuclear@0 255 fprintf(stderr, "unrecognized particle config option: %s\n", opt->name);
nuclear@0 256 goto err;
nuclear@0 257 }
nuclear@0 258
nuclear@0 259 release_cfg_opt(opt);
nuclear@0 260 }
nuclear@0 261
nuclear@0 262 return 0;
nuclear@0 263
nuclear@0 264 err:
nuclear@0 265 fprintf(stderr, "Line %d: error parsing particle definition\n", lineno);
nuclear@0 266 release_cfg_opt(opt);
nuclear@0 267 return -1;
nuclear@0 268 }
nuclear@0 269
nuclear@0 270 static struct cfgopt *get_cfg_opt(const char *line)
nuclear@0 271 {
nuclear@0 272 char *buf, *tmp;
nuclear@0 273 struct cfgopt *opt;
nuclear@0 274
nuclear@0 275 /* allocate a working buffer on the stack that could fit the current line */
nuclear@0 276 buf = alloca(strlen(line) + 1);
nuclear@0 277
nuclear@0 278 line = stripspace((char*)line);
nuclear@0 279 if(line[0] == '#' || !line[0]) {
nuclear@0 280 return 0; /* skip empty lines and comments */
nuclear@0 281 }
nuclear@0 282
nuclear@0 283 if(!(opt = malloc(sizeof *opt))) {
nuclear@0 284 return 0;
nuclear@0 285 }
nuclear@0 286
nuclear@0 287 if(!(opt->valstr = strchr(line, '='))) {
nuclear@0 288 release_cfg_opt(opt);
nuclear@0 289 return 0;
nuclear@0 290 }
nuclear@0 291 *opt->valstr++ = 0;
nuclear@0 292 opt->valstr = stripspace(opt->valstr);
nuclear@0 293
nuclear@0 294 strcpy(buf, line);
nuclear@0 295 buf = stripspace(buf);
nuclear@0 296
nuclear@0 297 /* parse the keyframe time specifier if it exists */
nuclear@0 298 if((tmp = strchr(buf, '('))) {
nuclear@0 299 char *endp;
nuclear@0 300 float tval;
nuclear@0 301
nuclear@0 302 *tmp++ = 0;
nuclear@0 303 opt->name = malloc(strlen(buf) + 1);
nuclear@0 304 strcpy(opt->name, buf);
nuclear@0 305
nuclear@0 306 tval = strtod(tmp, &endp);
nuclear@0 307 if(endp == tmp) { /* nada ... */
nuclear@0 308 opt->tm = 0;
nuclear@0 309 } else if(*endp == 's') { /* seconds suffix */
nuclear@0 310 opt->tm = (long)(tval * 1000.0f);
nuclear@0 311 } else {
nuclear@0 312 opt->tm = (long)tval;
nuclear@0 313 }
nuclear@0 314 } else {
nuclear@0 315 opt->name = malloc(strlen(buf) + 1);
nuclear@0 316 strcpy(opt->name, buf);
nuclear@0 317 opt->tm = 0;
nuclear@0 318 }
nuclear@0 319
nuclear@0 320 if(sscanf(opt->valstr, "[%f %f %f] ~ [%f %f %f]", &opt->val.x, &opt->val.y, &opt->val.z,
nuclear@0 321 &opt->valrng.x, &opt->valrng.y, &opt->valrng.z) == 6) {
nuclear@0 322 /* value is a vector range */
nuclear@0 323 opt->type = OPT_VEC_RANGE;
nuclear@0 324
nuclear@0 325 } else if(sscanf(opt->valstr, "%f ~ %f", &opt->val.x, &opt->valrng.x) == 2) {
nuclear@0 326 /* value is a number range */
nuclear@0 327 opt->type = OPT_NUM_RANGE;
nuclear@0 328 opt->val.y = opt->val.z = opt->val.x;
nuclear@0 329 opt->valrng.y = opt->valrng.z = opt->valrng.x;
nuclear@0 330
nuclear@0 331 } else if(sscanf(opt->valstr, "[%f %f %f]", &opt->val.x, &opt->val.y, &opt->val.z) == 3) {
nuclear@0 332 /* value is a vector */
nuclear@0 333 opt->type = OPT_VEC;
nuclear@0 334 opt->valrng.x = opt->valrng.y = opt->valrng.z = 0.0f;
nuclear@0 335
nuclear@0 336 } else if(sscanf(opt->valstr, "%f", &opt->val.x) == 1) {
nuclear@0 337 /* value is a number */
nuclear@0 338 opt->type = OPT_NUM;
nuclear@0 339 opt->val.y = opt->val.z = opt->val.x;
nuclear@0 340 opt->valrng.x = opt->valrng.y = opt->valrng.z = 0.0f;
nuclear@0 341
nuclear@0 342 } else if(sscanf(opt->valstr, "\"%s\"", buf) == 1) {
nuclear@0 343 /* just a string... strip the quotes */
nuclear@0 344 if(buf[strlen(buf) - 1] == '\"') {
nuclear@0 345 buf[strlen(buf) - 1] = 0;
nuclear@0 346 }
nuclear@0 347 opt->type = OPT_STR;
nuclear@0 348 opt->valstr = strdup(buf);
nuclear@0 349 } else {
nuclear@0 350 /* fuck it ... */
nuclear@0 351 release_cfg_opt(opt);
nuclear@0 352 return 0;
nuclear@0 353 }
nuclear@0 354
nuclear@0 355 return opt;
nuclear@0 356 }
nuclear@0 357
nuclear@0 358 static void release_cfg_opt(struct cfgopt *opt)
nuclear@0 359 {
nuclear@0 360 if(opt) {
nuclear@0 361 free(opt->name);
nuclear@0 362 opt->name = 0;
nuclear@0 363 }
nuclear@0 364 opt = 0;
nuclear@0 365 }
nuclear@0 366
nuclear@0 367
nuclear@0 368 int psys_save_attr(const struct psys_attributes *attr, const char *fname)
nuclear@0 369 {
nuclear@0 370 FILE *fp;
nuclear@0 371 int res;
nuclear@0 372
nuclear@0 373 if(!(fp = fopen(fname, "w"))) {
nuclear@0 374 fprintf(stderr, "%s: failed to write file: %s: %s\n", __FUNCTION__, fname, strerror(errno));
nuclear@0 375 return -1;
nuclear@0 376 }
nuclear@0 377 res = psys_save_attr_stream(attr, fp);
nuclear@0 378 fclose(fp);
nuclear@0 379 return res;
nuclear@0 380 }
nuclear@0 381
nuclear@0 382 int psys_save_attr_stream(const struct psys_attributes *attr, FILE *fp)
nuclear@0 383 {
nuclear@0 384 return -1; /* TODO */
nuclear@0 385 }
nuclear@0 386
nuclear@0 387
nuclear@0 388 static char *stripspace(char *str)
nuclear@0 389 {
nuclear@0 390 char *end;
nuclear@0 391
nuclear@0 392 while(*str && isspace(*str)) {
nuclear@0 393 str++;
nuclear@0 394 }
nuclear@0 395
nuclear@0 396 end = str + strlen(str) - 1;
nuclear@0 397 while(end >= str && isspace(*end)) {
nuclear@0 398 *end-- = 0;
nuclear@0 399 }
nuclear@0 400 return str;
nuclear@0 401 }