reorganized the source code
[dosdemo] / src / scr / smoketxt.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <math.h>
5 #include <errno.h>
6 #include "imago2.h"
7 #include "3dgfx.h"
8 #include "smoketxt.h"
9 #include "util.h"
10 #include "noise.h"
11
12 /* if defined, use bilinear interpolation for dispersion field vectors */
13 #define BILERP_FIELD
14 /* if defined randomize field vectors by RAND_FIELD_MAX */
15 #define RANDOMIZE_FIELD
16
17 #define RAND_FIELD_MAX  0.7
18
19 #define DFL_PCOUNT              4000
20 #define DFL_MAX_LIFE    7.0f
21 #define DFL_PALPHA              1.0f
22 #define DFL_ZBIAS               0.25
23 #define DFL_DRAG                0.95
24 #define DFL_FORCE               0.07
25 #define DFL_FREQ                0.085
26
27 struct vec2 {
28         float x, y;
29 };
30
31 struct vec3 {
32         float x, y, z;
33 };
34
35 struct particle {
36         float x, y, z;
37         float vx, vy, vz;       /* velocity */
38         int r, g, b;
39         float life;
40 };
41
42 struct emitter {
43         struct particle *plist;
44         int pcount;
45
46         struct vec3 wind;
47         float drag;
48         float max_life;
49
50         struct g3d_vertex *varr;
51 };
52
53 struct vfield {
54         struct vec2 pos, size;
55
56         int width, height;
57         int xshift;
58         struct vec2 *v;
59 };
60
61
62 struct smktxt {
63         struct emitter em;
64         struct vfield vfield;
65
66         unsigned char *img_pixels;
67         int img_xsz, img_ysz;
68 };
69
70 static int init_emitter(struct emitter *em, int num, unsigned char *map, int xsz, int ysz);
71 static int load_vfield(struct vfield *vf, const char *fname);
72 static void vfield_eval(struct vfield *vf, float x, float y, struct vec2 *dir);
73
74 struct smktxt *create_smktxt(const char *imgname, const char *vfieldname)
75 {
76         struct smktxt *stx;
77
78         if(!imgname) return 0;
79
80         if(!(stx = calloc(sizeof *stx, 1))) {
81                 return 0;
82         }
83         if(!(stx->img_pixels = img_load_pixels(imgname, &stx->img_xsz, &stx->img_ysz, IMG_FMT_GREY8))) {
84                 fprintf(stderr, "create_smktxt: failed to load particle spawnmap: %s\n", imgname);
85                 free(stx);
86                 return 0;
87         }
88
89         stx->em.pcount = DFL_PCOUNT;
90         stx->em.wind.z = 0.01;
91         stx->em.drag = DFL_DRAG;
92         stx->em.max_life = DFL_MAX_LIFE;
93
94         if(vfieldname) {
95                 if(load_vfield(&stx->vfield, vfieldname) == -1) {
96                         img_free_pixels(stx->img_pixels);
97                         free(stx);
98                         return 0;
99                 }
100         }
101
102         return stx;
103 }
104
105 void destroy_smktxt(struct smktxt *stx)
106 {
107         if(!stx) return;
108
109         img_free_pixels(stx->img_pixels);
110         free(stx);
111 }
112
113 int gen_smktxt_vfield(struct smktxt *stx, int xres, int yres, float xfreq, float yfreq)
114 {
115         int i, j;
116         unsigned int tmp;
117         struct vec2 *vptr;
118         struct vfield *vf = &stx->vfield;
119
120         if(!(vf->v = malloc(xres * yres * sizeof *vf->v))) {
121                 fprintf(stderr, "failed to allocate %dx%d vector field\n", xres, yres);
122                 return -1;
123         }
124
125         vf->width = xres;
126         vf->height = yres;
127         vf->pos.x = vf->pos.y = 0.0f;
128         vf->size.x = vf->size.y = 1.0f;
129
130         /* assume xres is pow2 otherwise fuck you */
131         tmp = xres - 1;
132         vf->xshift = 0;
133         while(tmp) {
134                 ++vf->xshift;
135                 tmp >>= 1;
136         }
137
138         vptr = vf->v;
139         for(i=0; i<yres; i++) {
140                 for(j=0; j<xres; j++) {
141                         float x = noise3(j * xfreq, i * yfreq, 0.5);
142                         float y = noise3(j * xfreq, i * yfreq, 10.5);
143                         float len = sqrt(x * x + y * y);
144                         if(len == 0.0f) len = 1.0f;
145
146                         vptr->x = x / len;
147                         vptr->y = y / len;
148
149                         ++vptr;
150                 }
151         }
152         return 0;
153 }
154
155 int dump_smktxt_vfield(struct smktxt *stx, const char *fname)
156 {
157         FILE *fp;
158         int xsz, ysz;
159
160         if(!(fp = fopen(fname, "wb"))) {
161                 fprintf(stderr, "failed to open %s for writing: %s\n", fname, strerror(errno));
162                 return -1;
163         }
164
165         xsz = stx->vfield.width;
166         ysz = stx->vfield.height;
167
168         fwrite(&xsz, sizeof xsz, 1, fp);
169         fwrite(&ysz, sizeof ysz, 1, fp);
170         fwrite(stx->vfield.v, sizeof *stx->vfield.v, xsz * ysz, fp);
171         fclose(fp);
172         return 0;
173 }
174
175 void set_smktxt_wind(struct smktxt *stx, float x, float y, float z)
176 {
177         stx->em.wind.x = x;
178         stx->em.wind.y = y;
179         stx->em.wind.z = z;
180 }
181
182 void set_smktxt_plife(struct smktxt *stx, float life)
183 {
184         stx->em.max_life = life;
185 }
186
187 void set_smktxt_pcount(struct smktxt *stx, int count)
188 {
189         free(stx->em.plist);
190         stx->em.plist = 0;
191         stx->em.pcount = count;
192 }
193
194 void set_smktxt_drag(struct smktxt *stx, float drag)
195 {
196         stx->em.drag = drag;
197 }
198
199 void update_smktxt(struct smktxt *stx, float dt)
200 {
201         int i;
202         struct vec2 accel;
203         struct particle *p;
204         struct g3d_vertex *v;
205
206         if(!stx->em.plist) {
207                 if(init_emitter(&stx->em, stx->em.pcount, stx->img_pixels, stx->img_xsz, stx->img_ysz) == -1) {
208                         fprintf(stderr, "failed to initialize emitter with %d particles\n", stx->em.pcount);
209                         return;
210                 }
211         }
212
213         p = stx->em.plist;
214         v = stx->em.varr;
215
216         for(i=0; i<stx->em.pcount; i++) {
217                 vfield_eval(&stx->vfield, p->x, p->y, &accel);
218                 p->x += p->vx * stx->em.drag * dt;
219                 p->y += p->vy * stx->em.drag * dt;
220                 p->z += p->vz * stx->em.drag * dt;
221                 p->vx += (stx->em.wind.x + accel.x * DFL_FORCE) * dt;
222                 p->vy += (stx->em.wind.y + accel.y * DFL_FORCE) * dt;
223                 p->vz += (stx->em.wind.z + p->z * DFL_ZBIAS) * dt;
224                 p->life -= dt;
225                 if(p->life < 0.0f) p->life = 0.0f;
226
227                 v->x = p->x;
228                 v->y = p->y;
229                 v->z = p->z;
230                 v->w = 1.0f;
231                 v->a = cround64(p->life * 255.0f / stx->em.max_life);
232                 v->r = 0;
233                 v->g = (v->a & 0xe0) >> 3;
234                 v->b = (v->a & 0x1f) << 3;
235                 ++v;
236
237                 ++p;
238         }
239 }
240
241 void draw_smktxt(struct smktxt *stx)
242 {
243         g3d_draw(G3D_POINTS, stx->em.varr, stx->em.pcount);
244 }
245
246
247 static int init_emitter(struct emitter *em, int num, unsigned char *map, int xsz, int ysz)
248 {
249         int i, x, y;
250         float aspect = (float)xsz / (float)ysz;
251         struct particle *p;
252
253         free(em->varr);
254         if(!(em->varr = malloc(num * sizeof *em->varr))) {
255                 fprintf(stderr, "failed to allocate particle vertex array (%d verts)\n", num);
256                 return -1;
257         }
258
259         free(em->plist);
260         if(!(em->plist = malloc(num * sizeof *em->plist))) {
261                 free(em->varr);
262                 return -1;
263         }
264         em->pcount = num;
265
266         p = em->plist;
267         for(i=0; i<num; i++) {
268                 do {
269                         x = rand() % xsz;
270                         y = rand() % ysz;
271                 } while(map[y * xsz + x] < 128);
272
273                 p->x = (float)x / (float)xsz - 0.5;
274                 p->y = -(float)y / (float)xsz + 0.5 / aspect;
275                 p->z = ((float)i / (float)num * 2.0 - 1.0) * 0.005;
276                 p->r = p->g = p->b = 255;
277                 p->vx = p->vy = p->vz = 0.0f;
278                 p->life = em->max_life;
279                 ++p;
280         }
281         return 0;
282 }
283
284 static int load_vfield(struct vfield *vf, const char *fname)
285 {
286         FILE *fp;
287         int tmp;
288
289         if(!(fp = fopen(fname, "rb"))) {
290                 fprintf(stderr, "failed to open vector field: %s\n", fname);
291                 return -1;
292         }
293         if(fread(&vf->width, sizeof vf->width, 1, fp) < 1 ||
294                         fread(&vf->height, sizeof vf->height, 1, fp) < 1) {
295                 fprintf(stderr, "load_vfield: unexpected end of file while reading header\n");
296                 fclose(fp);
297                 return -1;
298         }
299
300         /* assume xsz is pow2 otherwise fuck you */
301         tmp = vf->width - 1;
302         vf->xshift = 0;
303         while(tmp) {
304                 ++vf->xshift;
305                 tmp >>= 1;
306         }
307
308         if(!(vf->v = malloc(vf->width * vf->height * sizeof *vf->v))) {
309                 fprintf(stderr, "failed to allocate %dx%d vector field\n", vf->width, vf->height);
310                 fclose(fp);
311                 return -1;
312         }
313         if(fread(vf->v, sizeof *vf->v, vf->width * vf->height, fp) < vf->width * vf->height) {
314                 fprintf(stderr, "load_vfield: unexpected end of file while reading %dx%d vector field\n",
315                                 vf->width, vf->height);
316                 fclose(fp);
317                 return -1;
318         }
319         fclose(fp);
320
321         vf->pos.x = vf->pos.y = 0;
322         vf->size.x = vf->size.y = 1;
323         return 0;
324 }
325
326 static void vfield_eval(struct vfield *vf, float x, float y, struct vec2 *dir)
327 {
328         int px, py;
329         float tx, ty;
330         struct vec2 *p1, *p2;
331         struct vec2 left, right;
332
333         x = ((x - vf->pos.x) / vf->size.x + 0.5f) * vf->width;
334         y = ((y - vf->pos.y) / vf->size.y + 0.5f) * vf->height;
335         x = floor(x);
336         y = floor(y);
337
338         if(x < 0) x = 0;
339         if(y < 0) y = 0;
340         if(x > vf->width - 2) x = vf->width - 2;
341         if(y > vf->height - 2) y = vf->height - 2;
342
343         px = (int)x;
344         py = (int)y;
345
346         p1 = vf->v + (py << vf->xshift) + px;
347 #ifdef BILERP_FIELD
348         p2 = p1 + vf->width;
349
350         tx = fmod(x, 1.0f);
351         ty = fmod(y, 1.0f);
352
353         left.x = p1->x + (p2->x - p1->x) * ty;
354         left.y = p1->y + (p2->y - p1->y) * ty;
355         ++p1;
356         ++p2;
357         right.x = p1->x + (p2->x - p1->x) * ty;
358         right.y = p1->y + (p2->y - p1->y) * ty;
359
360         dir->x = left.x + (right.x - left.x) * tx;
361         dir->y = left.y + (right.y - left.y) * ty;
362 #else
363         dir->x = p1->x;
364         dir->y = p1->y;
365 #endif
366
367 #ifdef RANDOMIZE_FIELD
368         dir->x += ((float)rand() / RAND_MAX - 0.5) * RAND_FIELD_MAX;
369         dir->y += ((float)rand() / RAND_MAX - 0.5) * RAND_FIELD_MAX;
370 #endif
371 }