diff --git a/common/mathlib.cc b/common/mathlib.cc index 99ecee31..eda282ba 100644 --- a/common/mathlib.cc +++ b/common/mathlib.cc @@ -768,3 +768,26 @@ qvec3f ClosestPointOnLineSegment(const qvec3f &v, const qvec3f &w, const qvec3f& return ClosestPointOnLine(v, w, p); } + +/// Returns degrees of clockwise rotation from start to end, assuming `normal` is pointing towards the viewer +float SignedDegreesBetweenUnitVectors(const vec3_t start, const vec3_t end, const vec3_t normal) +{ + const float cosangle = qmax(-1.0, qmin(1.0, DotProduct(start, end))); + const float unsigned_degrees = acos(cosangle) * (360.0 / (2.0 * Q_PI)); + + if (unsigned_degrees < ANGLEEPSILON) + return 0; + + // get a normal for the rotation plane using the right-hand rule + vec3_t rotationNormal; + CrossProduct(start, end, rotationNormal); + VectorNormalize(rotationNormal); + + const float normalsCosAngle = DotProduct(rotationNormal, normal); + if (normalsCosAngle >= 0) { + // counterclockwise rotation + return -unsigned_degrees; + } + // clockwise rotation + return unsigned_degrees; +} diff --git a/include/common/mathlib.hh b/include/common/mathlib.hh index 20c46204..852e2fb8 100644 --- a/include/common/mathlib.hh +++ b/include/common/mathlib.hh @@ -57,7 +57,7 @@ typedef struct { extern const vec3_t vec3_origin; #define EQUAL_EPSILON 0.001 - +#define ANGLEEPSILON 0.000001 #define ZERO_TRI_AREA_EPSILON 0.05f #define POINT_EQUAL_EPSILON 0.05f @@ -313,6 +313,8 @@ float DistToLineSegment(const qvec3f &v, const qvec3f &w, const qvec3f &p); qvec3f ClosestPointOnLineSegment(const qvec3f &v, const qvec3f &w, const qvec3f &p); +float SignedDegreesBetweenUnitVectors(const vec3_t start, const vec3_t end, const vec3_t normal); + // Returns weights for f(0,0), f(1,0), f(0,1), f(1,1) // from: https://en.wikipedia.org/wiki/Bilinear_interpolation#Unit_Square static inline qvec4f bilinearWeights(const float x, const float y) { diff --git a/light/test_light.cc b/light/test_light.cc index 81445dce..551873ab 100644 --- a/light/test_light.cc +++ b/light/test_light.cc @@ -412,6 +412,15 @@ TEST(mathlib, ShrinkPoly2) { EXPECT_TRUE(polysEqual(shrunkPoly, actualShrunk)); } +TEST(mathlib, SignedDegreesBetweenUnitVectors) { + const vec3_t up = {0, 0, 1}; + const vec3_t fwd = {0, 1, 0}; + const vec3_t right = {1, 0, 0}; + + EXPECT_FLOAT_EQ(-90, SignedDegreesBetweenUnitVectors(right, fwd, up)); + EXPECT_FLOAT_EQ(90, SignedDegreesBetweenUnitVectors(fwd, right, up)); +} + static const float MANGLE_EPSILON = 0.1f; TEST(light, vec_from_mangle) {