16 #include "dos/gfx.h" /* for wait_vsync assembly macro */
18 void wait_vsync(void);
21 /* if defined, use bilinear interpolation for dispersion field vectors */
23 /* if defined randomize field vectors by RAND_FIELD_MAX */
24 #define RANDOMIZE_FIELD
26 #define RAND_FIELD_MAX 0.7
37 static float wind[] = {-0.0, 0.0, 0.01};
50 float vx, vy, vz; /* velocity */
56 struct particle *plist;
61 struct vec2 pos, size;
69 static int init(void);
70 static void destroy(void);
71 static void start(long trans_time);
72 static void draw(void);
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);
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);
82 static struct screen scr = {
90 static struct emitter em;
91 static struct vfield vfield;
92 static struct g3d_vertex *varr;
93 static long start_time;
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() \
100 uint16_t *tmp = cur_smokebuf; \
101 cur_smokebuf = prev_smokebuf; \
102 prev_smokebuf = tmp; \
105 static float cam_theta, cam_phi = 25;
106 static float cam_dist = 3;
108 struct screen *greets_screen(void)
114 static int init(void)
117 unsigned char *pixels;
119 if(!(pixels = img_load_pixels("data/greets1.png", &xsz, &ysz, IMG_FMT_GREY8))) {
120 fprintf(stderr, "failed to load particle spawn map\n");
124 init_emitter(&em, PCOUNT, pixels, xsz, ysz);
125 img_free_pixels(pixels);
127 if(!(varr = malloc(PCOUNT * sizeof *varr))) {
128 perror("failed to allocate particle vertex buffer\n");
132 if(init_vfield_load(&vfield, "data/vfield1") == -1) {
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");
141 prev_smokebuf = cur_smokebuf + fb_width * fb_height;
146 static void destroy(void)
150 free(smokebuf_start);
153 static void start(long trans_time)
155 g3d_matrix_mode(G3D_PROJECTION);
157 g3d_perspective(50.0, 1.3333333, 0.5, 100.0);
159 memset(smokebuf_start, 0, smokebuf_size * 2);
161 start_time = time_msec;
164 static void update(void)
166 static long prev_msec;
167 static int prev_mx, prev_my;
168 static unsigned int prev_bmask;
170 long msec = time_msec - start_time;
171 float dt = (msec - prev_msec) / 1000.0f;
175 if((mouse_bmask ^ prev_bmask) == 0) {
176 int dx = mouse_x - prev_mx;
177 int dy = mouse_y - prev_my;
180 if(mouse_bmask & 1) {
181 cam_theta += dx * 1.0;
184 if(cam_phi < -90) cam_phi = -90;
185 if(cam_phi > 90) cam_phi = 90;
187 if(mouse_bmask & 4) {
188 cam_dist += dy * 0.5;
190 if(cam_dist < 0) cam_dist = 0;
197 prev_bmask = mouse_bmask;
199 update_particles(&em, dt);
202 static void draw(void)
205 uint16_t *dest, *src;
207 static unsigned long last_swap;
211 g3d_matrix_mode(G3D_MODELVIEW);
213 g3d_translate(0, 0, -cam_dist);
214 g3d_rotate(cam_phi, 1, 0, 0);
215 g3d_rotate(cam_theta, 0, 1, 0);
217 g3d_mult_matrix(sball_matrix);
220 memcpy(cur_smokebuf, prev_smokebuf, smokebuf_size);
222 g3d_framebuffer(fb_width, fb_height, cur_smokebuf);
224 g3d_framebuffer(fb_width, fb_height, fb_pixels);
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);
236 blur_grey_horiz(prev_smokebuf, cur_smokebuf, fb_width, fb_height, BLUR_RAD, 240);
239 printf("blur perf: %lu\n", (unsigned long)perf_interval_count);
241 blur_grey_vert(cur_smokebuf, prev_smokebuf, fb_width, fb_height, BLUR_RAD, 240);
242 swap_smoke_buffers();
245 if(msec - last_swap < 16) {
251 swap_buffers(fb_pixels);
252 last_swap = get_msec();
257 int init_emitter(struct emitter *em, int num, unsigned char *map, int xsz, int ysz)
260 float aspect = (float)xsz / (float)ysz;
263 if(!(em->plist = malloc(num * sizeof *em->plist))) {
269 for(i=0; i<num; i++) {
273 } while(map[y * xsz + x] < 128);
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;
286 void update_particles(struct emitter *em, float dt)
290 struct particle *p = em->plist;
291 struct g3d_vertex *v = varr;
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;
302 if(p->life < 0.0f) p->life = 0.0f;
308 v->a = cround64(p->life * 255.0 / MAX_LIFE);
310 v->g = (v->a & 0xe0) >> 3;
311 v->b = (v->a & 0x1f) << 3;
318 void draw_particles(struct emitter *em)
320 g3d_draw(G3D_POINTS, varr, PCOUNT);
324 int init_vfield_load(struct vfield *vf, const char *fname)
329 if(!(fp = fopen(fname, "rb"))) {
330 fprintf(stderr, "failed to open vector field: %s\n", fname);
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");
340 /* assume xsz is pow2 otherwise fuck you */
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);
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);
361 vf->pos.x = vf->pos.y = 0;
362 vf->size.x = vf->size.y = 1;
366 void vfield_eval(struct vfield *vf, float x, float y, struct vec2 *dir)
370 struct vec2 *p1, *p2;
371 struct vec2 left, right;
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;
380 if(x > vf->width - 2) x = vf->width - 2;
381 if(y > vf->height - 2) y = vf->height - 2;
386 p1 = vf->v + (py << vf->xshift) + px;
393 left.x = p1->x + (p2->x - p1->x) * ty;
394 left.y = p1->y + (p2->y - p1->y) * ty;
397 right.x = p1->x + (p2->x - p1->x) * ty;
398 right.y = p1->y + (p2->y - p1->y) * ty;
400 dir->x = left.x + (right.x - left.x) * tx;
401 dir->y = left.y + (right.y - left.y) * ty;
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;