server loopback tool
[carveJwlIkooP6JGAAIwe30JlM.git] / player_replay.c
1 #include "skaterift.h"
2 #include "player.h"
3 #include "player_replay.h"
4 #include "input.h"
5 #include "gui.h"
6 #include "freecam.h"
7
8 #include "player_walk.h"
9 #include "player_skate.h"
10 #include "player_dead.h"
11 #include "player_glide.h"
12
13 struct replay_globals player_replay =
14 {
15 .active_keyframe = -1,
16 .show_ui = 1,
17 .editor_mode = 0
18 };
19
20 void replay_clear( replay_buffer *replay )
21 {
22 replay->head = NULL;
23 replay->tail = NULL;
24 replay->cursor_frame = NULL;
25 replay->statehead = NULL;
26 replay->cursor = -99999.9;
27 }
28
29 void *replay_frame_data( replay_frame *frame, enum replay_framedata type )
30 {
31 if( frame->data_table[type][1] == 0 )
32 return NULL;
33
34 void *baseptr = frame;
35 return baseptr + frame->data_table[type][0];
36 }
37
38 static u16 replay_frame_calculate_data_offsets(
39 u16 data_table[k_replay_framedata_rows][2] ){
40
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]);
45
46 if( total > 0xffff )
47 vg_fatal_error( "Exceeded frame storage capacity\n" );
48 }
49 return total;
50 }
51
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;
57
58 replay->tail = replay->tail->r;
59
60 if( replay->tail )
61 replay->tail->l = NULL;
62 else
63 replay->head = NULL;
64 }
65
66 static replay_frame *replay_newframe( replay_buffer *replay,
67 u16 animator_size,
68 u16 gamestate_size,
69 u16 sfx_count,
70 bool save_glider ){
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;
76 if( gamestate_size )
77 {
78 data_table[ k_replay_framedata_internal_gamestate ][1] =
79 sizeof( replay_gamestate );
80 }
81
82 data_table[ k_replay_framedata_glider ][1] = 0;
83 if( save_glider )
84 {
85 data_table[ k_replay_framedata_glider ][1] =
86 sizeof(struct replay_glider_data);
87 }
88
89 u32 nextsize = replay_frame_calculate_data_offsets( data_table );
90
91 replay_frame *frame = NULL;
92 if( replay->head )
93 {
94 u32 headsize = replay->head->total_size,
95 nextpos = ((void *)replay->head - replay->data) + headsize;
96
97 if( nextpos + nextsize > replay->size )
98 {
99 nextpos = 0;
100
101 /* maintain contiguity */
102 while( replay->tail )
103 {
104 if( (void *)replay->tail - replay->data )
105 replay_tailpop( replay );
106 else break;
107 }
108 }
109
110 check_again:;
111 u32 tailpos = (void *)replay->tail - replay->data;
112
113 if( tailpos >= nextpos )
114 {
115 if( nextpos + nextsize > tailpos )
116 {
117 replay_tailpop( replay );
118
119 if( replay->tail )
120 goto check_again;
121 }
122 }
123
124 frame = replay->data + nextpos;
125
126 if( replay->head )
127 replay->head->r = frame;
128 }
129 else
130 frame = replay->data;
131
132 for( u32 i=0; i<k_replay_framedata_rows; i++ )
133 {
134 frame->data_table[i][0] = data_table[i][0];
135 frame->data_table[i][1] = data_table[i][1];
136 }
137
138 frame->total_size = nextsize;
139 frame->l = replay->head;
140 frame->r = NULL;
141 replay->head = frame;
142 if( !replay->tail ) replay->tail = frame;
143 if( gamestate_size ) replay->statehead = frame;
144
145 return frame;
146 }
147
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);
153
154 for( u32 i=0; i<count; i ++ ){
155 net_sfx_play( buffer + i );
156 }
157 }
158
159 int replay_seek( replay_buffer *replay, f64 t )
160 {
161 if( !replay->head ) return 0;
162
163 if( t < replay->tail->time ) t = replay->tail->time;
164 if( t > replay->head->time ) t = replay->head->time;
165
166 if( !replay->cursor_frame ) {
167 replay->cursor = replay->head->time;
168 replay->cursor_frame = replay->head;
169
170 if( fabs(replay->head->time-t) > fabs(replay->tail->time-t) ){
171 replay->cursor = replay->tail->time;
172 replay->cursor_frame = replay->tail;
173 }
174 }
175
176 f64 dir = t - replay->cursor;
177 if( dir == 0.0 ) return 0;
178 dir = vg_signf( dir );
179
180
181 u32 i=4096;
182 while( i --> 0 ){
183 if( dir < 0.0 ){
184 if( t > replay->cursor_frame->time ) {
185 replay->cursor = t;
186 return 1;
187 }
188 }
189
190 replay_frame *next;
191 if( dir > 0.0 ) next = replay->cursor_frame->r;
192 else next = replay->cursor_frame->l;
193
194 if( !next ) break;
195
196 if( dir > 0.0 ){
197 if( t < next->time ){
198 replay->cursor = t;
199 return 1;
200 }
201 }
202
203 replay_emit_frame_sounds( next );
204
205 replay->cursor_frame = next;
206 replay->cursor = next->time;
207
208 if( !i ) return 1;
209 }
210
211 replay->cursor = t;
212 return 0;
213 }
214
215 replay_frame *replay_find_recent_stateframe( replay_buffer *replay )
216 {
217 replay_frame *frame = replay->cursor_frame;
218 u32 i=4096;
219 while( i --> 0 ){
220 if( !frame ) return frame;
221 if( frame->data_table[ k_replay_framedata_gamestate ][1] ) return frame;
222 frame = frame->l;
223 }
224
225 return NULL;
226 }
227
228 f32 replay_subframe_time( replay_buffer *replay )
229 {
230 replay_frame *frame = replay->cursor_frame;
231 if( !frame ) return 0.0f;
232 replay_frame *next = frame->r;
233 if( next )
234 {
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 );
238 }
239 else
240 return 0.0f;
241 }
242
243 void replay_get_frame_camera( replay_frame *frame, vg_camera *cam )
244 {
245 cam->fov = frame->cam.fov;
246 v3_copy( frame->cam.pos, cam->pos );
247 v3_copy( frame->cam.angles, cam->angles );
248 }
249
250 void replay_get_camera( replay_buffer *replay, vg_camera *cam )
251 {
252 cam->nearz = 0.1f;
253 cam->farz = 100.0f;
254 if( replay->cursor_frame )
255 {
256 replay_frame *next = replay->cursor_frame->r;
257
258 if( next )
259 {
260 vg_camera temp;
261
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 );
265 }
266 else
267 {
268 replay_get_frame_camera( replay->cursor_frame, cam );
269 }
270 }
271 else
272 {
273 v3_zero( cam->pos );
274 v3_zero( cam->angles );
275 cam->fov = 90.0f;
276 }
277 }
278
279 void skaterift_get_replay_cam( vg_camera *cam )
280 {
281 replay_buffer *replay = &player_replay.local;
282
283 if( player_replay.active_keyframe != -1 )
284 {
285 replay_keyframe *kf =
286 &player_replay.keyframes[player_replay.active_keyframe];
287
288 v3_copy( kf->cam.pos, cam->pos );
289 v3_copy( kf->cam.angles, cam->angles );
290 cam->fov = kf->cam.fov;
291 return;
292 }
293
294 if( player_replay.keyframe_count >= 2 )
295 {
296 for( u32 i=0; i<player_replay.keyframe_count-1; i ++ )
297 {
298 replay_keyframe *kf = &player_replay.keyframes[i];
299
300 if( (kf[0].time<=replay->cursor) && (kf[1].time>replay->cursor) )
301 {
302 f64 l = kf[1].time - kf[0].time,
303 t = (l <= (1.0/128.0))? 0.0: (replay->cursor-kf[0].time) / l;
304
305 if( player_replay.keyframe_count >= 3 )
306 {
307 f32 m_start = 0.5f, m_end = 0.5f;
308
309 if( i > 0 )
310 {
311 if( (t < 0.5f) || (i==player_replay.keyframe_count-2) )
312 {
313 kf --;
314 }
315 }
316
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;
320
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 );
323
324 l = te-ts;
325 t = (replay->cursor-ts)/l;
326
327 /*
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.
331 *
332 * Newton method, going through standard direct quadratic eq has
333 * precision / other problems. Also we only care about 0>t>1.
334 */
335 f32 b = (kf[1].time-ts)/l,
336 x0 = 1.0-t;
337 for( u32 i=0; i<4; i ++ )
338 {
339 f32 ix0 = 1.0f-x0,
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);
343 }
344 t = 1.0-x0;
345
346 f32 t0 = t*m_start+(1.0f-m_start),
347 t1 = t*m_end;
348
349 v3f ps, pe, as, ae;
350 f32 fs, fe;
351
352 /* first order */
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,
355 t0, as );
356 fs = vg_lerpf( kf[0].cam.fov, kf[1].cam.fov, t0 );
357
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,
360 t1, ae );
361 fe = vg_lerpf( kf[1].cam.fov, kf[2].cam.fov, t1 );
362
363 /* second order */
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 );
367 }
368 else
369 {
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,
372 t, cam->angles );
373 cam->fov = vg_lerpf( kf[0].cam.fov, kf[1].cam.fov, t );
374 }
375 return;
376 }
377 }
378 }
379
380 replay_get_camera( replay, cam );
381 }
382
383 struct replay_rb
384 {
385 v3f co, v, w;
386 v4f q;
387 };
388
389 void skaterift_record_frame( replay_buffer *replay, int force_gamestate )
390 {
391 f64 delta = 9999999.9,
392 statedelta = 9999999.9;
393
394 if( replay->head )
395 delta = vg.time - replay->head->time;
396
397 if( replay->statehead )
398 statedelta = vg.time - replay->statehead->time;
399
400 const f64 k_replay_rate = 1.0/30.0,
401 k_gamestate_rate = 0.5;
402
403 int save_frame = 0,
404 save_state = 0,
405 save_glider = 0;
406
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;
411
412 if( localplayer.have_glider || localplayer.glider_orphan ||
413 localplayer.subsystem == k_player_subsystem_glide ){
414 save_glider = 1;
415 }
416
417 if( !save_frame ) return;
418
419 u16 gamestate_size = 0;
420 if( save_state ){
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 ];
430 }
431
432 u16 animator_size = player_subsystems[localplayer.subsystem]->animator_size;
433
434 replay_frame *frame = replay_newframe( replay,
435 animator_size, gamestate_size,
436 localplayer.local_sfx_buffer_count,
437 save_glider );
438 frame->system = localplayer.subsystem;
439
440 if( save_state ){
441 replay_gamestate *gs =
442 replay_frame_data( frame, k_replay_framedata_internal_gamestate );
443
444 gs->current_run_version = world_static.current_run_version;
445 gs->drowned = localplayer.drowned;
446
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 );
453
454 void *dst = replay_frame_data( frame, k_replay_framedata_gamestate );
455
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 );
469 }
470 }
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 );
478 }
479 }
480
481 if( save_glider ){
482 struct replay_glider_data *inf =
483 replay_frame_data( frame, k_replay_framedata_glider );
484
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 );
490 }
491
492 replay->cursor = vg.time;
493 replay->cursor_frame = frame;
494 frame->time = vg.time;
495
496 /* camera */
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 );
501
502 v3f v0;
503 v3_angles_vector( localplayer.cam.angles, v0 );
504 m3x3_mulv( localplayer.gate_waiting->transport, v0, v0 );
505 v3_angles( v0, frame->cam.angles );
506 }
507 else
508 v3_copy( localplayer.cam.angles, frame->cam.angles );
509
510 frame->cam.fov = localplayer.cam.fov;
511
512 /* animator */
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 );
516
517 /* sound effects */
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 );
521
522 localplayer.local_sfx_buffer_count = 0;
523 }
524
525 static void skaterift_restore_frame( replay_frame *frame )
526 {
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;
533
534 if(frame->system == k_player_subsystem_walk ){
535 memcpy( &player_walk.state, src, src_size );
536 }
537 else if( frame->system == k_player_subsystem_skate ){
538 memcpy( &player_skate.state, src, src_size );
539 }
540 else if( frame->system == k_player_subsystem_dead ){
541 player__dead_transition(0);
542 struct replay_rb *arr = src;
543
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;
547
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 );
552
553 v3_copy( arr[i].co, part->prev_co );
554 v4_copy( arr[i].q, part->prev_q );
555 rb_update_matrices( rb );
556 }
557 }
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 );
566 }
567
568 localplayer.subsystem = frame->system;
569
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 );
574
575 localplayer.have_glider = inf->have_glider;
576 localplayer.glider_orphan = inf->glider_orphan;
577 player_glide.t = inf->t;
578 }
579 else {
580 localplayer.have_glider = 0;
581 localplayer.glider_orphan = 0;
582 player_glide.t = 0.0f;
583 }
584
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 );
588
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;
592
593 memcpy( &localplayer.cam_control, &gs->cam_control,
594 sizeof(struct player_cam_controller) );
595
596 /* chop end off replay */
597 frame->r = NULL;
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;
605 }
606
607 static void skaterift_replay_resume(void){
608 replay_frame *prev = replay_find_recent_stateframe(&player_replay.local);
609
610 if( prev ){
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;
615 }
616
617 gui_helper_clear();
618 }
619
620 static void skaterift_replay_update_helpers(void);
621
622 void skaterift_replay_pre_update(void)
623 {
624 if( skaterift.activity != k_skaterift_replay ) return;
625
626 bool input = player_replay.editor_mode^0x1;
627
628 if( player_replay.replay_control == k_replay_control_resume )
629 {
630 if( player_replay.local.cursor_frame == player_replay.resume_target ||
631 player_replay.local.cursor_frame == NULL )
632 {
633 skaterift_restore_frame( player_replay.resume_target );
634 }
635 else
636 {
637 vg_slewf( &player_replay.resume_transition, 1.0f,
638 vg.time_frame_delta * (1.0f/1.0f) );
639
640 if( player_replay.resume_transition >= 1.0f )
641 skaterift_restore_frame( player_replay.resume_target );
642 else {
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;
648 else
649 player_replay.track_velocity = 0.0f;
650 }
651 }
652 }
653 else
654 {
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 ) )
658 {
659 player_replay.use_freecam ^= 0x1;
660
661 if( player_replay.use_freecam )
662 {
663 replay_get_camera( &player_replay.local,
664 &player_replay.replay_freecam );
665 }
666 skaterift_replay_update_helpers();
667 }
668
669 f32 target_speed = 0.0f;
670 if( input )
671 target_speed = axis_state( k_sraxis_replay_h ) * 5.0;
672
673 if( input && button_press( k_srbind_reset ) )
674 target_speed += -2.0;
675
676 if( fabsf(target_speed) > 0.01f )
677 player_replay.replay_control = k_replay_control_scrub;
678
679 if( player_replay.replay_control == k_replay_control_play )
680 target_speed = 1.0;
681
682 vg_slewf( &player_replay.track_velocity, target_speed,
683 18.0f*vg.time_frame_delta );
684
685 if( fabsf( player_replay.track_velocity ) > 0.0001f )
686 {
687 f64 target = player_replay.local.cursor;
688 target += player_replay.track_velocity * vg.time_frame_delta;
689
690 if( !replay_seek( &player_replay.local, target ) )
691 player_replay.track_velocity = 0.0f;
692 }
693
694 if( input && button_down( k_srbind_mback ) )
695 {
696 if( player_replay.local.statehead )
697 skaterift_restore_frame( player_replay.local.statehead );
698 else
699 skaterift.activity = k_skaterift_default;
700 srinput.state = k_input_state_resume;
701 gui_helper_clear();
702 }
703
704 if( input )
705 {
706 if( player_replay.use_freecam )
707 {
708 freecam_preupdate();
709 }
710 else
711 {
712 if( button_down( k_srbind_replay_resume ) )
713 {
714 skaterift_replay_resume();
715 }
716 }
717 }
718 }
719 }
720
721 static void skaterift_replay_update_helpers(void)
722 {
723 player_replay.helper_resume->greyed = player_replay.use_freecam;
724
725 vg_str freecam_text;
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" );
730 }
731
732 static void replay_show_helpers(void)
733 {
734 gui_helper_clear();
735 vg_str text;
736
737 if( gui_new_helper( input_axis_list[k_sraxis_replay_h], &text ) )
738 vg_strcat( &text, "scrub" );
739
740 if( (player_replay.helper_resume = gui_new_helper(
741 input_button_list[k_srbind_replay_resume], &text )) )
742 vg_strcat( &text, "resume" );
743
744 if( gui_new_helper( input_button_list[k_srbind_replay_play], &text ))
745 vg_strcat( &text, "playback" );
746
747 player_replay.helper_freecam = gui_new_helper(
748 input_button_list[k_srbind_replay_freecam], &text );
749
750 skaterift_replay_update_helpers();
751 }
752
753 void skaterift_replay_post_render(void)
754 {
755 #ifndef SR_ALLOW_REWIND_HUB
756 if( world_static.active_instance != k_world_purpose_client )
757 return;
758 #endif
759
760 /* capture the current resume frame at the very last point */
761 if( button_down( k_srbind_reset ) )
762 {
763 if( skaterift.activity == k_skaterift_default )
764 {
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 )
769 {
770 player_replay.local.cursor = player_replay.local.head->time;
771 player_replay.local.cursor_frame = player_replay.local.head;
772 }
773 player_replay.replay_control = k_replay_control_scrub;
774 replay_show_helpers();
775 }
776 }
777 }
778
779 void skaterift_replay_init(void)
780 {
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 );
785 }
786
787 void skaterift_replay_debug_info(void)
788 {
789 player__debugtext( 2, "replay info" );
790 replay_buffer *replay = &player_replay.local;
791
792 u32 head = 0,
793 tail = 0;
794 if( replay->tail ) tail = (void *)replay->tail - replay->data;
795 if( replay->head ) head = (void *)replay->head - replay->data;
796
797 player__debugtext( 1, "head @%u | tail @%u\n", head, tail );
798
799 if( replay->statehead ){
800 for( u32 i=0; i<k_replay_framedata_rows; i++ ){
801 player__debugtext( 1, "[%u]: [%hu, %hu]\n", i,
802 replay->statehead->data_table[i][0],
803 replay->statehead->data_table[i][1] );
804 }
805 u32 state = (void *)replay->statehead - replay->data;
806 player__debugtext( 1, "gs @%u\n", state );
807 player__debugtext( 1, "gamestate_size: %hu\n",
808 replay->statehead->data_table[k_replay_framedata_gamestate][1] );
809 }
810 else
811 player__debugtext( 1, "gs @NULL\n" );
812
813 f64 start = replay->cursor,
814 end = replay->cursor;
815 if( replay->tail ) start = replay->tail->time;
816 if( replay->head ) end = replay->head->time;
817
818 f64 cur = replay->cursor - start,
819 len = end - start;
820
821 player__debugtext( 1, "cursor: %.2fs / %.2fs\n", cur, len );
822 }
823
824 static int _keyframe_cmp( const void *p1, const void *p2 )
825 {
826 const replay_keyframe *kf1 = p1, *kf2 = p2;
827 return kf1->time > kf2->time;
828 }
829
830 static void replay_keyframe_sort(void)
831 {
832 qsort( player_replay.keyframes, player_replay.keyframe_count,
833 sizeof(replay_keyframe), _keyframe_cmp );
834 }
835
836 static void replay_fly_edit_keyframe( replay_keyframe *kf )
837 {
838 if( ui_click_down( UI_MOUSE_LEFT ) )
839 {
840 /* init freecam */
841 v3_copy( kf->cam.pos, player_replay.replay_freecam.pos );
842 v3_copy( kf->cam.angles, player_replay.replay_freecam.angles );
843 v3_zero( player_replay.freecam_v );
844 v3_zero( player_replay.freecam_w );
845 player_replay.replay_freecam.fov = kf->cam.fov;
846 }
847
848 /* move freecam */
849 vg_ui.wants_mouse = 0;
850 freecam_preupdate();
851
852 if( vg_getkey(SDLK_q) )
853 player_replay.freecam_v[1] -= vg.time_frame_delta*6.0f*20.0f;
854 if( vg_getkey(SDLK_e) )
855 player_replay.freecam_v[1] += vg.time_frame_delta*6.0f*20.0f;
856
857 v3_copy( player_replay.replay_freecam.pos, skaterift.cam.pos );
858 v3_copy( player_replay.replay_freecam.angles, skaterift.cam.angles);
859 skaterift.cam.fov = player_replay.replay_freecam.fov;
860
861 v3_copy( skaterift.cam.pos, kf->cam.pos );
862 v3_copy( skaterift.cam.angles, kf->cam.angles );
863 kf->cam.fov = skaterift.cam.fov;
864 }
865
866 void skaterift_replay_imgui(void)
867 {
868 if( skaterift.activity != k_skaterift_replay ) return;
869
870 /* extra keys for entering editor */
871 static u8 f1_key = 0;
872 u8 f1_now = vg_getkey(SDLK_F1);
873 if( f1_now && !f1_key && player_replay.show_ui )
874 {
875 player_replay.editor_mode ^= 0x1;
876
877 if( player_replay.editor_mode )
878 gui_helper_clear();
879 else
880 replay_show_helpers();
881 }
882 f1_key = f1_now;
883
884 static u8 f2_key = 0;
885 u8 f2_now = vg_getkey(SDLK_F2);
886 if( f2_now && !f2_key )
887 {
888 player_replay.show_ui ^= 0x1;
889 }
890 f2_key = f2_now;
891
892 if( player_replay.editor_mode )
893 {
894 static u8 space_key = 0;
895 u8 space_now = vg_getkey(SDLK_SPACE);
896 if( space_now & !space_key )
897 {
898 player_replay.replay_control ^= k_replay_control_play;
899 }
900 space_key = space_now;
901 }
902
903 if( !player_replay.show_ui ) return;
904
905 if( player_replay.editor_mode )
906 {
907 u32 colour = ui_opacity( ui_colour(k_ui_fg), 0.3333f );
908 ui_rect cx = { vg.window_x/2, 0, 1, vg.window_y },
909 cy = { 0, vg.window_y/2, vg.window_x, 1 };
910 ui_fill( cx, colour );
911 ui_fill( cy, colour );
912 }
913
914 replay_buffer *replay = &player_replay.local;
915 f64 start = replay->cursor,
916 end = replay->cursor;
917 if( replay->tail ) start = replay->tail->time;
918 if( replay->head ) end = replay->head->time;
919 f64 len = end - start,
920 cur = (replay->cursor - start) / len;
921
922 char buffer[ 128 ];
923
924 /* mainbar */
925 ui_px height = 32,
926 cwidth = 2;
927 ui_rect timeline = { 0, 0, vg.window_x, height };
928 ui_fill( timeline, ui_colour( k_ui_bg ) );
929
930 /* cursor frame block */
931 if( replay->cursor_frame )
932 {
933 if( replay->cursor_frame->r )
934 {
935 f64 l = (replay->cursor_frame->r->time-replay->cursor_frame->time)/len,
936 s = (replay->cursor_frame->time - start) / len;
937 ui_rect box = { s*(f64)vg.window_x, 0,
938 VG_MAX(4,(ui_px)(l*vg.window_x)), timeline[3]+2 };
939 ui_fill( box, ui_colour( k_ui_bg+4 ) );
940 }
941 }
942
943 /* cursor */
944 ui_rect cusor = { cur * (f64)vg.window_x - (cwidth/2), 0,
945 cwidth, (player_replay.editor_mode? 0: 16) + timeline[3] };
946 ui_fill( cusor, ui_colour( k_ui_bg+7 ) );
947
948 /* latest state marker */
949 if( replay->statehead )
950 {
951 f64 t = (replay->statehead->time - start) / len;
952 ui_rect tag = { t*(f64)vg.window_x, 0, 2, timeline[3]+8 };
953 ui_fill( tag, ui_colour( k_ui_green+k_ui_brighter ) );
954 }
955
956 /* previous state marker */
957 replay_frame *prev = replay_find_recent_stateframe( replay );
958 if( prev )
959 {
960 f64 t = (prev->time - start) / len;
961 ui_rect tag = { t*(f64)vg.window_x, 0, 2, timeline[3]+8 };
962 ui_fill( tag, ui_colour( k_ui_yellow+k_ui_brighter ) );
963 }
964
965 snprintf( buffer, 128, "-%.2fs (F1: Edit replay)", (end-replay->cursor) );
966 ui_text( timeline, buffer, 1, k_ui_align_middle_left, 0 );
967 ui_text( timeline, "0s", 1, k_ui_align_middle_right, 0 );
968
969 if( !player_replay.editor_mode ) return;
970 vg_ui.wants_mouse = 1;
971
972 ui_rect panel = { 0, timeline[3] + 20, 200, 400 };
973 ui_fill( panel, ui_opacity( ui_colour(k_ui_bg), 0.5f ) );
974 ui_rect_pad( panel, (ui_px[2]){4,4} );
975
976 if( ui_button( panel,
977 (player_replay.replay_control == k_replay_control_play)?
978 "Pause (space)": "Play (space)" ) == k_ui_button_click )
979 {
980 player_replay.replay_control ^= k_replay_control_play;
981 }
982
983 /* script bar */
984 ui_rect script = { 0, height + 2, vg.window_x, 16 };
985 ui_fill( script, ui_colour( k_ui_bg ) );
986
987 f64 mouse_t = start + ((f64)vg_ui.mouse[0] / (f64)vg.window_x)*len;
988
989 /* keyframe draw and select */
990 bool absorb_by_keyframe = 0;
991 ui_px lx = 0;
992 for( u32 i=0; i<player_replay.keyframe_count; i ++ )
993 {
994 replay_keyframe *kf = &player_replay.keyframes[i];
995 f64 t = (kf->time-start)/len;
996
997 ui_px x = t*(f64)vg.window_x-8;
998
999 /* draw connections between keyframes */
1000 if( i )
1001 {
1002 ui_rect con = { lx, script[1]+7, x-lx, 1 };
1003 ui_fill( con, ui_colour(k_ui_blue) );
1004 vg_line( kf->cam.pos, player_replay.keyframes[i-1].cam.pos, VG__BLUE );
1005 }
1006
1007 /* keyframe selection */
1008 ui_rect tag = { x, script[1], 16, 16 };
1009
1010 if( ui_inside_rect( tag, vg_ui.mouse ) )
1011 {
1012 absorb_by_keyframe = 1;
1013
1014 if( ui_click_down( UI_MOUSE_LEFT ) )
1015 {
1016 if( player_replay.active_keyframe != i )
1017 {
1018 player_replay.active_keyframe = i;
1019 replay_seek( &player_replay.local, kf->time );
1020 }
1021 }
1022 else
1023 {
1024 ui_outline( tag, 1, ui_colour(k_ui_fg), 0 );
1025 }
1026 }
1027
1028 /* edit controls */
1029 u32 drag_colour = ui_opacity( ui_colour(k_ui_bg+2), 0.5f );
1030 if( i == player_replay.active_keyframe )
1031 {
1032 ui_outline( tag, 2, ui_colour(k_ui_fg), 0 );
1033
1034 ui_rect tray = { tag[0]+8-32, tag[1]+16+2, 64, 16 };
1035 ui_rect dragbar = { tray[0]+16, tray[1], 32, 16 };
1036
1037 bool pos_correct = 0;
1038
1039 if( ui_inside_rect( dragbar, vg_ui.mouse_click ) )
1040 {
1041 if( ui_clicking( UI_MOUSE_LEFT ) )
1042 {
1043 drag_colour = ui_opacity( ui_colour(k_ui_fg), 0.5f );
1044 pos_correct = 1;
1045 replay_seek( &player_replay.local, mouse_t );
1046 }
1047 else if( ui_click_up( UI_MOUSE_LEFT ) )
1048 {
1049 pos_correct = 1;
1050 kf->time = mouse_t;
1051 replay_keyframe_sort();
1052
1053 for( u32 j=0; j<player_replay.keyframe_count; j ++ )
1054 {
1055 if( player_replay.keyframes[j].time == mouse_t )
1056 {
1057 player_replay.active_keyframe = j;
1058 break;
1059 }
1060 }
1061 }
1062
1063 if( pos_correct )
1064 {
1065 tag[0] = vg_ui.mouse[0]-8;
1066 tray[0] = tag[0]+8-32;
1067 dragbar[0] = tray[0]+16;
1068 }
1069 }
1070
1071 if( ui_inside_rect( dragbar, vg_ui.mouse ) )
1072 {
1073 vg_ui.cursor = k_ui_cursor_hand;
1074 }
1075
1076 if( !pos_correct )
1077 ui_fill( tray, ui_opacity( ui_colour(k_ui_bg+2), 0.5f ) );
1078
1079 ui_fill( dragbar, drag_colour );
1080 ui_text( dragbar, ":::", 1, k_ui_align_middle_center, 0 );
1081
1082 if( !pos_correct )
1083 {
1084 ui_rect btn = { tray[0], tray[1], 16, 16 };
1085 if( ui_button_text( btn, "X", 1 ) == k_ui_button_click )
1086 {
1087 for( u32 j=i; j<player_replay.keyframe_count-1; j ++ )
1088 player_replay.keyframes[j] = player_replay.keyframes[j+1];
1089
1090 player_replay.keyframe_count --;
1091 player_replay.active_keyframe = -1;
1092 }
1093
1094 ui_rect btn1 = { tray[0]+48, tray[1], 16, 16 };
1095
1096 enum ui_button_state mask_using =
1097 k_ui_button_holding_inside |
1098 k_ui_button_holding_outside |
1099 k_ui_button_click;
1100
1101 if( ui_button_text( btn1, "E", 1 ) & mask_using )
1102 {
1103 replay_fly_edit_keyframe( kf );
1104 ui_set_mouse_pos( btn1[0]+8, btn1[1]+8 );
1105 }
1106 }
1107 }
1108
1109 ui_fill( tag, ui_colour(k_ui_blue) );
1110 lx = x;
1111 }
1112
1113 /* adding keyframes */
1114 if( ui_inside_rect( script, vg_ui.mouse ) )
1115 {
1116 vg_ui.cursor = k_ui_cursor_hand;
1117
1118 ui_rect cursor = { vg_ui.mouse[0], script[1], 4, 16 };
1119 ui_fill( cursor, ui_colour( k_ui_fg ) );
1120
1121 if( !absorb_by_keyframe && ui_click_down( UI_MOUSE_LEFT ) )
1122 {
1123 u32 max = vg_list_size( player_replay.keyframes );
1124 if( player_replay.keyframe_count == max )
1125 {
1126 ui_start_modal( "Maximum keyframes reached", UI_MODAL_BAD );
1127 }
1128 else
1129 {
1130 replay_keyframe *kf =
1131 &player_replay.keyframes[player_replay.keyframe_count++];
1132
1133 kf->time = mouse_t;
1134 v3_copy( skaterift.cam.pos, kf->cam.pos );
1135 v3_copy( skaterift.cam.angles, kf->cam.angles );
1136 kf->cam.fov = skaterift.cam.fov;
1137
1138 replay_keyframe_sort();
1139 }
1140 }
1141 }
1142
1143 /* timeline scrub */
1144 bool start_in_timeline =
1145 ui_clicking(UI_MOUSE_LEFT) &&
1146 ui_inside_rect(timeline, vg_ui.mouse_click);
1147 if( (ui_inside_rect( timeline, vg_ui.mouse )) || start_in_timeline )
1148 {
1149 ui_rect cursor = { vg_ui.mouse[0], timeline[1], 4, timeline[3] };
1150 ui_fill( cursor, ui_colour( k_ui_fg ) );
1151 vg_ui.cursor = k_ui_cursor_ibeam;
1152
1153 if( ui_clicking( UI_MOUSE_LEFT ) && start_in_timeline )
1154 {
1155 replay_seek( &player_replay.local, mouse_t );
1156 player_replay.active_keyframe = -1;
1157 }
1158 }
1159
1160 if( ui_button( panel, "Clear keyframes" ) == k_ui_button_click )
1161 {
1162 player_replay.keyframe_count = 0;
1163 }
1164
1165 if( (ui_button( panel, "Hide UI (F2)" ) == k_ui_button_click) )
1166 {
1167 player_replay.show_ui ^= 0x1;
1168 }
1169
1170 if( player_replay.active_keyframe != -1 )
1171 {
1172 replay_keyframe *kf =
1173 &player_replay.keyframes[ player_replay.active_keyframe ];
1174
1175 enum ui_button_state mask_using =
1176 k_ui_button_holding_inside |
1177 k_ui_button_holding_outside |
1178 k_ui_button_click;
1179
1180 if( ui_button( panel, "Edit cam" ) & mask_using )
1181 {
1182 replay_fly_edit_keyframe( kf );
1183 }
1184 }
1185
1186 ui_info( panel, "World settings" );
1187 f32 new_time = world_current_instance()->time;
1188 if( ui_slider( panel, "Time of day", 0, 1, &new_time, NULL ) )
1189 {
1190 world_current_instance()->time = new_time;
1191 }
1192
1193 ui_info( panel, "" );
1194 if( ui_button( panel, "Exit editor (F1)" ) == k_ui_button_click )
1195 {
1196 player_replay.editor_mode = 0;
1197 replay_show_helpers();
1198 }
1199
1200 /* TODO: Add Q/E scrub here too.
1201 * Add replay trimming
1202 */
1203 }