+
+#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));
+}
+
+/*---------------------------------------------------------------------------*/