Correct logic of BSP back/front tests
[neverball] / ball / game.c
1 /*
2  * Copyright (C) 2003 Robert Kooima
3  *
4  * NEVERBALL is  free software; you can redistribute  it and/or modify
5  * it under the  terms of the GNU General  Public License as published
6  * by the Free  Software Foundation; either version 2  of the License,
7  * or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT  ANY  WARRANTY;  without   even  the  implied  warranty  of
11  * MERCHANTABILITY or  FITNESS FOR A PARTICULAR PURPOSE.   See the GNU
12  * General Public License for more details.
13  */
14
15 #include <SDL.h>
16 #include <math.h>
17
18 #include "glext.h"
19 #include "game.h"
20 #include "vec3.h"
21 #include "geom.h"
22 #include "item.h"
23 #include "back.h"
24 #include "part.h"
25 #include "ball.h"
26 #include "image.h"
27 #include "audio.h"
28 #include "solid_gl.h"
29 #include "config.h"
30 #include "binary.h"
31
32 /*---------------------------------------------------------------------------*/
33
34 static int game_state = 0;
35
36 static struct s_file file;
37 static struct s_file back;
38
39 static int   reflective;                /* Reflective geometry used?         */
40
41 static float timer      = 0.f;          /* Clock time                        */
42 static int   timer_down = 1;            /* Timer go up or down?              */
43
44 static float game_rx;                   /* Floor rotation about X axis       */
45 static float game_rz;                   /* Floor rotation about Z axis       */
46
47 static float view_a;                    /* Ideal view rotation about Y axis  */
48 static float view_dc;                   /* Ideal view distance above ball    */
49 static float view_dp;                   /* Ideal view distance above ball    */
50 static float view_dz;                   /* Ideal view distance behind ball   */
51 static float view_fov;                  /* Field of view                     */
52
53 static float view_c[3];                 /* Current view center               */
54 static float view_v[3];                 /* Current view vector               */
55 static float view_p[3];                 /* Current view position             */
56 static float view_e[3][3];              /* Current view reference frame      */
57 static float view_k;
58
59 static int   coins  = 0;                /* Collected coins                   */
60 static int   goal_e = 0;                /* Goal enabled flag                 */
61 static float goal_k = 0;                /* Goal animation                    */
62 static int   jump_e = 1;                /* Jumping enabled flag              */
63 static int   jump_b = 0;                /* Jump-in-progress flag             */
64 static float jump_dt;                   /* Jump duration                     */
65 static float jump_p[3];                 /* Jump destination                  */
66 static float fade_k = 0.0;              /* Fade in/out level                 */
67 static float fade_d = 0.0;              /* Fade in/out direction             */
68
69 /*---------------------------------------------------------------------------*/
70
71 /*
72  * This is an abstraction of the game's input state.  All input is
73  * encapsulated here, and all references to the input by the game are made
74  * here.  This has the effect of homogenizing input for use in replay
75  * recording and playback.
76  *
77  * x and z:
78  *     -32767 = -ANGLE_BOUND
79  *     +32767 = +ANGLE_BOUND
80  *
81  * r:
82  *     -32767 = -VIEWR_BOUND
83  *     +32767 = +VIEWR_BOUND
84  *
85  */
86
87 struct input
88 {
89     short x;
90     short z;
91     short r;
92     short c;
93 };
94
95 static struct input input_current;
96
97 static void input_init(void)
98 {
99     input_current.x = 0;
100     input_current.z = 0;
101     input_current.r = 0;
102     input_current.c = 0;
103 }
104
105 static void input_set_x(float x)
106 {
107     if (x < -ANGLE_BOUND) x = -ANGLE_BOUND;
108     if (x >  ANGLE_BOUND) x =  ANGLE_BOUND;
109
110     input_current.x = (short) (32767.0f * x / ANGLE_BOUND);
111 }
112
113 static void input_set_z(float z)
114 {
115     if (z < -ANGLE_BOUND) z = -ANGLE_BOUND;
116     if (z >  ANGLE_BOUND) z =  ANGLE_BOUND;
117
118     input_current.z = (short) (32767.0f * z / ANGLE_BOUND);
119 }
120
121 static void input_set_r(float r)
122 {
123     if (r < -VIEWR_BOUND) r = -VIEWR_BOUND;
124     if (r >  VIEWR_BOUND) r =  VIEWR_BOUND;
125
126     input_current.r = (short) (32767.0f * r / VIEWR_BOUND);
127 }
128
129 static void input_set_c(int c)
130 {
131     input_current.c = (short) c;
132 }
133
134 static float input_get_x(void)
135 {
136     return ANGLE_BOUND * (float) input_current.x / 32767.0f;
137 }
138
139 static float input_get_z(void)
140 {
141     return ANGLE_BOUND * (float) input_current.z / 32767.0f;
142 }
143
144 static float input_get_r(void)
145 {
146     return VIEWR_BOUND * (float) input_current.r / 32767.0f;
147 }
148
149 static int input_get_c(void)
150 {
151     return (int) input_current.c;
152 }
153
154 int input_put(FILE *fout)
155 {
156     if (game_state)
157     {
158         put_short(fout, &input_current.x);
159         put_short(fout, &input_current.z);
160         put_short(fout, &input_current.r);
161         put_short(fout, &input_current.c);
162
163         return 1;
164     }
165     return 0;
166 }
167
168 int input_get(FILE *fin)
169 {
170     if (game_state)
171     {
172         get_short(fin, &input_current.x);
173         get_short(fin, &input_current.z);
174         get_short(fin, &input_current.r);
175         get_short(fin, &input_current.c);
176
177         return (feof(fin) ? 0 : 1);
178     }
179     return 0;
180 }
181
182 /*---------------------------------------------------------------------------*/
183
184 static int   grow = 0;                  /* Should the ball be changing size? */
185 static float grow_orig = 0;             /* the original ball size            */
186 static float grow_goal = 0;             /* how big or small to get!          */
187 static float grow_t = 0.0;              /* timer for the ball to grow...     */
188 static float grow_strt = 0;             /* starting value for growth         */
189 static int   got_orig = 0;              /* Do we know original ball size?    */
190
191 #define GROW_TIME  0.5f                 /* sec for the ball to get to size.  */
192 #define GROW_BIG   1.5f                 /* large factor                      */
193 #define GROW_SMALL 0.5f                 /* small factor                      */
194
195 static int   grow_state = 0;            /* Current state (values -1, 0, +1)  */
196
197 static void grow_init(const struct s_file *fp, int type)
198 {
199     if (!got_orig)
200     {
201         grow_orig  = fp->uv->r;
202         grow_goal  = grow_orig;
203         grow_strt  = grow_orig;
204
205         grow_state = 0;
206
207         got_orig   = 1;
208     }
209
210     if (type == ITEM_SHRINK)
211     {
212         switch (grow_state)
213         {
214         case -1:
215             break;
216
217         case  0:
218             audio_play(AUD_SHRINK, 1.f);
219             grow_goal = grow_orig * GROW_SMALL;
220             grow_state = -1;
221             grow = 1;
222             break;
223
224         case +1:
225             audio_play(AUD_SHRINK, 1.f);
226             grow_goal = grow_orig;
227             grow_state = 0;
228             grow = 1;
229             break;
230         }
231     }
232     else if (type == ITEM_GROW)
233     {
234         switch (grow_state)
235         {
236         case -1:
237             audio_play(AUD_GROW, 1.f);
238             grow_goal = grow_orig;
239             grow_state = 0;
240             grow = 1;
241             break;
242
243         case  0:
244             audio_play(AUD_GROW, 1.f);
245             grow_goal = grow_orig * GROW_BIG;
246             grow_state = +1;
247             grow = 1;
248             break;
249
250         case +1:
251             break;
252         }
253     }
254
255     if (grow)
256     {
257         grow_t = 0.0;
258         grow_strt = fp->uv->r;
259     }
260 }
261
262 static void grow_step(const struct s_file *fp, float dt)
263 {
264     float dr;
265
266     if (!grow)
267         return;
268
269     /* Calculate new size based on how long since you touched the coin... */
270
271     grow_t += dt;
272
273     if (grow_t >= GROW_TIME)
274     {
275         grow = 0;
276         grow_t = GROW_TIME;
277     }
278
279     dr = grow_strt + ((grow_goal-grow_strt) * (1.0f / (GROW_TIME / grow_t)));
280
281     /* No sinking through the floor! Keeps ball's bottom constant. */
282
283     fp->uv->p[1] += (dr - fp->uv->r);
284     fp->uv->r     =  dr;
285 }
286
287 /*---------------------------------------------------------------------------*/
288
289 static void view_init(void)
290 {
291     view_fov = (float) config_get_d(CONFIG_VIEW_FOV);
292     view_dp  = (float) config_get_d(CONFIG_VIEW_DP) / 100.0f;
293     view_dc  = (float) config_get_d(CONFIG_VIEW_DC) / 100.0f;
294     view_dz  = (float) config_get_d(CONFIG_VIEW_DZ) / 100.0f;
295     view_k   = 1.0f;
296     view_a   = 0.0f;
297
298     view_c[0] = 0.f;
299     view_c[1] = view_dc;
300     view_c[2] = 0.f;
301
302     view_p[0] =     0.f;
303     view_p[1] = view_dp;
304     view_p[2] = view_dz;
305
306     view_e[0][0] = 1.f;
307     view_e[0][1] = 0.f;
308     view_e[0][2] = 0.f;
309     view_e[1][0] = 0.f;
310     view_e[1][1] = 1.f;
311     view_e[1][2] = 0.f;
312     view_e[2][0] = 0.f;
313     view_e[2][1] = 0.f;
314     view_e[2][2] = 1.f;
315 }
316
317 int game_init(const char *file_name, int t, int e)
318 {
319     char *back_name = NULL, *grad_name = NULL;
320
321     int i;
322
323     timer      = (float) t / 100.f;
324     timer_down = (t > 0);
325     coins      = 0;
326
327     if (game_state)
328         game_free();
329
330     if (!sol_load_gl(&file, config_data(file_name),
331                      config_get_d(CONFIG_TEXTURES),
332                      config_get_d(CONFIG_SHADOW)))
333         return (game_state = 0);
334
335     reflective = sol_reflective(&file);
336
337     game_state = 1;
338
339     input_init();
340
341     game_rx = 0.0f;
342     game_rz = 0.0f;
343
344     /* Initialize jump and goal states. */
345
346     jump_e = 1;
347     jump_b = 0;
348
349     goal_e = e ? 1    : 0;
350     goal_k = e ? 1.0f : 0.0f;
351
352     /* Initialise the level, background, particles, fade, and view. */
353
354     fade_k =  1.0f;
355     fade_d = -2.0f;
356
357     for (i = 0; i < file.dc; i++)
358     {
359         char *k = file.av + file.dv[i].ai;
360         char *v = file.av + file.dv[i].aj;
361
362         if (strcmp(k, "back") == 0) back_name = v;
363         if (strcmp(k, "grad") == 0) grad_name = v;
364     }
365
366     part_reset(GOAL_HEIGHT);
367     view_init();
368     back_init(grad_name, config_get_d(CONFIG_GEOMETRY));
369
370     sol_load_gl(&back, config_data(back_name),
371                 config_get_d(CONFIG_TEXTURES), 0);
372
373     /* Initialize ball size tracking... */
374
375     got_orig = 0;
376     grow = 0;
377
378     return game_state;
379 }
380
381 void game_free(void)
382 {
383     if (game_state)
384     {
385         sol_free_gl(&file);
386         sol_free_gl(&back);
387         back_free();
388     }
389     game_state = 0;
390 }
391
392 /*---------------------------------------------------------------------------*/
393
394 int curr_clock(void)
395 {
396     return (int) (timer * 100.f);
397 }
398
399 int curr_coins(void)
400 {
401     return coins;
402 }
403
404 /*---------------------------------------------------------------------------*/
405
406 static void game_draw_balls(const struct s_file *fp,
407                             const float *bill_M, float t)
408 {
409     float c[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
410
411     float ball_M[16];
412     float pend_M[16];
413
414     m_basis(ball_M, fp->uv[0].e[0], fp->uv[0].e[1], fp->uv[0].e[2]);
415     m_basis(pend_M, fp->uv[0].E[0], fp->uv[0].E[1], fp->uv[0].E[2]);
416
417     glPushAttrib(GL_LIGHTING_BIT);
418     glPushMatrix();
419     {
420         glTranslatef(fp->uv[0].p[0],
421                      fp->uv[0].p[1] + BALL_FUDGE,
422                      fp->uv[0].p[2]);
423         glScalef(fp->uv[0].r,
424                  fp->uv[0].r,
425                  fp->uv[0].r);
426
427         glColor4fv(c);
428         ball_draw(ball_M, pend_M, bill_M, t);
429     }
430     glPopMatrix();
431     glPopAttrib();
432 }
433
434 static void game_draw_items(const struct s_file *fp, float t)
435 {
436     float r = 360.f * t;
437     int hi;
438
439     glPushAttrib(GL_LIGHTING_BIT);
440     {
441         item_push(ITEM_COIN);
442         {
443             for (hi = 0; hi < fp->hc; hi++)
444
445                 if (fp->hv[hi].t == ITEM_COIN && fp->hv[hi].n > 0)
446                 {
447                     glPushMatrix();
448                     {
449                         glTranslatef(fp->hv[hi].p[0],
450                                      fp->hv[hi].p[1],
451                                      fp->hv[hi].p[2]);
452                         glRotatef(r, 0.0f, 1.0f, 0.0f);
453                         item_draw(&fp->hv[hi], r);
454                     }
455                     glPopMatrix();
456                 }
457         }
458         item_pull();
459
460         item_push(ITEM_SHRINK);
461         {
462             for (hi = 0; hi < fp->hc; hi++)
463
464                 if (fp->hv[hi].t == ITEM_SHRINK)
465                 {
466                     glPushMatrix();
467                     {
468                         glTranslatef(fp->hv[hi].p[0],
469                                      fp->hv[hi].p[1],
470                                      fp->hv[hi].p[2]);
471                         glRotatef(r, 0.0f, 1.0f, 0.0f);
472                         item_draw(&fp->hv[hi], r);
473                     }
474                     glPopMatrix();
475                 }
476         }
477         item_pull();
478
479         item_push(ITEM_GROW);
480         {
481             for (hi = 0; hi < fp->hc; hi++)
482
483                 if (fp->hv[hi].t == ITEM_GROW)
484                 {
485                     glPushMatrix();
486                     {
487                         glTranslatef(fp->hv[hi].p[0],
488                                      fp->hv[hi].p[1],
489                                      fp->hv[hi].p[2]);
490                         glRotatef(r, 0.0f, 1.0f, 0.0f);
491                         item_draw(&fp->hv[hi], r);
492                     }
493                     glPopMatrix();
494                 }
495         }
496         item_pull();
497     }
498     glPopAttrib();
499 }
500
501 static void game_draw_goals(const struct s_file *fp, const float *M, float t)
502 {
503     if (goal_e)
504     {
505         int zi;
506
507         /* Draw the goal particles. */
508
509         glEnable(GL_TEXTURE_2D);
510         {
511             for (zi = 0; zi < fp->zc; zi++)
512             {
513                 glPushMatrix();
514                 {
515                     glTranslatef(fp->zv[zi].p[0],
516                                  fp->zv[zi].p[1],
517                                  fp->zv[zi].p[2]);
518
519                     part_draw_goal(M, fp->zv[zi].r, goal_k, t);
520                 }
521                 glPopMatrix();
522             }
523         }
524         glDisable(GL_TEXTURE_2D);
525
526         /* Draw the goal column. */
527
528         for (zi = 0; zi < fp->zc; zi++)
529         {
530             glPushMatrix();
531             {
532                 glTranslatef(fp->zv[zi].p[0],
533                              fp->zv[zi].p[1],
534                              fp->zv[zi].p[2]);
535
536                 glScalef(fp->zv[zi].r,
537                          goal_k,
538                          fp->zv[zi].r);
539
540                 goal_draw();
541             }
542             glPopMatrix();
543         }
544     }
545 }
546
547 static void game_draw_jumps(const struct s_file *fp)
548 {
549     int ji;
550
551     for (ji = 0; ji < fp->jc; ji++)
552     {
553         glPushMatrix();
554         {
555             glTranslatef(fp->jv[ji].p[0],
556                          fp->jv[ji].p[1],
557                          fp->jv[ji].p[2]);
558             glScalef(fp->jv[ji].r,
559                      1.0f,
560                      fp->jv[ji].r);
561
562             jump_draw(!jump_e);
563         }
564         glPopMatrix();
565     }
566 }
567
568 static void game_draw_swchs(const struct s_file *fp)
569 {
570     int xi;
571
572     for (xi = 0; xi < fp->xc; xi++)
573     {
574         if (fp->xv[xi].i)
575             continue;
576
577         glPushMatrix();
578         {
579             glTranslatef(fp->xv[xi].p[0],
580                          fp->xv[xi].p[1],
581                          fp->xv[xi].p[2]);
582             glScalef(fp->xv[xi].r,
583                      1.0f,
584                      fp->xv[xi].r);
585
586             swch_draw(fp->xv[xi].f, fp->xv[xi].e);
587         }
588         glPopMatrix();
589     }
590 }
591
592 /*---------------------------------------------------------------------------*/
593
594 static void game_draw_tilt(int d)
595 {
596     const float *ball_p = file.uv->p;
597
598     /* Rotate the environment about the position of the ball. */
599
600     glTranslatef(+ball_p[0], +ball_p[1] * d, +ball_p[2]);
601     glRotatef(-game_rz * d, view_e[2][0], view_e[2][1], view_e[2][2]);
602     glRotatef(-game_rx * d, view_e[0][0], view_e[0][1], view_e[0][2]);
603     glTranslatef(-ball_p[0], -ball_p[1] * d, -ball_p[2]);
604 }
605
606 static void game_refl_all(void)
607 {
608     glPushMatrix();
609     {
610         game_draw_tilt(1);
611
612         /* Draw the floor. */
613
614         sol_refl(&file);
615     }
616     glPopMatrix();
617 }
618
619 /*---------------------------------------------------------------------------*/
620
621 static void game_draw_light(void)
622 {
623     const float light_p[2][4] = {
624         { -8.0f, +32.0f, -8.0f, 0.0f },
625         { +8.0f, +32.0f, +8.0f, 0.0f },
626     };
627     const float light_c[2][4] = {
628         { 1.0f, 0.8f, 0.8f, 1.0f },
629         { 0.8f, 1.0f, 0.8f, 1.0f },
630     };
631
632     /* Configure the lighting. */
633
634     glEnable(GL_LIGHT0);
635     glLightfv(GL_LIGHT0, GL_POSITION, light_p[0]);
636     glLightfv(GL_LIGHT0, GL_DIFFUSE,  light_c[0]);
637     glLightfv(GL_LIGHT0, GL_SPECULAR, light_c[0]);
638
639     glEnable(GL_LIGHT1);
640     glLightfv(GL_LIGHT1, GL_POSITION, light_p[1]);
641     glLightfv(GL_LIGHT1, GL_DIFFUSE,  light_c[1]);
642     glLightfv(GL_LIGHT1, GL_SPECULAR, light_c[1]);
643 }
644
645 static void game_draw_back(int pose, int d, float t)
646 {
647     glPushMatrix();
648     {
649         if (d < 0)
650         {
651             glRotatef(game_rz * 2, view_e[2][0], view_e[2][1], view_e[2][2]);
652             glRotatef(game_rx * 2, view_e[0][0], view_e[0][1], view_e[0][2]);
653         }
654
655         glTranslatef(view_p[0], view_p[1] * d, view_p[2]);
656
657         if (config_get_d(CONFIG_BACKGROUND))
658         {
659             /* Draw all background layers back to front. */
660
661             sol_back(&back, BACK_DIST, FAR_DIST,  t);
662             back_draw(0);
663             sol_back(&back,         0, BACK_DIST, t);
664         }
665         else back_draw(0);
666     }
667     glPopMatrix();
668 }
669
670 static void game_clip_refl(int d)
671 {
672     /* Fudge to eliminate the floor from reflection. */
673
674     GLdouble e[4], k = -0.00001;
675
676     e[0] = 0;
677     e[1] = 1;
678     e[2] = 0;
679     e[3] = k;
680
681     glClipPlane(GL_CLIP_PLANE0, e);
682 }
683
684 static void game_clip_ball(int d, const float *p)
685 {
686     GLdouble r, c[3], pz[4], nz[4];
687
688     /* Compute the plane giving the front of the ball, as seen from view_p. */
689
690     c[0] = p[0];
691     c[1] = p[1] * d;
692     c[2] = p[2];
693
694     pz[0] = view_p[0] - c[0];
695     pz[1] = view_p[1] - c[1];
696     pz[2] = view_p[2] - c[2];
697
698     r = sqrt(pz[0] * pz[0] + pz[1] * pz[1] + pz[2] * pz[2]);
699
700     pz[0] /= r;
701     pz[1] /= r;
702     pz[2] /= r;
703     pz[3] = -(pz[0] * c[0] +
704               pz[1] * c[1] +
705               pz[2] * c[2]);
706
707     /* Find the plane giving the back of the ball, as seen from view_p. */
708
709     nz[0] = -pz[0];
710     nz[1] = -pz[1];
711     nz[2] = -pz[2];
712     nz[3] = -pz[3];
713
714     /* Reflect these planes as necessary, and store them in the GL state. */
715
716     pz[1] *= d;
717     nz[1] *= d;
718
719     glClipPlane(GL_CLIP_PLANE1, nz);
720     glClipPlane(GL_CLIP_PLANE2, pz);
721 }
722
723 static void game_draw_fore(int pose, const float *M, int d, float t)
724 {
725     const float *ball_p = file.uv->p;
726     const float  ball_r = file.uv->r;
727
728     glPushMatrix();
729     {
730         /* Rotate the environment about the position of the ball. */
731
732         game_draw_tilt(d);
733
734         /* Compute clipping planes for reflection and ball facing. */
735
736         game_clip_refl(d);
737         game_clip_ball(d, ball_p);
738
739         if (d < 0)
740             glEnable(GL_CLIP_PLANE0);
741
742         if (pose)
743             sol_draw(&file, 0, 1);
744         else
745         {
746             /* Draw the coins. */
747
748             game_draw_items(&file, t);
749
750             /* Draw the floor. */
751
752             sol_draw(&file, 0, 1);
753
754             /* Draw the ball shadow. */
755
756             if (d > 0 && config_get_d(CONFIG_SHADOW))
757             {
758                 shad_draw_set(ball_p, ball_r);
759                 sol_shad(&file);
760                 shad_draw_clr();
761             }
762
763             /* Draw the ball. */
764
765             game_draw_balls(&file, M, t);
766         }
767
768         /* Draw the particles and light columns. */
769
770         glEnable(GL_COLOR_MATERIAL);
771         glDisable(GL_LIGHTING);
772         glDepthMask(GL_FALSE);
773         {
774             glColor3f(1.0f, 1.0f, 1.0f);
775
776             sol_bill(&file, M, t);
777             part_draw_coin(M, t);
778
779             glDisable(GL_TEXTURE_2D);
780             {
781                 game_draw_goals(&file, M, t);
782                 game_draw_jumps(&file);
783                 game_draw_swchs(&file);
784             }
785             glEnable(GL_TEXTURE_2D);
786
787             glColor3f(1.0f, 1.0f, 1.0f);
788         }
789         glDepthMask(GL_TRUE);
790         glEnable(GL_LIGHTING);
791         glDisable(GL_COLOR_MATERIAL);
792
793         if (d < 0)
794             glDisable(GL_CLIP_PLANE0);
795     }
796     glPopMatrix();
797 }
798
799 void game_draw(int pose, float t)
800 {
801     float fov = view_fov;
802
803     if (jump_b) fov *= 2.f * fabsf(jump_dt - 0.5);
804
805     if (game_state)
806     {
807         config_push_persp(fov, 0.1f, FAR_DIST);
808         glPushMatrix();
809         {
810             float T[16], U[16], M[16], v[3];
811
812             /* Compute direct and reflected view bases. */
813
814             v[0] = +view_p[0];
815             v[1] = -view_p[1];
816             v[2] = +view_p[2];
817
818             m_view(T, view_c, view_p, view_e[1]);
819             m_view(U, view_c, v,      view_e[1]);
820
821             m_xps(M, T);
822
823             /* Apply current the view. */
824
825             v_sub(v, view_c, view_p);
826
827             glTranslatef(0.f, 0.f, -v_len(v));
828             glMultMatrixf(M);
829             glTranslatef(-view_c[0], -view_c[1], -view_c[2]);
830
831             if (reflective && config_get_d(CONFIG_REFLECTION))
832             {
833                 glEnable(GL_STENCIL_TEST);
834                 {
835                     /* Draw the mirrors only into the stencil buffer. */
836
837                     glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);
838                     glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
839                     glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
840                     glDepthMask(GL_FALSE);
841
842                     game_refl_all();
843
844                     glDepthMask(GL_TRUE);
845                     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
846                     glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
847                     glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF);
848
849                     /* Draw the scene reflected into color and depth buffers. */
850
851                     glFrontFace(GL_CW);
852                     glPushMatrix();
853                     {
854                         glScalef(+1.0f, -1.0f, +1.0f);
855
856                         game_draw_light();
857                         game_draw_back(pose,    -1, t);
858                         game_draw_fore(pose, U, -1, t);
859                     }
860                     glPopMatrix();
861                     glFrontFace(GL_CCW);
862                 }
863                 glDisable(GL_STENCIL_TEST);
864             }
865
866             /* Draw the scene normally. */
867
868             game_draw_light();
869             game_refl_all();
870             game_draw_back(pose,    +1, t);
871             game_draw_fore(pose, T, +1, t);
872         }
873         glPopMatrix();
874         config_pop_matrix();
875
876         /* Draw the fade overlay. */
877
878         fade_draw(fade_k);
879     }
880 }
881
882 /*---------------------------------------------------------------------------*/
883
884 static void game_update_grav(float h[3], const float g[3])
885 {
886     float x[3];
887     float y[3] = { 0.0f, 1.0f, 0.0f };
888     float z[3];
889     float X[16];
890     float Z[16];
891     float M[16];
892
893     /* Compute the gravity vector from the given world rotations. */
894
895     z[0] = fsinf(V_RAD(view_a));
896     z[1] = 0.0f;
897     z[2] = fcosf(V_RAD(view_a));
898
899     v_crs(x, y, z);
900     v_crs(z, x, y);
901     v_nrm(x, x);
902     v_nrm(z, z);
903
904     m_rot (Z, z, V_RAD(game_rz));
905     m_rot (X, x, V_RAD(game_rx));
906     m_mult(M, Z, X);
907     m_vxfm(h, M, g);
908 }
909
910 static void game_update_view(float dt)
911 {
912     float dc = view_dc * (jump_b ? 2.0f * fabsf(jump_dt - 0.5f) : 1.0f);
913     float da = input_get_r() * dt * 90.0f;
914     float k;
915
916     float M[16], v[3], Y[3] = { 0.0f, 1.0f, 0.0f };
917
918     view_a += da;
919
920     /* Center the view about the ball. */
921
922     v_cpy(view_c, file.uv->p);
923     v_inv(view_v, file.uv->v);
924
925     view_e[2][0] = fsinf(V_RAD(view_a));
926     view_e[2][1] = 0.0;
927     view_e[2][2] = fcosf(V_RAD(view_a));
928
929     switch (input_get_c())
930     {
931     case 1: /* Camera 1: Viewpoint chases the ball position. */
932
933         v_sub(view_e[2], view_p, view_c);
934
935         break;
936
937     case 2: /* Camera 2: View vector is given by view angle. */
938
939         break;
940
941     default: /* Default: View vector approaches the ball velocity vector. */
942
943         v_mad(view_e[2], view_e[2], view_v, v_dot(view_v, view_v) * dt / 4);
944
945         break;
946     }
947
948     /* Orthonormalize the new view reference frame. */
949
950     v_crs(view_e[0], view_e[1], view_e[2]);
951     v_crs(view_e[2], view_e[0], view_e[1]);
952     v_nrm(view_e[0], view_e[0]);
953     v_nrm(view_e[2], view_e[2]);
954
955     /* Compute the new view position. */
956
957     k = 1.0f + v_dot(view_e[2], view_v) / 10.0f;
958
959     view_k = view_k + (k - view_k) * dt;
960
961     if (view_k < 0.5) view_k = 0.5;
962
963     v_scl(v,    view_e[1], view_dp * view_k);
964     v_mad(v, v, view_e[2], view_dz * view_k);
965     m_rot(M, Y, V_RAD(da));
966     m_vxfm(view_p, M, v);
967     v_add(view_p, view_p, file.uv->p);
968
969     /* Compute the new view center. */
970
971     v_cpy(view_c, file.uv->p);
972     v_mad(view_c, view_c, view_e[1], dc);
973
974     /* Note the current view angle. */
975
976     view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
977 }
978
979 static void game_update_time(float dt, int b)
980 {
981     if (goal_e && goal_k < 1.0f)
982         goal_k += dt;
983
984    /* The ticking clock. */
985
986     if (b && timer_down)
987     {
988         if (timer < 600.f)
989             timer -= dt;
990         if (timer < 0.f)
991             timer = 0.f;
992     }
993     else if (b)
994     {
995         timer += dt;
996     }
997 }
998
999 static int game_update_state(int bt)
1000 {
1001     struct s_file *fp = &file;
1002     struct s_goal *zp;
1003     struct s_item *hp;
1004
1005     float p[3];
1006     float c[3];
1007
1008     /* Test for an item. */
1009
1010     if (bt && (hp = sol_item_test(fp, p, ITEM_RADIUS)))
1011     {
1012         item_color(hp, c);
1013         part_burst(p, c);
1014
1015         grow_init(fp, hp->t);
1016
1017         if (hp->t == ITEM_COIN)
1018             coins += hp->n;
1019
1020         audio_play(AUD_COIN, 1.f);
1021
1022         /* Discard item. */
1023
1024         hp->t = ITEM_NONE;
1025     }
1026
1027     /* Test for a switch. */
1028
1029     if (sol_swch_test(fp, 0))
1030         audio_play(AUD_SWITCH, 1.f);
1031
1032     /* Test for a jump. */
1033
1034     if (jump_e == 1 && jump_b == 0 && sol_jump_test(fp, jump_p, 0) == 1)
1035     {
1036         jump_b  = 1;
1037         jump_e  = 0;
1038         jump_dt = 0.f;
1039
1040         audio_play(AUD_JUMP, 1.f);
1041     }
1042     if (jump_e == 0 && jump_b == 0 && sol_jump_test(fp, jump_p, 0) == 0)
1043         jump_e = 1;
1044
1045     /* Test for a goal. */
1046
1047     if (bt && goal_e && (zp = sol_goal_test(fp, p, 0)))
1048     {
1049         audio_play(AUD_GOAL, 1.0f);
1050         return GAME_GOAL;
1051     }
1052
1053     /* Test for time-out. */
1054
1055     if (bt && timer_down && timer <= 0.f)
1056     {
1057         audio_play(AUD_TIME, 1.0f);
1058         return GAME_TIME;
1059     }
1060
1061     /* Test for fall-out. */
1062
1063     if (bt && fp->uv[0].p[1] < fp->vv[0].p[1])
1064     {
1065         audio_play(AUD_FALL, 1.0f);
1066         return GAME_FALL;
1067     }
1068
1069     return GAME_NONE;
1070 }
1071
1072 int game_step(const float g[3], float dt, int bt)
1073 {
1074     if (game_state)
1075     {
1076         struct s_file *fp = &file;
1077
1078         float h[3];
1079
1080         /* Smooth jittery or discontinuous input. */
1081
1082         game_rx += (input_get_x() - game_rx) * dt / RESPONSE;
1083         game_rz += (input_get_z() - game_rz) * dt / RESPONSE;
1084
1085         grow_step(fp, dt);
1086
1087         game_update_grav(h, g);
1088         part_step(h, dt);
1089
1090         if (jump_b)
1091         {
1092             jump_dt += dt;
1093
1094             /* Handle a jump. */
1095
1096             if (0.5f < jump_dt)
1097             {
1098                 fp->uv[0].p[0] = jump_p[0];
1099                 fp->uv[0].p[1] = jump_p[1];
1100                 fp->uv[0].p[2] = jump_p[2];
1101             }
1102             if (1.0f < jump_dt)
1103                 jump_b = 0;
1104         }
1105         else
1106         {
1107             /* Run the sim. */
1108
1109             float b = sol_step(fp, h, dt, 0, NULL);
1110
1111             /* Mix the sound of a ball bounce. */
1112
1113             if (b > 0.5f)
1114             {
1115                 float k = (b - 0.5f) * 2.0f;
1116
1117                 if (got_orig)
1118                 {
1119                     if      (fp->uv->r > grow_orig) audio_play(AUD_BUMPL, k);
1120                     else if (fp->uv->r < grow_orig) audio_play(AUD_BUMPS, k);
1121                     else                            audio_play(AUD_BUMPM, k);
1122                 }
1123                 else audio_play(AUD_BUMPM, k);
1124             }
1125         }
1126
1127         game_step_fade(dt);
1128         game_update_view(dt);
1129         game_update_time(dt, bt);
1130
1131         return game_update_state(bt);
1132     }
1133     return GAME_NONE;
1134 }
1135
1136 /*---------------------------------------------------------------------------*/
1137
1138 void game_set_goal(void)
1139 {
1140     audio_play(AUD_SWITCH, 1.0f);
1141     goal_e = 1;
1142 }
1143
1144 void game_clr_goal(void)
1145 {
1146     goal_e = 0;
1147 }
1148
1149 /*---------------------------------------------------------------------------*/
1150
1151 void game_set_x(int k)
1152 {
1153     input_set_x(-ANGLE_BOUND * k / JOY_MAX);
1154 }
1155
1156 void game_set_z(int k)
1157 {
1158     input_set_z(+ANGLE_BOUND * k / JOY_MAX);
1159 }
1160
1161 void game_set_ang(int x, int z)
1162 {
1163     input_set_x(x);
1164     input_set_z(z);
1165 }
1166
1167 void game_set_pos(int x, int y)
1168 {
1169     input_set_x(input_get_x() + 40.0f * y / config_get_d(CONFIG_MOUSE_SENSE));
1170     input_set_z(input_get_z() + 40.0f * x / config_get_d(CONFIG_MOUSE_SENSE));
1171 }
1172
1173 void game_set_cam(int c)
1174 {
1175     input_set_c(c);
1176 }
1177
1178 void game_set_rot(float r)
1179 {
1180     input_set_r(r);
1181 }
1182
1183 /*---------------------------------------------------------------------------*/
1184
1185 void game_set_fly(float k)
1186 {
1187     struct s_file *fp = &file;
1188
1189     float  x[3] = { 1.f, 0.f, 0.f };
1190     float  y[3] = { 0.f, 1.f, 0.f };
1191     float  z[3] = { 0.f, 0.f, 1.f };
1192     float c0[3] = { 0.f, 0.f, 0.f };
1193     float p0[3] = { 0.f, 0.f, 0.f };
1194     float c1[3] = { 0.f, 0.f, 0.f };
1195     float p1[3] = { 0.f, 0.f, 0.f };
1196     float  v[3];
1197
1198     z[0] = fsinf(V_RAD(view_a));
1199     z[2] = fcosf(V_RAD(view_a));
1200
1201     v_cpy(view_e[0], x);
1202     v_cpy(view_e[1], y);
1203     v_cpy(view_e[2], z);
1204
1205     /* k = 0.0 view is at the ball. */
1206
1207     if (fp->uc > 0)
1208     {
1209         v_cpy(c0, fp->uv[0].p);
1210         v_cpy(p0, fp->uv[0].p);
1211     }
1212
1213     v_mad(p0, p0, y, view_dp);
1214     v_mad(p0, p0, z, view_dz);
1215     v_mad(c0, c0, y, view_dc);
1216
1217     /* k = +1.0 view is s_view 0 */
1218
1219     if (k >= 0 && fp->wc > 0)
1220     {
1221         v_cpy(p1, fp->wv[0].p);
1222         v_cpy(c1, fp->wv[0].q);
1223     }
1224
1225     /* k = -1.0 view is s_view 1 */
1226
1227     if (k <= 0 && fp->wc > 1)
1228     {
1229         v_cpy(p1, fp->wv[1].p);
1230         v_cpy(c1, fp->wv[1].q);
1231     }
1232
1233     /* Interpolate the views. */
1234
1235     v_sub(v, p1, p0);
1236     v_mad(view_p, p0, v, k * k);
1237
1238     v_sub(v, c1, c0);
1239     v_mad(view_c, c0, v, k * k);
1240
1241     /* Orthonormalize the view basis. */
1242
1243     v_sub(view_e[2], view_p, view_c);
1244     v_crs(view_e[0], view_e[1], view_e[2]);
1245     v_crs(view_e[2], view_e[0], view_e[1]);
1246     v_nrm(view_e[0], view_e[0]);
1247     v_nrm(view_e[2], view_e[2]);
1248 }
1249
1250 void game_look(float phi, float theta)
1251 {
1252     view_c[0] = view_p[0] + fsinf(V_RAD(theta)) * fcosf(V_RAD(phi));
1253     view_c[1] = view_p[1] +                       fsinf(V_RAD(phi));
1254     view_c[2] = view_p[2] - fcosf(V_RAD(theta)) * fcosf(V_RAD(phi));
1255 }
1256
1257 /*---------------------------------------------------------------------------*/
1258
1259 void game_kill_fade(void)
1260 {
1261     fade_k = 0.0f;
1262     fade_d = 0.0f;
1263 }
1264
1265 void game_step_fade(float dt)
1266 {
1267     if ((fade_k < 1.0f && fade_d > 0.0f) ||
1268         (fade_k > 0.0f && fade_d < 0.0f))
1269         fade_k += fade_d * dt;
1270
1271     if (fade_k < 0.0f)
1272     {
1273         fade_k = 0.0f;
1274         fade_d = 0.0f;
1275     }
1276     if (fade_k > 1.0f)
1277     {
1278         fade_k = 1.0f;
1279         fade_d = 0.0f;
1280     }
1281 }
1282
1283 void game_fade(float d)
1284 {
1285     fade_d = d;
1286 }
1287
1288 /*---------------------------------------------------------------------------*/
1289
1290 const char *status_to_str(int s)
1291 {
1292     switch (s)
1293     {
1294     case GAME_NONE:    return _("Aborted");
1295     case GAME_TIME:    return _("Time-out");
1296     case GAME_GOAL:    return _("Success");
1297     case GAME_FALL:    return _("Fall-out");
1298     default:           return _("Unknown");
1299     }
1300 }
1301
1302 /*---------------------------------------------------------------------------*/