diff --git a/common/mathlib.cc b/common/mathlib.cc index e91c92ed..477b8f0f 100644 --- a/common/mathlib.cc +++ b/common/mathlib.cc @@ -835,17 +835,15 @@ qvec3f ClosestPointOnLineSegment(const qvec3f &v, const qvec3f &w, const qvec3f& } /// 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) +float SignedDegreesBetweenUnitVectors(const qvec3f &start, const qvec3f &end, const qvec3f &normal) { - const float cosangle = qmax(-1.0, qmin(1.0, DotProduct(start, end))); + const float cosangle = qmax(-1.0, qmin(1.0, qv::dot(start, end))); const float unsigned_degrees = acos(cosangle) * (360.0 / (2.0 * Q_PI)); // get a normal for the rotation plane using the right-hand rule - vec3_t rotationNormal; - CrossProduct(start, end, rotationNormal); - VectorNormalize(rotationNormal); + const qvec3f rotationNormal = qv::normalize(qv::cross(start, end)); - const float normalsCosAngle = DotProduct(rotationNormal, normal); + const float normalsCosAngle = qv::dot(rotationNormal, normal); if (normalsCosAngle >= 0) { // counterclockwise rotation return -unsigned_degrees; @@ -854,6 +852,24 @@ float SignedDegreesBetweenUnitVectors(const vec3_t start, const vec3_t end, cons return unsigned_degrees; } +concavity_t FacePairConcavity(const qvec3f &face1Center, + const qvec3f &face1Normal, + const qvec3f &face2Center, + const qvec3f &face2Normal) +{ + const qvec3f face1to2_dir = qv::normalize(face2Center - face1Center); + const qvec3f towards_viewer_dir = qv::cross(face1to2_dir, face1Normal); + + const float degrees = SignedDegreesBetweenUnitVectors(face1Normal, face2Normal, towards_viewer_dir); + if (fabs(degrees) < DEGREES_EPSILON) { + return concavity_t::Coplanar; + } else if (degrees < 0.0f) { + return concavity_t::Concave; + } else { + return concavity_t::Convex; + } +} + /** * do the line segments overlap at all? * - if not colinear, returns false. diff --git a/include/common/mathlib.hh b/include/common/mathlib.hh index b2cfdcb6..fb649614 100644 --- a/include/common/mathlib.hh +++ b/include/common/mathlib.hh @@ -60,6 +60,7 @@ extern const vec3_t vec3_origin; #define POINT_EQUAL_EPSILON 0.05f #define NORMAL_EPSILON 0.000001 +#define DEGREES_EPSILON 0.001 qboolean VectorCompare(const vec3_t v1, const vec3_t v2, vec_t epsilon); @@ -367,7 +368,18 @@ 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); +float SignedDegreesBetweenUnitVectors(const qvec3f &start, const qvec3f &end, const qvec3f &normal); + +enum class concavity_t { + Coplanar, + Concave, + Convex +}; + +concavity_t FacePairConcavity(const qvec3f &face1Center, + const qvec3f &face1Normal, + const qvec3f &face2Center, + const qvec3f &face2Normal); // 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 diff --git a/light/test_light.cc b/light/test_light.cc index 5a2d4da6..8eeb2874 100644 --- a/light/test_light.cc +++ b/light/test_light.cc @@ -422,15 +422,64 @@ TEST(mathlib, ShrinkPoly2) { } TEST(mathlib, SignedDegreesBetweenUnitVectors) { - const vec3_t up = {0, 0, 1}; - const vec3_t fwd = {0, 1, 0}; - const vec3_t right = {1, 0, 0}; + const qvec3f up {0, 0, 1}; + const qvec3f fwd {0, 1, 0}; + const qvec3f right {1, 0, 0}; EXPECT_FLOAT_EQ(-90, SignedDegreesBetweenUnitVectors(right, fwd, up)); EXPECT_FLOAT_EQ(90, SignedDegreesBetweenUnitVectors(fwd, right, up)); EXPECT_FLOAT_EQ(0, SignedDegreesBetweenUnitVectors(right, right, up)); } +TEST(mathlib, ConcavityTest_concave) { + const qvec3f face1center {0, 0, 10}; + const qvec3f face2center {10, 0, 200}; + + const qvec3f face1normal {0, 0, 1}; + const qvec3f face2normal {-1, 0, 0}; + + EXPECT_EQ(concavity_t::Concave, FacePairConcavity(face1center, face1normal, face2center, face2normal)); +} + +TEST(mathlib, ConcavityTest_concave2) { + const qvec3f face1center {0, 0, 10}; + const qvec3f face2center {-10, 0, 200}; + + const qvec3f face1normal {0, 0, 1}; + const qvec3f face2normal {1, 0, 0}; + + EXPECT_EQ(concavity_t::Concave, FacePairConcavity(face1center, face1normal, face2center, face2normal)); +} + +TEST(mathlib, ConcavityTest_convex) { + const qvec3f face1center {0, 0, 10}; + const qvec3f face2center {10, 0, 5}; + + const qvec3f face1normal {0, 0, 1}; + const qvec3f face2normal {1, 0, 0}; + + EXPECT_EQ(concavity_t::Convex, FacePairConcavity(face1center, face1normal, face2center, face2normal)); +} + +TEST(mathlib, ConcavityTest_convex2) { + const qvec3f face1center {0, 0, 10}; + const qvec3f face2center {-10, 0, 5}; + + const qvec3f face1normal {0, 0, 1}; + const qvec3f face2normal {-1, 0, 0}; + + EXPECT_EQ(concavity_t::Convex, FacePairConcavity(face1center, face1normal, face2center, face2normal)); +} + +TEST(mathlib, ConcavityTest_coplanar) { + const qvec3f face1center {0, 0, 10}; + const qvec3f face2center {100, 100, 10}; + + const qvec3f face1normal {0, 0, 1}; + const qvec3f face2normal {0, 0, 1}; + + EXPECT_EQ(concavity_t::Coplanar, FacePairConcavity(face1center, face1normal, face2center, face2normal)); +} static const float MANGLE_EPSILON = 0.1f; TEST(light, vec_from_mangle) {