1 #ifndef PLAYER_REPLAY_C
2 #define PLAYER_REPLAY_C
4 #include "player_replay.h"
8 static void replay_clear( replay_buffer
*replay
){
11 replay
->cursor_frame
= NULL
;
12 replay
->statehead
= NULL
;
13 replay
->cursor
= -99999.9;
17 void * replay_frame_data( replay_frame
*frame
, enum replay_framedata type
){
18 void *baseptr
= frame
;
19 return baseptr
+ frame
->data_table
[type
][0];
23 replay_frame_calculate_data_offsets( u16 data_table
[4][2] ){
24 u32 total
= vg_align8( sizeof(replay_frame
) );
25 for( u32 i
=0; i
<4; i
++ ){
26 data_table
[i
][0] = total
;
27 total
+= vg_align8(data_table
[i
][1]);
30 vg_fatal_error( "Exceeded frame storage capacity\n" );
35 static void replay_tailpop( replay_buffer
*replay
){
36 if( replay
->cursor_frame
== replay
->tail
)
37 replay
->cursor_frame
= NULL
;
38 if( replay
->statehead
== replay
->tail
)
39 replay
->statehead
= NULL
;
41 replay
->tail
= replay
->tail
->r
;
44 replay
->tail
->l
= NULL
;
49 static replay_frame
*replay_newframe( replay_buffer
*replay
,
54 data_table
[ k_replay_framedata_animator
][1] = animator_size
;
55 data_table
[ k_replay_framedata_gamestate
][1] = gamestate_size
;
56 data_table
[ k_replay_framedata_sfx
][1] = 0;
57 data_table
[ k_replay_framedata_internal_gamestate
][1] = 0;
59 data_table
[ k_replay_framedata_internal_gamestate
][1] =
60 sizeof( replay_gamestate
);
63 u32 nextsize
= replay_frame_calculate_data_offsets( data_table
);
65 replay_frame
*frame
= NULL
;
67 u32 headsize
= replay
->head
->total_size
,
68 nextpos
= ((void *)replay
->head
- replay
->data
) + headsize
;
70 if( nextpos
+ nextsize
> replay
->size
){
73 /* maintain contiguity */
74 while( replay
->tail
){
75 if( (void *)replay
->tail
- replay
->data
)
76 replay_tailpop( replay
);
82 u32 tailpos
= (void *)replay
->tail
- replay
->data
;
84 if( tailpos
>= nextpos
){
85 if( nextpos
+ nextsize
> tailpos
){
86 replay_tailpop( replay
);
93 frame
= replay
->data
+ nextpos
;
96 replay
->head
->r
= frame
;
101 for( u32 i
=0; i
<4; i
++ ){
102 frame
->data_table
[i
][0] = data_table
[i
][0];
103 frame
->data_table
[i
][1] = data_table
[i
][1];
106 frame
->total_size
= nextsize
;
107 frame
->l
= replay
->head
;
109 replay
->head
= frame
;
110 if( !replay
->tail
) replay
->tail
= frame
;
111 if( gamestate_size
) replay
->statehead
= frame
;
116 static void replay_seek( replay_buffer
*replay
, f64 t
){
117 if( !replay
->head
) return;
118 assert( replay
->tail
);
120 if( t
< replay
->tail
->time
) t
= replay
->tail
->time
;
121 if( t
> replay
->head
->time
) t
= replay
->head
->time
;
123 if( !replay
->cursor_frame
) {
124 replay
->cursor
= replay
->head
->time
;
125 replay
->cursor_frame
= replay
->head
;
127 if( fabs(replay
->head
->time
-t
) > fabs(replay
->tail
->time
-t
) ){
128 replay
->cursor
= replay
->tail
->time
;
129 replay
->cursor_frame
= replay
->tail
;
133 f64 dir
= t
- replay
->cursor
;
134 if( dir
== 0.0 ) return;
135 dir
= vg_signf( dir
);
140 if( t
> replay
->cursor_frame
->time
) break;
143 if( dir
> 0.0 ) next
= replay
->cursor_frame
->r
;
144 else next
= replay
->cursor_frame
->l
;
149 if( t
< next
->time
) break;
151 replay
->cursor_frame
= next
;
152 replay
->cursor
= next
->time
;
160 static replay_frame
*replay_find_recent_stateframe( replay_buffer
*replay
){
161 replay_frame
*frame
= replay
->cursor_frame
;
164 if( !frame
) return frame
;
165 if( frame
->data_table
[ k_replay_framedata_gamestate
][1] ) return frame
;
172 static f32
replay_subframe_time( replay_buffer
*replay
){
173 replay_frame
*frame
= replay
->cursor_frame
;
174 if( !frame
) return 0.0f
;
175 replay_frame
*next
= frame
->r
;
177 f64 l
= next
->time
- frame
->time
,
178 t
= (replay
->cursor
- frame
->time
) / l
;
179 return vg_clampf( t
, 0.0f
, 1.0f
);
185 static void replay_get_frame_camera( replay_frame
*frame
, camera
*cam
){
186 cam
->fov
= frame
->cam_fov
;
187 v3_copy( frame
->cam_pos
, cam
->pos
);
188 v3_copy( frame
->cam_angles
, cam
->angles
);
191 static void replay_get_camera( replay_buffer
*replay
, camera
*cam
){
194 if( replay
->cursor_frame
){
195 replay_frame
*next
= replay
->cursor_frame
->r
;
200 replay_get_frame_camera( replay
->cursor_frame
, cam
);
201 replay_get_frame_camera( next
, &temp
);
202 camera_lerp( cam
, &temp
, replay_subframe_time( replay
), cam
);
205 replay_get_frame_camera( replay
->cursor_frame
, cam
);
210 v3_zero( cam
->angles
);
221 void skaterift_record_frame( replay_buffer
*replay
, int force_gamestate
){
222 f64 delta
= 9999999.9,
223 statedelta
= 9999999.9;
226 delta
= vg
.time
- replay
->head
->time
;
228 if( replay
->statehead
)
229 statedelta
= vg
.time
- replay
->statehead
->time
;
231 const f64 k_replay_rate
= 1.0/30.0,
232 k_gamestate_rate
= 0.5;
237 if( force_gamestate
) save_state
= 1;
238 if( statedelta
> k_gamestate_rate
) save_state
= 1;
239 if( delta
> k_replay_rate
) save_frame
= 1;
240 if( save_state
) save_frame
= 1;
242 if( !save_frame
) return;
244 u16 gamestate_size
= 0;
246 gamestate_size
= (u32
[]){
247 [k_player_subsystem_walk
] = sizeof(struct player_walk_state
),
248 [k_player_subsystem_drive
] = 0,
249 [k_player_subsystem_skate
] = sizeof(struct player_skate_state
),
250 [k_player_subsystem_dead
] = localplayer
.ragdoll
.part_count
*
251 sizeof(struct replay_rb
)
252 }[ localplayer
.subsystem
];
255 u16 animator_size
= (u16
[]){
256 [k_player_subsystem_walk
] = sizeof(struct player_walk_animator
),
257 [k_player_subsystem_drive
] = 0,
258 [k_player_subsystem_skate
] = sizeof(struct player_skate_animator
),
259 [k_player_subsystem_dead
] = sizeof(struct player_dead_animator
)
260 }[ localplayer
.subsystem
];
262 replay_frame
*frame
= replay_newframe( replay
,
263 animator_size
, gamestate_size
, 0 );
264 frame
->system
= localplayer
.subsystem
;
267 replay_gamestate
*gs
=
268 replay_frame_data( frame
, k_replay_framedata_internal_gamestate
);
270 /* permanent block */
271 memcpy( &gs
->rb
, &localplayer
.rb
, sizeof(rigidbody
) );
272 memcpy( &gs
->cam_control
, &localplayer
.cam_control
,
273 sizeof(struct player_cam_controller
) );
274 v3_copy( localplayer
.angles
, gs
->angles
);
276 void *dst
= replay_frame_data( frame
, k_replay_framedata_gamestate
);
278 /* subsytem/dynamic block */
279 if( localplayer
.subsystem
== k_player_subsystem_walk
)
280 memcpy( dst
, &player_walk
.state
, gamestate_size
);
281 else if( localplayer
.subsystem
== k_player_subsystem_skate
)
282 memcpy( dst
, &player_skate
.state
, gamestate_size
);
283 else if( localplayer
.subsystem
== k_player_subsystem_dead
){
284 struct replay_rb
*arr
= dst
;
285 for( u32 i
=0; i
<localplayer
.ragdoll
.part_count
; i
++ ){
286 rigidbody
*rb
= &localplayer
.ragdoll
.parts
[i
].obj
.rb
;
287 v3_copy( rb
->co
, arr
[i
].co
);
288 v3_copy( rb
->w
, arr
[i
].w
);
289 v3_copy( rb
->v
, arr
[i
].v
);
290 v4_copy( rb
->q
, arr
[i
].q
);
295 replay
->cursor
= vg
.time
;
296 replay
->cursor_frame
= frame
;
297 frame
->time
= vg
.time
;
300 v3_copy( localplayer
.cam
.pos
, frame
->cam_pos
);
301 if( localplayer
.gate_waiting
){
302 m4x3_mulv( localplayer
.gate_waiting
->transport
,
303 frame
->cam_pos
, frame
->cam_pos
);
305 v3_copy( localplayer
.cam
.angles
, frame
->cam_angles
);
306 frame
->cam_fov
= localplayer
.cam
.fov
;
309 void *dst
= replay_frame_data( frame
, k_replay_framedata_animator
);
311 if( localplayer
.subsystem
== k_player_subsystem_walk
)
312 memcpy( dst
, &player_walk
.animator
, animator_size
);
313 else if( localplayer
.subsystem
== k_player_subsystem_skate
)
314 memcpy( dst
, &player_skate
.animator
, animator_size
);
315 else if( localplayer
.subsystem
== k_player_subsystem_dead
){
316 memcpy( dst
, &player_dead
.animator
, animator_size
);
321 void skaterift_restore_frame( replay_frame
*frame
){
322 replay_gamestate
*gs
=
323 replay_frame_data( frame
, k_replay_framedata_internal_gamestate
);
324 void *src
= replay_frame_data( frame
, k_replay_framedata_gamestate
);
325 u16 src_size
= frame
->data_table
[ k_replay_framedata_gamestate
][1];
328 if(frame
->system
== k_player_subsystem_walk
){
329 memcpy( &player_walk
.state
, src
, src_size
);
331 else if( frame
->system
== k_player_subsystem_skate
){
332 memcpy( &player_skate
.state
, src
, src_size
);
334 else if( frame
->system
== k_player_subsystem_dead
){
335 player__dead_transition();
336 struct replay_rb
*arr
= src
;
338 for( u32 i
=0; i
<localplayer
.ragdoll
.part_count
; i
++ ){
339 struct ragdoll_part
*part
= &localplayer
.ragdoll
.parts
[i
];
340 rigidbody
*rb
= &part
->obj
.rb
;
342 v3_copy( arr
[i
].co
, rb
->co
);
343 v3_copy( arr
[i
].w
, rb
->w
);
344 v3_copy( arr
[i
].v
, rb
->v
);
345 v4_copy( arr
[i
].q
, rb
->q
);
347 v3_copy( arr
[i
].co
, part
->prev_co
);
348 v4_copy( arr
[i
].q
, part
->prev_q
);
352 localplayer
.subsystem
= frame
->system
;
354 memcpy( &localplayer
.rb
, &gs
->rb
, sizeof(rigidbody
) );
355 v3_copy( gs
->angles
, localplayer
.angles
);
357 v3_copy( frame
->cam_pos
, localplayer
.cam
.pos
);
358 v3_copy( frame
->cam_angles
, localplayer
.cam
.angles
);
359 localplayer
.cam
.fov
= frame
->cam_fov
;
361 memcpy( &localplayer
.cam_control
, &gs
->cam_control
,
362 sizeof(struct player_cam_controller
) );
364 /* chop end off replay */
366 skaterift
.replay
.statehead
= frame
;
367 skaterift
.replay
.head
= frame
;
368 skaterift
.replay
.cursor_frame
= frame
;
369 skaterift
.replay
.cursor
= frame
->time
;
370 skaterift
.replay_control
= k_replay_control_scrub
;
371 skaterift
.activity
= k_skaterift_default
;
372 vg
.time
= frame
->time
;
375 static void skaterift_replay_resume(void){
376 replay_frame
*prev
= replay_find_recent_stateframe(&skaterift
.replay
);
379 skaterift
.replay_control
= k_replay_control_resume
;
380 skaterift
.resume_target
= prev
;
381 skaterift
.resume_begin
= skaterift
.replay
.cursor
;
382 skaterift
.resume_transition
= 0.0f
;
386 static void skaterift_replay_pre_update(void){
387 if( skaterift
.activity
!= k_skaterift_replay
) return;
389 if( skaterift
.replay_control
== k_replay_control_resume
){
390 if( skaterift
.replay
.cursor_frame
== skaterift
.resume_target
||
391 skaterift
.replay
.cursor_frame
== NULL
){
392 skaterift_restore_frame( skaterift
.resume_target
);
395 vg_slewf( &skaterift
.resume_transition
, 1.0f
,
396 vg
.time_frame_delta
* (1.0f
/1.0f
) );
398 if( skaterift
.resume_transition
>= 1.0f
)
399 skaterift_restore_frame( skaterift
.resume_target
);
401 f64 target
= vg_lerp( skaterift
.resume_begin
,
402 skaterift
.resume_target
->time
,
403 vg_smoothstepf( skaterift
.resume_transition
) );
404 replay_seek( &skaterift
.replay
, target
);
409 if( button_down( k_srbind_replay_play
) )
410 skaterift
.replay_control
= k_replay_control_play
;
411 if( button_down( k_srbind_replay_freecam
) ){
412 skaterift
.freecam
= skaterift
.freecam
^ 0x1;
414 if( skaterift
.freecam
)
415 replay_get_camera( &skaterift
.replay
, &skaterift
.replay_freecam
);
418 f32 target_speed
= axis_state( k_sraxis_replay_h
) * 5.0;
419 if( button_press( k_srbind_reset
) )
420 target_speed
+= -2.0;
422 if( fabsf(target_speed
) > 0.01f
)
423 skaterift
.replay_control
= k_replay_control_scrub
;
425 if( skaterift
.replay_control
== k_replay_control_play
)
428 vg_slewf( &skaterift
.track_velocity
, target_speed
,
429 18.0f
*vg
.time_frame_delta
);
431 if( fabsf( skaterift
.track_velocity
) > 0.0001f
){
432 f64 target
= skaterift
.replay
.cursor
;
433 target
+= skaterift
.track_velocity
* vg
.time_frame_delta
;
435 replay_seek( &skaterift
.replay
, target
);
438 if( button_down( k_srbind_mback
) ){
439 if( skaterift
.replay
.statehead
)
440 skaterift_restore_frame( skaterift
.replay
.statehead
);
442 skaterift
.activity
= k_skaterift_default
;
443 srinput
.state
= k_input_state_resume
;
446 gui_helper_action( button_display_string(k_srbind_replay_play
), "play" );
447 gui_helper_action( axis_display_string(k_sraxis_replay_h
), "scrub" );
448 if( skaterift
.freecam
){
449 gui_helper_action( button_display_string(k_srbind_replay_freecam
),
452 camera
*cam
= &skaterift
.replay_freecam
;
454 v3_copy( cam
->angles
, angles
);
455 player_look( angles
, 1.0f
);
457 f32 decay
= vg_maxf(0.0f
,1.0f
-vg
.time_frame_delta
*10.0f
);
460 v3_sub( angles
, cam
->angles
, d
);
461 v3_muladds( skaterift
.freecam_w
, d
, 20.0f
, skaterift
.freecam_w
);
462 v3_muls( skaterift
.freecam_w
, decay
, skaterift
.freecam_w
);
463 v3_muladds( cam
->angles
, skaterift
.freecam_w
, vg
.time_frame_delta
,
465 cam
->angles
[1] = vg_clampf( cam
->angles
[1], -VG_PIf
*0.5f
,VG_PIf
*0.5f
);
467 camera_update_transform( cam
);
469 v3f lookdir
= { 0.0f
, 0.0f
, -1.0f
},
470 sidedir
= { 1.0f
, 0.0f
, 0.0f
};
473 m3x3_mul( cam
->transform
, localplayer
.basis
, mtx
);
475 m3x3_mulv( mtx
, lookdir
, lookdir
);
476 m3x3_mulv( mtx
, sidedir
, sidedir
);
479 joystick_state( k_srjoystick_steer
, input
);
480 v2_muls( input
, vg
.time_frame_delta
*6.0f
*20.0f
, input
);
482 v3_muladds( skaterift
.freecam_v
, lookdir
, -input
[1],
483 skaterift
.freecam_v
);
484 v3_muladds( skaterift
.freecam_v
, sidedir
, input
[0],
485 skaterift
.freecam_v
);
487 v3_muls( skaterift
.freecam_v
, decay
, skaterift
.freecam_v
);
488 v3_muladds( cam
->pos
,
489 skaterift
.freecam_v
, vg
.time_frame_delta
, cam
->pos
);
492 gui_helper_action( button_display_string(k_srbind_replay_resume
),
494 gui_helper_action( button_display_string(k_srbind_replay_freecam
),
497 if( button_down( k_srbind_replay_resume
) ){
498 skaterift_replay_resume();
504 static void skaterift_get_replay_camera( camera
*cam
){
505 if( skaterift
.freecam
){
508 v3_copy( skaterift
.replay_freecam
.pos
, cam
->pos
);
509 v3_copy( skaterift
.replay_freecam
.angles
, cam
->angles
);
510 cam
->fov
= skaterift
.replay_freecam
.fov
;
513 replay_get_camera( &skaterift
.replay
, &skaterift
.cam
);
517 static void skaterift_replay_debug_info(void){
518 player__debugtext( 2, "replay info" );
520 replay_buffer
*replay
= &skaterift
.replay
;
524 if( replay
->tail
) tail
= (void *)replay
->tail
- replay
->data
;
525 if( replay
->head
) head
= (void *)replay
->head
- replay
->data
;
527 player__debugtext( 1, "head @%u | tail @%u\n", head
, tail
);
529 if( replay
->statehead
){
530 for( u32 i
=0; i
<4; i
++ ){
531 player__debugtext( 1, "[%u]: [%hu, %hu]\n", i
,
532 replay
->statehead
->data_table
[i
][0],
533 replay
->statehead
->data_table
[i
][1] );
535 u32 state
= (void *)replay
->statehead
- replay
->data
;
536 player__debugtext( 1, "gs @%u\n", state
);
537 player__debugtext( 1, "gamestate_size: %hu\n",
538 replay
->statehead
->data_table
[k_replay_framedata_gamestate
][1] );
541 player__debugtext( 1, "gs @NULL\n" );
543 f64 start
= replay
->cursor
,
544 end
= replay
->cursor
;
545 if( replay
->tail
) start
= replay
->tail
->time
;
546 if( replay
->head
) end
= replay
->head
->time
;
548 f64 cur
= replay
->cursor
- start
,
551 player__debugtext( 1, "cursor: %.2fs / %.2fs\n", cur
, len
);
554 static void skaterift_replay_imgui(void){
555 if( skaterift
.activity
!= k_skaterift_replay
) return;
557 replay_buffer
*replay
= &skaterift
.replay
;
558 f64 start
= replay
->cursor
,
559 end
= replay
->cursor
;
560 if( replay
->tail
) start
= replay
->tail
->time
;
561 if( replay
->head
) end
= replay
->head
->time
;
562 f64 len
= end
- start
,
563 cur
= (replay
->cursor
- start
) / len
;
570 ui_rect bar
= { 0, 0, vg
.window_x
, height
};
571 ui_fill( bar
, ui_colour( k_ui_bg
) );
573 /* cursor frame block */
574 if( replay
->cursor_frame
){
575 if( replay
->cursor_frame
->r
){
576 f64 l
= (replay
->cursor_frame
->r
->time
-replay
->cursor_frame
->time
)/len
,
577 s
= (replay
->cursor_frame
->time
- start
) / len
;
578 ui_rect box
= { s
*(f64
)vg
.window_x
, 0,
579 VG_MAX(4,(ui_px
)(l
*vg
.window_x
)), bar
[3]+2 };
580 ui_fill( box
, ui_colour( k_ui_bg
+4 ) );
585 ui_rect cusor
= { cur
* (f64
)vg
.window_x
- (cwidth
/2), 0,
587 ui_fill( cusor
, ui_colour( k_ui_bg
+7 ) );
589 /* latest state marker */
590 if( replay
->statehead
){
591 f64 t
= (replay
->statehead
->time
- start
) / len
;
592 ui_rect tag
= { t
*(f64
)vg
.window_x
, 0, 2, bar
[3]+8 };
593 ui_fill( tag
, ui_colour( k_ui_green
+k_ui_brighter
) );
596 /* previous state marker */
597 replay_frame
*prev
= replay_find_recent_stateframe( replay
);
599 f64 t
= (prev
->time
- start
) / len
;
600 ui_rect tag
= { t
*(f64
)vg
.window_x
, 0, 2, bar
[3]+8 };
601 ui_fill( tag
, ui_colour( k_ui_yellow
+k_ui_brighter
) );
606 snprintf( buffer
, 128, "-%.2fs\n", (end
-replay
->cursor
) );
607 ui_text( cusor
, buffer
, 1, k_ui_align_middle_left
, 0 );
609 snprintf( buffer
, 128, "-%.2fs\n", len
);
610 ui_text( bar
, buffer
, 1, k_ui_align_middle_left
, 0 );
611 ui_text( bar
, "0s", 1, k_ui_align_middle_right
, 0 );
615 snprintf( buf
, 256, "scrub: %s\nresume: %s\nplay: %s\n",
616 axis_display_string( k_sraxis_replay_h
),
617 button_display_string( k_srbind_replay_resume
),
618 button_display_string( k_srbind_replay_play
) );
620 ui_rect info
= { 0, vg
.window_y
-bar
[3]-128, 256, 128 };
621 ui_text( info
, buf
, 2,0,0 );
625 #endif /* PLAYER_REPLAY_C */