+}
+
+VG_STATIC void player_grind(void)
+{
+ struct player_phys *phys = &player.phys;
+
+ v3f closest;
+ int idx = bh_closest_point( world.grind_bh, phys->rb.co, closest, INFINITY );
+ if( idx == -1 )
+ return;
+
+ struct grind_edge *edge = &world.grind_edges[ idx ];
+
+ vg_line( phys->rb.co, closest, 0xff000000 );
+ vg_line_cross( closest, 0xff000000, 0.3f );
+ vg_line( edge->p0, edge->p1, 0xff000000 );
+
+ v3f grind_delta;
+ v3_sub( closest, phys->rb.co, grind_delta );
+
+ float p = v3_dot( phys->rb.forward, grind_delta );
+ v3_muladds( grind_delta, phys->rb.forward, -p, grind_delta );
+
+ float a = vg_maxf( 0.0f, 4.0f-v3_dist2( closest, phys->rb.co ) );
+ v3_muladds( phys->rb.v, grind_delta, a*0.2f, phys->rb.v );
+}
+
+VG_STATIC int player_update_grind_collision( rb_ct *contact )
+{
+ struct player_phys *phys = &player.phys;
+
+ v3f p0, p1, c0, c1;
+ v3_muladds( phys->rb.co, phys->rb.forward, 0.5f, p0 );
+ v3_muladds( phys->rb.co, phys->rb.forward, -0.5f, p1 );
+ v3_muladds( p0, phys->rb.up, 0.125f, p0 );
+ v3_muladds( p1, phys->rb.up, 0.125f, p1 );
+
+ float const k_r = 0.25f;
+ struct grind_edge *closest_edge = player_grind_collect_edge( p0, p1,
+ c0, c1, k_r );
+
+
+ vg_line( p0, p1, 0xff0000ff );
+
+ if( closest_edge )