20 Commits

Author SHA1 Message Date
32ec2325dc fix: YamlDotNet being on the wrong branch fixed 2025-10-19 23:44:49 +03:00
b42f1f1881 fix: added missing types for new primitives 2025-10-19 19:03:30 +03:00
f8096377b2 chore: removed unsupported leftover methods from int vectors 2025-10-19 19:02:37 +03:00
a9fc819268 feat: added 3D AABB primitive 2025-10-19 18:45:57 +03:00
1d63391975 chore!: renamed AABB to AABB2D 2025-10-19 18:45:48 +03:00
61ff0887e2 feat: 3D camera added 2025-10-19 00:28:40 +03:00
16344dccc7 feat: 3D transforms added 2025-10-19 00:24:56 +03:00
dc4bea3eef feat: Vector4D added 2025-10-19 00:24:19 +03:00
d1b2723a70 feat: 3D ray and line primitives added 2025-10-19 00:24:06 +03:00
2f5c04e66b feat: ITransform.OnTransformUpdated event added 2025-10-19 00:22:30 +03:00
f753da1f87 chore: added XNA Vector3 and Quaternion conversions 2025-10-19 00:16:39 +03:00
6901159106 fix: Quaternion.RotateVector method not working properly on some angles fixed 2025-10-19 00:16:07 +03:00
7469c9ab0c docs: Vector3D.FromAxisAngle parameters updated 2025-10-19 00:15:09 +03:00
ede90adbdc feat: quaternion rotate method added 2025-10-19 00:14:39 +03:00
eeaca3a6c7 feat: quaternion to angles conversion added 2025-10-19 00:13:59 +03:00
3b984a0a4b feat: added Vector3D.Transform method 2025-10-19 00:13:01 +03:00
c5afb70b18 docs: vector3d rotate parameters updated 2025-10-19 00:12:35 +03:00
9d2a192f04 refactor: monogame 2D camera now use engine events 2025-10-19 00:11:51 +03:00
598debc233 perf: removed unnecessary null checks on events 2025-10-19 00:10:07 +03:00
ab05a89175 feat: line 2D to line 2D equation implicit operator added 2025-10-18 14:43:00 +03:00
52 changed files with 2120 additions and 284 deletions

View File

@@ -0,0 +1,6 @@
namespace Engine.Core;
public interface IBehaviour3D : IBehaviour
{
ITransform3D Transform { get; }
}

View File

@@ -0,0 +1,26 @@
namespace Engine.Core;
/// <summary>
/// Represents a 3D camera in the engine.
/// </summary>
public interface ICamera3D : IBehaviour3D
{
/// <summary>
/// Field of View (FOV) value of the camera
/// </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);
}

View File

@@ -16,10 +16,15 @@ public interface ITransform2D : IBehaviour
Event<ITransform2D, ScaleChangedArguments> OnScaleChanged { get; } Event<ITransform2D, ScaleChangedArguments> OnScaleChanged { get; }
/// <summary> /// <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> /// </summary>
Event<ITransform2D, RotationChangedArguments> OnRotationChanged { get; } 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> /// <summary>
/// The <see cref="Vector2D"/> pointing upwards in world space. /// The <see cref="Vector2D"/> pointing upwards in world space.
/// </summary> /// </summary>

View 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);
}

View File

@@ -14,4 +14,17 @@ public static class TransformExtensions
if (localScale.HasValue) transform.LocalScale = localScale.Value; if (localScale.HasValue) transform.LocalScale = localScale.Value;
return transform; 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;
}
} }

View File

@@ -1,111 +0,0 @@
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="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) : IEquatable<AABB>
{
/// <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;
public bool Equals(AABB other) => this == other;
/// <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);
}

View File

@@ -0,0 +1,111 @@
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;
/// <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);
}

View 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);
}

View File

@@ -50,15 +50,7 @@ public readonly struct Line2D(Vector2D from, Vector2D to) : IEquatable<Line2D>
/// <summary> /// <summary>
/// The equation of the <see cref="Line2D"/> defined by this <see cref="Line2D"/> segment. /// The equation of the <see cref="Line2D"/> defined by this <see cref="Line2D"/> segment.
/// </summary> /// </summary>
public static Line2DEquation GetLineEquation(Line2D line) public static Line2DEquation GetLineEquation(Line2D line) => 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> /// <summary>
/// Determines whether the specified <see cref="Vector2D"/> lies on the <see cref="Line2D"/>. /// Determines whether the specified <see cref="Vector2D"/> lies on the <see cref="Line2D"/>.

View File

@@ -26,6 +26,16 @@ public readonly struct Line2DEquation(float slope, float offsetY) : IEquatable<L
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 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> /// <summary>
/// Resolves the Y coordinate for a given X coordinate using the <see cref="Line2DEquation"/>. /// Resolves the Y coordinate for a given X coordinate using the <see cref="Line2DEquation"/>.
/// </summary> /// </summary>

View 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);
}

View File

@@ -79,6 +79,30 @@ public readonly struct Quaternion(float x, float y, float z, float w) : IEquatab
public static implicit operator Quaternion(System.Numerics.Quaternion quaternion) => new(quaternion.X, quaternion.Y, quaternion.Z, quaternion.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); 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> /// <summary>
/// Calculates the length of the <see cref="Quaternion"/>. /// Calculates the length of the <see cref="Quaternion"/>.
/// </summary> /// </summary>
@@ -132,6 +156,15 @@ public readonly struct Quaternion(float x, float y, float z, float w) : IEquatab
/// <returns>The normalized <see cref="Quaternion"/>.</returns> /// <returns>The normalized <see cref="Quaternion"/>.</returns>
public static Quaternion Normalize(Quaternion quaternion) => quaternion / Length(quaternion); public static Quaternion Normalize(Quaternion quaternion) => quaternion / Length(quaternion);
/// <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> /// <summary>
/// Inverts the direction of the <see cref="Quaternion"/>. /// Inverts the direction of the <see cref="Quaternion"/>.
/// </summary> /// </summary>
@@ -154,8 +187,12 @@ public readonly struct Quaternion(float x, float y, float z, float w) : IEquatab
/// <returns>The rotated <see cref="Vector3D"/>.</returns> /// <returns>The rotated <see cref="Vector3D"/>.</returns>
public static Vector3D RotateVector(Vector3D vector, Quaternion quaternion) public static Vector3D RotateVector(Vector3D vector, Quaternion quaternion)
{ {
Quaternion rotation = quaternion * new Quaternion(vector.X, vector.Y, vector.Z, 0) * Invert(quaternion); // https://blog.molecular-matters.com/2013/05/24/a-faster-quaternion-vector-multiplication/
return new(rotation.X, rotation.Y, rotation.Z); // 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> /// <summary>
@@ -210,9 +247,9 @@ public readonly struct Quaternion(float x, float y, float z, float w) : IEquatab
/// <param name="axis">The axis of the rotation in <see cref="Vector3D"/>.</param> /// <param name="axis">The axis of the rotation in <see cref="Vector3D"/>.</param>
/// <param name="angle">The angle in radians.</param> /// <param name="angle">The angle in radians.</param>
/// <returns>The rotation <see cref="Quaternion"/> calculated by the given parameters.</returns> /// <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); float sinHalf = Math.Sin(halfAngle);
return new Quaternion(axis.X * sinHalf, axis.Y * sinHalf, axis.Z * sinHalf, Math.Cos(halfAngle)); return new Quaternion(axis.X * sinHalf, axis.Y * sinHalf, axis.Z * sinHalf, Math.Cos(halfAngle));
} }
@@ -310,6 +347,9 @@ public readonly struct Quaternion(float x, float y, float z, float w) : IEquatab
/// </summary> /// </summary>
public static class QuaternionExtensions public static class QuaternionExtensions
{ {
/// <inheritdoc cref="Quaternion.ToAngles(Quaternion)" />
public static Vector3D ToAngles(this Quaternion quaternion) => Quaternion.ToAngles(quaternion);
/// <inheritdoc cref="Quaternion.Length(Quaternion)" /> /// <inheritdoc cref="Quaternion.Length(Quaternion)" />
public static float Length(this Quaternion quaternion) => Quaternion.Length(quaternion); public static float Length(this Quaternion quaternion) => Quaternion.Length(quaternion);
@@ -331,6 +371,9 @@ public static class QuaternionExtensions
/// <inheritdoc cref="Quaternion.Normalize(Quaternion)" /> /// <inheritdoc cref="Quaternion.Normalize(Quaternion)" />
public static Quaternion Normalize(this Quaternion quaternion) => 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)" /> /// <inheritdoc cref="Quaternion.Invert(Quaternion)" />
public static Quaternion Invert(this Quaternion quaternion) => Quaternion.Invert(quaternion); public static Quaternion Invert(this Quaternion quaternion) => Quaternion.Invert(quaternion);

View 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);
}

