1 #ifndef PLAYER_REPLAY_C
2 #define PLAYER_REPLAY_C
4 #include "player_replay.h"
6 VG_STATIC
void replay_clear( replay_buffer
*replay
){
9 replay
->cursor_frame
= NULL
;
10 replay
->statehead
= NULL
;
11 replay
->cursor
= -99999.9;
14 replay_gamestate
*replay_frame_gamestate( replay_frame
*frame
){
15 void *baseptr
= frame
;
16 return baseptr
+ vg_align8(sizeof(replay_frame
));
19 void *replay_gamestate_subsystem_data( replay_gamestate
*gs
){
21 return baseptr
+ vg_align8(sizeof(replay_gamestate
));
24 u32
replay_frame_gamestate_total_size( u32 subsystem_gamestate_size
){
25 if( subsystem_gamestate_size
){
26 return vg_align8( sizeof(replay_gamestate
) ) +
27 vg_align8( subsystem_gamestate_size
);
33 replay_sfx
*replay_frame_sfx( replay_frame
*frame
, u32 index
){
34 void *gs
= replay_frame_gamestate( frame
);
36 replay_frame_gamestate_total_size( frame
->subsystem_gamestate_size
);
38 replay_sfx
*array
= (gs
+ total_size
);
42 u32
_replay_frame_size( u32 subsystem_gamestate_size
, u32 sfx_count
){
43 return vg_align8( sizeof( replay_frame
) ) +
44 replay_frame_gamestate_total_size( subsystem_gamestate_size
) +
45 vg_align8( sfx_count
* sizeof(replay_sfx
) );
48 u32
replay_frame_size( replay_frame
*frame
){
49 return _replay_frame_size( frame
->subsystem_gamestate_size
,
53 VG_STATIC
void replay_tailpop( replay_buffer
*replay
){
54 if( replay
->cursor_frame
== replay
->tail
)
55 replay
->cursor_frame
= NULL
;
56 if( replay
->statehead
== replay
->tail
)
57 replay
->statehead
= NULL
;
59 replay
->tail
= replay
->tail
->r
;
62 replay
->tail
->l
= NULL
;
67 VG_STATIC replay_frame
*replay_newframe( replay_buffer
*replay
,
68 u32 subsystem_gamestate_size
,
70 replay_frame
*frame
= NULL
;
72 assert( replay
->head
);
74 u32 headsize
= replay_frame_size( replay
->head
),
75 nextpos
= ((void *)replay
->head
- replay
->data
) + headsize
,
76 nextsize
= _replay_frame_size( subsystem_gamestate_size
, sfx_count
);
78 if( nextsize
> replay
->size
){
79 vg_error( "Keyframe too big\n" );
83 if( nextpos
+ nextsize
> replay
->size
){
86 /* maintain contiguity */
87 while( replay
->tail
){
88 if( (void *)replay
->tail
- replay
->data
)
89 replay_tailpop( replay
);
96 u32 tailpos
= (void *)replay
->tail
- replay
->data
;
98 if( tailpos
>= nextpos
){
99 if( nextpos
+ nextsize
> tailpos
){
100 replay_tailpop( replay
);
107 frame
= replay
->data
+ nextpos
;
110 replay
->head
->r
= frame
;
113 frame
= replay
->data
;
115 frame
->subsystem_gamestate_size
= subsystem_gamestate_size
;
116 frame
->sfx_count
= sfx_count
;
117 frame
->l
= replay
->head
;
119 replay
->head
= frame
;
120 if( !replay
->tail
) replay
->tail
= frame
;
121 if( subsystem_gamestate_size
) replay
->statehead
= frame
;
126 VG_STATIC
void replay_seek( replay_buffer
*replay
, f64 t
){
127 if( !replay
->head
) return;
128 assert( replay
->tail
);
130 if( t
< replay
->tail
->time
) t
= replay
->tail
->time
;
131 if( t
> replay
->head
->time
) t
= replay
->head
->time
;
133 if( !replay
->cursor_frame
) {
134 replay
->cursor
= replay
->head
->time
;
135 replay
->cursor_frame
= replay
->head
;
137 if( fabs(replay
->head
->time
-t
) > fabs(replay
->tail
->time
-t
) ){
138 replay
->cursor
= replay
->tail
->time
;
139 replay
->cursor_frame
= replay
->tail
;
143 f64 dir
= t
- replay
->cursor
;
144 if( dir
== 0.0 ) return;
145 dir
= vg_signf( dir
);
150 if( t
> replay
->cursor_frame
->time
) break;
153 if( dir
> 0.0 ) next
= replay
->cursor_frame
->r
;
154 else next
= replay
->cursor_frame
->l
;
159 if( t
< next
->time
) break;
161 replay
->cursor_frame
= next
;
162 replay
->cursor
= next
->time
;
170 VG_STATIC replay_frame
*replay_find_recent_stateframe( replay_buffer
*replay
){
171 replay_frame
*frame
= replay
->cursor_frame
;
174 if( !frame
) return frame
;
175 if( frame
->subsystem_gamestate_size
) return frame
;
182 VG_STATIC f32
replay_subframe_time( replay_buffer
*replay
){
183 replay_frame
*frame
= replay
->cursor_frame
;
184 if( !frame
) return 0.0f
;
185 replay_frame
*next
= frame
->r
;
187 f64 l
= next
->time
- frame
->time
,
188 t
= (replay
->cursor
- frame
->time
) / l
;
189 return vg_clampf( t
, 0.0f
, 1.0f
);
195 VG_STATIC
void replay_get_frame_camera( replay_frame
*frame
, camera
*cam
){
196 cam
->fov
= frame
->cam_fov
;
197 v3_copy( frame
->cam_pos
, cam
->pos
);
198 v3_copy( frame
->cam_angles
, cam
->angles
);
201 VG_STATIC
void replay_get_camera( replay_buffer
*replay
, camera
*cam
){
204 if( replay
->cursor_frame
){
205 replay_frame
*next
= replay
->cursor_frame
->r
;
210 replay_get_frame_camera( replay
->cursor_frame
, cam
);
211 replay_get_frame_camera( next
, &temp
);
212 camera_lerp( cam
, &temp
, replay_subframe_time( replay
), cam
);
215 replay_get_frame_camera( replay
->cursor_frame
, cam
);
220 v3_zero( cam
->angles
);
231 void skaterift_record_frame( replay_buffer
*replay
, int force_gamestate
){
232 f64 delta
= 9999999.9,
233 statedelta
= 9999999.9;
236 delta
= vg
.time
- replay
->head
->time
;
238 if( replay
->statehead
)
239 statedelta
= vg
.time
- replay
->statehead
->time
;
241 const f64 k_replay_rate
= 1.0/30.0,
242 k_gamestate_rate
= 0.5;
247 if( force_gamestate
) save_state
= 1;
248 if( statedelta
> k_gamestate_rate
) save_state
= 1;
249 if( delta
> k_replay_rate
) save_frame
= 1;
250 if( save_state
) save_frame
= 1;
252 if( !save_frame
) return;
254 u32 gamestate_size
= 0;
257 gamestate_size
= (u32
[]){
258 [k_player_subsystem_walk
] = sizeof(struct player_walk_state
),
259 [k_player_subsystem_drive
] = 0,
260 [k_player_subsystem_skate
] = sizeof(struct player_skate_state
),
261 [k_player_subsystem_dead
] = localplayer
.ragdoll
.part_count
*
262 sizeof(struct replay_rb
)
263 }[ localplayer
.subsystem
];
266 replay_frame
*frame
= replay_newframe( replay
, gamestate_size
, 0 );
269 replay_gamestate
*gs
= replay_frame_gamestate( frame
);
270 gs
->system
= localplayer
.subsystem
;
272 /* permanent block */
273 memcpy( &gs
->rb
, &localplayer
.rb
, sizeof(rigidbody
) );
274 memcpy( &gs
->cam_control
, &localplayer
.cam_control
,
275 sizeof(struct player_cam_controller
) );
276 v3_copy( localplayer
.angles
, gs
->angles
);
278 void *dst
= replay_gamestate_subsystem_data( gs
);
280 /* subsytem/dynamic block */
281 if( localplayer
.subsystem
== k_player_subsystem_walk
)
282 memcpy( dst
, &localplayer
._walk
.state
, gamestate_size
);
283 else if( localplayer
.subsystem
== k_player_subsystem_skate
)
284 memcpy( dst
, &localplayer
._skate
.state
, gamestate_size
);
285 else if( localplayer
.subsystem
== k_player_subsystem_dead
){
286 struct replay_rb
*arr
= dst
;
287 for( u32 i
=0; i
<localplayer
.ragdoll
.part_count
; i
++ ){
288 rigidbody
*rb
= &localplayer
.ragdoll
.parts
[i
].obj
.rb
;
289 v3_copy( rb
->co
, arr
[i
].co
);
290 v3_copy( rb
->w
, arr
[i
].w
);
291 v3_copy( rb
->v
, arr
[i
].v
);
292 v4_copy( rb
->q
, arr
[i
].q
);
297 replay
->cursor
= vg
.time
;
298 replay
->cursor_frame
= frame
;
300 player_animation
*res
= &frame
->anim
;
301 v3_zero( res
->root_co
);
302 q_identity( res
->root_q
);
303 res
->type
= k_player_animation_type_absolute
;
305 struct skeleton
*sk
= &localplayer
.playeravatar
->sk
;
306 skeleton_decompose_mtx_absolute( sk
, res
->pose
);
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
);
312 v3_copy( localplayer
.cam
.angles
, frame
->cam_angles
);
313 frame
->cam_fov
= localplayer
.cam
.fov
;
317 void skaterift_restore_frame( replay_frame
*frame
){
318 replay_gamestate
*gs
= replay_frame_gamestate( frame
);
319 void *src
= replay_gamestate_subsystem_data( gs
);
321 /* TODO: Move this to subsystem bindings now that its variable */
322 if( gs
->system
== k_player_subsystem_walk
){
323 memcpy( &localplayer
._walk
.state
, src
,
324 frame
->subsystem_gamestate_size
);
326 else if( gs
->system
== k_player_subsystem_skate
){
327 memcpy( &localplayer
._skate
.state
, src
,
328 frame
->subsystem_gamestate_size
);
330 else if( gs
->system
== k_player_subsystem_dead
){
331 player__dead_transition( &localplayer
);
332 struct replay_rb
*arr
= src
;
334 for( u32 i
=0; i
<localplayer
.ragdoll
.part_count
; i
++ ){
335 struct ragdoll_part
*part
= &localplayer
.ragdoll
.parts
[i
];
336 rigidbody
*rb
= &part
->obj
.rb
;
338 v3_copy( arr
[i
].co
, rb
->co
);
339 v3_copy( arr
[i
].w
, rb
->w
);
340 v3_copy( arr
[i
].v
, rb
->v
);
341 v4_copy( arr
[i
].q
, rb
->q
);
343 v3_copy( arr
[i
].co
, part
->prev_co
);
344 v4_copy( arr
[i
].q
, part
->prev_q
);
348 localplayer
.subsystem
= gs
->system
;
350 memcpy( &localplayer
.rb
, &gs
->rb
, sizeof(rigidbody
) );
351 v3_copy( gs
->angles
, localplayer
.angles
);
353 v3_copy( frame
->cam_pos
, localplayer
.cam
.pos
);
354 v3_copy( frame
->cam_angles
, localplayer
.cam
.angles
);
355 localplayer
.cam
.fov
= frame
->cam_fov
;
357 memcpy( &localplayer
.cam_control
, &gs
->cam_control
,
358 sizeof(struct player_cam_controller
) );
361 VG_STATIC
void skaterift_replay_pre_update(void){
362 if( skaterift
.activity
!= k_skaterift_replay
) return;
365 f64 target
= skaterift
.replay
.cursor
;
367 if( vg_getkey( SDLK_9
) ){
368 target
-= vg
.time_frame_delta
* speed
;
369 skaterift
.replay_control
= k_replay_control_scrub
;
370 replay_seek( &skaterift
.replay
, target
);
372 if( vg_getkey( SDLK_0
) ){
373 target
+= vg
.time_frame_delta
* speed
;
374 skaterift
.replay_control
= k_replay_control_scrub
;
375 replay_seek( &skaterift
.replay
, target
);
378 if( vg_getkey( SDLK_7
) )
379 skaterift
.replay_control
= k_replay_control_play
;
381 if( skaterift
.replay_control
== k_replay_control_play
){
382 target
+= vg
.time_frame_delta
;
383 replay_seek( &skaterift
.replay
, target
);
386 if( vg_getkey( SDLK_8
) ){
387 replay_frame
*prev
= replay_find_recent_stateframe( &skaterift
.replay
);
390 skaterift_restore_frame( prev
);
392 /* chop end off replay */
394 skaterift
.replay
.statehead
= prev
;
395 skaterift
.replay
.head
= prev
;
396 skaterift
.replay
.cursor_frame
= prev
;
397 skaterift
.replay
.cursor
= prev
->time
;
398 skaterift
.replay_control
= k_replay_control_scrub
;
399 skaterift
.activity
= k_skaterift_default
;
400 vg
.time
= prev
->time
;
406 VG_STATIC
void skaterift_replay_debug_info(void){
407 player__debugtext( 2, "replay info" );
409 replay_buffer
*replay
= &skaterift
.replay
;
413 if( replay
->tail
) tail
= (void *)replay
->tail
- replay
->data
;
414 if( replay
->head
) head
= (void *)replay
->head
- replay
->data
;
416 player__debugtext( 1, "head @%u | tail @%u\n", head
, tail
);
418 if( replay
->statehead
){
419 u32 state
= (void *)replay
->statehead
- replay
->data
;
420 player__debugtext( 1, "gs @%u\n", state
);
421 player__debugtext( 1, "gamestate_size: %u\n",
422 replay
->statehead
->subsystem_gamestate_size
);
425 player__debugtext( 1, "gs @NULL\n" );
427 f64 start
= replay
->cursor
,
428 end
= replay
->cursor
;
429 if( replay
->tail
) start
= replay
->tail
->time
;
430 if( replay
->head
) end
= replay
->head
->time
;
432 f64 cur
= replay
->cursor
- start
,
435 player__debugtext( 1, "cursor: %.2fs / %.2fs\n", cur
, len
);
438 VG_STATIC
void skaterift_replay_imgui(void){
439 if( skaterift
.activity
!= k_skaterift_replay
) return;
441 replay_buffer
*replay
= &skaterift
.replay
;
442 f64 start
= replay
->cursor
,
443 end
= replay
->cursor
;
444 if( replay
->tail
) start
= replay
->tail
->time
;
445 if( replay
->head
) end
= replay
->head
->time
;
446 f64 len
= end
- start
,
447 cur
= (replay
->cursor
- start
) / len
;
454 ui_rect bar
= { 0, vg
.window_y
- height
, vg
.window_x
, height
};
455 ui_fill( bar
, ui_colour( k_ui_bg
) );
457 /* cursor frame block */
458 if( replay
->cursor_frame
){
459 if( replay
->cursor_frame
->r
){
460 f64 l
= (replay
->cursor_frame
->r
->time
-replay
->cursor_frame
->time
)/len
,
461 s
= (replay
->cursor_frame
->time
- start
) / len
;
462 ui_rect box
= { s
*(f64
)vg
.window_x
, bar
[1]-2,
463 VG_MAX(4,(ui_px
)(l
*vg
.window_x
)), bar
[3]+2 };
464 ui_fill( box
, ui_colour( k_ui_bg
+4 ) );
469 ui_rect cusor
= { cur
* (f64
)vg
.window_x
- (cwidth
/2), bar
[1],
471 ui_fill( cusor
, ui_colour( k_ui_bg
+7 ) );
473 /* latest state marker */
474 if( replay
->statehead
){
475 f64 t
= (replay
->statehead
->time
- start
) / len
;
476 ui_rect tag
= { t
*(f64
)vg
.window_x
, bar
[1]-8, 2, bar
[3]+8 };
477 ui_fill( tag
, ui_colour( k_ui_green
+k_ui_brighter
) );
480 /* previous state marker */
481 replay_frame
*prev
= replay_find_recent_stateframe( replay
);
483 f64 t
= (prev
->time
- start
) / len
;
484 ui_rect tag
= { t
*(f64
)vg
.window_x
, bar
[1]-8, 2, bar
[3]+8 };
485 ui_fill( tag
, ui_colour( k_ui_yellow
+k_ui_brighter
) );
490 snprintf( buffer
, 128, "-%.2fs\n", (end
-replay
->cursor
) );
491 ui_text( cusor
, buffer
, 1, k_ui_align_middle_left
, 0 );
493 snprintf( buffer
, 128, "-%.2fs\n", len
);
494 ui_text( bar
, buffer
, 1, k_ui_align_middle_left
, 0 );
495 ui_text( bar
, "0s", 1, k_ui_align_middle_right
, 0 );
498 #endif /* PLAYER_REPLAY_C */