diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs
index 85e579ec9..66328926d 100644
--- a/Source/Editor/Editor.cs
+++ b/Source/Editor/Editor.cs
@@ -1587,7 +1587,7 @@ namespace FlaxEditor
if (dockedTo != null && dockedTo.SelectedTab != gameWin && dockedTo.SelectedTab != null)
result = dockedTo.SelectedTab.Size;
else
- result = gameWin.Viewport.Size;
+ result = gameWin.Viewport.ContentSize;
result *= root.DpiScale;
result = Float2.Round(result);
diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs
index 0088ddd71..872e78509 100644
--- a/Source/Editor/Windows/GameWindow.cs
+++ b/Source/Editor/Windows/GameWindow.cs
@@ -10,17 +10,117 @@ using FlaxEditor.Modules;
using FlaxEditor.Options;
using FlaxEngine;
using FlaxEngine.GUI;
-using FlaxEngine.Json;
namespace FlaxEditor.Windows
{
+ ///
+ /// Render output control with content scaling support.
+ ///
+ public class ScaledRenderOutputControl : RenderOutputControl
+ {
+ ///
+ /// Custom scale.
+ ///
+ public float ContentScale = 1.0f;
+
+ ///
+ /// Actual bounds size for content (incl. scale).
+ ///
+ public Float2 ContentSize => Size / ContentScale;
+
+ ///
+ public ScaledRenderOutputControl(SceneRenderTask task)
+ : base(task)
+ {
+ }
+
+ ///
+ public override void Draw()
+ {
+ DrawSelf();
+
+ // Draw children with scale
+ var scaling = new Float3(ContentScale, ContentScale, 1);
+ Matrix3x3.Scaling(ref scaling, out Matrix3x3 scale);
+ Render2D.PushTransform(scale);
+ if (ClipChildren)
+ {
+ GetDesireClientArea(out var clientArea);
+ Render2D.PushClip(ref clientArea);
+ DrawChildren();
+ Render2D.PopClip();
+ }
+ else
+ {
+ DrawChildren();
+ }
+
+ Render2D.PopTransform();
+ }
+
+ ///
+ public override void GetDesireClientArea(out Rectangle rect)
+ {
+ // Scale the area for the client controls
+ rect = new Rectangle(Float2.Zero, Size / ContentScale);
+ }
+
+ ///
+ public override bool IntersectsContent(ref Float2 locationParent, out Float2 location)
+ {
+ // Skip local PointFromParent but use base code
+ location = base.PointFromParent(ref locationParent);
+ return ContainsPoint(ref location);
+ }
+
+ ///
+ public override bool IntersectsChildContent(Control child, Float2 location, out Float2 childSpaceLocation)
+ {
+ location /= ContentScale;
+ return base.IntersectsChildContent(child, location, out childSpaceLocation);
+ }
+
+ ///
+ public override bool ContainsPoint(ref Float2 location, bool precise = false)
+ {
+ if (precise) // Ignore as utility-only element
+ return false;
+ return base.ContainsPoint(ref location, precise);
+ }
+
+ ///
+ public override bool RayCast(ref Float2 location, out Control hit)
+ {
+ var p = location / ContentScale;
+ if (RayCastChildren(ref p, out hit))
+ return true;
+ return base.RayCast(ref location, out hit);
+ }
+
+ ///
+ public override Float2 PointToParent(ref Float2 location)
+ {
+ var result = base.PointToParent(ref location);
+ result *= ContentScale;
+ return result;
+ }
+
+ ///
+ public override Float2 PointFromParent(ref Float2 location)
+ {
+ var result = base.PointFromParent(ref location);
+ result /= ContentScale;
+ return result;
+ }
+ }
+
///
/// Provides in-editor play mode simulation.
///
///
public class GameWindow : EditorWindow
{
- private readonly RenderOutputControl _viewport;
+ private readonly ScaledRenderOutputControl _viewport;
private readonly GameRoot _guiRoot;
private bool _showGUI = true, _editGUI = true;
private bool _showDebugDraw = false;
@@ -77,7 +177,7 @@ namespace FlaxEditor.Windows
///
/// Gets the viewport.
///
- public RenderOutputControl Viewport => _viewport;
+ public ScaledRenderOutputControl Viewport => _viewport;
///
/// Gets or sets a value indicating whether show game GUI in the view or keep it hidden.
@@ -295,7 +395,7 @@ namespace FlaxEditor.Windows
var task = MainRenderTask.Instance;
// Setup viewport
- _viewport = new RenderOutputControl(task)
+ _viewport = new ScaledRenderOutputControl(task)
{
AnchorPreset = AnchorPresets.StretchAll,
Offsets = Margin.Zero,
@@ -396,11 +496,8 @@ namespace FlaxEditor.Windows
{
if (v == null)
return;
-
if (v.Size.Y <= 0 || v.Size.X <= 0)
- {
return;
- }
if (string.Equals(v.Label, "Free Aspect", StringComparison.Ordinal) && v.Size == new Int2(1, 1))
{
@@ -448,15 +545,7 @@ namespace FlaxEditor.Windows
private void ResizeViewport()
{
- if (!_freeAspect)
- {
- _windowAspectRatio = Width / Height;
- }
- else
- {
- _windowAspectRatio = 1;
- }
-
+ _windowAspectRatio = _freeAspect ? 1 : Width / Height;
var scaleWidth = _viewportAspectRatio / _windowAspectRatio;
var scaleHeight = _windowAspectRatio / _viewportAspectRatio;
@@ -468,6 +557,24 @@ namespace FlaxEditor.Windows
{
_viewport.Bounds = new Rectangle(Width * (1 - scaleWidth) / 2, 0, Width * scaleWidth, Height);
}
+
+ if (_viewport.KeepAspectRatio)
+ {
+ var resolution = _viewport.CustomResolution.HasValue ? (Float2)_viewport.CustomResolution.Value : Size;
+ if (scaleHeight < 1)
+ {
+ _viewport.ContentScale = _viewport.Width / resolution.X;
+ }
+ else
+ {
+ _viewport.ContentScale = _viewport.Height / resolution.Y;
+ }
+ }
+ else
+ {
+ _viewport.ContentScale = 1;
+ }
+
_viewport.SyncBackbufferSize();
PerformLayout();
}
@@ -907,6 +1014,7 @@ namespace FlaxEditor.Windows
return child.OnNavigate(direction, Float2.Zero, this, visited);
}
}
+
return null;
}
@@ -957,7 +1065,7 @@ namespace FlaxEditor.Windows
else
_defaultScaleActiveIndex = 0;
}
-
+
if (_customScaleActiveIndex != -1)
{
var options = Editor.UI.CustomViewportScaleOptions;
diff --git a/Source/Engine/UI/GUI/RenderOutputControl.cs b/Source/Engine/UI/GUI/RenderOutputControl.cs
index 9025cbc04..8dc6707bf 100644
--- a/Source/Engine/UI/GUI/RenderOutputControl.cs
+++ b/Source/Engine/UI/GUI/RenderOutputControl.cs
@@ -180,7 +180,7 @@ namespace FlaxEngine.GUI
}
///
- public override void Draw()
+ public override void DrawSelf()
{
var bounds = new Rectangle(Float2.Zero, Size);
@@ -205,21 +205,6 @@ namespace FlaxEngine.GUI
Render2D.DrawTexture(buffer, bounds, color);
else
Render2D.FillRectangle(bounds, Color.Black);
-
- // Push clipping mask
- if (ClipChildren)
- {
- GetDesireClientArea(out var clientArea);
- Render2D.PushClip(ref clientArea);
- }
-
- DrawChildren();
-
- // Pop clipping mask
- if (ClipChildren)
- {
- Render2D.PopClip();
- }
}
///