From 0d5d11e184045edc9660a21fe8154718191b8422 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Wed, 20 Dec 2023 10:03:38 -0600 Subject: [PATCH 01/36] Add selecting and starting rename on actors spawned in the prefab window. --- Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs index 478439e0e..f263d4734 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs @@ -386,6 +386,7 @@ namespace FlaxEditor.Windows.Assets // Spawn it Spawn(actor); + Rename(); } /// @@ -415,6 +416,7 @@ namespace FlaxEditor.Windows.Assets // Create undo action var action = new CustomDeleteActorsAction(new List(1) { actorNode }, true); Undo.AddAction(action); + Select(actorNode); } private void OnTreeRightClick(TreeNode node, Float2 location) From 435341abd3778afca9f99b8ff898588bb58cd408 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Wed, 20 Dec 2023 10:51:38 -0600 Subject: [PATCH 02/36] Add color box to `ColorTrackBall`. --- .../CustomEditors/Editors/ColorTrackball.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/ColorTrackball.cs b/Source/Editor/CustomEditors/Editors/ColorTrackball.cs index 8de972f86..836a1e9b8 100644 --- a/Source/Editor/CustomEditors/Editors/ColorTrackball.cs +++ b/Source/Editor/CustomEditors/Editors/ColorTrackball.cs @@ -3,6 +3,7 @@ using System.Linq; using FlaxEditor.CustomEditors.Elements; using FlaxEditor.GUI.Dialogs; +using FlaxEditor.GUI.Input; using FlaxEngine; using FlaxEngine.GUI; @@ -17,6 +18,7 @@ namespace FlaxEditor.CustomEditors.Editors private FloatValueElement _yElement; private FloatValueElement _zElement; private FloatValueElement _wElement; + private ColorValueBox _colorBox; private CustomElement _trackball; /// @@ -53,7 +55,7 @@ namespace FlaxEditor.CustomEditors.Editors gridControl.SlotPadding = new Margin(4, 2, 2, 2); gridControl.ClipChildren = false; gridControl.SlotsHorizontally = 1; - gridControl.SlotsVertically = 4; + gridControl.SlotsVertically = 5; LimitAttribute limit = null; var attributes = Values.GetAttributes(); @@ -61,7 +63,8 @@ namespace FlaxEditor.CustomEditors.Editors { limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute); } - + _colorBox = grid.Custom().CustomControl; + _colorBox.ValueChanged += OnColorBoxChanged; _xElement = CreateFloatEditor(grid, limit, Color.Red); _yElement = CreateFloatEditor(grid, limit, Color.Green); _zElement = CreateFloatEditor(grid, limit, Color.Blue); @@ -93,6 +96,13 @@ namespace FlaxEditor.CustomEditors.Editors SetValue(value, token); } + private void OnColorBoxChanged() + { + var token = _colorBox.IsSliding ? this : null; + var color = _colorBox.Value; + SetValue(new Float4(color.R, color.G, color.B, color.A), token); + } + private void OnValueChanged() { if (IsSetBlocked) @@ -130,6 +140,7 @@ namespace FlaxEditor.CustomEditors.Editors _yElement.Value = color.Y; _zElement.Value = color.Z; _wElement.Value = scale; + _colorBox.Value = new Color(color.X, color.Y, color.Z, scale); _trackball.CustomControl.Color = Float3.Abs(color); } } From 35becc674a449956c49ab6086ffb05ff16ce698d Mon Sep 17 00:00:00 2001 From: Preben Eriksen Date: Mon, 25 Dec 2023 12:08:34 +0100 Subject: [PATCH 03/36] PE: Fix problem with Procedural Texture Sample flicker if using scaled UVs. --- .../MaterialGenerator.Textures.cpp | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp index ed79b5edc..475809b6c 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp @@ -584,7 +584,7 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value) { // Procedural Texture Sample textureBox->Cache = writeLocal(Value::InitForZero(ValueType::Float4), node); - createGradients(node); + // createGradients(node); //PE: Not needed should always use the scaled or not scaled uv. auto proceduralSample = String::Format(TEXT( " {{\n" " float3 weights;\n" @@ -613,19 +613,19 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value) " uv1 = {0} + frac(sin(mul(float2x2(127.1, 311.7, 269.5, 183.3), vertex1)) * 43758.5453);\n" " uv2 = {0} + frac(sin(mul(float2x2(127.1, 311.7, 269.5, 183.3), vertex2)) * 43758.5453);\n" " uv3 = {0} + frac(sin(mul(float2x2(127.1, 311.7, 269.5, 183.3), vertex3)) * 43758.5453);\n" - " float4 tex1 = {1}.SampleGrad({4}, uv1, {2}, {3}, {6}) * weights.x;\n" - " float4 tex2 = {1}.SampleGrad({4}, uv2, {2}, {3}, {6}) * weights.y;\n" - " float4 tex3 = {1}.SampleGrad({4}, uv3, {2}, {3}, {6}) * weights.z;\n" - " {5} = tex1 + tex2 + tex3;\n" + " float2 fdx = ddx({0});\n" + " float2 fdy = ddy({0});\n" + " float4 tex1 = {1}.SampleGrad({2}, uv1, fdx, fdy, {4}) * weights.x;\n" + " float4 tex2 = {1}.SampleGrad({2}, uv2, fdx, fdy, {4}) * weights.y;\n" + " float4 tex3 = {1}.SampleGrad({2}, uv3, fdx, fdy, {4}) * weights.z;\n" + " {3} = tex1 + tex2 + tex3;\n" " }}\n" ), uvs.Value, // {0} texture.Value, // {1} - _ddx.Value, // {2} - _ddy.Value, // {3} - samplerName, // {4} - textureBox->Cache.Value, // {5} - offset.Value // {6} + samplerName, // {2} + textureBox->Cache.Value, // {3} + offset.Value // {4} ); _writer.Write(*proceduralSample); From 55066cd738c7609e65757e353aa637bb1303d067 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 26 Dec 2023 13:12:51 +0100 Subject: [PATCH 04/36] Add red tint highlight for CPU profiler table entries based on event duration --- Source/Editor/GUI/Row.cs | 8 ++++++++ Source/Editor/Windows/Profiler/CPU.cs | 12 +++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/Source/Editor/GUI/Row.cs b/Source/Editor/GUI/Row.cs index f6bd5b02a..ca45ec6f5 100644 --- a/Source/Editor/GUI/Row.cs +++ b/Source/Editor/GUI/Row.cs @@ -24,6 +24,11 @@ namespace FlaxEditor.GUI /// public object[] Values { get; set; } + /// + /// Gets or sets the cell background colors. Null if unused, transparent values are ignored. + /// + public Color[] BackgroundColors { get; set; } + /// /// Gets or sets the row depth level. /// @@ -58,6 +63,7 @@ namespace FlaxEditor.GUI { float x = 0; int end = Mathf.Min(Values.Length, _table.Columns.Length); + var backgroundColors = BackgroundColors; for (int i = 0; i < end; i++) { var column = _table.Columns[i]; @@ -98,6 +104,8 @@ namespace FlaxEditor.GUI rect.Width -= leftDepthMargin; Render2D.PushClip(rect); + if (backgroundColors != null && backgroundColors[i].A > 0) + Render2D.FillRectangle(rect, backgroundColors[i]); Render2D.DrawText(style.FontMedium, text, rect, style.Foreground, column.CellAlignment, TextAlignment.Center); Render2D.PopClip(); diff --git a/Source/Editor/Windows/Profiler/CPU.cs b/Source/Editor/Windows/Profiler/CPU.cs index 0cbb3fa9b..ba569f04f 100644 --- a/Source/Editor/Windows/Profiler/CPU.cs +++ b/Source/Editor/Windows/Profiler/CPU.cs @@ -452,7 +452,6 @@ namespace FlaxEditor.Windows.Profiler var data = _events.Get(_mainChart.SelectedSampleIndex); if (data == null || data.Length == 0) return; - float totalTimeMs = _mainChart.SelectedSample; // Add rows @@ -501,17 +500,24 @@ namespace FlaxEditor.Windows.Profiler row = new Row { Values = new object[6], + BackgroundColors = new Color[6], }; + for (int k = 0; k < row.BackgroundColors.Length; k++) + row.BackgroundColors[k] = Color.Transparent; } { // Event row.Values[0] = name; // Total (%) - row.Values[1] = (int)(time / totalTimeMs * 1000.0f) / 10.0f; + float rowTotalTimePerc = (float)(time / totalTimeMs); + row.Values[1] = (int)(rowTotalTimePerc * 1000.0f) / 10.0f; + row.BackgroundColors[1] = Color.Red.AlphaMultiplied(Mathf.Min(1, rowTotalTimePerc) * 0.5f); // Self (%) - row.Values[2] = (int)((time - subEventsTimeTotal) / time * 1000.0f) / 10.0f; + float rowSelfTimePerc = (float)((time - subEventsTimeTotal) / totalTimeMs); + row.Values[2] = (int)(rowSelfTimePerc * 1000.0f) / 10.0f; + row.BackgroundColors[2] = Color.Red.AlphaMultiplied(Mathf.Min(1, rowSelfTimePerc) * 0.5f); // Time ms row.Values[3] = (float)((time * 10000.0f) / 10000.0f); From 4a42aa8c443555e741259f566753f3184aa55c73 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 26 Dec 2023 13:54:12 +0100 Subject: [PATCH 05/36] Fix fog to be usable for transparent materials (if enabled) #1408 --- Content/Editor/MaterialTemplates/Features/ForwardShading.hlsl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Content/Editor/MaterialTemplates/Features/ForwardShading.hlsl b/Content/Editor/MaterialTemplates/Features/ForwardShading.hlsl index 066815dfe..435675a9f 100644 --- a/Content/Editor/MaterialTemplates/Features/ForwardShading.hlsl +++ b/Content/Editor/MaterialTemplates/Features/ForwardShading.hlsl @@ -133,6 +133,8 @@ void PS_Forward( // Add lighting (apply ambient occlusion) output.rgb += light.rgb * gBuffer.AO; +#endif + #if USE_FOG // Calculate exponential height fog float4 fog = GetExponentialHeightFog(ExponentialHeightFog, materialInput.WorldPosition, ViewPos, 0); @@ -148,7 +150,5 @@ void PS_Forward( output = float4(lerp(float3(1, 1, 1), output.rgb, fog.aaa * fog.aaa), output.a); #endif -#endif - #endif } From 4d9ef32abc6bb5297d7a8612c4ae434272db8341 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 27 Dec 2023 00:29:30 +0100 Subject: [PATCH 06/36] Fix auto-docking windows on open when system DPI scale is not `1` --- Source/Editor/GUI/Docking/DockWindow.cs | 7 ++----- Source/Editor/Modules/WindowsModule.cs | 6 ++++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Source/Editor/GUI/Docking/DockWindow.cs b/Source/Editor/GUI/Docking/DockWindow.cs index dd4e39ce2..72c74ad9e 100644 --- a/Source/Editor/GUI/Docking/DockWindow.cs +++ b/Source/Editor/GUI/Docking/DockWindow.cs @@ -63,12 +63,9 @@ namespace FlaxEditor.GUI.Docking public bool IsHidden => !Visible || _dockedTo == null; /// - /// Gets the default window size. + /// Gets the default window size (in UI units, unscaled by DPI which is handled by windowing system). /// - /// - /// Scaled by the DPI, because the window should be large enough for its content on every monitor - /// - public virtual Float2 DefaultSize => new Float2(900, 580) * DpiScale; + public virtual Float2 DefaultSize => new Float2(900, 580); /// /// Gets the serialization typename. diff --git a/Source/Editor/Modules/WindowsModule.cs b/Source/Editor/Modules/WindowsModule.cs index cda83b56e..245fd4d5d 100644 --- a/Source/Editor/Modules/WindowsModule.cs +++ b/Source/Editor/Modules/WindowsModule.cs @@ -676,7 +676,9 @@ namespace FlaxEditor.Modules if (newLocation == DockState.Float) { // Check if there is a floating window that has the same size - var defaultSize = window.DefaultSize; + var dpi = (float)Platform.Dpi / 96.0f; + var dpiScale = Platform.CustomDpiScale; + var defaultSize = window.DefaultSize * dpi; for (var i = 0; i < Editor.UI.MasterPanel.FloatingPanels.Count; i++) { var win = Editor.UI.MasterPanel.FloatingPanels[i]; @@ -688,7 +690,7 @@ namespace FlaxEditor.Modules } } - window.ShowFloating(defaultSize); + window.ShowFloating(defaultSize * dpiScale); } else { From 4a8c94e9d419b0393968dc70b2b3f70ee2daa720 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 27 Dec 2023 20:57:37 +0100 Subject: [PATCH 07/36] Fix missing asset update when dependant asset gets saved in Editor #1489 --- Source/Engine/Content/BinaryAsset.cpp | 12 ++++++++++-- Source/Engine/Content/Storage/FlaxStorage.cpp | 1 + 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Content/BinaryAsset.cpp b/Source/Engine/Content/BinaryAsset.cpp index 9519f73df..cf4aa36b6 100644 --- a/Source/Engine/Content/BinaryAsset.cpp +++ b/Source/Engine/Content/BinaryAsset.cpp @@ -150,9 +150,7 @@ void BinaryAsset::ClearDependencies() { auto asset = Cast(Content::GetAsset(e.First)); if (asset) - { asset->_dependantAssets.Remove(this); - } } Dependencies.Clear(); } @@ -387,6 +385,16 @@ bool BinaryAsset::SaveToAsset(const StringView& path, AssetInitData& data, bool if (binaryAsset) binaryAsset->_isSaving = false; + if (binaryAsset) + { + // Inform dependant asset (use cloned version because it might be modified by assets when they got reloaded) + auto dependantAssets = binaryAsset->_dependantAssets; + for (auto& e : dependantAssets) + { + e->OnDependencyModified(binaryAsset); + } + } + return result; } diff --git a/Source/Engine/Content/Storage/FlaxStorage.cpp b/Source/Engine/Content/Storage/FlaxStorage.cpp index 9d4691be1..df99418bb 100644 --- a/Source/Engine/Content/Storage/FlaxStorage.cpp +++ b/Source/Engine/Content/Storage/FlaxStorage.cpp @@ -963,6 +963,7 @@ bool FlaxStorage::Create(WriteStream* stream, const AssetInitData* data, int32 d // Asset Dependencies stream->WriteInt32(header.Dependencies.Count()); stream->WriteBytes(header.Dependencies.Get(), header.Dependencies.Count() * sizeof(Pair)); + static_assert(sizeof(Pair) == sizeof(Guid) + sizeof(DateTime), "Invalid data size."); } #if ASSETS_LOADING_EXTRA_VERIFICATION From b4c23c969aba6994c5b47dee5c679930606e6e90 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 27 Dec 2023 21:11:01 +0100 Subject: [PATCH 08/36] Fix `unlink` usag on Unix systems to properly use returned value #2078 --- Source/Engine/Platform/Android/AndroidFileSystem.cpp | 2 +- Source/Engine/Platform/Apple/AppleFileSystem.cpp | 2 +- Source/Engine/Platform/Linux/LinuxFileSystem.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Platform/Android/AndroidFileSystem.cpp b/Source/Engine/Platform/Android/AndroidFileSystem.cpp index a04fc0229..6ea5b394b 100644 --- a/Source/Engine/Platform/Android/AndroidFileSystem.cpp +++ b/Source/Engine/Platform/Android/AndroidFileSystem.cpp @@ -244,7 +244,7 @@ bool AndroidFileSystem::FileExists(const StringView& path) bool AndroidFileSystem::DeleteFile(const StringView& path) { const StringAsANSI<> pathANSI(*path, path.Length()); - return unlink(pathANSI.Get()) == 0; + return unlink(pathANSI.Get()) != 0; } uint64 AndroidFileSystem::GetFileSize(const StringView& path) diff --git a/Source/Engine/Platform/Apple/AppleFileSystem.cpp b/Source/Engine/Platform/Apple/AppleFileSystem.cpp index 9e306019a..0d36f1dfb 100644 --- a/Source/Engine/Platform/Apple/AppleFileSystem.cpp +++ b/Source/Engine/Platform/Apple/AppleFileSystem.cpp @@ -218,7 +218,7 @@ bool AppleFileSystem::FileExists(const StringView& path) bool AppleFileSystem::DeleteFile(const StringView& path) { const StringAsANSI<> pathANSI(*path, path.Length()); - return unlink(pathANSI.Get()) == 0; + return unlink(pathANSI.Get()) != 0; } uint64 AppleFileSystem::GetFileSize(const StringView& path) diff --git a/Source/Engine/Platform/Linux/LinuxFileSystem.cpp b/Source/Engine/Platform/Linux/LinuxFileSystem.cpp index 97cde4a1c..23d47a029 100644 --- a/Source/Engine/Platform/Linux/LinuxFileSystem.cpp +++ b/Source/Engine/Platform/Linux/LinuxFileSystem.cpp @@ -364,7 +364,7 @@ bool LinuxFileSystem::FileExists(const StringView& path) bool LinuxFileSystem::DeleteFile(const StringView& path) { const StringAsANSI<> pathANSI(*path, path.Length()); - return unlink(pathANSI.Get()) == 0; + return unlink(pathANSI.Get()) != 0; } uint64 LinuxFileSystem::GetFileSize(const StringView& path) From b275ffc146923eb11de0ea7611b945d8c36072e1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 27 Dec 2023 21:18:28 +0100 Subject: [PATCH 09/36] Fix PostFx Materials blending for duplicated entries #1991 --- Source/Engine/Graphics/PostProcessSettings.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Source/Engine/Graphics/PostProcessSettings.cpp b/Source/Engine/Graphics/PostProcessSettings.cpp index a564bc85f..6e2b21045 100644 --- a/Source/Engine/Graphics/PostProcessSettings.cpp +++ b/Source/Engine/Graphics/PostProcessSettings.cpp @@ -207,13 +207,12 @@ void PostFxMaterialsSettings::BlendWith(PostFxMaterialsSettings& other, float we if (isHalf) { int32 indexSrc = 0; - const auto materialsSrc = other.Materials.Get(); + const AssetReference* materialsSrc = other.Materials.Get(); while (Materials.Count() != POST_PROCESS_SETTINGS_MAX_MATERIALS && indexSrc < other.Materials.Count()) { - if (materialsSrc[indexSrc]) - { - Materials.Add(materialsSrc[indexSrc]); - } + MaterialBase* mat = materialsSrc[indexSrc].Get(); + if (mat && !Materials.Contains(mat)) + Materials.Add(mat); indexSrc++; } } From 8174e8ab775267ce69e7061a42f56db93c3174a3 Mon Sep 17 00:00:00 2001 From: nothingTVatYT Date: Thu, 28 Dec 2023 22:24:34 +0100 Subject: [PATCH 10/36] fix getting file access time for Linux --- Source/Engine/Platform/Linux/LinuxFileSystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Platform/Linux/LinuxFileSystem.cpp b/Source/Engine/Platform/Linux/LinuxFileSystem.cpp index 97cde4a1c..c56027cd2 100644 --- a/Source/Engine/Platform/Linux/LinuxFileSystem.cpp +++ b/Source/Engine/Platform/Linux/LinuxFileSystem.cpp @@ -657,7 +657,7 @@ DateTime LinuxFileSystem::GetFileLastEditTime(const StringView& path) return DateTime::MinValue(); } - const TimeSpan timeSinceEpoch(0, 0, fileInfo.st_mtime); + const TimeSpan timeSinceEpoch(0, 0, 0, fileInfo.st_mtime); return UnixEpoch + timeSinceEpoch; } From 21c4667f8c8fdc4870df1225719fb85d1264bce6 Mon Sep 17 00:00:00 2001 From: nothingTVatYT Date: Thu, 28 Dec 2023 22:36:33 +0100 Subject: [PATCH 11/36] same problem for Android and Apple --- Source/Engine/Platform/Android/AndroidFileSystem.cpp | 2 +- Source/Engine/Platform/Apple/AppleFileSystem.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Platform/Android/AndroidFileSystem.cpp b/Source/Engine/Platform/Android/AndroidFileSystem.cpp index a04fc0229..a939124f6 100644 --- a/Source/Engine/Platform/Android/AndroidFileSystem.cpp +++ b/Source/Engine/Platform/Android/AndroidFileSystem.cpp @@ -517,7 +517,7 @@ DateTime AndroidFileSystem::GetFileLastEditTime(const StringView& path) const StringAsANSI<> pathANSI(*path, path.Length()); if (stat(pathANSI.Get(), &fileInfo) == -1) return DateTime::MinValue(); - const TimeSpan timeSinceEpoch(0, 0, fileInfo.st_mtime); + const TimeSpan timeSinceEpoch(0, 0, 0, fileInfo.st_mtime); const DateTime UnixEpoch(1970, 1, 1); return UnixEpoch + timeSinceEpoch; } diff --git a/Source/Engine/Platform/Apple/AppleFileSystem.cpp b/Source/Engine/Platform/Apple/AppleFileSystem.cpp index 9e306019a..3fd52a54b 100644 --- a/Source/Engine/Platform/Apple/AppleFileSystem.cpp +++ b/Source/Engine/Platform/Apple/AppleFileSystem.cpp @@ -498,7 +498,7 @@ DateTime AppleFileSystem::GetFileLastEditTime(const StringView& path) return DateTime::MinValue(); } - const TimeSpan timeSinceEpoch(0, 0, fileInfo.st_mtime); + const TimeSpan timeSinceEpoch(0, 0, 0, fileInfo.st_mtime); return UnixEpoch + timeSinceEpoch; } From e851d18227fcbf26aca8968c2e2a1b69b9c9cb29 Mon Sep 17 00:00:00 2001 From: nothingTVatYT Date: Fri, 29 Dec 2023 01:20:17 +0100 Subject: [PATCH 12/36] same in UnixFile.cpp --- Source/Engine/Platform/Unix/UnixFile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Platform/Unix/UnixFile.cpp b/Source/Engine/Platform/Unix/UnixFile.cpp index 5fbc75f2e..9cf629d2c 100644 --- a/Source/Engine/Platform/Unix/UnixFile.cpp +++ b/Source/Engine/Platform/Unix/UnixFile.cpp @@ -137,7 +137,7 @@ DateTime UnixFile::GetLastWriteTime() const { return DateTime::MinValue(); } - const TimeSpan timeSinceEpoch(0, 0, fileInfo.st_mtime); + const TimeSpan timeSinceEpoch(0, 0, 0, fileInfo.st_mtime); const DateTime unixEpoch(1970, 1, 1); return unixEpoch + timeSinceEpoch; } From f10840efb483739be6b048c0204bf0478e57d8f0 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Fri, 29 Dec 2023 19:24:00 +0200 Subject: [PATCH 13/36] Fix uninitialized value of `NetworkConfig` `NetworkDriver` field --- Source/Engine/Networking/NetworkConfig.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Networking/NetworkConfig.h b/Source/Engine/Networking/NetworkConfig.h index b92d822cb..0768d3402 100644 --- a/Source/Engine/Networking/NetworkConfig.h +++ b/Source/Engine/Networking/NetworkConfig.h @@ -43,7 +43,7 @@ API_STRUCT(Namespace="FlaxEngine.Networking") struct FLAXENGINE_API NetworkConfi /// /// Object is managed by the created network peer (will be deleted on peer shutdown). API_FIELD() - ScriptingObject* NetworkDriver; + ScriptingObject* NetworkDriver = nullptr; /// /// The upper limit on how many peers can join when we're listening. From 8841a603f650757d89a2032c2ef46074de62c849 Mon Sep 17 00:00:00 2001 From: NoriteSC <53096989+NoriteSC@users.noreply.github.com> Date: Fri, 29 Dec 2023 23:15:03 +0100 Subject: [PATCH 14/36] Virtual proxy --- Source/Editor/Content/Proxy/AssetProxy.cs | 16 +++++++++++ .../Editor/Modules/ContentDatabaseModule.cs | 28 ++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Content/Proxy/AssetProxy.cs b/Source/Editor/Content/Proxy/AssetProxy.cs index 3bd23ad03..7dbd178a9 100644 --- a/Source/Editor/Content/Proxy/AssetProxy.cs +++ b/Source/Editor/Content/Proxy/AssetProxy.cs @@ -22,6 +22,22 @@ namespace FlaxEditor.Content /// public abstract string TypeName { get; } + /// + /// Gets a value indicating whether this instance is virtual Proxy not linked to any asset. + /// + protected virtual bool IsVirtual { get; } + + /// + /// Determines whether [is virtual proxy]. + /// + /// + /// true if [is virtual proxy]; otherwise, false. + /// + public bool IsVirtualProxy() + { + return IsVirtual && CanExport == false; + } + /// /// Checks if this proxy supports the given asset type id at the given path. /// diff --git a/Source/Editor/Modules/ContentDatabaseModule.cs b/Source/Editor/Modules/ContentDatabaseModule.cs index 80f419a6f..fa023bf7b 100644 --- a/Source/Editor/Modules/ContentDatabaseModule.cs +++ b/Source/Editor/Modules/ContentDatabaseModule.cs @@ -196,6 +196,25 @@ namespace FlaxEditor.Modules return null; } + /// + /// Gets the virtual proxy object from given path. + ///

