+ v3f pco, pco1, pv;
+ v3_copy( phys->rb.co, pco );
+ v3_muls( phys->rb.v, k_bias, pv );
+
+ m3x3_mulv( vr, pv, pv );
+ v3_muladds( pco, pv, pstep, pco );
+
+ struct grind_edge *best_grind = NULL;
+ float closest_grind = INFINITY;
+
+ float grind_score = INFINITY,
+ air_score = INFINITY;
+
+ prediction->log_length = 0;
+
+ for( int i=0; i<vg_list_size(prediction->log); i++ )
+ {
+ v3_copy( pco, pco1 );
+ apply_gravity( pv, pstep );
+
+ m3x3_mulv( vr, pv, pv );
+ v3_muladds( pco, pv, pstep, pco );
+
+ v3f vdir;
+
+ v3_sub( pco, pco1, vdir );
+
+ float l = v3_length( vdir );
+ v3_muls( vdir, 1.0f/l, vdir );
+
+ v3f c0, c1;
+ struct grind_edge *ge = player_grind_collect_edge( pco, pco1,
+ c0, c1, 0.4f );
+
+ if( ge && (v3_dot((v3f){0.0f,1.0f,0.0f},vdir) < -0.2f ) )
+ {
+ float d2 = v3_dist2( c0, c1 );
+ if( d2 < closest_grind )
+ {
+ closest_grind = d2;
+ best_grind = ge;
+ grind_score = closest_grind * 0.05f;
+ }
+ }
+
+ v3f n1;
+
+ float t1;
+ int idx = spherecast_world( pco1, pco, 0.4f, &t1, n1 );
+ if( idx != -1 )
+ {
+ v3_copy( n1, prediction->n );
+ air_score = -v3_dot( pv, n1 );
+
+ u32 vert_index = world.scene_geo->arrindices[ idx*3 ];
+ struct world_material *mat = world_tri_index_material( vert_index );
+
+ /* Bias prediction towords ramps */
+ if( mat->info.flags & k_material_flag_skate_surface )
+ air_score *= 0.1f;
+
+ v3_lerp( pco1, pco, t1, prediction->log[ prediction->log_length ++ ] );
+ break;
+ }
+
+ v3_copy( pco, prediction->log[ prediction->log_length ++ ] );
+ }
+
+ if( grind_score < air_score )
+ {
+ prediction->score = grind_score;
+ prediction->type = k_prediction_grind;
+ }
+ else if( air_score < INFINITY )
+ {
+ prediction->score = air_score;
+ prediction->type = k_prediction_land;
+ }
+ else
+ {
+ prediction->score = INFINITY;
+ prediction->type = k_prediction_none;
+ }
+}
+
+/*
+ * Called when launching into the air to predict and adjust trajectories
+ */
+VG_STATIC void player_start_air(void)
+{
+ struct player_phys *phys = &player.phys;
+
+ float pstep = VG_TIMESTEP_FIXED * 10.0f;
+ float best_velocity_delta = -9999.9f;
+