feat: added basic 4d matrix

This commit is contained in:
2025-10-24 23:03:07 +03:00
parent 2d612ea0d4
commit f180713f4b
2 changed files with 298 additions and 9 deletions

View File

@@ -0,0 +1,289 @@
using System;
using System.Numerics;
namespace Engine.Core;
// TODO Comments
/// <summary>
/// Represents a 4D left handed space matrix.
/// </summary>
/// <remarks>
/// Initializes a new instance of the <see cref="Matrix4x4"/> struct with the specified values.
/// </remarks>
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")]
public readonly struct Matrix4x4(
float m11, float m12, float m13, float m14,
float m21, float m22, float m23, float m24,
float m31, float m32, float m33, float m34,
float m41, float m42, float m43, float m44
) : IEquatable<Matrix4x4>
{
public readonly float M11 = m11, M12 = m12, M13 = m13, M14 = m14;
public readonly float M21 = m21, M22 = m22, M23 = m23, M24 = m24;
public readonly float M31 = m31, M32 = m32, M33 = m33, M34 = m34;
public readonly float M41 = m41, M42 = m42, M43 = m43, M44 = m44;
/// <summary>
/// Extracts the position (translation) from the <see cref="Matrix4x4"/>.
/// </summary>
public readonly Vector3D Position => new(M41, M42, M43);
/// <summary>
/// Extracts the scale from the <see cref="Matrix4x4"/>.
/// </summary>
public readonly Vector3D Scale
{
get
{
float scaleX = new Vector3D(M11, M12, M13).Length();
float scaleY = new Vector3D(M21, M22, M23).Length();
float scaleZ = new Vector3D(M31, M32, M33).Length();
if (Determinant(this) < 0)
scaleX *= -1;
return new(scaleX, scaleY, scaleZ);
}
}
/// <summary>
/// Extracts the rotation from the <see cref="Matrix4x4"/>.
/// </summary>
public readonly Quaternion Rotation => Quaternion.FromRotationMatrix4x4(this).Normalized;
/// <summary>
/// Represents the identity <see cref="Matrix4x4"/>.
/// </summary>
public static Matrix4x4 Identity => new(
1f, 0f, 0f, 0f,
0f, 1f, 0f, 0f,
0f, 0f, 1f, 0f,
0f, 0f, 0f, 1f
);
public static Matrix4x4 operator *(Matrix4x4 a, Matrix4x4 b) => new(
a.M11 * b.M11 + a.M12 * b.M21 + a.M13 * b.M31 + a.M14 * b.M41,
a.M11 * b.M12 + a.M12 * b.M22 + a.M13 * b.M32 + a.M14 * b.M42,
a.M11 * b.M13 + a.M12 * b.M23 + a.M13 * b.M33 + a.M14 * b.M43,
a.M11 * b.M14 + a.M12 * b.M24 + a.M13 * b.M34 + a.M14 * b.M44,
a.M21 * b.M11 + a.M22 * b.M21 + a.M23 * b.M31 + a.M24 * b.M41,
a.M21 * b.M12 + a.M22 * b.M22 + a.M23 * b.M32 + a.M24 * b.M42,
a.M21 * b.M13 + a.M22 * b.M23 + a.M23 * b.M33 + a.M24 * b.M43,
a.M21 * b.M14 + a.M22 * b.M24 + a.M23 * b.M34 + a.M24 * b.M44,
a.M31 * b.M11 + a.M32 * b.M21 + a.M33 * b.M31 + a.M34 * b.M41,
a.M31 * b.M12 + a.M32 * b.M22 + a.M33 * b.M32 + a.M34 * b.M42,
a.M31 * b.M13 + a.M32 * b.M23 + a.M33 * b.M33 + a.M34 * b.M43,
a.M31 * b.M14 + a.M32 * b.M24 + a.M33 * b.M34 + a.M34 * b.M44,
a.M41 * b.M11 + a.M42 * b.M21 + a.M43 * b.M31 + a.M44 * b.M41,
a.M41 * b.M12 + a.M42 * b.M22 + a.M43 * b.M32 + a.M44 * b.M42,
a.M41 * b.M13 + a.M42 * b.M23 + a.M43 * b.M33 + a.M44 * b.M43,
a.M41 * b.M14 + a.M42 * b.M24 + a.M43 * b.M34 + a.M44 * b.M44
);
public static bool operator ==(Matrix4x4 left, Matrix4x4 right) =>
left.M11 == right.M11 && left.M12 == right.M12 && left.M13 == right.M13 && left.M14 == right.M14 &&
left.M21 == right.M21 && left.M22 == right.M22 && left.M23 == right.M23 && left.M24 == right.M24 &&
left.M31 == right.M31 && left.M32 == right.M32 && left.M33 == right.M33 && left.M34 == right.M34 &&
left.M41 == right.M41 && left.M42 == right.M42 && left.M43 == right.M43 && left.M44 == right.M44;
public static bool operator !=(Matrix4x4 left, Matrix4x4 right) =>
left.M11 != right.M11 || left.M12 != right.M12 || left.M13 != right.M13 || left.M14 != right.M14 ||
left.M21 != right.M21 || left.M22 != right.M22 || left.M23 != right.M23 || left.M24 != right.M24 ||
left.M31 != right.M31 || left.M32 != right.M32 || left.M33 != right.M33 || left.M34 != right.M34 ||
left.M41 != right.M41 || left.M42 != right.M42 || left.M43 != right.M43 || left.M44 != right.M44;
public static implicit operator System.Numerics.Matrix4x4(Matrix4x4 m) => new(
m.M11, m.M12, m.M13, m.M14,
m.M21, m.M22, m.M23, m.M24,
m.M31, m.M32, m.M33, m.M34,
m.M41, m.M42, m.M43, m.M44
);
public static implicit operator Matrix4x4(System.Numerics.Matrix4x4 m) => new(
m.M11, m.M12, m.M13, m.M14,
m.M21, m.M22, m.M23, m.M24,
m.M31, m.M32, m.M33, m.M34,
m.M41, m.M42, m.M43, m.M44
);
/// <summary>
/// Calculates the determinant of the <see cref="Matrix4x4"/>.
/// </summary>
/// <param name="m">The <see cref="Matrix4x4"/>.</param>
/// <returns>The determinant of the <see cref="Matrix4x4"/>.</returns>
public static float Determinant(Matrix4x4 m) => // https://www.euclideanspace.com/maths/algebra/matrix/functions/determinant/fourD/index.htm
m.M14 * m.M23 * m.M32 * m.M41 - m.M13 * m.M24 * m.M32 * m.M41 -
m.M14 * m.M22 * m.M33 * m.M41 + m.M12 * m.M24 * m.M33 * m.M41 +
m.M13 * m.M22 * m.M34 * m.M41 - m.M12 * m.M23 * m.M34 * m.M41 -
m.M14 * m.M23 * m.M31 * m.M42 + m.M13 * m.M24 * m.M31 * m.M42 +
m.M14 * m.M21 * m.M33 * m.M42 - m.M11 * m.M24 * m.M33 * m.M42 -
m.M13 * m.M21 * m.M34 * m.M42 + m.M11 * m.M23 * m.M34 * m.M42 +
m.M14 * m.M22 * m.M31 * m.M43 - m.M12 * m.M24 * m.M31 * m.M43 -
m.M14 * m.M21 * m.M32 * m.M43 + m.M11 * m.M24 * m.M32 * m.M43 +
m.M12 * m.M21 * m.M34 * m.M43 - m.M11 * m.M22 * m.M34 * m.M43 -
m.M13 * m.M22 * m.M31 * m.M44 + m.M12 * m.M23 * m.M31 * m.M44 +
m.M13 * m.M21 * m.M32 * m.M44 - m.M11 * m.M23 * m.M32 * m.M44 -
m.M12 * m.M21 * m.M33 * m.M44 + m.M11 * m.M22 * m.M33 * m.M44;
public static Matrix4x4 CreateTranslation(Vector3D position) => new(
1f, 0f, 0f, 0f,
0f, 1f, 0f, 0f,
0f, 0f, 1f, 0f,
position.X, position.Y, position.Z, 1
);
public static Matrix4x4 CreateScale(Vector3D scale) => new(
scale.X, 0f, 0f, 0f,
0f, scale.Y, 0f, 0f,
0f, 0f, scale.Z, 0f,
0f, 0f, 0f, 1f
);
public static Matrix4x4 CreateRotationX(float radians)
{
float c = Math.Cos(radians);
float s = Math.Sin(radians);
return new Matrix4x4(
1f, 0f, 0f, 0f,
0f, c, s, 0f,
0f, -s, c, 0f,
0f, 0f, 0f, 1f
);
}
public static Matrix4x4 CreateRotationY(float radians)
{
float c = Math.Cos(radians);
float s = Math.Sin(radians);
return new Matrix4x4(
c, 0f, -s, 0f,
0f, 1f, 0f, 0f,
s, 0f, c, 0f,
0f, 0f, 0f, 1f
);
}
public static Matrix4x4 CreateRotationZ(float radians)
{
float c = Math.Cos(radians);
float s = Math.Sin(radians);
return new Matrix4x4(
c, s, 0f, 0f,
-s, c, 0f, 0f,
0f, 0f, 1f, 0f,
0f, 0f, 0f, 1f
);
}
// TODO Find a better calculation for this
public static Matrix4x4 CreateRotation(Quaternion quaternion)
{
Vector3D angles = quaternion.ToAngles();
return Identity * CreateRotationX(angles.X) * CreateRotationY(angles.Y) * CreateRotationZ(angles.Z);
}
public static Matrix4x4 CreateLookMatrix(Vector3D forward, Vector3D up)
{
Vector3D z = forward.Normalized;
Vector3D x = up.Cross(z).Normalized;
Vector3D y = z.Cross(x);
return new Matrix4x4(
x.X, y.X, z.X, 0f,
x.Y, y.Y, z.Y, 0f,
x.Z, y.Z, z.Z, 0f,
0f, 0f, 0f, 1f
);
}
public static Matrix4x4 CreateLookMatrix(Vector3D position, Vector3D target, Vector3D up)
{
Vector3D z = position.FromTo(target).Normalized;
Vector3D x = up.Cross(z).Normalized;
Vector3D y = z.Cross(x);
return new Matrix4x4(
x.X, y.X, z.X, 0f,
x.Y, y.Y, z.Y, 0f,
x.Z, y.Z, z.Z, 0f,
-x.Dot(position), -y.Dot(position), -z.Dot(position), 1f
);
}
public static Matrix4x4 CreatePerspectiveFieldOfView(float fieldOfViewInRadians, float aspectRatio, float nearPlane, float farPlane)
{
float yScale = 1f / Math.Tan(fieldOfViewInRadians / 2f);
float xScale = yScale / aspectRatio;
return new Matrix4x4(
xScale, 0f, 0f, 0f,
0f, yScale, 0f, 0f,
0f, 0f, farPlane / (farPlane - nearPlane), 1f,
0f, 0f, -nearPlane * farPlane / (farPlane - nearPlane), 0f
);
}
public static Matrix4x4 ToRightHanded(Matrix4x4 m) => new(
m.M11, m.M12, m.M13, m.M14,
m.M31, m.M32, m.M33, m.M34,
m.M21, m.M22, m.M23, m.M24,
m.M41, m.M42, m.M43, m.M44
);
public override bool Equals(object? obj) => obj is Matrix4x4 matrix && this == matrix;
public bool Equals(Matrix4x4 other) => this == other;
public override int GetHashCode() => HashCode.Combine(
HashCode.Combine(M11, M12, M13, M14, M21, M22, M23, M24),
HashCode.Combine(M31, M32, M33, M34, M41, M42, M43, M44)
);
public override string ToString() => $"Matrix4x4({M11}, {M12}, {M13}, {M14},{M21}, {M22}, {M23}, {M24},{M31}, {M32}, {M33}, {M34},{M41}, {M42}, {M43}, {M44})";
}
/// <summary>
/// Provides extension methods for <see cref="Matrix4x4"/> type.
/// </summary>
public static class Matrix4x4Extensions
{
/// <inheritdoc cref="Matrix4x4.Determinant(Matrix4x4)" />
public static float Determinant(this Matrix4x4 matrix) => Matrix4x4.Determinant(matrix);
/// <inheritdoc cref="Matrix4x4.CreateTranslation(Vector3D)" />
public static Matrix4x4 ApplyTranslation(this Matrix4x4 matrix, Vector3D translation) => matrix * Matrix4x4.CreateTranslation(translation);
/// <inheritdoc cref="Matrix4x4.CreateScale(Vector3D)" />
public static Matrix4x4 ApplyScale(this Matrix4x4 matrix, Vector3 scale) => matrix * Matrix4x4.CreateScale(scale);
/// <inheritdoc cref="Matrix4x4.CreateRotationZ(float)" />
public static Matrix4x4 ApplyRotationX(this Matrix4x4 matrix, float radians) => matrix * Matrix4x4.CreateRotationX(radians);
/// <inheritdoc cref="Matrix4x4.CreateRotationY(float)" />
public static Matrix4x4 ApplyRotationY(this Matrix4x4 matrix, float radians) => matrix * Matrix4x4.CreateRotationY(radians);
/// <inheritdoc cref="Matrix4x4.CreateRotationZ(float)" />
public static Matrix4x4 ApplyRotationZ(this Matrix4x4 matrix, float radians) => matrix * Matrix4x4.CreateRotationZ(radians);
/// <inheritdoc cref="Matrix4x4.CreateRotation(Quater)" />
public static Matrix4x4 ApplyRotation(this Matrix4x4 matrix, Quaternion quaternion) => matrix * Matrix4x4.CreateRotation(quaternion);
/// <inheritdoc cref="Matrix4x4.CreateLookMatrix( Vector3D, Vector3D)" />
public static Matrix4x4 ApplyLookRotationTo(this Matrix4x4 matrix, Vector3D forward, Vector3 up) => matrix * Matrix4x4.CreateLookMatrix(forward, up);
/// <inheritdoc cref="Matrix4x4.CreateLookMatrix(Vector3D, Vector3D, Vector3D)" />
public static Matrix4x4 CreateLookMatrixTo(this Vector3D from, Vector3D to, Vector3 up) => Matrix4x4.CreateLookMatrix(from, to, up);
/// <inheritdoc cref="Matrix4x4.CreatePerspectiveFieldOfView(float, float, float, float)" />
public static Matrix4x4 ApplyPerspectiveFieldOfView(this Matrix4x4 matrix, float fieldOfViewInRadians, float aspectRatio, float nearPlane, float farPlane)
=> matrix * Matrix4x4.CreatePerspectiveFieldOfView(fieldOfViewInRadians, aspectRatio, nearPlane, farPlane);
/// <inheritdoc cref="Matrix4x4.ToRightHanded(Matrix4x4) />
public static Matrix4x4 ToRightHanded(this Matrix4x4 matrix) => Matrix4x4.ToRightHanded(matrix);
}

