+#ifndef MENU_H
+#define MENU_H
+
+#include "common.h"
+#include "model.h"
+#include "world_render.h"
+#include "player.h"
+
+#include "shaders/menu.h"
+
+static mdl_header *menu_model;
+static glmesh menu_glmesh;
+static v3f menu_cam_pos,
+ menu_target_cam_pos;
+static v4f menu_cam_q = { 0.0f, 0.0f, 0.0f, 1.0f },
+ menu_target_cam_q;
+static m4x3f menu_cam, menu_cam_inv;
+static float menu_opacity = 0.0f;
+
+static void menu_init(void)
+{
+ menu_model = mdl_load( "models/rs_menu.mdl" );
+
+ if( !menu_model )
+ vg_fatal_exit_loop( "No menu model" );
+
+ vg_acquire_thread_sync();
+ mdl_unpack_glmesh( menu_model, &menu_glmesh );
+ vg_release_thread_sync();
+
+ shader_menu_register();
+}
+
+static void menu_update( int enabled )
+{
+ static int enabled_last = 0;
+
+ if( enabled && !enabled_last )
+ {
+ v3_copy( player.camera[3], menu_cam_pos );
+ m3x3_q( player.camera, menu_cam_q );
+
+ if( player.phys.on_board )
+ {
+ v4f r90;
+ q_axis_angle( r90, player.phys.rb.up, VG_PIf*-0.5f );
+ q_mul( r90, player.phys.rb.q, menu_target_cam_q );
+ m4x3_mulv( player.phys.rb.to_world, (v3f){-1.0f,1.6f,0.0f},
+ menu_target_cam_pos );
+ }
+ else
+ {
+ v4f r180;
+ q_axis_angle( r180, player.phys.rb.up, VG_PIf );
+ q_mul( r180, player.phys.rb.q, menu_target_cam_q );
+ m4x3_mulv( player.phys.rb.to_world, (v3f){0.0f,1.6f,-1.0f},
+ menu_target_cam_pos );
+ }
+
+ q_normalize( menu_target_cam_q );
+ q_normalize( menu_cam_q );
+ menu_opacity = 0.0f;
+ }
+
+ if( enabled_last && !enabled )
+ {
+ m3x3_q( player.camera, menu_target_cam_q );
+ v3_copy( player.camera[3], menu_target_cam_pos );
+ }
+
+ float dt = vg.time_delta * 6.0f;
+
+ q_nlerp( menu_cam_q, menu_target_cam_q, dt, menu_cam_q );
+ v3_lerp( menu_cam_pos, menu_target_cam_pos, dt, menu_cam_pos );
+
+ q_m3x3( menu_cam_q, menu_cam );
+ v3_copy( menu_cam_pos, menu_cam[3] );
+ m4x3_invert_affine( menu_cam, menu_cam_inv );
+ menu_opacity = vg_lerpf( menu_opacity, enabled, dt );
+
+ enabled_last = enabled;
+}
+
+static void menu_render( m4x4f projection )
+{
+ glEnable(GL_BLEND);
+ glDisable(GL_DEPTH_TEST);
+ glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
+ glBlendEquation(GL_FUNC_ADD);
+
+ shader_fscolour_use();
+ shader_fscolour_uColour( (v4f){ 0.1f, 0.1f, 0.3f, menu_opacity*0.5f } );
+ render_fsquad();
+
+ glEnable( GL_DEPTH_TEST );
+ glDisable( GL_BLEND );
+
+ m4x3f mtx;
+
+ shader_menu_use();
+ shader_menu_uColour( (v4f){ 1.0f,1.0f,1.0f,1.0f} );
+ shader_menu_uTexMain( 1 );
+ vg_tex2d_bind( &tex_graffiti, 1 );
+
+ shader_menu_uPv( projection );
+ mesh_bind( &menu_glmesh );
+
+ m4x3_identity( mtx );
+ shader_menu_uMdl( mtx );
+ mesh_draw( &menu_glmesh );
+
+ for( int i=0; i<menu_model->node_count; i++ )
+ {
+ mdl_node *pnode = mdl_node_from_id( menu_model, i );
+
+ for( int j=0; j<pnode->submesh_count; j++ )
+ {
+ mdl_submesh *sm =
+ mdl_submesh_from_id( menu_model, pnode->submesh_start+j );
+
+ mdl_node_transform( pnode, mtx );
+ m4x3_mul( player.phys.rb.to_world, mtx, mtx );
+ shader_menu_uMdl( mtx );
+
+ mdl_draw_submesh( sm );
+ }
+ }
+}
+
+static void menu_free(void *_)
+{
+ mesh_free( &menu_glmesh );
+}
+
+#endif /* MENU_H */