Compare commits
	
		
			21 Commits
		
	
	
		
			2ef7fa6577
			...
			7e07cd30db
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 7e07cd30db | |||
| e9ba7867ac | |||
| f28307be80 | |||
| 5f019892f1 | |||
| 0bd2b0618d | |||
| 0691d7092c | |||
| 4ed049573a | |||
| 599774ea8b | |||
| b17f880ecf | |||
| 98bc0693dd | |||
| ac2e160abb | |||
| 30a07dd034 | |||
| f180713f4b | |||
| 2d612ea0d4 | |||
| 5a6883a87f | |||
| a6eb67551d | |||
| 981732ff75 | |||
| 4bdd32b808 | |||
| cac69f5f35 | |||
| 1660915678 | |||
| 1d02b0eba2 | 
@@ -62,7 +62,8 @@ public interface IBehaviourController : IEntity, IHasUniverseObject
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <typeparam name="T">The type of <see cref="IBehaviour"/>s to get.</typeparam>
 | 
			
		||||
    /// <param name="results">The list to store the <see cref="IBehaviour"/>s.</param>
 | 
			
		||||
    void GetBehaviours<T>(IList<T> results);
 | 
			
		||||
    /// <param name="collectionMethod">Whether to clear the <paramref name="results"/> before collection or append the results to the list.</param>
 | 
			
		||||
    void GetBehaviours<T>(IList<T> results, CollectionMethod collectionMethod = CollectionMethod.Clear);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Removes <see cref="IBehaviour"/>s of the specified type from the <see cref="IBehaviourController"/>.
 | 
			
		||||
@@ -78,6 +79,8 @@ public interface IBehaviourController : IEntity, IHasUniverseObject
 | 
			
		||||
    /// <param name="behaviour">The <see cref="IBehaviour"/> to remove.</param>
 | 
			
		||||
    void RemoveBehaviour<T>(T behaviour) where T : class, IBehaviour;
 | 
			
		||||
 | 
			
		||||
    enum CollectionMethod { Clear, Append };
 | 
			
		||||
 | 
			
		||||
    readonly record struct BehaviourAddedArguments(IBehaviour BehaviourAdded);
 | 
			
		||||
    readonly record struct BehaviourRemovedArguments(IBehaviour BehaviourRemoved);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,32 @@ namespace Engine.Core;
 | 
			
		||||
public interface ICamera3D : IBehaviour3D
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Field of View (FOV) value of the camera
 | 
			
		||||
    /// Event triggered when the near plane of the <see cref="ICamera3D"/> changes.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Event<ICamera3D, NearPlaneChangedArguments> OnNearPlaneChanged { get; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Event triggered when the far plane of the <see cref="ICamera3D"/> changes.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Event<ICamera3D, FarPlaneChangedArguments> OnFarPlaneChanged { get; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Event triggered when the field of view of the <see cref="ICamera3D"/> changes.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Event<ICamera3D, FieldOfViewChangedArguments> OnFieldOfViewChanged { get; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Near plane distance of the camera.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    float NearPlane { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Far plane distance of the camera.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    float FarPlane { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Field of View (FOV) value of the camera in degrees.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    float FieldOfView { get; set; }
 | 
			
		||||
 | 
			
		||||
@@ -23,4 +48,8 @@ public interface ICamera3D : IBehaviour3D
 | 
			
		||||
    /// <param name="worldPosition">The position in world coordinates.</param>
 | 
			
		||||
    /// <returns>The position in screen coordinates.</returns>
 | 
			
		||||
    Vector2D WorldToScreenPosition(Vector3D worldPosition);
 | 
			
		||||
 | 
			
		||||
    readonly record struct NearPlaneChangedArguments(float PreviousNearPlane);
 | 
			
		||||
    readonly record struct FarPlaneChangedArguments(float PreviousFarPlane);
 | 
			
		||||
    readonly record struct FieldOfViewChangedArguments(float PreviousFieldOfView);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,18 +3,4 @@ namespace Engine.Core;
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Represents a basic entity in the engine.
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface IEntity : IInitializable, IHasStateEnable
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Event triggered when the <see cref="Id"/> of the <see cref="IEntity"/> changes.
 | 
			
		||||
    /// The string action parameter is the previous <see cref="Id"/> of the <see cref="IEntity"/>. 
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Event<IEntity, IdChangedArguments> OnIdChanged { get; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The ID of the <see cref="IEntity"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    string Id { get; set; }
 | 
			
		||||
 | 
			
		||||
    readonly record struct IdChangedArguments(string PreviousId);
 | 
			
		||||
}
 | 
			
		||||
public interface IEntity : IInitializable, IIdentifiable, IHasStateEnable;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										20
									
								
								Engine.Core/Abstract/IHasId.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								Engine.Core/Abstract/IHasId.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Represents any instance in the engine with an id.
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface IIdentifiable
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Event triggered when the <see cref="Id"/> of the <see cref="IIdentifiable"/> changes.
 | 
			
		||||
    /// The string action parameter is the previous <see cref="Id"/> of the <see cref="IIdentifiable"/>. 
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Event<IIdentifiable, IdChangedArguments> OnIdChanged { get; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The ID of the <see cref="IIdentifiable"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    string Id { get; set; }
 | 
			
		||||
 | 
			
		||||
    readonly record struct IdChangedArguments(string PreviousId);
 | 
			
		||||
}
 | 
			
		||||
