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;