View File

@@ -71,9 +71,6 @@ public readonly struct Vector2DInt(int x, int y) : IEquatable<Vector2DInt>
public static Vector2DInt operator -(Vector2DInt vector) => new(0 - vector.X, 0 - vector.Y); 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 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 Vector2DInt operator *(Vector2DInt vector, int value) => new(vector.X * value, vector.Y * value);
public static Vector2DInt operator *(int value, Vector2DInt vector) => new(vector.X * value, vector.Y * value);
public static Vector2DInt operator /(Vector2DInt vector, int value) => new(vector.X / value, vector.Y / value);
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 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;
@@ -125,22 +122,6 @@ public readonly struct Vector2DInt(int x, int y) : IEquatable<Vector2DInt>
/// <returns>The result of subtracting the second <see cref="Vector2DInt"/> from the first.</returns> /// <returns>The result of subtracting the second <see cref="Vector2DInt"/> from the first.</returns>
public static Vector2DInt Subtract(Vector2DInt left, Vector2DInt right) => left - right; public static Vector2DInt Subtract(Vector2DInt left, Vector2DInt right) => left - right;
/// <summary>
/// Multiplies a <see cref="Vector2DInt"/> by a scalar value.
/// </summary>
/// <param name="vector">The <see cref="Vector2DInt"/>.</param>
/// <param name="value">The scalar value.</param>
/// <returns>The result of multiplying the <see cref="Vector2DInt"/> by the scalar value.</returns>
public static Vector2DInt Multiply(Vector2DInt vector, int value) => vector * value;
/// <summary>
/// Divides a <see cref="Vector2DInt"/> by a scalar value.
/// </summary>
/// <param name="vector">The <see cref="Vector2DInt"/>.</param>
/// <param name="value">The scalar value.</param>
/// <returns>The result of dividing the <see cref="Vector2DInt"/> by the scalar value.</returns>
public static Vector2DInt Divide(Vector2DInt vector, int value) => vector / value;
/// <summary> /// <summary>
/// Calculates the absolute value of each component of the vector. /// Calculates the absolute value of each component of the vector.
/// </summary> /// </summary>
@@ -196,15 +177,6 @@ public readonly struct Vector2DInt(int x, int y) : IEquatable<Vector2DInt>
/// <returns>A <see cref="Vector2DInt"/> with each component clamped between the corresponding components of the min and max <see cref="Vector2DInt"/>s.</returns> /// <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)); 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>
/// Performs linear interpolation between two <see cref="Vector2DInt"/>s.
/// </summary>
/// <param name="from">The starting <see cref="Vector2DInt"/> (t = 0).</param>
/// <param name="to">The ending <see cref="Vector2DInt"/> (t = 1).</param>
/// <param name="t">The interpolation parameter.</param>
/// <returns>The interpolated <see cref="Vector2DInt"/>.</returns>
public static Vector2DInt Lerp(Vector2DInt from, Vector2DInt to, int t) => from + FromTo(from, to) * t;
/// <summary> /// <summary>
/// Calculates the cross product of two <see cref="Vector2DInt"/>s. /// Calculates the cross product of two <see cref="Vector2DInt"/>s.
/// </summary> /// </summary>
@@ -265,12 +237,6 @@ public static class Vector2DIntExtensions
/// <inheritdoc cref="Vector2DInt.Subtract(Vector2DInt, Vector2DInt)" /> /// <inheritdoc cref="Vector2DInt.Subtract(Vector2DInt, Vector2DInt)" />
public static Vector2DInt Subtract(this Vector2DInt vector, Vector2DInt vectorToSubtract) => Vector2DInt.Subtract(vector, vectorToSubtract); public static Vector2DInt Subtract(this Vector2DInt vector, Vector2DInt vectorToSubtract) => Vector2DInt.Subtract(vector, vectorToSubtract);
/// <inheritdoc cref="Vector2DInt.Multiply(Vector2DInt, int)" />
public static Vector2DInt Multiply(this Vector2DInt vector, int value) => Vector2DInt.Multiply(vector, value);
/// <inheritdoc cref="Vector2DInt.Divide(Vector2DInt, int)" />
public static Vector2DInt Divide(this Vector2DInt vector, int value) => Vector2DInt.Divide(vector, value);
/// <inheritdoc cref="Vector2DInt.Abs(Vector2DInt)" /> /// <inheritdoc cref="Vector2DInt.Abs(Vector2DInt)" />
public static Vector2DInt Abs(this Vector2DInt vector) => Vector2DInt.Abs(vector); public static Vector2DInt Abs(this Vector2DInt vector) => Vector2DInt.Abs(vector);
@@ -292,9 +258,6 @@ public static class Vector2DIntExtensions
/// <inheritdoc cref="Vector2DInt.Clamp(Vector2DInt, Vector2DInt,Vector2DInt)" /> /// <inheritdoc cref="Vector2DInt.Clamp(Vector2DInt, Vector2DInt,Vector2DInt)" />
public static Vector2DInt Clamp(this Vector2DInt vector, Vector2DInt min, Vector2DInt max) => Vector2DInt.Clamp(vector, min, max); public static Vector2DInt Clamp(this Vector2DInt vector, Vector2DInt min, Vector2DInt max) => Vector2DInt.Clamp(vector, min, max);
/// <inheritdoc cref="Vector2DInt.Lerp(Vector2DInt, Vector2DInt," />
public static Vector2DInt Lerp(this Vector2DInt from, Vector2DInt to, int t) => Vector2DInt.Lerp(from, to, t);
/// <inheritdoc cref="Vector2DInt.Cross(Vector2DInt, Vector2DInt)" /> /// <inheritdoc cref="Vector2DInt.Cross(Vector2DInt, Vector2DInt)" />
public static int Cross(this Vector2DInt left, Vector2DInt right) => Vector2DInt.Cross(left, right); public static int Cross(this Vector2DInt left, Vector2DInt right) => Vector2DInt.Cross(left, right);

View File

