VG_VAR_F32( k_cam_punch );
VG_VAR_F32( k_cam_shake_strength );
VG_VAR_F32( k_cam_shake_trackspeed );
- VG_VAR_I32( k_player_debug_info );
+ VG_VAR_I32( k_player_debug_info, flags=VG_VAR_PERSISTENT );
vg_console_reg_var( "cinema", &k_cinema, k_var_dtype_f32, 0 );
vg_console_reg_var( "cinema_fixed", &k_cinema_fixed, k_var_dtype_i32, 0 );
if( _player_im_gui[ player->subsystem ] )
_player_im_gui[ player->subsystem ]( player );
+
+ replay_debug_info( player );
}
VG_STATIC void global_skateshop_exit(void);
#include "player_drive.c"
#include "player_render.c"
#include "player_ragdoll.c"
+#include "player_replay.c"
#endif /* PLAYER_C */
#ifndef PLAYER_H
#define PLAYER_H
+enum player_subsystem{
+ k_player_subsystem_walk = 0,
+ k_player_subsystem_skate = 1,
+ k_player_subsystem_dead = 2,
+ k_player_subsystem_drive = 3
+};
+
#include "player_ragdoll.h"
#include "player_render.h"
#include "player_model.h"
#include "player_skate.h"
#include "player_dead.h"
#include "player_drive.h"
+#include "player_replay.h"
#define PLAYER_REWIND_FRAMES 60*4
#define RESET_MAX_TIME 45.0
struct board_pose board_pose;
/*
- * Rewind
+ * Rewind (OLD SYSTEM)
* ----------------------------------------------------
*/
int rewinding, rewind_sound_wait;
dist_accum;
double rewind_start, rewind_time;
+ /*
+ * Replay
+ * -------------------------------------------------
+ */
+ replay_buffer replay;
+
/*
* Subsystems
* -------------------------------------------------
*/
- enum player_subsystem{
- k_player_subsystem_walk = 0,
- k_player_subsystem_skate = 1,
- k_player_subsystem_dead = 2,
- k_player_subsystem_drive = 3
- }
+ enum player_subsystem
subsystem,
subsystem_gate;
player__drive_post_animate
};
+
+__attribute__((deprecated))
VG_STATIC
void( *_player_restore[] )( player_instance *player ) =
{
NULL
};
+VG_STATIC
+void( *_player_store_state[] )( player_instance *player ) =
+{
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+VG_STATIC
+void( *_player_load_state_lerp[] )( player_instance *player,
+ void *A, void *B, f32 t ) =
+{
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
PLAYER_API void player__debugtext( int size, const char *fmt, ... );
PLAYER_API void player__create( player_instance *inst );
PLAYER_API void player__use_avatar( player_instance *player,
typedef mdl_keyframe player_pose[32];
typedef struct player_animation player_animation;
-struct player_animation
-{
+struct player_animation{
player_pose pose;
v3f root_co;
v4f root_q;
+
+ enum player_animation_type {
+ k_player_animation_type_fk, /* regular FK animation */
+ k_player_animation_type_absolute /* decomposition of the final matrices */
+ }
+ type;
};
#endif /* PLAYER_API_H */
{
if( _player_animate[ player->subsystem ] ){
player_animation res;
+ res.type = k_player_animation_type_fk;
+
_player_animate[ player->subsystem ]( player, &res );
m4x3f transform;
player->holdout_time -= vg.time_frame_delta * 2.0f;
}
- skeleton_apply_pose( sk, res.pose, k_anim_apply_defer_ik );
- skeleton_apply_ik_pass( sk );
- skeleton_apply_pose( sk, res.pose, k_anim_apply_deffered_only );
- skeleton_apply_inverses( sk );
- skeleton_apply_transform( sk, transform );
+ if( res.type == k_player_animation_type_fk ){
+ skeleton_apply_pose( sk, res.pose, k_anim_apply_defer_ik );
+ skeleton_apply_ik_pass( sk );
+ skeleton_apply_pose( sk, res.pose, k_anim_apply_deffered_only );
+ skeleton_apply_inverses( sk );
+ skeleton_apply_transform( sk, transform );
+ }
+ else {
+ skeleton_apply_pose( sk, res.pose, k_anim_apply_absolute );
+ }
skeleton_debug( sk );
}
return;
}
+#if 0
if( player->rewinding ){
if( player->rewind_time <= 0.0f ){
double taken = vg.time - player->rewind_start;
}
}
else player->cam_override_strength = 0.0f;
+#else
+ player->cam_override_strength = 0.0f;
+#endif
player__cam_iterate( player );
+
+ /* dev playbacker */
+ if( k_replay_test ){
+ f64 speed = 1.0;
+ f64 target = player->replay.cursor;
+
+ if( vg_getkey( SDLK_9 ) )
+ target -= vg.time_frame_delta * speed;
+ if( vg_getkey( SDLK_0 ) )
+ target += vg.time_frame_delta * speed;
+
+ replay_seek( &player->replay, target );
+
+ player_animation res;
+ replay_frame *frame = player->replay.cursor_frame;
+
+ if( frame ){
+ memcpy( &res, &frame->anim, sizeof(frame->anim) );
+ memcpy( &frame->board_pose, &player->board_pose,
+ sizeof(player->board_pose) );
+ }
+ else return;
+
+ struct skeleton *sk = &player->playeravatar->sk;
+ skeleton_apply_pose( sk, res.pose, k_anim_apply_absolute );
+ }
+ else {
+ /* replay recorder */
+ replay_buffer *replay = &player->replay;
+ f64 delta = vg.time - replay->cursor;
+ if( delta > (1.0/30.0) ){
+ replay->cursor = vg.time;
+ replay_frame *frame = replay_newframe( replay );
+
+ player_animation *res = &frame->anim;
+ v3_zero( res->root_co );
+ q_identity( res->root_q );
+ res->type = k_player_animation_type_absolute;
+
+ struct skeleton *sk = &player->playeravatar->sk;
+ skeleton_decompose_mtx_absolute( sk, res->pose );
+
+ memcpy( &frame->board_pose, &player->board_pose,
+ sizeof(player->board_pose) );
+ frame->time = vg.time;
+ }
+ }
}
VG_STATIC void render_board( camera *cam, world_instance *world,
--- /dev/null
+#ifndef PLAYER_REPLAY_C
+#define PLAYER_REPLAY_C
+
+#include "player_replay.h"
+
+VG_STATIC void local_replay_init( u32 bytes ){
+ localplayer.replay.data = vg_linear_alloc( vg_mem.rtmemory, bytes );
+ localplayer.replay.size = bytes;
+ localplayer.replay.cursor = -9999.9;
+ vg_console_reg_var( "k_replay_test", &k_replay_test, k_var_dtype_i32, 0 );
+}
+
+u32 replay_frame_size( replay_frame *frame ){
+ /* eventually it will be a dynamic size */
+ return vg_align8( sizeof( replay_frame ) );
+}
+
+VG_STATIC replay_frame *replay_newframe( replay_buffer *replay ){
+ replay_frame *frame = NULL;
+ if( replay->head ){
+ assert( replay->head );
+
+ u32 headsize = replay_frame_size( replay->head ),
+ nextpos = ((void *)replay->head - replay->data) + headsize,
+ nextsize = vg_align8( sizeof(replay_frame) );
+
+ if( nextsize > replay->size ){
+ vg_error( "Keyframe too big\n" );
+ return NULL;
+ }
+
+ if( nextpos + nextsize > replay->size )
+ nextpos = 0;
+
+check_again:;
+
+ u32 tailpos = (void *)replay->tail - replay->data;
+
+ if( tailpos >= nextpos ){
+ if( nextpos + nextsize > tailpos ){
+ if( replay->cursor_frame == replay->tail ){
+ replay->cursor_frame = NULL;
+ }
+
+ replay->tail = replay->tail->r;
+
+ if( replay->tail ) {
+ replay->tail->l = NULL;
+ goto check_again;
+ }
+ else{
+ replay->head = NULL;
+ }
+ }
+ }
+
+ frame = replay->data + nextpos;
+
+ if( replay->head ){
+ replay->head->r = frame;
+ }
+ }
+ else {
+ frame = replay->data;
+ }
+
+ frame->l = replay->head;
+ frame->r = NULL;
+ replay->head = frame;
+ if( !replay->tail ) replay->tail = frame;
+
+ return frame;
+}
+
+VG_STATIC void replay_seek( replay_buffer *replay, f64 t ){
+ if( !replay->head ) return;
+ assert( replay->tail );
+
+ if( t < replay->tail->time ) t = replay->tail->time;
+ if( t > replay->head->time ) t = replay->head->time;
+
+ if( !replay->cursor_frame ) {
+ replay->cursor = replay->head->time;
+ replay->cursor_frame = replay->head;
+
+ if( fabs(replay->head->time-t) > fabs(replay->tail->time-t) ){
+ replay->cursor = replay->tail->time;
+ replay->cursor_frame = replay->tail;
+ }
+ }
+
+ f64 dir = t - replay->cursor;
+ if( dir == 0.0 ) return;
+ dir = vg_signf( dir );
+
+ u32 i=0;
+ for( ; i<100; i++ ){
+ if( dir < 0.0 )
+ if( t > replay->cursor_frame->time ) break;
+
+ replay_frame *next;
+ if( dir > 0.0 ) next = replay->cursor_frame->r;
+ else next = replay->cursor_frame->l;
+
+ if( !next ) break;
+
+ if( dir > 0.0 )
+ if( t < next->time ) break;
+
+ replay->cursor_frame = next;
+ }
+
+ replay->cursor = t;
+}
+
+VG_STATIC void replay_debug_info( player_instance *player ){
+ player__debugtext( 2, "replay info" );
+
+ replay_buffer *replay = &player->replay;
+
+ u32 head = 0,
+ tail = 0;
+ if( replay->tail ) tail = (void *)replay->tail - replay->data;
+ if( replay->head ) head = (void *)replay->head - replay->data;
+
+ player__debugtext( 1, "head @%u | tail @%u\n", head, tail );
+
+ f64 start = replay->cursor,
+ end = replay->cursor;
+ if( replay->tail ) start = replay->tail->time;
+ if( replay->head ) end = replay->head->time;
+
+ f64 cur = replay->cursor - start,
+ len = end - start;
+
+ player__debugtext( 1, "cursor: %.2fs / %.2fs\n", cur, len );
+}
+
+VG_STATIC void replay_imgui( player_instance *player ){
+ if( !k_replay_test ) return;
+
+ replay_buffer *replay = &player->replay;
+ f64 start = replay->cursor,
+ end = replay->cursor;
+ if( replay->tail ) start = replay->tail->time;
+ if( replay->head ) end = replay->head->time;
+ f64 len = end - start,
+ cur = (replay->cursor - start) / len;
+
+ char buffer[ 128 ];
+
+ ui_px height = 20,
+ cwidth = 8;
+ ui_rect bar = { 0, vg.window_y - height, vg.window_x, height };
+ ui_fill( bar, ui_colour( k_ui_bg ) );
+
+ ui_rect cusor = { cur * (f64)vg.window_x - (cwidth/2), bar[1],
+ cwidth, bar[3] };
+ ui_fill( cusor, ui_colour( k_ui_bg+4 ) );
+
+ cusor[1] -= height;
+ cusor[2] = 200;
+ snprintf( buffer, 128, "-%.2fs\n", (end-replay->cursor) );
+ ui_text( cusor, buffer, 1, k_ui_align_middle_left, 0 );
+
+ snprintf( buffer, 128, "-%.2fs\n", len );
+ ui_text( bar, buffer, 1, k_ui_align_middle_left, 0 );
+ ui_text( bar, "0s", 1, k_ui_align_middle_right, 0 );
+}
+
+#endif /* PLAYER_REPLAY_C */
--- /dev/null
+#ifndef PLAYER_REPLAY_H
+#define PLAYER_REPLAY_H
+
+#include "skaterift.h"
+#include "player.h"
+#include "player_render.h"
+
+static i32 k_replay_test = 0;
+
+typedef struct replay_buffer replay_buffer;
+typedef struct replay_frame replay_frame;
+
+struct replay_buffer {
+ void *data;
+ u32 size; /* bytes */
+
+ replay_frame *head, *tail, *cursor_frame;
+
+ f64 cursor;
+};
+
+struct replay_frame {
+ player_animation anim;
+ struct board_pose board_pose;
+
+ f64 time;
+ replay_frame *l, *r;
+
+ /* eventually, sound events, player iframes and stuff? */
+};
+
+VG_STATIC void replay_debug_info( player_instance *player );
+VG_STATIC replay_frame *replay_newframe( replay_buffer *replay );
+VG_STATIC void replay_imgui( player_instance *player );
+VG_STATIC void replay_seek( replay_buffer *replay, f64 t );
+
+#endif /* PLAYER_REPLAY_H */
vg_loader_step( load_playermodels, NULL );
/* player setup */
+ local_replay_init( (1024*1024*1)/2 );
player__create( &localplayer );
player_avatar_load( &localplayer_avatar, "models/ch_none.mdl" );
player__use_avatar( &localplayer, &localplayer_avatar );
player__im_gui( &localplayer );
world_instance *world = world_current_instance();
+ replay_imgui( &localplayer );
workshop_form_gui();
render_view_framebuffer_ui();
}
{
k_anim_apply_always,
k_anim_apply_defer_ik,
- k_anim_apply_deffered_only
+ k_anim_apply_deffered_only,
+ k_anim_apply_absolute
}
anim_apply;
VG_STATIC void skeleton_apply_pose( struct skeleton *skele, mdl_keyframe *pose,
anim_apply passtype )
{
+ if( passtype == k_anim_apply_absolute ){
+ for( u32 i=1; i<skele->bone_count; i++ ){
+ mdl_keyframe *kf = &pose[i-1];
+
+ v3f *posemtx = skele->final_mtx[i];
+
+ q_m3x3( kf->q, posemtx );
+ v3_copy( kf->co, posemtx[3] );
+ }
+ return;
+ }
+
m4x3_identity( skele->final_mtx[0] );
skele->bones[0].defer = 0;
skele->bones[0].flags &= ~k_bone_flag_ik;
}
}
+/*
+ * Take the final matrices and decompose it into an absolute positioned anim
+ */
+VG_STATIC void skeleton_decompose_mtx_absolute( struct skeleton *skele,
+ mdl_keyframe *anim ){
+ for( u32 i=1; i<skele->bone_count; i++ ){
+ struct skeleton_bone *sb = &skele->bones[i];
+ mdl_keyframe *kf = &anim[i-1];
+ m4x3_decompose( skele->final_mtx[i], kf->co, kf->q, kf->s );
+ }
+}
+
/*
* creates the reference inverse matrix for an IK bone, as it has an initial
* intrisic rotation based on the direction that the IK is setup..