before the storm
[carveJwlIkooP6JGAAIwe30JlM.git] / player.h
index aa5b548d0821dc27f2203166c9a6b34816842241..6fb74dcf6427c5c6ce6a709399f77edb9a9dadec 100644 (file)
--- a/player.h
+++ b/player.h
@@ -1,3 +1,7 @@
+/*
+ * Copyright 2021-2022 (C) Mount0 Software, Harry Godden - All Rights Reserved
+ */
+
 #ifndef PLAYER_H
 #define PLAYER_H
 
@@ -13,7 +17,8 @@
  */
 
 static float 
-   k_walkspeed             = 2.0f,
+   k_walkspeed             = 7.0f,  /* no longer used */
+   k_runspeed              = 14.0f,
    k_board_radius          = 0.3f,
    k_board_length          = 0.45f,
    k_board_allowance       = 0.04f,
@@ -25,10 +30,14 @@ static float
    k_steer_ground          = 2.5f,
    k_steer_air             = 3.6f,
    k_steer_air_lerp        = 0.3f,
-   k_pump_force            = 000.0f,
+   k_pump_force            = 0.0f,
    k_downforce             = 5.0f,
    k_jump_charge_speed     = (1.0f/1.0f),
-   k_jump_force            = 5.0f;
+   k_jump_force            = 5.0f,
+   k_pitch_limit           = 1.5f,
+   k_look_speed            = 2.0f,
+   k_walk_accel            = 5.0f,
+   k_walk_friction         = 8.0f;
 
 static int freecam = 0;
 static int walk_grid_iterations = 1;
@@ -39,6 +48,10 @@ static struct gplayer
    /* Physics */
    rigidbody rb, collide_front, collide_back, rb_gate_frame;
 
+   /* TODO: eugh */
+   m3x3f gate_vr_frame, gate_vr_pstep_frame;
+   int on_board_frame, in_air_frame;
+
    v3f a, v_last, m, bob, vl;
 
    /* Utility */
@@ -53,7 +66,7 @@ static struct gplayer
    float pitch;
    float pushing, push_time;
    float jump;
-   int jump_charge;
+   int jump_charge, jump_dir;
    
    v3f land_target;
    v3f land_target_log[22];
@@ -81,11 +94,16 @@ static struct gplayer
          ffly,
          fpush,
          fairdir,
