public static int sol_test_back(float dt, s_ball up, s_side sp, float[] o, float[] w) { float[] q = new float[3]; /* If the ball is not in front of the plane, the test passes. */ Vec3.v_sub(q, up.m_p, o); if (Vec3.v_dot(q, sp.m_n) - sp.m_d - up.m_r <= 0) { return(1); } /* If it's not in front of the plane after DT seconds, the test passes. */ Vec3.v_mad(q, q, up.m_v, dt); if (Vec3.v_dot(q, sp.m_n) - sp.m_d - up.m_r <= 0) { return(1); } /* Else, test fails. */ return(0); }
public static float sol_test_file(float dt, float[] T, float[] V, s_ball up, s_file fp) { float u, t = dt; float[] U = new float[3]; float[] W = new float[3]; int i; for (i = 0; i < fp.m_bc; i++) { s_body bp = fp.m_bv[i]; if ((u = sol_test_body(t, U, W, up, fp, bp)) < t) { Vec3.v_cpy(T, U); Vec3.v_cpy(V, W); t = u; } } return(t); }
public static float sol_test_side(float dt, float[] T, s_ball up, s_file fp, s_lump lp, s_side sp, float[] o, float[] w) { float t = v_side(T, o, w, sp.m_n, sp.m_d, up.m_p, up.m_v, up.m_r); int i; if (t < dt) { for (i = 0; i < lp.m_sc; i++) { s_side sq = fp.m_sv[fp.m_iv[lp.m_s0 + i]]; if (sp != sq && Vec3.v_dot(T, sq.m_n) - Vec3.v_dot(o, sq.m_n) - Vec3.v_dot(w, sq.m_n) * t > sq.m_d) { return((float)LARGE); } } } return(t); }
public static float sol_test_body(float dt, float[] T, float[] V, s_ball up, s_file fp, s_body bp) { float u, t = dt; float[] U = new float[3]; float[] O = new float[3]; float[] W = new float[3]; s_node np = fp.m_nv[bp.m_ni]; sol_body_p(O, fp, bp); sol_body_v(W, fp, bp); if ((u = sol_test_node(t, U, up, fp, np, O, W)) < t) { Vec3.v_cpy(T, U); Vec3.v_cpy(V, W); t = u; } return(t); }
public static float sol_test_vert(float dt, float[] T, s_ball up, s_vert vp, float[] o, float[] w) { return(v_vert(T, o, vp.m_p, w, up.m_p, up.m_v, up.m_r)); }
public static float sol_test_node(float dt, float[] T, s_ball up, s_file fp, s_node np, float[] o, float[] w) { float u, t = dt; float[] U = new float[3]; int i; /* Test all lumps */ for (i = 0; i < np.m_lc; i++) { s_lump lp = fp.m_lv[np.m_l0 + i]; if ((u = sol_test_lump(t, U, up, fp, lp, o, w)) < t) { Vec3.v_cpy(T, U); t = u; } } /* Test in front of this node */ if (np.m_ni >= 0 && sol_test_fore(t, up, fp.m_sv[np.m_si], o, w) != 0) { s_node nq = fp.m_nv[np.m_ni]; if ((u = sol_test_node(t, U, up, fp, nq, o, w)) < t) { Vec3.v_cpy(T, U); t = u; } } /* Test behind this node */ if (np.m_nj >= 0 && sol_test_back(t, up, fp.m_sv[np.m_si], o, w) != 0) { s_node nq = fp.m_nv[np.m_nj]; if ((u = sol_test_node(t, U, up, fp, nq, o, w)) < t) { Vec3.v_cpy(T, U); t = u; } } return(t); }
/* * Compute the positions of all balls after DT seconds have passed. */ public static void sol_ball_step(s_file fp, float dt) { int i; for (i = 0; i < fp.m_uc; i++) { s_ball up = fp.m_uv[i];// + i; Vec3.v_mad(up.m_p, up.m_p, up.m_v, dt); sol_rotate(up.m_e, up.m_w, dt); } }
public static float sol_test_edge(float dt, float[] T, s_ball up, s_file fp, s_edge ep, float[] o, float[] w) { float[] q = new float[3]; float[] u = new float[3]; Vec3.v_cpy(q, fp.m_vv[ep.m_vi].m_p); Vec3.v_sub(u, fp.m_vv[ep.m_vj].m_p, fp.m_vv[ep.m_vi].m_p); return(v_edge(T, o, q, u, w, up.m_p, up.m_v, up.m_r)); }
public static void sol_load_ball(IntPtr fin, s_ball bp) { Binary.get_array(fin, bp.m_p, 3); Binary.get_float(fin, ref bp.m_r); bp.m_e[0][0] = 1.0f; bp.m_e[0][1] = 0.0f; bp.m_e[0][2] = 0.0f; bp.m_e[1][0] = 0.0f; bp.m_e[1][1] = 1.0f; bp.m_e[1][2] = 0.0f; bp.m_e[2][0] = 0.0f; bp.m_e[2][1] = 0.0f; bp.m_e[2][2] = 1.0f; }
/* * Compute the lnew linear and angular velocities of a bouncing ball. * Q gives the position of the point of impact and W gives the * velocity of the object being impacted. */ public static float sol_bounce(s_ball up, float[] q, float[] w, float dt) { float[] n = new float[3]; float[] r = new float[3]; float[] d = new float[3]; float vn, wn; float[] p = up.m_p; float[] v = up.m_v; /* Find the normal of the impact. */ Vec3.v_sub(r, p, q); Vec3.v_sub(d, v, w); Vec3.v_nrm(n, r); /* Find the lnew angular velocity. */ Vec3.v_crs(up.m_w, d, r); Vec3.v_scl(up.m_w, up.m_w, -1.0f / (up.m_r * up.m_r)); /* Find the lnew linear velocity. */ vn = Vec3.v_dot(v, n); wn = Vec3.v_dot(w, n); Vec3.v_mad(v, v, n, 1.7f * (wn - vn)); Vec3.v_mad(p, q, n, up.m_r); /* Return the "energy" of the impact, to determine the sound amplitude. */ return(System.Math.Abs(Vec3.v_dot(n, d))); }
public static void game_run_cmd(Command cmd) { /* * 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. */ float[] f = new float[3]; if (client_state != 0) { s_ball[] up; float dt; int i; switch (cmd.type) { case cmd_type.CMD_END_OF_UPDATE: game_run_cmd_got_tilt_axes = 0; if (first_update != 0) { first_update = 0; break; } /* Compute gravity for particle effects. */ if (status == (int)GAME.GAME_GOAL) { game_common.game_tilt_grav(f, game_run_cmd_gup, tilt); } else { game_common.game_tilt_grav(f, game_run_cmd_gdn, tilt); } /* Step particle, goal and jump effects. */ if (ups > 0) { dt = 1.0f / (float)ups; if (goal_e != 0 && goal_k < 1.0f) { goal_k += dt; } if (jump_b != 0) { jump_dt += dt; if (1.0f < jump_dt) { jump_b = 0; } } Part.part_step(f, dt); } break; case cmd_type.CMD_MAKE_BALL: /* Allocate a new ball and mark it as the current ball. */ up = new s_ball[file.m_uc + 1]; { for (i = 0; i < file.m_uc; i++) { up[i] = file.m_uv[i]; } up[file.m_uc] = new s_ball(); } { file.m_uv = up; curr_ball = file.m_uc; file.m_uc++; } break; case cmd_type.CMD_MAKE_ITEM: /* Allocate and initialise a new item. */ { s_item[] hp = new s_item[file.m_hc + 1]; { for (i = 0; i < file.m_hc; i++) { hp[i] = file.m_hv[i]; } } { s_item h = new s_item(); Vec3.v_cpy(h.m_p, cmd.mkitem.p); h.m_t = cmd.mkitem.t; h.m_n = cmd.mkitem.n; file.m_hv = hp; file.m_hv[file.m_hc] = h; file.m_hc++; } } break; case cmd_type.CMD_PICK_ITEM: /* Set up particle effects and discard the item. */ { s_item hp = file.m_hv[cmd.pkitem.hi]; Item.item_color(hp, f); Part.part_burst(hp.m_p, f); hp.m_t = Solid.ITEM_NONE; } break; case cmd_type.CMD_TILT_ANGLES: if (game_run_cmd_got_tilt_axes == 0) { game_common.game_tilt_axes(tilt, view_e); } tilt.rx = cmd.tiltangles.x; tilt.rz = cmd.tiltangles.z; break; case cmd_type.CMD_SOUND: /* Play the sound, then free its file name. */ if (cmd.sound.n != null) { Audio.audio_play(cmd.sound.n, cmd.sound.a); /* * FIXME Command memory management should be done * elsewhere and done properly. */ cmd.sound.n = null; } break; case cmd_type.CMD_TIMER: timer = cmd.timer.t; break; case cmd_type.CMD_STATUS: status = cmd.status.t; break; case cmd_type.CMD_COINS: coins = cmd.coins.n; break; case cmd_type.CMD_JUMP_ENTER: jump_b = 1; jump_e = 0; jump_dt = 0.0f; break; case cmd_type.CMD_JUMP_EXIT: jump_e = 1; break; case cmd_type.CMD_BODY_PATH: file.m_bv[cmd.bodypath.bi].m_pi = cmd.bodypath.pi; break; case cmd_type.CMD_BODY_TIME: file.m_bv[cmd.bodytime.bi].m_t = cmd.bodytime.t; break; case cmd_type.CMD_GOAL_OPEN: /* * Enable the goal and make sure it's fully visible if * this is the first update. */ if (goal_e == 0) { goal_e = 1; goal_k = first_update != 0 ? 1.0f : 0.0f; } break; case cmd_type.CMD_SWCH_ENTER: file.m_xv[cmd.swchenter.xi].m_e = 1; break; case cmd_type.CMD_SWCH_TOGGLE: file.m_xv[cmd.swchtoggle.xi].m_f = file.m_xv[cmd.swchtoggle.xi].m_f == 0 ? 1 : 0; break; case cmd_type.CMD_SWCH_EXIT: file.m_xv[cmd.swchexit.xi].m_e = 0; break; case cmd_type.CMD_UPDATES_PER_SECOND: ups = cmd.ups.n; break; case cmd_type.CMD_BALL_RADIUS: file.m_uv[curr_ball].m_r = cmd.ballradius.r; break; case cmd_type.CMD_CLEAR_ITEMS: if (file.m_hv != null) { file.m_hv = null; } file.m_hc = 0; break; case cmd_type.CMD_CLEAR_BALLS: if (file.m_uv != null) { file.m_uv = null; } file.m_uc = 0; break; case cmd_type.CMD_BALL_POSITION: Vec3.v_cpy(file.m_uv[curr_ball].m_p, cmd.ballpos.p); break; case cmd_type.CMD_BALL_BASIS: Vec3.v_cpy(file.m_uv[curr_ball].m_e[0], cmd.ballbasis.e[0]); Vec3.v_cpy(file.m_uv[curr_ball].m_e[1], cmd.ballbasis.e[1]); Vec3.v_crs(file.m_uv[curr_ball].m_e[2], file.m_uv[curr_ball].m_e[0], file.m_uv[curr_ball].m_e[1]); break; case cmd_type.CMD_VIEW_POSITION: Vec3.v_cpy(view_p, cmd.viewpos.p); break; case cmd_type.CMD_VIEW_CENTER: Vec3.v_cpy(view_c, cmd.viewcenter.c); break; case cmd_type.CMD_VIEW_BASIS: Vec3.v_cpy(view_e[0], cmd.viewbasis.e[0]); Vec3.v_cpy(view_e[1], cmd.viewbasis.e[1]); Vec3.v_crs(view_e[2], view_e[0], view_e[1]); break; case cmd_type.CMD_CURRENT_BALL: curr_ball = cmd.currball.ui; break; case cmd_type.CMD_PATH_FLAG: file.m_pv[cmd.pathflag.pi].m_f = cmd.pathflag.f; break; case cmd_type.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_type.CMD_END_OF_UPDATE to * match the server state as closely as possible. Body * time is still synchronised with the server on a * semi-regular basis and path indices are handled through * cmd_type.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 < file.m_bc; i++) { s_body bp = file.m_bv[i]; // + i; if (bp.m_pi >= 0) { s_path pp = file.m_pv[bp.m_pi]; if (pp.m_f != 0) { bp.m_t += dt; } } } break; case cmd_type.CMD_MAP: /* * Note if the loaded map matches the server's * expectations. (No, this doesn't actually load a map, * yet. Something else somewhere else does.) */ cmd.map.name = null; game_compat_map = version_x == cmd.map.version.x ? 1 : 0; break; case cmd_type.CMD_TILT_AXES: game_run_cmd_got_tilt_axes = 1; Vec3.v_cpy(tilt.x, cmd.tiltaxes.x); Vec3.v_cpy(tilt.z, cmd.tiltaxes.z); break; case cmd_type.CMD_NONE: case cmd_type.CMD_MAX: break; } } }
public static float sol_test_lump(float dt, float[] T, s_ball up, s_file fp, s_lump lp, float[] o, float[] w) { float[] U = new float[3] { 0.0f, 0.0f, 0.0f }; float u, t = dt; int i; /* Short circuit a non-solid lump. */ if ((lp.m_fl & Solid.L_DETAIL) != 0) { return(t); } /* Test all verts */ if (up.m_r > 0.0f) { for (i = 0; i < lp.m_vc; i++) { s_vert vp = fp.m_vv[fp.m_iv[lp.m_v0 + i]]; if ((u = sol_test_vert(t, U, up, vp, o, w)) < t) { Vec3.v_cpy(T, U); t = u; } } } /* Test all edges */ if (up.m_r > 0.0f) { for (i = 0; i < lp.m_ec; i++) { s_edge ep = fp.m_ev[fp.m_iv[lp.m_e0 + i]]; if ((u = sol_test_edge(t, U, up, fp, ep, o, w)) < t) { Vec3.v_cpy(T, U); t = u; } } } /* Test all sides */ for (i = 0; i < lp.m_sc; i++) { s_side sp = fp.m_sv[fp.m_iv[lp.m_s0 + i]]; if ((u = sol_test_side(t, U, up, fp, lp, sp, o, w)) < t) { Vec3.v_cpy(T, U); t = u; } } return(t); }
/* * Step the physics forward DT seconds under the influence of gravity * vector G. If the ball gets pinched between two moving solids, this * loop might not terminate. It is better to do something physically * impossible than to lock up the game. So, if we make more than C * iterations, punt it. */ public static float sol_step(s_file fp, float[] g, float dt, int ui, ref int m, bool m_IsNull) { float d, e, nt, b = 0.0f, tt = dt; float[] P = new float[3]; float[] V = new float[3]; float[] v = new float[3]; float[] r = new float[3]; float[] a = new float[3]; int c = 16; defer_cmds = 1; if (ui < fp.m_uc) { s_ball up = fp.m_uv[ui]; /* If the ball is in contact with a surface, apply friction. */ Vec3.v_cpy(a, up.m_v); Vec3.v_cpy(v, up.m_v); Vec3.v_cpy(up.m_v, g); if (!m_IsNull && sol_test_file(tt, P, V, up, fp) < 0.0005f) { Vec3.v_cpy(up.m_v, v); Vec3.v_sub(r, P, up.m_p); if ((d = Vec3.v_dot(r, g) / (Vec3.v_len(r) * Vec3.v_len(g))) > 0.999f) { if ((e = (Vec3.v_len(up.m_v) - dt)) > 0.0f) { /* Scale the linear velocity. */ Vec3.v_nrm(up.m_v, up.m_v); Vec3.v_scl(up.m_v, up.m_v, e); /* Scale the angular velocity. */ Vec3.v_sub(v, V, up.m_v); Vec3.v_crs(up.m_w, v, r); Vec3.v_scl(up.m_w, up.m_w, -1.0f / (up.m_r * up.m_r)); } else { /* Friction has brought the ball to a stop. */ up.m_v[0] = 0.0f; up.m_v[1] = 0.0f; up.m_v[2] = 0.0f; m++; } } else { Vec3.v_mad(up.m_v, v, g, tt); } } else { Vec3.v_mad(up.m_v, v, g, tt); } /* Test for collision. */ while (c > 0 && tt > 0 && tt > (nt = sol_test_file(tt, P, V, up, fp))) { cmd.type = cmd_type.CMD_STEP_SIMULATION; cmd.stepsim.dt = nt; sol_cmd_enq(cmd); sol_body_step(fp, nt); sol_swch_step(fp, nt); sol_ball_step(fp, nt); tt -= nt; if (b < (d = sol_bounce(up, P, V, nt))) { b = d; } c--; } cmd.type = cmd_type.CMD_STEP_SIMULATION; cmd.stepsim.dt = tt; sol_cmd_enq(cmd); sol_body_step(fp, tt); sol_swch_step(fp, tt); sol_ball_step(fp, tt); } sol_cmd_enq_deferred(); defer_cmds = 0; return(b); }