start of a replay system
authorhgn <hgodden00@gmail.com>
Fri, 7 Jul 2023 05:22:18 +0000 (06:22 +0100)
committerhgn <hgodden00@gmail.com>
Fri, 7 Jul 2023 05:22:18 +0000 (06:22 +0100)
player.c
player.h
player_api.h
player_render.c
player_replay.c [new file with mode: 0644]
player_replay.h [new file with mode: 0644]
skaterift.c
skeleton.h

index 52e56838b8986b815f748163ca84e62890fffd19..33b1c1e9b8cc47cf951d4499d371f9c2481f7871 100644 (file)
--- a/player.c
+++ b/player.c
@@ -40,7 +40,7 @@ VG_STATIC void player_init(void)
    VG_VAR_F32( k_cam_punch );
    VG_VAR_F32( k_cam_shake_strength );
    VG_VAR_F32( k_cam_shake_trackspeed );
-   VG_VAR_I32( k_player_debug_info );
+   VG_VAR_I32( k_player_debug_info, flags=VG_VAR_PERSISTENT );
 
    vg_console_reg_var( "cinema", &k_cinema, k_var_dtype_f32, 0 );
    vg_console_reg_var( "cinema_fixed", &k_cinema_fixed, k_var_dtype_i32, 0 );
@@ -327,6 +327,8 @@ PLAYER_API void player__im_gui( player_instance *player ){
 
    if( _player_im_gui[ player->subsystem ] )
       _player_im_gui[ player->subsystem ]( player );
+
+   replay_debug_info( player );
 }
 
 VG_STATIC void global_skateshop_exit(void);
