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 #include "solid_draw.h"
31 #include "solid_sim.h"
32 #include "solid_all.h"
34 /*---------------------------------------------------------------------------*/
36 static struct s_full file;
39 static float view_a; /* Ideal view rotation about Y axis */
41 static float view_ry; /* Angular velocity about Y axis */
42 static float view_dy; /* Ideal view distance above ball */
43 static float view_dz; /* Ideal view distance behind ball */
45 static float view_c[3]; /* Current view center */
46 static float view_v[3]; /* Current view vector */
47 static float view_p[3]; /* Current view position */
48 static float view_e[3][3]; /* Current view orientation */
50 static float jump_e = 1; /* Jumping enabled flag */
51 static float jump_b = 0; /* Jump-in-progress flag */
52 static float jump_dt; /* Jump duration */
53 static float jump_p[3]; /* Jump destination */
55 static float idle_t; /* Idling timeout */
57 /*---------------------------------------------------------------------------*/
59 static void view_init(void)
86 void game_init(const char *s)
96 sol_load_full(&file, s, config_get_d(CONFIG_SHADOW));
97 sol_init_sim(&file.vary);
99 for (i = 0; i < file.base.dc; i++)
101 const char *k = file.base.av + file.base.dv[i].ai;
102 const char *v = file.base.av + file.base.dv[i].aj;
104 if (strcmp(k, "idle") == 0)
106 sscanf(v, "%f", &idle_t);
117 sol_free_full(&file);
120 /*---------------------------------------------------------------------------*/
122 static void game_draw_vect(struct s_rend *rend, const struct s_vary *fp)
126 glDisable(GL_LIGHTING);
129 glTranslatef(fp->uv[ball].p[0],
132 glRotatef(view_a, 0.0f, 1.0f, 0.0f);
133 glScalef(fp->uv[ball].r,
134 fp->uv[ball].r * 0.1f, view_m);
139 glEnable(GL_LIGHTING);
143 static void game_draw_balls(struct s_rend *rend,
144 const struct s_vary *fp,
145 const float *bill_M, float t)
147 static const GLfloat color[5][4] = {
148 { 1.0f, 1.0f, 1.0f, 0.7f },
149 { 1.0f, 0.0f, 0.0f, 1.0f },
150 { 0.0f, 1.0f, 0.0f, 1.0f },
151 { 0.0f, 0.0f, 1.0f, 1.0f },
152 { 1.0f, 1.0f, 0.0f, 1.0f },
157 glEnable(GL_COLOR_MATERIAL);
159 for (ui = curr_party(); ui > 0; ui--)
166 m_basis(ball_M, fp->uv[ui].e[0], fp->uv[ui].e[1], fp->uv[ui].e[2]);
167 m_basis(pend_M, fp->uv[ui].E[0], fp->uv[ui].E[1], fp->uv[ui].E[2]);
171 glTranslatef(fp->uv[ui].p[0],
172 fp->uv[ui].p[1] + BALL_FUDGE,
174 glScalef(fp->uv[ui].r,
178 glColor4f(color[ui][0],
182 ball_draw(rend, ball_M, pend_M, bill_M, t);
190 glTranslatef(fp->uv[ui].p[0],
191 fp->uv[ui].p[1] - fp->uv[ui].r + BALL_FUDGE,
193 glScalef(fp->uv[ui].r,
197 glColor4f(color[ui][0],
207 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
208 glDisable(GL_COLOR_MATERIAL);
211 static void game_draw_goals(struct s_rend *rend, const struct s_base *fp)
215 for (zi = 0; zi < fp->zc; zi++)
219 glTranslatef(fp->zv[zi].p[0],
228 static void game_draw_jumps(struct s_rend *rend, const struct s_base *fp)
230 float t = 0.001f * SDL_GetTicks();
233 for (ji = 0; ji < fp->jc; ji++)
237 glTranslatef(fp->jv[ji].p[0],
241 glScalef(fp->jv[ji].r, 1.f, fp->jv[ji].r);
242 jump_draw(rend, t, !jump_e);
248 static void game_draw_swchs(struct s_rend *rend, const struct s_vary *fp)
252 for (xi = 0; xi < fp->xc; xi++)
254 struct v_swch *xp = fp->xv + xi;
261 glTranslatef(xp->base->p[0],
265 glScalef(xp->base->r, 1.f, xp->base->r);
266 swch_draw(rend, xp->f, xp->e);
272 /*---------------------------------------------------------------------------*/
274 void game_draw(int pose, float t)
276 const float light_p[4] = { 8.f, 32.f, 8.f, 0.f };
278 const struct s_draw *fp = &file.draw;
279 struct s_rend rend = { NULL };
283 sol_draw_enable(&rend);
285 if (jump_b) fov *= 2.0f * fabsf(jump_dt - 0.5f);
287 video_push_persp(fov, 0.1f, FAR_DIST);
290 float T[16], M[16], v[3], rx, ry;
292 m_view(T, view_c, view_p, view_e[1]);
295 v_sub(v, view_c, view_p);
297 rx = V_DEG(fatan2f(-v[1], fsqrtf(v[0] * v[0] + v[2] * v[2])));
298 ry = V_DEG(fatan2f(+v[0], -v[2]));
300 glTranslatef(0.f, 0.f, -v_len(v));
302 glTranslatef(-view_c[0], -view_c[1], -view_c[2]);
304 /* Center the skybox about the position of the camera. */
308 glTranslatef(view_p[0], view_p[1], view_p[2]);
314 glLightfv(GL_LIGHT0, GL_POSITION, light_p);
316 /* Draw the floor. */
318 sol_draw(fp, &rend, 0, 1);
320 /* Draw the game elements. */
323 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
327 game_draw_balls(&rend, fp->vary, T, t);
328 game_draw_vect(&rend, fp->vary);
331 glEnable(GL_COLOR_MATERIAL);
332 glDisable(GL_LIGHTING);
333 glDepthMask(GL_FALSE);
335 game_draw_goals(&rend, fp->base);
336 game_draw_jumps(&rend, fp->base);
337 game_draw_swchs(&rend, fp->vary);
339 glDepthMask(GL_TRUE);
340 glEnable(GL_LIGHTING);
341 glDisable(GL_COLOR_MATERIAL);
346 sol_draw_disable(&rend);
349 /*---------------------------------------------------------------------------*/
351 void game_update_view(float dt)
353 const float y[3] = { 0.f, 1.f, 0.f };
362 /* Center the view about the ball. */
364 v_cpy(view_c, file.vary.uv[ball].p);
365 v_inv(view_v, file.vary.uv[ball].v);
367 switch (config_get_d(CONFIG_CAMERA))
370 /* Camera 2: View vector is given by view angle. */
372 view_e[2][0] = fsinf(V_RAD(view_a));
374 view_e[2][2] = fcosf(V_RAD(view_a));
380 /* View vector approaches the ball velocity vector. */
382 v_mad(e, view_v, y, v_dot(view_v, y));
385 k = v_dot(view_v, view_v);
387 v_sub(view_e[2], view_p, view_c);
388 v_mad(view_e[2], view_e[2], view_v, k * dt * 0.1f);
391 /* Orthonormalize the basis of the view in its new position. */
393 v_crs(view_e[0], view_e[1], view_e[2]);
394 v_crs(view_e[2], view_e[0], view_e[1]);
395 v_nrm(view_e[0], view_e[0]);
396 v_nrm(view_e[2], view_e[2]);
398 /* The current view (dy, dz) approaches the ideal (view_dy, view_dz). */
400 v_sub(d, view_p, view_c);
402 dy = v_dot(view_e[1], d);
403 dz = v_dot(view_e[2], d);
405 dy += (view_dy - dy) * s;
406 dz += (view_dz - dz) * s;
408 /* Compute the new view position. */
410 view_p[0] = view_p[1] = view_p[2] = 0.f;
412 v_mad(view_p, view_c, view_e[1], dy);
413 v_mad(view_p, view_p, view_e[2], dz);
415 view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
418 static int game_update_state(float dt)
420 static float t = 0.f;
422 struct s_vary *fp = &file.vary;
430 /* Test for a switch. */
432 if (sol_swch_test(fp, ball) == SWCH_TRIGGER)
433 audio_play(AUD_SWITCH, 1.f);
435 /* Test for a jump. */
437 if (jump_e == 1 && jump_b == 0 && (sol_jump_test(fp, jump_p, ball) ==
444 audio_play(AUD_JUMP, 1.f);
446 if (jump_e == 0 && jump_b == 0 && (sol_jump_test(fp, jump_p, ball) ==
452 /* Test for fall-out. */
454 if (fp->uv[ball].p[1] < -10.f)
457 /* Test for a goal or stop. */
459 if (t > 1.f && sol_goal_test(fp, p, ball))
475 * On most hardware, rendering requires much more computing power than
476 * physics. Since physics takes less time than graphics, it make sense to
477 * detach the physics update time step from the graphics frame rate. By
478 * performing multiple physics updates for each graphics update, we get away
479 * with higher quality physics with little impact on overall performance.
481 * Toward this end, we establish a baseline maximum physics time step. If
482 * the measured frame time exceeds this maximum, we cut the time step in
483 * half, and do two updates. If THIS time step exceeds the maximum, we do
484 * four updates. And so on. In this way, the physics system is allowed to
485 * seek an optimal update rate independent of, yet in integral sync with, the
486 * graphics frame rate.
489 int game_step(const float g[3], float dt)
491 struct s_vary *fp = &file.vary;
493 static float s = 0.f;
494 static float t = 0.f;
501 s = (7.f * s + dt) / 8.f;
512 fp->uv[ball].p[0] = jump_p[0];
513 fp->uv[ball].p[1] = jump_p[1];
514 fp->uv[ball].p[2] = jump_p[2];
523 while (t > MAX_DT && n < MAX_DN)
529 for (i = 0; i < n; i++)
531 d = sol_step(fp, g, t, ball, &m);
539 /* Mix the sound of a ball bounce. */
542 audio_play(AUD_BUMP, (float) (b - 0.5) * 2.0f);
545 game_update_view(dt);
546 return game_update_state(st);
552 * HACK: The BALL_FUDGE here guarantees that a putt doesn't drive
553 * the ball too directly down toward a lump, triggering rolling
554 * friction too early and stopping the ball prematurely.
557 file.vary.uv[ball].v[0] = -4.f * view_e[2][0] * view_m;
558 file.vary.uv[ball].v[1] = -4.f * view_e[2][1] * view_m + BALL_FUDGE;
559 file.vary.uv[ball].v[2] = -4.f * view_e[2][2] * view_m;
564 /*---------------------------------------------------------------------------*/
566 void game_set_rot(int d)
568 view_a += (float) (30.f * d) / config_get_d(CONFIG_MOUSE_SENSE);
571 void game_clr_mag(void)
576 void game_set_mag(int d)
578 view_m -= (float) (1.f * d) / config_get_d(CONFIG_MOUSE_SENSE);
584 void game_set_fly(float k)
586 struct s_vary *fp = &file.vary;
588 float x[3] = { 1.f, 0.f, 0.f };
589 float y[3] = { 0.f, 1.f, 0.f };
590 float z[3] = { 0.f, 0.f, 1.f };
591 float c0[3] = { 0.f, 0.f, 0.f };
592 float p0[3] = { 0.f, 0.f, 0.f };
593 float c1[3] = { 0.f, 0.f, 0.f };
594 float p1[3] = { 0.f, 0.f, 0.f };
599 v_sub(view_e[2], fp->uv[ball].p, fp->base->zv[0].p);
601 if (fabs(v_dot(view_e[1], view_e[2])) > 0.999)
604 v_crs(view_e[0], view_e[1], view_e[2]);
605 v_crs(view_e[2], view_e[0], view_e[1]);
607 v_nrm(view_e[0], view_e[0]);
608 v_nrm(view_e[2], view_e[2]);
610 /* k = 0.0 view is at the ball. */
614 v_cpy(c0, fp->uv[ball].p);
615 v_cpy(p0, fp->uv[ball].p);
618 v_mad(p0, p0, view_e[1], view_dy);
619 v_mad(p0, p0, view_e[2], view_dz);
621 /* k = +1.0 view is s_view 0 */
623 if (k >= 0 && fp->base->wc > 0)
625 v_cpy(p1, fp->base->wv[0].p);
626 v_cpy(c1, fp->base->wv[0].q);
629 /* k = -1.0 view is s_view 1 */
631 if (k <= 0 && fp->base->wc > 1)
633 v_cpy(p1, fp->base->wv[1].p);
634 v_cpy(c1, fp->base->wv[1].q);
637 /* Interpolate the views. */
640 v_mad(view_p, p0, v, k * k);
643 v_mad(view_c, c0, v, k * k);
645 /* Orthonormalize the view basis. */
647 v_sub(view_e[2], view_p, view_c);
648 v_crs(view_e[0], view_e[1], view_e[2]);
649 v_crs(view_e[2], view_e[0], view_e[1]);
650 v_nrm(view_e[0], view_e[0]);
651 v_nrm(view_e[2], view_e[2]);
653 view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
656 void game_ball(int i)
665 for (ui = 0; ui < file.vary.uc; ui++)
667 file.vary.uv[ui].v[0] = 0.f;
668 file.vary.uv[ui].v[1] = 0.f;
669 file.vary.uv[ui].v[2] = 0.f;
671 file.vary.uv[ui].w[0] = 0.f;
672 file.vary.uv[ui].w[1] = 0.f;
673 file.vary.uv[ui].w[2] = 0.f;
677 void game_get_pos(float p[3], float e[3][3])
679 v_cpy(p, file.vary.uv[ball].p);
680 v_cpy(e[0], file.vary.uv[ball].e[0]);
681 v_cpy(e[1], file.vary.uv[ball].e[1]);
682 v_cpy(e[2], file.vary.uv[ball].e[2]);
685 void game_set_pos(float p[3], float e[3][3])
687 v_cpy(file.vary.uv[ball].p, p);
688 v_cpy(file.vary.uv[ball].e[0], e[0]);
689 v_cpy(file.vary.uv[ball].e[1], e[1]);
690 v_cpy(file.vary.uv[ball].e[2], e[2]);
693 /*---------------------------------------------------------------------------*/