smooth replay resume
[carveJwlIkooP6JGAAIwe30JlM.git] / player_replay.c
1 #ifndef PLAYER_REPLAY_C
2 #define PLAYER_REPLAY_C
3
4 #include "player_replay.h"
5
6 VG_STATIC void replay_clear( replay_buffer *replay ){
7 replay->head = NULL;
8 replay->tail = NULL;
9 replay->cursor_frame = NULL;
10 replay->statehead = NULL;
11 replay->cursor = -99999.9;
12 }
13
14 replay_gamestate *replay_frame_gamestate( replay_frame *frame ){
15 void *baseptr = frame;
16 return baseptr + vg_align8(sizeof(replay_frame));
17 }
18
19 void *replay_gamestate_subsystem_data( replay_gamestate *gs ){
20 void *baseptr = gs;
21 return baseptr + vg_align8(sizeof(replay_gamestate));
22 }
23
24 u32 replay_frame_gamestate_total_size( u32 subsystem_gamestate_size ){
25 if( subsystem_gamestate_size ){
26 return vg_align8( sizeof(replay_gamestate) ) +
27 vg_align8( subsystem_gamestate_size );
28 }
29 else
30 return 0;
31 }
32
33 replay_sfx *replay_frame_sfx( replay_frame *frame, u32 index ){
34 void *gs = replay_frame_gamestate( frame );
35 u32 total_size =
36 replay_frame_gamestate_total_size( frame->subsystem_gamestate_size );
37
38 replay_sfx *array = (gs + total_size);
39 return &array[index];
40 }
41
42 u32 _replay_frame_size( u32 subsystem_gamestate_size, u32 sfx_count ){
43 return vg_align8( sizeof( replay_frame ) ) +
44 replay_frame_gamestate_total_size( subsystem_gamestate_size ) +
45 vg_align8( sfx_count * sizeof(replay_sfx) );
46 }
47
48 u32 replay_frame_size( replay_frame *frame ){
49 return _replay_frame_size( frame->subsystem_gamestate_size,
50 frame->sfx_count );
51 }
52
53 VG_STATIC void replay_tailpop( replay_buffer *replay ){
54 if( replay->cursor_frame == replay->tail )
55 replay->cursor_frame = NULL;
56 if( replay->statehead == replay->tail )
57 replay->statehead = NULL;
58
59 replay->tail = replay->tail->r;
60
61 if( replay->tail )
62 replay->tail->l = NULL;
63 else
64 replay->head = NULL;
65 }
66
67 VG_STATIC replay_frame *replay_newframe( replay_buffer *replay,
68 u32 subsystem_gamestate_size,
69 u32 sfx_count ){
70 replay_frame *frame = NULL;
71 if( replay->head ){
72 assert( replay->head );
73
74 u32 headsize = replay_frame_size( replay->head ),
75 nextpos = ((void *)replay->head - replay->data) + headsize,
76 nextsize = _replay_frame_size( subsystem_gamestate_size, sfx_count );
77
78 if( nextsize > replay->size ){
79 vg_error( "Keyframe too big\n" );
80 return NULL;
81 }
82
83 if( nextpos + nextsize > replay->size ){
84 nextpos = 0;
85
86 /* maintain contiguity */
87 while( replay->tail ){
88 if( (void *)replay->tail - replay->data )
89 replay_tailpop( replay );
90 else break;
91 }
92 }
93
94 check_again:;
95
96 u32 tailpos = (void *)replay->tail - replay->data;
97
98 if( tailpos >= nextpos ){
99 if( nextpos + nextsize > tailpos ){
100 replay_tailpop( replay );
101
102 if( replay->tail )
103 goto check_again;
104 }
105 }
106
107 frame = replay->data + nextpos;
108
109 if( replay->head )
110 replay->head->r = frame;
111 }
112 else
113 frame = replay->data;
114
115 frame->subsystem_gamestate_size = subsystem_gamestate_size;
116 frame->sfx_count = sfx_count;
117 frame->l = replay->head;
118 frame->r = NULL;
119 replay->head = frame;
120 if( !replay->tail ) replay->tail = frame;
121 if( subsystem_gamestate_size ) replay->statehead = frame;
122
123 return frame;
124 }
125
126 VG_STATIC void replay_seek( replay_buffer *replay, f64 t ){
127 if( !replay->head ) return;
128 assert( replay->tail );
129
130 if( t < replay->tail->time ) t = replay->tail->time;
131 if( t > replay->head->time ) t = replay->head->time;
132
133 if( !replay->cursor_frame ) {
134 replay->cursor = replay->head->time;
135 replay->cursor_frame = replay->head;
136
137 if( fabs(replay->head->time-t) > fabs(replay->tail->time-t) ){
138 replay->cursor = replay->tail->time;
139 replay->cursor_frame = replay->tail;
140 }
141 }
142
143 f64 dir = t - replay->cursor;
144 if( dir == 0.0 ) return;
145 dir = vg_signf( dir );
146
147 u32 i=4096;
148 while( i --> 0 ){
149 if( dir < 0.0 )
150 if( t > replay->cursor_frame->time ) break;
151
152 replay_frame *next;
153 if( dir > 0.0 ) next = replay->cursor_frame->r;
154 else next = replay->cursor_frame->l;
155
156 if( !next ) break;
157
158 if( dir > 0.0 )
159 if( t < next->time ) break;
160
161 replay->cursor_frame = next;
162 replay->cursor = next->time;
163
164 if( !i ) return;
165 }
166
167 replay->cursor = t;
168 }
169
170 VG_STATIC replay_frame *replay_find_recent_stateframe( replay_buffer *replay ){
171 replay_frame *frame = replay->cursor_frame;
172 u32 i=4096;
173 while( i --> 0 ){
174 if( !frame ) return frame;
175 if( frame->subsystem_gamestate_size ) return frame;
176 frame = frame->l;
177 }
178
179 return NULL;
180 }
181
182 VG_STATIC f32 replay_subframe_time( replay_buffer *replay ){
183 replay_frame *frame = replay->cursor_frame;
184 if( !frame ) return 0.0f;
185 replay_frame *next = frame->r;
186 if( next ){
187 f64 l = next->time - frame->time,
188 t = (replay->cursor - frame->time) / l;
189 return vg_clampf( t, 0.0f, 1.0f );
190 }
191 else
192 return 0.0f;
193 }
194
195 VG_STATIC void replay_get_frame_camera( replay_frame *frame, camera *cam ){
196 cam->fov = frame->cam_fov;
197 v3_copy( frame->cam_pos, cam->pos );
198 v3_copy( frame->cam_angles, cam->angles );
199 }
200
201 VG_STATIC void replay_get_camera( replay_buffer *replay, camera *cam ){
202 cam->nearz = 0.1f;
203 cam->farz = 100.0f;
204 if( replay->cursor_frame ){
205 replay_frame *next = replay->cursor_frame->r;
206
207 if( next ){
208 camera temp;
209
210 replay_get_frame_camera( replay->cursor_frame, cam );
211 replay_get_frame_camera( next, &temp );
212 camera_lerp( cam, &temp, replay_subframe_time( replay ), cam );
213 }
214 else {
215 replay_get_frame_camera( replay->cursor_frame, cam );
216 }
217 }
218 else {
219 v3_zero( cam->pos );
220 v3_zero( cam->angles );
221 cam->fov = 90.0f;
222 }
223 }
224
225 struct replay_rb{
226 v3f co, v, w;
227 v4f q;
228 };
229
230 VG_STATIC
231 void skaterift_record_frame( replay_buffer *replay, int force_gamestate ){
232 f64 delta = 9999999.9,
233 statedelta = 9999999.9;
234
235 if( replay->head )
236 delta = vg.time - replay->head->time;
237
238 if( replay->statehead )
239 statedelta = vg.time - replay->statehead->time;
240
241 const f64 k_replay_rate = 1.0/30.0,
242 k_gamestate_rate = 0.5;
243
244 int save_frame = 0,
245 save_state = 0;
246
247 if( force_gamestate ) save_state = 1;
248 if( statedelta > k_gamestate_rate ) save_state = 1;
249 if( delta > k_replay_rate ) save_frame = 1;
250 if( save_state ) save_frame = 1;
251
252 if( !save_frame ) return;
253
254 u32 gamestate_size = 0;
255
256 if( save_state ){
257 gamestate_size = (u32 []){
258 [k_player_subsystem_walk ] = sizeof(struct player_walk_state),
259 [k_player_subsystem_drive] = 0,
260 [k_player_subsystem_skate] = sizeof(struct player_skate_state),
261 [k_player_subsystem_dead ] = localplayer.ragdoll.part_count *
262 sizeof(struct replay_rb)
263 }[ localplayer.subsystem ];
264 }
265
266 replay_frame *frame = replay_newframe( replay, gamestate_size, 0 );
267
268 if( save_state ){
269 replay_gamestate *gs = replay_frame_gamestate( frame );
270 gs->system = localplayer.subsystem;
271
272 /* permanent block */
273 memcpy( &gs->rb, &localplayer.rb, sizeof(rigidbody) );
274 memcpy( &gs->cam_control, &localplayer.cam_control,
275 sizeof(struct player_cam_controller) );
276 v3_copy( localplayer.angles, gs->angles );
277
278 void *dst = replay_gamestate_subsystem_data( gs );
279
280 /* subsytem/dynamic block */
281 if( localplayer.subsystem == k_player_subsystem_walk )
282 memcpy( dst, &localplayer._walk.state, gamestate_size );
283 else if( localplayer.subsystem == k_player_subsystem_skate )
284 memcpy( dst, &localplayer._skate.state, gamestate_size );
285 else if( localplayer.subsystem == k_player_subsystem_dead ){
286 struct replay_rb *arr = dst;
287 for( u32 i=0; i<localplayer.ragdoll.part_count; i ++ ){
288 rigidbody *rb = &localplayer.ragdoll.parts[i].obj.rb;
289 v3_copy( rb->co, arr[i].co );
290 v3_copy( rb->w, arr[i].w );
291 v3_copy( rb->v, arr[i].v );
292 v4_copy( rb->q, arr[i].q );
293 }
294 }
295 }
296
297 replay->cursor = vg.time;
298 replay->cursor_frame = frame;
299
300 player_animation *res = &frame->anim;
301 v3_zero( res->root_co );
302 q_identity( res->root_q );
303 res->type = k_player_animation_type_absolute;
304
305 struct skeleton *sk = &localplayer.playeravatar->sk;
306 skeleton_decompose_mtx_absolute( sk, res->pose );
307
308 memcpy( &frame->board_pose, &localplayer.board_pose,
309 sizeof(localplayer.board_pose) );
310 frame->time = vg.time;
311 v3_copy( localplayer.cam.pos, frame->cam_pos );
312 v3_copy( localplayer.cam.angles, frame->cam_angles );
313 frame->cam_fov = localplayer.cam.fov;
314 }
315
316 VG_STATIC
317 void skaterift_restore_frame( replay_frame *frame ){
318 replay_gamestate *gs = replay_frame_gamestate( frame );
319 void *src = replay_gamestate_subsystem_data( gs );
320
321 /* TODO: Move this to subsystem bindings now that its variable */
322 if( gs->system == k_player_subsystem_walk ){
323 memcpy( &localplayer._walk.state, src,
324 frame->subsystem_gamestate_size );
325 }
326 else if( gs->system == k_player_subsystem_skate ){
327 memcpy( &localplayer._skate.state, src,
328 frame->subsystem_gamestate_size );
329 }
330 else if( gs->system == k_player_subsystem_dead ){
331 player__dead_transition( &localplayer );
332 struct replay_rb *arr = src;
333
334 for( u32 i=0; i<localplayer.ragdoll.part_count; i ++ ){
335 struct ragdoll_part *part = &localplayer.ragdoll.parts[i];
336 rigidbody *rb = &part->obj.rb;
337
338 v3_copy( arr[i].co, rb->co );
339 v3_copy( arr[i].w, rb->w );
340 v3_copy( arr[i].v, rb->v );
341 v4_copy( arr[i].q, rb->q );
342
343 v3_copy( arr[i].co, part->prev_co );
344 v4_copy( arr[i].q, part->prev_q );
345 }
346 }
347
348 localplayer.subsystem = gs->system;
349
350 memcpy( &localplayer.rb, &gs->rb, sizeof(rigidbody) );
351 v3_copy( gs->angles, localplayer.angles );
352
353 v3_copy( frame->cam_pos, localplayer.cam.pos );
354 v3_copy( frame->cam_angles, localplayer.cam.angles );
355 localplayer.cam.fov = frame->cam_fov;
356
357 memcpy( &localplayer.cam_control, &gs->cam_control,
358 sizeof(struct player_cam_controller) );
359
360 /* chop end off replay */
361 frame->r = NULL;
362 skaterift.replay.statehead = frame;
363 skaterift.replay.head = frame;
364 skaterift.replay.cursor_frame = frame;
365 skaterift.replay.cursor = frame->time;
366 skaterift.replay_control = k_replay_control_scrub;
367 skaterift.activity = k_skaterift_default;
368 vg.time = frame->time;
369 }
370
371 VG_STATIC void skaterift_replay_pre_update(void){
372 if( skaterift.activity != k_skaterift_replay ) return;
373
374 f64 speed = vg.time_frame_delta * 1.0;
375 f64 target = skaterift.replay.cursor;
376
377 if( skaterift.replay_control == k_replay_control_resume ){
378 if( skaterift.replay.cursor_frame == skaterift.resume_target ||
379 skaterift.replay.cursor_frame == NULL ){
380 skaterift_restore_frame( skaterift.resume_target );
381 }
382 else {
383 vg_slewf( &skaterift.resume_transition, 1.0f,
384 vg.time_frame_delta * (1.0f/1.0f) );
385
386 if( skaterift.resume_transition >= 1.0f )
387 skaterift_restore_frame( skaterift.resume_target );
388 else {
389 target = vg_lerp( skaterift.resume_begin,
390 skaterift.resume_target->time,
391 vg_smoothstepf( skaterift.resume_transition ) );
392 replay_seek( &skaterift.replay, target );
393 }
394 }
395 }
396 else {
397 if( vg_getkey( SDLK_9 ) ){
398 target -= speed;
399 skaterift.replay_control = k_replay_control_scrub;
400 replay_seek( &skaterift.replay, target );
401 }
402 if( vg_getkey( SDLK_0 ) ){
403 target += speed;
404 skaterift.replay_control = k_replay_control_scrub;
405 replay_seek( &skaterift.replay, target );
406 }
407
408 if( vg_getkey( SDLK_7 ) )
409 skaterift.replay_control = k_replay_control_play;
410
411 if( skaterift.replay_control == k_replay_control_play ){
412 target += vg.time_frame_delta;
413 replay_seek( &skaterift.replay, target );
414 }
415
416 if( vg_getkey( SDLK_8 ) ){
417 replay_frame *prev = replay_find_recent_stateframe(&skaterift.replay);
418
419 if( prev ){
420 skaterift.replay_control = k_replay_control_resume;
421 skaterift.resume_target = prev;
422 skaterift.resume_begin = skaterift.replay.cursor;
423 skaterift.resume_transition = 0.0f;
424 }
425 }
426 }
427 }
428
429 VG_STATIC void skaterift_replay_debug_info(void){
430 player__debugtext( 2, "replay info" );
431
432 replay_buffer *replay = &skaterift.replay;
433
434 u32 head = 0,
435 tail = 0;
436 if( replay->tail ) tail = (void *)replay->tail - replay->data;
437 if( replay->head ) head = (void *)replay->head - replay->data;
438
439 player__debugtext( 1, "head @%u | tail @%u\n", head, tail );
440
441 if( replay->statehead ){
442 u32 state = (void *)replay->statehead - replay->data;
443 player__debugtext( 1, "gs @%u\n", state );
444 player__debugtext( 1, "gamestate_size: %u\n",
445 replay->statehead->subsystem_gamestate_size );
446 }
447 else
448 player__debugtext( 1, "gs @NULL\n" );
449
450 f64 start = replay->cursor,
451 end = replay->cursor;
452 if( replay->tail ) start = replay->tail->time;
453 if( replay->head ) end = replay->head->time;
454
455 f64 cur = replay->cursor - start,
456 len = end - start;
457
458 player__debugtext( 1, "cursor: %.2fs / %.2fs\n", cur, len );
459 }
460
461 VG_STATIC void skaterift_replay_imgui(void){
462 if( skaterift.activity != k_skaterift_replay ) return;
463
464 replay_buffer *replay = &skaterift.replay;
465 f64 start = replay->cursor,
466 end = replay->cursor;
467 if( replay->tail ) start = replay->tail->time;
468 if( replay->head ) end = replay->head->time;
469 f64 len = end - start,
470 cur = (replay->cursor - start) / len;
471
472 char buffer[ 128 ];
473
474 /* mainbar */
475 ui_px height = 20,
476 cwidth = 2;
477 ui_rect bar = { 0, vg.window_y - height, vg.window_x, height };
478 ui_fill( bar, ui_colour( k_ui_bg ) );
479
480 /* cursor frame block */
481 if( replay->cursor_frame ){
482 if( replay->cursor_frame->r ){
483 f64 l = (replay->cursor_frame->r->time-replay->cursor_frame->time)/len,
484 s = (replay->cursor_frame->time - start) / len;
485 ui_rect box = { s*(f64)vg.window_x, bar[1]-2,
486 VG_MAX(4,(ui_px)(l*vg.window_x)), bar[3]+2 };
487 ui_fill( box, ui_colour( k_ui_bg+4 ) );
488 }
489 }
490
491 /* cursor */
492 ui_rect cusor = { cur * (f64)vg.window_x - (cwidth/2), bar[1],
493 cwidth, bar[3] };
494 ui_fill( cusor, ui_colour( k_ui_bg+7 ) );
495
496 /* latest state marker */
497 if( replay->statehead ){
498 f64 t = (replay->statehead->time - start) / len;
499 ui_rect tag = { t*(f64)vg.window_x, bar[1]-8, 2, bar[3]+8 };
500 ui_fill( tag, ui_colour( k_ui_green+k_ui_brighter ) );
501 }
502
503 /* previous state marker */
504 replay_frame *prev = replay_find_recent_stateframe( replay );
505 if( prev ){
506 f64 t = (prev->time - start) / len;
507 ui_rect tag = { t*(f64)vg.window_x, bar[1]-8, 2, bar[3]+8 };
508 ui_fill( tag, ui_colour( k_ui_yellow+k_ui_brighter ) );
509 }
510
511 cusor[1] -= height;
512 cusor[2] = 200;
513 snprintf( buffer, 128, "-%.2fs\n", (end-replay->cursor) );
514 ui_text( cusor, buffer, 1, k_ui_align_middle_left, 0 );
515
516 snprintf( buffer, 128, "-%.2fs\n", len );
517 ui_text( bar, buffer, 1, k_ui_align_middle_left, 0 );
518 ui_text( bar, "0s", 1, k_ui_align_middle_right, 0 );
519 }
520
521 #endif /* PLAYER_REPLAY_C */