From b0f6ce518ec2db33110b6215cbdcac6c56e2b856 Mon Sep 17 00:00:00 2001 From: Juna <46632782+JunaMeinhold@users.noreply.github.com> Date: Wed, 8 May 2024 15:43:10 +0200 Subject: [PATCH] Added local post processing volumes with transitions, added parallel scripts and order of execution. --- HexaEngine.Core/Graphics/Texture2D.cs | 2 +- .../IO/Binary/Terrains/TerrainLayerGroup.cs | 2 +- .../Editors/VolumeObjectEditor.cs | 36 +- .../Widgets/InputManagerWindow.cs | 9 +- .../Widgets/ScriptBehaviorWidget.cs | 4 - HexaEngine.Mathematics/MathUtil.cs | 233 +++++++++++ HexaEngine.Tests/MathematicsTests.cs | 97 +++++ HexaEngine/AppConfig.cs | 13 + HexaEngine/Collections/FlaggedList.cs | 41 +- HexaEngine/Collections/TopologicalSorter.cs | 53 +++ HexaEngine/Editor/ProxyBase.cs | 6 +- HexaEngine/Input/Input.cs | 2 +- HexaEngine/Input/InputMap.cs | 5 + HexaEngine/Input/InputSystem.cs | 12 +- HexaEngine/PostFx/CopyPass.cs | 5 + HexaEngine/PostFx/IPostFx.cs | 2 + HexaEngine/PostFx/PostFxBase.cs | 12 + HexaEngine/PostFx/PostFxComposeTarget.cs | 5 + HexaEngine/PostFx/PostProcessingManager.cs | 54 +-- HexaEngine/Queries/Generic/ObjectTypeQuery.cs | 2 +- HexaEngine/Resources/TerrainMaterial.cs | 2 +- HexaEngine/Scenes/GameObject.cs | 1 - HexaEngine/Scenes/IComponent.cs | 3 + HexaEngine/Scripts/GlobalScriptManager.cs | 44 +++ HexaEngine/Scripts/ScriptComponent.cs | 5 + HexaEngine/Scripts/ScriptDependencyBuilder.cs | 48 ++- .../Scripts/ScriptExecutionOrderComparer.cs | 12 + HexaEngine/Scripts/ScriptGraph.cs | 65 ++- HexaEngine/Scripts/ScriptGroup.cs | 135 +++++++ HexaEngine/Scripts/ScriptManager.cs | 217 ++++++---- HexaEngine/Scripts/ScriptNode.cs | 25 +- HexaEngine/Scripts/ScriptPriorityAttribute.cs | 13 - HexaEngine/Volumes/IVolume.cs | 24 ++ HexaEngine/Volumes/PostFxProxy.cs | 98 +++++ HexaEngine/Volumes/PostFxSettingsContainer.cs | 8 + HexaEngine/Volumes/Volume.cs | 37 +- HexaEngine/Volumes/VolumeMode.cs | 28 +- HexaEngine/Volumes/VolumeSystem.cs | 370 +++++++++++++++++- .../shared/shaders/deferred/brdf/light.hlsl | 30 +- .../shaders/forward/sky/preethamSky.hlsl | 2 +- 40 files changed, 1542 insertions(+), 220 deletions(-) create mode 100644 HexaEngine/Scripts/GlobalScriptManager.cs create mode 100644 HexaEngine/Scripts/ScriptExecutionOrderComparer.cs create mode 100644 HexaEngine/Scripts/ScriptGroup.cs delete mode 100644 HexaEngine/Scripts/ScriptPriorityAttribute.cs create mode 100644 HexaEngine/Volumes/IVolume.cs diff --git a/HexaEngine.Core/Graphics/Texture2D.cs b/HexaEngine.Core/Graphics/Texture2D.cs index ff71ab80..15ebdd8a 100644 --- a/HexaEngine.Core/Graphics/Texture2D.cs +++ b/HexaEngine.Core/Graphics/Texture2D.cs @@ -414,7 +414,7 @@ public static Texture2D LoadFromAssets(AssetRef assetRef, TextureDimension force /// /// The used to create the texture. /// A task that represents the asynchronous operation and contains the created . - [Obsolete("")] + [Obsolete("For legacy code, don't use.")] public static Task CreateTextureAsync(TextureFileDescription description) { return Task.Factory.StartNew((object state) => diff --git a/HexaEngine.Core/IO/Binary/Terrains/TerrainLayerGroup.cs b/HexaEngine.Core/IO/Binary/Terrains/TerrainLayerGroup.cs index 5f5fb6a0..efc0c642 100644 --- a/HexaEngine.Core/IO/Binary/Terrains/TerrainLayerGroup.cs +++ b/HexaEngine.Core/IO/Binary/Terrains/TerrainLayerGroup.cs @@ -125,7 +125,7 @@ public void RemoveAt(int index) return; } - Array.Copy(layers, index + 1, layers, index, count - index); + Array.Copy(layers, index + 1, layers, index, count - index - 1); count--; return; } diff --git a/HexaEngine.Editor/Editors/VolumeObjectEditor.cs b/HexaEngine.Editor/Editors/VolumeObjectEditor.cs index a13d2f98..fbb8d947 100644 --- a/HexaEngine.Editor/Editors/VolumeObjectEditor.cs +++ b/HexaEngine.Editor/Editors/VolumeObjectEditor.cs @@ -7,6 +7,7 @@ using HexaEngine.PostFx; using HexaEngine.Volumes; using System; + using System.Numerics; using System.Reflection; public class VolumeObjectEditor : IObjectEditor @@ -70,13 +71,44 @@ public unsafe bool Draw(IGraphicsContext context) switch (shape) { case VolumeShape.Box: - + Vector3 min = volume.BoundingBox.Min; + Vector3 max = volume.BoundingBox.Max; + if (ImGui.InputFloat3("Min", ref min)) + { + volume.BoundingBox = new(min, max); + } + if (ImGui.InputFloat3("Max", ref max)) + { + volume.BoundingBox = new(min, max); + } break; case VolumeShape.Sphere: + Vector3 center = volume.BoundingSphere.Center; + float radius = volume.BoundingSphere.Radius; + if (ImGui.InputFloat3("Center", ref center)) + { + volume.BoundingSphere = new(center, radius); + } + if (ImGui.InputFloat("Radius", ref radius)) + { + volume.BoundingSphere = new(center, radius); + } break; } + VolumeTransitionMode transitionMode = volume.TransitionMode; + if (ComboEnumHelper.Combo("Transition Mode", ref transitionMode)) + { + volume.TransitionMode = transitionMode; + } + + int transitionDuration = volume.TransitionDuration; + if (ImGui.InputInt("Transition Duration", ref transitionDuration)) + { + volume.TransitionDuration = transitionDuration; + } + ImGui.TableNextRow(); ImGui.TableSetColumnIndex(0); @@ -179,7 +211,7 @@ private static void DrawEnable(int id, IPostFx effect, PostFxProxy proxy, bool i if ((effect.Flags & (PostFxFlags.AlwaysEnabled | PostFxFlags.Optional)) == 0) { - bool enabled = effect.Enabled; + bool enabled = proxy.Enabled; if (ImGui.Checkbox($"{effect.DisplayName.Id}Enable", ref enabled)) { effect.Enabled = enabled; diff --git a/HexaEngine.Editor/Widgets/InputManagerWindow.cs b/HexaEngine.Editor/Widgets/InputManagerWindow.cs index e73003f9..faaf52cf 100644 --- a/HexaEngine.Editor/Widgets/InputManagerWindow.cs +++ b/HexaEngine.Editor/Widgets/InputManagerWindow.cs @@ -98,12 +98,12 @@ private void OnKeyDown(object? sender, Core.Input.Events.KeyboardEventArgs e) private void Load() { - if (Platform.AppConfig == null || !Platform.AppConfig.Variables.TryGetValue("InputMap", out var xml)) + if (Platform.AppConfig == null) { return; } - inputMap = InputMap.LoadFromText(xml); + inputMap = Platform.AppConfig.InputMap; } private void Save() @@ -113,7 +113,6 @@ private void Save() return; } - Platform.AppConfig.Variables["InputMap"] = inputMap.SaveAsText(); Platform.AppConfig.Save(); } @@ -185,10 +184,6 @@ public override unsafe void DrawContent(IGraphicsContext context) return; } - { - // AxisContextMenu(axisName, currentAxis); - } - ImGui.SeparatorText("Bindings"); ImGui.BeginTable("##Table", 2, ImGuiTableFlags.SizingFixedFit); diff --git a/HexaEngine.Editor/Widgets/ScriptBehaviorWidget.cs b/HexaEngine.Editor/Widgets/ScriptBehaviorWidget.cs index 8aa49532..d4bfed5b 100644 --- a/HexaEngine.Editor/Widgets/ScriptBehaviorWidget.cs +++ b/HexaEngine.Editor/Widgets/ScriptBehaviorWidget.cs @@ -2,10 +2,6 @@ { using HexaEngine.Core.Graphics; using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Threading.Tasks; public class ScriptBehaviorWidget : EditorWindow { diff --git a/HexaEngine.Mathematics/MathUtil.cs b/HexaEngine.Mathematics/MathUtil.cs index 8e664e08..98e157d9 100644 --- a/HexaEngine.Mathematics/MathUtil.cs +++ b/HexaEngine.Mathematics/MathUtil.cs @@ -1241,6 +1241,19 @@ public static float Lerp(float x, float y, float s) return x * (1 - s) + y * s; } + /// + /// Performs linear interpolation between two values. + /// + /// The first value. + /// The second value. + /// The interpolation factor (0 to 1). + /// The interpolated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Lerp(double x, double y, double s) + { + return x * (1 - s) + y * s; + } + /// /// Linearly interpolates between two 2D vectors. /// @@ -1859,6 +1872,26 @@ public static float Clamp01(float value) return value; } + /// + /// Clamps a value to the range [0, 1]. + /// + /// The input value to be clamped. + /// The clamped value within the range [0, 1]. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Clamp01(double value) + { + if (value < 0) + { + return 0; + } + else if (value > 1) + { + return 1; + } + + return value; + } + /// /// Clamps each component of a to a specified range. /// @@ -1869,6 +1902,18 @@ public static float Clamp01(float value) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 Clamp(Vector2 v, Vector2 min, Vector2 max) { + if (Sse.IsSupported) + { + Vector128 vec = v.AsVector128(); + Vector128 vecMin = min.AsVector128(); + Vector128 vecMax = max.AsVector128(); + + vec = Sse.Max(vec, vecMin); + vec = Sse.Min(vec, vecMax); + + return vec.AsVector2(); + } + return Vector2.Clamp(v, min, max); } @@ -1882,6 +1927,18 @@ public static Vector2 Clamp(Vector2 v, Vector2 min, Vector2 max) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Clamp(Vector3 v, Vector3 min, Vector3 max) { + if (Sse.IsSupported) + { + Vector128 vec = v.AsVector128(); + Vector128 vecMin = min.AsVector128(); + Vector128 vecMax = max.AsVector128(); + + vec = Sse.Max(vec, vecMin); + vec = Sse.Min(vec, vecMax); + + return vec.AsVector3(); + } + return Vector3.Clamp(v, min, max); } @@ -1895,6 +1952,18 @@ public static Vector3 Clamp(Vector3 v, Vector3 min, Vector3 max) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Clamp(Vector4 v, Vector4 min, Vector4 max) { + if (Sse.IsSupported) + { + Vector128 vec = v.AsVector128(); + Vector128 vecMin = min.AsVector128(); + Vector128 vecMax = max.AsVector128(); + + vec = Sse.Max(vec, vecMin); + vec = Sse.Min(vec, vecMax); + + return vec.AsVector4(); + } + return Vector4.Clamp(v, min, max); } @@ -1906,6 +1975,16 @@ public static Vector4 Clamp(Vector4 v, Vector4 min, Vector4 max) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 Clamp01(Vector2 v) { + if (Sse.IsSupported) + { + Vector128 vec = v.AsVector128(); + + vec = Sse.MaxScalar(vec, Vector128.Zero); + vec = Sse.MinScalar(vec, Vector128.One); + + return vec.AsVector2(); + } + return Vector2.Clamp(v, Vector2.Zero, Vector2.One); } @@ -1917,6 +1996,16 @@ public static Vector2 Clamp01(Vector2 v) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Clamp01(Vector3 v) { + if (Sse.IsSupported) + { + Vector128 vec = v.AsVector128(); + + vec = Sse.MaxScalar(vec, Vector128.Zero); + vec = Sse.MinScalar(vec, Vector128.One); + + return vec.AsVector3(); + } + return Vector3.Clamp(v, Vector3.Zero, Vector3.One); } @@ -1928,6 +2017,16 @@ public static Vector3 Clamp01(Vector3 v) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Clamp01(Vector4 v) { + if (Sse.IsSupported) + { + Vector128 vec = v.AsVector128(); + + vec = Sse.MaxScalar(vec, Vector128.Zero); + vec = Sse.MinScalar(vec, Vector128.One); + + return vec.AsVector4(); + } + return Vector4.Clamp(v, Vector4.Zero, Vector4.One); } @@ -2964,5 +3063,139 @@ public static Vector4 Remap(Vector4 value, Vector4 low1, Vector4 high1, Vector4 return low2 + (value - low1) * (high2 - low2) / (high1 - low1); } + + /// + /// Performs smooth Hermite interpolation between two values. + /// + /// + /// This method interpolates smoothly between edge0 and edge1, based on the input parameter s. + /// The return value is clamped between 0 and 1. Edge0 and edge1 values are expected to be between 0 and 1. + /// + /// The lower edge. + /// The upper edge. + /// The interpolation value. + /// A smooth Hermite interpolation between edge0 and edge1. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float SmoothStep(float edge0, float edge1, float s) + { + s = Clamp01(s); + s = s * s * (3f - 2f * s); + return edge0 + (edge1 - edge0) * s; + } + + /// + /// Performs smooth Hermite interpolation between two values. + /// + /// + /// This method interpolates smoothly between edge0 and edge1, based on the input parameter s. + /// The return value is clamped between 0 and 1. Edge0 and edge1 values are expected to be between 0 and 1. + /// + /// The lower edge. + /// The upper edge. + /// The interpolation value. + /// A smooth Hermite interpolation between edge0 and edge1. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double SmoothStep(double edge0, double edge1, double s) + { + s = Clamp01(s); + s = s * s * (3f - 2f * s); + return edge0 + (edge1 - edge0) * s; + } + + /// + /// Performs smooth Hermite interpolation between two values. + /// + /// + /// This method interpolates smoothly between edge0 and edge1, based on the input parameter s. + /// The return value is clamped between 0 and 1. Edge0 and edge1 values are expected to be between 0 and 1. + /// + /// The lower edge. + /// The upper edge. + /// The interpolation value. + /// A smooth Hermite interpolation between edge0 and edge1. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector2 SmoothStep(Vector2 edge0, Vector2 edge1, float s) + { + if (Sse.IsSupported) + { + Vector128 vecEdge0 = edge0.AsVector128(); + Vector128 vecEdge1 = edge1.AsVector128(); + + s = Clamp01(s); + s = s * s * (3f - 2f * s); + + Vector128 vecResult = Sse.Add(vecEdge0, Sse.Multiply(Sse.Subtract(vecEdge1, vecEdge0), Vector128.Create(s))); + + return vecResult.AsVector2(); + } + + s = Clamp01(s); + s = s * s * (3f - 2f * s); + return edge0 + (edge1 - edge0) * s; + } + + /// + /// Performs smooth Hermite interpolation between two values. + /// + /// + /// This method interpolates smoothly between edge0 and edge1, based on the input parameter s. + /// The return value is clamped between 0 and 1. Edge0 and edge1 values are expected to be between 0 and 1. + /// + /// The lower edge. + /// The upper edge. + /// The interpolation value. + /// A smooth Hermite interpolation between edge0 and edge1. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 SmoothStep(Vector3 edge0, Vector3 edge1, float s) + { + if (Sse.IsSupported) + { + Vector128 vecEdge0 = edge0.AsVector128(); + Vector128 vecEdge1 = edge1.AsVector128(); + + s = Clamp01(s); + s = s * s * (3f - 2f * s); + + Vector128 vecResult = Sse.Add(vecEdge0, Sse.Multiply(Sse.Subtract(vecEdge1, vecEdge0), Vector128.Create(s))); + + return vecResult.AsVector3(); + } + + s = Clamp01(s); + s = s * s * (3f - 2f * s); + return edge0 + (edge1 - edge0) * s; + } + + /// + /// Performs smooth Hermite interpolation between two values. + /// + /// + /// This method interpolates smoothly between edge0 and edge1, based on the input parameter s. + /// The return value is clamped between 0 and 1. Edge0 and edge1 values are expected to be between 0 and 1. + /// + /// The lower edge. + /// The upper edge. + /// The interpolation value. + /// A smooth Hermite interpolation between edge0 and edge1. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector4 SmoothStep(Vector4 edge0, Vector4 edge1, float s) + { + if (Sse.IsSupported) + { + Vector128 vecEdge0 = edge0.AsVector128(); + Vector128 vecEdge1 = edge1.AsVector128(); + + s = Clamp01(s); + s = s * s * (3f - 2f * s); + + Vector128 vecResult = Sse.Add(vecEdge0, Sse.Multiply(Sse.Subtract(vecEdge1, vecEdge0), Vector128.Create(s))); + + return vecResult.AsVector4(); + } + + s = Clamp01(s); + s = s * s * (3f - 2f * s); + return edge0 + (edge1 - edge0) * s; + } } } \ No newline at end of file diff --git a/HexaEngine.Tests/MathematicsTests.cs b/HexaEngine.Tests/MathematicsTests.cs index 3b9f81c3..4a2761c5 100644 --- a/HexaEngine.Tests/MathematicsTests.cs +++ b/HexaEngine.Tests/MathematicsTests.cs @@ -251,5 +251,102 @@ public void DotVec2D() Assert.That(dot0, Is.EqualTo(dot1)); } + + [TestCase(0, ExpectedResult = 0f)] + [TestCase(0.5f, ExpectedResult = 0.5f)] + [TestCase(1, ExpectedResult = 1f)] + [Test] + public float Smoothstep(float s) + { + float edge0 = 0; + float edge1 = 1; + + float result = MathUtil.SmoothStep(edge0, edge1, s); + + return result; + } + + [TestCase(0, ExpectedResult = -2f)] + [TestCase(0.5f, ExpectedResult = 0f)] + [TestCase(1, ExpectedResult = 2f)] + [Test] + public float Smoothstep2(float s) + { + float edge0 = -2; + float edge1 = 2; + + float result = MathUtil.SmoothStep(edge0, edge1, s); + + return result; + } + + [Test] + public void SmoothstepVec2() + { + Vector2 edge0 = new(0); + Vector2 edge1 = new(1); + float s = 0.5f; + + Vector2 resultSSE = MathUtil.SmoothStep(edge0, edge1, s); + Vector2 resultNonSSE = SmoothStepNonSSE(edge0, edge1, s); + + Assert.That(resultSSE, Is.EqualTo(resultNonSSE)); + } + + [Test] + public void SmoothstepVec3() + { + Vector3 edge0 = new(0); + Vector3 edge1 = new(1); + float s = 0.5f; + + Vector3 resultSSE = MathUtil.SmoothStep(edge0, edge1, s); + Vector3 resultNonSSE = SmoothStepNonSSE(edge0, edge1, s); + + Assert.That(resultSSE, Is.EqualTo(resultNonSSE)); + } + + [Test] + public void SmoothstepVec4() + { + Vector4 edge0 = new(0); + Vector4 edge1 = new(1); + float s = 0.5f; + + Vector4 resultSSE = MathUtil.SmoothStep(edge0, edge1, s); + Vector4 resultNonSSE = SmoothStepNonSSE(edge0, edge1, s); + + Assert.That(resultSSE, Is.EqualTo(resultNonSSE)); + } + + private static float Clamp01(float s) + { + if (s < 0.0f) + return 0.0f; + if (s > 1.0f) + return 1.0f; + return s; + } + + private static Vector2 SmoothStepNonSSE(Vector2 edge0, Vector2 edge1, float s) + { + s = Clamp01(s); + s = s * s * (3f - 2f * s); + return edge0 + (edge1 - edge0) * s; + } + + private static Vector3 SmoothStepNonSSE(Vector3 edge0, Vector3 edge1, float s) + { + s = Clamp01(s); + s = s * s * (3f - 2f * s); + return edge0 + (edge1 - edge0) * s; + } + + private static Vector4 SmoothStepNonSSE(Vector4 edge0, Vector4 edge1, float s) + { + s = Clamp01(s); + s = s * s * (3f - 2f * s); + return edge0 + (edge1 - edge0) * s; + } } } \ No newline at end of file diff --git a/HexaEngine/AppConfig.cs b/HexaEngine/AppConfig.cs index 3d971e80..55a1b649 100644 --- a/HexaEngine/AppConfig.cs +++ b/HexaEngine/AppConfig.cs @@ -1,5 +1,6 @@ namespace HexaEngine { + using HexaEngine.Input; using System.Xml; using System.Xml.Schema; using System.Xml.Serialization; @@ -22,6 +23,8 @@ public AppConfig(string path) public Dictionary Variables { get; set; } = []; + public InputMap InputMap { get; set; } = new(); + public XmlSchema? GetSchema() { return null; @@ -46,6 +49,14 @@ public void ReadXml(XmlReader reader) if (reader.MoveToContent() == XmlNodeType.Element) { string key = reader.Name; + + if (key == "InputMap" && string.IsNullOrEmpty(reader.Value)) + { + InputMap.ReadXml(reader); + + continue; + } + string value = reader.ReadElementContentAsString(); Variables.Add(key, value); reader.ReadEndElement(); @@ -71,6 +82,8 @@ public void WriteXml(XmlWriter writer) writer.WriteAttributeString("ScriptAssembly", ScriptAssembly); } + InputMap.WriteXml(writer); + foreach (var kvp in Variables) { writer.WriteStartElement(kvp.Key); diff --git a/HexaEngine/Collections/FlaggedList.cs b/HexaEngine/Collections/FlaggedList.cs index c740a699..beae8d28 100644 --- a/HexaEngine/Collections/FlaggedList.cs +++ b/HexaEngine/Collections/FlaggedList.cs @@ -2,10 +2,13 @@ { using HexaEngine.Core.Extensions; using HexaEngine.Scenes; + using Silk.NET.Vulkan; using System; using System.Collections; using System.Collections.Generic; + using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; + using System.Reflection; public interface IHasFlags where T : Enum { @@ -17,7 +20,7 @@ public interface INotifyFlagsChanged : IHasFlags where T : Enum public event Action, T>? FlagsChanged; } - public class FlaggedList : IList, IDictionary> where TFlags : unmanaged, Enum where TItem : IHasFlags + public class FlaggedList : IList, IDictionary>, IReadOnlyDictionary> where TFlags : unmanaged, Enum where TItem : IHasFlags { private readonly Dictionary> lists = new(); private readonly List values = new(); @@ -43,6 +46,10 @@ public FlaggedList() public ICollection> Values => lists.Values; + IEnumerable IReadOnlyDictionary>.Keys => lists.Keys; + + IEnumerable> IReadOnlyDictionary>.Values => lists.Values; + public IList this[TFlags index] { get { return lists[index]; } @@ -260,5 +267,37 @@ public bool Remove(KeyValuePair> item) { return ((IDictionary>)lists).GetEnumerator(); } + + public void Sort(IComparer? comparer) + { + for (int i = 0; i < flags.Length; i++) + { + ((List)lists[flags[i]]).Sort(comparer); + } + } + + public void Sort(Comparison comparison) + { + for (int i = 0; i < flags.Length; i++) + { + ((List)lists[flags[i]]).Sort(comparison); + } + } + + public void Sort(int index, int count, IComparer? comparer) + { + for (int i = 0; i < flags.Length; i++) + { + ((List)lists[flags[i]]).Sort(index, count, comparer); + } + } + + public void Sort() + { + for (int i = 0; i < flags.Length; i++) + { + ((List)lists[flags[i]]).Sort(); + } + } } } \ No newline at end of file diff --git a/HexaEngine/Collections/TopologicalSorter.cs b/HexaEngine/Collections/TopologicalSorter.cs index 660a9b3b..2d04a73c 100644 --- a/HexaEngine/Collections/TopologicalSorter.cs +++ b/HexaEngine/Collections/TopologicalSorter.cs @@ -35,6 +35,28 @@ public List TopologicalSort(List nodes) return sortedList; } + public List TopologicalSort(List nodes, List sortedList) + { + visitedNodes.Clear(); + recursionStack.Clear(); + cycleStack.Clear(); + + for (int i = 0; i < nodes.Count; i++) + { + T node = nodes[i]; + if (!visitedNodes.Contains(node)) + { + if (!TopologicalSortRecursive(node, nodes, sortedList)) + { + // A cycle is detected + throw new Exception("The graph contains a cycle: " + GetCyclePath()); + } + } + } + + return sortedList; + } + private bool TopologicalSortRecursive(T node, List nodes) { visitedNodes.Add(node); @@ -66,6 +88,37 @@ private bool TopologicalSortRecursive(T node, List nodes) return true; } + private bool TopologicalSortRecursive(T node, List nodes, List sortedList) + { + visitedNodes.Add(node); + recursionStack.Add(node); + cycleStack.Push(node); + + for (int i = 0; i < node.Dependencies.Count; i++) + { + T dependency = (T)node.Dependencies[i]; + if (!visitedNodes.Contains(dependency)) + { + if (!TopologicalSortRecursive(dependency, nodes)) + { + return false; + } + } + else if (recursionStack.Contains(dependency)) + { + cycleStack.Push(dependency); + // A cycle is detected + return false; + } + } + + sortedList.Add(node); + recursionStack.Remove(node); + cycleStack.Pop(); + + return true; + } + private string GetCyclePath() { if (cycleStack.Count == 0) diff --git a/HexaEngine/Editor/ProxyBase.cs b/HexaEngine/Editor/ProxyBase.cs index 8a17e49d..e861a40a 100644 --- a/HexaEngine/Editor/ProxyBase.cs +++ b/HexaEngine/Editor/ProxyBase.cs @@ -5,11 +5,11 @@ public class ProxyBase : IProxy { - private readonly List properties = []; - private readonly Dictionary propertyData = []; + protected readonly List properties = []; + protected readonly Dictionary propertyData = []; [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] - private Type? targetType; + protected Type? targetType; public ProxyBase(object target) { diff --git a/HexaEngine/Input/Input.cs b/HexaEngine/Input/Input.cs index a7e1b4be..168d1520 100644 --- a/HexaEngine/Input/Input.cs +++ b/HexaEngine/Input/Input.cs @@ -4,7 +4,7 @@ public static class Input { - public static IInputManager Current { get; set; } = new InputManager(); + public static IInputManager Current { get; internal set; } public static float GetAxis(string name) { diff --git a/HexaEngine/Input/InputMap.cs b/HexaEngine/Input/InputMap.cs index ade78965..d798e28b 100644 --- a/HexaEngine/Input/InputMap.cs +++ b/HexaEngine/Input/InputMap.cs @@ -45,6 +45,11 @@ public void ReadXml(XmlReader reader) if (reader.NodeType == XmlNodeType.Element && reader.Name == "VirtualAxes") { + if (reader.IsEmptyElement) + { + continue; + } + reader.ReadStartElement("VirtualAxes"); while (reader.Read()) diff --git a/HexaEngine/Input/InputSystem.cs b/HexaEngine/Input/InputSystem.cs index 808a85f6..dd69395c 100644 --- a/HexaEngine/Input/InputSystem.cs +++ b/HexaEngine/Input/InputSystem.cs @@ -5,12 +5,15 @@ public class InputSystem : ISceneSystem { + private InputManager inputManager; + public string Name { get; } = "Input System"; public SystemFlags Flags { get; } = SystemFlags.Awake | SystemFlags.EarlyUpdate | SystemFlags.Destroy; public void Awake(Scene scene) { + Input.Current = inputManager = new(); if (!scene.Variables.TryGetValue("InputMapAsset", out var guidString) || !Guid.TryParse(guidString, out var assetGuid)) { LoadFromAppConfig(); @@ -28,12 +31,12 @@ public void Awake(Scene scene) private static void LoadFromAppConfig() { - if (Platform.AppConfig == null || !Platform.AppConfig.Variables.TryGetValue("InputMap", out var xml)) + if (Platform.AppConfig == null) { return; } - InputMap? map = InputMap.LoadFromText(xml); + InputMap map = Platform.AppConfig.InputMap; if (map != null) { @@ -43,11 +46,14 @@ private static void LoadFromAppConfig() public void Update(float delta) { - Input.Current.Update(); + inputManager.Update(); } public void Destroy() { + var tmp = Input.Current; + Input.Current = null; + tmp.Dispose(); } } } \ No newline at end of file diff --git a/HexaEngine/PostFx/CopyPass.cs b/HexaEngine/PostFx/CopyPass.cs index 6aaf7df7..aa7c464c 100644 --- a/HexaEngine/PostFx/CopyPass.cs +++ b/HexaEngine/PostFx/CopyPass.cs @@ -75,5 +75,10 @@ public void Dispose() { // nothing to dispose. } + + public void ResetSettings() + { + // nothing to do. + } } } \ No newline at end of file diff --git a/HexaEngine/PostFx/IPostFx.cs b/HexaEngine/PostFx/IPostFx.cs index 7e25a3b8..f6509e85 100644 --- a/HexaEngine/PostFx/IPostFx.cs +++ b/HexaEngine/PostFx/IPostFx.cs @@ -117,5 +117,7 @@ void PrePassDraw(IGraphicsContext context, GraphResourceBuilder creator) /// /// The graphics context. public void Update(IGraphicsContext context); + + public void ResetSettings(); } } \ No newline at end of file diff --git a/HexaEngine/PostFx/PostFxBase.cs b/HexaEngine/PostFx/PostFxBase.cs index 16e8b5d7..88a64784 100644 --- a/HexaEngine/PostFx/PostFxBase.cs +++ b/HexaEngine/PostFx/PostFxBase.cs @@ -2,6 +2,7 @@ { using HexaEngine.Core.Graphics; using HexaEngine.Core.UI; + using HexaEngine.Editor; using HexaEngine.Editor.Attributes; using HexaEngine.Graphics.Graph; using HexaEngine.Mathematics; @@ -18,6 +19,7 @@ public abstract class PostFxBase : IPostFx { private bool initialized = false; private bool enabled = false; + private readonly ProxyBase proxy; /// /// Indicates whether the post-processing effect is dirty and needs an update. @@ -51,6 +53,11 @@ public abstract class PostFxBase : IPostFx private ImGuiName? displayName; + public PostFxBase() + { + proxy = new(this); + } + /// public abstract string Name { get; } @@ -262,5 +269,10 @@ private string GetDebuggerDisplay() { return $"{Name}, {(Initialized ? "I" : "")}{(Enabled ? "E" : "")}{(dirty ? "D" : "")}, {Flags}"; } + + public virtual void ResetSettings() + { + proxy.Apply(this); + } } } \ No newline at end of file diff --git a/HexaEngine/PostFx/PostFxComposeTarget.cs b/HexaEngine/PostFx/PostFxComposeTarget.cs index b3e6af4a..22a20caf 100644 --- a/HexaEngine/PostFx/PostFxComposeTarget.cs +++ b/HexaEngine/PostFx/PostFxComposeTarget.cs @@ -85,5 +85,10 @@ public void Update(IGraphicsContext context) public void Dispose() { } + + public void ResetSettings() + { + // nothing to do. + } } } \ No newline at end of file diff --git a/HexaEngine/PostFx/PostProcessingManager.cs b/HexaEngine/PostFx/PostProcessingManager.cs index 2f27cf5d..41b4b917 100644 --- a/HexaEngine/PostFx/PostProcessingManager.cs +++ b/HexaEngine/PostFx/PostProcessingManager.cs @@ -321,6 +321,17 @@ public void Remove(IPostFx effect) } } + public void ResetSettings() + { + using (SupressReload()) + { + for (int i = 0; i < effects.Count; i++) + { + effects[i].ResetSettings(); + } + } + } + public void Invalidate() { for (int i = 0; i < groups.Count; i++) @@ -568,36 +579,33 @@ public void Draw(IGraphicsContext context, GraphResourceBuilder creator, ICPUPro return; } - lock (_lock) - { - postContext.Clear(context); + postContext.Clear(context); - if (isDirty) + if (isDirty) + { + postContext.Reset(); + for (int i = 0; i < groups.Count; i++) { - postContext.Reset(); - for (int i = 0; i < groups.Count; i++) - { - groups[i].SetupInputOutputs(postContext); - } - isDirty = false; + groups[i].SetupInputOutputs(postContext); } + isDirty = false; + } - for (int i = 0; i < activeEffects.Count; i++) + for (int i = 0; i < activeEffects.Count; i++) + { + var effect = activeEffects[i]; + if (!effect.Enabled || (effect.Flags & PostFxFlags.PrePass) != 0 || (effect.Flags & PostFxFlags.ComposeTarget) != 0) { - var effect = activeEffects[i]; - if (!effect.Enabled || (effect.Flags & PostFxFlags.PrePass) != 0 || (effect.Flags & PostFxFlags.ComposeTarget) != 0) - { - continue; - } - profiler?.Begin(effect.Name); - effect.Update(context); - profiler?.End(effect.Name); + continue; } + profiler?.Begin(effect.Name); + effect.Update(context); + profiler?.End(effect.Name); + } - for (int i = 0; i < groups.Count; i++) - { - groups[i].Execute(context, deferredContext, creator); - } + for (int i = 0; i < groups.Count; i++) + { + groups[i].Execute(context, deferredContext, creator); } } diff --git a/HexaEngine/Queries/Generic/ObjectTypeQuery.cs b/HexaEngine/Queries/Generic/ObjectTypeQuery.cs index 3a24f105..57e8faf9 100644 --- a/HexaEngine/Queries/Generic/ObjectTypeQuery.cs +++ b/HexaEngine/Queries/Generic/ObjectTypeQuery.cs @@ -8,7 +8,7 @@ public class ObjectTypeQuery : IQuery, IReadOnlyList where T : GameObject private readonly List cache = new(); private bool disposedValue; - public ObjectTypeQuery(QueryFlags flags = QueryFlags.Default) + public ObjectTypeQuery(QueryFlags flags = QueryFlags.ObjectAdded | QueryFlags.ObjectRemoved) { Flags = flags; } diff --git a/HexaEngine/Resources/TerrainMaterial.cs b/HexaEngine/Resources/TerrainMaterial.cs index 74e8b621..55479d49 100644 --- a/HexaEngine/Resources/TerrainMaterial.cs +++ b/HexaEngine/Resources/TerrainMaterial.cs @@ -184,7 +184,7 @@ private static MaterialShaderPassDesc[] GetMaterialShaderPasses(bool alphaBlend, BlendDescription blend = BlendDescription.Opaque; if (blendFunc) { - blend = BlendDescription.AlphaBlend; + blend = BlendDescription.Additive; } GraphicsPipelineDesc pipelineDescForward = new() diff --git a/HexaEngine/Scenes/GameObject.cs b/HexaEngine/Scenes/GameObject.cs index b7a837f6..5506106e 100644 --- a/HexaEngine/Scenes/GameObject.cs +++ b/HexaEngine/Scenes/GameObject.cs @@ -30,7 +30,6 @@ public partial class GameObject : EntityNotifyBase, IHierarchyObject, IEditorSel private string name = string.Empty; private string? fullName; - private GCHandle gcHandle; private object? tag; private readonly EventHandlers enabledChangedList = new(); diff --git a/HexaEngine/Scenes/IComponent.cs b/HexaEngine/Scenes/IComponent.cs index 779f4b76..1496ccd1 100644 --- a/HexaEngine/Scenes/IComponent.cs +++ b/HexaEngine/Scenes/IComponent.cs @@ -28,6 +28,9 @@ public interface IAudioComponent : IComponent public interface IScriptComponent : IComponent, INotifyFlagsChanged { + int ExecutionOrderIndex { get; set; } + Type? ScriptType { get; } + void ScriptCreate(); void ScriptLoad(); diff --git a/HexaEngine/Scripts/GlobalScriptManager.cs b/HexaEngine/Scripts/GlobalScriptManager.cs new file mode 100644 index 00000000..89edfb64 --- /dev/null +++ b/HexaEngine/Scripts/GlobalScriptManager.cs @@ -0,0 +1,44 @@ +namespace HexaEngine.Scripts +{ + using HexaEngine.Core; + using HexaEngine.Scenes; + using System.Reflection; + + public class GlobalScriptManager + { + private List scripts = new(); + + static GlobalScriptManager() + { + ScriptAssemblyManager.AssembliesUnloaded += AssembliesUnloaded; + ScriptAssemblyManager.AssemblyLoaded += AssemblyLoaded; + Application.OnEditorPlayStateTransition += EditorPlayStateTransition; + Application.OnEditorPlayStateChanged += EditorPlayStateChanged; + SceneManager.SceneChanged += SceneChanged; + } + + private static void SceneChanged(object? sender, SceneChangedEventArgs e) + { + } + + private static void EditorPlayStateTransition(EditorPlayStateTransitionEventArgs args) + { + } + + private static void EditorPlayStateChanged(EditorPlayState newState) + { + } + + private static void AssemblyLoaded(object? sender, Assembly e) + { + } + + private static void AssembliesUnloaded(object? sender, EventArgs? e) + { + } + + public void Load() + { + } + } +} \ No newline at end of file diff --git a/HexaEngine/Scripts/ScriptComponent.cs b/HexaEngine/Scripts/ScriptComponent.cs index f45e8f4b..af54b38d 100644 --- a/HexaEngine/Scripts/ScriptComponent.cs +++ b/HexaEngine/Scripts/ScriptComponent.cs @@ -50,6 +50,8 @@ public Guid Guid set => guid = value; } + public bool Enabled { get; set; } + [JsonIgnore] public bool IsSerializable { get; } = true; @@ -161,6 +163,9 @@ public GameObject GameObject [JsonIgnore] public Type? ScriptType => scriptType; + [JsonIgnore] + public int ExecutionOrderIndex { get; set; } + public event Action, ScriptFlags>? FlagsChanged; public void ScriptCreate() diff --git a/HexaEngine/Scripts/ScriptDependencyBuilder.cs b/HexaEngine/Scripts/ScriptDependencyBuilder.cs index 45d8032f..09e40a0f 100644 --- a/HexaEngine/Scripts/ScriptDependencyBuilder.cs +++ b/HexaEngine/Scripts/ScriptDependencyBuilder.cs @@ -1,11 +1,8 @@ namespace HexaEngine.Scripts { - using HexaEngine.PostFx; using System; using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Threading.Tasks; + using System.Reflection; public class ScriptDependencyBuilder { @@ -25,8 +22,49 @@ public void Clear() Dependencies.Clear(); } - public void Build(IReadOnlyList others, ScriptFlags stage) + private static ScriptNode? ResolveNode(Type scriptType, IReadOnlyList others) { + for (int i = 0; i < others.Count; i++) + { + var other = others[i]; + if (other.ScriptType == scriptType) + { + return other; + } + } + return null; + } + + public void Build(IReadOnlyList others) + { + foreach (var after in node.ScriptType.GetCustomAttributes()) + { + var runAfter = after.Type; + var afterNode = ResolveNode(runAfter, others); + if (afterNode is null) + { + continue; + } + if (!node.Dependencies.Contains(afterNode)) + { + node.Dependencies.Add(afterNode); + afterNode.Dependants.Add(node); + } + } + + foreach (var before in node.ScriptType.GetCustomAttributes()) + { + var beforeNode = ResolveNode(before.Type, others); + if (beforeNode is null) + { + continue; + } + if (!beforeNode.Dependencies.Contains(node)) + { + beforeNode.Dependencies.Add(node); + node.Dependants.Add(beforeNode); + } + } } } } \ No newline at end of file diff --git a/HexaEngine/Scripts/ScriptExecutionOrderComparer.cs b/HexaEngine/Scripts/ScriptExecutionOrderComparer.cs new file mode 100644 index 00000000..0cb9195b --- /dev/null +++ b/HexaEngine/Scripts/ScriptExecutionOrderComparer.cs @@ -0,0 +1,12 @@ +namespace HexaEngine.Scripts +{ + using HexaEngine.Scenes; + + public struct ScriptExecutionOrderComparer : IComparer + { + public readonly int Compare(IScriptComponent? x, IScriptComponent? y) + { + return x == null || y == null ? 0 : x.ExecutionOrderIndex.CompareTo(y.ExecutionOrderIndex); + } + } +} \ No newline at end of file diff --git a/HexaEngine/Scripts/ScriptGraph.cs b/HexaEngine/Scripts/ScriptGraph.cs index e1d5262d..a7e11782 100644 --- a/HexaEngine/Scripts/ScriptGraph.cs +++ b/HexaEngine/Scripts/ScriptGraph.cs @@ -2,22 +2,22 @@ { using HexaEngine.Collections; using System.Collections.Generic; + using System.Globalization; + using System.Linq; public class ScriptGraph { - private readonly List nodes = new(); - private readonly Dictionary scriptToNode = new(); - private readonly ScriptNode root; + private readonly List nodes = []; + private readonly List sorted = []; + private readonly Dictionary scriptToNode = []; private readonly ScriptTypeRegistry scriptTypeRegistry = new(); private readonly TopologicalSorter sorter = new(); public ScriptGraph() { - root = new ScriptNode(typeof(ScriptRoot), ScriptFlags.Awake | ScriptFlags.Destroy | ScriptFlags.Update | ScriptFlags.FixedUpdate, scriptTypeRegistry); - nodes.Add(root); } - public void AddNode(ScriptComponent script) + public void AddNode(Type script) { ScriptNode node = new(script, scriptTypeRegistry); nodes.Add(node); @@ -25,7 +25,7 @@ public void AddNode(ScriptComponent script) scriptTypeRegistry.Add(script, node); } - public bool RemoveNode(ScriptComponent script) + public bool RemoveNode(Type script) { if (!scriptToNode.TryGetValue(script, out var node)) { @@ -40,7 +40,6 @@ public bool RemoveNode(ScriptComponent script) public void Clear() { - root.Reset(); for (int i = 0; i < nodes.Count; i++) { nodes[i].Reset(); @@ -49,17 +48,59 @@ public void Clear() scriptTypeRegistry.Clear(); nodes.Clear(); scriptToNode.Clear(); - nodes.Add(root); } - public void Build(IList scriptsSorted, ScriptFlags stage) + public List> GroupNodesForParallelExecution() + { + var depthGroups = nodes.GroupBy(node => (node.Depth, node.RunInParallel)).OrderBy(group => group.Key.Depth); + var parallelNodeLists = depthGroups.Select(group => group.ToList()).ToList(); + return parallelNodeLists; + } + + private void CalculateDepths() + { + var visited = new HashSet(); + foreach (var node in nodes) + { + CalculateDepth(node, visited); + } + } + + private static void CalculateDepth(ScriptNode node, HashSet visited) + { + if (visited.Contains(node)) + return; + + visited.Add(node); + + if (node.Dependencies.Count == 0) + { + node.Depth = 0; + } + else + { + foreach (var dependency in node.Dependencies) + { + CalculateDepth(dependency, visited); + node.Depth = Math.Max(node.Depth, dependency.Depth + 1); + } + } + } + + public void Build() { for (int i = 0; i < nodes.Count; i++) { ScriptNode node = nodes[i]; - node.Builder.Dependencies.Add(root.ScriptType); // prevent nodes from being culled away by topological sort. - node.Builder.Build(nodes, stage); + + node.Builder.Build(nodes); } + + sorted.Clear(); + + sorter.TopologicalSort(nodes, sorted); + + CalculateDepths(); } } } \ No newline at end of file diff --git a/HexaEngine/Scripts/ScriptGroup.cs b/HexaEngine/Scripts/ScriptGroup.cs new file mode 100644 index 00000000..6be38e4c --- /dev/null +++ b/HexaEngine/Scripts/ScriptGroup.cs @@ -0,0 +1,135 @@ +namespace HexaEngine.Scripts +{ + using HexaEngine.Collections; + using HexaEngine.Scenes; + + public class ScriptGroup + { + private readonly Dictionary map = []; + private readonly List nodes; + private readonly bool parallel; + private readonly FlaggedList instances = []; + private readonly object _lock = new(); + + public ScriptGroup(List nodes, bool parallel) + { + this.nodes = nodes; + this.parallel = parallel; + for (int i = 0; i < nodes.Count; i++) + { + map.Add(nodes[i].ScriptType, i); + } + } + + public bool AddInstance(IScriptComponent component) + { + lock (_lock) + { + if (map.TryGetValue(component.ScriptType, out int index)) + { + component.ExecutionOrderIndex = index; + instances.Add(component); + instances.Sort(new ScriptExecutionOrderComparer()); + return true; + } + } + + return false; + } + + public bool RemoveInstance(IScriptComponent component) + { + lock (_lock) + { + return instances.Remove(component); + } + } + + public IReadOnlyList Nodes => nodes; + + public IReadOnlyDictionary> Instances => instances; + + public void ExecuteAwake() + { + var scriptList = instances[ScriptFlags.Awake]; + + if (parallel) + { + Parallel.ForEach(scriptList, script => + { + script.ScriptAwake(); + }); + return; + } + + for (int i = 0; i < scriptList.Count; i++) + { + scriptList[i].ScriptAwake(); + } + } + + public void ExecuteDestroy() + { + var scriptList = instances[ScriptFlags.Destroy]; + + if (parallel) + { + Parallel.ForEach(scriptList, script => + { + script.Destroy(); + }); + return; + } + + for (int i = 0; i < scriptList.Count; i++) + { + scriptList[i].Destroy(); + } + } + + public void ExecuteUpdate() + { + var scriptList = instances[ScriptFlags.Update]; + + if (parallel) + { + Parallel.ForEach(scriptList, script => + { + script.Update(); + }); + return; + } + + for (int i = 0; i < scriptList.Count; i++) + { + scriptList[i].Update(); + } + } + + public void ExecuteFixedUpdate() + { + var scriptList = instances[ScriptFlags.FixedUpdate]; + + if (parallel) + { + Parallel.ForEach(scriptList, script => + { + script.FixedUpdate(); + }); + return; + } + + for (int i = 0; i < scriptList.Count; i++) + { + scriptList[i].FixedUpdate(); + } + } + + public void Clear() + { + instances.Clear(); + nodes.Clear(); + map.Clear(); + } + } +} \ No newline at end of file diff --git a/HexaEngine/Scripts/ScriptManager.cs b/HexaEngine/Scripts/ScriptManager.cs index b6c2ed3d..10b64e14 100644 --- a/HexaEngine/Scripts/ScriptManager.cs +++ b/HexaEngine/Scripts/ScriptManager.cs @@ -1,96 +1,134 @@ namespace HexaEngine.Scripts { - using HexaEngine.Collections; using HexaEngine.Core; using HexaEngine.Queries.Generic; using HexaEngine.Scenes; - using System.Reflection; - public class GlobalScriptManager + public class ScriptManager : ISceneSystem { - private List scripts = new(); + private readonly ComponentTypeQuery components = new(); + private readonly ScriptGraph graph = new(); + private readonly List groups = []; + private readonly object _lock = new(); + private bool awaked; - static GlobalScriptManager() - { - ScriptAssemblyManager.AssembliesUnloaded += AssembliesUnloaded; - ScriptAssemblyManager.AssemblyLoaded += AssemblyLoaded; - Application.OnEditorPlayStateTransition += EditorPlayStateTransition; - Application.OnEditorPlayStateChanged += EditorPlayStateChanged; - SceneManager.SceneChanged += SceneChanged; - } + public string Name => "Scripts"; - private static void SceneChanged(object? sender, SceneChangedEventArgs e) - { - } + public SystemFlags Flags { get; } = SystemFlags.Awake | SystemFlags.Update | SystemFlags.FixedUpdate | SystemFlags.Destroy; - private static void EditorPlayStateTransition(EditorPlayStateTransitionEventArgs args) - { - } + public ScriptGraph Graph => graph; - private static void EditorPlayStateChanged(EditorPlayState newState) - { - } + public IReadOnlyList Groups => groups; - private static void AssemblyLoaded(object? sender, Assembly e) + private void AssemblyLoaded(object? sender, System.Reflection.Assembly e) { + lock (_lock) + { + UpdateGraphInternal(); + } } - private static void AssembliesUnloaded(object? sender, EventArgs? e) + private void AssembliesUnloaded(object? sender, EventArgs? e) { + lock (_lock) + { + ClearGroups(); + graph.Clear(); + } } - public void Load() + public void Awake(Scene scene) { - } - } + lock (_lock) + { + ScriptAssemblyManager.AssemblyLoaded += AssemblyLoaded; + ScriptAssemblyManager.AssembliesUnloaded += AssembliesUnloaded; - public class ScriptManager : ISceneSystem - { - private readonly ComponentTypeQuery components = new(); - private readonly FlaggedList scripts = new(); - private bool awaked; + scene.QueryManager.AddQuery(components); - public string Name => "Scripts"; + for (int i = 0; i < components.Count; i++) + { + components[i].ScriptCreate(); + } - public SystemFlags Flags { get; } = SystemFlags.Awake | SystemFlags.Update | SystemFlags.FixedUpdate | SystemFlags.Destroy; + for (int i = 0; i < components.Count; i++) + { + components[i].ScriptLoad(); + } - public IReadOnlyList Scripts => (IReadOnlyList)scripts; + UpdateGraphInternal(); - public void Awake(Scene scene) - { - scene.QueryManager.AddQuery(components); + components.OnAdded += OnAdded; + components.OnRemoved += OnRemoved; - components.OnAdded += OnAdded; - components.OnRemoved -= OnRemoved; + if (Application.InEditMode) + { + return; + } - for (int i = 0; i < components.Count; i++) + awaked = true; + + for (int i = 0; i < groups.Count; i++) + { + groups[i].ExecuteAwake(); + } + } + } + + public void UpdateGraph() + { + lock (_lock) { - scripts.Add(components[i]); - components[i].ScriptCreate(); + UpdateGraphInternal(); } + } - for (int i = 0; i < components.Count; i++) + private void UpdateGraphInternal() + { + ClearGroups(); + + graph.Clear(); + + IList types = ScriptAssemblyManager.GetAssignableTypes(); + foreach (var item in types) { - components[i].ScriptLoad(); + graph.AddNode(item); } - if (Application.InEditMode) + graph.Build(); + + var nodeGroups = graph.GroupNodesForParallelExecution(); + + for (int i = 0; i < nodeGroups.Count; i++) { - return; + var nodeGroup = nodeGroups[i]; + bool runInParallel = nodeGroup[0].RunInParallel; + ScriptGroup group = new(nodeGroup, runInParallel); + groups.Add(group); } - awaked = true; + for (int i = 0; i < components.Count; i++) + { + AddToGroup(components[i]); + } + } - var scriptList = scripts[ScriptFlags.Awake]; - for (int i = 0; i < scriptList.Count; i++) + private void ClearGroups() + { + for (int i = 0; i < groups.Count; i++) { - scriptList[i].ScriptAwake(); + groups[i].Clear(); } + + groups.Clear(); } private void OnRemoved(GameObject gameObject, IScriptComponent component) { - scripts.Remove(component); + lock (_lock) + { + RemoveFromGroup(component); + } if (Application.InEditMode || !awaked) { @@ -102,7 +140,10 @@ private void OnRemoved(GameObject gameObject, IScriptComponent component) private void OnAdded(GameObject gameObject, IScriptComponent component) { - scripts.Add(component); + lock (_lock) + { + AddToGroup(component); + } if (Application.InEditMode || !awaked) { @@ -118,24 +159,56 @@ private void OnAdded(GameObject gameObject, IScriptComponent component) } } - public void Destroy() + private void AddToGroup(IScriptComponent component) { - components.OnAdded -= OnAdded; - components.OnRemoved -= OnRemoved; - - components.Dispose(); - - if (Application.InEditMode) + for (int i = 0; i < groups.Count; i++) { - return; + if (groups[i].AddInstance(component)) + { + break; + } } + } - awaked = false; + private void RemoveFromGroup(IScriptComponent component) + { + for (int i = 0; i < groups.Count; i++) + { + if (groups[i].RemoveInstance(component)) + { + break; + } + } + } - var scriptList = scripts[ScriptFlags.Destroy]; - for (int i = 0; i < scriptList.Count; i++) + public void Destroy() + { + lock (_lock) { - scriptList[i].Destroy(); + ScriptAssemblyManager.AssemblyLoaded -= AssemblyLoaded; + ScriptAssemblyManager.AssembliesUnloaded -= AssembliesUnloaded; + + graph.Clear(); + + components.OnAdded -= OnAdded; + components.OnRemoved -= OnRemoved; + + components.Dispose(); + + if (Application.InEditMode) + { + ClearGroups(); + return; + } + + awaked = false; + + for (int i = 0; i < groups.Count; i++) + { + groups[i].ExecuteDestroy(); + } + + ClearGroups(); } } @@ -146,10 +219,12 @@ public void Update(float delta) return; } - var scriptList = scripts[ScriptFlags.Update]; - for (int i = 0; i < scriptList.Count; i++) + lock (_lock) { - scriptList[i].Update(); + for (int i = 0; i < groups.Count; i++) + { + groups[i].ExecuteUpdate(); + } } } @@ -160,10 +235,12 @@ public void FixedUpdate() return; } - var scriptList = scripts[ScriptFlags.FixedUpdate]; - for (int i = 0; i < scriptList.Count; i++) + lock (_lock) { - scriptList[i].FixedUpdate(); + for (int i = 0; i < groups.Count; i++) + { + groups[i].ExecuteFixedUpdate(); + } } } } diff --git a/HexaEngine/Scripts/ScriptNode.cs b/HexaEngine/Scripts/ScriptNode.cs index 09829c6f..188d9a81 100644 --- a/HexaEngine/Scripts/ScriptNode.cs +++ b/HexaEngine/Scripts/ScriptNode.cs @@ -3,36 +3,23 @@ using HexaEngine.Collections; using System.Collections.Generic; using System.Linq; + using System.Reflection; public class ScriptNode : INode { private readonly List dependencies = []; private readonly List dependants = []; private readonly ScriptDependencyBuilder builder; - private readonly ScriptComponent? script; - private readonly ScriptFlags flags; private readonly Type scriptType; - public ScriptNode(ScriptComponent script, ScriptTypeRegistry registry) - { - this.script = script; - flags = script.Flags; - - builder = new(this, registry); - } - - public ScriptNode(Type scriptType, ScriptFlags flags, ScriptTypeRegistry registry) + public ScriptNode(Type scriptType, ScriptTypeRegistry registry) { builder = new(this, registry); this.scriptType = scriptType; - this.flags = flags; + RunInParallel = scriptType.GetCustomAttribute() != null; } - public ScriptComponent? Script => script; - - public Type ScriptType => script?.ScriptType ?? scriptType; - - public ScriptFlags Flags => flags; + public Type ScriptType => scriptType; List INode.Dependencies => dependencies.Cast().ToList(); @@ -40,6 +27,10 @@ public ScriptNode(Type scriptType, ScriptFlags flags, ScriptTypeRegistry registr public List Dependants => dependants; + public int Depth { get; internal set; } + + public bool RunInParallel { get; } + public ScriptDependencyBuilder Builder => builder; public void Reset() diff --git a/HexaEngine/Scripts/ScriptPriorityAttribute.cs b/HexaEngine/Scripts/ScriptPriorityAttribute.cs deleted file mode 100644 index e44bd25c..00000000 --- a/HexaEngine/Scripts/ScriptPriorityAttribute.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace HexaEngine.Scripts -{ - [AttributeUsage(AttributeTargets.Class)] - public class ScriptPriorityAttribute : Attribute - { - public ScriptPriorityAttribute(int index) - { - Index = index; - } - - public int Index { get; } - } -} \ No newline at end of file diff --git a/HexaEngine/Volumes/IVolume.cs b/HexaEngine/Volumes/IVolume.cs new file mode 100644 index 00000000..3ea1507f --- /dev/null +++ b/HexaEngine/Volumes/IVolume.cs @@ -0,0 +1,24 @@ +using HexaEngine.Mathematics; +using HexaEngine.PostFx; + +namespace HexaEngine.Volumes +{ + public interface IVolume + { + BoundingBox BoundingBox { get; set; } + + BoundingSphere BoundingSphere { get; set; } + + PostFxSettingsContainer Container { get; } + + VolumeMode Mode { get; set; } + + VolumeShape Shape { get; set; } + + void Apply(PostProcessingManager manager); + + void Apply(PostProcessingManager manager, IVolume baseVolume, float blend, VolumeTransitionMode mode); + + void Init(PostProcessingManager manager); + } +} \ No newline at end of file diff --git a/HexaEngine/Volumes/PostFxProxy.cs b/HexaEngine/Volumes/PostFxProxy.cs index 8ef75912..ca4667b3 100644 --- a/HexaEngine/Volumes/PostFxProxy.cs +++ b/HexaEngine/Volumes/PostFxProxy.cs @@ -1,8 +1,11 @@ namespace HexaEngine.Volumes { using HexaEngine.Editor; + using HexaEngine.Mathematics; using HexaEngine.PostFx; using System.Collections.Generic; + using System.Management; + using System.Numerics; /// /// Represents a proxy for an IPostFx object, allowing dynamic access to its properties. @@ -20,5 +23,100 @@ public PostFxProxy(Dictionary data, string typeName) : base(dat [JsonIgnore] public bool Enabled { get => (bool)Data["Enabled"]; set => Data["Enabled"] = value; } + + public void Apply(object target, PostFxProxy proxyBase, float blend, VolumeTransitionMode mode) + { + foreach (var property in properties) + { + if (propertyData.TryGetValue(property.Name, out var value) && proxyBase.propertyData.TryGetValue(property.Name, out var baseValue) && value != null) + { + if (property.CanWrite) + { + try + { + value = mode switch + { + VolumeTransitionMode.Constant => value, + VolumeTransitionMode.Linear => BlendValueLerp(baseValue, value, blend), + VolumeTransitionMode.Smoothstep => BlendValueSmoothStep(baseValue, value, blend), + _ => value + }; + property.SetValue(target, value); + } + catch (Exception) + { + } + } + } + } + } + + private static object BlendValueLerp(object? baseValue, object value, float blend) + { + if (baseValue is Vector2 v20 && value is Vector2 v21) + { + return MathUtil.Lerp(v20, v21, blend); + } + else if (baseValue is Vector3 v30 && value is Vector3 v31) + { + return MathUtil.Lerp(v30, v31, blend); + } + else if (baseValue is Vector4 v40 && value is Vector4 v41) + { + return MathUtil.Lerp(v40, v41, blend); + } + else if (baseValue is float f0 && value is float f1) + { + return MathUtil.Lerp(f0, f1, blend); + } + else if (baseValue is int i0 && value is int i1) + { + return (int)MathUtil.Lerp(i0, i1, blend); + } + else if (baseValue is double d0 && value is double d1) + { + return MathUtil.Lerp(d0, d1, blend); + } + else if (baseValue is bool b0 && value is bool b1) + { + return blend >= 0.5f ? b0 : b1; + } + + return value; + } + + private static object BlendValueSmoothStep(object? baseValue, object value, float blend) + { + if (baseValue is Vector2 v20 && value is Vector2 v21) + { + return MathUtil.SmoothStep(v20, v21, blend); + } + else if (baseValue is Vector3 v30 && value is Vector3 v31) + { + return MathUtil.SmoothStep(v30, v31, blend); + } + else if (baseValue is Vector4 v40 && value is Vector4 v41) + { + return MathUtil.SmoothStep(v40, v41, blend); + } + else if (baseValue is float f0 && value is float f1) + { + return MathUtil.SmoothStep(f0, f1, blend); + } + else if (baseValue is int i0 && value is int i1) + { + return (int)MathUtil.SmoothStep(i0, i1, blend); + } + else if (baseValue is double d0 && value is double d1) + { + return MathUtil.SmoothStep(d0, d1, blend); + } + else if (baseValue is bool b0 && value is bool b1) + { + return blend <= 0.5f ? b0 : b1; + } + + return value; + } } } \ No newline at end of file diff --git a/HexaEngine/Volumes/PostFxSettingsContainer.cs b/HexaEngine/Volumes/PostFxSettingsContainer.cs index 6c5511bf..0df2d6f8 100644 --- a/HexaEngine/Volumes/PostFxSettingsContainer.cs +++ b/HexaEngine/Volumes/PostFxSettingsContainer.cs @@ -63,6 +63,14 @@ public void Apply(IReadOnlyList effects) } } + public void Apply(IReadOnlyList effects, PostFxSettingsContainer baseContainer, float blend, VolumeTransitionMode mode) + { + foreach (var effect in effects) + { + proxiesDictionary[effect].Apply(effect, baseContainer.proxiesDictionary[effect], blend, mode); + } + } + public void Clear() { proxies.Clear(); diff --git a/HexaEngine/Volumes/Volume.cs b/HexaEngine/Volumes/Volume.cs index af1a2304..6214b033 100644 --- a/HexaEngine/Volumes/Volume.cs +++ b/HexaEngine/Volumes/Volume.cs @@ -9,7 +9,7 @@ /// Represents a Volume in 3D space and controls post-processing and weather effects. /// [EditorGameObject("Volume")] - public class Volume : GameObject + public class Volume : GameObject, IVolume { /// /// Gets or sets the Volume mode. @@ -21,6 +21,16 @@ public class Volume : GameObject /// public VolumeShape Shape { get; set; } + /// + /// Gets or sets the Volume transition mode. + /// + public VolumeTransitionMode TransitionMode { get; set; } + + /// + /// The volume transition duration in milliseconds. + /// + public int TransitionDuration { get; set; } = 1000; + /// /// Gets or sets the bounding box of the Volume. /// @@ -33,17 +43,24 @@ public class Volume : GameObject public PostFxSettingsContainer Container { get; } = new(); - public override void Initialize() + void IVolume.Init(PostProcessingManager manager) + { + Container.Build(manager.Effects); + } + + void IVolume.Apply(PostProcessingManager manager) + { + using (manager.SupressReload()) + { + Container.Apply(manager.Effects); + } + } + + void IVolume.Apply(PostProcessingManager manager, IVolume baseVolume, float blend, VolumeTransitionMode mode) { - base.Initialize(); - PostProcessingManager postManager = PostProcessingManager.Current ?? throw new(); - Container.Build(postManager.Effects); - if (Mode == VolumeMode.Global) + using (manager.SupressReload()) { - using (postManager.SupressReload()) - { - Container.Apply(postManager.Effects); - } + Container.Apply(manager.Effects, baseVolume.Container, blend, mode); } } } diff --git a/HexaEngine/Volumes/VolumeMode.cs b/HexaEngine/Volumes/VolumeMode.cs index dabb2f2d..73ec2a35 100644 --- a/HexaEngine/Volumes/VolumeMode.cs +++ b/HexaEngine/Volumes/VolumeMode.cs @@ -14,34 +14,8 @@ public enum VolumeShape public enum VolumeTransitionMode { - Hard, + Constant, Linear, Smoothstep, - CustomCurve - } - - public enum CurveMode - { - Curve1ControlPoint, - Curve2ControlPoint, - Curve3ControlPoint, - Curve4ControlPoint, - Curve5ControlPoint, - Curve6ControlPoint, - Curve7ControlPoint, - Curve8ControlPoint, - } - - public struct Curve - { - public CurveMode Mode; - public float ControlPoint1; - public float ControlPoint2; - public float ControlPoint3; - public float ControlPoint4; - public float ControlPoint5; - public float ControlPoint6; - public float ControlPoint7; - public float ControlPoint8; } } \ No newline at end of file diff --git a/HexaEngine/Volumes/VolumeSystem.cs b/HexaEngine/Volumes/VolumeSystem.cs index 322f7eff..9ba67426 100644 --- a/HexaEngine/Volumes/VolumeSystem.cs +++ b/HexaEngine/Volumes/VolumeSystem.cs @@ -1,11 +1,379 @@ namespace HexaEngine.Volumes { + using HexaEngine.Core.Debugging; + using HexaEngine.Mathematics; + using HexaEngine.PostFx; + using HexaEngine.Queries.Generic; using HexaEngine.Scenes; + using HexaEngine.Scenes.Managers; + using System.Diagnostics; + using System.Numerics; + + public struct VolumeTransition : IEquatable + { + public long Start; + public long Duration; + public IVolume? From; + public IVolume? To; + public VolumeTransitionMode Mode; + + public VolumeTransition(long start, long duration, Volume? from, Volume? to, VolumeTransitionMode mode) + { + Start = start; + Duration = duration; + From = from; + To = to; + Mode = mode; + } + + public override readonly bool Equals(object? obj) + { + return obj is VolumeTransition transition && Equals(transition); + } + + public readonly bool Equals(VolumeTransition other) + { + return Start == other.Start && + Duration.Equals(other.Duration) && + EqualityComparer.Default.Equals(From, other.From) && + EqualityComparer.Default.Equals(To, other.To); + } + + public readonly float GetBlendValue(long now) + { + long elapsed = now - Start; + float blendValue = elapsed / (float)Duration; + return MathUtil.Clamp01(blendValue); + } + + public override readonly int GetHashCode() + { + return HashCode.Combine(Start, Duration, From, To); + } + + public static bool operator ==(VolumeTransition left, VolumeTransition right) + { + return left.Equals(right); + } + + public static bool operator !=(VolumeTransition left, VolumeTransition right) + { + return !(left == right); + } + } + + public delegate void VolumeTransitionEventHandler(VolumeTransition transition); + + public delegate void VolumeTransitionTickEventHandler(VolumeTransition transition, float value); + + public class DelegateList where T : Delegate + { + private readonly List handlers = new(); + + public IReadOnlyList Handlers => handlers; + + public int Count => handlers.Count; + + public T this[int index] + { + get => handlers[index]; + } + + public void AddHandler(T handler) + { + lock (handlers) + { + handlers.Add(handler); + } + } + + public void RemoveHandler(T handler) + { + lock (handlers) + { + handlers.Remove(handler); + } + } + + public void Clear() + { + handlers.Clear(); + } + } public class VolumeSystem : ISceneSystem { + private static readonly ILogger Logger = LoggerFactory.GetLogger(nameof(VolumeSystem)); + private PostProcessingManager postManager; + private readonly ObjectTypeQuery volumes = new(); + private Volume? activeVolume; + + private readonly List transitions = []; + public string Name { get; } = "VolumeSystem"; - public SystemFlags Flags { get; } + public SystemFlags Flags { get; } = SystemFlags.Awake | SystemFlags.Destroy | SystemFlags.Update; + + public Volume? ActiveVolume => activeVolume; + + private readonly DelegateList transitionStartList = new(); + private readonly DelegateList transitionEndList = new(); + private readonly DelegateList transitionAbortedList = new(); + private readonly DelegateList transitionTickList = new(); + + public event VolumeTransitionEventHandler TransitionStart + { + add => transitionStartList.AddHandler(value); + remove => transitionStartList.RemoveHandler(value); + } + + public event VolumeTransitionEventHandler TransitionEnd + { + add => transitionEndList.AddHandler(value); + remove => transitionEndList.RemoveHandler(value); + } + + public event VolumeTransitionEventHandler TransitionAborted + { + add => transitionAbortedList.AddHandler(value); + remove => transitionAbortedList.RemoveHandler(value); + } + + public event VolumeTransitionTickEventHandler TransitionTick + { + add => transitionTickList.AddHandler(value); + remove => transitionTickList.RemoveHandler(value); + } + + public void Awake(Scene scene) + { + scene.QueryManager.AddQuery(volumes); + + volumes.OnAdded += OnAdded; + volumes.OnRemoved += OnRemoved; + + postManager = PostProcessingManager.Current ?? throw new Exception("Cannot initialize without post fx"); + + //SetDefaults(); + + bool hasGlobal = false; + for (int i = 0; i < volumes.Count; i++) + { + var volume = volumes[i]; + ((IVolume)volume).Init(postManager); + if (volume.Mode == VolumeMode.Global) + { + if (hasGlobal) + { + Logger.Warn("Multiple global Volumes where found, first found will be used."); + } + hasGlobal = true; + } + else + { + for (int j = i + 1; j < volumes.Count; j++) + { + bool intersects = false; + var other = volumes[j]; + switch (other.Shape) + { + case VolumeShape.Box: + switch (volume.Shape) + { + case VolumeShape.Box: + intersects = other.BoundingBox.Intersects(volume.BoundingBox); + break; + + case VolumeShape.Sphere: + intersects = other.BoundingBox.Intersects(volume.BoundingSphere); + break; + } + break; + + case VolumeShape.Sphere: + switch (volume.Shape) + { + case VolumeShape.Box: + intersects = other.BoundingSphere.Intersects(volume.BoundingBox); + break; + + case VolumeShape.Sphere: + intersects = other.BoundingSphere.Intersects(volume.BoundingSphere); + break; + } + break; + } + + if (intersects) + { + Logger.Warn($"Volume '{volume}' and Volume '{other}' are overlapping, this can cause visual issues."); + } + } + } + } + } + + private void OnRemoved(Volume volume) + { + } + + private void OnAdded(Volume volume) + { + ((IVolume)volume).Init(postManager); + } + + public void TransitionTo(long now, int duration, VolumeTransitionMode transitionMode, Volume? from, Volume? to) + { + int index = -1; + VolumeTransition existingTransition = default; + for (int i = 0; i < transitions.Count; i++) + { + var trans = transitions[i]; + if (trans.From == to && trans.To == from) + { + index = i; + existingTransition = trans; + break; + } + } + + if (existingTransition != default) + { + float reverseBlend = 1 - existingTransition.GetBlendValue(now); + long remainingTicks = (long)(existingTransition.Duration * reverseBlend); + now -= remainingTicks; + transitions.RemoveAt(index); + for (int i = 0; i < transitionAbortedList.Count; i++) + { + transitionAbortedList[i](existingTransition); + } + } + + long durationInTicks = (long)(duration / 1000.0f * Stopwatch.Frequency); + + if (transitionMode == VolumeTransitionMode.Constant) + { + durationInTicks = 0; // simply set the duration to 0 this simply disables blending. + } + + VolumeTransition transition = new(now, durationInTicks, from, to, transitionMode); + + transitions.Add(transition); + for (int i = 0; i < transitionStartList.Count; i++) + { + transitionStartList[i](transition); + } + } + + public void Update(float delta) + { + var camera = CameraManager.Current; + + if (camera == null) + { + return; + } + + Vector3 camPos = camera.Transform.GlobalPosition; + + Volume? global = null; + Volume? local = null; + for (int i = 0; i < volumes.Count; i++) + { + var volume = volumes[i]; + + switch (volume.Mode) + { + case VolumeMode.Global: + global ??= volume; + break; + + case VolumeMode.Local: + break; + } + + bool intersects = false; + switch (volume.Shape) + { + case VolumeShape.Box: + var box = BoundingBox.Transform(volume.BoundingBox, volume.Transform); + intersects = box.Contains(camPos) != ContainmentType.Disjoint; + break; + + case VolumeShape.Sphere: + var sphere = BoundingSphere.Transform(volume.BoundingSphere, volume.Transform); + intersects = sphere.Contains(camPos) != ContainmentType.Disjoint; + break; + } + + if (intersects) + { + local = volume; + break; + } + } + + Volume? newActiveVolume = local ?? global; + + long now = Stopwatch.GetTimestamp(); + + if (newActiveVolume != activeVolume) + { + TransitionTo(now, newActiveVolume?.TransitionDuration ?? 0, newActiveVolume?.TransitionMode ?? 0, activeVolume, newActiveVolume); + activeVolume = newActiveVolume; + } + + for (int i = 0; i < transitions.Count; i++) + { + var transition = transitions[i]; + var blend = transition.GetBlendValue(now); + + for (int j = 0; j < transitionTickList.Count; i++) + { + transitionTickList[j](transition, blend); + } + + bool remove; + if (transition.To == null) + { + postManager.ResetSettings(); + remove = true; + } + else + { + if (transition.From == null) + { + transition.To.Apply(postManager); + remove = true; + } + else + { + transition.To.Apply(postManager, transition.From, blend, transition.Mode); + remove = blend == 1; + } + } + + if (remove) + { + transitions.RemoveAt(i); + i--; + + for (int j = 0; j < transitionEndList.Count; i++) + { + transitionEndList[j](transition); + } + } + } + } + + public void Destroy() + { + volumes.Dispose(); + postManager = null; + transitionStartList.Clear(); + transitionEndList.Clear(); + transitionAbortedList.Clear(); + transitionTickList.Clear(); + } } } \ No newline at end of file diff --git a/HexaEngine/assets/shared/shaders/deferred/brdf/light.hlsl b/HexaEngine/assets/shared/shaders/deferred/brdf/light.hlsl index 14e92bea..a250c44c 100644 --- a/HexaEngine/assets/shared/shaders/deferred/brdf/light.hlsl +++ b/HexaEngine/assets/shared/shaders/deferred/brdf/light.hlsl @@ -8,27 +8,27 @@ Texture2D Depth : register(t15); struct VSOut { - float4 Pos : SV_Position; - float2 Tex : TEXCOORD; + float4 Pos : SV_Position; + float2 Tex : TEXCOORD; }; float4 main(VSOut pin) : SV_TARGET { - float depth = Depth.Sample(linearWrapSampler, pin.Tex); - if (depth == 1) - discard; - float3 position = GetPositionWS(pin.Tex, depth); - GeometryAttributes attrs; - ExtractGeometryData(pin.Tex, GBufferA, GBufferB, GBufferC, GBufferD, linearWrapSampler, attrs); + float depth = Depth.Sample(linearWrapSampler, pin.Tex); + if (depth == 1) + discard; + float3 position = GetPositionWS(pin.Tex, depth); + GeometryAttributes attrs; + ExtractGeometryData(pin.Tex, GBufferA, GBufferB, GBufferC, GBufferD, linearWrapSampler, attrs); - float3 N = normalize(attrs.normal); - float3 VN = camPos - position; - float3 V = normalize(VN); + float3 N = normalize(attrs.normal); + float3 VN = camPos - position; + float3 V = normalize(VN); - PixelParams pixel = ComputeSurfaceProps(position, V, N, attrs.baseColor, attrs.roughness, attrs.metallic, attrs.reflectance); + PixelParams pixel = ComputeSurfaceProps(position, V, N, attrs.baseColor, attrs.roughness, attrs.metallic, attrs.reflectance); - float3 direct = ComputeDirectLightning(depth, pixel); - float3 ambient = ComputeIndirectLightning(pin.Tex, pixel, attrs.ao, attrs.emission); + float3 direct = ComputeDirectLightning(depth, pixel); + float3 ambient = ComputeIndirectLightning(pin.Tex, pixel, attrs.ao, attrs.emission); - return float4(ambient + direct, 1); + return float4(ambient + direct, 1); } \ No newline at end of file diff --git a/HexaEngine/assets/shared/shaders/forward/sky/preethamSky.hlsl b/HexaEngine/assets/shared/shaders/forward/sky/preethamSky.hlsl index 3ece4b09..854e15df 100644 --- a/HexaEngine/assets/shared/shaders/forward/sky/preethamSky.hlsl +++ b/HexaEngine/assets/shared/shaders/forward/sky/preethamSky.hlsl @@ -79,5 +79,5 @@ float4 main(VertexOut pin) : SV_TARGET float3 L0 = sunColor * brightness * sunIntensity; float3 finalColor = skyLuminance + L0; - return float4(finalColor, 1.0); + return float4(finalColor, 0.0); } \ No newline at end of file