Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ public partial struct Matrix4x4 : System.IEquatable<System.Numerics.Matrix4x4>
public System.Numerics.Vector3 Translation { readonly get { throw null; } set { } }
public static System.Numerics.Matrix4x4 Add(System.Numerics.Matrix4x4 value1, System.Numerics.Matrix4x4 value2) { throw null; }
public static System.Numerics.Matrix4x4 CreateBillboard(System.Numerics.Vector3 objectPosition, System.Numerics.Vector3 cameraPosition, System.Numerics.Vector3 cameraUpVector, System.Numerics.Vector3 cameraForwardVector) { throw null; }
public static System.Numerics.Matrix4x4 CreateBillboardLeftHanded(System.Numerics.Vector3 objectPosition, System.Numerics.Vector3 cameraPosition, System.Numerics.Vector3 cameraUpVector, System.Numerics.Vector3 cameraForwardVector) { throw null; }
public static System.Numerics.Matrix4x4 CreateConstrainedBillboard(System.Numerics.Vector3 objectPosition, System.Numerics.Vector3 cameraPosition, System.Numerics.Vector3 rotateAxis, System.Numerics.Vector3 cameraForwardVector, System.Numerics.Vector3 objectForwardVector) { throw null; }
public static System.Numerics.Matrix4x4 CreateConstrainedBillboardLeftHanded(System.Numerics.Vector3 objectPosition, System.Numerics.Vector3 cameraPosition, System.Numerics.Vector3 rotateAxis, System.Numerics.Vector3 cameraForwardVector, System.Numerics.Vector3 objectForwardVector) { throw null; }
public static System.Numerics.Matrix4x4 CreateFromAxisAngle(System.Numerics.Vector3 axis, float angle) { throw null; }
public static System.Numerics.Matrix4x4 CreateFromQuaternion(System.Numerics.Quaternion quaternion) { throw null; }
public static System.Numerics.Matrix4x4 CreateFromYawPitchRoll(float yaw, float pitch, float roll) { throw null; }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Numerics.Tests
{
public class Matrix4x4LeftHandChangedBasisTests : Matrix4x4RightHandTests
{
protected override string CreateLookAtMethodName => $"{nameof(Matrix4x4)}.{nameof(Matrix4x4.CreateLookAtLeftHanded)}";
protected override Matrix4x4 CreateLookAt(Vector3 cameraPosition, Vector3 cameraTarget, Vector3 cameraUpVector)
{
return LeftHandToRightHand * Matrix4x4.CreateLookAtLeftHanded(
ChangeRightHandToLeftHand(cameraPosition),
ChangeRightHandToLeftHand(cameraTarget),
ChangeRightHandToLeftHand(cameraUpVector)) * RightHandToLeftHand;
}

protected override string CreateLookToMethodName => $"{nameof(Matrix4x4)}.{nameof(Matrix4x4.CreateLookToLeftHanded)}";
protected override Matrix4x4 CreateLookTo(Vector3 cameraPosition, Vector3 cameraDirection, Vector3 cameraUpVector)
{
return LeftHandToRightHand * Matrix4x4.CreateLookToLeftHanded(
ChangeRightHandToLeftHand(cameraPosition),
ChangeRightHandToLeftHand(cameraDirection),
ChangeRightHandToLeftHand(cameraUpVector)) * RightHandToLeftHand;
}

protected override string CreateViewportMethodName => $"{nameof(Matrix4x4)}.{nameof(Matrix4x4.CreateViewportLeftHanded)}";
protected override Matrix4x4 CreateViewport(float x, float y, float width, float height, float minDepth, float maxDepth)
{
return LeftHandToRightHand * Matrix4x4.CreateViewportLeftHanded(x, y, width, height, -minDepth, -maxDepth) * RightHandToLeftHand;
}

protected override string CreateBillboardMethodName => $"{nameof(Matrix4x4)}.{nameof(Matrix4x4.CreateBillboardLeftHanded)}";
protected override Matrix4x4 CreateBillboard(Vector3 objectPosition, Vector3 cameraPosition, Vector3 cameraUpVector, Vector3 cameraForwardVector)
{
return LeftHandToRightHand * Matrix4x4.CreateBillboardLeftHanded(
ChangeRightHandToLeftHand(objectPosition),
ChangeRightHandToLeftHand(cameraPosition),
ChangeRightHandToLeftHand(cameraUpVector),
ChangeRightHandToLeftHand(cameraForwardVector)) * RightHandToLeftHand;
}

protected override string CreateConstrainedBillboardMethodName => $"{nameof(Matrix4x4)}.{nameof(Matrix4x4.CreateConstrainedBillboardLeftHanded)}";
protected override Matrix4x4 CreateConstrainedBillboard(
Vector3 objectPosition,
Vector3 cameraPosition,
Vector3 rotateAxis,
Vector3 cameraForwardVector,
Vector3 objectForwardVector)
{
return LeftHandToRightHand * Matrix4x4.CreateConstrainedBillboardLeftHanded(
ChangeRightHandToLeftHand(objectPosition),
ChangeRightHandToLeftHand(cameraPosition),
ChangeRightHandToLeftHand(rotateAxis),
ChangeRightHandToLeftHand(cameraForwardVector),
ChangeRightHandToLeftHand(objectForwardVector)) * RightHandToLeftHand;
}
}
}
113 changes: 113 additions & 0 deletions src/libraries/System.Numerics.Vectors/tests/Matrix4x4LeftHandTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Xunit;