@@ -78,9 +78,9 @@ public interface IUniverse : IEntity, IEnumerable<IUniverseObject>
 | 
			
		||||
    UniverseTime UnscaledTime { get; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Gets a read-only list of <see cref="IUniverseObject"/>s managed by the <see cref="IUniverse"/>.
 | 
			
		||||
    /// Gets the root <see cref="IUniverseObject"/> of the <see cref="IUniverse"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    IReadOnlyList<IUniverseObject> UniverseObjects { get; }
 | 
			
		||||
    IUniverseObject Root { get; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Registers an <see cref="IUniverseObject"/> to the <see cref="IUniverse"/>.
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
public abstract class BaseEntity : IEntity
 | 
			
		||||
{
 | 
			
		||||
    public Event<IEntity, IEntity.IdChangedArguments> OnIdChanged { get; } = new();
 | 
			
		||||
    public Event<IIdentifiable, IIdentifiable.IdChangedArguments> OnIdChanged { get; } = new();
 | 
			
		||||
    public Event<IInitializable> OnInitialized { get; } = new();
 | 
			
		||||
    public Event<IInitializable> OnFinalized { get; } = new();
 | 
			
		||||
    public Event<IHasStateEnable> OnStateEnableAssigned { get; } = new();
 | 
			
		||||
 
 | 
			
		||||
@@ -58,9 +58,11 @@ public class BehaviourController : BaseEntity, IBehaviourController
 | 
			
		||||
        return behaviours;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void GetBehaviours<T>(IList<T> results)
 | 
			
		||||
    public void GetBehaviours<T>(IList<T> 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)
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,7 @@ public abstract class ActiveBehaviourCollectorBase<T> : IBehaviourCollector<T> 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<T> : IBehaviourCollector<T> 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);
 | 
			
		||||
 
 | 
			
		||||
@@ -5,9 +5,8 @@ namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
public class ActiveBehaviourCollectorOrdered<TIndex, TItem> : ActiveBehaviourCollector<TItem> where TItem : class, IBehaviour where TIndex : IComparable
 | 
			
		||||
{
 | 
			
		||||
    private readonly Event<IBehaviour, IBehaviour.PriorityChangedArguments>.EventHandler delegateOnPriorityChanged = null!;
 | 
			
		||||
 | 
			
		||||
    private readonly SortedDictionary<TIndex, FastList<TItem>> behaviours = null!;
 | 
			
		||||
    private readonly Dictionary<TItem, TIndex> indexCache = [];
 | 
			
		||||
 | 
			
		||||
    private readonly Func<TItem, TIndex> getIndexFunc = null!;
 | 
			
		||||
    private readonly IComparer<TIndex> sortBy = null!;
 | 
			
		||||
@@ -39,11 +38,10 @@ public class ActiveBehaviourCollectorOrdered<TIndex, TItem> : ActiveBehaviourCol
 | 
			
		||||
 | 
			
		||||
    protected override bool RemoveBehaviour(TItem tBehaviour)
 | 
			
		||||
    {
 | 
			
		||||
        TIndex index = getIndexFunc(tBehaviour);
 | 
			
		||||
        if (!behaviours.TryGetValue(index, out FastList<TItem>? list))
 | 
			
		||||
        if (!indexCache.TryGetValue(tBehaviour, out TIndex? index) || !behaviours.TryGetValue(index, out FastList<TItem>? 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<TIndex, TItem> : 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<TItem, TIndex> getIndexFunc, Comparison<TIndex> sortBy)
 | 
			
		||||
    {
 | 
			
		||||
        delegateOnPriorityChanged = OnPriorityChanged;
 | 
			
		||||
        this.getIndexFunc = getIndexFunc;
 | 
			
		||||
        this.sortBy = Comparer<TIndex>.Create(sortBy);
 | 
			
		||||
        behaviours = new(this.sortBy);
 | 
			
		||||
@@ -80,7 +68,6 @@ public class ActiveBehaviourCollectorOrdered<TIndex, TItem> : ActiveBehaviourCol
 | 
			
		||||
 | 
			
		||||
    public ActiveBehaviourCollectorOrdered(IUniverse universe, Func<TItem, TIndex> getIndexFunc, Comparison<TIndex> sortBy) : base(universe)
 | 
			
		||||
    {
 | 
			
		||||
        delegateOnPriorityChanged = OnPriorityChanged;
 | 
			
		||||
        this.getIndexFunc = getIndexFunc;
 | 
			
		||||
        this.sortBy = Comparer<TIndex>.Create(sortBy);
 | 
			
		||||
        behaviours = new(this.sortBy);
 | 
			
		||||
@@ -89,14 +76,12 @@ public class ActiveBehaviourCollectorOrdered<TIndex, TItem> : ActiveBehaviourCol
 | 
			
		||||
    public ActiveBehaviourCollectorOrdered(Func<TItem, TIndex> getIndexFunc, IComparer<TIndex> sortBy)
 | 
			
		||||
    {
 | 
			
		||||
        this.getIndexFunc = getIndexFunc;
 | 
			
		||||
        delegateOnPriorityChanged = OnPriorityChanged;
 | 
			
		||||
        this.sortBy = sortBy;
 | 
			
		||||
        behaviours = new(sortBy);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ActiveBehaviourCollectorOrdered(IUniverse universe, Func<TItem, TIndex> getIndexFunc, IComparer<TIndex> sortBy) : base(universe)
 | 
			
		||||
    {
 | 
			
		||||
        delegateOnPriorityChanged = OnPriorityChanged;
 | 
			
		||||
        this.getIndexFunc = getIndexFunc;
 | 
			
		||||
        this.sortBy = sortBy;
 | 
			
		||||
        behaviours = new(sortBy);
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@ public abstract class BehaviourCollectorBase<T> : IBehaviourCollector<T> 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<T> : IBehaviourCollector<T> 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);
 | 
			
		||||
 
 | 
			
		||||
@@ -5,9 +5,8 @@ namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
public class BehaviourCollectorOrdered<TIndex, TItem> : BehaviourCollectorBase<TItem> where TItem : class where TIndex : IComparable
 | 
			
		||||
{
 | 
			
		||||
    private readonly Event<IBehaviour, IBehaviour.PriorityChangedArguments>.EventHandler delegateOnPriorityChanged = null!;
 | 
			
		||||
 | 
			
		||||
    private readonly SortedDictionary<TIndex, FastList<TItem>> behaviours = null!;
 | 
			
		||||
    private readonly Dictionary<TItem, TIndex> indexCache = null!;
 | 
			
		||||
 | 
			
		||||
    private readonly Func<TItem, TIndex> getIndexFunc = null!;
 | 
			
		||||
    private readonly IComparer<TIndex> sortBy = null!;
 | 
			
		||||
@@ -39,11 +38,10 @@ public class BehaviourCollectorOrdered<TIndex, TItem> : BehaviourCollectorBase<T
 | 
			
		||||
 | 
			
		||||
    protected override bool RemoveBehaviour(TItem tBehaviour)
 | 
			
		||||
    {
 | 
			
		||||
        TIndex index = getIndexFunc(tBehaviour);
 | 
			
		||||
        if (!behaviours.TryGetValue(index, out FastList<TItem>? list))
 | 
			
		||||
        if (!indexCache.TryGetValue(tBehaviour, out TIndex? index) || !behaviours.TryGetValue(index, out FastList<TItem>? 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<TIndex, TItem> : BehaviourCollectorBase<T
 | 
			
		||||
 | 
			
		||||
        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 BehaviourCollectorOrdered(Func<TItem, TIndex> getIndexFunc, Comparison<TIndex> sortBy)
 | 
			
		||||
    {
 | 
			
		||||
        delegateOnPriorityChanged = OnPriorityChanged;
 | 
			
		||||
        this.getIndexFunc = getIndexFunc;
 | 
			
		||||
        this.sortBy = Comparer<TIndex>.Create(sortBy);
 | 
			
		||||
        behaviours = new(this.sortBy);
 | 
			
		||||
@@ -80,7 +68,6 @@ public class BehaviourCollectorOrdered<TIndex, TItem> : BehaviourCollectorBase<T
 | 
			
		||||
 | 
			
		||||
    public BehaviourCollectorOrdered(IUniverse universe, Func<TItem, TIndex> getIndexFunc, Comparison<TIndex> sortBy) : base(universe)
 | 
			
		||||
    {
 | 
			
		||||
        delegateOnPriorityChanged = OnPriorityChanged;
 | 
			
		||||
        this.getIndexFunc = getIndexFunc;
 | 
			
		||||
        this.sortBy = Comparer<TIndex>.Create(sortBy);
 | 
			
		||||
        behaviours = new(this.sortBy);
 | 
			
		||||
@@ -89,14 +76,12 @@ public class BehaviourCollectorOrdered<TIndex, TItem> : BehaviourCollectorBase<T
 | 
			
		||||
    public BehaviourCollectorOrdered(Func<TItem, TIndex> getIndexFunc, IComparer<TIndex> sortBy)
 | 
			
		||||
    {
 | 
			
		||||
        this.getIndexFunc = getIndexFunc;
 | 
			
		||||
        delegateOnPriorityChanged = OnPriorityChanged;
 | 
			
		||||
        this.sortBy = sortBy;
 | 
			
		||||
        behaviours = new(sortBy);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public BehaviourCollectorOrdered(IUniverse universe, Func<TItem, TIndex> getIndexFunc, IComparer<TIndex> sortBy) : base(universe)
 | 
			
		||||
    {
 | 
			
		||||
        delegateOnPriorityChanged = OnPriorityChanged;
 | 
			
		||||
        this.getIndexFunc = getIndexFunc;
 | 
			
		||||
        this.sortBy = sortBy;
 | 
			
		||||
        behaviours = new(sortBy);
 | 
			
		||||
 
 | 
			
		||||
@@ -100,18 +100,16 @@ public static class BehaviourControllerExtensions
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <typeparam name="T">The type of <see cref="IBehaviour"/>s to get.</typeparam>
 | 
			
		||||
    /// <param name="behavioursInParent">The list to store the <see cref="IBehaviour"/>s.</param>
 | 
			
		||||
    public static void GetBehavioursInParent<T>(this IBehaviourController behaviourController, IList<T> behavioursInParent) where T : class
 | 
			
		||||
    /// <param name="collectionMethod">Whether to clear the <paramref name="behavioursInParent"/> before collection or append the results to the list.</param>
 | 
			
		||||
    public static void GetBehavioursInParent<T>(this IBehaviourController behaviourController, IList<T> behavioursInParent, IBehaviourController.CollectionMethod collectionMethod = IBehaviourController.CollectionMethod.Clear) where T : class
 | 
			
		||||
    {
 | 
			
		||||
        IBehaviourController? controller = behaviourController;
 | 
			
		||||
        List<T> 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
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <typeparam name="T">The type of <see cref="IBehaviour"/>s to get.</typeparam>
 | 
			
		||||
    /// <param name="behavioursInChildren">The list to store the <see cref="IBehaviour"/>s.</param>
 | 
			
		||||
    public static void GetBehavioursInChildren<T>(this IBehaviourController behaviourController, IList<T> behavioursInChildren) where T : class
 | 
			
		||||
    /// <param name="collectionMethod">Whether to clear the <paramref name="behavioursInChildren"/> before collection or append the results to the list.</param>
 | 
			
		||||
    public static void GetBehavioursInChildren<T>(this IBehaviourController behaviourController, IList<T> behavioursInChildren, IBehaviourController.CollectionMethod collectionMethod = IBehaviourController.CollectionMethod.Clear) where T : class
 | 
			
		||||
    {
 | 
			
		||||
        List<T> cache = [];
 | 
			
		||||
        behavioursInChildren.Clear();
 | 
			
		||||
        if (collectionMethod == IBehaviourController.CollectionMethod.Clear)
 | 
			
		||||
            behavioursInChildren.Clear();
 | 
			
		||||
 | 
			
		||||
        TraverseChildrenForBehaviour(behaviourController.UniverseObject, behavioursInChildren, cache);
 | 
			
		||||
        TraverseChildrenForBehaviour(behaviourController.UniverseObject, behavioursInChildren);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void TraverseChildrenForBehaviour<T>(IUniverseObject universeObject, IList<T> behaviours, IList<T> cache) where T : class
 | 
			
		||||
    private static void TraverseChildrenForBehaviour<T>(IUniverseObject universeObject, IList<T> 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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -32,5 +32,5 @@ public static class UniverseExtensions
 | 
			
		||||
    /// <typeparam name="T">Type to be searched through the <see cref="IUniverse"/>.</typeparam>
 | 
			
		||||
    /// <returns>The specified type if found; otherwise, throws <see cref="NotFoundException"/>.</returns>
 | 
			
		||||
    public static T FindRequired<T>(this IUniverse universe) where T : class
 | 
			
		||||
        => universe.Find<T>() ?? 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<T>() ?? throw new NotFoundException($"{universe.GetType().FullName}({universe.Id}) does not contain any {nameof(IUniverseObject)} or {nameof(IBehaviour)} of type {typeof(T).FullName}");
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,21 @@ public static class UniverseObjectExtensions
 | 
			
		||||
        return universeObject;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static IEnumerator<IUniverseObject> TraverseChildren(this IUniverseObject universeObject)
 | 
			
		||||
    {
 | 
			
		||||
        static IEnumerable<IUniverseObject> 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
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Gets a <see cref="IUniverseObject"/> of the specified type.
 | 
			
		||||
 
 | 
			
		||||
@@ -110,6 +110,13 @@ public static class Math
 | 
			
		||||
    /// <returns>The sine of <paramref name="x"/>.</returns>
 | 
			
		||||
    public static float Sin(float x) => MathF.Sin(x);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Returns the tangent of a number.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="x">The angle, in radians.</param>
 | 
			
		||||
    /// <returns>The tangent of <paramref name="x"/>.</returns>
 | 
			
		||||
    public static float Tan(float x) => MathF.Tan(x);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Returns the arccosine of a number.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
@@ -124,6 +131,13 @@ public static class Math
 | 
			
		||||
    /// <returns>The arcsine of <paramref name="x"/>.</returns>
 | 
			
		||||
    public static float Asin(float x) => MathF.Asin(x);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Returns the angle whose tangent is the specified number.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="x">The tangent value.</param>
 | 
			
		||||
    /// <returns>The angle, in radians.</returns>
 | 
			
		||||
    public static float Atan(float x) => MathF.Atan(x);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Returns the angle whose tangent is the quotient of two specified numbers.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -32,12 +32,18 @@ public static class MathExtensions
 | 
			
		||||
    /// <inheritdoc cref="Math.Sin(float)" />
 | 
			
		||||
    public static float Sin(this float x) => Math.Sin(x);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Math.Tan(float)" />
 | 
			
		||||
    public static float Tan(this float x) => Math.Tan(x);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Math.Acos(float)" />
 | 
			
		||||
    public static float Acos(this float x) => Math.Acos(x);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Math.Asin(float)" />
 | 
			
		||||
    public static float Asin(this float x) => Math.Asin(x);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Math.Atan(float)" />
 | 
			
		||||
    public static float Atan(this float x) => Math.Atan(x);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Math.Atan2(float, float)" />
 | 
			
		||||
    public static float Atan2(this float y, float x) => Math.Atan2(y, x);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										294
									
								
								Engine.Core/Primitives/Matrix4x4.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										294
									
								
								Engine.Core/Primitives/Matrix4x4.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,294 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Numerics;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
// TODO Comments
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Represents a 4D left handed space matrix.
 | 
			
		||||
/// </summary>
 | 
			
		||||
/// <remarks>
 | 
			
		||||
/// Initializes a new instance of the <see cref="Matrix4x4"/> struct with the specified values.
 | 
			
		||||
/// </remarks>
 | 
			
		||||
[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<Matrix4x4>
 | 
			
		||||
{
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Extracts the position (translation) from the <see cref="Matrix4x4"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly Vector3D Position => new(M41, M42, M43);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Extracts the scale from the <see cref="Matrix4x4"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Extracts the rotation from the <see cref="Matrix4x4"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly Quaternion Rotation => Quaternion.FromRotationMatrix4x4(this).Normalized;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Represents the identity <see cref="Matrix4x4"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    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
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Calculates the determinant of the <see cref="Matrix4x4"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="m">The <see cref="Matrix4x4"/>.</param>
 | 
			
		||||
    /// <returns>The determinant of the <see cref="Matrix4x4"/>.</returns>
 | 
			
		||||
    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})";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Provides extension methods for <see cref="Matrix4x4"/> type.
 | 
			
		||||
/// </summary>
 | 
			
		||||
public static class Matrix4x4Extensions
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc cref="Matrix4x4.Determinant(Matrix4x4)" />
 | 
			
		||||
    public static float Determinant(this Matrix4x4 matrix) => Matrix4x4.Determinant(matrix);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Matrix4x4.CreateTranslation(Vector3D)" />
 | 
			
		||||
    public static Matrix4x4 ApplyTranslation(this Matrix4x4 matrix, Vector3D translation) => matrix * Matrix4x4.CreateTranslation(translation);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Matrix4x4.CreateScale(Vector3D)" />
 | 
			
		||||
    public static Matrix4x4 ApplyScale(this Matrix4x4 matrix, Vector3 scale) => matrix * Matrix4x4.CreateScale(scale);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Matrix4x4.CreateRotationZ(float)" />
 | 
			
		||||
    public static Matrix4x4 ApplyRotationX(this Matrix4x4 matrix, float radians) => matrix * Matrix4x4.CreateRotationX(radians);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Matrix4x4.CreateRotationY(float)" />
 | 
			
		||||
    public static Matrix4x4 ApplyRotationY(this Matrix4x4 matrix, float radians) => matrix * Matrix4x4.CreateRotationY(radians);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Matrix4x4.CreateRotationZ(float)" />
 | 
			
		||||
    public static Matrix4x4 ApplyRotationZ(this Matrix4x4 matrix, float radians) => matrix * Matrix4x4.CreateRotationZ(radians);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Matrix4x4.CreateRotation(Quater)" />
 | 
			
		||||
    public static Matrix4x4 ApplyRotation(this Matrix4x4 matrix, Quaternion quaternion) => matrix * Matrix4x4.CreateRotation(quaternion);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Matrix4x4.CreateLookMatrix( Vector3D, Vector3D)" />
 | 
			
		||||
    public static Matrix4x4 ApplyLookRotationTo(this Matrix4x4 matrix, Vector3D forward, Vector3 up) => matrix * Matrix4x4.CreateLookMatrix(forward, up);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Matrix4x4.CreateLookMatrix(Vector3D, Vector3D, Vector3D)" />
 | 
			
		||||
    public static Matrix4x4 CreateLookMatrixTo(this Vector3D from, Vector3D to, Vector3 up) => Matrix4x4.CreateLookMatrix(from, to, up);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Matrix4x4.CreatePerspectiveFieldOfView(float, float, float, float)" />
 | 
			
		||||
    public static Matrix4x4 ApplyPerspectiveFieldOfView(this Matrix4x4 matrix, float fieldOfViewInRadians, float aspectRatio, float nearPlane, float farPlane)
 | 
			
		||||
        => matrix * Matrix4x4.CreatePerspectiveFieldOfView(fieldOfViewInRadians, aspectRatio, nearPlane, farPlane);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Matrix4x4.ToRightHanded(Matrix4x4) />
 | 
			
		||||
    public static Matrix4x4 ToRightHanded(this Matrix4x4 matrix) => Matrix4x4.ToRightHanded(matrix);
 | 
			
		||||
}
 | 
			
		||||
@@ -156,6 +156,27 @@ public readonly struct Quaternion(float x, float y, float z, float w) : IEquatab
 | 
			
		||||
    /// <returns>The normalized <see cref="Quaternion"/>.</returns>
 | 
			
		||||
    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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Rotates a <see cref="Quaternion"/> around a axis by the specified angle (in radians).
 | 
			
		||||
    /// </summary>
 | 
			
		||||
@@ -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
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Calculates the <see cref="System.Numerics.Matrix4x4"/> from given <see cref="Quaternion"/>.
 | 
			
		||||
    /// Calculates the <see cref="Matrix4x4"/> from given <see cref="Quaternion"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="axis">The axis of the rotation in <see cref="Vector3D"/>.</param>
 | 
			
		||||
    /// <param name="angle">The angle in radians.</param>
 | 
			
		||||
    /// <returns>The rotation <see cref="System.Numerics.Matrix4x4"/> calculated by the given <see cref="Quaternion"/>.</returns>
 | 
			
		||||
    public static System.Numerics.Matrix4x4 ToRotationMatrix4x4(Quaternion quaternion)
 | 
			
		||||
    /// <param name="quaternion">The rotation <see cref="Quaternion"/>.</param>
 | 
			
		||||
    /// <returns>The rotation <see cref="Matrix4x4"/> calculated by the given <see cref="Quaternion"/>.</returns>
 | 
			
		||||
    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
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Calculates the <see cref="Quaternion"/> from given <see cref="Matrix4x4"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="martix">The rotation <see cref="Matrix4x4"/>.</param>
 | 
			
		||||
    /// <returns>The rotation <see cref="Quaternion"/> calculated by the given <see cref="Matrix4x4"/>.</returns>
 | 
			
		||||
    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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Checks if two <see cref="Quaternion"/>s are approximately equal within a specified epsilon range.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
@@ -393,7 +459,7 @@ public static class QuaternionExtensions
 | 
			
		||||
    public static float Dot(this Quaternion left, Quaternion right) => Quaternion.Dot(left, right);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Quaternion.ToRotationMatrix4x4(Quaternion, Quaternion)" />
 | 
			
		||||
    public static System.Numerics.Matrix4x4 ToRotationMatrix4x4(this Quaternion quaternion) => Quaternion.ToRotationMatrix4x4(quaternion);
 | 
			
		||||
    public static Matrix4x4 ToRotationMatrix4x4(this Quaternion quaternion) => Quaternion.ToRotationMatrix4x4(quaternion);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Quaternion.FromAxisAngle(Vector3D, float)" />
 | 
			
		||||
    public static Quaternion CreateRotation(this Vector3D axis, float angle) => Quaternion.FromAxisAngle(axis, angle);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,39 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core.Serialization;
 | 
			
		||||
 | 
			
		||||
public class EntityRegistry
 | 
			
		||||
{
 | 
			
		||||
    public Event<EntityRegistry, EntityRegisteredArguments> OnEntityRegistered = null!;
 | 
			
		||||
 | 
			
		||||
    private readonly Dictionary<string, Action<IEntity>?> assignCallbacks = [];
 | 
			
		||||
    private readonly Dictionary<string, IEntity> registeredEntities = [];
 | 
			
		||||
    public IReadOnlyDictionary<string, IEntity> RegisteredEntities => registeredEntities;
 | 
			
		||||
 | 
			
		||||
    public void Add(IEntity entity)
 | 
			
		||||
    {
 | 
			
		||||
        if (registeredEntities.TryAdd(entity.Id, entity))
 | 
			
		||||
            OnEntityRegistered?.Invoke(this, new(entity));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void QueueAssign(string id, Action<IEntity> setMethod)
 | 
			
		||||
    {
 | 
			
		||||
        assignCallbacks.TryAdd(id, null);
 | 
			
		||||
        assignCallbacks[id] = assignCallbacks[id] + setMethod;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void AssignAll()
 | 
			
		||||
    {
 | 
			
		||||
        foreach ((string id, Action<IEntity>? action) in assignCallbacks)
 | 
			
		||||
            action?.Invoke(registeredEntities[id]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Reset()
 | 
			
		||||
    {
 | 
			
		||||
        assignCallbacks.Clear();
 | 
			
		||||
        registeredEntities.Clear();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public readonly record struct EntityRegisteredArguments(IEntity Entity);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										39
									
								
								Engine.Core/Serialization/IdentifiableRegistry.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								Engine.Core/Serialization/IdentifiableRegistry.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core.Serialization;
 | 
			
		||||
 | 
			
		||||
public class IdentifiableRegistry
 | 
			
		||||
{
 | 
			
		||||
    public Event<IdentifiableRegistry, EntityRegisteredArguments> OnEntityRegistered = null!;
 | 
			
		||||
 | 
			
		||||
    private readonly Dictionary<string, Action<IIdentifiable>?> assignCallbacks = [];
 | 
			
		||||
    private readonly Dictionary<string, IIdentifiable> registeredEntities = [];
 | 
			
		||||
    public IReadOnlyDictionary<string, IIdentifiable> RegisteredEntities => registeredEntities;
 | 
			
		||||
 | 
			
		||||
    public void Add(IIdentifiable identifiable)
 | 
			
		||||
    {
 | 
			
		||||
        if (registeredEntities.TryAdd(identifiable.Id, identifiable))
 | 
			
		||||
            OnEntityRegistered?.Invoke(this, new(identifiable));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void QueueAssign(string id, Action<IIdentifiable> setMethod)
 | 
			
		||||
    {
 | 
			
		||||
        assignCallbacks.TryAdd(id, null);
 | 
			
		||||
        assignCallbacks[id] = assignCallbacks[id] + setMethod;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void AssignAll()
 | 
			
		||||
    {
 | 
			
		||||
        foreach ((string id, Action<IIdentifiable>? action) in assignCallbacks)
 | 
			
		||||
            action?.Invoke(registeredEntities[id]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Reset()
 | 
			
		||||
    {
 | 
			
		||||
        assignCallbacks.Clear();
 | 
			
		||||
        registeredEntities.Clear();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public readonly record struct EntityRegisteredArguments(IIdentifiable Entity);
 | 
			
		||||
}
 | 
			
		||||
@@ -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);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,6 @@ public class UniverseEntranceManager : Internal.BehaviourIndependent
 | 
			
		||||
    private static System.Func<IBehaviour, int> GetPriority() => (b) => b.Priority;
 | 
			
		||||
 | 
			
		||||
    private readonly ActiveBehaviourCollectorOrdered<int, IEnterUniverse> enterUniverses = new(GetPriority(), SortByAscendingPriority());
 | 
			
		||||
    private readonly ActiveBehaviourCollectorOrdered<int, IExitUniverse> exitUniverses = new(GetPriority(), SortByAscendingPriority());
 | 
			
		||||
 | 
			
		||||
    private readonly List<IEnterUniverse> toCallEnterUniverses = new(32);
 | 
			
		||||
    private readonly List<IExitUniverse> 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<IExitUniverse> sender, IBehaviourCollector<IExitUniverse>.BehaviourCollectedArguments args)
 | 
			
		||||
    {
 | 
			
		||||
        toCallExitUniverses.Add(args.BehaviourCollected);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public UniverseEntranceManager()
 | 
			
		||||
    {
 | 
			
		||||
        enterUniverses.OnCollected.AddListener(OnEnterUniverseCollected);
 | 
			
		||||
        exitUniverses.OnCollected.AddListener(OnExitUniverseCollected);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -31,7 +31,7 @@ public class Universe : BaseEntity, IUniverse
 | 
			
		||||
        delegateOnUniverseObjectExitedUniverse = OnUniverseObjectExitedUniverse;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public IReadOnlyList<IUniverseObject> 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<IUniverseObject> GetEnumerator() => _universeObjects.GetEnumerator();
 | 
			
		||||
    IEnumerator IEnumerable.GetEnumerator() => _universeObjects.GetEnumerator();
 | 
			
		||||
    public IEnumerator<IUniverseObject> GetEnumerator() => Root.TraverseChildren();
 | 
			
		||||
    IEnumerator IEnumerable.GetEnumerator() => Root.TraverseChildren();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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>(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)
 | 
			
		||||
        {
 | 
			
		||||
 
 | 
			
		||||
@@ -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<Type, Event<IConnection, object>> listeners = [];
 | 
			
		||||
    private readonly Dictionary<string, IConnection> _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); }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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<T>(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;
 | 
			
		||||
 
 | 
			
		||||
@@ -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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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<MonoGameCamera2D> OnMatrixTransformChanged { get; } = new();
 | 
			
		||||
    public Event<MonoGameCamera2D> 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<MonoGameWindowContainer>().Window.Graphics;
 | 
			
		||||
        Viewport = Graphics.GraphicsDevice.Viewport;
 | 
			
		||||
        Transform = BehaviourController.GetRequiredBehaviour<ITransform2D>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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<ITransform2D>();
 | 
			
		||||
    protected sealed override void FinalizeInternal() => Transform = null!;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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<MonoGameCamera3D, ViewChangedArguments> OnViewChanged { get; } = new();
 | 
			
		||||
    public Event<MonoGameCamera3D, ProjectionChangedArguments> OnProjectionChanged { get; } = new();
 | 
			
		||||
    public Event<MonoGameCamera3D, ViewportChangedArguments> OnViewportChanged { get; } = new();
 | 
			
		||||
    public Event<MonoGameCamera3D, FieldOfViewChangedArguments> OnFieldOfViewChanged { get; } = new();
 | 
			
		||||
 | 
			
		||||
    public Event<ICamera3D, ICamera3D.NearPlaneChangedArguments> OnNearPlaneChanged { get; } = new();
 | 
			
		||||
    public Event<ICamera3D, ICamera3D.FarPlaneChangedArguments> OnFarPlaneChanged { get; } = new();
 | 
			
		||||
    public Event<ICamera3D, ICamera3D.FieldOfViewChangedArguments> 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<ITransform3D>();
 | 
			
		||||
        Graphics = BehaviourController.UniverseObject.Universe.FindRequiredBehaviour<MonoGameWindowContainer>().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<ITransform3D>();
 | 
			
		||||
    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);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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<MonoGameWindowContainer>(this);
 | 
			
		||||
 | 
			
		||||
        Universe.InstantiateUniverseObject().SetUniverseObject("Content Loader")
 | 
			
		||||
        Universe.InstantiateUniverseObject().SetUniverseObject("Content Loader", monogameParent)
 | 
			
		||||
            .BehaviourController.AddBehaviour<LoadContentManager>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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; }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -36,7 +36,7 @@ public class BehaviourControllerConverter : EngineTypeYamlSerializerBase<IBehavi
 | 
			
		||||
            throw new();
 | 
			
		||||
        SerializedClass instanceSerializedClass = (SerializedClass)rootDeserializer(typeof(SerializedClass))!;
 | 
			
		||||
        ProgressionTracker.Set(isTrackingController ? .5f : ProgressionTracker.Progression, $"Creating {instanceSerializedClass.Type}");
 | 
			
		||||
        behaviourController = (IBehaviourController)instanceSerializedClass.CreateInstance(EntityRegistry);
 | 
			
		||||
        behaviourController = (IBehaviourController)instanceSerializedClass.CreateInstance(IdentifiableRegistry);
 | 
			
		||||
 | 
			
		||||
        string value = parser.Consume<Scalar>().Value;
 | 
			
		||||
        if (value.CompareTo(nameof(IBehaviourController.StateEnable)) != 0)
 | 
			
		||||
 
 | 
			
		||||
@@ -36,7 +36,7 @@ public class BehaviourConverter : EngineTypeYamlSerializerBase<IBehaviour>
 | 
			
		||||
            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<Scalar>().Value.CompareTo(nameof(IBehaviour.StateEnable)) != 0)
 | 
			
		||||
            throw new();
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@ public abstract class EngineTypeYamlSerializerBase<T> : 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<T> : IEngineTypeYamlConverter
 | 
			
		||||
        T? result = Read(parser, type, rootDeserializer);
 | 
			
		||||
 | 
			
		||||
        if (result is IEntity entity)
 | 
			
		||||
            EntityRegistry.Add(entity);
 | 
			
		||||
            IdentifiableRegistry.Add(entity);
 | 
			
		||||
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,7 @@ public class StateEnableConverter : EngineTypeYamlSerializerBase<IStateEnable>
 | 
			
		||||
        if (parser.Consume<Scalar>().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<MappingEnd>();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@ public class UniverseConverter : EngineTypeYamlSerializerBase<IUniverse>
 | 
			
		||||
 | 
			
		||||
        IUniverse universe;
 | 
			
		||||
        IStateEnable stateEnable;
 | 
			
		||||
        List<IUniverseObject> universeObjects;
 | 
			
		||||
        IUniverseObject rootUniverseObject;
 | 
			
		||||
 | 
			
		||||
        parser.Consume<MappingStart>();
 | 
			
		||||
 | 
			
		||||
@@ -34,16 +34,16 @@ public class UniverseConverter : EngineTypeYamlSerializerBase<IUniverse>
 | 
			
		||||
            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<Scalar>().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<Scalar>().Value.CompareTo(nameof(IUniverse.UniverseObjects)) != 0)
 | 
			
		||||
        if (parser.Consume<Scalar>().Value.CompareTo(nameof(IUniverse.Root)) != 0)
 | 
			
		||||
            throw new();
 | 
			
		||||
        universeObjects = (List<IUniverseObject>)rootDeserializer(typeof(List<IUniverseObject>))!;
 | 
			
		||||
        rootUniverseObject = (IUniverseObject)rootDeserializer(typeof(IUniverseObject))!;
 | 
			
		||||
 | 
			
		||||
        parser.Consume<MappingEnd>();
 | 
			
		||||
 | 
			
		||||
@@ -52,13 +52,12 @@ public class UniverseConverter : EngineTypeYamlSerializerBase<IUniverse>
 | 
			
		||||
        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<IUniverse>
 | 
			
		||||
        bool isTrackingController = ProgressionTracker.Progression.ApproximatelyEquals(0f);
 | 
			
		||||
        ProgressionTracker.Set(isTrackingController ? .25f : ProgressionTracker.Progression, $"Serializing universe");
 | 
			
		||||
 | 
			
		||||
        IEnumerable<IUniverseObject> 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<IUniverse>
 | 
			
		||||
        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());
 | 
			
		||||
 
 | 
			
		||||
@@ -40,7 +40,7 @@ public class UniverseObjectSerializer : EngineTypeYamlSerializerBase<IUniverseOb
 | 
			
		||||
            throw new();
 | 
			
		||||
        SerializedClass instanceSerializedClass = (SerializedClass)rootDeserializer(typeof(SerializedClass))!;
 | 
			
		||||
        ProgressionTracker.Set(isTrackingController ? .3f : ProgressionTracker.Progression, $"Creating {instanceSerializedClass.Type}");
 | 
			
		||||
        universeObject = (IUniverseObject)instanceSerializedClass.CreateInstance(EntityRegistry);
 | 
			
		||||
        universeObject = (IUniverseObject)instanceSerializedClass.CreateInstance(IdentifiableRegistry);
 | 
			
		||||
 | 
			
		||||
        if (parser.Consume<Scalar>().Value.CompareTo(nameof(IUniverseObject.StateEnable)) != 0)
 | 
			
		||||
            throw new();
 | 
			
		||||
 
 | 
			
		||||
@@ -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<T>(configuration);
 | 
			
		||||
            entityRegistry.AssignAll();
 | 
			
		||||
            identifiableRegistry.AssignAll();
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,3 @@
 | 
			
		||||
namespace Engine.Systems.Network;
 | 
			
		||||
 | 
			
		||||
public interface INetworkPacketEncrypted : INetworkPacket;
 | 
			
		||||
							
								
								
									
										16
									
								
								Engine.Systems/Network/ClientBehaviour.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								Engine.Systems/Network/ClientBehaviour.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
using Engine.Core;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Systems.Network;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Basic client behaviour that finds the <see cref="INetworkCommunicatorClient"/> in the universe in it's first active frame.
 | 
			
		||||
/// <br/> 
 | 
			
		||||
/// Disclaimer: It implements <see cref="IFirstFrameUpdate"/> and <see cref="ILastFrameUpdate"/> in virtual methods.
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class ClientBehaviour : Behaviour, IFirstFrameUpdate, ILastFrameUpdate
 | 
			
		||||
{
 | 
			
		||||
    public INetworkCommunicatorClient Client { get; private set; } = null!;
 | 
			
		||||
 | 
			
		||||
    public virtual void FirstActiveFrame() => Client = Universe.FindRequiredBehaviour<INetworkCommunicatorClient>();
 | 
			
		||||
    public virtual void LastActiveFrame() => Client = null!;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										21
									
								
								Engine.Systems/Network/Hasher.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								Engine.Systems/Network/Hasher.cs
									
									
									
									
									
										Normal file
									
								
							@@ -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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								Engine.Systems/Network/ServerBehaviour.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								Engine.Systems/Network/ServerBehaviour.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
using Engine.Core;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Systems.Network;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Basic server behaviour that finds the <see cref="INetworkCommunicatorServer"/> in the universe in it's first active frame.
 | 
			
		||||
/// <br/> 
 | 
			
		||||
/// Disclaimer: It implements <see cref="IFirstFrameUpdate"/> and <see cref="ILastFrameUpdate"/> in virtual methods.
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class ServerBehaviour : Behaviour, IFirstFrameUpdate, ILastFrameUpdate
 | 
			
		||||
{
 | 
			
		||||
    public INetworkCommunicatorServer Server { get; private set; } = null!;
 | 
			
		||||
 | 
			
		||||
    public virtual void FirstActiveFrame() => Server = Universe.FindRequiredBehaviour<INetworkCommunicatorServer>();
 | 
			
		||||
    public virtual void LastActiveFrame() => Server = null!;
 | 
			
		||||
}
 | 
			
		||||
@@ -8,18 +8,7 @@ public static class TypeHasher<T>
 | 
			
		||||
        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;
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user