From baf604837704b4e62d5eef17b3417d222a5bdec7 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 3 Feb 2023 11:22:41 -0600 Subject: [PATCH 001/116] Added Play, pause, and stop functions to particle effect for more manual control if needed. --- Source/Engine/Particles/ParticleEffect.cpp | 45 ++++++++++++++++++++++ Source/Engine/Particles/ParticleEffect.h | 32 ++++++++++++++- 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Particles/ParticleEffect.cpp b/Source/Engine/Particles/ParticleEffect.cpp index 4b7923439..6b7346de3 100644 --- a/Source/Engine/Particles/ParticleEffect.cpp +++ b/Source/Engine/Particles/ParticleEffect.cpp @@ -275,6 +275,30 @@ void ParticleEffect::UpdateSimulation(bool singleFrame) Particles::UpdateEffect(this); } +void ParticleEffect::Play(bool reset) +{ + _play = true; + IsPlaying = true; + + if (reset) + ResetSimulation(); + else + UpdateSimulation(true); +} + +void ParticleEffect::Pause() +{ + _play = false; + IsPlaying = false; +} + +void ParticleEffect::Stop() +{ + _play = false; + IsPlaying = false; + ResetSimulation(); +} + void ParticleEffect::UpdateBounds() { BoundingBox bounds = BoundingBox::Empty; @@ -395,6 +419,9 @@ void ParticleEffect::SetParametersOverrides(const Array& valu void ParticleEffect::Update() { + if (!_play) + return; + // Skip if off-screen if (!UpdateWhenOffscreen && _lastMinDstSqr >= MAX_Real) return; @@ -417,7 +444,14 @@ void ParticleEffect::Update() void ParticleEffect::UpdateExecuteInEditor() { if (!Editor::IsPlayMode) + { + // Always Play in editor while not playing. + // Could be useful to have a GUI to change this state + if (!_play) + _play = true; + Update(); + } } #endif @@ -576,6 +610,7 @@ void ParticleEffect::Serialize(SerializeStream& stream, const void* otherObj) SERIALIZE(SimulationSpeed); SERIALIZE(UseTimeScale); SERIALIZE(IsLooping); + SERIALIZE(PlayOnStart); SERIALIZE(UpdateWhenOffscreen); SERIALIZE(DrawModes); SERIALIZE(SortOrder); @@ -675,6 +710,7 @@ void ParticleEffect::Deserialize(DeserializeStream& stream, ISerializeModifier* DESERIALIZE(SimulationSpeed); DESERIALIZE(UseTimeScale); DESERIALIZE(IsLooping); + DESERIALIZE(PlayOnStart); DESERIALIZE(UpdateWhenOffscreen); DESERIALIZE(DrawModes); DESERIALIZE(SortOrder); @@ -685,6 +721,12 @@ void ParticleEffect::Deserialize(DeserializeStream& stream, ISerializeModifier* } } +void ParticleEffect::BeginPlay(SceneBeginData* data) +{ + + Actor::BeginPlay(data); +} + void ParticleEffect::EndPlay() { CacheModifiedParameters(); @@ -706,6 +748,9 @@ void ParticleEffect::OnEnable() GetScene()->Ticking.Update.AddTickExecuteInEditor(this); #endif + if (PlayOnStart) + Play(); + // Base Actor::OnEnable(); } diff --git a/Source/Engine/Particles/ParticleEffect.h b/Source/Engine/Particles/ParticleEffect.h index 3487a35f4..a88628ccd 100644 --- a/Source/Engine/Particles/ParticleEffect.h +++ b/Source/Engine/Particles/ParticleEffect.h @@ -184,6 +184,7 @@ private: uint32 _parametersVersion = 0; // Version number for _parameters to be in sync with Instance.ParametersVersion Array _parameters; // Cached for scripting API Array _parametersOverrides; // Cached parameter modifications to be applied to the parameters + bool _play = false; public: /// @@ -235,9 +236,21 @@ public: bool IsLooping = true; /// - /// If true, the particle simulation will be updated even when an actor cannot be seen by any camera. Otherwise, the simulation will stop running when the actor is off-screen. + /// Determines whether the particle effect should play on start. /// API_FIELD(Attributes="EditorDisplay(\"Particle Effect\"), DefaultValue(true), EditorOrder(60)") + bool PlayOnStart = true; + + /// + /// If true, the particle effect is playing. + /// + API_FIELD() + bool IsPlaying = false; + + /// + /// If true, the particle simulation will be updated even when an actor cannot be seen by any camera. Otherwise, the simulation will stop running when the actor is off-screen. + /// + API_FIELD(Attributes="EditorDisplay(\"Particle Effect\"), DefaultValue(true), EditorOrder(70)") bool UpdateWhenOffscreen = true; /// @@ -337,6 +350,22 @@ public: /// True if update animation by a single frame only (time time since last engine update), otherwise will update simulation with delta time since last update. API_FUNCTION() void UpdateSimulation(bool singleFrame = false); + /// + /// Plays the simulation. + /// + /// /// If true, the simulation will be reset + API_FUNCTION() void Play(bool reset = true); + + /// + /// Pauses the simulation. + /// + API_FUNCTION() void Pause(); + + /// + /// Stops and resets the simulation. + /// + API_FUNCTION() void Stop(); + /// /// Updates the actor bounds. /// @@ -389,6 +418,7 @@ public: protected: // [Actor] + void BeginPlay(SceneBeginData* data) override; void EndPlay() override; void OnEnable() override; void OnDisable() override; From 2d98a46d94b2af98d4c389d72a52d5f09400540d Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 3 Feb 2023 11:27:11 -0600 Subject: [PATCH 002/116] Clean up code --- Source/Engine/Particles/ParticleEffect.cpp | 6 ------ Source/Engine/Particles/ParticleEffect.h | 1 - 2 files changed, 7 deletions(-) diff --git a/Source/Engine/Particles/ParticleEffect.cpp b/Source/Engine/Particles/ParticleEffect.cpp index 6b7346de3..2e76ea65b 100644 --- a/Source/Engine/Particles/ParticleEffect.cpp +++ b/Source/Engine/Particles/ParticleEffect.cpp @@ -721,12 +721,6 @@ void ParticleEffect::Deserialize(DeserializeStream& stream, ISerializeModifier* } } -void ParticleEffect::BeginPlay(SceneBeginData* data) -{ - - Actor::BeginPlay(data); -} - void ParticleEffect::EndPlay() { CacheModifiedParameters(); diff --git a/Source/Engine/Particles/ParticleEffect.h b/Source/Engine/Particles/ParticleEffect.h index a88628ccd..832327935 100644 --- a/Source/Engine/Particles/ParticleEffect.h +++ b/Source/Engine/Particles/ParticleEffect.h @@ -418,7 +418,6 @@ public: protected: // [Actor] - void BeginPlay(SceneBeginData* data) override; void EndPlay() override; void OnEnable() override; void OnDisable() override; From dbee9f681652f059640a02dc1ac9bab9ef82a5df Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 3 Feb 2023 12:11:18 -0600 Subject: [PATCH 003/116] Small fix --- Source/Engine/Particles/ParticleEffect.cpp | 11 ++++++++--- Source/Engine/Particles/ParticleEffect.h | 12 ++++++------ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/Source/Engine/Particles/ParticleEffect.cpp b/Source/Engine/Particles/ParticleEffect.cpp index 2e76ea65b..04fd6eedb 100644 --- a/Source/Engine/Particles/ParticleEffect.cpp +++ b/Source/Engine/Particles/ParticleEffect.cpp @@ -253,6 +253,11 @@ int32 ParticleEffect::GetParticlesCount() const return Instance.GetParticlesCount(); } +bool ParticleEffect::GetIsPlaying() const +{ + return _isPlaying; +} + void ParticleEffect::ResetSimulation() { Instance.ClearState(); @@ -278,7 +283,7 @@ void ParticleEffect::UpdateSimulation(bool singleFrame) void ParticleEffect::Play(bool reset) { _play = true; - IsPlaying = true; + _isPlaying = true; if (reset) ResetSimulation(); @@ -289,13 +294,13 @@ void ParticleEffect::Play(bool reset) void ParticleEffect::Pause() { _play = false; - IsPlaying = false; + _isPlaying = false; } void ParticleEffect::Stop() { _play = false; - IsPlaying = false; + _isPlaying = false; ResetSimulation(); } diff --git a/Source/Engine/Particles/ParticleEffect.h b/Source/Engine/Particles/ParticleEffect.h index 832327935..5a4bae771 100644 --- a/Source/Engine/Particles/ParticleEffect.h +++ b/Source/Engine/Particles/ParticleEffect.h @@ -185,6 +185,7 @@ private: Array _parameters; // Cached for scripting API Array _parametersOverrides; // Cached parameter modifications to be applied to the parameters bool _play = false; + bool _isPlaying = false; public: /// @@ -241,12 +242,6 @@ public: API_FIELD(Attributes="EditorDisplay(\"Particle Effect\"), DefaultValue(true), EditorOrder(60)") bool PlayOnStart = true; - /// - /// If true, the particle effect is playing. - /// - API_FIELD() - bool IsPlaying = false; - /// /// If true, the particle simulation will be updated even when an actor cannot be seen by any camera. Otherwise, the simulation will stop running when the actor is off-screen. /// @@ -339,6 +334,11 @@ public: /// API_PROPERTY() int32 GetParticlesCount() const; + /// + /// Gets whether or not the particle effect is playing. + /// + API_PROPERTY(Attributes="NoSerialize, HideInEditor") bool GetIsPlaying() const; + /// /// Resets the particles simulation state (clears the instance state data but preserves the instance parameters values). /// From f3b2011feff8c82742d2041f7a5055c9f330fc9f Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 4 Feb 2023 08:25:07 -0600 Subject: [PATCH 004/116] Small fix --- Source/Engine/Particles/ParticleEffect.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Particles/ParticleEffect.cpp b/Source/Engine/Particles/ParticleEffect.cpp index 04fd6eedb..651ff719b 100644 --- a/Source/Engine/Particles/ParticleEffect.cpp +++ b/Source/Engine/Particles/ParticleEffect.cpp @@ -453,7 +453,10 @@ void ParticleEffect::UpdateExecuteInEditor() // Always Play in editor while not playing. // Could be useful to have a GUI to change this state if (!_play) - _play = true; + { + _play = true; + _isPlaying = true; + } Update(); } From 558a7d99ffb281f413580c1347e297ce4df00304 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Mon, 27 Mar 2023 17:43:32 +0300 Subject: [PATCH 005/116] Add LateFixedUpdate event for scripts --- Source/Engine/Engine/Engine.cpp | 13 +++++++++++++ Source/Engine/Engine/Engine.h | 10 ++++++++++ Source/Engine/Engine/EngineService.cpp | 1 + Source/Engine/Engine/EngineService.h | 1 + Source/Engine/Scripting/Script.h | 7 +++++++ Source/Engine/Scripting/Scripting.cpp | 8 ++++++++ Source/Engine/Scripting/Scripting.cs | 16 +++++++++++++--- 7 files changed, 53 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Engine/Engine.cpp b/Source/Engine/Engine/Engine.cpp index 07038f146..58a435cbb 100644 --- a/Source/Engine/Engine/Engine.cpp +++ b/Source/Engine/Engine/Engine.cpp @@ -65,6 +65,7 @@ Action Engine::FixedUpdate; Action Engine::Update; TaskGraph* Engine::UpdateGraph = nullptr; Action Engine::LateUpdate; +Action Engine::LateFixedUpdate; Action Engine::Draw; Action Engine::Pause; Action Engine::Unpause; @@ -198,6 +199,7 @@ int32 Engine::Main(const Char* cmdLine) if (Time::OnBeginPhysics()) { OnFixedUpdate(); + OnLateFixedUpdate(); Time::OnEndPhysics(); } @@ -273,6 +275,17 @@ void Engine::OnFixedUpdate() } } +void Engine::OnLateFixedUpdate() +{ + PROFILE_CPU_NAMED("Late Fixed Update"); + + // Call event + LateFixedUpdate(); + + // Update services + EngineService::OnLateFixedUpdate(); +} + void Engine::OnUpdate() { PROFILE_CPU_NAMED("Update"); diff --git a/Source/Engine/Engine/Engine.h b/Source/Engine/Engine/Engine.h index 6f8c5fe6e..31d1f6ea6 100644 --- a/Source/Engine/Engine/Engine.h +++ b/Source/Engine/Engine/Engine.h @@ -54,6 +54,11 @@ public: /// static Action LateUpdate; + /// + /// Event called after engine update. + /// + static Action LateFixedUpdate; + /// /// Event called during frame rendering and can be used to invoke custom rendering with GPUDevice. /// @@ -107,6 +112,11 @@ public: /// static void OnLateUpdate(); + /// + /// Late fixed update callback. + /// + static void OnLateFixedUpdate(); + /// /// Draw callback. /// diff --git a/Source/Engine/Engine/EngineService.cpp b/Source/Engine/Engine/EngineService.cpp index ac58ba40c..204e926f1 100644 --- a/Source/Engine/Engine/EngineService.cpp +++ b/Source/Engine/Engine/EngineService.cpp @@ -33,6 +33,7 @@ static bool CompareEngineServices(EngineService* const& a, EngineService* const& DEFINE_ENGINE_SERVICE_EVENT(FixedUpdate); DEFINE_ENGINE_SERVICE_EVENT(Update); DEFINE_ENGINE_SERVICE_EVENT(LateUpdate); +DEFINE_ENGINE_SERVICE_EVENT(LateFixedUpdate); DEFINE_ENGINE_SERVICE_EVENT(Draw); DEFINE_ENGINE_SERVICE_EVENT_INVERTED(BeforeExit); diff --git a/Source/Engine/Engine/EngineService.h b/Source/Engine/Engine/EngineService.h index f4ae1b271..a142455d0 100644 --- a/Source/Engine/Engine/EngineService.h +++ b/Source/Engine/Engine/EngineService.h @@ -44,6 +44,7 @@ public: DECLARE_ENGINE_SERVICE_EVENT(void, FixedUpdate); DECLARE_ENGINE_SERVICE_EVENT(void, Update); DECLARE_ENGINE_SERVICE_EVENT(void, LateUpdate); + DECLARE_ENGINE_SERVICE_EVENT(void, LateFixedUpdate); DECLARE_ENGINE_SERVICE_EVENT(void, Draw); DECLARE_ENGINE_SERVICE_EVENT(void, BeforeExit); DECLARE_ENGINE_SERVICE_EVENT(void, Dispose); diff --git a/Source/Engine/Scripting/Script.h b/Source/Engine/Scripting/Script.h index 2304ea57c..769771f46 100644 --- a/Source/Engine/Scripting/Script.h +++ b/Source/Engine/Scripting/Script.h @@ -108,6 +108,13 @@ public: { } + /// + /// Called every fixed framerate frame (after FixedUpdate) if object is enabled. + /// + API_FUNCTION(Attributes = "NoAnimate") virtual void OnLateFixedUpdate() + { + } + /// /// Called during drawing debug shapes in editor. Use to draw debug shapes and other visualization. /// diff --git a/Source/Engine/Scripting/Scripting.cpp b/Source/Engine/Scripting/Scripting.cpp index 8796c7587..bec0e6c20 100644 --- a/Source/Engine/Scripting/Scripting.cpp +++ b/Source/Engine/Scripting/Scripting.cpp @@ -46,6 +46,7 @@ public: void Update() override; void LateUpdate() override; void FixedUpdate() override; + void LateFixedUpdate() override; void Draw() override; void BeforeExit() override; void Dispose() override; @@ -100,6 +101,7 @@ namespace MMethod* _method_Update = nullptr; MMethod* _method_LateUpdate = nullptr; MMethod* _method_FixedUpdate = nullptr; + MMethod* _method_LateFixedUpdate = nullptr; MMethod* _method_Draw = nullptr; MMethod* _method_Exit = nullptr; Array> _nonNativeModules; @@ -210,6 +212,12 @@ void ScriptingService::FixedUpdate() INVOKE_EVENT(FixedUpdate); } +void ScriptingService::LateFixedUpdate() +{ + PROFILE_CPU_NAMED("Scripting::LateFixedUpdate"); + INVOKE_EVENT(LateFixedUpdate); +} + void ScriptingService::Draw() { PROFILE_CPU_NAMED("Scripting::Draw"); diff --git a/Source/Engine/Scripting/Scripting.cs b/Source/Engine/Scripting/Scripting.cs index 204401867..7a91b2d96 100644 --- a/Source/Engine/Scripting/Scripting.cs +++ b/Source/Engine/Scripting/Scripting.cs @@ -80,17 +80,22 @@ namespace FlaxEngine public static event Action Update; /// - /// Occurs on scripting 'late' update. + /// Occurs on scripting late update. /// public static event Action LateUpdate; /// - /// Occurs on scripting `fixed` update. + /// Occurs on scripting fixed update. /// public static event Action FixedUpdate; /// - /// Occurs on scripting `draw` update. Called during frame rendering and can be used to invoke custom rendering with GPUDevice. + /// Occurs on scripting late fixed update. + /// + public static event Action LateFixedUpdate; + + /// + /// Occurs on scripting draw update. Called during frame rendering and can be used to invoke custom rendering with GPUDevice. /// public static event Action Draw; @@ -302,6 +307,11 @@ namespace FlaxEngine FixedUpdate?.Invoke(); } + internal static void Internal_LateFixedUpdate() + { + LateFixedUpdate?.Invoke(); + } + internal static void Internal_Draw() { Draw?.Invoke(); From 141555377b8198476880c6ec4f18e07e27c2941e Mon Sep 17 00:00:00 2001 From: Menotdan Date: Sun, 7 May 2023 22:58:57 -0400 Subject: [PATCH 006/116] Begin working on an IconButton class, and demoing the IconButton idea using the scale link icon. --- .../Editors/ActorTransformEditor.cs | 32 +++- Source/Engine/UI/GUI/Common/IconButton.cs | 167 ++++++++++++++++++ 2 files changed, 192 insertions(+), 7 deletions(-) create mode 100644 Source/Engine/UI/GUI/Common/IconButton.cs diff --git a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs index 2612440a4..a824076f7 100644 --- a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs @@ -78,7 +78,8 @@ namespace FlaxEditor.CustomEditors.Editors /// public class ScaleEditor : Float3Editor { - private Image _linkImage; + private Button _linkButton; + private SpriteBrush _linkBrush; /// public override void Initialize(LayoutElementsContainer layout) @@ -87,18 +88,26 @@ namespace FlaxEditor.CustomEditors.Editors LinkValues = Editor.Instance.Windows.PropertiesWin.ScaleLinked; - _linkImage = new Image + // Add button with the link icon. + _linkBrush = new SpriteBrush(Editor.Instance.Icons.Link32); + _linkButton = new Button { Parent = LinkedLabel, Width = 18, Height = 18, - Brush = LinkValues ? new SpriteBrush(Editor.Instance.Icons.Link32) : new SpriteBrush(), + BackgroundBrush = _linkBrush, AnchorPreset = AnchorPresets.TopLeft, - TooltipText = "Scale values are linked together.", }; + + _linkButton.Clicked += ToggleLink; + _linkButton.BorderColor = Color.Transparent; + _linkButton.BorderColorSelected = Color.Transparent; + _linkButton.BorderColorHighlighted = Color.Transparent; + SetLinkStyle(); + var x = LinkedLabel.Text.Value.Length * 7 + 5; - _linkImage.LocalX += x; - _linkImage.LocalY += 1; + _linkButton.LocalX += x; + _linkButton.LocalY += 1; LinkedLabel.SetupContextMenu += (label, menu, editor) => { @@ -127,7 +136,16 @@ namespace FlaxEditor.CustomEditors.Editors { LinkValues = !LinkValues; Editor.Instance.Windows.PropertiesWin.ScaleLinked = LinkValues; - _linkImage.Brush = LinkValues ? new SpriteBrush(Editor.Instance.Icons.Link32) : new SpriteBrush(); + SetLinkStyle(); + } + + private void SetLinkStyle() + { + Color backgroundColor = LinkValues ? FlaxEngine.GUI.Style.Current.BackgroundSelected : FlaxEngine.GUI.Style.Current.ForegroundDisabled; + _linkButton.BackgroundColor = backgroundColor; + _linkButton.BackgroundColorHighlighted = backgroundColor.RGBMultiplied(0.9f); + _linkButton.BackgroundColorSelected = backgroundColor.RGBMultiplied(0.8f); + _linkButton.TooltipText = (LinkValues ? "Unlink" : "Link") + " values for uniform scaling."; } } } diff --git a/Source/Engine/UI/GUI/Common/IconButton.cs b/Source/Engine/UI/GUI/Common/IconButton.cs new file mode 100644 index 000000000..196b4b9c4 --- /dev/null +++ b/Source/Engine/UI/GUI/Common/IconButton.cs @@ -0,0 +1,167 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +using System; + +namespace FlaxEngine.GUI +{ + /// + /// Button with an icon. + /// + public class IconButton : Button + { + /// + public override void ClearState() + { + base.ClearState(); + + if (_isPressed) + OnPressEnd(); + } + + /// + public override void DrawSelf() + { + // Cache data + Rectangle clientRect = new Rectangle(Float2.Zero, Size); + bool enabled = EnabledInHierarchy; + Color backgroundColor = BackgroundColor; + Color borderColor = BorderColor; + Color textColor = TextColor; + if (!enabled) + { + backgroundColor *= 0.5f; + borderColor *= 0.5f; + textColor *= 0.6f; + } + else if (_isPressed) + { + backgroundColor = BackgroundColorSelected; + borderColor = BorderColorSelected; + } + else if (IsMouseOver || IsNavFocused) + { + backgroundColor = BackgroundColorHighlighted; + borderColor = BorderColorHighlighted; + } + + // Draw background + if (BackgroundBrush != null) + BackgroundBrush.Draw(clientRect, backgroundColor); + else + Render2D.FillRectangle(clientRect, backgroundColor); + Render2D.DrawRectangle(clientRect, borderColor); + + // Draw text + Render2D.DrawText(_font?.GetFont(), TextMaterial, _text, clientRect, textColor, TextAlignment.Center, TextAlignment.Center); + } + + /// + public override void OnMouseEnter(Float2 location) + { + base.OnMouseEnter(location); + + HoverBegin?.Invoke(); + } + + /// + public override void OnMouseLeave() + { + if (_isPressed) + { + OnPressEnd(); + } + + HoverEnd?.Invoke(); + + base.OnMouseLeave(); + } + + /// + public override bool OnMouseDown(Float2 location, MouseButton button) + { + if (base.OnMouseDown(location, button)) + return true; + + if (button == MouseButton.Left && !_isPressed) + { + OnPressBegin(); + return true; + } + return false; + } + + /// + public override bool OnMouseUp(Float2 location, MouseButton button) + { + if (base.OnMouseUp(location, button)) + return true; + + if (button == MouseButton.Left && _isPressed) + { + OnPressEnd(); + OnClick(); + return true; + } + return false; + } + + /// + public override bool OnTouchDown(Float2 location, int pointerId) + { + if (base.OnTouchDown(location, pointerId)) + return true; + + if (!_isPressed) + { + OnPressBegin(); + return true; + } + return false; + } + + /// + public override bool OnTouchUp(Float2 location, int pointerId) + { + if (base.OnTouchUp(location, pointerId)) + return true; + + if (_isPressed) + { + OnPressEnd(); + OnClick(); + return true; + } + return false; + } + + /// + public override void OnTouchLeave() + { + if (_isPressed) + { + OnPressEnd(); + } + + base.OnTouchLeave(); + } + + /// + public override void OnLostFocus() + { + if (_isPressed) + { + OnPressEnd(); + } + + base.OnLostFocus(); + } + + /// + public override void OnSubmit() + { + OnClick(); + + base.OnSubmit(); + } + } +} From 296ac0b9404dbff349007aca8d6b348abe7930f1 Mon Sep 17 00:00:00 2001 From: Menotdan Date: Sun, 7 May 2023 23:04:11 -0400 Subject: [PATCH 007/116] Revert "Use shorter, relative path for displaying Asset Tooltips." This reverts commit 4c906f4040880b3a77686f27289800fb6c0cb55a. undo changes from master branch which I don't want for this seperate change. --- Source/Editor/Content/Items/AssetItem.cs | 4 +- Source/Editor/Content/Items/ContentFolder.cs | 9 +-- Source/Editor/Content/Items/ContentItem.cs | 14 +--- Source/Editor/Options/InterfaceOptions.cs | 7 -- Source/Editor/Utilities/Utils.cs | 76 +------------------- 5 files changed, 8 insertions(+), 102 deletions(-) diff --git a/Source/Editor/Content/Items/AssetItem.cs b/Source/Editor/Content/Items/AssetItem.cs index 120d0e416..6697ed27c 100644 --- a/Source/Editor/Content/Items/AssetItem.cs +++ b/Source/Editor/Content/Items/AssetItem.cs @@ -80,9 +80,9 @@ namespace FlaxEditor.Content /// The String Builder. protected virtual void OnBuildTooltipText(StringBuilder sb) { - sb.Append("Type: ").Append(Utilities.Utils.TranslateTypeName(TypeName)).AppendLine(); + sb.Append("Type: ").Append(TypeName).AppendLine(); sb.Append("Size: ").Append(Utilities.Utils.FormatBytesCount((int)new FileInfo(Path).Length)).AppendLine(); - sb.Append("Path: ").Append(Utilities.Utils.GetAssetNamePathWithExt(Path)).AppendLine(); + sb.Append("Path: ").Append(Path).AppendLine(); } /// diff --git a/Source/Editor/Content/Items/ContentFolder.cs b/Source/Editor/Content/Items/ContentFolder.cs index 974b813ee..cbbc15f24 100644 --- a/Source/Editor/Content/Items/ContentFolder.cs +++ b/Source/Editor/Content/Items/ContentFolder.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Text; using FlaxEditor.GUI.Drag; using FlaxEngine; using FlaxEngine.GUI; @@ -138,13 +137,7 @@ namespace FlaxEditor.Content /// public override void UpdateTooltipText() { - string fileDescription = "Folder"; - StringBuilder sb = new StringBuilder(); - - sb.Append("Type: ").Append(fileDescription).AppendLine(); - sb.Append("Path: ").Append(Utilities.Utils.GetAssetNamePathWithExt(Path)).AppendLine(); - - TooltipText = sb.ToString(); + TooltipText = Path; } /// diff --git a/Source/Editor/Content/Items/ContentItem.cs b/Source/Editor/Content/Items/ContentItem.cs index b4a4caa9a..284f3f1b3 100644 --- a/Source/Editor/Content/Items/ContentItem.cs +++ b/Source/Editor/Content/Items/ContentItem.cs @@ -2,8 +2,6 @@ using System; using System.Collections.Generic; -using System.IO; -using System.Text; using FlaxEditor.Content.GUI; using FlaxEditor.GUI.Drag; using FlaxEngine; @@ -355,19 +353,11 @@ namespace FlaxEditor.Content } /// - /// Updates the tooltip text. + /// Updates the tooltip text text. /// public virtual void UpdateTooltipText() { - string fileExtension = System.IO.Path.GetExtension(Path); - string fileDescription = Utilities.Utils.TranslateFileExtension(fileExtension); - StringBuilder sb = new StringBuilder(); - - sb.Append("Type: ").Append(fileDescription).AppendLine(); - sb.Append("Size: ").Append(Utilities.Utils.FormatBytesCount((int)new FileInfo(Path).Length)).AppendLine(); - sb.Append("Path: ").Append(Utilities.Utils.GetAssetNamePathWithExt(Path)).AppendLine(); - - TooltipText = sb.ToString(); + TooltipText = "Path: " + Path; } /// diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index d8e30d62b..acfb7f5e8 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -134,13 +134,6 @@ namespace FlaxEditor.Options [EditorDisplay("Interface"), EditorOrder(280), Tooltip("Editor content window orientation.")] public FlaxEngine.GUI.Orientation ContentWindowOrientation { get; set; } = FlaxEngine.GUI.Orientation.Horizontal; - /// - /// Gets or sets the option to use type name translations. - /// - [DefaultValue(true)] - [EditorDisplay("Interface"), EditorOrder(290), Tooltip("Attempt to translate asset type names.")] - public bool TranslateTypeNames { get; set; } = true; - /// /// Gets or sets the timestamps prefix mode for output log messages. /// diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index 6ef5a8ea0..ade084431 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -20,8 +20,6 @@ using FlaxEditor.SceneGraph; using FlaxEditor.Scripting; using FlaxEngine; using FlaxEngine.GUI; -using FlaxEditor.Options; -using System.Linq; namespace FlaxEngine { @@ -1019,76 +1017,6 @@ namespace FlaxEditor.Utilities node.Visible = isThisVisible | isAnyChildVisible; } - /// - /// Gets the asset type, translating if possible, and if enabled in InterfaceOptions.TranslateTypes. - /// - /// The type name. - /// The translated type name. - public static string TranslateTypeName(string typeName) - { - // TODO: Surely there is a better way to get this value. - if (!Editor.Instance.Options.Options.Interface.TranslateTypeNames) - { - return typeName; - } - - string[] typeNamespaces = typeName.Split('.'); - string lastNamespace = typeNamespaces.Last(); - - // TODO: Add better handling for unconventional type names. - try - { - // Adds spaces between capital letters. - return string.Concat(lastNamespace.Select(x => Char.IsUpper(x) ? " " + x : x.ToString())).TrimStart(' '); - } catch { - return typeName; - } - } - - /// - /// Gets a description of a file from it's extension. - /// - /// The file's extension - /// The processed description. - public static string TranslateFileExtension(string fileExtension) - { - string fileDescription = ""; - switch (fileExtension) - { - case ".cs": - fileDescription = "C# Source Code"; - break; - case ".cpp": - fileDescription = "C++ Source Code"; - break; - case ".h": - fileDescription = "C++ Header File"; - break; - case ".json": - fileDescription = "JSON File"; - break; - default: - fileDescription = fileExtension; - break; - } - - return fileDescription; - } - - - /// - /// Gets the asset name relative to the project root folder (with asset file extension) - /// - /// The asset path. - /// The processed name path. - public static string GetAssetNamePathWithExt(string path) - { - var projectFolder = Globals.ProjectFolder; - if (path.StartsWith(projectFolder)) - path = path.Substring(projectFolder.Length + 1); - return path; - } - /// /// Gets the asset name relative to the project root folder (without asset file extension) /// @@ -1096,7 +1024,9 @@ namespace FlaxEditor.Utilities /// The processed name path. public static string GetAssetNamePath(string path) { - path = GetAssetNamePathWithExt(path); + var projectFolder = Globals.ProjectFolder; + if (path.StartsWith(projectFolder)) + path = path.Substring(projectFolder.Length + 1); return StringUtils.GetPathWithoutExtension(path); } From f6ee5123d73cb221423f0d82b3df2a78b1a8a0ba Mon Sep 17 00:00:00 2001 From: Menotdan Date: Sun, 7 May 2023 23:10:07 -0400 Subject: [PATCH 008/116] Revert "Use shorter, relative path for displaying Asset Tooltips." This reverts commit 4c906f4040880b3a77686f27289800fb6c0cb55a. Revert changes I don't want anymore, after making a new branch for the changes I've done. --- Source/Editor/Content/Items/AssetItem.cs | 4 +- Source/Editor/Content/Items/ContentFolder.cs | 9 +-- Source/Editor/Content/Items/ContentItem.cs | 14 +--- Source/Editor/Options/InterfaceOptions.cs | 7 -- Source/Editor/Utilities/Utils.cs | 76 +------------------- 5 files changed, 8 insertions(+), 102 deletions(-) diff --git a/Source/Editor/Content/Items/AssetItem.cs b/Source/Editor/Content/Items/AssetItem.cs index 120d0e416..6697ed27c 100644 --- a/Source/Editor/Content/Items/AssetItem.cs +++ b/Source/Editor/Content/Items/AssetItem.cs @@ -80,9 +80,9 @@ namespace FlaxEditor.Content /// The String Builder. protected virtual void OnBuildTooltipText(StringBuilder sb) { - sb.Append("Type: ").Append(Utilities.Utils.TranslateTypeName(TypeName)).AppendLine(); + sb.Append("Type: ").Append(TypeName).AppendLine(); sb.Append("Size: ").Append(Utilities.Utils.FormatBytesCount((int)new FileInfo(Path).Length)).AppendLine(); - sb.Append("Path: ").Append(Utilities.Utils.GetAssetNamePathWithExt(Path)).AppendLine(); + sb.Append("Path: ").Append(Path).AppendLine(); } /// diff --git a/Source/Editor/Content/Items/ContentFolder.cs b/Source/Editor/Content/Items/ContentFolder.cs index 974b813ee..cbbc15f24 100644 --- a/Source/Editor/Content/Items/ContentFolder.cs +++ b/Source/Editor/Content/Items/ContentFolder.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Text; using FlaxEditor.GUI.Drag; using FlaxEngine; using FlaxEngine.GUI; @@ -138,13 +137,7 @@ namespace FlaxEditor.Content /// public override void UpdateTooltipText() { - string fileDescription = "Folder"; - StringBuilder sb = new StringBuilder(); - - sb.Append("Type: ").Append(fileDescription).AppendLine(); - sb.Append("Path: ").Append(Utilities.Utils.GetAssetNamePathWithExt(Path)).AppendLine(); - - TooltipText = sb.ToString(); + TooltipText = Path; } /// diff --git a/Source/Editor/Content/Items/ContentItem.cs b/Source/Editor/Content/Items/ContentItem.cs index b4a4caa9a..284f3f1b3 100644 --- a/Source/Editor/Content/Items/ContentItem.cs +++ b/Source/Editor/Content/Items/ContentItem.cs @@ -2,8 +2,6 @@ using System; using System.Collections.Generic; -using System.IO; -using System.Text; using FlaxEditor.Content.GUI; using FlaxEditor.GUI.Drag; using FlaxEngine; @@ -355,19 +353,11 @@ namespace FlaxEditor.Content } /// - /// Updates the tooltip text. + /// Updates the tooltip text text. /// public virtual void UpdateTooltipText() { - string fileExtension = System.IO.Path.GetExtension(Path); - string fileDescription = Utilities.Utils.TranslateFileExtension(fileExtension); - StringBuilder sb = new StringBuilder(); - - sb.Append("Type: ").Append(fileDescription).AppendLine(); - sb.Append("Size: ").Append(Utilities.Utils.FormatBytesCount((int)new FileInfo(Path).Length)).AppendLine(); - sb.Append("Path: ").Append(Utilities.Utils.GetAssetNamePathWithExt(Path)).AppendLine(); - - TooltipText = sb.ToString(); + TooltipText = "Path: " + Path; } /// diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index d8e30d62b..acfb7f5e8 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -134,13 +134,6 @@ namespace FlaxEditor.Options [EditorDisplay("Interface"), EditorOrder(280), Tooltip("Editor content window orientation.")] public FlaxEngine.GUI.Orientation ContentWindowOrientation { get; set; } = FlaxEngine.GUI.Orientation.Horizontal; - /// - /// Gets or sets the option to use type name translations. - /// - [DefaultValue(true)] - [EditorDisplay("Interface"), EditorOrder(290), Tooltip("Attempt to translate asset type names.")] - public bool TranslateTypeNames { get; set; } = true; - /// /// Gets or sets the timestamps prefix mode for output log messages. /// diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index 6ef5a8ea0..ade084431 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -20,8 +20,6 @@ using FlaxEditor.SceneGraph; using FlaxEditor.Scripting; using FlaxEngine; using FlaxEngine.GUI; -using FlaxEditor.Options; -using System.Linq; namespace FlaxEngine { @@ -1019,76 +1017,6 @@ namespace FlaxEditor.Utilities node.Visible = isThisVisible | isAnyChildVisible; } - /// - /// Gets the asset type, translating if possible, and if enabled in InterfaceOptions.TranslateTypes. - /// - /// The type name. - /// The translated type name. - public static string TranslateTypeName(string typeName) - { - // TODO: Surely there is a better way to get this value. - if (!Editor.Instance.Options.Options.Interface.TranslateTypeNames) - { - return typeName; - } - - string[] typeNamespaces = typeName.Split('.'); - string lastNamespace = typeNamespaces.Last(); - - // TODO: Add better handling for unconventional type names. - try - { - // Adds spaces between capital letters. - return string.Concat(lastNamespace.Select(x => Char.IsUpper(x) ? " " + x : x.ToString())).TrimStart(' '); - } catch { - return typeName; - } - } - - /// - /// Gets a description of a file from it's extension. - /// - /// The file's extension - /// The processed description. - public static string TranslateFileExtension(string fileExtension) - { - string fileDescription = ""; - switch (fileExtension) - { - case ".cs": - fileDescription = "C# Source Code"; - break; - case ".cpp": - fileDescription = "C++ Source Code"; - break; - case ".h": - fileDescription = "C++ Header File"; - break; - case ".json": - fileDescription = "JSON File"; - break; - default: - fileDescription = fileExtension; - break; - } - - return fileDescription; - } - - - /// - /// Gets the asset name relative to the project root folder (with asset file extension) - /// - /// The asset path. - /// The processed name path. - public static string GetAssetNamePathWithExt(string path) - { - var projectFolder = Globals.ProjectFolder; - if (path.StartsWith(projectFolder)) - path = path.Substring(projectFolder.Length + 1); - return path; - } - /// /// Gets the asset name relative to the project root folder (without asset file extension) /// @@ -1096,7 +1024,9 @@ namespace FlaxEditor.Utilities /// The processed name path. public static string GetAssetNamePath(string path) { - path = GetAssetNamePathWithExt(path); + var projectFolder = Globals.ProjectFolder; + if (path.StartsWith(projectFolder)) + path = path.Substring(projectFolder.Length + 1); return StringUtils.GetPathWithoutExtension(path); } From 681564189fe720005470cc54d06af60ae149a499 Mon Sep 17 00:00:00 2001 From: Menotdan Date: Mon, 8 May 2023 00:42:38 -0400 Subject: [PATCH 009/116] Finish implementing IconButton and use that to create a more intuitive scale linking interface. --- .../Editors/ActorTransformEditor.cs | 15 +- Source/Engine/UI/GUI/Common/Button.cs | 4 +- Source/Engine/UI/GUI/Common/IconButton.cs | 191 +++++------------- 3 files changed, 55 insertions(+), 155 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs index a824076f7..fb7a0b29d 100644 --- a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs @@ -79,7 +79,6 @@ namespace FlaxEditor.CustomEditors.Editors public class ScaleEditor : Float3Editor { private Button _linkButton; - private SpriteBrush _linkBrush; /// public override void Initialize(LayoutElementsContainer layout) @@ -89,20 +88,16 @@ namespace FlaxEditor.CustomEditors.Editors LinkValues = Editor.Instance.Windows.PropertiesWin.ScaleLinked; // Add button with the link icon. - _linkBrush = new SpriteBrush(Editor.Instance.Icons.Link32); - _linkButton = new Button + //Editor.Instance.Icons.Link32 + _linkButton = new IconButton(Editor.Instance.Icons.Link32) { Parent = LinkedLabel, Width = 18, Height = 18, - BackgroundBrush = _linkBrush, - AnchorPreset = AnchorPresets.TopLeft, + AnchorPreset = AnchorPresets.TopLeft }; _linkButton.Clicked += ToggleLink; - _linkButton.BorderColor = Color.Transparent; - _linkButton.BorderColorSelected = Color.Transparent; - _linkButton.BorderColorHighlighted = Color.Transparent; SetLinkStyle(); var x = LinkedLabel.Text.Value.Length * 7 + 5; @@ -142,9 +137,7 @@ namespace FlaxEditor.CustomEditors.Editors private void SetLinkStyle() { Color backgroundColor = LinkValues ? FlaxEngine.GUI.Style.Current.BackgroundSelected : FlaxEngine.GUI.Style.Current.ForegroundDisabled; - _linkButton.BackgroundColor = backgroundColor; - _linkButton.BackgroundColorHighlighted = backgroundColor.RGBMultiplied(0.9f); - _linkButton.BackgroundColorSelected = backgroundColor.RGBMultiplied(0.8f); + _linkButton.SetColors(backgroundColor); _linkButton.TooltipText = (LinkValues ? "Unlink" : "Link") + " values for uniform scaling."; } } diff --git a/Source/Engine/UI/GUI/Common/Button.cs b/Source/Engine/UI/GUI/Common/Button.cs index 0e0d29615..b6ee13e41 100644 --- a/Source/Engine/UI/GUI/Common/Button.cs +++ b/Source/Engine/UI/GUI/Common/Button.cs @@ -10,7 +10,7 @@ namespace FlaxEngine.GUI public class Button : ContainerControl { /// - /// The default height fro the buttons. + /// The default height for the buttons. /// public const float DefaultHeight = 24.0f; @@ -195,7 +195,7 @@ namespace FlaxEngine.GUI /// Sets the button colors palette based on a given main color. /// /// The main color. - public void SetColors(Color color) + public virtual void SetColors(Color color) { BackgroundColor = color; BorderColor = color.RGBMultiplied(0.5f); diff --git a/Source/Engine/UI/GUI/Common/IconButton.cs b/Source/Engine/UI/GUI/Common/IconButton.cs index 196b4b9c4..e2652cd9f 100644 --- a/Source/Engine/UI/GUI/Common/IconButton.cs +++ b/Source/Engine/UI/GUI/Common/IconButton.cs @@ -9,159 +9,66 @@ namespace FlaxEngine.GUI /// public class IconButton : Button { - /// - public override void ClearState() + /// + /// The sprite rendered on the button. + /// + public SpriteHandle ButtonSprite { get; set; } + + /// + /// Whether or not to hide the border of the button. + /// + public bool HideBorder = true; + + /// + /// Initializes a new instance of the class. + /// + /// The sprite used by the button. + public IconButton(SpriteHandle buttonSprite) + : this(0, 0, buttonSprite) { - base.ClearState(); - - if (_isPressed) - OnPressEnd(); } - /// - public override void DrawSelf() + /// + /// Initializes a new instance of the class. + /// + /// Position X coordinate + /// Position Y coordinate + /// The sprite used by the button. + /// Width + /// Height + /// Whether or not to hide the border. + public IconButton(float x, float y, SpriteHandle buttonSprite, float width = 120, float height = DefaultHeight, bool hideBorder = true) + : base(x, y, width, height) { - // Cache data - Rectangle clientRect = new Rectangle(Float2.Zero, Size); - bool enabled = EnabledInHierarchy; - Color backgroundColor = BackgroundColor; - Color borderColor = BorderColor; - Color textColor = TextColor; - if (!enabled) - { - backgroundColor *= 0.5f; - borderColor *= 0.5f; - textColor *= 0.6f; - } - else if (_isPressed) - { - backgroundColor = BackgroundColorSelected; - borderColor = BorderColorSelected; - } - else if (IsMouseOver || IsNavFocused) - { - backgroundColor = BackgroundColorHighlighted; - borderColor = BorderColorHighlighted; - } - - // Draw background - if (BackgroundBrush != null) - BackgroundBrush.Draw(clientRect, backgroundColor); - else - Render2D.FillRectangle(clientRect, backgroundColor); - Render2D.DrawRectangle(clientRect, borderColor); - - // Draw text - Render2D.DrawText(_font?.GetFont(), TextMaterial, _text, clientRect, textColor, TextAlignment.Center, TextAlignment.Center); + ButtonSprite = buttonSprite; + BackgroundBrush = new SpriteBrush(ButtonSprite); + HideBorder = hideBorder; } - /// - public override void OnMouseEnter(Float2 location) + /// + /// Initializes a new instance of the class. + /// + /// Position + /// Size + /// The sprite used by the button. + public IconButton(Float2 location, Float2 size, SpriteHandle buttonSprite) + : this(location.X, location.Y, buttonSprite, size.X, size.Y) { - base.OnMouseEnter(location); - - HoverBegin?.Invoke(); } - /// - public override void OnMouseLeave() + /// + /// Sets the colors of the button, taking into account the field.> + /// + /// The color to use. + public override void SetColors(Color color) { - if (_isPressed) - { - OnPressEnd(); - } + BackgroundColor = color; + BackgroundColorSelected = color.RGBMultiplied(0.8f); + BackgroundColorHighlighted = color.RGBMultiplied(1.2f); - HoverEnd?.Invoke(); - - base.OnMouseLeave(); - } - - /// - public override bool OnMouseDown(Float2 location, MouseButton button) - { - if (base.OnMouseDown(location, button)) - return true; - - if (button == MouseButton.Left && !_isPressed) - { - OnPressBegin(); - return true; - } - return false; - } - - /// - public override bool OnMouseUp(Float2 location, MouseButton button) - { - if (base.OnMouseUp(location, button)) - return true; - - if (button == MouseButton.Left && _isPressed) - { - OnPressEnd(); - OnClick(); - return true; - } - return false; - } - - /// - public override bool OnTouchDown(Float2 location, int pointerId) - { - if (base.OnTouchDown(location, pointerId)) - return true; - - if (!_isPressed) - { - OnPressBegin(); - return true; - } - return false; - } - - /// - public override bool OnTouchUp(Float2 location, int pointerId) - { - if (base.OnTouchUp(location, pointerId)) - return true; - - if (_isPressed) - { - OnPressEnd(); - OnClick(); - return true; - } - return false; - } - - /// - public override void OnTouchLeave() - { - if (_isPressed) - { - OnPressEnd(); - } - - base.OnTouchLeave(); - } - - /// - public override void OnLostFocus() - { - if (_isPressed) - { - OnPressEnd(); - } - - base.OnLostFocus(); - } - - /// - public override void OnSubmit() - { - OnClick(); - - base.OnSubmit(); + BorderColor = HideBorder ? Color.Transparent : color.RGBMultiplied(0.5f); + BorderColorSelected = BorderColor; + BorderColorHighlighted = BorderColor; } } } From 74af2e53a8b0a16f4e1a8012ca52b62cbaf10ca9 Mon Sep 17 00:00:00 2001 From: Menotdan Date: Mon, 8 May 2023 12:50:33 -0400 Subject: [PATCH 010/116] Add IconButton for this branch. --- Source/Engine/UI/GUI/Common/IconButton.cs | 74 +++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 Source/Engine/UI/GUI/Common/IconButton.cs diff --git a/Source/Engine/UI/GUI/Common/IconButton.cs b/Source/Engine/UI/GUI/Common/IconButton.cs new file mode 100644 index 000000000..e2652cd9f --- /dev/null +++ b/Source/Engine/UI/GUI/Common/IconButton.cs @@ -0,0 +1,74 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +using System; + +namespace FlaxEngine.GUI +{ + /// + /// Button with an icon. + /// + public class IconButton : Button + { + /// + /// The sprite rendered on the button. + /// + public SpriteHandle ButtonSprite { get; set; } + + /// + /// Whether or not to hide the border of the button. + /// + public bool HideBorder = true; + + /// + /// Initializes a new instance of the class. + /// + /// The sprite used by the button. + public IconButton(SpriteHandle buttonSprite) + : this(0, 0, buttonSprite) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Position X coordinate + /// Position Y coordinate + /// The sprite used by the button. + /// Width + /// Height + /// Whether or not to hide the border. + public IconButton(float x, float y, SpriteHandle buttonSprite, float width = 120, float height = DefaultHeight, bool hideBorder = true) + : base(x, y, width, height) + { + ButtonSprite = buttonSprite; + BackgroundBrush = new SpriteBrush(ButtonSprite); + HideBorder = hideBorder; + } + + /// + /// Initializes a new instance of the class. + /// + /// Position + /// Size + /// The sprite used by the button. + public IconButton(Float2 location, Float2 size, SpriteHandle buttonSprite) + : this(location.X, location.Y, buttonSprite, size.X, size.Y) + { + } + + /// + /// Sets the colors of the button, taking into account the field.> + /// + /// The color to use. + public override void SetColors(Color color) + { + BackgroundColor = color; + BackgroundColorSelected = color.RGBMultiplied(0.8f); + BackgroundColorHighlighted = color.RGBMultiplied(1.2f); + + BorderColor = HideBorder ? Color.Transparent : color.RGBMultiplied(0.5f); + BorderColorSelected = BorderColor; + BorderColorHighlighted = BorderColor; + } + } +} From 86d0ccb2103a140047702ff0c345062b7bde82bf Mon Sep 17 00:00:00 2001 From: Menotdan Date: Mon, 8 May 2023 12:51:34 -0400 Subject: [PATCH 011/116] Mark SetColors() virtual to override it in IconButton --- Source/Engine/UI/GUI/Common/Button.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/UI/GUI/Common/Button.cs b/Source/Engine/UI/GUI/Common/Button.cs index 0e0d29615..532523be7 100644 --- a/Source/Engine/UI/GUI/Common/Button.cs +++ b/Source/Engine/UI/GUI/Common/Button.cs @@ -195,7 +195,7 @@ namespace FlaxEngine.GUI /// Sets the button colors palette based on a given main color. /// /// The main color. - public void SetColors(Color color) + public virtual void SetColors(Color color) { BackgroundColor = color; BorderColor = color.RGBMultiplied(0.5f); From ec60c90963066254074c7c9a4eebe3298f38080c Mon Sep 17 00:00:00 2001 From: Menotdan Date: Mon, 8 May 2023 15:59:19 -0400 Subject: [PATCH 012/116] Implement basic design of the eyedropper button. --- .../Editor/GUI/Dialogs/ColorPickerDialog.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs index 8209b63cf..daf5cfa05 100644 --- a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs +++ b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs @@ -3,6 +3,7 @@ using FlaxEditor.GUI.Input; using FlaxEngine; using FlaxEngine.GUI; +using System; namespace FlaxEditor.GUI.Dialogs { @@ -25,6 +26,7 @@ namespace FlaxEditor.GUI.Dialogs { private const float ButtonsWidth = 60.0f; private const float PickerMargin = 6.0f; + private const float EyedropperMargin = 8.0f; private const float RGBAMargin = 12.0f; private const float HSVMargin = 0.0f; private const float ChannelsMargin = 4.0f; @@ -48,6 +50,7 @@ namespace FlaxEditor.GUI.Dialogs private TextBox _cHex; private Button _cCancel; private Button _cOK; + private IconButton _cEyedropper; /// /// Gets the selected color. @@ -192,10 +195,26 @@ namespace FlaxEditor.GUI.Dialogs }; _cOK.Clicked += OnSubmit; + // Eyedropper button + _cEyedropper = new IconButton(_cOK.X - EyedropperMargin, _cHex.Bottom + PickerMargin, Editor.Instance.Icons.Add64, hideBorder: false) + { + Parent = this, + }; + _cEyedropper.Clicked += OnEyedropColor; + _cEyedropper.Height = (_cValue.Bottom - _cEyedropper.Y) * 0.5f; + _cEyedropper.Width = _cEyedropper.Height; + _cEyedropper.X -= _cEyedropper.Width; + //_cEyedropper.SetColors(_cEyedropper.BackgroundColor); + // Set initial color SelectedColor = initialValue; } + private void OnEyedropColor() + { + throw new NotImplementedException(); + } + private void OnRGBAChanged() { if (_disableEvents) From c73ab05d70afd299a11f12cd52a5e4883eb780a6 Mon Sep 17 00:00:00 2001 From: Menotdan Date: Tue, 9 May 2023 16:14:37 -0400 Subject: [PATCH 013/116] Rather odd attempt to screenshot the view --- .../Editor/GUI/Dialogs/ColorPickerDialog.cs | 2 +- Source/Engine/Utilities/Screenshot.cpp | 76 +++++++++++++++++++ Source/Engine/Utilities/Screenshot.h | 8 ++ 3 files changed, 85 insertions(+), 1 deletion(-) diff --git a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs index daf5cfa05..ab3a3fa17 100644 --- a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs +++ b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs @@ -212,7 +212,7 @@ namespace FlaxEditor.GUI.Dialogs private void OnEyedropColor() { - throw new NotImplementedException(); + Editor.Log("Color: " + Screenshot.GetPixelAt(7, 6).ToString()); } private void OnRGBAChanged() diff --git a/Source/Engine/Utilities/Screenshot.cpp b/Source/Engine/Utilities/Screenshot.cpp index d67f8d123..553801e5b 100644 --- a/Source/Engine/Utilities/Screenshot.cpp +++ b/Source/Engine/Utilities/Screenshot.cpp @@ -3,6 +3,7 @@ #include "Screenshot.h" #include "Engine/Core/Log.h" #include "Engine/Core/Math/Math.h" +#include "Engine/Core/Math/Color32.h" #include "Engine/Graphics/RenderTask.h" #include "Engine/Platform/FileSystem.h" #include "Engine/Graphics/Textures/TextureData.h" @@ -129,6 +130,66 @@ void CaptureScreenshot::OnFail() ThreadPoolTask::OnFail(); } + + +/// +/// Capture screenshot helper +/// +/// +class GetPixelData : public ThreadPoolTask +{ + friend Screenshot; +private: + TextureData _data; + Color32 _color; + +public: + int32 x; + int32 y; + +public: + /// + /// Gets the texture data container. + /// + /// Texture data + FORCE_INLINE TextureData& GetData() + { + return _data; + } + + FORCE_INLINE Color32 GetColor() + { + return _color; + } + +protected: + // [ThreadPoolTask] + bool Run() override; + void OnFail() override; +}; + +bool GetPixelData::Run() +{ + LOG(Warning, "REAL"); + TextureMipData *mipData = _data.GetData(0, 0); + Array pixels; + mipData->GetPixels(pixels, _data.Width, _data.Height, _data.Format); + + _color = pixels[(y * _data.Width) + x]; + LOG(Warning, "really real"); + LOG(Warning, "Color: {0} {1} {2}", _color.R, _color.B, _color.G); + return false; +} + +void GetPixelData::OnFail() +{ + LOG(Warning, "Cannot get pixel data."); + + // Base + ThreadPoolTask::OnFail(); +} + + void Screenshot::Capture(GPUTexture* target, const StringView& path) { // Validate @@ -242,3 +303,18 @@ void Screenshot::Capture(const StringView& path) Capture(mainTask, path); } + +Color32 Screenshot::GetPixelAt(int32 x, int32 y) { + GPUSwapChain* swapChain = Engine::MainWindow->GetSwapChain(); + + auto getPixelTask = New(); + getPixelTask->x = x; + getPixelTask->y = y; + + Task* downloadTask = swapChain->DownloadDataAsync(getPixelTask->GetData()); + downloadTask->ContinueWith(getPixelTask); + downloadTask->Start(); + + //downloadTask->Wait(750); + return getPixelTask->GetColor(); +} diff --git a/Source/Engine/Utilities/Screenshot.h b/Source/Engine/Utilities/Screenshot.h index c51c34199..e2b95e748 100644 --- a/Source/Engine/Utilities/Screenshot.h +++ b/Source/Engine/Utilities/Screenshot.h @@ -39,4 +39,12 @@ API_CLASS(Static) class FLAXENGINE_API Screenshot /// /// The custom file location. Use null or empty to use default one. API_FUNCTION() static void Capture(const StringView& path = StringView::Empty); + + /// + /// Get the pixel at specified coordinates. + /// + /// The x coordinate to read. + /// The y coordinate to read. + /// The color + API_FUNCTION() static Color32 GetPixelAt(int32 x, int32 y); }; From 1ebf4d49e512b2075899cf5c8496c3849076a26a Mon Sep 17 00:00:00 2001 From: Menotdan Date: Tue, 9 May 2023 16:49:03 -0400 Subject: [PATCH 014/116] trying to get the correct window --- Source/Editor/GUI/Dialogs/ColorPickerDialog.cs | 11 ++++++++++- Source/Engine/Utilities/Screenshot.cpp | 9 +++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs index ab3a3fa17..e30e9438d 100644 --- a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs +++ b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using FlaxEditor.GUI.Input; +using FlaxEditor.Windows; using FlaxEngine; using FlaxEngine.GUI; using System; @@ -212,7 +213,15 @@ namespace FlaxEditor.GUI.Dialogs private void OnEyedropColor() { - Editor.Log("Color: " + Screenshot.GetPixelAt(7, 6).ToString()); + Float2 mousePosition = new Float2(0, 0); + foreach (EditorWindow window in Editor.Instance.Windows.Windows) + { + if (window.IsMouseOver) + { + mousePosition = window.RootWindow.MousePosition; + } + } + Editor.Log("Color: " + Screenshot.GetPixelAt(Mathf.FloorToInt(mousePosition.X), Mathf.FloorToInt(mousePosition.Y)).ToString()); } private void OnRGBAChanged() diff --git a/Source/Engine/Utilities/Screenshot.cpp b/Source/Engine/Utilities/Screenshot.cpp index 553801e5b..c946ef695 100644 --- a/Source/Engine/Utilities/Screenshot.cpp +++ b/Source/Engine/Utilities/Screenshot.cpp @@ -174,10 +174,11 @@ bool GetPixelData::Run() TextureMipData *mipData = _data.GetData(0, 0); Array pixels; mipData->GetPixels(pixels, _data.Width, _data.Height, _data.Format); - - _color = pixels[(y * _data.Width) + x]; - LOG(Warning, "really real"); - LOG(Warning, "Color: {0} {1} {2}", _color.R, _color.B, _color.G); + + LOG(Warning, "{0}, {1} ({2} at {3})", x, y, pixels.Count(), (y * _data.Width) + x); + //_color = pixels[(y * _data.Width) + x]; + //LOG(Warning, "really real"); + //LOG(Warning, "Color: {0} {1} {2}", _color.R, _color.B, _color.G); return false; } From 59c54db27595ab04acf2312cdf70d3e3c85dab81 Mon Sep 17 00:00:00 2001 From: Menotdan Date: Wed, 10 May 2023 00:58:38 -0400 Subject: [PATCH 015/116] Implement C# callback for pixels finished reading --- .../Editor/GUI/Dialogs/ColorPickerDialog.cs | 20 ++++++++++--------- Source/Engine/Utilities/Screenshot.cpp | 13 +++++++----- Source/Engine/Utilities/Screenshot.h | 2 ++ 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs index e30e9438d..53eabebb4 100644 --- a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs +++ b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs @@ -112,6 +112,9 @@ namespace FlaxEditor.GUI.Dialogs _onChanged = colorChanged; _onClosed = pickerClosed; + // Register the event for eyedropper pixels being read. + Screenshot.PixelReadDelegate += PixelDataRead; + // Selector _cSelector = new ColorSelectorWithSliders(180, 18) { @@ -211,17 +214,16 @@ namespace FlaxEditor.GUI.Dialogs SelectedColor = initialValue; } + private void PixelDataRead(Color32 pixel_color) + { + Color color = pixel_color; + Editor.Log(string.Format("Color: {0} {1} {2}", color.R, color.G, color.B)); + } + private void OnEyedropColor() { - Float2 mousePosition = new Float2(0, 0); - foreach (EditorWindow window in Editor.Instance.Windows.Windows) - { - if (window.IsMouseOver) - { - mousePosition = window.RootWindow.MousePosition; - } - } - Editor.Log("Color: " + Screenshot.GetPixelAt(Mathf.FloorToInt(mousePosition.X), Mathf.FloorToInt(mousePosition.Y)).ToString()); + Float2 mousePosition = FlaxEngine.Input.MouseScreenPosition; + Screenshot.GetPixelAt(Mathf.FloorToInt(mousePosition.X), Mathf.FloorToInt(mousePosition.Y)); } private void OnRGBAChanged() diff --git a/Source/Engine/Utilities/Screenshot.cpp b/Source/Engine/Utilities/Screenshot.cpp index c946ef695..0635180a8 100644 --- a/Source/Engine/Utilities/Screenshot.cpp +++ b/Source/Engine/Utilities/Screenshot.cpp @@ -130,7 +130,7 @@ void CaptureScreenshot::OnFail() ThreadPoolTask::OnFail(); } - +Delegate Screenshot::PixelReadDelegate; /// /// Capture screenshot helper @@ -176,9 +176,12 @@ bool GetPixelData::Run() mipData->GetPixels(pixels, _data.Width, _data.Height, _data.Format); LOG(Warning, "{0}, {1} ({2} at {3})", x, y, pixels.Count(), (y * _data.Width) + x); - //_color = pixels[(y * _data.Width) + x]; - //LOG(Warning, "really real"); - //LOG(Warning, "Color: {0} {1} {2}", _color.R, _color.B, _color.G); + _color = pixels[(y * _data.Width) + x]; + LOG(Warning, "really real"); + LOG(Warning, "Color: R: {0}, G: {1}, B: {2}", _color.R, _color.G, _color.B); + + LOG(Warning, "Bound functions: {0}", Screenshot::PixelReadDelegate.Count()); + Screenshot::PixelReadDelegate(_color); return false; } @@ -314,8 +317,8 @@ Color32 Screenshot::GetPixelAt(int32 x, int32 y) { Task* downloadTask = swapChain->DownloadDataAsync(getPixelTask->GetData()); downloadTask->ContinueWith(getPixelTask); + LOG(Warning, "Started download task. real"); downloadTask->Start(); - //downloadTask->Wait(750); return getPixelTask->GetColor(); } diff --git a/Source/Engine/Utilities/Screenshot.h b/Source/Engine/Utilities/Screenshot.h index e2b95e748..4fe497355 100644 --- a/Source/Engine/Utilities/Screenshot.h +++ b/Source/Engine/Utilities/Screenshot.h @@ -47,4 +47,6 @@ API_CLASS(Static) class FLAXENGINE_API Screenshot /// The y coordinate to read. /// The color API_FUNCTION() static Color32 GetPixelAt(int32 x, int32 y); + + API_EVENT() static Delegate PixelReadDelegate; }; From 2b59cbf0f14387a76652a528313a1d9e9e56f25f Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Wed, 10 May 2023 07:43:40 -0500 Subject: [PATCH 016/116] Organize the style groups of UI controls --- Source/Engine/UI/GUI/Common/Button.cs | 78 +++++++++++----------- Source/Engine/UI/GUI/Common/CheckBox.cs | 20 +++--- Source/Engine/UI/GUI/Common/Dropdown.cs | 26 ++++---- Source/Engine/UI/GUI/Common/Image.cs | 6 +- Source/Engine/UI/GUI/Common/Label.cs | 14 ++-- Source/Engine/UI/GUI/Common/ProgressBar.cs | 6 +- Source/Engine/UI/GUI/Common/TextBox.cs | 12 ++-- Source/Engine/UI/GUI/Common/TextBoxBase.cs | 14 ++-- Source/Engine/UI/GUI/Control.cs | 2 +- Source/Engine/UI/GUI/Panels/DropPanel.cs | 70 +++++++++---------- 10 files changed, 125 insertions(+), 123 deletions(-) diff --git a/Source/Engine/UI/GUI/Common/Button.cs b/Source/Engine/UI/GUI/Common/Button.cs index 0e0d29615..41ea3a0d4 100644 --- a/Source/Engine/UI/GUI/Common/Button.cs +++ b/Source/Engine/UI/GUI/Common/Button.cs @@ -42,7 +42,7 @@ namespace FlaxEngine.GUI /// /// Gets or sets the font used to draw button text. /// - [EditorDisplay("Style"), EditorOrder(2000)] + [EditorDisplay("Text Style"), EditorOrder(2022), ExpandGroups] public FontReference Font { get => _font; @@ -52,14 +52,50 @@ namespace FlaxEngine.GUI /// /// Gets or sets the custom material used to render the text. It must has domain set to GUI and have a public texture parameter named Font used to sample font atlas texture with font characters data. /// - [EditorDisplay("Style"), EditorOrder(2000), Tooltip("Custom material used to render the text. It must has domain set to GUI and have a public texture parameter named Font used to sample font atlas texture with font characters data.")] + [EditorDisplay("Text Style"), EditorOrder(2021), Tooltip("Custom material used to render the text. It must has domain set to GUI and have a public texture parameter named Font used to sample font atlas texture with font characters data.")] public MaterialBase TextMaterial { get; set; } /// /// Gets or sets the color used to draw button text. /// - [EditorDisplay("Style"), EditorOrder(2000)] + [EditorDisplay("Text Style"), EditorOrder(2020)] public Color TextColor; + + /// + /// Gets or sets the brush used for background drawing. + /// + [EditorDisplay("Background Style"), EditorOrder(1999), Tooltip("The brush used for background drawing."), ExpandGroups] + public IBrush BackgroundBrush { get; set; } + + /// + /// Gets or sets the background color when button is highlighted. + /// + [EditorDisplay("Background Style"), EditorOrder(2001)] + public Color BackgroundColorHighlighted { get; set; } + + /// + /// Gets or sets the background color when button is selected. + /// + [EditorDisplay("Background Style"), EditorOrder(2002)] + public Color BackgroundColorSelected { get; set; } + + /// + /// Gets or sets the color of the border. + /// + [EditorDisplay("Border Style"), EditorOrder(2010), ExpandGroups] + public Color BorderColor { get; set; } + + /// + /// Gets or sets the border color when button is highlighted. + /// + [EditorDisplay("Border Style"), EditorOrder(2011)] + public Color BorderColorHighlighted { get; set; } + + /// + /// Gets or sets the border color when button is selected. + /// + [EditorDisplay("Border Style"), EditorOrder(2012)] + public Color BorderColorSelected { get; set; } /// /// Event fired when user clicks on the button. @@ -81,42 +117,6 @@ namespace FlaxEngine.GUI /// public event Action HoverEnd; - /// - /// Gets or sets the brush used for background drawing. - /// - [EditorDisplay("Style"), EditorOrder(2000), Tooltip("The brush used for background drawing.")] - public IBrush BackgroundBrush { get; set; } - - /// - /// Gets or sets the color of the border. - /// - [EditorDisplay("Style"), EditorOrder(2000)] - public Color BorderColor { get; set; } - - /// - /// Gets or sets the background color when button is selected. - /// - [EditorDisplay("Style"), EditorOrder(2010)] - public Color BackgroundColorSelected { get; set; } - - /// - /// Gets or sets the border color when button is selected. - /// - [EditorDisplay("Style"), EditorOrder(2020)] - public Color BorderColorSelected { get; set; } - - /// - /// Gets or sets the background color when button is highlighted. - /// - [EditorDisplay("Style"), EditorOrder(2000)] - public Color BackgroundColorHighlighted { get; set; } - - /// - /// Gets or sets the border color when button is highlighted. - /// - [EditorDisplay("Style"), EditorOrder(2000)] - public Color BorderColorHighlighted { get; set; } - /// /// Gets a value indicating whether this button is being pressed (by mouse or touch). /// diff --git a/Source/Engine/UI/GUI/Common/CheckBox.cs b/Source/Engine/UI/GUI/Common/CheckBox.cs index ab689bfc3..0f197422b 100644 --- a/Source/Engine/UI/GUI/Common/CheckBox.cs +++ b/Source/Engine/UI/GUI/Common/CheckBox.cs @@ -107,34 +107,34 @@ namespace FlaxEngine.GUI } } - /// - /// Gets or sets the color of the checkbox icon. - /// - [EditorDisplay("Style"), EditorOrder(2000)] - public Color ImageColor { get; set; } - /// /// Gets or sets the color of the border. /// - [EditorDisplay("Style"), EditorOrder(2000)] + [EditorDisplay("Border Style"), EditorOrder(2010), ExpandGroups] public Color BorderColor { get; set; } /// /// Gets or sets the border color when checkbox is hovered. /// - [EditorDisplay("Style"), EditorOrder(2000)] + [EditorDisplay("Border Style"), EditorOrder(2011)] public Color BorderColorHighlighted { get; set; } + + /// + /// Gets or sets the color of the checkbox icon. + /// + [EditorDisplay("Image Style"), EditorOrder(2020), ExpandGroups] + public Color ImageColor { get; set; } /// /// Gets or sets the image used to render checkbox checked state. /// - [EditorDisplay("Style"), EditorOrder(2000), Tooltip("The image used to render checkbox checked state.")] + [EditorDisplay("Image Style"), EditorOrder(2021), Tooltip("The image used to render checkbox checked state.")] public IBrush CheckedImage { get; set; } /// /// Gets or sets the image used to render checkbox intermediate state. /// - [EditorDisplay("Style"), EditorOrder(2000), Tooltip("The image used to render checkbox intermediate state.")] + [EditorDisplay("Image Style"), EditorOrder(2022), Tooltip("The image used to render checkbox intermediate state.")] public IBrush IntermediateImage { get; set; } /// diff --git a/Source/Engine/UI/GUI/Common/Dropdown.cs b/Source/Engine/UI/GUI/Common/Dropdown.cs index 80d4beb02..6ad962d6d 100644 --- a/Source/Engine/UI/GUI/Common/Dropdown.cs +++ b/Source/Engine/UI/GUI/Common/Dropdown.cs @@ -246,79 +246,79 @@ namespace FlaxEngine.GUI /// /// Gets or sets the font used to draw text. /// - [EditorDisplay("Style"), EditorOrder(2000)] + [EditorDisplay("Text Style"), EditorOrder(2021)] public FontReference Font { get; set; } /// /// Gets or sets the custom material used to render the text. It must has domain set to GUI and have a public texture parameter named Font used to sample font atlas texture with font characters data. /// - [EditorDisplay("Style"), EditorOrder(2000), Tooltip("Custom material used to render the text. It must has domain set to GUI and have a public texture parameter named Font used to sample font atlas texture with font characters data.")] + [EditorDisplay("Text Style"), EditorOrder(2022), Tooltip("Custom material used to render the text. It must has domain set to GUI and have a public texture parameter named Font used to sample font atlas texture with font characters data.")] public MaterialBase FontMaterial { get; set; } /// /// Gets or sets the color of the text. /// - [EditorDisplay("Style"), EditorOrder(2000)] + [EditorDisplay("Text Style"), EditorOrder(2020), ExpandGroups] public Color TextColor { get; set; } /// /// Gets or sets the color of the border. /// - [EditorDisplay("Style"), EditorOrder(2000)] + [EditorDisplay("Border Style"), EditorOrder(2010), ExpandGroups] public Color BorderColor { get; set; } /// /// Gets or sets the background color when dropdown popup is opened. /// - [EditorDisplay("Style"), EditorOrder(2010)] + [EditorDisplay("Background Style"), EditorOrder(2002)] public Color BackgroundColorSelected { get; set; } /// /// Gets or sets the border color when dropdown popup is opened. /// - [EditorDisplay("Style"), EditorOrder(2020)] + [EditorDisplay("Border Style"), EditorOrder(2012)] public Color BorderColorSelected { get; set; } /// /// Gets or sets the background color when dropdown is highlighted. /// - [EditorDisplay("Style"), EditorOrder(2000)] + [EditorDisplay("Background Style"), EditorOrder(2001), ExpandGroups] public Color BackgroundColorHighlighted { get; set; } /// /// Gets or sets the border color when dropdown is highlighted. /// - [EditorDisplay("Style"), EditorOrder(2000)] + [EditorDisplay("Border Style"), EditorOrder(2011)] public Color BorderColorHighlighted { get; set; } /// /// Gets or sets the image used to render dropdown drop arrow icon. /// - [EditorDisplay("Style"), EditorOrder(2000), Tooltip("The image used to render dropdown drop arrow icon.")] + [EditorDisplay("Icon Style"), EditorOrder(2033), Tooltip("The image used to render dropdown drop arrow icon.")] public IBrush ArrowImage { get; set; } /// /// Gets or sets the color used to render dropdown drop arrow icon. /// - [EditorDisplay("Style"), EditorOrder(2000), Tooltip("The color used to render dropdown drop arrow icon.")] + [EditorDisplay("Icon Style"), EditorOrder(2030), Tooltip("The color used to render dropdown drop arrow icon."), ExpandGroups] public Color ArrowColor { get; set; } /// /// Gets or sets the color used to render dropdown drop arrow icon (menu is opened). /// - [EditorDisplay("Style"), EditorOrder(2000), Tooltip("The color used to render dropdown drop arrow icon (menu is opened).")] + [EditorDisplay("Icon Style"), EditorOrder(2032), Tooltip("The color used to render dropdown drop arrow icon (menu is opened).")] public Color ArrowColorSelected { get; set; } /// /// Gets or sets the color used to render dropdown drop arrow icon (menu is highlighted). /// - [EditorDisplay("Style"), EditorOrder(2000), Tooltip("The color used to render dropdown drop arrow icon (menu is highlighted).")] + [EditorDisplay("Icon Style"), EditorOrder(2031), Tooltip("The color used to render dropdown drop arrow icon (menu is highlighted).")] public Color ArrowColorHighlighted { get; set; } /// /// Gets or sets the image used to render dropdown checked item icon. /// - [EditorDisplay("Style"), EditorOrder(2000), Tooltip("The image used to render dropdown checked item icon.")] + [EditorDisplay("Icon Style"), EditorOrder(2034), Tooltip("The image used to render dropdown checked item icon.")] public IBrush CheckedImage { get; set; } /// diff --git a/Source/Engine/UI/GUI/Common/Image.cs b/Source/Engine/UI/GUI/Common/Image.cs index 98113b00c..f911664f6 100644 --- a/Source/Engine/UI/GUI/Common/Image.cs +++ b/Source/Engine/UI/GUI/Common/Image.cs @@ -25,19 +25,19 @@ namespace FlaxEngine.GUI /// /// Gets or sets the color used to multiply the image pixels. /// - [EditorDisplay("Style"), EditorOrder(2000)] + [EditorDisplay("Image Style"), EditorOrder(2010), ExpandGroups] public Color Color { get; set; } = Color.White; /// /// Gets or sets the color used to multiply the image pixels when mouse is over the image. /// - [EditorDisplay("Style"), EditorOrder(2000)] + [EditorDisplay("Image Style"), EditorOrder(2011)] public Color MouseOverColor { get; set; } = Color.White; /// /// Gets or sets the color used to multiply the image pixels when control is disabled. /// - [EditorDisplay("Style"), EditorOrder(2000)] + [EditorDisplay("Image Style"), EditorOrder(2012)] public Color DisabledTint { get; set; } = Color.Gray; /// diff --git a/Source/Engine/UI/GUI/Common/Label.cs b/Source/Engine/UI/GUI/Common/Label.cs index b64764a40..2779dfdea 100644 --- a/Source/Engine/UI/GUI/Common/Label.cs +++ b/Source/Engine/UI/GUI/Common/Label.cs @@ -47,37 +47,37 @@ namespace FlaxEngine.GUI /// /// Gets or sets the color of the text. /// - [EditorDisplay("Style"), EditorOrder(2000), Tooltip("The color of the text.")] + [EditorDisplay("Text Style"), EditorOrder(2010), Tooltip("The color of the text."), ExpandGroups] public Color TextColor { get; set; } /// /// Gets or sets the color of the text when it is highlighted (mouse is over). /// - [EditorDisplay("Style"), EditorOrder(2000), Tooltip("The color of the text when it is highlighted (mouse is over).")] + [EditorDisplay("Text Style"), EditorOrder(2011), Tooltip("The color of the text when it is highlighted (mouse is over).")] public Color TextColorHighlighted { get; set; } /// /// Gets or sets the horizontal text alignment within the control bounds. /// - [EditorDisplay("Style"), EditorOrder(2010), Tooltip("The horizontal text alignment within the control bounds.")] + [EditorDisplay("Text Style"), EditorOrder(2020), Tooltip("The horizontal text alignment within the control bounds.")] public TextAlignment HorizontalAlignment { get; set; } = TextAlignment.Center; /// /// Gets or sets the vertical text alignment within the control bounds. /// - [EditorDisplay("Style"), EditorOrder(2020), Tooltip("The vertical text alignment within the control bounds.")] + [EditorDisplay("Text Style"), EditorOrder(2021), Tooltip("The vertical text alignment within the control bounds.")] public TextAlignment VerticalAlignment { get; set; } = TextAlignment.Center; /// /// Gets or sets the text wrapping within the control bounds. /// - [EditorDisplay("Style"), EditorOrder(2030), Tooltip("The text wrapping within the control bounds.")] + [EditorDisplay("Text Style"), EditorOrder(2022), Tooltip("The text wrapping within the control bounds.")] public TextWrapping Wrapping { get; set; } = TextWrapping.NoWrap; /// /// Gets or sets the font. /// - [EditorDisplay("Style"), EditorOrder(2000)] + [EditorDisplay("Text Style"), EditorOrder(2023)] public FontReference Font { get => _font; @@ -99,7 +99,7 @@ namespace FlaxEngine.GUI /// /// Gets or sets the custom material used to render the text. It must has domain set to GUI and have a public texture parameter named Font used to sample font atlas texture with font characters data. /// - [EditorDisplay("Style"), EditorOrder(2000)] + [EditorDisplay("Text Style"), EditorOrder(2024)] public MaterialBase Material { get; set; } /// diff --git a/Source/Engine/UI/GUI/Common/ProgressBar.cs b/Source/Engine/UI/GUI/Common/ProgressBar.cs index fa76d9d26..a742d3b43 100644 --- a/Source/Engine/UI/GUI/Common/ProgressBar.cs +++ b/Source/Engine/UI/GUI/Common/ProgressBar.cs @@ -99,19 +99,19 @@ namespace FlaxEngine.GUI /// /// Gets or sets the margin for the progress bar rectangle within the control bounds. /// - [EditorDisplay("Style"), EditorOrder(2000), Tooltip("The margin for the progress bar rectangle within the control bounds.")] + [EditorDisplay("Bar Style"), EditorOrder(2011), Tooltip("The margin for the progress bar rectangle within the control bounds.")] public Margin BarMargin { get; set; } /// /// Gets or sets the color of the progress bar rectangle. /// - [EditorDisplay("Style"), EditorOrder(2000), Tooltip("The color of the progress bar rectangle.")] + [EditorDisplay("Bar Style"), EditorOrder(2010), Tooltip("The color of the progress bar rectangle."), ExpandGroups] public Color BarColor { get; set; } /// /// Gets or sets the brush used for progress bar drawing. /// - [EditorDisplay("Style"), EditorOrder(2000), Tooltip("The brush used for progress bar drawing.")] + [EditorDisplay("Bar Style"), EditorOrder(2012), Tooltip("The brush used for progress bar drawing.")] public IBrush BarBrush { get; set; } /// diff --git a/Source/Engine/UI/GUI/Common/TextBox.cs b/Source/Engine/UI/GUI/Common/TextBox.cs index 00c381e01..5ec86a94e 100644 --- a/Source/Engine/UI/GUI/Common/TextBox.cs +++ b/Source/Engine/UI/GUI/Common/TextBox.cs @@ -27,7 +27,7 @@ namespace FlaxEngine.GUI /// /// Gets or sets the text wrapping within the control bounds. /// - [EditorDisplay("Style"), EditorOrder(2000), Tooltip("The text wrapping within the control bounds.")] + [EditorDisplay("Text Style"), EditorOrder(2023), Tooltip("The text wrapping within the control bounds.")] public TextWrapping Wrapping { get => _layout.TextWrapping; @@ -37,31 +37,31 @@ namespace FlaxEngine.GUI /// /// Gets or sets the font. /// - [EditorDisplay("Style"), EditorOrder(2000)] + [EditorDisplay("Text Style"), EditorOrder(2024)] public FontReference Font { get; set; } /// /// Gets or sets the custom material used to render the text. It must has domain set to GUI and have a public texture parameter named Font used to sample font atlas texture with font characters data. /// - [EditorDisplay("Style"), EditorOrder(2000), Tooltip("Custom material used to render the text. It must has domain set to GUI and have a public texture parameter named Font used to sample font atlas texture with font characters data.")] + [EditorDisplay("Text Style"), EditorOrder(2025), Tooltip("Custom material used to render the text. It must has domain set to GUI and have a public texture parameter named Font used to sample font atlas texture with font characters data.")] public MaterialBase TextMaterial { get; set; } /// /// Gets or sets the color of the text. /// - [EditorDisplay("Style"), EditorOrder(2000), Tooltip("The color of the text.")] + [EditorDisplay("Text Style"), EditorOrder(2020), Tooltip("The color of the text."), ExpandGroups] public Color TextColor { get; set; } /// /// Gets or sets the color of the text. /// - [EditorDisplay("Style"), EditorOrder(2000), Tooltip("The color of the watermark text.")] + [EditorDisplay("Text Style"), EditorOrder(2021), Tooltip("The color of the watermark text.")] public Color WatermarkTextColor { get; set; } /// /// Gets or sets the color of the selection (Transparent if not used). /// - [EditorDisplay("Style"), EditorOrder(2000), Tooltip("The color of the selection (Transparent if not used).")] + [EditorDisplay("Text Style"), EditorOrder(2022), Tooltip("The color of the selection (Transparent if not used).")] public Color SelectionColor { get; set; } /// diff --git a/Source/Engine/UI/GUI/Common/TextBoxBase.cs b/Source/Engine/UI/GUI/Common/TextBoxBase.cs index fb130bf18..9619f98bd 100644 --- a/Source/Engine/UI/GUI/Common/TextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/TextBoxBase.cs @@ -243,42 +243,43 @@ namespace FlaxEngine.GUI /// /// Gets or sets a value indicating whether you can scroll the text in the text box (eg. with a mouse wheel). /// + [EditorOrder(41)] public bool IsMultilineScrollable { get; set; } = true; /// /// Gets or sets textbox background color when the control is selected (has focus). /// - [EditorDisplay("Style"), EditorOrder(2000), Tooltip("The textbox background color when the control is selected (has focus).")] + [EditorDisplay("Background Style"), EditorOrder(2001), Tooltip("The textbox background color when the control is selected (has focus)."), ExpandGroups] public Color BackgroundSelectedColor { get; set; } /// /// Gets or sets the color of the caret (Transparent if not used). /// - [EditorDisplay("Style"), EditorOrder(2000), Tooltip("The color of the caret (Transparent if not used).")] + [EditorDisplay("Caret Style"), EditorOrder(2020), Tooltip("The color of the caret (Transparent if not used)."), ExpandGroups] public Color CaretColor { get; set; } /// /// Gets or sets the speed of the caret flashing animation. /// - [EditorDisplay("Style"), EditorOrder(2000), Tooltip("The speed of the caret flashing animation.")] + [EditorDisplay("Caret Style"), EditorOrder(2021), Tooltip("The speed of the caret flashing animation.")] public float CaretFlashSpeed { get; set; } = 6.0f; /// /// Gets or sets the speed of the selection background flashing animation. /// - [EditorDisplay("Style"), EditorOrder(2000), Tooltip("The speed of the selection background flashing animation.")] + [EditorDisplay("Background Style"), EditorOrder(2002), Tooltip("The speed of the selection background flashing animation.")] public float BackgroundSelectedFlashSpeed { get; set; } = 6.0f; /// /// Gets or sets the color of the border (Transparent if not used). /// - [EditorDisplay("Style"), EditorOrder(2000), Tooltip("The color of the border (Transparent if not used).")] + [EditorDisplay("Border Style"), EditorOrder(2010), Tooltip("The color of the border (Transparent if not used)."), ExpandGroups] public Color BorderColor { get; set; } /// /// Gets or sets the color of the border when control is focused (Transparent if not used). /// - [EditorDisplay("Style"), EditorOrder(2000), Tooltip("The color of the border when control is focused (Transparent if not used)")] + [EditorDisplay("Border Style"), EditorOrder(2011), Tooltip("The color of the border when control is focused (Transparent if not used)")] public Color BorderSelectedColor { get; set; } /// @@ -409,6 +410,7 @@ namespace FlaxEngine.GUI /// /// Gets or sets the selection range. /// + [EditorOrder(50)] public TextRange SelectionRange { get => new TextRange(SelectionLeft, SelectionRight); diff --git a/Source/Engine/UI/GUI/Control.cs b/Source/Engine/UI/GUI/Control.cs index 427c1aa8f..49b7f65d3 100644 --- a/Source/Engine/UI/GUI/Control.cs +++ b/Source/Engine/UI/GUI/Control.cs @@ -166,7 +166,7 @@ namespace FlaxEngine.GUI /// /// Gets or sets control background color (transparent color (alpha=0) means no background rendering) /// - [ExpandGroups, EditorDisplay("Style"), EditorOrder(2000), Tooltip("The control background color. Use transparent color (alpha=0) to hide background.")] + [ExpandGroups, EditorDisplay("Background Style"), EditorOrder(2000), Tooltip("The control background color. Use transparent color (alpha=0) to hide background.")] public Color BackgroundColor { get => _backgroundColor; diff --git a/Source/Engine/UI/GUI/Panels/DropPanel.cs b/Source/Engine/UI/GUI/Panels/DropPanel.cs index 409209549..9662f61b9 100644 --- a/Source/Engine/UI/GUI/Panels/DropPanel.cs +++ b/Source/Engine/UI/GUI/Panels/DropPanel.cs @@ -94,49 +94,49 @@ namespace FlaxEngine.GUI } } } - - /// - /// Gets or sets the color used to draw header text. - /// - [EditorDisplay("Style"), EditorOrder(2000)] - public Color HeaderTextColor; - - /// - /// Gets or sets the color of the header. - /// - [EditorDisplay("Style"), EditorOrder(2000)] - public Color HeaderColor { get; set; } - - /// - /// Gets or sets the color of the header when mouse is over. - /// - [EditorDisplay("Style"), EditorOrder(2000)] - public Color HeaderColorMouseOver { get; set; } - - /// - /// Gets or sets the font used to render panel header text. - /// - [EditorDisplay("Style"), EditorOrder(2000)] - public FontReference HeaderTextFont { get; set; } - - /// - /// Gets or sets the custom material used to render the text. It must has domain set to GUI and have a public texture parameter named Font used to sample font atlas texture with font characters data. - /// - [EditorDisplay("Style"), EditorOrder(2000), Tooltip("Custom material used to render the header text. It must has domain set to GUI and have a public texture parameter named Font used to sample font atlas texture with font characters data.")] - public MaterialBase HeaderTextMaterial { get; set; } - + /// /// Gets or sets a value indicating whether enable drop down icon drawing. /// - [EditorDisplay("Style"), EditorOrder(2000)] + [EditorOrder(1)] public bool EnableDropDownIcon { get; set; } /// /// Gets or sets a value indicating whether to enable containment line drawing, /// - [EditorDisplay("Style"), EditorOrder(2000)] + [EditorOrder(2)] public bool EnableContainmentLines { get; set; } = false; + /// + /// Gets or sets the color used to draw header text. + /// + [EditorDisplay("Header Style"), EditorOrder(2010), ExpandGroups] + public Color HeaderTextColor; + + /// + /// Gets or sets the color of the header. + /// + [EditorDisplay("Header Style"), EditorOrder(2011)] + public Color HeaderColor { get; set; } + + /// + /// Gets or sets the color of the header when mouse is over. + /// + [EditorDisplay("Header Style"), EditorOrder(2012)] + public Color HeaderColorMouseOver { get; set; } + + /// + /// Gets or sets the font used to render panel header text. + /// + [EditorDisplay("Header Text Style"), EditorOrder(2020), ExpandGroups] + public FontReference HeaderTextFont { get; set; } + + /// + /// Gets or sets the custom material used to render the text. It must has domain set to GUI and have a public texture parameter named Font used to sample font atlas texture with font characters data. + /// + [EditorDisplay("Header Text Style"), EditorOrder(2021), Tooltip("Custom material used to render the header text. It must has domain set to GUI and have a public texture parameter named Font used to sample font atlas texture with font characters data.")] + public MaterialBase HeaderTextMaterial { get; set; } + /// /// Occurs when mouse right-clicks over the header. /// @@ -192,13 +192,13 @@ namespace FlaxEngine.GUI /// /// Gets or sets the image used to render drop panel drop arrow icon when panel is opened. /// - [EditorDisplay("Style"), EditorOrder(2000), Tooltip("The image used to render drop panel drop arrow icon when panel is opened.")] + [EditorDisplay("Icon Style"), EditorOrder(2030), Tooltip("The image used to render drop panel drop arrow icon when panel is opened."), ExpandGroups] public IBrush ArrowImageOpened { get; set; } /// /// Gets or sets the image used to render drop panel drop arrow icon when panel is closed. /// - [EditorDisplay("Style"), EditorOrder(2000), Tooltip("The image used to render drop panel drop arrow icon when panel is closed.")] + [EditorDisplay("Icon Style"), EditorOrder(2031), Tooltip("The image used to render drop panel drop arrow icon when panel is closed.")] public IBrush ArrowImageClosed { get; set; } /// From eeaf7eb733b31b9b45ca74a8dcc127c5f329a3c5 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Wed, 10 May 2023 08:04:47 -0500 Subject: [PATCH 017/116] Move BoxSize --- Source/Engine/UI/GUI/Common/CheckBox.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Engine/UI/GUI/Common/CheckBox.cs b/Source/Engine/UI/GUI/Common/CheckBox.cs index 0f197422b..c5a6e3bb3 100644 --- a/Source/Engine/UI/GUI/Common/CheckBox.cs +++ b/Source/Engine/UI/GUI/Common/CheckBox.cs @@ -97,6 +97,7 @@ namespace FlaxEngine.GUI /// /// Gets or sets the size of the box. /// + [EditorOrder(20)] public float BoxSize { get => _boxSize; From 27b6ba83a60a7a698f438ed71ea58698971a2cb6 Mon Sep 17 00:00:00 2001 From: Menotdan Date: Wed, 10 May 2023 10:31:44 -0400 Subject: [PATCH 018/116] Added Windows Base Class for getting screen pixels and other screen-wide utilities --- Source/Engine/Platform/Windows/WindowsScreenUtils.cpp | 0 Source/Engine/Platform/Windows/WindowsScreenUtils.h | 5 +++++ 2 files changed, 5 insertions(+) create mode 100644 Source/Engine/Platform/Windows/WindowsScreenUtils.cpp create mode 100644 Source/Engine/Platform/Windows/WindowsScreenUtils.h diff --git a/Source/Engine/Platform/Windows/WindowsScreenUtils.cpp b/Source/Engine/Platform/Windows/WindowsScreenUtils.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/Source/Engine/Platform/Windows/WindowsScreenUtils.h b/Source/Engine/Platform/Windows/WindowsScreenUtils.h new file mode 100644 index 000000000..4e8841525 --- /dev/null +++ b/Source/Engine/Platform/Windows/WindowsScreenUtils.h @@ -0,0 +1,5 @@ +#pragma once +class WindowsScreenUtils +{ +}; + From 873d42344bc623612f054f50a97dd422b7b7a836 Mon Sep 17 00:00:00 2001 From: Menotdan Date: Wed, 10 May 2023 10:39:25 -0400 Subject: [PATCH 019/116] remove files as visual studio is bugging. --- Source/Engine/Platform/Windows/WindowsScreenUtils.cpp | 0 Source/Engine/Platform/Windows/WindowsScreenUtils.h | 5 ----- 2 files changed, 5 deletions(-) delete mode 100644 Source/Engine/Platform/Windows/WindowsScreenUtils.cpp delete mode 100644 Source/Engine/Platform/Windows/WindowsScreenUtils.h diff --git a/Source/Engine/Platform/Windows/WindowsScreenUtils.cpp b/Source/Engine/Platform/Windows/WindowsScreenUtils.cpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/Source/Engine/Platform/Windows/WindowsScreenUtils.h b/Source/Engine/Platform/Windows/WindowsScreenUtils.h deleted file mode 100644 index 4e8841525..000000000 --- a/Source/Engine/Platform/Windows/WindowsScreenUtils.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once -class WindowsScreenUtils -{ -}; - From 51004bbd9dd086f417a77972af79ba7bbe291055 Mon Sep 17 00:00:00 2001 From: Menotdan Date: Wed, 10 May 2023 11:03:05 -0400 Subject: [PATCH 020/116] Learned about filter folders and now VS likes me :) --- Source/Engine/Platform/Windows/WindowsScreenUtils.cpp | 0 Source/Engine/Platform/Windows/WindowsScreenUtils.h | 5 +++++ 2 files changed, 5 insertions(+) create mode 100644 Source/Engine/Platform/Windows/WindowsScreenUtils.cpp create mode 100644 Source/Engine/Platform/Windows/WindowsScreenUtils.h diff --git a/Source/Engine/Platform/Windows/WindowsScreenUtils.cpp b/Source/Engine/Platform/Windows/WindowsScreenUtils.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/Source/Engine/Platform/Windows/WindowsScreenUtils.h b/Source/Engine/Platform/Windows/WindowsScreenUtils.h new file mode 100644 index 000000000..4e8841525 --- /dev/null +++ b/Source/Engine/Platform/Windows/WindowsScreenUtils.h @@ -0,0 +1,5 @@ +#pragma once +class WindowsScreenUtils +{ +}; + From 1855ff133ec25ad68f8f482ae0eef6b19b0d7b7e Mon Sep 17 00:00:00 2001 From: Menotdan Date: Wed, 10 May 2023 12:46:54 -0400 Subject: [PATCH 021/116] Tweak a few classes, add the Base classes. --- .../Engine/Platform/Base/ScreenUtilsBase.cpp | 0 Source/Engine/Platform/Base/ScreenUtilsBase.h | 0 Source/Engine/Platform/ScreenUtils.cpp | 0 Source/Engine/Platform/ScreenUtils.h | 17 ++++ .../Platform/Windows/WindowsScreenUtils.cpp | 22 ++++++ .../Platform/Windows/WindowsScreenUtils.h | 8 +- Source/Engine/Utilities/Screenshot.cpp | 79 ------------------- Source/Engine/Utilities/Screenshot.h | 10 --- 8 files changed, 45 insertions(+), 91 deletions(-) create mode 100644 Source/Engine/Platform/Base/ScreenUtilsBase.cpp create mode 100644 Source/Engine/Platform/Base/ScreenUtilsBase.h create mode 100644 Source/Engine/Platform/ScreenUtils.cpp create mode 100644 Source/Engine/Platform/ScreenUtils.h diff --git a/Source/Engine/Platform/Base/ScreenUtilsBase.cpp b/Source/Engine/Platform/Base/ScreenUtilsBase.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/Source/Engine/Platform/Base/ScreenUtilsBase.h b/Source/Engine/Platform/Base/ScreenUtilsBase.h new file mode 100644 index 000000000..e69de29bb diff --git a/Source/Engine/Platform/ScreenUtils.cpp b/Source/Engine/Platform/ScreenUtils.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/Source/Engine/Platform/ScreenUtils.h b/Source/Engine/Platform/ScreenUtils.h new file mode 100644 index 000000000..91192dd41 --- /dev/null +++ b/Source/Engine/Platform/ScreenUtils.h @@ -0,0 +1,17 @@ +#if PLATFORM_WINDOWS +#include "Windows/WindowsScreenUtils.h" +#elif PLATFORM_LINUX +#include "Linux/LinuxScreenUtils.h" +#elif PLATFORM_MAC +#include "Mac/MacScreenUtils.h" +#else +#error No Screen Utils For This Platform! Please ensure you are not targetting an Editor build. +#endif + +#include "Types.h" +#include "Engine/Scripting/ScriptingType.h" + +API_CLASS(Static) class FLAXENGINE_API ScreenUtils +{ + DECLARE_SCRIPTING_TYPE_NO_SPAWN(ScreenUtils); +}; diff --git a/Source/Engine/Platform/Windows/WindowsScreenUtils.cpp b/Source/Engine/Platform/Windows/WindowsScreenUtils.cpp index e69de29bb..8f9798184 100644 --- a/Source/Engine/Platform/Windows/WindowsScreenUtils.cpp +++ b/Source/Engine/Platform/Windows/WindowsScreenUtils.cpp @@ -0,0 +1,22 @@ +#include "WindowsScreenUtils.h" +#include "Engine/Core/Math/Color32.h" +#include "Engine/Core/Math/Vector2.h" + +#include + +Color32 PlatformScreenUtils::GetPixelAt(int32 x, int32 y) { + HDC deviceContext = GetDC(NULL); + COLORREF color = GetPixel(deviceContext, x, y); + ReleaseDC(NULL, deviceContext); + + Color32 returnColor = { GetRValue(color), GetGValue(color), GetBValue(color), 255 }; + return returnColor; +} + +Int2 PlatformScreenUtils::GetScreenCursorPosition() { + POINT cursorPos; + GetCursorPos(&cursorPos); + + Int2 returnCursorPos = { cursorPos.x, cursorPos.y }; + return returnCursorPos; +} diff --git a/Source/Engine/Platform/Windows/WindowsScreenUtils.h b/Source/Engine/Platform/Windows/WindowsScreenUtils.h index 4e8841525..690be0b88 100644 --- a/Source/Engine/Platform/Windows/WindowsScreenUtils.h +++ b/Source/Engine/Platform/Windows/WindowsScreenUtils.h @@ -1,5 +1,9 @@ #pragma once -class WindowsScreenUtils -{ +#include "Engine/Core/Math/Color32.h" + +class PlatformScreenUtils { +public: + static Color32 GetPixelAt(int32 x, int32 y); + static Int2 GetScreenCursorPosition(); }; diff --git a/Source/Engine/Utilities/Screenshot.cpp b/Source/Engine/Utilities/Screenshot.cpp index 0635180a8..fbde8043b 100644 --- a/Source/Engine/Utilities/Screenshot.cpp +++ b/Source/Engine/Utilities/Screenshot.cpp @@ -130,70 +130,6 @@ void CaptureScreenshot::OnFail() ThreadPoolTask::OnFail(); } -Delegate Screenshot::PixelReadDelegate; - -/// -/// Capture screenshot helper -/// -/// -class GetPixelData : public ThreadPoolTask -{ - friend Screenshot; -private: - TextureData _data; - Color32 _color; - -public: - int32 x; - int32 y; - -public: - /// - /// Gets the texture data container. - /// - /// Texture data - FORCE_INLINE TextureData& GetData() - { - return _data; - } - - FORCE_INLINE Color32 GetColor() - { - return _color; - } - -protected: - // [ThreadPoolTask] - bool Run() override; - void OnFail() override; -}; - -bool GetPixelData::Run() -{ - LOG(Warning, "REAL"); - TextureMipData *mipData = _data.GetData(0, 0); - Array pixels; - mipData->GetPixels(pixels, _data.Width, _data.Height, _data.Format); - - LOG(Warning, "{0}, {1} ({2} at {3})", x, y, pixels.Count(), (y * _data.Width) + x); - _color = pixels[(y * _data.Width) + x]; - LOG(Warning, "really real"); - LOG(Warning, "Color: R: {0}, G: {1}, B: {2}", _color.R, _color.G, _color.B); - - LOG(Warning, "Bound functions: {0}", Screenshot::PixelReadDelegate.Count()); - Screenshot::PixelReadDelegate(_color); - return false; -} - -void GetPixelData::OnFail() -{ - LOG(Warning, "Cannot get pixel data."); - - // Base - ThreadPoolTask::OnFail(); -} - - void Screenshot::Capture(GPUTexture* target, const StringView& path) { // Validate @@ -307,18 +243,3 @@ void Screenshot::Capture(const StringView& path) Capture(mainTask, path); } - -Color32 Screenshot::GetPixelAt(int32 x, int32 y) { - GPUSwapChain* swapChain = Engine::MainWindow->GetSwapChain(); - - auto getPixelTask = New(); - getPixelTask->x = x; - getPixelTask->y = y; - - Task* downloadTask = swapChain->DownloadDataAsync(getPixelTask->GetData()); - downloadTask->ContinueWith(getPixelTask); - LOG(Warning, "Started download task. real"); - downloadTask->Start(); - - return getPixelTask->GetColor(); -} diff --git a/Source/Engine/Utilities/Screenshot.h b/Source/Engine/Utilities/Screenshot.h index 4fe497355..c51c34199 100644 --- a/Source/Engine/Utilities/Screenshot.h +++ b/Source/Engine/Utilities/Screenshot.h @@ -39,14 +39,4 @@ API_CLASS(Static) class FLAXENGINE_API Screenshot /// /// The custom file location. Use null or empty to use default one. API_FUNCTION() static void Capture(const StringView& path = StringView::Empty); - - /// - /// Get the pixel at specified coordinates. - /// - /// The x coordinate to read. - /// The y coordinate to read. - /// The color - API_FUNCTION() static Color32 GetPixelAt(int32 x, int32 y); - - API_EVENT() static Delegate PixelReadDelegate; }; From 2ce727d994802d1fdb7716676648ea60caf1f170 Mon Sep 17 00:00:00 2001 From: Menotdan Date: Wed, 10 May 2023 13:34:28 -0400 Subject: [PATCH 022/116] Correctly implement the ScreenUtils class --- .../Editor/GUI/Dialogs/ColorPickerDialog.cs | 28 +++++++++++------- .../Engine/Platform/Base/ScreenUtilsBase.cpp | 11 +++++++ Source/Engine/Platform/Base/ScreenUtilsBase.h | 29 +++++++++++++++++++ Source/Engine/Platform/ScreenUtils.h | 6 ---- .../Platform/Windows/WindowsScreenUtils.cpp | 8 +++-- .../Platform/Windows/WindowsScreenUtils.h | 11 +++++-- 6 files changed, 73 insertions(+), 20 deletions(-) diff --git a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs index 53eabebb4..8df064581 100644 --- a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs +++ b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs @@ -112,9 +112,6 @@ namespace FlaxEditor.GUI.Dialogs _onChanged = colorChanged; _onClosed = pickerClosed; - // Register the event for eyedropper pixels being read. - Screenshot.PixelReadDelegate += PixelDataRead; - // Selector _cSelector = new ColorSelectorWithSliders(180, 18) { @@ -204,7 +201,7 @@ namespace FlaxEditor.GUI.Dialogs { Parent = this, }; - _cEyedropper.Clicked += OnEyedropColor; + _cEyedropper.Clicked += OnEyedropStart; _cEyedropper.Height = (_cValue.Bottom - _cEyedropper.Y) * 0.5f; _cEyedropper.Width = _cEyedropper.Height; _cEyedropper.X -= _cEyedropper.Width; @@ -214,18 +211,21 @@ namespace FlaxEditor.GUI.Dialogs SelectedColor = initialValue; } - private void PixelDataRead(Color32 pixel_color) + private Color32 GetEyedropColor() { - Color color = pixel_color; - Editor.Log(string.Format("Color: {0} {1} {2}", color.R, color.G, color.B)); + Int2 mousePosition = ScreenUtils.GetScreenCursorPosition(); + Color32 pixelColor = ScreenUtils.GetPixelAt(mousePosition.X, mousePosition.Y); + + return pixelColor; } - private void OnEyedropColor() + private void OnEyedropStart() { - Float2 mousePosition = FlaxEngine.Input.MouseScreenPosition; - Screenshot.GetPixelAt(Mathf.FloorToInt(mousePosition.X), Mathf.FloorToInt(mousePosition.Y)); + Color32 pixelColor = GetEyedropColor(); + Editor.Log(string.Format("Pixel Color: ({0}, {1}, {2})", pixelColor.R, pixelColor.G, pixelColor.B)); } + private void OnRGBAChanged() { if (_disableEvents) @@ -251,6 +251,14 @@ namespace FlaxEditor.GUI.Dialogs SelectedColor = color; } + + /// + public override void Update(float deltaTime) + { + base.Update(deltaTime); + + } + /// public override void Draw() { diff --git a/Source/Engine/Platform/Base/ScreenUtilsBase.cpp b/Source/Engine/Platform/Base/ScreenUtilsBase.cpp index e69de29bb..081c65bf4 100644 --- a/Source/Engine/Platform/Base/ScreenUtilsBase.cpp +++ b/Source/Engine/Platform/Base/ScreenUtilsBase.cpp @@ -0,0 +1,11 @@ +#include "ScreenUtilsBase.h" +#include "Engine/Core/Math/Color32.h" +#include "Engine/Core/Math/Vector2.h" + +Color32 ScreenUtilsBase::GetPixelAt(int32 x, int32 y) { + return Color32::Black; +} + +Int2 ScreenUtilsBase::GetScreenCursorPosition() { + return { 0, 0 }; +} diff --git a/Source/Engine/Platform/Base/ScreenUtilsBase.h b/Source/Engine/Platform/Base/ScreenUtilsBase.h index e69de29bb..ddfa4f329 100644 --- a/Source/Engine/Platform/Base/ScreenUtilsBase.h +++ b/Source/Engine/Platform/Base/ScreenUtilsBase.h @@ -0,0 +1,29 @@ +#pragma once + +#include "Engine/Core/Types/BaseTypes.h" + +API_INJECT_CODE(cpp, "#include \"Engine/Platform/ScreenUtils.h\""); + +/// +/// Platform-dependent screen utilties. +/// +API_CLASS(Static, Name = "ScreenUtils", Tag = "NativeInvokeUseName") +class FLAXENGINE_API ScreenUtilsBase +{ +public: + static struct FLAXENGINE_API ScriptingTypeInitializer TypeInitializer; + + /// + /// Gets the pixel color at the specified coordinates. + /// + /// X Coordinate to read. + /// Y Coordinate to read. + /// Pixel color at the specified coordinates. + API_FUNCTION() static Color32 GetPixelAt(int32 x, int32 y); + + /// + /// Gets the cursor position, in screen cooridnates. + /// + /// Cursor position, in screen coordinates. + API_FUNCTION() static Int2 GetScreenCursorPosition(); +}; diff --git a/Source/Engine/Platform/ScreenUtils.h b/Source/Engine/Platform/ScreenUtils.h index 91192dd41..3033d2118 100644 --- a/Source/Engine/Platform/ScreenUtils.h +++ b/Source/Engine/Platform/ScreenUtils.h @@ -9,9 +9,3 @@ #endif #include "Types.h" -#include "Engine/Scripting/ScriptingType.h" - -API_CLASS(Static) class FLAXENGINE_API ScreenUtils -{ - DECLARE_SCRIPTING_TYPE_NO_SPAWN(ScreenUtils); -}; diff --git a/Source/Engine/Platform/Windows/WindowsScreenUtils.cpp b/Source/Engine/Platform/Windows/WindowsScreenUtils.cpp index 8f9798184..a4c4127bd 100644 --- a/Source/Engine/Platform/Windows/WindowsScreenUtils.cpp +++ b/Source/Engine/Platform/Windows/WindowsScreenUtils.cpp @@ -4,7 +4,11 @@ #include -Color32 PlatformScreenUtils::GetPixelAt(int32 x, int32 y) { +#if PLATFORM_WINDOWS +#pragma comment(lib, "Gdi32.lib") +#endif + +Color32 ScreenUtils::GetPixelAt(int32 x, int32 y) { HDC deviceContext = GetDC(NULL); COLORREF color = GetPixel(deviceContext, x, y); ReleaseDC(NULL, deviceContext); @@ -13,7 +17,7 @@ Color32 PlatformScreenUtils::GetPixelAt(int32 x, int32 y) { return returnColor; } -Int2 PlatformScreenUtils::GetScreenCursorPosition() { +Int2 ScreenUtils::GetScreenCursorPosition() { POINT cursorPos; GetCursorPos(&cursorPos); diff --git a/Source/Engine/Platform/Windows/WindowsScreenUtils.h b/Source/Engine/Platform/Windows/WindowsScreenUtils.h index 690be0b88..8f03e033f 100644 --- a/Source/Engine/Platform/Windows/WindowsScreenUtils.h +++ b/Source/Engine/Platform/Windows/WindowsScreenUtils.h @@ -1,9 +1,16 @@ #pragma once -#include "Engine/Core/Math/Color32.h" -class PlatformScreenUtils { +#if PLATFORM_WINDOWS + +#include "Engine/Core/Math/Color32.h" +#include "Engine/Platform/Base/ScreenUtilsBase.h" + +class FLAXENGINE_API ScreenUtils : public ScreenUtilsBase { public: + + // [ScreenUtilsBase] static Color32 GetPixelAt(int32 x, int32 y); static Int2 GetScreenCursorPosition(); }; +#endif From 4144c22dc45c001ba9093b6f7d6fb795fd7c5cc4 Mon Sep 17 00:00:00 2001 From: Menotdan Date: Wed, 10 May 2023 13:41:24 -0400 Subject: [PATCH 023/116] Working demo of colorpicker eyedropper --- Source/Editor/GUI/Dialogs/ColorPickerDialog.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs index 8df064581..1b4b8e740 100644 --- a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs +++ b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs @@ -37,6 +37,7 @@ namespace FlaxEditor.GUI.Dialogs private Color _value; private bool _disableEvents; private bool _useDynamicEditing; + private bool _activeEyedropper; private ColorValueBox.ColorPickerEvent _onChanged; private ColorValueBox.ColorPickerClosedEvent _onClosed; @@ -108,6 +109,7 @@ namespace FlaxEditor.GUI.Dialogs { _initialValue = initialValue; _useDynamicEditing = useDynamicEditing; + _activeEyedropper = false; _value = Color.Transparent; _onChanged = colorChanged; _onClosed = pickerClosed; @@ -221,10 +223,16 @@ namespace FlaxEditor.GUI.Dialogs private void OnEyedropStart() { - Color32 pixelColor = GetEyedropColor(); - Editor.Log(string.Format("Pixel Color: ({0}, {1}, {2})", pixelColor.R, pixelColor.G, pixelColor.B)); + _activeEyedropper = true; } + private void UpdateEyedrop() + { + Color32 pixelColor = GetEyedropColor(); + Editor.Log(string.Format("Pixel Color: ({0}, {1}, {2})", pixelColor.R, pixelColor.G, pixelColor.B)); + + SelectedColor = new Color(pixelColor.R, pixelColor.G, pixelColor.B); + } private void OnRGBAChanged() { @@ -257,6 +265,10 @@ namespace FlaxEditor.GUI.Dialogs { base.Update(deltaTime); + if (_activeEyedropper) + { + UpdateEyedrop(); + } } /// From 627b1cee103b58fe42324d72e6fa69323834f508 Mon Sep 17 00:00:00 2001 From: Menotdan <32620310+Menotdan@users.noreply.github.com> Date: Wed, 10 May 2023 17:49:45 -0400 Subject: [PATCH 024/116] Resolve issues that came through when attempting to merge previously. --- Source/Editor/Content/Items/ContentFolder.cs | 1 + Source/Editor/Content/Items/ContentItem.cs | 2 ++ Source/Editor/Utilities/Utils.cs | 1 + 3 files changed, 4 insertions(+) diff --git a/Source/Editor/Content/Items/ContentFolder.cs b/Source/Editor/Content/Items/ContentFolder.cs index a654ea697..86c374fee 100644 --- a/Source/Editor/Content/Items/ContentFolder.cs +++ b/Source/Editor/Content/Items/ContentFolder.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Text; using FlaxEditor.GUI.Drag; using FlaxEngine; using FlaxEngine.GUI; diff --git a/Source/Editor/Content/Items/ContentItem.cs b/Source/Editor/Content/Items/ContentItem.cs index 60adb1caa..2a0390b30 100644 --- a/Source/Editor/Content/Items/ContentItem.cs +++ b/Source/Editor/Content/Items/ContentItem.cs @@ -2,6 +2,8 @@ using System; using System.Collections.Generic; +using System.IO; +using System.Text; using FlaxEditor.Content.GUI; using FlaxEditor.GUI.Drag; using FlaxEngine; diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index 614c4cdc7..acc0cf605 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -1030,6 +1030,7 @@ namespace FlaxEditor.Utilities return path; } + /// /// Gets the asset name relative to the project root folder (without asset file extension) /// /// The asset path. From a2d4207504b65c4abaeec97950befb0d3519f429 Mon Sep 17 00:00:00 2001 From: Menotdan <32620310+Menotdan@users.noreply.github.com> Date: Wed, 10 May 2023 19:30:58 -0400 Subject: [PATCH 025/116] Fix compile errors. --- Source/Editor/Content/Items/ContentFolder.cs | 1 + Source/Editor/Content/Items/ContentItem.cs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Source/Editor/Content/Items/ContentFolder.cs b/Source/Editor/Content/Items/ContentFolder.cs index a654ea697..86c374fee 100644 --- a/Source/Editor/Content/Items/ContentFolder.cs +++ b/Source/Editor/Content/Items/ContentFolder.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Text; using FlaxEditor.GUI.Drag; using FlaxEngine; using FlaxEngine.GUI; diff --git a/Source/Editor/Content/Items/ContentItem.cs b/Source/Editor/Content/Items/ContentItem.cs index 60adb1caa..2a0390b30 100644 --- a/Source/Editor/Content/Items/ContentItem.cs +++ b/Source/Editor/Content/Items/ContentItem.cs @@ -2,6 +2,8 @@ using System; using System.Collections.Generic; +using System.IO; +using System.Text; using FlaxEditor.Content.GUI; using FlaxEditor.GUI.Drag; using FlaxEngine; From f80ded20573a6f1d85e79fd42bee1696d47567a0 Mon Sep 17 00:00:00 2001 From: Olly Rybak Date: Thu, 11 May 2023 17:30:20 +1000 Subject: [PATCH 026/116] Initial setup --- Source/Engine/Input/Enums.h | 25 +++++++++++++++++++++++++ Source/Engine/Input/Input.cpp | 20 ++++++++++++++++++++ Source/Engine/Input/Input.h | 8 ++++++++ Source/Engine/Input/VirtualInput.h | 3 +++ 4 files changed, 56 insertions(+) diff --git a/Source/Engine/Input/Enums.h b/Source/Engine/Input/Enums.h index 80e10786f..272231e41 100644 --- a/Source/Engine/Input/Enums.h +++ b/Source/Engine/Input/Enums.h @@ -263,6 +263,31 @@ API_ENUM() enum class InputActionMode Release = 2, }; +/// +/// The input action event trigger modes. +/// +API_ENUM() enum class InputActionPhase +{ + /// + /// User is pressing the key/button. + /// + Pressing = 0, + + /// + /// User pressed the key/button (but wasn't pressing it in the previous frame). + /// + Press = 1, + + /// + /// User released the key/button (was pressing it in the previous frame). + /// + Release = 2, + + Waiting = 3, + + None = 4, +}; + /// /// The input gamepad index. /// diff --git a/Source/Engine/Input/Input.cpp b/Source/Engine/Input/Input.cpp index 7a4547c62..32a672635 100644 --- a/Source/Engine/Input/Input.cpp +++ b/Source/Engine/Input/Input.cpp @@ -29,11 +29,13 @@ struct ActionData { bool Active; uint64 FrameIndex; + InputActionPhase Phase; ActionData() { Active = false; FrameIndex = 0; + Phase = InputActionPhase::None; } }; @@ -597,6 +599,16 @@ bool Input::GetAction(const StringView& name) return e ? e->Active : false; } +InputActionPhase Input::GetActionPhase(const StringView& name) +{ + const auto e = Actions.TryGet(name); + if (e != nullptr) + { + return e->Phase; + } + return InputActionPhase::None; +} + float Input::GetAxis(const StringView& name) { const auto e = Axes.TryGet(name); @@ -806,6 +818,7 @@ void InputService::Update() ActionData& data = Actions[name]; data.Active = false; + data.Phase = InputActionPhase::Waiting; // Mark as updated in this frame data.FrameIndex = frame; @@ -830,6 +843,13 @@ void InputService::Update() isActive = Input::GetKeyUp(config.Key) || Input::GetMouseButtonUp(config.MouseButton) || Input::GetGamepadButtonUp(config.Gamepad, config.GamepadButton); } + if (Input::GetKeyDown(config.Key) || Input::GetMouseButtonDown(config.MouseButton) || Input::GetGamepadButtonDown(config.Gamepad, config.GamepadButton)) + data.Phase = InputActionPhase::Press; + else if (Input::GetKey(config.Key) || Input::GetMouseButton(config.MouseButton) || Input::GetGamepadButton(config.Gamepad, config.GamepadButton)) + data.Phase = InputActionPhase::Pressing; + else if (Input::GetKeyUp(config.Key) || Input::GetMouseButtonUp(config.MouseButton) || Input::GetGamepadButtonUp(config.Gamepad, config.GamepadButton)) + data.Phase = InputActionPhase::Release; + data.Active |= isActive; } diff --git a/Source/Engine/Input/Input.h b/Source/Engine/Input/Input.h index d9f86076b..dd40c485a 100644 --- a/Source/Engine/Input/Input.h +++ b/Source/Engine/Input/Input.h @@ -309,6 +309,14 @@ public: /// API_FUNCTION() static bool GetAction(const StringView& name); + /// + /// Gets the value of the virtual action identified by name. Use to get the current config. + /// + /// The action name. + /// True if action has been triggered in the current frame (e.g. button pressed), otherwise false. + /// + API_FUNCTION() static InputActionPhase GetActionPhase(const StringView& name); + /// /// Gets the value of the virtual axis identified by name. Use to get the current config. /// diff --git a/Source/Engine/Input/VirtualInput.h b/Source/Engine/Input/VirtualInput.h index 8fd4efeb4..e8f669632 100644 --- a/Source/Engine/Input/VirtualInput.h +++ b/Source/Engine/Input/VirtualInput.h @@ -48,6 +48,9 @@ API_STRUCT() struct ActionConfig /// API_FIELD(Attributes="EditorOrder(40)") InputGamepadIndex Gamepad; + + API_FIELD(Attributes = "EditorOrder(50)") + InputActionMode Phase; }; /// From 395a72b7d45fc8d50be120cef62a277aff7efa13 Mon Sep 17 00:00:00 2001 From: Olly Rybak Date: Thu, 11 May 2023 21:10:40 +1000 Subject: [PATCH 027/116] Added comments / reformat --- Source/Engine/Input/Enums.h | 22 ++++++++++++++-------- Source/Engine/Input/Input.cpp | 10 ++++++++-- Source/Engine/Input/Input.h | 2 +- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/Source/Engine/Input/Enums.h b/Source/Engine/Input/Enums.h index 272231e41..4c96f715f 100644 --- a/Source/Engine/Input/Enums.h +++ b/Source/Engine/Input/Enums.h @@ -264,28 +264,34 @@ API_ENUM() enum class InputActionMode }; /// -/// The input action event trigger modes. +/// The input action event phases. /// API_ENUM() enum class InputActionPhase { + /// + /// The key/button is not assigned. + /// + None = 0, + + /// + /// The key/button is waiting for input. + /// + Waiting = 1, + /// /// User is pressing the key/button. /// - Pressing = 0, + Pressing = 2, /// /// User pressed the key/button (but wasn't pressing it in the previous frame). /// - Press = 1, + Press = 3, /// /// User released the key/button (was pressing it in the previous frame). /// - Release = 2, - - Waiting = 3, - - None = 4, + Release = 4, }; /// diff --git a/Source/Engine/Input/Input.cpp b/Source/Engine/Input/Input.cpp index 32a672635..2f45a4a16 100644 --- a/Source/Engine/Input/Input.cpp +++ b/Source/Engine/Input/Input.cpp @@ -25,7 +25,7 @@ struct AxisEvaluation bool Used; }; -struct ActionData +struct ActionData { bool Active; uint64 FrameIndex; @@ -35,7 +35,7 @@ struct ActionData { Active = false; FrameIndex = 0; - Phase = InputActionPhase::None; + Phase = InputActionPhase::Waiting; } }; @@ -844,11 +844,17 @@ void InputService::Update() } if (Input::GetKeyDown(config.Key) || Input::GetMouseButtonDown(config.MouseButton) || Input::GetGamepadButtonDown(config.Gamepad, config.GamepadButton)) + { data.Phase = InputActionPhase::Press; + } else if (Input::GetKey(config.Key) || Input::GetMouseButton(config.MouseButton) || Input::GetGamepadButton(config.Gamepad, config.GamepadButton)) + { data.Phase = InputActionPhase::Pressing; + } else if (Input::GetKeyUp(config.Key) || Input::GetMouseButtonUp(config.MouseButton) || Input::GetGamepadButtonUp(config.Gamepad, config.GamepadButton)) + { data.Phase = InputActionPhase::Release; + } data.Active |= isActive; } diff --git a/Source/Engine/Input/Input.h b/Source/Engine/Input/Input.h index dd40c485a..35f3dac32 100644 --- a/Source/Engine/Input/Input.h +++ b/Source/Engine/Input/Input.h @@ -313,7 +313,7 @@ public: /// Gets the value of the virtual action identified by name. Use to get the current config. /// /// The action name. - /// True if action has been triggered in the current frame (e.g. button pressed), otherwise false. + /// A InputActionPhase determining the current phase of the Action (e.g If it was just pressed, is being held or just released). /// API_FUNCTION() static InputActionPhase GetActionPhase(const StringView& name); From d5237715a5b14cec7321b2cc8006c5d73481a540 Mon Sep 17 00:00:00 2001 From: Olly Rybak Date: Thu, 11 May 2023 21:12:03 +1000 Subject: [PATCH 028/116] Removed unused field --- Source/Engine/Input/VirtualInput.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/Source/Engine/Input/VirtualInput.h b/Source/Engine/Input/VirtualInput.h index e8f669632..8fd4efeb4 100644 --- a/Source/Engine/Input/VirtualInput.h +++ b/Source/Engine/Input/VirtualInput.h @@ -48,9 +48,6 @@ API_STRUCT() struct ActionConfig /// API_FIELD(Attributes="EditorOrder(40)") InputGamepadIndex Gamepad; - - API_FIELD(Attributes = "EditorOrder(50)") - InputActionMode Phase; }; /// From 4b4bf833a93fb6d2d28994f1f700b54ed16a9bae Mon Sep 17 00:00:00 2001 From: Olly Rybak Date: Fri, 12 May 2023 00:25:03 +1000 Subject: [PATCH 029/116] Rename Phase > State --- Source/Engine/Input/Enums.h | 2 +- Source/Engine/Input/Input.cpp | 16 ++++++++-------- Source/Engine/Input/Input.h | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Source/Engine/Input/Enums.h b/Source/Engine/Input/Enums.h index 4c96f715f..82498f500 100644 --- a/Source/Engine/Input/Enums.h +++ b/Source/Engine/Input/Enums.h @@ -266,7 +266,7 @@ API_ENUM() enum class InputActionMode /// /// The input action event phases. /// -API_ENUM() enum class InputActionPhase +API_ENUM() enum class InputActionState { /// /// The key/button is not assigned. diff --git a/Source/Engine/Input/Input.cpp b/Source/Engine/Input/Input.cpp index 2f45a4a16..b22efa4fc 100644 --- a/Source/Engine/Input/Input.cpp +++ b/Source/Engine/Input/Input.cpp @@ -29,13 +29,13 @@ struct ActionData { bool Active; uint64 FrameIndex; - InputActionPhase Phase; + InputActionState Phase; ActionData() { Active = false; FrameIndex = 0; - Phase = InputActionPhase::Waiting; + Phase = InputActionState::Waiting; } }; @@ -599,14 +599,14 @@ bool Input::GetAction(const StringView& name) return e ? e->Active : false; } -InputActionPhase Input::GetActionPhase(const StringView& name) +InputActionState Input::GetActionState(const StringView& name) { const auto e = Actions.TryGet(name); if (e != nullptr) { return e->Phase; } - return InputActionPhase::None; + return InputActionState::None; } float Input::GetAxis(const StringView& name) @@ -818,7 +818,7 @@ void InputService::Update() ActionData& data = Actions[name]; data.Active = false; - data.Phase = InputActionPhase::Waiting; + data.Phase = InputActionState::Waiting; // Mark as updated in this frame data.FrameIndex = frame; @@ -845,15 +845,15 @@ void InputService::Update() if (Input::GetKeyDown(config.Key) || Input::GetMouseButtonDown(config.MouseButton) || Input::GetGamepadButtonDown(config.Gamepad, config.GamepadButton)) { - data.Phase = InputActionPhase::Press; + data.Phase = InputActionState::Press; } else if (Input::GetKey(config.Key) || Input::GetMouseButton(config.MouseButton) || Input::GetGamepadButton(config.Gamepad, config.GamepadButton)) { - data.Phase = InputActionPhase::Pressing; + data.Phase = InputActionState::Pressing; } else if (Input::GetKeyUp(config.Key) || Input::GetMouseButtonUp(config.MouseButton) || Input::GetGamepadButtonUp(config.Gamepad, config.GamepadButton)) { - data.Phase = InputActionPhase::Release; + data.Phase = InputActionState::Release; } data.Active |= isActive; diff --git a/Source/Engine/Input/Input.h b/Source/Engine/Input/Input.h index 35f3dac32..1fcb352ea 100644 --- a/Source/Engine/Input/Input.h +++ b/Source/Engine/Input/Input.h @@ -315,7 +315,7 @@ public: /// The action name. /// A InputActionPhase determining the current phase of the Action (e.g If it was just pressed, is being held or just released). /// - API_FUNCTION() static InputActionPhase GetActionPhase(const StringView& name); + API_FUNCTION() static InputActionState GetActionState(const StringView& name); /// /// Gets the value of the virtual axis identified by name. Use to get the current config. From f94ae3f3fd7095767f48c3c7d14b6cafedd06616 Mon Sep 17 00:00:00 2001 From: Menotdan <32620310+Menotdan@users.noreply.github.com> Date: Thu, 11 May 2023 13:20:43 -0400 Subject: [PATCH 030/116] Add mouse hook to the color picker. --- .../Editor/GUI/Dialogs/ColorPickerDialog.cs | 5 ++- .../Engine/Platform/Base/ScreenUtilsBase.cpp | 3 ++ Source/Engine/Platform/Base/ScreenUtilsBase.h | 5 +++ .../Platform/Windows/WindowsScreenUtils.cpp | 41 ++++++++++++++++++- .../Platform/Windows/WindowsScreenUtils.h | 1 + 5 files changed, 51 insertions(+), 4 deletions(-) diff --git a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs index 1b4b8e740..c5842a41f 100644 --- a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs +++ b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs @@ -224,14 +224,15 @@ namespace FlaxEditor.GUI.Dialogs private void OnEyedropStart() { _activeEyedropper = true; + ScreenUtils.BlockAndReadMouse(); } private void UpdateEyedrop() { Color32 pixelColor = GetEyedropColor(); - Editor.Log(string.Format("Pixel Color: ({0}, {1}, {2})", pixelColor.R, pixelColor.G, pixelColor.B)); - SelectedColor = new Color(pixelColor.R, pixelColor.G, pixelColor.B); + + } private void OnRGBAChanged() diff --git a/Source/Engine/Platform/Base/ScreenUtilsBase.cpp b/Source/Engine/Platform/Base/ScreenUtilsBase.cpp index 081c65bf4..7445ee4ab 100644 --- a/Source/Engine/Platform/Base/ScreenUtilsBase.cpp +++ b/Source/Engine/Platform/Base/ScreenUtilsBase.cpp @@ -9,3 +9,6 @@ Color32 ScreenUtilsBase::GetPixelAt(int32 x, int32 y) { Int2 ScreenUtilsBase::GetScreenCursorPosition() { return { 0, 0 }; } + +void ScreenUtilsBase::BlockAndReadMouse() { +} diff --git a/Source/Engine/Platform/Base/ScreenUtilsBase.h b/Source/Engine/Platform/Base/ScreenUtilsBase.h index ddfa4f329..86a776fc1 100644 --- a/Source/Engine/Platform/Base/ScreenUtilsBase.h +++ b/Source/Engine/Platform/Base/ScreenUtilsBase.h @@ -26,4 +26,9 @@ public: /// /// Cursor position, in screen coordinates. API_FUNCTION() static Int2 GetScreenCursorPosition(); + + /// + /// Blocks mouse input and runs a callback + /// + API_FUNCTION() static void BlockAndReadMouse(); }; diff --git a/Source/Engine/Platform/Windows/WindowsScreenUtils.cpp b/Source/Engine/Platform/Windows/WindowsScreenUtils.cpp index a4c4127bd..f0ec7acf0 100644 --- a/Source/Engine/Platform/Windows/WindowsScreenUtils.cpp +++ b/Source/Engine/Platform/Windows/WindowsScreenUtils.cpp @@ -7,8 +7,10 @@ #if PLATFORM_WINDOWS #pragma comment(lib, "Gdi32.lib") #endif +#include -Color32 ScreenUtils::GetPixelAt(int32 x, int32 y) { +Color32 ScreenUtils::GetPixelAt(int32 x, int32 y) +{ HDC deviceContext = GetDC(NULL); COLORREF color = GetPixel(deviceContext, x, y); ReleaseDC(NULL, deviceContext); @@ -17,10 +19,45 @@ Color32 ScreenUtils::GetPixelAt(int32 x, int32 y) { return returnColor; } -Int2 ScreenUtils::GetScreenCursorPosition() { +Int2 ScreenUtils::GetScreenCursorPosition() +{ POINT cursorPos; GetCursorPos(&cursorPos); Int2 returnCursorPos = { cursorPos.x, cursorPos.y }; return returnCursorPos; } + +static HHOOK _mouseCallbackHook; +LRESULT CALLBACK ScreenUtilsMouseCallback( + _In_ int nCode, + _In_ WPARAM wParam, + _In_ LPARAM lParam +) +{ + LOG(Warning, "Hell lag. {0}", GetCurrentThreadId()); + if (wParam != WM_LBUTTONDOWN) { // Return as early as possible. + return CallNextHookEx(NULL, nCode, wParam, lParam); + } + + if (nCode < 0) { + return CallNextHookEx(NULL, nCode, wParam, lParam); + } + + + if (nCode >= 0 && wParam == WM_LBUTTONDOWN) { // Now try to run our code. + LOG(Warning, "Mouse callback hit. Skipping event. (hopefully)"); + UnhookWindowsHookEx(_mouseCallbackHook); + return 1; + } +} + +void ScreenUtils::BlockAndReadMouse() +{ + _mouseCallbackHook = SetWindowsHookEx(WH_MOUSE_LL, ScreenUtilsMouseCallback, NULL, NULL); + if (_mouseCallbackHook == NULL) + { + LOG(Warning, "Failed to set mouse hook."); + LOG(Warning, "Error: {0}", GetLastError()); + } +} diff --git a/Source/Engine/Platform/Windows/WindowsScreenUtils.h b/Source/Engine/Platform/Windows/WindowsScreenUtils.h index 8f03e033f..c10179eac 100644 --- a/Source/Engine/Platform/Windows/WindowsScreenUtils.h +++ b/Source/Engine/Platform/Windows/WindowsScreenUtils.h @@ -11,6 +11,7 @@ public: // [ScreenUtilsBase] static Color32 GetPixelAt(int32 x, int32 y); static Int2 GetScreenCursorPosition(); + static void BlockAndReadMouse(); }; #endif From de7c6483e0236a7a15bd4fad97c079d17a6ecccc Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Thu, 11 May 2023 21:09:33 +0300 Subject: [PATCH 031/116] Add LateFixedUpdate event for C# scripts --- Source/Engine/Level/Scene/SceneTicking.cpp | 19 +++++++++++++++++++ Source/Engine/Level/Scene/SceneTicking.h | 12 ++++++++++++ Source/Engine/Scripting/Script.cpp | 2 ++ Source/Engine/Scripting/Script.h | 1 + 4 files changed, 34 insertions(+) diff --git a/Source/Engine/Level/Scene/SceneTicking.cpp b/Source/Engine/Level/Scene/SceneTicking.cpp index 8998a4b37..7aeee452b 100644 --- a/Source/Engine/Level/Scene/SceneTicking.cpp +++ b/Source/Engine/Level/Scene/SceneTicking.cpp @@ -121,6 +121,19 @@ void SceneTicking::LateUpdateTickData::TickScripts(const Array& scripts } } +SceneTicking::LateFixedUpdateTickData::LateFixedUpdateTickData() + : TickData(64) +{ +} + +void SceneTicking::LateFixedUpdateTickData::TickScripts(const Array& scripts) +{ + for (auto* script : scripts) + { + script->OnLateFixedUpdate(); + } +} + void SceneTicking::AddScript(Script* obj) { ASSERT_LOW_LAYER(obj && obj->GetParent() && obj->GetParent()->GetScene()); @@ -130,6 +143,9 @@ void SceneTicking::AddScript(Script* obj) Update.AddScript(obj); if (obj->_tickLateUpdate) LateUpdate.AddScript(obj); + if (obj->_tickLateFixedUpdate) + LateFixedUpdate.AddScript(obj); + } void SceneTicking::RemoveScript(Script* obj) @@ -141,6 +157,8 @@ void SceneTicking::RemoveScript(Script* obj) Update.RemoveScript(obj); if (obj->_tickLateUpdate) LateUpdate.RemoveScript(obj); + if (obj->_tickLateFixedUpdate) + LateFixedUpdate.RemoveScript(obj); } void SceneTicking::Clear() @@ -148,4 +166,5 @@ void SceneTicking::Clear() FixedUpdate.Clear(); Update.Clear(); LateUpdate.Clear(); + LateFixedUpdate.Clear(); } diff --git a/Source/Engine/Level/Scene/SceneTicking.h b/Source/Engine/Level/Scene/SceneTicking.h index 230861087..7d590c8e7 100644 --- a/Source/Engine/Level/Scene/SceneTicking.h +++ b/Source/Engine/Level/Scene/SceneTicking.h @@ -109,6 +109,13 @@ public: void TickScripts(const Array& scripts) override; }; + class FLAXENGINE_API LateFixedUpdateTickData : public TickData + { + public: + LateFixedUpdateTickData(); + void TickScripts(const Array& scripts) override; + }; + public: /// /// Adds the script to scene ticking system. @@ -142,4 +149,9 @@ public: /// The late update tick function. /// LateUpdateTickData LateUpdate; + + /// + /// The late fixed update tick function. + /// + LateFixedUpdateTickData LateFixedUpdate; }; diff --git a/Source/Engine/Scripting/Script.cpp b/Source/Engine/Scripting/Script.cpp index 3f8df354c..8eaff42de 100644 --- a/Source/Engine/Scripting/Script.cpp +++ b/Source/Engine/Scripting/Script.cpp @@ -26,6 +26,7 @@ Script::Script(const SpawnParams& params) , _tickFixedUpdate(false) , _tickUpdate(false) , _tickLateUpdate(false) + , _tickLateFixedUpdate(false) , _wasStartCalled(false) , _wasEnableCalled(false) { @@ -181,6 +182,7 @@ void Script::SetupType() _tickUpdate |= type.Script.ScriptVTable[8] != nullptr; _tickLateUpdate |= type.Script.ScriptVTable[9] != nullptr; _tickFixedUpdate |= type.Script.ScriptVTable[10] != nullptr; + _tickLateFixedUpdate |= type.Script.ScriptVTable[11] != nullptr; } typeHandle = type.GetBaseType(); } diff --git a/Source/Engine/Scripting/Script.h b/Source/Engine/Scripting/Script.h index 769771f46..49cba0ec4 100644 --- a/Source/Engine/Scripting/Script.h +++ b/Source/Engine/Scripting/Script.h @@ -19,6 +19,7 @@ protected: int32 _tickFixedUpdate : 1; int32 _tickUpdate : 1; int32 _tickLateUpdate : 1; + int32 _tickLateFixedUpdate : 1; int32 _wasStartCalled : 1; int32 _wasEnableCalled : 1; #if USE_EDITOR From cbe07b842fe55a7e87c676a3cbdd36bd1af7b9e0 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Thu, 11 May 2023 22:10:26 +0300 Subject: [PATCH 032/116] Fix resolving managed scripts references in Editor --- .../Engine/Engine/NativeInterop.Unmanaged.cs | 7 +---- Source/Engine/Engine/NativeInterop.cs | 31 +++++++++++++++---- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/Source/Engine/Engine/NativeInterop.Unmanaged.cs b/Source/Engine/Engine/NativeInterop.Unmanaged.cs index ae55f1ed7..bc2af6d61 100644 --- a/Source/Engine/Engine/NativeInterop.Unmanaged.cs +++ b/Source/Engine/Engine/NativeInterop.Unmanaged.cs @@ -908,12 +908,7 @@ namespace FlaxEngine.Interop while (unloading) System.Threading.Thread.Sleep(1); -#if FLAX_EDITOR - var isCollectible = true; -#else - var isCollectible = false; -#endif - scriptingAssemblyLoadContext = new AssemblyLoadContext("Flax", isCollectible); + InitScriptingAssemblyLoadContext(); DelegateHelpers.InitMethods(); } diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs index 38af2e8fe..39adc59ab 100644 --- a/Source/Engine/Engine/NativeInterop.cs +++ b/Source/Engine/Engine/NativeInterop.cs @@ -78,6 +78,19 @@ namespace FlaxEngine.Interop return nativeLibrary; } + private static void InitScriptingAssemblyLoadContext() + { +#if FLAX_EDITOR + var isCollectible = true; +#else + var isCollectible = false; +#endif + scriptingAssemblyLoadContext = new AssemblyLoadContext("Flax", isCollectible); +#if FLAX_EDITOR + scriptingAssemblyLoadContext.Resolving += ScriptingAssemblyLoadContext_Resolving; +#endif + } + [UnmanagedCallersOnly] internal static unsafe void Init() { @@ -89,15 +102,21 @@ namespace FlaxEngine.Interop System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; System.Threading.Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture; -#if FLAX_EDITOR - var isCollectible = true; -#else - var isCollectible = false; -#endif - scriptingAssemblyLoadContext = new AssemblyLoadContext("Flax", isCollectible); + InitScriptingAssemblyLoadContext(); DelegateHelpers.InitMethods(); } + private static Assembly? ScriptingAssemblyLoadContext_Resolving(AssemblyLoadContext assemblyLoadContext, AssemblyName assemblyName) + { + // FIXME: There should be a better way to resolve the path to EditorTargetPath where the dependencies are stored + string editorTargetPath = Path.GetDirectoryName(nativeLibraryPaths.Keys.First(x => x != "FlaxEngine")); + + var assemblyPath = Path.Combine(editorTargetPath, assemblyName.Name + ".dll"); + if (File.Exists(assemblyPath)) + return assemblyLoadContext.LoadFromAssemblyPath(assemblyPath); + return null; + } + [UnmanagedCallersOnly] internal static unsafe void Exit() { From b8ed115f63097210b667fbb67ba1e43216deec5e Mon Sep 17 00:00:00 2001 From: Olly Rybak Date: Fri, 12 May 2023 09:30:35 +1000 Subject: [PATCH 033/116] More renames --- Source/Engine/Input/Input.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Source/Engine/Input/Input.cpp b/Source/Engine/Input/Input.cpp index b22efa4fc..8658b984c 100644 --- a/Source/Engine/Input/Input.cpp +++ b/Source/Engine/Input/Input.cpp @@ -29,13 +29,13 @@ struct ActionData { bool Active; uint64 FrameIndex; - InputActionState Phase; + InputActionState State; ActionData() { Active = false; FrameIndex = 0; - Phase = InputActionState::Waiting; + State = InputActionState::Waiting; } }; @@ -604,7 +604,7 @@ InputActionState Input::GetActionState(const StringView& name) const auto e = Actions.TryGet(name); if (e != nullptr) { - return e->Phase; + return e->State; } return InputActionState::None; } @@ -818,7 +818,7 @@ void InputService::Update() ActionData& data = Actions[name]; data.Active = false; - data.Phase = InputActionState::Waiting; + data.State = InputActionState::Waiting; // Mark as updated in this frame data.FrameIndex = frame; @@ -845,15 +845,15 @@ void InputService::Update() if (Input::GetKeyDown(config.Key) || Input::GetMouseButtonDown(config.MouseButton) || Input::GetGamepadButtonDown(config.Gamepad, config.GamepadButton)) { - data.Phase = InputActionState::Press; + data.State = InputActionState::Press; } else if (Input::GetKey(config.Key) || Input::GetMouseButton(config.MouseButton) || Input::GetGamepadButton(config.Gamepad, config.GamepadButton)) { - data.Phase = InputActionState::Pressing; + data.State = InputActionState::Pressing; } else if (Input::GetKeyUp(config.Key) || Input::GetMouseButtonUp(config.MouseButton) || Input::GetGamepadButtonUp(config.Gamepad, config.GamepadButton)) { - data.Phase = InputActionState::Release; + data.State = InputActionState::Release; } data.Active |= isActive; From b30f845924376b29e51308c6eb80dd0dad3d1a23 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 12 May 2023 12:22:04 +0200 Subject: [PATCH 034/116] Simplify radial light attenuation in shaders #1094 --- Content/Shaders/Lights.flax | 4 ++-- Content/Shaders/VolumetricFog.flax | 4 ++-- Source/Shaders/Lighting.hlsl | 5 ++--- Source/Shaders/LightingCommon.hlsl | 23 +++++++++++------------ Source/Shaders/VolumetricFog.shader | 11 ++++------- 5 files changed, 21 insertions(+), 26 deletions(-) diff --git a/Content/Shaders/Lights.flax b/Content/Shaders/Lights.flax index 34e8c9a8c..ea30cd9f7 100644 --- a/Content/Shaders/Lights.flax +++ b/Content/Shaders/Lights.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b405f5698304e87e6f510c49a0cea3817c9a1a00d7ba63ef4027ac49ba7cc7a4 -size 5299 +oid sha256:83cb261770aba2813f85063f987032f9914387f4011474b0fe347c42522116b4 +size 5122 diff --git a/Content/Shaders/VolumetricFog.flax b/Content/Shaders/VolumetricFog.flax index 18eddae16..eb3ca9f2c 100644 --- a/Content/Shaders/VolumetricFog.flax +++ b/Content/Shaders/VolumetricFog.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:72415ba69a685e9a739d1a08d08c6eb9efd3cab31d8cc0c949cb187a9bad19a5 -size 13841 +oid sha256:4ab2c2551ce9cce0355c92f4f6499858e04e4bf764900b305273a7d57b887596 +size 13633 diff --git a/Source/Shaders/Lighting.hlsl b/Source/Shaders/Lighting.hlsl index d4b9da107..4b909c024 100644 --- a/Source/Shaders/Lighting.hlsl +++ b/Source/Shaders/Lighting.hlsl @@ -129,9 +129,8 @@ float4 GetLighting(float3 viewPos, LightData lightData, GBufferSample gBuffer, f toLight = lightData.Position - gBuffer.WorldPos; float distanceSqr = dot(toLight, toLight); L = toLight * rsqrt(distanceSqr); - float distanceAttenuation = 1, lightRadiusMask = 1, spotAttenuation = 1; - GetRadialLightAttenuation(lightData, isSpotLight, N, distanceSqr, 1, toLight, L, NoL, distanceAttenuation, lightRadiusMask, spotAttenuation); - float attenuation = distanceAttenuation * lightRadiusMask * spotAttenuation; + float attenuation = 1; + GetRadialLightAttenuation(lightData, isSpotLight, N, distanceSqr, 1, toLight, L, NoL, attenuation); shadow.SurfaceShadow *= attenuation; shadow.TransmissionShadow *= attenuation; } diff --git a/Source/Shaders/LightingCommon.hlsl b/Source/Shaders/LightingCommon.hlsl index bb7a6ce7e..3479157b5 100644 --- a/Source/Shaders/LightingCommon.hlsl +++ b/Source/Shaders/LightingCommon.hlsl @@ -63,10 +63,9 @@ void GetRadialLightAttenuation( float3 toLight, float3 L, inout float NoL, - inout float distanceAttenuation, - inout float lightRadiusMask, - inout float spotAttenuation) + inout float attenuation) { + // Distance attenuation if (lightData.InverseSquared) { BRANCH @@ -77,29 +76,31 @@ void GetRadialLightAttenuation( float3 l1 = toLight + 0.5 * l01; float lengthL0 = length(l0); float lengthL1 = length(l1); - distanceAttenuation = rcp((lengthL0 * lengthL1 + dot(l0, l1)) * 0.5 + distanceBiasSqr); + attenuation = rcp((lengthL0 * lengthL1 + dot(l0, l1)) * 0.5 + distanceBiasSqr); NoL = saturate(0.5 * (dot(N, l0) / lengthL0 + dot(N, l1) / lengthL1)); } else { - distanceAttenuation = rcp(distanceSqr + distanceBiasSqr); + attenuation = rcp(distanceSqr + distanceBiasSqr); NoL = saturate(dot(N, L)); } - lightRadiusMask = Square(saturate(1 - Square(distanceSqr * Square(lightData.RadiusInv)))); + attenuation *= Square(saturate(1 - Square(distanceSqr * Square(lightData.RadiusInv)))); } else { - distanceAttenuation = 1; + attenuation = 1; NoL = saturate(dot(N, L)); float3 worldLightVector = toLight * lightData.RadiusInv; float t = dot(worldLightVector, worldLightVector); - lightRadiusMask = pow(1.0f - saturate(t), lightData.FalloffExponent); + attenuation *= pow(1.0f - saturate(t), lightData.FalloffExponent); } + // Spot mask attenuation if (isSpotLight) { - // SpotAngles.x is CosOuterCone, SpotAngles.y is InvCosConeDifference - spotAttenuation = Square(saturate((dot(normalize(-L), lightData.Direction) - lightData.SpotAngles.x) * lightData.SpotAngles.y)); + float cosOuterCone = lightData.SpotAngles.x; + float invCosConeDifference = lightData.SpotAngles.y; + attenuation *= Square(saturate((dot(normalize(-L), lightData.Direction) - cosOuterCone) * invCosConeDifference)); } } @@ -107,7 +108,6 @@ void GetRadialLightAttenuation( float AreaLightSpecular(LightData lightData, float roughness, inout float3 toLight, inout float3 L, float3 V, half3 N) { float energy = 1; - float m = roughness * roughness; float3 r = reflect(-V, N); float invDistToLight = rsqrt(dot(toLight, toLight)); @@ -137,7 +137,6 @@ float AreaLightSpecular(LightData lightData, float roughness, inout float3 toLig } L = normalize(toLight); - return energy; } diff --git a/Source/Shaders/VolumetricFog.shader b/Source/Shaders/VolumetricFog.shader index 2f5103736..b97fede24 100644 --- a/Source/Shaders/VolumetricFog.shader +++ b/Source/Shaders/VolumetricFog.shader @@ -205,9 +205,6 @@ float4 PS_InjectLight(Quad_GS2PS input) : SV_Target0 uint samplesCount = historyAlpha < 0.001f ? MissedHistorySamplesCount : 1; float NoL = 0; - float distanceAttenuation = 1; - float lightRadiusMask = 1; - float spotAttenuation = 1; bool isSpotLight = LocalLight.SpotAngles.x > -2.0f; float4 scattering = 0; for (uint sampleIndex = 0; sampleIndex < samplesCount; sampleIndex++) @@ -224,19 +221,19 @@ float4 PS_InjectLight(Quad_GS2PS input) : SV_Target0 float3 L = toLight * rsqrt(distanceSqr); // Calculate the light attenuation - GetRadialLightAttenuation(LocalLight, isSpotLight, float3(0, 0, 1), distanceSqr, distanceBias * distanceBias, toLight, L, NoL, distanceAttenuation, lightRadiusMask, spotAttenuation); - float combinedAttenuation = distanceAttenuation * lightRadiusMask * spotAttenuation; + float attenuation = 1; + GetRadialLightAttenuation(LocalLight, isSpotLight, float3(0, 0, 1), distanceSqr, distanceBias * distanceBias, toLight, L, NoL, attenuation); // Peek the shadow float shadowFactor = 1.0f; #if USE_SHADOW - if (combinedAttenuation > 0) + if (attenuation > 0) { shadowFactor = ComputeVolumeShadowing(positionWS, isSpotLight); } #endif - scattering.rgb += LocalLight.Color * (GetPhase(PhaseG, dot(L, -cameraVector)) * combinedAttenuation * shadowFactor * LocalLightScatteringIntensity); + scattering.rgb += LocalLight.Color * (GetPhase(PhaseG, dot(L, -cameraVector)) * attenuation * shadowFactor * LocalLightScatteringIntensity); } scattering.rgb /= (float)samplesCount; From 5c6de69e0ef556f71fb3500591fd90a4a8490246 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 12 May 2023 12:22:39 +0200 Subject: [PATCH 035/116] Hide `FallOffExponent` when light is using inverse squared falloff #1094 --- Source/Engine/Level/Actors/PointLight.h | 5 ++--- Source/Engine/Level/Actors/SpotLight.h | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Level/Actors/PointLight.h b/Source/Engine/Level/Actors/PointLight.h index 43f0e7d06..d3e170a03 100644 --- a/Source/Engine/Level/Actors/PointLight.h +++ b/Source/Engine/Level/Actors/PointLight.h @@ -33,7 +33,7 @@ public: /// /// Controls the radial falloff of light when UseInverseSquaredFalloff is disabled. /// - API_FIELD(Attributes="EditorOrder(13), DefaultValue(8.0f), EditorDisplay(\"Light\"), Limit(2, 16, 0.01f)") + API_FIELD(Attributes="EditorOrder(13), DefaultValue(8.0f), EditorDisplay(\"Light\"), Limit(2, 16, 0.01f), VisibleIf(nameof(UseInverseSquaredFalloff), true)") float FallOffExponent = 8.0f; /// @@ -62,9 +62,8 @@ public: public: /// - /// Computes light brightness value + /// Computes light brightness value. /// - /// Brightness float ComputeBrightness() const; /// diff --git a/Source/Engine/Level/Actors/SpotLight.h b/Source/Engine/Level/Actors/SpotLight.h index 547a54e3c..6bb0daf8d 100644 --- a/Source/Engine/Level/Actors/SpotLight.h +++ b/Source/Engine/Level/Actors/SpotLight.h @@ -32,7 +32,7 @@ public: /// /// Controls the radial falloff of light when UseInverseSquaredFalloff is disabled. /// - API_FIELD(Attributes="EditorOrder(13), DefaultValue(8.0f), EditorDisplay(\"Light\"), Limit(2, 16, 0.01f)") + API_FIELD(Attributes="EditorOrder(13), DefaultValue(8.0f), EditorDisplay(\"Light\"), Limit(2, 16, 0.01f), VisibleIf(nameof(UseInverseSquaredFalloff), true)") float FallOffExponent = 8.0f; /// From 42c042c249c384223b17b0fe523197e4594eb195 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 12 May 2023 13:53:31 +0200 Subject: [PATCH 036/116] Fix text words wrapping deadlock #1093 --- Source/Engine/Render2D/Font.cpp | 92 ++++++++++----------------------- 1 file changed, 26 insertions(+), 66 deletions(-) diff --git a/Source/Engine/Render2D/Font.cpp b/Source/Engine/Render2D/Font.cpp index b026e2d80..c36f40515 100644 --- a/Source/Engine/Render2D/Font.cpp +++ b/Source/Engine/Render2D/Font.cpp @@ -118,16 +118,10 @@ void Font::ProcessText(const StringView& text, Array& outputLines tmpLine.FirstCharIndex = 0; tmpLine.LastCharIndex = -1; - int32 lastWhitespaceIndex = INVALID_INDEX; - float lastWhitespaceX = 0; + int32 lastWrapCharIndex = INVALID_INDEX; + float lastWrapCharX = 0; bool lastMoveLine = false; - int32 lastUpperIndex = INVALID_INDEX; - float lastUpperX = 0; - - int32 lastUnderscoreIndex = INVALID_INDEX; - float lastUnderscoreX = 0; - // Process each character to split text into single lines for (int32 currentIndex = 0; currentIndex < textLength;) { @@ -137,30 +131,14 @@ void Font::ProcessText(const StringView& text, Array& outputLines // Cache current character const Char currentChar = text[currentIndex]; - - // Check if character is a whitespace const bool isWhitespace = StringUtils::IsWhitespace(currentChar); - if (isWhitespace) - { - // Cache line break point - lastWhitespaceIndex = currentIndex; - lastWhitespaceX = cursorX; - } - // Check if character is an upper case letter - const bool isUpper = StringUtils::IsUpper(currentChar); - if (isUpper && currentIndex != 0) + // Check if character can wrap words + const bool isWrapChar = !StringUtils::IsAlnum(currentChar) || isWhitespace || StringUtils::IsUpper(currentChar); + if (isWrapChar && currentIndex != 0) { - lastUpperIndex = currentIndex; - lastUpperX = cursorX; - } - - // Check if character is an underscore - const bool isUnderscore = currentChar == '_'; - if (isUnderscore) - { - lastUnderscoreIndex = currentIndex; - lastUnderscoreX = cursorX; + lastWrapCharIndex = currentIndex; + lastWrapCharX = cursorX; } // Check if it's a newline character @@ -197,41 +175,31 @@ void Font::ProcessText(const StringView& text, Array& outputLines } else if (layout.TextWrapping == TextWrapping::WrapWords) { - // Move line but back to the last after-whitespace character - moveLine = true; - if (lastWhitespaceIndex != INVALID_INDEX) - { - cursorX = lastWhitespaceX; - tmpLine.LastCharIndex = lastWhitespaceIndex - 1; - nextCharIndex = currentIndex = lastWhitespaceIndex + 1; - } - else if (lastUpperIndex != INVALID_INDEX) + if (lastWrapCharIndex != INVALID_INDEX) { // Skip moving twice for the same character - if (outputLines.HasItems() && outputLines.Last().LastCharIndex == lastUpperIndex - 1) + if (outputLines.HasItems() && outputLines.Last().LastCharIndex == lastWrapCharIndex - 1) { currentIndex = nextCharIndex; lastMoveLine = moveLine; continue; } - cursorX = lastUpperX; - tmpLine.LastCharIndex = lastUpperIndex - 1; - nextCharIndex = currentIndex = lastUpperIndex; - } - else if (lastUnderscoreIndex != INVALID_INDEX) - { - cursorX = lastUnderscoreX; - tmpLine.LastCharIndex = lastUnderscoreIndex - 2; - nextCharIndex = currentIndex = lastUnderscoreIndex + 1; - } - else - { - nextCharIndex = currentIndex; - - // Skip moving twice for the same character - if (lastMoveLine) - break; + // Move line + const Char wrapChar = text[lastWrapCharIndex]; + moveLine = true; + cursorX = lastWrapCharX; + if (StringUtils::IsWhitespace(wrapChar)) + { + // Skip whitespaces + tmpLine.LastCharIndex = lastWrapCharIndex - 1; + nextCharIndex = currentIndex = lastWrapCharIndex + 1; + } + else + { + tmpLine.LastCharIndex = lastWrapCharIndex - 1; + nextCharIndex = currentIndex = lastWrapCharIndex; + } } } else if (layout.TextWrapping == TextWrapping::WrapChars) @@ -260,16 +228,8 @@ void Font::ProcessText(const StringView& text, Array& outputLines tmpLine.FirstCharIndex = currentIndex; tmpLine.LastCharIndex = currentIndex - 1; cursorX = 0; - - lastWhitespaceIndex = INVALID_INDEX; - lastWhitespaceX = 0; - - lastUpperIndex = INVALID_INDEX; - lastUpperX = 0; - - lastUnderscoreIndex = INVALID_INDEX; - lastUnderscoreX = 0; - + lastWrapCharIndex = INVALID_INDEX; + lastWrapCharX = 0; previous.IsValid = false; } From 92b155dbb316cbc0409f1261997f9d021faadce9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 12 May 2023 13:58:39 +0200 Subject: [PATCH 037/116] Fix creating C# scripts in Editor with private `ctor` method #1018 --- Source/Editor/Scripting/ScriptType.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Scripting/ScriptType.cs b/Source/Editor/Scripting/ScriptType.cs index aa0ca5655..d174b7dff 100644 --- a/Source/Editor/Scripting/ScriptType.cs +++ b/Source/Editor/Scripting/ScriptType.cs @@ -832,7 +832,7 @@ namespace FlaxEditor.Scripting get { if (_managed != null) - return _managed.GetConstructor(Type.EmptyTypes) != null; + return _managed.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null) != null; return _custom?.CanCreateInstance ?? false; } } @@ -892,7 +892,12 @@ namespace FlaxEditor.Scripting public object CreateInstance() { if (_managed != null) - return Activator.CreateInstance(_managed); + { + var ctor = _managed.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null); + object value = RuntimeHelpers.GetUninitializedObject(_managed); + ctor.Invoke(value, null); + return value; + } return _custom.CreateInstance(); } From d7327d62e2889fb3a1f870d8270a2b8d15e257f7 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 12 May 2023 13:53:31 +0200 Subject: [PATCH 038/116] Fix text words wrapping deadlock #1093 --- Source/Engine/Render2D/Font.cpp | 92 ++++++++++----------------------- 1 file changed, 26 insertions(+), 66 deletions(-) diff --git a/Source/Engine/Render2D/Font.cpp b/Source/Engine/Render2D/Font.cpp index b026e2d80..c36f40515 100644 --- a/Source/Engine/Render2D/Font.cpp +++ b/Source/Engine/Render2D/Font.cpp @@ -118,16 +118,10 @@ void Font::ProcessText(const StringView& text, Array& outputLines tmpLine.FirstCharIndex = 0; tmpLine.LastCharIndex = -1; - int32 lastWhitespaceIndex = INVALID_INDEX; - float lastWhitespaceX = 0; + int32 lastWrapCharIndex = INVALID_INDEX; + float lastWrapCharX = 0; bool lastMoveLine = false; - int32 lastUpperIndex = INVALID_INDEX; - float lastUpperX = 0; - - int32 lastUnderscoreIndex = INVALID_INDEX; - float lastUnderscoreX = 0; - // Process each character to split text into single lines for (int32 currentIndex = 0; currentIndex < textLength;) { @@ -137,30 +131,14 @@ void Font::ProcessText(const StringView& text, Array& outputLines // Cache current character const Char currentChar = text[currentIndex]; - - // Check if character is a whitespace const bool isWhitespace = StringUtils::IsWhitespace(currentChar); - if (isWhitespace) - { - // Cache line break point - lastWhitespaceIndex = currentIndex; - lastWhitespaceX = cursorX; - } - // Check if character is an upper case letter - const bool isUpper = StringUtils::IsUpper(currentChar); - if (isUpper && currentIndex != 0) + // Check if character can wrap words + const bool isWrapChar = !StringUtils::IsAlnum(currentChar) || isWhitespace || StringUtils::IsUpper(currentChar); + if (isWrapChar && currentIndex != 0) { - lastUpperIndex = currentIndex; - lastUpperX = cursorX; - } - - // Check if character is an underscore - const bool isUnderscore = currentChar == '_'; - if (isUnderscore) - { - lastUnderscoreIndex = currentIndex; - lastUnderscoreX = cursorX; + lastWrapCharIndex = currentIndex; + lastWrapCharX = cursorX; } // Check if it's a newline character @@ -197,41 +175,31 @@ void Font::ProcessText(const StringView& text, Array& outputLines } else if (layout.TextWrapping == TextWrapping::WrapWords) { - // Move line but back to the last after-whitespace character - moveLine = true; - if (lastWhitespaceIndex != INVALID_INDEX) - { - cursorX = lastWhitespaceX; - tmpLine.LastCharIndex = lastWhitespaceIndex - 1; - nextCharIndex = currentIndex = lastWhitespaceIndex + 1; - } - else if (lastUpperIndex != INVALID_INDEX) + if (lastWrapCharIndex != INVALID_INDEX) { // Skip moving twice for the same character - if (outputLines.HasItems() && outputLines.Last().LastCharIndex == lastUpperIndex - 1) + if (outputLines.HasItems() && outputLines.Last().LastCharIndex == lastWrapCharIndex - 1) { currentIndex = nextCharIndex; lastMoveLine = moveLine; continue; } - cursorX = lastUpperX; - tmpLine.LastCharIndex = lastUpperIndex - 1; - nextCharIndex = currentIndex = lastUpperIndex; - } - else if (lastUnderscoreIndex != INVALID_INDEX) - { - cursorX = lastUnderscoreX; - tmpLine.LastCharIndex = lastUnderscoreIndex - 2; - nextCharIndex = currentIndex = lastUnderscoreIndex + 1; - } - else - { - nextCharIndex = currentIndex; - - // Skip moving twice for the same character - if (lastMoveLine) - break; + // Move line + const Char wrapChar = text[lastWrapCharIndex]; + moveLine = true; + cursorX = lastWrapCharX; + if (StringUtils::IsWhitespace(wrapChar)) + { + // Skip whitespaces + tmpLine.LastCharIndex = lastWrapCharIndex - 1; + nextCharIndex = currentIndex = lastWrapCharIndex + 1; + } + else + { + tmpLine.LastCharIndex = lastWrapCharIndex - 1; + nextCharIndex = currentIndex = lastWrapCharIndex; + } } } else if (layout.TextWrapping == TextWrapping::WrapChars) @@ -260,16 +228,8 @@ void Font::ProcessText(const StringView& text, Array& outputLines tmpLine.FirstCharIndex = currentIndex; tmpLine.LastCharIndex = currentIndex - 1; cursorX = 0; - - lastWhitespaceIndex = INVALID_INDEX; - lastWhitespaceX = 0; - - lastUpperIndex = INVALID_INDEX; - lastUpperX = 0; - - lastUnderscoreIndex = INVALID_INDEX; - lastUnderscoreX = 0; - + lastWrapCharIndex = INVALID_INDEX; + lastWrapCharX = 0; previous.IsValid = false; } From b5117af4b8fad5a76b9bb83aee86955f6c03244e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 12 May 2023 14:19:40 +0200 Subject: [PATCH 039/116] Format code --- Source/Engine/Input/Input.cpp | 4 ++-- Source/Engine/UI/GUI/Common/Button.cs | 8 ++++---- Source/Engine/UI/GUI/Common/CheckBox.cs | 2 +- Source/Engine/UI/GUI/Common/TextBoxBase.cs | 2 +- Source/Engine/UI/GUI/Panels/DropPanel.cs | 5 ++--- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/Source/Engine/Input/Input.cpp b/Source/Engine/Input/Input.cpp index 8658b984c..1fa7454ea 100644 --- a/Source/Engine/Input/Input.cpp +++ b/Source/Engine/Input/Input.cpp @@ -25,7 +25,7 @@ struct AxisEvaluation bool Used; }; -struct ActionData +struct ActionData { bool Active; uint64 FrameIndex; @@ -602,7 +602,7 @@ bool Input::GetAction(const StringView& name) InputActionState Input::GetActionState(const StringView& name) { const auto e = Actions.TryGet(name); - if (e != nullptr) + if (e != nullptr) { return e->State; } diff --git a/Source/Engine/UI/GUI/Common/Button.cs b/Source/Engine/UI/GUI/Common/Button.cs index 41ea3a0d4..e3bee0017 100644 --- a/Source/Engine/UI/GUI/Common/Button.cs +++ b/Source/Engine/UI/GUI/Common/Button.cs @@ -60,7 +60,7 @@ namespace FlaxEngine.GUI /// [EditorDisplay("Text Style"), EditorOrder(2020)] public Color TextColor; - + /// /// Gets or sets the brush used for background drawing. /// @@ -78,13 +78,13 @@ namespace FlaxEngine.GUI /// [EditorDisplay("Background Style"), EditorOrder(2002)] public Color BackgroundColorSelected { get; set; } - + /// /// Gets or sets the color of the border. /// [EditorDisplay("Border Style"), EditorOrder(2010), ExpandGroups] public Color BorderColor { get; set; } - + /// /// Gets or sets the border color when button is highlighted. /// @@ -209,7 +209,7 @@ namespace FlaxEngine.GUI public override void ClearState() { base.ClearState(); - + if (_isPressed) OnPressEnd(); } diff --git a/Source/Engine/UI/GUI/Common/CheckBox.cs b/Source/Engine/UI/GUI/Common/CheckBox.cs index c5a6e3bb3..939708f36 100644 --- a/Source/Engine/UI/GUI/Common/CheckBox.cs +++ b/Source/Engine/UI/GUI/Common/CheckBox.cs @@ -119,7 +119,7 @@ namespace FlaxEngine.GUI /// [EditorDisplay("Border Style"), EditorOrder(2011)] public Color BorderColorHighlighted { get; set; } - + /// /// Gets or sets the color of the checkbox icon. /// diff --git a/Source/Engine/UI/GUI/Common/TextBoxBase.cs b/Source/Engine/UI/GUI/Common/TextBoxBase.cs index 9619f98bd..d94ff8854 100644 --- a/Source/Engine/UI/GUI/Common/TextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/TextBoxBase.cs @@ -1197,7 +1197,7 @@ namespace FlaxEngine.GUI { SetSelection(hitPos); } - + if (Cursor == CursorType.Default && _changeCursor) Cursor = CursorType.IBeam; diff --git a/Source/Engine/UI/GUI/Panels/DropPanel.cs b/Source/Engine/UI/GUI/Panels/DropPanel.cs index 9662f61b9..d650d6205 100644 --- a/Source/Engine/UI/GUI/Panels/DropPanel.cs +++ b/Source/Engine/UI/GUI/Panels/DropPanel.cs @@ -94,7 +94,7 @@ namespace FlaxEngine.GUI } } } - + /// /// Gets or sets a value indicating whether enable drop down icon drawing. /// @@ -376,7 +376,6 @@ namespace FlaxEngine.GUI Render2D.DrawText(HeaderTextFont.GetFont(), HeaderTextMaterial, HeaderText, textRect, textColor, TextAlignment.Near, TextAlignment.Center); - if (!_isClosed && EnableContainmentLines) { Color lineColor = Style.Current.ForegroundGrey - new Color(0, 0, 0, 100); @@ -385,7 +384,7 @@ namespace FlaxEngine.GUI Render2D.DrawLine(new Float2(1, Height), new Float2(Width, Height), lineColor, lineThickness); Render2D.DrawLine(new Float2(Width, HeaderHeight), new Float2(Width, Height), lineColor, lineThickness); } - + // Children DrawChildren(); } From a8f670a9c9246d6253daf53ae9ec0191cbd31f34 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 12 May 2023 14:39:25 +0200 Subject: [PATCH 040/116] Simplify changes from #924 and properly update after unpausing effect --- Source/Engine/Particles/ParticleEffect.cpp | 31 ++++++++-------------- Source/Engine/Particles/ParticleEffect.h | 4 +-- 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/Source/Engine/Particles/ParticleEffect.cpp b/Source/Engine/Particles/ParticleEffect.cpp index 651ff719b..18bcbdd54 100644 --- a/Source/Engine/Particles/ParticleEffect.cpp +++ b/Source/Engine/Particles/ParticleEffect.cpp @@ -280,26 +280,18 @@ void ParticleEffect::UpdateSimulation(bool singleFrame) Particles::UpdateEffect(this); } -void ParticleEffect::Play(bool reset) +void ParticleEffect::Play() { - _play = true; _isPlaying = true; - - if (reset) - ResetSimulation(); - else - UpdateSimulation(true); } void ParticleEffect::Pause() { - _play = false; _isPlaying = false; } void ParticleEffect::Stop() { - _play = false; _isPlaying = false; ResetSimulation(); } @@ -424,9 +416,13 @@ void ParticleEffect::SetParametersOverrides(const Array& valu void ParticleEffect::Update() { - if (!_play) + if (!_isPlaying) + { + // Move update timer forward while paused for correct delta time after unpause + Instance.LastUpdateTime = (UseTimeScale ? Time::Update.Time : Time::Update.UnscaledTime).GetTotalSeconds(); return; - + } + // Skip if off-screen if (!UpdateWhenOffscreen && _lastMinDstSqr >= MAX_Real) return; @@ -448,16 +444,10 @@ void ParticleEffect::Update() void ParticleEffect::UpdateExecuteInEditor() { + // Auto-play in Editor if (!Editor::IsPlayMode) { - // Always Play in editor while not playing. - // Could be useful to have a GUI to change this state - if (!_play) - { - _play = true; - _isPlaying = true; - } - + _isPlaying = true; Update(); } } @@ -632,6 +622,7 @@ void ParticleEffect::Deserialize(DeserializeStream& stream, ISerializeModifier* const auto overridesMember = stream.FindMember("Overrides"); if (overridesMember != stream.MemberEnd()) { + // [Deprecated on 25.11.2018, expires on 25.11.2022] if (modifier->EngineBuild < 6197) { const auto& overrides = overridesMember->value; @@ -752,7 +743,7 @@ void ParticleEffect::OnEnable() if (PlayOnStart) Play(); - + // Base Actor::OnEnable(); } diff --git a/Source/Engine/Particles/ParticleEffect.h b/Source/Engine/Particles/ParticleEffect.h index 5a4bae771..bfee23a3b 100644 --- a/Source/Engine/Particles/ParticleEffect.h +++ b/Source/Engine/Particles/ParticleEffect.h @@ -184,7 +184,6 @@ private: uint32 _parametersVersion = 0; // Version number for _parameters to be in sync with Instance.ParametersVersion Array _parameters; // Cached for scripting API Array _parametersOverrides; // Cached parameter modifications to be applied to the parameters - bool _play = false; bool _isPlaying = false; public: @@ -353,8 +352,7 @@ public: /// /// Plays the simulation. /// - /// /// If true, the simulation will be reset - API_FUNCTION() void Play(bool reset = true); + API_FUNCTION() void Play(); /// /// Pauses the simulation. From eab14e28da9922d2bcf1f86238209bb441b49dc7 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 12 May 2023 14:39:57 +0200 Subject: [PATCH 041/116] Add play/pause/stop buttons to particle effect editor while in play-mode --- .../Dedicated/ParticleEffectEditor.cs | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/Source/Editor/CustomEditors/Dedicated/ParticleEffectEditor.cs b/Source/Editor/CustomEditors/Dedicated/ParticleEffectEditor.cs index 75fec4eba..ec71348cf 100644 --- a/Source/Editor/CustomEditors/Dedicated/ParticleEffectEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ParticleEffectEditor.cs @@ -1,8 +1,10 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. +using System; using System.Linq; using FlaxEditor.Surface; using FlaxEngine; +using FlaxEngine.GUI; namespace FlaxEditor.CustomEditors.Dedicated { @@ -13,6 +15,7 @@ namespace FlaxEditor.CustomEditors.Dedicated [CustomEditor(typeof(ParticleEffect)), DefaultEditor] public class ParticleEffectEditor : ActorEditor { + private Label _infoLabel; private bool _isValid; private bool _isActive; private uint _parametersVersion; @@ -48,6 +51,15 @@ namespace FlaxEditor.CustomEditors.Dedicated return null; } + private void Foreach(Action func) + { + foreach (var value in Values) + { + if (value is ParticleEffect player) + func(player); + } + } + /// public override void Initialize(LayoutElementsContainer layout) { @@ -60,6 +72,26 @@ namespace FlaxEditor.CustomEditors.Dedicated _parametersVersion = effect.ParametersVersion; _isActive = effect.IsActive; + // Show playback options during simulation + if (Editor.IsPlayMode) + { + var playbackGroup = layout.Group("Playback"); + playbackGroup.Panel.Open(); + + _infoLabel = playbackGroup.Label(string.Empty).Label; + _infoLabel.AutoHeight = true; + + var grid = playbackGroup.CustomContainer(); + var gridControl = grid.CustomControl; + gridControl.ClipChildren = false; + gridControl.Height = Button.DefaultHeight; + gridControl.SlotsHorizontally = 3; + gridControl.SlotsVertically = 1; + grid.Button("Play").Button.Clicked += () => Foreach(x => x.Play()); + grid.Button("Pause").Button.Clicked += () => Foreach(x => x.Pause()); + grid.Button("Stop").Button.Clicked += () => Foreach(x => x.Stop()); + } + // Show all effect parameters grouped by the emitter track name var groups = layout.Group("Parameters"); groups.Panel.Open(); @@ -99,7 +131,7 @@ namespace FlaxEditor.CustomEditors.Dedicated base.RefreshRootChild(); return; } - + for (int i = 0; i < ChildrenEditors.Count; i++) { if (_isActive != effect.IsActive || _parametersVersion != effect.ParametersVersion) From b43196cd1a8ff399dc4eb65929989195859362e2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 12 May 2023 14:56:19 +0200 Subject: [PATCH 042/116] Add missing implementation for #987 --- Source/Editor/Utilities/EditorScene.cpp | 2 + Source/Engine/Level/Level.cpp | 115 +++++++-------------- Source/Engine/Level/Scene/SceneTicking.cpp | 1 - 3 files changed, 42 insertions(+), 76 deletions(-) diff --git a/Source/Editor/Utilities/EditorScene.cpp b/Source/Editor/Utilities/EditorScene.cpp index 764b79184..7060baac5 100644 --- a/Source/Editor/Utilities/EditorScene.cpp +++ b/Source/Editor/Utilities/EditorScene.cpp @@ -20,4 +20,6 @@ void EditorScene::Update() e.Call(); for (auto& e : Ticking.FixedUpdate.Ticks) e.Call(); + for (auto& e : Ticking.LateFixedUpdate.Ticks) + e.Call(); } diff --git a/Source/Engine/Level/Level.cpp b/Source/Engine/Level/Level.cpp index 9e3bc1915..124a415cd 100644 --- a/Source/Engine/Level/Level.cpp +++ b/Source/Engine/Level/Level.cpp @@ -138,6 +138,7 @@ public: void Update() override; void LateUpdate() override; void FixedUpdate() override; + void LateFixedUpdate() override; void Dispose() override; }; @@ -242,96 +243,60 @@ void LayersAndTagsSettings::Apply() } } -void LevelService::Update() -{ - PROFILE_CPU_NAMED("Level::Update"); - - ScopeLock lock(Level::ScenesLock); - auto& scenes = Level::Scenes; - - // Update all actors - if (!Time::GetGamePaused() && Level::TickEnabled) - { - for (int32 i = 0; i < scenes.Count(); i++) - { - if (scenes[i]->GetIsActive()) - scenes[i]->Ticking.Update.Tick(); - } +#define TICK_LEVEL(tickingStage, name) \ + PROFILE_CPU_NAMED(name); \ + ScopeLock lock(Level::ScenesLock); \ + auto& scenes = Level::Scenes; \ + if (!Time::GetGamePaused() && Level::TickEnabled) \ + { \ + for (int32 i = 0; i < scenes.Count(); i++) \ + { \ + if (scenes[i]->GetIsActive()) \ + scenes[i]->Ticking.tickingStage.Tick(); \ + } \ } #if USE_EDITOR - else if (!Editor::IsPlayMode) - { - // Run event for script executed in editor - for (int32 i = 0; i < scenes.Count(); i++) - { - if (scenes[i]->GetIsActive()) - scenes[i]->Ticking.Update.TickExecuteInEditor(); - } +#define TICK_LEVEL_EDITOR(tickingStage) \ + else if (!Editor::IsPlayMode) \ + { \ + for (int32 i = 0; i < scenes.Count(); i++) \ + { \ + if (scenes[i]->GetIsActive()) \ + scenes[i]->Ticking.tickingStage.TickExecuteInEditor(); \ + } \ } +#else +#define TICK_LEVEL_EDITOR(tickingStage) #endif + +void LevelService::Update() +{ + TICK_LEVEL(Update, "Level::Update") + TICK_LEVEL_EDITOR(Update) } void LevelService::LateUpdate() { - PROFILE_CPU_NAMED("Level::LateUpdate"); - - ScopeLock lock(Level::ScenesLock); - auto& scenes = Level::Scenes; - - // Update all actors - if (!Time::GetGamePaused() && Level::TickEnabled) - { - for (int32 i = 0; i < scenes.Count(); i++) - { - if (scenes[i]->GetIsActive()) - scenes[i]->Ticking.LateUpdate.Tick(); - } - } -#if USE_EDITOR - else if (!Editor::IsPlayMode) - { - // Run event for script executed in editor - for (int32 i = 0; i < scenes.Count(); i++) - { - if (scenes[i]->GetIsActive()) - scenes[i]->Ticking.LateUpdate.TickExecuteInEditor(); - } - } -#endif - - // Flush actions + TICK_LEVEL(LateUpdate, "Level::LateUpdate") + TICK_LEVEL_EDITOR(LateUpdate) flushActions(); } void LevelService::FixedUpdate() { - PROFILE_CPU_NAMED("Level::FixedUpdate"); - - ScopeLock lock(Level::ScenesLock); - auto& scenes = Level::Scenes; - - // Update all actors - if (!Time::GetGamePaused() && Level::TickEnabled) - { - for (int32 i = 0; i < scenes.Count(); i++) - { - if (scenes[i]->GetIsActive()) - scenes[i]->Ticking.FixedUpdate.Tick(); - } - } -#if USE_EDITOR - else if (!Editor::IsPlayMode) - { - // Run event for script executed in editor - for (int32 i = 0; i < scenes.Count(); i++) - { - if (scenes[i]->GetIsActive()) - scenes[i]->Ticking.FixedUpdate.TickExecuteInEditor(); - } - } -#endif + TICK_LEVEL(FixedUpdate, "Level::FixedUpdate") + TICK_LEVEL_EDITOR(FixedUpdate) } +void LevelService::LateFixedUpdate() +{ + TICK_LEVEL(LateFixedUpdate, "Level::LateFixedUpdate") + TICK_LEVEL_EDITOR(LateFixedUpdate) +} + +#undef TICK_LEVEL +#undef TICK_LEVEL_EDITOR + void LevelService::Dispose() { ScopeLock lock(_sceneActionsLocker); diff --git a/Source/Engine/Level/Scene/SceneTicking.cpp b/Source/Engine/Level/Scene/SceneTicking.cpp index 7aeee452b..235d7bbab 100644 --- a/Source/Engine/Level/Scene/SceneTicking.cpp +++ b/Source/Engine/Level/Scene/SceneTicking.cpp @@ -145,7 +145,6 @@ void SceneTicking::AddScript(Script* obj) LateUpdate.AddScript(obj); if (obj->_tickLateFixedUpdate) LateFixedUpdate.AddScript(obj); - } void SceneTicking::RemoveScript(Script* obj) From 1af69421fa5c9bb36238c215f4644044aadfee29 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 12 May 2023 18:41:58 +0200 Subject: [PATCH 043/116] Fix actor scene tree dragging regression from d813078e91062f031520ec55d8745a81b4519171 #1104 --- Source/Editor/SceneGraph/GUI/ActorTreeNode.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs index 5c87cbff6..8845c24bf 100644 --- a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs +++ b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs @@ -729,8 +729,6 @@ namespace FlaxEditor.SceneGraph.GUI { DragData data; var tree = ParentTree; - if (tree.Selection.Count == 1) - Select(); // Check if this node is selected if (tree.Selection.Contains(this)) From 2a7a07d2fc3a623b9bd7aeea259d0031a66889a4 Mon Sep 17 00:00:00 2001 From: Menotdan <32620310+Menotdan@users.noreply.github.com> Date: Mon, 15 May 2023 20:44:18 -0400 Subject: [PATCH 044/116] Add a delegate, unfortunately it doesn't work? --- .../Editor/GUI/Dialogs/ColorPickerDialog.cs | 15 +++++++--- .../Engine/Platform/Base/ScreenUtilsBase.cpp | 5 ++++ Source/Engine/Platform/Base/ScreenUtilsBase.h | 11 ++++++++ .../Platform/Windows/WindowsScreenUtils.cpp | 28 +++++++++++++++++++ .../Platform/Windows/WindowsScreenUtils.h | 8 +++++- 5 files changed, 62 insertions(+), 5 deletions(-) diff --git a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs index c5842a41f..bdef28db2 100644 --- a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs +++ b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs @@ -221,18 +221,25 @@ namespace FlaxEditor.GUI.Dialogs return pixelColor; } + private void ColorPicked(Color32 colorPicked) + { + Editor.LogWarning("Ok???"); + _activeEyedropper = false; + SelectedColor = colorPicked; + ScreenUtils.PickColorDone -= ColorPicked; + } + private void OnEyedropStart() { _activeEyedropper = true; - ScreenUtils.BlockAndReadMouse(); + ScreenUtils.PickColor(); + ScreenUtils.PickColorDone += ColorPicked; } private void UpdateEyedrop() { Color32 pixelColor = GetEyedropColor(); - SelectedColor = new Color(pixelColor.R, pixelColor.G, pixelColor.B); - - + SelectedColor = pixelColor; } private void OnRGBAChanged() diff --git a/Source/Engine/Platform/Base/ScreenUtilsBase.cpp b/Source/Engine/Platform/Base/ScreenUtilsBase.cpp index 7445ee4ab..833588336 100644 --- a/Source/Engine/Platform/Base/ScreenUtilsBase.cpp +++ b/Source/Engine/Platform/Base/ScreenUtilsBase.cpp @@ -12,3 +12,8 @@ Int2 ScreenUtilsBase::GetScreenCursorPosition() { void ScreenUtilsBase::BlockAndReadMouse() { } + +void ScreenUtilsBase::PickColor() { +} + +Delegate ScreenUtilsBase::PickColorDone; diff --git a/Source/Engine/Platform/Base/ScreenUtilsBase.h b/Source/Engine/Platform/Base/ScreenUtilsBase.h index 86a776fc1..9a4299ad6 100644 --- a/Source/Engine/Platform/Base/ScreenUtilsBase.h +++ b/Source/Engine/Platform/Base/ScreenUtilsBase.h @@ -1,6 +1,7 @@ #pragma once #include "Engine/Core/Types/BaseTypes.h" +#include "Engine/Core/Delegate.h" API_INJECT_CODE(cpp, "#include \"Engine/Platform/ScreenUtils.h\""); @@ -27,8 +28,18 @@ public: /// Cursor position, in screen coordinates. API_FUNCTION() static Int2 GetScreenCursorPosition(); + /// + /// Starts async color picking. Will return a color through ColorReturnCallback. + /// /// Blocks mouse input and runs a callback /// API_FUNCTION() static void BlockAndReadMouse(); + + /// + /// Called when PickColor() is finished. + /// + API_EVENT() static Delegate PickColorDone; }; diff --git a/Source/Engine/Platform/Windows/WindowsScreenUtils.cpp b/Source/Engine/Platform/Windows/WindowsScreenUtils.cpp index f0ec7acf0..17bcfa2a8 100644 --- a/Source/Engine/Platform/Windows/WindowsScreenUtils.cpp +++ b/Source/Engine/Platform/Windows/WindowsScreenUtils.cpp @@ -1,6 +1,7 @@ #include "WindowsScreenUtils.h" #include "Engine/Core/Math/Color32.h" #include "Engine/Core/Math/Vector2.h" +#include "Engine/Core/Delegate.h" #include @@ -8,6 +9,7 @@ #pragma comment(lib, "Gdi32.lib") #endif #include +#include Color32 ScreenUtils::GetPixelAt(int32 x, int32 y) { @@ -48,8 +50,12 @@ LRESULT CALLBACK ScreenUtilsMouseCallback( if (nCode >= 0 && wParam == WM_LBUTTONDOWN) { // Now try to run our code. LOG(Warning, "Mouse callback hit. Skipping event. (hopefully)"); UnhookWindowsHookEx(_mouseCallbackHook); + + ScreenUtils::PickSelected(); return 1; } + + return CallNextHookEx(NULL, nCode, wParam, lParam); } void ScreenUtils::BlockAndReadMouse() @@ -61,3 +67,25 @@ void ScreenUtils::BlockAndReadMouse() LOG(Warning, "Error: {0}", GetLastError()); } } + +Delegate ScreenUtils::PickColorDone; + +void ScreenUtils::Test(Color32 testVal) { + LOG(Warning, "GOT IT"); +} + +void ScreenUtils::PickSelected() { + // Push event with color. + Int2 cursorPos = ScreenUtils::GetScreenCursorPosition(); + Color32 colorPicked = ScreenUtils::GetPixelAt(cursorPos.X, cursorPos.Y); + + LOG(Warning, "REAL: {0}", PickColorDone.Count()); + PickColorDone(colorPicked); + LOG(Warning, "FAKE"); +} + +void ScreenUtils::PickColor() +{ + MCore::AttachThread(); + BlockAndReadMouse(); +} diff --git a/Source/Engine/Platform/Windows/WindowsScreenUtils.h b/Source/Engine/Platform/Windows/WindowsScreenUtils.h index c10179eac..d8cd1c9f7 100644 --- a/Source/Engine/Platform/Windows/WindowsScreenUtils.h +++ b/Source/Engine/Platform/Windows/WindowsScreenUtils.h @@ -2,8 +2,8 @@ #if PLATFORM_WINDOWS -#include "Engine/Core/Math/Color32.h" #include "Engine/Platform/Base/ScreenUtilsBase.h" +#include "Engine/Core/Math/Color32.h" class FLAXENGINE_API ScreenUtils : public ScreenUtilsBase { public: @@ -12,6 +12,12 @@ public: static Color32 GetPixelAt(int32 x, int32 y); static Int2 GetScreenCursorPosition(); static void BlockAndReadMouse(); + static void PickColor(); + static Delegate PickColorDone; + +public: + static void PickSelected(); + static void Test(Color32 testVal); }; #endif From a3ab7cd14e66456f006cc71f07ebaa2309ea2958 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 16 May 2023 11:52:49 +0200 Subject: [PATCH 045/116] Fix file lock when loading asset fails #1103 --- Source/Engine/Content/Content.cpp | 7 ++++++- Source/Engine/Content/Storage/FlaxStorage.cpp | 6 ++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Content/Content.cpp b/Source/Engine/Content/Content.cpp index e58aa458d..d57529b92 100644 --- a/Source/Engine/Content/Content.cpp +++ b/Source/Engine/Content/Content.cpp @@ -760,9 +760,14 @@ bool Content::CloneAssetFile(const StringView& dstPath, const StringView& srcPat // Change asset ID { auto storage = ContentStorageManager::GetStorage(tmpPath); + if (!storage) + { + LOG(Warning, "Cannot change asset ID."); + return true; + } FlaxStorage::Entry e; storage->GetEntry(0, e); - if (!storage || storage->ChangeAssetID(e, dstId)) + if (storage->ChangeAssetID(e, dstId)) { LOG(Warning, "Cannot change asset ID."); return true; diff --git a/Source/Engine/Content/Storage/FlaxStorage.cpp b/Source/Engine/Content/Storage/FlaxStorage.cpp index 8608a34f6..52d7c34b5 100644 --- a/Source/Engine/Content/Storage/FlaxStorage.cpp +++ b/Source/Engine/Content/Storage/FlaxStorage.cpp @@ -211,6 +211,12 @@ FlaxStorage::~FlaxStorage() CHECK(_chunksLock == 0); CHECK(_refCount == 0); ASSERT(_chunks.IsEmpty()); + +#if USE_EDITOR + // Ensure to close any outstanding file handles to prevent file locking in case it failed to load + _file.DeleteAll(); +#endif + } FlaxStorage::LockData FlaxStorage::LockSafe() From 4482fc0bcfc7242ece25be5679b5b62e127f33a2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 16 May 2023 12:13:23 +0200 Subject: [PATCH 046/116] Cleanup #1086 Reuse text for UI tooltip Remove `IconButton` and use simple button instead Cleanup code style --- .../Editors/ActorTransformEditor.cs | 17 ++++++++--------- Source/Editor/Utilities/Utils.cs | 4 +--- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs index fb7a0b29d..076b94b0a 100644 --- a/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ActorTransformEditor.cs @@ -87,23 +87,20 @@ namespace FlaxEditor.CustomEditors.Editors LinkValues = Editor.Instance.Windows.PropertiesWin.ScaleLinked; - // Add button with the link icon. - //Editor.Instance.Icons.Link32 - _linkButton = new IconButton(Editor.Instance.Icons.Link32) + // Add button with the link icon + _linkButton = new Button { + BackgroundBrush = new SpriteBrush(Editor.Instance.Icons.Link32), Parent = LinkedLabel, Width = 18, Height = 18, - AnchorPreset = AnchorPresets.TopLeft + AnchorPreset = AnchorPresets.TopLeft, }; - _linkButton.Clicked += ToggleLink; SetLinkStyle(); - var x = LinkedLabel.Text.Value.Length * 7 + 5; _linkButton.LocalX += x; _linkButton.LocalY += 1; - LinkedLabel.SetupContextMenu += (label, menu, editor) => { menu.AddSeparator(); @@ -136,9 +133,11 @@ namespace FlaxEditor.CustomEditors.Editors private void SetLinkStyle() { - Color backgroundColor = LinkValues ? FlaxEngine.GUI.Style.Current.BackgroundSelected : FlaxEngine.GUI.Style.Current.ForegroundDisabled; + var style = FlaxEngine.GUI.Style.Current; + var backgroundColor = LinkValues ? style.Foreground : style.ForegroundDisabled; _linkButton.SetColors(backgroundColor); - _linkButton.TooltipText = (LinkValues ? "Unlink" : "Link") + " values for uniform scaling."; + _linkButton.BorderColor = _linkButton.BorderColorSelected = _linkButton.BorderColorHighlighted = Color.Transparent; + _linkButton.TooltipText = LinkValues ? "Unlinks scale components from uniform scaling" : "Links scale components for uniform scaling"; } } } diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index acc0cf605..5f836e069 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -1037,9 +1037,7 @@ namespace FlaxEditor.Utilities /// The processed name path. public static string GetAssetNamePath(string path) { - var projectFolder = Globals.ProjectFolder; - if (path.StartsWith(projectFolder)) - path = path.Substring(projectFolder.Length + 1); + path = GetAssetNamePathWithExt(path); return StringUtils.GetPathWithoutExtension(path); } From 3fa9f9e9cfc9ea52e7e493dc59d6aa2139f525db Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 16 May 2023 12:14:52 +0200 Subject: [PATCH 047/116] Follow up 4482fc0bcfc7242ece25be5679b5b62e127f33a2 #1086 --- Source/Engine/UI/GUI/Common/IconButton.cs | 74 ----------------------- 1 file changed, 74 deletions(-) delete mode 100644 Source/Engine/UI/GUI/Common/IconButton.cs diff --git a/Source/Engine/UI/GUI/Common/IconButton.cs b/Source/Engine/UI/GUI/Common/IconButton.cs deleted file mode 100644 index e2652cd9f..000000000 --- a/Source/Engine/UI/GUI/Common/IconButton.cs +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. - -using System; - -namespace FlaxEngine.GUI -{ - /// - /// Button with an icon. - /// - public class IconButton : Button - { - /// - /// The sprite rendered on the button. - /// - public SpriteHandle ButtonSprite { get; set; } - - /// - /// Whether or not to hide the border of the button. - /// - public bool HideBorder = true; - - /// - /// Initializes a new instance of the class. - /// - /// The sprite used by the button. - public IconButton(SpriteHandle buttonSprite) - : this(0, 0, buttonSprite) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Position X coordinate - /// Position Y coordinate - /// The sprite used by the button. - /// Width - /// Height - /// Whether or not to hide the border. - public IconButton(float x, float y, SpriteHandle buttonSprite, float width = 120, float height = DefaultHeight, bool hideBorder = true) - : base(x, y, width, height) - { - ButtonSprite = buttonSprite; - BackgroundBrush = new SpriteBrush(ButtonSprite); - HideBorder = hideBorder; - } - - /// - /// Initializes a new instance of the class. - /// - /// Position - /// Size - /// The sprite used by the button. - public IconButton(Float2 location, Float2 size, SpriteHandle buttonSprite) - : this(location.X, location.Y, buttonSprite, size.X, size.Y) - { - } - - /// - /// Sets the colors of the button, taking into account the field.> - /// - /// The color to use. - public override void SetColors(Color color) - { - BackgroundColor = color; - BackgroundColorSelected = color.RGBMultiplied(0.8f); - BackgroundColorHighlighted = color.RGBMultiplied(1.2f); - - BorderColor = HideBorder ? Color.Transparent : color.RGBMultiplied(0.5f); - BorderColorSelected = BorderColor; - BorderColorHighlighted = BorderColor; - } - } -} From 64f3f1e9bc38125d1d457ab3d63d92a7b510b67d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 16 May 2023 13:08:25 +0200 Subject: [PATCH 048/116] Add network error log for missing network object when accessing ownership #1066 --- Source/Engine/Networking/NetworkReplicator.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Source/Engine/Networking/NetworkReplicator.cpp b/Source/Engine/Networking/NetworkReplicator.cpp index e150b1ea0..2145753fe 100644 --- a/Source/Engine/Networking/NetworkReplicator.cpp +++ b/Source/Engine/Networking/NetworkReplicator.cpp @@ -878,9 +878,16 @@ uint32 NetworkReplicator::GetObjectOwnerClientId(const ScriptingObject* obj) { if (item.HasOwnership) id = item.OwnerClientId; +#if USE_NETWORK_REPLICATOR_LOG + return id; +#else break; +#endif } } +#if USE_NETWORK_REPLICATOR_LOG + NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Failed to get ownership of unregistered network object {} ({})", obj->GetID(), obj->GetType().ToString()); +#endif } } return id; @@ -903,9 +910,16 @@ NetworkObjectRole NetworkReplicator::GetObjectRole(const ScriptingObject* obj) { if (item.HasOwnership) role = item.Role; +#if USE_NETWORK_REPLICATOR_LOG + return role; +#else break; +#endif } } +#if USE_NETWORK_REPLICATOR_LOG + NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Failed to get ownership of unregistered network object {} ({})", obj->GetID(), obj->GetType().ToString()); +#endif } } return role; From 953ae3e9bbbfd9d353bd7d2b0b390edbf784b308 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 16 May 2023 14:58:16 +0200 Subject: [PATCH 049/116] Fix hierarchical network ownership propagation to sub-objects #1066 --- .../Engine/Networking/NetworkReplicator.cpp | 86 ++++++++++++------- Source/Engine/Networking/NetworkReplicator.h | 6 +- 2 files changed, 60 insertions(+), 32 deletions(-) diff --git a/Source/Engine/Networking/NetworkReplicator.cpp b/Source/Engine/Networking/NetworkReplicator.cpp index 2145753fe..3142e54b3 100644 --- a/Source/Engine/Networking/NetworkReplicator.cpp +++ b/Source/Engine/Networking/NetworkReplicator.cpp @@ -745,7 +745,7 @@ bool NetworkReplicator::InvokeSerializer(const ScriptingTypeHandle& typeHandle, return false; } -void NetworkReplicator::AddObject(ScriptingObject* obj, ScriptingObject* parent) +void NetworkReplicator::AddObject(ScriptingObject* obj, const ScriptingObject* parent) { if (!obj || NetworkManager::IsOffline()) return; @@ -774,6 +774,19 @@ void NetworkReplicator::AddObject(ScriptingObject* obj, ScriptingObject* parent) item.OwnerClientId = NetworkManager::ServerClientId; // Server owns objects by default item.Role = NetworkManager::IsClient() ? NetworkObjectRole::Replicated : NetworkObjectRole::OwnedAuthoritative; NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Add new object {}:{}, parent {}:{}", item.ToString(), obj->GetType().ToString(), item.ParentId.ToString(), parent ? parent->GetType().ToString() : String::Empty); + for (const SpawnItem& spawnItem : SpawnQueue) + { + if (spawnItem.HasOwnership && spawnItem.HierarchicalOwnership) + { + if (IsParentOf(obj, spawnItem.Object)) + { + // Inherit ownership + item.Role = spawnItem.Role; + item.OwnerClientId = spawnItem.OwnerClientId; + break; + } + } + } Objects.Add(MoveTemp(item)); } @@ -864,7 +877,7 @@ void NetworkReplicator::DespawnObject(ScriptingObject* obj) uint32 NetworkReplicator::GetObjectOwnerClientId(const ScriptingObject* obj) { uint32 id = NetworkManager::ServerClientId; - if (obj) + if (obj && NetworkManager::IsConnected()) { ScopeLock lock(ObjectsLock); const auto it = Objects.Find(obj->GetID()); @@ -896,7 +909,7 @@ uint32 NetworkReplicator::GetObjectOwnerClientId(const ScriptingObject* obj) NetworkObjectRole NetworkReplicator::GetObjectRole(const ScriptingObject* obj) { NetworkObjectRole role = NetworkObjectRole::None; - if (obj) + if (obj && NetworkManager::IsConnected()) { ScopeLock lock(ObjectsLock); const auto it = Objects.Find(obj->GetID()); @@ -927,10 +940,11 @@ NetworkObjectRole NetworkReplicator::GetObjectRole(const ScriptingObject* obj) void NetworkReplicator::SetObjectOwnership(ScriptingObject* obj, uint32 ownerClientId, NetworkObjectRole localRole, bool hierarchical) { - if (!obj) + if (!obj || NetworkManager::IsOffline()) return; + const Guid objectId = obj->GetID(); ScopeLock lock(ObjectsLock); - const auto it = Objects.Find(obj->GetID()); + const auto it = Objects.Find(objectId); if (it == Objects.End()) { // Special case if we're just spawning this object @@ -958,31 +972,33 @@ void NetworkReplicator::SetObjectOwnership(ScriptingObject* obj, uint32 ownerCli break; } } - return; - } - auto& item = it->Item; - if (item.Object != obj) - return; - - // Check if this client is object owner - if (item.OwnerClientId == NetworkManager::LocalClientId) - { - // Check if object owner will change - if (item.OwnerClientId != ownerClientId) - { - // Change role locally - CHECK(localRole != NetworkObjectRole::OwnedAuthoritative); - item.OwnerClientId = ownerClientId; - item.LastOwnerFrame = 1; - item.Role = localRole; - SendObjectRoleMessage(item); - } } else { - // Allow to change local role of the object (except ownership) - CHECK(localRole != NetworkObjectRole::OwnedAuthoritative); - item.Role = localRole; + auto& item = it->Item; + if (item.Object != obj) + return; + + // Check if this client is object owner + if (item.OwnerClientId == NetworkManager::LocalClientId) + { + // Check if object owner will change + if (item.OwnerClientId != ownerClientId) + { + // Change role locally + CHECK(localRole != NetworkObjectRole::OwnedAuthoritative); + item.OwnerClientId = ownerClientId; + item.LastOwnerFrame = 1; + item.Role = localRole; + SendObjectRoleMessage(item); + } + } + else + { + // Allow to change local role of the object (except ownership) + CHECK(localRole != NetworkObjectRole::OwnedAuthoritative); + item.Role = localRole; + } } // Go down hierarchy @@ -990,7 +1006,7 @@ void NetworkReplicator::SetObjectOwnership(ScriptingObject* obj, uint32 ownerCli { for (auto& e : Objects) { - if (e.Item.ParentId == item.ObjectId) + if (e.Item.ParentId == objectId) SetObjectOwnership(e.Item.Object.Get(), ownerClientId, localRole, hierarchical); } } @@ -1196,9 +1212,11 @@ void NetworkInternal::NetworkReplicatorUpdate() { if (!q.HasOwnership && IsParentOf(q.Object, e.Object)) { + // Inherit ownership q.HasOwnership = true; q.Role = e.Role; q.OwnerClientId = e.OwnerClientId; + break; } } } @@ -1637,7 +1655,7 @@ void NetworkInternal::OnNetworkMessageObjectSpawn(NetworkEvent& event, NetworkCl } } - // Setup all newly spawned objects + // Add all newly spawned objects for (int32 i = 0; i < msgData.ItemsCount; i++) { auto& msgDataItem = msgDataItems[i]; @@ -1666,6 +1684,16 @@ void NetworkInternal::OnNetworkMessageObjectSpawn(NetworkEvent& event, NetworkCl // Boost future lookups by using indirection NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Remap object ID={} into object {}:{}", msgDataItem.ObjectId, item.ToString(), obj->GetType().ToString()); IdsRemappingTable.Add(msgDataItem.ObjectId, item.ObjectId); + } + + // Spawn all newly spawned objects (ensure to have valid ownership hierarchy set before spawning object) + for (int32 i = 0; i < msgData.ItemsCount; i++) + { + auto& msgDataItem = msgDataItems[i]; + ScriptingObject* obj = objects[i]; + auto it = Objects.Find(obj->GetID()); + auto& item = it->Item; + const NetworkReplicatedObject* parent = ResolveObject(msgDataItem.ParentId); // Automatic parenting for scene objects auto sceneObject = ScriptingObject::Cast(obj); diff --git a/Source/Engine/Networking/NetworkReplicator.h b/Source/Engine/Networking/NetworkReplicator.h index 389350e5f..84db2797b 100644 --- a/Source/Engine/Networking/NetworkReplicator.h +++ b/Source/Engine/Networking/NetworkReplicator.h @@ -68,7 +68,7 @@ public: /// Does nothing if network is offline. /// The object to replicate. /// The parent of the object (eg. player that spawned it). - API_FUNCTION() static void AddObject(ScriptingObject* obj, ScriptingObject* parent = nullptr); + API_FUNCTION() static void AddObject(ScriptingObject* obj, const ScriptingObject* parent = nullptr); /// /// Removes the object from the network replication system. @@ -80,14 +80,14 @@ public: /// /// Spawns the object to the other clients. Can be spawned by the owner who locally created it (eg. from prefab). /// - /// Does nothing if network is offline. + /// Does nothing if network is offline. Doesn't spawn actor in a level - but in network replication system. /// The object to spawn on other clients. API_FUNCTION() static void SpawnObject(ScriptingObject* obj); /// /// Spawns the object to the other clients. Can be spawned by the owner who locally created it (eg. from prefab). /// - /// Does nothing if network is offline. + /// Does nothing if network is offline. Doesn't spawn actor in a level - but in network replication system. /// The object to spawn on other clients. /// List with network client IDs that should receive network spawn event. Empty to spawn on all clients. API_FUNCTION() static void SpawnObject(ScriptingObject* obj, const DataContainer& clientIds); From 70593177c71d730cc89dd622fc2f11e241fd3b2a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 16 May 2023 14:58:47 +0200 Subject: [PATCH 050/116] Add `NetworkReplicator::HasObject` --- Source/Engine/Networking/NetworkReplicator.cpp | 17 +++++++++++++++++ Source/Engine/Networking/NetworkReplicator.h | 7 +++++++ 2 files changed, 24 insertions(+) diff --git a/Source/Engine/Networking/NetworkReplicator.cpp b/Source/Engine/Networking/NetworkReplicator.cpp index 3142e54b3..165d20659 100644 --- a/Source/Engine/Networking/NetworkReplicator.cpp +++ b/Source/Engine/Networking/NetworkReplicator.cpp @@ -874,6 +874,23 @@ void NetworkReplicator::DespawnObject(ScriptingObject* obj) DeleteNetworkObject(obj); } +bool NetworkReplicator::HasObject(const ScriptingObject* obj) +{ + if (obj) + { + ScopeLock lock(ObjectsLock); + const auto it = Objects.Find(obj->GetID()); + if (it != Objects.End()) + return true; + for (const SpawnItem& item : SpawnQueue) + { + if (item.Object == obj) + return true; + } + } + return false; +} + uint32 NetworkReplicator::GetObjectOwnerClientId(const ScriptingObject* obj) { uint32 id = NetworkManager::ServerClientId; diff --git a/Source/Engine/Networking/NetworkReplicator.h b/Source/Engine/Networking/NetworkReplicator.h index 84db2797b..0e9a2d0d8 100644 --- a/Source/Engine/Networking/NetworkReplicator.h +++ b/Source/Engine/Networking/NetworkReplicator.h @@ -99,6 +99,13 @@ public: /// The object to despawn on other clients. API_FUNCTION() static void DespawnObject(ScriptingObject* obj); + /// + /// Checks if the network object is spawned or added to the network replication system. + /// + /// The network object. + /// True if object exists in networking, otherwise false. + API_FUNCTION() static bool HasObject(const ScriptingObject* obj); + /// /// Gets the Client Id of the network object owner. /// From e045f096a9dbe635ad02548155b30e61ea842812 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 16 May 2023 14:59:15 +0200 Subject: [PATCH 051/116] Add network debugging panel to actors and scripts #1066 --- .../Editor/CustomEditors/CustomEditorsUtil.cs | 5 +++- .../CustomEditors/Dedicated/ActorEditor.cs | 2 +- .../Dedicated/ScriptingObjectEditor.cs | 29 +++++++++++++++++++ .../CustomEditors/Dedicated/ScriptsEditor.cs | 1 - 4 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 Source/Editor/CustomEditors/Dedicated/ScriptingObjectEditor.cs diff --git a/Source/Editor/CustomEditors/CustomEditorsUtil.cs b/Source/Editor/CustomEditors/CustomEditorsUtil.cs index 070ad1fb3..7f653376b 100644 --- a/Source/Editor/CustomEditors/CustomEditorsUtil.cs +++ b/Source/Editor/CustomEditors/CustomEditorsUtil.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; +using FlaxEditor.CustomEditors.Dedicated; using FlaxEditor.CustomEditors.Editors; using FlaxEditor.Scripting; using FlaxEngine; @@ -107,7 +108,7 @@ namespace FlaxEditor.CustomEditors // Select default editor (based on type) if (targetType.IsEnum) return new EnumEditor(); - if (targetType.IsGenericType) + if (targetType.IsGenericType) { if (targetTypeType.GetGenericTypeDefinition() == typeof(Dictionary<,>)) return new DictionaryEditor(); @@ -118,6 +119,8 @@ namespace FlaxEditor.CustomEditors if (customEditorType != null) return (CustomEditor)Activator.CreateInstance(customEditorType); } + if (typeof(FlaxEngine.Object).IsAssignableFrom(targetTypeType)) + return new ScriptingObjectEditor(); // The most generic editor return new GenericEditor(); diff --git a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs index 75e3fff23..a8773a8d7 100644 --- a/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ActorEditor.cs @@ -20,7 +20,7 @@ namespace FlaxEditor.CustomEditors.Dedicated /// /// [CustomEditor(typeof(Actor)), DefaultEditor] - public class ActorEditor : GenericEditor + public class ActorEditor : ScriptingObjectEditor { private Guid _linkedPrefabId; diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptingObjectEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptingObjectEditor.cs new file mode 100644 index 000000000..477deb708 --- /dev/null +++ b/Source/Editor/CustomEditors/Dedicated/ScriptingObjectEditor.cs @@ -0,0 +1,29 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +using FlaxEditor.CustomEditors.Editors; +using FlaxEngine.Networking; + +namespace FlaxEditor.CustomEditors.Dedicated +{ + /// + /// Custom editor for . + /// + public class ScriptingObjectEditor : GenericEditor + { + /// + public override void Initialize(LayoutElementsContainer layout) + { + // Network objects debugging + var obj = Values[0] as FlaxEngine.Object; + if (Editor.IsPlayMode && NetworkManager.IsConnected && NetworkReplicator.HasObject(obj)) + { + var group = layout.Group("Network"); + group.Panel.Open(); + group.Label("Role", Utilities.Utils.GetPropertyNameUI(NetworkReplicator.GetObjectRole(obj).ToString())); + group.Label("Owner Client Id", NetworkReplicator.GetObjectOwnerClientId(obj).ToString()); + } + + base.Initialize(layout); + } + } +} diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs index 73a43f1bf..613f5d3b4 100644 --- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs @@ -3,7 +3,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Linq; using FlaxEditor.Actions; using FlaxEditor.Content; using FlaxEditor.GUI; From 5cf380386092175e0644f444283764132a9805b0 Mon Sep 17 00:00:00 2001 From: Menotdan <32620310+Menotdan@users.noreply.github.com> Date: Tue, 16 May 2023 10:50:53 -0400 Subject: [PATCH 052/116] Add ScreenUtilities class, to replace ScreenUtils in the Engine, so it is being moved to the Editor. --- .../ScreenUtilities/ScreenUtilities.h | 45 +++++++++++++++++++ .../ScreenUtilitiesWindows.cpp | 6 +++ 2 files changed, 51 insertions(+) create mode 100644 Source/Editor/Utilities/ScreenUtilities/ScreenUtilities.h create mode 100644 Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesWindows.cpp diff --git a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilities.h b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilities.h new file mode 100644 index 000000000..468934b63 --- /dev/null +++ b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilities.h @@ -0,0 +1,45 @@ +#pragma once + +#include "Engine/Core/Types/BaseTypes.h" +#include "Engine/Core/Delegate.h" + +API_INJECT_CODE(cpp, "#include \"Editor/Utilities/ScreenUtilities/ScreenUtilities.h\""); + +/// +/// Platform-dependent screen utilties. +/// +API_CLASS(Static, Name = "ScreenUtils", Tag = "NativeInvokeUseName") +class FLAXENGINE_API ScreenUtilities +{ +public: + static struct FLAXENGINE_API ScriptingTypeInitializer TypeInitializer; + + /// + /// Gets the pixel color at the specified coordinates. + /// + /// X Coordinate to read. + /// Y Coordinate to read. + /// Pixel color at the specified coordinates. + API_FUNCTION() static Color32 GetPixelAt(int32 x, int32 y); + + /// + /// Gets the cursor position, in screen cooridnates. + /// + /// Cursor position, in screen coordinates. + API_FUNCTION() static Int2 GetScreenCursorPosition(); + + /// + /// Starts async color picking. Will return a color through ColorReturnCallback. + /// + /// Blocks mouse input and runs a callback + /// + API_FUNCTION() static void BlockAndReadMouse(); + + /// + /// Called when PickColor() is finished. + /// + API_EVENT() static Delegate PickColorDone; +}; diff --git a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesWindows.cpp b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesWindows.cpp new file mode 100644 index 000000000..0ff177a70 --- /dev/null +++ b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesWindows.cpp @@ -0,0 +1,6 @@ +#if PLATFORM_WINDOWS + +#include "ScreenUtilities.h" + + +#endif From a8705130860b5d6314fb41c30c944238c7a6d565 Mon Sep 17 00:00:00 2001 From: Menotdan <32620310+Menotdan@users.noreply.github.com> Date: Tue, 16 May 2023 13:54:25 -0400 Subject: [PATCH 053/116] Color picker finally works, refactored the code. --- .../Editor/GUI/Dialogs/ColorPickerDialog.cs | 10 +- .../ScreenUtilities/ScreenUtilities.h | 2 +- .../ScreenUtilitiesWindows.cpp | 92 +++++++++++++++++++ .../Engine/Platform/Base/ScreenUtilsBase.cpp | 19 ---- Source/Engine/Platform/Base/ScreenUtilsBase.h | 45 --------- Source/Engine/Platform/ScreenUtils.cpp | 0 Source/Engine/Platform/ScreenUtils.h | 11 --- .../Platform/Windows/WindowsScreenUtils.cpp | 91 ------------------ .../Platform/Windows/WindowsScreenUtils.h | 23 ----- 9 files changed, 98 insertions(+), 195 deletions(-) delete mode 100644 Source/Engine/Platform/Base/ScreenUtilsBase.cpp delete mode 100644 Source/Engine/Platform/Base/ScreenUtilsBase.h delete mode 100644 Source/Engine/Platform/ScreenUtils.cpp delete mode 100644 Source/Engine/Platform/ScreenUtils.h delete mode 100644 Source/Engine/Platform/Windows/WindowsScreenUtils.cpp delete mode 100644 Source/Engine/Platform/Windows/WindowsScreenUtils.h diff --git a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs index bdef28db2..7f24372e3 100644 --- a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs +++ b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs @@ -215,8 +215,8 @@ namespace FlaxEditor.GUI.Dialogs private Color32 GetEyedropColor() { - Int2 mousePosition = ScreenUtils.GetScreenCursorPosition(); - Color32 pixelColor = ScreenUtils.GetPixelAt(mousePosition.X, mousePosition.Y); + Int2 mousePosition = ScreenUtilities.GetScreenCursorPosition(); + Color32 pixelColor = ScreenUtilities.GetPixelAt(mousePosition.X, mousePosition.Y); return pixelColor; } @@ -226,14 +226,14 @@ namespace FlaxEditor.GUI.Dialogs Editor.LogWarning("Ok???"); _activeEyedropper = false; SelectedColor = colorPicked; - ScreenUtils.PickColorDone -= ColorPicked; + ScreenUtilities.PickColorDone -= ColorPicked; } private void OnEyedropStart() { _activeEyedropper = true; - ScreenUtils.PickColor(); - ScreenUtils.PickColorDone += ColorPicked; + ScreenUtilities.PickColor(); + ScreenUtilities.PickColorDone += ColorPicked; } private void UpdateEyedrop() diff --git a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilities.h b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilities.h index 468934b63..34d6d49da 100644 --- a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilities.h +++ b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilities.h @@ -8,7 +8,7 @@ API_INJECT_CODE(cpp, "#include \"Editor/Utilities/ScreenUtilities/ScreenUtilitie /// /// Platform-dependent screen utilties. /// -API_CLASS(Static, Name = "ScreenUtils", Tag = "NativeInvokeUseName") +API_CLASS(Static, Name = "ScreenUtilities", Tag = "NativeInvokeUseName") class FLAXENGINE_API ScreenUtilities { public: diff --git a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesWindows.cpp b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesWindows.cpp index 0ff177a70..5bc0b17cc 100644 --- a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesWindows.cpp +++ b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesWindows.cpp @@ -1,6 +1,98 @@ #if PLATFORM_WINDOWS #include "ScreenUtilities.h" +#include "Engine/Core/Math/Color32.h" +#include "Engine/Core/Math/Vector2.h" +#include "Engine/Core/Delegate.h" +#include "Engine/Core/Log.h" +#include "Engine/Scripting/ManagedCLR/MCore.h" + +#include + + +#pragma comment(lib, "Gdi32.lib") + + +Color32 ScreenUtilities::GetPixelAt(int32 x, int32 y) +{ + HDC deviceContext = GetDC(NULL); + COLORREF color = GetPixel(deviceContext, x, y); + ReleaseDC(NULL, deviceContext); + + Color32 returnColor = { GetRValue(color), GetGValue(color), GetBValue(color), 255 }; + return returnColor; +} + +Int2 ScreenUtilities::GetScreenCursorPosition() +{ + POINT cursorPos; + GetCursorPos(&cursorPos); + + Int2 returnCursorPos = { cursorPos.x, cursorPos.y }; + return returnCursorPos; +} + +class ScreenUtilitiesWindows +{ +public: + static void PickSelected(); +}; + +void ScreenUtilitiesWindows::PickSelected() { + // Push event with the picked color. + Int2 cursorPos = ScreenUtilities::GetScreenCursorPosition(); + Color32 colorPicked = ScreenUtilities::GetPixelAt(cursorPos.X, cursorPos.Y); + + LOG(Warning, "REAL: {0}", ScreenUtilities::PickColorDone.Count()); + ScreenUtilities::PickColorDone(colorPicked); + LOG(Warning, "FAKE"); +} + +static HHOOK _mouseCallbackHook; +LRESULT CALLBACK ScreenUtilsMouseCallback( + _In_ int nCode, + _In_ WPARAM wParam, + _In_ LPARAM lParam +) +{ + LOG(Warning, "Hell lag. {0}", GetCurrentThreadId()); + if (wParam != WM_LBUTTONDOWN) { // Return as early as possible. + return CallNextHookEx(NULL, nCode, wParam, lParam); + } + + if (nCode < 0) { + return CallNextHookEx(NULL, nCode, wParam, lParam); + } + + + if (nCode >= 0 && wParam == WM_LBUTTONDOWN) { // Now try to run our code. + LOG(Warning, "Mouse callback hit. Skipping event. (hopefully)"); + UnhookWindowsHookEx(_mouseCallbackHook); + + ScreenUtilitiesWindows::PickSelected(); + return 1; + } + + return CallNextHookEx(NULL, nCode, wParam, lParam); +} + +void ScreenUtilities::BlockAndReadMouse() +{ + _mouseCallbackHook = SetWindowsHookEx(WH_MOUSE_LL, ScreenUtilsMouseCallback, NULL, NULL); + if (_mouseCallbackHook == NULL) + { + LOG(Warning, "Failed to set mouse hook."); + LOG(Warning, "Error: {0}", GetLastError()); + } +} + +Delegate ScreenUtilities::PickColorDone; + +void ScreenUtilities::PickColor() +{ +// MCore::AttachThread(); + BlockAndReadMouse(); +} #endif diff --git a/Source/Engine/Platform/Base/ScreenUtilsBase.cpp b/Source/Engine/Platform/Base/ScreenUtilsBase.cpp deleted file mode 100644 index 833588336..000000000 --- a/Source/Engine/Platform/Base/ScreenUtilsBase.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "ScreenUtilsBase.h" -#include "Engine/Core/Math/Color32.h" -#include "Engine/Core/Math/Vector2.h" - -Color32 ScreenUtilsBase::GetPixelAt(int32 x, int32 y) { - return Color32::Black; -} - -Int2 ScreenUtilsBase::GetScreenCursorPosition() { - return { 0, 0 }; -} - -void ScreenUtilsBase::BlockAndReadMouse() { -} - -void ScreenUtilsBase::PickColor() { -} - -Delegate ScreenUtilsBase::PickColorDone; diff --git a/Source/Engine/Platform/Base/ScreenUtilsBase.h b/Source/Engine/Platform/Base/ScreenUtilsBase.h deleted file mode 100644 index 9a4299ad6..000000000 --- a/Source/Engine/Platform/Base/ScreenUtilsBase.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include "Engine/Core/Types/BaseTypes.h" -#include "Engine/Core/Delegate.h" - -API_INJECT_CODE(cpp, "#include \"Engine/Platform/ScreenUtils.h\""); - -/// -/// Platform-dependent screen utilties. -/// -API_CLASS(Static, Name = "ScreenUtils", Tag = "NativeInvokeUseName") -class FLAXENGINE_API ScreenUtilsBase -{ -public: - static struct FLAXENGINE_API ScriptingTypeInitializer TypeInitializer; - - /// - /// Gets the pixel color at the specified coordinates. - /// - /// X Coordinate to read. - /// Y Coordinate to read. - /// Pixel color at the specified coordinates. - API_FUNCTION() static Color32 GetPixelAt(int32 x, int32 y); - - /// - /// Gets the cursor position, in screen cooridnates. - /// - /// Cursor position, in screen coordinates. - API_FUNCTION() static Int2 GetScreenCursorPosition(); - - /// - /// Starts async color picking. Will return a color through ColorReturnCallback. - /// - /// Blocks mouse input and runs a callback - /// - API_FUNCTION() static void BlockAndReadMouse(); - - /// - /// Called when PickColor() is finished. - /// - API_EVENT() static Delegate PickColorDone; -}; diff --git a/Source/Engine/Platform/ScreenUtils.cpp b/Source/Engine/Platform/ScreenUtils.cpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/Source/Engine/Platform/ScreenUtils.h b/Source/Engine/Platform/ScreenUtils.h deleted file mode 100644 index 3033d2118..000000000 --- a/Source/Engine/Platform/ScreenUtils.h +++ /dev/null @@ -1,11 +0,0 @@ -#if PLATFORM_WINDOWS -#include "Windows/WindowsScreenUtils.h" -#elif PLATFORM_LINUX -#include "Linux/LinuxScreenUtils.h" -#elif PLATFORM_MAC -#include "Mac/MacScreenUtils.h" -#else -#error No Screen Utils For This Platform! Please ensure you are not targetting an Editor build. -#endif - -#include "Types.h" diff --git a/Source/Engine/Platform/Windows/WindowsScreenUtils.cpp b/Source/Engine/Platform/Windows/WindowsScreenUtils.cpp deleted file mode 100644 index 17bcfa2a8..000000000 --- a/Source/Engine/Platform/Windows/WindowsScreenUtils.cpp +++ /dev/null @@ -1,91 +0,0 @@ -#include "WindowsScreenUtils.h" -#include "Engine/Core/Math/Color32.h" -#include "Engine/Core/Math/Vector2.h" -#include "Engine/Core/Delegate.h" - -#include - -#if PLATFORM_WINDOWS -#pragma comment(lib, "Gdi32.lib") -#endif -#include -#include - -Color32 ScreenUtils::GetPixelAt(int32 x, int32 y) -{ - HDC deviceContext = GetDC(NULL); - COLORREF color = GetPixel(deviceContext, x, y); - ReleaseDC(NULL, deviceContext); - - Color32 returnColor = { GetRValue(color), GetGValue(color), GetBValue(color), 255 }; - return returnColor; -} - -Int2 ScreenUtils::GetScreenCursorPosition() -{ - POINT cursorPos; - GetCursorPos(&cursorPos); - - Int2 returnCursorPos = { cursorPos.x, cursorPos.y }; - return returnCursorPos; -} - -static HHOOK _mouseCallbackHook; -LRESULT CALLBACK ScreenUtilsMouseCallback( - _In_ int nCode, - _In_ WPARAM wParam, - _In_ LPARAM lParam -) -{ - LOG(Warning, "Hell lag. {0}", GetCurrentThreadId()); - if (wParam != WM_LBUTTONDOWN) { // Return as early as possible. - return CallNextHookEx(NULL, nCode, wParam, lParam); - } - - if (nCode < 0) { - return CallNextHookEx(NULL, nCode, wParam, lParam); - } - - - if (nCode >= 0 && wParam == WM_LBUTTONDOWN) { // Now try to run our code. - LOG(Warning, "Mouse callback hit. Skipping event. (hopefully)"); - UnhookWindowsHookEx(_mouseCallbackHook); - - ScreenUtils::PickSelected(); - return 1; - } - - return CallNextHookEx(NULL, nCode, wParam, lParam); -} - -void ScreenUtils::BlockAndReadMouse() -{ - _mouseCallbackHook = SetWindowsHookEx(WH_MOUSE_LL, ScreenUtilsMouseCallback, NULL, NULL); - if (_mouseCallbackHook == NULL) - { - LOG(Warning, "Failed to set mouse hook."); - LOG(Warning, "Error: {0}", GetLastError()); - } -} - -Delegate ScreenUtils::PickColorDone; - -void ScreenUtils::Test(Color32 testVal) { - LOG(Warning, "GOT IT"); -} - -void ScreenUtils::PickSelected() { - // Push event with color. - Int2 cursorPos = ScreenUtils::GetScreenCursorPosition(); - Color32 colorPicked = ScreenUtils::GetPixelAt(cursorPos.X, cursorPos.Y); - - LOG(Warning, "REAL: {0}", PickColorDone.Count()); - PickColorDone(colorPicked); - LOG(Warning, "FAKE"); -} - -void ScreenUtils::PickColor() -{ - MCore::AttachThread(); - BlockAndReadMouse(); -} diff --git a/Source/Engine/Platform/Windows/WindowsScreenUtils.h b/Source/Engine/Platform/Windows/WindowsScreenUtils.h deleted file mode 100644 index d8cd1c9f7..000000000 --- a/Source/Engine/Platform/Windows/WindowsScreenUtils.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#if PLATFORM_WINDOWS - -#include "Engine/Platform/Base/ScreenUtilsBase.h" -#include "Engine/Core/Math/Color32.h" - -class FLAXENGINE_API ScreenUtils : public ScreenUtilsBase { -public: - - // [ScreenUtilsBase] - static Color32 GetPixelAt(int32 x, int32 y); - static Int2 GetScreenCursorPosition(); - static void BlockAndReadMouse(); - static void PickColor(); - static Delegate PickColorDone; - -public: - static void PickSelected(); - static void Test(Color32 testVal); -}; - -#endif From 31fb25a43de10ede8a31cb79a378bde2f0424b6f Mon Sep 17 00:00:00 2001 From: Wiktor Kocielski Date: Wed, 17 May 2023 02:28:45 +0300 Subject: [PATCH 054/116] Fix dispatch order regression --- Source/Engine/Networking/NetworkManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Networking/NetworkManager.cpp b/Source/Engine/Networking/NetworkManager.cpp index 88a104c3e..373a24c9d 100644 --- a/Source/Engine/Networking/NetworkManager.cpp +++ b/Source/Engine/Networking/NetworkManager.cpp @@ -318,10 +318,10 @@ bool NetworkManager::StartHost() // Auto-connect host LocalClient->State = NetworkConnectionState::Connected; - ClientConnected(LocalClient); - State = NetworkConnectionState::Connected; StateChanged(); + + ClientConnected(LocalClient); return false; } From f0564e0b066ec0673274b905c223b0ff741e151b Mon Sep 17 00:00:00 2001 From: Wiktor Kocielski Date: Wed, 17 May 2023 03:34:03 +0300 Subject: [PATCH 055/116] Fix NetworkReplicator::SetObjectOwnership not considering spawn queue for hierarchical asignment --- Source/Engine/Networking/NetworkReplicator.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Source/Engine/Networking/NetworkReplicator.cpp b/Source/Engine/Networking/NetworkReplicator.cpp index 165d20659..be7571b80 100644 --- a/Source/Engine/Networking/NetworkReplicator.cpp +++ b/Source/Engine/Networking/NetworkReplicator.cpp @@ -1026,6 +1026,12 @@ void NetworkReplicator::SetObjectOwnership(ScriptingObject* obj, uint32 ownerCli if (e.Item.ParentId == objectId) SetObjectOwnership(e.Item.Object.Get(), ownerClientId, localRole, hierarchical); } + + for (const SpawnItem& spawnItem : SpawnQueue) + { + if (IsParentOf(spawnItem.Object, obj)) + SetObjectOwnership(spawnItem.Object, ownerClientId, localRole, hierarchical); + } } } From b244ffedd2aafd72c7e1af4d88c744d82000871d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 17 May 2023 12:23:01 +0200 Subject: [PATCH 056/116] Fix output log text ranges to handle line ending --- Source/Editor/Windows/OutputLogWindow.cs | 4 ++-- Source/Engine/Render2D/Font.h | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Source/Editor/Windows/OutputLogWindow.cs b/Source/Editor/Windows/OutputLogWindow.cs index 544253fa5..f2f3b4aff 100644 --- a/Source/Editor/Windows/OutputLogWindow.cs +++ b/Source/Editor/Windows/OutputLogWindow.cs @@ -541,7 +541,7 @@ namespace FlaxEditor.Windows { ref var line = ref lines[j]; textBlock.Range.StartIndex = startIndex + line.FirstCharIndex; - textBlock.Range.EndIndex = startIndex + line.LastCharIndex; + textBlock.Range.EndIndex = startIndex + line.LastCharIndex + 1; textBlock.Bounds = new Rectangle(new Float2(0.0f, prevBlockBottom), line.Size); if (textBlock.Range.Length > 0) @@ -550,7 +550,7 @@ namespace FlaxEditor.Windows var regexStart = line.FirstCharIndex; if (j == 0) regexStart += prefixLength; - var regexLength = line.LastCharIndex - regexStart; + var regexLength = line.LastCharIndex + 1 - regexStart; if (regexLength > 0) { var match = _compileRegex.Match(entryText, regexStart, regexLength); diff --git a/Source/Engine/Render2D/Font.h b/Source/Engine/Render2D/Font.h index d3806f0b4..3f42fd396 100644 --- a/Source/Engine/Render2D/Font.h +++ b/Source/Engine/Render2D/Font.h @@ -17,17 +17,17 @@ class FontAsset; /// /// The text range. /// -API_STRUCT() struct TextRange +API_STRUCT(NoDefault) struct TextRange { DECLARE_SCRIPTING_TYPE_MINIMAL(TextRange); /// - /// The start index. + /// The start index (inclusive). /// API_FIELD() int32 StartIndex; /// - /// The end index. + /// The end index (exclusive). /// API_FIELD() int32 EndIndex; @@ -70,7 +70,7 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(TextRange); /// /// Gets the substring from the source text. /// - /// The text. + /// The text. /// The substring of the original text of the defined range. StringView Substring(const StringView& text) const { @@ -87,7 +87,7 @@ struct TIsPODType /// /// The font line info generated during text processing. /// -API_STRUCT() struct FontLineCache +API_STRUCT(NoDefault) struct FontLineCache { DECLARE_SCRIPTING_TYPE_MINIMAL(FontLineCache); @@ -151,7 +151,7 @@ struct TIsPODType /// /// The cached font character entry (read for rendering and further processing). /// -API_STRUCT() struct FontCharacterEntry +API_STRUCT(NoDefault) struct FontCharacterEntry { DECLARE_SCRIPTING_TYPE_MINIMAL(FontCharacterEntry); From 0d3bae376157fce471fac7b7fb52b886e482930e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 17 May 2023 21:37:55 +0200 Subject: [PATCH 057/116] Minor adjustment to #1105 so the local client state is reflected as connecting --- Source/Engine/Networking/NetworkManager.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Networking/NetworkManager.cpp b/Source/Engine/Networking/NetworkManager.cpp index 373a24c9d..88c59ad9a 100644 --- a/Source/Engine/Networking/NetworkManager.cpp +++ b/Source/Engine/Networking/NetworkManager.cpp @@ -317,11 +317,12 @@ bool NetworkManager::StartHost() LocalClient = New(LocalClientId, NetworkConnection{ 0 }); // Auto-connect host - LocalClient->State = NetworkConnectionState::Connected; + LocalClient->State = NetworkConnectionState::Connecting; State = NetworkConnectionState::Connected; StateChanged(); - + LocalClient->State = NetworkConnectionState::Connected; ClientConnected(LocalClient); + return false; } From 7a6c1b8b86a2095d72a1ca718daca21433077dc0 Mon Sep 17 00:00:00 2001 From: Menotdan Date: Wed, 17 May 2023 20:59:42 -0400 Subject: [PATCH 058/116] Add linux implementation definitions. --- .../ScreenUtilities/ScreenUtilitiesLinux.cpp | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp diff --git a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp new file mode 100644 index 000000000..531255df0 --- /dev/null +++ b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp @@ -0,0 +1,32 @@ +#if PLATFORM_LINUX + +#include "ScreenUtilities.h" +#include "Engine/Core/Math/Color32.h" +#include "Engine/Core/Math/Vector2.h" +#include "Engine/Core/Delegate.h" +#include "Engine/Core/Log.h" + +Color32 ScreenUtilities::GetPixelAt(int32 x, int32 y) +{ +} + +Int2 ScreenUtilities::GetScreenCursorPosition() +{ +} + +class ScreenUtilitiesLinux +{ +public: +}; + +void ScreenUtilities::BlockAndReadMouse() +{ +} + +Delegate ScreenUtilities::PickColorDone; + +void ScreenUtilities::PickColor() +{ +} + +#endif \ No newline at end of file From 6e6a4274889da76f45a0e78914f3a542c6301b93 Mon Sep 17 00:00:00 2001 From: Menotdan Date: Wed, 17 May 2023 21:44:26 -0400 Subject: [PATCH 059/116] refactor the code to not include BlockAndReadMouse() as a public API_FUNCTION() --- Source/Editor/Utilities/ScreenUtilities/ScreenUtilities.h | 5 ----- .../Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp | 3 ++- .../Utilities/ScreenUtilities/ScreenUtilitiesWindows.cpp | 6 +++--- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilities.h b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilities.h index 34d6d49da..01380e25b 100644 --- a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilities.h +++ b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilities.h @@ -33,11 +33,6 @@ public: /// - /// Blocks mouse input and runs a callback - /// - API_FUNCTION() static void BlockAndReadMouse(); - /// /// Called when PickColor() is finished. /// diff --git a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp index 531255df0..5e0efb2f2 100644 --- a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp +++ b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp @@ -17,9 +17,10 @@ Int2 ScreenUtilities::GetScreenCursorPosition() class ScreenUtilitiesLinux { public: + static void BlockAndReadMouse(); }; -void ScreenUtilities::BlockAndReadMouse() +void ScreenUtilitiesLinux::BlockAndReadMouse() { } diff --git a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesWindows.cpp b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesWindows.cpp index 5bc0b17cc..e04d3fb77 100644 --- a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesWindows.cpp +++ b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesWindows.cpp @@ -36,6 +36,7 @@ class ScreenUtilitiesWindows { public: static void PickSelected(); + static void BlockAndReadMouse(); }; void ScreenUtilitiesWindows::PickSelected() { @@ -76,7 +77,7 @@ LRESULT CALLBACK ScreenUtilsMouseCallback( return CallNextHookEx(NULL, nCode, wParam, lParam); } -void ScreenUtilities::BlockAndReadMouse() +void ScreenUtilitiesWindows::BlockAndReadMouse() { _mouseCallbackHook = SetWindowsHookEx(WH_MOUSE_LL, ScreenUtilsMouseCallback, NULL, NULL); if (_mouseCallbackHook == NULL) @@ -90,8 +91,7 @@ Delegate ScreenUtilities::PickColorDone; void ScreenUtilities::PickColor() { -// MCore::AttachThread(); - BlockAndReadMouse(); + ScreenUtilitiesWindows::BlockAndReadMouse(); } From d0f5d3270fcceb9cd5ca1e6d435eff2bb9ec065d Mon Sep 17 00:00:00 2001 From: Menotdan Date: Wed, 17 May 2023 23:16:44 -0400 Subject: [PATCH 060/116] Implement GetPixelAt and GetCursorPosition. --- .../ScreenUtilities/ScreenUtilitiesLinux.cpp | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp index 5e0efb2f2..4e81f3018 100644 --- a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp +++ b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp @@ -6,12 +6,53 @@ #include "Engine/Core/Delegate.h" #include "Engine/Core/Log.h" +#include "Engine/Platform/Linux/IncludeX11.h" + Color32 ScreenUtilities::GetPixelAt(int32 x, int32 y) { + X11::XColor color; + Color32 outputColor; + + X11::Display* display = X11::XOpenDisplay((char *) NULL); + int defaultScreen = X11::XDefaultScreen(display); + + X11::XImage* image; + image = X11::XGetImage(display, X11::XRootWindow(display, defaultScreen), x, y, 1, 1, AllPlanes, XYPixmap); + color.pixel = XGetPixel(image, 0, 0); + X11::XFree(image); + + X11::XQueryColor(display, X11::XDefaultColormap(display, defaultScreen), &color); + outputColor.R = color.red / 256; + outputColor.G = color.green / 256; + outputColor.B = color.blue / 256; + + X11::XCloseDisplay(display); + return outputColor; } Int2 ScreenUtilities::GetScreenCursorPosition() { + Int2 cursorPosition = { 0, 0 }; + X11::Display* display = X11::XOpenDisplay(NULL); + X11::Window rootWindow = X11::XRootWindow(display, X11::XDefaultScreen(display)); + + // Buffers (Some useful, some not.) + X11::Window rootWindowBuffer; + int rootX, rootY; + int winXBuffer, winYBuffer; + uint maskBuffer; + + int gotPointer = X11::XQueryPointer(display, rootWindow, &rootWindowBuffer, &rootWindowBuffer, &rootX, &rootY, &winXBuffer, &winYBuffer, &maskBuffer); + if (!gotPointer) { + LOG(Error, "Failed to read mouse pointer (Are you using multiple displays?)"); + return cursorPosition; + } + + cursorPosition.X = rootX; + cursorPosition.Y = rootY; + + X11::XCloseDisplay(display); + return cursorPosition; } class ScreenUtilitiesLinux @@ -22,12 +63,14 @@ public: void ScreenUtilitiesLinux::BlockAndReadMouse() { + return; } Delegate ScreenUtilities::PickColorDone; void ScreenUtilities::PickColor() { + return; } #endif \ No newline at end of file From b6395cf6f1ad673cefd75ab8759e7c076bee86f2 Mon Sep 17 00:00:00 2001 From: Menotdan Date: Wed, 17 May 2023 23:51:24 -0400 Subject: [PATCH 061/116] Trying to implement XGrabPointer... --- .../Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp index 4e81f3018..537038396 100644 --- a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp +++ b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp @@ -63,6 +63,16 @@ public: void ScreenUtilitiesLinux::BlockAndReadMouse() { + X11::Display* display = X11::XOpenDisplay(NULL); + X11::Window rootWindow = X11::XRootWindow(display, X11::XDefaultScreen(display)); + + int grabbedPointer = X11::XGrabPointer(display, rootWindow, 0, Button1Mask, GrabModeAsync, GrabModeAsync, rootWindow, NULL, CurrentTime); + if (grabbedPointer != GrabSuccess) { + LOG(Error, "Failed to grab cursor for events."); + } + + // No idea how to proceed from here for events. + return; } From 1d079842f660adec794f2c7e1e75626fddac6b76 Mon Sep 17 00:00:00 2001 From: Menotdan <32620310+Menotdan@users.noreply.github.com> Date: Wed, 17 May 2023 23:57:23 -0400 Subject: [PATCH 062/116] Add Mac screen utilities, and removed an unneeded log. --- Source/Editor/GUI/Dialogs/ColorPickerDialog.cs | 1 - .../Utilities/ScreenUtilities/ScreenUtilitiesMac.cpp | 7 +++++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesMac.cpp diff --git a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs index 7f24372e3..0c81ecc51 100644 --- a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs +++ b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs @@ -223,7 +223,6 @@ namespace FlaxEditor.GUI.Dialogs private void ColorPicked(Color32 colorPicked) { - Editor.LogWarning("Ok???"); _activeEyedropper = false; SelectedColor = colorPicked; ScreenUtilities.PickColorDone -= ColorPicked; diff --git a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesMac.cpp b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesMac.cpp new file mode 100644 index 000000000..8b816608b --- /dev/null +++ b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesMac.cpp @@ -0,0 +1,7 @@ +#if PLATFORM_MAC +#include +#include + + + +#endif From 408d620b17a028df2377bafb9d85e3cb563fa563 Mon Sep 17 00:00:00 2001 From: Menotdan <32620310+Menotdan@users.noreply.github.com> Date: Thu, 18 May 2023 00:00:06 -0400 Subject: [PATCH 063/116] Removed more unneeded logs. --- .../Utilities/ScreenUtilities/ScreenUtilitiesWindows.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesWindows.cpp b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesWindows.cpp index e04d3fb77..5af695c4d 100644 --- a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesWindows.cpp +++ b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesWindows.cpp @@ -40,13 +40,11 @@ public: }; void ScreenUtilitiesWindows::PickSelected() { - // Push event with the picked color. Int2 cursorPos = ScreenUtilities::GetScreenCursorPosition(); Color32 colorPicked = ScreenUtilities::GetPixelAt(cursorPos.X, cursorPos.Y); - LOG(Warning, "REAL: {0}", ScreenUtilities::PickColorDone.Count()); + // Push event with the picked color. ScreenUtilities::PickColorDone(colorPicked); - LOG(Warning, "FAKE"); } static HHOOK _mouseCallbackHook; @@ -56,7 +54,6 @@ LRESULT CALLBACK ScreenUtilsMouseCallback( _In_ LPARAM lParam ) { - LOG(Warning, "Hell lag. {0}", GetCurrentThreadId()); if (wParam != WM_LBUTTONDOWN) { // Return as early as possible. return CallNextHookEx(NULL, nCode, wParam, lParam); } @@ -67,7 +64,6 @@ LRESULT CALLBACK ScreenUtilsMouseCallback( if (nCode >= 0 && wParam == WM_LBUTTONDOWN) { // Now try to run our code. - LOG(Warning, "Mouse callback hit. Skipping event. (hopefully)"); UnhookWindowsHookEx(_mouseCallbackHook); ScreenUtilitiesWindows::PickSelected(); From f5d3e30972b0b4edd3249aa5e5bbcbfecf1cdef7 Mon Sep 17 00:00:00 2001 From: Menotdan Date: Thu, 18 May 2023 15:42:04 -0400 Subject: [PATCH 064/116] XGrabPointer doesn't seem to be doing what I want. --- .../ScreenUtilities/ScreenUtilitiesLinux.cpp | 28 +++++++++++++++---- .../Engine/Platform/Linux/LinuxPlatform.cpp | 7 +++-- Source/Engine/Platform/Linux/LinuxPlatform.h | 5 ++++ 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp index 537038396..15077f4f2 100644 --- a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp +++ b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp @@ -5,6 +5,7 @@ #include "Engine/Core/Math/Vector2.h" #include "Engine/Core/Delegate.h" #include "Engine/Core/Log.h" +#include "Engine/Platform/Linux/LinuxPlatform.h" #include "Engine/Platform/Linux/IncludeX11.h" @@ -40,7 +41,7 @@ Int2 ScreenUtilities::GetScreenCursorPosition() X11::Window rootWindowBuffer; int rootX, rootY; int winXBuffer, winYBuffer; - uint maskBuffer; + unsigned int maskBuffer; int gotPointer = X11::XQueryPointer(display, rootWindow, &rootWindowBuffer, &rootWindowBuffer, &rootX, &rootY, &winXBuffer, &winYBuffer, &maskBuffer); if (!gotPointer) { @@ -59,28 +60,43 @@ class ScreenUtilitiesLinux { public: static void BlockAndReadMouse(); + static void xEventHandler(void* event); }; +void ScreenUtilitiesLinux::xEventHandler(void* eventPtr) { + X11::XEvent* event = (X11::XEvent*) eventPtr; + + LOG(Warning, "Got event. {0}", event->type); + + if (0) { + LOG(Warning, "Got MOUSE CLICK event."); + X11::Display* display = X11::XOpenDisplay(NULL); + LOG(Warning, "Tried to ungrab pointer. {0}", X11::XUngrabPointer(display, CurrentTime)); + X11::XCloseDisplay(display); + + LinuxPlatform::xEventRecieved.Unbind(xEventHandler); // Unbind the event, we only want to handle one click event. + } +} + void ScreenUtilitiesLinux::BlockAndReadMouse() { X11::Display* display = X11::XOpenDisplay(NULL); X11::Window rootWindow = X11::XRootWindow(display, X11::XDefaultScreen(display)); - int grabbedPointer = X11::XGrabPointer(display, rootWindow, 0, Button1Mask, GrabModeAsync, GrabModeAsync, rootWindow, NULL, CurrentTime); + int grabbedPointer = X11::XGrabPointer(display, rootWindow, 1, Button1Mask, GrabModeAsync, GrabModeAsync, rootWindow, NULL, CurrentTime); if (grabbedPointer != GrabSuccess) { LOG(Error, "Failed to grab cursor for events."); + return; } - // No idea how to proceed from here for events. - - return; + LinuxPlatform::xEventRecieved.Bind(xEventHandler); } Delegate ScreenUtilities::PickColorDone; void ScreenUtilities::PickColor() { - return; + ScreenUtilitiesLinux::BlockAndReadMouse(); } #endif \ No newline at end of file diff --git a/Source/Engine/Platform/Linux/LinuxPlatform.cpp b/Source/Engine/Platform/Linux/LinuxPlatform.cpp index 8015162ec..6de848180 100644 --- a/Source/Engine/Platform/Linux/LinuxPlatform.cpp +++ b/Source/Engine/Platform/Linux/LinuxPlatform.cpp @@ -5,6 +5,7 @@ #include "LinuxPlatform.h" #include "LinuxWindow.h" #include "LinuxInput.h" +#include "IncludeX11.h" #include "Engine/Core/Log.h" #include "Engine/Core/Types/Guid.h" #include "Engine/Core/Types/String.h" @@ -30,7 +31,6 @@ #include "Engine/Input/Input.h" #include "Engine/Input/Mouse.h" #include "Engine/Input/Keyboard.h" -#include "IncludeX11.h" #include #include #include @@ -2217,6 +2217,8 @@ void LinuxPlatform::BeforeRun() { } +Delegate LinuxPlatform::xEventRecieved; + void LinuxPlatform::Tick() { UnixPlatform::Tick(); @@ -2234,7 +2236,8 @@ void LinuxPlatform::Tick() if (X11::XFilterEvent(&event, 0)) continue; - + + xEventRecieved(&event); // Fire the event, since we recieved an event. LinuxWindow* window; switch (event.type) { diff --git a/Source/Engine/Platform/Linux/LinuxPlatform.h b/Source/Engine/Platform/Linux/LinuxPlatform.h index 89afe9292..486674652 100644 --- a/Source/Engine/Platform/Linux/LinuxPlatform.h +++ b/Source/Engine/Platform/Linux/LinuxPlatform.h @@ -33,6 +33,11 @@ public: /// The user home directory. static const String& GetHomeDirectory(); + /// + /// An event that is fired when an XEvent is recieved by Flax. + /// + static Delegate xEventRecieved; + public: // [UnixPlatform] From 8f6ceece10dd548fae88793b43b25b535d221e51 Mon Sep 17 00:00:00 2001 From: Menotdan Date: Thu, 18 May 2023 19:40:39 -0400 Subject: [PATCH 065/116] Change XGrabPointer call a bit. --- .../ScreenUtilities/ScreenUtilitiesLinux.cpp | 17 +++++++++++------ Source/Engine/Platform/Linux/LinuxPlatform.cpp | 3 ++- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp index 15077f4f2..d4734b12d 100644 --- a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp +++ b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp @@ -67,14 +67,18 @@ void ScreenUtilitiesLinux::xEventHandler(void* eventPtr) { X11::XEvent* event = (X11::XEvent*) eventPtr; LOG(Warning, "Got event. {0}", event->type); + X11::Display* display = X11::XOpenDisplay(NULL); - if (0) { + if (event->type == ButtonPress) { LOG(Warning, "Got MOUSE CLICK event."); - X11::Display* display = X11::XOpenDisplay(NULL); - LOG(Warning, "Tried to ungrab pointer. {0}", X11::XUngrabPointer(display, CurrentTime)); - X11::XCloseDisplay(display); - LinuxPlatform::xEventRecieved.Unbind(xEventHandler); // Unbind the event, we only want to handle one click event. + + X11::XUngrabPointer(display, CurrentTime); + X11::XCloseDisplay(display); + } else + { + LOG(Warning, "Got a different event.."); + X11::XCloseDisplay(display); } } @@ -83,7 +87,8 @@ void ScreenUtilitiesLinux::BlockAndReadMouse() X11::Display* display = X11::XOpenDisplay(NULL); X11::Window rootWindow = X11::XRootWindow(display, X11::XDefaultScreen(display)); - int grabbedPointer = X11::XGrabPointer(display, rootWindow, 1, Button1Mask, GrabModeAsync, GrabModeAsync, rootWindow, NULL, CurrentTime); + X11::Cursor cursor = XCreateFontCursor(display, 130); + int grabbedPointer = X11::XGrabPointer(display, rootWindow, 0, ButtonPressMask, GrabModeAsync, GrabModeAsync, rootWindow, cursor, CurrentTime); if (grabbedPointer != GrabSuccess) { LOG(Error, "Failed to grab cursor for events."); return; diff --git a/Source/Engine/Platform/Linux/LinuxPlatform.cpp b/Source/Engine/Platform/Linux/LinuxPlatform.cpp index 6de848180..631b8d017 100644 --- a/Source/Engine/Platform/Linux/LinuxPlatform.cpp +++ b/Source/Engine/Platform/Linux/LinuxPlatform.cpp @@ -2237,7 +2237,8 @@ void LinuxPlatform::Tick() if (X11::XFilterEvent(&event, 0)) continue; - xEventRecieved(&event); // Fire the event, since we recieved an event. + xEventRecieved(&event); // Fire this event, since we recieved an event. + LinuxWindow* window; switch (event.type) { From cdee91f2580b4fe9d10fe9037a524a8c62ff30fa Mon Sep 17 00:00:00 2001 From: Menotdan Date: Thu, 18 May 2023 21:48:47 -0400 Subject: [PATCH 066/116] Color Picker FINALLY works --- .../ScreenUtilities/ScreenUtilitiesLinux.cpp | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp index d4734b12d..6dc2103d9 100644 --- a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp +++ b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp @@ -14,7 +14,7 @@ Color32 ScreenUtilities::GetPixelAt(int32 x, int32 y) X11::XColor color; Color32 outputColor; - X11::Display* display = X11::XOpenDisplay((char *) NULL); + X11::Display* display = (X11::Display*) LinuxPlatform::GetXDisplay(); int defaultScreen = X11::XDefaultScreen(display); X11::XImage* image; @@ -27,14 +27,13 @@ Color32 ScreenUtilities::GetPixelAt(int32 x, int32 y) outputColor.G = color.green / 256; outputColor.B = color.blue / 256; - X11::XCloseDisplay(display); return outputColor; } Int2 ScreenUtilities::GetScreenCursorPosition() { Int2 cursorPosition = { 0, 0 }; - X11::Display* display = X11::XOpenDisplay(NULL); + X11::Display* display = (X11::Display*) LinuxPlatform::GetXDisplay(); X11::Window rootWindow = X11::XRootWindow(display, X11::XDefaultScreen(display)); // Buffers (Some useful, some not.) @@ -45,14 +44,13 @@ Int2 ScreenUtilities::GetScreenCursorPosition() int gotPointer = X11::XQueryPointer(display, rootWindow, &rootWindowBuffer, &rootWindowBuffer, &rootX, &rootY, &winXBuffer, &winYBuffer, &maskBuffer); if (!gotPointer) { - LOG(Error, "Failed to read mouse pointer (Are you using multiple displays?)"); + LOG(Error, "Failed to find the mouse pointer (Are you using multiple displays?)"); return cursorPosition; } cursorPosition.X = rootX; cursorPosition.Y = rootY; - X11::XCloseDisplay(display); return cursorPosition; } @@ -66,34 +64,33 @@ public: void ScreenUtilitiesLinux::xEventHandler(void* eventPtr) { X11::XEvent* event = (X11::XEvent*) eventPtr; - LOG(Warning, "Got event. {0}", event->type); - X11::Display* display = X11::XOpenDisplay(NULL); + X11::Display* display = (X11::Display*) LinuxPlatform::GetXDisplay(); if (event->type == ButtonPress) { - LOG(Warning, "Got MOUSE CLICK event."); - LinuxPlatform::xEventRecieved.Unbind(xEventHandler); // Unbind the event, we only want to handle one click event. + Int2 cursorPosition = ScreenUtilities::GetScreenCursorPosition(); + Color32 colorPicked = ScreenUtilities::GetPixelAt(cursorPosition.X, cursorPosition.Y); + ScreenUtilities::PickColorDone(colorPicked); // Run the callback for picking colors being complete. + LinuxPlatform::xEventRecieved.Unbind(xEventHandler); // Unbind the event, we only want to handle one click event X11::XUngrabPointer(display, CurrentTime); - X11::XCloseDisplay(display); - } else - { - LOG(Warning, "Got a different event.."); - X11::XCloseDisplay(display); } } void ScreenUtilitiesLinux::BlockAndReadMouse() { - X11::Display* display = X11::XOpenDisplay(NULL); + X11::Display* display = (X11::Display*) LinuxPlatform::GetXDisplay(); X11::Window rootWindow = X11::XRootWindow(display, X11::XDefaultScreen(display)); X11::Cursor cursor = XCreateFontCursor(display, 130); int grabbedPointer = X11::XGrabPointer(display, rootWindow, 0, ButtonPressMask, GrabModeAsync, GrabModeAsync, rootWindow, cursor, CurrentTime); if (grabbedPointer != GrabSuccess) { LOG(Error, "Failed to grab cursor for events."); + + X11::XFreeCursor(display, cursor); return; } + X11::XFreeCursor(display, cursor); LinuxPlatform::xEventRecieved.Bind(xEventHandler); } From 8c20ba83de53338ada8fed81eb40811eb97547aa Mon Sep 17 00:00:00 2001 From: Menotdan <32620310+Menotdan@users.noreply.github.com> Date: Thu, 18 May 2023 22:10:35 -0400 Subject: [PATCH 067/116] Lay out what needs to be implemented for Mac --- .../ScreenUtilities/ScreenUtilitiesMac.cpp | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesMac.cpp b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesMac.cpp index 8b816608b..2a81ab301 100644 --- a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesMac.cpp +++ b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesMac.cpp @@ -2,6 +2,39 @@ #include #include +Color32 ScreenUtilities::GetPixelAt(int32 x, int32 y) +{ + // Called from C# for live updates to the color. + return { 0, 0, 0, 255 }; +} + +Int2 ScreenUtilities::GetScreenCursorPosition() +{ + // Called from C# for live updates to the color. + + return { 0, 0 }; +} + +class ScreenUtilitiesMac +{ +public: + static void BlockAndReadMouse(); +}; + +void ScreenUtilitiesMac::BlockAndReadMouse() +{ + // Maybe you don't need this if you go with NSColorSampler +} + +Delegate ScreenUtilities::PickColorDone; + +void ScreenUtilities::PickColor() +{ + // This is what C# calls to start the color picking sequence + // This should stop mouse clicks from working for one click, and that click is on the selected color + // There is a class called NSColorSample that might implement that for you, but maybe not. + // It also might just work to copy the Linux Impl since Mac uses X as well, right? +} #endif From 66f66c548182826837c1a5c768db7c7209e64415 Mon Sep 17 00:00:00 2001 From: Menotdan <32620310+Menotdan@users.noreply.github.com> Date: Fri, 19 May 2023 00:11:29 -0400 Subject: [PATCH 068/116] Whoops forgot includes. Hopefully this builds, but I don't have a mac to test so... --- Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesMac.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesMac.cpp b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesMac.cpp index 2a81ab301..ae48112ea 100644 --- a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesMac.cpp +++ b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesMac.cpp @@ -1,6 +1,9 @@ #if PLATFORM_MAC #include #include +#include "ScreenUtilities.h" +#include "Engine/Core/Types/BaseTypes.h" +#include "Engine/Core/Delegate.h" Color32 ScreenUtilities::GetPixelAt(int32 x, int32 y) { From 306cedeb3958cb2e05c22db95cf7fa1fad137569 Mon Sep 17 00:00:00 2001 From: Menotdan <32620310+Menotdan@users.noreply.github.com> Date: Fri, 19 May 2023 00:54:49 -0400 Subject: [PATCH 069/116] stopped being stupid and just copied dependencies from the other implementations. --- .../Editor/Utilities/ScreenUtilities/ScreenUtilitiesMac.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesMac.cpp b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesMac.cpp index ae48112ea..f774242ce 100644 --- a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesMac.cpp +++ b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesMac.cpp @@ -1,9 +1,12 @@ #if PLATFORM_MAC #include #include + #include "ScreenUtilities.h" -#include "Engine/Core/Types/BaseTypes.h" +#include "Engine/Core/Math/Color32.h" +#include "Engine/Core/Math/Vector2.h" #include "Engine/Core/Delegate.h" +#include "Engine/Core/Log.h" Color32 ScreenUtilities::GetPixelAt(int32 x, int32 y) { From f46d1a4aba17542041be64ceb0804647fbaedd8b Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Fri, 19 May 2023 02:16:20 +0300 Subject: [PATCH 070/116] Enable roll-forward to latest .NET major preview version --- Source/Engine/Scripting/Runtime/DotNet.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index 3ae1e02d5..801674752 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -681,6 +681,7 @@ bool MAssembly::LoadCorlib() bool MAssembly::LoadImage(const String& assemblyPath, const StringView& nativePath) { + // TODO: Use new hostfxr delegate load_assembly_bytes? (.NET 8+) // Open .Net assembly const StringAnsi assemblyPathAnsi = assemblyPath.ToStringAnsi(); const char* name; @@ -1567,9 +1568,9 @@ bool InitHostfxr() Platform::OpenUrl(TEXT("https://dotnet.microsoft.com/en-us/download/dotnet/7.0")); #endif #if USE_EDITOR - LOG(Fatal, "Missing .NET 7 SDK installation requried to run Flax Editor."); + LOG(Fatal, "Missing .NET 7 SDK installation required to run Flax Editor."); #else - LOG(Fatal, "Missing .NET 7 Runtime installation requried to run this application."); + LOG(Fatal, "Missing .NET 7 Runtime installation required to run this application."); #endif return true; } @@ -1596,6 +1597,15 @@ bool InitHostfxr() return true; } + // TODO: Implement picking different version of hostfxr, currently prefers highest available version. + // Allow future and preview versions of .NET + String dotnetRollForward; + String dotnetRollForwardPr; + if (Platform::GetEnvironmentVariable(TEXT("DOTNET_ROLL_FORWARD"), dotnetRollForward)) + Platform::SetEnvironmentVariable(TEXT("DOTNET_ROLL_FORWARD"), TEXT("LatestMajor")); + if (Platform::GetEnvironmentVariable(TEXT("DOTNET_ROLL_FORWARD_TO_PRERELEASE"), dotnetRollForwardPr)) + Platform::SetEnvironmentVariable(TEXT("DOTNET_ROLL_FORWARD_TO_PRERELEASE"), TEXT("1")); + // Initialize hosting component const char_t* argv[1] = { libraryPath.Get() }; hostfxr_initialize_parameters init_params; From ed70eb24c7bcb7adfc51ec600f9efdd679c05e3c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 19 May 2023 13:49:15 +0200 Subject: [PATCH 071/116] Fix using proper default value in scripting bindings for `IntPtr` value type --- .../Bindings/BindingsGenerator.CSharp.cs | 39 +++++++++++-------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 205cefac3..8ccb6105e 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -90,7 +90,7 @@ namespace Flax.Build.Bindings "Int4", }; - private static string GenerateCSharpDefaultValueNativeToManaged(BuildData buildData, string value, ApiTypeInfo caller, TypeInfo valueType = null, bool attribute = false) + private static string GenerateCSharpDefaultValueNativeToManaged(BuildData buildData, string value, ApiTypeInfo caller, TypeInfo valueType = null, bool attribute = false, string managedType = null) { if (string.IsNullOrEmpty(value)) return null; @@ -99,6 +99,13 @@ namespace Flax.Build.Bindings if (value.StartsWith("TEXT(\"") && value.EndsWith("\")")) return value.Substring(5, value.Length - 6); + // Pointer constant value + if (managedType != null && managedType == "IntPtr" && + (value == "nullptr" || value == "NULL" || value == "0")) + { + return "new IntPtr()"; + } + // In-built constants switch (value) { @@ -1059,12 +1066,12 @@ namespace Flax.Build.Bindings else if (fieldInfo.IsStatic) contents.Append("static "); - var returnValueType = GenerateCSharpNativeToManaged(buildData, fieldInfo.Type, classInfo); - contents.Append(returnValueType).Append(' ').Append(fieldInfo.Name); + var managedType = GenerateCSharpNativeToManaged(buildData, fieldInfo.Type, classInfo); + contents.Append(managedType).Append(' ').Append(fieldInfo.Name); if (!useUnmanaged || fieldInfo.IsConstexpr) { - var defaultValue = GenerateCSharpDefaultValueNativeToManaged(buildData, fieldInfo.DefaultValue, classInfo, fieldInfo.Type); + var defaultValue = GenerateCSharpDefaultValueNativeToManaged(buildData, fieldInfo.DefaultValue, classInfo, fieldInfo.Type, false, managedType); if (!string.IsNullOrEmpty(defaultValue)) contents.Append(" = ").Append(defaultValue); contents.AppendLine(";"); @@ -1183,7 +1190,7 @@ namespace Flax.Build.Bindings contents.Append(' '); contents.Append(parameterInfo.Name); - var defaultValue = GenerateCSharpDefaultValueNativeToManaged(buildData, parameterInfo.DefaultValue, classInfo, parameterInfo.Type); + var defaultValue = GenerateCSharpDefaultValueNativeToManaged(buildData, parameterInfo.DefaultValue, classInfo, parameterInfo.Type, false, managedType); if (!string.IsNullOrEmpty(defaultValue)) contents.Append(" = ").Append(defaultValue); } @@ -1243,7 +1250,7 @@ namespace Flax.Build.Bindings contents.Append(' '); contents.Append(parameterInfo.Name); - var defaultValue = GenerateCSharpDefaultValueNativeToManaged(buildData, parameterInfo.DefaultValue, classInfo, parameterInfo.Type); + var defaultValue = GenerateCSharpDefaultValueNativeToManaged(buildData, parameterInfo.DefaultValue, classInfo, parameterInfo.Type, false, managedType); if (!string.IsNullOrEmpty(defaultValue)) contents.Append(" = ").Append(defaultValue); } @@ -1697,25 +1704,25 @@ namespace Flax.Build.Bindings else if (fieldInfo.IsStatic) contents.Append("static "); hasDefaultMember |= string.Equals(fieldInfo.Name, "Default", StringComparison.Ordinal); - string type; + string managedType; if (fieldInfo.Type.IsArray && (fieldInfo.NoArray || structureInfo.IsPod)) { fieldInfo.Type.IsArray = false; - type = GenerateCSharpNativeToManaged(buildData, fieldInfo.Type, structureInfo); + managedType = GenerateCSharpNativeToManaged(buildData, fieldInfo.Type, structureInfo); fieldInfo.Type.IsArray = true; #if USE_NETCORE // Use fixed statement with primitive types of buffers - if (type == "char") + if (managedType == "char") { // char's are not blittable, store as short instead - contents.Append($"fixed short {fieldInfo.Name}0[{fieldInfo.Type.ArraySize}]; // {type}*").AppendLine(); + contents.Append($"fixed short {fieldInfo.Name}0[{fieldInfo.Type.ArraySize}]; // {managedType}*").AppendLine(); } else #endif { // Padding in structs for fixed-size array - contents.Append(type).Append(' ').Append(fieldInfo.Name + "0;").AppendLine(); + contents.Append(managedType).Append(' ').Append(fieldInfo.Name + "0;").AppendLine(); for (int i = 1; i < fieldInfo.Type.ArraySize; i++) { contents.AppendLine(); @@ -1724,19 +1731,19 @@ namespace Flax.Build.Bindings contents.Append(indent).Append(GenerateCSharpAccessLevel(fieldInfo.Access)); if (fieldInfo.IsStatic) contents.Append("static "); - contents.Append(type).Append(' ').Append(fieldInfo.Name + i).Append(';').AppendLine(); + contents.Append(managedType).Append(' ').Append(fieldInfo.Name + i).Append(';').AppendLine(); } } continue; } - type = GenerateCSharpNativeToManaged(buildData, fieldInfo.Type, structureInfo); - contents.Append(type).Append(' ').Append(fieldInfo.Name); + managedType = GenerateCSharpNativeToManaged(buildData, fieldInfo.Type, structureInfo); + contents.Append(managedType).Append(' ').Append(fieldInfo.Name); if (fieldInfo.IsConstexpr) { // Compile-time constant - var defaultValue = GenerateCSharpDefaultValueNativeToManaged(buildData, fieldInfo.DefaultValue, structureInfo, fieldInfo.Type); + var defaultValue = GenerateCSharpDefaultValueNativeToManaged(buildData, fieldInfo.DefaultValue, structureInfo, fieldInfo.Type, false, managedType); if (!string.IsNullOrEmpty(defaultValue)) contents.Append(" = ").Append(defaultValue); contents.AppendLine(";"); @@ -1950,7 +1957,7 @@ namespace Flax.Build.Bindings contents.Append(' '); contents.Append(parameterInfo.Name); - var defaultValue = GenerateCSharpDefaultValueNativeToManaged(buildData, parameterInfo.DefaultValue, interfaceInfo, parameterInfo.Type); + var defaultValue = GenerateCSharpDefaultValueNativeToManaged(buildData, parameterInfo.DefaultValue, interfaceInfo, parameterInfo.Type, false, managedType); if (!string.IsNullOrEmpty(defaultValue)) contents.Append(" = ").Append(defaultValue); } From 3e792e6cd7555ac6ceb7ef356f6bbd2e86758e2e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 19 May 2023 13:52:30 +0200 Subject: [PATCH 072/116] Add `NativeInteropException` for native interop crashes --- Source/Engine/Engine/NativeInterop.Managed.cs | 8 ++++---- .../Engine/Engine/NativeInterop.Unmanaged.cs | 2 +- Source/Engine/Engine/NativeInterop.cs | 19 ++++++++++++++++--- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/Source/Engine/Engine/NativeInterop.Managed.cs b/Source/Engine/Engine/NativeInterop.Managed.cs index 41ace593b..bc240275f 100644 --- a/Source/Engine/Engine/NativeInterop.Managed.cs +++ b/Source/Engine/Engine/NativeInterop.Managed.cs @@ -261,7 +261,7 @@ namespace FlaxEngine.Interop return; } - throw new Exception("Tried to free non-pooled ManagedArray as pooled ManagedArray"); + throw new NativeInteropException("Tried to free non-pooled ManagedArray as pooled ManagedArray"); } } } @@ -499,7 +499,7 @@ namespace FlaxEngine.Interop else if (weakPoolOther.TryGetValue(handle, out value)) return value; - throw new Exception("Invalid ManagedHandle"); + throw new NativeInteropException("Invalid ManagedHandle"); } internal static void SetObject(IntPtr handle, object value) @@ -527,7 +527,7 @@ namespace FlaxEngine.Interop else if (weakPoolOther.ContainsKey(handle)) weakPoolOther[handle] = value; - throw new Exception("Invalid ManagedHandle"); + throw new NativeInteropException("Invalid ManagedHandle"); } internal static void FreeHandle(IntPtr handle) @@ -556,7 +556,7 @@ namespace FlaxEngine.Interop else return; - throw new Exception("Invalid ManagedHandle"); + throw new NativeInteropException("Invalid ManagedHandle"); } } } diff --git a/Source/Engine/Engine/NativeInterop.Unmanaged.cs b/Source/Engine/Engine/NativeInterop.Unmanaged.cs index bc2af6d61..c079a14e1 100644 --- a/Source/Engine/Engine/NativeInterop.Unmanaged.cs +++ b/Source/Engine/Engine/NativeInterop.Unmanaged.cs @@ -1157,7 +1157,7 @@ namespace FlaxEngine.Interop case Type _ when type.IsClass: monoType = MTypes.Object; break; - default: throw new Exception($"Unsupported type '{type.FullName}'"); + default: throw new NativeInteropException($"Unsupported type '{type.FullName}'"); } return (uint)monoType; } diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs index 39adc59ab..52dc82fce 100644 --- a/Source/Engine/Engine/NativeInterop.cs +++ b/Source/Engine/Engine/NativeInterop.cs @@ -87,7 +87,7 @@ namespace FlaxEngine.Interop #endif scriptingAssemblyLoadContext = new AssemblyLoadContext("Flax", isCollectible); #if FLAX_EDITOR - scriptingAssemblyLoadContext.Resolving += ScriptingAssemblyLoadContext_Resolving; + scriptingAssemblyLoadContext.Resolving += OnScriptingAssemblyLoadContextResolving; #endif } @@ -106,7 +106,8 @@ namespace FlaxEngine.Interop DelegateHelpers.InitMethods(); } - private static Assembly? ScriptingAssemblyLoadContext_Resolving(AssemblyLoadContext assemblyLoadContext, AssemblyName assemblyName) +#if FLAX_EDITOR + private static Assembly? OnScriptingAssemblyLoadContextResolving(AssemblyLoadContext assemblyLoadContext, AssemblyName assemblyName) { // FIXME: There should be a better way to resolve the path to EditorTargetPath where the dependencies are stored string editorTargetPath = Path.GetDirectoryName(nativeLibraryPaths.Keys.First(x => x != "FlaxEngine")); @@ -116,6 +117,7 @@ namespace FlaxEngine.Interop return assemblyLoadContext.LoadFromAssemblyPath(assemblyPath); return null; } +#endif [UnmanagedCallersOnly] internal static unsafe void Exit() @@ -523,7 +525,7 @@ namespace FlaxEngine.Interop } } - throw new Exception($"Invalid field {field.Name} to marshal for type {typeof(T).Name}"); + throw new NativeInteropException($"Invalid field {field.Name} to marshal for type {typeof(T).Name}"); } private static void ToManagedFieldPointer(FieldInfo field, ref T fieldOwner, IntPtr fieldPtr, out int fieldOffset) @@ -1271,6 +1273,17 @@ namespace FlaxEngine.Interop } #endif } + + internal class NativeInteropException : Exception + { + public NativeInteropException(string message) + : base(message) + { +#if !BUILD_RELEASE + Debug.Logger.LogHandler.LogWrite(LogType.Error, "Native interop exception!"); +#endif + } + } } #endif From 7b8c01391853727400f07c8d06ab0578d0592447 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 19 May 2023 13:53:18 +0200 Subject: [PATCH 073/116] Various minor cleanups --- Source/Engine/Platform/Base/PlatformBase.h | 12 --------- .../Platform/Windows/WindowsPlatform.cpp | 6 ++--- .../Engine/Platform/Windows/WindowsPlatform.h | 2 +- Source/Engine/Scripting/Runtime/DotNet.cpp | 27 ++++++++----------- 4 files changed, 14 insertions(+), 33 deletions(-) diff --git a/Source/Engine/Platform/Base/PlatformBase.h b/Source/Engine/Platform/Base/PlatformBase.h index d3bcb8dfb..46a2fa8f9 100644 --- a/Source/Engine/Platform/Base/PlatformBase.h +++ b/Source/Engine/Platform/Base/PlatformBase.h @@ -167,7 +167,6 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(PlatformBase); static void Exit(); public: - /// /// Copy memory region /// @@ -334,7 +333,6 @@ public: static void FreePages(void* ptr); public: - /// /// Returns the current runtime platform type. It's compile-time constant. /// @@ -409,7 +407,6 @@ public: static void Sleep(int32 milliseconds) = delete; public: - /// /// Gets the current time in seconds. /// @@ -455,7 +452,6 @@ public: static void GetUTCTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond) = delete; public: - /// /// Shows the fatal error message to the user. /// @@ -482,7 +478,6 @@ public: static void Info(const Char* msg); public: - /// /// Shows the fatal error message to the user. /// @@ -520,7 +515,6 @@ public: static bool IsDebuggerPresent(); public: - /// /// Performs a fatal crash. /// @@ -560,7 +554,6 @@ public: static void CheckFailed(const char* message, const char* file, int line); public: - /// /// Sets the High DPI awareness. /// @@ -628,7 +621,6 @@ public: static void CreateGuid(Guid& result); public: - /// /// The list of users. /// @@ -645,7 +637,6 @@ public: API_EVENT() static Delegate UserRemoved; public: - /// /// Returns a value indicating whether can open a given URL in a web browser. /// @@ -660,7 +651,6 @@ public: API_FUNCTION() static void OpenUrl(const StringView& url) = delete; public: - /// /// Gets the origin position and size of the monitor at the given screen-space location. /// @@ -686,7 +676,6 @@ public: API_PROPERTY() static Float2 GetVirtualDesktopSize(); public: - /// /// Gets full path of the main engine directory. /// @@ -719,7 +708,6 @@ public: static bool SetWorkingDirectory(const String& path); public: - /// /// Gets the process environment variables (pairs of key and value). /// diff --git a/Source/Engine/Platform/Windows/WindowsPlatform.cpp b/Source/Engine/Platform/Windows/WindowsPlatform.cpp index 2a4cd2fd4..0ac741f71 100644 --- a/Source/Engine/Platform/Windows/WindowsPlatform.cpp +++ b/Source/Engine/Platform/Windows/WindowsPlatform.cpp @@ -1221,10 +1221,11 @@ void* WindowsPlatform::LoadLibrary(const Char* filename) return handle; } +#if CRASH_LOG_ENABLE + Array WindowsPlatform::GetStackFrames(int32 skipCount, int32 maxDepth, void* context) { Array result; -#if CRASH_LOG_ENABLE DbgHelpLock(); // Initialize @@ -1350,12 +1351,9 @@ Array WindowsPlatform::GetStackFrames(int32 skipCount, } DbgHelpUnlock(); -#endif return result; } -#if CRASH_LOG_ENABLE - void WindowsPlatform::CollectCrashData(const String& crashDataFolder, void* context) { // Create mini dump file for crash debugging diff --git a/Source/Engine/Platform/Windows/WindowsPlatform.h b/Source/Engine/Platform/Windows/WindowsPlatform.h index cc85a6d1e..fef8e4ac3 100644 --- a/Source/Engine/Platform/Windows/WindowsPlatform.h +++ b/Source/Engine/Platform/Windows/WindowsPlatform.h @@ -80,8 +80,8 @@ public: static int32 CreateProcess(CreateProcessSettings& settings); static Window* CreateWindow(const CreateWindowSettings& settings); static void* LoadLibrary(const Char* filename); - static Array GetStackFrames(int32 skipCount = 0, int32 maxDepth = 60, void* context = nullptr); #if CRASH_LOG_ENABLE + static Array GetStackFrames(int32 skipCount = 0, int32 maxDepth = 60, void* context = nullptr); static void CollectCrashData(const String& crashDataFolder, void* context = nullptr); #endif }; diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index 3ae1e02d5..76f6dd89f 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -165,9 +165,8 @@ extern MDomain* MActiveDomain; extern Array> MDomains; Dictionary CachedFunctions; - -Dictionary classHandles; -Dictionary assemblyHandles; +Dictionary CachedClassHandles; +Dictionary CachedAssemblyHandles; /// /// Returns the function pointer to the managed static method in NativeInterop class. @@ -672,7 +671,7 @@ bool MAssembly::LoadCorlib() return true; } _hasCachedClasses = false; - assemblyHandles.Add(_handle, this); + CachedAssemblyHandles.Add(_handle, this); // End OnLoaded(startTime); @@ -696,7 +695,7 @@ bool MAssembly::LoadImage(const String& assemblyPath, const StringView& nativePa Log::CLRInnerException(TEXT(".NET assembly image is invalid at ") + assemblyPath); return true; } - assemblyHandles.Add(_handle, this); + CachedAssemblyHandles.Add(_handle, this); // Provide new path of hot-reloaded native library path for managed DllImport if (nativePath.HasChars()) @@ -722,7 +721,7 @@ bool MAssembly::UnloadImage(bool isReloading) CallStaticMethod(CloseAssemblyPtr, _handle); } - assemblyHandles.Remove(_handle); + CachedAssemblyHandles.Remove(_handle); _handle = nullptr; } return false; @@ -780,7 +779,7 @@ MClass::MClass(const MAssembly* parentAssembly, void* handle, const char* name, static void* TypeIsEnumPtr = GetStaticMethodPointer(TEXT("TypeIsEnum")); _isEnum = CallStaticMethod(TypeIsEnumPtr, handle); - classHandles.Add(handle, this); + CachedClassHandles.Add(handle, this); } bool MAssembly::ResolveMissingFile(String& assemblyPath) const @@ -800,7 +799,7 @@ MClass::~MClass() _properties.ClearDelete(); _events.ClearDelete(); - classHandles.Remove(_handle); + CachedClassHandles.Remove(_handle); } StringAnsiView MClass::GetName() const @@ -1018,11 +1017,7 @@ const Array& MClass::GetAttributes() const int numAttributes; static void* GetClassAttributesPtr = GetStaticMethodPointer(TEXT("GetClassAttributes")); CallStaticMethod(GetClassAttributesPtr, _handle, &attributes, &numAttributes); - _attributes.Resize(numAttributes); - for (int i = 0; i < numAttributes; i++) - { - _attributes[i] = attributes[i]; - } + _attributes.Set(attributes, numAttributes); MCore::GC::FreeMemory(attributes); _hasCachedAttributes = true; @@ -1444,7 +1439,7 @@ const Array& MProperty::GetAttributes() const MAssembly* GetAssembly(void* assemblyHandle) { MAssembly* assembly; - if (assemblyHandles.TryGet(assemblyHandle, assembly)) + if (CachedAssemblyHandles.TryGet(assemblyHandle, assembly)) return assembly; return nullptr; } @@ -1452,7 +1447,7 @@ MAssembly* GetAssembly(void* assemblyHandle) MClass* GetClass(MType* typeHandle) { MClass* klass = nullptr; - classHandles.TryGet(typeHandle, klass); + CachedClassHandles.TryGet(typeHandle, klass); return nullptr; } @@ -1461,7 +1456,7 @@ MClass* GetOrCreateClass(MType* typeHandle) if (!typeHandle) return nullptr; MClass* klass; - if (!classHandles.TryGet(typeHandle, klass)) + if (!CachedClassHandles.TryGet(typeHandle, klass)) { NativeClassDefinitions classInfo; void* assemblyHandle; From 144b72109b43599bd7cffbf44b8772b06a84b75e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 19 May 2023 13:53:49 +0200 Subject: [PATCH 074/116] Fix crash after scripting hot-reload in editor due to cached class attribute objects #1108 --- Source/Engine/Scripting/ManagedCLR/MCore.h | 5 +++++ Source/Engine/Scripting/Runtime/DotNet.cpp | 11 +++++++++++ Source/Engine/Scripting/Runtime/Mono.cpp | 8 ++++++++ Source/Engine/Scripting/Runtime/None.cpp | 8 ++++++++ Source/Engine/Scripting/Scripting.cpp | 1 + 5 files changed, 33 insertions(+) diff --git a/Source/Engine/Scripting/ManagedCLR/MCore.h b/Source/Engine/Scripting/ManagedCLR/MCore.h index 88124a223..38b58403d 100644 --- a/Source/Engine/Scripting/ManagedCLR/MCore.h +++ b/Source/Engine/Scripting/ManagedCLR/MCore.h @@ -45,6 +45,11 @@ public: /// static void UnloadEngine(); +#if USE_EDITOR + // Called by Scripting in a middle of hot-reload (after unloading modules but before loading them again). + static void OnMidHotReload(); +#endif + public: /// /// Utilities for C# object management. diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index 76f6dd89f..27515f956 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -299,6 +299,17 @@ void MCore::UnloadEngine() ShutdownHostfxr(); } +#if USE_EDITOR + +void MCore::OnMidHotReload() +{ + // Clear any cached class attributes (see https://github.com/FlaxEngine/FlaxEngine/issues/1108) + for (auto e : CachedClassHandles) + e.Value->_attributes.Clear(); +} + +#endif + MObject* MCore::Object::Box(void* value, const MClass* klass) { static void* BoxValuePtr = GetStaticMethodPointer(TEXT("BoxValue")); diff --git a/Source/Engine/Scripting/Runtime/Mono.cpp b/Source/Engine/Scripting/Runtime/Mono.cpp index 54892b11b..ca4ef367b 100644 --- a/Source/Engine/Scripting/Runtime/Mono.cpp +++ b/Source/Engine/Scripting/Runtime/Mono.cpp @@ -714,6 +714,14 @@ void MCore::UnloadEngine() #endif } +#if USE_EDITOR + +void MCore::OnMidHotReload() +{ +} + +#endif + MObject* MCore::Object::Box(void* value, const MClass* klass) { return mono_value_box(mono_domain_get(), klass->GetNative(), value); diff --git a/Source/Engine/Scripting/Runtime/None.cpp b/Source/Engine/Scripting/Runtime/None.cpp index 0c88c519d..c8f8926e5 100644 --- a/Source/Engine/Scripting/Runtime/None.cpp +++ b/Source/Engine/Scripting/Runtime/None.cpp @@ -59,6 +59,14 @@ void MCore::UnloadEngine() MRootDomain = nullptr; } +#if USE_EDITOR + +void MCore::OnMidHotReload() +{ +} + +#endif + MObject* MCore::Object::Box(void* value, const MClass* klass) { return nullptr; diff --git a/Source/Engine/Scripting/Scripting.cpp b/Source/Engine/Scripting/Scripting.cpp index bec0e6c20..d82a05fa9 100644 --- a/Source/Engine/Scripting/Scripting.cpp +++ b/Source/Engine/Scripting/Scripting.cpp @@ -665,6 +665,7 @@ void Scripting::Reload(bool canTriggerSceneReload) modules.Clear(); _nonNativeModules.ClearDelete(); _hasGameModulesLoaded = false; + MCore::OnMidHotReload(); // Give GC a try to cleanup old user objects and the other mess MCore::GC::Collect(); From d5fcdf6edbb33ee85ebd6db5a38112d8f74ea9e3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 20 May 2023 11:11:06 +0200 Subject: [PATCH 075/116] Fix `BitArray::Set` to not be `const` --- Source/Engine/Core/Collections/BitArray.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Core/Collections/BitArray.h b/Source/Engine/Core/Collections/BitArray.h index 6058dc8a5..01238d434 100644 --- a/Source/Engine/Core/Collections/BitArray.h +++ b/Source/Engine/Core/Collections/BitArray.h @@ -214,7 +214,7 @@ public: /// /// The index of the item. /// The value to set. - void Set(int32 index, bool value) const + void Set(int32 index, bool value) { ASSERT(index >= 0 && index < _count); const ItemType offset = index / sizeof(ItemType); From 2ae20c5fc440a9d46b158f01987d38b4551774f8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 21 May 2023 22:36:14 +0200 Subject: [PATCH 076/116] Add formatting to network profiler bytes values --- Source/Editor/Windows/Profiler/Network.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Windows/Profiler/Network.cs b/Source/Editor/Windows/Profiler/Network.cs index 662ccf445..f0b93a03c 100644 --- a/Source/Editor/Windows/Profiler/Network.cs +++ b/Source/Editor/Windows/Profiler/Network.cs @@ -52,7 +52,7 @@ namespace FlaxEditor.Windows.Profiler private static string FormatSampleBytes(float v) { - return (uint)v + " bytes"; + return Utilities.Utils.FormatBytesCount((ulong)v); } /// From 1b8b5853671e14a386c9930bada2a3a9082b4b20 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 21 May 2023 22:38:48 +0200 Subject: [PATCH 077/116] Optimize network replication when no client can receive object --- Source/Engine/Networking/NetworkReplicator.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Networking/NetworkReplicator.cpp b/Source/Engine/Networking/NetworkReplicator.cpp index be7571b80..8ef668fdb 100644 --- a/Source/Engine/Networking/NetworkReplicator.cpp +++ b/Source/Engine/Networking/NetworkReplicator.cpp @@ -1347,6 +1347,15 @@ void NetworkInternal::NetworkReplicatorUpdate() if (item.Role != NetworkObjectRole::OwnedAuthoritative && (!isClient && item.OwnerClientId != NetworkManager::LocalClientId)) continue; // Send replication messages of only owned objects or from other client objects + // Skip serialization of objects that none will receive + if (!isClient) + { + // TODO: per-object relevancy for connected clients (eg. skip replicating actor to far players) + BuildCachedTargets(item); + if (CachedTargets.Count() == 0) + continue; + } + if (item.AsNetworkObject) item.AsNetworkObject->OnNetworkSerialize(); @@ -1400,8 +1409,6 @@ void NetworkInternal::NetworkReplicatorUpdate() peer->EndSendMessage(NetworkChannelType::Unreliable, msg); else { - // TODO: per-object relevancy for connected clients (eg. skip replicating actor to far players) - BuildCachedTargets(item); peer->EndSendMessage(NetworkChannelType::Unreliable, msg, CachedTargets); } From 2881ca17a09f98ac1bbb4c3c643d736974f2f40c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 22 May 2023 18:06:08 +0200 Subject: [PATCH 078/116] Fix bindings code instance object param `obj` to `__obj` to prevent name collisions --- .../Bindings/BindingsGenerator.CSharp.cs | 2 +- .../Bindings/BindingsGenerator.Cpp.cs | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 6f7cba455..1495c7e70 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -450,7 +450,7 @@ namespace Flax.Build.Bindings var separator = false; if (!functionInfo.IsStatic) { - contents.Append("IntPtr obj"); + contents.Append("IntPtr __obj"); separator = true; } diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 9acaf1f9c..826d77248 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -948,7 +948,7 @@ namespace Flax.Build.Bindings var separator = false; if (!functionInfo.IsStatic) { - contents.Append(string.Format("{0}* obj", caller.Name)); + contents.Append(string.Format("{0}* __obj", caller.Name)); separator = true; } @@ -1025,7 +1025,7 @@ namespace Flax.Build.Bindings contents.AppendLine(); contents.AppendLine(" {"); if (!functionInfo.IsStatic) - contents.AppendLine(" if (obj == nullptr) DebugLog::ThrowNullReference();"); + contents.AppendLine(" if (__obj == nullptr) DebugLog::ThrowNullReference();"); string callBegin = " "; if (functionInfo.Glue.UseReferenceForResult) @@ -1052,7 +1052,7 @@ namespace Flax.Build.Bindings else { // Call native member method - call = $"obj->{functionInfo.Name}"; + call = $"__obj->{functionInfo.Name}"; } string callParams = string.Empty; separator = false; @@ -1760,7 +1760,7 @@ namespace Flax.Build.Bindings continue; var paramsCount = eventInfo.Type.GenericArgs?.Count ?? 0; CppIncludeFiles.Add("Engine/Profiler/ProfilerCPU.h"); - var bindPrefix = eventInfo.IsStatic ? classTypeNameNative + "::" : "obj->"; + var bindPrefix = eventInfo.IsStatic ? classTypeNameNative + "::" : "__obj->"; if (useCSharp) { @@ -1838,7 +1838,7 @@ namespace Flax.Build.Bindings CppInternalCalls.Add(new KeyValuePair(eventInfo.Name + "_Bind", eventInfo.Name + "_ManagedBind")); contents.AppendFormat(" static void {0}_ManagedBind(", eventInfo.Name); if (!eventInfo.IsStatic) - contents.AppendFormat("{0}* obj, ", classTypeNameNative); + contents.AppendFormat("{0}* __obj, ", classTypeNameNative); contents.Append("bool bind)").AppendLine(); contents.Append(" {").AppendLine(); contents.Append(" Function();", eventInfo.Name).AppendLine(); else - contents.AppendFormat(" f.Bind<{1}Internal, &{1}Internal::{0}_ManagedWrapper>(({1}Internal*)obj);", eventInfo.Name, classTypeNameInternal).AppendLine(); + contents.AppendFormat(" f.Bind<{1}Internal, &{1}Internal::{0}_ManagedWrapper>(({1}Internal*)__obj);", eventInfo.Name, classTypeNameInternal).AppendLine(); contents.Append(" if (bind)").AppendLine(); contents.AppendFormat(" {0}{1}.Bind(f);", bindPrefix, eventInfo.Name).AppendLine(); contents.Append(" else").AppendLine(); @@ -1890,7 +1890,7 @@ namespace Flax.Build.Bindings // Scripting event wrapper binding method (binds/unbinds generic wrapper to C++ delegate) contents.AppendFormat(" static void {0}_Bind(", eventInfo.Name); - contents.AppendFormat("{0}* obj, void* instance, bool bind)", classTypeNameNative).AppendLine(); + contents.AppendFormat("{0}* __obj, void* instance, bool bind)", classTypeNameNative).AppendLine(); contents.Append(" {").AppendLine(); contents.Append(" Function();"); - contents.AppendLine(" wrapper->Object = obj;"); + contents.AppendLine(" wrapper->Object = __obj;"); contents.AppendLine(" return wrapper;"); contents.AppendLine(" }"); From f5adbc08fa522bba62fb59dd3c643ec86cb86b2b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 23 May 2023 16:08:51 +0200 Subject: [PATCH 079/116] Remove unused `Function::TryCall` --- Source/Engine/Core/Delegate.h | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/Source/Engine/Core/Delegate.h b/Source/Engine/Core/Delegate.h index 21a4374ea..4fcf60390 100644 --- a/Source/Engine/Core/Delegate.h +++ b/Source/Engine/Core/Delegate.h @@ -203,23 +203,12 @@ public: return _function != nullptr; } - /// - /// Calls the binded function if any has been assigned. - /// - /// A list of parameters for the function invocation. - /// Function result - void TryCall(Params ... params) const - { - if (_function) - _function(_callee, Forward(params)...); - } - /// /// Calls the binded function (it must be assigned). /// /// A list of parameters for the function invocation. /// Function result - ReturnType operator()(Params ... params) const + FORCE_INLINE ReturnType operator()(Params ... params) const { ASSERT(_function); return _function(_callee, Forward(params)...); From 4879b9bd909bb7813b03c1f879287ded74548ba9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 23 May 2023 16:11:34 +0200 Subject: [PATCH 080/116] Add CPU profiler events to various networking functions --- Source/Engine/Networking/NetworkPeer.cpp | 2 + .../Engine/Networking/NetworkReplicator.cpp | 120 ++++++++++-------- 2 files changed, 68 insertions(+), 54 deletions(-) diff --git a/Source/Engine/Networking/NetworkPeer.cpp b/Source/Engine/Networking/NetworkPeer.cpp index 342cba674..d86824156 100644 --- a/Source/Engine/Networking/NetworkPeer.cpp +++ b/Source/Engine/Networking/NetworkPeer.cpp @@ -6,6 +6,7 @@ #include "Engine/Core/Log.h" #include "Engine/Core/Math/Math.h" #include "Engine/Platform/CPUInfo.h" +#include "Engine/Profiler/ProfilerCPU.h" namespace { @@ -131,6 +132,7 @@ void NetworkPeer::Disconnect(const NetworkConnection& connection) bool NetworkPeer::PopEvent(NetworkEvent& eventRef) { + PROFILE_CPU(); return NetworkDriver->PopEvent(&eventRef); } diff --git a/Source/Engine/Networking/NetworkReplicator.cpp b/Source/Engine/Networking/NetworkReplicator.cpp index 8ef668fdb..7ac978bd2 100644 --- a/Source/Engine/Networking/NetworkReplicator.cpp +++ b/Source/Engine/Networking/NetworkReplicator.cpp @@ -1302,28 +1302,31 @@ void NetworkInternal::NetworkReplicatorUpdate() } // Apply parts replication - for (int32 i = ReplicationParts.Count() - 1; i >= 0; i--) { - auto& e = ReplicationParts[i]; - if (e.PartsLeft > 0) + PROFILE_CPU_NAMED("ReplicationParts"); + for (int32 i = ReplicationParts.Count() - 1; i >= 0; i--) { - // TODO: remove replication items after some TTL to prevent memory leaks - continue; - } - ScriptingObject* obj = e.Object.Get(); - if (obj) - { - auto it = Objects.Find(obj->GetID()); - if (it != Objects.End()) + auto& e = ReplicationParts[i]; + if (e.PartsLeft > 0) { - auto& item = it->Item; - - // Replicate from all collected parts data - InvokeObjectReplication(item, e.OwnerFrame, e.Data.Get(), e.Data.Count(), e.OwnerClientId); + // TODO: remove replication items after some TTL to prevent memory leaks + continue; } - } + ScriptingObject* obj = e.Object.Get(); + if (obj) + { + auto it = Objects.Find(obj->GetID()); + if (it != Objects.End()) + { + auto& item = it->Item; - ReplicationParts.RemoveAt(i); + // Replicate from all collected parts data + InvokeObjectReplication(item, e.OwnerFrame, e.Data.Get(), e.Data.Count(), e.OwnerClientId); + } + } + + ReplicationParts.RemoveAt(i); + } } // Brute force synchronize all networked objects with clients @@ -1438,49 +1441,52 @@ void NetworkInternal::NetworkReplicatorUpdate() } // Invoke RPCs - for (auto& e : RpcQueue) { - ScriptingObject* obj = e.Object.Get(); - if (!obj) - continue; - auto it = Objects.Find(obj->GetID()); - if (it == Objects.End()) - continue; - auto& item = it->Item; + PROFILE_CPU_NAMED("Rpc"); + for (auto& e : RpcQueue) + { + ScriptingObject* obj = e.Object.Get(); + if (!obj) + continue; + auto it = Objects.Find(obj->GetID()); + if (it == Objects.End()) + continue; + auto& item = it->Item; - // Send despawn message - //NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Rpc {}::{} object ID={}", e.Name.First.ToString(), String(e.Name.Second), item.ToString()); - NetworkMessageObjectRpc msgData; - msgData.ObjectId = item.ObjectId; - if (isClient) - { - // Remap local client object ids into server ids - IdsRemappingTable.KeyOf(msgData.ObjectId, &msgData.ObjectId); - } - GetNetworkName(msgData.RpcTypeName, e.Name.First.GetType().Fullname); - GetNetworkName(msgData.RpcName, e.Name.Second); - msgData.ArgsSize = (uint16)e.ArgsData.Length(); - NetworkMessage msg = peer->BeginSendMessage(); - msg.WriteStructure(msgData); - msg.WriteBytes(e.ArgsData.Get(), e.ArgsData.Length()); - NetworkChannelType channel = (NetworkChannelType)e.Info.Channel; - if (e.Info.Server && isClient) - { - // Client -> Server + // Send despawn message + //NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Rpc {}::{} object ID={}", e.Name.First.ToString(), String(e.Name.Second), item.ToString()); + NetworkMessageObjectRpc msgData; + msgData.ObjectId = item.ObjectId; + if (isClient) + { + // Remap local client object ids into server ids + IdsRemappingTable.KeyOf(msgData.ObjectId, &msgData.ObjectId); + } + GetNetworkName(msgData.RpcTypeName, e.Name.First.GetType().Fullname); + GetNetworkName(msgData.RpcName, e.Name.Second); + msgData.ArgsSize = (uint16)e.ArgsData.Length(); + NetworkMessage msg = peer->BeginSendMessage(); + msg.WriteStructure(msgData); + msg.WriteBytes(e.ArgsData.Get(), e.ArgsData.Length()); + NetworkChannelType channel = (NetworkChannelType)e.Info.Channel; + if (e.Info.Server && isClient) + { + // Client -> Server #if USE_NETWORK_REPLICATOR_LOG - if (e.Targets.Length() != 0) - NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Server RPC '{}::{}' called with non-empty list of targets is not supported (only server will receive it)", e.Name.First.ToString(), e.Name.Second.ToString()); + if (e.Targets.Length() != 0) + NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Server RPC '{}::{}' called with non-empty list of targets is not supported (only server will receive it)", e.Name.First.ToString(), e.Name.Second.ToString()); #endif - peer->EndSendMessage(channel, msg); - } - else if (e.Info.Client && (isServer || isHost)) - { - // Server -> Client(s) - BuildCachedTargets(NetworkManager::Clients, item.TargetClientIds, e.Targets, NetworkManager::LocalClientId); - peer->EndSendMessage(channel, msg, CachedTargets); + peer->EndSendMessage(channel, msg); + } + else if (e.Info.Client && (isServer || isHost)) + { + // Server -> Client(s) + BuildCachedTargets(NetworkManager::Clients, item.TargetClientIds, e.Targets, NetworkManager::LocalClientId); + peer->EndSendMessage(channel, msg, CachedTargets); + } } + RpcQueue.Clear(); } - RpcQueue.Clear(); // Clear networked objects mapping table Scripting::ObjectsLookupIdMapping.Set(nullptr); @@ -1488,6 +1494,7 @@ void NetworkInternal::NetworkReplicatorUpdate() void NetworkInternal::OnNetworkMessageObjectReplicate(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer) { + PROFILE_CPU(); NetworkMessageObjectReplicate msgData; event.Message.ReadStructure(msgData); ScopeLock lock(ObjectsLock); @@ -1519,6 +1526,7 @@ void NetworkInternal::OnNetworkMessageObjectReplicate(NetworkEvent& event, Netwo void NetworkInternal::OnNetworkMessageObjectReplicatePart(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer) { + PROFILE_CPU(); NetworkMessageObjectReplicatePart msgData; event.Message.ReadStructure(msgData); ScopeLock lock(ObjectsLock); @@ -1531,6 +1539,7 @@ void NetworkInternal::OnNetworkMessageObjectReplicatePart(NetworkEvent& event, N void NetworkInternal::OnNetworkMessageObjectSpawn(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer) { + PROFILE_CPU(); NetworkMessageObjectSpawn msgData; event.Message.ReadStructure(msgData); auto* msgDataItems = (NetworkMessageObjectSpawnItem*)event.Message.SkipBytes(msgData.ItemsCount * sizeof(NetworkMessageObjectSpawnItem)); @@ -1759,6 +1768,7 @@ void NetworkInternal::OnNetworkMessageObjectSpawn(NetworkEvent& event, NetworkCl void NetworkInternal::OnNetworkMessageObjectDespawn(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer) { + PROFILE_CPU(); NetworkMessageObjectDespawn msgData; event.Message.ReadStructure(msgData); ScopeLock lock(ObjectsLock); @@ -1790,6 +1800,7 @@ void NetworkInternal::OnNetworkMessageObjectDespawn(NetworkEvent& event, Network void NetworkInternal::OnNetworkMessageObjectRole(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer) { + PROFILE_CPU(); NetworkMessageObjectRole msgData; event.Message.ReadStructure(msgData); ScopeLock lock(ObjectsLock); @@ -1833,6 +1844,7 @@ void NetworkInternal::OnNetworkMessageObjectRole(NetworkEvent& event, NetworkCli void NetworkInternal::OnNetworkMessageObjectRpc(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer) { + PROFILE_CPU(); NetworkMessageObjectRpc msgData; event.Message.ReadStructure(msgData); ScopeLock lock(ObjectsLock); From 914f7b842e45d4913bec92be001f340db4959b83 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 23 May 2023 18:22:23 +0200 Subject: [PATCH 081/116] Optimize interface method lookup to eliminate` strlen` --- Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 826d77248..41887c347 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -2339,7 +2339,7 @@ namespace Flax.Build.Bindings contents.AppendLine(" auto typeHandle = Object->GetTypeHandle();"); contents.AppendLine(" while (typeHandle)"); contents.AppendLine(" {"); - contents.AppendLine($" auto method = typeHandle.Module->FindMethod(typeHandle, \"{functionInfo.Name}\", {functionInfo.Parameters.Count});"); + contents.AppendLine($" auto method = typeHandle.Module->FindMethod(typeHandle, StringAnsiView(\"{functionInfo.Name}\", {functionInfo.Name.Length}), {functionInfo.Parameters.Count});"); contents.AppendLine(" if (method)"); contents.AppendLine(" {"); contents.AppendLine(" Variant __result;"); From 8ba17f10267d6951eecea47b246c86e02ba41b94 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 23 May 2023 18:22:43 +0200 Subject: [PATCH 082/116] Optimize `MUtils::UnboxScriptingTypeHandle` --- Source/Engine/Scripting/ManagedCLR/MUtils.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Scripting/ManagedCLR/MUtils.cpp b/Source/Engine/Scripting/ManagedCLR/MUtils.cpp index 3d8e93250..bdc16cbd5 100644 --- a/Source/Engine/Scripting/ManagedCLR/MUtils.cpp +++ b/Source/Engine/Scripting/ManagedCLR/MUtils.cpp @@ -191,10 +191,9 @@ ScriptingTypeHandle MUtils::UnboxScriptingTypeHandle(MonoReflectionType* value) MonoClass* klass = GetClass(value); if (!klass) return ScriptingTypeHandle(); - const MString typeName = MUtils::GetClassFullname(klass); - const ScriptingTypeHandle typeHandle = Scripting::FindScriptingType(typeName); + const ScriptingTypeHandle typeHandle = ManagedBinaryModule::FindType(klass); if (!typeHandle) - LOG(Warning, "Unknown scripting type {}", String(typeName)); + LOG(Warning, "Unknown scripting type {}", String(MUtils::GetClassFullname(klass))); return typeHandle; } From eb2fc0f02fa469b9b82a99f24b70407a657fb80d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 24 May 2023 09:46:38 +0200 Subject: [PATCH 083/116] Add **Network Replication Hierarchy** for robust control over replication in multiplayer games --- .../NetworkReplicationHierarchy.cpp | 199 ++++++++++++ .../Networking/NetworkReplicationHierarchy.cs | 25 ++ .../Networking/NetworkReplicationHierarchy.h | 258 +++++++++++++++ .../Engine/Networking/NetworkReplicator.cpp | 300 ++++++++++++------ Source/Engine/Networking/NetworkReplicator.h | 11 + Source/Engine/Networking/Types.h | 1 + 6 files changed, 691 insertions(+), 103 deletions(-) create mode 100644 Source/Engine/Networking/NetworkReplicationHierarchy.cpp create mode 100644 Source/Engine/Networking/NetworkReplicationHierarchy.cs create mode 100644 Source/Engine/Networking/NetworkReplicationHierarchy.h diff --git a/Source/Engine/Networking/NetworkReplicationHierarchy.cpp b/Source/Engine/Networking/NetworkReplicationHierarchy.cpp new file mode 100644 index 000000000..783992fd8 --- /dev/null +++ b/Source/Engine/Networking/NetworkReplicationHierarchy.cpp @@ -0,0 +1,199 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +#include "NetworkReplicationHierarchy.h" +#include "NetworkManager.h" +#include "Engine/Level/Actor.h" +#include "Engine/Level/SceneObject.h" + +uint16 NetworkReplicationNodeObjectCounter = 0; +NetworkClientsMask NetworkClientsMask::All = { MAX_uint64, MAX_uint64 }; + +Actor* NetworkReplicationHierarchyObject::GetActor() const +{ + auto* actor = ScriptingObject::Cast(Object); + if (!actor) + { + if (const auto* sceneObject = ScriptingObject::Cast(Object)) + actor = sceneObject->GetParent(); + } + return actor; +} + +void NetworkReplicationHierarchyUpdateResult::Init() +{ + _clientsHaveLocation = false; + _clients.Resize(NetworkManager::Clients.Count()); + _clientsMask = NetworkClientsMask(); + for (int32 i = 0; i < _clients.Count(); i++) + _clientsMask.SetBit(i); + _entries.Clear(); + ReplicationScale = 1.0f; +} + +void NetworkReplicationHierarchyUpdateResult::SetClientLocation(int32 clientIndex, const Vector3& location) +{ + CHECK(clientIndex >= 0 && clientIndex < _clients.Count()); + _clientsHaveLocation = true; + Client& client = _clients[clientIndex]; + client.HasLocation = true; + client.Location = location; +} + +bool NetworkReplicationHierarchyUpdateResult::GetClientLocation(int32 clientIndex, Vector3& location) const +{ + CHECK_RETURN(clientIndex >= 0 && clientIndex < _clients.Count(), false); + const Client& client = _clients[clientIndex]; + location = client.Location; + return client.HasLocation; +} + +void NetworkReplicationNode::AddObject(NetworkReplicationHierarchyObject obj) +{ + ASSERT(obj.Object && obj.ReplicationFPS > 0.0f); + + // Randomize initial replication update to spread rep rates more evenly for large scenes that register all objects within the same frame + obj.ReplicationUpdatesLeft = NetworkReplicationNodeObjectCounter++ % Math::Clamp(Math::RoundToInt(NetworkManager::NetworkFPS / obj.ReplicationFPS), 1, 60); + + Objects.Add(obj); +} + +bool NetworkReplicationNode::RemoveObject(ScriptingObject* obj) +{ + return !Objects.Remove(obj); +} + +bool NetworkReplicationNode::DirtyObject(ScriptingObject* obj) +{ + const int32 index = Objects.Find(obj); + if (index != -1) + { + NetworkReplicationHierarchyObject& e = Objects[index]; + e.ReplicationUpdatesLeft = 0; + } + return index != -1; +} + +void NetworkReplicationNode::Update(NetworkReplicationHierarchyUpdateResult* result) +{ + CHECK(result); + const float networkFPS = NetworkManager::NetworkFPS / result->ReplicationScale; + for (NetworkReplicationHierarchyObject& obj : Objects) + { + if (obj.ReplicationUpdatesLeft > 0) + { + // Move to the next frame + obj.ReplicationUpdatesLeft--; + } + else + { + NetworkClientsMask targetClients = result->GetClientsMask(); + if (result->_clientsHaveLocation) + { + // Cull object against viewers locations + if (const Actor* actor = obj.GetActor()) + { + const Vector3 objPosition = actor->GetPosition(); + const Real cullDistanceSq = Math::Square(obj.CullDistance); + for (int32 clientIndex = 0; clientIndex < result->_clients.Count(); clientIndex++) + { + const auto& client = result->_clients[clientIndex]; + if (client.HasLocation) + { + const Real distanceSq = Vector3::DistanceSquared(objPosition, client.Location); + // TODO: scale down replication FPS when object is far away from all clients (eg. by 10-50%) + if (distanceSq >= cullDistanceSq) + { + // Object is too far from this viewer so don't send data to him + targetClients.UnsetBit(clientIndex); + } + } + } + } + } + if (targetClients) + { + // Replicate this frame + result->AddObject(obj.Object, targetClients); + } + + // Calculate frames until next replication + obj.ReplicationUpdatesLeft = (uint16)Math::Clamp(Math::RoundToInt(networkFPS / obj.ReplicationFPS) - 1, 0, MAX_uint16); + } + } +} + +NetworkReplicationGridNode::~NetworkReplicationGridNode() +{ + for (const auto& e : _children) + Delete(e.Value.Node); +} + +void NetworkReplicationGridNode::AddObject(NetworkReplicationHierarchyObject obj) +{ + // Chunk actors locations into a grid coordinates + Int3 coord = Int3::Zero; + if (const Actor* actor = obj.GetActor()) + { + coord = actor->GetPosition() / CellSize; + } + + Cell* cell = _children.TryGet(coord); + if (!cell) + { + // Allocate new cell + cell = &_children[coord]; + cell->Node = New(); + cell->MinCullDistance = obj.CullDistance; + } + cell->Node->AddObject(obj); + + // Cache minimum culling distance for a whole cell to skip it at once + cell->MinCullDistance = Math::Min(cell->MinCullDistance, obj.CullDistance); +} + +bool NetworkReplicationGridNode::RemoveObject(ScriptingObject* obj) +{ + for (const auto& e : _children) + { + if (e.Value.Node->RemoveObject(obj)) + { + // TODO: remove empty cells? + // TODO: update MinCullDistance for cell? + return true; + } + } + return false; +} + +void NetworkReplicationGridNode::Update(NetworkReplicationHierarchyUpdateResult* result) +{ + CHECK(result); + if (result->_clientsHaveLocation) + { + // Update only cells within a range + const Real cellRadiusSq = Math::Square(CellSize * 1.414f); + for (const auto& e : _children) + { + const Vector3 cellPosition = (e.Key * CellSize) + (CellSize * 0.5f); + Real distanceSq = MAX_Real; + for (auto& client : result->_clients) + { + if (client.HasLocation) + distanceSq = Math::Min(distanceSq, Vector3::DistanceSquared(cellPosition, client.Location)); + } + const Real minCullDistanceSq = Math::Square(e.Value.MinCullDistance); + if (distanceSq < minCullDistanceSq + cellRadiusSq) + { + e.Value.Node->Update(result); + } + } + } + else + { + // Brute-force over all cells + for (const auto& e : _children) + { + e.Value.Node->Update(result); + } + } +} diff --git a/Source/Engine/Networking/NetworkReplicationHierarchy.cs b/Source/Engine/Networking/NetworkReplicationHierarchy.cs new file mode 100644 index 000000000..f03419275 --- /dev/null +++ b/Source/Engine/Networking/NetworkReplicationHierarchy.cs @@ -0,0 +1,25 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +namespace FlaxEngine.Networking +{ + partial struct NetworkReplicationHierarchyObject + { + /// + /// Gets the actors context (object itself or parent actor). + /// + public Actor Actor + { + get + { + var actor = Object as Actor; + if (actor == null) + { + var sceneObject = Object as SceneObject; + if (sceneObject != null) + actor = sceneObject.Parent; + } + return actor; + } + } + } +} diff --git a/Source/Engine/Networking/NetworkReplicationHierarchy.h b/Source/Engine/Networking/NetworkReplicationHierarchy.h new file mode 100644 index 000000000..7d6db336d --- /dev/null +++ b/Source/Engine/Networking/NetworkReplicationHierarchy.h @@ -0,0 +1,258 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Types.h" +#include "Engine/Core/Math/Vector3.h" +#include "Engine/Core/Collections/Array.h" +#include "Engine/Core/Collections/Dictionary.h" +#include "Engine/Scripting/ScriptingObject.h" +#include "Engine/Scripting/ScriptingObjectReference.h" + +class Actor; + +/// +/// Network replication hierarchy object data. +/// +API_STRUCT(NoDefault, Namespace = "FlaxEngine.Networking") struct FLAXENGINE_API NetworkReplicationHierarchyObject +{ + DECLARE_SCRIPTING_TYPE_MINIMAL(NetworkReplicationObjectInfo); + // The object to replicate. + API_FIELD() ScriptingObject* Object; + // The target amount of the replication updates per second (frequency of the replication). Constrained by NetworkManager::NetworkFPS. + API_FIELD() float ReplicationFPS = 60; + // The minimum distance from the player to the object at which it can process replication. For example, players further away won't receive object data. + API_FIELD() float CullDistance = 15000; + // Runtime value for update frames left for the next replication of this object. Matches NetworkManager::NetworkFPS calculated from ReplicationFPS. + API_FIELD(Attributes="HideInEditor") uint16 ReplicationUpdatesLeft = 0; + + FORCE_INLINE NetworkReplicationHierarchyObject(const ScriptingObjectReference& obj) + : Object(obj.Get()) + { + } + + FORCE_INLINE NetworkReplicationHierarchyObject(ScriptingObject* obj = nullptr) + : Object(obj) + { + } + + // Gets the actors context (object itself or parent actor). + Actor* GetActor() const; + + bool operator==(const NetworkReplicationHierarchyObject& other) const + { + return Object == other.Object; + } + + bool operator==(const ScriptingObject* other) const + { + return Object == other; + } +}; + +inline uint32 GetHash(const NetworkReplicationHierarchyObject& key) +{ + return GetHash(key.Object); +} + +/// +/// Bit mask for NetworkClient list (eg. to selectively send object replication). +/// +API_STRUCT(NoDefault, Namespace = "FlaxEngine.Networking") struct FLAXENGINE_API NetworkClientsMask +{ + DECLARE_SCRIPTING_TYPE_MINIMAL(NetworkClientsMask); + // The first 64 bits (each for one client). + API_FIELD() uint64 Word0 = 0; + // The second 64 bits (each for one client). + API_FIELD() uint64 Word1 = 0; + + // All bits set for all clients. + API_FIELD() static NetworkClientsMask All; + + FORCE_INLINE bool HasBit(int32 bitIndex) const + { + const int32 wordIndex = bitIndex / 64; + const uint64 wordMask = 1ull << (uint64)(bitIndex - wordIndex * 64); + const uint64 word = *(&Word0 + wordIndex); + return (word & wordMask) == wordMask; + } + + FORCE_INLINE void SetBit(int32 bitIndex) + { + const int32 wordIndex = bitIndex / 64; + const uint64 wordMask = 1ull << (uint64)(bitIndex - wordIndex * 64); + uint64& word = *(&Word0 + wordIndex); + word |= wordMask; + } + + FORCE_INLINE void UnsetBit(int32 bitIndex) + { + const int32 wordIndex = bitIndex / 64; + const uint64 wordMask = 1ull << (uint64)(bitIndex - wordIndex * 64); + uint64& word = *(&Word0 + wordIndex); + word &= ~wordMask; + } + + FORCE_INLINE operator bool() const + { + return Word0 + Word1 != 0; + } + + bool operator==(const NetworkClientsMask& other) const + { + return Word0 == other.Word0 && Word1 == other.Word1; + } +}; + +/// +/// Network replication hierarchy output data to send. +/// +API_CLASS(Namespace = "FlaxEngine.Networking") class FLAXENGINE_API NetworkReplicationHierarchyUpdateResult : public ScriptingObject +{ + DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(NetworkReplicationHierarchyUpdateResult, ScriptingObject); + friend class NetworkInternal; + friend class NetworkReplicationNode; + friend class NetworkReplicationGridNode; + +private: + struct Client + { + bool HasLocation; + Vector3 Location; + }; + + struct Entry + { + ScriptingObject* Object; + NetworkClientsMask TargetClients; + }; + + bool _clientsHaveLocation; + NetworkClientsMask _clientsMask; + Array _clients; + Array _entries; + + void Init(); + +public: + // Scales the ReplicationFPS property of objects in hierarchy. Can be used to slow down or speed up replication rate. + API_FIELD() float ReplicationScale = 1.0f; + + // Adds object to the update results. + API_FUNCTION() void AddObject(ScriptingObject* obj) + { + Entry& e = _entries.AddOne(); + e.Object = obj; + e.TargetClients = NetworkClientsMask::All; + } + + // Adds object to the update results. Defines specific clients to receive the update (server-only, unused on client). Mask matches NetworkManager::Clients. + API_FUNCTION() void AddObject(ScriptingObject* obj, NetworkClientsMask targetClients) + { + Entry& e = _entries.AddOne(); + e.Object = obj; + e.TargetClients = targetClients; + } + + // Gets amount of the clients to use. Matches NetworkManager::Clients. + API_PROPERTY() int32 GetClientsCount() const + { + return _clients.Count(); + } + + // Gets mask with all client bits set. Matches NetworkManager::Clients. + API_PROPERTY() NetworkClientsMask GetClientsMask() const + { + return _clientsMask; + } + + // Sets the viewer location for a certain client. Client index must match NetworkManager::Clients. + API_FUNCTION() void SetClientLocation(int32 clientIndex, const Vector3& location); + + // Gets the viewer location for a certain client. Client index must match NetworkManager::Clients. Returns true if got a location set, otherwise false. + API_FUNCTION() bool GetClientLocation(int32 clientIndex, API_PARAM(out) Vector3& location) const; +}; + +/// +/// Base class for the network objects replication hierarchy nodes. Contains a list of objects. +/// +API_CLASS(Abstract, Namespace = "FlaxEngine.Networking") class FLAXENGINE_API NetworkReplicationNode : public ScriptingObject +{ + DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(NetworkReplicationNode, ScriptingObject); + + /// + /// List with objects stored in this node. + /// + API_FIELD() Array Objects; + + /// + /// Adds an object into the hierarchy. + /// + /// The object to add. + API_FUNCTION() virtual void AddObject(NetworkReplicationHierarchyObject obj); + + /// + /// Removes object from the hierarchy. + /// + /// The object to remove. + /// True on successful removal, otherwise false. + API_FUNCTION() virtual bool RemoveObject(ScriptingObject* obj); + + /// + /// Force replicates the object during the next update. Resets any internal tracking state to force the synchronization. + /// + /// The object to update. + /// True on successful update, otherwise false. + API_FUNCTION() virtual bool DirtyObject(ScriptingObject* obj); + + /// + /// Iterates over all objects and adds them to the replication work. + /// + /// The update results container. + API_FUNCTION() virtual void Update(NetworkReplicationHierarchyUpdateResult* result); +}; + +inline uint32 GetHash(const Int3& key) +{ + uint32 hash = GetHash(key.X); + CombineHash(hash, GetHash(key.Y)); + CombineHash(hash, GetHash(key.Z)); + return hash; +} + +/// +/// Network replication hierarchy node with 3D grid spatialization. Organizes static objects into chunks to improve performance in large worlds. +/// +API_CLASS(Namespace = "FlaxEngine.Networking") class FLAXENGINE_API NetworkReplicationGridNode : public NetworkReplicationNode +{ + DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(NetworkReplicationGridNode, NetworkReplicationNode); + ~NetworkReplicationGridNode(); + +private: + struct Cell + { + NetworkReplicationNode* Node; + float MinCullDistance; + }; + + Dictionary _children; + +public: + /// + /// Size of the grid cell (in world units). Used to chunk the space for separate nodes. + /// + API_FIELD() float CellSize = 10000.0f; + + void AddObject(NetworkReplicationHierarchyObject obj) override; + bool RemoveObject(ScriptingObject* obj) override; + void Update(NetworkReplicationHierarchyUpdateResult* result) override; +}; + +/// +/// Defines the network objects replication hierarchy (tree structure) that controls chunking and configuration of the game objects replication. +/// Contains only 'owned' objects. It's used by the networking system only on a main thread. +/// +API_CLASS(Namespace = "FlaxEngine.Networking") class FLAXENGINE_API NetworkReplicationHierarchy : public NetworkReplicationNode +{ + DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(NetworkReplicationHierarchy, NetworkReplicationNode); +}; diff --git a/Source/Engine/Networking/NetworkReplicator.cpp b/Source/Engine/Networking/NetworkReplicator.cpp index 7ac978bd2..b1088bfa5 100644 --- a/Source/Engine/Networking/NetworkReplicator.cpp +++ b/Source/Engine/Networking/NetworkReplicator.cpp @@ -12,6 +12,7 @@ #include "NetworkRpc.h" #include "INetworkSerializable.h" #include "INetworkObject.h" +#include "NetworkReplicationHierarchy.h" #include "Engine/Core/Collections/HashSet.h" #include "Engine/Core/Collections/Dictionary.h" #include "Engine/Core/Collections/ChunkedArray.h" @@ -199,6 +200,8 @@ namespace Dictionary IdsRemappingTable; NetworkStream* CachedWriteStream = nullptr; NetworkStream* CachedReadStream = nullptr; + NetworkReplicationHierarchyUpdateResult* CachedReplicationResult = nullptr; + NetworkReplicationHierarchy* Hierarchy = nullptr; Array NewClients; Array CachedTargets; Dictionary SerializersTable; @@ -307,14 +310,15 @@ void BuildCachedTargets(const Array& clients, const NetworkClien } } -void BuildCachedTargets(const Array& clients, const DataContainer& clientIds, const uint32 excludedClientId = NetworkManager::ServerClientId) +void BuildCachedTargets(const Array& clients, const DataContainer& clientIds, const uint32 excludedClientId = NetworkManager::ServerClientId, const NetworkClientsMask clientsMask = NetworkClientsMask::All) { CachedTargets.Clear(); if (clientIds.IsValid()) { - for (const NetworkClient* client : clients) + for (int32 clientIndex = 0; clientIndex < clients.Count(); clientIndex++) { - if (client->State == NetworkConnectionState::Connected && client->ClientId != excludedClientId) + const NetworkClient* client = clients.Get()[clientIndex]; + if (client->State == NetworkConnectionState::Connected && client->ClientId != excludedClientId && clientsMask.HasBit(clientIndex)) { for (int32 i = 0; i < clientIds.Length(); i++) { @@ -329,9 +333,10 @@ void BuildCachedTargets(const Array& clients, const DataContaine } else { - for (const NetworkClient* client : clients) + for (int32 clientIndex = 0; clientIndex < clients.Count(); clientIndex++) { - if (client->State == NetworkConnectionState::Connected && client->ClientId != excludedClientId) + const NetworkClient* client = clients.Get()[clientIndex]; + if (client->State == NetworkConnectionState::Connected && client->ClientId != excludedClientId && clientsMask.HasBit(clientIndex)) CachedTargets.Add(client->Connection); } } @@ -377,10 +382,10 @@ void BuildCachedTargets(const Array& clients, const DataContaine } } -FORCE_INLINE void BuildCachedTargets(const NetworkReplicatedObject& item) +FORCE_INLINE void BuildCachedTargets(const NetworkReplicatedObject& item, const NetworkClientsMask clientsMask = NetworkClientsMask::All) { // By default send object to all connected clients excluding the owner but with optional TargetClientIds list - BuildCachedTargets(NetworkManager::Clients, item.TargetClientIds, item.OwnerClientId); + BuildCachedTargets(NetworkManager::Clients, item.TargetClientIds, item.OwnerClientId, clientsMask); } FORCE_INLINE void GetNetworkName(char buffer[128], const StringAnsiView& name) @@ -561,9 +566,10 @@ void FindObjectsForSpawn(SpawnGroup& group, ChunkedArray& spawnI } } -void DirtyObjectImpl(NetworkReplicatedObject& item, ScriptingObject* obj) +FORCE_INLINE void DirtyObjectImpl(NetworkReplicatedObject& item, ScriptingObject* obj) { - // TODO: implement objects state replication frequency and dirtying + if (Hierarchy) + Hierarchy->DirtyObject(obj); } template @@ -703,6 +709,34 @@ StringAnsiView NetworkReplicator::GetCSharpCachedName(const StringAnsiView& name #endif +NetworkReplicationHierarchy* NetworkReplicator::GetHierarchy() +{ + return Hierarchy; +} + +void NetworkReplicator::SetHierarchy(NetworkReplicationHierarchy* value) +{ + ScopeLock lock(ObjectsLock); + if (Hierarchy == value) + return; + NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Set hierarchy to '{}'", value ? value->ToString() : String::Empty); + if (Hierarchy) + { + // Clear old hierarchy + Delete(Hierarchy); + } + Hierarchy = value; + if (value) + { + // Add all owned objects to the hierarchy + for (auto& e : Objects) + { + if (e.Item.Object && e.Item.Role == NetworkObjectRole::OwnedAuthoritative) + value->AddObject(e.Item.Object); + } + } +} + void NetworkReplicator::AddSerializer(const ScriptingTypeHandle& typeHandle, SerializeFunc serialize, SerializeFunc deserialize, void* serializeTag, void* deserializeTag) { if (!typeHandle) @@ -788,6 +822,8 @@ void NetworkReplicator::AddObject(ScriptingObject* obj, const ScriptingObject* p } } Objects.Add(MoveTemp(item)); + if (Hierarchy && item.Role == NetworkObjectRole::OwnedAuthoritative) + Hierarchy->AddObject(obj); } void NetworkReplicator::RemoveObject(ScriptingObject* obj) @@ -801,6 +837,8 @@ void NetworkReplicator::RemoveObject(ScriptingObject* obj) // Remove object from the list NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Remove object {}, owned by {}", obj->GetID().ToString(), it->Item.ParentId.ToString()); + if (Hierarchy && it->Item.Role == NetworkObjectRole::OwnedAuthoritative) + Hierarchy->RemoveObject(obj); Objects.Remove(it); } @@ -870,6 +908,8 @@ void NetworkReplicator::DespawnObject(ScriptingObject* obj) DespawnedObjects.Add(item.ObjectId); if (item.AsNetworkObject) item.AsNetworkObject->OnNetworkDespawn(); + if (Hierarchy && item.Role == NetworkObjectRole::OwnedAuthoritative) + Hierarchy->RemoveObject(obj); Objects.Remove(it); DeleteNetworkObject(obj); } @@ -1004,6 +1044,8 @@ void NetworkReplicator::SetObjectOwnership(ScriptingObject* obj, uint32 ownerCli { // Change role locally CHECK(localRole != NetworkObjectRole::OwnedAuthoritative); + if (Hierarchy && item.Role == NetworkObjectRole::OwnedAuthoritative) + Hierarchy->RemoveObject(obj); item.OwnerClientId = ownerClientId; item.LastOwnerFrame = 1; item.Role = localRole; @@ -1014,6 +1056,8 @@ void NetworkReplicator::SetObjectOwnership(ScriptingObject* obj, uint32 ownerCli { // Allow to change local role of the object (except ownership) CHECK(localRole != NetworkObjectRole::OwnedAuthoritative); + if (Hierarchy && it->Item.Role == NetworkObjectRole::OwnedAuthoritative) + Hierarchy->RemoveObject(obj); item.Role = localRole; } } @@ -1107,6 +1151,8 @@ void NetworkInternal::NetworkReplicatorClientDisconnected(NetworkClient* client) // Delete object locally NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Despawn object {}", item.ObjectId); + if (Hierarchy && item.Role == NetworkObjectRole::OwnedAuthoritative) + Hierarchy->RemoveObject(obj); if (item.AsNetworkObject) item.AsNetworkObject->OnNetworkDespawn(); DeleteNetworkObject(obj); @@ -1121,6 +1167,7 @@ void NetworkInternal::NetworkReplicatorClear() // Cleanup NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Shutdown"); + NetworkReplicator::SetHierarchy(nullptr); for (auto it = Objects.Begin(); it.IsNotEnd(); ++it) { auto& item = it->Item; @@ -1140,6 +1187,7 @@ void NetworkInternal::NetworkReplicatorClear() IdsRemappingTable.Clear(); SAFE_DELETE(CachedWriteStream); SAFE_DELETE(CachedReadStream); + SAFE_DELETE(CachedReplicationResult); NewClients.Clear(); CachedTargets.Clear(); DespawnedObjects.Clear(); @@ -1268,7 +1316,14 @@ void NetworkInternal::NetworkReplicatorUpdate() if (e.HasOwnership) { - item.Role = e.Role; + if (item.Role != e.Role) + { + if (Hierarchy && item.Role == NetworkObjectRole::OwnedAuthoritative) + Hierarchy->RemoveObject(obj); + item.Role = e.Role; + if (Hierarchy && item.Role == NetworkObjectRole::OwnedAuthoritative) + Hierarchy->AddObject(obj); + } item.OwnerClientId = e.OwnerClientId; if (e.HierarchicalOwnership) NetworkReplicator::SetObjectOwnership(obj, e.OwnerClientId, e.Role, true); @@ -1329,114 +1384,141 @@ void NetworkInternal::NetworkReplicatorUpdate() } } - // Brute force synchronize all networked objects with clients - if (CachedWriteStream == nullptr) - CachedWriteStream = New(); - NetworkStream* stream = CachedWriteStream; - stream->SenderId = NetworkManager::LocalClientId; - // TODO: introduce NetworkReplicationHierarchy to optimize objects replication in large worlds (eg. batched culling networked scene objects that are too far from certain client to be relevant) - // TODO: per-object sync interval (in frames) - could be scaled by hierarchy (eg. game could slow down sync rate for objects far from player) - for (auto it = Objects.Begin(); it.IsNotEnd(); ++it) + // Replicate all owned networked objects with other clients or server + if (!CachedReplicationResult) + CachedReplicationResult = New(); + CachedReplicationResult->Init(); + if (!isClient && NetworkManager::Clients.IsEmpty()) { - auto& item = it->Item; - ScriptingObject* obj = item.Object.Get(); - if (!obj) + // No need to update replication when nobody's around + } + else if (Hierarchy) + { + // Tick using hierarchy + PROFILE_CPU_NAMED("ReplicationHierarchyUpdate"); + Hierarchy->Update(CachedReplicationResult); + } + else + { + // Tick all owned objects + PROFILE_CPU_NAMED("ReplicationUpdate"); + for (auto it = Objects.Begin(); it.IsNotEnd(); ++it) { - // Object got deleted - NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Remove object {}, owned by {}", item.ToString(), item.ParentId.ToString()); - Objects.Remove(it); - continue; - } - if (item.Role != NetworkObjectRole::OwnedAuthoritative && (!isClient && item.OwnerClientId != NetworkManager::LocalClientId)) - continue; // Send replication messages of only owned objects or from other client objects - - // Skip serialization of objects that none will receive - if (!isClient) - { - // TODO: per-object relevancy for connected clients (eg. skip replicating actor to far players) - BuildCachedTargets(item); - if (CachedTargets.Count() == 0) + auto& item = it->Item; + ScriptingObject* obj = item.Object.Get(); + if (!obj) + { + // Object got deleted + NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Remove object {}, owned by {}", item.ToString(), item.ParentId.ToString()); + Objects.Remove(it); continue; + } + if (item.Role != NetworkObjectRole::OwnedAuthoritative) + continue; // Send replication messages of only owned objects or from other client objects + CachedReplicationResult->AddObject(obj); } - - if (item.AsNetworkObject) - item.AsNetworkObject->OnNetworkSerialize(); - - // Serialize object - stream->Initialize(); - const bool failed = NetworkReplicator::InvokeSerializer(obj->GetTypeHandle(), obj, stream, true); - if (failed) + } + if (CachedReplicationResult->_entries.HasItems()) + { + PROFILE_CPU_NAMED("Replication"); + if (CachedWriteStream == nullptr) + CachedWriteStream = New(); + NetworkStream* stream = CachedWriteStream; + stream->SenderId = NetworkManager::LocalClientId; + // TODO: use Job System when replicated objects count is large + for (auto& e : CachedReplicationResult->_entries) { - //NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Cannot serialize object {} of type {} (missing serialization logic)", item.ToString(), obj->GetType().ToString()); - continue; - } + ScriptingObject* obj = e.Object; + auto it = Objects.Find(obj->GetID()); + ASSERT(it.IsNotEnd()); + auto& item = it->Item; - // Send object to clients - { - const uint32 size = stream->GetPosition(); - ASSERT(size <= MAX_uint16) - NetworkMessageObjectReplicate msgData; - msgData.OwnerFrame = NetworkManager::Frame; - msgData.ObjectId = item.ObjectId; - msgData.ParentId = item.ParentId; - if (isClient) + // Skip serialization of objects that none will receive + if (!isClient) { - // Remap local client object ids into server ids - IdsRemappingTable.KeyOf(msgData.ObjectId, &msgData.ObjectId); - IdsRemappingTable.KeyOf(msgData.ParentId, &msgData.ParentId); - } - GetNetworkName(msgData.ObjectTypeName, obj->GetType().Fullname); - msgData.DataSize = size; - const uint32 msgMaxData = peer->Config.MessageSize - sizeof(NetworkMessageObjectReplicate); - const uint32 partMaxData = peer->Config.MessageSize - sizeof(NetworkMessageObjectReplicatePart); - uint32 partsCount = 1; - uint32 dataStart = 0; - uint32 msgDataSize = size; - if (size > msgMaxData) - { - // Send msgMaxData within first message - msgDataSize = msgMaxData; - dataStart += msgMaxData; - - // Send rest of the data in separate parts - partsCount += Math::DivideAndRoundUp(size - dataStart, partMaxData); - } - else - dataStart += size; - ASSERT(partsCount <= MAX_uint8) - msgData.PartsCount = partsCount; - NetworkMessage msg = peer->BeginSendMessage(); - msg.WriteStructure(msgData); - msg.WriteBytes(stream->GetBuffer(), msgDataSize); - if (isClient) - peer->EndSendMessage(NetworkChannelType::Unreliable, msg); - else - { - peer->EndSendMessage(NetworkChannelType::Unreliable, msg, CachedTargets); + // TODO: per-object relevancy for connected clients (eg. skip replicating actor to far players) + BuildCachedTargets(item, e.TargetClients); + if (CachedTargets.Count() == 0) + return; } - // Send all other parts - for (uint32 partIndex = 1; partIndex < partsCount; partIndex++) + if (item.AsNetworkObject) + item.AsNetworkObject->OnNetworkSerialize(); + + // Serialize object + stream->Initialize(); + const bool failed = NetworkReplicator::InvokeSerializer(obj->GetTypeHandle(), obj, stream, true); + if (failed) { - NetworkMessageObjectReplicatePart msgDataPart; - msgDataPart.OwnerFrame = msgData.OwnerFrame; - msgDataPart.ObjectId = msgData.ObjectId; - msgDataPart.DataSize = msgData.DataSize; - msgDataPart.PartsCount = msgData.PartsCount; - msgDataPart.PartStart = dataStart; - msgDataPart.PartSize = Math::Min(size - dataStart, partMaxData); - msg = peer->BeginSendMessage(); - msg.WriteStructure(msgDataPart); - msg.WriteBytes(stream->GetBuffer() + msgDataPart.PartStart, msgDataPart.PartSize); - dataStart += msgDataPart.PartSize; + //NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Cannot serialize object {} of type {} (missing serialization logic)", item.ToString(), obj->GetType().ToString()); + return; + } + + // Send object to clients + { + const uint32 size = stream->GetPosition(); + ASSERT(size <= MAX_uint16) + NetworkMessageObjectReplicate msgData; + msgData.OwnerFrame = NetworkManager::Frame; + msgData.ObjectId = item.ObjectId; + msgData.ParentId = item.ParentId; + if (isClient) + { + // Remap local client object ids into server ids + IdsRemappingTable.KeyOf(msgData.ObjectId, &msgData.ObjectId); + IdsRemappingTable.KeyOf(msgData.ParentId, &msgData.ParentId); + } + GetNetworkName(msgData.ObjectTypeName, obj->GetType().Fullname); + msgData.DataSize = size; + const uint32 msgMaxData = peer->Config.MessageSize - sizeof(NetworkMessageObjectReplicate); + const uint32 partMaxData = peer->Config.MessageSize - sizeof(NetworkMessageObjectReplicatePart); + uint32 partsCount = 1; + uint32 dataStart = 0; + uint32 msgDataSize = size; + if (size > msgMaxData) + { + // Send msgMaxData within first message + msgDataSize = msgMaxData; + dataStart += msgMaxData; + + // Send rest of the data in separate parts + partsCount += Math::DivideAndRoundUp(size - dataStart, partMaxData); + } + else + dataStart += size; + ASSERT(partsCount <= MAX_uint8) + msgData.PartsCount = partsCount; + NetworkMessage msg = peer->BeginSendMessage(); + msg.WriteStructure(msgData); + msg.WriteBytes(stream->GetBuffer(), msgDataSize); if (isClient) peer->EndSendMessage(NetworkChannelType::Unreliable, msg); else peer->EndSendMessage(NetworkChannelType::Unreliable, msg, CachedTargets); - } - ASSERT_LOW_LAYER(dataStart == size); - // TODO: stats for bytes send per object type + // Send all other parts + for (uint32 partIndex = 1; partIndex < partsCount; partIndex++) + { + NetworkMessageObjectReplicatePart msgDataPart; + msgDataPart.OwnerFrame = msgData.OwnerFrame; + msgDataPart.ObjectId = msgData.ObjectId; + msgDataPart.DataSize = msgData.DataSize; + msgDataPart.PartsCount = msgData.PartsCount; + msgDataPart.PartStart = dataStart; + msgDataPart.PartSize = Math::Min(size - dataStart, partMaxData); + msg = peer->BeginSendMessage(); + msg.WriteStructure(msgDataPart); + msg.WriteBytes(stream->GetBuffer() + msgDataPart.PartStart, msgDataPart.PartSize); + dataStart += msgDataPart.PartSize; + if (isClient) + peer->EndSendMessage(NetworkChannelType::Unreliable, msg); + else + peer->EndSendMessage(NetworkChannelType::Unreliable, msg, CachedTargets); + } + ASSERT_LOW_LAYER(dataStart == size); + + // TODO: stats for bytes send per object type + } } } @@ -1564,7 +1646,11 @@ void NetworkInternal::OnNetworkMessageObjectSpawn(NetworkEvent& event, NetworkCl // Server always knows the best so update ownership of the existing object item.OwnerClientId = msgData.OwnerClientId; if (item.Role == NetworkObjectRole::OwnedAuthoritative) + { + if (Hierarchy) + Hierarchy->AddObject(item.Object); item.Role = NetworkObjectRole::Replicated; + } } else if (item.OwnerClientId != msgData.OwnerClientId) { @@ -1719,6 +1805,8 @@ void NetworkInternal::OnNetworkMessageObjectSpawn(NetworkEvent& event, NetworkCl item.Spawned = true; NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Add new object {}:{}, parent {}:{}", item.ToString(), obj->GetType().ToString(), item.ParentId.ToString(), parent ? parent->Object->GetType().ToString() : String::Empty); Objects.Add(MoveTemp(item)); + if (Hierarchy && item.Role == NetworkObjectRole::OwnedAuthoritative) + Hierarchy->AddObject(obj); // Boost future lookups by using indirection NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Remap object ID={} into object {}:{}", msgDataItem.ObjectId, item.ToString(), obj->GetType().ToString()); @@ -1786,6 +1874,8 @@ void NetworkInternal::OnNetworkMessageObjectDespawn(NetworkEvent& event, Network // Remove object NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Despawn object {}", msgData.ObjectId); + if (Hierarchy && item.Role == NetworkObjectRole::OwnedAuthoritative) + Hierarchy->RemoveObject(obj); DespawnedObjects.Add(msgData.ObjectId); if (item.AsNetworkObject) item.AsNetworkObject->OnNetworkDespawn(); @@ -1822,12 +1912,16 @@ void NetworkInternal::OnNetworkMessageObjectRole(NetworkEvent& event, NetworkCli if (item.OwnerClientId == NetworkManager::LocalClientId) { // Upgrade ownership automatically + if (Hierarchy && item.Role != NetworkObjectRole::OwnedAuthoritative) + Hierarchy->AddObject(obj); item.Role = NetworkObjectRole::OwnedAuthoritative; item.LastOwnerFrame = 0; } else if (item.Role == NetworkObjectRole::OwnedAuthoritative) { // Downgrade ownership automatically + if (Hierarchy) + Hierarchy->RemoveObject(obj); item.Role = NetworkObjectRole::Replicated; } if (!NetworkManager::IsClient()) diff --git a/Source/Engine/Networking/NetworkReplicator.h b/Source/Engine/Networking/NetworkReplicator.h index 0e9a2d0d8..931e13afa 100644 --- a/Source/Engine/Networking/NetworkReplicator.h +++ b/Source/Engine/Networking/NetworkReplicator.h @@ -42,6 +42,17 @@ public: API_FIELD() static bool EnableLog; #endif + /// + /// Gets the network replication hierarchy. + /// + API_PROPERTY() static NetworkReplicationHierarchy* GetHierarchy(); + + /// + /// Sets the network replication hierarchy. + /// + API_PROPERTY() static void SetHierarchy(NetworkReplicationHierarchy* value); + +public: /// /// Adds the network replication serializer for a given type. /// diff --git a/Source/Engine/Networking/Types.h b/Source/Engine/Networking/Types.h index 11675023a..0d754e727 100644 --- a/Source/Engine/Networking/Types.h +++ b/Source/Engine/Networking/Types.h @@ -11,6 +11,7 @@ class INetworkSerializable; class NetworkPeer; class NetworkClient; class NetworkStream; +class NetworkReplicationHierarchy; struct NetworkEvent; struct NetworkConnection; From 47f9bc2017e11b04dfc969b69253e1e98dae7c0a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 24 May 2023 09:47:27 +0200 Subject: [PATCH 084/116] Fix regression in Content Item tooltip #1085 --- Source/Editor/Content/Items/ContentItem.cs | 5 +++-- Source/Editor/Content/Items/NewItem.cs | 7 +++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Content/Items/ContentItem.cs b/Source/Editor/Content/Items/ContentItem.cs index 2a0390b30..66825fb42 100644 --- a/Source/Editor/Content/Items/ContentItem.cs +++ b/Source/Editor/Content/Items/ContentItem.cs @@ -384,7 +384,8 @@ namespace FlaxEditor.Content protected virtual void OnBuildTooltipText(StringBuilder sb) { sb.Append("Type: ").Append(TypeDescription).AppendLine(); - sb.Append("Size: ").Append(Utilities.Utils.FormatBytesCount((int)new FileInfo(Path).Length)).AppendLine(); + if (File.Exists(Path)) + sb.Append("Size: ").Append(Utilities.Utils.FormatBytesCount((int)new FileInfo(Path).Length)).AppendLine(); sb.Append("Path: ").Append(Utilities.Utils.GetAssetNamePathWithExt(Path)).AppendLine(); } @@ -718,7 +719,7 @@ namespace FlaxEditor.Content public override bool OnMouseDoubleClick(Float2 location, MouseButton button) { Focus(); - + // Open (Parent as ContentView).OnItemDoubleClick(this); diff --git a/Source/Editor/Content/Items/NewItem.cs b/Source/Editor/Content/Items/NewItem.cs index 94d95f15b..7040adf44 100644 --- a/Source/Editor/Content/Items/NewItem.cs +++ b/Source/Editor/Content/Items/NewItem.cs @@ -1,5 +1,6 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. +using System.Text; using FlaxEngine; namespace FlaxEditor.Content @@ -47,5 +48,11 @@ namespace FlaxEditor.Content /// protected override bool DrawShadow => true; + + /// + public override void UpdateTooltipText() + { + TooltipText = null; + } } } From 1a6e4bf14b7e9e6ccd198ff8c09c185990e4de01 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 24 May 2023 09:48:02 +0200 Subject: [PATCH 085/116] Fix missing tracy header in cooked build #948 --- Source/ThirdParty/tracy/tracy.Build.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/ThirdParty/tracy/tracy.Build.cs b/Source/ThirdParty/tracy/tracy.Build.cs index d6119a5ca..ca5f485b2 100644 --- a/Source/ThirdParty/tracy/tracy.Build.cs +++ b/Source/ThirdParty/tracy/tracy.Build.cs @@ -57,6 +57,7 @@ public class tracy : ThirdPartyModule files.Add(Path.Combine(FolderPath, "tracy", "Tracy.hpp")); files.Add(Path.Combine(FolderPath, "common", "TracySystem.hpp")); + files.Add(Path.Combine(FolderPath, "common", "TracyQueue.hpp")); files.Add(Path.Combine(FolderPath, "client", "TracyCallstack.h")); } } From c4f57d19db4442da6955d61dd0c45ad1c0afe377 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 24 May 2023 10:52:59 +0200 Subject: [PATCH 086/116] Fix network replicator incorrect error message filter --- Source/Engine/Networking/NetworkReplicator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Networking/NetworkReplicator.cpp b/Source/Engine/Networking/NetworkReplicator.cpp index b1088bfa5..42da4870b 100644 --- a/Source/Engine/Networking/NetworkReplicator.cpp +++ b/Source/Engine/Networking/NetworkReplicator.cpp @@ -1835,7 +1835,7 @@ void NetworkInternal::OnNetworkMessageObjectSpawn(NetworkEvent& event, NetworkCl #if USE_NETWORK_REPLICATOR_LOG // Ignore case when parent object in a message was a scene (eg. that is already unloaded on a client) AssetInfo assetInfo; - if (!Content::GetAssetInfo(msgDataItem.ParentId, assetInfo) || assetInfo.TypeName == TEXT("FlaxEngine.SceneAsset")) + if (!Content::GetAssetInfo(msgDataItem.ParentId, assetInfo) || assetInfo.TypeName != TEXT("FlaxEngine.SceneAsset")) { NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Failed to find object {} as parent to spawned object", msgDataItem.ParentId.ToString()); } From 2c1f6e561aaf9d350d18082dbba974792f4097ad Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 24 May 2023 11:52:32 +0200 Subject: [PATCH 087/116] Fix replication regression from eb2fc0f02fa469b9b82a99f24b70407a657fb80d --- .../Engine/Networking/NetworkReplicator.cpp | 117 +++++++++--------- 1 file changed, 57 insertions(+), 60 deletions(-) diff --git a/Source/Engine/Networking/NetworkReplicator.cpp b/Source/Engine/Networking/NetworkReplicator.cpp index 42da4870b..6579d2cc9 100644 --- a/Source/Engine/Networking/NetworkReplicator.cpp +++ b/Source/Engine/Networking/NetworkReplicator.cpp @@ -1436,10 +1436,9 @@ void NetworkInternal::NetworkReplicatorUpdate() // Skip serialization of objects that none will receive if (!isClient) { - // TODO: per-object relevancy for connected clients (eg. skip replicating actor to far players) BuildCachedTargets(item, e.TargetClients); if (CachedTargets.Count() == 0) - return; + continue; } if (item.AsNetworkObject) @@ -1451,74 +1450,72 @@ void NetworkInternal::NetworkReplicatorUpdate() if (failed) { //NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Cannot serialize object {} of type {} (missing serialization logic)", item.ToString(), obj->GetType().ToString()); - return; + continue; } // Send object to clients + const uint32 size = stream->GetPosition(); + ASSERT(size <= MAX_uint16) + NetworkMessageObjectReplicate msgData; + msgData.OwnerFrame = NetworkManager::Frame; + msgData.ObjectId = item.ObjectId; + msgData.ParentId = item.ParentId; + if (isClient) { - const uint32 size = stream->GetPosition(); - ASSERT(size <= MAX_uint16) - NetworkMessageObjectReplicate msgData; - msgData.OwnerFrame = NetworkManager::Frame; - msgData.ObjectId = item.ObjectId; - msgData.ParentId = item.ParentId; - if (isClient) - { - // Remap local client object ids into server ids - IdsRemappingTable.KeyOf(msgData.ObjectId, &msgData.ObjectId); - IdsRemappingTable.KeyOf(msgData.ParentId, &msgData.ParentId); - } - GetNetworkName(msgData.ObjectTypeName, obj->GetType().Fullname); - msgData.DataSize = size; - const uint32 msgMaxData = peer->Config.MessageSize - sizeof(NetworkMessageObjectReplicate); - const uint32 partMaxData = peer->Config.MessageSize - sizeof(NetworkMessageObjectReplicatePart); - uint32 partsCount = 1; - uint32 dataStart = 0; - uint32 msgDataSize = size; - if (size > msgMaxData) - { - // Send msgMaxData within first message - msgDataSize = msgMaxData; - dataStart += msgMaxData; + // Remap local client object ids into server ids + IdsRemappingTable.KeyOf(msgData.ObjectId, &msgData.ObjectId); + IdsRemappingTable.KeyOf(msgData.ParentId, &msgData.ParentId); + } + GetNetworkName(msgData.ObjectTypeName, obj->GetType().Fullname); + msgData.DataSize = size; + const uint32 msgMaxData = peer->Config.MessageSize - sizeof(NetworkMessageObjectReplicate); + const uint32 partMaxData = peer->Config.MessageSize - sizeof(NetworkMessageObjectReplicatePart); + uint32 partsCount = 1; + uint32 dataStart = 0; + uint32 msgDataSize = size; + if (size > msgMaxData) + { + // Send msgMaxData within first message + msgDataSize = msgMaxData; + dataStart += msgMaxData; - // Send rest of the data in separate parts - partsCount += Math::DivideAndRoundUp(size - dataStart, partMaxData); - } - else - dataStart += size; - ASSERT(partsCount <= MAX_uint8) - msgData.PartsCount = partsCount; - NetworkMessage msg = peer->BeginSendMessage(); - msg.WriteStructure(msgData); - msg.WriteBytes(stream->GetBuffer(), msgDataSize); + // Send rest of the data in separate parts + partsCount += Math::DivideAndRoundUp(size - dataStart, partMaxData); + } + else + dataStart += size; + ASSERT(partsCount <= MAX_uint8) + msgData.PartsCount = partsCount; + NetworkMessage msg = peer->BeginSendMessage(); + msg.WriteStructure(msgData); + msg.WriteBytes(stream->GetBuffer(), msgDataSize); + if (isClient) + peer->EndSendMessage(NetworkChannelType::Unreliable, msg); + else + peer->EndSendMessage(NetworkChannelType::Unreliable, msg, CachedTargets); + + // Send all other parts + for (uint32 partIndex = 1; partIndex < partsCount; partIndex++) + { + NetworkMessageObjectReplicatePart msgDataPart; + msgDataPart.OwnerFrame = msgData.OwnerFrame; + msgDataPart.ObjectId = msgData.ObjectId; + msgDataPart.DataSize = msgData.DataSize; + msgDataPart.PartsCount = msgData.PartsCount; + msgDataPart.PartStart = dataStart; + msgDataPart.PartSize = Math::Min(size - dataStart, partMaxData); + msg = peer->BeginSendMessage(); + msg.WriteStructure(msgDataPart); + msg.WriteBytes(stream->GetBuffer() + msgDataPart.PartStart, msgDataPart.PartSize); + dataStart += msgDataPart.PartSize; if (isClient) peer->EndSendMessage(NetworkChannelType::Unreliable, msg); else peer->EndSendMessage(NetworkChannelType::Unreliable, msg, CachedTargets); - - // Send all other parts - for (uint32 partIndex = 1; partIndex < partsCount; partIndex++) - { - NetworkMessageObjectReplicatePart msgDataPart; - msgDataPart.OwnerFrame = msgData.OwnerFrame; - msgDataPart.ObjectId = msgData.ObjectId; - msgDataPart.DataSize = msgData.DataSize; - msgDataPart.PartsCount = msgData.PartsCount; - msgDataPart.PartStart = dataStart; - msgDataPart.PartSize = Math::Min(size - dataStart, partMaxData); - msg = peer->BeginSendMessage(); - msg.WriteStructure(msgDataPart); - msg.WriteBytes(stream->GetBuffer() + msgDataPart.PartStart, msgDataPart.PartSize); - dataStart += msgDataPart.PartSize; - if (isClient) - peer->EndSendMessage(NetworkChannelType::Unreliable, msg); - else - peer->EndSendMessage(NetworkChannelType::Unreliable, msg, CachedTargets); - } - ASSERT_LOW_LAYER(dataStart == size); - - // TODO: stats for bytes send per object type } + ASSERT_LOW_LAYER(dataStart == size); + + // TODO: stats for bytes send per object type } } From 9986d62a28f9e904e72069160459b4132925b58c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 24 May 2023 12:06:09 +0200 Subject: [PATCH 088/116] Fix crash when scripting object gets deleted while it exists in NetworkReplicationHierarchy --- Source/Engine/Networking/NetworkReplicationHierarchy.cpp | 2 +- Source/Engine/Networking/NetworkReplicationHierarchy.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Networking/NetworkReplicationHierarchy.cpp b/Source/Engine/Networking/NetworkReplicationHierarchy.cpp index 783992fd8..450f22d47 100644 --- a/Source/Engine/Networking/NetworkReplicationHierarchy.cpp +++ b/Source/Engine/Networking/NetworkReplicationHierarchy.cpp @@ -110,7 +110,7 @@ void NetworkReplicationNode::Update(NetworkReplicationHierarchyUpdateResult* res } } } - if (targetClients) + if (targetClients && obj.Object) { // Replicate this frame result->AddObject(obj.Object, targetClients); diff --git a/Source/Engine/Networking/NetworkReplicationHierarchy.h b/Source/Engine/Networking/NetworkReplicationHierarchy.h index 7d6db336d..99770c5a4 100644 --- a/Source/Engine/Networking/NetworkReplicationHierarchy.h +++ b/Source/Engine/Networking/NetworkReplicationHierarchy.h @@ -18,7 +18,7 @@ API_STRUCT(NoDefault, Namespace = "FlaxEngine.Networking") struct FLAXENGINE_API { DECLARE_SCRIPTING_TYPE_MINIMAL(NetworkReplicationObjectInfo); // The object to replicate. - API_FIELD() ScriptingObject* Object; + API_FIELD() ScriptingObjectReference Object; // The target amount of the replication updates per second (frequency of the replication). Constrained by NetworkManager::NetworkFPS. API_FIELD() float ReplicationFPS = 60; // The minimum distance from the player to the object at which it can process replication. For example, players further away won't receive object data. From 982639f21581bbb44ea4de5be6e61647a34cf436 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 24 May 2023 22:55:02 +0200 Subject: [PATCH 089/116] Fix syncing Custom Editor value when using it to edit value-type --- Source/Editor/CustomEditors/CustomEditor.cs | 26 +++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/Source/Editor/CustomEditors/CustomEditor.cs b/Source/Editor/CustomEditors/CustomEditor.cs index 8e252a207..1330616d1 100644 --- a/Source/Editor/CustomEditors/CustomEditor.cs +++ b/Source/Editor/CustomEditors/CustomEditor.cs @@ -147,7 +147,7 @@ namespace FlaxEditor.CustomEditors return; // Special case for root objects to run normal layout build - if (_presenter.Selection == Values) + if (_presenter != null && _presenter.Selection == Values) { _presenter.BuildLayout(); return; @@ -158,7 +158,7 @@ namespace FlaxEditor.CustomEditors var layout = _layout; var control = layout.ContainerControl; var parent = _parent; - var parentScrollV = (_presenter.Panel.Parent as Panel)?.VScrollBar?.Value ?? -1; + var parentScrollV = (_presenter?.Panel.Parent as Panel)?.VScrollBar?.Value ?? -1; control.IsLayoutLocked = true; control.DisposeChildren(); @@ -248,6 +248,28 @@ namespace FlaxEditor.CustomEditors internal virtual void RefreshRootChild() { + // Check if need to update value + if (_hasValueDirty) + { + IsSettingValue = true; + try + { + // Cleanup (won't retry update in case of exception) + object val = _valueToSet; + _hasValueDirty = false; + _valueToSet = null; + + // Assign value + for (int i = 0; i < _values.Count; i++) + _values[i] = val; + } + finally + { + OnUnDirty(); + IsSettingValue = false; + } + } + Refresh(); for (int i = 0; i < _children.Count; i++) From 7efb3e3f3d04781c417e41ea1cb2326f521646bd Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 24 May 2023 22:56:29 +0200 Subject: [PATCH 090/116] Add `SoftTypeReference` to scripting API for lazy-load type references (via typename) --- .../CustomEditors/Editors/TypeEditor.cs | 34 ++-- Source/Engine/Scripting/SoftTypeReference.cs | 87 ++++++++++ Source/Engine/Scripting/SoftTypeReference.h | 151 ++++++++++++++++++ Source/Engine/Serialization/JsonConverters.cs | 39 ++++- Source/Engine/Serialization/JsonSerializer.cs | 1 + 5 files changed, 298 insertions(+), 14 deletions(-) create mode 100644 Source/Engine/Scripting/SoftTypeReference.cs create mode 100644 Source/Engine/Scripting/SoftTypeReference.h diff --git a/Source/Editor/CustomEditors/Editors/TypeEditor.cs b/Source/Editor/CustomEditors/Editors/TypeEditor.cs index 722235088..9ee2c37a4 100644 --- a/Source/Editor/CustomEditors/Editors/TypeEditor.cs +++ b/Source/Editor/CustomEditors/Editors/TypeEditor.cs @@ -393,11 +393,8 @@ namespace FlaxEditor.CustomEditors.Editors if (_element != null) { _element.CustomControl.ValueChanged += () => SetValue(_element.CustomControl.Value.Type); - if (_element.CustomControl.Type == ScriptType.Object) - { _element.CustomControl.Type = Values.Type.Type != typeof(object) || Values[0] == null ? ScriptType.Object : TypeUtils.GetObjectType(Values[0]); - } } } @@ -407,9 +404,7 @@ namespace FlaxEditor.CustomEditors.Editors base.Refresh(); if (!HasDifferentValues) - { _element.CustomControl.Value = new ScriptType(Values[0] as Type); - } } } @@ -425,9 +420,7 @@ namespace FlaxEditor.CustomEditors.Editors base.Initialize(layout); if (_element != null) - { _element.CustomControl.ValueChanged += () => SetValue(_element.CustomControl.Value); - } } /// @@ -436,9 +429,32 @@ namespace FlaxEditor.CustomEditors.Editors base.Refresh(); if (!HasDifferentValues) - { _element.CustomControl.Value = (ScriptType)Values[0]; - } + } + } + + /// + /// Default implementation of the inspector used to edit reference to the . Used to pick classes. + /// + [CustomEditor(typeof(SoftTypeReference)), DefaultEditor] + public class SoftTypeReferenceEditor : TypeEditorBase + { + /// + public override void Initialize(LayoutElementsContainer layout) + { + base.Initialize(layout); + + if (_element != null) + _element.CustomControl.ValueChanged += () => SetValue(new SoftTypeReference(_element.CustomControl.ValueTypeName)); + } + + /// + public override void Refresh() + { + base.Refresh(); + + if (!HasDifferentValues) + _element.CustomControl.ValueTypeName = ((SoftTypeReference)Values[0]).TypeName; } } diff --git a/Source/Engine/Scripting/SoftTypeReference.cs b/Source/Engine/Scripting/SoftTypeReference.cs new file mode 100644 index 000000000..f12f2bdb7 --- /dev/null +++ b/Source/Engine/Scripting/SoftTypeReference.cs @@ -0,0 +1,87 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +using System; + +namespace FlaxEngine +{ + /// + /// The soft reference to the scripting type contained in the scripting assembly. + /// + public struct SoftTypeReference : IComparable, IComparable + { + private string _typeName; + + /// + /// Gets or sets the type full name (eg. FlaxEngine.Actor). + /// + public string TypeName + { + get => _typeName; + set => _typeName = value; + } + + /// + /// Gets or sets the type (resolves soft reference). + /// + public Type Type + { + get => _typeName != null ? Type.GetType(_typeName) : null; + set => _typeName = value?.FullName; + } + + /// + /// Initializes a new instance of the . + /// + /// The type name. + public SoftTypeReference(string typeName) + { + _typeName = typeName; + } + + /// + /// Gets the soft type reference from full name. + /// + /// The type name. + /// The soft type reference. + public static implicit operator SoftTypeReference(string s) + { + return new SoftTypeReference { _typeName = s }; + } + + /// + /// Gets the soft type reference from runtime type. + /// + /// The type. + /// The soft type reference. + public static implicit operator SoftTypeReference(Type s) + { + return new SoftTypeReference { _typeName = s?.FullName }; + } + + /// + public override string ToString() + { + return _typeName; + } + + /// + public override int GetHashCode() + { + return _typeName?.GetHashCode() ?? 0; + } + + /// + public int CompareTo(object obj) + { + if (obj is SoftTypeReference other) + return CompareTo(other); + return 0; + } + + /// + public int CompareTo(SoftTypeReference other) + { + return string.Compare(_typeName, other._typeName, StringComparison.Ordinal); + } + } +} diff --git a/Source/Engine/Scripting/SoftTypeReference.h b/Source/Engine/Scripting/SoftTypeReference.h new file mode 100644 index 000000000..30508f639 --- /dev/null +++ b/Source/Engine/Scripting/SoftTypeReference.h @@ -0,0 +1,151 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Scripting.h" +#include "ScriptingObject.h" +#include "Engine/Core/Log.h" +#include "Engine/Core/Types/String.h" +#include "Engine/Serialization/SerializationFwd.h" + +/// +/// The soft reference to the scripting type contained in the scripting assembly. +/// +template +API_STRUCT(InBuild) struct SoftTypeReference +{ +protected: + StringAnsi _typeName; + +public: + SoftTypeReference() = default; + + SoftTypeReference(const SoftTypeReference& s) + : _typeName(s._typeName) + { + } + + SoftTypeReference(SoftTypeReference&& s) noexcept + : _typeName(MoveTemp(s._typeName)) + { + } + + SoftTypeReference(const StringView& s) + : _typeName(s) + { + } + + SoftTypeReference(const StringAnsiView& s) + : _typeName(s) + { + } + + SoftTypeReference(const char* s) + : _typeName(s) + { + } + +public: + FORCE_INLINE SoftTypeReference& operator=(SoftTypeReference&& s) noexcept + { + _typeName = MoveTemp(s._typeName); + return *this; + } + + FORCE_INLINE SoftTypeReference& operator=(StringAnsi&& s) noexcept + { + _typeName = MoveTemp(s); + return *this; + } + + FORCE_INLINE SoftTypeReference& operator=(const SoftTypeReference& s) + { + _typeName = s._typeName; + return *this; + } + + FORCE_INLINE SoftTypeReference& operator=(const StringAnsiView& s) + { + _typeName = s; + return *this; + } + + FORCE_INLINE bool operator==(const SoftTypeReference& other) const + { + return _typeName == other._typeName; + } + + FORCE_INLINE bool operator!=(const SoftTypeReference& other) const + { + return _typeName != other._typeName; + } + + FORCE_INLINE bool operator==(const StringAnsiView& other) const + { + return _typeName == other; + } + + FORCE_INLINE bool operator!=(const StringAnsiView& other) const + { + return _typeName != other; + } + + FORCE_INLINE operator bool() const + { + return _typeName.HasChars(); + } + +public: + // Gets the type full name (eg. FlaxEngine.Actor). + StringAnsiView GetTypeName() const + { + return StringAnsiView(_typeName); + } + + // Gets the type (resolves soft reference). + ScriptingTypeHandle GetType() const + { + return Scripting::FindScriptingType(_typeName); + } + + // Creates a new objects of that type (or of type T if failed to solve typename). + T* NewObject() const + { + const ScriptingTypeHandle type = Scripting::FindScriptingType(_typeName); + auto obj = ScriptingObject::NewObject(type); + if (!obj) + { + if (_typeName.HasChars()) + LOG(Error, "Unknown or invalid type {0}", String(_typeName)); + obj = ScriptingObject::NewObject(); + } + return obj; + } +}; + +template +uint32 GetHash(const SoftTypeReference& key) +{ + return GetHash(key.GetTypeName()); +} + +// @formatter:off +namespace Serialization +{ + template + bool ShouldSerialize(const SoftTypeReference& v, const void* otherObj) + { + return !otherObj || v != *(SoftTypeReference*)otherObj; + } + template + void Serialize(ISerializable::SerializeStream& stream, const SoftTypeReference& v, const void* otherObj) + { + stream.String(v.GetTypeName()); + } + template + void Deserialize(ISerializable::DeserializeStream& stream, SoftTypeReference& v, ISerializeModifier* modifier) + { + v = stream.GetTextAnsi(); + } +} +// @formatter:on diff --git a/Source/Engine/Serialization/JsonConverters.cs b/Source/Engine/Serialization/JsonConverters.cs index 07388a09b..40f9cb5a2 100644 --- a/Source/Engine/Serialization/JsonConverters.cs +++ b/Source/Engine/Serialization/JsonConverters.cs @@ -7,7 +7,7 @@ using Newtonsoft.Json; namespace FlaxEngine.Json { /// - /// Serialize references to the FlaxEngine.Object as Guid. + /// Serialize references to the as Guid. /// /// internal class FlaxObjectConverter : JsonConverter @@ -46,7 +46,7 @@ namespace FlaxEngine.Json } /// - /// Serialize SceneReference as Guid in internal format. + /// Serialize as Guid in internal format. /// /// internal class SceneReferenceConverter : JsonConverter @@ -79,7 +79,7 @@ namespace FlaxEngine.Json } /// - /// Serialize SoftObjectReference as Guid in internal format. + /// Serialize as Guid in internal format. /// /// internal class SoftObjectReferenceConverter : JsonConverter @@ -111,7 +111,36 @@ namespace FlaxEngine.Json } /// - /// Serialize SoftObjectReference as Guid in internal format. + /// Serialize as typename string in internal format. + /// + /// + internal class SoftTypeReferenceConverter : JsonConverter + { + /// + public override void WriteJson(JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer) + { + writer.WriteValue(((SoftTypeReference)value).TypeName); + } + + /// + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer) + { + var result = new SoftTypeReference(); + if (reader.TokenType == JsonToken.String) + result.TypeName = (string)reader.Value; + + return result; + } + + /// + public override bool CanConvert(Type objectType) + { + return objectType == typeof(SoftTypeReference); + } + } + + /// + /// Serialize as Guid in internal format. /// /// internal class MarginConverter : JsonConverter @@ -237,7 +266,7 @@ namespace FlaxEngine.Json } /// - /// Serialize LocalizedString as inlined text is not using localization (Id member is empty). + /// Serialize as inlined text is not using localization (Id member is empty). /// /// internal class LocalizedStringConverter : JsonConverter diff --git a/Source/Engine/Serialization/JsonSerializer.cs b/Source/Engine/Serialization/JsonSerializer.cs index 617768a83..30889fce0 100644 --- a/Source/Engine/Serialization/JsonSerializer.cs +++ b/Source/Engine/Serialization/JsonSerializer.cs @@ -123,6 +123,7 @@ namespace FlaxEngine.Json settings.Converters.Add(ObjectConverter); settings.Converters.Add(new SceneReferenceConverter()); settings.Converters.Add(new SoftObjectReferenceConverter()); + settings.Converters.Add(new SoftTypeReferenceConverter()); settings.Converters.Add(new MarginConverter()); settings.Converters.Add(new VersionConverter()); settings.Converters.Add(new LocalizedStringConverter()); From e10aa1ff58a4c520ac6b0a0a5d47053dc0cfdd11 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 24 May 2023 22:58:07 +0200 Subject: [PATCH 091/116] Add support for editing dictionary keys that are structures --- .../CustomEditors/Editors/DictionaryEditor.cs | 39 ++++++++++++------- .../Editor/GUI/ContextMenu/ContextMenuBase.cs | 19 +++++++++ 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs b/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs index 1b9a7cca1..18500fc08 100644 --- a/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs +++ b/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs @@ -2,7 +2,6 @@ using System; using System.Collections; -using System.Collections.Generic; using System.Linq; using FlaxEditor.CustomEditors.Elements; using FlaxEditor.CustomEditors.GUI; @@ -45,8 +44,8 @@ namespace FlaxEditor.CustomEditors.Editors private void OnSetupContextMenu(PropertyNameLabel label, ContextMenu menu, CustomEditor linkedEditor) { - menu.AddSeparator(); - + if (menu.Items.Any()) + menu.AddSeparator(); menu.AddButton("Remove", OnRemoveClicked).Enabled = !_editor._readOnly; menu.AddButton("Edit", OnEditClicked).Enabled = _editor._canEditKeys; } @@ -61,6 +60,7 @@ namespace FlaxEditor.CustomEditors.Editors var keyType = _editor.Values.Type.GetGenericArguments()[0]; if (keyType == typeof(string) || keyType.IsPrimitive) { + // Edit as text var popup = RenamePopup.Show(Parent, Rectangle.Margin(Bounds, Margin), Text, false); popup.Validate += (renamePopup, value) => { @@ -78,7 +78,6 @@ namespace FlaxEditor.CustomEditors.Editors newKey = JsonSerializer.Deserialize(renamePopup.Text, keyType); else newKey = renamePopup.Text; - _editor.ChangeKey(_key, newKey); _key = newKey; Text = _key.ToString(); @@ -86,6 +85,7 @@ namespace FlaxEditor.CustomEditors.Editors } else if (keyType.IsEnum) { + // Edit via enum picker var popup = RenamePopup.Show(Parent, Rectangle.Margin(Bounds, Margin), Text, false); var picker = new EnumComboBox(keyType) { @@ -108,7 +108,21 @@ namespace FlaxEditor.CustomEditors.Editors } else { - throw new NotImplementedException("Missing editing for dictionary key type " + keyType); + // Generic editor + var popup = ContextMenuBase.ShowEmptyMenu(Parent, Rectangle.Margin(Bounds, Margin)); + var presenter = new CustomEditorPresenter(null); + presenter.Panel.AnchorPreset = AnchorPresets.StretchAll; + presenter.Panel.IsScrollable = false; + presenter.Panel.Parent = popup; + presenter.Select(_key); + presenter.Modified += () => + { + popup.Hide(); + object newKey = presenter.Selection[0]; + _editor.ChangeKey(_key, newKey); + _key = newKey; + Text = _key?.ToString(); + }; } } @@ -159,7 +173,7 @@ namespace FlaxEditor.CustomEditors.Editors var argTypes = type.GetGenericArguments(); var keyType = argTypes[0]; var valueType = argTypes[1]; - _canEditKeys = keyType == typeof(string) || keyType.IsPrimitive || keyType.IsEnum; + _canEditKeys = keyType == typeof(string) || keyType.IsPrimitive || keyType.IsEnum || keyType.IsValueType; _background = FlaxEngine.GUI.Style.Current.CollectionBackgroundColor; _readOnly = false; _notNullItems = false; @@ -382,6 +396,7 @@ namespace FlaxEditor.CustomEditors.Editors int newItemsLeft = newSize - oldSize; while (newItemsLeft-- > 0) { + object newKey = null; if (keyType.IsPrimitive) { long uniqueKey = 0; @@ -400,8 +415,7 @@ namespace FlaxEditor.CustomEditors.Editors } } } while (!isUnique); - - newValues[Convert.ChangeType(uniqueKey, keyType)] = TypeUtils.GetDefaultValue(new ScriptType(valueType)); + newKey = Convert.ChangeType(uniqueKey, keyType); } else if (keyType.IsEnum) { @@ -421,8 +435,7 @@ namespace FlaxEditor.CustomEditors.Editors } } } while (!isUnique && uniqueKeyIndex < enumValues.Length); - - newValues[enumValues.GetValue(uniqueKeyIndex)] = TypeUtils.GetDefaultValue(new ScriptType(valueType)); + newKey = enumValues.GetValue(uniqueKeyIndex); } else if (keyType == typeof(string)) { @@ -441,13 +454,13 @@ namespace FlaxEditor.CustomEditors.Editors } } } while (!isUnique); - - newValues[uniqueKey] = TypeUtils.GetDefaultValue(new ScriptType(valueType)); + newKey = uniqueKey; } else { - throw new InvalidOperationException(); + newKey = TypeUtils.GetDefaultValue(new ScriptType(keyType)); } + newValues[newKey] = TypeUtils.GetDefaultValue(new ScriptType(valueType)); } SetValue(newValues); diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs b/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs index 27d47345d..7ecd197eb 100644 --- a/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs +++ b/Source/Editor/GUI/ContextMenu/ContextMenuBase.cs @@ -110,6 +110,25 @@ namespace FlaxEditor.GUI.ContextMenu _isSubMenu = true; } + /// + /// Shows the empty menu popup o na screen. + /// + /// The target control. + /// The target control area to cover. + /// Created popup. + public static ContextMenuBase ShowEmptyMenu(Control control, Rectangle area) + { + // Calculate the control size in the window space to handle scaled controls + var upperLeft = control.PointToWindow(area.UpperLeft); + var bottomRight = control.PointToWindow(area.BottomRight); + var size = bottomRight - upperLeft; + + var popup = new ContextMenuBase(); + popup.Size = size; + popup.Show(control, area.Location + new Float2(0, (size.Y - popup.Height) * 0.5f)); + return popup; + } + /// /// Show context menu over given control. /// From 90d633fb2d735bc49bc09e1342d179f125ad94da Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 24 May 2023 22:59:22 +0200 Subject: [PATCH 092/116] Minor improvements to new replication hierarchy --- .../Networking/NetworkReplicationHierarchy.cpp | 18 ++++++++++++------ .../Networking/NetworkReplicationHierarchy.h | 6 ++++-- Source/Engine/Networking/NetworkReplicator.cpp | 3 ++- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/Source/Engine/Networking/NetworkReplicationHierarchy.cpp b/Source/Engine/Networking/NetworkReplicationHierarchy.cpp index 450f22d47..d86acfc7f 100644 --- a/Source/Engine/Networking/NetworkReplicationHierarchy.cpp +++ b/Source/Engine/Networking/NetworkReplicationHierarchy.cpp @@ -49,10 +49,11 @@ bool NetworkReplicationHierarchyUpdateResult::GetClientLocation(int32 clientInde void NetworkReplicationNode::AddObject(NetworkReplicationHierarchyObject obj) { - ASSERT(obj.Object && obj.ReplicationFPS > 0.0f); - - // Randomize initial replication update to spread rep rates more evenly for large scenes that register all objects within the same frame - obj.ReplicationUpdatesLeft = NetworkReplicationNodeObjectCounter++ % Math::Clamp(Math::RoundToInt(NetworkManager::NetworkFPS / obj.ReplicationFPS), 1, 60); + if (obj.ReplicationFPS > 0.0f) + { + // Randomize initial replication update to spread rep rates more evenly for large scenes that register all objects within the same frame + obj.ReplicationUpdatesLeft = NetworkReplicationNodeObjectCounter++ % Math::Clamp(Math::RoundToInt(NetworkManager::NetworkFPS / obj.ReplicationFPS), 1, 60); + } Objects.Add(obj); } @@ -79,7 +80,12 @@ void NetworkReplicationNode::Update(NetworkReplicationHierarchyUpdateResult* res const float networkFPS = NetworkManager::NetworkFPS / result->ReplicationScale; for (NetworkReplicationHierarchyObject& obj : Objects) { - if (obj.ReplicationUpdatesLeft > 0) + if (obj.ReplicationFPS <= 0.0f) + { + // Always relevant + result->AddObject(obj.Object); + } + else if (obj.ReplicationUpdatesLeft > 0) { // Move to the next frame obj.ReplicationUpdatesLeft--; @@ -87,7 +93,7 @@ void NetworkReplicationNode::Update(NetworkReplicationHierarchyUpdateResult* res else { NetworkClientsMask targetClients = result->GetClientsMask(); - if (result->_clientsHaveLocation) + if (result->_clientsHaveLocation && obj.CullDistance > 0.0f) { // Cull object against viewers locations if (const Actor* actor = obj.GetActor()) diff --git a/Source/Engine/Networking/NetworkReplicationHierarchy.h b/Source/Engine/Networking/NetworkReplicationHierarchy.h index 99770c5a4..55cd6b7b1 100644 --- a/Source/Engine/Networking/NetworkReplicationHierarchy.h +++ b/Source/Engine/Networking/NetworkReplicationHierarchy.h @@ -17,11 +17,12 @@ class Actor; API_STRUCT(NoDefault, Namespace = "FlaxEngine.Networking") struct FLAXENGINE_API NetworkReplicationHierarchyObject { DECLARE_SCRIPTING_TYPE_MINIMAL(NetworkReplicationObjectInfo); + // The object to replicate. API_FIELD() ScriptingObjectReference Object; - // The target amount of the replication updates per second (frequency of the replication). Constrained by NetworkManager::NetworkFPS. + // The target amount of the replication updates per second (frequency of the replication). Constrained by NetworkManager::NetworkFPS. Use 0 for 'always relevant' object. API_FIELD() float ReplicationFPS = 60; - // The minimum distance from the player to the object at which it can process replication. For example, players further away won't receive object data. + // The minimum distance from the player to the object at which it can process replication. For example, players further away won't receive object data. Use 0 if unused. API_FIELD() float CullDistance = 15000; // Runtime value for update frames left for the next replication of this object. Matches NetworkManager::NetworkFPS calculated from ReplicationFPS. API_FIELD(Attributes="HideInEditor") uint16 ReplicationUpdatesLeft = 0; @@ -61,6 +62,7 @@ inline uint32 GetHash(const NetworkReplicationHierarchyObject& key) API_STRUCT(NoDefault, Namespace = "FlaxEngine.Networking") struct FLAXENGINE_API NetworkClientsMask { DECLARE_SCRIPTING_TYPE_MINIMAL(NetworkClientsMask); + // The first 64 bits (each for one client). API_FIELD() uint64 Word0 = 0; // The second 64 bits (each for one client). diff --git a/Source/Engine/Networking/NetworkReplicator.cpp b/Source/Engine/Networking/NetworkReplicator.cpp index 6579d2cc9..8d0ba3c0e 100644 --- a/Source/Engine/Networking/NetworkReplicator.cpp +++ b/Source/Engine/Networking/NetworkReplicator.cpp @@ -1430,7 +1430,8 @@ void NetworkInternal::NetworkReplicatorUpdate() { ScriptingObject* obj = e.Object; auto it = Objects.Find(obj->GetID()); - ASSERT(it.IsNotEnd()); + if (it.IsEnd()) + continue; auto& item = it->Item; // Skip serialization of objects that none will receive From 48b18bf84e6eb9b53863a59d70a90f8d2fc68cab Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 24 May 2023 23:40:40 +0200 Subject: [PATCH 093/116] Fix regression from 7c0d1ab97762c386cc8382663395b892635e17e8 when using different access level to property getter/setter --- Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 25d30a37a..93b81f655 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -1120,7 +1120,7 @@ namespace Flax.Build.Bindings { contents.Append(indent); if (propertyInfo.Access != propertyInfo.Getter.Access) - contents.Append(GenerateCSharpAccessLevel(propertyInfo.Access)); + contents.Append(GenerateCSharpAccessLevel(propertyInfo.Getter.Access)); contents.Append("get { "); GenerateCSharpWrapperFunctionCall(buildData, contents, classInfo, propertyInfo.Getter); contents.Append(" }").AppendLine(); @@ -1130,7 +1130,7 @@ namespace Flax.Build.Bindings { contents.Append(indent); if (propertyInfo.Access != propertyInfo.Setter.Access) - contents.Append(GenerateCSharpAccessLevel(propertyInfo.Access)); + contents.Append(GenerateCSharpAccessLevel(propertyInfo.Setter.Access)); contents.Append("set { "); GenerateCSharpWrapperFunctionCall(buildData, contents, classInfo, propertyInfo.Setter, true); contents.Append(" }").AppendLine(); From c2cd3b64fff51748684230c2286b13abbaa1a16a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 24 May 2023 23:50:51 +0200 Subject: [PATCH 094/116] Improve generated bindings code usability in Editor --- Source/Engine/Engine/NativeInterop.Managed.cs | 9 +++ .../Engine/NativeInterop.Marshallers.cs | 73 +++++++++++++++++++ .../Bindings/BindingsGenerator.CSharp.cs | 31 ++++++-- 3 files changed, 108 insertions(+), 5 deletions(-) diff --git a/Source/Engine/Engine/NativeInterop.Managed.cs b/Source/Engine/Engine/NativeInterop.Managed.cs index bc240275f..538703d4e 100644 --- a/Source/Engine/Engine/NativeInterop.Managed.cs +++ b/Source/Engine/Engine/NativeInterop.Managed.cs @@ -16,6 +16,9 @@ namespace FlaxEngine.Interop /// /// Wrapper for managed arrays which are passed to unmanaged code. /// +#if FLAX_EDITOR + [HideInEditor] +#endif public unsafe class ManagedArray { private ManagedHandle _pinnedArrayHandle; @@ -266,6 +269,9 @@ namespace FlaxEngine.Interop } } +#if FLAX_EDITOR + [HideInEditor] +#endif internal static class ManagedString { internal static ManagedHandle EmptyStringHandle = ManagedHandle.Alloc(string.Empty); @@ -315,6 +321,9 @@ namespace FlaxEngine.Interop /// /// Handle to managed objects which can be stored in native code. /// +#if FLAX_EDITOR + [HideInEditor] +#endif public struct ManagedHandle { private IntPtr handle; diff --git a/Source/Engine/Engine/NativeInterop.Marshallers.cs b/Source/Engine/Engine/NativeInterop.Marshallers.cs index 148f02539..a3531d11d 100644 --- a/Source/Engine/Engine/NativeInterop.Marshallers.cs +++ b/Source/Engine/Engine/NativeInterop.Marshallers.cs @@ -12,6 +12,9 @@ using System.Runtime.InteropServices.Marshalling; namespace FlaxEngine.Interop { +#if FLAX_EDITOR + [HideInEditor] +#endif [CustomMarshaller(typeof(object), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedHandleMarshaller.ManagedToNative))] [CustomMarshaller(typeof(object), MarshalMode.UnmanagedToManagedOut, typeof(ManagedHandleMarshaller.ManagedToNative))] [CustomMarshaller(typeof(object), MarshalMode.ElementIn, typeof(ManagedHandleMarshaller.ManagedToNative))] @@ -23,6 +26,9 @@ namespace FlaxEngine.Interop [CustomMarshaller(typeof(object), MarshalMode.ElementRef, typeof(ManagedHandleMarshaller))] public static class ManagedHandleMarshaller { +#if FLAX_EDITOR + [HideInEditor] +#endif public static class NativeToManaged { public static object ConvertToManaged(IntPtr unmanaged) => unmanaged == IntPtr.Zero ? null : ManagedHandle.FromIntPtr(unmanaged).Target; @@ -33,6 +39,9 @@ namespace FlaxEngine.Interop } } +#if FLAX_EDITOR + [HideInEditor] +#endif public static class ManagedToNative { public static IntPtr ConvertToUnmanaged(object managed) => managed != null ? ManagedHandle.ToIntPtr(managed, GCHandleType.Weak) : IntPtr.Zero; @@ -48,6 +57,9 @@ namespace FlaxEngine.Interop } } +#if FLAX_EDITOR + [HideInEditor] +#endif public struct Bidirectional { object managed; @@ -99,6 +111,9 @@ namespace FlaxEngine.Interop } } +#if FLAX_EDITOR + [HideInEditor] +#endif [CustomMarshaller(typeof(Type), MarshalMode.Default, typeof(SystemTypeMarshaller))] public static class SystemTypeMarshaller { @@ -118,6 +133,9 @@ namespace FlaxEngine.Interop } } +#if FLAX_EDITOR + [HideInEditor] +#endif [CustomMarshaller(typeof(Exception), MarshalMode.Default, typeof(ExceptionMarshaller))] public static class ExceptionMarshaller { @@ -126,6 +144,9 @@ namespace FlaxEngine.Interop public static void Free(IntPtr unmanaged) => ManagedHandleMarshaller.Free(unmanaged); } +#if FLAX_EDITOR + [HideInEditor] +#endif [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ManagedToUnmanagedIn, typeof(ObjectMarshaller.ManagedToNative))] [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.UnmanagedToManagedOut, typeof(ObjectMarshaller.ManagedToNative))] [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ElementIn, typeof(ObjectMarshaller.ManagedToNative))] @@ -134,17 +155,26 @@ namespace FlaxEngine.Interop [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ElementOut, typeof(ObjectMarshaller.NativeToManaged))] public static class ObjectMarshaller { +#if FLAX_EDITOR + [HideInEditor] +#endif public static class NativeToManaged { public static FlaxEngine.Object ConvertToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? Unsafe.As(ManagedHandle.FromIntPtr(unmanaged).Target) : null; } +#if FLAX_EDITOR + [HideInEditor] +#endif public static class ManagedToNative { public static IntPtr ConvertToUnmanaged(FlaxEngine.Object managed) => Unsafe.As(managed) != null ? ManagedHandle.ToIntPtr(managed) : IntPtr.Zero; } } +#if FLAX_EDITOR + [HideInEditor] +#endif [CustomMarshaller(typeof(CultureInfo), MarshalMode.Default, typeof(CultureInfoMarshaller))] public static class CultureInfoMarshaller { @@ -159,6 +189,9 @@ namespace FlaxEngine.Interop [CustomMarshaller(typeof(Array), MarshalMode.UnmanagedToManagedIn, typeof(SystemArrayMarshaller.NativeToManaged))] public static unsafe class SystemArrayMarshaller { +#if FLAX_EDITOR + [HideInEditor] +#endif public struct ManagedToNative { ManagedArray managedArray; @@ -187,6 +220,9 @@ namespace FlaxEngine.Interop } } +#if FLAX_EDITOR + [HideInEditor] +#endif public struct NativeToManaged { ManagedHandle handle; @@ -217,6 +253,9 @@ namespace FlaxEngine.Interop } } +#if FLAX_EDITOR + [HideInEditor] +#endif [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.ManagedToUnmanagedIn, typeof(DictionaryMarshaller<,>.ManagedToNative))] [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.UnmanagedToManagedOut, typeof(DictionaryMarshaller<,>.ManagedToNative))] [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.ElementIn, typeof(DictionaryMarshaller<,>.ManagedToNative))] @@ -228,21 +267,31 @@ namespace FlaxEngine.Interop [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.ElementRef, typeof(DictionaryMarshaller<,>))] public static unsafe class DictionaryMarshaller { +#if FLAX_EDITOR + [HideInEditor] +#endif public static class NativeToManaged { public static Dictionary ConvertToManaged(IntPtr unmanaged) => DictionaryMarshaller.ToManaged(unmanaged); public static void Free(IntPtr unmanaged) => DictionaryMarshaller.Free(unmanaged); } +#if FLAX_EDITOR + [HideInEditor] +#endif public static class ManagedToNative { public static IntPtr ConvertToUnmanaged(Dictionary managed) => DictionaryMarshaller.ToNative(managed, GCHandleType.Weak); + public static void Free(IntPtr unmanaged) { //DictionaryMarshaller.Free(unmanaged); // No need to free weak handles } } +#if FLAX_EDITOR + [HideInEditor] +#endif public struct Bidirectional { Dictionary managed; @@ -281,6 +330,9 @@ namespace FlaxEngine.Interop } } +#if FLAX_EDITOR + [HideInEditor] +#endif [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedIn, typeof(ArrayMarshaller<,>.ManagedToNative))] [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedOut, typeof(ArrayMarshaller<,>.ManagedToNative))] [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementIn, typeof(ArrayMarshaller<,>.ManagedToNative))] @@ -293,6 +345,9 @@ namespace FlaxEngine.Interop [ContiguousCollectionMarshaller] public static unsafe class ArrayMarshaller where TUnmanagedElement : unmanaged { +#if FLAX_EDITOR + [HideInEditor] +#endif public static class NativeToManaged { public static T[]? AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int numElements) @@ -330,6 +385,9 @@ namespace FlaxEngine.Interop } } +#if FLAX_EDITOR + [HideInEditor] +#endif public static class ManagedToNative { public static TUnmanagedElement* AllocateContainerForUnmanagedElements(T[]? managed, out int numElements) @@ -364,6 +422,9 @@ namespace FlaxEngine.Interop } } +#if FLAX_EDITOR + [HideInEditor] +#endif public struct Bidirectional { T[] managedArray; @@ -460,6 +521,9 @@ namespace FlaxEngine.Interop } } +#if FLAX_EDITOR + [HideInEditor] +#endif [CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedIn, typeof(StringMarshaller.ManagedToNative))] [CustomMarshaller(typeof(string), MarshalMode.UnmanagedToManagedOut, typeof(StringMarshaller.ManagedToNative))] [CustomMarshaller(typeof(string), MarshalMode.ElementIn, typeof(StringMarshaller.ManagedToNative))] @@ -471,12 +535,18 @@ namespace FlaxEngine.Interop [CustomMarshaller(typeof(string), MarshalMode.ElementRef, typeof(StringMarshaller))] public static class StringMarshaller { +#if FLAX_EDITOR + [HideInEditor] +#endif public static class NativeToManaged { public static string ConvertToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged); public static void Free(IntPtr unmanaged) => ManagedString.Free(unmanaged); } +#if FLAX_EDITOR + [HideInEditor] +#endif public static class ManagedToNative { public static unsafe IntPtr ConvertToUnmanaged(string managed) @@ -490,6 +560,9 @@ namespace FlaxEngine.Interop } } +#if FLAX_EDITOR + [HideInEditor] +#endif public struct Bidirectional { string managed; diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 93b81f655..eb35e475b 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -1294,6 +1294,9 @@ namespace Flax.Build.Bindings /// /// Marshaller for type . /// "); + #if FLAX_EDITOR + [HideInEditor] + #endif [CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.ManagedToUnmanagedIn, typeof({{marshallerName}}.ManagedToNative))] [CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.UnmanagedToManagedOut, typeof({{marshallerName}}.ManagedToNative))] [CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.ElementIn, typeof({{marshallerName}}.ManagedToNative))] @@ -1306,17 +1309,25 @@ namespace Flax.Build.Bindings {{GenerateCSharpAccessLevel(classInfo.Access)}}static class {{marshallerName}} { #pragma warning disable 1591 + #if FLAX_EDITOR + [HideInEditor] + #endif public static class NativeToManaged { public static {{classInfo.Name}} ConvertToManaged(IntPtr unmanaged) => Unsafe.As<{{classInfo.Name}}>(ManagedHandleMarshaller.NativeToManaged.ConvertToManaged(unmanaged)); public static void Free(IntPtr unmanaged) => ManagedHandleMarshaller.NativeToManaged.Free(unmanaged); } + #if FLAX_EDITOR + [HideInEditor] + #endif public static class ManagedToNative { public static IntPtr ConvertToUnmanaged({{classInfo.Name}} managed) => ManagedHandleMarshaller.ManagedToNative.ConvertToUnmanaged(managed); public static void Free(IntPtr unmanaged) => ManagedHandleMarshaller.ManagedToNative.Free(unmanaged); } + #if FLAX_EDITOR [HideInEditor] + #endif public struct Bidirectional { ManagedHandleMarshaller.Bidirectional marsh; @@ -1365,6 +1376,8 @@ namespace Flax.Build.Bindings contents.Append(indent).AppendLine($"/// "); contents.Append(indent).AppendLine($"/// Marshaller for type ."); contents.Append(indent).AppendLine($"/// "); + if (buildData.Target != null & buildData.Target.IsEditor) + contents.Append(indent).AppendLine("[HideInEditor]"); contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({structureInfo.Name}), MarshalMode.ManagedToUnmanagedIn, typeof({marshallerName}.ManagedToNative))]"); contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({structureInfo.Name}), MarshalMode.UnmanagedToManagedOut, typeof({marshallerName}.ManagedToNative))]"); contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({structureInfo.Name}), MarshalMode.ElementIn, typeof({marshallerName}.ManagedToNative))]"); @@ -1389,7 +1402,8 @@ namespace Flax.Build.Bindings // Native struct begin // TODO: skip using this utility structure if the auto-generated C# struct is already the same as XXXInternal here below GenerateCSharpAttributes(buildData, contents, indent, structureInfo, true); - contents.Append(indent).AppendLine("[HideInEditor]"); + if (buildData.Target != null & buildData.Target.IsEditor) + contents.Append(indent).AppendLine("[HideInEditor]"); contents.Append(indent).AppendLine("[StructLayout(LayoutKind.Sequential)]"); contents.Append(indent).Append("public struct ").Append(structureInfo.Name).Append("Internal"); if (structureInfo.BaseType != null && structureInfo.IsPod) @@ -1625,13 +1639,17 @@ namespace Flax.Build.Bindings // NativeToManaged stateless shape // NOTE: GCHandles of FlaxEngine.Object must not be released in this case - contents.Append(indent).AppendLine($"public static class NativeToManaged").Append(indent).AppendLine("{"); + if (buildData.Target != null && buildData.Target.IsEditor) + contents.Append(indent).AppendLine("[HideInEditor]"); + contents.Append(indent).AppendLine("public static class NativeToManaged").Append(indent).AppendLine("{"); contents.Append(indent2).AppendLine($"public static {structureInfo.Name} ConvertToManaged({structureInfo.Name}Internal unmanaged) => {marshallerName}.ToManaged(unmanaged);"); contents.Append(indent2).AppendLine($"public static void Free({structureInfo.Name}Internal unmanaged)"); contents.Append(indent2).AppendLine("{").Append(indent3).AppendLine(freeContents2.Replace("\n", "\n" + indent3).ToString().TrimEnd()).Append(indent2).AppendLine("}"); contents.Append(indent).AppendLine("}"); // ManagedToNative stateless shape + if (buildData.Target != null && buildData.Target.IsEditor) + contents.Append(indent).AppendLine("[HideInEditor]"); contents.Append(indent).AppendLine($"public static class ManagedToNative").Append(indent).AppendLine("{"); contents.Append(indent2).AppendLine($"public static {structureInfo.Name}Internal ConvertToUnmanaged({structureInfo.Name} managed) => {marshallerName}.ToNative(managed);"); contents.Append(indent2).AppendLine($"public static void Free({structureInfo.Name}Internal unmanaged) => {marshallerName}.Free(unmanaged);"); @@ -1639,7 +1657,8 @@ namespace Flax.Build.Bindings // Bidirectional stateful shape // NOTE: GCHandles of FlaxEngine.Object must not be released unless they were allocated by this marshaller - contents.Append(indent).AppendLine("[HideInEditor]"); + if (buildData.Target != null && buildData.Target.IsEditor) + contents.Append(indent).AppendLine("[HideInEditor]"); contents.Append(indent).AppendLine($"public struct Bidirectional").Append(indent).AppendLine("{"); contents.Append(indent2).AppendLine($"{structureInfo.Name} managed;"); contents.Append(indent2).AppendLine($"{structureInfo.Name}Internal unmanaged;"); @@ -1976,9 +1995,11 @@ namespace Flax.Build.Bindings { string marshallerName = interfaceInfo.Name + "Marshaller"; contents.AppendLine(); - contents.Append(indent).AppendLine($"/// "); + contents.Append(indent).AppendLine("/// "); contents.Append(indent).AppendLine($"/// Marshaller for type ."); - contents.Append(indent).AppendLine($"/// "); + contents.Append(indent).AppendLine("/// "); + if (buildData.Target != null & buildData.Target.IsEditor) + contents.Append(indent).AppendLine("[HideInEditor]"); contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({interfaceInfo.Name}), MarshalMode.Default, typeof({marshallerName}))]"); contents.Append(indent).AppendLine($"public static class {marshallerName}"); contents.Append(indent).AppendLine("{"); From ad1c573678fa7a9acb02fe6050a19d96c88ee55e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 25 May 2023 00:12:54 +0200 Subject: [PATCH 095/116] Fix false-positive error when interop structure has a additional memory size (alignment) --- Source/Engine/Engine/NativeInterop.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs index 52dc82fce..4f9822570 100644 --- a/Source/Engine/Engine/NativeInterop.cs +++ b/Source/Engine/Engine/NativeInterop.cs @@ -706,7 +706,7 @@ namespace FlaxEngine.Interop MarshalHelper.toManagedFieldMarshallers[i](MarshalHelper.marshallableFields[i], ref managedValue, fieldPtr, out int fieldOffset); fieldPtr += fieldOffset; } - Assert.IsTrue((fieldPtr - nativePtr) == Unsafe.SizeOf()); + Assert.IsTrue((fieldPtr - nativePtr) <= Unsafe.SizeOf()); } else managedValue = Unsafe.Read(nativePtr.ToPointer()); @@ -745,7 +745,7 @@ namespace FlaxEngine.Interop MarshalHelper.toNativeFieldMarshallers[i](MarshalHelper.marshallableFields[i], ref managedValue, nativePtr, out int fieldOffset); nativePtr += fieldOffset; } - Assert.IsTrue((nativePtr - fieldPtr) == Unsafe.SizeOf()); + Assert.IsTrue((nativePtr - fieldPtr) <= Unsafe.SizeOf()); } else Unsafe.AsRef(nativePtr.ToPointer()) = managedValue; From 9572073eda3c28852c17b44710d54e92560a926a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 26 May 2023 13:43:20 +0200 Subject: [PATCH 096/116] Add `CustomArgs` to compile and link environment in build tool for customization --- .../Tools/Flax.Build/Build/NativeCpp/CompileEnvironment.cs | 6 ++++++ Source/Tools/Flax.Build/Build/NativeCpp/LinkEnvironment.cs | 6 ++++++ Source/Tools/Flax.Build/Platforms/Mac/MacToolchain.cs | 2 ++ Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs | 2 ++ .../Flax.Build/Platforms/Windows/WindowsToolchainBase.cs | 2 ++ 5 files changed, 18 insertions(+) diff --git a/Source/Tools/Flax.Build/Build/NativeCpp/CompileEnvironment.cs b/Source/Tools/Flax.Build/Build/NativeCpp/CompileEnvironment.cs index 9ca96f4fe..72d0380e4 100644 --- a/Source/Tools/Flax.Build/Build/NativeCpp/CompileEnvironment.cs +++ b/Source/Tools/Flax.Build/Build/NativeCpp/CompileEnvironment.cs @@ -157,6 +157,11 @@ namespace Flax.Build.NativeCpp /// public readonly List IncludePaths = new List(); + /// + /// The collection of custom arguments to pass to the compilator. + /// + public readonly HashSet CustomArgs = new HashSet(); + /// public object Clone() { @@ -183,6 +188,7 @@ namespace Flax.Build.NativeCpp }; clone.PreprocessorDefinitions.AddRange(PreprocessorDefinitions); clone.IncludePaths.AddRange(IncludePaths); + clone.CustomArgs.AddRange(CustomArgs); return clone; } } diff --git a/Source/Tools/Flax.Build/Build/NativeCpp/LinkEnvironment.cs b/Source/Tools/Flax.Build/Build/NativeCpp/LinkEnvironment.cs index 34b7df03f..92f08d71f 100644 --- a/Source/Tools/Flax.Build/Build/NativeCpp/LinkEnvironment.cs +++ b/Source/Tools/Flax.Build/Build/NativeCpp/LinkEnvironment.cs @@ -106,6 +106,11 @@ namespace Flax.Build.NativeCpp /// public readonly List LibraryPaths = new List(); + /// + /// The collection of custom arguments to pass to the linker. + /// + public readonly HashSet CustomArgs = new HashSet(); + /// public object Clone() { @@ -127,6 +132,7 @@ namespace Flax.Build.NativeCpp clone.DocumentationFiles.AddRange(DocumentationFiles); clone.InputLibraries.AddRange(InputLibraries); clone.LibraryPaths.AddRange(LibraryPaths); + clone.CustomArgs.AddRange(CustomArgs); return clone; } } diff --git a/Source/Tools/Flax.Build/Platforms/Mac/MacToolchain.cs b/Source/Tools/Flax.Build/Platforms/Mac/MacToolchain.cs index bc6482fff..8c82d2058 100644 --- a/Source/Tools/Flax.Build/Platforms/Mac/MacToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Mac/MacToolchain.cs @@ -125,6 +125,7 @@ namespace Flax.Build.Platforms // Setup arguments shared by all source files var commonArgs = new List(); + commonArgs.AddRange(options.CompileEnv.CustomArgs); { commonArgs.Add("-c"); commonArgs.Add("-fmessage-length=0"); @@ -252,6 +253,7 @@ namespace Flax.Build.Platforms // Setup arguments var args = new List(); + args.AddRange(options.LinkEnv.CustomArgs); { args.Add(string.Format("-o \"{0}\"", outputFilePath)); AddArgsCommon(options, args); diff --git a/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs b/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs index 16e4b62f0..17e97c274 100644 --- a/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs @@ -315,6 +315,7 @@ namespace Flax.Build.Platforms // Setup arguments shared by all source files var commonArgs = new List(); + commonArgs.AddRange(options.CompileEnv.CustomArgs); SetupCompileCppFilesArgs(graph, options, commonArgs, outputPath); { commonArgs.Add("-c"); @@ -501,6 +502,7 @@ namespace Flax.Build.Platforms // Setup arguments var args = new List(); + args.AddRange(options.LinkEnv.CustomArgs); { args.Add(string.Format("-o \"{0}\"", outputFilePath)); diff --git a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs index 0190050fb..9cde9ae13 100644 --- a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs +++ b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs @@ -428,6 +428,7 @@ namespace Flax.Build.Platforms // Setup arguments shared by all source files var commonArgs = new List(); + commonArgs.AddRange(options.CompileEnv.CustomArgs); SetupCompileCppFilesArgs(graph, options, commonArgs); { // Suppress Startup Banner @@ -669,6 +670,7 @@ namespace Flax.Build.Platforms // Setup arguments var args = new List(); + args.AddRange(options.LinkEnv.CustomArgs); SetupLinkFilesArgs(graph, options, args); { // Suppress startup banner From 91fdd60b072bd7d9e8c7d0d81e15c135f5c10ec2 Mon Sep 17 00:00:00 2001 From: Wiktor Kocielski Date: Fri, 26 May 2023 08:35:55 +0300 Subject: [PATCH 097/116] BytesToText -> UnitsToText --- Source/Engine/Core/Utilities.h | 39 +++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/Source/Engine/Core/Utilities.h b/Source/Engine/Core/Utilities.h index c65200077..0c7de9323 100644 --- a/Source/Engine/Core/Utilities.h +++ b/Source/Engine/Core/Utilities.h @@ -31,20 +31,43 @@ namespace Utilities return (T)round((double)value * 1000.0) / (T)1000; } + // Converts units to the best fitting human-readable denominator + // @param units Units count + // @param divider Amount of units required for the next size + // @param sizes Array with human-readable sizes to convert from + // @return The best fitting string of the units + template + String UnitsToText(T units, int32 divider, const Array& sizes) + { + if(sizes.Count() == 0) + return String::Format(TEXT("{0}"), units); + int32 i = 0; + double dblSUnits = static_cast(units); + for (; static_cast(units / static_cast(divider)) > 0; i++, units /= divider) + dblSUnits = units / static_cast(divider); + if (i >= sizes.Count()) + return String::Format(TEXT("{0}{1}"), units, sizes[0]); + return String::Format(TEXT("{0}{1}"), RoundTo2DecimalPlaces(dblSUnits), sizes[i]); + } + // Converts size of the file (in bytes) to the best fitting string // @param bytes Size of the file in bytes // @return The best fitting string of the file size template String BytesToText(T bytes) { - static const Char* sizes[] = { TEXT("B"), TEXT("KB"), TEXT("MB"), TEXT("GB"), TEXT("TB") }; - uint64 i = 0; - double dblSByte = static_cast(bytes); - for (; static_cast(bytes / 1024.0) > 0; i++, bytes /= 1024) - dblSByte = bytes / 1024.0; - if (i >= ARRAY_COUNT(sizes)) - return String::Empty; - return String::Format(TEXT("{0} {1}"), RoundTo2DecimalPlaces(dblSByte), sizes[i]); + static Array sizes = { TEXT("b"), TEXT("Kb"), TEXT("Mb"), TEXT("Gb"), TEXT("Tb"), TEXT("Pb"), TEXT("Eb"), TEXT("Zb"), TEXT("Yb") }; + return UnitsToText(bytes, 1024, sizes); + } + + // Converts hertz to the best fitting string + // @param hertz Hertz for convertion + // @return The best fitting string + template + String HertzToText(T hertz) + { + static Array sizes = { TEXT("Hz"), TEXT("KHz"), TEXT("MHz"), TEXT("GHz"), TEXT("THz"), TEXT("PHz"), TEXT("EHz"), TEXT("ZHz"), TEXT("YHz") }; + return UnitsToText(hertz, 1000, sizes); } // Returns the amount of set bits in 32-bit integer. From bdf6a114911cb7102a60d30bfb525ba362cb4f27 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Fri, 26 May 2023 19:55:11 +0300 Subject: [PATCH 098/116] Prefer high-performance discrete GPUs when enumerating adapters --- .../DirectX/DX11/GPUDeviceDX11.cpp | 64 +++++++++++++++++-- .../DirectX/DX12/GPUDeviceDX12.cpp | 44 ++++++++++++- .../DirectX/IncludeDirectXHeaders.h | 2 + .../GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp | 18 ++++-- 4 files changed, 115 insertions(+), 13 deletions(-) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp index ede8ad404..1f833933a 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp @@ -114,7 +114,15 @@ GPUDevice* GPUDeviceDX11::Create() // Create DXGI factory #if PLATFORM_WINDOWS IDXGIFactory1* dxgiFactory; - HRESULT hr = CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory)); + IDXGIFactory6* dxgiFactory6; + HRESULT hr = CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory6)); + if (hr == S_OK) + dxgiFactory = dxgiFactory6; + else + { + dxgiFactory6 = nullptr; + hr = CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory)); + } #else IDXGIFactory2* dxgiFactory; HRESULT hr = CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory)); @@ -126,16 +134,17 @@ GPUDevice* GPUDeviceDX11::Create() } // Enumerate the DXGIFactory's adapters + int32 selectedAdapterIndex = -1; Array adapters; - IDXGIAdapter* tmpAdapter; - for (uint32 index = 0; dxgiFactory->EnumAdapters(index, &tmpAdapter) != DXGI_ERROR_NOT_FOUND; index++) + IDXGIAdapter* tempAdapter; + for (uint32 index = 0; dxgiFactory->EnumAdapters(index, &tempAdapter) != DXGI_ERROR_NOT_FOUND; index++) { GPUAdapterDX adapter; - if (tmpAdapter && TryCreateDevice(tmpAdapter, maxAllowedFeatureLevel, &adapter.MaxFeatureLevel)) + if (tempAdapter && TryCreateDevice(tempAdapter, maxAllowedFeatureLevel, &adapter.MaxFeatureLevel)) { adapter.Index = index; - VALIDATE_DIRECTX_RESULT(tmpAdapter->GetDesc(&adapter.Description)); - uint32 outputs = RenderToolsDX::CountAdapterOutputs(tmpAdapter); + VALIDATE_DIRECTX_RESULT(tempAdapter->GetDesc(&adapter.Description)); + uint32 outputs = RenderToolsDX::CountAdapterOutputs(tempAdapter); LOG(Info, "Adapter {1}: '{0}', DirectX {2}", adapter.Description.Description, index, RenderToolsDX::GetFeatureLevelString(adapter.MaxFeatureLevel)); LOG(Info, " Dedicated Video Memory: {0}, Dedicated System Memory: {1}, Shared System Memory: {2}, Output(s): {3}", Utilities::BytesToText(adapter.Description.DedicatedVideoMemory), Utilities::BytesToText(adapter.Description.DedicatedSystemMemory), Utilities::BytesToText(adapter.Description.SharedSystemMemory), outputs); @@ -143,9 +152,41 @@ GPUDevice* GPUDeviceDX11::Create() adapters.Add(adapter); } } +#if PLATFORM_WINDOWS + // Find the best performing adapter and prefer using it instead of the first device + const auto gpuPreference = DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE; + if (dxgiFactory6 != nullptr && selectedAdapterIndex == -1) + { + if (dxgiFactory6->EnumAdapterByGpuPreference(0, gpuPreference, IID_PPV_ARGS(&tempAdapter)) != DXGI_ERROR_NOT_FOUND) + { + GPUAdapterDX adapter; + if (tempAdapter && TryCreateDevice(tempAdapter, maxAllowedFeatureLevel, &adapter.MaxFeatureLevel)) + { + DXGI_ADAPTER_DESC desc; + VALIDATE_DIRECTX_RESULT(tempAdapter->GetDesc(&desc)); + for (int i = 0; i < adapters.Count(); i++) + { + if (adapters[i].Description.AdapterLuid.LowPart == desc.AdapterLuid.LowPart && + adapters[i].Description.AdapterLuid.HighPart == desc.AdapterLuid.HighPart) + { + selectedAdapterIndex = i; + break; + } + } + } + } + } +#endif // Select the adapter to use - GPUAdapterDX selectedAdapter = adapters[0]; + if (selectedAdapterIndex < 0) + selectedAdapterIndex = 0; + if (adapters.Count() == 0 || selectedAdapterIndex >= adapters.Count()) + { + LOG(Error, "Failed to find valid DirectX adapter!"); + return nullptr; + } + GPUAdapterDX selectedAdapter = adapters[selectedAdapterIndex]; uint32 vendorId = 0; if (CommandLine::Options.NVIDIA) vendorId = GPU_VENDOR_ID_NVIDIA; @@ -180,6 +221,15 @@ GPUDevice* GPUDeviceDX11::Create() Delete(device); return nullptr; } + +#if PLATFORM_WINDOWS + if (dxgiFactory6 != nullptr) + dxgiFactory6->Release(); + else +#endif + { + dxgiFactory->Release(); + } return device; } diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp index f12efeafc..456a793f1 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp @@ -89,7 +89,15 @@ GPUDevice* GPUDeviceDX12::Create() // Create DXGI factory (CreateDXGIFactory2 is supported on Windows 8.1 or newer) IDXGIFactory4* dxgiFactory; - HRESULT hr = CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory)); + IDXGIFactory6* dxgiFactory6; + HRESULT hr = CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory6)); + if (hr == S_OK) + dxgiFactory = dxgiFactory6; + else + { + dxgiFactory6 = nullptr; + hr = CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory)); + } if (hr != S_OK) { LOG(Error, "Cannot create DXGI adapter. Error code: {0:x}.", hr); @@ -97,6 +105,7 @@ GPUDevice* GPUDeviceDX12::Create() } // Enumerate the DXGIFactory's adapters + int32 selectedAdapterIndex = -1; Array adapters; IDXGIAdapter* tempAdapter; for (uint32 index = 0; dxgiFactory->EnumAdapters(index, &tempAdapter) != DXGI_ERROR_NOT_FOUND; index++) @@ -118,8 +127,39 @@ GPUDevice* GPUDeviceDX12::Create() } } + // Find the best performing adapter and prefer using it instead of the first device + const auto gpuPreference = DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE; + if (dxgiFactory6 != nullptr && selectedAdapterIndex == -1) + { + if (dxgiFactory6->EnumAdapterByGpuPreference(0, gpuPreference, IID_PPV_ARGS(&tempAdapter)) != DXGI_ERROR_NOT_FOUND) + { + GPUAdapterDX adapter; + if (tempAdapter && CheckDX12Support(tempAdapter)) + { + DXGI_ADAPTER_DESC desc; + VALIDATE_DIRECTX_RESULT(tempAdapter->GetDesc(&desc)); + for (int i = 0; i < adapters.Count(); i++) + { + if (adapters[i].Description.AdapterLuid.LowPart == desc.AdapterLuid.LowPart && + adapters[i].Description.AdapterLuid.HighPart == desc.AdapterLuid.HighPart) + { + selectedAdapterIndex = i; + break; + } + } + } + } + } + // Select the adapter to use - GPUAdapterDX selectedAdapter = adapters[0]; + if (selectedAdapterIndex < 0) + selectedAdapterIndex = 0; + if (adapters.Count() == 0 || selectedAdapterIndex >= adapters.Count()) + { + LOG(Error, "Failed to find valid DirectX adapter!"); + return nullptr; + } + GPUAdapterDX selectedAdapter = adapters[selectedAdapterIndex]; uint32 vendorId = 0; if (CommandLine::Options.NVIDIA) vendorId = GPU_VENDOR_ID_NVIDIA; diff --git a/Source/Engine/GraphicsDevice/DirectX/IncludeDirectXHeaders.h b/Source/Engine/GraphicsDevice/DirectX/IncludeDirectXHeaders.h index 79e8fb7bd..deb0168fb 100644 --- a/Source/Engine/GraphicsDevice/DirectX/IncludeDirectXHeaders.h +++ b/Source/Engine/GraphicsDevice/DirectX/IncludeDirectXHeaders.h @@ -40,9 +40,11 @@ typedef IGraphicsUnknown IDXGISwapChain3; #include #include #include +#include #endif #if GRAPHICS_API_DIRECTX12 #include +#include #endif #pragma comment(lib, "DXGI.lib") diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp index d78155fc8..9c6d158ce 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp @@ -1155,6 +1155,7 @@ GPUDevice* GPUDeviceVulkan::Create() #endif // Enumerate all GPU devices and pick one + int32 selectedAdapterIndex = -1; uint32 gpuCount = 0; VALIDATE_VULKAN_RESULT(vkEnumeratePhysicalDevices(Instance, &gpuCount, nullptr)); if (gpuCount <= 0) @@ -1187,6 +1188,9 @@ GPUDevice* GPUDeviceVulkan::Create() break; case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: type = TEXT("Discrete GPU"); + // Select the first discrete GPU device + if (selectedAdapterIndex == -1) + selectedAdapterIndex = gpuIndex; break; case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: type = TEXT("Virtual GPU"); @@ -1203,7 +1207,13 @@ GPUDevice* GPUDeviceVulkan::Create() } // Select the adapter to use - int32 selectedAdapter = 0; + if (selectedAdapterIndex < 0) + selectedAdapterIndex = 0; + if (adapters.Count() == 0 || selectedAdapterIndex >= adapters.Count()) + { + LOG(Error, "Failed to find valid Vulkan adapter!"); + return nullptr; + } uint32 vendorId = 0; if (CommandLine::Options.NVIDIA) vendorId = GPU_VENDOR_ID_NVIDIA; @@ -1217,15 +1227,15 @@ GPUDevice* GPUDeviceVulkan::Create() { if (adapters[i].GetVendorId() == vendorId) { - selectedAdapter = i; + selectedAdapterIndex = i; break; } } } - ASSERT(selectedAdapter != -1 && adapters[selectedAdapter].IsValid()); + ASSERT(adapters[selectedAdapterIndex].IsValid()); // Create device - auto device = New(ShaderProfile::Vulkan_SM5, New(adapters[selectedAdapter])); + auto device = New(ShaderProfile::Vulkan_SM5, New(adapters[selectedAdapterIndex])); if (device->Init()) { LOG(Warning, "Graphics Device init failed"); From 4e683dcc1577429e7c4332c7614e44b5642c396c Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Fri, 26 May 2023 20:17:14 +0300 Subject: [PATCH 099/116] Clean up DXGIFactory after use on D3D12 --- .../Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp index 456a793f1..257593a9d 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp @@ -202,6 +202,15 @@ GPUDevice* GPUDeviceDX12::Create() return nullptr; } +#if !(PLATFORM_XBOX_SCARLETT || PLATFORM_XBOX_ONE) + if (dxgiFactory6 != nullptr) + dxgiFactory6->Release(); + else +#endif + { + dxgiFactory->Release(); + } + return device; } From bca5e908f1b85da6ec41fb3e2d5bf283ff126326 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sat, 27 May 2023 00:40:52 +0300 Subject: [PATCH 100/116] Copy content files using Content Database instead of Content Importing Fixes an issue where copying asset files does not change the asset ID, causing the copied assets to remain linked to the original files. --- Source/Editor/Windows/ContentWindow.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index c9c1a8d21..c2b56466d 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -611,7 +611,20 @@ namespace FlaxEditor.Windows /// The files paths to import. public void Paste(string[] files) { - Editor.ContentImporting.Import(files, CurrentViewFolder); + List importFiles = new List(); + foreach (var sourcePath in files) + { + var item = Editor.ContentDatabase.Find(sourcePath); + if (item != null) + { + string targetPath = Path.Combine(CurrentViewFolder.Path, item.FileName); + Editor.ContentDatabase.Copy(item, targetPath); + } + else + importFiles.Add(sourcePath); + } + + Editor.ContentImporting.Import(importFiles, CurrentViewFolder); } /// From a21abaac8495b31c927ce94cc0ffa0e309a4e4aa Mon Sep 17 00:00:00 2001 From: Wiktor Kocielski Date: Fri, 26 May 2023 08:19:32 +0300 Subject: [PATCH 101/116] Implement INetworkObject::OnNetworkSync and make INetworkObject just virtual --- Source/Engine/Networking/INetworkObject.h | 19 ++++++++++++------- .../Engine/Networking/NetworkReplicator.cpp | 8 ++++++++ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/Source/Engine/Networking/INetworkObject.h b/Source/Engine/Networking/INetworkObject.h index 457811557..932c3aa0d 100644 --- a/Source/Engine/Networking/INetworkObject.h +++ b/Source/Engine/Networking/INetworkObject.h @@ -13,22 +13,27 @@ API_INTERFACE(Namespace = "FlaxEngine.Networking") class FLAXENGINE_API INetwork DECLARE_SCRIPTING_TYPE_MINIMAL(INetworkObject); public: /// - /// Event called when network objects gets spawned. + /// Event called when network object gets spawned. /// - API_FUNCTION() virtual void OnNetworkSpawn() = 0; + API_FUNCTION() virtual void OnNetworkSpawn() {}; /// - /// Event called when network objects gets despawned. + /// Event called when network object gets despawned. /// - API_FUNCTION() virtual void OnNetworkDespawn() = 0; + API_FUNCTION() virtual void OnNetworkDespawn() {}; /// /// Event called before network object gets replicated (before reading data). /// - API_FUNCTION() virtual void OnNetworkSerialize() = 0; + API_FUNCTION() virtual void OnNetworkSerialize() {}; /// - /// Event called when network objects gets replicated (after reading data). + /// Event called when network object gets replicated (after reading data). /// - API_FUNCTION() virtual void OnNetworkDeserialize() = 0; + API_FUNCTION() virtual void OnNetworkDeserialize() {}; + + /// + /// Event called when network object gets synced (called only once upon initial sync). + /// + API_FUNCTION() virtual void OnNetworkSync() {}; }; diff --git a/Source/Engine/Networking/NetworkReplicator.cpp b/Source/Engine/Networking/NetworkReplicator.cpp index 8d0ba3c0e..0a0319196 100644 --- a/Source/Engine/Networking/NetworkReplicator.cpp +++ b/Source/Engine/Networking/NetworkReplicator.cpp @@ -111,6 +111,7 @@ struct NetworkReplicatedObject uint8 Spawned : 1; DataContainer TargetClientIds; INetworkObject* AsNetworkObject; + bool NetworkObjectSync = false; NetworkReplicatedObject() { @@ -637,7 +638,14 @@ void InvokeObjectReplication(NetworkReplicatedObject& item, uint32 ownerFrame, b } if (item.AsNetworkObject) + { item.AsNetworkObject->OnNetworkDeserialize(); + if (!item.NetworkObjectSync) + { + item.AsNetworkObject->OnNetworkSync(); + item.NetworkObjectSync = true; + } + } // Speed up replication of client-owned objects to other clients from server to reduce lag (data has to go from client to server and then to other clients) if (NetworkManager::IsServer()) From c9d48d00e72703ed475939532206a36231d908a6 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 27 May 2023 11:15:54 -0500 Subject: [PATCH 102/116] Change default script name to MyScript and make the name invalid if it is called Script. --- Source/Editor/Content/Proxy/ScriptProxy.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Content/Proxy/ScriptProxy.cs b/Source/Editor/Content/Proxy/ScriptProxy.cs index 3196ea833..f688f37df 100644 --- a/Source/Editor/Content/Proxy/ScriptProxy.cs +++ b/Source/Editor/Content/Proxy/ScriptProxy.cs @@ -45,7 +45,7 @@ namespace FlaxEditor.Content } /// - public override string NewItemName => "Script"; + public override string NewItemName => "MyScript"; /// public override bool CanCreate(ContentFolder targetLocation) @@ -72,6 +72,8 @@ namespace FlaxEditor.Content // Scripts cannot start with digit. if (Char.IsDigit(filename[0])) return false; + if (filename.Equals("Script")) + return false; return true; } From bb567cafdedfa060fcc483f687540b46e0c1ef7f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 28 May 2023 11:46:48 +0200 Subject: [PATCH 103/116] Improve #1120 to use shared lookup table export from engine source and reduce memory alloc from Array via Span --- Source/Engine/Core/ObjectsRemovalService.cpp | 6 +++++ Source/Engine/Core/Utilities.h | 23 ++++++++++++-------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/Source/Engine/Core/ObjectsRemovalService.cpp b/Source/Engine/Core/ObjectsRemovalService.cpp index 77ce686e0..35458595e 100644 --- a/Source/Engine/Core/ObjectsRemovalService.cpp +++ b/Source/Engine/Core/ObjectsRemovalService.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. #include "ObjectsRemovalService.h" +#include "Utilities.h" #include "Collections/Dictionary.h" #include "Engine/Engine/Time.h" #include "Engine/Engine/EngineService.h" @@ -8,6 +9,11 @@ #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Scripting/ScriptingObject.h" +const Char* BytesSizesData[] = { TEXT("b"), TEXT("Kb"), TEXT("Mb"), TEXT("Gb"), TEXT("Tb"), TEXT("Pb"), TEXT("Eb"), TEXT("Zb"), TEXT("Yb") }; +const Char* HertzSizesData[] = { TEXT("Hz"), TEXT("KHz"), TEXT("MHz"), TEXT("GHz"), TEXT("THz"), TEXT("PHz"), TEXT("EHz"), TEXT("ZHz"), TEXT("YHz") }; +Span Utilities::Private::BytesSizes(BytesSizesData, ARRAY_COUNT(BytesSizesData)); +Span Utilities::Private::HertzSizes(HertzSizesData, ARRAY_COUNT(HertzSizesData)); + namespace ObjectsRemovalServiceImpl { CriticalSection PoolLocker; diff --git a/Source/Engine/Core/Utilities.h b/Source/Engine/Core/Utilities.h index 0c7de9323..36339baf5 100644 --- a/Source/Engine/Core/Utilities.h +++ b/Source/Engine/Core/Utilities.h @@ -4,12 +4,19 @@ #include "Types/BaseTypes.h" #include "Types/String.h" +#include "Types/Span.h" #if _MSC_VER && PLATFORM_SIMD_SSE4_2 #include #endif namespace Utilities { + struct Private + { + static FLAXENGINE_API Span BytesSizes; + static FLAXENGINE_API Span HertzSizes; + }; + // Round floating point value up to 1 decimal place template FORCE_INLINE T RoundTo1DecimalPlace(T value) @@ -37,17 +44,17 @@ namespace Utilities // @param sizes Array with human-readable sizes to convert from // @return The best fitting string of the units template - String UnitsToText(T units, int32 divider, const Array& sizes) + String UnitsToText(T units, int32 divider, const Span sizes) { - if(sizes.Count() == 0) + if (sizes.Length() == 0) return String::Format(TEXT("{0}"), units); int32 i = 0; double dblSUnits = static_cast(units); for (; static_cast(units / static_cast(divider)) > 0; i++, units /= divider) dblSUnits = units / static_cast(divider); - if (i >= sizes.Count()) - return String::Format(TEXT("{0}{1}"), units, sizes[0]); - return String::Format(TEXT("{0}{1}"), RoundTo2DecimalPlaces(dblSUnits), sizes[i]); + if (i >= sizes.Length()) + i = 0; + return String::Format(TEXT("{0} {1}"), RoundTo2DecimalPlaces(dblSUnits), sizes[i]); } // Converts size of the file (in bytes) to the best fitting string @@ -56,8 +63,7 @@ namespace Utilities template String BytesToText(T bytes) { - static Array sizes = { TEXT("b"), TEXT("Kb"), TEXT("Mb"), TEXT("Gb"), TEXT("Tb"), TEXT("Pb"), TEXT("Eb"), TEXT("Zb"), TEXT("Yb") }; - return UnitsToText(bytes, 1024, sizes); + return UnitsToText(bytes, 1024, Private::BytesSizes); } // Converts hertz to the best fitting string @@ -66,8 +72,7 @@ namespace Utilities template String HertzToText(T hertz) { - static Array sizes = { TEXT("Hz"), TEXT("KHz"), TEXT("MHz"), TEXT("GHz"), TEXT("THz"), TEXT("PHz"), TEXT("EHz"), TEXT("ZHz"), TEXT("YHz") }; - return UnitsToText(hertz, 1000, sizes); + return UnitsToText(hertz, 1000, Private::HertzSizes); } // Returns the amount of set bits in 32-bit integer. From ecc6f83130b78a19d667cd478566bb4146ed1068 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 28 May 2023 12:03:22 +0200 Subject: [PATCH 104/116] Use `Utilities::HertzToText` from #1120 to log cpu clock speed --- Source/Engine/Platform/Base/PlatformBase.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Platform/Base/PlatformBase.cpp b/Source/Engine/Platform/Base/PlatformBase.cpp index 353e2b8f9..eb02c1ce9 100644 --- a/Source/Engine/Platform/Base/PlatformBase.cpp +++ b/Source/Engine/Platform/Base/PlatformBase.cpp @@ -157,7 +157,7 @@ void PlatformBase::LogInfo() LOG(Info, "CPU package count: {0}, Core count: {1}, Logical processors: {2}", cpuInfo.ProcessorPackageCount, cpuInfo.ProcessorCoreCount, cpuInfo.LogicalProcessorCount); LOG(Info, "CPU Page size: {0}, cache line size: {1} bytes", Utilities::BytesToText(cpuInfo.PageSize), cpuInfo.CacheLineSize); LOG(Info, "L1 cache: {0}, L2 cache: {1}, L3 cache: {2}", Utilities::BytesToText(cpuInfo.L1CacheSize), Utilities::BytesToText(cpuInfo.L2CacheSize), Utilities::BytesToText(cpuInfo.L3CacheSize)); - LOG(Info, "Clock speed: {0} GHz", Utilities::RoundTo2DecimalPlaces(cpuInfo.ClockSpeed * 1e-9f)); + LOG(Info, "Clock speed: {0}", Utilities::HertzToText(cpuInfo.ClockSpeed)); const MemoryStats memStats = Platform::GetMemoryStats(); LOG(Info, "Physical Memory: {0} total, {1} used ({2}%)", Utilities::BytesToText(memStats.TotalPhysicalMemory), Utilities::BytesToText(memStats.UsedPhysicalMemory), Utilities::RoundTo2DecimalPlaces((float)memStats.UsedPhysicalMemory * 100.0f / (float)memStats.TotalPhysicalMemory)); From 95d89382c9f58a1f085e62e66ffd21feccda9aa7 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 28 May 2023 12:35:14 +0200 Subject: [PATCH 105/116] Update Flax `.gitignore` to skip generated code module header files --- .gitignore | 1 + Source/FlaxEngine.Gen.cpp | 12 ------------ Source/FlaxEngine.Gen.cs | 17 ----------------- Source/FlaxEngine.Gen.h | 15 --------------- 4 files changed, 1 insertion(+), 44 deletions(-) delete mode 100644 Source/FlaxEngine.Gen.cpp delete mode 100644 Source/FlaxEngine.Gen.cs delete mode 100644 Source/FlaxEngine.Gen.h diff --git a/.gitignore b/.gitignore index 8ee95cf27..1e9aef426 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ Cache/ Binaries/ Output/ Logs/ +Source/*.Gen.* Source/*.csproj /Package_*/ !Source/Engine/Debug diff --git a/Source/FlaxEngine.Gen.cpp b/Source/FlaxEngine.Gen.cpp deleted file mode 100644 index d175cbc6a..000000000 --- a/Source/FlaxEngine.Gen.cpp +++ /dev/null @@ -1,12 +0,0 @@ -// This code was auto-generated. Do not modify it. - -#include "Engine/Scripting/BinaryModule.h" -#include "FlaxEngine.Gen.h" - -StaticallyLinkedBinaryModuleInitializer StaticallyLinkedBinaryModuleFlaxEngine(GetBinaryModuleFlaxEngine); - -extern "C" BinaryModule* GetBinaryModuleFlaxEngine() -{ - static NativeBinaryModule module("FlaxEngine", MAssemblyOptions()); - return &module; -} diff --git a/Source/FlaxEngine.Gen.cs b/Source/FlaxEngine.Gen.cs deleted file mode 100644 index f6090396e..000000000 --- a/Source/FlaxEngine.Gen.cs +++ /dev/null @@ -1,17 +0,0 @@ -// This code was auto-generated. Do not modify it. - -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("FlaxEngine")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Flax")] -[assembly: AssemblyProduct("FlaxEngine")] -[assembly: AssemblyCopyright("Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] -[assembly: ComVisible(false)] -[assembly: Guid("b8442186-4a70-7c85-704a-857c68060f38")] -[assembly: AssemblyVersion("1.5.6339")] -[assembly: AssemblyFileVersion("1.5.6339")] diff --git a/Source/FlaxEngine.Gen.h b/Source/FlaxEngine.Gen.h deleted file mode 100644 index 81aab8d55..000000000 --- a/Source/FlaxEngine.Gen.h +++ /dev/null @@ -1,15 +0,0 @@ -// This code was auto-generated. Do not modify it. - -#pragma once - -#define FLAXENGINE_NAME "FlaxEngine" -#define FLAXENGINE_VERSION Version(1, 5, 6339) -#define FLAXENGINE_VERSION_TEXT "1.5.6339" -#define FLAXENGINE_VERSION_MAJOR 1 -#define FLAXENGINE_VERSION_MINOR 5 -#define FLAXENGINE_VERSION_BUILD 6339 -#define FLAXENGINE_COMPANY "Flax" -#define FLAXENGINE_COPYRIGHT "Copyright (c) 2012-2023 Wojciech Figat. All rights reserved." - -class BinaryModule; -extern "C" FLAXENGINE_API BinaryModule* GetBinaryModuleFlaxEngine(); From 583a5d9223a2e5f5ca2a6a05a5f4010a3a193f5b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 28 May 2023 13:17:43 +0200 Subject: [PATCH 106/116] Minor fixes to content window navigation --- Source/Editor/Content/GUI/ContentView.cs | 7 +++++-- Source/Editor/Windows/ContentWindow.cs | 8 ++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Source/Editor/Content/GUI/ContentView.cs b/Source/Editor/Content/GUI/ContentView.cs index 7eb2e0950..6065ca9f8 100644 --- a/Source/Editor/Content/GUI/ContentView.cs +++ b/Source/Editor/Content/GUI/ContentView.cs @@ -692,10 +692,13 @@ namespace FlaxEditor.Content.GUI c = char.ToLowerInvariant(c); for (int i = 0; i < _items.Count; i++) { - var name = _items[i].ShortName; + var item = _items[i]; + var name = item.ShortName; if (!string.IsNullOrEmpty(name) && char.ToLowerInvariant(name[0]) == c) { - Select(_items[i]); + Select(item); + if (Parent is Panel panel) + panel.ScrollViewTo(item, true); break; } } diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index c2b56466d..207abb84f 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -611,19 +611,15 @@ namespace FlaxEditor.Windows /// The files paths to import. public void Paste(string[] files) { - List importFiles = new List(); + var importFiles = new List(); foreach (var sourcePath in files) { var item = Editor.ContentDatabase.Find(sourcePath); if (item != null) - { - string targetPath = Path.Combine(CurrentViewFolder.Path, item.FileName); - Editor.ContentDatabase.Copy(item, targetPath); - } + Editor.ContentDatabase.Copy(item, Path.Combine(CurrentViewFolder.Path, item.FileName)); else importFiles.Add(sourcePath); } - Editor.ContentImporting.Import(importFiles, CurrentViewFolder); } From 3e906686db005d9ea7f1e187644d663700fdbae1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 28 May 2023 13:30:14 +0200 Subject: [PATCH 107/116] Improve #1119 to use a bit flag for object sync --- Source/Engine/Networking/NetworkReplicator.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Networking/NetworkReplicator.cpp b/Source/Engine/Networking/NetworkReplicator.cpp index 0a0319196..6f6211b74 100644 --- a/Source/Engine/Networking/NetworkReplicator.cpp +++ b/Source/Engine/Networking/NetworkReplicator.cpp @@ -109,13 +109,14 @@ struct NetworkReplicatedObject uint32 LastOwnerFrame = 0; NetworkObjectRole Role; uint8 Spawned : 1; + uint8 Synced : 1; DataContainer TargetClientIds; INetworkObject* AsNetworkObject; - bool NetworkObjectSync = false; NetworkReplicatedObject() { Spawned = 0; + Synced = 0; } bool operator==(const NetworkReplicatedObject& other) const @@ -640,10 +641,10 @@ void InvokeObjectReplication(NetworkReplicatedObject& item, uint32 ownerFrame, b if (item.AsNetworkObject) { item.AsNetworkObject->OnNetworkDeserialize(); - if (!item.NetworkObjectSync) + if (!item.Synced) { + item.Synced = true; item.AsNetworkObject->OnNetworkSync(); - item.NetworkObjectSync = true; } } From 9cdd1cbc45a14cd0b5df8f974b37fa1f960cf9db Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 28 May 2023 15:30:16 +0200 Subject: [PATCH 108/116] Add `Platform::GetMousePosition`/`WindowsPlatform::SetMousePosition` for unified access to screen-space mouse position on all platforms #1109 --- Source/Engine/Input/Keyboard.h | 3 +-- Source/Engine/Input/Mouse.h | 3 --- Source/Engine/Platform/Base/PlatformBase.cpp | 16 ++++++++++++++ Source/Engine/Platform/Base/PlatformBase.h | 12 +++++++++++ .../Engine/Platform/Linux/LinuxPlatform.cpp | 5 +---- Source/Engine/Platform/UWP/UWPPlatform.cpp | 21 ------------------- Source/Engine/Platform/UWP/UWPPlatform.h | 2 -- Source/Engine/Platform/Win32/Win32Platform.h | 2 +- .../Platform/Windows/WindowsPlatform.cpp | 12 +++++++++++ .../Engine/Platform/Windows/WindowsPlatform.h | 2 ++ 10 files changed, 45 insertions(+), 33 deletions(-) diff --git a/Source/Engine/Input/Keyboard.h b/Source/Engine/Input/Keyboard.h index 45f8e295e..7e238bcf5 100644 --- a/Source/Engine/Input/Keyboard.h +++ b/Source/Engine/Input/Keyboard.h @@ -26,9 +26,8 @@ protected: public: /// - /// Gets the text entered during the current frame. + /// Gets the text entered during the current frame (Unicode format). /// - /// The input text (Unicode). API_PROPERTY() StringView GetInputText() const { return StringView(_state.InputText, _state.InputTextLength); diff --git a/Source/Engine/Input/Mouse.h b/Source/Engine/Input/Mouse.h index 7fc2be75a..0acd31238 100644 --- a/Source/Engine/Input/Mouse.h +++ b/Source/Engine/Input/Mouse.h @@ -58,7 +58,6 @@ public: /// /// Gets the position of the mouse in the screen-space coordinates. /// - /// The mouse position API_PROPERTY() FORCE_INLINE Float2 GetPosition() const { return _state.MousePosition; @@ -72,7 +71,6 @@ public: /// /// Gets the delta position of the mouse in the screen-space coordinates. /// - /// The mouse position delta API_PROPERTY() FORCE_INLINE Float2 GetPositionDelta() const { return _state.MousePosition - _prevState.MousePosition; @@ -81,7 +79,6 @@ public: /// /// Gets the mouse wheel change during the last frame. /// - /// Mouse wheel value delta API_PROPERTY() FORCE_INLINE float GetScrollDelta() const { return _state.MouseWheelDelta; diff --git a/Source/Engine/Platform/Base/PlatformBase.cpp b/Source/Engine/Platform/Base/PlatformBase.cpp index eb02c1ce9..89fcc1011 100644 --- a/Source/Engine/Platform/Base/PlatformBase.cpp +++ b/Source/Engine/Platform/Base/PlatformBase.cpp @@ -5,6 +5,7 @@ #include "Engine/Platform/MemoryStats.h" #include "Engine/Platform/MessageBox.h" #include "Engine/Platform/FileSystem.h" +#include "Engine/Platform/Window.h" #include "Engine/Platform/User.h" #include "Engine/Core/Log.h" #include "Engine/Core/Types/DateTime.h" @@ -520,6 +521,21 @@ void PlatformBase::CreateGuid(Guid& result) result = Guid(dateThingHigh, randomThing | (sequentialThing << 16), cyclesThing, dateThingLow); } +Float2 PlatformBase::GetMousePosition() +{ + const Window* win = Engine::MainWindow; + if (win) + return win->ClientToScreen(win->GetMousePosition()); + return Float2::Minimum; +} + +void PlatformBase::SetMousePosition(const Float2& position) +{ + const Window* win = Engine::MainWindow; + if (win) + win->SetMousePosition(win->ScreenToClient(position)); +} + Float2 PlatformBase::GetVirtualDesktopSize() { return Platform::GetVirtualDesktopBounds().Size; diff --git a/Source/Engine/Platform/Base/PlatformBase.h b/Source/Engine/Platform/Base/PlatformBase.h index e7800c94d..7b06fa3d0 100644 --- a/Source/Engine/Platform/Base/PlatformBase.h +++ b/Source/Engine/Platform/Base/PlatformBase.h @@ -660,6 +660,18 @@ public: public: + /// + /// Gets the mouse cursor position in screen-space coordinates. + /// + /// Mouse cursor coordinates. + API_PROPERTY() static Float2 GetMousePosition(); + + /// + /// Sets the mouse cursor position in screen-space coordinates. + /// + /// Cursor position to set. + API_PROPERTY() static void SetMousePosition(const Float2& position); + /// /// Gets the origin position and size of the monitor at the given screen-space location. /// diff --git a/Source/Engine/Platform/Linux/LinuxPlatform.cpp b/Source/Engine/Platform/Linux/LinuxPlatform.cpp index 631b8d017..e954dd784 100644 --- a/Source/Engine/Platform/Linux/LinuxPlatform.cpp +++ b/Source/Engine/Platform/Linux/LinuxPlatform.cpp @@ -2643,10 +2643,8 @@ Float2 LinuxPlatform::GetMousePosition() { if (!xDisplay) return Float2::Zero; - - int32 x, y; + int32 x = 0, y = 0; uint32 screenCount = (uint32)X11::XScreenCount(xDisplay); - for (uint32 i = 0; i < screenCount; i++) { X11::Window outRoot, outChild; @@ -2655,7 +2653,6 @@ Float2 LinuxPlatform::GetMousePosition() if (X11::XQueryPointer(xDisplay, X11::XRootWindow(xDisplay, i), &outRoot, &outChild, &x, &y, &childX, &childY, &mask)) break; } - return Float2((float)x, (float)y); } diff --git a/Source/Engine/Platform/UWP/UWPPlatform.cpp b/Source/Engine/Platform/UWP/UWPPlatform.cpp index f7c883878..06a0d964d 100644 --- a/Source/Engine/Platform/UWP/UWPPlatform.cpp +++ b/Source/Engine/Platform/UWP/UWPPlatform.cpp @@ -158,27 +158,6 @@ void UWPPlatform::OpenUrl(const StringView& url) // TODO: add support for OpenUrl on UWP } -Float2 UWPPlatform::GetMousePosition() -{ - // Use the main window - auto win = Engine::MainWindow; - if (win) - { - return win->ClientToScreen(win->GetMousePosition()); - } - return Float2::Minimum; -} - -void UWPPlatform::SetMousePosition(const Float2& pos) -{ - // Use the main window - auto win = Engine::MainWindow; - if (win) - { - win->SetMousePosition(win->ScreenToClient(pos)); - } -} - Float2 UWPPlatform::GetDesktopSize() { Float2 result; diff --git a/Source/Engine/Platform/UWP/UWPPlatform.h b/Source/Engine/Platform/UWP/UWPPlatform.h index be114f419..9bc0d7afd 100644 --- a/Source/Engine/Platform/UWP/UWPPlatform.h +++ b/Source/Engine/Platform/UWP/UWPPlatform.h @@ -37,8 +37,6 @@ public: static bool GetHasFocus(); static bool CanOpenUrl(const StringView& url); static void OpenUrl(const StringView& url); - static Float2 GetMousePosition(); - static void SetMousePosition(const Float2& pos); static Rectangle GetMonitorBounds(const Float2& screenPos); static Float2 GetDesktopSize(); static Rectangle GetVirtualDesktopBounds(); diff --git a/Source/Engine/Platform/Win32/Win32Platform.h b/Source/Engine/Platform/Win32/Win32Platform.h index b50d31616..d3f9a5cdc 100644 --- a/Source/Engine/Platform/Win32/Win32Platform.h +++ b/Source/Engine/Platform/Win32/Win32Platform.h @@ -109,7 +109,7 @@ public: static void CreateGuid(Guid& result); static String GetMainDirectory(); static String GetExecutableFilePath(); - static struct Guid GetUniqueDeviceId(); + static Guid GetUniqueDeviceId(); static String GetWorkingDirectory(); static bool SetWorkingDirectory(const String& path); static void FreeLibrary(void* handle); diff --git a/Source/Engine/Platform/Windows/WindowsPlatform.cpp b/Source/Engine/Platform/Windows/WindowsPlatform.cpp index e215cbaf0..7c8a5b060 100644 --- a/Source/Engine/Platform/Windows/WindowsPlatform.cpp +++ b/Source/Engine/Platform/Windows/WindowsPlatform.cpp @@ -825,6 +825,18 @@ void WindowsPlatform::OpenUrl(const StringView& url) ::ShellExecuteW(nullptr, TEXT("open"), *url, nullptr, nullptr, SW_SHOWNORMAL); } +Float2 WindowsPlatform::GetMousePosition() +{ + POINT cursorPos; + GetCursorPos(&cursorPos); + return Float2((float)cursorPos.x, (float)cursorPos.y); +} + +void WindowsPlatform::SetMousePosition(const Float2& pos) +{ + ::SetCursorPos((int)pos.X, (int)pos.Y); +} + struct GetMonitorBoundsData { Float2 Pos; diff --git a/Source/Engine/Platform/Windows/WindowsPlatform.h b/Source/Engine/Platform/Windows/WindowsPlatform.h index 7441d0812..a144e09e5 100644 --- a/Source/Engine/Platform/Windows/WindowsPlatform.h +++ b/Source/Engine/Platform/Windows/WindowsPlatform.h @@ -71,6 +71,8 @@ public: static bool GetHasFocus(); static bool CanOpenUrl(const StringView& url); static void OpenUrl(const StringView& url); + static Float2 GetMousePosition(); + static void SetMousePosition(const Float2& pos); static Rectangle GetMonitorBounds(const Float2& screenPos); static Float2 GetDesktopSize(); static Rectangle GetVirtualDesktopBounds(); From 62946f63598ae501bd3c217dc7b602a280a44d35 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 28 May 2023 16:12:32 +0200 Subject: [PATCH 109/116] Cleanup and improve code from #1109 --- .../Editor/GUI/Dialogs/ColorPickerDialog.cs | 61 ++++---- Source/Editor/GUI/Input/ColorValueBox.cs | 7 +- Source/Editor/Surface/Elements/ColorValue.cs | 2 +- Source/Editor/Utilities/ScreenUtilities.cpp | 130 ++++++++++++++++++ Source/Editor/Utilities/ScreenUtilities.h | 33 +++++ .../ScreenUtilities/ScreenUtilities.h | 40 ------ .../ScreenUtilities/ScreenUtilitiesLinux.cpp | 104 -------------- .../ScreenUtilities/ScreenUtilitiesMac.cpp | 46 ------- .../ScreenUtilitiesWindows.cpp | 94 ------------- Source/Editor/Utilities/Utils.cs | 4 +- .../Engine/Platform/Linux/LinuxPlatform.cpp | 13 +- Source/Engine/Platform/Linux/LinuxPlatform.h | 2 +- Source/Engine/UI/GUI/Common/IconButton.cs | 74 ---------- Source/Engine/Utilities/Screenshot.cpp | 2 - 14 files changed, 212 insertions(+), 400 deletions(-) create mode 100644 Source/Editor/Utilities/ScreenUtilities.cpp create mode 100644 Source/Editor/Utilities/ScreenUtilities.h delete mode 100644 Source/Editor/Utilities/ScreenUtilities/ScreenUtilities.h delete mode 100644 Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp delete mode 100644 Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesMac.cpp delete mode 100644 Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesWindows.cpp delete mode 100644 Source/Engine/UI/GUI/Common/IconButton.cs diff --git a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs index 0c81ecc51..161b3f4ae 100644 --- a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs +++ b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs @@ -1,10 +1,8 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using FlaxEditor.GUI.Input; -using FlaxEditor.Windows; using FlaxEngine; using FlaxEngine.GUI; -using System; namespace FlaxEditor.GUI.Dialogs { @@ -52,7 +50,7 @@ namespace FlaxEditor.GUI.Dialogs private TextBox _cHex; private Button _cCancel; private Button _cOK; - private IconButton _cEyedropper; + private Button _cEyedropper; /// /// Gets the selected color. @@ -109,7 +107,6 @@ namespace FlaxEditor.GUI.Dialogs { _initialValue = initialValue; _useDynamicEditing = useDynamicEditing; - _activeEyedropper = false; _value = Color.Transparent; _onChanged = colorChanged; _onClosed = pickerClosed; @@ -199,46 +196,41 @@ namespace FlaxEditor.GUI.Dialogs _cOK.Clicked += OnSubmit; // Eyedropper button - _cEyedropper = new IconButton(_cOK.X - EyedropperMargin, _cHex.Bottom + PickerMargin, Editor.Instance.Icons.Add64, hideBorder: false) + var style = Style.Current; + _cEyedropper = new Button(_cOK.X - EyedropperMargin, _cHex.Bottom + PickerMargin) { + TooltipText = "Eyedropper tool to pick a color directly from the screen", + BackgroundBrush = new SpriteBrush(Editor.Instance.Icons.Search32), + BackgroundColor = style.Foreground, + BackgroundColorHighlighted = style.Foreground.RGBMultiplied(0.9f), + BorderColor = Color.Transparent, + BorderColorHighlighted = style.BorderSelected, Parent = this, }; _cEyedropper.Clicked += OnEyedropStart; _cEyedropper.Height = (_cValue.Bottom - _cEyedropper.Y) * 0.5f; _cEyedropper.Width = _cEyedropper.Height; _cEyedropper.X -= _cEyedropper.Width; - //_cEyedropper.SetColors(_cEyedropper.BackgroundColor); // Set initial color SelectedColor = initialValue; } - private Color32 GetEyedropColor() + private void OnColorPicked(Color32 colorPicked) { - Int2 mousePosition = ScreenUtilities.GetScreenCursorPosition(); - Color32 pixelColor = ScreenUtilities.GetPixelAt(mousePosition.X, mousePosition.Y); - - return pixelColor; - } - - private void ColorPicked(Color32 colorPicked) - { - _activeEyedropper = false; - SelectedColor = colorPicked; - ScreenUtilities.PickColorDone -= ColorPicked; + if (_activeEyedropper) + { + _activeEyedropper = false; + SelectedColor = colorPicked; + ScreenUtilities.PickColorDone -= OnColorPicked; + } } private void OnEyedropStart() { _activeEyedropper = true; ScreenUtilities.PickColor(); - ScreenUtilities.PickColorDone += ColorPicked; - } - - private void UpdateEyedrop() - { - Color32 pixelColor = GetEyedropColor(); - SelectedColor = pixelColor; + ScreenUtilities.PickColorDone += OnColorPicked; } private void OnRGBAChanged() @@ -266,15 +258,16 @@ namespace FlaxEditor.GUI.Dialogs SelectedColor = color; } - /// public override void Update(float deltaTime) { base.Update(deltaTime); + // Update eye dropper tool if (_activeEyedropper) { - UpdateEyedrop(); + Float2 mousePosition = Platform.MousePosition; + SelectedColor = ScreenUtilities.GetColorAt(mousePosition); } } @@ -331,6 +324,20 @@ namespace FlaxEditor.GUI.Dialogs base.OnShow(); } + /// + public override bool OnKeyDown(KeyboardKeys key) + { + if (_activeEyedropper && key == KeyboardKeys.Escape) + { + // Cancel eye dropping + _activeEyedropper = false; + ScreenUtilities.PickColorDone -= OnColorPicked; + return true; + } + + return base.OnKeyDown(key); + } + /// public override void OnSubmit() { diff --git a/Source/Editor/GUI/Input/ColorValueBox.cs b/Source/Editor/GUI/Input/ColorValueBox.cs index bafa27c87..167cc65bb 100644 --- a/Source/Editor/GUI/Input/ColorValueBox.cs +++ b/Source/Editor/GUI/Input/ColorValueBox.cs @@ -57,6 +57,11 @@ namespace FlaxEditor.GUI.Input /// protected Color _value; + /// + /// Enables live preview of the selected value from the picker. Otherwise will update the value only when user confirms it on dialog closing. + /// + public bool UseDynamicEditing = true; + /// /// Occurs when value gets changed. /// @@ -143,7 +148,7 @@ namespace FlaxEditor.GUI.Input base.OnSubmit(); // Show color picker dialog - _currentDialog = ShowPickColorDialog?.Invoke(this, _value, OnColorChanged, OnPickerClosed); + _currentDialog = ShowPickColorDialog?.Invoke(this, _value, OnColorChanged, OnPickerClosed, UseDynamicEditing); } private void OnColorChanged(Color color, bool sliding) diff --git a/Source/Editor/Surface/Elements/ColorValue.cs b/Source/Editor/Surface/Elements/ColorValue.cs index 96069cfeb..19934581d 100644 --- a/Source/Editor/Surface/Elements/ColorValue.cs +++ b/Source/Editor/Surface/Elements/ColorValue.cs @@ -30,7 +30,7 @@ namespace FlaxEditor.Surface.Elements { ParentNode = parentNode; Archetype = archetype; - + UseDynamicEditing = false; ParentNode.ValuesChanged += OnNodeValuesChanged; } diff --git a/Source/Editor/Utilities/ScreenUtilities.cpp b/Source/Editor/Utilities/ScreenUtilities.cpp new file mode 100644 index 000000000..cff41f7bf --- /dev/null +++ b/Source/Editor/Utilities/ScreenUtilities.cpp @@ -0,0 +1,130 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +#include "ScreenUtilities.h" +#include "Engine/Core/Math/Vector2.h" +#include "Engine/Core/Delegate.h" +#include "Engine/Core/Log.h" +#include "Engine/Profiler/ProfilerCPU.h" + +Delegate ScreenUtilities::PickColorDone; + +#if PLATFORM_WINDOWS + +#include + +#pragma comment(lib, "Gdi32.lib") + +static HHOOK MouseCallbackHook; + +LRESULT CALLBACK OnScreenUtilsMouseCallback(_In_ int nCode, _In_ WPARAM wParam, _In_ LPARAM lParam) +{ + if (nCode >= 0 && wParam == WM_LBUTTONDOWN) + { + UnhookWindowsHookEx(MouseCallbackHook); + + // Push event with the picked color + const Float2 cursorPos = Platform::GetMousePosition(); + const Color32 colorPicked = ScreenUtilities::GetColorAt(cursorPos); + ScreenUtilities::PickColorDone(colorPicked); + return 1; + } + return CallNextHookEx(NULL, nCode, wParam, lParam); +} + +Color32 ScreenUtilities::GetColorAt(const Float2& pos) +{ + PROFILE_CPU(); + HDC deviceContext = GetDC(NULL); + COLORREF color = GetPixel(deviceContext, (int)pos.X, (int)pos.Y); + ReleaseDC(NULL, deviceContext); + return Color32(GetRValue(color), GetGValue(color), GetBValue(color), 255); +} + +void ScreenUtilities::PickColor() +{ + MouseCallbackHook = SetWindowsHookEx(WH_MOUSE_LL, OnScreenUtilsMouseCallback, NULL, NULL); + if (MouseCallbackHook == NULL) + { + LOG(Warning, "Failed to set mouse hook."); + LOG(Warning, "Error: {0}", GetLastError()); + } +} + +#elif PLATFORM_LINUX + +#include "Engine/Platform/Linux/LinuxPlatform.h" +#include "Engine/Platform/Linux/IncludeX11.h" + +Color32 ScreenUtilities::GetColorAt(const Float2& pos) +{ + X11::XColor color; + + X11::Display* display = (X11::Display*) LinuxPlatform::GetXDisplay(); + int defaultScreen = X11::XDefaultScreen(display); + + X11::XImage* image; + image = X11::XGetImage(display, X11::XRootWindow(display, defaultScreen), x, y, 1, 1, AllPlanes, XYPixmap); + color.pixel = XGetPixel(image, 0, 0); + X11::XFree(image); + + X11::XQueryColor(display, X11::XDefaultColormap(display, defaultScreen), &color); + + Color32 outputColor; + outputColor.R = color.red / 256; + outputColor.G = color.green / 256; + outputColor.B = color.blue / 256; + return outputColor; +} + +void OnScreenUtilsXEventCallback(void* eventPtr) +{ + X11::XEvent* event = (X11::XEvent*) eventPtr; + X11::Display* display = (X11::Display*)LinuxPlatform::GetXDisplay(); + if (event->type == ButtonPress) + { + const Float2 cursorPos = Platform::GetMousePosition(); + const Color32 colorPicked = ScreenUtilities::GetColorAt(cursorPos); + X11::XUngrabPointer(display, CurrentTime); + ScreenUtilities::PickColorDone(colorPicked); + LinuxPlatform::xEventRecieved.Unbind(OnScreenUtilsXEventCallback); + } +} + +void ScreenUtilities::PickColor() +{ + PROFILE_CPU(); + X11::Display* display = (X11::Display*) LinuxPlatform::GetXDisplay(); + X11::Window rootWindow = X11::XRootWindow(display, X11::XDefaultScreen(display)); + + X11::Cursor cursor = XCreateFontCursor(display, 130); + int grabbedPointer = X11::XGrabPointer(display, rootWindow, 0, ButtonPressMask, GrabModeAsync, GrabModeAsync, rootWindow, cursor, CurrentTime); + if (grabbedPointer != GrabSuccess) + { + LOG(Error, "Failed to grab cursor for events."); + X11::XFreeCursor(display, cursor); + return; + } + + X11::XFreeCursor(display, cursor); + LinuxPlatform::xEventRecieved.Bind(OnScreenUtilsXEventCallback); +} + +#elif PLATFORM_MAC + +#include +#include + +Color32 ScreenUtilities::GetColorAt(const Float2& pos) +{ + // TODO: implement ScreenUtilities for macOS + return { 0, 0, 0, 255 }; +} + +void ScreenUtilities::PickColor() +{ + // This is what C# calls to start the color picking sequence + // This should stop mouse clicks from working for one click, and that click is on the selected color + // There is a class called NSColorSample that might implement that for you, but maybe not. +} + +#endif diff --git a/Source/Editor/Utilities/ScreenUtilities.h b/Source/Editor/Utilities/ScreenUtilities.h new file mode 100644 index 000000000..506dc8634 --- /dev/null +++ b/Source/Editor/Utilities/ScreenUtilities.h @@ -0,0 +1,33 @@ +// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Engine/Core/Types/BaseTypes.h" +#include "Engine/Core/Math/Color32.h" +#include "Engine/Core/Math/Vector2.h" +#include "Engine/Core/Delegate.h" + +/// +/// Platform-dependent screen utilities. +/// +API_CLASS(Static) class FLAXENGINE_API ScreenUtilities +{ + DECLARE_SCRIPTING_TYPE_MINIMAL(ScreenUtilities); + + /// + /// Gets the pixel color at the specified coordinates. + /// + /// Screen-space coordinate to read. + /// Pixel color at the specified coordinates. + API_FUNCTION() static Color32 GetColorAt(const Float2& pos); + + /// + /// Starts async color picking. Color will be returned through PickColorDone event when the actions ends (user selected the final color with a mouse). When action is active, GetColorAt can be used to read the current value. + /// + API_FUNCTION() static void PickColor(); + + /// + /// Called when PickColor action is finished. + /// + API_EVENT() static Delegate PickColorDone; +}; diff --git a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilities.h b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilities.h deleted file mode 100644 index 01380e25b..000000000 --- a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilities.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include "Engine/Core/Types/BaseTypes.h" -#include "Engine/Core/Delegate.h" - -API_INJECT_CODE(cpp, "#include \"Editor/Utilities/ScreenUtilities/ScreenUtilities.h\""); - -/// -/// Platform-dependent screen utilties. -/// -API_CLASS(Static, Name = "ScreenUtilities", Tag = "NativeInvokeUseName") -class FLAXENGINE_API ScreenUtilities -{ -public: - static struct FLAXENGINE_API ScriptingTypeInitializer TypeInitializer; - - /// - /// Gets the pixel color at the specified coordinates. - /// - /// X Coordinate to read. - /// Y Coordinate to read. - /// Pixel color at the specified coordinates. - API_FUNCTION() static Color32 GetPixelAt(int32 x, int32 y); - - /// - /// Gets the cursor position, in screen cooridnates. - /// - /// Cursor position, in screen coordinates. - API_FUNCTION() static Int2 GetScreenCursorPosition(); - - /// - /// Starts async color picking. Will return a color through ColorReturnCallback. - /// - /// Called when PickColor() is finished. - /// - API_EVENT() static Delegate PickColorDone; -}; diff --git a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp deleted file mode 100644 index 6dc2103d9..000000000 --- a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesLinux.cpp +++ /dev/null @@ -1,104 +0,0 @@ -#if PLATFORM_LINUX - -#include "ScreenUtilities.h" -#include "Engine/Core/Math/Color32.h" -#include "Engine/Core/Math/Vector2.h" -#include "Engine/Core/Delegate.h" -#include "Engine/Core/Log.h" -#include "Engine/Platform/Linux/LinuxPlatform.h" - -#include "Engine/Platform/Linux/IncludeX11.h" - -Color32 ScreenUtilities::GetPixelAt(int32 x, int32 y) -{ - X11::XColor color; - Color32 outputColor; - - X11::Display* display = (X11::Display*) LinuxPlatform::GetXDisplay(); - int defaultScreen = X11::XDefaultScreen(display); - - X11::XImage* image; - image = X11::XGetImage(display, X11::XRootWindow(display, defaultScreen), x, y, 1, 1, AllPlanes, XYPixmap); - color.pixel = XGetPixel(image, 0, 0); - X11::XFree(image); - - X11::XQueryColor(display, X11::XDefaultColormap(display, defaultScreen), &color); - outputColor.R = color.red / 256; - outputColor.G = color.green / 256; - outputColor.B = color.blue / 256; - - return outputColor; -} - -Int2 ScreenUtilities::GetScreenCursorPosition() -{ - Int2 cursorPosition = { 0, 0 }; - X11::Display* display = (X11::Display*) LinuxPlatform::GetXDisplay(); - X11::Window rootWindow = X11::XRootWindow(display, X11::XDefaultScreen(display)); - - // Buffers (Some useful, some not.) - X11::Window rootWindowBuffer; - int rootX, rootY; - int winXBuffer, winYBuffer; - unsigned int maskBuffer; - - int gotPointer = X11::XQueryPointer(display, rootWindow, &rootWindowBuffer, &rootWindowBuffer, &rootX, &rootY, &winXBuffer, &winYBuffer, &maskBuffer); - if (!gotPointer) { - LOG(Error, "Failed to find the mouse pointer (Are you using multiple displays?)"); - return cursorPosition; - } - - cursorPosition.X = rootX; - cursorPosition.Y = rootY; - - return cursorPosition; -} - -class ScreenUtilitiesLinux -{ -public: - static void BlockAndReadMouse(); - static void xEventHandler(void* event); -}; - -void ScreenUtilitiesLinux::xEventHandler(void* eventPtr) { - X11::XEvent* event = (X11::XEvent*) eventPtr; - - X11::Display* display = (X11::Display*) LinuxPlatform::GetXDisplay(); - - if (event->type == ButtonPress) { - Int2 cursorPosition = ScreenUtilities::GetScreenCursorPosition(); - Color32 colorPicked = ScreenUtilities::GetPixelAt(cursorPosition.X, cursorPosition.Y); - - ScreenUtilities::PickColorDone(colorPicked); // Run the callback for picking colors being complete. - LinuxPlatform::xEventRecieved.Unbind(xEventHandler); // Unbind the event, we only want to handle one click event - X11::XUngrabPointer(display, CurrentTime); - } -} - -void ScreenUtilitiesLinux::BlockAndReadMouse() -{ - X11::Display* display = (X11::Display*) LinuxPlatform::GetXDisplay(); - X11::Window rootWindow = X11::XRootWindow(display, X11::XDefaultScreen(display)); - - X11::Cursor cursor = XCreateFontCursor(display, 130); - int grabbedPointer = X11::XGrabPointer(display, rootWindow, 0, ButtonPressMask, GrabModeAsync, GrabModeAsync, rootWindow, cursor, CurrentTime); - if (grabbedPointer != GrabSuccess) { - LOG(Error, "Failed to grab cursor for events."); - - X11::XFreeCursor(display, cursor); - return; - } - - X11::XFreeCursor(display, cursor); - LinuxPlatform::xEventRecieved.Bind(xEventHandler); -} - -Delegate ScreenUtilities::PickColorDone; - -void ScreenUtilities::PickColor() -{ - ScreenUtilitiesLinux::BlockAndReadMouse(); -} - -#endif \ No newline at end of file diff --git a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesMac.cpp b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesMac.cpp deleted file mode 100644 index f774242ce..000000000 --- a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesMac.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#if PLATFORM_MAC -#include -#include - -#include "ScreenUtilities.h" -#include "Engine/Core/Math/Color32.h" -#include "Engine/Core/Math/Vector2.h" -#include "Engine/Core/Delegate.h" -#include "Engine/Core/Log.h" - -Color32 ScreenUtilities::GetPixelAt(int32 x, int32 y) -{ - // Called from C# for live updates to the color. - - return { 0, 0, 0, 255 }; -} - -Int2 ScreenUtilities::GetScreenCursorPosition() -{ - // Called from C# for live updates to the color. - - return { 0, 0 }; -} - -class ScreenUtilitiesMac -{ -public: - static void BlockAndReadMouse(); -}; - -void ScreenUtilitiesMac::BlockAndReadMouse() -{ - // Maybe you don't need this if you go with NSColorSampler -} - -Delegate ScreenUtilities::PickColorDone; - -void ScreenUtilities::PickColor() -{ - // This is what C# calls to start the color picking sequence - // This should stop mouse clicks from working for one click, and that click is on the selected color - // There is a class called NSColorSample that might implement that for you, but maybe not. - // It also might just work to copy the Linux Impl since Mac uses X as well, right? -} - -#endif diff --git a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesWindows.cpp b/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesWindows.cpp deleted file mode 100644 index 5af695c4d..000000000 --- a/Source/Editor/Utilities/ScreenUtilities/ScreenUtilitiesWindows.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#if PLATFORM_WINDOWS - -#include "ScreenUtilities.h" -#include "Engine/Core/Math/Color32.h" -#include "Engine/Core/Math/Vector2.h" -#include "Engine/Core/Delegate.h" -#include "Engine/Core/Log.h" -#include "Engine/Scripting/ManagedCLR/MCore.h" - -#include - - -#pragma comment(lib, "Gdi32.lib") - - -Color32 ScreenUtilities::GetPixelAt(int32 x, int32 y) -{ - HDC deviceContext = GetDC(NULL); - COLORREF color = GetPixel(deviceContext, x, y); - ReleaseDC(NULL, deviceContext); - - Color32 returnColor = { GetRValue(color), GetGValue(color), GetBValue(color), 255 }; - return returnColor; -} - -Int2 ScreenUtilities::GetScreenCursorPosition() -{ - POINT cursorPos; - GetCursorPos(&cursorPos); - - Int2 returnCursorPos = { cursorPos.x, cursorPos.y }; - return returnCursorPos; -} - -class ScreenUtilitiesWindows -{ -public: - static void PickSelected(); - static void BlockAndReadMouse(); -}; - -void ScreenUtilitiesWindows::PickSelected() { - Int2 cursorPos = ScreenUtilities::GetScreenCursorPosition(); - Color32 colorPicked = ScreenUtilities::GetPixelAt(cursorPos.X, cursorPos.Y); - - // Push event with the picked color. - ScreenUtilities::PickColorDone(colorPicked); -} - -static HHOOK _mouseCallbackHook; -LRESULT CALLBACK ScreenUtilsMouseCallback( - _In_ int nCode, - _In_ WPARAM wParam, - _In_ LPARAM lParam -) -{ - if (wParam != WM_LBUTTONDOWN) { // Return as early as possible. - return CallNextHookEx(NULL, nCode, wParam, lParam); - } - - if (nCode < 0) { - return CallNextHookEx(NULL, nCode, wParam, lParam); - } - - - if (nCode >= 0 && wParam == WM_LBUTTONDOWN) { // Now try to run our code. - UnhookWindowsHookEx(_mouseCallbackHook); - - ScreenUtilitiesWindows::PickSelected(); - return 1; - } - - return CallNextHookEx(NULL, nCode, wParam, lParam); -} - -void ScreenUtilitiesWindows::BlockAndReadMouse() -{ - _mouseCallbackHook = SetWindowsHookEx(WH_MOUSE_LL, ScreenUtilsMouseCallback, NULL, NULL); - if (_mouseCallbackHook == NULL) - { - LOG(Warning, "Failed to set mouse hook."); - LOG(Warning, "Error: {0}", GetLastError()); - } -} - -Delegate ScreenUtilities::PickColorDone; - -void ScreenUtilities::PickColor() -{ - ScreenUtilitiesWindows::BlockAndReadMouse(); -} - - -#endif diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index acc0cf605..5f836e069 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -1037,9 +1037,7 @@ namespace FlaxEditor.Utilities /// The processed name path. public static string GetAssetNamePath(string path) { - var projectFolder = Globals.ProjectFolder; - if (path.StartsWith(projectFolder)) - path = path.Substring(projectFolder.Length + 1); + path = GetAssetNamePathWithExt(path); return StringUtils.GetPathWithoutExtension(path); } diff --git a/Source/Engine/Platform/Linux/LinuxPlatform.cpp b/Source/Engine/Platform/Linux/LinuxPlatform.cpp index e954dd784..92a26032d 100644 --- a/Source/Engine/Platform/Linux/LinuxPlatform.cpp +++ b/Source/Engine/Platform/Linux/LinuxPlatform.cpp @@ -5,7 +5,6 @@ #include "LinuxPlatform.h" #include "LinuxWindow.h" #include "LinuxInput.h" -#include "IncludeX11.h" #include "Engine/Core/Log.h" #include "Engine/Core/Types/Guid.h" #include "Engine/Core/Types/String.h" @@ -31,6 +30,7 @@ #include "Engine/Input/Input.h" #include "Engine/Input/Mouse.h" #include "Engine/Input/Keyboard.h" +#include "IncludeX11.h" #include #include #include @@ -88,6 +88,7 @@ X11::Cursor Cursors[(int32)CursorType::MAX]; X11::XcursorImage* CursorsImg[(int32)CursorType::MAX]; Dictionary KeyNameMap; Array KeyCodeMap; +Delegate LinuxPlatform::xEventRecieved; // Message boxes configuration #define LINUX_DIALOG_MIN_BUTTON_WIDTH 64 @@ -2217,8 +2218,6 @@ void LinuxPlatform::BeforeRun() { } -Delegate LinuxPlatform::xEventRecieved; - void LinuxPlatform::Tick() { UnixPlatform::Tick(); @@ -2233,12 +2232,12 @@ void LinuxPlatform::Tick() { X11::XEvent event; X11::XNextEvent(xDisplay, &event); - if (X11::XFilterEvent(&event, 0)) continue; - - xEventRecieved(&event); // Fire this event, since we recieved an event. - + + // External event handling + xEventRecieved(&event); + LinuxWindow* window; switch (event.type) { diff --git a/Source/Engine/Platform/Linux/LinuxPlatform.h b/Source/Engine/Platform/Linux/LinuxPlatform.h index 486674652..e995ab42d 100644 --- a/Source/Engine/Platform/Linux/LinuxPlatform.h +++ b/Source/Engine/Platform/Linux/LinuxPlatform.h @@ -34,7 +34,7 @@ public: static const String& GetHomeDirectory(); /// - /// An event that is fired when an XEvent is recieved by Flax. + /// An event that is fired when an XEvent is received during platform tick. /// static Delegate xEventRecieved; diff --git a/Source/Engine/UI/GUI/Common/IconButton.cs b/Source/Engine/UI/GUI/Common/IconButton.cs deleted file mode 100644 index e2652cd9f..000000000 --- a/Source/Engine/UI/GUI/Common/IconButton.cs +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. - -using System; - -namespace FlaxEngine.GUI -{ - /// - /// Button with an icon. - /// - public class IconButton : Button - { - /// - /// The sprite rendered on the button. - /// - public SpriteHandle ButtonSprite { get; set; } - - /// - /// Whether or not to hide the border of the button. - /// - public bool HideBorder = true; - - /// - /// Initializes a new instance of the class. - /// - /// The sprite used by the button. - public IconButton(SpriteHandle buttonSprite) - : this(0, 0, buttonSprite) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Position X coordinate - /// Position Y coordinate - /// The sprite used by the button. - /// Width - /// Height - /// Whether or not to hide the border. - public IconButton(float x, float y, SpriteHandle buttonSprite, float width = 120, float height = DefaultHeight, bool hideBorder = true) - : base(x, y, width, height) - { - ButtonSprite = buttonSprite; - BackgroundBrush = new SpriteBrush(ButtonSprite); - HideBorder = hideBorder; - } - - /// - /// Initializes a new instance of the class. - /// - /// Position - /// Size - /// The sprite used by the button. - public IconButton(Float2 location, Float2 size, SpriteHandle buttonSprite) - : this(location.X, location.Y, buttonSprite, size.X, size.Y) - { - } - - /// - /// Sets the colors of the button, taking into account the field.> - /// - /// The color to use. - public override void SetColors(Color color) - { - BackgroundColor = color; - BackgroundColorSelected = color.RGBMultiplied(0.8f); - BackgroundColorHighlighted = color.RGBMultiplied(1.2f); - - BorderColor = HideBorder ? Color.Transparent : color.RGBMultiplied(0.5f); - BorderColorSelected = BorderColor; - BorderColorHighlighted = BorderColor; - } - } -} diff --git a/Source/Engine/Utilities/Screenshot.cpp b/Source/Engine/Utilities/Screenshot.cpp index fbde8043b..7e1d3d902 100644 --- a/Source/Engine/Utilities/Screenshot.cpp +++ b/Source/Engine/Utilities/Screenshot.cpp @@ -3,7 +3,6 @@ #include "Screenshot.h" #include "Engine/Core/Log.h" #include "Engine/Core/Math/Math.h" -#include "Engine/Core/Math/Color32.h" #include "Engine/Graphics/RenderTask.h" #include "Engine/Platform/FileSystem.h" #include "Engine/Graphics/Textures/TextureData.h" @@ -11,7 +10,6 @@ #include "Engine/Graphics/GPUResourceProperty.h" #include "Engine/Graphics/GPUDevice.h" #include "Engine/Graphics/GPUSwapChain.h" -#include "Engine/Engine/Engine.h" #include "Engine/Threading/ThreadPoolTask.h" #include "Engine/Engine/Globals.h" #if COMPILE_WITH_TEXTURE_TOOL From 311637616b4825a2dd1c20c14677da314d6fbbd1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 28 May 2023 16:22:34 +0200 Subject: [PATCH 110/116] Fix Android Debug compilation error after recent Tracy update #948 --- Source/ThirdParty/tracy/client/TracyProfiler.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Source/ThirdParty/tracy/client/TracyProfiler.cpp b/Source/ThirdParty/tracy/client/TracyProfiler.cpp index 46a9d36e4..dfbb22a83 100644 --- a/Source/ThirdParty/tracy/client/TracyProfiler.cpp +++ b/Source/ThirdParty/tracy/client/TracyProfiler.cpp @@ -368,7 +368,6 @@ static int64_t SetupHwTimer() InitFailure( "CPU doesn't support invariant TSC.\nDefine TRACY_NO_INVARIANT_CHECK=1 to ignore this error, *if you know what you are doing*.\nAlternatively you may rebuild the application with the TRACY_TIMER_FALLBACK define to use lower resolution timer." ); #endif } -#endif #endif return Profiler::GetTime(); @@ -4784,4 +4783,4 @@ TRACY_API void ___tracy_shutdown_profiler( void ) #endif #endif - +#endif From 587578ccfbd3e946d22564265f39dd54ae9bf039 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 28 May 2023 16:45:08 +0200 Subject: [PATCH 111/116] Update platforms impl --- Source/Engine/Platform/Apple/ApplePlatform.cpp | 9 --------- Source/Engine/Platform/Apple/ApplePlatform.h | 2 -- Source/Engine/Platform/Base/PlatformBase.cpp | 9 +++++++++ Source/Engine/Platform/Base/PlatformBase.h | 4 ++-- Source/Engine/Platform/UWP/UWPPlatform.cpp | 10 ---------- Source/Engine/Platform/UWP/UWPPlatform.h | 2 -- 6 files changed, 11 insertions(+), 25 deletions(-) diff --git a/Source/Engine/Platform/Apple/ApplePlatform.cpp b/Source/Engine/Platform/Apple/ApplePlatform.cpp index a7ceaec26..4a34ad269 100644 --- a/Source/Engine/Platform/Apple/ApplePlatform.cpp +++ b/Source/Engine/Platform/Apple/ApplePlatform.cpp @@ -315,15 +315,6 @@ void ApplePlatform::CreateGuid(Guid& result) result.D = ptr[3]; } -bool ApplePlatform::CanOpenUrl(const StringView& url) -{ - return false; -} - -void ApplePlatform::OpenUrl(const StringView& url) -{ -} - String ApplePlatform::GetExecutableFilePath() { char buf[PATH_MAX]; diff --git a/Source/Engine/Platform/Apple/ApplePlatform.h b/Source/Engine/Platform/Apple/ApplePlatform.h index fdc0f45c7..27688ba8e 100644 --- a/Source/Engine/Platform/Apple/ApplePlatform.h +++ b/Source/Engine/Platform/Apple/ApplePlatform.h @@ -87,8 +87,6 @@ public: static String GetUserLocaleName(); static bool GetHasFocus(); static void CreateGuid(Guid& result); - static bool CanOpenUrl(const StringView& url); - static void OpenUrl(const StringView& url); static Float2 GetDesktopSize(); static String GetMainDirectory(); static String GetExecutableFilePath(); diff --git a/Source/Engine/Platform/Base/PlatformBase.cpp b/Source/Engine/Platform/Base/PlatformBase.cpp index d9c8b2826..d15bec61c 100644 --- a/Source/Engine/Platform/Base/PlatformBase.cpp +++ b/Source/Engine/Platform/Base/PlatformBase.cpp @@ -521,6 +521,15 @@ void PlatformBase::CreateGuid(Guid& result) result = Guid(dateThingHigh, randomThing | (sequentialThing << 16), cyclesThing, dateThingLow); } +bool PlatformBase::CanOpenUrl(const StringView& url) +{ + return false; +} + +void PlatformBase::OpenUrl(const StringView& url) +{ +} + Float2 PlatformBase::GetMousePosition() { const Window* win = Engine::MainWindow; diff --git a/Source/Engine/Platform/Base/PlatformBase.h b/Source/Engine/Platform/Base/PlatformBase.h index 737427ca7..2780d3fa4 100644 --- a/Source/Engine/Platform/Base/PlatformBase.h +++ b/Source/Engine/Platform/Base/PlatformBase.h @@ -642,13 +642,13 @@ public: /// /// The URI to assign to web browser. /// True if can open URL, otherwise false. - API_FUNCTION() static bool CanOpenUrl(const StringView& url) = delete; + API_FUNCTION() static bool CanOpenUrl(const StringView& url); /// /// Launches a web browser and opens a given URL. /// /// The URI to assign to web browser. - API_FUNCTION() static void OpenUrl(const StringView& url) = delete; + API_FUNCTION() static void OpenUrl(const StringView& url); public: /// diff --git a/Source/Engine/Platform/UWP/UWPPlatform.cpp b/Source/Engine/Platform/UWP/UWPPlatform.cpp index eb496a4a1..58240ca89 100644 --- a/Source/Engine/Platform/UWP/UWPPlatform.cpp +++ b/Source/Engine/Platform/UWP/UWPPlatform.cpp @@ -148,16 +148,6 @@ bool UWPPlatform::GetHasFocus() return true; } -bool UWPPlatform::CanOpenUrl(const StringView& url) -{ - return false; -} - -void UWPPlatform::OpenUrl(const StringView& url) -{ - // TODO: add support for OpenUrl on UWP -} - Float2 UWPPlatform::GetDesktopSize() { Float2 result; diff --git a/Source/Engine/Platform/UWP/UWPPlatform.h b/Source/Engine/Platform/UWP/UWPPlatform.h index 655209e9f..f2f629d02 100644 --- a/Source/Engine/Platform/UWP/UWPPlatform.h +++ b/Source/Engine/Platform/UWP/UWPPlatform.h @@ -35,8 +35,6 @@ public: static String GetUserLocaleName(); static String GetComputerName(); static bool GetHasFocus(); - static bool CanOpenUrl(const StringView& url); - static void OpenUrl(const StringView& url); static Float2 GetDesktopSize(); static Window* CreateWindow(const CreateWindowSettings& settings); static void* LoadLibrary(const Char* filename); From f3393c46a56b74f8b5e5390dcd7cd982f001cbc3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 28 May 2023 16:55:47 +0200 Subject: [PATCH 112/116] Fix text formatting infinite loop #1127 --- Source/Engine/Render2D/Font.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Render2D/Font.cpp b/Source/Engine/Render2D/Font.cpp index c36f40515..f12a9ce1c 100644 --- a/Source/Engine/Render2D/Font.cpp +++ b/Source/Engine/Render2D/Font.cpp @@ -178,7 +178,8 @@ void Font::ProcessText(const StringView& text, Array& outputLines if (lastWrapCharIndex != INVALID_INDEX) { // Skip moving twice for the same character - if (outputLines.HasItems() && outputLines.Last().LastCharIndex == lastWrapCharIndex - 1) + int32 lastLineLasCharIndex = outputLines.HasItems() ? outputLines.Last().LastCharIndex : -10000; + if (lastLineLasCharIndex == lastWrapCharIndex || lastLineLasCharIndex == lastWrapCharIndex - 1 || lastLineLasCharIndex == lastWrapCharIndex - 2) { currentIndex = nextCharIndex; lastMoveLine = moveLine; From 5a45ccf70dba13b0c56739220697b3597481ac87 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 28 May 2023 16:55:47 +0200 Subject: [PATCH 113/116] Fix text formatting infinite loop #1127 --- Source/Engine/Render2D/Font.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Render2D/Font.cpp b/Source/Engine/Render2D/Font.cpp index c36f40515..f12a9ce1c 100644 --- a/Source/Engine/Render2D/Font.cpp +++ b/Source/Engine/Render2D/Font.cpp @@ -178,7 +178,8 @@ void Font::ProcessText(const StringView& text, Array& outputLines if (lastWrapCharIndex != INVALID_INDEX) { // Skip moving twice for the same character - if (outputLines.HasItems() && outputLines.Last().LastCharIndex == lastWrapCharIndex - 1) + int32 lastLineLasCharIndex = outputLines.HasItems() ? outputLines.Last().LastCharIndex : -10000; + if (lastLineLasCharIndex == lastWrapCharIndex || lastLineLasCharIndex == lastWrapCharIndex - 1 || lastLineLasCharIndex == lastWrapCharIndex - 2) { currentIndex = nextCharIndex; lastMoveLine = moveLine; From b65c84c02e4d884b10d3accbb88b8e67c20d44a1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 28 May 2023 16:57:38 +0200 Subject: [PATCH 114/116] Fix compilation issue from 62946f63598ae501bd3c217dc7b602a280a44d35 --- Source/Editor/Utilities/ScreenUtilities.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Utilities/ScreenUtilities.cpp b/Source/Editor/Utilities/ScreenUtilities.cpp index cff41f7bf..44f52350e 100644 --- a/Source/Editor/Utilities/ScreenUtilities.cpp +++ b/Source/Editor/Utilities/ScreenUtilities.cpp @@ -63,7 +63,7 @@ Color32 ScreenUtilities::GetColorAt(const Float2& pos) int defaultScreen = X11::XDefaultScreen(display); X11::XImage* image; - image = X11::XGetImage(display, X11::XRootWindow(display, defaultScreen), x, y, 1, 1, AllPlanes, XYPixmap); + image = X11::XGetImage(display, X11::XRootWindow(display, defaultScreen), (int)pos.X, (int)pos.Y, 1, 1, AllPlanes, XYPixmap); color.pixel = XGetPixel(image, 0, 0); X11::XFree(image); From 6b4340ce241a4c50107bc2e12a5da3fb6f184bd0 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 28 May 2023 19:42:52 +0200 Subject: [PATCH 115/116] Add build tool and game cooker caches clearing when opening project with different Editor version --- Source/Editor/Editor.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Source/Editor/Editor.cpp b/Source/Editor/Editor.cpp index fb6d53c4c..62ef85f2c 100644 --- a/Source/Editor/Editor.cpp +++ b/Source/Editor/Editor.cpp @@ -259,13 +259,13 @@ bool Editor::CheckProjectUpgrade() LOG(Warning, "Project layout upgraded!"); } - // Check if last version was the same + // Check if last version was the same else if (lastMajor == FLAXENGINE_VERSION_MAJOR && lastMinor == FLAXENGINE_VERSION_MINOR) { // Do nothing IsOldProjectOpened = false; } - // Check if last version was older + // Check if last version was older else if (lastMajor < FLAXENGINE_VERSION_MAJOR || (lastMajor == FLAXENGINE_VERSION_MAJOR && lastMinor < FLAXENGINE_VERSION_MINOR)) { LOG(Warning, "The project was opened with the older editor version last time"); @@ -288,7 +288,7 @@ bool Editor::CheckProjectUpgrade() return true; } } - // Check if last version was newer + // Check if last version was newer else if (lastMajor > FLAXENGINE_VERSION_MAJOR || (lastMajor == FLAXENGINE_VERSION_MAJOR && lastMinor > FLAXENGINE_VERSION_MINOR)) { LOG(Warning, "The project was opened with the newer editor version last time"); @@ -312,6 +312,14 @@ bool Editor::CheckProjectUpgrade() } } + // When changing between major/minor version clear some caches to prevent possible issues + if (lastMajor != FLAXENGINE_VERSION_MAJOR || lastMinor != FLAXENGINE_VERSION_MINOR) + { + LOG(Info, "Cleaning cache files from different engine version"); + FileSystem::DeleteDirectory(Globals::ProjectFolder / TEXT("Cache/Cooker")); + FileSystem::DeleteDirectory(Globals::ProjectFolder / TEXT("Cache/Intermediate")); + } + // Upgrade old 0.7 projects // [Deprecated: 01.11.2020, expires 01.11.2021] if (lastMajor == 0 && lastMinor == 7 && lastBuild <= 6197) @@ -330,12 +338,11 @@ bool Editor::CheckProjectUpgrade() file->WriteInt32(FLAXENGINE_VERSION_MAJOR); file->WriteInt32(FLAXENGINE_VERSION_MINOR); file->WriteInt32(FLAXENGINE_VERSION_BUILD); - Delete(file); } else { - LOG(Warning, "Failed to create version cache file"); + LOG(Error, "Failed to create version cache file"); } } From f2ecefb7ee9b9e6c5daac9f44fe40ebdccbb1c76 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 28 May 2023 21:16:29 +0200 Subject: [PATCH 116/116] Refactor `StringUtils` --- .../Engine/Platform/Base/StringUtilsBase.cpp | 127 +++--------------- Source/Engine/Platform/StringUtils.h | 102 +------------- 2 files changed, 21 insertions(+), 208 deletions(-) diff --git a/Source/Engine/Platform/Base/StringUtilsBase.cpp b/Source/Engine/Platform/Base/StringUtilsBase.cpp index c18d309a5..ef4e66b72 100644 --- a/Source/Engine/Platform/Base/StringUtilsBase.cpp +++ b/Source/Engine/Platform/Base/StringUtilsBase.cpp @@ -11,9 +11,9 @@ #include #endif -const char DirectorySeparatorChar = '\\'; -const char AltDirectorySeparatorChar = '/'; -const char VolumeSeparatorChar = ':'; +constexpr char DirectorySeparatorChar = '\\'; +constexpr char AltDirectorySeparatorChar = '/'; +constexpr char VolumeSeparatorChar = ':'; const Char* StringUtils::FindIgnoreCase(const Char* str, const Char* toFind) { @@ -378,20 +378,17 @@ void StringUtils::PathRemoveRelativeParts(String& path) path.Insert(0, TEXT("/")); } -const char DigitPairs[201] = { - "00010203040506070809" - "10111213141516171819" - "20212223242526272829" - "30313233343536373839" - "40414243444546474849" - "50515253545556575859" - "60616263646566676869" - "70717273747576777879" - "80818283848586878889" - "90919293949596979899" -}; - -#define STRING_UTILS_ITOSTR_BUFFER_SIZE 15 +int32 StringUtils::HexDigit(Char c) +{ + int32 result = 0; + if (c >= '0' && c <= '9') + result = c - '0'; + else if (c >= 'a' && c <= 'f') + result = c + 10 - 'a'; + else if (c >= 'A' && c <= 'F') + result = c + 10 - 'A'; + return result; +} bool StringUtils::Parse(const Char* str, float* result) { @@ -419,108 +416,22 @@ bool StringUtils::Parse(const char* str, float* result) String StringUtils::ToString(int32 value) { - char buf[STRING_UTILS_ITOSTR_BUFFER_SIZE]; - char* it = &buf[STRING_UTILS_ITOSTR_BUFFER_SIZE - 2]; - int32 div = value / 100; - if (value >= 0) - { - while (div) - { - Platform::MemoryCopy(it, &DigitPairs[2 * (value - div * 100)], 2); - value = div; - it -= 2; - div = value / 100; - } - Platform::MemoryCopy(it, &DigitPairs[2 * value], 2); - if (value < 10) - it++; - } - else - { - while (div) - { - Platform::MemoryCopy(it, &DigitPairs[-2 * (value - div * 100)], 2); - value = div; - it -= 2; - div = value / 100; - } - Platform::MemoryCopy(it, &DigitPairs[-2 * value], 2); - if (value <= -10) - it--; - *it = '-'; - } - return String(it, (int32)(&buf[STRING_UTILS_ITOSTR_BUFFER_SIZE] - it)); + return String::Format(TEXT("{}"), value); } String StringUtils::ToString(int64 value) { - char buf[STRING_UTILS_ITOSTR_BUFFER_SIZE]; - char* it = &buf[STRING_UTILS_ITOSTR_BUFFER_SIZE - 2]; - int64 div = value / 100; - if (value >= 0) - { - while (div) - { - Platform::MemoryCopy(it, &DigitPairs[2 * (value - div * 100)], 2); - value = div; - it -= 2; - div = value / 100; - } - Platform::MemoryCopy(it, &DigitPairs[2 * value], 2); - if (value < 10) - it++; - } - else - { - while (div) - { - Platform::MemoryCopy(it, &DigitPairs[-2 * (value - div * 100)], 2); - value = div; - it -= 2; - div = value / 100; - } - Platform::MemoryCopy(it, &DigitPairs[-2 * value], 2); - if (value <= -10) - it--; - *it = '-'; - } - return String(it, (int32)(&buf[STRING_UTILS_ITOSTR_BUFFER_SIZE] - it)); + return String::Format(TEXT("{}"), value); } String StringUtils::ToString(uint32 value) { - char buf[STRING_UTILS_ITOSTR_BUFFER_SIZE]; - char* it = &buf[STRING_UTILS_ITOSTR_BUFFER_SIZE - 2]; - int32 div = value / 100; - while (div) - { - Platform::MemoryCopy(it, &DigitPairs[2 * (value - div * 100)], 2); - value = div; - it -= 2; - div = value / 100; - } - Platform::MemoryCopy(it, &DigitPairs[2 * value], 2); - if (value < 10) - it++; - return String((char*)it, (int32)((char*)&buf[STRING_UTILS_ITOSTR_BUFFER_SIZE] - (char*)it)); + return String::Format(TEXT("{}"), value); } String StringUtils::ToString(uint64 value) { - char buf[STRING_UTILS_ITOSTR_BUFFER_SIZE]; - char* it = &buf[STRING_UTILS_ITOSTR_BUFFER_SIZE - 2]; - int64 div = value / 100; - while (div) - { - Platform::MemoryCopy(it, &DigitPairs[2 * (value - div * 100)], 2); - value = div; - it -= 2; - div = value / 100; - } - Platform::MemoryCopy(it, &DigitPairs[2 * value], 2); - if (value < 10) - it++; - return String((char*)it, (int32)((char*)&buf[STRING_UTILS_ITOSTR_BUFFER_SIZE] - (char*)it)); + return String::Format(TEXT("{}"), value); } String StringUtils::ToString(float value) @@ -544,5 +455,3 @@ String StringUtils::GetZZString(const Char* str) } return String(str, (int32)(end - str)); } - -#undef STRING_UTILS_ITOSTR_BUFFER_SIZE diff --git a/Source/Engine/Platform/StringUtils.h b/Source/Engine/Platform/StringUtils.h index ea982c2ee..04e644d2e 100644 --- a/Source/Engine/Platform/StringUtils.h +++ b/Source/Engine/Platform/StringUtils.h @@ -21,12 +21,11 @@ enum class StringSearchCase }; /// -/// The string operations utilities collection. +/// The string operations utilities. /// class FLAXENGINE_API StringUtils { public: - /// /// Calculates the hash code for input string. /// @@ -65,7 +64,6 @@ public: } public: - // Returns true if character is uppercase static bool IsUpper(char c); @@ -92,7 +90,6 @@ public: static char ToLower(char c); public: - // Returns true if character is uppercase static bool IsUpper(Char c); @@ -119,7 +116,6 @@ public: static Char ToLower(Char c); public: - // Compare two strings with case sensitive. Strings must not be null. static int32 Compare(const Char* str1, const Char* str2); @@ -145,7 +141,6 @@ public: static int32 CompareIgnoreCase(const char* str1, const char* str2, int32 maxCount); public: - // Get string length. Returns 0 if str is null. static int32 Length(const Char* str); @@ -183,7 +178,6 @@ public: static const char* FindIgnoreCase(const char* str, const char* toFind); public: - // Converts characters from ANSI to UTF-16 static void ConvertANSI2UTF16(const char* from, Char* to, int32 len); @@ -203,7 +197,6 @@ public: static char* ConvertUTF162UTF8(const Char* from, int32 fromLength, int32& toLength); public: - // Returns the directory name of the specified path string // @param path The path string from which to obtain the directory name // @returns Directory name @@ -224,95 +217,8 @@ public: static void PathRemoveRelativeParts(String& path); public: - - /// - /// Convert integer value to string - /// - /// Value to convert - /// Base (8,10,16) - /// Input buffer - /// Result string length - template - static void itoa(int32 value, int32 base, CharType* buffer, int32& length) - { - // Allocate buffer - bool isNegative = false; - CharType* pos = buffer; - CharType* pos1 = buffer; - length = 0; - - // Validate input base - if (base < 8 || base > 16) - { - *pos = '\0'; - return; - } - - // Special case for zero - if (value == 0) - { - length++; - *pos++ = '0'; - *pos = '\0'; - return; - } - - // Check if value is negative - if (value < 0) - { - isNegative = true; - value = -value; - } - - // Convert. If base is power of two (2,4,8,16..) - // we could use binary and operation and shift offset instead of division - while (value) - { - length++; - int32 reminder = value % base; - *pos++ = reminder + (reminder > 9 ? 'a' - 10 : '0'); - value /= base; - } - - // Apply negative sign - if (isNegative) - *pos++ = '-'; - - // Add null terminator char - *pos-- = 0; - - // Reverse the buffer - while (pos1 < pos) - { - CharType c = *pos; - *pos-- = *pos1; - *pos1++ = c; - } - } - - static int32 HexDigit(Char c) - { - int32 result = 0; - - if (c >= '0' && c <= '9') - { - result = c - '0'; - } - else if (c >= 'a' && c <= 'f') - { - result = c + 10 - 'a'; - } - else if (c >= 'A' && c <= 'F') - { - result = c + 10 - 'A'; - } - else - { - result = 0; - } - - return result; - } + // Converts hexadecimal character into the value. + static int32 HexDigit(Char c); // Parse text to unsigned integer value // @param str String to parse @@ -431,7 +337,6 @@ public: static bool Parse(const char* str, float* result); public: - static String ToString(int32 value); static String ToString(int64 value); static String ToString(uint32 value); @@ -440,7 +345,6 @@ public: static String ToString(double value); public: - // Returns the String to double null-terminated string // @param str Double null-terminated string // @return Double null-terminated String