diff --git a/Engine.Core/Abstract/IBehaviourController.cs b/Engine.Core/Abstract/IBehaviourController.cs
index 87af209..b1500da 100644
--- a/Engine.Core/Abstract/IBehaviourController.cs
+++ b/Engine.Core/Abstract/IBehaviourController.cs
@@ -62,7 +62,8 @@ public interface IBehaviourController : IEntity, IHasUniverseObject
///
/// The type of s to get.
/// The list to store the s.
- void GetBehaviours(IList results);
+ /// Whether to clear the before collection or append the results to the list.
+ void GetBehaviours(IList results, CollectionMethod collectionMethod = CollectionMethod.Clear);
///
/// Removes s of the specified type from the .
@@ -78,6 +79,8 @@ public interface IBehaviourController : IEntity, IHasUniverseObject
/// The to remove.
void RemoveBehaviour(T behaviour) where T : class, IBehaviour;
+ enum CollectionMethod { Clear, Append };
+
readonly record struct BehaviourAddedArguments(IBehaviour BehaviourAdded);
readonly record struct BehaviourRemovedArguments(IBehaviour BehaviourRemoved);
}
diff --git a/Engine.Core/Abstract/ICamera3D.cs b/Engine.Core/Abstract/ICamera3D.cs
index 01ce185..a0024a0 100644
--- a/Engine.Core/Abstract/ICamera3D.cs
+++ b/Engine.Core/Abstract/ICamera3D.cs
@@ -6,7 +6,32 @@ namespace Engine.Core;
public interface ICamera3D : IBehaviour3D
{
///
- /// Field of View (FOV) value of the camera
+ /// Event triggered when the near plane of the changes.
+ ///
+ Event OnNearPlaneChanged { get; }
+
+ ///
+ /// Event triggered when the far plane of the changes.
+ ///
+ Event OnFarPlaneChanged { get; }
+
+ ///
+ /// Event triggered when the field of view of the changes.
+ ///
+ Event OnFieldOfViewChanged { get; }
+
+ ///
+ /// Near plane distance of the camera.
+ ///
+ float NearPlane { get; set; }
+
+ ///
+ /// Far plane distance of the camera.
+ ///
+ float FarPlane { get; set; }
+
+ ///
+ /// Field of View (FOV) value of the camera in degrees.
///
float FieldOfView { get; set; }
@@ -23,4 +48,8 @@ public interface ICamera3D : IBehaviour3D
/// The position in world coordinates.
/// The position in screen coordinates.
Vector2D WorldToScreenPosition(Vector3D worldPosition);
+
+ readonly record struct NearPlaneChangedArguments(float PreviousNearPlane);
+ readonly record struct FarPlaneChangedArguments(float PreviousFarPlane);
+ readonly record struct FieldOfViewChangedArguments(float PreviousFieldOfView);
}
diff --git a/Engine.Core/Abstract/IEntity.cs b/Engine.Core/Abstract/IEntity.cs
index 53a719a..0903fa4 100644
--- a/Engine.Core/Abstract/IEntity.cs
+++ b/Engine.Core/Abstract/IEntity.cs
@@ -3,18 +3,4 @@ namespace Engine.Core;
///
/// Represents a basic entity in the engine.
///
-public interface IEntity : IInitializable, IHasStateEnable
-{
- ///
- /// Event triggered when the of the changes.
- /// The string action parameter is the previous of the .
- ///
- Event OnIdChanged { get; }
-
- ///
- /// The ID of the .
- ///
- string Id { get; set; }
-
- readonly record struct IdChangedArguments(string PreviousId);
-}
+public interface IEntity : IInitializable, IIdentifiable, IHasStateEnable;
diff --git a/Engine.Core/Abstract/IHasId.cs b/Engine.Core/Abstract/IHasId.cs
new file mode 100644
index 0000000..c4cd50a
--- /dev/null
+++ b/Engine.Core/Abstract/IHasId.cs
@@ -0,0 +1,20 @@
+namespace Engine.Core;
+
+///
+/// Represents any instance in the engine with an id.
+///
+public interface IIdentifiable
+{
+ ///
+ /// Event triggered when the of the changes.
+ /// The string action parameter is the previous of the .
+ ///
+ Event OnIdChanged { get; }
+
+ ///
+ /// The ID of the .
+ ///
+ string Id { get; set; }
+
+ readonly record struct IdChangedArguments(string PreviousId);
+}
diff --git a/Engine.Core/Abstract/IUniverse.cs b/Engine.Core/Abstract/IUniverse.cs
index e0896a1..9b95611 100644
--- a/Engine.Core/Abstract/IUniverse.cs
+++ b/Engine.Core/Abstract/IUniverse.cs
@@ -78,9 +78,9 @@ public interface IUniverse : IEntity, IEnumerable
UniverseTime UnscaledTime { get; }
///
- /// Gets a read-only list of s managed by the .
+ /// Gets the root of the .
///
- IReadOnlyList UniverseObjects { get; }
+ IUniverseObject Root { get; }
///
/// Registers an to the .
diff --git a/Engine.Core/BaseEntity.cs b/Engine.Core/BaseEntity.cs
index a16b072..f848e3d 100644
--- a/Engine.Core/BaseEntity.cs
+++ b/Engine.Core/BaseEntity.cs
@@ -4,7 +4,7 @@ namespace Engine.Core;
public abstract class BaseEntity : IEntity
{
- public Event OnIdChanged { get; } = new();
+ public Event OnIdChanged { get; } = new();
public Event OnInitialized { get; } = new();
public Event OnFinalized { get; } = new();
public Event OnStateEnableAssigned { get; } = new();
diff --git a/Engine.Core/BehaviourController.cs b/Engine.Core/BehaviourController.cs
index 00618a8..0adff40 100644
--- a/Engine.Core/BehaviourController.cs
+++ b/Engine.Core/BehaviourController.cs
@@ -58,9 +58,11 @@ public class BehaviourController : BaseEntity, IBehaviourController
return behaviours;
}
- public void GetBehaviours(IList results)
+ public void GetBehaviours(IList results, IBehaviourController.CollectionMethod collectionMethod = IBehaviourController.CollectionMethod.Clear)
{
- results.Clear();
+ if (collectionMethod == IBehaviourController.CollectionMethod.Clear)
+ results.Clear();
+
foreach (IBehaviour behaviourItem in behaviours)
{
if (behaviourItem is not T behaviour)
diff --git a/Engine.Core/Collectors/ActiveBehaviourCollectorBase.cs b/Engine.Core/Collectors/ActiveBehaviourCollectorBase.cs
index f389a61..fe97bcd 100644
--- a/Engine.Core/Collectors/ActiveBehaviourCollectorBase.cs
+++ b/Engine.Core/Collectors/ActiveBehaviourCollectorBase.cs
@@ -29,7 +29,7 @@ public abstract class ActiveBehaviourCollectorBase : IBehaviourCollector w
if (Universe is not null)
return false;
- foreach (IUniverseObject universeObject in universe.UniverseObjects)
+ foreach (IUniverseObject universeObject in universe)
OnUniverseObjectRegistered(universe, new(universeObject));
universe.OnUniverseObjectRegistered.AddListener(delegateOnUniverseObjectRegistered);
@@ -46,7 +46,7 @@ public abstract class ActiveBehaviourCollectorBase : IBehaviourCollector w
if (Universe is null)
return false;
- foreach (IUniverseObject universeObject in Universe.UniverseObjects)
+ foreach (IUniverseObject universeObject in Universe)
OnUniverseObjectUnregistered(Universe, new(universeObject));
Universe.OnUniverseObjectRegistered.RemoveListener(delegateOnUniverseObjectRegistered);
diff --git a/Engine.Core/Collectors/ActiveBehaviourCollectorOrdered.cs b/Engine.Core/Collectors/ActiveBehaviourCollectorOrdered.cs
index 502e45c..927d009 100644
--- a/Engine.Core/Collectors/ActiveBehaviourCollectorOrdered.cs
+++ b/Engine.Core/Collectors/ActiveBehaviourCollectorOrdered.cs
@@ -5,9 +5,8 @@ namespace Engine.Core;
public class ActiveBehaviourCollectorOrdered : ActiveBehaviourCollector where TItem : class, IBehaviour where TIndex : IComparable
{
- private readonly Event.EventHandler delegateOnPriorityChanged = null!;
-
private readonly SortedDictionary> behaviours = null!;
+ private readonly Dictionary indexCache = [];
private readonly Func getIndexFunc = null!;
private readonly IComparer sortBy = null!;
@@ -39,11 +38,10 @@ public class ActiveBehaviourCollectorOrdered : ActiveBehaviourCol
protected override bool RemoveBehaviour(TItem tBehaviour)
{
- TIndex index = getIndexFunc(tBehaviour);
- if (!behaviours.TryGetValue(index, out FastList? list))
+ if (!indexCache.TryGetValue(tBehaviour, out TIndex? index) || !behaviours.TryGetValue(index, out FastList? list))
throw new Exceptions.NotFoundException($"Index of '{index}' is not found in the collector");
- if (!list.Remove(tBehaviour))
+ if (!list.Remove(tBehaviour) || !indexCache.Remove(tBehaviour))
return false;
count--;
@@ -58,21 +56,11 @@ public class ActiveBehaviourCollectorOrdered : ActiveBehaviourCol
count++;
list.Add(behaviour);
- }
-
- protected override void OnBehaviourAdd(IBehaviour behaviour) => behaviour.OnPriorityChanged.AddListener(delegateOnPriorityChanged);
- protected override void OnBehaviourRemove(IBehaviour behaviour) => behaviour.OnPriorityChanged.RemoveListener(delegateOnPriorityChanged);
-
- private void OnPriorityChanged(IBehaviour sender, IBehaviour.PriorityChangedArguments args)
- {
- TItem behaviour = (TItem)sender;
- RemoveBehaviour(behaviour);
- AddBehaviour(behaviour);
+ indexCache.Add(behaviour, key);
}
public ActiveBehaviourCollectorOrdered(Func getIndexFunc, Comparison sortBy)
{
- delegateOnPriorityChanged = OnPriorityChanged;
this.getIndexFunc = getIndexFunc;
this.sortBy = Comparer.Create(sortBy);
behaviours = new(this.sortBy);
@@ -80,7 +68,6 @@ public class ActiveBehaviourCollectorOrdered : ActiveBehaviourCol
public ActiveBehaviourCollectorOrdered(IUniverse universe, Func getIndexFunc, Comparison sortBy) : base(universe)
{
- delegateOnPriorityChanged = OnPriorityChanged;
this.getIndexFunc = getIndexFunc;
this.sortBy = Comparer.Create(sortBy);
behaviours = new(this.sortBy);
@@ -89,14 +76,12 @@ public class ActiveBehaviourCollectorOrdered : ActiveBehaviourCol
public ActiveBehaviourCollectorOrdered(Func getIndexFunc, IComparer sortBy)
{
this.getIndexFunc = getIndexFunc;
- delegateOnPriorityChanged = OnPriorityChanged;
this.sortBy = sortBy;
behaviours = new(sortBy);
}
public ActiveBehaviourCollectorOrdered(IUniverse universe, Func getIndexFunc, IComparer sortBy) : base(universe)
{
- delegateOnPriorityChanged = OnPriorityChanged;
this.getIndexFunc = getIndexFunc;
this.sortBy = sortBy;
behaviours = new(sortBy);
diff --git a/Engine.Core/Collectors/BehaviourCollectorBase.cs b/Engine.Core/Collectors/BehaviourCollectorBase.cs
index e531466..6e7ca0e 100644
--- a/Engine.Core/Collectors/BehaviourCollectorBase.cs
+++ b/Engine.Core/Collectors/BehaviourCollectorBase.cs
@@ -25,7 +25,7 @@ public abstract class BehaviourCollectorBase : IBehaviourCollector where T
if (Universe is not null)
return false;
- foreach (IUniverseObject universeObject in universe.UniverseObjects)
+ foreach (IUniverseObject universeObject in universe)
OnUniverseObjectRegistered(universe, new(universeObject));
universe.OnUniverseObjectRegistered.AddListener(delegateOnUniverseObjectRegistered);
@@ -43,7 +43,7 @@ public abstract class BehaviourCollectorBase : IBehaviourCollector where T
if (Universe is null)
return false;
- foreach (IUniverseObject universeObject in Universe.UniverseObjects)
+ foreach (IUniverseObject universeObject in Universe)
OnUniverseObjectUnregistered(Universe, new(universeObject));
Universe.OnUniverseObjectRegistered.RemoveListener(delegateOnUniverseObjectRegistered);
diff --git a/Engine.Core/Collectors/BehaviourCollectorOrdered.cs b/Engine.Core/Collectors/BehaviourCollectorOrdered.cs
index 6f1077a..4fe27ff 100644
--- a/Engine.Core/Collectors/BehaviourCollectorOrdered.cs
+++ b/Engine.Core/Collectors/BehaviourCollectorOrdered.cs
@@ -5,9 +5,8 @@ namespace Engine.Core;
public class BehaviourCollectorOrdered : BehaviourCollectorBase where TItem : class where TIndex : IComparable
{
- private readonly Event.EventHandler delegateOnPriorityChanged = null!;
-
private readonly SortedDictionary> behaviours = null!;
+ private readonly Dictionary indexCache = null!;
private readonly Func getIndexFunc = null!;
private readonly IComparer sortBy = null!;
@@ -39,11 +38,10 @@ public class BehaviourCollectorOrdered : BehaviourCollectorBase? list))
+ if (!indexCache.TryGetValue(tBehaviour, out TIndex? index) || !behaviours.TryGetValue(index, out FastList? list))
throw new Exceptions.NotFoundException($"Index of '{index}' is not found in the collector");
- if (!list.Remove(tBehaviour))
+ if (!list.Remove(tBehaviour) || !indexCache.Remove(tBehaviour))
return false;
count--;
@@ -58,21 +56,11 @@ public class BehaviourCollectorOrdered : BehaviourCollectorBase behaviour.OnPriorityChanged.AddListener(delegateOnPriorityChanged);
- protected override void OnBehaviourRemove(IBehaviour behaviour) => behaviour.OnPriorityChanged.RemoveListener(delegateOnPriorityChanged);
-
- private void OnPriorityChanged(IBehaviour sender, IBehaviour.PriorityChangedArguments args)
- {
- TItem behaviour = (TItem)sender;
- RemoveBehaviour(behaviour);
- AddBehaviour(behaviour);
+ indexCache.Add(behaviour, key);
}
public BehaviourCollectorOrdered(Func getIndexFunc, Comparison sortBy)
{
- delegateOnPriorityChanged = OnPriorityChanged;
this.getIndexFunc = getIndexFunc;
this.sortBy = Comparer.Create(sortBy);
behaviours = new(this.sortBy);
@@ -80,7 +68,6 @@ public class BehaviourCollectorOrdered : BehaviourCollectorBase getIndexFunc, Comparison sortBy) : base(universe)
{
- delegateOnPriorityChanged = OnPriorityChanged;
this.getIndexFunc = getIndexFunc;
this.sortBy = Comparer.Create(sortBy);
behaviours = new(this.sortBy);
@@ -89,14 +76,12 @@ public class BehaviourCollectorOrdered : BehaviourCollectorBase getIndexFunc, IComparer sortBy)
{
this.getIndexFunc = getIndexFunc;
- delegateOnPriorityChanged = OnPriorityChanged;
this.sortBy = sortBy;
behaviours = new(sortBy);
}
public BehaviourCollectorOrdered(IUniverse universe, Func getIndexFunc, IComparer sortBy) : base(universe)
{
- delegateOnPriorityChanged = OnPriorityChanged;
this.getIndexFunc = getIndexFunc;
this.sortBy = sortBy;
behaviours = new(sortBy);
diff --git a/Engine.Core/Extensions/BehaviourControllerExtensions.cs b/Engine.Core/Extensions/BehaviourControllerExtensions.cs
index bcf0773..9228992 100644
--- a/Engine.Core/Extensions/BehaviourControllerExtensions.cs
+++ b/Engine.Core/Extensions/BehaviourControllerExtensions.cs
@@ -100,18 +100,16 @@ public static class BehaviourControllerExtensions
///
/// The type of s to get.
/// The list to store the s.
- public static void GetBehavioursInParent(this IBehaviourController behaviourController, IList behavioursInParent) where T : class
+ /// Whether to clear the before collection or append the results to the list.
+ public static void GetBehavioursInParent(this IBehaviourController behaviourController, IList behavioursInParent, IBehaviourController.CollectionMethod collectionMethod = IBehaviourController.CollectionMethod.Clear) where T : class
{
- IBehaviourController? controller = behaviourController;
- List cache = [];
- behavioursInParent.Clear();
+ if (collectionMethod == IBehaviourController.CollectionMethod.Clear)
+ behavioursInParent.Clear();
+ IBehaviourController? controller = behaviourController;
while (controller is not null)
{
- controller.GetBehaviours(cache);
- foreach (T behaviour in cache)
- behavioursInParent.Add(behaviour);
-
+ controller.GetBehaviours(behavioursInParent, IBehaviourController.CollectionMethod.Append);
controller = controller.UniverseObject.Parent?.BehaviourController;
}
}
@@ -161,22 +159,20 @@ public static class BehaviourControllerExtensions
///
/// The type of s to get.
/// The list to store the s.
- public static void GetBehavioursInChildren(this IBehaviourController behaviourController, IList behavioursInChildren) where T : class
+ /// Whether to clear the before collection or append the results to the list.
+ public static void GetBehavioursInChildren(this IBehaviourController behaviourController, IList behavioursInChildren, IBehaviourController.CollectionMethod collectionMethod = IBehaviourController.CollectionMethod.Clear) where T : class
{
- List cache = [];
- behavioursInChildren.Clear();
+ if (collectionMethod == IBehaviourController.CollectionMethod.Clear)
+ behavioursInChildren.Clear();
- TraverseChildrenForBehaviour(behaviourController.UniverseObject, behavioursInChildren, cache);
+ TraverseChildrenForBehaviour(behaviourController.UniverseObject, behavioursInChildren);
}
- private static void TraverseChildrenForBehaviour(IUniverseObject universeObject, IList behaviours, IList cache) where T : class
+ private static void TraverseChildrenForBehaviour(IUniverseObject universeObject, IList behaviours) where T : class
{
- universeObject.BehaviourController.GetBehaviours(cache);
-
- foreach (T behaviour in cache)
- behaviours.Add(behaviour);
+ universeObject.BehaviourController.GetBehaviours(behaviours, IBehaviourController.CollectionMethod.Append);
foreach (IUniverseObject child in universeObject.Children)
- TraverseChildrenForBehaviour(child, behaviours, cache);
+ TraverseChildrenForBehaviour(child, behaviours);
}
}
diff --git a/Engine.Core/Extensions/UniverseExtensions.cs b/Engine.Core/Extensions/UniverseExtensions.cs
index e7379b0..d6a40e8 100644
--- a/Engine.Core/Extensions/UniverseExtensions.cs
+++ b/Engine.Core/Extensions/UniverseExtensions.cs
@@ -32,5 +32,5 @@ public static class UniverseExtensions
/// Type to be searched through the .
/// The specified type if found; otherwise, throws .
public static T FindRequired(this IUniverse universe) where T : class
- => universe.Find() ?? throw new NotFoundException($"{universe.GetType().FullName}({universe.Id}) does not contain any {nameof(IUniverseObject)} or {nameof(IBehaviour)} of type {typeof(T).FullName}");
+ => universe.Root.BehaviourController.GetBehaviourInChildren() ?? throw new NotFoundException($"{universe.GetType().FullName}({universe.Id}) does not contain any {nameof(IUniverseObject)} or {nameof(IBehaviour)} of type {typeof(T).FullName}");
}
diff --git a/Engine.Core/Extensions/UniverseObjectExtensions.cs b/Engine.Core/Extensions/UniverseObjectExtensions.cs
index 2f843cb..4b630bc 100644
--- a/Engine.Core/Extensions/UniverseObjectExtensions.cs
+++ b/Engine.Core/Extensions/UniverseObjectExtensions.cs
@@ -16,6 +16,21 @@ public static class UniverseObjectExtensions
return universeObject;
}
+ public static IEnumerator TraverseChildren(this IUniverseObject universeObject)
+ {
+ static IEnumerable Traverse(IUniverseObject obj)
+ {
+ foreach (IUniverseObject child in obj.Children)
+ {
+ yield return child;
+ foreach (IUniverseObject descendant in Traverse(child))
+ yield return descendant;
+ }
+ }
+
+ return Traverse(universeObject).GetEnumerator();
+ }
+
#region Universe Object Search
///
/// Gets a of the specified type.
diff --git a/Engine.Core/Math.cs b/Engine.Core/Math.cs
index b65cc5e..14ba680 100644
--- a/Engine.Core/Math.cs
+++ b/Engine.Core/Math.cs
@@ -110,6 +110,13 @@ public static class Math
/// The sine of .
public static float Sin(float x) => MathF.Sin(x);
+ ///
+ /// Returns the tangent of a number.
+ ///
+ /// The angle, in radians.
+ /// The tangent of .
+ public static float Tan(float x) => MathF.Tan(x);
+
///
/// Returns the arccosine of a number.
///
@@ -124,6 +131,13 @@ public static class Math
/// The arcsine of .
public static float Asin(float x) => MathF.Asin(x);
+ ///
+ /// Returns the angle whose tangent is the specified number.
+ ///
+ /// The tangent value.
+ /// The angle, in radians.
+ public static float Atan(float x) => MathF.Atan(x);
+
///
/// Returns the angle whose tangent is the quotient of two specified numbers.
///
diff --git a/Engine.Core/MathExtensions.cs b/Engine.Core/MathExtensions.cs
index d1fd4aa..ac67041 100644
--- a/Engine.Core/MathExtensions.cs
+++ b/Engine.Core/MathExtensions.cs
@@ -32,12 +32,18 @@ public static class MathExtensions
///
public static float Sin(this float x) => Math.Sin(x);
+ ///
+ public static float Tan(this float x) => Math.Tan(x);
+
///
public static float Acos(this float x) => Math.Acos(x);
///
public static float Asin(this float x) => Math.Asin(x);
+ ///
+ public static float Atan(this float x) => Math.Atan(x);
+
///
public static float Atan2(this float y, float x) => Math.Atan2(y, x);
diff --git a/Engine.Core/Primitives/Matrix4x4.cs b/Engine.Core/Primitives/Matrix4x4.cs
new file mode 100644
index 0000000..0288b41
--- /dev/null
+++ b/Engine.Core/Primitives/Matrix4x4.cs
@@ -0,0 +1,294 @@
+using System;
+using System.Numerics;
+
+namespace Engine.Core;
+
+// TODO Comments
+
+///
+/// Represents a 4D left handed space matrix.
+///
+///
+/// Initializes a new instance of the struct with the specified values.
+///
+[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")]
+public readonly struct Matrix4x4(
+ float m11, float m12, float m13, float m14,
+ float m21, float m22, float m23, float m24,
+ float m31, float m32, float m33, float m34,
+ float m41, float m42, float m43, float m44
+) : IEquatable
+{
+ public readonly float M11 = m11, M12 = m12, M13 = m13, M14 = m14;
+ public readonly float M21 = m21, M22 = m22, M23 = m23, M24 = m24;
+ public readonly float M31 = m31, M32 = m32, M33 = m33, M34 = m34;
+ public readonly float M41 = m41, M42 = m42, M43 = m43, M44 = m44;
+
+ ///
+ /// Extracts the position (translation) from the .
+ ///
+ public readonly Vector3D Position => new(M41, M42, M43);
+
+ ///
+ /// Extracts the scale from the .
+ ///
+ public readonly Vector3D Scale
+ {
+ get
+ {
+ float scaleX = new Vector3D(M11, M12, M13).Length();
+ float scaleY = new Vector3D(M21, M22, M23).Length();
+ float scaleZ = new Vector3D(M31, M32, M33).Length();
+
+ if (Determinant(this) < 0)
+ scaleX *= -1;
+
+ return new(scaleX, scaleY, scaleZ);
+ }
+ }
+
+ ///
+ /// Extracts the rotation from the .
+ ///
+ public readonly Quaternion Rotation => Quaternion.FromRotationMatrix4x4(this).Normalized;
+
+ ///
+ /// Represents the identity .
+ ///
+ public static Matrix4x4 Identity => new(
+ 1f, 0f, 0f, 0f,
+ 0f, 1f, 0f, 0f,
+ 0f, 0f, 1f, 0f,
+ 0f, 0f, 0f, 1f
+ );
+
+ 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,
+ a.M11 * b.M13 + a.M12 * b.M23 + a.M13 * b.M33 + a.M14 * b.M43,
+ a.M11 * b.M14 + a.M12 * b.M24 + a.M13 * b.M34 + a.M14 * b.M44,
+
+ a.M21 * b.M11 + a.M22 * b.M21 + a.M23 * b.M31 + a.M24 * b.M41,
+ a.M21 * b.M12 + a.M22 * b.M22 + a.M23 * b.M32 + a.M24 * b.M42,
+ a.M21 * b.M13 + a.M22 * b.M23 + a.M23 * b.M33 + a.M24 * b.M43,
+ a.M21 * b.M14 + a.M22 * b.M24 + a.M23 * b.M34 + a.M24 * b.M44,
+
+ a.M31 * b.M11 + a.M32 * b.M21 + a.M33 * b.M31 + a.M34 * b.M41,
+ a.M31 * b.M12 + a.M32 * b.M22 + a.M33 * b.M32 + a.M34 * b.M42,
+ a.M31 * b.M13 + a.M32 * b.M23 + a.M33 * b.M33 + a.M34 * b.M43,
+ a.M31 * b.M14 + a.M32 * b.M24 + a.M33 * b.M34 + a.M34 * b.M44,
+
+ a.M41 * b.M11 + a.M42 * b.M21 + a.M43 * b.M31 + a.M44 * b.M41,
+ a.M41 * b.M12 + a.M42 * b.M22 + a.M43 * b.M32 + a.M44 * b.M42,
+ a.M41 * b.M13 + a.M42 * b.M23 + a.M43 * b.M33 + a.M44 * b.M43,
+ a.M41 * b.M14 + a.M42 * b.M24 + a.M43 * b.M34 + a.M44 * b.M44
+ );
+
+ 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 &&
+ left.M31 == right.M31 && left.M32 == right.M32 && left.M33 == right.M33 && left.M34 == right.M34 &&
+ left.M41 == right.M41 && left.M42 == right.M42 && left.M43 == right.M43 && left.M44 == right.M44;
+
+ 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 ||
+ left.M31 != right.M31 || left.M32 != right.M32 || left.M33 != right.M33 || left.M34 != right.M34 ||
+ left.M41 != right.M41 || left.M42 != right.M42 || left.M43 != right.M43 || left.M44 != right.M44;
+
+ public static implicit operator System.Numerics.Matrix4x4(Matrix4x4 m) => new(
+ m.M11, m.M12, m.M13, m.M14,
+ m.M21, m.M22, m.M23, m.M24,
+ m.M31, m.M32, m.M33, m.M34,
+ m.M41, m.M42, m.M43, m.M44
+ );
+
+ public static implicit operator Matrix4x4(System.Numerics.Matrix4x4 m) => new(
+ m.M11, m.M12, m.M13, m.M14,
+ m.M21, m.M22, m.M23, m.M24,
+ m.M31, m.M32, m.M33, m.M34,
+ m.M41, m.M42, m.M43, m.M44
+ );
+
+ ///
+ /// Calculates the determinant of the .
+ ///
+ /// The .
+ /// The determinant of the .
+ public static float Determinant(Matrix4x4 m) => // https://www.euclideanspace.com/maths/algebra/matrix/functions/determinant/fourD/index.htm
+ m.M14 * m.M23 * m.M32 * m.M41 - m.M13 * m.M24 * m.M32 * m.M41 -
+ m.M14 * m.M22 * m.M33 * m.M41 + m.M12 * m.M24 * m.M33 * m.M41 +
+ m.M13 * m.M22 * m.M34 * m.M41 - m.M12 * m.M23 * m.M34 * m.M41 -
+ m.M14 * m.M23 * m.M31 * m.M42 + m.M13 * m.M24 * m.M31 * m.M42 +
+ m.M14 * m.M21 * m.M33 * m.M42 - m.M11 * m.M24 * m.M33 * m.M42 -
+ m.M13 * m.M21 * m.M34 * m.M42 + m.M11 * m.M23 * m.M34 * m.M42 +
+ m.M14 * m.M22 * m.M31 * m.M43 - m.M12 * m.M24 * m.M31 * m.M43 -
+ m.M14 * m.M21 * m.M32 * m.M43 + m.M11 * m.M24 * m.M32 * m.M43 +
+ m.M12 * m.M21 * m.M34 * m.M43 - m.M11 * m.M22 * m.M34 * m.M43 -
+ m.M13 * m.M22 * m.M31 * m.M44 + m.M12 * m.M23 * m.M31 * m.M44 +
+ 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;
+
+ 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
+ );
+
+ public static Matrix4x4 CreateScale(Vector3D scale) => new(
+ scale.X, 0f, 0f, 0f,
+ 0f, scale.Y, 0f, 0f,
+ 0f, 0f, scale.Z, 0f,
+ 0f, 0f, 0f, 1f
+ );
+
+ public static Matrix4x4 CreateRotationX(float radians)
+ {
+ 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
+ );
+ }
+
+ public static Matrix4x4 CreateRotationY(float radians)
+ {
+ float c = Math.Cos(radians);
+ float s = Math.Sin(radians);
+
+ return new Matrix4x4(
+ c, 0f, -s, 0f,
+ 0f, 1f, 0f, 0f,
+ s, 0f, c, 0f,
+ 0f, 0f, 0f, 1f
+ );
+ }
+
+ public static Matrix4x4 CreateRotationZ(float radians)
+ {
+ float c = Math.Cos(radians);
+ float s = Math.Sin(radians);
+
+ return new Matrix4x4(
+ c, s, 0f, 0f,
+ -s, c, 0f, 0f,
+ 0f, 0f, 1f, 0f,
+ 0f, 0f, 0f, 1f
+ );
+ }
+
+ // TODO Find a better calculation for this
+ public static Matrix4x4 CreateRotation(Quaternion quaternion)
+ {
+ Vector3D angles = quaternion.ToAngles();
+ return Identity * CreateRotationX(angles.X) * CreateRotationY(angles.Y) * CreateRotationZ(angles.Z);
+ }
+
+ public static Matrix4x4 CreateLookMatrix(Vector3D forward, Vector3D up)
+ {
+ Vector3D z = forward.Normalized;
+ Vector3D x = up.Cross(z).Normalized;
+ Vector3D y = z.Cross(x);
+
+ return new Matrix4x4(
+ x.X, y.X, z.X, 0f,
+ x.Y, y.Y, z.Y, 0f,
+ x.Z, y.Z, z.Z, 0f,
+ 0f, 0f, 0f, 1f
+ );
+ }
+
+ 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);
+
+ 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
+ );
+ }
+
+ public static Matrix4x4 CreatePerspectiveFieldOfView(float fieldOfViewInRadians, float aspectRatio, float nearPlane, float farPlane)
+ {
+ float yScale = 1f / Math.Tan(fieldOfViewInRadians / 2f);
+ float xScale = yScale / aspectRatio;
+
+ return new Matrix4x4(
+ xScale, 0f, 0f, 0f,
+ 0f, yScale, 0f, 0f,
+ 0f, 0f, farPlane / (farPlane - nearPlane), 1f,
+ 0f, 0f, -nearPlane * farPlane / (farPlane - nearPlane), 0f
+ );
+ }
+
+ public static Matrix4x4 ToRightHanded(Matrix4x4 m) => new(
+ m.M11, m.M12, m.M13, m.M14,
+ m.M31, m.M32, m.M33, m.M34,
+ m.M21, m.M22, m.M23, m.M24,
+ m.M41, m.M42, m.M43, m.M44
+ );
+
+ public override bool Equals(object? obj) => obj is Matrix4x4 matrix && this == matrix;
+ public bool Equals(Matrix4x4 other) => this == other;
+
+ public override int GetHashCode()
+ {
+ HashCode hashCode = new();
+ hashCode.Add(M11); hashCode.Add(M12); hashCode.Add(M13); hashCode.Add(M14);
+ hashCode.Add(M21); hashCode.Add(M22); hashCode.Add(M23); hashCode.Add(M24);
+ hashCode.Add(M31); hashCode.Add(M32); hashCode.Add(M33); hashCode.Add(M34);
+ hashCode.Add(M41); hashCode.Add(M42); hashCode.Add(M43); hashCode.Add(M44);
+ return hashCode.ToHashCode();
+ }
+
+ public override string ToString() => $"Matrix4x4({M11}, {M12}, {M13}, {M14},{M21}, {M22}, {M23}, {M24},{M31}, {M32}, {M33}, {M34},{M41}, {M42}, {M43}, {M44})";
+}
+
+///
+/// Provides extension methods for type.
+///
+public static class Matrix4x4Extensions
+{
+ ///
+ public static float Determinant(this Matrix4x4 matrix) => Matrix4x4.Determinant(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 ApplyRotationX(this Matrix4x4 matrix, float radians) => matrix * Matrix4x4.CreateRotationX(radians);
+
+ ///
+ public static Matrix4x4 ApplyRotationY(this Matrix4x4 matrix, float radians) => matrix * Matrix4x4.CreateRotationY(radians);
+
+ ///
+ public static Matrix4x4 ApplyRotationZ(this Matrix4x4 matrix, float radians) => matrix * Matrix4x4.CreateRotationZ(radians);
+
+ ///
+ 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 CreateLookMatrixTo(this Vector3D from, Vector3D to, Vector3 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 Quaternion Normalize(Quaternion quaternion) => quaternion / Length(quaternion);
+ public static Quaternion LookAt(Vector3D origin, Vector3D target, Vector3D up) => LookAt(target - origin, up);
+ public static Quaternion LookAt(Vector3D target, Vector3D up)
+ {
+ Vector3D forward = target.Normalized;
+
+ if (forward.LengthSquared() < 1e-6f)
+ return Identity;
+
+ Vector3D right = up.Cross(forward).Normalized;
+ Vector3D newUp = forward.Cross(right);
+
+ Matrix4x4 rot = new(
+ right.X, right.Y, right.Z, 0f,
+ newUp.X, newUp.Y, newUp.Z, 0f,
+ forward.X, forward.Y, forward.Z, 0f,
+ 0f, 0f, 0f, 1f
+ );
+
+ return FromRotationMatrix4x4(rot);
+ }
+
///
/// Rotates a around a axis by the specified angle (in radians).
///
@@ -212,8 +233,8 @@ public readonly struct Quaternion(float x, float y, float z, float w) : IEquatab
dot = -dot;
}
- if (dot > 0.9995f)
- return Lerp(from, to, t);
+ if (dot > 0.999999f)
+ return to;
float angle = Math.Acos(dot);
float sinAngle = Math.Sin(angle);
@@ -276,12 +297,11 @@ public readonly struct Quaternion(float x, float y, float z, float w) : IEquatab
}
///
- /// Calculates the from given .
+ /// Calculates the from given .
///
- /// The axis of the rotation in .
- /// The angle in radians.
- /// The rotation calculated by the given .
- public static System.Numerics.Matrix4x4 ToRotationMatrix4x4(Quaternion quaternion)
+ /// The rotation .
+ /// The rotation calculated by the given .
+ public static Matrix4x4 ToRotationMatrix4x4(Quaternion quaternion)
{
float m00 = 1 - 2 * (quaternion.Y * quaternion.Y + quaternion.Z * quaternion.Z);
float m01 = 2 * (quaternion.X * quaternion.Y - quaternion.W * quaternion.Z);
@@ -311,6 +331,52 @@ public readonly struct Quaternion(float x, float y, float z, float w) : IEquatab
);
}
+ ///
+ /// Calculates the from given .
+ ///
+ /// The rotation .
+ /// The rotation calculated by the given .
+ public static Quaternion FromRotationMatrix4x4(Matrix4x4 martix)
+ {
+ float trace = martix.M11 + martix.M22 + martix.M33;
+ float w, x, y, z;
+
+ if (trace > 0)
+ {
+ float s = Math.Sqrt(trace + 1.0f) * 2f;
+ w = .25f * s;
+ x = (martix.M23 - martix.M32) / s;
+ y = (martix.M31 - martix.M13) / s;
+ z = (martix.M12 - martix.M21) / s;
+ }
+ else if ((martix.M11 > martix.M22) && (martix.M11 > martix.M33))
+ {
+ float s = Math.Sqrt(1.0f + martix.M11 - martix.M22 - martix.M33) * 2f;
+ w = (martix.M23 - martix.M32) / s;
+ x = .25f * s;
+ y = (martix.M12 + martix.M21) / s;
+ z = (martix.M31 + martix.M13) / s;
+ }
+ else if (martix.M22 > martix.M33)
+ {
+ float s = Math.Sqrt(1.0f + martix.M22 - martix.M11 - martix.M33) * 2f;
+ w = (martix.M31 - martix.M13) / s;
+ x = (martix.M12 + martix.M21) / s;
+ y = .25f * s;
+ z = (martix.M23 + martix.M32) / s;
+ }
+ else
+ {
+ float s = Math.Sqrt(1.0f + martix.M33 - martix.M11 - martix.M22) * 2f;
+ w = (martix.M12 - martix.M21) / s;
+ x = (martix.M31 + martix.M13) / s;
+ y = (martix.M23 + martix.M32) / s;
+ z = .25f * s;
+ }
+
+ return new(x, y, z, w);
+ }
+
///
/// Checks if two s are approximately equal within a specified epsilon range.
///
@@ -393,7 +459,7 @@ public static class QuaternionExtensions
public static float Dot(this Quaternion left, Quaternion right) => Quaternion.Dot(left, right);
///
- public static System.Numerics.Matrix4x4 ToRotationMatrix4x4(this Quaternion quaternion) => Quaternion.ToRotationMatrix4x4(quaternion);
+ public static Matrix4x4 ToRotationMatrix4x4(this Quaternion quaternion) => Quaternion.ToRotationMatrix4x4(quaternion);
///
public static Quaternion CreateRotation(this Vector3D axis, float angle) => Quaternion.FromAxisAngle(axis, angle);
diff --git a/Engine.Core/Serialization/EntityRegistry.cs b/Engine.Core/Serialization/EntityRegistry.cs
deleted file mode 100644
index 6339ba1..0000000
--- a/Engine.Core/Serialization/EntityRegistry.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace Engine.Core.Serialization;
-
-public class EntityRegistry
-{
- public Event OnEntityRegistered = null!;
-
- private readonly Dictionary?> assignCallbacks = [];
- private readonly Dictionary registeredEntities = [];
- public IReadOnlyDictionary RegisteredEntities => registeredEntities;
-
- public void Add(IEntity entity)
- {
- if (registeredEntities.TryAdd(entity.Id, entity))
- OnEntityRegistered?.Invoke(this, new(entity));
- }
-
- public void QueueAssign(string id, Action setMethod)
- {
- assignCallbacks.TryAdd(id, null);
- assignCallbacks[id] = assignCallbacks[id] + setMethod;
- }
-
- public void AssignAll()
- {
- foreach ((string id, Action? action) in assignCallbacks)
- action?.Invoke(registeredEntities[id]);
- }
-
- public void Reset()
- {
- assignCallbacks.Clear();
- registeredEntities.Clear();
- }
-
- public readonly record struct EntityRegisteredArguments(IEntity Entity);
-}
diff --git a/Engine.Core/Serialization/IdentifiableRegistry.cs b/Engine.Core/Serialization/IdentifiableRegistry.cs
new file mode 100644
index 0000000..a69e160
--- /dev/null
+++ b/Engine.Core/Serialization/IdentifiableRegistry.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+
+namespace Engine.Core.Serialization;
+
+public class IdentifiableRegistry
+{
+ public Event OnEntityRegistered = null!;
+
+ private readonly Dictionary?> assignCallbacks = [];
+ private readonly Dictionary registeredEntities = [];
+ public IReadOnlyDictionary RegisteredEntities => registeredEntities;
+
+ public void Add(IIdentifiable identifiable)
+ {
+ if (registeredEntities.TryAdd(identifiable.Id, identifiable))
+ OnEntityRegistered?.Invoke(this, new(identifiable));
+ }
+
+ public void QueueAssign(string id, Action setMethod)
+ {
+ assignCallbacks.TryAdd(id, null);
+ assignCallbacks[id] = assignCallbacks[id] + setMethod;
+ }
+
+ public void AssignAll()
+ {
+ foreach ((string id, Action? action) in assignCallbacks)
+ action?.Invoke(registeredEntities[id]);
+ }
+
+ public void Reset()
+ {
+ assignCallbacks.Clear();
+ registeredEntities.Clear();
+ }
+
+ public readonly record struct EntityRegisteredArguments(IIdentifiable Entity);
+}
diff --git a/Engine.Core/Serialization/SerializedClass.cs b/Engine.Core/Serialization/SerializedClass.cs
index 803c83b..4abe1b6 100644
--- a/Engine.Core/Serialization/SerializedClass.cs
+++ b/Engine.Core/Serialization/SerializedClass.cs
@@ -43,8 +43,8 @@ public class SerializedClass
continue;
object? value = privatePropertyInfo.GetValue(@class);
- if (value is IEntity entity)
- Private.Add(privatePropertyInfo.Name, entity.Id);
+ if (value is IIdentifiable identifiable)
+ Private.Add(privatePropertyInfo.Name, identifiable.Id);
else
Private.Add(privatePropertyInfo.Name, value);
}
@@ -61,8 +61,8 @@ public class SerializedClass
continue;
object? value = publicPropertyInfo.GetValue(@class);
- if (value is IEntity entity)
- Public.Add(publicPropertyInfo.Name, entity.Id);
+ if (value is IIdentifiable identifiable)
+ Public.Add(publicPropertyInfo.Name, identifiable.Id);
else
Public.Add(publicPropertyInfo.Name, value);
}
@@ -76,8 +76,8 @@ public class SerializedClass
continue;
object? value = privateFieldInfo.GetValue(@class);
- if (value is IEntity entity)
- Private.Add(privateFieldInfo.Name, entity.Id);
+ if (value is IIdentifiable identifiable)
+ Private.Add(privateFieldInfo.Name, identifiable.Id);
else
Private.Add(privateFieldInfo.Name, value);
}
@@ -91,8 +91,8 @@ public class SerializedClass
continue;
object? value = publicFieldInfo.GetValue(@class);
- if (value is IEntity entity)
- Public.Add(publicFieldInfo.Name, entity.Id);
+ if (value is IIdentifiable identifiable)
+ Public.Add(publicFieldInfo.Name, identifiable.Id);
else
Public.Add(publicFieldInfo.Name, value);
}
@@ -112,36 +112,36 @@ public class SerializedClass
return instance;
}
- public object CreateInstance(EntityRegistry? entityRegistry)
+ public object CreateInstance(IdentifiableRegistry? identifiableRegistry)
{
- if (entityRegistry is null)
+ if (identifiableRegistry is null)
return CreateInstance();
Type type = TypeFactory.GetType(Type);
object instance = TypeFactory.Get(type);
foreach ((string key, object? value) in Private)
- AssignVariable(key, type, instance, value, PRIVATE_BINDING_FLAGS, entityRegistry);
+ AssignVariable(key, type, instance, value, PRIVATE_BINDING_FLAGS, identifiableRegistry);
foreach ((string key, object? value) in Public)
- AssignVariable(key, type, instance, value, PUBLIC_BINDING_FLAGS, entityRegistry);
+ AssignVariable(key, type, instance, value, PUBLIC_BINDING_FLAGS, identifiableRegistry);
return instance;
}
- private static void AssignVariable(string key, Type type, object instance, object? value, BindingFlags bindingFlags, EntityRegistry entityRegistry)
+ private static void AssignVariable(string key, Type type, object instance, object? value, BindingFlags bindingFlags, IdentifiableRegistry identifiableRegistry)
{
if (type.GetField(key, bindingFlags) is FieldInfo fieldInfo)
{
- if (typeof(IEntity).IsAssignableFrom(fieldInfo.FieldType))
- entityRegistry.QueueAssign(value?.ToString() ?? "", (entity) => fieldInfo.SetValue(instance, entity));
+ if (typeof(IIdentifiable).IsAssignableFrom(fieldInfo.FieldType))
+ identifiableRegistry.QueueAssign(value?.ToString() ?? "", (entity) => fieldInfo.SetValue(instance, entity));
else
fieldInfo.SetValue(instance, value);
}
else if (type.GetProperty(key, bindingFlags) is PropertyInfo propertyInfo)
{
- if (typeof(IEntity).IsAssignableFrom(propertyInfo.PropertyType))
- entityRegistry.QueueAssign(value?.ToString() ?? "", (entity) => propertyInfo.SetValue(instance, entity));
+ if (typeof(IIdentifiable).IsAssignableFrom(propertyInfo.PropertyType))
+ identifiableRegistry.QueueAssign(value?.ToString() ?? "", (entity) => propertyInfo.SetValue(instance, entity));
else
propertyInfo.SetValue(instance, value);
}
diff --git a/Engine.Core/Systems/UniverseEntranceManager.cs b/Engine.Core/Systems/UniverseEntranceManager.cs
index 60aea30..85f579e 100644
--- a/Engine.Core/Systems/UniverseEntranceManager.cs
+++ b/Engine.Core/Systems/UniverseEntranceManager.cs
@@ -9,7 +9,6 @@ public class UniverseEntranceManager : Internal.BehaviourIndependent
private static System.Func GetPriority() => (b) => b.Priority;
private readonly ActiveBehaviourCollectorOrdered enterUniverses = new(GetPriority(), SortByAscendingPriority());
- private readonly ActiveBehaviourCollectorOrdered exitUniverses = new(GetPriority(), SortByAscendingPriority());
private readonly List toCallEnterUniverses = new(32);
private readonly List toCallExitUniverses = new(32);
@@ -21,9 +20,8 @@ public class UniverseEntranceManager : Internal.BehaviourIndependent
// event it tries to call OnUniverseObjectRegistered again on the same object, causing a duplicate entry error.
Debug.Assert.AssertTrue(BehaviourController.Count == 1, $"{nameof(UniverseEntranceManager)} must be in it's own {nameof(IUniverseObject)} with no other {nameof(IBehaviour)}s attached at the moment. Failing to do so might cause instantiation or serialization issues.");
enterUniverses.Assign(universe);
- exitUniverses.Assign(universe);
- foreach (IUniverseObject universeObject in universe.UniverseObjects)
+ foreach (IUniverseObject universeObject in universe)
OnUniverseObjectRegistered(universe, new(universeObject));
universe.OnUniverseObjectRegistered.AddListener(OnUniverseObjectRegistered);
@@ -33,9 +31,8 @@ public class UniverseEntranceManager : Internal.BehaviourIndependent
protected override void OnExitedUniverse(IUniverse universe)
{
enterUniverses.Unassign();
- exitUniverses.Unassign();
- foreach (IUniverseObject universeObject in universe.UniverseObjects)
+ foreach (IUniverseObject universeObject in universe)
OnUniverseObjectUnRegistered(universe, new(universeObject));
universe.OnUniverseObjectRegistered.RemoveListener(OnUniverseObjectRegistered);
@@ -44,6 +41,8 @@ public class UniverseEntranceManager : Internal.BehaviourIndependent
private void OnUniverseObjectUnRegistered(IUniverse sender, IUniverse.UniverseObjectUnRegisteredArguments args)
{
+ args.UniverseObjectUnregistered.BehaviourController.GetBehavioursInChildren(toCallExitUniverses);
+
for (int i = toCallExitUniverses.Count - 1; i >= 0; i--)
{
IExitUniverse exitUniverse = toCallExitUniverses[i];
@@ -67,14 +66,8 @@ public class UniverseEntranceManager : Internal.BehaviourIndependent
toCallEnterUniverses.Add(args.BehaviourCollected);
}
- private void OnExitUniverseCollected(IBehaviourCollector sender, IBehaviourCollector.BehaviourCollectedArguments args)
- {
- toCallExitUniverses.Add(args.BehaviourCollected);
- }
-
public UniverseEntranceManager()
{
enterUniverses.OnCollected.AddListener(OnEnterUniverseCollected);
- exitUniverses.OnCollected.AddListener(OnExitUniverseCollected);
}
}
diff --git a/Engine.Core/Universe.cs b/Engine.Core/Universe.cs
index 8e3c28f..95041e5 100644
--- a/Engine.Core/Universe.cs
+++ b/Engine.Core/Universe.cs
@@ -31,7 +31,7 @@ public class Universe : BaseEntity, IUniverse
delegateOnUniverseObjectExitedUniverse = OnUniverseObjectExitedUniverse;
}
- public IReadOnlyList UniverseObjects => _universeObjects;
+ public IUniverseObject Root { get; private set; } = Factory.UniverseObjectFactory.Instantiate().SetUniverseObject("Root");
public UniverseTime Time { get; private set; } = new();
public UniverseTime UnscaledTime { get; private set; } = new();
@@ -63,14 +63,17 @@ public class Universe : BaseEntity, IUniverse
if (!universeObject.Initialize())
throw new Exception($"{universeObject.Name} can't be initialized");
- for (int i = 0; i < universeObject.Children.Count; i++)
- Register(universeObject.Children[i]);
+ if (universeObject.Parent == null)
+ universeObject.Parent = Root;
_universeObjects.Add(universeObject);
if (!universeObject.EnterUniverse(this))
throw new Exception($"{universeObject.Name} can't enter the universe");
+ for (int i = 0; i < universeObject.Children.Count; i++)
+ Register(universeObject.Children[i]);
+
OnUniverseObjectRegistered?.Invoke(this, new(universeObject));
}
@@ -113,15 +116,15 @@ public class Universe : BaseEntity, IUniverse
protected override void InitializeInternal()
{
- foreach (IUniverseObject universeObject in UniverseObjects)
+ foreach (IUniverseObject universeObject in _universeObjects)
universeObject.Initialize();
}
protected override void FinalizeInternal()
{
base.FinalizeInternal();
- for (int i = UniverseObjects.Count - 1; i >= 0; i--)
- Remove(UniverseObjects[i]);
+ for (int i = _universeObjects.Count - 1; i >= 0; i--)
+ Remove(_universeObjects[i]);
}
public void Update(UniverseTime engineTime)
@@ -158,6 +161,6 @@ public class Universe : BaseEntity, IUniverse
Remove(universeObject);
}
- public IEnumerator GetEnumerator() => _universeObjects.GetEnumerator();
- IEnumerator IEnumerable.GetEnumerator() => _universeObjects.GetEnumerator();
+ public IEnumerator GetEnumerator() => Root.TraverseChildren();
+ IEnumerator IEnumerable.GetEnumerator() => Root.TraverseChildren();
}
diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibClient.cs b/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibClient.cs
index cec0006..c6f578c 100644
--- a/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibClient.cs
+++ b/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibClient.cs
@@ -11,6 +11,7 @@ namespace Engine.Systems.Network;
public class LiteNetLibClient : LiteNetLibCommunicatorBase, INetworkCommunicatorClient
{
private readonly NetDataWriter netDataWriter = new();
+ private readonly NetDataWriter netDataWriterEncrypted = new();
private CancellationTokenSource? cancellationTokenSource = null;
@@ -42,7 +43,20 @@ public class LiteNetLibClient : LiteNetLibCommunicatorBase, INetworkCommunicator
public INetworkCommunicatorClient SendToServer(T packet, PacketDelivery packetDelivery) where T : class, new()
{
netDataWriter.Reset();
- netPacketProcessor.Write(netDataWriter, packet);
+ netDataWriterEncrypted.Reset();
+
+ if (packet is INetworkPacketEncrypted)
+ {
+ netPacketProcessor.Write(netDataWriterEncrypted, packet);
+ byte[] encryptedData = cryptor.Encrypt(netDataWriterEncrypted.CopyData());
+ netDataWriter.Put(true);
+ netDataWriter.PutBytesWithLength(encryptedData);
+ }
+ else
+ {
+ netDataWriter.Put(false);
+ netPacketProcessor.Write(netDataWriter, packet);
+ }
switch (packetDelivery)
{
diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibCommunicatorBase.cs b/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibCommunicatorBase.cs
index 4dd1016..daa7a0d 100644
--- a/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibCommunicatorBase.cs
+++ b/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibCommunicatorBase.cs
@@ -12,6 +12,7 @@ namespace Engine.Systems.Network;
public abstract class LiteNetLibCommunicatorBase : Behaviour, IEnterUniverse, IExitUniverse, INetworkCommunicator
{
protected readonly NetPacketProcessor netPacketProcessor = new();
+ protected readonly PacketCryptor cryptor = new("At4ywW9PGoWH3g==", "NmpMFTvd3pvUbA=="); // TODO implement public key exchange
private readonly Dictionary> listeners = [];
private readonly Dictionary _connections = [];
@@ -54,7 +55,20 @@ public abstract class LiteNetLibCommunicatorBase : Behaviour, IEnterUniverse, IE
private void NetworkReceiveEvent(NetPeer peer, NetPacketReader reader, byte channel, DeliveryMethod deliveryMethod)
{
- try { netPacketProcessor.ReadAllPackets(reader, peer); }
+ try
+ {
+ bool isEncrypted = reader.GetBool();
+ if (isEncrypted) // TODO performance improvements
+ {
+ byte[] encryptedData = reader.GetBytesWithLength();
+ byte[] decryptedData = cryptor.Decrypt(encryptedData);
+ NetDataReader innerReader = new(decryptedData);
+ netPacketProcessor.ReadAllPackets(innerReader, peer);
+ return;
+ }
+
+ netPacketProcessor.ReadAllPackets(reader, peer);
+ }
catch (Exception exception) { logger?.LogException(this, exception, force: true); }
}
diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibServer.cs b/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibServer.cs
index c9e8d62..0ba6fa4 100644
--- a/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibServer.cs
+++ b/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibServer.cs
@@ -13,6 +13,7 @@ public class LiteNetLibServer : LiteNetLibCommunicatorBase, INetworkCommunicator
public int Port { get; private set; } = 8888;
private readonly NetDataWriter netDataWriter = new();
+ private readonly NetDataWriter netDataWriterEncrypted = new();
public LiteNetLibServer() : this(8888, 2) { }
public LiteNetLibServer(int port, int maxConnectionCount) : base()
@@ -53,11 +54,25 @@ public class LiteNetLibServer : LiteNetLibCommunicatorBase, INetworkCommunicator
public INetworkCommunicatorServer SendToClient(IConnection connection, T packet, PacketDelivery packetDelivery) where T : class, new()
{
netDataWriter.Reset();
- netPacketProcessor.Write(netDataWriter, packet);
- if (Manager.ConnectedPeerList.FirstOrDefault(p => p.Id.CompareTo(connection.Id) == 0) is not NetPeer netPeer)
+ if (Manager.ConnectedPeerList.FirstOrDefault(p => p.Id.ToString().CompareTo(connection.Id) == 0) is not NetPeer netPeer)
throw new($"Peer {connection} couldn't be found.");
+ if (packet is INetworkPacketEncrypted) // TODO performance improvements
+ {
+ netDataWriterEncrypted.Reset();
+ netPacketProcessor.Write(netDataWriterEncrypted, packet);
+ byte[] encryptedData = cryptor.Encrypt(netDataWriterEncrypted.CopyData());
+
+ netDataWriter.Put(true);
+ netDataWriter.PutBytesWithLength(encryptedData);
+ }
+ else
+ {
+ netDataWriter.Put(false);
+ netPacketProcessor.Write(netDataWriter, packet);
+ }
+
switch (packetDelivery)
{
case PacketDelivery.ReliableInOrder: netPeer.Send(netDataWriter, DeliveryMethod.ReliableOrdered); break;
@@ -75,6 +90,23 @@ public class LiteNetLibServer : LiteNetLibCommunicatorBase, INetworkCommunicator
netDataWriter.Reset();
netPacketProcessor.Write(netDataWriter, packet);
+ if (packet is INetworkPacketEncrypted)
+ {
+ netDataWriterEncrypted.Reset();
+
+ logger?.Log($"Encrypted Packet Sending");
+ netPacketProcessor.Write(netDataWriterEncrypted, packet);
+ byte[] encryptedData = cryptor.Encrypt(netDataWriterEncrypted.CopyData());
+ netDataWriter.PutBytesWithLength(encryptedData);
+ netDataWriter.Put(true);
+ }
+ else
+ {
+ logger?.Log($"Regular Packet Sending");
+ netPacketProcessor.Write(netDataWriter, packet);
+ netDataWriter.Put(false);
+ }
+
switch (packetDelivery)
{
case PacketDelivery.ReliableInOrder: Manager.SendToAll(netDataWriter, DeliveryMethod.ReliableOrdered); break;
diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/PacketCryptor.cs b/Engine.Integration/Engine.Integration.LiteNetLib/PacketCryptor.cs
new file mode 100644
index 0000000..3a3c5ba
--- /dev/null
+++ b/Engine.Integration/Engine.Integration.LiteNetLib/PacketCryptor.cs
@@ -0,0 +1,26 @@
+using System.Security.Cryptography;
+using System.Text;
+
+namespace Engine.Systems.Network;
+
+public class PacketCryptor // TODO performance improvements
+{
+ private readonly Aes aes = null!;
+
+ private readonly ICryptoTransform encrpytor = null!;
+ private readonly ICryptoTransform decryptor = null!;
+
+ public byte[] Encrypt(byte[] data) => encrpytor.TransformFinalBlock(data, 0, data.Length);
+ public byte[] Decrypt(byte[] data) => decryptor.TransformFinalBlock(data, 0, data.Length);
+
+ public PacketCryptor(string key, string initializationVector)
+ {
+ aes = Aes.Create();
+
+ aes.Key = Encoding.UTF8.GetBytes(key);
+ aes.IV = Encoding.UTF8.GetBytes(initializationVector);
+
+ encrpytor = aes.CreateEncryptor();
+ decryptor = aes.CreateDecryptor();
+ }
+}
diff --git a/Engine.Integration/Engine.Integration.MonoGame/Behaviours/MonoGameCamera2D.cs b/Engine.Integration/Engine.Integration.MonoGame/Behaviours/MonoGameCamera2D.cs
index 911a7af..e8827be 100644
--- a/Engine.Integration/Engine.Integration.MonoGame/Behaviours/MonoGameCamera2D.cs
+++ b/Engine.Integration/Engine.Integration.MonoGame/Behaviours/MonoGameCamera2D.cs
@@ -5,7 +5,7 @@ using Engine.Core;
namespace Engine.Integration.MonoGame;
-public class MonoGameCamera2D : Behaviour, ICamera2D, IFirstFrameUpdate, IPreDraw
+public class MonoGameCamera2D : Behaviour, ICamera2D, IFirstFrameUpdate, ILastFrameUpdate, IPreDraw
{
public Event OnMatrixTransformChanged { get; } = new();
public Event OnViewportChanged { get; } = new();
@@ -56,7 +56,7 @@ public class MonoGameCamera2D : Behaviour, ICamera2D, IFirstFrameUpdate, IPreDra
get => _zoom;
set
{
- float newValue = Engine.Core.Math.Max(0.1f, value);
+ float newValue = Math.Max(0.1f, value);
if (_zoom == newValue)
return;
@@ -84,22 +84,21 @@ public class MonoGameCamera2D : Behaviour, ICamera2D, IFirstFrameUpdate, IPreDra
return screenPosition.Scale(EngineConverterExtensions.screenScale);
}
+ public void LastActiveFrame() => Transform = null!;
public void FirstActiveFrame()
{
Graphics = BehaviourController.UniverseObject.Universe.FindRequiredBehaviour().Window.Graphics;
Viewport = Graphics.GraphicsDevice.Viewport;
+ Transform = BehaviourController.GetRequiredBehaviour();
}
public void PreDraw()
{
MatrixTransform =
Matrix.CreateTranslation(new Vector3(-Position.X, Position.Y, 0f)) *
- Matrix.CreateRotationZ(Rotation * Engine.Core.Math.DegreeToRadian) *
+ Matrix.CreateRotationZ(Rotation * Math.DegreeToRadian) *
Matrix.CreateScale(Transform.Scale.X.Max(Transform.Scale.Y)) *
Matrix.CreateScale(Zoom) *
Matrix.CreateTranslation(new Vector3(_viewport.Width * .5f, _viewport.Height * .5f, 0f));
}
-
- protected sealed override void InitializeInternal() => Transform = BehaviourController.GetRequiredBehaviour();
- protected sealed override void FinalizeInternal() => Transform = null!;
}
diff --git a/Engine.Integration/Engine.Integration.MonoGame/Behaviours/MonoGameCamera3D.cs b/Engine.Integration/Engine.Integration.MonoGame/Behaviours/MonoGameCamera3D.cs
index 591ed3b..1bf3468 100644
--- a/Engine.Integration/Engine.Integration.MonoGame/Behaviours/MonoGameCamera3D.cs
+++ b/Engine.Integration/Engine.Integration.MonoGame/Behaviours/MonoGameCamera3D.cs
@@ -5,17 +5,23 @@ using Engine.Core;
namespace Engine.Integration.MonoGame;
-public class MonoGameCamera3D : Behaviour, ICamera3D, IFirstFrameUpdate, IPreDraw
+public class MonoGameCamera3D : Behaviour, ICamera3D, IFirstFrameUpdate, ILastFrameUpdate, IPreDraw
{
public Event OnViewChanged { get; } = new();
public Event OnProjectionChanged { get; } = new();
public Event OnViewportChanged { get; } = new();
- public Event OnFieldOfViewChanged { get; } = new();
+
+ public Event OnNearPlaneChanged { get; } = new();
+ public Event OnFarPlaneChanged { get; } = new();
+ public Event OnFieldOfViewChanged { get; } = new();
private Matrix _view = Matrix.Identity;
private Matrix _projection = Matrix.Identity;
private Viewport _viewport = default;
- private float _fieldOfView = 1f;
+ private float _nearPlane = 0.01f;
+ private float _farPlane = 100f;
+ private float _fieldOfView = Math.DegreeToRadian * 70f;
+ private bool isRecalculationNeeded = true;
public GraphicsDeviceManager Graphics { get; private set; } = null!;
public ITransform3D Transform { get; private set; } = null!;
@@ -47,12 +53,6 @@ public class MonoGameCamera3D : Behaviour, ICamera3D, IFirstFrameUpdate, IPreDra
}
}
- public Vector3D Position
- {
- get => Transform.Position;
- set => Transform.Position = value;
- }
-
public Viewport Viewport
{
get => _viewport;
@@ -63,32 +63,52 @@ public class MonoGameCamera3D : Behaviour, ICamera3D, IFirstFrameUpdate, IPreDra
Viewport previousViewport = _viewport;
_viewport = value;
+ SetForRecalculation();
OnViewportChanged.Invoke(this, new(previousViewport));
}
}
+ public float NearPlane
+ {
+ get => _nearPlane;
+ set
+ {
+ float previousNearPlane = _nearPlane;
+ _nearPlane = value.Max(0.0001f);
+ SetForRecalculation();
+ OnNearPlaneChanged.Invoke(this, new(previousNearPlane));
+ }
+ }
+
+ public float FarPlane
+ {
+ get => _farPlane;
+ set
+ {
+ float previousFarPlane = _farPlane;
+ _farPlane = value.Max(NearPlane);
+ SetForRecalculation();
+ OnFarPlaneChanged.Invoke(this, new(previousFarPlane));
+ }
+ }
+
public float FieldOfView
{
- get => _fieldOfView;
+ get => _fieldOfView * Math.RadianToDegree;
set
{
- float newValue = Math.Max(0.1f, value);
+ value = value.Max(0.1f) * Math.DegreeToRadian;
- if (_fieldOfView == newValue)
+ if (_fieldOfView == value)
return;
float previousFieldOfView = _fieldOfView;
- _fieldOfView = newValue;
+ _fieldOfView = value;
+ SetForRecalculation();
OnFieldOfViewChanged.Invoke(this, new(previousFieldOfView));
}
}
- public Engine.Core.Quaternion Rotation
- {
- get => Transform.Rotation;
- set => Transform.Rotation = value;
- }
-
// TODO This causes delay since OnPreDraw calls assuming this is called in in Update
public Ray3D ScreenToWorldRay(Vector2D screenPosition)
{
@@ -104,28 +124,61 @@ public class MonoGameCamera3D : Behaviour, ICamera3D, IFirstFrameUpdate, IPreDra
public Vector2D WorldToScreenPosition(Vector3D worldPosition) => Viewport.Project(worldPosition.ToVector3(), _projection, _view, Matrix.Identity).ToVector3D();
+ public void LastActiveFrame() => Transform.OnTransformUpdated.RemoveListener(SetDirtyOnTransformUpdate);
public void FirstActiveFrame()
{
+ Transform = BehaviourController.GetRequiredBehaviour();
Graphics = BehaviourController.UniverseObject.Universe.FindRequiredBehaviour().Window.Graphics;
Viewport = Graphics.GraphicsDevice.Viewport;
+
+ Transform.OnTransformUpdated.AddListener(SetDirtyOnTransformUpdate);
}
+ private void SetDirtyOnTransformUpdate(ITransform3D sender) => SetForRecalculation();
+ private void SetForRecalculation() => isRecalculationNeeded = true;
+
public void PreDraw()
{
- Vector3 cameraPosition = Position.ToVector3();
- View = Matrix.CreateLookAt(
- cameraPosition,
- Transform.Forward.ToVector3() + cameraPosition,
- Transform.Up.ToVector3()
- );
- Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, Viewport.AspectRatio, 0.1f, 100f);
+ if (!isRecalculationNeeded)
+ return;
+
+ CalculateView();
+ CalculateProjection();
+ isRecalculationNeeded = false;
}
- protected sealed override void InitializeInternal() => Transform = BehaviourController.GetRequiredBehaviour();
- protected sealed override void FinalizeInternal() => Transform = null!;
+ private void CalculateView()
+ {
+ Vector3 forward = Vector3.Normalize(Transform.Forward.ToVector3());
+ Vector3 up = Vector3.Normalize(Transform.Up.ToVector3());
+ Vector3 right = Vector3.Normalize(Transform.Right.ToVector3());
+ Vector3 position = Transform.Position.ToVector3();
+
+ View = new Matrix(
+ right.X, up.X, forward.X, 0,
+ right.Y, up.Y, forward.Y, 0,
+ right.Z, up.Z, forward.Z, 0,
+ -Vector3.Dot(right, position),
+ -Vector3.Dot(up, position),
+ -Vector3.Dot(forward, position),
+ 1
+ );
+ }
+
+ private void CalculateProjection()
+ {
+ float yScale = 1f / (float)Math.Tan(_fieldOfView / 2f);
+ float xScale = yScale / Viewport.AspectRatio;
+
+ Projection = new Matrix(
+ xScale, 0, 0, 0,
+ 0, yScale, 0, 0,
+ 0, 0, _farPlane / (_farPlane - _nearPlane), 1,
+ 0, 0, -_nearPlane * _farPlane / (_farPlane - _nearPlane), 0
+ );
+ }
public readonly record struct ViewChangedArguments(Matrix PreviousView);
public readonly record struct ProjectionChangedArguments(Matrix PreviousProjection);
public readonly record struct ViewportChangedArguments(Viewport PreviousViewport);
- public readonly record struct FieldOfViewChangedArguments(float PreviousFieldOfView);
}
diff --git a/Engine.Integration/Engine.Integration.MonoGame/EngineConverter.cs b/Engine.Integration/Engine.Integration.MonoGame/EngineConverter.cs
index 32e48d1..354b196 100644
--- a/Engine.Integration/Engine.Integration.MonoGame/EngineConverter.cs
+++ b/Engine.Integration/Engine.Integration.MonoGame/EngineConverter.cs
@@ -41,6 +41,14 @@ public static class EngineConverterExtensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3 ToVector3(this Vector3D vector) => new(vector.X, vector.Y, vector.Z);
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Matrix ToXnaMatrix(this Matrix4x4 m) => new(
+ m.M11, m.M12, m.M13, m.M14,
+ m.M21, m.M22, m.M23, m.M24,
+ m.M31, m.M32, m.M33, m.M34,
+ m.M41, m.M42, m.M43, m.M44
+ );
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Microsoft.Xna.Framework.Quaternion ToXnaQuaternion(this Core.Quaternion quaternion) => new(quaternion.X, quaternion.Y, quaternion.Z, quaternion.W);
diff --git a/Engine.Integration/Engine.Integration.MonoGame/MonoGameWindow.cs b/Engine.Integration/Engine.Integration.MonoGame/MonoGameWindow.cs
index 7decf16..dd39082 100644
--- a/Engine.Integration/Engine.Integration.MonoGame/MonoGameWindow.cs
+++ b/Engine.Integration/Engine.Integration.MonoGame/MonoGameWindow.cs
@@ -19,10 +19,12 @@ public class MonoGameWindow : Game
IsMouseVisible = true;
Universe = universe ?? new Universe();
- Universe.InstantiateUniverseObject().SetUniverseObject("Window Container")
+ IUniverseObject monogameParent = Universe.InstantiateUniverseObject().SetUniverseObject("MonoGame");
+
+ Universe.InstantiateUniverseObject().SetUniverseObject("Window Container", monogameParent)
.BehaviourController.AddBehaviour(this);
- Universe.InstantiateUniverseObject().SetUniverseObject("Content Loader")
+ Universe.InstantiateUniverseObject().SetUniverseObject("Content Loader", monogameParent)
.BehaviourController.AddBehaviour();
}
diff --git a/Engine.Integration/Engine.Integration.Yaml/Converters/Abstract/IEngineTypeYamlConverter.cs b/Engine.Integration/Engine.Integration.Yaml/Converters/Abstract/IEngineTypeYamlConverter.cs
index 873b796..f641442 100644
--- a/Engine.Integration/Engine.Integration.Yaml/Converters/Abstract/IEngineTypeYamlConverter.cs
+++ b/Engine.Integration/Engine.Integration.Yaml/Converters/Abstract/IEngineTypeYamlConverter.cs
@@ -8,6 +8,6 @@ namespace Engine.Serializers.Yaml;
public interface IEngineTypeYamlConverter : IYamlTypeConverter
{
YamlSerializer Serializer { get; set; }
- EntityRegistry EntityRegistry { get; set; }
+ IdentifiableRegistry IdentifiableRegistry { get; set; }
IProgressionTracker ProgressionTracker { get; set; }
}
diff --git a/Engine.Integration/Engine.Integration.Yaml/Converters/BehaviourControllerConverter.cs b/Engine.Integration/Engine.Integration.Yaml/Converters/BehaviourControllerConverter.cs
index 89391b4..530ae2c 100644
--- a/Engine.Integration/Engine.Integration.Yaml/Converters/BehaviourControllerConverter.cs
+++ b/Engine.Integration/Engine.Integration.Yaml/Converters/BehaviourControllerConverter.cs
@@ -36,7 +36,7 @@ public class BehaviourControllerConverter : EngineTypeYamlSerializerBase().Value;
if (value.CompareTo(nameof(IBehaviourController.StateEnable)) != 0)
diff --git a/Engine.Integration/Engine.Integration.Yaml/Converters/BehaviourConverter.cs b/Engine.Integration/Engine.Integration.Yaml/Converters/BehaviourConverter.cs
index abc3b97..a475f96 100644
--- a/Engine.Integration/Engine.Integration.Yaml/Converters/BehaviourConverter.cs
+++ b/Engine.Integration/Engine.Integration.Yaml/Converters/BehaviourConverter.cs
@@ -36,7 +36,7 @@ public class BehaviourConverter : EngineTypeYamlSerializerBase
throw new();
SerializedClass instanceSerializedClass = (SerializedClass)rootDeserializer(typeof(SerializedClass))!;
ProgressionTracker.Set(isTrackingController ? .5f : ProgressionTracker.Progression, $"Creating {instanceSerializedClass.Type}");
- behaviour = (IBehaviour)instanceSerializedClass.CreateInstance(EntityRegistry);
+ behaviour = (IBehaviour)instanceSerializedClass.CreateInstance(IdentifiableRegistry);
if (parser.Consume().Value.CompareTo(nameof(IBehaviour.StateEnable)) != 0)
throw new();
diff --git a/Engine.Integration/Engine.Integration.Yaml/Converters/EngineTypeYamlConverterBase.cs b/Engine.Integration/Engine.Integration.Yaml/Converters/EngineTypeYamlConverterBase.cs
index 134e922..99167ee 100644
--- a/Engine.Integration/Engine.Integration.Yaml/Converters/EngineTypeYamlConverterBase.cs
+++ b/Engine.Integration/Engine.Integration.Yaml/Converters/EngineTypeYamlConverterBase.cs
@@ -12,7 +12,7 @@ public abstract class EngineTypeYamlSerializerBase : IEngineTypeYamlConverter
{
protected const string SERIALIZED_SCALAR_NAME = "Properties";
- public EntityRegistry EntityRegistry { get; set; } = null!;
+ public IdentifiableRegistry IdentifiableRegistry { get; set; } = null!;
public YamlSerializer Serializer { get; set; } = null!;
public IProgressionTracker ProgressionTracker { get; set; } = null!;
@@ -24,7 +24,7 @@ public abstract class EngineTypeYamlSerializerBase : IEngineTypeYamlConverter
T? result = Read(parser, type, rootDeserializer);
if (result is IEntity entity)
- EntityRegistry.Add(entity);
+ IdentifiableRegistry.Add(entity);
return result;
}
diff --git a/Engine.Integration/Engine.Integration.Yaml/Converters/StateEnableConverter.cs b/Engine.Integration/Engine.Integration.Yaml/Converters/StateEnableConverter.cs
index 9c317b0..d5c0bc0 100644
--- a/Engine.Integration/Engine.Integration.Yaml/Converters/StateEnableConverter.cs
+++ b/Engine.Integration/Engine.Integration.Yaml/Converters/StateEnableConverter.cs
@@ -29,7 +29,7 @@ public class StateEnableConverter : EngineTypeYamlSerializerBase
if (parser.Consume().Value.CompareTo(SERIALIZED_SCALAR_NAME) != 0)
throw new();
SerializedClass instanceSerializedClass = (SerializedClass)rootDeserializer(typeof(SerializedClass))!;
- stateEnable = (IStateEnable)instanceSerializedClass.CreateInstance(EntityRegistry);
+ stateEnable = (IStateEnable)instanceSerializedClass.CreateInstance(IdentifiableRegistry);
parser.Consume();
diff --git a/Engine.Integration/Engine.Integration.Yaml/Converters/UniverseConverter.cs b/Engine.Integration/Engine.Integration.Yaml/Converters/UniverseConverter.cs
index 6fdf87e..4a21d0e 100644
--- a/Engine.Integration/Engine.Integration.Yaml/Converters/UniverseConverter.cs
+++ b/Engine.Integration/Engine.Integration.Yaml/Converters/UniverseConverter.cs
@@ -22,7 +22,7 @@ public class UniverseConverter : EngineTypeYamlSerializerBase
IUniverse universe;
IStateEnable stateEnable;
- List universeObjects;
+ IUniverseObject rootUniverseObject;
parser.Consume();
@@ -34,16 +34,16 @@ public class UniverseConverter : EngineTypeYamlSerializerBase
throw new();
SerializedClass instanceSerializedClass = (SerializedClass)rootDeserializer(typeof(SerializedClass))!;
ProgressionTracker.Set(isTrackingController ? .2f : ProgressionTracker.Progression, $"Creating {instanceSerializedClass.Type}");
- universe = (IUniverse)instanceSerializedClass.CreateInstance(EntityRegistry);
+ universe = (IUniverse)instanceSerializedClass.CreateInstance(IdentifiableRegistry);
if (parser.Consume().Value.CompareTo(nameof(IUniverse.StateEnable)) != 0)
throw new();
stateEnable = (IStateEnable)rootDeserializer(typeof(IStateEnable))!;
ProgressionTracker.Set(isTrackingController ? .5f : ProgressionTracker.Progression, $"Reading universe objects");
- if (parser.Consume().Value.CompareTo(nameof(IUniverse.UniverseObjects)) != 0)
+ if (parser.Consume().Value.CompareTo(nameof(IUniverse.Root)) != 0)
throw new();
- universeObjects = (List)rootDeserializer(typeof(List))!;
+ rootUniverseObject = (IUniverseObject)rootDeserializer(typeof(IUniverseObject))!;
parser.Consume();
@@ -52,13 +52,12 @@ public class UniverseConverter : EngineTypeYamlSerializerBase
stateEnable.Assign(universe);
universe.Assign(stateEnable);
- ProgressionTracker.Set(isTrackingController ? .9f : ProgressionTracker.Progression, "Registering universe objects");
- for (int i = 0; i < universeObjects.Count; i++)
+ ProgressionTracker.Set(isTrackingController ? .9f : ProgressionTracker.Progression, "Registering root universe object");
+ for (int i = rootUniverseObject.Children.Count - 1; i >= 0; i--)
{
- IUniverseObject uo = universeObjects[i];
- ProgressionTracker.Set(isTrackingController ? .9f + .1f * ((float)i / universeObjects.Count) : ProgressionTracker.Progression, $"Registering {uo.Name}");
-
- universe.Register(uo);
+ IUniverseObject uo = rootUniverseObject.Children[i];
+ ProgressionTracker.Set(isTrackingController ? .9f + .1f * ((float)i / rootUniverseObject.Children.Count) : ProgressionTracker.Progression, $"Registering {uo.Name}");
+ universe.Root.AddChild(uo);
}
ProgressionTracker.Set(isTrackingController ? 1f : ProgressionTracker.Progression, $"Created {instanceSerializedClass.Type}");
@@ -72,7 +71,7 @@ public class UniverseConverter : EngineTypeYamlSerializerBase
bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
ProgressionTracker.Set(isTrackingController ? .25f : ProgressionTracker.Progression, $"Serializing universe");
- IEnumerable rootUniverseObjects = universe.UniverseObjects.Where(uo => uo.Parent is null);
+ IUniverseObject rootUniverseObject = universe.Root;
emitter.Emit(new MappingStart());
@@ -85,8 +84,8 @@ public class UniverseConverter : EngineTypeYamlSerializerBase
emitter.Emit(new Scalar(nameof(IUniverse.StateEnable)));
serializer(universe.StateEnable);
- emitter.Emit(new Scalar(nameof(IUniverse.UniverseObjects)));
- serializer(rootUniverseObjects);
+ emitter.Emit(new Scalar(nameof(IUniverse.Root)));
+ serializer(rootUniverseObject);
ProgressionTracker.Set(isTrackingController ? 1f : ProgressionTracker.Progression, $"Serialized universe");
emitter.Emit(new MappingEnd());
diff --git a/Engine.Integration/Engine.Integration.Yaml/Converters/UniverseObjectConverter.cs b/Engine.Integration/Engine.Integration.Yaml/Converters/UniverseObjectConverter.cs
index 76268fe..47d8513 100644
--- a/Engine.Integration/Engine.Integration.Yaml/Converters/UniverseObjectConverter.cs
+++ b/Engine.Integration/Engine.Integration.Yaml/Converters/UniverseObjectConverter.cs
@@ -40,7 +40,7 @@ public class UniverseObjectSerializer : EngineTypeYamlSerializerBase().Value.CompareTo(nameof(IUniverseObject.StateEnable)) != 0)
throw new();
diff --git a/Engine.Integration/Engine.Integration.Yaml/YamlSerializer.cs b/Engine.Integration/Engine.Integration.Yaml/YamlSerializer.cs
index 1dfadd1..6df1e67 100644
--- a/Engine.Integration/Engine.Integration.Yaml/YamlSerializer.cs
+++ b/Engine.Integration/Engine.Integration.Yaml/YamlSerializer.cs
@@ -17,14 +17,14 @@ public class YamlSerializer : Core.Serialization.ISerializer
private readonly YamlDotNet.Serialization.ISerializer serializer = null!;
private readonly YamlDotNet.Serialization.IDeserializer deserializer = null!;
- private readonly EntityRegistry entityRegistry = null!;
+ private readonly IdentifiableRegistry identifiableRegistry = null!;
private readonly IProgressionTracker progressionTracker = null!;
private readonly System.Threading.Lock Lock = new();
public YamlSerializer()
{
- entityRegistry = new();
+ identifiableRegistry = new();
progressionTracker = new ProgressionTracker();
SerializerBuilder serializerBuilder = new SerializerBuilder()
@@ -37,7 +37,7 @@ public class YamlSerializer : Core.Serialization.ISerializer
foreach (IEngineTypeYamlConverter typeConverter in GetEngineYamlTypeConverters())
{
typeConverter.Serializer = this;
- typeConverter.EntityRegistry = entityRegistry;
+ typeConverter.IdentifiableRegistry = identifiableRegistry;
typeConverter.ProgressionTracker = progressionTracker;
deserializerBuilder = deserializerBuilder.WithTypeConverter(typeConverter);
@@ -66,9 +66,9 @@ public class YamlSerializer : Core.Serialization.ISerializer
{
lock (Lock)
{
- entityRegistry.Reset();
+ identifiableRegistry.Reset();
object result = deserializer.Deserialize(configuration)!;
- entityRegistry.AssignAll();
+ identifiableRegistry.AssignAll();
return result;
}
}
@@ -77,9 +77,9 @@ public class YamlSerializer : Core.Serialization.ISerializer
{
lock (Lock)
{
- entityRegistry.Reset();
+ identifiableRegistry.Reset();
object result = deserializer.Deserialize(configuration, type)!;
- entityRegistry.AssignAll();
+ identifiableRegistry.AssignAll();
return result;
}
}
@@ -88,9 +88,9 @@ public class YamlSerializer : Core.Serialization.ISerializer
{
lock (Lock)
{
- entityRegistry.Reset();
+ identifiableRegistry.Reset();
T result = deserializer.Deserialize(configuration);
- entityRegistry.AssignAll();
+ identifiableRegistry.AssignAll();
return result;
}
}
diff --git a/Engine.Systems/Network/Abstract/INetworkPacketEncrypted.cs b/Engine.Systems/Network/Abstract/INetworkPacketEncrypted.cs
new file mode 100644
index 0000000..f7c2bb1
--- /dev/null
+++ b/Engine.Systems/Network/Abstract/INetworkPacketEncrypted.cs
@@ -0,0 +1,3 @@
+namespace Engine.Systems.Network;
+
+public interface INetworkPacketEncrypted : INetworkPacket;
diff --git a/Engine.Systems/Network/ClientBehaviour.cs b/Engine.Systems/Network/ClientBehaviour.cs
new file mode 100644
index 0000000..889de6f
--- /dev/null
+++ b/Engine.Systems/Network/ClientBehaviour.cs
@@ -0,0 +1,16 @@
+using Engine.Core;
+
+namespace Engine.Systems.Network;
+
+///
+/// Basic client behaviour that finds the in the universe in it's first active frame.
+///
+/// Disclaimer: It implements and in virtual methods.
+///
+public class ClientBehaviour : Behaviour, IFirstFrameUpdate, ILastFrameUpdate
+{
+ public INetworkCommunicatorClient Client { get; private set; } = null!;
+
+ public virtual void FirstActiveFrame() => Client = Universe.FindRequiredBehaviour();
+ public virtual void LastActiveFrame() => Client = null!;
+}
diff --git a/Engine.Systems/Network/Hasher.cs b/Engine.Systems/Network/Hasher.cs
new file mode 100644
index 0000000..0f881e5
--- /dev/null
+++ b/Engine.Systems/Network/Hasher.cs
@@ -0,0 +1,21 @@
+namespace Engine.Systems.Network;
+
+public static class Hasher
+{
+ public static long FNV1a(string name)
+ {
+ const long fnvPrime = 1099511628211;
+ long result = 1469598103934665603;
+
+ unchecked
+ {
+ foreach (char c in name)
+ {
+ result ^= c;
+ result *= fnvPrime;
+ }
+ }
+
+ return result;
+ }
+}
diff --git a/Engine.Systems/Network/ServerBehaviour.cs b/Engine.Systems/Network/ServerBehaviour.cs
new file mode 100644
index 0000000..9ffe2eb
--- /dev/null
+++ b/Engine.Systems/Network/ServerBehaviour.cs
@@ -0,0 +1,16 @@
+using Engine.Core;
+
+namespace Engine.Systems.Network;
+
+///
+/// Basic server behaviour that finds the in the universe in it's first active frame.
+///
+/// Disclaimer: It implements and in virtual methods.
+///
+public class ServerBehaviour : Behaviour, IFirstFrameUpdate, ILastFrameUpdate
+{
+ public INetworkCommunicatorServer Server { get; private set; } = null!;
+
+ public virtual void FirstActiveFrame() => Server = Universe.FindRequiredBehaviour();
+ public virtual void LastActiveFrame() => Server = null!;
+}
diff --git a/Engine.Systems/Network/TypeHasher.cs b/Engine.Systems/Network/TypeHasher.cs
index 82451d4..58dc572 100644
--- a/Engine.Systems/Network/TypeHasher.cs
+++ b/Engine.Systems/Network/TypeHasher.cs
@@ -8,18 +8,7 @@ public static class TypeHasher
get
{
if (_fnv1a == 0)
- unchecked
- {
- const long fnvPrime = 1099511628211;
- _fnv1a = 1469598103934665603;
-
- string typeName = typeof(T).FullName ?? typeof(T).Name;
- foreach (char c in typeName)
- {
- _fnv1a ^= c;
- _fnv1a *= fnvPrime;
- }
- }
+ _fnv1a = Hasher.FNV1a(typeof(T).FullName ?? typeof(T).Name);
return _fnv1a;
}