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