Skip to content

Commit 909fa97

Browse files
committed
Add new settings look and first setup guide
1 parent e5a6451 commit 909fa97

File tree

5 files changed

+125
-33
lines changed

5 files changed

+125
-33
lines changed

include/pch.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -522,7 +522,8 @@ struct ModSettings {
522522
std::atomic_bool enableDebugOverlay = false;
523523
std::atomic<AngularVelocityFixerMode> buggyAngularVelocity = AngularVelocityFixerMode::AUTO;
524524
std::atomic_uint32_t performanceOverlay = 0;
525-
std::atomic_uint32_t performanceOverlayFrequency = 60;
525+
std::atomic_uint32_t performanceOverlayFrequency = 90;
526+
std::atomic_bool tutorialPromptShown = false;
526527

527528
CameraMode GetCameraMode() const { return cameraMode; }
528529

resources/controller_help.png

602 KB
Loading

src/hooking/settings.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ static void Settings_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry,
3232
if (sscanf(line, "BuggyAngularVelocity=%d", &i_val) == 1) { s->buggyAngularVelocity.store((AngularVelocityFixerMode)i_val); return; }
3333
if (sscanf(line, "PerformanceOverlay=%d", &i_val) == 1) { s->performanceOverlay.store(i_val); return; }
3434
if (sscanf(line, "PerformanceOverlayFrequency=%d", &i_val) == 1) { s->performanceOverlayFrequency.store(i_val); return; }
35+
if (sscanf(line, "TutorialPromptShown=%d", &i_val) == 1) { s->tutorialPromptShown.store(i_val); return; }
3536
}
3637

3738
static void Settings_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) {
@@ -50,7 +51,8 @@ static void Settings_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler,
5051
buf->appendf("EnableDebugOverlay=%d\n", (int)s.enableDebugOverlay.load());
5152
buf->appendf("BuggyAngularVelocity=%d\n", (int)s.buggyAngularVelocity.load());
5253
buf->appendf("PerformanceOverlay=%d\n", (int)s.performanceOverlay.load());
53-
buf->appendf("PerformanceOverlayFrequency=%d\n", (int)s.performanceOverlayFrequency.load());
54+
buf->appendf("PerformanceOverlayFrequency=%d\n", s.performanceOverlayFrequency.load());
55+
buf->appendf("TutorialPromptShown=%d\n", (int)s.tutorialPromptShown.load());
5456
buf->appendf("\n");
5557
}
5658

src/rendering/openxr.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ class OpenXR {
162162
};
163163
std::atomic<GameState> m_gameState{};
164164
std::atomic_bool m_isMenuOpen;
165+
std::atomic_uint8_t m_currMenuTab;
166+
std::atomic_bool m_forceTabChange;
165167

166168
// We'll manage the rumble commands priority inside controls.cpp
167169
struct RumbleParameters {

src/rendering/vulkan_imgui.cpp

Lines changed: 118 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -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;
524523
void 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

624647
void 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

901988
void RND_Renderer::ImGuiOverlay::Draw3DLayerAsBackground(VkCommandBuffer cb, VkImage srcImage, float aspectRatio, long frameIdx) {

0 commit comments

Comments
 (0)