FromToRotationの判定をより厳密にする

This commit is contained in:
notargs
2026-05-20 17:17:25 +09:00
parent 2b5ebcf3d7
commit aa1ac445bb
2 changed files with 71 additions and 7 deletions

View File

@@ -27,7 +27,10 @@ namespace UniGLTF.Runtime.Utils
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static quaternion FromToRotation(in float3 fromVector, in float3 toVector)
{
if (math.lengthsq(fromVector) == 0 || math.lengthsq(toVector) == 0)
const float epsilon = 1e-6f;
const float epsilonSq = epsilon * epsilon;
if (math.lengthsq(fromVector) < epsilonSq || math.lengthsq(toVector) < epsilonSq)
{
return quaternion.identity;
}
@@ -35,15 +38,15 @@ namespace UniGLTF.Runtime.Utils
float3 from = math.normalize(fromVector);
float3 to = math.normalize(toVector);
var dot = math.dot(from, to);
switch(dot)
var dot = math.clamp(math.dot(from, to), -1.0f, 1.0f);
switch (dot)
{
case >= 1.0f:
return quaternion.identity;
case <= -1.0f:
case > 1.0f - epsilon:
return quaternion.identity;
case < -1.0f + epsilon:
{
var axis = math.cross(from, new float3(1, 0, 0));
if (math.lengthsq(axis) < 0.0001f)
if (math.lengthsq(axis) < epsilonSq)
{
axis = math.cross(from, new float3(0, 1, 0));
}

View File

@@ -45,5 +45,66 @@ namespace UniGLTF
var expected = Quaternion.FromToRotation(_vector1, _vector2);
Assert.That(MathHelper.Approximately(result, expected), Is.True);
}
[Test]
public void FromToRotationMatchesUnityForStandardCasesTest()
{
AssertFromToRotationMatchesUnity(new float3(1, 0, 0), new float3(0, 1, 0));
AssertFromToRotationMatchesUnity(new float3(0, 1, 0), new float3(0, 0, 1));
AssertFromToRotationMatchesUnity(new float3(1, 2, 3), new float3(4, 5, 6));
AssertFromToRotationMatchesUnity(new float3(-2, 0.5f, 3), new float3(1, -4, 0.25f));
}
[Test]
public void FromToRotationSameDirectionTest()
{
AssertFromToRotation(new float3(1, 0, 0), new float3(2, 0, 0));
AssertFromToRotation(new float3(1, 2, 3), new float3(2, 4, 6));
}
[Test]
public void FromToRotationOppositeDirectionTest()
{
AssertFromToRotation(new float3(1, 0, 0), new float3(-1, 0, 0));
AssertFromToRotation(new float3(0, 1, 0), new float3(0, -1, 0));
AssertFromToRotation(new float3(0, 0, 1), new float3(0, 0, -1));
AssertFromToRotation(new float3(1, 2, 3), new float3(-1, -2, -3));
}
[Test]
public void FromToRotationNearlyOppositeDirectionTest()
{
AssertFromToRotation(new float3(1, 0, 0), math.normalize(new float3(-1, 0.0001f, 0)));
AssertFromToRotation(new float3(1, 2, 3), math.normalize(new float3(-1.0001f, -2, -3)));
}
[Test]
public void FromToRotationZeroVectorTest()
{
Assert.That(MathHelper.Approximately(MathHelper.FromToRotation(float3.zero, new float3(0, 1, 0)), quaternion.identity), Is.True);
Assert.That(MathHelper.Approximately(MathHelper.FromToRotation(new float3(1, 0, 0), float3.zero), quaternion.identity), Is.True);
}
[Test]
public void FromToRotationTinyVectorTest()
{
var result = MathHelper.FromToRotation(new float3(1e-12f, 0, 0), new float3(0, 1, 0));
Assert.That(MathHelper.Approximately(result, quaternion.identity), Is.True);
}
private static void AssertFromToRotation(float3 from, float3 to)
{
var result = MathHelper.FromToRotation(from, to);
var rotated = math.mul(result, math.normalize(from));
var dot = math.dot(math.normalize(rotated), math.normalize(to));
Assert.That(dot, Is.GreaterThan(0.9999f));
}
private static void AssertFromToRotationMatchesUnity(float3 from, float3 to)
{
var result = MathHelper.FromToRotation(from, to);
var expected = Quaternion.FromToRotation(from, to);
Assert.That(MathHelper.Approximately(result, expected), Is.True);
}
}
}