switch to dynamic sizes
[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 VG_STATIC void skaterift_replay_pre_update(void){
226 if( skaterift.activity != k_skaterift_replay ) return;
227
228 f64 speed = 1.0;
229 f64 target = skaterift.replay.cursor;
230
231 if( vg_getkey( SDLK_9 ) ){
232 target -= vg.time_frame_delta * speed;
233 skaterift.replay_control = k_replay_control_scrub;
234 replay_seek( &skaterift.replay, target );
235 }
236 if( vg_getkey( SDLK_0 ) ){
237 target += vg.time_frame_delta * speed;
238 skaterift.replay_control = k_replay_control_scrub;
239 replay_seek( &skaterift.replay, target );
240 }
241
242 if( vg_getkey( SDLK_7 ) )
243 skaterift.replay_control = k_replay_control_play;
244
245 if( skaterift.replay_control == k_replay_control_play ){
246 target += vg.time_frame_delta;
247 replay_seek( &skaterift.replay, target );
248 }
249
250 if( vg_getkey( SDLK_8 ) ){
251 replay_frame *prev = replay_find_recent_stateframe( &skaterift.replay );
252
253 if( prev ){
254 /* TODO: Make gamestate_apply function / swap ... */
255 replay_gamestate *gs = replay_frame_gamestate( prev );
256 void *src = replay_gamestate_subsystem_data( gs );
257
258 if( gs->system == k_player_subsystem_walk ){
259 memcpy( &localplayer._walk.state, src,
260 prev->subsystem_gamestate_size );
261 }
262 else if( gs->system == k_player_subsystem_skate ){
263 memcpy( &localplayer._skate.state, src,
264 prev->subsystem_gamestate_size );
265 }
266 else if( gs->system == k_player_subsystem_dead ){
267 player__dead_transition( &localplayer );
268 memcpy( &localplayer.ragdoll, src,
269 prev->subsystem_gamestate_size );
270 }
271
272 localplayer.subsystem = gs->system;
273
274 memcpy( &localplayer.rb, &gs->rb, sizeof(rigidbody) );
275 v3_copy( gs->angles, localplayer.angles );
276
277 v3_copy( prev->cam_pos, localplayer.cam.pos );
278 v3_copy( prev->cam_angles, localplayer.cam.angles );
279 localplayer.cam.fov = prev->cam_fov;
280
281 memcpy( &localplayer.cam_control, &gs->cam_control,
282 sizeof(struct player_cam_controller) );
283
284 /* chop end off replay */
285 prev->r = NULL;
286 skaterift.replay.statehead = prev;
287 skaterift.replay.head = prev;
288 skaterift.replay.cursor_frame = prev;
289 skaterift.replay.cursor = prev->time;
290 skaterift.replay_control = k_replay_control_scrub;
291 skaterift.activity = k_skaterift_default;
292 vg.time = prev->time;
293 return;
294 }
295 }
296 }
297
298 VG_STATIC void skaterift_replay_debug_info(void){
299 player__debugtext( 2, "replay info" );
300
301 replay_buffer *replay = &skaterift.replay;
302
303 u32 head = 0,
304 tail = 0;
305 if( replay->tail ) tail = (void *)replay->tail - replay->data;
306 if( replay->head ) head = (void *)replay->head - replay->data;
307
308 player__debugtext( 1, "head @%u | tail @%u\n", head, tail );
309
310 if( replay->statehead ){
311 u32 state = (void *)replay->statehead - replay->data;
312 player__debugtext( 1, "gs @%u\n", state );
313 player__debugtext( 1, "gamestate_size: %u\n",
314 replay->statehead->subsystem_gamestate_size );
315 }
316 else
317 player__debugtext( 1, "gs @NULL\n" );
318
319 f64 start = replay->cursor,
320 end = replay->cursor;
321 if( replay->tail ) start = replay->tail->time;
322 if( replay->head ) end = replay->head->time;
323
324 f64 cur = replay->cursor - start,
325 len = end - start;
326
327 player__debugtext( 1, "cursor: %.2fs / %.2fs\n", cur, len );
328 }
329
330 VG_STATIC void skaterift_replay_imgui(void){
331 if( skaterift.activity != k_skaterift_replay ) return;
332
333 replay_buffer *replay = &skaterift.replay;
334 f64 start = replay->cursor,
335 end = replay->cursor;
336 if( replay->tail ) start = replay->tail->time;
337 if( replay->head ) end = replay->head->time;
338 f64 len = end - start,
339 cur = (replay->cursor - start) / len;
340
341 char buffer[ 128 ];
342
343 /* mainbar */
344 ui_px height = 20,
345 cwidth = 2;
346 ui_rect bar = { 0, vg.window_y - height, vg.window_x, height };
347 ui_fill( bar, ui_colour( k_ui_bg ) );
348
349 /* cursor frame block */
350 if( replay->cursor_frame ){
351 if( replay->cursor_frame->r ){
352 f64 l = (replay->cursor_frame->r->time-replay->cursor_frame->time)/len,
353 s = (replay->cursor_frame->time - start) / len;
354 ui_rect box = { s*(f64)vg.window_x, bar[1]-2,
355 VG_MAX(4,(ui_px)(l*vg.window_x)), bar[3]+2 };
356 ui_fill( box, ui_colour( k_ui_bg+4 ) );
357 }
358 }
359
360 /* cursor */
361 ui_rect cusor = { cur * (f64)vg.window_x - (cwidth/2), bar[1],
362 cwidth, bar[3] };
363 ui_fill( cusor, ui_colour( k_ui_bg+7 ) );
364
365 /* latest state marker */
366 if( replay->statehead ){
367 f64 t = (replay->statehead->time - start) / len;
368 ui_rect tag = { t*(f64)vg.window_x, bar[1]-8, 2, bar[3]+8 };
369 ui_fill( tag, ui_colour( k_ui_green+k_ui_brighter ) );
370 }
371
372 /* previous state marker */
373 replay_frame *prev = replay_find_recent_stateframe( replay );
374 if( prev ){
375 f64 t = (prev->time - start) / len;
376 ui_rect tag = { t*(f64)vg.window_x, bar[1]-8, 2, bar[3]+8 };
377 ui_fill( tag, ui_colour( k_ui_yellow+k_ui_brighter ) );
378 }
379
380 cusor[1] -= height;
381 cusor[2] = 200;
382 snprintf( buffer, 128, "-%.2fs\n", (end-replay->cursor) );
383 ui_text( cusor, buffer, 1, k_ui_align_middle_left, 0 );
384
385 snprintf( buffer, 128, "-%.2fs\n", len );
386 ui_text( bar, buffer, 1, k_ui_align_middle_left, 0 );
387 ui_text( bar, "0s", 1, k_ui_align_middle_right, 0 );
388 }
389
390 #endif /* PLAYER_REPLAY_C */