Compare commits
	
		
			87 Commits
		
	
	
		
			efed24de20
			...
			developmen
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 7e07cd30db | |||
| e9ba7867ac | |||
| f28307be80 | |||
| 5f019892f1 | |||
| 0bd2b0618d | |||
| 0691d7092c | |||
| 4ed049573a | |||
| 599774ea8b | |||
| b17f880ecf | |||
| 98bc0693dd | |||
| ac2e160abb | |||
| 30a07dd034 | |||
| f180713f4b | |||
| 2d612ea0d4 | |||
| 5a6883a87f | |||
| a6eb67551d | |||
| 981732ff75 | |||
| 4bdd32b808 | |||
| cac69f5f35 | |||
| 1660915678 | |||
| 1d02b0eba2 | |||
| 2ef7fa6577 | |||
| 193b2c5af0 | |||
| a859c0cbff | |||
| fba64c3854 | |||
| a975cbb56b | |||
| 1664a9ccf7 | |||
| 988a6f67f2 | |||
| 2f32038f04 | |||
| 4b756fa232 | |||
| 0db2cae1bb | |||
| 896f7876c1 | |||
| 56d3112e35 | |||
| 32ec2325dc | |||
| b42f1f1881 | |||
| f8096377b2 | |||
| a9fc819268 | |||
| 1d63391975 | |||
| 61ff0887e2 | |||
| 16344dccc7 | |||
| dc4bea3eef | |||
| d1b2723a70 | |||
| 2f5c04e66b | |||
| f753da1f87 | |||
| 6901159106 | |||
| 7469c9ab0c | |||
| ede90adbdc | |||
| eeaca3a6c7 | |||
| 3b984a0a4b | |||
| c5afb70b18 | |||
| 9d2a192f04 | |||
| 598debc233 | |||
| ab05a89175 | |||
| dbd15cbbc2 | |||
| e051f5cfb4 | |||
| e70b7f112f | |||
| f55ba499b6 | |||
| b75f30f864 | |||
| 6f1f30bd53 | |||
| 92a5c276a4 | |||
| 69bc6573d1 | |||
| 28bc022587 | |||
| 25db60e436 | |||
| 7c62440bba | |||
| 4bec7bce6e | |||
| 8d31372c24 | |||
| a2e704916e | |||
| c7d170fad9 | |||
| 9ccf7b754d | |||
| e3d4899112 | |||
| 566c16d09c | |||
| ae9d4f02ef | |||
| e77772cbc2 | |||
| 4c542df401 | |||
| 28ca343b43 | |||
| 651b0614c4 | |||
| f47488c6f1 | |||
| 6d159330a1 | |||
| 8e314f3269 | |||
| f5a7077570 | |||
| 746d29fb7a | |||
| cf68f6ca6f | |||
| a4b83679b1 | |||
| a31b39fd1d | |||
| 0205354202 | |||
| 949dfeb3d9 | |||
| 620ef911fa | 
							
								
								
									
										2
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							@@ -1,3 +1,3 @@
 | 
			
		||||
[submodule "Engine.Integration/YamlDotNet"]
 | 
			
		||||
	path = Engine.Integration/YamlDotNet
 | 
			
		||||
	url = git@github.com:Syntriax/YamlDotNet.git
 | 
			
		||||
	url = https://github.com/Syntriax/YamlDotNet.git
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								Engine.Core/Abstract/IBehaviour3D.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								Engine.Core/Abstract/IBehaviour3D.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
public interface IBehaviour3D : IBehaviour
 | 
			
		||||
{
 | 
			
		||||
    ITransform3D Transform { get; }
 | 
			
		||||
}
 | 
			
		||||
@@ -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);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										55
									
								
								Engine.Core/Abstract/ICamera3D.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								Engine.Core/Abstract/ICamera3D.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Represents a 3D camera in the engine.
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface ICamera3D : IBehaviour3D
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 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; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Converts a position from screen coordinates to a <see cref="Ray3D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="screenPosition">The position in screen coordinates.</param>
 | 
			
		||||
    /// <returns>The <see cref="Ray3D"/> originating from the camera to the screen position in world coordinates.</returns>
 | 
			
		||||
    Ray3D ScreenToWorldRay(Vector2D screenPosition);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Converts a position from world coordinates to screen coordinates.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <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);
 | 
			
		||||
}
 | 
			
		||||
