From aa1ac445bb09d519fb2a2c2be1927305bbd12981 Mon Sep 17 00:00:00 2001 From: notargs Date: Wed, 20 May 2026 17:17:25 +0900 Subject: [PATCH] =?UTF-8?q?FromToRotation=E3=81=AE=E5=88=A4=E5=AE=9A?= =?UTF-8?q?=E3=82=92=E3=82=88=E3=82=8A=E5=8E=B3=E5=AF=86=E3=81=AB=E3=81=99?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Packages/UniGLTF/Runtime/Utils/MathHelper.cs | 17 +++--- Packages/UniGLTF/Tests/UniGLTF/MathTests.cs | 61 ++++++++++++++++++++ 2 files changed, 71 insertions(+), 7 deletions(-) diff --git a/Packages/UniGLTF/Runtime/Utils/MathHelper.cs b/Packages/UniGLTF/Runtime/Utils/MathHelper.cs index 86cb3646b..75d57a6b9 100644 --- a/Packages/UniGLTF/Runtime/Utils/MathHelper.cs +++ b/Packages/UniGLTF/Runtime/Utils/MathHelper.cs @@ -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)); } diff --git a/Packages/UniGLTF/Tests/UniGLTF/MathTests.cs b/Packages/UniGLTF/Tests/UniGLTF/MathTests.cs index 5c1395b64..cf1cc3b8f 100644 --- a/Packages/UniGLTF/Tests/UniGLTF/MathTests.cs +++ b/Packages/UniGLTF/Tests/UniGLTF/MathTests.cs @@ -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); + } } }