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;
16 replay_gamestate
*replay_frame_gamestate( replay_frame
*frame
){
17 void *baseptr
= frame
;
18 return baseptr
+ vg_align8(sizeof(replay_frame
));
21 void *replay_gamestate_subsystem_data( replay_gamestate
*gs
){
23 return baseptr
+ vg_align8(sizeof(replay_gamestate
));
26 u32
replay_frame_gamestate_total_size( u32 subsystem_gamestate_size
){
27 if( subsystem_gamestate_size
){
28 return vg_align8( sizeof(replay_gamestate
) ) +
29 vg_align8( subsystem_gamestate_size
);
35 replay_sfx
*replay_frame_sfx( replay_frame
*frame
, u32 index
){
36 void *gs
= replay_frame_gamestate( frame
);
38 replay_frame_gamestate_total_size( frame
->subsystem_gamestate_size
);
40 replay_sfx
*array
= (gs
+ total_size
);
44 u32
_replay_frame_size( u32 subsystem_gamestate_size
, u32 sfx_count
){
45 return vg_align8( sizeof( replay_frame
) ) +
46 replay_frame_gamestate_total_size( subsystem_gamestate_size
) +
47 vg_align8( sfx_count
* sizeof(replay_sfx
) );
50 u32
replay_frame_size( replay_frame
*frame
){
51 return _replay_frame_size( frame
->subsystem_gamestate_size
,
55 VG_STATIC
void replay_tailpop( replay_buffer
*replay
){
56 if( replay
->cursor_frame
== replay
->tail
)
57 replay
->cursor_frame
= NULL
;
58 if( replay
->statehead
== replay
->tail
)
59 replay
->statehead
= NULL
;
61 replay
->tail
= replay
->tail
->r
;
64 replay
->tail
->l
= NULL
;
69 VG_STATIC replay_frame
*replay_newframe( replay_buffer
*replay
,
70 u32 subsystem_gamestate_size
,
72 replay_frame
*frame
= NULL
;
74 assert( replay
->head
);
76 u32 headsize
= replay_frame_size( replay
->head
),
77 nextpos
= ((void *)replay
->head
- replay
->data
) + headsize
,
78 nextsize
= _replay_frame_size( subsystem_gamestate_size
, sfx_count
);
80 if( nextsize
> replay
->size
){
81 vg_error( "Keyframe too big\n" );
85 if( nextpos
+ nextsize
> replay
->size
){
88 /* maintain contiguity */
89 while( replay
->tail
){
90 if( (void *)replay
->tail
- replay
->data
)
91 replay_tailpop( replay
);
98 u32 tailpos
= (void *)replay
->tail
- replay
->data
;
100 if( tailpos
>= nextpos
){
101 if( nextpos
+ nextsize
> tailpos
){
102 replay_tailpop( replay
);
109 frame
= replay
->data
+ nextpos
;
112 replay
->head
->r
= frame
;
115 frame
= replay
->data
;
117 frame
->subsystem_gamestate_size
= subsystem_gamestate_size
;
118 frame
->sfx_count
= sfx_count
;
119 frame
->l
= replay
->head
;
121 replay
->head
= frame
;
122 if( !replay
->tail
) replay
->tail
= frame
;
123 if( subsystem_gamestate_size
) replay
->statehead
= frame
;
128 VG_STATIC
void replay_seek( replay_buffer
*replay
, f64 t
){
129 if( !replay
->head
) return;
130 assert( replay
->tail
);
132 if( t
< replay
->tail
->time
) t
= replay
->tail
->time
;
133 if( t
> replay
->head
->time
) t
= replay
->head
->time
;
135 if( !replay
->cursor_frame
) {
136 replay
->cursor
= replay
->head
->time
;
137 replay
->cursor_frame
= replay
->head
;
139 if( fabs(replay
->head
->time
-t
) > fabs(replay
->tail
->time
-t
) ){
140 replay
->cursor
= replay
->tail
->time
;
141 replay
->cursor_frame
= replay
->tail
;
145 f64 dir
= t
- replay
->cursor
;
146 if( dir
== 0.0 ) return;
147 dir
= vg_signf( dir
);
152 if( t
> replay
->cursor_frame
->time
) break;
155 if( dir
> 0.0 ) next
= replay
->cursor_frame
->r
;
156 else next
= replay
->cursor_frame
->l
;
161 if( t
< next
->time
) break;
163 replay
->cursor_frame
= next
;
164 replay
->cursor
= next
->time
;
172 VG_STATIC replay_frame
*replay_find_recent_stateframe( replay_buffer
*replay
){
173 replay_frame
*frame
= replay
->cursor_frame
;
176 if( !frame
) return frame
;
177 if( frame
->subsystem_gamestate_size
) return frame
;
184 VG_STATIC f32
replay_subframe_time( replay_buffer
*replay
){
185 replay_frame
*frame
= replay
->cursor_frame
;
186 if( !frame
) return 0.0f
;
187 replay_frame
*next
= frame
->r
;
189 f64 l
= next
->time
- frame
->time
,
190 t
= (replay
->cursor
- frame
->time
) / l
;
191 return vg_clampf( t
, 0.0f
, 1.0f
);
197 VG_STATIC
void replay_get_frame_camera( replay_frame
*frame
, camera
*cam
){
198 cam
->fov
= frame
->cam_fov
;
199 v3_copy( frame
->cam_pos
, cam
->pos
);
200 v3_copy( frame
->cam_angles
, cam
->angles
);
203 VG_STATIC
void replay_get_camera( replay_buffer
*replay
, camera
*cam
){
206 if( replay
->cursor_frame
){
207 replay_frame
*next
= replay
->cursor_frame
->r
;
212 replay_get_frame_camera( replay
->cursor_frame
, cam
);
213 replay_get_frame_camera( next
, &temp
);
214 camera_lerp( cam
, &temp
, replay_subframe_time( replay
), cam
);
217 replay_get_frame_camera( replay
->cursor_frame
, cam
);
222 v3_zero( cam
->angles
);
233 void skaterift_record_frame( replay_buffer
*replay
, int force_gamestate
){
234 f64 delta
= 9999999.9,
235 statedelta
= 9999999.9;
238 delta
= vg
.time
- replay
->head
->time
;
240 if( replay
->statehead
)
241 statedelta
= vg
.time
- replay
->statehead
->time
;
243 const f64 k_replay_rate
= 1.0/30.0,
244 k_gamestate_rate
= 0.5;
249 if( force_gamestate
) save_state
= 1;
250 if( statedelta
> k_gamestate_rate
) save_state
= 1;
251 if( delta
> k_replay_rate
) save_frame
= 1;
252 if( save_state
) save_frame
= 1;
254 if( !save_frame
) return;
256 u32 gamestate_size
= 0;
259 gamestate_size
= (u32
[]){
260 [k_player_subsystem_walk
] = sizeof(struct player_walk_state
),
261 [k_player_subsystem_drive
] = 0,
262 [k_player_subsystem_skate
] = sizeof(struct player_skate_state
),
263 [k_player_subsystem_dead
] = localplayer
.ragdoll
.part_count
*
264 sizeof(struct replay_rb
)
265 }[ localplayer
.subsystem
];
268 replay_frame
*frame
= replay_newframe( replay
, gamestate_size
, 0 );
271 replay_gamestate
*gs
= replay_frame_gamestate( frame
);
272 gs
->system
= localplayer
.subsystem
;
274 /* permanent block */
275 memcpy( &gs
->rb
, &localplayer
.rb
, sizeof(rigidbody
) );
276 memcpy( &gs
->cam_control
, &localplayer
.cam_control
,
277 sizeof(struct player_cam_controller
) );
278 v3_copy( localplayer
.angles
, gs
->angles
);
280 void *dst
= replay_gamestate_subsystem_data( gs
);
282 /* subsytem/dynamic block */
283 if( localplayer
.subsystem
== k_player_subsystem_walk
)
284 memcpy( dst
, &localplayer
._walk
.state
, gamestate_size
);
285 else if( localplayer
.subsystem
== k_player_subsystem_skate
)
286 memcpy( dst
, &localplayer
._skate
.state
, gamestate_size
);
287 else if( localplayer
.subsystem
== k_player_subsystem_dead
){
288 struct replay_rb
*arr
= dst
;
289 for( u32 i
=0; i
<localplayer
.ragdoll
.part_count
; i
++ ){
290 rigidbody
*rb
= &localplayer
.ragdoll
.parts
[i
].obj
.rb
;
291 v3_copy( rb
->co
, arr
[i
].co
);
292 v3_copy( rb
->w
, arr
[i
].w
);
293 v3_copy( rb
->v
, arr
[i
].v
);
294 v4_copy( rb
->q
, arr
[i
].q
);
299 replay
->cursor
= vg
.time
;
300 replay
->cursor_frame
= frame
;
302 player_animation
*res
= &frame
->anim
;
303 v3_zero( res
->root_co
);
304 q_identity( res
->root_q
);
305 res
->type
= k_player_animation_type_absolute
;
307 struct skeleton
*sk
= &localplayer
.playeravatar
->sk
;
309 memcpy( &frame
->board_pose
, &localplayer
.board_pose
,
310 sizeof(localplayer
.board_pose
) );
311 frame
->time
= vg
.time
;
312 v3_copy( localplayer
.cam
.pos
, frame
->cam_pos
);
314 if( localplayer
.gate_waiting
){
315 m4x3_mulv( localplayer
.gate_waiting
->transport
,
316 frame
->cam_pos
, frame
->cam_pos
);
318 for( u32 i
=1; i
<sk
->bone_count
; i
++ ){
319 struct skeleton_bone
*sb
= &sk
->bones
[i
];
320 mdl_keyframe
*kf
= &res
->pose
[i
-1];
322 m4x3_mul( localplayer
.gate_waiting
->transport
, sk
->final_mtx
[i
], mtx
);
323 m4x3_decompose( mtx
, kf
->co
, kf
->q
, kf
->s
);
327 skeleton_decompose_mtx_absolute( sk
, res
->pose
);
329 v3_copy( localplayer
.cam
.angles
, frame
->cam_angles
);
330 frame
->cam_fov
= localplayer
.cam
.fov
;
334 void skaterift_restore_frame( replay_frame
*frame
){
335 replay_gamestate
*gs
= replay_frame_gamestate( frame
);
336 void *src
= replay_gamestate_subsystem_data( gs
);
338 /* TODO: Move this to subsystem bindings now that its variable */
339 if( gs
->system
== k_player_subsystem_walk
){
340 memcpy( &localplayer
._walk
.state
, src
,
341 frame
->subsystem_gamestate_size
);
343 else if( gs
->system
== k_player_subsystem_skate
){
344 memcpy( &localplayer
._skate
.state
, src
,
345 frame
->subsystem_gamestate_size
);
347 else if( gs
->system
== k_player_subsystem_dead
){
348 player__dead_transition( &localplayer
);
349 struct replay_rb
*arr
= src
;
351 for( u32 i
=0; i
<localplayer
.ragdoll
.part_count
; i
++ ){
352 struct ragdoll_part
*part
= &localplayer
.ragdoll
.parts
[i
];
353 rigidbody
*rb
= &part
->obj
.rb
;
355 v3_copy( arr
[i
].co
, rb
->co
);
356 v3_copy( arr
[i
].w
, rb
->w
);
357 v3_copy( arr
[i
].v
, rb
->v
);
358 v4_copy( arr
[i
].q
, rb
->q
);
360 v3_copy( arr
[i
].co
, part
->prev_co
);
361 v4_copy( arr
[i
].q
, part
->prev_q
);
365 localplayer
.subsystem
= gs
->system
;
367 memcpy( &localplayer
.rb
, &gs
->rb
, sizeof(rigidbody
) );
368 v3_copy( gs
->angles
, localplayer
.angles
);
370 v3_copy( frame
->cam_pos
, localplayer
.cam
.pos
);
371 v3_copy( frame
->cam_angles
, localplayer
.cam
.angles
);
372 localplayer
.cam
.fov
= frame
->cam_fov
;
374 memcpy( &localplayer
.cam_control
, &gs
->cam_control
,
375 sizeof(struct player_cam_controller
) );
377 /* chop end off replay */
379 skaterift
.replay
.statehead
= frame
;
380 skaterift
.replay
.head
= frame
;
381 skaterift
.replay
.cursor_frame
= frame
;
382 skaterift
.replay
.cursor
= frame
->time
;
383 skaterift
.replay_control
= k_replay_control_scrub
;
384 skaterift
.activity
= k_skaterift_default
;
385 vg
.time
= frame
->time
;
388 VG_STATIC
void skaterift_replay_resume(void){
389 replay_frame
*prev
= replay_find_recent_stateframe(&skaterift
.replay
);
392 skaterift
.replay_control
= k_replay_control_resume
;
393 skaterift
.resume_target
= prev
;
394 skaterift
.resume_begin
= skaterift
.replay
.cursor
;
395 skaterift
.resume_transition
= 0.0f
;
399 VG_STATIC
void skaterift_replay_pre_update(void){
400 if( skaterift
.activity
!= k_skaterift_replay
) return;
402 if( skaterift
.replay_control
== k_replay_control_resume
){
403 if( skaterift
.replay
.cursor_frame
== skaterift
.resume_target
||
404 skaterift
.replay
.cursor_frame
== NULL
){
405 skaterift_restore_frame( skaterift
.resume_target
);
408 vg_slewf( &skaterift
.resume_transition
, 1.0f
,
409 vg
.time_frame_delta
* (1.0f
/1.0f
) );
411 if( skaterift
.resume_transition
>= 1.0f
)
412 skaterift_restore_frame( skaterift
.resume_target
);
414 f64 target
= vg_lerp( skaterift
.resume_begin
,
415 skaterift
.resume_target
->time
,
416 vg_smoothstepf( skaterift
.resume_transition
) );
417 replay_seek( &skaterift
.replay
, target
);
422 if( button_down( k_srbind_replay_play
) )
423 skaterift
.replay_control
= k_replay_control_play
;
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_replay_resume
) ){
446 skaterift_replay_resume();
448 else if( button_down( k_srbind_mback
) ){
449 if( skaterift
.replay
.statehead
)
450 skaterift_restore_frame( skaterift
.replay
.statehead
);
452 skaterift
.activity
= k_skaterift_default
;
458 VG_STATIC
void skaterift_replay_debug_info(void){
459 player__debugtext( 2, "replay info" );
461 replay_buffer
*replay
= &skaterift
.replay
;
465 if( replay
->tail
) tail
= (void *)replay
->tail
- replay
->data
;
466 if( replay
->head
) head
= (void *)replay
->head
- replay
->data
;
468 player__debugtext( 1, "head @%u | tail @%u\n", head
, tail
);
470 if( replay
->statehead
){
471 u32 state
= (void *)replay
->statehead
- replay
->data
;
472 player__debugtext( 1, "gs @%u\n", state
);
473 player__debugtext( 1, "gamestate_size: %u\n",
474 replay
->statehead
->subsystem_gamestate_size
);
477 player__debugtext( 1, "gs @NULL\n" );
479 f64 start
= replay
->cursor
,
480 end
= replay
->cursor
;
481 if( replay
->tail
) start
= replay
->tail
->time
;
482 if( replay
->head
) end
= replay
->head
->time
;
484 f64 cur
= replay
->cursor
- start
,
487 player__debugtext( 1, "cursor: %.2fs / %.2fs\n", cur
, len
);
490 VG_STATIC
void skaterift_replay_imgui(void){
491 if( skaterift
.activity
!= k_skaterift_replay
) return;
493 replay_buffer
*replay
= &skaterift
.replay
;
494 f64 start
= replay
->cursor
,
495 end
= replay
->cursor
;
496 if( replay
->tail
) start
= replay
->tail
->time
;
497 if( replay
->head
) end
= replay
->head
->time
;
498 f64 len
= end
- start
,
499 cur
= (replay
->cursor
- start
) / len
;
506 ui_rect bar
= { 0, 0, vg
.window_x
, height
};
507 ui_fill( bar
, ui_colour( k_ui_bg
) );
509 /* cursor frame block */
510 if( replay
->cursor_frame
){
511 if( replay
->cursor_frame
->r
){
512 f64 l
= (replay
->cursor_frame
->r
->time
-replay
->cursor_frame
->time
)/len
,
513 s
= (replay
->cursor_frame
->time
- start
) / len
;
514 ui_rect box
= { s
*(f64
)vg
.window_x
, 0,
515 VG_MAX(4,(ui_px
)(l
*vg
.window_x
)), bar
[3]+2 };
516 ui_fill( box
, ui_colour( k_ui_bg
+4 ) );
521 ui_rect cusor
= { cur
* (f64
)vg
.window_x
- (cwidth
/2), 0,
523 ui_fill( cusor
, ui_colour( k_ui_bg
+7 ) );
525 /* latest state marker */
526 if( replay
->statehead
){
527 f64 t
= (replay
->statehead
->time
- start
) / len
;
528 ui_rect tag
= { t
*(f64
)vg
.window_x
, 0, 2, bar
[3]+8 };
529 ui_fill( tag
, ui_colour( k_ui_green
+k_ui_brighter
) );
532 /* previous state marker */
533 replay_frame
*prev
= replay_find_recent_stateframe( replay
);
535 f64 t
= (prev
->time
- start
) / len
;
536 ui_rect tag
= { t
*(f64
)vg
.window_x
, 0, 2, bar
[3]+8 };
537 ui_fill( tag
, ui_colour( k_ui_yellow
+k_ui_brighter
) );
542 snprintf( buffer
, 128, "-%.2fs\n", (end
-replay
->cursor
) );
543 ui_text( cusor
, buffer
, 1, k_ui_align_middle_left
, 0 );
545 snprintf( buffer
, 128, "-%.2fs\n", len
);
546 ui_text( bar
, buffer
, 1, k_ui_align_middle_left
, 0 );
547 ui_text( bar
, "0s", 1, k_ui_align_middle_right
, 0 );
551 snprintf( buf
, 256, "scrub: %s\nresume: %s\nplay: %s\n",
552 axis_display_string( k_sraxis_replay_h
),
553 button_display_string( k_srbind_replay_resume
),
554 button_display_string( k_srbind_replay_play
) );
556 ui_rect info
= { 0, vg
.window_y
-bar
[3]-128, 256, 128 };
557 ui_text( info
, buf
, 2,0,0 );
560 gui_helper_action( axis_display_string(k_sraxis_replay_h
), "scrub" );
561 gui_helper_action( button_display_string(k_srbind_replay_resume
), "resume" );
562 gui_helper_action( button_display_string(k_srbind_replay_play
), "play" );
565 #endif /* PLAYER_REPLAY_C */