void game_get_pos(float p[3], float e[3][3]) { v_cpy(p, file.vary.uv[ball].p); v_cpy(e[0], file.vary.uv[ball].e[0]); v_cpy(e[1], file.vary.uv[ball].e[1]); v_cpy(e[2], file.vary.uv[ball].e[2]); }
static void game_draw_vect_prim(const struct s_file *fp, GLenum mode) { float p[3]; float x[3]; float z[3]; float r; v_cpy(p, fp->uv[ball].p); v_cpy(x, view_e[0]); v_cpy(z, view_e[2]); r = fp->uv[ball].r; glBegin(mode); { glColor4f(1.0f, 1.0f, 0.5f, 0.5f); glVertex3f(p[0] - x[0] * r, p[1] - x[1] * r, p[2] - x[2] * r); glColor4f(1.0f, 0.0f, 0.0f, 0.5f); glVertex3f(p[0] + z[0] * view_m, p[1] + z[1] * view_m, p[2] + z[2] * view_m); glColor4f(1.0f, 1.0f, 0.0f, 0.5f); glVertex3f(p[0] + x[0] * r, p[1] + x[1] * r, p[2] + x[2] * r); } glEnd(); }
void game_set_pos(float p[3], float e[3][3]) { v_cpy(file.vary.uv[ball].p, p); v_cpy(file.vary.uv[ball].e[0], e[0]); v_cpy(file.vary.uv[ball].e[1], e[1]); v_cpy(file.vary.uv[ball].e[2], e[2]); }
static float sol_test_lump(float dt, float T[3], const struct v_ball *up, const struct s_base *base, const struct b_lump *lp, const float o[3], const float w[3]) { float U[3] = { 0.0f, 0.0f, 0.0f }; float u, t = dt; int i; /* Short circuit a non-solid lump. */ if (lp->fl & L_DETAIL) return t; /* Test all verts */ if (up->r > 0.0f) for (i = 0; i < lp->vc; i++) { const struct b_vert *vp = base->vv + base->iv[lp->v0 + i]; if ((u = sol_test_vert(t, U, up, vp, o, w)) < t) { v_cpy(T, U); t = u; } } /* Test all edges */ if (up->r > 0.0f) for (i = 0; i < lp->ec; i++) { const struct b_edge *ep = base->ev + base->iv[lp->e0 + i]; if ((u = sol_test_edge(t, U, up, base, ep, o, w)) < t) { v_cpy(T, U); t = u; } } /* Test all sides */ for (i = 0; i < lp->sc; i++) { const struct b_side *sp = base->sv + base->iv[lp->s0 + i]; if ((u = sol_test_side(t, U, up, base, lp, sp, o, w)) < t) { v_cpy(T, U); t = u; } } return t; }
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); }
static void pl_check_bounds(Polygon * pl, const vec p) { if (pl->last > 1){ check_bounds(&pl->bb, p); } else{ v_cpy(pl->bb.min, p); v_cpy(pl->bb.max, p); } }
/* * 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]); }
static float sol_test_node(float dt, float T[3], const struct v_ball *up, const struct s_base *base, const struct b_node *np, const float o[3], const float w[3]) { float U[3], u, t = dt; int i; /* Test all lumps */ for (i = 0; i < np->lc; i++) { const struct b_lump *lp = base->lv + np->l0 + i; if ((u = sol_test_lump(t, U, up, base, lp, o, w)) < t) { v_cpy(T, U); t = u; } } /* Test in front of this node */ if (np->ni >= 0 && sol_test_fore(t, up, base->sv + np->si, o, w)) { const struct b_node *nq = base->nv + np->ni; if ((u = sol_test_node(t, U, up, base, nq, o, w)) < t) { v_cpy(T, U); t = u; } } /* Test behind this node */ if (np->nj >= 0 && sol_test_back(t, up, base->sv + np->si, o, w)) { const struct b_node *nq = base->nv + np->nj; if ((u = sol_test_node(t, U, up, base, nq, o, w)) < t) { v_cpy(T, U); t = u; } } return t; }
static void pl_recalc_bounds(Polygon * pl) { assert(pl); v_cpy(pl->bb.min, pl->points[0].co); v_cpy(pl->bb.max, pl->points[0].co); uint i; for (i=1; i < pl->last; i++){ check_bounds(&pl->bb, pl->points[i].co); } }
static void game_cmd_updview(void) { cmd.type = CMD_VIEW_POSITION; v_cpy(cmd.viewpos.p, view.p); game_proxy_enq(&cmd); cmd.type = CMD_VIEW_CENTER; v_cpy(cmd.viewcenter.c, view.c); game_proxy_enq(&cmd); cmd.type = CMD_VIEW_BASIS; v_cpy(cmd.viewbasis.e[0], view.e[0]); v_cpy(cmd.viewbasis.e[1], view.e[1]); game_proxy_enq(&cmd); }
static void game_cmd_updball(void) { cmd.type = CMD_BALL_POSITION; v_cpy(cmd.ballpos.p, vary.uv[0].p); game_proxy_enq(&cmd); cmd.type = CMD_BALL_BASIS; v_cpy(cmd.ballbasis.e[0], vary.uv[0].e[0]); v_cpy(cmd.ballbasis.e[1], vary.uv[0].e[1]); game_proxy_enq(&cmd); cmd.type = CMD_BALL_PEND_BASIS; v_cpy(cmd.ballpendbasis.E[0], vary.uv[0].E[0]); v_cpy(cmd.ballpendbasis.E[1], vary.uv[0].E[1]); game_proxy_enq(&cmd); }
int sol_load_lerp(struct s_lerp *fp, struct s_vary *vary) { int i; fp->vary = vary; if (fp->vary->mc) { fp->mv = calloc(fp->vary->mc, sizeof (*fp->mv)); fp->mc = fp->vary->mc; for (i = 0; i < fp->vary->mc; i++) fp->mv[i][CURR].pi = fp->vary->mv[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; }
static float sol_test_file(float dt, float T[3], float V[3], const struct v_ball *up, const struct s_vary *vary) { float U[3], W[3], u, t = dt; int i; for (i = 0; i < vary->bc; i++) { const struct v_body *bp = vary->bv + i; if ((u = sol_test_body(t, U, W, up, vary, bp)) < t) { v_cpy(T, U); v_cpy(V, W); t = u; } } return t; }
PolyVert * pl_pop(Polygon * pl) { assert(pl); if (pl->last){ PolyVert * result= (PolyVert *)malloc(sizeof(PolyVert)); pl->last--; PolyVert * last= pl->points + pl->last; v_cpy(result->co, last->co); result->flags= last->flags; return result; } return NULL; }
static float sol_test_edge(float dt, float T[3], const struct v_ball *up, const struct s_base *base, const struct b_edge *ep, const float o[3], const float w[3]) { float q[3]; float u[3]; v_cpy(q, base->vv[ep->vi].p); v_sub(u, base->vv[ep->vj].p, base->vv[ep->vi].p); return v_edge(T, o, q, u, w, up->p, up->v, up->r); }
PolyVert * pl_append(Polygon **pl_pt, const vec co, int flags) { assert(co); Polygon * pl= pl_check_pointer(pl_pt); if (pl->last > pl->size) pl_expand(pl, pl->size); PolyVert * vx= pl->points + pl->last++; v_cpy(vx->co, co); vx->flags= flags; pl_check_bounds(pl, co); return vx; }
void pl_extend_points(Polygon * pl, const vec * points, uint count) { assert(pl); assert(points); PolyVert *tmp; pl_check_size(pl); uint i; for (i=0 ; i < count; i++){ tmp= pl->points + pl->last++; tmp->flags= 0; v_cpy(tmp->co, points[i]); } }
static void game_cmd_init_items(void) { int i; cmd.type = CMD_CLEAR_ITEMS; game_proxy_enq(&cmd); for (i = 0; i < vary.hc; i++) { cmd.type = CMD_MAKE_ITEM; v_cpy(cmd.mkitem.p, vary.hv[i].p); cmd.mkitem.t = vary.hv[i].t; cmd.mkitem.n = vary.hv[i].n; game_proxy_enq(&cmd); } }
PolyVert * pl_insert_point(Polygon * pl, uint pos, const vec p) { assert(pl); assert(p); pl_check_size(pl); uint i= pl->last++; uint j= pl->last; for (; i >= pos; j=i--){ pl->points[j]= pl->points[i]; } PolyVert *vx= pl->points + i; v_cpy(vx->co, p); vx->flags= 0; pl_check_bounds(pl, p); return vx; }
double Vector3D::horizontalAngle(const Vector3D & v) const { Vector3D v_cpy(*this); Vector3D v_cpy2(v); Vector3D v_signe; v_cpy.Z = 0; v_cpy2.Z = 0; if (v_cpy == 0 || v_cpy2 == 0) return 0; v_signe = v_cpy.crossProduct(v_cpy2); if (v_signe.Z > 0.0001) { //std::cout << "angle : " << -acos((v_cpy.normalize()).scalarProduct(v_cpy2.normalize()))*(180/M_PI) << std::endl; return -acos((v_cpy.normalize()).scalarProduct(v_cpy2.normalize()))*(180/M_PI); } else if (v_signe.Z < -0.0001) { //std::cout << "angle : " << acos((v_cpy.normalize()).scalarProduct(v_cpy2.normalize()))*(180/M_PI) << std::endl; return acos((v_cpy.normalize()).scalarProduct(v_cpy2.normalize()))*(180/M_PI); } else { return 0; } }
float sol_step(struct s_vary *vary, const float *g, float dt, int ui, int *m) { float P[3], V[3], v[3], r[3], a[3], d, e, nt, b = 0.0f, tt = dt, t; int c; union cmd cmd; if (ui < vary->uc) { struct v_ball *up = vary->uv + ui; /* If the ball is in contact with a surface, apply friction. */ v_cpy(a, up->v); v_cpy(v, up->v); v_cpy(up->v, g); if (m && sol_test_file(tt, P, V, up, vary) < 0.0005f) { v_cpy(up->v, v); v_sub(r, P, up->p); t = v_len(r) * v_len(g); if (t == 0.f) { t = SMALL; } if ((d = v_dot(r, g) / t) > 0.999f) { if ((e = (v_len(up->v) - dt)) > 0.0f) { /* Scale the linear velocity. */ v_nrm(up->v, up->v); v_scl(up->v, up->v, e); /* Scale the angular velocity. */ v_sub(v, V, up->v); v_crs(up->w, v, r); v_scl(up->w, up->w, -1.0f / (up->r * up->r)); } else { /* Friction has brought the ball to a stop. */ up->v[0] = 0.0f; up->v[1] = 0.0f; up->v[2] = 0.0f; (*m)++; } } else v_mad(up->v, v, g, tt); } else v_mad(up->v, v, g, tt); /* Test for collision. */ for (c = 16; c > 0 && tt > 0; c--) { float st; int mi, ms; /* HACK: avoid stepping across path changes. */ st = tt; for (mi = 0; mi < vary->mc; mi++) { struct v_move *mp = vary->mv + mi; struct v_path *pp = vary->pv + mp->pi; if (!pp->f) continue; if (mp->tm + ms_peek(st) > pp->base->tm) st = MS_TO_TIME(pp->base->tm - mp->tm); } /* Miss collisions if we reach the iteration limit. */ if (c > 1) nt = sol_test_file(st, P, V, up, vary); else nt = tt; cmd.type = CMD_STEP_SIMULATION; cmd.stepsim.dt = nt; sol_cmd_enq(&cmd); ms = ms_step(nt); sol_move_step(vary, nt, ms); sol_swch_step(vary, nt, ms); sol_ball_step(vary, nt); if (nt < st) if (b < (d = sol_bounce(up, P, V, nt))) b = d; tt -= nt; } v_sub(a, up->v, a); sol_pendulum(up, a, g, dt); } return b; }
void game_set_fly(float k) { struct s_vary *fp = &file.vary; float x[3] = { 1.f, 0.f, 0.f }; float y[3] = { 0.f, 1.f, 0.f }; float z[3] = { 0.f, 0.f, 1.f }; float c0[3] = { 0.f, 0.f, 0.f }; float p0[3] = { 0.f, 0.f, 0.f }; float c1[3] = { 0.f, 0.f, 0.f }; float p1[3] = { 0.f, 0.f, 0.f }; float v[3]; if (!state) return; v_cpy(view_e[0], x); v_cpy(view_e[1], y); if (fp->base->zc > 0) v_sub(view_e[2], fp->uv[ball].p, fp->base->zv[0].p); else v_cpy(view_e[2], z); if (fabs(v_dot(view_e[1], view_e[2])) > 0.999) v_cpy(view_e[2], z); v_crs(view_e[0], view_e[1], view_e[2]); v_crs(view_e[2], view_e[0], view_e[1]); v_nrm(view_e[0], view_e[0]); v_nrm(view_e[2], view_e[2]); /* k = 0.0 view is at the ball. */ if (fp->uc > 0) { v_cpy(c0, fp->uv[ball].p); v_cpy(p0, fp->uv[ball].p); } v_mad(p0, p0, view_e[1], view_dy); v_mad(p0, p0, view_e[2], view_dz); /* k = +1.0 view is s_view 0 */ if (k >= 0 && fp->base->wc > 0) { v_cpy(p1, fp->base->wv[0].p); v_cpy(c1, fp->base->wv[0].q); } /* k = -1.0 view is s_view 1 */ if (k <= 0 && fp->base->wc > 1) { v_cpy(p1, fp->base->wv[1].p); v_cpy(c1, fp->base->wv[1].q); } /* Interpolate the views. */ v_sub(v, p1, p0); v_mad(view_p, p0, v, k * k); v_sub(v, c1, c0); v_mad(view_c, c0, v, k * k); /* Orthonormalize the view basis. */ v_sub(view_e[2], view_p, view_c); v_crs(view_e[0], view_e[1], view_e[2]); v_crs(view_e[2], view_e[0], view_e[1]); v_nrm(view_e[0], view_e[0]); v_nrm(view_e[2], view_e[2]); view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2])); }
int sol_lerp_cmd(struct s_lerp *fp, struct cmd_state *cs, const union cmd *cmd) { struct l_ball (*uv)[2]; struct l_ball *up; int idx, mi, i; int rc = 0; switch (cmd->type) { case CMD_MAKE_BALL: if ((uv = realloc(fp->uv, sizeof (*uv) * (fp->uc + 1)))) { fp->uv = uv; /* Update varying state. */ if (sol_vary_cmd(fp->vary, cs, cmd)) { fp->uc++; rc = 1; } } break; case CMD_MOVE_PATH: if ((mi = cmd->movepath.mi) >= 0 && mi < fp->mc) { /* Be extra paranoid. */ if ((idx = cmd->movepath.pi) >= 0 && idx < fp->vary->base->pc) fp->mv[mi][CURR].pi = idx; } break; case CMD_MOVE_TIME: if ((mi = cmd->movetime.mi) >= 0 && mi < fp->mc) { fp->mv[mi][CURR].t = cmd->movetime.t; } break; case CMD_BODY_PATH: /* Backward compatibility: update linear mover only. */ if ((idx = cmd->bodypath.bi) >= 0 && idx < fp->vary->bc && (mi = fp->vary->bv[idx].mi) >= 0) { /* Be EXTRA paranoid. */ if ((idx = cmd->bodypath.pi) >= 0 && idx < fp->vary->base->pc) fp->mv[mi][CURR].pi = idx; } break; case CMD_BODY_TIME: /* Same as CMD_BODY_PATH. */ if ((idx = cmd->bodytime.bi) >= 0 && idx < fp->vary->bc && (mi = fp->vary->bv[idx].mi) >= 0) { fp->mv[mi][CURR].t = cmd->bodytime.t; } break; case CMD_BALL_RADIUS: fp->uv[cs->curr_ball][CURR].r = cmd->ballradius.r; break; case CMD_CLEAR_BALLS: free(fp->uv); fp->uv = NULL; fp->uc = 0; sol_vary_cmd(fp->vary, cs, cmd); break; case CMD_BALL_POSITION: up = &fp->uv[cs->curr_ball][CURR]; v_cpy(up->p, cmd->ballpos.p); break; case CMD_BALL_BASIS: up = &fp->uv[cs->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[cs->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_STEP_SIMULATION: /* * Step each mover ahead. This way we cut down on replay size * significantly while still keeping things in sync with * occasional CMD_MOVE_PATH and CMD_MOVE_TIME. */ for (i = 0; i < fp->mc; i++) { struct l_move *mp = &fp->mv[i][CURR]; if (mp->pi >= 0 && fp->vary->pv[mp->pi].f) mp->t += cmd->stepsim.dt; } break; default: break; } return rc; }
void game_draw(int pose, float t) { const float light_p[4] = { 8.f, 32.f, 8.f, 0.f }; struct s_draw *fp = &file.draw; struct s_rend rend; float fov = FOV; if (!state) return; fp->shadow_ui = ball; game_shadow_conf(1); sol_draw_enable(&rend); if (jump_b) fov *= 2.0f * fabsf(jump_dt - 0.5f); video_push_persp(fov, 0.1f, FAR_DIST); glPushMatrix(); { float T[16], M[16], v[3], c[3]; /* In VR, move the view center up to keep the viewer level. */ v_cpy(c, view_c); if (hmd_stat()) c[1] += view_dy; video_calc_view(T, c, view_p, view_e[1]); m_xps(M, T); v_sub(v, c, view_p); glTranslatef(0.f, 0.f, -v_len(v)); glMultMatrixf(M); glTranslatef(-c[0], -c[1], -c[2]); /* Center the skybox about the position of the camera. */ glPushMatrix(); { glTranslatef(view_p[0], view_p[1], view_p[2]); back_draw(&rend); } glPopMatrix(); glEnable(GL_LIGHT0); glLightfv(GL_LIGHT0, GL_POSITION, light_p); /* Draw the floor. */ sol_draw(fp, &rend, 0, 1); /* Draw the game elements. */ glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); if (pose == 0) { game_draw_balls(&rend, fp->vary, T, t); game_draw_vect(&rend, fp->vary); } glDisable(GL_LIGHTING); glDepthMask(GL_FALSE); { game_draw_flags(&rend, fp->base); game_draw_beams(&rend, fp->base, fp->vary); } glDepthMask(GL_TRUE); glEnable(GL_LIGHTING); } glPopMatrix(); video_pop_matrix(); sol_draw_disable(&rend); game_shadow_conf(0); }
void game_update_view(float dt) { const float y[3] = { 0.f, 1.f, 0.f }; float dy; float dz; float k; float e[3]; float d[3]; float s = 2.f * dt; if (!state) return; /* Center the view about the ball. */ v_cpy(view_c, file.vary.uv[ball].p); v_inv(view_v, file.vary.uv[ball].v); switch (config_get_d(CONFIG_CAMERA)) { case 2: /* Camera 2: View vector is given by view angle. */ view_e[2][0] = fsinf(V_RAD(view_a)); view_e[2][1] = 0.f; view_e[2][2] = fcosf(V_RAD(view_a)); s = 1.f; break; default: /* View vector approaches the ball velocity vector. */ v_mad(e, view_v, y, v_dot(view_v, y)); v_inv(e, e); k = v_dot(view_v, view_v); v_sub(view_e[2], view_p, view_c); v_mad(view_e[2], view_e[2], view_v, k * dt * 0.1f); } /* Orthonormalize the basis of the view in its new position. */ v_crs(view_e[0], view_e[1], view_e[2]); v_crs(view_e[2], view_e[0], view_e[1]); v_nrm(view_e[0], view_e[0]); v_nrm(view_e[2], view_e[2]); /* The current view (dy, dz) approaches the ideal (view_dy, view_dz). */ v_sub(d, view_p, view_c); dy = v_dot(view_e[1], d); dz = v_dot(view_e[2], d); dy += (view_dy - dy) * s; dz += (view_dz - dz) * s; /* Compute the new view position. */ view_p[0] = view_p[1] = view_p[2] = 0.f; v_mad(view_p, view_c, view_e[1], dy); v_mad(view_p, view_p, view_e[2], dz); view_a = V_DEG(fatan2f(view_e[2][0], view_e[2][2])); }
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.vary; struct v_item *hp; float v[4]; float dt; int idx; if (cs.next_update) { game_lerp_copy(&gl); cs.next_update = 0; } switch (cmd->type) { case CMD_END_OF_UPDATE: cs.got_tilt_axes = 0; cs.next_update = 1; if (cs.first_update) { game_lerp_copy(&gl); /* Hack to sync state before the next update. */ game_lerp_apply(&gl, &gd); cs.first_update = 0; break; } /* Compute gravity for particle effects. */ if (status == GAME_GOAL) game_tilt_grav(v, GRAVITY_UP, tilt); else game_tilt_grav(v, GRAVITY_DN, tilt); /* Step particle, goal and jump effects. */ if (cs.ups > 0) { dt = 1.0f / cs.ups; if (gd.goal_e && gl.goal_k[CURR] < 1.0f) gl.goal_k[CURR] += dt; if (gd.jump_b) { gl.jump_dt[CURR] += dt; if (gl.jump_dt[PREV] >= 1.0f) gd.jump_b = 0; } part_step(v, dt); } break; case CMD_MAKE_BALL: sol_lerp_cmd(&gl.lerp, &cs, cmd); break; case CMD_MAKE_ITEM: /* Allocate and initialize a new item. */ if ((hp = realloc(vary->hv, sizeof (*hp) * (vary->hc + 1)))) { vary->hv = hp; hp = &vary->hv[vary->hc]; vary->hc++; memset(hp, 0, sizeof (*hp)); v_cpy(hp->p, cmd->mkitem.p); hp->t = cmd->mkitem.t; hp->n = cmd->mkitem.n; } break; case CMD_PICK_ITEM: /* Set up particle effects and discard the item. */ if ((idx = cmd->pkitem.hi) >= 0 && idx < vary->hc) { hp = &vary->hv[idx]; item_color(hp, v); part_burst(hp->p, v); hp->t = ITEM_NONE; } break; case CMD_TILT_ANGLES: if (!cs.got_tilt_axes) { /* * 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. */ game_tilt_axes(tilt, view->e); } tilt->rx = cmd->tiltangles.x; tilt->rz = cmd->tiltangles.z; break; case CMD_SOUND: /* Play the sound. */ if (cmd->sound.n) audio_play(cmd->sound.n, cmd->sound.a); break; case CMD_TIMER: timer = cmd->timer.t; break; case CMD_STATUS: status = cmd->status.t; break; case CMD_COINS: coins = cmd->coins.n; break; case CMD_JUMP_ENTER: gd.jump_b = 1; gd.jump_e = 0; gl.jump_dt[PREV] = 0.0f; gl.jump_dt[CURR] = 0.0f; break; case CMD_JUMP_EXIT: gd.jump_e = 1; break; case CMD_MOVE_PATH: case CMD_MOVE_TIME: case CMD_BODY_PATH: case CMD_BODY_TIME: sol_lerp_cmd(&gl.lerp, &cs, cmd); break; case CMD_GOAL_OPEN: /* * Enable the goal and make sure it's fully visible if * this is the first update. */ if (!gd.goal_e) { gd.goal_e = 1; gl.goal_k[CURR] = cs.first_update ? 1.0f : 0.0f; } break; case CMD_SWCH_ENTER: if ((idx = cmd->swchenter.xi) >= 0 && idx < vary->xc) vary->xv[idx].e = 1; break; case CMD_SWCH_TOGGLE: if ((idx = cmd->swchtoggle.xi) >= 0 && idx < vary->xc) vary->xv[idx].f = !vary->xv[idx].f; break; case CMD_SWCH_EXIT: if ((idx = cmd->swchexit.xi) >= 0 && idx < vary->xc) vary->xv[idx].e = 0; break; case CMD_UPDATES_PER_SECOND: cs.ups = cmd->ups.n; break; case CMD_BALL_RADIUS: sol_lerp_cmd(&gl.lerp, &cs, cmd); break; case CMD_CLEAR_ITEMS: free(vary->hv); vary->hv = NULL; vary->hc = 0; break; case CMD_CLEAR_BALLS: sol_lerp_cmd(&gl.lerp, &cs, cmd); break; case CMD_BALL_POSITION: sol_lerp_cmd(&gl.lerp, &cs, cmd); break; case CMD_BALL_BASIS: sol_lerp_cmd(&gl.lerp, &cs, cmd); break; case CMD_BALL_PEND_BASIS: sol_lerp_cmd(&gl.lerp, &cs, cmd); break; case CMD_VIEW_POSITION: v_cpy(view->p, cmd->viewpos.p); break; case CMD_VIEW_CENTER: v_cpy(view->c, cmd->viewcenter.c); break; case CMD_VIEW_BASIS: 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: if ((idx = cmd->currball.ui) >= 0 && idx < vary->uc) cs.curr_ball = idx; break; case CMD_PATH_FLAG: if ((idx = cmd->pathflag.pi) >= 0 && idx < vary->pc) vary->pv[idx].f = cmd->pathflag.f; break; case CMD_STEP_SIMULATION: sol_lerp_cmd(&gl.lerp, &cs, cmd); break; case CMD_MAP: /* * 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: cs.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; } } }
static int game_step(const float g[3], float dt, int bt) { if (server_state) { float h[3]; /* Smooth jittery or discontinuous input. */ tilt.rx += (input_get_x() - tilt.rx) * dt / input_get_s(); tilt.rz += (input_get_z() - tilt.rz) * dt / input_get_s(); game_tilt_axes(&tilt, view.e); game_cmd_tiltaxes(); game_cmd_tiltangles(); grow_step(&vary, dt); game_tilt_grav(h, g, &tilt); if (jump_b > 0) { jump_dt += dt; /* Handle a jump. */ if (jump_dt >= 0.5f) { /* Translate view at the exact instant of the jump. */ if (jump_b == 1) { float dp[3]; v_sub(dp, jump_p, vary.uv->p); v_add(view.p, view.p, dp); jump_b = 2; } /* Translate ball and hold it at the destination. */ v_cpy(vary.uv->p, jump_p); } if (jump_dt >= 1.0f) jump_b = 0; } else { /* Run the sim. */ float b = sol_step(&vary, h, dt, 0, NULL); /* Mix the sound of a ball bounce. */ if (b > 0.5f) { float k = (b - 0.5f) * 2.0f; if (got_orig) { if (vary.uv->r > grow_orig) audio_play(AUD_BUMPL, k); else if (vary.uv->r < grow_orig) audio_play(AUD_BUMPS, k); else audio_play(AUD_BUMPM, k); } else audio_play(AUD_BUMPM, k); } } game_cmd_updball(); game_update_view(dt); game_update_time(dt, bt); return game_update_state(bt); } return GAME_NONE; }
int sol_load_vary(struct s_vary *fp, struct s_base *base) { int i; memset(fp, 0, sizeof (*fp)); fp->base = base; if (fp->base->pc) { fp->pv = (struct v_path*)calloc(fp->base->pc, sizeof (*fp->pv)); fp->pc = fp->base->pc; for (i = 0; i < fp->base->pc; i++) { struct v_path *pp = fp->pv + i; struct b_path *pq = fp->base->pv + i; pp->base = pq; pp->f = pq->f; } } if (fp->base->bc) { struct alloc mv; fp->bv = (struct v_body*)calloc(fp->base->bc, sizeof (*fp->bv)); fp->bc = fp->base->bc; alloc_new(&mv, sizeof (*fp->mv), (void **) &fp->mv, &fp->mc); for (i = 0; i < fp->base->bc; i++) { struct b_body *bbody = fp->base->bv + i; struct v_body *vbody = fp->bv + i; struct v_move *vmove; vbody->base = bbody; vbody->mi = -1; vbody->mj = -1; if (bbody->pi >= 0 && (vmove = (struct v_move*)alloc_add(&mv))) { memset(vmove, 0, sizeof (*vmove)); vbody->mi = fp->mc - 1; vmove->pi = bbody->pi; } if (bbody->pj == bbody->pi) { vbody->mj = vbody->mi; } else if (bbody->pj >= 0 && (vmove = (struct v_move*)alloc_add(&mv))) { memset(vmove, 0, sizeof (*vmove)); vbody->mj = fp->mc - 1; vmove->pi = bbody->pj; } } } if (fp->base->hc) { fp->hv = (struct v_item*)calloc(fp->base->hc, sizeof (*fp->hv)); fp->hc = fp->base->hc; for (i = 0; i < fp->base->hc; i++) { struct v_item *hp = fp->hv + i; struct b_item *hq = fp->base->hv + i; v_cpy(hp->p, hq->p); hp->t = hq->t; hp->n = hq->n; } } if (fp->base->xc) { fp->xv = (struct v_swch*)calloc(fp->base->xc, sizeof (*fp->xv)); fp->xc = fp->base->xc; for (i = 0; i < fp->base->xc; i++) { struct v_swch *xp = fp->xv + i; struct b_swch *xq = fp->base->xv + i; xp->base = xq; xp->t = xq->t; xp->tm = xq->tm; xp->f = xq->f; } } if (fp->base->uc) { fp->uv = (struct v_ball*)calloc(fp->base->uc, sizeof (*fp->uv)); fp->uc = fp->base->uc; for (i = 0; i < fp->base->uc; i++) { struct v_ball *up = fp->uv + i; struct b_ball *uq = fp->base->uv + i; v_cpy(up->p, uq->p); up->r = uq->r; up->E[0][0] = up->e[0][0] = 1.0f; up->E[0][1] = up->e[0][1] = 0.0f; up->E[0][2] = up->e[0][2] = 0.0f; up->E[1][0] = up->e[1][0] = 0.0f; up->E[1][1] = up->e[1][1] = 1.0f; up->E[1][2] = up->e[1][2] = 0.0f; up->E[2][0] = up->e[2][0] = 0.0f; up->E[2][1] = up->e[2][1] = 0.0f; up->E[2][2] = up->e[2][2] = 1.0f; } } return 1; }
static float sol_test_body(float dt, float T[3], float V[3], const struct v_ball *up, const struct s_vary *vary, const struct v_body *bp) { float U[3], O[3], E[4], W[3], u; const struct b_node *np = vary->base->nv + bp->base->ni; sol_body_p(O, vary, bp, 0.0f); sol_body_v(W, vary, bp, dt); sol_body_e(E, vary, bp, 0.0f); /* * For rotating bodies, rather than rotate every normal and vertex * of the body, we temporarily pretend the ball is rotating and * moving about a static body. */ /* * Linear velocity of a point rotating about the origin: * v = w x p */ if (E[0] != 1.0f || sol_body_w(vary, bp)) { /* The body has a non-identity orientation or it is rotating. */ struct v_ball ball; float e[4], p0[3], p1[3]; const float z[3] = { 0 }; /* First, calculate position at start and end of time interval. */ v_sub(p0, up->p, O); v_cpy(p1, p0); q_conj(e, E); q_rot(p0, e, p0); v_mad(p1, p1, up->v, dt); v_mad(p1, p1, W, -dt); sol_body_e(e, vary, bp, dt); q_conj(e, e); q_rot(p1, e, p1); /* Set up ball struct with values relative to body. */ ball = *up; v_cpy(ball.p, p0); /* Calculate velocity from start/end positions and time. */ v_sub(ball.v, p1, p0); v_scl(ball.v, ball.v, 1.0f / dt); if ((u = sol_test_node(dt, U, &ball, vary->base, np, z, z)) < dt) { /* Compute the final orientation. */ sol_body_e(e, vary, bp, u); /* Return world space coordinates. */ q_rot(T, e, U); v_add(T, O, T); /* Move forward. */ v_mad(T, T, W, u); /* Express "non-ball" velocity. */ q_rot(V, e, ball.v); v_sub(V, up->v, V); dt = u; } } else { if ((u = sol_test_node(dt, U, up, vary->base, np, O, W)) < dt) { v_cpy(T, U); v_cpy(V, W); dt = u; } } return dt; }
static void game_update_view(float dt) { float dc = view.dc * (jump_b > 0 ? 2.0f * fabsf(jump_dt - 0.5f) : 1.0f); float da = input_get_r() * dt * 90.0f; float k; float M[16], v[3], Y[3] = { 0.0f, 1.0f, 0.0f }; float view_v[3]; float spd = (float) cam_speed(input_get_c()) / 1000.0f; /* Track manual rotation time. */ if (da == 0.0f) { if (view_time < 0.0f) { /* Transition time is influenced by activity time. */ view_fade = CLAMP(VIEW_FADE_MIN, -view_time, VIEW_FADE_MAX); view_time = 0.0f; } /* Inactivity. */ view_time += dt; } else { if (view_time > 0.0f) { view_fade = 0.0f; view_time = 0.0f; } /* Activity (yes, this is negative). */ view_time -= dt; } /* Center the view about the ball. */ v_cpy(view.c, vary.uv->p); view_v[0] = -vary.uv->v[0]; view_v[1] = 0.0f; view_v[2] = -vary.uv->v[2]; /* Compute view vector. */ if (spd >= 0.0f) { /* Viewpoint chases ball position. */ if (da == 0.0f) { float s; v_sub(view.e[2], view.p, view.c); v_nrm(view.e[2], view.e[2]); /* Gradually restore view vector convergence rate. */ s = fpowf(view_time, 3.0f) / fpowf(view_fade, 3.0f); s = CLAMP(0.0f, s, 1.0f); v_mad(view.e[2], view.e[2], view_v, v_len(view_v) * spd * s * dt); } } else { /* View vector is given by view angle. */ view.e[2][0] = fsinf(V_RAD(view.a)); view.e[2][1] = 0.0; view.e[2][2] = fcosf(V_RAD(view.a)); } /* Apply manual rotation. */ if (da != 0.0f) { m_rot(M, Y, V_RAD(da)); m_vxfm(v, M, view.e[2]); v_cpy(view.e[2], v); } /* Orthonormalize the new view reference frame. */ v_crs(view.e[0], view.e[1], view.e[2]); v_crs(view.e[2], view.e[0], view.e[1]); v_nrm(view.e[0], view.e[0]); v_nrm(view.e[2], view.e[2]); /* Compute the new view position. */ k = 1.0f + v_dot(view.e[2], view_v) / 10.0f; view_k = view_k + (k - view_k) * dt; if (view_k < 0.5) view_k = 0.5; v_scl(v, view.e[1], view.dp * view_k); v_mad(v, v, view.e[2], view.dz * view_k); v_add(view.p, v, vary.uv->p); /* Compute the new view center. */ v_cpy(view.c, vary.uv->p); v_mad(view.c, view.c, view.e[1], dc); /* Note the current view angle. */ view.a = V_DEG(fatan2f(view.e[2][0], view.e[2][2])); game_cmd_updview(); }