Merge branch 'gles'
[neverball] / putt / game.c
index 42acfc4..70ce43c 100644 (file)
 #include "game.h"
 #include "vec3.h"
 #include "geom.h"
-#include "back.h"
+#include "ball.h"
 #include "hole.h"
 #include "hud.h"
 #include "image.h"
 #include "audio.h"
-#include "solid_gl.h"
 #include "config.h"
+#include "video.h"
+
+#include "solid_draw.h"
+#include "solid_sim.h"
+#include "solid_all.h"
 
 /*---------------------------------------------------------------------------*/
 
-static struct s_file file;
+static struct s_full file;
 static int           ball;
 
+static int state;
+
 static float view_a;                    /* Ideal view rotation about Y axis  */
 static float view_m;
 static float view_ry;                   /* Angular velocity about Y axis     */
@@ -48,6 +54,8 @@ static float jump_b = 0;                /* Jump-in-progress flag             */
 static float jump_dt;                   /* Jump duration                     */
 static float jump_p[3];                 /* Jump destination                  */
 
+static float idle_t;                    /* Idling timeout                    */
+
 /*---------------------------------------------------------------------------*/
 
 static void view_init(void)
@@ -77,84 +85,70 @@ static void view_init(void)
     view_e[2][2] = 1.f;
 }
 
-void game_init(const char *s)
+int game_init(const char *s)
 {
+    int i;
+
     jump_e = 1;
     jump_b = 0;
 
-    view_init();
-    sol_load_gl(&file, config_data(s), config_get_d(CONFIG_TEXTURES),
-                                    config_get_d(CONFIG_SHADOW));
-}
+    idle_t = 1.0f;
 
-void game_free(void)
-{
-    sol_free_gl(&file);
-}
+    view_init();
 
-/*---------------------------------------------------------------------------*/
+    if (!(state = sol_load_full(&file, s, config_get_d(CONFIG_SHADOW))))
+        return 0;
 
