diff --git a/Content.Client/Options/UI/Tabs/GraphicsTab.xaml b/Content.Client/Options/UI/Tabs/GraphicsTab.xaml index 118b85b87b..ec1b9aa002 100644 --- a/Content.Client/Options/UI/Tabs/GraphicsTab.xaml +++ b/Content.Client/Options/UI/Tabs/GraphicsTab.xaml @@ -36,6 +36,9 @@ + diff --git a/Content.Client/Options/UI/Tabs/GraphicsTab.xaml.cs b/Content.Client/Options/UI/Tabs/GraphicsTab.xaml.cs index 3113e644ba..a22adf3e63 100644 --- a/Content.Client/Options/UI/Tabs/GraphicsTab.xaml.cs +++ b/Content.Client/Options/UI/Tabs/GraphicsTab.xaml.cs @@ -67,6 +67,12 @@ namespace Content.Client.Options.UI.Tabs UpdateApplyButton(); }; + ViewportVerticalFitCheckBox.OnToggled += _ => + { + UpdateViewportScale(); + UpdateApplyButton(); + }; + IntegerScalingCheckBox.OnToggled += OnCheckBoxToggled; ViewportLowResCheckBox.OnToggled += OnCheckBoxToggled; ParallaxLowQualityCheckBox.OnToggled += OnCheckBoxToggled; @@ -79,6 +85,7 @@ namespace Content.Client.Options.UI.Tabs ViewportScaleSlider.Value = _cfg.GetCVar(CCVars.ViewportFixedScaleFactor); ViewportStretchCheckBox.Pressed = _cfg.GetCVar(CCVars.ViewportStretch); IntegerScalingCheckBox.Pressed = _cfg.GetCVar(CCVars.ViewportSnapToleranceMargin) != 0; + ViewportVerticalFitCheckBox.Pressed = _cfg.GetCVar(CCVars.ViewportVerticalFit); ViewportLowResCheckBox.Pressed = !_cfg.GetCVar(CCVars.ViewportScaleRender); ParallaxLowQualityCheckBox.Pressed = _cfg.GetCVar(CCVars.ParallaxLowQuality); FpsCounterCheckBox.Pressed = _cfg.GetCVar(CCVars.HudFpsCounterVisible); @@ -111,6 +118,7 @@ namespace Content.Client.Options.UI.Tabs _cfg.SetCVar(CCVars.ViewportFixedScaleFactor, (int) ViewportScaleSlider.Value); _cfg.SetCVar(CCVars.ViewportSnapToleranceMargin, IntegerScalingCheckBox.Pressed ? CCVars.ViewportSnapToleranceMargin.DefaultValue : 0); + _cfg.SetCVar(CCVars.ViewportVerticalFit, ViewportVerticalFitCheckBox.Pressed); _cfg.SetCVar(CCVars.ViewportScaleRender, !ViewportLowResCheckBox.Pressed); _cfg.SetCVar(CCVars.ParallaxLowQuality, ParallaxLowQualityCheckBox.Pressed); _cfg.SetCVar(CCVars.HudFpsCounterVisible, FpsCounterCheckBox.Pressed); @@ -140,6 +148,7 @@ namespace Content.Client.Options.UI.Tabs var isVPStretchSame = ViewportStretchCheckBox.Pressed == _cfg.GetCVar(CCVars.ViewportStretch); var isVPScaleSame = (int) ViewportScaleSlider.Value == _cfg.GetCVar(CCVars.ViewportFixedScaleFactor); var isIntegerScalingSame = IntegerScalingCheckBox.Pressed == (_cfg.GetCVar(CCVars.ViewportSnapToleranceMargin) != 0); + var isVPVerticalFitSame = ViewportVerticalFitCheckBox.Pressed == _cfg.GetCVar(CCVars.ViewportVerticalFit); var isVPResSame = ViewportLowResCheckBox.Pressed == !_cfg.GetCVar(CCVars.ViewportScaleRender); var isPLQSame = ParallaxLowQualityCheckBox.Pressed == _cfg.GetCVar(CCVars.ParallaxLowQuality); var isFpsCounterVisibleSame = FpsCounterCheckBox.Pressed == _cfg.GetCVar(CCVars.HudFpsCounterVisible); @@ -152,6 +161,7 @@ namespace Content.Client.Options.UI.Tabs isVPStretchSame && isVPScaleSame && isIntegerScalingSame && + isVPVerticalFitSame && isVPResSame && isPLQSame && isFpsCounterVisibleSame && @@ -235,6 +245,8 @@ namespace Content.Client.Options.UI.Tabs { ViewportScaleBox.Visible = !ViewportStretchCheckBox.Pressed; IntegerScalingCheckBox.Visible = ViewportStretchCheckBox.Pressed; + ViewportVerticalFitCheckBox.Visible = ViewportStretchCheckBox.Pressed; + ViewportWidthSlider.Visible = ViewportWidthSliderDisplay.Visible = !ViewportStretchCheckBox.Pressed || ViewportStretchCheckBox.Pressed && !ViewportVerticalFitCheckBox.Pressed; ViewportScaleText.Text = Loc.GetString("ui-options-vp-scale", ("scale", ViewportScaleSlider.Value)); } diff --git a/Content.Client/UserInterface/Controls/MainViewport.cs b/Content.Client/UserInterface/Controls/MainViewport.cs index e334f61572..721d750115 100644 --- a/Content.Client/UserInterface/Controls/MainViewport.cs +++ b/Content.Client/UserInterface/Controls/MainViewport.cs @@ -51,6 +51,7 @@ namespace Content.Client.UserInterface.Controls var stretch = _cfg.GetCVar(CCVars.ViewportStretch); var renderScaleUp = _cfg.GetCVar(CCVars.ViewportScaleRender); var fixedFactor = _cfg.GetCVar(CCVars.ViewportFixedScaleFactor); + var verticalFit = _cfg.GetCVar(CCVars.ViewportVerticalFit); if (stretch) { @@ -60,6 +61,7 @@ namespace Content.Client.UserInterface.Controls // Did not find a snap, enable stretching. Viewport.FixedStretchSize = null; Viewport.StretchMode = ScalingViewportStretchMode.Bilinear; + Viewport.IgnoreDimension = verticalFit ? ScalingViewportIgnoreDimension.Horizontal : ScalingViewportIgnoreDimension.None; if (renderScaleUp) { @@ -104,6 +106,8 @@ namespace Content.Client.UserInterface.Controls // where we are clipping the viewport to make it fit. var cfgToleranceClip = _cfg.GetCVar(CCVars.ViewportSnapToleranceClip); + var cfgVerticalFit = _cfg.GetCVar(CCVars.ViewportVerticalFit); + // Calculate if the viewport, when rendered at an integer scale, // is close enough to the control size to enable "snapping" to NN, // potentially cutting a tiny bit off/leaving a margin. @@ -123,7 +127,8 @@ namespace Content.Client.UserInterface.Controls // The rule for which snap fits is that at LEAST one axis needs to be in the tolerance size wise. // One axis MAY be larger but not smaller than tolerance. // Obviously if it's too small it's bad, and if it's too big on both axis we should stretch up. - if (Fits(dx) && Fits(dy) || Fits(dx) && Larger(dy) || Larger(dx) && Fits(dy)) + // Additionally, if the viewport's supposed to be vertically fit, then the horizontal scale should just be ignored where appropriate. + if ((Fits(dx) || cfgVerticalFit) && Fits(dy) || !cfgVerticalFit && Fits(dx) && Larger(dy) || Larger(dx) && Fits(dy)) { // Found snap that fits. return i; diff --git a/Content.Client/UserInterface/Systems/Viewport/ViewportUIController.cs b/Content.Client/UserInterface/Systems/Viewport/ViewportUIController.cs index d117043f42..59747b1b0f 100644 --- a/Content.Client/UserInterface/Systems/Viewport/ViewportUIController.cs +++ b/Content.Client/UserInterface/Systems/Viewport/ViewportUIController.cs @@ -25,6 +25,7 @@ public sealed class ViewportUIController : UIController _configurationManager.OnValueChanged(CCVars.ViewportMinimumWidth, _ => UpdateViewportRatio()); _configurationManager.OnValueChanged(CCVars.ViewportMaximumWidth, _ => UpdateViewportRatio()); _configurationManager.OnValueChanged(CCVars.ViewportWidth, _ => UpdateViewportRatio()); + _configurationManager.OnValueChanged(CCVars.ViewportVerticalFit, _ => UpdateViewportRatio()); var gameplayStateLoad = UIManager.GetUIController(); gameplayStateLoad.OnScreenLoad += OnScreenLoad; @@ -45,13 +46,19 @@ public sealed class ViewportUIController : UIController var min = _configurationManager.GetCVar(CCVars.ViewportMinimumWidth); var max = _configurationManager.GetCVar(CCVars.ViewportMaximumWidth); var width = _configurationManager.GetCVar(CCVars.ViewportWidth); + var verticalfit = _configurationManager.GetCVar(CCVars.ViewportVerticalFit) && _configurationManager.GetCVar(CCVars.ViewportStretch); - if (width < min || width > max) + if (verticalfit) + { + width = max; + } + else if (width < min || width > max) { width = CCVars.ViewportWidth.DefaultValue; } Viewport.Viewport.ViewportSize = (EyeManager.PixelsPerMeter * width, EyeManager.PixelsPerMeter * ViewportHeight); + Viewport.UpdateCfg(); } public void ReloadViewport() diff --git a/Content.Client/Viewport/ScalingViewport.cs b/Content.Client/Viewport/ScalingViewport.cs index 9271e010f3..1fa8f17161 100644 --- a/Content.Client/Viewport/ScalingViewport.cs +++ b/Content.Client/Viewport/ScalingViewport.cs @@ -32,6 +32,7 @@ namespace Content.Client.Viewport private int _curRenderScale; private ScalingViewportStretchMode _stretchMode = ScalingViewportStretchMode.Bilinear; private ScalingViewportRenderScaleMode _renderScaleMode = ScalingViewportRenderScaleMode.Fixed; + private ScalingViewportIgnoreDimension _ignoreDimension = ScalingViewportIgnoreDimension.None; private int _fixedRenderScale = 1; private readonly List> _queuedScreenshots = new(); @@ -106,6 +107,17 @@ namespace Content.Client.Viewport } } + [ViewVariables(VVAccess.ReadWrite)] + public ScalingViewportIgnoreDimension IgnoreDimension + { + get => _ignoreDimension; + set + { + _ignoreDimension = value; + InvalidateViewport(); + } + } + public ScalingViewport() { IoCManager.InjectDependencies(this); @@ -178,7 +190,19 @@ namespace Content.Client.Viewport if (FixedStretchSize == null) { var (ratioX, ratioY) = ourSize / vpSize; - var ratio = Math.Min(ratioX, ratioY); + var ratio = 1f; + switch (_ignoreDimension) + { + case ScalingViewportIgnoreDimension.None: + ratio = Math.Min(ratioX, ratioY); + break; + case ScalingViewportIgnoreDimension.Vertical: + ratio = ratioX; + break; + case ScalingViewportIgnoreDimension.Horizontal: + ratio = ratioY; + break; + } var size = vpSize * ratio; // Size @@ -357,4 +381,25 @@ namespace Content.Client.Viewport /// CeilInt } + + /// + /// If the viewport is allowed to freely scale, this determines which dimensions should be ignored while fitting the viewport + /// + public enum ScalingViewportIgnoreDimension + { + /// + /// The viewport won't ignore any dimension. + /// + None = 0, + + /// + /// The viewport will ignore the horizontal dimension, and will exclusively consider the vertical dimension for scaling. + /// + Horizontal, + + /// + /// The viewport will ignore the vertical dimension, and will exclusively consider the horizontal dimension for scaling. + /// + Vertical + } } diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index 3e1d3c2b10..70a702a0c5 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -1555,6 +1555,9 @@ namespace Content.Shared.CCVar public static readonly CVarDef ViewportWidth = CVarDef.Create("viewport.width", 21, CVar.CLIENTONLY | CVar.ARCHIVE); + public static readonly CVarDef ViewportVerticalFit = + CVarDef.Create("viewport.vertical_fit", true, CVar.CLIENTONLY | CVar.ARCHIVE); + /* * UI */ diff --git a/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl b/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl index 86ba4462cf..6173977285 100644 --- a/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl +++ b/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl @@ -83,6 +83,10 @@ ui-options-vp-integer-scaling-tooltip = If this option is enabled, the viewport at specific resolutions. While this results in crisp textures, it also often means that black bars appear at the top/bottom of the screen or that part of the viewport is not visible. +ui-options-vp-vertical-fit = Vertical viewport fitting +ui-options-vp-vertical-fit-tooltip = When enabled, the main viewport will ignore the horizontal axis entirely when + fitting to your screen. If your screen is smaller than the viewport, then this + will cause the viewport to be cut off on the horizontal axis. ui-options-vp-low-res = Low-resolution viewport ui-options-parallax-low-quality = Low-quality Parallax (background) ui-options-fps-counter = Show FPS counter