#include "gtest/gtest.h" #include #include #include #include #include using namespace glm; using namespace std; static glm::vec4 extendTo4(const glm::vec3 &v) { return glm::vec4(v[0], v[1], v[2], 1.0); } TEST(mathlib, MakeCDF) { std::vector pdfUnnormzlied { 25, 50, 25 }; std::vector cdf = MakeCDF(pdfUnnormzlied); ASSERT_EQ(3u, cdf.size()); ASSERT_FLOAT_EQ(0.25, cdf.at(0)); ASSERT_FLOAT_EQ(0.75, cdf.at(1)); ASSERT_FLOAT_EQ(1.0, cdf.at(2)); // TODO: return pdf ASSERT_EQ(0, SampleCDF(cdf, 0)); ASSERT_EQ(0, SampleCDF(cdf, 0.1)); ASSERT_EQ(0, SampleCDF(cdf, 0.25)); ASSERT_EQ(1, SampleCDF(cdf, 0.26)); ASSERT_EQ(1, SampleCDF(cdf, 0.75)); ASSERT_EQ(2, SampleCDF(cdf, 0.76)); ASSERT_EQ(2, SampleCDF(cdf, 1)); } static void checkBox(const vector &edges, const vector &poly) { EXPECT_TRUE(GLM_EdgePlanes_PointInside(edges, vec3(0,0,0))); EXPECT_TRUE(GLM_EdgePlanes_PointInside(edges, vec3(64,0,0))); EXPECT_TRUE(GLM_EdgePlanes_PointInside(edges, vec3(32,32,0))); EXPECT_TRUE(GLM_EdgePlanes_PointInside(edges, vec3(32,32,32))); // off plane EXPECT_FALSE(GLM_EdgePlanes_PointInside(edges, vec3(-0.1,0,0))); EXPECT_FALSE(GLM_EdgePlanes_PointInside(edges, vec3(64.1,0,0))); EXPECT_FALSE(GLM_EdgePlanes_PointInside(edges, vec3(0,-0.1,0))); EXPECT_FALSE(GLM_EdgePlanes_PointInside(edges, vec3(0,64.1,0))); } TEST(mathlib, EdgePlanesOfNonConvexPoly) { // hourglass, non-convex const vector poly { { 0,0,0 }, { 64,64,0 }, { 0,64,0 }, { 64,0,0 } }; const auto edges = GLM_MakeInwardFacingEdgePlanes(poly); // EXPECT_EQ(vector(), edges); } TEST(mathlib, SlightlyConcavePoly) { const vector poly { {225.846161, -1744, 1774}, {248, -1744, 1798}, {248, -1763.82605, 1799.65222}, {248, -1764, 1799.66663}, {248, -1892, 1810.33337}, {248, -1893.21741, 1810.43481}, {248, -1921.59998, 1812.80005}, {248, -1924, 1813}, {80, -1924, 1631}, {80, -1744, 1616} }; const auto edges = GLM_MakeInwardFacingEdgePlanes(poly); ASSERT_FALSE(edges.empty()); EXPECT_TRUE(GLM_EdgePlanes_PointInside(edges, vec3(152.636963, -1814, 1702))); } TEST(mathlib, PointInPolygon) { // clockwise const vector poly { { 0,0,0 }, { 0,64,0 }, { 64,64,0 }, { 64,0,0 } }; const auto edges = GLM_MakeInwardFacingEdgePlanes(poly); checkBox(edges, poly); } TEST(mathlib, PointInPolygon_DegenerateEdgeHandling) { // clockwise const vector poly { { 0,0,0 }, { 0,64,0 }, { 0,64,0 }, // repeat of last point { 64,64,0 }, { 64,0,0 } }; const auto edges = GLM_MakeInwardFacingEdgePlanes(poly); checkBox(edges, poly); } TEST(mathlib, PointInPolygon_DegenerateFaceHandling1) { const vector poly { }; const auto edges = GLM_MakeInwardFacingEdgePlanes(poly); EXPECT_FALSE(GLM_EdgePlanes_PointInside(edges, vec3(0,0,0))); EXPECT_FALSE(GLM_EdgePlanes_PointInside(edges, vec3(10,10,10))); } TEST(mathlib, PointInPolygon_DegenerateFaceHandling2) { const vector poly { {0,0,0}, {0,0,0}, {0,0,0}, }; const auto edges = GLM_MakeInwardFacingEdgePlanes(poly); EXPECT_FALSE(GLM_EdgePlanes_PointInside(edges, vec3(0,0,0))); EXPECT_FALSE(GLM_EdgePlanes_PointInside(edges, vec3(10,10,10))); EXPECT_FALSE(GLM_EdgePlanes_PointInside(edges, vec3(-10,-10,-10))); } TEST(mathlib, PointInPolygon_DegenerateFaceHandling3) { const vector poly { {0,0,0}, {10,10,10}, {20,20,20}, }; const auto edges = GLM_MakeInwardFacingEdgePlanes(poly); EXPECT_FALSE(GLM_EdgePlanes_PointInside(edges, vec3(0,0,0))); EXPECT_FALSE(GLM_EdgePlanes_PointInside(edges, vec3(10,10,10))); EXPECT_FALSE(GLM_EdgePlanes_PointInside(edges, vec3(-10,-10,-10))); } TEST(mathlib, PointInPolygon_ColinearPointHandling) { // clockwise const vector poly { { 0,0,0 }, { 0,32,0 }, // colinear { 0,64,0 }, { 64,64,0 }, { 64,0,0 } }; const auto edges = GLM_MakeInwardFacingEdgePlanes(poly); checkBox(edges, poly); } TEST(mathlib, ClosestPointOnPolyBoundary) { // clockwise const vector poly { { 0,0,0 }, // edge 0 start, edge 3 end { 0,64,0 }, // edge 1 start, edge 0 end { 64,64,0 }, // edge 2 start, edge 1 end { 64,0,0 } // edge 3 start, edge 2 end }; EXPECT_EQ(make_pair(0, vec3(0,0,0)), GLM_ClosestPointOnPolyBoundary(poly, vec3(0,0,0))); // Either edge 1 or 2 contain the point vec3(64,64,0), but we expect the first edge to be returned EXPECT_EQ(make_pair(1, vec3(64,64,0)), GLM_ClosestPointOnPolyBoundary(poly, vec3(100,100,100))); EXPECT_EQ(make_pair(2, vec3(64,32,0)), GLM_ClosestPointOnPolyBoundary(poly, vec3(100,32,0))); EXPECT_EQ(make_pair(0, vec3(0,0,0)), GLM_ClosestPointOnPolyBoundary(poly, vec3(-1,-1,0))); } TEST(mathlib, PolygonCentroid) { // poor test.. but at least checks that the colinear point is treated correctly const vector poly { { 0,0,0 }, { 0,32,0 }, // colinear { 0,64,0 }, { 64,64,0 }, { 64,0,0 } }; EXPECT_EQ(vec3(32,32,0), GLM_PolyCentroid(poly)); } TEST(mathlib, BarycentricFromPoint) { const tri_t tri = make_tuple( // clockwise { 0,0,0 }, { 0,64,0 }, { 64,0,0 } ); EXPECT_EQ(vec3(1,0,0), Barycentric_FromPoint(get<0>(tri), tri)); EXPECT_EQ(vec3(0,1,0), Barycentric_FromPoint(get<1>(tri), tri)); EXPECT_EQ(vec3(0,0,1), Barycentric_FromPoint(get<2>(tri), tri)); EXPECT_EQ(vec3(0.5, 0.5, 0.0), Barycentric_FromPoint(vec3(0,32,0), tri)); EXPECT_EQ(vec3(0.0, 0.5, 0.5), Barycentric_FromPoint(vec3(32,32,0), tri)); EXPECT_EQ(vec3(0.5, 0.0, 0.5), Barycentric_FromPoint(vec3(32,0,0), tri)); } TEST(mathlib, BarycentricToPoint) { const tri_t tri = make_tuple( // clockwise { 0,0,0 }, { 0,64,0 }, { 64,0,0 } ); EXPECT_EQ(get<0>(tri), Barycentric_ToPoint(vec3(1,0,0), tri)); EXPECT_EQ(get<1>(tri), Barycentric_ToPoint(vec3(0,1,0), tri)); EXPECT_EQ(get<2>(tri), Barycentric_ToPoint(vec3(0,0,1), tri)); EXPECT_EQ(vec3(0,32,0), Barycentric_ToPoint(vec3(0.5, 0.5, 0.0), tri)); EXPECT_EQ(vec3(32,32,0), Barycentric_ToPoint(vec3(0.0, 0.5, 0.5), tri)); EXPECT_EQ(vec3(32,0,0), Barycentric_ToPoint(vec3(0.5, 0.0, 0.5), tri)); } TEST(mathlib, BarycentricRandom) { const tri_t tri = make_tuple( // clockwise { 0,0,0 }, { 0,64,0 }, { 64,0,0 } ); const auto triAsVec = vector{get<0>(tri), get<1>(tri), get<2>(tri)}; const auto edges = GLM_MakeInwardFacingEdgePlanes(triAsVec); const auto plane = GLM_PolyPlane(triAsVec); for (int i=0; i<100; i++) { const float r0 = Random(); const float r1 = Random(); ASSERT_GE(r0, 0); ASSERT_GE(r1, 0); ASSERT_LE(r0, 1); ASSERT_LE(r1, 1); const auto bary = Barycentric_Random(r0, r1); EXPECT_FLOAT_EQ(1.0f, bary.x + bary.y + bary.z); const vec3 point = Barycentric_ToPoint(bary, tri); EXPECT_TRUE(GLM_EdgePlanes_PointInside(edges, point)); EXPECT_FLOAT_EQ(0.0f, GLM_DistAbovePlane(plane, point)); } } TEST(mathlib, DistAbovePlane) { vec4 plane(0, 0, 1, 10); vec3 point(100, 100, 100); EXPECT_FLOAT_EQ(90, GLM_DistAbovePlane(plane, point)); } TEST(mathlib, ProjectPointOntoPlane) { vec4 plane(0, 0, 1, 10); vec3 point(100, 100, 100); vec3 projected = GLM_ProjectPointOntoPlane(plane, point); EXPECT_FLOAT_EQ(100, projected.x); EXPECT_FLOAT_EQ(100, projected.y); EXPECT_FLOAT_EQ(10, projected.z); } TEST(mathlib, InterpolateNormals) { // This test relies on the way GLM_InterpolateNormal is implemented // o--o--o // | / / | // |// | // o-----o const vector poly { { 0,0,0 }, { 0,64,0 }, { 32,64,0 }, // colinear { 64,64,0 }, { 64,0,0 } }; const vector normals { { 1,0,0 }, { 0,1,0 }, { 0,0,1 }, // colinear { 0,0,0 }, { -1,0,0 } }; // First try all the known points for (int i=0; i &p1, const vector &p2) { if (p1.size() != p2.size()) return false; for (int i=0; i poly { { 0,0,0 }, { 0,64,0 }, { 64,64,0 }, { 64,0,0 } }; const vector frontRes { { 0,0,0 }, { 0,64,0 }, { 32,64,0 }, { 32,0,0 } }; const vector backRes { { 32,64,0 }, { 64,64,0 }, { 64,0,0 }, { 32,0,0 } }; auto clipRes = GLM_ClipPoly(poly, vec4(-1,0,0,-32)); EXPECT_TRUE(polysEqual(frontRes, clipRes.first)); EXPECT_TRUE(polysEqual(backRes, clipRes.second)); } TEST(mathlib, ShrinkPoly1) { const vector poly { { 0,0,0 }, { 0,64,0 }, { 64,64,0 }, { 64,0,0 } }; const vector shrunkPoly { { 1,1,0 }, { 1,63,0 }, { 63,63,0 }, { 63,1,0 } }; const auto actualShrunk = GLM_ShrinkPoly(poly, 1.0f); EXPECT_TRUE(polysEqual(shrunkPoly, actualShrunk)); } TEST(mathlib, ShrinkPoly2) { const vector poly { { 0,0,0 }, { 64,64,0 }, { 64,0,0 } }; const vector shrunkPoly { { 1.0f + sqrtf(2.0f), 1.0f, 0.0f }, { 63.0f, 63.0f - sqrtf(2.0f), 0.0f }, { 63,1,0 }, }; const auto actualShrunk = GLM_ShrinkPoly(poly, 1.0f); EXPECT_TRUE(polysEqual(shrunkPoly, actualShrunk)); } constexpr float MANGLE_EPSILON = 0.1f; TEST(light, vec_from_mangle) { EXPECT_TRUE(pointsEqualEpsilon(vec3(1,0,0), vec_from_mangle(vec3(0,0,0)), MANGLE_EPSILON)); EXPECT_TRUE(pointsEqualEpsilon(vec3(-1,0,0), vec_from_mangle(vec3(180,0,0)), MANGLE_EPSILON)); EXPECT_TRUE(pointsEqualEpsilon(vec3(0,0,1), vec_from_mangle(vec3(0,90,0)), MANGLE_EPSILON)); EXPECT_TRUE(pointsEqualEpsilon(vec3(0,0,-1), vec_from_mangle(vec3(0,-90,0)), MANGLE_EPSILON)); } TEST(light, mangle_from_vec) { EXPECT_TRUE(pointsEqualEpsilon(vec3(0,0,0), mangle_from_vec(vec3(1,0,0)), MANGLE_EPSILON)); EXPECT_TRUE(pointsEqualEpsilon(vec3(180,0,0), mangle_from_vec(vec3(-1,0,0)), MANGLE_EPSILON)); EXPECT_TRUE(pointsEqualEpsilon(vec3(0,90,0), mangle_from_vec(vec3(0,0,1)), MANGLE_EPSILON)); EXPECT_TRUE(pointsEqualEpsilon(vec3(0,-90,0), mangle_from_vec(vec3(0,0,-1)), MANGLE_EPSILON)); for (int yaw = -179; yaw <= 179; yaw++) { for (int pitch = -89; pitch <= 89; pitch++) { const vec3 origMangle = vec3(yaw, pitch, 0); const vec3 vec = vec_from_mangle(origMangle); const vec3 roundtrip = mangle_from_vec(vec); EXPECT_TRUE(pointsEqualEpsilon(origMangle, roundtrip, MANGLE_EPSILON)); } } } TEST(mathlib, bilinearInterpolate) { const vec4 v1(0,1,2,3); const vec4 v2(4,5,6,7); const vec4 v3(1,1,1,1); const vec4 v4(2,2,2,2); EXPECT_EQ(v1, bilinearInterpolate(v1, v2, v3, v4, 0.0f, 0.0f)); EXPECT_EQ(v2, bilinearInterpolate(v1, v2, v3, v4, 1.0f, 0.0f)); EXPECT_EQ(v3, bilinearInterpolate(v1, v2, v3, v4, 0.0f, 1.0f)); EXPECT_EQ(v4, bilinearInterpolate(v1, v2, v3, v4, 1.0f, 1.0f)); EXPECT_EQ(vec4(1.5, 1.5, 1.5, 1.5), bilinearInterpolate(v1, v2, v3, v4, 0.5f, 1.0f)); EXPECT_EQ(vec4(2, 3, 4, 5), bilinearInterpolate(v1, v2, v3, v4, 0.5f, 0.0f)); EXPECT_EQ(vec4(1.75, 2.25, 2.75, 3.25), bilinearInterpolate(v1, v2, v3, v4, 0.5f, 0.5f)); } TEST(mathlib, bilinearWeightsAndCoords) { const auto res = bilinearWeightsAndCoords(vec2(0.5, 0.25), ivec2(2,2)); vec2 sum(0); for (int i=0; i<4; i++) { const float weight = res[i].second; const ivec2 intPos = res[i].first; sum += vec2(intPos) * weight; } EXPECT_EQ(vec2(0.5, 0.25), sum); } TEST(mathlib, bilinearWeightsAndCoords2) { const auto res = bilinearWeightsAndCoords(vec2(1.5, 0.5), ivec2(2,2)); vec2 sum(0); for (int i=0; i<4; i++) { const float weight = res[i].second; const ivec2 intPos = res[i].first; sum += vec2(intPos) * weight; } EXPECT_EQ(vec2(1.0, 0.5), sum); } TEST(mathlib, pointsAlongLine) { const auto res = PointsAlongLine(vec3(1,0,0), vec3(3.5, 0, 0), 1.5f); ASSERT_EQ(2, res.size()); ASSERT_TRUE(pointsEqualEpsilon(vec3(1,0,0), res[0], POINT_EQUAL_EPSILON)); ASSERT_TRUE(pointsEqualEpsilon(vec3(2.5,0,0), res[1], POINT_EQUAL_EPSILON)); }