Import from neverball-1.4.0.tar.gz
[neverball-archive.git] / putt / game.c
blob92bd34768c81334d4b8d216853599c7624938617
1 /*
2 * Copyright (C) 2003 Robert Kooima
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.
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.
15 #include <SDL.h>
16 #include <math.h>
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"
30 /*---------------------------------------------------------------------------*/
32 static struct s_file file;
33 static short ball;
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 */
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 */
46 static int swch_e = 1; /* Switching enabled flag */
47 static float jump_e = 1; /* Jumping enabled flag */
48 static float jump_b = 0; /* Jump-in-progress flag */
49 static float jump_dt; /* Jump duration */
50 static float jump_p[3]; /* Jump destination */
52 /*---------------------------------------------------------------------------*/
54 static void view_init(void)
56 view_a = 0.f;
57 view_m = 0.f;
58 view_ry = 0.f;
59 view_dy = 3.f;
60 view_dz = 5.f;
62 view_c[0] = 0.f;
63 view_c[1] = 0.f;
64 view_c[2] = 0.f;
66 view_p[0] = 0.f;
67 view_p[1] = view_dy;
68 view_p[2] = view_dz;
70 view_e[0][0] = 1.f;
71 view_e[0][1] = 0.f;
72 view_e[0][2] = 0.f;
73 view_e[1][0] = 0.f;
74 view_e[1][1] = 1.f;
75 view_e[1][2] = 0.f;
76 view_e[2][0] = 0.f;
77 view_e[2][1] = 0.f;
78 view_e[2][2] = 1.f;
81 void game_init(const char *s)
83 jump_e = 1;
84 jump_b = 0;
86 view_init();
87 sol_load(&file, config_data(s), config_get_d(CONFIG_TEXTURES),
88 config_get_d(CONFIG_SHADOW));
91 void game_free(void)
93 sol_free(&file);
96 /*---------------------------------------------------------------------------*/
98 static void game_draw_vect_prim(const struct s_file *fp, GLenum mode)
100 float p[3];
101 float x[3];
102 float z[3];
103 float r;
105 v_cpy(p, fp->uv[ball].p);
106 v_cpy(x, view_e[0]);
107 v_cpy(z, view_e[2]);
109 r = fp->uv[ball].r;
111 glBegin(mode);
113 glColor4f(1.0f, 1.0f, 0.5f, 0.5f);
114 glVertex3f(p[0] - x[0] * r,
115 p[1] - x[1] * r,
116 p[2] - x[2] * r);
118 glColor4f(1.0f, 0.0f, 0.0f, 0.5f);
119 glVertex3f(p[0] + z[0] * view_m,
120 p[1] + z[1] * view_m,
121 p[2] + z[2] * view_m);
123 glColor4f(1.0f, 1.0f, 0.0f, 0.5f);
124 glVertex3f(p[0] + x[0] * r,
125 p[1] + x[1] * r,
126 p[2] + x[2] * r);
128 glEnd();
131 static void game_draw_vect(const struct s_file *fp)
133 if (view_m > 0.f)
135 glPushAttrib(GL_TEXTURE_BIT);
136 glPushAttrib(GL_POLYGON_BIT);
137 glPushAttrib(GL_LIGHTING_BIT);
138 glPushAttrib(GL_DEPTH_BUFFER_BIT);
140 glEnable(GL_COLOR_MATERIAL);
141 glDisable(GL_LIGHTING);
142 glDisable(GL_TEXTURE_2D);
143 glDepthMask(GL_FALSE);
145 glEnable(GL_DEPTH_TEST);
146 game_draw_vect_prim(fp, GL_TRIANGLES);
148 glDisable(GL_DEPTH_TEST);
149 game_draw_vect_prim(fp, GL_LINE_STRIP);
151 glPopAttrib();
152 glPopAttrib();
153 glPopAttrib();
154 glPopAttrib();
158 static void game_draw_balls(const struct s_file *fp)
160 static const GLfloat color[5][4] = {
161 { 1.0f, 1.0f, 1.0f, 0.7f },
162 { 1.0f, 0.0f, 0.0f, 1.0f },
163 { 0.0f, 1.0f, 0.0f, 1.0f },
164 { 0.0f, 0.0f, 1.0f, 1.0f },
165 { 1.0f, 1.0f, 0.0f, 1.0f },
168 float M[16];
169 int ui;
171 for (ui = curr_party(); ui > 0; ui--)
173 if (ui == ball)
175 glPushMatrix();
177 m_basis(M, fp->uv[ui].e[0], fp->uv[ui].e[1], fp->uv[ui].e[2]);
179 glTranslatef(fp->uv[ui].p[0],
180 fp->uv[ui].p[1] + BALL_FUDGE,
181 fp->uv[ui].p[2]);
182 glMultMatrixf(M);
183 glScalef(fp->uv[ui].r,
184 fp->uv[ui].r,
185 fp->uv[ui].r);
187 glColor4fv(color[ui]);
189 ball_draw();
191 glPopMatrix();
193 else
195 glPushMatrix();
197 glTranslatef(fp->uv[ui].p[0],
198 fp->uv[ui].p[1] - fp->uv[ui].r + BALL_FUDGE,
199 fp->uv[ui].p[2]);
200 glScalef(fp->uv[ui].r,
201 fp->uv[ui].r,
202 fp->uv[ui].r);
204 glColor4f(color[ui][0],
205 color[ui][1],
206 color[ui][2], 0.5f);
208 mark_draw();
210 glPopMatrix();
215 static void game_draw_goals(const struct s_file *fp, float rx, float ry)
217 int zi;
219 for (zi = 0; zi < fp->zc; zi++)
221 glPushMatrix();
223 glTranslatef(fp->zv[zi].p[0],
224 fp->zv[zi].p[1],
225 fp->zv[zi].p[2]);
226 flag_draw();
228 glPopMatrix();
232 static void game_draw_jumps(const struct s_file *fp)
234 int ji;
236 for (ji = 0; ji < fp->jc; ji++)
238 glPushMatrix();
240 glTranslatef(fp->jv[ji].p[0],
241 fp->jv[ji].p[1],
242 fp->jv[ji].p[2]);
244 glScalef(fp->jv[ji].r, 1.f, fp->jv[ji].r);
245 jump_draw();
247 glPopMatrix();
251 static void game_draw_swchs(const struct s_file *fp)
253 int xi;
255 for (xi = 0; xi < fp->xc; xi++)
257 glPushMatrix();
259 glTranslatef(fp->xv[xi].p[0],
260 fp->xv[xi].p[1],
261 fp->xv[xi].p[2]);
263 glScalef(fp->xv[xi].r, 1.f, fp->xv[xi].r);
264 swch_draw(fp->xv[xi].f);
266 glPopMatrix();
270 /*---------------------------------------------------------------------------*/
272 void game_draw(int pose)
274 const float light_p[4] = { 8.f, 32.f, 8.f, 1.f };
276 const struct s_file *fp = &file;
278 float fov = FOV;
280 if (jump_b) fov *= 2.0f * fabsf(jump_dt - 0.5f);
282 config_push_persp(fov, 0.1f, FAR_DIST);
283 glPushAttrib(GL_LIGHTING_BIT);
284 glPushMatrix();
286 float v[3], rx, ry;
288 v_sub(v, view_c, view_p);
290 rx = V_DEG(fatan2f(-v[1], fsqrtf(v[0] * v[0] + v[2] * v[2])));
291 ry = V_DEG(fatan2f(+v[0], -v[2]));
293 glTranslatef(0.f, 0.f, -v_len(v));
294 glRotatef(rx, 1.f, 0.f, 0.f);
295 glRotatef(ry, 0.f, 1.f, 0.f);
296 glTranslatef(-view_c[0], -view_c[1], -view_c[2]);
298 /* Center the skybox about the position of the camera. */
300 glPushMatrix();
302 glTranslatef(view_p[0], view_p[1], view_p[2]);
303 back_draw(0);
305 glPopMatrix();
307 glEnable(GL_LIGHT0);
308 glLightfv(GL_LIGHT0, GL_POSITION, light_p);
310 /* Draw the floor. */
312 sol_draw(fp);
314 if (config_get_d(CONFIG_SHADOW) && !pose)
316 shad_draw_set(fp->uv[ball].p, fp->uv[ball].r);
317 sol_shad(fp);
318 shad_draw_clr();
321 /* Draw the game elements. */
323 glEnable(GL_BLEND);
324 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
326 if (pose == 0)
328 game_draw_balls(fp);
329 game_draw_vect(fp);
332 game_draw_goals(fp, -rx, -ry);
333 game_draw_jumps(fp);
334 game_draw_swchs(fp);
336 glPopMatrix();
337 glPopAttrib();
338 config_pop_matrix();
341 /*---------------------------------------------------------------------------*/
343 void game_update_view(float dt)
345 const float y[3] = { 0.f, 1.f, 0.f };
347 float dy;
348 float dz;
349 float k;
350 float e[3];
351 float d[3];
352 float s = 2.f * dt;
354 /* Center the view about the ball. */
356 v_cpy(view_c, file.uv[ball].p);
357 v_inv(view_v, file.uv[ball].v);
359 switch (config_get_d(CONFIG_CAMERA))
361 case 2:
362 /* Camera 2: View vector is given by view angle. */
364 view_e[2][0] = fsinf(V_RAD(view_a));
365 view_e[2][1] = 0.f;
366 view_e[2][2] = fcosf(V_RAD(view_a));
368 s = 1.f;
369 break;
371 default:
372 /* View vector approaches the ball velocity vector. */
374 v_mad(e, view_v, y, v_dot(view_v, y));
375 v_inv(e, e);
377 k = v_dot(view_v, view_v);
379 v_sub(view_e[2], view_p, view_c);
380 v_mad(view_e[2], view_e[2], view_v, k * dt * 0.1f);
383 /* Orthonormalize the basis of the view in its new position. */
385 v_crs(view_e[0], view_e[1], view_e[2]);
386 v_crs(view_e[2], view_e[0], view_e[1]);
387 v_nrm(view_e[0], view_e[0]);
388 v_nrm(view_e[2], view_e[2]);
390 /* The current view (dy, dz) approaches the ideal (view_dy, view_dz). */
392 v_sub(d, view_p, view_c);
394 dy = v_dot(view_e[1], d);
395 dz = v_dot(view_e[2], d);
397 dy += (view_dy - dy) * s;
398 dz += (view_dz - dz) * s;
400 /* Compute the new view position. */
402 view_p[0] = view_p[1] = view_p[2] = 0.f;
404 v_mad(view_p, view_c, view_e[1], dy);
405 v_mad(view_p, view_p, view_e[2], dz);
407 view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
410 static int game_update_state(float dt)
412 static float t = 0.f;
414 struct s_file *fp = &file;
415 float p[3];
416 int e = swch_e;
418 if (dt > 0.f)
419 t += dt;
420 else
421 t = 0.f;
423 /* Test for a switch. */
425 if ((swch_e = sol_swch_test(fp, swch_e, ball)) != e && e)
426 audio_play(AUD_SWITCH, 1.f);
428 /* Test for a jump. */
430 if (jump_e == 1 && jump_b == 0 && sol_jump_test(fp, jump_p, ball) == 1)
432 jump_b = 1;
433 jump_e = 0;
434 jump_dt = 0.f;
436 audio_play(AUD_JUMP, 1.f);
438 if (jump_e == 0 && jump_b == 0 && sol_jump_test(fp, jump_p, ball) == 0)
439 jump_e = 1;
441 /* Test for fall-out. */
443 if (fp->uv[ball].p[1] < -10.f)
444 return GAME_FALL;
446 /* Test for a goal or stop. */
448 if (t > 1.f)
450 t = 0.f;
452 if (sol_goal_test(fp, p, ball))
453 return GAME_GOAL;
454 else
455 return GAME_STOP;
458 return GAME_NONE;
462 * On most hardware, rendering requires much more computing power than
463 * physics. Since physics takes less time than graphics, it make sense to
464 * detach the physics update time step from the graphics frame rate. By
465 * performing multiple physics updates for each graphics update, we get away
466 * with higher quality physics with little impact on overall performance.
468 * Toward this end, we establish a baseline maximum physics time step. If
469 * the measured frame time exceeds this maximum, we cut the time step in
470 * half, and do two updates. If THIS time step exceeds the maximum, we do
471 * four updates. And so on. In this way, the physics system is allowed to
472 * seek an optimal update rate independant of, yet in integral sync with, the
473 * graphics frame rate.
476 int game_step(const float g[3], float dt)
478 struct s_file *fp = &file;
480 static float s = 0.f;
481 static float t = 0.f;
483 float d = 0.f;
484 float b = 0.f;
485 float st = 0.f;
486 int i, n = 1, m = 0;
488 s = (7.f * s + dt) / 8.f;
489 t = s;
491 if (jump_b)
493 jump_dt += dt;
495 /* Handle a jump. */
497 if (0.5 < jump_dt)
499 fp->uv[ball].p[0] = jump_p[0];
500 fp->uv[ball].p[1] = jump_p[1];
501 fp->uv[ball].p[2] = jump_p[2];
503 if (1.f < jump_dt)
504 jump_b = 0;
506 else
508 /* Run the sim. */
510 while (t > MAX_DT && n < MAX_DN)
512 t /= 2;
513 n *= 2;
516 for (i = 0; i < n; i++)
518 d = sol_step(fp, g, t, ball, &m);
520 if (b < d)
521 b = d;
522 if (m)
523 st += t;
526 /* Mix the sound of a ball bounce. */
528 if (b > 0.5)
529 audio_play(AUD_BUMP, (float) (b - 0.5) * 2.0f);
532 game_update_view(dt);
533 return game_update_state(st);
536 void game_putt(void)
539 * HACK: The BALL_FUDGE here guarantees that a putt doesn't drive
540 * the ball too directly down toward a lump, triggering rolling
541 * friction too early and stopping the ball prematurely.
544 file.uv[ball].v[0] = -4.f * view_e[2][0] * view_m;
545 file.uv[ball].v[1] = -4.f * view_e[2][1] * view_m + BALL_FUDGE;
546 file.uv[ball].v[2] = -4.f * view_e[2][2] * view_m;
548 view_m = 0.f;
551 /*---------------------------------------------------------------------------*/
553 void game_set_rot(int d)
555 view_a += (float) (30.f * d) / config_get_d(CONFIG_MOUSE_SENSE);
558 void game_clr_mag(void)
560 view_m = 1.f;
563 void game_set_mag(int d)
565 view_m -= (float) (1.f * d) / config_get_d(CONFIG_MOUSE_SENSE);
567 if (view_m < 0.25)
568 view_m = 0.25;
571 void game_set_fly(float k)
573 struct s_file *fp = &file;
575 float x[3] = { 1.f, 0.f, 0.f };
576 float y[3] = { 0.f, 1.f, 0.f };
577 float z[3] = { 0.f, 0.f, 1.f };
578 float c0[3] = { 0.f, 0.f, 0.f };
579 float p0[3] = { 0.f, 0.f, 0.f };
580 float c1[3] = { 0.f, 0.f, 0.f };
581 float p1[3] = { 0.f, 0.f, 0.f };
582 float v[3];
584 v_cpy(view_e[0], x);
585 v_cpy(view_e[1], y);
586 v_sub(view_e[2], fp->uv[ball].p, fp->zv[0].p);
588 if (fabs(v_dot(view_e[1], view_e[2])) > 0.999)
589 v_cpy(view_e[2], z);
591 v_crs(view_e[0], view_e[1], view_e[2]);
592 v_crs(view_e[2], view_e[0], view_e[1]);
594 v_nrm(view_e[0], view_e[0]);
595 v_nrm(view_e[2], view_e[2]);
597 /* k = 0.0 view is at the ball. */
599 if (fp->uc > 0)
601 v_cpy(c0, fp->uv[ball].p);
602 v_cpy(p0, fp->uv[ball].p);
605 v_mad(p0, p0, view_e[1], view_dy);
606 v_mad(p0, p0, view_e[2], view_dz);
608 /* k = +1.0 view is s_view 0 */
610 if (k >= 0 && fp->wc > 0)
612 v_cpy(p1, fp->wv[0].p);
613 v_cpy(c1, fp->wv[0].q);
616 /* k = -1.0 view is s_view 1 */
618 if (k <= 0 && fp->wc > 1)
620 v_cpy(p1, fp->wv[1].p);
621 v_cpy(c1, fp->wv[1].q);
624 /* Interpolate the views. */
626 v_sub(v, p1, p0);
627 v_mad(view_p, p0, v, k * k);
629 v_sub(v, c1, c0);
630 v_mad(view_c, c0, v, k * k);
632 /* Orthonormalize the view basis. */
634 v_sub(view_e[2], view_p, view_c);
635 v_crs(view_e[0], view_e[1], view_e[2]);
636 v_crs(view_e[2], view_e[0], view_e[1]);
637 v_nrm(view_e[0], view_e[0]);
638 v_nrm(view_e[2], view_e[2]);
640 view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
643 void game_ball(int i)
645 int ui;
647 ball = i;
649 jump_e = 1;
650 jump_b = 0;
652 for (ui = 0; ui < file.uc; ui++)
654 file.uv[ui].v[0] = 0.f;
655 file.uv[ui].v[1] = 0.f;
656 file.uv[ui].v[2] = 0.f;
658 file.uv[ui].w[0] = 0.f;
659 file.uv[ui].w[1] = 0.f;
660 file.uv[ui].w[2] = 0.f;
664 void game_get_pos(float p[3], float e[3][3])
666 v_cpy(p, file.uv[ball].p);
667 v_cpy(e[0], file.uv[ball].e[0]);
668 v_cpy(e[1], file.uv[ball].e[1]);
669 v_cpy(e[2], file.uv[ball].e[2]);
672 void game_set_pos(float p[3], float e[3][3])
674 v_cpy(file.uv[ball].p, p);
675 v_cpy(file.uv[ball].e[0], e[0]);
676 v_cpy(file.uv[ball].e[1], e[1]);
677 v_cpy(file.uv[ball].e[2], e[2]);
680 /*---------------------------------------------------------------------------*/