2 * Copyright (C) 2003 Robert Kooima
4 * NEVERBALL 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.
32 /*---------------------------------------------------------------------------*/
34 static int game_state
= 0;
36 static struct s_file file
;
37 static struct s_file back
;
39 static float clock
= 0.f
; /* Clock time */
41 static float game_ix
; /* Input rotation about X axis */
42 static float game_iz
; /* Input rotation about Z axis */
43 static float game_rx
; /* Floor rotation about X axis */
44 static float game_rz
; /* Floor rotation about Z axis */
46 static float view_a
; /* Ideal view rotation about Y axis */
47 static float view_ry
; /* Angular velocity about Y axis */
48 static float view_dc
; /* Ideal view distance above ball */
49 static float view_dp
; /* Ideal view distance above ball */
50 static float view_dz
; /* Ideal view distance behind ball */
51 static float view_fov
; /* Field of view */
53 static float view_c
[3]; /* Current view center */
54 static float view_v
[3]; /* Current view vector */
55 static float view_p
[3]; /* Current view position */
56 static float view_e
[3][3]; /* Current view orientation */
59 static int goal_e
= 0; /* Goal enabled flag */
60 static float goal_k
= 0; /* Goal animation */
61 static int swch_e
= 1; /* Switching enabled flag */
62 static int jump_e
= 1; /* Jumping enabled flag */
63 static int jump_b
= 0; /* Jump-in-progress flag */
64 static float jump_dt
; /* Jump duration */
65 static float jump_p
[3]; /* Jump destination */
66 static float fade_k
= 0.0; /* Fade in/out level */
67 static float fade_d
= 0.0; /* Fade in/out direction */
69 /*---------------------------------------------------------------------------*/
71 static void view_init(void)
76 view_fov
= (float) config_get_d(CONFIG_VIEW_FOV
);
77 view_dp
= (float) config_get_d(CONFIG_VIEW_DP
) / 100.0f
;
78 view_dc
= (float) config_get_d(CONFIG_VIEW_DC
) / 100.0f
;
79 view_dz
= (float) config_get_d(CONFIG_VIEW_DZ
) / 100.0f
;
101 int game_init(const char *file_name
,
102 const char *back_name
,
103 const char *grad_name
, int t
, int e
)
105 clock
= (float) t
/ 100.f
;
115 /* Initialize jump and goal states. */
121 goal_k
= e
? 1.0f
: 0.0f
;
129 /* Initialise the level, background, particles, fade, and view. */
134 part_reset(GOAL_HEIGHT
);
136 back_init(grad_name
, config_get_d(CONFIG_GEOMETRY
));
138 if (sol_load(&back
, config_data(back_name
),
139 config_get_d(CONFIG_TEXTURES
), 0) &&
140 sol_load(&file
, config_data(file_name
),
141 config_get_d(CONFIG_TEXTURES
), config_get_d(CONFIG_SHADOW
)))
142 return (game_state
= 1);
144 return (game_state
= 0);
158 /*---------------------------------------------------------------------------*/
162 return (int) (clock
* 100.f
);
165 char *curr_intro(void)
167 return (file
.ac
> 0) ? file
.av
: NULL
;
170 /*---------------------------------------------------------------------------*/
172 static void game_draw_balls(const struct s_file
*fp
)
174 float c
[4] = { 1.0f
, 1.0f
, 1.0f
, 1.0f
};
177 m_basis(M
, fp
->uv
[0].e
[0], fp
->uv
[0].e
[1], fp
->uv
[0].e
[2]);
181 glTranslatef(fp
->uv
[0].p
[0],
182 fp
->uv
[0].p
[1] + BALL_FUDGE
,
185 glScalef(fp
->uv
[0].r
,
196 static void game_draw_coins(const struct s_file
*fp
)
198 float r
= 360.f
* SDL_GetTicks() / 1000.f
;
203 for (ci
= 0; ci
< fp
->cc
; ci
++)
204 if (fp
->cv
[ci
].n
> 0)
208 glTranslatef(fp
->cv
[ci
].p
[0],
211 glRotatef(r
, 0.0f
, 1.0f
, 0.0f
);
212 coin_draw(fp
->cv
[ci
].n
, r
);
220 static void game_draw_goals(const struct s_file
*fp
, float rx
, float ry
)
225 for (zi
= 0; zi
< fp
->zc
; zi
++)
229 glTranslatef(fp
->zv
[zi
].p
[0],
233 part_draw_goal(rx
, ry
, fp
->zv
[zi
].r
, goal_k
);
235 glScalef(fp
->zv
[zi
].r
, goal_k
, fp
->zv
[zi
].r
);
242 static void game_draw_jumps(const struct s_file
*fp
)
246 for (ji
= 0; ji
< fp
->jc
; ji
++)
250 glTranslatef(fp
->jv
[ji
].p
[0],
254 glScalef(fp
->jv
[ji
].r
, 1.f
, fp
->jv
[ji
].r
);
261 static void game_draw_swchs(const struct s_file
*fp
)
265 for (xi
= 0; xi
< fp
->xc
; xi
++)
269 glTranslatef(fp
->xv
[xi
].p
[0],
273 glScalef(fp
->xv
[xi
].r
, 1.f
, fp
->xv
[xi
].r
);
274 swch_draw(fp
->xv
[xi
].f
);
280 /*---------------------------------------------------------------------------*/
282 static void game_refl_all(int s
)
284 const float *ball_p
= file
.uv
->p
;
288 /* Rotate the environment about the position of the ball. */
290 glTranslatef(+ball_p
[0], +ball_p
[1], +ball_p
[2]);
291 glRotatef(-game_rz
, view_e
[2][0], view_e
[2][1], view_e
[2][2]);
292 glRotatef(-game_rx
, view_e
[0][0], view_e
[0][1], view_e
[0][2]);
293 glTranslatef(-ball_p
[0], -ball_p
[1], -ball_p
[2]);
295 /* Draw the floor. */
302 /*---------------------------------------------------------------------------*/
304 static void game_draw_light(void)
306 const float light_p
[2][4] = {
307 { -8.0f
, +32.0f
, -8.0f
, 1.0f
},
308 { +8.0f
, +32.0f
, +8.0f
, 1.0f
},
310 const float light_c
[2][4] = {
311 { 1.0f
, 0.8f
, 0.8f
, 1.0f
},
312 { 0.8f
, 1.0f
, 0.8f
, 1.0f
},
315 /* Configure the lighting. */
318 glLightfv(GL_LIGHT0
, GL_POSITION
, light_p
[0]);
319 glLightfv(GL_LIGHT0
, GL_DIFFUSE
, light_c
[0]);
320 glLightfv(GL_LIGHT0
, GL_SPECULAR
, light_c
[0]);
323 glLightfv(GL_LIGHT1
, GL_POSITION
, light_p
[1]);
324 glLightfv(GL_LIGHT1
, GL_DIFFUSE
, light_c
[1]);
325 glLightfv(GL_LIGHT1
, GL_SPECULAR
, light_c
[1]);
328 static void game_draw_back(int pose
, int d
, const float p
[3])
330 float c
[4] = { 1.0f
, 1.0f
, 1.0f
, 1.0f
};
331 float t
= SDL_GetTicks() / 1000.f
+ 120.0f
;
337 glRotatef(game_rz
* 2, view_e
[2][0], view_e
[2][1], view_e
[2][2]);
338 glRotatef(game_rx
* 2, view_e
[0][0], view_e
[0][1], view_e
[0][2]);
341 glTranslatef(p
[0], p
[1], p
[2]);
344 if (config_get_d(CONFIG_BACKGROUND
))
346 /* Draw all background layers back to front. */
348 sol_back(&back
, BACK_DIST
, FAR_DIST
, t
);
350 sol_back(&back
, 0, BACK_DIST
, t
);
352 /* Draw all foreground geometry in the background file. */
361 static void game_draw_fore(int pose
, float rx
, float ry
, int d
, const float p
[3])
363 const float *ball_p
= file
.uv
->p
;
364 const float ball_r
= file
.uv
->r
;
366 glPushAttrib(GL_LIGHTING_BIT
| GL_COLOR_BUFFER_BIT
);
370 /* Rotate the environment about the position of the ball. */
372 glTranslatef(+ball_p
[0], +ball_p
[1] * d
, +ball_p
[2]);
373 glRotatef(-game_rz
* d
, view_e
[2][0], view_e
[2][1], view_e
[2][2]);
374 glRotatef(-game_rx
* d
, view_e
[0][0], view_e
[0][1], view_e
[0][2]);
375 glTranslatef(-ball_p
[0], -ball_p
[1] * d
, -ball_p
[2]);
386 glEnable(GL_CLIP_PLANE0
);
387 glClipPlane(GL_CLIP_PLANE0
, e
);
390 /* Draw the floor. */
394 if (config_get_d(CONFIG_SHADOW
))
396 shad_draw_set(ball_p
, ball_r
);
401 /* Draw the game elements. */
404 glBlendFunc(GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
408 part_draw_coin(-rx
* d
, -ry
);
409 game_draw_coins(&file
);
410 game_draw_balls(&file
);
412 game_draw_goals(&file
, -rx
* d
, -ry
);
413 game_draw_jumps(&file
);
414 game_draw_swchs(&file
);
416 glDisable(GL_CLIP_PLANE0
);
423 void game_draw(int pose
, float st
)
425 float fov
= view_fov
;
427 if (jump_b
) fov
*= 2.f
* fabsf(jump_dt
- 0.5);
431 config_push_persp(fov
, 0.1f
, FAR_DIST
);
442 /* Compute and apply the view. */
444 v_sub(v
, view_c
, view_p
);
446 rx
= V_DEG(fatan2f(-v
[1], fsqrtf(v
[0] * v
[0] + v
[2] * v
[2])));
447 ry
= V_DEG(fatan2f(+v
[0], -v
[2])) + st
;
449 glTranslatef(0.f
, 0.f
, -v_len(v
));
450 glRotatef(rx
, 1.f
, 0.f
, 0.f
);
451 glRotatef(ry
, 0.f
, 1.f
, 0.f
);
452 glTranslatef(-view_c
[0], -view_c
[1], -view_c
[2]);
454 if (config_get_d(CONFIG_REFLECTION
))
456 /* Draw the mirror only into the stencil buffer. */
458 glDisable(GL_DEPTH_TEST
);
459 glEnable(GL_STENCIL_TEST
);
460 glStencilFunc(GL_ALWAYS
, 1, 0xFFFFFFFF);
461 glStencilOp(GL_REPLACE
, GL_REPLACE
, GL_REPLACE
);
462 glColorMask(GL_FALSE
, GL_FALSE
, GL_FALSE
, GL_FALSE
);
466 /* Draw the scene reflected into color and depth buffers. */
468 glColorMask(GL_TRUE
, GL_TRUE
, GL_TRUE
, GL_TRUE
);
469 glStencilOp(GL_KEEP
, GL_KEEP
, GL_KEEP
);
470 glStencilFunc(GL_EQUAL
, 1, 0xFFFFFFFF);
471 glEnable(GL_DEPTH_TEST
);
476 glScalef(+1.f
, -1.f
, +1.f
);
479 game_draw_back(pose
, -1, pdn
);
480 game_draw_fore(pose
, rx
, ry
, -1, pdn
);
485 glDisable(GL_STENCIL_TEST
);
488 /* Draw the scene normally. */
491 game_refl_all(pose
? 0 : config_get_d(CONFIG_SHADOW
));
492 game_draw_back(pose
, +1, pup
);
493 game_draw_fore(pose
, rx
, ry
, +1, pup
);
498 /* Draw the fade overlay. */
504 /*---------------------------------------------------------------------------*/
506 static void game_update_grav(float h
[3], const float g
[3])
508 struct s_file
*fp
= &file
;
511 float y
[3] = { 0.f
, 1.f
, 0.f
};
517 /* Compute the gravity vector from the given world rotations. */
519 v_sub(z
, view_p
, fp
->uv
->p
);
525 m_rot (Z
, z
, V_RAD(game_rz
));
526 m_rot (X
, x
, V_RAD(game_rx
));
531 static void game_update_view(float dt
)
533 float dc
= view_dc
* (jump_b
? 2.0f
* fabsf(jump_dt
- 0.5f
) : 1.0f
);
534 float dx
= view_ry
* dt
* 5.0f
;
537 view_a
+= view_ry
* dt
* 90.f
;
539 /* Center the view about the ball. */
541 v_cpy(view_c
, file
.uv
->p
);
542 v_inv(view_v
, file
.uv
->v
);
544 switch (config_get_d(CONFIG_CAMERA
))
546 case 1: /* Camera 1: Viewpoint chases the ball position. */
548 v_sub(view_e
[2], view_p
, view_c
);
551 case 2: /* Camera 2: View vector is given by view angle. */
553 view_e
[2][0] = fsinf(V_RAD(view_a
));
555 view_e
[2][2] = fcosf(V_RAD(view_a
));
561 default: /* Default: View vector approaches the ball velocity vector. */
563 k
= v_dot(view_v
, view_v
);
565 v_sub(view_e
[2], view_p
, view_c
);
566 v_mad(view_e
[2], view_e
[2], view_v
, k
* dt
/ 4);
571 /* Orthonormalize the basis of the view in its new position. */
573 v_crs(view_e
[0], view_e
[1], view_e
[2]);
574 v_crs(view_e
[2], view_e
[0], view_e
[1]);
575 v_nrm(view_e
[0], view_e
[0]);
576 v_nrm(view_e
[2], view_e
[2]);
578 /* Compute the new view position. */
580 k
= 1.0f
+ v_dot(view_e
[2], view_v
) / 10.0f
;
582 view_k
= view_k
+ (k
- view_k
) * dt
;
584 if (view_k
< 0.5) view_k
= 0.5;
586 v_cpy(view_p
, file
.uv
->p
);
587 v_mad(view_p
, view_p
, view_e
[0], dx
* view_k
);
588 v_mad(view_p
, view_p
, view_e
[1], view_dp
* view_k
);
589 v_mad(view_p
, view_p
, view_e
[2], view_dz
* view_k
);
591 /* Compute the new view center. */
593 v_cpy(view_c
, file
.uv
->p
);
594 v_mad(view_c
, view_c
, view_e
[1], dc
);
596 /* Note the current view angle. */
598 view_a
= V_DEG(fatan2f(view_e
[2][0], view_e
[2][2]));
601 static void game_update_time(float dt
, int b
)
603 int tick
= (int) floor(clock
);
604 int tock
= (int) floor(clock
* 2);
606 if (goal_e
&& goal_k
< 1.0f
)
609 /* The ticking clock. */
618 if (0 < tick
&& tick
<= 10 && tick
== (int) ceil(clock
))
620 audio_play(AUD_TICK
, 1.f
);
621 hud_time_pulse(1.50);
623 else if (0 < tock
&& tock
<= 10 && tock
== (int) ceil(clock
* 2))
625 audio_play(AUD_TOCK
, 1.f
);
626 hud_time_pulse(1.25);
631 static int game_update_state(void)
633 struct s_file
*fp
= &file
;
638 /* Test for a coin grab and a possible 1UP. */
640 if ((n
= sol_coin_test(fp
, p
, COIN_RADIUS
)) > 0)
649 /* Test for a switch. */
651 if ((swch_e
= sol_swch_test(fp
, swch_e
, 0)) != e
&& e
)
652 audio_play(AUD_SWITCH
, 1.f
);
654 /* Test for a jump. */
656 if (jump_e
== 1 && jump_b
== 0 && sol_jump_test(fp
, jump_p
, 0) == 1)
662 audio_play(AUD_JUMP
, 1.f
);
664 if (jump_e
== 0 && jump_b
== 0 && sol_jump_test(fp
, jump_p
, 0) == 0)
667 /* Test for a goal. */
669 if (goal_e
&& sol_goal_test(fp
, p
, 0))
672 /* Test for time-out. */
677 /* Test for fall-out. */
679 if (fp
->uv
[0].p
[1] < fp
->vv
[0].p
[1])
686 * On most hardware, rendering requires much more computing power than
687 * physics. Since physics takes less time than graphics, it make sense to
688 * detach the physics update time step from the graphics frame rate. By
689 * performing multiple physics updates for each graphics update, we get away
690 * with higher quality physics with little impact on overall performance.
692 * Toward this end, we establish a baseline maximum physics time step. If
693 * the measured frame time exceeds this maximum, we cut the time step in
694 * half, and do two updates. If THIS time step exceeds the maximum, we do
695 * four updates. And so on. In this way, the physics system is allowed to
696 * seek an optimal update rate independant of, yet in integral sync with, the
697 * graphics frame rate.
700 int game_step(const float g
[3], float dt
, int bt
)
702 struct s_file
*fp
= &file
;
714 /* Smooth jittery or discontinuous input. */
718 game_rx
+= (game_ix
- game_rx
) * t
/ RESPONSE
;
719 game_rz
+= (game_iz
- game_rz
) * t
/ RESPONSE
;
727 game_update_grav(h
, g
);
738 fp
->uv
[0].p
[0] = jump_p
[0];
739 fp
->uv
[0].p
[1] = jump_p
[1];
740 fp
->uv
[0].p
[2] = jump_p
[2];
749 while (t
> MAX_DT
&& n
< MAX_DN
)
755 for (i
= 0; i
< n
; i
++)
756 if (b
< (d
= sol_step(fp
, h
, t
, 0, NULL
)))
759 /* Mix the sound of a ball bounce. */
762 audio_play(AUD_BUMP
, (b
- 0.5f
) * 2.0f
);
766 game_update_view(dt
);
767 game_update_time(dt
, bt
);
769 return game_update_state();
774 /*---------------------------------------------------------------------------*/
776 void game_set_x(int k
)
778 game_ix
= -20.f
* k
/ JOY_MAX
;
781 void game_set_z(int k
)
783 game_iz
= +20.f
* k
/ JOY_MAX
;
786 void game_set_pos(int x
, int y
)
790 game_ix
+= 40.f
* y
/ config_get_d(CONFIG_MOUSE_SENSE
);
791 game_iz
+= 40.f
* x
/ config_get_d(CONFIG_MOUSE_SENSE
);
793 if (game_ix
> +bound
) game_ix
= +bound
;
794 if (game_ix
< -bound
) game_ix
= -bound
;
795 if (game_iz
> +bound
) game_iz
= +bound
;
796 if (game_iz
< -bound
) game_iz
= -bound
;
799 void game_set_rot(float r
)
804 /*---------------------------------------------------------------------------*/
806 void game_set_fly(float k
)
808 struct s_file
*fp
= &file
;
810 float x
[3] = { 1.f
, 0.f
, 0.f
};
811 float y
[3] = { 0.f
, 1.f
, 0.f
};
812 float z
[3] = { 0.f
, 0.f
, 1.f
};
813 float c0
[3] = { 0.f
, 0.f
, 0.f
};
814 float p0
[3] = { 0.f
, 0.f
, 0.f
};
815 float c1
[3] = { 0.f
, 0.f
, 0.f
};
816 float p1
[3] = { 0.f
, 0.f
, 0.f
};
823 /* k = 0.0 view is at the ball. */
827 v_cpy(c0
, fp
->uv
[0].p
);
828 v_cpy(p0
, fp
->uv
[0].p
);
831 v_mad(p0
, p0
, y
, view_dp
);
832 v_mad(p0
, p0
, z
, view_dz
);
833 v_mad(c0
, c0
, y
, view_dc
);
835 /* k = +1.0 view is s_view 0 */
837 if (k
>= 0 && fp
->wc
> 0)
839 v_cpy(p1
, fp
->wv
[0].p
);
840 v_cpy(c1
, fp
->wv
[0].q
);
843 /* k = -1.0 view is s_view 1 */
845 if (k
<= 0 && fp
->wc
> 1)
847 v_cpy(p1
, fp
->wv
[1].p
);
848 v_cpy(c1
, fp
->wv
[1].q
);
851 /* Interpolate the views. */
854 v_mad(view_p
, p0
, v
, k
* k
);
857 v_mad(view_c
, c0
, v
, k
* k
);
859 /* Orthonormalize the view basis. */
861 v_sub(view_e
[2], view_p
, view_c
);
862 v_crs(view_e
[0], view_e
[1], view_e
[2]);
863 v_crs(view_e
[2], view_e
[0], view_e
[1]);
864 v_nrm(view_e
[0], view_e
[0]);
865 v_nrm(view_e
[2], view_e
[2]);
868 void game_look(float phi
, float theta
)
870 view_c
[0] = view_p
[0] + fsinf(V_RAD(theta
)) * fcosf(V_RAD(phi
));
871 view_c
[1] = view_p
[1] + fsinf(V_RAD(phi
));
872 view_c
[2] = view_p
[2] - fcosf(V_RAD(theta
)) * fcosf(V_RAD(phi
));
875 /*---------------------------------------------------------------------------*/
877 void game_kill_fade(void)
883 void game_step_fade(float dt
)
885 if ((fade_k
< 1.0f
&& fade_d
> 0.0f
) ||
886 (fade_k
> 0.0f
&& fade_d
< 0.0f
))
887 fade_k
+= fade_d
* dt
;
901 void game_fade(float d
)
906 /*---------------------------------------------------------------------------*/
908 int put_game_state(FILE *fout
)
912 /* Write the view and tilt state. */
914 put_float(fout
, &game_rx
);
915 put_float(fout
, &game_rz
);
916 put_array(fout
, view_c
, 3);
917 put_array(fout
, view_p
, 3);
919 /* Write the game simulation state. */
921 put_file_state(fout
, &file
);
928 int get_game_state(FILE *fin
)
932 /* Read the view and tilt state. */
934 get_float(fin
, &game_rx
);
935 get_float(fin
, &game_rz
);
936 get_array(fin
, view_c
, 3);
937 get_array(fin
, view_p
, 3);
939 /* Read the game simulation state. */
941 get_file_state(fin
, &file
);
943 return (feof(fin
) ? 0 : 1);
948 /*---------------------------------------------------------------------------*/