0876b6003d3063b7b34df5c7255fc9f10bbc5f67
[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         float 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 = p->g = p->b = 0.9;
207                 p->vx = p->vy = p->vz = 0.0f;
208                 p->life = MAX_LIFE;
209                 ++p;
210         }
211         return 0;
212 }
213
214 void update_particles(struct emitter *em, float dt)
215 {
216         int i;
217         struct vec2 accel;
218         struct particle *p = em->plist;
219         struct g3d_vertex *v = varr;
220
221         for(i=0; i<em->pcount; i++) {
222                 vfield_eval(&vfield, p->x, p->y, &accel);
223                 p->x += p->vx * DRAG * dt;
224                 p->y += p->vy * DRAG * dt;
225                 p->z += p->vz * DRAG * dt;
226                 p->vx += (wind[0] + accel.x * FORCE) * dt;
227                 p->vy += (wind[1] + accel.y * FORCE) * dt;
228                 p->vz += (wind[2] + p->z * ZBIAS) * dt;
229                 p->life -= dt;
230                 if(p->life < 0.0f) p->life = 0.0f;
231
232                 v->x = p->x;
233                 v->y = p->y;
234                 v->z = p->z;
235                 v->w = 1.0f;
236                 v->r = v->g = v->b = 200;
237                 v->a = cround64(p->life * 255.0 / MAX_LIFE);
238                 ++v;
239
240                 ++p;
241         }
242 }
243
244 void draw_particles(struct emitter *em)
245 {
246         g3d_enable(G3D_BLEND);
247         g3d_draw(G3D_POINTS, varr, PCOUNT);
248         g3d_disable(G3D_BLEND);
249 }
250
251
252 int init_vfield_load(struct vfield *vf, const char *fname)
253 {
254         FILE *fp;
255         int tmp;
256
257         if(!(fp = fopen(fname, "rb"))) {
258                 fprintf(stderr, "failed to open vector field: %s\n", fname);
259                 return -1;
260         }
261         if(fread(&vf->width, sizeof vf->width, 1, fp) < 1 ||
262                         fread(&vf->height, sizeof vf->height, 1, fp) < 1) {
263                 fprintf(stderr, "init_vfield_load: unexpected end of file while reading header\n");
264                 fclose(fp);
265                 return -1;
266         }
267
268         /* assume xsz is pow2 otherwise fuck you */
269         tmp = vf->width - 1;
270         vf->xshift = 0;
271         while(tmp) {
272                 ++vf->xshift;
273                 tmp >>= 1;
274         }
275
276         if(!(vf->v = malloc(vf->width * vf->height * sizeof *vf->v))) {
277                 fprintf(stderr, "failed to allocate %dx%d vector field\n", vf->width, vf->height);
278                 fclose(fp);
279                 return -1;
280         }
281         if(fread(vf->v, sizeof *vf->v, vf->width * vf->height, fp) < vf->width * vf->height) {
282                 fprintf(stderr, "init_vfield_load: unexpected end of file while reading %dx%d vector field\n",
283                                 vf->width, vf->height);
284                 fclose(fp);
285                 return -1;
286         }
287         fclose(fp);
288
289         vf->pos.x = vf->pos.y = 0;
290         vf->size.x = vf->size.y = 1;
291         return 0;
292 }
293
294 void vfield_eval(struct vfield *vf, float x, float y, struct vec2 *dir)
295 {
296         int px, py;
297         float tx, ty;
298         struct vec2 *p1, *p2;
299         struct vec2 left, right;
300
301         x = ((x - vf->pos.x) / vf->size.x + 0.5f) * vf->width;
302         y = ((y - vf->pos.y) / vf->size.y + 0.5f) * vf->height;
303         x = floor(x);
304         y = floor(y);
305
306         if(x < 0) x = 0;
307         if(y < 0) y = 0;
308         if(x > vf->width - 2) x = vf->width - 2;
309         if(y > vf->height - 2) y = vf->height - 2;
310
311         tx = fmod(x, 1.0f);
312         ty = fmod(y, 1.0f);
313
314         px = (int)x;
315         py = (int)y;
316
317         p1 = vf->v + (py << vf->xshift) + px;
318         p2 = p1 + vf->width;
319
320         left.x = p1->x + (p2->x - p1->x) * ty;
321         left.y = p1->y + (p2->y - p1->y) * ty;
322         ++p1;
323         ++p2;
324         right.x = p1->x + (p2->x - p1->x) * ty;
325         right.y = p1->y + (p2->y - p1->y) * ty;
326
327         dir->x = left.x + (right.x - left.x) * tx;
328         dir->y = left.y + (right.y - left.y) * ty;
329
330         dir->x += ((float)rand() / RAND_MAX - 0.5) * 0.7;
331         dir->y += ((float)rand() / RAND_MAX - 0.5) * 0.7;
332 }