Implement game state interpolation (WIP)
authorparasti <parasti@78b8d119-cf0a-0410-b17c-f493084dd1d7>
Thu, 10 Feb 2011 19:00:24 +0000 (19:00 +0000)
committerparasti <parasti@78b8d119-cf0a-0410-b17c-f493084dd1d7>
Thu, 10 Feb 2011 19:00:24 +0000 (19:00 +0000)
git-svn-id: https://s.snth.net/svn/neverball/trunk@3487 78b8d119-cf0a-0410-b17c-f493084dd1d7

21 files changed:
ball/demo.c
ball/demo.h
ball/game_client.c
ball/game_client.h
ball/game_common.h
ball/game_draw.c
ball/game_draw.h
ball/game_server.c
ball/game_server.h
ball/main.c
ball/set.c
ball/st_ball.c
ball/st_demo.c
ball/st_help.c
ball/st_level.c
ball/st_name.c
ball/st_play.c
ball/st_shared.c
share/solid_vary.c
share/solid_vary.h
share/vec3.h

index b843e56..53d3e80 100644 (file)
@@ -376,6 +376,11 @@ static void demo_update_read(float dt)
 
 static struct lockstep update_step = { demo_update_read, DT };
 
+float demo_play_blend(void)
+{
+    return lockstep_blend(&update_step);
+}
+
 /*---------------------------------------------------------------------------*/
 
 static struct demo demo_replay;
index 839a977..a32c817 100644 (file)
@@ -50,6 +50,7 @@ int  demo_play_init(const char *, const struct level *, int, int, int, int);
 void demo_play_step(void);
 void demo_play_stat(int, int, int);
 void demo_play_stop(void);
+float demo_play_blend(void);
 
 int  demo_saved (void);
 void demo_rename(const char *);
index 690b3b2..e857b31 100644 (file)
@@ -43,7 +43,11 @@ int game_compat_map;                    /* Client/server map compat flag     */
 
 /*---------------------------------------------------------------------------*/
 
+#define CURR 0
+#define PREV 1
+
 static struct game_draw gd;
+static struct game_lerp gl;
 
 static float timer  = 0.0f;             /* Clock time                        */
 static int   status = GAME_NONE;        /* Outcome of the game               */
@@ -67,28 +71,41 @@ struct
 
 static int got_tilt_axes;
 
