From b615f03b922d155f9449d2a23d3cc18eed320ed3 Mon Sep 17 00:00:00 2001 From: hgn Date: Fri, 7 Jul 2023 06:22:18 +0100 Subject: [PATCH] start of a replay system --- player.c | 5 +- player.h | 44 +++++++++++-- player_api.h | 9 ++- player_render.c | 68 +++++++++++++++++-- player_replay.c | 171 ++++++++++++++++++++++++++++++++++++++++++++++++ player_replay.h | 37 +++++++++++ skaterift.c | 2 + skeleton.h | 27 +++++++- 8 files changed, 347 insertions(+), 16 deletions(-) create mode 100644 player_replay.c create mode 100644 player_replay.h diff --git a/player.c b/player.c index 52e5683..33b1c1e 100644 --- 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 */ diff --git a/player.h b/player.h index 7484ce9..77c63db 100644 --- 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, diff --git a/player_api.h b/player_api.h index ad9e4ca..a009d6d 100644 --- a/player_api.h +++ b/player_api.h @@ -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 */ diff --git a/player_render.c b/player_render.c index ce3fa5a..d687370 100644 --- a/player_render.c +++ b/player_render.c @@ -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 index 0000000..83b9b83 --- /dev/null +++ b/player_replay.c @@ -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 index 0000000..1befaad --- /dev/null +++ b/player_replay.h @@ -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 */ diff --git a/skaterift.c b/skaterift.c index 175291f..cbefd25 100644 --- a/skaterift.c +++ b/skaterift.c @@ -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(); } diff --git a/skeleton.h b/skeleton.h index 0529752..dc506ab 100644 --- a/skeleton.h +++ b/skeleton.h @@ -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; ibone_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; ibone_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.. -- 2.25.1