X-Git-Url: https://harrygodden.com/git/?a=blobdiff_plain;f=player_replay.c;h=0db1ca4d665822b8251555d62b5289c412e89c97;hb=refs%2Fheads%2Fmenu2;hp=a013f7ce4059dc9222adb2145ec2f9c919def399;hpb=859178c6d4b2a9b9a95f8b01b113f589ce0f197f;p=carveJwlIkooP6JGAAIwe30JlM.git diff --git a/player_replay.c b/player_replay.c index a013f7c..0db1ca4 100644 --- a/player_replay.c +++ b/player_replay.c @@ -1,12 +1,24 @@ -#ifndef PLAYER_REPLAY_C -#define PLAYER_REPLAY_C - +#include "skaterift.h" +#include "player.h" #include "player_replay.h" #include "input.h" #include "gui.h" #include "freecam.h" -static void replay_clear( replay_buffer *replay ){ +#include "player_walk.h" +#include "player_skate.h" +#include "player_dead.h" +#include "player_glide.h" + +struct replay_globals player_replay = +{ + .active_keyframe = -1, + .show_ui = 1, + .editor_mode = 0 +}; + +void replay_clear( replay_buffer *replay ) +{ replay->head = NULL; replay->tail = NULL; replay->cursor_frame = NULL; @@ -14,8 +26,8 @@ static void replay_clear( replay_buffer *replay ){ replay->cursor = -99999.9; } -static -void * replay_frame_data( replay_frame *frame, enum replay_framedata type ){ +void *replay_frame_data( replay_frame *frame, enum replay_framedata type ) +{ if( frame->data_table[type][1] == 0 ) return NULL; @@ -61,13 +73,15 @@ static replay_frame *replay_newframe( replay_buffer *replay, data_table[ k_replay_framedata_gamestate ][1] = gamestate_size; data_table[ k_replay_framedata_sfx ][1] = sfx_count*sizeof(struct net_sfx); data_table[ k_replay_framedata_internal_gamestate ][1] = 0; - if( gamestate_size ){ + if( gamestate_size ) + { data_table[ k_replay_framedata_internal_gamestate ][1] = sizeof( replay_gamestate ); } data_table[ k_replay_framedata_glider ][1] = 0; - if( save_glider ){ + if( save_glider ) + { data_table[ k_replay_framedata_glider ][1] = sizeof(struct replay_glider_data); } @@ -75,15 +89,18 @@ static replay_frame *replay_newframe( replay_buffer *replay, u32 nextsize = replay_frame_calculate_data_offsets( data_table ); replay_frame *frame = NULL; - if( replay->head ){ + if( replay->head ) + { u32 headsize = replay->head->total_size, nextpos = ((void *)replay->head - replay->data) + headsize; - if( nextpos + nextsize > replay->size ){ + if( nextpos + nextsize > replay->size ) + { nextpos = 0; /* maintain contiguity */ - while( replay->tail ){ + while( replay->tail ) + { if( (void *)replay->tail - replay->data ) replay_tailpop( replay ); else break; @@ -93,8 +110,10 @@ static replay_frame *replay_newframe( replay_buffer *replay, check_again:; u32 tailpos = (void *)replay->tail - replay->data; - if( tailpos >= nextpos ){ - if( nextpos + nextsize > tailpos ){ + if( tailpos >= nextpos ) + { + if( nextpos + nextsize > tailpos ) + { replay_tailpop( replay ); if( replay->tail ) @@ -110,7 +129,8 @@ check_again:; else frame = replay->data; - for( u32 i=0; idata_table[i][0] = data_table[i][0]; frame->data_table[i][1] = data_table[i][1]; } @@ -136,9 +156,9 @@ static void replay_emit_frame_sounds( replay_frame *frame ){ } } -static int replay_seek( replay_buffer *replay, f64 t ){ +int replay_seek( replay_buffer *replay, f64 t ) +{ if( !replay->head ) return 0; - assert( replay->tail ); if( t < replay->tail->time ) t = replay->tail->time; if( t > replay->head->time ) t = replay->head->time; @@ -192,7 +212,8 @@ static int replay_seek( replay_buffer *replay, f64 t ){ return 0; } -static replay_frame *replay_find_recent_stateframe( replay_buffer *replay ){ +replay_frame *replay_find_recent_stateframe( replay_buffer *replay ) +{ replay_frame *frame = replay->cursor_frame; u32 i=4096; while( i --> 0 ){ @@ -204,11 +225,13 @@ static replay_frame *replay_find_recent_stateframe( replay_buffer *replay ){ return NULL; } -static f32 replay_subframe_time( replay_buffer *replay ){ +f32 replay_subframe_time( replay_buffer *replay ) +{ replay_frame *frame = replay->cursor_frame; if( !frame ) return 0.0f; replay_frame *next = frame->r; - if( next ){ + if( next ) + { f64 l = next->time - frame->time, t = (l <= (1.0/128.0))? 0.0: (replay->cursor - frame->time) / l; return vg_clampf( t, 0.0f, 1.0f ); @@ -217,43 +240,154 @@ static f32 replay_subframe_time( replay_buffer *replay ){ return 0.0f; } -static void replay_get_frame_camera( replay_frame *frame, camera *cam ){ - cam->fov = frame->cam_fov; - v3_copy( frame->cam_pos, cam->pos ); - v3_copy( frame->cam_angles, cam->angles ); +void replay_get_frame_camera( replay_frame *frame, vg_camera *cam ) +{ + cam->fov = frame->cam.fov; + v3_copy( frame->cam.pos, cam->pos ); + v3_copy( frame->cam.angles, cam->angles ); } -static void replay_get_camera( replay_buffer *replay, camera *cam ){ +void replay_get_camera( replay_buffer *replay, vg_camera *cam ) +{ cam->nearz = 0.1f; cam->farz = 100.0f; - if( replay->cursor_frame ){ + if( replay->cursor_frame ) + { replay_frame *next = replay->cursor_frame->r; - if( next ){ - camera temp; + if( next ) + { + vg_camera temp; replay_get_frame_camera( replay->cursor_frame, cam ); replay_get_frame_camera( next, &temp ); - camera_lerp( cam, &temp, replay_subframe_time( replay ), cam ); + vg_camera_lerp( cam, &temp, replay_subframe_time( replay ), cam ); } - else { + else + { replay_get_frame_camera( replay->cursor_frame, cam ); } } - else { + else + { v3_zero( cam->pos ); v3_zero( cam->angles ); cam->fov = 90.0f; } } -struct replay_rb{ +void skaterift_get_replay_cam( vg_camera *cam ) +{ + replay_buffer *replay = &player_replay.local; + + if( player_replay.active_keyframe != -1 ) + { + replay_keyframe *kf = + &player_replay.keyframes[player_replay.active_keyframe]; + + v3_copy( kf->cam.pos, cam->pos ); + v3_copy( kf->cam.angles, cam->angles ); + cam->fov = kf->cam.fov; + return; + } + + if( player_replay.keyframe_count >= 2 ) + { + for( u32 i=0; icursor) && (kf[1].time>replay->cursor) ) + { + f64 l = kf[1].time - kf[0].time, + t = (l <= (1.0/128.0))? 0.0: (replay->cursor-kf[0].time) / l; + + if( player_replay.keyframe_count >= 3 ) + { + f32 m_start = 0.5f, m_end = 0.5f; + + if( i > 0 ) + { + if( (t < 0.5f) || (i==player_replay.keyframe_count-2) ) + { + kf --; + } + } + + u32 last = player_replay.keyframe_count-1; + if( kf+0 == player_replay.keyframes ) m_start = 1.0f; + if( kf+2 == player_replay.keyframes+last ) m_end = 1.0f; + + f32 ts = vg_lerpf( kf[0].time, kf[1].time, 1.0f-m_start ), + te = vg_lerpf( kf[1].time, kf[2].time, m_end ); + + l = te-ts; + t = (replay->cursor-ts)/l; + + /* + * Adjust t, so that its derivative matches at the endpoints. + * Since t needs to go from 0 to 1, it will naturally change at + * different rates between keyframes. So this smooths it out. + * + * Newton method, going through standard direct quadratic eq has + * precision / other problems. Also we only care about 0>t>1. + */ + f32 b = (kf[1].time-ts)/l, + x0 = 1.0-t; + for( u32 i=0; i<4; i ++ ) + { + f32 ix0 = 1.0f-x0, + fx_x0 = 2.0f*b*x0*ix0 + ix0*ix0 - t, + fxd_x0 = 2.0f*(-2.0f*b*x0 + b + x0 - 1.0f); + x0 = x0 - (fx_x0/fxd_x0); + } + t = 1.0-x0; + + f32 t0 = t*m_start+(1.0f-m_start), + t1 = t*m_end; + + v3f ps, pe, as, ae; + f32 fs, fe; + + /* first order */ + v3_lerp( kf[0].cam.pos, kf[1].cam.pos, t0, ps ); + vg_camera_lerp_angles( kf[0].cam.angles, kf[1].cam.angles, + t0, as ); + fs = vg_lerpf( kf[0].cam.fov, kf[1].cam.fov, t0 ); + + v3_lerp( kf[1].cam.pos, kf[2].cam.pos, t1, pe ); + vg_camera_lerp_angles( kf[1].cam.angles, kf[2].cam.angles, + t1, ae ); + fe = vg_lerpf( kf[1].cam.fov, kf[2].cam.fov, t1 ); + + /* second order */ + v3_lerp( ps, pe, t, cam->pos ); + vg_camera_lerp_angles( as, ae, t, cam->angles ); + cam->fov = vg_lerpf( fs, fe, t ); + } + else + { + v3_lerp( kf[0].cam.pos, kf[1].cam.pos, t, cam->pos ); + vg_camera_lerp_angles( kf[0].cam.angles, kf[1].cam.angles, + t, cam->angles ); + cam->fov = vg_lerpf( kf[0].cam.fov, kf[1].cam.fov, t ); + } + return; + } + } + } + + replay_get_camera( replay, cam ); +} + +struct replay_rb +{ v3f co, v, w; v4f q; }; -static void skaterift_record_frame( replay_buffer *replay, - int force_gamestate ){ +void skaterift_record_frame( replay_buffer *replay, int force_gamestate ) +{ f64 delta = 9999999.9, statedelta = 9999999.9; @@ -307,6 +441,9 @@ static void skaterift_record_frame( replay_buffer *replay, replay_gamestate *gs = replay_frame_data( frame, k_replay_framedata_internal_gamestate ); + gs->current_run_version = world_static.current_run_version; + gs->drowned = localplayer.drowned; + /* permanent block */ memcpy( &gs->rb, &localplayer.rb, sizeof(rigidbody) ); memcpy( &gs->glider_rb, &player_glide.rb, sizeof(rigidbody) ); @@ -357,20 +494,20 @@ static void skaterift_record_frame( replay_buffer *replay, frame->time = vg.time; /* camera */ - v3_copy( localplayer.cam.pos, frame->cam_pos ); + v3_copy( localplayer.cam.pos, frame->cam.pos ); if( localplayer.gate_waiting ){ m4x3_mulv( localplayer.gate_waiting->transport, - frame->cam_pos, frame->cam_pos ); + frame->cam.pos, frame->cam.pos ); v3f v0; v3_angles_vector( localplayer.cam.angles, v0 ); m3x3_mulv( localplayer.gate_waiting->transport, v0, v0 ); - v3_angles( v0, frame->cam_angles ); + v3_angles( v0, frame->cam.angles ); } else - v3_copy( localplayer.cam.angles, frame->cam_angles ); + v3_copy( localplayer.cam.angles, frame->cam.angles ); - frame->cam_fov = localplayer.cam.fov; + frame->cam.fov = localplayer.cam.fov; /* animator */ void *dst = replay_frame_data( frame, k_replay_framedata_animator ), @@ -385,13 +522,14 @@ static void skaterift_record_frame( replay_buffer *replay, localplayer.local_sfx_buffer_count = 0; } -static -void skaterift_restore_frame( replay_frame *frame ){ +static void skaterift_restore_frame( replay_frame *frame ) +{ replay_gamestate *gs = replay_frame_data( frame, k_replay_framedata_internal_gamestate ); void *src = replay_frame_data( frame, k_replay_framedata_gamestate ); u16 src_size = frame->data_table[ k_replay_framedata_gamestate ][1]; - assert( src_size ); + world_static.current_run_version = gs->current_run_version; + localplayer.drowned = gs->drowned; if(frame->system == k_player_subsystem_walk ){ memcpy( &player_walk.state, src, src_size ); @@ -427,10 +565,10 @@ void skaterift_restore_frame( replay_frame *frame ){ rb_update_matrices( rb ); } + localplayer.subsystem = frame->system; + /* restore the seperated glider data if we have it */ if( frame->data_table[ k_replay_framedata_glider ][1] ){ - localplayer.subsystem = frame->system; - struct replay_glider_data *inf = replay_frame_data( frame, k_replay_framedata_glider ); @@ -448,32 +586,32 @@ void skaterift_restore_frame( replay_frame *frame ){ memcpy( &player_glide.rb, &gs->glider_rb, sizeof(rigidbody) ); v3_copy( gs->angles, localplayer.angles ); - v3_copy( frame->cam_pos, localplayer.cam.pos ); - v3_copy( frame->cam_angles, localplayer.cam.angles ); - localplayer.cam.fov = frame->cam_fov; + v3_copy( frame->cam.pos, localplayer.cam.pos ); + v3_copy( frame->cam.angles, localplayer.cam.angles ); + localplayer.cam.fov = frame->cam.fov; memcpy( &localplayer.cam_control, &gs->cam_control, sizeof(struct player_cam_controller) ); /* chop end off replay */ frame->r = NULL; - skaterift.replay.statehead = frame; - skaterift.replay.head = frame; - skaterift.replay.cursor_frame = frame; - skaterift.replay.cursor = frame->time; - skaterift.replay_control = k_replay_control_scrub; + player_replay.local.statehead = frame; + player_replay.local.head = frame; + player_replay.local.cursor_frame = frame; + player_replay.local.cursor = frame->time; + player_replay.replay_control = k_replay_control_scrub; skaterift.activity = k_skaterift_default; vg.time = frame->time; } static void skaterift_replay_resume(void){ - replay_frame *prev = replay_find_recent_stateframe(&skaterift.replay); + replay_frame *prev = replay_find_recent_stateframe(&player_replay.local); if( prev ){ - skaterift.replay_control = k_replay_control_resume; - skaterift.resume_target = prev; - skaterift.resume_begin = skaterift.replay.cursor; - skaterift.resume_transition = 0.0f; + player_replay.replay_control = k_replay_control_resume; + player_replay.resume_target = prev; + player_replay.resume_begin = player_replay.local.cursor; + player_replay.resume_transition = 0.0f; } gui_helper_clear(); @@ -481,157 +619,198 @@ static void skaterift_replay_resume(void){ static void skaterift_replay_update_helpers(void); -static void skaterift_replay_pre_update(void){ +void skaterift_replay_pre_update(void) +{ if( skaterift.activity != k_skaterift_replay ) return; - if( skaterift.replay_control == k_replay_control_resume ){ - if( skaterift.replay.cursor_frame == skaterift.resume_target || - skaterift.replay.cursor_frame == NULL ){ - skaterift_restore_frame( skaterift.resume_target ); + bool input = player_replay.editor_mode^0x1; + + if( player_replay.replay_control == k_replay_control_resume ) + { + if( player_replay.local.cursor_frame == player_replay.resume_target || + player_replay.local.cursor_frame == NULL ) + { + skaterift_restore_frame( player_replay.resume_target ); } - else { - vg_slewf( &skaterift.resume_transition, 1.0f, + else + { + vg_slewf( &player_replay.resume_transition, 1.0f, vg.time_frame_delta * (1.0f/1.0f) ); - if( skaterift.resume_transition >= 1.0f ) - skaterift_restore_frame( skaterift.resume_target ); + if( player_replay.resume_transition >= 1.0f ) + skaterift_restore_frame( player_replay.resume_target ); else { - f64 target = vg_lerp( skaterift.resume_begin, - skaterift.resume_target->time, - vg_smoothstepf( skaterift.resume_transition ) ); - if( replay_seek( &skaterift.replay, target ) ) - skaterift.track_velocity = 1.0f; + f64 target = vg_lerp( player_replay.resume_begin, + player_replay.resume_target->time, + vg_smoothstepf( player_replay.resume_transition ) ); + if( replay_seek( &player_replay.local, target ) ) + player_replay.track_velocity = 1.0f; else - skaterift.track_velocity = 0.0f; + player_replay.track_velocity = 0.0f; } } } - else { - if( button_down( k_srbind_replay_play ) ) - skaterift.replay_control = k_replay_control_play; - if( button_down( k_srbind_replay_freecam ) ){ - skaterift.freecam = skaterift.freecam ^ 0x1; - - if( skaterift.freecam ) - replay_get_camera( &skaterift.replay, &skaterift.replay_freecam ); - + else + { + if( input && button_down( k_srbind_replay_play ) ) + player_replay.replay_control = k_replay_control_play; + if( input && button_down( k_srbind_replay_freecam ) ) + { + player_replay.use_freecam ^= 0x1; + + if( player_replay.use_freecam ) + { + replay_get_camera( &player_replay.local, + &player_replay.replay_freecam ); + } skaterift_replay_update_helpers(); } - f32 target_speed = axis_state( k_sraxis_replay_h ) * 5.0; - if( button_press( k_srbind_reset ) ) + f32 target_speed = 0.0f; + if( input ) + target_speed = axis_state( k_sraxis_replay_h ) * 5.0; + + if( input && button_press( k_srbind_reset ) ) target_speed += -2.0; if( fabsf(target_speed) > 0.01f ) - skaterift.replay_control = k_replay_control_scrub; + player_replay.replay_control = k_replay_control_scrub; - if( skaterift.replay_control == k_replay_control_play ) + if( player_replay.replay_control == k_replay_control_play ) target_speed = 1.0; - vg_slewf( &skaterift.track_velocity, target_speed, + vg_slewf( &player_replay.track_velocity, target_speed, 18.0f*vg.time_frame_delta ); - if( fabsf( skaterift.track_velocity ) > 0.0001f ){ - f64 target = skaterift.replay.cursor; - target += skaterift.track_velocity * vg.time_frame_delta; + if( fabsf( player_replay.track_velocity ) > 0.0001f ) + { + f64 target = player_replay.local.cursor; + target += player_replay.track_velocity * vg.time_frame_delta; - if( !replay_seek( &skaterift.replay, target ) ) - skaterift.track_velocity = 0.0f; + if( !replay_seek( &player_replay.local, target ) ) + player_replay.track_velocity = 0.0f; } - if( button_down( k_srbind_mback ) ){ - if( skaterift.replay.statehead ) - skaterift_restore_frame( skaterift.replay.statehead ); + if( input && button_down( k_srbind_mback ) ) + { + if( player_replay.local.statehead ) + skaterift_restore_frame( player_replay.local.statehead ); else skaterift.activity = k_skaterift_default; srinput.state = k_input_state_resume; gui_helper_clear(); } - if( skaterift.freecam ){ - //freecam_preupdate(); - } - else { - if( button_down( k_srbind_replay_resume ) ){ - skaterift_replay_resume(); + if( input ) + { + if( player_replay.use_freecam ) + { + freecam_preupdate(); + } + else + { + if( button_down( k_srbind_replay_resume ) ) + { + skaterift_replay_resume(); + } } } } } -static void skaterift_replay_update_helpers(void){ - skaterift.helper_resume->greyed = skaterift.freecam; +static void skaterift_replay_update_helpers(void) +{ + player_replay.helper_resume->greyed = player_replay.use_freecam; vg_str freecam_text; - vg_strnull( &freecam_text, skaterift.helper_freecam->text, + vg_strnull( &freecam_text, player_replay.helper_freecam->text, GUI_HELPER_TEXT_LENGTH ); - vg_strcat( &freecam_text, skaterift.freecam? "exit freecam": "freecam" ); + vg_strcat( &freecam_text, + player_replay.use_freecam? "Exit freecam": "Freecam" ); +} + +static void replay_show_helpers(void) +{ + gui_helper_clear(); + vg_str text; + + if( gui_new_helper( input_axis_list[k_sraxis_replay_h], &text ) ) + vg_strcat( &text, "Scrub" ); + + if( (player_replay.helper_resume = gui_new_helper( + input_button_list[k_srbind_replay_resume], &text )) ) + vg_strcat( &text, "Resume" ); + + if( gui_new_helper( input_button_list[k_srbind_replay_play], &text )) + vg_strcat( &text, "Playback" ); + + player_replay.helper_freecam = gui_new_helper( + input_button_list[k_srbind_replay_freecam], &text ); + + skaterift_replay_update_helpers(); } -static void skaterift_replay_post_render(void){ +void skaterift_replay_post_render(void) +{ #ifndef SR_ALLOW_REWIND_HUB if( world_static.active_instance != k_world_purpose_client ) return; #endif /* capture the current resume frame at the very last point */ - if( button_down( k_srbind_reset ) ){ - if( skaterift.activity == k_skaterift_default ){ + if( button_down( k_srbind_reset ) ) + { + if( skaterift.activity == k_skaterift_default ) + { localplayer.rewinded_since_last_gate = 1; skaterift.activity = k_skaterift_replay; - skaterift_record_frame( &skaterift.replay, 1 ); - if( skaterift.replay.head ){ - skaterift.replay.cursor = skaterift.replay.head->time; - skaterift.replay.cursor_frame = skaterift.replay.head; + skaterift_record_frame( &player_replay.local, 1 ); + if( player_replay.local.head ) + { + player_replay.local.cursor = player_replay.local.head->time; + player_replay.local.cursor_frame = player_replay.local.head; } - skaterift.replay_control = k_replay_control_scrub; - - gui_helper_clear(); - vg_str text; - - if( gui_new_helper( input_axis_list[k_sraxis_replay_h], &text ) ) - vg_strcat( &text, "scrub" ); - - if( (skaterift.helper_resume = gui_new_helper( - input_button_list[k_srbind_replay_resume], &text )) ) - vg_strcat( &text, "resume" ); - - if( gui_new_helper( input_button_list[k_srbind_replay_play], &text )) - vg_strcat( &text, "playback" ); - - skaterift.helper_freecam = gui_new_helper( - input_button_list[k_srbind_replay_freecam], &text ); - - skaterift_replay_update_helpers(); + player_replay.replay_control = k_replay_control_scrub; + replay_show_helpers(); } } } -static void skaterift_replay_debug_info(void){ - player__debugtext( 2, "replay info" ); +void skaterift_replay_init(void) +{ + u32 bytes = 1024*1024*10; + player_replay.local.data = vg_linear_alloc( vg_mem.rtmemory, bytes ); + player_replay.local.size = bytes; + replay_clear( &player_replay.local ); +} - replay_buffer *replay = &skaterift.replay; +void skaterift_replay_debug_info( ui_context *ctx ) +{ + player__debugtext( ctx, 2, "replay info" ); + replay_buffer *replay = &player_replay.local; 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 ); + player__debugtext( ctx, 1, "head @%u | tail @%u\n", head, tail ); - if( replay->statehead ){ - for( u32 i=0; istatehead ) + { + for( u32 i=0; istatehead->data_table[i][0], replay->statehead->data_table[i][1] ); } u32 state = (void *)replay->statehead - replay->data; - player__debugtext( 1, "gs @%u\n", state ); - player__debugtext( 1, "gamestate_size: %hu\n", + player__debugtext( ctx, 1, "gs @%u\n", state ); + player__debugtext( ctx, 1, "gamestate_size: %hu\n", replay->statehead->data_table[k_replay_framedata_gamestate][1] ); } else - player__debugtext( 1, "gs @NULL\n" ); + player__debugtext( ctx, 1, "gs @NULL\n" ); f64 start = replay->cursor, end = replay->cursor; @@ -641,13 +820,100 @@ static void skaterift_replay_debug_info(void){ f64 cur = replay->cursor - start, len = end - start; - player__debugtext( 1, "cursor: %.2fs / %.2fs\n", cur, len ); + player__debugtext( ctx, 1, "cursor: %.2fs / %.2fs\n", cur, len ); +} + +static int _keyframe_cmp( const void *p1, const void *p2 ) +{ + const replay_keyframe *kf1 = p1, *kf2 = p2; + return kf1->time > kf2->time; +} + +static void replay_keyframe_sort(void) +{ + qsort( player_replay.keyframes, player_replay.keyframe_count, + sizeof(replay_keyframe), _keyframe_cmp ); } -static void skaterift_replay_imgui(void){ +static void replay_fly_edit_keyframe( ui_context *ctx, replay_keyframe *kf ) +{ + if( ui_click_down( ctx, UI_MOUSE_LEFT ) ) + { + /* init freecam */ + v3_copy( kf->cam.pos, player_replay.replay_freecam.pos ); + v3_copy( kf->cam.angles, player_replay.replay_freecam.angles ); + v3_zero( player_replay.freecam_v ); + v3_zero( player_replay.freecam_w ); + player_replay.replay_freecam.fov = kf->cam.fov; + } + + /* move freecam */ + ui_capture_mouse( ctx, 0 ); + freecam_preupdate(); + + if( vg_getkey(SDLK_q) ) + player_replay.freecam_v[1] -= vg.time_frame_delta*6.0f*20.0f; + if( vg_getkey(SDLK_e) ) + player_replay.freecam_v[1] += vg.time_frame_delta*6.0f*20.0f; + + v3_copy( player_replay.replay_freecam.pos, g_render.cam.pos ); + v3_copy( player_replay.replay_freecam.angles, g_render.cam.angles); + g_render.cam.fov = player_replay.replay_freecam.fov; + + v3_copy( g_render.cam.pos, kf->cam.pos ); + v3_copy( g_render.cam.angles, kf->cam.angles ); + kf->cam.fov = g_render.cam.fov; +} + +void skaterift_replay_imgui( ui_context *ctx ) +{ if( skaterift.activity != k_skaterift_replay ) return; - replay_buffer *replay = &skaterift.replay; + /* extra keys for entering editor */ + static u8 f1_key = 0; + u8 f1_now = vg_getkey(SDLK_F1); + if( f1_now && !f1_key && player_replay.show_ui ) + { + player_replay.editor_mode ^= 0x1; + + if( player_replay.editor_mode ) + gui_helper_clear(); + else + replay_show_helpers(); + } + f1_key = f1_now; + + static u8 f2_key = 0; + u8 f2_now = vg_getkey(SDLK_F2); + if( f2_now && !f2_key ) + { + player_replay.show_ui ^= 0x1; + } + f2_key = f2_now; + + if( player_replay.editor_mode ) + { + static u8 space_key = 0; + u8 space_now = vg_getkey(SDLK_SPACE); + if( space_now & !space_key ) + { + player_replay.replay_control ^= k_replay_control_play; + } + space_key = space_now; + } + + if( !player_replay.show_ui ) return; + + if( player_replay.editor_mode ) + { + u32 colour = ui_opacity( ui_colour(ctx,k_ui_fg), 0.3333f ); + ui_rect cx = { vg.window_x/2, 0, 1, vg.window_y }, + cy = { 0, vg.window_y/2, vg.window_x, 1 }; + ui_fill( ctx, cx, colour ); + ui_fill( ctx, cy, colour ); + } + + replay_buffer *replay = &player_replay.local; f64 start = replay->cursor, end = replay->cursor; if( replay->tail ) start = replay->tail->time; @@ -660,48 +926,282 @@ static void skaterift_replay_imgui(void){ /* mainbar */ ui_px height = 32, cwidth = 2; - ui_rect bar = { 0, 0, vg.window_x, height }; - ui_fill( bar, ui_colour( k_ui_bg ) ); + ui_rect timeline = { 0, 0, vg.window_x, height }; + ui_fill( ctx, timeline, ui_colour( ctx, k_ui_bg ) ); /* cursor frame block */ - if( replay->cursor_frame ){ - if( replay->cursor_frame->r ){ + if( replay->cursor_frame ) + { + if( replay->cursor_frame->r ) + { f64 l = (replay->cursor_frame->r->time-replay->cursor_frame->time)/len, s = (replay->cursor_frame->time - start) / len; ui_rect box = { s*(f64)vg.window_x, 0, - VG_MAX(4,(ui_px)(l*vg.window_x)), bar[3]+2 }; - ui_fill( box, ui_colour( k_ui_bg+4 ) ); + VG_MAX(4,(ui_px)(l*vg.window_x)), timeline[3]+2 }; + ui_fill( ctx, box, ui_colour( ctx, k_ui_bg+4 ) ); } } /* cursor */ ui_rect cusor = { cur * (f64)vg.window_x - (cwidth/2), 0, - cwidth, bar[3] }; - ui_fill( cusor, ui_colour( k_ui_bg+7 ) ); + cwidth, (player_replay.editor_mode? 0: 16) + timeline[3] }; + ui_fill( ctx, cusor, ui_colour( ctx, k_ui_bg+7 ) ); /* latest state marker */ - if( replay->statehead ){ + if( replay->statehead ) + { f64 t = (replay->statehead->time - start) / len; - ui_rect tag = { t*(f64)vg.window_x, 0, 2, bar[3]+8 }; - ui_fill( tag, ui_colour( k_ui_green+k_ui_brighter ) ); + ui_rect tag = { t*(f64)vg.window_x, 0, 2, timeline[3]+8 }; + ui_fill( ctx, tag, ui_colour( ctx, k_ui_green+k_ui_brighter ) ); } /* previous state marker */ replay_frame *prev = replay_find_recent_stateframe( replay ); - if( prev ){ + if( prev ) + { f64 t = (prev->time - start) / len; - ui_rect tag = { t*(f64)vg.window_x, 0, 2, bar[3]+8 }; - ui_fill( tag, ui_colour( k_ui_yellow+k_ui_brighter ) ); + ui_rect tag = { t*(f64)vg.window_x, 0, 2, timeline[3]+8 }; + ui_fill( ctx, tag, ui_colour( ctx, k_ui_yellow+k_ui_brighter ) ); } - 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 (F1: Edit replay)", (end-replay->cursor) ); + ui_text( ctx, timeline, buffer, 1, k_ui_align_middle_left, 0 ); + ui_text( ctx, timeline, "0s", 1, k_ui_align_middle_right, 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 ); -} + if( !player_replay.editor_mode ) return; + ui_capture_mouse( ctx, 1 ); + + ui_rect panel = { 0, timeline[3] + 20, 200, 400 }; + ui_fill( ctx, panel, ui_opacity( ui_colour( ctx, k_ui_bg ), 0.5f ) ); + ui_rect_pad( panel, (ui_px[2]){4,4} ); + + if( ui_button( ctx, panel, + (player_replay.replay_control == k_replay_control_play)? + "Pause (space)": "Play (space)" ) == k_ui_button_click ) + { + player_replay.replay_control ^= k_replay_control_play; + } + + /* script bar */ + ui_rect script = { 0, height + 2, vg.window_x, 16 }; + ui_fill( ctx, script, ui_colour( ctx, k_ui_bg ) ); + f64 mouse_t = start + ((f64)ctx->mouse[0] / (f64)vg.window_x)*len; + + /* keyframe draw and select */ + bool absorb_by_keyframe = 0; + ui_px lx = 0; + for( u32 i=0; itime-start)/len; + + ui_px x = t*(f64)vg.window_x-8; + + /* draw connections between keyframes */ + if( i ) + { + ui_rect con = { lx, script[1]+7, x-lx, 1 }; + ui_fill( ctx, con, ui_colour( ctx, k_ui_blue ) ); + vg_line( kf->cam.pos, player_replay.keyframes[i-1].cam.pos, VG__BLUE ); + } + + /* keyframe selection */ + ui_rect tag = { x, script[1], 16, 16 }; + + if( ui_inside_rect( tag, ctx->mouse ) ) + { + absorb_by_keyframe = 1; + + if( ui_click_down( ctx, UI_MOUSE_LEFT ) ) + { + if( player_replay.active_keyframe != i ) + { + player_replay.active_keyframe = i; + replay_seek( &player_replay.local, kf->time ); + } + } + else + { + ui_outline( ctx, tag, 1, ui_colour(ctx, k_ui_fg), 0 ); + } + } + + /* edit controls */ + u32 drag_colour = ui_opacity( ui_colour(ctx, k_ui_bg+2), 0.5f ); + if( i == player_replay.active_keyframe ) + { + ui_outline( ctx, tag, 2, ui_colour(ctx, k_ui_fg), 0 ); + + ui_rect tray = { tag[0]+8-32, tag[1]+16+2, 64, 16 }; + ui_rect dragbar = { tray[0]+16, tray[1], 32, 16 }; + + bool pos_correct = 0; + + if( ui_inside_rect( dragbar, ctx->mouse_click ) ) + { + if( ui_clicking( ctx, UI_MOUSE_LEFT ) ) + { + drag_colour = ui_opacity( ui_colour(ctx,k_ui_fg), 0.5f ); + pos_correct = 1; + replay_seek( &player_replay.local, mouse_t ); + } + else if( ui_click_up( ctx, UI_MOUSE_LEFT ) ) + { + pos_correct = 1; + kf->time = mouse_t; + replay_keyframe_sort(); + + for( u32 j=0; jmouse[0]-8; + tray[0] = tag[0]+8-32; + dragbar[0] = tray[0]+16; + } + } + + if( ui_inside_rect( dragbar, ctx->mouse ) ) + { + ctx->cursor = k_ui_cursor_hand; + } + + if( !pos_correct ) + { + ui_fill( ctx, tray, + ui_opacity( ui_colour( ctx, k_ui_bg+2 ), 0.5f ) ); + } -#endif /* PLAYER_REPLAY_C */ + ui_fill( ctx, dragbar, drag_colour ); + ui_text( ctx, dragbar, ":::", 1, k_ui_align_middle_center, 0 ); + + if( !pos_correct ) + { + ui_rect btn = { tray[0], tray[1], 16, 16 }; + if( ui_button_text( ctx, btn, "X", 1 ) == k_ui_button_click ) + { + for( u32 j=i; jmouse ) ) + { + ctx->cursor = k_ui_cursor_hand; + + ui_rect cursor = { ctx->mouse[0], script[1], 4, 16 }; + ui_fill( ctx, cursor, ui_colour( ctx, k_ui_fg ) ); + + if( !absorb_by_keyframe && ui_click_down( ctx, UI_MOUSE_LEFT ) ) + { + u32 max = vg_list_size( player_replay.keyframes ); + if( player_replay.keyframe_count == max ) + { + ui_start_modal( ctx, "Maximum keyframes reached", UI_MODAL_BAD ); + } + else + { + replay_keyframe *kf = + &player_replay.keyframes[player_replay.keyframe_count++]; + + kf->time = mouse_t; + v3_copy( g_render.cam.pos, kf->cam.pos ); + v3_copy( g_render.cam.angles, kf->cam.angles ); + kf->cam.fov = g_render.cam.fov; + + replay_keyframe_sort(); + } + } + } + + /* timeline scrub */ + bool start_in_timeline = + ui_clicking(ctx, UI_MOUSE_LEFT) && + ui_inside_rect(timeline, ctx->mouse_click); + if( (ui_inside_rect( timeline, ctx->mouse )) || start_in_timeline ) + { + ui_rect cursor = { ctx->mouse[0], timeline[1], 4, timeline[3] }; + ui_fill( ctx, cursor, ui_colour( ctx, k_ui_fg ) ); + ctx->cursor = k_ui_cursor_ibeam; + + if( ui_clicking( ctx, UI_MOUSE_LEFT ) && start_in_timeline ) + { + replay_seek( &player_replay.local, mouse_t ); + player_replay.active_keyframe = -1; + } + } + + if( ui_button( ctx, panel, "Clear keyframes" ) == k_ui_button_click ) + { + player_replay.keyframe_count = 0; + } + + if( (ui_button( ctx, panel, "Hide UI (F2)" ) == k_ui_button_click) ) + { + player_replay.show_ui ^= 0x1; + } + + if( player_replay.active_keyframe != -1 ) + { + replay_keyframe *kf = + &player_replay.keyframes[ player_replay.active_keyframe ]; + + enum ui_button_state mask_using = + k_ui_button_holding_inside | + k_ui_button_holding_outside | + k_ui_button_click; + + if( ui_button( ctx, panel, "Edit cam" ) & mask_using ) + { + replay_fly_edit_keyframe( ctx, kf ); + } + } + + ui_info( ctx, panel, "World settings" ); + f32 new_time = world_current_instance()->time; + if( ui_slider( ctx, panel, "Time of day", 0, 1, &new_time ) ) + { + world_current_instance()->time = new_time; + } + + ui_info( ctx, panel, "" ); + if( ui_button( ctx, panel, "Exit editor (F1)" ) == k_ui_button_click ) + { + player_replay.editor_mode = 0; + replay_show_helpers(); + } + + /* TODO: Add Q/E scrub here too. + * Add replay trimming + */ +}