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