+ camera ortho;
+
+ float fl = 0.0f,
+ fr = vg.window_x,
+ fb = 0.0f,
+ ft = vg.window_y,
+ rl = 1.0f / (fr-fl),
+ tb = 1.0f / (ft-fb);
+
+ m4x4_zero( ortho.mtx.p );
+ ortho.mtx.p[0][0] = 2.0f * rl;
+ ortho.mtx.p[1][1] = 2.0f * tb;
+ ortho.mtx.p[3][0] = (fr + fl) * -rl;
+ ortho.mtx.p[3][1] = (ft + fb) * -tb;
+ ortho.mtx.p[3][3] = 1.0f;
+ m4x3_identity( ortho.transform );
+ camera_update_view( &ortho );
+ camera_finalize( &ortho );
+
+ gui.factive = vg_lerpf( gui.factive, gui.helper_count?1.0f:0.0f,
+ vg.time_delta*2.0f );
+
+ if( gui.factive > 0.01f ){
+ /* draw bottom bar */
+ glEnable(GL_BLEND);
+ glDisable(GL_DEPTH_TEST);
+ glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
+ glBlendEquation(GL_FUNC_ADD);
+
+ shader_blitcolour_use();
+ shader_blitcolour_uColour( (v4f){ 0.0f, 0.0f, 0.0f, gui.factive*0.8f } );
+ render_fsquad1();
+ }
+
+ font3d *font = &world_global.font;
+ font3d_bind( font, &ortho );
+
+ float dy = ft/0.79f,
+ scale = dy*0x1p-4f*0.75f;
+
+ m4x3f mmdl;
+ v4f q;
+ m3x3_identity( mmdl );
+ m3x3_scale( mmdl, (v3f){scale,scale,scale} );
+ v3_zero( mmdl[3] );
+
+ float pad = dy*0x1p-4f*0.125f;
+ mmdl[3][0] = pad*2.0f;
+ mmdl[3][1] = pad;
+
+ for( u32 i=0; i<gui.helper_count; i++ ){
+ struct gui_helper *helper = &gui.helpers[i];
+
+ shader_model_font_uColour( (v4f){1.0f,1.0f,1.0f,1.0f} );
+
+ struct font3d_render render;
+ font3d_begin( font, 2, &ortho, mmdl, &render );
+
+ render.u8pch = (u8*)helper->bindstr;
+ font3d_draw( &render );
+
+ const char *make_smaller = "\x02\xaf\x03 ";
+ render.u8pch = (const u8*)make_smaller;
+ font3d_draw( &render );
+
+ render.u8pch = (u8*)helper->text;
+ font3d_draw( &render );
+
+ float w = render.offset[0]+1.0f;
+ mmdl[3][0] += w*scale;
+ }
+
+ gui.helper_count = 0;
+}
+
+VG_STATIC
+void gui_helper_action( const char *bindstr, const char *text )
+{
+ if( gui.helper_count >= vg_list_size(gui.helpers) )
+ vg_fatal_error( "Too many helpers\n" );
+
+ struct gui_helper *helper = &gui.helpers[ gui.helper_count ++ ];
+ helper->bindstr = bindstr;
+ helper->text = text;