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