+void player_glide_transition(void)
+{
+ localplayer.subsystem = k_player_subsystem_glide;
+ localplayer.have_glider = 0;
+ world_static.challenge_target = NULL;
+ world_static.challenge_timer = 0.0f;
+ world_static.focused_entity = 0;
+ world_static.last_use = 0.0;
+ for( u32 i=0; i<vg_list_size(world_static.instances); i++ ){
+ world_instance *instance = &world_static.instances[i];
+ if( instance->status == k_world_status_loaded ){
+ world_routes_clear( instance );
+ }
+ }
+
+ v3_copy( localplayer.rb.co, player_glide.rb.co );
+
+ f32 dir = v3_dot( localplayer.rb.v, localplayer.rb.to_world[2] );
+
+ if( dir > 0.0f ){
+ v4f qyaw;
+ q_axis_angle( qyaw, (v3f){0,1,0}, VG_TAUf*0.5f );
+ q_mul( qyaw, localplayer.rb.q, player_glide.rb.q );
+ q_normalize( player_glide.rb.q );
+ }
+ else
+ v4_copy( localplayer.rb.q, player_glide.rb.q );
+
+ v3_copy( localplayer.rb.v, player_glide.rb.v );
+ v3_copy( localplayer.rb.w, player_glide.rb.w );
+ rb_update_matrices( &player_glide.rb );
+
+ player__begin_holdout( (v3f){0,0,0} );
+}
+
+void render_glider_model( vg_camera *cam, world_instance *world,
+ m4x3f mmdl, enum board_shader shader )
+{
+ u32 current_mat = 0xffffffff;
+ glActiveTexture( GL_TEXTURE0 );
+
+ mdl_context *mdl = &player_glide.glider;
+ mesh_bind( &player_glide.glider.mesh );
+
+ for( u32 i=0; i<mdl_arrcount(&mdl->meshs); i ++ )
+ {
+ mdl_mesh *mesh = mdl_arritm( &mdl->meshs, i );
+
+ m4x3f mmmdl;
+ mdl_transform_m4x3( &mesh->transform, mmmdl );
+ m4x3_mul( mmdl, mmmdl, mmmdl );
+
+ if( shader == k_board_shader_player )
+ shader_model_board_view_uMdl( mmmdl );
+ else if( shader == k_board_shader_entity )
+ {
+ m4x4f m4mmmdl;
+ m4x3_expand( mmmdl, m4mmmdl );
+ m4x4_mul( cam->mtx_prev.pv, m4mmmdl, m4mmmdl );
+
+ shader_model_entity_uMdl( mmmdl );
+ shader_model_entity_uPvmPrev( m4mmmdl );
+ }
+
+ for( u32 j=0; j<mesh->submesh_count; j ++ )
+ {
+ mdl_submesh *sm = mdl_arritm( &mdl->submeshs, mesh->submesh_start+j );
+ if( !sm->material_id )
+ {
+ vg_error( "Invalid material ID 0\n" );
+ continue;
+ }
+
+ if( sm->material_id != current_mat )
+ {
+ mdl_material *mat = mdl_arritm( &mdl->materials,sm->material_id-1 );
+ GLuint tex = vg.tex_missing;
+
+ if( mat->shader == k_shader_standard )
+ {
+ struct shader_props_standard *props = mat->props.compiled;
+
+ u32 index = props->tex_diffuse-1;
+ mdl_texture *ptex = mdl_arritm( &mdl->textures, index );
+ tex = ptex->glname;
+ }
+
+ glBindTexture( GL_TEXTURE_2D, tex );
+ current_mat = sm->material_id;
+ }
+
+ mdl_draw_submesh( sm );
+ }
+ }
+}
+
+/*
+ * TODO: more flexible way to call
+ * - this depends on the current state, but we need to pass a struct in
+ * that can hold that information instead so we can save it into
+ * the replay
+ */
+void player_glide_render( vg_camera *cam, world_instance *world,
+ player_pose *pose )
+{
+ if( !((localplayer.subsystem == k_player_subsystem_glide) ||
+ (localplayer.observing_system == k_player_subsystem_glide) ||
+ localplayer.have_glider ||
+ localplayer.glider_orphan) )
+ return;
+
+ shader_model_board_view_use();
+ shader_model_board_view_uTexMain( 0 );
+ shader_model_board_view_uCamera( cam->transform[3] );
+ shader_model_board_view_uPv( cam->mtx.pv );
+ shader_model_board_view_uDepthMode(1);
+ depth_compare_bind(
+ shader_model_board_view_uTexSceneDepth,
+ shader_model_board_view_uInverseRatioDepth,
+ shader_model_board_view_uInverseRatioMain,
+ cam );
+
+ WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( world, model_board_view );
+
+ mdl_keyframe kf_res;
+ if( localplayer.glider_orphan ){
+ rb_extrapolate( &player_glide.rb, kf_res.co, kf_res.q );
+ v3_fill( kf_res.s, 1.0f );
+
+ v3f temp;
+ q_mulv( kf_res.q, (v3f){0,-0.5f,0}, temp );
+ v3_add( temp, kf_res.co, kf_res.co );
+ }
+ else {
+ f32 target;
+ if( localplayer.subsystem == k_player_subsystem_glide ) target = 1.0f;
+ else target = 0.0f;
+
+ /* TODO: TEMP */
+ if( skaterift.activity != k_skaterift_replay )
+ vg_slewf( &player_glide.t, target, vg.time_frame_delta * 4.0f );
+
+ mdl_keyframe kf_backpack;
+
+ struct skeleton *sk = &localplayer.skeleton;
+ m4x3_mulv( localplayer.final_mtx[localplayer.id_chest ],
+ sk->bones[localplayer.id_chest].co,
+ kf_backpack.co );
+
+ v4f qyaw, qpitch, qchest, q;
+ q_axis_angle( qyaw, (v3f){0,1,0}, VG_TAUf*0.25f );
+ q_axis_angle( qpitch, (v3f){1,0,0}, VG_TAUf*0.25f );
+ m3x3_q( localplayer.final_mtx[ localplayer.id_chest ], qchest );
+
+ q_mul( qyaw, qpitch, q );
+ q_mul( qchest, q, kf_backpack.q );
+ q_normalize( kf_backpack.q );
+
+ f32 scale;
+ if( player_glide.t <= 0.0f ){
+ f32 st = player_glide.t + 1.0f,
+ sst = vg_smoothstepf(st),
+ isst= 1.0f - sst;
+ scale = vg_lerpf( 0.0f, 0.2f, sst );
+
+ v4f qspin;
+ q_axis_angle( qspin, (v3f){0,0,1}, VG_TAUf * isst * 0.5f );
+ q_mul( kf_backpack.q, qspin, kf_backpack.q );
+ kf_backpack.co[1] += isst * 1.0f;
+ v3_muladds( kf_backpack.co,
+ localplayer.final_mtx[ localplayer.id_chest ][0],
+ isst * 0.25f,
+ kf_backpack.co );
+ }
+ else{
+ scale = vg_lerpf( 0.2f, 1.0f, vg_smoothstepf(player_glide.t) );
+ }
+
+
+ v3_fill( kf_backpack.s, scale );
+
+ v3_copy( pose->root_co, kf_res.co );
+ v4_copy( pose->root_q, kf_res.q );
+ v3_fill( kf_res.s, scale );
+
+ f32 blend = vg_smoothstepf( vg_maxf( 0, player_glide.t ) );
+ keyframe_lerp( &kf_backpack, &kf_res, blend, &kf_res );
+ }
+
+ m4x3f mmdl;
+ q_m3x3( kf_res.q, mmdl );
+ m3x3_scale( mmdl, kf_res.s );
+ v3_copy( kf_res.co, mmdl[3] );
+
+ render_glider_model( cam, world, mmdl, k_board_shader_player );
+
+ /* totally FUCKED */
+ v4_copy( kf_res.q, player_glide.remote_animator.root_q );
+ v3_copy( kf_res.co, player_glide.remote_animator.root_co );
+ player_glide.remote_animator.s = kf_res.s[0];
+}
+
+void player_glide_render_effects( vg_camera *cam )
+{
+ v3f co, temp;
+ v4f q;
+ rb_extrapolate( &player_glide.rb, co, q );
+ q_mulv( q, (v3f){0,-0.5f,0}, temp );
+ v3_add( temp, co, co );
+
+ f32 alpha = vg_maxf( (fabsf(player_glide.info_lift[2])-1.0f), 0.0f ) /18.0f;
+
+ for( u32 i=0; i<player_glide.trail_count; i ++ ){
+ v3f vvert;
+ q_mulv( q, player_glide.trail_positions[i], vvert );
+ v3_add( co, vvert, vvert );
+
+ trail_system_update( &trails_glider[i], vg.time_delta, vvert,
+ localplayer.rb.to_world[1], alpha );
+
+ trail_system_prerender( &trails_glider[i] );
+ trail_system_render( &trails_glider[i], &skaterift.cam );
+ }
+}