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 int jump_s; /* Has ball reached other end? */
48 static int jump_u; /* Which ball is jumping? */
49 static float jump_b; /* Jump-in-progress flag */
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)
90 sol_load_gl(&file, config_data(s), config_get_d(CONFIG_TEXTURES),
91 config_get_d(CONFIG_SHADOW));
93 game_set_play(PLAY_ALL, 0);
103 /*---------------------------------------------------------------------------*/
105 static void game_draw_vect_prim(const struct s_file *fp, GLenum mode)
112 v_cpy(p, fp->uv[ball].p);
120 glColor4f(1.0f, 1.0f, 0.5f, 0.5f);
121 glVertex3f(p[0] - x[0] * r,
125 glColor4f(1.0f, 0.0f, 0.0f, 0.5f);
126 glVertex3f(p[0] + z[0] * view_m,
127 p[1] + z[1] * view_m,
128 p[2] + z[2] * view_m);
130 glColor4f(1.0f, 1.0f, 0.0f, 0.5f);
131 glVertex3f(p[0] + x[0] * r,
138 static void game_draw_vect(const struct s_file *fp)
142 glPushAttrib(GL_TEXTURE_BIT);
143 glPushAttrib(GL_POLYGON_BIT);
144 glPushAttrib(GL_LIGHTING_BIT);
145 glPushAttrib(GL_DEPTH_BUFFER_BIT);
147 glEnable(GL_COLOR_MATERIAL);
148 glDisable(GL_LIGHTING);
149 glDisable(GL_TEXTURE_2D);
150 glDepthMask(GL_FALSE);
152 glEnable(GL_DEPTH_TEST);
153 game_draw_vect_prim(fp, GL_TRIANGLES);
155 glDisable(GL_DEPTH_TEST);
156 game_draw_vect_prim(fp, GL_LINE_STRIP);
165 static void game_draw_balls(const struct s_file *fp,
166 const float *bill_M, float t)
168 static const GLfloat color[5][4] = {
169 { 1.0f, 1.0f, 1.0f, 0.7f },
170 { 1.0f, 0.0f, 0.0f, 1.0f },
171 { 0.0f, 1.0f, 0.0f, 1.0f },
172 { 0.0f, 0.0f, 1.0f, 1.0f },
173 { 1.0f, 1.0f, 0.0f, 1.0f },
178 for (ui = 1; ui < fp->uc; ui++)
180 if (ui == ball || fp->uv[ui].P)
185 m_basis(ball_M, fp->uv[ui].e[0], fp->uv[ui].e[1], fp->uv[ui].e[2]);
186 m_basis(pend_M, fp->uv[ui].E[0], fp->uv[ui].E[1], fp->uv[ui].E[2]);
188 glPushAttrib(GL_LIGHTING_BIT);
191 glTranslatef(fp->uv[ui].p[0],
192 fp->uv[ui].p[1] + BALL_FUDGE,
194 glScalef(fp->uv[ui].r,
198 glEnable(GL_COLOR_MATERIAL);
199 glColor4fv(color[ui]);
201 ball_draw(ball_M, pend_M, bill_M, t);
202 glDisable(GL_COLOR_MATERIAL);
208 else if (ui <= curr_party())
212 glTranslatef(fp->uv[ui].p[0],
213 fp->uv[ui].p[1] - fp->uv[ui].r + BALL_FUDGE,
215 glScalef(fp->uv[ui].r,
219 glColor4f(color[ui][0],
228 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
231 static void game_draw_goals(const struct s_file *fp)
235 for (zi = 0; zi < fp->zc; zi++)
239 glTranslatef(fp->zv[zi].p[0],
248 static void game_draw_jumps(const struct s_file *fp)
252 for (ji = 0; ji < fp->jc; ji++)
256 glTranslatef(fp->jv[ji].p[0],
260 glScalef(fp->jv[ji].r, 1.f, fp->jv[ji].r);
261 jump_draw((fp->jv[ji].b > 0) ? 1 : 0);
267 static void game_draw_swchs(const struct s_file *fp)
271 for (xi = 0; xi < fp->xc; xi++)
275 glTranslatef(fp->xv[xi].p[0],
279 glScalef(fp->xv[xi].r, 1.f, fp->xv[xi].r);
280 swch_draw(fp->xv[xi].f, fp->xv[xi].e);
286 /*---------------------------------------------------------------------------*/
288 void game_draw(int pose, float t)
290 static const float a[4] = { 0.2f, 0.2f, 0.2f, 1.0f };
291 static const float s[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
292 static const float e[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
293 static const float h[1] = { 0.0f };
295 const float light_p[4] = { 8.f, 32.f, 8.f, 1.f };
297 const struct s_file *fp = &file;
303 if (jump_b == JUMP_CURR_BALL)
305 fov *= 2.0f * fabsf(jump_dt - 0.5f);
308 config_push_persp(fov, 0.1f, FAR_DIST);
309 glPushAttrib(GL_LIGHTING_BIT);
312 float T[16], M[16], v[3], rx, ry;
314 m_view(T, view_c, view_p, view_e[1]);
317 v_sub(v, view_c, view_p);
319 rx = V_DEG(fatan2f(-v[1], fsqrtf(v[0] * v[0] + v[2] * v[2])));
320 ry = V_DEG(fatan2f(+v[0], -v[2]));
322 glTranslatef(0.f, 0.f, -v_len(v));
324 glTranslatef(-view_c[0], -view_c[1], -view_c[2]);
326 /* Center the skybox about the position of the camera. */
330 glTranslatef(view_p[0], view_p[1], view_p[2]);
336 glLightfv(GL_LIGHT0, GL_POSITION, light_p);
338 /* Draw the floor. */
342 if (config_get_d(CONFIG_SHADOW) && !pose)
344 for (i = 0; i < fp->uc; i++)
348 shad_draw_set(fp->uv[i].p, fp->uv[i].r);
355 /* Draw the game elements. */
358 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
362 game_draw_balls(fp, T, t);
366 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, a);
367 glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, s);
368 glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, e);
369 glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, h);
373 glEnable(GL_COLOR_MATERIAL);
374 glDisable(GL_LIGHTING);
375 glDisable(GL_TEXTURE_2D);
376 glDepthMask(GL_FALSE);
381 glDepthMask(GL_TRUE);
382 glEnable(GL_TEXTURE_2D);
383 glEnable(GL_LIGHTING);
384 glDisable(GL_COLOR_MATERIAL);
391 /*---------------------------------------------------------------------------*/
393 void game_update_view(float dt)
395 const float y[3] = { 0.f, 1.f, 0.f };
404 /* Center the view about the ball. */
406 v_cpy(view_c, file.uv[ball].p);
407 v_inv(view_v, file.uv[ball].v);
409 switch (config_get_d(CONFIG_CAMERA))
412 /* Camera 2: View vector is given by view angle. */
414 view_e[2][0] = fsinf(V_RAD(view_a));
416 view_e[2][2] = fcosf(V_RAD(view_a));
422 /* View vector approaches the ball velocity vector. */
424 v_mad(e, view_v, y, v_dot(view_v, y));
427 k = v_dot(view_v, view_v);
429 v_sub(view_e[2], view_p, view_c);
430 v_mad(view_e[2], view_e[2], view_v, k * dt * 0.1f);
433 /* Orthonormalize the basis of the view in its new position. */
435 v_crs(view_e[0], view_e[1], view_e[2]);
436 v_crs(view_e[2], view_e[0], view_e[1]);
437 v_nrm(view_e[0], view_e[0]);
438 v_nrm(view_e[2], view_e[2]);
440 /* The current view (dy, dz) approaches the ideal (view_dy, view_dz). */
442 v_sub(d, view_p, view_c);
444 dy = v_dot(view_e[1], d);
445 dz = v_dot(view_e[2], d);
447 dy += (view_dy - dy) * s;
448 dz += (view_dz - dz) * s;
450 /* Compute the new view position. */
452 view_p[0] = view_p[1] = view_p[2] = 0.f;
454 v_mad(view_p, view_c, view_e[1], dy);
455 v_mad(view_p, view_p, view_e[2], dz);
457 view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
460 static int game_update_state(float dt)
462 static float t = 0.f;
464 struct s_file *fp = &file;
465 float p[3], d[3], z[3] = {0.f, 0.f, 0.f};
467 int i, j, u, ui, m = 0;
474 for (ui = 0; ui < fp->uc; ui++)
475 if (ui != ball && fp->uv[ui].P && v_len(fp->uv[ui].v) > 0.0f)
478 /* Test for a switch. */
480 if (sol_swch_test(fp))
481 audio_play(AUD_SWITCH, 1.f);
483 /* Test for a jump. */
485 if (jump_b == JUMP_NONE && (u = sol_jump_test(fp, jump_p)))
489 jump_b = JUMP_CURR_BALL;
493 jump_b = JUMP_OTHR_BALL;
498 audio_play(AUD_JUMP, 1.f);
501 /* Test for fall-out. */
503 for (ui = 0; ui < fp->uc; ui++)
504 if (ui != ball && fp->uv[ui].P && fp->uv[ui].p[1] < -10.f)
506 game_set_play(ui, 0);
507 v_cpy(fp->uv[ui].v, z);
508 v_cpy(fp->uv[ui].w, z);
512 if (!m && fp->uv[ball].p[1] < -10.f)
514 game_set_play(ball, 0);
515 v_cpy(fp->uv[ball].v, z);
516 v_cpy(fp->uv[ball].w, z);
520 /* Test for intersections */
522 for (i = 0; i < fp->uc; i++)
524 struct s_ball *up = fp->uv + i;
526 if (!up->P || v_len(up->v) > 0.0f)
529 for (j = i + 1; j < fp->uc; j++)
531 struct s_ball *u2p = fp->uv + j;
533 if (!u2p->P || v_len(u2p->v) > 0.0f)
536 v_sub(d, up->p, u2p->p);
538 if (v_len(d) < up->r + u2p->r)
548 /* Test for a goal or stop. */
550 for (ui = 0; ui < fp->uc; ui++)
552 if (ui != ball && fp->uv[ui].P && !(v_len(fp->uv[ui].v) > 0.0f) && sol_goal_test(fp, p, ui))
554 game_set_play(ui, 0);
563 if (sol_goal_test(fp, p, ball))
565 game_set_play(ui, 0);
579 * On most hardware, rendering requires much more computing power than
580 * physics. Since physics takes less time than graphics, it make sense to
581 * detach the physics update time step from the graphics frame rate. By
582 * performing multiple physics updates for each graphics update, we get away
583 * with higher quality physics with little impact on overall performance.
585 * Toward this end, we establish a baseline maximum physics time step. If
586 * the measured frame time exceeds this maximum, we cut the time step in
587 * half, and do two updates. If THIS time step exceeds the maximum, we do
588 * four updates. And so on. In this way, the physics system is allowed to
589 * seek an optimal update rate independent of, yet in integral sync with, the
590 * graphics frame rate.
593 int game_step(const float g[3], float dt)
595 struct s_file *fp = &file;
597 static float s = 0.f;
598 static float t = 0.f;
605 s = (7.f * s + dt) / 8.f;
609 * The JUMP_NONE here ensures that no two balls
610 * are being processed simultaneously. If two
611 * enter a jump at exactly the same time, the
612 * ball with the lower ui will be processed
613 * first, and the second ball will be processed
617 if (jump_b == JUMP_NONE)
621 while (t > MAX_DT && n < MAX_DN)
627 for (i = 0; i < n; i++)
629 d = sol_step(fp, g, t, ball, &m);
637 /* Mix the sound of a ball bounce. */
640 audio_play(AUD_BUMP, (float) (b - 0.5) * 2.0f);
649 if (0.5f < jump_dt && jump_s)
652 fp->uv[jump_u].p[0] = jump_p[0];
653 fp->uv[jump_u].p[1] = jump_p[1];
654 fp->uv[jump_u].p[2] = jump_p[2];
655 sol_jump_test(fp, NULL);
666 game_update_view(dt);
667 return game_update_state(st);
673 * HACK: The BALL_FUDGE here guarantees that a putt doesn't drive
674 * the ball too directly down toward a lump, triggering rolling
675 * friction too early and stopping the ball prematurely.
678 file.uv[ball].v[0] = -4.f * view_e[2][0] * view_m;
679 file.uv[ball].v[1] = -4.f * view_e[2][1] * view_m + BALL_FUDGE;
680 file.uv[ball].v[2] = -4.f * view_e[2][2] * view_m;
685 /*---------------------------------------------------------------------------*/
688 * Set ball B's play state as S. Additional values can be used for b:
691 void game_set_play(int b, int s)
695 if (b >= 0 && b < file.uc)
698 if (b == PLAY_CURRENT && ball < file.uc)
702 for (i = 1; i < file.uc; i++)
706 for (i = 1; i <= curr_party() && i < file.uc; i++)
710 /*---------------------------------------------------------------------------*/
712 void game_set_rot(int d)
714 view_a += (float) (30.f * d) / config_get_d(CONFIG_MOUSE_SENSE);
717 void game_clr_mag(void)
722 void game_set_mag(int d)
724 view_m -= (float) (1.f * d) / config_get_d(CONFIG_MOUSE_SENSE);
730 void game_set_fly(float k)
732 struct s_file *fp = &file;
734 float x[3] = { 1.f, 0.f, 0.f };
735 float y[3] = { 0.f, 1.f, 0.f };
736 float z[3] = { 0.f, 0.f, 1.f };
737 float c0[3] = { 0.f, 0.f, 0.f };
738 float p0[3] = { 0.f, 0.f, 0.f };
739 float c1[3] = { 0.f, 0.f, 0.f };
740 float p1[3] = { 0.f, 0.f, 0.f };
745 v_sub(view_e[2], fp->uv[ball].p, fp->zv[0].p);
747 if (fabs(v_dot(view_e[1], view_e[2])) > 0.999)
750 v_crs(view_e[0], view_e[1], view_e[2]);
751 v_crs(view_e[2], view_e[0], view_e[1]);
753 v_nrm(view_e[0], view_e[0]);
754 v_nrm(view_e[2], view_e[2]);
756 /* k = 0.0 view is at the ball. */
760 v_cpy(c0, fp->uv[ball].p);
761 v_cpy(p0, fp->uv[ball].p);
764 v_mad(p0, p0, view_e[1], view_dy);
765 v_mad(p0, p0, view_e[2], view_dz);
767 /* k = +1.0 view is s_view 0 */
769 if (k >= 0 && fp->wc > 0)
771 v_cpy(p1, fp->wv[0].p);
772 v_cpy(c1, fp->wv[0].q);
775 /* k = -1.0 view is s_view 1 */
777 if (k <= 0 && fp->wc > 1)
779 v_cpy(p1, fp->wv[1].p);
780 v_cpy(c1, fp->wv[1].q);
783 /* Interpolate the views. */
786 v_mad(view_p, p0, v, k * k);
789 v_mad(view_c, c0, v, k * k);
791 /* Orthonormalize the view basis. */
793 v_sub(view_e[2], view_p, view_c);
794 v_crs(view_e[0], view_e[1], view_e[2]);
795 v_crs(view_e[2], view_e[0], view_e[1]);
796 v_nrm(view_e[0], view_e[0]);
797 v_nrm(view_e[2], view_e[2]);
799 view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
802 void game_ball(int i)
808 for (ui = 0; ui < file.uc; ui++)
810 file.uv[ui].v[0] = 0.f;
811 file.uv[ui].v[1] = 0.f;
812 file.uv[ui].v[2] = 0.f;
814 file.uv[ui].w[0] = 0.f;
815 file.uv[ui].w[1] = 0.f;
816 file.uv[ui].w[2] = 0.f;
820 void game_get_pos(float p[3], float e[3][3], int ui)
827 v_cpy(p, file.uv[ui].p);
828 v_cpy(e[0], file.uv[ui].e[0]);
829 v_cpy(e[1], file.uv[ui].e[1]);
830 v_cpy(e[2], file.uv[ui].e[2]);
834 void game_set_pos(float p[3], float e[3][3], int ui)
841 v_cpy(file.uv[ui].p, p);
842 v_cpy(file.uv[ui].e[0], e[0]);
843 v_cpy(file.uv[ui].e[1], e[1]);
844 v_cpy(file.uv[ui].e[2], e[2]);
848 /*---------------------------------------------------------------------------*/