+ v3f delta;
+ v3_sub( ci->rba->co, ci->co, delta );
+
+ float c0 = v3_dot( ci->n, delta ),
+ c1 = v3_dot( cj->n, delta );
+
+ if( c0 < 0.0f || c1 < 0.0f ){
+ /* error */
+ ci->type = k_contact_type_disabled;
+ }
+ else{
+ v3f n;
+ v3_muls( ci->n, c0, n );
+ v3_muladds( n, cj->n, c1, n );
+ v3_normalize( n );
+ v3_copy( n, ci->n );
+ }
+ }
+}
+
+/*
+ *
+ */
+static void rb_manifold_filter_joint_edges( rb_ct *man, int len, float r ){
+ for( int i=0; i<len-1; i++ ){
+ rb_ct *ci = &man[i];
+ if( ci->type != k_contact_type_edge )
+ continue;
+
+ for( int j=i+1; j<len; j++ ){
+ rb_ct *cj = &man[j];
+ if( cj->type != k_contact_type_edge )
+ continue;
+
+ rb_manifold_contact_weld( ci, cj, r );
+ }
+ }
+}
+
+/*
+ * Resolve overlapping pairs
+ */
+static void rb_manifold_filter_pairs( rb_ct *man, int len, float r ){
+ for( int i=0; i<len-1; i++ ){
+ rb_ct *ci = &man[i];
+ int similar = 0;
+
+ if( ci->type == k_contact_type_disabled ) continue;
+
+ for( int j=i+1; j<len; j++ ){
+ rb_ct *cj = &man[j];
+
+ if( cj->type == k_contact_type_disabled ) continue;
+
+ if( v3_dist2( ci->co, cj->co ) < r*r ){
+ cj->type = k_contact_type_disabled;
+ v3_add( cj->n, ci->n, ci->n );
+ ci->p += cj->p;
+ similar ++;
+ }
+ }
+
+ if( similar ){
+ float n = 1.0f/((float)similar+1.0f);
+ v3_muls( ci->n, n, ci->n );
+ ci->p *= n;
+
+ if( v3_length2(ci->n) < 0.1f*0.1f )
+ ci->type = k_contact_type_disabled;
+ else
+ v3_normalize( ci->n );
+ }
+ }
+}
+
+/*
+ * Remove contacts that are facing away from A
+ */
+static void rb_manifold_filter_backface( rb_ct *man, int len ){
+ for( int i=0; i<len; i++ ){
+ rb_ct *ct = &man[i];
+ if( ct->type == k_contact_type_disabled )
+ continue;
+
+ v3f delta;
+ v3_sub( ct->co, ct->rba->co, delta );
+
+ if( v3_dot( delta, ct->n ) > -0.001f )
+ ct->type = k_contact_type_disabled;
+ }
+}
+
+/*
+ * Filter out duplicate coplanar results. Good for spheres.
+ */
+static void rb_manifold_filter_coplanar( rb_ct *man, int len, float w ){
+ for( int i=0; i<len; i++ ){
+ rb_ct *ci = &man[i];
+ if( ci->type == k_contact_type_disabled ||
+ ci->type == k_contact_type_edge )
+ continue;
+
+ float d1 = v3_dot( ci->co, ci->n );
+
+ for( int j=0; j<len; j++ ){
+ if( j == i )
+ continue;
+
+ rb_ct *cj = &man[j];
+ if( cj->type == k_contact_type_disabled )
+ continue;
+
+ float d2 = v3_dot( cj->co, ci->n ),
+ d = d2-d1;
+
+ if( fabsf( d ) <= w ){
+ cj->type = k_contact_type_disabled;
+ }
+ }
+ }
+}
+
+/*
+ * -----------------------------------------------------------------------------
+ * 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( m4x3f mtx, rb_capsule *c,
+ capsule_manifold *manifold,
+ rb_ct *buf ){
+ v3f p0, p1;
+ v3_muladds( mtx[3], mtx[1], -c->height*0.5f+c->radius, p0 );
+ v3_muladds( mtx[3], mtx[1], c->height*0.5f-c->radius, 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, -c->radius, ct->co );
+
+ ct->p = manifold->r0 - d;
+ ct->type = k_contact_type_default;
+ 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, -c->radius, ct->co );
+
+ ct->p = manifold->r1 - d;
+ ct->type = k_contact_type_default;
+
+ count ++;
+ }
+
+ /*
+ * Debugging
+ */
+
+ if( count == 2 )
+ vg_line( buf[0].co, buf[1].co, 0xff0000ff );
+
+ return count;
+}
+
+static int rb_capsule_sphere( rb_object *obja, rb_object *objb, rb_ct *buf ){
+ rigidbody *rba = &obja->rb, *rbb = &objb->rb;
+ float h = obja->inf.capsule.height,
+ ra = obja->inf.capsule.radius,
+ rb = objb->inf.sphere.radius;
+
+ v3f p0, p1;
+ v3_muladds( rba->co, rba->to_world[1], -h*0.5f+ra, p0 );
+ v3_muladds( rba->co, rba->to_world[1], 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;
+ ct->type = k_contact_type_default;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int rb_capsule__capsule( m4x3f mtxA, rb_capsule *ca,
+ m4x3f mtxB, rb_capsule *cb, rb_ct *buf ){
+ float ha = ca->height,
+ hb = cb->height,
+ ra = ca->radius,
+ rb = cb->radius,
+ r = ra+rb;
+
+ v3f p0, p1, p2, p3;
+ v3_muladds( mtxA[3], mtxA[1], -ha*0.5f+ra, p0 );
+ v3_muladds( mtxA[3], mtxA[1], ha*0.5f-ra, p1 );
+ v3_muladds( mtxB[3], mtxB[1], -hb*0.5f+rb, p2 );
+ v3_muladds( mtxB[3], mtxB[1], hb*0.5f-rb, p3 );
+
+ capsule_manifold manifold;
+ rb_capsule_manifold_init( &manifold );
+
+ v3f pa, pb;
+ float ta, tb;
+ closest_segment_segment( p0, p1, p2, p3, &ta, &tb, pa, pb );
+ rb_capsule_manifold( pa, pb, ta, r, &manifold );
+
+ ta = closest_point_segment( p0, p1, p2, pa );
+ tb = closest_point_segment( p0, p1, p3, pb );
+ rb_capsule_manifold( pa, p2, ta, r, &manifold );
+ rb_capsule_manifold( pb, p3, tb, r, &manifold );
+
+ closest_point_segment( p2, p3, p0, pa );
+ closest_point_segment( p2, p3, p1, pb );
+ rb_capsule_manifold( p0, pa, 0.0f, r, &manifold );
+ rb_capsule_manifold( p1, pb, 1.0f, r, &manifold );
+
+ return rb_capsule__manifold_done( mtxA, ca, &manifold, buf );
+}
+
+static int rb_sphere_box( rb_object *obja, rb_object *objb, rb_ct *buf ){
+ v3f co, delta;
+ rigidbody *rba = &obja->rb, *rbb = &objb->rb;
+
+ closest_point_obb( rba->co, rbb->bbx, rbb->to_world, rbb->to_local, co );
+ v3_sub( rba->co, co, delta );
+
+ float d2 = v3_length2(delta),
+ r = obja->inf.sphere.radius;
+
+ if( d2 <= r*r ){
+ float d;
+
+ rb_ct *ct = buf;
+ if( d2 <= 0.0001f ){
+ v3_sub( rba->co, rbb->co, delta );
+
+ /*
+ * some extra testing is required to find the best axis to push the
+ * object back outside the box. Since there isnt a clear seperating
+ * vector already, especially on really high aspect boxes.
+ */
+ float lx = v3_dot( rbb->to_world[0], delta ),
+ ly = v3_dot( rbb->to_world[1], delta ),
+ lz = v3_dot( rbb->to_world[2], delta ),
+ px = rbb->bbx[1][0] - fabsf(lx),
+ py = rbb->bbx[1][1] - fabsf(ly),
+ pz = rbb->bbx[1][2] - fabsf(lz);
+
+ if( px < py && px < pz )
+ v3_muls( rbb->to_world[0], vg_signf(lx), ct->n );
+ else if( py < pz )
+ v3_muls( rbb->to_world[1], vg_signf(ly), ct->n );
+ else
+ v3_muls( rbb->to_world[2], vg_signf(lz), ct->n );
+
+ v3_muladds( rba->co, ct->n, -r, ct->co );
+ ct->p = r;
+ }
+ else{
+ d = sqrtf(d2);
+ v3_muls( delta, 1.0f/d, ct->n );
+ ct->p = r-d;
+ v3_copy( co, ct->co );
+ }
+
+ ct->rba = rba;
+ ct->rbb = rbb;
+ ct->type = k_contact_type_default;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int rb_sphere_sphere( rb_object *obja, rb_object *objb, rb_ct *buf ){
+ rigidbody *rba = &obja->rb, *rbb = &objb->rb;
+ v3f delta;
+ v3_sub( rba->co, rbb->co, delta );
+
+ float d2 = v3_length2(delta),
+ r = obja->inf.sphere.radius + objb->inf.sphere.radius;
+
+ if( d2 < r*r ){
+ float d = sqrtf(d2);
+
+ rb_ct *ct = buf;
+ v3_muls( delta, 1.0f/d, ct->n );
+
+ v3f p0, p1;
+ v3_muladds( rba->co, ct->n,-obja->inf.sphere.radius, p0 );
+ v3_muladds( rbb->co, ct->n, objb->inf.sphere.radius, p1 );
+ v3_add( p0, p1, ct->co );
+ v3_muls( ct->co, 0.5f, ct->co );
+ ct->type = k_contact_type_default;
+ ct->p = r-d;
+ ct->rba = rba;
+ ct->rbb = rbb;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int rb_sphere__triangle( m4x3f mtxA, rb_sphere *b,
+ v3f tri[3], rb_ct *buf ){
+ v3f delta, co;
+ enum contact_type type = closest_on_triangle_1( mtxA[3], tri, co );
+
+ v3_sub( mtxA[3], co, delta );
+
+ float d2 = v3_length2( delta ),
+ r = b->radius;
+
+ if( d2 <= r*r ){
+ rb_ct *ct = buf;
+
+ v3f ab, ac, tn;
+ v3_sub( tri[2], tri[0], ab );
+ v3_sub( tri[1], tri[0], ac );
+ v3_cross( ac, ab, tn );
+ v3_copy( tn, ct->n );
+
+ if( v3_length2( ct->n ) <= 0.00001f ){
+#ifdef RIGIDBODY_CRY_ABOUT_EVERYTHING
+ vg_error( "Zero area triangle!\n" );
+#endif
+ return 0;
+ }
+
+ v3_normalize( ct->n );
+
+ float d = sqrtf(d2);
+
+ v3_copy( co, ct->co );
+ ct->type = type;
+ ct->p = r-d;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int rb_sphere__scene( m4x3f mtxA, rb_sphere *b,
+ m4x3f mtxB, rb_scene *s, rb_ct *buf,
+ u16 ignore ){
+ scene_context *sc = s->bh_scene->user;
+
+ int count = 0;
+
+ float r = b->radius + 0.1f;
+ boxf box;
+ v3_sub( mtxA[3], (v3f){ r,r,r }, box[0] );
+ v3_add( mtxA[3], (v3f){ r,r,r }, box[1] );
+
+ bh_iter it;
+ i32 idx;
+ bh_iter_init_box( 0, &it, box );
+
+ while( bh_next( s->bh_scene, &it, &idx ) ){
+ u32 *ptri = &sc->arrindices[ idx*3 ];
+ v3f tri[3];
+
+ if( sc->arrvertices[ptri[0]].flags & ignore ) continue;
+
+ for( int j=0; j<3; j++ )
+ v3_copy( sc->arrvertices[ptri[j]].co, tri[j] );
+
+ buf[ count ].element_id = ptri[0];
+
+ vg_line( tri[0],tri[1],0x70ff6000 );
+ vg_line( tri[1],tri[2],0x70ff6000 );
+ vg_line( tri[2],tri[0],0x70ff6000 );
+
+ int contact = rb_sphere__triangle( mtxA, b, tri, &buf[count] );
+ count += contact;
+
+ if( count == 16 ){
+ vg_warn( "Exceeding sphere_vs_scene capacity. Geometry too dense!\n" );
+ return count;
+ }
+ }
+
+ return count;
+}
+
+static int rb_box__scene( m4x3f mtxA, boxf bbx,
+ m4x3f mtxB, rb_scene *s, rb_ct *buf, u16 ignore ){
+ scene_context *sc = s->bh_scene->user;
+ v3f tri[3];
+
+ v3f extent, center;
+ v3_sub( bbx[1], bbx[0], extent );
+ v3_muls( extent, 0.5f, extent );
+ v3_add( bbx[0], extent, center );
+
+ float r = v3_length(extent);
+ boxf world_bbx;
+ v3_fill( world_bbx[0], -r );
+ v3_fill( world_bbx[1], r );
+ for( int i=0; i<2; i++ ){
+ v3_add( center, world_bbx[i], world_bbx[i] );
+ v3_add( mtxA[3], world_bbx[i], world_bbx[i] );
+ }
+
+ m4x3f to_local;
+ m4x3_invert_affine( mtxA, to_local );
+
+ bh_iter it;
+ bh_iter_init_box( 0, &it, world_bbx );
+ int idx;
+ int count = 0;
+
+ vg_line_boxf( world_bbx, VG__RED );
+
+ while( bh_next( s->bh_scene, &it, &idx ) ){
+ u32 *ptri = &sc->arrindices[ idx*3 ];
+ if( sc->arrvertices[ptri[0]].flags & ignore ) continue;
+
+ for( int j=0; j<3; j++ )
+ v3_copy( sc->arrvertices[ptri[j]].co, tri[j] );
+
+ if( rb_box_triangle_sat( extent, center, to_local, tri ) ){
+ vg_line(tri[0],tri[1],0xff50ff00 );
+ vg_line(tri[1],tri[2],0xff50ff00 );
+ vg_line(tri[2],tri[0],0xff50ff00 );
+ }
+ else{
+ vg_line(tri[0],tri[1],0xff0000ff );
+ vg_line(tri[1],tri[2],0xff0000ff );
+ vg_line(tri[2],tri[0],0xff0000ff );
+ continue;
+ }
+
+ v3f v0,v1,n;
+ v3_sub( tri[1], tri[0], v0 );
+ v3_sub( tri[2], tri[0], v1 );
+ v3_cross( v0, v1, n );
+
+ if( v3_length2( n ) <= 0.00001f ){
+#ifdef RIGIDBODY_CRY_ABOUT_EVERYTHING
+ vg_error( "Zero area triangle!\n" );
+#endif
+ return 0;
+ }