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