+static int next_update;
+
 /*---------------------------------------------------------------------------*/
 
 static void game_run_cmd(const union cmd *cmd)
 {
     if (gd.state)
     {
+        struct game_view *view = &gl.view[CURR];
+        struct game_tilt *tilt = &gl.tilt[CURR];
+
         struct s_vary *vary = &gd.file.vary;
         struct v_item *hp;
-        struct v_ball *up;
 
         float v[3];
         float dt;
-        int i;
+
+        if (next_update)
+        {
+            game_lerp_copy(&gl);
+            next_update = 0;
+        }
 
         switch (cmd->type)
         {
         case CMD_END_OF_UPDATE:
-
             got_tilt_axes = 0;
 
+            next_update = 1;
+
             if (first_update)
             {
+                game_lerp_copy(&gl);
+                /* Hack to sync state before the next update. */
+                game_lerp_apply(&gl, &gd, 1.0f);
                 first_update = 0;
                 break;
             }
@@ -96,9 +113,9 @@ static void game_run_cmd(const union cmd *cmd)
             /* Compute gravity for particle effects. */
 
             if (status == GAME_GOAL)
-                game_tilt_grav(v, GRAVITY_UP, &gd.tilt);
+                game_tilt_grav(v, GRAVITY_UP, tilt);
             else
-                game_tilt_grav(v, GRAVITY_DN, &gd.tilt);
+                game_tilt_grav(v, GRAVITY_DN, tilt);
 
             /* Step particle, goal and jump effects. */
 
@@ -106,14 +123,14 @@ static void game_run_cmd(const union cmd *cmd)
             {
                 dt = 1.0f / (float) ups;
 
-                if (gd.goal_e && gd.goal_k < 1.0f)
-                    gd.goal_k += dt;
+                if (gd.goal_e && gl.goal_k[CURR] < 1.0f)
+                    gl.goal_k[CURR] += dt;
 
                 if (gd.jump_b)
                 {
-                    gd.jump_dt += dt;
+                    gl.jump_dt[CURR] += dt;
 
-                    if (1.0f < gd.jump_dt)
+                    if (1.0f < gl.jump_dt[CURR])
                         gd.jump_b = 0;
                 }
 
@@ -125,12 +142,9 @@ static void game_run_cmd(const union cmd *cmd)
         case CMD_MAKE_BALL:
             /* Allocate a new ball and mark it as the current ball. */
 
-            if ((up = realloc(vary->uv, sizeof (*up) * (vary->uc + 1))))
-            {
-                vary->uv = up;
-                curr_ball = vary->uc;
-                vary->uc++;
-            }
+            if (sol_lerp_cmd(&gl.lerp, cmd))
+                curr_ball = gl.lerp.uc - 1;
+
             break;
 
         case CMD_MAKE_ITEM:
@@ -168,10 +182,10 @@ static void game_run_cmd(const union cmd *cmd)
 
         case CMD_TILT_ANGLES:
             if (!got_tilt_axes)
-                game_tilt_axes(&gd.tilt, gd.view.e);
+                game_tilt_axes(tilt, view->e);
 
-            gd.tilt.rx = cmd->tiltangles.x;
-            gd.tilt.rz = cmd->tiltangles.z;
+            tilt->rx = cmd->tiltangles.x;
+            tilt->rz = cmd->tiltangles.z;
             break;
 
         case CMD_SOUND:
@@ -197,7 +211,7 @@ static void game_run_cmd(const union cmd *cmd)
         case CMD_JUMP_ENTER:
             gd.jump_b  = 1;
             gd.jump_e  = 0;
-            gd.jump_dt = 0.0f;
+            gl.jump_dt[CURR] = 0.0f;
             break;
 
         case CMD_JUMP_EXIT:
@@ -205,11 +219,11 @@ static void game_run_cmd(const union cmd *cmd)
             break;
 
         case CMD_BODY_PATH:
-            vary->bv[cmd->bodypath.bi].pi = cmd->bodypath.pi;
+            sol_lerp_cmd(&gl.lerp, cmd);
             break;
 
         case CMD_BODY_TIME:
-            vary->bv[cmd->bodytime.bi].t = cmd->bodytime.t;
+            sol_lerp_cmd(&gl.lerp, cmd);
             break;
 
         case CMD_GOAL_OPEN:
@@ -221,7 +235,7 @@ static void game_run_cmd(const union cmd *cmd)
             if (!gd.goal_e)
             {
                 gd.goal_e = 1;
-                gd.goal_k = first_update ? 1.0f : 0.0f;
+                gl.goal_k[CURR] = first_update ? 1.0f : 0.0f;
             }
             break;
 
@@ -242,7 +256,7 @@ static void game_run_cmd(const union cmd *cmd)
             break;
 
         case CMD_BALL_RADIUS:
-            vary->uv[curr_ball].r = cmd->ballradius.r;
+            sol_lerp_cmd(&gl.lerp, cmd);
             break;
 
         case CMD_CLEAR_ITEMS:
@@ -255,51 +269,37 @@ static void game_run_cmd(const union cmd *cmd)
             break;
 
         case CMD_CLEAR_BALLS:
-            if (vary->uv)
-            {
-                free(vary->uv);
-                vary->uv = NULL;
-            }
-            vary->uc = 0;
+            sol_lerp_cmd(&gl.lerp, cmd);
             break;
 
         case CMD_BALL_POSITION:
-            up = vary->uv + curr_ball;
-
-            v_cpy(up->p, cmd->ballpos.p);
+            sol_lerp_cmd(&gl.lerp, cmd);
             break;
 
         case CMD_BALL_BASIS:
-            up = vary->uv + curr_ball;
-
-            v_cpy(up->e[0], cmd->ballbasis.e[0]);
-            v_cpy(up->e[1], cmd->ballbasis.e[1]);
-            v_crs(up->e[2], up->e[0], up->e[1]);
+            sol_lerp_cmd(&gl.lerp, cmd);
             break;
 
         case CMD_BALL_PEND_BASIS:
-            up = vary->uv + curr_ball;
-
-            v_cpy(up->E[0], cmd->ballpendbasis.E[0]);
-            v_cpy(up->E[1], cmd->ballpendbasis.E[1]);
-            v_crs(up->E[2], up->E[0], up->E[1]);
+            sol_lerp_cmd(&gl.lerp, cmd);
             break;
 
         case CMD_VIEW_POSITION:
-            v_cpy(gd.view.p, cmd->viewpos.p);
+            v_cpy(view->p, cmd->viewpos.p);
             break;
 
         case CMD_VIEW_CENTER:
-            v_cpy(gd.view.c, cmd->viewcenter.c);
+            v_cpy(view->c, cmd->viewcenter.c);
             break;
 
         case CMD_VIEW_BASIS:
-            v_cpy(gd.view.e[0], cmd->viewbasis.e[0]);
-            v_cpy(gd.view.e[1], cmd->viewbasis.e[1]);
-            v_crs(gd.view.e[2], gd.view.e[0], gd.view.e[1]);
+            v_cpy(view->e[0], cmd->viewbasis.e[0]);
+            v_cpy(view->e[1], cmd->viewbasis.e[1]);
+            v_crs(view->e[2], view->e[0], view->e[1]);
             break;
 
         case CMD_CURRENT_BALL:
+            sol_lerp_cmd(&gl.lerp, cmd);
             curr_ball = cmd->currball.ui;
             break;
 
@@ -308,28 +308,7 @@ static void game_run_cmd(const union cmd *cmd)
             break;
 
         case CMD_STEP_SIMULATION:
-            /*
-             * Simulate body motion.
-             *
-             * This is done on the client side due to replay file size
-             * concerns and isn't done as part of CMD_END_OF_UPDATE to
-             * match the server state as closely as possible.  Body time
-             * is still synchronized with the server on a semi-regular
-             * basis and path indices are handled through CMD_BODY_PATH,
-             * thus this code doesn't need to be as sophisticated as
-             * sol_body_step.
-             */
-
-            dt = cmd->stepsim.dt;
-
-            for (i = 0; i < vary->bc; i++)
-            {
-                struct v_body *bp = vary->bv + i;
-                struct v_path *pp = vary->pv + bp->pi;
-
-                if (bp->pi >= 0 && pp->f)
-                    bp->t += dt;
-            }
+            sol_lerp_cmd(&gl.lerp, cmd);
             break;
 
         case CMD_MAP:
@@ -337,14 +316,13 @@ static void game_run_cmd(const union cmd *cmd)
              * Note a version (mis-)match between the loaded map and what
              * the server has. (This doesn't actually load a map.)
              */
-
             game_compat_map = version.x == cmd->map.version.x;
             break;
 
         case CMD_TILT_AXES:
             got_tilt_axes = 1;
-            v_cpy(gd.tilt.x, cmd->tiltaxes.x);
-            v_cpy(gd.tilt.z, cmd->tiltaxes.z);
+            v_cpy(tilt->x, cmd->tiltaxes.x);
+            v_cpy(tilt->z, cmd->tiltaxes.z);
             break;
 
         case CMD_NONE:
@@ -353,6 +331,7 @@ static void game_run_cmd(const union cmd *cmd)
         }
     }
 }
+
 void game_client_sync(fs_file demo_fp)
 {
     union cmd *cmdp;
@@ -386,21 +365,28 @@ int  game_client_init(const char *file_name)
 
     gd.state = 1;
 
-    game_tilt_init(&gd.tilt);
+    /* Initialize game state. */
 
-    /* Initialize jump and goal states. */
+    game_tilt_init(&gd.tilt);
+    game_view_init(&gd.view);
 
-    gd.jump_e = 1;
-    gd.jump_b = 0;
+    gd.jump_e  = 1;
+    gd.jump_b  = 0;
+    gd.jump_dt = 0.0f;
 
     gd.goal_e = 0;
     gd.goal_k = 0.0f;
 
-    /* Initialise the level, background, particles, fade, and view. */
+    /* Initialize interpolation. */
+
+    game_lerp_init(&gl, &gd);
+
+    /* Initialize fade. */
 
     gd.fade_k =  1.0f;
     gd.fade_d = -2.0f;
 
+    /* Load level info. */
 
     version.x = 0;
     version.y = 0;
@@ -426,11 +412,17 @@ int  game_client_init(const char *file_name)
 
     game_compat_map = version.x == 1;
 
+    /* Initialize particles. */
+
     part_reset(GOAL_HEIGHT, JUMP_HEIGHT);
 
+    /* Initialize command state. */
+
     ups          = 0;
     first_update = 1;
 
+    /* Initialize background. */
+
     back_init(grad_name);
     sol_load_full(&gd.back, back_name, 0);
 
@@ -442,6 +434,7 @@ void game_client_free(void)
     if (gd.state)
     {
         game_proxy_clr();
+        game_lerp_free(&gl);
         sol_free_full(&gd.file);
         sol_free_full(&gd.back);
         back_free();
@@ -451,8 +444,9 @@ void game_client_free(void)
 
 /*---------------------------------------------------------------------------*/
 
-void game_client_draw(int pose, float t)
+void game_client_draw(int pose, float t, float a)
 {
+    game_lerp_apply(&gl, &gd, a);
     game_draw(&gd, pose, t);
 }
 
@@ -475,12 +469,13 @@ int curr_status(void)
 
 /*---------------------------------------------------------------------------*/
 
-
 void game_look(float phi, float theta)
 {
-    gd.view.c[0] = gd.view.p[0] + fsinf(V_RAD(theta)) * fcosf(V_RAD(phi));
-    gd.view.c[1] = gd.view.p[1] +                       fsinf(V_RAD(phi));
-    gd.view.c[2] = gd.view.p[2] - fcosf(V_RAD(theta)) * fcosf(V_RAD(phi));
+    struct game_view *view = &gl.view[CURR];
+
+    view->c[0] = view->p[0] + fsinf(V_RAD(theta)) * fcosf(V_RAD(phi));
+    view->c[1] = view->p[1] +                       fsinf(V_RAD(phi));
+    view->c[2] = view->p[2] - fcosf(V_RAD(theta)) * fcosf(V_RAD(phi));
 }
 
 /*---------------------------------------------------------------------------*/
@@ -518,7 +513,7 @@ void game_fade(float d)
 
 void game_client_fly(float k)
 {
-    game_view_fly(&gd.view, &gd.file.vary, k);
+    game_view_fly(&gl.view[CURR], &gd.file.vary, k);
 }
 
 /*---------------------------------------------------------------------------*/
index 4e28b18..d3bca90 100644 (file)
@@ -15,7 +15,7 @@ enum
 int   game_client_init(const char *);
 void  game_client_free(void);
 void  game_client_sync(fs_file);
-void  game_client_draw(int, float);
+void  game_client_draw(int, float, float);
 
 int   curr_clock(void);
 int   curr_coins(void);
index f958b1e..174c4a9 100644 (file)
@@ -113,6 +113,8 @@ void lockstep_clr(struct lockstep *);
 void lockstep_run(struct lockstep *, float);
 void lockstep_scl(struct lockstep *, float);
 
+#define lockstep_blend(ls) ((ls)->at / (ls)->dt)
+
 /*---------------------------------------------------------------------------*/
 
 #endif
index e0097db..f735cdf 100644 (file)
@@ -603,3 +603,61 @@ void game_draw(const struct game_draw *gd, int pose, float t)
 }
 
 /*---------------------------------------------------------------------------*/
+
+#define CURR 0
+#define PREV 1
+
+void game_lerp_init(struct game_lerp *gl, struct game_draw *gd)
+{
+    sol_load_lerp(&gl->lerp, &gd->file.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)
+{
+    /* Solid. */
+
+    sol_lerp_apply(&gl->lerp, 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);
+}
+
+/*---------------------------------------------------------------------------*/
index bc79058..5fcb4c6 100644 (file)
@@ -4,6 +4,8 @@
 #include "solid_draw.h"
 #include "game_common.h"
 
+/*---------------------------------------------------------------------------*/
+
 struct game_draw
 {
     int state;
@@ -30,4 +32,24 @@ struct game_draw
 
 void game_draw(const struct game_draw *, int, float);
 
+/*---------------------------------------------------------------------------*/
+
+struct game_lerp
+{
+    struct s_lerp lerp;
+
+    struct game_tilt tilt[2];
+    struct game_view view[2];
+
+    float goal_k[2];
+    float jump_dt[2];
+};
+
+void game_lerp_init(struct game_lerp *, struct game_draw *);
+void game_lerp_free(struct game_lerp *);
+void game_lerp_copy(struct game_lerp *);
+void game_lerp_apply(struct game_lerp *, struct game_draw *, float);
+
+/*---------------------------------------------------------------------------*/
+
 #endif
index 0d7b1ab..0516f3f 100644 (file)
@@ -517,6 +517,7 @@ void game_server_free(void)
     {
         sol_quit_sim();
         sol_free_vary(&file);
+        sol_free_base(&base);
         server_state = 0;
     }
 }
@@ -801,6 +802,11 @@ void game_server_step(float dt)
     lockstep_run(&server_step, dt);
 }
 
+float game_server_blend(void)
+{
+    return lockstep_blend(&server_step);
+}
+
 /*---------------------------------------------------------------------------*/
 
 void game_set_goal(void)
index 07137b1..1621d36 100644 (file)
@@ -12,6 +12,7 @@
 int   game_server_init(const char *, int, int);
 void  game_server_free(void);
 void  game_server_step(float);
+float game_server_blend(void);
 
 void  game_set_goal(void);
 void  game_clr_goal(void);
index 961000d..6dd19f1 100644 (file)
@@ -103,7 +103,6 @@ static int handle_key_dn(SDL_Event *e)
         if (config_cheat())
             toggle_wire();
         break;
-
     case SDLK_RETURN:
         d = st_buttn(config_get_d(CONFIG_JOYSTICK_BUTTON_A), 1);
         break;
index f641724..f812d27 100644 (file)
@@ -503,7 +503,7 @@ void level_snap(int i, const char *path)
         video_clear();
         game_client_fly(1.0f);
         game_kill_fade();
-        game_client_draw(POSE_LEVEL, 0);
+        game_client_draw(POSE_LEVEL, 0, 1.0f);
         SDL_GL_SwapBuffers();
 
         image_snap(filename);
index 8e75b5e..8691ba3 100644 (file)
@@ -228,7 +228,7 @@ static void ball_paint(int id, float t)
     }
     video_pop_matrix();
 
-    game_client_draw(POSE_BALL, t);
+    game_client_draw(POSE_BALL, t, demo_play_blend());
     gui_paint(id);
 }
 
index cb13b1f..7e388dc 100644 (file)
@@ -443,7 +443,7 @@ static int demo_play_enter(struct state *st, struct state *prev)
 
 static void demo_play_paint(int id, float t)
 {
-    game_client_draw(0, t);
+    game_client_draw(0, t, demo_play_blend());
 
     if (show_hud)
         hud_paint();
@@ -621,7 +621,7 @@ static int demo_end_enter(struct state *st, struct state *prev)
 
 static void demo_end_paint(int id, float t)
 {
-    game_client_draw(0, t);
+    game_client_draw(0, t, demo_play_blend());
     gui_paint(id);
 
     if (demo_paused)
index 2623181..14d1f65 100644 (file)
@@ -385,7 +385,7 @@ static void help_demo_leave(struct state *st, struct state *next, int id)
 
 static void help_demo_paint(int id, float t)
 {
-    game_client_draw(0, t);
+    game_client_draw(0, t, demo_play_blend());
 }
 
 static void help_demo_timer(int id, float dt)
index a83c6a4..64fc1b4 100644 (file)
@@ -136,7 +136,7 @@ static int level_buttn(int b, int d)
 
 static void poser_paint(int id, float t)
 {
-    game_client_draw(POSE_LEVEL, t);
+    game_client_draw(POSE_LEVEL, t, 1.0f);
 }
 
 static int poser_buttn(int c, int d)
index e71dc21..2e5ba1b 100644 (file)
@@ -157,7 +157,7 @@ static void name_paint(int id, float t)
         video_pop_matrix();
     }
     else
-        game_client_draw(0, t);
+        game_client_draw(0, t, 1.0f);
 
     gui_paint(id);
 }
index 7f12f3c..3c180a4 100644 (file)
@@ -134,7 +134,7 @@ static int play_ready_enter(struct state *st, struct state *prev)
 
 static void play_ready_paint(int id, float t)
 {
-    game_client_draw(0, t);
+    game_client_draw(0, t, 1.0f);
     hud_view_paint();
     gui_paint(id);
 }
@@ -215,7 +215,7 @@ static int play_set_enter(struct state *st, struct state *prev)
 
 static void play_set_paint(int id, float t)
 {
-    game_client_draw(0, t);
+    game_client_draw(0, t, 1.0f);
     hud_view_paint();
     gui_paint(id);
 }
@@ -333,7 +333,7 @@ static int play_loop_enter(struct state *st, struct state *prev)
 
 static void play_loop_paint(int id, float t)
 {
-    game_client_draw(0, t);
+    game_client_draw(0, t, game_server_blend());
 
     if (show_hud)
         hud_paint();
@@ -518,7 +518,7 @@ static void look_leave(struct state *st, struct state *next, int id)
 
 static void look_paint(int id, float t)
 {
-    game_client_draw(0, t);
+    game_client_draw(0, t, 1.0f);
 }
 
 static void look_point(int id, int x, int y, int dx, int dy)
index c6fa4ca..b21478e 100644 (file)
@@ -30,7 +30,7 @@ void shared_leave(struct state *st, struct state *next, int id)
 
 void shared_paint(int id, float t)
 {
-    game_client_draw(0, t);
+    game_client_draw(0, t, 1.0f);
     gui_paint(id);
 }
 
index 033ee44..70c0439 100644 (file)
@@ -135,3 +135,195 @@ void sol_free_vary(struct s_vary *fp)
 }
 
 /*---------------------------------------------------------------------------*/
+
+#define CURR 0
+#define PREV 1
+
+static int curr_ball;
+
+int sol_lerp_cmd(struct s_lerp *fp, const union cmd *cmd)
+{
+    struct l_ball (*uv)[2];
+    struct l_ball *up;
+
+    int i, rc = 0;
+
+    switch (cmd->type)
+    {
+    case CMD_MAKE_BALL:
+        if ((uv = realloc(fp->uv, sizeof (*uv) * (fp->uc + 1))))
+        {
+            struct v_ball *up;
+
+            fp->uv = uv;
+            fp->uc++;
+
+            /* Sync the main structure. */
+
+            if ((up = realloc(fp->vary->uv, sizeof (*up) * fp->uc)))
+            {
+                fp->vary->uv = up;
+                fp->vary->uc = fp->uc;
+
+                curr_ball = fp->uc - 1;
+                rc = 1;
+            }
+        }
+        break;
+
+    case CMD_BODY_PATH:
+        fp->bv[cmd->bodypath.bi][CURR].pi = cmd->bodypath.pi;
+        break;
+
+    case CMD_BODY_TIME:
+        fp->bv[cmd->bodytime.bi][CURR].t = cmd->bodytime.t;
+        break;
+
+    case CMD_BALL_RADIUS:
+        fp->uv[curr_ball][CURR].r = cmd->ballradius.r;
+        break;
+
+    case CMD_CLEAR_BALLS:
+        free(fp->uv);
+        fp->uv = NULL;
+        fp->uc = 0;
+
+        free(fp->vary->uv);
+        fp->vary->uv = NULL;
+        fp->vary->uc = 0;
+        break;
+
+    case CMD_BALL_POSITION:
+        up = &fp->uv[curr_ball][CURR];
+        v_cpy(up->p, cmd->ballpos.p);
+        break;
+
+    case CMD_BALL_BASIS:
+        up = &fp->uv[curr_ball][CURR];
+        v_cpy(up->e[0], cmd->ballbasis.e[0]);
+        v_cpy(up->e[1], cmd->ballbasis.e[1]);
+        v_crs(up->e[2], up->e[0], up->e[1]);
+        break;
+
+    case CMD_BALL_PEND_BASIS:
+        up = &fp->uv[curr_ball][CURR];
+        v_cpy(up->E[0], cmd->ballpendbasis.E[0]);
+        v_cpy(up->E[1], cmd->ballpendbasis.E[1]);
+        v_crs(up->E[2], up->E[0], up->E[1]);
+        break;
+
+    case CMD_CURRENT_BALL:
+        curr_ball = cmd->currball.ui;
+        break;
+
+    case CMD_STEP_SIMULATION:
+        /*
+         * Simulate body motion.
+         *
+         * This is done on the client side due to replay file size
+         * concerns and isn't done as part of CMD_END_OF_UPDATE to
+         * match the server state as closely as possible.  Body time
+         * is still synchronized with the server on a semi-regular
+         * basis and path indices are handled through CMD_BODY_PATH,
+         * thus this code doesn't need to be as sophisticated as
+         * sol_body_step.
+         */
+
+        for (i = 0; i < fp->bc; i++)
+        {
+            struct l_body *bp = &fp->bv[i][CURR];
+            struct v_path *pp = &fp->vary->pv[bp->pi];
+
+            if (bp->pi >= 0 && pp->f)
+                bp->t += cmd->stepsim.dt;
+        }
+        break;
+
+    default:
+        break;
+    }
+
+    return rc;
+}
+
+void sol_lerp_copy(struct s_lerp *fp)
+{
+    int i;
+
+    for (i = 0; i < fp->bc; i++)
+        fp->bv[i][PREV] = fp->bv[i][CURR];
+
+    for (i = 0; i < fp->uc; i++)
+        fp->uv[i][PREV] = fp->uv[i][CURR];
+}
+
+void sol_lerp_apply(struct s_lerp *fp, float a)
+{
+    int i;
+
+    for (i = 0; i < fp->bc; i++)
+    {
+        if (fp->bv[i][PREV].pi == fp->bv[i][CURR].pi)
+            fp->vary->bv[i].t = (fp->bv[i][PREV].t * (1.0f - a) +
+                                 fp->bv[i][CURR].t * a);
+        else
+            fp->vary->bv[i].t = fp->bv[i][CURR].t * a;
+
+        fp->vary->bv[i].pi = fp->bv[i][CURR].pi;
+    }
+
+    for (i = 0; i < fp->uc; i++)
+    {
+        e_lerp(fp->vary->uv[i].e, fp->uv[i][PREV].e, fp->uv[i][CURR].e, a);
+        v_lerp(fp->vary->uv[i].p, fp->uv[i][PREV].p, fp->uv[i][CURR].p, a);
+        e_lerp(fp->vary->uv[i].E, fp->uv[i][PREV].E, fp->uv[i][CURR].E, a);
+
+        fp->vary->uv[i].r = (fp->uv[i][PREV].r * (1.0f - a) +
+                             fp->uv[i][CURR].r * a);
+    }
+}
+
+int sol_load_lerp(struct s_lerp *fp, struct s_vary *vary)
+{
+    int i;
+
+    fp->vary = vary;
+
+    if (fp->vary->bc)
+    {
+        fp->bv = calloc(fp->vary->bc, sizeof (*fp->bv));
+        fp->bc = fp->vary->bc;
+
+        for (i = 0; i < fp->vary->bc; i++)
+            fp->bv[i][CURR].pi = fp->vary->bv[i].pi;
+    }
+
+    if (fp->vary->uc)
+    {
+        fp->uv = calloc(fp->vary->uc, sizeof (*fp->uv));
+        fp->uc = fp->vary->uc;
+
+        for (i = 0; i < fp->vary->uc; i++)
+        {
+            e_cpy(fp->uv[i][CURR].e, fp->vary->uv[i].e);
+            v_cpy(fp->uv[i][CURR].p, fp->vary->uv[i].p);
+            e_cpy(fp->uv[i][CURR].E, fp->vary->uv[i].E);
+
+            fp->uv[i][CURR].r = fp->vary->uv[i].r;
+        }
+    }
+
+    sol_lerp_copy(fp);
+
+    return 1;
+}
+
+void sol_free_lerp(struct s_lerp *fp)
+{
+    if (fp->bv) free(fp->bv);
+    if (fp->uv) free(fp->uv);
+
+    memset(fp, 0, sizeof (*fp));
+}
+
+/*---------------------------------------------------------------------------*/
index 403099e..c758d8a 100644 (file)
@@ -93,4 +93,47 @@ void sol_free_vary(struct s_vary *);
 
 /*---------------------------------------------------------------------------*/
 
+/*
+ * Buffers changes to the varying SOL data for interpolation purposes.
+ */
+
+struct l_body
+{
+    float t;                                   /* time on current path       */
+
+    int pi;
+};
+
+struct l_ball
+{
+    float e[3][3];                             /* basis of orientation       */
+    float p[3];                                /* position vector            */
+    float E[3][3];                             /* basis of pendulum          */
+    float r;                                   /* radius                     */
+};
+
+struct s_lerp
+{
+    struct s_vary *vary;
+
+    int bc;
+    int uc;
+
+    struct l_body (*bv)[2];
+    struct l_ball (*uv)[2];
+};
+
+/*---------------------------------------------------------------------------*/
+
+#include "cmd.h"
+
+int  sol_load_lerp(struct s_lerp *, struct s_vary *);
+void sol_free_lerp(struct s_lerp *);
+
+void sol_lerp_copy(struct s_lerp *);
+void sol_lerp_apply(struct s_lerp *, float);
+int  sol_lerp_cmd(struct s_lerp *, const union cmd *);
+
+/*---------------------------------------------------------------------------*/
+
 #endif
index cb6ba52..d540bae 100644 (file)
     (u)[2] = (p)[2] + (v)[2] * (t); \
 } while (0)
 
-/*---------------------------------------------------------------------------*/
+#define v_lerp(u, v, w, a) {                       \
+    (u)[0] = (v)[0] * (1.0f - (a)) + (w)[0] * (a); \
+    (u)[1] = (v)[1] * (1.0f - (a)) + (w)[1] * (a); \
+    (u)[2] = (v)[2] * (1.0f - (a)) + (w)[2] * (a); \
+}
+
+#define e_cpy(d, e) {      \
+    v_cpy((d)[0], (e)[0]); \
+    v_cpy((d)[1], (e)[1]); \
+    v_cpy((d)[2], (e)[2]); \
+}
+
+#define e_lerp(c, d, e, a) {           \
+    v_lerp((c)[0], (d)[0], (e)[0], a); \
+    v_lerp((c)[1], (d)[1], (e)[1], a); \
+    v_lerp((c)[2], (d)[2], (e)[2], a); \
+}
 
+/*---------------------------------------------------------------------------*/
 
 void   v_nrm(float *, const float *);
 void   v_crs(float *, const float *, const float *);