Browse Source

add unit tests for embree (#952)

* add unit tests for embree

* discovered bug with intersect beam and wrote unit test for the fix

* Update EmbreeIntersector.cpp

updated test to catch2

* Update CMakeLists.txt

removed tab to rerun CI

* Update CMakeLists.txt

* Update CMakeLists.txt

* caching value of BUILD_TESTING before resetting it

* Try to build embree statically on Windows.

* Try to enable testing early on.

* Update Catch2 version.

* Set CMake build type.

* Trying to build embree statically.

* Fix static compilation of embree on Windows.

* Cleanup.
ikoruk 6 years ago
parent
commit
b7124f179f

+ 12 - 5
.appveyor.yml

@@ -1,15 +1,20 @@
 version: 1.0.{build}
 os: Visual Studio 2017
-test: off
+platform: x64
 clone_folder: C:\projects\libigl
+shallow_clone: true
 branches:
   only:
     - master
     - dev
 environment:
-  BOOST_ROOT: C:/Libraries/boost_1_65_1
+  matrix:
+  - config: Debug
+    BOOST_ROOT: C:/Libraries/boost_1_65_1
 install:
   - cinstall: python
+build:
+  parallel: true
 build_script:
   - cd c:\projects\libigl
   # External libraries (boost)
@@ -31,8 +36,10 @@ build_script:
   # Tutorials and tests
   - mkdir build
   - cd build
-  - cmake -DLIBIGL_BUILD_TESTS=ON -DLIBIGL_WITH_COMISO=OFF -DLIBIGL_BUILD_TUTORIALS=ON -DLIBIGL_BUILD_PYTHON=OFF -G "Visual Studio 15 2017 Win64" ../
+  - cmake -DCMAKE_BUILD_TYPE=%config% -DLIBIGL_WITH_COMISO=OFF -DLIBIGL_BUILD_PYTHON=OFF -G "Visual Studio 15 2017 Win64" ../
   - set MSBuildLogger="C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
-  - set MSBuildOptions=/v:m /p:Configuration=Debug /logger:%MSBuildLogger%
+  - set MSBuildOptions=/v:m /p:Configuration=%config% /logger:%MSBuildLogger%
   - msbuild %MSBuildOptions% libigl.sln
-  - ctest -C Debug --verbose
+
+test_script:
+  - ctest -C %config% --verbose --output-on-failure -j 2

+ 2 - 2
cmake/LibiglDownloadExternal.cmake

@@ -133,11 +133,11 @@ function(igl_download_triangle)
 	)
 endfunction()
 
-## Google test
+## Catch2
 function(igl_download_catch2)
 	igl_download_project(catch2
 		GIT_REPOSITORY https://github.com/catchorg/Catch2.git
-		GIT_TAG        1faccd601d904a951142d8fba82914a8325b764e
+		GIT_TAG        03d122a35c3f5c398c43095a87bc82ed44642516
 	)
 endfunction()
 

+ 2 - 0
cmake/LibiglWindows.cmake

@@ -9,6 +9,7 @@ if(MSVC)
 			string(REPLACE /MD /MT CMAKE_C_FLAGS_${config} "${CMAKE_C_FLAGS_${config}}")
 			string(REPLACE /MD /MT CMAKE_CXX_FLAGS_${config} "${CMAKE_CXX_FLAGS_${config}}")
 		endforeach()
+		string(REPLACE "/MDd" "/MTd" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG})
 	else()
 		message(STATUS "MSVC -> forcing use of dynamically-linked runtime.")
 		foreach(config ${CMAKE_CONFIGURATION_TYPES})
@@ -16,5 +17,6 @@ if(MSVC)
 			string(REPLACE /MT /MD CMAKE_C_FLAGS_${config} "${CMAKE_C_FLAGS_${config}}")
 			string(REPLACE /MT /MD CMAKE_CXX_FLAGS_${config} "${CMAKE_CXX_FLAGS_${config}}")
 		endforeach()
+		string(REPLACE "/MTd" "/MDd" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG})
 	endif()
 endif()

+ 7 - 18
cmake/libigl.cmake

@@ -265,39 +265,28 @@ endif()
 if(LIBIGL_WITH_EMBREE)
   set(EMBREE_DIR "${LIBIGL_EXTERNAL}/embree")
 