@@ -375,5 +377,6 @@ PLAYER_API void player__kill( player_instance *player ){
 #include "player_drive.c"
 #include "player_render.c"
 #include "player_ragdoll.c"
+#include "player_replay.c"
 
 #endif /* PLAYER_C */
index 7484ce94610c1100a030fe03cc4f2c3399fdef5e..77c63db3ae355ed68b7006fd4d5ca1ea69f5853a 100644 (file)
--- a/player.h
+++ b/player.h
@@ -1,6 +1,13 @@
 #ifndef PLAYER_H
 #define PLAYER_H
 
+enum player_subsystem{
+   k_player_subsystem_walk = 0,
+   k_player_subsystem_skate = 1,
+   k_player_subsystem_dead = 2,
+   k_player_subsystem_drive = 3
+};
+
 #include "player_ragdoll.h"
 #include "player_render.h"
 #include "player_model.h"
@@ -9,6 +16,7 @@
 #include "player_skate.h"
 #include "player_dead.h"
 #include "player_drive.h"
+#include "player_replay.h"
 
 #define PLAYER_REWIND_FRAMES 60*4
 #define RESET_MAX_TIME 45.0
@@ -85,7 +93,7 @@ struct player_instance{
    struct board_pose      board_pose;
 
    /*
-    * Rewind
+    * Rewind (OLD SYSTEM)
     * ----------------------------------------------------
     */
    int rewinding, rewind_sound_wait;
@@ -103,17 +111,18 @@ struct player_instance{
          dist_accum;
    double rewind_start, rewind_time;
 
+   /* 
+    * Replay
+    * -------------------------------------------------
+    */
+   replay_buffer replay;
+
    /*
     * Subsystems
     * -------------------------------------------------
     */
 
-   enum player_subsystem{
-      k_player_subsystem_walk = 0,
-      k_player_subsystem_skate = 1,
-      k_player_subsystem_dead = 2,
-      k_player_subsystem_drive = 3
-   }
+   enum player_subsystem
    subsystem,
    subsystem_gate;
 
@@ -210,6 +219,8 @@ void( *_player_post_animate[])( player_instance *player ) =
    player__drive_post_animate
 };
 
+
+__attribute__((deprecated))
 VG_STATIC
 void( *_player_restore[] )( player_instance *player ) =
 {
@@ -219,6 +230,25 @@ void( *_player_restore[] )( player_instance *player ) =
    NULL
 };
 
+VG_STATIC
+void( *_player_store_state[] )( player_instance *player ) = 
+{
+   NULL,
+   NULL,
+   NULL,
+   NULL
+};
+
+VG_STATIC
+void( *_player_load_state_lerp[] )( player_instance *player, 
+                                    void *A, void *B, f32 t ) =
+{
+   NULL,
+   NULL,
+   NULL,
+   NULL
+};
+
 PLAYER_API void player__debugtext( int size, const char *fmt, ... );
 PLAYER_API void player__create( player_instance *inst );
 PLAYER_API void player__use_avatar( player_instance *player, 
index ad9e4ca496a0043ed229ebd2b1678fc33fa76c4e..a009d6ddd4201e0594e957370109f77ffff3bab1 100644 (file)
@@ -10,11 +10,16 @@ typedef struct player_instance player_instance;
 typedef mdl_keyframe player_pose[32];
 typedef struct player_animation player_animation;
 
-struct player_animation
-{
+struct player_animation{
    player_pose pose;
    v3f         root_co;
    v4f         root_q;
+
+   enum player_animation_type {
+      k_player_animation_type_fk,      /* regular FK animation */
+      k_player_animation_type_absolute /* decomposition of the final matrices */
+   }
+   type;
 };
 
 #endif /* PLAYER_API_H */
index ce3fa5a6a527463c99aada84fc8ee1735fd0a6be..d687370573016ce3a98c4a3495f5dc75827edb63 100644 (file)
@@ -141,6 +141,8 @@ VG_STATIC void player__pre_render( player_instance *player )
 {
    if( _player_animate[ player->subsystem ] ){
       player_animation res;
+      res.type = k_player_animation_type_fk;
+
       _player_animate[ player->subsystem ]( player, &res );
 
       m4x3f transform;
@@ -155,11 +157,16 @@ VG_STATIC void player__pre_render( player_instance *player )
          player->holdout_time -= vg.time_frame_delta * 2.0f;
       }
 
-      skeleton_apply_pose( sk, res.pose, k_anim_apply_defer_ik );
-      skeleton_apply_ik_pass( sk );
-      skeleton_apply_pose( sk, res.pose, k_anim_apply_deffered_only );
-      skeleton_apply_inverses( sk );
-      skeleton_apply_transform( sk, transform );
+      if( res.type == k_player_animation_type_fk ){
+         skeleton_apply_pose( sk, res.pose, k_anim_apply_defer_ik );
+         skeleton_apply_ik_pass( sk );
+         skeleton_apply_pose( sk, res.pose, k_anim_apply_deffered_only );
+         skeleton_apply_inverses( sk );
+         skeleton_apply_transform( sk, transform );
+      }
+      else {
+         skeleton_apply_pose( sk, res.pose, k_anim_apply_absolute );
+      }
 
       skeleton_debug( sk );
    }
@@ -192,6 +199,7 @@ VG_STATIC void player__pre_render( player_instance *player )
       return;
    }
 
+#if 0
    if( player->rewinding ){
       if( player->rewind_time <= 0.0f ){
          double taken = vg.time - player->rewind_start;
@@ -287,8 +295,58 @@ VG_STATIC void player__pre_render( player_instance *player )
       }
    }
    else player->cam_override_strength = 0.0f;
+#else
+   player->cam_override_strength = 0.0f;
+#endif
 
    player__cam_iterate( player );
+
+   /* dev playbacker */
+   if( k_replay_test ){
+      f64 speed = 1.0;
+      f64 target = player->replay.cursor;
+
+      if( vg_getkey( SDLK_9 ) )
+         target -= vg.time_frame_delta * speed;
+      if( vg_getkey( SDLK_0 ) )
+         target += vg.time_frame_delta * speed;
+
+      replay_seek( &player->replay, target );
+
+      player_animation res;
+      replay_frame *frame = player->replay.cursor_frame;
+
+      if( frame ){
+         memcpy( &res, &frame->anim, sizeof(frame->anim) );
+         memcpy( &frame->board_pose, &player->board_pose, 
+                 sizeof(player->board_pose) );
+      }
+      else return;
+
+      struct skeleton *sk = &player->playeravatar->sk;
+      skeleton_apply_pose( sk, res.pose, k_anim_apply_absolute );
+   }
+   else {
+      /* replay recorder */
+      replay_buffer *replay = &player->replay;
+      f64 delta = vg.time - replay->cursor;
+      if( delta > (1.0/30.0) ){
+         replay->cursor = vg.time;
+         replay_frame *frame = replay_newframe( replay );
+
+         player_animation *res = &frame->anim;
+         v3_zero( res->root_co );
+         q_identity( res->root_q );
+         res->type = k_player_animation_type_absolute;
+
+         struct skeleton *sk = &player->playeravatar->sk;
+         skeleton_decompose_mtx_absolute( sk, res->pose );
+
+         memcpy( &frame->board_pose, &player->board_pose, 
+                  sizeof(player->board_pose) );
+         frame->time = vg.time;
+      }
+   }
 }
 
 VG_STATIC void render_board( camera *cam, world_instance *world,
diff --git a/player_replay.c b/player_replay.c
new file mode 100644 (file)
index 0000000..83b9b83
--- /dev/null
@@ -0,0 +1,171 @@
+#ifndef PLAYER_REPLAY_C
+#define PLAYER_REPLAY_C
+
+#include "player_replay.h"
+
+VG_STATIC void local_replay_init( u32 bytes ){
+   localplayer.replay.data = vg_linear_alloc( vg_mem.rtmemory, bytes );
+   localplayer.replay.size = bytes;
+   localplayer.replay.cursor = -9999.9;
+   vg_console_reg_var( "k_replay_test", &k_replay_test, k_var_dtype_i32, 0 );
+}
+
+u32 replay_frame_size( replay_frame *frame ){
+   /* eventually it will be a dynamic size */
+   return vg_align8( sizeof( replay_frame ) );
+}
+
+VG_STATIC replay_frame *replay_newframe( replay_buffer *replay ){
+   replay_frame *frame = NULL;
+   if( replay->head ){
+      assert( replay->head );
+
+      u32 headsize = replay_frame_size( replay->head ),
+          nextpos  = ((void *)replay->head - replay->data) + headsize,
+          nextsize = vg_align8( sizeof(replay_frame) );
+
+      if( nextsize > replay->size ){
+         vg_error( "Keyframe too big\n" );
+         return NULL;
+      }
+
+      if( nextpos + nextsize > replay->size )
+         nextpos = 0;
+
+check_again:;
+
+      u32 tailpos = (void *)replay->tail - replay->data;
+
+      if( tailpos >= nextpos ){
+         if( nextpos + nextsize > tailpos ){
+            if( replay->cursor_frame == replay->tail ){
+               replay->cursor_frame = NULL;
+            }
+
+            replay->tail = replay->tail->r;
+
+            if( replay->tail ) {
+               replay->tail->l = NULL;
+               goto check_again;
+            }
+            else{
+               replay->head = NULL;
+            }
+         }
+      }
+
+      frame = replay->data + nextpos;
+
+      if( replay->head ){
+         replay->head->r = frame;
+      }
+   }
+   else {
+      frame = replay->data;
+   }
+
+   frame->l = replay->head;
+   frame->r = NULL;
+   replay->head = frame;
+   if( !replay->tail ) replay->tail = frame;
+
+   return frame;
+}
+
+VG_STATIC void replay_seek( replay_buffer *replay, f64 t ){
+   if( !replay->head ) return;
+   assert( replay->tail );
+
+   if( t < replay->tail->time ) t = replay->tail->time;
+   if( t > replay->head->time ) t = replay->head->time;
+
+   if( !replay->cursor_frame ) {
+      replay->cursor = replay->head->time;
+      replay->cursor_frame = replay->head;
+
+      if( fabs(replay->head->time-t) > fabs(replay->tail->time-t) ){
+         replay->cursor = replay->tail->time;
+         replay->cursor_frame = replay->tail;
+      }
+   }
+
+   f64 dir = t - replay->cursor;
+   if( dir == 0.0 ) return;
+   dir = vg_signf( dir );
+   
+   u32 i=0;
+   for( ; i<100; i++ ){
+      if( dir < 0.0 )
+         if( t > replay->cursor_frame->time ) break;
+
+      replay_frame *next;
+      if( dir > 0.0 ) next = replay->cursor_frame->r;
+      else            next = replay->cursor_frame->l;
+
+      if( !next ) break;
+
+      if( dir > 0.0 )
+         if( t < next->time ) break;
+
+      replay->cursor_frame = next;
+   }
+
+   replay->cursor = t;
+}
+
+VG_STATIC void replay_debug_info( player_instance *player ){
+   player__debugtext( 2, "replay info" );
+
+   replay_buffer *replay = &player->replay;
+
+   u32 head = 0,
+       tail = 0;
+   if( replay->tail ) tail = (void *)replay->tail - replay->data;
+   if( replay->head ) head = (void *)replay->head - replay->data;
+
+   player__debugtext( 1, "head @%u | tail @%u\n", head, tail );
+
+   f64 start = replay->cursor,
+       end   = replay->cursor;
+   if( replay->tail ) start = replay->tail->time;
+   if( replay->head ) end = replay->head->time;
+
+   f64 cur = replay->cursor - start,
+       len = end - start;
+
+   player__debugtext( 1, "cursor: %.2fs / %.2fs\n", cur, len );
+}
+
+VG_STATIC void replay_imgui( player_instance *player ){
+   if( !k_replay_test ) return;
+
+   replay_buffer *replay = &player->replay;
+   f64 start = replay->cursor,
+       end   = replay->cursor;
+   if( replay->tail ) start = replay->tail->time;
+   if( replay->head ) end = replay->head->time;
+   f64 len = end - start,
+       cur = (replay->cursor - start) / len;
+
+   char buffer[ 128 ];
+
+   ui_px height = 20,
+         cwidth = 8;
+   ui_rect bar = { 0, vg.window_y - height, vg.window_x, height };
+   ui_fill( bar, ui_colour( k_ui_bg ) );
+
+   ui_rect cusor = { cur * (f64)vg.window_x - (cwidth/2), bar[1], 
+                     cwidth, bar[3] };
+   ui_fill( cusor, ui_colour( k_ui_bg+4 ) );
+
+   cusor[1] -= height;
+   cusor[2] = 200;
+   snprintf( buffer, 128, "-%.2fs\n", (end-replay->cursor) );
+   ui_text( cusor, buffer, 1, k_ui_align_middle_left, 0 );
+
+   snprintf( buffer, 128, "-%.2fs\n", len );
+   ui_text( bar, buffer, 1, k_ui_align_middle_left, 0 );
+   ui_text( bar, "0s", 1, k_ui_align_middle_right, 0 );
+}
+
+#endif /* PLAYER_REPLAY_C */
diff --git a/player_replay.h b/player_replay.h
new file mode 100644 (file)
index 0000000..1befaad
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef PLAYER_REPLAY_H
+#define PLAYER_REPLAY_H
+
+#include "skaterift.h"
+#include "player.h"
+#include "player_render.h"
+
+static i32 k_replay_test = 0;
+
+typedef struct replay_buffer replay_buffer;
+typedef struct replay_frame replay_frame;
+
+struct replay_buffer {
+   void *data;
+   u32 size; /* bytes */
+
+   replay_frame *head, *tail, *cursor_frame;
+
+   f64 cursor;
+};
+
+struct replay_frame {
+   player_animation anim;
+   struct board_pose board_pose;
+
+   f64 time;
+   replay_frame *l, *r;
+
+   /* eventually, sound events, player iframes and stuff? */
+};
+
+VG_STATIC void replay_debug_info( player_instance *player );
+VG_STATIC replay_frame *replay_newframe( replay_buffer *replay );
+VG_STATIC void replay_imgui( player_instance *player );
+VG_STATIC void replay_seek( replay_buffer *replay, f64 t );
+
+#endif /* PLAYER_REPLAY_H */
index 175291f06178545609e0222484772f79f20f9de8..cbefd25249a7be24b4e04f62419487458a35eefc 100644 (file)
@@ -175,6 +175,7 @@ VG_STATIC void vg_load(void)
    vg_loader_step( load_playermodels, NULL );
   
    /* player setup */
+   local_replay_init( (1024*1024*1)/2 );
    player__create( &localplayer );
    player_avatar_load( &localplayer_avatar, "models/ch_none.mdl" );
    player__use_avatar( &localplayer, &localplayer_avatar );
@@ -508,6 +509,7 @@ VG_STATIC void vg_gui(void)
    player__im_gui( &localplayer );
    world_instance *world = world_current_instance();
 
+   replay_imgui( &localplayer );
    workshop_form_gui();
    render_view_framebuffer_ui();
 }
index 052975239bd6988290a2c731b8d4d4565fdc4b7a..dc506abdde0df3b0908dcbddc4c5ccd7e6712d88 100644 (file)
@@ -165,7 +165,8 @@ typedef enum anim_apply
 {
    k_anim_apply_always,
    k_anim_apply_defer_ik,
-   k_anim_apply_deffered_only
+   k_anim_apply_deffered_only,
+   k_anim_apply_absolute
 }
 anim_apply;
 
@@ -203,6 +204,18 @@ int should_apply_bone( struct skeleton *skele, u32 id, anim_apply type )
 VG_STATIC void skeleton_apply_pose( struct skeleton *skele, mdl_keyframe *pose,
                                     anim_apply passtype )
 {
+   if( passtype == k_anim_apply_absolute ){
+      for( u32 i=1; i<skele->bone_count; i++ ){
+         mdl_keyframe *kf = &pose[i-1];
+
+         v3f *posemtx = skele->final_mtx[i];
+
+         q_m3x3( kf->q, posemtx );
+         v3_copy( kf->co, posemtx[3] );
+      }
+      return;
+   }
+
    m4x3_identity( skele->final_mtx[0] );
    skele->bones[0].defer = 0;
    skele->bones[0].flags &= ~k_bone_flag_ik;
@@ -233,6 +246,18 @@ VG_STATIC void skeleton_apply_pose( struct skeleton *skele, mdl_keyframe *pose,
    }
 }
 
+/* 
+ * Take the final matrices and decompose it into an absolute positioned anim
+ */
+VG_STATIC void skeleton_decompose_mtx_absolute( struct skeleton *skele, 
+                                                mdl_keyframe *anim ){
+   for( u32 i=1; i<skele->bone_count; i++ ){
+      struct skeleton_bone *sb = &skele->bones[i];
+      mdl_keyframe *kf = &anim[i-1];
+      m4x3_decompose( skele->final_mtx[i], kf->co, kf->q, kf->s );
+   }
+}
+
 /* 
  * creates the reference inverse matrix for an IK bone, as it has an initial 
  * intrisic rotation based on the direction that the IK is setup..