-         fsetup;
+         fsetup,
+         walk_timer,
+         fonboard;
+
+v3f last_step_pos;
+   int step_phase;
 }
 player = 
 {
-   .on_board = 1,
+   .on_board = 0,
 
    .collide_front = { .type = k_rb_shape_sphere, .inf.sphere.radius = 0.3f },
    .collide_back  = { .type = k_rb_shape_sphere, .inf.sphere.radius = 0.3f }
@@ -95,6 +113,11 @@ player =
  * Player API
  */
 
+static float *player_get_pos(void)
+{
+   return player.rb.co;
+}
+
 
 /*
  * Free camera movement
@@ -313,6 +336,10 @@ static void player_physics_control(void)
    if( vg_get_button( "jump" ) )
    {
       player.jump += ktimestep * k_jump_charge_speed;
+
+      if( !player.jump_charge )
+         player.jump_dir = player.reverse > 0.0f? 1: 0;
+
       player.jump_charge = 1;
    }
 
@@ -438,6 +465,114 @@ static void player_init(void)
    rb_init( &player.collide_back  );
 }
 
+static void player_walk_physics(void)
+{
+   rigidbody *rbf = &player.collide_front,
+             *rbb = &player.collide_back;
+
+   m3x3_copy( player.rb.to_world, player.collide_front.to_world );
+   m3x3_copy( player.rb.to_world, player.collide_back.to_world );
+   
+   float h0 = 0.3f,
+         h1 = 0.9f;
+
+   m4x3_mulv( player.rb.to_world, (v3f){0.0f,h0,0.0f}, rbf->co );
+   v3_copy( rbf->co, rbf->to_world[3] );
+   m4x3_mulv( player.rb.to_world, (v3f){0.0f,h1,0.0f}, 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, 0xff0000ff );
+   rb_debug( rbb, 0xff0000ff );
+
+   rb_ct manifold[64];
+   int len = 0;
+
+   len += rb_sphere_scene( rbf, &world.rb_geo, manifold+len );
+   len += rb_sphere_scene( rbb, &world.rb_geo, manifold+len );
+
+   rb_presolve_contacts( manifold, len );
+
+   for( int j=0; j<5; j++ )
+   {
+      for( int i=0; i<len; i++ )
+      {
+         struct contact *ct = &manifold[i];
+         
+         /*normal */
+         float vn = -v3_dot( player.rb.v, 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 );
+
+         v3_add( impulse, player.rb.v, player.rb.v );
+
+         /* friction */
+         for( int j=0; j<2; j++ )
+         {
+            float     f = k_friction * ct->norm_impulse,
+                     vt = v3_dot( player.rb.v, ct->t[j] ),
+                 lambda = -vt;
+            
+            float temp = ct->tangent_impulse[j];
+            ct->tangent_impulse[j] = vg_clampf( temp + lambda, -f, f );
+            lambda = ct->tangent_impulse[j] - temp;
+
+            v3_muladds( player.rb.v, ct->t[j], lambda, player.rb.v );
+         }
+      }
+   }
+
+   player.in_air = len==0?1:0;
+   
+   v3_zero( player.rb.w );
+   q_axis_angle( player.rb.q, (v3f){0.0f,1.0f,0.0f}, -player.angles[0] );
+
+   v3f forward_dir = { sinf(player.angles[0]),0.0f,-cosf(player.angles[0]) };
+
+   v3f p1;
+   v3_muladds( player.rb.co, forward_dir, 2.0f, p1 );
+   vg_line( player.rb.co, p1, 0xff0000ff );
+
+   float move_dead = 0.1f,
+         move = vg_get_axis("grabr")*0.5f + 0.5f - move_dead;
+
+   if( move > 0.0f )
+   {
+      float move_norm = move * (1.0f/(1.0f-move_dead)),
+            speed     = vg_lerpf( 0.1f*k_runspeed, k_runspeed, move_norm ),
+            amt       = k_walk_accel * ktimestep,
+            zvel      = v3_dot( player.rb.v, forward_dir ),
+            new_vel   = vg_minf( zvel + amt, speed ),
+            diff      = new_vel - vg_minf( zvel, speed );
+
+      v3_muladds( player.rb.v, forward_dir, diff, player.rb.v );
+
+      /* TODO move */
+      float walk_norm = 30.0f/(float)player.mdl.anim_walk->length,
+            run_norm  = 30.0f/(float)player.mdl.anim_run->length ;
+
+      player.walk_timer += ktimestep * vg_lerpf( walk_norm,run_norm,move_norm );
+   }
+   else
+   {
+      player.walk_timer = 0.0f;
+   }
+
+   player.rb.v[0] *= 1.0f - (ktimestep*k_walk_friction);
+   player.rb.v[2] *= 1.0f - (ktimestep*k_walk_friction);
+}
+
 static void player_physics(void)
 {
    /*
@@ -467,11 +602,11 @@ static void player_physics(void)
    rb_debug( rbf, 0xff00ffff );
    rb_debug( rbb, 0xffffff00 );
 
-   rb_ct manifold[24];
+   rb_ct manifold[64];
    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 );
+   len += rb_sphere_scene( rbf, &world.rb_geo, manifold+len );
+   len += rb_sphere_scene( rbb, &world.rb_geo, manifold+len );
 
    rb_presolve_contacts( manifold, len );
    v3f surface_avg = {0.0f, 0.0f, 0.0f};
@@ -581,10 +716,31 @@ static void player_physics(void)
 
       if( !player.jump_charge && player.jump > 0.2f )
       {
-         v3_muladds( player.rb.v, player.rb.up, k_jump_force*player.jump,
-                     player.rb.v );
+         v3f jumpdir;
+         
+         /* Launch more up if alignment is up else improve velocity */
+         float aup = fabsf(v3_dot( (v3f){0.0f,1.0f,0.0f}, player.rb.up )),
+               mod = 0.5f,
+               dir = mod + aup*(1.0f-mod);
+
+         v3_copy( player.rb.v, jumpdir );
+         v3_normalize( jumpdir );
+         v3_muls( jumpdir, 1.0f-dir, jumpdir );
+         v3_muladds( jumpdir, player.rb.up, dir, jumpdir );
+         v3_normalize( jumpdir );
+         
+         float force = k_jump_force*player.jump;
+         v3_muladds( player.rb.v, jumpdir, force, player.rb.v );
+         player.jump = 0.0f;
 
          player.jump_time = vg_time;
+
+         audio_lock();
+         audio_player_set_flags( &audio_player_extra, AUDIO_FLAG_SPACIAL_3D );
+         audio_player_set_position( &audio_player_extra, player.rb.co );
+         audio_player_set_vol( &audio_player_extra, 20.0f );
+         audio_player_playclip( &audio_player_extra, &audio_jumps[rand()%4] );
+         audio_unlock();
       }
    }
    else
