diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp
index 9fa3cf33014d08..58decd369622d4 100644
--- a/src/coreclr/jit/lowerxarch.cpp
+++ b/src/coreclr/jit/lowerxarch.cpp
@@ -2809,6 +2809,9 @@ GenTree* Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
if (comp->compOpportunisticallyDependsOn(InstructionSet_SSE41))
{
+ assert(argCnt <= 4);
+ GenTree* insertedNodes[4];
+
for (N = 1; N < argCnt - 1; N++)
{
// We will be constructing the following parts:
@@ -2837,10 +2840,12 @@ GenTree* Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
idx = comp->gtNewIconNode(N << 4, TYP_INT);
BlockRange().InsertAfter(tmp2, idx);
- tmp1 = comp->gtNewSimdHWIntrinsicNode(simdType, tmp1, tmp2, idx, NI_SSE41_Insert, simdBaseJitType,
+ tmp3 = comp->gtNewSimdHWIntrinsicNode(simdType, tmp1, tmp2, idx, NI_SSE41_Insert, simdBaseJitType,
simdSize);
- BlockRange().InsertAfter(idx, tmp1);
- LowerNode(tmp1);
+ BlockRange().InsertAfter(idx, tmp3);
+
+ insertedNodes[N] = tmp3;
+ tmp1 = tmp3;
}
// We will be constructing the following parts:
@@ -2868,6 +2873,18 @@ GenTree* Lowering::LowerHWIntrinsicCreate(GenTreeHWIntrinsic* node)
BlockRange().InsertAfter(tmp2, idx);
node->ResetHWIntrinsicId(NI_SSE41_Insert, comp, tmp1, tmp2, idx);
+
+ for (N = 1; N < argCnt - 1; N++)
+ {
+ // LowerNode for NI_SSE41_Insert specially handles zeros, constants, and certain mask values
+ // to do the minimal number of operations and may merge together two neighboring inserts that
+ // don't have any side effects between them. Because of this and because of the interdependence
+ // of the inserts we've created above, we need to wait to lower the generated inserts until after
+ // we've completed the chain.
+
+ GenTree* insertedNode = insertedNodes[N];
+ LowerNode(insertedNode);
+ }
break;
}
diff --git a/src/libraries/System.Numerics.Vectors/tests/Matrix3x2Tests.cs b/src/libraries/System.Numerics.Vectors/tests/Matrix3x2Tests.cs
index f4144ad71576a7..4374cb75b2f7a3 100644
--- a/src/libraries/System.Numerics.Vectors/tests/Matrix3x2Tests.cs
+++ b/src/libraries/System.Numerics.Vectors/tests/Matrix3x2Tests.cs
@@ -568,12 +568,15 @@ public void Matrix3x2EqualsTest()
public void Matrix3x2GetHashCodeTest()
{
Matrix3x2 target = GenerateIncrementalMatrixNumber();
- int expected = HashCode.Combine(target.M11, target.M12,
- target.M21, target.M22,
- target.M31, target.M32);
- int actual;
- actual = target.GetHashCode();
+ int expected = HashCode.Combine(
+ new Vector2(target.M11, target.M12),
+ new Vector2(target.M21, target.M22),
+ new Vector2(target.M31, target.M32)
+ );
+
+ int actual = target.GetHashCode();
+
Assert.Equal(expected, actual);
}
diff --git a/src/libraries/System.Numerics.Vectors/tests/Matrix4x4Tests.cs b/src/libraries/System.Numerics.Vectors/tests/Matrix4x4Tests.cs
index 98bf54dad46231..0aacc92730f1f4 100644
--- a/src/libraries/System.Numerics.Vectors/tests/Matrix4x4Tests.cs
+++ b/src/libraries/System.Numerics.Vectors/tests/Matrix4x4Tests.cs
@@ -1690,29 +1690,13 @@ public void Matrix4x4GetHashCodeTest()
{
Matrix4x4 target = GenerateIncrementalMatrixNumber();
- HashCode hash = default;
+ int expected = HashCode.Combine(
+ new Vector4(target.M11, target.M12, target.M13, target.M14),
+ new Vector4(target.M21, target.M22, target.M23, target.M24),
+ new Vector4(target.M31, target.M32, target.M33, target.M34),
+ new Vector4(target.M41, target.M42, target.M43, target.M44)
+ );
- hash.Add(target.M11);
- hash.Add(target.M12);
- hash.Add(target.M13);
- hash.Add(target.M14);
-
- hash.Add(target.M21);
- hash.Add(target.M22);
- hash.Add(target.M23);
- hash.Add(target.M24);
-
- hash.Add(target.M31);
- hash.Add(target.M32);
- hash.Add(target.M33);
- hash.Add(target.M34);
-
- hash.Add(target.M41);
- hash.Add(target.M42);
- hash.Add(target.M43);
- hash.Add(target.M44);
-
- int expected = hash.ToHashCode();
int actual = target.GetHashCode();
Assert.Equal(expected, actual);
diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
index 58fe300c6ba3cb..63fd4595e9f804 100644
--- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
+++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
@@ -556,7 +556,9 @@
+
+
@@ -2535,4 +2537,4 @@
-
\ No newline at end of file
+
diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Matrix3x2.Impl.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Matrix3x2.Impl.cs
new file mode 100644
index 00000000000000..4fb270baa096b3
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Matrix3x2.Impl.cs
@@ -0,0 +1,522 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+
+namespace System.Numerics
+{
+ public partial struct Matrix3x2
+ {
+ // See Matrix3x2.cs for an explanation of why this file/type exists
+ //
+ // Note that we use some particular patterns below, such as defining a result
+ // and assigning the fields directly rather than using the object initializer
+ // syntax. We do this because it saves roughly 8-bytes of IL per method which
+ // in turn helps improve inlining chances.
+
+ internal const uint RowCount = 3;
+ internal const uint ColumnCount = 2;
+
+ [UnscopedRef]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal ref Impl AsImpl() => ref Unsafe.As(ref this);
+
+ [UnscopedRef]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly ref readonly Impl AsROImpl() => ref Unsafe.As(ref Unsafe.AsRef(in this));
+
+ internal struct Impl : IEquatable
+ {
+ [UnscopedRef]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ref Matrix3x2 AsM3x2() => ref Unsafe.As(ref this);
+
+ private const float RotationEpsilon = 0.001f * MathF.PI / 180f; // 0.1% of a degree
+
+ public Vector2 X;
+ public Vector2 Y;
+ public Vector2 Z;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Init(float m11, float m12,
+ float m21, float m22,
+ float m31, float m32)
+ {
+ X = new Vector2(m11, m12);
+ Y = new Vector2(m21, m22);
+ Z = new Vector2(m31, m32);
+ }
+
+ public static Impl Identity
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ Impl result;
+
+ result.X = Vector2.UnitX;
+ result.Y = Vector2.UnitY;
+ result.Z = Vector2.Zero;
+
+ return result;
+ }
+ }
+
+ public float this[int row, int column]
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get
+ {
+ if ((uint)row >= RowCount)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+ }
+
+ return Unsafe.Add(ref Unsafe.AsRef(in this.X), row)[column];
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set
+ {
+ if ((uint)row >= RowCount)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+ }
+ Unsafe.Add(ref this.X, row)[column] = value;
+ }
+ }
+
+ public readonly bool IsIdentity
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ return (X == Vector2.UnitX)
+ && (Y == Vector2.UnitY)
+ && (Z == Vector2.Zero);
+ }
+ }
+
+ public Vector2 Translation
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get
+ {
+ return Z;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set
+ {
+ Z = value;
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl operator +(in Impl left, in Impl right)
+ {
+ Impl result;
+
+ result.X = left.X + right.X;
+ result.Y = left.Y + right.Y;
+ result.Z = left.Z + right.Z;
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool operator ==(in Impl left, in Impl right)
+ {
+ return (left.X == right.X)
+ && (left.Y == right.Y)
+ && (left.Z == right.Z);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool operator !=(in Impl left, in Impl right)
+ {
+ return (left.X != right.X)
+ || (left.Y != right.Y)
+ || (left.Z != right.Z);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl operator *(in Impl left, in Impl right)
+ {
+ Impl result;
+
+ result.X = new Vector2(
+ left.X.X * right.X.X + left.X.Y * right.Y.X,
+ left.X.X * right.X.Y + left.X.Y * right.Y.Y
+ );
+ result.Y = new Vector2(
+ left.Y.X * right.X.X + left.Y.Y * right.Y.X,
+ left.Y.X * right.X.Y + left.Y.Y * right.Y.Y
+ );
+ result.Z = new Vector2(
+ left.Z.X * right.X.X + left.Z.Y * right.Y.X + right.Z.X,
+ left.Z.X * right.X.Y + left.Z.Y * right.Y.Y + right.Z.Y
+ );
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl operator *(in Impl left, float right)
+ {
+ Impl result;
+
+ result.X = left.X * right;
+ result.Y = left.Y * right;
+ result.Z = left.Z * right;
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl operator -(in Impl left, in Impl right)
+ {
+ Impl result;
+
+ result.X = left.X - right.X;
+ result.Y = left.Y - right.Y;
+ result.Z = left.Z - right.Z;
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl operator -(in Impl value)
+ {
+ Impl result;
+
+ result.X = -value.X;
+ result.Y = -value.Y;
+ result.Z = -value.Z;
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateRotation(float radians)
+ {
+ radians = MathF.IEEERemainder(radians, MathF.PI * 2);
+
+ float c;
+ float s;
+
+ if (radians > -RotationEpsilon && radians < RotationEpsilon)
+ {
+ // Exact case for zero rotation.
+ c = 1;
+ s = 0;
+ }
+ else if (radians > MathF.PI / 2 - RotationEpsilon && radians < MathF.PI / 2 + RotationEpsilon)
+ {
+ // Exact case for 90 degree rotation.
+ c = 0;
+ s = 1;
+ }
+ else if (radians < -MathF.PI + RotationEpsilon || radians > MathF.PI - RotationEpsilon)
+ {
+ // Exact case for 180 degree rotation.
+ c = -1;
+ s = 0;
+ }
+ else if (radians > -MathF.PI / 2 - RotationEpsilon && radians < -MathF.PI / 2 + RotationEpsilon)
+ {
+ // Exact case for 270 degree rotation.
+ c = 0;
+ s = -1;
+ }
+ else
+ {
+ // Arbitrary rotation.
+ c = MathF.Cos(radians);
+ s = MathF.Sin(radians);
+ }
+
+ // [ c s ]
+ // [ -s c ]
+ // [ 0 0 ]
+
+ Impl result;
+
+ result.X = new Vector2( c, s);
+ result.Y = new Vector2(-s, c);
+ result.Z = Vector2.Zero;
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateRotation(float radians, Vector2 centerPoint)
+ {
+ radians = MathF.IEEERemainder(radians, MathF.PI * 2);
+
+ float c, s;
+
+ if (radians > -RotationEpsilon && radians < RotationEpsilon)
+ {
+ // Exact case for zero rotation.
+ c = 1;
+ s = 0;
+ }
+ else if (radians > MathF.PI / 2 - RotationEpsilon && radians < MathF.PI / 2 + RotationEpsilon)
+ {
+ // Exact case for 90 degree rotation.
+ c = 0;
+ s = 1;
+ }
+ else if (radians < -MathF.PI + RotationEpsilon || radians > MathF.PI - RotationEpsilon)
+ {
+ // Exact case for 180 degree rotation.
+ c = -1;
+ s = 0;
+ }
+ else if (radians > -MathF.PI / 2 - RotationEpsilon && radians < -MathF.PI / 2 + RotationEpsilon)
+ {
+ // Exact case for 270 degree rotation.
+ c = 0;
+ s = -1;
+ }
+ else
+ {
+ // Arbitrary rotation.
+ c = MathF.Cos(radians);
+ s = MathF.Sin(radians);
+ }
+
+ float x = centerPoint.X * (1 - c) + centerPoint.Y * s;
+ float y = centerPoint.Y * (1 - c) - centerPoint.X * s;
+
+ // [ c s ]
+ // [ -s c ]
+ // [ x y ]
+
+ Impl result;
+
+ result.X = new Vector2( c, s);
+ result.Y = new Vector2(-s, c);
+ result.Z = new Vector2( x, y);
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateScale(Vector2 scales)
+ {
+ Impl result;
+
+ result.X = new Vector2(scales.X, 0);
+ result.Y = new Vector2(0, scales.Y);
+ result.Z = Vector2.Zero;
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateScale(float scaleX, float scaleY)
+ {
+ Impl result;
+
+ result.X = new Vector2(scaleX, 0);
+ result.Y = new Vector2(0, scaleY);
+ result.Z = Vector2.Zero;
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateScale(float scaleX, float scaleY, Vector2 centerPoint)
+ {
+ Impl result;
+
+ result.X = new Vector2(scaleX, 0);
+ result.Y = new Vector2(0, scaleY);
+ result.Z = centerPoint * (Vector2.One - new Vector2(scaleX, scaleY));
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateScale(Vector2 scales, Vector2 centerPoint)
+ {
+ Impl result;
+
+ result.X = new Vector2(scales.X, 0);
+ result.Y = new Vector2(0, scales.Y);
+ result.Z = centerPoint * (Vector2.One - scales);
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateScale(float scale)
+ {
+ Impl result;
+
+ result.X = new Vector2(scale, 0);
+ result.Y = new Vector2(0, scale);
+ result.Z = Vector2.Zero;
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateScale(float scale, Vector2 centerPoint)
+ {
+ Impl result;
+
+ result.X = new Vector2(scale, 0);
+ result.Y = new Vector2(0, scale);
+ result.Z = centerPoint * (Vector2.One - new Vector2(scale));
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateSkew(float radiansX, float radiansY)
+ {
+ Impl result;
+
+ result.X = new Vector2(1, MathF.Tan(radiansY));
+ result.Y = new Vector2(MathF.Tan(radiansX), 1);
+ result.Z = Vector2.Zero;
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateSkew(float radiansX, float radiansY, Vector2 centerPoint)
+ {
+ float xTan = MathF.Tan(radiansX);
+ float yTan = MathF.Tan(radiansY);
+
+ float tx = -centerPoint.Y * xTan;
+ float ty = -centerPoint.X * yTan;
+
+ Impl result;
+
+ result.X = new Vector2(1, yTan);
+ result.Y = new Vector2(xTan, 1);
+ result.Z = new Vector2(tx, ty);
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateTranslation(Vector2 position)
+ {
+ Impl result;
+
+ result.X = Vector2.UnitX;
+ result.Y = Vector2.UnitY;
+ result.Z = position;
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateTranslation(float positionX, float positionY)
+ {
+ Impl result;
+
+ result.X = Vector2.UnitX;
+ result.Y = Vector2.UnitY;
+ result.Z = new Vector2(positionX, positionY);
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool Invert(in Impl matrix, out Impl result)
+ {
+ float det = (matrix.X.X * matrix.Y.Y) - (matrix.Y.X * matrix.X.Y);
+
+ if (MathF.Abs(det) < float.Epsilon)
+ {
+ Vector2 vNaN = new Vector2(float.NaN);
+
+ result.X = vNaN;
+ result.Y = vNaN;
+ result.Z = vNaN;
+
+ return false;
+ }
+
+ float invDet = 1.0f / det;
+
+ result.X = new Vector2(
+ +matrix.Y.Y * invDet,
+ -matrix.X.Y * invDet
+ );
+ result.Y = new Vector2(
+ -matrix.Y.X * invDet,
+ +matrix.X.X * invDet
+ );
+ result.Z = new Vector2(
+ (matrix.Y.X * matrix.Z.Y - matrix.Z.X * matrix.Y.Y) * invDet,
+ (matrix.Z.X * matrix.X.Y - matrix.X.X * matrix.Z.Y) * invDet
+ );
+
+ return true;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl Lerp(in Impl left, in Impl right, float amount)
+ {
+ Impl result;
+
+ result.X = Vector2.Lerp(left.X, right.X, amount);
+ result.Y = Vector2.Lerp(left.Y, right.Y, amount);
+ result.Z = Vector2.Lerp(left.Z, right.Z, amount);
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public override readonly bool Equals([NotNullWhen(true)] object? obj)
+ => (obj is Matrix3x2 other) && Equals(in other.AsImpl());
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public readonly bool Equals(in Impl other)
+ {
+ // This function needs to account for floating-point equality around NaN
+ // and so must behave equivalently to the underlying float/double.Equals
+
+ return X.Equals(other.X)
+ && Y.Equals(other.Y)
+ && Z.Equals(other.Z);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public readonly float GetDeterminant()
+ {
+ // There isn't actually any such thing as a determinant for a non-square matrix,
+ // but this 3x2 type is really just an optimization of a 3x3 where we happen to
+ // know the rightmost column is always (0, 0, 1). So we expand to 3x3 format:
+ //
+ // [ X.X, X.Y, 0 ]
+ // [ Y.X, Y.Y, 0 ]
+ // [ Z.X, Z.Y, 1 ]
+ //
+ // Sum the diagonal products:
+ // (X.X * Y.Y * 1) + (X.Y * 0 * Z.X) + (0 * Y.X * Z.Y)
+ //
+ // Subtract the opposite diagonal products:
+ // (Z.X * Y.Y * 0) + (Z.Y * 0 * X.X) + (1 * Y.X * X.Y)
+ //
+ // Collapse out the constants and oh look, this is just a 2x2 determinant!
+
+ return (X.X * Y.Y) - (Y.X * X.Y);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public override readonly int GetHashCode() => HashCode.Combine(X, Y, Z);
+
+ bool IEquatable.Equals(Impl other) => Equals(in other);
+ }
+ }
+}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Matrix3x2.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Matrix3x2.cs
index f939ec9abded3a..49dac972571e7b 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Matrix3x2.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Matrix3x2.cs
@@ -3,7 +3,6 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
-using System.Runtime.Intrinsics;
namespace System.Numerics
{
@@ -12,15 +11,17 @@ namespace System.Numerics
/// [!INCLUDE[vectors-are-rows-paragraph](~/includes/system-numerics-vectors-are-rows.md)]
/// ]]>
[Intrinsic]
- public struct Matrix3x2 : IEquatable
+ public partial struct Matrix3x2 : IEquatable
{
- private const float RotationEpsilon = 0.001f * MathF.PI / 180f; // 0.1% of a degree
-
- private static readonly Matrix3x2 _identity = new Matrix3x2(
- 1f, 0f,
- 0f, 1f,
- 0f, 0f
- );
+ // In an ideal world, we'd have 3x Vector2 fields. However, Matrix3x2 was shipped with
+ // 6x public float fields and as such we cannot change the "backing" fields without it being
+ // a breaking change. Likewise, we cannot switch to using something like ExplicitLayout
+ // without it pessimizing other parts of the JIT and still preventing things like field promotion.
+ //
+ // This nested Impl struct works around this problem by relying on the JIT treating same sizeof
+ // value type bitcasts as a no-op. Effectively the entire implementation is here in this type
+ // and the public facing Matrix3x2 just defers to it with simple reinterpret casts inserted
+ // at the relevant points.
/// The first element of the first row.
public float M11;
@@ -51,21 +52,21 @@ public Matrix3x2(float m11, float m12,
float m21, float m22,
float m31, float m32)
{
- M11 = m11;
- M12 = m12;
-
- M21 = m21;
- M22 = m22;
+ Unsafe.SkipInit(out this);
- M31 = m31;
- M32 = m32;
+ AsImpl().Init(
+ m11, m12,
+ m21, m22,
+ m31, m32
+ );
}
/// Gets the multiplicative identity matrix.
/// The multiplicative identify matrix.
public static Matrix3x2 Identity
{
- get => _identity;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => Impl.Identity.AsM3x2();
}
/// Gets or sets the element at the specified indices.
@@ -77,45 +78,32 @@ public static Matrix3x2 Identity
/// -or-
/// was less than zero or greater than the number of columns.
///
- public unsafe float this[int row, int column]
+ public float this[int row, int column]
{
- get
- {
- if ((uint)row >= 3)
- ThrowHelper.ThrowArgumentOutOfRangeException();
-
- Vector2 vrow = Unsafe.Add(ref Unsafe.As(ref M11), row);
- return vrow[column];
- }
- set
- {
- if ((uint)row >= 3)
- ThrowHelper.ThrowArgumentOutOfRangeException();
-
- ref Vector2 vrow = ref Unsafe.Add(ref Unsafe.As(ref M11), row);
- var tmp = Vector2.WithElement(vrow, column, value);
- vrow = tmp;
- }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => AsROImpl()[row, column];
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => AsImpl()[row, column] = value;
}
/// Gets a value that indicates whether the current matrix is the identity matrix.
/// if the current matrix is the identity matrix; otherwise, .
public readonly bool IsIdentity
{
- get => this == Identity;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => AsROImpl().IsIdentity;
}
/// Gets or sets the translation component of this matrix.
/// The translation component of the current instance.
public Vector2 Translation
{
- readonly get => new Vector2(M31, M32);
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => AsROImpl().Translation;
- set
- {
- M31 = value.X;
- M32 = value.Y;
- }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => AsImpl().Translation = value;
}
/// Adds each element in one matrix with its corresponding element in a second matrix.
@@ -123,46 +111,26 @@ public Vector2 Translation
/// The second matrix.
/// The matrix that contains the summed values.
/// The method defines the operation of the addition operator for objects.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Matrix3x2 operator +(Matrix3x2 value1, Matrix3x2 value2)
- {
- Matrix3x2 m;
-
- m.M11 = value1.M11 + value2.M11;
- m.M12 = value1.M12 + value2.M12;
-
- m.M21 = value1.M21 + value2.M21;
- m.M22 = value1.M22 + value2.M22;
-
- m.M31 = value1.M31 + value2.M31;
- m.M32 = value1.M32 + value2.M32;
-
- return m;
- }
+ => (value1.AsImpl() + value2.AsImpl()).AsM3x2();
/// Returns a value that indicates whether the specified matrices are equal.
/// The first matrix to compare.
/// The second matrix to compare.
/// if and are equal; otherwise, .
/// Two matrices are equal if all their corresponding elements are equal.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator ==(Matrix3x2 value1, Matrix3x2 value2)
- {
- // Check diagonal element first for early out.
- return (value1.M11 == value2.M11
- && value1.M22 == value2.M22
- && value1.M12 == value2.M12
- && value1.M21 == value2.M21
- && value1.M31 == value2.M31
- && value1.M32 == value2.M32);
- }
+ => value1.AsImpl() == value2.AsImpl();
/// Returns a value that indicates whether the specified matrices are not equal.
/// The first matrix to compare.
/// The second matrix to compare.
/// if and are not equal; otherwise, .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator !=(Matrix3x2 value1, Matrix3x2 value2)
- {
- return !(value1 == value2);
- }
+ => value1.AsImpl() != value2.AsImpl();
/// Multiplies two matrices together to compute the product.
/// The first matrix.
@@ -170,85 +138,33 @@ public Vector2 Translation
/// The product matrix.
/// The method defines the operation of the multiplication operator for objects.
public static Matrix3x2 operator *(Matrix3x2 value1, Matrix3x2 value2)
- {
- Matrix3x2 m;
-
- // First row
- m.M11 = value1.M11 * value2.M11 + value1.M12 * value2.M21;
- m.M12 = value1.M11 * value2.M12 + value1.M12 * value2.M22;
-
- // Second row
- m.M21 = value1.M21 * value2.M11 + value1.M22 * value2.M21;
- m.M22 = value1.M21 * value2.M12 + value1.M22 * value2.M22;
-
- // Third row
- m.M31 = value1.M31 * value2.M11 + value1.M32 * value2.M21 + value2.M31;
- m.M32 = value1.M31 * value2.M12 + value1.M32 * value2.M22 + value2.M32;
-
- return m;
- }
+ => (value1.AsImpl() * value2.AsImpl()).AsM3x2();
/// Multiplies a matrix by a float to compute the product.
/// The matrix to scale.
/// The scaling value to use.
/// The scaled matrix.
/// The method defines the operation of the multiplication operator for objects.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Matrix3x2 operator *(Matrix3x2 value1, float value2)
- {
- Matrix3x2 m;
-
- m.M11 = value1.M11 * value2;
- m.M12 = value1.M12 * value2;
-
- m.M21 = value1.M21 * value2;
- m.M22 = value1.M22 * value2;
-
- m.M31 = value1.M31 * value2;
- m.M32 = value1.M32 * value2;
-
- return m;
- }
+ => (value1.AsImpl() * value2).AsM3x2();
/// Subtracts each element in a second matrix from its corresponding element in a first matrix.
/// The first matrix.
/// The second matrix.
/// The matrix containing the values that result from subtracting each element in from its corresponding element in .
/// The method defines the operation of the subtraction operator for objects.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Matrix3x2 operator -(Matrix3x2 value1, Matrix3x2 value2)
- {
- Matrix3x2 m;
-
- m.M11 = value1.M11 - value2.M11;
- m.M12 = value1.M12 - value2.M12;
-
- m.M21 = value1.M21 - value2.M21;
- m.M22 = value1.M22 - value2.M22;
-
- m.M31 = value1.M31 - value2.M31;
- m.M32 = value1.M32 - value2.M32;
-
- return m;
- }
+ => (value1.AsImpl() - value2.AsImpl()).AsM3x2();
/// Negates the specified matrix by multiplying all its values by -1.
/// The matrix to negate.
/// The negated matrix.
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Matrix3x2 operator -(Matrix3x2 value)
- {
- Matrix3x2 m;
-
- m.M11 = -value.M11;
- m.M12 = -value.M12;
-
- m.M21 = -value.M21;
- m.M22 = -value.M22;
-
- m.M31 = -value.M31;
- m.M32 = -value.M32;
-
- return m;
- }
+ => (-value.AsImpl()).AsM3x2();
/// Adds each element in one matrix with its corresponding element in a second matrix.
/// The first matrix.
@@ -256,148 +172,33 @@ public Vector2 Translation
/// The matrix that contains the summed values of and .
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Matrix3x2 Add(Matrix3x2 value1, Matrix3x2 value2)
- {
- return value1 + value2;
- }
+ => (value1.AsImpl() + value2.AsImpl()).AsM3x2();
/// Creates a rotation matrix using the given rotation in radians.
/// The amount of rotation, in radians.
/// The rotation matrix.
public static Matrix3x2 CreateRotation(float radians)
- {
- radians = MathF.IEEERemainder(radians, MathF.PI * 2);
-
- float c, s;
-
- if (radians > -RotationEpsilon && radians < RotationEpsilon)
- {
- // Exact case for zero rotation.
- c = 1;
- s = 0;
- }
- else if (radians > MathF.PI / 2 - RotationEpsilon && radians < MathF.PI / 2 + RotationEpsilon)
- {
- // Exact case for 90 degree rotation.
- c = 0;
- s = 1;
- }
- else if (radians < -MathF.PI + RotationEpsilon || radians > MathF.PI - RotationEpsilon)
- {
- // Exact case for 180 degree rotation.
- c = -1;
- s = 0;
- }
- else if (radians > -MathF.PI / 2 - RotationEpsilon && radians < -MathF.PI / 2 + RotationEpsilon)
- {
- // Exact case for 270 degree rotation.
- c = 0;
- s = -1;
- }
- else
- {
- // Arbitrary rotation.
- c = MathF.Cos(radians);
- s = MathF.Sin(radians);
- }
-
- // [ c s ]
- // [ -s c ]
- // [ 0 0 ]
- Matrix3x2 result = Identity;
-
- result.M11 = c;
- result.M12 = s;
- result.M21 = -s;
- result.M22 = c;
-
- return result;
- }
+ => Impl.CreateRotation(radians).AsM3x2();
/// Creates a rotation matrix using the specified rotation in radians and a center point.
/// The amount of rotation, in radians.
/// The center point.
/// The rotation matrix.
public static Matrix3x2 CreateRotation(float radians, Vector2 centerPoint)
- {
- Matrix3x2 result;
-
- radians = MathF.IEEERemainder(radians, MathF.PI * 2);
-
- float c, s;
-
- if (radians > -RotationEpsilon && radians < RotationEpsilon)
- {
- // Exact case for zero rotation.
- c = 1;
- s = 0;
- }
- else if (radians > MathF.PI / 2 - RotationEpsilon && radians < MathF.PI / 2 + RotationEpsilon)
- {
- // Exact case for 90 degree rotation.
- c = 0;
- s = 1;
- }
- else if (radians < -MathF.PI + RotationEpsilon || radians > MathF.PI - RotationEpsilon)
- {
- // Exact case for 180 degree rotation.
- c = -1;
- s = 0;
- }
- else if (radians > -MathF.PI / 2 - RotationEpsilon && radians < -MathF.PI / 2 + RotationEpsilon)
- {
- // Exact case for 270 degree rotation.
- c = 0;
- s = -1;
- }
- else
- {
- // Arbitrary rotation.
- c = MathF.Cos(radians);
- s = MathF.Sin(radians);
- }
-
- float x = centerPoint.X * (1 - c) + centerPoint.Y * s;
- float y = centerPoint.Y * (1 - c) - centerPoint.X * s;
-
- // [ c s ]
- // [ -s c ]
- // [ x y ]
- result.M11 = c;
- result.M12 = s;
- result.M21 = -s;
- result.M22 = c;
- result.M31 = x;
- result.M32 = y;
-
- return result;
- }
+ => Impl.CreateRotation(radians, centerPoint).AsM3x2();
/// Creates a scaling matrix from the specified vector scale.
/// The scale to use.
/// The scaling matrix.
public static Matrix3x2 CreateScale(Vector2 scales)
- {
- Matrix3x2 result = Identity;
-
- result.M11 = scales.X;
- result.M22 = scales.Y;
-
- return result;
- }
+ => Impl.CreateScale(scales).AsM3x2();
/// Creates a scaling matrix from the specified X and Y components.
/// The value to scale by on the X axis.
/// The value to scale by on the Y axis.
/// The scaling matrix.
public static Matrix3x2 CreateScale(float xScale, float yScale)
- {
- Matrix3x2 result = Identity;
-
- result.M11 = xScale;
- result.M22 = yScale;
-
- return result;
- }
+ => Impl.CreateScale(xScale, yScale).AsM3x2();
/// Creates a scaling matrix that is offset by a given center point.
/// The value to scale by on the X axis.
@@ -405,87 +206,34 @@ public static Matrix3x2 CreateScale(float xScale, float yScale)
/// The center point.
/// The scaling matrix.
public static Matrix3x2 CreateScale(float xScale, float yScale, Vector2 centerPoint)
- {
- Matrix3x2 result = Identity;
-
- float tx = centerPoint.X * (1 - xScale);
- float ty = centerPoint.Y * (1 - yScale);
-
- result.M11 = xScale;
- result.M22 = yScale;
- result.M31 = tx;
- result.M32 = ty;
-
- return result;
- }
+ => Impl.CreateScale(xScale, yScale, centerPoint).AsM3x2();
/// Creates a scaling matrix from the specified vector scale with an offset from the specified center point.
/// The scale to use.
/// The center offset.
/// The scaling matrix.
public static Matrix3x2 CreateScale(Vector2 scales, Vector2 centerPoint)
- {
- Matrix3x2 result = Identity;
-
- float tx = centerPoint.X * (1 - scales.X);
- float ty = centerPoint.Y * (1 - scales.Y);
-
- result.M11 = scales.X;
- result.M22 = scales.Y;
- result.M31 = tx;
- result.M32 = ty;
-
- return result;
- }
+ => Impl.CreateScale(scales, centerPoint).AsM3x2();
/// Creates a scaling matrix that scales uniformly with the given scale.
/// The uniform scale to use.
/// The scaling matrix.
public static Matrix3x2 CreateScale(float scale)
- {
- Matrix3x2 result = Identity;
-
- result.M11 = scale;
- result.M22 = scale;
-
- return result;
- }
+ => Impl.CreateScale(scale).AsM3x2();
/// Creates a scaling matrix that scales uniformly with the specified scale with an offset from the specified center.
/// The uniform scale to use.
/// The center offset.
/// The scaling matrix.
public static Matrix3x2 CreateScale(float scale, Vector2 centerPoint)
- {
- Matrix3x2 result = Identity;
-
- float tx = centerPoint.X * (1 - scale);
- float ty = centerPoint.Y * (1 - scale);
-
- result.M11 = scale;
- result.M22 = scale;
- result.M31 = tx;
- result.M32 = ty;
-
- return result;
- }
+ => Impl.CreateScale(scale, centerPoint).AsM3x2();
/// Creates a skew matrix from the specified angles in radians.
/// The X angle, in radians.
/// The Y angle, in radians.
/// The skew matrix.
public static Matrix3x2 CreateSkew(float radiansX, float radiansY)
- {
- Matrix3x2 result = Identity;
-
- float xTan = MathF.Tan(radiansX);
- float yTan = MathF.Tan(radiansY);
-
- result.M12 = yTan;
- result.M21 = xTan;
-
- return result;
- }
+ => Impl.CreateSkew(radiansX, radiansY).AsM3x2();
/// Creates a skew matrix from the specified angles in radians and a center point.
/// The X angle, in radians.
@@ -493,50 +241,20 @@ public static Matrix3x2 CreateSkew(float radiansX, float radiansY)
/// The center point.
/// The skew matrix.
public static Matrix3x2 CreateSkew(float radiansX, float radiansY, Vector2 centerPoint)
- {
- Matrix3x2 result = Identity;
-
- float xTan = MathF.Tan(radiansX);
- float yTan = MathF.Tan(radiansY);
-
- float tx = -centerPoint.Y * xTan;
- float ty = -centerPoint.X * yTan;
-
- result.M12 = yTan;
- result.M21 = xTan;
-
- result.M31 = tx;
- result.M32 = ty;
-
- return result;
- }
+ => Impl.CreateSkew(radiansX, radiansY, centerPoint).AsM3x2();
/// Creates a translation matrix from the specified 2-dimensional vector.
/// The translation position.
/// The translation matrix.
public static Matrix3x2 CreateTranslation(Vector2 position)
- {
- Matrix3x2 result = Identity;
-
- result.M31 = position.X;
- result.M32 = position.Y;
-
- return result;
- }
+ => Impl.CreateTranslation(position).AsM3x2();
/// Creates a translation matrix from the specified X and Y components.
/// The X position.
/// The Y position.
/// The translation matrix.
public static Matrix3x2 CreateTranslation(float xPosition, float yPosition)
- {
- Matrix3x2 result = Identity;
-
- result.M31 = xPosition;
- result.M32 = yPosition;
-
- return result;
- }
+ => Impl.CreateTranslation(xPosition, yPosition).AsM3x2();
/// Tries to invert the specified matrix. The return value indicates whether the operation succeeded.
/// The matrix to invert.
@@ -544,26 +262,8 @@ public static Matrix3x2 CreateTranslation(float xPosition, float yPosition)
/// if was converted successfully; otherwise, .
public static bool Invert(Matrix3x2 matrix, out Matrix3x2 result)
{
- float det = (matrix.M11 * matrix.M22) - (matrix.M21 * matrix.M12);
-
- if (MathF.Abs(det) < float.Epsilon)
- {
- result = new Matrix3x2(float.NaN, float.NaN, float.NaN, float.NaN, float.NaN, float.NaN);
- return false;
- }
-
- float invDet = 1.0f / det;
-
- result.M11 = matrix.M22 * invDet;
- result.M12 = -matrix.M12 * invDet;
-
- result.M21 = -matrix.M21 * invDet;
- result.M22 = matrix.M11 * invDet;
-
- result.M31 = (matrix.M21 * matrix.M32 - matrix.M31 * matrix.M22) * invDet;
- result.M32 = (matrix.M31 * matrix.M12 - matrix.M11 * matrix.M32) * invDet;
-
- return true;
+ Unsafe.SkipInit(out result);
+ return Impl.Invert(in matrix.AsImpl(), out result.AsImpl());
}
/// Performs a linear interpolation from one matrix to a second matrix based on a value that specifies the weighting of the second matrix.
@@ -571,24 +271,9 @@ public static bool Invert(Matrix3x2 matrix, out Matrix3x2 result)
/// The second matrix.
/// The relative weighting of .
/// The interpolated matrix.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Matrix3x2 Lerp(Matrix3x2 matrix1, Matrix3x2 matrix2, float amount)
- {
- Matrix3x2 result;
-
- // First row
- result.M11 = matrix1.M11 + (matrix2.M11 - matrix1.M11) * amount;
- result.M12 = matrix1.M12 + (matrix2.M12 - matrix1.M12) * amount;
-
- // Second row
- result.M21 = matrix1.M21 + (matrix2.M21 - matrix1.M21) * amount;
- result.M22 = matrix1.M22 + (matrix2.M22 - matrix1.M22) * amount;
-
- // Third row
- result.M31 = matrix1.M31 + (matrix2.M31 - matrix1.M31) * amount;
- result.M32 = matrix1.M32 + (matrix2.M32 - matrix1.M32) * amount;
-
- return result;
- }
+ => Impl.Lerp(in matrix1.AsImpl(), in matrix2.AsImpl(), amount).AsM3x2();
/// Multiplies two matrices together to compute the product.
/// The first matrix.
@@ -596,9 +281,7 @@ public static Matrix3x2 Lerp(Matrix3x2 matrix1, Matrix3x2 matrix2, float amount)
/// The product matrix.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Matrix3x2 Multiply(Matrix3x2 value1, Matrix3x2 value2)
- {
- return value1 * value2;
- }
+ => (value1.AsImpl() * value2.AsImpl()).AsM3x2();
/// Multiplies a matrix by a float to compute the product.
/// The matrix to scale.
@@ -606,18 +289,14 @@ public static Matrix3x2 Multiply(Matrix3x2 value1, Matrix3x2 value2)
/// The scaled matrix.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Matrix3x2 Multiply(Matrix3x2 value1, float value2)
- {
- return value1 * value2;
- }
+ => (value1.AsImpl() * value2).AsM3x2();
/// Negates the specified matrix by multiplying all its values by -1.
/// The matrix to negate.
/// The negated matrix.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Matrix3x2 Negate(Matrix3x2 value)
- {
- return -value;
- }
+ => (-value.AsImpl()).AsM3x2();
/// Subtracts each element in a second matrix from its corresponding element in a first matrix.
/// The first matrix.
@@ -625,9 +304,7 @@ public static Matrix3x2 Negate(Matrix3x2 value)
/// The matrix containing the values that result from subtracting each element in from its corresponding element in .
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Matrix3x2 Subtract(Matrix3x2 value1, Matrix3x2 value2)
- {
- return value1 - value2;
- }
+ => (value1.AsImpl() - value2.AsImpl()).AsM3x2();
/// Returns a value that indicates whether this instance and a specified object are equal.
/// The object to compare with the current instance.
@@ -635,9 +312,7 @@ public static Matrix3x2 Subtract(Matrix3x2 value1, Matrix3x2 value2)
/// The current instance and are equal if is a object and the corresponding elements of each matrix are equal.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override readonly bool Equals([NotNullWhen(true)] object? obj)
- {
- return (obj is Matrix3x2 other) && Equals(other);
- }
+ => AsROImpl().Equals(obj);
/// Returns a value that indicates whether this instance and another 3x2 matrix are equal.
/// The other matrix.
@@ -645,65 +320,25 @@ public override readonly bool Equals([NotNullWhen(true)] object? obj)
/// Two matrices are equal if all their corresponding elements are equal.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly bool Equals(Matrix3x2 other)
- {
- // This function needs to account for floating-point equality around NaN
- // and so must behave equivalently to the underlying float/double.Equals
-
- if (Vector128.IsHardwareAccelerated)
- {
- // We'll two two overlapping comparisons. The first gets M11, M12, M21, and M22
- // The second will get M21, M22, M31, and M32. This is more efficient overall.
-
- return Vector128.LoadUnsafe(ref Unsafe.AsRef(in M11)).Equals(Vector128.LoadUnsafe(ref other.M11))
- && Vector128.LoadUnsafe(ref Unsafe.AsRef(in M21)).Equals(Vector128.LoadUnsafe(ref other.M21));
-
- }
-
- return SoftwareFallback(in this, other);
-
- static bool SoftwareFallback(in Matrix3x2 self, Matrix3x2 other)
- {
- return self.M11.Equals(other.M11) && self.M22.Equals(other.M22) // Check diagonal element first for early out.
- && self.M12.Equals(other.M12) && self.M21.Equals(other.M21)
- && self.M31.Equals(other.M31) && self.M32.Equals(other.M32);
- }
- }
+ => AsROImpl().Equals(in other.AsImpl());
/// Calculates the determinant for this matrix.
/// The determinant.
/// The determinant is calculated by expanding the matrix with a third column whose values are (0,0,1).
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly float GetDeterminant()
- {
- // There isn't actually any such thing as a determinant for a non-square matrix,
- // but this 3x2 type is really just an optimization of a 3x3 where we happen to
- // know the rightmost column is always (0, 0, 1). So we expand to 3x3 format:
- //
- // [ M11, M12, 0 ]
- // [ M21, M22, 0 ]
- // [ M31, M32, 1 ]
- //
- // Sum the diagonal products:
- // (M11 * M22 * 1) + (M12 * 0 * M31) + (0 * M21 * M32)
- //
- // Subtract the opposite diagonal products:
- // (M31 * M22 * 0) + (M32 * 0 * M11) + (1 * M21 * M12)
- //
- // Collapse out the constants and oh look, this is just a 2x2 determinant!
-
- return (M11 * M22) - (M21 * M12);
- }
+ => AsROImpl().GetDeterminant();
/// Returns the hash code for this instance.
/// The hash code.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override readonly int GetHashCode()
- {
- return HashCode.Combine(M11, M12, M21, M22, M31, M32);
- }
+ => AsROImpl().GetHashCode();
/// Returns a string that represents this matrix.
/// The string representation of this matrix.
/// The numeric values in the returned string are formatted by using the conventions of the current culture. For example, for the en-US culture, the returned string might appear as { {M11:1.1 M12:1.2} {M21:2.1 M22:2.2} {M31:3.1 M32:3.2} }.
- public override readonly string ToString() =>
- $"{{ {{M11:{M11} M12:{M12}}} {{M21:{M21} M22:{M22}}} {{M31:{M31} M32:{M32}}} }}";
+ public override readonly string ToString()
+ => $"{{ {{M11:{M11} M12:{M12}}} {{M21:{M21} M22:{M22}}} {{M31:{M31} M32:{M32}}} }}";
}
}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Matrix4x4.Impl.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Matrix4x4.Impl.cs
new file mode 100644
index 00000000000000..f0a3b4f2128a57
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Matrix4x4.Impl.cs
@@ -0,0 +1,1620 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.Arm;
+using System.Runtime.Intrinsics.X86;
+
+namespace System.Numerics
+{
+ public partial struct Matrix4x4
+ {
+ // See Matrix4x4.cs for an explanation of why this file/type exists
+ //
+ // Note that we use some particular patterns below, such as defining a result
+ // and assigning the fields directly rather than using the object initializer
+ // syntax. We do this because it saves roughly 8-bytes of IL per method which
+ // in turn helps improve inlining chances.
+
+ // TODO: Vector3 is "inefficient" and we'd be better off taking Vector4 or Vector128
+
+ internal const uint RowCount = 4;
+ internal const uint ColumnCount = 4;
+
+ [UnscopedRef]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal ref Impl AsImpl() => ref Unsafe.As(ref this);
+
+ [UnscopedRef]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly ref readonly Impl AsROImpl() => ref Unsafe.As(ref Unsafe.AsRef(in this));
+
+ internal struct Impl : IEquatable
+ {
+ [UnscopedRef]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ref Matrix4x4 AsM4x4() => ref Unsafe.As(ref this);
+
+ private const float BillboardEpsilon = 1e-4f;
+ private const float BillboardMinAngle = 1.0f - (0.1f * (MathF.PI / 180.0f)); // 0.1 degrees
+ private const float DecomposeEpsilon = 0.0001f;
+
+ public Vector4 X;
+ public Vector4 Y;
+ public Vector4 Z;
+ public Vector4 W;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Init(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)
+ {
+ X = new Vector4(m11, m12, m13, m14);
+ Y = new Vector4(m21, m22, m23, m24);
+ Z = new Vector4(m31, m32, m33, m34);
+ W = new Vector4(m41, m42, m43, m44);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Init(in Matrix3x2.Impl value)
+ {
+ X = new Vector4(value.X, 0, 0);
+ Y = new Vector4(value.Y, 0, 0);
+ Z = Vector4.UnitZ;
+ W = new Vector4(value.Z, 0, 1);
+ }
+
+ public static Impl Identity
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ Impl result;
+
+ result.X = Vector4.UnitX;
+ result.Y = Vector4.UnitY;
+ result.Z = Vector4.UnitZ;
+ result.W = Vector4.UnitW;
+
+ return result;
+ }
+ }
+
+ public float this[int row, int column]
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get
+ {
+ if ((uint)row >= RowCount)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+ }
+
+ return Unsafe.Add(ref Unsafe.AsRef(in this.X), row)[column];
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set
+ {
+ if ((uint)row >= RowCount)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+ }
+ Unsafe.Add(ref this.X, row)[column] = value;
+ }
+ }
+
+ public readonly bool IsIdentity
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ return (X == Vector4.UnitX)
+ && (Y == Vector4.UnitY)
+ && (Z == Vector4.UnitZ)
+ && (W == Vector4.UnitW);
+ }
+ }
+
+ public Vector3 Translation
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => new Vector3(W.X, W.Y, W.Z);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set
+ {
+ W = new Vector4(value, W.W);
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl operator +(in Impl left, in Impl right)
+ {
+ Impl result;
+
+ result.X = left.X + right.X;
+ result.Y = left.Y + right.Y;
+ result.Z = left.Z + right.Z;
+ result.W = left.W + right.W;
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool operator ==(in Impl left, in Impl right)
+ {
+ return (left.X == right.X)
+ && (left.Y == right.Y)
+ && (left.Z == right.Z)
+ && (left.W == right.W);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool operator !=(in Impl left, in Impl right)
+ {
+ return (left.X != right.X)
+ || (left.Y != right.Y)
+ || (left.Z != right.Z)
+ || (left.W != right.W);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl operator *(in Impl left, in Impl right)
+ {
+ Impl result;
+
+ // result.X = Transform(left.X, in right);
+ result.X = right.X * left.X.X;
+ result.X += right.Y * left.X.Y;
+ result.X += right.Z * left.X.Z;
+ result.X += right.W * left.X.W;
+
+ // result.Y = Transform(left.Y, in right);
+ result.Y = right.X * left.Y.X;
+ result.Y += right.Y * left.Y.Y;
+ result.Y += right.Z * left.Y.Z;
+ result.Y += right.W * left.Y.W;
+
+ // result.Z = Transform(left.Z, in right);
+ result.Z = right.X * left.Z.X;
+ result.Z += right.Y * left.Z.Y;
+ result.Z += right.Z * left.Z.Z;
+ result.Z += right.W * left.Z.W;
+
+ // result.W = Transform(left.W, in right);
+ result.W = right.X * left.W.X;
+ result.W += right.Y * left.W.Y;
+ result.W += right.Z * left.W.Z;
+ result.W += right.W * left.W.W;
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl operator *(in Impl left, float right)
+ {
+ Impl result;
+
+ result.X = left.X * right;
+ result.Y = left.Y * right;
+ result.Z = left.Z * right;
+ result.W = left.W * right;
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl operator -(in Impl left, in Impl right)
+ {
+ Impl result;
+
+ result.X = left.X - right.X;
+ result.Y = left.Y - right.Y;
+ result.Z = left.Z - right.Z;
+ result.W = left.W - right.W;
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl operator -(in Impl value)
+ {
+ Impl result;
+
+ result.X = -value.X;
+ result.Y = -value.Y;
+ result.Z = -value.Z;
+ result.W = -value.W;
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateBillboard(in Vector3 objectPosition, in Vector3 cameraPosition, in Vector3 cameraUpVector, in Vector3 cameraForwardVector)
+ {
+ Vector3 axisZ = objectPosition - cameraPosition;
+ float norm = axisZ.LengthSquared();
+
+ if (norm < BillboardEpsilon)
+ {
+ axisZ = -cameraForwardVector;
+ }
+ else
+ {
+ axisZ = Vector3.Multiply(axisZ, 1.0f / MathF.Sqrt(norm));
+ }
+
+ Vector3 axisX = Vector3.Normalize(Vector3.Cross(cameraUpVector, axisZ));
+ Vector3 axisY = Vector3.Cross(axisZ, axisX);
+
+ Impl result;
+
+ result.X = new Vector4(axisX, 0);
+ result.Y = new Vector4(axisY, 0);
+ result.Z = new Vector4(axisZ, 0);
+ result.W = new Vector4(objectPosition, 1);
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateConstrainedBillboard(in Vector3 objectPosition, in Vector3 cameraPosition, in Vector3 rotateAxis, in Vector3 cameraForwardVector, in Vector3 objectForwardVector)
+ {
+ // Treat the case when object and camera positions are too close.
+ Vector3 faceDir = objectPosition - cameraPosition;
+ float norm = faceDir.LengthSquared();
+
+ if (norm < BillboardEpsilon)
+ {
+ faceDir = -cameraForwardVector;
+ }
+ else
+ {
+ faceDir = Vector3.Multiply(faceDir, (1.0f / MathF.Sqrt(norm)));
+ }
+
+ Vector3 axisY = rotateAxis;
+
+ // Treat the case when angle between faceDir and rotateAxis is too close to 0.
+ float dot = Vector3.Dot(axisY, faceDir);
+
+ if (MathF.Abs(dot) > BillboardMinAngle)
+ {
+ faceDir = objectForwardVector;
+
+ // Make sure passed values are useful for compute.
+ dot = Vector3.Dot(axisY, faceDir);
+
+ if (MathF.Abs(dot) > BillboardMinAngle)
+ {
+ faceDir = (MathF.Abs(axisY.Z) > BillboardMinAngle) ? Vector3.UnitX : new Vector3(0, 0, -1);
+ }
+ }
+
+ Vector3 axisX = Vector3.Normalize(Vector3.Cross(axisY, faceDir));
+ Vector3 axisZ = Vector3.Normalize(Vector3.Cross(axisX, axisY));
+
+ Impl result;
+
+ result.X = new Vector4(axisX, 0);
+ result.Y = new Vector4(axisY, 0);
+ result.Z = new Vector4(axisZ, 0);
+ result.W = new Vector4(objectPosition, 1);
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateFromAxisAngle(in Vector3 axis, float angle)
+ {
+ // a: angle
+ // x, y, z: unit vector for axis.
+ //
+ // Rotation matrix M can compute by using below equation.
+ //
+ // T T
+ // M = uu + (cos a)( I-uu ) + (sin a)S
+ //
+ // Where:
+ //
+ // u = ( x, y, z )
+ //
+ // [ 0 -z y ]
+ // S = [ z 0 -x ]
+ // [ -y x 0 ]
+ //
+ // [ 1 0 0 ]
+ // I = [ 0 1 0 ]
+ // [ 0 0 1 ]
+ //
+ //
+ // [ xx+cosa*(1-xx) yx-cosa*yx-sina*z zx-cosa*xz+sina*y ]
+ // M = [ xy-cosa*yx+sina*z yy+cosa(1-yy) yz-cosa*yz-sina*x ]
+ // [ zx-cosa*zx-sina*y zy-cosa*zy+sina*x zz+cosa*(1-zz) ]
+ //
+
+ float x = axis.X;
+ float y = axis.Y;
+ float z = axis.Z;
+
+ float sa = MathF.Sin(angle);
+ float ca = MathF.Cos(angle);
+
+ float xx = x * x;
+ float yy = y * y;
+ float zz = z * z;
+
+ float xy = x * y;
+ float xz = x * z;
+ float yz = y * z;
+
+ Impl result;
+
+ result.X = new Vector4(
+ xx + ca * (1.0f - xx),
+ xy - ca * xy + sa * z,
+ xz - ca * xz - sa * y,
+ 0
+ );
+ result.Y = new Vector4(
+ xy - ca * xy - sa * z,
+ yy + ca * (1.0f - yy),
+ yz - ca * yz + sa * x,
+ 0
+ );
+ result.Z = new Vector4(
+ xz - ca * xz + sa * y,
+ yz - ca * yz - sa * x,
+ zz + ca * (1.0f - zz),
+ 0
+ );
+ result.W = Vector4.UnitW;
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateFromQuaternion(in Quaternion quaternion)
+ {
+ float xx = quaternion.X * quaternion.X;
+ float yy = quaternion.Y * quaternion.Y;
+ float zz = quaternion.Z * quaternion.Z;
+
+ float xy = quaternion.X * quaternion.Y;
+ float wz = quaternion.Z * quaternion.W;
+ float xz = quaternion.Z * quaternion.X;
+ float wy = quaternion.Y * quaternion.W;
+ float yz = quaternion.Y * quaternion.Z;
+ float wx = quaternion.X * quaternion.W;
+
+ Impl result;
+
+ result.X = new Vector4(
+ 1.0f - 2.0f * (yy + zz),
+ 2.0f * (xy + wz),
+ 2.0f * (xz - wy),
+ 0
+ );
+ result.Y = new Vector4(
+ 2.0f * (xy - wz),
+ 1.0f - 2.0f * (zz + xx),
+ 2.0f * (yz + wx),
+ 0
+ );
+ result.Z = new Vector4(
+ 2.0f * (xz + wy),
+ 2.0f * (yz - wx),
+ 1.0f - 2.0f * (yy + xx),
+ 0
+ );
+ result.W = Vector4.UnitW;
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateFromYawPitchRoll(float yaw, float pitch, float roll)
+ {
+ Quaternion q = Quaternion.CreateFromYawPitchRoll(yaw, pitch, roll);
+ return CreateFromQuaternion(q);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateLookAt(in Vector3 cameraPosition, in Vector3 cameraTarget, in Vector3 cameraUpVector)
+ {
+ Vector3 axisZ = Vector3.Normalize(cameraPosition - cameraTarget);
+ Vector3 axisX = Vector3.Normalize(Vector3.Cross(cameraUpVector, axisZ));
+ Vector3 axisY = Vector3.Cross(axisZ, axisX);
+
+ Impl result;
+
+ result.X = new Vector4(
+ axisX.X,
+ axisY.X,
+ axisZ.X,
+ 0
+ );
+ result.Y = new Vector4(
+ axisX.Y,
+ axisY.Y,
+ axisZ.Y,
+ 0
+ );
+ result.Z = new Vector4(
+ axisX.Z,
+ axisY.Z,
+ axisZ.Z,
+ 0
+ );
+ result.W = new Vector4(
+ -Vector3.Dot(axisX, cameraPosition),
+ -Vector3.Dot(axisY, cameraPosition),
+ -Vector3.Dot(axisZ, cameraPosition),
+ 1
+ );
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateOrthographic(float width, float height, float zNearPlane, float zFarPlane)
+ {
+ Impl result;
+
+ result.X = new Vector4(2.0f / width, 0, 0, 0);
+ result.Y = new Vector4(0, 2.0f / height, 0, 0);
+ result.Z = new Vector4(0, 0, 1.0f / (zNearPlane - zFarPlane), 0);
+ result.W = new Vector4(0, 0, zNearPlane / (zNearPlane - zFarPlane), 1);
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateOrthographicOffCenter(float left, float right, float bottom, float top, float zNearPlane, float zFarPlane)
+ {
+ Impl result;
+
+ result.X = new Vector4(2.0f / (right - left), 0, 0, 0);
+ result.Y = new Vector4(0, 2.0f / (top - bottom), 0, 0);
+ result.Z = new Vector4(0, 0, 1.0f / (zNearPlane - zFarPlane), 0);
+ result.W = new Vector4(
+ (left + right) / (left - right),
+ (top + bottom) / (bottom - top),
+ zNearPlane / (zNearPlane - zFarPlane),
+ 1
+ );
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreatePerspective(float width, float height, float nearPlaneDistance, float farPlaneDistance)
+ {
+ ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(nearPlaneDistance, 0.0f);
+ ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(farPlaneDistance, 0.0f);
+ ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(nearPlaneDistance, farPlaneDistance);
+
+ float negFarRange = float.IsPositiveInfinity(farPlaneDistance) ? -1.0f : farPlaneDistance / (nearPlaneDistance - farPlaneDistance);
+
+ Impl result;
+
+ result.X = new Vector4(2.0f * nearPlaneDistance / width, 0, 0, 0);
+ result.Y = new Vector4(0, 2.0f * nearPlaneDistance / height, 0, 0);
+ result.Z = new Vector4(0, 0, negFarRange, -1.0f);
+ result.W = new Vector4(0, 0, nearPlaneDistance * negFarRange, 0);
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreatePerspectiveFieldOfView(float fieldOfView, float aspectRatio, float nearPlaneDistance, float farPlaneDistance)
+ {
+ ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(fieldOfView, 0.0f);
+ ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(fieldOfView, MathF.PI);
+
+ ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(nearPlaneDistance, 0.0f);
+ ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(farPlaneDistance, 0.0f);
+ ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(nearPlaneDistance, farPlaneDistance);
+
+ float scaleY = 1.0f / MathF.Tan(fieldOfView * 0.5f);
+ float scaleX = scaleY / aspectRatio;
+ float negFarRange = float.IsPositiveInfinity(farPlaneDistance) ? -1.0f : farPlaneDistance / (nearPlaneDistance - farPlaneDistance);
+
+ Impl result;
+
+ result.X = new Vector4(scaleX, 0, 0, 0);
+ result.Y = new Vector4(0, scaleY, 0, 0);
+ result.Z = new Vector4(0, 0, negFarRange, -1.0f);
+ result.W = new Vector4(0, 0, nearPlaneDistance * negFarRange, 0);
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreatePerspectiveOffCenter(float left, float right, float bottom, float top, float nearPlaneDistance, float farPlaneDistance)
+ {
+ ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(nearPlaneDistance, 0.0f);
+ ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(farPlaneDistance, 0.0f);
+ ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(nearPlaneDistance, farPlaneDistance);
+
+ float negFarRange = float.IsPositiveInfinity(farPlaneDistance) ? -1.0f : farPlaneDistance / (nearPlaneDistance - farPlaneDistance);
+
+ Impl result;
+
+ result.X = new Vector4(2.0f * nearPlaneDistance / (right - left), 0, 0, 0);
+ result.Y = new Vector4(0, 2.0f * nearPlaneDistance / (top - bottom), 0, 0);
+ result.Z = new Vector4(
+ (left + right) / (right - left),
+ (top + bottom) / (top - bottom),
+ negFarRange,
+ -1.0f
+ );
+ result.W = new Vector4(0, 0, nearPlaneDistance * negFarRange, 0);
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateReflection(in Plane value)
+ {
+ Plane p = Plane.Normalize(value);
+ Vector3 f = p.Normal * -2.0f;
+
+ Impl result;
+
+ result.X = new Vector4(f * p.Normal.X, 0) + Vector4.UnitX;
+ result.Y = new Vector4(f * p.Normal.Y, 0) + Vector4.UnitY;
+ result.Z = new Vector4(f * p.Normal.Z, 0) + Vector4.UnitZ;
+ result.W = new Vector4(f * p.D, 1);
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateRotationX(float radians)
+ {
+ float c = MathF.Cos(radians);
+ float s = MathF.Sin(radians);
+
+ // [ 1 0 0 0 ]
+ // [ 0 c s 0 ]
+ // [ 0 -s c 0 ]
+ // [ 0 0 0 1 ]
+
+ Impl result;
+
+ result.X = Vector4.UnitX;
+ result.Y = new Vector4(0, c, s, 0);
+ result.Z = new Vector4(0, -s, c, 0);
+ result.W = Vector4.UnitW;
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateRotationX(float radians, in Vector3 centerPoint)
+ {
+ float c = MathF.Cos(radians);
+ float s = MathF.Sin(radians);
+
+ float y = centerPoint.Y * (1 - c) + centerPoint.Z * s;
+ float z = centerPoint.Z * (1 - c) - centerPoint.Y * s;
+
+ // [ 1 0 0 0 ]
+ // [ 0 c s 0 ]
+ // [ 0 -s c 0 ]
+ // [ 0 y z 1 ]
+
+ Impl result;
+
+ result.X = Vector4.UnitX;
+ result.Y = new Vector4(0, c, s, 0);
+ result.Z = new Vector4(0, -s, c, 0);
+ result.W = new Vector4(0, y, z, 1);
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateRotationY(float radians)
+ {
+ float c = MathF.Cos(radians);
+ float s = MathF.Sin(radians);
+
+ // [ c 0 -s 0 ]
+ // [ 0 1 0 0 ]
+ // [ s 0 c 0 ]
+ // [ 0 0 0 1 ]
+
+ Impl result;
+
+ result.X = new Vector4(c, 0, -s, 0);
+ result.Y = Vector4.UnitY;
+ result.Z = new Vector4(s, 0, c, 0);
+ result.W = Vector4.UnitW;
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateRotationY(float radians, in Vector3 centerPoint)
+ {
+ float c = MathF.Cos(radians);
+ float s = MathF.Sin(radians);
+
+ float x = centerPoint.X * (1 - c) - centerPoint.Z * s;
+ float z = centerPoint.Z * (1 - c) + centerPoint.X * s;
+
+ // [ c 0 -s 0 ]
+ // [ 0 1 0 0 ]
+ // [ s 0 c 0 ]
+ // [ x 0 z 1 ]
+
+ Impl result;
+
+ result.X = new Vector4(c, 0, -s, 0);
+ result.Y = Vector4.UnitY;
+ result.Z = new Vector4(s, 0, c, 0);
+ result.W = new Vector4(x, 0, z, 1);
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateRotationZ(float radians)
+ {
+
+ float c = MathF.Cos(radians);
+ float s = MathF.Sin(radians);
+
+ // [ c s 0 0 ]
+ // [ -s c 0 0 ]
+ // [ 0 0 1 0 ]
+ // [ 0 0 0 1 ]
+
+ Impl result;
+
+ result.X = new Vector4( c, s, 0, 0);
+ result.Y = new Vector4(-s, c, 0, 0);
+ result.Z = Vector4.UnitZ;
+ result.W = Vector4.UnitW;
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateRotationZ(float radians, in Vector3 centerPoint)
+ {
+ float c = MathF.Cos(radians);
+ float s = MathF.Sin(radians);
+
+ float x = centerPoint.X * (1 - c) + centerPoint.Y * s;
+ float y = centerPoint.Y * (1 - c) - centerPoint.X * s;
+
+ // [ c s 0 0 ]
+ // [ -s c 0 0 ]
+ // [ 0 0 1 0 ]
+ // [ x y 0 1 ]
+
+ Impl result;
+
+ result.X = new Vector4( c, s, 0, 0);
+ result.Y = new Vector4(-s, c, 0, 0);
+ result.Z = Vector4.UnitZ;
+ result.W = new Vector4(x, y, 0, 1);
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateScale(float scaleX, float scaleY, float scaleZ)
+ {
+ Impl result;
+
+ result.X = new Vector4(scaleX, 0, 0, 0);
+ result.Y = new Vector4(0, scaleY, 0, 0);
+ result.Z = new Vector4(0, 0, scaleZ, 0);
+ result.W = Vector4.UnitW;
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateScale(float scaleX, float scaleY, float scaleZ, in Vector3 centerPoint)
+ {
+ Impl result;
+
+ result.X = new Vector4(scaleX, 0, 0, 0);
+ result.Y = new Vector4(0, scaleY, 0, 0);
+ result.Z = new Vector4(0, 0, scaleZ, 0);
+ result.W = new Vector4(centerPoint * (Vector3.One - new Vector3(scaleX, scaleY, scaleZ)), 1);
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateScale(in Vector3 scales)
+ {
+ Impl result;
+
+ result.X = new Vector4(scales.X, 0, 0, 0);
+ result.Y = new Vector4(0, scales.Y, 0, 0);
+ result.Z = new Vector4(0, 0, scales.Z, 0);
+ result.W = Vector4.UnitW;
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateScale(in Vector3 scales, in Vector3 centerPoint)
+ {
+ Impl result;
+
+ result.X = new Vector4(scales.X, 0, 0, 0);
+ result.Y = new Vector4(0, scales.Y, 0, 0);
+ result.Z = new Vector4(0, 0, scales.Z, 0);
+ result.W = new Vector4(centerPoint * (Vector3.One - scales), 1);
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateScale(float scale)
+ {
+ Impl result;
+
+ result.X = new Vector4(scale, 0, 0, 0);
+ result.Y = new Vector4(0, scale, 0, 0);
+ result.Z = new Vector4(0, 0, scale, 0);
+ result.W = Vector4.UnitW;
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateScale(float scale, in Vector3 centerPoint)
+ {
+ Impl result;
+
+ result.X = new Vector4(scale, 0, 0, 0);
+ result.Y = new Vector4(0, scale, 0, 0);
+ result.Z = new Vector4(0, 0, scale, 0);
+ result.W = new Vector4(centerPoint * (Vector3.One - new Vector3(scale)), 1);
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateShadow(in Vector3 lightDirection, in Plane plane)
+ {
+ Plane p = Plane.Normalize(plane);
+ float dot = Vector3.Dot(lightDirection, p.Normal);
+
+ Vector3 normal = -p.Normal;
+
+ Impl result;
+
+ result.X = new Vector4(lightDirection * normal.X, 0) + new Vector4(dot, 0, 0, 0);
+ result.Y = new Vector4(lightDirection * normal.Y, 0) + new Vector4(0, dot, 0, 0);
+ result.Z = new Vector4(lightDirection * normal.Z, 0) + new Vector4(0, 0, dot, 0);
+ result.W = new Vector4(lightDirection * -p.D, dot);
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateTranslation(in Vector3 position)
+ {
+ Impl result;
+
+ result.X = Vector4.UnitX;
+ result.Y = Vector4.UnitY;
+ result.Z = Vector4.UnitZ;
+ result.W = new Vector4(position, 1);
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateTranslation(float positionX, float positionY, float positionZ)
+ {
+ Impl result;
+
+ result.X = Vector4.UnitX;
+ result.Y = Vector4.UnitY;
+ result.Z = Vector4.UnitZ;
+ result.W = new Vector4(positionX, positionY, positionZ, 1);
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl CreateWorld(in Vector3 position, in Vector3 forward, in Vector3 up)
+ {
+ Vector3 axisZ = Vector3.Normalize(-forward);
+ Vector3 axisX = Vector3.Normalize(Vector3.Cross(up, axisZ));
+ Vector3 axisY = Vector3.Cross(axisZ, axisX);
+
+ Impl result;
+
+ result.X = new Vector4(axisX, 0);
+ result.Y = new Vector4(axisY, 0);
+ result.Z = new Vector4(axisZ, 0);
+ result.W = new Vector4(position, 1);
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe bool Decompose(in Impl matrix, out Vector3 scale, out Quaternion rotation, out Vector3 translation)
+ {
+ bool result = true;
+
+ fixed (Vector3* scaleBase = &scale)
+ {
+ float* pfScales = (float*)scaleBase;
+ float det;
+
+ VectorBasis vectorBasis;
+ Vector3** pVectorBasis = (Vector3**)&vectorBasis;
+
+ Impl matTemp = Identity;
+ CanonicalBasis canonicalBasis = default;
+ Vector3* pCanonicalBasis = &canonicalBasis.Row0;
+
+ canonicalBasis.Row0 = new Vector3(1.0f, 0.0f, 0.0f);
+ canonicalBasis.Row1 = new Vector3(0.0f, 1.0f, 0.0f);
+ canonicalBasis.Row2 = new Vector3(0.0f, 0.0f, 1.0f);
+
+ translation = new Vector3(
+ matrix.W.X,
+ matrix.W.Y,
+ matrix.W.Z);
+
+ pVectorBasis[0] = (Vector3*)&matTemp.X;
+ pVectorBasis[1] = (Vector3*)&matTemp.Y;
+ pVectorBasis[2] = (Vector3*)&matTemp.Z;
+
+ *(pVectorBasis[0]) = new Vector3(matrix.X.X, matrix.X.Y, matrix.X.Z);
+ *(pVectorBasis[1]) = new Vector3(matrix.Y.X, matrix.Y.Y, matrix.Y.Z);
+ *(pVectorBasis[2]) = new Vector3(matrix.Z.X, matrix.Z.Y, matrix.Z.Z);
+
+ scale.X = pVectorBasis[0]->Length();
+ scale.Y = pVectorBasis[1]->Length();
+ scale.Z = pVectorBasis[2]->Length();
+
+ uint a, b, c;
+
+ #region Ranking
+ float x = pfScales[0];
+ float y = pfScales[1];
+ float z = pfScales[2];
+
+ if (x < y)
+ {
+ if (y < z)
+ {
+ a = 2;
+ b = 1;
+ c = 0;
+ }
+ else
+ {
+ a = 1;
+
+ if (x < z)
+ {
+ b = 2;
+ c = 0;
+ }
+ else
+ {
+ b = 0;
+ c = 2;
+ }
+ }
+ }
+ else
+ {
+ if (x < z)
+ {
+ a = 2;
+ b = 0;
+ c = 1;
+ }
+ else
+ {
+ a = 0;
+
+ if (y < z)
+ {
+ b = 2;
+ c = 1;
+ }
+ else
+ {
+ b = 1;
+ c = 2;
+ }
+ }
+ }
+ #endregion
+
+ if (pfScales[a] < DecomposeEpsilon)
+ {
+ *(pVectorBasis[a]) = pCanonicalBasis[a];
+ }
+
+ *pVectorBasis[a] = Vector3.Normalize(*pVectorBasis[a]);
+
+ if (pfScales[b] < DecomposeEpsilon)
+ {
+ uint cc;
+ float fAbsX, fAbsY, fAbsZ;
+
+ fAbsX = MathF.Abs(pVectorBasis[a]->X);
+ fAbsY = MathF.Abs(pVectorBasis[a]->Y);
+ fAbsZ = MathF.Abs(pVectorBasis[a]->Z);
+
+ #region Ranking
+ if (fAbsX < fAbsY)
+ {
+ if (fAbsY < fAbsZ)
+ {
+ cc = 0;
+ }
+ else
+ {
+ if (fAbsX < fAbsZ)
+ {
+ cc = 0;
+ }
+ else
+ {
+ cc = 2;
+ }
+ }
+ }
+ else
+ {
+ if (fAbsX < fAbsZ)
+ {
+ cc = 1;
+ }
+ else
+ {
+ if (fAbsY < fAbsZ)
+ {
+ cc = 1;
+ }
+ else
+ {
+ cc = 2;
+ }
+ }
+ }
+ #endregion
+
+ *pVectorBasis[b] = Vector3.Cross(*pVectorBasis[a], *(pCanonicalBasis + cc));
+ }
+
+ *pVectorBasis[b] = Vector3.Normalize(*pVectorBasis[b]);
+
+ if (pfScales[c] < DecomposeEpsilon)
+ {
+ *pVectorBasis[c] = Vector3.Cross(*pVectorBasis[a], *pVectorBasis[b]);
+ }
+
+ *pVectorBasis[c] = Vector3.Normalize(*pVectorBasis[c]);
+
+ det = matTemp.GetDeterminant();
+
+ // use Kramer's rule to check for handedness of coordinate system
+ if (det < 0.0f)
+ {
+ // switch coordinate system by negating the scale and inverting the basis vector on the x-axis
+ pfScales[a] = -pfScales[a];
+ *pVectorBasis[a] = -(*pVectorBasis[a]);
+
+ det = -det;
+ }
+
+ det -= 1.0f;
+ det *= det;
+
+ if ((DecomposeEpsilon < det))
+ {
+ // Non-SRT matrix encountered
+ rotation = Quaternion.Identity;
+ result = false;
+ }
+ else
+ {
+ // generate the quaternion from the matrix
+ rotation = Quaternion.CreateFromRotationMatrix(Unsafe.As(ref matTemp));
+ }
+ }
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool Invert(in Impl matrix, out Impl result)
+ {
+ // This implementation is based on the DirectX Math Library XMMatrixInverse method
+ // https://github.com/microsoft/DirectXMath/blob/master/Inc/DirectXMathMatrix.inl
+
+ if (Sse.IsSupported)
+ {
+ return SseImpl(in matrix, out result);
+ }
+
+ return SoftwareFallback(in matrix, out result);
+
+ static bool SseImpl(in Impl matrix, out Impl result)
+ {
+ if (!Sse.IsSupported)
+ {
+ // Redundant test so we won't prejit remainder of this method on platforms without SSE.
+ throw new PlatformNotSupportedException();
+ }
+
+ // Load the matrix values into rows
+ Vector128 row1 = matrix.X.AsVector128();
+ Vector128 row2 = matrix.Y.AsVector128();
+ Vector128 row3 = matrix.Z.AsVector128();
+ Vector128 row4 = matrix.W.AsVector128();
+
+ // Transpose the matrix
+ Vector128 vTemp1 = Sse.Shuffle(row1, row2, 0x44); //_MM_SHUFFLE(1, 0, 1, 0)
+ Vector128 vTemp3 = Sse.Shuffle(row1, row2, 0xEE); //_MM_SHUFFLE(3, 2, 3, 2)
+ Vector128 vTemp2 = Sse.Shuffle(row3, row4, 0x44); //_MM_SHUFFLE(1, 0, 1, 0)
+ Vector128 vTemp4 = Sse.Shuffle(row3, row4, 0xEE); //_MM_SHUFFLE(3, 2, 3, 2)
+
+ row1 = Sse.Shuffle(vTemp1, vTemp2, 0x88); //_MM_SHUFFLE(2, 0, 2, 0)
+ row2 = Sse.Shuffle(vTemp1, vTemp2, 0xDD); //_MM_SHUFFLE(3, 1, 3, 1)
+ row3 = Sse.Shuffle(vTemp3, vTemp4, 0x88); //_MM_SHUFFLE(2, 0, 2, 0)
+ row4 = Sse.Shuffle(vTemp3, vTemp4, 0xDD); //_MM_SHUFFLE(3, 1, 3, 1)
+
+ Vector128 V00 = Permute(row3, 0x50); //_MM_SHUFFLE(1, 1, 0, 0)
+ Vector128 V10 = Permute(row4, 0xEE); //_MM_SHUFFLE(3, 2, 3, 2)
+ Vector128 V01 = Permute(row1, 0x50); //_MM_SHUFFLE(1, 1, 0, 0)
+ Vector128 V11 = Permute(row2, 0xEE); //_MM_SHUFFLE(3, 2, 3, 2)
+ Vector128 V02 = Sse.Shuffle(row3, row1, 0x88); //_MM_SHUFFLE(2, 0, 2, 0)
+ Vector128 V12 = Sse.Shuffle(row4, row2, 0xDD); //_MM_SHUFFLE(3, 1, 3, 1)
+
+ Vector128 D0 = V00 * V10;
+ Vector128 D1 = V01 * V11;
+ Vector128 D2 = V02 * V12;
+
+ V00 = Permute(row3, 0xEE); //_MM_SHUFFLE(3, 2, 3, 2)
+ V10 = Permute(row4, 0x50); //_MM_SHUFFLE(1, 1, 0, 0)
+ V01 = Permute(row1, 0xEE); //_MM_SHUFFLE(3, 2, 3, 2)
+ V11 = Permute(row2, 0x50); //_MM_SHUFFLE(1, 1, 0, 0)
+ V02 = Sse.Shuffle(row3, row1, 0xDD); //_MM_SHUFFLE(3, 1, 3, 1)
+ V12 = Sse.Shuffle(row4, row2, 0x88); //_MM_SHUFFLE(2, 0, 2, 0)
+
+ // Note: We use this expansion pattern instead of Fused Multiply Add
+ // in order to support older hardware
+ D0 -= V00 * V10;
+ D1 -= V01 * V11;
+ D2 -= V02 * V12;
+
+ // V11 = D0Y,D0W,D2Y,D2Y
+ V11 = Sse.Shuffle(D0, D2, 0x5D); //_MM_SHUFFLE(1, 1, 3, 1)
+ V00 = Permute(row2, 0x49); //_MM_SHUFFLE(1, 0, 2, 1)
+ V10 = Sse.Shuffle(V11, D0, 0x32); //_MM_SHUFFLE(0, 3, 0, 2)
+ V01 = Permute(row1, 0x12); //_MM_SHUFFLE(0, 1, 0, 2)
+ V11 = Sse.Shuffle(V11, D0, 0x99); //_MM_SHUFFLE(2, 1, 2, 1)
+
+ // V13 = D1Y,D1W,D2W,D2W
+ Vector128 V13 = Sse.Shuffle(D1, D2, 0xFD); //_MM_SHUFFLE(3, 3, 3, 1)
+ V02 = Permute(row4, 0x49); //_MM_SHUFFLE(1, 0, 2, 1)
+ V12 = Sse.Shuffle(V13, D1, 0x32); //_MM_SHUFFLE(0, 3, 0, 2)
+ Vector128 V03 = Permute(row3, 0x12); //_MM_SHUFFLE(0, 1, 0, 2)
+ V13 = Sse.Shuffle(V13, D1, 0x99); //_MM_SHUFFLE(2, 1, 2, 1)
+
+ Vector128 C0 = V00 * V10;
+ Vector128 C2 = V01 * V11;
+ Vector128 C4 = V02 * V12;
+ Vector128 C6 = V03 * V13;
+
+ // V11 = D0X,D0Y,D2X,D2X
+ V11 = Sse.Shuffle(D0, D2, 0x4); //_MM_SHUFFLE(0, 0, 1, 0)
+ V00 = Permute(row2, 0x9e); //_MM_SHUFFLE(2, 1, 3, 2)
+ V10 = Sse.Shuffle(D0, V11, 0x93); //_MM_SHUFFLE(2, 1, 0, 3)
+ V01 = Permute(row1, 0x7b); //_MM_SHUFFLE(1, 3, 2, 3)
+ V11 = Sse.Shuffle(D0, V11, 0x26); //_MM_SHUFFLE(0, 2, 1, 2)
+
+ // V13 = D1X,D1Y,D2Z,D2Z
+ V13 = Sse.Shuffle(D1, D2, 0xa4); //_MM_SHUFFLE(2, 2, 1, 0)
+ V02 = Permute(row4, 0x9e); //_MM_SHUFFLE(2, 1, 3, 2)
+ V12 = Sse.Shuffle(D1, V13, 0x93); //_MM_SHUFFLE(2, 1, 0, 3)
+ V03 = Permute(row3, 0x7b); //_MM_SHUFFLE(1, 3, 2, 3)
+ V13 = Sse.Shuffle(D1, V13, 0x26); //_MM_SHUFFLE(0, 2, 1, 2)
+
+ C0 -= V00 * V10;
+ C2 -= V01 * V11;
+ C4 -= V02 * V12;
+ C6 -= V03 * V13;
+
+ V00 = Permute(row2, 0x33); //_MM_SHUFFLE(0, 3, 0, 3)
+
+ // V10 = D0Z,D0Z,D2X,D2Y
+ V10 = Sse.Shuffle(D0, D2, 0x4A); //_MM_SHUFFLE(1, 0, 2, 2)
+ V10 = Permute(V10, 0x2C); //_MM_SHUFFLE(0, 2, 3, 0)
+ V01 = Permute(row1, 0x8D); //_MM_SHUFFLE(2, 0, 3, 1)
+
+ // V11 = D0X,D0W,D2X,D2Y
+ V11 = Sse.Shuffle(D0, D2, 0x4C); //_MM_SHUFFLE(1, 0, 3, 0)
+ V11 = Permute(V11, 0x93); //_MM_SHUFFLE(2, 1, 0, 3)
+ V02 = Permute(row4, 0x33); //_MM_SHUFFLE(0, 3, 0, 3)
+
+ // V12 = D1Z,D1Z,D2Z,D2W
+ V12 = Sse.Shuffle(D1, D2, 0xEA); //_MM_SHUFFLE(3, 2, 2, 2)
+ V12 = Permute(V12, 0x2C); //_MM_SHUFFLE(0, 2, 3, 0)
+ V03 = Permute(row3, 0x8D); //_MM_SHUFFLE(2, 0, 3, 1)
+
+ // V13 = D1X,D1W,D2Z,D2W
+ V13 = Sse.Shuffle(D1, D2, 0xEC); //_MM_SHUFFLE(3, 2, 3, 0)
+ V13 = Permute(V13, 0x93); //_MM_SHUFFLE(2, 1, 0, 3)
+
+ V00 *= V10;
+ V01 *= V11;
+ V02 *= V12;
+ V03 *= V13;
+
+ Vector128 C1 = C0 - V00;
+ C0 += V00;
+
+ Vector128 C3 = C2 + V01;
+ C2 -= V01;
+
+ Vector128 C5 = C4 - V02;
+ C4 += V02;
+
+ Vector128 C7 = C6 + V03;
+ C6 -= V03;
+
+ C0 = Sse.Shuffle(C0, C1, 0xD8); //_MM_SHUFFLE(3, 1, 2, 0)
+ C2 = Sse.Shuffle(C2, C3, 0xD8); //_MM_SHUFFLE(3, 1, 2, 0)
+ C4 = Sse.Shuffle(C4, C5, 0xD8); //_MM_SHUFFLE(3, 1, 2, 0)
+ C6 = Sse.Shuffle(C6, C7, 0xD8); //_MM_SHUFFLE(3, 1, 2, 0)
+
+ C0 = Permute(C0, 0xD8); //_MM_SHUFFLE(3, 1, 2, 0)
+ C2 = Permute(C2, 0xD8); //_MM_SHUFFLE(3, 1, 2, 0)
+ C4 = Permute(C4, 0xD8); //_MM_SHUFFLE(3, 1, 2, 0)
+ C6 = Permute(C6, 0xD8); //_MM_SHUFFLE(3, 1, 2, 0)
+
+ // Get the determinant
+ float det = Vector4.Dot(C0.AsVector4(), row1.AsVector4());
+
+ // Check determinate is not zero
+ if (MathF.Abs(det) < float.Epsilon)
+ {
+ Vector4 vNaN = new Vector4(float.NaN);
+
+ result.X = vNaN;
+ result.Y = vNaN;
+ result.Z = vNaN;
+ result.W = vNaN;
+
+ return false;
+ }
+
+ // Create Vector128 copy of the determinant and invert them.
+
+ Vector128 ones = Vector128.Create(1.0f);
+ Vector128 vTemp = Vector128.Create(det);
+
+ vTemp = ones / vTemp;
+
+ result.X = (C0 * vTemp).AsVector4();
+ result.Y = (C2 * vTemp).AsVector4();
+ result.Z = (C4 * vTemp).AsVector4();
+ result.W = (C6 * vTemp).AsVector4();
+
+ return true;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static Vector128 Permute(Vector128 value, [ConstantExpected] byte control)
+ {
+ if (Avx.IsSupported)
+ {
+ return Avx.Permute(value, control);
+ }
+ else if (Sse.IsSupported)
+ {
+ return Sse.Shuffle(value, value, control);
+ }
+ else
+ {
+ // Redundant test so we won't prejit remainder of this method on platforms without SSE.
+ throw new PlatformNotSupportedException();
+ }
+ }
+
+ static bool SoftwareFallback(in Impl matrix, out Impl result)
+ {
+ // -1
+ // If you have matrix M, inverse Matrix M can compute
+ //
+ // -1 1
+ // M = --------- A
+ // det(M)
+ //
+ // A is adjugate (adjoint) of M, where,
+ //
+ // T
+ // A = C
+ //
+ // C is Cofactor matrix of M, where,
+ // i + j
+ // C = (-1) * det(M )
+ // ij ij
+ //
+ // [ a b c d ]
+ // M = [ e f g h ]
+ // [ i j k l ]
+ // [ m n o p ]
+ //
+ // First Row
+ // 2 | f g h |
+ // C = (-1) | j k l | = + ( f ( kp - lo ) - g ( jp - ln ) + h ( jo - kn ) )
+ // 11 | n o p |
+ //
+ // 3 | e g h |
+ // C = (-1) | i k l | = - ( e ( kp - lo ) - g ( ip - lm ) + h ( io - km ) )
+ // 12 | m o p |
+ //
+ // 4 | e f h |
+ // C = (-1) | i j l | = + ( e ( jp - ln ) - f ( ip - lm ) + h ( in - jm ) )
+ // 13 | m n p |
+ //
+ // 5 | e f g |
+ // C = (-1) | i j k | = - ( e ( jo - kn ) - f ( io - km ) + g ( in - jm ) )
+ // 14 | m n o |
+ //
+ // Second Row
+ // 3 | b c d |
+ // C = (-1) | j k l | = - ( b ( kp - lo ) - c ( jp - ln ) + d ( jo - kn ) )
+ // 21 | n o p |
+ //
+ // 4 | a c d |
+ // C = (-1) | i k l | = + ( a ( kp - lo ) - c ( ip - lm ) + d ( io - km ) )
+ // 22 | m o p |
+ //
+ // 5 | a b d |
+ // C = (-1) | i j l | = - ( a ( jp - ln ) - b ( ip - lm ) + d ( in - jm ) )
+ // 23 | m n p |
+ //
+ // 6 | a b c |
+ // C = (-1) | i j k | = + ( a ( jo - kn ) - b ( io - km ) + c ( in - jm ) )
+ // 24 | m n o |
+ //
+ // Third Row
+ // 4 | b c d |
+ // C = (-1) | f g h | = + ( b ( gp - ho ) - c ( fp - hn ) + d ( fo - gn ) )
+ // 31 | n o p |
+ //
+ // 5 | a c d |
+ // C = (-1) | e g h | = - ( a ( gp - ho ) - c ( ep - hm ) + d ( eo - gm ) )
+ // 32 | m o p |
+ //
+ // 6 | a b d |
+ // C = (-1) | e f h | = + ( a ( fp - hn ) - b ( ep - hm ) + d ( en - fm ) )
+ // 33 | m n p |
+ //
+ // 7 | a b c |
+ // C = (-1) | e f g | = - ( a ( fo - gn ) - b ( eo - gm ) + c ( en - fm ) )
+ // 34 | m n o |
+ //
+ // Fourth Row
+ // 5 | b c d |
+ // C = (-1) | f g h | = - ( b ( gl - hk ) - c ( fl - hj ) + d ( fk - gj ) )
+ // 41 | j k l |
+ //
+ // 6 | a c d |
+ // C = (-1) | e g h | = + ( a ( gl - hk ) - c ( el - hi ) + d ( ek - gi ) )
+ // 42 | i k l |
+ //
+ // 7 | a b d |
+ // C = (-1) | e f h | = - ( a ( fl - hj ) - b ( el - hi ) + d ( ej - fi ) )
+ // 43 | i j l |
+ //
+ // 8 | a b c |
+ // C = (-1) | e f g | = + ( a ( fk - gj ) - b ( ek - gi ) + c ( ej - fi ) )
+ // 44 | i j k |
+ //
+ // Cost of operation
+ // 53 adds, 104 muls, and 1 div.
+
+ float a = matrix.X.X, b = matrix.X.Y, c = matrix.X.Z, d = matrix.X.W;
+ float e = matrix.Y.X, f = matrix.Y.Y, g = matrix.Y.Z, h = matrix.Y.W;
+ float i = matrix.Z.X, j = matrix.Z.Y, k = matrix.Z.Z, l = matrix.Z.W;
+ float m = matrix.W.X, n = matrix.W.Y, o = matrix.W.Z, p = matrix.W.W;
+
+ float kp_lo = k * p - l * o;
+ float jp_ln = j * p - l * n;
+ float jo_kn = j * o - k * n;
+ float ip_lm = i * p - l * m;
+ float io_km = i * o - k * m;
+ float in_jm = i * n - j * m;
+
+ float a11 = +(f * kp_lo - g * jp_ln + h * jo_kn);
+ float a12 = -(e * kp_lo - g * ip_lm + h * io_km);
+ float a13 = +(e * jp_ln - f * ip_lm + h * in_jm);
+ float a14 = -(e * jo_kn - f * io_km + g * in_jm);
+
+ float det = a * a11 + b * a12 + c * a13 + d * a14;
+
+ if (MathF.Abs(det) < float.Epsilon)
+ {
+ Vector4 vNaN = new Vector4(float.NaN);
+
+ result.X = vNaN;
+ result.Y = vNaN;
+ result.Z = vNaN;
+ result.W = vNaN;
+
+ return false;
+ }
+
+ float invDet = 1.0f / det;
+
+ result.X.X = a11 * invDet;
+ result.Y.X = a12 * invDet;
+ result.Z.X = a13 * invDet;
+ result.W.X = a14 * invDet;
+
+ result.X.Y = -(b * kp_lo - c * jp_ln + d * jo_kn) * invDet;
+ result.Y.Y = +(a * kp_lo - c * ip_lm + d * io_km) * invDet;
+ result.Z.Y = -(a * jp_ln - b * ip_lm + d * in_jm) * invDet;
+ result.W.Y = +(a * jo_kn - b * io_km + c * in_jm) * invDet;
+
+ float gp_ho = g * p - h * o;
+ float fp_hn = f * p - h * n;
+ float fo_gn = f * o - g * n;
+ float ep_hm = e * p - h * m;
+ float eo_gm = e * o - g * m;
+ float en_fm = e * n - f * m;
+
+ result.X.Z = +(b * gp_ho - c * fp_hn + d * fo_gn) * invDet;
+ result.Y.Z = -(a * gp_ho - c * ep_hm + d * eo_gm) * invDet;
+ result.Z.Z = +(a * fp_hn - b * ep_hm + d * en_fm) * invDet;
+ result.W.Z = -(a * fo_gn - b * eo_gm + c * en_fm) * invDet;
+
+ float gl_hk = g * l - h * k;
+ float fl_hj = f * l - h * j;
+ float fk_gj = f * k - g * j;
+ float el_hi = e * l - h * i;
+ float ek_gi = e * k - g * i;
+ float ej_fi = e * j - f * i;
+
+ result.X.W = -(b * gl_hk - c * fl_hj + d * fk_gj) * invDet;
+ result.Y.W = +(a * gl_hk - c * el_hi + d * ek_gi) * invDet;
+ result.Z.W = -(a * fl_hj - b * el_hi + d * ej_fi) * invDet;
+ result.W.W = +(a * fk_gj - b * ek_gi + c * ej_fi) * invDet;
+
+ return true;
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl Lerp(in Impl left, in Impl right, float amount)
+ {
+ Impl result;
+
+ result.X = Vector4.Lerp(left.X, right.X, amount);
+ result.Y = Vector4.Lerp(left.Y, right.Y, amount);
+ result.Z = Vector4.Lerp(left.Z, right.Z, amount);
+ result.W = Vector4.Lerp(left.W, right.W, amount);
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl Transform(in Impl value, in Quaternion rotation)
+ {
+ // Compute rotation matrix.
+ float x2 = rotation.X + rotation.X;
+ float y2 = rotation.Y + rotation.Y;
+ float z2 = rotation.Z + rotation.Z;
+
+ float wx2 = rotation.W * x2;
+ float wy2 = rotation.W * y2;
+ float wz2 = rotation.W * z2;
+
+ float xx2 = rotation.X * x2;
+ float xy2 = rotation.X * y2;
+ float xz2 = rotation.X * z2;
+
+ float yy2 = rotation.Y * y2;
+ float yz2 = rotation.Y * z2;
+ float zz2 = rotation.Z * z2;
+
+ float q11 = 1.0f - yy2 - zz2;
+ float q21 = xy2 - wz2;
+ float q31 = xz2 + wy2;
+
+ float q12 = xy2 + wz2;
+ float q22 = 1.0f - xx2 - zz2;
+ float q32 = yz2 - wx2;
+
+ float q13 = xz2 - wy2;
+ float q23 = yz2 + wx2;
+ float q33 = 1.0f - xx2 - yy2;
+
+ Impl result;
+
+ result.X = new Vector4(
+ value.X.X * q11 + value.X.Y * q21 + value.X.Z * q31,
+ value.X.X * q12 + value.X.Y * q22 + value.X.Z * q32,
+ value.X.X * q13 + value.X.Y * q23 + value.X.Z * q33,
+ value.X.W
+ );
+ result.Y = new Vector4(
+ value.Y.X * q11 + value.Y.Y * q21 + value.Y.Z * q31,
+ value.Y.X * q12 + value.Y.Y * q22 + value.Y.Z * q32,
+ value.Y.X * q13 + value.Y.Y * q23 + value.Y.Z * q33,
+ value.Y.W
+ );
+ result.Z = new Vector4(
+ value.Z.X * q11 + value.Z.Y * q21 + value.Z.Z * q31,
+ value.Z.X * q12 + value.Z.Y * q22 + value.Z.Z * q32,
+ value.Z.X * q13 + value.Z.Y * q23 + value.Z.Z * q33,
+ value.Z.W
+ );
+ result.W = new Vector4(
+ value.W.X * q11 + value.W.Y * q21 + value.W.Z * q31,
+ value.W.X * q12 + value.W.Y * q22 + value.W.Z * q32,
+ value.W.X * q13 + value.W.Y * q23 + value.W.Z * q33,
+ value.W.W
+ );
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Impl Transpose(in Impl matrix)
+ {
+ // This implementation is based on the DirectX Math Library XMMatrixTranspose method
+ // https://github.com/microsoft/DirectXMath/blob/master/Inc/DirectXMathMatrix.inl
+
+ Impl result;
+
+ if (AdvSimd.Arm64.IsSupported)
+ {
+ Vector128 x = matrix.X.AsVector128();
+ Vector128 y = matrix.Y.AsVector128();
+ Vector128 z = matrix.Z.AsVector128();
+ Vector128 w = matrix.W.AsVector128();
+
+ Vector128 lowerXZ = AdvSimd.Arm64.ZipLow(x, z); // x[0], z[0], x[1], z[1]
+ Vector128 lowerYW = AdvSimd.Arm64.ZipLow(y, w); // y[0], w[0], y[1], w[1]
+ Vector128 upperXZ = AdvSimd.Arm64.ZipHigh(x, z); // x[2], z[2], x[3], z[3]
+ Vector128 upperYW = AdvSimd.Arm64.ZipHigh(y, w); // y[2], w[2], y[3], z[3]
+
+ result.X = AdvSimd.Arm64.ZipLow(lowerXZ, lowerYW).AsVector4(); // x[0], y[0], z[0], w[0]
+ result.Y = AdvSimd.Arm64.ZipHigh(lowerXZ, lowerYW).AsVector4(); // x[1], y[1], z[1], w[1]
+ result.Z = AdvSimd.Arm64.ZipLow(upperXZ, upperYW).AsVector4(); // x[2], y[2], z[2], w[2]
+ result.W = AdvSimd.Arm64.ZipHigh(upperXZ, upperYW).AsVector4(); // x[3], y[3], z[3], w[3]
+ }
+ else if (Sse.IsSupported)
+ {
+ Vector128 x = matrix.X.AsVector128();
+ Vector128 y = matrix.Y.AsVector128();
+ Vector128 z = matrix.Z.AsVector128();
+ Vector128 w = matrix.W.AsVector128();
+
+ Vector128 lowerXZ = Sse.UnpackLow(x, z); // x[0], z[0], x[1], z[1]
+ Vector128 lowerYW = Sse.UnpackLow(y, w); // y[0], w[0], y[1], w[1]
+ Vector128 upperXZ = Sse.UnpackHigh(x, z); // x[2], z[2], x[3], z[3]
+ Vector128 upperYW = Sse.UnpackHigh(y, w); // y[2], w[2], y[3], z[3]
+
+ result.X = Sse.UnpackLow(lowerXZ, lowerYW).AsVector4(); // x[0], y[0], z[0], w[0]
+ result.Y = Sse.UnpackHigh(lowerXZ, lowerYW).AsVector4(); // x[1], y[1], z[1], w[1]
+ result.Z = Sse.UnpackLow(upperXZ, upperYW).AsVector4(); // x[2], y[2], z[2], w[2]
+ result.W = Sse.UnpackHigh(upperXZ, upperYW).AsVector4(); // x[3], y[3], z[3], w[3]
+ }
+ else
+ {
+ result.X = new Vector4(matrix.X.X, matrix.Y.X, matrix.Z.X, matrix.W.X);
+ result.Y = new Vector4(matrix.X.Y, matrix.Y.Y, matrix.Z.Y, matrix.W.Y);
+ result.Z = new Vector4(matrix.X.Z, matrix.Y.Z, matrix.Z.Z, matrix.W.Z);
+ result.W = new Vector4(matrix.X.W, matrix.Y.W, matrix.Z.W, matrix.W.W);
+ }
+
+ return result;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public override readonly bool Equals([NotNullWhen(true)] object? obj)
+ => (obj is Matrix4x4 other) && Equals(in other.AsImpl());
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public readonly bool Equals(in Impl other)
+ {
+ // This function needs to account for floating-point equality around NaN
+ // and so must behave equivalently to the underlying float/double.Equals
+
+ return X.Equals(other.X)
+ && Y.Equals(other.Y)
+ && Z.Equals(other.Z)
+ && W.Equals(other.W);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public readonly float GetDeterminant()
+ {
+ // | a b c d | | f g h | | e g h | | e f h | | e f g |
+ // | e f g h | = a | j k l | - b | i k l | + c | i j l | - d | i j k |
+ // | i j k l | | n o p | | m o p | | m n p | | m n o |
+ // | m n o p |
+ //
+ // | f g h |
+ // a | j k l | = a ( f ( kp - lo ) - g ( jp - ln ) + h ( jo - kn ) )
+ // | n o p |
+ //
+ // | e g h |
+ // b | i k l | = b ( e ( kp - lo ) - g ( ip - lm ) + h ( io - km ) )
+ // | m o p |
+ //
+ // | e f h |
+ // c | i j l | = c ( e ( jp - ln ) - f ( ip - lm ) + h ( in - jm ) )
+ // | m n p |
+ //
+ // | e f g |
+ // d | i j k | = d ( e ( jo - kn ) - f ( io - km ) + g ( in - jm ) )
+ // | m n o |
+ //
+ // Cost of operation
+ // 17 adds and 28 muls.
+ //
+ // add: 6 + 8 + 3 = 17
+ // mul: 12 + 16 = 28
+
+ float a = X.X, b = X.Y, c = X.Z, d = X.W;
+ float e = Y.X, f = Y.Y, g = Y.Z, h = Y.W;
+ float i = Z.X, j = Z.Y, k = Z.Z, l = Z.W;
+ float m = W.X, n = W.Y, o = W.Z, p = W.W;
+
+ float kp_lo = k * p - l * o;
+ float jp_ln = j * p - l * n;
+ float jo_kn = j * o - k * n;
+ float ip_lm = i * p - l * m;
+ float io_km = i * o - k * m;
+ float in_jm = i * n - j * m;
+
+ return a * (f * kp_lo - g * jp_ln + h * jo_kn) -
+ b * (e * kp_lo - g * ip_lm + h * io_km) +
+ c * (e * jp_ln - f * ip_lm + h * in_jm) -
+ d * (e * jo_kn - f * io_km + g * in_jm);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public override readonly int GetHashCode() => HashCode.Combine(X, Y, Z, W);
+
+ bool IEquatable.Equals(Impl other) => Equals(in other);
+
+ private struct CanonicalBasis
+ {
+ public Vector3 Row0;
+ public Vector3 Row1;
+ public Vector3 Row2;
+ };
+
+ private unsafe struct VectorBasis
+ {
+ public Vector3* Element0;
+ public Vector3* Element1;
+ public Vector3* Element2;
+ }
+ }
+ }
+}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Matrix4x4.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Matrix4x4.cs
index 0ea71be499a6f3..18e05f76910b38 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Matrix4x4.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Matrix4x4.cs
@@ -3,9 +3,6 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
-using System.Runtime.Intrinsics;
-using System.Runtime.Intrinsics.Arm;
-using System.Runtime.Intrinsics.X86;
namespace System.Numerics
{
@@ -14,19 +11,17 @@ namespace System.Numerics
/// [!INCLUDE[vectors-are-rows-paragraph](~/includes/system-numerics-vectors-are-rows.md)]
/// ]]>
[Intrinsic]
- public struct Matrix4x4 : IEquatable
+ public partial struct Matrix4x4 : IEquatable
{
- private const float BillboardEpsilon = 1e-4f;
- private const float BillboardMinAngle = 1.0f - (0.1f * (MathF.PI / 180.0f)); // 0.1 degrees
- private const float DecomposeEpsilon = 0.0001f;
-
- private static readonly Matrix4x4 _identity = new Matrix4x4
- (
- 1f, 0f, 0f, 0f,
- 0f, 1f, 0f, 0f,
- 0f, 0f, 1f, 0f,
- 0f, 0f, 0f, 1f
- );
+ // In an ideal world, we'd have 4x Vector4 fields. However, Matrix4x4 was shipped with
+ // 16x public float fields and as such we cannot change the "backing" fields without it being
+ // a breaking change. Likewise, we cannot switch to using something like ExplicitLayout
+ // without it pessimizing other parts of the JIT and still preventing things like field promotion.
+ //
+ // This nested Impl struct works around this problem by relying on the JIT treating same sizeof
+ // value type bitcasts as a no-op. Effectively the entire implementation is here in this type
+ // and the public facing Matrix4x4 just defers to it with simple reinterpret casts inserted
+ // at the relevant points.
/// The first element of the first row.
public float M11;
@@ -98,58 +93,31 @@ public Matrix4x4(float m11, float m12, float m13, float m14,
float m31, float m32, float m33, float m34,
float m41, float m42, float m43, float m44)
{
- M11 = m11;
- M12 = m12;
- M13 = m13;
- M14 = m14;
-
- M21 = m21;
- M22 = m22;
- M23 = m23;
- M24 = m24;
-
- M31 = m31;
- M32 = m32;
- M33 = m33;
- M34 = m34;
-
- M41 = m41;
- M42 = m42;
- M43 = m43;
- M44 = m44;
+ Unsafe.SkipInit(out this);
+
+ AsImpl().Init(
+ m11, m12, m13, m14,
+ m21, m22, m23, m24,
+ m31, m32, m33, m34,
+ m41, m42, m43, m44
+ );
}
- /// Creates a object from a specified object.
+ /// Creates a object from a specified object.
/// A 3x2 matrix.
- /// This constructor creates a 4x4 matrix whose , , , , , , , and components are zero, and whose and components are one.
+ /// This constructor creates a 4x4 matrix whose , , , , , , , and components are zero, and whose and components are one.
public Matrix4x4(Matrix3x2 value)
{
- M11 = value.M11;
- M12 = value.M12;
- M13 = 0f;
- M14 = 0f;
-
- M21 = value.M21;
- M22 = value.M22;
- M23 = 0f;
- M24 = 0f;
-
- M31 = 0f;
- M32 = 0f;
- M33 = 1f;
- M34 = 0f;
-
- M41 = value.M31;
- M42 = value.M32;
- M43 = 0f;
- M44 = 1f;
+ Unsafe.SkipInit(out this);
+ AsImpl().Init(in value.AsImpl());
}
/// Gets the multiplicative identity matrix.
/// Gets the multiplicative identity matrix.
public static Matrix4x4 Identity
{
- get => _identity;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => Impl.Identity.AsM4x4();
}
/// Gets or sets the element at the specified indices.
@@ -161,411 +129,92 @@ public static Matrix4x4 Identity
/// -or-
/// was less than zero or greater than the number of columns.
///
- public unsafe float this[int row, int column]
+ public float this[int row, int column]
{
- get
- {
- if ((uint)row >= 4)
- ThrowHelper.ThrowArgumentOutOfRangeException();
-
- Vector4 vrow = Unsafe.Add(ref Unsafe.As(ref M11), row);
- return vrow[column];
- }
- set
- {
- if ((uint)row >= 4)
- ThrowHelper.ThrowArgumentOutOfRangeException();
-
- ref Vector4 vrow = ref Unsafe.Add(ref Unsafe.As(ref M11), row);
- var tmp = Vector4.WithElement(vrow, column, value);
- vrow = tmp;
- }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => AsROImpl()[row, column];
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => AsImpl()[row, column] = value;
}
/// Indicates whether the current matrix is the identity matrix.
/// if the current matrix is the identity matrix; otherwise, .
public readonly bool IsIdentity
{
- get
- {
- return M11 == 1f && M22 == 1f && M33 == 1f && M44 == 1f && // Check diagonal element first for early out.
- M12 == 0f && M13 == 0f && M14 == 0f &&
- M21 == 0f && M23 == 0f && M24 == 0f &&
- M31 == 0f && M32 == 0f && M34 == 0f &&
- M41 == 0f && M42 == 0f && M43 == 0f;
- }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => AsROImpl().IsIdentity;
}
/// Gets or sets the translation component of this matrix.
/// The translation component of the current instance.
public Vector3 Translation
{
- readonly get => new Vector3(M41, M42, M43);
-
- set
- {
- M41 = value.X;
- M42 = value.Y;
- M43 = value.Z;
- }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ readonly get => AsROImpl().Translation;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => AsImpl().Translation = value;
}
/// Adds each element in one matrix with its corresponding element in a second matrix.
/// The first matrix.
/// The second matrix.
/// The matrix that contains the summed values.
- /// The method defines the operation of the addition operator for objects.
- public static unsafe Matrix4x4 operator +(Matrix4x4 value1, Matrix4x4 value2)
- {
- if (AdvSimd.IsSupported)
- {
- AdvSimd.Store(&value1.M11, AdvSimd.Add(AdvSimd.LoadVector128(&value1.M11), AdvSimd.LoadVector128(&value2.M11)));
- AdvSimd.Store(&value1.M21, AdvSimd.Add(AdvSimd.LoadVector128(&value1.M21), AdvSimd.LoadVector128(&value2.M21)));
- AdvSimd.Store(&value1.M31, AdvSimd.Add(AdvSimd.LoadVector128(&value1.M31), AdvSimd.LoadVector128(&value2.M31)));
- AdvSimd.Store(&value1.M41, AdvSimd.Add(AdvSimd.LoadVector128(&value1.M41), AdvSimd.LoadVector128(&value2.M41)));
- return value1;
- }
- else if (Sse.IsSupported)
- {
- Sse.Store(&value1.M11, Sse.Add(Sse.LoadVector128(&value1.M11), Sse.LoadVector128(&value2.M11)));
- Sse.Store(&value1.M21, Sse.Add(Sse.LoadVector128(&value1.M21), Sse.LoadVector128(&value2.M21)));
- Sse.Store(&value1.M31, Sse.Add(Sse.LoadVector128(&value1.M31), Sse.LoadVector128(&value2.M31)));
- Sse.Store(&value1.M41, Sse.Add(Sse.LoadVector128(&value1.M41), Sse.LoadVector128(&value2.M41)));
- return value1;
- }
-
- Matrix4x4 m;
-
- m.M11 = value1.M11 + value2.M11;
- m.M12 = value1.M12 + value2.M12;
- m.M13 = value1.M13 + value2.M13;
- m.M14 = value1.M14 + value2.M14;
- m.M21 = value1.M21 + value2.M21;
- m.M22 = value1.M22 + value2.M22;
- m.M23 = value1.M23 + value2.M23;
- m.M24 = value1.M24 + value2.M24;
- m.M31 = value1.M31 + value2.M31;
- m.M32 = value1.M32 + value2.M32;
- m.M33 = value1.M33 + value2.M33;
- m.M34 = value1.M34 + value2.M34;
- m.M41 = value1.M41 + value2.M41;
- m.M42 = value1.M42 + value2.M42;
- m.M43 = value1.M43 + value2.M43;
- m.M44 = value1.M44 + value2.M44;
-
- return m;
- }
+ /// The method defines the operation of the addition operator for objects.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Matrix4x4 operator +(Matrix4x4 value1, Matrix4x4 value2)
+ => (value1.AsImpl() + value2.AsImpl()).AsM4x4();
/// Returns a value that indicates whether the specified matrices are equal.
/// The first matrix to compare.
/// The second matrix to care
/// if and are equal; otherwise, .
/// Two matrices are equal if all their corresponding elements are equal.
- public static unsafe bool operator ==(Matrix4x4 value1, Matrix4x4 value2)
- {
- if (AdvSimd.Arm64.IsSupported)
- {
- return VectorMath.Equal(AdvSimd.LoadVector128(&value1.M11), AdvSimd.LoadVector128(&value2.M11)) &&
- VectorMath.Equal(AdvSimd.LoadVector128(&value1.M21), AdvSimd.LoadVector128(&value2.M21)) &&
- VectorMath.Equal(AdvSimd.LoadVector128(&value1.M31), AdvSimd.LoadVector128(&value2.M31)) &&
- VectorMath.Equal(AdvSimd.LoadVector128(&value1.M41), AdvSimd.LoadVector128(&value2.M41));
- }
- else if (Sse.IsSupported)
- {
- return VectorMath.Equal(Sse.LoadVector128(&value1.M11), Sse.LoadVector128(&value2.M11)) &&
- VectorMath.Equal(Sse.LoadVector128(&value1.M21), Sse.LoadVector128(&value2.M21)) &&
- VectorMath.Equal(Sse.LoadVector128(&value1.M31), Sse.LoadVector128(&value2.M31)) &&
- VectorMath.Equal(Sse.LoadVector128(&value1.M41), Sse.LoadVector128(&value2.M41));
- }
-
- return (value1.M11 == value2.M11 && value1.M22 == value2.M22 && value1.M33 == value2.M33 && value1.M44 == value2.M44 && // Check diagonal element first for early out.
- value1.M12 == value2.M12 && value1.M13 == value2.M13 && value1.M14 == value2.M14 && value1.M21 == value2.M21 &&
- value1.M23 == value2.M23 && value1.M24 == value2.M24 && value1.M31 == value2.M31 && value1.M32 == value2.M32 &&
- value1.M34 == value2.M34 && value1.M41 == value2.M41 && value1.M42 == value2.M42 && value1.M43 == value2.M43);
- }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool operator ==(Matrix4x4 value1, Matrix4x4 value2)
+ => value1.AsImpl() == value2.AsImpl();
/// Returns a value that indicates whether the specified matrices are not equal.
/// The first matrix to compare.
/// The second matrix to compare.
/// if and are not equal; otherwise, .
- public static unsafe bool operator !=(Matrix4x4 value1, Matrix4x4 value2)
- {
- if (AdvSimd.Arm64.IsSupported)
- {
- return VectorMath.NotEqual(AdvSimd.LoadVector128(&value1.M11), AdvSimd.LoadVector128(&value2.M11)) ||
- VectorMath.NotEqual(AdvSimd.LoadVector128(&value1.M21), AdvSimd.LoadVector128(&value2.M21)) ||
- VectorMath.NotEqual(AdvSimd.LoadVector128(&value1.M31), AdvSimd.LoadVector128(&value2.M31)) ||
- VectorMath.NotEqual(AdvSimd.LoadVector128(&value1.M41), AdvSimd.LoadVector128(&value2.M41));
- }
- else if (Sse.IsSupported)
- {
- return
- VectorMath.NotEqual(Sse.LoadVector128(&value1.M11), Sse.LoadVector128(&value2.M11)) ||
- VectorMath.NotEqual(Sse.LoadVector128(&value1.M21), Sse.LoadVector128(&value2.M21)) ||
- VectorMath.NotEqual(Sse.LoadVector128(&value1.M31), Sse.LoadVector128(&value2.M31)) ||
- VectorMath.NotEqual(Sse.LoadVector128(&value1.M41), Sse.LoadVector128(&value2.M41));
- }
-
- return (value1.M11 != value2.M11 || value1.M12 != value2.M12 || value1.M13 != value2.M13 || value1.M14 != value2.M14 ||
- value1.M21 != value2.M21 || value1.M22 != value2.M22 || value1.M23 != value2.M23 || value1.M24 != value2.M24 ||
- value1.M31 != value2.M31 || value1.M32 != value2.M32 || value1.M33 != value2.M33 || value1.M34 != value2.M34 ||
- value1.M41 != value2.M41 || value1.M42 != value2.M42 || value1.M43 != value2.M43 || value1.M44 != value2.M44);
- }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool operator !=(Matrix4x4 value1, Matrix4x4 value2)
+ => value1.AsImpl() != value2.AsImpl();
/// Multiplies two matrices together to compute the product.
/// The first matrix.
/// The second matrix.
/// The product matrix.
- /// The method defines the operation of the multiplication operator for objects.
- public static unsafe Matrix4x4 operator *(Matrix4x4 value1, Matrix4x4 value2)
- {
- if (AdvSimd.Arm64.IsSupported)
- {
- Unsafe.SkipInit(out Matrix4x4 result);
-
- // Perform the operation on the first row
-
- Vector128 M11 = AdvSimd.LoadVector128(&value1.M11);
-
- Vector128 vX = AdvSimd.MultiplyBySelectedScalar(AdvSimd.LoadVector128(&value2.M11), M11, 0);
- Vector128 vY = AdvSimd.MultiplyBySelectedScalar(AdvSimd.LoadVector128(&value2.M21), M11, 1);
- Vector128 vZ = AdvSimd.Arm64.FusedMultiplyAddBySelectedScalar(vX, AdvSimd.LoadVector128(&value2.M31), M11, 2);
- Vector128 vW = AdvSimd.Arm64.FusedMultiplyAddBySelectedScalar(vY, AdvSimd.LoadVector128(&value2.M41), M11, 3);
-
- AdvSimd.Store(&result.M11, AdvSimd.Add(vZ, vW));
-
- // Repeat for the other 3 rows
-
- Vector128 M21 = AdvSimd.LoadVector128(&value1.M21);
-
- vX = AdvSimd.MultiplyBySelectedScalar(AdvSimd.LoadVector128(&value2.M11), M21, 0);
- vY = AdvSimd.MultiplyBySelectedScalar(AdvSimd.LoadVector128(&value2.M21), M21, 1);
- vZ = AdvSimd.Arm64.FusedMultiplyAddBySelectedScalar(vX, AdvSimd.LoadVector128(&value2.M31), M21, 2);
- vW = AdvSimd.Arm64.FusedMultiplyAddBySelectedScalar(vY, AdvSimd.LoadVector128(&value2.M41), M21, 3);
-
- AdvSimd.Store(&result.M21, AdvSimd.Add(vZ, vW));
-
- Vector128 M31 = AdvSimd.LoadVector128(&value1.M31);
-
- vX = AdvSimd.MultiplyBySelectedScalar(AdvSimd.LoadVector128(&value2.M11), M31, 0);
- vY = AdvSimd.MultiplyBySelectedScalar(AdvSimd.LoadVector128(&value2.M21), M31, 1);
- vZ = AdvSimd.Arm64.FusedMultiplyAddBySelectedScalar(vX, AdvSimd.LoadVector128(&value2.M31), M31, 2);
- vW = AdvSimd.Arm64.FusedMultiplyAddBySelectedScalar(vY, AdvSimd.LoadVector128(&value2.M41), M31, 3);
-
- AdvSimd.Store(&result.M31, AdvSimd.Add(vZ, vW));
-
- Vector128 M41 = AdvSimd.LoadVector128(&value1.M41);
-
- vX = AdvSimd.MultiplyBySelectedScalar(AdvSimd.LoadVector128(&value2.M11), M41, 0);
- vY = AdvSimd.MultiplyBySelectedScalar(AdvSimd.LoadVector128(&value2.M21), M41, 1);
- vZ = AdvSimd.Arm64.FusedMultiplyAddBySelectedScalar(vX, AdvSimd.LoadVector128(&value2.M31), M41, 2);
- vW = AdvSimd.Arm64.FusedMultiplyAddBySelectedScalar(vY, AdvSimd.LoadVector128(&value2.M41), M41, 3);
-
- AdvSimd.Store(&result.M41, AdvSimd.Add(vZ, vW));
-
- return result;
- }
- else if (Sse.IsSupported)
- {
- Vector128 row = Sse.LoadVector128(&value1.M11);
- Sse.Store(&value1.M11,
- Sse.Add(Sse.Add(Sse.Multiply(Sse.Shuffle(row, row, 0x00), Sse.LoadVector128(&value2.M11)),
- Sse.Multiply(Sse.Shuffle(row, row, 0x55), Sse.LoadVector128(&value2.M21))),
- Sse.Add(Sse.Multiply(Sse.Shuffle(row, row, 0xAA), Sse.LoadVector128(&value2.M31)),
- Sse.Multiply(Sse.Shuffle(row, row, 0xFF), Sse.LoadVector128(&value2.M41)))));
-
- // 0x00 is _MM_SHUFFLE(0,0,0,0), 0x55 is _MM_SHUFFLE(1,1,1,1), etc.
- // TODO: Replace with a method once it's added to the API.
-
- row = Sse.LoadVector128(&value1.M21);
- Sse.Store(&value1.M21,
- Sse.Add(Sse.Add(Sse.Multiply(Sse.Shuffle(row, row, 0x00), Sse.LoadVector128(&value2.M11)),
- Sse.Multiply(Sse.Shuffle(row, row, 0x55), Sse.LoadVector128(&value2.M21))),
- Sse.Add(Sse.Multiply(Sse.Shuffle(row, row, 0xAA), Sse.LoadVector128(&value2.M31)),
- Sse.Multiply(Sse.Shuffle(row, row, 0xFF), Sse.LoadVector128(&value2.M41)))));
-
- row = Sse.LoadVector128(&value1.M31);
- Sse.Store(&value1.M31,
- Sse.Add(Sse.Add(Sse.Multiply(Sse.Shuffle(row, row, 0x00), Sse.LoadVector128(&value2.M11)),
- Sse.Multiply(Sse.Shuffle(row, row, 0x55), Sse.LoadVector128(&value2.M21))),
- Sse.Add(Sse.Multiply(Sse.Shuffle(row, row, 0xAA), Sse.LoadVector128(&value2.M31)),
- Sse.Multiply(Sse.Shuffle(row, row, 0xFF), Sse.LoadVector128(&value2.M41)))));
-
- row = Sse.LoadVector128(&value1.M41);
- Sse.Store(&value1.M41,
- Sse.Add(Sse.Add(Sse.Multiply(Sse.Shuffle(row, row, 0x00), Sse.LoadVector128(&value2.M11)),
- Sse.Multiply(Sse.Shuffle(row, row, 0x55), Sse.LoadVector128(&value2.M21))),
- Sse.Add(Sse.Multiply(Sse.Shuffle(row, row, 0xAA), Sse.LoadVector128(&value2.M31)),
- Sse.Multiply(Sse.Shuffle(row, row, 0xFF), Sse.LoadVector128(&value2.M41)))));
- return value1;
- }
-
- Matrix4x4 m;
-
- // First row
- m.M11 = value1.M11 * value2.M11 + value1.M12 * value2.M21 + value1.M13 * value2.M31 + value1.M14 * value2.M41;
- m.M12 = value1.M11 * value2.M12 + value1.M12 * value2.M22 + value1.M13 * value2.M32 + value1.M14 * value2.M42;
- m.M13 = value1.M11 * value2.M13 + value1.M12 * value2.M23 + value1.M13 * value2.M33 + value1.M14 * value2.M43;
- m.M14 = value1.M11 * value2.M14 + value1.M12 * value2.M24 + value1.M13 * value2.M34 + value1.M14 * value2.M44;
-
- // Second row
- m.M21 = value1.M21 * value2.M11 + value1.M22 * value2.M21 + value1.M23 * value2.M31 + value1.M24 * value2.M41;
- m.M22 = value1.M21 * value2.M12 + value1.M22 * value2.M22 + value1.M23 * value2.M32 + value1.M24 * value2.M42;
- m.M23 = value1.M21 * value2.M13 + value1.M22 * value2.M23 + value1.M23 * value2.M33 + value1.M24 * value2.M43;
- m.M24 = value1.M21 * value2.M14 + value1.M22 * value2.M24 + value1.M23 * value2.M34 + value1.M24 * value2.M44;
-
- // Third row
- m.M31 = value1.M31 * value2.M11 + value1.M32 * value2.M21 + value1.M33 * value2.M31 + value1.M34 * value2.M41;
- m.M32 = value1.M31 * value2.M12 + value1.M32 * value2.M22 + value1.M33 * value2.M32 + value1.M34 * value2.M42;
- m.M33 = value1.M31 * value2.M13 + value1.M32 * value2.M23 + value1.M33 * value2.M33 + value1.M34 * value2.M43;
- m.M34 = value1.M31 * value2.M14 + value1.M32 * value2.M24 + value1.M33 * value2.M34 + value1.M34 * value2.M44;
-
- // Fourth row
- m.M41 = value1.M41 * value2.M11 + value1.M42 * value2.M21 + value1.M43 * value2.M31 + value1.M44 * value2.M41;
- m.M42 = value1.M41 * value2.M12 + value1.M42 * value2.M22 + value1.M43 * value2.M32 + value1.M44 * value2.M42;
- m.M43 = value1.M41 * value2.M13 + value1.M42 * value2.M23 + value1.M43 * value2.M33 + value1.M44 * value2.M43;
- m.M44 = value1.M41 * value2.M14 + value1.M42 * value2.M24 + value1.M43 * value2.M34 + value1.M44 * value2.M44;
-
- return m;
- }
+ /// The method defines the operation of the multiplication operator for objects.
+ public static Matrix4x4 operator *(Matrix4x4 value1, Matrix4x4 value2)
+ => (value1.AsImpl() * value2.AsImpl()).AsM4x4();
/// Multiplies a matrix by a float to compute the product.
/// The matrix to scale.
/// The scaling value to use.
/// The scaled matrix.
- /// The method defines the operation of the multiplication operator for objects.
- public static unsafe Matrix4x4 operator *(Matrix4x4 value1, float value2)
- {
- if (AdvSimd.IsSupported)
- {
- Vector128 value2Vec = Vector128.Create(value2);
- AdvSimd.Store(&value1.M11, AdvSimd.Multiply(AdvSimd.LoadVector128(&value1.M11), value2Vec));
- AdvSimd.Store(&value1.M21, AdvSimd.Multiply(AdvSimd.LoadVector128(&value1.M21), value2Vec));
- AdvSimd.Store(&value1.M31, AdvSimd.Multiply(AdvSimd.LoadVector128(&value1.M31), value2Vec));
- AdvSimd.Store(&value1.M41, AdvSimd.Multiply(AdvSimd.LoadVector128(&value1.M41), value2Vec));
- return value1;
- }
- else if (Sse.IsSupported)
- {
- Vector128 value2Vec = Vector128.Create(value2);
- Sse.Store(&value1.M11, Sse.Multiply(Sse.LoadVector128(&value1.M11), value2Vec));
- Sse.Store(&value1.M21, Sse.Multiply(Sse.LoadVector128(&value1.M21), value2Vec));
- Sse.Store(&value1.M31, Sse.Multiply(Sse.LoadVector128(&value1.M31), value2Vec));
- Sse.Store(&value1.M41, Sse.Multiply(Sse.LoadVector128(&value1.M41), value2Vec));
- return value1;
- }
-
- Matrix4x4 m;
-
- m.M11 = value1.M11 * value2;
- m.M12 = value1.M12 * value2;
- m.M13 = value1.M13 * value2;
- m.M14 = value1.M14 * value2;
- m.M21 = value1.M21 * value2;
- m.M22 = value1.M22 * value2;
- m.M23 = value1.M23 * value2;
- m.M24 = value1.M24 * value2;
- m.M31 = value1.M31 * value2;
- m.M32 = value1.M32 * value2;
- m.M33 = value1.M33 * value2;
- m.M34 = value1.M34 * value2;
- m.M41 = value1.M41 * value2;
- m.M42 = value1.M42 * value2;
- m.M43 = value1.M43 * value2;
- m.M44 = value1.M44 * value2;
- return m;
- }
+ /// The method defines the operation of the multiplication operator for objects.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Matrix4x4 operator *(Matrix4x4 value1, float value2)
+ => (value1.AsImpl() * value2).AsM4x4();
/// Subtracts each element in a second matrix from its corresponding element in a first matrix.
/// The first matrix.
/// The second matrix.
/// The matrix containing the values that result from subtracting each element in from its corresponding element in .
- /// The method defines the operation of the subtraction operator for objects.
- public static unsafe Matrix4x4 operator -(Matrix4x4 value1, Matrix4x4 value2)
- {
- if (AdvSimd.IsSupported)
- {
- AdvSimd.Store(&value1.M11, AdvSimd.Subtract(AdvSimd.LoadVector128(&value1.M11), AdvSimd.LoadVector128(&value2.M11)));
- AdvSimd.Store(&value1.M21, AdvSimd.Subtract(AdvSimd.LoadVector128(&value1.M21), AdvSimd.LoadVector128(&value2.M21)));
- AdvSimd.Store(&value1.M31, AdvSimd.Subtract(AdvSimd.LoadVector128(&value1.M31), AdvSimd.LoadVector128(&value2.M31)));
- AdvSimd.Store(&value1.M41, AdvSimd.Subtract(AdvSimd.LoadVector128(&value1.M41), AdvSimd.LoadVector128(&value2.M41)));
- return value1;
- }
- else if (Sse.IsSupported)
- {
- Sse.Store(&value1.M11, Sse.Subtract(Sse.LoadVector128(&value1.M11), Sse.LoadVector128(&value2.M11)));
- Sse.Store(&value1.M21, Sse.Subtract(Sse.LoadVector128(&value1.M21), Sse.LoadVector128(&value2.M21)));
- Sse.Store(&value1.M31, Sse.Subtract(Sse.LoadVector128(&value1.M31), Sse.LoadVector128(&value2.M31)));
- Sse.Store(&value1.M41, Sse.Subtract(Sse.LoadVector128(&value1.M41), Sse.LoadVector128(&value2.M41)));
- return value1;
- }
-
- Matrix4x4 m;
-
- m.M11 = value1.M11 - value2.M11;
- m.M12 = value1.M12 - value2.M12;
- m.M13 = value1.M13 - value2.M13;
- m.M14 = value1.M14 - value2.M14;
- m.M21 = value1.M21 - value2.M21;
- m.M22 = value1.M22 - value2.M22;
- m.M23 = value1.M23 - value2.M23;
- m.M24 = value1.M24 - value2.M24;
- m.M31 = value1.M31 - value2.M31;
- m.M32 = value1.M32 - value2.M32;
- m.M33 = value1.M33 - value2.M33;
- m.M34 = value1.M34 - value2.M34;
- m.M41 = value1.M41 - value2.M41;
- m.M42 = value1.M42 - value2.M42;
- m.M43 = value1.M43 - value2.M43;
- m.M44 = value1.M44 - value2.M44;
-
- return m;
- }
+ /// The method defines the operation of the subtraction operator for objects.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Matrix4x4 operator -(Matrix4x4 value1, Matrix4x4 value2)
+ => (value1.AsImpl() - value2.AsImpl()).AsM4x4();
/// Negates the specified matrix by multiplying all its values by -1.
/// The matrix to negate.
/// The negated matrix.
- public static unsafe Matrix4x4 operator -(Matrix4x4 value)
- {
- if (AdvSimd.IsSupported)
- {
- AdvSimd.Store(&value.M11, AdvSimd.Negate(AdvSimd.LoadVector128(&value.M11)));
- AdvSimd.Store(&value.M21, AdvSimd.Negate(AdvSimd.LoadVector128(&value.M21)));
- AdvSimd.Store(&value.M31, AdvSimd.Negate(AdvSimd.LoadVector128(&value.M31)));
- AdvSimd.Store(&value.M41, AdvSimd.Negate(AdvSimd.LoadVector128(&value.M41)));
- return value;
- }
- else if (Sse.IsSupported)
- {
- Vector128 zero = Vector128.Zero;
- Sse.Store(&value.M11, Sse.Subtract(zero, Sse.LoadVector128(&value.M11)));
- Sse.Store(&value.M21, Sse.Subtract(zero, Sse.LoadVector128(&value.M21)));
- Sse.Store(&value.M31, Sse.Subtract(zero, Sse.LoadVector128(&value.M31)));
- Sse.Store(&value.M41, Sse.Subtract(zero, Sse.LoadVector128(&value.M41)));
- return value;
- }
-
- Matrix4x4 m;
-
- m.M11 = -value.M11;
- m.M12 = -value.M12;
- m.M13 = -value.M13;
- m.M14 = -value.M14;
- m.M21 = -value.M21;
- m.M22 = -value.M22;
- m.M23 = -value.M23;
- m.M24 = -value.M24;
- m.M31 = -value.M31;
- m.M32 = -value.M32;
- m.M33 = -value.M33;
- m.M34 = -value.M34;
- m.M41 = -value.M41;
- m.M42 = -value.M42;
- m.M43 = -value.M43;
- m.M44 = -value.M44;
-
- return m;
- }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Matrix4x4 operator -(Matrix4x4 value)
+ => (-value.AsImpl()).AsM4x4();
/// Adds each element in one matrix with its corresponding element in a second matrix.
/// The first matrix.
@@ -573,9 +222,7 @@ public Vector3 Translation
/// The matrix that contains the summed values of and .
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Matrix4x4 Add(Matrix4x4 value1, Matrix4x4 value2)
- {
- return value1 + value2;
- }
+ => (value1.AsImpl() + value2.AsImpl()).AsM4x4();
/// Creates a spherical billboard that rotates around a specified object position.
/// The position of the object that the billboard will rotate around.
@@ -584,46 +231,7 @@ public static Matrix4x4 Add(Matrix4x4 value1, Matrix4x4 value2)
/// The forward vector of the camera.
/// The created billboard.
public static Matrix4x4 CreateBillboard(Vector3 objectPosition, Vector3 cameraPosition, Vector3 cameraUpVector, Vector3 cameraForwardVector)
- {
- Vector3 zaxis = objectPosition - cameraPosition;
- float norm = zaxis.LengthSquared();
-
- if (norm < BillboardEpsilon)
- {
- zaxis = -cameraForwardVector;
- }
- else
- {
- zaxis = Vector3.Multiply(zaxis, 1.0f / MathF.Sqrt(norm));
- }
-
- Vector3 xaxis = Vector3.Normalize(Vector3.Cross(cameraUpVector, zaxis));
- Vector3 yaxis = Vector3.Cross(zaxis, xaxis);
-
- Matrix4x4 result;
-
- result.M11 = xaxis.X;
- result.M12 = xaxis.Y;
- result.M13 = xaxis.Z;
- result.M14 = 0.0f;
-
- result.M21 = yaxis.X;
- result.M22 = yaxis.Y;
- result.M23 = yaxis.Z;
- result.M24 = 0.0f;
-
- result.M31 = zaxis.X;
- result.M32 = zaxis.Y;
- result.M33 = zaxis.Z;
- result.M34 = 0.0f;
-
- result.M41 = objectPosition.X;
- result.M42 = objectPosition.Y;
- result.M43 = objectPosition.Z;
- result.M44 = 1.0f;
-
- return result;
- }
+ => Impl.CreateBillboard(in objectPosition, in cameraPosition, in cameraUpVector, in cameraForwardVector).AsM4x4();
/// Creates a cylindrical billboard that rotates around a specified axis.
/// The position of the object that the billboard will rotate around.
@@ -633,158 +241,20 @@ public static Matrix4x4 CreateBillboard(Vector3 objectPosition, Vector3 cameraPo
/// The forward vector of the object.
/// The billboard matrix.
public static Matrix4x4 CreateConstrainedBillboard(Vector3 objectPosition, Vector3 cameraPosition, Vector3 rotateAxis, Vector3 cameraForwardVector, Vector3 objectForwardVector)
- {
- // Treat the case when object and camera positions are too close.
- Vector3 faceDir = objectPosition - cameraPosition;
- float norm = faceDir.LengthSquared();
-
- if (norm < BillboardEpsilon)
- {
- faceDir = -cameraForwardVector;
- }
- else
- {
- faceDir = Vector3.Multiply(faceDir, (1.0f / MathF.Sqrt(norm)));
- }
-
- Vector3 yaxis = rotateAxis;
- Vector3 xaxis;
- Vector3 zaxis;
-
- // Treat the case when angle between faceDir and rotateAxis is too close to 0.
- float dot = Vector3.Dot(rotateAxis, faceDir);
-
- if (MathF.Abs(dot) > BillboardMinAngle)
- {
- zaxis = objectForwardVector;
-
- // Make sure passed values are useful for compute.
- dot = Vector3.Dot(rotateAxis, zaxis);
-
- if (MathF.Abs(dot) > BillboardMinAngle)
- {
- zaxis = (MathF.Abs(rotateAxis.Z) > BillboardMinAngle) ? new Vector3(1, 0, 0) : new Vector3(0, 0, -1);
- }
-
- xaxis = Vector3.Normalize(Vector3.Cross(rotateAxis, zaxis));
- zaxis = Vector3.Normalize(Vector3.Cross(xaxis, rotateAxis));
- }
- else
- {
- xaxis = Vector3.Normalize(Vector3.Cross(rotateAxis, faceDir));
- zaxis = Vector3.Normalize(Vector3.Cross(xaxis, yaxis));
- }
-
- Matrix4x4 result;
-
- result.M11 = xaxis.X;
- result.M12 = xaxis.Y;
- result.M13 = xaxis.Z;
- result.M14 = 0.0f;
-
- result.M21 = yaxis.X;
- result.M22 = yaxis.Y;
- result.M23 = yaxis.Z;
- result.M24 = 0.0f;
-
- result.M31 = zaxis.X;
- result.M32 = zaxis.Y;
- result.M33 = zaxis.Z;
- result.M34 = 0.0f;
-
- result.M41 = objectPosition.X;
- result.M42 = objectPosition.Y;
- result.M43 = objectPosition.Z;
- result.M44 = 1.0f;
-
- return result;
- }
+ => Impl.CreateConstrainedBillboard(in objectPosition, in cameraPosition, in rotateAxis, in cameraForwardVector, in objectForwardVector).AsM4x4();
/// Creates a matrix that rotates around an arbitrary vector.
/// The axis to rotate around.
/// The angle to rotate around , in radians.
/// The rotation matrix.
public static Matrix4x4 CreateFromAxisAngle(Vector3 axis, float angle)
- {
- // a: angle
- // x, y, z: unit vector for axis.
- //
- // Rotation matrix M can compute by using below equation.
- //
- // T T
- // M = uu + (cos a)( I-uu ) + (sin a)S
- //
- // Where:
- //
- // u = ( x, y, z )
- //
- // [ 0 -z y ]
- // S = [ z 0 -x ]
- // [ -y x 0 ]
- //
- // [ 1 0 0 ]
- // I = [ 0 1 0 ]
- // [ 0 0 1 ]
- //
- //
- // [ xx+cosa*(1-xx) yx-cosa*yx-sina*z zx-cosa*xz+sina*y ]
- // M = [ xy-cosa*yx+sina*z yy+cosa(1-yy) yz-cosa*yz-sina*x ]
- // [ zx-cosa*zx-sina*y zy-cosa*zy+sina*x zz+cosa*(1-zz) ]
- //
- float x = axis.X, y = axis.Y, z = axis.Z;
- float sa = MathF.Sin(angle), ca = MathF.Cos(angle);
- float xx = x * x, yy = y * y, zz = z * z;
- float xy = x * y, xz = x * z, yz = y * z;
-
- Matrix4x4 result = Identity;
-
- result.M11 = xx + ca * (1.0f - xx);
- result.M12 = xy - ca * xy + sa * z;
- result.M13 = xz - ca * xz - sa * y;
-
- result.M21 = xy - ca * xy - sa * z;
- result.M22 = yy + ca * (1.0f - yy);
- result.M23 = yz - ca * yz + sa * x;
-
- result.M31 = xz - ca * xz + sa * y;
- result.M32 = yz - ca * yz - sa * x;
- result.M33 = zz + ca * (1.0f - zz);
-
- return result;
- }
+ => Impl.CreateFromAxisAngle(in axis, angle).AsM4x4();
/// Creates a rotation matrix from the specified Quaternion rotation value.
/// The source Quaternion.
/// The rotation matrix.
public static Matrix4x4 CreateFromQuaternion(Quaternion quaternion)
- {
- Matrix4x4 result = Identity;
-
- float xx = quaternion.X * quaternion.X;
- float yy = quaternion.Y * quaternion.Y;
- float zz = quaternion.Z * quaternion.Z;
-
- float xy = quaternion.X * quaternion.Y;
- float wz = quaternion.Z * quaternion.W;
- float xz = quaternion.Z * quaternion.X;
- float wy = quaternion.Y * quaternion.W;
- float yz = quaternion.Y * quaternion.Z;
- float wx = quaternion.X * quaternion.W;
-
- result.M11 = 1.0f - 2.0f * (yy + zz);
- result.M12 = 2.0f * (xy + wz);
- result.M13 = 2.0f * (xz - wy);
-
- result.M21 = 2.0f * (xy - wz);
- result.M22 = 1.0f - 2.0f * (zz + xx);
- result.M23 = 2.0f * (yz + wx);
-
- result.M31 = 2.0f * (xz + wy);
- result.M32 = 2.0f * (yz - wx);
- result.M33 = 1.0f - 2.0f * (yy + xx);
-
- return result;
- }
+ => Impl.CreateFromQuaternion(in quaternion).AsM4x4();
/// Creates a rotation matrix from the specified yaw, pitch, and roll.
/// The angle of rotation, in radians, around the Y axis.
@@ -792,10 +262,7 @@ public static Matrix4x4 CreateFromQuaternion(Quaternion quaternion)
/// The angle of rotation, in radians, around the Z axis.
/// The rotation matrix.
public static Matrix4x4 CreateFromYawPitchRoll(float yaw, float pitch, float roll)
- {
- Quaternion q = Quaternion.CreateFromYawPitchRoll(yaw, pitch, roll);
- return CreateFromQuaternion(q);
- }
+ => Impl.CreateFromYawPitchRoll(yaw, pitch, roll).AsM4x4();
/// Creates a view matrix.
/// The position of the camera.
@@ -803,31 +270,7 @@ public static Matrix4x4 CreateFromYawPitchRoll(float yaw, float pitch, float rol
/// The direction that is "up" from the camera's point of view.
/// The view matrix.
public static Matrix4x4 CreateLookAt(Vector3 cameraPosition, Vector3 cameraTarget, Vector3 cameraUpVector)
- {
- Vector3 zaxis = Vector3.Normalize(cameraPosition - cameraTarget);
- Vector3 xaxis = Vector3.Normalize(Vector3.Cross(cameraUpVector, zaxis));
- Vector3 yaxis = Vector3.Cross(zaxis, xaxis);
-
- Matrix4x4 result = Identity;
-
- result.M11 = xaxis.X;
- result.M12 = yaxis.X;
- result.M13 = zaxis.X;
-
- result.M21 = xaxis.Y;
- result.M22 = yaxis.Y;
- result.M23 = zaxis.Y;
-
- result.M31 = xaxis.Z;
- result.M32 = yaxis.Z;
- result.M33 = zaxis.Z;
-
- result.M41 = -Vector3.Dot(xaxis, cameraPosition);
- result.M42 = -Vector3.Dot(yaxis, cameraPosition);
- result.M43 = -Vector3.Dot(zaxis, cameraPosition);
-
- return result;
- }
+ => Impl.CreateLookAt(in cameraPosition, in cameraTarget, in cameraUpVector).AsM4x4();
/// Creates an orthographic perspective matrix from the given view volume dimensions.
/// The width of the view volume.
@@ -836,16 +279,7 @@ public static Matrix4x4 CreateLookAt(Vector3 cameraPosition, Vector3 cameraTarge
/// The maximum Z-value of the view volume.
/// The orthographic projection matrix.
public static Matrix4x4 CreateOrthographic(float width, float height, float zNearPlane, float zFarPlane)
- {
- Matrix4x4 result = Identity;
-
- result.M11 = 2.0f / width;
- result.M22 = 2.0f / height;
- result.M33 = 1.0f / (zNearPlane - zFarPlane);
- result.M43 = zNearPlane / (zNearPlane - zFarPlane);
-
- return result;
- }
+ => Impl.CreateOrthographic(width, height, zNearPlane, zFarPlane).AsM4x4();
/// Creates a customized orthographic projection matrix.
/// The minimum X-value of the view volume.
@@ -856,21 +290,7 @@ public static Matrix4x4 CreateOrthographic(float width, float height, float zNea
/// The maximum Z-value of the view volume.
/// The orthographic projection matrix.
public static Matrix4x4 CreateOrthographicOffCenter(float left, float right, float bottom, float top, float zNearPlane, float zFarPlane)
- {
- Matrix4x4 result = Identity;
-
- result.M11 = 2.0f / (right - left);
-
- result.M22 = 2.0f / (top - bottom);
-
- result.M33 = 1.0f / (zNearPlane - zFarPlane);
-
- result.M41 = (left + right) / (left - right);
- result.M42 = (top + bottom) / (bottom - top);
- result.M43 = zNearPlane / (zNearPlane - zFarPlane);
-
- return result;
- }
+ => Impl.CreateOrthographicOffCenter(left, right, bottom, top, zNearPlane, zFarPlane).AsM4x4();
/// Creates a perspective projection matrix from the given view volume dimensions.
/// The width of the view volume at the near view plane.
@@ -878,35 +298,13 @@ public static Matrix4x4 CreateOrthographicOffCenter(float left, float right, flo
/// The distance to the near view plane.
/// The distance to the far view plane.
/// The perspective projection matrix.
- /// is less than or equal to zero.
+ /// is less than or equal to zero.
/// -or-
/// is less than or equal to zero.
/// -or-
/// is greater than or equal to .
public static Matrix4x4 CreatePerspective(float width, float height, float nearPlaneDistance, float farPlaneDistance)
- {
- ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(nearPlaneDistance, 0.0f);
- ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(farPlaneDistance, 0.0f);
- ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(nearPlaneDistance, farPlaneDistance);
-
- Matrix4x4 result;
-
- result.M11 = 2.0f * nearPlaneDistance / width;
- result.M12 = result.M13 = result.M14 = 0.0f;
-
- result.M22 = 2.0f * nearPlaneDistance / height;
- result.M21 = result.M23 = result.M24 = 0.0f;
-
- float negFarRange = float.IsPositiveInfinity(farPlaneDistance) ? -1.0f : farPlaneDistance / (nearPlaneDistance - farPlaneDistance);
- result.M33 = negFarRange;
- result.M31 = result.M32 = 0.0f;
- result.M34 = -1.0f;
-
- result.M41 = result.M42 = result.M44 = 0.0f;
- result.M43 = nearPlaneDistance * negFarRange;
-
- return result;
- }
+ => Impl.CreatePerspective(width, height, nearPlaneDistance, farPlaneDistance).AsM4x4();
/// Creates a perspective projection matrix based on a field of view, aspect ratio, and near and far view plane distances.
/// The field of view in the y direction, in radians.
@@ -914,44 +312,16 @@ public static Matrix4x4 CreatePerspective(float width, float height, float nearP
/// The distance to the near view plane.
/// The distance to the far view plane.
/// The perspective projection matrix.
- /// is less than or equal to zero.
+ /// is less than or equal to zero.
/// -or-
- /// is greater than or equal to .
+ /// is greater than or equal to .
/// is less than or equal to zero.
/// -or-
/// is less than or equal to zero.
/// -or-
/// is greater than or equal to .
public static Matrix4x4 CreatePerspectiveFieldOfView(float fieldOfView, float aspectRatio, float nearPlaneDistance, float farPlaneDistance)
- {
- ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(fieldOfView, 0.0f);
- ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(fieldOfView, MathF.PI);
-
- ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(nearPlaneDistance, 0.0f);
- ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(farPlaneDistance, 0.0f);
- ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(nearPlaneDistance, farPlaneDistance);
-
- float yScale = 1.0f / MathF.Tan(fieldOfView * 0.5f);
- float xScale = yScale / aspectRatio;
-
- Matrix4x4 result;
-
- result.M11 = xScale;
- result.M12 = result.M13 = result.M14 = 0.0f;
-
- result.M22 = yScale;
- result.M21 = result.M23 = result.M24 = 0.0f;
-
- result.M31 = result.M32 = 0.0f;
- float negFarRange = float.IsPositiveInfinity(farPlaneDistance) ? -1.0f : farPlaneDistance / (nearPlaneDistance - farPlaneDistance);
- result.M33 = negFarRange;
- result.M34 = -1.0f;
-
- result.M41 = result.M42 = result.M44 = 0.0f;
- result.M43 = nearPlaneDistance * negFarRange;
-
- return result;
- }
+ => Impl.CreatePerspectiveFieldOfView(fieldOfView, aspectRatio, nearPlaneDistance, farPlaneDistance).AsM4x4();
/// Creates a customized perspective projection matrix.
/// The minimum x-value of the view volume at the near view plane.
@@ -961,224 +331,58 @@ public static Matrix4x4 CreatePerspectiveFieldOfView(float fieldOfView, float as
/// The distance to the near view plane.
/// The distance to the far view plane.
/// The perspective projection matrix.
- /// is less than or equal to zero.
+ /// is less than or equal to zero.
/// -or-
/// is less than or equal to zero.
/// -or-
/// is greater than or equal to .
public static Matrix4x4 CreatePerspectiveOffCenter(float left, float right, float bottom, float top, float nearPlaneDistance, float farPlaneDistance)
- {
- ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(nearPlaneDistance, 0.0f);
- ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(farPlaneDistance, 0.0f);
- ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(nearPlaneDistance, farPlaneDistance);
-
- Matrix4x4 result;
-
- result.M11 = 2.0f * nearPlaneDistance / (right - left);
- result.M12 = result.M13 = result.M14 = 0.0f;
-
- result.M22 = 2.0f * nearPlaneDistance / (top - bottom);
- result.M21 = result.M23 = result.M24 = 0.0f;
-
- result.M31 = (left + right) / (right - left);
- result.M32 = (top + bottom) / (top - bottom);
- float negFarRange = float.IsPositiveInfinity(farPlaneDistance) ? -1.0f : farPlaneDistance / (nearPlaneDistance - farPlaneDistance);
- result.M33 = negFarRange;
- result.M34 = -1.0f;
-
- result.M43 = nearPlaneDistance * negFarRange;
- result.M41 = result.M42 = result.M44 = 0.0f;
-
- return result;
- }
+ => Impl.CreatePerspectiveOffCenter(left, right, bottom, top, nearPlaneDistance, farPlaneDistance).AsM4x4();
/// Creates a matrix that reflects the coordinate system about a specified plane.
/// The plane about which to create a reflection.
/// A new matrix expressing the reflection.
public static Matrix4x4 CreateReflection(Plane value)
- {
- value = Plane.Normalize(value);
-
- float a = value.Normal.X;
- float b = value.Normal.Y;
- float c = value.Normal.Z;
-
- float fa = -2.0f * a;
- float fb = -2.0f * b;
- float fc = -2.0f * c;
-
- Matrix4x4 result = Identity;
-
- result.M11 = fa * a + 1.0f;
- result.M12 = fb * a;
- result.M13 = fc * a;
-
- result.M21 = fa * b;
- result.M22 = fb * b + 1.0f;
- result.M23 = fc * b;
-
- result.M31 = fa * c;
- result.M32 = fb * c;
- result.M33 = fc * c + 1.0f;
-
- result.M41 = fa * value.D;
- result.M42 = fb * value.D;
- result.M43 = fc * value.D;
-
- return result;
- }
+ => Impl.CreateReflection(in value).AsM4x4();
/// Creates a matrix for rotating points around the X axis.
/// The amount, in radians, by which to rotate around the X axis.
/// The rotation matrix.
public static Matrix4x4 CreateRotationX(float radians)
- {
- Matrix4x4 result = Identity;
-
- float c = MathF.Cos(radians);
- float s = MathF.Sin(radians);
-
- // [ 1 0 0 0 ]
- // [ 0 c s 0 ]
- // [ 0 -s c 0 ]
- // [ 0 0 0 1 ]
-
- result.M22 = c;
- result.M23 = s;
- result.M32 = -s;
- result.M33 = c;
-
- return result;
- }
+ => Impl.CreateRotationX(radians).AsM4x4();
/// Creates a matrix for rotating points around the X axis from a center point.
/// The amount, in radians, by which to rotate around the X axis.
/// The center point.
/// The rotation matrix.
public static Matrix4x4 CreateRotationX(float radians, Vector3 centerPoint)
- {
- Matrix4x4 result = Identity;
-
- float c = MathF.Cos(radians);
- float s = MathF.Sin(radians);
-
- float y = centerPoint.Y * (1 - c) + centerPoint.Z * s;
- float z = centerPoint.Z * (1 - c) - centerPoint.Y * s;
-
- // [ 1 0 0 0 ]
- // [ 0 c s 0 ]
- // [ 0 -s c 0 ]
- // [ 0 y z 1 ]
-
- result.M22 = c;
- result.M23 = s;
- result.M32 = -s;
- result.M33 = c;
- result.M42 = y;
- result.M43 = z;
-
- return result;
- }
+ => Impl.CreateRotationX(radians, in centerPoint).AsM4x4();
/// Creates a matrix for rotating points around the Y axis.
/// The amount, in radians, by which to rotate around the Y-axis.
/// The rotation matrix.
public static Matrix4x4 CreateRotationY(float radians)
- {
- Matrix4x4 result = Identity;
-
- float c = MathF.Cos(radians);
- float s = MathF.Sin(radians);
-
- // [ c 0 -s 0 ]
- // [ 0 1 0 0 ]
- // [ s 0 c 0 ]
- // [ 0 0 0 1 ]
- result.M11 = c;
- result.M13 = -s;
- result.M31 = s;
- result.M33 = c;
-
- return result;
- }
+ => Impl.CreateRotationY(radians).AsM4x4();
/// The amount, in radians, by which to rotate around the Y axis from a center point.
/// The amount, in radians, by which to rotate around the Y-axis.
/// The center point.
/// The rotation matrix.
public static Matrix4x4 CreateRotationY(float radians, Vector3 centerPoint)
- {
- Matrix4x4 result = Identity;
-
- float c = MathF.Cos(radians);
- float s = MathF.Sin(radians);
-
- float x = centerPoint.X * (1 - c) - centerPoint.Z * s;
- float z = centerPoint.Z * (1 - c) + centerPoint.X * s;
-
- // [ c 0 -s 0 ]
- // [ 0 1 0 0 ]
- // [ s 0 c 0 ]
- // [ x 0 z 1 ]
- result.M11 = c;
- result.M13 = -s;
- result.M31 = s;
- result.M33 = c;
- result.M41 = x;
- result.M43 = z;
-
- return result;
- }
+ => Impl.CreateRotationY(radians, in centerPoint).AsM4x4();
/// Creates a matrix for rotating points around the Z axis.
/// The amount, in radians, by which to rotate around the Z-axis.
/// The rotation matrix.
public static Matrix4x4 CreateRotationZ(float radians)
- {
- Matrix4x4 result = Identity;
-
- float c = MathF.Cos(radians);
- float s = MathF.Sin(radians);
-
- // [ c s 0 0 ]
- // [ -s c 0 0 ]
- // [ 0 0 1 0 ]
- // [ 0 0 0 1 ]
- result.M11 = c;
- result.M12 = s;
- result.M21 = -s;
- result.M22 = c;
-
- return result;
- }
+ => Impl.CreateRotationZ(radians).AsM4x4();
/// Creates a matrix for rotating points around the Z axis from a center point.
/// The amount, in radians, by which to rotate around the Z-axis.
/// The center point.
/// The rotation matrix.
public static Matrix4x4 CreateRotationZ(float radians, Vector3 centerPoint)
- {
- Matrix4x4 result = Identity;
-
- float c = MathF.Cos(radians);
- float s = MathF.Sin(radians);
-
- float x = centerPoint.X * (1 - c) + centerPoint.Y * s;
- float y = centerPoint.Y * (1 - c) - centerPoint.X * s;
-
- // [ c s 0 0 ]
- // [ -s c 0 0 ]
- // [ 0 0 1 0 ]
- // [ x y 0 1 ]
- result.M11 = c;
- result.M12 = s;
- result.M21 = -s;
- result.M22 = c;
- result.M41 = x;
- result.M42 = y;
-
- return result;
- }
+ => Impl.CreateRotationZ(radians, in centerPoint).AsM4x4();
/// Creates a scaling matrix from the specified X, Y, and Z components.
/// The value to scale by on the X axis.
@@ -1186,16 +390,7 @@ public static Matrix4x4 CreateRotationZ(float radians, Vector3 centerPoint)
/// The value to scale by on the Z axis.
/// The scaling matrix.
public static Matrix4x4 CreateScale(float xScale, float yScale, float zScale)
- {
- Matrix4x4 result = default;
-
- result.M11 = xScale;
- result.M22 = yScale;
- result.M33 = zScale;
- result.M44 = 1;
-
- return result;
- }
+ => Impl.CreateScale(xScale, yScale, zScale).AsM4x4();
/// Creates a scaling matrix that is offset by a given center point.
/// The value to scale by on the X axis.
@@ -1204,146 +399,46 @@ public static Matrix4x4 CreateScale(float xScale, float yScale, float zScale)
/// The center point.
/// The scaling matrix.
public static Matrix4x4 CreateScale(float xScale, float yScale, float zScale, Vector3 centerPoint)
- {
- Matrix4x4 result = default;
-
- float tx = centerPoint.X * (1 - xScale);
- float ty = centerPoint.Y * (1 - yScale);
- float tz = centerPoint.Z * (1 - zScale);
-
- result.M11 = xScale;
- result.M22 = yScale;
- result.M33 = zScale;
- result.M44 = 1;
-
- result.M41 = tx;
- result.M42 = ty;
- result.M43 = tz;
-
- return result;
- }
+ => Impl.CreateScale(xScale, yScale, zScale, in centerPoint).AsM4x4();
/// Creates a scaling matrix from the specified vector scale.
/// The scale to use.
/// The scaling matrix.
public static Matrix4x4 CreateScale(Vector3 scales)
- {
- Matrix4x4 result = default;
-
- result.M11 = scales.X;
- result.M22 = scales.Y;
- result.M33 = scales.Z;
- result.M44 = 1;
-
- return result;
- }
+ => Impl.CreateScale(in scales).AsM4x4();
/// Creates a scaling matrix with a center point.
/// The vector that contains the amount to scale on each axis.
/// The center point.
/// The scaling matrix.
public static Matrix4x4 CreateScale(Vector3 scales, Vector3 centerPoint)
- {
- Matrix4x4 result = default;
-
- Vector3 t = centerPoint * (Vector3.One - scales);
-
- result.M11 = scales.X;
- result.M22 = scales.Y;
- result.M33 = scales.Z;
- result.M44 = 1;
-
- result.M41 = t.X;
- result.M42 = t.Y;
- result.M43 = t.Z;
-
- return result;
- }
+ => Impl.CreateScale(scales, in centerPoint).AsM4x4();
/// Creates a uniform scaling matrix that scale equally on each axis.
/// The uniform scaling factor.
/// The scaling matrix.
public static Matrix4x4 CreateScale(float scale)
- {
- Matrix4x4 result = default;
-
- result.M11 = scale;
- result.M22 = scale;
- result.M33 = scale;
- result.M44 = 1;
-
- return result;
- }
+ => Impl.CreateScale(scale).AsM4x4();
/// Creates a uniform scaling matrix that scales equally on each axis with a center point.
/// The uniform scaling factor.
/// The center point.
/// The scaling matrix.
public static Matrix4x4 CreateScale(float scale, Vector3 centerPoint)
- {
- Matrix4x4 result = default;
-
- Vector3 t = centerPoint * (Vector3.One - new Vector3(scale));
-
- result.M11 = scale;
- result.M22 = scale;
- result.M33 = scale;
- result.M44 = 1;
-
- result.M41 = t.X;
- result.M42 = t.Y;
- result.M43 = t.Z;
-
- return result;
- }
+ => Impl.CreateScale(scale, in centerPoint).AsM4x4();
/// Creates a matrix that flattens geometry into a specified plane as if casting a shadow from a specified light source.
/// The direction from which the light that will cast the shadow is coming.
/// The plane onto which the new matrix should flatten geometry so as to cast a shadow.
/// A new matrix that can be used to flatten geometry onto the specified plane from the specified direction.
public static Matrix4x4 CreateShadow(Vector3 lightDirection, Plane plane)
- {
- Plane p = Plane.Normalize(plane);
-
- float dot = p.Normal.X * lightDirection.X + p.Normal.Y * lightDirection.Y + p.Normal.Z * lightDirection.Z;
- float a = -p.Normal.X;
- float b = -p.Normal.Y;
- float c = -p.Normal.Z;
- float d = -p.D;
-
- Matrix4x4 result = Identity;
-
- result.M11 = a * lightDirection.X + dot;
- result.M21 = b * lightDirection.X;
- result.M31 = c * lightDirection.X;
- result.M41 = d * lightDirection.X;
-
- result.M12 = a * lightDirection.Y;
- result.M22 = b * lightDirection.Y + dot;
- result.M32 = c * lightDirection.Y;
- result.M42 = d * lightDirection.Y;
-
- result.M13 = a * lightDirection.Z;
- result.M23 = b * lightDirection.Z;
- result.M33 = c * lightDirection.Z + dot;
- result.M43 = d * lightDirection.Z;
-
- result.M44 = dot;
-
- return result;
- }
+ => Impl.CreateShadow(in lightDirection, in plane).AsM4x4();
/// Creates a translation matrix from the specified 3-dimensional vector.
/// The amount to translate in each axis.
/// The translation matrix.
public static Matrix4x4 CreateTranslation(Vector3 position)
- {
- Matrix4x4 result = Identity;
- result.M41 = position.X;
- result.M42 = position.Y;
- result.M43 = position.Z;
- return result;
- }
+ => Impl.CreateTranslation(in position).AsM4x4();
/// Creates a translation matrix from the specified X, Y, and Z components.
/// The amount to translate on the X axis.
@@ -1351,13 +446,7 @@ public static Matrix4x4 CreateTranslation(Vector3 position)
/// The amount to translate on the Z axis.
/// The translation matrix.
public static Matrix4x4 CreateTranslation(float xPosition, float yPosition, float zPosition)
- {
- Matrix4x4 result = Identity;
- result.M41 = xPosition;
- result.M42 = yPosition;
- result.M43 = zPosition;
- return result;
- }
+ => Impl.CreateTranslation(xPosition, yPosition, zPosition).AsM4x4();
/// Creates a world matrix with the specified parameters.
/// The position of the object.
@@ -1366,387 +455,42 @@ public static Matrix4x4 CreateTranslation(float xPosition, float yPosition, floa
/// The world matrix.
/// is used in translation operations.
public static Matrix4x4 CreateWorld(Vector3 position, Vector3 forward, Vector3 up)
- {
- Vector3 zaxis = Vector3.Normalize(-forward);
- Vector3 xaxis = Vector3.Normalize(Vector3.Cross(up, zaxis));
- Vector3 yaxis = Vector3.Cross(zaxis, xaxis);
-
- Matrix4x4 result = Identity;
-
- result.M11 = xaxis.X;
- result.M12 = xaxis.Y;
- result.M13 = xaxis.Z;
+ => Impl.CreateWorld(in position, in forward, in up).AsM4x4();
- result.M21 = yaxis.X;
- result.M22 = yaxis.Y;
- result.M23 = yaxis.Z;
-
- result.M31 = zaxis.X;
- result.M32 = zaxis.Y;
- result.M33 = zaxis.Z;
-
- result.M41 = position.X;
- result.M42 = position.Y;
- result.M43 = position.Z;
-
- return result;
- }
+ /// Attempts to extract the scale, translation, and rotation components from the given scale, rotation, or translation matrix. The return value indicates whether the operation succeeded.
+ /// The source matrix.
+ /// When this method returns, contains the scaling component of the transformation matrix if the operation succeeded.
+ /// When this method returns, contains the rotation component of the transformation matrix if the operation succeeded.
+ /// When the method returns, contains the translation component of the transformation matrix if the operation succeeded.
+ /// if was decomposed successfully; otherwise, .
+ public static unsafe bool Decompose(Matrix4x4 matrix, out Vector3 scale, out Quaternion rotation, out Vector3 translation)
+ => Impl.Decompose(in matrix.AsImpl(), out scale, out rotation, out translation);
/// Tries to invert the specified matrix. The return value indicates whether the operation succeeded.
/// The matrix to invert.
/// When this method returns, contains the inverted matrix if the operation succeeded.
/// if was converted successfully; otherwise, .
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static unsafe bool Invert(Matrix4x4 matrix, out Matrix4x4 result)
+ public static bool Invert(Matrix4x4 matrix, out Matrix4x4 result)
{
- // This implementation is based on the DirectX Math Library XMMatrixInverse method
- // https://github.com/microsoft/DirectXMath/blob/master/Inc/DirectXMathMatrix.inl
-
- if (Sse.IsSupported)
- {
- return SseImpl(matrix, out result);
- }
-
- return SoftwareFallback(matrix, out result);
-
- static unsafe bool SseImpl(Matrix4x4 matrix, out Matrix4x4 result)
- {
- if (!Sse.IsSupported)
- {
- // Redundant test so we won't prejit remainder of this method on platforms without SSE.
- throw new PlatformNotSupportedException();
- }
-
- // Load the matrix values into rows
- Vector128 row1 = Sse.LoadVector128(&matrix.M11);
- Vector128 row2 = Sse.LoadVector128(&matrix.M21);
- Vector128 row3 = Sse.LoadVector128(&matrix.M31);
- Vector128 row4 = Sse.LoadVector128(&matrix.M41);
-
- // Transpose the matrix
- Vector128 vTemp1 = Sse.Shuffle(row1, row2, 0x44); //_MM_SHUFFLE(1, 0, 1, 0)
- Vector128 vTemp3 = Sse.Shuffle(row1, row2, 0xEE); //_MM_SHUFFLE(3, 2, 3, 2)
- Vector128 vTemp2 = Sse.Shuffle(row3, row4, 0x44); //_MM_SHUFFLE(1, 0, 1, 0)
- Vector128 vTemp4 = Sse.Shuffle(row3, row4, 0xEE); //_MM_SHUFFLE(3, 2, 3, 2)
-
- row1 = Sse.Shuffle(vTemp1, vTemp2, 0x88); //_MM_SHUFFLE(2, 0, 2, 0)
- row2 = Sse.Shuffle(vTemp1, vTemp2, 0xDD); //_MM_SHUFFLE(3, 1, 3, 1)
- row3 = Sse.Shuffle(vTemp3, vTemp4, 0x88); //_MM_SHUFFLE(2, 0, 2, 0)
- row4 = Sse.Shuffle(vTemp3, vTemp4, 0xDD); //_MM_SHUFFLE(3, 1, 3, 1)
-
- Vector128 V00 = Permute(row3, 0x50); //_MM_SHUFFLE(1, 1, 0, 0)
- Vector128 V10 = Permute(row4, 0xEE); //_MM_SHUFFLE(3, 2, 3, 2)
- Vector128 V01 = Permute(row1, 0x50); //_MM_SHUFFLE(1, 1, 0, 0)
- Vector128 V11 = Permute(row2, 0xEE); //_MM_SHUFFLE(3, 2, 3, 2)
- Vector128 V02 = Sse.Shuffle(row3, row1, 0x88); //_MM_SHUFFLE(2, 0, 2, 0)
- Vector128 V12 = Sse.Shuffle(row4, row2, 0xDD); //_MM_SHUFFLE(3, 1, 3, 1)
-
- Vector128 D0 = Sse.Multiply(V00, V10);
- Vector128 D1 = Sse.Multiply(V01, V11);
- Vector128 D2 = Sse.Multiply(V02, V12);
-
- V00 = Permute(row3, 0xEE); //_MM_SHUFFLE(3, 2, 3, 2)
- V10 = Permute(row4, 0x50); //_MM_SHUFFLE(1, 1, 0, 0)
- V01 = Permute(row1, 0xEE); //_MM_SHUFFLE(3, 2, 3, 2)
- V11 = Permute(row2, 0x50); //_MM_SHUFFLE(1, 1, 0, 0)
- V02 = Sse.Shuffle(row3, row1, 0xDD); //_MM_SHUFFLE(3, 1, 3, 1)
- V12 = Sse.Shuffle(row4, row2, 0x88); //_MM_SHUFFLE(2, 0, 2, 0)
-
- // Note: We use this expansion pattern instead of Fused Multiply Add
- // in order to support older hardware
- D0 = Sse.Subtract(D0, Sse.Multiply(V00, V10));
- D1 = Sse.Subtract(D1, Sse.Multiply(V01, V11));
- D2 = Sse.Subtract(D2, Sse.Multiply(V02, V12));
-
- // V11 = D0Y,D0W,D2Y,D2Y
- V11 = Sse.Shuffle(D0, D2, 0x5D); //_MM_SHUFFLE(1, 1, 3, 1)
- V00 = Permute(row2, 0x49); //_MM_SHUFFLE(1, 0, 2, 1)
- V10 = Sse.Shuffle(V11, D0, 0x32); //_MM_SHUFFLE(0, 3, 0, 2)
- V01 = Permute(row1, 0x12); //_MM_SHUFFLE(0, 1, 0, 2)
- V11 = Sse.Shuffle(V11, D0, 0x99); //_MM_SHUFFLE(2, 1, 2, 1)
-
- // V13 = D1Y,D1W,D2W,D2W
- Vector128 V13 = Sse.Shuffle(D1, D2, 0xFD); //_MM_SHUFFLE(3, 3, 3, 1)
- V02 = Permute(row4, 0x49); //_MM_SHUFFLE(1, 0, 2, 1)
- V12 = Sse.Shuffle(V13, D1, 0x32); //_MM_SHUFFLE(0, 3, 0, 2)
- Vector128 V03 = Permute(row3, 0x12); //_MM_SHUFFLE(0, 1, 0, 2)
- V13 = Sse.Shuffle(V13, D1, 0x99); //_MM_SHUFFLE(2, 1, 2, 1)
-
- Vector128 C0 = Sse.Multiply(V00, V10);
- Vector128 C2 = Sse.Multiply(V01, V11);
- Vector128 C4 = Sse.Multiply(V02, V12);
- Vector128 C6 = Sse.Multiply(V03, V13);
-
- // V11 = D0X,D0Y,D2X,D2X
- V11 = Sse.Shuffle(D0, D2, 0x4); //_MM_SHUFFLE(0, 0, 1, 0)
- V00 = Permute(row2, 0x9e); //_MM_SHUFFLE(2, 1, 3, 2)
- V10 = Sse.Shuffle(D0, V11, 0x93); //_MM_SHUFFLE(2, 1, 0, 3)
- V01 = Permute(row1, 0x7b); //_MM_SHUFFLE(1, 3, 2, 3)
- V11 = Sse.Shuffle(D0, V11, 0x26); //_MM_SHUFFLE(0, 2, 1, 2)
-
- // V13 = D1X,D1Y,D2Z,D2Z
- V13 = Sse.Shuffle(D1, D2, 0xa4); //_MM_SHUFFLE(2, 2, 1, 0)
- V02 = Permute(row4, 0x9e); //_MM_SHUFFLE(2, 1, 3, 2)
- V12 = Sse.Shuffle(D1, V13, 0x93); //_MM_SHUFFLE(2, 1, 0, 3)
- V03 = Permute(row3, 0x7b); //_MM_SHUFFLE(1, 3, 2, 3)
- V13 = Sse.Shuffle(D1, V13, 0x26); //_MM_SHUFFLE(0, 2, 1, 2)
-
- C0 = Sse.Subtract(C0, Sse.Multiply(V00, V10));
- C2 = Sse.Subtract(C2, Sse.Multiply(V01, V11));
- C4 = Sse.Subtract(C4, Sse.Multiply(V02, V12));
- C6 = Sse.Subtract(C6, Sse.Multiply(V03, V13));
-
- V00 = Permute(row2, 0x33); //_MM_SHUFFLE(0, 3, 0, 3)
-
- // V10 = D0Z,D0Z,D2X,D2Y
- V10 = Sse.Shuffle(D0, D2, 0x4A); //_MM_SHUFFLE(1, 0, 2, 2)
- V10 = Permute(V10, 0x2C); //_MM_SHUFFLE(0, 2, 3, 0)
- V01 = Permute(row1, 0x8D); //_MM_SHUFFLE(2, 0, 3, 1)
-
- // V11 = D0X,D0W,D2X,D2Y
- V11 = Sse.Shuffle(D0, D2, 0x4C); //_MM_SHUFFLE(1, 0, 3, 0)
- V11 = Permute(V11, 0x93); //_MM_SHUFFLE(2, 1, 0, 3)
- V02 = Permute(row4, 0x33); //_MM_SHUFFLE(0, 3, 0, 3)
-
- // V12 = D1Z,D1Z,D2Z,D2W
- V12 = Sse.Shuffle(D1, D2, 0xEA); //_MM_SHUFFLE(3, 2, 2, 2)
- V12 = Permute(V12, 0x2C); //_MM_SHUFFLE(0, 2, 3, 0)
- V03 = Permute(row3, 0x8D); //_MM_SHUFFLE(2, 0, 3, 1)
-
- // V13 = D1X,D1W,D2Z,D2W
- V13 = Sse.Shuffle(D1, D2, 0xEC); //_MM_SHUFFLE(3, 2, 3, 0)
- V13 = Permute(V13, 0x93); //_MM_SHUFFLE(2, 1, 0, 3)
-
- V00 = Sse.Multiply(V00, V10);
- V01 = Sse.Multiply(V01, V11);
- V02 = Sse.Multiply(V02, V12);
- V03 = Sse.Multiply(V03, V13);
-
- Vector128 C1 = Sse.Subtract(C0, V00);
- C0 = Sse.Add(C0, V00);
- Vector128 C3 = Sse.Add(C2, V01);
- C2 = Sse.Subtract(C2, V01);
- Vector128 C5 = Sse.Subtract(C4, V02);
- C4 = Sse.Add(C4, V02);
- Vector128 C7 = Sse.Add(C6, V03);
- C6 = Sse.Subtract(C6, V03);
-
- C0 = Sse.Shuffle(C0, C1, 0xD8); //_MM_SHUFFLE(3, 1, 2, 0)
- C2 = Sse.Shuffle(C2, C3, 0xD8); //_MM_SHUFFLE(3, 1, 2, 0)
- C4 = Sse.Shuffle(C4, C5, 0xD8); //_MM_SHUFFLE(3, 1, 2, 0)
- C6 = Sse.Shuffle(C6, C7, 0xD8); //_MM_SHUFFLE(3, 1, 2, 0)
-
- C0 = Permute(C0, 0xD8); //_MM_SHUFFLE(3, 1, 2, 0)
- C2 = Permute(C2, 0xD8); //_MM_SHUFFLE(3, 1, 2, 0)
- C4 = Permute(C4, 0xD8); //_MM_SHUFFLE(3, 1, 2, 0)
- C6 = Permute(C6, 0xD8); //_MM_SHUFFLE(3, 1, 2, 0)
-
- // Get the determinant
- vTemp2 = row1;
- float det = Vector4.Dot(C0.AsVector4(), vTemp2.AsVector4());
-
- // Check determinate is not zero
- if (MathF.Abs(det) < float.Epsilon)
- {
- result = new Matrix4x4(float.NaN, float.NaN, float.NaN, float.NaN,
- float.NaN, float.NaN, float.NaN, float.NaN,
- float.NaN, float.NaN, float.NaN, float.NaN,
- float.NaN, float.NaN, float.NaN, float.NaN);
- return false;
- }
-
- // Create Vector128 copy of the determinant and invert them.
- Vector128 ones = Vector128.Create(1.0f);
- Vector128 vTemp = Vector128.Create(det);
- vTemp = Sse.Divide(ones, vTemp);
-
- row1 = Sse.Multiply(C0, vTemp);
- row2 = Sse.Multiply(C2, vTemp);
- row3 = Sse.Multiply(C4, vTemp);
- row4 = Sse.Multiply(C6, vTemp);
-
- Unsafe.SkipInit(out result);
- ref Vector128 vResult = ref Unsafe.As>(ref result);
-
- vResult = row1;
- Unsafe.Add(ref vResult, 1) = row2;
- Unsafe.Add(ref vResult, 2) = row3;
- Unsafe.Add(ref vResult, 3) = row4;
-
- return true;
- }
-
- static bool SoftwareFallback(Matrix4x4 matrix, out Matrix4x4 result)
- {
- // -1
- // If you have matrix M, inverse Matrix M can compute
- //
- // -1 1
- // M = --------- A
- // det(M)
- //
- // A is adjugate (adjoint) of M, where,
- //
- // T
- // A = C
- //
- // C is Cofactor matrix of M, where,
- // i + j
- // C = (-1) * det(M )
- // ij ij
- //
- // [ a b c d ]
- // M = [ e f g h ]
- // [ i j k l ]
- // [ m n o p ]
- //
- // First Row
- // 2 | f g h |
- // C = (-1) | j k l | = + ( f ( kp - lo ) - g ( jp - ln ) + h ( jo - kn ) )
- // 11 | n o p |
- //
- // 3 | e g h |
- // C = (-1) | i k l | = - ( e ( kp - lo ) - g ( ip - lm ) + h ( io - km ) )
- // 12 | m o p |
- //
- // 4 | e f h |
- // C = (-1) | i j l | = + ( e ( jp - ln ) - f ( ip - lm ) + h ( in - jm ) )
- // 13 | m n p |
- //
- // 5 | e f g |
- // C = (-1) | i j k | = - ( e ( jo - kn ) - f ( io - km ) + g ( in - jm ) )
- // 14 | m n o |
- //
- // Second Row
- // 3 | b c d |
- // C = (-1) | j k l | = - ( b ( kp - lo ) - c ( jp - ln ) + d ( jo - kn ) )
- // 21 | n o p |
- //
- // 4 | a c d |
- // C = (-1) | i k l | = + ( a ( kp - lo ) - c ( ip - lm ) + d ( io - km ) )
- // 22 | m o p |
- //
- // 5 | a b d |
- // C = (-1) | i j l | = - ( a ( jp - ln ) - b ( ip - lm ) + d ( in - jm ) )
- // 23 | m n p |
- //
- // 6 | a b c |
- // C = (-1) | i j k | = + ( a ( jo - kn ) - b ( io - km ) + c ( in - jm ) )
- // 24 | m n o |
- //
- // Third Row
- // 4 | b c d |
- // C = (-1) | f g h | = + ( b ( gp - ho ) - c ( fp - hn ) + d ( fo - gn ) )
- // 31 | n o p |
- //
- // 5 | a c d |
- // C = (-1) | e g h | = - ( a ( gp - ho ) - c ( ep - hm ) + d ( eo - gm ) )
- // 32 | m o p |
- //
- // 6 | a b d |
- // C = (-1) | e f h | = + ( a ( fp - hn ) - b ( ep - hm ) + d ( en - fm ) )
- // 33 | m n p |
- //
- // 7 | a b c |
- // C = (-1) | e f g | = - ( a ( fo - gn ) - b ( eo - gm ) + c ( en - fm ) )
- // 34 | m n o |
- //
- // Fourth Row
- // 5 | b c d |
- // C = (-1) | f g h | = - ( b ( gl - hk ) - c ( fl - hj ) + d ( fk - gj ) )
- // 41 | j k l |
- //
- // 6 | a c d |
- // C = (-1) | e g h | = + ( a ( gl - hk ) - c ( el - hi ) + d ( ek - gi ) )
- // 42 | i k l |
- //
- // 7 | a b d |
- // C = (-1) | e f h | = - ( a ( fl - hj ) - b ( el - hi ) + d ( ej - fi ) )
- // 43 | i j l |
- //
- // 8 | a b c |
- // C = (-1) | e f g | = + ( a ( fk - gj ) - b ( ek - gi ) + c ( ej - fi ) )
- // 44 | i j k |
- //
- // Cost of operation
- // 53 adds, 104 muls, and 1 div.
- float a = matrix.M11, b = matrix.M12, c = matrix.M13, d = matrix.M14;
- float e = matrix.M21, f = matrix.M22, g = matrix.M23, h = matrix.M24;
- float i = matrix.M31, j = matrix.M32, k = matrix.M33, l = matrix.M34;
- float m = matrix.M41, n = matrix.M42, o = matrix.M43, p = matrix.M44;
-
- float kp_lo = k * p - l * o;
- float jp_ln = j * p - l * n;
- float jo_kn = j * o - k * n;
- float ip_lm = i * p - l * m;
- float io_km = i * o - k * m;
- float in_jm = i * n - j * m;
-
- float a11 = +(f * kp_lo - g * jp_ln + h * jo_kn);
- float a12 = -(e * kp_lo - g * ip_lm + h * io_km);
- float a13 = +(e * jp_ln - f * ip_lm + h * in_jm);
- float a14 = -(e * jo_kn - f * io_km + g * in_jm);
-
- float det = a * a11 + b * a12 + c * a13 + d * a14;
-
- if (MathF.Abs(det) < float.Epsilon)
- {
- result = new Matrix4x4(float.NaN, float.NaN, float.NaN, float.NaN,
- float.NaN, float.NaN, float.NaN, float.NaN,
- float.NaN, float.NaN, float.NaN, float.NaN,
- float.NaN, float.NaN, float.NaN, float.NaN);
- return false;
- }
-
- float invDet = 1.0f / det;
-
- result.M11 = a11 * invDet;
- result.M21 = a12 * invDet;
- result.M31 = a13 * invDet;
- result.M41 = a14 * invDet;
-
- result.M12 = -(b * kp_lo - c * jp_ln + d * jo_kn) * invDet;
- result.M22 = +(a * kp_lo - c * ip_lm + d * io_km) * invDet;
- result.M32 = -(a * jp_ln - b * ip_lm + d * in_jm) * invDet;
- result.M42 = +(a * jo_kn - b * io_km + c * in_jm) * invDet;
-
- float gp_ho = g * p - h * o;
- float fp_hn = f * p - h * n;
- float fo_gn = f * o - g * n;
- float ep_hm = e * p - h * m;
- float eo_gm = e * o - g * m;
- float en_fm = e * n - f * m;
-
- result.M13 = +(b * gp_ho - c * fp_hn + d * fo_gn) * invDet;
- result.M23 = -(a * gp_ho - c * ep_hm + d * eo_gm) * invDet;
- result.M33 = +(a * fp_hn - b * ep_hm + d * en_fm) * invDet;
- result.M43 = -(a * fo_gn - b * eo_gm + c * en_fm) * invDet;
-
- float gl_hk = g * l - h * k;
- float fl_hj = f * l - h * j;
- float fk_gj = f * k - g * j;
- float el_hi = e * l - h * i;
- float ek_gi = e * k - g * i;
- float ej_fi = e * j - f * i;
-
- result.M14 = -(b * gl_hk - c * fl_hj + d * fk_gj) * invDet;
- result.M24 = +(a * gl_hk - c * el_hi + d * ek_gi) * invDet;
- result.M34 = -(a * fl_hj - b * el_hi + d * ej_fi) * invDet;
- result.M44 = +(a * fk_gj - b * ek_gi + c * ej_fi) * invDet;
-
- return true;
- }
+ Unsafe.SkipInit(out result);
+ return Impl.Invert(in matrix.AsImpl(), out result.AsImpl());
}
+ /// Performs a linear interpolation from one matrix to a second matrix based on a value that specifies the weighting of the second matrix.
+ /// The first matrix.
+ /// The second matrix.
+ /// The relative weighting of .
+ /// The interpolated matrix.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Matrix4x4 Lerp(Matrix4x4 matrix1, Matrix4x4 matrix2, float amount)
+ => Impl.Lerp(in matrix1.AsImpl(), in matrix2.AsImpl(), amount).AsM4x4();
+
/// Multiplies two matrices together to compute the product.
/// The first matrix.
/// The second matrix.
/// The product matrix.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Matrix4x4 Multiply(Matrix4x4 value1, Matrix4x4 value2)
- {
- return value1 * value2;
- }
+ => (value1.AsImpl() * value2.AsImpl()).AsM4x4();
/// Multiplies a matrix by a float to compute the product.
/// The matrix to scale.
@@ -1754,18 +498,14 @@ public static Matrix4x4 Multiply(Matrix4x4 value1, Matrix4x4 value2)
/// The scaled matrix.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Matrix4x4 Multiply(Matrix4x4 value1, float value2)
- {
- return value1 * value2;
- }
+ => (value1.AsImpl() * value2).AsM4x4();
/// Negates the specified matrix by multiplying all its values by -1.
/// The matrix to negate.
/// The negated matrix.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Matrix4x4 Negate(Matrix4x4 value)
- {
- return -value;
- }
+ => (-value.AsImpl()).AsM4x4();
/// Subtracts each element in a second matrix from its corresponding element in a first matrix.
/// The first matrix.
@@ -1773,548 +513,51 @@ public static Matrix4x4 Negate(Matrix4x4 value)
/// The matrix containing the values that result from subtracting each element in from its corresponding element in .
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Matrix4x4 Subtract(Matrix4x4 value1, Matrix4x4 value2)
- {
- return value1 - value2;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static Vector128 Permute(Vector128 value, [ConstantExpected] byte control)
- {
- if (Avx.IsSupported)
- {
- return Avx.Permute(value, control);
- }
- else if (Sse.IsSupported)
- {
- return Sse.Shuffle(value, value, control);
- }
- else
- {
- // Redundant test so we won't prejit remainder of this method on platforms without AdvSimd.
- throw new PlatformNotSupportedException();
- }
- }
-
- /// Attempts to extract the scale, translation, and rotation components from the given scale, rotation, or translation matrix. The return value indicates whether the operation succeeded.
- /// The source matrix.
- /// When this method returns, contains the scaling component of the transformation matrix if the operation succeeded.
- /// When this method returns, contains the rotation component of the transformation matrix if the operation succeeded.
- /// When the method returns, contains the translation component of the transformation matrix if the operation succeeded.
- /// if was decomposed successfully; otherwise, .
- public static bool Decompose(Matrix4x4 matrix, out Vector3 scale, out Quaternion rotation, out Vector3 translation)
- {
- bool result = true;
-
- unsafe
- {
- fixed (Vector3* scaleBase = &scale)
- {
- float* pfScales = (float*)scaleBase;
- float det;
-
- VectorBasis vectorBasis;
- Vector3** pVectorBasis = (Vector3**)&vectorBasis;
-
- Matrix4x4 matTemp = Identity;
- CanonicalBasis canonicalBasis = default;
- Vector3* pCanonicalBasis = &canonicalBasis.Row0;
-
- canonicalBasis.Row0 = new Vector3(1.0f, 0.0f, 0.0f);
- canonicalBasis.Row1 = new Vector3(0.0f, 1.0f, 0.0f);
- canonicalBasis.Row2 = new Vector3(0.0f, 0.0f, 1.0f);
-
- translation = new Vector3(
- matrix.M41,
- matrix.M42,
- matrix.M43);
-
- pVectorBasis[0] = (Vector3*)&matTemp.M11;
- pVectorBasis[1] = (Vector3*)&matTemp.M21;
- pVectorBasis[2] = (Vector3*)&matTemp.M31;
-
- *(pVectorBasis[0]) = new Vector3(matrix.M11, matrix.M12, matrix.M13);
- *(pVectorBasis[1]) = new Vector3(matrix.M21, matrix.M22, matrix.M23);
- *(pVectorBasis[2]) = new Vector3(matrix.M31, matrix.M32, matrix.M33);
-
- scale.X = pVectorBasis[0]->Length();
- scale.Y = pVectorBasis[1]->Length();
- scale.Z = pVectorBasis[2]->Length();
-
- uint a, b, c;
- #region Ranking
- float x = pfScales[0], y = pfScales[1], z = pfScales[2];
- if (x < y)
- {
- if (y < z)
- {
- a = 2;
- b = 1;
- c = 0;
- }
- else
- {
- a = 1;
-
- if (x < z)
- {
- b = 2;
- c = 0;
- }
- else
- {
- b = 0;
- c = 2;
- }
- }
- }
- else
- {
- if (x < z)
- {
- a = 2;
- b = 0;
- c = 1;
- }
- else
- {
- a = 0;
-
- if (y < z)
- {
- b = 2;
- c = 1;
- }
- else
- {
- b = 1;
- c = 2;
- }
- }
- }
- #endregion
-
- if (pfScales[a] < DecomposeEpsilon)
- {
- *(pVectorBasis[a]) = pCanonicalBasis[a];
- }
-
- *pVectorBasis[a] = Vector3.Normalize(*pVectorBasis[a]);
-
- if (pfScales[b] < DecomposeEpsilon)
- {
- uint cc;
- float fAbsX, fAbsY, fAbsZ;
-
- fAbsX = MathF.Abs(pVectorBasis[a]->X);
- fAbsY = MathF.Abs(pVectorBasis[a]->Y);
- fAbsZ = MathF.Abs(pVectorBasis[a]->Z);
-
- #region Ranking
- if (fAbsX < fAbsY)
- {
- if (fAbsY < fAbsZ)
- {
- cc = 0;
- }
- else
- {
- if (fAbsX < fAbsZ)
- {
- cc = 0;
- }
- else
- {
- cc = 2;
- }
- }
- }
- else
- {
- if (fAbsX < fAbsZ)
- {
- cc = 1;
- }
- else
- {
- if (fAbsY < fAbsZ)
- {
- cc = 1;
- }
- else
- {
- cc = 2;
- }
- }
- }
- #endregion
-
- *pVectorBasis[b] = Vector3.Cross(*pVectorBasis[a], *(pCanonicalBasis + cc));
- }
-
- *pVectorBasis[b] = Vector3.Normalize(*pVectorBasis[b]);
-
- if (pfScales[c] < DecomposeEpsilon)
- {
- *pVectorBasis[c] = Vector3.Cross(*pVectorBasis[a], *pVectorBasis[b]);
- }
-
- *pVectorBasis[c] = Vector3.Normalize(*pVectorBasis[c]);
-
- det = matTemp.GetDeterminant();
-
- // use Kramer's rule to check for handedness of coordinate system
- if (det < 0.0f)
- {
- // switch coordinate system by negating the scale and inverting the basis vector on the x-axis
- pfScales[a] = -pfScales[a];
- *pVectorBasis[a] = -(*pVectorBasis[a]);
-
- det = -det;
- }
-
- det -= 1.0f;
- det *= det;
-
- if ((DecomposeEpsilon < det))
- {
- // Non-SRT matrix encountered
- rotation = Quaternion.Identity;
- result = false;
- }
- else
- {
- // generate the quaternion from the matrix
- rotation = Quaternion.CreateFromRotationMatrix(matTemp);
- }
- }
- }
-
- return result;
- }
-
- /// Performs a linear interpolation from one matrix to a second matrix based on a value that specifies the weighting of the second matrix.
- /// The first matrix.
- /// The second matrix.
- /// The relative weighting of .
- /// The interpolated matrix.
- public static unsafe Matrix4x4 Lerp(Matrix4x4 matrix1, Matrix4x4 matrix2, float amount)
- {
- if (AdvSimd.IsSupported)
- {
- Vector128 amountVec = Vector128.Create(amount);
- AdvSimd.Store(&matrix1.M11, VectorMath.Lerp(AdvSimd.LoadVector128(&matrix1.M11), AdvSimd.LoadVector128(&matrix2.M11), amountVec));
- AdvSimd.Store(&matrix1.M21, VectorMath.Lerp(AdvSimd.LoadVector128(&matrix1.M21), AdvSimd.LoadVector128(&matrix2.M21), amountVec));
- AdvSimd.Store(&matrix1.M31, VectorMath.Lerp(AdvSimd.LoadVector128(&matrix1.M31), AdvSimd.LoadVector128(&matrix2.M31), amountVec));
- AdvSimd.Store(&matrix1.M41, VectorMath.Lerp(AdvSimd.LoadVector128(&matrix1.M41), AdvSimd.LoadVector128(&matrix2.M41), amountVec));
- return matrix1;
- }
- else if (Sse.IsSupported)
- {
- Vector128 amountVec = Vector128.Create(amount);
- Sse.Store(&matrix1.M11, VectorMath.Lerp(Sse.LoadVector128(&matrix1.M11), Sse.LoadVector128(&matrix2.M11), amountVec));
- Sse.Store(&matrix1.M21, VectorMath.Lerp(Sse.LoadVector128(&matrix1.M21), Sse.LoadVector128(&matrix2.M21), amountVec));
- Sse.Store(&matrix1.M31, VectorMath.Lerp(Sse.LoadVector128(&matrix1.M31), Sse.LoadVector128(&matrix2.M31), amountVec));
- Sse.Store(&matrix1.M41, VectorMath.Lerp(Sse.LoadVector128(&matrix1.M41), Sse.LoadVector128(&matrix2.M41), amountVec));
- return matrix1;
- }
-
- Matrix4x4 result;
-
- // First row
- result.M11 = matrix1.M11 + (matrix2.M11 - matrix1.M11) * amount;
- result.M12 = matrix1.M12 + (matrix2.M12 - matrix1.M12) * amount;
- result.M13 = matrix1.M13 + (matrix2.M13 - matrix1.M13) * amount;
- result.M14 = matrix1.M14 + (matrix2.M14 - matrix1.M14) * amount;
-
- // Second row
- result.M21 = matrix1.M21 + (matrix2.M21 - matrix1.M21) * amount;
- result.M22 = matrix1.M22 + (matrix2.M22 - matrix1.M22) * amount;
- result.M23 = matrix1.M23 + (matrix2.M23 - matrix1.M23) * amount;
- result.M24 = matrix1.M24 + (matrix2.M24 - matrix1.M24) * amount;
-
- // Third row
- result.M31 = matrix1.M31 + (matrix2.M31 - matrix1.M31) * amount;
- result.M32 = matrix1.M32 + (matrix2.M32 - matrix1.M32) * amount;
- result.M33 = matrix1.M33 + (matrix2.M33 - matrix1.M33) * amount;
- result.M34 = matrix1.M34 + (matrix2.M34 - matrix1.M34) * amount;
-
- // Fourth row
- result.M41 = matrix1.M41 + (matrix2.M41 - matrix1.M41) * amount;
- result.M42 = matrix1.M42 + (matrix2.M42 - matrix1.M42) * amount;
- result.M43 = matrix1.M43 + (matrix2.M43 - matrix1.M43) * amount;
- result.M44 = matrix1.M44 + (matrix2.M44 - matrix1.M44) * amount;
-
- return result;
- }
+ => (value1.AsImpl() - value2.AsImpl()).AsM4x4();
/// Transforms the specified matrix by applying the specified Quaternion rotation.
/// The matrix to transform.
/// The rotation t apply.
/// The transformed matrix.
public static Matrix4x4 Transform(Matrix4x4 value, Quaternion rotation)
- {
- // Compute rotation matrix.
- float x2 = rotation.X + rotation.X;
- float y2 = rotation.Y + rotation.Y;
- float z2 = rotation.Z + rotation.Z;
-
- float wx2 = rotation.W * x2;
- float wy2 = rotation.W * y2;
- float wz2 = rotation.W * z2;
- float xx2 = rotation.X * x2;
- float xy2 = rotation.X * y2;
- float xz2 = rotation.X * z2;
- float yy2 = rotation.Y * y2;
- float yz2 = rotation.Y * z2;
- float zz2 = rotation.Z * z2;
-
- float q11 = 1.0f - yy2 - zz2;
- float q21 = xy2 - wz2;
- float q31 = xz2 + wy2;
-
- float q12 = xy2 + wz2;
- float q22 = 1.0f - xx2 - zz2;
- float q32 = yz2 - wx2;
-
- float q13 = xz2 - wy2;
- float q23 = yz2 + wx2;
- float q33 = 1.0f - xx2 - yy2;
-
- Matrix4x4 result;
-
- // First row
- result.M11 = value.M11 * q11 + value.M12 * q21 + value.M13 * q31;
- result.M12 = value.M11 * q12 + value.M12 * q22 + value.M13 * q32;
- result.M13 = value.M11 * q13 + value.M12 * q23 + value.M13 * q33;
- result.M14 = value.M14;
-
- // Second row
- result.M21 = value.M21 * q11 + value.M22 * q21 + value.M23 * q31;
- result.M22 = value.M21 * q12 + value.M22 * q22 + value.M23 * q32;
- result.M23 = value.M21 * q13 + value.M22 * q23 + value.M23 * q33;
- result.M24 = value.M24;
-
- // Third row
- result.M31 = value.M31 * q11 + value.M32 * q21 + value.M33 * q31;
- result.M32 = value.M31 * q12 + value.M32 * q22 + value.M33 * q32;
- result.M33 = value.M31 * q13 + value.M32 * q23 + value.M33 * q33;
- result.M34 = value.M34;
-
- // Fourth row
- result.M41 = value.M41 * q11 + value.M42 * q21 + value.M43 * q31;
- result.M42 = value.M41 * q12 + value.M42 * q22 + value.M43 * q32;
- result.M43 = value.M41 * q13 + value.M42 * q23 + value.M43 * q33;
- result.M44 = value.M44;
-
- return result;
- }
+ => Impl.Transform(in value.AsImpl(), in rotation).AsM4x4();
/// Transposes the rows and columns of a matrix.
/// The matrix to transpose.
/// The transposed matrix.
- public static unsafe Matrix4x4 Transpose(Matrix4x4 matrix)
- {
- if (AdvSimd.Arm64.IsSupported)
- {
- // This implementation is based on the DirectX Math Library XMMatrixTranspose method
- // https://github.com/microsoft/DirectXMath/blob/master/Inc/DirectXMathMatrix.inl
-
- Vector128 M11 = AdvSimd.LoadVector128(&matrix.M11);
- Vector128 M31 = AdvSimd.LoadVector128(&matrix.M31);
-
- Vector128 P00 = AdvSimd.Arm64.ZipLow(M11, M31);
- Vector128 P01 = AdvSimd.Arm64.ZipHigh(M11, M31);
-
- Vector128 M21 = AdvSimd.LoadVector128(&matrix.M21);
- Vector128 M41 = AdvSimd.LoadVector128(&matrix.M41);
-
- Vector128 P10 = AdvSimd.Arm64.ZipLow(M21, M41);
- Vector128 P11 = AdvSimd.Arm64.ZipHigh(M21, M41);
-
- AdvSimd.Store(&matrix.M11, AdvSimd.Arm64.ZipLow(P00, P10));
- AdvSimd.Store(&matrix.M21, AdvSimd.Arm64.ZipHigh(P00, P10));
- AdvSimd.Store(&matrix.M31, AdvSimd.Arm64.ZipLow(P01, P11));
- AdvSimd.Store(&matrix.M41, AdvSimd.Arm64.ZipHigh(P01, P11));
-
- return matrix;
- }
- else if (Sse.IsSupported)
- {
- Vector128 row1 = Sse.LoadVector128(&matrix.M11);
- Vector128 row2 = Sse.LoadVector128(&matrix.M21);
- Vector128 row3 = Sse.LoadVector128(&matrix.M31);
- Vector128 row4 = Sse.LoadVector128(&matrix.M41);
-
- Vector128 l12 = Sse.UnpackLow(row1, row2);
- Vector128 l34 = Sse.UnpackLow(row3, row4);
- Vector128 h12 = Sse.UnpackHigh(row1, row2);
- Vector128 h34 = Sse.UnpackHigh(row3, row4);
-
- Sse.Store(&matrix.M11, Sse.MoveLowToHigh(l12, l34));
- Sse.Store(&matrix.M21, Sse.MoveHighToLow(l34, l12));
- Sse.Store(&matrix.M31, Sse.MoveLowToHigh(h12, h34));
- Sse.Store(&matrix.M41, Sse.MoveHighToLow(h34, h12));
-
- return matrix;
- }
-
- Matrix4x4 result;
-
- result.M11 = matrix.M11;
- result.M12 = matrix.M21;
- result.M13 = matrix.M31;
- result.M14 = matrix.M41;
- result.M21 = matrix.M12;
- result.M22 = matrix.M22;
- result.M23 = matrix.M32;
- result.M24 = matrix.M42;
- result.M31 = matrix.M13;
- result.M32 = matrix.M23;
- result.M33 = matrix.M33;
- result.M34 = matrix.M43;
- result.M41 = matrix.M14;
- result.M42 = matrix.M24;
- result.M43 = matrix.M34;
- result.M44 = matrix.M44;
-
- return result;
- }
+ public static Matrix4x4 Transpose(Matrix4x4 matrix)
+ => Impl.Transpose(in matrix.AsImpl()).AsM4x4();
/// Returns a value that indicates whether this instance and a specified object are equal.
/// The object to compare with the current instance.
/// if the current instance and are equal; otherwise, . If is , the method returns .
- /// The current instance and are equal if is a object and the corresponding elements of each matrix are equal.
+ /// The current instance and are equal if is a object and the corresponding elements of each matrix are equal.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override readonly bool Equals([NotNullWhen(true)] object? obj)
- {
- return (obj is Matrix4x4 other) && Equals(other);
- }
+ => AsROImpl().Equals(obj);
/// Returns a value that indicates whether this instance and another 4x4 matrix are equal.
/// The other matrix.
/// if the two matrices are equal; otherwise, .
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly bool Equals(Matrix4x4 other)
- {
- // This function needs to account for floating-point equality around NaN
- // and so must behave equivalently to the underlying float/double.Equals
-
- if (Vector128.IsHardwareAccelerated)
- {
- return Vector128.LoadUnsafe(ref Unsafe.AsRef(in M11)).Equals(Vector128.LoadUnsafe(ref other.M11))
- && Vector128.LoadUnsafe(ref Unsafe.AsRef(in M21)).Equals(Vector128.LoadUnsafe(ref other.M21))
- && Vector128.LoadUnsafe(ref Unsafe.AsRef(in M31)).Equals(Vector128.LoadUnsafe(ref other.M31))
- && Vector128.LoadUnsafe(ref Unsafe.AsRef(in M41)).Equals(Vector128.LoadUnsafe(ref other.M41));
-
- }
-
- return SoftwareFallback(in this, other);
-
- static bool SoftwareFallback(in Matrix4x4 self, Matrix4x4 other)
- {
- return self.M11.Equals(other.M11) && self.M22.Equals(other.M22) && self.M33.Equals(other.M33) && self.M44.Equals(other.M44) // Check diagonal element first for early out.
- && self.M12.Equals(other.M12) && self.M13.Equals(other.M13) && self.M14.Equals(other.M14) && self.M21.Equals(other.M21)
- && self.M23.Equals(other.M23) && self.M24.Equals(other.M24) && self.M31.Equals(other.M31) && self.M32.Equals(other.M32)
- && self.M34.Equals(other.M34) && self.M41.Equals(other.M41) && self.M42.Equals(other.M42) && self.M43.Equals(other.M43);
- }
- }
+ => AsROImpl().Equals(in other.AsImpl());
/// Calculates the determinant of the current 4x4 matrix.
/// The determinant.
public readonly float GetDeterminant()
- {
- // | a b c d | | f g h | | e g h | | e f h | | e f g |
- // | e f g h | = a | j k l | - b | i k l | + c | i j l | - d | i j k |
- // | i j k l | | n o p | | m o p | | m n p | | m n o |
- // | m n o p |
- //
- // | f g h |
- // a | j k l | = a ( f ( kp - lo ) - g ( jp - ln ) + h ( jo - kn ) )
- // | n o p |
- //
- // | e g h |
- // b | i k l | = b ( e ( kp - lo ) - g ( ip - lm ) + h ( io - km ) )
- // | m o p |
- //
- // | e f h |
- // c | i j l | = c ( e ( jp - ln ) - f ( ip - lm ) + h ( in - jm ) )
- // | m n p |
- //
- // | e f g |
- // d | i j k | = d ( e ( jo - kn ) - f ( io - km ) + g ( in - jm ) )
- // | m n o |
- //
- // Cost of operation
- // 17 adds and 28 muls.
- //
- // add: 6 + 8 + 3 = 17
- // mul: 12 + 16 = 28
-
- float a = M11, b = M12, c = M13, d = M14;
- float e = M21, f = M22, g = M23, h = M24;
- float i = M31, j = M32, k = M33, l = M34;
- float m = M41, n = M42, o = M43, p = M44;
-
- float kp_lo = k * p - l * o;
- float jp_ln = j * p - l * n;
- float jo_kn = j * o - k * n;
- float ip_lm = i * p - l * m;
- float io_km = i * o - k * m;
- float in_jm = i * n - j * m;
-
- return a * (f * kp_lo - g * jp_ln + h * jo_kn) -
- b * (e * kp_lo - g * ip_lm + h * io_km) +
- c * (e * jp_ln - f * ip_lm + h * in_jm) -
- d * (e * jo_kn - f * io_km + g * in_jm);
- }
+ => AsROImpl().GetDeterminant();
/// Returns the hash code for this instance.
/// The hash code.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override readonly int GetHashCode()
- {
- HashCode hash = default;
-
- hash.Add(M11);
- hash.Add(M12);
- hash.Add(M13);
- hash.Add(M14);
-
- hash.Add(M21);
- hash.Add(M22);
- hash.Add(M23);
- hash.Add(M24);
-
- hash.Add(M31);
- hash.Add(M32);
- hash.Add(M33);
- hash.Add(M34);
-
- hash.Add(M41);
- hash.Add(M42);
- hash.Add(M43);
- hash.Add(M44);
-
- return hash.ToHashCode();
- }
+ => AsROImpl().GetHashCode();
/// Returns a string that represents this matrix.
/// The string representation of this matrix.
/// The numeric values in the returned string are formatted by using the conventions of the current culture. For example, for the en-US culture, the returned string might appear as { {M11:1.1 M12:1.2 M13:1.3 M14:1.4} {M21:2.1 M22:2.2 M23:2.3 M24:2.4} {M31:3.1 M32:3.2 M33:3.3 M34:3.4} {M41:4.1 M42:4.2 M43:4.3 M44:4.4} }.
- public override readonly string ToString() =>
- $"{{ {{M11:{M11} M12:{M12} M13:{M13} M14:{M14}}} {{M21:{M21} M22:{M22} M23:{M23} M24:{M24}}} {{M31:{M31} M32:{M32} M33:{M33} M34:{M34}}} {{M41:{M41} M42:{M42} M43:{M43} M44:{M44}}} }}";
-
- private struct CanonicalBasis
- {
- public Vector3 Row0;
- public Vector3 Row1;
- public Vector3 Row2;
- };
-
- private struct VectorBasis
- {
- public unsafe Vector3* Element0;
- public unsafe Vector3* Element1;
- public unsafe Vector3* Element2;
- }
+ public override readonly string ToString()
+ => $"{{ {{M11:{M11} M12:{M12} M13:{M13} M14:{M14}}} {{M21:{M21} M22:{M22} M23:{M23} M24:{M24}}} {{M31:{M31} M32:{M32} M33:{M33} M34:{M34}}} {{M41:{M41} M42:{M42} M43:{M43} M44:{M44}}} }}";
}
}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector4.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector4.cs
index 1e519b8245b7f0..485cbd43f26cbc 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector4.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector4.cs
@@ -633,13 +633,18 @@ public static Vector4 Transform(Vector3 value, Quaternion rotation)
/// The transformed vector.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector4 Transform(Vector4 vector, Matrix4x4 matrix)
+ => Transform(vector, in matrix.AsImpl());
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static Vector4 Transform(Vector4 vector, in Matrix4x4.Impl matrix)
{
- return new Vector4(
- (vector.X * matrix.M11) + (vector.Y * matrix.M21) + (vector.Z * matrix.M31) + (vector.W * matrix.M41),
- (vector.X * matrix.M12) + (vector.Y * matrix.M22) + (vector.Z * matrix.M32) + (vector.W * matrix.M42),
- (vector.X * matrix.M13) + (vector.Y * matrix.M23) + (vector.Z * matrix.M33) + (vector.W * matrix.M43),
- (vector.X * matrix.M14) + (vector.Y * matrix.M24) + (vector.Z * matrix.M34) + (vector.W * matrix.M44)
- );
+ Vector4 result = matrix.X * vector.X;
+
+ result += matrix.Y * vector.Y;
+ result += matrix.Z * vector.Z;
+ result += matrix.W * vector.W;
+
+ return result;
}
/// Transforms a four-dimensional vector by the specified Quaternion rotation value.