1 #ifndef PLAYER_REPLAY_C
2 #define PLAYER_REPLAY_C
4 #include "player_replay.h"
9 static void replay_clear( replay_buffer
*replay
){
12 replay
->cursor_frame
= NULL
;
13 replay
->statehead
= NULL
;
14 replay
->cursor
= -99999.9;
18 void * replay_frame_data( replay_frame
*frame
, enum replay_framedata type
){
19 void *baseptr
= frame
;
20 return baseptr
+ frame
->data_table
[type
][0];
24 replay_frame_calculate_data_offsets( u16 data_table
[4][2] ){
25 u32 total
= vg_align8( sizeof(replay_frame
) );
26 for( u32 i
=0; i
<4; i
++ ){
27 data_table
[i
][0] = total
;
28 total
+= vg_align8(data_table
[i
][1]);
31 vg_fatal_error( "Exceeded frame storage capacity\n" );
36 static void replay_tailpop( replay_buffer
*replay
){
37 if( replay
->cursor_frame
== replay
->tail
)
38 replay
->cursor_frame
= NULL
;
39 if( replay
->statehead
== replay
->tail
)
40 replay
->statehead
= NULL
;
42 replay
->tail
= replay
->tail
->r
;
45 replay
->tail
->l
= NULL
;
50 static replay_frame
*replay_newframe( replay_buffer
*replay
,
55 data_table
[ k_replay_framedata_animator
][1] = animator_size
;
56 data_table
[ k_replay_framedata_gamestate
][1] = gamestate_size
;
57 data_table
[ k_replay_framedata_sfx
][1] = 0;
58 data_table
[ k_replay_framedata_internal_gamestate
][1] = 0;
60 data_table
[ k_replay_framedata_internal_gamestate
][1] =
61 sizeof( replay_gamestate
);
64 u32 nextsize
= replay_frame_calculate_data_offsets( data_table
);
66 replay_frame
*frame
= NULL
;
68 u32 headsize
= replay
->head
->total_size
,
69 nextpos
= ((void *)replay
->head
- replay
->data
) + headsize
;
71 if( nextpos
+ nextsize
> replay
->size
){
74 /* maintain contiguity */
75 while( replay
->tail
){
76 if( (void *)replay
->tail
- replay
->data
)
77 replay_tailpop( replay
);
83 u32 tailpos
= (void *)replay
->tail
- replay
->data
;
85 if( tailpos
>= nextpos
){
86 if( nextpos
+ nextsize
> tailpos
){
87 replay_tailpop( replay
);
94 frame
= replay
->data
+ nextpos
;
97 replay
->head
->r
= frame
;
100 frame
= replay
->data
;
102 for( u32 i
=0; i
<4; i
++ ){
103 frame
->data_table
[i
][0] = data_table
[i
][0];
104 frame
->data_table
[i
][1] = data_table
[i
][1];
107 frame
->total_size
= nextsize
;
108 frame
->l
= replay
->head
;
110 replay
->head
= frame
;
111 if( !replay
->tail
) replay
->tail
= frame
;
112 if( gamestate_size
) replay
->statehead
= frame
;
117 static void replay_seek( replay_buffer
*replay
, f64 t
){
118 if( !replay
->head
) return;
119 assert( replay
->tail
);
121 if( t
< replay
->tail
->time
) t
= replay
->tail
->time
;
122 if( t
> replay
->head
->time
) t
= replay
->head
->time
;
124 if( !replay
->cursor_frame
) {
125 replay
->cursor
= replay
->head
->time
;
126 replay
->cursor_frame
= replay
->head
;
128 if( fabs(replay
->head
->time
-t
) > fabs(replay
->tail
->time
-t
) ){
129 replay
->cursor
= replay
->tail
->time
;
130 replay
->cursor_frame
= replay
->tail
;
134 f64 dir
= t
- replay
->cursor
;
135 if( dir
== 0.0 ) return;
136 dir
= vg_signf( dir
);
141 if( t
> replay
->cursor_frame
->time
) break;
144 if( dir
> 0.0 ) next
= replay
->cursor_frame
->r
;
145 else next
= replay
->cursor_frame
->l
;
150 if( t
< next
->time
) break;
152 replay
->cursor_frame
= next
;
153 replay
->cursor
= next
->time
;
161 static replay_frame
*replay_find_recent_stateframe( replay_buffer
*replay
){
162 replay_frame
*frame
= replay
->cursor_frame
;
165 if( !frame
) return frame
;
166 if( frame
->data_table
[ k_replay_framedata_gamestate
][1] ) return frame
;
173 static f32
replay_subframe_time( replay_buffer
*replay
){
174 replay_frame
*frame
= replay
->cursor_frame
;
175 if( !frame
) return 0.0f
;
176 replay_frame
*next
= frame
->r
;
178 f64 l
= next
->time
- frame
->time
,
179 t
= (l
<= (1.0/128.0))? 0.0: (replay
->cursor
- frame
->time
) / l
;
180 return vg_clampf( t
, 0.0f
, 1.0f
);
186 static void replay_get_frame_camera( replay_frame
*frame
, camera
*cam
){
187 cam
->fov
= frame
->cam_fov
;
188 v3_copy( frame
->cam_pos
, cam
->pos
);
189 v3_copy( frame
->cam_angles
, cam
->angles
);
192 static void replay_get_camera( replay_buffer
*replay
, camera
*cam
){
195 if( replay
->cursor_frame
){
196 replay_frame
*next
= replay
->cursor_frame
->r
;
201 replay_get_frame_camera( replay
->cursor_frame
, cam
);
202 replay_get_frame_camera( next
, &temp
);
203 camera_lerp( cam
, &temp
, replay_subframe_time( replay
), cam
);
206 replay_get_frame_camera( replay
->cursor_frame
, cam
);
211 v3_zero( cam
->angles
);
221 static void skaterift_record_frame( replay_buffer
*replay
,
222 int force_gamestate
){
223 f64 delta
= 9999999.9,
224 statedelta
= 9999999.9;
227 delta
= vg
.time
- replay
->head
->time
;
229 if( replay
->statehead
)
230 statedelta
= vg
.time
- replay
->statehead
->time
;
232 const f64 k_replay_rate
= 1.0/30.0,
233 k_gamestate_rate
= 0.5;
238 if( force_gamestate
) save_state
= 1;
239 if( statedelta
> k_gamestate_rate
) save_state
= 1;
240 if( delta
> k_replay_rate
) save_frame
= 1;
241 if( save_state
) save_frame
= 1;
243 if( !save_frame
) return;
245 u16 gamestate_size
= 0;
247 gamestate_size
= (u32
[]){
248 [k_player_subsystem_walk
] = sizeof(struct player_walk_state
),
249 [k_player_subsystem_drive
] = 0,
250 [k_player_subsystem_skate
] = sizeof(struct player_skate_state
),
251 [k_player_subsystem_dead
] = localplayer
.ragdoll
.part_count
*
252 sizeof(struct replay_rb
)
253 }[ localplayer
.subsystem
];
256 u16 animator_size
= (u16
[]){
257 [k_player_subsystem_walk
] = sizeof(struct player_walk_animator
),
258 [k_player_subsystem_drive
] = 0,
259 [k_player_subsystem_skate
] = sizeof(struct player_skate_animator
),
260 [k_player_subsystem_dead
] = sizeof(struct player_dead_animator
)
261 }[ localplayer
.subsystem
];
263 replay_frame
*frame
= replay_newframe( replay
,
264 animator_size
, gamestate_size
, 0 );
265 frame
->system
= localplayer
.subsystem
;
268 replay_gamestate
*gs
=
269 replay_frame_data( frame
, k_replay_framedata_internal_gamestate
);
271 /* permanent block */
272 memcpy( &gs
->rb
, &localplayer
.rb
, sizeof(rigidbody
) );
273 memcpy( &gs
->cam_control
, &localplayer
.cam_control
,
274 sizeof(struct player_cam_controller
) );
275 v3_copy( localplayer
.angles
, gs
->angles
);
277 void *dst
= replay_frame_data( frame
, k_replay_framedata_gamestate
);
279 /* subsytem/dynamic block */
280 if( localplayer
.subsystem
== k_player_subsystem_walk
)
281 memcpy( dst
, &player_walk
.state
, gamestate_size
);
282 else if( localplayer
.subsystem
== k_player_subsystem_skate
)
283 memcpy( dst
, &player_skate
.state
, gamestate_size
);
284 else if( localplayer
.subsystem
== k_player_subsystem_dead
){
285 struct replay_rb
*arr
= dst
;
286 for( u32 i
=0; i
<localplayer
.ragdoll
.part_count
; i
++ ){
287 rigidbody
*rb
= &localplayer
.ragdoll
.parts
[i
].obj
.rb
;
288 v3_copy( rb
->co
, arr
[i
].co
);
289 v3_copy( rb
->w
, arr
[i
].w
);
290 v3_copy( rb
->v
, arr
[i
].v
);
291 v4_copy( rb
->q
, arr
[i
].q
);
296 replay
->cursor
= vg
.time
;
297 replay
->cursor_frame
= frame
;
298 frame
->time
= vg
.time
;
301 v3_copy( localplayer
.cam
.pos
, frame
->cam_pos
);
302 if( localplayer
.gate_waiting
){
303 m4x3_mulv( localplayer
.gate_waiting
->transport
,
304 frame
->cam_pos
, frame
->cam_pos
);
307 v3_angles_vector( localplayer
.cam
.angles
, v0
);
308 m3x3_mulv( localplayer
.gate_waiting
->transport
, v0
, v0
);
309 v3_angles( v0
, frame
->cam_angles
);
312 v3_copy( localplayer
.cam
.angles
, frame
->cam_angles
);
314 frame
->cam_fov
= localplayer
.cam
.fov
;
317 void *dst
= replay_frame_data( frame
, k_replay_framedata_animator
);
319 if( localplayer
.subsystem
== k_player_subsystem_walk
)
320 memcpy( dst
, &player_walk
.animator
, animator_size
);
321 else if( localplayer
.subsystem
== k_player_subsystem_skate
)
322 memcpy( dst
, &player_skate
.animator
, animator_size
);
323 else if( localplayer
.subsystem
== k_player_subsystem_dead
){
324 memcpy( dst
, &player_dead
.animator
, animator_size
);
329 void skaterift_restore_frame( replay_frame
*frame
){
330 replay_gamestate
*gs
=
331 replay_frame_data( frame
, k_replay_framedata_internal_gamestate
);
332 void *src
= replay_frame_data( frame
, k_replay_framedata_gamestate
);
333 u16 src_size
= frame
->data_table
[ k_replay_framedata_gamestate
][1];
336 if(frame
->system
== k_player_subsystem_walk
){
337 memcpy( &player_walk
.state
, src
, src_size
);
339 else if( frame
->system
== k_player_subsystem_skate
){
340 memcpy( &player_skate
.state
, src
, src_size
);
342 else if( frame
->system
== k_player_subsystem_dead
){
343 player__dead_transition(0);
344 struct replay_rb
*arr
= src
;
346 for( u32 i
=0; i
<localplayer
.ragdoll
.part_count
; i
++ ){
347 struct ragdoll_part
*part
= &localplayer
.ragdoll
.parts
[i
];
348 rigidbody
*rb
= &part
->obj
.rb
;
350 v3_copy( arr
[i
].co
, rb
->co
);
351 v3_copy( arr
[i
].w
, rb
->w
);
352 v3_copy( arr
[i
].v
, rb
->v
);
353 v4_copy( arr
[i
].q
, rb
->q
);
355 v3_copy( arr
[i
].co
, part
->prev_co
);
356 v4_copy( arr
[i
].q
, part
->prev_q
);
360 localplayer
.subsystem
= frame
->system
;
362 memcpy( &localplayer
.rb
, &gs
->rb
, sizeof(rigidbody
) );
363 v3_copy( gs
->angles
, localplayer
.angles
);
365 v3_copy( frame
->cam_pos
, localplayer
.cam
.pos
);
366 v3_copy( frame
->cam_angles
, localplayer
.cam
.angles
);
367 localplayer
.cam
.fov
= frame
->cam_fov
;
369 memcpy( &localplayer
.cam_control
, &gs
->cam_control
,
370 sizeof(struct player_cam_controller
) );
372 /* chop end off replay */
374 skaterift
.replay
.statehead
= frame
;
375 skaterift
.replay
.head
= frame
;
376 skaterift
.replay
.cursor_frame
= frame
;
377 skaterift
.replay
.cursor
= frame
->time
;
378 skaterift
.replay_control
= k_replay_control_scrub
;
379 skaterift
.activity
= k_skaterift_default
;
380 vg
.time
= frame
->time
;
383 static void skaterift_replay_resume(void){
384 replay_frame
*prev
= replay_find_recent_stateframe(&skaterift
.replay
);
387 skaterift
.replay_control
= k_replay_control_resume
;
388 skaterift
.resume_target
= prev
;
389 skaterift
.resume_begin
= skaterift
.replay
.cursor
;
390 skaterift
.resume_transition
= 0.0f
;
396 static void skaterift_replay_update_helpers(void);
398 static void skaterift_replay_pre_update(void){
399 if( skaterift
.activity
!= k_skaterift_replay
) return;
401 if( skaterift
.replay_control
== k_replay_control_resume
){
402 if( skaterift
.replay
.cursor_frame
== skaterift
.resume_target
||
403 skaterift
.replay
.cursor_frame
== NULL
){
404 skaterift_restore_frame( skaterift
.resume_target
);
407 vg_slewf( &skaterift
.resume_transition
, 1.0f
,
408 vg
.time_frame_delta
* (1.0f
/1.0f
) );
410 if( skaterift
.resume_transition
>= 1.0f
)
411 skaterift_restore_frame( skaterift
.resume_target
);
413 f64 target
= vg_lerp( skaterift
.resume_begin
,
414 skaterift
.resume_target
->time
,
415 vg_smoothstepf( skaterift
.resume_transition
) );
416 replay_seek( &skaterift
.replay
, target
);
421 if( button_down( k_srbind_replay_play
) )
422 skaterift
.replay_control
= k_replay_control_play
;
423 if( button_down( k_srbind_replay_freecam
) ){
424 skaterift
.freecam
= skaterift
.freecam
^ 0x1;
426 if( skaterift
.freecam
)
427 replay_get_camera( &skaterift
.replay
, &skaterift
.replay_freecam
);
429 skaterift_replay_update_helpers();
432 f32 target_speed
= axis_state( k_sraxis_replay_h
) * 5.0;
433 if( button_press( k_srbind_reset
) )
434 target_speed
+= -2.0;
436 if( fabsf(target_speed
) > 0.01f
)
437 skaterift
.replay_control
= k_replay_control_scrub
;
439 if( skaterift
.replay_control
== k_replay_control_play
)
442 vg_slewf( &skaterift
.track_velocity
, target_speed
,
443 18.0f
*vg
.time_frame_delta
);
445 if( fabsf( skaterift
.track_velocity
) > 0.0001f
){
446 f64 target
= skaterift
.replay
.cursor
;
447 target
+= skaterift
.track_velocity
* vg
.time_frame_delta
;
449 replay_seek( &skaterift
.replay
, target
);
452 if( button_down( k_srbind_mback
) ){
453 if( skaterift
.replay
.statehead
)
454 skaterift_restore_frame( skaterift
.replay
.statehead
);
456 skaterift
.activity
= k_skaterift_default
;
457 srinput
.state
= k_input_state_resume
;
461 if( skaterift
.freecam
){
462 //freecam_preupdate();
465 if( button_down( k_srbind_replay_resume
) ){
466 skaterift_replay_resume();
472 static void skaterift_replay_update_helpers(void){
473 skaterift
.helper_resume
->greyed
= skaterift
.freecam
;
476 vg_strnull( &freecam_text
, skaterift
.helper_freecam
->text
,
477 GUI_HELPER_TEXT_LENGTH
);
478 vg_strcat( &freecam_text
, skaterift
.freecam
? "exit freecam": "freecam" );
481 static void skaterift_replay_post_render(void){
482 if( world_static
.active_instance
!= k_world_purpose_client
)
485 /* capture the current resume frame at the very last point */
486 if( button_down( k_srbind_reset
) ){
487 if( skaterift
.activity
== k_skaterift_default
){
488 localplayer
.rewinded_since_last_gate
= 1;
489 skaterift
.activity
= k_skaterift_replay
;
490 skaterift_record_frame( &skaterift
.replay
, 1 );
491 if( skaterift
.replay
.head
){
492 skaterift
.replay
.cursor
= skaterift
.replay
.head
->time
;
493 skaterift
.replay
.cursor_frame
= skaterift
.replay
.head
;
495 skaterift
.replay_control
= k_replay_control_scrub
;
500 if( gui_new_helper( input_axis_list
[k_sraxis_replay_h
], &text
) )
501 vg_strcat( &text
, "scrub" );
503 if( (skaterift
.helper_resume
= gui_new_helper(
504 input_button_list
[k_srbind_replay_resume
], &text
)) )
505 vg_strcat( &text
, "resume" );
507 if( gui_new_helper( input_button_list
[k_srbind_replay_play
], &text
))
508 vg_strcat( &text
, "playback" );
510 skaterift
.helper_freecam
= gui_new_helper(
511 input_button_list
[k_srbind_replay_freecam
], &text
);
513 skaterift_replay_update_helpers();
519 static void skaterift_get_replay_camera( camera
*cam
){
520 if( skaterift
.freecam
){
523 v3_copy( skaterift
.replay_freecam
.pos
, cam
->pos
);
524 v3_copy( skaterift
.replay_freecam
.angles
, cam
->angles
);
525 cam
->fov
= skaterift
.replay_freecam
.fov
;
528 replay_get_camera( &skaterift
.replay
, &skaterift
.cam
);
533 static void skaterift_replay_debug_info(void){
534 player__debugtext( 2, "replay info" );
536 replay_buffer
*replay
= &skaterift
.replay
;
540 if( replay
->tail
) tail
= (void *)replay
->tail
- replay
->data
;
541 if( replay
->head
) head
= (void *)replay
->head
- replay
->data
;
543 player__debugtext( 1, "head @%u | tail @%u\n", head
, tail
);
545 if( replay
->statehead
){
546 for( u32 i
=0; i
<4; i
++ ){
547 player__debugtext( 1, "[%u]: [%hu, %hu]\n", i
,
548 replay
->statehead
->data_table
[i
][0],
549 replay
->statehead
->data_table
[i
][1] );
551 u32 state
= (void *)replay
->statehead
- replay
->data
;
552 player__debugtext( 1, "gs @%u\n", state
);
553 player__debugtext( 1, "gamestate_size: %hu\n",
554 replay
->statehead
->data_table
[k_replay_framedata_gamestate
][1] );
557 player__debugtext( 1, "gs @NULL\n" );
559 f64 start
= replay
->cursor
,
560 end
= replay
->cursor
;
561 if( replay
->tail
) start
= replay
->tail
->time
;
562 if( replay
->head
) end
= replay
->head
->time
;
564 f64 cur
= replay
->cursor
- start
,
567 player__debugtext( 1, "cursor: %.2fs / %.2fs\n", cur
, len
);
570 static void skaterift_replay_imgui(void){
571 if( skaterift
.activity
!= k_skaterift_replay
) return;
573 replay_buffer
*replay
= &skaterift
.replay
;
574 f64 start
= replay
->cursor
,
575 end
= replay
->cursor
;
576 if( replay
->tail
) start
= replay
->tail
->time
;
577 if( replay
->head
) end
= replay
->head
->time
;
578 f64 len
= end
- start
,
579 cur
= (replay
->cursor
- start
) / len
;
586 ui_rect bar
= { 0, 0, vg
.window_x
, height
};
587 ui_fill( bar
, ui_colour( k_ui_bg
) );
589 /* cursor frame block */
590 if( replay
->cursor_frame
){
591 if( replay
->cursor_frame
->r
){
592 f64 l
= (replay
->cursor_frame
->r
->time
-replay
->cursor_frame
->time
)/len
,
593 s
= (replay
->cursor_frame
->time
- start
) / len
;
594 ui_rect box
= { s
*(f64
)vg
.window_x
, 0,
595 VG_MAX(4,(ui_px
)(l
*vg
.window_x
)), bar
[3]+2 };
596 ui_fill( box
, ui_colour( k_ui_bg
+4 ) );
601 ui_rect cusor
= { cur
* (f64
)vg
.window_x
- (cwidth
/2), 0,
603 ui_fill( cusor
, ui_colour( k_ui_bg
+7 ) );
605 /* latest state marker */
606 if( replay
->statehead
){
607 f64 t
= (replay
->statehead
->time
- start
) / len
;
608 ui_rect tag
= { t
*(f64
)vg
.window_x
, 0, 2, bar
[3]+8 };
609 ui_fill( tag
, ui_colour( k_ui_green
+k_ui_brighter
) );
612 /* previous state marker */
613 replay_frame
*prev
= replay_find_recent_stateframe( replay
);
615 f64 t
= (prev
->time
- start
) / len
;
616 ui_rect tag
= { t
*(f64
)vg
.window_x
, 0, 2, bar
[3]+8 };
617 ui_fill( tag
, ui_colour( k_ui_yellow
+k_ui_brighter
) );
622 snprintf( buffer
, 128, "-%.2fs\n", (end
-replay
->cursor
) );
623 ui_text( cusor
, buffer
, 1, k_ui_align_middle_left
, 0 );
625 snprintf( buffer
, 128, "-%.2fs\n", len
);
626 ui_text( bar
, buffer
, 1, k_ui_align_middle_left
, 0 );
627 ui_text( bar
, "0s", 1, k_ui_align_middle_right
, 0 );
630 #endif /* PLAYER_REPLAY_C */