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