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