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