summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--soh/soh/Enhancements/resolution-editor/ResolutionEditor.cpp491
-rw-r--r--soh/soh/Enhancements/resolution-editor/ResolutionEditor.h16
-rw-r--r--soh/soh/SohGui.cpp6
-rw-r--r--soh/soh/SohMenuBar.cpp23
4 files changed, 534 insertions, 2 deletions
diff --git a/soh/soh/Enhancements/resolution-editor/ResolutionEditor.cpp b/soh/soh/Enhancements/resolution-editor/ResolutionEditor.cpp
new file mode 100644
index 000000000..11a7cd75a
--- /dev/null
+++ b/soh/soh/Enhancements/resolution-editor/ResolutionEditor.cpp
@@ -0,0 +1,491 @@
+#include "ResolutionEditor.h"
+#include <ImGui/imgui.h>
+#include <libultraship/libultraship.h>
+
+#include <soh/UIWidgets.hpp>
+#include <graphic/Fast3D/gfx_pc.h>
+
+/* Console Variables are grouped under gAdvancedResolution. (e.g. "gAdvancedResolution.Enabled")
+
+ The following cvars are used in Libultraship and can be edited here:
+ - Enabled - Turns Advanced Resolution Mode on.
+ - AspectRatioX, AspectRatioY - Aspect ratio controls. To toggle off, set either to zero.
+ - VerticalPixelCount, VerticalResolutionToggle - Resolution controls.
+ - PixelPerfectMode, IntegerScale.Factor - Pixel Perfect Mode a.k.a. integer scaling controls.
+ - IntegerScale.FitAutomatically - Automatic resizing for Pixel Perfect Mode.
+ - IntegerScale.NeverExceedBounds - Prevents manual resizing from exceeding screen bounds.
+
+ The following cvars are also implemented in LUS for niche use cases:
+ - IgnoreAspectCorrection - Stretch framebuffer to fill screen.
+ This is something of a power-user setting for niche setups that most people won't need or care about,
+ but may be useful if playing the Switch/Wii U ports on a 4:3 television.
+ - IntegerScale.ExceedBoundsBy - Offset the max screen bounds, usually by +1.
+ This isn't that useful at the moment, so it's unused here.
+*/
+
+namespace AdvancedResolutionSettings {
+enum setting { UPDATE_aspectRatioX, UPDATE_aspectRatioY, UPDATE_verticalPixelCount };
+
+const char* aspectRatioPresetLabels[] = {
+ "Off", "Custom", "Original (4:3)", "Widescreen (16:9)", "Nintendo 3DS (5:3)", "16:10 (8:5)", "Ultrawide (21:9)"
+};
+const float aspectRatioPresetsX[] = { 0.0f, 16.0f, 4.0f, 16.0f, 5.0f, 16.0f, 21.0f };
+const float aspectRatioPresetsY[] = { 0.0f, 9.0f, 3.0f, 9.0f, 3.0f, 10.0f, 9.0f };
+const int default_aspectRatio = 1; // Default combo list option
+
+const char* pixelCountPresetLabels[] = { "Custom", "Native N64 (240p)", "2x (480p)", "3x (720p)", "4x (960p)",
+ "5x (1200p)", "6x (1440p)", "Full HD (1080p)", "4K (2160p)" };
+const int pixelCountPresets[] = { 480, 240, 480, 720, 960, 1200, 1440, 1080, 2160 };
+const int default_pixelCount = 0; // Default combo list option
+
+// Resolution clamp values as hardcoded in LUS::Gui::ApplyResolutionChanges()
+const uint32_t minVerticalPixelCount = SCREEN_HEIGHT;
+const uint32_t maxVerticalPixelCount = 4320; // 18x native, or 8K TV resolution
+
+const unsigned short default_maxIntegerScaleFactor = 6; // Default size of Integer scale factor slider.
+
+enum messageType { MESSAGE_ERROR, MESSAGE_WARNING, MESSAGE_QUESTION, MESSAGE_INFO, MESSAGE_GRAY_75 };
+const ImVec4 messageColor[]{
+ { 0.85f, 0.0f, 0.0f, 1.0f }, // MESSAGE_ERROR
+ { 0.85f, 0.85f, 0.0f, 1.0f }, // MESSAGE_WARNING
+ { 0.0f, 0.85f, 0.85f, 1.0f }, // MESSAGE_QUESTION
+ { 0.0f, 0.85f, 0.55f, 1.0f }, // MESSAGE_INFO
+ { 0.75f, 0.75f, 0.75f, 1.0f } // MESSAGE_GRAY_75
+};
+const float enhancementSpacerHeight = 19.0f;
+
+void AdvancedResolutionSettingsWindow::InitElement() {
+}
+
+void AdvancedResolutionSettingsWindow::DrawElement() {
+ ImGui::SetNextWindowSize(ImVec2(497, 599), ImGuiCond_FirstUseEver);
+ if (ImGui::Begin("Advanced Resolution Settings", &mIsVisible)) {
+ // Initialise update flags.
+ bool update[3];
+ for (uint8_t i = 0; i < sizeof(update); i++)
+ update[i] = false;
+
+ // Initialise integer scale bounds.
+ short max_integerScaleFactor = default_maxIntegerScaleFactor; // default value, which may or may not get
+ // overridden depending on viewport res
+
+ short integerScale_maximumBounds = 1; // can change when window is resized
+ // This is mostly just for UX purposes, as Fit Automatically logic is part of LUS.
+ if (((float)gfx_current_game_window_viewport.width / gfx_current_game_window_viewport.height) >
+ ((float)gfx_current_dimensions.width / gfx_current_dimensions.height)) {
+ // Scale to window height
+ integerScale_maximumBounds = gfx_current_game_window_viewport.height / gfx_current_dimensions.height;
+ } else {
+ // Scale to window width
+ integerScale_maximumBounds = gfx_current_game_window_viewport.width / gfx_current_dimensions.width;
+ }
+ // Lower-clamping maximum bounds value to 1 is no-longer necessary as that's accounted for in LUS.
+ // Letting it go below 1 in this Editor will even allow for checking if screen bounds are being exceeded.
+ if (default_maxIntegerScaleFactor < integerScale_maximumBounds) {
+ max_integerScaleFactor =
+ integerScale_maximumBounds + CVarGetInteger("gAdvancedResolution.IntegerScale.ExceedBoundsBy", 0);
+ }
+
+ // Combo List defaults
+ static int item_aspectRatio = CVarGetInteger("gAdvancedResolution.UIComboItem.AspectRatio", 3);
+ static int item_pixelCount = CVarGetInteger("gAdvancedResolution.UIComboItem.PixelCount", default_pixelCount);
+ // Stored Values for non-UIWidgets elements
+ static float aspectRatioX =
+ CVarGetFloat("gAdvancedResolution.AspectRatioX", aspectRatioPresetsX[item_aspectRatio]);
+ static float aspectRatioY =
+ CVarGetFloat("gAdvancedResolution.AspectRatioY", aspectRatioPresetsY[item_aspectRatio]);
+ static int verticalPixelCount =
+ CVarGetInteger("gAdvancedResolution.VerticalPixelCount", pixelCountPresets[item_pixelCount]);
+ // Additional settings
+ static bool showHorizontalResField = false;
+ static int horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX;
+ // Disabling flags
+ const bool disabled_everything = !CVarGetInteger("gAdvancedResolution.Enabled", 0);
+ const bool disabled_pixelCount = !CVarGetInteger("gAdvancedResolution.VerticalResolutionToggle", 0);
+
+#ifdef __APPLE__
+ // Display HiDPI warning. (Remove this once we can definitively say it's fixed.)
+ ImGui::TextColored(messageColor[MESSAGE_INFO],
+ ICON_FA_INFO_CIRCLE " These settings may behave incorrectly on Retina displays.");
+ UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f);
+#endif
+
+ if (ImGui::CollapsingHeader("Original Settings", ImGuiTreeNodeFlags_DefaultOpen)) {
+ // The original resolution slider (for convenience)
+ const bool disabled_resolutionSlider = (CVarGetInteger("gAdvancedResolution.VerticalResolutionToggle", 0) &&
+ CVarGetInteger("gAdvancedResolution.Enabled", 0)) ||
+ CVarGetInteger("gLowResMode", 0);
+ if (UIWidgets::EnhancementSliderFloat("Internal Resolution: %d %%", "##IMul", "gInternalResolution", 0.5f,
+ 2.0f, "", 1.0f, true, true, disabled_resolutionSlider)) {
+ LUS::Context::GetInstance()->GetWindow()->SetResolutionMultiplier(
+ CVarGetFloat("gInternalResolution", 1));
+ }
+ UIWidgets::Tooltip("Multiplies your output resolution by the value entered.");
+
+ // The original MSAA slider (also for convenience)
+#ifndef __WIIU__
+ if (UIWidgets::PaddedEnhancementSliderInt("MSAA: %d", "##IMSAA", "gMSAAValue", 1, 8, "", 1, true, true,
+ false)) {
+ LUS::Context::GetInstance()->GetWindow()->SetMsaaLevel(CVarGetInteger("gMSAAValue", 1));
+ };
+ UIWidgets::Tooltip(
+ "Activates multi-sample anti-aliasing when above 1x, up to 8x for 8 samples for every pixel.\n\n"
+ " " ICON_FA_INFO_CIRCLE
+ " (Higher MSAA with low resolution can approximate an authentic \"real N64\" look!)");
+#endif
+
+ // N64 Mode toggle (again for convenience)
+ // UIWidgets::PaddedEnhancementCheckbox("(Enhancements>Graphics) N64 Mode", "gLowResMode", false, false, false, "", UIWidgets::CheckboxGraphics::Cross, false);
+ }
+
+ UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f);
+ // Activator
+ UIWidgets::PaddedEnhancementCheckbox("Enable advanced settings.", "gAdvancedResolution.Enabled", false, false,
+ false, "", UIWidgets::CheckboxGraphics::Cross, false);
+ // Error/Warning display
+ if (!CVarGetInteger("gLowResMode", 0)) {
+ if (IsDroppingFrames()) { // Significant frame drop warning
+ ImGui::TextColored(messageColor[MESSAGE_WARNING],
+ ICON_FA_EXCLAMATION_TRIANGLE " Significant frame rate (FPS) drops may be occuring.");
+ UIWidgets::Spacer(2);
+ } else { // No warnings
+ UIWidgets::Spacer(enhancementSpacerHeight);
+ }
+ } else { // N64 Mode warning
+ ImGui::TextColored(messageColor[MESSAGE_QUESTION],
+ ICON_FA_QUESTION_CIRCLE " \"N64 Mode\" is overriding these settings.");
+ ImGui::SameLine();
+ if (ImGui::Button("Click to disable")) {
+ CVarSetInteger("gLowResMode", 0);
+ CVarSave();
+ }
+ }
+ // Resolution visualiser
+ ImGui::Text("Viewport dimensions: %d x %d", gfx_current_game_window_viewport.width,
+ gfx_current_game_window_viewport.height);
+ ImGui::Text("Internal resolution: %d x %d", gfx_current_dimensions.width, gfx_current_dimensions.height);
+
+ UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f);
+ if (disabled_everything) { // Hide aspect ratio controls.
+ UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f);
+ }
+
+ // Aspect Ratio
+ ImGui::Text("Force aspect ratio:");
+ ImGui::SameLine();
+ ImGui::TextColored(messageColor[MESSAGE_GRAY_75], "(Select \"Off\" to disable.)");
+ // Presets
+ if (ImGui::Combo(" ", &item_aspectRatio, aspectRatioPresetLabels,
+ IM_ARRAYSIZE(aspectRatioPresetLabels)) &&
+ item_aspectRatio != default_aspectRatio) { // don't change anything if "Custom" is selected.
+ aspectRatioX = aspectRatioPresetsX[item_aspectRatio];
+ aspectRatioY = aspectRatioPresetsY[item_aspectRatio];
+
+ if (showHorizontalResField) {
+ horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX;
+ }
+
+ CVarSetFloat("gAdvancedResolution.AspectRatioX", aspectRatioX);
+ CVarSetFloat("gAdvancedResolution.AspectRatioY", aspectRatioY);
+ CVarSetInteger("gAdvancedResolution.UIComboItem.AspectRatio", item_aspectRatio);
+ CVarSave();
+ }
+ // Hide aspect ratio input fields if using one of the presets.
+ if (item_aspectRatio == default_aspectRatio && !showHorizontalResField) {
+ // Declare input interaction bools outside of IF statement to prevent Y field from disappearing.
+ const bool input_X = ImGui::InputFloat("X", &aspectRatioX, 0.1f, 1.0f, "%.3f");
+ const bool input_Y = ImGui::InputFloat("Y", &aspectRatioY, 0.1f, 1.0f, "%.3f");
+ if (input_X || input_Y) {
+ item_aspectRatio = default_aspectRatio;
+ update[UPDATE_aspectRatioX] = true;
+ update[UPDATE_aspectRatioY] = true;
+ }
+ } else if (showHorizontalResField) { // Show calculated aspect ratio
+ if (item_aspectRatio) {
+ UIWidgets::Spacer(2);
+ const float resolvedAspectRatio = (float)gfx_current_dimensions.width / gfx_current_dimensions.height;
+ ImGui::Text("Aspect ratio: %.2f:1", resolvedAspectRatio);
+ } else {
+ UIWidgets::Spacer(enhancementSpacerHeight);
+ }
+ }
+
+ if (disabled_everything) { // Hide aspect ratio controls.
+ UIWidgets::ReEnableComponent("disabledTooltipText");
+ }
+ UIWidgets::Spacer(0);
+
+ // Vertical Resolution
+ UIWidgets::PaddedEnhancementCheckbox("Set fixed vertical resolution (disables Resolution slider)",
+ "gAdvancedResolution.VerticalResolutionToggle", true, false,
+ disabled_everything, "", UIWidgets::CheckboxGraphics::Cross, false);
+ UIWidgets::Tooltip(
+ "Override the resolution scale slider and use the settings below, irrespective of window size.");
+ if (disabled_pixelCount || disabled_everything) { // Hide pixel count controls.
+ UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f);
+ }
+ if (ImGui::Combo("Pixel Count Presets", &item_pixelCount, pixelCountPresetLabels,
+ IM_ARRAYSIZE(pixelCountPresetLabels)) &&
+ item_pixelCount != default_pixelCount) { // don't change anything if "Custom" is selected.
+ verticalPixelCount = pixelCountPresets[item_pixelCount];
+
+ if (showHorizontalResField) {
+ horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX;
+ }
+
+ CVarSetInteger("gAdvancedResolution.VerticalPixelCount", verticalPixelCount);
+ CVarSetInteger("gAdvancedResolution.UIComboItem.PixelCount", item_pixelCount);
+ CVarSave();
+ }
+ // Horizontal Resolution, if visibility is enabled for it.
+ if (showHorizontalResField) {
+ // Only show the field if Aspect Ratio is being enforced.
+ if ((aspectRatioX > 0.0f) && (aspectRatioY > 0.0f)) {
+ // So basically we're "faking" this one by setting aspectRatioX instead.
+ if (ImGui::InputInt("Horiz. Pixel Count", &horizontalPixelCount, 8, 320)) {
+ item_aspectRatio = default_aspectRatio;
+ if (horizontalPixelCount < SCREEN_WIDTH) {
+ horizontalPixelCount = SCREEN_WIDTH;
+ }
+ aspectRatioX = horizontalPixelCount;
+ aspectRatioY = verticalPixelCount;
+ update[UPDATE_aspectRatioX] = true;
+ update[UPDATE_aspectRatioY] = true;
+ }
+ } else { // Display a notice instead.
+ ImGui::TextColored(messageColor[MESSAGE_QUESTION],
+ ICON_FA_QUESTION_CIRCLE " \"Force aspect ratio\" required.");
+ // ImGui::Text(" ");
+ ImGui::SameLine();
+ if (ImGui::Button("Click to resolve")) {
+ item_aspectRatio = default_aspectRatio; // Set it to Custom
+ aspectRatioX = aspectRatioPresetsX[2]; // but use the 4:3 defaults
+ aspectRatioY = aspectRatioPresetsY[2];
+ update[UPDATE_aspectRatioX] = true;
+ update[UPDATE_aspectRatioY] = true;
+ horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX;
+ }
+ }
+ }
+ // Vertical Resolution part 2
+ if (ImGui::InputInt("Vertical Pixel Count", &verticalPixelCount, 8, 240)) {
+ item_pixelCount = default_pixelCount;
+ update[UPDATE_verticalPixelCount] = true;
+
+ // Account for the natural instinct to enter horizontal first.
+ // Ignore vertical resolutions that are below the lower clamp constant.
+ if (showHorizontalResField && !(verticalPixelCount < minVerticalPixelCount)) {
+ item_aspectRatio = default_aspectRatio;
+ aspectRatioX = horizontalPixelCount;
+ aspectRatioY = verticalPixelCount;
+ update[UPDATE_aspectRatioX] = true;
+ update[UPDATE_aspectRatioY] = true;
+ }
+ }
+ if (disabled_pixelCount || disabled_everything) { // Hide pixel count controls.
+ UIWidgets::ReEnableComponent("disabledTooltipText");
+ }
+
+ UIWidgets::Spacer(0);
+
+ // Integer scaling settings group (Pixel-perfect Mode)
+ static const ImGuiTreeNodeFlags IntegerScalingResolvedImGuiFlag =
+ CVarGetInteger("gAdvancedResolution.PixelPerfectMode", 0) ? ImGuiTreeNodeFlags_DefaultOpen
+ : ImGuiTreeNodeFlags_None;
+ if (ImGui::CollapsingHeader("Integer Scaling Settings", IntegerScalingResolvedImGuiFlag)) {
+ const bool disabled_pixelPerfectMode =
+ !CVarGetInteger("gAdvancedResolution.PixelPerfectMode", 0) || disabled_everything;
+ // Pixel-perfect Mode
+ UIWidgets::PaddedEnhancementCheckbox("Pixel-perfect Mode", "gAdvancedResolution.PixelPerfectMode", true,
+ true, disabled_pixelCount || disabled_everything, "",
+ UIWidgets::CheckboxGraphics::Cross, false);
+ UIWidgets::Tooltip("Don't scale image to fill window.");
+ if (disabled_pixelCount && CVarGetInteger("gAdvancedResolution.PixelPerfectMode", 0)) {
+ CVarSetInteger("gAdvancedResolution.PixelPerfectMode", 0);
+ CVarSave();
+ }
+
+ // Integer Scaling
+ UIWidgets::EnhancementSliderInt(
+ "Integer scale factor: %d", "##ARSIntScale", "gAdvancedResolution.IntegerScale.Factor", 1,
+ max_integerScaleFactor, "%d", 1, true,
+ disabled_pixelPerfectMode || CVarGetInteger("gAdvancedResolution.IntegerScale.FitAutomatically", 0));
+ UIWidgets::Tooltip("Integer scales the image. Only available in pixel-perfect mode.");
+ // Display warning if size is being clamped or if framebuffer is larger than viewport.
+ if (!disabled_pixelPerfectMode &&
+ (CVarGetInteger("gAdvancedResolution.IntegerScale.NeverExceedBounds", 1) &&
+ CVarGetInteger("gAdvancedResolution.IntegerScale.Factor", 1) > integerScale_maximumBounds)) {
+ ImGui::SameLine();
+ ImGui::TextColored(messageColor[MESSAGE_WARNING], ICON_FA_EXCLAMATION_TRIANGLE " Window exceeded.");
+ }
+
+ UIWidgets::PaddedEnhancementCheckbox(
+ "Automatically scale image to fit viewport", "gAdvancedResolution.IntegerScale.FitAutomatically", true,
+ true, disabled_pixelPerfectMode, "", UIWidgets::CheckboxGraphics::Cross, false);
+ UIWidgets::Tooltip("Automatically sets scale factor to fit window. Only available in pixel-perfect mode.");
+ if (CVarGetInteger("gAdvancedResolution.IntegerScale.FitAutomatically", 0)) {
+ // This is just here to update the value shown on the slider.
+ // The function in LUS to handle this setting will ignore IntegerScaleFactor while active.
+ CVarSetInteger("gAdvancedResolution.IntegerScale.Factor", integerScale_maximumBounds);
+ // CVarSave();
+ }
+ } // End of integer scaling settings
+
+ UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f);
+
+ // Collapsible panel for additional settings
+ if (ImGui::CollapsingHeader("Additional Settings")) {
+ UIWidgets::Spacer(0);
+
+#if defined(__SWITCH__) || defined(__WIIU__)
+ // Disable aspect correction, stretching the framebuffer to fill the viewport.
+ // This option is only really needed on systems limited to 16:9 TV resolutions, such as consoles.
+ // The associated cvar is still functional on PC platforms if you want to use it though.
+ UIWidgets::PaddedEnhancementCheckbox("Disable aspect correction and stretch the output image.\n"
+ "(Might be useful for 4:3 televisions!)\n"
+ "Not available in Pixel Perfect Mode.",
+ "gAdvancedResolution.IgnoreAspectCorrection", false, true,
+ CVarGetInteger("gAdvancedResolution.PixelPerfectMode", 0) ||
+ disabled_everything,
+ "", UIWidgets::CheckboxGraphics::Cross, false);
+#else
+ if (CVarGetInteger("gAdvancedResolution.IgnoreAspectCorrection", 0)) {
+ // This setting is intentionally not exposed on PC platforms,
+ // but may be accidentally activated for varying reasons.
+ // Having this button should hopefully prevent support headaches.
+ ImGui::TextColored(messageColor[MESSAGE_QUESTION], ICON_FA_QUESTION_CIRCLE
+ " If the image is stretched and you don't know why, click this.");
+ if (ImGui::Button("Click to reenable aspect correction.")) {
+ CVarSetInteger("gAdvancedResolution.IgnoreAspectCorrection", 0);
+ CVarSave();
+ }
+ UIWidgets::Spacer(2);
+ }
+#endif
+
+ // A requested addition; an alternative way of displaying the resolution field.
+ if (ImGui::Checkbox("Show a horizontal resolution field, instead of aspect ratio.", &showHorizontalResField)) {
+ if (!showHorizontalResField && (aspectRatioX > 0.0f)) { // when turning this setting off
+ // Refresh relevant values
+ aspectRatioX = aspectRatioY * horizontalPixelCount / verticalPixelCount;
+ horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX;
+ } else { // when turning this setting on
+ item_aspectRatio = default_aspectRatio;
+ if (aspectRatioX > 0.0f) {
+ // Refresh relevant values in the opposite order
+ horizontalPixelCount = (verticalPixelCount / aspectRatioY) * aspectRatioX;
+ aspectRatioX = aspectRatioY * horizontalPixelCount / verticalPixelCount;
+ }
+ }
+ update[UPDATE_aspectRatioX] = true;
+ }
+
+ // Beginning of Integer Scaling additional settings.
+ {
+ // UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f);
+
+ // Integer Scaling - Never Exceed Bounds.
+ const bool disabled_neverExceedBounds =
+ !CVarGetInteger("gAdvancedResolution.PixelPerfectMode", 0) ||
+ CVarGetInteger("gAdvancedResolution.IntegerScale.FitAutomatically", 0) || disabled_everything;
+ const bool checkbox_neverExceedBounds =
+ UIWidgets::PaddedEnhancementCheckbox("Prevent integer scaling from exceeding screen bounds.\n"
+ "(Makes screen bounds take priority over specified factor.)",
+ "gAdvancedResolution.IntegerScale.NeverExceedBounds",
+ true, false, disabled_neverExceedBounds, "",
+ UIWidgets::CheckboxGraphics::Cross, true);
+ UIWidgets::Tooltip(
+ "Prevents integer scaling factor from exceeding screen bounds.\n\n"
+ "Enabled: Will clamp the scaling factor and display a gentle warning in the resolution editor.\n"
+ "Disabled: Will allow scaling to exceed screen bounds, for users who want to crop overscan.\n\n"
+ " " ICON_FA_INFO_CIRCLE
+ " Please note that exceeding screen bounds may show a scroll bar on-screen.");
+
+ // Initialise the (currently unused) "Exceed Bounds By" cvar if it's been changed.
+ if (checkbox_neverExceedBounds &&
+ CVarGetInteger("gAdvancedResolution.IntegerScale.ExceedBoundsBy", 0)) {
+ CVarSetInteger("gAdvancedResolution.IntegerScale.ExceedBoundsBy", 0);
+ CVarSave();
+ }
+
+ // Integer Scaling - Exceed Bounds By 1x/Offset.
+ // A popular feature in some retro frontends/upscalers, sometimes called "crop overscan" or "1080p 5x".
+ /*
+ UIWidgets::PaddedEnhancementCheckbox("Allow integer scale factor to go +1 above maximum screen bounds.", "gAdvancedResolution.IntegerScale.ExceedBoundsBy", false, false, !CVarGetInteger("gAdvancedResolution.PixelPerfectMode", 0) || disabled_everything, "", UIWidgets::CheckboxGraphics::Cross, false);
+ */
+ // It does actually function as expected, but exceeding the bottom of the screen shows a scroll bar.
+ // I've ended up commenting this one out because of the scroll bar, and for simplicity.
+
+ // Display an info message about the scroll bar.
+ if (!CVarGetInteger("gAdvancedResolution.IntegerScale.NeverExceedBounds", 1) ||
+ CVarGetInteger("gAdvancedResolution.IntegerScale.ExceedBoundsBy", 0)) {
+ if (disabled_neverExceedBounds) { // Dim this help text accordingly
+ UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f);
+ }
+ ImGui::TextColored(messageColor[MESSAGE_INFO],
+ " " ICON_FA_INFO_CIRCLE
+ " A scroll bar may become visible if screen bounds are exceeded.");
+ if (disabled_neverExceedBounds) { // Dim this help text accordingly
+ UIWidgets::ReEnableComponent("disabledTooltipText");
+ }
+
+ // Another support helper button, to disable the unused "Exceed Bounds By" cvar.
+ // (Remove this button if uncommenting the checkbox.)
+ if (CVarGetInteger("gAdvancedResolution.IntegerScale.ExceedBoundsBy", 0)) {
+ if (ImGui::Button("Click to reset a console variable that may be causing this.")) {
+ CVarSetInteger("gAdvancedResolution.IntegerScale.ExceedBoundsBy", 0);
+ CVarSave();
+ }
+ }
+ } else {
+ ImGui::Text(" ");
+ }
+ // UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f);
+ } // End of Integer Scaling additional settings.
+
+ } // End of additional settings
+
+ // Clamp and update the cvars that don't use UIWidgets
+ if (update[UPDATE_aspectRatioX] || update[UPDATE_aspectRatioY] || update[UPDATE_verticalPixelCount]) {
+ if (update[UPDATE_aspectRatioX]) {
+ if (aspectRatioX < 0.0f) {
+ aspectRatioX = 0.0f;
+ }
+ CVarSetFloat("gAdvancedResolution.AspectRatioX", aspectRatioX);
+ }
+ if (update[UPDATE_aspectRatioY]) {
+ if (aspectRatioY < 0.0f) {
+ aspectRatioY = 0.0f;
+ }
+ CVarSetFloat("gAdvancedResolution.AspectRatioY", aspectRatioY);
+ }
+ if (update[UPDATE_verticalPixelCount]) {
+ // There's a upper and lower clamp on the Libultraship side too,
+ // so clamping it here is entirely visual, so the vertical resolution field reflects it.
+ if (verticalPixelCount < minVerticalPixelCount) {
+ verticalPixelCount = minVerticalPixelCount;
+ }
+ if (verticalPixelCount > maxVerticalPixelCount) {
+ verticalPixelCount = maxVerticalPixelCount;
+ }
+ CVarSetInteger("gAdvancedResolution.VerticalPixelCount", verticalPixelCount);
+ }
+ CVarSetInteger("gAdvancedResolution.UIComboItem.AspectRatio", item_aspectRatio);
+ CVarSetInteger("gAdvancedResolution.UIComboItem.PixelCount", item_pixelCount);
+ CVarSave();
+ }
+ }
+ ImGui::End();
+}
+
+void AdvancedResolutionSettingsWindow::UpdateElement() {
+}
+
+bool AdvancedResolutionSettingsWindow::IsDroppingFrames() {
+ // a rather imprecise way of checking for frame drops.
+ // but it's mostly there to inform the player of large drops.
+ const short targetFPS = CVarGetInteger("gInterpolationFPS", 20);
+ const float threshold = targetFPS / 20.0f + 4.1f;
+ return ImGui::GetIO().Framerate < targetFPS - threshold;
+}
+} // namespace AdvancedResolutionSettings
diff --git a/soh/soh/Enhancements/resolution-editor/ResolutionEditor.h b/soh/soh/Enhancements/resolution-editor/ResolutionEditor.h
new file mode 100644
index 000000000..68680c824
--- /dev/null
+++ b/soh/soh/Enhancements/resolution-editor/ResolutionEditor.h
@@ -0,0 +1,16 @@
+#pragma once
+#include <libultraship/libultraship.h>
+
+namespace AdvancedResolutionSettings {
+class AdvancedResolutionSettingsWindow : public LUS::GuiWindow {
+ private:
+ bool IsDroppingFrames();
+
+ public:
+ using LUS::GuiWindow::GuiWindow;
+
+ void InitElement() override;
+ void DrawElement() override;
+ void UpdateElement() override;
+};
+} // namespace AdvancedResolutionSettings
diff --git a/soh/soh/SohGui.cpp b/soh/soh/SohGui.cpp
index 6dba67a05..ecc7dd234 100644
--- a/soh/soh/SohGui.cpp
+++ b/soh/soh/SohGui.cpp
@@ -38,6 +38,7 @@
#include "Enhancements/game-interactor/GameInteractor.h"
#include "Enhancements/cosmetics/authenticGfxPatches.h"
+#include "Enhancements/resolution-editor/ResolutionEditor.h"
bool ToggleAltAssetsAtEndOfFrame = false;
bool isBetaQuestEnabled = false;
@@ -128,6 +129,8 @@ namespace SohGui {
std::shared_ptr<ItemTrackerWindow> mItemTrackerWindow;
std::shared_ptr<RandomizerSettingsWindow> mRandomizerSettingsWindow;
+ std::shared_ptr<AdvancedResolutionSettings::AdvancedResolutionSettingsWindow> mAdvancedResolutionSettingsWindow;
+
void SetupGuiElements() {
auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui();
@@ -187,9 +190,12 @@ namespace SohGui {
gui->AddGuiWindow(mItemTrackerSettingsWindow);
mRandomizerSettingsWindow = std::make_shared<RandomizerSettingsWindow>("gRandomizerSettingsEnabled", "Randomizer Settings");
gui->AddGuiWindow(mRandomizerSettingsWindow);
+ mAdvancedResolutionSettingsWindow = std::make_shared<AdvancedResolutionSettings::AdvancedResolutionSettingsWindow>("gAdvancedResolutionEditorEnabled", "Advanced Resolution Settings");
+ gui->AddGuiWindow(mAdvancedResolutionSettingsWindow);
}
void Destroy() {
+ mAdvancedResolutionSettingsWindow = nullptr;
mRandomizerSettingsWindow = nullptr;
mItemTrackerWindow = nullptr;
mItemTrackerSettingsWindow = nullptr;
diff --git a/soh/soh/SohMenuBar.cpp b/soh/soh/SohMenuBar.cpp
index 31d19a8c1..4e488521f 100644
--- a/soh/soh/SohMenuBar.cpp
+++ b/soh/soh/SohMenuBar.cpp
@@ -30,6 +30,7 @@
#include "Enhancements/randomizer/randomizer_entrance_tracker.h"
#include "Enhancements/randomizer/randomizer_item_tracker.h"
#include "Enhancements/randomizer/randomizer_settings_window.h"
+#include "Enhancements/resolution-editor/ResolutionEditor.h"
extern bool ToggleAltAssetsAtEndOfFrame;
extern bool isBetaQuestEnabled;
@@ -177,6 +178,7 @@ void DrawShipMenu() {
extern std::shared_ptr<LUS::GuiWindow> mInputEditorWindow;
extern std::shared_ptr<GameControlEditor::GameControlEditorWindow> mGameControlEditorWindow;
+extern std::shared_ptr<AdvancedResolutionSettings::AdvancedResolutionSettingsWindow> mAdvancedResolutionSettingsWindow;
void DrawSettingsMenu() {
if (ImGui::BeginMenu("Settings"))
@@ -262,11 +264,28 @@ void DrawSettingsMenu() {
if (ImGui::BeginMenu("Graphics")) {
#ifndef __APPLE__
- if (UIWidgets::EnhancementSliderFloat("Internal Resolution: %d %%", "##IMul", "gInternalResolution", 0.5f, 2.0f, "", 1.0f, true)) {
+ const bool disabled_resolutionSlider = CVarGetInteger("gAdvancedResolution.VerticalResolutionToggle", 0) &&
+ CVarGetInteger("gAdvancedResolution.Enabled", 0);
+ if (UIWidgets::EnhancementSliderFloat("Internal Resolution: %d %%", "##IMul", "gInternalResolution", 0.5f,
+ 2.0f, "", 1.0f, true, true, disabled_resolutionSlider)) {
LUS::Context::GetInstance()->GetWindow()->SetResolutionMultiplier(CVarGetFloat("gInternalResolution", 1));
- };
+ }
UIWidgets::Tooltip("Multiplies your output resolution by the value inputted, as a more intensive but effective form of anti-aliasing");
#endif
+
+ if (mAdvancedResolutionSettingsWindow) {
+ ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(12.0f, 6.0f));
+ ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0f, 0.0f));
+ ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
+ ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.22f, 0.38f, 0.56f, 1.0f));
+ UIWidgets::Spacer(0);
+ if (ImGui::Button(GetWindowButtonText("Advanced Resolution", CVarGetInteger("gAdvancedResolutionEditorEnabled", 0)).c_str(), ImVec2(-1.0f, 0.0f))) {
+ mAdvancedResolutionSettingsWindow->ToggleVisibility();
+ }
+ ImGui::PopStyleColor(1);
+ ImGui::PopStyleVar(3);
+ }
+
#ifndef __WIIU__
if (UIWidgets::PaddedEnhancementSliderInt("MSAA: %d", "##IMSAA", "gMSAAValue", 1, 8, "", 1, true, true, false)) {
LUS::Context::GetInstance()->GetWindow()->SetMsaaLevel(CVarGetInteger("gMSAAValue", 1));