+ void *alloc = vg_mem.rtmemory;
+
+ mdl_open( &menu.model, "models/rs_menu.mdl", alloc );
+ mdl_load_metadata_block( &menu.model, alloc );
+
+ vg_linear_clear( vg_mem.scratch );
+
+ mdl_load_array( &menu.model, &menu.items, "ent_menuitem", alloc );
+ mdl_load_array( &menu.model, &menu.markers, "ent_marker", alloc );
+ mdl_load_array( &menu.model, &menu.cameras, "ent_camera", alloc );
+
+ vg_linear_clear( vg_mem.scratch );
+
+ if( !mdl_arrcount( &menu.model.textures ) )
+ vg_fatal_error( "No texture in menu file" );
+
+ mdl_texture *tex0 = mdl_arritm( &menu.model.textures, 0 );
+ void *data = vg_linear_alloc( vg_mem.scratch, tex0->file.pack_size );
+ mdl_fread_pack_file( &menu.model, &tex0->file, data );
+
+ mdl_async_load_glmesh( &menu.model, &menu.mesh );
+ vg_tex2d_load_qoi_async( data, tex0->file.pack_size,
+ VG_TEX2D_LINEAR|VG_TEX2D_CLAMP,
+ &menu.texture );
+
+ mdl_close( &menu.model );
+ shader_model_menu_register();
+}
+
+/*
+ * Drop back a page until we're at the bottom which then we jus quit
+ */
+static void menu_back_page(void)
+{
+ vg_info( "menu_back_page()\n" );
+ menu.page_depth --;
+ if( menu.page_depth == 0 ){
+ menu.active = 0;
+ menu.page = 0xffffffff;
+ }
+ else{
+ menu.page = menu.page_stack[ menu.page_depth ].page;
+ menu.cam = menu.page_stack[ menu.page_depth ].cam;
+
+ if( menu.input_mode == k_menu_input_mode_keys )
+ menu.loc = menu.page_stack[ menu.page_depth ].loc;
+ else menu.loc = NULL;
+ }
+}
+
+/*
+ * Open page to the string identifier
+ */
+static void menu_open_page( const char *name )
+{
+ if( menu.page_depth >= MENU_STACK_SIZE )
+ vg_fatal_error( "Stack overflow\n" );
+
+ vg_info( "Try to open %s\n", name );
+
+ u32 hash = vg_strdjb2( name );
+ for( u32 i=0; i<mdl_arrcount(&menu.items); i++ ){
+ ent_menuitem *item = mdl_arritm( &menu.items, i );
+
+ if( item->type == k_ent_menuitem_type_page ){
+ if( mdl_pstreq( &menu.model, item->page.pstr_name, name, hash ) ){
+ u32 new_page = __builtin_ctz( item->groups );
+
+ if( new_page == menu.page ){
+ menu_back_page();
+ }
+ else{
+ menu.page_stack[ menu.page_depth ].page = menu.page;
+ menu.page_stack[ menu.page_depth ].cam = menu.cam;
+ menu.page_stack[ menu.page_depth ++ ].loc = menu.loc;
+ menu.page = __builtin_ctz( item->groups );
+
+ if( menu.input_mode == k_menu_input_mode_keys ){
+ if( item->page.id_entrypoint ){
+ u32 id = mdl_entity_id_id( item->page.id_entrypoint );
+ menu.loc = mdl_arritm( &menu.items, id );
+ }
+ }
+
+ if( item->page.id_viewpoint ){
+ u32 id = mdl_entity_id_id( item->page.id_viewpoint );
+ menu.cam = mdl_arritm( &menu.cameras, id );
+ }
+ vg_info( "menu page: %u (%p,%p)\n",
+ menu.page, menu.loc, menu.cam );
+ }
+ return;
+ }
+ }
+ }
+}
+
+/*
+ * activate a pressable type
+ */
+static void menu_trigger_item( ent_menuitem *item )
+{
+ if ( item->type == k_ent_menuitem_type_event_button ){
+ u32 q = item->button.pstr;
+
+ if( MDL_CONST_PSTREQ( &menu.model, q, "quit" ) ){
+ vg.window_should_close = 1;
+ }
+ else if( MDL_CONST_PSTREQ( &menu.model, q, "reset_nearest" ) ){
+ localplayer_cmd_respawn( 0, NULL );
+
+ menu.page_depth = 0;
+ menu.active = 0;
+ menu.page = 0xffffffff;
+ }
+ else if( MDL_CONST_PSTREQ( &menu.model, q, "reset_home" ) ){
+ world_static.active_world = 0;
+ world_static.active_trigger_volume_count = 0;
+ localplayer.viewable_world = world_current_instance();
+ localplayer_cmd_respawn( 1, (const char *[]){"start"} );
+
+ menu.page_depth = 0;
+ menu.active = 0;
+ menu.page = 0xffffffff;
+ }
+ else if( MDL_CONST_PSTREQ( &menu.model, q, "credits" ) ){
+ menu.credits_open = 1;
+ }
+ else if( MDL_CONST_PSTREQ( &menu.model, q, "workshop" ) ){
+ workshop_submit_command(0,NULL);
+ }
+ }
+ else if( item->type == k_ent_menuitem_type_page_button ){
+ menu_open_page( mdl_pstr( &menu.model, item->button.pstr ) );
+ }
+ else if( item->type == k_ent_menuitem_type_toggle ){
+ if( item->pi32 ){
+ *item->pi32 = *item->pi32 ^ 0x1;
+ }
+ }
+}