2 * Copyright (C) 2003 Robert Kooima
4 * NEVERBALL is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published
6 * by the Free Software Foundation; either version 2 of the License,
7 * or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
21 #include "solid_sim.h"
22 #include "solid_all.h"
23 #include "solid_cmd.h"
28 /*---------------------------------------------------------------------------*/
29 /* Solves (p + v * t) . (p + v * t) == r * r for smallest t. */
32 * Given vectors A = P, B = V * t, C = A + B, |C| = r, solve for
35 * Some useful dot product properties:
37 * 1) A . A = |A| * |A|
38 * 2) A . (B + C) = A . B + A . C
39 * 3) (r * A) . B = r * (A . B)
41 * Deriving a quadratic equation:
44 * (A + B) . (A + B) = r * r
45 * A . (A + B) + B . (A + B) = r * r (2)
46 * A . A + A . B + B . A + B . B = r * r (2)
47 * A . A + 2 * (A . B) + B . B = r * r
48 * P . P + 2 * (P . V * t) + (V * t . V * t) = r * r
49 * P . P + 2 * (P . V) * t + (V . V) * t * t = r * r (3)
50 * (V . V) * t * t + 2 * (P . V) * t + P . P - r * r = 0
52 * This equation is solved using the quadratic formula.
55 static float v_sol(const float p[3], const float v[3], float r)
57 float a = v_dot(v, v);
58 float b = v_dot(v, p) * 2.0f;
59 float c = v_dot(p, p) - r * r;
60 float d = b * b - 4.0f * a * c;
62 /* HACK: This seems to cause failures to detect low-velocity collision
63 Yet, the potential division by zero below seems fine.
64 if (fabsf(a) < SMALL) return LARGE;
67 if (d < 0.0f) return LARGE;
70 float t0 = 0.5f * (-b - fsqrtf(d)) / a;
71 float t1 = 0.5f * (-b + fsqrtf(d)) / a;
72 float t = (t0 < t1) ? t0 : t1;
74 return (t < 0.0f) ? LARGE : t;
76 else return -b * 0.5f / a;
79 /*---------------------------------------------------------------------------*/
82 * Compute the earliest time and position of the intersection of a
83 * sphere and a vertex.
85 * The sphere has radius R and moves along vector V from point P. The
86 * vertex moves along vector W from point Q in a coordinate system
89 static float v_vert(float Q[3],
94 const float v[3], float r)
96 float O[3], P[3], V[3];
103 if (v_dot(P, V) < 0.0f)
114 * Compute the earliest time and position of the intersection of a
115 * sphere and an edge.
117 * The sphere has radius R and moves along vector V from point P. The
118 * edge moves along vector W from point Q in a coordinate system based
119 * at O. The edge extends along the length of vector U.
121 static float v_edge(float Q[3],
127 const float v[3], float r)
131 float du, eu, uu, s, t;
138 * Think projections. Vectors D, extending from the edge vertex Q
139 * to the sphere, and E, the relative velocity of sphere wrt the
140 * edge, are made orthogonal to the edge vector U. Division of
141 * the dot products is required to obtain the true projection
142 * ratios since U does not have unit length.
149 v_mad(P, d, u, -du / uu);
150 v_mad(V, e, u, -eu / uu);
153 s = (du + eu * t) / uu; /* Projection of D + E * t on U. */
155 if (0.0f <= t && t < LARGE && 0.0f < s && s < 1.0f)
168 * Compute the earliest time and position of the intersection of a
169 * sphere and a plane.
171 * The sphere has radius R and moves along vector V from point P. The
172 * plane moves along vector W. The plane has normal N and is
173 * positioned at distance D from the origin O along that normal.
175 static float v_side(float Q[3],
178 const float n[3], float d,
180 const float v[3], float r)
182 float vn = v_dot(v, n);
183 float wn = v_dot(w, n);
188 float on = v_dot(o, n);
189 float pn = v_dot(p, n);
191 float u = (r + d + on - pn) / (vn - wn);
192 float a = ( d + on - pn) / (vn - wn);
212 /*---------------------------------------------------------------------------*/
215 * Compute the new linear and angular velocities of a bouncing ball.
216 * Q gives the position of the point of impact and W gives the
217 * velocity of the object being impacted.
219 static float sol_bounce(struct s_ball *up,
221 const float w[3], float dt)
223 float n[3], r[3], d[3], vn, wn;
227 /* Find the normal of the impact. */
233 /* Find the new angular velocity. */
236 v_scl(up->w, up->w, -1.0f / (up->r * up->r));
238 /* Find the new linear velocity. */
243 v_mad(v, v, n, 1.7 * (wn - vn));
245 v_mad(p, q, n, up->r);
247 /* Return the "energy" of the impact, to determine the sound amplitude. */
249 return fabsf(v_dot(n, d));
252 /*---------------------------------------------------------------------------*/
254 static float sol_test_vert(float dt,
256 const struct s_ball *up,
257 const struct s_vert *vp,
261 return v_vert(T, o, vp->p, w, up->p, up->v, up->r);
264 static float sol_test_edge(float dt,
266 const struct s_ball *up,
267 const struct s_file *fp,
268 const struct s_edge *ep,
275 v_cpy(q, fp->vv[ep->vi].p);
276 v_sub(u, fp->vv[ep->vj].p,
279 return v_edge(T, o, q, u, w, up->p, up->v, up->r);
282 static float sol_test_side(float dt,
284 const struct s_ball *up,
285 const struct s_file *fp,
286 const struct s_lump *lp,
287 const struct s_side *sp,
291 float t = v_side(T, o, w, sp->n, sp->d, up->p, up->v, up->r);
295 for (i = 0; i < lp->sc; i++)
297 const struct s_side *sq = fp->sv + fp->iv[lp->s0 + i];
302 v_dot(w, sq->n) * t > sq->d)
308 /*---------------------------------------------------------------------------*/
310 static int sol_test_fore(float dt,
311 const struct s_ball *up,
312 const struct s_side *sp,
318 /* If the ball is not behind the plane, the test passes. */
322 if (v_dot(q, sp->n) - sp->d + up->r >= 0)
325 /* If it's not behind the plane after DT seconds, the test passes. */
327 v_mad(q, q, up->v, dt);
329 if (v_dot(q, sp->n) - sp->d + up->r >= 0)
332 /* Else, test fails. */
337 static int sol_test_back(float dt,
338 const struct s_ball *up,
339 const struct s_side *sp,
345 /* If the ball is not in front of the plane, the test passes. */
349 if (v_dot(q, sp->n) - sp->d - up->r <= 0)
352 /* If it's not in front of the plane after DT seconds, the test passes. */
354 v_mad(q, q, up->v, dt);
356 if (v_dot(q, sp->n) - sp->d - up->r <= 0)
359 /* Else, test fails. */
364 /*---------------------------------------------------------------------------*/
366 static float sol_test_lump(float dt,
368 const struct s_ball *up,
369 const struct s_file *fp,
370 const struct s_lump *lp,
374 float U[3] = { 0.0f, 0.0f, 0.0f };
378 /* Short circuit a non-solid lump. */
380 if (lp->fl & L_DETAIL) return t;
385 for (i = 0; i < lp->vc; i++)
387 const struct s_vert *vp = fp->vv + fp->iv[lp->v0 + i];
389 if ((u = sol_test_vert(t, U, up, vp, o, w)) < t)
399 for (i = 0; i < lp->ec; i++)
401 const struct s_edge *ep = fp->ev + fp->iv[lp->e0 + i];
403 if ((u = sol_test_edge(t, U, up, fp, ep, o, w)) < t)
412 for (i = 0; i < lp->sc; i++)
414 const struct s_side *sp = fp->sv + fp->iv[lp->s0 + i];
416 if ((u = sol_test_side(t, U, up, fp, lp, sp, o, w)) < t)
425 static float sol_test_node(float dt,
427 const struct s_ball *up,
428 const struct s_file *fp,
429 const struct s_node *np,
433 float U[3], u, t = dt;
438 for (i = 0; i < np->lc; i++)
440 const struct s_lump *lp = fp->lv + np->l0 + i;
442 if ((u = sol_test_lump(t, U, up, fp, lp, o, w)) < t)
449 /* Test in front of this node */
451 if (np->ni >= 0 && sol_test_fore(t, up, fp->sv + np->si, o, w))
453 const struct s_node *nq = fp->nv + np->ni;
455 if ((u = sol_test_node(t, U, up, fp, nq, o, w)) < t)
462 /* Test behind this node */
464 if (np->nj >= 0 && sol_test_back(t, up, fp->sv + np->si, o, w))
466 const struct s_node *nq = fp->nv + np->nj;
468 if ((u = sol_test_node(t, U, up, fp, nq, o, w)) < t)
478 static float sol_test_body(float dt,
479 float T[3], float V[3],
480 const struct s_ball *up,
481 const struct s_file *fp,
482 const struct s_body *bp)
484 float U[3], O[3], W[3], u, t = dt;
486 const struct s_node *np = fp->nv + bp->ni;
488 sol_body_p(O, fp, bp->pi, bp->t);
489 sol_body_v(W, fp, bp->pi, bp->t, dt);
491 if ((u = sol_test_node(t, U, up, fp, np, O, W)) < t)
500 static float sol_test_file(float dt,
501 float T[3], float V[3],
502 const struct s_ball *up,
503 const struct s_file *fp)
505 float U[3], W[3], u, t = dt;
508 for (i = 0; i < fp->bc; i++)
510 const struct s_body *bp = fp->bv + i;
512 if ((u = sol_test_body(t, U, W, up, fp, bp)) < t)
522 /*---------------------------------------------------------------------------*/
525 * Step the physics forward DT seconds under the influence of gravity
526 * vector G. If the ball gets pinched between two moving solids, this
527 * loop might not terminate. It is better to do something physically
528 * impossible than to lock up the game. So, if we make more than C
529 * iterations, punt it.
532 float sol_step(struct s_file *fp, const float *g, float dt, int ui, int *m)
534 float P[3], V[3], v[3], r[3], a[3], d, e, nt, b = 0.0f, tt = dt;
543 struct s_ball *up = fp->uv + ui;
545 /* If the ball is in contact with a surface, apply friction. */
551 if (m && sol_test_file(tt, P, V, up, fp) < 0.0005f)
556 if ((d = v_dot(r, g) / (v_len(r) * v_len(g))) > 0.999f)
558 if ((e = (v_len(up->v) - dt)) > 0.0f)
560 /* Scale the linear velocity. */
563 v_scl(up->v, up->v, e);
565 /* Scale the angular velocity. */
569 v_scl(up->w, up->w, -1.0f / (up->r * up->r));
573 /* Friction has brought the ball to a stop. */
582 else v_mad(up->v, v, g, tt);
584 else v_mad(up->v, v, g, tt);
586 /* Test for collision. */
588 while (c > 0 && tt > 0 && tt > (nt = sol_test_file(tt, P, V, up, fp)))
590 cmd.type = CMD_STEP_SIMULATION;
594 sol_body_step(fp, nt);
595 sol_swch_step(fp, nt);
596 sol_ball_step(fp, nt);
600 if (b < (d = sol_bounce(up, P, V, nt)))
606 cmd.type = CMD_STEP_SIMULATION;
610 sol_body_step(fp, tt);
611 sol_swch_step(fp, tt);
612 sol_ball_step(fp, tt);
614 /* Apply the ball's accelleration to the pendulum. */
618 sol_pendulum(up, a, g, dt);
621 sol_cmd_enq_deferred();
627 /*---------------------------------------------------------------------------*/
629 void sol_init_sim(struct s_file *fp)
634 void sol_quit_sim(void)
639 /*---------------------------------------------------------------------------*/