+static int ray_hit_is_ramp( ray_hit *hit )
+{
+ return hit->tri[0] < world.sm_road.vertex_count;
+}
+
+static void player_start_air(void)
+{
+ player.in_air = 1;
+
+ float pstep = ktimestep*10.0f;
+
+ float best_velocity_mod = 0.0f,
+ best_velocity_delta = -9999.9f;
+
+ v3f axis, vup;
+ m3x3_mulv( player.to_world, (v3f){0.0f,1.0f,0.0f}, vup );
+ v3_cross( vup, player.v, axis );
+ v3_normalize( axis );
+ player.land_log_count = 0;
+
+ m3x3_identity( player.vr );
+
+ for( int m=-3;m<=12; m++ )
+ {
+ float vmod = ((float)m / 15.0f)*0.09f;
+
+ v3f pco, pco1, pv;
+ v3_copy( player.co, pco );
+ v3_copy( player.v, pv );
+ v3_muladds( pco, pv, ktimestep, pco );
+
+ /*
+ * Try different 'rotations' of the velocity to find the best possible
+ * landing normal. This conserves magnitude at the expense of slightly
+ * unrealistic results
+ */
+
+ m3x3f vr;
+ v4f vr_q;
+
+ q_axis_angle( vr_q, axis, vmod );
+ q_m3x3( vr_q, vr );
+
+ for( int i=0; i<50; i++ )
+ {
+ v3_copy( pco, pco1 );
+ apply_gravity( pv, pstep );
+
+ m3x3_mulv( vr, pv, pv );
+ v3_muladds( pco, pv, pstep, pco );
+
+ ray_hit contact;
+ v3f vdir;
+
+ v3_sub( pco, pco1, vdir );
+ contact.dist = v3_length( vdir );
+ v3_divs( vdir, contact.dist, vdir);
+
+ if( bvh_raycast( &world.geo, pco1, vdir, &contact ))
+ {
+ float land_delta = v3_dot( pv, contact.normal );
+ u32 scolour = (u8)(vg_minf(-land_delta * 2.0f, 255.0f));
+
+ /* Bias prediction towords ramps */
+ if( ray_hit_is_ramp( &contact ) )
+ {
+ land_delta *= 0.1f;
+ scolour |= 0x0000a000;
+ }
+
+ if( (land_delta < 0.0f) && (land_delta > best_velocity_delta) )
+ {
+ best_velocity_delta = land_delta;
+ best_velocity_mod = vmod;
+
+ v3_copy( contact.pos, player.land_target );
+
+ q_axis_angle( vr_q, axis, vmod*0.1f );
+ q_m3x3( vr_q, player.vr );
+ }
+
+ v3_copy( contact.pos,
+ player.land_target_log[player.land_log_count] );
+ player.land_target_colours[player.land_log_count] =
+ 0xff000000 | scolour;
+
+ player.land_log_count ++;
+
+ break;
+ }
+ }
+ }
+
+ //v3_rotate( player.v, best_velocity_mod, axis, player.v );
+
+ return;
+ v3_muls( player.v, best_velocity_mod, player.v );
+}
+
+static int sample_if_resistant( v3f pos )
+{
+ v3f ground;
+ v3_copy( pos, ground );
+
+ ray_hit hit;
+ if( bvh_scene_sample( &world.geo, ground, &hit ) )
+ {
+ v3f angle;
+ v3_copy( player.v, angle );
+ v3_normalize( angle );
+ float resistance = v3_dot( hit.normal, angle );
+
+ if( resistance < 0.25f )
+ {
+ v3_copy( ground, pos );
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static float stable_force( float current, float diff )
+{
+ float new = current + diff;
+
+ if( new * current < 0.0f )
+ return 0.0f;
+
+ return new;
+}
+