View File

@@ -167,7 +167,7 @@ public readonly struct Quaternion(float x, float y, float z, float w) : IEquatab
Vector3D right = up.Cross(forward).Normalized;
Vector3D newUp = forward.Cross(right);
System.Numerics.Matrix4x4 rot = new(
Matrix4x4 rot = new(
right.X, right.Y, right.Z, 0f,
newUp.X, newUp.Y, newUp.Z, 0f,
forward.X, forward.Y, forward.Z, 0f,
@@ -297,11 +297,11 @@ public readonly struct Quaternion(float x, float y, float z, float w) : IEquatab
}
/// <summary>
/// Calculates the <see cref="System.Numerics.Matrix4x4"/> from given <see cref="Quaternion"/>.
/// Calculates the <see cref="Matrix4x4"/> from given <see cref="Quaternion"/>.
/// </summary>
/// <param name="quaternion">The rotation <see cref="Quaternion"/>.</param>
/// <returns>The rotation <see cref="System.Numerics.Matrix4x4"/> calculated by the given <see cref="Quaternion"/>.</returns>
public static System.Numerics.Matrix4x4 ToRotationMatrix4x4(Quaternion quaternion)
/// <returns>The rotation <see cref="Matrix4x4"/> calculated by the given <see cref="Quaternion"/>.</returns>
public static Matrix4x4 ToRotationMatrix4x4(Quaternion quaternion)
{
float m00 = 1 - 2 * (quaternion.Y * quaternion.Y + quaternion.Z * quaternion.Z);
float m01 = 2 * (quaternion.X * quaternion.Y - quaternion.W * quaternion.Z);
@@ -332,11 +332,11 @@ public readonly struct Quaternion(float x, float y, float z, float w) : IEquatab
}
/// <summary>
/// Calculates the <see cref="Quaternion"/> from given <see cref="System.Numerics.Matrix4x4"/>.
/// Calculates the <see cref="Quaternion"/> from given <see cref="Matrix4x4"/>.
/// </summary>
/// <param name="martix">The rotation <see cref="System.Numerics.Matrix4x4"/>.</param>
/// <returns>The rotation <see cref="Quaternion"/> calculated by the given <see cref="System.Numerics.Matrix4x4"/>.</returns>
public static Quaternion FromRotationMatrix4x4(System.Numerics.Matrix4x4 martix)
/// <param name="martix">The rotation <see cref="Matrix4x4"/>.</param>
/// <returns>The rotation <see cref="Quaternion"/> calculated by the given <see cref="Matrix4x4"/>.</returns>
public static Quaternion FromRotationMatrix4x4(Matrix4x4 martix)
{
float trace = martix.M11 + martix.M22 + martix.M33;
float w, x, y, z;
@@ -459,7 +459,7 @@ public static class QuaternionExtensions
public static float Dot(this Quaternion left, Quaternion right) => Quaternion.Dot(left, right);
/// <inheritdoc cref="Quaternion.ToRotationMatrix4x4(Quaternion, Quaternion)" />
public static System.Numerics.Matrix4x4 ToRotationMatrix4x4(this Quaternion quaternion) => Quaternion.ToRotationMatrix4x4(quaternion);
public static Matrix4x4 ToRotationMatrix4x4(this Quaternion quaternion) => Quaternion.ToRotationMatrix4x4(quaternion);
/// <inheritdoc cref="Quaternion.FromAxisAngle(Vector3D, float)" />
public static Quaternion CreateRotation(this Vector3D axis, float angle) => Quaternion.FromAxisAngle(axis, angle);