controls overlay
[carveJwlIkooP6JGAAIwe30JlM.git] / control_overlay.c
diff --git a/control_overlay.c b/control_overlay.c
new file mode 100644 (file)
index 0000000..083a411
--- /dev/null
@@ -0,0 +1,886 @@
+#include "control_overlay.h"
+#include "model.h"
+#include "input.h"
+#include "player.h"
+#include "player_skate.h"
+#include "player_walk.h"
+#include "shaders/model_menu.h"
+#include "vg/vg_engine.h"
+#include "vg/vg_mem.h"
+#include "vg/vg_m.h"
+
+struct control_overlay control_overlay = { .enabled = 1 };
+
+void control_overlay_init(void)
+{
+   void *alloc = vg_mem.rtmemory;
+   mdl_context *mdl = &control_overlay.mdl;
+
+   mdl_open( mdl, "models/rs_overlay.mdl", alloc );
+   mdl_load_metadata_block( mdl, alloc );
+   mdl_async_full_load_std( mdl );
+   mdl_close( mdl );
+
+   vg_async_stall();
+
+   if( mdl_arrcount( &mdl->textures ) )
+   {
+      mdl_texture *tex = mdl_arritm( &mdl->textures, 0 );
+      control_overlay.tex = tex->glname;
+   }
+   else
+   {
+      control_overlay.tex = vg.tex_missing;
+      vg_error( "No texture in control overlay\n" );
+   }
+
+   control_overlay.m_key = mdl_find_submesh( mdl, "ov_key" );
+   control_overlay.m_key_down = mdl_find_submesh( mdl, "ov_key_down" );
+   control_overlay.m_shift = mdl_find_submesh( mdl, "ov_shift" );
+   control_overlay.m_shift_down = mdl_find_submesh( mdl, "ov_shift_down" );
+   control_overlay.m_space = mdl_find_submesh( mdl, "ov_space" );
+   control_overlay.m_space_down = mdl_find_submesh( mdl, "ov_space_down" );
+   control_overlay.m_jump_ind = mdl_find_submesh( mdl, "ov_jump_ind" );
+   control_overlay.m_text_jump = mdl_find_submesh( mdl, "ov_text_jump" );
+   control_overlay.m_text_crouch = mdl_find_submesh( mdl, "ov_text_crouch" );
+   control_overlay.m_text_shift = mdl_find_submesh( mdl, "ov_text_shift" );
+   control_overlay.m_text_grab = mdl_find_submesh( mdl, "ov_text_grab" );
+   control_overlay.m_text_carve = mdl_find_submesh( mdl, "ov_text_carve" );
+   control_overlay.m_stored_ind = mdl_find_submesh( mdl, "ov_stored_ind" );
+   control_overlay.m_text_stored = mdl_find_submesh( mdl, "ov_text_stored" );
+   control_overlay.m_text_left = mdl_find_submesh( mdl, "ov_text_left" );
+   control_overlay.m_text_right = mdl_find_submesh( mdl, "ov_text_right" );
+   control_overlay.m_text_push = mdl_find_submesh( mdl, "ov_text_push" );
+   control_overlay.m_text_manual = mdl_find_submesh( mdl, "ov_text_manual" );
+   control_overlay.m_text_front_flip = mdl_find_submesh( mdl, "ov_text_front_flip" );
+   control_overlay.m_text_back_flip = mdl_find_submesh( mdl, "ov_text_back_flip" );
+   control_overlay.m_text_s = mdl_find_submesh( mdl, "ov_text_s" );
+   control_overlay.m_text_w = mdl_find_submesh( mdl, "ov_text_w" );
+
+   control_overlay.m_text_walk = mdl_find_submesh( mdl, "ov_text_walk" );
+   control_overlay.m_text_back = mdl_find_submesh( mdl, "ov_text_back" );
+   control_overlay.m_text_forward = mdl_find_submesh( mdl, "ov_text_forward" );
+   control_overlay.m_text_skate = mdl_find_submesh( mdl, "ov_text_skate" );
+   control_overlay.m_text_walk_lwr = mdl_find_submesh( mdl, "ov_text_walk_lwr" );
+   control_overlay.m_text_e = mdl_find_submesh( mdl, "ov_text_e" );
+   control_overlay.m_text_glide = mdl_find_submesh( mdl, "ov_text_glide" );
+   control_overlay.m_text_camera = mdl_find_submesh( mdl, "ov_text_camera" );
+   control_overlay.m_text_run = mdl_find_submesh( mdl, "ov_text_run" );
+   control_overlay.m_text_look = mdl_find_submesh( mdl, "ov_text_look" );
+   control_overlay.m_text_rewind = mdl_find_submesh( mdl, "ov_text_rewind" );
+
+   control_overlay.m_lmb = mdl_find_submesh( mdl, "ov_lmb" );
+   control_overlay.m_lmb_down = mdl_find_submesh( mdl, "ov_lmb_down" );
+   control_overlay.m_rmb = mdl_find_submesh( mdl, "ov_rmb" );
+   control_overlay.m_rmb_down = mdl_find_submesh( mdl, "ov_rmb_down" );
+   control_overlay.m_mouse = mdl_find_submesh( mdl, "ov_mouse" );
+   control_overlay.m_mouse_grabs = mdl_find_submesh( mdl, "ov_mouse_grabs" );
+   control_overlay.m_text_kickflip = mdl_find_submesh( mdl, "ov_text_kickflip" );
+   control_overlay.m_text_treflip = mdl_find_submesh( mdl, "ov_text_treflip" );
+   control_overlay.m_text_shuvit = mdl_find_submesh( mdl, "ov_text_shuvit" );
+   control_overlay.m_text_respawn = mdl_find_submesh( mdl, "ov_text_respawn" );
+
+   control_overlay.m_ls = mdl_find_submesh( mdl, "ov_ls" );
+   control_overlay.m_ls_circ_backflip = mdl_find_submesh( mdl, "ov_ls_circ_backflip" );
+   control_overlay.m_ls_circ_frontflip = mdl_find_submesh( mdl, "ov_ls_circ_frontflip" );
+   control_overlay.m_ls_circ_manual = mdl_find_submesh( mdl, "ov_ls_circ_manual" );
+   control_overlay.m_ls_circ_skate = mdl_find_submesh( mdl, "ov_ls_circ_skate" );
+   control_overlay.m_ls_circ_walk = mdl_find_submesh( mdl, "ov_ls_circ_walk" );
+   control_overlay.m_rs = mdl_find_submesh( mdl, "ov_rs" );
+   control_overlay.m_rs_circ_look = mdl_find_submesh( mdl, "ov_rs_circ_look" );
+   control_overlay.m_rs_circ_grab = mdl_find_submesh( mdl, "ov_rs_circ_grab" );
+
+   control_overlay.m_lb = mdl_find_submesh( mdl, "ov_lb" );
+   control_overlay.m_lb_down = mdl_find_submesh( mdl, "ov_lb_down" );
+   control_overlay.m_carve_l = mdl_find_submesh( mdl, "ov_carve_l" );
+   control_overlay.m_rb = mdl_find_submesh( mdl, "ov_rb" );
+   control_overlay.m_rb_down = mdl_find_submesh( mdl, "ov_rb_down" );
+   control_overlay.m_carve_r = mdl_find_submesh( mdl, "ov_carve_r" );
+   control_overlay.m_lt = mdl_find_submesh( mdl, "ov_lt" );
+   control_overlay.m_lt_act = mdl_find_submesh( mdl, "ov_lt_act" );
+   control_overlay.m_lt_run = mdl_find_submesh( mdl, "ov_lt_run" );
+   control_overlay.m_rt = mdl_find_submesh( mdl, "ov_rt" );
+   control_overlay.m_rt_act = mdl_find_submesh( mdl, "ov_rt_act" );
+   control_overlay.m_rt_grab = mdl_find_submesh( mdl, "ov_rt_grab" );
+   control_overlay.m_rt_crouch = mdl_find_submesh( mdl, "ov_rt_crouch" );
+
+   control_overlay.m_y = mdl_find_submesh( mdl, "ov_y" );
+   control_overlay.m_y_down = mdl_find_submesh( mdl, "ov_y_down" );
+   control_overlay.m_text_y_walk = mdl_find_submesh( mdl, "ov_text_y_walk" );
+   control_overlay.m_text_y_walk_lwr = mdl_find_submesh( mdl, "ov_text_y_walk_lwr" );
+   control_overlay.m_text_y_glide = mdl_find_submesh( mdl, "ov_text_y_glide" );
+   control_overlay.m_text_y_skate = mdl_find_submesh( mdl, "ov_text_y_skate" );
+   control_overlay.m_b = mdl_find_submesh( mdl, "ov_b" );
+   control_overlay.m_b_down = mdl_find_submesh( mdl, "ov_b_down" );
+   control_overlay.m_text_b_kickflip = mdl_find_submesh( mdl, "ov_text_b_kickflip" );
+   control_overlay.m_text_b_push = mdl_find_submesh( mdl, "ov_text_b_push" );
+   control_overlay.m_x = mdl_find_submesh( mdl, "ov_x" );
+   control_overlay.m_x_down = mdl_find_submesh( mdl, "ov_x_down" );
+   control_overlay.m_text_x_treflip = mdl_find_submesh( mdl, "ov_text_x_treflip" );
+   control_overlay.m_a = mdl_find_submesh( mdl, "ov_a" );
+   control_overlay.m_a_down = mdl_find_submesh( mdl, "ov_a_down" );
+   control_overlay.m_text_a_shuvit = mdl_find_submesh( mdl, "ov_text_a_shuvit" );
+   control_overlay.m_text_a_jump = mdl_find_submesh( mdl, "ov_text_a_jump" );
+   control_overlay.m_text_a_jump_mid = mdl_find_submesh( mdl, "ov_text_a_jump_mid" );
+
+   control_overlay.m_dpad = mdl_find_submesh( mdl, "ov_dpad" );
+   control_overlay.m_dpad_w = mdl_find_submesh( mdl, "ov_dpad_w" );
+   control_overlay.m_dpad_n = mdl_find_submesh( mdl, "ov_dpad_n" );
+   control_overlay.m_dpad_e = mdl_find_submesh( mdl, "ov_dpad_e" );
+   control_overlay.m_dpad_s = mdl_find_submesh( mdl, "ov_dpad_s" );
+   control_overlay.m_text_dw_rewind = mdl_find_submesh( mdl, "ov_text_dw_rewind" );
+   control_overlay.m_text_de_camera = mdl_find_submesh( mdl, "ov_text_de_camera" );
+   control_overlay.m_text_dn_respawn = mdl_find_submesh( mdl, "ov_text_dn_respawn" );
+
+   control_overlay.m_met = mdl_find_submesh( mdl, "ov_met" );
+   control_overlay.m_met_r = mdl_find_submesh( mdl, "ov_met_r" );
+   control_overlay.m_met_l = mdl_find_submesh( mdl, "ov_met_l" );
+   control_overlay.m_met_r_down = mdl_find_submesh( mdl, "ov_met_r_down" );
+   control_overlay.m_met_l_down = mdl_find_submesh( mdl, "ov_met_l_down" );
+   control_overlay.m_text_met_menu = mdl_find_submesh( mdl, "ov_text_met_menu" );
+
+   control_overlay.m_key_menu = mdl_find_submesh( mdl, "ov_key_menu" );
+   control_overlay.m_key_menu_down = mdl_find_submesh( mdl, "ov_key_menu_down" );
+   control_overlay.m_text_menu = mdl_find_submesh( mdl, "ov_text_menu" );
+
+   vg_console_reg_var( "control_overlay", &control_overlay.enabled, 
+                       k_var_dtype_i32, VG_VAR_PERSISTENT );
+}
+
+static void draw_key( bool press, bool wide )
+{
+   if( wide )
+      mdl_draw_submesh( press? 
+                        control_overlay.m_shift_down: control_overlay.m_shift );
+   else
+      mdl_draw_submesh( press? 
+                        control_overlay.m_key_down: control_overlay.m_key );
+}
+
+static void colorize( bool press, bool condition )
+{
+   v4f cnorm = { 1,1,1,0.76f },
+       cdis  = { 1,1,1,0.35f },
+       chit  = { 1,0.5f,0.2f,0.8f };
+
+   if( condition )
+      if( press )
+         shader_model_menu_uColour( chit );
+      else
+         shader_model_menu_uColour( cnorm );
+   else
+      shader_model_menu_uColour( cdis );
+}
+
+void control_overlay_render(void)
+{
+   if( !control_overlay.enabled ) return;
+   if( skaterift.activity != k_skaterift_default ) return;
+
+   glEnable(GL_BLEND);
+   glDisable(GL_DEPTH_TEST);
+   glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
+   glBlendEquation(GL_FUNC_ADD);
+
+   m4x4f ortho;
+   f32  r = (f32)vg.window_x / (f32)vg.window_y,
+       fl = -r,
+       fr =  r,
+       fb =  1.0f,
+       ft = -1.0f,
+       rl = 1.0f / (fr-fl),
+       tb = 1.0f / (ft-fb);
+
+   m4x4_zero( ortho );
+   ortho[0][0] = 2.0f * rl;
+   ortho[2][1] = 2.0f * tb;
+   ortho[3][0] = (fr + fl) * -rl;
+   ortho[3][1] = (ft + fb) * -tb;
+   ortho[3][3] = 1.0f;
+
+   v4f cnorm = { 1,1,1,0.76f },
+       cdis  = { 1,1,1,0.35f },
+       chit  = { 1,0.5f,0.2f,0.8f };
+
+   shader_model_menu_use();
+   shader_model_menu_uTexMain( 1 );
+   shader_model_menu_uPv( ortho );
+   shader_model_menu_uColour( cnorm );
+
+   mdl_context *mdl = &control_overlay.mdl;
+   mesh_bind( &mdl->mesh );
+   glActiveTexture( GL_TEXTURE1 );
+   glBindTexture( GL_TEXTURE_2D, control_overlay.tex );
+
+   enum player_subsystem subsytem = localplayer.subsystem;
+
+   m4x3f mmdl;
+   m4x3_identity( mmdl );
+
+   bool in_air = 0, grinding = 0;
+
+   if( subsytem == k_player_subsystem_walk )
+      in_air = player_walk.state.activity == k_walk_activity_air;
+   else if( subsytem == k_player_subsystem_skate )
+      in_air = player_skate.state.activity < k_skate_activity_ground;
+
+   grinding = (subsytem == k_player_subsystem_skate) &&
+              (player_skate.state.activity >= k_skate_activity_grind_any);
+
+   if( vg_input.display_input_method == k_input_method_controller )
+   {
+      bool press_jump = player_skate.state.jump_charge > 0.2f;
+      u8 lb_down = 0, rb_down = 0;
+      vg_exec_input_program( k_vg_input_type_button_u8, 
+            (vg_input_op[]){
+               vg_joy_button, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, vg_end 
+            }, &rb_down );
+      vg_exec_input_program( k_vg_input_type_button_u8, 
+            (vg_input_op[]){
+               vg_joy_button, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, vg_end 
+            }, &lb_down );
+      f32 lt_amt = 0.0f, rt_amt = 0.0f;
+      vg_exec_input_program( k_vg_input_type_axis_f32,
+            (vg_input_op[]){ vg_joy_axis, SDL_CONTROLLER_AXIS_TRIGGERLEFT, vg_end },
+            &lt_amt );
+      vg_exec_input_program( k_vg_input_type_axis_f32,
+            (vg_input_op[]){ vg_joy_axis, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, vg_end },
+            &rt_amt );
+
+      /* joystick L */
+      v2f steer;
+      joystick_state( k_srjoystick_steer, steer );
+
+      mmdl[3][0] =   -r + 0.375f;
+      mmdl[3][2] = 1.0f - 0.375f;
+      shader_model_menu_uMdl( mmdl );
+
+      if( subsytem == k_player_subsystem_skate )
+      {
+         colorize( 0, 1 );
+         mdl_draw_submesh( control_overlay.m_ls_circ_skate );
+
+         colorize( steer[1]>=0.5f, press_jump );
+         mdl_draw_submesh( control_overlay.m_ls_circ_backflip );
+         colorize( steer[1]<=-0.5f, press_jump );
+         mdl_draw_submesh( control_overlay.m_ls_circ_frontflip );
+
+         colorize( steer[1] > 0.7f, !press_jump && !in_air );
+         mdl_draw_submesh( control_overlay.m_ls_circ_manual );
+      }
+      else if( subsytem == k_player_subsystem_walk )
+      {
+         colorize( 0, 1 );
+         mdl_draw_submesh( control_overlay.m_ls_circ_walk );
+      }
+
+      mmdl[3][0] += steer[0]*0.125f*0.75f;
+      mmdl[3][2] += steer[1]*0.125f*0.75f;
+
+      colorize( 0, 1 );
+      shader_model_menu_uMdl( mmdl );
+      mdl_draw_submesh( control_overlay.m_ls );
+
+      /* joystick R */
+      mmdl[3][0] =    r - 0.375f;
+      mmdl[3][2] = 1.0f - 0.375f;
+      shader_model_menu_uMdl( mmdl );
+
+      if( subsytem == k_player_subsystem_skate )
+      {
+         colorize( rt_amt > 0.5f, in_air );
+         mdl_draw_submesh( control_overlay.m_rs_circ_grab );
+         colorize( 0, in_air );
+      }
+      else if( subsytem == k_player_subsystem_walk )
+      {
+         colorize( 0, 1 );
+         mdl_draw_submesh( control_overlay.m_rs_circ_look );
+      }
+
+      v2f jlook;
+      joystick_state( k_srjoystick_look, jlook );
+
+      mmdl[3][0] += jlook[0]*0.125f*0.75f;
+      mmdl[3][2] += jlook[1]*0.125f*0.75f;
+      shader_model_menu_uMdl( mmdl );
+      mdl_draw_submesh( control_overlay.m_rs );
+
+
+
+      /* LEFT UPPERS */
+      mmdl[3][0] =    -r;
+      mmdl[3][2] = -1.0f;
+      shader_model_menu_uMdl( mmdl );
+
+      /* LB -------------------------------------------------------------- */
+
+      if( subsytem == k_player_subsystem_skate )
+      {
+         colorize( lb_down, !in_air );
+         mdl_draw_submesh( control_overlay.m_carve_l );
+      }
+      else
+         colorize( 0, 0 );
+
+      mdl_draw_submesh( lb_down? control_overlay.m_lb_down: control_overlay.m_lb );
+
+      /* LT ---------------------------------------------------------------- */
+
+      if( subsytem == k_player_subsystem_skate )
+      {
+         colorize( 0, 0 );
+      }
+      else if( subsytem == k_player_subsystem_walk )
+      {
+         colorize( lt_amt>0.2f, 1 );
+         mdl_draw_submesh( control_overlay.m_lt_run );
+      }
+
+      mdl_draw_submesh( control_overlay.m_lt );
+
+      mmdl[3][2] += lt_amt*0.125f*0.5f;
+      shader_model_menu_uMdl( mmdl );
+      mdl_draw_submesh( control_overlay.m_lt_act );
+
+      /* RIGHT UPPERS */
+      mmdl[3][0] =     r;
+      mmdl[3][2] = -1.0f;
+      shader_model_menu_uMdl( mmdl );
+
+      if( subsytem == k_player_subsystem_skate )
+      {
+         colorize( rb_down, !in_air );
+         mdl_draw_submesh( control_overlay.m_carve_r );
+      }
+      else
+         colorize( 0, 0 );
+
+      mdl_draw_submesh( rb_down? control_overlay.m_rb_down: control_overlay.m_rb );
+
+      /* RT ---------------------------------------------------------------- */
+
+      if( subsytem == k_player_subsystem_skate )
+      {
+         colorize( rt_amt>0.2f, in_air );
+         mdl_draw_submesh( control_overlay.m_rt_grab );
+         colorize( rt_amt>0.2f, !in_air );
+         mdl_draw_submesh( control_overlay.m_rt_crouch );
+         colorize( rt_amt>0.2f, 1 );
+      }
+      else if( subsytem == k_player_subsystem_walk )
+      {
+         colorize( 0, 0 );
+      }
+
+      mdl_draw_submesh( control_overlay.m_rt );
+
+      mmdl[3][2] += rt_amt*0.125f*0.5f;
+      shader_model_menu_uMdl( mmdl );
+      mdl_draw_submesh( control_overlay.m_rt_act );
+
+      /* RIGHT SIDE BUTTONS */
+      bool press_a = 0, press_b = 0, press_x = 0, press_y = 0,
+           press_dpad_w = 0, press_dpad_e = 0, press_dpad_n = 0, press_dpad_s = 0,
+           press_menu = 0, press_back = 0;
+
+      vg_exec_input_program( k_vg_input_type_button_u8, 
+            (vg_input_op[]){
+               vg_joy_button, SDL_CONTROLLER_BUTTON_A, vg_end }, &press_a );
+      vg_exec_input_program( k_vg_input_type_button_u8, 
+            (vg_input_op[]){
+               vg_joy_button, SDL_CONTROLLER_BUTTON_B, vg_end }, &press_b );
+      vg_exec_input_program( k_vg_input_type_button_u8, 
+            (vg_input_op[]){
+               vg_joy_button, SDL_CONTROLLER_BUTTON_X, vg_end }, &press_x );
+      vg_exec_input_program( k_vg_input_type_button_u8, 
+            (vg_input_op[]){
+               vg_joy_button, SDL_CONTROLLER_BUTTON_Y, vg_end }, &press_y );
+      vg_exec_input_program( k_vg_input_type_button_u8, 
+            (vg_input_op[]){
+               vg_joy_button, SDL_CONTROLLER_BUTTON_DPAD_LEFT, vg_end }, &press_dpad_w );
+      vg_exec_input_program( k_vg_input_type_button_u8, 
+            (vg_input_op[]){
+               vg_joy_button, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, vg_end }, &press_dpad_e );
+      vg_exec_input_program( k_vg_input_type_button_u8, 
+            (vg_input_op[]){
+               vg_joy_button, SDL_CONTROLLER_BUTTON_DPAD_UP, vg_end }, &press_dpad_n );
+      vg_exec_input_program( k_vg_input_type_button_u8, 
+            (vg_input_op[]){
+               vg_joy_button, SDL_CONTROLLER_BUTTON_DPAD_DOWN, vg_end }, &press_dpad_s );
+      vg_exec_input_program( k_vg_input_type_button_u8, 
+            (vg_input_op[]){
+               vg_joy_button, SDL_CONTROLLER_BUTTON_BACK, vg_end }, &press_back );
+      vg_exec_input_program( k_vg_input_type_button_u8, 
+            (vg_input_op[]){
+               vg_joy_button, SDL_CONTROLLER_BUTTON_START, vg_end }, &press_menu );
+
+      mmdl[3][0] =    r;
+      mmdl[3][2] = 0.0f;
+      shader_model_menu_uMdl( mmdl );
+
+      /* B / KICKFLIP / PUSH */
+      if( subsytem == k_player_subsystem_skate )
+      {
+         colorize( press_b, !in_air );
+         mdl_draw_submesh( control_overlay.m_text_b_push );
+         colorize( press_b,  in_air );
+         mdl_draw_submesh( control_overlay.m_text_b_kickflip );
+         colorize( press_b, 1 );
+      }
+      else
+      {
+         colorize( 0, 0 );
+      }
+      mdl_draw_submesh( press_b? control_overlay.m_b_down: control_overlay.m_b );
+
+      /* Y / SKATE / WALK / GLIDE */
+
+      if( subsytem == k_player_subsystem_skate )
+      {
+         if( localplayer.have_glider )
+         {
+            colorize( press_y, !in_air );
+            mdl_draw_submesh( control_overlay.m_text_y_glide );
+            colorize( press_y, in_air );
+            mdl_draw_submesh( control_overlay.m_text_y_walk_lwr );
+         }
+         else
+         {
+            colorize( press_y, 1 );
+            mdl_draw_submesh( control_overlay.m_text_y_walk );
+         }
+      }
+      else if( subsytem == k_player_subsystem_walk )
+      {
+         colorize( press_y, player_walk.state.activity < k_walk_activity_inone );
+         mdl_draw_submesh( control_overlay.m_text_y_skate );
+      }
+      else if( subsytem == k_player_subsystem_glide )
+      {
+         colorize( press_y, 1 );
+         mdl_draw_submesh( control_overlay.m_text_y_skate );
+      }
+      else
+         colorize( 0, 0 );
+      mdl_draw_submesh( press_y? control_overlay.m_y_down: control_overlay.m_y );
+
+      /* X / TREFLIP */
+      if( subsytem == k_player_subsystem_skate )
+      {
+         colorize( press_x, in_air );
+         mdl_draw_submesh( control_overlay.m_text_x_treflip );
+      }
+      else
+         colorize( press_x, 0 );
+      mdl_draw_submesh( press_x? control_overlay.m_x_down: control_overlay.m_x );
+
+      /* A / JUMP / SHUVIT */
+      if( subsytem == k_player_subsystem_skate )
+      {
+         colorize( press_a, !in_air );
+         mdl_draw_submesh( control_overlay.m_text_a_jump );
+         colorize( press_a,  in_air );
+         mdl_draw_submesh( control_overlay.m_text_a_shuvit );
+         colorize( press_a, 1 );
+      }
+      else if( subsytem == k_player_subsystem_walk )
+      {
+         colorize( press_a, !in_air );
+         mdl_draw_submesh( control_overlay.m_text_a_jump_mid );
+      }
+      mdl_draw_submesh( press_a? control_overlay.m_a_down: control_overlay.m_a );
+
+      /* JUMP CHARGE */
+      if( subsytem == k_player_subsystem_skate )
+      {
+         if( player_skate.state.jump_charge > 0.01f )
+         {
+            mmdl[0][0] = player_skate.state.jump_charge * 0.465193f;
+            mmdl[3][0] += -0.4375f;
+            mmdl[3][2] +=  0.09375f;
+            shader_model_menu_uMdl( mmdl );
+            mdl_draw_submesh( control_overlay.m_jump_ind );
+            mmdl[0][0] = 1.0f;
+         }
+      }
+
+
+      /* DPAD --------------------------------------------------- */
+      
+      mmdl[3][0] =   -r;
+      mmdl[3][2] = 0.0f;
+      shader_model_menu_uMdl( mmdl );
+      colorize( 0, 1 );
+      mdl_draw_submesh( control_overlay.m_dpad );
+      
+      colorize( press_dpad_e, 1 );
+      mdl_draw_submesh( control_overlay.m_text_de_camera );
+      if( press_dpad_e )
+         mdl_draw_submesh( control_overlay.m_dpad_e );
+
+      colorize( press_dpad_w, 1 );
+      mdl_draw_submesh( control_overlay.m_text_dw_rewind );
+      if( press_dpad_w )
+         mdl_draw_submesh( control_overlay.m_dpad_w );
+
+      if( subsytem == k_player_subsystem_dead )
+      {
+         colorize( press_dpad_n, 1 );
+         mdl_draw_submesh( control_overlay.m_text_dn_respawn );
+      }
+      else colorize( press_dpad_n, 0 );
+      if( press_dpad_n )
+         mdl_draw_submesh( control_overlay.m_dpad_n );
+
+      colorize( press_dpad_s, 0 );
+      if( press_dpad_s )
+         mdl_draw_submesh( control_overlay.m_dpad_s );
+
+      
+      /* WEIGHT */
+      if( subsytem == k_player_subsystem_skate )
+      {
+         /* stored indicator text */
+         mmdl[3][0] = r -0.842671f;
+         mmdl[3][2] = -1.0f + 0.435484f;
+         colorize( 0, !in_air );
+         shader_model_menu_uMdl( mmdl );
+         mdl_draw_submesh( control_overlay.m_text_stored );
+      
+         mmdl[0][0] = v3_length( player_skate.state.throw_v ) / k_mmthrow_scale;
+         shader_model_menu_uMdl( mmdl );
+         colorize( 0, !in_air );
+         mdl_draw_submesh( control_overlay.m_stored_ind );
+
+         static f32 collect = 0.0f;
+         collect = vg_lerpf( collect, player_skate.collect_feedback, 
+                             vg.time_frame_delta * 15.0f );
+         collect = vg_clampf( collect, 0.0f, 1.0f );
+
+         mmdl[0][0] = collect;
+         mmdl[3][2] += 0.015625f;
+         shader_model_menu_uMdl( mmdl );
+         mdl_draw_submesh( control_overlay.m_stored_ind );
+      }
+
+      mmdl[0][0] =  1.0f;
+      mmdl[3][0] =  0.0f;
+      mmdl[3][2] = -1.0f;
+      shader_model_menu_uMdl( mmdl );
+      colorize( press_menu, 1 );
+      mdl_draw_submesh( press_menu? control_overlay.m_met_r_down: control_overlay.m_met_r );
+      mdl_draw_submesh( control_overlay.m_text_met_menu );
+
+      colorize( press_back, 0 );
+      mdl_draw_submesh( press_back? control_overlay.m_met_l_down: control_overlay.m_met_l );
+
+      colorize( 0, 0 );
+      mdl_draw_submesh( control_overlay.m_met );
+   }
+   else 
+   {
+      static v2f gd;
+      v2_lerp( gd, player_skate.state.grab_mouse_delta, vg.time_frame_delta*20.0f, gd );
+
+      /* CTRL  ||  CARVE */
+      if( subsytem == k_player_subsystem_skate )
+      {
+         bool press_ctrl = vg_getkey(SDLK_LCTRL);
+         mmdl[3][0] = -r + 0.25f;
+         mmdl[3][2] = 1.0f - 0.125f;
+         shader_model_menu_uMdl( mmdl );
+         colorize( press_ctrl, !in_air && !grinding );
+         draw_key( press_ctrl, 1 );
+         mdl_draw_submesh( control_overlay.m_text_carve );
+      }
+
+      /* SHIFT  ||  CROUCH / GRAB / RUN */
+      bool press_shift = vg_getkey( SDLK_LSHIFT );
+      if( subsytem == k_player_subsystem_skate ||
+          subsytem == k_player_subsystem_walk )
+      {
+         mmdl[3][0] = -r + 0.25f;
+         mmdl[3][2] = 1.0f - 0.125f - 0.25f;
+         shader_model_menu_uMdl( mmdl );
+         colorize( press_shift, !grinding );
+         draw_key( press_shift, 1 );
+         mdl_draw_submesh( control_overlay.m_text_shift );
+
+         if( subsytem == k_player_subsystem_skate )
+         {
+            colorize( press_shift, !in_air && !grinding );
+            mdl_draw_submesh( control_overlay.m_text_crouch );
+            colorize( press_shift,  in_air && !grinding );
+            mdl_draw_submesh( control_overlay.m_text_grab );
+         }
+         else if( subsytem == k_player_subsystem_walk )
+         {
+            mdl_draw_submesh( control_overlay.m_text_run );
+         }
+      }
+
+      if( subsytem == k_player_subsystem_skate )
+      {
+         /* stored indicator text */
+         mmdl[3][0] = -r + 0.25f + 0.203125f + 0.007812f;
+         colorize( 0, !in_air );
+         shader_model_menu_uMdl( mmdl );
+         mdl_draw_submesh( control_overlay.m_text_stored );
+      
+         mmdl[0][0] = v3_length( player_skate.state.throw_v ) / k_mmthrow_scale;
+         shader_model_menu_uMdl( mmdl );
+         colorize( 0, !in_air );
+         mdl_draw_submesh( control_overlay.m_stored_ind );
+
+         static f32 collect = 0.0f;
+         collect = vg_lerpf( collect, player_skate.collect_feedback, 
+                             vg.time_frame_delta * 15.0f );
+         collect = vg_clampf( collect, 0.0f, 1.0f );
+
+         mmdl[0][0] = collect;
+         mmdl[3][2] += 0.015625f;
+         shader_model_menu_uMdl( mmdl );
+         mdl_draw_submesh( control_overlay.m_stored_ind );
+      }
+
+      /* -1 */
+      if( subsytem != k_player_subsystem_dead )
+      {
+         bool press_c =  vg_getkey(SDLK_c);
+         mmdl[0][0] = 1.0f;
+         mmdl[3][0] = -r + 0.125f + 1.0f;
+         mmdl[3][2] = 1.0f - 0.125f - 0.25f;
+         shader_model_menu_uMdl( mmdl );
+         colorize( press_c, 1 );
+         draw_key( press_c, 0 );
+         mdl_draw_submesh( control_overlay.m_text_camera );
+      }
+
+      /* +0 */
+      mmdl[0][0] = 1.0f;
+      mmdl[3][2] = 1.0f - 0.125f - 0.25f - 0.25f;
+
+      /* A  ||  LEFT */
+      if( subsytem != k_player_subsystem_dead )
+      {
+         bool press_a =  vg_getkey(SDLK_a);
+         mmdl[3][0] = -r + 0.125f;
+         shader_model_menu_uMdl( mmdl );
+         colorize( press_a, 1 );
+         draw_key( press_a, 0 );
+         mdl_draw_submesh( control_overlay.m_text_left );
+      }
+
+      bool press_jump = player_skate.state.jump_charge < 0.2f;
+
+      /* S  ||  MANUAL / BACKFLIP */
+      bool press_s =  vg_getkey(SDLK_s);
+      mmdl[3][0] = -r + 0.125f + 0.25f;
+      shader_model_menu_uMdl( mmdl );
+
+      if( subsytem == k_player_subsystem_skate )
+      {
+         colorize( press_s, !in_air );
+         draw_key( press_s, 0 );
+         mdl_draw_submesh( control_overlay.m_text_s );
+         /* backflip/manual */
+         colorize( press_s, !in_air && !press_jump );
+         mdl_draw_submesh( control_overlay.m_text_back_flip );
+         colorize( press_s, !in_air &&  press_jump );
+         mdl_draw_submesh( control_overlay.m_text_manual );
+      }
+      else if( subsytem != k_player_subsystem_dead )
+      {
+         colorize( press_s, 1 );
+         draw_key( press_s, 0 );
+         mdl_draw_submesh( control_overlay.m_text_s );
+         mdl_draw_submesh( control_overlay.m_text_back );
+      }
+
+      /* D  ||  RIGHT */
+      if( subsytem != k_player_subsystem_dead )
+      {
+         bool press_d = vg_getkey(SDLK_d);
+         mmdl[3][0] = -r + 0.125f + 0.25f + 0.25f;
+         shader_model_menu_uMdl( mmdl );
+         colorize( press_d, 1 );
+         draw_key( press_d, 0 );
+         mdl_draw_submesh( control_overlay.m_text_right );
+      }
+
+      /* +1 */
+      mmdl[3][2] = 1.0f - 0.125f - 0.25f - 0.25f - 0.25f;
+
+      /* Q */
+      if( subsytem == k_player_subsystem_dead )
+      {
+         bool press_q = vg_getkey(SDLK_q);
+         mmdl[3][0] = -r + 0.125f;
+         shader_model_menu_uMdl( mmdl );
+         colorize( press_q, 1 );
+         draw_key( press_q, 0 );
+         mdl_draw_submesh( control_overlay.m_text_respawn );
+      }
+
+      /* W  ||  PUSH / FRONTFLIP */
+      bool press_w = vg_getkey(SDLK_w);
+      mmdl[3][0] = -r + 0.125f + 0.25f;
+      shader_model_menu_uMdl( mmdl );
+
+      if( subsytem == k_player_subsystem_skate )
+      {
+         colorize( press_w, !in_air );
+         draw_key( press_w, 0 );
+         mdl_draw_submesh( control_overlay.m_text_w );
+         /* frontflip/push */
+         colorize( press_w, !in_air && !press_jump );
+         mdl_draw_submesh( control_overlay.m_text_front_flip );
+         colorize( press_w, !in_air &&  press_jump );
+         mdl_draw_submesh( control_overlay.m_text_push );
+      }
+      else if( subsytem != k_player_subsystem_dead )
+      {
+         colorize( press_w, 1 );
+         draw_key( press_w, 0 );
+         mdl_draw_submesh( control_overlay.m_text_w );
+         mdl_draw_submesh( control_overlay.m_text_forward );
+      }
+
+      /* E */
+      bool press_e = vg_getkey(SDLK_e);
+      mmdl[3][0] = -r + 0.125f + 0.25f + 0.25f;
+
+      shader_model_menu_uMdl( mmdl );
+
+      if( subsytem == k_player_subsystem_skate )
+      {
+         if( localplayer.have_glider )
+         {
+            colorize( press_e, !in_air );
+            mdl_draw_submesh( control_overlay.m_text_walk_lwr );
+
+            colorize( press_e, in_air );
+            mdl_draw_submesh( control_overlay.m_text_glide );
+         }
+         else
+         {
+            colorize( press_e, 1 );
+            mdl_draw_submesh( control_overlay.m_text_walk );
+         }
+      }
+      else if( subsytem == k_player_subsystem_glide )
+      {
+         colorize( press_e, 1 );
+         mdl_draw_submesh( control_overlay.m_text_skate );
+      }
+      else if( subsytem == k_player_subsystem_walk )
+      {
+         colorize( press_e, player_walk.state.activity < k_walk_activity_inone );
+         mdl_draw_submesh( control_overlay.m_text_skate );
+      }
+
+      if( subsytem != k_player_subsystem_dead )
+      {
+         draw_key( press_e, 0 );
+         mdl_draw_submesh( control_overlay.m_text_e );
+      }
+
+      /* R */
+      bool press_r = vg_getkey(SDLK_r);
+      mmdl[3][0] = -r + 0.125f + 0.25f + 0.25f + 0.25f;
+      shader_model_menu_uColour( cnorm );
+      shader_model_menu_uMdl( mmdl );
+
+      colorize( press_r, 1 );
+      draw_key( press_r, 0 );
+      mdl_draw_submesh( control_overlay.m_text_rewind );
+
+      /* space */
+      bool press_space = vg_getkey(SDLK_SPACE);
+      mmdl[3][0] = 0.0f;
+      mmdl[3][2] = 1.0f - 0.125f;
+
+
+      if( subsytem == k_player_subsystem_skate ||
+          subsytem == k_player_subsystem_walk )
+      {
+         shader_model_menu_uMdl( mmdl );
+         colorize( press_space, !in_air );
+
+         mdl_draw_submesh( press_space?
+               control_overlay.m_space_down: control_overlay.m_space );
+         mdl_draw_submesh( control_overlay.m_text_jump );
+      }
+
+      if( subsytem == k_player_subsystem_skate )
+      {
+         if( player_skate.state.jump_charge > 0.01f )
+         {
+            mmdl[0][0] = player_skate.state.jump_charge;
+            mmdl[3][0] = -0.4375f;
+            shader_model_menu_uMdl( mmdl );
+            mdl_draw_submesh( control_overlay.m_jump_ind );
+         }
+      }
+
+      bool press_esc = vg_getkey(SDLK_ESCAPE);
+      mmdl[0][0] = 1.0f;
+      mmdl[3][0] = -r + 0.125f;;
+      mmdl[3][2] = -1.0f + 0.125f;
+      shader_model_menu_uMdl( mmdl );
+      colorize( press_esc, 1 );
+      mdl_draw_submesh( control_overlay.m_text_menu );
+      mdl_draw_submesh( press_esc? control_overlay.m_key_menu_down: control_overlay.m_key_menu );
+      mmdl[3][0] = r - 0.38f;
+      mmdl[3][2] = 0.0f;
+      shader_model_menu_uMdl( mmdl );
+      colorize( press_shift, in_air );
+
+      if( subsytem == k_player_subsystem_skate )
+      {
+         mdl_draw_submesh( control_overlay.m_mouse_grabs );
+
+         if( in_air && press_shift )
+         {
+            mmdl[3][0] += gd[0]*0.125f;
+            mmdl[3][2] += gd[1]*0.125f;
+         }
+      }
+
+      shader_model_menu_uMdl( mmdl );
+
+      bool lmb = button_press( k_srbind_trick0 ),
+           rmb = button_press( k_srbind_trick1 );
+
+      if( subsytem == k_player_subsystem_skate )
+      {
+         colorize( 0, press_space || in_air );
+         mdl_draw_submesh( control_overlay.m_mouse );
+
+         colorize( lmb&&!rmb, press_space || in_air );
+         mdl_draw_submesh( control_overlay.m_text_shuvit );
+
+         colorize( lmb, press_space || in_air );
+         mdl_draw_submesh( lmb? control_overlay.m_lmb_down: control_overlay.m_lmb );
+
+         colorize( rmb&&!lmb, press_space || in_air );
+         mdl_draw_submesh( control_overlay.m_text_kickflip );
+
+         colorize( rmb, press_space || in_air );
+         mdl_draw_submesh( rmb? control_overlay.m_rmb_down: control_overlay.m_rmb );
+
+         colorize( rmb&&lmb, press_space || in_air );
+         mdl_draw_submesh( control_overlay.m_text_treflip );
+      }
+      else if( subsytem == k_player_subsystem_walk )
+      {
+         colorize( 0, 1 );
+         mdl_draw_submesh( control_overlay.m_mouse );
+         mdl_draw_submesh( control_overlay.m_text_look );
+
+         mdl_draw_submesh( lmb? control_overlay.m_lmb_down: control_overlay.m_lmb );
+         mdl_draw_submesh( rmb? control_overlay.m_rmb_down: control_overlay.m_rmb );
+      }
+   }
+}