option between water plane / water trigger
[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
446 /* permanent block */
447 memcpy( &gs->rb, &localplayer.rb, sizeof(rigidbody) );
448 memcpy( &gs->glider_rb, &player_glide.rb, sizeof(rigidbody) );
449 memcpy( &gs->cam_control, &localplayer.cam_control,
450 sizeof(struct player_cam_controller) );
451 v3_copy( localplayer.angles, gs->angles );
452
453 void *dst = replay_frame_data( frame, k_replay_framedata_gamestate );
454
455 /* subsytem/dynamic block */
456 if( localplayer.subsystem == k_player_subsystem_walk )
457 memcpy( dst, &player_walk.state, gamestate_size );
458 else if( localplayer.subsystem == k_player_subsystem_skate )
459 memcpy( dst, &player_skate.state, gamestate_size );
460 else if( localplayer.subsystem == k_player_subsystem_dead ){
461 struct replay_rb *arr = dst;
462 for( u32 i=0; i<localplayer.ragdoll.part_count; i ++ ){
463 rigidbody *rb = &localplayer.ragdoll.parts[i].rb;
464 v3_copy( rb->co, arr[i].co );
465 v3_copy( rb->w, arr[i].w );
466 v3_copy( rb->v, arr[i].v );
467 v4_copy( rb->q, arr[i].q );
468 }
469 }
470 else if( localplayer.subsystem == k_player_subsystem_glide ){
471 struct replay_rb *arr = dst;
472 rigidbody *rb = &player_glide.rb;
473 v3_copy( rb->co, arr[0].co );
474 v3_copy( rb->w, arr[0].w );
475 v3_copy( rb->v, arr[0].v );
476 v4_copy( rb->q, arr[0].q );
477 }
478 }
479
480 if( save_glider ){
481 struct replay_glider_data *inf =
482 replay_frame_data( frame, k_replay_framedata_glider );
483
484 inf->have_glider = localplayer.have_glider;
485 inf->glider_orphan = localplayer.glider_orphan;
486 inf->t = player_glide.t;
487 v3_copy( player_glide.rb.co, inf->co );
488 v4_copy( player_glide.rb.q, inf->q );
489 }
490
491 replay->cursor = vg.time;
492 replay->cursor_frame = frame;
493 frame->time = vg.time;
494
495 /* camera */
496 v3_copy( localplayer.cam.pos, frame->cam.pos );
497 if( localplayer.gate_waiting ){
498 m4x3_mulv( localplayer.gate_waiting->transport,
499 frame->cam.pos, frame->cam.pos );
500
501 v3f v0;
502 v3_angles_vector( localplayer.cam.angles, v0 );
503 m3x3_mulv( localplayer.gate_waiting->transport, v0, v0 );
504 v3_angles( v0, frame->cam.angles );
505 }
506 else
507 v3_copy( localplayer.cam.angles, frame->cam.angles );
508
509 frame->cam.fov = localplayer.cam.fov;
510
511 /* animator */
512 void *dst = replay_frame_data( frame, k_replay_framedata_animator ),
513 *src = player_subsystems[localplayer.subsystem]->animator_data;
514 memcpy( dst, src, animator_size );
515
516 /* sound effects */
517 memcpy( replay_frame_data( frame, k_replay_framedata_sfx ),
518 localplayer.local_sfx_buffer,
519 sizeof(struct net_sfx)*localplayer.local_sfx_buffer_count );
520
521 localplayer.local_sfx_buffer_count = 0;
522 }
523
524 static
525 void skaterift_restore_frame( replay_frame *frame ){
526 replay_gamestate *gs =
527 replay_frame_data( frame, k_replay_framedata_internal_gamestate );
528 void *src = replay_frame_data( frame, k_replay_framedata_gamestate );
529 u16 src_size = frame->data_table[ k_replay_framedata_gamestate ][1];
530 world_static.current_run_version = gs->current_run_version;
531
532 if(frame->system == k_player_subsystem_walk ){
533 memcpy( &player_walk.state, src, src_size );
534 }
535 else if( frame->system == k_player_subsystem_skate ){
536 memcpy( &player_skate.state, src, src_size );
537 }
538 else if( frame->system == k_player_subsystem_dead ){
539 player__dead_transition(0);
540 struct replay_rb *arr = src;
541
542 for( u32 i=0; i<localplayer.ragdoll.part_count; i ++ ){
543 struct ragdoll_part *part = &localplayer.ragdoll.parts[i];
544 rigidbody *rb = &part->rb;
545
546 v3_copy( arr[i].co, rb->co );
547 v3_copy( arr[i].w, rb->w );
548 v3_copy( arr[i].v, rb->v );
549 v4_copy( arr[i].q, rb->q );
550
551 v3_copy( arr[i].co, part->prev_co );
552 v4_copy( arr[i].q, part->prev_q );
553 rb_update_matrices( rb );
554 }
555 }
556 else if( frame->system == k_player_subsystem_glide ){
557 struct replay_rb *arr = src;
558 rigidbody *rb = &player_glide.rb;
559 v3_copy( arr[0].co, rb->co );
560 v3_copy( arr[0].w, rb->w );
561 v3_copy( arr[0].v, rb->v );
562 v4_copy( arr[0].q, rb->q );
563 rb_update_matrices( rb );
564 }
565
566 localplayer.subsystem = frame->system;
567
568 /* restore the seperated glider data if we have it */
569 if( frame->data_table[ k_replay_framedata_glider ][1] ){
570 struct replay_glider_data *inf =
571 replay_frame_data( frame, k_replay_framedata_glider );
572
573 localplayer.have_glider = inf->have_glider;
574 localplayer.glider_orphan = inf->glider_orphan;
575 player_glide.t = inf->t;
576 }
577 else {
578 localplayer.have_glider = 0;
579 localplayer.glider_orphan = 0;
580 player_glide.t = 0.0f;
581 }
582
583 memcpy( &localplayer.rb, &gs->rb, sizeof(rigidbody) );
584 memcpy( &player_glide.rb, &gs->glider_rb, sizeof(rigidbody) );
585 v3_copy( gs->angles, localplayer.angles );
586
587 v3_copy( frame->cam.pos, localplayer.cam.pos );
588 v3_copy( frame->cam.angles, localplayer.cam.angles );
589 localplayer.cam.fov = frame->cam.fov;
590
591 memcpy( &localplayer.cam_control, &gs->cam_control,
592 sizeof(struct player_cam_controller) );
593
594 /* chop end off replay */
595 frame->r = NULL;
596 player_replay.local.statehead = frame;
597 player_replay.local.head = frame;
598 player_replay.local.cursor_frame = frame;
599 player_replay.local.cursor = frame->time;
600 player_replay.replay_control = k_replay_control_scrub;
601 skaterift.activity = k_skaterift_default;
602 vg.time = frame->time;
603 }
604
605 static void skaterift_replay_resume(void){
606 replay_frame *prev = replay_find_recent_stateframe(&player_replay.local);
607
608 if( prev ){
609 player_replay.replay_control = k_replay_control_resume;
610 player_replay.resume_target = prev;
611 player_replay.resume_begin = player_replay.local.cursor;
612 player_replay.resume_transition = 0.0f;
613 }
614
615 gui_helper_clear();
616 }
617
618 static void skaterift_replay_update_helpers(void);
619
620 void skaterift_replay_pre_update(void)
621 {
622 if( skaterift.activity != k_skaterift_replay ) return;
623
624 bool input = player_replay.editor_mode^0x1;
625
626 if( player_replay.replay_control == k_replay_control_resume )
627 {
628 if( player_replay.local.cursor_frame == player_replay.resume_target ||
629 player_replay.local.cursor_frame == NULL )
630 {
631 skaterift_restore_frame( player_replay.resume_target );
632 }
633 else
634 {
635 vg_slewf( &player_replay.resume_transition, 1.0f,
636 vg.time_frame_delta * (1.0f/1.0f) );
637
638 if( player_replay.resume_transition >= 1.0f )
639 skaterift_restore_frame( player_replay.resume_target );
640 else {
641 f64 target = vg_lerp( player_replay.resume_begin,
642 player_replay.resume_target->time,
643 vg_smoothstepf( player_replay.resume_transition ) );
644 if( replay_seek( &player_replay.local, target ) )
645 player_replay.track_velocity = 1.0f;
646 else
647 player_replay.track_velocity = 0.0f;
648 }
649 }
650 }
651 else
652 {
653 if( input && button_down( k_srbind_replay_play ) )
654 player_replay.replay_control = k_replay_control_play;
655 if( input && button_down( k_srbind_replay_freecam ) )
656 {
657 player_replay.use_freecam ^= 0x1;
658
659 if( player_replay.use_freecam )
660 {
661 replay_get_camera( &player_replay.local,
662 &player_replay.replay_freecam );
663 }
664 skaterift_replay_update_helpers();
665 }
666
667 f32 target_speed = 0.0f;
668 if( input )
669 target_speed = axis_state( k_sraxis_replay_h ) * 5.0;
670
671 if( input && button_press( k_srbind_reset ) )
672 target_speed += -2.0;
673
674 if( fabsf(target_speed) > 0.01f )
675 player_replay.replay_control = k_replay_control_scrub;
676
677 if( player_replay.replay_control == k_replay_control_play )
678 target_speed = 1.0;
679
680 vg_slewf( &player_replay.track_velocity, target_speed,
681 18.0f*vg.time_frame_delta );
682
683 if( fabsf( player_replay.track_velocity ) > 0.0001f )
684 {
685 f64 target = player_replay.local.cursor;
686 target += player_replay.track_velocity * vg.time_frame_delta;
687
688 if( !replay_seek( &player_replay.local, target ) )
689 player_replay.track_velocity = 0.0f;
690 }
691
692 if( input && button_down( k_srbind_mback ) )
693 {
694 if( player_replay.local.statehead )
695 skaterift_restore_frame( player_replay.local.statehead );
696 else
697 skaterift.activity = k_skaterift_default;
698 srinput.state = k_input_state_resume;
699 gui_helper_clear();
700 }
701
702 if( input )
703 {
704 if( player_replay.use_freecam )
705 {
706 freecam_preupdate();
707 }
708 else
709 {
710 if( button_down( k_srbind_replay_resume ) )
711 {
712 skaterift_replay_resume();
713 }
714 }
715 }
716 }
717 }
718
719 static void skaterift_replay_update_helpers(void)
720 {
721 player_replay.helper_resume->greyed = player_replay.use_freecam;
722
723 vg_str freecam_text;
724 vg_strnull( &freecam_text, player_replay.helper_freecam->text,
725 GUI_HELPER_TEXT_LENGTH );
726 vg_strcat( &freecam_text,
727 player_replay.use_freecam? "exit freecam": "freecam" );
728 }
729
730 static void replay_show_helpers(void)
731 {
732 gui_helper_clear();
733 vg_str text;
734
735 if( gui_new_helper( input_axis_list[k_sraxis_replay_h], &text ) )
736 vg_strcat( &text, "scrub" );
737
738 if( (player_replay.helper_resume = gui_new_helper(
739 input_button_list[k_srbind_replay_resume], &text )) )
740 vg_strcat( &text, "resume" );
741
742 if( gui_new_helper( input_button_list[k_srbind_replay_play], &text ))
743 vg_strcat( &text, "playback" );
744
745 player_replay.helper_freecam = gui_new_helper(
746 input_button_list[k_srbind_replay_freecam], &text );
747
748 skaterift_replay_update_helpers();
749 }
750
751 void skaterift_replay_post_render(void)
752 {
753 #ifndef SR_ALLOW_REWIND_HUB
754 if( world_static.active_instance != k_world_purpose_client )
755 return;
756 #endif
757
758 /* capture the current resume frame at the very last point */
759 if( button_down( k_srbind_reset ) )
760 {
761 if( skaterift.activity == k_skaterift_default )
762 {
763 localplayer.rewinded_since_last_gate = 1;
764 skaterift.activity = k_skaterift_replay;
765 skaterift_record_frame( &player_replay.local, 1 );
766 if( player_replay.local.head )
767 {
768 player_replay.local.cursor = player_replay.local.head->time;
769 player_replay.local.cursor_frame = player_replay.local.head;
770 }
771 player_replay.replay_control = k_replay_control_scrub;
772 replay_show_helpers();
773 }
774 }
775 }
776
777 void skaterift_replay_init(void)
778 {
779 u32 bytes = 1024*1024*10;
780 player_replay.local.data = vg_linear_alloc( vg_mem.rtmemory, bytes );
781 player_replay.local.size = bytes;
782 replay_clear( &player_replay.local );
783 }
784
785 void skaterift_replay_debug_info(void)
786 {
787 player__debugtext( 2, "replay info" );
788 replay_buffer *replay = &player_replay.local;
789
790 u32 head = 0,
791 tail = 0;
792 if( replay->tail ) tail = (void *)replay->tail - replay->data;
793 if( replay->head ) head = (void *)replay->head - replay->data;
794
795 player__debugtext( 1, "head @%u | tail @%u\n", head, tail );
796
797 if( replay->statehead ){
798 for( u32 i=0; i<k_replay_framedata_rows; i++ ){
799 player__debugtext( 1, "[%u]: [%hu, %hu]\n", i,
800 replay->statehead->data_table[i][0],
801 replay->statehead->data_table[i][1] );
802 }
803 u32 state = (void *)replay->statehead - replay->data;
804 player__debugtext( 1, "gs @%u\n", state );
805 player__debugtext( 1, "gamestate_size: %hu\n",
806 replay->statehead->data_table[k_replay_framedata_gamestate][1] );
807 }
808 else
809 player__debugtext( 1, "gs @NULL\n" );
810
811 f64 start = replay->cursor,
812 end = replay->cursor;
813 if( replay->tail ) start = replay->tail->time;
814 if( replay->head ) end = replay->head->time;
815
816 f64 cur = replay->cursor - start,
817 len = end - start;
818
819 player__debugtext( 1, "cursor: %.2fs / %.2fs\n", cur, len );
820 }
821
822 static int _keyframe_cmp( const void *p1, const void *p2 )
823 {
824 const replay_keyframe *kf1 = p1, *kf2 = p2;
825 return kf1->time > kf2->time;
826 }
827
828 static void replay_keyframe_sort(void)
829 {
830 qsort( player_replay.keyframes, player_replay.keyframe_count,
831 sizeof(replay_keyframe), _keyframe_cmp );
832 }
833
834 static void replay_fly_edit_keyframe( replay_keyframe *kf )
835 {
836 if( ui_click_down( UI_MOUSE_LEFT ) )
837 {
838 /* init freecam */
839 v3_copy( kf->cam.pos, player_replay.replay_freecam.pos );
840 v3_copy( kf->cam.angles, player_replay.replay_freecam.angles );
841 v3_zero( player_replay.freecam_v );
842 v3_zero( player_replay.freecam_w );
843 player_replay.replay_freecam.fov = kf->cam.fov;
844 }
845
846 /* move freecam */
847 vg_ui.wants_mouse = 0;
848 freecam_preupdate();
849
850 if( vg_getkey(SDLK_q) )
851 player_replay.freecam_v[1] -= vg.time_frame_delta*6.0f*20.0f;
852 if( vg_getkey(SDLK_e) )
853 player_replay.freecam_v[1] += vg.time_frame_delta*6.0f*20.0f;
854
855 v3_copy( player_replay.replay_freecam.pos, skaterift.cam.pos );
856 v3_copy( player_replay.replay_freecam.angles, skaterift.cam.angles);
857 skaterift.cam.fov = player_replay.replay_freecam.fov;
858
859 v3_copy( skaterift.cam.pos, kf->cam.pos );
860 v3_copy( skaterift.cam.angles, kf->cam.angles );
861 kf->cam.fov = skaterift.cam.fov;
862 }
863
864 void skaterift_replay_imgui(void)
865 {
866 if( skaterift.activity != k_skaterift_replay ) return;
867
868 /* extra keys for entering editor */
869 static u8 f1_key = 0;
870 u8 f1_now = vg_getkey(SDLK_F1);
871 if( f1_now && !f1_key && player_replay.show_ui )
872 {
873 player_replay.editor_mode ^= 0x1;
874
875 if( player_replay.editor_mode )
876 gui_helper_clear();
877 else
878 replay_show_helpers();
879 }
880 f1_key = f1_now;
881
882 static u8 f2_key = 0;
883 u8 f2_now = vg_getkey(SDLK_F2);
884 if( f2_now && !f2_key )
885 {
886 player_replay.show_ui ^= 0x1;
887 }
888 f2_key = f2_now;
889
890 if( player_replay.editor_mode )
891 {
892 static u8 space_key = 0;
893 u8 space_now = vg_getkey(SDLK_SPACE);
894 if( space_now & !space_key )
895 {
896 player_replay.replay_control ^= k_replay_control_play;
897 }
898 space_key = space_now;
899 }
900
901 if( !player_replay.show_ui ) return;
902
903 if( player_replay.editor_mode )
904 {
905 u32 colour = ui_opacity( ui_colour(k_ui_fg), 0.3333f );
906 ui_rect cx = { vg.window_x/2, 0, 1, vg.window_y },
907 cy = { 0, vg.window_y/2, vg.window_x, 1 };
908 ui_fill( cx, colour );
909 ui_fill( cy, colour );
910 }
911
912 replay_buffer *replay = &player_replay.local;
913 f64 start = replay->cursor,
914 end = replay->cursor;
915 if( replay->tail ) start = replay->tail->time;
916 if( replay->head ) end = replay->head->time;
917 f64 len = end - start,
918 cur = (replay->cursor - start) / len;
919
920 char buffer[ 128 ];
921
922 /* mainbar */
923 ui_px height = 32,
924 cwidth = 2;
925 ui_rect timeline = { 0, 0, vg.window_x, height };
926 ui_fill( timeline, ui_colour( k_ui_bg ) );
927
928 /* cursor frame block */
929 if( replay->cursor_frame )
930 {
931 if( replay->cursor_frame->r )
932 {
933 f64 l = (replay->cursor_frame->r->time-replay->cursor_frame->time)/len,
934 s = (replay->cursor_frame->time - start) / len;
935 ui_rect box = { s*(f64)vg.window_x, 0,
936 VG_MAX(4,(ui_px)(l*vg.window_x)), timeline[3]+2 };
937 ui_fill( box, ui_colour( k_ui_bg+4 ) );
938 }
939 }
940
941 /* cursor */
942 ui_rect cusor = { cur * (f64)vg.window_x - (cwidth/2), 0,
943 cwidth, (player_replay.editor_mode? 0: 16) + timeline[3] };
944 ui_fill( cusor, ui_colour( k_ui_bg+7 ) );
945
946 /* latest state marker */
947 if( replay->statehead )
948 {
949 f64 t = (replay->statehead->time - start) / len;
950 ui_rect tag = { t*(f64)vg.window_x, 0, 2, timeline[3]+8 };
951 ui_fill( tag, ui_colour( k_ui_green+k_ui_brighter ) );
952 }
953
954 /* previous state marker */
955 replay_frame *prev = replay_find_recent_stateframe( replay );
956 if( prev )
957 {
958 f64 t = (prev->time - start) / len;
959 ui_rect tag = { t*(f64)vg.window_x, 0, 2, timeline[3]+8 };
960 ui_fill( tag, ui_colour( k_ui_yellow+k_ui_brighter ) );
961 }
962
963 snprintf( buffer, 128, "-%.2fs (F1: Edit replay)", (end-replay->cursor) );
964 ui_text( timeline, buffer, 1, k_ui_align_middle_left, 0 );
965 ui_text( timeline, "0s", 1, k_ui_align_middle_right, 0 );
966
967 if( !player_replay.editor_mode ) return;
968 vg_ui.wants_mouse = 1;
969
970 ui_rect panel = { 0, timeline[3] + 20, 200, 400 };
971 ui_fill( panel, ui_opacity( ui_colour(k_ui_bg), 0.5f ) );
972 ui_rect_pad( panel, (ui_px[2]){4,4} );
973
974 if( ui_button( panel,
975 (player_replay.replay_control == k_replay_control_play)?
976 "Pause (space)": "Play (space)" ) == k_ui_button_click )
977 {
978 player_replay.replay_control ^= k_replay_control_play;
979 }
980
981 /* script bar */
982 ui_rect script = { 0, height + 2, vg.window_x, 16 };
983 ui_fill( script, ui_colour( k_ui_bg ) );
984
985 f64 mouse_t = start + ((f64)vg_ui.mouse[0] / (f64)vg.window_x)*len;
986
987 /* keyframe draw and select */
988 bool absorb_by_keyframe = 0;
989 ui_px lx = 0;
990 for( u32 i=0; i<player_replay.keyframe_count; i ++ )
991 {
992 replay_keyframe *kf = &player_replay.keyframes[i];
993 f64 t = (kf->time-start)/len;
994
995 ui_px x = t*(f64)vg.window_x-8;
996
997 /* draw connections between keyframes */
998 if( i )
999 {
1000 ui_rect con = { lx, script[1]+7, x-lx, 1 };
1001 ui_fill( con, ui_colour(k_ui_blue) );
1002 vg_line( kf->cam.pos, player_replay.keyframes[i-1].cam.pos, VG__BLUE );
1003 }
1004
1005 /* keyframe selection */
1006 ui_rect tag = { x, script[1], 16, 16 };
1007
1008 if( ui_inside_rect( tag, vg_ui.mouse ) )
1009 {
1010 absorb_by_keyframe = 1;
1011
1012 if( ui_click_down( UI_MOUSE_LEFT ) )
1013 {
1014 if( player_replay.active_keyframe != i )
1015 {
1016 player_replay.active_keyframe = i;
1017 replay_seek( &player_replay.local, kf->time );
1018 }
1019 }
1020 else
1021 {
1022 ui_outline( tag, 1, ui_colour(k_ui_fg), 0 );
1023 }
1024 }
1025
1026 /* edit controls */
1027 u32 drag_colour = ui_opacity( ui_colour(k_ui_bg+2), 0.5f );
1028 if( i == player_replay.active_keyframe )
1029 {
1030 ui_outline( tag, 2, ui_colour(k_ui_fg), 0 );
1031
1032 ui_rect tray = { tag[0]+8-32, tag[1]+16+2, 64, 16 };
1033 ui_rect dragbar = { tray[0]+16, tray[1], 32, 16 };
1034
1035 bool pos_correct = 0;
1036
1037 if( ui_inside_rect( dragbar, vg_ui.mouse_click ) )
1038 {
1039 if( ui_clicking( UI_MOUSE_LEFT ) )
1040 {
1041 drag_colour = ui_opacity( ui_colour(k_ui_fg), 0.5f );
1042 pos_correct = 1;
1043 replay_seek( &player_replay.local, mouse_t );
1044 }
1045 else if( ui_click_up( UI_MOUSE_LEFT ) )
1046 {
1047 pos_correct = 1;
1048 kf->time = mouse_t;
1049 replay_keyframe_sort();
1050
1051 for( u32 j=0; j<player_replay.keyframe_count; j ++ )
1052 {
1053 if( player_replay.keyframes[j].time == mouse_t )
1054 {
1055 player_replay.active_keyframe = j;
1056 break;
1057 }
1058 }
1059 }
1060
1061 if( pos_correct )
1062 {
1063 tag[0] = vg_ui.mouse[0]-8;
1064 tray[0] = tag[0]+8-32;
1065 dragbar[0] = tray[0]+16;
1066 }
1067 }
1068
1069 if( ui_inside_rect( dragbar, vg_ui.mouse ) )
1070 {
1071 vg_ui.cursor = k_ui_cursor_hand;
1072 }
1073
1074 if( !pos_correct )
1075 ui_fill( tray, ui_opacity( ui_colour(k_ui_bg+2), 0.5f ) );
1076
1077 ui_fill( dragbar, drag_colour );
1078 ui_text( dragbar, ":::", 1, k_ui_align_middle_center, 0 );
1079
1080 if( !pos_correct )
1081 {
1082 ui_rect btn = { tray[0], tray[1], 16, 16 };
1083 if( ui_button_text( btn, "X", 1 ) == k_ui_button_click )
1084 {
1085 for( u32 j=i; j<player_replay.keyframe_count-1; j ++ )
1086 player_replay.keyframes[j] = player_replay.keyframes[j+1];
1087
1088 player_replay.keyframe_count --;
1089 player_replay.active_keyframe = -1;
1090 }
1091
1092 ui_rect btn1 = { tray[0]+48, tray[1], 16, 16 };
1093
1094 enum ui_button_state mask_using =
1095 k_ui_button_holding_inside |
1096 k_ui_button_holding_outside |
1097 k_ui_button_click;
1098
1099 if( ui_button_text( btn1, "E", 1 ) & mask_using )
1100 {
1101 replay_fly_edit_keyframe( kf );
1102 ui_set_mouse_pos( btn1[0]+8, btn1[1]+8 );
1103 }
1104 }
1105 }
1106
1107 ui_fill( tag, ui_colour(k_ui_blue) );
1108 lx = x;
1109 }
1110
1111 /* adding keyframes */
1112 if( ui_inside_rect( script, vg_ui.mouse ) )
1113 {
1114 vg_ui.cursor = k_ui_cursor_hand;
1115
1116 ui_rect cursor = { vg_ui.mouse[0], script[1], 4, 16 };
1117 ui_fill( cursor, ui_colour( k_ui_fg ) );
1118
1119 if( !absorb_by_keyframe && ui_click_down( UI_MOUSE_LEFT ) )
1120 {
1121 u32 max = vg_list_size( player_replay.keyframes );
1122 if( player_replay.keyframe_count == max )
1123 {
1124 ui_start_modal( "Maximum keyframes reached", UI_MODAL_BAD );
1125 }
1126 else
1127 {
1128 replay_keyframe *kf =
1129 &player_replay.keyframes[player_replay.keyframe_count++];
1130
1131 kf->time = mouse_t;
1132 v3_copy( skaterift.cam.pos, kf->cam.pos );
1133 v3_copy( skaterift.cam.angles, kf->cam.angles );
1134 kf->cam.fov = skaterift.cam.fov;
1135
1136 replay_keyframe_sort();
1137 }
1138 }
1139 }
1140
1141 /* timeline scrub */
1142 bool start_in_timeline =
1143 ui_clicking(UI_MOUSE_LEFT) &&
1144 ui_inside_rect(timeline, vg_ui.mouse_click);
1145 if( (ui_inside_rect( timeline, vg_ui.mouse )) || start_in_timeline )
1146 {
1147 ui_rect cursor = { vg_ui.mouse[0], timeline[1], 4, timeline[3] };
1148 ui_fill( cursor, ui_colour( k_ui_fg ) );
1149 vg_ui.cursor = k_ui_cursor_ibeam;
1150
1151 if( ui_clicking( UI_MOUSE_LEFT ) && start_in_timeline )
1152 {
1153 replay_seek( &player_replay.local, mouse_t );
1154 player_replay.active_keyframe = -1;
1155 }
1156 }
1157
1158 if( ui_button( panel, "Clear keyframes" ) == k_ui_button_click )
1159 {
1160 player_replay.keyframe_count = 0;
1161 }
1162
1163 if( (ui_button( panel, "Hide UI (F2)" ) == k_ui_button_click) )
1164 {
1165 player_replay.show_ui ^= 0x1;
1166 }
1167
1168 if( player_replay.active_keyframe != -1 )
1169 {
1170 replay_keyframe *kf =
1171 &player_replay.keyframes[ player_replay.active_keyframe ];
1172
1173 enum ui_button_state mask_using =
1174 k_ui_button_holding_inside |
1175 k_ui_button_holding_outside |
1176 k_ui_button_click;
1177
1178 if( ui_button( panel, "Edit cam" ) & mask_using )
1179 {
1180 replay_fly_edit_keyframe( kf );
1181 }
1182 }
1183
1184 ui_info( panel, "World settings" );
1185 f32 new_time = world_current_instance()->time;
1186 if( ui_slider( panel, "Time of day", 0, 1, &new_time, NULL ) )
1187 {
1188 world_current_instance()->time = new_time;
1189 }
1190
1191 ui_info( panel, "" );
1192 if( ui_button( panel, "Exit editor (F1)" ) == k_ui_button_click )
1193 {
1194 player_replay.editor_mode = 0;
1195 replay_show_helpers();
1196 }
1197
1198 /* TODO: Add Q/E scrub here too.
1199 * Add replay trimming
1200 */
1201 }