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 }
|