@@ -198,13 +198,13 @@ public readonly struct Vector3D(float x, float y, float z) : IEquatable<Vector3D
public static Vector3D Scale(Vector3D vector, Vector3D scale) => new(vector.X * scale.X, vector.Y * scale.Y, vector.Z * scale.Z); public static Vector3D Scale(Vector3D vector, Vector3D scale) => new(vector.X * scale.X, vector.Y * scale.Y, vector.Z * scale.Z);
/// <summary> /// <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> /// </summary>
/// <param name="vector">The <see cref="Vector3D"/> to rotate.</param> /// <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> /// <param name="angleInRadian">The angle to rotate by, in radians.</param>
/// <returns>The rotated <see cref="Vector3D"/>.</returns> /// <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> /// <summary>
/// Returns the component-wise minimum of two <see cref="Vector3D"/>s. /// Returns the component-wise minimum of two <see cref="Vector3D"/>s.
@@ -264,6 +264,15 @@ public readonly struct Vector3D(float x, float y, float z) : IEquatable<Vector3D
/// <returns>The dot product of the two <see cref="Vector3D"/>s.</returns> /// <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; 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> /// <summary>
/// Checks if two <see cref="Vector3D"/>s are approximately equal within a specified epsilon range. /// Checks if two <see cref="Vector3D"/>s are approximately equal within a specified epsilon range.
/// </summary> /// </summary>
@@ -363,6 +372,12 @@ public static class Vector3DExtensions
/// <inheritdoc cref="Vector3D.Dot(Vector3D, Vector3D)" /> /// <inheritdoc cref="Vector3D.Dot(Vector3D, Vector3D)" />
public static float Dot(this Vector3D left, Vector3D right) => Vector3D.Dot(left, right); 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)" /> /// <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); public static bool ApproximatelyEquals(this Vector3D left, Vector3D right, float epsilon = float.Epsilon) => Vector3D.ApproximatelyEquals(left, right, epsilon);
} }

View File

@@ -81,9 +81,6 @@ public readonly struct Vector3DInt(int x, int y, int z) : IEquatable<Vector3DInt
public static Vector3DInt operator -(Vector3DInt vector) => new(0 - vector.X, 0 - vector.Y, 0 - vector.Z); 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 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 Vector3DInt operator *(Vector3DInt vector, int value) => new(vector.X * value, vector.Y * value, vector.Z * value);
public static Vector3DInt operator *(int value, Vector3DInt vector) => new(vector.X * value, vector.Y * value, vector.Z * value);
public static Vector3DInt operator /(Vector3DInt vector, int value) => new(vector.X / value, vector.Y / value, vector.Z / value);
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 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;
@@ -135,22 +132,6 @@ public readonly struct Vector3DInt(int x, int y, int z) : IEquatable<Vector3DInt
/// <returns>The result of subtracting the second <see cref="Vector3DInt"/> from the first.</returns> /// <returns>The result of subtracting the second <see cref="Vector3DInt"/> from the first.</returns>
public static Vector3DInt Subtract(Vector3DInt left, Vector3DInt right) => left - right; public static Vector3DInt Subtract(Vector3DInt left, Vector3DInt right) => left - right;
/// <summary>
/// Multiplies a <see cref="Vector3DInt"/> by a scalar value.
/// </summary>
/// <param name="vector">The <see cref="Vector3DInt"/>.</param>
/// <param name="value">The scalar value.</param>
/// <returns>The result of multiplying the <see cref="Vector3DInt"/> by the scalar value.</returns>
public static Vector3DInt Multiply(Vector3DInt vector, int value) => vector * value;
/// <summary>
/// Divides a <see cref="Vector3DInt"/> by a scalar value.
/// </summary>
/// <param name="vector">The <see cref="Vector3DInt"/>.</param>
/// <param name="value">The scalar value.</param>
/// <returns>The result of dividing the <see cref="Vector3DInt"/> by the scalar value.</returns>
public static Vector3DInt Divide(Vector3DInt vector, int value) => vector / value;
/// <summary> /// <summary>
/// Calculates the absolute value of each component of the vector. /// Calculates the absolute value of each component of the vector.
/// </summary> /// </summary>
@@ -199,15 +180,6 @@ public readonly struct Vector3DInt(int x, int y, int z) : IEquatable<Vector3DInt
/// <returns>A <see cref="Vector3DInt"/> with each component clamped between the corresponding components of the min and max <see cref="Vector3DInt"/>s.</returns> /// <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)); 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>
/// Performs linear interpolation between two <see cref="Vector3DInt"/>s.
/// </summary>
/// <param name="from">The starting <see cref="Vector3DInt"/> (t = 0).</param>
/// <param name="to">The ending <see cref="Vector3DInt"/> (t = 1).</param>
/// <param name="t">The interpolation parameter.</param>
/// <returns>The interpolated <see cref="Vector3DInt"/>.</returns>
public static Vector3DInt Lerp(Vector3DInt from, Vector3DInt to, int t) => from + FromTo(from, to) * t;
/// <summary> /// <summary>
/// Calculates the cross product of two <see cref="Vector3DInt"/>s. /// Calculates the cross product of two <see cref="Vector3DInt"/>s.
/// </summary> /// </summary>
@@ -276,12 +248,6 @@ public static class Vector3DIntExtensions
/// <inheritdoc cref="Vector3DInt.Subtract(Vector3DInt, Vector3DInt)" /> /// <inheritdoc cref="Vector3DInt.Subtract(Vector3DInt, Vector3DInt)" />
public static Vector3DInt Subtract(this Vector3DInt vector, Vector3DInt vectorToSubtract) => Vector3DInt.Subtract(vector, vectorToSubtract); public static Vector3DInt Subtract(this Vector3DInt vector, Vector3DInt vectorToSubtract) => Vector3DInt.Subtract(vector, vectorToSubtract);
/// <inheritdoc cref="Vector3DInt.Multiply(Vector3DInt, int)" />
public static Vector3DInt Multiply(this Vector3DInt vector, int value) => Vector3DInt.Multiply(vector, value);
/// <inheritdoc cref="Vector3DInt.Divide(Vector3DInt, int)" />
public static Vector3DInt Divide(this Vector3DInt vector, int value) => Vector3DInt.Divide(vector, value);
/// <inheritdoc cref="Vector3DInt.Abs(Vector3DInt)" /> /// <inheritdoc cref="Vector3DInt.Abs(Vector3DInt)" />
public static Vector3DInt Abs(this Vector3DInt vector) => Vector3DInt.Abs(vector); public static Vector3DInt Abs(this Vector3DInt vector) => Vector3DInt.Abs(vector);
@@ -300,9 +266,6 @@ public static class Vector3DIntExtensions
/// <inheritdoc cref="Vector3DInt.Clamp(Vector3DInt, Vector3DInt, Vector3DInt)" /> /// <inheritdoc cref="Vector3DInt.Clamp(Vector3DInt, Vector3DInt, Vector3DInt)" />
public static Vector3DInt Clamp(this Vector3DInt vector, Vector3DInt min, Vector3DInt max) => Vector3DInt.Clamp(vector, min, max); public static Vector3DInt Clamp(this Vector3DInt vector, Vector3DInt min, Vector3DInt max) => Vector3DInt.Clamp(vector, min, max);
/// <inheritdoc cref="Vector3DInt.Lerp(Vector3DInt, Vector3DInt, int)" />
public static Vector3DInt Lerp(this Vector3DInt from, Vector3DInt to, int t) => Vector3DInt.Lerp(from, to, t);
/// <inheritdoc cref="Vector3DInt.Cross(Vector3DInt, Vector3DInt)" /> /// <inheritdoc cref="Vector3DInt.Cross(Vector3DInt, Vector3DInt)" />
public static Vector3DInt Cross(this Vector3DInt left, Vector3DInt right) => Vector3DInt.Cross(left, right); public static Vector3DInt Cross(this Vector3DInt left, Vector3DInt right) => Vector3DInt.Cross(left, right);

View 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);
}

View File

