1 #ifndef PLAYER_REPLAY_C
2 #define PLAYER_REPLAY_C
4 #include "player_replay.h"
8 VG_STATIC
void replay_clear( replay_buffer
*replay
){
11 replay
->cursor_frame
= NULL
;
12 replay
->statehead
= NULL
;
13 replay
->cursor
= -99999.9;
17 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 VG_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 VG_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 assert( replay
->head
);
69 u32 headsize
= replay
->head
->total_size
,
70 nextpos
= ((void *)replay
->head
- replay
->data
) + headsize
;
72 if( nextsize
> replay
->size
){
73 vg_error( "Keyframe too big\n" );
77 if( nextpos
+ nextsize
> replay
->size
){
80 /* maintain contiguity */
81 while( replay
->tail
){
82 if( (void *)replay
->tail
- replay
->data
)
83 replay_tailpop( replay
);
89 u32 tailpos
= (void *)replay
->tail
- replay
->data
;
91 if( tailpos
>= nextpos
){
92 if( nextpos
+ nextsize
> tailpos
){
93 replay_tailpop( replay
);
100 frame
= replay
->data
+ nextpos
;
103 replay
->head
->r
= frame
;
106 frame
= replay
->data
;
108 for( u32 i
=0; i
<4; i
++ ){
109 frame
->data_table
[i
][0] = data_table
[i
][0];
110 frame
->data_table
[i
][1] = data_table
[i
][1];
113 frame
->total_size
= nextsize
;
114 frame
->l
= replay
->head
;
116 replay
->head
= frame
;
117 if( !replay
->tail
) replay
->tail
= frame
;
118 if( gamestate_size
) replay
->statehead
= frame
;
123 VG_STATIC
void replay_seek( replay_buffer
*replay
, f64 t
){
124 if( !replay
->head
) return;
125 assert( replay
->tail
);
127 if( t
< replay
->tail
->time
) t
= replay
->tail
->time
;
128 if( t
> replay
->head
->time
) t
= replay
->head
->time
;
130 if( !replay
->cursor_frame
) {
131 replay
->cursor
= replay
->head
->time
;
132 replay
->cursor_frame
= replay
->head
;
134 if( fabs(replay
->head
->time
-t
) > fabs(replay
->tail
->time
-t
) ){
135 replay
->cursor
= replay
->tail
->time
;
136 replay
->cursor_frame
= replay
->tail
;
140 f64 dir
= t
- replay
->cursor
;
141 if( dir
== 0.0 ) return;
142 dir
= vg_signf( dir
);
147 if( t
> replay
->cursor_frame
->time
) break;
150 if( dir
> 0.0 ) next
= replay
->cursor_frame
->r
;
151 else next
= replay
->cursor_frame
->l
;
156 if( t
< next
->time
) break;
158 replay
->cursor_frame
= next
;
159 replay
->cursor
= next
->time
;
167 VG_STATIC replay_frame
*replay_find_recent_stateframe( replay_buffer
*replay
){
168 replay_frame
*frame
= replay
->cursor_frame
;
171 if( !frame
) return frame
;
172 if( frame
->data_table
[ k_replay_framedata_gamestate
][1] ) return frame
;
179 VG_STATIC f32
replay_subframe_time( replay_buffer
*replay
){
180 replay_frame
*frame
= replay
->cursor_frame
;
181 if( !frame
) return 0.0f
;
182 replay_frame
*next
= frame
->r
;
184 f64 l
= next
->time
- frame
->time
,
185 t
= (replay
->cursor
- frame
->time
) / l
;
186 return vg_clampf( t
, 0.0f
, 1.0f
);
192 VG_STATIC
void replay_get_frame_camera( replay_frame
*frame
, camera
*cam
){
193 cam
->fov
= frame
->cam_fov
;
194 v3_copy( frame
->cam_pos
, cam
->pos
);
195 v3_copy( frame
->cam_angles
, cam
->angles
);
198 VG_STATIC
void replay_get_camera( replay_buffer
*replay
, camera
*cam
){
201 if( replay
->cursor_frame
){
202 replay_frame
*next
= replay
->cursor_frame
->r
;
207 replay_get_frame_camera( replay
->cursor_frame
, cam
);
208 replay_get_frame_camera( next
, &temp
);
209 camera_lerp( cam
, &temp
, replay_subframe_time( replay
), cam
);
212 replay_get_frame_camera( replay
->cursor_frame
, cam
);
217 v3_zero( cam
->angles
);
228 void skaterift_record_frame( replay_buffer
*replay
, int force_gamestate
){
229 f64 delta
= 9999999.9,
230 statedelta
= 9999999.9;
233 delta
= vg
.time
- replay
->head
->time
;
235 if( replay
->statehead
)
236 statedelta
= vg
.time
- replay
->statehead
->time
;
238 const f64 k_replay_rate
= 1.0/30.0,
239 k_gamestate_rate
= 0.5;
244 if( force_gamestate
) save_state
= 1;
245 if( statedelta
> k_gamestate_rate
) save_state
= 1;
246 if( delta
> k_replay_rate
) save_frame
= 1;
247 if( save_state
) save_frame
= 1;
249 if( !save_frame
) return;
251 u16 gamestate_size
= 0;
253 gamestate_size
= (u32
[]){
254 [k_player_subsystem_walk
] = sizeof(struct player_walk_state
),
255 [k_player_subsystem_drive
] = 0,
256 [k_player_subsystem_skate
] = sizeof(struct player_skate_state
),
257 [k_player_subsystem_dead
] = localplayer
.ragdoll
.part_count
*
258 sizeof(struct replay_rb
)
259 }[ localplayer
.subsystem
];
262 u16 animator_size
= (u16
[]){
263 [k_player_subsystem_walk
] = sizeof(struct player_walk_animator
),
264 [k_player_subsystem_drive
] = 0,
265 [k_player_subsystem_skate
] = sizeof(struct player_skate_animator
),
266 [k_player_subsystem_dead
] = sizeof(struct player_dead_animator
)
267 }[ localplayer
.subsystem
];
269 replay_frame
*frame
= replay_newframe( replay
,
270 animator_size
, gamestate_size
, 0 );
271 frame
->system
= localplayer
.subsystem
;
274 replay_gamestate
*gs
=
275 replay_frame_data( frame
, k_replay_framedata_internal_gamestate
);
277 /* permanent block */
278 memcpy( &gs
->rb
, &localplayer
.rb
, sizeof(rigidbody
) );
279 memcpy( &gs
->cam_control
, &localplayer
.cam_control
,
280 sizeof(struct player_cam_controller
) );
281 v3_copy( localplayer
.angles
, gs
->angles
);
283 void *dst
= replay_frame_data( frame
, k_replay_framedata_gamestate
);
285 /* subsytem/dynamic block */
286 if( localplayer
.subsystem
== k_player_subsystem_walk
)
287 memcpy( dst
, &localplayer
._walk
.state
, gamestate_size
);
288 else if( localplayer
.subsystem
== k_player_subsystem_skate
)
289 memcpy( dst
, &localplayer
._skate
.state
, gamestate_size
);
290 else if( localplayer
.subsystem
== k_player_subsystem_dead
){
291 struct replay_rb
*arr
= dst
;
292 for( u32 i
=0; i
<localplayer
.ragdoll
.part_count
; i
++ ){
293 rigidbody
*rb
= &localplayer
.ragdoll
.parts
[i
].obj
.rb
;
294 v3_copy( rb
->co
, arr
[i
].co
);
295 v3_copy( rb
->w
, arr
[i
].w
);
296 v3_copy( rb
->v
, arr
[i
].v
);
297 v4_copy( rb
->q
, arr
[i
].q
);
302 replay
->cursor
= vg
.time
;
303 replay
->cursor_frame
= frame
;
304 frame
->time
= vg
.time
;
307 v3_copy( localplayer
.cam
.pos
, frame
->cam_pos
);
308 if( localplayer
.gate_waiting
){
309 m4x3_mulv( localplayer
.gate_waiting
->transport
,
310 frame
->cam_pos
, frame
->cam_pos
);
312 v3_copy( localplayer
.cam
.angles
, frame
->cam_angles
);
313 frame
->cam_fov
= localplayer
.cam
.fov
;
316 void *dst
= replay_frame_data( frame
, k_replay_framedata_animator
);
318 if( localplayer
.subsystem
== k_player_subsystem_walk
)
319 memcpy( dst
, &localplayer
._walk
.animator
, animator_size
);
320 else if( localplayer
.subsystem
== k_player_subsystem_skate
)
321 memcpy( dst
, &localplayer
._skate
.animator
, animator_size
);
322 else if( localplayer
.subsystem
== k_player_subsystem_dead
){
323 memcpy( dst
, &localplayer
._dead
.animator
, animator_size
);
328 void skaterift_restore_frame( replay_frame
*frame
){
329 replay_gamestate
*gs
=
330 replay_frame_data( frame
, k_replay_framedata_internal_gamestate
);
331 void *src
= replay_frame_data( frame
, k_replay_framedata_gamestate
);
332 u16 src_size
= frame
->data_table
[ k_replay_framedata_gamestate
][1];
335 if(frame
->system
== k_player_subsystem_walk
){
336 memcpy( &localplayer
._walk
.state
, src
, src_size
);
338 else if( frame
->system
== k_player_subsystem_skate
){
339 memcpy( &localplayer
._skate
.state
, src
, src_size
);
341 else if( frame
->system
== k_player_subsystem_dead
){
342 player__dead_transition( &localplayer
);
343 struct replay_rb
*arr
= src
;
345 for( u32 i
=0; i
<localplayer
.ragdoll
.part_count
; i
++ ){
346 struct ragdoll_part
*part
= &localplayer
.ragdoll
.parts
[i
];
347 rigidbody
*rb
= &part
->obj
.rb
;
349 v3_copy( arr
[i
].co
, rb
->co
);
350 v3_copy( arr
[i
].w
, rb
->w
);
351 v3_copy( arr
[i
].v
, rb
->v
);
352 v4_copy( arr
[i
].q
, rb
->q
);
354 v3_copy( arr
[i
].co
, part
->prev_co
);
355 v4_copy( arr
[i
].q
, part
->prev_q
);
359 localplayer
.subsystem
= frame
->system
;
361 memcpy( &localplayer
.rb
, &gs
->rb
, sizeof(rigidbody
) );
362 v3_copy( gs
->angles
, localplayer
.angles
);
364 v3_copy( frame
->cam_pos
, localplayer
.cam
.pos
);
365 v3_copy( frame
->cam_angles
, localplayer
.cam
.angles
);
366 localplayer
.cam
.fov
= frame
->cam_fov
;
368 memcpy( &localplayer
.cam_control
, &gs
->cam_control
,
369 sizeof(struct player_cam_controller
) );
371 /* chop end off replay */
373 skaterift
.replay
.statehead
= frame
;
374 skaterift
.replay
.head
= frame
;
375 skaterift
.replay
.cursor_frame
= frame
;
376 skaterift
.replay
.cursor
= frame
->time
;
377 skaterift
.replay_control
= k_replay_control_scrub
;
378 skaterift
.activity
= k_skaterift_default
;
379 vg
.time
= frame
->time
;
382 VG_STATIC
void skaterift_replay_resume(void){
383 replay_frame
*prev
= replay_find_recent_stateframe(&skaterift
.replay
);
386 skaterift
.replay_control
= k_replay_control_resume
;
387 skaterift
.resume_target
= prev
;
388 skaterift
.resume_begin
= skaterift
.replay
.cursor
;
389 skaterift
.resume_transition
= 0.0f
;
393 VG_STATIC
void skaterift_replay_pre_update(void){
394 if( skaterift
.activity
!= k_skaterift_replay
) return;
396 if( skaterift
.replay_control
== k_replay_control_resume
){
397 if( skaterift
.replay
.cursor_frame
== skaterift
.resume_target
||
398 skaterift
.replay
.cursor_frame
== NULL
){
399 skaterift_restore_frame( skaterift
.resume_target
);
402 vg_slewf( &skaterift
.resume_transition
, 1.0f
,
403 vg
.time_frame_delta
* (1.0f
/1.0f
) );
405 if( skaterift
.resume_transition
>= 1.0f
)
406 skaterift_restore_frame( skaterift
.resume_target
);
408 f64 target
= vg_lerp( skaterift
.resume_begin
,
409 skaterift
.resume_target
->time
,
410 vg_smoothstepf( skaterift
.resume_transition
) );
411 replay_seek( &skaterift
.replay
, target
);
416 if( button_down( k_srbind_replay_play
) )
417 skaterift
.replay_control
= k_replay_control_play
;
418 if( button_down( k_srbind_replay_freecam
) ){
419 skaterift
.freecam
= skaterift
.freecam
^ 0x1;
421 if( skaterift
.freecam
)
422 replay_get_camera( &skaterift
.replay
, &skaterift
.replay_freecam
);
425 f32 target_speed
= axis_state( k_sraxis_replay_h
) * 5.0;
426 if( button_press( k_srbind_reset
) )
427 target_speed
+= -2.0;
429 if( fabsf(target_speed
) > 0.01f
)
430 skaterift
.replay_control
= k_replay_control_scrub
;
432 if( skaterift
.replay_control
== k_replay_control_play
)
435 vg_slewf( &skaterift
.track_velocity
, target_speed
,
436 18.0f
*vg
.time_frame_delta
);
438 if( fabsf( skaterift
.track_velocity
) > 0.0001f
){
439 f64 target
= skaterift
.replay
.cursor
;
440 target
+= skaterift
.track_velocity
* vg
.time_frame_delta
;
442 replay_seek( &skaterift
.replay
, target
);
445 if( button_down( k_srbind_mback
) ){
446 if( skaterift
.replay
.statehead
)
447 skaterift_restore_frame( skaterift
.replay
.statehead
);
449 skaterift
.activity
= k_skaterift_default
;
453 gui_helper_action( button_display_string(k_srbind_replay_play
), "play" );
454 gui_helper_action( axis_display_string(k_sraxis_replay_h
), "scrub" );
455 if( skaterift
.freecam
){
456 gui_helper_action( button_display_string(k_srbind_replay_freecam
),
459 camera
*cam
= &skaterift
.replay_freecam
;
461 v3_copy( cam
->angles
, angles
);
462 player_look( angles
, 1.0f
);
464 f32 decay
= vg_maxf(0.0f
,1.0f
-vg
.time_frame_delta
*10.0f
);
467 v3_sub( angles
, cam
->angles
, d
);
468 v3_muladds( skaterift
.freecam_w
, d
, 20.0f
, skaterift
.freecam_w
);
469 v3_muls( skaterift
.freecam_w
, decay
, skaterift
.freecam_w
);
470 v3_muladds( cam
->angles
, skaterift
.freecam_w
, vg
.time_frame_delta
,
472 cam
->angles
[1] = vg_clampf( cam
->angles
[1], -VG_PIf
*0.5f
,VG_PIf
*0.5f
);
474 camera_update_transform( cam
);
476 v3f lookdir
= { 0.0f
, 0.0f
, -1.0f
},
477 sidedir
= { 1.0f
, 0.0f
, 0.0f
};
480 m3x3_mul( cam
->transform
, localplayer
.basis
, mtx
);
482 m3x3_mulv( mtx
, lookdir
, lookdir
);
483 m3x3_mulv( mtx
, sidedir
, sidedir
);
486 joystick_state( k_srjoystick_steer
, input
);
487 v2_muls( input
, vg
.time_frame_delta
*6.0f
*20.0f
, input
);
489 v3_muladds( skaterift
.freecam_v
, lookdir
, -input
[1],
490 skaterift
.freecam_v
);
491 v3_muladds( skaterift
.freecam_v
, sidedir
, input
[0],
492 skaterift
.freecam_v
);
494 v3_muls( skaterift
.freecam_v
, decay
, skaterift
.freecam_v
);
495 v3_muladds( cam
->pos
,
496 skaterift
.freecam_v
, vg
.time_frame_delta
, cam
->pos
);
499 gui_helper_action( button_display_string(k_srbind_replay_resume
),
501 gui_helper_action( button_display_string(k_srbind_replay_freecam
),
504 if( button_down( k_srbind_replay_resume
) ){
505 skaterift_replay_resume();
511 VG_STATIC
void skaterift_get_replay_camera( camera
*cam
){
512 if( skaterift
.freecam
){
515 v3_copy( skaterift
.replay_freecam
.pos
, cam
->pos
);
516 v3_copy( skaterift
.replay_freecam
.angles
, cam
->angles
);
517 cam
->fov
= skaterift
.replay_freecam
.fov
;
520 replay_get_camera( &skaterift
.replay
, &skaterift
.cam
);
524 VG_STATIC
void skaterift_replay_debug_info(void){
525 player__debugtext( 2, "replay info" );
527 replay_buffer
*replay
= &skaterift
.replay
;
531 if( replay
->tail
) tail
= (void *)replay
->tail
- replay
->data
;
532 if( replay
->head
) head
= (void *)replay
->head
- replay
->data
;
534 player__debugtext( 1, "head @%u | tail @%u\n", head
, tail
);
536 if( replay
->statehead
){
537 for( u32 i
=0; i
<4; i
++ ){
538 player__debugtext( 1, "[%u]: [%hu, %hu]\n", i
,
539 replay
->statehead
->data_table
[i
][0],
540 replay
->statehead
->data_table
[i
][1] );
542 u32 state
= (void *)replay
->statehead
- replay
->data
;
543 player__debugtext( 1, "gs @%u\n", state
);
544 player__debugtext( 1, "gamestate_size: %hu\n",
545 replay
->statehead
->data_table
[k_replay_framedata_gamestate
][1] );
548 player__debugtext( 1, "gs @NULL\n" );
550 f64 start
= replay
->cursor
,
551 end
= replay
->cursor
;
552 if( replay
->tail
) start
= replay
->tail
->time
;
553 if( replay
->head
) end
= replay
->head
->time
;
555 f64 cur
= replay
->cursor
- start
,
558 player__debugtext( 1, "cursor: %.2fs / %.2fs\n", cur
, len
);
561 VG_STATIC
void skaterift_replay_imgui(void){
562 if( skaterift
.activity
!= k_skaterift_replay
) return;
564 replay_buffer
*replay
= &skaterift
.replay
;
565 f64 start
= replay
->cursor
,
566 end
= replay
->cursor
;
567 if( replay
->tail
) start
= replay
->tail
->time
;
568 if( replay
->head
) end
= replay
->head
->time
;
569 f64 len
= end
- start
,
570 cur
= (replay
->cursor
- start
) / len
;
577 ui_rect bar
= { 0, 0, vg
.window_x
, height
};
578 ui_fill( bar
, ui_colour( k_ui_bg
) );
580 /* cursor frame block */
581 if( replay
->cursor_frame
){
582 if( replay
->cursor_frame
->r
){
583 f64 l
= (replay
->cursor_frame
->r
->time
-replay
->cursor_frame
->time
)/len
,
584 s
= (replay
->cursor_frame
->time
- start
) / len
;
585 ui_rect box
= { s
*(f64
)vg
.window_x
, 0,
586 VG_MAX(4,(ui_px
)(l
*vg
.window_x
)), bar
[3]+2 };
587 ui_fill( box
, ui_colour( k_ui_bg
+4 ) );
592 ui_rect cusor
= { cur
* (f64
)vg
.window_x
- (cwidth
/2), 0,
594 ui_fill( cusor
, ui_colour( k_ui_bg
+7 ) );
596 /* latest state marker */
597 if( replay
->statehead
){
598 f64 t
= (replay
->statehead
->time
- start
) / len
;
599 ui_rect tag
= { t
*(f64
)vg
.window_x
, 0, 2, bar
[3]+8 };
600 ui_fill( tag
, ui_colour( k_ui_green
+k_ui_brighter
) );
603 /* previous state marker */
604 replay_frame
*prev
= replay_find_recent_stateframe( replay
);
606 f64 t
= (prev
->time
- start
) / len
;
607 ui_rect tag
= { t
*(f64
)vg
.window_x
, 0, 2, bar
[3]+8 };
608 ui_fill( tag
, ui_colour( k_ui_yellow
+k_ui_brighter
) );
613 snprintf( buffer
, 128, "-%.2fs\n", (end
-replay
->cursor
) );
614 ui_text( cusor
, buffer
, 1, k_ui_align_middle_left
, 0 );
616 snprintf( buffer
, 128, "-%.2fs\n", len
);
617 ui_text( bar
, buffer
, 1, k_ui_align_middle_left
, 0 );
618 ui_text( bar
, "0s", 1, k_ui_align_middle_right
, 0 );
622 snprintf( buf
, 256, "scrub: %s\nresume: %s\nplay: %s\n",
623 axis_display_string( k_sraxis_replay_h
),
624 button_display_string( k_srbind_replay_resume
),
625 button_display_string( k_srbind_replay_play
) );
627 ui_rect info
= { 0, vg
.window_y
-bar
[3]-128, 256, 128 };
628 ui_text( info
, buf
, 2,0,0 );
632 #endif /* PLAYER_REPLAY_C */