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