@@ -605,7 +761,10 @@ static void player_do_motion(void)
    float horizontal = vg_get_axis("horizontal"),
          vertical = vg_get_axis("vertical");
 
-   player_physics();
+   if( player.on_board )
+      player_physics();
+   else
+      player_walk_physics();
    
    /* Integrate velocity */
    v3f prevco;
@@ -662,6 +821,25 @@ static void player_do_motion(void)
          
          world_routes_activate_gate( i );
          player.rb_gate_frame = player.rb;
+         player.in_air_frame = player.in_air;
+         player.on_board_frame = player.on_board;
+
+         if( !player.on_board )
+         {
+            v3f fwd_dir = {cosf(player.angles[0]),
+                           0.0f,
+                           sinf(player.angles[0])};
+            m3x3_mulv( gate->transport, fwd_dir, fwd_dir );
+
+            player.angles[0] = atan2f( fwd_dir[2], fwd_dir[0] );
+         }
+         
+         m3x3_copy( player.vr, player.gate_vr_frame );
+         m3x3_copy( player.vr_pstep, player.gate_vr_pstep_frame );
+
+         audio_lock();
+         audio_play_oneshot( &audio_gate_lap, 1.0f );
+         audio_unlock();
          break;
       }
    }
@@ -673,8 +851,53 @@ static void player_do_motion(void)
  * Animation
  */
 