@@ -8,6 +8,7 @@ public class Transform2D : Behaviour, ITransform2D
public Event<ITransform2D, ITransform2D.PositionChangedArguments> OnPositionChanged { get; } = new(); public Event<ITransform2D, ITransform2D.PositionChangedArguments> OnPositionChanged { get; } = new();
public Event<ITransform2D, ITransform2D.ScaleChangedArguments> OnScaleChanged { get; } = new(); public Event<ITransform2D, ITransform2D.ScaleChangedArguments> OnScaleChanged { get; } = new();
public Event<ITransform2D, ITransform2D.RotationChangedArguments> OnRotationChanged { get; } = new(); public Event<ITransform2D, ITransform2D.RotationChangedArguments> OnRotationChanged { get; } = new();
public Event<ITransform2D> OnTransformUpdated { get; } = new();
private Vector2D _position = Vector2D.Zero; private Vector2D _position = Vector2D.Zero;
private Vector2D _scale = Vector2D.One; private Vector2D _scale = Vector2D.One;
@@ -36,7 +37,8 @@ public class Transform2D : Behaviour, ITransform2D
_position = value; _position = value;
UpdateLocalPosition(); 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; _scale = value;
UpdateLocalScale(); 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; _rotation = value;
UpdateLocalRotation(); 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; _localPosition = value;
UpdatePosition(); UpdatePosition();
OnPositionChanged?.Invoke(this, new(previousPosition)); OnPositionChanged.Invoke(this, new(previousPosition));
OnTransformUpdated.Invoke(this);
} }
} }
@@ -102,8 +107,9 @@ public class Transform2D : Behaviour, ITransform2D
UpdateScale(); UpdateScale();
UpdatePosition(); UpdatePosition();
OnScaleChanged?.Invoke(this, new(previousScale)); OnScaleChanged.Invoke(this, new(previousScale));
OnPositionChanged?.Invoke(this, new(previousPosition)); OnPositionChanged.Invoke(this, new(previousPosition));
OnTransformUpdated.Invoke(this);
} }
} }
@@ -119,7 +125,8 @@ public class Transform2D : Behaviour, ITransform2D
_localRotation = value; _localRotation = value;
UpdateRotation(); UpdateRotation();
OnRotationChanged?.Invoke(this, new(previousRotation)); OnRotationChanged.Invoke(this, new(previousRotation));
OnTransformUpdated.Invoke(this);
} }
} }
@@ -130,7 +137,8 @@ public class Transform2D : Behaviour, ITransform2D
UpdatePosition(); UpdatePosition();
OnPositionChanged?.Invoke(this, args); OnPositionChanged.Invoke(this, args);
OnTransformUpdated.Invoke(this);
} }
private void RecalculateScale(ITransform2D _, ITransform2D.ScaleChangedArguments args) private void RecalculateScale(ITransform2D _, ITransform2D.ScaleChangedArguments args)
@@ -143,8 +151,9 @@ public class Transform2D : Behaviour, ITransform2D
UpdateScale(); UpdateScale();
UpdatePosition(); UpdatePosition();
OnScaleChanged?.Invoke(this, args); OnScaleChanged.Invoke(this, args);
OnPositionChanged?.Invoke(this, new(previousPosition)); OnPositionChanged.Invoke(this, new(previousPosition));
OnTransformUpdated.Invoke(this);
} }
private void RecalculateRotation(ITransform2D _, ITransform2D.RotationChangedArguments args) private void RecalculateRotation(ITransform2D _, ITransform2D.RotationChangedArguments args)
@@ -157,8 +166,9 @@ public class Transform2D : Behaviour, ITransform2D
UpdateRotation(); UpdateRotation();
UpdatePosition(); UpdatePosition();
OnRotationChanged?.Invoke(this, args); OnRotationChanged.Invoke(this, args);
OnPositionChanged?.Invoke(this, new(previousPosition)); OnPositionChanged.Invoke(this, new(previousPosition));
OnTransformUpdated.Invoke(this);
} }
private void UpdateLocalPosition() private void UpdateLocalPosition()
@@ -252,9 +262,10 @@ public class Transform2D : Behaviour, ITransform2D
UpdateLocalScale(); UpdateLocalScale();
UpdateLocalRotation(); UpdateLocalRotation();
OnPositionChanged?.Invoke(this, new(Position)); OnPositionChanged.Invoke(this, new(Position));
OnScaleChanged?.Invoke(this, new(Scale)); OnScaleChanged.Invoke(this, new(Scale));
OnRotationChanged?.Invoke(this, new(Rotation)); OnRotationChanged.Invoke(this, new(Rotation));
OnTransformUpdated.Invoke(this);
} }
private void LookForTransform2D(IBehaviourController sender, IBehaviourController.BehaviourAddedArguments args) private void LookForTransform2D(IBehaviourController sender, IBehaviourController.BehaviourAddedArguments args)

285
Engine.Core/Transform3D.cs Normal file
View 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);
}
}

View File

@@ -143,19 +143,27 @@ public abstract class LiteNetLibCommunicatorBase : Behaviour, INetworkCommunicat
private void SetupEnginePackets() private void SetupEnginePackets()
{ {
// I know, ugly af. I need to find a better way // 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(CircleNetPacker.Write, CircleNetPacker.Read);
netPacketProcessor.RegisterNestedType(ColorHSVNetPacker.Write, ColorHSVNetPacker.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(ColorRGBNetPacker.Write, ColorRGBNetPacker.Read);
netPacketProcessor.RegisterNestedType(Line2DEquationNetPacker.Write, Line2DEquationNetPacker.Read); netPacketProcessor.RegisterNestedType(ColorRGBANetPacker.Write, ColorRGBANetPacker.Read);
netPacketProcessor.RegisterNestedType(Line2DNetPacker.Write, Line2DNetPacker.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(Projection1DNetPacker.Write, Projection1DNetPacker.Read);
netPacketProcessor.RegisterNestedType(QuaternionNetPacker.Write, QuaternionNetPacker.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(Shape2DNetPacker.Write, Shape2DNetPacker.Read);
netPacketProcessor.RegisterNestedType(TriangleNetPacker.Write, TriangleNetPacker.Read); netPacketProcessor.RegisterNestedType(TriangleNetPacker.Write, TriangleNetPacker.Read);
netPacketProcessor.RegisterNestedType(Vector2DNetPacker.Write, Vector2DNetPacker.Read); netPacketProcessor.RegisterNestedType(Vector2DNetPacker.Write, Vector2DNetPacker.Read);
netPacketProcessor.RegisterNestedType(Vector2DIntNetPacker.Write, Vector2DIntNetPacker.Read);
netPacketProcessor.RegisterNestedType(Vector3DNetPacker.Write, Vector3DNetPacker.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) public INetworkCommunicator SubscribeToPackets<T>(Event<IConnection, T>.EventHandler callback)

View File

@@ -4,19 +4,19 @@ using Engine.Core;
namespace Engine.Systems.Network; namespace Engine.Systems.Network;
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.LowerBoundary);
Vector2DNetPacker.Write(writer, data.UpperBoundary); Vector2DNetPacker.Write(writer, data.UpperBoundary);
} }
internal static AABB Read(NetDataReader reader) internal static AABB2D Read(NetDataReader reader)
{ {
Vector2D lowerBoundary = Vector2DNetPacker.Read(reader); Vector2D lowerBoundary = Vector2DNetPacker.Read(reader);
Vector2D upperBoundary = Vector2DNetPacker.Read(reader); Vector2D upperBoundary = Vector2DNetPacker.Read(reader);
return new AABB(lowerBoundary, upperBoundary); return new AABB2D(lowerBoundary, upperBoundary);
} }
} }

View File

@@ -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);
}
}

View File

@@ -0,0 +1,26 @@
using LiteNetLib.Utils;
using Engine.Core;
namespace Engine.Systems.Network;
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);
}
}

View File

@@ -0,0 +1,22 @@
using LiteNetLib.Utils;
using Engine.Core;
namespace Engine.Systems.Network;
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);
}
}

View File

