2 /* Random code used in more than one place. */
11 /*---------------------------------------------------------------------------*/
13 static float erp(float t)
15 return 3.0f * t * t - 2.0f * t * t * t;
19 static float derp(float t)
21 return 6.0f * t - 6.0f * t * t;
25 void sol_body_p(float p[3], const struct s_file *fp, int pi, float t)
31 const struct s_path *pp = fp->pv + pi;
32 const struct s_path *pq = fp->pv + pp->pi;
34 float s = MIN(t / pp->t, 1.0f);
36 v_sub(v, pq->p, pp->p);
37 v_mad(p, pp->p, v, pp->s ? erp(s) : s);
47 void sol_body_v(float v[3],
48 const struct s_file *fp,
49 int pi, float t, float dt)
51 if (pi >= 0 && fp->pv[pi].f)
55 sol_body_p(p, fp, pi, t);
56 sol_body_p(q, fp, pi, t + dt);
72 void sol_body_e(float e[4],
73 const struct s_file *fp,
74 const struct s_body *bp)
76 struct s_path *pp = fp->pv + bp->pi;
80 struct s_path *pq = fp->pv + pp->pi;
82 if (pp->fl & P_ORIENTED || pq->fl & P_ORIENTED)
84 q_slerp(e, pp->e, pq->e, bp->t / pp->t);
95 void sol_body_w(float w[3],
96 const struct s_file *fp,
97 const struct s_body *bp)
99 struct s_path *pp = fp->pv + bp->pi;
101 if (bp->pi >= 0 && pp->f)
103 struct s_path *pq = fp->pv + pp->pi;
105 if (pp->fl & P_ORIENTED || pq->fl & P_ORIENTED)
123 /* Match slerp by using the short path. */
133 q_as_axisangle(d, w, &a);
134 v_scl(w, w, a / pp->t);
144 /*---------------------------------------------------------------------------*/
147 * Integrate the rotation of the given basis E under angular velocity W
150 void sol_rotate(float e[3][3], const float w[3], float dt)
154 float a[3], M[16], f[3][3];
156 /* Compute the rotation matrix. */
159 m_rot(M, a, v_len(w) * dt);
161 /* Apply it to the basis. */
163 m_vxfm(f[0], M, e[0]);
164 m_vxfm(f[1], M, e[1]);
165 m_vxfm(f[2], M, e[2]);
167 /* Re-orthonormalize the basis. */
169 v_crs(e[2], f[0], f[1]);
170 v_crs(e[1], f[2], f[0]);
171 v_crs(e[0], f[1], f[2]);
179 /*---------------------------------------------------------------------------*/
182 * Compute the new angular velocity and orientation of a ball pendulum.
183 * A gives the accelleration of the ball. G gives the gravity vector.
185 void sol_pendulum(struct s_ball *up,
187 const float g[3], float dt)
189 float v[3], A[3], F[3], r[3], Y[3], T[3] = { 0.0f, 0.0f, 0.0f };
191 const float m = 5.000f;
192 const float ka = 0.500f;
193 const float kd = 0.995f;
195 /* Find the total force over DT. */
200 /* Find the force. */
204 /* Find the position of the pendulum. */
206 v_scl(r, up->E[1], -up->r);
208 /* Find the torque on the pendulum. */
210 if (fabsf(v_dot(r, F)) > 0.0f)
213 /* Apply the torque and dampen the angular velocity. */
215 v_mad(up->W, up->W, T, dt);
216 v_scl(up->W, up->W, kd);
218 /* Apply the angular velocity to the pendulum basis. */
220 sol_rotate(up->E, up->W, dt);
222 /* Apply a torque turning the pendulum toward the ball velocity. */
224 v_mad(v, up->v, up->E[1], v_dot(up->v, up->E[1]));
225 v_crs(Y, v, up->E[2]);
226 v_scl(Y, up->E[1], 2 * v_dot(Y, up->E[1]));
228 sol_rotate(up->E, Y, dt);
231 /*---------------------------------------------------------------------------*/
234 * Compute the states of all switches after DT seconds have passed.
236 void sol_swch_step(struct s_file *fp, float dt)
242 for (xi = 0; xi < fp->xc; xi++)
244 struct s_swch *xp = fp->xv + xi;
246 volatile float t = xp->t;
257 do /* Tortoise and hare cycle traverser. */
259 fp->pv[pi].f = xp->f0;
260 fp->pv[pj].f = xp->f0;
262 cmd.type = CMD_PATH_FLAG;
263 cmd.pathflag.pi = pi;
264 cmd.pathflag.f = fp->pv[pi].f;
275 cmd.type = CMD_SWCH_TOGGLE;
276 cmd.swchtoggle.xi = xi;
284 * Compute the positions of all bodies after DT seconds have passed.
286 void sol_body_step(struct s_file *fp, float dt)
292 for (i = 0; i < fp->bc; i++)
294 struct s_body *bp = fp->bv + i;
295 struct s_path *pp = fp->pv + bp->pi;
297 volatile float t = bp->t;
299 if (bp->pi >= 0 && pp->f)
308 cmd.type = CMD_BODY_TIME;
310 cmd.bodytime.t = bp->t;
313 cmd.type = CMD_BODY_PATH;
315 cmd.bodypath.pi = bp->pi;
323 * Compute the positions of all balls after DT seconds have passed.
325 void sol_ball_step(struct s_file *fp, float dt)
329 for (i = 0; i < fp->uc; i++)
331 struct s_ball *up = fp->uv + i;
333 v_mad(up->p, up->p, up->v, dt);
335 sol_rotate(up->e, up->w, dt);
339 /*---------------------------------------------------------------------------*/
341 int sol_item_test(struct s_file *fp, float *p, float item_r)
343 const float *ball_p = fp->uv->p;
344 const float ball_r = fp->uv->r;
348 for (hi = 0; hi < fp->hc; hi++)
352 r[0] = ball_p[0] - fp->hv[hi].p[0];
353 r[1] = ball_p[1] - fp->hv[hi].p[1];
354 r[2] = ball_p[2] - fp->hv[hi].p[2];
356 if (fp->hv[hi].t != ITEM_NONE && v_len(r) < ball_r + item_r)
358 p[0] = fp->hv[hi].p[0];
359 p[1] = fp->hv[hi].p[1];
360 p[2] = fp->hv[hi].p[2];
368 struct s_goal *sol_goal_test(struct s_file *fp, float *p, int ui)
370 const float *ball_p = fp->uv[ui].p;
371 const float ball_r = fp->uv[ui].r;
374 for (zi = 0; zi < fp->zc; zi++)
378 r[0] = ball_p[0] - fp->zv[zi].p[0];
379 r[1] = ball_p[2] - fp->zv[zi].p[2];
382 if (v_len(r) < fp->zv[zi].r - ball_r &&
383 ball_p[1] > fp->zv[zi].p[1] &&
384 ball_p[1] < fp->zv[zi].p[1] + GOAL_HEIGHT / 2)
386 p[0] = fp->zv[zi].p[0];
387 p[1] = fp->zv[zi].p[1];
388 p[2] = fp->zv[zi].p[2];
397 * Test if the ball UI is inside a jump. Return 1 if yes and fill P
398 * with the destination position, return 0 if not, and return 2 if the
399 * ball is on the border of a jump.
401 int sol_jump_test(struct s_file *fp, float *p, int ui)
403 const float *ball_p = fp->uv[ui].p;
404 const float ball_r = fp->uv[ui].r;
409 for (ji = 0; ji < fp->jc; ji++)
413 r[0] = ball_p[0] - fp->jv[ji].p[0];
414 r[1] = ball_p[2] - fp->jv[ji].p[2];
417 l = v_len(r) - fp->jv[ji].r;
419 ball_p[1] > fp->jv[ji].p[1] &&
420 ball_p[1] < fp->jv[ji].p[1] + JUMP_HEIGHT / 2)
424 p[0] = fp->jv[ji].q[0] + (ball_p[0] - fp->jv[ji].p[0]);
425 p[1] = fp->jv[ji].q[1] + (ball_p[1] - fp->jv[ji].p[1]);
426 p[2] = fp->jv[ji].q[2] + (ball_p[2] - fp->jv[ji].p[2]);
438 * Test and process the event the ball UI enters a switch. Return 1 if
439 * a visible switch is activated, return 0 otherwise (no switch is
440 * activated or only invisible switches).
442 int sol_swch_test(struct s_file *fp, int ui)
444 const float *ball_p = fp->uv[ui].p;
445 const float ball_r = fp->uv[ui].r;
451 for (xi = 0; xi < fp->xc; xi++)
453 struct s_swch *xp = fp->xv + xi;
455 /* FIXME enter/exit events don't work for timed switches */
457 if (xp->t0 == 0 || xp->f == xp->f0)
462 r[0] = ball_p[0] - xp->p[0];
463 r[1] = ball_p[2] - xp->p[2];
466 l = v_len(r) - xp->r;
469 ball_p[1] > xp->p[1] &&
470 ball_p[1] < xp->p[1] + SWCH_HEIGHT / 2)
472 if (!xp->e && l < - ball_r)
477 /* The ball enters. */
483 cmd.type = CMD_SWCH_ENTER;
484 cmd.swchenter.xi = xi;
488 /* Toggle the state, update the path. */
490 xp->f = xp->f ? 0 : 1;
492 cmd.type = CMD_SWCH_TOGGLE;
493 cmd.swchtoggle.xi = xi;
496 do /* Tortoise and hare cycle traverser. */
498 fp->pv[pi].f = xp->f;
499 fp->pv[pj].f = xp->f;
501 cmd.type = CMD_PATH_FLAG;
502 cmd.pathflag.pi = pi;
503 cmd.pathflag.f = fp->pv[pi].f;
512 /* It toggled to non-default state, start the timer. */
517 /* If visible, set the result. */
524 /* The ball exits. */
530 cmd.type = CMD_SWCH_EXIT;
531 cmd.swchexit.xi = xi;
539 /*---------------------------------------------------------------------------*/