@@ -41,8 +41,8 @@ void SetupImGuiStyle() {
4141 style.ScrollbarRounding = 9 .0f ;
4242 style.GrabMinSize = 10 .0f ;
4343 style.GrabRounding = 4 .0f ;
44- style.TabRounding = 1 .0f ;
45- style.TabBorderSize = 0 .2f ;
44+ style.TabRounding = 4 .0f ;
45+ style.TabBorderSize = 0 .5f ;
4646 // style.TabMinWidthForCloseButton = 0.0f;
4747 style.ColorButtonPosition = ImGuiDir_Right;
4848 style.ButtonTextAlign = ImVec2 (0 .5f , 0 .5f );
@@ -113,20 +113,18 @@ RND_Renderer::ImGuiOverlay::ImGuiOverlay(VkCommandBuffer cb, VkExtent2D fbRes, V
113113 ImFontConfig fontCfg{};
114114 fontCfg.OversampleH = 8 ;
115115 fontCfg.OversampleV = 8 ;
116- fontCfg.RasterizerMultiply = 1 .0f ;
117116 fontCfg.FontDataOwnedByAtlas = false ;
118- ImFont* textFont = ImGui::GetIO ().Fonts ->AddFontFromMemoryCompressedTTF (roboto_compressed_data, roboto_compressed_size, 15 .0f , &fontCfg);
117+ ImFont* textFont = ImGui::GetIO ().Fonts ->AddFontFromMemoryCompressedTTF (roboto_compressed_data, roboto_compressed_size, 16 .0f , &fontCfg);
119118
120119 ImFontConfig iconCfg{};
121120 iconCfg.MergeMode = true ;
122- iconCfg.GlyphMinAdvanceX = 14 .0f ;
121+ iconCfg.GlyphMinAdvanceX = 16 .0f ;
123122 iconCfg.OversampleH = 8 ;
124123 iconCfg.OversampleV = 8 ;
125- iconCfg.RasterizerMultiply = 1 .0f ;
126124 iconCfg.FontDataOwnedByAtlas = false ;
127125 static const ImWchar icon_ranges[] = { ICON_MIN_KI, ICON_MAX_KI, 0 };
128126 iconCfg.GlyphRanges = icon_ranges;
129- ImFont* iconFont = ImGui::GetIO ().Fonts ->AddFontFromMemoryCompressedTTF (kenney_compressed_data, kenney_compressed_size, 12 .0f , &iconCfg);
127+ ImFont* iconFont = ImGui::GetIO ().Fonts ->AddFontFromMemoryCompressedTTF (kenney_compressed_data, kenney_compressed_size, 16 .0f , &iconCfg);
130128 if (iconFont == nullptr || textFont == nullptr ) {
131129 Log::print<ERROR>(" Failed to load custom fonts for ImGui overlay, using default font" );
132130 }
@@ -521,6 +519,7 @@ void RND_Renderer::ImGuiOverlay::Render(long frameIdx, bool renderBackground) {
521519 DrawHelpMenu ();
522520}
523521
522+ constexpr uint8_t TOTAL_TABS = 4 ;
524523void RND_Renderer::ImGuiOverlay::ProcessInputs (OpenXR::InputState& inputs) {
525524 auto & isMenuOpen = VRManager::instance ().XR ->m_isMenuOpen ;
526525 if (!isMenuOpen)
@@ -531,22 +530,46 @@ void RND_Renderer::ImGuiOverlay::ProcessInputs(OpenXR::InputState& inputs) {
531530
532531 bool backDown;
533532 bool confirmDown;
533+ bool pageLeft;
534+ bool pageRight;
534535 XrActionStateVector2f stick;
535536 if (inputs.shared .in_game ) {
536537 stick = inputs.inGame .move ;
537538 backDown = inputs.inGame .jump_cancel .currentState ;
538539 confirmDown = inputs.inGame .run_interact .currentState ;
540+ pageLeft = inputs.inGame .useLeftItem .currentState && inputs.inGame .useLeftItem .changedSinceLastSync ;
541+ pageRight = inputs.inGame .useRightItem .currentState && inputs.inGame .useRightItem .changedSinceLastSync ;
542+
539543 }
540544 else {
541545 stick = inputs.inMenu .navigate ;
542546 backDown = inputs.inMenu .back .currentState ;
543547 confirmDown = inputs.inMenu .select .currentState ;
548+ pageLeft = inputs.inMenu .leftTrigger .currentState && inputs.inMenu .leftTrigger .changedSinceLastSync ;
549+ pageRight = inputs.inMenu .rightTrigger .currentState && inputs.inMenu .rightTrigger .changedSinceLastSync ;
550+ }
551+
552+ VRManager::instance ().XR ->m_forceTabChange = false ;
553+ if (pageLeft || pageRight) {
554+ auto & currTab = VRManager::instance ().XR ->m_currMenuTab ;
555+ uint8_t prevTab = currTab;
556+ if (pageLeft) {
557+ currTab = (currTab - 1 + TOTAL_TABS) % TOTAL_TABS;
558+ }
559+ if (pageRight) {
560+ currTab = (currTab + 1 ) % TOTAL_TABS;
561+ }
562+
563+ if (prevTab != currTab) {
564+ VRManager::instance ().XR ->m_forceTabChange = true ;
565+ }
544566 }
545567
546568 // imgui wants us to only have state changes, and we also want to refiring DPAD inputs (used for moving the menu cursor) when held down
547569 constexpr float THRESHOLD_PRESS = 0 .5f ;
548570 constexpr float THRESHOLD_RELEASE = 0 .3f ;
549- constexpr double REFIRE_DELAY = 0 .4f ;
571+ constexpr double HORIZONTAL_REFIRE_DELAY = 0 .5f ;
572+ constexpr double VERTICAL_REFIRE_DELAY = 1 .0f ;
550573
551574 static bool dpadState[4 ] = { false };
552575 static double lastRefireTime[8 ] = { 0.0 }; // 0-3 Dpad, 4 B, 5 A, 6 LB, 7 RB
@@ -568,9 +591,9 @@ void RND_Renderer::ImGuiOverlay::ProcessInputs(OpenXR::InputState& inputs) {
568591 return isPressed;
569592 };
570593
571- auto applyInput = [&](ImGuiKey key, bool isPressed, int idx) {
594+ auto applyInput = [&](ImGuiKey key, bool isPressed, float refireDelay, int idx) {
572595 if (isPressed) {
573- if (currentTime - lastRefireTime[idx] >= REFIRE_DELAY || lastRefireTime[idx] == 0.0 ) {
596+ if (currentTime - lastRefireTime[idx] >= refireDelay || lastRefireTime[idx] == 0.0 ) {
574597 io.AddKeyEvent (key, true );
575598 lastRefireTime[idx] = currentTime;
576599 }
@@ -584,18 +607,18 @@ void RND_Renderer::ImGuiOverlay::ProcessInputs(OpenXR::InputState& inputs) {
584607 }
585608 };
586609
587- applyInput (ImGuiKey_GamepadDpadUp, updateDpadState (0 , stick.currentState .y , true ), 0 );
588- applyInput (ImGuiKey_GamepadDpadDown, updateDpadState (1 , stick.currentState .y , false ), 1 );
589- applyInput (ImGuiKey_GamepadDpadLeft, updateDpadState (2 , stick.currentState .x , false ), 2 );
590- applyInput (ImGuiKey_GamepadDpadRight, updateDpadState (3 , stick.currentState .x , true ), 3 );
610+ applyInput (ImGuiKey_GamepadDpadUp, updateDpadState (0 , stick.currentState .y , true ), VERTICAL_REFIRE_DELAY, 0 );
611+ applyInput (ImGuiKey_GamepadDpadDown, updateDpadState (1 , stick.currentState .y , false ), VERTICAL_REFIRE_DELAY, 1 );
612+ applyInput (ImGuiKey_GamepadDpadLeft, updateDpadState (2 , stick.currentState .x , false ), HORIZONTAL_REFIRE_DELAY, 2 );
613+ applyInput (ImGuiKey_GamepadDpadRight, updateDpadState (3 , stick.currentState .x , true ), HORIZONTAL_REFIRE_DELAY, 3 );
591614
592615 // convert B/A to ImGui gamepad face buttons
593- applyInput (ImGuiKey_GamepadFaceRight, backDown, 4 );
594- applyInput (ImGuiKey_GamepadFaceDown, confirmDown, 5 );
616+ applyInput (ImGuiKey_GamepadFaceRight, backDown, VERTICAL_REFIRE_DELAY, 4 );
617+ applyInput (ImGuiKey_GamepadFaceDown, confirmDown, VERTICAL_REFIRE_DELAY, 5 );
595618
596619 // triggers for tab switching
597- applyInput (ImGuiKey_GamepadL1, inputs. inMenu . leftTrigger . currentState , 6 );
598- applyInput (ImGuiKey_GamepadR1, inputs. inMenu . rightTrigger . currentState , 7 );
620+ applyInput (ImGuiKey_GamepadL1, pageLeft, VERTICAL_REFIRE_DELAY , 6 );
621+ applyInput (ImGuiKey_GamepadR1, pageRight, VERTICAL_REFIRE_DELAY , 7 );
599622
600623 // prevent exiting menu if a popup or field is being edited
601624 if (ImGui::IsAnyItemActive () && ImGui::IsPopupOpen (NULL , ImGuiPopupFlags_AnyPopupId + ImGuiPopupFlags_AnyPopupLevel)) {
@@ -623,12 +646,37 @@ void RND_Renderer::ImGuiOverlay::ProcessInputs(OpenXR::InputState& inputs) {
623646
624647void RND_Renderer::ImGuiOverlay::DrawHelpMenu () {
625648 auto & isMenuOpen = VRManager::instance ().XR ->m_isMenuOpen ;
649+ auto & settings = GetSettings ();
626650
627- if (ImGui::GetTime () < 10 .0f && !isMenuOpen) {
628- ImGui::SetNextWindowBgAlpha (0 .8f );
629- ImGui::SetNextWindowPos (ImVec2 (20 .0f , 20 .0f ), ImGuiCond_Always);
651+ float alphaForNotify = settings.tutorialPromptShown ? 0 .9f : 0 .98f ;
652+ float timeLimit = settings.tutorialPromptShown ? 14 .0f : 60 .0f ;
653+
654+ if (!settings.tutorialPromptShown ) {
655+ if (isMenuOpen || ImGui::GetTime () > timeLimit) {
656+ settings.tutorialPromptShown = true ;
657+ ImGui::SaveIniSettingsToDisk (" BetterVR_settings.ini" );
658+ }
659+ }
660+
661+ if (!isMenuOpen && ImGui::GetTime () < timeLimit) {
662+ ImVec2 fullWindowWidth = ImVec2 (ImGui::GetIO ().DisplaySize .x , ImGui::GetIO ().DisplaySize .y );
663+ ImGui::SetNextWindowBgAlpha (alphaForNotify);
664+ ImGui::SetNextWindowPos (fullWindowWidth * 0 .5f , ImGuiCond_Always, ImVec2 (0 .5f , 0 .5f ));
630665 if (ImGui::Begin (" HelpNotify" , nullptr , ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs)) {
631- ImGui::TextColored (ImVec4 (1.0 , 1.0 , 1.0 , 1.0 ), " Long-Press The X Button (or A button on left controller for Valve hmd) To Open BetterVR Help & Settings" );
666+ if (!settings.tutorialPromptShown ) {
667+ ImGui::Text (" First-Time Setup:" );
668+ ImGui::Separator ();
669+ ImGui::Text (" To get started, open the BetterVR menu to configure various settings and to see the controller guide." );
670+ ImGui::Text (" This is where you can adjust the camera mode, player height, and other options to suit your preferences." );
671+ ImGui::Spacing ();
672+ ImGui::Text (" You can always access this menu by holding the " ICON_KI_BUTTON_X " for 1 second." );
673+ ImGui::Text (" (Alternatively, for Valve Index Controllers, hold " ICON_KI_BUTTON_A " . For regular game controllers, hold " ICON_KI_BUTTON_START " instead)" );
674+ ImGui::Text (" " );
675+ ImGui::Text (" TO CONTINUE: Try holding the button and open the menu" );
676+ }
677+ else {
678+ ImGui::Text (" Hold " ICON_KI_BUTTON_X " (or " ICON_KI_BUTTON_A " for Valve Index Controllers or " ICON_KI_BUTTON_START " for Xbox/Playstation etc. controllers) To Open Mod Settings" );
679+ }
632680 }
633681 ImGui::End ();
634682 }
@@ -669,16 +717,21 @@ void RND_Renderer::ImGuiOverlay::DrawHelpMenu() {
669717 ImGui::SetNextWindowSize (windowWidth, ImGuiCond_Always);
670718 ImGui::PushStyleVar (ImGuiStyleVar_WindowPadding, ImVec2 (0 .0f , 0 .0f ));
671719
672- if (ImGui::Begin (" BetterVR Settings & Help##Settings" , nullptr , ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove)) {
720+ bool shouldStayOpen = true ;
721+
722+ if (ImGui::Begin (" BetterVR Settings & Help##Settings" , &shouldStayOpen, ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove)) {
673723 bool changed = false ;
674724
675725 ImGui::Indent (10 .0f );
676726 ImGui::Dummy (ImVec2 (10 .0f , 0 .0f ));
677727
678728 float footerHeight = ImGui::GetFrameHeight () * 1 .5f ;
679729 if (ImGui::BeginChild (" Content" , ImVec2 (0 , -footerHeight), ImGuiChildFlags_None, ImGuiWindowFlags_NoBackground)) {
730+ bool setTab = VRManager::instance ().XR ->m_forceTabChange ;
731+ uint8_t selectedTab = VRManager::instance ().XR ->m_currMenuTab ;
732+
680733 if (ImGui::BeginTabBar (" HelpMenuTabs" )) {
681- if (ImGui::BeginTabItem (" Settings" , nullptr , 0 )) {
734+ if (ImGui::BeginTabItem (ICON_KI_COG " Settings" , nullptr , (setTab && selectedTab == 0 ) ? ImGuiTabItemFlags_SetSelected : 0 )) {
682735 auto & settings = GetSettings ();
683736
684737 ImGui::Separator ();
@@ -751,7 +804,7 @@ void RND_Renderer::ImGuiOverlay::DrawHelpMenu() {
751804 });
752805
753806 bool blackBars = settings.useBlackBarsForCutscenes ;
754- DrawSettingRow (" Cinematic Black Bars" , [&]() {
807+ DrawSettingRow (" Black Bars In Third-Person Cutscenes " , [&]() {
755808 if (ImGui::Checkbox (" ##BlackBars" , &blackBars)) {
756809 settings.useBlackBarsForCutscenes = blackBars ? 1 : 0 ;
757810 changed = true ;
@@ -765,7 +818,7 @@ void RND_Renderer::ImGuiOverlay::DrawHelpMenu() {
765818 ImGui::PopStyleColor ();
766819 if (cameraMode == 1 ) {
767820 bool guiFollow = settings.uiFollowsGaze ;
768- DrawSettingRow (" UI Follows View " , [&]() {
821+ DrawSettingRow (" UI Follows Where You Look " , [&]() {
769822 if (ImGui::Checkbox (" ##UIFollow" , &guiFollow)) {
770823 settings.uiFollowsGaze = guiFollow ? 1 : 0 ;
771824 changed = true ;
@@ -775,15 +828,15 @@ void RND_Renderer::ImGuiOverlay::DrawHelpMenu() {
775828
776829 if (ImGui::CollapsingHeader (" Advanced Settings" )) {
777830 bool crop16x9 = settings.cropFlatTo16x9 ;
778- DrawSettingRow (" Crop VR Image To 16:9" , [&]() {
831+ DrawSettingRow (" Crop VR Image To 16:9 For Cemu Window " , [&]() {
779832 if (ImGui::Checkbox (" ##Crop16x9" , &crop16x9)) {
780833 settings.cropFlatTo16x9 = crop16x9 ? 1 : 0 ;
781834 changed = true ;
782835 }
783836 });
784837
785838 bool debugOverlay = settings.ShowDebugOverlay ();
786- DrawSettingRow (" Show Debug Overlay " , [&]() {
839+ DrawSettingRow (" Show Debugging Overlays (for developers) " , [&]() {
787840 if (ImGui::Checkbox (" ##DebugOverlay" , &debugOverlay)) {
788841 settings.enableDebugOverlay = debugOverlay ? 1 : 0 ;
789842 changed = true ;
@@ -810,7 +863,7 @@ void RND_Renderer::ImGuiOverlay::DrawHelpMenu() {
810863 ImGui::EndTabItem ();
811864 }
812865
813- if (ImGui::BeginTabItem (" Control Scheme Guide/Help " , nullptr , 0 )) {
866+ if (ImGui::BeginTabItem (ICON_KI_INFO_CIRCLE " Help & Controller Guide" , nullptr , (setTab && selectedTab == 1 ) ? ImGuiTabItemFlags_SetSelected : 0 )) {
814867 ImGui::PushItemWidth (windowWidth.x * 0 .5f );
815868
816869 for (const auto & imagePage : m_helpImagePages) {
@@ -830,8 +883,8 @@ void RND_Renderer::ImGuiOverlay::DrawHelpMenu() {
830883 ImGui::PopItemWidth ();
831884 ImGui::EndTabItem ();
832885 }
833-
834- if (ImGui::BeginTabItem (" FPS Overlay" , nullptr , 0 )) {
886+
887+ if (ImGui::BeginTabItem (ICON_KI_PODIUM " FPS Overlay" , nullptr , (setTab && selectedTab == 2 ) ? ImGuiTabItemFlags_SetSelected : 0 )) {
835888 ImGui::Dummy (ImVec2 (0 .0f , 10 .0f ));
836889
837890 auto & settings = GetSettings ();
@@ -873,6 +926,36 @@ void RND_Renderer::ImGuiOverlay::DrawHelpMenu() {
873926 ImGui::EndTabItem ();
874927 }
875928
929+ if (ImGui::BeginTabItem (ICON_KI_HEART " Credits" , nullptr , (setTab && selectedTab == 3 ) ? ImGuiTabItemFlags_SetSelected : 0 )) {
930+ ImGui::SeparatorText (" Project Links" );
931+ ImGui::TextLinkOpenURL (ICON_KI_GITHUB " https://github.com/Crementif/BotW-BetterVR" );
932+ ImGui::Text (" " );
933+
934+ ImGui::SeparatorText (" Credits" );
935+ ImGui::Text (" Crementif: Main Developer" );
936+ ImGui::Text (" Holydh: Inputs And Gestures" );
937+ ImGui::Text (" Acudofy: Sword & Stab Analysis System" );
938+ ImGui::Text (" leoetlino: Made the BotW Decomp project, which was useful while reverse engineering" );
939+ ImGui::Text (" Exzap: Technical support and optimization help" );
940+ ImGui::Text (" Mako Marci: Made Logo, Controller Guide And Trailer" );
941+ ImGui::Text (" Tim, Mako Marci, Solarwolf07, Elliott Tate & Derra: Helped with QA testing, recording, feedback and support" );
942+ ImGui::Text (" " );
943+
944+ ImGui::SeparatorText (" Donate To Support Development" );
945+ ImGui::TextWrapped (" Hey there!" );
946+ ImGui::Text (" " );
947+ ImGui::TextWrapped (" This mod is free and open-source, but it took a lot of late nights to create." );
948+ ImGui::TextWrapped (" If you're enjoying the mod and want to vote on new features, you can donate here. Thanks!" );
949+ ImGui::TextLinkOpenURL (" https://github.com/sponsors/Crementif/" );
950+
951+ ImGui::Text (" " );
952+ ImGui::Text (" - Crementif" );
953+
954+ ImGui::Dummy (ImVec2 (0 .0f , 10 .0f ));
955+
956+ ImGui::EndTabItem ();
957+ }
958+
876959 ImGui::EndTabBar ();
877960 }
878961 ImGui::EndChild ();
@@ -896,6 +979,10 @@ void RND_Renderer::ImGuiOverlay::DrawHelpMenu() {
896979 ImGui::End ();
897980 ImGui::PopStyleVar ();
898981 }
982+
983+ if (!shouldStayOpen) {
984+ isMenuOpen = false ;
985+ }
899986}
900987
901988void RND_Renderer::ImGuiOverlay::Draw3DLayerAsBackground (VkCommandBuffer cb, VkImage srcImage, float aspectRatio, long frameIdx) {
0 commit comments