namespace System.Numerics.Tests
{
public class Matrix4x4LeftHandTests : Matrix4x4OrientationAwareTests
{
[Fact]
public void Matrix4x4CreateLookAtLeftHandedTest()
{
Vector3 cameraPosition = new Vector3(10.0f, 20.0f, 30.0f);
Vector3 cameraTarget = new Vector3(3.0f, 2.0f, -4.0f);
Vector3 cameraUpVector = new Vector3(0.0f, 1.0f, 0.0f);

Matrix4x4 expected = new Matrix4x4();
expected.M11 = -0.979457f;
expected.M12 = -0.0928268f;
expected.M13 = -0.179017f;

expected.M21 = +0.0f;
expected.M22 = +0.887748f;
expected.M23 = -0.460329f;

expected.M31 = +0.201653f;
expected.M32 = -0.450873f;
expected.M33 = -0.869511f;

expected.M41 = +3.74498f;
expected.M42 = -3.30051f;
expected.M43 = +37.0821f;
expected.M44 = +1.0f;

Matrix4x4 actual = CreateLookAt(cameraPosition, cameraTarget, cameraUpVector);
Assert.True(MathHelper.Equal(expected, actual), $"{CreateLookAtMethodName} did not return the expected value.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: We have existing AssertExtensions.Equal(expected, actual, allowedVariance); helper that will provide better diagnostic messages in the case of failure.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated this and used 1e-5 for the variance (matches MathHelper.Equals). I think we can can use tighter errors in some cases where we have numbers with abs < 1 and having 6 sigfigs, but I'll just leave it for now.

}

[Fact]
public void Matrix4x4CreateLookToLeftHandedTest()
{
Vector3 cameraPosition = new Vector3(10.0f, 20.0f, 30.0f);
Vector3 cameraDirection = new Vector3(-7.0f, -18.0f, -34.0f);
Vector3 cameraUpVector = new Vector3(0.0f, 1.0f, 0.0f);

Matrix4x4 expected = new Matrix4x4();
expected.M11 = -0.979457f;
expected.M12 = -0.0928268f;
expected.M13 = -0.179017f;

expected.M21 = +0.0f;
expected.M22 = +0.887748f;
expected.M23 = -0.460329f;

expected.M31 = +0.201653f;
expected.M32 = -0.450873f;
expected.M33 = -0.869511f;

expected.M41 = +3.74498f;
expected.M42 = -3.30051f;
expected.M43 = +37.0821f;
expected.M44 = +1.0f;

Matrix4x4 actual = CreateLookTo(cameraPosition, cameraDirection, cameraUpVector);
Assert.True(MathHelper.Equal(expected, actual), $"{CreateLookToMethodName} did not return the expected value.");
}

[Fact]
public void Matrix4x4CreateViewportLeftHandedTest()
{
float x = 10.0f, y = 20.0f;
float width = 3.0f, height = 4.0f;
float minDepth = 100.0f, maxDepth = 200.0f;

Matrix4x4 expected = Matrix4x4.Identity;
expected.M11 = width * 0.5f;
expected.M22 = -height * 0.5f;
expected.M33 = maxDepth - minDepth;
expected.M41 = x + expected.M11;
expected.M42 = y - expected.M22;
expected.M43 = minDepth;

Matrix4x4 actual = CreateViewport(x, y, width, height, minDepth, maxDepth);
Assert.True(MathHelper.Equal(expected, actual), $"{CreateViewportMethodName} did not return the expected value.");
}

protected override string CreateLookAtMethodName => $"{nameof(Matrix4x4)}.{nameof(Matrix4x4.CreateLookAtLeftHanded)}";
protected override Matrix4x4 CreateLookAt(Vector3 cameraPosition, Vector3 cameraTarget, Vector3 cameraUpVector)
=> Matrix4x4.CreateLookAtLeftHanded(cameraPosition, cameraTarget, cameraUpVector);

protected override string CreateLookToMethodName => $"{nameof(Matrix4x4)}.{nameof(Matrix4x4.CreateLookToLeftHanded)}";
protected override Matrix4x4 CreateLookTo(Vector3 cameraPosition, Vector3 cameraDirection, Vector3 cameraUpVector)
=> Matrix4x4.CreateLookToLeftHanded(cameraPosition, cameraDirection, cameraUpVector);

protected override string CreateViewportMethodName => $"{nameof(Matrix4x4)}.{nameof(Matrix4x4.CreateViewportLeftHanded)}";
protected override Matrix4x4 CreateViewport(float x, float y, float width, float height, float minDepth, float maxDepth)
=> Matrix4x4.CreateViewportLeftHanded(x, y, width, height, minDepth, maxDepth);

protected override string CreateBillboardMethodName => $"{nameof(Matrix4x4)}.{nameof(Matrix4x4.CreateBillboardLeftHanded)}";
protected override Matrix4x4 CreateBillboard(Vector3 objectPosition, Vector3 cameraPosition, Vector3 cameraUpVector, Vector3 cameraForwardVector)
=> Matrix4x4.CreateBillboardLeftHanded(objectPosition, cameraPosition, cameraUpVector, cameraForwardVector);

protected override string CreateConstrainedBillboardMethodName => $"{nameof(Matrix4x4)}.{nameof(Matrix4x4.CreateConstrainedBillboardLeftHanded)}";

protected override Matrix4x4 CreateConstrainedBillboard(
Vector3 objectPosition,
Vector3 cameraPosition,
Vector3 rotateAxis,
Vector3 cameraForwardVector,
Vector3 objectForwardVector)
=> Matrix4x4.CreateConstrainedBillboardLeftHanded(objectPosition, cameraPosition, rotateAxis, cameraForwardVector, objectForwardVector);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Numerics.Tests
{
public abstract class Matrix4x4OrientationAwareTests
{
protected static Matrix4x4 RightHandToLeftHand =
new(1, 0, 0, 0,
0, 1, 0, 0,
0, 0, -1, 0,
0, 0, 0, 1);

// The basis change is a self-inverse
protected static Matrix4x4 LeftHandToRightHand = RightHandToLeftHand;

protected static Vector3 ChangeRightHandToLeftHand(Vector3 v) => new(v.X, v.Y, -v.Z);
protected static Vector3 ChangeLeftHandToRightHand(Vector3 v) => new(v.X, v.Y, -v.Z);

protected abstract string CreateLookAtMethodName { get; }
protected abstract Matrix4x4 CreateLookAt(Vector3 cameraPosition, Vector3 cameraTarget, Vector3 cameraUpVector);

protected abstract string CreateLookToMethodName { get; }
protected abstract Matrix4x4 CreateLookTo(Vector3 cameraPosition, Vector3 cameraDirection, Vector3 cameraUpVector);

protected abstract string CreateViewportMethodName { get; }
protected abstract Matrix4x4 CreateViewport(float x, float y, float width, float height, float minDepth, float maxDepth);

protected abstract string CreateBillboardMethodName { get; }
protected abstract Matrix4x4 CreateBillboard(Vector3 objectPosition, Vector3 cameraPosition, Vector3 cameraUpVector, Vector3 cameraForwardVector);

protected abstract string CreateConstrainedBillboardMethodName { get; }
protected abstract Matrix4x4 CreateConstrainedBillboard(
Vector3 objectPosition,
Vector3 cameraPosition,
Vector3 rotateAxis,
Vector3 cameraForwardVector,
Vector3 objectForwardVector);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Numerics.Tests
{
public class Matrix4x4RightHandChangedBasisTests : Matrix4x4LeftHandTests
{
protected override string CreateLookAtMethodName => $"{nameof(Matrix4x4)}.{nameof(Matrix4x4.CreateLookAt)}";
protected override Matrix4x4 CreateLookAt(Vector3 cameraPosition, Vector3 cameraTarget, Vector3 cameraUpVector)
{
return RightHandToLeftHand * Matrix4x4.CreateLookAt(
ChangeLeftHandToRightHand(cameraPosition),
ChangeLeftHandToRightHand(cameraTarget),
ChangeLeftHandToRightHand(cameraUpVector)) * LeftHandToRightHand;
}

protected override string CreateLookToMethodName => $"{nameof(Matrix4x4)}.{nameof(Matrix4x4.CreateLookTo)}";
protected override Matrix4x4 CreateLookTo(Vector3 cameraPosition, Vector3 cameraDirection, Vector3 cameraUpVector)
{
return RightHandToLeftHand * Matrix4x4.CreateLookTo(
ChangeLeftHandToRightHand(cameraPosition),
ChangeLeftHandToRightHand(cameraDirection),
ChangeLeftHandToRightHand(cameraUpVector)) * LeftHandToRightHand;
}

protected override string CreateViewportMethodName => $"{nameof(Matrix4x4)}.{nameof(Matrix4x4.CreateViewport)}";
protected override Matrix4x4 CreateViewport(float x, float y, float width, float height, float minDepth, float maxDepth)
{
return RightHandToLeftHand * Matrix4x4.CreateViewport(x, y, width, height, -minDepth, -maxDepth) * LeftHandToRightHand;
}

protected override string CreateBillboardMethodName => $"{nameof(Matrix4x4)}.{nameof(Matrix4x4.CreateBillboard)}";
protected override Matrix4x4 CreateBillboard(Vector3 objectPosition, Vector3 cameraPosition, Vector3 cameraUpVector, Vector3 cameraForwardVector)
{
return RightHandToLeftHand * Matrix4x4.CreateBillboard(
ChangeLeftHandToRightHand(objectPosition),
ChangeLeftHandToRightHand(cameraPosition),
ChangeLeftHandToRightHand(cameraUpVector),
ChangeLeftHandToRightHand(cameraForwardVector)) * LeftHandToRightHand;
}

protected override string CreateConstrainedBillboardMethodName => $"{nameof(Matrix4x4)}.{nameof(Matrix4x4.CreateConstrainedBillboard)}";
protected override Matrix4x4 CreateConstrainedBillboard(
Vector3 objectPosition,
Vector3 cameraPosition,
Vector3 rotateAxis,
Vector3 cameraForwardVector,
Vector3 objectForwardVector)
{
return RightHandToLeftHand * Matrix4x4.CreateConstrainedBillboard(
ChangeLeftHandToRightHand(objectPosition),
ChangeLeftHandToRightHand(cameraPosition),
ChangeLeftHandToRightHand(rotateAxis),
ChangeLeftHandToRightHand(cameraForwardVector),
ChangeLeftHandToRightHand(objectForwardVector)) * LeftHandToRightHand;
}
}
}
Loading
Loading