+ return 0;
+}
+
+static int rb_sphere_triangle( rigidbody *rba, rigidbody *rbb,
+ v3f tri[3], rb_ct *buf )
+{
+ v3f delta, co;
+
+ closest_on_triangle( rba->co, tri, co );
+ v3_sub( rba->co, co, delta );
+
+ vg_line( rba->co, co, 0xffff0000 );
+ vg_line_pt3( rba->co, 0.1f, 0xff00ffff );
+
+ float d2 = v3_length2( delta ),
+ r = rba->inf.sphere.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 );
+ v3_normalize( ct->n );
+
+ float d = sqrtf(d2);
+
+ v3_copy( co, ct->co );
+ ct->p = r-d;
+ ct->rba = rba;
+ ct->rbb = rbb;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int rb_sphere_scene( rigidbody *rba, rigidbody *rbb, rb_ct *buf )
+{
+ scene *sc = rbb->inf.scene.pscene;
+
+ u32 geo[128];
+ v3f tri[3];
+ int len = bh_select( &sc->bhtris, rba->bbx_world, geo, 128 );
+
+ int count = 0;
+
+ for( int i=0; i<len; i++ )
+ {
+ u32 *ptri = &sc->indices[ geo[i]*3 ];
+
+ for( int j=0; j<3; j++ )
+ v3_copy( sc->verts[ptri[j]].co, tri[j] );
+
+ vg_line(tri[0],tri[1],0xff00ff00 );
+ vg_line(tri[1],tri[2],0xff00ff00 );
+ vg_line(tri[2],tri[0],0xff00ff00 );
+
+ buf[count].element_id = ptri[0];
+ count += rb_sphere_triangle( rba, rbb, tri, buf+count );
+
+ if( count == 12 )
+ {
+ vg_warn( "Exceeding sphere_vs_scene capacity. Geometry too dense!\n" );
+ return count;
+ }
+ }
+
+ return count;
+}
+
+static int rb_box_scene( rigidbody *rba, rigidbody *rbb, rb_ct *buf )
+{
+ scene *sc = rbb->inf.scene.pscene;
+
+ u32 geo[128];
+ v3f tri[3];
+ int len = bh_select( &sc->bhtris, rba->bbx_world, geo, 128 );
+
+ int count = 0;
+
+ for( int i=0; i<len; i++ )
+ {
+ u32 *ptri = &sc->indices[ geo[i]*3 ];
+
+ for( int j=0; j<3; j++ )
+ v3_copy( sc->verts[ptri[j]].co, tri[j] );
+
+ if( rb_box_triangle_sat( rba, 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 );
+ v3_normalize( n );
+
+ /* find best feature */
+ float best = v3_dot( rba->right, n );
+ int axis = 0;
+
+ float cy = v3_dot( rba->up, n );
+ if( fabsf(cy) > fabsf(best) )
+ {
+ best = cy;
+ axis = 1;
+ }
+
+ float cz = -v3_dot( rba->forward, n );
+ if( fabsf(cz) > fabsf(best) )
+ {
+ best = cz;
+ axis = 2;
+ }
+
+ v3f manifold[4];
+
+ if( axis == 0 )
+ {
+ float px = best > 0.0f? rba->bbx[0][0]: rba->bbx[1][0];
+ manifold[0][0] = px;
+ manifold[0][1] = rba->bbx[0][1];
+ manifold[0][2] = rba->bbx[0][2];
+ manifold[1][0] = px;
+ manifold[1][1] = rba->bbx[1][1];
+ manifold[1][2] = rba->bbx[0][2];
+ manifold[2][0] = px;
+ manifold[2][1] = rba->bbx[1][1];
+ manifold[2][2] = rba->bbx[1][2];
+ manifold[3][0] = px;
+ manifold[3][1] = rba->bbx[0][1];
+ manifold[3][2] = rba->bbx[1][2];
+ }
+ else if( axis == 1 )
+ {
+ float py = best > 0.0f? rba->bbx[0][1]: rba->bbx[1][1];
+ manifold[0][0] = rba->bbx[0][0];
+ manifold[0][1] = py;
+ manifold[0][2] = rba->bbx[0][2];
+ manifold[1][0] = rba->bbx[1][0];
+ manifold[1][1] = py;
+ manifold[1][2] = rba->bbx[0][2];
+ manifold[2][0] = rba->bbx[1][0];
+ manifold[2][1] = py;
+ manifold[2][2] = rba->bbx[1][2];
+ manifold[3][0] = rba->bbx[0][0];
+ manifold[3][1] = py;
+ manifold[3][2] = rba->bbx[1][2];
+ }
+ else
+ {
+ float pz = best > 0.0f? rba->bbx[0][2]: rba->bbx[1][2];
+ manifold[0][0] = rba->bbx[0][0];
+ manifold[0][1] = rba->bbx[0][1];
+ manifold[0][2] = pz;
+ manifold[1][0] = rba->bbx[1][0];
+ manifold[1][1] = rba->bbx[0][1];
+ manifold[1][2] = pz;
+ manifold[2][0] = rba->bbx[1][0];
+ manifold[2][1] = rba->bbx[1][1];
+ manifold[2][2] = pz;
+ manifold[3][0] = rba->bbx[0][0];
+ manifold[3][1] = rba->bbx[1][1];
+ manifold[3][2] = pz;
+ }
+
+ for( int j=0; j<4; j++ )
+ m4x3_mulv( rba->to_world, manifold[j], manifold[j] );
+
+ vg_line( manifold[0], manifold[1], 0xffffffff );
+ vg_line( manifold[1], manifold[2], 0xffffffff );
+ vg_line( manifold[2], manifold[3], 0xffffffff );
+ vg_line( manifold[3], manifold[0], 0xffffffff );
+
+ for( int j=0; j<4; j++ )
+ {
+ rb_ct *ct = buf+count;
+
+ v3_copy( manifold[j], ct->co );
+ v3_copy( n, ct->n );
+
+ float l0 = v3_dot( tri[0], n ),
+ l1 = v3_dot( manifold[j], n );
+
+ ct->p = (l0-l1)*0.5f;
+ if( ct->p < 0.0f )
+ continue;
+
+ ct->rba = rba;
+ ct->rbb = rbb;
+ count ++;
+
+ if( count >= 12 )
+ return count;
+ }
+ }
+ return count;
+}
+
+static int RB_MATRIX_ERROR( rigidbody *rba, rigidbody *rbb, rb_ct *buf )
+{
+ vg_error( "Collision type is unimplemented between types %d and %d\n",
+ rba->type, rbb->type );
+
+ return 0;
+}
+
+static int rb_sphere_capsule( rigidbody *rba, rigidbody *rbb, rb_ct *buf )
+{
+ return rb_capsule_sphere( rbb, rba, buf );
+}
+
+static int rb_box_capsule( rigidbody *rba, rigidbody *rbb, rb_ct *buf )
+{
+ return rb_capsule_box( rbb, rba, buf );
+}
+
+static int rb_box_sphere( rigidbody *rba, rigidbody *rbb, rb_ct *buf )
+{
+ return rb_sphere_box( rbb, rba, buf );
+}
+
+static int rb_scene_box( rigidbody *rba, rigidbody *rbb, rb_ct *buf )
+{
+ return rb_box_scene( rbb, rba, buf );
+}
+
+static int (*rb_jump_table[4][4])( rigidbody *a, rigidbody *b, rb_ct *buf ) =
+{
+ /* box */ /* Sphere */ /* Capsule */ /* Mesh */
+ { RB_MATRIX_ERROR, rb_box_sphere, rb_box_capsule, rb_box_scene },
+ { rb_sphere_box, rb_sphere_sphere, rb_sphere_capsule, rb_sphere_scene },
+ { rb_capsule_box, rb_capsule_sphere, rb_capsule_capsule, RB_MATRIX_ERROR },
+ { rb_scene_box, RB_MATRIX_ERROR, RB_MATRIX_ERROR, RB_MATRIX_ERROR }
+};
+
+static int rb_collide( rigidbody *rba, rigidbody *rbb )
+{
+ int (*collider_jump)(rigidbody *rba, rigidbody *rbb, rb_ct *buf )
+ = rb_jump_table[rba->type][rbb->type];
+
+ /*
+ * 12 is the maximum manifold size we can generate, so we are forced to abort
+ * potentially checking any more.
+ */
+ if( rb_contact_count + 12 > vg_list_size(rb_contact_buffer) )
+ {
+ vg_warn( "Too many contacts made in global collider buffer (%d of %d\n)",
+ rb_contact_count, vg_list_size(rb_contact_buffer) );
+ return 0;
+ }
+
+ /*
+ * FUTURE: Replace this with a more dedicated broad phase pass
+ */
+ if( box_overlap( rba->bbx_world, rbb->bbx_world ) )
+ {
+ int count = collider_jump( rba, rbb, rb_contact_buffer+rb_contact_count);
+ rb_contact_count += count;
+ return count;
+ }
+ else
+ return 0;
+}
+
+/*
+ * -----------------------------------------------------------------------------
+ * Dynamics
+ * -----------------------------------------------------------------------------
+ */
+
+static void rb_solver_reset(void)
+{
+ rb_contact_count = 0;
+}
+
+static rb_ct *rb_global_ct(void)
+{
+ return rb_contact_buffer + rb_contact_count;
+}
+
+/*
+ * Initializing things like tangent vectors
+ */
+static void rb_presolve_contacts( rb_ct *buffer, int len )
+{
+ for( int i=0; i<len; i++ )
+ {
+ rb_ct *ct = &buffer[i];
+ ct->bias = -0.2f * k_rb_rate * vg_minf( 0.0f, -ct->p+k_penetration_slop );
+ rb_tangent_basis( ct->n, ct->t[0], ct->t[1] );
+
+ ct->norm_impulse = 0.0f;
+ ct->tangent_impulse[0] = 0.0f;
+ ct->tangent_impulse[1] = 0.0f;
+
+ v3f ra, rb, raCn, rbCn, raCt, rbCt;
+ v3_sub( ct->co, ct->rba->co, ra );
+ v3_sub( ct->co, ct->rbb->co, rb );
+ v3_cross( ra, ct->n, raCn );
+ v3_cross( rb, ct->n, rbCn );
+
+ /* orient inverse inertia tensors */
+ v3f raCnI, rbCnI;
+ m3x3_mulv( ct->rba->iIw, raCn, raCnI );
+ m3x3_mulv( ct->rbb->iIw, rbCn, rbCnI );
+
+ ct->normal_mass = ct->rba->inv_mass + ct->rbb->inv_mass;
+ ct->normal_mass += v3_dot( raCn, raCnI );
+ ct->normal_mass += v3_dot( rbCn, rbCnI );
+ ct->normal_mass = 1.0f/ct->normal_mass;
+
+ for( int j=0; j<2; j++ )
+ {
+ v3f raCtI, rbCtI;
+ v3_cross( ct->t[j], ra, raCt );
+ v3_cross( ct->t[j], rb, rbCt );
+ m3x3_mulv( ct->rba->iIw, raCt, raCtI );
+ m3x3_mulv( ct->rbb->iIw, rbCt, rbCtI );
+
+ ct->tangent_mass[j] = ct->rba->inv_mass + ct->rbb->inv_mass;
+ ct->tangent_mass[j] += v3_dot( raCt, raCtI );
+ ct->tangent_mass[j] += v3_dot( rbCt, rbCtI );
+ ct->tangent_mass[j] = 1.0f/ct->tangent_mass[j];
+ }
+
+ rb_debug_contact( ct );
+ }
+}
+
+/*
+ * Creates relative contact velocity vector, and offsets between each body
+ */
+static void rb_rcv( rb_ct *ct, v3f rv, v3f da, v3f db )
+{
+ rigidbody *rba = ct->rba,
+ *rbb = ct->rbb;
+
+ v3_sub( ct->co, rba->co, da );
+ v3_sub( ct->co, rbb->co, db );
+
+ v3f rva, rvb;
+ v3_cross( rba->w, da, rva );
+ v3_add( rba->v, rva, rva );
+ v3_cross( rbb->w, db, rvb );
+ v3_add( rbb->v, rvb, rvb );
+
+ v3_sub( rva, rvb, rv );
+}
+
+/*
+ * Apply impulse to object
+ */
+static void rb_linear_impulse( rigidbody *rb, v3f delta, v3f impulse )
+{
+ /* linear */
+ v3_muladds( rb->v, impulse, rb->inv_mass, rb->v );
+
+ /* Angular velocity */
+ v3f wa;
+ v3_cross( delta, impulse, wa );
+
+ m3x3_mulv( rb->iIw, wa, wa );
+ v3_add( rb->w, wa, rb->w );
+}
+
+/*
+ * One iteration to solve the contact constraint
+ */
+static void rb_solve_contacts( rb_ct *buf, int len )
+{
+ for( int i=0; i<len; i++ )
+ {
+ struct contact *ct = &buf[i];
+ rigidbody *rb = ct->rba;
+
+ v3f rv, da, db;
+ rb_rcv( ct, rv, da, db );
+
+ /* Friction */
+ for( int j=0; j<2; j++ )
+ {
+ float f = k_friction * ct->norm_impulse,
+ vt = v3_dot( rv, ct->t[j] ),
+ lambda = ct->tangent_mass[j] * -vt;
+
+ float temp = ct->tangent_impulse[j];
+ ct->tangent_impulse[j] = vg_clampf( temp + lambda, -f, f );
+ lambda = ct->tangent_impulse[j] - temp;
+
+ v3f impulse;
+ v3_muls( ct->t[j], lambda, impulse );
+ rb_linear_impulse( ct->rba, da, impulse );
+
+ v3_muls( ct->t[j], -lambda, impulse );
+ rb_linear_impulse( ct->rbb, db, impulse );
+ }
+
+ /* Normal */
+ rb_rcv( ct, rv, da, db );
+ float vn = v3_dot( rv, ct->n ),
+ lambda = ct->normal_mass * (-vn + ct->bias);
+
+ float temp = ct->norm_impulse;
+ ct->norm_impulse = vg_maxf( temp + lambda, 0.0f );
+ lambda = ct->norm_impulse - temp;
+
+ v3f impulse;
+ v3_muls( ct->n, lambda, impulse );
+ rb_linear_impulse( ct->rba, da, impulse );
+
+ v3_muls( ct->n, -lambda, impulse );
+ rb_linear_impulse( ct->rbb, db, impulse );
+ }
+}
+
+/*
+ * -----------------------------------------------------------------------------
+ * Constraints
+ * -----------------------------------------------------------------------------
+ */
+
+static void draw_angle_limit( v3f c, v3f major, v3f minor,
+ float amin, float amax, float measured,
+ u32 colour )
+{
+ float f = 0.05f;
+ v3f ay, ax;
+ v3_muls( major, f, ay );
+ v3_muls( minor, f, ax );
+
+ for( int x=0; x<16; x++ )
+ {
+ float t0 = (float)x / 16.0f,
+ t1 = (float)(x+1) / 16.0f,
+ a0 = vg_lerpf( amin, amax, t0 ),
+ a1 = vg_lerpf( amin, amax, t1 );
+
+ v3f p0, p1;
+ v3_muladds( c, ay, cosf(a0), p0 );
+ v3_muladds( p0, ax, sinf(a0), p0 );
+ v3_muladds( c, ay, cosf(a1), p1 );
+ v3_muladds( p1, ax, sinf(a1), p1 );
+