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(&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
)
162 for (ui
= fp
->uc
- 1; ui
> 0; ui
--)
168 m_basis(M
, fp
->uv
[ui
].e
[0], fp
->uv
[ui
].e
[1], fp
->uv
[ui
].e
[2]);
170 glTranslatef(fp
->uv
[ui
].p
[0],
171 fp
->uv
[ui
].p
[1] + BALL_FUDGE
,
174 glScalef(fp
->uv
[ui
].r
,
178 glColor4fv(c_play
[ui
]);
188 glTranslatef(fp
->uv
[ui
].p
[0],
189 fp
->uv
[ui
].p
[1] - fp
->uv
[ui
].r
+ BALL_FUDGE
,
191 glScalef(fp
->uv
[ui
].r
,
195 glColor4f(c_play
[ui
][0],
197 c_play
[ui
][2], 0.5f
);
206 static void game_draw_goals(const struct s_file
*fp
, float rx
, float ry
)
210 for (zi
= 0; zi
< fp
->zc
; zi
++)
214 glTranslatef(fp
->zv
[zi
].p
[0],
223 static void game_draw_jumps(const struct s_file
*fp
)
227 for (ji
= 0; ji
< fp
->jc
; ji
++)
231 glTranslatef(fp
->jv
[ji
].p
[0],
235 glScalef(fp
->jv
[ji
].r
, 1.f
, fp
->jv
[ji
].r
);
242 static void game_draw_swchs(const struct s_file
*fp
)
246 for (xi
= 0; xi
< fp
->xc
; xi
++)
250 glTranslatef(fp
->xv
[xi
].p
[0],
254 glScalef(fp
->xv
[xi
].r
, 1.f
, fp
->xv
[xi
].r
);
255 swch_draw(fp
->xv
[xi
].f
);
261 /*---------------------------------------------------------------------------*/
263 void game_draw(int pose
)
265 const float light_p
[4] = { 8.f
, 32.f
, 8.f
, 1.f
};
267 const struct s_file
*fp
= &file
;
271 if (jump_b
) fov
*= 2.0f
* fabsf(jump_dt
- 0.5f
);
273 config_push_persp(fov
, 0.1f
, FAR_DIST
);
274 glPushAttrib(GL_LIGHTING_BIT
);
279 v_sub(v
, view_c
, view_p
);
281 rx
= V_DEG(fatan2f(-v
[1], fsqrtf(v
[0] * v
[0] + v
[2] * v
[2])));
282 ry
= V_DEG(fatan2f(+v
[0], -v
[2]));
284 glTranslatef(0.f
, 0.f
, -v_len(v
));
285 glRotatef(rx
, 1.f
, 0.f
, 0.f
);
286 glRotatef(ry
, 0.f
, 1.f
, 0.f
);
287 glTranslatef(-view_c
[0], -view_c
[1], -view_c
[2]);
289 /* Center the skybox about the position of the camera. */
293 glTranslatef(view_p
[0], view_p
[1], view_p
[2]);
298 glEnable(GL_LIGHTING
);
300 glLightfv(GL_LIGHT0
, GL_POSITION
, light_p
);
302 /* Draw the floor. */
304 if (pose
== 0) shad_draw_set(fp
->uv
[ball
].p
, fp
->uv
[ball
].r
);
305 sol_draw(fp
, config_get_d(CONFIG_SHADOW
));
306 if (pose
== 0) shad_draw_clr();
308 /* Draw the game elements. */
311 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
319 game_draw_goals(fp
, -rx
, -ry
);
328 /*---------------------------------------------------------------------------*/
330 void game_update_view(float dt
)
332 const float y
[3] = { 0.f
, 1.f
, 0.f
};
341 /* Center the view about the ball. */
343 v_cpy(view_c
, file
.uv
[ball
].p
);
344 v_inv(view_v
, file
.uv
[ball
].v
);
346 switch (config_get_d(CONFIG_CAMERA
))
349 /* Camera 2: View vector is given by view angle. */
351 view_e
[2][0] = fsinf(V_RAD(view_a
));
353 view_e
[2][2] = fcosf(V_RAD(view_a
));
359 /* View vector approaches the ball velocity vector. */
361 v_mad(e
, view_v
, y
, v_dot(view_v
, y
));
364 k
= v_dot(view_v
, view_v
);
366 v_sub(view_e
[2], view_p
, view_c
);
367 v_mad(view_e
[2], view_e
[2], view_v
, k
* dt
* 0.1f
);
370 /* Orthonormalize the basis of the view in its new position. */
372 v_crs(view_e
[0], view_e
[1], view_e
[2]);
373 v_crs(view_e
[2], view_e
[0], view_e
[1]);
374 v_nrm(view_e
[0], view_e
[0]);
375 v_nrm(view_e
[2], view_e
[2]);
377 /* The current view (dy, dz) approaches the ideal (view_dy, view_dz). */
379 v_sub(d
, view_p
, view_c
);
381 dy
= v_dot(view_e
[1], d
);
382 dz
= v_dot(view_e
[2], d
);
384 dy
+= (view_dy
- dy
) * s
;
385 dz
+= (view_dz
- dz
) * s
;
387 /* Compute the new view position. */
389 view_p
[0] = view_p
[1] = view_p
[2] = 0.f
;
391 v_mad(view_p
, view_c
, view_e
[1], dy
);
392 v_mad(view_p
, view_p
, view_e
[2], dz
);
394 view_a
= V_DEG(fatan2f(view_e
[2][0], view_e
[2][2]));
397 static int game_update_state(float dt
)
399 static float t
= 0.f
;
401 struct s_file
*fp
= &file
;
409 /* Test for a jump. */
411 if (jump_e
== 1 && jump_b
== 0 && sol_jump_test(fp
, jump_p
, ball
) == 1)
417 audio_play(AUD_JUMP
, 1.f
);
419 if (jump_e
== 0 && jump_b
== 0 && sol_jump_test(fp
, jump_p
, ball
) == 0)
422 /* Test for fall-out. */
424 if (fp
->uv
[ball
].p
[1] < -10.f
)
427 /* Test for a goal or stop. */
433 if (sol_goal_test(fp
, p
, ball
))
443 * On most hardware, rendering requires much more computing power than
444 * physics. Since physics takes less time than graphics, it make sense to
445 * detach the physics update time step from the graphics frame rate. By
446 * performing multiple physics updates for each graphics update, we get away
447 * with higher quality physics with little impact on overall performance.
449 * Toward this end, we establish a baseline maximum physics time step. If
450 * the measured frame time exceeds this maximum, we cut the time step in
451 * half, and do two updates. If THIS time step exceeds the maximum, we do
452 * four updates. And so on. In this way, the physics system is allowed to
453 * seek an optimal update rate independant of, yet in integral sync with, the
454 * graphics frame rate.
457 int game_step(const float g
[3], float dt
)
459 struct s_file
*fp
= &file
;
461 static float s
= 0.f
;
462 static float t
= 0.f
;
469 s
= (7.f
* s
+ dt
) / 8.f
;
480 fp
->uv
[ball
].p
[0] = jump_p
[0];
481 fp
->uv
[ball
].p
[1] = jump_p
[1];
482 fp
->uv
[ball
].p
[2] = jump_p
[2];
491 while (t
> MAX_DT
&& n
< MAX_DN
)
497 for (i
= 0; i
< n
; i
++)
499 d
= sol_step(fp
, g
, t
, ball
, &m
);
507 /* Mix the sound of a ball bounce. */
510 audio_play(AUD_BUMP
, (float) (b
- 0.5) * 2.0f
);
513 game_update_view(dt
);
514 return game_update_state(st
);
520 * HACK: The BALL_FUDGE here guarantees that a putt doesn't drive
521 * the ball too directly down toward a lump, triggering rolling
522 * friction too early and stopping the ball prematurely.
525 file
.uv
[ball
].v
[0] = -4.f
* view_e
[2][0] * view_m
;
526 file
.uv
[ball
].v
[1] = -4.f
* view_e
[2][1] * view_m
+ BALL_FUDGE
;
527 file
.uv
[ball
].v
[2] = -4.f
* view_e
[2][2] * view_m
;
532 /*---------------------------------------------------------------------------*/
534 void game_set_rot(int d
)
536 view_a
+= (float) (30.f
* d
) / config_get_d(CONFIG_MOUSE_SENSE
);
539 void game_clr_mag(void)
545 void game_set_mag(int d
)
547 view_m
-= (float) (1.f
* d
) / config_get_d(CONFIG_MOUSE_SENSE
);
553 void game_set_fly(float k
)
555 struct s_file
*fp
= &file
;
557 float x
[3] = { 1.f
, 0.f
, 0.f
};
558 float y
[3] = { 0.f
, 1.f
, 0.f
};
559 float z
[3] = { 0.f
, 0.f
, 1.f
};
560 float c0
[3] = { 0.f
, 0.f
, 0.f
};
561 float p0
[3] = { 0.f
, 0.f
, 0.f
};
562 float c1
[3] = { 0.f
, 0.f
, 0.f
};
563 float p1
[3] = { 0.f
, 0.f
, 0.f
};
570 /* k = 0.0 view is at the ball. */
574 v_cpy(c0
, fp
->uv
[ball
].p
);
575 v_cpy(p0
, fp
->uv
[ball
].p
);
578 v_mad(p0
, p0
, y
, view_dy
);
579 v_mad(p0
, p0
, z
, view_dz
);
581 /* k = +1.0 view is s_view 0 */
583 if (k
>= 0 && fp
->wc
> 0)
585 v_cpy(p1
, fp
->wv
[0].p
);
586 v_cpy(c1
, fp
->wv
[0].q
);
589 /* k = -1.0 view is s_view 1 */
591 if (k
<= 0 && fp
->wc
> 1)
593 v_cpy(p1
, fp
->wv
[1].p
);
594 v_cpy(c1
, fp
->wv
[1].q
);
597 /* Interpolate the views. */
600 v_mad(view_p
, p0
, v
, k
* k
);
603 v_mad(view_c
, c0
, v
, k
* k
);
605 /* Orthonormalize the view basis. */
607 v_sub(view_e
[2], view_p
, view_c
);
608 v_crs(view_e
[0], view_e
[1], view_e
[2]);
609 v_crs(view_e
[2], view_e
[0], view_e
[1]);
610 v_nrm(view_e
[0], view_e
[0]);
611 v_nrm(view_e
[2], view_e
[2]);
614 void game_ball(int i
)
623 for (ui
= 0; ui
< file
.uc
; ui
++)
625 file
.uv
[ui
].v
[0] = 0.f
;
626 file
.uv
[ui
].v
[1] = 0.f
;
627 file
.uv
[ui
].v
[2] = 0.f
;
629 file
.uv
[ui
].w
[0] = 0.f
;
630 file
.uv
[ui
].w
[1] = 0.f
;
631 file
.uv
[ui
].w
[2] = 0.f
;
635 void game_get_pos(float p
[3], float e
[3][3])
637 v_cpy(p
, file
.uv
[ball
].p
);
638 v_cpy(e
[0], file
.uv
[ball
].e
[0]);
639 v_cpy(e
[1], file
.uv
[ball
].e
[1]);
640 v_cpy(e
[2], file
.uv
[ball
].e
[2]);
643 void game_set_pos(float p
[3], float e
[3][3])
645 v_cpy(file
.uv
[ball
].p
, p
);
646 v_cpy(file
.uv
[ball
].e
[0], e
[0]);
647 v_cpy(file
.uv
[ball
].e
[1], e
[1]);
648 v_cpy(file
.uv
[ball
].e
[2], e
[2]);
651 /*---------------------------------------------------------------------------*/