feat: added 3D AABB primitive
This commit is contained in:
111
Engine.Core/Primitives/AABB3D.cs
Normal file
111
Engine.Core/Primitives/AABB3D.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
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;
|
||||
|
||||
/// <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);
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
using LiteNetLib.Utils;
|
||||
|
||||
using Engine.Core;
|
||||
|
||||
namespace Engine.Systems.Network;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
|
||||
using Engine.Core;
|
||||
|
||||
using YamlDotNet.Core;
|
||||
using YamlDotNet.Core.Events;
|
||||
using YamlDotNet.Serialization;
|
||||
|
||||
namespace Engine.Serializers.Yaml;
|
||||
|
||||
public class AABB3DConverter : EngineTypeYamlSerializerBase<AABB3D>
|
||||
{
|
||||
public override AABB3D Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
|
||||
{
|
||||
parser.Consume<MappingStart>();
|
||||
|
||||
if (parser.Consume<Scalar>().Value.CompareTo(nameof(AABB3D.LowerBoundary)) != 0)
|
||||
throw new ArgumentException($"{nameof(AABB3D)} mapping must start with {nameof(AABB3D.LowerBoundary)}");
|
||||
Vector3D lowerBoundary = (Vector3D)rootDeserializer(typeof(Vector3D))!;
|
||||
|
||||
if (parser.Consume<Scalar>().Value.CompareTo(nameof(AABB3D.UpperBoundary)) != 0)
|
||||
throw new ArgumentException($"{nameof(AABB3D)} mapping must end with {nameof(AABB3D.UpperBoundary)}");
|
||||
Vector3D upperBoundary = (Vector3D)rootDeserializer(typeof(Vector3D))!;
|
||||
|
||||
parser.Consume<MappingEnd>();
|
||||
|
||||
return new AABB3D(lowerBoundary, upperBoundary);
|
||||
}
|
||||
|
||||
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
|
||||
{
|
||||
AABB3D aabb = (AABB3D)value!;
|
||||
|
||||
emitter.Emit(new MappingStart());
|
||||
emitter.Emit(new Scalar(nameof(AABB3D.LowerBoundary)));
|
||||
serializer(aabb.LowerBoundary, typeof(Vector3D));
|
||||
emitter.Emit(new Scalar(nameof(AABB3D.UpperBoundary)));
|
||||
serializer(aabb.UpperBoundary, typeof(Vector3D));
|
||||
emitter.Emit(new MappingEnd());
|
||||
}
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
using Engine.Core;
|
||||
|
||||
namespace Engine.Systems.Tween;
|
||||
|
||||
public static class TweenAABB3DExtensions
|
||||
{
|
||||
private static readonly BoxedPool<AABB3D> boxedAABBPool = new(2);
|
||||
|
||||
public static ITween TweenAABB(this AABB3D initialAABB, ITweenManager tweenManager, float duration, AABB3D targetAABB, System.Action<AABB3D> setMethod)
|
||||
{
|
||||
Boxed<AABB3D> boxedInitial = boxedAABBPool.Get(initialAABB);
|
||||
Boxed<AABB3D> boxedTarget = boxedAABBPool.Get(targetAABB);
|
||||
|
||||
ITween tween = tweenManager.StartTween(duration, t => setMethod?.Invoke(new AABB3D(boxedInitial.Value.LowerBoundary.Lerp(boxedTarget.Value.LowerBoundary, t), boxedInitial.Value.UpperBoundary.Lerp(boxedTarget.Value.UpperBoundary, t))));
|
||||
|
||||
tween.OnComplete(() =>
|
||||
{
|
||||
boxedAABBPool.Return(boxedInitial);
|
||||
boxedAABBPool.Return(boxedTarget);
|
||||
});
|
||||
|
||||
return tween;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user