static int status = GAME_NONE; /* Outcome of the game */
-static float game_rx; /* Floor rotation about X axis */
-static float game_rz; /* Floor rotation about Z axis */
+static struct game_tilt tilt; /* Floor rotation */
static float view_a; /* Ideal view rotation about Y axis */
static float view_fov; /* Field of view */
static const float gup[] = { 0.0f, +9.8f, 0.0f };
static const float gdn[] = { 0.0f, -9.8f, 0.0f };
+ /*
+ * Neverball <= 1.5.1 does not send explicit tilt axes, rotation
+ * happens directly around view vectors. So for compatibility if
+ * at the time of receiving tilt angles we have not yet received
+ * the tilt axes, we use the view vectors.
+ */
+ static int got_tilt_axes;
+
float f[3];
if (client_state)
switch (cmd->type)
{
case CMD_END_OF_UPDATE:
+
+ got_tilt_axes = 0;
+
if (first_update)
{
first_update = 0;
/* Compute gravity for particle effects. */
if (status == GAME_GOAL)
- game_comp_grav(f, gup, view_a, game_rx, game_rz);
+ game_comp_grav(f, gup, &tilt);
else
- game_comp_grav(f, gdn, view_a, game_rx, game_rz);
+ game_comp_grav(f, gdn, &tilt);
/* Step particle, goal and jump effects. */
break;
case CMD_TILT_ANGLES:
- game_rx = cmd->tiltangles.x;
- game_rz = cmd->tiltangles.z;
+ if (!got_tilt_axes)
+ game_tilt_axes(&tilt, view_e);
+
+ tilt.rx = cmd->tiltangles.x;
+ tilt.rz = cmd->tiltangles.z;
break;
case CMD_SOUND:
game_compat_map = version.x == cmd->map.version.x;
break;
+ case CMD_TILT_AXES:
+ got_tilt_axes = 1;
+ v_cpy(tilt.x, cmd->tiltaxes.x);
+ v_cpy(tilt.z, cmd->tiltaxes.z);
+ break;
+
case CMD_NONE:
case CMD_MAX:
break;
client_state = 1;
- game_rx = 0.0f;
- game_rz = 0.0f;
+ game_tilt_init(&tilt);
/* Initialize jump and goal states. */
{
const float *ball_p = file.uv->p;
- const float Y[3] = { 0.0f, 1.0f, 0.0f };
- float z[3];
-
- v_cpy(z, view_e[2]);
-
- /* Handle possible top-down view. */
-
- if (fabsf(v_dot(view_e[1], Y)) < fabsf(v_dot(view_e[2], Y)))
- v_inv(z, view_e[1]);
-
/* Rotate the environment about the position of the ball. */
glTranslatef(+ball_p[0], +ball_p[1] * d, +ball_p[2]);
- glRotatef(-game_rz * d, z[0], z[1], z[2]);
- glRotatef(-game_rx * d, view_e[0][0], view_e[0][1], view_e[0][2]);
+ glRotatef(-tilt.rz * d, tilt.z[0], tilt.z[1], tilt.z[2]);
+ glRotatef(-tilt.rx * d, tilt.x[0], tilt.x[1], tilt.x[2]);
glTranslatef(-ball_p[0], -ball_p[1] * d, -ball_p[2]);
}
{
if (d < 0)
{
- glRotatef(game_rz * 2, view_e[2][0], view_e[2][1], view_e[2][2]);
- glRotatef(game_rx * 2, view_e[0][0], view_e[0][1], view_e[0][2]);
+ 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(view_p[0], view_p[1] * d, view_p[2]);
}
}
-void game_comp_grav(float h[3], const float g[3],
- float view_a,
- float game_rx,
- float game_rz)
+/*---------------------------------------------------------------------------*/
+
+void game_tilt_init(struct game_tilt *tilt)
+{
+ tilt->x[0] = 1.0f;
+ tilt->x[1] = 0.0f;
+ tilt->x[2] = 0.0f;
+
+ tilt->rx = 0.0f;
+
+ tilt->z[0] = 0.0f;
+ tilt->z[1] = 0.0f;
+ tilt->z[2] = 1.0f;
+
+ tilt->rz = 0.0f;
+}
+
+/*
+ * Compute appropriate tilt axes from the view basis.
+ */
+void game_tilt_axes(struct game_tilt *tilt, float view_e[3][3])
+{
+ const float Y[3] = { 0.0f, 1.0f, 0.0f };
+
+ v_cpy(tilt->x, view_e[0]);
+ v_cpy(tilt->z, view_e[2]);
+
+ /* Handle possible top-down view. */
+
+ if (fabsf(v_dot(view_e[1], Y)) < fabsf(v_dot(view_e[2], Y)))
+ v_inv(tilt->z, view_e[1]);
+}
+
+void game_comp_grav(float h[3], const float g[3], const struct game_tilt *tilt)
{
- float x[3];
- float y[3] = { 0.0f, 1.0f, 0.0f };
- float z[3];
float X[16];
float Z[16];
float M[16];
/* Compute the gravity vector from the given world rotations. */
- z[0] = fsinf(V_RAD(view_a));
- z[1] = 0.0f;
- z[2] = fcosf(V_RAD(view_a));
-
- v_crs(x, y, z);
- v_crs(z, x, y);
- v_nrm(x, x);
- v_nrm(z, z);
-
- m_rot (Z, z, V_RAD(game_rz));
- m_rot (X, x, V_RAD(game_rx));
+ m_rot (Z, tilt->z, V_RAD(tilt->rz));
+ m_rot (X, tilt->x, V_RAD(tilt->rx));
m_mult(M, Z, X);
m_vxfm(h, M, g);
}
+
+/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
-void game_comp_grav(float h[3], const float g[3],
- float view_a,
- float game_rx,
- float game_rz);
+struct game_tilt
+{
+ float x[3], rx;
+ float z[3], rz;
+};
+
+void game_tilt_init(struct game_tilt *);
+void game_tilt_axes(struct game_tilt *, float view_e[3][3]);
+void game_comp_grav(float h[3], const float g[3], const struct game_tilt *);
/*---------------------------------------------------------------------------*/
static int status = GAME_NONE; /* Outcome of the game */
-static float game_rx; /* Floor rotation about X axis */
-static float game_rz; /* Floor rotation about Z axis */
+static struct game_tilt tilt; /* Floor rotation */
static float view_a; /* Ideal view rotation about Y axis */
static float view_dc; /* Ideal view distance above ball */
{
cmd.type = CMD_TILT_ANGLES;
- cmd.tiltangles.x = game_rx;
- cmd.tiltangles.z = game_rz;
+ cmd.tiltangles.x = tilt.rx;
+ cmd.tiltangles.z = tilt.rz;
+
+ game_proxy_enq(&cmd);
+}
+
+static void game_cmd_tiltaxes(void)
+{
+ cmd.type = CMD_TILT_AXES;
+
+ v_cpy(cmd.tiltaxes.x, tilt.x);
+ v_cpy(cmd.tiltaxes.z, tilt.z);
game_proxy_enq(&cmd);
}
input_init();
- game_rx = 0.0f;
- game_rz = 0.0f;
+ game_tilt_init(&tilt);
/* Initialize jump and goal states. */
/* Smooth jittery or discontinuous input. */
- game_rx += (input_get_x() - game_rx) * dt / RESPONSE;
- game_rz += (input_get_z() - game_rz) * dt / RESPONSE;
+ tilt.rx += (input_get_x() - tilt.rx) * dt / RESPONSE;
+ tilt.rz += (input_get_z() - tilt.rz) * dt / RESPONSE;
+
+ game_tilt_axes(&tilt, view_e);
+ game_cmd_tiltaxes();
game_cmd_tiltangles();
grow_step(fp, dt);
- game_comp_grav(h, g, view_a, game_rx, game_rz);
+ game_comp_grav(h, g, &tilt);
if (jump_b)
{
/*---------------------------------------------------------------------------*/
+#undef BYTES
+#define BYTES ARRAY_BYTES(3) * 2
+
+PUT_FUNC(CMD_TILT_AXES)
+{
+ put_array(fp, cmd->tiltaxes.x, 3);
+ put_array(fp, cmd->tiltaxes.z, 3);
+}
+END_FUNC;
+
+GET_FUNC(CMD_TILT_AXES)
+{
+ get_array(fp, cmd->tiltaxes.x, 3);
+ get_array(fp, cmd->tiltaxes.z, 3);
+}
+END_FUNC;
+
+/*---------------------------------------------------------------------------*/
+
#define PUT_CASE(t) case t: cmd_put_ ## t(fp, cmd); break
#define GET_CASE(t) case t: cmd_get_ ## t(fp, cmd); break
PUT_CASE(CMD_PATH_FLAG);
PUT_CASE(CMD_STEP_SIMULATION);
PUT_CASE(CMD_MAP);
+ PUT_CASE(CMD_TILT_AXES);
case CMD_NONE:
case CMD_MAX:
GET_CASE(CMD_PATH_FLAG);
GET_CASE(CMD_STEP_SIMULATION);
GET_CASE(CMD_MAP);
+ GET_CASE(CMD_TILT_AXES);
case CMD_NONE:
case CMD_MAX:
CMD_PATH_FLAG,
CMD_STEP_SIMULATION,
CMD_MAP,
+ CMD_TILT_AXES,
CMD_MAX
};
} version;
};
+struct cmd_tilt_axes
+{
+ HEADER;
+ float x[3], z[3];
+};
+
union cmd
{
HEADER;
struct cmd_path_flag pathflag;
struct cmd_step_simulation stepsim;
struct cmd_map map;
+ struct cmd_tilt_axes tiltaxes;
};
/* No module should see this. */