@@ -0,0 +1,22 @@
using LiteNetLib.Utils;
using Engine.Core;
namespace Engine.Systems.Network;
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);
}
}

View File

@@ -0,0 +1,22 @@
using LiteNetLib.Utils;
using Engine.Core;
namespace Engine.Systems.Network;
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);
}
}

View File

@@ -0,0 +1,22 @@
using LiteNetLib.Utils;
using Engine.Core;
namespace Engine.Systems.Network;
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);
}
}

View File

@@ -0,0 +1,24 @@
using LiteNetLib.Utils;
using Engine.Core;
namespace Engine.Systems.Network;
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);
}
}

View File

@@ -0,0 +1,26 @@
using LiteNetLib.Utils;
using Engine.Core;
namespace Engine.Systems.Network;
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);
}
}

View File

@@ -10,13 +10,13 @@ namespace Engine.Integration.MonoGame;
public interface ISpriteBatch 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 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, AABB2D? 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, Vector2D position, AABB2D? 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, AABB2D destinationAABB, AABB2D? 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, Vector2D position, AABB2D? sourceAABB, Color color);
void Draw(Texture2D texture, AABB destinationAABB, AABB? 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, 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);
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, 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); void DrawString(SpriteFont spriteFont, string text, Vector2D position, Color color, float rotation, Vector2D origin, Vector2D scale, SpriteEffects effects, float layerDepth);

View File

@@ -7,9 +7,9 @@ namespace Engine.Integration.MonoGame;
public class MonoGameCamera2D : BehaviourBase, ICamera2D, IFirstFrameUpdate, IPreDraw public class MonoGameCamera2D : BehaviourBase, ICamera2D, IFirstFrameUpdate, IPreDraw
{ {
public event MatrixTransformChangedArguments? OnMatrixTransformChanged = null; public Event<MonoGameCamera2D> OnMatrixTransformChanged { get; } = new();
public event ViewportChangedArguments? OnViewportChanged = null; public Event<MonoGameCamera2D> OnViewportChanged { get; } = new();
public event ZoomChangedArguments? OnZoomChanged = null; public Event<MonoGameCamera2D> OnZoomChanged { get; } = new();
private Matrix _matrixTransform = Matrix.Identity; private Matrix _matrixTransform = Matrix.Identity;
@@ -28,7 +28,7 @@ public class MonoGameCamera2D : BehaviourBase, ICamera2D, IFirstFrameUpdate, IPr
return; return;
_matrixTransform = value; _matrixTransform = value;
OnMatrixTransformChanged?.Invoke(this); OnMatrixTransformChanged.Invoke(this);
} }
} }
@@ -47,7 +47,7 @@ public class MonoGameCamera2D : BehaviourBase, ICamera2D, IFirstFrameUpdate, IPr
return; return;
_viewport = value; _viewport = value;
OnViewportChanged?.Invoke(this); OnViewportChanged.Invoke(this);
} }
} }
@@ -62,7 +62,7 @@ public class MonoGameCamera2D : BehaviourBase, ICamera2D, IFirstFrameUpdate, IPr
return; return;
_zoom = newValue; _zoom = newValue;
OnZoomChanged?.Invoke(this); OnZoomChanged.Invoke(this);
} }
} }
@@ -102,8 +102,4 @@ public class MonoGameCamera2D : BehaviourBase, ICamera2D, IFirstFrameUpdate, IPr
protected sealed override void InitializeInternal() => Transform = BehaviourController.GetRequiredBehaviour<ITransform2D>(); protected sealed override void InitializeInternal() => Transform = BehaviourController.GetRequiredBehaviour<ITransform2D>();
protected sealed override void FinalizeInternal() => Transform = null!; 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);
} }

View File

@@ -0,0 +1,131 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Engine.Core;
namespace Engine.Integration.MonoGame;
public class MonoGameCamera3D : BehaviourBase, ICamera3D, IFirstFrameUpdate, IPreDraw
{
public Event<MonoGameCamera3D, ViewChangedArguments> OnViewChanged { get; } = new();
public Event<MonoGameCamera3D, ProjectionChangedArguments> OnProjectionChanged { get; } = new();
public Event<MonoGameCamera3D, ViewportChangedArguments> OnViewportChanged { get; } = new();
public Event<MonoGameCamera3D, FieldOfViewChangedArguments> OnFieldOfViewChanged { get; } = new();
private Matrix _view = Matrix.Identity;
private Matrix _projection = Matrix.Identity;
private Viewport _viewport = default;
private float _fieldOfView = 1f;
public GraphicsDeviceManager Graphics { get; private set; } = null!;
public ITransform3D Transform { get; private set; } = null!;
public Matrix View
{
get => _view;
set
{
if (_view == value)
return;
Matrix previousView = _view;
_view = value;
OnViewChanged.Invoke(this, new(previousView));
}
}
public Matrix Projection
{
get => _projection;
set
{
if (_projection == value)
return;
Matrix previousProjection = _projection;
_projection = value;
OnProjectionChanged.Invoke(this, new(previousProjection));
}
}
public Vector3D Position
{
get => Transform.Position;
set => Transform.Position = value;
}
public Viewport Viewport
{
get => _viewport;
set
{
if (_viewport.Equals(value))
return;
Viewport previousViewport = _viewport;
_viewport = value;
OnViewportChanged.Invoke(this, new(previousViewport));
}
}
public float FieldOfView
{
get => _fieldOfView;
set
{
float newValue = Math.Max(0.1f, value);
if (_fieldOfView == newValue)
return;
float previousFieldOfView = _fieldOfView;
_fieldOfView = newValue;
OnFieldOfViewChanged.Invoke(this, new(previousFieldOfView));
}
}
public Engine.Core.Quaternion Rotation
{
get => Transform.Rotation;
set => Transform.Rotation = value;
}
// TODO This causes delay since OnPreDraw calls assuming this is called in in Update
public Ray3D ScreenToWorldRay(Vector2D screenPosition)
{
Vector3 nearPoint = new(screenPosition.X, screenPosition.Y, 0f);
Vector3 farPoint = new(screenPosition.X, screenPosition.Y, 1f);
Vector3 worldNear = Viewport.Unproject(nearPoint, _projection, _view, Matrix.Identity);
Vector3 worldFar = Viewport.Unproject(farPoint, _projection, _view, Matrix.Identity);
Vector3 direction = Vector3.Normalize(worldFar - worldNear);
return new(worldNear.ToVector3D(), direction.ToVector3D());
}
public Vector2D WorldToScreenPosition(Vector3D worldPosition) => Viewport.Project(worldPosition.ToVector3(), _projection, _view, Matrix.Identity).ToVector3D();
public void FirstActiveFrame()
{
Graphics = BehaviourController.UniverseObject.Universe.FindRequiredBehaviour<MonoGameWindowContainer>().Window.Graphics;
Viewport = Graphics.GraphicsDevice.Viewport;
}
public void PreDraw()
{
Vector3 cameraPosition = Position.ToVector3();
View = Matrix.CreateLookAt(
cameraPosition,
Transform.Forward.ToVector3() + cameraPosition,
Transform.Up.ToVector3()
);
Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, Viewport.AspectRatio, 0.1f, 100f);
}
protected sealed override void InitializeInternal() => Transform = BehaviourController.GetRequiredBehaviour<ITransform3D>();
protected sealed override void FinalizeInternal() => Transform = null!;
public readonly record struct ViewChangedArguments(Matrix PreviousView);
public readonly record struct ProjectionChangedArguments(Matrix PreviousProjection);
public readonly record struct ViewportChangedArguments(Viewport PreviousViewport);
public readonly record struct FieldOfViewChangedArguments(float PreviousFieldOfView);
}

View File

