rewinds
[carveJwlIkooP6JGAAIwe30JlM.git] / player.h
index c9502a32493d780a6504663af4423c78a3a38d63..e4814d3400d1b2066e24f769638b352dafc62ae8 100644 (file)
--- a/player.h
+++ b/player.h
@@ -5,6 +5,8 @@
 #ifndef PLAYER_H
 #define PLAYER_H
 
+#define PLAYER_REWIND_FRAMES 60*4
+
 #include "audio.h"
 #include "common.h"
 #include "world.h"
@@ -74,7 +76,7 @@ static struct gplayer
    m4x3f visual_transform,
          inv_visual_transform;
 
-   int is_dead, death_tick_allowance;
+   int is_dead, death_tick_allowance, rewinding;
 
    v3f land_target;
    v3f land_target_log[22];
@@ -90,6 +92,17 @@ static struct gplayer
    v3f camera_pos, smooth_localcam;
    v2f angles;
 
+   struct rewind_frame
+   {
+      v3f pos;
+      v2f ang;
+   }
+   *rewind_buffer;
+   u32 rewind_incrementer,
+       rewind_length;
+
+   float rewind_time;
+
    /* animation */
    double jump_time;
    float fslide,
@@ -163,6 +176,7 @@ static void player_kill(void);
 static float *player_cam_pos(void);
 static void player_save_frame(void);
 static void player_restore_frame(void);
+static void player_save_rewind_frame(void);
 
 /* 
  * Submodules
@@ -230,17 +244,44 @@ static void player_init(void)                                            /* 1 */
                .function = reset_player
        });
 
+   player.rewind_length = 0;
+   player.rewind_buffer = vg_alloc( sizeof(struct rewind_frame) 
+                                       * PLAYER_REWIND_FRAMES );
+
    /* other systems */
    vg_loader_highwater( player_model_init, player_model_free, NULL );
 }
 
+static void player_save_rewind_frame(void)
+{
+   if( player.rewind_length < PLAYER_REWIND_FRAMES )
+   {
+      struct rewind_frame *fr = 
+         &player.rewind_buffer[ player.rewind_length ++ ];
+
+      v2_copy( player.angles, fr->ang );
+      v3_copy( player.camera_pos, fr->pos );
+
+      player.rewind_incrementer = 0;
+   }
+}
+
 /* Deal with input etc */
 static void player_update_pre(void)
 {
    struct player_phys *phys = &player.phys;
 
+   if( player.rewinding )
+   {
+      return;
+   }
+
    if( vg_get_button_down( "reset" ) )
    {
+      player.rewinding = 1;
+      player.rewind_time = (float)player.rewind_length - 0.0001f;
+      player_save_rewind_frame();
+
       player.is_dead = 0;
       player.death_tick_allowance = 30;
       player_restore_frame();
@@ -255,6 +296,9 @@ static void player_update_pre(void)
       player.mdl.shoes[1] = 1;
 
       world_routes_notify_reset();
+
+      /* apply 1 frame of movement */
+      player_do_motion();
    }
 
    if( vg_get_button_down( "switchmode" ) )
@@ -270,6 +314,9 @@ static void player_update_pre(void)
 
 static void player_update_fixed(void)                                    /* 2 */
 {
+   if( player.rewinding )
+      return;
+
    if( player.death_tick_allowance )
       player.death_tick_allowance --;
 
@@ -281,6 +328,13 @@ static void player_update_fixed(void)                                    /* 2 */
    }
    else
    {
+      player.rewind_incrementer ++;
+
+      if( player.rewind_incrementer > (u32)(0.25/VG_TIMESTEP_FIXED) )
+      {
+         player_save_rewind_frame();
+      }
+
       player_do_motion();
    }
 }
@@ -309,11 +363,79 @@ static void player_update_post(void)
    if( freecam )
       player_freecam();
 
+
    /* CAMERA POSITIONING: LAYER 0 */
    v2_copy( player.angles, camera_angles );
    v3_copy( player.camera_pos, camera_pos );
-   camera_update();
 
+   if( player.rewinding )
+   {
+      if( player.rewind_time <= 0.0f )
+      {
+         player.rewinding = 0;
+         player.rewind_length = 1;
+      }
+      else
+      {
+         assert( player.rewind_length > 0 );
+
+         v2f override_angles;
+         v3f override_pos;
+
+         float budget         = vg.time_delta,
+               overall_length = player.rewind_length*0.25f;
+
+         for( int i=0; (i<10)&&(player.rewind_time>0.0f)&&(budget>0.0f); i ++ )
+         {
+            /* Interpolate frames */
+            int i0 = floorf( player.rewind_time ),
+                i1 = VG_MIN( i0+1, player.rewind_length-1 );
+            
+            struct rewind_frame *fr =  &player.rewind_buffer[i0],
+                                *fr1 = &player.rewind_buffer[i1];
+
+            float dist = vg_maxf( v3_dist( fr->pos, fr1->pos ), 0.001f ),
+                  subl = vg_fractf( player.rewind_time ) + 0.001f,
+                  
+#if 0
+                  speed=sqrtf(player.rewind_time*player.rewind_time+11.0f)*3.0f,
+#else
+                  speed = (3.0f-(1.0f/(0.4f+0.4f*player.rewind_time)))*28.0f,
+#endif
+                  mod  = speed * (budget / dist),
+
+                  advl = vg_minf( mod, subl ),
+                  advt = (advl / mod) * budget;
+
+            player.rewind_time -= advl;
+            budget -= advt;
+         }
+
+         player.rewind_time = vg_maxf( 0.0f, player.rewind_time );
+         
+         int i0 = floorf( player.rewind_time ),
+             i1 = VG_MIN( i0+1, player.rewind_length-1 );
+         
+         struct rewind_frame *fr =  &player.rewind_buffer[i0],
+                             *fr1 = &player.rewind_buffer[i1];
+         
+         float sub = vg_fractf(player.rewind_time);
+
+         v3_lerp( fr->pos, fr1->pos, sub, override_pos );
+         override_angles[0] = vg_alerpf( fr->ang[0], fr1->ang[0], sub );
+         override_angles[1] = vg_lerpf ( fr->ang[1], fr1->ang[1], sub );
+
+         /* CAMERA POSITIONING: LAYER 1 */
+         float blend = (4.0f-player.rewind_time) * 0.25f,
+               c     = vg_clampf( blend, 0.0f, 1.0f );
+
+         camera_angles[0] = vg_alerpf(override_angles[0], player.angles[0], c);
+         camera_angles[1] = vg_lerpf (override_angles[1], player.angles[1], c);
+         v3_lerp( override_pos, player.camera_pos, c, camera_pos );
+      }
+   }
+
+   camera_update();
    player_audio();
 }