+  set(EMBREE_TESTING_INTENSITY 0 CACHE INT "" FORCE)
   set(EMBREE_ISPC_SUPPORT OFF CACHE BOOL " " FORCE)
   set(EMBREE_TASKING_SYSTEM "INTERNAL" CACHE BOOL " " FORCE)
   set(EMBREE_TUTORIALS OFF CACHE BOOL " " FORCE)
   set(EMBREE_MAX_ISA NONE CACHE STRINGS " " FORCE)
-  set(BUILD_TESTING OFF CACHE BOOL " " FORCE)
-
-  # set(ENABLE_INSTALLER OFF CACHE BOOL " " FORCE)
+  set(EMBREE_STATIC_LIB ON CACHE BOOL " " FORCE)
   if(MSVC)
-    # set(EMBREE_STATIC_RUNTIME OFF CACHE BOOL " " FORCE)
-    set(EMBREE_STATIC_LIB OFF CACHE BOOL " " FORCE)
-  else()
-    set(EMBREE_STATIC_LIB ON CACHE BOOL " " FORCE)
+    set(EMBREE_STATIC_RUNTIME ON CACHE BOOL " " FORCE)
   endif()
 
   if(NOT TARGET embree)
+    # TODO: Should probably save/restore the CMAKE_CXX_FLAGS_*, since embree seems to be
+    # overriding them on Windows. But well... it works for now.
     igl_download_embree()
     add_subdirectory("${EMBREE_DIR}" "embree")
   endif()
 
-  if(MSVC)
-    add_custom_target(Copy-Embree-DLL ALL # Adds a post-build event
-        COMMAND ${CMAKE_COMMAND} -E copy_if_different # which executes "cmake - E
-        $<TARGET_FILE:embree> # <--this is in-file
-        "${CMAKE_BINARY_DIR}" # <--this is out-file path
-        DEPENDS embree) # Execute after embree target has been built
-  endif()
-
   compile_igl_module("embree")
   target_link_libraries(igl_embree ${IGL_SCOPE} embree)
   target_include_directories(igl_embree ${IGL_SCOPE} ${EMBREE_DIR}/include)
-  if(NOT MSVC)
-    target_compile_definitions(igl_embree ${IGL_SCOPE} -DENABLE_STATIC_LIB)
-  endif()
+  target_compile_definitions(igl_embree ${IGL_SCOPE} -DEMBREE_STATIC_LIB)
+
 endif()
 
 ################################################################################

+ 1 - 0
include/igl/embree/EmbreeIntersector.h

@@ -432,6 +432,7 @@ inline bool igl::embree::EmbreeIntersector::intersectBeam(
   const float eps= 1e-5;
 
   Eigen::RowVector3f up(0,1,0);
+  if (direction.cross(up).norm() < eps) up = Eigen::RowVector3f(1,0,0);
   Eigen::RowVector3f offset = direction.cross(up).normalized();
 
   Eigen::Matrix3f rot = Eigen::AngleAxis<float>(2*3.14159265358979/samples,direction).toRotationMatrix();

+ 20 - 24
tests/CMakeLists.txt

@@ -4,7 +4,7 @@ project(libigl_tests)
 list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/../cmake)
 
 ### Adding libIGL: choose the path to your local copy libIGL
-if(NOT TARGET igl_common)
+if(NOT TARGET igl::core)
   include(libigl)
 else()
   include(LibiglDownloadExternal)
@@ -14,77 +14,73 @@ endif()
 igl_download_test_data()
 set(IGL_TEST_DATA ${LIBIGL_EXTERNAL}/../tests/data)
 
-### Download Google unit test framework.
+### Download Catch2 unit test framework
 igl_download_catch2()
 list(APPEND CMAKE_MODULE_PATH ${LIBIGL_EXTERNAL}/catch2/contrib)
 
 
-
-
 # Add catch2
-add_library(catch INTERFACE)
-target_include_directories(catch SYSTEM INTERFACE ${LIBIGL_EXTERNAL}/catch2/single_include)
+add_subdirectory(${LIBIGL_EXTERNAL}/catch2 catch2)
 
 
 # Create test executable
 add_executable(libigl_tests main.cpp test_common.h)
