Merge branch 'gles'
[neverball] / ball / game_draw.c
index 7f00267..0591524 100644 (file)
  * General Public License for more details.
  */
 
-#include "solid.h"
 #include "vec3.h"
 #include "glext.h"
 #include "ball.h"
 #include "item.h"
 #include "part.h"
 #include "geom.h"
-#include "solid_gl.h"
 #include "config.h"
-#include "back.h"
 #include "video.h"
 
+#include "solid_draw.h"
+
 #include "game_draw.h"
 
 /*---------------------------------------------------------------------------*/
 
-static void game_draw_balls(const struct s_file *fp,
+static void game_draw_balls(struct s_rend *rend,
+                            const struct s_vary *vary,
                             const float *bill_M, float t)
 {
     float c[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
@@ -36,200 +36,153 @@ static void game_draw_balls(const struct s_file *fp,
     float ball_M[16];
     float pend_M[16];
 
-    m_basis(ball_M, fp->uv[0].e[0], fp->uv[0].e[1], fp->uv[0].e[2]);
-    m_basis(pend_M, fp->uv[0].E[0], fp->uv[0].E[1], fp->uv[0].E[2]);
+    m_basis(ball_M, vary->uv[0].e[0], vary->uv[0].e[1], vary->uv[0].e[2]);
+    m_basis(pend_M, vary->uv[0].E[0], vary->uv[0].E[1], vary->uv[0].E[2]);
 
-    glPushAttrib(GL_LIGHTING_BIT);
     glPushMatrix();
     {
-        glTranslatef(fp->uv[0].p[0],
-                     fp->uv[0].p[1] + BALL_FUDGE,
-                     fp->uv[0].p[2]);
-        glScalef(fp->uv[0].r,
-                 fp->uv[0].r,
-                 fp->uv[0].r);
-
-        glColor4fv(c);
-        ball_draw(ball_M, pend_M, bill_M, t);
+        glTranslatef(vary->uv[0].p[0],
+                     vary->uv[0].p[1] + BALL_FUDGE,
+                     vary->uv[0].p[2]);
+        glScalef(vary->uv[0].r,
+                 vary->uv[0].r,
+                 vary->uv[0].r);
+
+        glColor4f(c[0], c[1], c[2], c[3]);
+        ball_draw(rend, ball_M, pend_M, bill_M, t);
     }
     glPopMatrix();
-    glPopAttrib();
 }
 
-static void game_draw_items(const struct s_file *fp, float t)
+static void game_draw_items(struct s_rend *rend,
+                            const struct s_vary *vary,
+                            const float *bill_M, float t)
 {
-    float r = 360.f * t;
     int hi;
 
-    glPushAttrib(GL_LIGHTING_BIT);
+    glEnable(GL_COLOR_MATERIAL);
     {
-        item_push(ITEM_COIN);
-        {
-            for (hi = 0; hi < fp->hc; hi++)
-
-                if (fp->hv[hi].t == ITEM_COIN && fp->hv[hi].n > 0)
+        for (hi = 0; hi < vary->hc; hi++)
+            if (vary->hv[hi].t == ITEM_COIN && vary->hv[hi].n > 0)
+            {
+                glPushMatrix();
                 {
-                    glPushMatrix();
-                    {
-                        glTranslatef(fp->hv[hi].p[0],
-                                     fp->hv[hi].p[1],
-                                     fp->hv[hi].p[2]);
-                        glRotatef(r, 0.0f, 1.0f, 0.0f);
-                        item_draw(&fp->hv[hi], r);
-                    }
-                    glPopMatrix();
+                    glTranslatef(vary->hv[hi].p[0],
+                                 vary->hv[hi].p[1],
+                                 vary->hv[hi].p[2]);
+                    item_draw(rend, &vary->hv[hi], bill_M, t);
                 }
-        }
-        item_pull();
-
-        item_push(ITEM_SHRINK);
-        {
-            for (hi = 0; hi < fp->hc; hi++)
+                glPopMatrix();
+            }
 
-                if (fp->hv[hi].t == ITEM_SHRINK)
+        for (hi = 0; hi < vary->hc; hi++)
+            if (vary->hv[hi].t == ITEM_SHRINK)
+            {
+                glPushMatrix();
                 {
-                    glPushMatrix();
-                    {
-                        glTranslatef(fp->hv[hi].p[0],
-                                     fp->hv[hi].p[1],
-                                     fp->hv[hi].p[2]);
-                        glRotatef(r, 0.0f, 1.0f, 0.0f);
-                        item_draw(&fp->hv[hi], r);
-                    }
-                    glPopMatrix();
+                    glTranslatef(vary->hv[hi].p[0],
+                                 vary->hv[hi].p[1],
+                                 vary->hv[hi].p[2]);
+                    item_draw(rend, &vary->hv[hi], bill_M, t);
                 }
-        }
-        item_pull();
-
-        item_push(ITEM_GROW);
-        {
-            for (hi = 0; hi < fp->hc; hi++)
+                glPopMatrix();
+            }
 
-                if (fp->hv[hi].t == ITEM_GROW)
+        for (hi = 0; hi < vary->hc; hi++)
+            if (vary->hv[hi].t == ITEM_GROW)
+            {
+                glPushMatrix();
                 {
-                    glPushMatrix();
-                    {
-                        glTranslatef(fp->hv[hi].p[0],
-                                     fp->hv[hi].p[1],
-                                     fp->hv[hi].p[2]);
-                        glRotatef(r, 0.0f, 1.0f, 0.0f);
-                        item_draw(&fp->hv[hi], r);
-                    }
-                    glPopMatrix();
+                    glTranslatef(vary->hv[hi].p[0],
+                                 vary->hv[hi].p[1],
+                                 vary->hv[hi].p[2]);
+                    item_draw(rend, &vary->hv[hi], bill_M, t);
                 }
-        }
-        item_pull();
+                glPopMatrix();
+            }
     }
-    glPopAttrib();
+
+    glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+    glDisable(GL_COLOR_MATERIAL);
 }
 
-static void game_draw_goals(const struct game_draw *dr,
-                            const struct s_file *fp,
+static void game_draw_goals(struct s_rend *rend,
+                            const struct game_draw *gd,
                             const float *M, float t)
 {
-    if (dr->goal_e)
+    const struct s_base *base = gd->vary.base;
+
+    if (gd->goal_e)
     {
         int zi;
 
-        /* Draw the goal particles. */
-
-        glEnable(GL_TEXTURE_2D);
-        {
-            for (zi = 0; zi < fp->zc; zi++)
-            {
-                glPushMatrix();
-                {
-                    glTranslatef(fp->zv[zi].p[0],
-                                 fp->zv[zi].p[1],
-                                 fp->zv[zi].p[2]);
-
-                    part_draw_goal(M, fp->zv[zi].r, dr->goal_k, t);
-                }
-                glPopMatrix();
-            }
-        }
-        glDisable(GL_TEXTURE_2D);
-
         /* Draw the goal column. */
 
-        for (zi = 0; zi < fp->zc; zi++)
+        for (zi = 0; zi < base->zc; zi++)
         {
             glPushMatrix();
             {
-                glTranslatef(fp->zv[zi].p[0],
-                             fp->zv[zi].p[1],
-                             fp->zv[zi].p[2]);
+                glTranslatef(base->zv[zi].p[0],
+                             base->zv[zi].p[1],
+                             base->zv[zi].p[2]);
 
-                glScalef(fp->zv[zi].r,
-                         dr->goal_k,
-                         fp->zv[zi].r);
+                glScalef(base->zv[zi].r,
+                         gd->goal_k,
+                         base->zv[zi].r);
 
-                goal_draw();
+                goal_draw(rend, t);
             }
             glPopMatrix();
         }
     }
 }
 
-static void game_draw_jumps(const struct game_draw *dr,
-                            const struct s_file *fp,
+static void game_draw_jumps(struct s_rend *rend,
+                            const struct game_draw *gd,
                             const float *M, float t)
 {
-    int ji;
+    const struct s_base *base = gd->vary.base;
 
-    glEnable(GL_TEXTURE_2D);
-    {
-        for (ji = 0; ji < fp->jc; ji++)
-        {
-            glPushMatrix();
-            {
-                glTranslatef(fp->jv[ji].p[0],
-                             fp->jv[ji].p[1],
-                             fp->jv[ji].p[2]);
-
-                part_draw_jump(M, fp->jv[ji].r, 1.0f, t);
-            }
-            glPopMatrix();
-        }
-    }
-    glDisable(GL_TEXTURE_2D);
+    int ji;
 
-    for (ji = 0; ji < fp->jc; ji++)
+    for (ji = 0; ji < base->jc; ji++)
     {
         glPushMatrix();
         {
-            glTranslatef(fp->jv[ji].p[0],
-                         fp->jv[ji].p[1],
-                         fp->jv[ji].p[2]);
-            glScalef(fp->jv[ji].r,
+            glTranslatef(base->jv[ji].p[0],
+                         base->jv[ji].p[1],
+                         base->jv[ji].p[2]);
+            glScalef(base->jv[ji].r,
                      1.0f,
-                     fp->jv[ji].r);
+                     base->jv[ji].r);
 
-            jump_draw(!dr->jump_e);
+            jump_draw(rend, t, !gd->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 *vary)
 {
     int xi;
 
-    for (xi = 0; xi < fp->xc; xi++)
+    for (xi = 0; xi < vary->xc; xi++)
     {
-        if (fp->xv[xi].i)
+        struct v_swch *xp = vary->xv + xi;
+
+        if (xp->base->i)
             continue;
 
         glPushMatrix();
         {
-            glTranslatef(fp->xv[xi].p[0],
-                         fp->xv[xi].p[1],
-                         fp->xv[xi].p[2]);
-            glScalef(fp->xv[xi].r,
+            glTranslatef(xp->base->p[0],
+                         xp->base->p[1],
+                         xp->base->p[2]);
+            glScalef(xp->base->r,
                      1.0f,
-                     fp->xv[xi].r);
+                     xp->base->r);
 
-            swch_draw(fp->xv[xi].f, fp->xv[xi].e);
+            swch_draw(rend, xp->f, xp->e);
         }
         glPopMatrix();
     }
@@ -237,10 +190,10 @@ static void game_draw_swchs(const struct s_file *fp)
 
 /*---------------------------------------------------------------------------*/
 
-static void game_draw_tilt(const struct game_draw *dr, int d)
+static void game_draw_tilt(const struct game_draw *gd, int d)
 {
-    const struct game_tilt *tilt = &dr->tilt;
-    const float *ball_p = dr->file.uv->p;
+    const struct game_tilt *tilt = &gd->tilt;
+    const float *ball_p = gd->vary.uv[0].p;
 
     /* Rotate the environment about the position of the ball. */
 
@@ -250,15 +203,15 @@ static void game_draw_tilt(const struct game_draw *dr, int d)
     glTranslatef(-ball_p[0], -ball_p[1] * d, -ball_p[2]);
 }
 
-static void game_refl_all(const struct game_draw *dr)
+static void game_refl_all(struct s_rend *rend, const struct game_draw *gd)
 {
     glPushMatrix();
     {
-        game_draw_tilt(dr, 1);
+        game_draw_tilt(gd, 1);
 
         /* Draw the floor. */
 
-        sol_refl(&dr->file);
+        sol_refl(&gd->draw, rend);
     }
     glPopMatrix();
 }
@@ -289,32 +242,33 @@ static void game_draw_light(void)
     glLightfv(GL_LIGHT1, GL_SPECULAR, light_c[1]);
 }
 
-static void game_draw_back(const struct game_draw *dr, int pose, int d, float t)
+static void game_draw_back(struct s_rend *rend,
+                           const struct game_draw *gd,
+                           int pose, int d, float t)
 {
     if (pose == POSE_BALL)
         return;
 
     glPushMatrix();
     {
+        const struct game_view *view = &gd->view;
+
         if (d < 0)
         {
-            const struct game_tilt *tilt = &dr->tilt;
+            const struct game_tilt *tilt = &gd->tilt;
 
             glRotatef(tilt->rz * 2, tilt->z[0], tilt->z[1], tilt->z[2]);
             glRotatef(tilt->rx * 2, tilt->x[0], tilt->x[1], tilt->x[2]);
         }
 
-        glTranslatef(dr->view.p[0], dr->view.p[1] * d, dr->view.p[2]);
+        glTranslatef(view->p[0], view->p[1] * d, view->p[2]);
 
         if (config_get_d(CONFIG_BACKGROUND))
         {
-            /* Draw all background layers back to front. */
-
-            sol_back(&dr->back, BACK_DIST, FAR_DIST,  t);
-            back_draw(0);
-            sol_back(&dr->back,         0, BACK_DIST, t);
+            back_draw(rend, 0);
+            sol_back(&gd->back.draw, rend, 0, FAR_DIST, t);
         }
-        else back_draw(0);
+        else back_draw(rend, 0);
     }
     glPopMatrix();
 }
@@ -323,19 +277,12 @@ static void game_clip_refl(int d)
 {
     /* Fudge to eliminate the floor from reflection. */
 
-    GLdouble e[4], k = -0.00001;
-
-    e[0] = 0;
-    e[1] = 1;
-    e[2] = 0;
-    e[3] = k;
-
-    glClipPlane(GL_CLIP_PLANE0, e);
+    glClipPlane4f_(GL_CLIP_PLANE0, 0, 1, 0, -0.00001);
 }
 
-static void game_clip_ball(const struct game_draw *dr, int d, const float *p)
+static void game_clip_ball(const struct game_draw *gd, int d, const float *p)
 {
-    GLdouble r, c[3], pz[4], nz[4];
+    GLfloat r, c[3], pz[4], nz[4];
 
     /* Compute the plane giving the front of the ball, as seen from view.p. */
 
@@ -343,9 +290,9 @@ static void game_clip_ball(const struct game_draw *dr, int d, const float *p)
     c[1] = p[1] * d;
     c[2] = p[2];
 
-    pz[0] = dr->view.p[0] - c[0];
-    pz[1] = dr->view.p[1] - c[1];
-    pz[2] = dr->view.p[2] - c[2];
+    pz[0] = gd->view.p[0] - c[0];
+    pz[1] = gd->view.p[1] - c[1];
+    pz[2] = gd->view.p[2] - c[2];
 
     r = sqrt(pz[0] * pz[0] + pz[1] * pz[1] + pz[2] * pz[2]);
 
@@ -368,29 +315,29 @@ static void game_clip_ball(const struct game_draw *dr, int d, const float *p)
     pz[1] *= d;
     nz[1] *= d;
 
-    glClipPlane(GL_CLIP_PLANE1, nz);
-    glClipPlane(GL_CLIP_PLANE2, pz);
+    glClipPlane4f_(GL_CLIP_PLANE1, nz[0], nz[1], nz[2], nz[3]);
+    glClipPlane4f_(GL_CLIP_PLANE2, pz[0], pz[1], pz[2], pz[3]);
 }
 
-static void game_draw_fore(const struct game_draw *dr,
+static void game_draw_fore(struct s_rend *rend,
+                           struct game_draw *gd,
                            int pose, const float *M,
                            int d, float t)
 {
-    const float *ball_p = dr->file.uv->p;
-    const float  ball_r = dr->file.uv->r;
+    const float *ball_p = gd->vary.uv[0].p;
 
-    const struct s_file *fp = &dr->file;
+    struct s_draw *draw = &gd->draw;
 
     glPushMatrix();
     {
         /* Rotate the environment about the position of the ball. */
 
-        game_draw_tilt(dr, d);
+        game_draw_tilt(gd, d);
 
         /* Compute clipping planes for reflection and ball facing. */
 
         game_clip_refl(d);
-        game_clip_ball(dr, d, ball_p);
+        game_clip_ball(gd, d, ball_p);
 
         if (d < 0)
             glEnable(GL_CLIP_PLANE0);
@@ -398,58 +345,43 @@ static void game_draw_fore(const struct game_draw *dr,
         switch (pose)
         {
         case POSE_LEVEL:
-            sol_draw(fp, 0, 1);
+            sol_draw(draw, rend, 0, 1);
             break;
 
         case POSE_NONE:
-            /* Draw the coins. */
+            /* Draw the floor. */
 
-            game_draw_items(fp, t);
+            sol_draw(draw, rend, 0, 1);
 
-            /* Draw the floor. */
+            /* Draw the coins. */
 
-            sol_draw(fp, 0, 1);
+            game_draw_items(rend, draw->vary, M, t);
 
             /* Fall through. */
 
         case POSE_BALL:
 
-            /* Draw the ball shadow. */
-
-            if (d > 0 && config_get_d(CONFIG_SHADOW))
-            {
-                shad_draw_set(ball_p, ball_r);
-                sol_shad(fp);
-                shad_draw_clr();
-            }
-
             /* Draw the ball. */
 
-            game_draw_balls(fp, M, t);
+            game_draw_balls(rend, draw->vary, M, t);
 
             break;
         }
 
-        /* Draw the particles and light columns. */
+        /* Draw the billboards, entities, and  particles. */
 
         glEnable(GL_COLOR_MATERIAL);
         glDisable(GL_LIGHTING);
         glDepthMask(GL_FALSE);
         {
-            glColor3f(1.0f, 1.0f, 1.0f);
+            sol_bill(draw, rend, M, t);
 
-            sol_bill(fp, M, t);
-            part_draw_coin(M, t);
+            game_draw_goals(rend, gd, M, t);
+            game_draw_jumps(rend, gd, M, t);
+            game_draw_swchs(rend, draw->vary);
 
-            glDisable(GL_TEXTURE_2D);
-            {
-                game_draw_goals(dr, fp, M, t);
-                game_draw_jumps(dr, fp, M, t);
-                game_draw_swchs(fp);
-            }
-            glEnable(GL_TEXTURE_2D);
-
-            glColor3f(1.0f, 1.0f, 1.0f);
+            part_draw_coin(rend);
+            glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
         }
         glDepthMask(GL_TRUE);
         glEnable(GL_LIGHTING);
@@ -463,22 +395,30 @@ static void game_draw_fore(const struct game_draw *dr,
 
 /*---------------------------------------------------------------------------*/
 
-void game_draw(const struct game_draw *dr, int pose, float t)
+void game_draw(struct game_draw *gd, int pose, float t)
 {
     float fov = (float) config_get_d(CONFIG_VIEW_FOV);
+    int sh = config_get_d(CONFIG_SHADOW);
+
+    if (pose == POSE_LEVEL)
+        config_set_d(CONFIG_SHADOW, 0);
 
-    if (dr->jump_b) fov *= 2.f * fabsf(dr->jump_dt - 0.5);
+    if (gd->jump_b) fov *= 2.f * fabsf(gd->jump_dt - 0.5);
 
-    if (dr->state)
+    if (gd->state)
     {
-        const struct game_view *view = &dr->view;
+        const struct game_view *view = &gd->view;
+        struct s_rend rend;
+
+        gd->draw.shadow_ui = 0;
+
+        sol_draw_enable(&rend);
 
         video_push_persp(fov, 0.1f, FAR_DIST);
         glPushMatrix();
         {
             float T[16], U[16], M[16], v[3];
 
-
             /* Compute direct and reflected view bases. */
 
             v[0] = +view->p[0];
@@ -498,7 +438,13 @@ void game_draw(const struct game_draw *dr, int pose, float t)
             glMultMatrixf(M);
             glTranslatef(-view->c[0], -view->c[1], -view->c[2]);
 
-            if (dr->reflective && config_get_d(CONFIG_REFLECTION))
+            /* Draw the background. */
+
+            game_draw_back(&rend, gd, pose, +1, t);
+
+            /* Draw the reflection. */
+
+            if (gd->draw.reflective && config_get_d(CONFIG_REFLECTION))
             {
                 glEnable(GL_STENCIL_TEST);
                 {
@@ -509,7 +455,7 @@ void game_draw(const struct game_draw *dr, int pose, float t)
                     glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
                     glDepthMask(GL_FALSE);
 
-                    game_refl_all(dr);
+                    game_refl_all(&rend, gd);
 
                     glDepthMask(GL_TRUE);
                     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
@@ -524,76 +470,118 @@ void game_draw(const struct game_draw *dr, int pose, float t)
                         glScalef(+1.0f, -1.0f, +1.0f);
 
                         game_draw_light();
-                        game_draw_back(dr, pose,    -1, t);
-                        game_draw_fore(dr, pose, U, -1, t);
+
+                        game_draw_back(&rend, gd, pose,    -1, t);
+                        game_draw_fore(&rend, gd, pose, U, -1, t);
                     }
                     glPopMatrix();
                     glFrontFace(GL_CCW);
+
+                    glStencilFunc(GL_ALWAYS, 0, 0xFFFFFFF);
                 }
                 glDisable(GL_STENCIL_TEST);
             }
 
-            /* Draw the scene normally. */
+            /* Ready the lights for foreground rendering. */
 
             game_draw_light();
 
-            if (dr->reflective)
-            {
-                if (config_get_d(CONFIG_REFLECTION))
-                {
-                    /* Draw background while preserving reflections. */
-
-                    glEnable(GL_STENCIL_TEST);
-                    {
-                        glStencilFunc(GL_NOTEQUAL, 1, 0xFFFFFFFF);
-                        game_draw_back(dr, pose, +1, t);
-                    }
-                    glDisable(GL_STENCIL_TEST);
-
-                    /* Draw mirrors. */
+            /* When reflection is disabled, mirrors must be rendered opaque  */
+            /* to prevent the background from showing.                       */
 
-                    game_refl_all(dr);
-                }
-                else
+            if (gd->draw.reflective && !config_get_d(CONFIG_REFLECTION))
+            {
+                glEnable(GL_COLOR_MATERIAL);
                 {
-                    /* Draw background. */
-
-                    game_draw_back(dr, pose, +1, t);
-
-                    /*
-                     * Draw mirrors, first fully opaque with a custom
-                     * material color, then blending normally with the
-                     * opaque surfaces using their original material
-                     * properties.  (Keeps background from showing
-                     * through.)
-                     */
-
-                    glEnable(GL_COLOR_MATERIAL);
-                    {
-                        glColor4f(0.0, 0.0, 0.05, 1.0);
-                        game_refl_all(dr);
-                        glColor4f(1.0,  1.0,  1.0,  1.0);
-                    }
-                    glDisable(GL_COLOR_MATERIAL);
-
-                    game_refl_all(dr);
+                    glColor4f(0.0f, 0.0f, 0.0f, 1.0f);
+                    game_refl_all(&rend, gd);
+                    glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
                 }
-            }
-            else
-            {
-                game_draw_back(dr, pose, +1, t);
-                game_refl_all(dr);
+                glDisable(GL_COLOR_MATERIAL);
             }
 
-            game_draw_fore(dr, pose, T, +1, t);
+            /* Draw the mirrors and the rest of the foreground. */
+
+            game_refl_all (&rend, gd);
+            game_draw_fore(&rend, gd, pose, T, +1, t);
         }
         glPopMatrix();
         video_pop_matrix();
 
         /* Draw the fade overlay. */
 
-        fade_draw(dr->fade_k);
+        sol_fade(&gd->draw, gd->fade_k);
+        sol_draw_disable(&rend);
     }
+
+    if (pose == POSE_LEVEL)
+        config_set_d(CONFIG_SHADOW, sh);
+}
+
+/*---------------------------------------------------------------------------*/
+
+#define CURR 0
+#define PREV 1
+
+void game_lerp_init(struct game_lerp *gl, struct game_draw *gd)
+{
+    gl->alpha = 1.0f;
+
+    sol_load_lerp(&gl->lerp, &gd->vary);
+
+    gl->tilt[PREV] = gl->tilt[CURR] = gd->tilt;
+    gl->view[PREV] = gl->view[CURR] = gd->view;
+
+    gl->goal_k[PREV] = gl->goal_k[CURR] = gd->goal_k;
+    gl->jump_dt[PREV] = gl->jump_dt[CURR] = gd->jump_dt;
+}
+
+void game_lerp_free(struct game_lerp *gl)
+{
+    sol_free_lerp(&gl->lerp);
+}
+
+void game_lerp_copy(struct game_lerp *gl)
+{
+    sol_lerp_copy(&gl->lerp);
+
+    gl->tilt[PREV] = gl->tilt[CURR];
+    gl->view[PREV] = gl->view[CURR];
+
+    gl->goal_k[PREV] = gl->goal_k[CURR];
+    gl->jump_dt[PREV] = gl->jump_dt[CURR];
+}
+
+void game_lerp_apply(struct game_lerp *gl, struct game_draw *gd)
+{
+    float a = gl->alpha;
+
+    /* Solid. */
+
+    sol_lerp_apply(&gl->lerp, a);
+
+    /* Particles. */
+
+    part_lerp_apply(a);
+
+    /* Tilt. */
+
+    v_lerp(gd->tilt.x, gl->tilt[PREV].x, gl->tilt[CURR].x, a);
+    v_lerp(gd->tilt.z, gl->tilt[PREV].z, gl->tilt[CURR].z, a);
+
+    gd->tilt.rx = (gl->tilt[PREV].rx * (1.0f - a) + gl->tilt[CURR].rx * a);
+    gd->tilt.rz = (gl->tilt[PREV].rz * (1.0f - a) + gl->tilt[CURR].rz * a);
+
+    /* View. */
+
+    v_lerp(gd->view.c, gl->view[PREV].c, gl->view[CURR].c, a);
+    v_lerp(gd->view.p, gl->view[PREV].p, gl->view[CURR].p, a);
+    e_lerp(gd->view.e, gl->view[PREV].e, gl->view[CURR].e, a);
+
+    /* Effects. */
+
+    gd->goal_k = (gl->goal_k[PREV] * (1.0f - a) + gl->goal_k[CURR] * a);
+    gd->jump_dt = (gl->jump_dt[PREV] * (1.0f - a) + gl->jump_dt[CURR] * a);
 }
 
 /*---------------------------------------------------------------------------*/