@@ -16,10 +16,15 @@ public interface ITransform2D : IBehaviour
 | 
			
		||||
    Event<ITransform2D, ScaleChangedArguments> OnScaleChanged { get; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Event triggered when the <see cref="Rotation"/> of the <see cref="ITransform"/> changes.
 | 
			
		||||
    /// Event triggered when the <see cref="Rotation"/> of the <see cref="ITransform2D"/> changes.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Event<ITransform2D, RotationChangedArguments> OnRotationChanged { get; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Event triggered when any of the properties of the <see cref="ITransform2D"/> gets updated.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Event<ITransform2D> OnTransformUpdated { get; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The <see cref="Vector2D"/> pointing upwards in world space.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										105
									
								
								Engine.Core/Abstract/ITransform3D.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								Engine.Core/Abstract/ITransform3D.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,105 @@
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Represents the transformation properties of an object such as position, scale, and rotation in 3D space.
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface ITransform3D : IBehaviour
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Event triggered when the <see cref="Position"/> of the <see cref="ITransform3D"/> changes.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Event<ITransform3D, PositionChangedArguments> OnPositionChanged { get; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Event triggered when the <see cref="Scale"/> of the <see cref="ITransform3D"/> changes.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Event<ITransform3D, ScaleChangedArguments> OnScaleChanged { get; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Event triggered when the <see cref="Rotation"/> of the <see cref="ITransform3D"/> changes.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Event<ITransform3D, RotationChangedArguments> OnRotationChanged { get; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Event triggered when any of the properties of the <see cref="ITransform3D"/> gets updated.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Event<ITransform3D> OnTransformUpdated { get; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The <see cref="Vector3D"/> pointing upwards in world space.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Vector3D Up { get; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The <see cref="Vector3D"/> pointing upwards in world space.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Vector3D Down { get; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The <see cref="Vector3D"/> pointing upwards in world space.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Vector3D Left { get; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The <see cref="Vector3D"/> pointing upwards in world space.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Vector3D Right { get; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The <see cref="Vector3D"/> pointing forwards in world space.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Vector3D Forward { get; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The <see cref="Vector3D"/> pointing backwards in world space.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Vector3D Backward { get; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The world position of the <see cref="ITransform3D"/> in 3D space.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Vector3D Position { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The world scale of the <see cref="ITransform3D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Vector3D Scale { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The world rotation of the <see cref="ITransform3D"/> in degrees.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Quaternion Rotation { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The local position of the <see cref="ITransform3D"/> in 3D space.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Vector3D LocalPosition { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The local scale of the <see cref="ITransform3D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Vector3D LocalScale { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The local rotation of the <see cref="ITransform3D"/> in degrees.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Quaternion LocalRotation { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Arguments for the event triggered when the <see cref="ITransform3D"/>'s rotation changes.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="PreviousPosition">The previous <see cref="Position"/> of the <see cref="ITransform3D"/>.</param>
 | 
			
		||||
    readonly record struct PositionChangedArguments(Vector3D PreviousPosition);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Arguments for the event triggered when the <see cref="ITransform3D"/>'s rotation changes.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="PreviousScale">The previous <see cref="Scale"/> of the <see cref="ITransform3D"/>.</param>
 | 
			
		||||
    readonly record struct ScaleChangedArguments(Vector3D PreviousScale);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Arguments for the event triggered when the <see cref="ITransform3D"/>'s rotation changes.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="PreviousRotation">The previous <see cref="Rotation"/> of the <see cref="ITransform3D"/>.</param>
 | 
			
		||||
    readonly record struct RotationChangedArguments(Quaternion PreviousRotation);
 | 
			
		||||
}
 | 
			
		||||
@@ -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"/>.
 | 
			
		||||
 
 | 
			
		||||
@@ -1,67 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
public class ActiveBehaviourCollectorSorted<T> : ActiveBehaviourCollector<T> where T : class, IBehaviour
 | 
			
		||||
{
 | 
			
		||||
    private readonly Event<IBehaviour, IBehaviour.PriorityChangedArguments>.EventHandler delegateOnPriorityChanged = null!;
 | 
			
		||||
 | 
			
		||||
    private IComparer<T>? _sortBy = null;
 | 
			
		||||
    public IComparer<T>? SortBy
 | 
			
		||||
    {
 | 
			
		||||
        get => _sortBy;
 | 
			
		||||
        set
 | 
			
		||||
        {
 | 
			
		||||
            _sortBy = value;
 | 
			
		||||
 | 
			
		||||
            if (value is not null)
 | 
			
		||||
                activeBehaviours.Sort(value);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void AddBehaviour(T behaviour)
 | 
			
		||||
    {
 | 
			
		||||
        if (SortBy is null)
 | 
			
		||||
        {
 | 
			
		||||
            activeBehaviours.Add(behaviour);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int insertionIndex = activeBehaviours.BinarySearch(behaviour, SortBy);
 | 
			
		||||
 | 
			
		||||
        if (insertionIndex < 0)
 | 
			
		||||
            insertionIndex = ~insertionIndex;
 | 
			
		||||
 | 
			
		||||
        activeBehaviours.Insert(insertionIndex, 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)
 | 
			
		||||
    {
 | 
			
		||||
        T behaviour = (T)sender;
 | 
			
		||||
        activeBehaviours.Remove(behaviour);
 | 
			
		||||
        AddBehaviour(behaviour);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ActiveBehaviourCollectorSorted()
 | 
			
		||||
    {
 | 
			
		||||
        delegateOnPriorityChanged = OnPriorityChanged;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ActiveBehaviourCollectorSorted(IUniverse universe, Comparison<T> sortBy) : base(universe)
 | 
			
		||||
    {
 | 
			
		||||
        delegateOnPriorityChanged = OnPriorityChanged;
 | 
			
		||||
 | 
			
		||||
        SortBy = Comparer<T>.Create(sortBy);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,50 +1,103 @@
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
public abstract class Behaviour : BehaviourBase
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("{GetType().Name, nq}, Priority: {Priority}, Initialized: {Initialized}")]
 | 
			
		||||
public abstract class Behaviour : BaseEntity, IBehaviour
 | 
			
		||||
{
 | 
			
		||||
    private readonly Event<IUniverseObject, IUniverseObject.EnteredUniverseArguments>.EventHandler delegateEnteredUniverse = null!;
 | 
			
		||||
    private readonly Event<IUniverseObject, IUniverseObject.ExitedUniverseArguments>.EventHandler delegateExitedUniverse = null!;
 | 
			
		||||
    public Event<IBehaviour, IBehaviour.PriorityChangedArguments> OnPriorityChanged { get; } = new();
 | 
			
		||||
    public Event<IActive, IActive.ActiveChangedArguments> OnActiveChanged { get; } = new();
 | 
			
		||||
    public Event<IHasBehaviourController> OnBehaviourControllerAssigned { get; } = new();
 | 
			
		||||
 | 
			
		||||
    public Behaviour()
 | 
			
		||||
    private readonly Event<IHasUniverseObject>.EventHandler delegateOnUniverseObjectAssigned = null!;
 | 
			
		||||
    private readonly Event<IActive, IActive.ActiveChangedArguments>.EventHandler delegateOnUniverseObjectActiveChanged = null!;
 | 
			
		||||
    private readonly Event<IStateEnable, IStateEnable.EnabledChangedArguments>.EventHandler delegateOnStateEnabledChanged = null!;
 | 
			
		||||
 | 
			
		||||
    public IUniverse Universe => BehaviourController.UniverseObject.Universe;
 | 
			
		||||
    public IUniverseObject UniverseObject => BehaviourController.UniverseObject;
 | 
			
		||||
 | 
			
		||||
    private IBehaviourController _behaviourController = null!;
 | 
			
		||||
    public IBehaviourController BehaviourController => _behaviourController;
 | 
			
		||||
 | 
			
		||||
    private int _priority = 0;
 | 
			
		||||
    public int Priority
 | 
			
		||||
    {
 | 
			
		||||
        OnInitialized.AddListener(OnInitialize);
 | 
			
		||||
        OnFinalized.AddListener(OnFinalize);
 | 
			
		||||
        OnUnassigned.AddListener(OnUnassign);
 | 
			
		||||
        get => _priority;
 | 
			
		||||
        set
 | 
			
		||||
        {
 | 
			
		||||
            if (value == _priority)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
        delegateEnteredUniverse = EnteredUniverse;
 | 
			
		||||
        delegateExitedUniverse = ExitedUniverse;
 | 
			
		||||
            int previousPriority = _priority;
 | 
			
		||||
            _priority = value;
 | 
			
		||||
            OnPriorityChanged?.Invoke(this, new(previousPriority));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected virtual void OnUnassign() { }
 | 
			
		||||
    protected void OnUnassign(IAssignable assignable) => OnUnassign();
 | 
			
		||||
    private bool _isActive = false;
 | 
			
		||||
    public bool IsActive => _isActive;
 | 
			
		||||
 | 
			
		||||
    protected virtual void OnInitialize() { }
 | 
			
		||||
    protected void OnInitialize(IInitializable _)
 | 
			
		||||
    protected virtual void OnAssign(IBehaviourController behaviourController) { }
 | 
			
		||||
    public bool Assign(IBehaviourController behaviourController)
 | 
			
		||||
    {
 | 
			
		||||
        BehaviourController.UniverseObject.OnEnteredUniverse.AddListener(delegateEnteredUniverse);
 | 
			
		||||
        BehaviourController.UniverseObject.OnExitedUniverse.AddListener(delegateExitedUniverse);
 | 
			
		||||
        if (IsInitialized)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        OnInitialize();
 | 
			
		||||
 | 
			
		||||
        if (UniverseObject.IsInUniverse)
 | 
			
		||||
            EnteredUniverse(UniverseObject, new(Universe));
 | 
			
		||||
        _behaviourController = behaviourController;
 | 
			
		||||
        OnAssign(behaviourController);
 | 
			
		||||
        behaviourController.OnUniverseObjectAssigned.AddListener(delegateOnUniverseObjectAssigned);
 | 
			
		||||
        behaviourController.StateEnable.OnEnabledChanged.AddListener(delegateOnStateEnabledChanged);
 | 
			
		||||
        if (behaviourController.UniverseObject is not null)
 | 
			
		||||
            OnUniverseObjectAssigned(behaviourController);
 | 
			
		||||
        OnBehaviourControllerAssigned?.Invoke(this);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected virtual void OnFinalize() { }
 | 
			
		||||
    protected void OnFinalize(IInitializable _)
 | 
			
		||||
    private void OnUniverseObjectAssigned(IHasUniverseObject sender)
 | 
			
		||||
    {
 | 
			
		||||
        BehaviourController.UniverseObject.OnEnteredUniverse.RemoveListener(delegateEnteredUniverse);
 | 
			
		||||
        BehaviourController.UniverseObject.OnExitedUniverse.RemoveListener(delegateExitedUniverse);
 | 
			
		||||
 | 
			
		||||
        OnFinalize();
 | 
			
		||||
 | 
			
		||||
        if (UniverseObject.IsInUniverse)
 | 
			
		||||
            ExitedUniverse(UniverseObject, new(Universe));
 | 
			
		||||
        sender.UniverseObject.OnActiveChanged.AddListener(delegateOnUniverseObjectActiveChanged);
 | 
			
		||||
        UpdateActive();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected virtual void OnEnteredUniverse(IUniverse universe) { }
 | 
			
		||||
    protected void EnteredUniverse(IUniverseObject sender, IUniverseObject.EnteredUniverseArguments args) => OnEnteredUniverse(args.Universe);
 | 
			
		||||
    protected override void OnAssign(IStateEnable stateEnable)
 | 
			
		||||
    {
 | 
			
		||||
        base.OnAssign(stateEnable);
 | 
			
		||||
 | 
			
		||||
    protected virtual void OnExitedUniverse(IUniverse universe) { }
 | 
			
		||||
    protected void ExitedUniverse(IUniverseObject sender, IUniverseObject.ExitedUniverseArguments args) => OnExitedUniverse(args.Universe);
 | 
			
		||||
        stateEnable.OnEnabledChanged.AddListener(delegateOnStateEnabledChanged);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void UnassignInternal()
 | 
			
		||||
    {
 | 
			
		||||
        BehaviourController.UniverseObject.OnActiveChanged.RemoveListener(delegateOnUniverseObjectActiveChanged);
 | 
			
		||||
        StateEnable.OnEnabledChanged.RemoveListener(delegateOnStateEnabledChanged);
 | 
			
		||||
        BehaviourController.OnUniverseObjectAssigned.RemoveListener(delegateOnUniverseObjectAssigned);
 | 
			
		||||
        BehaviourController.StateEnable.OnEnabledChanged.RemoveListener(delegateOnStateEnabledChanged);
 | 
			
		||||
        base.UnassignInternal();
 | 
			
		||||
        _behaviourController = null!;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void InitializeInternal()
 | 
			
		||||
    {
 | 
			
		||||
        Debug.Assert.AssertBehaviourControllerAssigned(this);
 | 
			
		||||
        Debug.Assert.AssertStateEnableAssigned(this);
 | 
			
		||||
 | 
			
		||||
        UpdateActive();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void OnStateEnabledChanged(IStateEnable sender, IStateEnable.EnabledChangedArguments args) => UpdateActive();
 | 
			
		||||
    private void OnUniverseObjectActiveChanged(IActive sender, IActive.ActiveChangedArguments args) => UpdateActive();
 | 
			
		||||
 | 
			
		||||
    private void UpdateActive()
 | 
			
		||||
    {
 | 
			
		||||
        bool previousActive = IsActive;
 | 
			
		||||
        _isActive = StateEnable.Enabled && _behaviourController.StateEnable.Enabled && _behaviourController.UniverseObject.IsActive;
 | 
			
		||||
 | 
			
		||||
        if (previousActive != IsActive)
 | 
			
		||||
            OnActiveChanged?.Invoke(this, new(previousActive));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected Behaviour()
 | 
			
		||||
    {
 | 
			
		||||
        delegateOnUniverseObjectAssigned = OnUniverseObjectAssigned;
 | 
			
		||||
        delegateOnUniverseObjectActiveChanged = OnUniverseObjectActiveChanged;
 | 
			
		||||
        delegateOnStateEnabledChanged = OnStateEnabledChanged;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
public abstract class Behaviour2D : Behaviour, IBehaviour2D
 | 
			
		||||
// TODO this should not use independent behaviour, the OnInitialize usage for getting the transform can cause very unexpected issues
 | 
			
		||||
public abstract class Behaviour2D : Internal.BehaviourIndependent, IBehaviour2D
 | 
			
		||||
{
 | 
			
		||||
    public ITransform2D Transform { get; private set; } = null!;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,103 +0,0 @@
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("{GetType().Name, nq}, Priority: {Priority}, Initialized: {Initialized}")]
 | 
			
		||||
public abstract class BehaviourBase : BaseEntity, IBehaviour
 | 
			
		||||
{
 | 
			
		||||
    public Event<IBehaviour, IBehaviour.PriorityChangedArguments> OnPriorityChanged { get; } = new();
 | 
			
		||||
    public Event<IActive, IActive.ActiveChangedArguments> OnActiveChanged { get; } = new();
 | 
			
		||||
    public Event<IHasBehaviourController> OnBehaviourControllerAssigned { get; } = new();
 | 
			
		||||
 | 
			
		||||
    private readonly Event<IHasUniverseObject>.EventHandler delegateOnUniverseObjectAssigned = null!;
 | 
			
		||||
    private readonly Event<IActive, IActive.ActiveChangedArguments>.EventHandler delegateOnUniverseObjectActiveChanged = null!;
 | 
			
		||||
    private readonly Event<IStateEnable, IStateEnable.EnabledChangedArguments>.EventHandler delegateOnStateEnabledChanged = null!;
 | 
			
		||||
 | 
			
		||||
    public IUniverse Universe => BehaviourController.UniverseObject.Universe;
 | 
			
		||||
    public IUniverseObject UniverseObject => BehaviourController.UniverseObject;
 | 
			
		||||
 | 
			
		||||
    private IBehaviourController _behaviourController = null!;
 | 
			
		||||
    public IBehaviourController BehaviourController => _behaviourController;
 | 
			
		||||
 | 
			
		||||
    private int _priority = 0;
 | 
			
		||||
    public int Priority
 | 
			
		||||
    {
 | 
			
		||||
        get => _priority;
 | 
			
		||||
        set
 | 
			
		||||
        {
 | 
			
		||||
            if (value == _priority)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            int previousPriority = _priority;
 | 
			
		||||
            _priority = value;
 | 
			
		||||
            OnPriorityChanged?.Invoke(this, new(previousPriority));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private bool _isActive = false;
 | 
			
		||||
    public bool IsActive => _isActive;
 | 
			
		||||
 | 
			
		||||
    protected virtual void OnAssign(IBehaviourController behaviourController) { }
 | 
			
		||||
    public bool Assign(IBehaviourController behaviourController)
 | 
			
		||||
    {
 | 
			
		||||
        if (IsInitialized)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        _behaviourController = behaviourController;
 | 
			
		||||
        OnAssign(behaviourController);
 | 
			
		||||
        behaviourController.OnUniverseObjectAssigned.AddListener(delegateOnUniverseObjectAssigned);
 | 
			
		||||
        behaviourController.StateEnable.OnEnabledChanged.AddListener(delegateOnStateEnabledChanged);
 | 
			
		||||
        if (behaviourController.UniverseObject is not null)
 | 
			
		||||
            OnUniverseObjectAssigned(behaviourController);
 | 
			
		||||
        OnBehaviourControllerAssigned?.Invoke(this);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void OnUniverseObjectAssigned(IHasUniverseObject sender)
 | 
			
		||||
    {
 | 
			
		||||
        sender.UniverseObject.OnActiveChanged.AddListener(delegateOnUniverseObjectActiveChanged);
 | 
			
		||||
        UpdateActive();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void OnAssign(IStateEnable stateEnable)
 | 
			
		||||
    {
 | 
			
		||||
        base.OnAssign(stateEnable);
 | 
			
		||||
 | 
			
		||||
        stateEnable.OnEnabledChanged.AddListener(delegateOnStateEnabledChanged);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void UnassignInternal()
 | 
			
		||||
    {
 | 
			
		||||
        BehaviourController.UniverseObject.OnActiveChanged.RemoveListener(delegateOnUniverseObjectActiveChanged);
 | 
			
		||||
        StateEnable.OnEnabledChanged.RemoveListener(delegateOnStateEnabledChanged);
 | 
			
		||||
        BehaviourController.OnUniverseObjectAssigned.RemoveListener(delegateOnUniverseObjectAssigned);
 | 
			
		||||
        BehaviourController.StateEnable.OnEnabledChanged.RemoveListener(delegateOnStateEnabledChanged);
 | 
			
		||||
        base.UnassignInternal();
 | 
			
		||||
        _behaviourController = null!;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void InitializeInternal()
 | 
			
		||||
    {
 | 
			
		||||
        Debug.Assert.AssertBehaviourControllerAssigned(this);
 | 
			
		||||
        Debug.Assert.AssertStateEnableAssigned(this);
 | 
			
		||||
 | 
			
		||||
        UpdateActive();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void OnStateEnabledChanged(IStateEnable sender, IStateEnable.EnabledChangedArguments args) => UpdateActive();
 | 
			
		||||
    private void OnUniverseObjectActiveChanged(IActive sender, IActive.ActiveChangedArguments args) => UpdateActive();
 | 
			
		||||
 | 
			
		||||
    private void UpdateActive()
 | 
			
		||||
    {
 | 
			
		||||
        bool previousActive = IsActive;
 | 
			
		||||
        _isActive = StateEnable.Enabled && _behaviourController.StateEnable.Enabled && _behaviourController.UniverseObject.IsActive;
 | 
			
		||||
 | 
			
		||||
        if (previousActive != IsActive)
 | 
			
		||||
            OnActiveChanged?.Invoke(this, new(previousActive));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected BehaviourBase()
 | 
			
		||||
    {
 | 
			
		||||
        delegateOnUniverseObjectAssigned = OnUniverseObjectAssigned;
 | 
			
		||||
        delegateOnUniverseObjectActiveChanged = OnUniverseObjectActiveChanged;
 | 
			
		||||
        delegateOnStateEnabledChanged = OnStateEnabledChanged;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,67 +0,0 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
public class BehaviourCollectorSorted<T> : BehaviourCollector<T> where T : class
 | 
			
		||||
{
 | 
			
		||||
    private readonly Event<IBehaviour, IBehaviour.PriorityChangedArguments>.EventHandler delegateOnPriorityChanged = null!;
 | 
			
		||||
 | 
			
		||||
    private IComparer<T>? _sortBy = null;
 | 
			
		||||
    public IComparer<T>? SortBy
 | 
			
		||||
    {
 | 
			
		||||
        get => _sortBy;
 | 
			
		||||
        set
 | 
			
		||||
        {
 | 
			
		||||
            _sortBy = value;
 | 
			
		||||
 | 
			
		||||
            if (value is not null)
 | 
			
		||||
                behaviours.Sort(value);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void AddBehaviour(T behaviour)
 | 
			
		||||
    {
 | 
			
		||||
        if (SortBy is null)
 | 
			
		||||
        {
 | 
			
		||||
            behaviours.Add(behaviour);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int insertionIndex = behaviours.BinarySearch(behaviour, SortBy);
 | 
			
		||||
 | 
			
		||||
        if (insertionIndex < 0)
 | 
			
		||||
            insertionIndex = ~insertionIndex;
 | 
			
		||||
 | 
			
		||||
        behaviours.Insert(insertionIndex, 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)
 | 
			
		||||
    {
 | 
			
		||||
        T behaviour = (T)sender;
 | 
			
		||||
        behaviours.Remove(behaviour);
 | 
			
		||||
        AddBehaviour(behaviour);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public BehaviourCollectorSorted()
 | 
			
		||||
    {
 | 
			
		||||
        delegateOnPriorityChanged = OnPriorityChanged;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public BehaviourCollectorSorted(IUniverse universe, Comparison<T> sortBy) : base(universe)
 | 
			
		||||
    {
 | 
			
		||||
        delegateOnPriorityChanged = OnPriorityChanged;
 | 
			
		||||
 | 
			
		||||
        SortBy = Comparer<T>.Create(sortBy);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -10,7 +10,7 @@ public class BehaviourController : BaseEntity, IBehaviourController
 | 
			
		||||
    public Event<IBehaviourController, IBehaviourController.BehaviourRemovedArguments> OnBehaviourRemoved { get; } = new();
 | 
			
		||||
    public Event<IHasUniverseObject> OnUniverseObjectAssigned { get; } = new();
 | 
			
		||||
 | 
			
		||||
    private readonly List<IBehaviour> behaviours = new(Constants.BEHAVIOURS_SIZE_INITIAL);
 | 
			
		||||
    private readonly FastList<IBehaviour> behaviours = new(Constants.BEHAVIOURS_SIZE_INITIAL);
 | 
			
		||||
 | 
			
		||||
    private IUniverseObject _universeObject = null!;
 | 
			
		||||
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										56
									
								
								Engine.Core/BehaviourInternal.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								Engine.Core/BehaviourInternal.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,56 @@
 | 
			
		||||
namespace Engine.Core.Internal;
 | 
			
		||||
 | 
			
		||||
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// This behaviour can be used for core managers like <see cref="UpdateManager"/> etc. which need to be able to work even
 | 
			
		||||
/// in a very bare minimum setup without the presence of <see cref="UniverseEntranceManager"/> to set themselves up on universe entrance.
 | 
			
		||||
/// I recommend not using this unless you know what you are doing but it might come in handy for some use cases.
 | 
			
		||||
/// </summary>
 | 
			
		||||
public abstract class BehaviourIndependent : Behaviour
 | 
			
		||||
{
 | 
			
		||||
    private readonly Event<IUniverseObject, IUniverseObject.EnteredUniverseArguments>.EventHandler delegateEnteredUniverse = null!;
 | 
			
		||||
    private readonly Event<IUniverseObject, IUniverseObject.ExitedUniverseArguments>.EventHandler delegateExitedUniverse = null!;
 | 
			
		||||
 | 
			
		||||
    public BehaviourIndependent()
 | 
			
		||||
    {
 | 
			
		||||
        OnInitialized.AddListener(OnInitialize);
 | 
			
		||||
        OnFinalized.AddListener(OnFinalize);
 | 
			
		||||
        OnUnassigned.AddListener(OnUnassign);
 | 
			
		||||
 | 
			
		||||
        delegateEnteredUniverse = EnteredUniverse;
 | 
			
		||||
        delegateExitedUniverse = ExitedUniverse;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected virtual void OnUnassign() { }
 | 
			
		||||
    protected void OnUnassign(IAssignable assignable) => OnUnassign();
 | 
			
		||||
 | 
			
		||||
    protected virtual void OnInitialize() { }
 | 
			
		||||
    protected void OnInitialize(IInitializable _)
 | 
			
		||||
    {
 | 
			
		||||
        BehaviourController.UniverseObject.OnEnteredUniverse.AddListener(delegateEnteredUniverse);
 | 
			
		||||
        BehaviourController.UniverseObject.OnExitedUniverse.AddListener(delegateExitedUniverse);
 | 
			
		||||
 | 
			
		||||
        OnInitialize();
 | 
			
		||||
 | 
			
		||||
        if (UniverseObject.IsInUniverse)
 | 
			
		||||
            EnteredUniverse(UniverseObject, new(Universe));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected virtual void OnFinalize() { }
 | 
			
		||||
    protected void OnFinalize(IInitializable _)
 | 
			
		||||
    {
 | 
			
		||||
        BehaviourController.UniverseObject.OnEnteredUniverse.RemoveListener(delegateEnteredUniverse);
 | 
			
		||||
        BehaviourController.UniverseObject.OnExitedUniverse.RemoveListener(delegateExitedUniverse);
 | 
			
		||||
 | 
			
		||||
        OnFinalize();
 | 
			
		||||
 | 
			
		||||
        if (UniverseObject.IsInUniverse)
 | 
			
		||||
            ExitedUniverse(UniverseObject, new(Universe));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected virtual void OnEnteredUniverse(IUniverse universe) { }
 | 
			
		||||
    protected void EnteredUniverse(IUniverseObject sender, IUniverseObject.EnteredUniverseArguments args) => OnEnteredUniverse(args.Universe);
 | 
			
		||||
 | 
			
		||||
    protected virtual void OnExitedUniverse(IUniverse universe) { }
 | 
			
		||||
    protected void ExitedUniverse(IUniverseObject sender, IUniverseObject.ExitedUniverseArguments args) => OnExitedUniverse(args.Universe);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								Engine.Core/Collectors/ActiveBehaviourCollector.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								Engine.Core/Collectors/ActiveBehaviourCollector.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
public class ActiveBehaviourCollector<T> : ActiveBehaviourCollectorBase<T> where T : class, IBehaviour
 | 
			
		||||
{
 | 
			
		||||
    protected readonly FastList<T> activeBehaviours = new(32);
 | 
			
		||||
    public override T this[Index index] => activeBehaviours[index];
 | 
			
		||||
    public override int Count => activeBehaviours.Count;
 | 
			
		||||
 | 
			
		||||
    public ActiveBehaviourCollector() { }
 | 
			
		||||
    public ActiveBehaviourCollector(IUniverse universe) : base(universe) { }
 | 
			
		||||
 | 
			
		||||
    protected override void AddBehaviour(T behaviour) => activeBehaviours.Add(behaviour);
 | 
			
		||||
    protected override bool RemoveBehaviour(T tBehaviour) => activeBehaviours.Remove(tBehaviour);
 | 
			
		||||
}
 | 
			
		||||
@@ -3,12 +3,10 @@ using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
public class ActiveBehaviourCollector<T> : IBehaviourCollector<T> where T : class, IBehaviour
 | 
			
		||||
public abstract class ActiveBehaviourCollectorBase<T> : IBehaviourCollector<T> where T : class, IBehaviour
 | 
			
		||||
{
 | 
			
		||||
    public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourCollectedArguments> OnCollected { get; } = new();
 | 
			
		||||
    public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourRemovedArguments> OnRemoved { get; } = new();
 | 
			
		||||
    public Event<IHasUniverse> OnUniverseAssigned { get; } = new();
 | 
			
		||||
    public Event<IAssignable>? OnUnassigned { get; } = new();
 | 
			
		||||
    protected readonly Dictionary<IActive, T> monitoringActiveToBehaviour = new(32);
 | 
			
		||||
    protected readonly FastList<T> monitoringBehaviours = new(32);
 | 
			
		||||
 | 
			
		||||
    private readonly Event<IBehaviourController, IBehaviourController.BehaviourAddedArguments>.EventHandler delegateOnBehaviourAdded = null!;
 | 
			
		||||
    private readonly Event<IBehaviourController, IBehaviourController.BehaviourRemovedArguments>.EventHandler delegateOnBehaviourRemoved = null!;
 | 
			
		||||
@@ -16,12 +14,95 @@ public class ActiveBehaviourCollector<T> : IBehaviourCollector<T> where T : clas
 | 
			
		||||
    private readonly Event<IUniverse, IUniverse.UniverseObjectRegisteredArguments>.EventHandler delegateOnUniverseObjectRegistered = null!;
 | 
			
		||||
    private readonly Event<IUniverse, IUniverse.UniverseObjectUnRegisteredArguments>.EventHandler delegateOnUniverseObjectUnregistered = null!;
 | 
			
		||||
 | 
			
		||||
    private readonly List<T> monitoringBehaviours = new(32);
 | 
			
		||||
    protected readonly List<T> activeBehaviours = new(32);
 | 
			
		||||
    protected readonly Dictionary<IActive, T> monitoringActiveToBehaviour = new(32);
 | 
			
		||||
    public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourCollectedArguments> OnCollected { get; } = new();
 | 
			
		||||
    public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourRemovedArguments> OnRemoved { get; } = new();
 | 
			
		||||
    public Event<IHasUniverse> OnUniverseAssigned { get; } = new();
 | 
			
		||||
    public Event<IAssignable>? OnUnassigned { get; } = new();
 | 
			
		||||
 | 
			
		||||
    public abstract int Count { get; }
 | 
			
		||||
 | 
			
		||||
    public abstract T this[Index index] { get; }
 | 
			
		||||
    public IUniverse Universe { get; private set; } = null!;
 | 
			
		||||
 | 
			
		||||
    public bool Assign(IUniverse universe)
 | 
			
		||||
    {
 | 
			
		||||
        if (Universe is not null)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        foreach (IUniverseObject universeObject in universe)
 | 
			
		||||
            OnUniverseObjectRegistered(universe, new(universeObject));
 | 
			
		||||
 | 
			
		||||
        universe.OnUniverseObjectRegistered.AddListener(delegateOnUniverseObjectRegistered);
 | 
			
		||||
        universe.OnUniverseObjectUnRegistered.AddListener(delegateOnUniverseObjectUnregistered);
 | 
			
		||||
 | 
			
		||||
        Universe = universe;
 | 
			
		||||
        OnUniverseAssigned?.Invoke(this);
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public bool Unassign()
 | 
			
		||||
    {
 | 
			
		||||
        if (Universe is null)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        foreach (IUniverseObject universeObject in Universe)
 | 
			
		||||
            OnUniverseObjectUnregistered(Universe, new(universeObject));
 | 
			
		||||
 | 
			
		||||
        Universe.OnUniverseObjectRegistered.RemoveListener(delegateOnUniverseObjectRegistered);
 | 
			
		||||
        Universe.OnUniverseObjectUnRegistered.RemoveListener(delegateOnUniverseObjectUnregistered);
 | 
			
		||||
 | 
			
		||||
        Universe = null!;
 | 
			
		||||
        OnUnassigned?.Invoke(this);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected abstract void AddBehaviour(T behaviour);
 | 
			
		||||
    protected virtual void OnBehaviourAdd(IBehaviour behaviour) { }
 | 
			
		||||
    private void OnBehaviourAdded(IBehaviourController controller, IBehaviourController.BehaviourAddedArguments args)
 | 
			
		||||
    {
 | 
			
		||||
        if (args.BehaviourAdded is not T tBehaviour)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        monitoringBehaviours.Add(tBehaviour);
 | 
			
		||||
        monitoringActiveToBehaviour.Add(tBehaviour, tBehaviour);
 | 
			
		||||
        tBehaviour.OnActiveChanged.AddListener(delegateOnBehaviourStateChanged);
 | 
			
		||||
        OnBehaviourStateChanged(tBehaviour, new(!tBehaviour.IsActive));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected abstract bool RemoveBehaviour(T behaviour);
 | 
			
		||||
    protected virtual void OnBehaviourRemove(IBehaviour behaviour) { }
 | 
			
		||||
    private void OnBehaviourRemoved(IBehaviourController controller, IBehaviourController.BehaviourRemovedArguments args)
 | 
			
		||||
    {
 | 
			
		||||
        if (args.BehaviourRemoved is not T tBehaviour)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (!monitoringBehaviours.Remove(tBehaviour) || !monitoringActiveToBehaviour.Remove(tBehaviour))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        tBehaviour.OnActiveChanged.RemoveListener(delegateOnBehaviourStateChanged);
 | 
			
		||||
        if (!RemoveBehaviour(tBehaviour))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        OnBehaviourRemove(tBehaviour);
 | 
			
		||||
        OnRemoved?.Invoke(this, new(tBehaviour));
 | 
			
		||||
    }
 | 
			
		||||
    private void OnBehaviourStateChanged(IActive sender, IActive.ActiveChangedArguments args)
 | 
			
		||||
    {
 | 
			
		||||
        T behaviour = monitoringActiveToBehaviour[sender];
 | 
			
		||||
        if (sender.IsActive)
 | 
			
		||||
        {
 | 
			
		||||
            AddBehaviour(behaviour);
 | 
			
		||||
            OnBehaviourAdd(behaviour);
 | 
			
		||||
            OnCollected?.Invoke(this, new(behaviour));
 | 
			
		||||
        }
 | 
			
		||||
        else if (RemoveBehaviour(behaviour))
 | 
			
		||||
        {
 | 
			
		||||
            OnBehaviourRemove(behaviour);
 | 
			
		||||
            OnRemoved?.Invoke(this, new(behaviour));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void OnUniverseObjectRegistered(IUniverse manager, IUniverse.UniverseObjectRegisteredArguments args)
 | 
			
		||||
    {
 | 
			
		||||
        IUniverseObject universeObject = args.UniverseObjectRegistered;
 | 
			
		||||
@@ -44,89 +125,7 @@ public class ActiveBehaviourCollector<T> : IBehaviourCollector<T> where T : clas
 | 
			
		||||
            OnBehaviourRemoved(universeObject.BehaviourController, new(universeObject.BehaviourController[i]));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected virtual void OnBehaviourAdd(IBehaviour behaviour) { }
 | 
			
		||||
    private void OnBehaviourAdded(IBehaviourController controller, IBehaviourController.BehaviourAddedArguments args)
 | 
			
		||||
    {
 | 
			
		||||
        if (args.BehaviourAdded is not T tBehaviour)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        monitoringBehaviours.Add(tBehaviour);
 | 
			
		||||
        monitoringActiveToBehaviour.Add(tBehaviour, tBehaviour);
 | 
			
		||||
        tBehaviour.OnActiveChanged.AddListener(delegateOnBehaviourStateChanged);
 | 
			
		||||
        OnBehaviourStateChanged(tBehaviour, new(!tBehaviour.IsActive));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected virtual void AddBehaviour(T behaviour) => activeBehaviours.Add(behaviour);
 | 
			
		||||
    private void OnBehaviourStateChanged(IActive sender, IActive.ActiveChangedArguments args)
 | 
			
		||||
    {
 | 
			
		||||
        T behaviour = monitoringActiveToBehaviour[sender];
 | 
			
		||||
        if (sender.IsActive)
 | 
			
		||||
        {
 | 
			
		||||
            AddBehaviour(behaviour);
 | 
			
		||||
            OnBehaviourAdd(behaviour);
 | 
			
		||||
            OnCollected?.Invoke(this, new(behaviour));
 | 
			
		||||
        }
 | 
			
		||||
        else if (activeBehaviours.Remove(behaviour))
 | 
			
		||||
        {
 | 
			
		||||
            OnBehaviourRemove(behaviour);
 | 
			
		||||
            OnRemoved?.Invoke(this, new(behaviour));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected virtual void OnBehaviourRemove(IBehaviour behaviour) { }
 | 
			
		||||
    private void OnBehaviourRemoved(IBehaviourController controller, IBehaviourController.BehaviourRemovedArguments args)
 | 
			
		||||
    {
 | 
			
		||||
        if (args.BehaviourRemoved is not T tBehaviour)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (!monitoringBehaviours.Remove(tBehaviour) || !monitoringActiveToBehaviour.Remove(tBehaviour))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        tBehaviour.OnActiveChanged.RemoveListener(delegateOnBehaviourStateChanged);
 | 
			
		||||
        if (activeBehaviours.Remove(tBehaviour))
 | 
			
		||||
        {
 | 
			
		||||
            OnBehaviourRemove(tBehaviour);
 | 
			
		||||
            OnRemoved?.Invoke(this, new(tBehaviour));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public bool Assign(IUniverse universe)
 | 
			
		||||
    {
 | 
			
		||||
        if (Universe is not null)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        foreach (IUniverseObject universeObject in universe.UniverseObjects)
 | 
			
		||||
            OnUniverseObjectRegistered(universe, new(universeObject));
 | 
			
		||||
 | 
			
		||||
        universe.OnUniverseObjectRegistered.AddListener(delegateOnUniverseObjectRegistered);
 | 
			
		||||
        universe.OnUniverseObjectUnRegistered.AddListener(delegateOnUniverseObjectUnregistered);
 | 
			
		||||
 | 
			
		||||
        Universe = universe;
 | 
			
		||||
        OnUniverseAssigned?.Invoke(this);
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public bool Unassign()
 | 
			
		||||
    {
 | 
			
		||||
        if (Universe is null)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        foreach (IUniverseObject universeObject in Universe.UniverseObjects)
 | 
			
		||||
            OnUniverseObjectUnregistered(Universe, new(universeObject));
 | 
			
		||||
 | 
			
		||||
        Universe.OnUniverseObjectRegistered.RemoveListener(delegateOnUniverseObjectRegistered);
 | 
			
		||||
        Universe.OnUniverseObjectUnRegistered.RemoveListener(delegateOnUniverseObjectUnregistered);
 | 
			
		||||
 | 
			
		||||
        Universe = null!;
 | 
			
		||||
        OnUnassigned?.Invoke(this);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int Count => activeBehaviours.Count;
 | 
			
		||||
    public T this[Index index] => activeBehaviours[index];
 | 
			
		||||
 | 
			
		||||
    public ActiveBehaviourCollector()
 | 
			
		||||
    public ActiveBehaviourCollectorBase()
 | 
			
		||||
    {
 | 
			
		||||
        delegateOnBehaviourAdded = OnBehaviourAdded;
 | 
			
		||||
        delegateOnBehaviourRemoved = OnBehaviourRemoved;
 | 
			
		||||
@@ -135,7 +134,7 @@ public class ActiveBehaviourCollector<T> : IBehaviourCollector<T> where T : clas
 | 
			
		||||
        delegateOnUniverseObjectUnregistered = OnUniverseObjectUnregistered;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ActiveBehaviourCollector(IUniverse universe)
 | 
			
		||||
    public ActiveBehaviourCollectorBase(IUniverse universe)
 | 
			
		||||
    {
 | 
			
		||||
        delegateOnBehaviourAdded = OnBehaviourAdded;
 | 
			
		||||
        delegateOnBehaviourRemoved = OnBehaviourRemoved;
 | 
			
		||||
							
								
								
									
										89
									
								
								Engine.Core/Collectors/ActiveBehaviourCollectorOrdered.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								Engine.Core/Collectors/ActiveBehaviourCollectorOrdered.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,89 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
public class ActiveBehaviourCollectorOrdered<TIndex, TItem> : ActiveBehaviourCollector<TItem> where TItem : class, IBehaviour where TIndex : IComparable
 | 
			
		||||
{
 | 
			
		||||
    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!;
 | 
			
		||||
 | 
			
		||||
    private int count = 0;
 | 
			
		||||
    public override int Count => count;
 | 
			
		||||
 | 
			
		||||
    public override TItem this[Index index]
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            int actualIndex = index.IsFromEnd
 | 
			
		||||
                ? count - index.Value
 | 
			
		||||
                : index.Value;
 | 
			
		||||
 | 
			
		||||
            if (actualIndex < 0 || actualIndex >= count)
 | 
			
		||||
                throw new IndexOutOfRangeException();
 | 
			
		||||
 | 
			
		||||
            int leftIndex = actualIndex;
 | 
			
		||||
            foreach ((TIndex i, FastList<TItem> list) in behaviours)
 | 
			
		||||
            {
 | 
			
		||||
                if (leftIndex < list.Count)
 | 
			
		||||
                    return list[leftIndex];
 | 
			
		||||
                leftIndex -= list.Count;
 | 
			
		||||
            }
 | 
			
		||||
            throw new IndexOutOfRangeException();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override bool RemoveBehaviour(TItem tBehaviour)
 | 
			
		||||
    {
 | 
			
		||||
        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) || !indexCache.Remove(tBehaviour))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        count--;
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void AddBehaviour(TItem behaviour)
 | 
			
		||||
    {
 | 
			
		||||
        TIndex key = getIndexFunc(behaviour);
 | 
			
		||||
        if (!behaviours.TryGetValue(key, out FastList<TItem>? list))
 | 
			
		||||
            behaviours[key] = list = [];
 | 
			
		||||
 | 
			
		||||
        count++;
 | 
			
		||||
        list.Add(behaviour);
 | 
			
		||||
        indexCache.Add(behaviour, key);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ActiveBehaviourCollectorOrdered(Func<TItem, TIndex> getIndexFunc, Comparison<TIndex> sortBy)
 | 
			
		||||
    {
 | 
			
		||||
        this.getIndexFunc = getIndexFunc;
 | 
			
		||||
        this.sortBy = Comparer<TIndex>.Create(sortBy);
 | 
			
		||||
        behaviours = new(this.sortBy);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ActiveBehaviourCollectorOrdered(IUniverse universe, Func<TItem, TIndex> getIndexFunc, Comparison<TIndex> sortBy) : base(universe)
 | 
			
		||||
    {
 | 
			
		||||
        this.getIndexFunc = getIndexFunc;
 | 
			
		||||
        this.sortBy = Comparer<TIndex>.Create(sortBy);
 | 
			
		||||
        behaviours = new(this.sortBy);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ActiveBehaviourCollectorOrdered(Func<TItem, TIndex> getIndexFunc, IComparer<TIndex> sortBy)
 | 
			
		||||
    {
 | 
			
		||||
        this.getIndexFunc = getIndexFunc;
 | 
			
		||||
        this.sortBy = sortBy;
 | 
			
		||||
        behaviours = new(sortBy);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ActiveBehaviourCollectorOrdered(IUniverse universe, Func<TItem, TIndex> getIndexFunc, IComparer<TIndex> sortBy) : base(universe)
 | 
			
		||||
    {
 | 
			
		||||
        this.getIndexFunc = getIndexFunc;
 | 
			
		||||
        this.sortBy = sortBy;
 | 
			
		||||
        behaviours = new(sortBy);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										17
									
								
								Engine.Core/Collectors/BehaviourCollector.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								Engine.Core/Collectors/BehaviourCollector.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
public class BehaviourCollector<T> : BehaviourCollectorBase<T> where T : class
 | 
			
		||||
{
 | 
			
		||||
    protected readonly FastList<T> behaviours = new(32);
 | 
			
		||||
 | 
			
		||||
    public override T this[Index index] => behaviours[index];
 | 
			
		||||
    public override int Count => behaviours.Count;
 | 
			
		||||
 | 
			
		||||
    protected override void AddBehaviour(T behaviour) => behaviours.Add(behaviour);
 | 
			
		||||
    protected override bool RemoveBehaviour(T tBehaviour) => behaviours.Remove(tBehaviour);
 | 
			
		||||
 | 
			
		||||
    public BehaviourCollector() { }
 | 
			
		||||
    public BehaviourCollector(IUniverse universe) : base(universe) { }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,24 +1,87 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
 | 
			
		||||
public abstract class BehaviourCollectorBase<T> : IBehaviourCollector<T> where T : class
 | 
			
		||||
{
 | 
			
		||||
    public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourCollectedArguments> OnCollected { get; } = new();
 | 
			
		||||
    public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourRemovedArguments> OnRemoved { get; } = new();
 | 
			
		||||
    public Event<IHasUniverse> OnUniverseAssigned { get; } = new();
 | 
			
		||||
    public Event<IAssignable>? OnUnassigned { get; } = new();
 | 
			
		||||
 | 
			
		||||
    private readonly Event<IBehaviourController, IBehaviourController.BehaviourAddedArguments>.EventHandler delegateOnBehaviourAdded = null!;
 | 
			
		||||
    private readonly Event<IBehaviourController, IBehaviourController.BehaviourRemovedArguments>.EventHandler delegateOnBehaviourRemoved = null!;
 | 
			
		||||
    private readonly Event<IUniverse, IUniverse.UniverseObjectRegisteredArguments>.EventHandler delegateOnUniverseObjectRegistered = null!;
 | 
			
		||||
    private readonly Event<IUniverse, IUniverse.UniverseObjectUnRegisteredArguments>.EventHandler delegateOnUniverseObjectUnregistered = null!;
 | 
			
		||||
 | 
			
		||||
    protected readonly List<T> behaviours = new(32);
 | 
			
		||||
    public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourCollectedArguments> OnCollected { get; } = new();
 | 
			
		||||
    public Event<IBehaviourCollector<T>, IBehaviourCollector<T>.BehaviourRemovedArguments> OnRemoved { get; } = new();
 | 
			
		||||
    public Event<IHasUniverse> OnUniverseAssigned { get; } = new();
 | 
			
		||||
    public Event<IAssignable>? OnUnassigned { get; } = new();
 | 
			
		||||
 | 
			
		||||
    public IUniverse Universe { get; private set; } = null!;
 | 
			
		||||
 | 
			
		||||
    public abstract int Count { get; }
 | 
			
		||||
 | 
			
		||||
    public abstract T this[Index index] { get; }
 | 
			
		||||
 | 
			
		||||
    public bool Assign(IUniverse universe)
 | 
			
		||||
    {
 | 
			
		||||
        if (Universe is not null)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        foreach (IUniverseObject universeObject in universe)
 | 
			
		||||
            OnUniverseObjectRegistered(universe, new(universeObject));
 | 
			
		||||
 | 
			
		||||
        universe.OnUniverseObjectRegistered.AddListener(delegateOnUniverseObjectRegistered);
 | 
			
		||||
        universe.OnPreUniverseObjectUnRegistered.AddListener(delegateOnUniverseObjectUnregistered);
 | 
			
		||||
 | 
			
		||||
        Universe = universe;
 | 
			
		||||
        OnAssign(universe);
 | 
			
		||||
        OnUniverseAssigned?.Invoke(this);
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public bool Unassign()
 | 
			
		||||
    {
 | 
			
		||||
        if (Universe is null)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        foreach (IUniverseObject universeObject in Universe)
 | 
			
		||||
            OnUniverseObjectUnregistered(Universe, new(universeObject));
 | 
			
		||||
 | 
			
		||||
        Universe.OnUniverseObjectRegistered.RemoveListener(delegateOnUniverseObjectRegistered);
 | 
			
		||||
        Universe.OnPreUniverseObjectUnRegistered.RemoveListener(delegateOnUniverseObjectUnregistered);
 | 
			
		||||
 | 
			
		||||
        Universe = null!;
 | 
			
		||||
        OnUnassigned?.Invoke(this);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected virtual void OnAssign(IUniverse universe) { }
 | 
			
		||||
 | 
			
		||||
    protected abstract void AddBehaviour(T behaviour);
 | 
			
		||||
    protected virtual void OnBehaviourAdd(IBehaviour behaviour) { }
 | 
			
		||||
    private void OnBehaviourAdded(IBehaviourController controller, IBehaviourController.BehaviourAddedArguments args)
 | 
			
		||||
    {
 | 
			
		||||
        if (args.BehaviourAdded is not T tBehaviour)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        AddBehaviour(tBehaviour);
 | 
			
		||||
        OnBehaviourAdd(args.BehaviourAdded);
 | 
			
		||||
        OnCollected?.Invoke(this, new(tBehaviour));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected abstract bool RemoveBehaviour(T tBehaviour);
 | 
			
		||||
    protected virtual void OnBehaviourRemove(IBehaviour behaviour) { }
 | 
			
		||||
    private void OnBehaviourRemoved(IBehaviourController controller, IBehaviourController.BehaviourRemovedArguments args)
 | 
			
		||||
    {
 | 
			
		||||
        if (args.BehaviourRemoved is not T tBehaviour)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (!RemoveBehaviour(tBehaviour))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        OnBehaviourRemove(args.BehaviourRemoved);
 | 
			
		||||
        OnRemoved?.Invoke(this, new(tBehaviour));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void OnUniverseObjectRegistered(IUniverse manager, IUniverse.UniverseObjectRegisteredArguments args)
 | 
			
		||||
    {
 | 
			
		||||
        IUniverseObject universeObject = args.UniverseObjectRegistered;
 | 
			
		||||
@@ -41,70 +104,7 @@ public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
 | 
			
		||||
            OnBehaviourRemoved(universeObject.BehaviourController, new(universeObject.BehaviourController[i]));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected virtual void AddBehaviour(T behaviour) => behaviours.Add(behaviour);
 | 
			
		||||
    protected virtual void OnBehaviourAdd(IBehaviour behaviour) { }
 | 
			
		||||
    private void OnBehaviourAdded(IBehaviourController controller, IBehaviourController.BehaviourAddedArguments args)
 | 
			
		||||
    {
 | 
			
		||||
        if (args.BehaviourAdded is not T tBehaviour)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        AddBehaviour(tBehaviour);
 | 
			
		||||
        OnBehaviourAdd(args.BehaviourAdded);
 | 
			
		||||
        OnCollected?.Invoke(this, new(tBehaviour));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected virtual void OnBehaviourRemove(IBehaviour behaviour) { }
 | 
			
		||||
    private void OnBehaviourRemoved(IBehaviourController controller, IBehaviourController.BehaviourRemovedArguments args)
 | 
			
		||||
    {
 | 
			
		||||
        if (args.BehaviourRemoved is not T tBehaviour)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (!behaviours.Remove(tBehaviour))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        OnBehaviourRemove(args.BehaviourRemoved);
 | 
			
		||||
        OnRemoved?.Invoke(this, new(tBehaviour));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected virtual void OnAssign(IUniverse universe) { }
 | 
			
		||||
    public bool Assign(IUniverse universe)
 | 
			
		||||
    {
 | 
			
		||||
        if (Universe is not null)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        foreach (IUniverseObject universeObject in universe.UniverseObjects)
 | 
			
		||||
            OnUniverseObjectRegistered(universe, new(universeObject));
 | 
			
		||||
 | 
			
		||||
        universe.OnUniverseObjectRegistered.AddListener(delegateOnUniverseObjectRegistered);
 | 
			
		||||
        universe.OnPreUniverseObjectUnRegistered.AddListener(delegateOnUniverseObjectUnregistered);
 | 
			
		||||
 | 
			
		||||
        Universe = universe;
 | 
			
		||||
        OnAssign(universe);
 | 
			
		||||
        OnUniverseAssigned?.Invoke(this);
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public bool Unassign()
 | 
			
		||||
    {
 | 
			
		||||
        if (Universe is null)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        foreach (IUniverseObject universeObject in Universe.UniverseObjects)
 | 
			
		||||
            OnUniverseObjectUnregistered(Universe, new(universeObject));
 | 
			
		||||
 | 
			
		||||
        Universe.OnUniverseObjectRegistered.RemoveListener(delegateOnUniverseObjectRegistered);
 | 
			
		||||
        Universe.OnPreUniverseObjectUnRegistered.RemoveListener(delegateOnUniverseObjectUnregistered);
 | 
			
		||||
 | 
			
		||||
        Universe = null!;
 | 
			
		||||
        OnUnassigned?.Invoke(this);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int Count => behaviours.Count;
 | 
			
		||||
    public T this[Index index] => behaviours[index];
 | 
			
		||||
 | 
			
		||||
    public BehaviourCollector()
 | 
			
		||||
    public BehaviourCollectorBase()
 | 
			
		||||
    {
 | 
			
		||||
        delegateOnBehaviourAdded = OnBehaviourAdded;
 | 
			
		||||
        delegateOnBehaviourRemoved = OnBehaviourRemoved;
 | 
			
		||||
@@ -112,7 +112,7 @@ public class BehaviourCollector<T> : IBehaviourCollector<T> where T : class
 | 
			
		||||
        delegateOnUniverseObjectUnregistered = OnUniverseObjectUnregistered;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public BehaviourCollector(IUniverse universe)
 | 
			
		||||
    public BehaviourCollectorBase(IUniverse universe)
 | 
			
		||||
    {
 | 
			
		||||
        delegateOnBehaviourAdded = OnBehaviourAdded;
 | 
			
		||||
        delegateOnBehaviourRemoved = OnBehaviourRemoved;
 | 
			
		||||
							
								
								
									
										89
									
								
								Engine.Core/Collectors/BehaviourCollectorOrdered.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								Engine.Core/Collectors/BehaviourCollectorOrdered.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,89 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
public class BehaviourCollectorOrdered<TIndex, TItem> : BehaviourCollectorBase<TItem> where TItem : class where TIndex : IComparable
 | 
			
		||||
{
 | 
			
		||||
    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!;
 | 
			
		||||
 | 
			
		||||
    private int count = 0;
 | 
			
		||||
    public override int Count => count;
 | 
			
		||||
 | 
			
		||||
    public override TItem this[Index index]
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            int actualIndex = index.IsFromEnd
 | 
			
		||||
                ? count - index.Value
 | 
			
		||||
                : index.Value;
 | 
			
		||||
 | 
			
		||||
            if (actualIndex < 0 || actualIndex >= count)
 | 
			
		||||
                throw new IndexOutOfRangeException();
 | 
			
		||||
 | 
			
		||||
            int leftIndex = actualIndex;
 | 
			
		||||
            foreach ((TIndex i, FastList<TItem> list) in behaviours)
 | 
			
		||||
            {
 | 
			
		||||
                if (leftIndex < list.Count)
 | 
			
		||||
                    return list[leftIndex];
 | 
			
		||||
                leftIndex -= list.Count;
 | 
			
		||||
            }
 | 
			
		||||
            throw new IndexOutOfRangeException();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override bool RemoveBehaviour(TItem tBehaviour)
 | 
			
		||||
    {
 | 
			
		||||
        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) || !indexCache.Remove(tBehaviour))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        count--;
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void AddBehaviour(TItem behaviour)
 | 
			
		||||
    {
 | 
			
		||||
        TIndex key = getIndexFunc(behaviour);
 | 
			
		||||
        if (!behaviours.TryGetValue(key, out FastList<TItem>? list))
 | 
			
		||||
            behaviours[key] = list = [];
 | 
			
		||||
 | 
			
		||||
        count++;
 | 
			
		||||
        list.Add(behaviour);
 | 
			
		||||
        indexCache.Add(behaviour, key);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public BehaviourCollectorOrdered(Func<TItem, TIndex> getIndexFunc, Comparison<TIndex> sortBy)
 | 
			
		||||
    {
 | 
			
		||||
        this.getIndexFunc = getIndexFunc;
 | 
			
		||||
        this.sortBy = Comparer<TIndex>.Create(sortBy);
 | 
			
		||||
        behaviours = new(this.sortBy);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public BehaviourCollectorOrdered(IUniverse universe, Func<TItem, TIndex> getIndexFunc, Comparison<TIndex> sortBy) : base(universe)
 | 
			
		||||
    {
 | 
			
		||||
        this.getIndexFunc = getIndexFunc;
 | 
			
		||||
        this.sortBy = Comparer<TIndex>.Create(sortBy);
 | 
			
		||||
        behaviours = new(this.sortBy);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public BehaviourCollectorOrdered(Func<TItem, TIndex> getIndexFunc, IComparer<TIndex> sortBy)
 | 
			
		||||
    {
 | 
			
		||||
        this.getIndexFunc = getIndexFunc;
 | 
			
		||||
        this.sortBy = sortBy;
 | 
			
		||||
        behaviours = new(sortBy);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public BehaviourCollectorOrdered(IUniverse universe, Func<TItem, TIndex> getIndexFunc, IComparer<TIndex> sortBy) : base(universe)
 | 
			
		||||
    {
 | 
			
		||||
        this.getIndexFunc = getIndexFunc;
 | 
			
		||||
        this.sortBy = sortBy;
 | 
			
		||||
        behaviours = new(sortBy);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -4,6 +4,22 @@ namespace Engine.Core.Debug;
 | 
			
		||||
 | 
			
		||||
public static class Assert
 | 
			
		||||
{
 | 
			
		||||
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | 
			
		||||
    public static void AssertTrue(bool value, string errorMessage)
 | 
			
		||||
        => System.Diagnostics.Debug.Assert(value, errorMessage);
 | 
			
		||||
 | 
			
		||||
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | 
			
		||||
    public static void AssertFalse(bool value, string errorMessage)
 | 
			
		||||
        => System.Diagnostics.Debug.Assert(!value, errorMessage);
 | 
			
		||||
 | 
			
		||||
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | 
			
		||||
    public static void AssertNull(object? value, string errorMessage)
 | 
			
		||||
        => System.Diagnostics.Debug.Assert(value is null, errorMessage);
 | 
			
		||||
 | 
			
		||||
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | 
			
		||||
    public static void AssertNotNull(object? value, string errorMessage)
 | 
			
		||||
        => System.Diagnostics.Debug.Assert(value is not null, errorMessage);
 | 
			
		||||
 | 
			
		||||
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 | 
			
		||||
    public static void AssertInitialized(IInitializable initializable)
 | 
			
		||||
        => System.Diagnostics.Debug.Assert(initializable.IsInitialized, $"{initializable.GetType().Name} must be initialized");
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -14,4 +14,17 @@ public static class TransformExtensions
 | 
			
		||||
        if (localScale.HasValue) transform.LocalScale = localScale.Value;
 | 
			
		||||
        return transform;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static ITransform3D SetTransform(this ITransform3D transform,
 | 
			
		||||
        Vector3D? position = null, Quaternion? rotation = null, Vector3D? scale = null,
 | 
			
		||||
        Vector3D? localPosition = null, Quaternion? localRotation = null, Vector3D? localScale = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (position.HasValue) transform.Position = position.Value;
 | 
			
		||||
        if (rotation.HasValue) transform.Rotation = rotation.Value;
 | 
			
		||||
        if (scale.HasValue) transform.Scale = scale.Value;
 | 
			
		||||
        if (localPosition.HasValue) transform.LocalPosition = localPosition.Value;
 | 
			
		||||
        if (localRotation.HasValue) transform.LocalRotation = localRotation.Value;
 | 
			
		||||
        if (localScale.HasValue) transform.LocalScale = localScale.Value;
 | 
			
		||||
        return transform;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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.
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,12 @@ using Engine.Core.Debug;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
// TODO!: every reverse loop has a chance to have more than 1 unsubscription,
 | 
			
		||||
// for (int i = listeners.Count - 1; i >= 0; i--) 
 | 
			
		||||
// can be replaced with 
 | 
			
		||||
// for (int i = listeners.Count - 1; i >= 0; i = Math.Min(i - 1, listeners.Count - 1))
 | 
			
		||||
// but this would causes possible double calls on already called callbacks, find a better method.
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Represents a simple event with no parameters.
 | 
			
		||||
/// <para>Example usage:</para> 
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										102
									
								
								Engine.Core/Helpers/FastList.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								Engine.Core/Helpers/FastList.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,102 @@
 | 
			
		||||
using System.Collections;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
public class FastList<T> : IList<T>, IReadOnlyList<T>, IEnumerable<T> where T : notnull
 | 
			
		||||
{
 | 
			
		||||
    private readonly List<T> items = [];
 | 
			
		||||
    private readonly Dictionary<T, int> indexMap = [];
 | 
			
		||||
 | 
			
		||||
    public bool IsReadOnly { get; set; } = false;
 | 
			
		||||
    public int Count => items.Count;
 | 
			
		||||
    public T this[int index]
 | 
			
		||||
    {
 | 
			
		||||
        get => items[index];
 | 
			
		||||
        set
 | 
			
		||||
        {
 | 
			
		||||
            if (IsReadOnly)
 | 
			
		||||
                throw new System.Data.ReadOnlyException();
 | 
			
		||||
 | 
			
		||||
            items[index] = value;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Add(T item)
 | 
			
		||||
    {
 | 
			
		||||
        if (IsReadOnly)
 | 
			
		||||
            throw new System.Data.ReadOnlyException();
 | 
			
		||||
 | 
			
		||||
        indexMap[item] = items.Count;
 | 
			
		||||
        items.Add(item);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void RemoveAt(int i) => Remove(items[i], i);
 | 
			
		||||
    public bool Remove(T item)
 | 
			
		||||
    {
 | 
			
		||||
        if (IsReadOnly)
 | 
			
		||||
            throw new System.Data.ReadOnlyException();
 | 
			
		||||
 | 
			
		||||
        if (!indexMap.TryGetValue(item, out int index))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        Remove(item, index);
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void Remove(T item, int index)
 | 
			
		||||
    {
 | 
			
		||||
        int lastIndex = items.Count - 1;
 | 
			
		||||
        T lastItem = items[lastIndex];
 | 
			
		||||
 | 
			
		||||
        items[index] = lastItem;
 | 
			
		||||
        indexMap[lastItem] = index;
 | 
			
		||||
 | 
			
		||||
        items.RemoveAt(lastIndex);
 | 
			
		||||
        indexMap.Remove(item);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Insert(int index, T item)
 | 
			
		||||
    {
 | 
			
		||||
        if (IsReadOnly)
 | 
			
		||||
            throw new System.Data.ReadOnlyException();
 | 
			
		||||
 | 
			
		||||
        items.Insert(index, item);
 | 
			
		||||
 | 
			
		||||
        for (int i = index; i < items.Count; i++)
 | 
			
		||||
            indexMap[items[i]] = i;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Clear()
 | 
			
		||||
    {
 | 
			
		||||
        if (IsReadOnly)
 | 
			
		||||
            throw new System.Data.ReadOnlyException();
 | 
			
		||||
 | 
			
		||||
        items.Clear();
 | 
			
		||||
        indexMap.Clear();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public bool Contains(T item) => indexMap.ContainsKey(item);
 | 
			
		||||
    public int IndexOf(T item) => items.IndexOf(item);
 | 
			
		||||
    public int BinarySearch(T item, IComparer<T>? comparer = null) => items.BinarySearch(item, comparer);
 | 
			
		||||
 | 
			
		||||
    public void Sort(IComparer<T> comparer)
 | 
			
		||||
    {
 | 
			
		||||
        if (IsReadOnly)
 | 
			
		||||
            throw new System.Data.ReadOnlyException();
 | 
			
		||||
 | 
			
		||||
        items.Sort(comparer);
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < items.Count; i++)
 | 
			
		||||
            indexMap[items[i]] = i;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void CopyTo(T[] array, int arrayIndex) => items.CopyTo(array, arrayIndex);
 | 
			
		||||
 | 
			
		||||
    public IEnumerator<T> GetEnumerator() => items.GetEnumerator();
 | 
			
		||||
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 | 
			
		||||
 | 
			
		||||
    public FastList() { }
 | 
			
		||||
    public FastList(int count) { items.Capacity = count; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										172
									
								
								Engine.Core/Helpers/FastListOrdered.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								Engine.Core/Helpers/FastListOrdered.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,172 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// TODO This is VEERY experimental, and doesn't work well with the indices access. Use with caution
 | 
			
		||||
/// </summary>
 | 
			
		||||
/// <typeparam name="TIndex"></typeparam>
 | 
			
		||||
/// <typeparam name="TItem"></typeparam>
 | 
			
		||||
public class FastListOrdered<TIndex, TItem> : IList<TItem>, IReadOnlyList<TItem>, IEnumerable<TItem> where TItem : notnull where TIndex : IComparable
 | 
			
		||||
{
 | 
			
		||||
    private readonly SortedDictionary<TIndex, FastList<TItem>> items = null!;
 | 
			
		||||
 | 
			
		||||
    private readonly Func<TItem, TIndex> getIndexFunc = null!;
 | 
			
		||||
    private readonly IComparer<TIndex> sortBy = null!;
 | 
			
		||||
 | 
			
		||||
    private int count = 0;
 | 
			
		||||
    public int Count => count;
 | 
			
		||||
 | 
			
		||||
    public bool IsReadOnly { get; set; } = false;
 | 
			
		||||
 | 
			
		||||
    public TItem this[int index]
 | 
			
		||||
    {
 | 
			
		||||
        get { (TIndex tIndex, int i) = GetAt(index); return items[tIndex][i]; }
 | 
			
		||||
        set
 | 
			
		||||
        {
 | 
			
		||||
            if (IsReadOnly)
 | 
			
		||||
                throw new System.Data.ReadOnlyException();
 | 
			
		||||
            (TIndex tIndex, int i) = GetAt(index); items[tIndex][i] = value;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private (TIndex TIndex, int i) GetAt(Index index)
 | 
			
		||||
    {
 | 
			
		||||
        int actualIndex = index.IsFromEnd
 | 
			
		||||
                ? count - index.Value
 | 
			
		||||
                : index.Value;
 | 
			
		||||
 | 
			
		||||
        if (actualIndex < 0 || actualIndex >= count)
 | 
			
		||||
            throw new IndexOutOfRangeException();
 | 
			
		||||
 | 
			
		||||
        int leftIndex = actualIndex;
 | 
			
		||||
        foreach ((TIndex i, FastList<TItem> list) in items)
 | 
			
		||||
        {
 | 
			
		||||
            if (leftIndex < list.Count)
 | 
			
		||||
                return (i, leftIndex);
 | 
			
		||||
            leftIndex -= list.Count;
 | 
			
		||||
        }
 | 
			
		||||
        throw new IndexOutOfRangeException();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int IndexOf(TItem item)
 | 
			
		||||
    {
 | 
			
		||||
        int indexCounter = 0;
 | 
			
		||||
        foreach ((TIndex index, FastList<TItem> list) in items)
 | 
			
		||||
        {
 | 
			
		||||
            int i = list.IndexOf(item);
 | 
			
		||||
            if (i != -1)
 | 
			
		||||
                return indexCounter + i;
 | 
			
		||||
            indexCounter += list.Count;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Add(TItem item)
 | 
			
		||||
    {
 | 
			
		||||
        if (IsReadOnly)
 | 
			
		||||
            throw new System.Data.ReadOnlyException();
 | 
			
		||||
 | 
			
		||||
        TIndex key = getIndexFunc(item);
 | 
			
		||||
        if (!items.TryGetValue(key, out FastList<TItem>? list))
 | 
			
		||||
            items[key] = list = [];
 | 
			
		||||
 | 
			
		||||
        list.Add(item);
 | 
			
		||||
        count++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Insert(int index, TItem item)
 | 
			
		||||
    {
 | 
			
		||||
        if (IsReadOnly)
 | 
			
		||||
            throw new System.Data.ReadOnlyException();
 | 
			
		||||
 | 
			
		||||
        TIndex tIndex = getIndexFunc(item);
 | 
			
		||||
        if (!items.TryGetValue(tIndex, out FastList<TItem>? list))
 | 
			
		||||
            items[tIndex] = list = [];
 | 
			
		||||
 | 
			
		||||
        list.Insert(index, item);
 | 
			
		||||
        count++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public bool Remove(TItem item)
 | 
			
		||||
    {
 | 
			
		||||
        if (IsReadOnly)
 | 
			
		||||
            throw new System.Data.ReadOnlyException();
 | 
			
		||||
 | 
			
		||||
        TIndex index = getIndexFunc(item);
 | 
			
		||||
        if (!items.TryGetValue(index, out FastList<TItem>? list))
 | 
			
		||||
            throw new Exceptions.NotFoundException($"Index of '{index}' is not found in the collector");
 | 
			
		||||
 | 
			
		||||
        if (!list.Remove(item))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        count--;
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void RemoveAt(int index)
 | 
			
		||||
    {
 | 
			
		||||
        if (IsReadOnly)
 | 
			
		||||
            throw new System.Data.ReadOnlyException();
 | 
			
		||||
 | 
			
		||||
        (TIndex tIndex, int i) = GetAt(index);
 | 
			
		||||
        items[tIndex].RemoveAt(i);
 | 
			
		||||
        count--;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Clear()
 | 
			
		||||
    {
 | 
			
		||||
        if (IsReadOnly)
 | 
			
		||||
            throw new System.Data.ReadOnlyException();
 | 
			
		||||
 | 
			
		||||
        foreach ((TIndex index, FastList<TItem> list) in items)
 | 
			
		||||
            list.Clear();
 | 
			
		||||
 | 
			
		||||
        count = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public bool Contains(TItem item)
 | 
			
		||||
    {
 | 
			
		||||
        foreach ((TIndex index, FastList<TItem> list) in items)
 | 
			
		||||
            if (list.Contains(item))
 | 
			
		||||
                return true;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void CopyTo(TItem[] array, int arrayIndex)
 | 
			
		||||
    {
 | 
			
		||||
        int indexCounter = 0;
 | 
			
		||||
 | 
			
		||||
        foreach ((TIndex index, FastList<TItem> list) in items)
 | 
			
		||||
        {
 | 
			
		||||
            list.CopyTo(array, indexCounter);
 | 
			
		||||
            indexCounter += list.Count;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public IEnumerator<TItem> GetEnumerator()
 | 
			
		||||
    {
 | 
			
		||||
        foreach ((TIndex index, FastList<TItem> list) in items)
 | 
			
		||||
            foreach (TItem item in list)
 | 
			
		||||
                yield return item;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 | 
			
		||||
 | 
			
		||||
    public FastListOrdered(Func<TItem, TIndex> getIndexFunc, Comparison<TIndex> sortBy)
 | 
			
		||||
    {
 | 
			
		||||
        this.getIndexFunc = getIndexFunc;
 | 
			
		||||
        this.sortBy = Comparer<TIndex>.Create(sortBy);
 | 
			
		||||
        items = new(this.sortBy);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public FastListOrdered(Func<TItem, TIndex> getIndexFunc, IComparer<TIndex> sortBy)
 | 
			
		||||
    {
 | 
			
		||||
        this.getIndexFunc = getIndexFunc;
 | 
			
		||||
        this.sortBy = sortBy;
 | 
			
		||||
        items = new(sortBy);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -10,22 +10,25 @@ public class Pool<T> : IPool<T>
 | 
			
		||||
 | 
			
		||||
    private readonly Func<T> generator = null!;
 | 
			
		||||
    private readonly Queue<T> queue = new();
 | 
			
		||||
    private readonly HashSet<T> queuedHashes = [];
 | 
			
		||||
 | 
			
		||||
    public T Get()
 | 
			
		||||
    {
 | 
			
		||||
        if (!queue.TryDequeue(out T? result))
 | 
			
		||||
            result = generator();
 | 
			
		||||
 | 
			
		||||
        queuedHashes.Remove(result);
 | 
			
		||||
        OnRemoved?.Invoke(this, result);
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Return(T item)
 | 
			
		||||
    {
 | 
			
		||||
        if (queue.Contains(item))
 | 
			
		||||
        if (queuedHashes.Contains(item))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        queue.Enqueue(item);
 | 
			
		||||
        queuedHashes.Add(item);
 | 
			
		||||
        OnReturned?.Invoke(this, item);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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>
 | 
			
		||||
@@ -240,21 +254,33 @@ public static class Math
 | 
			
		||||
    public static T Lerp<T>(T x, T y, T t) where T : IFloatingPoint<T> => x + (y - x) * t;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Rounds a number to a specified number of fractional digits.
 | 
			
		||||
    /// Rounds a number to the closest integer.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="x">The number to round.</param>
 | 
			
		||||
    /// <param name="digits">The number of fractional digits in the return value.</param>
 | 
			
		||||
    /// <param name="mode">Specification for how to round <paramref name="x"/> if it is midway between two other numbers.</param>
 | 
			
		||||
    /// <returns>The number <paramref name="x"/> rounded to <paramref name="digits"/> fractional digits.</returns>
 | 
			
		||||
    public static float Round(float x, int digits, MidpointRounding mode) => MathF.Round(x, digits, mode);
 | 
			
		||||
    /// <param name="roundMode">Specification for how to round <paramref name="x"/> if it is midway between two other numbers.</param>
 | 
			
		||||
    /// <returns>The number <paramref name="x"/> rounded to the closest integer.</returns>
 | 
			
		||||
    public static float Round(float x, RoundMode roundMode) => RoundToInt(x, roundMode);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Rounds a number to an integer.
 | 
			
		||||
    /// Rounds a number to the closest integer.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="x">The number to round.</param>
 | 
			
		||||
    /// <param name="roundMode">Specification for how to round <paramref name="x"/> if it's midway between two numbers</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public static int RoundToInt(float x, RoundMode roundMode = RoundMode.Ceil) => (int)MathF.Round(x, 0, roundMode == RoundMode.Ceil ? MidpointRounding.ToPositiveInfinity : MidpointRounding.ToNegativeInfinity);
 | 
			
		||||
    /// <returns>The number <paramref name="x"/> rounded to the closest integer.</returns>
 | 
			
		||||
    public static int RoundToInt(float x, RoundMode roundMode = RoundMode.Ceil)
 | 
			
		||||
    {
 | 
			
		||||
        float remainder = x.Mod(1f);
 | 
			
		||||
 | 
			
		||||
        if (remainder == .5f)
 | 
			
		||||
            if (roundMode == RoundMode.Floor)
 | 
			
		||||
                return (int)x;
 | 
			
		||||
            else
 | 
			
		||||
                return (int)(x + .5f);
 | 
			
		||||
 | 
			
		||||
        if (x < 0f)
 | 
			
		||||
            return (int)(x - .5f);
 | 
			
		||||
        return (int)(x + .5f);
 | 
			
		||||
    }
 | 
			
		||||
    public enum RoundMode { Ceil, Floor };
 | 
			
		||||
 | 
			
		||||
    /// <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);
 | 
			
		||||
 | 
			
		||||
@@ -81,7 +87,7 @@ public static class MathExtensions
 | 
			
		||||
    public static T Lerp<T>(this T x, T y, T t) where T : IFloatingPoint<T> => Math.Lerp(x, y, t);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Math.Round(float, int, MidpointRounding)" />
 | 
			
		||||
    public static float Round(this float x, int digits, MidpointRounding mode) => Math.Round(x, digits, mode);
 | 
			
		||||
    public static float Round(this float x, Math.RoundMode mode) => Math.Round(x, mode);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Math.RoundToInt(float, Math.RoundMode)" />
 | 
			
		||||
    public static int RoundToInt(this float x, Math.RoundMode roundMode = Math.RoundMode.Ceil) => Math.RoundToInt(x, roundMode);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,109 +0,0 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Represents an Axis-Aligned Bounding Box (AABB) in 2D space.
 | 
			
		||||
/// </summary>
 | 
			
		||||
/// <param name="lowerBoundary">The lower boundary of the <see cref="AABB"/>.</param>
 | 
			
		||||
/// <param name="upperBoundary">The upper boundary of the <see cref="AABB"/>.</param>
 | 
			
		||||
/// <remarks>
 | 
			
		||||
/// Initializes a new instance of the <see cref="AABB"/> struct with the specified lower and upper boundaries.
 | 
			
		||||
/// </remarks>
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("LowerBoundary: {LowerBoundary.ToString(), nq}, UpperBoundary: {UpperBoundary.ToString(), nq}")]
 | 
			
		||||
public readonly struct AABB(Vector2D lowerBoundary, Vector2D upperBoundary)
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The lower boundary of the <see cref="AABB"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly Vector2D LowerBoundary = lowerBoundary;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The upper boundary of the <see cref="AABB"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly Vector2D UpperBoundary = upperBoundary;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Gets the center point of the <see cref="AABB"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly Vector2D Center => (LowerBoundary + UpperBoundary) * .5f;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Gets the size of the <see cref="AABB"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly Vector2D Size => LowerBoundary.FromTo(UpperBoundary).Abs();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Gets half the size of the <see cref="AABB"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly Vector2D SizeHalf => Size * .5f;
 | 
			
		||||
 | 
			
		||||
    public static bool operator ==(AABB left, AABB right) => left.UpperBoundary == right.UpperBoundary && left.LowerBoundary == right.LowerBoundary;
 | 
			
		||||
    public static bool operator !=(AABB left, AABB right) => left.UpperBoundary != right.UpperBoundary || left.LowerBoundary != right.LowerBoundary;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Creates an <see cref="AABB"/> from a collection of <see cref="Vector2D"/>s.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="vectors">The collection of <see cref="Vector2D"/>s.</param>
 | 
			
		||||
    /// <returns>An <see cref="AABB"/> that bounds all the <see cref="Vector2D"/>s.</returns>
 | 
			
		||||
    public static AABB FromVectors(IEnumerable<Vector2D> vectors)
 | 
			
		||||
    {
 | 
			
		||||
        int counter = 0;
 | 
			
		||||
 | 
			
		||||
        Vector2D lowerBoundary = new(float.MaxValue, float.MaxValue);
 | 
			
		||||
        Vector2D upperBoundary = new(float.MinValue, float.MinValue);
 | 
			
		||||
 | 
			
		||||
        foreach (Vector2D vector in vectors)
 | 
			
		||||
        {
 | 
			
		||||
            lowerBoundary = Vector2D.Min(lowerBoundary, vector);
 | 
			
		||||
            upperBoundary = Vector2D.Max(upperBoundary, vector);
 | 
			
		||||
            counter++;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (counter < 2)
 | 
			
		||||
            throw new System.ArgumentException($"Parameter {nameof(vectors)} must have at least 2 items.");
 | 
			
		||||
 | 
			
		||||
        return new(lowerBoundary, upperBoundary);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Checks if two <see cref="AABB"/>s are approximately equal.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="left">The first <see cref="AABB"/>.</param>
 | 
			
		||||
    /// <param name="right">The second <see cref="AABB"/>.</param>
 | 
			
		||||
    /// <param name="epsilon">The epsilon range.</param>
 | 
			
		||||
    /// <returns><see cref="true"/> if the <see cref="AABB"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
 | 
			
		||||
    public static bool ApproximatelyEquals(AABB left, AABB right, float epsilon = float.Epsilon)
 | 
			
		||||
        => left.LowerBoundary.ApproximatelyEquals(right.LowerBoundary, epsilon) && left.UpperBoundary.ApproximatelyEquals(right.UpperBoundary, epsilon);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Determines whether the specified object is equal to the current <see cref="AABB"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="obj">The object to compare with the current <see cref="AABB"/>.</param>
 | 
			
		||||
    /// <returns><see cref="true"/> if the specified object is equal to the current <see cref="AABB"/>; otherwise, <see cref="false"/>.</returns>
 | 
			
		||||
    public override bool Equals(object? obj) => obj is AABB aabb && this == aabb;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Generates a hash code for the <see cref="AABB"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>A hash code for the <see cref="AABB"/>.</returns>
 | 
			
		||||
    public override int GetHashCode() => System.HashCode.Combine(LowerBoundary, UpperBoundary);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Converts the <see cref="AABB"/> to its string representation.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>A string representation of the <see cref="AABB"/>.</returns>
 | 
			
		||||
    public override string ToString() => $"{nameof(AABB)}({LowerBoundary}, {UpperBoundary})";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Provides extension methods for the <see cref="AABB"/> struct.
 | 
			
		||||
/// </summary>
 | 
			
		||||
public static class AABBExtensions
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc cref="AABB.ToAABB" />
 | 
			
		||||
    public static AABB ToAABB(this IEnumerable<Vector2D> vectors) => AABB.FromVectors(vectors);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="AABB.ApproximatelyEquals" />
 | 
			
		||||
    public static bool ApproximatelyEquals(this AABB left, AABB right, float epsilon = float.Epsilon) => AABB.ApproximatelyEquals(left, right, epsilon);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										114
									
								
								Engine.Core/Primitives/AABB2D.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								Engine.Core/Primitives/AABB2D.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,114 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Represents an Axis-Aligned Bounding Box (AABB) in 2D space.
 | 
			
		||||
/// </summary>
 | 
			
		||||
/// <param name="lowerBoundary">The lower boundary of the <see cref="AABB2D"/>.</param>
 | 
			
		||||
/// <param name="upperBoundary">The upper boundary of the <see cref="AABB2D"/>.</param>
 | 
			
		||||
/// <remarks>
 | 
			
		||||
/// Initializes a new instance of the <see cref="AABB2D"/> struct with the specified lower and upper boundaries.
 | 
			
		||||
/// </remarks>
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("LowerBoundary: {LowerBoundary.ToString(), nq}, UpperBoundary: {UpperBoundary.ToString(), nq}")]
 | 
			
		||||
public readonly struct AABB2D(Vector2D lowerBoundary, Vector2D upperBoundary) : IEquatable<AABB2D>
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The lower boundary of the <see cref="AABB2D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly Vector2D LowerBoundary = lowerBoundary;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The upper boundary of the <see cref="AABB2D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly Vector2D UpperBoundary = upperBoundary;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Gets the center point of the <see cref="AABB2D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly Vector2D Center => (LowerBoundary + UpperBoundary) * .5f;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Gets the size of the <see cref="AABB2D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly Vector2D Size => LowerBoundary.FromTo(UpperBoundary).Abs();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Gets half the size of the <see cref="AABB2D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly Vector2D SizeHalf => Size * .5f;
 | 
			
		||||
 | 
			
		||||
    public static bool operator ==(AABB2D left, AABB2D right) => left.UpperBoundary == right.UpperBoundary && left.LowerBoundary == right.LowerBoundary;
 | 
			
		||||
    public static bool operator !=(AABB2D left, AABB2D right) => left.UpperBoundary != right.UpperBoundary || left.LowerBoundary != right.LowerBoundary;
 | 
			
		||||
 | 
			
		||||
    public static implicit operator AABB2D(Circle circle) => new(circle.Center - new Vector2D(circle.Radius, circle.Radius), circle.Center + new Vector2D(circle.Radius, circle.Radius));
 | 
			
		||||
    public static implicit operator AABB2D(Shape2D shape) => FromVectors(shape.Vertices);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Creates an <see cref="AABB2D"/> from a collection of <see cref="Vector2D"/>s.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="vectors">The collection of <see cref="Vector2D"/>s.</param>
 | 
			
		||||
    /// <returns>An <see cref="AABB2D"/> that bounds all the <see cref="Vector2D"/>s.</returns>
 | 
			
		||||
    public static AABB2D FromVectors(IEnumerable<Vector2D> vectors)
 | 
			
		||||
    {
 | 
			
		||||
        int counter = 0;
 | 
			
		||||
 | 
			
		||||
        Vector2D lowerBoundary = new(float.MaxValue, float.MaxValue);
 | 
			
		||||
        Vector2D upperBoundary = new(float.MinValue, float.MinValue);
 | 
			
		||||
 | 
			
		||||
        foreach (Vector2D vector in vectors)
 | 
			
		||||
        {
 | 
			
		||||
            lowerBoundary = Vector2D.Min(lowerBoundary, vector);
 | 
			
		||||
            upperBoundary = Vector2D.Max(upperBoundary, vector);
 | 
			
		||||
            counter++;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (counter < 2)
 | 
			
		||||
            throw new ArgumentException($"Parameter {nameof(vectors)} must have at least 2 items.");
 | 
			
		||||
 | 
			
		||||
        return new(lowerBoundary, upperBoundary);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Checks if two <see cref="AABB2D"/>s are approximately equal.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="left">The first <see cref="AABB2D"/>.</param>
 | 
			
		||||
    /// <param name="right">The second <see cref="AABB2D"/>.</param>
 | 
			
		||||
    /// <param name="epsilon">The epsilon range.</param>
 | 
			
		||||
    /// <returns><see cref="true"/> if the <see cref="AABB2D"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
 | 
			
		||||
    public static bool ApproximatelyEquals(AABB2D left, AABB2D right, float epsilon = float.Epsilon)
 | 
			
		||||
        => left.LowerBoundary.ApproximatelyEquals(right.LowerBoundary, epsilon) && left.UpperBoundary.ApproximatelyEquals(right.UpperBoundary, epsilon);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Determines whether the specified object is equal to the current <see cref="AABB2D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="obj">The object to compare with the current <see cref="AABB2D"/>.</param>
 | 
			
		||||
    /// <returns><see cref="true"/> if the specified object is equal to the current <see cref="AABB2D"/>; otherwise, <see cref="false"/>.</returns>
 | 
			
		||||
    public override bool Equals(object? obj) => obj is AABB2D aabb && this == aabb;
 | 
			
		||||
    public bool Equals(AABB2D other) => this == other;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Generates a hash code for the <see cref="AABB2D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>A hash code for the <see cref="AABB2D"/>.</returns>
 | 
			
		||||
    public override int GetHashCode() => System.HashCode.Combine(LowerBoundary, UpperBoundary);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Converts the <see cref="AABB2D"/> to its string representation.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>A string representation of the <see cref="AABB2D"/>.</returns>
 | 
			
		||||
    public override string ToString() => $"{nameof(AABB2D)}({LowerBoundary}, {UpperBoundary})";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Provides extension methods for the <see cref="AABB2D"/> struct.
 | 
			
		||||
/// </summary>
 | 
			
		||||
public static class AABBExtensions
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc cref="AABB2D.FromVectors" />
 | 
			
		||||
    public static AABB2D ToAABB(this IEnumerable<Vector2D> vectors) => AABB2D.FromVectors(vectors);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="AABB2D.ApproximatelyEquals" />
 | 
			
		||||
    public static bool ApproximatelyEquals(this AABB2D left, AABB2D right, float epsilon = float.Epsilon) => AABB2D.ApproximatelyEquals(left, right, epsilon);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										113
									
								
								Engine.Core/Primitives/AABB3D.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								Engine.Core/Primitives/AABB3D.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,113 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Represents an Axis-Aligned Bounding Box (AABB) in 3D space.
 | 
			
		||||
/// </summary>
 | 
			
		||||
/// <param name="lowerBoundary">The lower boundary of the <see cref="AABB3D"/>.</param>
 | 
			
		||||
/// <param name="upperBoundary">The upper boundary of the <see cref="AABB3D"/>.</param>
 | 
			
		||||
/// <remarks>
 | 
			
		||||
/// Initializes a new instance of the <see cref="AABB3D"/> struct with the specified lower and upper boundaries.
 | 
			
		||||
/// </remarks>
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("LowerBoundary: {LowerBoundary.ToString(), nq}, UpperBoundary: {UpperBoundary.ToString(), nq}")]
 | 
			
		||||
public readonly struct AABB3D(Vector3D lowerBoundary, Vector3D upperBoundary) : IEquatable<AABB3D>
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The lower boundary of the <see cref="AABB3D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly Vector3D LowerBoundary = lowerBoundary;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The upper boundary of the <see cref="AABB3D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly Vector3D UpperBoundary = upperBoundary;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Gets the center point of the <see cref="AABB3D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly Vector3D Center => (LowerBoundary + UpperBoundary) * .5f;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Gets the size of the <see cref="AABB3D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly Vector3D Size => LowerBoundary.FromTo(UpperBoundary).Abs();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Gets half the size of the <see cref="AABB3D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly Vector3D SizeHalf => Size * .5f;
 | 
			
		||||
 | 
			
		||||
    public static bool operator ==(AABB3D left, AABB3D right) => left.UpperBoundary == right.UpperBoundary && left.LowerBoundary == right.LowerBoundary;
 | 
			
		||||
    public static bool operator !=(AABB3D left, AABB3D right) => left.UpperBoundary != right.UpperBoundary || left.LowerBoundary != right.LowerBoundary;
 | 
			
		||||
 | 
			
		||||
    public static implicit operator AABB3D(Sphere3D sphere) => new(sphere.Center - new Vector3D(sphere.Radius, sphere.Radius, sphere.Radius), sphere.Center + new Vector3D(sphere.Radius, sphere.Radius, sphere.Radius));
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Creates an <see cref="AABB3D"/> from a collection of <see cref="Vector3D"/>s.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="vectors">The collection of <see cref="Vector3D"/>s.</param>
 | 
			
		||||
    /// <returns>An <see cref="AABB3D"/> that bounds all the <see cref="Vector3D"/>s.</returns>
 | 
			
		||||
    public static AABB3D FromVectors(IEnumerable<Vector3D> vectors)
 | 
			
		||||
    {
 | 
			
		||||
        int counter = 0;
 | 
			
		||||
 | 
			
		||||
        Vector3D lowerBoundary = new(float.MaxValue, float.MaxValue, float.MaxValue);
 | 
			
		||||
        Vector3D upperBoundary = new(float.MinValue, float.MinValue, float.MinValue);
 | 
			
		||||
 | 
			
		||||
        foreach (Vector3D vector in vectors)
 | 
			
		||||
        {
 | 
			
		||||
            lowerBoundary = Vector3D.Min(lowerBoundary, vector);
 | 
			
		||||
            upperBoundary = Vector3D.Max(upperBoundary, vector);
 | 
			
		||||
            counter++;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (counter < 2)
 | 
			
		||||
            throw new ArgumentException($"Parameter {nameof(vectors)} must have at least 2 items.");
 | 
			
		||||
 | 
			
		||||
        return new(lowerBoundary, upperBoundary);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Checks if two <see cref="AABB3D"/>s are approximately equal.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="left">The first <see cref="AABB3D"/>.</param>
 | 
			
		||||
    /// <param name="right">The second <see cref="AABB3D"/>.</param>
 | 
			
		||||
    /// <param name="epsilon">The epsilon range.</param>
 | 
			
		||||
    /// <returns><see cref="true"/> if the <see cref="AABB3D"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
 | 
			
		||||
    public static bool ApproximatelyEquals(AABB3D left, AABB3D right, float epsilon = float.Epsilon)
 | 
			
		||||
        => left.LowerBoundary.ApproximatelyEquals(right.LowerBoundary, epsilon) && left.UpperBoundary.ApproximatelyEquals(right.UpperBoundary, epsilon);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Determines whether the specified object is equal to the current <see cref="AABB3D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="obj">The object to compare with the current <see cref="AABB3D"/>.</param>
 | 
			
		||||
    /// <returns><see cref="true"/> if the specified object is equal to the current <see cref="AABB3D"/>; otherwise, <see cref="false"/>.</returns>
 | 
			
		||||
    public override bool Equals(object? obj) => obj is AABB3D aabb && this == aabb;
 | 
			
		||||
    public bool Equals(AABB3D other) => this == other;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Generates a hash code for the <see cref="AABB3D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>A hash code for the <see cref="AABB3D"/>.</returns>
 | 
			
		||||
    public override int GetHashCode() => System.HashCode.Combine(LowerBoundary, UpperBoundary);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Converts the <see cref="AABB3D"/> to its string representation.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>A string representation of the <see cref="AABB3D"/>.</returns>
 | 
			
		||||
    public override string ToString() => $"{nameof(AABB3D)}({LowerBoundary}, {UpperBoundary})";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Provides extension methods for the <see cref="AABB3D"/> struct.
 | 
			
		||||
/// </summary>
 | 
			
		||||
public static class AABB3DExtensions
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc cref="AABB3D.FromVectors" />
 | 
			
		||||
    public static AABB3D ToAABB3D(this IEnumerable<Vector3D> vectors) => AABB3D.FromVectors(vectors);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="AABB3D.ApproximatelyEquals" />
 | 
			
		||||
    public static bool ApproximatelyEquals(this AABB3D left, AABB3D right, float epsilon = float.Epsilon) => AABB3D.ApproximatelyEquals(left, right, epsilon);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
@@ -11,10 +12,10 @@ namespace Engine.Core;
 | 
			
		||||
/// Initializes a new instance of the <see cref="Circle"/> struct with the specified center and radius.
 | 
			
		||||
/// </remarks>
 | 
			
		||||
[DebuggerDisplay("Center: {Center.ToString(),nq}, Radius: {Radius}")]
 | 
			
		||||
public readonly struct Circle(Vector2D center, float radius)
 | 
			
		||||
public readonly struct Circle(Vector2D center, float radius) : IEquatable<Circle>
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The center of the circle.
 | 
			
		||||
    /// The center of the <see cref="Circle"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly Vector2D Center = center;
 | 
			
		||||
 | 
			
		||||
@@ -41,6 +42,8 @@ public readonly struct Circle(Vector2D center, float radius)
 | 
			
		||||
    public static bool operator ==(Circle left, Circle right) => left.Center == right.Center && left.Radius == right.Radius;
 | 
			
		||||
    public static bool operator !=(Circle left, Circle right) => left.Center != right.Center || left.Radius != right.Radius;
 | 
			
		||||
 | 
			
		||||
    public static implicit operator Circle(Sphere3D sphere) => new(sphere.Center, sphere.Radius);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Sets the center of the <see cref="Circle"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
@@ -87,6 +90,7 @@ public readonly struct Circle(Vector2D center, float radius)
 | 
			
		||||
    /// <param name="obj">The object to compare with the current <see cref="Circle"/>.</param>
 | 
			
		||||
    /// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Circle"/>; otherwise, <see cref="false"/>.</returns>
 | 
			
		||||
    public override bool Equals(object? obj) => obj is Circle circle && this == circle;
 | 
			
		||||
    public bool Equals(Circle other) => this == other;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Generates a hash code for the <see cref="Circle"/>.
 | 
			
		||||
@@ -116,7 +120,7 @@ public static class CircleExtensions
 | 
			
		||||
    public static Circle Displace(this Circle circle, Vector2D displaceVector) => Circle.Displace(circle, displaceVector);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Circle.Project(Circle, Vector2D)" />
 | 
			
		||||
    public static Projection1D ToProjection(this Circle circle, Vector2D projectionVector) => Circle.Project(circle, projectionVector);
 | 
			
		||||
    public static Projection1D ProjectTo(this Circle circle, Vector2D projectionVector) => Circle.Project(circle, projectionVector);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Circle.Transform(ITransform2D, Circle)" />
 | 
			
		||||
    public static Circle Transform(this ITransform2D transform, Circle circle) => Circle.Transform(transform, circle);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
@@ -10,7 +12,7 @@ namespace Engine.Core;
 | 
			
		||||
/// Initializes a new instance of the <see cref="ColorHSV"/> struct with the specified values.
 | 
			
		||||
/// </remarks>
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")]
 | 
			
		||||
public readonly struct ColorHSV(float hue, float saturation, float value)
 | 
			
		||||
public readonly struct ColorHSV(float hue, float saturation, float value) : IEquatable<ColorHSV>
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The Hue value of the <see cref="ColorHSV"/>.
 | 
			
		||||
@@ -112,6 +114,7 @@ public readonly struct ColorHSV(float hue, float saturation, float value)
 | 
			
		||||
    /// <param name="obj">The object to compare with the current <see cref="ColorHSV"/>.</param>
 | 
			
		||||
    /// <returns><see cref="true"/> if the specified object is equal to the current <see cref="ColorHSV"/>; otherwise, <see cref="false"/>.</returns>
 | 
			
		||||
    public override bool Equals(object? obj) => obj is ColorHSV colorHSV && this == colorHSV;
 | 
			
		||||
    public bool Equals(ColorHSV other) => this == other;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Generates a hash code for the <see cref="ColorHSV"/>.
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
@@ -11,7 +13,7 @@ namespace Engine.Core;
 | 
			
		||||
/// Initializes a new instance of the <see cref="ColorHSVA"/> struct with the specified values.
 | 
			
		||||
/// </remarks>
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")]
 | 
			
		||||
public readonly struct ColorHSVA(float hue, float saturation, float value, float alpha = 1)
 | 
			
		||||
public readonly struct ColorHSVA(float hue, float saturation, float value, float alpha = 1) : IEquatable<ColorHSVA>
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The Hue value of the <see cref="ColorHSVA"/>.
 | 
			
		||||
@@ -150,6 +152,7 @@ public readonly struct ColorHSVA(float hue, float saturation, float value, float
 | 
			
		||||
    /// <param name="obj">The object to compare with the current <see cref="ColorHSVA"/>.</param>
 | 
			
		||||
    /// <returns><see cref="true"/> if the specified object is equal to the current <see cref="ColorHSVA"/>; otherwise, <see cref="false"/>.</returns>
 | 
			
		||||
    public override bool Equals(object? obj) => obj is ColorHSVA colorHSVA && this == colorHSVA;
 | 
			
		||||
    public bool Equals(ColorHSVA other) => this == other;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Generates a hash code for the <see cref="ColorHSVA"/>.
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
@@ -10,7 +12,7 @@ namespace Engine.Core;
 | 
			
		||||
/// Initializes a new instance of the <see cref="ColorRGB"/> struct with the specified values.
 | 
			
		||||
/// </remarks>
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")]
 | 
			
		||||
public readonly struct ColorRGB(byte r, byte g, byte b)
 | 
			
		||||
public readonly struct ColorRGB(byte r, byte g, byte b) : IEquatable<ColorRGB>
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The Red value of the <see cref="ColorRGB"/>.
 | 
			
		||||
@@ -102,6 +104,7 @@ public readonly struct ColorRGB(byte r, byte g, byte b)
 | 
			
		||||
    /// <param name="obj">The object to compare with the current <see cref="ColorRGB"/>.</param>
 | 
			
		||||
    /// <returns><see cref="true"/> if the specified object is equal to the current <see cref="ColorRGB"/>; otherwise, <see cref="false"/>.</returns>
 | 
			
		||||
    public override bool Equals(object? obj) => obj is ColorRGB colorRGB && this == colorRGB;
 | 
			
		||||
    public bool Equals(ColorRGB other) => this == other;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Generates a hash code for the <see cref="ColorRGB"/>.
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
@@ -11,7 +13,7 @@ namespace Engine.Core;
 | 
			
		||||
/// Initializes a new instance of the <see cref="ColorRGBA"/> struct with the specified values.
 | 
			
		||||
/// </remarks>
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")]
 | 
			
		||||
public readonly struct ColorRGBA(byte r, byte g, byte b, byte a = 255)
 | 
			
		||||
public readonly struct ColorRGBA(byte r, byte g, byte b, byte a = 255) : IEquatable<ColorRGBA>
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The Red value of the <see cref="ColorRGBA"/>.
 | 
			
		||||
@@ -132,6 +134,7 @@ public readonly struct ColorRGBA(byte r, byte g, byte b, byte a = 255)
 | 
			
		||||
    /// <param name="obj">The object to compare with the current <see cref="ColorRGBA"/>.</param>
 | 
			
		||||
    /// <returns><see cref="true"/> if the specified object is equal to the current <see cref="ColorRGBA"/>; otherwise, <see cref="false"/>.</returns>
 | 
			
		||||
    public override bool Equals(object? obj) => obj is ColorRGBA colorRGBA && this == colorRGBA;
 | 
			
		||||
    public bool Equals(ColorRGBA other) => this == other;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Generates a hash code for the <see cref="ColorRGBA"/>.
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Diagnostics.CodeAnalysis;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
@@ -11,7 +12,7 @@ namespace Engine.Core;
 | 
			
		||||
/// Initializes a new instance of the <see cref="Line2D"/> struct with the specified endpoints.
 | 
			
		||||
/// </remarks>
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("From: {From.ToString(),nq}, To: {To.ToString(),nq}, Direction: {Direction.ToString(),nq}, Length: {Length}")]
 | 
			
		||||
public readonly struct Line2D(Vector2D from, Vector2D to)
 | 
			
		||||
public readonly struct Line2D(Vector2D from, Vector2D to) : IEquatable<Line2D>
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The starting point of the <see cref="Line2D"/> segment.
 | 
			
		||||
@@ -49,15 +50,7 @@ public readonly struct Line2D(Vector2D from, Vector2D to)
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The equation of the <see cref="Line2D"/> defined by this <see cref="Line2D"/> segment.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static Line2DEquation GetLineEquation(Line2D line)
 | 
			
		||||
    {
 | 
			
		||||
        Vector2D slopeVector = line.From.FromTo(line.To);
 | 
			
		||||
        float slope = slopeVector.Y / slopeVector.X;
 | 
			
		||||
 | 
			
		||||
        float yOffset = line.From.Y - (slope * line.From.X);
 | 
			
		||||
 | 
			
		||||
        return new Line2DEquation(slope, yOffset);
 | 
			
		||||
    }
 | 
			
		||||
    public static Line2DEquation GetLineEquation(Line2D line) => line;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Determines whether the specified <see cref="Vector2D"/> lies on the <see cref="Line2D"/>.
 | 
			
		||||
@@ -196,6 +189,7 @@ public readonly struct Line2D(Vector2D from, Vector2D to)
 | 
			
		||||
    /// <param name="obj">The object to compare with the current <see cref="Line2D"/>.</param>
 | 
			
		||||
    /// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Line2D"/>; otherwise, <see cref="false"/>.</returns>
 | 
			
		||||
    public override bool Equals(object? obj) => obj is Line2D line2D && this == line2D;
 | 
			
		||||
    public bool Equals(Line2D other) => this == other;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Generates a hash code for the <see cref="Line2D"/>.
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
@@ -9,7 +11,7 @@ namespace Engine.Core;
 | 
			
		||||
/// Initializes a new instance of the <see cref="Line2DEquation"/> struct with the specified slope and Y intercept.
 | 
			
		||||
/// </remarks>
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("y = {Slope}x + {OffsetY}")]
 | 
			
		||||
public readonly struct Line2DEquation(float slope, float offsetY)
 | 
			
		||||
public readonly struct Line2DEquation(float slope, float offsetY) : IEquatable<Line2DEquation>
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The slope of the <see cref="Line2DEquation"/>.
 | 
			
		||||
@@ -24,6 +26,16 @@ public readonly struct Line2DEquation(float slope, float offsetY)
 | 
			
		||||
    public static bool operator ==(Line2DEquation left, Line2DEquation right) => left.Slope == right.Slope && left.OffsetY == right.OffsetY;
 | 
			
		||||
    public static bool operator !=(Line2DEquation left, Line2DEquation right) => left.Slope != right.Slope || left.OffsetY != right.OffsetY;
 | 
			
		||||
 | 
			
		||||
    public static implicit operator Line2DEquation(Line2D line)
 | 
			
		||||
    {
 | 
			
		||||
        Vector2D slopeVector = line.From.FromTo(line.To);
 | 
			
		||||
        float slope = slopeVector.Y / slopeVector.X;
 | 
			
		||||
 | 
			
		||||
        float yOffset = line.From.Y - (slope * line.From.X);
 | 
			
		||||
 | 
			
		||||
        return new Line2DEquation(slope, yOffset);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Resolves the Y coordinate for a given X coordinate using the <see cref="Line2DEquation"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
@@ -48,6 +60,7 @@ public readonly struct Line2DEquation(float slope, float offsetY)
 | 
			
		||||
    /// <param name="obj">The object to compare with the current <see cref="Line2DEquation"/>.</param>
 | 
			
		||||
    /// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Line2DEquation"/>; otherwise, <see cref="false"/>.</returns>
 | 
			
		||||
    public override bool Equals(object? obj) => obj is Line2DEquation lineEquation && this == lineEquation;
 | 
			
		||||
    public bool Equals(Line2DEquation other) => this == other;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Generates a hash code for the <see cref="Line2DEquation"/>.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										114
									
								
								Engine.Core/Primitives/Line3D.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								Engine.Core/Primitives/Line3D.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,114 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Represents a 3D line segment defined by two endpoints.
 | 
			
		||||
/// </summary>
 | 
			
		||||
/// <param name="from">The starting point of the <see cref="Line3D"/> segment.</param>
 | 
			
		||||
/// <param name="to">The ending point of the <see cref="Line3D"/> segment.</param>
 | 
			
		||||
/// <remarks>
 | 
			
		||||
/// Initializes a new instance of the <see cref="Line3D"/> struct with the specified endpoints.
 | 
			
		||||
/// </remarks>
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("From: {From.ToString(),nq}, To: {To.ToString(),nq}, Direction: {Direction.ToString(),nq}, Length: {Length}")]
 | 
			
		||||
public readonly struct Line3D(Vector3D from, Vector3D to) : IEquatable<Line3D>
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The starting point of the <see cref="Line3D"/> segment.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly Vector3D From = from;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The ending point of the <see cref="Line3D"/> segment.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly Vector3D To = to;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The reversed <see cref="Line3D"/> segment.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly Line3D Reversed => new(To, From);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The normalized direction <see cref="Vector3D"/> of the <see cref="Line3D"/> segment.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly Vector3D Direction => From.FromTo(To).Normalize();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The length of the <see cref="Line3D"/> segment.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly float Length => From.FromTo(To).Length();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The squared length of the <see cref="Line3D"/> segment.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly float LengthSquared => From.FromTo(To).LengthSquared();
 | 
			
		||||
 | 
			
		||||
    public static bool operator ==(Line3D left, Line3D right) => left.From == right.From && left.To == right.To;
 | 
			
		||||
    public static bool operator !=(Line3D left, Line3D right) => left.From != right.From || left.To != right.To;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Linearly interpolates between the two endpoints of the <see cref="Line3D"/> segment using parameter 't'.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static Vector3D Lerp(Line3D line, float t)
 | 
			
		||||
        => Vector3D.Lerp(line.From, line.To, t);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Calculates the closest point on the <see cref="Line3D"/> segment to the specified point.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static Vector3D ClosestPointTo(Line3D line, Vector3D point)
 | 
			
		||||
    {
 | 
			
		||||
        Vector3D lineRelativeVector = line.From.FromTo(line.To);
 | 
			
		||||
 | 
			
		||||
        Vector3D lineDirection = lineRelativeVector.Normalized;
 | 
			
		||||
        Vector3D pointVector = line.From.FromTo(point);
 | 
			
		||||
 | 
			
		||||
        float dot = lineDirection.Dot(pointVector).Clamp(0f, lineRelativeVector.Magnitude);
 | 
			
		||||
 | 
			
		||||
        return lineDirection * dot;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Checks if two <see cref="Line3D"/> segments are approximately equal.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="left">The first <see cref="Line3D"/>.</param>
 | 
			
		||||
    /// <param name="right">The second <see cref="Line3D"/>.</param>
 | 
			
		||||
    /// <param name="epsilon">The epsilon range.</param>
 | 
			
		||||
    /// <returns><see cref="true"/> if the <see cref="Line3D"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
 | 
			
		||||
    public static bool ApproximatelyEquals(Line3D left, Line3D right, float epsilon = float.Epsilon)
 | 
			
		||||
        => left.From.ApproximatelyEquals(right.From, epsilon) && left.To.ApproximatelyEquals(right.To, epsilon);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Determines whether the specified object is equal to the current <see cref="Line3D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="obj">The object to compare with the current <see cref="Line3D"/>.</param>
 | 
			
		||||
    /// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Line3D"/>; otherwise, <see cref="false"/>.</returns>
 | 
			
		||||
    public override bool Equals(object? obj) => obj is Line3D line3D && this == line3D;
 | 
			
		||||
    public bool Equals(Line3D other) => this == other;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Generates a hash code for the <see cref="Line3D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>A hash code for the <see cref="Line3D"/>.</returns>
 | 
			
		||||
    public override int GetHashCode() => System.HashCode.Combine(From, To);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Converts the <see cref="Line3D"/> to its string representation.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>A string representation of the <see cref="Line3D"/>.</returns>
 | 
			
		||||
    public override string ToString() => $"{nameof(Line3D)}({From}, {To})";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Provides extension methods for the <see cref="Line3D"/> struct.
 | 
			
		||||
/// </summary>
 | 
			
		||||
public static class Line3DExtensions
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc cref="Line3D.Lerp(Line3D, float)" />
 | 
			
		||||
    public static Vector3D Lerp(this Line3D line, float t) => Line3D.Lerp(line, t);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Line3D.ClosestPointTo(Line3D, Vector3D)" />
 | 
			
		||||
    public static Vector3D ClosestPointTo(this Line3D line, Vector3D point) => Line3D.ClosestPointTo(line, point);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Line3D.ApproximatelyEquals(Line3D, Line3D, float)" />
 | 
			
		||||
    public static bool ApproximatelyEquals(this Line3D left, Line3D right, float epsilon = float.Epsilon) => Line3D.ApproximatelyEquals(left, right, epsilon);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										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);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
@@ -9,7 +11,7 @@ namespace Engine.Core;
 | 
			
		||||
/// Initializes a new instance of the <see cref="Projection1D"/> struct with the specified minimum and maximum values.
 | 
			
		||||
/// </remarks>
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("Min: {Min}, Max: {Max}")]
 | 
			
		||||
public readonly struct Projection1D(float min, float max)
 | 
			
		||||
public readonly struct Projection1D(float min, float max) : IEquatable<Projection1D>
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Gets the minimum value of the projection.
 | 
			
		||||
@@ -90,6 +92,7 @@ public readonly struct Projection1D(float min, float max)
 | 
			
		||||
    /// <param name="obj">The object to compare with the current <see cref="Projection1D"/>.</param>
 | 
			
		||||
    /// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Projection1D"/>; otherwise, <see cref="false"/>.</returns>
 | 
			
		||||
    public override bool Equals(object? obj) => obj is Projection1D projection1D && this == projection1D;
 | 
			
		||||
    public bool Equals(Projection1D other) => this == other;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Generates a hash code for the <see cref="Projection1D"/>.
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
@@ -11,7 +13,7 @@ namespace Engine.Core;
 | 
			
		||||
/// Initializes a new instance of the <see cref="Quaternion"/> struct with the specified positions.
 | 
			
		||||
/// </remarks>
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}, Length: {Magnitude}, LengthSquared: {MagnitudeSquared}, Normalized: {Normalized.ToString(),nq}")]
 | 
			
		||||
public readonly struct Quaternion(float x, float y, float z, float w)
 | 
			
		||||
public readonly struct Quaternion(float x, float y, float z, float w) : IEquatable<Quaternion>
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The X(i) imaginary of the <see cref="Quaternion"/>.
 | 
			
		||||
@@ -77,6 +79,30 @@ public readonly struct Quaternion(float x, float y, float z, float w)
 | 
			
		||||
    public static implicit operator Quaternion(System.Numerics.Quaternion quaternion) => new(quaternion.X, quaternion.Y, quaternion.Z, quaternion.W);
 | 
			
		||||
    public static implicit operator System.Numerics.Quaternion(Quaternion quaternion) => new(quaternion.X, quaternion.Y, quaternion.Z, quaternion.W);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Get the Pitch, Yaw and Roll of the <see cref="Quaternion"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static Vector3D ToAngles(Quaternion quaternion)
 | 
			
		||||
    {
 | 
			
		||||
        // Quaternion to Euler angles (in 3-2-1 sequence) conversion
 | 
			
		||||
        float sinr_cosp = 2f * (quaternion.W * quaternion.X + quaternion.Y * quaternion.Z);
 | 
			
		||||
        float cosr_cosp = 1f - 2f * (quaternion.X * quaternion.X + quaternion.Y * quaternion.Y);
 | 
			
		||||
        float pitch = MathF.Atan2(sinr_cosp, cosr_cosp);
 | 
			
		||||
 | 
			
		||||
        float sinp = 2f * (quaternion.W * quaternion.Y - quaternion.Z * quaternion.X);
 | 
			
		||||
        float yaw;
 | 
			
		||||
        if (MathF.Abs(sinp) >= 1f)
 | 
			
		||||
            yaw = MathF.CopySign(MathF.PI / 2f, sinp);
 | 
			
		||||
        else
 | 
			
		||||
            yaw = MathF.Asin(sinp);
 | 
			
		||||
 | 
			
		||||
        float siny_cosp = 2f * (quaternion.W * quaternion.Z + quaternion.X * quaternion.Y);
 | 
			
		||||
        float cosy_cosp = 1f - 2f * (quaternion.Y * quaternion.Y + quaternion.Z * quaternion.Z);
 | 
			
		||||
        float roll = MathF.Atan2(siny_cosp, cosy_cosp);
 | 
			
		||||
 | 
			
		||||
        return new Vector3D(pitch, yaw, roll) * Math.RadianToDegree;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Calculates the length of the <see cref="Quaternion"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
@@ -130,6 +156,36 @@ public readonly struct Quaternion(float x, float y, float z, float w)
 | 
			
		||||
    /// <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>
 | 
			
		||||
    /// <param name="vector">The <see cref="Quaternion"/> to rotate.</param>
 | 
			
		||||
    /// <param name="axis">The <see cref="Quaternion"/> to rotate around.</param>
 | 
			
		||||
    /// <param name="angleInRadian">The angle to rotate by, in radians.</param>
 | 
			
		||||
    /// <returns>The rotated <see cref="Quaternion"/>.</returns>
 | 
			
		||||
    public static Quaternion Rotate(Quaternion vector, Vector3D axis, float angleInRadian) => vector * Quaternion.FromAxisAngle(axis, angleInRadian);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Inverts the direction of the <see cref="Quaternion"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
@@ -152,8 +208,12 @@ public readonly struct Quaternion(float x, float y, float z, float w)
 | 
			
		||||
    /// <returns>The rotated <see cref="Vector3D"/>.</returns>
 | 
			
		||||
    public static Vector3D RotateVector(Vector3D vector, Quaternion quaternion)
 | 
			
		||||
    {
 | 
			
		||||
        Quaternion rotation = quaternion * new Quaternion(vector.X, vector.Y, vector.Z, 0) * Invert(quaternion);
 | 
			
		||||
        return new(rotation.X, rotation.Y, rotation.Z);
 | 
			
		||||
        // https://blog.molecular-matters.com/2013/05/24/a-faster-quaternion-vector-multiplication/
 | 
			
		||||
        // t = 2 * cross(q.xyz, v)
 | 
			
		||||
        // v' = v + q.w * t + cross(q.xyz, t)
 | 
			
		||||
        Vector3D quaternionVector = new(quaternion.X, quaternion.Y, quaternion.Z);
 | 
			
		||||
        Vector3D t = 2f * quaternionVector.Cross(vector);
 | 
			
		||||
        return vector + quaternion.W * t + quaternionVector.Cross(t);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
@@ -173,8 +233,8 @@ public readonly struct Quaternion(float x, float y, float z, float w)
 | 
			
		||||
            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);
 | 
			
		||||
@@ -208,9 +268,9 @@ public readonly struct Quaternion(float x, float y, float z, float w)
 | 
			
		||||
    /// <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="Quaternion"/> calculated by the given parameters.</returns>
 | 
			
		||||
    public static Quaternion FromAxisAngle(Vector3D axis, float angle)
 | 
			
		||||
    public static Quaternion FromAxisAngle(Vector3D axis, float angleInRadian)
 | 
			
		||||
    {
 | 
			
		||||
        float halfAngle = angle * .5f;
 | 
			
		||||
        float halfAngle = angleInRadian * .5f;
 | 
			
		||||
        float sinHalf = Math.Sin(halfAngle);
 | 
			
		||||
        return new Quaternion(axis.X * sinHalf, axis.Y * sinHalf, axis.Z * sinHalf, Math.Cos(halfAngle));
 | 
			
		||||
    }
 | 
			
		||||
@@ -237,12 +297,11 @@ public readonly struct Quaternion(float x, float y, float z, float w)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <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);
 | 
			
		||||
@@ -272,6 +331,52 @@ public readonly struct Quaternion(float x, float y, float z, float w)
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <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>
 | 
			
		||||
@@ -288,18 +393,19 @@ public readonly struct Quaternion(float x, float y, float z, float w)
 | 
			
		||||
    /// <param name="obj">The object to compare with the current <see cref="Quaternion"/>.</param>
 | 
			
		||||
    /// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Quaternion"/>; otherwise, <see cref="false"/>.</returns>
 | 
			
		||||
    public override bool Equals(object? obj) => obj is Quaternion quaternion && this == quaternion;
 | 
			
		||||
    public bool Equals(Quaternion other) => this == other;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Generates a hash code for the <see cref="Quaternion"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>A hash code for the <see cref="Quaternion"/>.</returns>
 | 
			
		||||
    public override int GetHashCode() => System.HashCode.Combine(W, X, Y, Z);
 | 
			
		||||
    public override int GetHashCode() => System.HashCode.Combine(X, Y, Z, W);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Converts the <see cref="Quaternion"/> to its string representation.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>A string representation of the <see cref="Quaternion"/>.</returns>
 | 
			
		||||
    public override string ToString() => $"{nameof(Quaternion)}({W}, {X}, {Y}, {Z})";
 | 
			
		||||
    public override string ToString() => $"{nameof(Quaternion)}({X}, {Y}, {Z}, {W})";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
@@ -307,6 +413,9 @@ public readonly struct Quaternion(float x, float y, float z, float w)
 | 
			
		||||
/// </summary>
 | 
			
		||||
public static class QuaternionExtensions
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc cref="Quaternion.ToAngles(Quaternion)" />
 | 
			
		||||
    public static Vector3D ToAngles(this Quaternion quaternion) => Quaternion.ToAngles(quaternion);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Quaternion.Length(Quaternion)" />
 | 
			
		||||
    public static float Length(this Quaternion quaternion) => Quaternion.Length(quaternion);
 | 
			
		||||
 | 
			
		||||
@@ -328,6 +437,9 @@ public static class QuaternionExtensions
 | 
			
		||||
    /// <inheritdoc cref="Quaternion.Normalize(Quaternion)" />
 | 
			
		||||
    public static Quaternion Normalize(this Quaternion quaternion) => Quaternion.Normalize(quaternion);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Quaternion.Rotate(Quaternion, Vector3D, float)" />
 | 
			
		||||
    public static Quaternion Rotate(this Quaternion vector, Vector3D normal, float angleInRadian) => Quaternion.Rotate(vector, normal, angleInRadian);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Quaternion.Invert(Quaternion)" />
 | 
			
		||||
    public static Quaternion Invert(this Quaternion quaternion) => Quaternion.Invert(quaternion);
 | 
			
		||||
 | 
			
		||||
@@ -347,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,3 +1,5 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
@@ -5,7 +7,7 @@ namespace Engine.Core;
 | 
			
		||||
/// </summary>
 | 
			
		||||
/// <param name="Origin">The <see cref="Vector2D"/> in 2D space where the ray starts from.</param>
 | 
			
		||||
/// <param name="Direction">Normalized <see cref="Vector2D"/> indicating the ray's is direction.</param> 
 | 
			
		||||
public readonly struct Ray2D(Vector2D Origin, Vector2D Direction)
 | 
			
		||||
public readonly struct Ray2D(Vector2D Origin, Vector2D Direction) : IEquatable<Ray2D>
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The starting point of the <see cref="Ray2D"/>.
 | 
			
		||||
@@ -72,6 +74,7 @@ public readonly struct Ray2D(Vector2D Origin, Vector2D Direction)
 | 
			
		||||
    /// <param name="obj">The object to compare with the current <see cref="Ray2D"/>.</param>
 | 
			
		||||
    /// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Ray2D"/>; otherwise, <see cref="false"/>.</returns>
 | 
			
		||||
    public override bool Equals(object? obj) => obj is Ray2D ray2D && this == ray2D;
 | 
			
		||||
    public bool Equals(Ray2D other) => this == other;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Generates a hash code for the <see cref="Ray2D"/>.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										109
									
								
								Engine.Core/Primitives/Ray3D.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								Engine.Core/Primitives/Ray3D.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,109 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Represents an infinite ray in 3D space.
 | 
			
		||||
/// </summary>
 | 
			
		||||
/// <param name="Origin">The <see cref="Vector3D"/> in 3D space where the ray starts from.</param>
 | 
			
		||||
/// <param name="Direction">Normalized <see cref="Vector3D"/> indicating the ray's is direction.</param> 
 | 
			
		||||
public readonly struct Ray3D(Vector3D Origin, Vector3D Direction) : IEquatable<Ray3D>
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The starting point of the <see cref="Ray3D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly Vector3D Origin = Origin;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The direction in which the <see cref="Ray3D"/> points. Should be a normalized vector.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly Vector3D Direction = Direction;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Gets a <see cref="Ray3D"/> with the same origin but with the direction reversed.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly Ray3D Reversed => new(Origin, -Direction);
 | 
			
		||||
 | 
			
		||||
    public static bool operator ==(Ray3D left, Ray3D right) => left.Origin == right.Origin && left.Direction == right.Direction;
 | 
			
		||||
    public static bool operator !=(Ray3D left, Ray3D right) => left.Origin != right.Origin || left.Direction != right.Direction;
 | 
			
		||||
    
 | 
			
		||||
    public static implicit operator Ray3D(Line3D line) => new(line.From, line.From.FromTo(line.To).Normalized);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Constructs a <see cref="Line3D"/> from a <see cref="Ray3D"/>, extending from its origin in the <see cref="Ray3D"/>'s direction for a given distance.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="ray">The source <see cref="Ray3D"/>.</param>
 | 
			
		||||
    /// <param name="distance">The length of the line segment to create from the <see cref="Ray3D"/>.</param>
 | 
			
		||||
    /// <returns>A <see cref="Line3D"/> representing the segment of the <see cref="Ray3D"/>.</returns>
 | 
			
		||||
    public static Line3D GetLine(Ray3D ray, float distance)
 | 
			
		||||
        => new(ray.Origin, ray.Origin + ray.Direction * distance);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Evaluates the point on the <see cref="Ray3D"/> at a specified distance from its origin.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="ray">The <see cref="Ray3D"/> to evaluate.</param>
 | 
			
		||||
    /// <param name="distanceFromOrigin">The distance from the origin along the <see cref="Ray3D"/>'s direction.</param>
 | 
			
		||||
    /// <returns>A <see cref="Vector3D"/> representing the point at the given distance on the <see cref="Ray3D"/>.</returns>
 | 
			
		||||
    public static Vector3D Evaluate(Ray3D ray, float distanceFromOrigin)
 | 
			
		||||
        => ray.Origin + ray.Direction * distanceFromOrigin;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Calculates the closest point on the <see cref="Ray3D"/> to the specified point.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static Vector3D ClosestPointTo(Ray3D ray, Vector3D point)
 | 
			
		||||
    {
 | 
			
		||||
        Vector3D originToPoint = ray.Origin.FromTo(point);
 | 
			
		||||
 | 
			
		||||
        float dot = ray.Direction.Dot(originToPoint);
 | 
			
		||||
 | 
			
		||||
        return ray.Origin + ray.Direction * dot;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Checks if two <see cref="Ray3D"/>s are approximately equal within a specified epsilon range.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="left">The first <see cref="Ray3D"/>.</param>
 | 
			
		||||
    /// <param name="right">The second <see cref="Ray3D"/>.</param>
 | 
			
		||||
    /// <param name="epsilon">The epsilon range.</param>
 | 
			
		||||
    /// <returns><see cref="true"/> if the <see cref="Ray3D"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
 | 
			
		||||
    public static bool ApproximatelyEquals(Ray3D left, Ray3D right, float epsilon = float.Epsilon)
 | 
			
		||||
        => left.Origin.ApproximatelyEquals(right.Origin, epsilon) && left.Direction.ApproximatelyEquals(right.Direction, epsilon);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Determines whether the specified object is equal to the current <see cref="Ray3D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="obj">The object to compare with the current <see cref="Ray3D"/>.</param>
 | 
			
		||||
    /// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Ray3D"/>; otherwise, <see cref="false"/>.</returns>
 | 
			
		||||
    public override bool Equals(object? obj) => obj is Ray3D ray3D && this == ray3D;
 | 
			
		||||
    public bool Equals(Ray3D other) => this == other;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Generates a hash code for the <see cref="Ray3D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>A hash code for the <see cref="Ray3D"/>.</returns>
 | 
			
		||||
    public override int GetHashCode() => System.HashCode.Combine(Origin, Direction);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Converts the <see cref="Ray3D"/> to its string representation.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>A string representation of the <see cref="Ray3D"/>.</returns>
 | 
			
		||||
    public override string ToString() => $"{nameof(Ray3D)}({Origin}, {Direction})";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Provides extension methods for the <see cref="Ray3D"/> struct.
 | 
			
		||||
/// </summary>
 | 
			
		||||
public static class Ray3DExtensions
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc cref="Ray3D.GetLine(Ray3D, float) />
 | 
			
		||||
    public static Line3D ToLine(this Ray3D ray, float distance) => Ray3D.GetLine(ray, distance);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Ray3D.Evaluate(Ray3D, float) />
 | 
			
		||||
    public static Vector3D Evaluate(this Ray3D ray, float distanceFromOrigin) => Ray3D.Evaluate(ray, distanceFromOrigin);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Ray3D.ClosestPointTo(Ray3D, Vector3D) />
 | 
			
		||||
    public static Vector3D ClosestPointTo(this Ray3D ray, Vector3D point) => Ray3D.ClosestPointTo(ray, point);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Ray3D.ApproximatelyEquals(Ray3D, Ray3D, float)" />
 | 
			
		||||
    public static bool ApproximatelyEquals(this Ray3D left, Ray3D right, float epsilon = float.Epsilon) => Ray3D.ApproximatelyEquals(left, right, epsilon);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										131
									
								
								Engine.Core/Primitives/Sphere3D.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								Engine.Core/Primitives/Sphere3D.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,131 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Represents a 3D sphere.
 | 
			
		||||
/// </summary>
 | 
			
		||||
/// <param name="center">The center of the sphere.</param>
 | 
			
		||||
/// <param name="radius">The radius of the sphere.</param>
 | 
			
		||||
/// <remarks>
 | 
			
		||||
/// Initializes a new instance of the <see cref="Sphere3D"/> struct with the specified center and radius.
 | 
			
		||||
/// </remarks>
 | 
			
		||||
[DebuggerDisplay("Center: {Center.ToString(),nq}, Radius: {Radius}")]
 | 
			
		||||
public readonly struct Sphere3D(Vector3D center, float radius) : IEquatable<Sphere3D>
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The center of the <see cref="Sphere3D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly Vector3D Center = center;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The radius of the <see cref="Sphere3D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly float Radius = radius;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Gets the squared radius of the <see cref="Sphere3D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly float RadiusSquared => Radius * Radius;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Gets the diameter of the <see cref="Sphere3D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly float Diameter => 2f * Radius;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// A predefined unit <see cref="Sphere3D"/> with a center at the origin and a radius of 1.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static readonly Sphere3D UnitSphere = new(Vector3D.Zero, 1f);
 | 
			
		||||
 | 
			
		||||
    public static bool operator ==(Sphere3D left, Sphere3D right) => left.Center == right.Center && left.Radius == right.Radius;
 | 
			
		||||
    public static bool operator !=(Sphere3D left, Sphere3D right) => left.Center != right.Center || left.Radius != right.Radius;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Sets the center of the <see cref="Sphere3D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static Sphere3D SetCenter(Sphere3D sphere, Vector3D center) => new(center, sphere.Radius);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Sets the radius of the <see cref="Sphere3D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static Sphere3D SetRadius(Sphere3D sphere, float radius) => new(sphere.Center, radius);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Displaces the <see cref="Sphere3D"/> by the specified <see cref="Vector3D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static Sphere3D Displace(Sphere3D sphere, Vector3D displaceVector) => new(sphere.Center + displaceVector, sphere.Radius);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Projects the <see cref="Sphere3D"/> onto the specified <see cref="Vector3D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static Projection1D Project(Sphere3D sphere, Vector3D projectionVector)
 | 
			
		||||
    {
 | 
			
		||||
        float projectedCenter = sphere.Center.Dot(projectionVector);
 | 
			
		||||
        return new(projectedCenter - sphere.Radius, projectedCenter + sphere.Radius);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Transforms the <see cref="Sphere3D"/> by the specified <see cref="ITransform3D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static Sphere3D Transform(ITransform3D transform, Sphere3D sphere)
 | 
			
		||||
        => new(transform.Transform(sphere.Center), sphere.Radius * (transform.Scale.Magnitude / Vector3D.One.Magnitude));
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Checks if two <see cref="Sphere3D"/>s are approximately equal.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="left">The first <see cref="Sphere3D"/>.</param>
 | 
			
		||||
    /// <param name="right">The second <see cref="Sphere3D"/>.</param>
 | 
			
		||||
    /// <param name="epsilon">The epsilon range.</param>
 | 
			
		||||
    /// <returns><see cref="true"/> if the <see cref="Sphere3D"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
 | 
			
		||||
    public static bool ApproximatelyEquals(Sphere3D left, Sphere3D right, float epsilon = float.Epsilon)
 | 
			
		||||
        => left.Center.ApproximatelyEquals(right.Center, epsilon) && left.Radius.ApproximatelyEquals(right.Radius, epsilon);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Determines whether the specified object is equal to the current <see cref="Sphere3D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="obj">The object to compare with the current <see cref="Sphere3D"/>.</param>
 | 
			
		||||
    /// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Sphere3D"/>; otherwise, <see cref="false"/>.</returns>
 | 
			
		||||
    public override bool Equals(object? obj) => obj is Sphere3D sphere && this == sphere;
 | 
			
		||||
    public bool Equals(Sphere3D other) => this == other;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Generates a hash code for the <see cref="Sphere3D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>A hash code for the <see cref="Sphere3D"/>.</returns>
 | 
			
		||||
    public override int GetHashCode() => System.HashCode.Combine(Center, Radius);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Converts the <see cref="Sphere3D"/> to its string representation.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>A string representation of the <see cref="Sphere3D"/>.</returns>
 | 
			
		||||
    public override string ToString() => $"{nameof(Sphere3D)}({Center}, {Radius})";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Provides extension methods for the <see cref="Sphere3D"/> struct.
 | 
			
		||||
/// </summary>
 | 
			
		||||
public static class SphereExtensions
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc cref="Sphere3D.SetCenter(Sphere3D, Vector3D)" />
 | 
			
		||||
    public static Sphere3D SetCenter(this Sphere3D sphere, Vector3D center) => Sphere3D.SetCenter(sphere, center);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Sphere3D.SetRadius(Sphere3D, float)" />
 | 
			
		||||
    public static Sphere3D SetRadius(this Sphere3D sphere, float radius) => Sphere3D.SetRadius(sphere, radius);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Sphere3D.Displace(Sphere3D, Vector3D)" />
 | 
			
		||||
    public static Sphere3D Displace(this Sphere3D sphere, Vector3D displaceVector) => Sphere3D.Displace(sphere, displaceVector);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Sphere3D.Project(Sphere3D, Vector3D)" />
 | 
			
		||||
    public static Projection1D ProjectTo(this Sphere3D sphere, Vector3D projectionVector) => Sphere3D.Project(sphere, projectionVector);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Sphere3D.Transform(ITransform3D, Sphere3D)" />
 | 
			
		||||
    public static Sphere3D Transform(this ITransform3D transform, Sphere3D sphere) => Sphere3D.Transform(transform, sphere);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Sphere3D.Transform(ITransform3D, Sphere3D)" />
 | 
			
		||||
    public static Sphere3D Transform(this Sphere3D sphere, ITransform3D transform) => Sphere3D.Transform(transform, sphere);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Sphere3D.ApproximatelyEquals(Sphere3D, Sphere3D, float)" />
 | 
			
		||||
    public static bool ApproximatelyEquals(this Sphere3D left, Sphere3D right, float epsilon = float.Epsilon) => Sphere3D.ApproximatelyEquals(left, right, epsilon);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,7 +1,9 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("A: {A.ToString(), nq}, B: {B.ToString(), nq}, B: {C.ToString(), nq}")]
 | 
			
		||||
public readonly struct Triangle(Vector2D A, Vector2D B, Vector2D C)
 | 
			
		||||
public readonly struct Triangle(Vector2D A, Vector2D B, Vector2D C) : IEquatable<Triangle>
 | 
			
		||||
{
 | 
			
		||||
    public readonly Vector2D A { get; init; } = A;
 | 
			
		||||
    public readonly Vector2D B { get; init; } = B;
 | 
			
		||||
@@ -54,6 +56,7 @@ public readonly struct Triangle(Vector2D A, Vector2D B, Vector2D C)
 | 
			
		||||
    /// <param name="obj">The object to compare with the current <see cref="Triangle"/>.</param>
 | 
			
		||||
    /// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Triangle"/>; otherwise, <see cref="false"/>.</returns>
 | 
			
		||||
    public override bool Equals(object? obj) => obj is Triangle triangle && this == triangle;
 | 
			
		||||
    public bool Equals(Triangle other) => this == other;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Generates a hash code for the <see cref="Triangle"/>.
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
@@ -9,7 +11,7 @@ namespace Engine.Core;
 | 
			
		||||
/// Initializes a new instance of the <see cref="Vector2D"/> struct with the specified positions.
 | 
			
		||||
/// </remarks>
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}, Length: {Magnitude}, LengthSquared: {MagnitudeSquared}, Normalized: {Normalized.ToString(),nq}")]
 | 
			
		||||
public readonly struct Vector2D(float x, float y)
 | 
			
		||||
public readonly struct Vector2D(float x, float y) : IEquatable<Vector2D>
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The X coordinate of the <see cref="Vector2D"/>.
 | 
			
		||||
@@ -82,6 +84,7 @@ public readonly struct Vector2D(float x, float y)
 | 
			
		||||
 | 
			
		||||
    public static implicit operator System.Numerics.Vector2(Vector2D vector) => new(vector.X, vector.Y);
 | 
			
		||||
    public static implicit operator Vector2D(System.Numerics.Vector2 vector) => new(vector.X, vector.Y);
 | 
			
		||||
    public static implicit operator Vector2D(Vector2DInt vector) => new(vector.X, vector.Y);
 | 
			
		||||
    public static implicit operator Vector2D(Vector3D vector) => new(vector.X, vector.Y);
 | 
			
		||||
    public static implicit operator Vector2D(System.Numerics.Vector3 vector) => new(vector.X, vector.Y);
 | 
			
		||||
 | 
			
		||||
@@ -308,6 +311,7 @@ public readonly struct Vector2D(float x, float y)
 | 
			
		||||
    /// <param name="obj">The object to compare with the current <see cref="Vector2D"/>.</param>
 | 
			
		||||
    /// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Vector2D"/>; otherwise, <see cref="false"/>.</returns>
 | 
			
		||||
    public override bool Equals(object? obj) => obj is Vector2D vector2D && this == vector2D;
 | 
			
		||||
    public bool Equals(Vector2D other) => this == other;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Generates a hash code for the <see cref="Vector2D"/>.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										269
									
								
								Engine.Core/Primitives/Vector2DInt.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										269
									
								
								Engine.Core/Primitives/Vector2DInt.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,269 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Represents a two-dimensional integer vector.
 | 
			
		||||
/// </summary>
 | 
			
		||||
/// <param name="x">X position of the <see cref="Vector2DInt"/>.</param>
 | 
			
		||||
/// <param name="y">Y position of the <see cref="Vector2DInt"/>.</param>
 | 
			
		||||
/// <remarks>
 | 
			
		||||
/// Initializes a new instance of the <see cref="Vector2DInt"/> struct with the specified positions.
 | 
			
		||||
/// </remarks>
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")]
 | 
			
		||||
public readonly struct Vector2DInt(int x, int y) : IEquatable<Vector2DInt>
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The X coordinate of the <see cref="Vector2DInt"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly int X = x;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The Y coordinate of the <see cref="Vector2DInt"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly int Y = y;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The magnitude (length) of the <see cref="Vector2DInt"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public float Magnitude => Length(this);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The squared magnitude (length) of the <see cref="Vector2DInt"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public float MagnitudeSquared => LengthSquared(this);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Gets a <see cref="Vector2DInt"/> with the direction reversed.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly Vector2DInt Reversed => -this;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Represents the unit <see cref="Vector2DInt"/> pointing upwards.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly static Vector2DInt Up = new(0, 1);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Represents the unit <see cref="Vector2DInt"/> pointing downwards.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly static Vector2DInt Down = new(0, -1);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Represents the unit <see cref="Vector2DInt"/> pointing leftwards.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly static Vector2DInt Left = new(-1, 0);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Represents the unit <see cref="Vector2DInt"/> pointing rightwards.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly static Vector2DInt Right = new(1, 0);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Represents the zero <see cref="Vector2DInt"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly static Vector2DInt Zero = new(0, 0);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Represents the <see cref="Vector2DInt"/> with both components equal to 1.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly static Vector2DInt One = new(1, 1);
 | 
			
		||||
 | 
			
		||||
    public static Vector2DInt operator -(Vector2DInt vector) => new(0 - vector.X, 0 - vector.Y);
 | 
			
		||||
    public static Vector2DInt operator +(Vector2DInt left, Vector2DInt right) => new(left.X + right.X, left.Y + right.Y);
 | 
			
		||||
    public static Vector2DInt operator -(Vector2DInt left, Vector2DInt right) => new(left.X - right.X, left.Y - right.Y);
 | 
			
		||||
    public static bool operator ==(Vector2DInt left, Vector2DInt right) => left.X == right.X && left.Y == right.Y;
 | 
			
		||||
    public static bool operator !=(Vector2DInt left, Vector2DInt right) => left.X != right.X || left.Y != right.Y;
 | 
			
		||||
 | 
			
		||||
    public static implicit operator Vector2DInt(Vector2D vector) => new(vector.X.RoundToInt(), vector.Y.RoundToInt());
 | 
			
		||||
    public static implicit operator Vector2DInt(Vector3DInt vector) => new(vector.X, vector.Y);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Calculates the length of the <see cref="Vector2DInt"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="vector">The <see cref="Vector2DInt"/>.</param>
 | 
			
		||||
    /// <returns>The length of the <see cref="Vector2DInt"/>.</returns>
 | 
			
		||||
    public static float Length(Vector2DInt vector) => Engine.Core.Math.Sqrt(LengthSquared(vector));
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Calculates the squared length of the <see cref="Vector2DInt"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="vector">The <see cref="Vector2DInt"/>.</param>
 | 
			
		||||
    /// <returns>The squared length of the <see cref="Vector2DInt"/>.</returns>
 | 
			
		||||
    public static float LengthSquared(Vector2DInt vector) => vector.X * vector.X + vector.Y * vector.Y;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Calculates the distance between two <see cref="Vector2DInt"/>s.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="from">The start <see cref="Vector2DInt"/>.</param>
 | 
			
		||||
    /// <param name="to">The end <see cref="Vector2DInt"/>.</param>
 | 
			
		||||
    /// <returns>The distance between the two <see cref="Vector2DInt"/>s.</returns>
 | 
			
		||||
    public static float Distance(Vector2DInt from, Vector2DInt to) => Length(FromTo(from, to));
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Inverts the direction of the <see cref="Vector2DInt"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="vector">The <see cref="Vector2DInt"/>.</param>
 | 
			
		||||
    /// <returns>The inverted <see cref="Vector2DInt"/>.</returns>
 | 
			
		||||
    public static Vector2DInt Invert(Vector2DInt vector) => -vector;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Adds two <see cref="Vector2DInt"/>s.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="left">The first <see cref="Vector2DInt"/>.</param>
 | 
			
		||||
    /// <param name="right">The second <see cref="Vector2DInt"/>.</param>
 | 
			
		||||
    /// <returns>The sum of the two <see cref="Vector2DInt"/>s.</returns>
 | 
			
		||||
    public static Vector2DInt Add(Vector2DInt left, Vector2DInt right) => left + right;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Subtracts one <see cref="Vector2DInt"/> from another.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="left">The <see cref="Vector2DInt"/> to subtract from.</param>
 | 
			
		||||
    /// <param name="right">The <see cref="Vector2DInt"/> to subtract.</param>
 | 
			
		||||
    /// <returns>The result of subtracting the second <see cref="Vector2DInt"/> from the first.</returns>
 | 
			
		||||
    public static Vector2DInt Subtract(Vector2DInt left, Vector2DInt right) => left - right;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Calculates the absolute value of each component of the vector.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="vector">The <see cref="Vector2DInt"/>.</param>
 | 
			
		||||
    /// <returns>The <see cref="Vector2DInt"/> with each component's absolute value.</returns>
 | 
			
		||||
    public static Vector2DInt Abs(Vector2DInt vector) => new(Engine.Core.Math.Abs(vector.X), Engine.Core.Math.Abs(vector.Y));
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Calculates the <see cref="Vector2DInt"/> from one point to another.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="from">The starting point.</param>
 | 
			
		||||
    /// <param name="to">The ending point.</param>
 | 
			
		||||
    /// <returns>The <see cref="Vector2DInt"/> from the starting point to the ending point.</returns>
 | 
			
		||||
    public static Vector2DInt FromTo(Vector2DInt from, Vector2DInt to) => to - from;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Scales a <see cref="Vector2DInt"/> by another <see cref="Vector2DInt"/> component-wise.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="vector">The <see cref="Vector2DInt"/> to scale.</param>
 | 
			
		||||
    /// <param name="scale">The <see cref="Vector2DInt"/> containing the scaling factors for each component.</param>
 | 
			
		||||
    /// <returns>The scaled <see cref="Vector2DInt"/>.</returns>
 | 
			
		||||
    public static Vector2DInt Scale(Vector2DInt vector, Vector2DInt scale) => new(vector.X * scale.X, vector.Y * scale.Y);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Calculates a perpendicular <see cref="Vector2DInt"/> to the given <see cref="Vector2DInt"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="vector">The input <see cref="Vector2DInt"/>.</param>
 | 
			
		||||
    /// <returns>A <see cref="Vector2DInt"/> perpendicular to the input <see cref="Vector2DInt"/>.</returns>
 | 
			
		||||
    public static Vector2DInt Perpendicular(Vector2DInt vector) => new(-vector.Y, vector.X);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Returns the component-wise minimum of two <see cref="Vector2DInt"/>s.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="left">The first <see cref="Vector2DInt"/>.</param>
 | 
			
		||||
    /// <param name="right">The second <see cref="Vector2DInt"/>.</param>
 | 
			
		||||
    /// <returns>The <see cref="Vector2DInt"/> containing the minimum components from both input <see cref="Vector2DInt"/>s.</returns>
 | 
			
		||||
    public static Vector2DInt Min(Vector2DInt left, Vector2DInt right) => new((left.X < right.X) ? left.X : right.X, (left.Y < right.Y) ? left.Y : right.Y);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Returns the component-wise maximum of two <see cref="Vector2DInt"/>s.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="left">The first <see cref="Vector2DInt"/>.</param>
 | 
			
		||||
    /// <param name="right">The second <see cref="Vector2DInt"/>.</param>
 | 
			
		||||
    /// <returns>The <see cref="Vector2DInt"/> containing the maximum components from both input <see cref="Vector2DInt"/>s.</returns>
 | 
			
		||||
    public static Vector2DInt Max(Vector2DInt left, Vector2DInt right) => new((left.X > right.X) ? left.X : right.X, (left.Y > right.Y) ? left.Y : right.Y);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Clamps each component of a <see cref="Vector2DInt"/> between the corresponding component of two other <see cref="Vector2DInt"/>s.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="vector">The <see cref="Vector2DInt"/> to clamp.</param>
 | 
			
		||||
    /// <param name="min">The <see cref="Vector2DInt"/> representing the minimum values for each component.</param>
 | 
			
		||||
    /// <param name="max">The <see cref="Vector2DInt"/> representing the maximum values for each component.</param>
 | 
			
		||||
    /// <returns>A <see cref="Vector2DInt"/> with each component clamped between the corresponding components of the min and max <see cref="Vector2DInt"/>s.</returns>
 | 
			
		||||
    public static Vector2DInt Clamp(Vector2DInt vector, Vector2DInt min, Vector2DInt max) => new(Engine.Core.Math.Clamp(vector.X, min.X, max.X), Engine.Core.Math.Clamp(vector.Y, min.Y, max.Y));
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Calculates the cross product of two <see cref="Vector2DInt"/>s.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="left">The first <see cref="Vector2DInt"/>.</param>
 | 
			
		||||
    /// <param name="right">The second <see cref="Vector2DInt"/>.</param>
 | 
			
		||||
    /// <returns>The cross product of the two <see cref="Vector2DInt"/>s.</returns>
 | 
			
		||||
    public static int Cross(Vector2DInt left, Vector2DInt right) => left.X * right.Y - left.Y * right.X;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Calculates the dot product of two <see cref="Vector2DInt"/>s.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="left">The first <see cref="Vector2DInt"/>.</param>
 | 
			
		||||
    /// <param name="right">The second <see cref="Vector2DInt"/>.</param>
 | 
			
		||||
    /// <returns>The dot product of the two <see cref="Vector2DInt"/>s.</returns>
 | 
			
		||||
    public static int Dot(Vector2DInt left, Vector2DInt right) => left.X * right.X + left.Y * right.Y;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Determines whether the specified object is equal to the current <see cref="Vector2DInt"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="obj">The object to compare with the current <see cref="Vector2DInt"/>.</param>
 | 
			
		||||
    /// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Vector2DInt"/>; otherwise, <see cref="false"/>.</returns>
 | 
			
		||||
    public override bool Equals(object? obj) => obj is Vector2DInt vector2DInt && this == vector2DInt;
 | 
			
		||||
    public bool Equals(Vector2DInt other) => this == other;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Generates a hash code for the <see cref="Vector2DInt"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>A hash code for the <see cref="Vector2DInt"/>.</returns>
 | 
			
		||||
    public override int GetHashCode() => System.HashCode.Combine(X, Y);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Converts the <see cref="Vector2DInt"/> to its string representation.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>A string representation of the <see cref="Vector2DInt"/>.</returns>
 | 
			
		||||
    public override string ToString() => $"{nameof(Vector2DInt)}({X}, {Y})";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Provides extension methods for <see cref="Vector2DInt"/> type.
 | 
			
		||||
/// </summary>
 | 
			
		||||
public static class Vector2DIntExtensions
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc cref="Vector2DInt.Length(Vector2DInt)" />
 | 
			
		||||
    public static float Length(this Vector2DInt vector) => Vector2DInt.Length(vector);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector2DInt.LengthSquared(this vector) => Vector2DInt/>
 | 
			
		||||
    public static float LengthSquared(this Vector2DInt vector) => Vector2DInt.LengthSquared(vector);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector2DInt.Distance(Vector2DInt, Vector2DInt)" />
 | 
			
		||||
    public static float Distance(this Vector2DInt from, Vector2DInt to) => Vector2DInt.Distance(from, to);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector2DInt.Invert(this vector) => Vector2DInt/>
 | 
			
		||||
    public static Vector2DInt Invert(this Vector2DInt vector) => Vector2DInt.Invert(vector);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector2DInt.Add(Vector2DInt, Vector2DInt)" />
 | 
			
		||||
    public static Vector2DInt Add(this Vector2DInt vector, Vector2DInt vectorToAdd) => Vector2DInt.Add(vector, vectorToAdd);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector2DInt.Subtract(Vector2DInt, Vector2DInt)" />
 | 
			
		||||
    public static Vector2DInt Subtract(this Vector2DInt vector, Vector2DInt vectorToSubtract) => Vector2DInt.Subtract(vector, vectorToSubtract);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector2DInt.Abs(Vector2DInt)" />
 | 
			
		||||
    public static Vector2DInt Abs(this Vector2DInt vector) => Vector2DInt.Abs(vector);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector2DInt.FromTo(Vector2DInt, Vector2DInt)" />
 | 
			
		||||
    public static Vector2DInt FromTo(this Vector2DInt from, Vector2DInt to) => Vector2DInt.FromTo(from, to);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector2DInt.Scale(Vector2DInt, Vector2DInt)" />
 | 
			
		||||
    public static Vector2DInt Scale(this Vector2DInt vector, Vector2DInt scale) => Vector2DInt.Scale(vector, scale);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector2DInt.Perpendicular(Vector2DInt)" />
 | 
			
		||||
    public static Vector2DInt Perpendicular(this Vector2DInt vector) => Vector2DInt.Perpendicular(vector);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector2DInt.Min(Vector2DInt, Vector2DInt)" />
 | 
			
		||||
    public static Vector2DInt Min(this Vector2DInt left, Vector2DInt right) => Vector2DInt.Min(left, right);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector2DInt.Max(Vector2DInt, Vector2DInt)" />
 | 
			
		||||
    public static Vector2DInt Max(this Vector2DInt left, Vector2DInt right) => Vector2DInt.Max(left, right);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector2DInt.Clamp(Vector2DInt, Vector2DInt,Vector2DInt)" />
 | 
			
		||||
    public static Vector2DInt Clamp(this Vector2DInt vector, Vector2DInt min, Vector2DInt max) => Vector2DInt.Clamp(vector, min, max);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector2DInt.Cross(Vector2DInt, Vector2DInt)" />
 | 
			
		||||
    public static int Cross(this Vector2DInt left, Vector2DInt right) => Vector2DInt.Cross(left, right);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector2D.Angle(Vector2D, Vector2D)" />
 | 
			
		||||
    public static float AngleBetween(this Vector2D left, Vector2D right) => Vector2D.Angle(left, right);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector2DInt.Dot(Vector2DInt, Vector2DInt)" />
 | 
			
		||||
    public static int Dot(this Vector2DInt left, Vector2DInt right) => Vector2DInt.Dot(left, right);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
@@ -10,7 +12,7 @@ namespace Engine.Core;
 | 
			
		||||
/// Initializes a new instance of the <see cref="Vector3D"/> struct with the specified positions.
 | 
			
		||||
/// </remarks>
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}, Length: {Magnitude}, LengthSquared: {MagnitudeSquared}, Normalized: {Normalized.ToString(),nq}")]
 | 
			
		||||
public readonly struct Vector3D(float x, float y, float z)
 | 
			
		||||
public readonly struct Vector3D(float x, float y, float z) : IEquatable<Vector3D>
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The X coordinate of the <see cref="Vector3D"/>.
 | 
			
		||||
@@ -92,6 +94,7 @@ public readonly struct Vector3D(float x, float y, float z)
 | 
			
		||||
 | 
			
		||||
    public static implicit operator System.Numerics.Vector3(Vector3D vector) => new(vector.X, vector.Y, vector.Z);
 | 
			
		||||
    public static implicit operator Vector3D(System.Numerics.Vector3 vector) => new(vector.X, vector.Y, vector.Z);
 | 
			
		||||
    public static implicit operator Vector3D(Vector3DInt vector) => new(vector.X, vector.Y, vector.Z);
 | 
			
		||||
    public static implicit operator Vector3D(Vector2D vector) => new(vector.X, vector.Y, 0f);
 | 
			
		||||
    public static implicit operator Vector3D(System.Numerics.Vector2 vector) => new(vector.X, vector.Y, 0f);
 | 
			
		||||
 | 
			
		||||
@@ -195,13 +198,13 @@ public readonly struct Vector3D(float x, float y, float z)
 | 
			
		||||
    public static Vector3D Scale(Vector3D vector, Vector3D scale) => new(vector.X * scale.X, vector.Y * scale.Y, vector.Z * scale.Z);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Rotates a <see cref="Vector3D"/> around a normal by the specified angle (in radians).
 | 
			
		||||
    /// Rotates a <see cref="Vector3D"/> around a axis by the specified angle (in radians).
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="vector">The <see cref="Vector3D"/> to rotate.</param>
 | 
			
		||||
    /// <param name="normal">The <see cref="Vector3D"/> to rotate around.</param>
 | 
			
		||||
    /// <param name="axis">The <see cref="Vector3D"/> to rotate around.</param>
 | 
			
		||||
    /// <param name="angleInRadian">The angle to rotate by, in radians.</param>
 | 
			
		||||
    /// <returns>The rotated <see cref="Vector3D"/>.</returns>
 | 
			
		||||
    public static Vector3D Rotate(Vector3D vector, Vector3D normal, float angleInRadian) => vector * Math.Cos(angleInRadian) + Cross(normal, vector) * Math.Sin(angleInRadian) + normal * Dot(normal, vector) * (1f - Math.Cos(angleInRadian));
 | 
			
		||||
    public static Vector3D Rotate(Vector3D vector, Vector3D axis, float angleInRadian) => vector * Math.Cos(angleInRadian) + Cross(axis, vector) * Math.Sin(angleInRadian) + axis * Dot(axis, vector) * (1f - Math.Cos(angleInRadian));
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Returns the component-wise minimum of two <see cref="Vector3D"/>s.
 | 
			
		||||
@@ -261,6 +264,15 @@ public readonly struct Vector3D(float x, float y, float z)
 | 
			
		||||
    /// <returns>The dot product of the two <see cref="Vector3D"/>s.</returns>
 | 
			
		||||
    public static float Dot(Vector3D left, Vector3D right) => left.X * right.X + left.Y * right.Y + left.Z * right.Z;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Transforms the <see cref="Vector3D"/> using the specified <see cref="ITransform3D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="vector">The <see cref="Vector3D"/> to transform.</param>
 | 
			
		||||
    /// <param name="transform">The <see cref="ITransform3D"/> to apply.</param>
 | 
			
		||||
    /// <returns>The transformed <see cref="Vector3D"/>.</returns>
 | 
			
		||||
    public static Vector3D Transform(Vector3D vector, ITransform3D transform)
 | 
			
		||||
        => Quaternion.RotateVector(vector, transform.Rotation).Add(transform.Position).Scale(transform.Scale);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Checks if two <see cref="Vector3D"/>s are approximately equal within a specified epsilon range.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
@@ -277,6 +289,7 @@ public readonly struct Vector3D(float x, float y, float z)
 | 
			
		||||
    /// <param name="obj">The object to compare with the current <see cref="Vector3D"/>.</param>
 | 
			
		||||
    /// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Vector3D"/>; otherwise, <see cref="false"/>.</returns>
 | 
			
		||||
    public override bool Equals(object? obj) => obj is Vector3D vector3D && this == vector3D;
 | 
			
		||||
    public bool Equals(Vector3D other) => this == other;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Generates a hash code for the <see cref="Vector3D"/>.
 | 
			
		||||
@@ -359,6 +372,12 @@ public static class Vector3DExtensions
 | 
			
		||||
    /// <inheritdoc cref="Vector3D.Dot(Vector3D, Vector3D)" />
 | 
			
		||||
    public static float Dot(this Vector3D left, Vector3D right) => Vector3D.Dot(left, right);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector3D.Transform(Vector3D, ITransform3D)" />
 | 
			
		||||
    public static Vector3D Transform(this Vector3D vector, ITransform3D transform) => Vector3D.Transform(vector, transform);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector3D.Transform(Vector3D, ITransform3D)" />
 | 
			
		||||
    public static Vector3D Transform(this ITransform3D transform, Vector3D vector) => Vector3D.Transform(vector, transform);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector3D.ApproximatelyEquals(Vector3D, Vector3D, float)" />
 | 
			
		||||
    public static bool ApproximatelyEquals(this Vector3D left, Vector3D right, float epsilon = float.Epsilon) => Vector3D.ApproximatelyEquals(left, right, epsilon);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										277
									
								
								Engine.Core/Primitives/Vector3DInt.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										277
									
								
								Engine.Core/Primitives/Vector3DInt.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,277 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Represents a three-dimensional integer vector.
 | 
			
		||||
/// </summary>
 | 
			
		||||
/// <param name="x">X position of the <see cref="Vector3DInt"/>.</param>
 | 
			
		||||
/// <param name="y">Y position of the <see cref="Vector3DInt"/>.</param>
 | 
			
		||||
/// <param name="z">Z position of the <see cref="Vector3DInt"/>.</param>
 | 
			
		||||
/// <remarks>
 | 
			
		||||
/// Initializes a new instance of the <see cref="Vector3DInt"/> struct with the specified positions.
 | 
			
		||||
/// </remarks>
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")]
 | 
			
		||||
public readonly struct Vector3DInt(int x, int y, int z) : IEquatable<Vector3DInt>
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The X coordinate of the <see cref="Vector3DInt"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly int X = x;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The Y coordinate of the <see cref="Vector3DInt"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly int Y = y;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The Z coordinate of the <see cref="Vector3DInt"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly int Z = z;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The magnitude (length) of the <see cref="Vector3DInt"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public float Magnitude => Length(this);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The squared magnitude (length) of the <see cref="Vector3DInt"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public float MagnitudeSquared => LengthSquared(this);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Represents the unit <see cref="Vector3DInt"/> pointing upwards.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly static Vector3DInt Up = new(0, 1, 0);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Represents the unit <see cref="Vector3DInt"/> pointing downwards.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly static Vector3DInt Down = new(0, -1, 0);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Represents the unit <see cref="Vector3DInt"/> pointing leftwards.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly static Vector3DInt Left = new(-1, 0, 0);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Represents the unit <see cref="Vector3DInt"/> pointing rightwards.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly static Vector3DInt Right = new(1, 0, 0);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Represents the unit <see cref="Vector3DInt"/> pointing forwards.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly static Vector3DInt Forward = new(0, 0, 1);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Represents the unit <see cref="Vector3DInt"/> pointing backwards.
 | 
			
		||||
    public readonly static Vector3DInt Backward = new(0, 0, -1);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Represents the zero <see cref="Vector3DInt"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly static Vector3DInt Zero = new(0, 0, 0);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Represents the <see cref="Vector3DInt"/> with both components equal to 1.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly static Vector3DInt One = new(1, 1, 1);
 | 
			
		||||
 | 
			
		||||
    public static Vector3DInt operator -(Vector3DInt vector) => new(0 - vector.X, 0 - vector.Y, 0 - vector.Z);
 | 
			
		||||
    public static Vector3DInt operator +(Vector3DInt left, Vector3DInt right) => new(left.X + right.X, left.Y + right.Y, left.Z + right.Z);
 | 
			
		||||
    public static Vector3DInt operator -(Vector3DInt left, Vector3DInt right) => new(left.X - right.X, left.Y - right.Y, left.Z - right.Z);
 | 
			
		||||
    public static bool operator ==(Vector3DInt left, Vector3DInt right) => left.X == right.X && left.Y == right.Y && left.Z == right.Z;
 | 
			
		||||
    public static bool operator !=(Vector3DInt left, Vector3DInt right) => left.X != right.X || left.Y != right.Y || left.Z != right.Z;
 | 
			
		||||
 | 
			
		||||
    public static implicit operator Vector3DInt(Vector3D vector) => new(vector.X.RoundToInt(), vector.Y.RoundToInt(), vector.Z.RoundToInt());
 | 
			
		||||
    public static implicit operator Vector3DInt(Vector2DInt vector) => new(vector.X, vector.Y, 0);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Calculates the length of the <see cref="Vector3DInt"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="vector">The <see cref="Vector3DInt"/>.</param>
 | 
			
		||||
    /// <returns>The length of the <see cref="Vector3DInt"/>.</returns>
 | 
			
		||||
    public static float Length(Vector3DInt vector) => Math.Sqrt(LengthSquared(vector));
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Calculates the squared length of the <see cref="Vector3DInt"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="vector">The <see cref="Vector3DInt"/>.</param>
 | 
			
		||||
    /// <returns>The squared length of the <see cref="Vector3DInt"/>.</returns>
 | 
			
		||||
    public static float LengthSquared(Vector3DInt vector) => vector.X * vector.X + vector.Y * vector.Y + vector.Z * vector.Z;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Calculates the distance between two <see cref="Vector3DInt"/>s.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="from">The start <see cref="Vector3DInt"/>.</param>
 | 
			
		||||
    /// <param name="to">The end <see cref="Vector3DInt"/>.</param>
 | 
			
		||||
    /// <returns>The distance between the two <see cref="Vector3DInt"/>s.</returns>
 | 
			
		||||
    public static float Distance(Vector3DInt from, Vector3DInt to) => Length(FromTo(from, to));
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Inverts the direction of the <see cref="Vector3DInt"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="vector">The <see cref="Vector3DInt"/>.</param>
 | 
			
		||||
    /// <returns>The inverted <see cref="Vector3DInt"/>.</returns>
 | 
			
		||||
    public static Vector3DInt Invert(Vector3DInt vector) => -vector;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Adds two <see cref="Vector3DInt"/>s.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="left">The first <see cref="Vector3DInt"/>.</param>
 | 
			
		||||
    /// <param name="right">The second <see cref="Vector3DInt"/>.</param>
 | 
			
		||||
    /// <returns>The sum of the two <see cref="Vector3DInt"/>s.</returns>
 | 
			
		||||
    public static Vector3DInt Add(Vector3DInt left, Vector3DInt right) => left + right;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Subtracts one <see cref="Vector3DInt"/> from another.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="left">The <see cref="Vector3DInt"/> to subtract from.</param>
 | 
			
		||||
    /// <param name="right">The <see cref="Vector3DInt"/> to subtract.</param>
 | 
			
		||||
    /// <returns>The result of subtracting the second <see cref="Vector3DInt"/> from the first.</returns>
 | 
			
		||||
    public static Vector3DInt Subtract(Vector3DInt left, Vector3DInt right) => left - right;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Calculates the absolute value of each component of the vector.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="vector">The <see cref="Vector3DInt"/>.</param>
 | 
			
		||||
    /// <returns>The <see cref="Vector3DInt"/> with each component's absolute value.</returns>
 | 
			
		||||
    public static Vector3DInt Abs(Vector3DInt vector) => new(Math.Abs(vector.X), Math.Abs(vector.Y), Math.Abs(vector.Z));
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Calculates the <see cref="Vector3DInt"/> from one point to another.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="from">The starting point.</param>
 | 
			
		||||
    /// <param name="to">The ending point.</param>
 | 
			
		||||
    /// <returns>The <see cref="Vector3DInt"/> from the starting point to the ending point.</returns>
 | 
			
		||||
    public static Vector3DInt FromTo(Vector3DInt from, Vector3DInt to) => to - from;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Scales a <see cref="Vector3DInt"/> by another <see cref="Vector3DInt"/> component-wise.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="vector">The <see cref="Vector3DInt"/> to scale.</param>
 | 
			
		||||
    /// <param name="scale">The <see cref="Vector3DInt"/> containing the scaling factors for each component.</param>
 | 
			
		||||
    /// <returns>The scaled <see cref="Vector3DInt"/>.</returns>
 | 
			
		||||
    public static Vector3DInt Scale(Vector3DInt vector, Vector3DInt scale) => new(vector.X * scale.X, vector.Y * scale.Y, vector.Z * scale.Z);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Returns the component-wise minimum of two <see cref="Vector3DInt"/>s.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="left">The first <see cref="Vector3DInt"/>.</param>
 | 
			
		||||
    /// <param name="right">The second <see cref="Vector3DInt"/>.</param>
 | 
			
		||||
    /// <returns>The <see cref="Vector3DInt"/> containing the minimum components from both input <see cref="Vector3DInt"/>s.</returns>
 | 
			
		||||
    public static Vector3DInt Min(Vector3DInt left, Vector3DInt right) => new((left.X < right.X) ? left.X : right.X, (left.Y < right.Y) ? left.Y : right.Y, (left.Z < right.Z) ? left.Z : right.Z);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Returns the component-wise maximum of two <see cref="Vector3DInt"/>s.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="left">The first <see cref="Vector3DInt"/>.</param>
 | 
			
		||||
    /// <param name="right">The second <see cref="Vector3DInt"/>.</param>
 | 
			
		||||
    /// <returns>The <see cref="Vector3DInt"/> containing the maximum components from both input <see cref="Vector3DInt"/>s.</returns>
 | 
			
		||||
    public static Vector3DInt Max(Vector3DInt left, Vector3DInt right) => new((left.X > right.X) ? left.X : right.X, (left.Y > right.Y) ? left.Y : right.Y, (left.Z > right.Z) ? left.Z : right.Z);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Clamps each component of a <see cref="Vector3DInt"/> between the corresponding component of two other <see cref="Vector3DInt"/>s.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="vector">The <see cref="Vector3DInt"/> to clamp.</param>
 | 
			
		||||
    /// <param name="min">The <see cref="Vector3DInt"/> representing the minimum values for each component.</param>
 | 
			
		||||
    /// <param name="max">The <see cref="Vector3DInt"/> representing the maximum values for each component.</param>
 | 
			
		||||
    /// <returns>A <see cref="Vector3DInt"/> with each component clamped between the corresponding components of the min and max <see cref="Vector3DInt"/>s.</returns>
 | 
			
		||||
    public static Vector3DInt Clamp(Vector3DInt vector, Vector3DInt min, Vector3DInt max) => new(Math.Clamp(vector.X, min.X, max.X), Math.Clamp(vector.Y, min.Y, max.Y), Math.Clamp(vector.Z, min.Z, max.Z));
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Calculates the cross product of two <see cref="Vector3DInt"/>s.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="left">The first <see cref="Vector3DInt"/>.</param>
 | 
			
		||||
    /// <param name="right">The second <see cref="Vector3DInt"/>.</param>
 | 
			
		||||
    /// <returns>The cross product of the two <see cref="Vector3DInt"/>s.</returns>
 | 
			
		||||
    public static Vector3DInt Cross(Vector3DInt left, Vector3DInt right) => new(left.Y * right.Z - left.Z * right.Y, left.Z * right.X - left.X * right.Z, left.X * right.Y - left.Y * right.X);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Calculates the angle between two <see cref="Vector3DInt"/>s.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="left">The first <see cref="Vector3DInt"/>.</param>
 | 
			
		||||
    /// <param name="right">The second <see cref="Vector3DInt"/>.</param>
 | 
			
		||||
    /// <returns>The angle between the two <see cref="Vector3DInt"/>s in radians.</returns>
 | 
			
		||||
    public static float Angle(Vector3DInt left, Vector3DInt right) => Math.Acos(Dot(left, right) / (Length(left) * Length(right)));
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Calculates the dot product of two <see cref="Vector3DInt"/>s.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="left">The first <see cref="Vector3DInt"/>.</param>
 | 
			
		||||
    /// <param name="right">The second <see cref="Vector3DInt"/>.</param>
 | 
			
		||||
    /// <returns>The dot product of the two <see cref="Vector3DInt"/>s.</returns>
 | 
			
		||||
    public static int Dot(Vector3DInt left, Vector3DInt right) => left.X * right.X + left.Y * right.Y + left.Z * right.Z;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Determines whether the specified object is equal to the current <see cref="Vector3DInt"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="obj">The object to compare with the current <see cref="Vector3DInt"/>.</param>
 | 
			
		||||
    /// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Vector3DInt"/>; otherwise, <see cref="false"/>.</returns>
 | 
			
		||||
    public override bool Equals(object? obj) => obj is Vector3DInt vector3D && this == vector3D;
 | 
			
		||||
    public bool Equals(Vector3DInt other) => this == other;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Generates a hash code for the <see cref="Vector3DInt"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>A hash code for the <see cref="Vector3DInt"/>.</returns>
 | 
			
		||||
    public override int GetHashCode() => System.HashCode.Combine(X, Y, Z);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Converts the <see cref="Vector3DInt"/> to its string representation.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>A string representation of the <see cref="Vector3DInt"/>.</returns>
 | 
			
		||||
    public override string ToString() => $"{nameof(Vector3DInt)}({X}, {Y}, {Z})";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Provides extension methods for <see cref="Vector3DInt"/> type.
 | 
			
		||||
/// </summary>
 | 
			
		||||
public static class Vector3DIntExtensions
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc cref="Vector3DInt.Length(Vector3DInt)" />
 | 
			
		||||
    public static float Length(this Vector3DInt vector) => Vector3DInt.Length(vector);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector3DInt.LengthSquared(Vector3DInt)" />
 | 
			
		||||
    public static float LengthSquared(this Vector3DInt vector) => Vector3DInt.LengthSquared(vector);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector3DInt.Distance(Vector3DInt, Vector3DInt)" />
 | 
			
		||||
    public static float Distance(this Vector3DInt from, Vector3DInt to) => Vector3DInt.Distance(from, to);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector3DInt.Invert(Vector3DInt)" />
 | 
			
		||||
    public static Vector3DInt Invert(this Vector3DInt vector) => Vector3DInt.Invert(vector);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector3DInt.Add(Vector3DInt, Vector3DInt)" />
 | 
			
		||||
    public static Vector3DInt Add(this Vector3DInt vector, Vector3DInt vectorToAdd) => Vector3DInt.Add(vector, vectorToAdd);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector3DInt.Subtract(Vector3DInt, Vector3DInt)" />
 | 
			
		||||
    public static Vector3DInt Subtract(this Vector3DInt vector, Vector3DInt vectorToSubtract) => Vector3DInt.Subtract(vector, vectorToSubtract);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector3DInt.Abs(Vector3DInt)" />
 | 
			
		||||
    public static Vector3DInt Abs(this Vector3DInt vector) => Vector3DInt.Abs(vector);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector3DInt.FromTo(Vector3DInt, Vector3DInt)" />
 | 
			
		||||
    public static Vector3DInt FromTo(this Vector3DInt from, Vector3DInt to) => Vector3DInt.FromTo(from, to);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector3DInt.Scale(Vector3DInt, Vector3DInt)" />
 | 
			
		||||
    public static Vector3DInt Scale(this Vector3DInt vector, Vector3DInt scale) => Vector3DInt.Scale(vector, scale);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector3DInt.Min(Vector3DInt, Vector3DInt)" />
 | 
			
		||||
    public static Vector3DInt Min(this Vector3DInt left, Vector3DInt right) => Vector3DInt.Min(left, right);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector3DInt.Max(Vector3DInt, Vector3DInt)" />
 | 
			
		||||
    public static Vector3DInt Max(this Vector3DInt left, Vector3DInt right) => Vector3DInt.Max(left, right);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector3DInt.Clamp(Vector3DInt, Vector3DInt, Vector3DInt)" />
 | 
			
		||||
    public static Vector3DInt Clamp(this Vector3DInt vector, Vector3DInt min, Vector3DInt max) => Vector3DInt.Clamp(vector, min, max);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector3DInt.Cross(Vector3DInt, Vector3DInt)" />
 | 
			
		||||
    public static Vector3DInt Cross(this Vector3DInt left, Vector3DInt right) => Vector3DInt.Cross(left, right);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector3DInt.Angle(Vector3DInt, Vector3DInt)" />
 | 
			
		||||
    public static float AngleBetween(this Vector3DInt left, Vector3DInt right) => Vector3DInt.Angle(left, right);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector3DInt.Dot(Vector3DInt, Vector3DInt)" />
 | 
			
		||||
    public static int Dot(this Vector3DInt left, Vector3DInt right) => Vector3DInt.Dot(left, right);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										318
									
								
								Engine.Core/Primitives/Vector4D.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										318
									
								
								Engine.Core/Primitives/Vector4D.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,318 @@
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Represents a four-dimensional vector.
 | 
			
		||||
/// </summary>
 | 
			
		||||
/// <param name="x">X position of the <see cref="Vector4D"/>.</param>
 | 
			
		||||
/// <param name="y">Y position of the <see cref="Vector4D"/>.</param>
 | 
			
		||||
/// <param name="z">Z position of the <see cref="Vector4D"/>.</param>
 | 
			
		||||
/// <param name="w">W position of the <see cref="Vector4D"/>.</param>
 | 
			
		||||
/// <remarks>
 | 
			
		||||
/// Initializes a new instance of the <see cref="Vector4D"/> struct with the specified positions.
 | 
			
		||||
/// </remarks>
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}, Length: {Magnitude}, LengthSquared: {MagnitudeSquared}, Normalized: {Normalized.ToString(),nq}")]
 | 
			
		||||
public readonly struct Vector4D(float x, float y, float z, float w) : IEquatable<Vector4D>
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The X coordinate of the <see cref="Vector4D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly float X = x;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The Y coordinate of the <see cref="Vector4D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly float Y = y;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The Z coordinate of the <see cref="Vector4D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly float Z = z;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The W coordinate of the <see cref="Vector4D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly float W = w;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The magnitude (length) of the <see cref="Vector4D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public float Magnitude => Length(this);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The squared magnitude (length) of the <see cref="Vector4D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public float MagnitudeSquared => LengthSquared(this);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The normalized form of the <see cref="Vector4D"/> (a <see cref="Vector4D"/> with the same direction and a magnitude of 1).
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public Vector4D Normalized => Normalize(this);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Represents the zero <see cref="Vector4D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly static Vector4D Zero = new(0f, 0f, 0f, 0f);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Represents the one <see cref="Vector4D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly static Vector4D One = new(1f, 1f, 1f, 1f);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Represents the unit X <see cref="Vector4D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly static Vector4D UnitX = new(1f, 0f, 0f, 0f);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Represents the unit Y <see cref="Vector4D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly static Vector4D UnitY = new(0f, 1f, 0f, 0f);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Represents the unit Z <see cref="Vector4D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly static Vector4D UnitZ = new(0f, 0f, 1f, 0f);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Represents the unit W <see cref="Vector4D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public readonly static Vector4D UnitW = new(0f, 0f, 0f, 1f);
 | 
			
		||||
 | 
			
		||||
    public static Vector4D operator -(Vector4D vector) => new(0f - vector.X, 0f - vector.Y, 0f - vector.Z, 0f - vector.W);
 | 
			
		||||
    public static Vector4D operator +(Vector4D left, Vector4D right) => new(left.X + right.X, left.Y + right.Y, left.Z + right.Z, left.W + right.W);
 | 
			
		||||
    public static Vector4D operator -(Vector4D left, Vector4D right) => new(left.X - right.X, left.Y - right.Y, left.Z - right.Z, left.W - right.W);
 | 
			
		||||
    public static Vector4D operator *(Vector4D vector, float value) => new(vector.X * value, vector.Y * value, vector.Z * value, vector.W * value);
 | 
			
		||||
    public static Vector4D operator *(float value, Vector4D vector) => new(vector.X * value, vector.Y * value, vector.Z * value, vector.W * value);
 | 
			
		||||
    public static Vector4D operator /(Vector4D vector, float value) => new(vector.X / value, vector.Y / value, vector.Z / value, vector.W / value);
 | 
			
		||||
    public static bool operator ==(Vector4D left, Vector4D right) => left.X == right.X && left.Y == right.Y && left.Z == right.Z && left.W == right.W;
 | 
			
		||||
    public static bool operator !=(Vector4D left, Vector4D right) => left.X != right.X || left.Y != right.Y || left.Z != right.Z || left.W != right.W;
 | 
			
		||||
 | 
			
		||||
    public static implicit operator System.Numerics.Vector4(Vector4D vector) => new(vector.X, vector.Y, vector.Z, vector.W);
 | 
			
		||||
    public static implicit operator Vector4D(System.Numerics.Vector4 vector) => new(vector.X, vector.Y, vector.Z, vector.W);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Calculates the length of the <see cref="Vector4D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="vector">The <see cref="Vector4D"/>.</param>
 | 
			
		||||
    /// <returns>The length of the <see cref="Vector4D"/>.</returns>
 | 
			
		||||
    public static float Length(Vector4D vector) => Math.Sqrt(LengthSquared(vector));
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Calculates the squared length of the <see cref="Vector4D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="vector">The <see cref="Vector4D"/>.</param>
 | 
			
		||||
    /// <returns>The squared length of the <see cref="Vector4D"/>.</returns>
 | 
			
		||||
    public static float LengthSquared(Vector4D vector) => vector.X * vector.X + vector.Y * vector.Y + vector.Z * vector.Z + vector.W * vector.W;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Calculates the distance between two <see cref="Vector4D"/>s.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="from">The start <see cref="Vector4D"/>.</param>
 | 
			
		||||
    /// <param name="to">The end <see cref="Vector4D"/>.</param>
 | 
			
		||||
    /// <returns>The distance between the two <see cref="Vector4D"/>s.</returns>
 | 
			
		||||
    public static float Distance(Vector4D from, Vector4D to) => Length(FromTo(from, to));
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Inverts the direction of the <see cref="Vector4D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="vector">The <see cref="Vector4D"/>.</param>
 | 
			
		||||
    /// <returns>The inverted <see cref="Vector4D"/>.</returns>
 | 
			
		||||
    public static Vector4D Invert(Vector4D vector) => -vector;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Adds two <see cref="Vector4D"/>s.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="left">The first <see cref="Vector4D"/>.</param>
 | 
			
		||||
    /// <param name="right">The second <see cref="Vector4D"/>.</param>
 | 
			
		||||
    /// <returns>The sum of the two <see cref="Vector4D"/>s.</returns>
 | 
			
		||||
    public static Vector4D Add(Vector4D left, Vector4D right) => left + right;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Subtracts one <see cref="Vector4D"/> from another.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="left">The <see cref="Vector4D"/> to subtract from.</param>
 | 
			
		||||
    /// <param name="right">The <see cref="Vector4D"/> to subtract.</param>
 | 
			
		||||
    /// <returns>The result of subtracting the second <see cref="Vector4D"/> from the first.</returns>
 | 
			
		||||
    public static Vector4D Subtract(Vector4D left, Vector4D right) => left - right;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Multiplies a <see cref="Vector4D"/> by a scalar value.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="vector">The <see cref="Vector4D"/>.</param>
 | 
			
		||||
    /// <param name="value">The scalar value.</param>
 | 
			
		||||
    /// <returns>The result of multiplying the <see cref="Vector4D"/> by the scalar value.</returns>
 | 
			
		||||
    public static Vector4D Multiply(Vector4D vector, float value) => vector * value;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Divides a <see cref="Vector4D"/> by a scalar value.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="vector">The <see cref="Vector4D"/>.</param>
 | 
			
		||||
    /// <param name="value">The scalar value.</param>
 | 
			
		||||
    /// <returns>The result of dividing the <see cref="Vector4D"/> by the scalar value.</returns>
 | 
			
		||||
    public static Vector4D Divide(Vector4D vector, float value) => vector / value;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Calculates the absolute value of each component of the vector.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="vector">The <see cref="Vector4D"/>.</param>
 | 
			
		||||
    /// <returns>The <see cref="Vector4D"/> with each component's absolute value.</returns>
 | 
			
		||||
    public static Vector4D Abs(Vector4D vector) => new(Math.Abs(vector.X), Math.Abs(vector.Y), Math.Abs(vector.Z), Math.Abs(vector.W));
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Normalizes the <see cref="Vector4D"/> (creates a unit <see cref="Vector4D"/> with the same direction).
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="vector">The <see cref="Vector4D"/> to normalize.</param>
 | 
			
		||||
    /// <returns>The normalized <see cref="Vector4D"/>.</returns>
 | 
			
		||||
    public static Vector4D Normalize(Vector4D vector) => vector / Length(vector);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Calculates the <see cref="Vector4D"/> from one point to another.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="from">The starting point.</param>
 | 
			
		||||
    /// <param name="to">The ending point.</param>
 | 
			
		||||
    /// <returns>The <see cref="Vector4D"/> from the starting point to the ending point.</returns>
 | 
			
		||||
    public static Vector4D FromTo(Vector4D from, Vector4D to) => to - from;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Scales a <see cref="Vector4D"/> by another <see cref="Vector4D"/> component-wise.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="vector">The <see cref="Vector4D"/> to scale.</param>
 | 
			
		||||
    /// <param name="scale">The <see cref="Vector4D"/> containing the scaling factors for each component.</param>
 | 
			
		||||
    /// <returns>The scaled <see cref="Vector4D"/>.</returns>
 | 
			
		||||
    public static Vector4D Scale(Vector4D vector, Vector4D scale) => new(vector.X * scale.X, vector.Y * scale.Y, vector.Z * scale.Z, vector.W * scale.W);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Returns the component-wise minimum of two <see cref="Vector4D"/>s.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="left">The first <see cref="Vector4D"/>.</param>
 | 
			
		||||
    /// <param name="right">The second <see cref="Vector4D"/>.</param>
 | 
			
		||||
    /// <returns>The <see cref="Vector4D"/> containing the minimum components from both input <see cref="Vector4D"/>s.</returns>
 | 
			
		||||
    public static Vector4D Min(Vector4D left, Vector4D right) => new((left.X < right.X) ? left.X : right.X, (left.Y < right.Y) ? left.Y : right.Y, (left.Z < right.Z) ? left.Z : right.Z, (left.W < right.W) ? left.W : right.W);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Returns the component-wise maximum of two <see cref="Vector4D"/>s.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="left">The first <see cref="Vector4D"/>.</param>
 | 
			
		||||
    /// <param name="right">The second <see cref="Vector4D"/>.</param>
 | 
			
		||||
    /// <returns>The <see cref="Vector4D"/> containing the maximum components from both input <see cref="Vector4D"/>s.</returns>
 | 
			
		||||
    public static Vector4D Max(Vector4D left, Vector4D right) => new((left.X > right.X) ? left.X : right.X, (left.Y > right.Y) ? left.Y : right.Y, (left.Z > right.Z) ? left.Z : right.Z, (left.W > right.W) ? left.W : right.W);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Clamps each component of a <see cref="Vector4D"/> between the corresponding component of two other <see cref="Vector4D"/>s.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="vector">The <see cref="Vector4D"/> to clamp.</param>
 | 
			
		||||
    /// <param name="min">The <see cref="Vector4D"/> representing the minimum values for each component.</param>
 | 
			
		||||
    /// <param name="max">The <see cref="Vector4D"/> representing the maximum values for each component.</param>
 | 
			
		||||
    /// <returns>A <see cref="Vector4D"/> with each component clamped between the corresponding components of the min and max <see cref="Vector4D"/>s.</returns>
 | 
			
		||||
    public static Vector4D Clamp(Vector4D vector, Vector4D min, Vector4D max) => new(Math.Clamp(vector.X, min.X, max.X), Math.Clamp(vector.Y, min.Y, max.Y), Math.Clamp(vector.Z, min.Z, max.Z), Math.Clamp(vector.W, min.W, max.W));
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Performs linear interpolation between two <see cref="Vector4D"/>s.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="from">The starting <see cref="Vector4D"/> (t = 0).</param>
 | 
			
		||||
    /// <param name="to">The ending <see cref="Vector4D"/> (t = 1).</param>
 | 
			
		||||
    /// <param name="t">The interpolation parameter.</param>
 | 
			
		||||
    /// <returns>The interpolated <see cref="Vector4D"/>.</returns>
 | 
			
		||||
    public static Vector4D Lerp(Vector4D from, Vector4D to, float t) => from + FromTo(from, to) * t;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Calculates the dot product of two <see cref="Vector4D"/>s.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="left">The first <see cref="Vector4D"/>.</param>
 | 
			
		||||
    /// <param name="right">The second <see cref="Vector4D"/>.</param>
 | 
			
		||||
    /// <returns>The dot product of the two <see cref="Vector4D"/>s.</returns>
 | 
			
		||||
    public static float Dot(Vector4D left, Vector4D right) => left.X * right.X + left.Y * right.Y + left.Z * right.Z + left.W * right.W;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Checks if two <see cref="Vector4D"/>s are approximately equal within a specified epsilon range.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="left">The first <see cref="Vector4D"/>.</param>
 | 
			
		||||
    /// <param name="right">The second <see cref="Vector4D"/>.</param>
 | 
			
		||||
    /// <param name="epsilon">The epsilon range.</param>
 | 
			
		||||
    /// <returns><see cref="true"/> if the <see cref="Vector4D"/>s are approximately equal; otherwise, <see cref="false"/>.</returns>
 | 
			
		||||
    public static bool ApproximatelyEquals(Vector4D left, Vector4D right, float epsilon = float.Epsilon)
 | 
			
		||||
        => left.X.ApproximatelyEquals(right.X, epsilon) && left.Y.ApproximatelyEquals(right.Y, epsilon) && left.Z.ApproximatelyEquals(right.Z, epsilon) && left.Z.ApproximatelyEquals(right.W, epsilon);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Determines whether the specified object is equal to the current <see cref="Vector4D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="obj">The object to compare with the current <see cref="Vector4D"/>.</param>
 | 
			
		||||
    /// <returns><see cref="true"/> if the specified object is equal to the current <see cref="Vector4D"/>; otherwise, <see cref="false"/>.</returns>
 | 
			
		||||
    public override bool Equals(object? obj) => obj is Vector4D vector4D && this == vector4D;
 | 
			
		||||
    public bool Equals(Vector4D other) => this == other;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Generates a hash code for the <see cref="Vector4D"/>.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>A hash code for the <see cref="Vector4D"/>.</returns>
 | 
			
		||||
    public override int GetHashCode() => System.HashCode.Combine(X, Y, Z, W);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Converts the <see cref="Vector4D"/> to its string representation.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>A string representation of the <see cref="Vector4D"/>.</returns>
 | 
			
		||||
    public override string ToString() => $"{nameof(Vector4D)}({X}, {Y}, {Z}, {W})";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Provides extension methods for <see cref="Vector4D"/> type.
 | 
			
		||||
/// </summary>
 | 
			
		||||
public static class Vector4DExtensions
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc cref="Vector4D.Length(Vector4D)" />
 | 
			
		||||
    public static float Length(this Vector4D vector) => Vector4D.Length(vector);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector4D.LengthSquared(Vector4D)" />
 | 
			
		||||
    public static float LengthSquared(this Vector4D vector) => Vector4D.LengthSquared(vector);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector4D.Distance(Vector4D, Vector4D)" />
 | 
			
		||||
    public static float Distance(this Vector4D from, Vector4D to) => Vector4D.Distance(from, to);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector4D.Invert(Vector4D)" />
 | 
			
		||||
    public static Vector4D Invert(this Vector4D vector) => Vector4D.Invert(vector);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector4D.Add(Vector4D, Vector4D)" />
 | 
			
		||||
    public static Vector4D Add(this Vector4D vector, Vector4D vectorToAdd) => Vector4D.Add(vector, vectorToAdd);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector4D.Subtract(Vector4D, Vector4D)" />
 | 
			
		||||
    public static Vector4D Subtract(this Vector4D vector, Vector4D vectorToSubtract) => Vector4D.Subtract(vector, vectorToSubtract);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector4D.Multiply(Vector4D, float)" />
 | 
			
		||||
    public static Vector4D Multiply(this Vector4D vector, float value) => Vector4D.Multiply(vector, value);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector4D.Divide(Vector4D, float)" />
 | 
			
		||||
    public static Vector4D Divide(this Vector4D vector, float value) => Vector4D.Divide(vector, value);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector4D.Abs(Vector4D)" />
 | 
			
		||||
    public static Vector4D Abs(this Vector4D vector) => Vector4D.Abs(vector);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector4D.Normalize(Vector4D)" />
 | 
			
		||||
    public static Vector4D Normalize(this Vector4D vector) => Vector4D.Normalize(vector);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector4D.FromTo(Vector4D, Vector4D)" />
 | 
			
		||||
    public static Vector4D FromTo(this Vector4D from, Vector4D to) => Vector4D.FromTo(from, to);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector4D.Scale(Vector4D, Vector4D)" />
 | 
			
		||||
    public static Vector4D Scale(this Vector4D vector, Vector4D scale) => Vector4D.Scale(vector, scale);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector4D.Min(Vector4D, Vector4D)" />
 | 
			
		||||
    public static Vector4D Min(this Vector4D left, Vector4D right) => Vector4D.Min(left, right);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector4D.Max(Vector4D, Vector4D)" />
 | 
			
		||||
    public static Vector4D Max(this Vector4D left, Vector4D right) => Vector4D.Max(left, right);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector4D.Clamp(Vector4D, Vector4D, Vector4D)" />
 | 
			
		||||
    public static Vector4D Clamp(this Vector4D vector, Vector4D min, Vector4D max) => Vector4D.Clamp(vector, min, max);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector4D.Lerp(Vector4D, Vector4D, float)" />
 | 
			
		||||
    public static Vector4D Lerp(this Vector4D from, Vector4D to, float t) => Vector4D.Lerp(from, to, t);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector4D.Dot(Vector4D, Vector4D)" />
 | 
			
		||||
    public static float Dot(this Vector4D left, Vector4D right) => Vector4D.Dot(left, right);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Vector4D.ApproximatelyEquals(Vector4D, Vector4D, float)" />
 | 
			
		||||
    public static bool ApproximatelyEquals(this Vector4D left, Vector4D right, float epsilon = float.Epsilon) => Vector4D.ApproximatelyEquals(left, right, epsilon);
 | 
			
		||||
}
 | 
			
		||||
@@ -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);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -2,14 +2,15 @@ using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
public class DrawManager : Behaviour
 | 
			
		||||
public class DrawManager : Behaviour, IEnterUniverse, IExitUniverse
 | 
			
		||||
{
 | 
			
		||||
    // We use Descending order because draw calls are running from last to first
 | 
			
		||||
    private static Comparer<IBehaviour> SortByDescendingPriority() => Comparer<IBehaviour>.Create((x, y) => y.Priority.CompareTo(x.Priority));
 | 
			
		||||
    private static Comparer<int> SortByDescendingPriority() => Comparer<int>.Create((x, y) => y.CompareTo(x));
 | 
			
		||||
    private static System.Func<IBehaviour, int> GetPriority() => (b) => b.Priority;
 | 
			
		||||
 | 
			
		||||
    private readonly ActiveBehaviourCollectorSorted<IPreDraw> preDrawEntities = new() { SortBy = SortByDescendingPriority() };
 | 
			
		||||
    private readonly ActiveBehaviourCollectorSorted<IDraw> drawEntities = new() { SortBy = SortByDescendingPriority() };
 | 
			
		||||
    private readonly ActiveBehaviourCollectorSorted<IPostDraw> postDrawEntities = new() { SortBy = SortByDescendingPriority() };
 | 
			
		||||
    private readonly ActiveBehaviourCollectorOrdered<int, IPreDraw> preDrawEntities = new(GetPriority(), SortByDescendingPriority());
 | 
			
		||||
    private readonly ActiveBehaviourCollectorOrdered<int, IDraw> drawEntities = new(GetPriority(), SortByDescendingPriority());
 | 
			
		||||
    private readonly ActiveBehaviourCollectorOrdered<int, IPostDraw> postDrawEntities = new(GetPriority(), SortByDescendingPriority());
 | 
			
		||||
 | 
			
		||||
    private void OnPreDraw(IUniverse sender)
 | 
			
		||||
    {
 | 
			
		||||
@@ -29,7 +30,7 @@ public class DrawManager : Behaviour
 | 
			
		||||
            postDrawEntities[i].PostDraw();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void OnEnteredUniverse(IUniverse universe)
 | 
			
		||||
    public void EnterUniverse(IUniverse universe)
 | 
			
		||||
    {
 | 
			
		||||
        preDrawEntities.Assign(universe);
 | 
			
		||||
        drawEntities.Assign(universe);
 | 
			
		||||
@@ -40,7 +41,7 @@ public class DrawManager : Behaviour
 | 
			
		||||
        universe.OnPostDraw.AddListener(OnPostDraw);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void OnExitedUniverse(IUniverse universe)
 | 
			
		||||
    public void ExitUniverse(IUniverse universe)
 | 
			
		||||
    {
 | 
			
		||||
        preDrawEntities.Unassign();
 | 
			
		||||
        drawEntities.Unassign();
 | 
			
		||||
 
 | 
			
		||||
@@ -2,23 +2,27 @@ using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
public class UniverseEntranceManager : Behaviour
 | 
			
		||||
public class UniverseEntranceManager : Internal.BehaviourIndependent
 | 
			
		||||
{
 | 
			
		||||
    // We use Ascending order because we are using reverse for loop to call them
 | 
			
		||||
    private static Comparer<IBehaviour> SortByAscendingPriority() => Comparer<IBehaviour>.Create((x, y) => x.Priority.CompareTo(y.Priority));
 | 
			
		||||
    private static Comparer<int> SortByAscendingPriority() => Comparer<int>.Create((x, y) => x.CompareTo(y));
 | 
			
		||||
    private static System.Func<IBehaviour, int> GetPriority() => (b) => b.Priority;
 | 
			
		||||
 | 
			
		||||
    private readonly ActiveBehaviourCollectorSorted<IEnterUniverse> enterUniverses = new() { SortBy = SortByAscendingPriority() };
 | 
			
		||||
    private readonly ActiveBehaviourCollectorSorted<IExitUniverse> exitUniverses = new() { SortBy = SortByAscendingPriority() };
 | 
			
		||||
    private readonly ActiveBehaviourCollectorOrdered<int, IEnterUniverse> enterUniverses = new(GetPriority(), SortByAscendingPriority());
 | 
			
		||||
 | 
			
		||||
    private readonly List<IEnterUniverse> toCallEnterUniverses = new(32);
 | 
			
		||||
    private readonly List<IExitUniverse> toCallExitUniverses = new(32);
 | 
			
		||||
 | 
			
		||||
    protected override void OnEnteredUniverse(IUniverse universe)
 | 
			
		||||
    {
 | 
			
		||||
        // FIXME: This causes an issue when the UniverseEntranceManager is already attached to a UniverseObject then registered into a Universe,
 | 
			
		||||
        // the enter/exit universe collectors call OnUniverseObjectRegistered internally on Assign, but since the Universe calls the OnUniverseObjectRegistered
 | 
			
		||||
        // 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);
 | 
			
		||||
 | 
			
		||||
        foreach (IUniverseObject universeObject in Universe.UniverseObjects)
 | 
			
		||||
            OnUniverseObjectRegistered(Universe, new(universeObject));
 | 
			
		||||
        foreach (IUniverseObject universeObject in universe)
 | 
			
		||||
            OnUniverseObjectRegistered(universe, new(universeObject));
 | 
			
		||||
 | 
			
		||||
        universe.OnUniverseObjectRegistered.AddListener(OnUniverseObjectRegistered);
 | 
			
		||||
        universe.OnUniverseObjectUnRegistered.AddListener(OnUniverseObjectUnRegistered);
 | 
			
		||||
@@ -28,8 +32,8 @@ public class UniverseEntranceManager : Behaviour
 | 
			
		||||
    {
 | 
			
		||||
        enterUniverses.Unassign();
 | 
			
		||||
 | 
			
		||||
        foreach (IUniverseObject universeObject in Universe.UniverseObjects)
 | 
			
		||||
            OnUniverseObjectUnRegistered(Universe, new(universeObject));
 | 
			
		||||
        foreach (IUniverseObject universeObject in universe)
 | 
			
		||||
            OnUniverseObjectUnRegistered(universe, new(universeObject));
 | 
			
		||||
 | 
			
		||||
        universe.OnUniverseObjectRegistered.RemoveListener(OnUniverseObjectRegistered);
 | 
			
		||||
        universe.OnUniverseObjectUnRegistered.RemoveListener(OnUniverseObjectUnRegistered);
 | 
			
		||||
@@ -37,10 +41,13 @@ public class UniverseEntranceManager : Behaviour
 | 
			
		||||
 | 
			
		||||
    private void OnUniverseObjectUnRegistered(IUniverse sender, IUniverse.UniverseObjectUnRegisteredArguments args)
 | 
			
		||||
    {
 | 
			
		||||
        args.UniverseObjectUnregistered.BehaviourController.GetBehavioursInChildren(toCallExitUniverses);
 | 
			
		||||
 | 
			
		||||
        for (int i = toCallExitUniverses.Count - 1; i >= 0; i--)
 | 
			
		||||
        {
 | 
			
		||||
            toCallExitUniverses[i].ExitUniverse(Universe);
 | 
			
		||||
            IExitUniverse exitUniverse = toCallExitUniverses[i];
 | 
			
		||||
            toCallExitUniverses.RemoveAt(i);
 | 
			
		||||
            exitUniverse.ExitUniverse(Universe);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -48,8 +55,9 @@ public class UniverseEntranceManager : Behaviour
 | 
			
		||||
    {
 | 
			
		||||
        for (int i = toCallEnterUniverses.Count - 1; i >= 0; i--)
 | 
			
		||||
        {
 | 
			
		||||
            toCallEnterUniverses[i].EnterUniverse(Universe);
 | 
			
		||||
            IEnterUniverse enterUniverse = toCallEnterUniverses[i];
 | 
			
		||||
            toCallEnterUniverses.RemoveAt(i);
 | 
			
		||||
            enterUniverse.EnterUniverse(Universe);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -58,14 +66,8 @@ public class UniverseEntranceManager : Behaviour
 | 
			
		||||
        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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,20 +2,21 @@ using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
public class UpdateManager : Behaviour
 | 
			
		||||
public class UpdateManager : Behaviour, IEnterUniverse, IExitUniverse
 | 
			
		||||
{
 | 
			
		||||
    // We use Ascending order because we are using reverse for loop to call them
 | 
			
		||||
    private static Comparer<IBehaviour> SortByAscendingPriority() => Comparer<IBehaviour>.Create((x, y) => x.Priority.CompareTo(y.Priority));
 | 
			
		||||
    private static Comparer<int> SortByAscendingPriority() => Comparer<int>.Create((x, y) => x.CompareTo(y));
 | 
			
		||||
    private static System.Func<IBehaviour, int> GetPriority() => (b) => b.Priority;
 | 
			
		||||
 | 
			
		||||
    private readonly ActiveBehaviourCollectorSorted<IFirstFrameUpdate> firstFrameUpdates = new() { SortBy = SortByAscendingPriority() };
 | 
			
		||||
    private readonly ActiveBehaviourCollectorOrdered<int, IFirstFrameUpdate> firstFrameUpdates = new(GetPriority(), SortByAscendingPriority());
 | 
			
		||||
    private readonly ActiveBehaviourCollector<ILastFrameUpdate> lastFrameUpdates = new();
 | 
			
		||||
    private readonly ActiveBehaviourCollectorSorted<IPreUpdate> preUpdateEntities = new() { SortBy = SortByAscendingPriority() };
 | 
			
		||||
    private readonly ActiveBehaviourCollectorSorted<IUpdate> updateEntities = new() { SortBy = SortByAscendingPriority() };
 | 
			
		||||
    private readonly ActiveBehaviourCollectorSorted<IPostUpdate> postUpdateEntities = new() { SortBy = SortByAscendingPriority() };
 | 
			
		||||
    private readonly ActiveBehaviourCollectorOrdered<int, IPreUpdate> preUpdateEntities = new(GetPriority(), SortByAscendingPriority());
 | 
			
		||||
    private readonly ActiveBehaviourCollectorOrdered<int, IUpdate> updateEntities = new(GetPriority(), SortByAscendingPriority());
 | 
			
		||||
    private readonly ActiveBehaviourCollectorOrdered<int, IPostUpdate> postUpdateEntities = new(GetPriority(), SortByAscendingPriority());
 | 
			
		||||
 | 
			
		||||
    private readonly List<IFirstFrameUpdate> toCallFirstFrameUpdates = new(32);
 | 
			
		||||
 | 
			
		||||
    protected override void OnEnteredUniverse(IUniverse universe)
 | 
			
		||||
    public void EnterUniverse(IUniverse universe)
 | 
			
		||||
    {
 | 
			
		||||
        firstFrameUpdates.Assign(universe);
 | 
			
		||||
        lastFrameUpdates.Assign(universe);
 | 
			
		||||
@@ -23,12 +24,13 @@ public class UpdateManager : Behaviour
 | 
			
		||||
        updateEntities.Assign(universe);
 | 
			
		||||
        postUpdateEntities.Assign(universe);
 | 
			
		||||
 | 
			
		||||
        universe.OnPreUpdate.AddListener(OnFirstUpdate, int.MaxValue);
 | 
			
		||||
        universe.OnPreUpdate.AddListener(OnPreUpdate);
 | 
			
		||||
        universe.OnUpdate.AddListener(OnUpdate);
 | 
			
		||||
        universe.OnPostUpdate.AddListener(OnPostUpdate);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void OnExitedUniverse(IUniverse universe)
 | 
			
		||||
    public void ExitUniverse(IUniverse universe)
 | 
			
		||||
    {
 | 
			
		||||
        firstFrameUpdates.Unassign();
 | 
			
		||||
        lastFrameUpdates.Unassign();
 | 
			
		||||
@@ -36,19 +38,23 @@ public class UpdateManager : Behaviour
 | 
			
		||||
        updateEntities.Unassign();
 | 
			
		||||
        postUpdateEntities.Unassign();
 | 
			
		||||
 | 
			
		||||
        universe.OnPreUpdate.RemoveListener(OnFirstUpdate);
 | 
			
		||||
        universe.OnPreUpdate.RemoveListener(OnPreUpdate);
 | 
			
		||||
        universe.OnUpdate.RemoveListener(OnUpdate);
 | 
			
		||||
        universe.OnPostUpdate.RemoveListener(OnPostUpdate);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void OnPreUpdate(IUniverse sender, IUniverse.UpdateArguments args)
 | 
			
		||||
    private void OnFirstUpdate(IUniverse sender, IUniverse.UpdateArguments args)
 | 
			
		||||
    {
 | 
			
		||||
        for (int i = toCallFirstFrameUpdates.Count - 1; i >= 0; i--)
 | 
			
		||||
        {
 | 
			
		||||
            toCallFirstFrameUpdates[i].FirstActiveFrame();
 | 
			
		||||
            toCallFirstFrameUpdates.RemoveAt(i);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void OnPreUpdate(IUniverse sender, IUniverse.UpdateArguments args)
 | 
			
		||||
    {
 | 
			
		||||
        for (int i = preUpdateEntities.Count - 1; i >= 0; i--)
 | 
			
		||||
            preUpdateEntities[i].PreUpdate();
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ public class Transform2D : Behaviour, ITransform2D
 | 
			
		||||
    public Event<ITransform2D, ITransform2D.PositionChangedArguments> OnPositionChanged { get; } = new();
 | 
			
		||||
    public Event<ITransform2D, ITransform2D.ScaleChangedArguments> OnScaleChanged { get; } = new();
 | 
			
		||||
    public Event<ITransform2D, ITransform2D.RotationChangedArguments> OnRotationChanged { get; } = new();
 | 
			
		||||
    public Event<ITransform2D> OnTransformUpdated { get; } = new();
 | 
			
		||||
 | 
			
		||||
    private Vector2D _position = Vector2D.Zero;
 | 
			
		||||
    private Vector2D _scale = Vector2D.One;
 | 
			
		||||
@@ -36,7 +37,8 @@ public class Transform2D : Behaviour, ITransform2D
 | 
			
		||||
            _position = value;
 | 
			
		||||
 | 
			
		||||
            UpdateLocalPosition();
 | 
			
		||||
            OnPositionChanged?.Invoke(this, new(previousPosition));
 | 
			
		||||
            OnPositionChanged.Invoke(this, new(previousPosition));
 | 
			
		||||
            OnTransformUpdated.Invoke(this);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -52,7 +54,8 @@ public class Transform2D : Behaviour, ITransform2D
 | 
			
		||||
            _scale = value;
 | 
			
		||||
 | 
			
		||||
            UpdateLocalScale();
 | 
			
		||||
            OnScaleChanged?.Invoke(this, new(previousScale));
 | 
			
		||||
            OnScaleChanged.Invoke(this, new(previousScale));
 | 
			
		||||
            OnTransformUpdated.Invoke(this);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -68,7 +71,8 @@ public class Transform2D : Behaviour, ITransform2D
 | 
			
		||||
            _rotation = value;
 | 
			
		||||
 | 
			
		||||
            UpdateLocalRotation();
 | 
			
		||||
            OnRotationChanged?.Invoke(this, new(previousRotation));
 | 
			
		||||
            OnRotationChanged.Invoke(this, new(previousRotation));
 | 
			
		||||
            OnTransformUpdated.Invoke(this);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -84,7 +88,8 @@ public class Transform2D : Behaviour, ITransform2D
 | 
			
		||||
            _localPosition = value;
 | 
			
		||||
 | 
			
		||||
            UpdatePosition();
 | 
			
		||||
            OnPositionChanged?.Invoke(this, new(previousPosition));
 | 
			
		||||
            OnPositionChanged.Invoke(this, new(previousPosition));
 | 
			
		||||
            OnTransformUpdated.Invoke(this);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -102,8 +107,9 @@ public class Transform2D : Behaviour, ITransform2D
 | 
			
		||||
 | 
			
		||||
            UpdateScale();
 | 
			
		||||
            UpdatePosition();
 | 
			
		||||
            OnScaleChanged?.Invoke(this, new(previousScale));
 | 
			
		||||
            OnPositionChanged?.Invoke(this, new(previousPosition));
 | 
			
		||||
            OnScaleChanged.Invoke(this, new(previousScale));
 | 
			
		||||
            OnPositionChanged.Invoke(this, new(previousPosition));
 | 
			
		||||
            OnTransformUpdated.Invoke(this);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -119,7 +125,8 @@ public class Transform2D : Behaviour, ITransform2D
 | 
			
		||||
            _localRotation = value;
 | 
			
		||||
 | 
			
		||||
            UpdateRotation();
 | 
			
		||||
            OnRotationChanged?.Invoke(this, new(previousRotation));
 | 
			
		||||
            OnRotationChanged.Invoke(this, new(previousRotation));
 | 
			
		||||
            OnTransformUpdated.Invoke(this);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -130,7 +137,8 @@ public class Transform2D : Behaviour, ITransform2D
 | 
			
		||||
 | 
			
		||||
        UpdatePosition();
 | 
			
		||||
 | 
			
		||||
        OnPositionChanged?.Invoke(this, args);
 | 
			
		||||
        OnPositionChanged.Invoke(this, args);
 | 
			
		||||
        OnTransformUpdated.Invoke(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void RecalculateScale(ITransform2D _, ITransform2D.ScaleChangedArguments args)
 | 
			
		||||
@@ -143,8 +151,9 @@ public class Transform2D : Behaviour, ITransform2D
 | 
			
		||||
        UpdateScale();
 | 
			
		||||
        UpdatePosition();
 | 
			
		||||
 | 
			
		||||
        OnScaleChanged?.Invoke(this, args);
 | 
			
		||||
        OnPositionChanged?.Invoke(this, new(previousPosition));
 | 
			
		||||
        OnScaleChanged.Invoke(this, args);
 | 
			
		||||
        OnPositionChanged.Invoke(this, new(previousPosition));
 | 
			
		||||
        OnTransformUpdated.Invoke(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void RecalculateRotation(ITransform2D _, ITransform2D.RotationChangedArguments args)
 | 
			
		||||
@@ -157,8 +166,9 @@ public class Transform2D : Behaviour, ITransform2D
 | 
			
		||||
        UpdateRotation();
 | 
			
		||||
        UpdatePosition();
 | 
			
		||||
 | 
			
		||||
        OnRotationChanged?.Invoke(this, args);
 | 
			
		||||
        OnPositionChanged?.Invoke(this, new(previousPosition));
 | 
			
		||||
        OnRotationChanged.Invoke(this, args);
 | 
			
		||||
        OnPositionChanged.Invoke(this, new(previousPosition));
 | 
			
		||||
        OnTransformUpdated.Invoke(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void UpdateLocalPosition()
 | 
			
		||||
@@ -252,9 +262,10 @@ public class Transform2D : Behaviour, ITransform2D
 | 
			
		||||
        UpdateLocalScale();
 | 
			
		||||
        UpdateLocalRotation();
 | 
			
		||||
 | 
			
		||||
        OnPositionChanged?.Invoke(this, new(Position));
 | 
			
		||||
        OnScaleChanged?.Invoke(this, new(Scale));
 | 
			
		||||
        OnRotationChanged?.Invoke(this, new(Rotation));
 | 
			
		||||
        OnPositionChanged.Invoke(this, new(Position));
 | 
			
		||||
        OnScaleChanged.Invoke(this, new(Scale));
 | 
			
		||||
        OnRotationChanged.Invoke(this, new(Rotation));
 | 
			
		||||
        OnTransformUpdated.Invoke(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void LookForTransform2D(IBehaviourController sender, IBehaviourController.BehaviourAddedArguments args)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										285
									
								
								Engine.Core/Transform3D.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										285
									
								
								Engine.Core/Transform3D.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,285 @@
 | 
			
		||||
using Engine.Core.Serialization;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
[System.Diagnostics.DebuggerDisplay("Name: {UniverseObject.Name, nq} Position: {Position.ToString(), nq}, Scale: {Scale.ToString(), nq}, Rotation: {Rotation}")]
 | 
			
		||||
public class Transform3D : Behaviour, ITransform3D
 | 
			
		||||
{
 | 
			
		||||
    public Event<ITransform3D, ITransform3D.PositionChangedArguments> OnPositionChanged { get; } = new();
 | 
			
		||||
    public Event<ITransform3D, ITransform3D.ScaleChangedArguments> OnScaleChanged { get; } = new();
 | 
			
		||||
    public Event<ITransform3D, ITransform3D.RotationChangedArguments> OnRotationChanged { get; } = new();
 | 
			
		||||
    public Event<ITransform3D> OnTransformUpdated { get; } = new();
 | 
			
		||||
 | 
			
		||||
    private Vector3D _position = Vector3D.Zero;
 | 
			
		||||
    private Vector3D _scale = Vector3D.One;
 | 
			
		||||
    private Quaternion _rotation = Quaternion.Identity;
 | 
			
		||||
 | 
			
		||||
    [Serialize] private Vector3D _localPosition = Vector3D.Zero;
 | 
			
		||||
    [Serialize] private Vector3D _localScale = Vector3D.One;
 | 
			
		||||
    [Serialize] private Quaternion _localRotation = Quaternion.Identity;
 | 
			
		||||
 | 
			
		||||
    private ITransform3D? parentTransform = null;
 | 
			
		||||
 | 
			
		||||
    public Vector3D Up => Quaternion.RotateVector(Vector3D.Up, Rotation);
 | 
			
		||||
    public Vector3D Down => Quaternion.RotateVector(Vector3D.Down, Rotation);
 | 
			
		||||
    public Vector3D Left => Quaternion.RotateVector(Vector3D.Left, Rotation);
 | 
			
		||||
    public Vector3D Right => Quaternion.RotateVector(Vector3D.Right, Rotation);
 | 
			
		||||
    public Vector3D Forward => Quaternion.RotateVector(Vector3D.Forward, Rotation);
 | 
			
		||||
    public Vector3D Backward => Quaternion.RotateVector(Vector3D.Backward, Rotation);
 | 
			
		||||
 | 
			
		||||
    public Vector3D Position
 | 
			
		||||
    {
 | 
			
		||||
        get => _position;
 | 
			
		||||
        set
 | 
			
		||||
        {
 | 
			
		||||
            if (value == _position)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            Vector3D previousPosition = _position;
 | 
			
		||||
            _position = value;
 | 
			
		||||
 | 
			
		||||
            UpdateLocalPosition();
 | 
			
		||||
            OnPositionChanged.Invoke(this, new(previousPosition));
 | 
			
		||||
            OnTransformUpdated.Invoke(this);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Vector3D Scale
 | 
			
		||||
    {
 | 
			
		||||
        get => _scale;
 | 
			
		||||
        set
 | 
			
		||||
        {
 | 
			
		||||
            if (value == _scale)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            Vector3D previousScale = _scale;
 | 
			
		||||
            _scale = value;
 | 
			
		||||
 | 
			
		||||
            UpdateLocalScale();
 | 
			
		||||
            OnScaleChanged.Invoke(this, new(previousScale));
 | 
			
		||||
            OnTransformUpdated.Invoke(this);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Quaternion Rotation
 | 
			
		||||
    {
 | 
			
		||||
        get => _rotation;
 | 
			
		||||
        set
 | 
			
		||||
        {
 | 
			
		||||
            if (value == _rotation)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            Quaternion previousRotation = _rotation;
 | 
			
		||||
            _rotation = value;
 | 
			
		||||
 | 
			
		||||
            UpdateLocalRotation();
 | 
			
		||||
            OnRotationChanged.Invoke(this, new(previousRotation));
 | 
			
		||||
            OnTransformUpdated.Invoke(this);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Vector3D LocalPosition
 | 
			
		||||
    {
 | 
			
		||||
        get => _localPosition;
 | 
			
		||||
        set
 | 
			
		||||
        {
 | 
			
		||||
            if (value == _localPosition)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            Vector3D previousPosition = _position;
 | 
			
		||||
            _localPosition = value;
 | 
			
		||||
 | 
			
		||||
            UpdatePosition();
 | 
			
		||||
            OnPositionChanged.Invoke(this, new(previousPosition));
 | 
			
		||||
            OnTransformUpdated.Invoke(this);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Vector3D LocalScale
 | 
			
		||||
    {
 | 
			
		||||
        get => _localScale;
 | 
			
		||||
        set
 | 
			
		||||
        {
 | 
			
		||||
            if (value == _localScale)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            Vector3D previousScale = _scale;
 | 
			
		||||
            Vector3D previousPosition = _position;
 | 
			
		||||
            _localScale = value;
 | 
			
		||||
 | 
			
		||||
            UpdateScale();
 | 
			
		||||
            UpdatePosition();
 | 
			
		||||
            OnScaleChanged.Invoke(this, new(previousScale));
 | 
			
		||||
            OnPositionChanged.Invoke(this, new(previousPosition));
 | 
			
		||||
            OnTransformUpdated.Invoke(this);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Quaternion LocalRotation
 | 
			
		||||
    {
 | 
			
		||||
        get => _localRotation;
 | 
			
		||||
        set
 | 
			
		||||
        {
 | 
			
		||||
            if (value == _localRotation)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            Quaternion previousRotation = _rotation;
 | 
			
		||||
            _localRotation = value;
 | 
			
		||||
 | 
			
		||||
            UpdateRotation();
 | 
			
		||||
            OnRotationChanged.Invoke(this, new(previousRotation));
 | 
			
		||||
            OnTransformUpdated.Invoke(this);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void RecalculatePosition(ITransform3D _, ITransform3D.PositionChangedArguments args)
 | 
			
		||||
    {
 | 
			
		||||
        if (parentTransform is null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        UpdatePosition();
 | 
			
		||||
 | 
			
		||||
        OnPositionChanged.Invoke(this, args);
 | 
			
		||||
        OnTransformUpdated.Invoke(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void RecalculateScale(ITransform3D _, ITransform3D.ScaleChangedArguments args)
 | 
			
		||||
    {
 | 
			
		||||
        if (parentTransform is null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        Vector3D previousPosition = _position;
 | 
			
		||||
 | 
			
		||||
        UpdateScale();
 | 
			
		||||
        UpdatePosition();
 | 
			
		||||
 | 
			
		||||
        OnScaleChanged.Invoke(this, args);
 | 
			
		||||
        OnPositionChanged.Invoke(this, new(previousPosition));
 | 
			
		||||
        OnTransformUpdated.Invoke(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void RecalculateRotation(ITransform3D _, ITransform3D.RotationChangedArguments args)
 | 
			
		||||
    {
 | 
			
		||||
        if (parentTransform is null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        Vector3D previousPosition = Position;
 | 
			
		||||
 | 
			
		||||
        UpdateRotation();
 | 
			
		||||
        UpdatePosition();
 | 
			
		||||
 | 
			
		||||
        OnRotationChanged.Invoke(this, args);
 | 
			
		||||
        OnPositionChanged.Invoke(this, new(previousPosition));
 | 
			
		||||
        OnTransformUpdated.Invoke(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void UpdateLocalPosition()
 | 
			
		||||
    {
 | 
			
		||||
        if (parentTransform is null)
 | 
			
		||||
            _localPosition = Position;
 | 
			
		||||
        else
 | 
			
		||||
            _localPosition = parentTransform.Position.FromTo(Position).Scale(parentTransform.Scale);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void UpdateLocalScale()
 | 
			
		||||
    {
 | 
			
		||||
        if (parentTransform is null)
 | 
			
		||||
            _localScale = Scale;
 | 
			
		||||
        else
 | 
			
		||||
            _localScale = Scale.Scale(new(1f / parentTransform.Scale.X, 1f / parentTransform.Scale.Y, 1f / parentTransform.Scale.Z));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void UpdateLocalRotation()
 | 
			
		||||
    {
 | 
			
		||||
        if (parentTransform is null)
 | 
			
		||||
            _localRotation = Rotation;
 | 
			
		||||
        else
 | 
			
		||||
            _localRotation = Rotation * parentTransform.Rotation.Invert();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void UpdatePosition()
 | 
			
		||||
    {
 | 
			
		||||
        if (parentTransform is null)
 | 
			
		||||
            _position = LocalPosition;
 | 
			
		||||
        else
 | 
			
		||||
            _position = parentTransform.Position + Quaternion.RotateVector(LocalPosition.Scale(new(parentTransform.Scale.X, parentTransform.Scale.Y, parentTransform.Scale.Z)), parentTransform.Rotation);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void UpdateScale()
 | 
			
		||||
    {
 | 
			
		||||
        if (parentTransform is null)
 | 
			
		||||
            _scale = LocalScale;
 | 
			
		||||
        else
 | 
			
		||||
            _scale = (parentTransform?.Scale ?? Vector3D.One).Scale(_localScale);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void UpdateRotation()
 | 
			
		||||
    {
 | 
			
		||||
        if (parentTransform is null)
 | 
			
		||||
            _rotation = LocalRotation;
 | 
			
		||||
        else
 | 
			
		||||
            _rotation = parentTransform.Rotation * LocalRotation;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void InitializeInternal()
 | 
			
		||||
    {
 | 
			
		||||
        UpdateReferences(UniverseObject.Parent);
 | 
			
		||||
        UniverseObject.OnParentChanged.AddListener(OnParentChanged);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void FinalizeInternal()
 | 
			
		||||
    {
 | 
			
		||||
        UniverseObject.OnParentChanged.RemoveListener(OnParentChanged);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void UpdateReferences(IUniverseObject? parent)
 | 
			
		||||
    {
 | 
			
		||||
        ITransform3D? previousParent = parentTransform;
 | 
			
		||||
        if (previousParent is not null)
 | 
			
		||||
        {
 | 
			
		||||
            previousParent.OnPositionChanged.RemoveListener(RecalculatePosition);
 | 
			
		||||
            previousParent.OnScaleChanged.RemoveListener(RecalculateScale);
 | 
			
		||||
            previousParent.OnRotationChanged.RemoveListener(RecalculateRotation);
 | 
			
		||||
            previousParent.BehaviourController.UniverseObject.OnParentChanged.RemoveListener(OnParentChanged);
 | 
			
		||||
            previousParent.BehaviourController.OnBehaviourAdded.RemoveListener(LookForTransform3D);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        parentTransform = parent?.BehaviourController.GetBehaviour<ITransform3D>();
 | 
			
		||||
 | 
			
		||||
        if (parentTransform is not null)
 | 
			
		||||
        {
 | 
			
		||||
            parentTransform.OnPositionChanged.AddListener(RecalculatePosition);
 | 
			
		||||
            parentTransform.OnScaleChanged.AddListener(RecalculateScale);
 | 
			
		||||
            parentTransform.OnRotationChanged.AddListener(RecalculateRotation);
 | 
			
		||||
            parentTransform.BehaviourController.UniverseObject.OnParentChanged.AddListener(OnParentChanged);
 | 
			
		||||
 | 
			
		||||
            UpdatePosition();
 | 
			
		||||
            UpdateScale();
 | 
			
		||||
            UpdateRotation();
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
            UniverseObject.Parent?.BehaviourController.OnBehaviourAdded.AddListener(LookForTransform3D);
 | 
			
		||||
 | 
			
		||||
        UpdateLocalPosition();
 | 
			
		||||
        UpdateLocalScale();
 | 
			
		||||
        UpdateLocalRotation();
 | 
			
		||||
 | 
			
		||||
        OnPositionChanged.Invoke(this, new(Position));
 | 
			
		||||
        OnScaleChanged.Invoke(this, new(Scale));
 | 
			
		||||
        OnRotationChanged.Invoke(this, new(Rotation));
 | 
			
		||||
        OnTransformUpdated.Invoke(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void LookForTransform3D(IBehaviourController sender, IBehaviourController.BehaviourAddedArguments args)
 | 
			
		||||
    {
 | 
			
		||||
        if (args.BehaviourAdded is not ITransform3D)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        UpdateReferences(UniverseObject.Parent);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void OnParentChanged(IUniverseObject sender, IUniverseObject.ParentChangedArguments args)
 | 
			
		||||
    {
 | 
			
		||||
        UpdateReferences(args.CurrentParent);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -22,7 +22,7 @@ public class Universe : BaseEntity, IUniverse
 | 
			
		||||
    private readonly Event<IInitializable>.EventHandler delegateOnUniverseObjectFinalize = null!;
 | 
			
		||||
    private readonly Event<IUniverseObject, IUniverseObject.ExitedUniverseArguments>.EventHandler delegateOnUniverseObjectExitedUniverse = null!;
 | 
			
		||||
 | 
			
		||||
    private readonly List<IUniverseObject> _universeObjects = new(Constants.UNIVERSE_OBJECTS_SIZE_INITIAL);
 | 
			
		||||
    private readonly FastList<IUniverseObject> _universeObjects = new(Constants.UNIVERSE_OBJECTS_SIZE_INITIAL);
 | 
			
		||||
    private float _timeScale = 1f;
 | 
			
		||||
 | 
			
		||||
    public Universe()
 | 
			
		||||
@@ -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; i >= 0; i--)
 | 
			
		||||
            UniverseObjects[i].Finalize();
 | 
			
		||||
        for (int i = _universeObjects.Count - 1; i >= 0; i--)
 | 
			
		||||
            Remove(_universeObjects[i]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Update(UniverseTime engineTime)
 | 
			
		||||
@@ -131,9 +134,10 @@ public class Universe : BaseEntity, IUniverse
 | 
			
		||||
        UnscaledTime = engineTime;
 | 
			
		||||
        Time = new(TimeSpan.FromTicks((long)(Time.TimeSinceStart.Ticks + engineTime.DeltaSpan.Ticks * TimeScale)), TimeSpan.FromTicks((long)(engineTime.DeltaSpan.Ticks * TimeScale)));
 | 
			
		||||
 | 
			
		||||
        OnPreUpdate?.Invoke(this, new(Time));
 | 
			
		||||
        OnUpdate?.Invoke(this, new(Time));
 | 
			
		||||
        OnPostUpdate?.Invoke(this, new(Time));
 | 
			
		||||
        IUniverse.UpdateArguments args = new(Time);
 | 
			
		||||
        OnPreUpdate?.Invoke(this, args);
 | 
			
		||||
        OnUpdate?.Invoke(this, args);
 | 
			
		||||
        OnPostUpdate?.Invoke(this, args);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Draw()
 | 
			
		||||
@@ -157,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();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,7 @@ public class UniverseObject : BaseEntity, IUniverseObject
 | 
			
		||||
    private IUniverse _universe = null!;
 | 
			
		||||
    private IBehaviourController _behaviourController = null!;
 | 
			
		||||
    private bool _isActive = false;
 | 
			
		||||
    private readonly List<IUniverseObject> _children = [];
 | 
			
		||||
    private readonly FastList<IUniverseObject> _children = [];
 | 
			
		||||
    private IUniverseObject? _parent = null;
 | 
			
		||||
 | 
			
		||||
    public IReadOnlyList<IUniverseObject> Children => _children;
 | 
			
		||||
@@ -100,14 +100,14 @@ public class UniverseObject : BaseEntity, IUniverseObject
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void AddChild(IUniverseObject parent)
 | 
			
		||||
    public void AddChild(IUniverseObject child)
 | 
			
		||||
    {
 | 
			
		||||
        if (_children.Contains(parent))
 | 
			
		||||
        if (_children.Contains(child))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        _children.Add(parent);
 | 
			
		||||
        parent.Parent = this;
 | 
			
		||||
        OnChildrenAdded?.Invoke(this, new(parent));
 | 
			
		||||
        _children.Add(child);
 | 
			
		||||
        child.Parent = this;
 | 
			
		||||
        OnChildrenAdded?.Invoke(this, new(child));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void RemoveChild(IUniverseObject child)
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ using System;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Core;
 | 
			
		||||
 | 
			
		||||
public readonly struct UniverseTime(TimeSpan TimeSinceStart, TimeSpan TimeDelta)
 | 
			
		||||
public readonly record struct UniverseTime(TimeSpan TimeSinceStart, TimeSpan TimeDelta)
 | 
			
		||||
{
 | 
			
		||||
    public readonly TimeSpan TimeSinceStart = TimeSinceStart;
 | 
			
		||||
    public readonly TimeSpan DeltaSpan = TimeDelta;
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
        {
 | 
			
		||||
@@ -56,17 +70,17 @@ public class LiteNetLibClient : LiteNetLibCommunicatorBase, INetworkCommunicator
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void OnEnteredUniverse(IUniverse universe)
 | 
			
		||||
    public override void EnterUniverse(IUniverse universe)
 | 
			
		||||
    {
 | 
			
		||||
        base.OnEnteredUniverse(universe);
 | 
			
		||||
        base.EnterUniverse(universe);
 | 
			
		||||
 | 
			
		||||
        cancellationTokenSource = new CancellationTokenSource();
 | 
			
		||||
        PollEvents(cancellationTokenSource.Token);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void OnExitedUniverse(IUniverse universe)
 | 
			
		||||
    public override void ExitUniverse(IUniverse universe)
 | 
			
		||||
    {
 | 
			
		||||
        base.OnExitedUniverse(universe);
 | 
			
		||||
        base.ExitUniverse(universe);
 | 
			
		||||
        cancellationTokenSource?.Cancel();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,18 @@
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
 | 
			
		||||
using Engine.Core;
 | 
			
		||||
using Engine.Core.Debug;
 | 
			
		||||
using Engine.Systems.Network.Packers;
 | 
			
		||||
 | 
			
		||||
using LiteNetLib;
 | 
			
		||||
using LiteNetLib.Utils;
 | 
			
		||||
 | 
			
		||||
using Engine.Core;
 | 
			
		||||
using Engine.Core.Debug;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Systems.Network;
 | 
			
		||||
 | 
			
		||||
public abstract class LiteNetLibCommunicatorBase : Behaviour, INetworkCommunicator
 | 
			
		||||
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 = [];
 | 
			
		||||
@@ -32,15 +34,13 @@ public abstract class LiteNetLibCommunicatorBase : Behaviour, INetworkCommunicat
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void OnEnteredUniverse(IUniverse universe)
 | 
			
		||||
    public virtual void EnterUniverse(IUniverse universe)
 | 
			
		||||
    {
 | 
			
		||||
        base.OnEnteredUniverse(universe);
 | 
			
		||||
        logger = universe.FindBehaviour<ILogger>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void OnExitedUniverse(IUniverse universe)
 | 
			
		||||
    public virtual void ExitUniverse(IUniverse universe)
 | 
			
		||||
    {
 | 
			
		||||
        base.OnExitedUniverse(universe);
 | 
			
		||||
        logger = null;
 | 
			
		||||
        Stop();
 | 
			
		||||
    }
 | 
			
		||||
@@ -55,7 +55,20 @@ public abstract class LiteNetLibCommunicatorBase : Behaviour, INetworkCommunicat
 | 
			
		||||
 | 
			
		||||
    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); }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -143,19 +156,28 @@ public abstract class LiteNetLibCommunicatorBase : Behaviour, INetworkCommunicat
 | 
			
		||||
    private void SetupEnginePackets()
 | 
			
		||||
    {
 | 
			
		||||
        // I know, ugly af. I need to find a better way
 | 
			
		||||
        netPacketProcessor.RegisterNestedType(AABBNetPacker.Write, AABBNetPacker.Read);
 | 
			
		||||
        netPacketProcessor.RegisterNestedType(AABB2DNetPacker.Write, AABB2DNetPacker.Read);
 | 
			
		||||
        netPacketProcessor.RegisterNestedType(AABB3DNetPacker.Write, AABB3DNetPacker.Read);
 | 
			
		||||
        netPacketProcessor.RegisterNestedType(CircleNetPacker.Write, CircleNetPacker.Read);
 | 
			
		||||
        netPacketProcessor.RegisterNestedType(ColorHSVNetPacker.Write, ColorHSVNetPacker.Read);
 | 
			
		||||
        netPacketProcessor.RegisterNestedType(ColorRGBANetPacker.Write, ColorRGBANetPacker.Read);
 | 
			
		||||
        netPacketProcessor.RegisterNestedType(ColorHSVANetPacker.Write, ColorHSVANetPacker.Read);
 | 
			
		||||
        netPacketProcessor.RegisterNestedType(ColorRGBNetPacker.Write, ColorRGBNetPacker.Read);
 | 
			
		||||
        netPacketProcessor.RegisterNestedType(Line2DEquationNetPacker.Write, Line2DEquationNetPacker.Read);
 | 
			
		||||
        netPacketProcessor.RegisterNestedType(ColorRGBANetPacker.Write, ColorRGBANetPacker.Read);
 | 
			
		||||
        netPacketProcessor.RegisterNestedType(Line2DNetPacker.Write, Line2DNetPacker.Read);
 | 
			
		||||
        netPacketProcessor.RegisterNestedType(Line2DEquationNetPacker.Write, Line2DEquationNetPacker.Read);
 | 
			
		||||
        netPacketProcessor.RegisterNestedType(Line3DNetPacker.Write, Line3DNetPacker.Read);
 | 
			
		||||
        netPacketProcessor.RegisterNestedType(Projection1DNetPacker.Write, Projection1DNetPacker.Read);
 | 
			
		||||
        netPacketProcessor.RegisterNestedType(QuaternionNetPacker.Write, QuaternionNetPacker.Read);
 | 
			
		||||
        netPacketProcessor.RegisterNestedType(Ray2DNetPacker.Write, Ray2DNetPacker.Read);
 | 
			
		||||
        netPacketProcessor.RegisterNestedType(Ray3DNetPacker.Write, Ray3DNetPacker.Read);
 | 
			
		||||
        netPacketProcessor.RegisterNestedType(Shape2DNetPacker.Write, Shape2DNetPacker.Read);
 | 
			
		||||
        netPacketProcessor.RegisterNestedType(Sphere3DNetPacker.Write, Sphere3DNetPacker.Read);
 | 
			
		||||
        netPacketProcessor.RegisterNestedType(TriangleNetPacker.Write, TriangleNetPacker.Read);
 | 
			
		||||
        netPacketProcessor.RegisterNestedType(Vector2DNetPacker.Write, Vector2DNetPacker.Read);
 | 
			
		||||
        netPacketProcessor.RegisterNestedType(Vector2DIntNetPacker.Write, Vector2DIntNetPacker.Read);
 | 
			
		||||
        netPacketProcessor.RegisterNestedType(Vector3DNetPacker.Write, Vector3DNetPacker.Read);
 | 
			
		||||
        netPacketProcessor.RegisterNestedType(Vector3DIntNetPacker.Write, Vector3DIntNetPacker.Read);
 | 
			
		||||
        netPacketProcessor.RegisterNestedType(Vector4DNetPacker.Write, Vector4DNetPacker.Read);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public INetworkCommunicator SubscribeToPackets<T>(Event<IConnection, T>.EventHandler callback)
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
@@ -88,15 +120,15 @@ public class LiteNetLibServer : LiteNetLibCommunicatorBase, INetworkCommunicator
 | 
			
		||||
 | 
			
		||||
    private void PollEvents(IUniverse sender, IUniverse.UpdateArguments args) => Manager.PollEvents();
 | 
			
		||||
 | 
			
		||||
    protected override void OnEnteredUniverse(IUniverse universe)
 | 
			
		||||
    public override void EnterUniverse(IUniverse universe)
 | 
			
		||||
    {
 | 
			
		||||
        base.OnEnteredUniverse(universe);
 | 
			
		||||
        base.EnterUniverse(universe);
 | 
			
		||||
        universe.OnPostUpdate.AddListener(PollEvents);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void OnExitedUniverse(IUniverse universe)
 | 
			
		||||
    public override void ExitUniverse(IUniverse universe)
 | 
			
		||||
    {
 | 
			
		||||
        base.OnExitedUniverse(universe);
 | 
			
		||||
        base.ExitUniverse(universe);
 | 
			
		||||
        universe.OnPostUpdate.RemoveListener(PollEvents);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,21 +2,21 @@ using LiteNetLib.Utils;
 | 
			
		||||
 | 
			
		||||
using Engine.Core;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Systems.Network;
 | 
			
		||||
namespace Engine.Systems.Network.Packers;
 | 
			
		||||
 | 
			
		||||
internal static class AABBNetPacker
 | 
			
		||||
internal static class AABB2DNetPacker
 | 
			
		||||
{
 | 
			
		||||
    internal static void Write(NetDataWriter writer, AABB data)
 | 
			
		||||
    internal static void Write(NetDataWriter writer, AABB2D data)
 | 
			
		||||
    {
 | 
			
		||||
        Vector2DNetPacker.Write(writer, data.LowerBoundary);
 | 
			
		||||
        Vector2DNetPacker.Write(writer, data.UpperBoundary);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal static AABB Read(NetDataReader reader)
 | 
			
		||||
    internal static AABB2D Read(NetDataReader reader)
 | 
			
		||||
    {
 | 
			
		||||
        Vector2D lowerBoundary = Vector2DNetPacker.Read(reader);
 | 
			
		||||
        Vector2D upperBoundary = Vector2DNetPacker.Read(reader);
 | 
			
		||||
 | 
			
		||||
        return new AABB(lowerBoundary, upperBoundary);
 | 
			
		||||
        return new AABB2D(lowerBoundary, upperBoundary);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,22 @@
 | 
			
		||||
using LiteNetLib.Utils;
 | 
			
		||||
 | 
			
		||||
using Engine.Core;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Systems.Network.Packers;
 | 
			
		||||
 | 
			
		||||
internal static class AABB3DNetPacker
 | 
			
		||||
{
 | 
			
		||||
    internal static void Write(NetDataWriter writer, AABB3D data)
 | 
			
		||||
    {
 | 
			
		||||
        Vector3DNetPacker.Write(writer, data.LowerBoundary);
 | 
			
		||||
        Vector3DNetPacker.Write(writer, data.UpperBoundary);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal static AABB3D Read(NetDataReader reader)
 | 
			
		||||
    {
 | 
			
		||||
        Vector3D lowerBoundary = Vector3DNetPacker.Read(reader);
 | 
			
		||||
        Vector3D upperBoundary = Vector3DNetPacker.Read(reader);
 | 
			
		||||
 | 
			
		||||
        return new AABB3D(lowerBoundary, upperBoundary);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -2,7 +2,7 @@ using LiteNetLib.Utils;
 | 
			
		||||
 | 
			
		||||
using Engine.Core;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Systems.Network;
 | 
			
		||||
namespace Engine.Systems.Network.Packers;
 | 
			
		||||
 | 
			
		||||
internal static class CircleNetPacker
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,26 @@
 | 
			
		||||
using LiteNetLib.Utils;
 | 
			
		||||
 | 
			
		||||
using Engine.Core;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Systems.Network.Packers;
 | 
			
		||||
 | 
			
		||||
internal static class ColorHSVANetPacker
 | 
			
		||||
{
 | 
			
		||||
    internal static void Write(NetDataWriter writer, ColorHSVA data)
 | 
			
		||||
    {
 | 
			
		||||
        writer.Put(data.Hue);
 | 
			
		||||
        writer.Put(data.Saturation);
 | 
			
		||||
        writer.Put(data.Value);
 | 
			
		||||
        writer.Put(data.Alpha);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal static ColorHSVA Read(NetDataReader reader)
 | 
			
		||||
    {
 | 
			
		||||
        float hue = reader.GetFloat();
 | 
			
		||||
        float saturation = reader.GetFloat();
 | 
			
		||||
        float value = reader.GetFloat();
 | 
			
		||||
        float alpha = reader.GetFloat();
 | 
			
		||||
 | 
			
		||||
        return new ColorHSVA(hue, saturation, value, alpha);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -2,7 +2,7 @@ using LiteNetLib.Utils;
 | 
			
		||||
 | 
			
		||||
using Engine.Core;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Systems.Network;
 | 
			
		||||
namespace Engine.Systems.Network.Packers;
 | 
			
		||||
 | 
			
		||||
internal static class ColorHSVNetPacker
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ using LiteNetLib.Utils;
 | 
			
		||||
 | 
			
		||||
using Engine.Core;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Systems.Network;
 | 
			
		||||
namespace Engine.Systems.Network.Packers;
 | 
			
		||||
 | 
			
		||||
internal static class ColorRGBANetPacker
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ using LiteNetLib.Utils;
 | 
			
		||||
 | 
			
		||||
using Engine.Core;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Systems.Network;
 | 
			
		||||
namespace Engine.Systems.Network.Packers;
 | 
			
		||||
 | 
			
		||||
internal static class ColorRGBNetPacker
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ using LiteNetLib.Utils;
 | 
			
		||||
 | 
			
		||||
using Engine.Core;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Systems.Network;
 | 
			
		||||
namespace Engine.Systems.Network.Packers;
 | 
			
		||||
 | 
			
		||||
internal static class Line2DEquationNetPacker
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ using LiteNetLib.Utils;
 | 
			
		||||
 | 
			
		||||
using Engine.Core;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Systems.Network;
 | 
			
		||||
namespace Engine.Systems.Network.Packers;
 | 
			
		||||
 | 
			
		||||
internal static class Line2DNetPacker
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,22 @@
 | 
			
		||||
using LiteNetLib.Utils;
 | 
			
		||||
 | 
			
		||||
using Engine.Core;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Systems.Network.Packers;
 | 
			
		||||
 | 
			
		||||
internal static class Line3DNetPacker
 | 
			
		||||
{
 | 
			
		||||
    internal static void Write(NetDataWriter writer, Line3D data)
 | 
			
		||||
    {
 | 
			
		||||
        Vector3DNetPacker.Write(writer, data.From);
 | 
			
		||||
        Vector3DNetPacker.Write(writer, data.To);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal static Line3D Read(NetDataReader reader)
 | 
			
		||||
    {
 | 
			
		||||
        Vector3D from = Vector3DNetPacker.Read(reader);
 | 
			
		||||
        Vector3D to = Vector3DNetPacker.Read(reader);
 | 
			
		||||
 | 
			
		||||
        return new Line3D(from, to);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -2,7 +2,7 @@ using LiteNetLib.Utils;
 | 
			
		||||
 | 
			
		||||
using Engine.Core;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Systems.Network;
 | 
			
		||||
namespace Engine.Systems.Network.Packers;
 | 
			
		||||
 | 
			
		||||
internal static class Projection1DNetPacker
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ using LiteNetLib.Utils;
 | 
			
		||||
 | 
			
		||||
using Engine.Core;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Systems.Network;
 | 
			
		||||
namespace Engine.Systems.Network.Packers;
 | 
			
		||||
 | 
			
		||||
internal static class QuaternionNetPacker
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,22 @@
 | 
			
		||||
using LiteNetLib.Utils;
 | 
			
		||||
 | 
			
		||||
using Engine.Core;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Systems.Network.Packers;
 | 
			
		||||
 | 
			
		||||
internal static class Ray2DNetPacker
 | 
			
		||||
{
 | 
			
		||||
    internal static void Write(NetDataWriter writer, Ray2D data)
 | 
			
		||||
    {
 | 
			
		||||
        Vector2DNetPacker.Write(writer, data.Origin);
 | 
			
		||||
        Vector2DNetPacker.Write(writer, data.Direction);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal static Ray2D Read(NetDataReader reader)
 | 
			
		||||
    {
 | 
			
		||||
        Vector2D from = Vector2DNetPacker.Read(reader);
 | 
			
		||||
        Vector2D direction = Vector2DNetPacker.Read(reader);
 | 
			
		||||
 | 
			
		||||
        return new Ray2D(from, direction);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,22 @@
 | 
			
		||||
using LiteNetLib.Utils;
 | 
			
		||||
 | 
			
		||||
using Engine.Core;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Systems.Network.Packers;
 | 
			
		||||
 | 
			
		||||
internal static class Ray3DNetPacker
 | 
			
		||||
{
 | 
			
		||||
    internal static void Write(NetDataWriter writer, Ray3D data)
 | 
			
		||||
    {
 | 
			
		||||
        Vector3DNetPacker.Write(writer, data.Origin);
 | 
			
		||||
        Vector3DNetPacker.Write(writer, data.Direction);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal static Ray3D Read(NetDataReader reader)
 | 
			
		||||
    {
 | 
			
		||||
        Vector3D from = Vector3DNetPacker.Read(reader);
 | 
			
		||||
        Vector3D direction = Vector3DNetPacker.Read(reader);
 | 
			
		||||
 | 
			
		||||
        return new Ray3D(from, direction);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -2,7 +2,7 @@ using LiteNetLib.Utils;
 | 
			
		||||
 | 
			
		||||
using Engine.Core;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Systems.Network;
 | 
			
		||||
namespace Engine.Systems.Network.Packers;
 | 
			
		||||
 | 
			
		||||
internal static class Shape2DNetPacker
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,22 @@
 | 
			
		||||
using LiteNetLib.Utils;
 | 
			
		||||
 | 
			
		||||
using Engine.Core;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Systems.Network.Packers;
 | 
			
		||||
 | 
			
		||||
internal static class Sphere3DNetPacker
 | 
			
		||||
{
 | 
			
		||||
    internal static void Write(NetDataWriter writer, Sphere3D data)
 | 
			
		||||
    {
 | 
			
		||||
        Vector3DNetPacker.Write(writer, data.Center);
 | 
			
		||||
        writer.Put(data.Radius);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal static Sphere3D Read(NetDataReader reader)
 | 
			
		||||
    {
 | 
			
		||||
        Vector3D center = Vector3DNetPacker.Read(reader);
 | 
			
		||||
        float radius = reader.GetFloat();
 | 
			
		||||
 | 
			
		||||
        return new Sphere3D(center, radius);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -2,7 +2,7 @@ using LiteNetLib.Utils;
 | 
			
		||||
 | 
			
		||||
using Engine.Core;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Systems.Network;
 | 
			
		||||
namespace Engine.Systems.Network.Packers;
 | 
			
		||||
 | 
			
		||||
internal static class TriangleNetPacker
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,22 @@
 | 
			
		||||
using LiteNetLib.Utils;
 | 
			
		||||
 | 
			
		||||
using Engine.Core;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Systems.Network.Packers;
 | 
			
		||||
 | 
			
		||||
internal static class Vector2DIntNetPacker
 | 
			
		||||
{
 | 
			
		||||
    internal static void Write(NetDataWriter writer, Vector2DInt data)
 | 
			
		||||
    {
 | 
			
		||||
        writer.Put(data.X);
 | 
			
		||||
        writer.Put(data.Y);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal static Vector2DInt Read(NetDataReader reader)
 | 
			
		||||
    {
 | 
			
		||||
        int x = reader.GetInt();
 | 
			
		||||
        int y = reader.GetInt();
 | 
			
		||||
 | 
			
		||||
        return new Vector2DInt(x, y);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -2,7 +2,7 @@ using LiteNetLib.Utils;
 | 
			
		||||
 | 
			
		||||
using Engine.Core;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Systems.Network;
 | 
			
		||||
namespace Engine.Systems.Network.Packers;
 | 
			
		||||
 | 
			
		||||
internal static class Vector2DNetPacker
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,24 @@
 | 
			
		||||
using LiteNetLib.Utils;
 | 
			
		||||
 | 
			
		||||
using Engine.Core;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Systems.Network.Packers;
 | 
			
		||||
 | 
			
		||||
internal static class Vector3DIntNetPacker
 | 
			
		||||
{
 | 
			
		||||
    internal static void Write(NetDataWriter writer, Vector3DInt data)
 | 
			
		||||
    {
 | 
			
		||||
        writer.Put(data.X);
 | 
			
		||||
        writer.Put(data.Y);
 | 
			
		||||
        writer.Put(data.Z);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal static Vector3DInt Read(NetDataReader reader)
 | 
			
		||||
    {
 | 
			
		||||
        int x = reader.GetInt();
 | 
			
		||||
        int y = reader.GetInt();
 | 
			
		||||
        int z = reader.GetInt();
 | 
			
		||||
 | 
			
		||||
        return new Vector3DInt(x, y, z);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -2,7 +2,7 @@ using LiteNetLib.Utils;
 | 
			
		||||
 | 
			
		||||
using Engine.Core;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Systems.Network;
 | 
			
		||||
namespace Engine.Systems.Network.Packers;
 | 
			
		||||
 | 
			
		||||
internal static class Vector3DNetPacker
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,26 @@
 | 
			
		||||
using LiteNetLib.Utils;
 | 
			
		||||
 | 
			
		||||
using Engine.Core;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Systems.Network.Packers;
 | 
			
		||||
 | 
			
		||||
internal static class Vector4DNetPacker
 | 
			
		||||
{
 | 
			
		||||
    internal static void Write(NetDataWriter writer, Vector4D data)
 | 
			
		||||
    {
 | 
			
		||||
        writer.Put(data.X);
 | 
			
		||||
        writer.Put(data.Y);
 | 
			
		||||
        writer.Put(data.Z);
 | 
			
		||||
        writer.Put(data.W);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal static Vector4D Read(NetDataReader reader)
 | 
			
		||||
    {
 | 
			
		||||
        float x = reader.GetFloat();
 | 
			
		||||
        float y = reader.GetFloat();
 | 
			
		||||
        float z = reader.GetFloat();
 | 
			
		||||
        float w = reader.GetFloat();
 | 
			
		||||
 | 
			
		||||
        return new Vector4D(x, y, z, w);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -10,13 +10,13 @@ namespace Engine.Integration.MonoGame;
 | 
			
		||||
public interface ISpriteBatch
 | 
			
		||||
{
 | 
			
		||||
    void Begin(SpriteSortMode sortMode = SpriteSortMode.Deferred, BlendState? blendState = null, SamplerState? samplerState = null, DepthStencilState? depthStencilState = null, RasterizerState? rasterizerState = null, Effect? effect = null, Matrix? transformMatrix = null);
 | 
			
		||||
    void Draw(Texture2D texture, Vector2D position, AABB? sourceAABB, Color color, float rotation, Vector2D origin, Vector2D scale, SpriteEffects effects, float layerDepth);
 | 
			
		||||
    void Draw(Texture2D texture, Vector2D position, AABB? sourceAABB, Color color, float rotation, Vector2D origin, float scale, SpriteEffects effects, float layerDepth);
 | 
			
		||||
    void Draw(Texture2D texture, AABB destinationAABB, AABB? sourceAABB, Color color, float rotation, Vector2D origin, SpriteEffects effects, float layerDepth);
 | 
			
		||||
    void Draw(Texture2D texture, Vector2D position, AABB? sourceAABB, Color color);
 | 
			
		||||
    void Draw(Texture2D texture, AABB destinationAABB, AABB? sourceAABB, Color color);
 | 
			
		||||
    void Draw(Texture2D texture, Vector2D position, AABB2D? sourceAABB, Color color, float rotation, Vector2D origin, Vector2D scale, SpriteEffects effects, float layerDepth);
 | 
			
		||||
    void Draw(Texture2D texture, Vector2D position, AABB2D? sourceAABB, Color color, float rotation, Vector2D origin, float scale, SpriteEffects effects, float layerDepth);
 | 
			
		||||
    void Draw(Texture2D texture, AABB2D destinationAABB, AABB2D? sourceAABB, Color color, float rotation, Vector2D origin, SpriteEffects effects, float layerDepth);
 | 
			
		||||
    void Draw(Texture2D texture, Vector2D position, AABB2D? sourceAABB, Color color);
 | 
			
		||||
    void Draw(Texture2D texture, AABB2D destinationAABB, AABB2D? sourceAABB, Color color);
 | 
			
		||||
    void Draw(Texture2D texture, Vector2D position, Color color);
 | 
			
		||||
    void Draw(Texture2D texture, AABB destinationAABB, Color color);
 | 
			
		||||
    void Draw(Texture2D texture, AABB2D destinationAABB, Color color);
 | 
			
		||||
    void DrawString(SpriteFont spriteFont, string text, Vector2D position, Color color);
 | 
			
		||||
    void DrawString(SpriteFont spriteFont, string text, Vector2D position, Color color, float rotation, Vector2D origin, float scale, SpriteEffects effects, float layerDepth);
 | 
			
		||||
    void DrawString(SpriteFont spriteFont, string text, Vector2D position, Color color, float rotation, Vector2D origin, Vector2D scale, SpriteEffects effects, float layerDepth);
 | 
			
		||||
 
 | 
			
		||||
@@ -10,11 +10,11 @@ namespace Engine.Integration.MonoGame;
 | 
			
		||||
 | 
			
		||||
public class KeyboardInputs : Behaviour, IButtonInputs<Keys>, IUpdate
 | 
			
		||||
{
 | 
			
		||||
    public Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments> OnAnyButtonPressed { get; } = new();
 | 
			
		||||
    public Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments> OnAnyButtonReleased { get; } = new();
 | 
			
		||||
    public IButtonInputs<Keys>.InputEvent OnAnyButtonPressed { get; } = new();
 | 
			
		||||
    public IButtonInputs<Keys>.InputEvent OnAnyButtonReleased { get; } = new();
 | 
			
		||||
 | 
			
		||||
    private readonly Dictionary<Keys, Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>> OnPressed = new(256);
 | 
			
		||||
    private readonly Dictionary<Keys, Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>> OnReleased = new(256);
 | 
			
		||||
    private readonly Dictionary<Keys, IButtonInputs<Keys>.InputEvent> OnPressed = new(256);
 | 
			
		||||
    private readonly Dictionary<Keys, IButtonInputs<Keys>.InputEvent> OnReleased = new(256);
 | 
			
		||||
 | 
			
		||||
    private int cachePressedCurrentlyCount = 0;
 | 
			
		||||
    private readonly Keys[] cachePressedCurrently = new Keys[256];
 | 
			
		||||
@@ -22,9 +22,9 @@ public class KeyboardInputs : Behaviour, IButtonInputs<Keys>, IUpdate
 | 
			
		||||
    private int cachePressedPreviouslyCount = 0;
 | 
			
		||||
    private readonly Keys[] cachePressedPreviously = new Keys[256];
 | 
			
		||||
 | 
			
		||||
    public void RegisterOnPress(Keys key, Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>.EventHandler callback)
 | 
			
		||||
    public void RegisterOnPress(Keys key, IButtonInputs<Keys>.InputEvent.EventHandler callback)
 | 
			
		||||
    {
 | 
			
		||||
        if (!OnPressed.TryGetValue(key, out Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>? delegateCallback))
 | 
			
		||||
        if (!OnPressed.TryGetValue(key, out IButtonInputs<Keys>.InputEvent? delegateCallback))
 | 
			
		||||
        {
 | 
			
		||||
            delegateCallback = new();
 | 
			
		||||
            OnPressed.Add(key, delegateCallback);
 | 
			
		||||
@@ -33,15 +33,15 @@ public class KeyboardInputs : Behaviour, IButtonInputs<Keys>, IUpdate
 | 
			
		||||
        delegateCallback.AddListener(callback);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void UnregisterOnPress(Keys key, Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>.EventHandler callback)
 | 
			
		||||
    public void UnregisterOnPress(Keys key, IButtonInputs<Keys>.InputEvent.EventHandler callback)
 | 
			
		||||
    {
 | 
			
		||||
        if (OnPressed.TryGetValue(key, out Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>? delegateCallback))
 | 
			
		||||
        if (OnPressed.TryGetValue(key, out IButtonInputs<Keys>.InputEvent? delegateCallback))
 | 
			
		||||
            delegateCallback.RemoveListener(callback);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void RegisterOnRelease(Keys key, Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>.EventHandler callback)
 | 
			
		||||
    public void RegisterOnRelease(Keys key, IButtonInputs<Keys>.InputEvent.EventHandler callback)
 | 
			
		||||
    {
 | 
			
		||||
        if (!OnReleased.TryGetValue(key, out Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>? delegateCallback))
 | 
			
		||||
        if (!OnReleased.TryGetValue(key, out IButtonInputs<Keys>.InputEvent? delegateCallback))
 | 
			
		||||
        {
 | 
			
		||||
            delegateCallback = new();
 | 
			
		||||
            OnReleased.Add(key, delegateCallback);
 | 
			
		||||
@@ -50,9 +50,9 @@ public class KeyboardInputs : Behaviour, IButtonInputs<Keys>, IUpdate
 | 
			
		||||
        delegateCallback.AddListener(callback);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void UnregisterOnRelease(Keys key, Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>.EventHandler callback)
 | 
			
		||||
    public void UnregisterOnRelease(Keys key, IButtonInputs<Keys>.InputEvent.EventHandler callback)
 | 
			
		||||
    {
 | 
			
		||||
        if (OnReleased.TryGetValue(key, out Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>? delegateCallback))
 | 
			
		||||
        if (OnReleased.TryGetValue(key, out IButtonInputs<Keys>.InputEvent? delegateCallback))
 | 
			
		||||
            delegateCallback.RemoveListener(callback);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -69,7 +69,7 @@ public class KeyboardInputs : Behaviour, IButtonInputs<Keys>, IUpdate
 | 
			
		||||
            if (WasPressed(currentlyPressedKey))
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            if (OnPressed.TryGetValue(currentlyPressedKey, out Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>? callback))
 | 
			
		||||
            if (OnPressed.TryGetValue(currentlyPressedKey, out IButtonInputs<Keys>.InputEvent? callback))
 | 
			
		||||
                callback?.Invoke(this, new(currentlyPressedKey));
 | 
			
		||||
 | 
			
		||||
            OnAnyButtonPressed?.Invoke(this, new(currentlyPressedKey));
 | 
			
		||||
@@ -82,7 +82,7 @@ public class KeyboardInputs : Behaviour, IButtonInputs<Keys>, IUpdate
 | 
			
		||||
            if (IsPressed(previouslyPressedKey))
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            if (OnReleased.TryGetValue(previouslyPressedKey, out Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>? callback))
 | 
			
		||||
            if (OnReleased.TryGetValue(previouslyPressedKey, out IButtonInputs<Keys>.InputEvent? callback))
 | 
			
		||||
                callback?.Invoke(this, new(previouslyPressedKey));
 | 
			
		||||
 | 
			
		||||
            OnAnyButtonReleased?.Invoke(this, new(previouslyPressedKey));
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,16 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
using Engine.Core;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Integration.MonoGame;
 | 
			
		||||
 | 
			
		||||
public class LoadContentManager : Behaviour, IFirstFrameUpdate
 | 
			
		||||
public class LoadContentManager : Behaviour, IEnterUniverse, IExitUniverse, IFirstFrameUpdate
 | 
			
		||||
{
 | 
			
		||||
    // We use Ascending order because we are using reverse for loop to call them
 | 
			
		||||
    private static Comparer<IBehaviour> SortByAscendingPriority() => Comparer<IBehaviour>.Create((x, y) => x.Priority.CompareTo(y.Priority));
 | 
			
		||||
    private static Comparer<int> SortByAscendingPriority() => Comparer<int>.Create((x, y) => x.CompareTo(y));
 | 
			
		||||
    private static System.Func<IBehaviour, int> GetPriority() => (b) => b.Priority;
 | 
			
		||||
 | 
			
		||||
    private readonly ActiveBehaviourCollectorSorted<ILoadContent> loadContents = new() { SortBy = SortByAscendingPriority() };
 | 
			
		||||
    private readonly ActiveBehaviourCollectorOrdered<int, ILoadContent> loadContents = new(GetPriority(), SortByAscendingPriority());
 | 
			
		||||
    private readonly List<ILoadContent> toCallLoadContents = new(32);
 | 
			
		||||
 | 
			
		||||
    private MonoGameWindowContainer monoGameWindowContainer = null!;
 | 
			
		||||
@@ -18,14 +20,14 @@ public class LoadContentManager : Behaviour, IFirstFrameUpdate
 | 
			
		||||
        monoGameWindowContainer = Universe.FindRequiredBehaviour<MonoGameWindowContainer>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void OnEnteredUniverse(IUniverse universe)
 | 
			
		||||
    public void EnterUniverse(IUniverse universe)
 | 
			
		||||
    {
 | 
			
		||||
        loadContents.Assign(universe);
 | 
			
		||||
 | 
			
		||||
        universe.OnPreUpdate.AddListener(OnPreUpdate);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected override void OnExitedUniverse(IUniverse universe)
 | 
			
		||||
    public void ExitUniverse(IUniverse universe)
 | 
			
		||||
    {
 | 
			
		||||
        loadContents.Unassign();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -5,11 +5,11 @@ using Engine.Core;
 | 
			
		||||
 | 
			
		||||
namespace Engine.Integration.MonoGame;
 | 
			
		||||
 | 
			
		||||
public class MonoGameCamera2D : BehaviourBase, ICamera2D, IFirstFrameUpdate, IPreDraw
 | 
			
		||||
public class MonoGameCamera2D : Behaviour, ICamera2D, IFirstFrameUpdate, ILastFrameUpdate, IPreDraw
 | 
			
		||||
{
 | 
			
		||||
    public event MatrixTransformChangedArguments? OnMatrixTransformChanged = null;
 | 
			
		||||
    public event ViewportChangedArguments? OnViewportChanged = null;
 | 
			
		||||
    public event ZoomChangedArguments? OnZoomChanged = null;
 | 
			
		||||
    public Event<MonoGameCamera2D> OnMatrixTransformChanged { get; } = new();
 | 
			
		||||
    public Event<MonoGameCamera2D> OnViewportChanged { get; } = new();
 | 
			
		||||
    public Event<MonoGameCamera2D> OnZoomChanged { get; } = new();
 | 
			
		||||
 | 
			
		||||
    private Matrix _matrixTransform = Matrix.Identity;
 | 
			
		||||
 | 
			
		||||
@@ -28,7 +28,7 @@ public class MonoGameCamera2D : BehaviourBase, ICamera2D, IFirstFrameUpdate, IPr
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            _matrixTransform = value;
 | 
			
		||||
            OnMatrixTransformChanged?.Invoke(this);
 | 
			
		||||
            OnMatrixTransformChanged.Invoke(this);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -47,7 +47,7 @@ public class MonoGameCamera2D : BehaviourBase, ICamera2D, IFirstFrameUpdate, IPr
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            _viewport = value;
 | 
			
		||||
            OnViewportChanged?.Invoke(this);
 | 
			
		||||
            OnViewportChanged.Invoke(this);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -56,13 +56,13 @@ public class MonoGameCamera2D : BehaviourBase, ICamera2D, IFirstFrameUpdate, IPr
 | 
			
		||||
        get => _zoom;
 | 
			
		||||
        set
 | 
			
		||||
        {
 | 
			
		||||
            float newValue = Engine.Core.Math.Max(0.1f, value);
 | 
			
		||||
            float newValue = Math.Max(0.1f, value);
 | 
			
		||||
 | 
			
		||||
            if (_zoom == newValue)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            _zoom = newValue;
 | 
			
		||||
            OnZoomChanged?.Invoke(this);
 | 
			
		||||
            OnZoomChanged.Invoke(this);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -84,26 +84,21 @@ public class MonoGameCamera2D : BehaviourBase, ICamera2D, IFirstFrameUpdate, IPr
 | 
			
		||||
        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!;
 | 
			
		||||
 | 
			
		||||
    public delegate void MatrixTransformChangedArguments(MonoGameCamera2D sender);
 | 
			
		||||
    public delegate void ViewportChangedArguments(MonoGameCamera2D sender);
 | 
			
		||||
    public delegate void ZoomChangedArguments(MonoGameCamera2D sender);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user