-target_link_libraries(libigl_tests PUBLIC igl::core catch)
+target_link_libraries(libigl_tests PUBLIC igl::core Catch2::Catch2)
 target_include_directories(libigl_tests PUBLIC ${CMAKE_CURRENT_LIST_DIR})
 
-# Set TEST_DIR definition
+# Set DATA_DIR definition
 set(DATA_DIR "${CMAKE_CURRENT_SOURCE_DIR}/data/")
 target_compile_definitions(libigl_tests PUBLIC -DLIBIGL_DATA_DIR="${IGL_TEST_DATA}")
 
 
 # Process code in each subdirectories: add in decreasing order of complexity
 # (last added will run first and those should be the fastest tests)
-IF(LIBIGL_WITH_MOSEK)
+if(LIBIGL_WITH_MOSEK)
   file(GLOB TEST_SRC_FILES ./include/igl/mosek/*.cpp)
   file(GLOB TEST_INC_FILES ./include/igl/mosek/*.h ./include/igl/mosek/*.inl)
   target_sources(libigl_tests PRIVATE ${TEST_SRC_FILES} ${TEST_INC_FILES})
 
   target_link_libraries(libigl_tests PUBLIC igl::mosek)
-ENDIF()
+endif()
 
-IF(LIBIGL_WITH_CGAL)
+if(LIBIGL_WITH_CGAL)
   file(GLOB TEST_SRC_FILES ./include/igl/copyleft/boolean/*.cpp ./include/igl/copyleft/cgal/*.cpp)
   file(GLOB TEST_INC_FILES ./include/igl/copyleft/boolean/*.h ./include/igl/copyleft/cgal/*.h ./include/igl/copyleft/boolean/*.inl ./include/igl/copyleft/cgal/*.inl)
   target_sources(libigl_tests PRIVATE ${TEST_SRC_FILES} ${TEST_INC_FILES})
 
   target_link_libraries(libigl_tests PUBLIC igl::cgal)
-ENDIF()
+endif()
 
-IF(LIBIGL_WITH_TETGEN)
+if(LIBIGL_WITH_TETGEN)
   file(GLOB TEST_SRC_FILES ./include/igl/copyleft/tetgen/*.cpp)
   file(GLOB TEST_INC_FILES ./include/igl/copyleft/tetgen/*.h ./include/igl/copyleft/tetgen/*.inl)
   target_sources(libigl_tests PRIVATE ${TEST_SRC_FILES} ${TEST_INC_FILES})
 
   target_link_libraries(libigl_tests PUBLIC igl::tetgen)
-ENDIF()
+endif()
 
-IF(LIBIGL_WITH_COMISO)
+if(LIBIGL_WITH_COMISO)
   file(GLOB TEST_SRC_FILES ./include/igl/copyleft/comiso/*.cpp)
   file(GLOB TEST_INC_FILES ./include/igl/copyleft/comiso/*.h ./include/igl/copyleft/comiso/*.inl)
   target_sources(libigl_tests PRIVATE ${TEST_SRC_FILES} ${TEST_INC_FILES})
 
   target_link_libraries(libigl_tests PUBLIC igl::comiso)
-ENDIF()
+endif()
+
+if(LIBIGL_WITH_EMBREE)
+  file(GLOB TEST_SRC_FILES ./include/igl/embree/*.cpp)
+  file(GLOB TEST_INC_FILES ./include/igl/embree/*.h ./include/igl/embree/*.inl)
+  target_sources(libigl_tests PRIVATE ${TEST_SRC_FILES} ${TEST_INC_FILES})
 
+  target_link_libraries(libigl_tests PUBLIC igl::embree)
+endif()
 
 file(GLOB TEST_SRC_FILES ./include/igl/*.cpp)
 file(GLOB TEST_INC_FILES ./include/igl/*.h ./include/igl/*.inl)
 target_sources(libigl_tests PRIVATE ${TEST_SRC_FILES} ${TEST_INC_FILES})
 
 
-
-
 # Register tests
 set(PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS ON)
 include(Catch)
 catch_discover_tests(libigl_tests)
-
-
-
-
-
-

+ 97 - 0
tests/include/igl/embree/EmbreeIntersector.cpp

@@ -0,0 +1,97 @@
+#include <test_common.h>
+#include <igl/embree/EmbreeIntersector.h>
+
+TEST_CASE("EmbreeIntersector: cube", "[igl/embree]")
+{
+  //The allowed error for this test
+  const double epsilon = 1e-6;
+
+  Eigen::MatrixXd V;
+  Eigen::MatrixXi F;
+  // This is a cube of dimensions 1.0x1.0x1.0
+  test_common::load_mesh("cube.obj", V, F);
+
+  // Initialize embree
+  igl::embree::EmbreeIntersector embree;
+  embree.init(V.cast<float>(),F.cast<int>());
+
+  const int expected_id[] = {4,8,5,2,7,0};
+  const float expected_u[] = {0.5,0.5,0.5,0.5,0.5,0.5};
+  const float expected_v[] = {0.5,0.0,0.0,0.0,0.5,0.0};
+
+  // Shoot ray from inside out
+  for (int dim=0; dim<6; ++dim)
+  {
+    Eigen::Vector3f pos(0,0,0);
+    Eigen::Vector3f dir(0,0,0);
+    // test each dimension, pos and neg
+    dir[dim/2] = dim%2 ? -1 : 1;
+    igl::Hit hit;
+    bool hitP = embree.intersectRay(pos, dir, hit);
+    CHECK(hitP);
+    REQUIRE(hit.t == Approx(0.5).margin(epsilon));
+    REQUIRE(hit.id == expected_id[dim]);
+    REQUIRE(hit.u == Approx(expected_u[dim]).margin(epsilon));
+    REQUIRE(hit.v == Approx(expected_v[dim]).margin(epsilon));
+  }
+
+  // Shoot ray from outside in
+  for (int dim=0; dim<6; ++dim)
+  {
+    Eigen::Vector3f dir(0,0,0);
+    // test each dimension, pos and neg
+    dir[dim/2] = dim%2 ? 1 : -1;
+
+    Eigen::Vector3f pos = -dir;
+
+    igl::Hit hit;
+    bool hitP = embree.intersectRay(pos, dir, hit);
+    CHECK(hitP);
+    REQUIRE(hit.t == Approx(0.5).margin(epsilon));
+    REQUIRE(hit.id == expected_id[dim]);
+    REQUIRE(hit.u == Approx(expected_u[dim]).margin(epsilon));
+    REQUIRE(hit.v == Approx(expected_v[dim]).margin(epsilon));
+  }
+
+  // Rays that miss
+  for (int dim=0; dim<6; ++dim)
+  {
+    Eigen::Vector3f pos(0,0,0);
+    Eigen::Vector3f dir(0,0,0);
+    // test each dimension, pos and neg
+    dir[dim/2] = dim%2 ? -1 : 1;
+    pos[(dim/2+1)%3] = dir[dim/2];
+
+    igl::Hit hit;
+    bool hitP = embree.intersectRay(pos, dir, hit);
+    CHECK_FALSE(hitP);
+  }
+
+  // intersect beam
+  {
+    Eigen::Vector3f pos(-0.5,-0.5,1);
+    Eigen::Vector3f dir(0,0,-1);
+
+    igl::Hit hit;
+    bool hitP = embree.intersectBeam(pos, dir, hit);
+    CHECK(hitP);
+    REQUIRE(hit.t == Approx(0.5).margin(epsilon));
+    REQUIRE(hit.id == 7);
+    REQUIRE(hit.u == Approx(0).margin(epsilon));
+    REQUIRE(hit.v == Approx(1).margin(epsilon));
+  }
+
+  {
+    Eigen::Vector3f pos(0.5,-1,0.5);
+    Eigen::Vector3f dir(0,1,0);
+
+    igl::Hit hit;
+    bool hitP = embree.intersectBeam(pos, dir, hit);
+    CHECK(hitP);
+    REQUIRE(hit.t == Approx(0.5).margin(epsilon));
+    REQUIRE(hit.id == 2);
+    REQUIRE(hit.u == Approx(0).margin(epsilon));
+    REQUIRE(hit.v == Approx(0).margin(epsilon));
+  }
+}
+