+static void player_animate_offboard(void)
+{
+   mdl_keyframe apose[32], bpose[32];
+   struct skeleton *sk = &player.mdl.sk;
+
+   float walk_norm = (float)player.mdl.anim_walk->length/30.0f,
+         run_norm  = (float)player.mdl.anim_run->length/30.0f,
+         t = player.walk_timer,
+         l = vg_get_axis("grabr") * 0.5f + 0.5f;
+
+   skeleton_sample_anim( sk, player.mdl.anim_walk, t*walk_norm, apose );
+   skeleton_sample_anim( sk, player.mdl.anim_run,  t*run_norm, bpose );
+
+   skeleton_lerp_pose( sk, apose, bpose, l, apose );
+
+   float idle_walk = vg_minf( l * 10.0f, 1.0f);
+
+   skeleton_sample_anim( sk, player.mdl.anim_idle, vg_time*0.1f, bpose );
+   skeleton_lerp_pose( sk, apose, bpose, 1.0f-idle_walk, apose );
+
+   skeleton_apply_pose( &player.mdl.sk, apose, k_anim_apply_defer_ik );
+   skeleton_apply_ik_pass( &player.mdl.sk );
+   skeleton_apply_pose( &player.mdl.sk, apose, k_anim_apply_deffered_only );
+
+   v3_copy( player.mdl.sk.final_mtx[player.mdl.id_head-1][3], 
+               player.mdl.cam_pos );
+
+   skeleton_apply_inverses( &player.mdl.sk );
+
+   m4x3f mtx;
+   v4f rot;
+   q_axis_angle( rot, (v3f){0.0f,1.0f,0.0f}, -player.angles[0] - VG_PIf*0.5f );
+   q_m3x3( rot, mtx );
+   v3_copy( player.rb.to_world[3], mtx[3] );
+
+   skeleton_apply_transform( &player.mdl.sk, mtx );
+   skeleton_debug( &player.mdl.sk );
+}
+
 static void player_animate(void)
 {
+   if( !player.on_board )
+   {
+      player_animate_offboard();
+      return;
+   }
+
    /* Camera position */
    v3_sub( player.rb.v, player.v_last, player.a );
    v3_copy( player.rb.v, player.v_last );
@@ -713,7 +936,6 @@ static void player_animate(void)
 
    offset[0] = vg_clampf( offset[0], -0.8f, 0.8f );
    offset[1] = vg_clampf( offset[1], -0.5f, 0.0f );
-   offset[1] = 0.0f;
 
    /* 
     * Animation blending
@@ -774,15 +996,19 @@ static void player_animate(void)
       skeleton_lerp_pose( sk, apose, bpose, player.fpush, apose );
 
       /* trick setup */
-      float setup_frame = player.jump * (12.0f/30.0f),
+      float jump_start_frame = 14.0f/30.0f;
+      float setup_frame = player.jump * jump_start_frame,
             setup_blend = vg_minf( player.jump*5.0f, 1.0f );
       
-      float jump_frame = (vg_time - player.jump_time) + (12.0f/30.0f);
-      if( jump_frame >= (12.0f/30.0f) && jump_frame <= (40.0f/30.0f) )
+      float jump_frame = (vg_time - player.jump_time) + jump_start_frame;
+      if( jump_frame >= jump_start_frame && jump_frame <= (40.0f/30.0f) )
          setup_frame = jump_frame;
 
-      skeleton_sample_anim_clamped( sk, player.mdl.anim_ollie, 
-                                       setup_frame, bpose );
+      struct skeleton_anim *jump_anim = player.jump_dir?
+                                        player.mdl.anim_ollie:
+                                        player.mdl.anim_ollie_reverse;
+
+      skeleton_sample_anim_clamped( sk, jump_anim, setup_frame, bpose );
       skeleton_lerp_pose( sk, apose, bpose, setup_blend, ground_pose );
    }
    
@@ -793,22 +1019,35 @@ static void player_animate(void)
       
       float air_frame = (player.fairdir*0.5f+0.5f) * (15.0f/30.0f);
       
-      skeleton_sample_anim( sk, player.mdl.anim_air, air_frame, air_pose );
+      skeleton_sample_anim( sk, player.mdl.anim_air, air_frame, apose );
+
+      static v2f grab_choice;
+      v2_lerp( grab_choice, (v2f){ vg_get_axis("h1"), vg_get_axis("v1") },
+                            0.04f, grab_choice );
+
+      float ang = atan2f( grab_choice[0], grab_choice[1] ),
+            ang_unit = (ang+VG_PIf) * (1.0f/VG_TAUf),
+            grab_frame = ang_unit * (15.0f/30.0f);
+
+      skeleton_sample_anim( sk, player.mdl.anim_grabs, grab_frame, bpose );
+      skeleton_lerp_pose( sk, apose, bpose, player.grab, air_pose );
    }
 
    skeleton_lerp_pose( sk, ground_pose, air_pose, player.ffly, apose );
 
+   float add_grab_mod = 1.0f - player.ffly*player.grab;
+
    /* additive effects */