use case if the asset u trying to display is not a flax asset but u like to add custom functionality + ///

to context menu,or display it the asset + ///
+ /// The asset path. + /// Asset proxy or null if cannot find. + public AssetProxy GetAssetVirtuallProxy(string path) + { + for (int i = 0; i < Proxy.Count; i++) + { + if (Proxy[i] is AssetProxy proxy && proxy.IsVirtualProxy() && path.EndsWith(proxy.FileExtension, StringComparison.OrdinalIgnoreCase)) + { + return proxy; + } + } + + return null; + } /// /// Refreshes the given item folder. Tries to find new content items and remove not existing ones. @@ -996,7 +1015,14 @@ namespace FlaxEditor.Modules item = proxy?.ConstructItem(path, assetInfo.TypeName, ref assetInfo.ID); } if (item == null) - item = new FileItem(path); + { + var proxy = GetAssetVirtuallProxy(path); + item = proxy?.ConstructItem(path, assetInfo.TypeName, ref assetInfo.ID); + if (item == null) + { + item = new FileItem(path); + } + } // Link item.ParentFolder = parent.Folder; From 343d7b4973f8e6d32905c14e89ad817d4b8a790e Mon Sep 17 00:00:00 2001 From: MineBill Date: Sat, 30 Dec 2023 18:30:26 +0200 Subject: [PATCH 15/36] Save the dock state of custom editor windows between script reloads. --- Source/Editor/CustomEditorWindow.cs | 9 ++++-- Source/Editor/GUI/Docking/DockPanel.cs | 12 ++++---- Source/Editor/GUI/Docking/DockWindow.cs | 6 ++-- Source/Editor/Modules/WindowsModule.cs | 40 +++++++++++++++++++++++-- 4 files changed, 54 insertions(+), 13 deletions(-) diff --git a/Source/Editor/CustomEditorWindow.cs b/Source/Editor/CustomEditorWindow.cs index 242b4d28d..5c3fedd6c 100644 --- a/Source/Editor/CustomEditorWindow.cs +++ b/Source/Editor/CustomEditorWindow.cs @@ -1,9 +1,9 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using FlaxEditor.CustomEditors; +using FlaxEditor.GUI.Docking; using FlaxEditor.Windows; using FlaxEngine.GUI; -using DockState = FlaxEditor.GUI.Docking.DockState; namespace FlaxEditor { @@ -97,9 +97,12 @@ namespace FlaxEditor /// Shows the window. /// /// Initial window state. - public void Show(DockState state = DockState.Float) + /// The panel to dock to, if any. + /// Only used if is set. If true the window will be selected after docking it. + /// The splitter value to use if toDock is not null. If not specified, a default value will be used. + public void Show(DockState state = DockState.Float, DockPanel toDock = null, bool autoSelect = true, float? splitterValue = null) { - _win.Show(state); + _win.Show(state, toDock, autoSelect, splitterValue); } } } diff --git a/Source/Editor/GUI/Docking/DockPanel.cs b/Source/Editor/GUI/Docking/DockPanel.cs index b04aad08c..df6a75bef 100644 --- a/Source/Editor/GUI/Docking/DockPanel.cs +++ b/Source/Editor/GUI/Docking/DockPanel.cs @@ -518,9 +518,9 @@ namespace FlaxEditor.GUI.Docking } } - internal virtual void DockWindowInternal(DockState state, DockWindow window) + internal virtual void DockWindowInternal(DockState state, DockWindow window, bool autoSelect = true, float? splitterValue = null) { - DockWindow(state, window); + DockWindow(state, window, autoSelect, splitterValue); } /// @@ -528,7 +528,9 @@ namespace FlaxEditor.GUI.Docking /// /// The state. /// The window. - protected virtual void DockWindow(DockState state, DockWindow window) + /// Whether or not to automatically select the window after docking it. + /// The splitter value to use when docking to window. + protected virtual void DockWindow(DockState state, DockWindow window, bool autoSelect = true, float? splitterValue = null) { CreateTabsProxy(); @@ -536,12 +538,12 @@ namespace FlaxEditor.GUI.Docking if (state == DockState.DockFill) { // Add tab - AddTab(window); + AddTab(window, autoSelect); } else { // Create child panel - var dockPanel = CreateChildPanel(state, DefaultSplitterValue); + var dockPanel = CreateChildPanel(state, splitterValue ?? DefaultSplitterValue); // Dock window as a tab in a child panel dockPanel.DockWindow(DockState.DockFill, window); diff --git a/Source/Editor/GUI/Docking/DockWindow.cs b/Source/Editor/GUI/Docking/DockWindow.cs index 72c74ad9e..15608d3e1 100644 --- a/Source/Editor/GUI/Docking/DockWindow.cs +++ b/Source/Editor/GUI/Docking/DockWindow.cs @@ -214,7 +214,9 @@ namespace FlaxEditor.GUI.Docking /// /// Initial window state. /// Panel to dock to it. - public void Show(DockState state = DockState.Float, DockPanel toDock = null) + /// Only used if is set. If true the window will be selected after docking it. + /// Only used if is set. The splitter value to use. If not specified, a default value will be used. + public void Show(DockState state = DockState.Float, DockPanel toDock = null, bool autoSelect = true, float? splitterValue = null) { if (state == DockState.Hidden) { @@ -232,7 +234,7 @@ namespace FlaxEditor.GUI.Docking Undock(); // Then dock - (toDock ?? _masterPanel).DockWindowInternal(state, this); + (toDock ?? _masterPanel).DockWindowInternal(state, this, autoSelect, splitterValue); OnShow(); PerformLayout(); } diff --git a/Source/Editor/Modules/WindowsModule.cs b/Source/Editor/Modules/WindowsModule.cs index 245fd4d5d..3e88a379c 100644 --- a/Source/Editor/Modules/WindowsModule.cs +++ b/Source/Editor/Modules/WindowsModule.cs @@ -36,6 +36,17 @@ namespace FlaxEditor.Modules { public string AssemblyName; public string TypeName; + + public DockState DockState; + public DockPanel DockedTo; + public float? SplitterValue = null; + + public bool SelectOnShow = false; + + // Constructor, to allow for default values + public WindowRestoreData() + { + } } private readonly List _restoreWindows = new List(); @@ -802,10 +813,33 @@ namespace FlaxEditor.Modules if (constructor == null || type.IsGenericType) return; - WindowRestoreData winData; + var winData = new WindowRestoreData(); + var panel = win.Window.ParentDockPanel; + + // Ensure that this window is only selected following recompilation + // if it was the active tab in its dock panel. Otherwise, there is a + // risk of interrupting the user's workflow by potentially selecting + // background tabs. + winData.SelectOnShow = panel.SelectedTab == win.Window; + if (panel is FloatWindowDockPanel) + { + winData.DockState = DockState.Float; + } + else + { + if (panel.TabsCount > 1) + { + winData.DockState = DockState.DockFill; + winData.DockedTo = panel; + }else + { + winData.DockState = panel.TryGetDockState(out var splitterValue); + winData.DockedTo = panel.ParentDockPanel; + winData.SplitterValue = splitterValue; + } + } winData.AssemblyName = type.Assembly.GetName().Name; winData.TypeName = type.FullName; - // TODO: cache and restore docking info _restoreWindows.Add(winData); } @@ -824,7 +858,7 @@ namespace FlaxEditor.Modules if (type != null) { var win = (CustomEditorWindow)Activator.CreateInstance(type); - win.Show(); + win.Show(winData.DockState, winData.DockedTo, winData.SelectOnShow, winData.SplitterValue); } } } From 1e797c244f24d685f5cc4aa2643f0aeaef391ddf Mon Sep 17 00:00:00 2001 From: MineBill Date: Sat, 30 Dec 2023 18:57:33 +0200 Subject: [PATCH 16/36] Restore floating window data. --- Source/Editor/Modules/WindowsModule.cs | 27 ++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Source/Editor/Modules/WindowsModule.cs b/Source/Editor/Modules/WindowsModule.cs index 3e88a379c..a935e73f2 100644 --- a/Source/Editor/Modules/WindowsModule.cs +++ b/Source/Editor/Modules/WindowsModule.cs @@ -43,6 +43,11 @@ namespace FlaxEditor.Modules public bool SelectOnShow = false; + public bool Maximize; + public bool Minimize; + public Float2 FloatSize; + public Float2 FloatPosition; + // Constructor, to allow for default values public WindowRestoreData() { @@ -824,6 +829,11 @@ namespace FlaxEditor.Modules if (panel is FloatWindowDockPanel) { winData.DockState = DockState.Float; + var window = win.Window.RootWindow.Window; + winData.FloatPosition = window.Position; + winData.FloatSize = window.ClientSize; + winData.Maximize = window.IsMaximized; + winData.Minimize = window.IsMinimized; } else { @@ -859,6 +869,23 @@ namespace FlaxEditor.Modules { var win = (CustomEditorWindow)Activator.CreateInstance(type); win.Show(winData.DockState, winData.DockedTo, winData.SelectOnShow, winData.SplitterValue); + if (winData.DockState == DockState.Float) + { + var window = win.Window.RootWindow.Window; + window.Position = winData.FloatPosition; + if (winData.Maximize) + { + window.Maximize(); + } + else if (winData.Minimize) + { + window.Minimize(); + } + else + { + window.ClientSize = winData.FloatSize; + } + } } } } From 4b6ada72898980bd6e96793c511af2d58fe6736f Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sun, 31 Dec 2023 13:10:20 +0200 Subject: [PATCH 17/36] Fix profiling hotkeys not working while profiler window is closed --- Source/Editor/Utilities/Utils.cs | 13 +++++++++++-- Source/Editor/Windows/Profiler/ProfilerWindow.cs | 1 + 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index 7184391d5..3d790ff81 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -1310,8 +1310,17 @@ namespace FlaxEditor.Utilities inputActions.Add(options => options.BuildSDF, Editor.Instance.BuildAllMeshesSDF); inputActions.Add(options => options.TakeScreenshot, Editor.Instance.Windows.TakeScreenshot); inputActions.Add(options => options.ProfilerWindow, () => Editor.Instance.Windows.ProfilerWin.FocusOrShow()); - inputActions.Add(options => options.ProfilerStartStop, () => { Editor.Instance.Windows.ProfilerWin.LiveRecording = !Editor.Instance.Windows.ProfilerWin.LiveRecording; Editor.Instance.UI.AddStatusMessage($"Profiling {(Editor.Instance.Windows.ProfilerWin.LiveRecording ? "started" : "stopped")}."); }); - inputActions.Add(options => options.ProfilerClear, () => { Editor.Instance.Windows.ProfilerWin.Clear(); Editor.Instance.UI.AddStatusMessage($"Profiling results cleared."); }); + inputActions.Add(options => options.ProfilerStartStop, () => + { + bool recording = !Editor.Instance.Windows.ProfilerWin.LiveRecording; + Editor.Instance.Windows.ProfilerWin.LiveRecording = recording; + Editor.Instance.UI.AddStatusMessage($"Profiling {(recording ? "started" : "stopped")}."); + }); + inputActions.Add(options => options.ProfilerClear, () => + { + Editor.Instance.Windows.ProfilerWin.Clear(); + Editor.Instance.UI.AddStatusMessage($"Profiling results cleared."); + }); inputActions.Add(options => options.SaveScenes, () => Editor.Instance.Scene.SaveScenes()); inputActions.Add(options => options.CloseScenes, () => Editor.Instance.Scene.CloseAllScenes()); inputActions.Add(options => options.OpenScriptsProject, () => Editor.Instance.CodeEditing.OpenSolution()); diff --git a/Source/Editor/Windows/Profiler/ProfilerWindow.cs b/Source/Editor/Windows/Profiler/ProfilerWindow.cs index f97e943ad..7a3924d1b 100644 --- a/Source/Editor/Windows/Profiler/ProfilerWindow.cs +++ b/Source/Editor/Windows/Profiler/ProfilerWindow.cs @@ -38,6 +38,7 @@ namespace FlaxEditor.Windows.Profiler if (value != LiveRecording) { _liveRecordingButton.Checked = value; + OnLiveRecordingChanged(); } } } From 9077e0cf226acf953fe62cca66d9c09a4de3f97d Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sun, 31 Dec 2023 14:36:04 +0200 Subject: [PATCH 18/36] Fix crash while moving simulation disabled kinematic actors --- Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp index cbc381142..af6c6403b 100644 --- a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp +++ b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp @@ -2260,7 +2260,13 @@ void PhysicsBackend::SetRigidActorPose(void* actor, const Vector3& position, con if (kinematic) { auto actorPhysX = (PxRigidDynamic*)actor; - actorPhysX->setKinematicTarget(trans); + if (actorPhysX->getActorFlags() & PxActorFlag::eDISABLE_SIMULATION) + { + // Ensures the disabled kinematic actor ends up in the correct pose after enabling simulation + actorPhysX->setGlobalPose(trans, wakeUp); + } + else + actorPhysX->setKinematicTarget(trans); } else { From bf6cb90a78b2c493dcb21e54318173be96e86dc0 Mon Sep 17 00:00:00 2001 From: Menotdan <32620310+Menotdan@users.noreply.github.com> Date: Sun, 31 Dec 2023 15:09:07 -0500 Subject: [PATCH 19/36] Expose partition mode, and cascade spacing. --- .../SceneGraph/Actors/DirectionalLightNode.cs | 11 +++++++ Source/Engine/Graphics/Enums.h | 21 +++++++++++++ .../Engine/Level/Actors/DirectionalLight.cpp | 18 +++++++++++ Source/Engine/Level/Actors/DirectionalLight.h | 30 +++++++++++++++++++ Source/Engine/Renderer/RenderList.h | 6 ++++ Source/Engine/Renderer/ShadowsPass.cpp | 17 ++++------- 6 files changed, 91 insertions(+), 12 deletions(-) diff --git a/Source/Editor/SceneGraph/Actors/DirectionalLightNode.cs b/Source/Editor/SceneGraph/Actors/DirectionalLightNode.cs index 5363d1f55..1af3812f7 100644 --- a/Source/Editor/SceneGraph/Actors/DirectionalLightNode.cs +++ b/Source/Editor/SceneGraph/Actors/DirectionalLightNode.cs @@ -2,6 +2,17 @@ using FlaxEngine; +namespace FlaxEngine +{ + public partial class DirectionalLight + { + bool ShowCascade1 => CascadeCount >= 1 && PartitionMode == PartitionMode.Manual; + bool ShowCascade2 => CascadeCount >= 2 && PartitionMode == PartitionMode.Manual; + bool ShowCascade3 => CascadeCount >= 3 && PartitionMode == PartitionMode.Manual; + bool ShowCascade4 => CascadeCount >= 4 && PartitionMode == PartitionMode.Manual; + } +} + namespace FlaxEditor.SceneGraph.Actors { /// diff --git a/Source/Engine/Graphics/Enums.h b/Source/Engine/Graphics/Enums.h index bbf9c0de8..6ae9ad4ec 100644 --- a/Source/Engine/Graphics/Enums.h +++ b/Source/Engine/Graphics/Enums.h @@ -236,6 +236,27 @@ API_ENUM(Attributes="Flags") enum class ShadowsCastingMode DECLARE_ENUM_OPERATORS(ShadowsCastingMode); +/// +/// The partitioning mode for shadow cascades. +/// +API_ENUM() enum class PartitionMode +{ + /// + /// Internally defined cascade splits. + /// + Manual = 0, + + /// + /// Logarithmic cascade splits. + /// + Logarithmic = 1, + + /// + /// PSSM cascade splits. + /// + PSSM = 2, +}; + /// /// Identifies expected GPU resource use during rendering. The usage directly reflects whether a resource is accessible by the CPU and/or the GPU. /// diff --git a/Source/Engine/Level/Actors/DirectionalLight.cpp b/Source/Engine/Level/Actors/DirectionalLight.cpp index 43450fd8e..32bb61653 100644 --- a/Source/Engine/Level/Actors/DirectionalLight.cpp +++ b/Source/Engine/Level/Actors/DirectionalLight.cpp @@ -41,6 +41,12 @@ void DirectionalLight::Draw(RenderContext& renderContext) data.RenderedVolumetricFog = 0; data.ShadowsMode = ShadowsMode; data.CascadeCount = CascadeCount; + data.Cascade1Spacing = Cascade1Spacing; + data.Cascade2Spacing = Cascade2Spacing; + data.Cascade3Spacing = Cascade3Spacing; + data.Cascade4Spacing = Cascade4Spacing; + + data.PartitionMode = PartitionMode; data.ContactShadowsLength = ContactShadowsLength; data.StaticFlags = GetStaticFlags(); data.ID = GetID(); @@ -56,6 +62,12 @@ void DirectionalLight::Serialize(SerializeStream& stream, const void* otherObj) SERIALIZE_GET_OTHER_OBJ(DirectionalLight); SERIALIZE(CascadeCount); + SERIALIZE(Cascade1Spacing); + SERIALIZE(Cascade2Spacing); + SERIALIZE(Cascade3Spacing); + SERIALIZE(Cascade4Spacing); + + SERIALIZE(PartitionMode); } void DirectionalLight::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) @@ -64,6 +76,12 @@ void DirectionalLight::Deserialize(DeserializeStream& stream, ISerializeModifier LightWithShadow::Deserialize(stream, modifier); DESERIALIZE(CascadeCount); + DESERIALIZE(Cascade1Spacing); + DESERIALIZE(Cascade2Spacing); + DESERIALIZE(Cascade3Spacing); + DESERIALIZE(Cascade4Spacing); + + DESERIALIZE(PartitionMode); } bool DirectionalLight::IntersectsItself(const Ray& ray, Real& distance, Vector3& normal) diff --git a/Source/Engine/Level/Actors/DirectionalLight.h b/Source/Engine/Level/Actors/DirectionalLight.h index d5f31324a..cb29112c0 100644 --- a/Source/Engine/Level/Actors/DirectionalLight.h +++ b/Source/Engine/Level/Actors/DirectionalLight.h @@ -12,12 +12,42 @@ class FLAXENGINE_API DirectionalLight : public LightWithShadow { DECLARE_SCENE_OBJECT(DirectionalLight); public: + /// + /// The partitioning mode for the shadow cascades. + /// + API_FIELD(Attributes = "EditorOrder(64), DefaultValue(PartitionMode.Manual), EditorDisplay(\"Shadow\")") + PartitionMode PartitionMode = PartitionMode::Manual; + /// /// The number of cascades used for slicing the range of depth covered by the light during shadow rendering. Values are 1, 2 or 4 cascades; a typical scene uses 4 cascades. /// API_FIELD(Attributes="EditorOrder(65), DefaultValue(4), Limit(1, 4), EditorDisplay(\"Shadow\")") int32 CascadeCount = 4; + /// + /// Percentage of the shadow distance used by the first cascade. + /// + API_FIELD(Attributes = "EditorOrder(66), DefaultValue(0.05f), VisibleIf(nameof(ShowCascade1)), Limit(0, 1, 0.001f), EditorDisplay(\"Shadow\")") + float Cascade1Spacing = 0.05f; + + /// + /// Percentage of the shadow distance used by the second cascade. + /// + API_FIELD(Attributes = "EditorOrder(67), DefaultValue(0.15f), VisibleIf(nameof(ShowCascade2)), Limit(0, 1, 0.001f), EditorDisplay(\"Shadow\")") + float Cascade2Spacing = 0.15f; + + /// + /// Percentage of the shadow distance used by the third cascade. + /// + API_FIELD(Attributes = "EditorOrder(68), DefaultValue(0.50f), VisibleIf(nameof(ShowCascade3)), Limit(0, 1, 0.001f), EditorDisplay(\"Shadow\")") + float Cascade3Spacing = 0.50f; + + /// + /// Percentage of the shadow distance used by the fourth cascade. + /// + API_FIELD(Attributes = "EditorOrder(69), DefaultValue(1.0f), VisibleIf(nameof(ShowCascade4)), Limit(0, 1, 0.001f), EditorDisplay(\"Shadow\")") + float Cascade4Spacing = 1.0f; + public: // [LightWithShadow] void Draw(RenderContext& renderContext) override; diff --git a/Source/Engine/Renderer/RenderList.h b/Source/Engine/Renderer/RenderList.h index a2cd48696..3f9a25694 100644 --- a/Source/Engine/Renderer/RenderList.h +++ b/Source/Engine/Renderer/RenderList.h @@ -46,6 +46,12 @@ struct RendererDirectionalLightData float ShadowsDistance; int32 CascadeCount; + float Cascade1Spacing; + float Cascade2Spacing; + float Cascade3Spacing; + float Cascade4Spacing; + + PartitionMode PartitionMode; float ContactShadowsLength; ShadowsCastingMode ShadowsMode; diff --git a/Source/Engine/Renderer/ShadowsPass.cpp b/Source/Engine/Renderer/ShadowsPass.cpp index ba326b728..937cd9e99 100644 --- a/Source/Engine/Renderer/ShadowsPass.cpp +++ b/Source/Engine/Renderer/ShadowsPass.cpp @@ -247,19 +247,12 @@ void ShadowsPass::SetupLight(RenderContext& renderContext, RenderContextBatch& r minDistance = cameraNear; maxDistance = cameraNear + shadowsDistance; - // TODO: expose partition mode? - enum class PartitionMode - { - Manual = 0, - Logarithmic = 1, - PSSM = 2, - }; - PartitionMode partitionMode = PartitionMode::Manual; + PartitionMode partitionMode = light.PartitionMode; float pssmFactor = 0.5f; - float splitDistance0 = 0.05f; - float splitDistance1 = 0.15f; - float splitDistance2 = 0.50f; - float splitDistance3 = 1.00f; + float splitDistance0 = light.Cascade1Spacing; + float splitDistance1 = Math::Max(splitDistance0, light.Cascade2Spacing); + float splitDistance2 = Math::Max(splitDistance1, light.Cascade3Spacing); + float splitDistance3 = Math::Max(splitDistance2, light.Cascade4Spacing); // Compute the split distances based on the partitioning mode if (partitionMode == PartitionMode::Manual) From 826a330f636dacc3c9947bb7035a30e9daea0396 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sun, 31 Dec 2023 16:06:38 -0600 Subject: [PATCH 20/36] Add asset reference to Guid editor for easy picking of specific Guid based on type in editor. --- .../CustomEditors/Editors/GuidEditor.cs | 82 +++++++++++++++++-- 1 file changed, 76 insertions(+), 6 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/GuidEditor.cs b/Source/Editor/CustomEditors/Editors/GuidEditor.cs index 28d93aa8e..adba58e26 100644 --- a/Source/Editor/CustomEditors/Editors/GuidEditor.cs +++ b/Source/Editor/CustomEditors/Editors/GuidEditor.cs @@ -1,8 +1,12 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; +using System.Linq; using FlaxEditor.CustomEditors.Elements; +using FlaxEditor.GUI; +using FlaxEditor.Scripting; using FlaxEngine; +using FlaxEngine.Utilities; namespace FlaxEditor.CustomEditors.Editors { @@ -13,6 +17,9 @@ namespace FlaxEditor.CustomEditors.Editors public sealed class GuidEditor : CustomEditor { private TextBoxElement _element; + private AssetPicker _picker; + private bool _isReference; + private bool _isRefreshing; /// public override DisplayStyle Style => DisplayStyle.Inline; @@ -20,8 +27,57 @@ namespace FlaxEditor.CustomEditors.Editors /// public override void Initialize(LayoutElementsContainer layout) { - _element = layout.TextBox(); - _element.TextBox.EditEnd += OnEditEnd; + + var attributes = Values.GetAttributes(); + var assetReference = (AssetReferenceAttribute)attributes?.FirstOrDefault(x => x is AssetReferenceAttribute); + if (assetReference != null) + { + _picker = layout.Custom().CustomControl; + ScriptType assetType = new ScriptType(); + + float height = 48; + if (assetReference.UseSmallPicker) + height = 32; + + if (string.IsNullOrEmpty(assetReference.TypeName)) + { + assetType = ScriptType.Void; + } + else if (assetReference.TypeName.Length > 1 && assetReference.TypeName[0] == '.') + { + // Generic file picker + assetType = ScriptType.Null; + _picker.Validator.FileExtension = assetReference.TypeName; + } + else + { + var customType = TypeUtils.GetType(assetReference.TypeName); + if (customType != ScriptType.Null) + assetType = customType; + else if (!Content.Settings.GameSettings.OptionalPlatformSettings.Contains(assetReference.TypeName)) + Debug.LogWarning(string.Format("Unknown asset type '{0}' to use for asset picker filter.", assetReference.TypeName)); + else + assetType = ScriptType.Void; + } + + _picker.Validator.AssetType = assetType; + _picker.Height = height; + _picker.SelectedItemChanged += OnSelectedItemChanged; + _isReference = true; + + } + else + { + _element = layout.TextBox(); + _element.TextBox.EditEnd += OnEditEnd; + } + } + + private void OnSelectedItemChanged() + { + if (_isRefreshing) + return; + SetValue(_picker.Validator.SelectedID); } private void OnEditEnd() @@ -39,13 +95,27 @@ namespace FlaxEditor.CustomEditors.Editors if (HasDifferentValues) { - _element.TextBox.Text = string.Empty; - _element.TextBox.WatermarkText = "Different values"; + if (_isReference) + { + //_picker.Validator.SelectedID = (Guid)Values[0]; + } + else + { + _element.TextBox.Text = string.Empty; + _element.TextBox.WatermarkText = "Different values"; + } } else { - _element.TextBox.Text = ((Guid)Values[0]).ToString("D"); - _element.TextBox.WatermarkText = string.Empty; + if (_isReference) + { + _picker.Validator.SelectedID = (Guid)Values[0]; + } + else + { + _element.TextBox.Text = ((Guid)Values[0]).ToString("D"); + _element.TextBox.WatermarkText = string.Empty; + } } } } From 57d4f0ff7ddcc016675c5329ec792436d1d5c545 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sun, 31 Dec 2023 16:09:32 -0600 Subject: [PATCH 21/36] Clean up code --- Source/Editor/CustomEditors/Editors/GuidEditor.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/GuidEditor.cs b/Source/Editor/CustomEditors/Editors/GuidEditor.cs index adba58e26..a151f308a 100644 --- a/Source/Editor/CustomEditors/Editors/GuidEditor.cs +++ b/Source/Editor/CustomEditors/Editors/GuidEditor.cs @@ -92,12 +92,12 @@ namespace FlaxEditor.CustomEditors.Editors public override void Refresh() { base.Refresh(); - + _isRefreshing = true; if (HasDifferentValues) { if (_isReference) { - //_picker.Validator.SelectedID = (Guid)Values[0]; + // Not supported } else { @@ -117,6 +117,7 @@ namespace FlaxEditor.CustomEditors.Editors _element.TextBox.WatermarkText = string.Empty; } } + _isRefreshing = false; } } } From 8bff9556defcaa66c28024c5f27f512261ff3be2 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sun, 31 Dec 2023 16:36:34 -0600 Subject: [PATCH 22/36] Code cleanup --- Source/Editor/CustomEditors/Editors/GuidEditor.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/GuidEditor.cs b/Source/Editor/CustomEditors/Editors/GuidEditor.cs index a151f308a..4573def77 100644 --- a/Source/Editor/CustomEditors/Editors/GuidEditor.cs +++ b/Source/Editor/CustomEditors/Editors/GuidEditor.cs @@ -27,7 +27,6 @@ namespace FlaxEditor.CustomEditors.Editors /// public override void Initialize(LayoutElementsContainer layout) { - var attributes = Values.GetAttributes(); var assetReference = (AssetReferenceAttribute)attributes?.FirstOrDefault(x => x is AssetReferenceAttribute); if (assetReference != null) @@ -64,7 +63,6 @@ namespace FlaxEditor.CustomEditors.Editors _picker.Height = height; _picker.SelectedItemChanged += OnSelectedItemChanged; _isReference = true; - } else { From 4839ef5ddc6e913c398ffce621eb73c1f7171f08 Mon Sep 17 00:00:00 2001 From: Menotdan <32620310+Menotdan@users.noreply.github.com> Date: Sun, 31 Dec 2023 19:42:13 -0500 Subject: [PATCH 23/36] Move all ShowCascadeX definitions into an engine class instead of an editor class to allow game builds. --- .../Editor/SceneGraph/Actors/DirectionalLightNode.cs | 11 ----------- Source/Engine/Level/DirectionalLight.cs | 10 ++++++++++ 2 files changed, 10 insertions(+), 11 deletions(-) create mode 100644 Source/Engine/Level/DirectionalLight.cs diff --git a/Source/Editor/SceneGraph/Actors/DirectionalLightNode.cs b/Source/Editor/SceneGraph/Actors/DirectionalLightNode.cs index 1af3812f7..5363d1f55 100644 --- a/Source/Editor/SceneGraph/Actors/DirectionalLightNode.cs +++ b/Source/Editor/SceneGraph/Actors/DirectionalLightNode.cs @@ -2,17 +2,6 @@ using FlaxEngine; -namespace FlaxEngine -{ - public partial class DirectionalLight - { - bool ShowCascade1 => CascadeCount >= 1 && PartitionMode == PartitionMode.Manual; - bool ShowCascade2 => CascadeCount >= 2 && PartitionMode == PartitionMode.Manual; - bool ShowCascade3 => CascadeCount >= 3 && PartitionMode == PartitionMode.Manual; - bool ShowCascade4 => CascadeCount >= 4 && PartitionMode == PartitionMode.Manual; - } -} - namespace FlaxEditor.SceneGraph.Actors { /// diff --git a/Source/Engine/Level/DirectionalLight.cs b/Source/Engine/Level/DirectionalLight.cs new file mode 100644 index 000000000..201d35dc1 --- /dev/null +++ b/Source/Engine/Level/DirectionalLight.cs @@ -0,0 +1,10 @@ +namespace FlaxEngine +{ + public partial class DirectionalLight + { + bool ShowCascade1 => CascadeCount >= 1 && PartitionMode == PartitionMode.Manual; + bool ShowCascade2 => CascadeCount >= 2 && PartitionMode == PartitionMode.Manual; + bool ShowCascade3 => CascadeCount >= 3 && PartitionMode == PartitionMode.Manual; + bool ShowCascade4 => CascadeCount >= 4 && PartitionMode == PartitionMode.Manual; + } +} From 5cf20b8eeeb647a59be05cad7d8598652395101b Mon Sep 17 00:00:00 2001 From: Menotdan <32620310+Menotdan@users.noreply.github.com> Date: Sun, 31 Dec 2023 21:11:39 -0500 Subject: [PATCH 24/36] Add an option to skip existing materials when reimporting. --- Source/Engine/Tools/ModelTool/ModelTool.cpp | 14 ++++++++++++++ Source/Engine/Tools/ModelTool/ModelTool.h | 7 +++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Tools/ModelTool/ModelTool.cpp b/Source/Engine/Tools/ModelTool/ModelTool.cpp index e2b2f5d8d..d0aa537f6 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.cpp @@ -374,6 +374,7 @@ void ModelTool::Options::Serialize(SerializeStream& stream, const void* otherObj SERIALIZE(InstanceToImportAs); SERIALIZE(ImportTextures); SERIALIZE(RestoreMaterialsOnReimport); + SERIALIZE(SkipExistingMaterialsOnReimport); SERIALIZE(GenerateSDF); SERIALIZE(SDFResolution); SERIALIZE(SplitObjects); @@ -422,6 +423,7 @@ void ModelTool::Options::Deserialize(DeserializeStream& stream, ISerializeModifi DESERIALIZE(InstanceToImportAs); DESERIALIZE(ImportTextures); DESERIALIZE(RestoreMaterialsOnReimport); + DESERIALIZE(SkipExistingMaterialsOnReimport); DESERIALIZE(GenerateSDF); DESERIALIZE(SDFResolution); DESERIALIZE(SplitObjects); @@ -1154,6 +1156,18 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option continue; } + // Skip any materials that already exist from the model. + // This allows the use of "import as material instances" without material properties getting overridden on each import. + if (options.SkipExistingMaterialsOnReimport) + { + AssetInfo info; + if (Content::GetAssetInfo(assetPath, info)) + { + material.AssetID = info.ID; + continue; + } + } + if (options.ImportMaterialsAsInstances) { // Create material instance diff --git a/Source/Engine/Tools/ModelTool/ModelTool.h b/Source/Engine/Tools/ModelTool/ModelTool.h index 416505631..629da7a3b 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.h +++ b/Source/Engine/Tools/ModelTool/ModelTool.h @@ -268,9 +268,12 @@ public: // If checked, the importer will import texture files used by the model and any embedded texture resources. API_FIELD(Attributes="EditorOrder(410), EditorDisplay(\"Materials\"), VisibleIf(nameof(ShowGeometry))") bool ImportTextures = true; - // If checked, the importer will try to keep the model's current material slots, instead of importing materials from the source file. - API_FIELD(Attributes="EditorOrder(420), EditorDisplay(\"Materials\", \"Keep Material Slots on Reimport\"), VisibleIf(nameof(ShowGeometry))") + // If checked, the importer will try to keep the model's current overridden material slots, instead of importing materials from the source file. + API_FIELD(Attributes="EditorOrder(420), EditorDisplay(\"Materials\", \"Keep Overridden Materials\"), VisibleIf(nameof(ShowGeometry))") bool RestoreMaterialsOnReimport = true; + // If checked, the importer will not reimport any material from this model which already exist in the sub-asset folder. + API_FIELD(Attributes = "EditorOrder(421), EditorDisplay(\"Materials\", \"Skip Existing Materials\"), VisibleIf(nameof(ShowGeometry))") + bool SkipExistingMaterialsOnReimport = true; public: // SDF From 8bcc526dd61d775accc79abb160281ce52c14273 Mon Sep 17 00:00:00 2001 From: Menotdan <32620310+Menotdan@users.noreply.github.com> Date: Mon, 1 Jan 2024 01:24:36 -0500 Subject: [PATCH 25/36] Add support for multiple VisibleIf attributes, and properly set up the VisibleIf entries for ImportMaterialsAsInstances and InstanceToImportAs. --- .../CustomEditors/Editors/GenericEditor.cs | 91 +++++++++++-------- .../Attributes/Editor/VisibleIfAttribute.cs | 4 +- Source/Engine/Tools/ModelTool/ModelTool.h | 4 +- 3 files changed, 57 insertions(+), 42 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/GenericEditor.cs b/Source/Editor/CustomEditors/Editors/GenericEditor.cs index 68c24a675..3359054fe 100644 --- a/Source/Editor/CustomEditors/Editors/GenericEditor.cs +++ b/Source/Editor/CustomEditors/Editors/GenericEditor.cs @@ -66,9 +66,9 @@ namespace FlaxEditor.CustomEditors.Editors public HeaderAttribute Header; /// - /// The visible if attribute. + /// The visible if attributes. /// - public VisibleIfAttribute VisibleIf; + public VisibleIfAttribute[] VisibleIfs; /// /// The read-only attribute usage flag. @@ -128,7 +128,7 @@ namespace FlaxEditor.CustomEditors.Editors CustomEditorAlias = (CustomEditorAliasAttribute)attributes.FirstOrDefault(x => x is CustomEditorAliasAttribute); Space = (SpaceAttribute)attributes.FirstOrDefault(x => x is SpaceAttribute); Header = (HeaderAttribute)attributes.FirstOrDefault(x => x is HeaderAttribute); - VisibleIf = (VisibleIfAttribute)attributes.FirstOrDefault(x => x is VisibleIfAttribute); + VisibleIfs = attributes.OfType().ToArray(); IsReadOnly = attributes.FirstOrDefault(x => x is ReadOnlyAttribute) != null; ExpandGroups = attributes.FirstOrDefault(x => x is ExpandGroupsAttribute) != null; @@ -210,17 +210,24 @@ namespace FlaxEditor.CustomEditors.Editors private struct VisibleIfCache { public ScriptMemberInfo Target; - public ScriptMemberInfo Source; + public ScriptMemberInfo[] Sources; public PropertiesListElement PropertiesList; public GroupElement Group; - public bool Invert; + public bool[] InversionList; public int LabelIndex; public bool GetValue(object instance) { - var value = (bool)Source.GetValue(instance); - if (Invert) - value = !value; + bool value = true; + + for (int i = 0; i < Sources.Length; i++) + { + bool currentValue = (bool)Sources[i].GetValue(instance); + if (InversionList[i]) + currentValue = !currentValue; + + value = value && currentValue; + } return value; } } @@ -298,40 +305,48 @@ namespace FlaxEditor.CustomEditors.Editors return items; } - private static ScriptMemberInfo GetVisibleIfSource(ScriptType type, VisibleIfAttribute visibleIf) + private static ScriptMemberInfo[] GetVisibleIfSources(ScriptType type, VisibleIfAttribute[] visibleIfs) { - var property = type.GetProperty(visibleIf.MemberName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); - if (property != ScriptMemberInfo.Null) + ScriptMemberInfo[] members = Array.Empty(); + + for (int i = 0; i < visibleIfs.Length; i++) { - if (!property.HasGet) + var property = type.GetProperty(visibleIfs[i].MemberName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); + if (property != ScriptMemberInfo.Null) { - Debug.LogError("Invalid VisibleIf rule. Property has missing getter " + visibleIf.MemberName); - return ScriptMemberInfo.Null; + if (!property.HasGet) + { + Debug.LogError("Invalid VisibleIf rule. Property has missing getter " + visibleIfs[i].MemberName); + continue; + } + + if (property.ValueType.Type != typeof(bool)) + { + Debug.LogError("Invalid VisibleIf rule. Property has to return bool type " + visibleIfs[i].MemberName); + continue; + } + + members = members.Append(property).ToArray(); + continue; } - if (property.ValueType.Type != typeof(bool)) + var field = type.GetField(visibleIfs[i].MemberName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); + if (field != ScriptMemberInfo.Null) { - Debug.LogError("Invalid VisibleIf rule. Property has to return bool type " + visibleIf.MemberName); - return ScriptMemberInfo.Null; + if (field.ValueType.Type != typeof(bool)) + { + Debug.LogError("Invalid VisibleIf rule. Field has to be bool type " + visibleIfs[i].MemberName); + continue; + } + + members = members.Append(field).ToArray(); + continue; } - return property; + Debug.LogError("Invalid VisibleIf rule. Cannot find member " + visibleIfs[i].MemberName); } - var field = type.GetField(visibleIf.MemberName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); - if (field != ScriptMemberInfo.Null) - { - if (field.ValueType.Type != typeof(bool)) - { - Debug.LogError("Invalid VisibleIf rule. Field has to be bool type " + visibleIf.MemberName); - return ScriptMemberInfo.Null; - } - - return field; - } - - Debug.LogError("Invalid VisibleIf rule. Cannot find member " + visibleIf.MemberName); - return ScriptMemberInfo.Null; + return members; } private static void GroupPanelCheckIfCanRevert(LayoutElementsContainer layout, ref bool canRevertReference, ref bool canRevertDefault) @@ -575,7 +590,7 @@ namespace FlaxEditor.CustomEditors.Editors protected virtual void SpawnProperty(LayoutElementsContainer itemLayout, ValueContainer itemValues, ItemInfo item) { int labelIndex = 0; - if ((item.IsReadOnly || item.VisibleIf != null) && + if ((item.IsReadOnly || item.VisibleIfs.Length > 0) && itemLayout.Children.Count > 0 && itemLayout.Children[itemLayout.Children.Count - 1] is PropertiesListElement propertiesListElement) { @@ -616,7 +631,7 @@ namespace FlaxEditor.CustomEditors.Editors } } } - if (item.VisibleIf != null && itemLayout.Children.Count > 0) + if (item.VisibleIfs.Length > 0 && itemLayout.Children.Count > 0) { PropertiesListElement list = null; GroupElement group = null; @@ -628,8 +643,8 @@ namespace FlaxEditor.CustomEditors.Editors return; // Get source member used to check rule - var sourceMember = GetVisibleIfSource(item.Info.DeclaringType, item.VisibleIf); - if (sourceMember == ScriptType.Null) + var sourceMembers = GetVisibleIfSources(item.Info.DeclaringType, item.VisibleIfs); + if (sourceMembers.Length == 0) return; // Resize cache @@ -645,11 +660,11 @@ namespace FlaxEditor.CustomEditors.Editors _visibleIfCaches[count] = new VisibleIfCache { Target = item.Info, - Source = sourceMember, + Sources = sourceMembers, PropertiesList = list, Group = group, LabelIndex = labelIndex, - Invert = item.VisibleIf.Invert, + InversionList = item.VisibleIfs.Select((x, i) => x.Invert).ToArray(), }; } } diff --git a/Source/Engine/Scripting/Attributes/Editor/VisibleIfAttribute.cs b/Source/Engine/Scripting/Attributes/Editor/VisibleIfAttribute.cs index 5e690caa8..3d606370a 100644 --- a/Source/Engine/Scripting/Attributes/Editor/VisibleIfAttribute.cs +++ b/Source/Engine/Scripting/Attributes/Editor/VisibleIfAttribute.cs @@ -5,11 +5,11 @@ using System; namespace FlaxEngine { /// - /// Shows property/field in the editor only if the specified member has a given value. Can be used to hide properties based on other properties (also private properties). The given member has to be bool type. + /// Shows property/field in the editor only if the specified member has a given value. Can be used to hide properties based on other properties (also private properties). The given member has to be bool type. Multiple VisibleIf attributes can be added for additional conditions to be met. /// /// [Serializable] - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] public sealed class VisibleIfAttribute : Attribute { /// diff --git a/Source/Engine/Tools/ModelTool/ModelTool.h b/Source/Engine/Tools/ModelTool/ModelTool.h index 416505631..7ae199136 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.h +++ b/Source/Engine/Tools/ModelTool/ModelTool.h @@ -260,10 +260,10 @@ public: API_FIELD(Attributes="EditorOrder(400), EditorDisplay(\"Materials\"), VisibleIf(nameof(ShowGeometry))") bool ImportMaterials = true; // If checked, the importer will create the model's materials as instances of a base material. - API_FIELD(Attributes = "EditorOrder(401), EditorDisplay(\"Materials\"), VisibleIf(nameof(ImportMaterials))") + API_FIELD(Attributes = "EditorOrder(401), EditorDisplay(\"Materials\"), VisibleIf(nameof(ImportMaterials)), VisibleIf(nameof(ShowGeometry))") bool ImportMaterialsAsInstances = false; // The material used as the base material that will be instanced as the imported model's material. - API_FIELD(Attributes = "EditorOrder(402), EditorDisplay(\"Materials\"), VisibleIf(nameof(ImportMaterialsAsInstances))") + API_FIELD(Attributes = "EditorOrder(402), EditorDisplay(\"Materials\"), VisibleIf(nameof(ImportMaterialsAsInstances)), VisibleIf(nameof(ShowGeometry))") AssetReference InstanceToImportAs; // If checked, the importer will import texture files used by the model and any embedded texture resources. API_FIELD(Attributes="EditorOrder(410), EditorDisplay(\"Materials\"), VisibleIf(nameof(ShowGeometry))") From a8913d89acc88808765df58e37afb44587b1d476 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Mon, 1 Jan 2024 17:46:33 +0200 Subject: [PATCH 26/36] Fix C++ Intellisense not working with latest version of Rider Project `IncludePath` doesn't seem to include NMake include paths anymore in Rider, populate the property with same paths to work around the issue. --- .../Flax.Build/Projects/VisualStudio/VCProjectGenerator.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/VCProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/VCProjectGenerator.cs index 4eba06cb0..4facc73c0 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudio/VCProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudio/VCProjectGenerator.cs @@ -179,7 +179,10 @@ namespace Flax.Build.Projects.VisualStudio customizer.WriteVisualStudioBuildProperties(vsProject, platform, toolchain, configuration, vcProjectFileContent, vcFiltersFileContent, vcUserFileContent); vcProjectFileContent.AppendLine(string.Format(" {0}", targetBuildOptions.IntermediateFolder)); vcProjectFileContent.AppendLine(string.Format(" {0}", targetBuildOptions.OutputFolder)); - vcProjectFileContent.AppendLine(" "); + if (includePaths.Count != 0) + vcProjectFileContent.AppendLine(string.Format(" $(IncludePath);{0}", string.Join(";", includePaths))); + else + vcProjectFileContent.AppendLine(" "); vcProjectFileContent.AppendLine(" "); vcProjectFileContent.AppendLine(" "); vcProjectFileContent.AppendLine(" "); From e0cb94ec747436c96c44971209f3dce6f52aab74 Mon Sep 17 00:00:00 2001 From: z1dev Date: Tue, 2 Jan 2024 00:08:00 +0100 Subject: [PATCH 27/36] Fix for editor view's cached "CameraNearPlaneValue" getting overwritten. Changing the editor's camera sometimes results in empty views in editors. It was caused by writing the _farPlane with the wrong name in the cache. --- Source/Editor/Viewport/EditorViewport.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index 9ac9b5571..526a84c45 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -1113,7 +1113,7 @@ namespace FlaxEditor.Viewport private void OnFarPlaneChanged(FloatValueBox control) { _farPlane = control.Value; - _editor.ProjectCache.SetCustomData("CameraNearPlaneValue", _farPlane.ToString()); + _editor.ProjectCache.SetCustomData("CameraFarPlaneValue", _farPlane.ToString()); } /// From 329bab11020b1dfee9641617c1615ed3bdfb1ee0 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 2 Jan 2024 11:06:54 +0100 Subject: [PATCH 28/36] Fix minor issue --- Source/Editor/CustomEditors/Editors/FloatEditor.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Editor/CustomEditors/Editors/FloatEditor.cs b/Source/Editor/CustomEditors/Editors/FloatEditor.cs index a79dc036f..07c07030e 100644 --- a/Source/Editor/CustomEditors/Editors/FloatEditor.cs +++ b/Source/Editor/CustomEditors/Editors/FloatEditor.cs @@ -88,6 +88,8 @@ namespace FlaxEditor.CustomEditors.Editors _element.Value = asFloat; else if (value is double asDouble) _element.Value = (float)asDouble; + else if (value is int asInt) + _element.Value = (float)asInt; else throw new Exception(string.Format("Invalid value type {0}.", value?.GetType().ToString() ?? "")); } From 8e331e4d1344b821f47b6cc6374b0f312c20d3a3 Mon Sep 17 00:00:00 2001 From: Wiktor Kocielski Date: Thu, 4 Jan 2024 08:54:04 +0300 Subject: [PATCH 29/36] Silently drop unreliable RPC calls if we failed to find an object --- .../Engine/Networking/NetworkReplicator.cpp | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/Source/Engine/Networking/NetworkReplicator.cpp b/Source/Engine/Networking/NetworkReplicator.cpp index c6311cd7a..f9efba4a5 100644 --- a/Source/Engine/Networking/NetworkReplicator.cpp +++ b/Source/Engine/Networking/NetworkReplicator.cpp @@ -2151,6 +2151,18 @@ void NetworkInternal::OnNetworkMessageObjectRpc(NetworkEvent& event, NetworkClie NetworkMessageObjectRpc msgData; event.Message.ReadStructure(msgData); ScopeLock lock(ObjectsLock); + + // Find RPC info + NetworkRpcName name; + name.First = Scripting::FindScriptingType(msgData.RpcTypeName); + name.Second = msgData.RpcName; + const NetworkRpcInfo* info = NetworkRpcInfo::RPCsTable.TryGet(name); + if (!info) + { + NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Unknown RPC {}::{} for object {}", String(msgData.RpcTypeName), String(msgData.RpcName), msgData.ObjectId); + return; + } + NetworkReplicatedObject* e = ResolveObject(msgData.ObjectId, msgData.ParentId, msgData.ObjectTypeName); if (e) { @@ -2159,17 +2171,7 @@ void NetworkInternal::OnNetworkMessageObjectRpc(NetworkEvent& event, NetworkClie if (!obj) return; - // Find RPC info - NetworkRpcName name; - name.First = Scripting::FindScriptingType(msgData.RpcTypeName); - name.Second = msgData.RpcName; - const NetworkRpcInfo* info = NetworkRpcInfo::RPCsTable.TryGet(name); - if (!info) - { - NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Unknown RPC {}::{} for object {}", String(msgData.RpcTypeName), String(msgData.RpcName), msgData.ObjectId); - return; - } - + // Validate RPC if (info->Server && NetworkManager::IsClient()) { @@ -2192,7 +2194,7 @@ void NetworkInternal::OnNetworkMessageObjectRpc(NetworkEvent& event, NetworkClie // Execute RPC info->Execute(obj, stream, info->Tag); } - else + else if(info->Channel != static_cast(NetworkChannelType::Unreliable) && info->Channel != static_cast(NetworkChannelType::UnreliableOrdered)) { NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Unknown object {} RPC {}::{}", msgData.ObjectId, String(msgData.RpcTypeName), String(msgData.RpcName)); } From 976d0992df59046a30d7fe07c29f09bf683b95b3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 5 Jan 2024 11:13:41 +0100 Subject: [PATCH 30/36] Fix `DateTime::GetDate` calculations #2089 #2086 --- Source/Engine/Core/Types/DateTime.cpp | 4 +--- Source/Engine/Tests/TestTime.cpp | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 Source/Engine/Tests/TestTime.cpp diff --git a/Source/Engine/Core/Types/DateTime.cpp b/Source/Engine/Core/Types/DateTime.cpp index a0d873b43..3141cd52d 100644 --- a/Source/Engine/Core/Types/DateTime.cpp +++ b/Source/Engine/Core/Types/DateTime.cpp @@ -35,8 +35,7 @@ void DateTime::GetDate(int32& year, int32& month, int32& day) const // Based on: // Fliegel, H. F. and van Flandern, T. C., // Communications of the ACM, Vol. 11, No. 10 (October 1968). - - int32 l = Math::FloorToInt(static_cast(GetJulianDay() + 0.5)) + 68569; + int32 l = Math::FloorToInt((float)(GetDate().GetJulianDay() + 0.5)) + 68569; const int32 n = 4 * l / 146097; l = l - (146097 * n + 3) / 4; int32 i = 4000 * (l + 1) / 1461001; @@ -46,7 +45,6 @@ void DateTime::GetDate(int32& year, int32& month, int32& day) const l = j / 11; j = j + 2 - 12 * l; i = 100 * (n - 49) + i + l; - year = i; month = j; day = k; diff --git a/Source/Engine/Tests/TestTime.cpp b/Source/Engine/Tests/TestTime.cpp new file mode 100644 index 000000000..0419c5e13 --- /dev/null +++ b/Source/Engine/Tests/TestTime.cpp @@ -0,0 +1,26 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +#include "Engine/Core/Types/DateTime.h" +#include + +TEST_CASE("DateTime") +{ + SECTION("Test Convertion") + { + constexpr int year = 2023; + constexpr int month = 12; + constexpr int day = 16; + constexpr int hour = 23; + constexpr int minute = 50; + constexpr int second = 13; + constexpr int millisecond = 5; + const DateTime dt1(year, month, day, hour, minute, second, millisecond); + CHECK(dt1.GetYear() == year); + CHECK(dt1.GetMonth() == month); + CHECK(dt1.GetDay() == day); + CHECK(dt1.GetHour() == hour); + CHECK(dt1.GetMinute() == minute); + CHECK(dt1.GetSecond() == second); + CHECK(dt1.GetMillisecond() == millisecond); + } +} From 37da55896c47b93f34ae20b60901e901fe07aab1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 5 Jan 2024 11:15:32 +0100 Subject: [PATCH 31/36] Remove unused DateTime apis and cleanup constants --- Source/Engine/Core/Types/DateTime.cpp | 49 ++++++----------- Source/Engine/Core/Types/DateTime.h | 19 ------- Source/Engine/Core/Types/TimeSpan.cpp | 27 +++++++--- Source/Engine/Core/Types/TimeSpan.h | 78 +++++++++++---------------- Source/Engine/Engine/Time.cpp | 4 +- 5 files changed, 72 insertions(+), 105 deletions(-) diff --git a/Source/Engine/Core/Types/DateTime.cpp b/Source/Engine/Core/Types/DateTime.cpp index 3141cd52d..85b42378c 100644 --- a/Source/Engine/Core/Types/DateTime.cpp +++ b/Source/Engine/Core/Types/DateTime.cpp @@ -12,22 +12,22 @@ const int32 CachedDaysToMonth[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, DateTime::DateTime(int32 year, int32 month, int32 day, int32 hour, int32 minute, int32 second, int32 millisecond) { ASSERT_LOW_LAYER(Validate(year, month, day, hour, minute, second, millisecond)); - int32 totalDays = 0; + int32 daysSum = 0; if (month > 2 && IsLeapYear(year)) - totalDays++; + daysSum++; year--; month--; - totalDays += year * 365 + year / 4 - year / 100 + year / 400 + CachedDaysToMonth[month] + day - 1; - Ticks = totalDays * Constants::TicksPerDay - + hour * Constants::TicksPerHour - + minute * Constants::TicksPerMinute - + second * Constants::TicksPerSecond - + millisecond * Constants::TicksPerMillisecond; + daysSum += year * 365 + year / 4 - year / 100 + year / 400 + CachedDaysToMonth[month] + day - 1; + Ticks = daysSum * TimeSpan::TicksPerDay + + hour * TimeSpan::TicksPerHour + + minute * TimeSpan::TicksPerMinute + + second * TimeSpan::TicksPerSecond + + millisecond * TimeSpan::TicksPerMillisecond; } DateTime DateTime::GetDate() const { - return DateTime(Ticks - Ticks % Constants::TicksPerDay); + return DateTime(Ticks - Ticks % TimeSpan::TicksPerDay); } void DateTime::GetDate(int32& year, int32& month, int32& day) const @@ -59,7 +59,7 @@ int32 DateTime::GetDay() const DayOfWeek DateTime::GetDayOfWeek() const { - return static_cast((Ticks / Constants::TicksPerDay) % 7); + return static_cast((Ticks / TimeSpan::TicksPerDay) % 7); } int32 DateTime::GetDayOfYear() const @@ -73,7 +73,7 @@ int32 DateTime::GetDayOfYear() const int32 DateTime::GetHour() const { - return static_cast(Ticks / Constants::TicksPerHour % 24); + return static_cast(Ticks / TimeSpan::TicksPerHour % 24); } int32 DateTime::GetHour12() const @@ -88,7 +88,7 @@ int32 DateTime::GetHour12() const double DateTime::GetJulianDay() const { - return 1721425.5 + static_cast(Ticks) / Constants::TicksPerDay; + return 1721425.5 + static_cast(Ticks) / TimeSpan::TicksPerDay; } double DateTime::GetModifiedJulianDay() const @@ -98,12 +98,12 @@ double DateTime::GetModifiedJulianDay() const int32 DateTime::GetMillisecond() const { - return static_cast(Ticks / Constants::TicksPerMillisecond % 1000); + return static_cast(Ticks / TimeSpan::TicksPerMillisecond % 1000); } int32 DateTime::GetMinute() const { - return static_cast(Ticks / Constants::TicksPerMinute % 60); + return static_cast(Ticks / TimeSpan::TicksPerMinute % 60); } int32 DateTime::GetMonth() const @@ -120,12 +120,12 @@ MonthOfYear DateTime::GetMonthOfYear() const int32 DateTime::GetSecond() const { - return static_cast(Ticks / Constants::TicksPerSecond % 60); + return static_cast(Ticks / TimeSpan::TicksPerSecond % 60); } TimeSpan DateTime::GetTimeOfDay() const { - return TimeSpan(Ticks % Constants::TicksPerDay); + return TimeSpan(Ticks % TimeSpan::TicksPerDay); } int32 DateTime::GetYear() const @@ -135,11 +135,6 @@ int32 DateTime::GetYear() const return year; } -int32 DateTime::ToUnixTimestamp() const -{ - return static_cast((Ticks - DateTime(1970, 1, 1).Ticks) / Constants::TicksPerSecond); -} - int32 DateTime::DaysInMonth(int32 year, int32 month) { ASSERT_LOW_LAYER((month >= 1) && (month <= 12)); @@ -153,16 +148,6 @@ int32 DateTime::DaysInYear(int32 year) return IsLeapYear(year) ? 366 : 365; } -DateTime DateTime::FromJulianDay(double julianDay) -{ - return DateTime(static_cast((julianDay - 1721425.5) * Constants::TicksPerDay)); -} - -DateTime DateTime::FromUnixTimestamp(int32 unixTime) -{ - return DateTime(1970, 1, 1) + TimeSpan(static_cast(unixTime) * Constants::TicksPerSecond); -} - bool DateTime::IsLeapYear(int32 year) { if ((year % 4) == 0) @@ -174,7 +159,7 @@ bool DateTime::IsLeapYear(int32 year) DateTime DateTime::MaxValue() { - return DateTime(3652059 * Constants::TicksPerDay - 1); + return DateTime(3652059 * TimeSpan::TicksPerDay - 1); } DateTime DateTime::Now() diff --git a/Source/Engine/Core/Types/DateTime.h b/Source/Engine/Core/Types/DateTime.h index 67b2d70ff..a89a69fcf 100644 --- a/Source/Engine/Core/Types/DateTime.h +++ b/Source/Engine/Core/Types/DateTime.h @@ -199,11 +199,6 @@ public: /// int32 GetYear() const; - /// - /// Gets this date as the number of seconds since the Unix Epoch (January 1st of 1970). - /// - int32 ToUnixTimestamp() const; - public: /// /// Gets the number of days in the year and month. @@ -220,20 +215,6 @@ public: /// The number of days. static int32 DaysInYear(int32 year); - /// - /// Returns the proleptic Gregorian date for the given Julian Day. - /// - /// The Julian Day. - /// Gregorian date and time. - static DateTime FromJulianDay(double julianDay); - - /// - /// Returns the date from Unix time (seconds from midnight 1970-01-01). - /// - /// The Unix time (seconds from midnight 1970-01-01). - /// The Gregorian date and time. - static DateTime FromUnixTimestamp(int32 unixTime); - /// /// Determines whether the specified year is a leap year. /// diff --git a/Source/Engine/Core/Types/TimeSpan.cpp b/Source/Engine/Core/Types/TimeSpan.cpp index 0e4aed407..287097672 100644 --- a/Source/Engine/Core/Types/TimeSpan.cpp +++ b/Source/Engine/Core/Types/TimeSpan.cpp @@ -6,38 +6,53 @@ TimeSpan TimeSpan::FromDays(double days) { ASSERT_LOW_LAYER((days >= MinValue().GetTotalDays()) && (days <= MaxValue().GetTotalDays())); - return TimeSpan(static_cast(days * Constants::TicksPerDay)); + return TimeSpan(static_cast(days * TicksPerDay)); } TimeSpan TimeSpan::FromHours(double hours) { ASSERT_LOW_LAYER((hours >= MinValue().GetTotalHours()) && (hours <= MaxValue().GetTotalHours())); - return TimeSpan(static_cast(hours * Constants::TicksPerHour)); + return TimeSpan(static_cast(hours * TicksPerHour)); } TimeSpan TimeSpan::FromMilliseconds(double milliseconds) { ASSERT_LOW_LAYER((milliseconds >= MinValue().GetTotalMilliseconds()) && (milliseconds <= MaxValue().GetTotalMilliseconds())); - return TimeSpan(static_cast(milliseconds * Constants::TicksPerMillisecond)); + return TimeSpan(static_cast(milliseconds * TicksPerMillisecond)); } TimeSpan TimeSpan::FromMinutes(double minutes) { ASSERT_LOW_LAYER((minutes >= MinValue().GetTotalMinutes()) && (minutes <= MaxValue().GetTotalMinutes())); - return TimeSpan(static_cast(minutes * Constants::TicksPerMinute)); + return TimeSpan(static_cast(minutes * TicksPerMinute)); } TimeSpan TimeSpan::FromSeconds(double seconds) { ASSERT_LOW_LAYER((seconds >= MinValue().GetTotalSeconds()) && (seconds <= MaxValue().GetTotalSeconds())); - return TimeSpan(static_cast(seconds * Constants::TicksPerSecond)); + return TimeSpan(static_cast(seconds * TicksPerSecond)); +} + +TimeSpan TimeSpan::MaxValue() +{ + return TimeSpan(9223372036854775807); +} + +TimeSpan TimeSpan::MinValue() +{ + return TimeSpan(-9223372036854775807 - 1); +} + +TimeSpan TimeSpan::Zero() +{ + return TimeSpan(0); } void TimeSpan::Set(int32 days, int32 hours, int32 minutes, int32 seconds, int32 milliseconds) { const int64 totalMs = 1000 * (60 * 60 * 24 * (int64)days + 60 * 60 * (int64)hours + 60 * (int64)minutes + (int64)seconds) + (int64)milliseconds; ASSERT_LOW_LAYER((totalMs >= MinValue().GetTotalMilliseconds()) && (totalMs <= MaxValue().GetTotalMilliseconds())); - Ticks = totalMs * Constants::TicksPerMillisecond; + Ticks = totalMs * TicksPerMillisecond; } String TimeSpan::ToString() const diff --git a/Source/Engine/Core/Types/TimeSpan.h b/Source/Engine/Core/Types/TimeSpan.h index 7545a21c0..f14a622a0 100644 --- a/Source/Engine/Core/Types/TimeSpan.h +++ b/Source/Engine/Core/Types/TimeSpan.h @@ -6,32 +6,30 @@ #include "Engine/Core/Formatting.h" #include "Engine/Core/Templates.h" -namespace Constants -{ - // The number of timespan ticks per day. - const int64 TicksPerDay = 864000000000; - - // The number of timespan ticks per hour. - const int64 TicksPerHour = 36000000000; - - // The number of timespan ticks per millisecond. - const int64 TicksPerMillisecond = 10000; - - // The number of timespan ticks per minute. - const int64 TicksPerMinute = 600000000; - - // The number of timespan ticks per second. - const int64 TicksPerSecond = 10000000; - - // The number of timespan ticks per week. - const int64 TicksPerWeek = 6048000000000; -} - /// /// Represents the difference between two dates and times. /// API_STRUCT(InBuild, Namespace="System") struct FLAXENGINE_API TimeSpan { +public: + // The number of timespan ticks per day. + static constexpr int64 TicksPerDay = 864000000000; + + // The number of timespan ticks per hour. + static constexpr int64 TicksPerHour = 36000000000; + + // The number of timespan ticks per millisecond. + static constexpr int64 TicksPerMillisecond = 10000; + + // The number of timespan ticks per minute. + static constexpr int64 TicksPerMinute = 600000000; + + // The number of timespan ticks per second. + static constexpr int64 TicksPerSecond = 10000000; + + // The number of timespan ticks per week. + static constexpr int64 TicksPerWeek = 6048000000000; + public: /// /// Time span in 100 nanoseconds resolution. @@ -170,7 +168,7 @@ public: /// FORCE_INLINE int32 GetDays() const { - return (int32)(Ticks / Constants::TicksPerDay); + return (int32)(Ticks / TicksPerDay); } /// @@ -186,7 +184,7 @@ public: /// FORCE_INLINE int32 GetHours() const { - return (int32)(Ticks / Constants::TicksPerHour % 24); + return (int32)(Ticks / TicksPerHour % 24); } /// @@ -194,7 +192,7 @@ public: /// FORCE_INLINE int32 GetMilliseconds() const { - return (int32)(Ticks / Constants::TicksPerMillisecond % 1000); + return (int32)(Ticks / TicksPerMillisecond % 1000); } /// @@ -202,7 +200,7 @@ public: /// FORCE_INLINE int32 GetMinutes() const { - return (int32)(Ticks / Constants::TicksPerMinute % 60); + return (int32)(Ticks / TicksPerMinute % 60); } /// @@ -210,7 +208,7 @@ public: /// FORCE_INLINE int32 GetSeconds() const { - return (int32)(Ticks / Constants::TicksPerSecond % 60); + return (int32)(Ticks / TicksPerSecond % 60); } /// @@ -218,7 +216,7 @@ public: /// FORCE_INLINE double GetTotalDays() const { - return (double)Ticks / Constants::TicksPerDay; + return (double)Ticks / TicksPerDay; } /// @@ -226,7 +224,7 @@ public: /// FORCE_INLINE double GetTotalHours() const { - return (double)Ticks / Constants::TicksPerHour; + return (double)Ticks / TicksPerHour; } /// @@ -234,7 +232,7 @@ public: /// FORCE_INLINE double GetTotalMilliseconds() const { - return (double)Ticks / Constants::TicksPerMillisecond; + return (double)Ticks / TicksPerMillisecond; } /// @@ -242,7 +240,7 @@ public: /// FORCE_INLINE double GetTotalMinutes() const { - return (double)Ticks / Constants::TicksPerMinute; + return (double)Ticks / TicksPerMinute; } /// @@ -250,7 +248,7 @@ public: /// FORCE_INLINE float GetTotalSeconds() const { - return static_cast(Ticks) / Constants::TicksPerSecond; + return static_cast(Ticks) / TicksPerSecond; } public: @@ -293,29 +291,17 @@ public: /// /// Returns the maximum time span value. /// - /// The time span. - static TimeSpan MaxValue() - { - return TimeSpan(9223372036854775807); - } + static TimeSpan MaxValue(); /// /// Returns the minimum time span value. /// - /// The time span. - static TimeSpan MinValue() - { - return TimeSpan(-9223372036854775807 - 1); - } + static TimeSpan MinValue(); /// /// Returns the zero time span value. /// - /// The time span. - static TimeSpan Zero() - { - return TimeSpan(0); - } + static TimeSpan Zero(); private: void Set(int32 days, int32 hours, int32 minutes, int32 seconds, int32 milliseconds); diff --git a/Source/Engine/Engine/Time.cpp b/Source/Engine/Engine/Time.cpp index 3adccdbd9..bb0c856f8 100644 --- a/Source/Engine/Engine/Time.cpp +++ b/Source/Engine/Engine/Time.cpp @@ -67,7 +67,7 @@ void Time::TickData::OnBeforeRun(float targetFps, double currentTime) { Time = UnscaledTime = TimeSpan::Zero(); DeltaTime = UnscaledDeltaTime = targetFps > ZeroTolerance ? TimeSpan::FromSeconds(1.0f / targetFps) : TimeSpan::Zero(); - LastLength = static_cast(DeltaTime.Ticks) / Constants::TicksPerSecond; + LastLength = static_cast(DeltaTime.Ticks) / TimeSpan::TicksPerSecond; LastBegin = currentTime - LastLength; LastEnd = currentTime; NextBegin = targetFps > ZeroTolerance ? LastBegin + (1.0f / targetFps) : 0.0; @@ -76,7 +76,7 @@ void Time::TickData::OnBeforeRun(float targetFps, double currentTime) void Time::TickData::OnReset(float targetFps, double currentTime) { DeltaTime = UnscaledDeltaTime = targetFps > ZeroTolerance ? TimeSpan::FromSeconds(1.0f / targetFps) : TimeSpan::Zero(); - LastLength = static_cast(DeltaTime.Ticks) / Constants::TicksPerSecond; + LastLength = static_cast(DeltaTime.Ticks) / TimeSpan::TicksPerSecond; LastBegin = currentTime - LastLength; LastEnd = currentTime; } From d9ca3e5b57df31bbb0116369f6de166c5df1a8c6 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 5 Jan 2024 11:29:53 +0100 Subject: [PATCH 32/36] Add sanity check to prevent crashes when Animated Model has NaN in skeleton pose #2118 --- Source/Engine/Animations/Graph/AnimGraph.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Source/Engine/Animations/Graph/AnimGraph.cpp b/Source/Engine/Animations/Graph/AnimGraph.cpp index 5aa720d31..7c4671a51 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.cpp +++ b/Source/Engine/Animations/Graph/AnimGraph.cpp @@ -282,6 +282,20 @@ void AnimGraphExecutor::Update(AnimGraphInstanceData& data, float dt) } } } +#if !BUILD_RELEASE + { + // Perform sanity check on nodes pose to prevent crashes due to NaNs + bool anyInvalid = animResult->RootMotion.IsNanOrInfinity(); + for (int32 i = 0; i < animResult->Nodes.Count(); i++) + anyInvalid |= animResult->Nodes.Get()[i].IsNanOrInfinity(); + if (anyInvalid) + { + LOG(Error, "Animated Model pose contains NaNs due to animations sampling/blending bug."); + context.Data = nullptr; + return; + } + } +#endif SkeletonData* animResultSkeleton = &skeleton; // Retarget animation when using output pose from other skeleton From 60fd4702a626241ef80293250f7be083ee9decb5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 5 Jan 2024 11:52:28 +0100 Subject: [PATCH 33/36] Fix crash when using degenerated triangle in Multi Blend 2D to properly sample animation #2118 --- .../Animations/Graph/AnimGroup.Animation.cpp | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp index 8a7751ecb..1058ea301 100644 --- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp +++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp @@ -1367,33 +1367,29 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu } // Use 1D blend if points are on the same line (degenerated triangle) - // TODO: simplify this code + struct BlendData + { + float AlphaX, AlphaY; + Animation* AnimA, *AnimB; + const Float4* AnimAd, *AnimBd; + }; + BlendData blendData; if (v1.Y >= v0.Y) { if (p.Y < v0.Y && v1.Y >= v0.Y) - { - const float alpha = p.Y / v0.Y; - value = SampleAnimationsWithBlend(node, loop, data.Length, startTimePos, bucket.TimePosition, newTimePos, aAnim, bAnim, aData.W, bData.W, alpha); - } + blendData = { p.Y, v0.Y, aAnim, bAnim, &aData, &bData }; else - { - const float alpha = (p.Y - v0.Y) / (v1.Y - v0.Y); - value = SampleAnimationsWithBlend(node, loop, data.Length, startTimePos, bucket.TimePosition, newTimePos, bAnim, cAnim, bData.W, cData.W, alpha); - } + blendData = { p.Y - v0.Y, v1.Y - v0.Y, bAnim, cAnim, &bData, &cData }; } else { if (p.Y < v1.Y) - { - const float alpha = p.Y / v1.Y; - value = SampleAnimationsWithBlend(node, loop, data.Length, startTimePos, bucket.TimePosition, newTimePos, aAnim, cAnim, aData.W, cData.W, alpha); - } + blendData = { p.Y, v1.Y, aAnim, cAnim, &aData, &cData }; else - { - const float alpha = (p.Y - v1.Y) / (v0.Y - v1.Y); - value = SampleAnimationsWithBlend(node, loop, data.Length, startTimePos, bucket.TimePosition, newTimePos, cAnim, bAnim, cData.W, bData.W, alpha); - } + blendData = { p.Y - v1.Y, v0.Y - v1.Y, cAnim, bAnim, &cData, &bData }; } + const float alpha = Math::IsZero(blendData.AlphaY) ? 0.0f : blendData.AlphaX / blendData.AlphaY; + value = SampleAnimationsWithBlend(node, loop, data.Length, startTimePos, bucket.TimePosition, newTimePos, blendData.AnimA, blendData.AnimB, blendData.AnimAd->W, blendData.AnimBd->W, alpha); } else { From 78b13ace5c73b184a5206c186752e4918e3492a1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 5 Jan 2024 12:19:37 +0100 Subject: [PATCH 34/36] Minor tweak --- .../Tools/MaterialGenerator/MaterialGenerator.Textures.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp index 475809b6c..a24c5e108 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp @@ -584,7 +584,6 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value) { // Procedural Texture Sample textureBox->Cache = writeLocal(Value::InitForZero(ValueType::Float4), node); - // createGradients(node); //PE: Not needed should always use the scaled or not scaled uv. auto proceduralSample = String::Format(TEXT( " {{\n" " float3 weights;\n" From 7f4d2d8db6718f3e4b8a7eb8ff3fe6b2fe46d239 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 5 Jan 2024 12:53:48 +0100 Subject: [PATCH 35/36] Fix GPU profiler event percentage calculation and add tint highlight to spot slow entries --- Source/Editor/Windows/Profiler/GPU.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Source/Editor/Windows/Profiler/GPU.cs b/Source/Editor/Windows/Profiler/GPU.cs index d2c34d335..6ddd5e704 100644 --- a/Source/Editor/Windows/Profiler/GPU.cs +++ b/Source/Editor/Windows/Profiler/GPU.cs @@ -321,8 +321,7 @@ namespace FlaxEditor.Windows.Profiler var data = _events.Get(_drawTimeCPU.SelectedSampleIndex); if (data == null || data.Length == 0) return; - - float totalTimeMs = _drawTimeCPU.SelectedSample; + float totalTimeMs = _drawTimeGPU.SelectedSample; // Add rows var rowColor2 = Style.Current.Background * 1.4f; @@ -343,14 +342,19 @@ namespace FlaxEditor.Windows.Profiler row = new Row { Values = new object[6], + BackgroundColors = new Color[6], }; + for (int k = 0; k < row.BackgroundColors.Length; k++) + row.BackgroundColors[k] = Color.Transparent; } { // Event row.Values[0] = name; // Total (%) - row.Values[1] = (int)(e.Time / totalTimeMs * 1000.0f) / 10.0f; + float rowTimePerc = (float)(e.Time / totalTimeMs); + row.Values[1] = (int)(rowTimePerc * 1000.0f) / 10.0f; + row.BackgroundColors[1] = Color.Red.AlphaMultiplied(Mathf.Min(1, rowTimePerc) * 0.5f); // GPU ms row.Values[2] = (e.Time * 10000.0f) / 10000.0f; From b6337c748c25f91c195ea7aa1825facab7637e7d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 27 Dec 2023 23:45:55 +0100 Subject: [PATCH 36/36] Fix stopping slot animations and playing the same frame #1927 --- Source/Engine/Animations/Graph/AnimGraph.h | 1 + Source/Engine/Animations/Graph/AnimGroup.Animation.cpp | 6 ++++++ Source/Engine/Level/Actors/AnimatedModel.cpp | 1 + 3 files changed, 8 insertions(+) diff --git a/Source/Engine/Animations/Graph/AnimGraph.h b/Source/Engine/Animations/Graph/AnimGraph.h index afaa4441b..30ac5a776 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.h +++ b/Source/Engine/Animations/Graph/AnimGraph.h @@ -197,6 +197,7 @@ struct FLAXENGINE_API AnimGraphSlot float BlendOutTime = 0.0f; int32 LoopCount = 0; bool Pause = false; + bool Reset = false; }; /// diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp index 1058ea301..fc7cf6dbc 100644 --- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp +++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp @@ -2098,6 +2098,12 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu auto& slot = slots[bucket.Index]; Animation* anim = slot.Animation; ASSERT(slot.Animation && slot.Animation->IsLoaded()); + if (slot.Reset) + { + // Start from the begining + slot.Reset = false; + bucket.TimePosition = 0.0f; + } const float deltaTime = slot.Pause ? 0.0f : context.DeltaTime * slot.Speed; const float length = anim->GetLength(); const bool loop = bucket.LoopsLeft != 0; diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index c1b5af398..17f117173 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -494,6 +494,7 @@ void AnimatedModel::StopSlotAnimation(const StringView& slotName, Animation* ani if (slot.Animation == anim && slot.Name == slotName) { slot.Animation = nullptr; + slot.Reset = true; break; } }