diff --git a/.editorconfig b/.editorconfig
index b314c0c..4803563 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -13,7 +13,7 @@ spelling_exclusion_path = SpellingExclusions.dic
[*.{cs,csx,vb,vbx}]
indent_size = 4
insert_final_newline = true
-charset = utf-8-bom
+charset = utf-8
# XML project files
[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
diff --git a/Engine.Core/Abstract/ICamera.cs b/Engine.Core/Abstract/ICamera.cs
new file mode 100644
index 0000000..cc9912d
--- /dev/null
+++ b/Engine.Core/Abstract/ICamera.cs
@@ -0,0 +1,22 @@
+namespace Engine.Core;
+
+///
+/// Represents a camera with view and projections matrices in the engine.
+///
+public interface ICamera
+{
+ ///
+ /// The viewport of the .
+ ///
+ Vector2D Viewport { get; }
+
+ ///
+ /// View of the .
+ ///
+ Matrix4x4 ViewMatrix { get; }
+
+ ///
+ /// Projection of the .
+ ///
+ Matrix4x4 ProjectionMatrix { get; }
+}
diff --git a/Engine.Core/Abstract/ICamera2D.cs b/Engine.Core/Abstract/ICamera2D.cs
index 4c12105..941fcd5 100644
--- a/Engine.Core/Abstract/ICamera2D.cs
+++ b/Engine.Core/Abstract/ICamera2D.cs
@@ -3,7 +3,7 @@ namespace Engine.Core;
///
/// Represents a 2D camera in the engine.
///
-public interface ICamera2D : IBehaviour2D
+public interface ICamera2D : ICamera, IBehaviour2D
{
///
/// The zoom level of the camera.
diff --git a/Engine.Core/Abstract/ICamera3D.cs b/Engine.Core/Abstract/ICamera3D.cs
index a0024a0..b2346bb 100644
--- a/Engine.Core/Abstract/ICamera3D.cs
+++ b/Engine.Core/Abstract/ICamera3D.cs
@@ -3,7 +3,7 @@ namespace Engine.Core;
///
/// Represents a 3D camera in the engine.
///
-public interface ICamera3D : IBehaviour3D
+public interface ICamera3D : ICamera, IBehaviour3D
{
///
/// Event triggered when the near plane of the changes.
diff --git a/Engine.Core/Abstract/IHasId.cs b/Engine.Core/Abstract/IIdentifiable.cs
similarity index 100%
rename from Engine.Core/Abstract/IHasId.cs
rename to Engine.Core/Abstract/IIdentifiable.cs
diff --git a/Engine.Core/BehaviourInternal.cs b/Engine.Core/BehaviourIndependent.cs
similarity index 100%
rename from Engine.Core/BehaviourInternal.cs
rename to Engine.Core/BehaviourIndependent.cs
diff --git a/Engine.Core/Config/BasicConfiguration.cs b/Engine.Core/Config/BasicConfiguration.cs
new file mode 100644
index 0000000..8f833c7
--- /dev/null
+++ b/Engine.Core/Config/BasicConfiguration.cs
@@ -0,0 +1,51 @@
+using System.Collections.Generic;
+
+namespace Engine.Core.Config;
+
+public class BasicConfiguration : IConfiguration
+{
+ public Event OnAdded { get; } = new();
+ public Event OnSet { get; } = new();
+ public Event OnRemoved { get; } = new();
+
+ private readonly Dictionary values = [];
+
+ public IReadOnlyDictionary Values => values;
+
+ public T Get(string key, T defaultValue) => Get(key) ?? defaultValue;
+ public T? Get(string key)
+ {
+ if (!values.TryGetValue(key, out object? value))
+ return default;
+
+ if (value is T castedObject)
+ return castedObject;
+
+ try { return (T?)System.Convert.ChangeType(value, typeof(T)); } catch { }
+
+ return default;
+ }
+
+ public object? Get(string key)
+ {
+ values.TryGetValue(key, out object? value);
+ return value;
+ }
+
+ public bool Has(string key) => values.ContainsKey(key);
+
+ public void Remove(string key)
+ {
+ if (values.Remove(key))
+ OnRemoved.Invoke(this, new(key));
+ }
+
+ public void Set(string key, T value)
+ {
+ if (!values.TryAdd(key, value))
+ values[key] = value;
+ else
+ OnAdded.Invoke(this, new(key));
+ OnSet.Invoke(this, new(key));
+ }
+}
diff --git a/Engine.Core/Config/ConfigurationExtensions.cs b/Engine.Core/Config/ConfigurationExtensions.cs
new file mode 100644
index 0000000..f83416f
--- /dev/null
+++ b/Engine.Core/Config/ConfigurationExtensions.cs
@@ -0,0 +1,8 @@
+using Engine.Core.Exceptions;
+
+namespace Engine.Core.Config;
+
+public static class ConfigurationExtensions
+{
+ public static T GetRequired(this IConfiguration configuration, string key) => configuration.Get(key) ?? throw new NotFoundException($"Type of {typeof(T).FullName} with the key {key} was not present in the {configuration.GetType().FullName}");
+}
diff --git a/Engine.Core/Config/IConfiguration.cs b/Engine.Core/Config/IConfiguration.cs
new file mode 100644
index 0000000..15abbb9
--- /dev/null
+++ b/Engine.Core/Config/IConfiguration.cs
@@ -0,0 +1,24 @@
+using System.Collections.Generic;
+
+namespace Engine.Core.Config;
+
+public interface IConfiguration
+{
+ static IConfiguration System { get; set; } = new SystemConfiguration();
+ static IConfiguration Shared { get; set; } = new BasicConfiguration();
+
+ Event OnAdded { get; }
+ Event OnSet { get; }
+ Event OnRemoved { get; }
+
+ IReadOnlyDictionary Values { get; }
+
+ bool Has(string key);
+ object? Get(string key);
+ T Get(string key, T defaultValue);
+ T? Get(string key);
+ void Set(string key, T value);
+ void Remove(string key);
+
+ readonly record struct ConfigUpdateArguments(string Key);
+}
diff --git a/Engine.Core/Config/SystemConfiguration.cs b/Engine.Core/Config/SystemConfiguration.cs
new file mode 100644
index 0000000..895b4bf
--- /dev/null
+++ b/Engine.Core/Config/SystemConfiguration.cs
@@ -0,0 +1,11 @@
+namespace Engine.Core.Config;
+
+public class SystemConfiguration : BasicConfiguration, IConfiguration
+{
+ public SystemConfiguration()
+ {
+ foreach (System.Collections.DictionaryEntry entry in System.Environment.GetEnvironmentVariables())
+ if (entry is { Key: string key, Value: not null })
+ Set(key, entry.Value);
+ }
+}
diff --git a/Engine.Core/Debug/AssertHelpers.cs b/Engine.Core/Debug/Assert.cs
similarity index 100%
rename from Engine.Core/Debug/AssertHelpers.cs
rename to Engine.Core/Debug/Assert.cs
diff --git a/Engine.Core/Debug/RotatingFileLogger.cs b/Engine.Core/Debug/RotatingFileLogger.cs
index ec3e007..fa4911d 100644
--- a/Engine.Core/Debug/RotatingFileLogger.cs
+++ b/Engine.Core/Debug/RotatingFileLogger.cs
@@ -55,7 +55,7 @@ public class RotatingFileLogger : ILogger
private static void RotateLastLogs(string directory, string prefix, int rotateLength)
{
IOrderedEnumerable logs = System.IO.Directory.GetFiles(directory, $"{prefix}*.log")
- .OrderBy(File.GetCreationTime);
+ .OrderByDescending(File.GetCreationTime);
foreach (string file in logs.Skip(rotateLength))
try
diff --git a/Engine.Core/Extensions/IdentifiableExtensions.cs b/Engine.Core/Extensions/IdentifiableExtensions.cs
new file mode 100644
index 0000000..0bb8f74
--- /dev/null
+++ b/Engine.Core/Extensions/IdentifiableExtensions.cs
@@ -0,0 +1,12 @@
+namespace Engine.Core;
+
+public static class IdentifiableExtensions
+{
+ public static bool IsIdentical(this IIdentifiable? left, IIdentifiable? right)
+ {
+ if (left == null || right == null)
+ return false;
+
+ return left?.Id?.CompareTo(right?.Id) == 0;
+ }
+}
diff --git a/Engine.Core/Helpers/Event.cs b/Engine.Core/Helpers/Event.cs
index c008c52..042a29b 100644
--- a/Engine.Core/Helpers/Event.cs
+++ b/Engine.Core/Helpers/Event.cs
@@ -58,6 +58,9 @@ public class Event
public ILogger Logger { get; set => field = value ?? ILogger.Shared; } = ILogger.Shared;
+ private int currentOnceCallIndex = -1; // These are for the purpose of if a listener is added/removed during invocation the index is dynamically updated so no missing/duplicate invocations happen.
+ private int currentCallIndex = -1; // These are for the purpose of if a listener is added/removed during invocation the index is dynamically updated so no missing/duplicate invocations happen.
+
private readonly List listeners = null!;
private readonly List onceListeners = null!;
@@ -74,6 +77,9 @@ public class Event
if (insertIndex < 0)
insertIndex = ~insertIndex;
+ if (insertIndex < currentCallIndex)
+ currentCallIndex++;
+
listeners.Insert(insertIndex, listenerData);
}
@@ -90,6 +96,9 @@ public class Event
if (insertIndex < 0)
insertIndex = ~insertIndex;
+ if (insertIndex < currentOnceCallIndex)
+ currentOnceCallIndex++;
+
onceListeners.Insert(insertIndex, listenerData);
}
@@ -103,6 +112,8 @@ public class Event
if (listeners[i].Callback == listener)
{
listeners.RemoveAt(i);
+ if (i < currentCallIndex)
+ currentCallIndex--;
return;
}
}
@@ -117,6 +128,8 @@ public class Event
if (onceListeners[i].Callback == listener)
{
onceListeners.RemoveAt(i);
+ if (i < currentOnceCallIndex)
+ currentOnceCallIndex--;
return;
}
}
@@ -131,23 +144,23 @@ public class Event
///
public void Invoke()
{
- for (int i = listeners.Count - 1; i >= 0; i--)
- try { listeners[i].Callback.Invoke(); }
+ for (currentCallIndex = listeners.Count - 1; currentCallIndex >= 0; currentCallIndex--)
+ try { listeners[currentCallIndex].Callback.Invoke(); }
catch (Exception exception)
{
- string methodCallRepresentation = $"{listeners[i].Callback.Method.DeclaringType?.FullName}.{listeners[i].Callback.Method.Name}()";
- EventHelpers.LogInvocationException(listeners[i].Callback.Target ?? this, Logger, exception, methodCallRepresentation);
+ string methodCallRepresentation = $"{listeners[currentCallIndex].Callback.Method.DeclaringType?.FullName}.{listeners[currentCallIndex].Callback.Method.Name}()";
+ EventHelpers.LogInvocationException(listeners[currentCallIndex].Callback.Target ?? this, Logger, exception, methodCallRepresentation);
}
- for (int i = onceListeners.Count - 1; i >= 0; i--)
+ for (currentOnceCallIndex = onceListeners.Count - 1; currentOnceCallIndex >= 0; currentOnceCallIndex--)
{
- try { onceListeners[i].Callback.Invoke(); }
+ try { onceListeners[currentOnceCallIndex].Callback.Invoke(); }
catch (Exception exception)
{
- string methodCallRepresentation = $"{onceListeners[i].Callback.Method.DeclaringType?.FullName}.{onceListeners[i].Callback.Method.Name}()";
- EventHelpers.LogInvocationException(onceListeners[i].Callback.Target ?? this, Logger, exception, methodCallRepresentation);
+ string methodCallRepresentation = $"{onceListeners[currentOnceCallIndex].Callback.Method.DeclaringType?.FullName}.{onceListeners[currentOnceCallIndex].Callback.Method.Name}()";
+ EventHelpers.LogInvocationException(onceListeners[currentOnceCallIndex].Callback.Target ?? this, Logger, exception, methodCallRepresentation);
}
- onceListeners.RemoveAt(i);
+ onceListeners.RemoveAt(currentOnceCallIndex);
}
}
@@ -216,6 +229,9 @@ public class Event where TSender : class
public ILogger Logger { get; set => field = value ?? ILogger.Shared; } = ILogger.Shared;
+ private int currentOnceCallIndex = -1; // These are for the purpose of if a listener is added/removed during invocation the index is dynamically updated so no missing/duplicate invocations happen.
+ private int currentCallIndex = -1; // These are for the purpose of if a listener is added/removed during invocation the index is dynamically updated so no missing/duplicate invocations happen.
+
private readonly List listeners = null!;
private readonly List onceListeners = null!;
@@ -232,6 +248,9 @@ public class Event where TSender : class
if (insertIndex < 0)
insertIndex = ~insertIndex;
+ if (insertIndex < currentCallIndex)
+ currentCallIndex++;
+
listeners.Insert(insertIndex, listenerData);
}
@@ -248,6 +267,9 @@ public class Event where TSender : class
if (insertIndex < 0)
insertIndex = ~insertIndex;
+ if (insertIndex < currentOnceCallIndex)
+ currentOnceCallIndex++;
+
onceListeners.Insert(insertIndex, listenerData);
}
@@ -261,6 +283,8 @@ public class Event where TSender : class
if (listeners[i].Callback == listener)
{
listeners.RemoveAt(i);
+ if (i < currentCallIndex)
+ currentCallIndex--;
return;
}
}
@@ -275,6 +299,8 @@ public class Event where TSender : class
if (onceListeners[i].Callback == listener)
{
onceListeners.RemoveAt(i);
+ if (i < currentOnceCallIndex)
+ currentOnceCallIndex--;
return;
}
}
@@ -290,23 +316,23 @@ public class Event where TSender : class
/// The caller that's triggering this event.
public void Invoke(TSender sender)
{
- for (int i = listeners.Count - 1; i >= 0; i--)
- try { listeners[i].Callback.Invoke(sender); }
+ for (currentCallIndex = listeners.Count - 1; currentCallIndex >= 0; currentCallIndex--)
+ try { listeners[currentCallIndex].Callback.Invoke(sender); }
catch (Exception exception)
{
- string methodCallRepresentation = $"{listeners[i].Callback.Method.DeclaringType?.FullName}.{listeners[i].Callback.Method.Name}({sender})";
- EventHelpers.LogInvocationException(listeners[i].Callback.Target ?? sender, Logger, exception, methodCallRepresentation);
+ string methodCallRepresentation = $"{listeners[currentCallIndex].Callback.Method.DeclaringType?.FullName}.{listeners[currentCallIndex].Callback.Method.Name}({sender})";
+ EventHelpers.LogInvocationException(listeners[currentCallIndex].Callback.Target ?? sender, Logger, exception, methodCallRepresentation);
}
- for (int i = onceListeners.Count - 1; i >= 0; i--)
+ for (currentOnceCallIndex = onceListeners.Count - 1; currentOnceCallIndex >= 0; currentOnceCallIndex--)
{
- try { onceListeners[i].Callback.Invoke(sender); }
+ try { onceListeners[currentOnceCallIndex].Callback.Invoke(sender); }
catch (Exception exception)
{
- string methodCallRepresentation = $"{onceListeners[i].Callback.Method.DeclaringType?.FullName}.{onceListeners[i].Callback.Method.Name}({sender})";
- EventHelpers.LogInvocationException(onceListeners[i].Callback.Target ?? sender, Logger, exception, methodCallRepresentation);
+ string methodCallRepresentation = $"{onceListeners[currentOnceCallIndex].Callback.Method.DeclaringType?.FullName}.{onceListeners[currentOnceCallIndex].Callback.Method.Name}({sender})";
+ EventHelpers.LogInvocationException(onceListeners[currentOnceCallIndex].Callback.Target ?? sender, Logger, exception, methodCallRepresentation);
}
- onceListeners.RemoveAt(i);
+ onceListeners.RemoveAt(currentOnceCallIndex);
}
}
@@ -382,6 +408,9 @@ public class Event where TSender : class
public ILogger Logger { get; set => field = value ?? ILogger.Shared; } = ILogger.Shared;
+ private int currentOnceCallIndex = -1; // These are for the purpose of if a listener is added/removed during invocation the index is dynamically updated so no missing/duplicate invocations happen.
+ private int currentCallIndex = -1; // These are for the purpose of if a listener is added/removed during invocation the index is dynamically updated so no missing/duplicate invocations happen.
+
private readonly List listeners = null!;
private readonly List onceListeners = null!;
@@ -398,6 +427,9 @@ public class Event where TSender : class
if (insertIndex < 0)
insertIndex = ~insertIndex;
+ if (insertIndex < currentCallIndex)
+ currentCallIndex++;
+
listeners.Insert(insertIndex, listenerData);
}
@@ -414,6 +446,9 @@ public class Event where TSender : class
if (insertIndex < 0)
insertIndex = ~insertIndex;
+ if (insertIndex < currentOnceCallIndex)
+ currentOnceCallIndex++;
+
onceListeners.Insert(insertIndex, listenerData);
}
@@ -427,6 +462,8 @@ public class Event where TSender : class
if (listeners[i].Callback == listener)
{
listeners.RemoveAt(i);
+ if (i < currentCallIndex)
+ currentCallIndex--;
return;
}
}
@@ -441,6 +478,8 @@ public class Event where TSender : class
if (onceListeners[i].Callback == listener)
{
onceListeners.RemoveAt(i);
+ if (i < currentOnceCallIndex)
+ currentOnceCallIndex--;
return;
}
}
@@ -457,23 +496,23 @@ public class Event where TSender : class
/// The arguments provided for this event.
public void Invoke(TSender sender, TArguments args)
{
- for (int i = listeners.Count - 1; i >= 0; i--)
- try { listeners[i].Callback.Invoke(sender, args); }
+ for (currentCallIndex = listeners.Count - 1; currentCallIndex >= 0; currentCallIndex--)
+ try { listeners[currentCallIndex].Callback.Invoke(sender, args); }
catch (Exception exception)
{
- string methodCallRepresentation = $"{listeners[i].Callback.Method.DeclaringType?.FullName}.{listeners[i].Callback.Method.Name}({sender}, {args})";
- EventHelpers.LogInvocationException(listeners[i].Callback.Target ?? sender, Logger, exception, methodCallRepresentation);
+ string methodCallRepresentation = $"{listeners[currentCallIndex].Callback.Method.DeclaringType?.FullName}.{listeners[currentCallIndex].Callback.Method.Name}({sender}, {args})";
+ EventHelpers.LogInvocationException(listeners[currentCallIndex].Callback.Target ?? sender, Logger, exception, methodCallRepresentation);
}
- for (int i = onceListeners.Count - 1; i >= 0; i--)
+ for (currentOnceCallIndex = onceListeners.Count - 1; currentOnceCallIndex >= 0; currentOnceCallIndex--)
{
- try { onceListeners[i].Callback.Invoke(sender, args); }
+ try { onceListeners[currentOnceCallIndex].Callback.Invoke(sender, args); }
catch (Exception exception)
{
- string methodCallRepresentation = $"{onceListeners[i].Callback.Method.DeclaringType?.FullName}.{onceListeners[i].Callback.Method.Name}({sender}, {args})";
- EventHelpers.LogInvocationException(onceListeners[i].Callback.Target ?? sender, Logger, exception, methodCallRepresentation);
+ string methodCallRepresentation = $"{onceListeners[currentOnceCallIndex].Callback.Method.DeclaringType?.FullName}.{onceListeners[currentOnceCallIndex].Callback.Method.Name}({sender}, {args})";
+ EventHelpers.LogInvocationException(onceListeners[currentOnceCallIndex].Callback.Target ?? sender, Logger, exception, methodCallRepresentation);
}
- onceListeners.RemoveAt(i);
+ onceListeners.RemoveAt(currentOnceCallIndex);
}
}
diff --git a/Engine.Core/Helpers/FastListOrdered.cs b/Engine.Core/Helpers/FastListOrdered.cs
index d99c826..cd72982 100644
--- a/Engine.Core/Helpers/FastListOrdered.cs
+++ b/Engine.Core/Helpers/FastListOrdered.cs
@@ -16,8 +16,7 @@ public class FastListOrdered : IList, IReadOnlyList
private readonly Func getIndexFunc = null!;
private readonly IComparer sortBy = null!;
- private int count = 0;
- public int Count => count;
+ public int Count { get; private set; } = 0;
public bool IsReadOnly { get; set; } = false;
@@ -35,10 +34,10 @@ public class FastListOrdered : IList, IReadOnlyList
private (TIndex TIndex, int i) GetAt(Index index)
{
int actualIndex = index.IsFromEnd
- ? count - index.Value
+ ? Count - index.Value
: index.Value;
- if (actualIndex < 0 || actualIndex >= count)
+ if (actualIndex < 0 || actualIndex >= Count)
throw new IndexOutOfRangeException();
int leftIndex = actualIndex;
@@ -75,7 +74,7 @@ public class FastListOrdered : IList, IReadOnlyList
items[key] = list = [];
list.Add(item);
- count++;
+ Count++;
}
public void Insert(int index, TItem item)
@@ -88,7 +87,7 @@ public class FastListOrdered : IList, IReadOnlyList
items[tIndex] = list = [];
list.Insert(index, item);
- count++;
+ Count++;
}
public bool Remove(TItem item)
@@ -103,7 +102,7 @@ public class FastListOrdered : IList, IReadOnlyList
if (!list.Remove(item))
return false;
- count--;
+ Count--;
return true;
}
@@ -114,7 +113,7 @@ public class FastListOrdered : IList, IReadOnlyList
(TIndex tIndex, int i) = GetAt(index);
items[tIndex].RemoveAt(i);
- count--;
+ Count--;
}
public void Clear()
@@ -125,7 +124,7 @@ public class FastListOrdered : IList, IReadOnlyList
foreach ((TIndex index, FastList list) in items)
list.Clear();
- count = 0;
+ Count = 0;
}
public bool Contains(TItem item)
diff --git a/Engine.Core/Primitives/ColorRGB.cs b/Engine.Core/Primitives/ColorRGB.cs
index 1fada67..afcc5e7 100644
--- a/Engine.Core/Primitives/ColorRGB.cs
+++ b/Engine.Core/Primitives/ColorRGB.cs
@@ -95,7 +95,11 @@ public readonly struct ColorRGB(byte r, byte g, byte b) : IEquatable
int greenDiff = to.G - from.G;
int blueDiff = to.B - from.B;
- return from + new ColorRGB((byte)(redDiff * t), (byte)(greenDiff * t), (byte)(blueDiff * t));
+ return new(
+ (byte)(from.R + redDiff * t),
+ (byte)(from.G + greenDiff * t),
+ (byte)(from.B + blueDiff * t)
+ );
}
///
diff --git a/Engine.Core/Primitives/ColorRGBA.cs b/Engine.Core/Primitives/ColorRGBA.cs
index 8163840..ce14ac8 100644
--- a/Engine.Core/Primitives/ColorRGBA.cs
+++ b/Engine.Core/Primitives/ColorRGBA.cs
@@ -1,4 +1,5 @@
using System;
+using Engine.Core.Debug;
namespace Engine.Core;
@@ -125,7 +126,12 @@ public readonly struct ColorRGBA(byte r, byte g, byte b, byte a = 255) : IEquata
int blueDiff = to.B - from.B;
int alphaDiff = to.A - from.A;
- return from + new ColorRGBA((byte)(redDiff * t), (byte)(greenDiff * t), (byte)(blueDiff * t), (byte)(alphaDiff * t));
+ return new(
+ (byte)(from.R + redDiff * t),
+ (byte)(from.G + greenDiff * t),
+ (byte)(from.B + blueDiff * t),
+ (byte)(from.A + alphaDiff * t)
+ );
}
///
diff --git a/Engine.Core/Primitives/Matrix4x4.cs b/Engine.Core/Primitives/Matrix4x4.cs
index 0288b41..cc6632f 100644
--- a/Engine.Core/Primitives/Matrix4x4.cs
+++ b/Engine.Core/Primitives/Matrix4x4.cs
@@ -1,12 +1,11 @@
using System;
-using System.Numerics;
namespace Engine.Core;
// TODO Comments
///
-/// Represents a 4D left handed space matrix.
+/// Represents a 4D left handed space matrix in a Column Major convention.
///
///
/// Initializes a new instance of the struct with the specified values.
@@ -62,6 +61,16 @@ public readonly struct Matrix4x4(
0f, 0f, 0f, 1f
);
+ ///
+ /// Represents the inverted version of this .
+ ///
+ public Matrix4x4 Inverse => Invert(this);
+
+ ///
+ /// Represents the transposed version of this .
+ ///
+ public Matrix4x4 Transposed => Transpose(this);
+
public static Matrix4x4 operator *(Matrix4x4 a, Matrix4x4 b) => new(
a.M11 * b.M11 + a.M12 * b.M21 + a.M13 * b.M31 + a.M14 * b.M41,
a.M11 * b.M12 + a.M12 * b.M22 + a.M13 * b.M32 + a.M14 * b.M42,
@@ -84,6 +93,13 @@ public readonly struct Matrix4x4(
a.M41 * b.M14 + a.M42 * b.M24 + a.M43 * b.M34 + a.M44 * b.M44
);
+ public static Vector4D operator *(Matrix4x4 m, Vector4D v) => new(
+ m.M11 * v.X + m.M12 * v.Y + m.M13 * v.Z + m.M14 * v.W,
+ m.M21 * v.X + m.M22 * v.Y + m.M23 * v.Z + m.M24 * v.W,
+ m.M31 * v.X + m.M32 * v.Y + m.M33 * v.Z + m.M34 * v.W,
+ m.M41 * v.X + m.M42 * v.Y + m.M43 * v.Z + m.M44 * v.W
+ );
+
public static bool operator ==(Matrix4x4 left, Matrix4x4 right) =>
left.M11 == right.M11 && left.M12 == right.M12 && left.M13 == right.M13 && left.M14 == right.M14 &&
left.M21 == right.M21 && left.M22 == right.M22 && left.M23 == right.M23 && left.M24 == right.M24 &&
@@ -129,11 +145,98 @@ public readonly struct Matrix4x4(
m.M13 * m.M21 * m.M32 * m.M44 - m.M11 * m.M23 * m.M32 * m.M44 -
m.M12 * m.M21 * m.M33 * m.M44 + m.M11 * m.M22 * m.M33 * m.M44;
+ ///
+ /// Inverts the given .
+ ///
+ /// The .
+ /// The inverted of the given .
+ public static Matrix4x4 Invert(Matrix4x4 m)
+ {
+ float m1 = m.M11, m2 = m.M12, m3 = m.M13, m4 = m.M14;
+ float m5 = m.M21, m6 = m.M22, m7 = m.M23, m8 = m.M24;
+ float m9 = m.M31, m10 = m.M32, m11 = m.M33, m12 = m.M34;
+ float m13 = m.M41, m14 = m.M42, m15 = m.M43, m16 = m.M44;
+
+ float num = m11 * m16 - m12 * m15;
+ float num2 = m10 * m16 - m12 * m14;
+ float num3 = m10 * m15 - m11 * m14;
+ float num4 = m9 * m16 - m12 * m13;
+ float num5 = m9 * m15 - m11 * m13;
+ float num6 = m9 * m14 - m10 * m13;
+
+ float num7 = m6 * num - m7 * num2 + m8 * num3;
+ float num8 = -(m5 * num - m7 * num4 + m8 * num5);
+ float num9 = m5 * num2 - m6 * num4 + m8 * num6;
+ float num10 = -(m5 * num3 - m6 * num5 + m7 * num6);
+
+ float invDet = 1f / (m1 * num7 + m2 * num8 + m3 * num9 + m4 * num10);
+
+ float r11 = num7 * invDet;
+ float r21 = num8 * invDet;
+ float r31 = num9 * invDet;
+ float r41 = num10 * invDet;
+
+ float r12 = (-(m2 * num - m3 * num2 + m4 * num3)) * invDet;
+ float r22 = (m1 * num - m3 * num4 + m4 * num5) * invDet;
+ float r32 = (-(m1 * num2 - m2 * num4 + m4 * num6)) * invDet;
+ float r42 = (m1 * num3 - m2 * num5 + m3 * num6) * invDet;
+
+ float num12 = m7 * m16 - m8 * m15;
+ float num13 = m6 * m16 - m8 * m14;
+ float num14 = m6 * m15 - m7 * m14;
+ float num15 = m5 * m16 - m8 * m13;
+ float num16 = m5 * m15 - m7 * m13;
+ float num17 = m5 * m14 - m6 * m13;
+
+ float r13 = (m2 * num12 - m3 * num13 + m4 * num14) * invDet;
+ float r23 = (-(m1 * num12 - m3 * num15 + m4 * num16)) * invDet;
+ float r33 = (m1 * num13 - m2 * num15 + m4 * num17) * invDet;
+ float r43 = (-(m1 * num14 - m2 * num16 + m3 * num17)) * invDet;
+
+ float num18 = m7 * m12 - m8 * m11;
+ float num19 = m6 * m12 - m8 * m10;
+ float num20 = m6 * m11 - m7 * m10;
+ float num21 = m5 * m12 - m8 * m9;
+ float num22 = m5 * m11 - m7 * m9;
+ float num23 = m5 * m10 - m6 * m9;
+
+ float r14 = (-(m2 * num18 - m3 * num19 + m4 * num20)) * invDet;
+ float r24 = (m1 * num18 - m3 * num21 + m4 * num22) * invDet;
+ float r34 = (-(m1 * num19 - m2 * num21 + m4 * num23)) * invDet;
+ float r44 = (m1 * num20 - m2 * num22 + m3 * num23) * invDet;
+
+ return new(
+ r11, r12, r13, r14,
+ r21, r22, r23, r24,
+ r31, r32, r33, r34,
+ r41, r42, r43, r44
+ );
+ }
+
+ ///
+ /// Transposes the given .
+ ///
+ /// The .
+ /// The transposed of the given .
+ public static Matrix4x4 Transpose(Matrix4x4 m) => new(
+ m.M11, m.M21, m.M31, m.M41,
+ m.M12, m.M22, m.M32, m.M42,
+ m.M13, m.M23, m.M33, m.M43,
+ m.M14, m.M24, m.M34, m.M44
+ );
+
public static Matrix4x4 CreateTranslation(Vector3D position) => new(
- 1f, 0f, 0f, 0f,
- 0f, 1f, 0f, 0f,
- 0f, 0f, 1f, 0f,
- position.X, position.Y, position.Z, 1
+ 1f, 0f, 0f, position.X,
+ 0f, 1f, 0f, position.Y,
+ 0f, 0f, 1f, position.Z,
+ 0f, 0f, 0f, 1f
+ );
+
+ public static Matrix4x4 CreateScale(float scale) => new(
+ scale, 0f, 0f, 0f,
+ 0f, scale, 0f, 0f,
+ 0f, 0f, scale, 0f,
+ 0f, 0f, 0f, 1f
);
public static Matrix4x4 CreateScale(Vector3D scale) => new(
@@ -148,11 +251,11 @@ public readonly struct Matrix4x4(
float c = Math.Cos(radians);
float s = Math.Sin(radians);
- return new Matrix4x4(
- 1f, 0f, 0f, 0f,
- 0f, c, s, 0f,
- 0f, -s, c, 0f,
- 0f, 0f, 0f, 1f
+ return new(
+ 1f, 0f, 0f, 0f,
+ 0f, c, -s, 0f,
+ 0f, s, c, 0f,
+ 0f, 0f, 0f, 1f
);
}
@@ -161,10 +264,10 @@ public readonly struct Matrix4x4(
float c = Math.Cos(radians);
float s = Math.Sin(radians);
- return new Matrix4x4(
- c, 0f, -s, 0f,
+ return new(
+ c, 0f, s, 0f,
0f, 1f, 0f, 0f,
- s, 0f, c, 0f,
+ -s, 0f, c, 0f,
0f, 0f, 0f, 1f
);
}
@@ -174,9 +277,9 @@ public readonly struct Matrix4x4(
float c = Math.Cos(radians);
float s = Math.Sin(radians);
- return new Matrix4x4(
- c, s, 0f, 0f,
- -s, c, 0f, 0f,
+ return new(
+ c, -s, 0f, 0f,
+ s, c, 0f, 0f,
0f, 0f, 1f, 0f,
0f, 0f, 0f, 1f
);
@@ -195,7 +298,7 @@ public readonly struct Matrix4x4(
Vector3D x = up.Cross(z).Normalized;
Vector3D y = z.Cross(x);
- return new Matrix4x4(
+ return new(
x.X, y.X, z.X, 0f,
x.Y, y.Y, z.Y, 0f,
x.Z, y.Z, z.Z, 0f,
@@ -205,28 +308,53 @@ public readonly struct Matrix4x4(
public static Matrix4x4 CreateLookMatrix(Vector3D position, Vector3D target, Vector3D up)
{
- Vector3D z = position.FromTo(target).Normalized;
- Vector3D x = up.Cross(z).Normalized;
- Vector3D y = z.Cross(x);
+ Vector3D f = (target - position).Normalized;
+ Vector3D s = f.Cross(up).Normalized;
+ Vector3D u = s.Cross(f);
- return new Matrix4x4(
- x.X, y.X, z.X, 0f,
- x.Y, y.Y, z.Y, 0f,
- x.Z, y.Z, z.Z, 0f,
- -x.Dot(position), -y.Dot(position), -z.Dot(position), 1f
+ return new(
+ s.X, u.X, -f.X, 0f,
+ s.Y, u.Y, -f.Y, 0f,
+ s.Z, u.Z, -f.Z, 0f,
+ -s.Dot(position), -u.Dot(position), f.Dot(position), 1f
);
}
- public static Matrix4x4 CreatePerspectiveFieldOfView(float fieldOfViewInRadians, float aspectRatio, float nearPlane, float farPlane)
+ public static Matrix4x4 CreateOrthographicView(float width, float height, float nearPlane = -1f, float farPlane = 1f)
{
- float yScale = 1f / Math.Tan(fieldOfViewInRadians / 2f);
- float xScale = yScale / aspectRatio;
+ float invDepth = 1f / (farPlane - nearPlane);
- return new Matrix4x4(
+ return new(
+ 2f / width, 0f, 0f, 0f,
+ 0f, -2f / height, 0f, 0f,
+ 0f, 0f, 1f * invDepth, 0f,
+ -1f, 1f, -nearPlane * invDepth, 1f
+ );
+ }
+
+ public static Matrix4x4 CreateOrthographicViewCentered(float width, float height, float nearPlane = -1f, float farPlane = 1f)
+ {
+ float invDepth = 1f / (farPlane - nearPlane);
+
+ return new(
+ 2f / width, 0f, 0f, 0f,
+ 0f, 2f / height, 0f, 0f,
+ 0f, 0f, 1f * invDepth, 0f,
+ 0f, 0f, -nearPlane * invDepth, 1f
+ );
+ }
+
+ public static Matrix4x4 CreatePerspectiveFieldOfView(float fovRadians, float aspectRatio, float nearPlane, float farPlane)
+ {
+ float yScale = 1f / Math.Tan(fovRadians / 2f);
+ float xScale = yScale / aspectRatio;
+ float zRange = farPlane - nearPlane;
+
+ return new(
xScale, 0f, 0f, 0f,
0f, yScale, 0f, 0f,
- 0f, 0f, farPlane / (farPlane - nearPlane), 1f,
- 0f, 0f, -nearPlane * farPlane / (farPlane - nearPlane), 0f
+ 0f, 0f, -(farPlane + nearPlane) / zRange, -1f,
+ 0f, 0f, -(2f * nearPlane * farPlane) / zRange, 0f
);
}
@@ -261,11 +389,20 @@ public static class Matrix4x4Extensions
///
public static float Determinant(this Matrix4x4 matrix) => Matrix4x4.Determinant(matrix);
+ ///
+ public static Matrix4x4 Invert(this Matrix4x4 matrix) => Matrix4x4.Invert(matrix);
+
+ ///
+ public static Matrix4x4 Transpose(this Matrix4x4 matrix) => Matrix4x4.Transpose(matrix);
+
///
public static Matrix4x4 ApplyTranslation(this Matrix4x4 matrix, Vector3D translation) => matrix * Matrix4x4.CreateTranslation(translation);
///
- public static Matrix4x4 ApplyScale(this Matrix4x4 matrix, Vector3 scale) => matrix * Matrix4x4.CreateScale(scale);
+ public static Matrix4x4 ApplyScale(this Matrix4x4 matrix, Vector3D scale) => matrix * Matrix4x4.CreateScale(scale);
+
+ ///
+ public static Matrix4x4 ApplyScale(this Matrix4x4 matrix, float scale) => matrix * Matrix4x4.CreateScale(scale);
///
public static Matrix4x4 ApplyRotationX(this Matrix4x4 matrix, float radians) => matrix * Matrix4x4.CreateRotationX(radians);
@@ -280,15 +417,28 @@ public static class Matrix4x4Extensions
public static Matrix4x4 ApplyRotation(this Matrix4x4 matrix, Quaternion quaternion) => matrix * Matrix4x4.CreateRotation(quaternion);
///
- public static Matrix4x4 ApplyLookRotationTo(this Matrix4x4 matrix, Vector3D forward, Vector3 up) => matrix * Matrix4x4.CreateLookMatrix(forward, up);
+ public static Matrix4x4 ApplyLookRotationTo(this Matrix4x4 matrix, Vector3D forward, Vector3D up) => matrix * Matrix4x4.CreateLookMatrix(forward, up);
///
- public static Matrix4x4 CreateLookMatrixTo(this Vector3D from, Vector3D to, Vector3 up) => Matrix4x4.CreateLookMatrix(from, to, up);
+ public static Matrix4x4 CreateLookMatrixTo(this Vector3D from, Vector3D to, Vector3D up) => Matrix4x4.CreateLookMatrix(from, to, up);
///
public static Matrix4x4 ApplyPerspectiveFieldOfView(this Matrix4x4 matrix, float fieldOfViewInRadians, float aspectRatio, float nearPlane, float farPlane)
=> matrix * Matrix4x4.CreatePerspectiveFieldOfView(fieldOfViewInRadians, aspectRatio, nearPlane, farPlane);
+ ///
+ public static Matrix4x4 ApplyOrthographicView(this Matrix4x4 matrix, float width, float height, float nearPlane = -1f, float farPlane = 1f)
+ => matrix * Matrix4x4.CreateOrthographicView(width, height, nearPlane, farPlane);
+
+ ///
+ public static Matrix4x4 ApplyOrthographicViewCentered(this Matrix4x4 matrix, float width, float height, float nearPlane = -1f, float farPlane = 1f)
+ => matrix * Matrix4x4.CreateOrthographicViewCentered(width, height, nearPlane, farPlane);
+
/// 's.
+ ///
+ public static Matrix4x4 ApplyMatrix(this Matrix4x4 left, Matrix4x4 right) => left * right;
}
diff --git a/Engine.Core/Primitives/Vector2D.cs b/Engine.Core/Primitives/Vector2D.cs
index 6009875..fbe4291 100644
--- a/Engine.Core/Primitives/Vector2D.cs
+++ b/Engine.Core/Primitives/Vector2D.cs
@@ -82,11 +82,19 @@ public readonly struct Vector2D(float x, float y) : IEquatable
public static bool operator ==(Vector2D left, Vector2D right) => left.X == right.X && left.Y == right.Y;
public static bool operator !=(Vector2D left, Vector2D right) => left.X != right.X || left.Y != right.Y;
- public static implicit operator System.Numerics.Vector2(Vector2D vector) => new(vector.X, vector.Y);
- public static implicit operator Vector2D(System.Numerics.Vector2 vector) => new(vector.X, vector.Y);
public static implicit operator Vector2D(Vector2DInt vector) => new(vector.X, vector.Y);
- public static implicit operator Vector2D(Vector3D vector) => new(vector.X, vector.Y);
+ public static implicit operator Vector2D(Vector3DInt vector) => new(vector.X, vector.Y);
+
+ public static implicit operator System.Numerics.Vector2(Vector2D vector) => new(vector.X, vector.Y);
+ public static implicit operator System.Numerics.Vector3(Vector2D vector) => new(vector.X, vector.Y, 0f);
+ public static implicit operator System.Numerics.Vector4(Vector2D vector) => new(vector.X, vector.Y, 0f, 0f);
+
+ public static implicit operator Vector2D(System.Numerics.Vector2 vector) => new(vector.X, vector.Y);
public static implicit operator Vector2D(System.Numerics.Vector3 vector) => new(vector.X, vector.Y);
+ public static implicit operator Vector2D(System.Numerics.Vector4 vector) => new(vector.X, vector.Y);
+
+ public static implicit operator Vector2D(Vector3D vector) => new(vector.X, vector.Y);
+ public static implicit operator Vector2D(Vector4D vector) => new(vector.X, vector.Y);
///
/// Calculates the length of the .
diff --git a/Engine.Core/Primitives/Vector2DInt.cs b/Engine.Core/Primitives/Vector2DInt.cs
index 31b9f37..ba9a58e 100644
--- a/Engine.Core/Primitives/Vector2DInt.cs
+++ b/Engine.Core/Primitives/Vector2DInt.cs
@@ -74,9 +74,12 @@ public readonly struct Vector2DInt(int x, int y) : IEquatable
public static bool operator ==(Vector2DInt left, Vector2DInt right) => left.X == right.X && left.Y == right.Y;
public static bool operator !=(Vector2DInt left, Vector2DInt right) => left.X != right.X || left.Y != right.Y;
- public static implicit operator Vector2DInt(Vector2D vector) => new(vector.X.RoundToInt(), vector.Y.RoundToInt());
public static implicit operator Vector2DInt(Vector3DInt vector) => new(vector.X, vector.Y);
+ public static implicit operator Vector2DInt(Vector2D vector) => new(vector.X.RoundToInt(), vector.Y.RoundToInt());
+ public static implicit operator Vector2DInt(Vector3D vector) => new(vector.X.RoundToInt(), vector.Y.RoundToInt());
+ public static implicit operator Vector2DInt(Vector4D vector) => new(vector.X.RoundToInt(), vector.Y.RoundToInt());
+
///
/// Calculates the length of the .
///
diff --git a/Engine.Core/Primitives/Vector3D.cs b/Engine.Core/Primitives/Vector3D.cs
index 3551f35..8b0598b 100644
--- a/Engine.Core/Primitives/Vector3D.cs
+++ b/Engine.Core/Primitives/Vector3D.cs
@@ -92,11 +92,19 @@ public readonly struct Vector3D(float x, float y, float z) : IEquatable left.X == right.X && left.Y == right.Y && left.Z == right.Z;
public static bool operator !=(Vector3D left, Vector3D right) => left.X != right.X || left.Y != right.Y || left.Z != right.Z;
- public static implicit operator System.Numerics.Vector3(Vector3D vector) => new(vector.X, vector.Y, vector.Z);
- public static implicit operator Vector3D(System.Numerics.Vector3 vector) => new(vector.X, vector.Y, vector.Z);
+ public static implicit operator Vector3D(Vector2DInt vector) => new(vector.X, vector.Y, 0f);
public static implicit operator Vector3D(Vector3DInt vector) => new(vector.X, vector.Y, vector.Z);
- public static implicit operator Vector3D(Vector2D vector) => new(vector.X, vector.Y, 0f);
+
+ public static implicit operator System.Numerics.Vector2(Vector3D vector) => new(vector.X, vector.Y);
+ public static implicit operator System.Numerics.Vector3(Vector3D vector) => new(vector.X, vector.Y, vector.Z);
+ public static implicit operator System.Numerics.Vector4(Vector3D vector) => new(vector.X, vector.Y, vector.Z, 0f);
+
public static implicit operator Vector3D(System.Numerics.Vector2 vector) => new(vector.X, vector.Y, 0f);
+ public static implicit operator Vector3D(System.Numerics.Vector3 vector) => new(vector.X, vector.Y, vector.Z);
+ public static implicit operator Vector3D(System.Numerics.Vector4 vector) => new(vector.X, vector.Y, vector.Z);
+
+ public static implicit operator Vector3D(Vector2D vector) => new(vector.X, vector.Y, 0f);
+ public static implicit operator Vector3D(Vector4D vector) => new(vector.X, vector.Y, vector.Z);
///
/// Calculates the length of the .
diff --git a/Engine.Core/Primitives/Vector3DInt.cs b/Engine.Core/Primitives/Vector3DInt.cs
index 817351f..e35c195 100644
--- a/Engine.Core/Primitives/Vector3DInt.cs
+++ b/Engine.Core/Primitives/Vector3DInt.cs
@@ -84,9 +84,12 @@ public readonly struct Vector3DInt(int x, int y, int z) : IEquatable left.X == right.X && left.Y == right.Y && left.Z == right.Z;
public static bool operator !=(Vector3DInt left, Vector3DInt right) => left.X != right.X || left.Y != right.Y || left.Z != right.Z;
- public static implicit operator Vector3DInt(Vector3D vector) => new(vector.X.RoundToInt(), vector.Y.RoundToInt(), vector.Z.RoundToInt());
public static implicit operator Vector3DInt(Vector2DInt vector) => new(vector.X, vector.Y, 0);
+ public static implicit operator Vector3DInt(Vector2D vector) => new(vector.X.RoundToInt(), vector.Y.RoundToInt(), 0);
+ public static implicit operator Vector3DInt(Vector3D vector) => new(vector.X.RoundToInt(), vector.Y.RoundToInt(), vector.Z.RoundToInt());
+ public static implicit operator Vector3DInt(Vector4D vector) => new(vector.X.RoundToInt(), vector.Y.RoundToInt(), vector.Z.RoundToInt());
+
///
/// Calculates the length of the .
///
diff --git a/Engine.Core/Primitives/Vector4D.cs b/Engine.Core/Primitives/Vector4D.cs
index 4e38feb..b613933 100644
--- a/Engine.Core/Primitives/Vector4D.cs
+++ b/Engine.Core/Primitives/Vector4D.cs
@@ -90,9 +90,20 @@ public readonly struct Vector4D(float x, float y, float z, float w) : IEquatable
public static bool operator ==(Vector4D left, Vector4D right) => left.X == right.X && left.Y == right.Y && left.Z == right.Z && left.W == right.W;
public static bool operator !=(Vector4D left, Vector4D right) => left.X != right.X || left.Y != right.Y || left.Z != right.Z || left.W != right.W;
+ public static implicit operator Vector4D(Vector2DInt vector) => new(vector.X, vector.Y, 0f, 0f);
+ public static implicit operator Vector4D(Vector3DInt vector) => new(vector.X, vector.Y, vector.Z, 0f);
+
+ public static implicit operator System.Numerics.Vector2(Vector4D vector) => new(vector.X, vector.Y);
+ public static implicit operator System.Numerics.Vector3(Vector4D vector) => new(vector.X, vector.Y, vector.Z);
public static implicit operator System.Numerics.Vector4(Vector4D vector) => new(vector.X, vector.Y, vector.Z, vector.W);
+
+ public static implicit operator Vector4D(System.Numerics.Vector2 vector) => new(vector.X, vector.Y, 0f, 0f);
+ public static implicit operator Vector4D(System.Numerics.Vector3 vector) => new(vector.X, vector.Y, vector.Z, 0f);
public static implicit operator Vector4D(System.Numerics.Vector4 vector) => new(vector.X, vector.Y, vector.Z, vector.W);
+ public static implicit operator Vector4D(Vector2D vector) => new(vector.X, vector.Y, 0f, 0f);
+ public static implicit operator Vector4D(Vector3D vector) => new(vector.X, vector.Y, vector.Z, 0f);
+
///
/// Calculates the length of the .
///
diff --git a/Engine.Core/Serialization/ISerializer.cs b/Engine.Core/Serialization/ISerializer.cs
index 0ada93c..783f2f7 100644
--- a/Engine.Core/Serialization/ISerializer.cs
+++ b/Engine.Core/Serialization/ISerializer.cs
@@ -4,15 +4,15 @@ namespace Engine.Core.Serialization;
public interface ISerializer
{
- object Deserialize(string configuration);
- object Deserialize(string configuration, Type type);
- T Deserialize(string configuration);
+ object Deserialize(string content);
+ object Deserialize(string content, Type type);
+ T Deserialize(string content);
string Serialize(object instance);
- ProgressiveTask