@@ -14,25 +14,25 @@ public class SpriteBatchWrapper(GraphicsDevice graphicsDevice) : ISpriteBatch
public void Begin(SpriteSortMode sortMode = SpriteSortMode.Deferred, BlendState? blendState = null, SamplerState? samplerState = null, DepthStencilState? depthStencilState = null, RasterizerState? rasterizerState = null, Effect? effect = null, Matrix? transformMatrix = null) public void Begin(SpriteSortMode sortMode = SpriteSortMode.Deferred, BlendState? blendState = null, SamplerState? samplerState = null, DepthStencilState? depthStencilState = null, RasterizerState? rasterizerState = null, Effect? effect = null, Matrix? transformMatrix = null)
=> spriteBatch.Begin(sortMode, blendState, samplerState, depthStencilState, rasterizerState, effect, transformMatrix); => spriteBatch.Begin(sortMode, blendState, samplerState, depthStencilState, rasterizerState, effect, transformMatrix);
public void Draw(Texture2D texture, Vector2D position, AABB? sourceAABB, Color color, float rotation, Vector2D origin, Vector2D scale, SpriteEffects effects, float layerDepth) public void Draw(Texture2D texture, Vector2D position, AABB2D? sourceAABB, Color color, float rotation, Vector2D origin, Vector2D scale, SpriteEffects effects, float layerDepth)
=> spriteBatch.Draw(texture, position.ToDisplayVector2(), sourceAABB?.ToRectangle(), color, rotation, origin.ToDisplayVector2(), scale.ToDisplayVector2(), effects, layerDepth); => spriteBatch.Draw(texture, position.ToDisplayVector2(), sourceAABB?.ToRectangle(), color, rotation, origin.ToDisplayVector2(), scale.ToDisplayVector2(), effects, layerDepth);
public void Draw(Texture2D texture, Vector2D position, AABB? sourceAABB, Color color, float rotation, Vector2D origin, float scale, SpriteEffects effects, float layerDepth) public void Draw(Texture2D texture, Vector2D position, AABB2D? sourceAABB, Color color, float rotation, Vector2D origin, float scale, SpriteEffects effects, float layerDepth)
=> spriteBatch.Draw(texture, position.ToDisplayVector2(), sourceAABB?.ToRectangle(), color, rotation, origin.ToDisplayVector2(), scale, effects, layerDepth); => spriteBatch.Draw(texture, position.ToDisplayVector2(), sourceAABB?.ToRectangle(), color, rotation, origin.ToDisplayVector2(), scale, effects, layerDepth);
public void Draw(Texture2D texture, AABB destinationAABB, AABB? sourceAABB, Color color, float rotation, Vector2D origin, SpriteEffects effects, float layerDepth) public void Draw(Texture2D texture, AABB2D destinationAABB, AABB2D? sourceAABB, Color color, float rotation, Vector2D origin, SpriteEffects effects, float layerDepth)
=> spriteBatch.Draw(texture, destinationAABB.ToRectangle(), sourceAABB?.ToRectangle(), color, rotation, origin.ToDisplayVector2(), effects, layerDepth); => spriteBatch.Draw(texture, destinationAABB.ToRectangle(), sourceAABB?.ToRectangle(), color, rotation, origin.ToDisplayVector2(), effects, layerDepth);
public void Draw(Texture2D texture, Vector2D position, AABB? sourceAABB, Color color) public void Draw(Texture2D texture, Vector2D position, AABB2D? sourceAABB, Color color)
=> spriteBatch.Draw(texture, position.ToDisplayVector2(), sourceAABB?.ToRectangle(), color); => spriteBatch.Draw(texture, position.ToDisplayVector2(), sourceAABB?.ToRectangle(), color);
public void Draw(Texture2D texture, AABB destinationAABB, AABB? sourceAABB, Color color) public void Draw(Texture2D texture, AABB2D destinationAABB, AABB2D? sourceAABB, Color color)
=> spriteBatch.Draw(texture, destinationAABB.ToRectangle(), sourceAABB?.ToRectangle(), color); => spriteBatch.Draw(texture, destinationAABB.ToRectangle(), sourceAABB?.ToRectangle(), color);
public void Draw(Texture2D texture, Vector2D position, Color color) public void Draw(Texture2D texture, Vector2D position, Color color)
=> spriteBatch.Draw(texture, position.ToDisplayVector2(), color); => spriteBatch.Draw(texture, position.ToDisplayVector2(), color);
public void Draw(Texture2D texture, AABB destinationAABB, Color color) public void Draw(Texture2D texture, AABB2D destinationAABB, Color color)
=> spriteBatch.Draw(texture, destinationAABB.ToRectangle(), color); => spriteBatch.Draw(texture, destinationAABB.ToRectangle(), color);
public void DrawString(SpriteFont spriteFont, string text, Vector2D position, Color color) public void DrawString(SpriteFont spriteFont, string text, Vector2D position, Color color)

View File

@@ -35,6 +35,15 @@ public static class EngineConverterExtensions
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2 ToVector2(this Vector2D vector) => new(vector.X, vector.Y); public static Vector2 ToVector2(this Vector2D vector) => new(vector.X, vector.Y);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3D ToVector3D(this Vector3 vector) => new(vector.X, vector.Y, vector.Z);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3 ToVector3(this Vector3D vector) => new(vector.X, vector.Y, vector.Z);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Microsoft.Xna.Framework.Quaternion ToXnaQuaternion(this Core.Quaternion quaternion) => new(quaternion.X, quaternion.Y, quaternion.Z, quaternion.W);
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2 ToDisplayVector2(this Vector2D vector) => vector.Scale(screenScale).ToVector2(); public static Vector2 ToDisplayVector2(this Vector2D vector) => vector.Scale(screenScale).ToVector2();
@@ -51,7 +60,7 @@ public static class EngineConverterExtensions
}; };
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Rectangle ToRectangle(this AABB aabb) => new() public static Rectangle ToRectangle(this AABB2D aabb) => new()
{ {
X = (int)(aabb.LowerBoundary.X * screenScale.X), X = (int)(aabb.LowerBoundary.X * screenScale.X),
Y = (int)(aabb.LowerBoundary.Y * screenScale.Y), Y = (int)(aabb.LowerBoundary.Y * screenScale.Y),

View File

@@ -8,33 +8,33 @@ using YamlDotNet.Serialization;
namespace Engine.Serializers.Yaml; namespace Engine.Serializers.Yaml;
public class AABBConverter : EngineTypeYamlSerializerBase<AABB> public class AABB2DConverter : EngineTypeYamlSerializerBase<AABB2D>
{ {
public override AABB Read(IParser parser, Type type, ObjectDeserializer rootDeserializer) public override AABB2D Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{ {
parser.Consume<MappingStart>(); parser.Consume<MappingStart>();
if (parser.Consume<Scalar>().Value.CompareTo(nameof(AABB.LowerBoundary)) != 0) if (parser.Consume<Scalar>().Value.CompareTo(nameof(AABB2D.LowerBoundary)) != 0)
throw new ArgumentException($"{nameof(AABB)} mapping must start with {nameof(AABB.LowerBoundary)}"); throw new ArgumentException($"{nameof(AABB2D)} mapping must start with {nameof(AABB2D.LowerBoundary)}");
Vector2D lowerBoundary = (Vector2D)rootDeserializer(typeof(Vector2D))!; Vector2D lowerBoundary = (Vector2D)rootDeserializer(typeof(Vector2D))!;
if (parser.Consume<Scalar>().Value.CompareTo(nameof(AABB.UpperBoundary)) != 0) if (parser.Consume<Scalar>().Value.CompareTo(nameof(AABB2D.UpperBoundary)) != 0)
throw new ArgumentException($"{nameof(AABB)} mapping must end with {nameof(AABB.UpperBoundary)}"); throw new ArgumentException($"{nameof(AABB2D)} mapping must end with {nameof(AABB2D.UpperBoundary)}");
Vector2D upperBoundary = (Vector2D)rootDeserializer(typeof(Vector2D))!; Vector2D upperBoundary = (Vector2D)rootDeserializer(typeof(Vector2D))!;
parser.Consume<MappingEnd>(); parser.Consume<MappingEnd>();
return new AABB(lowerBoundary, upperBoundary); return new AABB2D(lowerBoundary, upperBoundary);
} }
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{ {
AABB aabb = (AABB)value!; AABB2D aabb = (AABB2D)value!;
emitter.Emit(new MappingStart()); emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(AABB.LowerBoundary))); emitter.Emit(new Scalar(nameof(AABB2D.LowerBoundary)));
serializer(aabb.LowerBoundary, typeof(Vector2D)); serializer(aabb.LowerBoundary, typeof(Vector2D));
emitter.Emit(new Scalar(nameof(AABB.UpperBoundary))); emitter.Emit(new Scalar(nameof(AABB2D.UpperBoundary)));
serializer(aabb.UpperBoundary, typeof(Vector2D)); serializer(aabb.UpperBoundary, typeof(Vector2D));
emitter.Emit(new MappingEnd()); emitter.Emit(new MappingEnd());
} }

View File

@@ -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());
}
}

