refactor rewind tape 1
[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 replay->control = k_replay_control_none;
13 }
14
15 VG_STATIC void local_replay_init( u32 bytes ){
16 localplayer.replay.data = vg_linear_alloc( vg_mem.rtmemory, bytes );
17 localplayer.replay.size = bytes;
18 replay_clear( &localplayer.replay );
19 }
20
21 replay_gamestate *replay_frame_gamestate( replay_frame *frame, u16 index ){
22 void *baseptr = frame;
23
24 replay_gamestate *array = (baseptr + vg_align8( sizeof(replay_frame)));
25 return &array[ index ];
26 }
27
28 replay_sfx *replay_frame_sfx( replay_frame *frame, u16 index ){
29 void *gsarr = replay_frame_gamestate( frame, 0 );
30 u32 gssize = frame->gamestate_count * sizeof(replay_gamestate);
31
32 replay_sfx *array = (gsarr + vg_align8(gssize));
33 return &array[index];
34 }
35
36 u32 _replay_frame_size( u16 gamestate_count, u16 sfx_count ){
37 return vg_align8( sizeof( replay_frame ) ) +
38 vg_align8( gamestate_count * sizeof(replay_gamestate) ) +
39 vg_align8( sfx_count * sizeof(replay_sfx) );
40 }
41
42 u32 replay_frame_size( replay_frame *frame ){
43 return _replay_frame_size( frame->gamestate_count, frame->sfx_count );
44 }
45
46 VG_STATIC replay_frame *replay_newframe( replay_buffer *replay,
47 u16 gamestate_count, u16 sfx_count ){
48 replay_frame *frame = NULL;
49 if( replay->head ){
50 assert( replay->head );
51
52 u32 headsize = replay_frame_size( replay->head ),
53 nextpos = ((void *)replay->head - replay->data) + headsize,
54 nextsize = _replay_frame_size( gamestate_count, sfx_count );
55
56 if( nextsize > replay->size ){
57 vg_error( "Keyframe too big\n" );
58 return NULL;
59 }
60
61 if( nextpos + nextsize > replay->size )
62 nextpos = 0;
63
64 check_again:;
65
66 u32 tailpos = (void *)replay->tail - replay->data;
67
68 if( tailpos >= nextpos ){
69 if( nextpos + nextsize > tailpos ){
70 /* remove links */
71 if( replay->cursor_frame == replay->tail )
72 replay->cursor_frame = NULL;
73 if( replay->statehead == replay->tail )
74 replay->statehead = NULL;
75
76 /* pop node */
77 replay->tail = replay->tail->r;
78
79 if( replay->tail ) {
80 replay->tail->l = NULL;
81 goto check_again;
82 }
83 else
84 replay->head = NULL;
85 }
86 }
87
88 frame = replay->data + nextpos;
89
90 if( replay->head )
91 replay->head->r = frame;
92 }
93 else
94 frame = replay->data;
95
96 frame->gamestate_count = gamestate_count;
97 frame->sfx_count = sfx_count;
98 frame->l = replay->head;
99 frame->r = NULL;
100 replay->head = frame;
101 if( !replay->tail ) replay->tail = frame;
102 if( gamestate_count ) replay->statehead = frame;
103
104 return frame;
105 }
106
107 VG_STATIC void replay_seek( replay_buffer *replay, f64 t ){
108 if( !replay->head ) return;
109 assert( replay->tail );
110
111 if( t < replay->tail->time ) t = replay->tail->time;
112 if( t > replay->head->time ) t = replay->head->time;
113
114 if( !replay->cursor_frame ) {
115 replay->cursor = replay->head->time;
116 replay->cursor_frame = replay->head;
117
118 if( fabs(replay->head->time-t) > fabs(replay->tail->time-t) ){
119 replay->cursor = replay->tail->time;
120 replay->cursor_frame = replay->tail;
121 }
122 }
123
124 f64 dir = t - replay->cursor;
125 if( dir == 0.0 ) return;
126 dir = vg_signf( dir );
127
128 u32 i=4096;
129 while( i --> 0 ){
130 if( dir < 0.0 )
131 if( t > replay->cursor_frame->time ) break;
132
133 replay_frame *next;
134 if( dir > 0.0 ) next = replay->cursor_frame->r;
135 else next = replay->cursor_frame->l;
136
137 if( !next ) break;
138
139 if( dir > 0.0 )
140 if( t < next->time ) break;
141
142 replay->cursor_frame = next;
143 replay->cursor = next->time;
144
145 if( !i ) return;
146 }
147
148 replay->cursor = t;
149 }
150
151 VG_STATIC replay_frame *replay_find_recent_stateframe( replay_buffer *replay ){
152 replay_frame *frame = replay->cursor_frame;
153
154 u32 i=4096;
155 while( i --> 0 ){
156 if( !frame ) return frame;
157 if( frame->gamestate_count ) return frame;
158 frame = frame->l;
159 }
160
161 return NULL;
162 }
163
164 VG_STATIC void player_replay_control_update( player_instance *player ){
165 f64 speed = 1.0;
166 f64 target = player->replay.cursor;
167
168 if( vg_getkey( SDLK_9 ) ){
169 target -= vg.time_frame_delta * speed;
170 player->replay.control = k_replay_control_scrub;
171 replay_seek( &player->replay, target );
172 }
173 if( vg_getkey( SDLK_0 ) ){
174 target += vg.time_frame_delta * speed;
175 player->replay.control = k_replay_control_scrub;
176 replay_seek( &player->replay, target );
177 }
178
179 if( vg_getkey( SDLK_7 ) )
180 player->replay.control = k_replay_control_play;
181
182 if( player->replay.control == k_replay_control_play ){
183 target += vg.time_frame_delta;
184 replay_seek( &player->replay, target );
185 }
186
187 if( vg_getkey( SDLK_8 ) ){
188 replay_frame *prev = replay_find_recent_stateframe( &player->replay );
189
190 if( prev ){
191 /* TODO: Make gamestate_apply function / swap ... */
192 replay_gamestate *gs = replay_frame_gamestate( prev, 0 );
193
194 if( gs->system == k_player_subsystem_walk ){
195 memcpy( &player->_walk.state, &gs->walk,
196 sizeof(struct player_walk_state) );
197 }
198 else if( gs->system == k_player_subsystem_skate ){
199 memcpy( &player->_skate.state, &gs->skate,
200 sizeof(struct player_skate_state) );
201 }
202 player->subsystem = gs->system;
203
204 memcpy( &player->rb, &gs->rb, sizeof(rigidbody) );
205 v3_copy( gs->angles, player->angles );
206 v3_copy( prev->cam_pos, player->cam.pos );
207 v3_copy( prev->cam_angles, player->cam_override_angles );
208 player->cam.fov = prev->cam_fov;
209 memcpy( &player->cam_control, &gs->cam_control,
210 sizeof(struct player_cam_controller) );
211
212 /* chop end off replay */
213 prev->r = NULL;
214 player->replay.statehead = prev;
215 player->replay.head = prev;
216 player->replay.cursor_frame = prev;
217 player->replay.cursor = prev->time;
218 player->replay.control = k_replay_control_none;
219 vg.time = prev->time;
220 return;
221 }
222 }
223 }
224
225 VG_STATIC void replay_debug_info( player_instance *player ){
226 player__debugtext( 2, "replay info" );
227
228 replay_buffer *replay = &player->replay;
229
230 u32 head = 0,
231 tail = 0;
232 if( replay->tail ) tail = (void *)replay->tail - replay->data;
233 if( replay->head ) head = (void *)replay->head - replay->data;
234
235 player__debugtext( 1, "head @%u | tail @%u\n", head, tail );
236
237 if( replay->statehead ){
238 u32 state = (void *)replay->statehead - replay->data;
239 player__debugtext( 1, "gs @%u\n", state );
240 }
241 else
242 player__debugtext( 1, "gs @NULL\n" );
243
244 f64 start = replay->cursor,
245 end = replay->cursor;
246 if( replay->tail ) start = replay->tail->time;
247 if( replay->head ) end = replay->head->time;
248
249 f64 cur = replay->cursor - start,
250 len = end - start;
251
252 player__debugtext( 1, "cursor: %.2fs / %.2fs\n", cur, len );
253 }
254
255 VG_STATIC void replay_imgui( player_instance *player ){
256 if( player->replay.control == k_replay_control_none ) return;
257
258 replay_buffer *replay = &player->replay;
259 f64 start = replay->cursor,
260 end = replay->cursor;
261 if( replay->tail ) start = replay->tail->time;
262 if( replay->head ) end = replay->head->time;
263 f64 len = end - start,
264 cur = (replay->cursor - start) / len;
265
266 char buffer[ 128 ];
267
268 /* mainbar */
269 ui_px height = 20,
270 cwidth = 2;
271 ui_rect bar = { 0, vg.window_y - height, vg.window_x, height };
272 ui_fill( bar, ui_colour( k_ui_bg ) );
273
274 /* cursor frame block */
275 if( replay->cursor_frame ){
276 if( replay->cursor_frame->r ){
277 f64 l = (replay->cursor_frame->r->time-replay->cursor_frame->time)/len,
278 s = (replay->cursor_frame->time - start) / len;
279 ui_rect box = { s*(f64)vg.window_x, bar[1]-2,
280 VG_MAX(4,(ui_px)l), bar[3]+2 };
281 ui_fill( box, ui_colour( k_ui_bg+4 ) );
282 }
283 }
284
285 /* cursor */
286 ui_rect cusor = { cur * (f64)vg.window_x - (cwidth/2), bar[1],
287 cwidth, bar[3] };
288 ui_fill( cusor, ui_colour( k_ui_bg+7 ) );
289
290 /* latest state marker */
291 if( replay->statehead ){
292 f64 t = (replay->statehead->time - start) / len;
293 ui_rect tag = { t*(f64)vg.window_x, bar[1]-8, 2, bar[3]+8 };
294 ui_fill( tag, ui_colour( k_ui_green+k_ui_brighter ) );
295 }
296
297 /* previous state marker */
298 replay_frame *prev = replay_find_recent_stateframe( replay );
299 if( prev ){
300 f64 t = (prev->time - start) / len;
301 ui_rect tag = { t*(f64)vg.window_x, bar[1]-8, 2, bar[3]+8 };
302 ui_fill( tag, ui_colour( k_ui_yellow+k_ui_brighter ) );
303 }
304
305 cusor[1] -= height;
306 cusor[2] = 200;
307 snprintf( buffer, 128, "-%.2fs\n", (end-replay->cursor) );
308 ui_text( cusor, buffer, 1, k_ui_align_middle_left, 0 );
309
310 snprintf( buffer, 128, "-%.2fs\n", len );
311 ui_text( bar, buffer, 1, k_ui_align_middle_left, 0 );
312 ui_text( bar, "0s", 1, k_ui_align_middle_right, 0 );
313 }
314
315 #endif /* PLAYER_REPLAY_C */