+ /* Region outside C */
+ v3f cp;
+ float d5, d6;
+ v3_sub( p, tri[2], cp );
+ d5 = v3_dot(ab, cp);
+ d6 = v3_dot(ac, cp);
+
+ if( d6 >= 0.0f && d5 <= d6 )
+ {
+ v3_copy( tri[2], dest );
+ v3_copy( (v3f){INFINITY,INFINITY,INFINITY}, dest );
+ return;
+ }
+
+ /* Region of AC */
+ float vb = d5*d2 - d1*d6;
+ if( vb <= 0.0f && d2 >= 0.0f && d6 <= 0.0f )
+ {
+ float w = d2 / (d2-d6);
+ v3_muladds( tri[0], ac, w, dest );
+ v3_copy( (v3f){INFINITY,INFINITY,INFINITY}, dest );
+ return;
+ }
+
+ /* Region of BC */
+ float va = d3*d6 - d5*d4;
+ if( va <= 0.0f && (d4-d3) >= 0.0f && (d5-d6) >= 0.0f )
+ {
+ float w = (d4-d3) / ((d4-d3) + (d5-d6));
+ v3f bc;
+ v3_sub( tri[2], tri[1], bc );
+ v3_muladds( tri[1], bc, w, dest );
+ v3_copy( (v3f){INFINITY,INFINITY,INFINITY}, dest );
+ return;
+ }
+
+ /* P inside region, Q via barycentric coordinates uvw */
+ float d = 1.0f/(va+vb+vc),
+ v = vb*d,
+ w = vc*d;
+
+ v3_muladds( tri[0], ab, v, dest );
+ v3_muladds( dest, ac, w, dest );
+}
+
+static void closest_on_triangle_1( v3f p, v3f tri[3], v3f dest )
+{
+ v3f ab, ac, ap;
+ float d1, d2;
+
+ /* Region outside A */
+ v3_sub( tri[1], tri[0], ab );
+ v3_sub( tri[2], tri[0], ac );
+ v3_sub( p, tri[0], ap );
+
+ d1 = v3_dot(ab,ap);
+ d2 = v3_dot(ac,ap);
+ if( d1 <= 0.0f && d2 <= 0.0f )
+ {
+ v3_copy( tri[0], dest );
+ return;
+ }
+
+ /* Region outside B */
+ v3f bp;
+ float d3, d4;
+
+ v3_sub( p, tri[1], bp );
+ d3 = v3_dot( ab, bp );
+ d4 = v3_dot( ac, bp );
+
+ if( d3 >= 0.0f && d4 <= d3 )
+ {
+ v3_copy( tri[1], dest );
+ return;
+ }
+
+ /* Edge region of AB */
+ float vc = d1*d4 - d3*d2;
+ if( vc <= 0.0f && d1 >= 0.0f && d3 <= 0.0f )
+ {
+ float v = d1 / (d1-d3);
+ v3_muladds( tri[0], ab, v, dest );
+ return;
+ }
+
+ /* Region outside C */
+ v3f cp;
+ float d5, d6;
+ v3_sub( p, tri[2], cp );
+ d5 = v3_dot(ab, cp);
+ d6 = v3_dot(ac, cp);
+
+ if( d6 >= 0.0f && d5 <= d6 )
+ {
+ v3_copy( tri[2], dest );
+ return;
+ }
+
+ /* Region of AC */
+ float vb = d5*d2 - d1*d6;
+ if( vb <= 0.0f && d2 >= 0.0f && d6 <= 0.0f )
+ {
+ float w = d2 / (d2-d6);
+ v3_muladds( tri[0], ac, w, dest );
+ return;
+ }
+
+ /* Region of BC */
+ float va = d3*d6 - d5*d4;
+ if( va <= 0.0f && (d4-d3) >= 0.0f && (d5-d6) >= 0.0f )
+ {
+ float w = (d4-d3) / ((d4-d3) + (d5-d6));
+ v3f bc;
+ v3_sub( tri[2], tri[1], bc );
+ v3_muladds( tri[1], bc, w, dest );
+ return;
+ }
+
+ /* P inside region, Q via barycentric coordinates uvw */
+ float d = 1.0f/(va+vb+vc),
+ v = vb*d,
+ w = vc*d;
+
+ v3_muladds( tri[0], ab, v, dest );
+ v3_muladds( dest, ac, w, dest );
+}
+
+/*
+ * -----------------------------------------------------------------------------
+ * Boolean shape overlap functions
+ * -----------------------------------------------------------------------------
+ */
+
+/*
+ * Project AABB, and triangle interval onto axis to check if they overlap
+ */
+static int rb_box_triangle_interval( v3f extent, v3f axis, v3f tri[3] )
+{
+ float
+
+ r = extent[0] * fabsf(axis[0]) +
+ extent[1] * fabsf(axis[1]) +
+ extent[2] * fabsf(axis[2]),
+
+ p0 = v3_dot( axis, tri[0] ),
+ p1 = v3_dot( axis, tri[1] ),
+ p2 = v3_dot( axis, tri[2] ),
+
+ e = vg_maxf(-vg_maxf(p0,vg_maxf(p1,p2)), vg_minf(p0,vg_minf(p1,p2)));
+
+ if( e > r ) return 0;
+ else return 1;
+}
+
+/*
+ * Seperating axis test box vs triangle
+ */
+static int rb_box_triangle_sat( rigidbody *rba, v3f tri_src[3] )
+{
+ v3f tri[3];
+
+ v3f extent, c;
+ v3_sub( rba->bbx[1], rba->bbx[0], extent );
+ v3_muls( extent, 0.5f, extent );
+ v3_add( rba->bbx[0], extent, c );
+
+ for( int i=0; i<3; i++ )
+ {
+ m4x3_mulv( rba->to_local, tri_src[i], tri[i] );
+ v3_sub( tri[i], c, tri[i] );
+ }
+
+ /* u0, u1, u2 */
+ if(!rb_box_triangle_interval( extent, (v3f){1.0f,0.0f,0.0f}, tri )) return 0;
+ if(!rb_box_triangle_interval( extent, (v3f){0.0f,1.0f,0.0f}, tri )) return 0;
+ if(!rb_box_triangle_interval( extent, (v3f){0.0f,0.0f,1.0f}, tri )) return 0;
+
+ v3f v0,v1,v2,n, e0,e1,e2;
+ v3_sub( tri[1], tri[0], v0 );
+ v3_sub( tri[2], tri[0], v1 );
+ v3_sub( tri[2], tri[1], v2 );
+ v3_normalize( v0 );
+ v3_normalize( v1 );
+ v3_normalize( v2 );
+ v3_cross( v0, v1, n );
+ v3_cross( v0, n, e0 );
+ v3_cross( n, v1, e1 );
+ v3_cross( v2, n, e2 );
+
+ /* normal */
+ if(!rb_box_triangle_interval( extent, n, tri )) return 0;
+
+ v3f axis[9];
+ v3_cross( e0, (v3f){1.0f,0.0f,0.0f}, axis[0] );
+ v3_cross( e0, (v3f){0.0f,1.0f,0.0f}, axis[1] );
+ v3_cross( e0, (v3f){0.0f,0.0f,1.0f}, axis[2] );
+ v3_cross( e1, (v3f){1.0f,0.0f,0.0f}, axis[3] );
+ v3_cross( e1, (v3f){0.0f,1.0f,0.0f}, axis[4] );
+ v3_cross( e1, (v3f){0.0f,0.0f,1.0f}, axis[5] );
+ v3_cross( e2, (v3f){1.0f,0.0f,0.0f}, axis[6] );
+ v3_cross( e2, (v3f){0.0f,1.0f,0.0f}, axis[7] );
+ v3_cross( e2, (v3f){0.0f,0.0f,1.0f}, axis[8] );
+
+ for( int i=0; i<9; i++ )
+ if(!rb_box_triangle_interval( extent, axis[i], tri )) return 0;
+
+ return 1;
+}
+
+/*
+ * -----------------------------------------------------------------------------
+ * Collision matrix
+ * -----------------------------------------------------------------------------
+ */
+
+/*
+ * Contact generators
+ *
+ * These do not automatically allocate contacts, an appropriately sized
+ * buffer must be supplied. The function returns the size of the manifold
+ * which was generated.
+ *
+ * The values set on the contacts are: n, co, p, rba, rbb
+ */
+
+/*
+ * By collecting the minimum(time) and maximum(time) pairs of points, we
+ * build a reduced and stable exact manifold.
+ *
+ * tx: time at point
+ * rx: minimum distance of these points
+ * dx: the delta between the two points
+ *
+ * pairs will only ammend these if they are creating a collision
+ */
+typedef struct capsule_manifold capsule_manifold;
+struct capsule_manifold
+{
+ float t0, t1;
+ float r0, r1;
+ v3f d0, d1;
+};
+
+/*
+ * Expand a line manifold with a new pair. t value is the time along segment
+ * on the oriented object which created this pair.
+ */
+static void rb_capsule_manifold( v3f pa, v3f pb, float t, float r,
+ capsule_manifold *manifold )
+{
+ v3f delta;
+ v3_sub( pa, pb, delta );
+
+ if( v3_length2(delta) < r*r )
+ {
+ if( t < manifold->t0 )
+ {
+ v3_copy( delta, manifold->d0 );
+ manifold->t0 = t;
+ manifold->r0 = r;
+ }
+
+ if( t > manifold->t1 )
+ {
+ v3_copy( delta, manifold->d1 );
+ manifold->t1 = t;
+ manifold->r1 = r;
+ }
+ }
+}
+
+static void rb_capsule_manifold_init( capsule_manifold *manifold )
+{
+ manifold->t0 = INFINITY;
+ manifold->t1 = -INFINITY;
+}
+
+static int rb_capsule_manifold_done( rigidbody *rba, rigidbody *rbb,
+ capsule_manifold *manifold, rb_ct *buf )
+{
+ float h = rba->inf.capsule.height,
+ ra = rba->inf.capsule.radius;
+
+ v3f p0, p1;
+ v3_muladds( rba->co, rba->up, -h*0.5f+ra, p0 );
+ v3_muladds( rba->co, rba->up, h*0.5f-ra, p1 );
+
+ int count = 0;
+ if( manifold->t0 <= 1.0f )
+ {
+ rb_ct *ct = buf;
+
+ v3f pa;
+ v3_muls( p0, 1.0f-manifold->t0, pa );
+ v3_muladds( pa, p1, manifold->t0, pa );
+
+ float d = v3_length( manifold->d0 );
+ v3_muls( manifold->d0, 1.0f/d, ct->n );
+ v3_muladds( pa, ct->n, -ra, ct->co );
+
+ ct->p = manifold->r0 - d;
+ ct->rba = rba;
+ ct->rbb = rbb;
+
+ count ++;
+ }
+
+ if( (manifold->t1 >= 0.0f) && (manifold->t0 != manifold->t1) )
+ {
+ rb_ct *ct = buf+count;
+
+ v3f pa;
+ v3_muls( p0, 1.0f-manifold->t1, pa );
+ v3_muladds( pa, p1, manifold->t1, pa );
+
+ float d = v3_length( manifold->d1 );
+ v3_muls( manifold->d1, 1.0f/d, ct->n );
+ v3_muladds( pa, ct->n, -ra, ct->co );
+
+ ct->p = manifold->r1 - d;
+ ct->rba = rba;
+ ct->rbb = rbb;
+
+ count ++;
+ }
+
+ /*
+ * Debugging
+ */
+
+ if( count == 2 )
+ vg_line( buf[0].co, buf[1].co, 0xff0000ff );
+
+ return count;
+}
+
+static int rb_capsule_sphere( rigidbody *rba, rigidbody *rbb, rb_ct *buf )
+{
+ float h = rba->inf.capsule.height,
+ ra = rba->inf.capsule.radius,
+ rb = rbb->inf.sphere.radius;
+
+ v3f p0, p1;
+ v3_muladds( rba->co, rba->up, -h*0.5f+ra, p0 );
+ v3_muladds( rba->co, rba->up, h*0.5f-ra, p1 );
+
+ v3f c, delta;
+ closest_point_segment( p0, p1, rbb->co, c );
+ v3_sub( c, rbb->co, delta );
+
+ float d2 = v3_length2(delta),
+ r = ra + rb;
+
+ if( d2 < r*r )
+ {
+ float d = sqrtf(d2);
+
+ rb_ct *ct = buf;
+ v3_muls( delta, 1.0f/d, ct->n );
+ ct->p = r-d;
+
+ v3f p0, p1;
+ v3_muladds( c, ct->n, -ra, p0 );
+ v3_muladds( rbb->co, ct->n, rb, p1 );
+ v3_add( p0, p1, ct->co );
+ v3_muls( ct->co, 0.5f, ct->co );
+
+ ct->rba = rba;
+ ct->rbb = rbb;
+
+ return 1;
+ }
+
+ return 0;