X-Git-Url: https://harrygodden.com/git/?a=blobdiff_plain;f=player_replay.c;h=50ffc5ca564a7a8af2743ddedbe536c2fe044356;hb=95f6ef1a859423a0d9554ac6a9f4c6cbd8bb540c;hp=04650b73b81ed05d2de6de8ea482cad313553abe;hpb=9a63a9dde9257c8f140af3a96816bcda232fe739;p=carveJwlIkooP6JGAAIwe30JlM.git diff --git a/player_replay.c b/player_replay.c index 04650b7..50ffc5c 100644 --- a/player_replay.c +++ b/player_replay.c @@ -1,11 +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" + +#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 +}; -static void replay_clear( replay_buffer *replay ){ +void replay_clear( replay_buffer *replay ) +{ replay->head = NULL; replay->tail = NULL; replay->cursor_frame = NULL; @@ -13,16 +26,20 @@ 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; + void *baseptr = frame; return baseptr + frame->data_table[type][0]; } -static u16 -replay_frame_calculate_data_offsets( u16 data_table[4][2] ){ +static u16 replay_frame_calculate_data_offsets( + u16 data_table[k_replay_framedata_rows][2] ){ + u32 total = vg_align8( sizeof(replay_frame) ); - for( u32 i=0; i<4; i++ ){ + for( u32 i=0; ihead ){ + 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; @@ -81,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 ) @@ -98,7 +129,8 @@ check_again:; else frame = replay->data; - for( u32 i=0; i<4; i++ ){ + for( u32 i=0; idata_table[i][0] = data_table[i][0]; frame->data_table[i][1] = data_table[i][1]; } @@ -113,9 +145,20 @@ check_again:; return frame; } -static void replay_seek( replay_buffer *replay, f64 t ){ - if( !replay->head ) return; - assert( replay->tail ); +static void replay_emit_frame_sounds( replay_frame *frame ){ + void *baseptr = frame; + u16 *inf = frame->data_table[k_replay_framedata_sfx]; + struct net_sfx *buffer = baseptr + inf[0]; + u32 count = inf[1] / sizeof(struct net_sfx); + + for( u32 i=0; ihead ) return 0; if( t < replay->tail->time ) t = replay->tail->time; if( t > replay->head->time ) t = replay->head->time; @@ -131,13 +174,18 @@ static void replay_seek( replay_buffer *replay, f64 t ){ } f64 dir = t - replay->cursor; - if( dir == 0.0 ) return; + if( dir == 0.0 ) return 0; dir = vg_signf( dir ); + u32 i=4096; while( i --> 0 ){ - if( dir < 0.0 ) - if( t > replay->cursor_frame->time ) break; + if( dir < 0.0 ){ + if( t > replay->cursor_frame->time ) { + replay->cursor = t; + return 1; + } + } replay_frame *next; if( dir > 0.0 ) next = replay->cursor_frame->r; @@ -145,19 +193,27 @@ static void replay_seek( replay_buffer *replay, f64 t ){ if( !next ) break; - if( dir > 0.0 ) - if( t < next->time ) break; + if( dir > 0.0 ){ + if( t < next->time ){ + replay->cursor = t; + return 1; + } + } + + replay_emit_frame_sounds( next ); replay->cursor_frame = next; replay->cursor = next->time; - if( !i ) return; + if( !i ) return 1; } replay->cursor = 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 ){ @@ -169,56 +225,169 @@ 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 = (replay->cursor - frame->time) / l; + t = (l <= (1.0/128.0))? 0.0: (replay->cursor - frame->time) / l; return vg_clampf( t, 0.0f, 1.0f ); } else 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; @@ -232,43 +401,52 @@ void skaterift_record_frame( replay_buffer *replay, int force_gamestate ){ k_gamestate_rate = 0.5; int save_frame = 0, - save_state = 0; + save_state = 0, + save_glider = 0; if( force_gamestate ) save_state = 1; if( statedelta > k_gamestate_rate ) save_state = 1; if( delta > k_replay_rate ) save_frame = 1; if( save_state ) save_frame = 1; + if( localplayer.have_glider || localplayer.glider_orphan || + localplayer.subsystem == k_player_subsystem_glide ){ + save_glider = 1; + } + if( !save_frame ) return; u16 gamestate_size = 0; if( save_state ){ + /* TODO: have as part of system struct */ gamestate_size = (u32 []){ [k_player_subsystem_walk ] = sizeof(struct player_walk_state), [k_player_subsystem_drive] = 0, [k_player_subsystem_skate] = sizeof(struct player_skate_state), [k_player_subsystem_dead ] = localplayer.ragdoll.part_count * - sizeof(struct replay_rb) + sizeof(struct replay_rb), + [k_player_subsystem_glide] = sizeof(struct replay_rb), }[ localplayer.subsystem ]; } - u16 animator_size = (u16 []){ - [k_player_subsystem_walk ] = sizeof(struct player_walk_animator), - [k_player_subsystem_drive] = 0, - [k_player_subsystem_skate] = sizeof(struct player_skate_animator), - [k_player_subsystem_dead ] = sizeof(struct player_dead_animator) - }[ localplayer.subsystem ]; + u16 animator_size = player_subsystems[localplayer.subsystem]->animator_size; replay_frame *frame = replay_newframe( replay, - animator_size, gamestate_size, 0 ); + animator_size, gamestate_size, + localplayer.local_sfx_buffer_count, + save_glider ); frame->system = localplayer.subsystem; if( save_state ){ 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) ); memcpy( &gs->cam_control, &localplayer.cam_control, sizeof(struct player_cam_controller) ); v3_copy( localplayer.angles, gs->angles ); @@ -283,13 +461,32 @@ void skaterift_record_frame( replay_buffer *replay, int force_gamestate ){ else if( localplayer.subsystem == k_player_subsystem_dead ){ struct replay_rb *arr = dst; for( u32 i=0; ico, arr[i].co ); v3_copy( rb->w, arr[i].w ); v3_copy( rb->v, arr[i].v ); v4_copy( rb->q, arr[i].q ); } } + else if( localplayer.subsystem == k_player_subsystem_glide ){ + struct replay_rb *arr = dst; + rigidbody *rb = &player_glide.rb; + v3_copy( rb->co, arr[0].co ); + v3_copy( rb->w, arr[0].w ); + v3_copy( rb->v, arr[0].v ); + v4_copy( rb->q, arr[0].q ); + } + } + + if( save_glider ){ + struct replay_glider_data *inf = + replay_frame_data( frame, k_replay_framedata_glider ); + + inf->have_glider = localplayer.have_glider; + inf->glider_orphan = localplayer.glider_orphan; + inf->t = player_glide.t; + v3_copy( player_glide.rb.co, inf->co ); + v4_copy( player_glide.rb.q, inf->q ); } replay->cursor = vg.time; @@ -297,33 +494,42 @@ void skaterift_record_frame( replay_buffer *replay, int force_gamestate ){ 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_copy( localplayer.cam.angles, frame->cam_angles ); - frame->cam_fov = localplayer.cam.fov; + else + v3_copy( localplayer.cam.angles, frame->cam.angles ); + + frame->cam.fov = localplayer.cam.fov; /* animator */ - void *dst = replay_frame_data( frame, k_replay_framedata_animator ); + void *dst = replay_frame_data( frame, k_replay_framedata_animator ), + *src = player_subsystems[localplayer.subsystem]->animator_data; + memcpy( dst, src, animator_size ); - if( localplayer.subsystem == k_player_subsystem_walk ) - memcpy( dst, &player_walk.animator, animator_size ); - else if( localplayer.subsystem == k_player_subsystem_skate ) - memcpy( dst, &player_skate.animator, animator_size ); - else if( localplayer.subsystem == k_player_subsystem_dead ){ - memcpy( dst, &player_dead.animator, animator_size ); - } + /* sound effects */ + memcpy( replay_frame_data( frame, k_replay_framedata_sfx ), + localplayer.local_sfx_buffer, + sizeof(struct net_sfx)*localplayer.local_sfx_buffer_count ); + + 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 ); @@ -332,12 +538,12 @@ void skaterift_restore_frame( replay_frame *frame ){ memcpy( &player_skate.state, src, src_size ); } else if( frame->system == k_player_subsystem_dead ){ - player__dead_transition(); + player__dead_transition(0); struct replay_rb *arr = src; for( u32 i=0; iobj.rb; + rigidbody *rb = &part->rb; v3_copy( arr[i].co, rb->co ); v3_copy( arr[i].w, rb->w ); @@ -346,178 +552,242 @@ void skaterift_restore_frame( replay_frame *frame ){ v3_copy( arr[i].co, part->prev_co ); v4_copy( arr[i].q, part->prev_q ); + rb_update_matrices( rb ); } } + else if( frame->system == k_player_subsystem_glide ){ + struct replay_rb *arr = src; + rigidbody *rb = &player_glide.rb; + v3_copy( arr[0].co, rb->co ); + v3_copy( arr[0].w, rb->w ); + v3_copy( arr[0].v, rb->v ); + v4_copy( arr[0].q, rb->q ); + 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] ){ + struct replay_glider_data *inf = + replay_frame_data( frame, k_replay_framedata_glider ); + + localplayer.have_glider = inf->have_glider; + localplayer.glider_orphan = inf->glider_orphan; + player_glide.t = inf->t; + } + else { + localplayer.have_glider = 0; + localplayer.glider_orphan = 0; + player_glide.t = 0.0f; + } + memcpy( &localplayer.rb, &gs->rb, sizeof(rigidbody) ); + 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(); } -static void skaterift_replay_pre_update(void){ +static void skaterift_replay_update_helpers(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 ) ); - replay_seek( &skaterift.replay, target ); + 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 + 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; - replay_seek( &skaterift.replay, target ); + 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.enabled = 0; + srinput.state = k_input_state_resume; + gui_helper_clear(); } - gui_helper_action( button_display_string(k_srbind_replay_play), "play" ); - gui_helper_action( axis_display_string(k_sraxis_replay_h), "scrub" ); - if( skaterift.freecam ){ - gui_helper_action( button_display_string(k_srbind_replay_freecam), - "exit freecam" ); - - camera *cam = &skaterift.replay_freecam; - v3f angles; - v3_copy( cam->angles, angles ); - player_look( angles, 1.0f ); - - f32 decay = vg_maxf(0.0f,1.0f-vg.time_frame_delta*10.0f); - - v3f d; - v3_sub( angles, cam->angles, d ); - v3_muladds( skaterift.freecam_w, d, 20.0f, skaterift.freecam_w ); - v3_muls( skaterift.freecam_w, decay, skaterift.freecam_w ); - v3_muladds( cam->angles, skaterift.freecam_w, vg.time_frame_delta, - cam->angles ); - cam->angles[1] = vg_clampf( cam->angles[1], -VG_PIf*0.5f,VG_PIf*0.5f); - - camera_update_transform( cam ); - - v3f lookdir = { 0.0f, 0.0f, -1.0f }, - sidedir = { 1.0f, 0.0f, 0.0f }; - - m3x3f mtx; - m3x3_mul( cam->transform, localplayer.basis, mtx ); - - m3x3_mulv( mtx, lookdir, lookdir ); - m3x3_mulv( mtx, sidedir, sidedir ); - - v2f input; - joystick_state( k_srjoystick_steer, input ); - v2_muls( input, vg.time_frame_delta*6.0f*20.0f, input ); - - v3_muladds( skaterift.freecam_v, lookdir, -input[1], - skaterift.freecam_v ); - v3_muladds( skaterift.freecam_v, sidedir, input[0], - skaterift.freecam_v ); - - v3_muls( skaterift.freecam_v, decay, skaterift.freecam_v ); - v3_muladds( cam->pos, - skaterift.freecam_v, vg.time_frame_delta, cam->pos ); - } - else { - gui_helper_action( button_display_string(k_srbind_replay_resume), - "resume" ); - gui_helper_action( button_display_string(k_srbind_replay_freecam), - "enter freecam" ); - - 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_get_replay_camera( camera *cam ){ - if( skaterift.freecam ){ - cam->nearz = 0.1f; - cam->farz = 100.0f; - v3_copy( skaterift.replay_freecam.pos, cam->pos ); - v3_copy( skaterift.replay_freecam.angles, cam->angles ); - cam->fov = skaterift.replay_freecam.fov; - } - else{ - replay_get_camera( &skaterift.replay, &skaterift.cam ); +static void skaterift_replay_update_helpers(void) +{ + player_replay.helper_resume->greyed = player_replay.use_freecam; + + vg_str freecam_text; + vg_strnull( &freecam_text, player_replay.helper_freecam->text, + GUI_HELPER_TEXT_LENGTH ); + 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(); +} + +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 ) + { + localplayer.rewinded_since_last_gate = 1; + skaterift.activity = k_skaterift_replay; + 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; + } + 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(void) +{ + player__debugtext( 2, "replay info" ); + replay_buffer *replay = &player_replay.local; u32 head = 0, tail = 0; @@ -527,7 +797,7 @@ static void skaterift_replay_debug_info(void){ player__debugtext( 1, "head @%u | tail @%u\n", head, tail ); if( replay->statehead ){ - for( u32 i=0; i<4; i++ ){ + for( u32 i=0; istatehead->data_table[i][0], replay->statehead->data_table[i][1] ); @@ -551,10 +821,97 @@ static void skaterift_replay_debug_info(void){ player__debugtext( 1, "cursor: %.2fs / %.2fs\n", cur, len ); } -static void skaterift_replay_imgui(void){ +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 replay_fly_edit_keyframe( replay_keyframe *kf ) +{ + if( ui_click_down( 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 */ + vg_ui.wants_mouse = 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, skaterift.cam.pos ); + v3_copy( player_replay.replay_freecam.angles, skaterift.cam.angles); + skaterift.cam.fov = player_replay.replay_freecam.fov; + + v3_copy( skaterift.cam.pos, kf->cam.pos ); + v3_copy( skaterift.cam.angles, kf->cam.angles ); + kf->cam.fov = skaterift.cam.fov; +} + +void skaterift_replay_imgui(void) +{ 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(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( cx, colour ); + ui_fill( cy, colour ); + } + + replay_buffer *replay = &player_replay.local; f64 start = replay->cursor, end = replay->cursor; if( replay->tail ) start = replay->tail->time; @@ -567,59 +924,280 @@ 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( timeline, ui_colour( 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 }; + VG_MAX(4,(ui_px)(l*vg.window_x)), timeline[3]+2 }; ui_fill( box, ui_colour( k_ui_bg+4 ) ); } } /* cursor */ ui_rect cusor = { cur * (f64)vg.window_x - (cwidth/2), 0, - cwidth, bar[3] }; + cwidth, (player_replay.editor_mode? 0: 16) + timeline[3] }; ui_fill( cusor, ui_colour( 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_rect tag = { t*(f64)vg.window_x, 0, 2, timeline[3]+8 }; ui_fill( tag, ui_colour( 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_rect tag = { t*(f64)vg.window_x, 0, 2, timeline[3]+8 }; ui_fill( tag, ui_colour( 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( timeline, buffer, 1, k_ui_align_middle_left, 0 ); + ui_text( 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; + vg_ui.wants_mouse = 1; -#if 0 - char buf[256]; - snprintf( buf, 256, "scrub: %s\nresume: %s\nplay: %s\n", - axis_display_string( k_sraxis_replay_h ), - button_display_string( k_srbind_replay_resume ), - button_display_string( k_srbind_replay_play ) ); + ui_rect panel = { 0, timeline[3] + 20, 200, 400 }; + ui_fill( panel, ui_opacity( ui_colour(k_ui_bg), 0.5f ) ); + ui_rect_pad( panel, (ui_px[2]){4,4} ); - ui_rect info = { 0, vg.window_y-bar[3]-128, 256, 128 }; - ui_text( info, buf, 2,0,0 ); -#endif -} + if( ui_button( 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( script, ui_colour( k_ui_bg ) ); + + f64 mouse_t = start + ((f64)vg_ui.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( con, ui_colour(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, vg_ui.mouse ) ) + { + absorb_by_keyframe = 1; + + if( ui_click_down( UI_MOUSE_LEFT ) ) + { + if( player_replay.active_keyframe != i ) + { + player_replay.active_keyframe = i; + replay_seek( &player_replay.local, kf->time ); + } + } + else + { + ui_outline( tag, 1, ui_colour(k_ui_fg), 0 ); + } + } + + /* edit controls */ + u32 drag_colour = ui_opacity( ui_colour(k_ui_bg+2), 0.5f ); + if( i == player_replay.active_keyframe ) + { + ui_outline( tag, 2, ui_colour(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, vg_ui.mouse_click ) ) + { + if( ui_clicking( UI_MOUSE_LEFT ) ) + { + drag_colour = ui_opacity( ui_colour(k_ui_fg), 0.5f ); + pos_correct = 1; + replay_seek( &player_replay.local, mouse_t ); + } + else if( ui_click_up( UI_MOUSE_LEFT ) ) + { + pos_correct = 1; + kf->time = mouse_t; + replay_keyframe_sort(); + + for( u32 j=0; jtime = mouse_t; + v3_copy( skaterift.cam.pos, kf->cam.pos ); + v3_copy( skaterift.cam.angles, kf->cam.angles ); + kf->cam.fov = skaterift.cam.fov; + + replay_keyframe_sort(); + } + } + } + + /* timeline scrub */ + bool start_in_timeline = + ui_clicking(UI_MOUSE_LEFT) && + ui_inside_rect(timeline, vg_ui.mouse_click); + if( (ui_inside_rect( timeline, vg_ui.mouse )) || start_in_timeline ) + { + ui_rect cursor = { vg_ui.mouse[0], timeline[1], 4, timeline[3] }; + ui_fill( cursor, ui_colour( k_ui_fg ) ); + vg_ui.cursor = k_ui_cursor_ibeam; + + if( ui_clicking( UI_MOUSE_LEFT ) && start_in_timeline ) + { + replay_seek( &player_replay.local, mouse_t ); + player_replay.active_keyframe = -1; + } + } + + if( ui_button( panel, "Clear keyframes" ) == k_ui_button_click ) + { + player_replay.keyframe_count = 0; + } + + if( (ui_button( panel, "Hide UI (F2)" ) == k_ui_button_click) ) + { + player_replay.show_ui ^= 0x1; + } -#endif /* PLAYER_REPLAY_C */ + 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( panel, "Edit cam" ) & mask_using ) + { + replay_fly_edit_keyframe( kf ); + } + } + + ui_info( panel, "World settings" ); + f32 new_time = world_current_instance()->time; + if( ui_slider( panel, "Time of day", 0, 1, &new_time ) ) + { + world_current_instance()->time = new_time; + } + + ui_info( panel, "" ); + if( ui_button( 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 + */ +}