353d29cf9e324a38a12a424c181171c1fd1884da
[dosdemo] / src / greets.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <math.h>
5 #include <assert.h>
6 #include "demo.h"
7 #include "3dgfx.h"
8 #include "screen.h"
9 #include "cfgopt.h"
10 #include "imago2.h"
11 #include "util.h"
12 #include "gfxutil.h"
13 #include "timer.h"
14
15 #ifdef MSDOS
16 #include "dos/gfx.h"    /* for wait_vsync assembly macro */
17 #else
18 void wait_vsync(void);
19 #endif
20
21 /* if defined, use bilinear interpolation for dispersion field vectors */
22 #define BILERP_FIELD
23 /* if defined randomize field vectors by RAND_FIELD_MAX */
24 #define RANDOMIZE_FIELD
25
26 #define RAND_FIELD_MAX  0.7
27
28 #define BLUR_RAD        5
29
30 #define PCOUNT          4000
31 #define MAX_LIFE        7.0f
32 #define PALPHA          1.0f
33 #define ZBIAS           0.25
34 #define DRAG            0.95
35 #define FORCE           0.07
36 #define FREQ            0.085
37 static float wind[] = {-0.0, 0.0, 0.01};
38
39
40 struct vec2 {
41         float x, y;
42 };
43
44 struct vec3 {
45         float x, y, z;
46 };
47
48 struct particle {
49         float x, y, z;
50         float vx, vy, vz;       /* velocity */
51         int r, g, b;
52         float life;
53 };
54
55 struct emitter {
56         struct particle *plist;
57         int pcount;
58 };
59
60 struct vfield {
61         struct vec2 pos, size;
62
63         int width, height;
64         int xshift;
65         struct vec2 *v;
66 };
67
68
69 static int init(void);
70 static void destroy(void);
71 static void start(long trans_time);
72 static void draw(void);
73
74 int init_emitter(struct emitter *em, int num, unsigned char *map, int xsz, int ysz);
75 void update_particles(struct emitter *em, float dt);
76 void draw_particles(struct emitter *em);
77
78 int init_vfield_load(struct vfield *vf, const char *fname);
79 void vfield_eval(struct vfield *vf, float x, float y, struct vec2 *dir);
80
81
82 static struct screen scr = {
83         "greets",
84         init,
85         destroy,
86         start, 0,
87         draw
88 };
89
90 static struct emitter em;
91 static struct vfield vfield;
92 static struct g3d_vertex *varr;
93 static long start_time;
94
95 static uint16_t *cur_smokebuf, *prev_smokebuf;
96 static int smokebuf_size;
97 #define smokebuf_start  (cur_smokebuf < prev_smokebuf ? cur_smokebuf : prev_smokebuf)
98 #define swap_smoke_buffers() \
99         do { \
100                 uint16_t *tmp = cur_smokebuf; \
101                 cur_smokebuf = prev_smokebuf; \
102                 prev_smokebuf = tmp; \
103         } while(0)
104
105 static float cam_theta, cam_phi = 25;
106 static float cam_dist = 3;
107
108 struct screen *greets_screen(void)
109 {
110         return &scr;
111 }
112
113
114 static int init(void)
115 {
116         int xsz, ysz;
117         unsigned char *pixels;
118
119         if(!(pixels = img_load_pixels("data/greets1.png", &xsz, &ysz, IMG_FMT_GREY8))) {
120                 fprintf(stderr, "failed to load particle spawn map\n");
121                 return -1;
122         }
123
124         init_emitter(&em, PCOUNT, pixels, xsz, ysz);
125         img_free_pixels(pixels);
126
127         if(!(varr = malloc(PCOUNT * sizeof *varr))) {
128                 perror("failed to allocate particle vertex buffer\n");
129                 return -1;
130         }
131
132         if(init_vfield_load(&vfield, "data/vfield1") == -1) {
133                 return -1;
134         }
135
136         smokebuf_size = fb_width * fb_height * sizeof *cur_smokebuf;
137         if(!(cur_smokebuf = malloc(smokebuf_size * 2))) {
138                 perror("failed to allocate smoke framebuffer");
139                 return -1;
140         }
141         prev_smokebuf = cur_smokebuf + fb_width * fb_height;
142
143         return 0;
144 }
145
146 static void destroy(void)
147 {
148         free(varr);
149         free(vfield.v);
150         free(smokebuf_start);
151 }
152
153 static void start(long trans_time)
154 {
155         g3d_matrix_mode(G3D_PROJECTION);
156         g3d_load_identity();
157         g3d_perspective(50.0, 1.3333333, 0.5, 100.0);
158
159         memset(smokebuf_start, 0, smokebuf_size * 2);
160
161         start_time = time_msec;
162 }
163
164 static void update(void)
165 {
166         static long prev_msec;
167         static int prev_mx, prev_my;
168         static unsigned int prev_bmask;
169
170         long msec = time_msec - start_time;
171         float dt = (msec - prev_msec) / 1000.0f;
172         prev_msec = msec;
173
174         if(mouse_bmask) {
175                 if((mouse_bmask ^ prev_bmask) == 0) {
176                         int dx = mouse_x - prev_mx;
177                         int dy = mouse_y - prev_my;
178
179                         if(dx || dy) {
180                                 if(mouse_bmask & 1) {
181                                         cam_theta += dx * 1.0;
182                                         cam_phi += dy * 1.0;
183
184                                         if(cam_phi < -90) cam_phi = -90;
185                                         if(cam_phi > 90) cam_phi = 90;
186                                 }
187                                 if(mouse_bmask & 4) {
188                                         cam_dist += dy * 0.5;
189
190                                         if(cam_dist < 0) cam_dist = 0;
191                                 }
192                         }
193                 }
194         }
195         prev_mx = mouse_x;
196         prev_my = mouse_y;
197         prev_bmask = mouse_bmask;
198
199         update_particles(&em, dt);
200 }
201
202 static void draw(void)
203 {
204         int i, j;
205         uint16_t *dest, *src;
206         unsigned long msec;
207         static unsigned long last_swap;
208
209         update();
210
211         g3d_matrix_mode(G3D_MODELVIEW);
212         g3d_load_identity();
213         g3d_translate(0, 0, -cam_dist);
214         g3d_rotate(cam_phi, 1, 0, 0);
215         g3d_rotate(cam_theta, 0, 1, 0);
216         if(opt.sball) {
217                 g3d_mult_matrix(sball_matrix);
218         }
219
220         memcpy(cur_smokebuf, prev_smokebuf, smokebuf_size);
221
222         g3d_framebuffer(fb_width, fb_height, cur_smokebuf);
223         draw_particles(&em);
224         g3d_framebuffer(fb_width, fb_height, fb_pixels);
225
226         dest = fb_pixels;
227         src = cur_smokebuf;
228         for(i=0; i<fb_height; i++) {
229                 for(j=0; j<fb_width; j++) {
230                         unsigned int alpha = *src++;
231                         *dest++ = PACK_RGB16(alpha, alpha, alpha);
232                 }
233         }
234
235         /*perf_start();*/
236         blur_grey_horiz(prev_smokebuf, cur_smokebuf, fb_width, fb_height, BLUR_RAD, 240);
237         /*
238         perf_end();
239         printf("blur perf: %lu\n", (unsigned long)perf_interval_count);
240         */
241         blur_grey_vert(cur_smokebuf, prev_smokebuf, fb_width, fb_height, BLUR_RAD, 240);
242         swap_smoke_buffers();
243
244         msec = get_msec();
245         if(msec - last_swap < 16) {
246                 wait_vsync();
247         }
248         if(!opt.vsync) {
249                 wait_vsync();
250         }
251         swap_buffers(fb_pixels);
252         last_swap = get_msec();
253 }
254
255
256
257 int init_emitter(struct emitter *em, int num, unsigned char *map, int xsz, int ysz)
258 {
259         int i, x, y;
260         float aspect = (float)xsz / (float)ysz;
261         struct particle *p;
262
263         if(!(em->plist = malloc(num * sizeof *em->plist))) {
264                 return -1;
265         }
266         em->pcount = num;
267
268         p = em->plist;
269         for(i=0; i<num; i++) {
270                 do {
271                         x = rand() % xsz;
272                         y = rand() % ysz;
273                 } while(map[y * xsz + x] < 128);
274
275                 p->x = (float)x / (float)xsz - 0.5;
276                 p->y = -(float)y / (float)xsz + 0.5 / aspect;
277                 p->z = ((float)i / (float)num * 2.0 - 1.0) * 0.005;
278                 p->r = p->g = p->b = 255;
279                 p->vx = p->vy = p->vz = 0.0f;
280                 p->life = MAX_LIFE;
281                 ++p;
282         }
283         return 0;
284 }
285
286 void update_particles(struct emitter *em, float dt)
287 {
288         int i;
289         struct vec2 accel;
290         struct particle *p = em->plist;
291         struct g3d_vertex *v = varr;
292
293         for(i=0; i<em->pcount; i++) {
294                 vfield_eval(&vfield, p->x, p->y, &accel);
295                 p->x += p->vx * DRAG * dt;
296                 p->y += p->vy * DRAG * dt;
297                 p->z += p->vz * DRAG * dt;
298                 p->vx += (wind[0] + accel.x * FORCE) * dt;
299                 p->vy += (wind[1] + accel.y * FORCE) * dt;
300                 p->vz += (wind[2] + p->z * ZBIAS) * dt;
301                 p->life -= dt;
302                 if(p->life < 0.0f) p->life = 0.0f;
303
304                 v->x = p->x;
305                 v->y = p->y;
306                 v->z = p->z;
307                 v->w = 1.0f;
308                 v->a = cround64(p->life * 255.0 / MAX_LIFE);
309                 v->r = 0;
310                 v->g = (v->a & 0xe0) >> 3;
311                 v->b = (v->a & 0x1f) << 3;
312                 ++v;
313
314                 ++p;
315         }
316 }
317
318 void draw_particles(struct emitter *em)
319 {
320         g3d_draw(G3D_POINTS, varr, PCOUNT);
321 }
322
323
324 int init_vfield_load(struct vfield *vf, const char *fname)
325 {
326         FILE *fp;
327         int tmp;
328
329         if(!(fp = fopen(fname, "rb"))) {
330                 fprintf(stderr, "failed to open vector field: %s\n", fname);
331                 return -1;
332         }
333         if(fread(&vf->width, sizeof vf->width, 1, fp) < 1 ||
334                         fread(&vf->height, sizeof vf->height, 1, fp) < 1) {
335                 fprintf(stderr, "init_vfield_load: unexpected end of file while reading header\n");
336                 fclose(fp);
337                 return -1;
338         }
339
340         /* assume xsz is pow2 otherwise fuck you */
341         tmp = vf->width - 1;
342         vf->xshift = 0;
343         while(tmp) {
344                 ++vf->xshift;
345                 tmp >>= 1;
346         }
347
348         if(!(vf->v = malloc(vf->width * vf->height * sizeof *vf->v))) {
349                 fprintf(stderr, "failed to allocate %dx%d vector field\n", vf->width, vf->height);
350                 fclose(fp);
351                 return -1;
352         }
353         if(fread(vf->v, sizeof *vf->v, vf->width * vf->height, fp) < vf->width * vf->height) {
354                 fprintf(stderr, "init_vfield_load: unexpected end of file while reading %dx%d vector field\n",
355                                 vf->width, vf->height);
356                 fclose(fp);
357                 return -1;
358         }
359         fclose(fp);
360
361         vf->pos.x = vf->pos.y = 0;
362         vf->size.x = vf->size.y = 1;
363         return 0;
364 }
365
366 void vfield_eval(struct vfield *vf, float x, float y, struct vec2 *dir)
367 {
368         int px, py;
369         float tx, ty;
370         struct vec2 *p1, *p2;
371         struct vec2 left, right;
372
373         x = ((x - vf->pos.x) / vf->size.x + 0.5f) * vf->width;
374         y = ((y - vf->pos.y) / vf->size.y + 0.5f) * vf->height;
375         x = floor(x);
376         y = floor(y);
377
378         if(x < 0) x = 0;
379         if(y < 0) y = 0;
380         if(x > vf->width - 2) x = vf->width - 2;
381         if(y > vf->height - 2) y = vf->height - 2;
382
383         px = (int)x;
384         py = (int)y;
385
386         p1 = vf->v + (py << vf->xshift) + px;
387 #ifdef BILERP_FIELD
388         p2 = p1 + vf->width;
389
390         tx = fmod(x, 1.0f);
391         ty = fmod(y, 1.0f);
392
393         left.x = p1->x + (p2->x - p1->x) * ty;
394         left.y = p1->y + (p2->y - p1->y) * ty;
395         ++p1;
396         ++p2;
397         right.x = p1->x + (p2->x - p1->x) * ty;
398         right.y = p1->y + (p2->y - p1->y) * ty;
399
400         dir->x = left.x + (right.x - left.x) * tx;
401         dir->y = left.y + (right.y - left.y) * ty;
402 #else
403         dir->x = p1->x;
404         dir->y = p1->y;
405 #endif
406
407 #ifdef RANDOMIZE_FIELD
408         dir->x += ((float)rand() / RAND_MAX - 0.5) * RAND_FIELD_MAX;
409         dir->y += ((float)rand() / RAND_MAX - 0.5) * RAND_FIELD_MAX;
410 #endif
411 }