diff --git a/Content/Shaders/GI/DDGI.flax b/Content/Shaders/GI/DDGI.flax index 257953bf9..98cfbb80a 100644 --- a/Content/Shaders/GI/DDGI.flax +++ b/Content/Shaders/GI/DDGI.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5b017cf857f443553020e4bc7c8c8c5da3a826a2514322664a023ffa6005f7a5 -size 38217 +oid sha256:50753dbd37cb2a6fd6f48a597e90c8b8b689c7e433e66a5f21bc4ffa94244895 +size 38260 diff --git a/Flax.flaxproj b/Flax.flaxproj index c9c27281b..78c03fce6 100644 --- a/Flax.flaxproj +++ b/Flax.flaxproj @@ -4,7 +4,7 @@ "Major": 1, "Minor": 11, "Revision": 0, - "Build": 6807 + "Build": 6808 }, "Company": "Flax", "Copyright": "Copyright (c) 2012-2026 Wojciech Figat. All rights reserved.", diff --git a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs index 614d2c160..1628f69ec 100644 --- a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs @@ -690,7 +690,7 @@ namespace FlaxEditor.CustomEditors.Dedicated return grid; } - private CustomElementsContainer UniformPanelCapsuleForObjectWithText(LayoutElementsContainer el, string text, ValueContainer values, Color borderColor, out FloatValueBox valueBox) + private CustomElementsContainer UniformPanelCapsuleForObjectWithText(LayoutElementsContainer el, string text, ValueContainer values, Color highlightColor, out FloatValueBox valueBox) { valueBox = null; var grid = UniformGridTwoByOne(el); @@ -701,8 +701,8 @@ namespace FlaxEditor.CustomEditors.Dedicated { valueBox = floatEditorElement.ValueBox; var back = FlaxEngine.GUI.Style.Current.TextBoxBackground; - valueBox.BorderColor = Color.Lerp(borderColor, back, ActorTransformEditor.AxisGreyOutFactor); - valueBox.BorderSelectedColor = borderColor; + valueBox.HighlightColor = highlightColor; + valueBox.BorderSelectedColor = highlightColor; } return grid; } diff --git a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs index 02f5ca7ec..c1179e393 100644 --- a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs @@ -14,22 +14,17 @@ namespace FlaxEditor.CustomEditors.Editors /// /// The X axis color. /// - public static Color AxisColorX = new Color(1.0f, 0.0f, 0.02745f, 1.0f); + public static Color AxisColorX = new Color(0.8f, 0.0f, 0.027f, 1.0f); /// /// The Y axis color. /// - public static Color AxisColorY = new Color(0.239215f, 1.0f, 0.047058f, 1.0f); + public static Color AxisColorY = new Color(0.239215f, 0.65f, 0.047058f, 1.0f); /// /// The Z axis color. /// - public static Color AxisColorZ = new Color(0.0f, 0.0235294f, 1.0f, 1.0f); - - /// - /// The axes colors grey out scale when input field is not focused. - /// - public static float AxisGreyOutFactor = 0.6f; + public static Color AxisColorZ = new Color(0.0f, 0.0235294f, 0.9f, 1.0f); /// /// Custom editor for actor position property. @@ -43,18 +38,20 @@ namespace FlaxEditor.CustomEditors.Editors base.Initialize(layout); if (XElement.ValueBox.Parent is UniformGridPanel ug) + { + ug.SlotPadding = new Margin(3.0f, 0.0f, 0.0f, 0.0f); CheckLayout(ug); + } // Override colors - var back = FlaxEngine.GUI.Style.Current.TextBoxBackground; - XElement.ValueBox.BorderColor = Color.Lerp(AxisColorX, back, AxisGreyOutFactor); XElement.ValueBox.BorderSelectedColor = AxisColorX; - XElement.ValueBox.Category = Utils.ValueCategory.Distance; - YElement.ValueBox.BorderColor = Color.Lerp(AxisColorY, back, AxisGreyOutFactor); YElement.ValueBox.BorderSelectedColor = AxisColorY; - YElement.ValueBox.Category = Utils.ValueCategory.Distance; - ZElement.ValueBox.BorderColor = Color.Lerp(AxisColorZ, back, AxisGreyOutFactor); ZElement.ValueBox.BorderSelectedColor = AxisColorZ; + XElement.ValueBox.HighlightColor = AxisColorX; + XElement.ValueBox.Category = Utils.ValueCategory.Distance; + YElement.ValueBox.HighlightColor = AxisColorY; + YElement.ValueBox.Category = Utils.ValueCategory.Distance; + ZElement.ValueBox.HighlightColor = AxisColorZ; ZElement.ValueBox.Category = Utils.ValueCategory.Distance; } } @@ -71,18 +68,20 @@ namespace FlaxEditor.CustomEditors.Editors base.Initialize(layout); if (XElement.ValueBox.Parent is UniformGridPanel ug) + { + ug.SlotPadding = new Margin(3.0f, 0.0f, 0.0f, 0.0f); CheckLayout(ug); + } // Override colors - var back = FlaxEngine.GUI.Style.Current.TextBoxBackground; - XElement.ValueBox.BorderColor = Color.Lerp(AxisColorX, back, AxisGreyOutFactor); XElement.ValueBox.BorderSelectedColor = AxisColorX; - XElement.ValueBox.Category = Utils.ValueCategory.Angle; - YElement.ValueBox.BorderColor = Color.Lerp(AxisColorY, back, AxisGreyOutFactor); YElement.ValueBox.BorderSelectedColor = AxisColorY; - YElement.ValueBox.Category = Utils.ValueCategory.Angle; - ZElement.ValueBox.BorderColor = Color.Lerp(AxisColorZ, back, AxisGreyOutFactor); ZElement.ValueBox.BorderSelectedColor = AxisColorZ; + XElement.ValueBox.HighlightColor = AxisColorX; + XElement.ValueBox.Category = Utils.ValueCategory.Angle; + YElement.ValueBox.HighlightColor = AxisColorY; + YElement.ValueBox.Category = Utils.ValueCategory.Angle; + ZElement.ValueBox.HighlightColor = AxisColorZ; ZElement.ValueBox.Category = Utils.ValueCategory.Angle; } } @@ -129,17 +128,19 @@ namespace FlaxEditor.CustomEditors.Editors } if (XElement.ValueBox.Parent is UniformGridPanel ug) + { + ug.SlotPadding = new Margin(3.0f, 0.0f, 0.0f, 0.0f); CheckLayout(ug); + } // Override colors var back = FlaxEngine.GUI.Style.Current.TextBoxBackground; - var grayOutFactor = 0.6f; - XElement.ValueBox.BorderColor = Color.Lerp(AxisColorX, back, grayOutFactor); XElement.ValueBox.BorderSelectedColor = AxisColorX; - YElement.ValueBox.BorderColor = Color.Lerp(AxisColorY, back, grayOutFactor); YElement.ValueBox.BorderSelectedColor = AxisColorY; - ZElement.ValueBox.BorderColor = Color.Lerp(AxisColorZ, back, grayOutFactor); ZElement.ValueBox.BorderSelectedColor = AxisColorZ; + XElement.ValueBox.HighlightColor = AxisColorX; + YElement.ValueBox.HighlightColor = AxisColorY; + ZElement.ValueBox.HighlightColor = AxisColorZ; } /// diff --git a/Source/Editor/GUI/Input/ValueBox.cs b/Source/Editor/GUI/Input/ValueBox.cs index 88ec9a4ee..c26761683 100644 --- a/Source/Editor/GUI/Input/ValueBox.cs +++ b/Source/Editor/GUI/Input/ValueBox.cs @@ -89,6 +89,11 @@ namespace FlaxEditor.GUI.Input /// public bool IsSliding => _isSliding; + /// + /// The color of the highlight to the left of the value box. + /// + public Color HighlightColor; + /// /// Occurs when sliding starts. /// @@ -211,6 +216,12 @@ namespace FlaxEditor.GUI.Input Render2D.DrawRectangle(bounds, style.SelectionBorder); } } + + if (HighlightColor != Color.Transparent) + { + var highlightRect = new Rectangle(-3.0f, 0.0f, 3.0f, Height); + Render2D.FillRectangle(highlightRect, HighlightColor); + } } /// diff --git a/Source/Editor/Gizmo/UIEditorGizmo.cs b/Source/Editor/Gizmo/UIEditorGizmo.cs index 765893bed..61109556c 100644 --- a/Source/Editor/Gizmo/UIEditorGizmo.cs +++ b/Source/Editor/Gizmo/UIEditorGizmo.cs @@ -180,6 +180,11 @@ namespace FlaxEditor /// public bool EnableCamera => _view != null && EnableBackground; + /// + /// True if enable grid drawing. + /// + public bool ShowGrid { get; set; } = true; + /// /// Transform gizmo to use sync with (selection, snapping, transformation settings). /// @@ -387,19 +392,53 @@ namespace FlaxEditor if (_mouseMovesWidget && _activeWidget.UIControl) { // Calculate transform delta - var resizeAxisAbs = _activeWidget.ResizeAxis.Absolute; - var resizeAxisPos = Float2.Clamp(_activeWidget.ResizeAxis, Float2.Zero, Float2.One); - var resizeAxisNeg = Float2.Clamp(-_activeWidget.ResizeAxis, Float2.Zero, Float2.One); var delta = location - _mouseMovesPos; // TODO: scale/size snapping? - delta *= resizeAxisAbs; // Resize control via widget var moveLocation = _mouseMovesPos + delta; var control = _activeWidget.UIControl.Control; var uiControlDelta = GetControlDelta(control, ref _mouseMovesPos, ref moveLocation); - control.LocalLocation += uiControlDelta * resizeAxisNeg; - control.Size += uiControlDelta * resizeAxisPos - uiControlDelta * resizeAxisNeg; + + // Transform delta to control local space + var rotation = GetTotalRotation(control) * Mathf.DegreesToRadians; + var cos = Mathf.Cos(rotation); + var sin = Mathf.Sin(rotation); + var localDeltaX = uiControlDelta.X * cos + uiControlDelta.Y * sin; + var localDeltaY = uiControlDelta.Y * cos - uiControlDelta.X * sin; + var localDelta = new Float2(localDeltaX, localDeltaY); + localDelta *= _activeWidget.ResizeAxis.Absolute; + + // Calculate size change + var resizeAxisPos = Float2.Clamp(_activeWidget.ResizeAxis, Float2.Zero, Float2.One); + var resizeAxisNeg = Float2.Clamp(-_activeWidget.ResizeAxis, Float2.Zero, Float2.One); + var dSizeScaled = localDelta * resizeAxisPos - localDelta * resizeAxisNeg; + var scale = control.Scale; + var dSize = new Float2( + Mathf.Abs(scale.X) > Mathf.Epsilon ? dSizeScaled.X / scale.X : 0, + Mathf.Abs(scale.Y) > Mathf.Epsilon ? dSizeScaled.Y / scale.Y : 0); + + // Apply size change + control.Size += dSize; + + // Calculate location offset to keep the opposite edge stationary + // When PivotRelative is false, resizing keeps Top-Left (Location) constant, + // so we only need to slide back if we are resizing Left or Top edges. + if (!control.PivotRelative) + { + var pivotOffset = Float2.Zero; + if (_activeWidget.ResizeAxis.X < 0 && Mathf.Abs(dSize.X) > Mathf.Epsilon) + pivotOffset.X = -dSize.X * scale.X; + if (_activeWidget.ResizeAxis.Y < 0 && Mathf.Abs(dSize.Y) > Mathf.Epsilon) + pivotOffset.Y = -dSize.Y * scale.Y; + + // Transform offset back to parent space and apply + var dLocationX = pivotOffset.X * cos - pivotOffset.Y * sin; + var dLocationY = pivotOffset.X * sin + pivotOffset.Y * cos; + var dLocation = new Float2(dLocationX, dLocationY); + + control.LocalLocation += dLocation; + } // Don't move if layout doesn't allow it if (control.Parent != null) @@ -492,17 +531,20 @@ namespace FlaxEditor // Draw background Surface.VisjectSurface.DrawBackgroundDefault(Editor.Instance.UI.VisjectSurfaceBackground, Width, Height); - // Draw grid - var viewRect = GetClientArea(); - var upperLeft = _view.PointFromParent(viewRect.Location); - var bottomRight = _view.PointFromParent(viewRect.Size); - var min = Float2.Min(upperLeft, bottomRight); - var max = Float2.Max(upperLeft, bottomRight); - var pixelRange = (max - min) * ViewScale; - Render2D.PushClip(ref viewRect); - DrawAxis(Float2.UnitX, viewRect, min.X, max.X, pixelRange.X); - DrawAxis(Float2.UnitY, viewRect, min.Y, max.Y, pixelRange.Y); - Render2D.PopClip(); + if (ShowGrid) + { + // Draw grid + var viewRect = GetClientArea(); + var upperLeft = _view.PointFromParent(viewRect.Location); + var bottomRight = _view.PointFromParent(viewRect.Size); + var min = Float2.Min(upperLeft, bottomRight); + var max = Float2.Max(upperLeft, bottomRight); + var pixelRange = (max - min) * ViewScale; + Render2D.PushClip(ref viewRect); + DrawAxis(Float2.UnitX, viewRect, min.X, max.X, pixelRange.X); + DrawAxis(Float2.UnitY, viewRect, min.Y, max.Y, pixelRange.Y); + Render2D.PopClip(); + } } base.Draw(); @@ -634,7 +676,7 @@ namespace FlaxEditor // Draw sizing widgets if (_widgets == null) _widgets = new List(); - var widgetSize = 10.0f; + var widgetSize = 8.0f; var viewScale = ViewScale; if (viewScale < 0.7f) widgetSize *= viewScale; @@ -685,7 +727,7 @@ namespace FlaxEditor anchorRectSize *= viewScale; // Make anchor rects and rotate if parent is rotated. - var parentRotation = controlParent.Rotation * Mathf.DegreesToRadians; + var parentRotation = GetTotalRotation(controlParent) * Mathf.DegreesToRadians; var rect1Axis = new Float2(-1, -1); var rect1 = new Rectangle(anchorUpperLeft + @@ -717,17 +759,25 @@ namespace FlaxEditor } } + private float GetTotalRotation(Control control) + { + if (control.Parent != null) + return control.Rotation + GetTotalRotation(control.Parent); + return control.Rotation; + } + private void DrawControlWidget(UIControl uiControl, ref Float2 pos, ref Float2 mousePos, ref Float2 size, float scale, Float2 resizeAxis, CursorType cursor) { var style = Style.Current; var control = uiControl.Control; - var rotation = control.Rotation; + var rotation = GetTotalRotation(control); var rotationInRadians = rotation * Mathf.DegreesToRadians; - var rect = new Rectangle((pos + - new Float2(resizeAxis.X * Mathf.Cos(rotationInRadians) - resizeAxis.Y * Mathf.Sin(rotationInRadians), - resizeAxis.Y * Mathf.Cos(rotationInRadians) + resizeAxis.X * Mathf.Sin(rotationInRadians)) * 10 * scale) - size * 0.5f, - size); - + var position = (pos + new Float2(resizeAxis.X * Mathf.Cos(rotationInRadians) - resizeAxis.Y * Mathf.Sin(rotationInRadians), + resizeAxis.Y * Mathf.Cos(rotationInRadians) + resizeAxis.X * Mathf.Sin(rotationInRadians)) * 4 * (scale < 0.7f ? scale : 1)); + var halfSize = size * 0.5f; + // Keep at 0, 0 rect position until later to correctly render rotation. + var rect = new Rectangle(0, 0, size); + // Find more correct cursor at different angles var unwindRotation = Mathf.UnwindDegrees(rotation); if (unwindRotation is (>= 45 and < 135) or (> -135 and <= -45) ) @@ -749,6 +799,10 @@ namespace FlaxEditor default: break; } } + + Render2D.PushTransform(Matrix3x3.Translation2D(position)); + Render2D.PushTransform(Matrix3x3.RotationZ(rotationInRadians)); + Render2D.PushTransform(Matrix3x3.Translation2D(-1 * halfSize)); if (rect.Contains(ref mousePos)) { Render2D.FillRectangle(rect, style.Foreground); @@ -759,9 +813,14 @@ namespace FlaxEditor Render2D.FillRectangle(rect, style.ForegroundGrey); Render2D.DrawRectangle(rect, style.Foreground); } + Render2D.PopTransform(); + Render2D.PopTransform(); + Render2D.PopTransform(); + if (!_mouseMovesWidget && uiControl != null) { // Collect widget + rect.Location = position - halfSize; _widgets.Add(new Widget { UIControl = uiControl, diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index 7f2b1a471..a8bdc8f20 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -6,7 +6,6 @@ using System.Linq; using FlaxEditor.Content; using FlaxEditor.Gizmo; using FlaxEditor.GUI.ContextMenu; -using FlaxEditor.GUI.Input; using FlaxEditor.Modules; using FlaxEditor.SceneGraph; using FlaxEditor.Scripting; @@ -15,7 +14,6 @@ using FlaxEditor.Viewport.Previews; using FlaxEditor.Windows.Assets; using FlaxEngine; using FlaxEngine.GUI; -using FlaxEngine.Json; using Utils = FlaxEditor.Utilities.Utils; namespace FlaxEditor.Viewport @@ -185,6 +183,7 @@ namespace FlaxEditor.Viewport showGridButton.Clicked += () => { _gridGizmo.Enabled = !_gridGizmo.Enabled; + _uiRoot.ShowGrid = _gridGizmo.Enabled; showGridButton.Checked = _gridGizmo.Enabled; }; showGridButton.Checked = true; diff --git a/Source/Editor/Windows/AboutDialog.cs b/Source/Editor/Windows/AboutDialog.cs index af0d121c2..bd34802ce 100644 --- a/Source/Editor/Windows/AboutDialog.cs +++ b/Source/Editor/Windows/AboutDialog.cs @@ -2,6 +2,7 @@ //#define USE_AUTODESK_FBX_SDK using System.Collections.Generic; +using System.Reflection; using FlaxEditor.GUI.Dialogs; using FlaxEngine; using FlaxEngine.GUI; @@ -45,9 +46,16 @@ namespace FlaxEditor.Windows VerticalAlignment = TextAlignment.Center, Parent = this }; + var assembly = typeof(Editor).Assembly; + var assemblyCopyright = assembly.GetCustomAttribute(); + var assemblyInformationalVersion = assembly.GetCustomAttribute(); + var versionParts = assemblyInformationalVersion.InformationalVersion.Split('+'); + string versionInfo = string.Empty; + if (versionParts.Length == 3) + versionInfo = $"\nBranch: {versionParts[1]}+{(versionParts[2].Length == 40 ? versionParts[2].Substring(0, 8) : versionParts[2])}"; new Label(nameLabel.Left, nameLabel.Bottom + 4, nameLabel.Width, 50) { - Text = string.Format("Version: {0}\nCopyright (c) 2012-2025 Wojciech Figat.\nAll rights reserved.", Globals.EngineVersion), + Text = $"Version: {Globals.EngineVersion}{versionInfo}\n{assemblyCopyright.Copyright.Replace(". ", ".\n")}", HorizontalAlignment = TextAlignment.Near, VerticalAlignment = TextAlignment.Near, Parent = this diff --git a/Source/Engine/Engine/Engine.cpp b/Source/Engine/Engine/Engine.cpp index 5e8224b4e..15b3a1932 100644 --- a/Source/Engine/Engine/Engine.cpp +++ b/Source/Engine/Engine/Engine.cpp @@ -595,7 +595,11 @@ void EngineImpl::InitLog() #if COMPILE_WITH_DEV_ENV LOG(Info, "Compiled for Dev Environment"); #endif +#if defined(FLAXENGINE_BRANCH) && defined(FLAXENGINE_COMMIT) + LOG(Info, "Version " FLAXENGINE_VERSION_TEXT ", " FLAXENGINE_BRANCH ", " FLAXENGINE_COMMIT); +#else LOG(Info, "Version " FLAXENGINE_VERSION_TEXT); +#endif const Char* cpp = TEXT("?"); if (__cplusplus == 202101L) cpp = TEXT("C++23"); else if (__cplusplus == 202002L) cpp = TEXT("C++20"); diff --git a/Source/Engine/Level/Actors/PointLight.cpp b/Source/Engine/Level/Actors/PointLight.cpp index 607bf1bc4..b5801166e 100644 --- a/Source/Engine/Level/Actors/PointLight.cpp +++ b/Source/Engine/Level/Actors/PointLight.cpp @@ -1,6 +1,7 @@ // Copyright (c) Wojciech Figat. All rights reserved. #include "PointLight.h" +#include "Engine/Content/Deprecated.h" #include "Engine/Graphics/RenderTask.h" #include "Engine/Graphics/RenderTools.h" #include "Engine/Graphics/RenderView.h" @@ -196,6 +197,14 @@ void PointLight::Deserialize(DeserializeStream& stream, ISerializeModifier* modi DESERIALIZE(UseInverseSquaredFalloff); DESERIALIZE(UseIESBrightness); DESERIALIZE(IESBrightnessScale); + + // [Deprecated on 12.03.2026, expires on 12.03.2028] + if (modifier->EngineBuild <= 6807 && SERIALIZE_FIND_MEMBER(stream, "UseInverseSquaredFalloff") != stream.MemberEnd() && UseInverseSquaredFalloff) + { + // Convert old non-physical brightness value that was used for Inverse Squared Falloff which wasn't based on proper cm/m units calculations + MARK_CONTENT_DEPRECATED(); + Brightness = Math::Sqrt(Brightness * 0.01f); + } } bool PointLight::IntersectsItself(const Ray& ray, Real& distance, Vector3& normal) diff --git a/Source/Engine/Level/Actors/SpotLight.cpp b/Source/Engine/Level/Actors/SpotLight.cpp index 85b77647a..38502cf1b 100644 --- a/Source/Engine/Level/Actors/SpotLight.cpp +++ b/Source/Engine/Level/Actors/SpotLight.cpp @@ -1,6 +1,8 @@ // Copyright (c) Wojciech Figat. All rights reserved. #include "SpotLight.h" + +#include "Engine/Content/Deprecated.h" #include "Engine/Graphics/RenderView.h" #include "Engine/Renderer/RenderList.h" #include "Engine/Content/Assets/IESProfile.h" @@ -282,6 +284,14 @@ void SpotLight::Deserialize(DeserializeStream& stream, ISerializeModifier* modif DESERIALIZE(UseInverseSquaredFalloff); DESERIALIZE(UseIESBrightness); DESERIALIZE(IESBrightnessScale); + + // [Deprecated on 12.03.2026, expires on 12.03.2028] + if (modifier->EngineBuild <= 6807 && SERIALIZE_FIND_MEMBER(stream, "UseInverseSquaredFalloff") != stream.MemberEnd() && UseInverseSquaredFalloff) + { + // Convert old non-physical brightness value that was used for Inverse Squared Falloff which wasn't based on proper cm/m units calculations + MARK_CONTENT_DEPRECATED(); + Brightness = Math::Sqrt(Brightness * 0.01f); + } } bool SpotLight::IntersectsItself(const Ray& ray, Real& distance, Vector3& normal) diff --git a/Source/Engine/Level/Level.cpp b/Source/Engine/Level/Level.cpp index 956005f57..a46ee52f6 100644 --- a/Source/Engine/Level/Level.cpp +++ b/Source/Engine/Level/Level.cpp @@ -958,9 +958,6 @@ bool LevelImpl::unloadScene(Scene* scene) // Simple enqueue scene root object to be deleted scene->DeleteObject(); - // Force flush deleted objects so we actually delete unloaded scene objects (prevent from reloading their managed objects, etc.) - ObjectsRemovalService::Flush(); - return false; } diff --git a/Source/Engine/Level/Scene/Scene.cpp b/Source/Engine/Level/Scene/Scene.cpp index f60aa8d2f..b45424a3d 100644 --- a/Source/Engine/Level/Scene/Scene.cpp +++ b/Source/Engine/Level/Scene/Scene.cpp @@ -389,11 +389,14 @@ void Scene::BeginPlay(SceneBeginData* data) if (model == nullptr) CreateCsgModel(); } + + Ticking.SetTicking(true); } void Scene::EndPlay() { // Improve scene cleanup performance by removing all data from scene rendering and ticking containers + Ticking.SetTicking(false); Ticking.Clear(); Rendering.Clear(); Navigation.Clear(); diff --git a/Source/Engine/Level/Scene/SceneTicking.cpp b/Source/Engine/Level/Scene/SceneTicking.cpp index 30a551117..2b0ccc506 100644 --- a/Source/Engine/Level/Scene/SceneTicking.cpp +++ b/Source/Engine/Level/Scene/SceneTicking.cpp @@ -44,7 +44,7 @@ void SceneTicking::TickData::Tick() { TickScripts(Scripts); - for (int32 i = 0; i < Ticks.Count(); i++) + for (int32 i = 0; i < Ticks.Count() && _canTick; i++) Ticks.Get()[i].Call(); } @@ -66,7 +66,7 @@ void SceneTicking::TickData::TickExecuteInEditor() { TickScripts(ScriptsExecuteInEditor); - for (int32 i = 0; i < TicksExecuteInEditor.Count(); i++) + for (int32 i = 0; i < TicksExecuteInEditor.Count() && _canTick; i++) TicksExecuteInEditor.Get()[i].Call(); } @@ -89,10 +89,8 @@ SceneTicking::FixedUpdateTickData::FixedUpdateTickData() void SceneTicking::FixedUpdateTickData::TickScripts(const Array& scripts) { - for (auto* script : scripts) - { - script->OnFixedUpdate(); - } + for (int32 i = 0; i < scripts.Count() && _canTick; i++) + scripts.Get()[i]->OnFixedUpdate(); } SceneTicking::UpdateTickData::UpdateTickData() @@ -102,36 +100,30 @@ SceneTicking::UpdateTickData::UpdateTickData() void SceneTicking::UpdateTickData::TickScripts(const Array& scripts) { - for (auto* script : scripts) - { - script->OnUpdate(); - } + for (int32 i = 0; i < scripts.Count() && _canTick; i++) + scripts.Get()[i]->OnUpdate(); } SceneTicking::LateUpdateTickData::LateUpdateTickData() - : TickData(64) + : TickData(0) { } void SceneTicking::LateUpdateTickData::TickScripts(const Array& scripts) { - for (auto* script : scripts) - { - script->OnLateUpdate(); - } + for (int32 i = 0; i < scripts.Count() && _canTick; i++) + scripts.Get()[i]->OnLateUpdate(); } SceneTicking::LateFixedUpdateTickData::LateFixedUpdateTickData() - : TickData(64) + : TickData(0) { } void SceneTicking::LateFixedUpdateTickData::TickScripts(const Array& scripts) { - for (auto* script : scripts) - { - script->OnLateFixedUpdate(); - } + for (int32 i = 0; i < scripts.Count() && _canTick; i++) + scripts.Get()[i]->OnLateFixedUpdate(); } void SceneTicking::AddScript(Script* obj) @@ -167,3 +159,11 @@ void SceneTicking::Clear() LateUpdate.Clear(); LateFixedUpdate.Clear(); } + +void SceneTicking::SetTicking(bool enable) +{ + FixedUpdate._canTick = enable; + Update._canTick = enable; + LateUpdate._canTick = enable; + LateFixedUpdate._canTick = enable; +} diff --git a/Source/Engine/Level/Scene/SceneTicking.h b/Source/Engine/Level/Scene/SceneTicking.h index 78e028b8e..26040852b 100644 --- a/Source/Engine/Level/Scene/SceneTicking.h +++ b/Source/Engine/Level/Scene/SceneTicking.h @@ -46,6 +46,9 @@ public: /// class FLAXENGINE_API TickData { + protected: + friend SceneTicking; + bool _canTick = true; public: Array Scripts; Array Ticks; @@ -134,6 +137,11 @@ public: /// void Clear(); + /// + /// Changes the ability to tick. When disabled, the ticking functions won't be called. Can be called during ticking (eg. when scene is unloaded) to stp performing any more ticks. + /// + void SetTicking(bool enable); + public: /// /// The fixed update tick function. diff --git a/Source/Engine/Render2D/Render2D.cpp b/Source/Engine/Render2D/Render2D.cpp index 5ae034bc5..689c1a3c1 100644 --- a/Source/Engine/Render2D/Render2D.cpp +++ b/Source/Engine/Render2D/Render2D.cpp @@ -1440,8 +1440,28 @@ void Render2D::DrawRectangle(const Rectangle& rect, const Color& color1, const C RENDER2D_CHECK_RENDERING_STATE; const auto& mask = ClipLayersStack.Peek().Mask; + float thick = thickness; thickness *= (TransformCached.M11 + TransformCached.M22 + TransformCached.M33) * 0.3333333f; + // When lines thickness is very large, don't use corner caps and place line ends to not overlap + if (thickness > 4.0f) + { + thick *= Math::Lerp(0.6f, 1.0f, Math::Saturate(thick - 4.0f)); // Smooth transition between soft LineAA and harsh FillRect + Float2 totalMin = rect.GetUpperLeft() - Float2(thick * 0.5f); + Float2 totalMax = rect.GetBottomRight() + Float2(thick * 0.5f); + Float2 size = totalMax - totalMin; + Render2DDrawCall& drawCall = DrawCalls.AddOne(); + drawCall.Type = NeedAlphaWithTint(color1, color2, color3, color4) ? DrawCallType::FillRect : DrawCallType::FillRectNoAlpha; + drawCall.StartIB = IBIndex; + drawCall.CountIB = 6 * 4; + // TODO: interpolate colors from corners to extended rectangle edges properly + WriteRect(Rectangle(totalMin.X, totalMin.Y, size.X, thick), color1, color2, color2, color1); + WriteRect(Rectangle(totalMin.X, totalMin.Y + rect.Size.Y, size.X, thick), color4, color3, color3, color4); + WriteRect(Rectangle(totalMin.X, totalMin.Y + thick, thick, rect.Size.Y - thick), color1, color1, color4, color4); + WriteRect(Rectangle(totalMax.X - thick, totalMin.Y + thick, thick, rect.Size.Y - thick), color2, color2, color3, color3); + return; + } + Float2 points[5]; ApplyTransform(rect.GetUpperLeft(), points[0]); ApplyTransform(rect.GetUpperRight(), points[1]); diff --git a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp index 25550ecd8..37d268991 100644 --- a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp +++ b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp @@ -90,6 +90,7 @@ public: Float3 ProbesOrigin; float ProbesSpacing = 0.0f; Int3 ProbeScrollOffsets; + bool PendingUpdate = true; Int3 ProbeScrollClears; void Clear() @@ -97,6 +98,7 @@ public: ProbesOrigin = Float3::Zero; ProbeScrollOffsets = Int3::Zero; ProbeScrollClears = Int3::Zero; + PendingUpdate = true; } } Cascades[4]; @@ -457,9 +459,12 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont //const uint64 cascadeFrequencies[] = { 1, 1, 1, 1 }; //const uint64 cascadeFrequencies[] = { 10, 10, 10, 10 }; bool cascadeSkipUpdate[4]; + int32 maxCascadesPerFrame = renderContext.View.IsSingleFrame ? cascadesCount : 2; for (int32 cascadeIndex = 0; cascadeIndex < cascadesCount; cascadeIndex++) { - cascadeSkipUpdate[cascadeIndex] = !clear && (ddgiData.LastFrameUsed % cascadeFrequencies[cascadeIndex]) != 0 && GPU_SPREAD_WORKLOAD; + auto& cascade = ddgiData.Cascades[cascadeIndex]; + cascade.PendingUpdate |= !clear && (ddgiData.LastFrameUsed % cascadeFrequencies[cascadeIndex]) != 0 && GPU_SPREAD_WORKLOAD; + cascadeSkipUpdate[cascadeIndex] = !cascade.PendingUpdate || maxCascadesPerFrame-- <= 0; } // Compute scrolling (probes are placed around camera but are scrolling to increase stability during movement) @@ -468,6 +473,7 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont if (cascadeSkipUpdate[cascadeIndex]) continue; auto& cascade = ddgiData.Cascades[cascadeIndex]; + cascade.PendingUpdate = false; // Calculate the count of grid cells between the view origin and the scroll anchor const Float3 volumeOrigin = cascade.ProbesOrigin + Float3(cascade.ProbeScrollOffsets) * cascade.ProbesSpacing; diff --git a/Source/Engine/Tools/TextureTool/TextureTool.DirectXTex.cpp b/Source/Engine/Tools/TextureTool/TextureTool.DirectXTex.cpp index d7199a470..be38f0015 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.DirectXTex.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.DirectXTex.cpp @@ -722,6 +722,10 @@ bool TextureTool::ImportTextureDirectXTex(ImageType type, const StringView& path errorMsg = String::Format(TEXT("Imported texture has not full mip chain, loaded mips count: {0}, expected: {1}"), sourceMipLevels, mipLevels); return true; } + if (options.GenerateMipMaps && !isPowerOfTwo) + { + LOG(Warning, "Cannot generate mip maps for texture '{}' that size is not power of two. Use Resize or Max Size to change dimensions.", StringUtils::GetFileName(path), width, height); + } // Allocate memory for texture data auto& data = textureData.Items; diff --git a/Source/Engine/Tools/TextureTool/TextureTool.stb.cpp b/Source/Engine/Tools/TextureTool/TextureTool.stb.cpp index 7c74ef3f2..544877232 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.stb.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.stb.cpp @@ -543,6 +543,10 @@ bool TextureTool::ImportTextureStb(ImageType type, const StringView& path, Textu errorMsg = String::Format(TEXT("Imported texture has not full mip chain, loaded mips count: {0}, expected: {1}"), sourceMipLevels, mipLevels); return true; } + if (options.GenerateMipMaps && !isPowerOfTwo) + { + LOG(Warning, "Cannot generate mip maps for texture '{}' that size is not power of two. Use Resize or Max Size to change dimensions.", StringUtils::GetFileName(path), width, height); + } // Decompress if texture is compressed (next steps need decompressed input data, for eg. mip maps generation or format changing) if (PixelFormatExtensions::IsCompressed(textureDataSrc->Format)) diff --git a/Source/Engine/UI/GUI/Control.Bounds.cs b/Source/Engine/UI/GUI/Control.Bounds.cs index 62bffd6f7..079530c50 100644 --- a/Source/Engine/UI/GUI/Control.Bounds.cs +++ b/Source/Engine/UI/GUI/Control.Bounds.cs @@ -166,8 +166,26 @@ namespace FlaxEngine.GUI [NoSerialize, HideInEditor] public Float2 LocalLocation { - get => _bounds.Location - (_parent != null ? _parent._bounds.Size * (_anchorMax + _anchorMin) * 0.5f : Float2.Zero) + _bounds.Size * _pivot; - set => Bounds = new Rectangle(value + (_parent != null ? _parent.Bounds.Size * (_anchorMax + _anchorMin) * 0.5f : Float2.Zero) - _bounds.Size * _pivot, _bounds.Size); + get + { + var anchor = Float2.Zero; + if (_parent != null) + { + _parent.GetDesireClientArea(out var parentBounds); + anchor = parentBounds.Location + parentBounds.Size * (_anchorMin + _anchorMax) * 0.5f; + } + return _bounds.Location - anchor + _bounds.Size * _pivot; + } + set + { + var anchor = Float2.Zero; + if (_parent != null) + { + _parent.GetDesireClientArea(out var parentBounds); + anchor = parentBounds.Location + parentBounds.Size * (_anchorMin + _anchorMax) * 0.5f; + } + Bounds = new Rectangle(value + anchor - _bounds.Size * _pivot, _bounds.Size); + } } /// diff --git a/Source/Engine/UI/GUI/ScrollableControl.cs b/Source/Engine/UI/GUI/ScrollableControl.cs index 4977ac89b..7caa211cf 100644 --- a/Source/Engine/UI/GUI/ScrollableControl.cs +++ b/Source/Engine/UI/GUI/ScrollableControl.cs @@ -6,7 +6,6 @@ namespace FlaxEngine.GUI /// Base class for container controls that can offset controls in a view (eg. scroll panels). /// /// - [HideInEditor] public class ScrollableControl : ContainerControl { /// diff --git a/Source/Shaders/Common.hlsl b/Source/Shaders/Common.hlsl index 66303546a..2052214b2 100644 --- a/Source/Shaders/Common.hlsl +++ b/Source/Shaders/Common.hlsl @@ -131,6 +131,7 @@ SamplerComparisonState ShadowSamplerLinear : register(s5); #define SAMPLE_RT_LINEAR(rt, texCoord) rt.SampleLevel(SamplerLinearClamp, texCoord, 0) #define HDR_CLAMP_MAX 65472.0 #define PI 3.1415926535897932 +#define UNITS_TO_METERS_SCALE 0.01f // Structure that contains information about GBuffer struct GBufferData diff --git a/Source/Shaders/GI/DDGI.shader b/Source/Shaders/GI/DDGI.shader index b080efc0b..bade984fc 100644 --- a/Source/Shaders/GI/DDGI.shader +++ b/Source/Shaders/GI/DDGI.shader @@ -351,6 +351,7 @@ META_CS(true, FEATURE_LEVEL_SM5) void CS_UpdateProbesInitArgs() { uint activeProbesCount = ActiveProbes.Load(0); // Counter at 0 + activeProbesCount = min(activeProbesCount, ProbesCount); uint arg = 0; for (uint probesOffset = 0; probesOffset < activeProbesCount; probesOffset += DDGI_TRACE_RAYS_PROBES_COUNT_LIMIT) { diff --git a/Source/Shaders/LightingCommon.hlsl b/Source/Shaders/LightingCommon.hlsl index f09572310..734b9b709 100644 --- a/Source/Shaders/LightingCommon.hlsl +++ b/Source/Shaders/LightingCommon.hlsl @@ -68,20 +68,25 @@ void GetRadialLightAttenuation( // Distance attenuation if (lightData.InverseSquared) { + // Convert scene units to meters for inverse-squared falloff + const float distanceScale = UNITS_TO_METERS_SCALE; + const float distanceScaleSqr = distanceScale * distanceScale; + float distanceSqrScaled = distanceSqr * distanceScaleSqr; + float distanceBiasSqrScaled = distanceBiasSqr * distanceScaleSqr; BRANCH if (lightData.SourceLength > 0) { - float3 l01 = lightData.Direction * lightData.SourceLength; - float3 l0 = toLight - 0.5 * l01; - float3 l1 = toLight + 0.5 * l01; + float3 l01 = lightData.Direction * (lightData.SourceLength * distanceScale); + float3 l0 = (toLight * distanceScale) - 0.5 * l01; + float3 l1 = (toLight * distanceScale) + 0.5 * l01; float lengthL0 = length(l0); float lengthL1 = length(l1); - attenuation = rcp((lengthL0 * lengthL1 + dot(l0, l1)) * 0.5 + distanceBiasSqr); + attenuation = rcp((lengthL0 * lengthL1 + dot(l0, l1)) * 0.5 + distanceBiasSqrScaled); NoL = saturate(0.5 * (dot(N, l0) / lengthL0 + dot(N, l1) / lengthL1)); } else { - attenuation = rcp(distanceSqr + distanceBiasSqr); + attenuation = rcp(distanceSqrScaled + distanceBiasSqrScaled); NoL = saturate(dot(N, L)); } attenuation *= Square(saturate(1 - Square(distanceSqr * Square(lightData.RadiusInv)))); diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 034803d25..174c6de2c 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -2429,7 +2429,7 @@ namespace Flax.Build.Bindings contents.AppendLine(); contents.AppendLine($"[assembly: AssemblyTitle(\"{binaryModuleName}\")]"); contents.AppendLine("[assembly: AssemblyDescription(\"\")]"); - contents.AppendLine("[assembly: AssemblyConfiguration(\"\")]"); + contents.AppendLine($"[assembly: AssemblyConfiguration(\"{buildData.Configuration}\")]"); contents.AppendLine($"[assembly: AssemblyCompany(\"{project.Company}\")]"); contents.AppendLine("[assembly: AssemblyProduct(\"FlaxEngine\")]"); contents.AppendLine($"[assembly: AssemblyCopyright(\"{project.Copyright}\")]"); @@ -2439,6 +2439,7 @@ namespace Flax.Build.Bindings contents.AppendLine($"[assembly: Guid(\"{id:d}\")]"); contents.AppendLine($"[assembly: AssemblyVersion(\"{project.Version}\")]"); contents.AppendLine($"[assembly: AssemblyFileVersion(\"{project.Version}\")]"); + contents.AppendLine($"[assembly: AssemblyInformationalVersion(\"{project.VersionControlInfo}\")]"); #if USE_NETCORE contents.AppendLine("[assembly: DisableRuntimeMarshalling]"); #endif diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index f1c4c6be8..ce557be20 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -3289,6 +3289,10 @@ namespace Flax.Build.Bindings contents.AppendLine($"#define {binaryModuleNameUpper}_VERSION_REVISION {version.Revision}"); contents.AppendLine($"#define {binaryModuleNameUpper}_COMPANY \"{project.Company}\""); contents.AppendLine($"#define {binaryModuleNameUpper}_COPYRIGHT \"{project.Copyright}\""); + if (project.VersionControlBranch.Length != 0) + contents.AppendLine($"#define {binaryModuleNameUpper}_BRANCH \"{project.VersionControlBranch}\""); + if (project.VersionControlCommit.Length != 0) + contents.AppendLine($"#define {binaryModuleNameUpper}_COMMIT \"{project.VersionControlCommit}\""); contents.AppendLine(); contents.AppendLine("class BinaryModule;"); contents.AppendLine($"extern \"C\" {binaryModuleNameUpper}_API BinaryModule* GetBinaryModule{binaryModuleName}();"); diff --git a/Source/Tools/Flax.Build/ProjectInfo.cs b/Source/Tools/Flax.Build/ProjectInfo.cs index 44065015d..00bdbf938 100644 --- a/Source/Tools/Flax.Build/ProjectInfo.cs +++ b/Source/Tools/Flax.Build/ProjectInfo.cs @@ -145,6 +145,7 @@ namespace Flax.Build public sealed class ProjectInfo { private static List _projectsCache; + private string _versionControlCommit, _versionControlBranch; /// /// The project reference. @@ -232,6 +233,51 @@ namespace Flax.Build [System.Text.Json.Serialization.JsonConverter(typeof(ConfigurationDictionaryConverter))] public Dictionary Configuration; + /// + /// Gets the name of the branch from Version Control System (VCS) used by the project. Empty when unused. + /// + public string VersionControlBranch + { + get + { + if (_versionControlBranch == null) + InitVersionControlInfo(); + return _versionControlBranch; + } + } + + /// + /// Gets the commit hash/changeset identifier from Version Control System (VCS) used by the project. Empty when unused. + /// + public string VersionControlCommit + { + get + { + if (_versionControlCommit == null) + InitVersionControlInfo(); + return _versionControlCommit; + } + } + + /// + /// Gets the informative version of the project including any Version Control System (VCS) information such as branch name, commit hash or changeset identifier. + /// + public string VersionControlInfo + { + + get + { + if (_versionControlCommit == null) + InitVersionControlInfo(); + var version = Version.ToString(); + if (_versionControlBranch.Length != 0) + version += "+" + _versionControlBranch; + if (_versionControlCommit.Length != 0) + version += "+" + _versionControlCommit; + return version; + } + } + /// /// True if project is using C#-only and no native toolsets is required to build and use scripts. /// @@ -267,6 +313,26 @@ namespace Flax.Build }); } + private void InitVersionControlInfo() + { + _versionControlBranch = string.Empty; + _versionControlCommit = string.Empty; + + // Git + if (Directory.Exists(Path.Combine(ProjectFolderPath, ".git"))) + { + try + { + _versionControlBranch = Utilities.ReadProcessOutput("git", "rev-parse --abbrev-ref HEAD", ProjectFolderPath); + _versionControlCommit = Utilities.ReadProcessOutput("git", "rev-parse HEAD", ProjectFolderPath); + } + catch (Exception) + { + // Ignored + } + } + } + /// /// Gets all projects including this project, it's references and their references (any deep level of references). /// diff --git a/Source/Tools/Flax.Build/Utilities/Utilities.cs b/Source/Tools/Flax.Build/Utilities/Utilities.cs index 872b269ee..cb55b154a 100644 --- a/Source/Tools/Flax.Build/Utilities/Utilities.cs +++ b/Source/Tools/Flax.Build/Utilities/Utilities.cs @@ -587,8 +587,9 @@ namespace Flax.Build /// /// The executable file path. /// The custom arguments. + /// The custom folder to run program in it. /// Returned process output. - public static string ReadProcessOutput(string filename, string args = null) + public static string ReadProcessOutput(string filename, string args = null, string workspace = null) { Process p = new Process { @@ -599,6 +600,7 @@ namespace Flax.Build UseShellExecute = false, CreateNoWindow = true, RedirectStandardOutput = true, + WorkingDirectory = workspace, } }; p.Start();