+ m3x3_copy( player.rb.to_world, player.collide_front.to_world );
+ m3x3_copy( player.rb.to_world, player.collide_back.to_world );
+
+ player.air_blend = vg_lerpf( player.air_blend, player.in_air, 0.1f );
+ float h = player.air_blend*0.2f;
+
+ m4x3_mulv( player.rb.to_world, (v3f){0.0f,h,-k_board_length}, rbf->co );
+ v3_copy( rbf->co, rbf->to_world[3] );
+ m4x3_mulv( player.rb.to_world, (v3f){0.0f,h, k_board_length}, rbb->co );
+ v3_copy( rbb->co, rbb->to_world[3] );
+
+ m4x3_invert_affine( rbf->to_world, rbf->to_local );
+ m4x3_invert_affine( rbb->to_world, rbb->to_local );
+
+ rb_update_bounds( rbf );
+ rb_update_bounds( rbb );
+
+ rb_debug( rbf, 0xff00ffff );
+ rb_debug( rbb, 0xffffff00 );
+
+ rb_ct manifold[24];
+ int len = 0;
+
+ len += rb_sphere_vs_scene( rbf, &world.rb_geo, manifold+len );
+ len += rb_sphere_vs_scene( rbb, &world.rb_geo, manifold+len );
+
+ rb_presolve_contacts( manifold, len );
+ v3f surface_avg = {0.0f, 0.0f, 0.0f};
+
+ if( !len )
+ {
+ player_start_air();
+ }
+ else
+ {
+ for( int i=0; i<len; i++ )
+ {
+ v3_add( manifold[i].n, surface_avg, surface_avg );
+
+#if 0
+ if( manifold[i].element_id <= world.sm_geo_std_oob.vertex_count )
+ {
+ player.is_dead = 1;
+ character_ragdoll_copypose( &player.mdl, player.rb.v );
+ return;
+ }
+#endif
+ }
+
+ v3_normalize( surface_avg );
+
+ if( v3_dot( player.rb.v, surface_avg ) > 0.5f )
+ {
+ player_start_air();
+ }
+ else
+ player.in_air = 0;
+ }
+
+ for( int j=0; j<5; j++ )
+ {
+ for( int i=0; i<len; i++ )
+ {
+ struct contact *ct = &manifold[i];
+
+ v3f dv, delta;
+ v3_sub( ct->co, player.rb.co, delta );
+ v3_cross( player.rb.w, delta, dv );
+ v3_add( player.rb.v, dv, dv );
+
+ float vn = -v3_dot( dv, ct->n );
+ vn += ct->bias;
+
+ float temp = ct->norm_impulse;
+ ct->norm_impulse = vg_maxf( temp + vn, 0.0f );
+ vn = ct->norm_impulse - temp;
+
+ v3f impulse;
+ v3_muls( ct->n, vn, impulse );
+
+ if( fabsf(v3_dot( impulse, player.rb.forward )) > 10.0f ||
+ fabsf(v3_dot( impulse, player.rb.up )) > 50.0f )
+ {
+ player.is_dead = 1;
+ character_ragdoll_copypose( &player.mdl, player.rb.v );
+ return;
+ }
+
+ v3_add( impulse, player.rb.v, player.rb.v );
+ v3_cross( delta, impulse, impulse );
+
+ /*
+ * W Impulses are limited to the Y and X axises, we don't really want
+ * roll angular velocities being included.
+ *
+ * Can also tweak the resistance of each axis here by scaling the wx,wy
+ * components.
+ */
+
+ float wy = v3_dot( player.rb.up, impulse ),
+ wx = v3_dot( player.rb.right, impulse )*1.5f;
+
+ v3_muladds( player.rb.w, player.rb.up, wy, player.rb.w );
+ v3_muladds( player.rb.w, player.rb.right, wx, player.rb.w );
+ }
+ }
+
+ float grabt = vg_get_axis( "grabr" )*0.5f+0.5f;
+ player.grab = vg_lerpf( player.grab, grabt, 0.14f );