diff --git a/Source/Editor/GUI/Popups/AutoSavePopup.cs b/Source/Editor/GUI/Popups/AutoSavePopup.cs
index 28f9577d8..0992def3e 100644
--- a/Source/Editor/GUI/Popups/AutoSavePopup.cs
+++ b/Source/Editor/GUI/Popups/AutoSavePopup.cs
@@ -101,18 +101,6 @@ namespace FlaxEditor.GUI
_timeRemaining = Mathf.CeilToInt(time);
if (_timeLabel != null)
_timeLabel.Text = "Auto Save in: " + _timeRemaining;
-
- // Move on text update if the progress bar is visible - removes this call from update
- if (Editor.Instance.UI.ProgressVisible && !_isMoved)
- {
- _isMoved = true;
- LocalX -= 250;
- }
- else if (!Editor.Instance.UI.ProgressVisible && _isMoved)
- {
- LocalX += 250;
- _isMoved = false;
- }
}
///
@@ -123,6 +111,18 @@ namespace FlaxEditor.GUI
_saveNowButton.TextColor = _saveNowButton.IsMouseOver ? Style.Current.BackgroundHighlighted : _defaultTextColor;
_cancelSaveButton.TextColor = _cancelSaveButton.IsMouseOver ? Style.Current.BackgroundHighlighted : _defaultTextColor;
}
+
+ // Move if the progress bar is visible
+ if (Editor.Instance.UI.ProgressVisible && !_isMoved)
+ {
+ _isMoved = true;
+ LocalX -= 280;
+ }
+ else if (!Editor.Instance.UI.ProgressVisible && _isMoved)
+ {
+ LocalX += 280;
+ _isMoved = false;
+ }
base.Update(deltaTime);
}
diff --git a/Source/Editor/GUI/StatusBar.cs b/Source/Editor/GUI/StatusBar.cs
index 80a500236..f8f7ae839 100644
--- a/Source/Editor/GUI/StatusBar.cs
+++ b/Source/Editor/GUI/StatusBar.cs
@@ -30,6 +30,11 @@ namespace FlaxEditor.GUI
///
public string Text { get; set; }
+ ///
+ /// Gets or sets the status text color
+ ///
+ public Color TextColor { get; set; } = Style.Current.Foreground;
+
///
/// Initializes a new instance of the class.
///
@@ -51,7 +56,7 @@ namespace FlaxEditor.GUI
Render2D.DrawSprite(style.StatusBarSizeGrip, new Rectangle(Width - 12, 10, 12, 12), style.Foreground);
// Draw status text
- Render2D.DrawText(style.FontSmall, Text, new Rectangle(4, 0, Width - 20, Height), style.Foreground, TextAlignment.Near, TextAlignment.Center);
+ Render2D.DrawText(style.FontSmall, Text, new Rectangle(4, 0, Width - 20, Height), TextColor, TextAlignment.Near, TextAlignment.Center);
}
}
}
diff --git a/Source/Editor/Modules/ProgressReportingModule.cs b/Source/Editor/Modules/ProgressReportingModule.cs
index 713292657..16acd7f8b 100644
--- a/Source/Editor/Modules/ProgressReportingModule.cs
+++ b/Source/Editor/Modules/ProgressReportingModule.cs
@@ -96,6 +96,7 @@ namespace FlaxEditor.Modules
handler.ProgressStart += HandlerOnProgressStart;
handler.ProgressChanged += HandlerOnProgressChanged;
handler.ProgressEnd += HandlerOnProgressEnd;
+ handler.ProgressFailed += HandlerOnProgressFail;
}
///
@@ -113,6 +114,7 @@ namespace FlaxEditor.Modules
handler.ProgressStart -= HandlerOnProgressStart;
handler.ProgressChanged -= HandlerOnProgressChanged;
handler.ProgressEnd -= HandlerOnProgressEnd;
+ handler.ProgressFailed -= HandlerOnProgressFail;
}
private void UpdateProgress()
@@ -150,5 +152,11 @@ namespace FlaxEditor.Modules
Editor.Windows.FlashMainWindow();
}
}
+
+ private void HandlerOnProgressFail(ProgressHandler handler, string message)
+ {
+ UpdateProgress();
+ Editor.UI.ProgressFailed(message);
+ }
}
}
diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs
index 2910e122f..4edd4540b 100644
--- a/Source/Editor/Modules/UIModule.cs
+++ b/Source/Editor/Modules/UIModule.cs
@@ -31,8 +31,10 @@ namespace FlaxEditor.Modules
{
private Label _progressLabel;
private ProgressBar _progressBar;
+ private Button _outputLogButton;
private List> _statusMessages;
private ContentStats _contentStats;
+ private bool _progressFailed;
private ContextMenuButton _menuFileSaveScenes;
private ContextMenuButton _menuFileCloseScenes;
@@ -252,6 +254,12 @@ namespace FlaxEditor.Modules
{
if (StatusBar == null)
return;
+
+ if (ScriptsBuilder.LastCompilationFailed)
+ {
+ ProgressFailed("Scripts Compilation Failed");
+ return;
+ }
var contentStats = FlaxEngine.Content.Stats;
Color color;
@@ -303,7 +311,28 @@ namespace FlaxEditor.Modules
if (_progressLabel != null)
_progressLabel.Text = text;
if (_progressBar != null)
+ {
+ if (_progressFailed)
+ {
+ ResetProgressFailure();
+ }
_progressBar.Value = progress * 100.0f;
+ }
+ }
+
+ internal void ProgressFailed(string message)
+ {
+ _progressFailed = true;
+ StatusBar.StatusColor = Color.Red;
+ StatusBar.Text = message;
+ _outputLogButton.Visible = true;
+ }
+
+ internal void ResetProgressFailure()
+ {
+ _outputLogButton.Visible = false;
+ _progressFailed = false;
+ UpdateStatusBar();
}
///
@@ -584,6 +613,30 @@ namespace FlaxEditor.Modules
Parent = mainWindow,
Offsets = new Margin(0, 0, -StatusBar.DefaultHeight, StatusBar.DefaultHeight),
};
+ // Output log button
+ _outputLogButton = new Button()
+ {
+ AnchorPreset = AnchorPresets.TopLeft,
+ Parent = StatusBar,
+ Visible = false,
+ Text = "",
+ Width = 200,
+ TooltipText = "Opens or shows the output log window.",
+ BackgroundColor = Color.Transparent,
+ BorderColor = Color.Transparent,
+ BackgroundColorHighlighted = Color.Transparent,
+ BackgroundColorSelected = Color.Transparent,
+ BorderColorHighlighted = Color.Transparent,
+ BorderColorSelected = Color.Transparent,
+ };
+ _outputLogButton.LocalY -= 2;
+ var defaultTextColor = StatusBar.TextColor;
+ _outputLogButton.HoverBegin += () => StatusBar.TextColor = Style.Current.BackgroundSelected;
+ _outputLogButton.HoverEnd += () => StatusBar.TextColor = defaultTextColor;
+ _outputLogButton.Clicked += () =>
+ {
+ Editor.Windows.OutputLogWin.FocusOrShow();
+ };
// Progress bar with label
const float progressBarWidth = 120.0f;
diff --git a/Source/Editor/Progress/Handlers/CompileScriptsProgress.cs b/Source/Editor/Progress/Handlers/CompileScriptsProgress.cs
index ef814a0fb..59d40463e 100644
--- a/Source/Editor/Progress/Handlers/CompileScriptsProgress.cs
+++ b/Source/Editor/Progress/Handlers/CompileScriptsProgress.cs
@@ -1,6 +1,5 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
-using FlaxEngine;
using FlaxEditor.Utilities;
namespace FlaxEditor.Progress.Handlers
@@ -21,7 +20,7 @@ namespace FlaxEditor.Progress.Handlers
// Link for events
ScriptsBuilder.CompilationBegin += OnStart;
ScriptsBuilder.CompilationSuccess += OnEnd;
- ScriptsBuilder.CompilationFailed += OnEnd;
+ ScriptsBuilder.CompilationFailed += OnCompilationFailed;
ScriptsBuilder.CompilationStarted += () => OnUpdate(0.2f, "Compiling scripts...");
ScriptsBuilder.ScriptsReloadCalled += () => OnUpdate(0.8f, "Reloading scripts...");
ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
@@ -45,6 +44,11 @@ namespace FlaxEditor.Progress.Handlers
Newtonsoft.Json.JsonSerializer.ClearCache();
}
+ private void OnCompilationFailed()
+ {
+ OnFail("Scripts compilation failed");
+ }
+
private void OnScriptsReloadEnd()
{
_selectionCache.Restore();
diff --git a/Source/Editor/Progress/ProgressHandler.cs b/Source/Editor/Progress/ProgressHandler.cs
index 74b31ff1f..2738b7c57 100644
--- a/Source/Editor/Progress/ProgressHandler.cs
+++ b/Source/Editor/Progress/ProgressHandler.cs
@@ -16,6 +16,11 @@ namespace FlaxEditor.Progress
///
/// The calling handler.
public delegate void ProgressDelegate(ProgressHandler handler);
+
+ ///
+ /// Progress failed handler event delegate
+ ///
+ public delegate void ProgressFailedDelegate(ProgressHandler handler, string message);
private float _progress;
private bool _isActive;
@@ -51,6 +56,11 @@ namespace FlaxEditor.Progress
///
public event ProgressDelegate ProgressEnd;
+ ///
+ /// Occurs when the progress fails
+ ///
+ public event ProgressFailedDelegate ProgressFailed;
+
///
/// Gets a value indicating whether this handler action can be cancelled.
///
@@ -109,5 +119,19 @@ namespace FlaxEditor.Progress
ProgressChanged?.Invoke(this);
ProgressEnd?.Invoke(this);
}
+
+ ///
+ /// Called when progress action fails
+ ///
+ protected virtual void OnFail(string message)
+ {
+ if (!_isActive)
+ throw new InvalidOperationException("Already ended.");
+
+ _isActive = false;
+ _progress = 0;
+ _infoText = string.Empty;
+ ProgressFailed?.Invoke(this, message);
+ }
}
}
diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs
index 8084d638e..f17deb4bd 100644
--- a/Source/Editor/Windows/GameWindow.cs
+++ b/Source/Editor/Windows/GameWindow.cs
@@ -8,6 +8,7 @@ using FlaxEditor.GUI.Input;
using FlaxEditor.Options;
using FlaxEngine;
using FlaxEngine.GUI;
+using FlaxEngine.Json;
namespace FlaxEditor.Windows
{
@@ -28,6 +29,14 @@ namespace FlaxEditor.Windows
private GUI.Docking.DockPanel _maximizeRestoreDockTo;
private CursorLockMode _cursorLockMode = CursorLockMode.None;
+ // Viewport scaling variables
+ private List _defaultViewportScaling = new List();
+ private List _customViewportScaling = new List();
+ private float _viewportAspectRatio = 1;
+ private float _windowAspectRatio = 1;
+ private bool _useAspect = false;
+ private bool _freeAspect = true;
+
///
/// Gets the viewport.
///
@@ -106,6 +115,35 @@ namespace FlaxEditor.Windows
///
public bool FocusOnPlay { get; set; }
+ private enum ViewportScaleType
+ {
+ Resolution = 0,
+ Aspect = 1,
+ }
+
+ private class ViewportScaleOptions
+ {
+ ///
+ /// The name.
+ ///
+ public string Label;
+
+ ///
+ /// The Type of scaling to do.
+ ///
+ public ViewportScaleType ScaleType;
+
+ ///
+ /// The width and height to scale by.
+ ///
+ public Int2 Size;
+
+ ///
+ /// If the scaling is active.
+ ///
+ public bool Active;
+ }
+
private class GameRoot : ContainerControl
{
public bool EnableEvents => !Time.GamePaused;
@@ -255,6 +293,9 @@ namespace FlaxEditor.Windows
Parent = _viewport
};
RootControl.GameRoot = _guiRoot;
+
+ SizeChanged += control => { ResizeViewport(); };
+
Editor.StateMachine.PlayingState.SceneDuplicating += PlayingStateOnSceneDuplicating;
Editor.StateMachine.PlayingState.SceneRestored += PlayingStateOnSceneRestored;
@@ -267,6 +308,85 @@ namespace FlaxEditor.Windows
InputActions.Add(options => options.StepFrame, Editor.Simulation.RequestPlayOneFrame);
}
+ private void ChangeViewportRatio(ViewportScaleOptions v)
+ {
+ if (v == null)
+ return;
+
+ if (v.Size.Y <= 0 || v.Size.X <= 0)
+ {
+ return;
+ }
+
+ if (string.Equals(v.Label, "Free Aspect") && v.Size == new Int2(1, 1))
+ {
+ _freeAspect = true;
+ _useAspect = true;
+ }
+ else
+ {
+ switch (v.ScaleType)
+ {
+ case ViewportScaleType.Aspect:
+ _useAspect = true;
+ _freeAspect = false;
+ break;
+ case ViewportScaleType.Resolution:
+ _useAspect = false;
+ _freeAspect = false;
+ break;
+ }
+ }
+
+ _viewportAspectRatio = (float)v.Size.X / v.Size.Y;
+
+ if (!_freeAspect)
+ {
+ if (!_useAspect)
+ {
+ _viewport.KeepAspectRatio = true;
+ _viewport.CustomResolution = new Int2(v.Size.X, v.Size.Y);
+ }
+ else
+ {
+ _viewport.CustomResolution = new Int2?();
+ _viewport.KeepAspectRatio = true;
+ }
+ }
+ else
+ {
+ _viewport.CustomResolution = new Int2?();
+ _viewport.KeepAspectRatio = false;
+ }
+
+ ResizeViewport();
+ }
+
+ private void ResizeViewport()
+ {
+ if (!_freeAspect)
+ {
+ _windowAspectRatio = Width / Height;
+ }
+ else
+ {
+ _windowAspectRatio = 1;
+ }
+
+ var scaleWidth = _viewportAspectRatio / _windowAspectRatio;
+ var scaleHeight = _windowAspectRatio / _viewportAspectRatio;
+
+ if (scaleHeight < 1)
+ {
+ _viewport.Bounds = new Rectangle(0, Height * (1 - scaleHeight) / 2, Width, Height * scaleHeight);
+ }
+ else
+ {
+ _viewport.Bounds = new Rectangle(Width * (1 - scaleWidth) / 2, 0, Width * scaleWidth, Height);
+ }
+ PerformLayout();
+ }
+
private void OnPostRender(GPUContext context, ref RenderContext renderContext)
{
// Debug Draw shapes
@@ -372,6 +492,53 @@ namespace FlaxEditor.Windows
resolutionValue.ValueChanged += () => _viewport.ResolutionScale = resolutionValue.Value;
}
+ // Viewport aspect ratio
+ {
+ // Create default scaling options if they dont exist from deserialization.
+ if (_defaultViewportScaling.Count == 0)
+ {
+ _defaultViewportScaling.Add(new ViewportScaleOptions
+ {
+ Label = "Free Aspect",
+ ScaleType = ViewportScaleType.Aspect,
+ Size = new Int2(1, 1),
+ Active = true,
+ });
+ _defaultViewportScaling.Add(new ViewportScaleOptions
+ {
+ Label = "16:9 Aspect",
+ ScaleType = ViewportScaleType.Aspect,
+ Size = new Int2(16, 9),
+ Active = false,
+ });
+ _defaultViewportScaling.Add(new ViewportScaleOptions
+ {
+ Label = "16:10 Aspect",
+ ScaleType = ViewportScaleType.Aspect,
+ Size = new Int2(16, 10),
+ Active = false,
+ });
+ _defaultViewportScaling.Add(new ViewportScaleOptions
+ {
+ Label = "1920x1080 Resolution",
+ ScaleType = ViewportScaleType.Resolution,
+ Size = new Int2(1920, 1080),
+ Active = false,
+ });
+ _defaultViewportScaling.Add(new ViewportScaleOptions
+ {
+ Label = "2560x1440 Resolution",
+ ScaleType = ViewportScaleType.Resolution,
+ Size = new Int2(2560, 1440),
+ Active = false,
+ });
+ }
+
+ var vsMenu = menu.AddChildMenu("Viewport Size").ContextMenu;
+
+ CreateViewportSizingContextMenu(vsMenu);
+ }
+
// Take Screenshot
{
var takeScreenshot = menu.AddButton("Take Screenshot");
@@ -400,6 +567,243 @@ namespace FlaxEditor.Windows
menu.AddSeparator();
}
+ private void CreateViewportSizingContextMenu(ContextMenu vsMenu)
+ {
+ // Add default viewport sizing options
+ for (int i = 0; i < _defaultViewportScaling.Count; i++)
+ {
+ var viewportScale = _defaultViewportScaling[i];
+ var button = vsMenu.AddButton(viewportScale.Label);
+ button.CloseMenuOnClick = false;
+ button.Icon = viewportScale.Active ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
+ button.Tag = viewportScale;
+ if (viewportScale.Active)
+ ChangeViewportRatio(viewportScale);
+
+ button.Clicked += () =>
+ {
+ if (button.Tag == null)
+ return;
+
+ // Reset selected icon on all buttons
+ foreach (var child in vsMenu.Items)
+ {
+ if (child is ContextMenuButton cmb && cmb.Tag is ViewportScaleOptions v)
+ {
+ if (cmb == button)
+ {
+ v.Active = true;
+ button.Icon = Style.Current.CheckBoxTick;
+ ChangeViewportRatio(v);
+ }
+ else if (v.Active)
+ {
+ cmb.Icon = SpriteHandle.Invalid;
+ v.Active = false;
+ }
+ }
+ }
+ };
+ }
+ if (_defaultViewportScaling.Count != 0)
+ vsMenu.AddSeparator();
+
+ // Add custom viewport options
+ for (int i = 0; i < _customViewportScaling.Count; i++)
+ {
+ var viewportScale = _customViewportScaling[i];
+ var childCM = vsMenu.AddChildMenu(viewportScale.Label);
+ childCM.CloseMenuOnClick = false;
+ childCM.Icon = viewportScale.Active ? Style.Current.CheckBoxTick : SpriteHandle.Invalid;
+ childCM.Tag = viewportScale;
+ if (viewportScale.Active)
+ ChangeViewportRatio(viewportScale);
+
+ var applyButton = childCM.ContextMenu.AddButton("Apply");
+ applyButton.Tag = childCM.Tag = viewportScale;
+ applyButton.CloseMenuOnClick = false;
+ applyButton.Clicked += () =>
+ {
+ if (childCM.Tag == null)
+ return;
+
+ // Reset selected icon on all buttons
+ foreach (var child in vsMenu.Items)
+ {
+ if (child is ContextMenuButton cmb && cmb.Tag is ViewportScaleOptions v)
+ {
+ if (child == childCM)
+ {
+ v.Active = true;
+ childCM.Icon = Style.Current.CheckBoxTick;
+ ChangeViewportRatio(v);
+ }
+ else if (v.Active)
+ {
+ cmb.Icon = SpriteHandle.Invalid;
+ v.Active = false;
+ }
+ }
+ }
+ };
+
+ var deleteButton = childCM.ContextMenu.AddButton("Delete");
+ deleteButton.CloseMenuOnClick = false;
+ deleteButton.Clicked += () =>
+ {
+ if (childCM.Tag == null)
+ return;
+
+ var v = (ViewportScaleOptions)childCM.Tag;
+ if (v.Active)
+ {
+ v.Active = false;
+ _defaultViewportScaling[0].Active = true;
+ ChangeViewportRatio(_defaultViewportScaling[0]);
+ }
+ _customViewportScaling.Remove(v);
+ vsMenu.DisposeAllItems();
+ CreateViewportSizingContextMenu(vsMenu);
+ vsMenu.PerformLayout();
+ };
+ }
+ if (_customViewportScaling.Count != 0)
+ vsMenu.AddSeparator();
+
+ // Add button
+ var add = vsMenu.AddButton("Add...");
+ add.CloseMenuOnClick = false;
+ add.Clicked += () =>
+ {
+ var popup = new ContextMenuBase
+ {
+ Size = new Float2(230, 125),
+ ClipChildren = false,
+ CullChildren = false,
+ };
+ popup.Show(add, new Float2(add.Width, 0));
+
+ var nameLabel = new Label
+ {
+ Parent = popup,
+ AnchorPreset = AnchorPresets.TopLeft,
+ Text = "Name",
+ HorizontalAlignment = TextAlignment.Near,
+ };
+ nameLabel.LocalX += 10;
+ nameLabel.LocalY += 10;
+
+ var nameTextBox = new TextBox
+ {
+ Parent = popup,
+ AnchorPreset = AnchorPresets.TopLeft,
+ IsMultiline = false,
+ };
+ nameTextBox.LocalX += 100;
+ nameTextBox.LocalY += 10;
+
+ var typeLabel = new Label
+ {
+ Parent = popup,
+ AnchorPreset = AnchorPresets.TopLeft,
+ Text = "Type",
+ HorizontalAlignment = TextAlignment.Near,
+ };
+ typeLabel.LocalX += 10;
+ typeLabel.LocalY += 35;
+
+ var typeDropdown = new Dropdown
+ {
+ Parent = popup,
+ AnchorPreset = AnchorPresets.TopLeft,
+ Items = { "Aspect", "Resolution" },
+ SelectedItem = "Aspect",
+ Width = nameTextBox.Width
+ };
+ typeDropdown.LocalY += 35;
+ typeDropdown.LocalX += 100;
+
+ var whLabel = new Label
+ {
+ Parent = popup,
+ AnchorPreset = AnchorPresets.TopLeft,
+ Text = "Width & Height",
+ HorizontalAlignment = TextAlignment.Near,
+ };
+ whLabel.LocalX += 10;
+ whLabel.LocalY += 60;
+
+ var wValue = new IntValueBox(16)
+ {
+ Parent = popup,
+ AnchorPreset = AnchorPresets.TopLeft,
+ MinValue = 1,
+ Width = 55,
+ };
+ wValue.LocalY += 60;
+ wValue.LocalX += 100;
+
+ var hValue = new IntValueBox(9)
+ {
+ Parent = popup,
+ AnchorPreset = AnchorPresets.TopLeft,
+ MinValue = 1,
+ Width = 55,
+ };
+ hValue.LocalY += 60;
+ hValue.LocalX += 165;
+
+ var submitButton = new Button
+ {
+ Parent = popup,
+ AnchorPreset = AnchorPresets.TopLeft,
+ Text = "Submit",
+ Width = 70,
+ };
+ submitButton.LocalX += 40;
+ submitButton.LocalY += 90;
+
+ submitButton.Clicked += () =>
+ {
+ Enum.TryParse(typeDropdown.SelectedItem, out ViewportScaleType type);
+
+ var combineString = type == ViewportScaleType.Aspect ? ":" : "x";
+ var name = nameTextBox.Text + " (" + wValue.Value + combineString + hValue.Value + ") " + typeDropdown.SelectedItem;
+
+ var newViewportOption = new ViewportScaleOptions
+ {
+ ScaleType = type,
+ Label = name,
+ Size = new Int2(wValue.Value, hValue.Value),
+ };
+
+ _customViewportScaling.Add(newViewportOption);
+ vsMenu.DisposeAllItems();
+ CreateViewportSizingContextMenu(vsMenu);
+ vsMenu.PerformLayout();
+ };
+
+ var cancelButton = new Button
+ {
+ Parent = popup,
+ AnchorPreset = AnchorPresets.TopLeft,
+ Text = "Cancel",
+ Width = 70,
+ };
+ cancelButton.LocalX += 120;
+ cancelButton.LocalY += 90;
+
+ cancelButton.Clicked += () =>
+ {
+ nameTextBox.Clear();
+ typeDropdown.SelectedItem = "Aspect";
+ hValue.Value = 9;
+ wValue.Value = 16;
+ popup.Hide();
+ };
+ };
+ }
+
///
public override void Draw()
{
@@ -471,7 +875,7 @@ namespace FlaxEditor.Windows
Screen.CursorVisible = true;
if (Screen.CursorLock == CursorLockMode.Clipped)
Screen.CursorLock = CursorLockMode.None;
-
+
// Defocus
_isUnlockingMouse = true;
Focus(null);
@@ -624,6 +1028,8 @@ namespace FlaxEditor.Windows
{
writer.WriteAttributeString("ShowGUI", ShowGUI.ToString());
writer.WriteAttributeString("ShowDebugDraw", ShowDebugDraw.ToString());
+ writer.WriteAttributeString("DefaultViewportScaling", JsonSerializer.Serialize(_defaultViewportScaling));
+ writer.WriteAttributeString("CustomViewportScaling", JsonSerializer.Serialize(_customViewportScaling));
}
///
@@ -633,6 +1039,23 @@ namespace FlaxEditor.Windows
ShowGUI = value1;
if (bool.TryParse(node.GetAttribute("ShowDebugDraw"), out value1))
ShowDebugDraw = value1;
+ if (node.HasAttribute("CustomViewportScaling"))
+ _customViewportScaling = JsonSerializer.Deserialize>(node.GetAttribute("CustomViewportScaling"));
+
+ for (int i = 0; i < _customViewportScaling.Count; i++)
+ {
+ if (_customViewportScaling[i].Active)
+ ChangeViewportRatio(_customViewportScaling[i]);
+ }
+
+ if (node.HasAttribute("DefaultViewportScaling"))
+ _defaultViewportScaling = JsonSerializer.Deserialize>(node.GetAttribute("DefaultViewportScaling"));
+
+ for (int i = 0; i < _defaultViewportScaling.Count; i++)
+ {
+ if (_defaultViewportScaling[i].Active)
+ ChangeViewportRatio(_defaultViewportScaling[i]);
+ }
}
///
diff --git a/Source/Engine/Engine/RandomStream.cs b/Source/Engine/Engine/RandomStream.cs
new file mode 100644
index 000000000..90be3bd2c
--- /dev/null
+++ b/Source/Engine/Engine/RandomStream.cs
@@ -0,0 +1,198 @@
+// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
+
+using System.Runtime.CompilerServices;
+
+#pragma warning disable 675
+
+namespace FlaxEngine
+{
+ ///
+ /// Very basic pseudo numbers generator.
+ ///
+ [HideInEditor]
+ public class RandomStream
+ {
+ ///
+ /// Holds the initial seed.
+ ///
+ private int _initialSeed;
+
+ ///
+ /// Holds the current seed.
+ ///
+ private int _seed;
+
+ ///
+ /// Init
+ ///
+ public RandomStream()
+ {
+ _initialSeed = 0;
+ _seed = 0;
+ }
+
+ ///
+ /// Creates and initializes a new random stream from the specified seed value.
+ ///
+ /// The seed value.
+ public RandomStream(int seed)
+ {
+ _initialSeed = seed;
+ _seed = seed;
+ }
+
+ ///
+ /// Gets initial seed value
+ ///
+ public int GetInitialSeed()
+ {
+ return _initialSeed;
+ }
+
+
+ ///
+ /// Gets the current seed.
+ ///
+ public int GetCurrentSeed()
+ {
+ return _seed;
+ }
+
+ ///
+ /// Initializes this random stream with the specified seed value.
+ ///
+ /// The seed value.
+ public void Initialize(int seed)
+ {
+ _initialSeed = seed;
+ _seed = seed;
+ }
+
+ ///
+ /// Resets this random stream to the initial seed value.
+ ///
+ public void Reset()
+ {
+ _seed = _initialSeed;
+ }
+
+ ///
+ /// Generates a new random seed.
+ ///
+ public void GenerateNewSeed()
+ {
+ Initialize(new System.Random().Next());
+ }
+
+ ///
+ /// Returns a random boolean.
+ ///
+ public unsafe bool GetBool()
+ {
+ MutateSeed();
+ fixed (int* seedPtr = &_seed)
+ return *seedPtr < (uint.MaxValue / 2);
+ }
+
+ ///
+ /// Returns a random number between 0 and uint.MaxValue.
+ ///
+ public unsafe uint GetUnsignedInt()
+ {
+ MutateSeed();
+ fixed (int* seedPtr = &_seed)
+ return (uint)*seedPtr;
+ }
+
+ ///
+ /// Returns a random number between 0 and 1.
+ ///
+ public unsafe float GetFraction()
+ {
+ MutateSeed();
+ float temp = 1.0f;
+ float result = 0.0f;
+ *(int*)&result = (int)((*(int*)&temp & 0xff800000) | (_seed & 0x007fffff));
+ return Mathf.Frac(result);
+ }
+
+ ///
+ /// Returns a random number between 0 and 1.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public float Rand()
+ {
+ return GetFraction();
+ }
+
+ ///
+ /// Returns a random vector of unit size.
+ ///
+ public Float3 GetUnitVector()
+ {
+ Float3 result;
+ float l;
+ do
+ {
+ result.X = GetFraction() * 2.0f - 1.0f;
+ result.Y = GetFraction() * 2.0f - 1.0f;
+ result.Z = GetFraction() * 2.0f - 1.0f;
+ l = result.LengthSquared;
+ } while (l > 1.0f || l < Mathf.Epsilon);
+ return Float3.Normalize(result);
+ }
+
+ ///
+ /// Gets a random with components in a range between [0;1].
+ ///
+ public Vector3 GetVector3()
+ {
+ return new Vector3(GetFraction(), GetFraction(), GetFraction());
+ }
+
+ ///
+ /// Helper function for rand implementations.
+ ///
+ /// Top border
+ /// A random number in [0..A)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int RandHelper(int a)
+ {
+ return a > 0 ? Mathf.FloorToInt(GetFraction() * ((float)a - Mathf.Epsilon)) : 0;
+ }
+
+ ///
+ /// Helper function for rand implementations
+ ///
+ /// Minimum value
+ /// Maximum value
+ /// A random number in [min; max] range
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int RandRange(int min, int max)
+ {
+ int range = max - min + 1;
+ return min + RandHelper(range);
+ }
+
+ ///
+ /// Helper function for rand implementations
+ ///
+ /// Minimum value
+ /// Maximum value
+ /// A random number in [min; max] range
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public float RandRange(float min, float max)
+ {
+ return min + (max - min) * Rand();
+ }
+
+ ///
+ /// Mutates the current seed into the next seed.
+ ///
+ protected void MutateSeed()
+ {
+ // This can be modified to provide better randomization
+ _seed = _seed * 196314165 + 907633515;
+ }
+ }
+}
diff --git a/Source/Engine/Engine/RandomUtil.cs b/Source/Engine/Engine/RandomUtil.cs
new file mode 100644
index 000000000..41682dc53
--- /dev/null
+++ b/Source/Engine/Engine/RandomUtil.cs
@@ -0,0 +1,25 @@
+// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
+
+using System;
+using System.Runtime.CompilerServices;
+
+namespace FlaxEngine
+{
+ ///
+ /// Basic pseudo numbers generator utility.
+ ///
+ public static class RandomUtil
+ {
+ private static readonly Random _random = new Random();
+
+ ///
+ /// Generates a pseudo-random number from normalized range [0;1].
+ ///
+ /// The random number.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Rand()
+ {
+ return _random.Next(0, int.MaxValue) / (float)int.MaxValue;
+ }
+ }
+}
diff --git a/Source/Engine/Renderer/AmbientOcclusionPass.cpp b/Source/Engine/Renderer/AmbientOcclusionPass.cpp
index a0d1bebb6..17dac35c2 100644
--- a/Source/Engine/Renderer/AmbientOcclusionPass.cpp
+++ b/Source/Engine/Renderer/AmbientOcclusionPass.cpp
@@ -206,20 +206,12 @@ void AmbientOcclusionPass::Render(RenderContext& renderContext)
if (renderContext.List == nullptr)
return;
auto& aoSettings = renderContext.List->Settings.AmbientOcclusion;
- if (aoSettings.Enabled == false || (renderContext.View.Flags & ViewFlags::AO) == ViewFlags::None)
+ if (aoSettings.Enabled == false ||
+ (renderContext.View.Flags & ViewFlags::AO) == ViewFlags::None ||
+ renderContext.View.IsOrthographicProjection() || // TODO: add support for SSAO in ortho projection
+ Math::Min(renderContext.Buffers->GetWidth(), renderContext.Buffers->GetHeight()) < 16 ||
+ checkIfSkipPass())
return;
-
- // TODO: add support for SSAO in ortho projection
- if (renderContext.View.IsOrthographicProjection())
- return;
-
- // Ensure to have valid data
- if (checkIfSkipPass())
- {
- // Resources are missing. Do not perform rendering.
- return;
- }
-
PROFILE_GPU_CPU("Ambient Occlusion");
settings.Radius = aoSettings.Radius * 0.006f;