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.
30 /*---------------------------------------------------------------------------*/
32 static struct s_file file;
35 static float view_a; /* Ideal view rotation about Y axis */
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 float jump_e = 1; /* Jumping enabled flag */
47 static float jump_b = 0; /* Jump-in-progress flag */
48 static float jump_dt; /* Jump duration */
49 static float jump_p[3]; /* Jump destination */
51 /*---------------------------------------------------------------------------*/
53 static void view_init(void)
80 void game_init(const char *s)
86 sol_load_gl(&file, config_data(s), config_get_d(CONFIG_TEXTURES),
87 config_get_d(CONFIG_SHADOW));
95 /*---------------------------------------------------------------------------*/
97 static void game_draw_vect_prim(const struct s_file *fp, GLenum mode)
104 v_cpy(p, fp->uv[ball].p);
112 glColor4f(1.0f, 1.0f, 0.5f, 0.5f);
113 glVertex3f(p[0] - x[0] * r,
117 glColor4f(1.0f, 0.0f, 0.0f, 0.5f);
118 glVertex3f(p[0] + z[0] * view_m,
119 p[1] + z[1] * view_m,
120 p[2] + z[2] * view_m);
122 glColor4f(1.0f, 1.0f, 0.0f, 0.5f);
123 glVertex3f(p[0] + x[0] * r,
130 static void game_draw_vect(const struct s_file *fp)
134 glPushAttrib(GL_TEXTURE_BIT);
135 glPushAttrib(GL_POLYGON_BIT);
136 glPushAttrib(GL_LIGHTING_BIT);
137 glPushAttrib(GL_DEPTH_BUFFER_BIT);
139 glEnable(GL_COLOR_MATERIAL);
140 glDisable(GL_LIGHTING);
141 glDisable(GL_TEXTURE_2D);
142 glDepthMask(GL_FALSE);
144 glEnable(GL_DEPTH_TEST);
145 game_draw_vect_prim(fp, GL_TRIANGLES);
147 glDisable(GL_DEPTH_TEST);
148 game_draw_vect_prim(fp, GL_LINE_STRIP);
157 static void game_draw_balls(const struct s_file *fp)
159 static const GLfloat color[5][4] = {
160 { 1.0f, 1.0f, 1.0f, 0.7f },
161 { 1.0f, 0.0f, 0.0f, 1.0f },
162 { 0.0f, 1.0f, 0.0f, 1.0f },
163 { 0.0f, 0.0f, 1.0f, 1.0f },
164 { 1.0f, 1.0f, 0.0f, 1.0f },
170 for (ui = curr_party(); ui > 0; ui--)
176 m_basis(M, fp->uv[ui].e[0], fp->uv[ui].e[1], fp->uv[ui].e[2]);
178 glTranslatef(fp->uv[ui].p[0],
179 fp->uv[ui].p[1] + BALL_FUDGE,
182 glScalef(fp->uv[ui].r,
186 glColor4fv(color[ui]);
196 glTranslatef(fp->uv[ui].p[0],
197 fp->uv[ui].p[1] - fp->uv[ui].r + BALL_FUDGE,
199 glScalef(fp->uv[ui].r,
203 glColor4f(color[ui][0],
214 static void game_draw_goals(const struct s_file *fp, float rx, float ry)
218 for (zi = 0; zi < fp->zc; zi++)
222 glTranslatef(fp->zv[zi].p[0],
231 static void game_draw_jumps(const struct s_file *fp)
235 for (ji = 0; ji < fp->jc; ji++)
239 glTranslatef(fp->jv[ji].p[0],
243 glScalef(fp->jv[ji].r, 1.f, fp->jv[ji].r);
250 static void game_draw_swchs(const struct s_file *fp)
254 for (xi = 0; xi < fp->xc; xi++)
258 glTranslatef(fp->xv[xi].p[0],
262 glScalef(fp->xv[xi].r, 1.f, fp->xv[xi].r);
263 swch_draw(fp->xv[xi].f, fp->xv[xi].e);
269 /*---------------------------------------------------------------------------*/
271 void game_draw(int pose)
273 const float light_p[4] = { 8.f, 32.f, 8.f, 1.f };
275 const struct s_file *fp = &file;
279 if (jump_b) fov *= 2.0f * fabsf(jump_dt - 0.5f);
281 config_push_persp(fov, 0.1f, FAR_DIST);
282 glPushAttrib(GL_LIGHTING_BIT);
287 v_sub(v, view_c, view_p);
289 rx = V_DEG(fatan2f(-v[1], fsqrtf(v[0] * v[0] + v[2] * v[2])));
290 ry = V_DEG(fatan2f(+v[0], -v[2]));
292 glTranslatef(0.f, 0.f, -v_len(v));
293 glRotatef(rx, 1.f, 0.f, 0.f);
294 glRotatef(ry, 0.f, 1.f, 0.f);
295 glTranslatef(-view_c[0], -view_c[1], -view_c[2]);
297 /* Center the skybox about the position of the camera. */
301 glTranslatef(view_p[0], view_p[1], view_p[2]);
307 glLightfv(GL_LIGHT0, GL_POSITION, light_p);
309 /* Draw the floor. */
313 if (config_get_d(CONFIG_SHADOW) && !pose)
315 shad_draw_set(fp->uv[ball].p, fp->uv[ball].r);
320 /* Draw the game elements. */
323 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
331 game_draw_goals(fp, -rx, -ry);
340 /*---------------------------------------------------------------------------*/
342 void game_update_view(float dt)
344 const float y[3] = { 0.f, 1.f, 0.f };
353 /* Center the view about the ball. */
355 v_cpy(view_c, file.uv[ball].p);
356 v_inv(view_v, file.uv[ball].v);
358 switch (config_get_d(CONFIG_CAMERA))
361 /* Camera 2: View vector is given by view angle. */
363 view_e[2][0] = fsinf(V_RAD(view_a));
365 view_e[2][2] = fcosf(V_RAD(view_a));
371 /* View vector approaches the ball velocity vector. */
373 v_mad(e, view_v, y, v_dot(view_v, y));
376 k = v_dot(view_v, view_v);
378 v_sub(view_e[2], view_p, view_c);
379 v_mad(view_e[2], view_e[2], view_v, k * dt * 0.1f);
382 /* Orthonormalize the basis of the view in its new position. */
384 v_crs(view_e[0], view_e[1], view_e[2]);
385 v_crs(view_e[2], view_e[0], view_e[1]);
386 v_nrm(view_e[0], view_e[0]);
387 v_nrm(view_e[2], view_e[2]);
389 /* The current view (dy, dz) approaches the ideal (view_dy, view_dz). */
391 v_sub(d, view_p, view_c);
393 dy = v_dot(view_e[1], d);
394 dz = v_dot(view_e[2], d);
396 dy += (view_dy - dy) * s;
397 dz += (view_dz - dz) * s;
399 /* Compute the new view position. */
401 view_p[0] = view_p[1] = view_p[2] = 0.f;
403 v_mad(view_p, view_c, view_e[1], dy);
404 v_mad(view_p, view_p, view_e[2], dz);
406 view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
409 static int game_update_state(float dt)
411 static float t = 0.f;
413 struct s_file *fp = &file;
421 /* Test for a switch. */
423 if (sol_swch_test(fp, ball))
424 audio_play(AUD_SWITCH, 1.f);
426 /* Test for a jump. */
428 if (jump_e == 1 && jump_b == 0 && sol_jump_test(fp, jump_p, ball) == 1)
434 audio_play(AUD_JUMP, 1.f);
436 if (jump_e == 0 && jump_b == 0 && sol_jump_test(fp, jump_p, ball) == 0)
439 /* Test for fall-out. */
441 if (fp->uv[ball].p[1] < -10.f)
444 /* Test for a goal or stop. */
450 if (sol_goal_test(fp, p, ball))
460 * On most hardware, rendering requires much more computing power than
461 * physics. Since physics takes less time than graphics, it make sense to
462 * detach the physics update time step from the graphics frame rate. By
463 * performing multiple physics updates for each graphics update, we get away
464 * with higher quality physics with little impact on overall performance.
466 * Toward this end, we establish a baseline maximum physics time step. If
467 * the measured frame time exceeds this maximum, we cut the time step in
468 * half, and do two updates. If THIS time step exceeds the maximum, we do
469 * four updates. And so on. In this way, the physics system is allowed to
470 * seek an optimal update rate independant of, yet in integral sync with, the
471 * graphics frame rate.
474 int game_step(const float g[3], float dt)
476 struct s_file *fp = &file;
478 static float s = 0.f;
479 static float t = 0.f;
486 s = (7.f * s + dt) / 8.f;
497 fp->uv[ball].p[0] = jump_p[0];
498 fp->uv[ball].p[1] = jump_p[1];
499 fp->uv[ball].p[2] = jump_p[2];
508 while (t > MAX_DT && n < MAX_DN)
514 for (i = 0; i < n; i++)
516 d = sol_step(fp, g, t, ball, &m);
524 /* Mix the sound of a ball bounce. */
527 audio_play(AUD_BUMP, (float) (b - 0.5) * 2.0f);
530 game_update_view(dt);
531 return game_update_state(st);
537 * HACK: The BALL_FUDGE here guarantees that a putt doesn't drive
538 * the ball too directly down toward a lump, triggering rolling
539 * friction too early and stopping the ball prematurely.
542 file.uv[ball].v[0] = -4.f * view_e[2][0] * view_m;
543 file.uv[ball].v[1] = -4.f * view_e[2][1] * view_m + BALL_FUDGE;
544 file.uv[ball].v[2] = -4.f * view_e[2][2] * view_m;
549 /*---------------------------------------------------------------------------*/
551 void game_set_rot(int d)
553 view_a += (float) (30.f * d) / config_get_d(CONFIG_MOUSE_SENSE);
556 void game_clr_mag(void)
561 void game_set_mag(int d)
563 view_m -= (float) (1.f * d) / config_get_d(CONFIG_MOUSE_SENSE);
569 void game_set_fly(float k)
571 struct s_file *fp = &file;
573 float x[3] = { 1.f, 0.f, 0.f };
574 float y[3] = { 0.f, 1.f, 0.f };
575 float z[3] = { 0.f, 0.f, 1.f };
576 float c0[3] = { 0.f, 0.f, 0.f };
577 float p0[3] = { 0.f, 0.f, 0.f };
578 float c1[3] = { 0.f, 0.f, 0.f };
579 float p1[3] = { 0.f, 0.f, 0.f };
584 v_sub(view_e[2], fp->uv[ball].p, fp->zv[0].p);
586 if (fabs(v_dot(view_e[1], view_e[2])) > 0.999)
589 v_crs(view_e[0], view_e[1], view_e[2]);
590 v_crs(view_e[2], view_e[0], view_e[1]);
592 v_nrm(view_e[0], view_e[0]);
593 v_nrm(view_e[2], view_e[2]);
595 /* k = 0.0 view is at the ball. */
599 v_cpy(c0, fp->uv[ball].p);
600 v_cpy(p0, fp->uv[ball].p);
603 v_mad(p0, p0, view_e[1], view_dy);
604 v_mad(p0, p0, view_e[2], view_dz);
606 /* k = +1.0 view is s_view 0 */
608 if (k >= 0 && fp->wc > 0)
610 v_cpy(p1, fp->wv[0].p);
611 v_cpy(c1, fp->wv[0].q);
614 /* k = -1.0 view is s_view 1 */
616 if (k <= 0 && fp->wc > 1)
618 v_cpy(p1, fp->wv[1].p);
619 v_cpy(c1, fp->wv[1].q);
622 /* Interpolate the views. */
625 v_mad(view_p, p0, v, k * k);
628 v_mad(view_c, c0, v, k * k);
630 /* Orthonormalize the view basis. */
632 v_sub(view_e[2], view_p, view_c);
633 v_crs(view_e[0], view_e[1], view_e[2]);
634 v_crs(view_e[2], view_e[0], view_e[1]);
635 v_nrm(view_e[0], view_e[0]);
636 v_nrm(view_e[2], view_e[2]);
638 view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
641 void game_ball(int i)
650 for (ui = 0; ui < file.uc; ui++)
652 file.uv[ui].v[0] = 0.f;
653 file.uv[ui].v[1] = 0.f;
654 file.uv[ui].v[2] = 0.f;
656 file.uv[ui].w[0] = 0.f;
657 file.uv[ui].w[1] = 0.f;
658 file.uv[ui].w[2] = 0.f;
662 void game_get_pos(float p[3], float e[3][3])
664 v_cpy(p, file.uv[ball].p);
665 v_cpy(e[0], file.uv[ball].e[0]);
666 v_cpy(e[1], file.uv[ball].e[1]);
667 v_cpy(e[2], file.uv[ball].e[2]);
670 void game_set_pos(float p[3], float e[3][3])
672 v_cpy(file.uv[ball].p, p);
673 v_cpy(file.uv[ball].e[0], e[0]);
674 v_cpy(file.uv[ball].e[1], e[1]);
675 v_cpy(file.uv[ball].e[2], e[2]);
678 /*---------------------------------------------------------------------------*/