-   apose[player.mdl.id_hip-1].co[0] += offset[0];
-   apose[player.mdl.id_hip-1].co[2] += offset[2];
-   apose[player.mdl.id_ik_hand_l-1].co[0] += offset[0];
-   apose[player.mdl.id_ik_hand_l-1].co[2] += offset[2];
-   apose[player.mdl.id_ik_hand_r-1].co[0] += offset[0];
-   apose[player.mdl.id_ik_hand_r-1].co[2] += offset[2];
-   apose[player.mdl.id_ik_elbow_l-1].co[0] += offset[0];
-   apose[player.mdl.id_ik_elbow_l-1].co[2] += offset[2];
-   apose[player.mdl.id_ik_elbow_r-1].co[0] += offset[0];
-   apose[player.mdl.id_ik_elbow_r-1].co[2] += offset[2];
+   apose[player.mdl.id_hip-1].co[0] += offset[0]*add_grab_mod;
+   apose[player.mdl.id_hip-1].co[2] += offset[2]*add_grab_mod;
+   apose[player.mdl.id_ik_hand_l-1].co[0] += offset[0]*add_grab_mod;
+   apose[player.mdl.id_ik_hand_l-1].co[2] += offset[2]*add_grab_mod;
+   apose[player.mdl.id_ik_hand_r-1].co[0] += offset[0]*add_grab_mod;
+   apose[player.mdl.id_ik_hand_r-1].co[2] += offset[2]*add_grab_mod;
+   apose[player.mdl.id_ik_elbow_l-1].co[0] += offset[0]*add_grab_mod;
+   apose[player.mdl.id_ik_elbow_l-1].co[2] += offset[2]*add_grab_mod;
+   apose[player.mdl.id_ik_elbow_r-1].co[0] += offset[0]*add_grab_mod;
+   apose[player.mdl.id_ik_elbow_r-1].co[2] += offset[2]*add_grab_mod;
 
    skeleton_apply_pose( &player.mdl.sk, apose, k_anim_apply_defer_ik );
    skeleton_apply_ik_pass( &player.mdl.sk );
