TEST(TriangleMesh3, ClosestDistance) { std::string objStr = getCubeTriMesh3x3x3Obj(); std::istringstream objStream(objStr); TriangleMesh3 mesh; mesh.readObj(&objStream); const auto bruteForceSearch = [&](const Vector3D& pt) { double minDist = kMaxD; for (size_t i = 0; i < mesh.numberOfTriangles(); ++i) { Triangle3 tri = mesh.triangle(i); auto localResult = tri.closestDistance(pt); if (localResult < minDist) { minDist = localResult; } } return minDist; }; size_t numSamples = getNumberOfSamplePoints3(); for (size_t i = 0; i < numSamples; ++i) { auto actual = mesh.closestDistance(getSamplePoints3()[i]); auto expected = bruteForceSearch(getSamplePoints3()[i]); EXPECT_DOUBLE_EQ(expected, actual); } }
TEST(TriangleMesh3, Intersects) { std::string objStr = getCubeTriMesh3x3x3Obj(); std::istringstream objStream(objStr); TriangleMesh3 mesh; mesh.readObj(&objStream); size_t numSamples = getNumberOfSamplePoints3(); const auto bruteForceTest = [&](const Ray3D& ray) { for (size_t i = 0; i < mesh.numberOfTriangles(); ++i) { Triangle3 tri = mesh.triangle(i); if (tri.intersects(ray)) { return true; } } return false; }; for (size_t i = 0; i < numSamples; ++i) { Ray3D ray(getSamplePoints3()[i], getSampleDirs3()[i]); bool actual = mesh.intersects(ray); bool expected = bruteForceTest(ray); EXPECT_EQ(expected, actual); } }
TEST(TriangleMesh3, ClosestNormal) { std::string objStr = getSphereTriMesh5x5Obj(); std::istringstream objStream(objStr); TriangleMesh3 mesh; mesh.readObj(&objStream); const auto bruteForceSearch = [&](const Vector3D& pt) { double minDist2 = kMaxD; Vector3D result; for (size_t i = 0; i < mesh.numberOfTriangles(); ++i) { Triangle3 tri = mesh.triangle(i); auto localResult = tri.closestNormal(pt); auto closestPt = tri.closestPoint(pt); double localDist2 = pt.distanceSquaredTo(closestPt); if (localDist2 < minDist2) { minDist2 = localDist2; result = localResult; } } return result; }; size_t numSamples = getNumberOfSamplePoints3(); for (size_t i = 0; i < numSamples; ++i) { auto actual = mesh.closestNormal(getSamplePoints3()[i]); auto expected = bruteForceSearch(getSamplePoints3()[i]); EXPECT_VECTOR3_NEAR(expected, actual, 1e-9); } }
TEST(TriangleMesh3, ClosestIntersection) { std::string objStr = getCubeTriMesh3x3x3Obj(); std::istringstream objStream(objStr); TriangleMesh3 mesh; mesh.readObj(&objStream); size_t numSamples = getNumberOfSamplePoints3(); const auto bruteForceTest = [&](const Ray3D& ray) { SurfaceRayIntersection3 result{}; for (size_t i = 0; i < mesh.numberOfTriangles(); ++i) { Triangle3 tri = mesh.triangle(i); auto localResult = tri.closestIntersection(ray); if (localResult.distance < result.distance) { result = localResult; } } return result; }; for (size_t i = 0; i < numSamples; ++i) { Ray3D ray(getSamplePoints3()[i], getSampleDirs3()[i]); auto actual = mesh.closestIntersection(ray); auto expected = bruteForceTest(ray); EXPECT_DOUBLE_EQ(expected.distance, actual.distance); EXPECT_VECTOR3_EQ(expected.point, actual.point); EXPECT_VECTOR3_EQ(expected.normal, actual.normal); EXPECT_EQ(expected.isIntersecting, actual.isIntersecting); } }
TEST(TriangleMesh3, BoundingBox) { std::string objStr = getCubeTriMesh3x3x3Obj(); std::istringstream objStream(objStr); TriangleMesh3 mesh; mesh.readObj(&objStream); EXPECT_BOUNDING_BOX3_EQ( BoundingBox3D({-0.5, -0.5, -0.5}, {0.5, 0.5, 0.5}), mesh.boundingBox()); }
JET_END_TEST_F JET_BEGIN_TEST_F(TriangleMesh3, BasicIO) { TriangleMesh3 triMesh; std::ifstream file(RESOURCES_DIR "bunny.obj"); if (file) { triMesh.readObj(&file); file.close(); } }
TEST(TriangleMesh3, IsInside) { std::string objStr = getCubeTriMesh3x3x3Obj(); std::istringstream objStream(objStr); TriangleMesh3 mesh; mesh.readObj(&objStream); size_t numSamples = getNumberOfSamplePoints3(); for (size_t i = 0; i < numSamples; ++i) { Vector3D p = getSamplePoints3()[i]; auto actual = mesh.isInside(p); auto expected = mesh.boundingBox().contains(p); EXPECT_EQ(expected, actual); } }
void triangulateAndSave(const ScalarGrid3& sdf, const std::string& objFilename) { TriangleMesh3 mesh; marchingCubes(sdf.constDataAccessor(), sdf.gridSpacing(), sdf.dataOrigin(), &mesh, 0.0, kDirectionAll); std::ofstream file(objFilename.c_str()); if (file) { printf("Writing %s...\n", objFilename.c_str()); mesh.writeObj(&file); file.close(); } else { printf("Cannot write file %s.\n", objFilename.c_str()); exit(EXIT_FAILURE); } }
TEST(TriangleMesh3, ReadObj) { std::string objStr = getCubeTriMesh3x3x3Obj(); std::istringstream objStream(objStr); TriangleMesh3 mesh; mesh.readObj(&objStream); EXPECT_EQ(56u, mesh.numberOfPoints()); EXPECT_EQ(96u, mesh.numberOfNormals()); EXPECT_EQ(76u, mesh.numberOfUvs()); EXPECT_EQ(108u, mesh.numberOfTriangles()); }
TEST(TriangleMesh3, Builder) { TriangleMesh3::PointArray points = { Vector3D(1, 2, 3), Vector3D(4, 5, 6), Vector3D(7, 8, 9), Vector3D(10, 11, 12) }; TriangleMesh3::NormalArray normals = { Vector3D(10, 11, 12), Vector3D(7, 8, 9), Vector3D(4, 5, 6), Vector3D(1, 2, 3) }; TriangleMesh3::UvArray uvs = { Vector2D(13, 14), Vector2D(15, 16) }; TriangleMesh3::IndexArray pointIndices = { Point3UI(0, 1, 2), Point3UI(0, 1, 3) }; TriangleMesh3::IndexArray normalIndices = { Point3UI(1, 2, 3), Point3UI(2, 1, 0) }; TriangleMesh3::IndexArray uvIndices = { Point3UI(1, 0, 2), Point3UI(3, 1, 0) }; TriangleMesh3 mesh = TriangleMesh3::builder() .withPoints(points) .withNormals(normals) .withUvs(uvs) .withPointIndices(pointIndices) .withNormalIndices(normalIndices) .withUvIndices(uvIndices) .build(); EXPECT_EQ(4u, mesh.numberOfPoints()); EXPECT_EQ(4u, mesh.numberOfNormals()); EXPECT_EQ(2u, mesh.numberOfUvs()); EXPECT_EQ(2u, mesh.numberOfTriangles()); for (size_t i = 0; i < mesh.numberOfPoints(); ++i) { EXPECT_EQ(points[i], mesh.point(i)); } for (size_t i = 0; i < mesh.numberOfNormals(); ++i) { EXPECT_EQ(normals[i], mesh.normal(i)); } for (size_t i = 0; i < mesh.numberOfUvs(); ++i) { EXPECT_EQ(uvs[i], mesh.uv(i)); } for (size_t i = 0; i < mesh.numberOfTriangles(); ++i) { EXPECT_EQ(pointIndices[i], mesh.pointIndex(i)); EXPECT_EQ(normalIndices[i], mesh.normalIndex(i)); EXPECT_EQ(uvIndices[i], mesh.uvIndex(i)); } }
JET_END_TEST_F JET_BEGIN_TEST_F(TriangleMesh3, PointsAndNormalGeometries) { TriangleMesh3 triMesh; triMesh.addPoint({0, 0, 0}); triMesh.addPoint({0, 0, 1}); triMesh.addPoint({0, 1, 0}); triMesh.addPoint({0, 1, 1}); triMesh.addPoint({1, 0, 0}); triMesh.addPoint({1, 0, 1}); triMesh.addPoint({1, 1, 0}); triMesh.addPoint({1, 1, 1}); triMesh.addNormal({-1, 0, 0}); triMesh.addNormal({1, 0, 0}); triMesh.addNormal({0, -1, 0}); triMesh.addNormal({0, 1, 0}); triMesh.addNormal({0, 0, -1}); triMesh.addNormal({0, 0, 1}); // -x triMesh.addPointNormalTriangle({0, 1, 3}, {0, 0, 0}); triMesh.addPointNormalTriangle({0, 3, 2}, {0, 0, 0}); // +x triMesh.addPointNormalTriangle({4, 6, 7}, {1, 1, 1}); triMesh.addPointNormalTriangle({4, 7, 5}, {1, 1, 1}); // -y triMesh.addPointNormalTriangle({0, 4, 5}, {2, 2, 2}); triMesh.addPointNormalTriangle({0, 5, 1}, {2, 2, 2}); // +y triMesh.addPointNormalTriangle({2, 3, 7}, {3, 3, 3}); triMesh.addPointNormalTriangle({2, 7, 6}, {3, 3, 3}); // -z triMesh.addPointNormalTriangle({0, 2, 6}, {4, 4, 4}); triMesh.addPointNormalTriangle({0, 6, 4}, {4, 4, 4}); // +z triMesh.addPointNormalTriangle({1, 5, 7}, {5, 5, 5}); triMesh.addPointNormalTriangle({1, 7, 3}, {5, 5, 5}); saveTriangleMeshData(triMesh, "cube_with_normal.obj"); }
JET_BEGIN_TEST_F(TriangleMesh3, PointsOnlyGeometries) { TriangleMesh3 triMesh; triMesh.addPoint({0, 0, 0}); triMesh.addPoint({0, 0, 1}); triMesh.addPoint({0, 1, 0}); triMesh.addPoint({0, 1, 1}); triMesh.addPoint({1, 0, 0}); triMesh.addPoint({1, 0, 1}); triMesh.addPoint({1, 1, 0}); triMesh.addPoint({1, 1, 1}); // -x triMesh.addPointTriangle({0, 1, 3}); triMesh.addPointTriangle({0, 3, 2}); // +x triMesh.addPointTriangle({4, 6, 7}); triMesh.addPointTriangle({4, 7, 5}); // -y triMesh.addPointTriangle({0, 4, 5}); triMesh.addPointTriangle({0, 5, 1}); // +y triMesh.addPointTriangle({2, 3, 7}); triMesh.addPointTriangle({2, 7, 6}); // -z triMesh.addPointTriangle({0, 2, 6}); triMesh.addPointTriangle({0, 6, 4}); // +z triMesh.addPointTriangle({1, 5, 7}); triMesh.addPointTriangle({1, 7, 3}); saveTriangleMeshData(triMesh, "cube.obj"); }