View File

@@ -0,0 +1,28 @@
using System;
using Engine.Core;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Engine.Serializers.Yaml;
public class ColorHSVAConverter : EngineTypeYamlSerializerBase<ColorHSVA>
{
private static readonly int SUBSTRING_START_LENGTH = nameof(ColorHSVA).Length + 1;
public override ColorHSVA Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
string value = parser.Consume<Scalar>().Value;
string insideParenthesis = value[SUBSTRING_START_LENGTH..^1];
string[] values = insideParenthesis.Split(", ");
return new ColorHSVA(float.Parse(values[0]), float.Parse(values[1]), float.Parse(values[2]), float.Parse(values[3]));
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
ColorHSVA hsva = (ColorHSVA)value!;
emitter.Emit(new Scalar($"{nameof(ColorHSVA)}({hsva.Hue}, {hsva.Saturation}, {hsva.Value}, {hsva.Alpha})"));
}
}

View File

@@ -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 Line3DConverter : EngineTypeYamlSerializerBase<Line3D>
{
public override Line3D Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
parser.Consume<MappingStart>();
if (parser.Consume<Scalar>().Value.CompareTo(nameof(Line3D.From)) != 0)
throw new ArgumentException($"{nameof(Line3D)} mapping must start with {nameof(Line3D.From)}");
Vector3D from = (Vector3D)rootDeserializer(typeof(Vector3D))!;
if (parser.Consume<Scalar>().Value.CompareTo(nameof(Line3D.To)) != 0)
throw new ArgumentException($"{nameof(Line3D)} mapping must end with {nameof(Line3D.To)}");
Vector3D to = (Vector3D)rootDeserializer(typeof(Vector3D))!;
parser.Consume<MappingEnd>();
return new Line3D(from, to);
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
Line3D line3D = (Line3D)value!;
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(Line3D.From)));
serializer(line3D.From, typeof(Vector3D));
emitter.Emit(new Scalar(nameof(Line3D.To)));
serializer(line3D.To, typeof(Vector3D));
emitter.Emit(new MappingEnd());
}
}

View File

@@ -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 Ray2DConverter : EngineTypeYamlSerializerBase<Ray2D>
{
public override Ray2D Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
parser.Consume<MappingStart>();
if (parser.Consume<Scalar>().Value.CompareTo(nameof(Ray2D.Origin)) != 0)
throw new ArgumentException($"{nameof(Ray2D)} mapping must start with {nameof(Ray2D.Origin)}");
Vector2D origin = (Vector2D)rootDeserializer(typeof(Vector2D))!;
if (parser.Consume<Scalar>().Value.CompareTo(nameof(Ray2D.Direction)) != 0)
throw new ArgumentException($"{nameof(Ray2D)} mapping must end with {nameof(Ray2D.Direction)}");
Vector2D direction = (Vector2D)rootDeserializer(typeof(Vector2D))!;
parser.Consume<MappingEnd>();
return new Ray2D(origin, direction);
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
Ray2D ray2D = (Ray2D)value!;
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(ray2D.Origin)));
serializer(ray2D.Origin, typeof(Vector2D));
emitter.Emit(new Scalar(nameof(ray2D.Direction)));
serializer(ray2D.Direction, typeof(Vector2D));
emitter.Emit(new MappingEnd());
}
}

View File

@@ -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 Ray3DConverter : EngineTypeYamlSerializerBase<Ray3D>
{
public override Ray3D Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
parser.Consume<MappingStart>();
if (parser.Consume<Scalar>().Value.CompareTo(nameof(Ray3D.Origin)) != 0)
throw new ArgumentException($"{nameof(Ray3D)} mapping must start with {nameof(Ray3D.Origin)}");
Vector3D origin = (Vector3D)rootDeserializer(typeof(Vector3D))!;
if (parser.Consume<Scalar>().Value.CompareTo(nameof(Ray3D.Direction)) != 0)
throw new ArgumentException($"{nameof(Ray3D)} mapping must end with {nameof(Ray3D.Direction)}");
Vector3D direction = (Vector3D)rootDeserializer(typeof(Vector3D))!;
parser.Consume<MappingEnd>();
return new Ray3D(origin, direction);
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
Ray3D ray3D = (Ray3D)value!;
emitter.Emit(new MappingStart());
emitter.Emit(new Scalar(nameof(ray3D.Origin)));
serializer(ray3D.Origin, typeof(Vector3D));
emitter.Emit(new Scalar(nameof(ray3D.Direction)));
serializer(ray3D.Direction, typeof(Vector3D));
emitter.Emit(new MappingEnd());
}
}

View File

@@ -0,0 +1,28 @@
using System;
using Engine.Core;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Engine.Serializers.Yaml;
public class Vector2DIntConverter : EngineTypeYamlSerializerBase<Vector2DInt>
{
private static readonly int SUBSTRING_START_LENGTH = nameof(Vector2DInt).Length + 1;
public override Vector2DInt Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
string value = parser.Consume<Scalar>().Value;
string insideParenthesis = value[SUBSTRING_START_LENGTH..^1];
string[] values = insideParenthesis.Split(", ");
return new Vector2DInt(int.Parse(values[0]), int.Parse(values[1]));
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
Vector2DInt vector2DInt = (Vector2DInt)value!;
emitter.Emit(new Scalar($"{nameof(Vector2DInt)}({vector2DInt.X}, {vector2DInt.Y})"));
}
}

View File

@@ -0,0 +1,28 @@
using System;
using Engine.Core;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Engine.Serializers.Yaml;
public class Vector3DIntConverter : EngineTypeYamlSerializerBase<Vector3DInt>
{
private static readonly int SUBSTRING_START_LENGTH = nameof(Vector3DInt).Length + 1;
public override Vector3DInt Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
string value = parser.Consume<Scalar>().Value;
string insideParenthesis = value[SUBSTRING_START_LENGTH..^1];
string[] values = insideParenthesis.Split(", ");
return new Vector3DInt(int.Parse(values[0]), int.Parse(values[1]), int.Parse(values[2]));
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
Vector3DInt vector3DInt = (Vector3DInt)value!;
emitter.Emit(new Scalar($"{nameof(Vector3DInt)}({vector3DInt.X}, {vector3DInt.Y}, {vector3DInt.Z})"));
}
}

View File

