preliminary compressed audio
[carveJwlIkooP6JGAAIwe30JlM.git] / player.h
index 726d62483c5788bd3e713ad64fa9a355c7a1ff44..c3ff3e97511e2c99386161c8c64fe7bf71f1a9a0 100644 (file)
--- a/player.h
+++ b/player.h
@@ -13,7 +13,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 +26,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 +44,9 @@ static struct gplayer
    /* Physics */
    rigidbody rb, collide_front, collide_back, rb_gate_frame;
 
+   /* TODO: eugh */
+   m3x3f gate_vr_frame, gate_vr_pstep_frame;
+
    v3f a, v_last, m, bob, vl;
 
    /* Utility */
@@ -53,7 +61,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 +89,13 @@ static struct gplayer
          ffly,
          fpush,
          fairdir,
-         fsetup;
+         fsetup,
+         walk_timer,
+         fonboard;
 }
 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 }
@@ -313,6 +323,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 +452,112 @@ 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 );
+         }
+      }
+   }
+   
+   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 = (float)player.mdl.anim_walk->length / 30.0f,
+            run_norm  = (float)player.mdl.anim_run->length  / 30.0f;
+
+      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,7 +587,7 @@ 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_scene( rbf, &world.rb_geo, manifold+len );
@@ -581,8 +701,22 @@ 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;
       }
@@ -605,7 +739,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 +799,9 @@ static void player_do_motion(void)
          
          world_routes_activate_gate( i );
          player.rb_gate_frame = player.rb;
+
+         m3x3_copy( player.vr, player.gate_vr_frame );
+         m3x3_copy( player.vr_pstep, player.gate_vr_pstep_frame );
          break;
       }
    }
@@ -673,8 +813,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 = 30.0f/(float)player.mdl.anim_walk->length,
+         run_norm  = 30.0f/(float)player.mdl.anim_run->length,
+         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 );
@@ -774,15 +959,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 +982,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 = 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 );
@@ -868,34 +1070,66 @@ static void player_animate_death_cam(void)
 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 );
-
-   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;
+   v3f cam_pos;
 
-   player.angles[0] = yaw;
-   player.angles[1] = pitch + 0.30f;
+   player.fonboard = vg_lerpf(player.fonboard, player.on_board, ktimestep*1.0f);
 
-   /* 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 );
+   }
 }
 
 /* 
@@ -903,47 +1137,62 @@ 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;
+   audio_lock();
 
    static float air = 0.0f;
-   air = vg_lerpf(air, player.in_air? 1.0f: 0.0f, 0.7f);
-   
+   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;
+
+   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 );
+
    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] );
+   
+   if( freecam || player.is_dead || !player.on_board )
    {
-      audio_player0.vol = 0.0f;
-      audio_player1.vol = 0.0f;
-      audio_player2.vol = 0.0f;
+      audio_player_set_vol( &audio_player0, 0.0f );
+      audio_player_set_vol( &audio_player1, 0.0f );
+      audio_player_set_vol( &audio_player2, 0.0f );
    }
    else
    {
-      if( player.is_dead )
-      {
-         audio_player0.vol = 0.0f;
-         audio_player1.vol = 0.0f;
-         audio_player2.vol = 0.0f;
-      }
-      else
-      {
-         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;
-      }
+      /* 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 * 1.0f;
+      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();
 }
 
 /*
@@ -1015,6 +1264,8 @@ 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;
    return 1;
@@ -1029,6 +1280,8 @@ static void player_update(void)
    if( vg_get_axis("grabl")>0.0f)
    {
       player.rb = player.rb_gate_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;
       m3x3_identity( player.vr );
@@ -1044,7 +1297,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;
@@ -1060,14 +1314,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 )