-static void game_draw_vect_prim(const struct s_file *fp, GLenum mode)
-{
-    float p[3];
-    float x[3];
-    float z[3];
-    float r;
+    sol_init_sim(&file.vary);
 
-    v_cpy(p, fp->uv[ball].p);
-    v_cpy(x, view_e[0]);
-    v_cpy(z, view_e[2]);
+    for (i = 0; i < file.base.dc; i++)
+    {
+        const char *k = file.base.av + file.base.dv[i].ai;
+        const char *v = file.base.av + file.base.dv[i].aj;
 
-    r = fp->uv[ball].r;
+        if (strcmp(k, "idle") == 0)
+        {
+            sscanf(v, "%f", &idle_t);
 
-    glBegin(mode);
-    {
-        glColor4f(1.0f, 1.0f, 0.5f, 0.5f);
-        glVertex3f(p[0] - x[0] * r,
-                   p[1] - x[1] * r,
-                   p[2] - x[2] * r);
-
-        glColor4f(1.0f, 0.0f, 0.0f, 0.5f);
-        glVertex3f(p[0] + z[0] * view_m,
-                   p[1] + z[1] * view_m,
-                   p[2] + z[2] * view_m);
-
-        glColor4f(1.0f, 1.0f, 0.0f, 0.5f);
-        glVertex3f(p[0] + x[0] * r,
-                   p[1] + x[1] * r,
-                   p[2] + x[2] * r);
+            if (idle_t < 1.0f)
+                idle_t = 1.0f;
+        }
     }
-    glEnd();
+    return 1;
 }
 
-static void game_draw_vect(const struct s_file *fp)
+void game_free(void)
+{
+    sol_quit_sim();
+    sol_free_full(&file);
+}
+
+/*---------------------------------------------------------------------------*/
+
+static void game_draw_vect(struct s_rend *rend, const struct s_vary *fp)
 {
     if (view_m > 0.f)
     {
-        glPushAttrib(GL_TEXTURE_BIT);
-        glPushAttrib(GL_POLYGON_BIT);
-        glPushAttrib(GL_LIGHTING_BIT);
-        glPushAttrib(GL_DEPTH_BUFFER_BIT);
+        glDisable(GL_LIGHTING);
+        glPushMatrix();
         {
-            glEnable(GL_COLOR_MATERIAL);
-            glDisable(GL_LIGHTING);
-            glDisable(GL_TEXTURE_2D);
-            glDepthMask(GL_FALSE);
-
-            glEnable(GL_DEPTH_TEST);
-            game_draw_vect_prim(fp, GL_TRIANGLES);
-
-            glDisable(GL_DEPTH_TEST);
-            game_draw_vect_prim(fp, GL_LINE_STRIP);
+            glTranslatef(fp->uv[ball].p[0],
+                         fp->uv[ball].p[1],
+                         fp->uv[ball].p[2]);
+            glRotatef(view_a, 0.0f, 1.0f, 0.0f);
+            glScalef(fp->uv[ball].r,
+                     fp->uv[ball].r * 0.1f, view_m);
+
+            vect_draw(rend);
         }
-        glPopAttrib();
-        glPopAttrib();
-        glPopAttrib();
-        glPopAttrib();
+        glPopMatrix();
+        glEnable(GL_LIGHTING);
     }
 }
 
-static void game_draw_balls(const struct s_file *fp)
+static void game_draw_balls(struct s_rend *rend,
+                            const struct s_vary *fp,
+                            const float *bill_M, float t)
 {
     static const GLfloat color[5][4] = {
         { 1.0f, 1.0f, 1.0f, 0.7f },
@@ -164,28 +158,34 @@ static void game_draw_balls(const struct s_file *fp)
         { 1.0f, 1.0f, 0.0f, 1.0f },
     };
 
-    float M[16];
     int ui;
 
+    glEnable(GL_COLOR_MATERIAL);
+
     for (ui = curr_party(); ui > 0; ui--)
     {
         if (ui == ball)
         {
+            float ball_M[16];
+            float pend_M[16];
+
+            m_basis(ball_M, fp->uv[ui].e[0], fp->uv[ui].e[1], fp->uv[ui].e[2]);
+            m_basis(pend_M, fp->uv[ui].E[0], fp->uv[ui].E[1], fp->uv[ui].E[2]);
+
             glPushMatrix();
             {
-                m_basis(M, fp->uv[ui].e[0], fp->uv[ui].e[1], fp->uv[ui].e[2]);
-
                 glTranslatef(fp->uv[ui].p[0],
                              fp->uv[ui].p[1] + BALL_FUDGE,
                              fp->uv[ui].p[2]);
-                glMultMatrixf(M);
                 glScalef(fp->uv[ui].r,
                          fp->uv[ui].r,
                          fp->uv[ui].r);
 
-                glColor4fv(color[ui]);
-
-                ball_draw(0);
+                glColor4f(color[ui][0],
+                          color[ui][1],
+                          color[ui][2],
+                          color[ui][3]);
+                ball_draw(rend, ball_M, pend_M, bill_M, t);
             }
             glPopMatrix();
         }
@@ -204,14 +204,17 @@ static void game_draw_balls(const struct s_file *fp)
                           color[ui][1],
                           color[ui][2], 0.5f);
 
-                mark_draw();
+                mark_draw(rend);
             }
             glPopMatrix();
         }
     }
+
+    glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+    glDisable(GL_COLOR_MATERIAL);
 }
 
-static void game_draw_goals(const struct s_file *fp, float rx, float ry)
+static void game_draw_goals(struct s_rend *rend, const struct s_base *fp)
 {
     int zi;
 
@@ -222,14 +225,15 @@ static void game_draw_goals(const struct s_file *fp, float rx, float ry)
             glTranslatef(fp->zv[zi].p[0],
                          fp->zv[zi].p[1],
                          fp->zv[zi].p[2]);
-            flag_draw();
+            flag_draw(rend);
         }
         glPopMatrix();
     }
 }
 
