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