1 #ifndef PLAYER_REPLAY_C
2 #define PLAYER_REPLAY_C
4 #include "player_replay.h"
7 VG_STATIC
void replay_clear( replay_buffer
*replay
){
10 replay
->cursor_frame
= NULL
;
11 replay
->statehead
= NULL
;
12 replay
->cursor
= -99999.9;
15 replay_gamestate
*replay_frame_gamestate( replay_frame
*frame
){
16 void *baseptr
= frame
;
17 return baseptr
+ vg_align8(sizeof(replay_frame
));
20 void *replay_gamestate_subsystem_data( replay_gamestate
*gs
){
22 return baseptr
+ vg_align8(sizeof(replay_gamestate
));
25 u32
replay_frame_gamestate_total_size( u32 subsystem_gamestate_size
){
26 if( subsystem_gamestate_size
){
27 return vg_align8( sizeof(replay_gamestate
) ) +
28 vg_align8( subsystem_gamestate_size
);
34 replay_sfx
*replay_frame_sfx( replay_frame
*frame
, u32 index
){
35 void *gs
= replay_frame_gamestate( frame
);
37 replay_frame_gamestate_total_size( frame
->subsystem_gamestate_size
);
39 replay_sfx
*array
= (gs
+ total_size
);
43 u32
_replay_frame_size( u32 subsystem_gamestate_size
, u32 sfx_count
){
44 return vg_align8( sizeof( replay_frame
) ) +
45 replay_frame_gamestate_total_size( subsystem_gamestate_size
) +
46 vg_align8( sfx_count
* sizeof(replay_sfx
) );
49 u32
replay_frame_size( replay_frame
*frame
){
50 return _replay_frame_size( frame
->subsystem_gamestate_size
,
54 VG_STATIC
void replay_tailpop( replay_buffer
*replay
){
55 if( replay
->cursor_frame
== replay
->tail
)
56 replay
->cursor_frame
= NULL
;
57 if( replay
->statehead
== replay
->tail
)
58 replay
->statehead
= NULL
;
60 replay
->tail
= replay
->tail
->r
;
63 replay
->tail
->l
= NULL
;
68 VG_STATIC replay_frame
*replay_newframe( replay_buffer
*replay
,
69 u32 subsystem_gamestate_size
,
71 replay_frame
*frame
= NULL
;
73 assert( replay
->head
);
75 u32 headsize
= replay_frame_size( replay
->head
),
76 nextpos
= ((void *)replay
->head
- replay
->data
) + headsize
,
77 nextsize
= _replay_frame_size( subsystem_gamestate_size
, sfx_count
);
79 if( nextsize
> replay
->size
){
80 vg_error( "Keyframe too big\n" );
84 if( nextpos
+ nextsize
> replay
->size
){
87 /* maintain contiguity */
88 while( replay
->tail
){
89 if( (void *)replay
->tail
- replay
->data
)
90 replay_tailpop( replay
);
97 u32 tailpos
= (void *)replay
->tail
- replay
->data
;
99 if( tailpos
>= nextpos
){
100 if( nextpos
+ nextsize
> tailpos
){
101 replay_tailpop( replay
);
108 frame
= replay
->data
+ nextpos
;
111 replay
->head
->r
= frame
;
114 frame
= replay
->data
;
116 frame
->subsystem_gamestate_size
= subsystem_gamestate_size
;
117 frame
->sfx_count
= sfx_count
;
118 frame
->l
= replay
->head
;
120 replay
->head
= frame
;
121 if( !replay
->tail
) replay
->tail
= frame
;
122 if( subsystem_gamestate_size
) replay
->statehead
= frame
;
127 VG_STATIC
void replay_seek( replay_buffer
*replay
, f64 t
){
128 if( !replay
->head
) return;
129 assert( replay
->tail
);
131 if( t
< replay
->tail
->time
) t
= replay
->tail
->time
;
132 if( t
> replay
->head
->time
) t
= replay
->head
->time
;
134 if( !replay
->cursor_frame
) {
135 replay
->cursor
= replay
->head
->time
;
136 replay
->cursor_frame
= replay
->head
;
138 if( fabs(replay
->head
->time
-t
) > fabs(replay
->tail
->time
-t
) ){
139 replay
->cursor
= replay
->tail
->time
;
140 replay
->cursor_frame
= replay
->tail
;
144 f64 dir
= t
- replay
->cursor
;
145 if( dir
== 0.0 ) return;
146 dir
= vg_signf( dir
);
151 if( t
> replay
->cursor_frame
->time
) break;
154 if( dir
> 0.0 ) next
= replay
->cursor_frame
->r
;
155 else next
= replay
->cursor_frame
->l
;
160 if( t
< next
->time
) break;
162 replay
->cursor_frame
= next
;
163 replay
->cursor
= next
->time
;
171 VG_STATIC replay_frame
*replay_find_recent_stateframe( replay_buffer
*replay
){
172 replay_frame
*frame
= replay
->cursor_frame
;
175 if( !frame
) return frame
;
176 if( frame
->subsystem_gamestate_size
) return frame
;
183 VG_STATIC f32
replay_subframe_time( replay_buffer
*replay
){
184 replay_frame
*frame
= replay
->cursor_frame
;
185 if( !frame
) return 0.0f
;
186 replay_frame
*next
= frame
->r
;
188 f64 l
= next
->time
- frame
->time
,
189 t
= (replay
->cursor
- frame
->time
) / l
;
190 return vg_clampf( t
, 0.0f
, 1.0f
);
196 VG_STATIC
void replay_get_frame_camera( replay_frame
*frame
, camera
*cam
){
197 cam
->fov
= frame
->cam_fov
;
198 v3_copy( frame
->cam_pos
, cam
->pos
);
199 v3_copy( frame
->cam_angles
, cam
->angles
);
202 VG_STATIC
void replay_get_camera( replay_buffer
*replay
, camera
*cam
){
205 if( replay
->cursor_frame
){
206 replay_frame
*next
= replay
->cursor_frame
->r
;
211 replay_get_frame_camera( replay
->cursor_frame
, cam
);
212 replay_get_frame_camera( next
, &temp
);
213 camera_lerp( cam
, &temp
, replay_subframe_time( replay
), cam
);
216 replay_get_frame_camera( replay
->cursor_frame
, cam
);
221 v3_zero( cam
->angles
);
232 void skaterift_record_frame( replay_buffer
*replay
, int force_gamestate
){
233 f64 delta
= 9999999.9,
234 statedelta
= 9999999.9;
237 delta
= vg
.time
- replay
->head
->time
;
239 if( replay
->statehead
)
240 statedelta
= vg
.time
- replay
->statehead
->time
;
242 const f64 k_replay_rate
= 1.0/30.0,
243 k_gamestate_rate
= 0.5;
248 if( force_gamestate
) save_state
= 1;
249 if( statedelta
> k_gamestate_rate
) save_state
= 1;
250 if( delta
> k_replay_rate
) save_frame
= 1;
251 if( save_state
) save_frame
= 1;
253 if( !save_frame
) return;
255 u32 gamestate_size
= 0;
258 gamestate_size
= (u32
[]){
259 [k_player_subsystem_walk
] = sizeof(struct player_walk_state
),
260 [k_player_subsystem_drive
] = 0,
261 [k_player_subsystem_skate
] = sizeof(struct player_skate_state
),
262 [k_player_subsystem_dead
] = localplayer
.ragdoll
.part_count
*
263 sizeof(struct replay_rb
)
264 }[ localplayer
.subsystem
];
267 replay_frame
*frame
= replay_newframe( replay
, gamestate_size
, 0 );
270 replay_gamestate
*gs
= replay_frame_gamestate( frame
);
271 gs
->system
= localplayer
.subsystem
;
273 /* permanent block */
274 memcpy( &gs
->rb
, &localplayer
.rb
, sizeof(rigidbody
) );
275 memcpy( &gs
->cam_control
, &localplayer
.cam_control
,
276 sizeof(struct player_cam_controller
) );
277 v3_copy( localplayer
.angles
, gs
->angles
);
279 void *dst
= replay_gamestate_subsystem_data( gs
);
281 /* subsytem/dynamic block */
282 if( localplayer
.subsystem
== k_player_subsystem_walk
)
283 memcpy( dst
, &localplayer
._walk
.state
, gamestate_size
);
284 else if( localplayer
.subsystem
== k_player_subsystem_skate
)
285 memcpy( dst
, &localplayer
._skate
.state
, gamestate_size
);
286 else if( localplayer
.subsystem
== k_player_subsystem_dead
){
287 struct replay_rb
*arr
= dst
;
288 for( u32 i
=0; i
<localplayer
.ragdoll
.part_count
; i
++ ){
289 rigidbody
*rb
= &localplayer
.ragdoll
.parts
[i
].obj
.rb
;
290 v3_copy( rb
->co
, arr
[i
].co
);
291 v3_copy( rb
->w
, arr
[i
].w
);
292 v3_copy( rb
->v
, arr
[i
].v
);
293 v4_copy( rb
->q
, arr
[i
].q
);
298 replay
->cursor
= vg
.time
;
299 replay
->cursor_frame
= frame
;
301 player_animation
*res
= &frame
->anim
;
302 v3_zero( res
->root_co
);
303 q_identity( res
->root_q
);
304 res
->type
= k_player_animation_type_absolute
;
306 struct skeleton
*sk
= &localplayer
.playeravatar
->sk
;
308 memcpy( &frame
->board_pose
, &localplayer
.board_pose
,
309 sizeof(localplayer
.board_pose
) );
310 frame
->time
= vg
.time
;
311 v3_copy( localplayer
.cam
.pos
, frame
->cam_pos
);
313 if( localplayer
.gate_waiting
){
314 m4x3_mulv( localplayer
.gate_waiting
->transport
,
315 frame
->cam_pos
, frame
->cam_pos
);
317 for( u32 i
=1; i
<sk
->bone_count
; i
++ ){
318 struct skeleton_bone
*sb
= &sk
->bones
[i
];
319 mdl_keyframe
*kf
= &res
->pose
[i
-1];
321 m4x3_mul( localplayer
.gate_waiting
->transport
, sk
->final_mtx
[i
], mtx
);
322 m4x3_decompose( mtx
, kf
->co
, kf
->q
, kf
->s
);
326 skeleton_decompose_mtx_absolute( sk
, res
->pose
);
328 v3_copy( localplayer
.cam
.angles
, frame
->cam_angles
);
329 frame
->cam_fov
= localplayer
.cam
.fov
;
333 void skaterift_restore_frame( replay_frame
*frame
){
334 replay_gamestate
*gs
= replay_frame_gamestate( frame
);
335 void *src
= replay_gamestate_subsystem_data( gs
);
337 /* TODO: Move this to subsystem bindings now that its variable */
338 if( gs
->system
== k_player_subsystem_walk
){
339 memcpy( &localplayer
._walk
.state
, src
,
340 frame
->subsystem_gamestate_size
);
342 else if( gs
->system
== k_player_subsystem_skate
){
343 memcpy( &localplayer
._skate
.state
, src
,
344 frame
->subsystem_gamestate_size
);
346 else if( gs
->system
== k_player_subsystem_dead
){
347 player__dead_transition( &localplayer
);
348 struct replay_rb
*arr
= src
;
350 for( u32 i
=0; i
<localplayer
.ragdoll
.part_count
; i
++ ){
351 struct ragdoll_part
*part
= &localplayer
.ragdoll
.parts
[i
];
352 rigidbody
*rb
= &part
->obj
.rb
;
354 v3_copy( arr
[i
].co
, rb
->co
);
355 v3_copy( arr
[i
].w
, rb
->w
);
356 v3_copy( arr
[i
].v
, rb
->v
);
357 v4_copy( arr
[i
].q
, rb
->q
);
359 v3_copy( arr
[i
].co
, part
->prev_co
);
360 v4_copy( arr
[i
].q
, part
->prev_q
);
364 localplayer
.subsystem
= gs
->system
;
366 memcpy( &localplayer
.rb
, &gs
->rb
, sizeof(rigidbody
) );
367 v3_copy( gs
->angles
, localplayer
.angles
);
369 v3_copy( frame
->cam_pos
, localplayer
.cam
.pos
);
370 v3_copy( frame
->cam_angles
, localplayer
.cam
.angles
);
371 localplayer
.cam
.fov
= frame
->cam_fov
;
373 memcpy( &localplayer
.cam_control
, &gs
->cam_control
,
374 sizeof(struct player_cam_controller
) );
376 /* chop end off replay */
378 skaterift
.replay
.statehead
= frame
;
379 skaterift
.replay
.head
= frame
;
380 skaterift
.replay
.cursor_frame
= frame
;
381 skaterift
.replay
.cursor
= frame
->time
;
382 skaterift
.replay_control
= k_replay_control_scrub
;
383 skaterift
.activity
= k_skaterift_default
;
384 vg
.time
= frame
->time
;
387 VG_STATIC
void skaterift_replay_pre_update(void){
388 if( skaterift
.activity
!= k_skaterift_replay
) return;
390 if( skaterift
.replay_control
== k_replay_control_resume
){
391 if( skaterift
.replay
.cursor_frame
== skaterift
.resume_target
||
392 skaterift
.replay
.cursor_frame
== NULL
){
393 skaterift_restore_frame( skaterift
.resume_target
);
396 vg_slewf( &skaterift
.resume_transition
, 1.0f
,
397 vg
.time_frame_delta
* (1.0f
/1.0f
) );
399 if( skaterift
.resume_transition
>= 1.0f
)
400 skaterift_restore_frame( skaterift
.resume_target
);
402 f64 target
= vg_lerp( skaterift
.resume_begin
,
403 skaterift
.resume_target
->time
,
404 vg_smoothstepf( skaterift
.resume_transition
) );
405 replay_seek( &skaterift
.replay
, target
);
410 if( button_down( k_srbind_replay_play
) )
411 skaterift
.replay_control
= k_replay_control_play
;
413 f32 target_speed
= axis_state( k_sraxis_replay_h
) * 5.0;
415 if( fabsf(target_speed
) > 0.01f
)
416 skaterift
.replay_control
= k_replay_control_scrub
;
418 if( skaterift
.replay_control
== k_replay_control_play
)
421 vg_slewf( &skaterift
.track_velocity
, target_speed
,
422 10.0f
*vg
.time_frame_delta
);
424 if( fabsf( skaterift
.track_velocity
) > 0.0001f
){
425 f64 target
= skaterift
.replay
.cursor
;
426 target
+= skaterift
.track_velocity
* vg
.time_frame_delta
;
428 replay_seek( &skaterift
.replay
, target
);
431 if( button_down( k_srbind_replay_resume
) ){
432 replay_frame
*prev
= replay_find_recent_stateframe(&skaterift
.replay
);
435 skaterift
.replay_control
= k_replay_control_resume
;
436 skaterift
.resume_target
= prev
;
437 skaterift
.resume_begin
= skaterift
.replay
.cursor
;
438 skaterift
.resume_transition
= 0.0f
;
441 else if( button_down( k_srbind_mback
) ){
442 if( skaterift
.replay
.statehead
)
443 skaterift_restore_frame( skaterift
.replay
.statehead
);
445 skaterift
.activity
= k_skaterift_default
;
451 VG_STATIC
void skaterift_replay_debug_info(void){
452 player__debugtext( 2, "replay info" );
454 replay_buffer
*replay
= &skaterift
.replay
;
458 if( replay
->tail
) tail
= (void *)replay
->tail
- replay
->data
;
459 if( replay
->head
) head
= (void *)replay
->head
- replay
->data
;
461 player__debugtext( 1, "head @%u | tail @%u\n", head
, tail
);
463 if( replay
->statehead
){
464 u32 state
= (void *)replay
->statehead
- replay
->data
;
465 player__debugtext( 1, "gs @%u\n", state
);
466 player__debugtext( 1, "gamestate_size: %u\n",
467 replay
->statehead
->subsystem_gamestate_size
);
470 player__debugtext( 1, "gs @NULL\n" );
472 f64 start
= replay
->cursor
,
473 end
= replay
->cursor
;
474 if( replay
->tail
) start
= replay
->tail
->time
;
475 if( replay
->head
) end
= replay
->head
->time
;
477 f64 cur
= replay
->cursor
- start
,
480 player__debugtext( 1, "cursor: %.2fs / %.2fs\n", cur
, len
);
483 VG_STATIC
void skaterift_replay_imgui(void){
484 if( skaterift
.activity
!= k_skaterift_replay
) return;
486 replay_buffer
*replay
= &skaterift
.replay
;
487 f64 start
= replay
->cursor
,
488 end
= replay
->cursor
;
489 if( replay
->tail
) start
= replay
->tail
->time
;
490 if( replay
->head
) end
= replay
->head
->time
;
491 f64 len
= end
- start
,
492 cur
= (replay
->cursor
- start
) / len
;
499 ui_rect bar
= { 0, vg
.window_y
- height
, vg
.window_x
, height
};
500 ui_fill( bar
, ui_colour( k_ui_bg
) );
502 /* cursor frame block */
503 if( replay
->cursor_frame
){
504 if( replay
->cursor_frame
->r
){
505 f64 l
= (replay
->cursor_frame
->r
->time
-replay
->cursor_frame
->time
)/len
,
506 s
= (replay
->cursor_frame
->time
- start
) / len
;
507 ui_rect box
= { s
*(f64
)vg
.window_x
, bar
[1]-2,
508 VG_MAX(4,(ui_px
)(l
*vg
.window_x
)), bar
[3]+2 };
509 ui_fill( box
, ui_colour( k_ui_bg
+4 ) );
514 ui_rect cusor
= { cur
* (f64
)vg
.window_x
- (cwidth
/2), bar
[1],
516 ui_fill( cusor
, ui_colour( k_ui_bg
+7 ) );
518 /* latest state marker */
519 if( replay
->statehead
){
520 f64 t
= (replay
->statehead
->time
- start
) / len
;
521 ui_rect tag
= { t
*(f64
)vg
.window_x
, bar
[1]-8, 2, bar
[3]+8 };
522 ui_fill( tag
, ui_colour( k_ui_green
+k_ui_brighter
) );
525 /* previous state marker */
526 replay_frame
*prev
= replay_find_recent_stateframe( replay
);
528 f64 t
= (prev
->time
- start
) / len
;
529 ui_rect tag
= { t
*(f64
)vg
.window_x
, bar
[1]-8, 2, bar
[3]+8 };
530 ui_fill( tag
, ui_colour( k_ui_yellow
+k_ui_brighter
) );
535 snprintf( buffer
, 128, "-%.2fs\n", (end
-replay
->cursor
) );
536 ui_text( cusor
, buffer
, 1, k_ui_align_middle_left
, 0 );
538 snprintf( buffer
, 128, "-%.2fs\n", len
);
539 ui_text( bar
, buffer
, 1, k_ui_align_middle_left
, 0 );
540 ui_text( bar
, "0s", 1, k_ui_align_middle_right
, 0 );
542 ui_rect info
= { 0, vg
.window_y
-bar
[3]-64, 128, 64 };
543 if( vg_input
.display_input_method
== k_input_method_controller
)
544 ui_text( info
, "scrub: <- LS ->\nresume:LB\nplay: A", 1,0,0 );
546 ui_text( info
, "scrub: <- ->\nresume:R\nplay: space", 1,0,0 );
549 #endif /* PLAYER_REPLAY_C */