-static void game_draw_jumps(const struct s_file *fp)
+static void game_draw_jumps(struct s_rend *rend, const struct s_base *fp)
 {
+    float t = 0.001f * SDL_GetTicks();
     int ji;
 
     for (ji = 0; ji < fp->jc; ji++)
@@ -241,26 +245,31 @@ static void game_draw_jumps(const struct s_file *fp)
                          fp->jv[ji].p[2]);
 
             glScalef(fp->jv[ji].r, 1.f, fp->jv[ji].r);
-            jump_draw(!jump_e);
+            jump_draw(rend, t, !jump_e);
         }
         glPopMatrix();
     }
 }
 
-static void game_draw_swchs(const struct s_file *fp)
+static void game_draw_swchs(struct s_rend *rend, const struct s_vary *fp)
 {
     int xi;
 
     for (xi = 0; xi < fp->xc; xi++)
     {
+        struct v_swch *xp = fp->xv + xi;
+
+        if (xp->base->i)
+            continue;
+
         glPushMatrix();
         {
-            glTranslatef(fp->xv[xi].p[0],
-                         fp->xv[xi].p[1],
-                         fp->xv[xi].p[2]);
+            glTranslatef(xp->base->p[0],
+                         xp->base->p[1],
+                         xp->base->p[2]);
 
-            glScalef(fp->xv[xi].r, 1.f, fp->xv[xi].r);
-            swch_draw(fp->xv[xi].f, fp->xv[xi].e);
+            glScalef(xp->base->r, 1.f, xp->base->r);
+            swch_draw(rend, xp->f, xp->e);
         }
         glPopMatrix();
     }
@@ -268,21 +277,31 @@ static void game_draw_swchs(const struct s_file *fp)
 
 /*---------------------------------------------------------------------------*/
 
-void game_draw(int pose)
+void game_draw(int pose, float t)
 {
-    const float light_p[4] = { 8.f, 32.f, 8.f, 1.f };
+    const float light_p[4] = { 8.f, 32.f, 8.f, 0.f };
 
-    const struct s_file *fp = &file;
+    struct s_draw *fp = &file.draw;
+    struct s_rend rend = { NULL };
 
     float fov = FOV;
 
+    if (!state)
+        return;
+
+    fp->shadow_ui = ball;
+
+    sol_draw_enable(&rend);
+
     if (jump_b) fov *= 2.0f * fabsf(jump_dt - 0.5f);
 
-    config_push_persp(fov, 0.1f, FAR_DIST);
-    glPushAttrib(GL_LIGHTING_BIT);
+    video_push_persp(fov, 0.1f, FAR_DIST);
     glPushMatrix();
     {
-        float v[3], rx, ry;
+        float T[16], M[16], v[3], rx, ry;
+
+        m_view(T, view_c, view_p, view_e[1]);
+        m_xps(M, T);
 
         v_sub(v, view_c, view_p);
 
@@ -290,8 +309,7 @@ void game_draw(int pose)
         ry = V_DEG(fatan2f(+v[0], -v[2]));
 
         glTranslatef(0.f, 0.f, -v_len(v));
-        glRotatef(rx, 1.f, 0.f, 0.f);
-        glRotatef(ry, 0.f, 1.f, 0.f);
+        glMultMatrixf(M);
         glTranslatef(-view_c[0], -view_c[1], -view_c[2]);
 
         /* Center the skybox about the position of the camera. */
@@ -299,7 +317,7 @@ void game_draw(int pose)
         glPushMatrix();
         {
             glTranslatef(view_p[0], view_p[1], view_p[2]);
-            back_draw(0);
+            back_draw(&rend, 0);
         }
         glPopMatrix();
 
@@ -308,14 +326,7 @@ void game_draw(int pose)
 
         /* Draw the floor. */
 
-        sol_draw(fp);
-
-        if (config_get_d(CONFIG_SHADOW) && !pose)
-        {
-            shad_draw_set(fp->uv[ball].p, fp->uv[ball].r);
-            sol_shad(fp);
-            shad_draw_clr();
-        }
+        sol_draw(fp, &rend, 0, 1);
 
         /* Draw the game elements. */
 
@@ -324,17 +335,26 @@ void game_draw(int pose)
 
         if (pose == 0)
         {
-            game_draw_balls(fp);
-            game_draw_vect(fp);
+            game_draw_balls(&rend, fp->vary, T, t);
+            game_draw_vect(&rend, fp->vary);
         }
 
-        game_draw_goals(fp, -rx, -ry);
-        game_draw_jumps(fp);
-        game_draw_swchs(fp);
+        glEnable(GL_COLOR_MATERIAL);
+        glDisable(GL_LIGHTING);
+        glDepthMask(GL_FALSE);
+        {
+            game_draw_goals(&rend, fp->base);
+            game_draw_jumps(&rend, fp->base);
+            game_draw_swchs(&rend, fp->vary);
+        }
+        glDepthMask(GL_TRUE);
+        glEnable(GL_LIGHTING);
+        glDisable(GL_COLOR_MATERIAL);
     }
     glPopMatrix();
-    glPopAttrib();
-    config_pop_matrix();
+    video_pop_matrix();
+
+    sol_draw_disable(&rend);
 }
 
 /*---------------------------------------------------------------------------*/
@@ -350,10 +370,13 @@ void game_update_view(float dt)
     float d[3];
     float s = 2.f * dt;
 
+    if (!state)
+        return;
+
     /* Center the view about the ball. */
 
-    v_cpy(view_c, file.uv[ball].p);
-    v_inv(view_v, file.uv[ball].v);
+    v_cpy(view_c, file.vary.uv[ball].p);
+    v_inv(view_v, file.vary.uv[ball].v);
 
     switch (config_get_d(CONFIG_CAMERA))
     {
@@ -406,31 +429,27 @@ void game_update_view(float dt)
     view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2]));
 }
 
