From 692a61c94826957073465078b54ec04e7dcb9759 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 17 Apr 2024 13:01:58 +0200 Subject: [PATCH 01/43] Add `Time.Synchronize` to stabilize engine loop --- Source/Editor/States/PlayingState.cs | 4 ++++ Source/Engine/Engine/Engine.cpp | 4 ++-- Source/Engine/Engine/Time.cpp | 10 +++++----- Source/Engine/Engine/Time.h | 8 +++++--- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/Source/Editor/States/PlayingState.cs b/Source/Editor/States/PlayingState.cs index 1be2a1816..b2654f3d9 100644 --- a/Source/Editor/States/PlayingState.cs +++ b/Source/Editor/States/PlayingState.cs @@ -163,6 +163,8 @@ namespace FlaxEditor.States Editor.OnPlayBegin(); IsPlayModeStarting = false; Profiler.EndEvent(); + + Time.Synchronize(); } private void SetupEditorEnvOptions() @@ -209,6 +211,8 @@ namespace FlaxEditor.States Editor.OnPlayEnd(); IsPlayModeEnding = false; Profiler.EndEvent(); + + Time.Synchronize(); } } } diff --git a/Source/Engine/Engine/Engine.cpp b/Source/Engine/Engine/Engine.cpp index 27b35a0b7..abebbdb91 100644 --- a/Source/Engine/Engine/Engine.cpp +++ b/Source/Engine/Engine/Engine.cpp @@ -156,7 +156,7 @@ int32 Engine::Main(const Char* cmdLine) #endif Log::Logger::WriteFloor(); LOG_FLUSH(); - Time::OnBeforeRun(); + Time::Synchronize(); EngineImpl::IsReady = true; // Main engine loop @@ -465,7 +465,7 @@ void Engine::OnUnpause() LOG(Info, "App unpaused"); Unpause(); - Time::OnBeforeRun(); + Time::Synchronize(); } void Engine::OnExit() diff --git a/Source/Engine/Engine/Time.cpp b/Source/Engine/Engine/Time.cpp index 6df5275f5..7f4571b32 100644 --- a/Source/Engine/Engine/Time.cpp +++ b/Source/Engine/Engine/Time.cpp @@ -54,7 +54,7 @@ void TimeSettings::Apply() ::MaxUpdateDeltaTime = MaxUpdateDeltaTime; } -void Time::TickData::OnBeforeRun(float targetFps, double currentTime) +void Time::TickData::Synchronize(float targetFps, double currentTime) { Time = UnscaledTime = TimeSpan::Zero(); DeltaTime = UnscaledDeltaTime = targetFps > ZeroTolerance ? TimeSpan::FromSeconds(1.0f / targetFps) : TimeSpan::Zero(); @@ -240,13 +240,13 @@ void Time::SetFixedDeltaTime(bool enable, float value) FixedDeltaTimeValue = value; } -void Time::OnBeforeRun() +void Time::Synchronize() { // Initialize tick data (based on a time settings) const double time = Platform::GetTimeSeconds(); - Update.OnBeforeRun(UpdateFPS, time); - Physics.OnBeforeRun(PhysicsFPS, time); - Draw.OnBeforeRun(DrawFPS, time); + Update.Synchronize(UpdateFPS, time); + Physics.Synchronize(PhysicsFPS, time); + Draw.Synchronize(DrawFPS, time); } bool Time::OnBeginUpdate() diff --git a/Source/Engine/Engine/Time.h b/Source/Engine/Engine/Time.h index 06e0d1cc5..4bb269cb9 100644 --- a/Source/Engine/Engine/Time.h +++ b/Source/Engine/Engine/Time.h @@ -225,12 +225,14 @@ public: /// The fixed draw/update rate for the time. API_FUNCTION() static void SetFixedDeltaTime(bool enable, float value); + /// + /// Synchronizes update, fixed update and draw. Resets any pending deltas for fresh ticking in sync. + /// + API_FUNCTION() static void Synchronize(); + private: - // Methods used by the Engine class - static void OnBeforeRun(); - static bool OnBeginUpdate(); static bool OnBeginPhysics(); static bool OnBeginDraw(); From e53ab10145dc69fbcbe5d94e7ecb5a0a9f395b26 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 17 Apr 2024 13:31:12 +0200 Subject: [PATCH 02/43] Add `Engine::UpdateCount` to sync gameplay logic updates with game loop rather than draw frames --- Source/Engine/Engine/Engine.cpp | 3 +++ Source/Engine/Engine/Engine.h | 7 ++++++- Source/Engine/Level/Actors/AnimatedModel.cpp | 4 ++-- Source/Engine/Particles/ParticleEffect.cpp | 6 +++--- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/Source/Engine/Engine/Engine.cpp b/Source/Engine/Engine/Engine.cpp index abebbdb91..c9d4bc5ae 100644 --- a/Source/Engine/Engine/Engine.cpp +++ b/Source/Engine/Engine/Engine.cpp @@ -60,6 +60,7 @@ namespace EngineImpl DateTime Engine::StartupTime; bool Engine::HasFocus = false; +uint64 Engine::UpdateCount = 0; uint64 Engine::FrameCount = 0; Action Engine::FixedUpdate; Action Engine::Update; @@ -296,6 +297,8 @@ void Engine::OnUpdate() { PROFILE_CPU_NAMED("Update"); + UpdateCount++; + // Update application (will gather data and other platform related events) { PROFILE_CPU_NAMED("Platform.Tick"); diff --git a/Source/Engine/Engine/Engine.h b/Source/Engine/Engine/Engine.h index f74fcab4b..15285a07e 100644 --- a/Source/Engine/Engine/Engine.h +++ b/Source/Engine/Engine/Engine.h @@ -28,7 +28,12 @@ public: API_FIELD(ReadOnly) static bool HasFocus; /// - /// Gets the current frame count since the start of the game. + /// Gets the current update counter since the start of the game. + /// + API_FIELD(ReadOnly) static uint64 UpdateCount; + + /// + /// Gets the current frame (drawing) count since the start of the game. /// API_FIELD(ReadOnly) static uint64 FrameCount; diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index 6f8137bc6..fbc434099 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -55,10 +55,10 @@ void AnimatedModel::UpdateAnimation() || !IsActiveInHierarchy() || SkinnedModel == nullptr || !SkinnedModel->IsLoaded() - || _lastUpdateFrame == Engine::FrameCount + || _lastUpdateFrame == Engine::UpdateCount || _masterPose) return; - _lastUpdateFrame = Engine::FrameCount; + _lastUpdateFrame = Engine::UpdateCount; if (AnimationGraph && AnimationGraph->IsLoaded() && AnimationGraph->Graph.IsReady()) { diff --git a/Source/Engine/Particles/ParticleEffect.cpp b/Source/Engine/Particles/ParticleEffect.cpp index a5fd4cd02..6c584d513 100644 --- a/Source/Engine/Particles/ParticleEffect.cpp +++ b/Source/Engine/Particles/ParticleEffect.cpp @@ -270,11 +270,11 @@ void ParticleEffect::UpdateSimulation(bool singleFrame) if (!IsActiveInHierarchy() || ParticleSystem == nullptr || !ParticleSystem->IsLoaded() - || _lastUpdateFrame == Engine::FrameCount) + || _lastUpdateFrame == Engine::UpdateCount) return; // Request update - _lastUpdateFrame = Engine::FrameCount; + _lastUpdateFrame = Engine::UpdateCount; _lastMinDstSqr = MAX_Real; if (singleFrame) Instance.LastUpdateTime = (UseTimeScale ? Time::Update.Time : Time::Update.UnscaledTime).GetTotalSeconds(); @@ -371,7 +371,7 @@ void ParticleEffect::Sync() SceneRenderTask* ParticleEffect::GetRenderTask() const { - const uint64 minFrame = Engine::FrameCount - 2; + const uint64 minFrame = Engine::UpdateCount - 2; // Custom task const auto customViewRenderTask = CustomViewRenderTask.Get(); From 9f983cff49b5fe4748766a07d85f3cf67d917139 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 17 Apr 2024 13:38:06 +0200 Subject: [PATCH 03/43] Refactor engine loop to have better sync between game update, physics and drawing --- .../Engine/Animations/InverseKinematics.cpp | 13 +++++----- Source/Engine/Engine/Engine.cpp | 9 ++++--- Source/Engine/Engine/Time.cpp | 18 ++++++------- Source/Engine/Engine/Time.h | 25 ++++++------------- 4 files changed, 28 insertions(+), 37 deletions(-) diff --git a/Source/Engine/Animations/InverseKinematics.cpp b/Source/Engine/Animations/InverseKinematics.cpp index 06523be81..c5db48320 100644 --- a/Source/Engine/Animations/InverseKinematics.cpp +++ b/Source/Engine/Animations/InverseKinematics.cpp @@ -10,7 +10,6 @@ void InverseKinematics::SolveAimIK(const Transform& node, const Vector3& target, Quaternion::FindBetween(fromNode, toTarget, outNodeCorrection); } - void InverseKinematics::SolveTwoBoneIK(Transform& rootTransform, Transform& midJointTransform, Transform& endEffectorTransform, const Vector3& targetPosition, const Vector3& poleVector, bool allowStretching, float maxStretchScale) { // Calculate limb segment lengths @@ -82,17 +81,20 @@ void InverseKinematics::SolveTwoBoneIK(Transform& rootTransform, Transform& midJ Vector3 newEndEffectorPos = targetPosition; Vector3 newMidJointPos = midJointPos; - if (toTargetLength >= totalLimbLength) { + if (toTargetLength >= totalLimbLength) + { // Target is beyond the reach of the limb Vector3 rootToEnd = (targetPosition - rootTransform.Translation).GetNormalized(); // Calculate the slight offset towards the pole vector Vector3 rootToPole = (poleVector - rootTransform.Translation).GetNormalized(); Vector3 slightBendDirection = Vector3::Cross(rootToEnd, rootToPole); - if (slightBendDirection.LengthSquared() < ZeroTolerance * ZeroTolerance) { + if (slightBendDirection.LengthSquared() < ZeroTolerance * ZeroTolerance) + { slightBendDirection = Vector3::Up; } - else { + else + { slightBendDirection.Normalize(); } @@ -140,7 +142,6 @@ void InverseKinematics::SolveTwoBoneIK(Transform& rootTransform, Transform& midJ rootTransform.Orientation = newRootJointOrientation; } - // Update mid joint orientation to point Y-axis towards the end effector and Z-axis perpendicular to the IK plane { // Vector from mid joint to end effector (local Y-axis direction after rotation) @@ -150,7 +151,6 @@ void InverseKinematics::SolveTwoBoneIK(Transform& rootTransform, Transform& midJ Vector3 rootToMid = (newMidJointPos - rootTransform.Translation).GetNormalized(); Vector3 planeNormal = Vector3::Cross(rootToMid, midToEnd).GetNormalized(); - // Vector from mid joint to end effector (local Y-axis direction) Vector3 localY = (newEndEffectorPos - newMidJointPos).GetNormalized(); @@ -163,7 +163,6 @@ void InverseKinematics::SolveTwoBoneIK(Transform& rootTransform, Transform& midJ // Correct the local Z-axis direction based on the cross product of X and Y to ensure orthogonality localZ = Vector3::Cross(localX, localY).GetNormalized(); - // Construct a rotation from the orthogonal basis vectors // The axes are used differently here than a standard LookRotation to align Z towards the end and Y perpendicular Quaternion newMidJointOrientation = Quaternion::LookRotation(localZ, localY); // Assuming FromLookRotation creates a rotation with the first vector as forward and the second as up diff --git a/Source/Engine/Engine/Engine.cpp b/Source/Engine/Engine/Engine.cpp index c9d4bc5ae..0a500eaf4 100644 --- a/Source/Engine/Engine/Engine.cpp +++ b/Source/Engine/Engine/Engine.cpp @@ -192,8 +192,11 @@ int32 Engine::Main(const Char* cmdLine) OnUnpause(); } + // Use the same time for all ticks to improve synchronization + const double time = Platform::GetTimeSeconds(); + // Update game logic - if (Time::OnBeginUpdate()) + if (Time::OnBeginUpdate(time)) { OnUpdate(); OnLateUpdate(); @@ -201,7 +204,7 @@ int32 Engine::Main(const Char* cmdLine) } // Start physics simulation - if (Time::OnBeginPhysics()) + if (Time::OnBeginPhysics(time)) { OnFixedUpdate(); OnLateFixedUpdate(); @@ -209,7 +212,7 @@ int32 Engine::Main(const Char* cmdLine) } // Draw frame - if (Time::OnBeginDraw()) + if (Time::OnBeginDraw(time)) { OnDraw(); Time::OnEndDraw(); diff --git a/Source/Engine/Engine/Time.cpp b/Source/Engine/Engine/Time.cpp index 7f4571b32..43ac4f9b5 100644 --- a/Source/Engine/Engine/Time.cpp +++ b/Source/Engine/Engine/Time.cpp @@ -72,10 +72,9 @@ void Time::TickData::OnReset(float targetFps, double currentTime) LastEnd = currentTime; } -bool Time::TickData::OnTickBegin(float targetFps, float maxDeltaTime) +bool Time::TickData::OnTickBegin(double time, float targetFps, float maxDeltaTime) { // Check if can perform a tick - const double time = Platform::GetTimeSeconds(); double deltaTime; if (FixedDeltaTimeEnable) { @@ -126,10 +125,9 @@ void Time::TickData::Advance(double time, double deltaTime) TicksCount++; } -bool Time::FixedStepTickData::OnTickBegin(float targetFps, float maxDeltaTime) +bool Time::FixedStepTickData::OnTickBegin(double time, float targetFps, float maxDeltaTime) { // Check if can perform a tick - double time = Platform::GetTimeSeconds(); double deltaTime, minDeltaTime; if (FixedDeltaTimeEnable) { @@ -249,9 +247,9 @@ void Time::Synchronize() Draw.Synchronize(DrawFPS, time); } -bool Time::OnBeginUpdate() +bool Time::OnBeginUpdate(double time) { - if (Update.OnTickBegin(UpdateFPS, MaxUpdateDeltaTime)) + if (Update.OnTickBegin(time, UpdateFPS, MaxUpdateDeltaTime)) { Current = &Update; return true; @@ -259,9 +257,9 @@ bool Time::OnBeginUpdate() return false; } -bool Time::OnBeginPhysics() +bool Time::OnBeginPhysics(double time) { - if (Physics.OnTickBegin(PhysicsFPS, _physicsMaxDeltaTime)) + if (Physics.OnTickBegin(time, PhysicsFPS, _physicsMaxDeltaTime)) { Current = &Physics; return true; @@ -269,9 +267,9 @@ bool Time::OnBeginPhysics() return false; } -bool Time::OnBeginDraw() +bool Time::OnBeginDraw(double time) { - if (Draw.OnTickBegin(DrawFPS, 1.0f)) + if (Draw.OnTickBegin(time, DrawFPS, 1.0f)) { Current = &Draw; return true; diff --git a/Source/Engine/Engine/Time.h b/Source/Engine/Engine/Time.h index 4bb269cb9..ff81fb93e 100644 --- a/Source/Engine/Engine/Time.h +++ b/Source/Engine/Engine/Time.h @@ -12,12 +12,12 @@ /// API_CLASS(Static) class FLAXENGINE_API Time { -DECLARE_SCRIPTING_TYPE_NO_SPAWN(Time); + DECLARE_SCRIPTING_TYPE_NO_SPAWN(Time); friend class Engine; friend class TimeService; friend class PhysicsSettings; -public: +public: /// /// Engine subsystem updating data. /// Used to invoke game logic updates, physics updates and rendering with possibly different frequencies. @@ -25,7 +25,6 @@ public: class FLAXENGINE_API TickData { public: - virtual ~TickData() = default; /// @@ -75,14 +74,12 @@ public: TimeSpan UnscaledTime; public: - - virtual void OnBeforeRun(float targetFps, double currentTime); + virtual void Synchronize(float targetFps, double currentTime); virtual void OnReset(float targetFps, double currentTime); - virtual bool OnTickBegin(float targetFps, float maxDeltaTime); + virtual bool OnTickBegin(double time, float targetFps, float maxDeltaTime); virtual void OnTickEnd(); protected: - void Advance(double time, double deltaTime); }; @@ -92,25 +89,21 @@ public: class FixedStepTickData : public TickData { public: - /// /// The last few ticks delta times. Used to check if can use fixed steps or whenever is running slowly so should use normal stepping. /// SamplesBuffer Samples; public: - // [TickData] - bool OnTickBegin(float targetFps, float maxDeltaTime) override; + bool OnTickBegin(double time, float targetFps, float maxDeltaTime) override; }; private: - static bool _gamePaused; static float _physicsMaxDeltaTime; public: - /// /// The time at which the game started (UTC local). /// @@ -140,7 +133,6 @@ public: API_FIELD() static float TimeScale; public: - /// /// The game logic updating data. /// @@ -162,7 +154,6 @@ public: static TickData* Current; public: - /// /// Gets the current tick data (safety so returns Update tick data if no active). /// @@ -233,9 +224,9 @@ public: private: // Methods used by the Engine class - static bool OnBeginUpdate(); - static bool OnBeginPhysics(); - static bool OnBeginDraw(); + static bool OnBeginUpdate(double time); + static bool OnBeginPhysics(double time); + static bool OnBeginDraw(double time); static void OnEndUpdate(); static void OnEndPhysics(); From 16554fe742b2264e220f06f4fbc9ec0b7422e0a2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 17 Apr 2024 16:17:39 +0200 Subject: [PATCH 04/43] Fix cloning value utility for object references #2399 --- Source/Editor/Utilities/Utils.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index 1b76de47e..fbbbd71bf 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -210,12 +210,17 @@ namespace FlaxEditor.Utilities /// The duplicated value. internal static object CloneValue(object value) { + // For object references just clone it + if (value is FlaxEngine.Object) + return value; + // For objects (eg. arrays) we need to clone them to prevent editing default/reference value within editor if (value != null && (!value.GetType().IsValueType || !value.GetType().IsClass)) { var json = JsonSerializer.Serialize(value); value = JsonSerializer.Deserialize(json, value.GetType()); } + return value; } From 261faad93eb1474efb1a80894e162750c560524d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 17 Apr 2024 16:41:19 +0200 Subject: [PATCH 05/43] Fix incorrect View Size for 2d character font material --- Source/Engine/Render2D/Render2D.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Render2D/Render2D.cpp b/Source/Engine/Render2D/Render2D.cpp index 1c650585f..31136b455 100644 --- a/Source/Engine/Render2D/Render2D.cpp +++ b/Source/Engine/Render2D/Render2D.cpp @@ -988,7 +988,7 @@ void DrawBatch(int32 startIndex, int32 count) MaterialBase::BindParameters bindParams(Context, *(RenderContext*)nullptr); Render2D::CustomData customData; customData.ViewProjection = ViewProjection; - customData.ViewSize = Float2(d.AsMaterial.Width, d.AsMaterial.Height); + customData.ViewSize = Float2::One; bindParams.CustomData = &customData; material->Bind(bindParams); From d379b4c046784796bed29fce01397c2dfe23c6d7 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 17 Apr 2024 16:43:53 +0200 Subject: [PATCH 06/43] Fix missing UI Control preview when changing UI Widget type --- Source/Editor/Viewport/Previews/PrefabPreview.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/Editor/Viewport/Previews/PrefabPreview.cs b/Source/Editor/Viewport/Previews/PrefabPreview.cs index 16ec8f132..1673c1723 100644 --- a/Source/Editor/Viewport/Previews/PrefabPreview.cs +++ b/Source/Editor/Viewport/Previews/PrefabPreview.cs @@ -115,7 +115,12 @@ namespace FlaxEditor.Viewport.Previews _hasUILinked = true; } else if (_uiControlLinked != null) + { + if (_uiControlLinked.Control != null && + _uiControlLinked.Control.Parent == null) + _uiControlLinked.Control.Parent = _uiParentLink; _hasUILinked = true; + } } private void LinkCanvas(Actor actor) From e31ce787aa3d8bf43a45659c159c2a71addd80c0 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 17 Apr 2024 23:04:09 +0200 Subject: [PATCH 07/43] Add improved size of Multi Blend nodes #1980 --- .../Archetypes/Animation.MultiBlend.cs | 5 ++-- Source/Editor/Surface/Archetypes/Animation.cs | 26 +++++++++---------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs index 1178f54de..1b7ec5fc1 100644 --- a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs +++ b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs @@ -795,11 +795,12 @@ namespace FlaxEditor.Surface.Archetypes }; _animationY.ValueChanged += OnAnimationYChanged; + var size = Width - FlaxEditor.Surface.Constants.NodeMarginX * 2.0f; _editor = new Editor(this, FlaxEditor.Surface.Constants.NodeMarginX, _animationY.Bottom + 4.0f, - Width - FlaxEditor.Surface.Constants.NodeMarginX * 2.0f, - 120.0f); + size, + size); _editor.Parent = this; } diff --git a/Source/Editor/Surface/Archetypes/Animation.cs b/Source/Editor/Surface/Archetypes/Animation.cs index 98f238391..f00bd670b 100644 --- a/Source/Editor/Surface/Archetypes/Animation.cs +++ b/Source/Editor/Surface/Archetypes/Animation.cs @@ -658,10 +658,10 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Input(2, "Start Position", true, typeof(float), 3, 3), // Axis X - NodeElementArchetype.Factory.Input(4, "X", true, typeof(float), 4), - NodeElementArchetype.Factory.Text(30, 4 * Surface.Constants.LayoutOffsetY, "(min: max: )"), - NodeElementArchetype.Factory.Float(60, 4 * Surface.Constants.LayoutOffsetY, 0, 0), - NodeElementArchetype.Factory.Float(145, 4 * Surface.Constants.LayoutOffsetY, 0, 1), + NodeElementArchetype.Factory.Input(3, "X", true, typeof(float), 4), + NodeElementArchetype.Factory.Text(30, 3 * Surface.Constants.LayoutOffsetY + 2, "(min: max: )"), + NodeElementArchetype.Factory.Float(60, 3 * Surface.Constants.LayoutOffsetY + 2, 0, 0), + NodeElementArchetype.Factory.Float(145, 3 * Surface.Constants.LayoutOffsetY + 2, 0, 1), } }, new NodeArchetype @@ -671,7 +671,7 @@ namespace FlaxEditor.Surface.Archetypes Title = "Multi Blend 2D", Description = "Animation blending in 2D", Flags = NodeFlags.AnimGraph, - Size = new Float2(420, 320), + Size = new Float2(420, 620), DefaultValues = new object[] { // Node data @@ -707,16 +707,16 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Input(2, "Start Position", true, typeof(float), 3, 3), // Axis X - NodeElementArchetype.Factory.Input(4, "X", true, typeof(float), 4), - NodeElementArchetype.Factory.Text(30, 4 * Surface.Constants.LayoutOffsetY, "(min: max: )"), - NodeElementArchetype.Factory.Float(60, 4 * Surface.Constants.LayoutOffsetY, 0, 0), - NodeElementArchetype.Factory.Float(145, 4 * Surface.Constants.LayoutOffsetY, 0, 1), + NodeElementArchetype.Factory.Input(3, "X", true, typeof(float), 4), + NodeElementArchetype.Factory.Text(30, 3 * Surface.Constants.LayoutOffsetY + 2, "(min: max: )"), + NodeElementArchetype.Factory.Float(60, 3 * Surface.Constants.LayoutOffsetY + 2, 0, 0), + NodeElementArchetype.Factory.Float(145, 3 * Surface.Constants.LayoutOffsetY + 2, 0, 1), // Axis Y - NodeElementArchetype.Factory.Input(5, "Y", true, typeof(float), 5), - NodeElementArchetype.Factory.Text(30, 5 * Surface.Constants.LayoutOffsetY, "(min: max: )"), - NodeElementArchetype.Factory.Float(60, 5 * Surface.Constants.LayoutOffsetY, 0, 2), - NodeElementArchetype.Factory.Float(145, 5 * Surface.Constants.LayoutOffsetY, 0, 3), + NodeElementArchetype.Factory.Input(4, "Y", true, typeof(float), 5), + NodeElementArchetype.Factory.Text(30, 4 * Surface.Constants.LayoutOffsetY + 2, "(min: max: )"), + NodeElementArchetype.Factory.Float(60, 4 * Surface.Constants.LayoutOffsetY + 2, 0, 2), + NodeElementArchetype.Factory.Float(145, 4 * Surface.Constants.LayoutOffsetY + 2, 0, 3), } }, new NodeArchetype From 05278ca418b07dd0d829c39d21b6232dd48880c7 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 17 Apr 2024 23:28:10 +0200 Subject: [PATCH 08/43] Add undo for Multi Blend points moving #1980 --- .../Archetypes/Animation.MultiBlend.cs | 46 ++++++++++++++----- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs index 1b7ec5fc1..89eda77b4 100644 --- a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs +++ b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs @@ -6,6 +6,7 @@ using System.IO; using FlaxEditor.GUI; using FlaxEditor.GUI.Input; using FlaxEditor.Scripting; +using FlaxEditor.Surface.Undo; using FlaxEngine; using FlaxEngine.GUI; @@ -18,6 +19,7 @@ namespace FlaxEditor.Surface.Archetypes [HideInEditor] public abstract class BlendPointsEditor : ContainerControl { + private readonly Animation.MultiBlend _node; private readonly bool _is2D; private Float2 _rangeX; private Float2 _rangeY; @@ -34,7 +36,8 @@ namespace FlaxEditor.Surface.Archetypes private static Matrix3x3 _transform = Matrix3x3.RotationZ(45.0f * Mathf.DegreesToRadians) * Matrix3x3.Translation2D(4.0f, 0.5f); private readonly BlendPointsEditor _editor; private readonly int _index; - private bool _isMouseDown; + private bool _isMouseDown, _mouseMoved; + private object[] _mouseMoveStartValues; /// /// The default size for the blend points. @@ -53,6 +56,18 @@ namespace FlaxEditor.Surface.Archetypes _index = index; } + private void EndMove() + { + _isMouseDown = false; + EndMouseCapture(); + if (_mouseMoveStartValues != null) + { + // Add undo action + _editor._node.Surface.AddBatchedUndoAction(new EditNodeValuesAction(_editor._node, _mouseMoveStartValues, true)); + _mouseMoveStartValues = null; + } + } + /// public override void OnGotFocus() { @@ -80,6 +95,8 @@ namespace FlaxEditor.Surface.Archetypes { Focus(); _isMouseDown = true; + _mouseMoved = false; + _mouseMoveStartValues = null; StartMouseCapture(); return true; } @@ -92,8 +109,7 @@ namespace FlaxEditor.Surface.Archetypes { if (button == MouseButton.Left && _isMouseDown) { - _isMouseDown = false; - EndMouseCapture(); + EndMove(); return true; } @@ -105,6 +121,13 @@ namespace FlaxEditor.Surface.Archetypes { if (_isMouseDown) { + if (!_mouseMoved) + { + // Capture initial state for undo + _mouseMoved = true; + _mouseMoveStartValues = _editor._node.Surface.Undo != null ? (object[])_editor._node.Values.Clone() : null; + } + _editor.SetLocation(_index, _editor.BlendPointPosToBlendSpacePos(Location + location)); } @@ -116,8 +139,7 @@ namespace FlaxEditor.Surface.Archetypes { if (_isMouseDown) { - _isMouseDown = false; - EndMouseCapture(); + EndMove(); } base.OnMouseLeave(); @@ -128,8 +150,7 @@ namespace FlaxEditor.Surface.Archetypes { if (_isMouseDown) { - _isMouseDown = false; - EndMouseCapture(); + EndMove(); } base.OnLostFocus(); @@ -152,14 +173,16 @@ namespace FlaxEditor.Surface.Archetypes /// /// Initializes a new instance of the class. /// + /// The node. /// The value indicating whether blend space is 2D, otherwise it is 1D. /// The X location. /// The Y location. /// The width. /// The height. - public BlendPointsEditor(bool is2D, float x, float y, float width, float height) + public BlendPointsEditor(Animation.MultiBlend node, bool is2D, float x, float y, float width, float height) : base(x, y, width, height) { + _node = node; _is2D = is2D; } @@ -330,11 +353,10 @@ namespace FlaxEditor.Surface.Archetypes Render2D.DrawLine(new Float2(1, y), new Float2(rect.Width - 2, y), gridColor); } - // Base base.Draw(); // Frame - Render2D.DrawRectangle(new Rectangle(1, 1, rect.Width - 2, rect.Height - 2), containsFocus ? style.ProgressNormal : style.BackgroundSelected); + Render2D.DrawRectangle(new Rectangle(1, 1, rect.Width - 2, rect.Height - 2), containsFocus ? style.BackgroundSelected : style.ForegroundDisabled); } } @@ -585,7 +607,7 @@ namespace FlaxEditor.Surface.Archetypes /// The width. /// The height. public Editor(MultiBlend1D node, float x, float y, float width, float height) - : base(false, x, y, width, height) + : base(node, false, x, y, width, height) { _node = node; } @@ -719,7 +741,7 @@ namespace FlaxEditor.Surface.Archetypes /// The width. /// The height. public Editor(MultiBlend2D node, float x, float y, float width, float height) - : base(true, x, y, width, height) + : base(node, true, x, y, width, height) { _node = node; } From 695c212cf0a0af6bfbdb64bc7de861d439dcceef Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 18 Apr 2024 13:10:33 +0200 Subject: [PATCH 09/43] Add tooltips to Multi Blend points --- .../Surface/Archetypes/Animation.MultiBlend.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs index 89eda77b4..1998ce8f4 100644 --- a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs +++ b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs @@ -275,7 +275,9 @@ namespace FlaxEditor.Surface.Archetypes GetData(out _rangeX, out _rangeY, _pointsAnims, _pointsLocations); for (int i = 0; i < Animation.MultiBlend.MaxAnimationsCount; i++) { - if (_pointsAnims[i] != Guid.Empty) + var animId = _pointsAnims[i]; + var location = _pointsLocations[i]; + if (animId != Guid.Empty) { if (_blendPoints[i] == null) { @@ -287,7 +289,13 @@ namespace FlaxEditor.Surface.Archetypes } // Update blend point - _blendPoints[i].Location = BlendSpacePosToBlendPointPos(_pointsLocations[i]); + _blendPoints[i].Location = BlendSpacePosToBlendPointPos(location); + var asset = Editor.Instance.ContentDatabase.FindAsset(animId); + var tooltip = asset?.ShortName ?? string.Empty; + tooltip += "\nX: " + location.X; + if (_is2D) + tooltip += "\nY: " + location.Y; + _blendPoints[i].TooltipText = tooltip; } else { From 285710360c12e3885998e38edfeb998f0e038938 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 18 Apr 2024 15:26:52 +0200 Subject: [PATCH 10/43] Fix margins issues in context menus --- Source/Editor/GUI/ContextMenu/ContextMenu.cs | 11 ++++++----- .../GUI/ContextMenu/ContextMenuChildMenu.cs | 16 +++++++++++----- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/Source/Editor/GUI/ContextMenu/ContextMenu.cs b/Source/Editor/GUI/ContextMenu/ContextMenu.cs index 05d62d3f2..cb197e141 100644 --- a/Source/Editor/GUI/ContextMenu/ContextMenu.cs +++ b/Source/Editor/GUI/ContextMenu/ContextMenu.cs @@ -42,15 +42,14 @@ namespace FlaxEditor.GUI.ContextMenu // Arrange controls Margin margin = _menu._itemsMargin; - float y = margin.Top; - float x = margin.Left; + float y = 0; float width = Width - margin.Width; for (int i = 0; i < _children.Count; i++) { if (_children[i] is ContextMenuItem item && item.Visible) { var height = item.Height; - item.Bounds = new Rectangle(x, y, width, height); + item.Bounds = new Rectangle(margin.Left, y, width, height); y += height + margin.Height; } } @@ -300,7 +299,6 @@ namespace FlaxEditor.GUI.ContextMenu if (_panel.Children[i] is ContextMenuChildMenu menu && menu.Text == text) return menu; } - return null; } @@ -319,7 +317,6 @@ namespace FlaxEditor.GUI.ContextMenu Parent = _panel }; } - return item; } @@ -396,10 +393,12 @@ namespace FlaxEditor.GUI.ContextMenu float height = _itemsAreaMargin.Height; int itemsLeft = MaximumItemsInViewCount; int overflowItemCount = 0; + int itemsCount = 0; for (int i = 0; i < _panel.Children.Count; i++) { if (_panel.Children[i] is ContextMenuItem item && item.Visible) { + itemsCount++; if (itemsLeft > 0) { height += item.Height + _itemsMargin.Height; @@ -412,6 +411,8 @@ namespace FlaxEditor.GUI.ContextMenu maxWidth = Mathf.Max(maxWidth, item.MinimumWidth); } } + if (itemsCount != 0) + height -= _itemsMargin.Height; // Remove item margin from top and bottom maxWidth = Mathf.Max(maxWidth + 20, MinimumWidth); // Move child arrows to accommodate scroll bar showing diff --git a/Source/Editor/GUI/ContextMenu/ContextMenuChildMenu.cs b/Source/Editor/GUI/ContextMenu/ContextMenuChildMenu.cs index 3e422f98f..af0308184 100644 --- a/Source/Editor/GUI/ContextMenu/ContextMenuChildMenu.cs +++ b/Source/Editor/GUI/ContextMenu/ContextMenuChildMenu.cs @@ -29,6 +29,15 @@ namespace FlaxEditor.GUI.ContextMenu CloseMenuOnClick = false; } + private void ShowChild(ContextMenu parentContextMenu) + { + // Hide parent CM popups and set itself as child + var vAlign = parentContextMenu.ItemsAreaMargin.Top; + var location = new Float2(Width, -vAlign); + location = PointToParent(parentContextMenu, location); + parentContextMenu.ShowChild(ContextMenu, location); + } + /// public override void Draw() { @@ -58,14 +67,12 @@ namespace FlaxEditor.GUI.ContextMenu var parentContextMenu = ParentContextMenu; if (parentContextMenu == ContextMenu) return; - if (ContextMenu.IsOpened) return; base.OnMouseEnter(location); - // Hide parent CM popups and set itself as child - parentContextMenu.ShowChild(ContextMenu, PointToParent(ParentContextMenu, new Float2(Width, 0))); + ShowChild(parentContextMenu); } /// @@ -78,8 +85,7 @@ namespace FlaxEditor.GUI.ContextMenu if (ContextMenu.IsOpened) return true; - // Hide parent CM popups and set itself as child - parentContextMenu.ShowChild(ContextMenu, PointToParent(ParentContextMenu, new Float2(Width, 0))); + ShowChild(parentContextMenu); return base.OnMouseUp(location, button); } } From f8f1c02338e8c51590ac0a0d34af288b57314106 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 18 Apr 2024 15:27:23 +0200 Subject: [PATCH 11/43] Add context menu to Multi Blend node points --- .../Archetypes/Animation.MultiBlend.cs | 301 +++++++++--------- 1 file changed, 155 insertions(+), 146 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs index 1998ce8f4..ff3e5d8dd 100644 --- a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs +++ b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs @@ -17,7 +17,7 @@ namespace FlaxEditor.Surface.Archetypes /// /// [HideInEditor] - public abstract class BlendPointsEditor : ContainerControl + public class BlendPointsEditor : ContainerControl { private readonly Animation.MultiBlend _node; private readonly bool _is2D; @@ -73,14 +73,14 @@ namespace FlaxEditor.Surface.Archetypes { base.OnGotFocus(); - _editor.SelectedIndex = _index; + _editor._node.SelectedAnimationIndex = _index; } /// public override void Draw() { // Cache data - var isSelected = _editor.SelectedIndex == _index; + var isSelected = _editor._node.SelectedAnimationIndex == _index; // Draw rotated rectangle Render2D.PushTransform(ref _transform); @@ -173,7 +173,7 @@ namespace FlaxEditor.Surface.Archetypes /// /// Initializes a new instance of the class. /// - /// The node. + /// The node. /// The value indicating whether blend space is 2D, otherwise it is 1D. /// The X location. /// The Y location. @@ -193,19 +193,110 @@ namespace FlaxEditor.Surface.Archetypes /// The space range for Y axis (X-width, Y-height). /// The points anims (input array to fill of size equal 14). /// The points locations (input array to fill of size equal 14). - public abstract void GetData(out Float2 rangeX, out Float2 rangeY, Guid[] pointsAnims, Float2[] pointsLocations); + public void GetData(out Float2 rangeX, out Float2 rangeY, Guid[] pointsAnims, Float2[] pointsLocations) + { + var data0 = (Float4)_node.Values[0]; + rangeX = new Float2(data0.X, data0.Y); + rangeY = _is2D ? new Float2(data0.Z, data0.W) : Float2.Zero; + for (int i = 0; i < Animation.MultiBlend.MaxAnimationsCount; i++) + { + var dataA = (Float4)_node.Values[4 + i * 2]; + var dataB = (Guid)_node.Values[5 + i * 2]; + + pointsAnims[i] = dataB; + pointsLocations[i] = new Float2(Mathf.Clamp(dataA.X, rangeX.X, rangeX.Y), _is2D ? Mathf.Clamp(dataA.Y, rangeY.X, rangeY.Y) : 0.0f); + } + } + + private void AddAsset(Float2 location) + { + // Reuse existing animation + Guid id = Guid.Empty; + for (int i = 0; i < Animation.MultiBlend.MaxAnimationsCount; i++) + { + id = (Guid)_node.Values[5 + i * 2]; + if (id != Guid.Empty) + break; + } + if (id == Guid.Empty) + { + // Just use the first anim from project, user will change it + var ids = FlaxEngine.Content.GetAllAssetsByType(typeof(FlaxEngine.Animation)); + if (ids.Length != 0) + id = ids[0]; + else + return; + } + + AddAsset(id, location); + } /// - /// Gets or sets the index of the selected blend point. + /// Sets the blend point asset. /// - public abstract int SelectedIndex { get; set; } + /// The asset. + /// The location. + public void AddAsset(Guid asset, Float2 location) + { + // Find the first free slot + var values = (object[])_node.Values.Clone(); + var index = 0; + for (; index < Animation.MultiBlend.MaxAnimationsCount; index++) + { + var dataB = (Guid)_node.Values[5 + index * 2]; + if (dataB == Guid.Empty) + break; + } + if (index == Animation.MultiBlend.MaxAnimationsCount) + return; // TODO: unlimited amount of blend points + + values[4 + index * 2] = new Float4(location.X, _is2D ? location.Y : 0.0f, 0, 1.0f); + values[5 + index * 2] = asset; + _node.SetValues(values); + + _node.UpdateUI(); + } + + /// + /// Sets the blend point asset. + /// + /// The index. + /// The asset. + /// True to use undo action. + public void SetAsset(int index, Guid asset, bool withUndo = true) + { + if (withUndo) + { + _node.SetValue(5 + index * 2, asset); + } + else + { + _node.Values[5 + index * 2] = asset; + _node.Surface.MarkAsEdited(); + } + + _node.UpdateUI(); + } /// /// Sets the blend point location. /// /// The index. /// The location. - public abstract void SetLocation(int index, Float2 location); + public void SetLocation(int index, Float2 location) + { + var dataA = (Float4)_node.Values[4 + index * 2]; + var ranges = (Float4)_node.Values[0]; + + dataA.X = Mathf.Clamp(location.X, ranges.X, ranges.Y); + if (_is2D) + dataA.Y = Mathf.Clamp(location.Y, ranges.Z, ranges.W); + + _node.Values[4 + index * 2] = dataA; + _node.Surface.MarkAsEdited(); + + _node.UpdateUI(); + } /// /// Gets the blend points area. @@ -284,6 +375,7 @@ namespace FlaxEditor.Surface.Archetypes // Create missing blend point _blendPoints[i] = new BlendPoint(this, i) { + Tag = i, Parent = this, }; } @@ -324,6 +416,50 @@ namespace FlaxEditor.Surface.Archetypes } } + /// + public override bool OnMouseDown(Float2 location, MouseButton button) + { + if (base.OnMouseDown(location, button)) + return true; + + Focus(); + return true; + } + + /// + public override bool OnMouseUp(Float2 location, MouseButton button) + { + if (base.OnMouseUp(location, button)) + return true; + + if (button == MouseButton.Right) + { + // Show context menu + var menu = new FlaxEditor.GUI.ContextMenu.ContextMenu(); + var b = menu.AddButton("Add point", OnAddPoint); + b.Tag = location; + if (GetChildAt(location) is BlendPoint blendPoint) + { + b = menu.AddButton("Remove point", OnRemovePoint); + b.Tag = blendPoint.Tag; + b.TooltipText = blendPoint.TooltipText; + } + menu.Show(this, location); + } + + return true; + } + + private void OnAddPoint(FlaxEditor.GUI.ContextMenu.ContextMenuButton b) + { + AddAsset(BlendPointPosToBlendSpacePos((Float2)b.Tag)); + } + + private void OnRemovePoint(FlaxEditor.GUI.ContextMenu.ContextMenuButton b) + { + SetAsset((int)b.Tag, Guid.Empty); + } + /// public override void Draw() { @@ -364,7 +500,8 @@ namespace FlaxEditor.Surface.Archetypes base.Draw(); // Frame - Render2D.DrawRectangle(new Rectangle(1, 1, rect.Width - 2, rect.Height - 2), containsFocus ? style.BackgroundSelected : style.ForegroundDisabled); + var frameColor = containsFocus ? style.BackgroundSelected : (IsMouseOver ? style.ForegroundGrey : style.ForegroundDisabled); + Render2D.DrawRectangle(new Rectangle(1, 1, rect.Width - 2, rect.Height - 2), frameColor); } } @@ -521,7 +658,7 @@ namespace FlaxEditor.Surface.Archetypes /// if set to true is selection valid. /// The packed data 0. /// The packed data 1. - protected virtual void UpdateUI(int selectedIndex, bool isValid, ref Float4 data0, ref Guid data1) + public virtual void UpdateUI(int selectedIndex, bool isValid, ref Float4 data0, ref Guid data1) { if (isValid) { @@ -546,7 +683,7 @@ namespace FlaxEditor.Surface.Archetypes /// /// Updates the editor UI. /// - protected void UpdateUI() + public void UpdateUI() { if (_isUpdatingUI) return; @@ -596,67 +733,7 @@ namespace FlaxEditor.Surface.Archetypes { private readonly Label _animationXLabel; private readonly FloatValueBox _animationX; - private readonly Editor _editor; - - /// - /// The Multi Blend 1D blend space editor. - /// - /// - protected class Editor : BlendPointsEditor - { - private MultiBlend1D _node; - - /// - /// Initializes a new instance of the class. - /// - /// The parent Visject Node node. - /// The X location. - /// The Y location. - /// The width. - /// The height. - public Editor(MultiBlend1D node, float x, float y, float width, float height) - : base(node, false, x, y, width, height) - { - _node = node; - } - - /// - public override void GetData(out Float2 rangeX, out Float2 rangeY, Guid[] pointsAnims, Float2[] pointsLocations) - { - var data0 = (Float4)_node.Values[0]; - rangeX = new Float2(data0.X, data0.Y); - rangeY = Float2.Zero; - for (int i = 0; i < MaxAnimationsCount; i++) - { - var dataA = (Float4)_node.Values[4 + i * 2]; - var dataB = (Guid)_node.Values[5 + i * 2]; - - pointsAnims[i] = dataB; - pointsLocations[i] = new Float2(Mathf.Clamp(dataA.X, rangeX.X, rangeX.Y), 0.0f); - } - } - - /// - public override int SelectedIndex - { - get => _node.SelectedAnimationIndex; - set => _node.SelectedAnimationIndex = value; - } - - /// - public override void SetLocation(int index, Float2 location) - { - var dataA = (Float4)_node.Values[4 + index * 2]; - var ranges = (Float4)_node.Values[0]; - - dataA.X = Mathf.Clamp(location.X, ranges.X, ranges.Y); - - _node.Values[4 + index * 2] = dataA; - _node.Surface.MarkAsEdited(); - - _node.UpdateUI(); - } - } + private readonly BlendPointsEditor _editor; /// public MultiBlend1D(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch) @@ -676,11 +753,8 @@ namespace FlaxEditor.Surface.Archetypes }; _animationX.ValueChanged += OnAnimationXChanged; - _editor = new Editor(this, - FlaxEditor.Surface.Constants.NodeMarginX, - _animationX.Bottom + 4.0f, - Width - FlaxEditor.Surface.Constants.NodeMarginX * 2.0f, - 120.0f); + var size = Width - FlaxEditor.Surface.Constants.NodeMarginX * 2.0f; + _editor = new BlendPointsEditor(this, false, FlaxEditor.Surface.Constants.NodeMarginX, _animationX.Bottom + 4.0f, size, 120.0f); _editor.Parent = this; } @@ -700,7 +774,7 @@ namespace FlaxEditor.Surface.Archetypes } /// - protected override void UpdateUI(int selectedIndex, bool isValid, ref Float4 data0, ref Guid data1) + public override void UpdateUI(int selectedIndex, bool isValid, ref Float4 data0, ref Guid data1) { base.UpdateUI(selectedIndex, isValid, ref data0, ref data1); @@ -730,68 +804,7 @@ namespace FlaxEditor.Surface.Archetypes private readonly FloatValueBox _animationX; private readonly Label _animationYLabel; private readonly FloatValueBox _animationY; - private readonly Editor _editor; - - /// - /// The Multi Blend 2D blend space editor. - /// - /// - protected class Editor : BlendPointsEditor - { - private MultiBlend2D _node; - - /// - /// Initializes a new instance of the class. - /// - /// The parent Visject Node node. - /// The X location. - /// The Y location. - /// The width. - /// The height. - public Editor(MultiBlend2D node, float x, float y, float width, float height) - : base(node, true, x, y, width, height) - { - _node = node; - } - - /// - public override void GetData(out Float2 rangeX, out Float2 rangeY, Guid[] pointsAnims, Float2[] pointsLocations) - { - var data0 = (Float4)_node.Values[0]; - rangeX = new Float2(data0.X, data0.Y); - rangeY = new Float2(data0.Z, data0.W); - for (int i = 0; i < MaxAnimationsCount; i++) - { - var dataA = (Float4)_node.Values[4 + i * 2]; - var dataB = (Guid)_node.Values[5 + i * 2]; - - pointsAnims[i] = dataB; - pointsLocations[i] = new Float2(Mathf.Clamp(dataA.X, rangeX.X, rangeX.Y), Mathf.Clamp(dataA.Y, rangeY.X, rangeY.Y)); - } - } - - /// - public override int SelectedIndex - { - get => _node.SelectedAnimationIndex; - set => _node.SelectedAnimationIndex = value; - } - - /// - public override void SetLocation(int index, Float2 location) - { - var dataA = (Float4)_node.Values[4 + index * 2]; - var ranges = (Float4)_node.Values[0]; - - dataA.X = Mathf.Clamp(location.X, ranges.X, ranges.Y); - dataA.Y = Mathf.Clamp(location.Y, ranges.Z, ranges.W); - - _node.Values[4 + index * 2] = dataA; - _node.Surface.MarkAsEdited(); - - _node.UpdateUI(); - } - } + private readonly BlendPointsEditor _editor; /// public MultiBlend2D(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch) @@ -826,11 +839,7 @@ namespace FlaxEditor.Surface.Archetypes _animationY.ValueChanged += OnAnimationYChanged; var size = Width - FlaxEditor.Surface.Constants.NodeMarginX * 2.0f; - _editor = new Editor(this, - FlaxEditor.Surface.Constants.NodeMarginX, - _animationY.Bottom + 4.0f, - size, - size); + _editor = new BlendPointsEditor(this, true, FlaxEditor.Surface.Constants.NodeMarginX, _animationY.Bottom + 4.0f, size, size); _editor.Parent = this; } @@ -865,7 +874,7 @@ namespace FlaxEditor.Surface.Archetypes } /// - protected override void UpdateUI(int selectedIndex, bool isValid, ref Float4 data0, ref Guid data1) + public override void UpdateUI(int selectedIndex, bool isValid, ref Float4 data0, ref Guid data1) { base.UpdateUI(selectedIndex, isValid, ref data0, ref data1); From 83b9e6e32a927b60545a752b7641ea1f9efd4da1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 18 Apr 2024 17:04:23 +0200 Subject: [PATCH 12/43] Add `Ctrl` to snap points to grid in Multi Blend editor --- .../Surface/Archetypes/Animation.MultiBlend.cs | 12 +++++++++++- Source/Engine/Core/Math/Float2.cs | 4 ++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs index ff3e5d8dd..0a71189be 100644 --- a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs +++ b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs @@ -128,7 +128,17 @@ namespace FlaxEditor.Surface.Archetypes _mouseMoveStartValues = _editor._node.Surface.Undo != null ? (object[])_editor._node.Values.Clone() : null; } - _editor.SetLocation(_index, _editor.BlendPointPosToBlendSpacePos(Location + location)); + var newLocation = Location + location; + newLocation = _editor.BlendPointPosToBlendSpacePos(newLocation); + if (Root != null && Root.GetKey(KeyboardKeys.Control)) + { + var data0 = (Float4)_editor._node.Values[0]; + var rangeX = new Float2(data0.X, data0.Y); + var rangeY = _editor._is2D ? new Float2(data0.Z, data0.W) : Float2.One; + var grid = new Float2(Mathf.Abs(rangeX.Y - rangeX.X) * 0.01f, Mathf.Abs(rangeY.X - rangeY.Y) * 0.01f); + newLocation = Float2.SnapToGrid(newLocation, grid); + } + _editor.SetLocation(_index, newLocation); } base.OnMouseMove(location); diff --git a/Source/Engine/Core/Math/Float2.cs b/Source/Engine/Core/Math/Float2.cs index ef76cf7cb..9c3909214 100644 --- a/Source/Engine/Core/Math/Float2.cs +++ b/Source/Engine/Core/Math/Float2.cs @@ -1281,8 +1281,8 @@ namespace FlaxEngine /// The position snapped to the grid. public static Float2 SnapToGrid(Float2 pos, Float2 gridSize) { - pos.X = Mathf.Ceil((pos.X - (gridSize.X * 0.5f)) / gridSize.Y) * gridSize.X; - pos.Y = Mathf.Ceil((pos.Y - (gridSize.Y * 0.5f)) / gridSize.X) * gridSize.Y; + pos.X = Mathf.Ceil((pos.X - (gridSize.X * 0.5f)) / gridSize.X) * gridSize.X; + pos.Y = Mathf.Ceil((pos.Y - (gridSize.Y * 0.5f)) / gridSize.Y) * gridSize.Y; return pos; } From d8079367fd0ffdef4a339108b9a40913d1cd6346 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 18 Apr 2024 17:22:21 +0200 Subject: [PATCH 13/43] Add improved visuals of points in Multi Blend --- .../Archetypes/Animation.MultiBlend.cs | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs index 0a71189be..ee2178c7c 100644 --- a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs +++ b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs @@ -33,7 +33,6 @@ namespace FlaxEditor.Surface.Archetypes /// protected class BlendPoint : Control { - private static Matrix3x3 _transform = Matrix3x3.RotationZ(45.0f * Mathf.DegreesToRadians) * Matrix3x3.Translation2D(4.0f, 0.5f); private readonly BlendPointsEditor _editor; private readonly int _index; private bool _isMouseDown, _mouseMoved; @@ -79,13 +78,20 @@ namespace FlaxEditor.Surface.Archetypes /// public override void Draw() { - // Cache data - var isSelected = _editor._node.SelectedAnimationIndex == _index; - - // Draw rotated rectangle - Render2D.PushTransform(ref _transform); - Render2D.FillRectangle(new Rectangle(0, 0, 5, 5), isSelected ? Color.Orange : Color.BlueViolet); - Render2D.PopTransform(); + // Draw dot with outline + var style = Style.Current; + var icon = Editor.Instance.Icons.VisjectBoxClosed32; + var size = Height; + var rect = new Rectangle(new Float2(size * -0.5f) + Size * 0.5f, new Float2(size)); + var outline = Color.Black; // Shadow + if (_isMouseDown) + outline = style.SelectionBorder; + else if (IsMouseOver) + outline = style.BorderHighlighted; + else if (_editor._node.SelectedAnimationIndex == _index) + outline = style.BackgroundSelected; + Render2D.DrawSprite(icon, rect.MakeExpanded(4.0f), outline); + Render2D.DrawSprite(icon, rect, style.Foreground); } /// From 4b6b24f4c55689b01bdf1d19434d7e88abe126e4 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 18 Apr 2024 17:22:37 +0200 Subject: [PATCH 14/43] Fix precision of blend points moving in Multi Blend --- Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs index ee2178c7c..41b984650 100644 --- a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs +++ b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs @@ -35,6 +35,7 @@ namespace FlaxEditor.Surface.Archetypes { private readonly BlendPointsEditor _editor; private readonly int _index; + private Float2 _mousePosOffset; private bool _isMouseDown, _mouseMoved; private object[] _mouseMoveStartValues; @@ -65,6 +66,7 @@ namespace FlaxEditor.Surface.Archetypes _editor._node.Surface.AddBatchedUndoAction(new EditNodeValuesAction(_editor._node, _mouseMoveStartValues, true)); _mouseMoveStartValues = null; } + _editor._node.Surface.MarkAsEdited(); } /// @@ -103,6 +105,7 @@ namespace FlaxEditor.Surface.Archetypes _isMouseDown = true; _mouseMoved = false; _mouseMoveStartValues = null; + _mousePosOffset = -location; StartMouseCapture(); return true; } @@ -134,7 +137,7 @@ namespace FlaxEditor.Surface.Archetypes _mouseMoveStartValues = _editor._node.Surface.Undo != null ? (object[])_editor._node.Values.Clone() : null; } - var newLocation = Location + location; + var newLocation = Location + location + _mousePosOffset; newLocation = _editor.BlendPointPosToBlendSpacePos(newLocation); if (Root != null && Root.GetKey(KeyboardKeys.Control)) { @@ -309,7 +312,6 @@ namespace FlaxEditor.Surface.Archetypes dataA.Y = Mathf.Clamp(location.Y, ranges.Z, ranges.W); _node.Values[4 + index * 2] = dataA; - _node.Surface.MarkAsEdited(); _node.UpdateUI(); } From a3b5f4b78920c6933aee61cd06847359ab744ec5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 18 Apr 2024 18:22:19 +0200 Subject: [PATCH 15/43] Add grid labels drawing to Multi Blend --- .../Archetypes/Animation.MultiBlend.cs | 57 ++++++++++++++++--- 1 file changed, 49 insertions(+), 8 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs index 41b984650..74e0a2b4b 100644 --- a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs +++ b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs @@ -66,7 +66,8 @@ namespace FlaxEditor.Surface.Archetypes _editor._node.Surface.AddBatchedUndoAction(new EditNodeValuesAction(_editor._node, _mouseMoveStartValues, true)); _mouseMoveStartValues = null; } - _editor._node.Surface.MarkAsEdited(); + if (_mouseMoved) + _editor._node.Surface.MarkAsEdited(); } /// @@ -478,14 +479,47 @@ namespace FlaxEditor.Surface.Archetypes SetAsset((int)b.Tag, Guid.Empty); } + private void DrawAxis(bool vertical, Float2 start, Float2 end, ref Color gridColor, ref Color labelColor, Font labelFont, float value, bool isLast) + { + // Draw line + Render2D.DrawLine(start, end, gridColor); + + // Draw label + var labelWidth = 50.0f; + var labelHeight = 10.0f; + var labelMargin = 2.0f; + string label = Utils.RoundTo2DecimalPlaces(value).ToString(System.Globalization.CultureInfo.InvariantCulture); + var hAlign = TextAlignment.Near; + Rectangle labelRect; + if (vertical) + { + labelRect = new Rectangle(start.X + labelMargin * 2, start.Y, labelWidth, labelHeight); + if (isLast) + return; // Don't overlap with the first horizontal label + } + else + { + labelRect = new Rectangle(start.X + labelMargin, start.Y - labelHeight - labelMargin, labelWidth, labelHeight); + if (isLast) + { + labelRect.X = start.X - labelMargin - labelRect.Width; + hAlign = TextAlignment.Far; + } + } + Render2D.DrawText(labelFont, label, labelRect, labelColor, hAlign, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.7f); + } + /// public override void Draw() { - // Cache data var style = Style.Current; var rect = new Rectangle(Float2.Zero, Size); var containsFocus = ContainsFocus; GetPointsArea(out var pointsArea); + var data0 = (Float4)_node.Values[0]; + var rangeX = new Float2(data0.X, data0.Y); + var rangeY = _is2D ? new Float2(data0.Z, data0.W) : Float2.One; + var grid = new Float2(Mathf.Abs(rangeX.Y - rangeX.X) * 0.01f, Mathf.Abs(rangeY.X - rangeY.Y) * 0.01f); // Background Render2D.DrawRectangle(rect, IsMouseOver ? style.TextBoxBackgroundSelected : style.TextBoxBackground); @@ -494,19 +528,26 @@ namespace FlaxEditor.Surface.Archetypes // Grid int splits = 10; var gridColor = style.TextBoxBackgroundSelected * 1.1f; + var labelColor = style.ForegroundDisabled; + var labelFont = style.FontSmall; //var blendArea = BlendAreaRect; var blendArea = pointsArea; - for (int i = 0; i < splits; i++) + + for (int i = 0; i <= splits; i++) { - float x = blendArea.Left + blendArea.Width * i / splits; - Render2D.DrawLine(new Float2(x, 1), new Float2(x, rect.Height - 2), gridColor); + float alpha = (float)i / splits; + float x = blendArea.Left + blendArea.Width * alpha; + float value = Mathf.Lerp(rangeX.X, rangeX.Y, alpha); + DrawAxis(false, new Float2(x, rect.Height - 2), new Float2(x, 1), ref gridColor, ref labelColor, labelFont, value, i == splits); } if (_is2D) { - for (int i = 0; i < splits; i++) + for (int i = 0; i <= splits; i++) { - float y = blendArea.Top + blendArea.Height * i / splits; - Render2D.DrawLine(new Float2(1, y), new Float2(rect.Width - 2, y), gridColor); + float alpha = (float)i / splits; + float y = blendArea.Top + blendArea.Height * alpha; + float value = Mathf.Lerp(rangeY.X, rangeY.Y, alpha); + DrawAxis(true, new Float2(1, y), new Float2(rect.Width - 2, y), ref gridColor, ref labelColor, labelFont, value, i == splits); } } else From 0845866c4db71e5bedde92f243c91b7504796706 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 18 Apr 2024 18:42:31 +0200 Subject: [PATCH 16/43] Add live debugging of blend position in Multi Blend --- .../Archetypes/Animation.MultiBlend.cs | 37 ++++++++++++++++++- .../Animations/Graph/AnimGraph.Base.cpp | 2 - .../Animations/Graph/AnimGroup.Animation.cpp | 15 ++++++++ 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs index 74e0a2b4b..174f867fc 100644 --- a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs +++ b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs @@ -21,8 +21,8 @@ namespace FlaxEditor.Surface.Archetypes { private readonly Animation.MultiBlend _node; private readonly bool _is2D; - private Float2 _rangeX; - private Float2 _rangeY; + private Float2 _rangeX, _rangeY; + private Float2 _debugPos = Float2.Minimum; private readonly BlendPoint[] _blendPoints = new BlendPoint[Animation.MultiBlend.MaxAnimationsCount]; private readonly Guid[] _pointsAnims = new Guid[Animation.MultiBlend.MaxAnimationsCount]; private readonly Float2[] _pointsLocations = new Float2[Animation.MultiBlend.MaxAnimationsCount]; @@ -419,6 +419,26 @@ namespace FlaxEditor.Surface.Archetypes } } + // Debug current playback position + if (((AnimGraphSurface)_node.Surface).TryGetTraceEvent(_node, out var traceEvent)) + { + if (_is2D) + { + unsafe + { + // Unpack xy from 32-bits + Half2 packed = *(Half2*)&traceEvent.Value; + _debugPos = (Float2)packed; + } + } + else + _debugPos = new Float2(traceEvent.Value, 0.0f); + } + else + { + _debugPos = Float2.Minimum; + } + base.Update(deltaTime); } @@ -558,6 +578,19 @@ namespace FlaxEditor.Surface.Archetypes base.Draw(); + // Draw debug position + if (_debugPos.X > float.MinValue) + { + // Draw dot with outline + var icon = Editor.Instance.Icons.VisjectBoxOpen32; + var size = BlendPoint.DefaultSize; + var debugPos = BlendSpacePosToBlendPointPos(_debugPos); + var debugRect = new Rectangle(debugPos + new Float2(size * -0.5f) + size * 0.5f, new Float2(size)); + var outline = Color.Black; // Shadow + Render2D.DrawSprite(icon, debugRect.MakeExpanded(2.0f), outline); + Render2D.DrawSprite(icon, debugRect, style.ProgressNormal); + } + // Frame var frameColor = containsFocus ? style.BackgroundSelected : (IsMouseOver ? style.ForegroundGrey : style.ForegroundDisabled); Render2D.DrawRectangle(new Rectangle(1, 1, rect.Width - 2, rect.Height - 2), frameColor); diff --git a/Source/Engine/Animations/Graph/AnimGraph.Base.cpp b/Source/Engine/Animations/Graph/AnimGraph.Base.cpp index 7cbc3367d..dcc46f1ce 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.Base.cpp +++ b/Source/Engine/Animations/Graph/AnimGraph.Base.cpp @@ -174,7 +174,6 @@ bool AnimGraphBase::onNodeLoaded(Node* n) { ADD_BUCKET(MultiBlendBucketInit); n->Data.MultiBlend1D.Length = -1; - const Float4 range = n->Values[0].AsFloat4(); for (int32 i = 0; i < ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS; i++) { n->Assets[i] = Content::LoadAsync((Guid)n->Values[i * 2 + 5]); @@ -192,7 +191,6 @@ bool AnimGraphBase::onNodeLoaded(Node* n) // Get blend points locations Array> vertices; byte vertexIndexToAnimIndex[ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS]; - const Float4 range = n->Values[0].AsFloat4(); for (int32 i = 0; i < ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS; i++) { n->Assets[i] = (Asset*)Content::LoadAsync((Guid)n->Values[i * 2 + 5]); diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp index 21eb44265..ae1b7369f 100644 --- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp +++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp @@ -1275,6 +1275,13 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu float x = (float)tryGetValue(node->GetBox(4), Value::Zero); x = Math::Clamp(x, range.X, range.Y); + // Add to trace + if (context.Data->EnableTracing) + { + auto& trace = context.AddTraceEvent(node); + trace.Value = x; + } + // Check if need to evaluate multi blend length if (data.Length < 0) ComputeMultiBlendLength(data.Length, node); @@ -1368,6 +1375,14 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu float y = (float)tryGetValue(node->GetBox(5), Value::Zero); y = Math::Clamp(y, range.Z, range.W); + // Add to trace + if (context.Data->EnableTracing) + { + auto& trace = context.AddTraceEvent(node); + const Half2 packed(x, y); // Pack xy into 32-bits + *(uint32*)&trace.Value = *(uint32*)&packed; + } + // Check if need to evaluate multi blend length if (data.Length < 0) ComputeMultiBlendLength(data.Length, node); From 71fe280464ffbf83fcd4ec78fa161aa0d2202354 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 19 Apr 2024 12:22:04 +0200 Subject: [PATCH 17/43] Refactor Visject Graph nodes data for unlimited size --- .../Animations/Graph/AnimGraph.Base.cpp | 5 +++++ Source/Engine/Animations/Graph/AnimGraph.h | 20 ++----------------- .../Animations/Graph/AnimGroup.Animation.cpp | 11 +++++----- .../Graph/GPU/ParticleEmitterGraph.GPU.h | 2 +- .../Particles/Graph/ParticleEmitterGraph.h | 6 ++++++ Source/Engine/Visject/GraphNode.h | 5 +---- Source/Engine/Visject/VisjectGraph.h | 8 +++----- 7 files changed, 24 insertions(+), 33 deletions(-) diff --git a/Source/Engine/Animations/Graph/AnimGraph.Base.cpp b/Source/Engine/Animations/Graph/AnimGraph.Base.cpp index dcc46f1ce..ea887b056 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.Base.cpp +++ b/Source/Engine/Animations/Graph/AnimGraph.Base.cpp @@ -163,10 +163,12 @@ bool AnimGraphBase::onNodeLoaded(Node* n) // Animation case 2: ADD_BUCKET(AnimationBucketInit); + n->Assets.Resize(1); n->Assets[0] = (Asset*)Content::LoadAsync((Guid)n->Values[0]); break; // Blend with Mask case 11: + n->Assets.Resize(1); n->Assets[0] = (Asset*)Content::LoadAsync((Guid)n->Values[1]); break; // Multi Blend 1D @@ -174,6 +176,7 @@ bool AnimGraphBase::onNodeLoaded(Node* n) { ADD_BUCKET(MultiBlendBucketInit); n->Data.MultiBlend1D.Length = -1; + n->Assets.Resize(ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS); for (int32 i = 0; i < ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS; i++) { n->Assets[i] = Content::LoadAsync((Guid)n->Values[i * 2 + 5]); @@ -191,6 +194,7 @@ bool AnimGraphBase::onNodeLoaded(Node* n) // Get blend points locations Array> vertices; byte vertexIndexToAnimIndex[ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS]; + n->Assets.Resize(ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS); for (int32 i = 0; i < ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS; i++) { n->Assets[i] = (Asset*)Content::LoadAsync((Guid)n->Values[i * 2 + 5]); @@ -305,6 +309,7 @@ bool AnimGraphBase::onNodeLoaded(Node* n) data.Graph = nullptr; break; } + n->Assets.Resize(1); n->Assets[0] = function; // Load the graph diff --git a/Source/Engine/Animations/Graph/AnimGraph.h b/Source/Engine/Animations/Graph/AnimGraph.h index ebee7e901..ef72c2d72 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.h +++ b/Source/Engine/Animations/Graph/AnimGraph.h @@ -427,23 +427,7 @@ struct AnimGraphTransitionData float Length; }; -class AnimGraphBox : public VisjectGraphBox -{ -public: - AnimGraphBox() - { - } - - AnimGraphBox(AnimGraphNode* parent, byte id, const VariantType::Types type) - : VisjectGraphBox(parent, id, type) - { - } - - AnimGraphBox(AnimGraphNode* parent, byte id, const VariantType& type) - : VisjectGraphBox(parent, id, type) - { - } -}; +typedef VisjectGraphBox AnimGraphBox; class AnimGraphNode : public VisjectGraphNode { @@ -603,7 +587,7 @@ public: /// Gets the per-node node transformations cache (cached). /// /// The Graph execution context. - /// The modes data. + /// Nodes data. AnimGraphImpulse* GetNodes(AnimGraphExecutor* executor); }; diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp index ae1b7369f..fbb61c4a2 100644 --- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp +++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp @@ -673,19 +673,20 @@ void ComputeMultiBlendLength(float& length, AnimGraphNode* node) // TODO: lock graph or graph asset here? make it thread safe length = 0.0f; - for (int32 i = 0; i < ARRAY_COUNT(node->Assets); i++) + for (int32 i = 0; i < node->Assets.Count(); i++) { - if (node->Assets[i]) + auto& asset = node->Assets[i]; + if (asset) { // TODO: maybe don't update if not all anims are loaded? just skip the node with the bind pose? - if (node->Assets[i]->WaitForLoaded()) + if (asset->WaitForLoaded()) { - node->Assets[i] = nullptr; + asset = nullptr; LOG(Warning, "Failed to load one of the animations."); } else { - const auto anim = node->Assets[i].As(); + const auto anim = asset.As(); const auto aData = node->Values[4 + i * 2].AsFloat4(); length = Math::Max(length, anim->GetLength() * Math::Abs(aData.W)); } diff --git a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.h b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.h index 7874e4d82..25d9c853f 100644 --- a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.h +++ b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.h @@ -26,7 +26,7 @@ public: /// /// The asset references. Linked resources such as Animation assets are referenced in graph data as ID. We need to keep valid refs to them at runtime to keep data in memory. /// - AssetReference Assets[14]; + Array> Assets; }; /// diff --git a/Source/Engine/Particles/Graph/ParticleEmitterGraph.h b/Source/Engine/Particles/Graph/ParticleEmitterGraph.h index 77dd81d0f..07630a4b4 100644 --- a/Source/Engine/Particles/Graph/ParticleEmitterGraph.h +++ b/Source/Engine/Particles/Graph/ParticleEmitterGraph.h @@ -177,6 +177,7 @@ public: // Get Gameplay Global case GRAPH_NODE_MAKE_TYPE(7, 16): { + node->Assets.Resize(1); node->Assets[0] = Content::LoadAsync((Guid)node->Values[0]); break; } @@ -296,6 +297,7 @@ public: break; // Particle Emitter Function case GRAPH_NODE_MAKE_TYPE(14, 300): + node->Assets.Resize(1); InitParticleEmitterFunctionCall((Guid)node->Values[0], node->Assets[0], node->UsesParticleData, Layout); break; // Particle Index @@ -447,6 +449,7 @@ public: // Sprite Rendering case GRAPH_NODE_MAKE_TYPE(15, 400): { + node->Assets.Resize(1); node->Assets[0] = Content::LoadAsync((Guid)node->Values[2]); USE_ATTRIBUTE(Position, Float3, 0); USE_ATTRIBUTE(Rotation, Float3, 1); @@ -484,6 +487,7 @@ public: // Model Rendering case GRAPH_NODE_MAKE_TYPE(15, 403): { + node->Assets.Resize(2); node->Assets[0] = Content::LoadAsync((Guid)node->Values[2]); node->Assets[1] = Content::LoadAsync((Guid)node->Values[3]); USE_ATTRIBUTE(Position, Float3, 0); @@ -494,6 +498,7 @@ public: // Ribbon Rendering case GRAPH_NODE_MAKE_TYPE(15, 404): { + node->Assets.Resize(1); node->Assets[0] = Content::LoadAsync((Guid)node->Values[2]); USE_ATTRIBUTE(Position, Float3, 0); // TODO: add support for custom sorting key - not only by age @@ -503,6 +508,7 @@ public: // Volumetric Fog Rendering case GRAPH_NODE_MAKE_TYPE(15, 405): { + node->Assets.Resize(1); node->Assets[0] = Content::LoadAsync((Guid)node->Values[2]); USE_ATTRIBUTE(Position, Float3, 0); USE_ATTRIBUTE(Radius, Float, 1); diff --git a/Source/Engine/Visject/GraphNode.h b/Source/Engine/Visject/GraphNode.h index 628bc6d57..f4eb9cc29 100644 --- a/Source/Engine/Visject/GraphNode.h +++ b/Source/Engine/Visject/GraphNode.h @@ -11,8 +11,6 @@ class GraphNode; #define GRAPH_NODE_MAKE_TYPE(groupID, typeID) (uint32)((groupID) << 16 | (typeID)) -#define GRAPH_NODE_MAX_VALUES 32 - /// /// Represents single box of the graph node /// @@ -114,14 +112,13 @@ public: uint16 TypeID; uint16 GroupID; }; - uint32 Type; }; /// /// List of all node values. Array size and value types are constant over time. Only value data can change. /// - Array> Values; + Array> Values; /// /// Node boxes cache. Array index matches the box ID (for fast O(1) lookups). diff --git a/Source/Engine/Visject/VisjectGraph.h b/Source/Engine/Visject/VisjectGraph.h index 90ba44ed5..3a2e6439d 100644 --- a/Source/Engine/Visject/VisjectGraph.h +++ b/Source/Engine/Visject/VisjectGraph.h @@ -6,13 +6,12 @@ #include "Engine/Core/Math/Vector2.h" #include "Engine/Core/Math/Vector3.h" #include "Engine/Core/Math/Vector4.h" +#include "Engine/Core/Collections/Array.h" #include "Engine/Content/Asset.h" #include "Engine/Content/AssetReference.h" #include "Engine/Content/AssetsContainer.h" #include "Engine/Animations/Curve.h" -#define VISJECT_GRAPH_NODE_MAX_ASSETS 14 - template class VisjectGraphNode; @@ -94,7 +93,7 @@ public: /// /// The asset references. Linked resources such as Animation assets are referenced in graph data as ID. We need to keep valid refs to them at runtime to keep data in memory. /// - AssetReference Assets[VISJECT_GRAPH_NODE_MAX_ASSETS]; + Array> Assets; }; /// @@ -188,11 +187,10 @@ public: #undef SETUP_CURVE // Get Gameplay Global case 16: - { + n->Assets.Resize(1); n->Assets[0] = ::LoadAsset((Guid)n->Values[0], Asset::TypeInitializer); break; } - } } // Base From 7653fba3814855ac24911654aa578155b97ba87a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 19 Apr 2024 16:30:34 +0200 Subject: [PATCH 18/43] Refactor Multi Blend nodes to support up to `255` blend points --- .../Archetypes/Animation.MultiBlend.cs | 104 ++++++++++-------- Source/Editor/Surface/Archetypes/Animation.cs | 30 +---- Source/Editor/Surface/NodeFlags.cs | 5 + Source/Editor/Surface/SurfaceNode.cs | 11 +- .../Surface/Undo/EditNodeValuesAction.cs | 14 ++- .../VisjectSurfaceContext.Serialization.cs | 15 +++ .../Animations/Graph/AnimGraph.Base.cpp | 53 ++++----- Source/Engine/Animations/Graph/AnimGraph.cpp | 22 ++++ Source/Engine/Animations/Graph/AnimGraph.h | 46 +++----- .../Animations/Graph/AnimGroup.Animation.cpp | 18 +-- 10 files changed, 168 insertions(+), 150 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs index 174f867fc..a3095a6bf 100644 --- a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs +++ b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs @@ -23,9 +23,7 @@ namespace FlaxEditor.Surface.Archetypes private readonly bool _is2D; private Float2 _rangeX, _rangeY; private Float2 _debugPos = Float2.Minimum; - private readonly BlendPoint[] _blendPoints = new BlendPoint[Animation.MultiBlend.MaxAnimationsCount]; - private readonly Guid[] _pointsAnims = new Guid[Animation.MultiBlend.MaxAnimationsCount]; - private readonly Float2[] _pointsLocations = new Float2[Animation.MultiBlend.MaxAnimationsCount]; + private readonly List _blendPoints = new List(); /// /// Represents single blend point. @@ -44,6 +42,11 @@ namespace FlaxEditor.Surface.Archetypes /// public const float DefaultSize = 8.0f; + /// + /// Blend point index. + /// + public int Index => _index; + /// /// Initializes a new instance of the class. /// @@ -190,6 +193,11 @@ namespace FlaxEditor.Surface.Archetypes /// public bool Is2D => _is2D; + /// + /// Blend points count. + /// + public int PointsCount => (_node.Values.Length - 4) / 2; // 4 node values + 2 per blend point + /// /// Initializes a new instance of the class. /// @@ -206,28 +214,6 @@ namespace FlaxEditor.Surface.Archetypes _is2D = is2D; } - /// - /// Gets the blend space data. - /// - /// The space range for X axis (X-width, Y-height). - /// The space range for Y axis (X-width, Y-height). - /// The points anims (input array to fill of size equal 14). - /// The points locations (input array to fill of size equal 14). - public void GetData(out Float2 rangeX, out Float2 rangeY, Guid[] pointsAnims, Float2[] pointsLocations) - { - var data0 = (Float4)_node.Values[0]; - rangeX = new Float2(data0.X, data0.Y); - rangeY = _is2D ? new Float2(data0.Z, data0.W) : Float2.Zero; - for (int i = 0; i < Animation.MultiBlend.MaxAnimationsCount; i++) - { - var dataA = (Float4)_node.Values[4 + i * 2]; - var dataB = (Guid)_node.Values[5 + i * 2]; - - pointsAnims[i] = dataB; - pointsLocations[i] = new Float2(Mathf.Clamp(dataA.X, rangeX.X, rangeX.Y), _is2D ? Mathf.Clamp(dataA.Y, rangeY.X, rangeY.Y) : 0.0f); - } - } - private void AddAsset(Float2 location) { // Reuse existing animation @@ -259,16 +245,22 @@ namespace FlaxEditor.Surface.Archetypes public void AddAsset(Guid asset, Float2 location) { // Find the first free slot + var count = PointsCount; + if (count == Animation.MultiBlend.MaxAnimationsCount) + return; var values = (object[])_node.Values.Clone(); var index = 0; - for (; index < Animation.MultiBlend.MaxAnimationsCount; index++) + for (; index < count; index++) { var dataB = (Guid)_node.Values[5 + index * 2]; if (dataB == Guid.Empty) break; } - if (index == Animation.MultiBlend.MaxAnimationsCount) - return; // TODO: unlimited amount of blend points + if (index == count) + { + // Add another blend point + Array.Resize(ref values, values.Length + 2); + } values[4 + index * 2] = new Float4(location.X, _is2D ? location.Y : 0.0f, 0, 1.0f); values[5 + index * 2] = asset; @@ -382,11 +374,22 @@ namespace FlaxEditor.Surface.Archetypes public override void Update(float deltaTime) { // Synchronize blend points collection - GetData(out _rangeX, out _rangeY, _pointsAnims, _pointsLocations); - for (int i = 0; i < Animation.MultiBlend.MaxAnimationsCount; i++) + var data0 = (Float4)_node.Values[0]; + _rangeX = new Float2(data0.X, data0.Y); + _rangeY = _is2D ? new Float2(data0.Z, data0.W) : Float2.Zero; + var count = PointsCount; + while (_blendPoints.Count > count) { - var animId = _pointsAnims[i]; - var location = _pointsLocations[i]; + _blendPoints[count].Dispose(); + _blendPoints.RemoveAt(count); + } + while (_blendPoints.Count < count) + _blendPoints.Add(null); + for (int i = 0; i < count; i++) + { + var animId = (Guid)_node.Values[5 + i * 2]; + var dataA = (Float4)_node.Values[4 + i * 2]; + var location = new Float2(Mathf.Clamp(dataA.X, _rangeX.X, _rangeX.Y), _is2D ? Mathf.Clamp(dataA.Y, _rangeY.X, _rangeY.Y) : 0.0f); if (animId != Guid.Empty) { if (_blendPoints[i] == null) @@ -394,7 +397,6 @@ namespace FlaxEditor.Surface.Archetypes // Create missing blend point _blendPoints[i] = new BlendPoint(this, i) { - Tag = i, Parent = this, }; } @@ -477,10 +479,11 @@ namespace FlaxEditor.Surface.Archetypes var menu = new FlaxEditor.GUI.ContextMenu.ContextMenu(); var b = menu.AddButton("Add point", OnAddPoint); b.Tag = location; + b.Enabled = PointsCount < Animation.MultiBlend.MaxAnimationsCount; if (GetChildAt(location) is BlendPoint blendPoint) { b = menu.AddButton("Remove point", OnRemovePoint); - b.Tag = blendPoint.Tag; + b.Tag = blendPoint.Index; b.TooltipText = blendPoint.TooltipText; } menu.Show(this, location); @@ -538,8 +541,6 @@ namespace FlaxEditor.Surface.Archetypes GetPointsArea(out var pointsArea); var data0 = (Float4)_node.Values[0]; var rangeX = new Float2(data0.X, data0.Y); - var rangeY = _is2D ? new Float2(data0.Z, data0.W) : Float2.One; - var grid = new Float2(Mathf.Abs(rangeX.Y - rangeX.X) * 0.01f, Mathf.Abs(rangeY.X - rangeY.Y) * 0.01f); // Background Render2D.DrawRectangle(rect, IsMouseOver ? style.TextBoxBackgroundSelected : style.TextBoxBackground); @@ -562,6 +563,7 @@ namespace FlaxEditor.Surface.Archetypes } if (_is2D) { + var rangeY = new Float2(data0.Z, data0.W); for (int i = 0; i <= splits; i++) { float alpha = (float)i / splits; @@ -605,6 +607,11 @@ namespace FlaxEditor.Surface.Archetypes /// public abstract class MultiBlend : SurfaceNode { + /// + /// The blend space editor. + /// + protected BlendPointsEditor _editor; + /// /// The selected animation label. /// @@ -638,7 +645,7 @@ namespace FlaxEditor.Surface.Archetypes /// /// The maximum animations amount to blend per node. /// - public const int MaxAnimationsCount = 14; + public const int MaxAnimationsCount = 255; /// /// Gets or sets the index of the selected animation. @@ -661,20 +668,13 @@ namespace FlaxEditor.Surface.Archetypes Text = "Selected Animation:", Parent = this }; - _selectedAnimation = new ComboBox(_selectedAnimationLabel.X, 4 * layoutOffsetY, _selectedAnimationLabel.Width) { Parent = this }; - _selectedAnimation.PopupShowing += OnSelectedAnimationPopupShowing; _selectedAnimation.SelectedIndexChanged += OnSelectedAnimationChanged; - var items = new List(MaxAnimationsCount); - while (items.Count < MaxAnimationsCount) - items.Add(string.Empty); - _selectedAnimation.Items = items; - _animationPicker = new AssetPicker(new ScriptType(typeof(FlaxEngine.Animation)), new Float2(_selectedAnimation.Left, _selectedAnimation.Bottom + 4)) { Parent = this @@ -687,7 +687,6 @@ namespace FlaxEditor.Surface.Archetypes Text = "Speed:", Parent = this }; - _animationSpeed = new FloatValueBox(1.0f, _animationSpeedLabel.Right + 4, _animationSpeedLabel.Y, _selectedAnimation.Right - _animationSpeedLabel.Right - 4) { SlideSpeed = 0.01f, @@ -700,7 +699,8 @@ namespace FlaxEditor.Surface.Archetypes { var items = comboBox.Items; items.Clear(); - for (var i = 0; i < MaxAnimationsCount; i++) + var count = _editor.PointsCount; + for (var i = 0; i < count; i++) { var animId = (Guid)Values[5 + i * 2]; var path = string.Empty; @@ -808,6 +808,16 @@ namespace FlaxEditor.Surface.Archetypes UpdateUI(); } + /// + public override void OnSpawned(SurfaceNodeActions action) + { + base.OnSpawned(action); + + // Select the first animation to make setup easier + OnSelectedAnimationPopupShowing(_selectedAnimation); + _selectedAnimation.SelectedIndex = 0; + } + /// public override void OnValuesChanged() { @@ -825,7 +835,6 @@ namespace FlaxEditor.Surface.Archetypes { private readonly Label _animationXLabel; private readonly FloatValueBox _animationX; - private readonly BlendPointsEditor _editor; /// public MultiBlend1D(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch) @@ -896,7 +905,6 @@ namespace FlaxEditor.Surface.Archetypes private readonly FloatValueBox _animationX; private readonly Label _animationYLabel; private readonly FloatValueBox _animationY; - private readonly BlendPointsEditor _editor; /// public MultiBlend2D(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch) diff --git a/Source/Editor/Surface/Archetypes/Animation.cs b/Source/Editor/Surface/Archetypes/Animation.cs index f00bd670b..1d897c395 100644 --- a/Source/Editor/Surface/Archetypes/Animation.cs +++ b/Source/Editor/Surface/Archetypes/Animation.cs @@ -621,7 +621,7 @@ namespace FlaxEditor.Surface.Archetypes Create = (id, context, arch, groupArch) => new MultiBlend1D(id, context, arch, groupArch), Title = "Multi Blend 1D", Description = "Animation blending in 1D", - Flags = NodeFlags.AnimGraph, + Flags = NodeFlags.AnimGraph | NodeFlags.VariableValuesSize, Size = new Float2(420, 300), DefaultValues = new object[] { @@ -633,19 +633,6 @@ namespace FlaxEditor.Surface.Archetypes // Per blend sample data new Float4(0, 0, 0, 1.0f), Guid.Empty, - new Float4(0, 0, 0, 1.0f), Guid.Empty, - new Float4(0, 0, 0, 1.0f), Guid.Empty, - new Float4(0, 0, 0, 1.0f), Guid.Empty, - new Float4(0, 0, 0, 1.0f), Guid.Empty, - new Float4(0, 0, 0, 1.0f), Guid.Empty, - new Float4(0, 0, 0, 1.0f), Guid.Empty, - new Float4(0, 0, 0, 1.0f), Guid.Empty, - new Float4(0, 0, 0, 1.0f), Guid.Empty, - new Float4(0, 0, 0, 1.0f), Guid.Empty, - new Float4(0, 0, 0, 1.0f), Guid.Empty, - new Float4(0, 0, 0, 1.0f), Guid.Empty, - new Float4(0, 0, 0, 1.0f), Guid.Empty, - new Float4(0, 0, 0, 1.0f), Guid.Empty, }, Elements = new[] { @@ -670,7 +657,7 @@ namespace FlaxEditor.Surface.Archetypes Create = (id, context, arch, groupArch) => new MultiBlend2D(id, context, arch, groupArch), Title = "Multi Blend 2D", Description = "Animation blending in 2D", - Flags = NodeFlags.AnimGraph, + Flags = NodeFlags.AnimGraph | NodeFlags.VariableValuesSize, Size = new Float2(420, 620), DefaultValues = new object[] { @@ -682,19 +669,6 @@ namespace FlaxEditor.Surface.Archetypes // Per blend sample data new Float4(0, 0, 0, 1.0f), Guid.Empty, - new Float4(0, 0, 0, 1.0f), Guid.Empty, - new Float4(0, 0, 0, 1.0f), Guid.Empty, - new Float4(0, 0, 0, 1.0f), Guid.Empty, - new Float4(0, 0, 0, 1.0f), Guid.Empty, - new Float4(0, 0, 0, 1.0f), Guid.Empty, - new Float4(0, 0, 0, 1.0f), Guid.Empty, - new Float4(0, 0, 0, 1.0f), Guid.Empty, - new Float4(0, 0, 0, 1.0f), Guid.Empty, - new Float4(0, 0, 0, 1.0f), Guid.Empty, - new Float4(0, 0, 0, 1.0f), Guid.Empty, - new Float4(0, 0, 0, 1.0f), Guid.Empty, - new Float4(0, 0, 0, 1.0f), Guid.Empty, - new Float4(0, 0, 0, 1.0f), Guid.Empty, }, Elements = new[] { diff --git a/Source/Editor/Surface/NodeFlags.cs b/Source/Editor/Surface/NodeFlags.cs index 2c02489ee..d3b469772 100644 --- a/Source/Editor/Surface/NodeFlags.cs +++ b/Source/Editor/Surface/NodeFlags.cs @@ -73,6 +73,11 @@ namespace FlaxEditor.Surface /// BehaviorTreeGraph = 1024, + /// + /// Node can have different amount of items in values array. + /// + VariableValuesSize = 2048, + /// /// Node can be used in the all visual graphs. /// diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index fdb323de9..ac5680d8c 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -951,15 +951,20 @@ namespace FlaxEditor.Surface { if (_isDuringValuesEditing || !Surface.CanEdit) return; - - if (values == null || Values == null || values.Length != Values.Length) + if (values == null || Values == null) + throw new ArgumentException(); + bool resize = values.Length != Values.Length; + if (resize && (Archetype.Flags & NodeFlags.VariableValuesSize) == 0) throw new ArgumentException(); _isDuringValuesEditing = true; var before = Surface.Undo != null ? (object[])Values.Clone() : null; - Array.Copy(values, Values, values.Length); + if (resize) + Values = (object[])values.Clone(); + else + Array.Copy(values, Values, values.Length); OnValuesChanged(); Surface.MarkAsEdited(graphEdited); diff --git a/Source/Editor/Surface/Undo/EditNodeValuesAction.cs b/Source/Editor/Surface/Undo/EditNodeValuesAction.cs index 24dd686f2..8425fd65f 100644 --- a/Source/Editor/Surface/Undo/EditNodeValuesAction.cs +++ b/Source/Editor/Surface/Undo/EditNodeValuesAction.cs @@ -14,6 +14,7 @@ namespace FlaxEditor.Surface.Undo private ContextHandle _context; private readonly uint _nodeId; private readonly bool _graphEdited; + private readonly bool _resize; private object[] _before; private object[] _after; @@ -23,7 +24,8 @@ namespace FlaxEditor.Surface.Undo throw new ArgumentNullException(nameof(before)); if (node?.Values == null) throw new ArgumentNullException(nameof(node)); - if (before.Length != node.Values.Length) + _resize = before.Length != node.Values.Length; + if (_resize && (node.Archetype.Flags & NodeFlags.VariableValuesSize) == 0) throw new ArgumentException(nameof(before)); _surface = node.Surface; @@ -48,7 +50,10 @@ namespace FlaxEditor.Surface.Undo throw new Exception("Missing node."); node.SetIsDuringValuesEditing(true); - Array.Copy(_after, node.Values, _after.Length); + if (_resize) + node.Values = (object[])_after.Clone(); + else + Array.Copy(_after, node.Values, _after.Length); node.OnValuesChanged(); context.MarkAsModified(_graphEdited); node.SetIsDuringValuesEditing(false); @@ -65,7 +70,10 @@ namespace FlaxEditor.Surface.Undo throw new Exception("Missing node."); node.SetIsDuringValuesEditing(true); - Array.Copy(_before, node.Values, _before.Length); + if (_resize) + node.Values = (object[])_before.Clone(); + else + Array.Copy(_before, node.Values, _before.Length); node.OnValuesChanged(); context.MarkAsModified(_graphEdited); node.SetIsDuringValuesEditing(false); diff --git a/Source/Editor/Surface/VisjectSurfaceContext.Serialization.cs b/Source/Editor/Surface/VisjectSurfaceContext.Serialization.cs index 02aaa4c6d..09147efac 100644 --- a/Source/Editor/Surface/VisjectSurfaceContext.Serialization.cs +++ b/Source/Editor/Surface/VisjectSurfaceContext.Serialization.cs @@ -630,6 +630,15 @@ namespace FlaxEditor.Surface stream.ReadCommonValue(ref node.Values[j]); } } + else if ((node.Archetype.Flags & NodeFlags.VariableValuesSize) != 0) + { + node.Values = new object[valuesCnt]; + for (int j = firstValueReadIdx; j < valuesCnt; j++) + { + // ReSharper disable once PossibleNullReferenceException + stream.ReadCommonValue(ref node.Values[j]); + } + } else { Editor.LogWarning(string.Format("Invalid node values. Loaded: {0}, expected: {1}. Type: {2}, {3}", valuesCnt, nodeValuesCnt, node.Archetype.Title, node.Archetype.TypeID)); @@ -795,6 +804,12 @@ namespace FlaxEditor.Surface for (int j = firstValueReadIdx; j < valuesCnt; j++) node.Values[j] = stream.ReadVariant(); } + else if ((node.Archetype.Flags & NodeFlags.VariableValuesSize) != 0) + { + node.Values = new object[valuesCnt]; + for (int j = firstValueReadIdx; j < valuesCnt; j++) + node.Values[j] = stream.ReadVariant(); + } else { Editor.LogWarning(string.Format("Invalid node values. Loaded: {0}, expected: {1}. Type: {2}, {3}", valuesCnt, nodeValuesCnt, node.Archetype.Title, node.Archetype.TypeID)); diff --git a/Source/Engine/Animations/Graph/AnimGraph.Base.cpp b/Source/Engine/Animations/Graph/AnimGraph.Base.cpp index ea887b056..55241fc70 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.Base.cpp +++ b/Source/Engine/Animations/Graph/AnimGraph.Base.cpp @@ -117,11 +117,11 @@ void InstanceDataBucketInit(AnimGraphInstanceData::Bucket& bucket) bucket.InstanceData.Init = true; } -bool SortMultiBlend1D(const byte& a, const byte& b, AnimGraphNode* n) +bool SortMultiBlend1D(const ANIM_GRAPH_MULTI_BLEND_INDEX& a, const ANIM_GRAPH_MULTI_BLEND_INDEX& b, AnimGraphNode* n) { // Sort items by X location from the lowest to the highest - const auto aX = a == ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS ? MAX_float : n->Values[4 + a * 2].AsFloat4().X; - const auto bX = b == ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS ? MAX_float : n->Values[4 + b * 2].AsFloat4().X; + const auto aX = a == ANIM_GRAPH_MULTI_BLEND_INVALID ? MAX_float : n->Values[4 + a * 2].AsFloat4().X; + const auto bX = b == ANIM_GRAPH_MULTI_BLEND_INVALID ? MAX_float : n->Values[4 + b * 2].AsFloat4().X; return aX < bX; } @@ -133,8 +133,6 @@ bool SortMultiBlend1D(const byte& a, const byte& b, AnimGraphNode* n) bool AnimGraphBase::onNodeLoaded(Node* n) { ((AnimGraphNode*)n)->Graph = _graph; - - // Check if this node needs a state container switch (n->GroupID) { // Tools @@ -173,45 +171,41 @@ bool AnimGraphBase::onNodeLoaded(Node* n) break; // Multi Blend 1D case 12: - { ADD_BUCKET(MultiBlendBucketInit); + n->Data.MultiBlend1D.Count = (ANIM_GRAPH_MULTI_BLEND_INDEX)((n->Values.Count() - 4) / 2); // 4 node values + 2 per blend point n->Data.MultiBlend1D.Length = -1; - n->Assets.Resize(ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS); - for (int32 i = 0; i < ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS; i++) + n->Data.MultiBlend1D.IndicesSorted = (ANIM_GRAPH_MULTI_BLEND_INDEX*)Allocator::Allocate(sizeof(ANIM_GRAPH_MULTI_BLEND_INDEX) * n->Data.MultiBlend1D.Count); + n->Assets.Resize(n->Data.MultiBlend1D.Count); + for (int32 i = 0; i < n->Data.MultiBlend1D.Count; i++) { n->Assets[i] = Content::LoadAsync((Guid)n->Values[i * 2 + 5]); - n->Data.MultiBlend1D.IndicesSorted[i] = n->Assets[i] ? i : ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS; + n->Data.MultiBlend1D.IndicesSorted[i] = (ANIM_GRAPH_MULTI_BLEND_INDEX)(n->Assets[i] ? i : ANIM_GRAPH_MULTI_BLEND_INVALID); } - Sorting::SortArray(n->Data.MultiBlend1D.IndicesSorted, ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS, &SortMultiBlend1D, n); + Sorting::SortArray(n->Data.MultiBlend1D.IndicesSorted, n->Data.MultiBlend1D.Count, &SortMultiBlend1D, n); break; - } // Multi Blend 2D case 13: { ADD_BUCKET(MultiBlendBucketInit); + n->Data.MultiBlend1D.Count = (ANIM_GRAPH_MULTI_BLEND_INDEX)((n->Values.Count() - 4) / 2); // 4 node values + 2 per blend point n->Data.MultiBlend2D.Length = -1; // Get blend points locations - Array> vertices; - byte vertexIndexToAnimIndex[ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS]; - n->Assets.Resize(ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS); - for (int32 i = 0; i < ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS; i++) + Array vertices; + Array vertexToAnim; + n->Assets.Resize(n->Data.MultiBlend1D.Count); + for (int32 i = 0; i < n->Data.MultiBlend1D.Count; i++) { - n->Assets[i] = (Asset*)Content::LoadAsync((Guid)n->Values[i * 2 + 5]); + n->Assets[i] = Content::LoadAsync((Guid)n->Values[i * 2 + 5]); if (n->Assets[i]) { - const int32 vertexIndex = vertices.Count(); - vertexIndexToAnimIndex[vertexIndex] = i; vertices.Add(Float2(n->Values[i * 2 + 4].AsFloat4())); - } - else - { - vertexIndexToAnimIndex[i] = -1; + vertexToAnim.Add((ANIM_GRAPH_MULTI_BLEND_INDEX)i); } } // Triangulate - Array> triangles; + Array triangles; Delaunay2D::Triangulate(vertices, triangles); if (triangles.Count() == 0) { @@ -229,15 +223,14 @@ bool AnimGraphBase::onNodeLoaded(Node* n) } // Store triangles vertices indices (map the back to the anim node slots) - for (int32 i = 0; i < triangles.Count(); i++) + n->Data.MultiBlend2D.TrianglesCount = triangles.Count(); + n->Data.MultiBlend2D.Triangles = (ANIM_GRAPH_MULTI_BLEND_INDEX*)Allocator::Allocate(triangles.Count() * 3 - sizeof(ANIM_GRAPH_MULTI_BLEND_INDEX)); + for (int32 i = 0, t = 0; i < triangles.Count(); i++) { - n->Data.MultiBlend2D.TrianglesP0[i] = vertexIndexToAnimIndex[triangles[i].Indices[0]]; - n->Data.MultiBlend2D.TrianglesP1[i] = vertexIndexToAnimIndex[triangles[i].Indices[1]]; - n->Data.MultiBlend2D.TrianglesP2[i] = vertexIndexToAnimIndex[triangles[i].Indices[2]]; + n->Data.MultiBlend2D.Triangles[t++] = vertexToAnim[triangles[i].Indices[0]]; + n->Data.MultiBlend2D.Triangles[t++] = vertexToAnim[triangles[i].Indices[1]]; + n->Data.MultiBlend2D.Triangles[t++] = vertexToAnim[triangles[i].Indices[2]]; } - if (triangles.Count() < ANIM_GRAPH_MULTI_BLEND_2D_MAX_TRIS) - n->Data.MultiBlend2D.TrianglesP0[triangles.Count()] = ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS; - break; } // Blend Pose diff --git a/Source/Engine/Animations/Graph/AnimGraph.cpp b/Source/Engine/Animations/Graph/AnimGraph.cpp index defd2b03d..321f10604 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.cpp +++ b/Source/Engine/Animations/Graph/AnimGraph.cpp @@ -102,6 +102,28 @@ AnimGraphInstanceData::OutgoingEvent AnimGraphInstanceData::ActiveEvent::End(Ani return out; } +AnimGraphNode::~AnimGraphNode() +{ + // Free allocated memory + switch (GroupID) + { + // Animation + case 9: + switch (TypeID) + { + // Multi Blend 1D + case 12: + Allocator::Free(Data.MultiBlend1D.IndicesSorted); + break; + // Multi Blend 2D + case 13: + Allocator::Free(Data.MultiBlend2D.Triangles); + break; + } + break; + } +} + AnimGraphImpulse* AnimGraphNode::GetNodes(AnimGraphExecutor* executor) { auto& context = *AnimGraphExecutor::Context.Get(); diff --git a/Source/Engine/Animations/Graph/AnimGraph.h b/Source/Engine/Animations/Graph/AnimGraph.h index ef72c2d72..f34b7900f 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.h +++ b/Source/Engine/Animations/Graph/AnimGraph.h @@ -13,8 +13,8 @@ #define ANIM_GRAPH_PARAM_BASE_MODEL_ID Guid(1000, 0, 0, 0) #define ANIM_GRAPH_IS_VALID_PTR(value) (value.Type.Type == VariantType::Pointer && value.AsPointer != nullptr) -#define ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS 14 -#define ANIM_GRAPH_MULTI_BLEND_2D_MAX_TRIS 32 +#define ANIM_GRAPH_MULTI_BLEND_INDEX byte +#define ANIM_GRAPH_MULTI_BLEND_INVALID 0xff #define ANIM_GRAPH_MAX_STATE_TRANSITIONS 64 #define ANIM_GRAPH_MAX_CALL_STACK 100 #define ANIM_GRAPH_MAX_EVENTS 64 @@ -434,38 +434,24 @@ class AnimGraphNode : public VisjectGraphNode public: struct MultiBlend1DData { - /// - /// The computed length of the mixes animations. Shared for all blend points to provide more stabilization during looped playback. - /// + // Amount of blend points. + ANIM_GRAPH_MULTI_BLEND_INDEX Count; + // The computed length of the mixes animations. Shared for all blend points to provide more stabilization during looped playback. float Length; - - /// - /// The indices of the animations to blend. Sorted from the lowest X to the highest X. Contains only valid used animations. Unused items are using index ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS which is invalid. - /// - byte IndicesSorted[ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS]; + // The indices of the animations to blend. Sorted from the lowest X to the highest X. Contains only valid used animations. Unused items are using index ANIM_GRAPH_MULTI_BLEND_INVALID which is invalid. + ANIM_GRAPH_MULTI_BLEND_INDEX* IndicesSorted; }; struct MultiBlend2DData { - /// - /// The computed length of the mixes animations. Shared for all blend points to provide more stabilization during looped playback. - /// + // Amount of blend points. + ANIM_GRAPH_MULTI_BLEND_INDEX Count; + // The computed length of the mixes animations. Shared for all blend points to provide more stabilization during looped playback. float Length; - - /// - /// Cached triangles vertices (vertex 0). Contains list of indices for triangles to use for blending. - /// - byte TrianglesP0[ANIM_GRAPH_MULTI_BLEND_2D_MAX_TRIS]; - - /// - /// Cached triangles vertices (vertex 1). Contains list of indices for triangles to use for blending. - /// - byte TrianglesP1[ANIM_GRAPH_MULTI_BLEND_2D_MAX_TRIS]; - - /// - /// Cached triangles vertices (vertex 2). Contains list of indices for triangles to use for blending. - /// - byte TrianglesP2[ANIM_GRAPH_MULTI_BLEND_2D_MAX_TRIS]; + // Amount of triangles. + int32 TrianglesCount; + // Cached triangles vertices (3 bytes per triangle). Contains list of indices for triangles to use for blending. + ANIM_GRAPH_MULTI_BLEND_INDEX* Triangles; }; struct StateMachineData @@ -582,6 +568,8 @@ public: { } + ~AnimGraphNode(); + public: /// /// Gets the per-node node transformations cache (cached). @@ -784,7 +772,7 @@ struct AnimGraphContext bool StackOverFlow; Array> CallStack; Array> GraphStack; - Array > NodePath; + Array> NodePath; Dictionary Functions; ChunkedArray PoseCache; int32 PoseCacheSize; diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp index fbb61c4a2..c85d83603 100644 --- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp +++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp @@ -1269,7 +1269,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu auto& data = node->Data.MultiBlend1D; // Check if not valid animation binded - if (data.IndicesSorted[0] == ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS) + if (data.Count == 0) break; // Get axis X @@ -1300,7 +1300,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu ANIM_GRAPH_PROFILE_EVENT("Multi Blend 1D"); // Find 2 animations to blend (line) - for (int32 i = 0; i < ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS - 1; i++) + for (int32 i = 0; i < data.Count - 1; i++) { const auto a = data.IndicesSorted[i]; const auto b = data.IndicesSorted[i + 1]; @@ -1310,14 +1310,14 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu auto aData = node->Values[4 + a * 2].AsFloat4(); // Check single A case or the last valid animation - if (x <= aData.X + ANIM_GRAPH_BLEND_THRESHOLD || b == ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS) + if (x <= aData.X + ANIM_GRAPH_BLEND_THRESHOLD || b == ANIM_GRAPH_MULTI_BLEND_INVALID) { value = SampleAnimation(node, loop, data.Length, startTimePos, bucket.TimePosition, newTimePos, aAnim, aData.W); break; } // Get B animation data - ASSERT(b != ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS); + ASSERT(b != ANIM_GRAPH_MULTI_BLEND_INVALID); const auto bAnim = node->Assets[b].As(); auto bData = node->Values[4 + b * 2].AsFloat4(); @@ -1365,7 +1365,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu auto& data = node->Data.MultiBlend2D; // Check if not valid animation binded - if (data.TrianglesP0[0] == ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS) + if (data.TrianglesCount == 0) break; // Get axis X @@ -1406,20 +1406,20 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu Float2 bestPoint; float bestWeight = 0.0f; byte bestAnims[2]; - for (int32 i = 0; i < ANIM_GRAPH_MULTI_BLEND_2D_MAX_TRIS && data.TrianglesP0[i] != ANIM_GRAPH_MULTI_BLEND_MAX_ANIMS; i++) + for (int32 i = 0, t = 0; i < data.TrianglesCount; i++) { // Get A animation data - const auto a = data.TrianglesP0[i]; + const auto a = data.Triangles[t++]; const auto aAnim = node->Assets[a].As(); const auto aData = node->Values[4 + a * 2].AsFloat4(); // Get B animation data - const auto b = data.TrianglesP1[i]; + const auto b = data.Triangles[t++]; const auto bAnim = node->Assets[b].As(); const auto bData = node->Values[4 + b * 2].AsFloat4(); // Get C animation data - const auto c = data.TrianglesP2[i]; + const auto c = data.Triangles[t++]; const auto cAnim = node->Assets[c].As(); const auto cData = node->Values[4 + c * 2].AsFloat4(); From 93e26afa053e048499c661cf67ec64bfa210ddee Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 19 Apr 2024 16:31:11 +0200 Subject: [PATCH 19/43] Optimize Anim Graph node size and remove limit of max `64` state transitions --- .../Engine/Animations/Graph/AnimGraph.Base.cpp | 17 +++++++---------- Source/Engine/Animations/Graph/AnimGraph.cpp | 6 ++++++ Source/Engine/Animations/Graph/AnimGraph.h | 11 +++-------- .../Animations/Graph/AnimGroup.Animation.cpp | 2 +- 4 files changed, 17 insertions(+), 19 deletions(-) diff --git a/Source/Engine/Animations/Graph/AnimGraph.Base.cpp b/Source/Engine/Animations/Graph/AnimGraph.Base.cpp index 55241fc70..204026724 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.Base.cpp +++ b/Source/Engine/Animations/Graph/AnimGraph.Base.cpp @@ -380,6 +380,7 @@ bool AnimGraphBase::onNodeLoaded(Node* n) void AnimGraphBase::LoadStateTransitions(AnimGraphNode::StateBaseData& data, Value& transitionsData) { int32 validTransitions = 0; + data.Transitions = nullptr; if (transitionsData.Type == VariantType::Blob && transitionsData.AsBlob.Length) { MemoryReadStream stream((byte*)transitionsData.AsBlob.Data, transitionsData.AsBlob.Length); @@ -394,8 +395,11 @@ void AnimGraphBase::LoadStateTransitions(AnimGraphNode::StateBaseData& data, Val int32 transitionsCount; stream.ReadInt32(&transitionsCount); + if (transitionsCount == 0) + return; StateTransitions.EnsureCapacity(StateTransitions.Count() + transitionsCount); + data.Transitions = (uint16*)Allocator::Allocate(transitionsCount * sizeof(uint16)); AnimGraphStateTransition transition; for (int32 i = 0; i < transitionsCount; i++) @@ -430,7 +434,6 @@ void AnimGraphBase::LoadStateTransitions(AnimGraphNode::StateBaseData& data, Val // Skip disabled transitions continue; } - if (ruleSize != 0) { transition.RuleGraph = LoadSubGraph(ruleBytes, ruleSize, TEXT("Rule")); @@ -440,25 +443,19 @@ void AnimGraphBase::LoadStateTransitions(AnimGraphNode::StateBaseData& data, Val continue; } } - if (transition.Destination == nullptr) { LOG(Warning, "Missing target node for the state machine transition."); continue; } - if (validTransitions == ANIM_GRAPH_MAX_STATE_TRANSITIONS) - { - LOG(Warning, "State uses too many transitions."); - continue; - } - data.Transitions[validTransitions++] = (uint16)StateTransitions.Count(); StateTransitions.Add(transition); } - } - if (validTransitions != ANIM_GRAPH_MAX_STATE_TRANSITIONS) + + // Last entry is invalid to indicate end data.Transitions[validTransitions] = AnimGraphNode::StateData::InvalidTransitionIndex; + } // Release data to don't use that memory transitionsData = AnimGraphExecutor::Value::Null; diff --git a/Source/Engine/Animations/Graph/AnimGraph.cpp b/Source/Engine/Animations/Graph/AnimGraph.cpp index 321f10604..18013b169 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.cpp +++ b/Source/Engine/Animations/Graph/AnimGraph.cpp @@ -119,6 +119,12 @@ AnimGraphNode::~AnimGraphNode() case 13: Allocator::Free(Data.MultiBlend2D.Triangles); break; + // State + case 20: + // Any State + case 34: + Allocator::Free(Data.State.Transitions); + break; } break; } diff --git a/Source/Engine/Animations/Graph/AnimGraph.h b/Source/Engine/Animations/Graph/AnimGraph.h index f34b7900f..ff8d9b977 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.h +++ b/Source/Engine/Animations/Graph/AnimGraph.h @@ -15,7 +15,6 @@ #define ANIM_GRAPH_IS_VALID_PTR(value) (value.Type.Type == VariantType::Pointer && value.AsPointer != nullptr) #define ANIM_GRAPH_MULTI_BLEND_INDEX byte #define ANIM_GRAPH_MULTI_BLEND_INVALID 0xff -#define ANIM_GRAPH_MAX_STATE_TRANSITIONS 64 #define ANIM_GRAPH_MAX_CALL_STACK 100 #define ANIM_GRAPH_MAX_EVENTS 64 @@ -464,15 +463,11 @@ public: struct StateBaseData { - /// - /// The invalid transition valid used in Transitions to indicate invalid transition linkage. - /// + // The invalid transition valid used in Transitions to indicate invalid transition linkage. const static uint16 InvalidTransitionIndex = MAX_uint16; - /// - /// The outgoing transitions from this state to the other states. Each array item contains index of the transition data from the state node graph transitions cache. Value InvalidTransitionIndex is used for last transition to indicate the transitions amount. - /// - uint16 Transitions[ANIM_GRAPH_MAX_STATE_TRANSITIONS]; + // The outgoing transitions from this state to the other states. Each array item contains index of the transition data from the state node graph transitions cache. Value InvalidTransitionIndex is used for last transition to indicate the transitions amount. + uint16* Transitions; }; struct StateData : StateBaseData diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp index c85d83603..75d21c99c 100644 --- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp +++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp @@ -585,7 +585,7 @@ AnimGraphStateTransition* AnimGraphExecutor::UpdateStateTransitions(AnimGraphCon AnimGraphStateTransition* AnimGraphExecutor::UpdateStateTransitions(AnimGraphContext& context, const AnimGraphNode::StateMachineData& stateMachineData, const AnimGraphNode::StateBaseData& stateData, AnimGraphNode* state, AnimGraphNode* ignoreState) { int32 transitionIndex = 0; - while (transitionIndex < ANIM_GRAPH_MAX_STATE_TRANSITIONS && stateData.Transitions[transitionIndex] != AnimGraphNode::StateData::InvalidTransitionIndex) + while (stateData.Transitions && stateData.Transitions[transitionIndex] != AnimGraphNode::StateData::InvalidTransitionIndex) { const uint16 idx = stateData.Transitions[transitionIndex]; ASSERT(idx < stateMachineData.Graph->StateTransitions.Count()); From 560cf651214a18f66a32e2705782ac7125cfacd4 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 19 Apr 2024 17:50:14 +0200 Subject: [PATCH 20/43] Add add/remove buttons to Multi Blend --- Source/Editor/GUI/ComboBox.cs | 2 +- .../Archetypes/Animation.MultiBlend.cs | 64 ++++++++++++++++--- 2 files changed, 56 insertions(+), 10 deletions(-) diff --git a/Source/Editor/GUI/ComboBox.cs b/Source/Editor/GUI/ComboBox.cs index bb767040d..a7058ebc8 100644 --- a/Source/Editor/GUI/ComboBox.cs +++ b/Source/Editor/GUI/ComboBox.cs @@ -554,7 +554,7 @@ namespace FlaxEditor.GUI // Check if has selected item if (_selectedIndices != null && _selectedIndices.Count > 0) { - string text = _selectedIndices.Count == 1 ? _items[_selectedIndices[0]] : "Multiple Values"; + string text = _selectedIndices.Count == 1 ? (_selectedIndices[0] >= 0 && _selectedIndices[0] < _items.Count ? _items[_selectedIndices[0]] : "") : "Multiple Values"; // Draw text of the selected item float textScale = Height / DefaultHeight; diff --git a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs index a3095a6bf..80419ba90 100644 --- a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs +++ b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs @@ -132,7 +132,7 @@ namespace FlaxEditor.Surface.Archetypes /// public override void OnMouseMove(Float2 location) { - if (_isMouseDown) + if (_isMouseDown && (_mouseMoved || Float2.DistanceSquared(location, _mousePosOffset) > 16.0f)) { if (!_mouseMoved) { @@ -214,11 +214,19 @@ namespace FlaxEditor.Surface.Archetypes _is2D = is2D; } - private void AddAsset(Float2 location) + internal void AddPoint() + { + // Add random point within range + var rand = new Float2(Mathf.Frac((float)Platform.TimeSeconds), (Platform.TimeCycles % 10000) / 10000.0f); + AddPoint(Float2.Lerp(new Float2(_rangeX.X, _rangeY.X), new Float2(_rangeX.Y, _rangeY.Y), rand)); + } + + private void AddPoint(Float2 location) { // Reuse existing animation + var count = PointsCount; Guid id = Guid.Empty; - for (int i = 0; i < Animation.MultiBlend.MaxAnimationsCount; i++) + for (int i = 0; i < count; i++) { id = (Guid)_node.Values[5 + i * 2]; if (id != Guid.Empty) @@ -234,7 +242,7 @@ namespace FlaxEditor.Surface.Archetypes return; } - AddAsset(id, location); + AddPoint(id, location); } /// @@ -242,7 +250,7 @@ namespace FlaxEditor.Surface.Archetypes /// /// The asset. /// The location. - public void AddAsset(Guid asset, Float2 location) + public void AddPoint(Guid asset, Float2 location) { // Find the first free slot var count = PointsCount; @@ -266,7 +274,8 @@ namespace FlaxEditor.Surface.Archetypes values[5 + index * 2] = asset; _node.SetValues(values); - _node.UpdateUI(); + // Auto-select + _node.SelectedAnimationIndex = index; } /// @@ -494,7 +503,7 @@ namespace FlaxEditor.Surface.Archetypes private void OnAddPoint(FlaxEditor.GUI.ContextMenu.ContextMenuButton b) { - AddAsset(BlendPointPosToBlendSpacePos((Float2)b.Tag)); + AddPoint(BlendPointPosToBlendSpacePos((Float2)b.Tag)); } private void OnRemovePoint(FlaxEditor.GUI.ContextMenu.ContextMenuButton b) @@ -607,6 +616,9 @@ namespace FlaxEditor.Surface.Archetypes /// public abstract class MultiBlend : SurfaceNode { + private Button _addButton; + private Button _removeButton; + /// /// The blend space editor. /// @@ -653,7 +665,12 @@ namespace FlaxEditor.Surface.Archetypes public int SelectedAnimationIndex { get => _selectedAnimation.SelectedIndex; - set => _selectedAnimation.SelectedIndex = value; + set + { + OnSelectedAnimationPopupShowing(_selectedAnimation); + _selectedAnimation.SelectedIndex = value; + UpdateUI(); + } } /// @@ -670,6 +687,7 @@ namespace FlaxEditor.Surface.Archetypes }; _selectedAnimation = new ComboBox(_selectedAnimationLabel.X, 4 * layoutOffsetY, _selectedAnimationLabel.Width) { + TooltipText = "Select blend point to view and edit it", Parent = this }; _selectedAnimation.PopupShowing += OnSelectedAnimationPopupShowing; @@ -693,6 +711,22 @@ namespace FlaxEditor.Surface.Archetypes Parent = this }; _animationSpeed.ValueChanged += OnAnimationSpeedValueChanged; + + var buttonsSize = 12; + _addButton = new Button(_selectedAnimation.Right - buttonsSize, _selectedAnimation.Bottom + 4, buttonsSize, buttonsSize) + { + Text = "+", + TooltipText = "Add a new blend point", + Parent = this + }; + _addButton.Clicked += OnAddButtonClicked; + _removeButton = new Button(_addButton.Left - buttonsSize - 4, _addButton.Y, buttonsSize, buttonsSize) + { + Text = "-", + TooltipText = "Remove selected blend point", + Parent = this + }; + _removeButton.Clicked += OnRemoveButtonClicked; } private void OnSelectedAnimationPopupShowing(ComboBox comboBox) @@ -743,6 +777,16 @@ namespace FlaxEditor.Surface.Archetypes } } + private void OnAddButtonClicked() + { + _editor.AddPoint(); + } + + private void OnRemoveButtonClicked() + { + _editor.SetAsset(SelectedAnimationIndex, Guid.Empty); + } + /// /// Updates the editor UI. /// @@ -770,6 +814,8 @@ namespace FlaxEditor.Surface.Archetypes _animationPicker.Enabled = isValid; _animationSpeedLabel.Enabled = isValid; _animationSpeed.Enabled = isValid; + _addButton.Enabled = _editor.PointsCount < MaxAnimationsCount; + _removeButton.Enabled = isValid && data1 != Guid.Empty; } /// @@ -782,7 +828,7 @@ namespace FlaxEditor.Surface.Archetypes _isUpdatingUI = true; var selectedIndex = _selectedAnimation.SelectedIndex; - var isValid = selectedIndex != -1; + var isValid = selectedIndex >= 0 && selectedIndex < _editor.PointsCount; Float4 data0; Guid data1; if (isValid) From 41ffc16b66d756e82b160b1803a97512e0c3f0f2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 20 Apr 2024 15:01:27 +0200 Subject: [PATCH 21/43] Fix `FindRandomPointAroundCircle` to always find a valid point on a NavMesh in the radius #2398 --- Source/Engine/Navigation/NavMeshBuilder.cpp | 1 + Source/Engine/Navigation/NavMeshData.cpp | 13 +-- Source/Engine/Navigation/NavMeshRuntime.cpp | 87 +++++-------------- Source/Engine/Navigation/NavMeshRuntime.h | 2 +- .../recastnavigation/DetourNavMeshQuery.cpp | 27 +++++- 5 files changed, 54 insertions(+), 76 deletions(-) diff --git a/Source/Engine/Navigation/NavMeshBuilder.cpp b/Source/Engine/Navigation/NavMeshBuilder.cpp index eedd71158..5bc5141fa 100644 --- a/Source/Engine/Navigation/NavMeshBuilder.cpp +++ b/Source/Engine/Navigation/NavMeshBuilder.cpp @@ -628,6 +628,7 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B LOG(Warning, "Could not build Detour navmesh."); return true; } + ASSERT_LOW_LAYER(navDataSize > 4 && *(uint32*)navData == DT_NAVMESH_MAGIC); // Sanity check for Detour header { PROFILE_CPU_NAMED("Navigation.CreateTile"); diff --git a/Source/Engine/Navigation/NavMeshData.cpp b/Source/Engine/Navigation/NavMeshData.cpp index 16227ce98..c6faccc1e 100644 --- a/Source/Engine/Navigation/NavMeshData.cpp +++ b/Source/Engine/Navigation/NavMeshData.cpp @@ -17,7 +17,7 @@ void NavMeshData::Save(WriteStream& stream) // Write tiles for (int32 tileIndex = 0; tileIndex < Tiles.Count(); tileIndex++) { - auto& tile = Tiles[tileIndex]; + auto& tile = Tiles.Get()[tileIndex]; // Write tile header NavMeshTileDataHeader tileHeader; @@ -41,7 +41,6 @@ void NavMeshData::Save(WriteStream& stream) bool NavMeshData::Load(BytesContainer& data, bool copyData) { - // No data if (data.Length() < sizeof(NavMeshDataHeader)) { LOG(Warning, "No valid navmesh data."); @@ -50,7 +49,7 @@ bool NavMeshData::Load(BytesContainer& data, bool copyData) MemoryReadStream stream(data.Get(), data.Length()); // Read header - const auto header = stream.Move(1); + const auto header = stream.Move(); if (header->Version != 1) { LOG(Warning, "Invalid valid navmesh data version {0}.", header->Version); @@ -67,10 +66,10 @@ bool NavMeshData::Load(BytesContainer& data, bool copyData) // Read tiles for (int32 tileIndex = 0; tileIndex < Tiles.Count(); tileIndex++) { - auto& tile = Tiles[tileIndex]; + auto& tile = Tiles.Get()[tileIndex]; // Read tile header - const auto tileHeader = stream.Move(1); + const auto tileHeader = stream.Move(); if (tileHeader->DataSize <= 0) { LOG(Warning, "Invalid navmesh tile data."); @@ -83,13 +82,9 @@ bool NavMeshData::Load(BytesContainer& data, bool copyData) // Read tile data const auto tileData = stream.Move(tileHeader->DataSize); if (copyData) - { tile.Data.Copy(tileData, tileHeader->DataSize); - } else - { tile.Data.Link(tileData, tileHeader->DataSize); - } } return false; diff --git a/Source/Engine/Navigation/NavMeshRuntime.cpp b/Source/Engine/Navigation/NavMeshRuntime.cpp index 0e6ae0a0e..c4576aa53 100644 --- a/Source/Engine/Navigation/NavMeshRuntime.cpp +++ b/Source/Engine/Navigation/NavMeshRuntime.cpp @@ -60,17 +60,12 @@ bool NavMeshRuntime::FindDistanceToWall(const Vector3& startPosition, NavMeshHit Float3::Transform(startPosition, Properties.Rotation, startPositionNavMesh); dtPolyRef startPoly = 0; - query->findNearestPoly(&startPositionNavMesh.X, &extent.X, &filter, &startPoly, nullptr); - if (!startPoly) - { + if (!dtStatusSucceed(query->findNearestPoly(&startPositionNavMesh.X, &extent.X, &filter, &startPoly, nullptr))) return false; - } Float3 hitPosition, hitNormal; if (!dtStatusSucceed(query->findDistanceToWall(startPoly, &startPositionNavMesh.X, maxDistance, &filter, &hitInfo.Distance, &hitPosition.X, &hitNormal.X))) - { return false; - } Quaternion invRotation; Quaternion::Invert(Properties.Rotation, invRotation); @@ -98,17 +93,11 @@ bool NavMeshRuntime::FindPath(const Vector3& startPosition, const Vector3& endPo Float3::Transform(endPosition, Properties.Rotation, endPositionNavMesh); dtPolyRef startPoly = 0; - query->findNearestPoly(&startPositionNavMesh.X, &extent.X, &filter, &startPoly, nullptr); - if (!startPoly) - { + if (!dtStatusSucceed(query->findNearestPoly(&startPositionNavMesh.X, &extent.X, &filter, &startPoly, nullptr))) return false; - } dtPolyRef endPoly = 0; - query->findNearestPoly(&endPositionNavMesh.X, &extent.X, &filter, &endPoly, nullptr); - if (!endPoly) - { + if (!dtStatusSucceed(query->findNearestPoly(&endPositionNavMesh.X, &extent.X, &filter, &endPoly, nullptr))) return false; - } dtPolyRef path[NAV_MESH_PATH_MAX_SIZE]; int32 pathSize; @@ -166,30 +155,19 @@ bool NavMeshRuntime::TestPath(const Vector3& startPosition, const Vector3& endPo Float3::Transform(endPosition, Properties.Rotation, endPositionNavMesh); dtPolyRef startPoly = 0; - query->findNearestPoly(&startPositionNavMesh.X, &extent.X, &filter, &startPoly, nullptr); - if (!startPoly) - { + if (!dtStatusSucceed(query->findNearestPoly(&startPositionNavMesh.X, &extent.X, &filter, &startPoly, nullptr))) return false; - } dtPolyRef endPoly = 0; - query->findNearestPoly(&endPositionNavMesh.X, &extent.X, &filter, &endPoly, nullptr); - if (!endPoly) - { + if (!dtStatusSucceed(query->findNearestPoly(&endPositionNavMesh.X, &extent.X, &filter, &endPoly, nullptr))) return false; - } dtPolyRef path[NAV_MESH_PATH_MAX_SIZE]; int32 pathSize; const auto findPathStatus = query->findPath(startPoly, endPoly, &startPositionNavMesh.X, &endPositionNavMesh.X, &filter, path, &pathSize, NAV_MESH_PATH_MAX_SIZE); if (dtStatusFailed(findPathStatus)) - { return false; - } - if (dtStatusDetail(findPathStatus, DT_PARTIAL_RESULT)) - { return false; - } return true; } @@ -210,11 +188,8 @@ bool NavMeshRuntime::FindClosestPoint(const Vector3& point, Vector3& result) con dtPolyRef startPoly = 0; Float3 nearestPt; - query->findNearestPoly(&pointNavMesh.X, &extent.X, &filter, &startPoly, &nearestPt.X); - if (!startPoly) - { + if (!dtStatusSucceed(query->findNearestPoly(&pointNavMesh.X, &extent.X, &filter, &startPoly, &nearestPt.X))) return false; - } Quaternion invRotation; Quaternion::Invert(Properties.Rotation, invRotation); @@ -235,11 +210,8 @@ bool NavMeshRuntime::FindRandomPoint(Vector3& result) const dtPolyRef randomPoly = 0; Float3 randomPt; - query->findRandomPoint(&filter, Random::Rand, &randomPoly, &randomPt.X); - if (!randomPoly) - { + if (!dtStatusSucceed(query->findRandomPoint(&filter, Random::Rand, &randomPoly, &randomPt.X))) return false; - } Quaternion invRotation; Quaternion::Invert(Properties.Rotation, invRotation); @@ -257,25 +229,19 @@ bool NavMeshRuntime::FindRandomPointAroundCircle(const Vector3& center, float ra dtQueryFilter filter; InitFilter(filter); - Float3 extent = Properties.DefaultQueryExtent; + Float3 extent(radius); Float3 centerNavMesh; Float3::Transform(center, Properties.Rotation, centerNavMesh); dtPolyRef centerPoly = 0; - query->findNearestPoly(¢erNavMesh.X, &extent.X, &filter, ¢erPoly, nullptr); - if (!centerPoly) - { + if (!dtStatusSucceed(query->findNearestPoly(¢erNavMesh.X, &extent.X, &filter, ¢erPoly, nullptr))) return false; - } dtPolyRef randomPoly = 0; Float3 randomPt; - query->findRandomPointAroundCircle(centerPoly, ¢erNavMesh.X, radius, &filter, Random::Rand, &randomPoly, &randomPt.X); - if (!randomPoly) - { + if (!dtStatusSucceed(query->findRandomPointAroundCircle(centerPoly, ¢erNavMesh.X, radius, &filter, Random::Rand, &randomPoly, &randomPt.X))) return false; - } Quaternion invRotation; Quaternion::Invert(Properties.Rotation, invRotation); @@ -300,11 +266,8 @@ bool NavMeshRuntime::RayCast(const Vector3& startPosition, const Vector3& endPos Float3::Transform(endPosition, Properties.Rotation, endPositionNavMesh); dtPolyRef startPoly = 0; - query->findNearestPoly(&startPositionNavMesh.X, &extent.X, &filter, &startPoly, nullptr); - if (!startPoly) - { + if (!dtStatusSucceed(query->findNearestPoly(&startPositionNavMesh.X, &extent.X, &filter, &startPoly, nullptr))) return false; - } dtRaycastHit hit; hit.path = nullptr; @@ -361,14 +324,6 @@ void NavMeshRuntime::EnsureCapacity(int32 tilesToAddCount) // Ensure to have size assigned ASSERT(_tileSize != 0); - // Allocate navmesh and initialize the default query - if (!_navMesh) - _navMesh = dtAllocNavMesh(); - if (dtStatusFailed(_navMeshQuery->init(_navMesh, MAX_NODES))) - { - LOG(Error, "Failed to initialize navmesh {0}.", Properties.Name); - } - // Prepare parameters dtNavMeshParams params; params.orig[0] = 0.0f; @@ -381,11 +336,17 @@ void NavMeshRuntime::EnsureCapacity(int32 tilesToAddCount) params.maxPolys = 1 << (22 - tilesBits); // Initialize nav mesh + if (!_navMesh) + _navMesh = dtAllocNavMesh(); if (dtStatusFailed(_navMesh->init(¶ms))) { - LOG(Error, "Navmesh {0} init failed.", Properties.Name); + LOG(Error, "Navmesh {0} init failed", Properties.Name); return; } + if (dtStatusFailed(_navMeshQuery->init(_navMesh, MAX_NODES))) + { + LOG(Error, "Navmesh query {0} init failed", Properties.Name); + } // Prepare tiles container _tiles.EnsureCapacity(newCapacity); @@ -405,7 +366,7 @@ void NavMeshRuntime::EnsureCapacity(int32 tilesToAddCount) const auto result = _navMesh->addTile(data, dataSize, flags, 0, nullptr); if (dtStatusFailed(result)) { - LOG(Warning, "Could not add tile to navmesh {0} (error: {1}).", Properties.Name, result & ~DT_FAILURE); + LOG(Warning, "Could not add tile ({2}x{3}, layer {4}) to navmesh {0} (error: {1})", Properties.Name, result & ~DT_FAILURE, tile.X, tile.Y, tile.Layer); } } } @@ -490,13 +451,11 @@ void NavMeshRuntime::RemoveTile(int32 x, int32 y, int32 layer) const auto tileRef = _navMesh->getTileRefAt(x, y, layer); if (tileRef == 0) - { return; - } if (dtStatusFailed(_navMesh->removeTile(tileRef, nullptr, nullptr))) { - LOG(Warning, "Failed to remove tile from navmesh {0}.", Properties.Name); + LOG(Warning, "Failed to remove tile ({1}x{2}, layer {3}) from navmesh {0}", Properties.Name, x, y, layer); } for (int32 i = 0; i < _tiles.Count(); i++) @@ -532,7 +491,7 @@ void NavMeshRuntime::RemoveTiles(bool (*prediction)(const NavMeshRuntime* navMes { if (dtStatusFailed(_navMesh->removeTile(tileRef, nullptr, nullptr))) { - LOG(Warning, "Failed to remove tile from navmesh {0}.", Properties.Name); + LOG(Warning, "Failed to remove tile ({1}x{2}, layer {3}) from navmesh {0}", Properties.Name, tile.X, tile.Y, tile.Layer); } } @@ -668,7 +627,7 @@ void NavMeshRuntime::AddTileInternal(NavMesh* navMesh, NavMeshTileData& tileData // Remove any existing tile at that location if (dtStatusFailed(_navMesh->removeTile(tileRef, nullptr, nullptr))) { - LOG(Warning, "Failed to remove tile from navmesh {0}.", Properties.Name); + LOG(Warning, "Failed to remove tile from navmesh {0}", Properties.Name); } // Reuse tile data container @@ -712,6 +671,6 @@ void NavMeshRuntime::AddTileInternal(NavMesh* navMesh, NavMeshTileData& tileData const auto result = _navMesh->addTile(data, dataSize, flags, 0, nullptr); if (dtStatusFailed(result)) { - LOG(Warning, "Could not add tile to navmesh {0} (error: {1}).", Properties.Name, result & ~DT_FAILURE); + LOG(Warning, "Could not add tile ({2}x{3}, layer {4}) to navmesh {0} (error: {1})", Properties.Name, result & ~DT_FAILURE, tileData.PosX, tileData.PosY, tileData.Layer); } } diff --git a/Source/Engine/Navigation/NavMeshRuntime.h b/Source/Engine/Navigation/NavMeshRuntime.h index 9b090b97c..40ea0959e 100644 --- a/Source/Engine/Navigation/NavMeshRuntime.h +++ b/Source/Engine/Navigation/NavMeshRuntime.h @@ -160,7 +160,7 @@ public: /// The source point. /// The result position on the navmesh (valid only if method returns true). /// True if found valid location on the navmesh, otherwise false. - API_FUNCTION() bool ProjectPoint(const Vector3& point, API_PARAM(Out) Vector3& result) const + API_FUNCTION() DEPRECATED bool ProjectPoint(const Vector3& point, API_PARAM(Out) Vector3& result) const { return FindClosestPoint(point, result); } diff --git a/Source/ThirdParty/recastnavigation/DetourNavMeshQuery.cpp b/Source/ThirdParty/recastnavigation/DetourNavMeshQuery.cpp index 7faefde81..5c7b6d797 100644 --- a/Source/ThirdParty/recastnavigation/DetourNavMeshQuery.cpp +++ b/Source/ThirdParty/recastnavigation/DetourNavMeshQuery.cpp @@ -487,14 +487,37 @@ dtStatus dtNavMeshQuery::findRandomPointAroundCircle(dtPolyRef startRef, const f int checksLimit = 100; do { + // Loop until finds a point on a random poly that is in the radius const float s = frand(); const float t = frand(); dtRandomPointInConvexPoly(verts, randomPoly->vertCount, areas, s, t, pt); } while (dtDistancePtPtSqr2D(centerPos, pt) > radiusSqr && checksLimit-- > 0); if (checksLimit <= 0) - return DT_FAILURE; - + { + // Check nearby polygons + float halfExtents[3] = { maxRadius, maxRadius, maxRadius }; + dtPolyRef polys[32]; + int polyCount; + queryPolygons(centerPos, halfExtents, filter, polys, &polyCount, 32); + for (int i = 0; i < polyCount && checksLimit <= 0; i++) + { + checksLimit = 100; + randomPolyRef = polys[i]; + m_nav->getTileAndPolyByRefUnsafe(randomPolyRef, &randomTile, &randomPoly); + do + { + // Loop until finds a point on a random poly that is in the radius + const float s = frand(); + const float t = frand(); + dtRandomPointInConvexPoly(verts, randomPoly->vertCount, areas, s, t, pt); + } + while (dtDistancePtPtSqr2D(centerPos, pt) > radiusSqr && checksLimit-- > 0); + } + if (checksLimit <= 0) + return DT_FAILURE; + } + closestPointOnPoly(randomPolyRef, pt, pt, NULL); dtVcopy(randomPt, pt); From 5e086809aece7d5c62f39c8e388360782584482d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 20 Apr 2024 15:51:20 +0200 Subject: [PATCH 22/43] Fix crash on prefab preview with lightmap in use #2454 --- Source/Engine/Foliage/Foliage.cpp | 4 ++-- Source/Engine/Level/Actors/StaticModel.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Foliage/Foliage.cpp b/Source/Engine/Foliage/Foliage.cpp index 31039ce03..39451207d 100644 --- a/Source/Engine/Foliage/Foliage.cpp +++ b/Source/Engine/Foliage/Foliage.cpp @@ -115,7 +115,7 @@ void Foliage::DrawInstance(RenderContext& renderContext, FoliageInstance& instan e = &result[key]; ASSERT_LOW_LAYER(key.Mat); e->DrawCall.Material = key.Mat; - e->DrawCall.Surface.Lightmap = EnumHasAnyFlags(_staticFlags, StaticFlags::Lightmap) ? _scene->LightmapsData.GetReadyLightmap(key.Lightmap) : nullptr; + e->DrawCall.Surface.Lightmap = EnumHasAnyFlags(_staticFlags, StaticFlags::Lightmap) && _scene ? _scene->LightmapsData.GetReadyLightmap(key.Lightmap) : nullptr; } // Add instance to the draw batch @@ -1172,7 +1172,7 @@ void Foliage::Draw(RenderContext& renderContext) draw.ForcedLOD = -1; draw.SortOrder = 0; draw.VertexColors = nullptr; - draw.Lightmap = _scene->LightmapsData.GetReadyLightmap(instance.Lightmap.TextureIndex); + draw.Lightmap = _scene ? _scene->LightmapsData.GetReadyLightmap(instance.Lightmap.TextureIndex) : nullptr; draw.LightmapUVs = &instance.Lightmap.UVsArea; draw.Buffer = &type.Entries; draw.World = &world; diff --git a/Source/Engine/Level/Actors/StaticModel.cpp b/Source/Engine/Level/Actors/StaticModel.cpp index d85088a69..dd77692b3 100644 --- a/Source/Engine/Level/Actors/StaticModel.cpp +++ b/Source/Engine/Level/Actors/StaticModel.cpp @@ -346,7 +346,7 @@ void StaticModel::Draw(RenderContext& renderContext) draw.World = &world; draw.DrawState = &_drawState; draw.Deformation = _deformation; - draw.Lightmap = _scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex); + draw.Lightmap = _scene ? _scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex) : nullptr; draw.LightmapUVs = &Lightmap.UVsArea; draw.Flags = _staticFlags; draw.DrawModes = DrawModes; @@ -380,7 +380,7 @@ void StaticModel::Draw(RenderContextBatch& renderContextBatch) draw.World = &world; draw.DrawState = &_drawState; draw.Deformation = _deformation; - draw.Lightmap = _scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex); + draw.Lightmap = _scene ? _scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex) : nullptr; draw.LightmapUVs = &Lightmap.UVsArea; draw.Flags = _staticFlags; draw.DrawModes = DrawModes; From 6aacea99ab883fabb4c696498d05ab24c7d6cf31 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 20 Apr 2024 16:16:01 +0200 Subject: [PATCH 23/43] Fix blend shapes transformation applying #2459 --- Source/Engine/Graphics/Models/ModelData.cpp | 28 +++++++++++-------- .../Tools/ModelTool/ModelTool.OpenFBX.cpp | 4 +-- Source/Engine/Tools/ModelTool/ModelTool.cpp | 3 ++ 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/Source/Engine/Graphics/Models/ModelData.cpp b/Source/Engine/Graphics/Models/ModelData.cpp index dc9527b1d..50361d309 100644 --- a/Source/Engine/Graphics/Models/ModelData.cpp +++ b/Source/Engine/Graphics/Models/ModelData.cpp @@ -508,6 +508,22 @@ void MeshData::TransformBuffer(const Matrix& matrix) Matrix::Invert(matrix, inverseTransposeMatrix); Matrix::Transpose(inverseTransposeMatrix, inverseTransposeMatrix); + // Transform blend shapes + for (auto& blendShape : BlendShapes) + { + const auto vv = blendShape.Vertices.Get(); + for (int32 i = 0; i < blendShape.Vertices.Count(); i++) + { + auto& v = vv[i]; + Float3 p = Positions[v.VertexIndex], vp; + Float3::Transform(p + v.PositionDelta, matrix, vp); + Float3::Transform(p, matrix, p); + v.PositionDelta = vp - p; + Float3::TransformNormal(v.NormalDelta, inverseTransposeMatrix, v.NormalDelta); + v.NormalDelta.Normalize(); + } + } + // Transform positions const auto pp = Positions.Get(); for (int32 i = 0; i < Positions.Count(); i++) @@ -531,18 +547,6 @@ void MeshData::TransformBuffer(const Matrix& matrix) Float3::TransformNormal(t, inverseTransposeMatrix, t); t.Normalize(); } - - // Transform blend shapes - for (auto& blendShape : BlendShapes) - { - for (int32 i = 0; i < blendShape.Vertices.Count(); i++) - { - auto& v = blendShape.Vertices[i]; - Float3::Transform(v.PositionDelta, matrix, v.PositionDelta); - Float3::TransformNormal(v.NormalDelta, inverseTransposeMatrix, v.NormalDelta); - v.NormalDelta.Normalize(); - } - } } void MeshData::NormalizeBlendWeights() diff --git a/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp b/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp index 5bf9ef7d8..b4b46c2f3 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp @@ -800,7 +800,7 @@ bool ProcessMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh* { // Mirror positions along the Z axis for (int32 i = 0; i < vertexCount; i++) - mesh.Positions[i].Z *= -1.0f; + mesh.Positions.Get()[i].Z *= -1.0f; for (auto& blendShapeData : mesh.BlendShapes) { for (auto& v : blendShapeData.Vertices) @@ -815,7 +815,7 @@ bool ProcessMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh* { // Invert the order for (int32 i = 0; i < mesh.Indices.Count(); i += 3) - Swap(mesh.Indices[i], mesh.Indices[i + 2]); + Swap(mesh.Indices.Get()[i], mesh.Indices.Get()[i + 2]); } if ((data.Options.CalculateTangents || !tangents) && mesh.UVs.HasItems()) diff --git a/Source/Engine/Tools/ModelTool/ModelTool.cpp b/Source/Engine/Tools/ModelTool/ModelTool.cpp index fc2298993..bfbd9421b 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.cpp @@ -589,6 +589,9 @@ bool ModelTool::ImportData(const String& path, ModelData& data, Options& options { for (auto& n : mesh->Normals) n *= -1; + for (auto& shape : mesh->BlendShapes) + for (auto& v : shape.Vertices) + v.NormalDelta *= -1; } } } From 1072b90c5b523a55e3f14352e12f97a9ec6b4e8b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 20 Apr 2024 16:52:07 +0200 Subject: [PATCH 24/43] Fix blend shapes normals usage #2459 --- Source/Engine/Graphics/Models/ModelData.cpp | 17 +++++++++++++---- Source/Engine/Level/Actors/AnimatedModel.cpp | 2 +- .../Tools/ModelTool/ModelTool.OpenFBX.cpp | 12 +++++++----- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/Source/Engine/Graphics/Models/ModelData.cpp b/Source/Engine/Graphics/Models/ModelData.cpp index 50361d309..02cb9e6eb 100644 --- a/Source/Engine/Graphics/Models/ModelData.cpp +++ b/Source/Engine/Graphics/Models/ModelData.cpp @@ -515,12 +515,21 @@ void MeshData::TransformBuffer(const Matrix& matrix) for (int32 i = 0; i < blendShape.Vertices.Count(); i++) { auto& v = vv[i]; - Float3 p = Positions[v.VertexIndex], vp; - Float3::Transform(p + v.PositionDelta, matrix, vp); + + Float3 p = Positions[v.VertexIndex]; + Float3 vp = p + v.PositionDelta; + Float3::Transform(vp, matrix, vp); Float3::Transform(p, matrix, p); v.PositionDelta = vp - p; - Float3::TransformNormal(v.NormalDelta, inverseTransposeMatrix, v.NormalDelta); - v.NormalDelta.Normalize(); + + Float3 n = Normals[v.VertexIndex]; + Float3 vn = n + v.NormalDelta; + vn.Normalize(); + Float3::TransformNormal(vn, inverseTransposeMatrix, vn); + vn.Normalize(); + Float3::TransformNormal(n, inverseTransposeMatrix, n); + n.Normalize(); + v.NormalDelta = vn - n; } } diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index fbc434099..18bd7fc87 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -640,7 +640,7 @@ void AnimatedModel::RunBlendShapeDeformer(const MeshBase* mesh, MeshDeformationD ASSERT_LOW_LAYER(blendShapeVertex.VertexIndex < vertexCount); VB0SkinnedElementType& vertex = *(data + blendShapeVertex.VertexIndex); vertex.Position = vertex.Position + blendShapeVertex.PositionDelta * q.Second; - Float3 normal = (vertex.Normal.ToFloat3() * 2.0f - 1.0f) + blendShapeVertex.NormalDelta; + Float3 normal = (vertex.Normal.ToFloat3() * 2.0f - 1.0f) + blendShapeVertex.NormalDelta * q.Second; vertex.Normal = normal * 0.5f + 0.5f; } } diff --git a/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp b/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp index b4b46c2f3..a9f06d239 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp @@ -786,11 +786,13 @@ bool ProcessMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh* auto shapeNormals = shape->getNormals(); for (int32 i = 0; i < blendShapeData.Vertices.Count(); i++) { - /*auto delta = ToFloat3(shapeNormals[i + firstVertexOffset]) - mesh.Normals[i]; - auto length = delta.Length(); - if (length > ZeroTolerance) - delta /= length;*/ - auto delta = Float3::Zero; // TODO: blend shape normals deltas fix when importing from fbx + auto delta = ToFloat3(shapeNormals[i + firstVertexOffset]); + if (data.ConvertRH) + { + // Mirror normals along the Z axis + delta.Z *= -1.0f; + } + delta = delta - mesh.Normals.Get()[i]; blendShapeData.Vertices.Get()[i].NormalDelta = delta; } } From 568a69081d4f404c5f4e47d790e7751a8159fc35 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 22 Apr 2024 13:18:52 +0200 Subject: [PATCH 25/43] Fix animated model skinning precision issues #2460 --- .../Editor/MaterialTemplates/Surface.shader | 33 +++++++++---------- .../Graphics/Materials/MaterialShader.h | 2 +- Source/Engine/Graphics/Models/ModelData.cpp | 5 +-- Source/Engine/Level/Actors/AnimatedModel.cpp | 2 +- .../Tools/ModelTool/ModelTool.OpenFBX.cpp | 8 ++--- 5 files changed, 24 insertions(+), 26 deletions(-) diff --git a/Content/Editor/MaterialTemplates/Surface.shader b/Content/Editor/MaterialTemplates/Surface.shader index 1e8589ff2..29d803545 100644 --- a/Content/Editor/MaterialTemplates/Surface.shader +++ b/Content/Editor/MaterialTemplates/Surface.shader @@ -430,7 +430,9 @@ float3x4 GetPrevBoneMatrix(int index) float3 SkinPrevPosition(ModelInput_Skinned input) { float4 position = float4(input.Position.xyz, 1); - float3x4 boneMatrix = input.BlendWeights.x * GetPrevBoneMatrix(input.BlendIndices.x); + float weightsSum = input.BlendWeights.x + input.BlendWeights.y + input.BlendWeights.z + input.BlendWeights.w; + float mainWeight = input.BlendWeights.x + (1.0f - weightsSum); // Re-normalize to account for 16-bit weights encoding erros + float3x4 boneMatrix = mainWeight * GetPrevBoneMatrix(input.BlendIndices.x); boneMatrix += input.BlendWeights.y * GetPrevBoneMatrix(input.BlendIndices.y); boneMatrix += input.BlendWeights.z * GetPrevBoneMatrix(input.BlendIndices.z); boneMatrix += input.BlendWeights.w * GetPrevBoneMatrix(input.BlendIndices.w); @@ -439,12 +441,6 @@ float3 SkinPrevPosition(ModelInput_Skinned input) #endif -// Cached skinning data to avoid multiple calculation -struct SkinningData -{ - float3x4 BlendMatrix; -}; - // Calculates the transposed transform matrix for the given bone index float3x4 GetBoneMatrix(int index) { @@ -457,7 +453,9 @@ float3x4 GetBoneMatrix(int index) // Calculates the transposed transform matrix for the given vertex (uses blending) float3x4 GetBoneMatrix(ModelInput_Skinned input) { - float3x4 boneMatrix = input.BlendWeights.x * GetBoneMatrix(input.BlendIndices.x); + float weightsSum = input.BlendWeights.x + input.BlendWeights.y + input.BlendWeights.z + input.BlendWeights.w; + float mainWeight = input.BlendWeights.x + (1.0f - weightsSum); // Re-normalize to account for 16-bit weights encoding erros + float3x4 boneMatrix = mainWeight * GetBoneMatrix(input.BlendIndices.x); boneMatrix += input.BlendWeights.y * GetBoneMatrix(input.BlendIndices.y); boneMatrix += input.BlendWeights.z * GetBoneMatrix(input.BlendIndices.z); boneMatrix += input.BlendWeights.w * GetBoneMatrix(input.BlendIndices.w); @@ -465,13 +463,13 @@ float3x4 GetBoneMatrix(ModelInput_Skinned input) } // Transforms the vertex position by weighted sum of the skinning matrices -float3 SkinPosition(ModelInput_Skinned input, SkinningData data) +float3 SkinPosition(ModelInput_Skinned input, float3x4 boneMatrix) { - return mul(data.BlendMatrix, float4(input.Position.xyz, 1)); + return mul(boneMatrix, float4(input.Position.xyz, 1)); } // Transforms the vertex position by weighted sum of the skinning matrices -float3x3 SkinTangents(ModelInput_Skinned input, SkinningData data) +float3x3 SkinTangents(ModelInput_Skinned input, float3x4 boneMatrix) { // Unpack vertex tangent frame float bitangentSign = input.Tangent.w ? -1.0f : +1.0f; @@ -479,10 +477,10 @@ float3x3 SkinTangents(ModelInput_Skinned input, SkinningData data) float3 tangent = input.Tangent.xyz * 2.0 - 1.0; // Apply skinning - tangent = mul(data.BlendMatrix, float4(tangent, 0)); - normal = mul(data.BlendMatrix, float4(normal, 0)); + tangent = normalize(mul(boneMatrix, float4(tangent, 0))); + normal = normalize(mul(boneMatrix, float4(normal, 0))); - float3 bitangent = cross(normal, tangent) * bitangentSign; + float3 bitangent = normalize(cross(normal, tangent) * bitangentSign); return float3x3(tangent, bitangent, normal); } @@ -501,10 +499,9 @@ VertexOutput VS_Skinned(ModelInput_Skinned input) VertexOutput output; // Perform skinning - SkinningData data; - data.BlendMatrix = GetBoneMatrix(input); - float3 position = SkinPosition(input, data); - float3x3 tangentToLocal = SkinTangents(input, data); + float3x4 boneMatrix = GetBoneMatrix(input); + float3 position = SkinPosition(input, boneMatrix); + float3x3 tangentToLocal = SkinTangents(input, boneMatrix); // Compute world space vertex position CalculateInstanceTransform(input); diff --git a/Source/Engine/Graphics/Materials/MaterialShader.h b/Source/Engine/Graphics/Materials/MaterialShader.h index d79ef49b0..7a5b842e2 100644 --- a/Source/Engine/Graphics/Materials/MaterialShader.h +++ b/Source/Engine/Graphics/Materials/MaterialShader.h @@ -10,7 +10,7 @@ /// /// Current materials shader version. /// -#define MATERIAL_GRAPH_VERSION 161 +#define MATERIAL_GRAPH_VERSION 162 class Material; class GPUShader; diff --git a/Source/Engine/Graphics/Models/ModelData.cpp b/Source/Engine/Graphics/Models/ModelData.cpp index 02cb9e6eb..b42288f7f 100644 --- a/Source/Engine/Graphics/Models/ModelData.cpp +++ b/Source/Engine/Graphics/Models/ModelData.cpp @@ -563,9 +563,10 @@ void MeshData::NormalizeBlendWeights() ASSERT(Positions.Count() == BlendWeights.Count()); for (int32 i = 0; i < Positions.Count(); i++) { - const float sum = BlendWeights[i].SumValues(); + Float4& weights = BlendWeights.Get()[i]; + const float sum = weights.SumValues(); const float invSum = sum > ZeroTolerance ? 1.0f / sum : 0.0f; - BlendWeights[i] *= invSum; + weights *= invSum; } } diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index 18bd7fc87..17b9acc11 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -113,7 +113,7 @@ void AnimatedModel::PreInitSkinningData() for (int32 boneIndex = 0; boneIndex < bonesCount; boneIndex++) { auto& bone = skeleton.Bones[boneIndex]; - identityMatrices[boneIndex] = bone.OffsetMatrix * GraphInstance.NodesPose[bone.NodeIndex]; + identityMatrices.Get()[boneIndex] = bone.OffsetMatrix * GraphInstance.NodesPose[bone.NodeIndex]; } _skinningData.SetData(identityMatrices.Get(), true); diff --git a/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp b/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp index a9f06d239..cf124f977 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.OpenFBX.cpp @@ -720,23 +720,23 @@ bool ProcessMesh(ModelData& result, OpenFbxImporterData& data, const ofbx::Mesh* { int vtxIndex = clusterIndices[j] - firstVertexOffset; float vtxWeight = (float)clusterWeights[j]; - if (vtxWeight <= 0 || vtxIndex < 0 || vtxIndex >= vertexCount) continue; - - auto& indices = mesh.BlendIndices[vtxIndex]; - auto& weights = mesh.BlendWeights[vtxIndex]; + Int4& indices = mesh.BlendIndices.Get()[vtxIndex]; + Float4& weights = mesh.BlendWeights.Get()[vtxIndex]; for (int32 k = 0; k < 4; k++) { if (vtxWeight >= weights.Raw[k]) { + // Move lower weights by one down for (int32 l = 2; l >= k; l--) { indices.Raw[l + 1] = indices.Raw[l]; weights.Raw[l + 1] = weights.Raw[l]; } + // Set bone influence indices.Raw[k] = boneIndex; weights.Raw[k] = vtxWeight; break; From 5f02b4173a927ee494862957d6de10b2a966bed0 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 22 Apr 2024 13:29:29 +0200 Subject: [PATCH 26/43] Fix nested animations sampling #2472 --- .../Animations/Graph/AnimGroup.Animation.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp index 75d21c99c..0c1b0a373 100644 --- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp +++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp @@ -247,6 +247,7 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode* { // Per-channel bit to indicate which channels were used by nested usedNodesThis.Resize(nodes->Nodes.Count()); + usedNodesThis.SetAll(false); usedNodes = &usedNodesThis; } @@ -286,11 +287,11 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode* SkinnedModel::SkeletonMapping sourceMapping; if (retarget) sourceMapping = _graph.BaseModel->GetSkeletonMapping(mapping.SourceSkeleton); - for (int32 i = 0; i < nodes->Nodes.Count(); i++) + for (int32 nodeIndex = 0; nodeIndex < nodes->Nodes.Count(); nodeIndex++) { - const int32 nodeToChannel = mapping.NodesMapping[i]; - Transform& dstNode = nodes->Nodes[i]; - Transform srcNode = emptyNodes->Nodes[i]; + const int32 nodeToChannel = mapping.NodesMapping[nodeIndex]; + Transform& dstNode = nodes->Nodes[nodeIndex]; + Transform srcNode = emptyNodes->Nodes[nodeIndex]; if (nodeToChannel != -1) { // Calculate the animated node transformation @@ -299,14 +300,14 @@ void AnimGraphExecutor::ProcessAnimation(AnimGraphImpulse* nodes, AnimGraphNode* // Optionally retarget animation into the skeleton used by the Anim Graph if (retarget) { - RetargetSkeletonNode(mapping.SourceSkeleton->Skeleton, mapping.TargetSkeleton->Skeleton, sourceMapping, srcNode, i); + RetargetSkeletonNode(mapping.SourceSkeleton->Skeleton, mapping.TargetSkeleton->Skeleton, sourceMapping, srcNode, nodeIndex); } // Mark node as used if (usedNodes) - usedNodes->Set(i, true); + usedNodes->Set(nodeIndex, true); } - else if (usedNodes && usedNodes != &usedNodesThis) + else if (usedNodes && (usedNodes != &usedNodesThis || usedNodes->Get(nodeIndex))) { // Skip for nested animations so other one or top-level anim will update remaining nodes continue; From e551eae30d6dea91f1e6ddf6f61a27519f570e8e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 22 Apr 2024 13:32:09 +0200 Subject: [PATCH 27/43] Update engine assets --- Content/Editor/Camera/M_Camera.flax | 4 ++-- Content/Editor/CubeTexturePreviewMaterial.flax | 4 ++-- Content/Editor/DebugMaterials/DDGIDebugProbes.flax | 4 ++-- Content/Editor/DebugMaterials/SingleColor/Decal.flax | 2 +- Content/Editor/DebugMaterials/SingleColor/Particle.flax | 4 ++-- Content/Editor/DebugMaterials/SingleColor/Surface.flax | 4 ++-- .../Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax | 4 ++-- Content/Editor/DebugMaterials/SingleColor/Terrain.flax | 4 ++-- Content/Editor/DefaultFontMaterial.flax | 4 ++-- Content/Editor/Gizmo/FoliageBrushMaterial.flax | 4 ++-- Content/Editor/Gizmo/Material.flax | 4 ++-- Content/Editor/Gizmo/MaterialWire.flax | 4 ++-- Content/Editor/Gizmo/SelectionOutlineMaterial.flax | 4 ++-- Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax | 4 ++-- Content/Editor/Highlight Material.flax | 4 ++-- Content/Editor/Icons/IconsMaterial.flax | 4 ++-- Content/Editor/IesProfilePreviewMaterial.flax | 4 ++-- Content/Editor/Particles/Particle Material Color.flax | 4 ++-- Content/Editor/Particles/Smoke Material.flax | 4 ++-- Content/Editor/SpriteMaterial.flax | 4 ++-- Content/Editor/Terrain/Circle Brush Material.flax | 2 +- Content/Editor/Terrain/Highlight Terrain Material.flax | 4 ++-- Content/Editor/TexturePreviewMaterial.flax | 4 ++-- Content/Editor/Wires Debug Material.flax | 4 ++-- Content/Engine/DefaultDeformableMaterial.flax | 4 ++-- Content/Engine/DefaultMaterial.flax | 4 ++-- Content/Engine/DefaultTerrainMaterial.flax | 4 ++-- Content/Engine/SingleColorMaterial.flax | 4 ++-- Content/Engine/SkyboxMaterial.flax | 4 ++-- 29 files changed, 56 insertions(+), 56 deletions(-) diff --git a/Content/Editor/Camera/M_Camera.flax b/Content/Editor/Camera/M_Camera.flax index 26ba67429..16bf873eb 100644 --- a/Content/Editor/Camera/M_Camera.flax +++ b/Content/Editor/Camera/M_Camera.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:966db15e769106e448eedaaf8bff4d5724f63e813fc7f81c9da2962b37c9b57e -size 30094 +oid sha256:c7a4c3bb2a339de16d9738b35538d33670a224d69f8eba00e5977d03cfa85f83 +size 30456 diff --git a/Content/Editor/CubeTexturePreviewMaterial.flax b/Content/Editor/CubeTexturePreviewMaterial.flax index 828d620d2..d1e545233 100644 --- a/Content/Editor/CubeTexturePreviewMaterial.flax +++ b/Content/Editor/CubeTexturePreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:28e5aaeb274e7590bc9ec13212e041d4d14baef562487507ea3621ec040c393b -size 31807 +oid sha256:89a9ab2a0168f0f51fd34ee064a896a65d1a3f85214cf04e370b82d41329cb09 +size 32171 diff --git a/Content/Editor/DebugMaterials/DDGIDebugProbes.flax b/Content/Editor/DebugMaterials/DDGIDebugProbes.flax index cc4a16fe3..6a70e5490 100644 --- a/Content/Editor/DebugMaterials/DDGIDebugProbes.flax +++ b/Content/Editor/DebugMaterials/DDGIDebugProbes.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5d0dc041c6b8712c2f892ac3185c1f9e0b3608b774db5590bcaad3ad0775dc93 -size 41042 +oid sha256:90cfc9c29e29f8b25ff3f68394cb5133a95a936e7e6c0d0fe7cc4890a2a1ed61 +size 41404 diff --git a/Content/Editor/DebugMaterials/SingleColor/Decal.flax b/Content/Editor/DebugMaterials/SingleColor/Decal.flax index f5ed08a03..e1b60860c 100644 --- a/Content/Editor/DebugMaterials/SingleColor/Decal.flax +++ b/Content/Editor/DebugMaterials/SingleColor/Decal.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6854135456c47f2c693ebd875d83812f29a6c93b6cddad9e267de2db261a6133 +oid sha256:188d0f0574a2b12e96945fdde66082d0a63bf33965260969d50decbbbbd32590 size 7489 diff --git a/Content/Editor/DebugMaterials/SingleColor/Particle.flax b/Content/Editor/DebugMaterials/SingleColor/Particle.flax index e22b6c1fa..1a84ad997 100644 --- a/Content/Editor/DebugMaterials/SingleColor/Particle.flax +++ b/Content/Editor/DebugMaterials/SingleColor/Particle.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a54c7442f97baa1ab835485f906911372847808e2865790109b0524614663210 -size 31722 +oid sha256:a6bfa4696c210cfdc8a3f3b6c1c28217e717bf8abe2dfacb793b36410681b388 +size 31721 diff --git a/Content/Editor/DebugMaterials/SingleColor/Surface.flax b/Content/Editor/DebugMaterials/SingleColor/Surface.flax index 95bab955f..e824434ac 100644 --- a/Content/Editor/DebugMaterials/SingleColor/Surface.flax +++ b/Content/Editor/DebugMaterials/SingleColor/Surface.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:129afa8e75f4e83307e3171993f6e3ae52a205456a629beae4a58ed84e435292 -size 29990 +oid sha256:b5428f5f963d38ba8de16a932fba4f722e9b6bb03d874020122df3396841ea0e +size 30352 diff --git a/Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax b/Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax index 790690cd8..602434c46 100644 --- a/Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax +++ b/Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4cd7963263033f8f6e09de86da1ce14aa9baee3f1811986d56441cc62164e231 -size 31882 +oid sha256:c4aa5cf0289c1489829e400d8c244712df6214520d1bc67feabcfe7e71c3b0f9 +size 32243 diff --git a/Content/Editor/DebugMaterials/SingleColor/Terrain.flax b/Content/Editor/DebugMaterials/SingleColor/Terrain.flax index 6e85901e7..55029e5fb 100644 --- a/Content/Editor/DebugMaterials/SingleColor/Terrain.flax +++ b/Content/Editor/DebugMaterials/SingleColor/Terrain.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e21ef2afb59c22647d644502c6c0b6316f3e1b75be0b4ac95e4cde0701969d45 -size 20769 +oid sha256:07066043d6c634162ade92ca33119be5798fd9c8183712b6569e027c41cc1bff +size 20737 diff --git a/Content/Editor/DefaultFontMaterial.flax b/Content/Editor/DefaultFontMaterial.flax index 87e8f541c..fd8e96f88 100644 --- a/Content/Editor/DefaultFontMaterial.flax +++ b/Content/Editor/DefaultFontMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a7838428fc9afa03649b25168f2458f7c06fc99c6a50f17579daa19ccc93916e -size 30169 +oid sha256:33bd3e45519a05d48a7035f0740b3f879d962d80f7c4bc546505583585545341 +size 30531 diff --git a/Content/Editor/Gizmo/FoliageBrushMaterial.flax b/Content/Editor/Gizmo/FoliageBrushMaterial.flax index f1fa6e3f5..81da98526 100644 --- a/Content/Editor/Gizmo/FoliageBrushMaterial.flax +++ b/Content/Editor/Gizmo/FoliageBrushMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f87fe1172cc96b6aaef5653b9d7c5ef336b7f5934c620ae13661fd89b636cfcc -size 37909 +oid sha256:eafdd82ba4c82f82e70525b4530f7f332646ce1f7d19c4c435fae84a25c502bf +size 38270 diff --git a/Content/Editor/Gizmo/Material.flax b/Content/Editor/Gizmo/Material.flax index 80d319a03..e6dab36f5 100644 --- a/Content/Editor/Gizmo/Material.flax +++ b/Content/Editor/Gizmo/Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fc18e2ff1f55cfc41a5b083843b11e1573ac7066bc58153262b2b61ea6105fce -size 32486 +oid sha256:ae83a3e9e14ebdd5bde7e6b40f8bd2de8a530a4471b111470d14359536f22a00 +size 32847 diff --git a/Content/Editor/Gizmo/MaterialWire.flax b/Content/Editor/Gizmo/MaterialWire.flax index db5910a4f..b185becd2 100644 --- a/Content/Editor/Gizmo/MaterialWire.flax +++ b/Content/Editor/Gizmo/MaterialWire.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4dcae1147c6a225a8e5e0613d15950b77c05f3f90f60acdfa3943063ccc582a5 -size 31624 +oid sha256:cc57eb71f0cde517be6082957b85439e6e464795bca9ef1c23fb00c679ac183a +size 31985 diff --git a/Content/Editor/Gizmo/SelectionOutlineMaterial.flax b/Content/Editor/Gizmo/SelectionOutlineMaterial.flax index 1deecbcd9..f2877f0ec 100644 --- a/Content/Editor/Gizmo/SelectionOutlineMaterial.flax +++ b/Content/Editor/Gizmo/SelectionOutlineMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:811b50ce2d2672bf7ce12dc161f6dc2038c5cfe8792012632ed89fb951c4058f -size 16202 +oid sha256:7fbde1a8dd1bdc621f562f18b7056c50de9df801719989c304a5a3aecbb31a4e +size 16166 diff --git a/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax b/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax index f9f1b97c7..d92847c20 100644 --- a/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax +++ b/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:30bcf9eb156bafe9b5536c040994877eab6cb0fe942fc31d744e5bff1bb02838 -size 31103 +oid sha256:5835c9950bf5948a2a1bff56ed731a7e1788e0a03fea10e11762f40143057ae9 +size 31465 diff --git a/Content/Editor/Highlight Material.flax b/Content/Editor/Highlight Material.flax index c13a1c0d4..87488d15b 100644 --- a/Content/Editor/Highlight Material.flax +++ b/Content/Editor/Highlight Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eb2d2b798b5fe819a0c137603b1c1bb598530ef01efb681690e5ce0901a550a9 -size 30271 +oid sha256:ecf23fb09c1d049f3c42668c2dabc6ce53f168af1b7b4d3ead13309799011e17 +size 30632 diff --git a/Content/Editor/Icons/IconsMaterial.flax b/Content/Editor/Icons/IconsMaterial.flax index 7966e8335..241c15820 100644 --- a/Content/Editor/Icons/IconsMaterial.flax +++ b/Content/Editor/Icons/IconsMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2d4413df45e276c0cf6b0ede54ecf8dbf1a6f16ee12db3df9b409db2a632adc7 -size 30199 +oid sha256:97b6c13093d5ca38b2d70da09fb7c7e9a289dd6a7812d8333c67a5558e410b8e +size 30560 diff --git a/Content/Editor/IesProfilePreviewMaterial.flax b/Content/Editor/IesProfilePreviewMaterial.flax index ba6d02979..818bec914 100644 --- a/Content/Editor/IesProfilePreviewMaterial.flax +++ b/Content/Editor/IesProfilePreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4d3c6d38944ae59d48c60c7e937c43b03d4659398ac05ac863fd4da16156529f -size 18217 +oid sha256:933cf9e187f8a475ae571560b4fe20edddc5d3a869e4696b81021031d8f4981c +size 18205 diff --git a/Content/Editor/Particles/Particle Material Color.flax b/Content/Editor/Particles/Particle Material Color.flax index 3fbacc935..c7f46c810 100644 --- a/Content/Editor/Particles/Particle Material Color.flax +++ b/Content/Editor/Particles/Particle Material Color.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3217b2dc041d3c41e77522a0766d99de0af26fd3ec28142cb9bb505b0fbc7723 -size 29953 +oid sha256:0156c820df3b48f412b66c0bfdbc462896b36b30f59cab250c5a06e274bc193f +size 29952 diff --git a/Content/Editor/Particles/Smoke Material.flax b/Content/Editor/Particles/Smoke Material.flax index d12139eff..dae8d0226 100644 --- a/Content/Editor/Particles/Smoke Material.flax +++ b/Content/Editor/Particles/Smoke Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8657cb23bdf2423f4535cb67da0487be72864a12d53e9521fdc99bce752ff518 -size 37141 +oid sha256:20635d62896ef520330d4a63b357a2e2f991b0fa7cdcd8bb586bfe5e7669717a +size 37132 diff --git a/Content/Editor/SpriteMaterial.flax b/Content/Editor/SpriteMaterial.flax index 75f4d1f89..562280feb 100644 --- a/Content/Editor/SpriteMaterial.flax +++ b/Content/Editor/SpriteMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:eae8e6b9147611dcf98b181339d3b581128a877b55c8b52b586b7d3fe43d01b2 -size 31182 +oid sha256:9c2385c51030b3224bce8896d4c692b41316373c76341c51f15bbf0c4c3fc240 +size 31544 diff --git a/Content/Editor/Terrain/Circle Brush Material.flax b/Content/Editor/Terrain/Circle Brush Material.flax index 9b21e6c82..38e8ac906 100644 --- a/Content/Editor/Terrain/Circle Brush Material.flax +++ b/Content/Editor/Terrain/Circle Brush Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a20f7283220500bb17c8c5afbab5a5c1bfdc03703868b8ced7da462657806fd7 +oid sha256:f9c9455b6f4935d35b0a63cd9aea417f010576a630d3be9e1a124ca1d531ae8a size 27409 diff --git a/Content/Editor/Terrain/Highlight Terrain Material.flax b/Content/Editor/Terrain/Highlight Terrain Material.flax index 00560e1cb..f1e03310f 100644 --- a/Content/Editor/Terrain/Highlight Terrain Material.flax +++ b/Content/Editor/Terrain/Highlight Terrain Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f48c4f43dcf7e05255b41d763e1ca9b21d629df43d35b59b8574ae955d392fc4 -size 20822 +oid sha256:7436293db3692279754eaf29aed820a86ff214efc05a336a266582e8e0d3393c +size 20790 diff --git a/Content/Editor/TexturePreviewMaterial.flax b/Content/Editor/TexturePreviewMaterial.flax index b21daaa5a..80be4d726 100644 --- a/Content/Editor/TexturePreviewMaterial.flax +++ b/Content/Editor/TexturePreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:52921ebe6efaf3a74950e817a4e2224ef2ef54e0698efc042ba9cd2c17693e54 -size 10568 +oid sha256:3c8380071cf96428989cd6fd92d70c7b7ea694fe8a939608cd4367f957e75491 +size 10570 diff --git a/Content/Editor/Wires Debug Material.flax b/Content/Editor/Wires Debug Material.flax index caa177eac..0834d47a6 100644 --- a/Content/Editor/Wires Debug Material.flax +++ b/Content/Editor/Wires Debug Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6f8018999baf4b415d400b0b6150de27fbed48e2c93afbd16d074efc39b9c206 -size 30271 +oid sha256:a9dffca5051434a4731c16db377598d424dbeab57211331a8e7d16c05e18a7d9 +size 30632 diff --git a/Content/Engine/DefaultDeformableMaterial.flax b/Content/Engine/DefaultDeformableMaterial.flax index 052d5946e..097e5353c 100644 --- a/Content/Engine/DefaultDeformableMaterial.flax +++ b/Content/Engine/DefaultDeformableMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a70603cf2ace948b588924b39a1d31e309238782581f26b3388b8d9d3b44d4d7 -size 18530 +oid sha256:0fe507d8719930b86b9acfe2a6984f0d6e13523ba32c4c5df2c823bcb42281d9 +size 18524 diff --git a/Content/Engine/DefaultMaterial.flax b/Content/Engine/DefaultMaterial.flax index 588f85571..590833abc 100644 --- a/Content/Engine/DefaultMaterial.flax +++ b/Content/Engine/DefaultMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:de63950e622426ed8905d19097a5f9d189da4a6300ae9a77577f0e7fab59a1bb -size 32021 +oid sha256:404951f3ab7a8f05b7bec519f441e27225411c327508f795ac007a401d8d1453 +size 32377 diff --git a/Content/Engine/DefaultTerrainMaterial.flax b/Content/Engine/DefaultTerrainMaterial.flax index e9af0516e..f21cdc1df 100644 --- a/Content/Engine/DefaultTerrainMaterial.flax +++ b/Content/Engine/DefaultTerrainMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7c206df1af51d206c805449d5e6a4ce3fcaf70ac0c5310cf77dc0921a56e8552 -size 22912 +oid sha256:210ca70aeac7856c59bfcfb0c3370bc60a6724a4f2346cf4fbee1715878ca672 +size 22874 diff --git a/Content/Engine/SingleColorMaterial.flax b/Content/Engine/SingleColorMaterial.flax index 31af3b1c8..7edfefde9 100644 --- a/Content/Engine/SingleColorMaterial.flax +++ b/Content/Engine/SingleColorMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c87f422816ea68b3d6524b4f727d007ac4d5d2484931cf7a61cfe722b9c111d7 -size 30191 +oid sha256:6c03577e1dda81ef42be30e3f7fb1dd92821baf52a30cec05aae6fa687ede451 +size 30553 diff --git a/Content/Engine/SkyboxMaterial.flax b/Content/Engine/SkyboxMaterial.flax index df2859f7b..ac9e5fa57 100644 --- a/Content/Engine/SkyboxMaterial.flax +++ b/Content/Engine/SkyboxMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0e8a62e94c4e265902ed2e95ea059dcc0aca50f79f7a6986ff5a6d99f034f3d3 -size 31389 +oid sha256:4ddc74bc1aa47fe9b63e0a053b9dc56751ba2a9bcfa64987eddb230e382ef10a +size 31751 From e795a8b037435ab3c9628c6660f2cb0abef1eb93 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 22 Apr 2024 15:29:25 +0200 Subject: [PATCH 28/43] Fix compile warning --- Source/Engine/ContentImporters/ImportModel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/ContentImporters/ImportModel.cpp b/Source/Engine/ContentImporters/ImportModel.cpp index 75a25b5fa..6f0a9c626 100644 --- a/Source/Engine/ContentImporters/ImportModel.cpp +++ b/Source/Engine/ContentImporters/ImportModel.cpp @@ -467,7 +467,7 @@ CreateAssetResult ImportModel::Import(CreateAssetContext& context) writer.StartObject(); eKey.Value.Instance->Serialize(writer, nullptr); writer.EndObject(); - cloneKey.Value.JsonData.Set(buffer.GetString(), buffer.GetSize()); + cloneKey.Value.JsonData.Set(buffer.GetString(), (int32)buffer.GetSize()); } } } From 32b15f90ab2ea8c86dcf5ea06d0f48353ac6407c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 22 Apr 2024 18:10:58 +0200 Subject: [PATCH 29/43] Minor improvements --- Flax.sln.DotSettings | 1 + Source/Engine/Content/Asset.cpp | 3 +- Source/Engine/Content/Asset.h | 2 +- Source/Engine/Content/BinaryAsset.cpp | 6 +-- .../Engine/Content/Loading/ContentLoadTask.h | 41 ----------------- .../Content/Loading/ContentLoadingManager.cpp | 3 +- .../Content/Loading/Tasks/LoadAssetDataTask.h | 7 +-- .../Content/Loading/Tasks/LoadAssetTask.h | 46 +++++++++---------- Source/Engine/Threading/Task.h | 18 +++----- 9 files changed, 36 insertions(+), 91 deletions(-) diff --git a/Flax.sln.DotSettings b/Flax.sln.DotSettings index 6d72e44ed..96456601b 100644 --- a/Flax.sln.DotSettings +++ b/Flax.sln.DotSettings @@ -236,6 +236,7 @@ True True True + DisabledByUser True Blue True diff --git a/Source/Engine/Content/Asset.cpp b/Source/Engine/Content/Asset.cpp index 30771899f..ddff7fc8a 100644 --- a/Source/Engine/Content/Asset.cpp +++ b/Source/Engine/Content/Asset.cpp @@ -587,8 +587,8 @@ bool Asset::IsInternalType() const bool Asset::onLoad(LoadAssetTask* task) { - // It may fail when task is cancelled and new one is created later (don't crash but just end with an error) if (task->Asset.Get() != this || Platform::AtomicRead(&_loadingTask) == 0) + // It may fail when task is cancelled and new one was created later (don't crash but just end with an error) return true; Locker.Lock(); @@ -601,7 +601,6 @@ bool Asset::onLoad(LoadAssetTask* task) } const bool isLoaded = result == LoadResult::Ok; const bool failed = !isLoaded; - LoadState state = LoadState::Loaded; Platform::AtomicStore(&_loadState, (int64)(isLoaded ? LoadState::Loaded : LoadState::LoadFailed)); if (failed) { diff --git a/Source/Engine/Content/Asset.h b/Source/Engine/Content/Asset.h index c4b89935d..d1256b7c7 100644 --- a/Source/Engine/Content/Asset.h +++ b/Source/Engine/Content/Asset.h @@ -103,7 +103,7 @@ public: public: /// - /// Gets the path to the asset storage file. In Editor it reflects the actual file, in cooked Game, it fakes the Editor path to be informative for developers. + /// Gets the path to the asset storage file. In Editor, it reflects the actual file, in cooked Game, it fakes the Editor path to be informative for developers. /// API_PROPERTY() virtual const String& GetPath() const = 0; diff --git a/Source/Engine/Content/BinaryAsset.cpp b/Source/Engine/Content/BinaryAsset.cpp index 7735d8b28..692fc9329 100644 --- a/Source/Engine/Content/BinaryAsset.cpp +++ b/Source/Engine/Content/BinaryAsset.cpp @@ -507,8 +507,7 @@ public: /// /// The asset. InitAssetTask(BinaryAsset* asset) - : ContentLoadTask(Type::Custom) - , _asset(asset) + : _asset(asset) , _dataLock(asset->Storage->Lock()) { } @@ -527,8 +526,6 @@ protected: AssetReference ref = _asset.Get(); if (ref == nullptr) return Result::MissingReferences; - - // Prepare auto storage = ref->Storage; auto factory = (BinaryAssetFactoryBase*)Content::GetAssetFactory(ref->GetTypeName()); ASSERT(factory); @@ -548,7 +545,6 @@ protected: _dataLock.Release(); _asset = nullptr; - // Base ContentLoadTask::OnEnd(); } }; diff --git a/Source/Engine/Content/Loading/ContentLoadTask.h b/Source/Engine/Content/Loading/ContentLoadTask.h index d8d44b50a..2e2608612 100644 --- a/Source/Engine/Content/Loading/ContentLoadTask.h +++ b/Source/Engine/Content/Loading/ContentLoadTask.h @@ -15,52 +15,11 @@ class ContentLoadTask : public Task friend LoadingThread; public: - /// - /// Describes work type - /// - DECLARE_ENUM_3(Type, Custom, LoadAsset, LoadAssetData); - /// /// Describes work result value /// DECLARE_ENUM_5(Result, Ok, AssetLoadError, MissingReferences, LoadDataError, TaskFailed); -private: - /// - /// Task type - /// - Type _type; - -protected: - /// - /// Initializes a new instance of the class. - /// - /// The task type. - ContentLoadTask(const Type type) - : _type(type) - { - } - -public: - /// - /// Gets a task type. - /// - FORCE_INLINE Type GetType() const - { - return _type; - } - -public: - /// - /// Checks if async task is loading given asset resource - /// - /// Target asset to check - /// True if is loading that asset, otherwise false - bool IsLoading(Asset* asset) const - { - return _type == Type::LoadAsset && HasReference((Object*)asset); - } - protected: virtual Result run() = 0; diff --git a/Source/Engine/Content/Loading/ContentLoadingManager.cpp b/Source/Engine/Content/Loading/ContentLoadingManager.cpp index 68d06b3d2..d0efbd197 100644 --- a/Source/Engine/Content/Loading/ContentLoadingManager.cpp +++ b/Source/Engine/Content/Loading/ContentLoadingManager.cpp @@ -4,6 +4,7 @@ #include "ContentLoadTask.h" #include "Engine/Core/Log.h" #include "Engine/Core/Math/Math.h" +#include "Engine/Core/Collections/Array.h" #include "Engine/Platform/CPUInfo.h" #include "Engine/Platform/Thread.h" #include "Engine/Platform/ConditionVariable.h" @@ -212,7 +213,7 @@ void ContentLoadingManagerService::Dispose() String ContentLoadTask::ToString() const { - return String::Format(TEXT("Content Load Task {0} ({1})"), ToString(GetType()), (int32)GetState()); + return String::Format(TEXT("Content Load Task ({})"), (int32)GetState()); } void ContentLoadTask::Enqueue() diff --git a/Source/Engine/Content/Loading/Tasks/LoadAssetDataTask.h b/Source/Engine/Content/Loading/Tasks/LoadAssetDataTask.h index 60ebba6cd..3fbd34835 100644 --- a/Source/Engine/Content/Loading/Tasks/LoadAssetDataTask.h +++ b/Source/Engine/Content/Loading/Tasks/LoadAssetDataTask.h @@ -15,28 +15,24 @@ class LoadAssetDataTask : public ContentLoadTask { private: - WeakAssetReference _asset; // Don't keep ref to the asset (so it can be unloaded if none using it, task will fail then) AssetChunksFlag _chunks; FlaxStorage::LockData _dataLock; public: - /// /// Initializes a new instance of the class. /// /// The asset to load. /// The chunks to load. LoadAssetDataTask(BinaryAsset* asset, AssetChunksFlag chunks) - : ContentLoadTask(Type::LoadAssetData) - , _asset(asset) + : _asset(asset) , _chunks(chunks) , _dataLock(asset->Storage->Lock()) { } public: - // [ContentLoadTask] bool HasReference(Object* obj) const override { @@ -44,7 +40,6 @@ public: } protected: - // [ContentLoadTask] Result run() override { diff --git a/Source/Engine/Content/Loading/Tasks/LoadAssetTask.h b/Source/Engine/Content/Loading/Tasks/LoadAssetTask.h index f71f2b4a6..24bd66119 100644 --- a/Source/Engine/Content/Loading/Tasks/LoadAssetTask.h +++ b/Source/Engine/Content/Loading/Tasks/LoadAssetTask.h @@ -15,38 +15,35 @@ class LoadAssetTask : public ContentLoadTask { public: - /// /// Initializes a new instance of the class. /// /// The asset to load. LoadAssetTask(Asset* asset) - : ContentLoadTask(Type::LoadAsset) - , Asset(asset) + : Asset(asset) { } ~LoadAssetTask() { - if (Asset) + auto asset = Asset.Get(); + if (asset) { - Asset->Locker.Lock(); - if (Platform::AtomicRead(&Asset->_loadingTask) == (intptr)this) + asset->Locker.Lock(); + if (Platform::AtomicRead(&asset->_loadingTask) == (intptr)this) { - Platform::AtomicStore(&Asset->_loadState, (int64)Asset::LoadState::LoadFailed); - Platform::AtomicStore(&Asset->_loadingTask, 0); + Platform::AtomicStore(&asset->_loadState, (int64)Asset::LoadState::LoadFailed); + Platform::AtomicStore(&asset->_loadingTask, 0); LOG(Error, "Loading asset \'{0}\' result: {1}.", ToString(), ToString(Result::TaskFailed)); } - Asset->Locker.Unlock(); + asset->Locker.Unlock(); } } public: - WeakAssetReference Asset; public: - // [ContentLoadTask] bool HasReference(Object* obj) const override { @@ -54,7 +51,6 @@ public: } protected: - // [ContentLoadTask] Result run() override { @@ -68,32 +64,36 @@ protected: // Call loading if (ref->onLoad(this)) return Result::AssetLoadError; - return Result::Ok; } + void OnFail() override { - if (Asset) + auto asset = Asset.Get(); + if (asset) { - Asset->Locker.Lock(); - if (Platform::AtomicRead(&Asset->_loadingTask) == (intptr)this) - Platform::AtomicStore(&Asset->_loadingTask, 0); - Asset->Locker.Unlock(); Asset = nullptr; + asset->Locker.Lock(); + if (Platform::AtomicRead(&asset->_loadingTask) == (intptr)this) + Platform::AtomicStore(&asset->_loadingTask, 0); + asset->Locker.Unlock(); } // Base ContentLoadTask::OnFail(); } + void OnEnd() override { - if (Asset) + auto asset = Asset.Get(); + if (asset) { - Asset->Locker.Lock(); - if (Platform::AtomicRead(&Asset->_loadingTask) == (intptr)this) - Platform::AtomicStore(&Asset->_loadingTask, 0); - Asset->Locker.Unlock(); Asset = nullptr; + asset->Locker.Lock(); + if (Platform::AtomicRead(&asset->_loadingTask) == (intptr)this) + Platform::AtomicStore(&asset->_loadingTask, 0); + asset->Locker.Unlock(); + asset = nullptr; } // Base diff --git a/Source/Engine/Threading/Task.h b/Source/Engine/Threading/Task.h index d2d77a2e9..c741da73d 100644 --- a/Source/Engine/Threading/Task.h +++ b/Source/Engine/Threading/Task.h @@ -7,7 +7,6 @@ #include "Engine/Core/NonCopyable.h" #include "Engine/Core/Enums.h" #include "Engine/Core/Types/TimeSpan.h" -#include "Engine/Core/Collections/Array.h" #include "Engine/Platform/Platform.h" /// @@ -188,8 +187,8 @@ public: /// The tasks list to wait for. /// The maximum amount of milliseconds to wait for the task to finish it's job. Timeout smaller/equal 0 will result in infinite waiting. /// True if any task failed or has been canceled or has timeout, otherwise false. - template - static bool WaitAll(Array& tasks, double timeoutMilliseconds = -1) + template + static bool WaitAll(Array& tasks, double timeoutMilliseconds = -1) { for (int32 i = 0; i < tasks.Count(); i++) { @@ -300,27 +299,22 @@ public: /// /// Cancels all the tasks from the list and clears it. /// - template - static void CancelAll(Array& tasks) + template + static void CancelAll(Array& tasks) { for (int32 i = 0; i < tasks.Count(); i++) - { tasks[i]->Cancel(); - } tasks.Clear(); } protected: /// - /// Executes this task. - /// It should be called by the task consumer (thread pool or other executor of this task type). - /// It calls run() and handles result). + /// Executes this task. It should be called by the task consumer (thread pool or other executor of this task type). It calls run() and handles result). /// void Execute(); /// - /// Runs the task specified operations - /// Does not handles any task related logic, only performs the actual job. + /// Runs the task specified operations. It does not handle any task related logic, but only performs the actual job. /// /// The task execution result. Returns true if failed, otherwise false. virtual bool Run() = 0; From b7dc0dd0044a0d8fe1b4183b132960706c8ff0dc Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 22 Apr 2024 18:11:25 +0200 Subject: [PATCH 30/43] Fix crash when removing Anim Event visual script that is used in opened Animation timeline #2471 --- .../Editor/Content/Items/VisualScriptItem.cs | 16 ++++++++++ .../Timeline/Tracks/AnimationEventTrack.cs | 32 +++++++++++++++---- Source/Editor/Scripting/ScriptType.Custom.cs | 6 ++++ .../Editor/Scripting/ScriptType.Interfaces.cs | 6 ++++ Source/Editor/Scripting/ScriptType.cs | 18 ++++++++--- .../Editor/Surface/Archetypes/BehaviorTree.cs | 11 +++++++ 6 files changed, 78 insertions(+), 11 deletions(-) diff --git a/Source/Editor/Content/Items/VisualScriptItem.cs b/Source/Editor/Content/Items/VisualScriptItem.cs index 1d2e6a0bc..b99125f9b 100644 --- a/Source/Editor/Content/Items/VisualScriptItem.cs +++ b/Source/Editor/Content/Items/VisualScriptItem.cs @@ -249,6 +249,7 @@ namespace FlaxEditor.Content private ScriptMemberInfo[] _parameters; private ScriptMemberInfo[] _methods; private object[] _attributes; + private List> _disposing; /// /// Gets the Visual Script asset that contains this type. @@ -310,6 +311,13 @@ namespace FlaxEditor.Content internal void Dispose() { + if (_disposing != null) + { + foreach (var e in _disposing) + e(new ScriptType(this)); + _disposing.Clear(); + _disposing = null; + } if (_parameters != null) { OnAssetReloading(_asset); @@ -510,6 +518,14 @@ namespace FlaxEditor.Content } return _methods; } + + /// + public void TrackLifetime(Action disposing) + { + if (_disposing == null) + _disposing = new List>(); + _disposing.Add(disposing); + } } /// diff --git a/Source/Editor/GUI/Timeline/Tracks/AnimationEventTrack.cs b/Source/Editor/GUI/Timeline/Tracks/AnimationEventTrack.cs index ef0cef279..bf46e36ab 100644 --- a/Source/Editor/GUI/Timeline/Tracks/AnimationEventTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/AnimationEventTrack.cs @@ -99,6 +99,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks if (Type == ScriptType.Null) { Editor.LogError("Missing anim event type " + _instanceTypeName); + InitMissing(); return; } Instance = (AnimEvent)Type.CreateInstance(); @@ -125,20 +126,37 @@ namespace FlaxEditor.GUI.Timeline.Tracks _isRegisteredForScriptsReload = true; ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin; } + Type.TrackLifetime(OnTypeDisposing); + } + + private void OnTypeDisposing(ScriptType type) + { + if (Type == type && !IsDisposing) + { + // Turn into missing script + OnScriptsReloadBegin(); + ScriptsBuilder.ScriptsReloadEnd -= OnScriptsReloadEnd; + InitMissing(); + } + } + + private void InitMissing() + { + CanDelete = true; + CanSplit = false; + CanResize = false; + TooltipText = $"Missing Anim Event Type '{_instanceTypeName}'"; + BackgroundColor = Color.Red; + Type = ScriptType.Null; + Instance = null; } internal void InitMissing(string typeName, byte[] data) { - Type = ScriptType.Null; IsContinuous = false; - CanDelete = true; - CanSplit = false; - CanResize = false; - TooltipText = $"Missing Anim Event Type '{typeName}'"; - Instance = null; - BackgroundColor = Color.Red; _instanceTypeName = typeName; _instanceData = data; + InitMissing(); } internal void Load(BinaryReader stream) diff --git a/Source/Editor/Scripting/ScriptType.Custom.cs b/Source/Editor/Scripting/ScriptType.Custom.cs index 6da34f448..49bdd6013 100644 --- a/Source/Editor/Scripting/ScriptType.Custom.cs +++ b/Source/Editor/Scripting/ScriptType.Custom.cs @@ -144,5 +144,11 @@ namespace FlaxEditor.Scripting { return Utils.GetEmptyArray(); } + + /// + public void TrackLifetime(Action disposing) + { + ElementType.TrackLifetime(disposing); + } } } diff --git a/Source/Editor/Scripting/ScriptType.Interfaces.cs b/Source/Editor/Scripting/ScriptType.Interfaces.cs index 2aac4babc..01a4755ec 100644 --- a/Source/Editor/Scripting/ScriptType.Interfaces.cs +++ b/Source/Editor/Scripting/ScriptType.Interfaces.cs @@ -167,6 +167,12 @@ namespace FlaxEditor.Scripting /// A bitmask comprised of one or more that specify how the search is conducted.-or- Zero (), to return an empty array. /// An array of member objects representing all methods defined for the current type that match the specified binding constraints.-or- An empty array of type member, if no methods are defined for the current type, or if none of the defined methods match the binding constraints. ScriptMemberInfo[] GetMethods(BindingFlags bindingAttr); + + /// + /// Registers delegate to be invoked upon script type disposal (except hot-reload in Editor via ). For example, can happen when user deleted Visual Script asset. + /// + /// Event to call when script type gets disposed (eg. removed asset). + void TrackLifetime(Action disposing); } /// diff --git a/Source/Editor/Scripting/ScriptType.cs b/Source/Editor/Scripting/ScriptType.cs index 5a5a3ac93..848fda30e 100644 --- a/Source/Editor/Scripting/ScriptType.cs +++ b/Source/Editor/Scripting/ScriptType.cs @@ -1395,11 +1395,21 @@ namespace FlaxEditor.Scripting } /// - /// Basic check to see if a type could be casted to another type + /// Registers delegate to be invoked upon script type disposal (except hot-reload in Editor via ). For example, can happen when user deleted Visual Script asset. + /// + /// Event to call when script type gets disposed (eg. removed asset). + public void TrackLifetime(Action disposing) + { + if (_custom != null) + _custom.TrackLifetime(disposing); + } + + /// + /// Basic check to see if a type could be cast to another type /// /// Source type /// Target type - /// True if the type can be casted + /// True if the type can be cast. public static bool CanCast(ScriptType from, ScriptType to) { if (from == to) @@ -1412,10 +1422,10 @@ namespace FlaxEditor.Scripting } /// - /// Basic check to see if this type could be casted to another type + /// Basic check to see if this type could be cast to another type /// /// Target type - /// True if the type can be casted + /// True if the type can be cast. public bool CanCastTo(ScriptType to) { return CanCast(this, to); diff --git a/Source/Editor/Surface/Archetypes/BehaviorTree.cs b/Source/Editor/Surface/Archetypes/BehaviorTree.cs index 1fa4f8402..c4bb13c5e 100644 --- a/Source/Editor/Surface/Archetypes/BehaviorTree.cs +++ b/Source/Editor/Surface/Archetypes/BehaviorTree.cs @@ -104,6 +104,16 @@ namespace FlaxEditor.Surface.Archetypes } } + private void OnTypeDisposing(ScriptType type) + { + if (_type == type && !IsDisposing) + { + // Turn into missing script + _type = ScriptType.Null; + Instance = null; + } + } + public override void OnLoaded(SurfaceNodeActions action) { base.OnLoaded(action); @@ -113,6 +123,7 @@ namespace FlaxEditor.Surface.Archetypes _type = TypeUtils.GetType(typeName); if (_type != null) { + _type.TrackLifetime(OnTypeDisposing); TooltipText = Editor.Instance.CodeDocs.GetTooltip(_type); try { From a01495cde112de4eb1a67627839f261b6e2f3cd2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 22 Apr 2024 18:36:25 +0200 Subject: [PATCH 31/43] Merge similar source chunks #2468 --- Source/Engine/Scripting/Scripting.cpp | 102 +++++++++++--------------- 1 file changed, 41 insertions(+), 61 deletions(-) diff --git a/Source/Engine/Scripting/Scripting.cpp b/Source/Engine/Scripting/Scripting.cpp index d6f2524c7..23ac2a935 100644 --- a/Source/Engine/Scripting/Scripting.cpp +++ b/Source/Engine/Scripting/Scripting.cpp @@ -109,6 +109,45 @@ namespace #if USE_EDITOR bool LastBinariesLoadTriggeredCompilation = false; #endif + + void ReleaseObjects(bool gameOnly) + { + // Flush objects already enqueued objects to delete + ObjectsRemovalService::Flush(); + + // Give GC a try to cleanup old user objects and the other mess + MCore::GC::Collect(); + MCore::GC::WaitForPendingFinalizers(); + + // Destroy objects from game assemblies (eg. not released objects that might crash if persist in memory after reload) + const auto flaxModule = GetBinaryModuleFlaxEngine(); + _objectsLocker.Lock(); + for (auto i = _objectsDictionary.Begin(); i.IsNotEnd(); ++i) + { + auto obj = i->Value; + if (gameOnly && obj->GetTypeHandle().Module == flaxModule) + continue; + +#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING + LOG(Info, "[OnScriptingDispose] obj = 0x{0:x}, {1}", (uint64)obj.Ptr, String(obj.TypeName)); +#endif + obj->OnScriptingDispose(); + } + _objectsLocker.Unlock(); + + // Release assets sourced from game assemblies + Array assets = Content::GetAssets(); + for (auto asset : assets) + { + if (asset->GetTypeHandle().Module == flaxModule) + { + continue; + } + + asset->DeleteObject(); + } + ObjectsRemovalService::Flush(); + } } Delegate Scripting::BinaryModuleLoaded; @@ -566,36 +605,8 @@ void Scripting::Release() // Fire event ScriptsUnload(); - // Cleanup - ObjectsRemovalService::Flush(); - - // Cleanup some managed objects - MCore::GC::Collect(); - MCore::GC::WaitForPendingFinalizers(); - // Release managed objects instances for persistent objects (assets etc.) - _objectsLocker.Lock(); - { - for (auto i = _objectsDictionary.Begin(); i.IsNotEnd(); ++i) - { - auto obj = i->Value; -#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING - LOG(Info, "[OnScriptingDispose] obj = 0x{0:x}, {1}", (uint64)obj.Ptr, String(obj.TypeName)); -#endif - obj->OnScriptingDispose(); - } - } - _objectsLocker.Unlock(); - - // Release assets sourced from game assemblies - const auto flaxModule = GetBinaryModuleFlaxEngine(); - for (auto asset : Content::GetAssets()) - { - if (asset->GetTypeHandle().Module == flaxModule) - continue; - - asset->DeleteObjectNow(); - } + ReleaseObjects(false); auto* flaxEngineModule = (NativeBinaryModule*)GetBinaryModuleFlaxEngine(); onEngineUnloading(flaxEngineModule->Assembly); @@ -673,39 +684,8 @@ void Scripting::Reload(bool canTriggerSceneReload) LOG(Info, "Start user scripts reload"); ScriptsReloading(); - // Flush cache (some objects may be deleted after reload start event) - ObjectsRemovalService::Flush(); - - // Give GC a try to cleanup old user objects and the other mess - MCore::GC::Collect(); - MCore::GC::WaitForPendingFinalizers(); - // Destroy objects from game assemblies (eg. not released objects that might crash if persist in memory after reload) - const auto flaxModule = GetBinaryModuleFlaxEngine(); - _objectsLocker.Lock(); - { - for (auto i = _objectsDictionary.Begin(); i.IsNotEnd(); ++i) - { - auto obj = i->Value; - if (obj->GetTypeHandle().Module == flaxModule) - continue; - -#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING - LOG(Info, "[OnScriptingDispose] obj = 0x{0:x}, {1}", (uint64)obj.Ptr, String(obj.TypeName)); -#endif - obj->OnScriptingDispose(); - } - } - _objectsLocker.Unlock(); - - // Release assets sourced from game assemblies - for (auto asset : Content::GetAssets()) - { - if (asset->GetTypeHandle().Module == flaxModule) - continue; - - asset->DeleteObjectNow(); - } + ReleaseObjects(true); // Unload all game modules LOG(Info, "Unloading game binary modules"); From 3ecbbcd0a07ac3de8a76aa47038d566dca54f615 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 22 Apr 2024 18:44:50 +0200 Subject: [PATCH 32/43] Update `Newtonsoft.Json` --- Source/Platforms/DotNet/Newtonsoft.Json.dll | 2 +- Source/Platforms/DotNet/Newtonsoft.Json.pdb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Platforms/DotNet/Newtonsoft.Json.dll b/Source/Platforms/DotNet/Newtonsoft.Json.dll index b56719858..52dc74149 100644 --- a/Source/Platforms/DotNet/Newtonsoft.Json.dll +++ b/Source/Platforms/DotNet/Newtonsoft.Json.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2189e00df470e0fa7e3294a90a2faf286af51bd25f224605faf53cc42a7ff381 +oid sha256:5a3db675abf8c06f537cba70efa992723a93749e5771e24b8c911a90ccaea5a5 size 604672 diff --git a/Source/Platforms/DotNet/Newtonsoft.Json.pdb b/Source/Platforms/DotNet/Newtonsoft.Json.pdb index 97d99bb47..01e0fd1ce 100644 --- a/Source/Platforms/DotNet/Newtonsoft.Json.pdb +++ b/Source/Platforms/DotNet/Newtonsoft.Json.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cd30824b5d1593411aa7c47816afb37f63ba4ec4f82c5777324d0411c853a912 -size 239516 +oid sha256:dbb3be761269e1f66da40e85bf426841bbdaf6c9e5e8df1a5771e82d96aaf999 +size 239564 From 96f628429c59ceeac6d31f83e006d22777e05094 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 22 Apr 2024 18:56:54 +0200 Subject: [PATCH 33/43] Improve `const` correctness in `Mesh` and `SkinnedMesh` api #2467i --- Source/Engine/Graphics/GPUBufferDescription.h | 4 +- Source/Engine/Graphics/Models/Mesh.cpp | 40 +++++++++---------- Source/Engine/Graphics/Models/Mesh.h | 26 ++++++------ Source/Engine/Graphics/Models/SkinnedMesh.cpp | 12 +++--- Source/Engine/Graphics/Models/SkinnedMesh.h | 14 +++---- 5 files changed, 47 insertions(+), 49 deletions(-) diff --git a/Source/Engine/Graphics/GPUBufferDescription.h b/Source/Engine/Graphics/GPUBufferDescription.h index e93541d7a..d4049ea5b 100644 --- a/Source/Engine/Graphics/GPUBufferDescription.h +++ b/Source/Engine/Graphics/GPUBufferDescription.h @@ -182,7 +182,7 @@ public: /// The elements count. /// The data. /// The buffer description. - static GPUBufferDescription Vertex(int32 elementStride, int32 elementsCount, void* data) + static GPUBufferDescription Vertex(int32 elementStride, int32 elementsCount, const void* data) { return Buffer(elementsCount * elementStride, GPUBufferFlags::VertexBuffer, PixelFormat::Unknown, data, elementStride, GPUResourceUsage::Default); } @@ -217,7 +217,7 @@ public: /// The elements count. /// The data. /// The buffer description. - static GPUBufferDescription Index(int32 elementStride, int32 elementsCount, void* data) + static GPUBufferDescription Index(int32 elementStride, int32 elementsCount, const void* data) { const auto format = elementStride == 4 ? PixelFormat::R32_UInt : PixelFormat::R16_UInt; return Buffer(elementsCount * elementStride, GPUBufferFlags::IndexBuffer, format, data, elementStride, GPUResourceUsage::Default); diff --git a/Source/Engine/Graphics/Models/Mesh.cpp b/Source/Engine/Graphics/Models/Mesh.cpp index 8aa090325..567dbeba6 100644 --- a/Source/Engine/Graphics/Models/Mesh.cpp +++ b/Source/Engine/Graphics/Models/Mesh.cpp @@ -24,7 +24,7 @@ namespace { template - bool UpdateMesh(Mesh* mesh, uint32 vertexCount, uint32 triangleCount, Float3* vertices, IndexType* triangles, Float3* normals, Float3* tangents, Float2* uvs, Color32* colors) + bool UpdateMesh(Mesh* mesh, uint32 vertexCount, uint32 triangleCount, const Float3* vertices, const IndexType* triangles, const Float3* normals, const Float3* tangents, const Float2* uvs, const Color32* colors) { auto model = mesh->GetModel(); CHECK_RETURN(model && model->IsVirtual(), true); @@ -63,40 +63,39 @@ namespace const auto t = Float1010102(Float3::UnitX); for (uint32 i = 0; i < vertexCount; i++) { - vb1[i].Normal = n; - vb1[i].Tangent = t; + vb1.Get()[i].Normal = n; + vb1.Get()[i].Tangent = t; } } if (uvs) { for (uint32 i = 0; i < vertexCount; i++) - vb1[i].TexCoord = Half2(uvs[i]); + vb1.Get()[i].TexCoord = Half2(uvs[i]); } else { auto v = Half2::Zero; for (uint32 i = 0; i < vertexCount; i++) - vb1[i].TexCoord = v; + vb1.Get()[i].TexCoord = v; } { auto v = Half2::Zero; for (uint32 i = 0; i < vertexCount; i++) - vb1[i].LightmapUVs = v; + vb1.Get()[i].LightmapUVs = v; } if (colors) { vb2.Resize(vertexCount); for (uint32 i = 0; i < vertexCount; i++) - vb2[i].Color = colors[i]; + vb2.Get()[i].Color = colors[i]; } return mesh->UpdateMesh(vertexCount, triangleCount, (VB0ElementType*)vertices, vb1.Get(), vb2.HasItems() ? vb2.Get() : nullptr, triangles); } #if !COMPILE_WITHOUT_CSHARP - template - bool UpdateMesh(Mesh* mesh, uint32 vertexCount, uint32 triangleCount, MArray* verticesObj, MArray* trianglesObj, MArray* normalsObj, MArray* tangentsObj, MArray* uvObj, MArray* colorsObj) + bool UpdateMesh(Mesh* mesh, uint32 vertexCount, uint32 triangleCount, const MArray* verticesObj, const MArray* trianglesObj, const MArray* normalsObj, const MArray* tangentsObj, const MArray* uvObj, const MArray* colorsObj) { ASSERT((uint32)MCore::Array::GetLength(verticesObj) >= vertexCount); ASSERT((uint32)MCore::Array::GetLength(trianglesObj) / 3 >= triangleCount); @@ -110,7 +109,7 @@ namespace } template - bool UpdateTriangles(Mesh* mesh, int32 triangleCount, MArray* trianglesObj) + bool UpdateTriangles(Mesh* mesh, int32 triangleCount, const MArray* trianglesObj) { const auto model = mesh->GetModel(); ASSERT(model && model->IsVirtual() && trianglesObj); @@ -121,7 +120,6 @@ namespace return mesh->UpdateTriangles(triangleCount, ib); } - #endif } @@ -130,7 +128,7 @@ bool Mesh::HasVertexColors() const return _vertexBuffers[2] != nullptr && _vertexBuffers[2]->IsAllocated(); } -bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, void* ib, bool use16BitIndices) +bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, const VB0ElementType* vb0, const VB1ElementType* vb1, const VB2ElementType* vb2, const void* ib, bool use16BitIndices) { auto model = (Model*)_model; @@ -145,7 +143,7 @@ bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* // Calculate mesh bounds BoundingBox bounds; - BoundingBox::FromPoints((Float3*)vb0, vertexCount, bounds); + BoundingBox::FromPoints((const Float3*)vb0, vertexCount, bounds); SetBounds(bounds); // Send event (actors using this model can update bounds, etc.) @@ -155,17 +153,17 @@ bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* return failed; } -bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, Float3* vertices, uint16* triangles, Float3* normals, Float3* tangents, Float2* uvs, Color32* colors) +bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, const Float3* vertices, const uint16* triangles, const Float3* normals, const Float3* tangents, const Float2* uvs, const Color32* colors) { return ::UpdateMesh(this, vertexCount, triangleCount, vertices, triangles, normals, tangents, uvs, colors); } -bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, Float3* vertices, uint32* triangles, Float3* normals, Float3* tangents, Float2* uvs, Color32* colors) +bool Mesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, const Float3* vertices, const uint32* triangles, const Float3* normals, const Float3* tangents, const Float2* uvs, const Color32* colors) { return ::UpdateMesh(this, vertexCount, triangleCount, vertices, triangles, normals, tangents, uvs, colors); } -bool Mesh::UpdateTriangles(uint32 triangleCount, void* ib, bool use16BitIndices) +bool Mesh::UpdateTriangles(uint32 triangleCount, const void* ib, bool use16BitIndices) { // Cache data uint32 indicesCount = triangleCount * 3; @@ -217,7 +215,7 @@ Mesh::~Mesh() SAFE_DELETE_GPU_RESOURCE(_indexBuffer); } -bool Mesh::Load(uint32 vertices, uint32 triangles, void* vb0, void* vb1, void* vb2, void* ib, bool use16BitIndexBuffer) +bool Mesh::Load(uint32 vertices, uint32 triangles, const void* vb0, const void* vb1, const void* vb2, const void* ib, bool use16BitIndexBuffer) { // Cache data uint32 indicesCount = triangles * 3; @@ -697,22 +695,22 @@ ScriptingObject* Mesh::GetParentModel() #if !COMPILE_WITHOUT_CSHARP -bool Mesh::UpdateMeshUInt(int32 vertexCount, int32 triangleCount, MArray* verticesObj, MArray* trianglesObj, MArray* normalsObj, MArray* tangentsObj, MArray* uvObj, MArray* colorsObj) +bool Mesh::UpdateMeshUInt(int32 vertexCount, int32 triangleCount, const MArray* verticesObj, const MArray* trianglesObj, const MArray* normalsObj, const MArray* tangentsObj, const MArray* uvObj, const MArray* colorsObj) { return ::UpdateMesh(this, (uint32)vertexCount, (uint32)triangleCount, verticesObj, trianglesObj, normalsObj, tangentsObj, uvObj, colorsObj); } -bool Mesh::UpdateMeshUShort(int32 vertexCount, int32 triangleCount, MArray* verticesObj, MArray* trianglesObj, MArray* normalsObj, MArray* tangentsObj, MArray* uvObj, MArray* colorsObj) +bool Mesh::UpdateMeshUShort(int32 vertexCount, int32 triangleCount, const MArray* verticesObj, const MArray* trianglesObj, const MArray* normalsObj, const MArray* tangentsObj, const MArray* uvObj, const MArray* colorsObj) { return ::UpdateMesh(this, (uint32)vertexCount, (uint32)triangleCount, verticesObj, trianglesObj, normalsObj, tangentsObj, uvObj, colorsObj); } -bool Mesh::UpdateTrianglesUInt(int32 triangleCount, MArray* trianglesObj) +bool Mesh::UpdateTrianglesUInt(int32 triangleCount, const MArray* trianglesObj) { return ::UpdateTriangles(this, triangleCount, trianglesObj); } -bool Mesh::UpdateTrianglesUShort(int32 triangleCount, MArray* trianglesObj) +bool Mesh::UpdateTrianglesUShort(int32 triangleCount, const MArray* trianglesObj) { return ::UpdateTriangles(this, triangleCount, trianglesObj); } diff --git a/Source/Engine/Graphics/Models/Mesh.h b/Source/Engine/Graphics/Models/Mesh.h index 4885ce3cf..9b229fc5b 100644 --- a/Source/Engine/Graphics/Models/Mesh.h +++ b/Source/Engine/Graphics/Models/Mesh.h @@ -112,7 +112,7 @@ public: /// The third vertex buffer data. /// The index buffer in clockwise order. /// True if failed, otherwise false. - FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, uint32* ib) + FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, const VB0ElementType* vb0, const VB1ElementType* vb1, const VB2ElementType* vb2, const uint32* ib) { return UpdateMesh(vertexCount, triangleCount, vb0, vb1, vb2, ib, false); } @@ -127,7 +127,7 @@ public: /// The third vertex buffer data. /// The index buffer in clockwise order. /// True if failed, otherwise false. - FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, uint16* ib) + FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, const VB0ElementType* vb0, const VB1ElementType* vb1, const VB2ElementType* vb2, const uint16* ib) { return UpdateMesh(vertexCount, triangleCount, vb0, vb1, vb2, ib, true); } @@ -145,7 +145,7 @@ public: /// The index buffer in clockwise order. /// True if index buffer uses 16-bit index buffer, otherwise 32-bit. /// True if failed, otherwise false. - bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0ElementType* vb0, VB1ElementType* vb1, VB2ElementType* vb2, void* ib, bool use16BitIndices); + bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, const VB0ElementType* vb0, const VB1ElementType* vb1, const VB2ElementType* vb2, const void* ib, bool use16BitIndices); /// /// Updates the model mesh (used by the virtual models created with Init rather than Load). @@ -161,7 +161,7 @@ public: /// The texture coordinates (per vertex). /// The vertex colors (per vertex). /// True if failed, otherwise false. - bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, Float3* vertices, uint16* triangles, Float3* normals = nullptr, Float3* tangents = nullptr, Float2* uvs = nullptr, Color32* colors = nullptr); + bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, const Float3* vertices, const uint16* triangles, const Float3* normals = nullptr, const Float3* tangents = nullptr, const Float2* uvs = nullptr, const Color32* colors = nullptr); /// /// Updates the model mesh (used by the virtual models created with Init rather than Load). @@ -177,7 +177,7 @@ public: /// The texture coordinates (per vertex). /// The vertex colors (per vertex). /// True if failed, otherwise false. - bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, Float3* vertices, uint32* triangles, Float3* normals = nullptr, Float3* tangents = nullptr, Float2* uvs = nullptr, Color32* colors = nullptr); + bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, const Float3* vertices, const uint32* triangles, const Float3* normals = nullptr, const Float3* tangents = nullptr, const Float2* uvs = nullptr, const Color32* colors = nullptr); public: /// @@ -186,7 +186,7 @@ public: /// The amount of triangles in the index buffer. /// The index buffer. /// True if failed, otherwise false. - FORCE_INLINE bool UpdateTriangles(uint32 triangleCount, uint32* ib) + FORCE_INLINE bool UpdateTriangles(uint32 triangleCount, const uint32* ib) { return UpdateTriangles(triangleCount, ib, false); } @@ -197,7 +197,7 @@ public: /// The amount of triangles in the index buffer. /// The index buffer. /// True if failed, otherwise false. - FORCE_INLINE bool UpdateTriangles(uint32 triangleCount, uint16* ib) + FORCE_INLINE bool UpdateTriangles(uint32 triangleCount, const uint16* ib) { return UpdateTriangles(triangleCount, ib, true); } @@ -209,7 +209,7 @@ public: /// The index buffer. /// True if index buffer uses 16-bit index buffer, otherwise 32-bit. /// True if failed, otherwise false. - bool UpdateTriangles(uint32 triangleCount, void* ib, bool use16BitIndices); + bool UpdateTriangles(uint32 triangleCount, const void* ib, bool use16BitIndices); public: /// @@ -235,7 +235,7 @@ public: /// Index buffer data /// True if use 16 bit indices for the index buffer (true: uint16, false: uint32). /// True if cannot load data, otherwise false. - bool Load(uint32 vertices, uint32 triangles, void* vb0, void* vb1, void* vb2, void* ib, bool use16BitIndexBuffer); + bool Load(uint32 vertices, uint32 triangles, const void* vb0, const void* vb1, const void* vb2, const void* ib, bool use16BitIndexBuffer); /// /// Unloads the mesh data (vertex buffers and cache). The opposite to Load. @@ -315,10 +315,10 @@ private: // Internal bindings API_FUNCTION(NoProxy) ScriptingObject* GetParentModel(); #if !COMPILE_WITHOUT_CSHARP - API_FUNCTION(NoProxy) bool UpdateMeshUInt(int32 vertexCount, int32 triangleCount, MArray* verticesObj, MArray* trianglesObj, MArray* normalsObj, MArray* tangentsObj, MArray* uvObj, MArray* colorsObj); - API_FUNCTION(NoProxy) bool UpdateMeshUShort(int32 vertexCount, int32 triangleCount, MArray* verticesObj, MArray* trianglesObj, MArray* normalsObj, MArray* tangentsObj, MArray* uvObj, MArray* colorsObj); - API_FUNCTION(NoProxy) bool UpdateTrianglesUInt(int32 triangleCount, MArray* trianglesObj); - API_FUNCTION(NoProxy) bool UpdateTrianglesUShort(int32 triangleCount, MArray* trianglesObj); + API_FUNCTION(NoProxy) bool UpdateMeshUInt(int32 vertexCount, int32 triangleCount, const MArray* verticesObj, const MArray* trianglesObj, const MArray* normalsObj, const MArray* tangentsObj, const MArray* uvObj, const MArray* colorsObj); + API_FUNCTION(NoProxy) bool UpdateMeshUShort(int32 vertexCount, int32 triangleCount, const MArray* verticesObj, const MArray* trianglesObj, const MArray* normalsObj, const MArray* tangentsObj, const MArray* uvObj, const MArray* colorsObj); + API_FUNCTION(NoProxy) bool UpdateTrianglesUInt(int32 triangleCount, const MArray* trianglesObj); + API_FUNCTION(NoProxy) bool UpdateTrianglesUShort(int32 triangleCount, const MArray* trianglesObj); API_FUNCTION(NoProxy) MArray* DownloadBuffer(bool forceGpu, MTypeObject* resultType, int32 typeI); #endif }; diff --git a/Source/Engine/Graphics/Models/SkinnedMesh.cpp b/Source/Engine/Graphics/Models/SkinnedMesh.cpp index 8ee9599b0..14542ef91 100644 --- a/Source/Engine/Graphics/Models/SkinnedMesh.cpp +++ b/Source/Engine/Graphics/Models/SkinnedMesh.cpp @@ -105,7 +105,7 @@ SkinnedMesh::~SkinnedMesh() SAFE_DELETE_GPU_RESOURCE(_indexBuffer); } -bool SkinnedMesh::Load(uint32 vertices, uint32 triangles, void* vb0, void* ib, bool use16BitIndexBuffer) +bool SkinnedMesh::Load(uint32 vertices, uint32 triangles, const void* vb0, const void* ib, bool use16BitIndexBuffer) { // Cache data uint32 indicesCount = triangles * 3; @@ -159,7 +159,7 @@ void SkinnedMesh::Unload() _use16BitIndexBuffer = false; } -bool SkinnedMesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0SkinnedElementType* vb, void* ib, bool use16BitIndices) +bool SkinnedMesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, const VB0SkinnedElementType* vb, const void* ib, bool use16BitIndices) { auto model = (SkinnedModel*)_model; @@ -169,7 +169,7 @@ bool SkinnedMesh::UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0Skinne { // Calculate mesh bounds BoundingBox bounds; - BoundingBox::FromPoints((Float3*)vb, vertexCount, bounds); + BoundingBox::FromPoints((const Float3*)vb, vertexCount, bounds); SetBounds(bounds); // Send event (actors using this model can update bounds, etc.) @@ -429,7 +429,7 @@ ScriptingObject* SkinnedMesh::GetParentModel() #if !COMPILE_WITHOUT_CSHARP template -bool UpdateMesh(SkinnedMesh* mesh, MArray* verticesObj, MArray* trianglesObj, MArray* blendIndicesObj, MArray* blendWeightsObj, MArray* normalsObj, MArray* tangentsObj, MArray* uvObj) +bool UpdateMesh(SkinnedMesh* mesh, const MArray* verticesObj, const MArray* trianglesObj, const MArray* blendIndicesObj, const MArray* blendWeightsObj, const MArray* normalsObj, const MArray* tangentsObj, const MArray* uvObj) { auto model = mesh->GetSkinnedModel(); ASSERT(model && model->IsVirtual() && verticesObj && trianglesObj && blendIndicesObj && blendWeightsObj); @@ -505,12 +505,12 @@ bool UpdateMesh(SkinnedMesh* mesh, MArray* verticesObj, MArray* trianglesObj, MA return mesh->UpdateMesh(vertexCount, triangleCount, vb.Get(), ib); } -bool SkinnedMesh::UpdateMeshUInt(MArray* verticesObj, MArray* trianglesObj, MArray* blendIndicesObj, MArray* blendWeightsObj, MArray* normalsObj, MArray* tangentsObj, MArray* uvObj) +bool SkinnedMesh::UpdateMeshUInt(const MArray* verticesObj, const MArray* trianglesObj, const MArray* blendIndicesObj, const MArray* blendWeightsObj, const MArray* normalsObj, const MArray* tangentsObj, const MArray* uvObj) { return ::UpdateMesh(this, verticesObj, trianglesObj, blendIndicesObj, blendWeightsObj, normalsObj, tangentsObj, uvObj); } -bool SkinnedMesh::UpdateMeshUShort(MArray* verticesObj, MArray* trianglesObj, MArray* blendIndicesObj, MArray* blendWeightsObj, MArray* normalsObj, MArray* tangentsObj, MArray* uvObj) +bool SkinnedMesh::UpdateMeshUShort(const MArray* verticesObj, const MArray* trianglesObj, const MArray* blendIndicesObj, const MArray* blendWeightsObj, const MArray* normalsObj, const MArray* tangentsObj, const MArray* uvObj) { return ::UpdateMesh(this, verticesObj, trianglesObj, blendIndicesObj, blendWeightsObj, normalsObj, tangentsObj, uvObj); } diff --git a/Source/Engine/Graphics/Models/SkinnedMesh.h b/Source/Engine/Graphics/Models/SkinnedMesh.h index d6ab54f16..963511671 100644 --- a/Source/Engine/Graphics/Models/SkinnedMesh.h +++ b/Source/Engine/Graphics/Models/SkinnedMesh.h @@ -76,7 +76,7 @@ public: /// Index buffer data /// True if use 16 bit indices for the index buffer (true: uint16, false: uint32). /// True if cannot load data, otherwise false. - bool Load(uint32 vertices, uint32 triangles, void* vb0, void* ib, bool use16BitIndexBuffer); + bool Load(uint32 vertices, uint32 triangles, const void* vb0, const void* ib, bool use16BitIndexBuffer); /// /// Unloads the mesh data (vertex buffers and cache). The opposite to Load. @@ -92,7 +92,7 @@ public: /// The vertex buffer data. /// The index buffer in clockwise order. /// True if failed, otherwise false. - FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0SkinnedElementType* vb, int32* ib) + FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, const VB0SkinnedElementType* vb, const int32* ib) { return UpdateMesh(vertexCount, triangleCount, vb, ib, false); } @@ -105,7 +105,7 @@ public: /// The vertex buffer data. /// The index buffer in clockwise order. /// True if failed, otherwise false. - FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0SkinnedElementType* vb, uint32* ib) + FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, const VB0SkinnedElementType* vb, const uint32* ib) { return UpdateMesh(vertexCount, triangleCount, vb, ib, false); } @@ -118,7 +118,7 @@ public: /// The vertex buffer data. /// The index buffer, clockwise order. /// True if failed, otherwise false. - FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0SkinnedElementType* vb, uint16* ib) + FORCE_INLINE bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, const VB0SkinnedElementType* vb, const uint16* ib) { return UpdateMesh(vertexCount, triangleCount, vb, ib, true); } @@ -132,7 +132,7 @@ public: /// The index buffer in clockwise order. /// True if index buffer uses 16-bit index buffer, otherwise 32-bit. /// True if failed, otherwise false. - bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, VB0SkinnedElementType* vb, void* ib, bool use16BitIndices); + bool UpdateMesh(uint32 vertexCount, uint32 triangleCount, const VB0SkinnedElementType* vb, const void* ib, bool use16BitIndices); public: /// @@ -188,8 +188,8 @@ private: // Internal bindings API_FUNCTION(NoProxy) ScriptingObject* GetParentModel(); #if !COMPILE_WITHOUT_CSHARP - API_FUNCTION(NoProxy) bool UpdateMeshUInt(MArray* verticesObj, MArray* trianglesObj, MArray* blendIndicesObj, MArray* blendWeightsObj, MArray* normalsObj, MArray* tangentsObj, MArray* uvObj); - API_FUNCTION(NoProxy) bool UpdateMeshUShort(MArray* verticesObj, MArray* trianglesObj, MArray* blendIndicesObj, MArray* blendWeightsObj, MArray* normalsObj, MArray* tangentsObj, MArray* uvObj); + API_FUNCTION(NoProxy) bool UpdateMeshUInt(const MArray* verticesObj, const MArray* trianglesObj, const MArray* blendIndicesObj, const MArray* blendWeightsObj, const MArray* normalsObj, const MArray* tangentsObj, const MArray* uvObj); + API_FUNCTION(NoProxy) bool UpdateMeshUShort(const MArray* verticesObj, const MArray* trianglesObj, const MArray* blendIndicesObj, const MArray* blendWeightsObj, const MArray* normalsObj, const MArray* tangentsObj, const MArray* uvObj); API_FUNCTION(NoProxy) MArray* DownloadBuffer(bool forceGpu, MTypeObject* resultType, int32 typeI); #endif }; From f19977a9561caf5a199379ed183aa6321e240cbb Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 22 Apr 2024 19:03:40 +0200 Subject: [PATCH 34/43] Fix surface node context menu to not show when moving surface #2466 --- Source/Editor/Surface/SurfaceNode.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index ac5680d8c..2e755f733 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -1062,6 +1062,20 @@ namespace FlaxEditor.Surface } } + /// + public override bool OnMouseDown(Float2 location, MouseButton button) + { + if (base.OnMouseDown(location, button)) + return true; + + if (button == MouseButton.Left && (Archetype.Flags & NodeFlags.NoCloseButton) == 0) + return true; + if (button == MouseButton.Right) + return true; + + return false; + } + /// public override bool OnMouseUp(Float2 location, MouseButton button) { From 515ee96a316b27c9a0bdeb008a83adfcd6fe6487 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 22 Apr 2024 19:10:06 +0200 Subject: [PATCH 35/43] Fix compilation regression --- Source/Engine/Threading/ThreadPool.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Engine/Threading/ThreadPool.cpp b/Source/Engine/Threading/ThreadPool.cpp index 0ffb7c12d..ee72d92ee 100644 --- a/Source/Engine/Threading/ThreadPool.cpp +++ b/Source/Engine/Threading/ThreadPool.cpp @@ -8,6 +8,7 @@ #include "Engine/Core/Log.h" #include "Engine/Core/Math/Math.h" #include "Engine/Core/Types/String.h" +#include "Engine/Core/Collections/Array.h" #include "Engine/Engine/Globals.h" #include "Engine/Engine/EngineService.h" #include "Engine/Platform/ConditionVariable.h" From b92fbcb3bc1e2a93619ebf2e024899ba50e23cbd Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 22 Apr 2024 22:53:27 +0200 Subject: [PATCH 36/43] Add more `const` correctness #2467 --- Source/Engine/Animations/Animations.cpp | 2 +- .../Engine/Animations/SceneAnimations/SceneAnimation.cpp | 2 +- Source/Engine/Animations/SceneAnimations/SceneAnimation.h | 4 ++-- Source/Engine/Content/Assets/Material.cpp | 2 +- Source/Engine/Content/Assets/Material.h | 2 +- Source/Engine/Content/Content.cpp | 6 +++--- Source/Engine/Content/Content.h | 6 +++--- Source/Engine/Content/Storage/FlaxStorage.h | 2 +- Source/Engine/Core/Math/Matrix.cpp | 2 +- Source/Engine/Core/Math/Matrix.h | 2 +- Source/Engine/Core/Math/Rectangle.cpp | 2 +- Source/Engine/Core/Math/Rectangle.h | 2 +- Source/Engine/Core/Types/DataContainer.h | 2 +- Source/Engine/Core/Types/Variant.cpp | 2 +- Source/Engine/Core/Types/Variant.h | 2 +- Source/Engine/Scripting/Scripting.cpp | 8 ++++---- Source/Engine/Scripting/Scripting.h | 8 ++++---- 17 files changed, 28 insertions(+), 28 deletions(-) diff --git a/Source/Engine/Animations/Animations.cpp b/Source/Engine/Animations/Animations.cpp index 1366e18cf..21235daa5 100644 --- a/Source/Engine/Animations/Animations.cpp +++ b/Source/Engine/Animations/Animations.cpp @@ -35,7 +35,7 @@ public: namespace { - FORCE_INLINE bool CanUpdateModel(AnimatedModel* animatedModel) + FORCE_INLINE bool CanUpdateModel(const AnimatedModel* animatedModel) { auto skinnedModel = animatedModel->SkinnedModel.Get(); auto animGraph = animatedModel->AnimationGraph.Get(); diff --git a/Source/Engine/Animations/SceneAnimations/SceneAnimation.cpp b/Source/Engine/Animations/SceneAnimations/SceneAnimation.cpp index 5d75b676c..b3e7a6e27 100644 --- a/Source/Engine/Animations/SceneAnimations/SceneAnimation.cpp +++ b/Source/Engine/Animations/SceneAnimations/SceneAnimation.cpp @@ -35,7 +35,7 @@ const BytesContainer& SceneAnimation::LoadTimeline() #if USE_EDITOR -bool SceneAnimation::SaveTimeline(BytesContainer& data) +bool SceneAnimation::SaveTimeline(const BytesContainer& data) { // Wait for asset to be loaded or don't if last load failed (eg. by shader source compilation error) if (LastLoadFailed()) diff --git a/Source/Engine/Animations/SceneAnimations/SceneAnimation.h b/Source/Engine/Animations/SceneAnimations/SceneAnimation.h index f6beeb416..e08f0c38c 100644 --- a/Source/Engine/Animations/SceneAnimations/SceneAnimation.h +++ b/Source/Engine/Animations/SceneAnimations/SceneAnimation.h @@ -454,10 +454,10 @@ public: /// /// Saves the serialized timeline data to the asset. /// - /// The cannot be used by virtual assets. + /// It cannot be used by virtual assets. /// The timeline data container. /// true failed to save data; otherwise, false. - API_FUNCTION() bool SaveTimeline(BytesContainer& data); + API_FUNCTION() bool SaveTimeline(const BytesContainer& data); #endif diff --git a/Source/Engine/Content/Assets/Material.cpp b/Source/Engine/Content/Assets/Material.cpp index 9a3f7bace..ad373d660 100644 --- a/Source/Engine/Content/Assets/Material.cpp +++ b/Source/Engine/Content/Assets/Material.cpp @@ -550,7 +550,7 @@ BytesContainer Material::LoadSurface(bool createDefaultIfMissing) #if USE_EDITOR -bool Material::SaveSurface(BytesContainer& data, const MaterialInfo& info) +bool Material::SaveSurface(const BytesContainer& data, const MaterialInfo& info) { // Wait for asset to be loaded or don't if last load failed (eg. by shader source compilation error) if (LastLoadFailed()) diff --git a/Source/Engine/Content/Assets/Material.h b/Source/Engine/Content/Assets/Material.h index 569c86d93..e8f398e3d 100644 --- a/Source/Engine/Content/Assets/Material.h +++ b/Source/Engine/Content/Assets/Material.h @@ -32,7 +32,7 @@ public: /// The surface graph data. /// The material info structure. /// True if cannot save it, otherwise false. - API_FUNCTION() bool SaveSurface(BytesContainer& data, const MaterialInfo& info); + API_FUNCTION() bool SaveSurface(const BytesContainer& data, const MaterialInfo& info); #endif diff --git a/Source/Engine/Content/Content.cpp b/Source/Engine/Content/Content.cpp index 39fae81db..fa4517911 100644 --- a/Source/Engine/Content/Content.cpp +++ b/Source/Engine/Content/Content.cpp @@ -403,7 +403,7 @@ ContentStats Content::GetStats() return stats; } -Asset* Content::LoadAsyncInternal(const StringView& internalPath, MClass* type) +Asset* Content::LoadAsyncInternal(const StringView& internalPath, const MClass* type) { CHECK_RETURN(type, nullptr); const auto scriptingType = Scripting::FindScriptingType(type->GetFullName()); @@ -445,7 +445,7 @@ FLAXENGINE_API Asset* LoadAsset(const Guid& id, const ScriptingTypeHandle& type) return Content::LoadAsync(id, type); } -Asset* Content::LoadAsync(const StringView& path, MClass* type) +Asset* Content::LoadAsync(const StringView& path, const MClass* type) { CHECK_RETURN(type, nullptr); const auto scriptingType = Scripting::FindScriptingType(type->GetFullName()); @@ -832,7 +832,7 @@ void Content::UnloadAsset(Asset* asset) asset->DeleteObject(); } -Asset* Content::CreateVirtualAsset(MClass* type) +Asset* Content::CreateVirtualAsset(const MClass* type) { CHECK_RETURN(type, nullptr); const auto scriptingType = Scripting::FindScriptingType(type->GetFullName()); diff --git a/Source/Engine/Content/Content.h b/Source/Engine/Content/Content.h index 56363dd43..5697c17b0 100644 --- a/Source/Engine/Content/Content.h +++ b/Source/Engine/Content/Content.h @@ -164,7 +164,7 @@ public: /// The path of the asset (absolute or relative to the current workspace directory). /// The asset type. If loaded object has different type (excluding types derived from the given) the loading fails. /// Loaded asset or null if cannot - API_FUNCTION(Attributes="HideInEditor") static Asset* LoadAsync(const StringView& path, MClass* type); + API_FUNCTION(Attributes="HideInEditor") static Asset* LoadAsync(const StringView& path, const MClass* type); /// /// Loads asset and holds it until it won't be referenced by any object. Returns null if asset is missing. Actual asset data loading is performed on a other thread in async. @@ -192,7 +192,7 @@ public: /// The path of the asset relative to the engine internal content (excluding the extension). /// The asset type. If loaded object has different type (excluding types derived from the given) the loading fails. /// The loaded asset or null if failed. - API_FUNCTION(Attributes="HideInEditor") static Asset* LoadAsyncInternal(const StringView& internalPath, MClass* type); + API_FUNCTION(Attributes="HideInEditor") static Asset* LoadAsyncInternal(const StringView& internalPath, const MClass* type); /// /// Loads internal engine asset and holds it until it won't be referenced by any object. Returns null if asset is missing. Actual asset data loading is performed on a other thread in async. @@ -342,7 +342,7 @@ public: /// /// The asset type klass. /// Created asset or null if failed. - API_FUNCTION() static Asset* CreateVirtualAsset(API_PARAM(Attributes="TypeReference(typeof(Asset))") MClass* type); + API_FUNCTION() static Asset* CreateVirtualAsset(API_PARAM(Attributes="TypeReference(typeof(Asset))") const MClass* type); /// /// Creates temporary and virtual asset of the given type. diff --git a/Source/Engine/Content/Storage/FlaxStorage.h b/Source/Engine/Content/Storage/FlaxStorage.h index cc04e24a6..3b982c32e 100644 --- a/Source/Engine/Content/Storage/FlaxStorage.h +++ b/Source/Engine/Content/Storage/FlaxStorage.h @@ -440,7 +440,7 @@ public: /// In silent mode don't reload opened storage container that is using target file. /// Custom options. /// True if cannot create package, otherwise false - FORCE_INLINE static bool Create(const StringView& path, AssetInitData& data, bool silentMode = false, const CustomData* customData = nullptr) + FORCE_INLINE static bool Create(const StringView& path, const AssetInitData& data, bool silentMode = false, const CustomData* customData = nullptr) { return Create(path, &data, 1, silentMode, customData); } diff --git a/Source/Engine/Core/Math/Matrix.cpp b/Source/Engine/Core/Math/Matrix.cpp index 8445bd84a..028cc70b0 100644 --- a/Source/Engine/Core/Math/Matrix.cpp +++ b/Source/Engine/Core/Math/Matrix.cpp @@ -773,7 +773,7 @@ void Matrix::Transformation(const Float3& scalingCenter, const Quaternion& scali result = Translation(-scalingCenter) * Transpose(sr) * Scaling(scaling) * sr * Translation(scalingCenter) * Translation(-rotationCenter) * RotationQuaternion(rotation) * Translation(rotationCenter) * Translation(translation); } -void Matrix::Transformation2D(Float2& scalingCenter, float scalingRotation, const Float2& scaling, const Float2& rotationCenter, float rotation, const Float2& translation, Matrix& result) +void Matrix::Transformation2D(const Float2& scalingCenter, float scalingRotation, const Float2& scaling, const Float2& rotationCenter, float rotation, const Float2& translation, Matrix& result) { result = Translation((Float3)-scalingCenter) * RotationZ(-scalingRotation) * Scaling((Float3)scaling) * RotationZ(scalingRotation) * Translation((Float3)scalingCenter) * Translation((Float3)-rotationCenter) * RotationZ(rotation) * Translation((Float3)rotationCenter) * Translation((Float3)translation); result.M33 = 1.0f; diff --git a/Source/Engine/Core/Math/Matrix.h b/Source/Engine/Core/Math/Matrix.h index 0a76a98d1..087d9602c 100644 --- a/Source/Engine/Core/Math/Matrix.h +++ b/Source/Engine/Core/Math/Matrix.h @@ -941,7 +941,7 @@ public: // @param rotation The rotation of the transformation. // @param translation The translation factor of the transformation. // @param result When the method completes, contains the created transformation matrix. - static void Transformation2D(Float2& scalingCenter, float scalingRotation, const Float2& scaling, const Float2& rotationCenter, float rotation, const Float2& translation, Matrix& result); + static void Transformation2D(const Float2& scalingCenter, float scalingRotation, const Float2& scaling, const Float2& rotationCenter, float rotation, const Float2& translation, Matrix& result); // Creates a world matrix with the specified parameters. // @param position Position of the object. This value is used in translation operations. diff --git a/Source/Engine/Core/Math/Rectangle.cpp b/Source/Engine/Core/Math/Rectangle.cpp index 53d56678b..b4d832288 100644 --- a/Source/Engine/Core/Math/Rectangle.cpp +++ b/Source/Engine/Core/Math/Rectangle.cpp @@ -99,7 +99,7 @@ Rectangle Rectangle::FromPoints(const Float2& p1, const Float2& p2) return Rectangle(upperLeft, Math::Max(rightBottom - upperLeft, Float2::Zero)); } -Rectangle Rectangle::FromPoints(Float2* points, int32 pointsCount) +Rectangle Rectangle::FromPoints(const Float2* points, int32 pointsCount) { ASSERT(pointsCount > 0); Float2 upperLeft = points[0]; diff --git a/Source/Engine/Core/Math/Rectangle.h b/Source/Engine/Core/Math/Rectangle.h index 496b8bb8a..6a41fbe0f 100644 --- a/Source/Engine/Core/Math/Rectangle.h +++ b/Source/Engine/Core/Math/Rectangle.h @@ -287,7 +287,7 @@ public: // @returns Rectangle that contains both p1 and p2 static Rectangle FromPoints(const Float2& p1, const Float2& p2); - static Rectangle FromPoints(Float2* points, int32 pointsCount); + static Rectangle FromPoints(const Float2* points, int32 pointsCount); }; template<> diff --git a/Source/Engine/Core/Types/DataContainer.h b/Source/Engine/Core/Types/DataContainer.h index 500041d1c..6ad6f75c9 100644 --- a/Source/Engine/Core/Types/DataContainer.h +++ b/Source/Engine/Core/Types/DataContainer.h @@ -320,7 +320,7 @@ public: /// /// The data. /// The length. - void Append(T* data, int32 length) + void Append(const T* data, int32 length) { if (length <= 0) return; diff --git a/Source/Engine/Core/Types/Variant.cpp b/Source/Engine/Core/Types/Variant.cpp index f30662f86..7061567f7 100644 --- a/Source/Engine/Core/Types/Variant.cpp +++ b/Source/Engine/Core/Types/Variant.cpp @@ -112,7 +112,7 @@ VariantType::VariantType(Types type, const StringAnsiView& typeName) } } -VariantType::VariantType(Types type, MClass* klass) +VariantType::VariantType(Types type, const MClass* klass) { Type = type; TypeName = nullptr; diff --git a/Source/Engine/Core/Types/Variant.h b/Source/Engine/Core/Types/Variant.h index 80205cd36..fa7f748fe 100644 --- a/Source/Engine/Core/Types/Variant.h +++ b/Source/Engine/Core/Types/Variant.h @@ -105,7 +105,7 @@ public: explicit VariantType(Types type, const StringView& typeName); explicit VariantType(Types type, const StringAnsiView& typeName); - explicit VariantType(Types type, MClass* klass); + explicit VariantType(Types type, const MClass* klass); explicit VariantType(const StringAnsiView& typeName); VariantType(const VariantType& other); VariantType(VariantType&& other) noexcept; diff --git a/Source/Engine/Scripting/Scripting.cpp b/Source/Engine/Scripting/Scripting.cpp index 23ac2a935..279e4d986 100644 --- a/Source/Engine/Scripting/Scripting.cpp +++ b/Source/Engine/Scripting/Scripting.cpp @@ -840,7 +840,7 @@ void ScriptingObjectReferenceBase::OnDeleted(ScriptingObject* obj) } } -ScriptingObject* Scripting::FindObject(Guid id, MClass* type) +ScriptingObject* Scripting::FindObject(Guid id, const MClass* type) { if (!id.IsValid()) return nullptr; @@ -894,7 +894,7 @@ ScriptingObject* Scripting::FindObject(Guid id, MClass* type) return nullptr; } -ScriptingObject* Scripting::TryFindObject(Guid id, MClass* type) +ScriptingObject* Scripting::TryFindObject(Guid id, const MClass* type) { if (!id.IsValid()) return nullptr; @@ -930,7 +930,7 @@ ScriptingObject* Scripting::TryFindObject(Guid id, MClass* type) return result; } -ScriptingObject* Scripting::TryFindObject(MClass* type) +ScriptingObject* Scripting::TryFindObject(const MClass* type) { if (type == nullptr) return nullptr; @@ -1000,7 +1000,7 @@ bool Scripting::IsEveryAssemblyLoaded() return true; } -bool Scripting::IsTypeFromGameScripts(MClass* type) +bool Scripting::IsTypeFromGameScripts(const MClass* type) { const auto binaryModule = ManagedBinaryModule::GetModule(type ? type->GetAssembly() : nullptr); return binaryModule && binaryModule != GetBinaryModuleCorlib() && binaryModule != GetBinaryModuleFlaxEngine(); diff --git a/Source/Engine/Scripting/Scripting.h b/Source/Engine/Scripting/Scripting.h index 41b0cbf4d..d9f2c5b4d 100644 --- a/Source/Engine/Scripting/Scripting.h +++ b/Source/Engine/Scripting/Scripting.h @@ -133,14 +133,14 @@ public: /// The object unique identifier. /// The type of the object to find (optional). /// The found object or null if missing. - static ScriptingObject* FindObject(Guid id, MClass* type = nullptr); + static ScriptingObject* FindObject(Guid id, const MClass* type = nullptr); /// /// Tries to find the object by the given class. /// /// The type of the object to find. /// The found object or null if missing. - static ScriptingObject* TryFindObject(MClass* type); + static ScriptingObject* TryFindObject(const MClass* type); /// /// Tries to find the object by the given identifier. @@ -159,7 +159,7 @@ public: /// The object unique identifier. /// The type of the object to find (optional). /// The found object or null if missing. - static ScriptingObject* TryFindObject(Guid id, MClass* type = nullptr); + static ScriptingObject* TryFindObject(Guid id, const MClass* type = nullptr); /// /// Finds the object by the given managed instance handle. Searches only registered scene objects. @@ -189,7 +189,7 @@ public: /// /// Returns true if given type is from one of the game scripts assemblies. /// - static bool IsTypeFromGameScripts(MClass* type); + static bool IsTypeFromGameScripts(const MClass* type); static void ProcessBuildInfoPath(String& path, const String& projectFolderPath); From d8a1de64d1ff0c5a59dfbcc8d2e891f11b74be81 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 22 Apr 2024 22:56:01 +0200 Subject: [PATCH 37/43] Fix surface node moving regression from f19977a9561caf5a199379ed183aa6321e240cbb #2466 --- Source/Editor/Surface/SurfaceNode.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index 2e755f733..0eb739d72 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -1068,7 +1068,7 @@ namespace FlaxEditor.Surface if (base.OnMouseDown(location, button)) return true; - if (button == MouseButton.Left && (Archetype.Flags & NodeFlags.NoCloseButton) == 0) + if (button == MouseButton.Left && (Archetype.Flags & NodeFlags.NoCloseButton) == 0 && _closeButtonRect.Contains(ref location)) return true; if (button == MouseButton.Right) return true; @@ -1083,13 +1083,10 @@ namespace FlaxEditor.Surface return true; // Close - if (button == MouseButton.Left && (Archetype.Flags & NodeFlags.NoCloseButton) == 0) + if (button == MouseButton.Left && (Archetype.Flags & NodeFlags.NoCloseButton) == 0 && _closeButtonRect.Contains(ref location)) { - if (_closeButtonRect.Contains(ref location)) - { - Surface.Delete(this); - return true; - } + Surface.Delete(this); + return true; } // Secondary Context Menu From 890569ea3bdb583726e6097366106148327288e5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 22 Apr 2024 22:59:38 +0200 Subject: [PATCH 38/43] Add logging and cleaning up leaked scene objects after play mode in Editor --- Source/Editor/Managed/ManagedEditor.cpp | 24 ++++++++++++++++++++++ Source/Editor/Managed/ManagedEditor.h | 1 + Source/Editor/States/PlayingState.cs | 2 +- Source/Editor/Utilities/DuplicateScenes.cs | 6 ++++-- Source/Engine/Scripting/Scripting.cpp | 9 ++++++++ Source/Engine/Scripting/Scripting.h | 7 +++++++ 6 files changed, 46 insertions(+), 3 deletions(-) diff --git a/Source/Editor/Managed/ManagedEditor.cpp b/Source/Editor/Managed/ManagedEditor.cpp index 7a9e55b88..c4c71bdfd 100644 --- a/Source/Editor/Managed/ManagedEditor.cpp +++ b/Source/Editor/Managed/ManagedEditor.cpp @@ -16,6 +16,7 @@ #include "Engine/Engine/CommandLine.h" #include "Engine/Renderer/ProbesRenderer.h" #include "Engine/Animations/Graph/AnimGraph.h" +#include "Engine/Core/ObjectsRemovalService.h" ManagedEditor::InternalOptions ManagedEditor::ManagedEditorOptions; @@ -572,6 +573,29 @@ bool ManagedEditor::EvaluateVisualScriptLocal(VisualScript* script, VisualScript return false; } +void ManagedEditor::WipeOutLeftoverSceneObjects() +{ + Array objects = Scripting::GetObjects(); + bool removedAny = false; + for (ScriptingObject* object : objects) + { + if (EnumHasAllFlags(object->Flags, ObjectFlags::IsDuringPlay) && EnumHasNoneFlags(object->Flags, ObjectFlags::WasMarkedToDelete)) + { + if (auto* sceneObject = Cast(object)) + { + if (sceneObject->HasParent()) + continue; // Skip sub-objects + + LOG(Error, "Object '{}' (ID={}, Type={}) is still in memory after play end but should be destroyed (memory leak).", sceneObject->GetNamePath(), sceneObject->GetID(), sceneObject->GetType().ToString()); + sceneObject->DeleteObject(); + removedAny = true; + } + } + } + if (removedAny) + ObjectsRemovalService::Flush(); +} + void ManagedEditor::OnEditorAssemblyLoaded(MAssembly* assembly) { ASSERT(!HasManagedInstance()); diff --git a/Source/Editor/Managed/ManagedEditor.h b/Source/Editor/Managed/ManagedEditor.h index 601b4f2cf..e51749e5f 100644 --- a/Source/Editor/Managed/ManagedEditor.h +++ b/Source/Editor/Managed/ManagedEditor.h @@ -241,6 +241,7 @@ public: API_FUNCTION(Internal) static VisualScriptStackFrame GetVisualScriptPreviousScopeFrame(); API_FUNCTION(Internal) static Array GetVisualScriptLocals(); API_FUNCTION(Internal) static bool EvaluateVisualScriptLocal(VisualScript* script, API_PARAM(Ref) VisualScriptLocal& local); + API_FUNCTION(Internal) static void WipeOutLeftoverSceneObjects(); private: void OnEditorAssemblyLoaded(MAssembly* assembly); diff --git a/Source/Editor/States/PlayingState.cs b/Source/Editor/States/PlayingState.cs index b2654f3d9..69a11cf1b 100644 --- a/Source/Editor/States/PlayingState.cs +++ b/Source/Editor/States/PlayingState.cs @@ -194,7 +194,7 @@ namespace FlaxEditor.States // Restore editor scene SceneRestoring?.Invoke(); - _duplicateScenes.DeletedScenes(); + _duplicateScenes.UnloadScenes(); PluginManager.Internal_DeinitializeGamePlugins(); Editor.Internal_SetPlayMode(false); _duplicateScenes.RestoreSceneData(); diff --git a/Source/Editor/Utilities/DuplicateScenes.cs b/Source/Editor/Utilities/DuplicateScenes.cs index 20979940e..7902633b2 100644 --- a/Source/Editor/Utilities/DuplicateScenes.cs +++ b/Source/Editor/Utilities/DuplicateScenes.cs @@ -120,9 +120,9 @@ namespace FlaxEditor.Utilities /// /// Deletes the creates scenes for the simulation. /// - public void DeletedScenes() + public void UnloadScenes() { - Profiler.BeginEvent("DuplicateScenes.DeletedScenes"); + Profiler.BeginEvent("DuplicateScenes.UnloadScenes"); Editor.Log("Restoring scene data"); // TODO: here we can keep changes for actors marked to keep their state after simulation @@ -134,6 +134,8 @@ namespace FlaxEditor.Utilities throw new Exception("Failed to unload scenes."); } FlaxEngine.Scripting.FlushRemovedObjects(); + Editor.WipeOutLeftoverSceneObjects(); + Profiler.EndEvent(); } diff --git a/Source/Engine/Scripting/Scripting.cpp b/Source/Engine/Scripting/Scripting.cpp index 279e4d986..35b90a9ad 100644 --- a/Source/Engine/Scripting/Scripting.cpp +++ b/Source/Engine/Scripting/Scripting.cpp @@ -721,6 +721,15 @@ void Scripting::Reload(bool canTriggerSceneReload) #endif +Array Scripting::GetObjects() +{ + Array objects; + _objectsLocker.Lock(); + _objectsDictionary.GetValues(objects); + _objectsLocker.Unlock(); + return objects; +} + MClass* Scripting::FindClass(const StringAnsiView& fullname) { if (fullname.IsEmpty()) diff --git a/Source/Engine/Scripting/Scripting.h b/Source/Engine/Scripting/Scripting.h index d9f2c5b4d..dad5a02fb 100644 --- a/Source/Engine/Scripting/Scripting.h +++ b/Source/Engine/Scripting/Scripting.h @@ -79,6 +79,13 @@ public: public: + /// + /// Gets all registered scripting objects. + /// + /// Use with caution due to potentially large memory allocation. + /// The collection of the objects. + static Array GetObjects(); + /// /// Finds the class with given fully qualified name within whole assembly. /// From 6eb431d12c795e7c4a311e756cff58a318fe669b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 22 Apr 2024 23:17:56 +0200 Subject: [PATCH 39/43] Fix rare error on drag&drop in Editor --- Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs | 5 ++--- Source/Editor/CustomEditors/Editors/CollectionEditor.cs | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs index fe068e4de..9227dac06 100644 --- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs @@ -209,16 +209,15 @@ namespace FlaxEditor.CustomEditors.Dedicated public override DragDropEffect OnDragMove(ref Float2 location, DragData data) { var result = base.OnDragMove(ref location, data); - if (result != DragDropEffect.None) + if (result != DragDropEffect.None || _dragHandlers == null) return result; - return _dragHandlers.Effect; } /// public override void OnDragLeave() { - _dragHandlers.OnDragLeave(); + _dragHandlers?.OnDragLeave(); base.OnDragLeave(); } diff --git a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs index 41715d654..d7d16083e 100644 --- a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs +++ b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs @@ -585,7 +585,7 @@ namespace FlaxEditor.CustomEditors.Editors public override DragDropEffect OnDragMove(ref Float2 location, DragData data) { var result = base.OnDragMove(ref location, data); - if (result != DragDropEffect.None) + if (result != DragDropEffect.None || _dragHandlers == null) return result; return _dragHandlers.Effect; @@ -594,7 +594,7 @@ namespace FlaxEditor.CustomEditors.Editors /// public override void OnDragLeave() { - _dragHandlers.OnDragLeave(); + _dragHandlers?.OnDragLeave(); base.OnDragLeave(); } From 4fbe2107304e240aa7d0f1a1e5e2ac6c8ed545b3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 22 Apr 2024 23:20:44 +0200 Subject: [PATCH 40/43] Fix heightfield not saved when editing splatmap of terrain with physical materials in use #2262 --- Source/Engine/Terrain/TerrainPatch.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Terrain/TerrainPatch.cpp b/Source/Engine/Terrain/TerrainPatch.cpp index d2c4eaa94..b20b5e9a1 100644 --- a/Source/Engine/Terrain/TerrainPatch.cpp +++ b/Source/Engine/Terrain/TerrainPatch.cpp @@ -1808,6 +1808,9 @@ bool TerrainPatch::UpdateHeightData(TerrainDataUpdateInfo& info, const Int2& mod } #endif + // Mark as modified (need to save texture data during scene saving) + _wasHeightModified = true; + if (!wasHeightChanged) return false; @@ -1820,9 +1823,6 @@ bool TerrainPatch::UpdateHeightData(TerrainDataUpdateInfo& info, const Int2& mod #endif _collisionVertices.Resize(0); - // Mark as modified (need to save texture data during scene saving) - _wasHeightModified = true; - // Note: if terrain is using virtual storage then it won't be updated, we could synchronize that data... // TODO: disable heightmap dynamic streaming - data on a GPU was modified and we don't want to override it with the old data stored in the asset container From 203f03a597a5749cf77c24f69479022a6e8a2cac Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 22 Apr 2024 23:25:19 +0200 Subject: [PATCH 41/43] Add `Write`/`Read` methods to `NetworkStream` for `INetworkSerializable` sending in C# api --- Source/Engine/Networking/NetworkStream.cs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Networking/NetworkStream.cs b/Source/Engine/Networking/NetworkStream.cs index 02fb5eab8..bf8b9b269 100644 --- a/Source/Engine/Networking/NetworkStream.cs +++ b/Source/Engine/Networking/NetworkStream.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. +// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using System; using System.Text; @@ -441,5 +441,23 @@ namespace FlaxEngine.Networking ReadBytes((byte*)&value, sizeof(bool)); return value; } + + /// + /// Writes the object data to the stream. Object has to be allocated. + /// + /// The serializable object. + public void Write(INetworkSerializable obj) + { + obj.Serialize(this); + } + + /// + /// Reads the object data from the stream. Object has to be allocated. + /// + /// The serializable object. + public void Read(INetworkSerializable obj) + { + obj.Deserialize(this); + } } } From ab9cc16529c0d618958a7b7ce59f8d2bce99892b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 23 Apr 2024 00:03:21 +0200 Subject: [PATCH 42/43] Fix `EyeAdaptation` bug to flash on play mode start in Editor when time gets reset --- Source/Engine/Renderer/EyeAdaptationPass.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Renderer/EyeAdaptationPass.cpp b/Source/Engine/Renderer/EyeAdaptationPass.cpp index e44189454..9b664278c 100644 --- a/Source/Engine/Renderer/EyeAdaptationPass.cpp +++ b/Source/Engine/Renderer/EyeAdaptationPass.cpp @@ -42,7 +42,7 @@ void EyeAdaptationPass::Render(RenderContext& renderContext, GPUTexture* colorBu bool dropHistory = renderContext.Buffers->LastEyeAdaptationTime < ZeroTolerance || renderContext.Task->IsCameraCut; const float time = Time::Draw.UnscaledTime.GetTotalSeconds(); //const float frameDelta = Time::ElapsedGameTime.GetTotalSeconds(); - const float frameDelta = time - renderContext.Buffers->LastEyeAdaptationTime; + const float frameDelta = Math::Clamp(time - renderContext.Buffers->LastEyeAdaptationTime, 0.0f, 1.0f); renderContext.Buffers->LastEyeAdaptationTime = 0.0f; if ((view.Flags & ViewFlags::EyeAdaptation) == ViewFlags::None || settings.Mode == EyeAdaptationMode::None || checkIfSkipPass()) return; From 705856da24b9bb8ba8c39b74087ef7dc3b09ba01 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 23 Apr 2024 10:08:16 +0200 Subject: [PATCH 43/43] Fix crash when resizing window on Vulkan Regression from 8488a8d387461cb1932158fc461eedd025aa8460 #2356 --- Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.cpp index cbd6d210f..3ef9aa9c3 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.cpp @@ -131,14 +131,14 @@ void GPUTextureViewVulkan::Release() { Device->OnImageViewDestroy(ViewFramebuffer); Device->DeferredDeletionQueue.EnqueueResource(DeferredDeletionQueueVulkan::Type::ImageView, ViewFramebuffer); - ViewFramebuffer = VK_NULL_HANDLE; } + ViewFramebuffer = VK_NULL_HANDLE; if (ViewSRV != View && ViewSRV != VK_NULL_HANDLE) { Device->OnImageViewDestroy(ViewSRV); Device->DeferredDeletionQueue.EnqueueResource(DeferredDeletionQueueVulkan::Type::ImageView, ViewSRV); - ViewSRV = VK_NULL_HANDLE; } + ViewSRV = VK_NULL_HANDLE; Device->OnImageViewDestroy(View); Device->DeferredDeletionQueue.EnqueueResource(DeferredDeletionQueueVulkan::Type::ImageView, View);