Import from neverball-1.2.6-9.tar.gz
[neverball-archive.git] / ball / game.c
blob0fce8537290f1a1a99a4582de6b670216c3ccb7a
1 /*
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.
15 #include <SDL.h>
16 #include <math.h>
18 #include "glext.h"
19 #include "game.h"
20 #include "vec3.h"
21 #include "geom.h"
22 #include "back.h"
23 #include "part.h"
24 #include "hud.h"
25 #include "image.h"
26 #include "audio.h"
27 #include "solid.h"
28 #include "level.h"
29 #include "config.h"
31 /*---------------------------------------------------------------------------*/
33 static struct s_file file;
34 static struct s_file back;
36 static float clock = 0.f; /* Clock time */
38 static float game_ix; /* Input rotation about X axis */
39 static float game_iz; /* Input rotation about Z axis */
40 static float game_rx; /* Floor rotation about X axis */
41 static float game_rz; /* Floor rotation about Z axis */
43 static float view_a; /* Ideal view rotation about Y axis */
44 static float view_ry; /* Angular velocity about Y axis */
45 static float view_dc; /* Ideal view distance above ball */
46 static float view_dp; /* Ideal view distance above ball */
47 static float view_dz; /* Ideal view distance behind ball */
48 static float view_fov; /* Field of view */
50 static float view_c[3]; /* Current view center */
51 static float view_v[3]; /* Current view vector */
52 static float view_p[3]; /* Current view position */
53 static float view_e[3][3]; /* Current view orientation */
55 static int swch_e = 1; /* Switching enabled flag */
56 static int jump_e = 1; /* Jumping enabled flag */
57 static int jump_b = 0; /* Jump-in-progress flag */
58 static float jump_dt; /* Jump duration */
59 static float jump_p[3]; /* Jump destination */
61 static GLuint shadow_text; /* Shadow texture object */
63 /*---------------------------------------------------------------------------*/
65 static void view_init(void)
67 view_a = 0.f;
68 view_ry = 0.f;
70 view_fov = (float) config_get(CONFIG_VIEW_FOV);
71 view_dp = (float) config_get(CONFIG_VIEW_DP) / 100.0f;
72 view_dc = (float) config_get(CONFIG_VIEW_DC) / 100.0f;
73 view_dz = (float) config_get(CONFIG_VIEW_DZ) / 100.0f;
75 view_c[0] = 0.f;
76 view_c[1] = view_dc;
77 view_c[2] = 0.f;
79 view_p[0] = 0.f;
80 view_p[1] = view_dp;
81 view_p[2] = view_dz;
83 view_e[0][0] = 1.f;
84 view_e[0][1] = 0.f;
85 view_e[0][2] = 0.f;
86 view_e[1][0] = 0.f;
87 view_e[1][1] = 1.f;
88 view_e[1][2] = 0.f;
89 view_e[2][0] = 0.f;
90 view_e[2][1] = 0.f;
91 view_e[2][2] = 1.f;
94 int game_init(const char *file_name, const char *back_name, int t)
96 game_ix = 0.f;
97 game_iz = 0.f;
98 game_rx = 0.f;
99 game_rz = 0.f;
101 jump_e = 1;
102 jump_b = 0;
104 view_init();
105 part_init(GOAL_HEIGHT);
107 hud_ball_pulse(0.f);
108 hud_time_pulse(0.f);
109 hud_coin_pulse(0.f);
111 clock = (float) t / 100.f;
113 shadow_text = make_image_from_file(NULL, NULL, NULL, NULL, IMG_SHADOW);
115 if (config_get(CONFIG_SHADOW) == 2)
117 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
118 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
121 return (sol_load(&back, back_name, config_get(CONFIG_TEXTURES), 0) &&
122 sol_load(&file, file_name, config_get(CONFIG_TEXTURES),
123 config_get(CONFIG_SHADOW)));
126 void game_free(void)
128 if (glIsTexture(shadow_text))
129 glDeleteTextures(1, &shadow_text);
131 sol_free(&file);
132 sol_free(&back);
133 part_free();
136 /*---------------------------------------------------------------------------*/
138 int curr_clock(void)
140 return (int) (clock * 100.f);
143 char *curr_intro(void)
145 return (file.ac > 0) ? file.av : NULL;
148 /*---------------------------------------------------------------------------*/
150 static void game_draw_balls(const struct s_file *fp)
152 float c[4] = { 1.0f, 1.0f, 1.0f, 0.7f };
153 float M[16];
155 m_basis(M, fp->uv[0].e[0], fp->uv[0].e[1], fp->uv[0].e[2]);
157 glPushMatrix();
159 glTranslatef(fp->uv[0].p[0],
160 fp->uv[0].p[1] + BALL_FUDGE,
161 fp->uv[0].p[2]);
162 glMultMatrixf(M);
163 glScalef(fp->uv[0].r,
164 fp->uv[0].r,
165 fp->uv[0].r);
167 glColor4fv(c);
169 ball_draw();
171 glPopMatrix();
174 static void game_draw_coins(const struct s_file *fp)
176 float r = 360.f * SDL_GetTicks() / 1000.f;
177 int ci;
179 coin_push();
181 for (ci = 0; ci < fp->cc; ci++)
182 if (fp->cv[ci].n > 0)
184 glPushMatrix();
186 glTranslatef(fp->cv[ci].p[0],
187 fp->cv[ci].p[1],
188 fp->cv[ci].p[2]);
189 glRotatef(r, 0.0f, 1.0f, 0.0f);
190 coin_draw(fp->cv[ci].n, r);
192 glPopMatrix();
195 coin_pull();
198 static void game_draw_goals(const struct s_file *fp, float rx, float ry)
200 int zi;
202 for (zi = 0; zi < fp->zc; zi++)
204 glPushMatrix();
206 glTranslatef(fp->zv[zi].p[0],
207 fp->zv[zi].p[1],
208 fp->zv[zi].p[2]);
210 part_draw_goal(rx, ry, fp->zv[zi].r);
212 glScalef(fp->zv[zi].r, 1.f, fp->zv[zi].r);
213 goal_draw();
215 glPopMatrix();
219 static void game_draw_jumps(const struct s_file *fp)
221 int ji;
223 for (ji = 0; ji < fp->jc; ji++)
225 glPushMatrix();
227 glTranslatef(fp->jv[ji].p[0],
228 fp->jv[ji].p[1],
229 fp->jv[ji].p[2]);
231 glScalef(fp->jv[ji].r, 1.f, fp->jv[ji].r);
232 jump_draw();
234 glPopMatrix();
238 static void game_draw_swchs(const struct s_file *fp)
240 int xi;
242 for (xi = 0; xi < fp->xc; xi++)
244 glPushMatrix();
246 glTranslatef(fp->xv[xi].p[0],
247 fp->xv[xi].p[1],
248 fp->xv[xi].p[2]);
250 glScalef(fp->xv[xi].r, 1.f, fp->xv[xi].r);
251 swch_draw(fp->xv[xi].f);
253 glPopMatrix();
257 /*---------------------------------------------------------------------------*/
260 * A note about lighting and shadow: technically speaking, it's wrong.
261 * The light position and shadow projection behave as if the
262 * light-source rotates with the floor. However, the skybox does not
263 * rotate, thus the light should also remain stationary.
265 * The correct behavior would eliminate a significant 3D cue: the
266 * shadow of the ball indicates the ball's position relative to the
267 * floor even when the ball is in the air. This was the motivating
268 * idea behind the shadow in the first place, so correct shadow
269 * projection would only magnify the problem.
272 static void game_set_shadow(const struct s_file *fp)
274 const float *ball_p = fp->uv->p;
275 const float ball_r = fp->uv->r;
277 if (config_get(CONFIG_SHADOW))
279 glActiveTexture(GL_TEXTURE1);
280 glMatrixMode(GL_TEXTURE);
282 float k = 0.5f / ball_r;
284 glEnable(GL_TEXTURE_2D);
286 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
287 glBindTexture(GL_TEXTURE_2D, shadow_text);
289 glLoadIdentity();
290 glTranslatef(0.5f - ball_p[0] * k,
291 0.5f - ball_p[2] * k, 0.f);
292 glScalef(k, k, 1.f);
294 glMatrixMode(GL_MODELVIEW);
295 glActiveTexture(GL_TEXTURE0);
299 static void game_clr_shadow(void)
301 if (config_get(CONFIG_SHADOW))
303 glActiveTexture(GL_TEXTURE1);
305 glDisable(GL_TEXTURE_2D);
307 glActiveTexture(GL_TEXTURE0);
311 /*---------------------------------------------------------------------------*/
313 static void game_refl_all(int s)
315 const float *ball_p = file.uv->p;
317 glPushMatrix();
319 /* Rotate the environment about the position of the ball. */
321 glTranslatef(+ball_p[0], +ball_p[1], +ball_p[2]);
322 glRotatef(-game_rz, view_e[2][0], view_e[2][1], view_e[2][2]);
323 glRotatef(-game_rx, view_e[0][0], view_e[0][1], view_e[0][2]);
324 glTranslatef(-ball_p[0], -ball_p[1], -ball_p[2]);
326 /* Draw the floor. */
328 if (s) game_set_shadow(&file);
329 sol_refl(&file, s);
330 if (s) game_clr_shadow();
332 glPopMatrix();
335 /*---------------------------------------------------------------------------*/
337 static void game_draw_light(void)
339 const float light_p[2][4] = {
340 { -8.0f, +32.0f, -8.0f, 1.0f },
341 { +8.0f, +32.0f, +8.0f, 1.0f },
343 const float light_c[2][4] = {
344 { 1.0f, 0.8f, 0.8f, 1.0f },
345 { 0.8f, 1.0f, 0.8f, 1.0f },
348 /* Configure the lighting. */
350 glEnable(GL_LIGHT0);
351 glLightfv(GL_LIGHT0, GL_POSITION, light_p[0]);
352 glLightfv(GL_LIGHT0, GL_DIFFUSE, light_c[0]);
353 glLightfv(GL_LIGHT0, GL_SPECULAR, light_c[0]);
355 glEnable(GL_LIGHT1);
356 glLightfv(GL_LIGHT1, GL_POSITION, light_p[1]);
357 glLightfv(GL_LIGHT1, GL_DIFFUSE, light_c[1]);
358 glLightfv(GL_LIGHT1, GL_SPECULAR, light_c[1]);
363 static void game_draw_back(int pose, float rx, float ry, int d, const float p[3])
365 float o[3] = { 0.0f, 0.0f, 0.0f };
366 float c[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
368 glPushAttrib(GL_FOG_BIT);
369 glPushMatrix();
371 if (d < 0)
373 glRotatef(game_rz * 2, view_e[2][0], view_e[2][1], view_e[2][2]);
374 glRotatef(game_rx * 2, view_e[0][0], view_e[0][1], view_e[0][2]);
377 glTranslatef(p[0], p[1], p[2]);
378 game_clr_shadow();
379 glColor3f(1.0f, 1.0f, 1.0f);
381 /* Draw all background layers back to front. */
383 sol_back(&back, o, BACK_DIST, FAR_DIST);
384 back_draw(0);
385 sol_back(&back, o, 0, BACK_DIST);
387 /* Draw all foreground geometry in the background file. */
389 glEnable(GL_FOG);
390 glFogf(GL_FOG_START, BACK_DIST / 2.0f);
391 glFogf(GL_FOG_END, BACK_DIST);
392 glFogfv(GL_FOG_COLOR, c);
394 sol_draw(&back, 0);
396 glPopMatrix();
397 glPopAttrib();
400 static void game_draw_fore(int pose, float rx, float ry, int d, const float p[3])
402 const float *ball_p = file.uv->p;
404 glPushAttrib(GL_LIGHTING_BIT);
405 glPushAttrib(GL_COLOR_BUFFER_BIT);
407 glPushMatrix();
409 /* Rotate the environment about the position of the ball. */
411 glTranslatef(+ball_p[0], +ball_p[1] * d, +ball_p[2]);
412 glRotatef(-game_rz * d, view_e[2][0], view_e[2][1], view_e[2][2]);
413 glRotatef(-game_rx * d, view_e[0][0], view_e[0][1], view_e[0][2]);
414 glTranslatef(-ball_p[0], -ball_p[1] * d, -ball_p[2]);
416 if (d < 0)
418 GLdouble e[4];
420 e[0] = +0;
421 e[1] = +1;
422 e[2] = +0;
423 e[3] = -0.00001;
425 glEnable(GL_CLIP_PLANE0);
426 glClipPlane(GL_CLIP_PLANE0, e);
429 /* Draw the floor. */
431 if (pose == 0) game_set_shadow(&file);
432 sol_draw(&file, config_get(CONFIG_SHADOW));
433 if (pose == 0) game_clr_shadow();
435 /* Draw the game elements. */
437 glEnable(GL_BLEND);
438 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
440 if (pose == 0)
442 part_draw_coin(-rx * d, -ry);
443 game_draw_coins(&file);
444 game_draw_balls(&file);
446 game_draw_goals(&file, -rx * d, -ry);
447 game_draw_jumps(&file);
448 game_draw_swchs(&file);
450 glDisable(GL_CLIP_PLANE0);
452 glPopMatrix();
454 glPopAttrib();
455 glPopAttrib();
458 void game_draw(int pose, float st)
460 float fov = view_fov;
462 if (jump_b) fov *= 2.f * fabsf(jump_dt - 0.5);
464 config_push_persp(fov, 0.1f, FAR_DIST);
465 glPushMatrix();
467 float v[3], rx, ry;
468 float pup[3];
469 float pdn[3];
471 v_cpy(pup, view_p);
472 v_cpy(pdn, view_p);
473 pdn[1] = -pdn[1];
475 /* Compute and apply the view. */
477 v_sub(v, view_c, view_p);
479 rx = V_DEG(fatan2f(-v[1], fsqrtf(v[0] * v[0] + v[2] * v[2])));
480 ry = V_DEG(fatan2f(+v[0], -v[2])) + st;
482 glTranslatef(0.f, 0.f, -v_len(v));
483 glRotatef(rx, 1.f, 0.f, 0.f);
484 glRotatef(ry, 0.f, 1.f, 0.f);
485 glTranslatef(-view_c[0], -view_c[1], -view_c[2]);
487 if (config_get(CONFIG_REFLECTION))
489 /* Draw the mirror only into the stencil buffer. */
491 glDisable(GL_DEPTH_TEST);
492 glEnable(GL_STENCIL_TEST);
493 glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);
494 glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
495 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
497 game_refl_all(0);
499 /* Draw the scene reflected into color and depth buffers. */
501 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
502 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
503 glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF);
504 glEnable(GL_DEPTH_TEST);
506 glFrontFace(GL_CW);
507 glPushMatrix();
509 glScalef(+1.f, -1.f, +1.f);
511 game_draw_light();
512 game_draw_back(pose, rx, ry, -1, pdn);
513 game_draw_fore(pose, rx, ry, -1, pdn);
515 glPopMatrix();
516 glFrontFace(GL_CCW);
518 glDisable(GL_STENCIL_TEST);
521 /* Draw the scene normally. */
523 game_draw_light();
524 game_refl_all(config_get(CONFIG_SHADOW));
525 game_draw_back(pose, rx, ry, +1, pup);
526 game_draw_fore(pose, rx, ry, +1, pup);
528 glPopMatrix();
529 config_pop_matrix();
532 /*---------------------------------------------------------------------------*/
534 static void game_update_grav(float h[3], const float g[3])
536 struct s_file *fp = &file;
538 float x[3];
539 float y[3] = { 0.f, 1.f, 0.f };
540 float z[3];
541 float X[16];
542 float Z[16];
543 float M[16];
545 /* Compute the gravity vector from the given world rotations. */
547 v_sub(z, view_p, fp->uv->p);
548 v_crs(x, y, z);
549 v_crs(z, x, y);
550 v_nrm(x, x);
551 v_nrm(z, z);
553 m_rot (Z, z, V_RAD(game_rz));
554 m_rot (X, x, V_RAD(game_rx));
555 m_mult(M, Z, X);
556 m_vxfm(h, M, g);
559 static void game_update_view(float dt)
561 const float y[3] = { 0.f, 1.f, 0.f };
563 float dc = view_dc * (jump_b ? 2.0f * fabsf(jump_dt - 0.5f) : 1.0f);
564 float dx = view_ry * dt * 5.0f;
565 float k;
566 float e[3];
567 float s = 4.f * dt;
569 view_a += view_ry * dt * 90.f;
571 /* Center the view about the ball. */
573 v_cpy(view_c, file.uv->p);
574 v_inv(view_v, file.uv->v);
576 switch (config_get(CONFIG_CAMERA))
578 case 1:
579 /* Camera 1: Viewpoint chases the ball position. */
581 v_sub(view_e[2], view_p, view_c);
582 break;
584 case 2:
585 /* Camera 2: View vector is given by view angle. */
587 view_e[2][0] = fsinf(V_RAD(view_a));
588 view_e[2][1] = 0.f;
589 view_e[2][2] = fcosf(V_RAD(view_a));
591 dx = 0.0f;
592 s = 8.0f * dt;
593 break;
595 default:
596 /* Default: View vector approaches the ball velocity vector. */
598 v_mad(e, view_v, y, v_dot(view_v, y));
599 v_inv(e, e);
601 k = v_dot(view_v, view_v);
603 v_sub(view_e[2], view_p, view_c);
604 v_mad(view_e[2], view_e[2], view_v, k * dt * 0.25f);
606 break;
609 /* Orthonormalize the basis of the view in its new position. */
611 v_crs(view_e[0], view_e[1], view_e[2]);
612 v_crs(view_e[2], view_e[0], view_e[1]);
613 v_nrm(view_e[0], view_e[0]);
614 v_nrm(view_e[2], view_e[2]);
616 /* Compute the new view position. */
618 v_cpy(view_p, file.uv->p);
619 v_mad(view_p, view_p, view_e[0], dx);
620 v_mad(view_p, view_p, view_e[1], view_dp);
621 v_mad(view_p, view_p, view_e[2], view_dz);
623 v_cpy(view_c, file.uv->p);
624 v_mad(view_c, view_c, view_e[1], dc);
626 view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
629 static void game_update_time(float dt, int b)
631 int tick = (int) floor(clock);
632 int tock = (int) floor(clock * 2);
634 /* The ticking clock. */
636 if (b)
638 if (clock < 600.f)
639 clock -= dt;
640 if (clock < 0.f)
641 clock = 0.f;
643 if (0 < tick && tick <= 10 && tick == (int) ceil(clock))
645 audio_play(AUD_TICK, 1.f);
646 hud_time_pulse(1.50);
648 else if (0 < tock && tock <= 10 && tock == (int) ceil(clock * 2))
650 audio_play(AUD_TOCK, 1.f);
651 hud_time_pulse(1.25);
656 static int game_update_state(void)
658 struct s_file *fp = &file;
659 float p[3];
660 float c[3];
661 int n, e = swch_e;
663 /* Test for a coin grab and a possible 1UP. */
665 if ((n = sol_coin_test(fp, p, COIN_RADIUS)) > 0)
667 coin_color(c, n);
668 part_burst(p, c);
669 level_score(n);
672 /* Test for a switch. */
674 if ((swch_e = sol_swch_test(fp, swch_e, 0)) != e && e)
675 audio_play(AUD_SWITCH, 1.f);
677 /* Test for a jump. */
679 if (jump_e == 1 && jump_b == 0 && sol_jump_test(fp, jump_p, 0) == 1)
681 jump_b = 1;
682 jump_e = 0;
683 jump_dt = 0.f;
685 audio_play(AUD_JUMP, 1.f);
687 if (jump_e == 0 && jump_b == 0 && sol_jump_test(fp, jump_p, 0) == 0)
688 jump_e = 1;
690 /* Test for a goal. */
692 if (sol_goal_test(fp, p, 0))
693 return GAME_GOAL;
695 /* Test for time-out. */
697 if (clock <= 0.f)
698 return GAME_TIME;
700 /* Test for fall-out. */
702 if (fp->uv[0].p[1] < -20.f)
703 return GAME_FALL;
705 return GAME_NONE;
709 * On most hardware, rendering requires much more computing power than
710 * physics. Since physics takes less time than graphics, it make sense to
711 * detach the physics update time step from the graphics frame rate. By
712 * performing multiple physics updates for each graphics update, we get away
713 * with higher quality physics with little impact on overall performance.
715 * Toward this end, we establish a baseline maximum physics time step. If
716 * the measured frame time exceeds this maximum, we cut the time step in
717 * half, and do two updates. If THIS time step exceeds the maximum, we do
718 * four updates. And so on. In this way, the physics system is allowed to
719 * seek an optimal update rate independant of, yet in integral sync with, the
720 * graphics frame rate.
723 int game_step(const float g[3], float dt, int bt)
725 struct s_file *fp = &file;
727 float h[3];
728 float d = 0.f;
729 float b = 0.f;
730 float t;
731 int i, n = 1;
733 t = dt;
735 /* Smooth jittery or discontinuous input. */
737 if (t < RESPONSE)
739 game_rx += (game_ix - game_rx) * t / RESPONSE;
740 game_rz += (game_iz - game_rz) * t / RESPONSE;
742 else
744 game_rx = game_ix;
745 game_rz = game_iz;
748 game_update_grav(h, g);
749 part_step(h, t);
751 if (jump_b)
753 jump_dt += t;
755 /* Handle a jump. */
757 if (0.5 < jump_dt)
759 fp->uv[0].p[0] = jump_p[0];
760 fp->uv[0].p[1] = jump_p[1];
761 fp->uv[0].p[2] = jump_p[2];
763 if (1.f < jump_dt)
764 jump_b = 0;
766 else
768 /* Run the sim. */
770 while (t > MAX_DT && n < MAX_DN)
772 t /= 2;
773 n *= 2;
776 for (i = 0; i < n; i++)
777 if (b < (d = sol_step(fp, h, t, 0, NULL)))
778 b = d;
780 /* Mix the sound of a ball bounce. */
782 if (b > 0.5)
783 audio_play(AUD_BUMP, (b - 0.5f) * 2.0f);
786 game_update_view(dt);
787 game_update_time(dt, bt);
789 return game_update_state();
792 /*---------------------------------------------------------------------------*/
794 void game_set_x(int k)
796 game_ix = -20.f * k / JOY_MAX;
799 void game_set_z(int k)
801 game_iz = +20.f * k / JOY_MAX;
804 void game_set_pos(int x, int y)
806 float bound = 20.f;
808 game_ix += 40.f * y / config_get(CONFIG_MOUSE_SENSE);
809 game_iz += 40.f * x / config_get(CONFIG_MOUSE_SENSE);
811 if (game_ix > +bound) game_ix = +bound;
812 if (game_ix < -bound) game_ix = -bound;
813 if (game_iz > +bound) game_iz = +bound;
814 if (game_iz < -bound) game_iz = -bound;
817 void game_set_rot(int r)
819 view_ry = (float) r;
822 /*---------------------------------------------------------------------------*/
824 void game_set_fly(float k)
826 struct s_file *fp = &file;
828 float x[3] = { 1.f, 0.f, 0.f };
829 float y[3] = { 0.f, 1.f, 0.f };
830 float z[3] = { 0.f, 0.f, 1.f };
831 float c0[3] = { 0.f, 0.f, 0.f };
832 float p0[3] = { 0.f, 0.f, 0.f };
833 float c1[3] = { 0.f, 0.f, 0.f };
834 float p1[3] = { 0.f, 0.f, 0.f };
835 float v[3];
837 v_cpy(view_e[0], x);
838 v_cpy(view_e[1], y);
839 v_cpy(view_e[2], z);
841 /* k = 0.0 view is at the ball. */
843 if (fp->uc > 0)
845 v_cpy(c0, fp->uv[0].p);
846 v_cpy(p0, fp->uv[0].p);
849 v_mad(p0, p0, y, view_dp);
850 v_mad(p0, p0, z, view_dz);
851 v_mad(c0, c0, y, view_dc);
853 /* k = +1.0 view is s_view 0 */
855 if (k >= 0 && fp->wc > 0)
857 v_cpy(p1, fp->wv[0].p);
858 v_cpy(c1, fp->wv[0].q);
861 /* k = -1.0 view is s_view 1 */
863 if (k <= 0 && fp->wc > 1)
865 v_cpy(p1, fp->wv[1].p);
866 v_cpy(c1, fp->wv[1].q);
869 /* Interpolate the views. */
871 v_sub(v, p1, p0);
872 v_mad(view_p, p0, v, k * k);
874 v_sub(v, c1, c0);
875 v_mad(view_c, c0, v, k * k);
877 /* Orthonormalize the view basis. */
879 v_sub(view_e[2], view_p, view_c);
880 v_crs(view_e[0], view_e[1], view_e[2]);
881 v_crs(view_e[2], view_e[0], view_e[1]);
882 v_nrm(view_e[0], view_e[0]);
883 v_nrm(view_e[2], view_e[2]);
886 void game_look(float phi, float theta)
888 view_c[0] = view_p[0] + fsinf(V_RAD(theta)) * fcosf(V_RAD(phi));
889 view_c[1] = view_p[1] + fsinf(V_RAD(phi));
890 view_c[2] = view_p[2] - fcosf(V_RAD(theta)) * fcosf(V_RAD(phi));
893 /*---------------------------------------------------------------------------*/
895 int game_put(FILE *fout)
897 return (float_put(fout, &game_rx) &&
898 float_put(fout, &game_rz) &&
899 vector_put(fout, view_c) &&
900 vector_put(fout, view_p) &&
901 sol_put(fout, &file));
904 int game_get(FILE *fin)
906 return (float_get(fin, &game_rx) &&
907 float_get(fin, &game_rz) &&
908 vector_get(fin, view_c) &&
909 vector_get(fin, view_p) &&
910 sol_get(fin, &file));
913 /*---------------------------------------------------------------------------*/