3 #include "player_replay.h"
8 #include "player_walk.h"
9 #include "player_skate.h"
10 #include "player_dead.h"
11 #include "player_glide.h"
13 struct replay_globals player_replay
=
15 .active_keyframe
= -1,
20 void replay_clear( replay_buffer
*replay
)
24 replay
->cursor_frame
= NULL
;
25 replay
->statehead
= NULL
;
26 replay
->cursor
= -99999.9;
29 void *replay_frame_data( replay_frame
*frame
, enum replay_framedata type
)
31 if( frame
->data_table
[type
][1] == 0 )
34 void *baseptr
= frame
;
35 return baseptr
+ frame
->data_table
[type
][0];
38 static u16
replay_frame_calculate_data_offsets(
39 u16 data_table
[k_replay_framedata_rows
][2] ){
41 u32 total
= vg_align8( sizeof(replay_frame
) );
42 for( u32 i
=0; i
<k_replay_framedata_rows
; i
++ ){
43 data_table
[i
][0] = total
;
44 total
+= vg_align8(data_table
[i
][1]);
47 vg_fatal_error( "Exceeded frame storage capacity\n" );
52 static void replay_tailpop( replay_buffer
*replay
){
53 if( replay
->cursor_frame
== replay
->tail
)
54 replay
->cursor_frame
= NULL
;
55 if( replay
->statehead
== replay
->tail
)
56 replay
->statehead
= NULL
;
58 replay
->tail
= replay
->tail
->r
;
61 replay
->tail
->l
= NULL
;
66 static replay_frame
*replay_newframe( replay_buffer
*replay
,
71 u16 data_table
[ k_replay_framedata_rows
][2];
72 data_table
[ k_replay_framedata_animator
][1] = animator_size
;
73 data_table
[ k_replay_framedata_gamestate
][1] = gamestate_size
;
74 data_table
[ k_replay_framedata_sfx
][1] = sfx_count
*sizeof(struct net_sfx
);
75 data_table
[ k_replay_framedata_internal_gamestate
][1] = 0;
78 data_table
[ k_replay_framedata_internal_gamestate
][1] =
79 sizeof( replay_gamestate
);
82 data_table
[ k_replay_framedata_glider
][1] = 0;
85 data_table
[ k_replay_framedata_glider
][1] =
86 sizeof(struct replay_glider_data
);
89 u32 nextsize
= replay_frame_calculate_data_offsets( data_table
);
91 replay_frame
*frame
= NULL
;
94 u32 headsize
= replay
->head
->total_size
,
95 nextpos
= ((void *)replay
->head
- replay
->data
) + headsize
;
97 if( nextpos
+ nextsize
> replay
->size
)
101 /* maintain contiguity */
102 while( replay
->tail
)
104 if( (void *)replay
->tail
- replay
->data
)
105 replay_tailpop( replay
);
111 u32 tailpos
= (void *)replay
->tail
- replay
->data
;
113 if( tailpos
>= nextpos
)
115 if( nextpos
+ nextsize
> tailpos
)
117 replay_tailpop( replay
);
124 frame
= replay
->data
+ nextpos
;
127 replay
->head
->r
= frame
;
130 frame
= replay
->data
;
132 for( u32 i
=0; i
<k_replay_framedata_rows
; i
++ )
134 frame
->data_table
[i
][0] = data_table
[i
][0];
135 frame
->data_table
[i
][1] = data_table
[i
][1];
138 frame
->total_size
= nextsize
;
139 frame
->l
= replay
->head
;
141 replay
->head
= frame
;
142 if( !replay
->tail
) replay
->tail
= frame
;
143 if( gamestate_size
) replay
->statehead
= frame
;
148 static void replay_emit_frame_sounds( replay_frame
*frame
){
149 void *baseptr
= frame
;
150 u16
*inf
= frame
->data_table
[k_replay_framedata_sfx
];
151 struct net_sfx
*buffer
= baseptr
+ inf
[0];
152 u32 count
= inf
[1] / sizeof(struct net_sfx
);
154 for( u32 i
=0; i
<count
; i
++ ){
155 net_sfx_play( buffer
+ i
);
159 int replay_seek( replay_buffer
*replay
, f64 t
)
161 if( !replay
->head
) return 0;
163 if( t
< replay
->tail
->time
) t
= replay
->tail
->time
;
164 if( t
> replay
->head
->time
) t
= replay
->head
->time
;
166 if( !replay
->cursor_frame
) {
167 replay
->cursor
= replay
->head
->time
;
168 replay
->cursor_frame
= replay
->head
;
170 if( fabs(replay
->head
->time
-t
) > fabs(replay
->tail
->time
-t
) ){
171 replay
->cursor
= replay
->tail
->time
;
172 replay
->cursor_frame
= replay
->tail
;
176 f64 dir
= t
- replay
->cursor
;
177 if( dir
== 0.0 ) return 0;
178 dir
= vg_signf( dir
);
184 if( t
> replay
->cursor_frame
->time
) {
191 if( dir
> 0.0 ) next
= replay
->cursor_frame
->r
;
192 else next
= replay
->cursor_frame
->l
;
197 if( t
< next
->time
){
203 replay_emit_frame_sounds( next
);
205 replay
->cursor_frame
= next
;
206 replay
->cursor
= next
->time
;
215 replay_frame
*replay_find_recent_stateframe( replay_buffer
*replay
)
217 replay_frame
*frame
= replay
->cursor_frame
;
220 if( !frame
) return frame
;
221 if( frame
->data_table
[ k_replay_framedata_gamestate
][1] ) return frame
;
228 f32
replay_subframe_time( replay_buffer
*replay
)
230 replay_frame
*frame
= replay
->cursor_frame
;
231 if( !frame
) return 0.0f
;
232 replay_frame
*next
= frame
->r
;
235 f64 l
= next
->time
- frame
->time
,
236 t
= (l
<= (1.0/128.0))? 0.0: (replay
->cursor
- frame
->time
) / l
;
237 return vg_clampf( t
, 0.0f
, 1.0f
);
243 void replay_get_frame_camera( replay_frame
*frame
, vg_camera
*cam
)
245 cam
->fov
= frame
->cam
.fov
;
246 v3_copy( frame
->cam
.pos
, cam
->pos
);
247 v3_copy( frame
->cam
.angles
, cam
->angles
);
250 void replay_get_camera( replay_buffer
*replay
, vg_camera
*cam
)
254 if( replay
->cursor_frame
)
256 replay_frame
*next
= replay
->cursor_frame
->r
;
262 replay_get_frame_camera( replay
->cursor_frame
, cam
);
263 replay_get_frame_camera( next
, &temp
);
264 vg_camera_lerp( cam
, &temp
, replay_subframe_time( replay
), cam
);
268 replay_get_frame_camera( replay
->cursor_frame
, cam
);
274 v3_zero( cam
->angles
);
279 void skaterift_get_replay_cam( vg_camera
*cam
)
281 replay_buffer
*replay
= &player_replay
.local
;
283 if( player_replay
.active_keyframe
!= -1 )
285 replay_keyframe
*kf
=
286 &player_replay
.keyframes
[player_replay
.active_keyframe
];
288 v3_copy( kf
->cam
.pos
, cam
->pos
);
289 v3_copy( kf
->cam
.angles
, cam
->angles
);
290 cam
->fov
= kf
->cam
.fov
;
294 if( player_replay
.keyframe_count
>= 2 )
296 for( u32 i
=0; i
<player_replay
.keyframe_count
-1; i
++ )
298 replay_keyframe
*kf
= &player_replay
.keyframes
[i
];
300 if( (kf
[0].time
<=replay
->cursor
) && (kf
[1].time
>replay
->cursor
) )
302 f64 l
= kf
[1].time
- kf
[0].time
,
303 t
= (l
<= (1.0/128.0))? 0.0: (replay
->cursor
-kf
[0].time
) / l
;
305 if( player_replay
.keyframe_count
>= 3 )
307 f32 m_start
= 0.5f
, m_end
= 0.5f
;
311 if( (t
< 0.5f
) || (i
==player_replay
.keyframe_count
-2) )
317 u32 last
= player_replay
.keyframe_count
-1;
318 if( kf
+0 == player_replay
.keyframes
) m_start
= 1.0f
;
319 if( kf
+2 == player_replay
.keyframes
+last
) m_end
= 1.0f
;
321 f32 ts
= vg_lerpf( kf
[0].time
, kf
[1].time
, 1.0f
-m_start
),
322 te
= vg_lerpf( kf
[1].time
, kf
[2].time
, m_end
);
325 t
= (replay
->cursor
-ts
)/l
;
328 * Adjust t, so that its derivative matches at the endpoints.
329 * Since t needs to go from 0 to 1, it will naturally change at
330 * different rates between keyframes. So this smooths it out.
332 * Newton method, going through standard direct quadratic eq has
333 * precision / other problems. Also we only care about 0>t>1.
335 f32 b
= (kf
[1].time
-ts
)/l
,
337 for( u32 i
=0; i
<4; i
++ )
340 fx_x0
= 2.0f
*b
*x0
*ix0
+ ix0
*ix0
- t
,
341 fxd_x0
= 2.0f
*(-2.0f
*b
*x0
+ b
+ x0
- 1.0f
);
342 x0
= x0
- (fx_x0
/fxd_x0
);
346 f32 t0
= t
*m_start
+(1.0f
-m_start
),
353 v3_lerp( kf
[0].cam
.pos
, kf
[1].cam
.pos
, t0
, ps
);
354 vg_camera_lerp_angles( kf
[0].cam
.angles
, kf
[1].cam
.angles
,
356 fs
= vg_lerpf( kf
[0].cam
.fov
, kf
[1].cam
.fov
, t0
);
358 v3_lerp( kf
[1].cam
.pos
, kf
[2].cam
.pos
, t1
, pe
);
359 vg_camera_lerp_angles( kf
[1].cam
.angles
, kf
[2].cam
.angles
,
361 fe
= vg_lerpf( kf
[1].cam
.fov
, kf
[2].cam
.fov
, t1
);
364 v3_lerp( ps
, pe
, t
, cam
->pos
);
365 vg_camera_lerp_angles( as
, ae
, t
, cam
->angles
);
366 cam
->fov
= vg_lerpf( fs
, fe
, t
);
370 v3_lerp( kf
[0].cam
.pos
, kf
[1].cam
.pos
, t
, cam
->pos
);
371 vg_camera_lerp_angles( kf
[0].cam
.angles
, kf
[1].cam
.angles
,
373 cam
->fov
= vg_lerpf( kf
[0].cam
.fov
, kf
[1].cam
.fov
, t
);
380 replay_get_camera( replay
, cam
);
389 void skaterift_record_frame( replay_buffer
*replay
, int force_gamestate
)
391 f64 delta
= 9999999.9,
392 statedelta
= 9999999.9;
395 delta
= vg
.time
- replay
->head
->time
;
397 if( replay
->statehead
)
398 statedelta
= vg
.time
- replay
->statehead
->time
;
400 const f64 k_replay_rate
= 1.0/30.0,
401 k_gamestate_rate
= 0.5;
407 if( force_gamestate
) save_state
= 1;
408 if( statedelta
> k_gamestate_rate
) save_state
= 1;
409 if( delta
> k_replay_rate
) save_frame
= 1;
410 if( save_state
) save_frame
= 1;
412 if( localplayer
.have_glider
|| localplayer
.glider_orphan
||
413 localplayer
.subsystem
== k_player_subsystem_glide
){
417 if( !save_frame
) return;
419 u16 gamestate_size
= 0;
421 /* TODO: have as part of system struct */
422 gamestate_size
= (u32
[]){
423 [k_player_subsystem_walk
] = sizeof(struct player_walk_state
),
424 [k_player_subsystem_drive
] = 0,
425 [k_player_subsystem_skate
] = sizeof(struct player_skate_state
),
426 [k_player_subsystem_dead
] = localplayer
.ragdoll
.part_count
*
427 sizeof(struct replay_rb
),
428 [k_player_subsystem_glide
] = sizeof(struct replay_rb
),
429 }[ localplayer
.subsystem
];
432 u16 animator_size
= player_subsystems
[localplayer
.subsystem
]->animator_size
;
434 replay_frame
*frame
= replay_newframe( replay
,
435 animator_size
, gamestate_size
,
436 localplayer
.local_sfx_buffer_count
,
438 frame
->system
= localplayer
.subsystem
;
441 replay_gamestate
*gs
=
442 replay_frame_data( frame
, k_replay_framedata_internal_gamestate
);
444 gs
->current_run_version
= world_static
.current_run_version
;
445 gs
->drowned
= localplayer
.drowned
;
447 /* permanent block */
448 memcpy( &gs
->rb
, &localplayer
.rb
, sizeof(rigidbody
) );
449 memcpy( &gs
->glider_rb
, &player_glide
.rb
, sizeof(rigidbody
) );
450 memcpy( &gs
->cam_control
, &localplayer
.cam_control
,
451 sizeof(struct player_cam_controller
) );
452 v3_copy( localplayer
.angles
, gs
->angles
);
454 void *dst
= replay_frame_data( frame
, k_replay_framedata_gamestate
);
456 /* subsytem/dynamic block */
457 if( localplayer
.subsystem
== k_player_subsystem_walk
)
458 memcpy( dst
, &player_walk
.state
, gamestate_size
);
459 else if( localplayer
.subsystem
== k_player_subsystem_skate
)
460 memcpy( dst
, &player_skate
.state
, gamestate_size
);
461 else if( localplayer
.subsystem
== k_player_subsystem_dead
){
462 struct replay_rb
*arr
= dst
;
463 for( u32 i
=0; i
<localplayer
.ragdoll
.part_count
; i
++ ){
464 rigidbody
*rb
= &localplayer
.ragdoll
.parts
[i
].rb
;
465 v3_copy( rb
->co
, arr
[i
].co
);
466 v3_copy( rb
->w
, arr
[i
].w
);
467 v3_copy( rb
->v
, arr
[i
].v
);
468 v4_copy( rb
->q
, arr
[i
].q
);
471 else if( localplayer
.subsystem
== k_player_subsystem_glide
){
472 struct replay_rb
*arr
= dst
;
473 rigidbody
*rb
= &player_glide
.rb
;
474 v3_copy( rb
->co
, arr
[0].co
);
475 v3_copy( rb
->w
, arr
[0].w
);
476 v3_copy( rb
->v
, arr
[0].v
);
477 v4_copy( rb
->q
, arr
[0].q
);
482 struct replay_glider_data
*inf
=
483 replay_frame_data( frame
, k_replay_framedata_glider
);
485 inf
->have_glider
= localplayer
.have_glider
;
486 inf
->glider_orphan
= localplayer
.glider_orphan
;
487 inf
->t
= player_glide
.t
;
488 v3_copy( player_glide
.rb
.co
, inf
->co
);
489 v4_copy( player_glide
.rb
.q
, inf
->q
);
492 replay
->cursor
= vg
.time
;
493 replay
->cursor_frame
= frame
;
494 frame
->time
= vg
.time
;
497 v3_copy( localplayer
.cam
.pos
, frame
->cam
.pos
);
498 if( localplayer
.gate_waiting
){
499 m4x3_mulv( localplayer
.gate_waiting
->transport
,
500 frame
->cam
.pos
, frame
->cam
.pos
);
503 v3_angles_vector( localplayer
.cam
.angles
, v0
);
504 m3x3_mulv( localplayer
.gate_waiting
->transport
, v0
, v0
);
505 v3_angles( v0
, frame
->cam
.angles
);
508 v3_copy( localplayer
.cam
.angles
, frame
->cam
.angles
);
510 frame
->cam
.fov
= localplayer
.cam
.fov
;
513 void *dst
= replay_frame_data( frame
, k_replay_framedata_animator
),
514 *src
= player_subsystems
[localplayer
.subsystem
]->animator_data
;
515 memcpy( dst
, src
, animator_size
);
518 memcpy( replay_frame_data( frame
, k_replay_framedata_sfx
),
519 localplayer
.local_sfx_buffer
,
520 sizeof(struct net_sfx
)*localplayer
.local_sfx_buffer_count
);
522 localplayer
.local_sfx_buffer_count
= 0;
525 static void skaterift_restore_frame( replay_frame
*frame
)
527 replay_gamestate
*gs
=
528 replay_frame_data( frame
, k_replay_framedata_internal_gamestate
);
529 void *src
= replay_frame_data( frame
, k_replay_framedata_gamestate
);
530 u16 src_size
= frame
->data_table
[ k_replay_framedata_gamestate
][1];
531 world_static
.current_run_version
= gs
->current_run_version
;
532 localplayer
.drowned
= gs
->drowned
;
534 if(frame
->system
== k_player_subsystem_walk
){
535 memcpy( &player_walk
.state
, src
, src_size
);
537 else if( frame
->system
== k_player_subsystem_skate
){
538 memcpy( &player_skate
.state
, src
, src_size
);
540 else if( frame
->system
== k_player_subsystem_dead
){
541 player__dead_transition(0);
542 struct replay_rb
*arr
= src
;
544 for( u32 i
=0; i
<localplayer
.ragdoll
.part_count
; i
++ ){
545 struct ragdoll_part
*part
= &localplayer
.ragdoll
.parts
[i
];
546 rigidbody
*rb
= &part
->rb
;
548 v3_copy( arr
[i
].co
, rb
->co
);
549 v3_copy( arr
[i
].w
, rb
->w
);
550 v3_copy( arr
[i
].v
, rb
->v
);
551 v4_copy( arr
[i
].q
, rb
->q
);
553 v3_copy( arr
[i
].co
, part
->prev_co
);
554 v4_copy( arr
[i
].q
, part
->prev_q
);
555 rb_update_matrices( rb
);
558 else if( frame
->system
== k_player_subsystem_glide
){
559 struct replay_rb
*arr
= src
;
560 rigidbody
*rb
= &player_glide
.rb
;
561 v3_copy( arr
[0].co
, rb
->co
);
562 v3_copy( arr
[0].w
, rb
->w
);
563 v3_copy( arr
[0].v
, rb
->v
);
564 v4_copy( arr
[0].q
, rb
->q
);
565 rb_update_matrices( rb
);
568 localplayer
.subsystem
= frame
->system
;
570 /* restore the seperated glider data if we have it */
571 if( frame
->data_table
[ k_replay_framedata_glider
][1] ){
572 struct replay_glider_data
*inf
=
573 replay_frame_data( frame
, k_replay_framedata_glider
);
575 localplayer
.have_glider
= inf
->have_glider
;
576 localplayer
.glider_orphan
= inf
->glider_orphan
;
577 player_glide
.t
= inf
->t
;
580 localplayer
.have_glider
= 0;
581 localplayer
.glider_orphan
= 0;
582 player_glide
.t
= 0.0f
;
585 memcpy( &localplayer
.rb
, &gs
->rb
, sizeof(rigidbody
) );
586 memcpy( &player_glide
.rb
, &gs
->glider_rb
, sizeof(rigidbody
) );
587 v3_copy( gs
->angles
, localplayer
.angles
);
589 v3_copy( frame
->cam
.pos
, localplayer
.cam
.pos
);
590 v3_copy( frame
->cam
.angles
, localplayer
.cam
.angles
);
591 localplayer
.cam
.fov
= frame
->cam
.fov
;
593 memcpy( &localplayer
.cam_control
, &gs
->cam_control
,
594 sizeof(struct player_cam_controller
) );
596 /* chop end off replay */
598 player_replay
.local
.statehead
= frame
;
599 player_replay
.local
.head
= frame
;
600 player_replay
.local
.cursor_frame
= frame
;
601 player_replay
.local
.cursor
= frame
->time
;
602 player_replay
.replay_control
= k_replay_control_scrub
;
603 skaterift
.activity
= k_skaterift_default
;
604 vg
.time
= frame
->time
;
607 static void skaterift_replay_resume(void){
608 replay_frame
*prev
= replay_find_recent_stateframe(&player_replay
.local
);
611 player_replay
.replay_control
= k_replay_control_resume
;
612 player_replay
.resume_target
= prev
;
613 player_replay
.resume_begin
= player_replay
.local
.cursor
;
614 player_replay
.resume_transition
= 0.0f
;
620 static void skaterift_replay_update_helpers(void);
622 void skaterift_replay_pre_update(void)
624 if( skaterift
.activity
!= k_skaterift_replay
) return;
626 bool input
= player_replay
.editor_mode
^0x1;
628 if( player_replay
.replay_control
== k_replay_control_resume
)
630 if( player_replay
.local
.cursor_frame
== player_replay
.resume_target
||
631 player_replay
.local
.cursor_frame
== NULL
)
633 skaterift_restore_frame( player_replay
.resume_target
);
637 vg_slewf( &player_replay
.resume_transition
, 1.0f
,
638 vg
.time_frame_delta
* (1.0f
/1.0f
) );
640 if( player_replay
.resume_transition
>= 1.0f
)
641 skaterift_restore_frame( player_replay
.resume_target
);
643 f64 target
= vg_lerp( player_replay
.resume_begin
,
644 player_replay
.resume_target
->time
,
645 vg_smoothstepf( player_replay
.resume_transition
) );
646 if( replay_seek( &player_replay
.local
, target
) )
647 player_replay
.track_velocity
= 1.0f
;
649 player_replay
.track_velocity
= 0.0f
;
655 if( input
&& button_down( k_srbind_replay_play
) )
656 player_replay
.replay_control
= k_replay_control_play
;
657 if( input
&& button_down( k_srbind_replay_freecam
) )
659 player_replay
.use_freecam
^= 0x1;
661 if( player_replay
.use_freecam
)
663 replay_get_camera( &player_replay
.local
,
664 &player_replay
.replay_freecam
);
666 skaterift_replay_update_helpers();
669 f32 target_speed
= 0.0f
;
671 target_speed
= axis_state( k_sraxis_replay_h
) * 5.0;
673 if( input
&& button_press( k_srbind_reset
) )
674 target_speed
+= -2.0;
676 if( fabsf(target_speed
) > 0.01f
)
677 player_replay
.replay_control
= k_replay_control_scrub
;
679 if( player_replay
.replay_control
== k_replay_control_play
)
682 vg_slewf( &player_replay
.track_velocity
, target_speed
,
683 18.0f
*vg
.time_frame_delta
);
685 if( fabsf( player_replay
.track_velocity
) > 0.0001f
)
687 f64 target
= player_replay
.local
.cursor
;
688 target
+= player_replay
.track_velocity
* vg
.time_frame_delta
;
690 if( !replay_seek( &player_replay
.local
, target
) )
691 player_replay
.track_velocity
= 0.0f
;
694 if( input
&& button_down( k_srbind_mback
) )
696 if( player_replay
.local
.statehead
)
697 skaterift_restore_frame( player_replay
.local
.statehead
);
699 skaterift
.activity
= k_skaterift_default
;
700 srinput
.state
= k_input_state_resume
;
706 if( player_replay
.use_freecam
)
712 if( button_down( k_srbind_replay_resume
) )
714 skaterift_replay_resume();
721 static void skaterift_replay_update_helpers(void)
723 player_replay
.helper_resume
->greyed
= player_replay
.use_freecam
;
726 vg_strnull( &freecam_text
, player_replay
.helper_freecam
->text
,
727 GUI_HELPER_TEXT_LENGTH
);
728 vg_strcat( &freecam_text
,
729 player_replay
.use_freecam
? "Exit freecam": "Freecam" );
732 static void replay_show_helpers(void)
737 if( gui_new_helper( input_axis_list
[k_sraxis_replay_h
], &text
) )
738 vg_strcat( &text
, "Scrub" );
740 if( (player_replay
.helper_resume
= gui_new_helper(
741 input_button_list
[k_srbind_replay_resume
], &text
)) )
742 vg_strcat( &text
, "Resume" );
744 if( gui_new_helper( input_button_list
[k_srbind_replay_play
], &text
))
745 vg_strcat( &text
, "Playback" );
747 player_replay
.helper_freecam
= gui_new_helper(
748 input_button_list
[k_srbind_replay_freecam
], &text
);
750 skaterift_replay_update_helpers();
753 void skaterift_replay_post_render(void)
755 #ifndef SR_ALLOW_REWIND_HUB
756 if( world_static
.active_instance
!= k_world_purpose_client
)
760 /* capture the current resume frame at the very last point */
761 if( button_down( k_srbind_reset
) )
763 if( skaterift
.activity
== k_skaterift_default
)
765 localplayer
.rewinded_since_last_gate
= 1;
766 skaterift
.activity
= k_skaterift_replay
;
767 skaterift_record_frame( &player_replay
.local
, 1 );
768 if( player_replay
.local
.head
)
770 player_replay
.local
.cursor
= player_replay
.local
.head
->time
;
771 player_replay
.local
.cursor_frame
= player_replay
.local
.head
;
773 player_replay
.replay_control
= k_replay_control_scrub
;
774 replay_show_helpers();
779 void skaterift_replay_init(void)
781 u32 bytes
= 1024*1024*10;
782 player_replay
.local
.data
= vg_linear_alloc( vg_mem
.rtmemory
, bytes
);
783 player_replay
.local
.size
= bytes
;
784 replay_clear( &player_replay
.local
);
787 void skaterift_replay_debug_info( ui_context
*ctx
)
789 player__debugtext( ctx
, 2, "replay info" );
790 replay_buffer
*replay
= &player_replay
.local
;
794 if( replay
->tail
) tail
= (void *)replay
->tail
- replay
->data
;
795 if( replay
->head
) head
= (void *)replay
->head
- replay
->data
;
797 player__debugtext( ctx
, 1, "head @%u | tail @%u\n", head
, tail
);
799 if( replay
->statehead
)
801 for( u32 i
=0; i
<k_replay_framedata_rows
; i
++ )
803 player__debugtext( ctx
, 1, "[%u]: [%hu, %hu]\n", i
,
804 replay
->statehead
->data_table
[i
][0],
805 replay
->statehead
->data_table
[i
][1] );
807 u32 state
= (void *)replay
->statehead
- replay
->data
;
808 player__debugtext( ctx
, 1, "gs @%u\n", state
);
809 player__debugtext( ctx
, 1, "gamestate_size: %hu\n",
810 replay
->statehead
->data_table
[k_replay_framedata_gamestate
][1] );
813 player__debugtext( ctx
, 1, "gs @NULL\n" );
815 f64 start
= replay
->cursor
,
816 end
= replay
->cursor
;
817 if( replay
->tail
) start
= replay
->tail
->time
;
818 if( replay
->head
) end
= replay
->head
->time
;
820 f64 cur
= replay
->cursor
- start
,
823 player__debugtext( ctx
, 1, "cursor: %.2fs / %.2fs\n", cur
, len
);
826 static int _keyframe_cmp( const void *p1
, const void *p2
)
828 const replay_keyframe
*kf1
= p1
, *kf2
= p2
;
829 return kf1
->time
> kf2
->time
;
832 static void replay_keyframe_sort(void)
834 qsort( player_replay
.keyframes
, player_replay
.keyframe_count
,
835 sizeof(replay_keyframe
), _keyframe_cmp
);
838 static void replay_fly_edit_keyframe( ui_context
*ctx
, replay_keyframe
*kf
)
840 if( ui_click_down( ctx
, UI_MOUSE_LEFT
) )
843 v3_copy( kf
->cam
.pos
, player_replay
.replay_freecam
.pos
);
844 v3_copy( kf
->cam
.angles
, player_replay
.replay_freecam
.angles
);
845 v3_zero( player_replay
.freecam_v
);
846 v3_zero( player_replay
.freecam_w
);
847 player_replay
.replay_freecam
.fov
= kf
->cam
.fov
;
851 ui_capture_mouse( ctx
, 0 );
854 if( vg_getkey(SDLK_q
) )
855 player_replay
.freecam_v
[1] -= vg
.time_frame_delta
*6.0f
*20.0f
;
856 if( vg_getkey(SDLK_e
) )
857 player_replay
.freecam_v
[1] += vg
.time_frame_delta
*6.0f
*20.0f
;
859 v3_copy( player_replay
.replay_freecam
.pos
, skaterift
.cam
.pos
);
860 v3_copy( player_replay
.replay_freecam
.angles
, skaterift
.cam
.angles
);
861 skaterift
.cam
.fov
= player_replay
.replay_freecam
.fov
;
863 v3_copy( skaterift
.cam
.pos
, kf
->cam
.pos
);
864 v3_copy( skaterift
.cam
.angles
, kf
->cam
.angles
);
865 kf
->cam
.fov
= skaterift
.cam
.fov
;
868 void skaterift_replay_imgui( ui_context
*ctx
)
870 if( skaterift
.activity
!= k_skaterift_replay
) return;
872 /* extra keys for entering editor */
873 static u8 f1_key
= 0;
874 u8 f1_now
= vg_getkey(SDLK_F1
);
875 if( f1_now
&& !f1_key
&& player_replay
.show_ui
)
877 player_replay
.editor_mode
^= 0x1;
879 if( player_replay
.editor_mode
)
882 replay_show_helpers();
886 static u8 f2_key
= 0;
887 u8 f2_now
= vg_getkey(SDLK_F2
);
888 if( f2_now
&& !f2_key
)
890 player_replay
.show_ui
^= 0x1;
894 if( player_replay
.editor_mode
)
896 static u8 space_key
= 0;
897 u8 space_now
= vg_getkey(SDLK_SPACE
);
898 if( space_now
& !space_key
)
900 player_replay
.replay_control
^= k_replay_control_play
;
902 space_key
= space_now
;
905 if( !player_replay
.show_ui
) return;
907 if( player_replay
.editor_mode
)
909 u32 colour
= ui_opacity( ui_colour(ctx
,k_ui_fg
), 0.3333f
);
910 ui_rect cx
= { vg
.window_x
/2, 0, 1, vg
.window_y
},
911 cy
= { 0, vg
.window_y
/2, vg
.window_x
, 1 };
912 ui_fill( ctx
, cx
, colour
);
913 ui_fill( ctx
, cy
, colour
);
916 replay_buffer
*replay
= &player_replay
.local
;
917 f64 start
= replay
->cursor
,
918 end
= replay
->cursor
;
919 if( replay
->tail
) start
= replay
->tail
->time
;
920 if( replay
->head
) end
= replay
->head
->time
;
921 f64 len
= end
- start
,
922 cur
= (replay
->cursor
- start
) / len
;
929 ui_rect timeline
= { 0, 0, vg
.window_x
, height
};
930 ui_fill( ctx
, timeline
, ui_colour( ctx
, k_ui_bg
) );
932 /* cursor frame block */
933 if( replay
->cursor_frame
)
935 if( replay
->cursor_frame
->r
)
937 f64 l
= (replay
->cursor_frame
->r
->time
-replay
->cursor_frame
->time
)/len
,
938 s
= (replay
->cursor_frame
->time
- start
) / len
;
939 ui_rect box
= { s
*(f64
)vg
.window_x
, 0,
940 VG_MAX(4,(ui_px
)(l
*vg
.window_x
)), timeline
[3]+2 };
941 ui_fill( ctx
, box
, ui_colour( ctx
, k_ui_bg
+4 ) );
946 ui_rect cusor
= { cur
* (f64
)vg
.window_x
- (cwidth
/2), 0,
947 cwidth
, (player_replay
.editor_mode
? 0: 16) + timeline
[3] };
948 ui_fill( ctx
, cusor
, ui_colour( ctx
, k_ui_bg
+7 ) );
950 /* latest state marker */
951 if( replay
->statehead
)
953 f64 t
= (replay
->statehead
->time
- start
) / len
;
954 ui_rect tag
= { t
*(f64
)vg
.window_x
, 0, 2, timeline
[3]+8 };
955 ui_fill( ctx
, tag
, ui_colour( ctx
, k_ui_green
+k_ui_brighter
) );
958 /* previous state marker */
959 replay_frame
*prev
= replay_find_recent_stateframe( replay
);
962 f64 t
= (prev
->time
- start
) / len
;
963 ui_rect tag
= { t
*(f64
)vg
.window_x
, 0, 2, timeline
[3]+8 };
964 ui_fill( ctx
, tag
, ui_colour( ctx
, k_ui_yellow
+k_ui_brighter
) );
967 snprintf( buffer
, 128, "-%.2fs (F1: Edit replay)", (end
-replay
->cursor
) );
968 ui_text( ctx
, timeline
, buffer
, 1, k_ui_align_middle_left
, 0 );
969 ui_text( ctx
, timeline
, "0s", 1, k_ui_align_middle_right
, 0 );
971 if( !player_replay
.editor_mode
) return;
972 ui_capture_mouse( ctx
, 1 );
974 ui_rect panel
= { 0, timeline
[3] + 20, 200, 400 };
975 ui_fill( ctx
, panel
, ui_opacity( ui_colour( ctx
, k_ui_bg
), 0.5f
) );
976 ui_rect_pad( panel
, (ui_px
[2]){4,4} );
978 if( ui_button( ctx
, panel
,
979 (player_replay
.replay_control
== k_replay_control_play
)?
980 "Pause (space)": "Play (space)" ) == k_ui_button_click
)
982 player_replay
.replay_control
^= k_replay_control_play
;
986 ui_rect script
= { 0, height
+ 2, vg
.window_x
, 16 };
987 ui_fill( ctx
, script
, ui_colour( ctx
, k_ui_bg
) );
988 f64 mouse_t
= start
+ ((f64
)ctx
->mouse
[0] / (f64
)vg
.window_x
)*len
;
990 /* keyframe draw and select */
991 bool absorb_by_keyframe
= 0;
993 for( u32 i
=0; i
<player_replay
.keyframe_count
; i
++ )
995 replay_keyframe
*kf
= &player_replay
.keyframes
[i
];
996 f64 t
= (kf
->time
-start
)/len
;
998 ui_px x
= t
*(f64
)vg
.window_x
-8;
1000 /* draw connections between keyframes */
1003 ui_rect con
= { lx
, script
[1]+7, x
-lx
, 1 };
1004 ui_fill( ctx
, con
, ui_colour( ctx
, k_ui_blue
) );
1005 vg_line( kf
->cam
.pos
, player_replay
.keyframes
[i
-1].cam
.pos
, VG__BLUE
);
1008 /* keyframe selection */
1009 ui_rect tag
= { x
, script
[1], 16, 16 };
1011 if( ui_inside_rect( tag
, ctx
->mouse
) )
1013 absorb_by_keyframe
= 1;
1015 if( ui_click_down( ctx
, UI_MOUSE_LEFT
) )
1017 if( player_replay
.active_keyframe
!= i
)
1019 player_replay
.active_keyframe
= i
;
1020 replay_seek( &player_replay
.local
, kf
->time
);
1025 ui_outline( ctx
, tag
, 1, ui_colour(ctx
, k_ui_fg
), 0 );
1030 u32 drag_colour
= ui_opacity( ui_colour(ctx
, k_ui_bg
+2), 0.5f
);
1031 if( i
== player_replay
.active_keyframe
)
1033 ui_outline( ctx
, tag
, 2, ui_colour(ctx
, k_ui_fg
), 0 );
1035 ui_rect tray
= { tag
[0]+8-32, tag
[1]+16+2, 64, 16 };
1036 ui_rect dragbar
= { tray
[0]+16, tray
[1], 32, 16 };
1038 bool pos_correct
= 0;
1040 if( ui_inside_rect( dragbar
, ctx
->mouse_click
) )
1042 if( ui_clicking( ctx
, UI_MOUSE_LEFT
) )
1044 drag_colour
= ui_opacity( ui_colour(ctx
,k_ui_fg
), 0.5f
);
1046 replay_seek( &player_replay
.local
, mouse_t
);
1048 else if( ui_click_up( ctx
, UI_MOUSE_LEFT
) )
1052 replay_keyframe_sort();
1054 for( u32 j
=0; j
<player_replay
.keyframe_count
; j
++ )
1056 if( player_replay
.keyframes
[j
].time
== mouse_t
)
1058 player_replay
.active_keyframe
= j
;
1066 tag
[0] = ctx
->mouse
[0]-8;
1067 tray
[0] = tag
[0]+8-32;
1068 dragbar
[0] = tray
[0]+16;
1072 if( ui_inside_rect( dragbar
, ctx
->mouse
) )
1074 ctx
->cursor
= k_ui_cursor_hand
;
1080 ui_opacity( ui_colour( ctx
, k_ui_bg
+2 ), 0.5f
) );
1083 ui_fill( ctx
, dragbar
, drag_colour
);
1084 ui_text( ctx
, dragbar
, ":::", 1, k_ui_align_middle_center
, 0 );
1088 ui_rect btn
= { tray
[0], tray
[1], 16, 16 };
1089 if( ui_button_text( ctx
, btn
, "X", 1 ) == k_ui_button_click
)
1091 for( u32 j
=i
; j
<player_replay
.keyframe_count
-1; j
++ )
1092 player_replay
.keyframes
[j
] = player_replay
.keyframes
[j
+1];
1094 player_replay
.keyframe_count
--;
1095 player_replay
.active_keyframe
= -1;
1098 ui_rect btn1
= { tray
[0]+48, tray
[1], 16, 16 };
1100 enum ui_button_state mask_using
=
1101 k_ui_button_holding_inside
|
1102 k_ui_button_holding_outside
|
1105 if( ui_button_text( ctx
, btn1
, "E", 1 ) & mask_using
)
1107 replay_fly_edit_keyframe( ctx
, kf
);
1108 vg_ui_set_mouse_pos( btn1
[0]+8, btn1
[1]+8 );
1113 ui_fill( ctx
, tag
, ui_colour( ctx
, k_ui_blue
) );
1117 /* adding keyframes */
1118 if( ui_inside_rect( script
, ctx
->mouse
) )
1120 ctx
->cursor
= k_ui_cursor_hand
;
1122 ui_rect cursor
= { ctx
->mouse
[0], script
[1], 4, 16 };
1123 ui_fill( ctx
, cursor
, ui_colour( ctx
, k_ui_fg
) );
1125 if( !absorb_by_keyframe
&& ui_click_down( ctx
, UI_MOUSE_LEFT
) )
1127 u32 max
= vg_list_size( player_replay
.keyframes
);
1128 if( player_replay
.keyframe_count
== max
)
1130 ui_start_modal( ctx
, "Maximum keyframes reached", UI_MODAL_BAD
);
1134 replay_keyframe
*kf
=
1135 &player_replay
.keyframes
[player_replay
.keyframe_count
++];
1138 v3_copy( skaterift
.cam
.pos
, kf
->cam
.pos
);
1139 v3_copy( skaterift
.cam
.angles
, kf
->cam
.angles
);
1140 kf
->cam
.fov
= skaterift
.cam
.fov
;
1142 replay_keyframe_sort();
1147 /* timeline scrub */
1148 bool start_in_timeline
=
1149 ui_clicking(ctx
, UI_MOUSE_LEFT
) &&
1150 ui_inside_rect(timeline
, ctx
->mouse_click
);
1151 if( (ui_inside_rect( timeline
, ctx
->mouse
)) || start_in_timeline
)
1153 ui_rect cursor
= { ctx
->mouse
[0], timeline
[1], 4, timeline
[3] };
1154 ui_fill( ctx
, cursor
, ui_colour( ctx
, k_ui_fg
) );
1155 ctx
->cursor
= k_ui_cursor_ibeam
;
1157 if( ui_clicking( ctx
, UI_MOUSE_LEFT
) && start_in_timeline
)
1159 replay_seek( &player_replay
.local
, mouse_t
);
1160 player_replay
.active_keyframe
= -1;
1164 if( ui_button( ctx
, panel
, "Clear keyframes" ) == k_ui_button_click
)
1166 player_replay
.keyframe_count
= 0;
1169 if( (ui_button( ctx
, panel
, "Hide UI (F2)" ) == k_ui_button_click
) )
1171 player_replay
.show_ui
^= 0x1;
1174 if( player_replay
.active_keyframe
!= -1 )
1176 replay_keyframe
*kf
=
1177 &player_replay
.keyframes
[ player_replay
.active_keyframe
];
1179 enum ui_button_state mask_using
=
1180 k_ui_button_holding_inside
|
1181 k_ui_button_holding_outside
|
1184 if( ui_button( ctx
, panel
, "Edit cam" ) & mask_using
)
1186 replay_fly_edit_keyframe( ctx
, kf
);
1190 ui_info( ctx
, panel
, "World settings" );
1191 f32 new_time
= world_current_instance()->time
;
1192 if( ui_slider( ctx
, panel
, "Time of day", 0, 1, &new_time
) )
1194 world_current_instance()->time
= new_time
;
1197 ui_info( ctx
, panel
, "" );
1198 if( ui_button( ctx
, panel
, "Exit editor (F1)" ) == k_ui_button_click
)
1200 player_replay
.editor_mode
= 0;
1201 replay_show_helpers();
1204 /* TODO: Add Q/E scrub here too.
1205 * Add replay trimming