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.
31 /*---------------------------------------------------------------------------*/
33 static struct s_file file;
36 static float view_a; /* Ideal view rotation about Y axis */
38 static float view_ry; /* Angular velocity about Y axis */
39 static float view_dy; /* Ideal view distance above ball */
40 static float view_dz; /* Ideal view distance behind ball */
42 static float view_c[3]; /* Current view center */
43 static float view_v[3]; /* Current view vector */
44 static float view_p[3]; /* Current view position */
45 static float view_e[3][3]; /* Current view orientation */
47 static float jump_e = 1; /* Jumping enabled flag */
48 static float jump_b = 0; /* Jump-in-progress flag */
49 static int jump_u = 0; /* Which ball is jumping? */
50 static float jump_dt; /* Jump duration */
51 static float jump_p[3]; /* Jump destination */
53 /*---------------------------------------------------------------------------*/
55 static void view_init(void)
82 void game_init(const char *s)
89 sol_load_gl(&file, config_data(s), config_get_d(CONFIG_TEXTURES),
90 config_get_d(CONFIG_SHADOW));
98 /*---------------------------------------------------------------------------*/
100 int game_check_balls(struct s_file *fp)
102 float z[3] = {0.0f, 0.0f, 0.0f};
105 for (i = 1; i < fp->uc && config_get_d(CONFIG_BALL_COLLISIONS); i++)
107 struct s_ball *up = fp->uv + i;
110 * If a ball falls out, return the ball to the camera marker
111 * and reset the play state for fair play
113 if (i != ball && up->p[1] < -10.f && (up->p[1] > -199.9f || up->p[1] < -599.9f))
116 v_cpy(up->p, fp->uv->p);
121 if (i == ball && up->p[1] < -30.0f)
123 v_cpy(up->p, fp->uv->p);
129 * If an OTHER ball stops in a hole, mark it as done
130 * and drop it -200.0 units to allow room for more balls
132 if (i != ball && !(v_len(up->v) > 0.0f))
134 const float *ball_p = up->p;
135 const float ball_r = up->r;
137 for (zi = 0; zi < fp->zc; zi++)
141 r[0] = ball_p[0] - fp->zv[zi].p[0];
142 r[1] = ball_p[2] - fp->zv[zi].p[2];
145 if (v_len(r) < fp->zv[zi].r * 1.1 - ball_r &&
146 ball_p[1] > fp->zv[zi].p[1] &&
147 ball_p[1] < fp->zv[zi].p[1] + GOAL_HEIGHT / 2)
158 * Check for intesecting balls.
159 * If there are any, reset the proper
162 for (j = i + 1; j < fp->uc && config_get_d(CONFIG_BALL_COLLISIONS); j++)
164 struct s_ball *u2p = fp->uv + j;
166 v_sub(d, up->p, u2p->p);
167 if (v_len(up->v) > 0.005f || v_len(u2p->v) > 0.005f)
169 if (v_len(d) < (fsqrtf((up->r + u2p->r) * (up->r + u2p->r))) * 1.0f && i != ball)
171 else if (v_len(d) < (fsqrtf((up->r + u2p->r) * (up->r + u2p->r)) * 1.0f))
176 for (i = 0; i < fp->yc; i++)
178 struct s_ball *yp = fp->yv + i;
180 if (yp->p[1] < -20.0f && yp->n)
187 if (!(v_len(yp->v) > 0.0f))
189 const float *ball_p = yp->p;
190 const float ball_r = yp->r;
192 for (zi = 0; zi < fp->zc; zi++)
196 r[0] = ball_p[0] - fp->zv[zi].p[0];
197 r[1] = ball_p[2] - fp->zv[zi].p[2];
200 if (v_len(r) < fp->zv[zi].r * 1.1 - ball_r &&
201 ball_p[1] > fp->zv[zi].p[1] &&
202 ball_p[1] < fp->zv[zi].p[1] + GOAL_HEIGHT / 2)
215 static void game_draw_vect_prim(const struct s_file *fp, GLenum mode)
222 v_cpy(p, fp->uv[ball].p);
230 glColor4f(1.0f, 1.0f, 0.5f, 0.5f);
231 glVertex3f(p[0] - x[0] * r,
235 glColor4f(1.0f, 0.0f, 0.0f, 0.5f);
236 glVertex3f(p[0] + z[0] * view_m,
237 p[1] + z[1] * view_m,
238 p[2] + z[2] * view_m);
240 glColor4f(1.0f, 1.0f, 0.0f, 0.5f);
241 glVertex3f(p[0] + x[0] * r,
248 static void game_draw_vect(const struct s_file *fp)
252 glPushAttrib(GL_TEXTURE_BIT);
253 glPushAttrib(GL_POLYGON_BIT);
254 glPushAttrib(GL_LIGHTING_BIT);
255 glPushAttrib(GL_DEPTH_BUFFER_BIT);
257 glEnable(GL_COLOR_MATERIAL);
258 glDisable(GL_LIGHTING);
259 glDisable(GL_TEXTURE_2D);
260 glDepthMask(GL_FALSE);
262 glEnable(GL_DEPTH_TEST);
263 game_draw_vect_prim(fp, GL_TRIANGLES);
265 glDisable(GL_DEPTH_TEST);
266 game_draw_vect_prim(fp, GL_LINE_STRIP);
275 static void game_draw_balls(const struct s_file *fp,
276 const float *bill_M, float t)
278 static const GLfloat color[6][4] = {
279 { 1.0f, 1.0f, 1.0f, 0.7f },
280 { 1.0f, 0.0f, 0.0f, 1.0f },
281 { 0.0f, 1.0f, 0.0f, 1.0f },
282 { 0.0f, 0.0f, 1.0f, 1.0f },
283 { 1.0f, 1.0f, 0.0f, 1.0f },
284 { 0.1f, 0.1f, 0.1f, 1.0f },
289 for (yi = 0; yi < fp->yc; yi++)
293 if (!config_get_d(CONFIG_BALL_COLLISIONS) && fp->yv[yi].c)
296 m_basis(M, fp->yv[yi].e[0], fp->yv[yi].e[1], fp->yv[yi].e[2]);
300 glTranslatef(fp->yv[yi].p[0],
301 fp->yv[yi].p[1] + BALL_FUDGE,
304 glScalef(fp->yv[yi].r,
308 glColor4fv(color[5]);
314 for (ui = curr_party(); ui > 0; ui--)
316 if (ui == ball || (config_get_d(CONFIG_BALL_COLLISIONS) && fp->uv[ui].P))
320 m_basis(M, fp->uv[ui].e[0], fp->uv[ui].e[1], fp->uv[ui].e[2]);
324 glTranslatef(fp->uv[ui].p[0],
325 fp->uv[ui].p[1] + BALL_FUDGE,
328 glScalef(fp->uv[ui].r,
332 glColor4fv(color[ui]);
341 glTranslatef(fp->uv[ui].p[0],
342 fp->uv[ui].p[1] - fp->uv[ui].r + BALL_FUDGE,
344 glScalef(fp->uv[ui].r,
348 glColor4f(color[ui][0],
357 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
360 static void game_draw_goals(const struct s_file *fp)
364 for (zi = 0; zi < fp->zc; zi++)
368 glTranslatef(fp->zv[zi].p[0],
377 static void game_draw_jumps(const struct s_file *fp)
381 for (ji = 0; ji < fp->jc; ji++)
385 glTranslatef(fp->jv[ji].p[0],
389 glScalef(fp->jv[ji].r, 1.f, fp->jv[ji].r);
396 static void game_draw_swchs(const struct s_file *fp)
400 for (xi = 0; xi < fp->xc; xi++)
404 glTranslatef(fp->xv[xi].p[0],
408 glScalef(fp->xv[xi].r, 1.f, fp->xv[xi].r);
409 swch_draw(fp->xv[xi].f, fp->xv[xi].e);
415 /*---------------------------------------------------------------------------*/
417 void game_draw(int pose, float t)
419 static const float a[4] = { 0.2f, 0.2f, 0.2f, 1.0f };
420 static const float s[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
421 static const float e[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
422 static const float h[1] = { 0.0f };
424 const float light_p[4] = { 8.f, 32.f, 8.f, 1.f };
426 const struct s_file *fp = &file;
432 if (config_get_d(CONFIG_BALL_COLLISIONS) && jump_b && jump_u != ball * 2)
433 fov /= 1.9f * fabsf(jump_dt - 0.5f);
436 fov *= 2.0f * fabsf(jump_dt - 0.5f);
438 config_push_persp(fov, 0.1f, FAR_DIST);
439 glPushAttrib(GL_LIGHTING_BIT);
442 float T[16], M[16], v[3], rx, ry;
444 m_view(T, view_c, view_p, view_e[1]);
447 v_sub(v, view_c, view_p);
449 rx = V_DEG(fatan2f(-v[1], fsqrtf(v[0] * v[0] + v[2] * v[2])));
450 ry = V_DEG(fatan2f(+v[0], -v[2]));
452 glTranslatef(0.f, 0.f, -v_len(v));
454 glTranslatef(-view_c[0], -view_c[1], -view_c[2]);
456 /* Center the skybox about the position of the camera. */
460 glTranslatef(view_p[0], view_p[1], view_p[2]);
466 glLightfv(GL_LIGHT0, GL_POSITION, light_p);
468 /* Draw the floor. */
472 if (config_get_d(CONFIG_SHADOW) && !pose)
474 for (i = 0; i < fp->yc; i++)
476 shad_draw_set(fp->yv[i].p, fp->yv[i].r);
481 for (i = 0; i < fp->uc; i++)
485 shad_draw_set(fp->uv[i].p, fp->uv[i].r);
491 shad_draw_set(fp->uv[ball].p, fp->uv[ball].r);
496 /* Draw the game elements. */
499 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
503 game_draw_balls(fp, T, t);
507 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, a);
508 glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, s);
509 glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, e);
510 glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, h);
514 glEnable(GL_COLOR_MATERIAL);
515 glDisable(GL_LIGHTING);
516 glDisable(GL_TEXTURE_2D);
517 glDepthMask(GL_FALSE);
522 glDepthMask(GL_TRUE);
523 glEnable(GL_TEXTURE_2D);
524 glEnable(GL_LIGHTING);
525 glDisable(GL_COLOR_MATERIAL);
532 /*---------------------------------------------------------------------------*/
534 void game_update_view(float dt)
536 const float y[3] = { 0.f, 1.f, 0.f };
545 /* Center the view about the ball. */
547 v_cpy(view_c, file.uv[ball].p);
548 v_inv(view_v, file.uv[ball].v);
550 switch (config_get_d(CONFIG_CAMERA))
553 /* Camera 2: View vector is given by view angle. */
555 view_e[2][0] = fsinf(V_RAD(view_a));
557 view_e[2][2] = fcosf(V_RAD(view_a));
563 /* View vector approaches the ball velocity vector. */
565 v_mad(e, view_v, y, v_dot(view_v, y));
568 k = v_dot(view_v, view_v);
570 v_sub(view_e[2], view_p, view_c);
571 v_mad(view_e[2], view_e[2], view_v, k * dt * 0.1f);
574 /* Orthonormalize the basis of the view in its new position. */
576 v_crs(view_e[0], view_e[1], view_e[2]);
577 v_crs(view_e[2], view_e[0], view_e[1]);
578 v_nrm(view_e[0], view_e[0]);
579 v_nrm(view_e[2], view_e[2]);
581 /* The current view (dy, dz) approaches the ideal (view_dy, view_dz). */
583 v_sub(d, view_p, view_c);
585 dy = v_dot(view_e[1], d);
586 dz = v_dot(view_e[2], d);
588 dy += (view_dy - dy) * s;
589 dz += (view_dz - dz) * s;
591 /* Compute the new view position. */
593 view_p[0] = view_p[1] = view_p[2] = 0.f;
595 v_mad(view_p, view_c, view_e[1], dy);
596 v_mad(view_p, view_p, view_e[2], dz);
598 view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
601 static int game_update_state(float dt)
603 static float t = 0.f;
605 struct s_file *fp = &file;
615 /* Test for a switch. */
616 if (sol_swch_test(fp))
617 audio_play(AUD_SWITCH, 1.f);
619 /* Test for a jump. */
621 if (config_get_d(CONFIG_BALL_COLLISIONS))
623 for (i = 1; i < curr_party() + 1; i++)
625 if (!jump_u && jump_e == 1 && jump_b == 0 && sol_jump_test(fp, jump_p, i) == 1)
632 audio_play(AUD_JUMP, 1.f);
634 if (jump_e == 0 && jump_b == 0 && sol_jump_test(fp, jump_p, i) == 0)
636 if (!jump_b && jump_u && i == jump_u / 2 && sol_jump_test(fp, jump_p, i) == 0)
640 for (i = 0; i < fp->yc; i++)
642 if (!jump_u && jump_e == 1 && jump_b == 0 && sol_jump_test(fp, jump_p, fp->yv + i - fp->uv) == 1)
649 audio_play(AUD_JUMP, 1.f);
651 if (jump_e == 0 && jump_b == 0 && sol_jump_test(fp, jump_p, fp->yv + i - fp->uv) == 0)
653 if (!jump_b && jump_u && i == jump_u / 2 && sol_jump_test(fp, jump_p, fp->yv + i - fp->uv) == 0)
659 if (jump_e == 1 && jump_b == 0 && sol_jump_test(fp, jump_p, ball) == 1)
665 audio_play(AUD_JUMP, 1.f);
667 if (jump_e == 0 && jump_b == 0 && sol_jump_test(fp, jump_p, ball) == 0)
671 /* Test for fall-out. */
673 if (fp->uv[ball].p[1] < -10.0f)
676 /* Test for a goal or stop. */
682 if (config_get_d(CONFIG_BALL_COLLISIONS))
684 switch (sol_goal_test(fp, p, ball))
686 case 2: /* The player's ball landed in the goal and the all of the other balls have stopped */
690 case 1: /* All balls have stopped */
694 case 0: /* Game still running; there may be a ball that has not yet stopped */
697 default: /* Should never reach this */
704 if (sol_goal_test(fp, p, ball))
714 void game_set_played(int b)
729 * On most hardware, rendering requires much more computing power than
730 * physics. Since physics takes less time than graphics, it make sense to
731 * detach the physics update time step from the graphics frame rate. By
732 * performing multiple physics updates for each graphics update, we get away
733 * with higher quality physics with little impact on overall performance.
735 * Toward this end, we establish a baseline maximum physics time step. If
736 * the measured frame time exceeds this maximum, we cut the time step in
737 * half, and do two updates. If THIS time step exceeds the maximum, we do
738 * four updates. And so on. In this way, the physics system is allowed to
739 * seek an optimal update rate independent of, yet in integral sync with, the
740 * graphics frame rate.
743 int game_step(const float g[3], float dt)
745 struct s_file *fp = &file;
747 static float s = 0.f;
748 static float t = 0.f;
755 s = (7.f * s + dt) / 8.f;
760 if (config_get_d(CONFIG_BALL_COLLISIONS))
770 fp->yv[jump_u / 2].p[0] = jump_p[0];
771 fp->yv[jump_u / 2].p[1] = jump_p[1];
772 fp->yv[jump_u / 2].p[2] = jump_p[2];
777 fp->uv[jump_u / 2].p[0] = jump_p[0];
778 fp->uv[jump_u / 2].p[1] = jump_p[1];
779 fp->uv[jump_u / 2].p[2] = jump_p[2];
796 fp->uv[ball].p[0] = jump_p[0];
797 fp->uv[ball].p[1] = jump_p[1];
798 fp->uv[ball].p[2] = jump_p[2];
808 while (t > MAX_DT && n < MAX_DN)
814 for (i = 0; i < n; i++)
816 int ball_in_hole = 0;
818 d = sol_step(fp, g, t, ball, &m);
820 if ((ball_in_hole = game_check_balls(fp)))
821 hole_goal(ball_in_hole);
829 /* Mix the sound of a ball bounce. */
832 audio_play(AUD_BUMP, (float) (b - 0.5) * 2.0f);
835 game_update_view(dt);
836 return game_update_state(st);
842 * HACK: The BALL_FUDGE here guarantees that a putt doesn't drive
843 * the ball too directly down toward a lump, triggering rolling
844 * friction too early and stopping the ball prematurely.
847 if (config_get_d(CONFIG_BALL_COLLISIONS))
849 file.uv[ball].v[0] = -4.f * view_e[2][0] * view_m;
850 file.uv[ball].v[1] = -4.f * view_e[2][1] * view_m + BALL_FUDGE;
851 file.uv[ball].v[2] = -4.f * view_e[2][2] * view_m;
856 file.uv[ball].v[0] = -4.f * view_e[2][0] * view_m;
857 file.uv[ball].v[1] = -4.f * view_e[2][1] * view_m + BALL_FUDGE;
858 file.uv[ball].v[2] = -4.f * view_e[2][2] * view_m;
864 /*---------------------------------------------------------------------------*/
866 void game_set_rot(int d)
868 view_a += (float) (30.f * d) / config_get_d(CONFIG_MOUSE_SENSE);
871 void game_clr_mag(void)
876 void game_set_mag(int d)
878 view_m -= (float) (1.f * d) / config_get_d(CONFIG_MOUSE_SENSE);
884 void game_set_fly(float k)
886 struct s_file *fp = &file;
888 float x[3] = { 1.f, 0.f, 0.f };
889 float y[3] = { 0.f, 1.f, 0.f };
890 float z[3] = { 0.f, 0.f, 1.f };
891 float c0[3] = { 0.f, 0.f, 0.f };
892 float p0[3] = { 0.f, 0.f, 0.f };
893 float c1[3] = { 0.f, 0.f, 0.f };
894 float p1[3] = { 0.f, 0.f, 0.f };
899 v_sub(view_e[2], fp->uv[ball].p, fp->zv[0].p);
901 if (fabs(v_dot(view_e[1], view_e[2])) > 0.999)
904 v_crs(view_e[0], view_e[1], view_e[2]);
905 v_crs(view_e[2], view_e[0], view_e[1]);
907 v_nrm(view_e[0], view_e[0]);
908 v_nrm(view_e[2], view_e[2]);
910 /* k = 0.0 view is at the ball. */
914 v_cpy(c0, fp->uv[ball].p);
915 v_cpy(p0, fp->uv[ball].p);
918 v_mad(p0, p0, view_e[1], view_dy);
919 v_mad(p0, p0, view_e[2], view_dz);
921 /* k = +1.0 view is s_view 0 */
923 if (k >= 0 && fp->wc > 0)
925 v_cpy(p1, fp->wv[0].p);
926 v_cpy(c1, fp->wv[0].q);
929 /* k = -1.0 view is s_view 1 */
931 if (k <= 0 && fp->wc > 1)
933 v_cpy(p1, fp->wv[1].p);
934 v_cpy(c1, fp->wv[1].q);
937 /* Interpolate the views. */
940 v_mad(view_p, p0, v, k * k);
943 v_mad(view_c, c0, v, k * k);
945 /* Orthonormalize the view basis. */
947 v_sub(view_e[2], view_p, view_c);
948 v_crs(view_e[0], view_e[1], view_e[2]);
949 v_crs(view_e[2], view_e[0], view_e[1]);
950 v_nrm(view_e[0], view_e[0]);
951 v_nrm(view_e[2], view_e[2]);
953 view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
956 void game_ball(int i)
965 for (ui = 0; ui < file.uc; ui++)
967 file.uv[ui].v[0] = 0.f;
968 file.uv[ui].v[1] = 0.f;
969 file.uv[ui].v[2] = 0.f;
971 file.uv[ui].w[0] = 0.f;
972 file.uv[ui].w[1] = 0.f;
973 file.uv[ui].w[2] = 0.f;
977 void game_get_pos(float p[3], float e[3][3])
979 v_cpy(p, file.uv[ball].p);
980 v_cpy(e[0], file.uv[ball].e[0]);
981 v_cpy(e[1], file.uv[ball].e[1]);
982 v_cpy(e[2], file.uv[ball].e[2]);
985 void game_set_pos(float p[3], float e[3][3])
987 v_cpy(file.uv[ball].p, p);
988 v_cpy(file.uv[ball].e[0], e[0]);
989 v_cpy(file.uv[ball].e[1], e[1]);
990 v_cpy(file.uv[ball].e[2], e[2]);
993 /*---------------------------------------------------------------------------*/