Converted mark GL into SOL.
[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 "back.h"
24 #include "hole.h"
25 #include "hud.h"
26 #include "image.h"
27 #include "audio.h"
28 #include "config.h"
29 #include "video.h"
30
31 #include "solid_draw.h"
32 #include "solid_sim.h"
33 #include "solid_all.h"
34
35 /*---------------------------------------------------------------------------*/
36
37 static struct s_full file;
38 static int           ball;
39
40 static float view_a;                    /* Ideal view rotation about Y axis  */
41 static float view_m;
42 static float view_ry;                   /* Angular velocity about Y axis     */
43 static float view_dy;                   /* Ideal view distance above ball    */
44 static float view_dz;                   /* Ideal view distance behind ball   */
45
46 static float view_c[3];                 /* Current view center               */
47 static float view_v[3];                 /* Current view vector               */
48 static float view_p[3];                 /* Current view position             */
49 static float view_e[3][3];              /* Current view orientation          */
50
51 static float jump_e = 1;                /* Jumping enabled flag              */
52 static float jump_b = 0;                /* Jump-in-progress flag             */
53 static float jump_dt;                   /* Jump duration                     */
54 static float jump_p[3];                 /* Jump destination                  */
55
56 static float idle_t;                    /* Idling timeout                    */
57
58 /*---------------------------------------------------------------------------*/
59
60 static void view_init(void)
61 {
62     view_a  = 0.f;
63     view_m  = 0.f;
64     view_ry = 0.f;
65     view_dy = 3.f;
66     view_dz = 5.f;
67
68     view_c[0] = 0.f;
69     view_c[1] = 0.f;
70     view_c[2] = 0.f;
71
72     view_p[0] =     0.f;
73     view_p[1] = view_dy;
74     view_p[2] = view_dz;
75
76     view_e[0][0] = 1.f;
77     view_e[0][1] = 0.f;
78     view_e[0][2] = 0.f;
79     view_e[1][0] = 0.f;
80     view_e[1][1] = 1.f;
81     view_e[1][2] = 0.f;
82     view_e[2][0] = 0.f;
83     view_e[2][1] = 0.f;
84     view_e[2][2] = 1.f;
85 }
86
87 void game_init(const char *s)
88 {
89     int i;
90
91     jump_e = 1;
92     jump_b = 0;
93
94     idle_t = 1.0f;
95
96     view_init();
97     sol_load_full(&file, s, config_get_d(CONFIG_SHADOW));
98     sol_init_sim(&file.vary);
99
100     for (i = 0; i < file.base.dc; i++)
101     {
102         const char *k = file.base.av + file.base.dv[i].ai;
103         const char *v = file.base.av + file.base.dv[i].aj;
104
105         if (strcmp(k, "idle") == 0)
106         {
107             sscanf(v, "%f", &idle_t);
108
109             if (idle_t < 1.0f)
110                 idle_t = 1.0f;
111         }
112     }
113 }
114
115 void game_free(void)
116 {
117     sol_quit_sim();
118     sol_free_full(&file);
119 }
120
121 /*---------------------------------------------------------------------------*/
122
123 static void game_draw_vect_prim(const struct s_vary *fp, GLenum mode)
124 {
125     float p[3];
126     float x[3];
127     float z[3];
128     float r;
129
130     v_cpy(p, fp->uv[ball].p);
131     v_cpy(x, view_e[0]);
132     v_cpy(z, view_e[2]);
133
134     r = fp->uv[ball].r;
135
136     glBegin(mode);
137     {
138         glColor4f(1.0f, 1.0f, 0.5f, 0.5f);
139         glVertex3f(p[0] - x[0] * r,
140                    p[1] - x[1] * r,
141                    p[2] - x[2] * r);
142
143         glColor4f(1.0f, 0.0f, 0.0f, 0.5f);
144         glVertex3f(p[0] + z[0] * view_m,
145                    p[1] + z[1] * view_m,
146                    p[2] + z[2] * view_m);
147
148         glColor4f(1.0f, 1.0f, 0.0f, 0.5f);
149         glVertex3f(p[0] + x[0] * r,
150                    p[1] + x[1] * r,
151                    p[2] + x[2] * r);
152     }
153     glEnd();
154 }
155
156 static void game_draw_vect(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 }
182
183 static void game_draw_balls(const struct s_vary *fp,
184                             const float *bill_M, float t)
185 {
186     static const GLfloat color[5][4] = {
187         { 1.0f, 1.0f, 1.0f, 0.7f },
188         { 1.0f, 0.0f, 0.0f, 1.0f },
189         { 0.0f, 1.0f, 0.0f, 1.0f },
190         { 0.0f, 0.0f, 1.0f, 1.0f },
191         { 1.0f, 1.0f, 0.0f, 1.0f },
192     };
193
194     int ui;
195
196     glEnable(GL_COLOR_MATERIAL);
197
198     for (ui = curr_party(); ui > 0; ui--)
199     {
200         if (ui == ball)
201         {
202             float ball_M[16];
203             float pend_M[16];
204
205             m_basis(ball_M, fp->uv[ui].e[0], fp->uv[ui].e[1], fp->uv[ui].e[2]);
206             m_basis(pend_M, fp->uv[ui].E[0], fp->uv[ui].E[1], fp->uv[ui].E[2]);
207
208             glPushMatrix();
209             {
210                 glTranslatef(fp->uv[ui].p[0],
211                              fp->uv[ui].p[1] + BALL_FUDGE,
212                              fp->uv[ui].p[2]);
213                 glScalef(fp->uv[ui].r,
214                          fp->uv[ui].r,
215                          fp->uv[ui].r);
216
217                 glColor4fv(color[ui]);
218                 ball_draw(ball_M, pend_M, bill_M, t);
219             }
220             glPopMatrix();
221         }
222         else
223         {
224             glPushMatrix();
225             {
226                 glTranslatef(fp->uv[ui].p[0],
227                              fp->uv[ui].p[1] - fp->uv[ui].r + BALL_FUDGE,
228                              fp->uv[ui].p[2]);
229                 glScalef(fp->uv[ui].r,
230                          fp->uv[ui].r,
231                          fp->uv[ui].r);
232
233                 glColor4f(color[ui][0],
234                           color[ui][1],
235                           color[ui][2], 0.5f);
236
237                 mark_draw();
238             }
239             glPopMatrix();
240         }
241     }
242
243     glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
244     glDisable(GL_COLOR_MATERIAL);
245 }
246
247 static void game_draw_goals(const struct s_base *fp)
248 {
249     int zi;
250
251     for (zi = 0; zi < fp->zc; zi++)
252     {
253         glPushMatrix();
254         {
255             glTranslatef(fp->zv[zi].p[0],
256                          fp->zv[zi].p[1],
257                          fp->zv[zi].p[2]);
258             flag_draw();
259         }
260         glPopMatrix();
261     }
262 }
263
264 static void game_draw_jumps(const struct s_base *fp)
265 {
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(!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