diff --git a/HandmadeMath.h b/HandmadeMath.h index d7f35eb..542ae53 100644 --- a/HandmadeMath.h +++ b/HandmadeMath.h @@ -2600,6 +2600,27 @@ static inline HMM_Quat HMM_QFromAxisAngle_LH(HMM_Vec3 Axis, float Angle) return HMM_QFromAxisAngle_RH(Axis, -Angle); } +COVERAGE(HMM_QFromNormPair, 1) +static inline HMM_Quat HMM_QFromNormPair(HMM_Vec3 Left, HMM_Vec3 Right) +{ + ASSERT_COVERED(HMM_QFromNormPair); + + HMM_Quat Result; + + Result.XYZ = HMM_Cross(Left, Right); + Result.W = 1.0f + HMM_DotV3(Left, Right); + + return HMM_NormQ(Result); +} + +COVERAGE(HMM_QFromVecPair, 1) +static inline HMM_Quat HMM_QFromVecPair(HMM_Vec3 Left, HMM_Vec3 Right) +{ + ASSERT_COVERED(HMM_QFromVecPair); + + return HMM_QFromNormPair(HMM_NormV3(Left), HMM_NormV3(Right)); +} + COVERAGE(HMM_RotateV2, 1) static inline HMM_Vec2 HMM_RotateV2(HMM_Vec2 V, float Angle) { diff --git a/test/categories/QuaternionOps.h b/test/categories/QuaternionOps.h index 18bebf2..57927c4 100644 --- a/test/categories/QuaternionOps.h +++ b/test/categories/QuaternionOps.h @@ -273,3 +273,25 @@ TEST(QuaternionOps, RotateVectorAxisAngle) EXPECT_NEAR(result.Z, 0.707170f, 0.001f); } } + +TEST(QuaternionOps, QuatFromPairs) +{ + { + HMM_Vec3 n1 = HMM_V3(0.0f, 1.0f, 0.0f); + HMM_Vec3 n2 = HMM_V3(0.0f, 0.0f, 1.0f); + HMM_Quat q = HMM_QFromNormPair(n1, n2); + HMM_Vec3 result = HMM_RotateV3Q(n1, q); + EXPECT_NEAR(result.X, n2.X, 0.001f); + EXPECT_NEAR(result.Y, n2.Y, 0.001f); + EXPECT_NEAR(result.Z, n2.Z, 0.001f); + } + { + HMM_Vec3 v1 = HMM_V3(2.0f, 2.0f, 2.0f); + HMM_Vec3 v2 = HMM_V3(3.0f, 0.0f, 0.0f); + HMM_Quat q = HMM_QFromVecPair(v1, v2); + HMM_Vec3 result = HMM_RotateV3Q(HMM_V3(0.0f, 1.0f, 0.0f), q); + EXPECT_NEAR(result.X, 0.577350, 0.001f); + EXPECT_NEAR(result.Y, 0.788675, 0.001f); + EXPECT_NEAR(result.Z, -0.211325, 0.001f); + } +}