@@ -825,19 +1064,22 @@ static void player_animate(void)
 static void player_camera_update(void)
 {
    /* Update camera matrices */
-   m4x3_identity( player.camera );
-   m4x3_rotate_y( player.camera, -player.angles[0] );
-   m4x3_rotate_x( player.camera, -player.angles[1] );
+   v4f qyaw, qpitch, qcam;
+   q_axis_angle( qyaw, (v3f){ 0.0f, 1.0f, 0.0f }, -player.angles[0] ); 
+   q_axis_angle( qpitch, (v3f){ 1.0f, 0.0f, 0.0f }, -player.angles[1] ); 
+
+   q_mul( qyaw, qpitch, qcam );
+   q_m3x3( qcam, player.camera );
+
    v3_copy( player.camera_pos, player.camera[3] );
    m4x3_invert_affine( player.camera, player.camera_inverse );
 }
 
 static void player_animate_death_cam(void)
 {
-#if 0
    v3f delta;
    v3f head_pos;
-   v3_copy( player.mdl.ragdoll[k_chpart_head].co, head_pos );
+   v3_copy( player.mdl.ragdoll[0].rb.co, head_pos );
 
    v3_sub( head_pos, player.camera_pos, delta );
    v3_normalize( delta );
@@ -864,40 +1106,71 @@ static void player_animate_death_cam(void)
 
    player.angles[0] = atan2f( delta[0], -delta[2] ); 
    player.angles[1] = -asinf( delta[1] );
-#endif
 }
 
 static void player_animate_camera(void)
 {
    static v3f lerp_cam = {0.0f,0.0f,0.0f};
-   v3f offs = { -0.4f, 0.15f, 0.0f };
-
-   v3_lerp( lerp_cam, player.mdl.cam_pos, 0.8f, lerp_cam );
-   v3_add( lerp_cam, offs, offs );
-   m4x3_mulv( player.rb.to_world, offs, player.camera_pos );
-   
-   /* Look angles */
-   v3_lerp( player.vl, player.rb.v, 0.05f, player.vl );
+   v3f cam_pos;
 
-   float yaw = atan2f(  player.vl[0], -player.vl[2] ),
-       pitch = atan2f( -player.vl[1], 
-             sqrtf(
-               player.vl[0]*player.vl[0] + player.vl[2]*player.vl[2]
-             )) * 0.7f;
+   player.fonboard = vg_lerpf(player.fonboard, player.on_board, ktimestep*1.0f);
 
-   player.angles[0] = yaw;
-   player.angles[1] = pitch + 0.30f;
-
-   /* Camera shake */
-   static v2f shake_damp = {0.0f,0.0f};
-   v2f shake = { vg_randf()-0.5f, vg_randf()-0.5f };
-   v2_muls( shake, v3_length(player.rb.v)*0.3f 
-                        * (1.0f+fabsf(player.slip)), shake);
-
-   v2_lerp( shake_damp, shake, 0.01f, shake_damp );
-   shake_damp[0] *= 0.2f;
+   if( player.on_board )
+   {
+      v3f offs = { -0.4f, 0.15f, 0.0f };
+      v3_lerp( lerp_cam, player.mdl.cam_pos, 0.8f, lerp_cam );
+      v3_add( lerp_cam, offs, cam_pos );
+
+      /* Look angles */
+      v3_lerp( player.vl, player.rb.v, 0.05f, player.vl );
+
+      float yaw = atan2f(  player.vl[0], -player.vl[2] ),
+          pitch = atan2f( -player.vl[1], 
+                sqrtf(
+                  player.vl[0]*player.vl[0] + player.vl[2]*player.vl[2]
+                )) * 0.7f;
+
+      player.angles[0] = yaw;
+      player.angles[1] = vg_lerpf( player.angles[1], pitch + 0.30f, 
+                                                         player.fonboard );
+
+      /* Camera shake */
+      static v2f shake_damp = {0.0f,0.0f};
+      v2f shake = { vg_randf()-0.5f, vg_randf()-0.5f };
+      v2_muls( shake, v3_length(player.rb.v)*0.3f 
+                           * (1.0f+fabsf(player.slip)), shake);
+
+      v2_lerp( shake_damp, shake, 0.01f, shake_damp );
+      shake_damp[0] *= 0.2f;
+
+      v2_muladds( player.angles, shake_damp, 0.1f, player.angles );
+      m4x3_mulv( player.rb.to_world, cam_pos, player.camera_pos );
+   }
+   else
+   {
+      float speed = ktimestep * k_look_speed;
+      player.angles[0] += vg_get_axis( "horizontal" ) * speed;
+      player.angles[1] += vg_get_axis( "vertical" ) * speed;
+      
+      player.angles[1] = vg_clampf( player.angles[1], 
+                                       -k_pitch_limit, k_pitch_limit );
+      
+      float s =  sinf(player.angles[0]) * 0.2f,
+            c = -cosf(player.angles[0]) * 0.2f;
+      v3f forward_dir = { s,0.15f,c };
 
-   v2_muladds( player.angles, shake_damp, 0.1f, player.angles );
+      
+      m4x3f mtx;
+      v4f rot;
+      q_axis_angle( rot, (v3f){0.0f,1.0f,0.0f}, 
+                              -player.angles[0] -VG_PIf*0.5f );
+      q_m3x3( rot, mtx );
+      v3_copy( player.rb.to_world[3], mtx[3] );
+
+      m4x3_mulv( mtx, player.mdl.cam_pos, cam_pos );
+      v3_add( cam_pos, forward_dir, player.camera_pos );
+      v3_lerp( player.vl, player.rb.v, 0.3f, player.vl );
+   }
 }
 
 /* 
@@ -905,47 +1178,151 @@ static void player_animate_camera(void)
  */
 static void player_audio(void)
 {
-   float speed = vg_minf(v3_length( player.rb.v )*0.1f,1.0f),
-         attn = v3_dist( player.rb.co, player.camera[3] )+1.0f;
-   attn = (1.0f/(attn*attn)) * speed;
+   static int _ding = 0;
+   
+   int last = _ding;
+   _ding = glfwGetKey(vg_window, GLFW_KEY_C);
 
-   static float air = 0.0f;
-   air = vg_lerpf(air, player.in_air? 1.0f: 0.0f, 0.7f);
+   int trigger_ding = 0;
+   if( _ding && !last )
+      trigger_ding = 1;
+
+   static int _air = 0;
+
+   int l2 = _air;
+   _air = player.in_air;
+
+   static double last_revert = -2000.0;
+
+
+
+
+   audio_lock();
    
+   double revert_delta = vg_time - last_revert;
+   if( player.on_board && (!_air && l2) && (fabsf(player.slip) > 0.5f) && 
+         (revert_delta > 0.7) )
+   {
+      audio_player_set_position( &audio_player_extra, player.rb.co );
+      audio_player_set_flags( &audio_player_extra, AUDIO_FLAG_SPACIAL_3D );
+      audio_player_set_vol( &audio_player_extra, 2.0f );
+      audio_player_playclip( &audio_player_extra, &audio_lands[rand()%5] );
+
+      last_revert = vg_time;
+   }
+
+   static float air = 0.0f;
+   air = vg_lerpf(air, player.in_air? 1.0f: 0.0f, 5.0f*ktimestep);
+
+   /* Spacial info */
    v3f ears = { 1.0f,0.0f,0.0f };
    v3f delta;
 
+   float *cam = player.camera[3],
+         *pos = player.rb.co;
+   
+   if( trigger_ding )
+      audio_player_playclip( &audio_player_extra, &audio_ding );
+
+   audio_player_set_position( &audio_player0, player.rb.co );
+   audio_player_set_position( &audio_player1, player.rb.co );
+   audio_player_set_position( &audio_player2, player.rb.co );
+   audio_player_set_position( &audio_player_gate, world.render_gate_pos );
+
    v3_sub( player.rb.co, player.camera[3], delta );
    v3_normalize( delta );
    m3x3_mulv( player.camera, ears, ears );
 
-   float pan = v3_dot( ears, delta );
-   audio_player0.pan = pan;
-   audio_player1.pan = pan;
-   audio_player2.pan = pan;
+   /* TODO, Make function */
+   v3_copy( ears, vg_audio.listener_ears );
+   v3_copy( player.camera[3], vg_audio.listener_pos );
 
-   if( freecam )
+   /* Tunnel / occlusion */
+   audio_sample_occlusion( player.camera[3] );
+
+   int sprite_avail = -1;
+   for( int i=0; i<vg_list_size(ambient_sprites); i++ )
    {
-      audio_player0.vol = 0.0f;
-      audio_player1.vol = 0.0f;
-      audio_player2.vol = 0.0f;
+      if( !audio_player_is_playing( &ambient_sprites[i] ) )
+      {
+         sprite_avail = i;
+         break;
+      }
    }
-   else
+   
+   if( sprite_avail != -1 )
    {
-      if( player.is_dead )
+      v3f waterpos;
+      enum audio_sprite_type sprite_type = 
+         audio_sample_sprite_random( player.rb.co, waterpos );
+
+      if( sprite_type != k_audio_sprite_type_none )
       {
-         audio_player0.vol = 0.0f;
-         audio_player1.vol = 0.0f;
-         audio_player2.vol = 0.0f;
+         audio_player *avail = &ambient_sprites[ sprite_avail ];
+
+         audio_player_set_vol( avail, 20.0f );
+         audio_player_set_flags( avail, AUDIO_FLAG_SPACIAL_3D );
+         audio_player_set_position( avail, waterpos );
+
+         if( sprite_type == k_audio_sprite_type_grass )
+         {
+            audio_player_playclip( avail, &audio_grass[rand()%4] );
+         }
+         else if( sprite_type == k_audio_sprite_type_water )
+         {
+            audio_player_playclip( avail, &audio_water[rand()%6] );
+         }
       }
+   }
+   
+   if( freecam || player.is_dead || !player.on_board )
+   {
+      audio_player_set_vol( &audio_player0, 0.0f );
+      audio_player_set_vol( &audio_player1, 0.0f );
+      audio_player_set_vol( &audio_player2, 0.0f );
+
+      int walk_phase = 0;
+      if( vg_fractf(player.walk_timer) > 0.5f )
+         walk_phase = 1;
       else
+         walk_phase = 0;
+
+      if( (player.step_phase != walk_phase) && !player.in_air )
       {
-         float slide = vg_clampf( fabsf(player.slip), 0.0f, 1.0f );
-         audio_player0.vol = (1.0f-air)*attn*(1.0f-slide);
-         audio_player1.vol =       air *attn;
-         audio_player2.vol = (1.0f-air)*attn*slide;
+         v3_copy( player.rb.co, player.last_step_pos );
+
+         audio_player_set_flags( &audio_player_extra, AUDIO_FLAG_SPACIAL_3D );
+         audio_player_set_position( &audio_player_extra, player.rb.co );
+         audio_player_set_vol( &audio_player_extra, 6.0f );
+         audio_player_playclip( &audio_player_extra, 
+                                          &audio_footsteps[rand()%4] );
       }
+
+      player.step_phase = walk_phase;
+   }
+   else
+   {
+      /* Composite */
+      float speed = vg_minf(v3_length( player.rb.v )*0.1f,1.0f),
+            attn  = speed,
+            slide = vg_clampf( fabsf(player.slip), 0.0f, 1.0f ),
+            vol0  = (1.0f-air)*attn*(1.0f-slide),
+            vol1  =       air *attn,
+            vol2  = (1.0f-air)*attn*slide;
+      
+      audio_player_set_vol( &audio_player0, vol0 );
+      audio_player_set_vol( &audio_player1, vol1 );
+      audio_player_set_vol( &audio_player2, vol2 );
+
+      float reverb_amt = vol0 * audio_occlusion_current * 0.5f;
+      audio_player_set_pan( &audio_player3, 0.0f );
+      audio_player_set_vol( &audio_player3, reverb_amt );
    }
+   
+#if 0
+   world_audio_update( cam, ears );
+#endif
+   audio_unlock();
 }
 
 /*
@@ -1017,8 +1394,12 @@ static int reset_player( int argc, char const *argv[] )
    
    rb_update_transform( &player.rb );
    m3x3_mulv( player.rb.to_world, (v3f){ 0.0f, 0.0f, -1.2f }, player.rb.v );
+   m3x3_identity( player.gate_vr_frame );
+   m3x3_identity( player.gate_vr_pstep_frame );
 
    player.rb_gate_frame = player.rb;
+   player.on_board_frame = player.on_board;
+   player.in_air_frame = player.in_air;
    return 1;
 }
 
@@ -1031,8 +1412,20 @@ static void player_update(void)
    if( vg_get_axis("grabl")>0.0f)
    {
       player.rb = player.rb_gate_frame;
+      player.on_board = player.on_board_frame;
+      player.in_air = player.in_air_frame;
+      m3x3_copy( player.gate_vr_frame, player.vr );
+      m3x3_copy( player.gate_vr_pstep_frame, player.vr_pstep );
       player.is_dead = 0;
       player.in_air = 1;
+
+
+      if( !player.on_board )
+      {
+         player.angles[0] = atan2f( -player.rb.forward[2], 
+                                    -player.rb.forward[0] );
+      }
+
       m3x3_identity( player.vr );
 
       player.mdl.shoes[0] = 1;
@@ -1046,7 +1439,8 @@ static void player_update(void)
       player.on_board ^= 0x1;
    }
 
-   if( glfwGetKey( vg_window, GLFW_KEY_O ) )
+   if( (glfwGetKey( vg_window, GLFW_KEY_O ) || (player.rb.co[1] < 0.0f)) &&
+       !player.is_dead)
    {
       character_ragdoll_copypose( &player.mdl, player.rb.v );
       player.is_dead = 1;
@@ -1062,14 +1456,11 @@ static void player_update(void)
    }
    else
    {
-      if( player.on_board )
-      {
-         player_do_motion();
-         player_animate();
+      player_do_motion();
+      player_animate();
 
-         if( !freecam )
-            player_animate_camera();
-      }
+      if( !freecam )
+         player_animate_camera();
    }
 
    if( freecam )
@@ -1081,21 +1472,8 @@ static void player_update(void)
 
 static void draw_player(void)
 {
-   /* Draw */
-#if 0
-   m4x3_copy( player.rb.to_world, player.mdl.mroot );
-
    if( player.is_dead )
       character_mimic_ragdoll( &player.mdl );
-   else
-      character_eval( &player.mdl );
-
-   float opacity = 1.0f-player.air_blend;
-   if( player.is_dead )
-      opacity = 0.0f;
-
-   character_draw( &player.mdl, opacity, player.camera );
-#endif
 
    shader_viewchar_use();
    vg_tex2d_bind( &tex_characters, 0 );