-static int game_update_state(float dt, int day_the_earth_stood_still)
+static int game_update_state(float dt)
 {
     static float t = 0.f;
 
-    struct s_file *fp = &file;
+    struct s_vary *fp = &file.vary;
     float p[3];
 
     if (dt > 0.f)
-    {
         t += dt;
-    }
     else
-    {
-        if (!day_the_earth_stood_still)
-            t = 0.f;
-    }
+        t = 0.f;
 
     /* Test for a switch. */
 
-    if (sol_swch_test(fp, ball))
+    if (sol_swch_test(fp, ball) == SWCH_INSIDE)
         audio_play(AUD_SWITCH, 1.f);
 
     /* Test for a jump. */
 
-    if (jump_e == 1 && jump_b == 0 && sol_jump_test(fp, jump_p, ball) == 1)
+    if (jump_e == 1 && jump_b == 0 && (sol_jump_test(fp, jump_p, ball) ==
+                                       JUMP_INSIDE))
     {
         jump_b  = 1;
         jump_e  = 0;
@@ -438,8 +457,11 @@ static int game_update_state(float dt, int day_the_earth_stood_still)
 
         audio_play(AUD_JUMP, 1.f);
     }
-    if (jump_e == 0 && jump_b == 0 &&  sol_jump_test(fp, jump_p, ball) == 0)
+    if (jump_e == 0 && jump_b == 0 && (sol_jump_test(fp, jump_p, ball) ==
+                                       JUMP_OUTSIDE))
+    {
         jump_e = 1;
+    }
 
     /* Test for fall-out. */
 
@@ -448,36 +470,39 @@ static int game_update_state(float dt, int day_the_earth_stood_still)
 
     /* Test for a goal or stop. */
 
-    if (t > 1.f)
+    if (t > 1.f && sol_goal_test(fp, p, ball))
     {
         t = 0.f;
+        return GAME_GOAL;
+    }
 
-        if (sol_goal_test(fp, p, ball))
-            return GAME_GOAL;
-        else
-            return GAME_STOP;
+    if (t > idle_t)
+    {
+        t = 0.f;
+        return GAME_STOP;
     }
 
     return GAME_NONE;
 }
 
 /*
- * By performing multiple physics updates or skipping an update for  a  given
- * graphics update, we get away  with  higher  quality  physics  with  little
- * impact on overall performance.  Toward this end, we establish  a  baseline
- * maximum and minimum physics time step.  If the measured frame time exceeds
- * the maximum time step, we cut the time step in half, and do  two  updates.
- * If THIS time step exceeds the maximum, we do four updates.  And so on.  On
- * the other hand, if the frame time is lower than the minimum time step,  we
- * skip an update and do so until the accumulated frame time has reached this
- * minimum.  In this way, the physics system is allowed to  seek  an  optimal
- * update rate independent of, yet in integral sync with, the graphics  frame
- * rate.
+ * On  most  hardware, rendering  requires  much  more  computing power  than
+ * physics.  Since  physics takes less time  than graphics, it  make sense to
+ * detach  the physics update  time step  from the  graphics frame  rate.  By
+ * performing multiple physics updates for  each graphics update, we get away
+ * with higher quality physics with little impact on overall performance.
+ *
+ * Toward this  end, we establish a  baseline maximum physics  time step.  If
+ * the measured  frame time  exceeds this  maximum, we cut  the time  step in
+ * half, and  do two updates.  If THIS  time step exceeds the  maximum, we do
+ * four updates.  And  so on.  In this way, the physics  system is allowed to
+ * seek an optimal update rate independent of, yet in integral sync with, the
+ * graphics frame rate.
  */
 
 int game_step(const float g[3], float dt)
 {
-    struct s_file *fp = &file;
+    struct s_vary *fp = &file.vary;
 
     static float s = 0.f;
     static float t = 0.f;
@@ -487,6 +512,9 @@ int game_step(const float g[3], float dt)
     float st = 0.f;
     int i, n = 1, m = 0;
 
+    if (!state)
+        return GAME_NONE;
+
     s = (7.f * s + dt) / 8.f;
     t = s;
 
@@ -507,22 +535,8 @@ int game_step(const float g[3], float dt)
     }
     else
     {
-        static float accumulated_t = 0.f;
-
         /* Run the sim. */
 
-        accumulated_t += t;
-
-        if (accumulated_t < MIN_DT)
-        {
-            n = 0;
-        }
-        else
-        {
-            t = accumulated_t;
-            accumulated_t = 0.f;
-        }
-
         while (t > MAX_DT && n < MAX_DN)
         {
             t /= 2;
@@ -546,7 +560,7 @@ int game_step(const float g[3], float dt)
     }
 
     game_update_view(dt);
-    return game_update_state(st, (n == 0));
+    return game_update_state(st);
 }
 
 void game_putt(void)
