KK-branch, Updated message in intro, merged current trunk (3120) with branch
[neverball] / putt / game.c
1 /*
2  * Copyright (C) 2003 Robert Kooima
3  *
4  * NEVERPUTT 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 "back.h"
23 #include "hole.h"
24 #include "hud.h"
25 #include "image.h"
26 #include "audio.h"
27 #include "solid_gl.h"
28 #include "config.h"
29
30 /*---------------------------------------------------------------------------*/
31
32 static struct s_file file;
33 static int           ball;
34
35 static float view_a;                    /* Ideal view rotation about Y axis  */
36 static float view_m;
37 static float view_ry;                   /* Angular velocity about Y axis     */
38 static float view_dy;                   /* Ideal view distance above ball    */
39 static float view_dz;                   /* Ideal view distance behind ball   */
40
41 static float view_c[3];                 /* Current view center               */
42 static float view_v[3];                 /* Current view vector               */
43 static float view_p[3];                 /* Current view position             */
44 static float view_e[3][3];              /* Current view orientation          */
45
46 static float jump_e = 1;                /* Jumping enabled flag              */
47 static float jump_b = 0;                /* Jump-in-progress flag             */
48 static float jump_dt;                   /* Jump duration                     */
49 static float jump_p[3];                 /* Jump destination                  */
50
51 /*---------------------------------------------------------------------------*/
52
53 static void view_init(void)
54 {
55     view_a  = 0.f;
56     view_m  = 0.f;
57     view_ry = 0.f;
58     view_dy = 3.f;
59     view_dz = 5.f;
60
61     view_c[0] = 0.f;
62     view_c[1] = 0.f;
63     view_c[2] = 0.f;
64
65     view_p[0] =     0.f;
66     view_p[1] = view_dy;
67     view_p[2] = view_dz;
68
69     view_e[0][0] = 1.f;
70     view_e[0][1] = 0.f;
71     view_e[0][2] = 0.f;
72     view_e[1][0] = 0.f;
73     view_e[1][1] = 1.f;
74     view_e[1][2] = 0.f;
75     view_e[2][0] = 0.f;
76     view_e[2][1] = 0.f;
77     view_e[2][2] = 1.f;
78 }
79
80 void game_init(const char *s)
81 {
82     jump_e = 1;
83     jump_b = 0;
84
85     view_init();
86     sol_load_gl(&file, config_data(s), config_get_d(CONFIG_TEXTURES),
87                                     config_get_d(CONFIG_SHADOW));
88 }
89
90 void game_free(void)
91 {
92     sol_free_gl(&file);
93 }
94
95 /*---------------------------------------------------------------------------*/
96
97 static void game_draw_vect_prim(const struct s_file *fp, GLenum mode)
98 {
99     float p[3];
100     float x[3];
101     float z[3];
102     float r;
103
104     v_cpy(p, fp->uv[ball].p);
105     v_cpy(x, view_e[0]);
106     v_cpy(z, view_e[2]);
107
108     r = fp->uv[ball].r;
109
110     glBegin(mode);
111     {
112         glColor4f(1.0f, 1.0f, 0.5f, 0.5f);
113         glVertex3f(p[0] - x[0] * r,
114                    p[1] - x[1] * r,
115                    p[2] - x[2] * r);
116
117         glColor4f(1.0f, 0.0f, 0.0f, 0.5f);
118         glVertex3f(p[0] + z[0] * view_m,
119                    p[1] + z[1] * view_m,
120                    p[2] + z[2] * view_m);
121
122         glColor4f(1.0f, 1.0f, 0.0f, 0.5f);
123         glVertex3f(p[0] + x[0] * r,
124                    p[1] + x[1] * r,
125                    p[2] + x[2] * r);
126     }
127     glEnd();
128 }
129
130 static void game_draw_vect(const struct s_file *fp)
131 {
132     if (view_m > 0.f)
133     {
134         glPushAttrib(GL_TEXTURE_BIT);
135         glPushAttrib(GL_POLYGON_BIT);
136         glPushAttrib(GL_LIGHTING_BIT);
137         glPushAttrib(GL_DEPTH_BUFFER_BIT);
138         {
139             glEnable(GL_COLOR_MATERIAL);
140             glDisable(GL_LIGHTING);
141             glDisable(GL_TEXTURE_2D);
142             glDepthMask(GL_FALSE);
143
144             glEnable(GL_DEPTH_TEST);
145             game_draw_vect_prim(fp, GL_TRIANGLES);
146
147             glDisable(GL_DEPTH_TEST);
148             game_draw_vect_prim(fp, GL_LINE_STRIP);
149         }
150         glPopAttrib();
151         glPopAttrib();
152         glPopAttrib();
153         glPopAttrib();
154     }
155 }
156
157 static void game_draw_balls(const struct s_file *fp)
158 {
159     static const GLfloat color[5][4] = {
160         { 1.0f, 1.0f, 1.0f, 0.7f },
161         { 1.0f, 0.0f, 0.0f, 1.0f },
162         { 0.0f, 1.0f, 0.0f, 1.0f },
163         { 0.0f, 0.0f, 1.0f, 1.0f },
164         { 1.0f, 1.0f, 0.0f, 1.0f },
165     };
166
167     float M[16];
168     int ui;
169
170     for (ui = curr_party(); ui > 0; ui--)
171     {
172         if (ui == ball)
173         {
174             glPushMatrix();
175             {
176                 m_basis(M, fp->uv[ui].e[0], fp->uv[ui].e[1], fp->uv[ui].e[2]);
177
178                 glTranslatef(fp->uv[ui].p[0],
179                              fp->uv[ui].p[1] + BALL_FUDGE,
180                              fp->uv[ui].p[2]);
181                 glMultMatrixf(M);
182                 glScalef(fp->uv[ui].r,
183                          fp->uv[ui].r,
184                          fp->uv[ui].r);
185
186                 glColor4fv(color[ui]);
187                 ball_draw();
188             }
189             glPopMatrix();
190         }
191         else
192         {
193             glPushMatrix();
194             {
195                 glTranslatef(fp->uv[ui].p[0],
196                              fp->uv[ui].p[1] - fp->uv[ui].r + BALL_FUDGE,
197                              fp->uv[ui].p[2]);
198                 glScalef(fp->uv[ui].r,
199                          fp->uv[ui].r,
200                          fp->uv[ui].r);
201
202                 glColor4f(color[ui][0],
203                           color[ui][1],
204                           color[ui][2], 0.5f);
205
206                 mark_draw();
207             }
208             glPopMatrix();
209         }
210     }
211 }
212
213 static void game_draw_goals(const struct s_file *fp, float rx, float ry)
214 {
215     int zi;
216
217     for (zi = 0; zi < fp->zc; zi++)
218     {
219         glPushMatrix();
220         {
221             glTranslatef(fp->zv[zi].p[0],
222                          fp->zv[zi].p[1],
223                          fp->zv[zi].p[2]);
224             flag_draw();
225         }
226         glPopMatrix();
227     }
228 }
229
230 static void game_draw_jumps(const struct s_file *fp)
231 {
232     int ji;
233
234     for (ji = 0; ji < fp->jc; ji++)
235     {
236         glPushMatrix();
237         {
238             glTranslatef(fp->jv[ji].p[0],
239                          fp->jv[ji].p[1],
240                          fp->jv[ji].p[2]);
241
242             glScalef(fp->jv[ji].r, 1.f, fp->jv[ji].r);
243             jump_draw(!jump_e);
244         }
245         glPopMatrix();
246     }
247 }
248
249 static void game_draw_swchs(const struct s_file *fp)
250 {
251     int xi;
252
253     for (xi = 0; xi < fp->xc; xi++)
254     {
255         glPushMatrix();
256         {
257             glTranslatef(fp->xv[xi].p[0],
258                          fp->xv[xi].p[1],
259                          fp->xv[xi].p[2]);
260
261             glScalef(fp->xv[xi].r, 1.f, fp->xv[xi].r);
262             swch_draw(fp->xv[xi].f, fp->xv[xi].e);
263         }
264         glPopMatrix();
265     }
266 }
267
268 /*---------------------------------------------------------------------------*/
269
270 void game_draw(int pose)
271 {
272     static const float a[4] = { 0.2f, 0.2f, 0.2f, 1.0f };
273     static const float s[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
274     static const float e[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
275     static const float h[1] = { 0.0f };
276     
277     const float light_p[4] = { 8.f, 32.f, 8.f, 1.f };
278
279     const struct s_file *fp = &file;
280
281     float fov = FOV;
282
283     if (jump_b) fov *= 2.0f * fabsf(jump_dt - 0.5f);
284
285     config_push_persp(fov, 0.1f, FAR_DIST);
286     glPushAttrib(GL_LIGHTING_BIT);
287     glPushMatrix();
288     {
289         float v[3], rx, ry;
290
291         v_sub(v, view_c, view_p);
292
293         rx = V_DEG(fatan2f(-v[1], fsqrtf(v[0] * v[0] + v[2] * v[2])));
294         ry = V_DEG(fatan2f(+v[0], -v[2]));
295
296         glTranslatef(0.f, 0.f, -v_len(v));
297         glRotatef(rx, 1.f, 0.f, 0.f);
298         glRotatef(ry, 0.f, 1.f, 0.f);
299         glTranslatef(-view_c[0], -view_c[1], -view_c[2]);
300
301         /* Center the skybox about the position of the camera. */
302
303         glPushMatrix();
304         {
305             glTranslatef(view_p[0], view_p[1], view_p[2]);
306             back_draw(0);
307         }
308         glPopMatrix();
309
310         glEnable(GL_LIGHT0);
311         glLightfv(GL_LIGHT0, GL_POSITION, light_p);
312
313         /* Draw the floor. */
314
315         sol_draw(fp);
316
317         if (config_get_d(CONFIG_SHADOW) && !pose)
318         {
319             shad_draw_set(fp->uv[ball].p, fp->uv[ball].r);
320             sol_shad(fp);
321             shad_draw_clr();
322         }
323
324         /* Draw the game elements. */
325
326         glEnable(GL_BLEND);
327         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
328
329         if (pose == 0)
330         {
331             game_draw_balls(fp);
332             game_draw_vect(fp);
333         }
334
335         glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT,   a);
336         glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR,  s);
337         glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION,  e);
338         glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, h);
339
340         game_draw_goals(fp, -rx, -ry);
341
342         glEnable(GL_COLOR_MATERIAL);
343         glDisable(GL_LIGHTING);
344         glDisable(GL_TEXTURE_2D);
345         glDepthMask(GL_FALSE);
346         {
347             game_draw_jumps(fp);
348             game_draw_swchs(fp);
349         }
350         glDepthMask(GL_TRUE);
351         glEnable(GL_TEXTURE_2D);
352         glEnable(GL_LIGHTING);
353         glDisable(GL_COLOR_MATERIAL);
354     }
355     glPopMatrix();
356     glPopAttrib();
357     config_pop_matrix();
358 }
359
360 /*---------------------------------------------------------------------------*/
361
362 void game_update_view(float dt)
363 {
364     const float y[3] = { 0.f, 1.f, 0.f };
365
366     float dy;
367     float dz;
368     float k;
369     float e[3];
370     float d[3];
371     float s = 2.f * dt;
372
373     /* Center the view about the ball. */
374
375     v_cpy(view_c, file.uv[ball].p);
376     v_inv(view_v, file.uv[ball].v);
377
378     switch (config_get_d(CONFIG_CAMERA))
379     {
380     case 2:
381         /* Camera 2: View vector is given by view angle. */
382
383         view_e[2][0] = fsinf(V_RAD(view_a));
384         view_e[2][1] = 0.f;
385         view_e[2][2] = fcosf(V_RAD(view_a));
386
387         s = 1.f;
388         break;
389
390     default:
391         /* View vector approaches the ball velocity vector. */
392
393         v_mad(e, view_v, y, v_dot(view_v, y));
394         v_inv(e, e);
395
396         k = v_dot(view_v, view_v);
397
398         v_sub(view_e[2], view_p, view_c);
399         v_mad(view_e[2], view_e[2], view_v, k * dt * 0.1f);
400     }
401
402     /* Orthonormalize the basis of the view in its new position. */
403
404     v_crs(view_e[0], view_e[1], view_e[2]);
405     v_crs(view_e[2], view_e[0], view_e[1]);
406     v_nrm(view_e[0], view_e[0]);
407     v_nrm(view_e[2], view_e[2]);
408
409     /* The current view (dy, dz) approaches the ideal (view_dy, view_dz). */
410
411     v_sub(d, view_p, view_c);
412
413     dy = v_dot(view_e[1], d);
414     dz = v_dot(view_e[2], d);
415
416     dy += (view_dy - dy) * s;
417     dz += (view_dz - dz) * s;
418
419     /* Compute the new view position. */
420
421     view_p[0] = view_p[1] = view_p[2] = 0.f;
422
423     v_mad(view_p, view_c, view_e[1], dy);
424     v_mad(view_p, view_p, view_e[2], dz);
425
426     view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
427 }
428
429 static int game_update_state(float dt)
430 {
431     static float t = 0.f;
432
433     struct s_file *fp = &file;
434     float p[3];
435
436     if (dt > 0.f)
437         t += dt;
438     else
439         t = 0.f;
440
441     /* Test for a switch. */
442
443     if (sol_swch_test(fp, ball))
444         audio_play(AUD_SWITCH, 1.f);
445
446     /* Test for a jump. */
447
448     if (jump_e == 1 && jump_b == 0 && sol_jump_test(fp, jump_p, ball) == 1)
449     {
450         jump_b  = 1;
451         jump_e  = 0;
452         jump_dt = 0.f;
453
454         audio_play(AUD_JUMP, 1.f);
455     }
456     if (jump_e == 0 && jump_b == 0 &&  sol_jump_test(fp, jump_p, ball) == 0)
457         jump_e = 1;
458
459     /* Test for fall-out. */
460
461     if (fp->uv[ball].p[1] < -10.f)
462         return GAME_FALL;
463
464     /* Test for a goal or stop. */
465
466     if (t > 1.f)
467     {
468         t = 0.f;
469
470         if (sol_goal_test(fp, p, ball))
471             return GAME_GOAL;
472         else
473             return GAME_STOP;
474     }
475
476     return GAME_NONE;
477 }
478
479 /*
480  * On  most  hardware, rendering  requires  much  more  computing power  than
481  * physics.  Since  physics takes less time  than graphics, it  make sense to
482  * detach  the physics update  time step  from the  graphics frame  rate.  By
483  * performing multiple physics updates for  each graphics update, we get away
484  * with higher quality physics with little impact on overall performance.
485  *
486  * Toward this  end, we establish a  baseline maximum physics  time step.  If
487  * the measured  frame time  exceeds this  maximum, we cut  the time  step in
488  * half, and  do two updates.  If THIS  time step exceeds the  maximum, we do
489  * four updates.  And  so on.  In this way, the physics  system is allowed to
490  * seek an optimal update rate independent of, yet in integral sync with, the
491  * graphics frame rate.
492  */
493
494 int game_step(const float g[3], float dt)
495 {
496     struct s_file *fp = &file;
497
498     static float s = 0.f;
499     static float t = 0.f;
500
501     float d = 0.f;
502     float b = 0.f;
503     float st = 0.f;
504     int i, n = 1, m = 0;
505
506     s = (7.f * s + dt) / 8.f;
507     t = s;
508
509     if (jump_b)
510     {
511         jump_dt += dt;
512
513         /* Handle a jump. */
514
515         if (0.5 < jump_dt)
516         {
517             fp->uv[ball].p[0] = jump_p[0];
518             fp->uv[ball].p[1] = jump_p[1];
519             fp->uv[ball].p[2] = jump_p[2];
520         }
521         if (1.f < jump_dt)
522             jump_b = 0;
523     }
524     else
525     {
526         /* Run the sim. */
527
528         while (t > MAX_DT && n < MAX_DN)
529         {
530             t /= 2;
531             n *= 2;
532         }
533
534         for (i = 0; i < n; i++)
535         {
536             d = sol_step(fp, g, t, ball, &m);
537
538             if (b < d)
539                 b = d;
540             if (m)
541                 st += t;
542         }
543
544         /* Mix the sound of a ball bounce. */
545
546         if (b > 0.5)
547             audio_play(AUD_BUMP, (float) (b - 0.5) * 2.0f);
548     }
549
550     game_update_view(dt);
551     return game_update_state(st);
552 }
553
554 void game_putt(void)
555 {
556     /*
557      * HACK: The BALL_FUDGE here  guarantees that a putt doesn't drive
558      * the ball  too directly down  toward a lump,  triggering rolling
559      * friction too early and stopping the ball prematurely.
560      */
561
562     file.uv[ball].v[0] = -4.f * view_e[2][0] * view_m;
563     file.uv[ball].v[1] = -4.f * view_e[2][1] * view_m + BALL_FUDGE;
564     file.uv[ball].v[2] = -4.f * view_e[2][2] * view_m;
565
566     view_m = 0.f;
567 }
568
569 /*---------------------------------------------------------------------------*/
570
571 void game_set_rot(int d)
572 {
573     view_a += (float) (30.f * d) / config_get_d(CONFIG_MOUSE_SENSE);
574 }
575
576 void game_clr_mag(void)
577 {
578     view_m = 1.f;
579 }
580
581 void game_set_mag(int d)
582 {
583     view_m -= (float) (1.f * d) / config_get_d(CONFIG_MOUSE_SENSE);
584
585     if (view_m < 0.25)
586         view_m = 0.25;
587 }
588
589 void game_set_fly(float k)
590 {
591     struct s_file *fp = &file;
592
593     float  x[3] = { 1.f, 0.f, 0.f };
594     float  y[3] = { 0.f, 1.f, 0.f };
595     float  z[3] = { 0.f, 0.f, 1.f };
596     float c0[3] = { 0.f, 0.f, 0.f };
597     float p0[3] = { 0.f, 0.f, 0.f };
598     float c1[3] = { 0.f, 0.f, 0.f };
599     float p1[3] = { 0.f, 0.f, 0.f };
600     float  v[3];
601
602     v_cpy(view_e[0], x);
603     v_cpy(view_e[1], y);
604     v_sub(view_e[2], fp->uv[ball].p, fp->zv[0].p);
605
606     if (fabs(v_dot(view_e[1], view_e[2])) > 0.999)
607         v_cpy(view_e[2], z);
608
609     v_crs(view_e[0], view_e[1], view_e[2]);
610     v_crs(view_e[2], view_e[0], view_e[1]);
611
612     v_nrm(view_e[0], view_e[0]);
613     v_nrm(view_e[2], view_e[2]);
614
615     /* k = 0.0 view is at the ball. */
616
617     if (fp->uc > 0)
618     {
619         v_cpy(c0, fp->uv[ball].p);
620         v_cpy(p0, fp->uv[ball].p);
621     }
622
623     v_mad(p0, p0, view_e[1], view_dy);
624     v_mad(p0, p0, view_e[2], view_dz);
625
626     /* k = +1.0 view is s_view 0 */
627
628     if (k >= 0 && fp->wc > 0)
629     {
630         v_cpy(p1, fp->wv[0].p);
631         v_cpy(c1, fp->wv[0].q);
632     }
633
634     /* k = -1.0 view is s_view 1 */
635
636     if (k <= 0 && fp->wc > 1)
637     {
638         v_cpy(p1, fp->wv[1].p);
639         v_cpy(c1, fp->wv[1].q);
640     }
641
642     /* Interpolate the views. */
643
644     v_sub(v, p1, p0);
645     v_mad(view_p, p0, v, k * k);
646
647     v_sub(v, c1, c0);
648     v_mad(view_c, c0, v, k * k);
649
650     /* Orthonormalize the view basis. */
651
652     v_sub(view_e[2], view_p, view_c);
653     v_crs(view_e[0], view_e[1], view_e[2]);
654     v_crs(view_e[2], view_e[0], view_e[1]);
655     v_nrm(view_e[0], view_e[0]);
656     v_nrm(view_e[2], view_e[2]);
657
658     view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
659 }
660
661 void game_ball(int i)
662 {
663     int ui;
664
665     ball = i;
666
667     jump_e = 1;
668     jump_b = 0;
669
670     for (ui = 0; ui < file.uc; ui++)
671     {
672         file.uv[ui].v[0] = 0.f;
673         file.uv[ui].v[1] = 0.f;
674         file.uv[ui].v[2] = 0.f;
675
676         file.uv[ui].w[0] = 0.f;
677         file.uv[ui].w[1] = 0.f;
678         file.uv[ui].w[2] = 0.f;
679     }
680 }
681
682 void game_get_pos(float p[3], float e[3][3])
683 {
684     v_cpy(p,    file.uv[ball].p);
685     v_cpy(e[0], file.uv[ball].e[0]);
686     v_cpy(e[1], file.uv[ball].e[1]);
687     v_cpy(e[2], file.uv[ball].e[2]);
688 }
689
690 void game_set_pos(float p[3], float e[3][3])
691 {
692     v_cpy(file.uv[ball].p,    p);
693     v_cpy(file.uv[ball].e[0], e[0]);
694     v_cpy(file.uv[ball].e[1], e[1]);
695     v_cpy(file.uv[ball].e[2], e[2]);
696 }
697
698 /*---------------------------------------------------------------------------*/
699