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