@@ -557,9 +571,9 @@ void game_putt(void)
      * friction too early and stopping the ball prematurely.
      */
 
-    file.uv[ball].v[0] = -4.f * view_e[2][0] * view_m;
-    file.uv[ball].v[1] = -4.f * view_e[2][1] * view_m + BALL_FUDGE;
-    file.uv[ball].v[2] = -4.f * view_e[2][2] * view_m;
+    file.vary.uv[ball].v[0] = -4.f * view_e[2][0] * view_m;
+    file.vary.uv[ball].v[1] = -4.f * view_e[2][1] * view_m + BALL_FUDGE;
+    file.vary.uv[ball].v[2] = -4.f * view_e[2][2] * view_m;
 
     view_m = 0.f;
 }
@@ -586,7 +600,7 @@ void game_set_mag(int d)
 
 void game_set_fly(float k)
 {
-    struct s_file *fp = &file;
+    struct s_vary *fp = &file.vary;
 
     float  x[3] = { 1.f, 0.f, 0.f };
     float  y[3] = { 0.f, 1.f, 0.f };
@@ -597,9 +611,12 @@ void game_set_fly(float k)
     float p1[3] = { 0.f, 0.f, 0.f };
     float  v[3];
 
+    if (!state)
+        return;
+
     v_cpy(view_e[0], x);
     v_cpy(view_e[1], y);
-    v_sub(view_e[2], fp->uv[ball].p, fp->zv[0].p);
+    v_sub(view_e[2], fp->uv[ball].p, fp->base->zv[0].p);
 
     if (fabs(v_dot(view_e[1], view_e[2])) > 0.999)
         v_cpy(view_e[2], z);
@@ -623,18 +640,18 @@ void game_set_fly(float k)
 
     /* k = +1.0 view is s_view 0 */
 
-    if (k >= 0 && fp->wc > 0)
+    if (k >= 0 && fp->base->wc > 0)
     {
-        v_cpy(p1, fp->wv[0].p);
-        v_cpy(c1, fp->wv[0].q);
+        v_cpy(p1, fp->base->wv[0].p);
+        v_cpy(c1, fp->base->wv[0].q);
     }
 
     /* k = -1.0 view is s_view 1 */
 
-    if (k <= 0 && fp->wc > 1)
+    if (k <= 0 && fp->base->wc > 1)
     {
-        v_cpy(p1, fp->wv[1].p);
-        v_cpy(c1, fp->wv[1].q);
+        v_cpy(p1, fp->base->wv[1].p);
+        v_cpy(c1, fp->base->wv[1].q);
     }
 
     /* Interpolate the views. */
@@ -665,32 +682,32 @@ void game_ball(int i)
     jump_e = 1;
     jump_b = 0;
 
-    for (ui = 0; ui < file.uc; ui++)
+    for (ui = 0; ui < file.vary.uc; ui++)
     {
-        file.uv[ui].v[0] = 0.f;
-        file.uv[ui].v[1] = 0.f;
-        file.uv[ui].v[2] = 0.f;
+        file.vary.uv[ui].v[0] = 0.f;
+        file.vary.uv[ui].v[1] = 0.f;
+        file.vary.uv[ui].v[2] = 0.f;
 
-        file.uv[ui].w[0] = 0.f;
-        file.uv[ui].w[1] = 0.f;
-        file.uv[ui].w[2] = 0.f;
+        file.vary.uv[ui].w[0] = 0.f;
+        file.vary.uv[ui].w[1] = 0.f;
+        file.vary.uv[ui].w[2] = 0.f;
     }
 }
 
 void game_get_pos(float p[3], float e[3][3])
 {
-    v_cpy(p,    file.uv[ball].p);
-    v_cpy(e[0], file.uv[ball].e[0]);
-    v_cpy(e[1], file.uv[ball].e[1]);
-    v_cpy(e[2], file.uv[ball].e[2]);
+    v_cpy(p,    file.vary.uv[ball].p);
+    v_cpy(e[0], file.vary.uv[ball].e[0]);
+    v_cpy(e[1], file.vary.uv[ball].e[1]);
+    v_cpy(e[2], file.vary.uv[ball].e[2]);
 }
 
 void game_set_pos(float p[3], float e[3][3])
 {
-    v_cpy(file.uv[ball].p,    p);
-    v_cpy(file.uv[ball].e[0], e[0]);
-    v_cpy(file.uv[ball].e[1], e[1]);
-    v_cpy(file.uv[ball].e[2], e[2]);
+    v_cpy(file.vary.uv[ball].p,    p);
+    v_cpy(file.vary.uv[ball].e[0], e[0]);
+    v_cpy(file.vary.uv[ball].e[1], e[1]);
+    v_cpy(file.vary.uv[ball].e[2], e[2]);
 }
 
 /*---------------------------------------------------------------------------*/