@@ -0,0 +1,28 @@
using System;
using Engine.Core;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Engine.Serializers.Yaml;
public class Vector4DConverter : EngineTypeYamlSerializerBase<Vector4D>
{
private static readonly int SUBSTRING_START_LENGTH = nameof(Vector4D).Length + 1;
public override Vector4D Read(IParser parser, Type type, ObjectDeserializer rootDeserializer)
{
string value = parser.Consume<Scalar>().Value;
string insideParenthesis = value[SUBSTRING_START_LENGTH..^1];
string[] values = insideParenthesis.Split(", ");
return new Vector4D(float.Parse(values[0]), float.Parse(values[1]), float.Parse(values[2]), float.Parse(values[3]));
}
public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer)
{
Vector4D vector4D = (Vector4D)value!;
emitter.Emit(new Scalar($"{nameof(Vector4D)}({vector4D.X}, {vector4D.Y}, {vector4D.Z}, {vector4D.W})"));
}
}

View File

@@ -68,11 +68,11 @@ public static class Physics2D
return isOverlapping; return isOverlapping;
} }
public static bool Overlaps(this AABB aabb, Vector2D point) public static bool Overlaps(this AABB2D aabb, Vector2D point)
=> point.X >= aabb.LowerBoundary.X && point.X <= aabb.UpperBoundary.X && => point.X >= aabb.LowerBoundary.X && point.X <= aabb.UpperBoundary.X &&
point.Y >= aabb.LowerBoundary.Y && point.Y <= aabb.UpperBoundary.Y; point.Y >= aabb.LowerBoundary.Y && point.Y <= aabb.UpperBoundary.Y;
public static bool Overlaps(this AABB left, AABB right) public static bool Overlaps(this AABB2D left, AABB2D right)
=> left.LowerBoundary.X <= right.UpperBoundary.X && left.UpperBoundary.X >= right.LowerBoundary.X && => left.LowerBoundary.X <= right.UpperBoundary.X && left.UpperBoundary.X >= right.LowerBoundary.X &&
left.LowerBoundary.Y <= right.UpperBoundary.Y && left.UpperBoundary.Y >= right.LowerBoundary.Y; left.LowerBoundary.Y <= right.UpperBoundary.Y && left.UpperBoundary.Y >= right.LowerBoundary.Y;

View File

@@ -0,0 +1,24 @@
using Engine.Core;
namespace Engine.Systems.Tween;
public static class TweenAABB2DExtensions
{
private static readonly BoxedPool<AABB2D> boxedAABBPool = new(2);
public static ITween TweenAABB(this AABB2D initialAABB, ITweenManager tweenManager, float duration, AABB2D targetAABB, System.Action<AABB2D> setMethod)
{
Boxed<AABB2D> boxedInitial = boxedAABBPool.Get(initialAABB);
Boxed<AABB2D> boxedTarget = boxedAABBPool.Get(targetAABB);
ITween tween = tweenManager.StartTween(duration, t => setMethod?.Invoke(new AABB2D(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;
}
}

View File

@@ -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;
}
}

View File

@@ -1,24 +0,0 @@
using Engine.Core;
namespace Engine.Systems.Tween;
public static class TweenAABBExtensions
{
private static readonly BoxedPool<AABB> boxedAABBPool = new(2);
public static ITween TweenAABB(this AABB initialAABB, ITweenManager tweenManager, float duration, AABB targetAABB, System.Action<AABB> setMethod)
{
Boxed<AABB> boxedInitial = boxedAABBPool.Get(initialAABB);
Boxed<AABB> boxedTarget = boxedAABBPool.Get(targetAABB);
ITween tween = tweenManager.StartTween(duration, t => setMethod?.Invoke(new AABB(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;
}
}

View File

@@ -0,0 +1,31 @@
using Engine.Core;
namespace Engine.Systems.Tween;
public static class TweenLine3DExtensions
{
private static readonly BoxedPool<Line3D> boxedLine3DPool = new(2);
public static ITween TweenLine3D(this Line3D initialLine3D, ITweenManager tweenManager, float duration, Line3D targetLine3D, System.Action<Line3D> setMethod)
{
Boxed<Line3D> boxedInitial = boxedLine3DPool.Get(initialLine3D);
Boxed<Line3D> boxedTarget = boxedLine3DPool.Get(targetLine3D);
ITween tween = tweenManager.StartTween(duration,
t => setMethod?.Invoke(
new Line3D(
boxedInitial.Value.From.Lerp(boxedTarget.Value.From, t),
boxedInitial.Value.To.Lerp(boxedTarget.Value.To, t)
)
)
);
tween.OnComplete(() =>
{
boxedLine3DPool.Return(boxedInitial);
boxedLine3DPool.Return(boxedTarget);
});
return tween;
}
}

View File

@@ -0,0 +1,31 @@
using Engine.Core;
namespace Engine.Systems.Tween;
public static class TweenRay2DExtensions
{
private static readonly BoxedPool<Ray2D> boxedRay2DPool = new(2);
public static ITween TweenRay2D(this Ray2D initialRay2D, ITweenManager tweenManager, float duration, Ray2D targetRay2D, System.Action<Ray2D> setMethod)
{
Boxed<Ray2D> boxedInitial = boxedRay2DPool.Get(initialRay2D);
Boxed<Ray2D> boxedTarget = boxedRay2DPool.Get(targetRay2D);
ITween tween = tweenManager.StartTween(duration,
t => setMethod?.Invoke(
new Ray2D(
boxedInitial.Value.Origin.Lerp(boxedTarget.Value.Origin, t),
boxedInitial.Value.Direction.Lerp(boxedTarget.Value.Direction, t).Normalized
)
)
);
tween.OnComplete(() =>
{
boxedRay2DPool.Return(boxedInitial);
boxedRay2DPool.Return(boxedTarget);
});
return tween;
}
}

View File

@@ -0,0 +1,31 @@
using Engine.Core;
namespace Engine.Systems.Tween;
public static class TweenRay3DExtensions
{
private static readonly BoxedPool<Ray3D> boxedRay3DPool = new(2);
public static ITween TweenRay3D(this Ray3D initialRay3D, ITweenManager tweenManager, float duration, Ray3D targetRay3D, System.Action<Ray3D> setMethod)
{
Boxed<Ray3D> boxedInitial = boxedRay3DPool.Get(initialRay3D);
Boxed<Ray3D> boxedTarget = boxedRay3DPool.Get(targetRay3D);
ITween tween = tweenManager.StartTween(duration,
t => setMethod?.Invoke(
new Ray3D(
boxedInitial.Value.Origin.Lerp(boxedTarget.Value.Origin, t),
boxedInitial.Value.Direction.Lerp(boxedTarget.Value.Direction, t).Normalized
)
)
);
tween.OnComplete(() =>
{
boxedRay3DPool.Return(boxedInitial);
boxedRay3DPool.Return(boxedTarget);
});
return tween;
}
}

View File

@@ -0,0 +1,24 @@
using Engine.Core;
namespace Engine.Systems.Tween;
public static class TweenVector4DExtensions
{
private static readonly BoxedPool<Vector4D> boxedVector4DPool = new(2);
public static ITween TweenVector4D(this Vector4D initialVector4D, ITweenManager tweenManager, float duration, Vector4D targetVector4D, System.Action<Vector4D> setMethod)
{
Boxed<Vector4D> boxedInitial = boxedVector4DPool.Get(initialVector4D);
Boxed<Vector4D> boxedTarget = boxedVector4DPool.Get(targetVector4D);
ITween tween = tweenManager.StartTween(duration, t => setMethod?.Invoke(boxedInitial.Value.Lerp(boxedTarget.Value, t)));
tween.OnComplete(() =>
{
boxedVector4DPool.Return(boxedInitial);
boxedVector4DPool.Return(boxedTarget);
});
return tween;
}
}