From c618f43c180554e7512966c015133476bc813529 Mon Sep 17 00:00:00 2001 From: cDc Date: Mon, 6 Jul 2020 15:17:46 +0300 Subject: [PATCH 001/252] dense: add image mask support --- apps/DensifyPointCloud/DensifyPointCloud.cpp | 3 ++ libs/Common/Types.h | 1 + libs/MVS/DepthMap.cpp | 44 ++++++++++++++++++++ libs/MVS/DepthMap.h | 4 ++ libs/MVS/Image.h | 5 +++ libs/MVS/Scene.cpp | 7 ++++ libs/MVS/SceneDensify.cpp | 13 +++++- 7 files changed, 76 insertions(+), 1 deletion(-) diff --git a/apps/DensifyPointCloud/DensifyPointCloud.cpp b/apps/DensifyPointCloud/DensifyPointCloud.cpp index d82c07294..8191db8f1 100644 --- a/apps/DensifyPointCloud/DensifyPointCloud.cpp +++ b/apps/DensifyPointCloud/DensifyPointCloud.cpp @@ -93,6 +93,7 @@ bool Initialize(size_t argc, LPCTSTR* argv) unsigned nMinViewsFuse; unsigned nEstimateColors; unsigned nEstimateNormals; + int nIgnoreMaskLabel; boost::program_options::options_description config("Densify options"); config.add_options() ("input-file,i", boost::program_options::value(&OPT::strInputFileName), "input filename containing camera poses and image list") @@ -102,6 +103,7 @@ bool Initialize(size_t argc, LPCTSTR* argv) ("min-resolution", boost::program_options::value(&nMinResolution)->default_value(640), "do not scale images lower than this resolution") ("number-views", boost::program_options::value(&nNumViews)->default_value(5), "number of views used for depth-map estimation (0 - all neighbor views available)") ("number-views-fuse", boost::program_options::value(&nMinViewsFuse)->default_value(3), "minimum number of images that agrees with an estimate during fusion in order to consider it inlier") + ("ignore-mask-label", boost::program_options::value(&nIgnoreMaskLabel)->default_value(-1), "integer value for the label to ignore in the segmentation mask (<0 - disabled)") ("estimate-colors", boost::program_options::value(&nEstimateColors)->default_value(2), "estimate the colors for the dense point-cloud (0 - disabled, 1 - final, 2 - estimate)") ("estimate-normals", boost::program_options::value(&nEstimateNormals)->default_value(2), "estimate the normals for the dense point-cloud (0 - disabled, 1 - final, 2 - estimate)") ("sample-mesh", boost::program_options::value(&OPT::fSampleMesh)->default_value(0.f), "uniformly samples points on a mesh (0 - disabled, <0 - number of points, >0 - sample density per square unit)") @@ -179,6 +181,7 @@ bool Initialize(size_t argc, LPCTSTR* argv) OPTDENSE::nMinViewsFuse = nMinViewsFuse; OPTDENSE::nEstimateColors = nEstimateColors; OPTDENSE::nEstimateNormals = nEstimateNormals; + OPTDENSE::nIgnoreMaskLabel = nIgnoreMaskLabel; if (!bValidConfig && !OPT::strDenseConfigFileName.IsEmpty()) OPTDENSE::oConfig.Save(OPT::strDenseConfigFileName); diff --git a/libs/Common/Types.h b/libs/Common/Types.h index 631f024ce..89d6e4294 100644 --- a/libs/Common/Types.h +++ b/libs/Common/Types.h @@ -2179,6 +2179,7 @@ class TImage : public TDMatrix }; /*----------------------------------------------------------------*/ typedef TImage Image8U; +typedef TImage Image16U; typedef TImage Image16F; typedef TImage Image32F; typedef TImage Image64F; diff --git a/libs/MVS/DepthMap.cpp b/libs/MVS/DepthMap.cpp index f4068a292..ad54a26a9 100644 --- a/libs/MVS/DepthMap.cpp +++ b/libs/MVS/DepthMap.cpp @@ -93,6 +93,7 @@ MDEFVAR_OPTDENSE_float(fOptimizerEps, "Optimizer Eps", "MRF optimizer stop epsil MDEFVAR_OPTDENSE_int32(nOptimizerMaxIters, "Optimizer Max Iters", "MRF optimizer max number of iterations", "80") MDEFVAR_OPTDENSE_uint32(nSpeckleSize, "Speckle Size", "maximal size of a speckle (small speckles get removed)", "100") MDEFVAR_OPTDENSE_uint32(nIpolGapSize, "Interpolate Gap Size", "interpolate small gaps (left<->right, top<->bottom)", "7") +MDEFVAR_OPTDENSE_int32(nIgnoreMaskLabel, "Ignore Mask Label", "label id used during ignore mask filter (<0 - disabled)", "-1") MDEFVAR_OPTDENSE_uint32(nOptimize, "Optimize", "should we filter the extracted depth-maps?", "7") // see DepthFlags MDEFVAR_OPTDENSE_uint32(nEstimateColors, "Estimate Colors", "should we estimate the colors for the dense point-cloud?", "2", "0", "1") MDEFVAR_OPTDENSE_uint32(nEstimateNormals, "Estimate Normals", "should we estimate the normals for the dense point-cloud?", "0", "1", "2") @@ -191,6 +192,26 @@ void DepthData::GetNormal(const Point2f& pt, Point3f& N, const TImage* /*----------------------------------------------------------------*/ +// apply mask to the depth map +void DepthData::ApplyIgnoreMask(const BitMatrix& mask) +{ + ASSERT(IsValid() && !IsEmpty() && mask.size() == depthMap.size()); + for (int r=0; r 4 5 6 diff --git a/libs/MVS/DepthMap.h b/libs/MVS/DepthMap.h index 6cdb72c30..21a5b5a7d 100644 --- a/libs/MVS/DepthMap.h +++ b/libs/MVS/DepthMap.h @@ -113,6 +113,7 @@ extern float fOptimizerEps; extern int nOptimizerMaxIters; extern unsigned nSpeckleSize; extern unsigned nIpolGapSize; +extern int nIgnoreMaskLabel; extern unsigned nOptimize; extern unsigned nEstimateColors; extern unsigned nEstimateNormals; @@ -202,6 +203,8 @@ struct MVS_API DepthData { void GetNormal(const ImageRef& ir, Point3f& N, const TImage* pPointMap=NULL) const; void GetNormal(const Point2f& x, Point3f& N, const TImage* pPointMap=NULL) const; + void ApplyIgnoreMask(const BitMatrix&); + bool Save(const String& fileName) const; bool Load(const String& fileName); @@ -423,6 +426,7 @@ struct MVS_API DepthEstimator { normal = RMatrixBaseF(normal.cross(viewDir), MINF((ACOS(cosAngLen/norm(viewDir))-FD2R(90.f))*1.01f, -0.001f)) * normal; } + static bool ImportIgnoreMask(const Image&, const Image8U::Size&, BitMatrix&, uint16_t nIgnoreMaskLabel); static void MapMatrix2ZigzagIdx(const Image8U::Size& size, DepthEstimator::MapRefArr& coords, const BitMatrix& mask, int rawStride=16); const float smoothBonusDepth, smoothBonusNormal; diff --git a/libs/MVS/Image.h b/libs/MVS/Image.h index 85a4f5eaf..ad31c8646 100644 --- a/libs/MVS/Image.h +++ b/libs/MVS/Image.h @@ -80,6 +80,7 @@ class MVS_API Image uint32_t poseID; // ID of the pose of the associated platform uint32_t ID; // global ID of the image String name; // image file name (relative path) + String maskName; // segmentation file name (optional) Camera camera; // view's pose uint32_t width, height; // image size Image8U3 image; // image color pixels @@ -137,6 +138,8 @@ class MVS_API Image ar & ID; const String relName(MAKE_PATH_REL(WORKING_FOLDER_FULL, name)); ar & relName; + const String relMaskName(MAKE_PATH_REL(WORKING_FOLDER_FULL, maskName)); + ar & relMaskName; ar & width & height; ar & neighbors; ar & avgDepth; @@ -149,6 +152,8 @@ class MVS_API Image ar & ID; ar & name; name = MAKE_PATH_FULL(WORKING_FOLDER_FULL, name); + ar & maskName; + maskName = MAKE_PATH_FULL(WORKING_FOLDER_FULL, maskName); ar & width & height; ar & neighbors; ar & avgDepth; diff --git a/libs/MVS/Scene.cpp b/libs/MVS/Scene.cpp index 4039377f2..559500619 100644 --- a/libs/MVS/Scene.cpp +++ b/libs/MVS/Scene.cpp @@ -115,6 +115,11 @@ bool Scene::LoadInterface(const String & fileName) imageData.name = image.name; Util::ensureUnifySlash(imageData.name); imageData.name = MAKE_PATH_FULL(WORKING_FOLDER_FULL, imageData.name); + if (!image.maskName.empty()) { + imageData.maskName = image.maskName; + Util::ensureUnifySlash(imageData.maskName); + imageData.maskName = MAKE_PATH_FULL(WORKING_FOLDER_FULL, imageData.maskName); + } imageData.poseID = image.poseID; if (imageData.poseID == NO_ID) { DEBUG_EXTRA("warning: uncalibrated image '%s'", image.name.c_str()); @@ -225,6 +230,8 @@ bool Scene::SaveInterface(const String & fileName, int version) const const Image& imageData = images[i]; MVS::Interface::Image& image = obj.images[i]; image.name = MAKE_PATH_REL(WORKING_FOLDER_FULL, imageData.name); + if (!imageData.maskName.empty()) + image.maskName = MAKE_PATH_REL(WORKING_FOLDER_FULL, imageData.maskName); image.poseID = imageData.poseID; image.platformID = imageData.platformID; image.cameraID = imageData.cameraID; diff --git a/libs/MVS/SceneDensify.cpp b/libs/MVS/SceneDensify.cpp index 1e97e8806..df013f260 100644 --- a/libs/MVS/SceneDensify.cpp +++ b/libs/MVS/SceneDensify.cpp @@ -545,9 +545,20 @@ bool DepthMapsData::EstimateDepthMap(IIndex idxImage) cv::integral(image.image, imageSum0, CV_64F); #endif if (prevDepthMapSize != size) { - prevDepthMapSize = size; BitMatrix mask; + if (OPTDENSE::nIgnoreMaskLabel >= 0 && DepthEstimator::ImportIgnoreMask(*depthData.GetView().pImageData, depthData.depthMap.size(), mask, (uint16_t)OPTDENSE::nIgnoreMaskLabel)) + depthData.ApplyIgnoreMask(mask); DepthEstimator::MapMatrix2ZigzagIdx(size, coords, mask, MAXF(64,(int)nMaxThreads*8)); + #if 0 + // show pixels to be processed + Image8U cmask(size); + cmask.memset(0); + for (const DepthEstimator::MapRef& x: coords) + cmask(x.y, x.x) = 255; + cmask.Show("cmask"); + #endif + if (mask.empty()) + prevDepthMapSize = size; } // init threads From 20995451d7882d6a03aee4b436b17bbd4589264c Mon Sep 17 00:00:00 2001 From: cDc Date: Tue, 7 Jul 2020 17:09:30 +0300 Subject: [PATCH 002/252] dense: fix bug in mask application --- libs/MVS/DepthMap.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libs/MVS/DepthMap.cpp b/libs/MVS/DepthMap.cpp index ad54a26a9..791cf2e06 100644 --- a/libs/MVS/DepthMap.cpp +++ b/libs/MVS/DepthMap.cpp @@ -198,7 +198,7 @@ void DepthData::ApplyIgnoreMask(const BitMatrix& mask) ASSERT(IsValid() && !IsEmpty() && mask.size() == depthMap.size()); for (int r=0; r Date: Wed, 8 Jul 2020 22:40:43 +0300 Subject: [PATCH 003/252] build: update find packages --- CMakeLists.txt | 30 +++++---- MvgMvsPipeline.py | 4 +- apps/Viewer/CMakeLists.txt | 27 +++------ build/Modules/FindCERES.cmake | 103 ------------------------------- build/Modules/FindCGAL.cmake | 89 --------------------------- build/Modules/FindEigen.cmake | 55 ----------------- build/Modules/FindEigen3.cmake | 107 +++++++++++++++++++++++++++++++++ build/Utils.cmake | 4 +- libs/MVS/CMakeLists.txt | 9 +-- 9 files changed, 140 insertions(+), 288 deletions(-) delete mode 100644 build/Modules/FindCERES.cmake delete mode 100644 build/Modules/FindCGAL.cmake delete mode 100644 build/Modules/FindEigen.cmake create mode 100644 build/Modules/FindEigen3.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index fff705bda..c2e497abf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,16 +45,17 @@ ConfigCompilerAndLinker() ConfigLibrary() # List configuration options +SET(OpenMVS_BUILD_TOOLS ON CACHE BOOL "Build example applications") SET(OpenMVS_USE_NONFREE ON CACHE BOOL "Build non-free (patented) functionality") +SET(OpenMVS_USE_OPENMP ON CACHE BOOL "Enable OpenMP library") +SET(OpenMVS_USE_OPENGL ON CACHE BOOL "Enable OpenGL library") +SET(OpenMVS_USE_BREAKPAD ON CACHE BOOL "Enable BreakPad library") SET(OpenMVS_USE_CERES OFF CACHE BOOL "Enable CERES optimization library") +SET(OpenMVS_USE_CUDA OFF CACHE BOOL "Enable CUDA library") SET(OpenMVS_USE_FAST_FLOAT2INT ON CACHE BOOL "Use an optimized code to convert real numbers to int") SET(OpenMVS_USE_FAST_INVSQRT OFF CACHE BOOL "Use an optimized code to compute the inverse square root (slower in fact on modern compilers)") SET(OpenMVS_USE_FAST_CBRT ON CACHE BOOL "Use an optimized code to compute the cubic root") SET(OpenMVS_USE_SSE ON CACHE BOOL "Enable SSE optimizations") -SET(OpenMVS_USE_OPENMP ON CACHE BOOL "Enable OpenMP library") -SET(OpenMVS_USE_OPENGL ON CACHE BOOL "Enable OpenGL library") -SET(OpenMVS_USE_CUDA ON CACHE BOOL "Enable CUDA library") -SET(OpenMVS_USE_BREAKPAD ON CACHE BOOL "Enable BreakPad library") SET(OpenMVS_CONFIG_INCLUDE_DIR "${CMAKE_BINARY_DIR}/" CACHE PATH "Where to create the build/platform specific header") INCLUDE_DIRECTORIES("${OpenMVS_SOURCE_DIR}") @@ -119,7 +120,7 @@ if(OpenMVS_USE_BREAKPAD) endif() endif() -FIND_PACKAGE(Boost ${SYSTEM_PACKAGE_REQUIRED} COMPONENTS iostreams program_options system serialization) +FIND_PACKAGE(Boost COMPONENTS iostreams program_options system serialization REQUIRED) if(Boost_FOUND) INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS}) ADD_DEFINITIONS(${Boost_DEFINITIONS} -D_USE_BOOST) @@ -127,14 +128,15 @@ if(Boost_FOUND) SET(_USE_BOOST TRUE) endif() -FIND_PACKAGE(Eigen ${SYSTEM_PACKAGE_REQUIRED}) -if(EIGEN_FOUND) - INCLUDE_DIRECTORIES(${EIGEN_INCLUDE_DIRS}) - ADD_DEFINITIONS(${EIGEN_DEFINITIONS} -D_USE_EIGEN) +FIND_PACKAGE(Eigen3 REQUIRED) +if(EIGEN3_FOUND) + INCLUDE_DIRECTORIES(${EIGEN3_INCLUDE_DIR}) + ADD_DEFINITIONS(${EIGEN3_DEFINITIONS} -D_USE_EIGEN) SET(_USE_EIGEN TRUE) + message(STATUS "Eigen ${EIGEN3_VERSION} found (include: ${EIGEN3_INCLUDE_DIR})") endif() -FIND_PACKAGE(OpenCV ${SYSTEM_PACKAGE_REQUIRED}) +FIND_PACKAGE(OpenCV REQUIRED) if(OpenCV_FOUND) INCLUDE_DIRECTORIES(${OpenCV_INCLUDE_DIRS}) ADD_DEFINITIONS(${OpenCV_DEFINITIONS}) @@ -170,7 +172,9 @@ ADD_DEFINITIONS(${OpenMVS_DEFINITIONS}) # Add modules ADD_SUBDIRECTORY(libs) -ADD_SUBDIRECTORY(apps) +if(OpenMVS_BUILD_TOOLS) + ADD_SUBDIRECTORY(apps) +endif() ADD_SUBDIRECTORY(docs) if(OpenMVS_USE_CERES) @@ -188,7 +192,7 @@ export(TARGETS Common IO Math MVS FILE "${PROJECT_BINARY_DIR}/OpenMVSTargets.cma # Export the package for use from the build-tree # (this registers the build-tree with a global CMake-registry) export(PACKAGE OpenMVS) - + # Create the OpenMVSConfig.cmake and OpenMVSConfigVersion files file(RELATIVE_PATH REL_INCLUDE_DIR "${INSTALL_CMAKE_DIR}" "${INSTALL_INCLUDE_DIR}") # ... for the build tree @@ -199,7 +203,7 @@ set(CONF_INCLUDE_DIRS "${INSTALL_CMAKE_DIR}/${REL_INCLUDE_DIR}") configure_file("${CMAKE_CURRENT_SOURCE_DIR}/build/OpenMVSConfig.cmake.in" "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/OpenMVSConfig.cmake" @ONLY) # ... for both configure_file("${CMAKE_CURRENT_SOURCE_DIR}/build/OpenMVSConfigVersion.cmake.in" "${PROJECT_BINARY_DIR}/OpenMVSConfigVersion.cmake" @ONLY) - + # Install the OpenMVSConfig.cmake and OpenMVSConfigVersion.cmake install(FILES "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/OpenMVSConfig.cmake" diff --git a/MvgMvsPipeline.py b/MvgMvsPipeline.py index 106857f7e..4f30795c0 100644 --- a/MvgMvsPipeline.py +++ b/MvgMvsPipeline.py @@ -183,10 +183,10 @@ def __init__(self): ["-i", "%input_dir%", "-o", "%matches_dir%", "-d", "%camera_file_params%"]], ["Compute features", # 1 os.path.join(OPENMVG_BIN, "openMVG_main_ComputeFeatures"), - ["-i", "%matches_dir%/sfm_data.json", "-o", "%matches_dir%", "-m", "SIFT", "-n", "4"]], + ["-i", "%matches_dir%/sfm_data.json", "-o", "%matches_dir%", "-m", "SIFT"]], ["Compute matches", # 2 os.path.join(OPENMVG_BIN, "openMVG_main_ComputeMatches"), - ["-i", "%matches_dir%/sfm_data.json", "-o", "%matches_dir%", "-n", "HNSWL2", "-r", ".8"]], + ["-i", "%matches_dir%/sfm_data.json", "-o", "%matches_dir%", "-n", "AUTO"]], ["Incremental reconstruction", # 3 os.path.join(OPENMVG_BIN, "openMVG_main_IncrementalSfM"), ["-i", "%matches_dir%/sfm_data.json", "-m", "%matches_dir%", "-o", "%reconstruction_dir%"]], diff --git a/apps/Viewer/CMakeLists.txt b/apps/Viewer/CMakeLists.txt index c519040d1..c90affca2 100644 --- a/apps/Viewer/CMakeLists.txt +++ b/apps/Viewer/CMakeLists.txt @@ -16,27 +16,14 @@ else() MESSAGE("-- Can't find GLEW. Continuing without it.") RETURN() endif() -if(CMAKE_COMPILER_IS_GNUCXX) - FIND_PACKAGE(PkgConfig QUIET) - pkg_search_module(GLFW QUIET glfw3) - if(GLFW_FOUND) - INCLUDE_DIRECTORIES(${GLFW_INCLUDE_DIRS}) - ADD_DEFINITIONS(${GLFW_DEFINITIONS}) - MESSAGE(STATUS "GLFW3 ${GLFW_VERSION} found (include: ${GLFW_INCLUDE_DIRS})") - else() - MESSAGE("-- Can't find GLFW3. Continuing without it.") - RETURN() - endif() +FIND_PACKAGE(glfw3 QUIET) +if(glfw3_FOUND) + INCLUDE_DIRECTORIES(${glfw3_INCLUDE_DIRS}) + ADD_DEFINITIONS(${glfw3_DEFINITIONS}) + MESSAGE(STATUS "GLFW3 ${glfw3_VERSION} found (include: ${glfw3_INCLUDE_DIRS})") else() - FIND_PACKAGE(glfw3 QUIET) - if(glfw3_FOUND) - INCLUDE_DIRECTORIES(${glfw3_INCLUDE_DIRS}) - ADD_DEFINITIONS(${glfw3_DEFINITIONS}) - MESSAGE(STATUS "GLFW3 ${glfw3_VERSION} found (include: ${glfw3_INCLUDE_DIRS})") - else() - MESSAGE("-- Can't find GLFW3. Continuing without it.") - RETURN() - endif() + MESSAGE("-- Can't find GLFW3. Continuing without it.") + RETURN() endif() # List sources files diff --git a/build/Modules/FindCERES.cmake b/build/Modules/FindCERES.cmake deleted file mode 100644 index 6905393be..000000000 --- a/build/Modules/FindCERES.cmake +++ /dev/null @@ -1,103 +0,0 @@ -# - try to find CERES headers -# -# Users may optionally supply: -# CERES_DIR - a prefix to start searching for the toon headers. -# -# Cache Variables: (probably not for direct use in your scripts) -# CERES_INCLUDE_DIR -# -# Non-cache variables you might use in your CMakeLists.txt: -# CERES_FOUND -# CERES_INCLUDE_DIRS -# CERES_LIBS -# CERES_DEFINITIONS -# -# Requires these CMake modules: -# FindPackageHandleStandardArgs (known included with CMake >=2.6.2) - -#try to use the Config script -if(NOT EXISTS "${CERES_DIR}") - find_path(CERES_DIR "CeresConfig.cmake" - HINTS "${CERES_ROOT}" "$ENV{CERES_ROOT}" "$ENV{CERES_DIR}" "$ENV{CERES_ROOT}/CMake" - PATHS "$ENV{PROGRAMFILES}" "$ENV{PROGRAMW6432}" "/usr" "/usr/local" "/usr/share" "/usr/local/share" "/usr/lib/cmake" "/usr/local/lib/cmake" "/usr/include" "/usr/lib/x86_64-linux-gnu/cmake" - PATH_SUFFIXES "Ceres" - DOC "Root directory of CERES") -endif() - -set(CERES_VERSION "") -if(EXISTS "${CERES_DIR}") - - ## Include the standard CMake script - include("${CERES_DIR}/CeresConfig.cmake") - set(CERES_LIBS ${CERES_LIBS} ${CERES_LIBRARIES}) - -else() - - # Find required packages - FIND_PACKAGE(Eigen ${SYSTEM_PACKAGE_QUIET}) - FIND_PACKAGE(SUITESPARSE ${SYSTEM_PACKAGE_QUIET}) - if(SUITESPARSE_FOUND) - set(CERES_LIBS ${CERES_LIBS} ${SUITESPARSE_LIBS}) - endif() - FIND_PACKAGE(GLOG ${SYSTEM_PACKAGE_QUIET}) - if(GLOG_FOUND) - set(CERES_INCLUDE_DIRS ${CERES_INCLUDE_DIRS} ${GLOG_INCLUDE_DIRS}) - set(CERES_LIBS ${CERES_LIBS} ${GLOG_LIBS}) - endif() - - if(NOT CERES_DIR OR "${CERES_DIR}" STREQUAL "") - set(CERES_DIR "$ENV{CERES_ROOT}") - endif() - set(CERES_DIR "${CERES_DIR}" CACHE PATH "Root directory of CERES library") - - #try to guess path - find_path(CERES_INCLUDE_DIR - NAMES "ceres/ceres.h" - HINTS "${CERES_DIR}" "$ENV{CERES_ROOT}" "/usr" "/usr/local" - PATH_SUFFIXES "include") - - set(CERES_FOUND FALSE) - if(EXISTS "${CERES_INCLUDE_DIR}" AND NOT "${CERES_INCLUDE_DIR}" STREQUAL "") - set(CERES_FOUND TRUE) - - find_library(CERES_LIBRARY_DEBUG "libceres" "ceres" "ceres_shared" PATHS "${CERES_DIR}/lib${PACKAGE_LIB_SUFFIX_DBG}" "$ENV{OpenCV_ROOT}/lib${PACKAGE_LIB_SUFFIX_DBG}" NO_DEFAULT_PATH) - find_library(CERES_LIBRARY_RELEASE "libceres" "ceres" "ceres_shared" PATHS "${CERES_DIR}/lib${PACKAGE_LIB_SUFFIX_REL}" "$ENV{OpenCV_ROOT}/lib${PACKAGE_LIB_SUFFIX_REL}" NO_DEFAULT_PATH) - find_library(CERES_LIBRARY_ALL NAMES "ceres" PATH_SUFFIXES "ceres") - - #Remove the cache value - set(CERES_LIBRARY "" CACHE STRING "" FORCE) - - #both debug/release - if(CERES_LIBRARY_DEBUG AND CERES_LIBRARY_RELEASE) - set(CERES_LIBRARY debug ${CERES_LIBRARY_DEBUG} optimized ${CERES_LIBRARY_RELEASE} CACHE STRING "" FORCE) - #only debug - elseif(CERES_LIBRARY_DEBUG) - set(CERES_LIBRARY ${CERES_LIBRARY_DEBUG} CACHE STRING "" FORCE) - #only release - elseif(CERES_LIBRARY_RELEASE) - set(CERES_LIBRARY ${CERES_LIBRARY_RELEASE} CACHE STRING "" FORCE) - #both debug/release - elseif(CERES_LIBRARY_ALL) - set(CERES_LIBRARY ${CERES_LIBRARY_ALL} CACHE STRING "" FORCE) - #no library found - else() - message("CERES library NOT found") - set(CERES_FOUND FALSE) - endif() - - #Add to the general list - if(CERES_LIBRARY) - set(CERES_LIBS ${CERES_LIBS} ${CERES_LIBRARY}) - endif() - endif() - -endif() - -if(CERES_FOUND) - set(CERES_INCLUDE_DIRS ${CERES_INCLUDE_DIRS} "${CERES_INCLUDE_DIR}") - set(CERES_DIR "${CERES_DIR}" CACHE PATH "" FORCE) - mark_as_advanced(CERES_DIR) - message(STATUS "CERES ${CERES_VERSION} found (include: ${CERES_INCLUDE_DIRS})") -else() - package_report_not_found(CERES "Please specify CERES directory using CERES_ROOT env. variable") -endif() diff --git a/build/Modules/FindCGAL.cmake b/build/Modules/FindCGAL.cmake deleted file mode 100644 index fcdc7b4fd..000000000 --- a/build/Modules/FindCGAL.cmake +++ /dev/null @@ -1,89 +0,0 @@ -########################################################### -# Find CGAL Library -#---------------------------------------------------------- -# CGAL_FOUND - True if headers and requested libraries were found -# CGAL_INCLUDE_DIRS - CGAL include directories -# CGAL_LIBRARY_DIRS - Link directories for CGAL libraries -# CGAL_LIBS - CGAL libraries -# CGAL_VERSION - MAJOR.MINOR -#---------------------------------------------------------- - -set(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS true) - -if(NOT CGAL_DIR) - # Get the system search path as a list. - if(UNIX) - string(REGEX MATCHALL "[^:]+" CGAL_DIR_SEARCH1 "$ENV{PATH}") - else() - string(REGEX REPLACE "\\\\" "/" CGAL_DIR_SEARCH1 "$ENV{PATH}") - endif() - string(REGEX REPLACE "/;" ";" CGAL_DIR_SEARCH2 "${CGAL_DIR_SEARCH1}") - # Construct a set of paths relative to the system search path. - set(CGAL_DIR_SEARCH "") - foreach(dir ${CGAL_DIR_SEARCH2}) - set(CGAL_DIR_SEARCH ${CGAL_DIR_SEARCH} ${dir}/../lib/CGAL) - endforeach() - set(CGAL_DIR_SEARCH ${CGAL_DIR_SEARCH} "lib" "lib64") - - # - # Look for an installation or build tree. - # - find_path(CGAL_DIR "CGALConfig.cmake" - # Look for an environment variable CGAL_DIR. - HINTS "${CGAL_ROOT}" "$ENV{CGAL_ROOT}" "$ENV{CGAL_DIR}" "$ENV{PROGRAMFILES}" "$ENV{PROGRAMW6432}" - - # Look in places relative to the system executable search path. - ${CGAL_DIR_SEARCH} - - # Look in standard UNIX install locations. - PATHS "/usr" "/usr/local" "/usr/share" "/usr/local/share" "/usr/lib/cmake" "/usr/local/lib/cmake" "/usr/include" "/usr/lib/x86_64-linux-gnu/cmake" - - # Read from the CMakeSetup registry entries. It is likely that - # CGAL will have been recently built. - [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild1] - [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild2] - [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild3] - [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild4] - [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild5] - [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild6] - [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild7] - [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild8] - [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild9] - [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild10] - - PATH_SUFFIXES "CGAL" "share" "share/cgal" "share/cmake" "share/cmake/cgal" - - DOC "Root directory of CGAL library" - ) -endif() - -##==================================================== -## Include CGAL library -##---------------------------------------------------- -set(CGAL_VERSION "") -if(EXISTS "${CGAL_DIR}" AND NOT "${CGAL_DIR}" STREQUAL "") - if(EXISTS "${CGAL_DIR}/CGALConfig.cmake") - include("${CGAL_DIR}/CGALConfig.cmake") - set(CGAL_TARGET_LIBS "") - if(TARGET CGAL::CGAL) - get_target_property(CGAL_TARGET_LIBS CGAL::CGAL INTERFACE_LINK_LIBRARIES) - elseif(TARGET CGAL) - get_target_property(CGAL_TARGET_LIBS CGAL INTERFACE_LINK_LIBRARIES) - endif() - if(NOT CGAL_TARGET_LIBS) - set(CGAL_TARGET_LIBS "") - endif() - set(CGAL_LIBS ${CGAL_LIBS} ${CGAL_LIBRARIES} ${CGAL_LIBRARY} ${CGAL_TARGET_LIBS} ${CGAL_Core_LIBRARY} ${CGAL_ImageIO_LIBRARY} ${CGAL_3RD_PARTY_LIBRARIES} ${CGAL_Core_3RD_PARTY_LIBRARIES} ${CGAL_ImageIO_3RD_PARTY_LIBRARIES} ${MPFR_LIBRARIES} ${GMP_LIBRARIES} ${ZLIB_LIBRARIES}) - set(CGAL_VERSION "${CGAL_MAJOR_VERSION}.${CGAL_MINOR_VERSION}") - else() - set(CGAL_INCLUDE_DIRS "${CGAL_DIR}/include" "${CGAL_DIR}/auxiliary/gmp/include") - set(CGAL_LIBRARY_DIRS "${CGAL_DIR}/lib${PACKAGE_LIB_SUFFIX}") - endif() - set(CGAL_FOUND TRUE) - set(CGAL_DIR "${CGAL_DIR}" CACHE PATH "" FORCE) - mark_as_advanced(CGAL_DIR) - message(STATUS "CGAL ${CGAL_VERSION} found (include: ${CGAL_INCLUDE_DIRS})") -else() - package_report_not_found(CGAL "Please specify CGAL directory using CGAL_ROOT env. variable") -endif() -##==================================================== diff --git a/build/Modules/FindEigen.cmake b/build/Modules/FindEigen.cmake deleted file mode 100644 index 0f1d74c02..000000000 --- a/build/Modules/FindEigen.cmake +++ /dev/null @@ -1,55 +0,0 @@ -########################################################### -# Find EIGEN Library -#---------------------------------------------------------- - -find_path(EIGEN_DIR "Eigen/Core" - HINTS "${EIGEN_ROOT}" "$ENV{EIGEN_ROOT}" - PATHS "$ENV{PROGRAMFILES}" "$ENV{PROGRAMW6432}" "/usr" "/usr/local" "/usr/share" "/usr/local/share" "/usr/lib/x86_64-linux-gnu/cmake" - PATH_SUFFIXES "eigen" "eigen3" "include" - DOC "Root directory of EIGEN library") - -##==================================================== -## Include EIGEN library -##---------------------------------------------------- -if(EXISTS "${EIGEN_DIR}" AND NOT "${EIGEN_DIR}" STREQUAL "") - set(EIGEN_FOUND TRUE) - set(EIGEN_INCLUDE_DIRS ${EIGEN_DIR}) - set(EIGEN_DIR "${EIGEN_DIR}" CACHE PATH "" FORCE) - mark_as_advanced(EIGEN_DIR) - - # Extract Eigen version from Eigen/src/Core/util/Macros.h - SET(EIGEN_VERSION_FILE ${EIGEN_INCLUDE_DIRS}/Eigen/src/Core/util/Macros.h) - IF (NOT EXISTS ${EIGEN_VERSION_FILE}) - EIGEN_REPORT_NOT_FOUND( - "Could not find file: ${EIGEN_VERSION_FILE} " - "containing version information in Eigen install located at: " - "${EIGEN_INCLUDE_DIRS}.") - ELSE (NOT EXISTS ${EIGEN_VERSION_FILE}) - FILE(READ ${EIGEN_VERSION_FILE} EIGEN_VERSION_FILE_CONTENTS) - - STRING(REGEX MATCH "#define EIGEN_WORLD_VERSION [0-9]+" - EIGEN_WORLD_VERSION "${EIGEN_VERSION_FILE_CONTENTS}") - STRING(REGEX REPLACE "#define EIGEN_WORLD_VERSION ([0-9]+)" "\\1" - EIGEN_WORLD_VERSION "${EIGEN_WORLD_VERSION}") - - STRING(REGEX MATCH "#define EIGEN_MAJOR_VERSION [0-9]+" - EIGEN_MAJOR_VERSION "${EIGEN_VERSION_FILE_CONTENTS}") - STRING(REGEX REPLACE "#define EIGEN_MAJOR_VERSION ([0-9]+)" "\\1" - EIGEN_MAJOR_VERSION "${EIGEN_MAJOR_VERSION}") - - STRING(REGEX MATCH "#define EIGEN_MINOR_VERSION [0-9]+" - EIGEN_MINOR_VERSION "${EIGEN_VERSION_FILE_CONTENTS}") - STRING(REGEX REPLACE "#define EIGEN_MINOR_VERSION ([0-9]+)" "\\1" - EIGEN_MINOR_VERSION "${EIGEN_MINOR_VERSION}") - - # This is on a single line s/t CMake does not interpret it as a list of - # elements and insert ';' separators which would result in 3.;2.;0 nonsense. - SET(EIGEN_VERSION "${EIGEN_WORLD_VERSION}.${EIGEN_MAJOR_VERSION}.${EIGEN_MINOR_VERSION}") - ENDIF (NOT EXISTS ${EIGEN_VERSION_FILE}) - set(EIGEN_INCLUDE_DIR ${EIGEN_DIR}) - - message(STATUS "Eigen ${EIGEN_VERSION} found (include: ${EIGEN_INCLUDE_DIRS})") -else() - package_report_not_found(EIGEN "Please specify EIGEN directory using EIGEN_ROOT env. variable") -endif() -##==================================================== diff --git a/build/Modules/FindEigen3.cmake b/build/Modules/FindEigen3.cmake new file mode 100644 index 000000000..0b36805e7 --- /dev/null +++ b/build/Modules/FindEigen3.cmake @@ -0,0 +1,107 @@ +# - Try to find Eigen3 lib +# +# This module supports requiring a minimum version, e.g. you can do +# find_package(Eigen3 3.1.2) +# to require version 3.1.2 or newer of Eigen3. +# +# Once done this will define +# +# EIGEN3_FOUND - system has eigen lib with correct version +# EIGEN3_INCLUDE_DIR - the eigen include directory +# EIGEN3_VERSION - eigen version +# +# and the following imported target: +# +# Eigen3::Eigen - The header-only Eigen library +# +# This module reads hints about search locations from +# the following environment variables: +# +# EIGEN3_ROOT +# EIGEN3_ROOT_DIR + +# Copyright (c) 2006, 2007 Montel Laurent, +# Copyright (c) 2008, 2009 Gael Guennebaud, +# Copyright (c) 2009 Benoit Jacob +# Redistribution and use is allowed according to the terms of the 2-clause BSD license. + +if(NOT Eigen3_FIND_VERSION) + if(NOT Eigen3_FIND_VERSION_MAJOR) + set(Eigen3_FIND_VERSION_MAJOR 2) + endif() + if(NOT Eigen3_FIND_VERSION_MINOR) + set(Eigen3_FIND_VERSION_MINOR 91) + endif() + if(NOT Eigen3_FIND_VERSION_PATCH) + set(Eigen3_FIND_VERSION_PATCH 0) + endif() + + set(Eigen3_FIND_VERSION "${Eigen3_FIND_VERSION_MAJOR}.${Eigen3_FIND_VERSION_MINOR}.${Eigen3_FIND_VERSION_PATCH}") +endif() + +macro(_eigen3_check_version) + file(READ "${EIGEN3_INCLUDE_DIR}/Eigen/src/Core/util/Macros.h" _eigen3_version_header) + + string(REGEX MATCH "define[ \t]+EIGEN_WORLD_VERSION[ \t]+([0-9]+)" _eigen3_world_version_match "${_eigen3_version_header}") + set(EIGEN3_WORLD_VERSION "${CMAKE_MATCH_1}") + string(REGEX MATCH "define[ \t]+EIGEN_MAJOR_VERSION[ \t]+([0-9]+)" _eigen3_major_version_match "${_eigen3_version_header}") + set(EIGEN3_MAJOR_VERSION "${CMAKE_MATCH_1}") + string(REGEX MATCH "define[ \t]+EIGEN_MINOR_VERSION[ \t]+([0-9]+)" _eigen3_minor_version_match "${_eigen3_version_header}") + set(EIGEN3_MINOR_VERSION "${CMAKE_MATCH_1}") + + set(EIGEN3_VERSION ${EIGEN3_WORLD_VERSION}.${EIGEN3_MAJOR_VERSION}.${EIGEN3_MINOR_VERSION}) + if(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION}) + set(EIGEN3_VERSION_OK FALSE) + else() + set(EIGEN3_VERSION_OK TRUE) + endif() + + if(NOT EIGEN3_VERSION_OK) + + message(STATUS "Eigen3 version ${EIGEN3_VERSION} found in ${EIGEN3_INCLUDE_DIR}, " + "but at least version ${Eigen3_FIND_VERSION} is required") + endif() +endmacro() + +if (EIGEN3_INCLUDE_DIR) + + # in cache already + _eigen3_check_version() + set(EIGEN3_FOUND ${EIGEN3_VERSION_OK}) + set(Eigen3_FOUND ${EIGEN3_VERSION_OK}) + +else () + + # search first if an Eigen3Config.cmake is available in the system, + # if successful this would set EIGEN3_INCLUDE_DIR and the rest of + # the script will work as usual + find_package(Eigen3 ${Eigen3_FIND_VERSION} NO_MODULE QUIET) + + if(NOT EIGEN3_INCLUDE_DIR) + find_path(EIGEN3_INCLUDE_DIR NAMES signature_of_eigen3_matrix_library + HINTS + ENV EIGEN3_ROOT + ENV EIGEN3_ROOT_DIR + PATHS + ${CMAKE_INSTALL_PREFIX}/include + ${KDE4_INCLUDE_DIR} + PATH_SUFFIXES eigen3 eigen + ) + endif() + + if(EIGEN3_INCLUDE_DIR) + _eigen3_check_version() + endif() + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Eigen3 DEFAULT_MSG EIGEN3_INCLUDE_DIR EIGEN3_VERSION_OK) + + mark_as_advanced(EIGEN3_INCLUDE_DIR) + +endif() + +if(EIGEN3_FOUND AND NOT TARGET Eigen3::Eigen) + add_library(Eigen3::Eigen INTERFACE IMPORTED) + set_target_properties(Eigen3::Eigen PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${EIGEN3_INCLUDE_DIR}") +endif() diff --git a/build/Utils.cmake b/build/Utils.cmake index f41c9d89f..671672889 100644 --- a/build/Utils.cmake +++ b/build/Utils.cmake @@ -174,9 +174,9 @@ macro(ComposePackageLibSuffix) set(PACKAGE_LIB_SUFFIX_DBG "") set(PACKAGE_LIB_SUFFIX_REL "") if(MSVC) - if("${MSVC_VERSION}" STREQUAL "1921") + if("${MSVC_VERSION}" STRGREATER "1916") set(PACKAGE_LIB_SUFFIX "/vc16") - elseif("${MSVC_VERSION}" STREQUAL "1916") + elseif("${MSVC_VERSION}" STRGREATER "1900") set(PACKAGE_LIB_SUFFIX "/vc15") elseif("${MSVC_VERSION}" STREQUAL "1900") set(PACKAGE_LIB_SUFFIX "/vc14") diff --git a/libs/MVS/CMakeLists.txt b/libs/MVS/CMakeLists.txt index 14be62072..9d9a687bc 100644 --- a/libs/MVS/CMakeLists.txt +++ b/libs/MVS/CMakeLists.txt @@ -1,12 +1,12 @@ # Find required packages -FIND_PACKAGE(CGAL ${SYSTEM_PACKAGE_REQUIRED}) +FIND_PACKAGE(CGAL REQUIRED) if(CGAL_FOUND) include_directories(${CGAL_INCLUDE_DIRS}) add_definitions(${CGAL_DEFINITIONS}) link_directories(${CGAL_LIBRARY_DIRS}) endif() -FIND_PACKAGE(VCG ${SYSTEM_PACKAGE_REQUIRED}) +FIND_PACKAGE(VCG REQUIRED) if(VCG_FOUND) include_directories(${VCG_INCLUDE_DIRS}) add_definitions(${VCG_DEFINITIONS}) @@ -14,12 +14,13 @@ endif() set(CERES_LIBS "") if(OpenMVS_USE_CERES) - FIND_PACKAGE(CERES) + FIND_PACKAGE(Ceres) if(CERES_FOUND) include_directories(${CERES_INCLUDE_DIRS}) add_definitions(${CERES_DEFINITIONS}) else() set(OpenMVS_USE_CERES OFF) + message("-- Can't find CERES. Continuing without it.") endif() endif() @@ -42,7 +43,7 @@ cxx_library_with_type_no_pch(MVS "Libs" "" "${cxx_default}" set_target_pch(MVS Common.h) # Link its dependencies -TARGET_LINK_LIBRARIES(MVS PRIVATE Common Math IO ${CERES_LIBS} ${CGAL_LIBS} ${CUDA_CUDA_LIBRARY}) +TARGET_LINK_LIBRARIES(MVS PRIVATE Common Math IO CGAL::CGAL ${CERES_LIBRARIES} ${CUDA_CUDA_LIBRARY}) # Install SET_TARGET_PROPERTIES(MVS PROPERTIES From 8d1b2f463d0bfd06f7fc7021a4b6981df3abbfc2 Mon Sep 17 00:00:00 2001 From: cDc Date: Thu, 9 Jul 2020 20:44:20 +0300 Subject: [PATCH 004/252] build: fix usage as third-party library --- BUILD.md | 29 +++- CMakeLists.txt | 130 +++++++++--------- build/OpenMVSConfig.cmake.in | 18 --- build/OpenMVSConfigVersion.cmake.in | 11 -- build/Templates/OpenMVSConfig.cmake.in | 23 ++++ .../{ => Templates}/cmake_uninstall.cmake.in | 0 build/Utils.cmake | 17 ++- libs/Common/CMakeLists.txt | 8 +- libs/IO/CMakeLists.txt | 8 +- libs/MVS/CMakeLists.txt | 8 +- libs/Math/CMakeLists.txt | 15 +- 11 files changed, 147 insertions(+), 120 deletions(-) delete mode 100644 build/OpenMVSConfig.cmake.in delete mode 100644 build/OpenMVSConfigVersion.cmake.in create mode 100644 build/Templates/OpenMVSConfig.cmake.in rename build/{ => Templates}/cmake_uninstall.cmake.in (100%) diff --git a/BUILD.md b/BUILD.md index 52e4dc0b8..f6295c723 100644 --- a/BUILD.md +++ b/BUILD.md @@ -82,7 +82,7 @@ sudo apt-get -y install libcgal-dev libcgal-qt5-dev #VCGLib (Required) git clone https://github.com/cdcseacave/VCG.git vcglib -#Ceres (optional) +#Ceres (Optional) sudo apt-get -y install libatlas-base-dev libsuitesparse-dev git clone https://ceres-solver.googlesource.com/ceres-solver ceres-solver mkdir ceres_build && cd ceres_build @@ -128,7 +128,11 @@ git clone https://github.com/cdcseacave/openMVS.git #Build OpenMVS mkdir openMVS_build && cd openMVS_build -cmake . ../openMVS -DCMAKE_BUILD_TYPE=Release -DVCG_DIR="$main_path/vcglib" +cmake . ../openMVS -DCMAKE_BUILD_TYPE=Release -DVCG_ROOT="$main_path/vcglib" + +#Alternatively, build using XCode +cmake . ../openMVS -G "Xcode" -DCMAKE_BUILD_TYPE=Release -DVCG_ROOT="$main_path/vcglib" +xcodebuild -configuration Release #If you want to use OpenMVS as shared library, add to the CMake command: -DBUILD_SHARED_LIBS=ON @@ -136,3 +140,24 @@ cmake . ../openMVS -DCMAKE_BUILD_TYPE=Release -DVCG_DIR="$main_path/vcglib" #Install OpenMVS library (optional): make && sudo make install ``` + +------------------- +Library usage +------------------- + +In order to use *OpenMVS* as a third-party libray in your project, first compile it as described above or simply use `vcpgk`: +``` +vcpkg install openmvs +``` + +And inside your project CMake script, use: +``` +find_package(OpenMVS) +if(OpenMVS_FOUND) + include_directories(${OpenMVS_INCLUDE_DIRS}) + add_definitions(${OpenMVS_DEFINITIONS}) +endif() + +add_executable(your_project source_code.cpp) +target_link_libraries(your_project PRIVATE OpenMVS::MVS) +``` diff --git a/CMakeLists.txt b/CMakeLists.txt index c2e497abf..febcd8de2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,31 +18,10 @@ endif() # ${OpenMVS_BINARY_DIR}. PROJECT(OpenMVS) -set(OpenMVS_MAJOR_VERSION 1) -set(OpenMVS_MINOR_VERSION 1) -set(OpenMVS_PATCH_VERSION 1) -set(OpenMVS_VERSION ${OpenMVS_MAJOR_VERSION}.${OpenMVS_MINOR_VERSION}.${OpenMVS_PATCH_VERSION}) - -# Find dependencies: -SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/build/Modules) - -# fix CMake IntDir variable -if(MSVC AND "${MSVC_VERSION}" STRGREATER "1500") - SET(CMAKE_CFG_INTDIR "$(Platform)/$(Configuration)") -endif() -SET(COTIRE_INTDIR "cotire") - -# Define helper functions and macros. -INCLUDE(build/Utils.cmake) -if(ENABLE_PRECOMPILED_HEADERS) - INCLUDE(build/Cotire.cmake) -endif() - -# Init session with macros defined in Utils.cmake -GetOperatingSystemArchitectureBitness(SYSTEM) -ComposePackageLibSuffix() -ConfigCompilerAndLinker() -ConfigLibrary() +SET(OpenMVS_MAJOR_VERSION 1) +SET(OpenMVS_MINOR_VERSION 1) +SET(OpenMVS_PATCH_VERSION 1) +SET(OpenMVS_VERSION ${OpenMVS_MAJOR_VERSION}.${OpenMVS_MINOR_VERSION}.${OpenMVS_PATCH_VERSION}) # List configuration options SET(OpenMVS_BUILD_TOOLS ON CACHE BOOL "Build example applications") @@ -56,18 +35,32 @@ SET(OpenMVS_USE_FAST_FLOAT2INT ON CACHE BOOL "Use an optimized code to convert r SET(OpenMVS_USE_FAST_INVSQRT OFF CACHE BOOL "Use an optimized code to compute the inverse square root (slower in fact on modern compilers)") SET(OpenMVS_USE_FAST_CBRT ON CACHE BOOL "Use an optimized code to compute the cubic root") SET(OpenMVS_USE_SSE ON CACHE BOOL "Enable SSE optimizations") -SET(OpenMVS_CONFIG_INCLUDE_DIR "${CMAKE_BINARY_DIR}/" CACHE PATH "Where to create the build/platform specific header") -INCLUDE_DIRECTORIES("${OpenMVS_SOURCE_DIR}") +# Define helper functions and macros. +SET(COTIRE_INTDIR "cotire") +INCLUDE(build/Cotire.cmake) +INCLUDE(build/Utils.cmake) + +# Init session with macros defined in Utils.cmake +GetOperatingSystemArchitectureBitness(SYSTEM) +ComposePackageLibSuffix() +ConfigCompilerAndLinker() +ConfigLibrary() + +# Find dependencies: +SET(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/build/Modules) # Find required packages +SET(OpenMVS_EXTRA_INCLUDES "") +SET(OpenMVS_DEFINITIONS "") SET(OpenMVS_EXTRA_LIBS "") + if(OpenMVS_USE_OPENMP) SET(OpenMP_LIBS "") FIND_PACKAGE(OpenMP) if(OPENMP_FOUND) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") - ADD_DEFINITIONS(-D_USE_OPENMP) + LIST(APPEND OpenMVS_DEFINITIONS -D_USE_OPENMP) SET(_USE_OPENMP TRUE) #cmake only check for separate OpenMP library on AppleClang 7+ #https://github.com/Kitware/CMake/blob/42212f7539040139ecec092547b7d58ef12a4d72/Modules/FindOpenMP.cmake#L252 @@ -76,7 +69,7 @@ if(OpenMVS_USE_OPENMP) LIST(APPEND OpenMVS_EXTRA_LIBS ${OpenMP_LIBS}) endif() else() - message("-- Can't find OpenMP. Continuing without it.") + MESSAGE("-- Can't find OpenMP. Continuing without it.") endif() endif() @@ -87,7 +80,8 @@ if(OpenMVS_USE_OPENGL) FIND_PACKAGE(OpenGL) if(OPENGL_FOUND) INCLUDE_DIRECTORIES(${OPENGL_INCLUDE_DIR}) - ADD_DEFINITIONS(${OpenGL_DEFINITIONS} -D_USE_OPENGL) + LIST(APPEND OpenMVS_DEFINITIONS -D_USE_OPENGL) + ADD_DEFINITIONS(${OpenGL_DEFINITIONS}) SET(_USE_OPENGL TRUE) else() MESSAGE("-- Can't find OpenGL. Continuing without it.") @@ -98,7 +92,7 @@ if(OpenMVS_USE_CUDA) FIND_PACKAGE(CUDA) if(CUDA_FOUND) INCLUDE_DIRECTORIES(${CUDA_INCLUDE_DIRS}) - ADD_DEFINITIONS(-D_USE_CUDA) + LIST(APPEND OpenMVS_DEFINITIONS -D_USE_CUDA) SET(_USE_CUDA TRUE) else() SET(CUDA_CUDA_LIBRARY "") @@ -112,7 +106,8 @@ if(OpenMVS_USE_BREAKPAD) FIND_PACKAGE(BREAKPAD) if(BREAKPAD_FOUND) INCLUDE_DIRECTORIES(${BREAKPAD_INCLUDE_DIRS}) - ADD_DEFINITIONS(${BREAKPAD_DEFINITIONS} -D_USE_BREAKPAD) + LIST(APPEND OpenMVS_DEFINITIONS -D_USE_BREAKPAD) + ADD_DEFINITIONS(${BREAKPAD_DEFINITIONS}) SET(_USE_BREAKPAD TRUE) LIST(APPEND OpenMVS_EXTRA_LIBS ${BREAKPAD_LIBS}) else() @@ -122,22 +117,27 @@ endif() FIND_PACKAGE(Boost COMPONENTS iostreams program_options system serialization REQUIRED) if(Boost_FOUND) + LIST(APPEND OpenMVS_EXTRA_INCLUDES ${Boost_INCLUDE_DIRS}) INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS}) - ADD_DEFINITIONS(${Boost_DEFINITIONS} -D_USE_BOOST) + LIST(APPEND OpenMVS_DEFINITIONS -D_USE_BOOST) + ADD_DEFINITIONS(${Boost_DEFINITIONS}) LINK_DIRECTORIES(${Boost_LIBRARY_DIRS}) SET(_USE_BOOST TRUE) endif() FIND_PACKAGE(Eigen3 REQUIRED) if(EIGEN3_FOUND) + LIST(APPEND OpenMVS_EXTRA_INCLUDES ${EIGEN3_INCLUDE_DIR}) INCLUDE_DIRECTORIES(${EIGEN3_INCLUDE_DIR}) - ADD_DEFINITIONS(${EIGEN3_DEFINITIONS} -D_USE_EIGEN) + LIST(APPEND OpenMVS_DEFINITIONS -D_USE_EIGEN) + ADD_DEFINITIONS(${EIGEN3_DEFINITIONS}) SET(_USE_EIGEN TRUE) - message(STATUS "Eigen ${EIGEN3_VERSION} found (include: ${EIGEN3_INCLUDE_DIR})") + MESSAGE(STATUS "Eigen ${EIGEN3_VERSION} found (include: ${EIGEN3_INCLUDE_DIR})") endif() FIND_PACKAGE(OpenCV REQUIRED) if(OpenCV_FOUND) + LIST(APPEND OpenMVS_EXTRA_INCLUDES ${OpenCV_INCLUDE_DIRS}) INCLUDE_DIRECTORIES(${OpenCV_INCLUDE_DIRS}) ADD_DEFINITIONS(${OpenCV_DEFINITIONS}) SET(_USE_OPENCV TRUE) @@ -146,8 +146,10 @@ else() MESSAGE("-- Can't find OpenCV. Please specify OpenCV directory using OpenCV_DIR variable") endif() +LIST(REMOVE_DUPLICATES OpenMVS_EXTRA_INCLUDES) +LIST(REMOVE_DUPLICATES OpenMVS_EXTRA_LIBS) + # Set defines -SET(OpenMVS_DEFINITIONS "") if(OpenMVS_USE_NONFREE) LIST(APPEND OpenMVS_DEFINITIONS -D_USE_NONFREE) SET(_USE_NONFREE TRUE) @@ -168,8 +170,12 @@ if(OpenMVS_USE_SSE) LIST(APPEND OpenMVS_DEFINITIONS -D_USE_SSE) SET(_USE_SSE TRUE) endif() + ADD_DEFINITIONS(${OpenMVS_DEFINITIONS}) +INCLUDE_DIRECTORIES("${OpenMVS_SOURCE_DIR}") +INCLUDE_DIRECTORIES("${CMAKE_BINARY_DIR}") + # Add modules ADD_SUBDIRECTORY(libs) if(OpenMVS_BUILD_TOOLS) @@ -181,43 +187,41 @@ if(OpenMVS_USE_CERES) SET(_USE_CERES TRUE) endif() -# Set configuration file -CONFIGURE_FILE("${OpenMVS_SOURCE_DIR}/build/Templates/ConfigLocal.h.in" "${OpenMVS_CONFIG_INCLUDE_DIR}/ConfigLocal.h") -INSTALL(FILES "${OpenMVS_CONFIG_INCLUDE_DIR}/ConfigLocal.h" DESTINATION "${INSTALL_INCLUDE_DIR}") -INCLUDE_DIRECTORIES(${OpenMVS_CONFIG_INCLUDE_DIR}) - -# Add all targets to the build-tree export set -export(TARGETS Common IO Math MVS FILE "${PROJECT_BINARY_DIR}/OpenMVSTargets.cmake") - # Export the package for use from the build-tree # (this registers the build-tree with a global CMake-registry) export(PACKAGE OpenMVS) -# Create the OpenMVSConfig.cmake and OpenMVSConfigVersion files -file(RELATIVE_PATH REL_INCLUDE_DIR "${INSTALL_CMAKE_DIR}" "${INSTALL_INCLUDE_DIR}") -# ... for the build tree -set(CONF_INCLUDE_DIRS "${PROJECT_SOURCE_DIR}" "${PROJECT_BINARY_DIR}") -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/build/OpenMVSConfig.cmake.in" "${PROJECT_BINARY_DIR}/OpenMVSConfig.cmake" @ONLY) -# ... for the install tree -set(CONF_INCLUDE_DIRS "${INSTALL_CMAKE_DIR}/${REL_INCLUDE_DIR}") -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/build/OpenMVSConfig.cmake.in" "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/OpenMVSConfig.cmake" @ONLY) -# ... for both -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/build/OpenMVSConfigVersion.cmake.in" "${PROJECT_BINARY_DIR}/OpenMVSConfigVersion.cmake" @ONLY) +# Install the export set for use with the install-tree +INSTALL(EXPORT OpenMVSTargets + NAMESPACE OpenMVS:: + DESTINATION "${INSTALL_CMAKE_DIR}") + +# Install configuration file +CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/build/Templates/ConfigLocal.h.in" "${CMAKE_BINARY_DIR}/ConfigLocal.h") +INSTALL(FILES "${CMAKE_BINARY_DIR}/ConfigLocal.h" DESTINATION "${INSTALL_INCLUDE_DIR}") +# Create the OpenMVSConfig.cmake and OpenMVSConfigVersion files +INCLUDE(CMakePackageConfigHelpers) +write_basic_package_version_file("${PROJECT_BINARY_DIR}/OpenMVSConfigVersion.cmake" + VERSION ${OpenMVS_VERSION} + COMPATIBILITY AnyNewerVersion) +SET(INSTALL_INCLUDE_DIR_IN ${INSTALL_INCLUDE_DIR_PREFIX} ${OpenMVS_EXTRA_INCLUDES}) +SET(INSTALL_CMAKE_DIR_IN ${INSTALL_CMAKE_DIR_PREFIX}) +configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/build/Templates/OpenMVSConfig.cmake.in" + "${PROJECT_BINARY_DIR}/OpenMVSConfig.cmake" + INSTALL_DESTINATION ${PROJECT_BINARY_DIR} + NO_SET_AND_CHECK_MACRO) # Install the OpenMVSConfig.cmake and OpenMVSConfigVersion.cmake -install(FILES - "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/OpenMVSConfig.cmake" +INSTALL(FILES + "${PROJECT_BINARY_DIR}/OpenMVSConfig.cmake" "${PROJECT_BINARY_DIR}/OpenMVSConfigVersion.cmake" - DESTINATION "${INSTALL_CMAKE_DIR}" COMPONENT dev) - -# Install the export set for use with the install-tree -install(EXPORT OpenMVSTargets DESTINATION "${INSTALL_CMAKE_DIR}" COMPONENT dev) + DESTINATION "${INSTALL_CMAKE_DIR}") # uninstall target -configure_file( - "${CMAKE_CURRENT_SOURCE_DIR}/build/cmake_uninstall.cmake.in" +CONFIGURE_FILE( + "${CMAKE_CURRENT_SOURCE_DIR}/build/Templates/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) -add_custom_target(uninstall +ADD_CUSTOM_TARGET(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) diff --git a/build/OpenMVSConfig.cmake.in b/build/OpenMVSConfig.cmake.in deleted file mode 100644 index 96b8fe2ce..000000000 --- a/build/OpenMVSConfig.cmake.in +++ /dev/null @@ -1,18 +0,0 @@ -# - Configure file for the OpenMVS package -# It defines the following variables -# OpenMVS_INCLUDE_DIRS - include directories for OpenMVS -# OpenMVS_LIBRARIES - libraries to link against -# OpenMVS_BINARIES - the binaries - -# Compute paths -get_filename_component(OpenMVS_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) -set(OpenMVS_INCLUDE_DIRS "@CONF_INCLUDE_DIRS@") - -# Our library dependencies (contains definitions for IMPORTED targets) -if(NOT TARGET MVS AND NOT OpenMVS_BINARY_DIR) - include("${OpenMVS_CMAKE_DIR}/OpenMVSTargets.cmake") -endif() - -# These are IMPORTED targets created by OpenMVSTargets.cmake -set(OpenMVS_LIBRARIES MVS) -set(OpenMVS_BINARIES InterfaceVisualSFM DensifyPointCloud ReconstructMesh RefineMesh TextureMesh) diff --git a/build/OpenMVSConfigVersion.cmake.in b/build/OpenMVSConfigVersion.cmake.in deleted file mode 100644 index 3c9e7eb61..000000000 --- a/build/OpenMVSConfigVersion.cmake.in +++ /dev/null @@ -1,11 +0,0 @@ -set(PACKAGE_VERSION "@OpenMVS_VERSION@") - -# Check whether the requested PACKAGE_FIND_VERSION is compatible -if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}") - set(PACKAGE_VERSION_COMPATIBLE FALSE) -else() - set(PACKAGE_VERSION_COMPATIBLE TRUE) - if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}") - set(PACKAGE_VERSION_EXACT TRUE) - endif() -endif() diff --git a/build/Templates/OpenMVSConfig.cmake.in b/build/Templates/OpenMVSConfig.cmake.in new file mode 100644 index 000000000..9747b3e16 --- /dev/null +++ b/build/Templates/OpenMVSConfig.cmake.in @@ -0,0 +1,23 @@ +# Configure file for the OpenMVS package, defining the following variables: +# OpenMVS_INCLUDE_DIRS - include directories +# OpenMVS_DEFINITIONS - definitions to be used +# OpenMVS_LIBRARIES - libraries to link against +# OpenMVS_BINARIES - binaries + +@PACKAGE_INIT@ + +set(OpenMVS_VERSION "@OpenMVS_VERSION@") + +# Compute paths +set(OpenMVS_PREFIX "@CMAKE_INSTALL_PREFIX@") +set(OpenMVS_CMAKE_DIR "@INSTALL_CMAKE_DIR_IN@") +set(OpenMVS_INCLUDE_DIRS "@INSTALL_INCLUDE_DIR_IN@") + +set(OpenMVS_DEFINITIONS "@OpenMVS_DEFINITIONS@") + +# These are IMPORTED targets created by OpenMVSTargets.cmake +set(OpenMVS_LIBRARIES MVS) +set(OpenMVS_BINARIES InterfaceCOLMAP DensifyPointCloud ReconstructMesh RefineMesh TextureMesh) + +include("${CMAKE_CURRENT_LIST_DIR}/OpenMVSTargets.cmake") +check_required_components("OpenMVS") diff --git a/build/cmake_uninstall.cmake.in b/build/Templates/cmake_uninstall.cmake.in similarity index 100% rename from build/cmake_uninstall.cmake.in rename to build/Templates/cmake_uninstall.cmake.in diff --git a/build/Utils.cmake b/build/Utils.cmake index 671672889..bbbc85662 100644 --- a/build/Utils.cmake +++ b/build/Utils.cmake @@ -825,22 +825,25 @@ endmacro() # Initialize variables needed for a library type project. macro(ConfigLibrary) # Offer the user the choice of overriding the installation directories - set(INSTALL_LIB_DIR "lib/${PROJECT_NAME}" CACHE PATH "Installation directory for libraries") - set(INSTALL_BIN_DIR "bin/${PROJECT_NAME}" CACHE PATH "Installation directory for executables") - set(INSTALL_INCLUDE_DIR "include/${PROJECT_NAME}" CACHE PATH "Installation directory for header files") + set(INSTALL_LIB_DIR "lib" CACHE PATH "Installation directory for libraries") + set(INSTALL_BIN_DIR "bin" CACHE PATH "Installation directory for executables") + set(INSTALL_INCLUDE_DIR "include" CACHE PATH "Installation directory for header files") if(WIN32 AND NOT CYGWIN) set(DEF_INSTALL_CMAKE_DIR "CMake") else() - set(DEF_INSTALL_CMAKE_DIR "lib/CMake/${PROJECT_NAME}") + set(DEF_INSTALL_CMAKE_DIR "lib/cmake") endif() set(INSTALL_CMAKE_DIR ${DEF_INSTALL_CMAKE_DIR} CACHE PATH "Installation directory for CMake files") - # Make relative paths absolute (needed later on) foreach(p LIB BIN INCLUDE CMAKE) set(var INSTALL_${p}_DIR) - if(NOT IS_ABSOLUTE "${${var}}") - set(${var} "${CMAKE_INSTALL_PREFIX}/${${var}}") + set(varp INSTALL_${p}_DIR_PREFIX) + if(IS_ABSOLUTE "${${varp}}") + set(${varp} "${${varp}}") + else() + set(${varp} "${CMAKE_INSTALL_PREFIX}/${${var}}") endif() + set(${var} "${${varp}}/${PROJECT_NAME}") endforeach() endmacro() diff --git a/libs/Common/CMakeLists.txt b/libs/Common/CMakeLists.txt index 2e6c1a408..b14bf2926 100644 --- a/libs/Common/CMakeLists.txt +++ b/libs/Common/CMakeLists.txt @@ -24,7 +24,7 @@ SET_TARGET_PROPERTIES(Common PROPERTIES PUBLIC_HEADER "${LIBRARY_FILES_H}") INSTALL(TARGETS Common EXPORT OpenMVSTargets - RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin - LIBRARY DESTINATION "${INSTALL_LIB_DIR}" COMPONENT shlib - ARCHIVE DESTINATION "${INSTALL_LIB_DIR}" COMPONENT lib - PUBLIC_HEADER DESTINATION "${INSTALL_INCLUDE_DIR}/Common" COMPONENT dev) + LIBRARY DESTINATION "${INSTALL_LIB_DIR}" + ARCHIVE DESTINATION "${INSTALL_LIB_DIR}" + RUNTIME DESTINATION "${INSTALL_BIN_DIR}" + PUBLIC_HEADER DESTINATION "${INSTALL_INCLUDE_DIR}/Common") diff --git a/libs/IO/CMakeLists.txt b/libs/IO/CMakeLists.txt index a354376be..fdf4fb92b 100644 --- a/libs/IO/CMakeLists.txt +++ b/libs/IO/CMakeLists.txt @@ -50,7 +50,7 @@ SET_TARGET_PROPERTIES(IO PROPERTIES PUBLIC_HEADER "${LIBRARY_FILES_H}") INSTALL(TARGETS IO EXPORT OpenMVSTargets - RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin - LIBRARY DESTINATION "${INSTALL_LIB_DIR}" COMPONENT shlib - ARCHIVE DESTINATION "${INSTALL_LIB_DIR}" COMPONENT lib - PUBLIC_HEADER DESTINATION "${INSTALL_INCLUDE_DIR}/IO" COMPONENT dev) + LIBRARY DESTINATION "${INSTALL_LIB_DIR}" + ARCHIVE DESTINATION "${INSTALL_LIB_DIR}" + RUNTIME DESTINATION "${INSTALL_BIN_DIR}" + PUBLIC_HEADER DESTINATION "${INSTALL_INCLUDE_DIR}/IO") diff --git a/libs/MVS/CMakeLists.txt b/libs/MVS/CMakeLists.txt index 9d9a687bc..46188c727 100644 --- a/libs/MVS/CMakeLists.txt +++ b/libs/MVS/CMakeLists.txt @@ -50,7 +50,7 @@ SET_TARGET_PROPERTIES(MVS PROPERTIES PUBLIC_HEADER "${LIBRARY_FILES_H}") INSTALL(TARGETS MVS EXPORT OpenMVSTargets - RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin - LIBRARY DESTINATION "${INSTALL_LIB_DIR}" COMPONENT shlib - ARCHIVE DESTINATION "${INSTALL_LIB_DIR}" COMPONENT lib - PUBLIC_HEADER DESTINATION "${INSTALL_INCLUDE_DIR}/MVS" COMPONENT dev) + LIBRARY DESTINATION "${INSTALL_LIB_DIR}" + ARCHIVE DESTINATION "${INSTALL_LIB_DIR}" + RUNTIME DESTINATION "${INSTALL_BIN_DIR}" + PUBLIC_HEADER DESTINATION "${INSTALL_INCLUDE_DIR}/MVS") diff --git a/libs/Math/CMakeLists.txt b/libs/Math/CMakeLists.txt index d592bd0d2..0c8474941 100644 --- a/libs/Math/CMakeLists.txt +++ b/libs/Math/CMakeLists.txt @@ -37,12 +37,13 @@ set_target_pch(Math Common.h) TARGET_LINK_LIBRARIES(Math Common) # Install -INSTALL(FILES ${LIBRARY_FILES_H} DESTINATION "${INSTALL_INCLUDE_DIR}/Math" COMPONENT dev) -INSTALL(FILES ${IBFS_LIBRARY_FILES_H} DESTINATION "${INSTALL_INCLUDE_DIR}/Math/IBFS" COMPONENT dev) -INSTALL(FILES ${LMFit_LIBRARY_FILES_H} DESTINATION "${INSTALL_INCLUDE_DIR}/Math/LMFit" COMPONENT dev) -INSTALL(FILES ${TRWS_LIBRARY_FILES_H} DESTINATION "${INSTALL_INCLUDE_DIR}/Math/TRWS" COMPONENT dev) +INSTALL(FILES ${LIBRARY_FILES_H} DESTINATION "${INSTALL_INCLUDE_DIR}/Math") +INSTALL(FILES ${IBFS_LIBRARY_FILES_H} DESTINATION "${INSTALL_INCLUDE_DIR}/Math/IBFS") +INSTALL(FILES ${LMFit_LIBRARY_FILES_H} DESTINATION "${INSTALL_INCLUDE_DIR}/Math/LMFit") +INSTALL(FILES ${TRWS_LIBRARY_FILES_H} DESTINATION "${INSTALL_INCLUDE_DIR}/Math/TRWS") INSTALL(TARGETS Math EXPORT OpenMVSTargets - RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin - LIBRARY DESTINATION "${INSTALL_LIB_DIR}" COMPONENT shlib - ARCHIVE DESTINATION "${INSTALL_LIB_DIR}" COMPONENT lib) + LIBRARY DESTINATION "${INSTALL_LIB_DIR}" + ARCHIVE DESTINATION "${INSTALL_LIB_DIR}" + RUNTIME DESTINATION "${INSTALL_BIN_DIR}" + PUBLIC_HEADER DESTINATION "${INSTALL_INCLUDE_DIR}/Math") From 486bfad0616d61fe824459dc59ab0550f695e253 Mon Sep 17 00:00:00 2001 From: cDc Date: Mon, 7 Sep 2020 16:33:24 +0300 Subject: [PATCH 005/252] interface: export and compare camera poses with an OpenMVG refined version --- MvgOptimizeSfM.py | 77 ++++++++++++++++++++++++ apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp | 66 +++++++++++++++++++- libs/MVS/Interface.h | 7 ++- 3 files changed, 147 insertions(+), 3 deletions(-) create mode 100644 MvgOptimizeSfM.py diff --git a/MvgOptimizeSfM.py b/MvgOptimizeSfM.py new file mode 100644 index 000000000..930d6805a --- /dev/null +++ b/MvgOptimizeSfM.py @@ -0,0 +1,77 @@ +#!/usr/bin/python3 +# -*- encoding: utf-8 -*- +""" +This script is for comparing the poses stored in an OpenMVS project to the poses optimized by OpenMVG + +usage: run 'MvgOptimizeSfM.py' in a sub-folder to the OpenMVS project folder containing + 'scene.mvs' and images stored in 'images' folder; structure ex: + + -OpenMVS_project + -images + -scene.mvs + -mvg + -run script here +""" + +import os +import sys +import subprocess + +if sys.platform.startswith('win'): + PATH_DELIM = ';' +else: + PATH_DELIM = ':' + +# add this script's directory to PATH +os.environ['PATH'] += PATH_DELIM + os.path.dirname(os.path.abspath(__file__)) + +# add current directory to PATH +os.environ['PATH'] += PATH_DELIM + os.getcwd() + + +def whereis(afile): + """ + return directory in which afile is, None if not found. Look in PATH + """ + if sys.platform.startswith('win'): + cmd = "where" + else: + cmd = "which" + try: + ret = subprocess.run([cmd, afile], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=True) + return os.path.split(ret.stdout.decode())[0] + except subprocess.CalledProcessError: + return None + + +def launch(cmdline): + # Launch the current step + print('Cmd: ' + ' '.join(cmdline)) + try: + pStep = subprocess.Popen(cmdline) + pStep.wait() + if pStep.returncode != 0: + return + except KeyboardInterrupt: + sys.exit('\r\nProcess canceled by user, all files remains') + + +# Try to find openMVG and openMVS binaries in PATH +OPENMVG_BIN = whereis("openMVG_main_SfMInit_ImageListing") +OPENMVS_BIN = whereis("ReconstructMesh") + +# Ask user for openMVG and openMVS directories if not found +if not OPENMVG_BIN: + OPENMVG_BIN = input("openMVG binary folder?\n") +if not OPENMVS_BIN: + OPENMVS_BIN = input("openMVS binary folder?\n") + +launch([os.path.join(OPENMVS_BIN, 'InterfaceCOLMAP'), '../scene.mvs', '-o', '../gt_dense_cameras.camera']) +launch([os.path.join(OPENMVG_BIN, 'openMVG_main_SfMInit_ImageListingFromKnownPoses'), '-i', '../images', '-g', '../gt_dense_cameras', '-t', '1', '-o', '.']) +launch([os.path.join(OPENMVG_BIN, 'openMVG_main_ComputeFeatures'), '-i', 'sfm_data.json', '-o', '.']) +launch([os.path.join(OPENMVG_BIN, 'openMVG_main_ComputeMatches'), '-i', 'sfm_data.json', '-o', '.', '-m', '1']) +launch([os.path.join(OPENMVG_BIN, 'openMVG_main_ComputeStructureFromKnownPoses'), '-i', 'sfm_data.json', '-m', '.', '-o', 'sfm_data_struct.bin', '-b']) +launch([os.path.join(OPENMVG_BIN, 'openMVG_main_ComputeSfM_DataColor'), '-i', 'sfm_data_struct.bin', '-o', 'scene.ply']) +launch([os.path.join(OPENMVG_BIN, 'openMVG_main_openMVG2openMVS'), '-i', 'sfm_data_struct.bin', '-o', 'scene.mvs', '-d', 'images']) +launch([os.path.join(OPENMVS_BIN, 'InterfaceCOLMAP'), 'scene.mvs', '-o', 'cameras.camera']) +launch([os.path.join(OPENMVG_BIN, 'openMVG_main_evalQuality'), '-i', '..', '-c', 'sfm_data_struct.bin', '-o', 'compare']) diff --git a/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp b/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp index dbe796ca6..8582a5797 100644 --- a/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp +++ b/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp @@ -1025,6 +1025,66 @@ bool ExportImagesLog(const String& fileName, const Interface& scene) return !out.fail(); } + +// export poses in Strecha camera format: +// Strecha model is P = K[R^T|-R^T t] +// our model is P = K[R|t], t = -RC +bool ExportImagesCamera(const String& pathName, const Interface& scene) +{ + LOG_OUT() << "Writing poses: " << pathName << std::endl; + Util::ensureFolder(pathName); + for (uint32_t ID=0; ID<(uint32_t)scene.images.size(); ++ID) { + const Interface::Image& image = scene.images[ID]; + String imageFileName(image.name); + Util::ensureValidPath(imageFileName); + const String fileName(pathName+Util::getFileNameExt(imageFileName)+".camera"); + std::ofstream out(fileName); + if (!out.good()) { + VERBOSE("error: unable to open file '%s'", fileName.c_str()); + return false; + } + out << std::setprecision(12); + KMatrix K(KMatrix::IDENTITY); + RMatrix R(RMatrix::IDENTITY); + CMatrix t(CMatrix::ZERO); + unsigned width(0), height(0); + if (image.platformID != NO_ID && image.cameraID != NO_ID) { + const Interface::Platform& platform = scene.platforms[image.platformID]; + const Interface::Platform::Camera& camera = platform.cameras[image.cameraID]; + if (camera.HasResolution()) { + width = camera.width; + height = camera.height; + K = camera.K; + } else { + IMAGEPTR pImage = Image::ReadImageHeader(image.name); + width = pImage->GetWidth(); + height = pImage->GetHeight(); + K = platform.GetFullK(image.cameraID, width, height); + } + if (image.poseID != NO_ID) { + const Interface::Platform::Pose& pose = platform.poses[image.poseID]; + R = pose.R.t(); + t = pose.C; + } + } + out << K(0,0) << _T(" ") << K(0,1) << _T(" ") << K(0,2) << _T("\n"); + out << K(1,0) << _T(" ") << K(1,1) << _T(" ") << K(1,2) << _T("\n"); + out << K(2,0) << _T(" ") << K(2,1) << _T(" ") << K(2,2) << _T("\n"); + out << _T("0 0 0") << _T("\n"); + out << R(0,0) << _T(" ") << R(0,1) << _T(" ") << R(0,2) << _T("\n"); + out << R(1,0) << _T(" ") << R(1,1) << _T(" ") << R(1,2) << _T("\n"); + out << R(2,0) << _T(" ") << R(2,1) << _T(" ") << R(2,2) << _T("\n"); + out << t.x << _T(" ") << t.y << _T(" ") << t.z << _T("\n"); + out << width << _T(" ") << height << _T("\n"); + if (out.fail()) { + VERBOSE("error: unable to write file '%s'", fileName.c_str()); + return false; + } + } + return true; +} + + int main(int argc, LPCTSTR* argv) { #ifdef _DEBUGINFO @@ -1045,6 +1105,10 @@ int main(int argc, LPCTSTR* argv) if (Util::getFileExt(OPT::strOutputFileName) == _T(".log")) { // write poses in log format ExportImagesLog(MAKE_PATH_SAFE(OPT::strOutputFileName), scene); + } else + if (Util::getFileExt(OPT::strOutputFileName) == _T(".camera")) { + // write poses in Strecha camera format + ExportImagesCamera((OPT::strOutputFileName=Util::getFileFullName(MAKE_PATH_FULL(WORKING_FOLDER_FULL, OPT::strOutputFileName)))+PATH_SEPARATOR, scene); } else { // write COLMAP input data Util::ensureFolderSlash(OPT::strOutputFileName); @@ -1058,7 +1122,7 @@ int main(int argc, LPCTSTR* argv) return EXIT_FAILURE; // write MVS input data Util::ensureFolder(Util::getFullPath(MAKE_PATH_FULL(WORKING_FOLDER_FULL, OPT::strOutputFileName))); - if (!ARCHIVE::SerializeSave(scene, MAKE_PATH_SAFE(OPT::strOutputFileName), (uint32_t)OPT::bNormalizeIntrinsics?0:1)) + if (!ARCHIVE::SerializeSave(scene, MAKE_PATH_SAFE(OPT::strOutputFileName))) return EXIT_FAILURE; VERBOSE("Exported data: %u images & %u vertices (%s)", scene.images.size(), scene.vertices.size(), TD_TIMER_GET_FMT().c_str()); } diff --git a/libs/MVS/Interface.h b/libs/MVS/Interface.h index 6592a32dd..d43234b1f 100644 --- a/libs/MVS/Interface.h +++ b/libs/MVS/Interface.h @@ -441,8 +441,11 @@ struct Interface return K; } Mat33d GetFullK(uint32_t cameraID, uint32_t width, uint32_t height) const { - return ScaleK(GetK(cameraID), (double)Camera::GetNormalizationScale(width, height)/ - (cameras[cameraID].IsNormalized()?1.0:(double)cameras[cameraID].GetNormalizationScale())); + const Camera& camera = cameras[cameraID]; + if (!camera.IsNormalized() && camera.width == width && camera.height == height) + return camera.K; + return ScaleK(camera.K, (double)Camera::GetNormalizationScale(width, height)/ + (camera.IsNormalized()?1.0:(double)camera.GetNormalizationScale())); } Pose GetPose(uint32_t cameraID, uint32_t poseID) const { From 4e1e2d5d4ebe4780e217592d5bf51f1f249bb4f9 Mon Sep 17 00:00:00 2001 From: cDc Date: Tue, 15 Sep 2020 10:10:11 +0300 Subject: [PATCH 006/252] interface: export MVS scene to NVM format --- apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp | 10 +- .../InterfaceVisualSFM/InterfaceVisualSFM.cpp | 127 +++++++++++++++--- 2 files changed, 115 insertions(+), 22 deletions(-) diff --git a/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp b/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp index 8582a5797..a20f35ecf 100644 --- a/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp +++ b/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp @@ -64,7 +64,7 @@ using namespace MVS; // S T R U C T S /////////////////////////////////////////////////// namespace OPT { -bool b3Dnovator2COLMAP; // conversion direction +bool bFromOpenMVS; // conversion direction bool bNormalizeIntrinsics; String strInputFileName; String strOutputFileName; @@ -147,8 +147,6 @@ bool Initialize(size_t argc, LPCTSTR* argv) // validate input Util::ensureValidPath(OPT::strInputFileName); - const String strInputFileNameExt(Util::getFileExt(OPT::strInputFileName).ToLower()); - OPT::b3Dnovator2COLMAP = (strInputFileNameExt == MVS_EXT); const bool bInvalidCommand(OPT::strInputFileName.empty()); if (OPT::vm.count("help") || bInvalidCommand) { boost::program_options::options_description visible("Available options"); @@ -166,7 +164,9 @@ bool Initialize(size_t argc, LPCTSTR* argv) // initialize optional options Util::ensureValidFolderPath(OPT::strImageFolder); Util::ensureValidPath(OPT::strOutputFileName); - if (OPT::b3Dnovator2COLMAP) { + const String strInputFileNameExt(Util::getFileExt(OPT::strInputFileName).ToLower()); + OPT::bFromOpenMVS = (strInputFileNameExt == MVS_EXT); + if (OPT::bFromOpenMVS) { if (OPT::strOutputFileName.empty()) OPT::strOutputFileName = Util::getFilePath(OPT::strInputFileName); } else { @@ -1097,7 +1097,7 @@ int main(int argc, LPCTSTR* argv) TD_TIMER_START(); - if (OPT::b3Dnovator2COLMAP) { + if (OPT::bFromOpenMVS) { // read MVS input data Interface scene; if (!ARCHIVE::SerializeLoad(scene, MAKE_PATH_SAFE(OPT::strInputFileName))) diff --git a/apps/InterfaceVisualSFM/InterfaceVisualSFM.cpp b/apps/InterfaceVisualSFM/InterfaceVisualSFM.cpp index c686abf8b..f23218800 100644 --- a/apps/InterfaceVisualSFM/InterfaceVisualSFM.cpp +++ b/apps/InterfaceVisualSFM/InterfaceVisualSFM.cpp @@ -52,6 +52,7 @@ namespace OPT { String strInputFileName; String strOutputFileName; String strOutputImageFolder; +bool bFromOpenMVS; // conversion direction unsigned nArchiveType; int nProcessPriority; unsigned nMaxThreads; @@ -145,8 +146,17 @@ bool Initialize(size_t argc, LPCTSTR* argv) Util::ensureUnifySlash(OPT::strOutputFileName); Util::ensureUnifySlash(OPT::strOutputImageFolder); Util::ensureFolderSlash(OPT::strOutputImageFolder); - if (OPT::strOutputFileName.IsEmpty()) - OPT::strOutputFileName = Util::getFileFullName(OPT::strInputFileName) + MVS_EXT; + const String strInputFileNameExt(Util::getFileExt(OPT::strInputFileName).ToLower()); + OPT::bFromOpenMVS = (strInputFileNameExt == MVS_EXT); + if (OPT::bFromOpenMVS) { + if (OPT::strOutputFileName.empty()) + OPT::strOutputFileName = Util::getFilePath(OPT::strInputFileName); + } else { + if (OPT::strOutputFileName.empty()) + OPT::strOutputFileName = Util::getFilePath(OPT::strInputFileName) + _T("scene") MVS_EXT; + else + OPT::strOutputImageFolder = Util::getRelativePath(Util::getFilePath(OPT::strOutputFileName), Util::getFilePath(OPT::strInputFileName)+OPT::strOutputImageFolder); + } // initialize global options Process::setCurrentProcessPriority((Process::Priority)OPT::nProcessPriority); @@ -275,7 +285,86 @@ void UndistortImage(const Camera& camera, const REAL& k1, const Image8U3 imgIn, } // namespace MVS -int ImportSceneVSFM() +bool ExportSceneVSFM() +{ + TD_TIMER_START(); + + // read MVS input data + MVS::Scene scene(OPT::nMaxThreads); + if (!scene.Load(MAKE_PATH_SAFE(OPT::strInputFileName))) + return false; + + // convert and write data from OpenMVS to VisualSFM + std::vector cameras; + std::vector vertices; + std::vector measurements; // the array of 2D projections (only inliers) + std::vector correspondingPoint; // 3D point index corresponding to each 2D projection + std::vector correspondingView; // and camera index + std::vector names; + std::vector ptc; + cameras.reserve(scene.images.size()); + names.reserve(scene.images.size()); + MVS::IIndexArr mapIdx(scene.images.size()); + bool bFocalWarning(false), bPrincipalpointWarning(false); + FOREACH(idx, scene.images) { + const MVS::Image& image = scene.images[idx]; + if (!image.IsValid()) { + mapIdx[idx] = NO_ID; + continue; + } + if (!bFocalWarning && !ISEQUAL(image.camera.K(0, 0), image.camera.K(1, 1))) { + DEBUG("warning: fx != fy and NVM format does not support it"); + bFocalWarning = true; + } + if (!bPrincipalpointWarning && (!ISEQUAL(REAL(image.width-1)*0.5, image.camera.K(0, 2)) || !ISEQUAL(REAL(image.height-1)*0.5, image.camera.K(1, 2)))) { + DEBUG("warning: cx, cy are not the image center and NVM format does not support it"); + bPrincipalpointWarning = true; + } + PBA::Camera cameraNVM; + cameraNVM.SetFocalLength((image.camera.K(0, 0) + image.camera.K(1, 1)) * 0.5); + cameraNVM.SetMatrixRotation(image.camera.R.val); + cameraNVM.SetCameraCenterAfterRotation(image.camera.C.ptr()); + mapIdx[idx] = static_cast(cameras.size()); + cameras.emplace_back(cameraNVM); + names.emplace_back(MAKE_PATH_REL(WORKING_FOLDER_FULL, image.name)); + } + vertices.reserve(scene.pointcloud.points.size()); + measurements.reserve(scene.pointcloud.pointViews.size()); + correspondingPoint.reserve(scene.pointcloud.pointViews.size()); + correspondingView.reserve(scene.pointcloud.pointViews.size()); + FOREACH(idx, scene.pointcloud.points) { + const MVS::PointCloud::Point& X = scene.pointcloud.points[idx]; + const MVS::PointCloud::ViewArr& views = scene.pointcloud.pointViews[idx]; + const size_t prevMeasurements(measurements.size()); + for (MVS::IIndex idxView: views) { + const MVS::Image& image = scene.images[idxView]; + const Point2f pt(image.camera.TransformPointW2I(Cast(X))); + if (pt.x < 0 || pt.y < 0 || pt.x > image.width-1 || pt.y > image.height-1) + continue; + measurements.emplace_back(pt.x, pt.y); + correspondingView.emplace_back(static_cast(mapIdx[idxView])); + correspondingPoint.emplace_back(static_cast(vertices.size())); + } + if (prevMeasurements < measurements.size()) + vertices.emplace_back(PBA::Point3D{X.x, X.y, X.z}); + } + if (!scene.pointcloud.colors.empty()) { + ptc.reserve(scene.pointcloud.colors.size()*3); + FOREACH(idx, scene.pointcloud.points) { + const MVS::PointCloud::Color& c = scene.pointcloud.colors[idx]; + ptc.emplace_back(c.r); + ptc.emplace_back(c.g); + ptc.emplace_back(c.b); + } + } + PBA::SaveModelFile(MAKE_PATH_SAFE(OPT::strOutputFileName), cameras, vertices, measurements, correspondingPoint, correspondingView, names, ptc); + + VERBOSE("Input data exported: %u images & %u points (%s)", scene.images.size(), scene.pointcloud.GetSize(), TD_TIMER_GET_FMT().c_str()); + return true; +} + + +bool ImportSceneVSFM() { TD_TIMER_START(); @@ -333,16 +422,16 @@ int ImportSceneVSFM() const PBA::Point3D& X = vertices[idx]; scene.pointcloud.points.AddConstruct(X.xyz[0], X.xyz[1], X.xyz[2]); } - if (ptc.size() == vertices.size()*3) { - scene.pointcloud.colors.Reserve(ptc.size()); - for (size_t idx=0; idx Date: Tue, 13 Oct 2020 15:33:34 +0300 Subject: [PATCH 007/252] common: fix camera scaling --- apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp | 31 +++--- .../InterfaceVisualSFM/InterfaceVisualSFM.cpp | 12 +-- libs/MVS/Camera.h | 97 ++++++++----------- libs/MVS/Interface.h | 23 +++-- libs/MVS/Scene.cpp | 8 +- 5 files changed, 76 insertions(+), 95 deletions(-) diff --git a/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp b/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp index a20f35ecf..25b7ed2c4 100644 --- a/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp +++ b/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp @@ -109,7 +109,7 @@ bool Initialize(size_t argc, LPCTSTR* argv) ("input-file,i", boost::program_options::value(&OPT::strInputFileName), "input COLMAP folder containing cameras, images and points files OR input MVS project file") ("output-file,o", boost::program_options::value(&OPT::strOutputFileName), "output filename for storing the MVS project") ("image-folder", boost::program_options::value(&OPT::strImageFolder)->default_value(COLMAP_IMAGES_FOLDER), "folder to the undistorted images") - ("normalize,f", boost::program_options::value(&OPT::bNormalizeIntrinsics)->default_value(true), "normalize intrinsics while exporting to MVS format") + ("normalize,f", boost::program_options::value(&OPT::bNormalizeIntrinsics)->default_value(false), "normalize intrinsics while exporting to MVS format") ; boost::program_options::options_description cmdline_options; @@ -627,11 +627,7 @@ bool ImportScene(const String& strFolder, Interface& scene) camera.C = Interface::Pos3d(0,0,0); if (OPT::bNormalizeIntrinsics) { // normalize camera intrinsics - const REAL fScale(REAL(1)/Camera::GetNormalizationScale(colmapCamera.width, colmapCamera.height)); - camera.K(0,0) *= fScale; - camera.K(1,1) *= fScale; - camera.K(0,2) *= fScale; - camera.K(1,2) *= fScale; + camera.K = Camera::ScaleK(camera.K, 1.0/Camera::GetNormalizationScale(colmapCamera.width, colmapCamera.height)); } else { camera.width = colmapCamera.width; camera.height = colmapCamera.height; @@ -774,10 +770,6 @@ bool ExportScene(const String& strFolder, const Interface& scene) ASSERT(platform.cameras.size() == 1); // only one camera per platform supported const Interface::Platform::Camera& camera = platform.cameras[0]; cam.ID = ID; - cam.params[0] = camera.K(0,0); - cam.params[1] = camera.K(1,1); - cam.params[2] = camera.K(0,2); - cam.params[3] = camera.K(1,2); if (camera.width == 0 || camera.height == 0) { // find one image using this camera const Interface::Image* pImage(NULL); @@ -797,20 +789,23 @@ bool ExportScene(const String& strFolder, const Interface& scene) return false; cam.width = ptrImage->GetWidth(); cam.height = ptrImage->GetHeight(); - // denormalize camera intrinsics - const double fScale(MVS::Camera::GetNormalizationScale(cam.width, cam.height)); - cam.params[0] *= fScale; - cam.params[1] *= fScale; - cam.params[2] *= fScale; - cam.params[3] *= fScale; + // unnormalize camera intrinsics + const double scale(MVS::Camera::GetNormalizationScale(cam.width, cam.height)); + cam.params[0] = camera.K(0,0) * scale; + cam.params[1] = camera.K(1,1) * scale; + cam.params[2] = camera.K(0,2) * scale; + cam.params[3] = camera.K(1,2) * scale; } else { cam.width = camera.width; cam.height = camera.height; + cam.params[0] = camera.K(0,0); + cam.params[1] = camera.K(1,1); + cam.params[2] = camera.K(0,2); + cam.params[3] = camera.K(1,2); } if (!cam.Write(file)) return false; - KMatrix& K = Ks.AddEmpty(); - K = KMatrix::IDENTITY; + KMatrix& K = Ks.emplace_back(KMatrix::IDENTITY); K(0,0) = cam.params[0]; K(1,1) = cam.params[1]; K(0,2) = cam.params[2]; diff --git a/apps/InterfaceVisualSFM/InterfaceVisualSFM.cpp b/apps/InterfaceVisualSFM/InterfaceVisualSFM.cpp index f23218800..f90e4b612 100644 --- a/apps/InterfaceVisualSFM/InterfaceVisualSFM.cpp +++ b/apps/InterfaceVisualSFM/InterfaceVisualSFM.cpp @@ -404,11 +404,7 @@ bool ImportSceneVSFM() camera.R = RMatrix::IDENTITY; camera.C = CMatrix::ZERO; // normalize camera intrinsics - const REAL fScale(REAL(1)/MVS::Camera::GetNormalizationScale(image.width, image.height)); - camera.K(0,0) *= fScale; - camera.K(1,1) *= fScale; - camera.K(0,2) *= fScale; - camera.K(1,2) *= fScale; + camera.K = camera.GetScaledK(REAL(1)/MVS::Camera::GetNormalizationScale(image.width, image.height)); // set pose image.poseID = platform.poses.GetSize(); MVS::Platform::Pose& pose = platform.poses.AddEmpty(); @@ -566,11 +562,7 @@ int ImportSceneCMPMVS() camera.R = RMatrix::IDENTITY; camera.C = CMatrix::ZERO; // normalize camera intrinsics - const REAL fScale(REAL(1)/MVS::Camera::GetNormalizationScale(image.width, image.height)); - camera.K(0, 0) *= fScale; - camera.K(1, 1) *= fScale; - camera.K(0, 2) *= fScale; - camera.K(1, 2) *= fScale; + camera.K = camera.GetScaledK(REAL(1)/MVS::Camera::GetNormalizationScale(image.width, image.height)); // set pose image.poseID = platform.poses.GetSize(); MVS::Platform::Pose& pose = platform.poses.AddEmpty(); diff --git a/libs/MVS/Camera.h b/libs/MVS/Camera.h index fabae38d4..6078f0abf 100644 --- a/libs/MVS/Camera.h +++ b/libs/MVS/Camera.h @@ -107,39 +107,6 @@ class MVS_API CameraIntern return float(MAXF(width, height)); } - // return scaled K (assuming standard K format) - template - static inline TMatrix ScaleK(const TMatrix& K, TYPE s) { - #if 0 - TMatrix S(TMatrix::IDENTITY); - S(0,0) = S(1,1) = s; - return S*K; - #else - return TMatrix( - K(0,0)*s, K(0,1)*s, K(0,2)*s, - TYPE(0), K(1,1)*s, K(1,2)*s, - TYPE(0), TYPE(0), TYPE(1) - ); - #endif - } - inline KMatrix GetScaledK(REAL s) const { - return ScaleK(K, s); - } - - // return K.inv() (assuming standard K format) - inline KMatrix GetInvK() const { - #if 0 - return K.inv(); - #else - KMatrix invK(KMatrix::IDENTITY); - invK(0,0) = REAL(1)/K(0,0); - invK(1,1) = REAL(1)/K(1,1); - invK(0,2) = -K(0,2)*invK(0,0); - invK(1,2) = -K(1,2)*invK(1,1); - return invK; - #endif - } - // create K with the supplied focal length and sensor center template static inline TMatrix ComposeK(const TYPE& fX, const TYPE& fY, TYPER w=TYPER(1), TYPER h=TYPER(1)) { @@ -162,36 +129,56 @@ class MVS_API CameraIntern return invK; } + // return scaled K (assuming standard K format) + template + static inline TMatrix ScaleK(const TMatrix& K, TYPE s) { + return TMatrix( + K(0,0)*s, K(0,1)*s, (K(0,2)+TYPE(0.5))*s-TYPE(0.5), + TYPE(0), K(1,1)*s, (K(1,2)+TYPE(0.5))*s-TYPE(0.5), + TYPE(0), TYPE(0), TYPE(1) + ); + } + inline KMatrix GetScaledK(REAL s) const { + return ScaleK(K, s); + } + + // return K.inv() (assuming standard K format and no shear) + template + static inline TMatrix InvK(const TMatrix& K) { + ASSERT(ISZERO(K(0,1))); + TMatrix invK(TMatrix::IDENTITY); + invK(0,0) = REAL(1)/K(0,0); + invK(1,1) = REAL(1)/K(1,1); + invK(0,2) = -K(0,2)*invK(0,0); + invK(1,2) = -K(1,2)*invK(1,1); + return invK; + } + inline KMatrix GetInvK() const { + return InvK(K); + } + // returns full K and the inverse of K (assuming standard K format) template inline TMatrix GetK(uint32_t width, uint32_t height) const { ASSERT(width>0 && height>0); - const float fScale(GetNormalizationScale(width, height)); - if (K(0,2)==0 && K(1,2)==0) - return ComposeK( - TYPE(K(0,0)*fScale), TYPE(K(1,1)*fScale), - width, height ); - TMatrix fullK(TMatrix::IDENTITY); - fullK(0,0) = K(0,0)*fScale; - fullK(1,1) = K(1,1)*fScale; - fullK(0,2) = K(0,2)*fScale; - fullK(1,2) = K(1,2)*fScale; - return fullK; + const float scale(GetNormalizationScale(width, height)); + if (K(0,2) != 0 || K(1,2) != 0) + return GetScaledK(scale); + ASSERT(ISZERO(K(0,1))); + return ComposeK( + TYPE(K(0,0)*scale), TYPE(K(1,1)*scale), + width, height ); } template inline TMatrix GetInvK(uint32_t width, uint32_t height) const { ASSERT(width>0 && height>0); - const float fScale(GetNormalizationScale(width, height)); - if (K(0,2)==0 && K(1,2)==0) - return ComposeInvK( - TYPE(K(0,0)*fScale), TYPE(K(1,1)*fScale), - width, height ); - TMatrix fullInvK(TMatrix::IDENTITY); - fullInvK(0,0) = TYPE(1)/(K(0,0)*fScale); - fullInvK(1,1) = TYPE(1)/(K(1,1)*fScale); - fullInvK(0,2) = -K(0,2)*fScale*fullInvK(0,0); - fullInvK(1,2) = -K(1,2)*fScale*fullInvK(1,1); - return fullInvK; + const float scale(GetNormalizationScale(width, height)); + if (K(0,2) != 0 || K(1,2) != 0) + return InvK(GetScaledK(scale)); + ASSERT(ISZERO(K(0,1))); + return ComposeInvK( + TYPE(K(0,0)*scale), TYPE(K(1,1)*scale), + width, height ); } // normalize inhomogeneous 2D point by the given camera intrinsics K diff --git a/libs/MVS/Interface.h b/libs/MVS/Interface.h index d43234b1f..63a799518 100644 --- a/libs/MVS/Interface.h +++ b/libs/MVS/Interface.h @@ -376,7 +376,7 @@ struct Interface std::string name; // camera's name std::string bandName; // camera's band name, ex: RGB, BLUE, GREEN, RED, NIR, THERMAL, etc (optional) uint32_t width, height; // image resolution in pixels for all images sharing this camera (optional) - Mat33d K; // camera's intrinsics matrix (normalized if image resolution not specified) + Mat33d K; // camera's intrinsics matrix (normalized if image resolution not specified), where integer coordinates is by convention the pixel center Mat33d R; // camera's rotation matrix relative to the platform Pos3d C; // camera's translation vector relative to the platform @@ -405,8 +405,8 @@ struct Interface // structure describing a pose along the trajectory of a platform struct Pose { - Mat33d R; // platform's rotation matrix - Pos3d C; // platform's translation vector in the global coordinate system + Mat33d R; // platform's rotation matrix that rotates a point from world to camera coordinate system + Pos3d C; // platform's translation vector (position) in world coordinate system Pose() {} template @@ -434,12 +434,23 @@ struct Interface static Mat33d ScaleK(const Mat33d& _K, double scale) { Mat33d K(_K); K(0,0) *= scale; - K(0,1) *= scale; - K(0,2) *= scale; K(1,1) *= scale; - K(1,2) *= scale; + K(0,2) = (K(0,2)+0.5)*scale-0.5; + K(1,2) = (K(1,2)+0.5)*scale-0.5; + K(0,1) *= scale; return K; } + const Mat33d& SetFullK(uint32_t cameraID, const Mat33d& K, uint32_t width, uint32_t height, bool normalize=false) { + Camera& camera = cameras[cameraID]; + if (normalize) { + camera.width = camera.height = 0; + camera.K = ScaleK(K, 1.0/(double)Camera::GetNormalizationScale(width, height)); + } else { + camera.width = width; camera.height = height; + camera.K = K; + } + return camera.K; + } Mat33d GetFullK(uint32_t cameraID, uint32_t width, uint32_t height) const { const Camera& camera = cameras[cameraID]; if (!camera.IsNormalized() && camera.width == width && camera.height == height) diff --git a/libs/MVS/Scene.cpp b/libs/MVS/Scene.cpp index 559500619..bff522540 100644 --- a/libs/MVS/Scene.cpp +++ b/libs/MVS/Scene.cpp @@ -83,11 +83,7 @@ bool Scene::LoadInterface(const String & fileName) if (!itCamera.IsNormalized()) { // normalize K ASSERT(itCamera.HasResolution()); - const REAL scale(REAL(1)/camera.GetNormalizationScale(itCamera.width,itCamera.height)); - camera.K(0,0) *= scale; - camera.K(1,1) *= scale; - camera.K(0,2) *= scale; - camera.K(1,2) *= scale; + camera.K = camera.GetScaledK(REAL(1)/Camera::GetNormalizationScale(itCamera.width, itCamera.height)); } DEBUG_EXTRA("Camera model loaded: platform %u; camera %2u; f %.3fx%.3f; poses %u", platforms.size()-1, platform.cameras.size()-1, camera.K(0,0), camera.K(1,1), itPlatform.poses.size()); } @@ -305,7 +301,7 @@ bool Scene::LoadDMAP(const String& fileName) // create image Platform& platform = platforms.AddEmpty(); platform.name = _T("platform0"); - platform.cameras.emplace_back(CameraIntern::ScaleK(camera.K, REAL(1)/CameraIntern::GetNormalizationScale((uint32_t)imageSize.width,(uint32_t)imageSize.height)), RMatrix::IDENTITY, CMatrix::ZERO); + platform.cameras.emplace_back(camera.GetScaledK(REAL(1)/CameraIntern::GetNormalizationScale((uint32_t)imageSize.width,(uint32_t)imageSize.height)), RMatrix::IDENTITY, CMatrix::ZERO); platform.poses.emplace_back(Platform::Pose{camera.R, camera.C}); Image& image = images.AddEmpty(); image.name = MAKE_PATH_FULL(WORKING_FOLDER_FULL, imageFileName); From ac89ffde4653c8a62c25f88f7a54155688d57cc6 Mon Sep 17 00:00:00 2001 From: cDc Date: Thu, 19 Nov 2020 18:42:53 +0200 Subject: [PATCH 008/252] dense: small code refactor --- libs/MVS/DepthMap.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/MVS/DepthMap.cpp b/libs/MVS/DepthMap.cpp index 791cf2e06..494e89f9c 100644 --- a/libs/MVS/DepthMap.cpp +++ b/libs/MVS/DepthMap.cpp @@ -1919,15 +1919,15 @@ void MVS::CompareDepthMaps(const DepthMap& depthMap, const DepthMap& depthMapGT, } depths.Insert(depth); depthsGT.Insert(depthGT); - const float error(depthGT==0 ? 0 : ABS(depth-depthGT)/depthGT); + const float error(depthGT==0 ? 0 : DepthSimilarity(depthGT, depth)); errors.Insert(error); } } - const float fPSNR((float)ComputePSNR(DMatrix32F((int)depths.GetSize(),1,depths.GetData()), DMatrix32F((int)depthsGT.GetSize(),1,depthsGT.GetData()))); - const MeanStd ms(errors.Begin(), errors.GetSize()); + const float fPSNR((float)ComputePSNR(DMatrix32F((int)depths.size(),1,depths.data()), DMatrix32F((int)depthsGT.size(),1,depthsGT.data()))); + const MeanStd ms(errors.data(), errors.size()); const float mean((float)ms.GetMean()); const float stddev((float)ms.GetStdDev()); - const std::pair th(ComputeX84Threshold(errors.Begin(), errors.GetSize())); + const std::pair th(ComputeX84Threshold(errors.data(), errors.size())); #if TD_VERBOSE != TD_VERBOSE_OFF IDX idxPixel = 0; Image8U3 errorsVisual(depthMap.size()); From 5d26c25bc7ec27cde93b88c809b4eaf576ead336 Mon Sep 17 00:00:00 2001 From: cDc Date: Mon, 21 Dec 2020 09:42:44 +0200 Subject: [PATCH 009/252] mvs: view DMAPs with different resolution --- libs/MVS/Camera.h | 18 ++++++++++++++++++ libs/MVS/Scene.cpp | 9 ++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/libs/MVS/Camera.h b/libs/MVS/Camera.h index 6078f0abf..c77c4d66b 100644 --- a/libs/MVS/Camera.h +++ b/libs/MVS/Camera.h @@ -141,6 +141,24 @@ class MVS_API CameraIntern inline KMatrix GetScaledK(REAL s) const { return ScaleK(K, s); } + // same as above, but for different scale on x and y; + // in order to preserve the aspect ratio of the original size, scale both focal lengths by + // the smaller of the scale factors, resulting in adding pixels in the dimension that's growing; + template + static inline TMatrix ScaleK(const TMatrix& K, const cv::Size& size, const cv::Size& newSize, bool keepAspect=false) { + ASSERT(size.area() && newSize.area()); + cv::Point_ s(cv::Point_(newSize) / cv::Point_(size)); + if (keepAspect) + s.x = s.y = MINF(s.x, s.y); + return TMatrix( + K(0,0)*s.x, K(0,1)*s.x, (K(0,2)+TYPE(0.5))*s.x-TYPE(0.5), + TYPE(0), K(1,1)*s.y, (K(1,2)+TYPE(0.5))*s.y-TYPE(0.5), + TYPE(0), TYPE(0), TYPE(1) + ); + } + inline KMatrix GetScaledK(const cv::Size& size, const cv::Size& newSize, bool keepAspect=false) const { + return ScaleK(K, size, newSize, keepAspect); + } // return K.inv() (assuming standard K format and no shear) template diff --git a/libs/MVS/Scene.cpp b/libs/MVS/Scene.cpp index bff522540..5dd3b87e7 100644 --- a/libs/MVS/Scene.cpp +++ b/libs/MVS/Scene.cpp @@ -318,12 +318,14 @@ bool Scene::LoadDMAP(const String& fileName) // load image pixels const Image8U3 imageDepth(DepthMap2Image(depthMap)); + Image8U3 imageColor; if (image.ReloadImage(MAXF(image.width,image.height))) - cv::resize(image.image, image.image, depthMap.size()); + cv::resize(image.image, imageColor, depthMap.size()); else - image.image = imageDepth; + imageColor = imageDepth; // create point-cloud + camera.K = camera.GetScaledK(imageSize, depthMap.size()); pointcloud.points.reserve(depthMap.area()); pointcloud.pointViews.reserve(depthMap.area()); pointcloud.colors.reserve(depthMap.area()); @@ -338,7 +340,7 @@ bool Scene::LoadDMAP(const String& fileName) continue; pointcloud.points.emplace_back(camera.TransformPointI2W(Point3(c,r,depth))); pointcloud.pointViews.emplace_back(PointCloud::ViewArr{0}); - pointcloud.colors.emplace_back(image.image(r,c)); + pointcloud.colors.emplace_back(imageColor(r,c)); if (!normalMap.empty()) pointcloud.normals.emplace_back(Cast(camera.R.t()*Cast(normalMap(r,c)))); if (!confMap.empty()) @@ -348,6 +350,7 @@ bool Scene::LoadDMAP(const String& fileName) // replace color-image with depth-image image.image = imageDepth; + cv::resize(image.image, image.image, imageSize); DEBUG_EXTRA("Scene loaded from depth-map format - %dx%d size, %.2f%% coverage (%s):\n" "\t1 images (1 calibrated) with a total of %.2f MPixels (%.2f MPixels/image)\n" From ede33fb540ae99aacdd22dc67e13fc30dc9a4def Mon Sep 17 00:00:00 2001 From: cDc Date: Tue, 22 Dec 2020 19:36:19 +0200 Subject: [PATCH 010/252] interface: replace define with constexp --- apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp | 10 +++++----- libs/Common/Types.h | 16 +++++++++------- libs/MVS/Interface.h | 8 ++++---- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp b/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp index 25b7ed2c4..10ae802f0 100644 --- a/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp +++ b/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp @@ -775,7 +775,7 @@ bool ExportScene(const String& strFolder, const Interface& scene) const Interface::Image* pImage(NULL); for (uint32_t i=0; i<(uint32_t)scene.images.size(); ++i) { const Interface::Image& image = scene.images[i]; - if (image.platformID == ID && image.cameraID == 0 && image.poseID != NO_ID) { + if (image.platformID == ID && image.cameraID == 0 && image.poseID != MVS::NO_ID) { pImage = ℑ break; } @@ -826,7 +826,7 @@ bool ExportScene(const String& strFolder, const Interface& scene) cameras.resize(scene.images.size()); for (uint32_t ID=0; ID<(uint32_t)scene.images.size(); ++ID) { const Interface::Image& image = scene.images[ID]; - if (image.poseID == NO_ID) + if (image.poseID == MVS::NO_ID) continue; const Interface::Platform& platform = scene.platforms[image.platformID]; const Interface::Platform::Pose& pose = platform.poses[image.poseID]; @@ -1002,7 +1002,7 @@ bool ExportImagesLog(const String& fileName, const Interface& scene) const Interface::Image& image = scene.images[ID]; Eigen::Matrix3d R(Eigen::Matrix3d::Identity()); Eigen::Vector3d t(Eigen::Vector3d::Zero()); - if (image.poseID != NO_ID) { + if (image.poseID != MVS::NO_ID) { const Interface::Platform& platform = scene.platforms[image.platformID]; const Interface::Platform::Pose& pose = platform.poses[image.poseID]; R = Eigen::Map(pose.R.val).transpose(); @@ -1043,7 +1043,7 @@ bool ExportImagesCamera(const String& pathName, const Interface& scene) RMatrix R(RMatrix::IDENTITY); CMatrix t(CMatrix::ZERO); unsigned width(0), height(0); - if (image.platformID != NO_ID && image.cameraID != NO_ID) { + if (image.platformID != MVS::NO_ID && image.cameraID != MVS::NO_ID) { const Interface::Platform& platform = scene.platforms[image.platformID]; const Interface::Platform::Camera& camera = platform.cameras[image.cameraID]; if (camera.HasResolution()) { @@ -1056,7 +1056,7 @@ bool ExportImagesCamera(const String& pathName, const Interface& scene) height = pImage->GetHeight(); K = platform.GetFullK(image.cameraID, width, height); } - if (image.poseID != NO_ID) { + if (image.poseID != MVS::NO_ID) { const Interface::Platform::Pose& pose = platform.poses[image.poseID]; R = pose.R.t(); t = pose.C; diff --git a/libs/Common/Types.h b/libs/Common/Types.h index 89d6e4294..e3c749dd9 100644 --- a/libs/Common/Types.h +++ b/libs/Common/Types.h @@ -268,7 +268,6 @@ int _vscprintf(LPCSTR format, va_list pargs); #endif //_MSC_VER #define DECLARE_NO_INDEX(...) std::numeric_limits<__VA_ARGS__>::max() -#define NO_ID DECLARE_NO_INDEX(uint32_t) #ifndef MAKEWORD #define MAKEWORD(a, b) ((WORD)(((BYTE)(((DWORD)(a)) & 0xff)) | ((WORD)((BYTE)(((DWORD)(b)) & 0xff))) << 8)) @@ -311,6 +310,10 @@ int _vscprintf(LPCSTR format, va_list pargs); #define MAXF std::max #endif +#ifndef RAND +#define RAND std::rand +#endif + namespace SEACAVE { // signed and unsigned types of the size of the architecture @@ -329,7 +332,10 @@ typedef int64_t size_f_t; // type used as the default floating number precision typedef double REAL; -template +// invalid index +constexpr uint32_t NO_ID = DECLARE_NO_INDEX(uint32_t); + +template struct RealType { typedef typename std::conditional::value, TYPE, REALTYPE>::type type; }; template @@ -341,15 +347,11 @@ inline T MAXF3(const T& x1, const T& x2, const T& x3) { return MAXF(MAXF(x1, x2), x3); } -#ifndef RAND -#define RAND std::rand -#endif template FORCEINLINE T RANDOM() { return T(RAND())/RAND_MAX; } template -union TAliasCast -{ +union TAliasCast { T1 f; T2 i; inline TAliasCast() {} diff --git a/libs/MVS/Interface.h b/libs/MVS/Interface.h index 63a799518..b6359c221 100644 --- a/libs/MVS/Interface.h +++ b/libs/MVS/Interface.h @@ -7,6 +7,7 @@ #include #include #include +#include // D E F I N E S /////////////////////////////////////////////////// @@ -25,10 +26,6 @@ #define _USE_CUSTOM_CV #endif -#ifndef NO_ID -#define NO_ID std::numeric_limits::max() -#endif - // S T R U C T S /////////////////////////////////////////////////// @@ -164,6 +161,9 @@ class Matx namespace _INTERFACE_NAMESPACE { +// invalid index +constexpr uint32_t NO_ID = std::numeric_limits::max(); + // custom serialization namespace ARCHIVE { From c75a600a50109002e3965eb08fe306aa6713297e Mon Sep 17 00:00:00 2001 From: cDc Date: Tue, 5 Jan 2021 14:01:28 +0200 Subject: [PATCH 011/252] interface: small change --- apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp b/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp index 10ae802f0..a88666a3f 100644 --- a/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp +++ b/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp @@ -977,7 +977,9 @@ bool ExportScene(const String& strFolder, const Interface& scene) file << _T("# IMAGE_ID, QW, QX, QY, QZ, TX, TY, TZ, CAMERA_ID, NAME") << std::endl; file << _T("# POINTS2D[] as (X, Y, POINT3D_ID)") << std::endl; for (const COLMAP::Image& img: images) { - if ((bSparsePointCloud && img.projs.empty()) || !img.Write(file)) + if (bSparsePointCloud && img.projs.empty()) + continue; + if (!img.Write(file)) return false; } } From b586605c68132a2a4b3317e528f5ef4e1da5a2a0 Mon Sep 17 00:00:00 2001 From: cDc Date: Mon, 18 Jan 2021 10:09:00 +0200 Subject: [PATCH 012/252] common: fix PFM exporter/importer --- libs/Common/Types.inl | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/libs/Common/Types.inl b/libs/Common/Types.inl index cbdf89fe1..42bd806c0 100644 --- a/libs/Common/Types.inl +++ b/libs/Common/Types.inl @@ -2965,8 +2965,8 @@ bool TImage::Load(const String& fileName) Base::create(h, w); ASSERT(sizeof(float)*Base::channels() == Base::step.p[1]); const size_t rowbytes((size_t)Base::size.p[1]*Base::step.p[1]); - for (int i=0; i(i), rowbytes) != rowbytes) + for (int i=rows; i>0; ) + if (fImage.read(cv::Mat::template ptr(--i), rowbytes) != rowbytes) return false; return true; } @@ -3014,14 +3014,16 @@ bool TImage::Save(const String& fileName) const if (!fImage.isOpen()) return false; ASSERT(sizeof(float) == 4); - static const uint8_t b[4] = { 255, 0, 0, 0 }; - static const bool is_little_endian = (*((float*)b) < 1.f); - static const double scale = (is_little_endian ? -1.0 : 1.0); + #if __BYTE_ORDER == __LITTLE_ENDIAN + static const double scale(-1.0); + #else + static const double scale(1.0); + #endif fImage.print("Pf\n%d %d\n%lf\n", width(), height(), scale*Base::channels()); ASSERT(sizeof(float)*Base::channels() == Base::step.p[1]); const size_t rowbytes = (size_t)Base::size.p[1]*Base::step.p[1]; - for (int i=0; i(i), rowbytes); + for (int i=rows; i>0; ) + fImage.write(cv::Mat::template ptr(--i), rowbytes); return true; } From 30d7ec980d5d09d4393726506dd0a9d6b512c53c Mon Sep 17 00:00:00 2001 From: cDc Date: Mon, 1 Feb 2021 18:54:28 +0200 Subject: [PATCH 013/252] common: disable CPUID on ARM platforms --- libs/Common/Config.h | 49 ++++++++++++++++++++--------------- libs/Common/Util.cpp | 28 ++++++++++++++++++++ libs/MVS/SceneReconstruct.cpp | 4 +-- 3 files changed, 58 insertions(+), 23 deletions(-) diff --git a/libs/Common/Config.h b/libs/Common/Config.h index 521355e27..13104f01a 100644 --- a/libs/Common/Config.h +++ b/libs/Common/Config.h @@ -50,6 +50,27 @@ #define _USE_MATH_DEFINES +#if _MSC_VER >= 1400 +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS 1 +#endif +#ifndef _CRT_SECURE_NO_DEPRECATE +#define _CRT_SECURE_NO_DEPRECATE 1 +#endif +#ifndef _ATL_SECURE_NO_DEPRECATE +#define _ATL_SECURE_NO_DEPRECATE 1 +#endif +#ifndef _CRT_NON_CONFORMING_SWPRINTFS +#define _CRT_NON_CONFORMING_SWPRINTFS 1 +#endif +#ifndef _CRT_SECURE_CPP_OVERLOAD_SECURE_NAMES +#define _CRT_SECURE_CPP_OVERLOAD_SECURE_NAMES 0 // disable automatically overloading CPP names to secure versions +#endif +#if 0 && defined(_DEBUG) && !defined(_ITERATOR_DEBUG_LEVEL) // might not build if linking statically to 3rd party libraries +#define _ITERATOR_DEBUG_LEVEL 1 // disable std iterator debugging even in Debug, as it is very slow +#endif +#endif + //---------------------------------------------------------------------- // For Microsoft Visual C++, externally accessible symbols must be @@ -120,33 +141,19 @@ #endif // _MSC_VER -#if !defined(_DEBUG) && !defined(_PROFILE) -#define _RELEASE // exclude code useful only for debug +#if defined(__arm__) || defined (__arm64__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARMT) +#define _PLATFORM_ARM 1 +#else +#define _PLATFORM_X86 1 #endif -#if defined(_MSC_VER) && _MSC_VER >= 1400 -#ifndef _CRT_SECURE_NO_WARNINGS -#define _CRT_SECURE_NO_WARNINGS 1 -#endif -#ifndef _CRT_SECURE_NO_DEPRECATE -#define _CRT_SECURE_NO_DEPRECATE 1 -#endif -#ifndef _ATL_SECURE_NO_DEPRECATE -#define _ATL_SECURE_NO_DEPRECATE 1 -#endif -#ifndef _CRT_NON_CONFORMING_SWPRINTFS -#define _CRT_NON_CONFORMING_SWPRINTFS 1 -#endif -#ifndef _CRT_SECURE_CPP_OVERLOAD_SECURE_NAMES -#define _CRT_SECURE_CPP_OVERLOAD_SECURE_NAMES 0 // disable automatically overloading CPP names to secure versions -#endif -#if 0 && defined(_DEBUG) && !defined(_ITERATOR_DEBUG_LEVEL) // might not build if linking statically to 3rd party libraries -#define _ITERATOR_DEBUG_LEVEL 1 // disable std iterator debugging even in Debug, as it is very slow -#endif +#if !defined(_DEBUG) && !defined(_PROFILE) +#define _RELEASE // exclude code useful only for debug #endif + //optimization flags #if defined(_MSC_VER) # define ALIGN(n) __declspec(align(n)) diff --git a/libs/Common/Util.cpp b/libs/Common/Util.cpp index a32268cf7..445fb7bbf 100644 --- a/libs/Common/Util.cpp +++ b/libs/Common/Util.cpp @@ -473,6 +473,7 @@ Flags InitCPU() /*----------------------------------------------------------------*/ +#if _PLATFORM_X86 #ifdef _MSC_VER #include inline void CPUID(int CPUInfo[4], int level) { @@ -485,6 +486,11 @@ inline void CPUID(int CPUInfo[4], int level) { __get_cpuid((unsigned&)level, p+0, p+1, p+2, p+3); } #endif +#else // _PLATFORM_X86 +inline void CPUID(int CPUInfo[4], int level) { + memset(CPUInfo, 0, sizeof(int)*4); +} +#endif // _PLATFORM_X86 /** * Function to detect SSE availability in CPU. @@ -551,6 +557,7 @@ CPUINFO GetCPUInfo() } /*----------------------------------------------------------------*/ +#if _PLATFORM_X86 #ifdef _MSC_VER // Function to detect SSE availability in operating system. bool OSSupportsSSE() @@ -626,6 +633,21 @@ bool OSSupportsAVX() /*----------------------------------------------------------------*/ #endif // _MSC_VER +#else // _PLATFORM_X86 + +// Function to detect SSE availability in operating system. +bool OSSupportsSSE() +{ + return false; +} +// Function to detect AVX availability in operating system. +bool OSSupportsAVX() +{ + return false; +} +/*----------------------------------------------------------------*/ +#endif // _PLATFORM_X86 + // print details about the current build and PC void Util::LogBuild() @@ -644,6 +666,7 @@ void Util::LogBuild() } // print information about the memory usage +#if _PLATFORM_X86 #ifdef _MSC_VER #include #pragma comment(lib, "Psapi.lib") @@ -679,6 +702,11 @@ void Util::LogMemoryInfo() LOG(_T("} ENDINFO")); } #endif // _MSC_VER +#else // _PLATFORM_X86 +void Util::LogMemoryInfo() +{ +} +#endif // _PLATFORM_X86 // Parses a ASCII command line string and returns an array of pointers to the command line arguments, diff --git a/libs/MVS/SceneReconstruct.cpp b/libs/MVS/SceneReconstruct.cpp index 5a87e74f4..005d975e8 100644 --- a/libs/MVS/SceneReconstruct.cpp +++ b/libs/MVS/SceneReconstruct.cpp @@ -333,7 +333,7 @@ inline Plane getFacetPlane(const facet_t& facet) // Check if a point (p) is coplanar with a triangle (a, b, c); // return orientation type -#ifdef __GNUC__ +#if _PLATFORM_X86 && defined(__GNUC__) #pragma GCC push_options #pragma GCC target ("no-fma") #endif @@ -364,7 +364,7 @@ static inline int orientation(const point_t& a, const point_t& b, const point_t& return CGAL::COPLANAR; #endif } -#ifdef __GNUC__ +#if _PLATFORM_X86 && defined(__GNUC__) #pragma GCC pop_options #endif From 0a49d561277aed60a50dd1f8e0e491b78cdb81b9 Mon Sep 17 00:00:00 2001 From: cDc Date: Sat, 13 Feb 2021 21:40:43 +0200 Subject: [PATCH 014/252] dense: add Region-Of-Interest (ROI) support --- CMakeLists.txt | 20 +++++++---- libs/Common/AABB.h | 9 +++++ libs/Common/OBB.h | 23 ++++++++++-- libs/Common/OBB.inl | 73 +++++++++++++++++++++++++++++++-------- libs/Common/Plane.h | 17 +++++++++ libs/Common/Types.inl | 19 ++++++++++ libs/MVS/Interface.h | 29 ++++++++++++++-- libs/MVS/PointCloud.cpp | 6 ++++ libs/MVS/PointCloud.h | 3 +- libs/MVS/Scene.cpp | 8 +++++ libs/MVS/Scene.h | 5 ++- libs/MVS/SceneDensify.cpp | 13 ++++--- 12 files changed, 193 insertions(+), 32 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index febcd8de2..3c69b60d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,12 +4,20 @@ # # Project-wide settings CMAKE_MINIMUM_REQUIRED(VERSION 3.8.0) -if(POLICY CMP0011) - cmake_policy(SET CMP0011 NEW) -endif() -if(POLICY CMP0074) - cmake_policy(SET CMP0074 NEW) -endif() +IF(POLICY CMP0011) + CMAKE_POLICY(SET CMP0011 NEW) +ENDIF() +IF(POLICY CMP0074) + CMAKE_POLICY(SET CMP0074 NEW) +ENDIF() + +# Load automatically VCPKG toolchain if available +IF(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{VCPKG_ROOT}) + SET(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" CACHE STRING "") + IF(NOT DEFINED VCPKG_TARGET_TRIPLET AND DEFINED ENV{VCPKG_DEFAULT_TRIPLET}) + SET(VCPKG_TARGET_TRIPLET "$ENV{VCPKG_DEFAULT_TRIPLET}" CACHE STRING "") + ENDIF() +ENDIF() # Name of the project. # diff --git a/libs/Common/AABB.h b/libs/Common/AABB.h index 63c707181..be2546526 100644 --- a/libs/Common/AABB.h +++ b/libs/Common/AABB.h @@ -88,6 +88,15 @@ class TAABB inline TYPE& operator [] (BYTE i) { ASSERT(i + void serialize(Archive& ar, const unsigned int /*version*/) { + ar & ptMin; + ar & ptMax; + } + #endif }; // class TAABB /*----------------------------------------------------------------*/ diff --git a/libs/Common/OBB.h b/libs/Common/OBB.h index 30ff5fcb9..766257b2c 100644 --- a/libs/Common/OBB.h +++ b/libs/Common/OBB.h @@ -42,16 +42,19 @@ class TOBB enum { numCorners = (DIMS==1 ? 2 : (DIMS==2 ? 4 : 8)) }; // 2^DIMS enum { numScalar = (5*DIMS) }; - MATRIX m_rot; // rotation matrix of the transformation (orthonormal axes) - POINT m_pos; // translation of the transformation (center-point) - POINT m_ext; // bounding box extents (half axis length) + MATRIX m_rot; // rotation matrix from world to local (orthonormal axes) + POINT m_pos; // translation from local to world (center-point) + POINT m_ext; // bounding box extents in local (half axis length) //--------------------------------------- inline TOBB() {} + inline TOBB(bool); + inline TOBB(const MATRIX& rot, const POINT& ptMin, const POINT& ptMax); inline TOBB(const POINT* pts, size_t n); inline TOBB(const POINT* pts, size_t n, const TRIANGLE* tris, size_t s); + inline void Set(const MATRIX& rot, const POINT& ptMin, const POINT& ptMax); // build from rotation matrix from world to local, and local min/max corners inline void Set(const POINT* pts, size_t n); // build from points inline void Set(const POINT* pts, size_t n, const TRIANGLE* tris, size_t s); // build from triangles inline void Set(const MATRIX& C, const POINT* pts, size_t n); // build from covariance matrix @@ -62,6 +65,8 @@ class TOBB inline void BuildAdd(const POINT&); // add a new point to the online build inline void BuildEnd(); // end online build for computing the rotation + inline bool IsValid() const; + inline void Enlarge(TYPE); inline void EnlargePercent(TYPE); @@ -79,8 +84,20 @@ class TOBB inline TYPE GetVolume() const; + bool Intersects(const POINT&) const; + inline TYPE& operator [] (BYTE i) { ASSERT(i + void serialize(Archive& ar, const unsigned int /*version*/) { + ar & m_rot; + ar & m_pos; + ar & m_ext; + } + #endif }; // class TOBB /*----------------------------------------------------------------*/ diff --git a/libs/Common/OBB.inl b/libs/Common/OBB.inl index 94fe16566..920b6302a 100644 --- a/libs/Common/OBB.inl +++ b/libs/Common/OBB.inl @@ -11,6 +11,19 @@ // S T R U C T S /////////////////////////////////////////////////// +template +inline TOBB::TOBB(bool) + : + m_rot(MATRIX::Identity()), + m_pos(POINT::Zero()), + m_ext(POINT::Zero()) +{ +} +template +inline TOBB::TOBB(const MATRIX& rot, const POINT& ptMin, const POINT& ptMax) +{ + Set(rot, ptMin, ptMax); +} template inline TOBB::TOBB(const POINT* pts, size_t n) { @@ -24,6 +37,15 @@ inline TOBB::TOBB(const POINT* pts, size_t n, const TRIANGLE* tris, s /*----------------------------------------------------------------*/ +// build from rotation matrix from world to local, and local min/max corners +template +inline void TOBB::Set(const MATRIX& rot, const POINT& ptMin, const POINT& ptMax) +{ + m_rot = rot; + m_pos = (ptMax + ptMin) * TYPE(0.5); + m_ext = (ptMax - ptMin) * TYPE(0.5); +} + // Inspired from "Fitting Oriented Bounding Boxes" by James Gregson // http://jamesgregson.blogspot.ro/2011/03/latex-test.html @@ -235,6 +257,15 @@ inline void TOBB::BuildEnd() /*----------------------------------------------------------------*/ +// check if the oriented bounding box has positive size +template +inline bool TOBB::IsValid() const +{ + return m_ext.minCoeff() > TYPE(0); +} // IsValid +/*----------------------------------------------------------------*/ + + template inline void TOBB::Enlarge(TYPE x) { @@ -248,6 +279,22 @@ inline void TOBB::EnlargePercent(TYPE x) /*----------------------------------------------------------------*/ +// Update the box by the given pos delta. +template +inline void TOBB::Translate(const POINT& d) +{ + m_pos += d; +} +// Update the box by the given transform. +template +inline void TOBB::Transform(const MATRIX& m) +{ + m_rot = m * m_rot; + m_pos = m * m_pos; +} +/*----------------------------------------------------------------*/ + + template inline typename TOBB::POINT TOBB::GetCenter() const { @@ -341,20 +388,18 @@ inline TYPE TOBB::GetVolume() const /*----------------------------------------------------------------*/ -// Update the box by the given pos delta. -template -inline void TOBB::Translate(const POINT& d) -{ - m_pos += d; -} -/*----------------------------------------------------------------*/ - - -// Update the box by the given transform. template -inline void TOBB::Transform(const MATRIX& m) +bool TOBB::Intersects(const POINT& pt) const { - m_rot = m * m_rot; - m_pos = m * m_pos; -} + const POINT dist(m_rot * (pt - m_pos)); + if (DIMS == 2) { + return ABS(dist[0]) <= m_ext[0] + && ABS(dist[1]) <= m_ext[1]; + } + if (DIMS == 3) { + return ABS(dist[0]) <= m_ext[0] + && ABS(dist[1]) <= m_ext[1] + && ABS(dist[2]) <= m_ext[2]; + } +} // Intersects(POINT) /*----------------------------------------------------------------*/ diff --git a/libs/Common/Plane.h b/libs/Common/Plane.h index a293c94d2..7cd439207 100644 --- a/libs/Common/Plane.h +++ b/libs/Common/Plane.h @@ -64,6 +64,15 @@ class TPlane inline TYPE& operator [] (BYTE i) { ASSERT(i<=DIMS); return m_vN.data()[i]; } inline TYPE operator [] (BYTE i) const { ASSERT(i<=DIMS); return m_vN.data()[i]; } + + #ifdef _USE_BOOST + // implement BOOST serialization + template + void serialize(Archive& ar, const unsigned int /*version*/) { + ar & m_vN; + ar & m_fD; + } + #endif }; // class TPlane /*----------------------------------------------------------------*/ @@ -106,6 +115,14 @@ class TFrustum inline TYPE& operator [] (BYTE i) { ASSERT(i + void serialize(Archive& ar, const unsigned int /*version*/) { + ar & m_planes; + } + #endif }; // class TPlane /*----------------------------------------------------------------*/ diff --git a/libs/Common/Types.inl b/libs/Common/Types.inl index 42bd806c0..645df52f5 100644 --- a/libs/Common/Types.inl +++ b/libs/Common/Types.inl @@ -3708,6 +3708,25 @@ namespace boost { ar & m.distance; } + #ifdef _USE_EIGEN + // Serialization support for Eigen::Matrix + // specialization handling fixed sized matrices + template + inline void save(Archive& ar, const Eigen::Matrix& M, const unsigned int /*version*/) { + ar << make_nvp("data", make_array(M.data(), _Rows*_Cols)); + } + template + inline void load(Archive& ar, Eigen::Matrix& M, const unsigned int /*version*/) { + ar >> make_nvp("data", make_array(M.data(), _Rows*_Cols)); + } + // The function that causes boost::serialization to look for separate + // save() and load() functions when serializing and Eigen matrix. + template + inline void serialize(Archive& ar, Eigen::Matrix& M, const unsigned int version) { + split_free(ar, M, version); + } + #endif // _USE_EIGEN + } // namespace serialization } // namespace boost /*----------------------------------------------------------------*/ diff --git a/libs/MVS/Interface.h b/libs/MVS/Interface.h index b6359c221..209f9f00a 100644 --- a/libs/MVS/Interface.h +++ b/libs/MVS/Interface.h @@ -13,7 +13,7 @@ // D E F I N E S /////////////////////////////////////////////////// #define MVSI_PROJECT_ID "MVSI" // identifies the project stream -#define MVSI_PROJECT_VER ((uint32_t)5) // identifies the version of a project stream +#define MVSI_PROJECT_VER ((uint32_t)6) // identifies the version of a project stream // set a default namespace name if none given #ifndef _INTERFACE_NAMESPACE @@ -162,7 +162,7 @@ class Matx namespace _INTERFACE_NAMESPACE { // invalid index -constexpr uint32_t NO_ID = std::numeric_limits::max(); +constexpr uint32_t NO_ID {std::numeric_limits::max()}; // custom serialization namespace ARCHIVE { @@ -489,7 +489,7 @@ struct Interface uint32_t ID; // ID of this image in the global space (optional) Image() : platformID(NO_ID), cameraID(NO_ID), poseID(NO_ID), ID(NO_ID) {} - + bool IsValid() const { return poseID != NO_ID; } template @@ -589,6 +589,25 @@ struct Interface typedef std::vector ColorArr; /*----------------------------------------------------------------*/ + // structure describing a Oriented Bounding-Box (optional) + struct OBB { + Mat33d rot; // rotation from scene to OBB coordinate system + Pos3d ptMin; // minimal point represented in OBB coordinate system + Pos3d ptMax; // maximal point represented in OBB coordinate system + + OBB() : rot(Mat33d::eye()), ptMin(0, 0, 0), ptMax(0, 0, 0) {} + + bool IsValid() const { return ptMin.x < ptMax.x && ptMin.y < ptMax.y && ptMin.z < ptMax.z; } + + template + void serialize(Archive& ar, const unsigned int /*version*/) { + ar & rot; + ar & ptMin; + ar & ptMax; + } + }; + /*----------------------------------------------------------------*/ + PlatformArr platforms; // array of platforms ImageArr images; // array of images VertexArr vertices; // array of reconstructed 3D points @@ -598,6 +617,7 @@ struct Interface NormalArr linesNormal; // array of reconstructed 3D lines' normal (optional) ColorArr linesColor; // array of reconstructed 3D lines' color (optional) Mat44d transform; // transformation used to convert from absolute to relative coordinate system (optional) + OBB obb; // minimum oriented bounding box containing the scene (optional) Interface() : transform(Mat44d::eye()) {} @@ -624,6 +644,9 @@ struct Interface ar & linesColor; if (version > 1) { ar & transform; + if (version > 5) { + ar & obb; + } } } } diff --git a/libs/MVS/PointCloud.cpp b/libs/MVS/PointCloud.cpp index 42e61230c..6605f0027 100644 --- a/libs/MVS/PointCloud.cpp +++ b/libs/MVS/PointCloud.cpp @@ -67,6 +67,12 @@ void PointCloud::RemovePoint(IDX idx) colors.RemoveAt(idx); points.RemoveAt(idx); } +void PointCloud::RemovePointsOutside(const OBB3f& obb) { + ASSERT(obb.IsValid()); + RFOREACH(i, points) + if (!obb.Intersects(points[i])) + RemovePoint(i); +} /*----------------------------------------------------------------*/ diff --git a/libs/MVS/PointCloud.h b/libs/MVS/PointCloud.h index ce2a9722d..0014103bb 100644 --- a/libs/MVS/PointCloud.h +++ b/libs/MVS/PointCloud.h @@ -86,7 +86,8 @@ class MVS_API PointCloud inline bool IsValid() const { ASSERT(points.GetSize() == pointViews.GetSize() || pointViews.IsEmpty()); return !pointViews.IsEmpty(); } inline size_t GetSize() const { ASSERT(points.GetSize() == pointViews.GetSize() || pointViews.IsEmpty()); return points.GetSize(); } - void RemovePoint(IDX idx); + void RemovePoint(IDX); + void RemovePointsOutside(const OBB3f&); Box GetAABB() const; Box GetAABB(const Box& bound) const; diff --git a/libs/MVS/Scene.cpp b/libs/MVS/Scene.cpp index 5dd3b87e7..7ab5c1c21 100644 --- a/libs/MVS/Scene.cpp +++ b/libs/MVS/Scene.cpp @@ -184,6 +184,9 @@ bool Scene::LoadInterface(const String & fileName) } } + // import region of interest + obb.Set(Matrix3x3f(obj.obb.rot), Point3f(obj.obb.ptMin), Point3f(obj.obb.ptMax)); + DEBUG_EXTRA("Scene loaded from interface format (%s):\n" "\t%u images (%u calibrated) with a total of %.2f MPixels (%.2f MPixels/image)\n" "\t%u points, %u vertices, %u faces", @@ -266,6 +269,11 @@ bool Scene::SaveInterface(const String & fileName, int version) const } } + // export region of interest + obj.obb.rot = Matrix3x3f(obb.m_rot); + obj.obb.ptMin = Point3f((obb.m_pos-obb.m_ext).eval()); + obj.obb.ptMax = Point3f((obb.m_pos+obb.m_ext).eval()); + // serialize out the current state if (!ARCHIVE::SerializeSave(obj, fileName, version>=0?uint32_t(version):MVSI_PROJECT_VER)) return false; diff --git a/libs/MVS/Scene.h b/libs/MVS/Scene.h index 71b94acab..070e37878 100644 --- a/libs/MVS/Scene.h +++ b/libs/MVS/Scene.h @@ -56,13 +56,15 @@ class MVS_API Scene ImageArr images; // images, each referencing a platform's camera pose PointCloud pointcloud; // point-cloud (sparse or dense), each containing the point position and the views seeing it Mesh mesh; // mesh, represented as vertices and triangles, constructed from the input point-cloud + OBB3f obb; // optional region-of-interest; oriented bounding box containing the entire scene unsigned nCalibratedImages; // number of valid images unsigned nMaxThreads; // maximum number of threads used to distribute the work load public: - inline Scene(unsigned _nMaxThreads=0) : nMaxThreads(Thread::getMaxThreads(_nMaxThreads)) {} + inline Scene(unsigned _nMaxThreads=0) + : nMaxThreads(Thread::getMaxThreads(_nMaxThreads)), obb(true) {} void Release(); bool IsEmpty() const; @@ -111,6 +113,7 @@ class MVS_API Scene ar & images; ar & pointcloud; ar & mesh; + ar & obb; } #endif }; diff --git a/libs/MVS/SceneDensify.cpp b/libs/MVS/SceneDensify.cpp index df013f260..367d3319b 100644 --- a/libs/MVS/SceneDensify.cpp +++ b/libs/MVS/SceneDensify.cpp @@ -1153,10 +1153,8 @@ bool DepthMapsData::FilterDepthMap(DepthData& depthDataRef, const IIndexArr& idx !SaveConfidenceMap(ComposeDepthFilePath(imageRef.GetID(), "filtered.cmap"), newConfMap)) return false; - #if TD_VERBOSE != TD_VERBOSE_OFF - DEBUG("Depth map %3u filtered using %u other images: %u/%u depths discarded (%s)", imageRef.GetID(), N, nDiscarded, nProcessed, TD_TIMER_GET_FMT().c_str()); - #endif - + DEBUG("Depth map %3u filtered using %u other images: %u/%u depths discarded (%s)", + imageRef.GetID(), N, nDiscarded, nProcessed, TD_TIMER_GET_FMT().c_str()); return true; } // FilterDepthMap /*----------------------------------------------------------------*/ @@ -1463,6 +1461,13 @@ bool Scene::DenseReconstruction(int nFusionMode) #endif if (!pointcloud.IsEmpty()) { + if (obb.IsValid()) { + TD_TIMER_START(); + const size_t numPoints = pointcloud.GetSize(); + pointcloud.RemovePointsOutside(obb); + VERBOSE("Point-cloud trimmed to ROI: %u points removed (%s)", + numPoints-pointcloud.GetSize(), TD_TIMER_GET_FMT().c_str()); + } if (pointcloud.colors.IsEmpty() && OPTDENSE::nEstimateColors == 1) EstimatePointColors(images, pointcloud); if (pointcloud.normals.IsEmpty() && OPTDENSE::nEstimateNormals == 1) From 4629ad26324b61a08561cc991b63f58cb79f07be Mon Sep 17 00:00:00 2001 From: cDc Date: Sun, 14 Feb 2021 21:35:32 +0200 Subject: [PATCH 015/252] common: improve octree speed --- .gitignore | 1 + apps/Viewer/Scene.cpp | 8 +- apps/Viewer/Scene.h | 4 +- libs/Common/ConfigTable.h | 2 +- libs/Common/HalfFloat.h | 4 - libs/Common/Octree.h | 187 ++++++++++------------ libs/Common/Octree.inl | 321 ++++++++++++++++++-------------------- libs/Common/Rotation.h | 5 - libs/Common/Rotation.inl | 11 -- libs/MVS/Mesh.cpp | 16 ++ libs/MVS/Mesh.h | 27 ++++ libs/MVS/Scene.h | 2 +- libs/MVS/SceneDensify.cpp | 6 +- libs/MVS/SceneRefine.cpp | 42 ++--- libs/MVS/SceneTexture.cpp | 33 +--- 15 files changed, 310 insertions(+), 359 deletions(-) diff --git a/.gitignore b/.gitignore index 1880d2e51..9a2dbda4c 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,4 @@ CMakeSettings.json out/ bin/ binaries/ +build_wsl/ diff --git a/apps/Viewer/Scene.cpp b/apps/Viewer/Scene.cpp index b40cb1bf2..c5dca5bd5 100644 --- a/apps/Viewer/Scene.cpp +++ b/apps/Viewer/Scene.cpp @@ -178,12 +178,16 @@ class EVTComputeOctree : public Event bool Run(void*) { MVS::Scene& scene = pScene->scene; if (!scene.mesh.IsEmpty()) { - Scene::OctreeMesh octMesh(scene.mesh.vertices); + Scene::OctreeMesh octMesh(scene.mesh.vertices, [](Scene::OctreeMesh::IDX_TYPE size, Scene::OctreeMesh::Type /*radius*/) { + return size > 256; + }); scene.mesh.ListIncidenteFaces(); pScene->octMesh.Swap(octMesh); } else if (!scene.pointcloud.IsEmpty()) { - Scene::OctreePoints octPoints(scene.pointcloud.points); + Scene::OctreePoints octPoints(scene.pointcloud.points, [](Scene::OctreePoints::IDX_TYPE size, Scene::OctreePoints::Type /*radius*/) { + return size > 512; + }); pScene->octPoints.Swap(octPoints); } return true; diff --git a/apps/Viewer/Scene.h b/apps/Viewer/Scene.h index 0d31fa076..63aca7d5e 100644 --- a/apps/Viewer/Scene.h +++ b/apps/Viewer/Scene.h @@ -48,8 +48,8 @@ namespace VIEWER { class Scene { public: - typedef TOctree OctreePoints; - typedef TOctree OctreeMesh; + typedef TOctree OctreePoints; + typedef TOctree OctreeMesh; public: String name; diff --git a/libs/Common/ConfigTable.h b/libs/Common/ConfigTable.h index d112a5ea1..337512108 100644 --- a/libs/Common/ConfigTable.h +++ b/libs/Common/ConfigTable.h @@ -56,7 +56,7 @@ class GENERAL_API CConfigTable const SML& GetConfig() const { return m_oSML; } SML& GetConfig() { return m_oSML; } inline SMLVALUE& operator[] (const String& name) { return m_oSML[name]; } - inline IDX InsertChild(CConfigTable& oCfg) { oCfg.SetParent(this); return m_oSML.InsertChild((const LPSML)&oCfg.m_oSML); } + inline IDX InsertChild(CConfigTable& oCfg) { oCfg.SetParent(this); return m_oSML.InsertChild(&oCfg.m_oSML); } inline void RemoveChild(CConfigTable& oCfg) { oCfg.SetParent(NULL); m_oSML.RemoveChild(oCfg.m_oSML.GetName()); } // misc methods diff --git a/libs/Common/HalfFloat.h b/libs/Common/HalfFloat.h index 80a0a4c9a..3f38c831a 100644 --- a/libs/Common/HalfFloat.h +++ b/libs/Common/HalfFloat.h @@ -39,10 +39,6 @@ class GENERAL_API hfloat template inline hfloat(T v) : val(fromFloat(static_cast(v))) {} - inline hfloat& operator = (hfloat v) { - val = v.val; - return *this; - } inline hfloat& operator = (float v) { val = fromFloat(v); return *this; diff --git a/libs/Common/Octree.h b/libs/Common/Octree.h index cc5f22e95..568b87ca6 100644 --- a/libs/Common/Octree.h +++ b/libs/Common/Octree.h @@ -22,106 +22,80 @@ namespace SEACAVE { // S T R U C T S /////////////////////////////////////////////////// -// raw array wrapper -template -class TItemArr -{ -public: - typedef TYPE Type; - - inline TItemArr() {} - inline TItemArr(const TYPE* data, IDX size) : m_data(data), m_size(size) {} - inline void Set(const TYPE* data, IDX size) { m_data = data; m_size = size; } - - inline const TYPE& operator[](IDX i) const { return m_data[i]; } - inline const TYPE* Begin() const { return m_data; } - inline const TYPE* GetData() const { return m_data; } - inline IDX GetSize() const { return m_size; } - -protected: - const TYPE* m_data; - IDX m_size; -}; // class TItemArr -/*----------------------------------------------------------------*/ - - -// octree cell class -template -class TOctreeCell +// basic octree class +// each item should define the operator const POINT_TYPE& returning its center; +// at build time, a functor must be supplied that returns true as long as +// the current cell should be split farther, given its number of items and radius, ex: +// [](Octree::IDX_TYPE size, Octree::Type radius) { +// return size > SIZE && radius > RADIUS; +// } +// where: +// SIZE is the minimum number of items contained by the cell so that this to be divided further +// RADIUS is the minimum size of the cell allowed to be divided further +// both conditions represent exclusive limits and both should be true for the division to take place +template +class TOctree { STATIC_ASSERT(DIMS > 0 && DIMS <= 3); public: + typedef TYPE Type; + typedef typename ITEMARR_TYPE::Type ITEM_TYPE; + typedef typename ITEMARR_TYPE::IDX IDX_TYPE; + typedef SEACAVE::cList IDXARR_TYPE; typedef Eigen::Matrix POINT_TYPE; typedef SEACAVE::TAABB AABB_TYPE; typedef uint32_t SIZE_TYPE; - typedef struct { - POINT_TYPE center; // center of the current cell - } NODE_TYPE; - typedef struct { - IDX idxBegin; // index in the global array of the first item contained by this cell - SIZE_TYPE size; // number of items contained by this cell in the global array - DATA_TYPE data; // user data associated with this leaf - } LEAF_TYPE; - enum { dataSize = (sizeof(NODE_TYPE)>sizeof(LEAF_TYPE) ? sizeof(NODE_TYPE) : sizeof(LEAF_TYPE)) }; - enum { numChildren = (2<<(DIMS-1)) }; - -public: - inline TOctreeCell(); - inline TOctreeCell(TOctreeCell*); - inline ~TOctreeCell(); - - inline void Release(); - inline void Swap(TOctreeCell&); - - inline unsigned ComputeChild(const POINT_TYPE& item) const; - static void ComputeCenter(POINT_TYPE []); - - inline bool IsLeaf() const { return (m_child==NULL); } - inline const TOctreeCell& GetChild(int i) const { ASSERT(!IsLeaf() && i -class TOctree -{ -public: - typedef TYPE Type; - typedef TOctreeCell CELL_TYPE; - typedef typename ITEMARR_TYPE::Type ITEM_TYPE; - typedef SEACAVE::cList IDXARR_TYPE; - typedef SEACAVE::cList CELLPTRARR_TYPE; - typedef typename IDXARR_TYPE::Type IDX_TYPE; - typedef typename CELL_TYPE::POINT_TYPE POINT_TYPE; - typedef typename CELL_TYPE::AABB_TYPE AABB_TYPE; + class CELL_TYPE { + public: + typedef struct { + POINT_TYPE center; // center of the current cell + } NODE_TYPE; + typedef struct { + IDX_TYPE idxBegin; // index in the global array of the first item contained by this cell + SIZE_TYPE size; // number of items contained by this cell in the global array + DATA_TYPE data; // user data associated with this leaf + } LEAF_TYPE; + enum { dataSize = (sizeof(NODE_TYPE)>sizeof(LEAF_TYPE) ? sizeof(NODE_TYPE) : sizeof(LEAF_TYPE)) }; + enum { numChildren = (2<<(DIMS-1)) }; + + public: + inline CELL_TYPE(); + inline ~CELL_TYPE(); + + inline void Release(); + inline void Swap(CELL_TYPE&); + + inline unsigned ComputeChild(const POINT_TYPE& item) const; + static void ComputeCenter(POINT_TYPE []); + + inline bool IsLeaf() const { return (m_child==NULL); } + inline const CELL_TYPE& GetChild(int i) const { ASSERT(!IsLeaf() && i CELLPTRARR_TYPE; struct IndexInserter { IDXARR_TYPE& indices; IndexInserter(IDXARR_TYPE& _indices) : indices(_indices) {} void operator()(IDX_TYPE idx) { indices.Insert(idx); } - void operator()(const IDX_TYPE* idices, size_t size) { indices.Join(idices, size); } + void operator()(const IDX_TYPE* idices, SIZE_TYPE size) { indices.Join(idices, size); } }; struct CellInserter { @@ -132,17 +106,23 @@ class TOctree public: inline TOctree() {} - inline TOctree(const ITEMARR_TYPE&); - inline TOctree(const ITEMARR_TYPE&, const AABB_TYPE&); + template + inline TOctree(const ITEMARR_TYPE&, Functor split); + template + inline TOctree(const ITEMARR_TYPE&, const AABB_TYPE&, Functor split); inline void Release(); inline void Swap(TOctree&); - void Insert(const ITEMARR_TYPE&); - void Insert(const ITEMARR_TYPE&, const AABB_TYPE&); + template + void Insert(const ITEMARR_TYPE&, Functor split); + template + void Insert(const ITEMARR_TYPE&, const AABB_TYPE&, Functor split); template inline void CollectCells(INSERTER&) const; + template + void CollectCells(const CELL_TYPE&, INSERTER&) const; template inline void ParseCells(PARSER&); @@ -170,30 +150,27 @@ class TOctree template inline void TraverseCells(const TFrustum&, CELLPTRARR_TYPE&); + inline const CELL_TYPE& GetRoot() const { return m_root; } inline AABB_TYPE GetAabb() const { return m_root.GetAabb(m_radius); } - inline TYPE GetRadius(const AABB_TYPE& aabb) const { - // radius of the root cell - const POINT_TYPE size(aabb.GetSize() / TYPE(2)); - TYPE radius = size[0]; - if (DIMS > 1 && radius < size[1]) - radius = size[1]; - if (DIMS > 2 && radius < size[2]) - radius = size[2]; - return radius; - } inline bool IsEmpty() const { return m_indices.IsEmpty(); } inline size_t GetNumItems() const { return m_indices.GetSize(); } inline const IDXARR_TYPE& GetIndexArr() const { return m_indices; } inline const ITEM_TYPE* GetItems() const { return m_items; } + inline const POINT_TYPE& GetItem(IDX_TYPE idx) const { ASSERT(m_items != NULL); return m_items[idx]; } + inline void ResetItems() { m_items = NULL; } protected: static inline POINT_TYPE ComputeChildCenter(const POINT_TYPE&, TYPE, unsigned); - void _Insert(CELL_TYPE&, TYPE, IDXARR_TYPE []); - void _Insert(CELL_TYPE&, const POINT_TYPE&, TYPE, IDXARR_TYPE&); + template + struct _InsertData { + enum : IDX_TYPE { NO_INDEX = DECLARE_NO_INDEX(IDX_TYPE) }; + IDXARR_TYPE successors; // single connected list of next item indices + Functor split; // used to decide if a cell needs to be split farther + }; + template + void _Insert(CELL_TYPE&, const POINT_TYPE& center, TYPE radius, IDX_TYPE start, IDX_TYPE size, _InsertData&); - template - void _CollectCells(const CELL_TYPE&, INSERTER&) const; template void _ParseCells(CELL_TYPE&, TYPE, PARSER&); diff --git a/libs/Common/Octree.inl b/libs/Common/Octree.inl index 83075ebb3..ce5ea80f8 100644 --- a/libs/Common/Octree.inl +++ b/libs/Common/Octree.inl @@ -8,53 +8,37 @@ // D E F I N E S /////////////////////////////////////////////////// -// max number of items in one cell -#define OCTREE_CELLITEMS SIZE - #ifdef _USE_OPENMP // minimum number of polygons for which we do multi-threading #define OCTREE_MIN_ITEMS_MINTHREAD 1024*2 #endif -// size of a cell -#define OCTREE_CELLSIZE (TYPE(NOM)/TYPE(DENOM)) -// radius of a cell -#define OCTREE_CELLRADIUS (OCTREE_CELLSIZE/2) - // S T R U C T S /////////////////////////////////////////////////// -template -inline TOctreeCell::TOctreeCell() +template +inline TOctree::CELL_TYPE::CELL_TYPE() : m_child(NULL) { } // constructor -template -inline TOctreeCell::TOctreeCell(TOctreeCell* children) - : - m_child(children) -{ -} // constructor -/*----------------------------------------------------------------*/ - -template -inline TOctreeCell::~TOctreeCell() +template +inline TOctree::CELL_TYPE::~CELL_TYPE() { delete[] m_child; } // destructor /*----------------------------------------------------------------*/ -template -inline void TOctreeCell::Release() +template +inline void TOctree::CELL_TYPE::Release() { delete[] m_child; m_child = NULL; -} // destructor +} // Release // swap the two octrees -template -inline void TOctreeCell::Swap(TOctreeCell& rhs) +template +inline void TOctree::CELL_TYPE::Swap(CELL_TYPE& rhs) { std::swap(m_child, rhs.m_child); uint8_t tmpData[dataSize]; @@ -66,8 +50,8 @@ inline void TOctreeCell::Swap(TOctreeCell& rhs) // compute item's index corresponding to the containing cell -template -inline unsigned TOctreeCell::ComputeChild(const POINT_TYPE& item) const +template +inline unsigned TOctree::CELL_TYPE::ComputeChild(const POINT_TYPE& item) const { ASSERT(!IsLeaf()); unsigned idx = 0; @@ -83,8 +67,8 @@ inline unsigned TOctreeCell::ComputeChild(const POINT_TYPE& } // ComputeChild /*----------------------------------------------------------------*/ -template -void TOctreeCell::ComputeCenter(POINT_TYPE centers[]) +template +void TOctree::CELL_TYPE::ComputeCenter(POINT_TYPE centers[]) { if (DIMS == 1) { centers[0] << -1; @@ -111,8 +95,8 @@ void TOctreeCell::ComputeCenter(POINT_TYPE centers[]) // count the number of items contained by the given octree-cell -template -size_t TOctreeCell::GetNumItemsHeld() const +template +size_t TOctree::CELL_TYPE::GetNumItemsHeld() const { if (IsLeaf()) return GetNumItems(); @@ -128,29 +112,31 @@ size_t TOctreeCell::GetNumItemsHeld() const // S T R U C T S /////////////////////////////////////////////////// // build tree with the given items -template -inline TOctree::TOctree(const ITEMARR_TYPE& items) +template +template +inline TOctree::TOctree(const ITEMARR_TYPE& items, Functor split) { - Insert(items); + Insert(items, split); } -template -inline TOctree::TOctree(const ITEMARR_TYPE& items, const AABB_TYPE& aabb) +template +template +inline TOctree::TOctree(const ITEMARR_TYPE& items, const AABB_TYPE& aabb, Functor split) { - Insert(items, aabb); + Insert(items, aabb, split); } // constructor /*----------------------------------------------------------------*/ // destroy tree -template -inline void TOctree::Release() +template +inline void TOctree::Release() { m_indices.Release(); m_root.Release(); } // Release // swap the two octrees -template -inline void TOctree::Swap(TOctree& rhs) +template +inline void TOctree::Swap(TOctree& rhs) { std::swap(m_items, rhs.m_items); m_indices.Swap(rhs.m_indices); @@ -161,8 +147,8 @@ inline void TOctree::Swap(TOctr // destroy tree -template -inline typename TOctree::POINT_TYPE TOctree::ComputeChildCenter(const POINT_TYPE& center, TYPE radius, unsigned idxChild) +template +inline typename TOctree::POINT_TYPE TOctree::ComputeChildCenter(const POINT_TYPE& center, TYPE radius, unsigned idxChild) { struct CENTERARR_TYPE { POINT_TYPE child[CELL_TYPE::numChildren]; @@ -174,108 +160,107 @@ inline typename TOctree::POINT_ /*----------------------------------------------------------------*/ // add the given item to the tree -template -void TOctree::_Insert(CELL_TYPE& cell, TYPE radius, IDXARR_TYPE childrenIndices[]) -{ - ASSERT(!cell.IsLeaf()); - // setup each cell - const POINT_TYPE& center = cell.Node().center; - const TYPE childRadius = radius / TYPE(2); - for (int i=0; i OCTREE_CELLITEMS && (NOM == 0 || childRadius > OCTREE_CELLRADIUS)) { - // proceed recursively +template +template +void TOctree::_Insert(CELL_TYPE& cell, const POINT_TYPE& center, TYPE radius, IDX_TYPE start, IDX_TYPE size, _InsertData& insertData) +{ + ASSERT(size > 0); + // if this child cell needs to be divided further + if (bForceSplit || insertData.split(size, radius)) { + // init node and proceed recursively + cell.m_child = new CELL_TYPE[CELL_TYPE::numChildren]; + cell.Node().center = center; + struct ChildData { + enum { ESTART=0, EEND=CELL_TYPE::numChildren, ESIZE=CELL_TYPE::numChildren*2, EALL=CELL_TYPE::numChildren*3}; + IDX_TYPE data[EALL]; + ChildData() { memset(data, 0, sizeof(IDX_TYPE)*EALL); } + inline IDX_TYPE Start(unsigned i) const { return data[ESTART+i]; } + inline IDX_TYPE& Start(unsigned i) { return data[ESTART+i]; } + inline IDX_TYPE End(unsigned i) const { return data[EEND+i]; } + inline IDX_TYPE& End(unsigned i) { return data[EEND+i]; } + inline IDX_TYPE Size(unsigned i) const { return data[ESIZE+i]; } + inline IDX_TYPE& Size(unsigned i) { return data[ESIZE+i]; } + } childD; + IDX_TYPE idx(start); + for (IDX_TYPE i=0; i::NO_INDEX); + const TYPE childRadius(radius / TYPE(2)); + for (unsigned i=0; i::NO_INDEX; // mark the end of child successors const POINT_TYPE childCenter(ComputeChildCenter(center, childRadius, i)); - _Insert(child, childCenter, childRadius, childIndices); - } else { - // init leaf - typename CELL_TYPE::LEAF_TYPE& leaf = child.Leaf(); - leaf.idxBegin = m_indices.GetSize(); - leaf.size = (typename CELL_TYPE::SIZE_TYPE)childIndices.GetSize(); - // store items - m_indices.Join(childIndices); - childIndices.Release(); + _Insert(child, childCenter, childRadius, childD.Start(i), childD.Size(i), insertData); } + } else { + // init leaf + cell.Leaf().idxBegin = m_indices.size(); + cell.Leaf().size = (SIZE_TYPE)size; + for (IDX_TYPE idx=start; idx!=_InsertData::NO_INDEX; idx=insertData.successors[idx]) + m_indices.push_back(idx); } } // _Insert -template -void TOctree::_Insert(CELL_TYPE& cell, const POINT_TYPE& center, TYPE radius, IDXARR_TYPE& indices) -{ - ASSERT(cell.IsLeaf()); - ASSERT(indices.GetSize() > OCTREE_CELLITEMS); - ASSERT(NOM == 0 || radius > OCTREE_CELLRADIUS); - // divide cell - // transform this cell in node - cell.m_child = new CELL_TYPE[CELL_TYPE::numChildren]; - cell.Node().center = center; - // divide the items in separate indices lists corresponding to each child - const size_t reserveSize = indices.GetSize() / 3; - IDXARR_TYPE childrenIndices[CELL_TYPE::numChildren]; - for (int i=0; i -inline void TOctree::Insert(const ITEMARR_TYPE& items, const AABB_TYPE& aabb) +template +template +inline void TOctree::Insert(const ITEMARR_TYPE& items, const AABB_TYPE& aabb, Functor split) { Release(); m_items = items.Begin(); - m_radius = GetRadius(aabb); - // build tree // create root as node, even if we do not need to divide m_indices.Reserve(items.GetSize()); // divide cell m_root.m_child = new CELL_TYPE[CELL_TYPE::numChildren]; m_root.Node().center = aabb.GetCenter(); - // divide the items in separate indices lists corresponding to each child - const size_t reserveSize = items.GetSize() / 3; - IDXARR_TYPE childrenIndices[CELL_TYPE::numChildren]; - for (int i=0; i insertData = {items.size(), split}; + std::iota(insertData.successors.begin(), insertData.successors.end(), IDX_TYPE(1)); + insertData.successors.back() = _InsertData::NO_INDEX; // setup each cell - _Insert(m_root, m_radius, childrenIndices); + _Insert(m_root, m_root.GetCenter(), m_radius, 0, items.GetSize(), insertData); } -template -inline void TOctree::Insert(const ITEMARR_TYPE& items) +template +template +inline void TOctree::Insert(const ITEMARR_TYPE& items, Functor split) { ASSERT(!items.IsEmpty()); - AABB_TYPE aabb; ASSERT(sizeof(POINT_TYPE) == sizeof(typename ITEMARR_TYPE::Type)); - aabb.Set((const POINT_TYPE*)items.Begin(), items.GetSize()); + AABB_TYPE aabb((const POINT_TYPE*)items.data(), items.size()); aabb.Enlarge(ZEROTOLERANCE()*TYPE(10)); - Insert(items, aabb); + Insert(items, aabb, split); } // Insert /*----------------------------------------------------------------*/ -template +template template -void TOctree::_CollectCells(const CELL_TYPE& cell, INSERTER& inserter) const +void TOctree::CollectCells(const CELL_TYPE& cell, INSERTER& inserter) const { if (cell.IsLeaf()) { - inserter(m_indices.Begin()+cell.Leaf().idxBegin, cell.GetNumItems()); + inserter(m_indices.Begin()+cell.GetFirstItemIdx(), cell.GetNumItems()); return; } for (int i=0; i +template template -void TOctree::_ParseCells(CELL_TYPE& cell, TYPE radius, PARSER& parser) +void TOctree::_ParseCells(CELL_TYPE& cell, TYPE radius, PARSER& parser) { if (cell.IsLeaf()) { parser(cell, radius); @@ -288,16 +273,16 @@ void TOctree::_ParseCells(CELL_ /*----------------------------------------------------------------*/ // calls parser for each leaf of the octree (the IDX_TYPE operator has to be defined) -template +template template -void TOctree::CollectCells(INSERTER& inserter) const +void TOctree::CollectCells(INSERTER& inserter) const { - _CollectCells(m_root, inserter); + CollectCells(m_root, inserter); } // calls parser for each leaf of the octree (the CELL_TYPE operator has to be defined) -template +template template -void TOctree::ParseCells(PARSER& parser) +void TOctree::ParseCells(PARSER& parser) { _ParseCells(m_root, m_radius, parser); } @@ -305,15 +290,15 @@ void TOctree::ParseCells(PARSER // find all items contained by the given bounding box -template +template template -void TOctree::_Collect(const CELL_TYPE& cell, const AABB_TYPE& aabb, INSERTER& inserter) const +void TOctree::_Collect(const CELL_TYPE& cell, const AABB_TYPE& aabb, INSERTER& inserter) const { if (cell.IsLeaf()) { // add all items contained by the bounding-box - for (IDX i=0; i::_Collect(const CE } } // Collect // find all items contained by the cells intersected by the given line -template +template template -void TOctree::_Collect(const CELL_TYPE& cell, TYPE radius, const COLLECTOR& collector, INSERTER& inserter) const +void TOctree::_Collect(const CELL_TYPE& cell, TYPE radius, const COLLECTOR& collector, INSERTER& inserter) const { ASSERT(!cell.IsLeaf()); const TYPE childRadius = radius / TYPE(2); @@ -363,7 +348,7 @@ void TOctree::_Collect(const CE const CELL_TYPE& childCell = cell.m_child[i]; if (childCell.IsLeaf()) { if (collector.Intersects(ComputeChildCenter(cell.GetCenter(), childRadius, i), childRadius)) - inserter(m_indices.Begin()+childCell.Leaf().idxBegin, childCell.GetNumItems()); + inserter(m_indices.Begin()+childCell.GetFirstItemIdx(), childCell.GetNumItems()); } else { if (collector.Intersects(childCell.Node().center, childRadius)) _Collect(childCell, childRadius, collector, inserter); @@ -372,45 +357,45 @@ void TOctree::_Collect(const CE } // Collect /*----------------------------------------------------------------*/ -template +template template -inline void TOctree::Collect(INSERTER& inserter, const AABB_TYPE& aabb) const +inline void TOctree::Collect(INSERTER& inserter, const AABB_TYPE& aabb) const { _Collect(m_root, aabb, inserter); } -template -inline void TOctree::Collect(IDXARR_TYPE& indices, const AABB_TYPE& aabb) const +template +inline void TOctree::Collect(IDXARR_TYPE& indices, const AABB_TYPE& aabb) const { _Collect(m_root, aabb, IndexInserter(indices)); } -template +template template -inline void TOctree::Collect(INSERTER& inserter, const POINT_TYPE& center, TYPE radius) const +inline void TOctree::Collect(INSERTER& inserter, const POINT_TYPE& center, TYPE radius) const { _Collect(m_root, AABB_TYPE(center, radius), inserter); } -template -inline void TOctree::Collect(IDXARR_TYPE& indices, const POINT_TYPE& center, TYPE radius) const +template +inline void TOctree::Collect(IDXARR_TYPE& indices, const POINT_TYPE& center, TYPE radius) const { _Collect(m_root, AABB_TYPE(center, radius), IndexInserter(indices)); } -template +template template -inline void TOctree::Collect(INSERTER& inserter, const COLLECTOR& collector) const +inline void TOctree::Collect(INSERTER& inserter, const COLLECTOR& collector) const { _Collect(m_root, m_radius, collector, inserter); } -template +template template -inline void TOctree::Collect(IDXARR_TYPE& indices, const COLLECTOR& collector) const +inline void TOctree::Collect(IDXARR_TYPE& indices, const COLLECTOR& collector) const { _Collect(m_root, m_radius, collector, IndexInserter(indices)); } -template -inline void TOctree::Collect(IDX_TYPE maxNeighbors, IDXARR_TYPE& indices, const AABB_TYPE& aabb) const +template +inline void TOctree::Collect(IDX_TYPE maxNeighbors, IDXARR_TYPE& indices, const AABB_TYPE& aabb) const { _Collect(m_root, aabb, IndexInserter(indices)); if (indices.GetSize() > maxNeighbors) { @@ -421,17 +406,17 @@ inline void TOctree::Collect(ID const POINT_TYPE center(aabb.GetCenter()); FOREACH(i, indices) { const IDX_TYPE& idx = indices[i]; - const TYPE score(-(center-(const POINT_TYPE)m_items[idx]).squaredNorm()); + const TYPE score(-(center-GetItem(idx)).squaredNorm()); indexscores[i] = ItemIndexScore(idx,score); } indices.Empty(); indexscores.Sort(); - for (IDX i=0; i -inline void TOctree::Collect(IDX_TYPE maxNeighbors, IDXARR_TYPE& indices, const POINT_TYPE& center, TYPE radius) const +template +inline void TOctree::Collect(IDX_TYPE maxNeighbors, IDXARR_TYPE& indices, const POINT_TYPE& center, TYPE radius) const { Collect(maxNeighbors, indices, AABB_TYPE(center, radius)); } // Collect @@ -439,9 +424,9 @@ inline void TOctree::Collect(ID // walk through the tree and collect visible indices -template +template template -void TOctree::_Traverse(const CELL_TYPE& cell, TYPE radius, const TFrustum& frustum, INSERTER& inserter) const +void TOctree::_Traverse(const CELL_TYPE& cell, TYPE radius, const TFrustum& frustum, INSERTER& inserter) const { ASSERT(!cell.IsLeaf()); switch (frustum.Classify(cell.GetAabb(radius))) { @@ -452,7 +437,7 @@ void TOctree::_Traverse(const C if (childCell.IsLeaf()) { const AABB_TYPE childAabb(ComputeChildCenter(cell.GetCenter(), childRadius, i), childRadius); if (frustum.Classify(childAabb) != CULLED) - inserter(m_indices.Begin()+childCell.Leaf().idxBegin, childCell.GetNumItems()); + inserter(m_indices.Begin()+childCell.GetFirstItemIdx(), childCell.GetNumItems()); } else { _Traverse(childCell, childRadius, frustum, inserter); } @@ -460,14 +445,14 @@ void TOctree::_Traverse(const C break; } case VISIBLE: { for (int i=0; i +template template -void TOctree::_TraverseCells(CELL_TYPE& cell, TYPE radius, const TFrustum& frustum, PARSER& parser) +void TOctree::_TraverseCells(CELL_TYPE& cell, TYPE radius, const TFrustum& frustum, PARSER& parser) { ASSERT(!cell.IsLeaf()); switch (frustum.Classify(cell.GetAabb(radius))) { @@ -491,27 +476,27 @@ void TOctree::_TraverseCells(CE } } -template +template template -inline void TOctree::Traverse(const TFrustum& frustum, INSERTER& inserter) const +inline void TOctree::Traverse(const TFrustum& frustum, INSERTER& inserter) const { _Traverse(m_root, m_radius, frustum, inserter); } -template +template template -inline void TOctree::Traverse(const TFrustum& frustum, IDXARR_TYPE& indices) const +inline void TOctree::Traverse(const TFrustum& frustum, IDXARR_TYPE& indices) const { _Traverse(m_root, m_radius, frustum, IndexInserter(indices)); } -template +template template -inline void TOctree::TraverseCells(const TFrustum& frustum, PARSER& parser) +inline void TOctree::TraverseCells(const TFrustum& frustum, PARSER& parser) { _TraverseCells(m_root, m_radius, frustum, parser); } -template +template template -inline void TOctree::TraverseCells(const TFrustum& frustum, CELLPTRARR_TYPE& leaves) +inline void TOctree::TraverseCells(const TFrustum& frustum, CELLPTRARR_TYPE& leaves) { _TraverseCells(m_root, m_radius, frustum, CellInserter(leaves)); } // Traverse @@ -519,8 +504,8 @@ inline void TOctree::TraverseCe #ifndef _RELEASE -template -void TOctree::_GetDebugInfo(const CELL_TYPE& cell, unsigned nDepth, DEBUGINFO& info) const +template +void TOctree::_GetDebugInfo(const CELL_TYPE& cell, unsigned nDepth, DEBUGINFO& info) const { if (cell.IsLeaf()) { if (info.minDepth > nDepth) @@ -537,8 +522,8 @@ void TOctree::_GetDebugInfo(con _GetDebugInfo(cell.m_child[i], nDepth, info); } -template -void TOctree::GetDebugInfo(DEBUGINFO* pInfo, bool bPrintStats) const +template +void TOctree::GetDebugInfo(DEBUGINFO* pInfo, bool bPrintStats) const { DEBUGINFO localInfo; DEBUGINFO& info = (pInfo ? *pInfo : localInfo); @@ -555,8 +540,8 @@ void TOctree::GetDebugInfo(DEBU } // GetDebugInfo /*----------------------------------------------------------------*/ -template -void TOctree::LogDebugInfo(const DEBUGINFO& info) +template +void TOctree::LogDebugInfo(const DEBUGINFO& info) { //VERBOSE("NoItems: %d; Mem %s; MemItems %s; MemStruct %s; AvgMemStruct %.2f%%%%; NoNodes %d; NoLeaf %d; AvgLeaf %.2f%%%%; AvgDepth %.2f; MinDepth %d; MaxDepth %d", VERBOSE("NumItems %d; Mem %s (%s items, %s struct - %.2f%%%%); NumNodes %d (leaves %d - %.2f%%%%); Depth %.2f (%d min, %d max)", @@ -574,7 +559,7 @@ inline bool OctreeTest(unsigned iters, unsigned maxItems=1000, bool bRandom=true srand(bRandom ? (unsigned)time(NULL) : 0); typedef Eigen::Matrix POINT_TYPE; typedef SEACAVE::cList TestArr; - typedef TOctree TestTree; + typedef TOctree TestTree; const TYPE ptMinData[] = {0,0,0}, ptMaxData[] = {640,480,240}; typename TestTree::AABB_TYPE aabb; aabb.Set(Eigen::Map(ptMinData), Eigen::Map(ptMaxData)); @@ -600,7 +585,9 @@ inline bool OctreeTest(unsigned iters, unsigned maxItems=1000, bool bRandom=true if (DIMS > 2) pt(2) = RAND()%ROUND2INT(ptMaxData[2]); const TYPE radius(TYPE(3+RAND()%30)); // build octree and find interest items - TestTree tree(items, aabb); + TestTree tree(items, aabb, [](typename TestTree::IDX_TYPE size, typename TestTree::Type radius) { + return size > 16 && radius > 10; + }); typename TestTree::IDXARR_TYPE indices; tree.Collect(indices, pt, radius); // find interest items by brute force @@ -620,7 +607,7 @@ inline bool OctreeTest(unsigned iters, unsigned maxItems=1000, bool bRandom=true // compare results unsigned nMatches = 0; FOREACH(i, trueIndices) { - const IDX idx = trueIndices[i]; + const typename TestTree::IDX_TYPE idx = trueIndices[i]; FOREACH(j, indices) { if (indices[j] == idx) { ++nMatches; diff --git a/libs/Common/Rotation.h b/libs/Common/Rotation.h index 87761e56e..22d00a356 100644 --- a/libs/Common/Rotation.h +++ b/libs/Common/Rotation.h @@ -280,9 +280,6 @@ class TRMatrixBase : public TMatrix inline TRMatrixBase(const BaseBase& rhs) : Base(rhs) {} template inline TRMatrixBase(const cv::Matx& rhs) : Base(rhs) {} - /** @brief Copy constructor from rotation matrix */ - inline TRMatrixBase(const TRMatrixBase& r); - /** @brief Copy constructor from 3x3 matrix @attention Orthonormality of matrix is enforced automatically! */ inline TRMatrixBase(const Mat& mat); @@ -299,8 +296,6 @@ class TRMatrixBase : public TMatrix /** @brief Initialization with the rotation from roll/pitch/yaw (in rad) */ inline TRMatrixBase(TYPE roll, TYPE pitch, TYPE yaw); - inline ~TRMatrixBase(); - template inline TRMatrixBase& operator = (const cv::Matx& rhs) { BaseBase::operator = (rhs); return *this; } inline TRMatrixBase& operator = (const cv::Mat& rhs) { BaseBase::operator = (rhs); return *this; } #ifdef _USE_EIGEN diff --git a/libs/Common/Rotation.inl b/libs/Common/Rotation.inl index 212d6c075..fb9b4e1d5 100644 --- a/libs/Common/Rotation.inl +++ b/libs/Common/Rotation.inl @@ -521,13 +521,6 @@ inline TRMatrixBase::TRMatrixBase() : Base(Base::IDENTITY) {} -template -inline TRMatrixBase::TRMatrixBase(const TRMatrixBase& r) - : Base(r) -{ - ASSERT(Check(R_CONSTRAINT_ACCURACY)); -} - template inline TRMatrixBase::TRMatrixBase(const Mat& mat) : Base(mat) @@ -559,10 +552,6 @@ inline TRMatrixBase::TRMatrixBase(TYPE roll, TYPE pitch, TYPE yaw) SetXYZ(roll, pitch, yaw); } -template -inline TRMatrixBase::~TRMatrixBase() -{} - template void TRMatrixBase::SetXYZ(TYPE PhiX, TYPE PhiY, TYPE PhiZ) diff --git a/libs/MVS/Mesh.cpp b/libs/MVS/Mesh.cpp index f50997438..b4193f01d 100644 --- a/libs/MVS/Mesh.cpp +++ b/libs/MVS/Mesh.cpp @@ -3419,6 +3419,22 @@ void Mesh::RemoveVertices(VertexIdxArr& vertexRemove, bool bUpdateLists) /*----------------------------------------------------------------*/ + + +// computes the centroid of the given mesh face +Mesh::Vertex Mesh::ComputeCentroid(FIndex idxFace) const +{ + const Face& face = faces[idxFace]; + return (vertices[face[0]] + vertices[face[1]] + vertices[face[2]]) * (Type(1)/Type(3)); +} + +// computes the area of the given mesh face +Mesh::Type Mesh::ComputeArea(FIndex idxFace) const +{ + const Face& face = faces[idxFace]; + return ComputeTriangleArea(vertices[face[0]], vertices[face[1]], vertices[face[2]]); +} + // computes the area of the mesh surface as the sum of the signed areas of its faces REAL Mesh::ComputeArea() const { diff --git a/libs/MVS/Mesh.h b/libs/MVS/Mesh.h index 09da00aed..e8e884fee 100644 --- a/libs/MVS/Mesh.h +++ b/libs/MVS/Mesh.h @@ -82,6 +82,31 @@ class MVS_API Mesh typedef AABB3f Box; + // used to render a mesh + typedef TOctree Octree; + struct FacesInserter { + FaceIdxArr& cameraFaces; + FacesInserter(FaceIdxArr& _cameraFaces) + : cameraFaces(_cameraFaces) {} + inline void operator() (const Octree::IDX_TYPE* indices, Octree::SIZE_TYPE size) { + cameraFaces.Join(indices, size); + } + static void CreateOctree(Octree& octree, const Mesh& mesh) { + VertexArr centroids(mesh.faces.size()); + FOREACH(idx, mesh.faces) + centroids[idx] = mesh.ComputeCentroid(idx); + octree.Insert(centroids, [](Octree::IDX_TYPE size, Octree::Type /*radius*/) { + return size > 32; + }); + #if 0 && !defined(_RELEASE) + Octree::DEBUGINFO_TYPE info; + octree.GetDebugInfo(&info); + Octree::LogDebugInfo(info); + #endif + octree.ResetItems(); + } + }; + public: VertexArr vertices; FaceArr faces; @@ -154,6 +179,8 @@ class MVS_API Mesh return n; } + Vertex ComputeCentroid(FIndex) const; + Type ComputeArea(FIndex) const; REAL ComputeArea() const; REAL ComputeVolume() const; diff --git a/libs/MVS/Scene.h b/libs/MVS/Scene.h index 070e37878..4c564101f 100644 --- a/libs/MVS/Scene.h +++ b/libs/MVS/Scene.h @@ -64,7 +64,7 @@ class MVS_API Scene public: inline Scene(unsigned _nMaxThreads=0) - : nMaxThreads(Thread::getMaxThreads(_nMaxThreads)), obb(true) {} + : obb(true), nMaxThreads(Thread::getMaxThreads(_nMaxThreads)) {} void Release(); bool IsEmpty() const; diff --git a/libs/MVS/SceneDensify.cpp b/libs/MVS/SceneDensify.cpp index 367d3319b..4d07e6e03 100644 --- a/libs/MVS/SceneDensify.cpp +++ b/libs/MVS/SceneDensify.cpp @@ -1881,7 +1881,7 @@ void Scene::PointCloudFilter(int thRemove) { TD_TIMER_STARTD(); - typedef TOctree Octree; + typedef TOctree Octree; struct Collector { typedef Octree::IDX_TYPE IDX; typedef PointCloud::Point::Type Real; @@ -1938,7 +1938,9 @@ void Scene::PointCloudFilter(int thRemove) typedef CLISTDEF2(Collector) Collectors; // create octree to speed-up search - Octree octree(pointcloud.points); + Octree octree(pointcloud.points, [](Octree::IDX_TYPE size, Octree::Type /*radius*/) { + return size > 128; + }); IntArr visibility(pointcloud.GetSize()); visibility.Memset(0); Collectors collectors; collectors.reserve(images.size()); FOREACH(idxView, images) { diff --git a/libs/MVS/SceneRefine.cpp b/libs/MVS/SceneRefine.cpp index 6cca7b809..5d3e04173 100644 --- a/libs/MVS/SceneRefine.cpp +++ b/libs/MVS/SceneRefine.cpp @@ -81,9 +81,6 @@ class MeshRefine { typedef TPoint3 Grad; typedef CLISTDEF0IDX(Grad,VIndex) GradArr; - typedef std::unordered_set CameraFaces; - typedef SEACAVE::cList CameraFacesArr; - typedef TImage FaceMap; typedef TImage BaryMap; @@ -152,7 +149,7 @@ class MeshRefine { static bool IsDepthSimilar(const DepthMap& depthMap, const Point2f& pt, Depth z); static void ProjectMesh( - const Mesh::VertexArr& vertices, const Mesh::FaceArr& faces, const CameraFaces& cameraFaces, + const Mesh::VertexArr& vertices, const Mesh::FaceArr& faces, const Mesh::FaceIdxArr& cameraFaces, const Camera& camera, const Image8U::Size& size, DepthMap& depthMap, FaceMap& faceMap, BaryMap& baryMap); static void ImageMeshWarp( @@ -188,7 +185,7 @@ class MeshRefine { void WaitThreadWorkers(size_t nJobs); void ThSelectNeighbors(uint32_t idxImage, std::unordered_set& mapPairs, unsigned nMaxViews); void ThInitImage(uint32_t idxImage, Real scale, Real sigma); - void ThProjectMesh(uint32_t idxImage, const CameraFaces& cameraFaces); + void ThProjectMesh(uint32_t idxImage, const Mesh::FaceIdxArr& cameraFaces); void ThProcessPair(uint32_t idxImageA, uint32_t idxImageB); void ThSmoothVertices1(VIndex idxStart, VIndex idxEnd); void ThSmoothVertices2(VIndex idxStart, VIndex idxEnd); @@ -297,12 +294,12 @@ class EVTProjectMesh : public Event { public: uint32_t idxImage; - const MeshRefine::CameraFaces& cameraFaces; + const Mesh::FaceIdxArr& cameraFaces; bool Run(void* pArgs) { ((MeshRefine*)pArgs)->ThProjectMesh(idxImage, cameraFaces); return true; } - EVTProjectMesh(uint32_t _idxImage, const MeshRefine::CameraFaces& _cameraFaces) : Event(EVT_JOB), idxImage(_idxImage), cameraFaces(_cameraFaces) {} + EVTProjectMesh(uint32_t _idxImage, const Mesh::FaceIdxArr& _cameraFaces) : Event(EVT_JOB), idxImage(_idxImage), cameraFaces(_cameraFaces) {} }; class EVTProcessPair : public Event { @@ -415,37 +412,18 @@ void MeshRefine::ListVertexFacesPost() void MeshRefine::ListCameraFaces() { // extract array of faces viewed by each camera + typedef SEACAVE::cList CameraFacesArr; CameraFacesArr arrCameraFaces(images.GetSize()); { - struct FacesInserter { - FacesInserter(const Mesh::VertexFacesArr& _vertexFaces, CameraFaces& _cameraFaces) - : vertexFaces(_vertexFaces), cameraFaces(_cameraFaces) {} - inline void operator() (IDX idxVertex) { - const Mesh::FaceIdxArr& vertexTris = vertexFaces[idxVertex]; - FOREACHPTR(pTri, vertexTris) - cameraFaces.emplace(*pTri); - } - inline void operator() (const IDX* idices, size_t size) { - FOREACHRAWPTR(pIdxVertex, idices, size) - operator()(*pIdxVertex); - } - const Mesh::VertexFacesArr& vertexFaces; - CameraFaces& cameraFaces; - }; - typedef TOctree Octree; - const Octree octree(vertices); - #if 0 && !defined(_RELEASE) - Octree::DEBUGINFO_TYPE info; - octree.GetDebugInfo(&info); - Octree::LogDebugInfo(info); - #endif + Mesh::Octree octree; + Mesh::FacesInserter::CreateOctree(octree, scene.mesh); FOREACH(ID, images) { const Image& imageData = images[ID]; if (!imageData.IsValid()) continue; typedef TFrustum Frustum; - FacesInserter inserter(vertexFaces, arrCameraFaces[ID]); const Frustum frustum(Frustum::MATRIX3x4(((PMatrix::CEMatMap)imageData.camera.P).cast()), (float)imageData.width, (float)imageData.height); + Mesh::FacesInserter inserter(arrCameraFaces[ID]); octree.Traverse(frustum, inserter); } } @@ -749,7 +727,7 @@ bool MeshRefine::IsDepthSimilar(const DepthMap& depthMap, const Point2f& pt, Dep // project mesh to the given camera plane void MeshRefine::ProjectMesh( - const Mesh::VertexArr& vertices, const Mesh::FaceArr& faces, const CameraFaces& cameraFaces, + const Mesh::VertexArr& vertices, const Mesh::FaceArr& faces, const Mesh::FaceIdxArr& cameraFaces, const Camera& camera, const Image8U::Size& size, DepthMap& depthMap, FaceMap& faceMap, BaryMap& baryMap) { @@ -1123,7 +1101,7 @@ void MeshRefine::ThInitImage(uint32_t idxImage, Real scale, Real sigma) #endif cv::merge(grad, 2, view.imageGrad); } -void MeshRefine::ThProjectMesh(uint32_t idxImage, const CameraFaces& cameraFaces) +void MeshRefine::ThProjectMesh(uint32_t idxImage, const Mesh::FaceIdxArr& cameraFaces) { const Image& imageData = images[idxImage]; if (!imageData.IsValid()) diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index 400616c5c..2bd975d1d 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -477,33 +477,12 @@ void MeshTexture::ListVertexFaces() // extract array of faces viewed by each image bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThreshold) { - // create vertices octree - facesDatas.Resize(faces.GetSize()); - typedef std::unordered_set CameraFaces; - struct FacesInserter { - FacesInserter(const Mesh::VertexFacesArr& _vertexFaces, CameraFaces& _cameraFaces) - : vertexFaces(_vertexFaces), cameraFaces(_cameraFaces) {} - inline void operator() (IDX idxVertex) { - const Mesh::FaceIdxArr& vertexTris = vertexFaces[idxVertex]; - FOREACHPTR(pTri, vertexTris) - cameraFaces.emplace(*pTri); - } - inline void operator() (const IDX* idices, size_t size) { - FOREACHRAWPTR(pIdxVertex, idices, size) - operator()(*pIdxVertex); - } - const Mesh::VertexFacesArr& vertexFaces; - CameraFaces& cameraFaces; - }; - typedef TOctree Octree; - const Octree octree(vertices); - #if 0 && !defined(_RELEASE) - Octree::DEBUGINFO_TYPE info; - octree.GetDebugInfo(&info); - Octree::LogDebugInfo(info); - #endif + // create faces octree + Mesh::Octree octree; + Mesh::FacesInserter::CreateOctree(octree, scene.mesh); // extract array of faces viewed by each image + facesDatas.Resize(faces.GetSize()); Util::Progress progress(_T("Initialized views"), images.GetSize()); typedef float real; TImage imageGradMag; @@ -562,8 +541,8 @@ bool MeshTexture::ListCameraFaces(FaceDataViewArr& facesDatas, float fOutlierThr #endif (TImage::EMatMap)imageGradMag = (mGrad[0].cwiseAbs2()+mGrad[1].cwiseAbs2()).cwiseSqrt(); // select faces inside view frustum - CameraFaces cameraFaces; - FacesInserter inserter(vertexFaces, cameraFaces); + Mesh::FaceIdxArr cameraFaces; + Mesh::FacesInserter inserter(cameraFaces); typedef TFrustum Frustum; const Frustum frustum(Frustum::MATRIX3x4(((PMatrix::CEMatMap)imageData.camera.P).cast()), (float)imageData.width, (float)imageData.height); octree.Traverse(frustum, inserter); From 7b55ad19699bd746eea06acbd35ea9655906aa70 Mon Sep 17 00:00:00 2001 From: cDc Date: Mon, 15 Feb 2021 23:13:02 +0200 Subject: [PATCH 016/252] build: update AppVeyor --- .appveyor.yml | 7 +++---- BUILD.md | 6 +++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 84d82a06f..ca38cbeee 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -48,18 +48,17 @@ for: # remove outdated versions - vcpkg remove --outdated --recurse # install dependencies - - vcpkg install --recurse --triplet %VCPKG_ARCH% zlib boost-iostreams boost-program-options boost-system boost-serialization eigen3 cgal[core] opencv glew glfw3 - - vcpkg integrate install + - vcpkg install --recurse yasm-tool:x86-windows + - vcpkg install --recurse --triplet %VCPKG_ARCH% zlib boost-iostreams boost-program-options boost-system boost-serialization eigen3 cgal[core] opencv vcglib glew glfw3 - cd "%APPVEYOR_BUILD_FOLDER%" # preserve contents of selected directories and files across project builds cache: - 'C:\tools\vcpkg\installed' build_script: - - git clone https://github.com/cdcseacave/VCG.git - if "%platform%"=="Win32" set CMAKE_GENERATOR=-G"Visual Studio 15 2017" - if "%platform%"=="x64" set CMAKE_GENERATOR=-G"Visual Studio 15 2017 Win64" - mkdir bin && cd bin - - cmake %CMAKE_GENERATOR% -DCMAKE_BUILD_TYPE=%Configuration% -DCMAKE_TOOLCHAIN_FILE="C:\tools\vcpkg\scripts\buildsystems\vcpkg.cmake" -DVCG_ROOT="%APPVEYOR_BUILD_FOLDER%\VCG" .. + - cmake %CMAKE_GENERATOR% -DCMAKE_BUILD_TYPE=%Configuration% -DCMAKE_TOOLCHAIN_FILE="C:\tools\vcpkg\scripts\buildsystems\vcpkg.cmake" .. - cmake --build . --target ALL_BUILD --config %Configuration% -- /maxcpucount:4 - #------------------ diff --git a/BUILD.md b/BUILD.md index f6295c723..75aaac1e4 100644 --- a/BUILD.md +++ b/BUILD.md @@ -3,7 +3,7 @@ Dependencies ------------ *OpenMVS* relies on a number of open source libraries, some of which are optional. For details on customizing the build process, see the compilation instructions. -* [Eigen](http://eigen.tuxfamily.org) version 3.2 (or higher on Windows only) +* [Eigen](http://eigen.tuxfamily.org) version 3.3.9 or higher * [OpenCV](http://opencv.org) version 2.4 or higher * [Ceres](http://ceres-solver.org) version 1.10 or higher * [CGAL](http://www.cgal.org) version 4.2 or higher @@ -33,7 +33,7 @@ cd OpenMVS #Get and install dependencies using vcpkg; #choose the desired triplet, like "x64-windows", by setting the VCPKG_DEFAULT_TRIPLET environment variable or by specifying it after each package: -vcpkg install zlib boost-iostreams boost-program-options boost-system boost-serialization eigen3 cgal[core] opencv glew glfw3 +vcpkg install zlib boost-iostreams boost-program-options boost-system boost-serialization eigen3 cgal[core] opencv vcglib glew glfw3 #Get VCGLib (Required): git clone https://github.com/cdcseacave/VCG.git @@ -46,7 +46,7 @@ mkdir build cd build #Run CMake, where VCPKG_ROOT environment variable points to the root of vcpkg installation: -cmake . ..\src -G "Visual Studio 15 2017 Win64" -DCMAKE_TOOLCHAIN_FILE=%VCPKG_ROOT%\scripts\buildsystems\vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows -DVCG_ROOT="..\VCG" +cmake . ..\src -G "Visual Studio 15 2017 Win64" -DCMAKE_TOOLCHAIN_FILE=%VCPKG_ROOT%\scripts\buildsystems\vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows #Open the solution in MSVC and build it ``` From 37f4b1eaff879aa051be59ef4bd0d355efdfb682 Mon Sep 17 00:00:00 2001 From: cDc Date: Sat, 20 Feb 2021 18:57:38 +0200 Subject: [PATCH 017/252] mesh: split mesh in sub-meshed by max area --- apps/ReconstructMesh/ReconstructMesh.cpp | 20 +- libs/Common/Octree.h | 19 +- libs/Common/Octree.inl | 270 ++++++++++++++++++++--- libs/MVS/Mesh.cpp | 104 ++++++++- libs/MVS/Mesh.h | 11 + 5 files changed, 385 insertions(+), 39 deletions(-) diff --git a/apps/ReconstructMesh/ReconstructMesh.cpp b/apps/ReconstructMesh/ReconstructMesh.cpp index 8651715e0..edd5b4cd2 100644 --- a/apps/ReconstructMesh/ReconstructMesh.cpp +++ b/apps/ReconstructMesh/ReconstructMesh.cpp @@ -63,6 +63,7 @@ float fRemoveSpurious; bool bRemoveSpikes; unsigned nCloseHoles; unsigned nSmoothMesh; +float fSplitMaxArea; unsigned nArchiveType; int nProcessPriority; unsigned nMaxThreads; @@ -124,7 +125,8 @@ bool Initialize(size_t argc, LPCTSTR* argv) boost::program_options::options_description hidden("Hidden options"); hidden.add_options() ("mesh-file", boost::program_options::value(&OPT::strMeshFileName), "mesh file name to clean (skips the reconstruction step)") - ("mesh-export", boost::program_options::value(&OPT::bMeshExport)->default_value(false), "just export the mesh contained in loaded project") + ("mesh-export", boost::program_options::value(&OPT::bMeshExport)->default_value(false), "just export the mesh contained in loaded project") + ("split-max-area", boost::program_options::value(&OPT::fSplitMaxArea)->default_value(0.f), "maximum surface area that a sub-mesh can contain (0 - disabled)") ; boost::program_options::options_description cmdline_options; @@ -219,8 +221,17 @@ int main(int argc, LPCTSTR* argv) Scene scene(OPT::nMaxThreads); // load project - if (!scene.Load(MAKE_PATH_SAFE(OPT::strInputFileName))) + if (!scene.Load(MAKE_PATH_SAFE(OPT::strInputFileName), OPT::fSplitMaxArea > 0)) return EXIT_FAILURE; + const String baseFileName(MAKE_PATH_SAFE(Util::getFileFullName(OPT::strOutputFileName))); + if (OPT::fSplitMaxArea > 0) { + // split mesh using max-area constraint + Mesh::FacesChunkArr chunks; + if (scene.mesh.Split(chunks, OPT::fSplitMaxArea)) + scene.mesh.Save(chunks, baseFileName); + Finalize(); + return EXIT_SUCCESS; + } if (OPT::bMeshExport) { // check there is a mesh to export if (scene.mesh.IsEmpty()) @@ -230,7 +241,7 @@ int main(int argc, LPCTSTR* argv) scene.mesh.Save(fileName); #if TD_VERBOSE != TD_VERBOSE_OFF if (VERBOSITY_LEVEL > 2) - scene.ExportCamerasMLP(Util::getFileFullName(OPT::strOutputFileName)+_T(".mlp"), fileName); + scene.ExportCamerasMLP(baseFileName+_T(".mlp"), fileName); #endif } else { if (OPT::strMeshFileName.IsEmpty()) { @@ -281,7 +292,7 @@ int main(int argc, LPCTSTR* argv) #if TD_VERBOSE != TD_VERBOSE_OFF if (VERBOSITY_LEVEL > 2) { // dump raw mesh - scene.mesh.Save(MAKE_PATH_SAFE(Util::getFileFullName(OPT::strOutputFileName))+_T("_raw")+OPT::strExportType); + scene.mesh.Save(baseFileName+_T("_raw")+OPT::strExportType); } #endif } else { @@ -295,7 +306,6 @@ int main(int argc, LPCTSTR* argv) scene.mesh.Clean(1.f, 0.f, false, 0, 0, true); // extra cleaning to remove non-manifold problems created by closing holes // save the final mesh - const String baseFileName(MAKE_PATH_SAFE(Util::getFileFullName(OPT::strOutputFileName))); scene.Save(baseFileName+_T(".mvs"), (ARCHIVE_TYPE)OPT::nArchiveType); scene.mesh.Save(baseFileName+OPT::strExportType); #if TD_VERBOSE != TD_VERBOSE_OFF diff --git a/libs/Common/Octree.h b/libs/Common/Octree.h index 568b87ca6..70acd14c6 100644 --- a/libs/Common/Octree.h +++ b/libs/Common/Octree.h @@ -69,15 +69,17 @@ class TOctree inline unsigned ComputeChild(const POINT_TYPE& item) const; static void ComputeCenter(POINT_TYPE []); + static inline POINT_TYPE ComputeChildCenter(const POINT_TYPE&, TYPE, unsigned); inline bool IsLeaf() const { return (m_child==NULL); } inline const CELL_TYPE& GetChild(int i) const { ASSERT(!IsLeaf() && i(m_data); } + inline NODE_TYPE& Node() { ASSERT(!IsLeaf()); return *reinterpret_cast(m_data); } + inline const LEAF_TYPE& Leaf() const { ASSERT(IsLeaf()); return *reinterpret_cast(m_data); } + inline LEAF_TYPE& Leaf() { ASSERT(IsLeaf()); return *reinterpret_cast(m_data); } inline const POINT_TYPE& GetCenter() const { return Node().center; } inline AABB_TYPE GetAabb(TYPE radius) const { return AABB_TYPE(Node().center, radius); } + inline AABB_TYPE GetChildAabb(unsigned idxChild, TYPE radius) const { const TYPE childRadius(radius / TYPE(2)); return AABB_TYPE(ComputeChildCenter(Node().center, childRadius, idxChild), childRadius); } inline IDX_TYPE GetFirstItemIdx() const { return Leaf().idxBegin; } inline IDX_TYPE GetLastItemIdx() const { return Leaf().idxBegin + Leaf().size; } inline SIZE_TYPE GetNumItems() const { return Leaf().size; } @@ -150,7 +152,11 @@ class TOctree template inline void TraverseCells(const TFrustum&, CELLPTRARR_TYPE&); + template + void SplitVolume(float maxArea, AREAESTIMATOR& areaEstimator, CHUNKINSERTER& chunkInserter); + inline const CELL_TYPE& GetRoot() const { return m_root; } + inline TYPE GetRadius() const { return m_radius; } inline AABB_TYPE GetAabb() const { return m_root.GetAabb(m_radius); } inline bool IsEmpty() const { return m_indices.IsEmpty(); } inline size_t GetNumItems() const { return m_indices.GetSize(); } @@ -160,8 +166,6 @@ class TOctree inline void ResetItems() { m_items = NULL; } protected: - static inline POINT_TYPE ComputeChildCenter(const POINT_TYPE&, TYPE, unsigned); - template struct _InsertData { enum : IDX_TYPE { NO_INDEX = DECLARE_NO_INDEX(IDX_TYPE) }; @@ -184,6 +188,9 @@ class TOctree template void _TraverseCells(CELL_TYPE&, TYPE, const TFrustum&, PARSER&); + template + void _SplitVolume(const CELL_TYPE& parentCell, TYPE parentRadius, unsigned idxChild, float maxArea, AREAESTIMATOR& areaEstimator, CHUNKINSERTER& chunkInserter, const UnsignedArr& indices=UnsignedArr{0,1,2,3,4,5,6,7}); + protected: const ITEM_TYPE* m_items; // original input items (the only condition is that every item to resolve to a position) IDXARR_TYPE m_indices; // indices to input items re-arranged spatially (as dictated by the octree) diff --git a/libs/Common/Octree.inl b/libs/Common/Octree.inl index ce5ea80f8..7a3026e25 100644 --- a/libs/Common/Octree.inl +++ b/libs/Common/Octree.inl @@ -93,6 +93,18 @@ void TOctree::CELL_TYPE::ComputeCenter(POINT_T } // ComputeCenter /*----------------------------------------------------------------*/ +template +inline typename TOctree::POINT_TYPE TOctree::CELL_TYPE::ComputeChildCenter(const POINT_TYPE& center, TYPE radius, unsigned idxChild) +{ + struct CENTERARR_TYPE { + POINT_TYPE child[CELL_TYPE::numChildren]; + inline CENTERARR_TYPE() { CELL_TYPE::ComputeCenter(child); } + }; + static const CENTERARR_TYPE centers; + return center + centers.child[idxChild] * radius; +} // ComputeChildCenter +/*----------------------------------------------------------------*/ + // count the number of items contained by the given octree-cell template @@ -146,19 +158,6 @@ inline void TOctree::Swap(TOctree& rhs) /*----------------------------------------------------------------*/ -// destroy tree -template -inline typename TOctree::POINT_TYPE TOctree::ComputeChildCenter(const POINT_TYPE& center, TYPE radius, unsigned idxChild) -{ - struct CENTERARR_TYPE { - POINT_TYPE child[CELL_TYPE::numChildren]; - inline CENTERARR_TYPE() { CELL_TYPE::ComputeCenter(child); } - }; - static const CENTERARR_TYPE centers; - return center + centers.child[idxChild] * radius; -} // ComputeChildCenter -/*----------------------------------------------------------------*/ - // add the given item to the tree template template @@ -202,7 +201,7 @@ void TOctree::_Insert(CELL_TYPE& cell, const P continue; } insertData.successors[childD.End(i)] = _InsertData::NO_INDEX; // mark the end of child successors - const POINT_TYPE childCenter(ComputeChildCenter(center, childRadius, i)); + const POINT_TYPE childCenter(CELL_TYPE::ComputeChildCenter(center, childRadius, i)); _Insert(child, childCenter, childRadius, childD.Start(i), childD.Size(i), insertData); } } else { @@ -220,9 +219,9 @@ template inline void TOctree::Insert(const ITEMARR_TYPE& items, const AABB_TYPE& aabb, Functor split) { Release(); - m_items = items.Begin(); + m_items = items.data(); // create root as node, even if we do not need to divide - m_indices.Reserve(items.GetSize()); + m_indices.Reserve(items.size()); // divide cell m_root.m_child = new CELL_TYPE[CELL_TYPE::numChildren]; m_root.Node().center = aabb.GetCenter(); @@ -232,7 +231,7 @@ inline void TOctree::Insert(const ITEMARR_TYPE std::iota(insertData.successors.begin(), insertData.successors.end(), IDX_TYPE(1)); insertData.successors.back() = _InsertData::NO_INDEX; // setup each cell - _Insert(m_root, m_root.GetCenter(), m_radius, 0, items.GetSize(), insertData); + _Insert(m_root, m_root.GetCenter(), m_radius, 0, items.size(), insertData); } template template @@ -252,7 +251,7 @@ template void TOctree::CollectCells(const CELL_TYPE& cell, INSERTER& inserter) const { if (cell.IsLeaf()) { - inserter(m_indices.Begin()+cell.GetFirstItemIdx(), cell.GetNumItems()); + inserter(m_indices.data()+cell.GetFirstItemIdx(), cell.GetNumItems()); return; } for (int i=0; i::_Collect(const CELL_TYPE& cell, for (int i=0; i inline void TOctree::Collect(IDX_TYPE maxNeighbors, IDXARR_TYPE& indices, const AABB_TYPE& aabb) const { _Collect(m_root, aabb, IndexInserter(indices)); - if (indices.GetSize() > maxNeighbors) { + if (indices.size() > maxNeighbors) { // keep only the closest neighbors typedef TIndexScore ItemIndexScore; typedef cList ItemIndexScoreArr; - ItemIndexScoreArr indexscores(indices.GetSize()); + ItemIndexScoreArr indexscores(indices.size()); const POINT_TYPE center(aabb.GetCenter()); FOREACH(i, indices) { const IDX_TYPE& idx = indices[i]; @@ -435,9 +434,9 @@ void TOctree::_Traverse(const CELL_TYPE& cell, for (int i=0; i::_TraverseCells(CELL_TYPE& cell, for (int i=0; i::TraverseCells(const TFrus } // Traverse /*----------------------------------------------------------------*/ +template +template +void TOctree::_SplitVolume(const CELL_TYPE& parentCell, TYPE parentRadius, unsigned idxChild, float maxArea, AREAESTIMATOR& areaEstimator, CHUNKINSERTER& chunkInserter, const UnsignedArr& indices) +{ + ASSERT(!indices.empty()); + typedef std::pair PairIndices; + struct GenerateSamples { + const UnsignedArr& indices; + const unsigned numSamples; + const unsigned halfSamples; + const unsigned numCommonAxis; + POINT_TYPE centers[8]; + cList arrHalfIndices; + GenerateSamples(const UnsignedArr& _indices) + : indices(_indices), numSamples((unsigned)indices.size()), halfSamples((unsigned)indices.size()/2), arrHalfIndices(0, numSamples), numCommonAxis(halfSamples==4?1:2) + { + ASSERT(indices.size()%2 == 0 && indices.IsSorted()); + ASSERT(halfSamples == 4 || halfSamples == 2); + CELL_TYPE::ComputeCenter(centers); + UnsignedArr samples(halfSamples); + for (unsigned hs=0; hs= maxArea) + ++numOverAreas; + if (numOverAreas == indices.size()) { + for (unsigned c: indices) + if (childArea[c] > 0) + _SplitVolume(cell, radius, c, maxArea, areaEstimator, chunkInserter); + return; + } + // split mesh children and retain the components with surface smaller than the given area + const cList halfIndices(std::move(GenerateSamples(indices).arrHalfIndices)); + IDX bestSplit(NO_ID); + float bestArea(0); + Point2f bestAs; + FOREACH(idx, halfIndices) { + const PairIndices& pairHalfIndices = halfIndices[idx]; + ASSERT(pairHalfIndices.first.size() == pairHalfIndices.second.size()); + Point2f as(Point2f::ZERO); + for (unsigned i=0; i qIndicesFirst(std::move(GenerateSamples(pairHalfIndices.first).arrHalfIndices)); + const cList qIndicesSecond(std::move(GenerateSamples(pairHalfIndices.second).arrHalfIndices)); + ASSERT(qIndicesFirst.size() == qIndicesSecond.size()); + FOREACH(q, qIndicesFirst) { + const PairIndices& qFirst = qIndicesFirst[q]; + const PairIndices& qSecond = qIndicesSecond[q]; + Eigen::Vector4f as(Eigen::Vector4f::Zero()); + for (unsigned i=0; i 0) { + // store found clusters + for (unsigned i=0; i<4; ++i) { + if (bestAs[i] < maxArea) { + chunkInserter(cell, radius, bestQIndices[i]); + } else { + _SplitVolume(cell, radius, bestQIndices[i][0], maxArea, areaEstimator, chunkInserter); + _SplitVolume(cell, radius, bestQIndices[i][1], maxArea, areaEstimator, chunkInserter); + } + } + return; + } + } + // split each child + for (unsigned c: indices) { + if (childArea[c] == 0) + continue; + if (childArea[c] < maxArea) + chunkInserter(cell, radius, UnsignedArr{c}); + else + _SplitVolume(cell, radius, c, maxArea, areaEstimator, chunkInserter); + } +} +template +template +void TOctree::SplitVolume(float maxArea, AREAESTIMATOR& areaEstimator, CHUNKINSERTER& chunkInserter) +{ + CELL_TYPE parent; + parent.m_child = new CELL_TYPE[1]; + parent.m_child[0] = m_root; + parent.Node().center = m_root.Node().center + POINT_TYPE::Constant(m_radius); + _SplitVolume(parent, m_radius*TYPE(2), 0, maxArea, areaEstimator, chunkInserter); + parent.m_child[0].m_child = NULL; +} // SplitVolume +/*----------------------------------------------------------------*/ + #ifndef _RELEASE template @@ -616,8 +832,8 @@ inline bool OctreeTest(unsigned iters, unsigned maxItems=1000, bool bRandom=true } } nTotalMatches += nMatches; - nTotalMissed += trueIndices.GetSize()-nMatches; - nTotalExtra += indices.GetSize()-nMatches; + nTotalMissed += trueIndices.size()-nMatches; + nTotalExtra += indices.size()-nMatches; #ifndef _RELEASE // print stats typename TestTree::DEBUGINFO_TYPE info; diff --git a/libs/MVS/Mesh.cpp b/libs/MVS/Mesh.cpp index b4193f01d..111de846f 100644 --- a/libs/MVS/Mesh.cpp +++ b/libs/MVS/Mesh.cpp @@ -54,6 +54,7 @@ #include #include #include +#undef Split #ifdef _MSC_VER # pragma warning(pop) #endif @@ -1575,6 +1576,18 @@ bool Mesh::SaveOBJ(const String& fileName) const } // Save /*----------------------------------------------------------------*/ +bool Mesh::Save(const FacesChunkArr& chunks, const String& fileName, const cList& comments, bool bBinary) const +{ + if (chunks.size() < 2) + return Save(fileName, comments, bBinary); + FOREACH(i, chunks) { + const Mesh mesh(SubMesh(chunks[i].faces)); + if (!mesh.Save(Util::insertBeforeFileExt(fileName, String::FormatString("_chunk%02u", i)), comments, bBinary)) + return false; + } + return true; +} + bool Mesh::Save(const VertexArr& vertices, const String& fileName, bool bBinary) { ASSERT(!fileName.IsEmpty()); @@ -3367,7 +3380,6 @@ void Mesh::RemoveFaces(FaceIdxArr& facesRemove, bool bUpdateLists) } vertexVertices.Release(); } -/*----------------------------------------------------------------*/ // remove the given list of vertices void Mesh::RemoveVertices(VertexIdxArr& vertexRemove, bool bUpdateLists) @@ -3416,6 +3428,22 @@ void Mesh::RemoveVertices(VertexIdxArr& vertexRemove, bool bUpdateLists) } RemoveFaces(facesRemove); } + +// remove all vertices that are not assigned to any face +// (require vertexFaces) +Mesh::VIndex Mesh::RemoveUnreferencedVertices(bool bUpdateLists) +{ + ASSERT(vertices.size() == vertexFaces.size()); + VertexIdxArr vertexRemove; + FOREACH(idxV, vertexFaces) { + if (vertexFaces[idxV].empty()) + vertexRemove.push_back(idxV); + } + if (vertexRemove.empty()) + return 0; + RemoveVertices(vertexRemove, bUpdateLists); + return vertexRemove.size(); +} /*----------------------------------------------------------------*/ @@ -3716,6 +3744,80 @@ void Mesh::ProjectOrthoTopDown(unsigned resolution, Image8U3& image, Image8U& ma /*----------------------------------------------------------------*/ +// split mesh into sub-meshes such that each has maxArea +bool Mesh::Split(FacesChunkArr& chunks, float maxArea) +{ + TD_TIMER_STARTD(); + Octree octree; + FacesInserter::CreateOctree(octree, *this); + FloatArr areas(faces.size()); + FOREACH(i, faces) + areas[i] = ComputeArea(i); + struct AreaInserter { + const FloatArr& areas; + float area; + inline void operator() (const Octree::IDX_TYPE* indices, Octree::SIZE_TYPE size) { + FOREACHRAWPTR(pIdx, indices, size) + area += areas[*pIdx]; + } + inline float PopArea() { + const float a(area); + area = 0; + return a; + } + } areaEstimator{areas, 0.f}; + struct ChunkInserter { + const Octree& octree; + FacesChunkArr& chunks; + void operator() (const Octree::CELL_TYPE& parentCell, Octree::Type parentRadius, const UnsignedArr& children) { + ASSERT(!children.empty()); + FaceChunk& chunk = chunks.AddEmpty(); + struct Inserter { + FaceIdxArr& faces; + inline void operator() (const Octree::IDX_TYPE* indices, Octree::SIZE_TYPE size) { + faces.Join(indices, size); + } + } inserter{chunk.faces}; + if (children.size() == 1) { + octree.CollectCells(parentCell.GetChild(children.front()), inserter); + chunk.box = parentCell.GetChildAabb(children.front(), parentRadius); + } else { + chunk.box.Reset(); + for (unsigned c: children) { + octree.CollectCells(parentCell.GetChild(c), inserter); + chunk.box.Insert(parentCell.GetChildAabb(c, parentRadius)); + } + } + if (chunk.faces.empty()) + chunks.RemoveLast(); + } + } chunkInserter{octree, chunks}; + octree.SplitVolume(maxArea, areaEstimator, chunkInserter); + if (chunks.size() < 2) + return false; + DEBUG_EXTRA("Mesh split (%g max-area): %u chunks (%s)", maxArea, chunks.size(), TD_TIMER_GET_FMT().c_str()); + return true; +} // Split +/*----------------------------------------------------------------*/ + +// extract the sub-mesh corresponding to the given chunk of faces +Mesh Mesh::SubMesh(const FaceIdxArr& chunk) const +{ + ASSERT(!chunk.empty()); + Mesh mesh; + mesh.vertices = vertices; + mesh.faces.reserve(chunk.size()); + for (FIndex idxFace: chunk) + mesh.faces.emplace_back(faces[idxFace]); + mesh.ListIncidenteFaces(); + mesh.RemoveUnreferencedVertices(); + // fix non-manifold vertices and edges + mesh.FixNonManifold(); + return mesh; +} // SubMesh +/*----------------------------------------------------------------*/ + + #ifdef _USE_CUDA CUDA::KernelRT Mesh::kernelComputeFaceNormal; diff --git a/libs/MVS/Mesh.h b/libs/MVS/Mesh.h index e8e884fee..94a0ec204 100644 --- a/libs/MVS/Mesh.h +++ b/libs/MVS/Mesh.h @@ -107,6 +107,12 @@ class MVS_API Mesh } }; + struct FaceChunk { + FaceIdxArr faces; + Box box; + }; + typedef cList FacesChunkArr; + public: VertexArr vertices; FaceArr faces; @@ -165,6 +171,7 @@ class MVS_API Mesh void CloseHoleQuality(VertexIdxArr& vertsLoop); void RemoveFaces(FaceIdxArr& facesRemove, bool bUpdateLists=false); void RemoveVertices(VertexIdxArr& vertexRemove, bool bUpdateLists=false); + VIndex RemoveUnreferencedVertices(bool bUpdateLists=false); inline Normal FaceNormal(const Face& f) const { return ComputeTriangleNormal(vertices[f[0]], vertices[f[1]], vertices[f[2]]); @@ -194,9 +201,13 @@ class MVS_API Mesh void ProjectOrtho(const Camera& camera, DepthMap& depthMap, Image8U3& image) const; void ProjectOrthoTopDown(unsigned resolution, Image8U3& image, Image8U& mask, Point3& center) const; + bool Split(FacesChunkArr&, float maxArea); + Mesh SubMesh(const FaceIdxArr& faces) const; + // file IO bool Load(const String& fileName); bool Save(const String& fileName, const cList& comments=cList(), bool bBinary=true) const; + bool Save(const FacesChunkArr&, const String& fileName, const cList& comments=cList(), bool bBinary=true) const; static bool Save(const VertexArr& vertices, const String& fileName, bool bBinary=true); static inline uint32_t FindVertex(const Face& f, VIndex v) { for (uint32_t i=0; i<3; ++i) if (f[i] == v) return i; return NO_ID; } From 7a88fefb0476fa4f6229e228a69641ff3febeb10 Mon Sep 17 00:00:00 2001 From: cDc Date: Sun, 21 Feb 2021 16:59:10 +0200 Subject: [PATCH 018/252] dense: split scene in sub-scenes --- apps/DensifyPointCloud/DensifyPointCloud.cpp | 10 + libs/Common/OBB.inl | 26 +- libs/MVS/DepthMap.cpp | 4 +- libs/MVS/Scene.cpp | 243 +++++++++++++++++++ libs/MVS/Scene.h | 9 + 5 files changed, 273 insertions(+), 19 deletions(-) diff --git a/apps/DensifyPointCloud/DensifyPointCloud.cpp b/apps/DensifyPointCloud/DensifyPointCloud.cpp index 8191db8f1..48e433931 100644 --- a/apps/DensifyPointCloud/DensifyPointCloud.cpp +++ b/apps/DensifyPointCloud/DensifyPointCloud.cpp @@ -48,6 +48,7 @@ String strInputFileName; String strOutputFileName; String strMeshFileName; String strDenseConfigFileName; +float fMaxSubsceneArea; float fSampleMesh; int thFilterPointCloud; int nFusionMode; @@ -106,6 +107,7 @@ bool Initialize(size_t argc, LPCTSTR* argv) ("ignore-mask-label", boost::program_options::value(&nIgnoreMaskLabel)->default_value(-1), "integer value for the label to ignore in the segmentation mask (<0 - disabled)") ("estimate-colors", boost::program_options::value(&nEstimateColors)->default_value(2), "estimate the colors for the dense point-cloud (0 - disabled, 1 - final, 2 - estimate)") ("estimate-normals", boost::program_options::value(&nEstimateNormals)->default_value(2), "estimate the normals for the dense point-cloud (0 - disabled, 1 - final, 2 - estimate)") + ("sub-scene-area", boost::program_options::value(&OPT::fMaxSubsceneArea)->default_value(0.f), "split the scene in sub-scenes such that each sub-scene surface does not exceed the given maximum sampling area (0 - disabled)") ("sample-mesh", boost::program_options::value(&OPT::fSampleMesh)->default_value(0.f), "uniformly samples points on a mesh (0 - disabled, <0 - number of points, >0 - sample density per square unit)") ("filter-point-cloud", boost::program_options::value(&OPT::thFilterPointCloud)->default_value(0), "filter dense point-cloud based on visibility (0 - disabled)") ("fusion-mode", boost::program_options::value(&OPT::nFusionMode)->default_value(0), "depth map fusion mode (-2 - fuse disparity-maps, -1 - export disparity-maps only, 0 - depth-maps & fusion, 1 - export depth-maps only)") @@ -247,6 +249,14 @@ int main(int argc, LPCTSTR* argv) VERBOSE("error: empty initial point-cloud"); return EXIT_FAILURE; } + if (OPT::fMaxSubsceneArea > 0) { + // split the scene in sub-scenes by maximum sampling area + Scene::ImagesChunkArr chunks; + scene.Split(chunks, OPT::fMaxSubsceneArea); + scene.ExportChunks(chunks, Util::getFilePath(MAKE_PATH_SAFE(OPT::strOutputFileName))); + Finalize(); + return EXIT_SUCCESS; + } if (OPT::thFilterPointCloud < 0) { // filter point-cloud based on camera-point visibility intersections scene.PointCloudFilter(OPT::thFilterPointCloud); diff --git a/libs/Common/OBB.inl b/libs/Common/OBB.inl index 920b6302a..657a4df97 100644 --- a/libs/Common/OBB.inl +++ b/libs/Common/OBB.inl @@ -162,24 +162,12 @@ template inline void TOBB::SetRotation(const MATRIX& C) { // extract the eigenvalues and eigenvectors from C - const Eigen::EigenSolver es(C); - if (es.info() != Eigen::Success) - return; - const MATRIX eigvec(es.eigenvectors().real()); - const POINT eigval(es.eigenvalues().real()); - int indices[3] = {0,1,2}; - std::sort(indices, indices+3, [&] (int i, int j) { - return eigval(i) < eigval(j); - }); - + const Eigen::SelfAdjointEigenSolver es(C); + ASSERT(es.info() == Eigen::Success); // find the right, up and forward vectors from the eigenvectors // and set the rotation matrix using the eigenvectors - m_rot.row(0) = eigvec.col(indices[0]); - m_rot.row(1) = eigvec.col(indices[1]); - m_rot.row(2) = eigvec.col(indices[2]); - ASSERT(ISEQUAL(m_rot.row(0).norm(), TYPE(1))); - ASSERT(ISEQUAL(m_rot.row(1).norm(), TYPE(1))); - ASSERT(ISEQUAL(m_rot.row(2).norm(), TYPE(1))); + ASSERT(es.eigenvalues()(0) < es.eigenvalues()(1) && es.eigenvalues()(1) < es.eigenvalues()(2)); + m_rot = es.eigenvectors().transpose(); if (m_rot.determinant() < 0) m_rot = -m_rot; } @@ -354,6 +342,7 @@ inline void TOBB::GetCorners(POINT pts[numCorners]) const template inline typename TOBB::AABB TOBB::GetAABB() const { + #if 0 if (DIMS == 2) { const POINT pEAxis[2] = { m_rot.row(0)*m_ext[0], @@ -375,6 +364,11 @@ inline typename TOBB::AABB TOBB::GetAABB() const m_pos + pEAxis[0] + pEAxis[1] + pEAxis[2] ); } + #else + POINT pts[numCorners]; + GetCorners(pts); + return AABB(pts, numCorners); + #endif } // GetAABB /*----------------------------------------------------------------*/ diff --git a/libs/MVS/DepthMap.cpp b/libs/MVS/DepthMap.cpp index 494e89f9c..9d9334d55 100644 --- a/libs/MVS/DepthMap.cpp +++ b/libs/MVS/DepthMap.cpp @@ -233,7 +233,6 @@ bool DepthData::Save(const String& fileName) const } bool DepthData::Load(const String& fileName) { - ASSERT(IsValid()); // serialize in the saved state String imageFileName; IIndexArr IDs; @@ -241,8 +240,7 @@ bool DepthData::Load(const String& fileName) Camera camera; if (!ImportDepthDataRaw(fileName, imageFileName, IDs, imageSize, camera.K, camera.R, camera.C, dMin, dMax, depthMap, normalMap, confMap)) return false; - ASSERT(IDs.size() == images.size()); - ASSERT(IDs.front() == GetView().GetID()); + ASSERT(!IsValid() || (IDs.size() == images.size() && IDs.front() == GetView().GetID())); ASSERT(depthMap.size() == imageSize); return true; } diff --git a/libs/MVS/Scene.cpp b/libs/MVS/Scene.cpp index 7ab5c1c21..7c847b7c9 100644 --- a/libs/MVS/Scene.cpp +++ b/libs/MVS/Scene.cpp @@ -734,3 +734,246 @@ bool Scene::ExportCamerasMLP(const String& fileName, const String& fileNameScene return true; } // ExportCamerasMLP /*----------------------------------------------------------------*/ + + +// split the scene in sub-scenes such that each sub-scene surface does not exceed the given +// maximum sampling area; the area is composed of overlapping samples from different cameras +// taking into account the footprint of each sample (pixels/unit-length, GSD inverse), +// including overlapping samples; +// the indirect goals this method tries to achieve are: +// - limit the maximum number of images in each sub-scene such that the depth-map fusion +// can load all sub-scene's depth-maps into memory at once +// - limit in the same time maximum accumulated images resolution (total number of pixels) +// per sub-scene in order to allow all images to be loaded and processed during mesh refinement +unsigned Scene::Split(ImagesChunkArr& chunks, IIndex maxArea, int depthMapStep) const +{ + TD_TIMER_STARTD(); + // gather samples from all depth-maps + const float areaScale(0.01f); + typedef cList Samples; + typedef TOctree Octree; + Octree octree; + FloatArr areas(0, images.size()*4192); + IIndexArr visibility(0, areas.capacity()); + Unsigned32Arr imageAreas(images.size()); { + Samples samples(0, areas.capacity()); + FOREACH(idxImage, images) { + const Image& imageData = images[idxImage]; + if (!imageData.IsValid()) + continue; + DepthData depthData; + depthData.Load(ComposeDepthFilePath(imageData.ID, "dmap")); + if (depthData.IsEmpty()) + continue; + const size_t numPointsBegin(visibility.size()); + const Camera camera(imageData.GetCamera(platforms, depthData.depthMap.size())); + for (int r=(depthData.depthMap.rows%depthMapStep)/2; r(camera.TransformPointI2W(Point3(c,r,depth)))); + areas.emplace_back(Footprint(camera, X)*areaScale); + visibility.emplace_back(idxImage); + } + } + imageAreas[idxImage] = visibility.size()-numPointsBegin; + } + #if 0 + const AABB3f aabb(samples.data(), samples.size()); + #else + // try to find a dominant plane, and set the bounding-box center on the plane bottom + OBB3f obb(samples.data(), samples.size()); + obb.m_ext(0) *= 2; + #if 1 + // dump box for visualization + OBB3f::POINT pts[8]; + obb.GetCorners(pts); + PointCloud pc; + for (int i=0; i<8; ++i) + pc.points.emplace_back(pts[i]); + pc.Save(MAKE_PATH("scene_obb.ply")); + #endif + const AABB3f aabb(obb.GetAABB()); + #endif + octree.Insert(samples, aabb, [](Octree::IDX_TYPE size, Octree::Type /*radius*/) { + return size > 128; + }); + #if 0 && !defined(_RELEASE) + Octree::DEBUGINFO_TYPE info; + octree.GetDebugInfo(&info); + Octree::LogDebugInfo(info); + #endif + octree.ResetItems(); + } + struct AreaInserter { + const FloatArr& areas; + float area; + inline void operator() (const Octree::IDX_TYPE* indices, Octree::SIZE_TYPE size) { + FOREACHRAWPTR(pIdx, indices, size) + area += areas[*pIdx]; + } + inline float PopArea() { + const float a(area); + area = 0; + return a; + } + } areaEstimator{areas, 0.f}; + struct ChunkInserter { + const IIndex numImages; + const Octree& octree; + const IIndexArr& visibility; + ImagesChunkArr& chunks; + CLISTDEF2(Unsigned32Arr) imagesAreas; + void operator() (const Octree::CELL_TYPE& parentCell, Octree::Type parentRadius, const UnsignedArr& children) { + ASSERT(!children.empty()); + ImagesChunk& chunk = chunks.AddEmpty(); + Unsigned32Arr& imageAreas = imagesAreas.AddEmpty(); + imageAreas.resize(numImages); + imageAreas.Memset(0); + struct Inserter { + const IIndexArr& visibility; + std::unordered_set& images; + Unsigned32Arr& imageAreas; + inline void operator() (const Octree::IDX_TYPE* indices, Octree::SIZE_TYPE size) { + FOREACHRAWPTR(pIdx, indices, size) { + const IIndex idxImage(visibility[*pIdx]); + images.emplace(idxImage); + ++imageAreas[idxImage]; + } + } + } inserter{visibility, chunk.images, imageAreas}; + if (children.size() == 1) { + octree.CollectCells(parentCell.GetChild(children.front()), inserter); + chunk.aabb = parentCell.GetChildAabb(children.front(), parentRadius); + } else { + chunk.aabb.Reset(); + for (unsigned c: children) { + octree.CollectCells(parentCell.GetChild(c), inserter); + chunk.aabb.Insert(parentCell.GetChildAabb(c, parentRadius)); + } + } + if (chunk.images.empty()) { + chunks.RemoveLast(); + imagesAreas.RemoveLast(); + } + } + } chunkInserter{images.size(), octree, visibility, chunks}; + octree.SplitVolume(maxArea, areaEstimator, chunkInserter); + if (chunks.size() < 2) + return 0; + // remove images with very little contribution + const float minImageContributionRatio(0.25f); + FOREACH(c, chunks) { + ImagesChunk& chunk = chunks[c]; + const Unsigned32Arr& chunkImageAreas = chunkInserter.imagesAreas[c]; + for (auto it = chunk.images.begin(); it != chunk.images.end(); ) { + const IIndex idxImage(*it); + if (float(chunkImageAreas[idxImage])/float(imageAreas[idxImage]) < minImageContributionRatio) + it = chunk.images.erase(it); + else + ++it; + } + } + DEBUG_EXTRA("Scene split (%g max-area): %u chunks (%s)", maxArea, chunks.size(), TD_TIMER_GET_FMT().c_str()); + #if 1 + // dump chunks for visualization + FOREACH(c, chunks) { + const ImagesChunk& chunk = chunks[c]; + PointCloud pc = pointcloud; + pc.RemovePointsOutside(OBB3f(OBB3f::MATRIX::Identity(), chunk.aabb.ptMin, chunk.aabb.ptMax)); + pc.Save(String::FormatString(MAKE_PATH("scene_%04u.ply"), c)); + } + #endif + return chunks.size(); +} // Split + +// split the scene in sub-scenes according to the given chunks array, and save them to disk +bool Scene::ExportChunks(const ImagesChunkArr& chunks, const String& path) const +{ + FOREACH(chunkID, chunks) { + const ImagesChunk& chunk = chunks[chunkID]; + Scene subset; + subset.nCalibratedImages = (IIndex)chunk.images.size(); + // extract chunk images + typedef std::unordered_map MapIIndex; + MapIIndex mapPlatforms(platforms.size()); + MapIIndex mapImages(images.size()); + FOREACH(idxImage, images) { + if (chunk.images.find(idxImage) == chunk.images.end()) + continue; + const Image& image = images[idxImage]; + if (!image.IsValid()) + continue; + // copy platform + const Platform& platform = platforms[image.platformID]; + MapIIndex::iterator itSubPlatformMVS = mapPlatforms.find(image.platformID); + uint32_t subPlatformID; + if (itSubPlatformMVS == mapPlatforms.end()) { + ASSERT(subset.platforms.size() == mapPlatforms.size()); + subPlatformID = subset.platforms.size(); + mapPlatforms.emplace(image.platformID, subPlatformID); + Platform subPlatform; + subPlatform.name = platform.name; + subPlatform.cameras = platform.cameras; + subset.platforms.emplace_back(std::move(subPlatform)); + } else { + subPlatformID = itSubPlatformMVS->second; + } + Platform& subPlatform = subset.platforms[subPlatformID]; + // copy image + const IIndex idxImageNew((IIndex)mapImages.size()); + mapImages[idxImage] = idxImageNew; + Image subImage(image); + subImage.platformID = subPlatformID; + subImage.poseID = subPlatform.poses.size(); + subImage.ID = idxImage; + subset.images.emplace_back(std::move(subImage)); + // copy pose + subPlatform.poses.emplace_back(platform.poses[image.poseID]); + } + // map image IDs from global to local + for (Image& image: subset.images) { + RFOREACH(i, image.neighbors) { + ViewScore& neighbor = image.neighbors[i]; + const auto itImage(mapImages.find(neighbor.idx.ID)); + if (itImage == mapImages.end()) { + image.neighbors.RemoveAtMove(i); + continue; + } + ASSERT(itImage->second < subset.images.size()); + neighbor.idx.ID = itImage->second; + } + } + // extract point-cloud + FOREACH(idxPoint, pointcloud.points) { + PointCloud::ViewArr subViews; + PointCloud::WeightArr subWeights; + const PointCloud::ViewArr& views = pointcloud.pointViews[idxPoint]; + FOREACH(i, views) { + const IIndex idxImage(views[i]); + const auto itImage(mapImages.find(idxImage)); + if (itImage == mapImages.end()) + continue; + subViews.emplace_back(itImage->second); + if (!pointcloud.pointWeights.empty()) + subWeights.emplace_back(pointcloud.pointWeights[idxPoint][i]); + } + if (subViews.size() < 2) + continue; + subset.pointcloud.points.emplace_back(pointcloud.points[idxPoint]); + subset.pointcloud.pointViews.emplace_back(std::move(subViews)); + if (!pointcloud.pointWeights.empty()) + subset.pointcloud.pointWeights.emplace_back(std::move(subWeights)); + if (!pointcloud.colors.empty()) + subset.pointcloud.colors.emplace_back(pointcloud.colors[idxPoint]); + } + // set scene ROI + subset.obb.Set(OBB3f::MATRIX::Identity(), chunk.aabb.ptMin, chunk.aabb.ptMax); + // serialize out the current state + if (!subset.Save(String::FormatString("%s" PATH_SEPARATOR_STR "scene_%04u.mvs", path.c_str(), chunkID))) + return false; + } + return true; +} // ExportChunks +/*----------------------------------------------------------------*/ diff --git a/libs/MVS/Scene.h b/libs/MVS/Scene.h index 4c564101f..06a81219a 100644 --- a/libs/MVS/Scene.h +++ b/libs/MVS/Scene.h @@ -83,6 +83,15 @@ class MVS_API Scene bool ExportCamerasMLP(const String& fileName, const String& fileNameScene) const; + // sub-scene split and save + struct ImagesChunk { + std::unordered_set images; + AABB3f aabb; + }; + typedef cList ImagesChunkArr; + unsigned Split(ImagesChunkArr& chunks, IIndex maxArea, int depthMapStep=8) const; + bool ExportChunks(const ImagesChunkArr& chunks, const String& path) const; + // Dense reconstruction bool DenseReconstruction(int nFusionMode=0); bool ComputeDepthMaps(DenseDepthMapData& data); From 00b5979320cbd453bf5b0b8cd03c732a7e72fabd Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Naoki Date: Wed, 24 Feb 2021 01:04:53 +0900 Subject: [PATCH 019/252] cmake: build libCommon/IO/Math as shared if BUILD_SHARED_LIBS=ON (#650) --- libs/Common/CMakeLists.txt | 2 +- libs/IO/CMakeLists.txt | 2 +- libs/Math/CMakeLists.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/Common/CMakeLists.txt b/libs/Common/CMakeLists.txt index b14bf2926..631164e61 100644 --- a/libs/Common/CMakeLists.txt +++ b/libs/Common/CMakeLists.txt @@ -9,7 +9,7 @@ FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl") LIST(REMOVE_ITEM LIBRARY_FILES_C ${PCH_C}) SET(LIBRARY_FILES_C "${PCH_C};${LIBRARY_FILES_C}") -cxx_library_with_type_no_pch(Common "Libs" "STATIC" "${cxx_default}" +cxx_library_with_type_no_pch(Common "Libs" "" "${cxx_default}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H} ) diff --git a/libs/IO/CMakeLists.txt b/libs/IO/CMakeLists.txt index fdf4fb92b..14d4d942f 100644 --- a/libs/IO/CMakeLists.txt +++ b/libs/IO/CMakeLists.txt @@ -35,7 +35,7 @@ FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl") LIST(REMOVE_ITEM LIBRARY_FILES_C ${PCH_C}) SET(LIBRARY_FILES_C "${PCH_C};${LIBRARY_FILES_C}") -cxx_library_with_type_no_pch(IO "Libs" "STATIC" "${cxx_default}" +cxx_library_with_type_no_pch(IO "Libs" "" "${cxx_default}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H} ) diff --git a/libs/Math/CMakeLists.txt b/libs/Math/CMakeLists.txt index 0c8474941..23db6f06a 100644 --- a/libs/Math/CMakeLists.txt +++ b/libs/Math/CMakeLists.txt @@ -23,7 +23,7 @@ SOURCE_GROUP("TRWS" FILES ${TRWS_LIBRARY_FILES_C} ${TRWS_LIBRARY_FILES_H}) LIST(REMOVE_ITEM LIBRARY_FILES_C ${PCH_C}) SET(LIBRARY_FILES_C "${PCH_C};${LIBRARY_FILES_C}") -cxx_library_with_type_no_pch(Math "Libs" "STATIC" "${cxx_default}" +cxx_library_with_type_no_pch(Math "Libs" "" "${cxx_default}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H} ${IBFS_LIBRARY_FILES_C} ${IBFS_LIBRARY_FILES_H} ${LMFit_LIBRARY_FILES_C} ${LMFit_LIBRARY_FILES_H} From c4b2f59b2c7949f4a94405c5ce6fda349d0ce9df Mon Sep 17 00:00:00 2001 From: cDc Date: Thu, 4 Mar 2021 23:02:58 +0200 Subject: [PATCH 020/252] mesh: fix bug in RemoveFaces() --- libs/MVS/Mesh.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/MVS/Mesh.cpp b/libs/MVS/Mesh.cpp index 111de846f..9664a5506 100644 --- a/libs/MVS/Mesh.cpp +++ b/libs/MVS/Mesh.cpp @@ -3359,10 +3359,10 @@ void Mesh::RemoveFaces(FaceIdxArr& facesRemove, bool bUpdateLists) FaceIdxArr& vf(vertexFaces[idxV]); const FIndex idx(vf.Find(idxF)); if (idx != FaceIdxArr::NO_INDEX) - vf[idx] = idxF; + vf.RemoveAt(idx); } } - const FIndex idxFM(faces.GetSize()-1); + const FIndex idxFM(faces.size()-1); if (idxF < idxFM) { // update all vertices of the moved face const Face& face = faces[idxFM]; From 3581e7a65b598120a33336a76b1c5423bda60fae Mon Sep 17 00:00:00 2001 From: cDc Date: Sat, 6 Mar 2021 14:48:28 +0200 Subject: [PATCH 021/252] dense: filter more redundant views from sub-scenes --- libs/MVS/Scene.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/libs/MVS/Scene.cpp b/libs/MVS/Scene.cpp index 7c847b7c9..bd16fddd3 100644 --- a/libs/MVS/Scene.cpp +++ b/libs/MVS/Scene.cpp @@ -875,6 +875,28 @@ unsigned Scene::Split(ImagesChunkArr& chunks, IIndex maxArea, int depthMapStep) ++it; } } + #if 1 + // remove images already completely contained by a larger chunk + const float minImageContributionRatioLargerChunk(0.9f); + FOREACH(cSmall, chunks) { + ImagesChunk& chunkSmall = chunks[cSmall]; + const Unsigned32Arr& chunkSmallImageAreas = chunkInserter.imagesAreas[cSmall]; + FOREACH(cLarge, chunks) { + const ImagesChunk& chunkLarge = chunks[cLarge]; + if (chunkLarge.images.size() <= chunkSmall.images.size()) + continue; + const Unsigned32Arr& chunkLargeImageAreas = chunkInserter.imagesAreas[cLarge]; + for (auto it = chunkSmall.images.begin(); it != chunkSmall.images.end(); ) { + const IIndex idxImage(*it); + if (chunkSmallImageAreas[idxImage] < chunkLargeImageAreas[idxImage] && + float(chunkLargeImageAreas[idxImage])/float(imageAreas[idxImage]) > minImageContributionRatioLargerChunk) + it = chunkSmall.images.erase(it); + else + ++it; + } + } + } + #endif DEBUG_EXTRA("Scene split (%g max-area): %u chunks (%s)", maxArea, chunks.size(), TD_TIMER_GET_FMT().c_str()); #if 1 // dump chunks for visualization From 3be30ef1112ddeafd2aa5d640968aea5d004e756 Mon Sep 17 00:00:00 2001 From: cDc Date: Sat, 6 Mar 2021 16:42:22 +0200 Subject: [PATCH 022/252] common: add ZSTD serialization support --- apps/DensifyPointCloud/DensifyPointCloud.cpp | 4 +- apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp | 2 +- .../InterfaceVisualSFM/InterfaceVisualSFM.cpp | 2 +- apps/ReconstructMesh/ReconstructMesh.cpp | 28 ++++++------- apps/RefineMesh/RefineMesh.cpp | 40 +++++++++---------- apps/TextureMesh/TextureMesh.cpp | 32 +++++++-------- apps/Viewer/Viewer.cpp | 14 +++---- libs/Common/Types.inl | 32 ++++++++++++++- libs/MVS/DepthMap.cpp | 12 +++--- libs/MVS/Scene.cpp | 6 +-- libs/MVS/Scene.h | 4 +- 11 files changed, 103 insertions(+), 73 deletions(-) diff --git a/apps/DensifyPointCloud/DensifyPointCloud.cpp b/apps/DensifyPointCloud/DensifyPointCloud.cpp index 48e433931..ad5bcf4e5 100644 --- a/apps/DensifyPointCloud/DensifyPointCloud.cpp +++ b/apps/DensifyPointCloud/DensifyPointCloud.cpp @@ -72,7 +72,7 @@ bool Initialize(size_t argc, LPCTSTR* argv) ("help,h", "produce this help message") ("working-folder,w", boost::program_options::value(&WORKING_FOLDER), "working directory (default current directory)") ("config-file,c", boost::program_options::value(&OPT::strConfigFileName)->default_value(APPNAME _T(".cfg")), "file name containing program options") - ("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(2), "project archive type: 0-text, 1-binary, 2-compressed binary") + ("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(ARCHIVE_DEFAULT), "project archive type: 0-text, 1-binary, 2-compressed binary") ("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)") ("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)") #if TD_VERBOSE != TD_VERBOSE_OFF @@ -253,7 +253,7 @@ int main(int argc, LPCTSTR* argv) // split the scene in sub-scenes by maximum sampling area Scene::ImagesChunkArr chunks; scene.Split(chunks, OPT::fMaxSubsceneArea); - scene.ExportChunks(chunks, Util::getFilePath(MAKE_PATH_SAFE(OPT::strOutputFileName))); + scene.ExportChunks(chunks, Util::getFilePath(MAKE_PATH_SAFE(OPT::strOutputFileName)), (ARCHIVE_TYPE)OPT::nArchiveType); Finalize(); return EXIT_SUCCESS; } diff --git a/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp b/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp index a88666a3f..2f34b4f2d 100644 --- a/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp +++ b/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp @@ -89,7 +89,7 @@ bool Initialize(size_t argc, LPCTSTR* argv) ("help,h", "produce this help message") ("working-folder,w", boost::program_options::value(&WORKING_FOLDER), "working directory (default current directory)") ("config-file,c", boost::program_options::value(&OPT::strConfigFileName)->default_value(APPNAME _T(".cfg")), "file name containing program options") - ("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(2), "project archive type: 0-text, 1-binary, 2-compressed binary") + ("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(ARCHIVE_DEFAULT), "project archive type: 0-text, 1-binary, 2-compressed binary") ("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)") ("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)") #if TD_VERBOSE != TD_VERBOSE_OFF diff --git a/apps/InterfaceVisualSFM/InterfaceVisualSFM.cpp b/apps/InterfaceVisualSFM/InterfaceVisualSFM.cpp index f90e4b612..b7092699c 100644 --- a/apps/InterfaceVisualSFM/InterfaceVisualSFM.cpp +++ b/apps/InterfaceVisualSFM/InterfaceVisualSFM.cpp @@ -73,7 +73,7 @@ bool Initialize(size_t argc, LPCTSTR* argv) ("help,h", "produce this help message") ("working-folder,w", boost::program_options::value(&WORKING_FOLDER), "working directory (default current directory)") ("config-file,c", boost::program_options::value(&OPT::strConfigFileName)->default_value(APPNAME _T(".cfg")), "file name containing program options") - ("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(2), "project archive type: 0-text, 1-binary, 2-compressed binary") + ("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(ARCHIVE_DEFAULT), "project archive type: 0-text, 1-binary, 2-compressed binary") ("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)") ("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)") #if TD_VERBOSE != TD_VERBOSE_OFF diff --git a/apps/ReconstructMesh/ReconstructMesh.cpp b/apps/ReconstructMesh/ReconstructMesh.cpp index edd5b4cd2..57a5045c8 100644 --- a/apps/ReconstructMesh/ReconstructMesh.cpp +++ b/apps/ReconstructMesh/ReconstructMesh.cpp @@ -86,11 +86,11 @@ bool Initialize(size_t argc, LPCTSTR* argv) ("working-folder,w", boost::program_options::value(&WORKING_FOLDER), "working directory (default current directory)") ("config-file,c", boost::program_options::value(&OPT::strConfigFileName)->default_value(APPNAME _T(".cfg")), "file name containing program options") ("export-type", boost::program_options::value(&OPT::strExportType)->default_value(_T("ply")), "file type used to export the 3D scene (ply or obj)") - ("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(2), "project archive type: 0-text, 1-binary, 2-compressed binary") - ("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)") - ("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)") + ("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(ARCHIVE_DEFAULT), "project archive type: 0-text, 1-binary, 2-compressed binary") + ("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)") + ("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)") #if TD_VERBOSE != TD_VERBOSE_OFF - ("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value( + ("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value( #if TD_VERBOSE == TD_VERBOSE_DEBUG 3 #else @@ -105,19 +105,19 @@ bool Initialize(size_t argc, LPCTSTR* argv) config_main.add_options() ("input-file,i", boost::program_options::value(&OPT::strInputFileName), "input filename containing camera poses and image list") ("output-file,o", boost::program_options::value(&OPT::strOutputFileName), "output filename for storing the mesh") - ("min-point-distance,d", boost::program_options::value(&OPT::fDistInsert)->default_value(2.5f), "minimum distance in pixels between the projection of two 3D points to consider them different while triangulating (0 - disabled)") - ("constant-weight", boost::program_options::value(&OPT::bUseConstantWeight)->default_value(true), "considers all view weights 1 instead of the available weight") - ("free-space-support,f", boost::program_options::value(&OPT::bUseFreeSpaceSupport)->default_value(false), "exploits the free-space support in order to reconstruct weakly-represented surfaces") - ("thickness-factor", boost::program_options::value(&OPT::fThicknessFactor)->default_value(1.f), "multiplier adjusting the minimum thickness considered during visibility weighting") - ("quality-factor", boost::program_options::value(&OPT::fQualityFactor)->default_value(1.f), "multiplier adjusting the quality weight considered during graph-cut") + ("min-point-distance,d", boost::program_options::value(&OPT::fDistInsert)->default_value(2.5f), "minimum distance in pixels between the projection of two 3D points to consider them different while triangulating (0 - disabled)") + ("constant-weight", boost::program_options::value(&OPT::bUseConstantWeight)->default_value(true), "considers all view weights 1 instead of the available weight") + ("free-space-support,f", boost::program_options::value(&OPT::bUseFreeSpaceSupport)->default_value(false), "exploits the free-space support in order to reconstruct weakly-represented surfaces") + ("thickness-factor", boost::program_options::value(&OPT::fThicknessFactor)->default_value(1.f), "multiplier adjusting the minimum thickness considered during visibility weighting") + ("quality-factor", boost::program_options::value(&OPT::fQualityFactor)->default_value(1.f), "multiplier adjusting the quality weight considered during graph-cut") ; boost::program_options::options_description config_clean("Clean options"); config_clean.add_options() - ("decimate", boost::program_options::value(&OPT::fDecimateMesh)->default_value(1.f), "decimation factor in range (0..1] to be applied to the reconstructed surface (1 - disabled)") - ("remove-spurious", boost::program_options::value(&OPT::fRemoveSpurious)->default_value(20.f), "spurious factor for removing faces with too long edges or isolated components (0 - disabled)") - ("remove-spikes", boost::program_options::value(&OPT::bRemoveSpikes)->default_value(true), "flag controlling the removal of spike faces") - ("close-holes", boost::program_options::value(&OPT::nCloseHoles)->default_value(30), "try to close small holes in the reconstructed surface (0 - disabled)") - ("smooth", boost::program_options::value(&OPT::nSmoothMesh)->default_value(2), "number of iterations to smooth the reconstructed surface (0 - disabled)") + ("decimate", boost::program_options::value(&OPT::fDecimateMesh)->default_value(1.f), "decimation factor in range (0..1] to be applied to the reconstructed surface (1 - disabled)") + ("remove-spurious", boost::program_options::value(&OPT::fRemoveSpurious)->default_value(20.f), "spurious factor for removing faces with too long edges or isolated components (0 - disabled)") + ("remove-spikes", boost::program_options::value(&OPT::bRemoveSpikes)->default_value(true), "flag controlling the removal of spike faces") + ("close-holes", boost::program_options::value(&OPT::nCloseHoles)->default_value(30), "try to close small holes in the reconstructed surface (0 - disabled)") + ("smooth", boost::program_options::value(&OPT::nSmoothMesh)->default_value(2), "number of iterations to smooth the reconstructed surface (0 - disabled)") ; // hidden options, allowed both on command line and diff --git a/apps/RefineMesh/RefineMesh.cpp b/apps/RefineMesh/RefineMesh.cpp index 1cbd6e227..954ab1fb9 100644 --- a/apps/RefineMesh/RefineMesh.cpp +++ b/apps/RefineMesh/RefineMesh.cpp @@ -87,11 +87,11 @@ bool Initialize(size_t argc, LPCTSTR* argv) ("working-folder,w", boost::program_options::value(&WORKING_FOLDER), "working directory (default current directory)") ("config-file,c", boost::program_options::value(&OPT::strConfigFileName)->default_value(APPNAME _T(".cfg")), "file name containing program options") ("export-type", boost::program_options::value(&OPT::strExportType)->default_value(_T("ply")), "file type used to export the 3D scene (ply or obj)") - ("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(2), "project archive type: 0-text, 1-binary, 2-compressed binary") - ("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)") - ("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)") + ("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(ARCHIVE_DEFAULT), "project archive type: 0-text, 1-binary, 2-compressed binary") + ("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)") + ("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)") #if TD_VERBOSE != TD_VERBOSE_OFF - ("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value( + ("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value( #if TD_VERBOSE == TD_VERBOSE_DEBUG 3 #else @@ -106,23 +106,23 @@ bool Initialize(size_t argc, LPCTSTR* argv) config.add_options() ("input-file,i", boost::program_options::value(&OPT::strInputFileName), "input filename containing camera poses and image list") ("output-file,o", boost::program_options::value(&OPT::strOutputFileName), "output filename for storing the mesh") - ("resolution-level", boost::program_options::value(&OPT::nResolutionLevel)->default_value(0), "how many times to scale down the images before mesh refinement") - ("min-resolution", boost::program_options::value(&OPT::nMinResolution)->default_value(640), "do not scale images lower than this resolution") - ("max-views", boost::program_options::value(&OPT::nMaxViews)->default_value(8), "maximum number of neighbor images used to refine the mesh") - ("decimate", boost::program_options::value(&OPT::fDecimateMesh)->default_value(0.f), "decimation factor in range [0..1] to be applied to the input surface before refinement (0 - auto, 1 - disabled)") - ("close-holes", boost::program_options::value(&OPT::nCloseHoles)->default_value(30), "try to close small holes in the input surface (0 - disabled)") - ("ensure-edge-size", boost::program_options::value(&OPT::nEnsureEdgeSize)->default_value(1), "ensure edge size and improve vertex valence of the input surface (0 - disabled, 1 - auto, 2 - force)") - ("max-face-area", boost::program_options::value(&OPT::nMaxFaceArea)->default_value(64), "maximum face area projected in any pair of images that is not subdivided (0 - disabled)") - ("scales", boost::program_options::value(&OPT::nScales)->default_value(3), "how many iterations to run mesh optimization on multi-scale images") - ("scale-step", boost::program_options::value(&OPT::fScaleStep)->default_value(0.5f), "image scale factor used at each mesh optimization step") - ("reduce-memory", boost::program_options::value(&OPT::nReduceMemory)->default_value(1), "recompute some data in order to reduce memory requirements") - ("alternate-pair", boost::program_options::value(&OPT::nAlternatePair)->default_value(0), "refine mesh using an image pair alternatively as reference (0 - both, 1 - alternate, 2 - only left, 3 - only right)") - ("regularity-weight", boost::program_options::value(&OPT::fRegularityWeight)->default_value(0.2f), "scalar regularity weight to balance between photo-consistency and regularization terms during mesh optimization") - ("rigidity-elasticity-ratio", boost::program_options::value(&OPT::fRatioRigidityElasticity)->default_value(0.9f), "scalar ratio used to compute the regularity gradient as a combination of rigidity and elasticity") - ("gradient-step", boost::program_options::value(&OPT::fGradientStep)->default_value(45.05f), "gradient step to be used instead (0 - auto)") - ("planar-vertex-ratio", boost::program_options::value(&OPT::fPlanarVertexRatio)->default_value(0.f), "threshold used to remove vertices on planar patches (0 - disabled)") + ("resolution-level", boost::program_options::value(&OPT::nResolutionLevel)->default_value(0), "how many times to scale down the images before mesh refinement") + ("min-resolution", boost::program_options::value(&OPT::nMinResolution)->default_value(640), "do not scale images lower than this resolution") + ("max-views", boost::program_options::value(&OPT::nMaxViews)->default_value(8), "maximum number of neighbor images used to refine the mesh") + ("decimate", boost::program_options::value(&OPT::fDecimateMesh)->default_value(0.f), "decimation factor in range [0..1] to be applied to the input surface before refinement (0 - auto, 1 - disabled)") + ("close-holes", boost::program_options::value(&OPT::nCloseHoles)->default_value(30), "try to close small holes in the input surface (0 - disabled)") + ("ensure-edge-size", boost::program_options::value(&OPT::nEnsureEdgeSize)->default_value(1), "ensure edge size and improve vertex valence of the input surface (0 - disabled, 1 - auto, 2 - force)") + ("max-face-area", boost::program_options::value(&OPT::nMaxFaceArea)->default_value(64), "maximum face area projected in any pair of images that is not subdivided (0 - disabled)") + ("scales", boost::program_options::value(&OPT::nScales)->default_value(3), "how many iterations to run mesh optimization on multi-scale images") + ("scale-step", boost::program_options::value(&OPT::fScaleStep)->default_value(0.5f), "image scale factor used at each mesh optimization step") + ("reduce-memory", boost::program_options::value(&OPT::nReduceMemory)->default_value(1), "recompute some data in order to reduce memory requirements") + ("alternate-pair", boost::program_options::value(&OPT::nAlternatePair)->default_value(0), "refine mesh using an image pair alternatively as reference (0 - both, 1 - alternate, 2 - only left, 3 - only right)") + ("regularity-weight", boost::program_options::value(&OPT::fRegularityWeight)->default_value(0.2f), "scalar regularity weight to balance between photo-consistency and regularization terms during mesh optimization") + ("rigidity-elasticity-ratio", boost::program_options::value(&OPT::fRatioRigidityElasticity)->default_value(0.9f), "scalar ratio used to compute the regularity gradient as a combination of rigidity and elasticity") + ("gradient-step", boost::program_options::value(&OPT::fGradientStep)->default_value(45.05f), "gradient step to be used instead (0 - auto)") + ("planar-vertex-ratio", boost::program_options::value(&OPT::fPlanarVertexRatio)->default_value(0.f), "threshold used to remove vertices on planar patches (0 - disabled)") #ifdef _USE_CUDA - ("use-cuda", boost::program_options::value(&OPT::bUseCUDA)->default_value(true), "refine mesh using CUDA") + ("use-cuda", boost::program_options::value(&OPT::bUseCUDA)->default_value(true), "refine mesh using CUDA") #endif ; diff --git a/apps/TextureMesh/TextureMesh.cpp b/apps/TextureMesh/TextureMesh.cpp index 23f1421d1..34209d0b2 100644 --- a/apps/TextureMesh/TextureMesh.cpp +++ b/apps/TextureMesh/TextureMesh.cpp @@ -81,11 +81,11 @@ bool Initialize(size_t argc, LPCTSTR* argv) ("working-folder,w", boost::program_options::value(&WORKING_FOLDER), "working directory (default current directory)") ("config-file,c", boost::program_options::value(&OPT::strConfigFileName)->default_value(APPNAME _T(".cfg")), "file name containing program options") ("export-type", boost::program_options::value(&OPT::strExportType)->default_value(_T("ply")), "file type used to export the 3D scene (ply or obj)") - ("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(2), "project archive type: 0-text, 1-binary, 2-compressed binary") - ("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)") - ("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)") + ("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(ARCHIVE_DEFAULT), "project archive type: 0-text, 1-binary, 2-compressed binary") + ("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)") + ("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)") #if TD_VERBOSE != TD_VERBOSE_OFF - ("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value( + ("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value( #if TD_VERBOSE == TD_VERBOSE_DEBUG 3 #else @@ -100,18 +100,18 @@ bool Initialize(size_t argc, LPCTSTR* argv) config.add_options() ("input-file,i", boost::program_options::value(&OPT::strInputFileName), "input filename containing camera poses and image list") ("output-file,o", boost::program_options::value(&OPT::strOutputFileName), "output filename for storing the mesh") - ("decimate", boost::program_options::value(&OPT::fDecimateMesh)->default_value(1.f), "decimation factor in range [0..1] to be applied to the input surface before refinement (0 - auto, 1 - disabled)") - ("close-holes", boost::program_options::value(&OPT::nCloseHoles)->default_value(30), "try to close small holes in the input surface (0 - disabled)") - ("resolution-level", boost::program_options::value(&OPT::nResolutionLevel)->default_value(0), "how many times to scale down the images before mesh refinement") - ("min-resolution", boost::program_options::value(&OPT::nMinResolution)->default_value(640), "do not scale images lower than this resolution") - ("outlier-threshold", boost::program_options::value(&OPT::fOutlierThreshold)->default_value(6e-2f), "threshold used to find and remove outlier face textures (0 - disabled)") - ("cost-smoothness-ratio", boost::program_options::value(&OPT::fRatioDataSmoothness)->default_value(0.1f), "ratio used to adjust the preference for more compact patches (1 - best quality/worst compactness, ~0 - worst quality/best compactness)") - ("global-seam-leveling", boost::program_options::value(&OPT::bGlobalSeamLeveling)->default_value(true), "generate uniform texture patches using global seam leveling") - ("local-seam-leveling", boost::program_options::value(&OPT::bLocalSeamLeveling)->default_value(true), "generate uniform texture patch borders using local seam leveling") - ("texture-size-multiple", boost::program_options::value(&OPT::nTextureSizeMultiple)->default_value(0), "texture size should be a multiple of this value (0 - power of two)") - ("patch-packing-heuristic", boost::program_options::value(&OPT::nRectPackingHeuristic)->default_value(3), "specify the heuristic used when deciding where to place a new patch (0 - best fit, 3 - good speed, 100 - best speed)") - ("empty-color", boost::program_options::value(&OPT::nColEmpty)->default_value(0x00FF7F27), "color used for faces not covered by any image") - ("orthographic-image-resolution", boost::program_options::value(&OPT::nOrthoMapResolution)->default_value(0), "orthographic image resolution to be generated from the textured mesh - the mesh is expected to be already geo-referenced or at least properly oriented (0 - disabled)") + ("decimate", boost::program_options::value(&OPT::fDecimateMesh)->default_value(1.f), "decimation factor in range [0..1] to be applied to the input surface before refinement (0 - auto, 1 - disabled)") + ("close-holes", boost::program_options::value(&OPT::nCloseHoles)->default_value(30), "try to close small holes in the input surface (0 - disabled)") + ("resolution-level", boost::program_options::value(&OPT::nResolutionLevel)->default_value(0), "how many times to scale down the images before mesh refinement") + ("min-resolution", boost::program_options::value(&OPT::nMinResolution)->default_value(640), "do not scale images lower than this resolution") + ("outlier-threshold", boost::program_options::value(&OPT::fOutlierThreshold)->default_value(6e-2f), "threshold used to find and remove outlier face textures (0 - disabled)") + ("cost-smoothness-ratio", boost::program_options::value(&OPT::fRatioDataSmoothness)->default_value(0.1f), "ratio used to adjust the preference for more compact patches (1 - best quality/worst compactness, ~0 - worst quality/best compactness)") + ("global-seam-leveling", boost::program_options::value(&OPT::bGlobalSeamLeveling)->default_value(true), "generate uniform texture patches using global seam leveling") + ("local-seam-leveling", boost::program_options::value(&OPT::bLocalSeamLeveling)->default_value(true), "generate uniform texture patch borders using local seam leveling") + ("texture-size-multiple", boost::program_options::value(&OPT::nTextureSizeMultiple)->default_value(0), "texture size should be a multiple of this value (0 - power of two)") + ("patch-packing-heuristic", boost::program_options::value(&OPT::nRectPackingHeuristic)->default_value(3), "specify the heuristic used when deciding where to place a new patch (0 - best fit, 3 - good speed, 100 - best speed)") + ("empty-color", boost::program_options::value(&OPT::nColEmpty)->default_value(0x00FF7F27), "color used for faces not covered by any image") + ("orthographic-image-resolution", boost::program_options::value(&OPT::nOrthoMapResolution)->default_value(0), "orthographic image resolution to be generated from the textured mesh - the mesh is expected to be already geo-referenced or at least properly oriented (0 - disabled)") ; // hidden options, allowed both on command line and diff --git a/apps/Viewer/Viewer.cpp b/apps/Viewer/Viewer.cpp index 2470e3abf..1ef9ad928 100644 --- a/apps/Viewer/Viewer.cpp +++ b/apps/Viewer/Viewer.cpp @@ -75,13 +75,13 @@ bool Initialize(size_t argc, LPCTSTR* argv) ("working-folder,w", boost::program_options::value(&WORKING_FOLDER), "working directory (default current directory)") ("config-file,c", boost::program_options::value(&OPT::strConfigFileName)->default_value(APPNAME _T(".cfg")), "file name containing program options") ("export-type", boost::program_options::value(&OPT::strExportType), "file type used to export the 3D scene (ply or obj)") - ("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(2), "project archive type: 0-text, 1-binary, 2-compressed binary") - ("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(0), "process priority (normal by default)") - ("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads that this process should use (0 - use all available cores)") - ("max-memory", boost::program_options::value(&OPT::nMaxMemory)->default_value(0), "maximum amount of memory in MB that this process should use (0 - use all available memory)") + ("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(ARCHIVE_DEFAULT), "project archive type: 0-text, 1-binary, 2-compressed binary") + ("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(0), "process priority (normal by default)") + ("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads that this process should use (0 - use all available cores)") + ("max-memory", boost::program_options::value(&OPT::nMaxMemory)->default_value(0), "maximum amount of memory in MB that this process should use (0 - use all available memory)") #if TD_VERBOSE != TD_VERBOSE_OFF - ("log-file", boost::program_options::value(&OPT::bLogFile)->default_value(false), "dump log to a file") - ("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value( + ("log-file", boost::program_options::value(&OPT::bLogFile)->default_value(false), "dump log to a file") + ("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value( #if TD_VERBOSE == TD_VERBOSE_DEBUG 3 #else @@ -96,7 +96,7 @@ bool Initialize(size_t argc, LPCTSTR* argv) config.add_options() ("input-file,i", boost::program_options::value(&OPT::strInputFileName), "input project filename containing camera poses and scene (point-cloud/mesh)") ("output-file,o", boost::program_options::value(&OPT::strOutputFileName), "output filename for storing the mesh") - ("texture-lossless", boost::program_options::value(&OPT::bLosslessTexture)->default_value(false), "export texture using a lossless image format") + ("texture-lossless", boost::program_options::value(&OPT::bLosslessTexture)->default_value(false), "export texture using a lossless image format") ; // hidden options, allowed both on command line and diff --git a/libs/Common/Types.inl b/libs/Common/Types.inl index 645df52f5..d63aca192 100644 --- a/libs/Common/Types.inl +++ b/libs/Common/Types.inl @@ -3734,6 +3734,7 @@ namespace boost { // include headers that implement a archive in simple text and binary format or XML format #if defined(_MSC_VER) #pragma warning (push) +#pragma warning (disable : 4275) // non dll-interface class #pragma warning (disable : 4715) // not all control paths return a value #endif #include @@ -3745,6 +3746,9 @@ namespace boost { // include headers that implement compressed serialization support #include #include +#if BOOST_VERSION >= 106900 +#include +#endif #if defined(_MSC_VER) #pragma warning (pop) #endif @@ -3754,7 +3758,13 @@ enum ARCHIVE_TYPE { ARCHIVE_TEXT = 0, ARCHIVE_BINARY, ARCHIVE_BINARY_ZIP, - ARCHIVE_LAST + ARCHIVE_BINARY_ZSTD, + ARCHIVE_LAST, + #if BOOST_VERSION >= 106900 + ARCHIVE_DEFAULT = ARCHIVE_BINARY_ZSTD + #else + ARCHIVE_DEFAULT = ARCHIVE_BINARY_ZIP + #endif }; // export the current state of the given reconstruction object @@ -3779,6 +3789,16 @@ bool SerializeSave(const TYPE& obj, std::ofstream& fs, ARCHIVE_TYPE type, unsign boost::archive::binary_oarchive ar(ffs, flags); ar << obj; break; } + #if BOOST_VERSION >= 106900 + case ARCHIVE_BINARY_ZSTD: { + namespace io = boost::iostreams; + io::filtering_streambuf ffs; + ffs.push(io::zstd_compressor(io::zstd::best_speed)); + ffs.push(fs); + boost::archive::binary_oarchive ar(ffs, flags); + ar << obj; + break; } + #endif default: VERBOSE("error: Can not save the object, invalid archive type"); return false; @@ -3819,6 +3839,16 @@ bool SerializeLoad(TYPE& obj, std::ifstream& fs, ARCHIVE_TYPE type, unsigned fla boost::archive::binary_iarchive ar(ffs, flags); ar >> obj; break; } + #if BOOST_VERSION >= 106900 + case ARCHIVE_BINARY_ZSTD: { + namespace io = boost::iostreams; + io::filtering_streambuf ffs; + ffs.push(io::zstd_decompressor()); + ffs.push(fs); + boost::archive::binary_iarchive ar(ffs, flags); + ar >> obj; + break; } + #endif default: VERBOSE("error: Can not load the object, invalid archive type"); return false; diff --git a/libs/MVS/DepthMap.cpp b/libs/MVS/DepthMap.cpp index 9d9334d55..b1266d361 100644 --- a/libs/MVS/DepthMap.cpp +++ b/libs/MVS/DepthMap.cpp @@ -1489,13 +1489,13 @@ bool MVS::EstimateNormalMap(const Matrix3x3f& K, const DepthMap& depthMap, Norma bool MVS::SaveDepthMap(const String& fileName, const DepthMap& depthMap) { ASSERT(!depthMap.empty()); - return SerializeSave(depthMap, fileName, ARCHIVE_BINARY_ZIP); + return SerializeSave(depthMap, fileName, ARCHIVE_DEFAULT); } // SaveDepthMap /*----------------------------------------------------------------*/ // load the depth map from our .dmap file format bool MVS::LoadDepthMap(const String& fileName, DepthMap& depthMap) { - return SerializeLoad(depthMap, fileName, ARCHIVE_BINARY_ZIP); + return SerializeLoad(depthMap, fileName, ARCHIVE_DEFAULT); } // LoadDepthMap /*----------------------------------------------------------------*/ @@ -1503,13 +1503,13 @@ bool MVS::LoadDepthMap(const String& fileName, DepthMap& depthMap) bool MVS::SaveNormalMap(const String& fileName, const NormalMap& normalMap) { ASSERT(!normalMap.empty()); - return SerializeSave(normalMap, fileName, ARCHIVE_BINARY_ZIP); + return SerializeSave(normalMap, fileName, ARCHIVE_DEFAULT); } // SaveNormalMap /*----------------------------------------------------------------*/ // load the normal map from our .nmap file format bool MVS::LoadNormalMap(const String& fileName, NormalMap& normalMap) { - return SerializeLoad(normalMap, fileName, ARCHIVE_BINARY_ZIP); + return SerializeLoad(normalMap, fileName, ARCHIVE_DEFAULT); } // LoadNormalMap /*----------------------------------------------------------------*/ @@ -1517,13 +1517,13 @@ bool MVS::LoadNormalMap(const String& fileName, NormalMap& normalMap) bool MVS::SaveConfidenceMap(const String& fileName, const ConfidenceMap& confMap) { ASSERT(!confMap.empty()); - return SerializeSave(confMap, fileName, ARCHIVE_BINARY_ZIP); + return SerializeSave(confMap, fileName, ARCHIVE_DEFAULT); } // SaveConfidenceMap /*----------------------------------------------------------------*/ // load the confidence map from our .cmap file format bool MVS::LoadConfidenceMap(const String& fileName, ConfidenceMap& confMap) { - return SerializeLoad(confMap, fileName, ARCHIVE_BINARY_ZIP); + return SerializeLoad(confMap, fileName, ARCHIVE_DEFAULT); } // LoadConfidenceMap /*----------------------------------------------------------------*/ diff --git a/libs/MVS/Scene.cpp b/libs/MVS/Scene.cpp index bd16fddd3..0c16feeeb 100644 --- a/libs/MVS/Scene.cpp +++ b/libs/MVS/Scene.cpp @@ -482,7 +482,7 @@ bool Scene::Save(const String& fileName, ARCHIVE_TYPE type) const if (type == ARCHIVE_MVS) { if (mesh.IsEmpty()) return SaveInterface(fileName); - type = ARCHIVE_BINARY_ZIP; + type = ARCHIVE_DEFAULT; } #ifdef _USE_BOOST // open the output stream @@ -911,7 +911,7 @@ unsigned Scene::Split(ImagesChunkArr& chunks, IIndex maxArea, int depthMapStep) } // Split // split the scene in sub-scenes according to the given chunks array, and save them to disk -bool Scene::ExportChunks(const ImagesChunkArr& chunks, const String& path) const +bool Scene::ExportChunks(const ImagesChunkArr& chunks, const String& path, ARCHIVE_TYPE type) const { FOREACH(chunkID, chunks) { const ImagesChunk& chunk = chunks[chunkID]; @@ -993,7 +993,7 @@ bool Scene::ExportChunks(const ImagesChunkArr& chunks, const String& path) const // set scene ROI subset.obb.Set(OBB3f::MATRIX::Identity(), chunk.aabb.ptMin, chunk.aabb.ptMax); // serialize out the current state - if (!subset.Save(String::FormatString("%s" PATH_SEPARATOR_STR "scene_%04u.mvs", path.c_str(), chunkID))) + if (!subset.Save(String::FormatString("%s" PATH_SEPARATOR_STR "scene_%04u.mvs", path.c_str(), chunkID), type)) return false; } return true; diff --git a/libs/MVS/Scene.h b/libs/MVS/Scene.h index 06a81219a..42b93db51 100644 --- a/libs/MVS/Scene.h +++ b/libs/MVS/Scene.h @@ -76,7 +76,7 @@ class MVS_API Scene bool Import(const String& fileName); bool Load(const String& fileName, bool bImport=false); - bool Save(const String& fileName, ARCHIVE_TYPE type=ARCHIVE_BINARY_ZIP) const; + bool Save(const String& fileName, ARCHIVE_TYPE type=ARCHIVE_DEFAULT) const; bool SelectNeighborViews(uint32_t ID, IndexArr& points, unsigned nMinViews=3, unsigned nMinPointViews=2, float fOptimAngle=FD2R(10)); static bool FilterNeighborViews(ViewScoreArr& neighbors, float fMinArea=0.1f, float fMinScale=0.2f, float fMaxScale=2.4f, float fMinAngle=FD2R(3), float fMaxAngle=FD2R(45), unsigned nMaxViews=12); @@ -90,7 +90,7 @@ class MVS_API Scene }; typedef cList ImagesChunkArr; unsigned Split(ImagesChunkArr& chunks, IIndex maxArea, int depthMapStep=8) const; - bool ExportChunks(const ImagesChunkArr& chunks, const String& path) const; + bool ExportChunks(const ImagesChunkArr& chunks, const String& path, ARCHIVE_TYPE type=ARCHIVE_DEFAULT) const; // Dense reconstruction bool DenseReconstruction(int nFusionMode=0); From 979d6dbeaff9fd7aff80beb9754c12eee4f9734c Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Naoki Date: Sat, 6 Mar 2021 23:54:15 +0900 Subject: [PATCH 023/252] apps: set internal linkage for functions and variables (#656) --- apps/DensifyPointCloud/DensifyPointCloud.cpp | 3 +++ apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp | 2 ++ apps/InterfaceOpenMVG/InterfaceOpenMVG.cpp | 3 +++ apps/InterfacePhotoScan/InterfacePhotoScan.cpp | 3 +++ apps/InterfaceVisualSFM/InterfaceVisualSFM.cpp | 3 +++ apps/ReconstructMesh/ReconstructMesh.cpp | 3 +++ apps/RefineMesh/RefineMesh.cpp | 3 +++ apps/TextureMesh/TextureMesh.cpp | 3 +++ apps/Viewer/Viewer.cpp | 3 +++ 9 files changed, 26 insertions(+) diff --git a/apps/DensifyPointCloud/DensifyPointCloud.cpp b/apps/DensifyPointCloud/DensifyPointCloud.cpp index ad5bcf4e5..f626416f0 100644 --- a/apps/DensifyPointCloud/DensifyPointCloud.cpp +++ b/apps/DensifyPointCloud/DensifyPointCloud.cpp @@ -40,6 +40,7 @@ using namespace MVS; #define APPNAME _T("DensifyPointCloud") +namespace { // S T R U C T S /////////////////////////////////////////////////// @@ -216,6 +217,8 @@ void Finalize() CLOSE_LOG(); } +} // unnamed namespace + int main(int argc, LPCTSTR* argv) { #ifdef _DEBUGINFO diff --git a/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp b/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp index 2f34b4f2d..8a2513ea7 100644 --- a/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp +++ b/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp @@ -60,6 +60,7 @@ using namespace MVS; #define COLMAP_STEREO_DEPTHMAPS_FOLDER COLMAP_STEREO_FOLDER _T("depth_maps/") #define COLMAP_STEREO_NORMALMAPS_FOLDER COLMAP_STEREO_FOLDER _T("normal_maps/") +namespace { // S T R U C T S /////////////////////////////////////////////////// @@ -1081,6 +1082,7 @@ bool ExportImagesCamera(const String& pathName, const Interface& scene) return true; } +} // unnamed namespace int main(int argc, LPCTSTR* argv) { diff --git a/apps/InterfaceOpenMVG/InterfaceOpenMVG.cpp b/apps/InterfaceOpenMVG/InterfaceOpenMVG.cpp index c410d0d75..2d43e5017 100644 --- a/apps/InterfaceOpenMVG/InterfaceOpenMVG.cpp +++ b/apps/InterfaceOpenMVG/InterfaceOpenMVG.cpp @@ -50,6 +50,7 @@ #define MVG2_EXT _T(".json") #define MVG3_EXT _T(".bin") +namespace { // S T R U C T S /////////////////////////////////////////////////// @@ -471,6 +472,8 @@ void Finalize() CLOSE_LOG(); } +} // unnamed namespace + int main(int argc, LPCTSTR* argv) { #ifdef _DEBUGINFO diff --git a/apps/InterfacePhotoScan/InterfacePhotoScan.cpp b/apps/InterfacePhotoScan/InterfacePhotoScan.cpp index 0b5f1effa..b4d8a57de 100644 --- a/apps/InterfacePhotoScan/InterfacePhotoScan.cpp +++ b/apps/InterfacePhotoScan/InterfacePhotoScan.cpp @@ -41,6 +41,7 @@ #define XML_EXT _T(".xml") #define PLY_EXT _T(".ply") +namespace { // S T R U C T S /////////////////////////////////////////////////// @@ -524,6 +525,8 @@ void AssignPoints(const MVS::Image& imageData, uint32_t ID, MVS::PointCloud& poi DEBUG_ULTIMATE("\tview %3u sees %u points", ID, nNumPoints); } +} // unnamed namespace + int main(int argc, LPCTSTR* argv) { #ifdef _DEBUGINFO diff --git a/apps/InterfaceVisualSFM/InterfaceVisualSFM.cpp b/apps/InterfaceVisualSFM/InterfaceVisualSFM.cpp index b7092699c..17b695137 100644 --- a/apps/InterfaceVisualSFM/InterfaceVisualSFM.cpp +++ b/apps/InterfaceVisualSFM/InterfaceVisualSFM.cpp @@ -45,6 +45,7 @@ #define BUNDLE_EXT _T(".out") #define CMPMVS_EXT _T(".lst") +namespace { // S T R U C T S /////////////////////////////////////////////////// @@ -187,6 +188,8 @@ void Finalize() CLOSE_LOG(); } +} // unnamed namespace + #define PBA_PRECISION float namespace PBA { diff --git a/apps/ReconstructMesh/ReconstructMesh.cpp b/apps/ReconstructMesh/ReconstructMesh.cpp index 57a5045c8..49a3bfdc7 100644 --- a/apps/ReconstructMesh/ReconstructMesh.cpp +++ b/apps/ReconstructMesh/ReconstructMesh.cpp @@ -45,6 +45,7 @@ using namespace MVS; #define RECMESH_USE_OPENMP #endif +namespace { // S T R U C T S /////////////////////////////////////////////////// @@ -209,6 +210,8 @@ void Finalize() CLOSE_LOG(); } +} // unnamed namespace + int main(int argc, LPCTSTR* argv) { #ifdef _DEBUGINFO diff --git a/apps/RefineMesh/RefineMesh.cpp b/apps/RefineMesh/RefineMesh.cpp index 954ab1fb9..6467da83f 100644 --- a/apps/RefineMesh/RefineMesh.cpp +++ b/apps/RefineMesh/RefineMesh.cpp @@ -40,6 +40,7 @@ using namespace MVS; #define APPNAME _T("RefineMesh") +namespace { // S T R U C T S /////////////////////////////////////////////////// @@ -213,6 +214,8 @@ void Finalize() CLOSE_LOG(); } +} // unnamed namespace + int main(int argc, LPCTSTR* argv) { #ifdef _DEBUGINFO diff --git a/apps/TextureMesh/TextureMesh.cpp b/apps/TextureMesh/TextureMesh.cpp index 34209d0b2..234d78a75 100644 --- a/apps/TextureMesh/TextureMesh.cpp +++ b/apps/TextureMesh/TextureMesh.cpp @@ -40,6 +40,7 @@ using namespace MVS; #define APPNAME _T("TextureMesh") +namespace { // S T R U C T S /////////////////////////////////////////////////// @@ -201,6 +202,8 @@ void Finalize() CLOSE_LOG(); } +} // unnamed namespace + int main(int argc, LPCTSTR* argv) { #ifdef _DEBUGINFO diff --git a/apps/Viewer/Viewer.cpp b/apps/Viewer/Viewer.cpp index 1ef9ad928..7b12bf1f6 100644 --- a/apps/Viewer/Viewer.cpp +++ b/apps/Viewer/Viewer.cpp @@ -41,6 +41,7 @@ using namespace VIEWER; #define APPNAME _T("Viewer") +namespace { // S T R U C T S /////////////////////////////////////////////////// @@ -200,6 +201,8 @@ void Finalize() CLOSE_LOG(); } +} // unnamed namespace + int main(int argc, LPCTSTR* argv) { #ifdef _DEBUGINFO From 55a2ab1b85da59a7f2a316a169253c9737399363 Mon Sep 17 00:00:00 2001 From: cDc Date: Sat, 6 Mar 2021 17:09:47 +0200 Subject: [PATCH 024/252] common: small refactor --- apps/DensifyPointCloud/DensifyPointCloud.cpp | 3 ++- apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp | 3 ++- apps/InterfaceOpenMVG/InterfaceOpenMVG.cpp | 13 +++++++------ apps/InterfacePhotoScan/InterfacePhotoScan.cpp | 11 ++++++----- apps/InterfaceVisualSFM/InterfaceVisualSFM.cpp | 3 ++- apps/ReconstructMesh/ReconstructMesh.cpp | 3 ++- apps/RefineMesh/RefineMesh.cpp | 3 ++- apps/TextureMesh/TextureMesh.cpp | 3 ++- apps/Viewer/Viewer.cpp | 3 ++- 9 files changed, 27 insertions(+), 18 deletions(-) diff --git a/apps/DensifyPointCloud/DensifyPointCloud.cpp b/apps/DensifyPointCloud/DensifyPointCloud.cpp index f626416f0..61ef6ddf5 100644 --- a/apps/DensifyPointCloud/DensifyPointCloud.cpp +++ b/apps/DensifyPointCloud/DensifyPointCloud.cpp @@ -40,10 +40,11 @@ using namespace MVS; #define APPNAME _T("DensifyPointCloud") -namespace { // S T R U C T S /////////////////////////////////////////////////// +namespace { + namespace OPT { String strInputFileName; String strOutputFileName; diff --git a/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp b/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp index 8a2513ea7..6c1082e29 100644 --- a/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp +++ b/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp @@ -60,10 +60,11 @@ using namespace MVS; #define COLMAP_STEREO_DEPTHMAPS_FOLDER COLMAP_STEREO_FOLDER _T("depth_maps/") #define COLMAP_STEREO_NORMALMAPS_FOLDER COLMAP_STEREO_FOLDER _T("normal_maps/") -namespace { // S T R U C T S /////////////////////////////////////////////////// +namespace { + namespace OPT { bool bFromOpenMVS; // conversion direction bool bNormalizeIntrinsics; diff --git a/apps/InterfaceOpenMVG/InterfaceOpenMVG.cpp b/apps/InterfaceOpenMVG/InterfaceOpenMVG.cpp index 2d43e5017..3f4a171ee 100644 --- a/apps/InterfaceOpenMVG/InterfaceOpenMVG.cpp +++ b/apps/InterfaceOpenMVG/InterfaceOpenMVG.cpp @@ -50,10 +50,11 @@ #define MVG2_EXT _T(".json") #define MVG3_EXT _T(".bin") -namespace { // S T R U C T S /////////////////////////////////////////////////// +namespace { + namespace openMVS { namespace MVS_IO { @@ -351,11 +352,11 @@ bool Initialize(size_t argc, LPCTSTR* argv) ("help,h", "produce this help message") ("working-folder,w", boost::program_options::value(&WORKING_FOLDER), "working directory (default current directory)") ("config-file,c", boost::program_options::value(&OPT::strConfigFileName)->default_value(APPNAME _T(".cfg")), "file name containing program options") - ("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(2), "project archive type: 0-text, 1-binary, 2-compressed binary") - ("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)") - ("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)") + ("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(ARCHIVE_DEFAULT), "project archive type: 0-text, 1-binary, 2-compressed binary") + ("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)") + ("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)") #if TD_VERBOSE != TD_VERBOSE_OFF - ("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value( + ("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value( #if TD_VERBOSE == TD_VERBOSE_DEBUG 3 #else @@ -372,7 +373,7 @@ bool Initialize(size_t argc, LPCTSTR* argv) ("input-file,i", boost::program_options::value(&OPT::strInputFileName), "input filename containing camera poses and image list") ("output-file,o", boost::program_options::value(&OPT::strOutputFileName), "output filename for storing the mesh") ("output-image-folder", boost::program_options::value(&OPT::strOutputImageFolder)->default_value("undistorted_images"), "output folder to store undistorted images") - ("normalize,f", boost::program_options::value(&OPT::bNormalizeIntrinsics)->default_value(true), "normalize intrinsics while exporting to OpenMVS format") + ("normalize,f", boost::program_options::value(&OPT::bNormalizeIntrinsics)->default_value(true), "normalize intrinsics while exporting to OpenMVS format") ; boost::program_options::options_description cmdline_options; diff --git a/apps/InterfacePhotoScan/InterfacePhotoScan.cpp b/apps/InterfacePhotoScan/InterfacePhotoScan.cpp index b4d8a57de..e12c4769b 100644 --- a/apps/InterfacePhotoScan/InterfacePhotoScan.cpp +++ b/apps/InterfacePhotoScan/InterfacePhotoScan.cpp @@ -41,10 +41,11 @@ #define XML_EXT _T(".xml") #define PLY_EXT _T(".ply") -namespace { // S T R U C T S /////////////////////////////////////////////////// +namespace { + namespace OPT { String strPointsFileName; String strInputFileName; @@ -70,11 +71,11 @@ bool Initialize(size_t argc, LPCTSTR* argv) ("help,h", "produce this help message") ("working-folder,w", boost::program_options::value(&WORKING_FOLDER), "working directory (default current directory)") ("config-file,c", boost::program_options::value(&OPT::strConfigFileName)->default_value(APPNAME _T(".cfg")), "file name containing program options") - ("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(2), "project archive type: 0-text, 1-binary, 2-compressed binary") - ("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)") - ("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)") + ("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(ARCHIVE_DEFAULT), "project archive type: 0-text, 1-binary, 2-compressed binary") + ("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)") + ("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)") #if TD_VERBOSE != TD_VERBOSE_OFF - ("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value( + ("verbosity,v", boost::program_options::value(&g_nVerbosityLevel)->default_value( #if TD_VERBOSE == TD_VERBOSE_DEBUG 3 #else diff --git a/apps/InterfaceVisualSFM/InterfaceVisualSFM.cpp b/apps/InterfaceVisualSFM/InterfaceVisualSFM.cpp index 17b695137..c0278aa11 100644 --- a/apps/InterfaceVisualSFM/InterfaceVisualSFM.cpp +++ b/apps/InterfaceVisualSFM/InterfaceVisualSFM.cpp @@ -45,10 +45,11 @@ #define BUNDLE_EXT _T(".out") #define CMPMVS_EXT _T(".lst") -namespace { // S T R U C T S /////////////////////////////////////////////////// +namespace { + namespace OPT { String strInputFileName; String strOutputFileName; diff --git a/apps/ReconstructMesh/ReconstructMesh.cpp b/apps/ReconstructMesh/ReconstructMesh.cpp index 49a3bfdc7..44becb3c3 100644 --- a/apps/ReconstructMesh/ReconstructMesh.cpp +++ b/apps/ReconstructMesh/ReconstructMesh.cpp @@ -45,10 +45,11 @@ using namespace MVS; #define RECMESH_USE_OPENMP #endif -namespace { // S T R U C T S /////////////////////////////////////////////////// +namespace { + namespace OPT { String strInputFileName; String strOutputFileName; diff --git a/apps/RefineMesh/RefineMesh.cpp b/apps/RefineMesh/RefineMesh.cpp index 6467da83f..53694ee9b 100644 --- a/apps/RefineMesh/RefineMesh.cpp +++ b/apps/RefineMesh/RefineMesh.cpp @@ -40,10 +40,11 @@ using namespace MVS; #define APPNAME _T("RefineMesh") -namespace { // S T R U C T S /////////////////////////////////////////////////// +namespace { + namespace OPT { String strInputFileName; String strOutputFileName; diff --git a/apps/TextureMesh/TextureMesh.cpp b/apps/TextureMesh/TextureMesh.cpp index 234d78a75..a0ce3cae5 100644 --- a/apps/TextureMesh/TextureMesh.cpp +++ b/apps/TextureMesh/TextureMesh.cpp @@ -40,10 +40,11 @@ using namespace MVS; #define APPNAME _T("TextureMesh") -namespace { // S T R U C T S /////////////////////////////////////////////////// +namespace { + namespace OPT { String strInputFileName; String strOutputFileName; diff --git a/apps/Viewer/Viewer.cpp b/apps/Viewer/Viewer.cpp index 7b12bf1f6..e0ff5b56d 100644 --- a/apps/Viewer/Viewer.cpp +++ b/apps/Viewer/Viewer.cpp @@ -41,10 +41,11 @@ using namespace VIEWER; #define APPNAME _T("Viewer") -namespace { // S T R U C T S /////////////////////////////////////////////////// +namespace { + namespace OPT { String strInputFileName; String strOutputFileName; From 43ca8f2a737db18024934c870a68d1b571bd1cc4 Mon Sep 17 00:00:00 2001 From: cDc Date: Sun, 7 Mar 2021 11:59:51 +0200 Subject: [PATCH 025/252] mesh: raster triangles using barycentric coordinates --- libs/Common/Octree.inl | 2 +- libs/Common/Types.h | 18 +++++++++++++ libs/Common/Types.inl | 42 +++++++++++++++++++++++++++++ libs/Common/Util.inl | 13 +++++++-- libs/MVS/Mesh.cpp | 55 ++++++++++++++------------------------ libs/MVS/Mesh.h | 56 +++++++++++++-------------------------- libs/MVS/SceneRefine.cpp | 11 ++++---- libs/MVS/SceneTexture.cpp | 9 +++---- 8 files changed, 120 insertions(+), 86 deletions(-) diff --git a/libs/Common/Octree.inl b/libs/Common/Octree.inl index 7a3026e25..e0e0f298b 100644 --- a/libs/Common/Octree.inl +++ b/libs/Common/Octree.inl @@ -515,7 +515,7 @@ void TOctree::_SplitVolume(const CELL_TYPE& pa POINT_TYPE centers[8]; cList arrHalfIndices; GenerateSamples(const UnsignedArr& _indices) - : indices(_indices), numSamples((unsigned)indices.size()), halfSamples((unsigned)indices.size()/2), arrHalfIndices(0, numSamples), numCommonAxis(halfSamples==4?1:2) + : indices(_indices), numSamples((unsigned)indices.size()), halfSamples(numSamples/2), numCommonAxis(halfSamples==4?1:2), arrHalfIndices(0, numSamples) { ASSERT(indices.size()%2 == 0 && indices.IsSorted()); ASSERT(halfSamples == 4 || halfSamples == 2); diff --git a/libs/Common/Types.h b/libs/Common/Types.h index e3c749dd9..6da8a0d77 100644 --- a/libs/Common/Types.h +++ b/libs/Common/Types.h @@ -1636,6 +1636,22 @@ class TDMatrix : public cv::Mat_ return pt.x>=T(border) && pt.y>=T(border) && pt.x<=T(Base::size().width-(border+1)) && pt.y<=T(Base::size().height-(border+1)); } + template + static inline void clip(TPoint2& ptMin, TPoint2& ptMax, const cv::Size& size) { + if (ptMin.x < T(border)) + ptMin.x = T(border); + if (ptMin.y < T(border)) + ptMin.y = T(border); + if (ptMax.x >= T(size.width-border)) + ptMax.x = T(size.width-(border+1)); + if (ptMax.y >= T(size.height-border)) + ptMax.y = T(size.height-(border+1)); + } + template + inline void clip(TPoint2& ptMin, TPoint2& ptMax) const { + clip(ptMin, ptMax, Base::size()); + } + /// Remove the given element from the vector inline void remove(int idx) { // replace the removed element by the last one and decrease the size @@ -2155,6 +2171,8 @@ class TImage : public TDMatrix template static void RasterizeTriangle(const TPoint2& v1, const TPoint2& v2, const TPoint2& v3, PARSER& parser); template + static void RasterizeTriangleBary(const TPoint2& v1, const TPoint2& v2, const TPoint2& v3, PARSER& parser); + template static void RasterizeTriangleDepth(TPoint3 p1, TPoint3 p2, TPoint3 p3, PARSER& parser); template diff --git a/libs/Common/Types.inl b/libs/Common/Types.inl index d63aca192..381b58dd7 100644 --- a/libs/Common/Types.inl +++ b/libs/Common/Types.inl @@ -2605,6 +2605,48 @@ void TImage::RasterizeTriangle(const TPoint2& v1, const TPoint2& v2, } } +// same as above, but raster a triangle using barycentric coordinates: +// https://www.scratchapixel.com/lessons/3d-basic-rendering/rasterization-practical-implementation +template +template +void TImage::RasterizeTriangleBary(const TPoint2& v1, const TPoint2& v2, const TPoint2& v3, PARSER& parser) +{ + // compute bounding-box fully containing the triangle + const TPoint2 boxMin(MINF3(v1.x, v2.x, v3.x), MINF3(v1.y, v2.y, v3.y)); + const TPoint2 boxMax(MAXF3(v1.x, v2.x, v3.x), MAXF3(v1.y, v2.y, v3.y)); + // check the bounding-box intersects the image + const cv::Size size(parser.Size()); + if (boxMax.x < T(0) || boxMin.x > T(size.width - 1) || + boxMax.y < T(0) || boxMin.y > T(size.height - 1)) + return; + // ignore back oriented triangles (negative area) + const T area(EdgeFunction(v1, v2, v3)); + if (area <= 0) + return; + // clip bounding-box to be fully contained by the image + ImageRef boxMinI(FLOOR2INT(boxMin)); + ImageRef boxMaxI(CEIL2INT(boxMax)); + Base::clip(boxMinI, boxMaxI, size); + // parse all pixels inside the bounding-box + const T invArea(T(1) / area); + for (int y = boxMinI.y; y <= boxMaxI.y; ++y) { + for (int x = boxMinI.x; x <= boxMaxI.x; ++x) { + const ImageRef pt(x, y); + const TPoint2 p(Cast(pt)); + const T b1(EdgeFunction(v2, v3, p)); + if (b1 < 0) + continue; + const T b2(EdgeFunction(v3, v1, p)); + if (b2 < 0) + continue; + const T b3(EdgeFunction(v1, v2, p)); + if (b3 < 0) + continue; + parser(pt, TPoint3(b1, b2, b3) * invArea); + } + } +} + // drawing line between 2 points from left to right // papb -> pcpd // pa, pb, pc, pd must then be sorted before diff --git a/libs/Common/Util.inl b/libs/Common/Util.inl index d605b9173..fb5aa82ec 100644 --- a/libs/Common/Util.inl +++ b/libs/Common/Util.inl @@ -468,13 +468,17 @@ TYPE ComputeTriangleAreaLenSq(TYPE lenaSq, TYPE lenbSq, TYPE lencSq) { } // compute area for a triangle defined by three 2D points template +TYPE EdgeFunction(const TPoint2& x0, const TPoint2& x1, const TPoint2& x2) { + return TYPE((x2-x0).cross(x1-x0)); +} +template TYPE ComputeTriangleArea(const TPoint2& x0, const TPoint2& x1, const TPoint2& x2) { - return abs((TYPE)((x0-x2).cross(x0-x1)/TYPE(2))); + return (TYPE)abs(EdgeFunction(x0, x1, x2)/TYPE(2)); } // compute area for a triangle defined by three 3D points template TYPE ComputeTriangleAreaSq(const TPoint3& x0, const TPoint3& x1, const TPoint3& x2) { - return (TYPE)(normSq((x1-x0).cross(x2-x0))/TYPE(4)); + return (TYPE)normSq((x1-x0).cross(x2-x0))/TYPE(4); } template TYPE ComputeTriangleArea(const TPoint3& x0, const TPoint3& x1, const TPoint3& x2) { @@ -607,6 +611,11 @@ inline TPoint3 CorrectBarycentricCoordinates(TPoint2 b) { ASSERT((b.x >= 0) && (b.y >= 0) && (b.x+b.y <= 1) && ISEQUAL(b.x+b.y+z, TYPE(1))); return TPoint3(b.x, b.y, z); } +template +inline TPoint3 PerspectiveCorrectBarycentricCoordinates(const TPoint3& b, TYPE z0, TYPE z1, TYPE z2) { + const TPoint3 pb(b.x * z1 * z2, b.y * z0 * z2, b.z * z0 * z1); + return pb / (pb.x + pb.y + pb.z); +} /*----------------------------------------------------------------*/ // Encodes/decodes a normalized 3D vector in two parameters for the direction diff --git a/libs/MVS/Mesh.cpp b/libs/MVS/Mesh.cpp index 9664a5506..2adb1be55 100644 --- a/libs/MVS/Mesh.cpp +++ b/libs/MVS/Mesh.cpp @@ -3600,18 +3600,16 @@ void Mesh::Project(const Camera& camera, DepthMap& depthMap, Image8U3& image) co Base::Clear(); image.memset(0); } - void Raster(const ImageRef& pt) { - if (!depthMap.isInsideWithBorder(pt)) - return; - const float z((float)INVERT(normalPlane.dot(camera.TransformPointI2C(Point2(pt))))); - ASSERT(z > 0); + void Raster(const ImageRef& pt, const Point3f& bary) { + const Point3f pbary(PerspectiveCorrectBarycentricCoordinates(bary)); + const Depth z(ComputeDepth(pbary)); + ASSERT(z > Depth(0)); Depth& depth = depthMap(pt); if (depth == 0 || depth > z) { depth = z; - const Point3f b(CorrectBarycentricCoordinates(BarycentricCoordinatesUV(pti[0], pti[1], pti[2], Point2f(pt)))); - xt = mesh.faceTexcoords[idxFaceTex+0] * b[0]; - xt += mesh.faceTexcoords[idxFaceTex+1] * b[1]; - xt += mesh.faceTexcoords[idxFaceTex+2] * b[2]; + xt = mesh.faceTexcoords[idxFaceTex+0] * pbary[0]; + xt += mesh.faceTexcoords[idxFaceTex+1] * pbary[1]; + xt += mesh.faceTexcoords[idxFaceTex+2] * pbary[2]; image(pt) = mesh.textureDiffuse.sampleSafe(Point2f(xt.x*mesh.textureDiffuse.width(), (1.f-xt.y)*mesh.textureDiffuse.height())); } } @@ -3634,16 +3632,12 @@ void Mesh::ProjectOrtho(const Camera& camera, DepthMap& depthMap) const RasterMesh(const VertexArr& _vertices, const Camera& _camera, DepthMap& _depthMap) : Base(_vertices, _camera, _depthMap) {} inline bool ProjectVertex(const Mesh::Vertex& pt, int v) { - ptc[v] = camera.TransformPointW2C(Cast(pt)); - pti[v] = camera.TransformPointC2I(reinterpret_cast(ptc[v])); - return depthMap.isInsideWithBorder(pti[v]); + return (ptc[v] = camera.TransformPointW2C(Cast(pt))).z > 0 && + depthMap.isInsideWithBorder(pti[v] = camera.TransformPointC2I(reinterpret_cast(ptc[v]))); } - void Raster(const ImageRef& pt) { - if (!depthMap.isInsideWithBorder(pt)) - return; - const Point3f b(CorrectBarycentricCoordinates(BarycentricCoordinatesUV(pti[0], pti[1], pti[2], Point2f(pt)))); - const float z((float)(ptc[0].z*b[0] + ptc[1].z*b[1] + ptc[2].z*b[2])); - ASSERT(z > 0); + void Raster(const ImageRef& pt, const Point3f& bary) { + const Depth z(ComputeDepth(bary)); + ASSERT(z > Depth(0)); Depth& depth = depthMap(pt); if (depth == 0 || depth > z) depth = z; @@ -3670,27 +3664,18 @@ void Mesh::ProjectOrtho(const Camera& camera, DepthMap& depthMap, Image8U3& imag image.memset(0); } inline bool ProjectVertex(const Mesh::Vertex& pt, int v) { - ptc[v] = camera.TransformPointW2C(Cast(pt)); - pti[v] = camera.TransformPointC2I(reinterpret_cast(ptc[v])); - return depthMap.isInsideWithBorder(pti[v]); + return (ptc[v] = camera.TransformPointW2C(Cast(pt))).z > 0 && + depthMap.isInsideWithBorder(pti[v] = camera.TransformPointC2I(reinterpret_cast(ptc[v]))); } - inline bool CheckNormal(const Point3& /*faceCenter*/) { - // skip face if the (cos) angle between - // the face normal and the view direction is negative - return camera.Direction().dot(normalPlane) >= ZEROTOLERANCE(); - } - void Raster(const ImageRef& pt) { - if (!depthMap.isInsideWithBorder(pt)) - return; - const Point3f b(CorrectBarycentricCoordinates(BarycentricCoordinatesUV(pti[0], pti[1], pti[2], Point2f(pt)))); - const float z((float)(ptc[0].z*b[0] + ptc[1].z*b[1] + ptc[2].z*b[2])); - ASSERT(z > 0); + void Raster(const ImageRef& pt, const Point3f& bary) { + const Depth z(ComputeDepth(bary)); + ASSERT(z > Depth(0)); Depth& depth = depthMap(pt); if (depth == 0 || depth > z) { depth = z; - xt = mesh.faceTexcoords[idxFaceTex+0] * b[0]; - xt += mesh.faceTexcoords[idxFaceTex+1] * b[1]; - xt += mesh.faceTexcoords[idxFaceTex+2] * b[2]; + xt = mesh.faceTexcoords[idxFaceTex+0] * bary[0]; + xt += mesh.faceTexcoords[idxFaceTex+1] * bary[1]; + xt += mesh.faceTexcoords[idxFaceTex+2] * bary[2]; image(pt) = mesh.textureDiffuse.sampleSafe(Point2f(xt.x*mesh.textureDiffuse.width(), (1.f-xt.y)*mesh.textureDiffuse.height())); } } diff --git a/libs/MVS/Mesh.h b/libs/MVS/Mesh.h index 94a0ec204..be6c0478c 100644 --- a/libs/MVS/Mesh.h +++ b/libs/MVS/Mesh.h @@ -253,7 +253,6 @@ struct TRasterMesh { DepthMap& depthMap; - Point3 normalPlane; Point3 ptc[3]; Point2f pti[3]; @@ -263,20 +262,13 @@ struct TRasterMesh { inline void Clear() { depthMap.memset(0); } + inline cv::Size Size() const { + return depthMap.size(); + } inline bool ProjectVertex(const Mesh::Vertex& pt, int v) { - ptc[v] = camera.TransformPointW2C(Cast(pt)); - pti[v] = camera.TransformPointC2I(ptc[v]); - return depthMap.isInsideWithBorder(pti[v]); - } - inline bool CheckNormal(const Point3& faceCenter) { - // skip face if the (cos) angle between - // the face normal and the face to view vector is negative - if (faceCenter.dot(normalPlane) >= ZEROTOLERANCE()) - return false; - // prepare vector used to compute the depth during rendering - normalPlane /= normalPlane.dot(ptc[0]); - return true; + return (ptc[v] = camera.TransformPointW2C(Cast(pt))).z > 0 && + depthMap.isInsideWithBorder(pti[v] = camera.TransformPointC2I(ptc[v])); } void Project(const Mesh::Face& facet) { // project face vertices to image plane @@ -285,36 +277,26 @@ struct TRasterMesh { if (!static_cast(this)->ProjectVertex(vertices[facet[v]], v)) return; } - // compute the face center, which is also the view to face vector - // (cause the face is in camera view space) - const Point3 faceCenter((ptc[0]+ptc[1]+ptc[2])/3); - // skip face if the (cos) angle between - // the view to face vector and the view direction is negative - if (faceCenter.z <= REAL(0)) - return; - // compute the plane defined by the 3 points - const Point3 edge1(ptc[1]-ptc[0]); - const Point3 edge2(ptc[2]-ptc[0]); - normalPlane = edge1.cross(edge2); - // check the face is facing the camera and - // prepare vector used to compute the depth during rendering - if (!static_cast(this)->CheckNormal(faceCenter)) - return; - // draw triangle and for each pixel compute depth as the ray intersection with the plane - Image8U3::RasterizeTriangle(pti[0], pti[1], pti[2], *this); + // draw triangle + Image8U3::RasterizeTriangleBary(pti[0], pti[1], pti[2], *this); } - void Raster(const ImageRef& pt) { - if (!depthMap.isInsideWithBorder(pt)) - return; - const Depth z((Depth)INVERT(normalPlane.dot(camera.TransformPointI2C(Point2(pt))))); - ASSERT(z > 0); + inline Point3f PerspectiveCorrectBarycentricCoordinates(const Point3f& bary) { + return SEACAVE::PerspectiveCorrectBarycentricCoordinates(bary, (float)ptc[0].z, (float)ptc[1].z, (float)ptc[2].z); + } + inline float ComputeDepth(const Point3f& pbary) { + return pbary[0]*(float)ptc[0].z + pbary[1]*(float)ptc[1].z + pbary[2]*(float)ptc[2].z; + } + void Raster(const ImageRef& pt, const Point3f& bary) { + const Point3f pbary(PerspectiveCorrectBarycentricCoordinates(bary)); + const Depth z(ComputeDepth(pbary)); + ASSERT(z > Depth(0)); Depth& depth = depthMap(pt); if (depth == 0 || depth > z) depth = z; } - inline void operator()(const ImageRef& pt) { - static_cast(this)->Raster(pt); + inline void operator()(const ImageRef& pt, const Point3f& bary) { + static_cast(this)->Raster(pt, bary); } }; /*----------------------------------------------------------------*/ diff --git a/libs/MVS/SceneRefine.cpp b/libs/MVS/SceneRefine.cpp index 5d3e04173..f4dab6f9b 100644 --- a/libs/MVS/SceneRefine.cpp +++ b/libs/MVS/SceneRefine.cpp @@ -111,16 +111,15 @@ class MeshRefine { faceMap.memset((uint8_t)NO_ID); baryMap.memset(0); } - void Raster(const ImageRef& pt) { - if (!depthMap.isInsideWithBorder(pt)) - return; - const float z((float)INVERT(normalPlane.dot(camera.TransformPointI2C(Point2(pt))))); - ASSERT(z > 0); + void Raster(const ImageRef& pt, const Point3f& bary) { + const Point3f pbary(PerspectiveCorrectBarycentricCoordinates(bary)); + const Depth z(ComputeDepth(pbary)); + ASSERT(z > Depth(0)); Depth& depth = depthMap(pt); if (depth == 0 || depth > z) { depth = z; faceMap(pt) = idxFace; - baryMap(pt) = CorrectBarycentricCoordinates(BarycentricCoordinatesUV(pti[0], pti[1], pti[2], Point2f(pt))); + baryMap(pt) = pbary; } } }; diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index 2bd975d1d..58b2c7aa0 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -157,11 +157,10 @@ struct MeshTexture { Base::Clear(); faceMap.memset((uint8_t)NO_ID); } - void Raster(const ImageRef& pt) { - if (!depthMap.isInside(pt)) - return; - const Depth z((Depth)INVERT(normalPlane.dot(camera.TransformPointI2C(Point2(pt))))); - ASSERT(z > 0); + void Raster(const ImageRef& pt, const Point3f& bary) { + const Point3f pbary(PerspectiveCorrectBarycentricCoordinates(bary)); + const Depth z(ComputeDepth(pbary)); + ASSERT(z > Depth(0)); Depth& depth = depthMap(pt); if (depth == 0 || depth > z) { depth = z; From 574d64f83c12f09ba0a83f16ccf2f8ff52e63c11 Mon Sep 17 00:00:00 2001 From: cdc Date: Sat, 13 Mar 2021 22:31:11 +0200 Subject: [PATCH 026/252] remove cotire --- CMakeLists.txt | 2 - apps/DensifyPointCloud/CMakeLists.txt | 2 +- apps/InterfaceCOLMAP/CMakeLists.txt | 2 +- apps/InterfaceOpenMVG/CMakeLists.txt | 2 +- apps/InterfacePhotoScan/CMakeLists.txt | 2 +- apps/InterfaceVisualSFM/CMakeLists.txt | 2 +- apps/ReconstructMesh/CMakeLists.txt | 2 +- apps/RefineMesh/CMakeLists.txt | 2 +- apps/TextureMesh/CMakeLists.txt | 2 +- apps/Viewer/CMakeLists.txt | 13 +- build/Cotire.cmake | 4190 ------------------------ build/Utils.cmake | 99 +- libs/Common/CMakeLists.txt | 13 +- libs/IO/CMakeLists.txt | 13 +- libs/MVS/CMakeLists.txt | 13 +- libs/Math/CMakeLists.txt | 13 +- 16 files changed, 32 insertions(+), 4340 deletions(-) delete mode 100644 build/Cotire.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c69b60d2..0e0d0b7bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,8 +45,6 @@ SET(OpenMVS_USE_FAST_CBRT ON CACHE BOOL "Use an optimized code to compute the cu SET(OpenMVS_USE_SSE ON CACHE BOOL "Enable SSE optimizations") # Define helper functions and macros. -SET(COTIRE_INTDIR "cotire") -INCLUDE(build/Cotire.cmake) INCLUDE(build/Utils.cmake) # Init session with macros defined in Utils.cmake diff --git a/apps/DensifyPointCloud/CMakeLists.txt b/apps/DensifyPointCloud/CMakeLists.txt index 5dddebee4..38e27310b 100644 --- a/apps/DensifyPointCloud/CMakeLists.txt +++ b/apps/DensifyPointCloud/CMakeLists.txt @@ -5,7 +5,7 @@ else() endif() FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl") -cxx_executable_with_flags_no_pch(DensifyPointCloud "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H}) +cxx_executable_with_flags(DensifyPointCloud "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H}) # Install INSTALL(TARGETS DensifyPointCloud diff --git a/apps/InterfaceCOLMAP/CMakeLists.txt b/apps/InterfaceCOLMAP/CMakeLists.txt index 0a5b0fc0e..5d48c79ec 100644 --- a/apps/InterfaceCOLMAP/CMakeLists.txt +++ b/apps/InterfaceCOLMAP/CMakeLists.txt @@ -5,7 +5,7 @@ else() endif() FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl") -cxx_executable_with_flags_no_pch(InterfaceCOLMAP "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H}) +cxx_executable_with_flags(InterfaceCOLMAP "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H}) # Install INSTALL(TARGETS InterfaceCOLMAP diff --git a/apps/InterfaceOpenMVG/CMakeLists.txt b/apps/InterfaceOpenMVG/CMakeLists.txt index 5dc56a0d9..6377497ea 100644 --- a/apps/InterfaceOpenMVG/CMakeLists.txt +++ b/apps/InterfaceOpenMVG/CMakeLists.txt @@ -15,7 +15,7 @@ else() endif() FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl") -cxx_executable_with_flags_no_pch(InterfaceOpenMVG "Apps" "${cxx_default}" "${LIBS_DEPEND};${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H}) +cxx_executable_with_flags(InterfaceOpenMVG "Apps" "${cxx_default}" "${LIBS_DEPEND};${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H}) # Install INSTALL(TARGETS InterfaceOpenMVG diff --git a/apps/InterfacePhotoScan/CMakeLists.txt b/apps/InterfacePhotoScan/CMakeLists.txt index e030ce189..2fd92c343 100644 --- a/apps/InterfacePhotoScan/CMakeLists.txt +++ b/apps/InterfacePhotoScan/CMakeLists.txt @@ -5,7 +5,7 @@ else() endif() FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl") -cxx_executable_with_flags_no_pch(InterfacePhotoScan "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H}) +cxx_executable_with_flags(InterfacePhotoScan "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H}) # Install INSTALL(TARGETS InterfacePhotoScan diff --git a/apps/InterfaceVisualSFM/CMakeLists.txt b/apps/InterfaceVisualSFM/CMakeLists.txt index 9bd8bdedf..a1b16af7b 100644 --- a/apps/InterfaceVisualSFM/CMakeLists.txt +++ b/apps/InterfaceVisualSFM/CMakeLists.txt @@ -5,7 +5,7 @@ else() endif() FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl") -cxx_executable_with_flags_no_pch(InterfaceVisualSFM "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H}) +cxx_executable_with_flags(InterfaceVisualSFM "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H}) # Install INSTALL(TARGETS InterfaceVisualSFM diff --git a/apps/ReconstructMesh/CMakeLists.txt b/apps/ReconstructMesh/CMakeLists.txt index 86cac3de4..4b6aac728 100644 --- a/apps/ReconstructMesh/CMakeLists.txt +++ b/apps/ReconstructMesh/CMakeLists.txt @@ -5,7 +5,7 @@ else() endif() FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl") -cxx_executable_with_flags_no_pch(ReconstructMesh "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H}) +cxx_executable_with_flags(ReconstructMesh "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H}) # Install INSTALL(TARGETS ReconstructMesh diff --git a/apps/RefineMesh/CMakeLists.txt b/apps/RefineMesh/CMakeLists.txt index eaffec244..26a6f84ed 100644 --- a/apps/RefineMesh/CMakeLists.txt +++ b/apps/RefineMesh/CMakeLists.txt @@ -5,7 +5,7 @@ else() endif() FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl") -cxx_executable_with_flags_no_pch(RefineMesh "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H}) +cxx_executable_with_flags(RefineMesh "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H}) # Install INSTALL(TARGETS RefineMesh diff --git a/apps/TextureMesh/CMakeLists.txt b/apps/TextureMesh/CMakeLists.txt index b53e2f560..bef488eeb 100644 --- a/apps/TextureMesh/CMakeLists.txt +++ b/apps/TextureMesh/CMakeLists.txt @@ -5,7 +5,7 @@ else() endif() FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl") -cxx_executable_with_flags_no_pch(TextureMesh "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H}) +cxx_executable_with_flags(TextureMesh "Apps" "${cxx_default}" "MVS;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H}) # Install INSTALL(TARGETS TextureMesh diff --git a/apps/Viewer/CMakeLists.txt b/apps/Viewer/CMakeLists.txt index c90affca2..fe0920242 100644 --- a/apps/Viewer/CMakeLists.txt +++ b/apps/Viewer/CMakeLists.txt @@ -27,8 +27,6 @@ else() endif() # List sources files -FILE(GLOB PCH_C "Common.cpp") - if(MSVC) FILE(GLOB LIBRARY_FILES_C "*.cpp" "*.rc") else() @@ -36,15 +34,12 @@ else() endif() FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl") -# Place Common.cpp as the first file in the list -# needed by cotire when setting PCH manually -LIST(REMOVE_ITEM LIBRARY_FILES_C ${PCH_C}) -SET(LIBRARY_FILES_C "${PCH_C};${LIBRARY_FILES_C}") - -cxx_executable_with_flags_no_pch(${VIEWER_NAME} "Apps" "${cxx_default}" "MVS;${OPENGL_LIBRARIES};${GLEW_LIBRARY};${GLFW_STATIC_LIBRARIES};GLEW::GLEW;${glfw3_LIBRARY};${GLFW3_LIBRARY};glfw;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H}) +cxx_executable_with_flags(${VIEWER_NAME} "Apps" "${cxx_default}" "MVS;${OPENGL_LIBRARIES};${GLEW_LIBRARY};${GLFW_STATIC_LIBRARIES};GLEW::GLEW;${glfw3_LIBRARY};${GLFW3_LIBRARY};glfw;${OpenMVS_EXTRA_LIBS}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H}) # Manually set Common.h as the precompiled header -set_target_pch(${VIEWER_NAME} Common.h) +IF(CMAKE_VERSION VERSION_GREATER_EQUAL 3.16.0) + TARGET_PRECOMPILE_HEADERS(${VIEWER_NAME} PRIVATE "Common.h") +endif() # Install INSTALL(TARGETS ${VIEWER_NAME} diff --git a/build/Cotire.cmake b/build/Cotire.cmake deleted file mode 100644 index 97275d649..000000000 --- a/build/Cotire.cmake +++ /dev/null @@ -1,4190 +0,0 @@ -# - cotire (compile time reducer) -# -# See the cotire manual for usage hints. -# -#============================================================================= -# Copyright 2012-2018 Sascha Kratky -# -# Permission is hereby granted, free of charge, to any person -# obtaining a copy of this software and associated documentation -# files (the "Software"), to deal in the Software without -# restriction, including without limitation the rights to use, -# copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the -# Software is furnished to do so, subject to the following -# conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -# OTHER DEALINGS IN THE SOFTWARE. -#============================================================================= - -if(__COTIRE_INCLUDED) - return() -endif() -set(__COTIRE_INCLUDED TRUE) - -# call cmake_minimum_required, but prevent modification of the CMake policy stack in include mode -# cmake_minimum_required also sets the policy version as a side effect, which we have to avoid -if (NOT CMAKE_SCRIPT_MODE_FILE) - cmake_policy(PUSH) -endif() -cmake_minimum_required(VERSION 2.8.12) -if (NOT CMAKE_SCRIPT_MODE_FILE) - cmake_policy(POP) -endif() - -set (COTIRE_CMAKE_MODULE_FILE "${CMAKE_CURRENT_LIST_FILE}") -set (COTIRE_CMAKE_MODULE_VERSION "1.8.0") - -# activate select policies -if (POLICY CMP0025) - # Compiler id for Apple Clang is now AppleClang - cmake_policy(SET CMP0025 NEW) -endif() - -if (POLICY CMP0026) - # disallow use of the LOCATION target property - cmake_policy(SET CMP0026 NEW) -endif() - -if (POLICY CMP0038) - # targets may not link directly to themselves - cmake_policy(SET CMP0038 NEW) -endif() - -if (POLICY CMP0039) - # utility targets may not have link dependencies - cmake_policy(SET CMP0039 NEW) -endif() - -if (POLICY CMP0040) - # target in the TARGET signature of add_custom_command() must exist - cmake_policy(SET CMP0040 NEW) -endif() - -if (POLICY CMP0045) - # error on non-existent target in get_target_property - cmake_policy(SET CMP0045 NEW) -endif() - -if (POLICY CMP0046) - # error on non-existent dependency in add_dependencies - cmake_policy(SET CMP0046 NEW) -endif() - -if (POLICY CMP0049) - # do not expand variables in target source entries - cmake_policy(SET CMP0049 NEW) -endif() - -if (POLICY CMP0050) - # disallow add_custom_command SOURCE signatures - cmake_policy(SET CMP0050 NEW) -endif() - -if (POLICY CMP0051) - # include TARGET_OBJECTS expressions in a target's SOURCES property - cmake_policy(SET CMP0051 NEW) -endif() - -if (POLICY CMP0053) - # simplify variable reference and escape sequence evaluation - cmake_policy(SET CMP0053 NEW) -endif() - -if (POLICY CMP0054) - # only interpret if() arguments as variables or keywords when unquoted - cmake_policy(SET CMP0054 NEW) -endif() - -if (POLICY CMP0055) - # strict checking for break() command - cmake_policy(SET CMP0055 NEW) -endif() - -include(CMakeParseArguments) -include(ProcessorCount) - -function (cotire_get_configuration_types _configsVar) - set (_configs "") - if (CMAKE_CONFIGURATION_TYPES) - list (APPEND _configs ${CMAKE_CONFIGURATION_TYPES}) - endif() - if (CMAKE_BUILD_TYPE) - list (APPEND _configs "${CMAKE_BUILD_TYPE}") - endif() - if (_configs) - list (REMOVE_DUPLICATES _configs) - set (${_configsVar} ${_configs} PARENT_SCOPE) - else() - set (${_configsVar} "None" PARENT_SCOPE) - endif() -endfunction() - -function (cotire_get_source_file_extension _sourceFile _extVar) - # get_filename_component returns extension from first occurrence of . in file name - # this function computes the extension from last occurrence of . in file name - string (FIND "${_sourceFile}" "." _index REVERSE) - if (_index GREATER -1) - math (EXPR _index "${_index} + 1") - string (SUBSTRING "${_sourceFile}" ${_index} -1 _sourceExt) - else() - set (_sourceExt "") - endif() - set (${_extVar} "${_sourceExt}" PARENT_SCOPE) -endfunction() - -macro (cotire_check_is_path_relative_to _path _isRelativeVar) - set (${_isRelativeVar} FALSE) - if (IS_ABSOLUTE "${_path}") - foreach (_dir ${ARGN}) - file (RELATIVE_PATH _relPath "${_dir}" "${_path}") - if (NOT _relPath OR (NOT IS_ABSOLUTE "${_relPath}" AND NOT "${_relPath}" MATCHES "^\\.\\.")) - set (${_isRelativeVar} TRUE) - break() - endif() - endforeach() - endif() -endmacro() - -function (cotire_filter_language_source_files _language _target _sourceFilesVar _excludedSourceFilesVar _cotiredSourceFilesVar) - if (CMAKE_${_language}_SOURCE_FILE_EXTENSIONS) - set (_languageExtensions "${CMAKE_${_language}_SOURCE_FILE_EXTENSIONS}") - else() - set (_languageExtensions "") - endif() - if (CMAKE_${_language}_IGNORE_EXTENSIONS) - set (_ignoreExtensions "${CMAKE_${_language}_IGNORE_EXTENSIONS}") - else() - set (_ignoreExtensions "") - endif() - if (COTIRE_UNITY_SOURCE_EXCLUDE_EXTENSIONS) - set (_excludeExtensions "${COTIRE_UNITY_SOURCE_EXCLUDE_EXTENSIONS}") - else() - set (_excludeExtensions "") - endif() - if (COTIRE_DEBUG AND _languageExtensions) - message (STATUS "${_language} source file extensions: ${_languageExtensions}") - endif() - if (COTIRE_DEBUG AND _ignoreExtensions) - message (STATUS "${_language} ignore extensions: ${_ignoreExtensions}") - endif() - if (COTIRE_DEBUG AND _excludeExtensions) - message (STATUS "${_language} exclude extensions: ${_excludeExtensions}") - endif() - if (CMAKE_VERSION VERSION_LESS "3.1.0") - set (_allSourceFiles ${ARGN}) - else() - # as of CMake 3.1 target sources may contain generator expressions - # since we cannot obtain required property information about source files added - # through generator expressions at configure time, we filter them out - string (GENEX_STRIP "${ARGN}" _allSourceFiles) - endif() - set (_filteredSourceFiles "") - set (_excludedSourceFiles "") - foreach (_sourceFile ${_allSourceFiles}) - get_source_file_property(_sourceIsHeaderOnly "${_sourceFile}" HEADER_FILE_ONLY) - get_source_file_property(_sourceIsExternal "${_sourceFile}" EXTERNAL_OBJECT) - get_source_file_property(_sourceIsSymbolic "${_sourceFile}" SYMBOLIC) - if (NOT _sourceIsHeaderOnly AND NOT _sourceIsExternal AND NOT _sourceIsSymbolic) - cotire_get_source_file_extension("${_sourceFile}" _sourceExt) - if (_sourceExt) - list (FIND _ignoreExtensions "${_sourceExt}" _ignoreIndex) - if (_ignoreIndex LESS 0) - list (FIND _excludeExtensions "${_sourceExt}" _excludeIndex) - if (_excludeIndex GREATER -1) - list (APPEND _excludedSourceFiles "${_sourceFile}") - else() - list (FIND _languageExtensions "${_sourceExt}" _sourceIndex) - if (_sourceIndex GREATER -1) - # consider source file unless it is excluded explicitly - get_source_file_property(_sourceIsExcluded "${_sourceFile}" COTIRE_EXCLUDED) - if (_sourceIsExcluded) - list (APPEND _excludedSourceFiles "${_sourceFile}") - else() - list (APPEND _filteredSourceFiles "${_sourceFile}") - endif() - else() - get_source_file_property(_sourceLanguage "${_sourceFile}" LANGUAGE) - if ("${_sourceLanguage}" STREQUAL "${_language}") - # add to excluded sources, if file is not ignored and has correct language without having the correct extension - list (APPEND _excludedSourceFiles "${_sourceFile}") - endif() - endif() - endif() - endif() - endif() - endif() - endforeach() - # separate filtered source files from already cotired ones - # the COTIRE_TARGET property of a source file may be set while a target is being processed by cotire - set (_sourceFiles "") - set (_cotiredSourceFiles "") - foreach (_sourceFile ${_filteredSourceFiles}) - get_source_file_property(_sourceIsCotired "${_sourceFile}" COTIRE_TARGET) - if (_sourceIsCotired) - list (APPEND _cotiredSourceFiles "${_sourceFile}") - else() - get_source_file_property(_sourceCompileFlags "${_sourceFile}" COMPILE_FLAGS) - if (_sourceCompileFlags) - # add to excluded sources, if file has custom compile flags - list (APPEND _excludedSourceFiles "${_sourceFile}") - else() - get_source_file_property(_sourceCompileOptions "${_sourceFile}" COMPILE_OPTIONS) - if (_sourceCompileOptions) - # add to excluded sources, if file has list of custom compile options - list (APPEND _excludedSourceFiles "${_sourceFile}") - else() - list (APPEND _sourceFiles "${_sourceFile}") - endif() - endif() - endif() - endforeach() - if (COTIRE_DEBUG) - if (_sourceFiles) - message (STATUS "Filtered ${_target} ${_language} sources: ${_sourceFiles}") - endif() - if (_excludedSourceFiles) - message (STATUS "Excluded ${_target} ${_language} sources: ${_excludedSourceFiles}") - endif() - if (_cotiredSourceFiles) - message (STATUS "Cotired ${_target} ${_language} sources: ${_cotiredSourceFiles}") - endif() - endif() - set (${_sourceFilesVar} ${_sourceFiles} PARENT_SCOPE) - set (${_excludedSourceFilesVar} ${_excludedSourceFiles} PARENT_SCOPE) - set (${_cotiredSourceFilesVar} ${_cotiredSourceFiles} PARENT_SCOPE) -endfunction() - -function (cotire_get_objects_with_property_on _filteredObjectsVar _property _type) - set (_filteredObjects "") - foreach (_object ${ARGN}) - get_property(_isSet ${_type} "${_object}" PROPERTY ${_property} SET) - if (_isSet) - get_property(_propertyValue ${_type} "${_object}" PROPERTY ${_property}) - if (_propertyValue) - list (APPEND _filteredObjects "${_object}") - endif() - endif() - endforeach() - set (${_filteredObjectsVar} ${_filteredObjects} PARENT_SCOPE) -endfunction() - -function (cotire_get_objects_with_property_off _filteredObjectsVar _property _type) - set (_filteredObjects "") - foreach (_object ${ARGN}) - get_property(_isSet ${_type} "${_object}" PROPERTY ${_property} SET) - if (_isSet) - get_property(_propertyValue ${_type} "${_object}" PROPERTY ${_property}) - if (NOT _propertyValue) - list (APPEND _filteredObjects "${_object}") - endif() - endif() - endforeach() - set (${_filteredObjectsVar} ${_filteredObjects} PARENT_SCOPE) -endfunction() - -function (cotire_get_source_file_property_values _valuesVar _property) - set (_values "") - foreach (_sourceFile ${ARGN}) - get_source_file_property(_propertyValue "${_sourceFile}" ${_property}) - if (_propertyValue) - list (APPEND _values "${_propertyValue}") - endif() - endforeach() - set (${_valuesVar} ${_values} PARENT_SCOPE) -endfunction() - -function (cotire_resolve_config_properties _configurations _propertiesVar) - set (_properties "") - foreach (_property ${ARGN}) - if ("${_property}" MATCHES "") - foreach (_config ${_configurations}) - string (TOUPPER "${_config}" _upperConfig) - string (REPLACE "" "${_upperConfig}" _configProperty "${_property}") - list (APPEND _properties ${_configProperty}) - endforeach() - else() - list (APPEND _properties ${_property}) - endif() - endforeach() - set (${_propertiesVar} ${_properties} PARENT_SCOPE) -endfunction() - -function (cotire_copy_set_properties _configurations _type _source _target) - cotire_resolve_config_properties("${_configurations}" _properties ${ARGN}) - foreach (_property ${_properties}) - get_property(_isSet ${_type} ${_source} PROPERTY ${_property} SET) - if (_isSet) - get_property(_propertyValue ${_type} ${_source} PROPERTY ${_property}) - set_property(${_type} ${_target} PROPERTY ${_property} "${_propertyValue}") - endif() - endforeach() -endfunction() - -function (cotire_get_target_usage_requirements _target _config _targetRequirementsVar) - set (_targetRequirements "") - get_target_property(_librariesToProcess ${_target} LINK_LIBRARIES) - while (_librariesToProcess) - # remove from head - list (GET _librariesToProcess 0 _library) - list (REMOVE_AT _librariesToProcess 0) - if (_library MATCHES "^\\$<\\$:([A-Za-z0-9_:-]+)>$") - set (_library "${CMAKE_MATCH_1}") - elseif (_config STREQUAL "None" AND _library MATCHES "^\\$<\\$:([A-Za-z0-9_:-]+)>$") - set (_library "${CMAKE_MATCH_1}") - endif() - if (TARGET ${_library}) - list (FIND _targetRequirements ${_library} _index) - if (_index LESS 0) - list (APPEND _targetRequirements ${_library}) - # BFS traversal of transitive libraries - get_target_property(_libraries ${_library} INTERFACE_LINK_LIBRARIES) - if (_libraries) - list (APPEND _librariesToProcess ${_libraries}) - list (REMOVE_DUPLICATES _librariesToProcess) - endif() - endif() - endif() - endwhile() - set (${_targetRequirementsVar} ${_targetRequirements} PARENT_SCOPE) -endfunction() - -function (cotire_filter_compile_flags _language _flagFilter _matchedOptionsVar _unmatchedOptionsVar) - if (WIN32 AND CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel") - set (_flagPrefix "[/-]") - else() - set (_flagPrefix "--?") - endif() - set (_optionFlag "") - set (_matchedOptions "") - set (_unmatchedOptions "") - foreach (_compileFlag ${ARGN}) - if (_compileFlag) - if (_optionFlag AND NOT "${_compileFlag}" MATCHES "^${_flagPrefix}") - # option with separate argument - list (APPEND _matchedOptions "${_compileFlag}") - set (_optionFlag "") - elseif ("${_compileFlag}" MATCHES "^(${_flagPrefix})(${_flagFilter})$") - # remember option - set (_optionFlag "${CMAKE_MATCH_2}") - elseif ("${_compileFlag}" MATCHES "^(${_flagPrefix})(${_flagFilter})(.+)$") - # option with joined argument - list (APPEND _matchedOptions "${CMAKE_MATCH_3}") - set (_optionFlag "") - else() - # flush remembered option - if (_optionFlag) - list (APPEND _matchedOptions "${_optionFlag}") - set (_optionFlag "") - endif() - # add to unfiltered options - list (APPEND _unmatchedOptions "${_compileFlag}") - endif() - endif() - endforeach() - if (_optionFlag) - list (APPEND _matchedOptions "${_optionFlag}") - endif() - if (COTIRE_DEBUG AND _matchedOptions) - message (STATUS "Filter ${_flagFilter} matched: ${_matchedOptions}") - endif() - if (COTIRE_DEBUG AND _unmatchedOptions) - message (STATUS "Filter ${_flagFilter} unmatched: ${_unmatchedOptions}") - endif() - set (${_matchedOptionsVar} ${_matchedOptions} PARENT_SCOPE) - set (${_unmatchedOptionsVar} ${_unmatchedOptions} PARENT_SCOPE) -endfunction() - -function (cotire_is_target_supported _target _isSupportedVar) - if (NOT TARGET "${_target}") - set (${_isSupportedVar} FALSE PARENT_SCOPE) - return() - endif() - get_target_property(_imported ${_target} IMPORTED) - if (_imported) - set (${_isSupportedVar} FALSE PARENT_SCOPE) - return() - endif() - get_target_property(_targetType ${_target} TYPE) - if (NOT _targetType MATCHES "EXECUTABLE|(STATIC|SHARED|MODULE|OBJECT)_LIBRARY") - set (${_isSupportedVar} FALSE PARENT_SCOPE) - return() - endif() - set (${_isSupportedVar} TRUE PARENT_SCOPE) -endfunction() - -function (cotire_get_target_compile_flags _config _language _target _flagsVar) - string (TOUPPER "${_config}" _upperConfig) - # collect options from CMake language variables - set (_compileFlags "") - if (CMAKE_${_language}_FLAGS) - set (_compileFlags "${_compileFlags} ${CMAKE_${_language}_FLAGS}") - endif() - if (CMAKE_${_language}_FLAGS_${_upperConfig}) - set (_compileFlags "${_compileFlags} ${CMAKE_${_language}_FLAGS_${_upperConfig}}") - endif() - if (_target) - # add target compile flags - get_target_property(_targetflags ${_target} COMPILE_FLAGS) - if (_targetflags) - set (_compileFlags "${_compileFlags} ${_targetflags}") - endif() - endif() - if (UNIX) - separate_arguments(_compileFlags UNIX_COMMAND "${_compileFlags}") - elseif(WIN32) - separate_arguments(_compileFlags WINDOWS_COMMAND "${_compileFlags}") - else() - separate_arguments(_compileFlags) - endif() - # target compile options - if (_target) - get_target_property(_targetOptions ${_target} COMPILE_OPTIONS) - if (_targetOptions) - list (APPEND _compileFlags ${_targetOptions}) - endif() - endif() - # interface compile options from linked library targets - if (_target) - set (_linkedTargets "") - cotire_get_target_usage_requirements(${_target} ${_config} _linkedTargets) - foreach (_linkedTarget ${_linkedTargets}) - get_target_property(_targetOptions ${_linkedTarget} INTERFACE_COMPILE_OPTIONS) - if (_targetOptions) - list (APPEND _compileFlags ${_targetOptions}) - endif() - endforeach() - endif() - # handle language standard properties - if (CMAKE_${_language}_STANDARD_DEFAULT) - # used compiler supports language standard levels - if (_target) - get_target_property(_targetLanguageStandard ${_target} ${_language}_STANDARD) - if (_targetLanguageStandard) - set (_type "EXTENSION") - get_property(_isSet TARGET ${_target} PROPERTY ${_language}_EXTENSIONS SET) - if (_isSet) - get_target_property(_targetUseLanguageExtensions ${_target} ${_language}_EXTENSIONS) - if (NOT _targetUseLanguageExtensions) - set (_type "STANDARD") - endif() - endif() - if (CMAKE_${_language}${_targetLanguageStandard}_${_type}_COMPILE_OPTION) - list (APPEND _compileFlags "${CMAKE_${_language}${_targetLanguageStandard}_${_type}_COMPILE_OPTION}") - endif() - endif() - endif() - endif() - # handle the POSITION_INDEPENDENT_CODE target property - if (_target) - get_target_property(_targetPIC ${_target} POSITION_INDEPENDENT_CODE) - if (_targetPIC) - get_target_property(_targetType ${_target} TYPE) - if (_targetType STREQUAL "EXECUTABLE" AND CMAKE_${_language}_COMPILE_OPTIONS_PIE) - list (APPEND _compileFlags "${CMAKE_${_language}_COMPILE_OPTIONS_PIE}") - elseif (CMAKE_${_language}_COMPILE_OPTIONS_PIC) - list (APPEND _compileFlags "${CMAKE_${_language}_COMPILE_OPTIONS_PIC}") - endif() - endif() - endif() - # handle visibility target properties - if (_target) - get_target_property(_targetVisibility ${_target} ${_language}_VISIBILITY_PRESET) - if (_targetVisibility AND CMAKE_${_language}_COMPILE_OPTIONS_VISIBILITY) - list (APPEND _compileFlags "${CMAKE_${_language}_COMPILE_OPTIONS_VISIBILITY}${_targetVisibility}") - endif() - get_target_property(_targetVisibilityInlines ${_target} VISIBILITY_INLINES_HIDDEN) - if (_targetVisibilityInlines AND CMAKE_${_language}_COMPILE_OPTIONS_VISIBILITY_INLINES_HIDDEN) - list (APPEND _compileFlags "${CMAKE_${_language}_COMPILE_OPTIONS_VISIBILITY_INLINES_HIDDEN}") - endif() - endif() - # platform specific flags - if (APPLE) - get_target_property(_architectures ${_target} OSX_ARCHITECTURES_${_upperConfig}) - if (NOT _architectures) - get_target_property(_architectures ${_target} OSX_ARCHITECTURES) - endif() - if (_architectures) - foreach (_arch ${_architectures}) - list (APPEND _compileFlags "-arch" "${_arch}") - endforeach() - endif() - if (CMAKE_OSX_SYSROOT) - if (CMAKE_${_language}_SYSROOT_FLAG) - list (APPEND _compileFlags "${CMAKE_${_language}_SYSROOT_FLAG}" "${CMAKE_OSX_SYSROOT}") - else() - list (APPEND _compileFlags "-isysroot" "${CMAKE_OSX_SYSROOT}") - endif() - endif() - if (CMAKE_OSX_DEPLOYMENT_TARGET) - if (CMAKE_${_language}_OSX_DEPLOYMENT_TARGET_FLAG) - list (APPEND _compileFlags "${CMAKE_${_language}_OSX_DEPLOYMENT_TARGET_FLAG}${CMAKE_OSX_DEPLOYMENT_TARGET}") - else() - list (APPEND _compileFlags "-mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}") - endif() - endif() - endif() - if (COTIRE_DEBUG AND _compileFlags) - message (STATUS "Target ${_target} compile flags: ${_compileFlags}") - endif() - set (${_flagsVar} ${_compileFlags} PARENT_SCOPE) -endfunction() - -function (cotire_get_target_include_directories _config _language _target _includeDirsVar _systemIncludeDirsVar) - set (_includeDirs "") - set (_systemIncludeDirs "") - # default include dirs - if (CMAKE_INCLUDE_CURRENT_DIR) - list (APPEND _includeDirs "${CMAKE_CURRENT_BINARY_DIR}") - list (APPEND _includeDirs "${CMAKE_CURRENT_SOURCE_DIR}") - endif() - set (_targetFlags "") - cotire_get_target_compile_flags("${_config}" "${_language}" "${_target}" _targetFlags) - # parse additional include directories from target compile flags - if (CMAKE_INCLUDE_FLAG_${_language}) - string (STRIP "${CMAKE_INCLUDE_FLAG_${_language}}" _includeFlag) - string (REGEX REPLACE "^[-/]+" "" _includeFlag "${_includeFlag}") - if (_includeFlag) - set (_dirs "") - cotire_filter_compile_flags("${_language}" "${_includeFlag}" _dirs _ignore ${_targetFlags}) - if (_dirs) - list (APPEND _includeDirs ${_dirs}) - endif() - endif() - endif() - # parse additional system include directories from target compile flags - if (CMAKE_INCLUDE_SYSTEM_FLAG_${_language}) - string (STRIP "${CMAKE_INCLUDE_SYSTEM_FLAG_${_language}}" _includeFlag) - string (REGEX REPLACE "^[-/]+" "" _includeFlag "${_includeFlag}") - if (_includeFlag) - set (_dirs "") - cotire_filter_compile_flags("${_language}" "${_includeFlag}" _dirs _ignore ${_targetFlags}) - if (_dirs) - list (APPEND _systemIncludeDirs ${_dirs}) - endif() - endif() - endif() - # target include directories - get_directory_property(_dirs DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" INCLUDE_DIRECTORIES) - if (_target) - get_target_property(_targetDirs ${_target} INCLUDE_DIRECTORIES) - if (_targetDirs) - list (APPEND _dirs ${_targetDirs}) - endif() - get_target_property(_targetDirs ${_target} INTERFACE_SYSTEM_INCLUDE_DIRECTORIES) - if (_targetDirs) - list (APPEND _systemIncludeDirs ${_targetDirs}) - endif() - endif() - # interface include directories from linked library targets - if (_target) - set (_linkedTargets "") - cotire_get_target_usage_requirements(${_target} ${_config} _linkedTargets) - foreach (_linkedTarget ${_linkedTargets}) - get_target_property(_linkedTargetType ${_linkedTarget} TYPE) - if (CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE AND NOT CMAKE_VERSION VERSION_LESS "3.4.0" AND - _linkedTargetType MATCHES "(STATIC|SHARED|MODULE|OBJECT)_LIBRARY") - # CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE refers to CMAKE_CURRENT_BINARY_DIR and CMAKE_CURRENT_SOURCE_DIR - # at the time, when the target was created. These correspond to the target properties BINARY_DIR and SOURCE_DIR - # which are only available with CMake 3.4 or later. - get_target_property(_targetDirs ${_linkedTarget} BINARY_DIR) - if (_targetDirs) - list (APPEND _dirs ${_targetDirs}) - endif() - get_target_property(_targetDirs ${_linkedTarget} SOURCE_DIR) - if (_targetDirs) - list (APPEND _dirs ${_targetDirs}) - endif() - endif() - get_target_property(_targetDirs ${_linkedTarget} INTERFACE_INCLUDE_DIRECTORIES) - if (_targetDirs) - list (APPEND _dirs ${_targetDirs}) - endif() - get_target_property(_targetDirs ${_linkedTarget} INTERFACE_SYSTEM_INCLUDE_DIRECTORIES) - if (_targetDirs) - list (APPEND _systemIncludeDirs ${_targetDirs}) - endif() - endforeach() - endif() - if (dirs) - list (REMOVE_DUPLICATES _dirs) - endif() - list (LENGTH _includeDirs _projectInsertIndex) - foreach (_dir ${_dirs}) - if (CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE) - cotire_check_is_path_relative_to("${_dir}" _isRelative "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}") - if (_isRelative) - list (LENGTH _includeDirs _len) - if (_len EQUAL _projectInsertIndex) - list (APPEND _includeDirs "${_dir}") - else() - list (INSERT _includeDirs _projectInsertIndex "${_dir}") - endif() - math (EXPR _projectInsertIndex "${_projectInsertIndex} + 1") - else() - list (APPEND _includeDirs "${_dir}") - endif() - else() - list (APPEND _includeDirs "${_dir}") - endif() - endforeach() - list (REMOVE_DUPLICATES _includeDirs) - list (REMOVE_DUPLICATES _systemIncludeDirs) - if (CMAKE_${_language}_IMPLICIT_INCLUDE_DIRECTORIES) - list (REMOVE_ITEM _includeDirs ${CMAKE_${_language}_IMPLICIT_INCLUDE_DIRECTORIES}) - endif() - if (WIN32 AND NOT MINGW) - # convert Windows paths in include directories to CMake paths - if (_includeDirs) - set (_paths "") - foreach (_dir ${_includeDirs}) - file (TO_CMAKE_PATH "${_dir}" _path) - list (APPEND _paths "${_path}") - endforeach() - set (_includeDirs ${_paths}) - endif() - if (_systemIncludeDirs) - set (_paths "") - foreach (_dir ${_systemIncludeDirs}) - file (TO_CMAKE_PATH "${_dir}" _path) - list (APPEND _paths "${_path}") - endforeach() - set (_systemIncludeDirs ${_paths}) - endif() - endif() - if (COTIRE_DEBUG AND _includeDirs) - message (STATUS "Target ${_target} include dirs: ${_includeDirs}") - endif() - set (${_includeDirsVar} ${_includeDirs} PARENT_SCOPE) - if (COTIRE_DEBUG AND _systemIncludeDirs) - message (STATUS "Target ${_target} system include dirs: ${_systemIncludeDirs}") - endif() - set (${_systemIncludeDirsVar} ${_systemIncludeDirs} PARENT_SCOPE) -endfunction() - -function (cotire_get_target_export_symbol _target _exportSymbolVar) - set (_exportSymbol "") - get_target_property(_targetType ${_target} TYPE) - get_target_property(_enableExports ${_target} ENABLE_EXPORTS) - if (_targetType MATCHES "(SHARED|MODULE)_LIBRARY" OR - (_targetType STREQUAL "EXECUTABLE" AND _enableExports)) - get_target_property(_exportSymbol ${_target} DEFINE_SYMBOL) - if (NOT _exportSymbol) - set (_exportSymbol "${_target}_EXPORTS") - endif() - string (MAKE_C_IDENTIFIER "${_exportSymbol}" _exportSymbol) - endif() - set (${_exportSymbolVar} ${_exportSymbol} PARENT_SCOPE) -endfunction() - -function (cotire_get_target_compile_definitions _config _language _target _definitionsVar) - string (TOUPPER "${_config}" _upperConfig) - set (_configDefinitions "") - # CMAKE_INTDIR for multi-configuration build systems - if (NOT "${CMAKE_CFG_INTDIR}" STREQUAL ".") - list (APPEND _configDefinitions "CMAKE_INTDIR=\"${_config}\"") - endif() - # target export define symbol - cotire_get_target_export_symbol("${_target}" _defineSymbol) - if (_defineSymbol) - list (APPEND _configDefinitions "${_defineSymbol}") - endif() - # directory compile definitions - get_directory_property(_definitions DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" COMPILE_DEFINITIONS) - if (_definitions) - list (APPEND _configDefinitions ${_definitions}) - endif() - get_directory_property(_definitions DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" COMPILE_DEFINITIONS_${_upperConfig}) - if (_definitions) - list (APPEND _configDefinitions ${_definitions}) - endif() - # target compile definitions - get_target_property(_definitions ${_target} COMPILE_DEFINITIONS) - if (_definitions) - list (APPEND _configDefinitions ${_definitions}) - endif() - get_target_property(_definitions ${_target} COMPILE_DEFINITIONS_${_upperConfig}) - if (_definitions) - list (APPEND _configDefinitions ${_definitions}) - endif() - # interface compile definitions from linked library targets - set (_linkedTargets "") - cotire_get_target_usage_requirements(${_target} ${_config} _linkedTargets) - foreach (_linkedTarget ${_linkedTargets}) - get_target_property(_definitions ${_linkedTarget} INTERFACE_COMPILE_DEFINITIONS) - if (_definitions) - list (APPEND _configDefinitions ${_definitions}) - endif() - endforeach() - # parse additional compile definitions from target compile flags - # and do not look at directory compile definitions, which we already handled - set (_targetFlags "") - cotire_get_target_compile_flags("${_config}" "${_language}" "${_target}" _targetFlags) - cotire_filter_compile_flags("${_language}" "D" _definitions _ignore ${_targetFlags}) - if (_definitions) - list (APPEND _configDefinitions ${_definitions}) - endif() - list (REMOVE_DUPLICATES _configDefinitions) - if (COTIRE_DEBUG AND _configDefinitions) - message (STATUS "Target ${_target} compile definitions: ${_configDefinitions}") - endif() - set (${_definitionsVar} ${_configDefinitions} PARENT_SCOPE) -endfunction() - -function (cotire_get_target_compiler_flags _config _language _target _compilerFlagsVar) - # parse target compile flags omitting compile definitions and include directives - set (_targetFlags "") - cotire_get_target_compile_flags("${_config}" "${_language}" "${_target}" _targetFlags) - set (_flagFilter "D") - if (CMAKE_INCLUDE_FLAG_${_language}) - string (STRIP "${CMAKE_INCLUDE_FLAG_${_language}}" _includeFlag) - string (REGEX REPLACE "^[-/]+" "" _includeFlag "${_includeFlag}") - if (_includeFlag) - set (_flagFilter "${_flagFilter}|${_includeFlag}") - endif() - endif() - if (CMAKE_INCLUDE_SYSTEM_FLAG_${_language}) - string (STRIP "${CMAKE_INCLUDE_SYSTEM_FLAG_${_language}}" _includeFlag) - string (REGEX REPLACE "^[-/]+" "" _includeFlag "${_includeFlag}") - if (_includeFlag) - set (_flagFilter "${_flagFilter}|${_includeFlag}") - endif() - endif() - set (_compilerFlags "") - cotire_filter_compile_flags("${_language}" "${_flagFilter}" _ignore _compilerFlags ${_targetFlags}) - if (COTIRE_DEBUG AND _compilerFlags) - message (STATUS "Target ${_target} compiler flags: ${_compilerFlags}") - endif() - set (${_compilerFlagsVar} ${_compilerFlags} PARENT_SCOPE) -endfunction() - -function (cotire_add_sys_root_paths _pathsVar) - if (APPLE) - if (CMAKE_OSX_SYSROOT AND CMAKE_${_language}_HAS_ISYSROOT) - foreach (_path IN LISTS ${_pathsVar}) - if (IS_ABSOLUTE "${_path}") - get_filename_component(_path "${CMAKE_OSX_SYSROOT}/${_path}" ABSOLUTE) - if (EXISTS "${_path}") - list (APPEND ${_pathsVar} "${_path}") - endif() - endif() - endforeach() - endif() - endif() - set (${_pathsVar} ${${_pathsVar}} PARENT_SCOPE) -endfunction() - -function (cotire_get_source_extra_properties _sourceFile _pattern _resultVar) - set (_extraProperties ${ARGN}) - set (_result "") - if (_extraProperties) - list (FIND _extraProperties "${_sourceFile}" _index) - if (_index GREATER -1) - math (EXPR _index "${_index} + 1") - list (LENGTH _extraProperties _len) - math (EXPR _len "${_len} - 1") - foreach (_index RANGE ${_index} ${_len}) - list (GET _extraProperties ${_index} _value) - if (_value MATCHES "${_pattern}") - list (APPEND _result "${_value}") - else() - break() - endif() - endforeach() - endif() - endif() - set (${_resultVar} ${_result} PARENT_SCOPE) -endfunction() - -function (cotire_get_source_compile_definitions _config _language _sourceFile _definitionsVar) - set (_compileDefinitions "") - if (NOT CMAKE_SCRIPT_MODE_FILE) - string (TOUPPER "${_config}" _upperConfig) - get_source_file_property(_definitions "${_sourceFile}" COMPILE_DEFINITIONS) - if (_definitions) - list (APPEND _compileDefinitions ${_definitions}) - endif() - get_source_file_property(_definitions "${_sourceFile}" COMPILE_DEFINITIONS_${_upperConfig}) - if (_definitions) - list (APPEND _compileDefinitions ${_definitions}) - endif() - endif() - cotire_get_source_extra_properties("${_sourceFile}" "^[a-zA-Z0-9_]+(=.*)?$" _definitions ${ARGN}) - if (_definitions) - list (APPEND _compileDefinitions ${_definitions}) - endif() - if (COTIRE_DEBUG AND _compileDefinitions) - message (STATUS "Source ${_sourceFile} compile definitions: ${_compileDefinitions}") - endif() - set (${_definitionsVar} ${_compileDefinitions} PARENT_SCOPE) -endfunction() - -function (cotire_get_source_files_compile_definitions _config _language _definitionsVar) - set (_configDefinitions "") - foreach (_sourceFile ${ARGN}) - cotire_get_source_compile_definitions("${_config}" "${_language}" "${_sourceFile}" _sourceDefinitions) - if (_sourceDefinitions) - list (APPEND _configDefinitions "${_sourceFile}" ${_sourceDefinitions} "-") - endif() - endforeach() - set (${_definitionsVar} ${_configDefinitions} PARENT_SCOPE) -endfunction() - -function (cotire_get_source_undefs _sourceFile _property _sourceUndefsVar) - set (_sourceUndefs "") - if (NOT CMAKE_SCRIPT_MODE_FILE) - get_source_file_property(_undefs "${_sourceFile}" ${_property}) - if (_undefs) - list (APPEND _sourceUndefs ${_undefs}) - endif() - endif() - cotire_get_source_extra_properties("${_sourceFile}" "^[a-zA-Z0-9_]+$" _undefs ${ARGN}) - if (_undefs) - list (APPEND _sourceUndefs ${_undefs}) - endif() - if (COTIRE_DEBUG AND _sourceUndefs) - message (STATUS "Source ${_sourceFile} ${_property} undefs: ${_sourceUndefs}") - endif() - set (${_sourceUndefsVar} ${_sourceUndefs} PARENT_SCOPE) -endfunction() - -function (cotire_get_source_files_undefs _property _sourceUndefsVar) - set (_sourceUndefs "") - foreach (_sourceFile ${ARGN}) - cotire_get_source_undefs("${_sourceFile}" ${_property} _undefs) - if (_undefs) - list (APPEND _sourceUndefs "${_sourceFile}" ${_undefs} "-") - endif() - endforeach() - set (${_sourceUndefsVar} ${_sourceUndefs} PARENT_SCOPE) -endfunction() - -macro (cotire_set_cmd_to_prologue _cmdVar) - set (${_cmdVar} "${CMAKE_COMMAND}") - if (COTIRE_DEBUG) - list (APPEND ${_cmdVar} "--warn-uninitialized") - endif() - list (APPEND ${_cmdVar} "-DCOTIRE_BUILD_TYPE:STRING=$") - if (XCODE) - list (APPEND ${_cmdVar} "-DXCODE:BOOL=TRUE") - endif() - if (COTIRE_VERBOSE) - list (APPEND ${_cmdVar} "-DCOTIRE_VERBOSE:BOOL=ON") - elseif("${CMAKE_GENERATOR}" MATCHES "Makefiles") - list (APPEND ${_cmdVar} "-DCOTIRE_VERBOSE:BOOL=$(VERBOSE)") - endif() -endmacro() - -function (cotire_init_compile_cmd _cmdVar _language _compilerLauncher _compilerExe _compilerArg1) - if (NOT _compilerLauncher) - set (_compilerLauncher ${CMAKE_${_language}_COMPILER_LAUNCHER}) - endif() - if (NOT _compilerExe) - set (_compilerExe "${CMAKE_${_language}_COMPILER}") - endif() - if (NOT _compilerArg1) - set (_compilerArg1 ${CMAKE_${_language}_COMPILER_ARG1}) - endif() - if (WIN32) - file (TO_NATIVE_PATH "${_compilerExe}" _compilerExe) - endif() - string (STRIP "${_compilerArg1}" _compilerArg1) - if ("${CMAKE_GENERATOR}" MATCHES "Make|Ninja") - # compiler launcher is only supported for Makefile and Ninja - set (${_cmdVar} ${_compilerLauncher} "${_compilerExe}" ${_compilerArg1} PARENT_SCOPE) - else() - set (${_cmdVar} "${_compilerExe}" ${_compilerArg1} PARENT_SCOPE) - endif() -endfunction() - -macro (cotire_add_definitions_to_cmd _cmdVar _language) - foreach (_definition ${ARGN}) - if (WIN32 AND CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel") - list (APPEND ${_cmdVar} "/D${_definition}") - else() - list (APPEND ${_cmdVar} "-D${_definition}") - endif() - endforeach() -endmacro() - -function (cotire_add_includes_to_cmd _cmdVar _language _includesVar _systemIncludesVar) - set (_includeDirs ${${_includesVar}} ${${_systemIncludesVar}}) - if (_includeDirs) - list (REMOVE_DUPLICATES _includeDirs) - foreach (_include ${_includeDirs}) - if (WIN32 AND CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel") - file (TO_NATIVE_PATH "${_include}" _include) - list (APPEND ${_cmdVar} "${CMAKE_INCLUDE_FLAG_${_language}}${CMAKE_INCLUDE_FLAG_SEP_${_language}}${_include}") - else() - set (_index -1) - if ("${CMAKE_INCLUDE_SYSTEM_FLAG_${_language}}" MATCHES ".+") - list (FIND ${_systemIncludesVar} "${_include}" _index) - endif() - if (_index GREATER -1) - list (APPEND ${_cmdVar} "${CMAKE_INCLUDE_SYSTEM_FLAG_${_language}}${CMAKE_INCLUDE_FLAG_SEP_${_language}}${_include}") - else() - list (APPEND ${_cmdVar} "${CMAKE_INCLUDE_FLAG_${_language}}${CMAKE_INCLUDE_FLAG_SEP_${_language}}${_include}") - endif() - endif() - endforeach() - endif() - set (${_cmdVar} ${${_cmdVar}} PARENT_SCOPE) -endfunction() - -function (cotire_add_frameworks_to_cmd _cmdVar _language _includesVar _systemIncludesVar) - if (APPLE) - set (_frameworkDirs "") - foreach (_include ${${_includesVar}}) - if (IS_ABSOLUTE "${_include}" AND _include MATCHES "\\.framework$") - get_filename_component(_frameworkDir "${_include}" DIRECTORY) - list (APPEND _frameworkDirs "${_frameworkDir}") - endif() - endforeach() - set (_systemFrameworkDirs "") - foreach (_include ${${_systemIncludesVar}}) - if (IS_ABSOLUTE "${_include}" AND _include MATCHES "\\.framework$") - get_filename_component(_frameworkDir "${_include}" DIRECTORY) - list (APPEND _systemFrameworkDirs "${_frameworkDir}") - endif() - endforeach() - if (_systemFrameworkDirs) - list (APPEND _frameworkDirs ${_systemFrameworkDirs}) - endif() - if (_frameworkDirs) - list (REMOVE_DUPLICATES _frameworkDirs) - foreach (_frameworkDir ${_frameworkDirs}) - set (_index -1) - if ("${CMAKE_${_language}_SYSTEM_FRAMEWORK_SEARCH_FLAG}" MATCHES ".+") - list (FIND _systemFrameworkDirs "${_frameworkDir}" _index) - endif() - if (_index GREATER -1) - list (APPEND ${_cmdVar} "${CMAKE_${_language}_SYSTEM_FRAMEWORK_SEARCH_FLAG}${_frameworkDir}") - else() - list (APPEND ${_cmdVar} "${CMAKE_${_language}_FRAMEWORK_SEARCH_FLAG}${_frameworkDir}") - endif() - endforeach() - endif() - endif() - set (${_cmdVar} ${${_cmdVar}} PARENT_SCOPE) -endfunction() - -macro (cotire_add_compile_flags_to_cmd _cmdVar) - foreach (_flag ${ARGN}) - list (APPEND ${_cmdVar} "${_flag}") - endforeach() -endmacro() - -function (cotire_check_file_up_to_date _fileIsUpToDateVar _file) - if (EXISTS "${_file}") - set (_triggerFile "") - foreach (_dependencyFile ${ARGN}) - if (EXISTS "${_dependencyFile}") - # IS_NEWER_THAN returns TRUE if both files have the same timestamp - # thus we do the comparison in both directions to exclude ties - if ("${_dependencyFile}" IS_NEWER_THAN "${_file}" AND - NOT "${_file}" IS_NEWER_THAN "${_dependencyFile}") - set (_triggerFile "${_dependencyFile}") - break() - endif() - endif() - endforeach() - if (_triggerFile) - if (COTIRE_VERBOSE) - get_filename_component(_fileName "${_file}" NAME) - message (STATUS "${_fileName} update triggered by ${_triggerFile} change.") - endif() - set (${_fileIsUpToDateVar} FALSE PARENT_SCOPE) - else() - if (COTIRE_VERBOSE) - get_filename_component(_fileName "${_file}" NAME) - message (STATUS "${_fileName} is up-to-date.") - endif() - set (${_fileIsUpToDateVar} TRUE PARENT_SCOPE) - endif() - else() - if (COTIRE_VERBOSE) - get_filename_component(_fileName "${_file}" NAME) - message (STATUS "${_fileName} does not exist yet.") - endif() - set (${_fileIsUpToDateVar} FALSE PARENT_SCOPE) - endif() -endfunction() - -macro (cotire_find_closest_relative_path _headerFile _includeDirs _relPathVar) - set (${_relPathVar} "") - foreach (_includeDir ${_includeDirs}) - if (IS_DIRECTORY "${_includeDir}") - file (RELATIVE_PATH _relPath "${_includeDir}" "${_headerFile}") - if (NOT IS_ABSOLUTE "${_relPath}" AND NOT "${_relPath}" MATCHES "^\\.\\.") - string (LENGTH "${${_relPathVar}}" _closestLen) - string (LENGTH "${_relPath}" _relLen) - if (_closestLen EQUAL 0 OR _relLen LESS _closestLen) - set (${_relPathVar} "${_relPath}") - endif() - endif() - elseif ("${_includeDir}" STREQUAL "${_headerFile}") - # if path matches exactly, return short non-empty string - set (${_relPathVar} "1") - break() - endif() - endforeach() -endmacro() - -macro (cotire_check_header_file_location _headerFile _insideIncludeDirs _outsideIncludeDirs _headerIsInside) - # check header path against ignored and honored include directories - cotire_find_closest_relative_path("${_headerFile}" "${_insideIncludeDirs}" _insideRelPath) - if (_insideRelPath) - # header is inside, but could be become outside if there is a shorter outside match - cotire_find_closest_relative_path("${_headerFile}" "${_outsideIncludeDirs}" _outsideRelPath) - if (_outsideRelPath) - string (LENGTH "${_insideRelPath}" _insideRelPathLen) - string (LENGTH "${_outsideRelPath}" _outsideRelPathLen) - if (_outsideRelPathLen LESS _insideRelPathLen) - set (${_headerIsInside} FALSE) - else() - set (${_headerIsInside} TRUE) - endif() - else() - set (${_headerIsInside} TRUE) - endif() - else() - # header is outside - set (${_headerIsInside} FALSE) - endif() -endmacro() - -macro (cotire_check_ignore_header_file_path _headerFile _headerIsIgnoredVar) - if (NOT EXISTS "${_headerFile}") - set (${_headerIsIgnoredVar} TRUE) - elseif (IS_DIRECTORY "${_headerFile}") - set (${_headerIsIgnoredVar} TRUE) - elseif ("${_headerFile}" MATCHES "\\.\\.|[_-]fixed" AND "${_headerFile}" MATCHES "\\.h$") - # heuristic: ignore C headers with embedded parent directory references or "-fixed" or "_fixed" in path - # these often stem from using GCC #include_next tricks, which may break the precompiled header compilation - # with the error message "error: no include path in which to search for header.h" - set (${_headerIsIgnoredVar} TRUE) - else() - set (${_headerIsIgnoredVar} FALSE) - endif() -endmacro() - -macro (cotire_check_ignore_header_file_ext _headerFile _ignoreExtensionsVar _headerIsIgnoredVar) - # check header file extension - cotire_get_source_file_extension("${_headerFile}" _headerFileExt) - set (${_headerIsIgnoredVar} FALSE) - if (_headerFileExt) - list (FIND ${_ignoreExtensionsVar} "${_headerFileExt}" _index) - if (_index GREATER -1) - set (${_headerIsIgnoredVar} TRUE) - endif() - endif() -endmacro() - -macro (cotire_parse_line _line _headerFileVar _headerDepthVar) - if (MSVC) - # cl.exe /showIncludes produces different output, depending on the language pack used, e.g.: - # English: "Note: including file: C:\directory\file" - # German: "Hinweis: Einlesen der Datei: C:\directory\file" - # We use a very general regular expression, relying on the presence of the : characters - if (_line MATCHES "( +)([a-zA-Z]:[^:]+)$") - string (LENGTH "${CMAKE_MATCH_1}" ${_headerDepthVar}) - get_filename_component(${_headerFileVar} "${CMAKE_MATCH_2}" ABSOLUTE) - else() - set (${_headerFileVar} "") - set (${_headerDepthVar} 0) - endif() - else() - if (_line MATCHES "^(\\.+) (.*)$") - # GCC like output - string (LENGTH "${CMAKE_MATCH_1}" ${_headerDepthVar}) - if (IS_ABSOLUTE "${CMAKE_MATCH_2}") - set (${_headerFileVar} "${CMAKE_MATCH_2}") - else() - get_filename_component(${_headerFileVar} "${CMAKE_MATCH_2}" REALPATH) - endif() - else() - set (${_headerFileVar} "") - set (${_headerDepthVar} 0) - endif() - endif() -endmacro() - -function (cotire_parse_includes _language _scanOutput _ignoredIncludeDirs _honoredIncludeDirs _ignoredExtensions _selectedIncludesVar _unparsedLinesVar) - if (WIN32) - # prevent CMake macro invocation errors due to backslash characters in Windows paths - string (REPLACE "\\" "/" _scanOutput "${_scanOutput}") - endif() - # canonize slashes - string (REPLACE "//" "/" _scanOutput "${_scanOutput}") - # prevent semicolon from being interpreted as a line separator - string (REPLACE ";" "\\;" _scanOutput "${_scanOutput}") - # then separate lines - string (REGEX REPLACE "\n" ";" _scanOutput "${_scanOutput}") - list (LENGTH _scanOutput _len) - # remove duplicate lines to speed up parsing - list (REMOVE_DUPLICATES _scanOutput) - list (LENGTH _scanOutput _uniqueLen) - if (COTIRE_VERBOSE OR COTIRE_DEBUG) - message (STATUS "Scanning ${_uniqueLen} unique lines of ${_len} for includes") - if (_ignoredExtensions) - message (STATUS "Ignored extensions: ${_ignoredExtensions}") - endif() - if (_ignoredIncludeDirs) - message (STATUS "Ignored paths: ${_ignoredIncludeDirs}") - endif() - if (_honoredIncludeDirs) - message (STATUS "Included paths: ${_honoredIncludeDirs}") - endif() - endif() - set (_sourceFiles ${ARGN}) - set (_selectedIncludes "") - set (_unparsedLines "") - # stack keeps track of inside/outside project status of processed header files - set (_headerIsInsideStack "") - foreach (_line IN LISTS _scanOutput) - if (_line) - cotire_parse_line("${_line}" _headerFile _headerDepth) - if (_headerFile) - cotire_check_header_file_location("${_headerFile}" "${_ignoredIncludeDirs}" "${_honoredIncludeDirs}" _headerIsInside) - if (COTIRE_DEBUG) - message (STATUS "${_headerDepth}: ${_headerFile} ${_headerIsInside}") - endif() - # update stack - list (LENGTH _headerIsInsideStack _stackLen) - if (_headerDepth GREATER _stackLen) - math (EXPR _stackLen "${_stackLen} + 1") - foreach (_index RANGE ${_stackLen} ${_headerDepth}) - list (APPEND _headerIsInsideStack ${_headerIsInside}) - endforeach() - else() - foreach (_index RANGE ${_headerDepth} ${_stackLen}) - list (REMOVE_AT _headerIsInsideStack -1) - endforeach() - list (APPEND _headerIsInsideStack ${_headerIsInside}) - endif() - if (COTIRE_DEBUG) - message (STATUS "${_headerIsInsideStack}") - endif() - # header is a candidate if it is outside project - if (NOT _headerIsInside) - # get parent header file's inside/outside status - if (_headerDepth GREATER 1) - math (EXPR _index "${_headerDepth} - 2") - list (GET _headerIsInsideStack ${_index} _parentHeaderIsInside) - else() - set (_parentHeaderIsInside TRUE) - endif() - # select header file if parent header file is inside project - # (e.g., a project header file that includes a standard header file) - if (_parentHeaderIsInside) - cotire_check_ignore_header_file_path("${_headerFile}" _headerIsIgnored) - if (NOT _headerIsIgnored) - cotire_check_ignore_header_file_ext("${_headerFile}" _ignoredExtensions _headerIsIgnored) - if (NOT _headerIsIgnored) - list (APPEND _selectedIncludes "${_headerFile}") - else() - # fix header's inside status on stack, it is ignored by extension now - list (REMOVE_AT _headerIsInsideStack -1) - list (APPEND _headerIsInsideStack TRUE) - endif() - endif() - if (COTIRE_DEBUG) - message (STATUS "${_headerFile} ${_ignoredExtensions} ${_headerIsIgnored}") - endif() - endif() - endif() - else() - if (MSVC) - # for cl.exe do not keep unparsed lines which solely consist of a source file name - string (FIND "${_sourceFiles}" "${_line}" _index) - if (_index LESS 0) - list (APPEND _unparsedLines "${_line}") - endif() - else() - list (APPEND _unparsedLines "${_line}") - endif() - endif() - endif() - endforeach() - list (REMOVE_DUPLICATES _selectedIncludes) - set (${_selectedIncludesVar} ${_selectedIncludes} PARENT_SCOPE) - set (${_unparsedLinesVar} ${_unparsedLines} PARENT_SCOPE) -endfunction() - -function (cotire_scan_includes _includesVar) - set(_options "") - set(_oneValueArgs COMPILER_ID COMPILER_EXECUTABLE COMPILER_ARG1 COMPILER_VERSION LANGUAGE UNPARSED_LINES SCAN_RESULT) - set(_multiValueArgs COMPILE_DEFINITIONS COMPILE_FLAGS INCLUDE_DIRECTORIES SYSTEM_INCLUDE_DIRECTORIES - IGNORE_PATH INCLUDE_PATH IGNORE_EXTENSIONS INCLUDE_PRIORITY_PATH COMPILER_LAUNCHER) - cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) - set (_sourceFiles ${_option_UNPARSED_ARGUMENTS}) - if (NOT _option_LANGUAGE) - set (_option_LANGUAGE "CXX") - endif() - if (NOT _option_COMPILER_ID) - set (_option_COMPILER_ID "${CMAKE_${_option_LANGUAGE}_ID}") - endif() - if (NOT _option_COMPILER_VERSION) - set (_option_COMPILER_VERSION "${CMAKE_${_option_LANGUAGE}_COMPILER_VERSION}") - endif() - cotire_init_compile_cmd(_cmd "${_option_LANGUAGE}" "${_option_COMPILER_LAUNCHER}" "${_option_COMPILER_EXECUTABLE}" "${_option_COMPILER_ARG1}") - cotire_add_definitions_to_cmd(_cmd "${_option_LANGUAGE}" ${_option_COMPILE_DEFINITIONS}) - cotire_add_compile_flags_to_cmd(_cmd ${_option_COMPILE_FLAGS}) - cotire_add_includes_to_cmd(_cmd "${_option_LANGUAGE}" _option_INCLUDE_DIRECTORIES _option_SYSTEM_INCLUDE_DIRECTORIES) - cotire_add_frameworks_to_cmd(_cmd "${_option_LANGUAGE}" _option_INCLUDE_DIRECTORIES _option_SYSTEM_INCLUDE_DIRECTORIES) - cotire_add_makedep_flags("${_option_LANGUAGE}" "${_option_COMPILER_ID}" "${_option_COMPILER_VERSION}" _cmd) - # only consider existing source files for scanning - set (_existingSourceFiles "") - foreach (_sourceFile ${_sourceFiles}) - if (EXISTS "${_sourceFile}") - list (APPEND _existingSourceFiles "${_sourceFile}") - endif() - endforeach() - if (NOT _existingSourceFiles) - set (${_includesVar} "" PARENT_SCOPE) - return() - endif() - # add source files to be scanned - if (WIN32) - foreach (_sourceFile ${_existingSourceFiles}) - file (TO_NATIVE_PATH "${_sourceFile}" _sourceFileNative) - list (APPEND _cmd "${_sourceFileNative}") - endforeach() - else() - list (APPEND _cmd ${_existingSourceFiles}) - endif() - if (COTIRE_VERBOSE) - message (STATUS "execute_process: ${_cmd}") - endif() - if (MSVC_IDE OR _option_COMPILER_ID MATCHES "MSVC") - # cl.exe messes with the output streams unless the environment variable VS_UNICODE_OUTPUT is cleared - unset (ENV{VS_UNICODE_OUTPUT}) - endif() - execute_process( - COMMAND ${_cmd} - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - RESULT_VARIABLE _result - OUTPUT_QUIET - ERROR_VARIABLE _output) - if (_result) - message (STATUS "Result ${_result} scanning includes of ${_existingSourceFiles}.") - endif() - cotire_parse_includes( - "${_option_LANGUAGE}" "${_output}" - "${_option_IGNORE_PATH}" "${_option_INCLUDE_PATH}" - "${_option_IGNORE_EXTENSIONS}" - _includes _unparsedLines - ${_sourceFiles}) - if (_option_INCLUDE_PRIORITY_PATH) - set (_sortedIncludes "") - foreach (_priorityPath ${_option_INCLUDE_PRIORITY_PATH}) - foreach (_include ${_includes}) - string (FIND ${_include} ${_priorityPath} _position) - if (_position GREATER -1) - list (APPEND _sortedIncludes ${_include}) - endif() - endforeach() - endforeach() - if (_sortedIncludes) - list (INSERT _includes 0 ${_sortedIncludes}) - list (REMOVE_DUPLICATES _includes) - endif() - endif() - set (${_includesVar} ${_includes} PARENT_SCOPE) - if (_option_UNPARSED_LINES) - set (${_option_UNPARSED_LINES} ${_unparsedLines} PARENT_SCOPE) - endif() - if (_option_SCAN_RESULT) - set (${_option_SCAN_RESULT} ${_result} PARENT_SCOPE) - endif() -endfunction() - -macro (cotire_append_undefs _contentsVar) - set (_undefs ${ARGN}) - if (_undefs) - list (REMOVE_DUPLICATES _undefs) - foreach (_definition ${_undefs}) - list (APPEND ${_contentsVar} "#undef ${_definition}") - endforeach() - endif() -endmacro() - -macro (cotire_comment_str _language _commentText _commentVar) - if ("${_language}" STREQUAL "CMAKE") - set (${_commentVar} "# ${_commentText}") - else() - set (${_commentVar} "/* ${_commentText} */") - endif() -endmacro() - -function (cotire_write_file _language _file _contents _force) - get_filename_component(_moduleName "${COTIRE_CMAKE_MODULE_FILE}" NAME) - cotire_comment_str("${_language}" "${_moduleName} ${COTIRE_CMAKE_MODULE_VERSION} generated file" _header1) - cotire_comment_str("${_language}" "${_file}" _header2) - set (_contents "${_header1}\n${_header2}\n${_contents}") - if (COTIRE_DEBUG) - message (STATUS "${_contents}") - endif() - if (_force OR NOT EXISTS "${_file}") - file (WRITE "${_file}" "${_contents}") - else() - file (READ "${_file}" _oldContents) - if (NOT "${_oldContents}" STREQUAL "${_contents}") - file (WRITE "${_file}" "${_contents}") - else() - if (COTIRE_DEBUG) - message (STATUS "${_file} unchanged") - endif() - endif() - endif() -endfunction() - -function (cotire_generate_unity_source _unityFile) - set(_options "") - set(_oneValueArgs LANGUAGE) - set(_multiValueArgs - DEPENDS SOURCES_COMPILE_DEFINITIONS - PRE_UNDEFS SOURCES_PRE_UNDEFS POST_UNDEFS SOURCES_POST_UNDEFS PROLOGUE EPILOGUE) - cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) - if (_option_DEPENDS) - cotire_check_file_up_to_date(_unityFileIsUpToDate "${_unityFile}" ${_option_DEPENDS}) - if (_unityFileIsUpToDate) - return() - endif() - endif() - set (_sourceFiles ${_option_UNPARSED_ARGUMENTS}) - if (NOT _option_PRE_UNDEFS) - set (_option_PRE_UNDEFS "") - endif() - if (NOT _option_SOURCES_PRE_UNDEFS) - set (_option_SOURCES_PRE_UNDEFS "") - endif() - if (NOT _option_POST_UNDEFS) - set (_option_POST_UNDEFS "") - endif() - if (NOT _option_SOURCES_POST_UNDEFS) - set (_option_SOURCES_POST_UNDEFS "") - endif() - set (_contents "") - if (_option_PROLOGUE) - list (APPEND _contents ${_option_PROLOGUE}) - endif() - if (_option_LANGUAGE AND _sourceFiles) - if ("${_option_LANGUAGE}" STREQUAL "CXX") - list (APPEND _contents "#ifdef __cplusplus") - elseif ("${_option_LANGUAGE}" STREQUAL "C") - list (APPEND _contents "#ifndef __cplusplus") - endif() - endif() - set (_compileUndefinitions "") - foreach (_sourceFile ${_sourceFiles}) - cotire_get_source_compile_definitions( - "${_option_CONFIGURATION}" "${_option_LANGUAGE}" "${_sourceFile}" _compileDefinitions - ${_option_SOURCES_COMPILE_DEFINITIONS}) - cotire_get_source_undefs("${_sourceFile}" COTIRE_UNITY_SOURCE_PRE_UNDEFS _sourcePreUndefs ${_option_SOURCES_PRE_UNDEFS}) - cotire_get_source_undefs("${_sourceFile}" COTIRE_UNITY_SOURCE_POST_UNDEFS _sourcePostUndefs ${_option_SOURCES_POST_UNDEFS}) - if (_option_PRE_UNDEFS) - list (APPEND _compileUndefinitions ${_option_PRE_UNDEFS}) - endif() - if (_sourcePreUndefs) - list (APPEND _compileUndefinitions ${_sourcePreUndefs}) - endif() - if (_compileUndefinitions) - cotire_append_undefs(_contents ${_compileUndefinitions}) - set (_compileUndefinitions "") - endif() - if (_sourcePostUndefs) - list (APPEND _compileUndefinitions ${_sourcePostUndefs}) - endif() - if (_option_POST_UNDEFS) - list (APPEND _compileUndefinitions ${_option_POST_UNDEFS}) - endif() - foreach (_definition ${_compileDefinitions}) - if (_definition MATCHES "^([a-zA-Z0-9_]+)=(.+)$") - list (APPEND _contents "#define ${CMAKE_MATCH_1} ${CMAKE_MATCH_2}") - list (INSERT _compileUndefinitions 0 "${CMAKE_MATCH_1}") - else() - list (APPEND _contents "#define ${_definition}") - list (INSERT _compileUndefinitions 0 "${_definition}") - endif() - endforeach() - # use absolute path as source file location - get_filename_component(_sourceFileLocation "${_sourceFile}" ABSOLUTE) - if (WIN32) - file (TO_NATIVE_PATH "${_sourceFileLocation}" _sourceFileLocation) - endif() - list (APPEND _contents "#include \"${_sourceFileLocation}\"") - endforeach() - if (_compileUndefinitions) - cotire_append_undefs(_contents ${_compileUndefinitions}) - set (_compileUndefinitions "") - endif() - if (_option_LANGUAGE AND _sourceFiles) - list (APPEND _contents "#endif") - endif() - if (_option_EPILOGUE) - list (APPEND _contents ${_option_EPILOGUE}) - endif() - list (APPEND _contents "") - string (REPLACE ";" "\n" _contents "${_contents}") - if (COTIRE_VERBOSE) - message ("${_contents}") - endif() - cotire_write_file("${_option_LANGUAGE}" "${_unityFile}" "${_contents}" TRUE) -endfunction() - -function (cotire_generate_prefix_header _prefixFile) - set(_options "") - set(_oneValueArgs LANGUAGE COMPILER_EXECUTABLE COMPILER_ARG1 COMPILER_ID COMPILER_VERSION) - set(_multiValueArgs DEPENDS COMPILE_DEFINITIONS COMPILE_FLAGS - INCLUDE_DIRECTORIES SYSTEM_INCLUDE_DIRECTORIES IGNORE_PATH INCLUDE_PATH - IGNORE_EXTENSIONS INCLUDE_PRIORITY_PATH COMPILER_LAUNCHER) - cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) - if (NOT _option_COMPILER_ID) - set (_option_COMPILER_ID "${CMAKE_${_option_LANGUAGE}_ID}") - endif() - if (NOT _option_COMPILER_VERSION) - set (_option_COMPILER_VERSION "${CMAKE_${_option_LANGUAGE}_COMPILER_VERSION}") - endif() - if (_option_DEPENDS) - cotire_check_file_up_to_date(_prefixFileIsUpToDate "${_prefixFile}" ${_option_DEPENDS}) - if (_prefixFileIsUpToDate) - # create empty log file - set (_unparsedLinesFile "${_prefixFile}.log") - file (WRITE "${_unparsedLinesFile}" "") - return() - endif() - endif() - set (_prologue "") - set (_epilogue "") - if (_option_COMPILER_ID MATCHES "Clang") - set (_prologue "#pragma clang system_header") - elseif (_option_COMPILER_ID MATCHES "GNU") - set (_prologue "#pragma GCC system_header") - elseif (_option_COMPILER_ID MATCHES "MSVC") - set (_prologue "#pragma warning(push, 0)") - set (_epilogue "#pragma warning(pop)") - elseif (_option_COMPILER_ID MATCHES "Intel") - # Intel compiler requires hdrstop pragma to stop generating PCH file - set (_epilogue "#pragma hdrstop") - endif() - set (_sourceFiles ${_option_UNPARSED_ARGUMENTS}) - cotire_scan_includes(_selectedHeaders ${_sourceFiles} - LANGUAGE "${_option_LANGUAGE}" - COMPILER_LAUNCHER "${_option_COMPILER_LAUNCHER}" - COMPILER_EXECUTABLE "${_option_COMPILER_EXECUTABLE}" - COMPILER_ARG1 "${_option_COMPILER_ARG1}" - COMPILER_ID "${_option_COMPILER_ID}" - COMPILER_VERSION "${_option_COMPILER_VERSION}" - COMPILE_DEFINITIONS ${_option_COMPILE_DEFINITIONS} - COMPILE_FLAGS ${_option_COMPILE_FLAGS} - INCLUDE_DIRECTORIES ${_option_INCLUDE_DIRECTORIES} - SYSTEM_INCLUDE_DIRECTORIES ${_option_SYSTEM_INCLUDE_DIRECTORIES} - IGNORE_PATH ${_option_IGNORE_PATH} - INCLUDE_PATH ${_option_INCLUDE_PATH} - IGNORE_EXTENSIONS ${_option_IGNORE_EXTENSIONS} - INCLUDE_PRIORITY_PATH ${_option_INCLUDE_PRIORITY_PATH} - UNPARSED_LINES _unparsedLines - SCAN_RESULT _scanResult) - cotire_generate_unity_source("${_prefixFile}" - PROLOGUE ${_prologue} EPILOGUE ${_epilogue} LANGUAGE "${_option_LANGUAGE}" ${_selectedHeaders}) - set (_unparsedLinesFile "${_prefixFile}.log") - if (_unparsedLines) - if (COTIRE_VERBOSE OR _scanResult OR NOT _selectedHeaders) - list (LENGTH _unparsedLines _skippedLineCount) - if (WIN32) - file (TO_NATIVE_PATH "${_unparsedLinesFile}" _unparsedLinesLogPath) - else() - set (_unparsedLinesLogPath "${_unparsedLinesFile}") - endif() - message (STATUS "${_skippedLineCount} line(s) skipped, see ${_unparsedLinesLogPath}") - endif() - string (REPLACE ";" "\n" _unparsedLines "${_unparsedLines}") - endif() - file (WRITE "${_unparsedLinesFile}" "${_unparsedLines}\n") -endfunction() - -function (cotire_add_makedep_flags _language _compilerID _compilerVersion _flagsVar) - set (_flags ${${_flagsVar}}) - if (_compilerID MATCHES "MSVC") - # cl.exe options used - # /nologo suppresses display of sign-on banner - # /TC treat all files named on the command line as C source files - # /TP treat all files named on the command line as C++ source files - # /EP preprocess to stdout without #line directives - # /showIncludes list include files - set (_sourceFileTypeC "/TC") - set (_sourceFileTypeCXX "/TP") - if (_flags) - # append to list - list (APPEND _flags /nologo "${_sourceFileType${_language}}" /EP /showIncludes) - else() - # return as a flag string - set (_flags "${_sourceFileType${_language}} /EP /showIncludes") - endif() - elseif (_compilerID MATCHES "GNU") - # GCC options used - # -H print the name of each header file used - # -E invoke preprocessor - # -fdirectives-only do not expand macros, requires GCC >= 4.3 - if (_flags) - # append to list - list (APPEND _flags -H -E) - if (NOT "${_compilerVersion}" VERSION_LESS "4.3.0") - list (APPEND _flags -fdirectives-only) - endif() - else() - # return as a flag string - set (_flags "-H -E") - if (NOT "${_compilerVersion}" VERSION_LESS "4.3.0") - set (_flags "${_flags} -fdirectives-only") - endif() - endif() - elseif (_compilerID MATCHES "Clang") - if (UNIX) - # Clang options used - # -H print the name of each header file used - # -E invoke preprocessor - # -fno-color-diagnostics do not print diagnostics in color - # -Eonly just run preprocessor, no output - if (_flags) - # append to list - list (APPEND _flags -H -E -fno-color-diagnostics -Xclang -Eonly) - else() - # return as a flag string - set (_flags "-H -E -fno-color-diagnostics -Xclang -Eonly") - endif() - elseif (WIN32) - # Clang-cl.exe options used - # /TC treat all files named on the command line as C source files - # /TP treat all files named on the command line as C++ source files - # /EP preprocess to stdout without #line directives - # -H print the name of each header file used - # -fno-color-diagnostics do not print diagnostics in color - # -Eonly just run preprocessor, no output - set (_sourceFileTypeC "/TC") - set (_sourceFileTypeCXX "/TP") - if (_flags) - # append to list - list (APPEND _flags "${_sourceFileType${_language}}" /EP -fno-color-diagnostics -Xclang -H -Xclang -Eonly) - else() - # return as a flag string - set (_flags "${_sourceFileType${_language}} /EP -fno-color-diagnostics -Xclang -H -Xclang -Eonly") - endif() - endif() - elseif (_compilerID MATCHES "Intel") - if (WIN32) - # Windows Intel options used - # /nologo do not display compiler version information - # /QH display the include file order - # /EP preprocess to stdout, omitting #line directives - # /TC process all source or unrecognized file types as C source files - # /TP process all source or unrecognized file types as C++ source files - set (_sourceFileTypeC "/TC") - set (_sourceFileTypeCXX "/TP") - if (_flags) - # append to list - list (APPEND _flags /nologo "${_sourceFileType${_language}}" /EP /QH) - else() - # return as a flag string - set (_flags "${_sourceFileType${_language}} /EP /QH") - endif() - else() - # Linux / Mac OS X Intel options used - # -H print the name of each header file used - # -EP preprocess to stdout, omitting #line directives - # -Kc++ process all source or unrecognized file types as C++ source files - if (_flags) - # append to list - if ("${_language}" STREQUAL "CXX") - list (APPEND _flags -Kc++) - endif() - list (APPEND _flags -H -EP) - else() - # return as a flag string - if ("${_language}" STREQUAL "CXX") - set (_flags "-Kc++ ") - endif() - set (_flags "${_flags}-H -EP") - endif() - endif() - else() - message (FATAL_ERROR "cotire: unsupported ${_language} compiler ${_compilerID} version ${_compilerVersion}.") - endif() - set (${_flagsVar} ${_flags} PARENT_SCOPE) -endfunction() - -function (cotire_add_pch_compilation_flags _language _compilerID _compilerVersion _prefixFile _pchFile _hostFile _flagsVar) - set (_flags ${${_flagsVar}}) - if (_compilerID MATCHES "MSVC") - file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative) - file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative) - file (TO_NATIVE_PATH "${_hostFile}" _hostFileNative) - # cl.exe options used - # /Yc creates a precompiled header file - # /Fp specifies precompiled header binary file name - # /FI forces inclusion of file - # /TC treat all files named on the command line as C source files - # /TP treat all files named on the command line as C++ source files - # /Zs syntax check only - # /Zm precompiled header memory allocation scaling factor - set (_sourceFileTypeC "/TC") - set (_sourceFileTypeCXX "/TP") - if (_flags) - # append to list - list (APPEND _flags /nologo "${_sourceFileType${_language}}" - "/Yc${_prefixFileNative}" "/Fp${_pchFileNative}" "/FI${_prefixFileNative}" /Zs "${_hostFileNative}") - if (COTIRE_PCH_MEMORY_SCALING_FACTOR) - list (APPEND _flags "/Zm${COTIRE_PCH_MEMORY_SCALING_FACTOR}") - endif() - else() - # return as a flag string - set (_flags "/Yc\"${_prefixFileNative}\" /Fp\"${_pchFileNative}\" /FI\"${_prefixFileNative}\"") - if (COTIRE_PCH_MEMORY_SCALING_FACTOR) - set (_flags "${_flags} /Zm${COTIRE_PCH_MEMORY_SCALING_FACTOR}") - endif() - endif() - elseif (_compilerID MATCHES "GNU") - # GCC options used - # -x specify the source language - # -c compile but do not link - # -o place output in file - # note that we cannot use -w to suppress all warnings upon pre-compiling, because turning off a warning may - # alter compile flags as a side effect (e.g., -Wwrite-string implies -fconst-strings) - set (_xLanguage_C "c-header") - set (_xLanguage_CXX "c++-header") - if (_flags) - # append to list - list (APPEND _flags -x "${_xLanguage_${_language}}" -c "${_prefixFile}" -o "${_pchFile}") - else() - # return as a flag string - set (_flags "-x ${_xLanguage_${_language}} -c \"${_prefixFile}\" -o \"${_pchFile}\"") - endif() - elseif (_compilerID MATCHES "Clang") - if (UNIX) - # Clang options used - # -x specify the source language - # -c compile but do not link - # -o place output in file - # -fno-pch-timestamp disable inclusion of timestamp in precompiled headers (clang 4.0.0+) - set (_xLanguage_C "c-header") - set (_xLanguage_CXX "c++-header") - if (_flags) - # append to list - list (APPEND _flags -x "${_xLanguage_${_language}}" -c "${_prefixFile}" -o "${_pchFile}") - if (NOT "${_compilerVersion}" VERSION_LESS "4.0.0") - list (APPEND _flags -Xclang -fno-pch-timestamp) - endif() - else() - # return as a flag string - set (_flags "-x ${_xLanguage_${_language}} -c \"${_prefixFile}\" -o \"${_pchFile}\"") - if (NOT "${_compilerVersion}" VERSION_LESS "4.0.0") - set (_flags "${_flags} -Xclang -fno-pch-timestamp") - endif() - endif() - elseif (WIN32) - # Clang-cl.exe options used - # /Yc creates a precompiled header file - # /Fp specifies precompiled header binary file name - # /FI forces inclusion of file - # /Zs syntax check only - # /TC treat all files named on the command line as C source files - # /TP treat all files named on the command line as C++ source files - set (_sourceFileTypeC "/TC") - set (_sourceFileTypeCXX "/TP") - if (_flags) - # append to list - list (APPEND _flags "${_sourceFileType${_language}}" - "/Yc${_prefixFile}" "/Fp${_pchFile}" "/FI${_prefixFile}" /Zs "${_hostFile}") - else() - # return as a flag string - set (_flags "/Yc\"${_prefixFile}\" /Fp\"${_pchFile}\" /FI\"${_prefixFile}\"") - endif() - endif() - elseif (_compilerID MATCHES "Intel") - if (WIN32) - file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative) - file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative) - file (TO_NATIVE_PATH "${_hostFile}" _hostFileNative) - # Windows Intel options used - # /nologo do not display compiler version information - # /Yc create a precompiled header (PCH) file - # /Fp specify a path or file name for precompiled header files - # /FI tells the preprocessor to include a specified file name as the header file - # /TC process all source or unrecognized file types as C source files - # /TP process all source or unrecognized file types as C++ source files - # /Zs syntax check only - # /Wpch-messages enable diagnostics related to pre-compiled headers (requires Intel XE 2013 Update 2) - set (_sourceFileTypeC "/TC") - set (_sourceFileTypeCXX "/TP") - if (_flags) - # append to list - list (APPEND _flags /nologo "${_sourceFileType${_language}}" - "/Yc" "/Fp${_pchFileNative}" "/FI${_prefixFileNative}" /Zs "${_hostFileNative}") - if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0") - list (APPEND _flags "/Wpch-messages") - endif() - else() - # return as a flag string - set (_flags "/Yc /Fp\"${_pchFileNative}\" /FI\"${_prefixFileNative}\"") - if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0") - set (_flags "${_flags} /Wpch-messages") - endif() - endif() - else() - # Linux / Mac OS X Intel options used - # -pch-dir location for precompiled header files - # -pch-create name of the precompiled header (PCH) to create - # -Kc++ process all source or unrecognized file types as C++ source files - # -fsyntax-only check only for correct syntax - # -Wpch-messages enable diagnostics related to pre-compiled headers (requires Intel XE 2013 Update 2) - get_filename_component(_pchDir "${_pchFile}" DIRECTORY) - get_filename_component(_pchName "${_pchFile}" NAME) - set (_xLanguage_C "c-header") - set (_xLanguage_CXX "c++-header") - set (_pchSuppressMessages FALSE) - if ("${CMAKE_${_language}_FLAGS}" MATCHES ".*-Wno-pch-messages.*") - set(_pchSuppressMessages TRUE) - endif() - if (_flags) - # append to list - if ("${_language}" STREQUAL "CXX") - list (APPEND _flags -Kc++) - endif() - list (APPEND _flags -include "${_prefixFile}" -pch-dir "${_pchDir}" -pch-create "${_pchName}" -fsyntax-only "${_hostFile}") - if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0") - if (NOT _pchSuppressMessages) - list (APPEND _flags -Wpch-messages) - endif() - endif() - else() - # return as a flag string - set (_flags "-include \"${_prefixFile}\" -pch-dir \"${_pchDir}\" -pch-create \"${_pchName}\"") - if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0") - if (NOT _pchSuppressMessages) - set (_flags "${_flags} -Wpch-messages") - endif() - endif() - endif() - endif() - else() - message (FATAL_ERROR "cotire: unsupported ${_language} compiler ${_compilerID} version ${_compilerVersion}.") - endif() - set (${_flagsVar} ${_flags} PARENT_SCOPE) -endfunction() - -function (cotire_add_prefix_pch_inclusion_flags _language _compilerID _compilerVersion _prefixFile _pchFile _flagsVar) - set (_flags ${${_flagsVar}}) - if (_compilerID MATCHES "MSVC") - file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative) - # cl.exe options used - # /Yu uses a precompiled header file during build - # /Fp specifies precompiled header binary file name - # /FI forces inclusion of file - # /Zm precompiled header memory allocation scaling factor - if (_pchFile) - file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative) - if (_flags) - # append to list - list (APPEND _flags "/Yu${_prefixFileNative}" "/Fp${_pchFileNative}" "/FI${_prefixFileNative}") - if (COTIRE_PCH_MEMORY_SCALING_FACTOR) - list (APPEND _flags "/Zm${COTIRE_PCH_MEMORY_SCALING_FACTOR}") - endif() - else() - # return as a flag string - set (_flags "/Yu\"${_prefixFileNative}\" /Fp\"${_pchFileNative}\" /FI\"${_prefixFileNative}\"") - if (COTIRE_PCH_MEMORY_SCALING_FACTOR) - set (_flags "${_flags} /Zm${COTIRE_PCH_MEMORY_SCALING_FACTOR}") - endif() - endif() - else() - # no precompiled header, force inclusion of prefix header - if (_flags) - # append to list - list (APPEND _flags "/FI${_prefixFileNative}") - else() - # return as a flag string - set (_flags "/FI\"${_prefixFileNative}\"") - endif() - endif() - elseif (_compilerID MATCHES "GNU") - # GCC options used - # -include process include file as the first line of the primary source file - # -Winvalid-pch warns if precompiled header is found but cannot be used - # note: ccache requires the -include flag to be used in order to process precompiled header correctly - if (_flags) - # append to list - list (APPEND _flags -Winvalid-pch -include "${_prefixFile}") - else() - # return as a flag string - set (_flags "-Winvalid-pch -include \"${_prefixFile}\"") - endif() - elseif (_compilerID MATCHES "Clang") - if (UNIX) - # Clang options used - # -include process include file as the first line of the primary source file - # note: ccache requires the -include flag to be used in order to process precompiled header correctly - if (_flags) - # append to list - list (APPEND _flags -include "${_prefixFile}") - else() - # return as a flag string - set (_flags "-include \"${_prefixFile}\"") - endif() - elseif (WIN32) - # Clang-cl.exe options used - # /Yu uses a precompiled header file during build - # /Fp specifies precompiled header binary file name - # /FI forces inclusion of file - if (_pchFile) - if (_flags) - # append to list - list (APPEND _flags "/Yu${_prefixFile}" "/Fp${_pchFile}" "/FI${_prefixFile}") - else() - # return as a flag string - set (_flags "/Yu\"${_prefixFile}\" /Fp\"${_pchFile}\" /FI\"${_prefixFile}\"") - endif() - else() - # no precompiled header, force inclusion of prefix header - if (_flags) - # append to list - list (APPEND _flags "/FI${_prefixFile}") - else() - # return as a flag string - set (_flags "/FI\"${_prefixFile}\"") - endif() - endif() - endif() - elseif (_compilerID MATCHES "Intel") - if (WIN32) - file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative) - # Windows Intel options used - # /Yu use a precompiled header (PCH) file - # /Fp specify a path or file name for precompiled header files - # /FI tells the preprocessor to include a specified file name as the header file - # /Wpch-messages enable diagnostics related to pre-compiled headers (requires Intel XE 2013 Update 2) - if (_pchFile) - file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative) - if (_flags) - # append to list - list (APPEND _flags "/Yu" "/Fp${_pchFileNative}" "/FI${_prefixFileNative}") - if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0") - list (APPEND _flags "/Wpch-messages") - endif() - else() - # return as a flag string - set (_flags "/Yu /Fp\"${_pchFileNative}\" /FI\"${_prefixFileNative}\"") - if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0") - set (_flags "${_flags} /Wpch-messages") - endif() - endif() - else() - # no precompiled header, force inclusion of prefix header - if (_flags) - # append to list - list (APPEND _flags "/FI${_prefixFileNative}") - else() - # return as a flag string - set (_flags "/FI\"${_prefixFileNative}\"") - endif() - endif() - else() - # Linux / Mac OS X Intel options used - # -pch-dir location for precompiled header files - # -pch-use name of the precompiled header (PCH) to use - # -include process include file as the first line of the primary source file - # -Wpch-messages enable diagnostics related to pre-compiled headers (requires Intel XE 2013 Update 2) - if (_pchFile) - get_filename_component(_pchDir "${_pchFile}" DIRECTORY) - get_filename_component(_pchName "${_pchFile}" NAME) - set (_pchSuppressMessages FALSE) - if ("${CMAKE_${_language}_FLAGS}" MATCHES ".*-Wno-pch-messages.*") - set(_pchSuppressMessages TRUE) - endif() - if (_flags) - # append to list - list (APPEND _flags -include "${_prefixFile}" -pch-dir "${_pchDir}" -pch-use "${_pchName}") - if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0") - if (NOT _pchSuppressMessages) - list (APPEND _flags -Wpch-messages) - endif() - endif() - else() - # return as a flag string - set (_flags "-include \"${_prefixFile}\" -pch-dir \"${_pchDir}\" -pch-use \"${_pchName}\"") - if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0") - if (NOT _pchSuppressMessages) - set (_flags "${_flags} -Wpch-messages") - endif() - endif() - endif() - else() - # no precompiled header, force inclusion of prefix header - if (_flags) - # append to list - list (APPEND _flags -include "${_prefixFile}") - else() - # return as a flag string - set (_flags "-include \"${_prefixFile}\"") - endif() - endif() - endif() - else() - message (FATAL_ERROR "cotire: unsupported ${_language} compiler ${_compilerID} version ${_compilerVersion}.") - endif() - set (${_flagsVar} ${_flags} PARENT_SCOPE) -endfunction() - -function (cotire_precompile_prefix_header _prefixFile _pchFile _hostFile) - set(_options "") - set(_oneValueArgs COMPILER_EXECUTABLE COMPILER_ARG1 COMPILER_ID COMPILER_VERSION LANGUAGE) - set(_multiValueArgs COMPILE_DEFINITIONS COMPILE_FLAGS INCLUDE_DIRECTORIES SYSTEM_INCLUDE_DIRECTORIES SYS COMPILER_LAUNCHER) - cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) - if (NOT _option_LANGUAGE) - set (_option_LANGUAGE "CXX") - endif() - if (NOT _option_COMPILER_ID) - set (_option_COMPILER_ID "${CMAKE_${_option_LANGUAGE}_ID}") - endif() - if (NOT _option_COMPILER_VERSION) - set (_option_COMPILER_VERSION "${CMAKE_${_option_LANGUAGE}_COMPILER_VERSION}") - endif() - cotire_init_compile_cmd(_cmd "${_option_LANGUAGE}" "${_option_COMPILER_LAUNCHER}" "${_option_COMPILER_EXECUTABLE}" "${_option_COMPILER_ARG1}") - cotire_add_definitions_to_cmd(_cmd "${_option_LANGUAGE}" ${_option_COMPILE_DEFINITIONS}) - cotire_add_compile_flags_to_cmd(_cmd ${_option_COMPILE_FLAGS}) - cotire_add_includes_to_cmd(_cmd "${_option_LANGUAGE}" _option_INCLUDE_DIRECTORIES _option_SYSTEM_INCLUDE_DIRECTORIES) - cotire_add_frameworks_to_cmd(_cmd "${_option_LANGUAGE}" _option_INCLUDE_DIRECTORIES _option_SYSTEM_INCLUDE_DIRECTORIES) - cotire_add_pch_compilation_flags( - "${_option_LANGUAGE}" "${_option_COMPILER_ID}" "${_option_COMPILER_VERSION}" - "${_prefixFile}" "${_pchFile}" "${_hostFile}" _cmd) - if (COTIRE_VERBOSE) - message (STATUS "execute_process: ${_cmd}") - endif() - if (MSVC_IDE OR _option_COMPILER_ID MATCHES "MSVC") - # cl.exe messes with the output streams unless the environment variable VS_UNICODE_OUTPUT is cleared - unset (ENV{VS_UNICODE_OUTPUT}) - elseif (_option_COMPILER_ID MATCHES "Clang" AND _option_COMPILER_VERSION VERSION_LESS "4.0.0") - if (_option_COMPILER_LAUNCHER MATCHES "ccache" OR - _option_COMPILER_EXECUTABLE MATCHES "ccache") - # Newer versions of Clang embed a compilation timestamp into the precompiled header binary, - # which results in "file has been modified since the precompiled header was built" errors if ccache is used. - # We work around the problem by disabling ccache upon pre-compiling the prefix header. - set (ENV{CCACHE_DISABLE} "true") - endif() - endif() - execute_process( - COMMAND ${_cmd} - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - RESULT_VARIABLE _result) - if (_result) - message (FATAL_ERROR "cotire: error ${_result} precompiling ${_prefixFile}.") - endif() -endfunction() - -function (cotire_check_precompiled_header_support _language _target _msgVar) - set (_unsupportedCompiler - "Precompiled headers not supported for ${_language} compiler ${CMAKE_${_language}_COMPILER_ID}") - if (CMAKE_${_language}_COMPILER_ID MATCHES "MSVC") - # PCH supported since Visual Studio C++ 6.0 - # and CMake does not support an earlier version - set (${_msgVar} "" PARENT_SCOPE) - elseif (CMAKE_${_language}_COMPILER_ID MATCHES "GNU") - # GCC PCH support requires version >= 3.4 - if ("${CMAKE_${_language}_COMPILER_VERSION}" VERSION_LESS "3.4.0") - set (${_msgVar} "${_unsupportedCompiler} version ${CMAKE_${_language}_COMPILER_VERSION}." PARENT_SCOPE) - else() - set (${_msgVar} "" PARENT_SCOPE) - endif() - elseif (CMAKE_${_language}_COMPILER_ID MATCHES "Clang") - if (UNIX) - # all Unix Clang versions have PCH support - set (${_msgVar} "" PARENT_SCOPE) - elseif (WIN32) - # only clang-cl is supported under Windows - get_filename_component(_compilerName "${CMAKE_${_language}_COMPILER}" NAME_WE) - if (NOT _compilerName MATCHES "cl$") - set (${_msgVar} "${_unsupportedCompiler} version ${CMAKE_${_language}_COMPILER_VERSION}. Use clang-cl instead." PARENT_SCOPE) - endif() - endif() - elseif (CMAKE_${_language}_COMPILER_ID MATCHES "Intel") - # Intel PCH support requires version >= 8.0.0 - if ("${CMAKE_${_language}_COMPILER_VERSION}" VERSION_LESS "8.0.0") - set (${_msgVar} "${_unsupportedCompiler} version ${CMAKE_${_language}_COMPILER_VERSION}." PARENT_SCOPE) - else() - set (${_msgVar} "" PARENT_SCOPE) - endif() - else() - set (${_msgVar} "${_unsupportedCompiler}." PARENT_SCOPE) - endif() - # check if ccache is used as a compiler launcher - get_target_property(_launcher ${_target} ${_language}_COMPILER_LAUNCHER) - get_filename_component(_realCompilerExe "${CMAKE_${_language}_COMPILER}" REALPATH) - if (_realCompilerExe MATCHES "ccache" OR _launcher MATCHES "ccache") - # verify that ccache configuration is compatible with precompiled headers - # always check environment variable CCACHE_SLOPPINESS, because earlier versions of ccache - # do not report the "sloppiness" setting correctly upon printing ccache configuration - if (DEFINED ENV{CCACHE_SLOPPINESS}) - if (NOT "$ENV{CCACHE_SLOPPINESS}" MATCHES "pch_defines" OR - NOT "$ENV{CCACHE_SLOPPINESS}" MATCHES "time_macros") - set (${_msgVar} - "ccache requires the environment variable CCACHE_SLOPPINESS to be set to \"pch_defines,time_macros\"." - PARENT_SCOPE) - endif() - else() - if (_realCompilerExe MATCHES "ccache") - set (_ccacheExe "${_realCompilerExe}") - else() - set (_ccacheExe "${_launcher}") - endif() - execute_process( - COMMAND "${_ccacheExe}" "--print-config" - WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" - RESULT_VARIABLE _result - OUTPUT_VARIABLE _ccacheConfig OUTPUT_STRIP_TRAILING_WHITESPACE - ERROR_QUIET) - if (_result) - set (${_msgVar} "ccache configuration cannot be determined." PARENT_SCOPE) - elseif (NOT _ccacheConfig MATCHES "sloppiness.*=.*time_macros" OR - NOT _ccacheConfig MATCHES "sloppiness.*=.*pch_defines") - set (${_msgVar} - "ccache requires configuration setting \"sloppiness\" to be set to \"pch_defines,time_macros\"." - PARENT_SCOPE) - endif() - endif() - endif() - if (APPLE) - # PCH compilation not supported by GCC / Clang for multi-architecture builds (e.g., i386, x86_64) - cotire_get_configuration_types(_configs) - foreach (_config ${_configs}) - set (_targetFlags "") - cotire_get_target_compile_flags("${_config}" "${_language}" "${_target}" _targetFlags) - cotire_filter_compile_flags("${_language}" "arch" _architectures _ignore ${_targetFlags}) - list (LENGTH _architectures _numberOfArchitectures) - if (_numberOfArchitectures GREATER 1) - string (REPLACE ";" ", " _architectureStr "${_architectures}") - set (${_msgVar} - "Precompiled headers not supported on Darwin for multi-architecture builds (${_architectureStr})." - PARENT_SCOPE) - break() - endif() - endforeach() - endif() -endfunction() - -macro (cotire_get_intermediate_dir _cotireDir) - # ${CMAKE_CFG_INTDIR} may reference a build-time variable when using a generator which supports configuration types - get_filename_component(${_cotireDir} "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${COTIRE_INTDIR}" ABSOLUTE) -endmacro() - -macro (cotire_setup_file_extension_variables) - set (_unityFileExt_C ".c") - set (_unityFileExt_CXX ".cxx") - set (_prefixFileExt_C ".h") - set (_prefixFileExt_CXX ".hxx") - set (_prefixSourceFileExt_C ".c") - set (_prefixSourceFileExt_CXX ".cxx") -endmacro() - -function (cotire_make_single_unity_source_file_path _language _target _unityFileVar) - cotire_setup_file_extension_variables() - if (NOT DEFINED _unityFileExt_${_language}) - set (${_unityFileVar} "" PARENT_SCOPE) - return() - endif() - set (_unityFileBaseName "${_target}_${_language}${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}") - set (_unityFileName "${_unityFileBaseName}${_unityFileExt_${_language}}") - cotire_get_intermediate_dir(_baseDir) - set (_unityFile "${_baseDir}/${_unityFileName}") - set (${_unityFileVar} "${_unityFile}" PARENT_SCOPE) -endfunction() - -function (cotire_make_unity_source_file_paths _language _target _maxIncludes _unityFilesVar) - cotire_setup_file_extension_variables() - if (NOT DEFINED _unityFileExt_${_language}) - set (${_unityFileVar} "" PARENT_SCOPE) - return() - endif() - set (_unityFileBaseName "${_target}_${_language}${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}") - cotire_get_intermediate_dir(_baseDir) - set (_startIndex 0) - set (_index 0) - set (_unityFiles "") - set (_sourceFiles ${ARGN}) - foreach (_sourceFile ${_sourceFiles}) - get_source_file_property(_startNew "${_sourceFile}" COTIRE_START_NEW_UNITY_SOURCE) - math (EXPR _unityFileCount "${_index} - ${_startIndex}") - if (_startNew OR (_maxIncludes GREATER 0 AND NOT _unityFileCount LESS _maxIncludes)) - if (_index GREATER 0) - # start new unity file segment - math (EXPR _endIndex "${_index} - 1") - set (_unityFileName "${_unityFileBaseName}_${_startIndex}_${_endIndex}${_unityFileExt_${_language}}") - list (APPEND _unityFiles "${_baseDir}/${_unityFileName}") - endif() - set (_startIndex ${_index}) - endif() - math (EXPR _index "${_index} + 1") - endforeach() - list (LENGTH _sourceFiles _numberOfSources) - if (_startIndex EQUAL 0) - # there is only a single unity file - cotire_make_single_unity_source_file_path(${_language} ${_target} _unityFiles) - elseif (_startIndex LESS _numberOfSources) - # end with final unity file segment - math (EXPR _endIndex "${_index} - 1") - set (_unityFileName "${_unityFileBaseName}_${_startIndex}_${_endIndex}${_unityFileExt_${_language}}") - list (APPEND _unityFiles "${_baseDir}/${_unityFileName}") - endif() - set (${_unityFilesVar} ${_unityFiles} PARENT_SCOPE) - if (COTIRE_DEBUG AND _unityFiles) - message (STATUS "unity files: ${_unityFiles}") - endif() -endfunction() - -function (cotire_unity_to_prefix_file_path _language _target _unityFile _prefixFileVar) - cotire_setup_file_extension_variables() - if (NOT DEFINED _unityFileExt_${_language}) - set (${_prefixFileVar} "" PARENT_SCOPE) - return() - endif() - set (_unityFileBaseName "${_target}_${_language}${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}") - set (_prefixFileBaseName "${_target}_${_language}${COTIRE_PREFIX_HEADER_FILENAME_SUFFIX}") - string (REPLACE "${_unityFileBaseName}" "${_prefixFileBaseName}" _prefixFile "${_unityFile}") - string (REGEX REPLACE "${_unityFileExt_${_language}}$" "${_prefixFileExt_${_language}}" _prefixFile "${_prefixFile}") - set (${_prefixFileVar} "${_prefixFile}" PARENT_SCOPE) -endfunction() - -function (cotire_prefix_header_to_source_file_path _language _prefixHeaderFile _prefixSourceFileVar) - cotire_setup_file_extension_variables() - if (NOT DEFINED _prefixSourceFileExt_${_language}) - set (${_prefixSourceFileVar} "" PARENT_SCOPE) - return() - endif() - string (REGEX REPLACE "${_prefixFileExt_${_language}}$" "${_prefixSourceFileExt_${_language}}" _prefixSourceFile "${_prefixHeaderFile}") - set (${_prefixSourceFileVar} "${_prefixSourceFile}" PARENT_SCOPE) -endfunction() - -function (cotire_make_prefix_file_name _language _target _prefixFileBaseNameVar _prefixFileNameVar) - cotire_setup_file_extension_variables() - if (NOT _language) - set (_prefixFileBaseName "${_target}${COTIRE_PREFIX_HEADER_FILENAME_SUFFIX}") - set (_prefixFileName "${_prefixFileBaseName}${_prefixFileExt_C}") - elseif (DEFINED _prefixFileExt_${_language}) - set (_prefixFileBaseName "${_target}_${_language}${COTIRE_PREFIX_HEADER_FILENAME_SUFFIX}") - set (_prefixFileName "${_prefixFileBaseName}${_prefixFileExt_${_language}}") - else() - set (_prefixFileBaseName "") - set (_prefixFileName "") - endif() - set (${_prefixFileBaseNameVar} "${_prefixFileBaseName}" PARENT_SCOPE) - set (${_prefixFileNameVar} "${_prefixFileName}" PARENT_SCOPE) -endfunction() - -function (cotire_make_prefix_file_path _language _target _prefixFileVar) - cotire_make_prefix_file_name("${_language}" "${_target}" _prefixFileBaseName _prefixFileName) - set (${_prefixFileVar} "" PARENT_SCOPE) - if (_prefixFileName) - if (NOT _language) - set (_language "C") - endif() - if (CMAKE_${_language}_COMPILER_ID MATCHES "GNU|Clang|Intel|MSVC") - cotire_get_intermediate_dir(_baseDir) - set (${_prefixFileVar} "${_baseDir}/${_prefixFileName}" PARENT_SCOPE) - endif() - endif() -endfunction() - -function (cotire_make_pch_file_path _language _target _pchFileVar) - cotire_make_prefix_file_name("${_language}" "${_target}" _prefixFileBaseName _prefixFileName) - set (${_pchFileVar} "" PARENT_SCOPE) - if (_prefixFileBaseName AND _prefixFileName) - cotire_check_precompiled_header_support("${_language}" "${_target}" _msg) - if (NOT _msg) - if (XCODE) - # For Xcode, we completely hand off the compilation of the prefix header to the IDE - return() - endif() - cotire_get_intermediate_dir(_baseDir) - if (CMAKE_${_language}_COMPILER_ID MATCHES "MSVC") - # MSVC uses the extension .pch added to the prefix header base name - set (${_pchFileVar} "${_baseDir}/${_prefixFileBaseName}.pch" PARENT_SCOPE) - elseif (CMAKE_${_language}_COMPILER_ID MATCHES "Clang") - # Clang looks for a precompiled header corresponding to the prefix header with the extension .pch appended - set (${_pchFileVar} "${_baseDir}/${_prefixFileName}.pch" PARENT_SCOPE) - elseif (CMAKE_${_language}_COMPILER_ID MATCHES "GNU") - # GCC looks for a precompiled header corresponding to the prefix header with the extension .gch appended - set (${_pchFileVar} "${_baseDir}/${_prefixFileName}.gch" PARENT_SCOPE) - elseif (CMAKE_${_language}_COMPILER_ID MATCHES "Intel") - # Intel uses the extension .pchi added to the prefix header base name - set (${_pchFileVar} "${_baseDir}/${_prefixFileBaseName}.pchi" PARENT_SCOPE) - endif() - endif() - endif() -endfunction() - -function (cotire_select_unity_source_files _unityFile _sourcesVar) - set (_sourceFiles ${ARGN}) - if (_sourceFiles AND "${_unityFile}" MATCHES "${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}_([0-9]+)_([0-9]+)") - set (_startIndex ${CMAKE_MATCH_1}) - set (_endIndex ${CMAKE_MATCH_2}) - list (LENGTH _sourceFiles _numberOfSources) - if (NOT _startIndex LESS _numberOfSources) - math (EXPR _startIndex "${_numberOfSources} - 1") - endif() - if (NOT _endIndex LESS _numberOfSources) - math (EXPR _endIndex "${_numberOfSources} - 1") - endif() - set (_files "") - foreach (_index RANGE ${_startIndex} ${_endIndex}) - list (GET _sourceFiles ${_index} _file) - list (APPEND _files "${_file}") - endforeach() - else() - set (_files ${_sourceFiles}) - endif() - set (${_sourcesVar} ${_files} PARENT_SCOPE) -endfunction() - -function (cotire_get_unity_source_dependencies _language _target _dependencySourcesVar) - set (_dependencySources "") - # depend on target's generated source files - get_target_property(_targetSourceFiles ${_target} SOURCES) - cotire_get_objects_with_property_on(_generatedSources GENERATED SOURCE ${_targetSourceFiles}) - if (_generatedSources) - # but omit all generated source files that have the COTIRE_EXCLUDED property set to true - cotire_get_objects_with_property_on(_excludedGeneratedSources COTIRE_EXCLUDED SOURCE ${_generatedSources}) - if (_excludedGeneratedSources) - list (REMOVE_ITEM _generatedSources ${_excludedGeneratedSources}) - endif() - # and omit all generated source files that have the COTIRE_DEPENDENCY property set to false explicitly - cotire_get_objects_with_property_off(_excludedNonDependencySources COTIRE_DEPENDENCY SOURCE ${_generatedSources}) - if (_excludedNonDependencySources) - list (REMOVE_ITEM _generatedSources ${_excludedNonDependencySources}) - endif() - if (_generatedSources) - list (APPEND _dependencySources ${_generatedSources}) - endif() - endif() - if (COTIRE_DEBUG AND _dependencySources) - message (STATUS "${_language} ${_target} unity source dependencies: ${_dependencySources}") - endif() - set (${_dependencySourcesVar} ${_dependencySources} PARENT_SCOPE) -endfunction() - -function (cotire_get_prefix_header_dependencies _language _target _dependencySourcesVar) - set (_dependencySources "") - # depend on target source files marked with custom COTIRE_DEPENDENCY property - get_target_property(_targetSourceFiles ${_target} SOURCES) - cotire_get_objects_with_property_on(_dependencySources COTIRE_DEPENDENCY SOURCE ${_targetSourceFiles}) - if (COTIRE_DEBUG AND _dependencySources) - message (STATUS "${_language} ${_target} prefix header dependencies: ${_dependencySources}") - endif() - set (${_dependencySourcesVar} ${_dependencySources} PARENT_SCOPE) -endfunction() - -function (cotire_generate_target_script _language _configurations _target _targetScriptVar _targetConfigScriptVar) - set (_targetSources ${ARGN}) - cotire_get_prefix_header_dependencies(${_language} ${_target} COTIRE_TARGET_PREFIX_DEPENDS ${_targetSources}) - cotire_get_unity_source_dependencies(${_language} ${_target} COTIRE_TARGET_UNITY_DEPENDS ${_targetSources}) - # set up variables to be configured - set (COTIRE_TARGET_LANGUAGE "${_language}") - get_target_property(COTIRE_TARGET_IGNORE_PATH ${_target} COTIRE_PREFIX_HEADER_IGNORE_PATH) - cotire_add_sys_root_paths(COTIRE_TARGET_IGNORE_PATH) - get_target_property(COTIRE_TARGET_INCLUDE_PATH ${_target} COTIRE_PREFIX_HEADER_INCLUDE_PATH) - cotire_add_sys_root_paths(COTIRE_TARGET_INCLUDE_PATH) - get_target_property(COTIRE_TARGET_PRE_UNDEFS ${_target} COTIRE_UNITY_SOURCE_PRE_UNDEFS) - get_target_property(COTIRE_TARGET_POST_UNDEFS ${_target} COTIRE_UNITY_SOURCE_POST_UNDEFS) - get_target_property(COTIRE_TARGET_MAXIMUM_NUMBER_OF_INCLUDES ${_target} COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES) - get_target_property(COTIRE_TARGET_INCLUDE_PRIORITY_PATH ${_target} COTIRE_PREFIX_HEADER_INCLUDE_PRIORITY_PATH) - cotire_get_source_files_undefs(COTIRE_UNITY_SOURCE_PRE_UNDEFS COTIRE_TARGET_SOURCES_PRE_UNDEFS ${_targetSources}) - cotire_get_source_files_undefs(COTIRE_UNITY_SOURCE_POST_UNDEFS COTIRE_TARGET_SOURCES_POST_UNDEFS ${_targetSources}) - set (COTIRE_TARGET_CONFIGURATION_TYPES "${_configurations}") - foreach (_config ${_configurations}) - string (TOUPPER "${_config}" _upperConfig) - cotire_get_target_include_directories( - "${_config}" "${_language}" "${_target}" COTIRE_TARGET_INCLUDE_DIRECTORIES_${_upperConfig} COTIRE_TARGET_SYSTEM_INCLUDE_DIRECTORIES_${_upperConfig}) - cotire_get_target_compile_definitions( - "${_config}" "${_language}" "${_target}" COTIRE_TARGET_COMPILE_DEFINITIONS_${_upperConfig}) - cotire_get_target_compiler_flags( - "${_config}" "${_language}" "${_target}" COTIRE_TARGET_COMPILE_FLAGS_${_upperConfig}) - cotire_get_source_files_compile_definitions( - "${_config}" "${_language}" COTIRE_TARGET_SOURCES_COMPILE_DEFINITIONS_${_upperConfig} ${_targetSources}) - endforeach() - get_target_property(COTIRE_TARGET_${_language}_COMPILER_LAUNCHER ${_target} ${_language}_COMPILER_LAUNCHER) - # set up COTIRE_TARGET_SOURCES - set (COTIRE_TARGET_SOURCES "") - foreach (_sourceFile ${_targetSources}) - get_source_file_property(_generated "${_sourceFile}" GENERATED) - if (_generated) - # use absolute paths for generated files only, retrieving the LOCATION property is an expensive operation - get_source_file_property(_sourceLocation "${_sourceFile}" LOCATION) - list (APPEND COTIRE_TARGET_SOURCES "${_sourceLocation}") - else() - list (APPEND COTIRE_TARGET_SOURCES "${_sourceFile}") - endif() - endforeach() - # copy variable definitions to cotire target script - get_cmake_property(_vars VARIABLES) - string (REGEX MATCHALL "COTIRE_[A-Za-z0-9_]+" _matchVars "${_vars}") - # omit COTIRE_*_INIT variables - string (REGEX MATCHALL "COTIRE_[A-Za-z0-9_]+_INIT" _initVars "${_matchVars}") - if (_initVars) - list (REMOVE_ITEM _matchVars ${_initVars}) - endif() - # omit COTIRE_VERBOSE which is passed as a CMake define on command line - list (REMOVE_ITEM _matchVars COTIRE_VERBOSE) - set (_contents "") - set (_contentsHasGeneratorExpressions FALSE) - foreach (_var IN LISTS _matchVars ITEMS - XCODE MSVC CMAKE_GENERATOR CMAKE_BUILD_TYPE CMAKE_CONFIGURATION_TYPES - CMAKE_${_language}_COMPILER_ID CMAKE_${_language}_COMPILER_VERSION - CMAKE_${_language}_COMPILER_LAUNCHER CMAKE_${_language}_COMPILER CMAKE_${_language}_COMPILER_ARG1 - CMAKE_INCLUDE_FLAG_${_language} CMAKE_INCLUDE_FLAG_SEP_${_language} - CMAKE_INCLUDE_SYSTEM_FLAG_${_language} - CMAKE_${_language}_FRAMEWORK_SEARCH_FLAG - CMAKE_${_language}_SYSTEM_FRAMEWORK_SEARCH_FLAG - CMAKE_${_language}_SOURCE_FILE_EXTENSIONS) - if (DEFINED ${_var}) - string (REPLACE "\"" "\\\"" _value "${${_var}}") - set (_contents "${_contents}set (${_var} \"${_value}\")\n") - if (NOT _contentsHasGeneratorExpressions) - if ("${_value}" MATCHES "\\$<.*>") - set (_contentsHasGeneratorExpressions TRUE) - endif() - endif() - endif() - endforeach() - # generate target script file - get_filename_component(_moduleName "${COTIRE_CMAKE_MODULE_FILE}" NAME) - set (_targetCotireScript "${CMAKE_CURRENT_BINARY_DIR}/${_target}_${_language}_${_moduleName}") - cotire_write_file("CMAKE" "${_targetCotireScript}" "${_contents}" FALSE) - if (_contentsHasGeneratorExpressions) - # use file(GENERATE ...) to expand generator expressions in the target script at CMake generate-time - set (_configNameOrNoneGeneratorExpression "$<$:None>$<$>:$>") - set (_targetCotireConfigScript "${CMAKE_CURRENT_BINARY_DIR}/${_target}_${_language}_${_configNameOrNoneGeneratorExpression}_${_moduleName}") - file (GENERATE OUTPUT "${_targetCotireConfigScript}" INPUT "${_targetCotireScript}") - else() - set (_targetCotireConfigScript "${_targetCotireScript}") - endif() - set (${_targetScriptVar} "${_targetCotireScript}" PARENT_SCOPE) - set (${_targetConfigScriptVar} "${_targetCotireConfigScript}" PARENT_SCOPE) -endfunction() - -function (cotire_setup_pch_file_compilation _language _target _targetScript _prefixFile _pchFile _hostFile) - set (_sourceFiles ${ARGN}) - if (CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel" OR - (WIN32 AND CMAKE_${_language}_COMPILER_ID MATCHES "Clang")) - # for MSVC, Intel and Clang-cl, we attach the precompiled header compilation to the host file - # the remaining files include the precompiled header, see cotire_setup_pch_file_inclusion - if (_sourceFiles) - set (_flags "") - cotire_add_pch_compilation_flags( - "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${CMAKE_${_language}_COMPILER_VERSION}" - "${_prefixFile}" "${_pchFile}" "${_hostFile}" _flags) - set_property (SOURCE ${_hostFile} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_flags} ") - set_property (SOURCE ${_hostFile} APPEND PROPERTY OBJECT_OUTPUTS "${_pchFile}") - # make object file generated from host file depend on prefix header - set_property (SOURCE ${_hostFile} APPEND PROPERTY OBJECT_DEPENDS "${_prefixFile}") - # mark host file as cotired to prevent it from being used in another cotired target - set_property (SOURCE ${_hostFile} PROPERTY COTIRE_TARGET "${_target}") - endif() - elseif ("${CMAKE_GENERATOR}" MATCHES "Make|Ninja") - # for makefile based generator, we add a custom command to precompile the prefix header - if (_targetScript) - cotire_set_cmd_to_prologue(_cmds) - list (APPEND _cmds -P "${COTIRE_CMAKE_MODULE_FILE}" "precompile" "${_targetScript}" "${_prefixFile}" "${_pchFile}" "${_hostFile}") - if (MSVC_IDE) - file (TO_NATIVE_PATH "${_pchFile}" _pchFileLogPath) - else() - file (RELATIVE_PATH _pchFileLogPath "${CMAKE_BINARY_DIR}" "${_pchFile}") - endif() - # make precompiled header compilation depend on the actual compiler executable used to force - # re-compilation when the compiler executable is updated. This prevents "created by a different GCC executable" - # warnings when the precompiled header is included. - get_filename_component(_realCompilerExe "${CMAKE_${_language}_COMPILER}" ABSOLUTE) - if (COTIRE_DEBUG) - message (STATUS "add_custom_command: OUTPUT ${_pchFile} ${_cmds} DEPENDS ${_prefixFile} ${_realCompilerExe} IMPLICIT_DEPENDS ${_language} ${_prefixFile}") - endif() - set_property (SOURCE "${_pchFile}" PROPERTY GENERATED TRUE) - add_custom_command( - OUTPUT "${_pchFile}" - COMMAND ${_cmds} - DEPENDS "${_prefixFile}" "${_realCompilerExe}" - IMPLICIT_DEPENDS ${_language} "${_prefixFile}" - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - COMMENT "Building ${_language} precompiled header ${_pchFileLogPath}" - VERBATIM) - endif() - endif() -endfunction() - -function (cotire_setup_pch_file_inclusion _language _target _wholeTarget _prefixFile _pchFile _hostFile) - if (CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel" OR - (WIN32 AND CMAKE_${_language}_COMPILER_ID MATCHES "Clang")) - # for MSVC, Intel and clang-cl, we include the precompiled header in all but the host file - # the host file does the precompiled header compilation, see cotire_setup_pch_file_compilation - set (_sourceFiles ${ARGN}) - list (LENGTH _sourceFiles _numberOfSourceFiles) - if (_numberOfSourceFiles GREATER 0) - # mark sources as cotired to prevent them from being used in another cotired target - set_source_files_properties(${_sourceFiles} PROPERTIES COTIRE_TARGET "${_target}") - set (_flags "") - cotire_add_prefix_pch_inclusion_flags( - "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${CMAKE_${_language}_COMPILER_VERSION}" - "${_prefixFile}" "${_pchFile}" _flags) - set_property (SOURCE ${_sourceFiles} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_flags} ") - # make object files generated from source files depend on precompiled header - set_property (SOURCE ${_sourceFiles} APPEND PROPERTY OBJECT_DEPENDS "${_pchFile}") - endif() - elseif ("${CMAKE_GENERATOR}" MATCHES "Make|Ninja") - set (_sourceFiles ${_hostFile} ${ARGN}) - if (NOT _wholeTarget) - # for makefile based generator, we force the inclusion of the prefix header for a subset - # of the source files, if this is a multi-language target or has excluded files - set (_flags "") - cotire_add_prefix_pch_inclusion_flags( - "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${CMAKE_${_language}_COMPILER_VERSION}" - "${_prefixFile}" "${_pchFile}" _flags) - set_property (SOURCE ${_sourceFiles} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_flags} ") - # mark sources as cotired to prevent them from being used in another cotired target - set_source_files_properties(${_sourceFiles} PROPERTIES COTIRE_TARGET "${_target}") - endif() - # make object files generated from source files depend on precompiled header - set_property (SOURCE ${_sourceFiles} APPEND PROPERTY OBJECT_DEPENDS "${_pchFile}") - endif() -endfunction() - -function (cotire_setup_prefix_file_inclusion _language _target _prefixFile) - set (_sourceFiles ${ARGN}) - # force the inclusion of the prefix header for the given source files - set (_flags "") - set (_pchFile "") - cotire_add_prefix_pch_inclusion_flags( - "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${CMAKE_${_language}_COMPILER_VERSION}" - "${_prefixFile}" "${_pchFile}" _flags) - set_property (SOURCE ${_sourceFiles} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_flags} ") - # mark sources as cotired to prevent them from being used in another cotired target - set_source_files_properties(${_sourceFiles} PROPERTIES COTIRE_TARGET "${_target}") - # make object files generated from source files depend on prefix header - set_property (SOURCE ${_sourceFiles} APPEND PROPERTY OBJECT_DEPENDS "${_prefixFile}") -endfunction() - -function (cotire_get_first_set_property_value _propertyValueVar _type _object) - set (_properties ${ARGN}) - foreach (_property ${_properties}) - get_property(_propertyValue ${_type} "${_object}" PROPERTY ${_property}) - if (_propertyValue) - set (${_propertyValueVar} ${_propertyValue} PARENT_SCOPE) - return() - endif() - endforeach() - set (${_propertyValueVar} "" PARENT_SCOPE) -endfunction() - -function (cotire_setup_combine_command _language _targetScript _joinedFile _cmdsVar) - set (_files ${ARGN}) - set (_filesPaths "") - foreach (_file ${_files}) - get_filename_component(_filePath "${_file}" ABSOLUTE) - list (APPEND _filesPaths "${_filePath}") - endforeach() - cotire_set_cmd_to_prologue(_prefixCmd) - list (APPEND _prefixCmd -P "${COTIRE_CMAKE_MODULE_FILE}" "combine") - if (_targetScript) - list (APPEND _prefixCmd "${_targetScript}") - endif() - list (APPEND _prefixCmd "${_joinedFile}" ${_filesPaths}) - if (COTIRE_DEBUG) - message (STATUS "add_custom_command: OUTPUT ${_joinedFile} COMMAND ${_prefixCmd} DEPENDS ${_files}") - endif() - set_property (SOURCE "${_joinedFile}" PROPERTY GENERATED TRUE) - if (MSVC_IDE) - file (TO_NATIVE_PATH "${_joinedFile}" _joinedFileLogPath) - else() - file (RELATIVE_PATH _joinedFileLogPath "${CMAKE_BINARY_DIR}" "${_joinedFile}") - endif() - get_filename_component(_joinedFileBaseName "${_joinedFile}" NAME_WE) - get_filename_component(_joinedFileExt "${_joinedFile}" EXT) - if (_language AND _joinedFileBaseName MATCHES "${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}$") - set (_comment "Generating ${_language} unity source ${_joinedFileLogPath}") - elseif (_language AND _joinedFileBaseName MATCHES "${COTIRE_PREFIX_HEADER_FILENAME_SUFFIX}$") - if (_joinedFileExt MATCHES "^\\.c") - set (_comment "Generating ${_language} prefix source ${_joinedFileLogPath}") - else() - set (_comment "Generating ${_language} prefix header ${_joinedFileLogPath}") - endif() - else() - set (_comment "Generating ${_joinedFileLogPath}") - endif() - add_custom_command( - OUTPUT "${_joinedFile}" - COMMAND ${_prefixCmd} - DEPENDS ${_files} - COMMENT "${_comment}" - WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" - VERBATIM) - list (APPEND ${_cmdsVar} COMMAND ${_prefixCmd}) - set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE) -endfunction() - -function (cotire_setup_target_pch_usage _languages _target _wholeTarget) - if (XCODE) - # for Xcode, we attach a pre-build action to generate the unity sources and prefix headers - set (_prefixFiles "") - foreach (_language ${_languages}) - get_property(_prefixFile TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER) - if (_prefixFile) - list (APPEND _prefixFiles "${_prefixFile}") - endif() - endforeach() - set (_cmds ${ARGN}) - list (LENGTH _prefixFiles _numberOfPrefixFiles) - if (_numberOfPrefixFiles GREATER 1) - # we also generate a generic, single prefix header which includes all language specific prefix headers - set (_language "") - set (_targetScript "") - cotire_make_prefix_file_path("${_language}" ${_target} _prefixHeader) - cotire_setup_combine_command("${_language}" "${_targetScript}" "${_prefixHeader}" _cmds ${_prefixFiles}) - else() - set (_prefixHeader "${_prefixFiles}") - endif() - if (COTIRE_DEBUG) - message (STATUS "add_custom_command: TARGET ${_target} PRE_BUILD ${_cmds}") - endif() - # because CMake PRE_BUILD command does not support dependencies, - # we check dependencies explicity in cotire script mode when the pre-build action is run - add_custom_command( - TARGET "${_target}" - PRE_BUILD ${_cmds} - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - COMMENT "Updating target ${_target} prefix headers" - VERBATIM) - # make Xcode precompile the generated prefix header with ProcessPCH and ProcessPCH++ - set_target_properties(${_target} PROPERTIES XCODE_ATTRIBUTE_GCC_PRECOMPILE_PREFIX_HEADER "YES") - set_target_properties(${_target} PROPERTIES XCODE_ATTRIBUTE_GCC_PREFIX_HEADER "${_prefixHeader}") - elseif ("${CMAKE_GENERATOR}" MATCHES "Make|Ninja") - # for makefile based generator, we force inclusion of the prefix header for all target source files - # if this is a single-language target without any excluded files - if (_wholeTarget) - set (_language "${_languages}") - # for MSVC, Intel and clang-cl, precompiled header inclusion is always done on the source file level - # see cotire_setup_pch_file_inclusion - if (NOT CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel" AND NOT - (WIN32 AND CMAKE_${_language}_COMPILER_ID MATCHES "Clang")) - get_property(_prefixFile TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER) - if (_prefixFile) - get_property(_pchFile TARGET ${_target} PROPERTY COTIRE_${_language}_PRECOMPILED_HEADER) - set (_options COMPILE_OPTIONS) - cotire_add_prefix_pch_inclusion_flags( - "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${CMAKE_${_language}_COMPILER_VERSION}" - "${_prefixFile}" "${_pchFile}" _options) - set_property(TARGET ${_target} APPEND PROPERTY ${_options}) - endif() - endif() - endif() - endif() -endfunction() - -function (cotire_setup_unity_generation_commands _language _target _targetScript _targetConfigScript _unityFiles _cmdsVar) - set (_dependencySources "") - cotire_get_unity_source_dependencies(${_language} ${_target} _dependencySources ${ARGN}) - foreach (_unityFile ${_unityFiles}) - set_property (SOURCE "${_unityFile}" PROPERTY GENERATED TRUE) - # set up compiled unity source dependencies via OBJECT_DEPENDS - # this ensures that missing source files are generated before the unity file is compiled - if (COTIRE_DEBUG AND _dependencySources) - message (STATUS "${_unityFile} OBJECT_DEPENDS ${_dependencySources}") - endif() - if (_dependencySources) - # the OBJECT_DEPENDS property requires a list of full paths - set (_objectDependsPaths "") - foreach (_sourceFile ${_dependencySources}) - get_source_file_property(_sourceLocation "${_sourceFile}" LOCATION) - list (APPEND _objectDependsPaths "${_sourceLocation}") - endforeach() - set_property (SOURCE "${_unityFile}" PROPERTY OBJECT_DEPENDS ${_objectDependsPaths}) - endif() - if (WIN32 AND CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel") - # unity file compilation results in potentially huge object file, - # thus use /bigobj by default unter cl.exe and Windows Intel - set_property (SOURCE "${_unityFile}" APPEND_STRING PROPERTY COMPILE_FLAGS "/bigobj") - endif() - cotire_set_cmd_to_prologue(_unityCmd) - list (APPEND _unityCmd -P "${COTIRE_CMAKE_MODULE_FILE}" "unity" "${_targetConfigScript}" "${_unityFile}") - if (CMAKE_VERSION VERSION_LESS "3.1.0") - set (_unityCmdDepends "${_targetScript}") - else() - # CMake 3.1.0 supports generator expressions in arguments to DEPENDS - set (_unityCmdDepends "${_targetConfigScript}") - endif() - if (MSVC_IDE) - file (TO_NATIVE_PATH "${_unityFile}" _unityFileLogPath) - else() - file (RELATIVE_PATH _unityFileLogPath "${CMAKE_BINARY_DIR}" "${_unityFile}") - endif() - if (COTIRE_DEBUG) - message (STATUS "add_custom_command: OUTPUT ${_unityFile} COMMAND ${_unityCmd} DEPENDS ${_unityCmdDepends}") - endif() - add_custom_command( - OUTPUT "${_unityFile}" - COMMAND ${_unityCmd} - DEPENDS ${_unityCmdDepends} - COMMENT "Generating ${_language} unity source ${_unityFileLogPath}" - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - VERBATIM) - list (APPEND ${_cmdsVar} COMMAND ${_unityCmd}) - endforeach() - set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE) -endfunction() - -function (cotire_setup_prefix_generation_command _language _target _targetScript _prefixFile _unityFiles _cmdsVar) - set (_sourceFiles ${ARGN}) - set (_dependencySources "") - cotire_get_prefix_header_dependencies(${_language} ${_target} _dependencySources ${_sourceFiles}) - cotire_set_cmd_to_prologue(_prefixCmd) - list (APPEND _prefixCmd -P "${COTIRE_CMAKE_MODULE_FILE}" "prefix" "${_targetScript}" "${_prefixFile}" ${_unityFiles}) - set_property (SOURCE "${_prefixFile}" PROPERTY GENERATED TRUE) - # make prefix header generation depend on the actual compiler executable used to force - # re-generation when the compiler executable is updated. This prevents "file not found" - # errors for compiler version specific system header files. - get_filename_component(_realCompilerExe "${CMAKE_${_language}_COMPILER}" ABSOLUTE) - if (COTIRE_DEBUG) - message (STATUS "add_custom_command: OUTPUT ${_prefixFile} COMMAND ${_prefixCmd} DEPENDS ${_unityFile} ${_dependencySources} ${_realCompilerExe}") - endif() - if (MSVC_IDE) - file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileLogPath) - else() - file (RELATIVE_PATH _prefixFileLogPath "${CMAKE_BINARY_DIR}" "${_prefixFile}") - endif() - get_filename_component(_prefixFileExt "${_prefixFile}" EXT) - if (_prefixFileExt MATCHES "^\\.c") - set (_comment "Generating ${_language} prefix source ${_prefixFileLogPath}") - else() - set (_comment "Generating ${_language} prefix header ${_prefixFileLogPath}") - endif() - # prevent pre-processing errors upon generating the prefix header when a target's generated include file does not yet exist - # we do not add a file-level dependency for the target's generated files though, because we only want to depend on their existence - # thus we make the prefix header generation depend on a custom helper target which triggers the generation of the files - set (_preTargetName "${_target}${COTIRE_PCH_TARGET_SUFFIX}_pre") - if (TARGET ${_preTargetName}) - # custom helper target has already been generated while processing a different language - list (APPEND _dependencySources ${_preTargetName}) - else() - get_target_property(_targetSourceFiles ${_target} SOURCES) - cotire_get_objects_with_property_on(_generatedSources GENERATED SOURCE ${_targetSourceFiles}) - if (_generatedSources) - add_custom_target("${_preTargetName}" DEPENDS ${_generatedSources}) - cotire_init_target("${_preTargetName}") - list (APPEND _dependencySources ${_preTargetName}) - endif() - endif() - add_custom_command( - OUTPUT "${_prefixFile}" "${_prefixFile}.log" - COMMAND ${_prefixCmd} - DEPENDS ${_unityFiles} ${_dependencySources} "${_realCompilerExe}" - COMMENT "${_comment}" - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - VERBATIM) - list (APPEND ${_cmdsVar} COMMAND ${_prefixCmd}) - set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE) -endfunction() - -function (cotire_setup_prefix_generation_from_unity_command _language _target _targetScript _prefixFile _unityFiles _cmdsVar) - set (_sourceFiles ${ARGN}) - if (CMAKE_${_language}_COMPILER_ID MATCHES "GNU|Clang") - # GNU and Clang require indirect compilation of the prefix header to make them honor the system_header pragma - cotire_prefix_header_to_source_file_path(${_language} "${_prefixFile}" _prefixSourceFile) - else() - set (_prefixSourceFile "${_prefixFile}") - endif() - cotire_setup_prefix_generation_command( - ${_language} ${_target} "${_targetScript}" - "${_prefixSourceFile}" "${_unityFiles}" ${_cmdsVar} ${_sourceFiles}) - if (CMAKE_${_language}_COMPILER_ID MATCHES "GNU|Clang") - # set up generation of a prefix source file which includes the prefix header - cotire_setup_combine_command(${_language} "${_targetScript}" "${_prefixFile}" _cmds ${_prefixSourceFile}) - endif() - set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE) -endfunction() - -function (cotire_setup_prefix_generation_from_provided_command _language _target _targetScript _prefixFile _cmdsVar) - set (_prefixHeaderFiles ${ARGN}) - if (CMAKE_${_language}_COMPILER_ID MATCHES "GNU|Clang") - # GNU and Clang require indirect compilation of the prefix header to make them honor the system_header pragma - cotire_prefix_header_to_source_file_path(${_language} "${_prefixFile}" _prefixSourceFile) - else() - set (_prefixSourceFile "${_prefixFile}") - endif() - cotire_setup_combine_command(${_language} "${_targetScript}" "${_prefixSourceFile}" _cmds ${_prefixHeaderFiles}) - if (CMAKE_${_language}_COMPILER_ID MATCHES "GNU|Clang") - # set up generation of a prefix source file which includes the prefix header - cotire_setup_combine_command(${_language} "${_targetScript}" "${_prefixFile}" _cmds ${_prefixSourceFile}) - endif() - set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE) -endfunction() - -function (cotire_init_cotire_target_properties _target) - get_property(_isSet TARGET ${_target} PROPERTY COTIRE_ENABLE_PRECOMPILED_HEADER SET) - if (NOT _isSet) - set_property(TARGET ${_target} PROPERTY COTIRE_ENABLE_PRECOMPILED_HEADER TRUE) - endif() - get_property(_isSet TARGET ${_target} PROPERTY COTIRE_ADD_UNITY_BUILD SET) - if (NOT _isSet) - set_property(TARGET ${_target} PROPERTY COTIRE_ADD_UNITY_BUILD TRUE) - endif() - get_property(_isSet TARGET ${_target} PROPERTY COTIRE_ADD_CLEAN SET) - if (NOT _isSet) - set_property(TARGET ${_target} PROPERTY COTIRE_ADD_CLEAN FALSE) - endif() - get_property(_isSet TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_IGNORE_PATH SET) - if (NOT _isSet) - set_property(TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_IGNORE_PATH "${CMAKE_SOURCE_DIR}") - cotire_check_is_path_relative_to("${CMAKE_BINARY_DIR}" _isRelative "${CMAKE_SOURCE_DIR}") - if (NOT _isRelative) - set_property(TARGET ${_target} APPEND PROPERTY COTIRE_PREFIX_HEADER_IGNORE_PATH "${CMAKE_BINARY_DIR}") - endif() - endif() - get_property(_isSet TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_INCLUDE_PATH SET) - if (NOT _isSet) - set_property(TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_INCLUDE_PATH "") - endif() - get_property(_isSet TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_INCLUDE_PRIORITY_PATH SET) - if (NOT _isSet) - set_property(TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_INCLUDE_PRIORITY_PATH "") - endif() - get_property(_isSet TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_PRE_UNDEFS SET) - if (NOT _isSet) - set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_PRE_UNDEFS "") - endif() - get_property(_isSet TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_POST_UNDEFS SET) - if (NOT _isSet) - set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_POST_UNDEFS "") - endif() - get_property(_isSet TARGET ${_target} PROPERTY COTIRE_UNITY_LINK_LIBRARIES_INIT SET) - if (NOT _isSet) - set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_LINK_LIBRARIES_INIT "COPY_UNITY") - endif() - get_property(_isSet TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES SET) - if (NOT _isSet) - if (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES) - set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES "${COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES}") - else() - set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES "") - endif() - endif() -endfunction() - -function (cotire_make_target_message _target _languages _disableMsg _targetMsgVar) - get_target_property(_targetUsePCH ${_target} COTIRE_ENABLE_PRECOMPILED_HEADER) - get_target_property(_targetAddSCU ${_target} COTIRE_ADD_UNITY_BUILD) - string (REPLACE ";" " " _languagesStr "${_languages}") - math (EXPR _numberOfExcludedFiles "${ARGC} - 4") - if (_numberOfExcludedFiles EQUAL 0) - set (_excludedStr "") - elseif (COTIRE_VERBOSE OR _numberOfExcludedFiles LESS 4) - string (REPLACE ";" ", " _excludedStr "excluding ${ARGN}") - else() - set (_excludedStr "excluding ${_numberOfExcludedFiles} files") - endif() - set (_targetMsg "") - if (NOT _languages) - set (_targetMsg "Target ${_target} cannot be cotired.") - if (_disableMsg) - set (_targetMsg "${_targetMsg} ${_disableMsg}") - endif() - elseif (NOT _targetUsePCH AND NOT _targetAddSCU) - set (_targetMsg "${_languagesStr} target ${_target} cotired without unity build and precompiled header.") - if (_disableMsg) - set (_targetMsg "${_targetMsg} ${_disableMsg}") - endif() - elseif (NOT _targetUsePCH) - if (_excludedStr) - set (_targetMsg "${_languagesStr} target ${_target} cotired without precompiled header ${_excludedStr}.") - else() - set (_targetMsg "${_languagesStr} target ${_target} cotired without precompiled header.") - endif() - if (_disableMsg) - set (_targetMsg "${_targetMsg} ${_disableMsg}") - endif() - elseif (NOT _targetAddSCU) - if (_excludedStr) - set (_targetMsg "${_languagesStr} target ${_target} cotired without unity build ${_excludedStr}.") - else() - set (_targetMsg "${_languagesStr} target ${_target} cotired without unity build.") - endif() - if (_disableMsg) - set (_targetMsg "${_targetMsg} ${_disableMsg}") - endif() - else() - if (_excludedStr) - set (_targetMsg "${_languagesStr} target ${_target} cotired ${_excludedStr}.") - else() - set (_targetMsg "${_languagesStr} target ${_target} cotired.") - endif() - endif() - set (${_targetMsgVar} "${_targetMsg}" PARENT_SCOPE) -endfunction() - -function (cotire_choose_target_languages _target _targetLanguagesVar _wholeTargetVar) - set (_languages ${ARGN}) - set (_allSourceFiles "") - set (_allExcludedSourceFiles "") - set (_allCotiredSourceFiles "") - set (_targetLanguages "") - set (_pchEligibleTargetLanguages "") - get_target_property(_targetType ${_target} TYPE) - get_target_property(_targetSourceFiles ${_target} SOURCES) - get_target_property(_targetUsePCH ${_target} COTIRE_ENABLE_PRECOMPILED_HEADER) - get_target_property(_targetAddSCU ${_target} COTIRE_ADD_UNITY_BUILD) - set (_disableMsg "") - foreach (_language ${_languages}) - get_target_property(_prefixHeader ${_target} COTIRE_${_language}_PREFIX_HEADER) - get_target_property(_unityBuildFile ${_target} COTIRE_${_language}_UNITY_SOURCE) - if (_prefixHeader OR _unityBuildFile) - message (STATUS "cotire: target ${_target} has already been cotired.") - set (${_targetLanguagesVar} "" PARENT_SCOPE) - return() - endif() - if (_targetUsePCH AND "${_language}" MATCHES "^C|CXX$" AND DEFINED CMAKE_${_language}_COMPILER_ID) - if (CMAKE_${_language}_COMPILER_ID) - cotire_check_precompiled_header_support("${_language}" "${_target}" _disableMsg) - if (_disableMsg) - set (_targetUsePCH FALSE) - endif() - endif() - endif() - set (_sourceFiles "") - set (_excludedSources "") - set (_cotiredSources "") - cotire_filter_language_source_files(${_language} ${_target} _sourceFiles _excludedSources _cotiredSources ${_targetSourceFiles}) - if (_sourceFiles OR _excludedSources OR _cotiredSources) - list (APPEND _targetLanguages ${_language}) - endif() - if (_sourceFiles) - list (APPEND _allSourceFiles ${_sourceFiles}) - endif() - list (LENGTH _sourceFiles _numberOfSources) - if (NOT _numberOfSources LESS ${COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES}) - list (APPEND _pchEligibleTargetLanguages ${_language}) - endif() - if (_excludedSources) - list (APPEND _allExcludedSourceFiles ${_excludedSources}) - endif() - if (_cotiredSources) - list (APPEND _allCotiredSourceFiles ${_cotiredSources}) - endif() - endforeach() - set (_targetMsgLevel STATUS) - if (NOT _targetLanguages) - string (REPLACE ";" " or " _languagesStr "${_languages}") - set (_disableMsg "No ${_languagesStr} source files.") - set (_targetUsePCH FALSE) - set (_targetAddSCU FALSE) - endif() - if (_targetUsePCH) - if (_allCotiredSourceFiles) - cotire_get_source_file_property_values(_cotireTargets COTIRE_TARGET ${_allCotiredSourceFiles}) - list (REMOVE_DUPLICATES _cotireTargets) - string (REPLACE ";" ", " _cotireTargetsStr "${_cotireTargets}") - set (_disableMsg "Target sources already include a precompiled header for target(s) ${_cotireTargets}.") - set (_disableMsg "${_disableMsg} Set target property COTIRE_ENABLE_PRECOMPILED_HEADER to FALSE for targets ${_target},") - set (_disableMsg "${_disableMsg} ${_cotireTargetsStr} to get a workable build system.") - set (_targetMsgLevel SEND_ERROR) - set (_targetUsePCH FALSE) - elseif (NOT _pchEligibleTargetLanguages) - set (_disableMsg "Too few applicable sources.") - set (_targetUsePCH FALSE) - elseif (XCODE AND _allExcludedSourceFiles) - # for Xcode, we cannot apply the precompiled header to individual sources, only to the whole target - set (_disableMsg "Exclusion of source files not supported for generator Xcode.") - set (_targetUsePCH FALSE) - elseif (XCODE AND "${_targetType}" STREQUAL "OBJECT_LIBRARY") - # for Xcode, we cannot apply the required PRE_BUILD action to generate the prefix header to an OBJECT_LIBRARY target - set (_disableMsg "Required PRE_BUILD action not supported for OBJECT_LIBRARY targets for generator Xcode.") - set (_targetUsePCH FALSE) - endif() - endif() - if (_targetAddSCU) - # disable unity builds if automatic Qt processing is used - get_target_property(_targetAutoMoc ${_target} AUTOMOC) - get_target_property(_targetAutoUic ${_target} AUTOUIC) - get_target_property(_targetAutoRcc ${_target} AUTORCC) - if (_targetAutoMoc OR _targetAutoUic OR _targetAutoRcc) - if (_disableMsg) - set (_disableMsg "${_disableMsg} Target uses automatic CMake Qt processing.") - else() - set (_disableMsg "Target uses automatic CMake Qt processing.") - endif() - set (_targetAddSCU FALSE) - endif() - endif() - set_property(TARGET ${_target} PROPERTY COTIRE_ENABLE_PRECOMPILED_HEADER ${_targetUsePCH}) - set_property(TARGET ${_target} PROPERTY COTIRE_ADD_UNITY_BUILD ${_targetAddSCU}) - cotire_make_target_message(${_target} "${_targetLanguages}" "${_disableMsg}" _targetMsg ${_allExcludedSourceFiles}) - if (_targetMsg) - if (NOT DEFINED COTIREMSG_${_target}) - set (COTIREMSG_${_target} "") - endif() - if (COTIRE_VERBOSE OR NOT "${_targetMsgLevel}" STREQUAL "STATUS" OR - NOT "${COTIREMSG_${_target}}" STREQUAL "${_targetMsg}") - # cache message to avoid redundant messages on re-configure - set (COTIREMSG_${_target} "${_targetMsg}" CACHE INTERNAL "${_target} cotire message.") - message (${_targetMsgLevel} "${_targetMsg}") - endif() - endif() - list (LENGTH _targetLanguages _numberOfLanguages) - if (_numberOfLanguages GREATER 1 OR _allExcludedSourceFiles) - set (${_wholeTargetVar} FALSE PARENT_SCOPE) - else() - set (${_wholeTargetVar} TRUE PARENT_SCOPE) - endif() - set (${_targetLanguagesVar} ${_targetLanguages} PARENT_SCOPE) -endfunction() - -function (cotire_compute_unity_max_number_of_includes _target _maxIncludesVar) - set (_sourceFiles ${ARGN}) - get_target_property(_maxIncludes ${_target} COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES) - if (_maxIncludes MATCHES "(-j|--parallel|--jobs) ?([0-9]*)") - if (DEFINED CMAKE_MATCH_2) - set (_numberOfThreads "${CMAKE_MATCH_2}") - else() - set (_numberOfThreads "") - endif() - if (NOT _numberOfThreads) - # use all available cores - ProcessorCount(_numberOfThreads) - endif() - list (LENGTH _sourceFiles _numberOfSources) - math (EXPR _maxIncludes "(${_numberOfSources} + ${_numberOfThreads} - 1) / ${_numberOfThreads}") - elseif (NOT _maxIncludes MATCHES "[0-9]+") - set (_maxIncludes 0) - endif() - if (COTIRE_DEBUG) - message (STATUS "${_target} unity source max includes: ${_maxIncludes}") - endif() - set (${_maxIncludesVar} ${_maxIncludes} PARENT_SCOPE) -endfunction() - -function (cotire_process_target_language _language _configurations _target _wholeTarget _cmdsVar) - set (${_cmdsVar} "" PARENT_SCOPE) - get_target_property(_targetSourceFiles ${_target} SOURCES) - set (_sourceFiles "") - set (_excludedSources "") - set (_cotiredSources "") - cotire_filter_language_source_files(${_language} ${_target} _sourceFiles _excludedSources _cotiredSources ${_targetSourceFiles}) - if (NOT _sourceFiles AND NOT _cotiredSources) - return() - endif() - set (_cmds "") - # check for user provided unity source file list - get_property(_unitySourceFiles TARGET ${_target} PROPERTY COTIRE_${_language}_UNITY_SOURCE_INIT) - if (NOT _unitySourceFiles) - set (_unitySourceFiles ${_sourceFiles} ${_cotiredSources}) - endif() - cotire_generate_target_script( - ${_language} "${_configurations}" ${_target} _targetScript _targetConfigScript ${_unitySourceFiles}) - # set up unity files for parallel compilation - cotire_compute_unity_max_number_of_includes(${_target} _maxIncludes ${_unitySourceFiles}) - cotire_make_unity_source_file_paths(${_language} ${_target} ${_maxIncludes} _unityFiles ${_unitySourceFiles}) - list (LENGTH _unityFiles _numberOfUnityFiles) - if (_numberOfUnityFiles EQUAL 0) - return() - elseif (_numberOfUnityFiles GREATER 1) - cotire_setup_unity_generation_commands( - ${_language} ${_target} "${_targetScript}" "${_targetConfigScript}" "${_unityFiles}" _cmds ${_unitySourceFiles}) - endif() - # set up single unity file for prefix header generation - cotire_make_single_unity_source_file_path(${_language} ${_target} _unityFile) - cotire_setup_unity_generation_commands( - ${_language} ${_target} "${_targetScript}" "${_targetConfigScript}" "${_unityFile}" _cmds ${_unitySourceFiles}) - cotire_make_prefix_file_path(${_language} ${_target} _prefixFile) - # set up prefix header - if (_prefixFile) - # check for user provided prefix header files - get_property(_prefixHeaderFiles TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER_INIT) - if (_prefixHeaderFiles) - cotire_setup_prefix_generation_from_provided_command( - ${_language} ${_target} "${_targetConfigScript}" "${_prefixFile}" _cmds ${_prefixHeaderFiles}) - else() - cotire_setup_prefix_generation_from_unity_command( - ${_language} ${_target} "${_targetConfigScript}" "${_prefixFile}" "${_unityFile}" _cmds ${_unitySourceFiles}) - endif() - # check if selected language has enough sources at all - list (LENGTH _sourceFiles _numberOfSources) - if (_numberOfSources LESS ${COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES}) - set (_targetUsePCH FALSE) - else() - get_target_property(_targetUsePCH ${_target} COTIRE_ENABLE_PRECOMPILED_HEADER) - endif() - if (_targetUsePCH) - cotire_make_pch_file_path(${_language} ${_target} _pchFile) - if (_pchFile) - # first file in _sourceFiles is passed as the host file - cotire_setup_pch_file_compilation( - ${_language} ${_target} "${_targetConfigScript}" "${_prefixFile}" "${_pchFile}" ${_sourceFiles}) - cotire_setup_pch_file_inclusion( - ${_language} ${_target} ${_wholeTarget} "${_prefixFile}" "${_pchFile}" ${_sourceFiles}) - endif() - elseif (_prefixHeaderFiles) - # user provided prefix header must be included unconditionally - cotire_setup_prefix_file_inclusion(${_language} ${_target} "${_prefixFile}" ${_sourceFiles}) - endif() - endif() - # mark target as cotired for language - set_property(TARGET ${_target} PROPERTY COTIRE_${_language}_UNITY_SOURCE "${_unityFiles}") - if (_prefixFile) - set_property(TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER "${_prefixFile}") - if (_targetUsePCH AND _pchFile) - set_property(TARGET ${_target} PROPERTY COTIRE_${_language}_PRECOMPILED_HEADER "${_pchFile}") - endif() - endif() - set (${_cmdsVar} ${_cmds} PARENT_SCOPE) -endfunction() - -function (cotire_setup_clean_target _target) - set (_cleanTargetName "${_target}${COTIRE_CLEAN_TARGET_SUFFIX}") - if (NOT TARGET "${_cleanTargetName}") - cotire_set_cmd_to_prologue(_cmds) - get_filename_component(_outputDir "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}" ABSOLUTE) - list (APPEND _cmds -P "${COTIRE_CMAKE_MODULE_FILE}" "cleanup" "${_outputDir}" "${COTIRE_INTDIR}" "${_target}") - add_custom_target(${_cleanTargetName} - COMMAND ${_cmds} - WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" - COMMENT "Cleaning up target ${_target} cotire generated files" - VERBATIM) - cotire_init_target("${_cleanTargetName}") - endif() -endfunction() - -function (cotire_setup_pch_target _languages _configurations _target) - if ("${CMAKE_GENERATOR}" MATCHES "Make|Ninja") - # for makefile based generators, we add a custom target to trigger the generation of the cotire related files - set (_dependsFiles "") - foreach (_language ${_languages}) - set (_props COTIRE_${_language}_PREFIX_HEADER COTIRE_${_language}_UNITY_SOURCE) - if (NOT CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel" AND NOT - (WIN32 AND CMAKE_${_language}_COMPILER_ID MATCHES "Clang")) - # MSVC, Intel and clang-cl only create precompiled header as a side effect - list (INSERT _props 0 COTIRE_${_language}_PRECOMPILED_HEADER) - endif() - cotire_get_first_set_property_value(_dependsFile TARGET ${_target} ${_props}) - if (_dependsFile) - list (APPEND _dependsFiles "${_dependsFile}") - endif() - endforeach() - if (_dependsFiles) - set (_pchTargetName "${_target}${COTIRE_PCH_TARGET_SUFFIX}") - add_custom_target("${_pchTargetName}" DEPENDS ${_dependsFiles}) - cotire_init_target("${_pchTargetName}") - cotire_add_to_pch_all_target(${_pchTargetName}) - endif() - else() - # for other generators, we add the "clean all" target to clean up the precompiled header - cotire_setup_clean_all_target() - endif() -endfunction() - -function (cotire_filter_object_libraries _target _objectLibrariesVar) - set (_objectLibraries "") - foreach (_source ${ARGN}) - if (_source MATCHES "^\\$$") - list (APPEND _objectLibraries "${_source}") - endif() - endforeach() - set (${_objectLibrariesVar} ${_objectLibraries} PARENT_SCOPE) -endfunction() - -function (cotire_collect_unity_target_sources _target _languages _unityTargetSourcesVar) - get_target_property(_targetSourceFiles ${_target} SOURCES) - set (_unityTargetSources ${_targetSourceFiles}) - foreach (_language ${_languages}) - get_property(_unityFiles TARGET ${_target} PROPERTY COTIRE_${_language}_UNITY_SOURCE) - if (_unityFiles) - # remove source files that are included in the unity source - set (_sourceFiles "") - set (_excludedSources "") - set (_cotiredSources "") - cotire_filter_language_source_files(${_language} ${_target} _sourceFiles _excludedSources _cotiredSources ${_targetSourceFiles}) - if (_sourceFiles OR _cotiredSources) - list (REMOVE_ITEM _unityTargetSources ${_sourceFiles} ${_cotiredSources}) - endif() - # add unity source files instead - list (APPEND _unityTargetSources ${_unityFiles}) - endif() - endforeach() - # handle object libraries which are part of the target's sources - get_target_property(_linkLibrariesStrategy ${_target} COTIRE_UNITY_LINK_LIBRARIES_INIT) - if ("${_linkLibrariesStrategy}" MATCHES "^COPY_UNITY$") - cotire_filter_object_libraries(${_target} _objectLibraries ${_targetSourceFiles}) - if (_objectLibraries) - cotire_map_libraries("${_linkLibrariesStrategy}" _unityObjectLibraries ${_objectLibraries}) - list (REMOVE_ITEM _unityTargetSources ${_objectLibraries}) - list (APPEND _unityTargetSources ${_unityObjectLibraries}) - endif() - endif() - set (${_unityTargetSourcesVar} ${_unityTargetSources} PARENT_SCOPE) -endfunction() - -function (cotire_setup_unity_target_pch_usage _languages _target) - foreach (_language ${_languages}) - get_property(_unityFiles TARGET ${_target} PROPERTY COTIRE_${_language}_UNITY_SOURCE) - if (_unityFiles) - get_property(_userPrefixFile TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER_INIT) - get_property(_prefixFile TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER) - if (_userPrefixFile AND _prefixFile) - # user provided prefix header must be included unconditionally by unity sources - cotire_setup_prefix_file_inclusion(${_language} ${_target} "${_prefixFile}" ${_unityFiles}) - endif() - endif() - endforeach() -endfunction() - -function (cotire_setup_unity_build_target _languages _configurations _target) - get_target_property(_unityTargetName ${_target} COTIRE_UNITY_TARGET_NAME) - if (NOT _unityTargetName) - set (_unityTargetName "${_target}${COTIRE_UNITY_BUILD_TARGET_SUFFIX}") - endif() - # determine unity target sub type - get_target_property(_targetType ${_target} TYPE) - if ("${_targetType}" STREQUAL "EXECUTABLE") - set (_unityTargetSubType "") - elseif (_targetType MATCHES "(STATIC|SHARED|MODULE|OBJECT)_LIBRARY") - set (_unityTargetSubType "${CMAKE_MATCH_1}") - else() - message (WARNING "cotire: target ${_target} has unknown target type ${_targetType}.") - return() - endif() - # determine unity target sources - set (_unityTargetSources "") - cotire_collect_unity_target_sources(${_target} "${_languages}" _unityTargetSources) - # prevent AUTOMOC, AUTOUIC and AUTORCC properties from being set when the unity target is created - set (CMAKE_AUTOMOC OFF) - set (CMAKE_AUTOUIC OFF) - set (CMAKE_AUTORCC OFF) - if (COTIRE_DEBUG) - message (STATUS "add target ${_targetType} ${_unityTargetName} ${_unityTargetSubType} EXCLUDE_FROM_ALL ${_unityTargetSources}") - endif() - # generate unity target - if ("${_targetType}" STREQUAL "EXECUTABLE") - add_executable(${_unityTargetName} ${_unityTargetSubType} EXCLUDE_FROM_ALL ${_unityTargetSources}) - else() - add_library(${_unityTargetName} ${_unityTargetSubType} EXCLUDE_FROM_ALL ${_unityTargetSources}) - endif() - # copy output location properties - set (_outputDirProperties - ARCHIVE_OUTPUT_DIRECTORY ARCHIVE_OUTPUT_DIRECTORY_ - LIBRARY_OUTPUT_DIRECTORY LIBRARY_OUTPUT_DIRECTORY_ - RUNTIME_OUTPUT_DIRECTORY RUNTIME_OUTPUT_DIRECTORY_) - if (COTIRE_UNITY_OUTPUT_DIRECTORY) - set (_setDefaultOutputDir TRUE) - if (IS_ABSOLUTE "${COTIRE_UNITY_OUTPUT_DIRECTORY}") - set (_outputDir "${COTIRE_UNITY_OUTPUT_DIRECTORY}") - else() - # append relative COTIRE_UNITY_OUTPUT_DIRECTORY to target's actual output directory - cotire_copy_set_properties("${_configurations}" TARGET ${_target} ${_unityTargetName} ${_outputDirProperties}) - cotire_resolve_config_properties("${_configurations}" _properties ${_outputDirProperties}) - foreach (_property ${_properties}) - get_property(_outputDir TARGET ${_target} PROPERTY ${_property}) - if (_outputDir) - get_filename_component(_outputDir "${_outputDir}/${COTIRE_UNITY_OUTPUT_DIRECTORY}" ABSOLUTE) - set_property(TARGET ${_unityTargetName} PROPERTY ${_property} "${_outputDir}") - set (_setDefaultOutputDir FALSE) - endif() - endforeach() - if (_setDefaultOutputDir) - get_filename_component(_outputDir "${CMAKE_CURRENT_BINARY_DIR}/${COTIRE_UNITY_OUTPUT_DIRECTORY}" ABSOLUTE) - endif() - endif() - if (_setDefaultOutputDir) - set_target_properties(${_unityTargetName} PROPERTIES - ARCHIVE_OUTPUT_DIRECTORY "${_outputDir}" - LIBRARY_OUTPUT_DIRECTORY "${_outputDir}" - RUNTIME_OUTPUT_DIRECTORY "${_outputDir}") - endif() - else() - cotire_copy_set_properties("${_configurations}" TARGET ${_target} ${_unityTargetName} - ${_outputDirProperties}) - endif() - # copy output name - cotire_copy_set_properties("${_configurations}" TARGET ${_target} ${_unityTargetName} - ARCHIVE_OUTPUT_NAME ARCHIVE_OUTPUT_NAME_ - LIBRARY_OUTPUT_NAME LIBRARY_OUTPUT_NAME_ - OUTPUT_NAME OUTPUT_NAME_ - RUNTIME_OUTPUT_NAME RUNTIME_OUTPUT_NAME_ - PREFIX _POSTFIX SUFFIX - IMPORT_PREFIX IMPORT_SUFFIX) - # copy compile stuff - cotire_copy_set_properties("${_configurations}" TARGET ${_target} ${_unityTargetName} - COMPILE_DEFINITIONS COMPILE_DEFINITIONS_ - COMPILE_FLAGS COMPILE_OPTIONS - Fortran_FORMAT Fortran_MODULE_DIRECTORY - INCLUDE_DIRECTORIES - INTERPROCEDURAL_OPTIMIZATION INTERPROCEDURAL_OPTIMIZATION_ - POSITION_INDEPENDENT_CODE - C_COMPILER_LAUNCHER CXX_COMPILER_LAUNCHER - C_INCLUDE_WHAT_YOU_USE CXX_INCLUDE_WHAT_YOU_USE - C_VISIBILITY_PRESET CXX_VISIBILITY_PRESET VISIBILITY_INLINES_HIDDEN - C_CLANG_TIDY CXX_CLANG_TIDY) - # copy compile features - cotire_copy_set_properties("${_configurations}" TARGET ${_target} ${_unityTargetName} - C_EXTENSIONS C_STANDARD C_STANDARD_REQUIRED - CXX_EXTENSIONS CXX_STANDARD CXX_STANDARD_REQUIRED - COMPILE_FEATURES) - # copy interface stuff - cotire_copy_set_properties("${_configurations}" TARGET ${_target} ${_unityTargetName} - COMPATIBLE_INTERFACE_BOOL COMPATIBLE_INTERFACE_NUMBER_MAX COMPATIBLE_INTERFACE_NUMBER_MIN - COMPATIBLE_INTERFACE_STRING - INTERFACE_COMPILE_DEFINITIONS INTERFACE_COMPILE_FEATURES INTERFACE_COMPILE_OPTIONS - INTERFACE_INCLUDE_DIRECTORIES INTERFACE_SOURCES - INTERFACE_POSITION_INDEPENDENT_CODE INTERFACE_SYSTEM_INCLUDE_DIRECTORIES - INTERFACE_AUTOUIC_OPTIONS NO_SYSTEM_FROM_IMPORTED) - # copy link stuff - cotire_copy_set_properties("${_configurations}" TARGET ${_target} ${_unityTargetName} - BUILD_WITH_INSTALL_RPATH BUILD_WITH_INSTALL_NAME_DIR - INSTALL_RPATH INSTALL_RPATH_USE_LINK_PATH SKIP_BUILD_RPATH - LINKER_LANGUAGE LINK_DEPENDS LINK_DEPENDS_NO_SHARED - LINK_FLAGS LINK_FLAGS_ - LINK_INTERFACE_LIBRARIES LINK_INTERFACE_LIBRARIES_ - LINK_INTERFACE_MULTIPLICITY LINK_INTERFACE_MULTIPLICITY_ - LINK_SEARCH_START_STATIC LINK_SEARCH_END_STATIC - STATIC_LIBRARY_FLAGS STATIC_LIBRARY_FLAGS_ - NO_SONAME SOVERSION VERSION - LINK_WHAT_YOU_USE BUILD_RPATH) - # copy cmake stuff - cotire_copy_set_properties("${_configurations}" TARGET ${_target} ${_unityTargetName} - IMPLICIT_DEPENDS_INCLUDE_TRANSFORM RULE_LAUNCH_COMPILE RULE_LAUNCH_CUSTOM RULE_LAUNCH_LINK) - # copy Apple platform specific stuff - cotire_copy_set_properties("${_configurations}" TARGET ${_target} ${_unityTargetName} - BUNDLE BUNDLE_EXTENSION FRAMEWORK FRAMEWORK_VERSION INSTALL_NAME_DIR - MACOSX_BUNDLE MACOSX_BUNDLE_INFO_PLIST MACOSX_FRAMEWORK_INFO_PLIST MACOSX_RPATH - OSX_ARCHITECTURES OSX_ARCHITECTURES_ PRIVATE_HEADER PUBLIC_HEADER RESOURCE XCTEST - IOS_INSTALL_COMBINED XCODE_EXPLICIT_FILE_TYPE XCODE_PRODUCT_TYPE) - # copy Windows platform specific stuff - cotire_copy_set_properties("${_configurations}" TARGET ${_target} ${_unityTargetName} - GNUtoMS - COMPILE_PDB_NAME COMPILE_PDB_NAME_ - COMPILE_PDB_OUTPUT_DIRECTORY COMPILE_PDB_OUTPUT_DIRECTORY_ - PDB_NAME PDB_NAME_ PDB_OUTPUT_DIRECTORY PDB_OUTPUT_DIRECTORY_ - VS_DESKTOP_EXTENSIONS_VERSION VS_DOTNET_REFERENCES VS_DOTNET_TARGET_FRAMEWORK_VERSION - VS_GLOBAL_KEYWORD VS_GLOBAL_PROJECT_TYPES VS_GLOBAL_ROOTNAMESPACE - VS_IOT_EXTENSIONS_VERSION VS_IOT_STARTUP_TASK - VS_KEYWORD VS_MOBILE_EXTENSIONS_VERSION - VS_SCC_AUXPATH VS_SCC_LOCALPATH VS_SCC_PROJECTNAME VS_SCC_PROVIDER - VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION - VS_WINRT_COMPONENT VS_WINRT_EXTENSIONS VS_WINRT_REFERENCES - WIN32_EXECUTABLE WINDOWS_EXPORT_ALL_SYMBOLS - DEPLOYMENT_REMOTE_DIRECTORY VS_CONFIGURATION_TYPE - VS_SDK_REFERENCES VS_USER_PROPS VS_DEBUGGER_WORKING_DIRECTORY) - # copy Android platform specific stuff - cotire_copy_set_properties("${_configurations}" TARGET ${_target} ${_unityTargetName} - ANDROID_API ANDROID_API_MIN ANDROID_GUI - ANDROID_ANT_ADDITIONAL_OPTIONS ANDROID_ARCH ANDROID_ASSETS_DIRECTORIES - ANDROID_JAR_DEPENDENCIES ANDROID_JAR_DIRECTORIES ANDROID_JAVA_SOURCE_DIR - ANDROID_NATIVE_LIB_DEPENDENCIES ANDROID_NATIVE_LIB_DIRECTORIES - ANDROID_PROCESS_MAX ANDROID_PROGUARD ANDROID_PROGUARD_CONFIG_PATH - ANDROID_SECURE_PROPS_PATH ANDROID_SKIP_ANT_STEP ANDROID_STL_TYPE) - # copy CUDA platform specific stuff - cotire_copy_set_properties("${_configurations}" TARGET ${_target} ${_unityTargetName} - CUDA_PTX_COMPILATION CUDA_SEPARABLE_COMPILATION CUDA_RESOLVE_DEVICE_SYMBOLS - CUDA_EXTENSIONS CUDA_STANDARD CUDA_STANDARD_REQUIRED) - # use output name from original target - get_target_property(_targetOutputName ${_unityTargetName} OUTPUT_NAME) - if (NOT _targetOutputName) - set_property(TARGET ${_unityTargetName} PROPERTY OUTPUT_NAME "${_target}") - endif() - # use export symbol from original target - cotire_get_target_export_symbol("${_target}" _defineSymbol) - if (_defineSymbol) - set_property(TARGET ${_unityTargetName} PROPERTY DEFINE_SYMBOL "${_defineSymbol}") - if ("${_targetType}" STREQUAL "EXECUTABLE") - set_property(TARGET ${_unityTargetName} PROPERTY ENABLE_EXPORTS TRUE) - endif() - endif() - # enable parallel compilation for MSVC - if (MSVC AND "${CMAKE_GENERATOR}" MATCHES "Visual Studio") - list (LENGTH _unityTargetSources _numberOfUnityTargetSources) - if (_numberOfUnityTargetSources GREATER 1) - set_property(TARGET ${_unityTargetName} APPEND PROPERTY COMPILE_OPTIONS "/MP") - endif() - endif() - cotire_init_target(${_unityTargetName}) - cotire_add_to_unity_all_target(${_unityTargetName}) - set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_TARGET_NAME "${_unityTargetName}") -endfunction(cotire_setup_unity_build_target) - -function (cotire_target _target) - set(_options "") - set(_oneValueArgs "") - set(_multiValueArgs LANGUAGES CONFIGURATIONS) - cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) - if (NOT _option_LANGUAGES) - get_property (_option_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) - endif() - if (NOT _option_CONFIGURATIONS) - cotire_get_configuration_types(_option_CONFIGURATIONS) - endif() - # check if cotire can be applied to target at all - cotire_is_target_supported(${_target} _isSupported) - if (NOT _isSupported) - get_target_property(_imported ${_target} IMPORTED) - get_target_property(_targetType ${_target} TYPE) - if (_imported) - message (WARNING "cotire: imported ${_targetType} target ${_target} cannot be cotired.") - else() - message (STATUS "cotire: ${_targetType} target ${_target} cannot be cotired.") - endif() - return() - endif() - # resolve alias - get_target_property(_aliasName ${_target} ALIASED_TARGET) - if (_aliasName) - if (COTIRE_DEBUG) - message (STATUS "${_target} is an alias. Applying cotire to aliased target ${_aliasName} instead.") - endif() - set (_target ${_aliasName}) - endif() - # check if target needs to be cotired for build type - # when using configuration types, the test is performed at build time - cotire_init_cotire_target_properties(${_target}) - if (NOT CMAKE_CONFIGURATION_TYPES) - if (CMAKE_BUILD_TYPE) - list (FIND _option_CONFIGURATIONS "${CMAKE_BUILD_TYPE}" _index) - else() - list (FIND _option_CONFIGURATIONS "None" _index) - endif() - if (_index EQUAL -1) - if (COTIRE_DEBUG) - message (STATUS "CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} not cotired (${_option_CONFIGURATIONS})") - endif() - return() - endif() - endif() - # when not using configuration types, immediately create cotire intermediate dir - if (NOT CMAKE_CONFIGURATION_TYPES) - cotire_get_intermediate_dir(_baseDir) - file (MAKE_DIRECTORY "${_baseDir}") - endif() - # choose languages that apply to the target - cotire_choose_target_languages("${_target}" _targetLanguages _wholeTarget ${_option_LANGUAGES}) - if (NOT _targetLanguages) - return() - endif() - set (_cmds "") - foreach (_language ${_targetLanguages}) - cotire_process_target_language("${_language}" "${_option_CONFIGURATIONS}" ${_target} ${_wholeTarget} _cmd) - if (_cmd) - list (APPEND _cmds ${_cmd}) - endif() - endforeach() - get_target_property(_targetAddSCU ${_target} COTIRE_ADD_UNITY_BUILD) - if (_targetAddSCU) - cotire_setup_unity_build_target("${_targetLanguages}" "${_option_CONFIGURATIONS}" ${_target}) - endif() - get_target_property(_targetUsePCH ${_target} COTIRE_ENABLE_PRECOMPILED_HEADER) - if (_targetUsePCH) - cotire_setup_target_pch_usage("${_targetLanguages}" ${_target} ${_wholeTarget} ${_cmds}) - cotire_setup_pch_target("${_targetLanguages}" "${_option_CONFIGURATIONS}" ${_target}) - if (_targetAddSCU) - cotire_setup_unity_target_pch_usage("${_targetLanguages}" ${_target}) - endif() - endif() - get_target_property(_targetAddCleanTarget ${_target} COTIRE_ADD_CLEAN) - if (_targetAddCleanTarget) - cotire_setup_clean_target(${_target}) - endif() -endfunction(cotire_target) - -function (cotire_map_libraries _strategy _mappedLibrariesVar) - set (_mappedLibraries "") - foreach (_library ${ARGN}) - if (_library MATCHES "^\\$$") - set (_libraryName "${CMAKE_MATCH_1}") - set (_linkOnly TRUE) - set (_objectLibrary FALSE) - elseif (_library MATCHES "^\\$$") - set (_libraryName "${CMAKE_MATCH_1}") - set (_linkOnly FALSE) - set (_objectLibrary TRUE) - else() - set (_libraryName "${_library}") - set (_linkOnly FALSE) - set (_objectLibrary FALSE) - endif() - if ("${_strategy}" MATCHES "COPY_UNITY") - cotire_is_target_supported(${_libraryName} _isSupported) - if (_isSupported) - # use target's corresponding unity target, if available - get_target_property(_libraryUnityTargetName ${_libraryName} COTIRE_UNITY_TARGET_NAME) - if (TARGET "${_libraryUnityTargetName}") - if (_linkOnly) - list (APPEND _mappedLibraries "$") - elseif (_objectLibrary) - list (APPEND _mappedLibraries "$") - else() - list (APPEND _mappedLibraries "${_libraryUnityTargetName}") - endif() - else() - list (APPEND _mappedLibraries "${_library}") - endif() - else() - list (APPEND _mappedLibraries "${_library}") - endif() - else() - list (APPEND _mappedLibraries "${_library}") - endif() - endforeach() - list (REMOVE_DUPLICATES _mappedLibraries) - set (${_mappedLibrariesVar} ${_mappedLibraries} PARENT_SCOPE) -endfunction() - -function (cotire_target_link_libraries _target) - cotire_is_target_supported(${_target} _isSupported) - if (NOT _isSupported) - return() - endif() - get_target_property(_unityTargetName ${_target} COTIRE_UNITY_TARGET_NAME) - if (TARGET "${_unityTargetName}") - get_target_property(_linkLibrariesStrategy ${_target} COTIRE_UNITY_LINK_LIBRARIES_INIT) - if (COTIRE_DEBUG) - message (STATUS "unity target ${_unityTargetName} link strategy: ${_linkLibrariesStrategy}") - endif() - if ("${_linkLibrariesStrategy}" MATCHES "^(COPY|COPY_UNITY)$") - get_target_property(_linkLibraries ${_target} LINK_LIBRARIES) - if (_linkLibraries) - cotire_map_libraries("${_linkLibrariesStrategy}" _unityLinkLibraries ${_linkLibraries}) - set_target_properties(${_unityTargetName} PROPERTIES LINK_LIBRARIES "${_unityLinkLibraries}") - if (COTIRE_DEBUG) - message (STATUS "unity target ${_unityTargetName} link libraries: ${_unityLinkLibraries}") - endif() - endif() - get_target_property(_interfaceLinkLibraries ${_target} INTERFACE_LINK_LIBRARIES) - if (_interfaceLinkLibraries) - cotire_map_libraries("${_linkLibrariesStrategy}" _unityLinkInterfaceLibraries ${_interfaceLinkLibraries}) - set_target_properties(${_unityTargetName} PROPERTIES INTERFACE_LINK_LIBRARIES "${_unityLinkInterfaceLibraries}") - if (COTIRE_DEBUG) - message (STATUS "unity target ${_unityTargetName} interface link libraries: ${_unityLinkInterfaceLibraries}") - endif() - endif() - get_target_property(_manualDependencies ${_target} MANUALLY_ADDED_DEPENDENCIES) - if (_manualDependencies) - cotire_map_libraries("${_linkLibrariesStrategy}" _unityManualDependencies ${_manualDependencies}) - if (_unityManualDependencies) - add_dependencies("${_unityTargetName}" ${_unityManualDependencies}) - endif() - endif() - endif() - endif() -endfunction(cotire_target_link_libraries) - -function (cotire_cleanup _binaryDir _cotireIntermediateDirName _targetName) - if (_targetName) - file (GLOB_RECURSE _cotireFiles "${_binaryDir}/${_targetName}*.*") - else() - file (GLOB_RECURSE _cotireFiles "${_binaryDir}/*.*") - endif() - # filter files in intermediate directory - set (_filesToRemove "") - foreach (_file ${_cotireFiles}) - get_filename_component(_dir "${_file}" DIRECTORY) - get_filename_component(_dirName "${_dir}" NAME) - if ("${_dirName}" STREQUAL "${_cotireIntermediateDirName}") - list (APPEND _filesToRemove "${_file}") - endif() - endforeach() - if (_filesToRemove) - if (COTIRE_VERBOSE) - message (STATUS "cleaning up ${_filesToRemove}") - endif() - file (REMOVE ${_filesToRemove}) - endif() -endfunction() - -function (cotire_init_target _targetName) - if (COTIRE_TARGETS_FOLDER) - set_target_properties(${_targetName} PROPERTIES FOLDER "${COTIRE_TARGETS_FOLDER}") - endif() - set_target_properties(${_targetName} PROPERTIES EXCLUDE_FROM_ALL TRUE) - if (MSVC_IDE) - set_target_properties(${_targetName} PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD TRUE) - endif() -endfunction() - -function (cotire_add_to_pch_all_target _pchTargetName) - set (_targetName "${COTIRE_PCH_ALL_TARGET_NAME}") - if (NOT TARGET "${_targetName}") - add_custom_target("${_targetName}" - WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" - VERBATIM) - cotire_init_target("${_targetName}") - endif() - cotire_setup_clean_all_target() - add_dependencies(${_targetName} ${_pchTargetName}) -endfunction() - -function (cotire_add_to_unity_all_target _unityTargetName) - set (_targetName "${COTIRE_UNITY_BUILD_ALL_TARGET_NAME}") - if (NOT TARGET "${_targetName}") - add_custom_target("${_targetName}" - WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" - VERBATIM) - cotire_init_target("${_targetName}") - endif() - cotire_setup_clean_all_target() - add_dependencies(${_targetName} ${_unityTargetName}) -endfunction() - -function (cotire_setup_clean_all_target) - set (_targetName "${COTIRE_CLEAN_ALL_TARGET_NAME}") - if (NOT TARGET "${_targetName}") - cotire_set_cmd_to_prologue(_cmds) - list (APPEND _cmds -P "${COTIRE_CMAKE_MODULE_FILE}" "cleanup" "${CMAKE_BINARY_DIR}" "${COTIRE_INTDIR}") - add_custom_target(${_targetName} - COMMAND ${_cmds} - WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" - COMMENT "Cleaning up all cotire generated files" - VERBATIM) - cotire_init_target("${_targetName}") - endif() -endfunction() - -function (cotire) - set(_options "") - set(_oneValueArgs "") - set(_multiValueArgs LANGUAGES CONFIGURATIONS) - cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) - set (_targets ${_option_UNPARSED_ARGUMENTS}) - foreach (_target ${_targets}) - if (TARGET ${_target}) - cotire_target(${_target} LANGUAGES ${_option_LANGUAGES} CONFIGURATIONS ${_option_CONFIGURATIONS}) - else() - message (WARNING "cotire: ${_target} is not a target.") - endif() - endforeach() - foreach (_target ${_targets}) - if (TARGET ${_target}) - cotire_target_link_libraries(${_target}) - endif() - endforeach() -endfunction() - -if (CMAKE_SCRIPT_MODE_FILE) - - # cotire is being run in script mode - # locate -P on command args - set (COTIRE_ARGC -1) - foreach (_index RANGE ${CMAKE_ARGC}) - if (COTIRE_ARGC GREATER -1) - set (COTIRE_ARGV${COTIRE_ARGC} "${CMAKE_ARGV${_index}}") - math (EXPR COTIRE_ARGC "${COTIRE_ARGC} + 1") - elseif ("${CMAKE_ARGV${_index}}" STREQUAL "-P") - set (COTIRE_ARGC 0) - endif() - endforeach() - - # include target script if available - if ("${COTIRE_ARGV2}" MATCHES "\\.cmake$") - # the included target scripts sets up additional variables relating to the target (e.g., COTIRE_TARGET_SOURCES) - include("${COTIRE_ARGV2}") - endif() - - if (COTIRE_DEBUG) - message (STATUS "${COTIRE_ARGV0} ${COTIRE_ARGV1} ${COTIRE_ARGV2} ${COTIRE_ARGV3} ${COTIRE_ARGV4} ${COTIRE_ARGV5}") - endif() - - if (NOT COTIRE_BUILD_TYPE) - set (COTIRE_BUILD_TYPE "None") - endif() - string (TOUPPER "${COTIRE_BUILD_TYPE}" _upperConfig) - set (_includeDirs ${COTIRE_TARGET_INCLUDE_DIRECTORIES_${_upperConfig}}) - set (_systemIncludeDirs ${COTIRE_TARGET_SYSTEM_INCLUDE_DIRECTORIES_${_upperConfig}}) - set (_compileDefinitions ${COTIRE_TARGET_COMPILE_DEFINITIONS_${_upperConfig}}) - set (_compileFlags ${COTIRE_TARGET_COMPILE_FLAGS_${_upperConfig}}) - # check if target has been cotired for actual build type COTIRE_BUILD_TYPE - list (FIND COTIRE_TARGET_CONFIGURATION_TYPES "${COTIRE_BUILD_TYPE}" _index) - if (_index GREATER -1) - set (_sources ${COTIRE_TARGET_SOURCES}) - set (_sourcesDefinitions ${COTIRE_TARGET_SOURCES_COMPILE_DEFINITIONS_${_upperConfig}}) - else() - if (COTIRE_DEBUG) - message (STATUS "COTIRE_BUILD_TYPE=${COTIRE_BUILD_TYPE} not cotired (${COTIRE_TARGET_CONFIGURATION_TYPES})") - endif() - set (_sources "") - set (_sourcesDefinitions "") - endif() - set (_targetPreUndefs ${COTIRE_TARGET_PRE_UNDEFS}) - set (_targetPostUndefs ${COTIRE_TARGET_POST_UNDEFS}) - set (_sourcesPreUndefs ${COTIRE_TARGET_SOURCES_PRE_UNDEFS}) - set (_sourcesPostUndefs ${COTIRE_TARGET_SOURCES_POST_UNDEFS}) - - if ("${COTIRE_ARGV1}" STREQUAL "unity") - - if (XCODE) - # executing pre-build action under Xcode, check dependency on target script - set (_dependsOption DEPENDS "${COTIRE_ARGV2}") - else() - # executing custom command, no need to re-check for dependencies - set (_dependsOption "") - endif() - - cotire_select_unity_source_files("${COTIRE_ARGV3}" _sources ${_sources}) - - cotire_generate_unity_source( - "${COTIRE_ARGV3}" ${_sources} - LANGUAGE "${COTIRE_TARGET_LANGUAGE}" - SOURCES_COMPILE_DEFINITIONS ${_sourcesDefinitions} - PRE_UNDEFS ${_targetPreUndefs} - POST_UNDEFS ${_targetPostUndefs} - SOURCES_PRE_UNDEFS ${_sourcesPreUndefs} - SOURCES_POST_UNDEFS ${_sourcesPostUndefs} - ${_dependsOption}) - - elseif ("${COTIRE_ARGV1}" STREQUAL "prefix") - - if (XCODE) - # executing pre-build action under Xcode, check dependency on unity file and prefix dependencies - set (_dependsOption DEPENDS "${COTIRE_ARGV4}" ${COTIRE_TARGET_PREFIX_DEPENDS}) - else() - # executing custom command, no need to re-check for dependencies - set (_dependsOption "") - endif() - - set (_files "") - foreach (_index RANGE 4 ${COTIRE_ARGC}) - if (COTIRE_ARGV${_index}) - list (APPEND _files "${COTIRE_ARGV${_index}}") - endif() - endforeach() - - cotire_generate_prefix_header( - "${COTIRE_ARGV3}" ${_files} - COMPILER_LAUNCHER "${COTIRE_TARGET_${COTIRE_TARGET_LANGUAGE}_COMPILER_LAUNCHER}" - COMPILER_EXECUTABLE "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER}" - COMPILER_ARG1 ${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_ARG1} - COMPILER_ID "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_ID}" - COMPILER_VERSION "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_VERSION}" - LANGUAGE "${COTIRE_TARGET_LANGUAGE}" - IGNORE_PATH "${COTIRE_TARGET_IGNORE_PATH};${COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_PATH}" - INCLUDE_PATH ${COTIRE_TARGET_INCLUDE_PATH} - IGNORE_EXTENSIONS "${CMAKE_${COTIRE_TARGET_LANGUAGE}_SOURCE_FILE_EXTENSIONS};${COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_EXTENSIONS}" - INCLUDE_PRIORITY_PATH ${COTIRE_TARGET_INCLUDE_PRIORITY_PATH} - INCLUDE_DIRECTORIES ${_includeDirs} - SYSTEM_INCLUDE_DIRECTORIES ${_systemIncludeDirs} - COMPILE_DEFINITIONS ${_compileDefinitions} - COMPILE_FLAGS ${_compileFlags} - ${_dependsOption}) - - elseif ("${COTIRE_ARGV1}" STREQUAL "precompile") - - set (_files "") - foreach (_index RANGE 5 ${COTIRE_ARGC}) - if (COTIRE_ARGV${_index}) - list (APPEND _files "${COTIRE_ARGV${_index}}") - endif() - endforeach() - - cotire_precompile_prefix_header( - "${COTIRE_ARGV3}" "${COTIRE_ARGV4}" "${COTIRE_ARGV5}" - COMPILER_LAUNCHER "${COTIRE_TARGET_${COTIRE_TARGET_LANGUAGE}_COMPILER_LAUNCHER}" - COMPILER_EXECUTABLE "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER}" - COMPILER_ARG1 ${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_ARG1} - COMPILER_ID "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_ID}" - COMPILER_VERSION "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_VERSION}" - LANGUAGE "${COTIRE_TARGET_LANGUAGE}" - INCLUDE_DIRECTORIES ${_includeDirs} - SYSTEM_INCLUDE_DIRECTORIES ${_systemIncludeDirs} - COMPILE_DEFINITIONS ${_compileDefinitions} - COMPILE_FLAGS ${_compileFlags}) - - elseif ("${COTIRE_ARGV1}" STREQUAL "combine") - - if (COTIRE_TARGET_LANGUAGE) - set (_combinedFile "${COTIRE_ARGV3}") - set (_startIndex 4) - else() - set (_combinedFile "${COTIRE_ARGV2}") - set (_startIndex 3) - endif() - set (_files "") - foreach (_index RANGE ${_startIndex} ${COTIRE_ARGC}) - if (COTIRE_ARGV${_index}) - list (APPEND _files "${COTIRE_ARGV${_index}}") - endif() - endforeach() - - if (XCODE) - # executing pre-build action under Xcode, check dependency on files to be combined - set (_dependsOption DEPENDS ${_files}) - else() - # executing custom command, no need to re-check for dependencies - set (_dependsOption "") - endif() - - if (COTIRE_TARGET_LANGUAGE) - cotire_generate_unity_source( - "${_combinedFile}" ${_files} - LANGUAGE "${COTIRE_TARGET_LANGUAGE}" - ${_dependsOption}) - else() - cotire_generate_unity_source("${_combinedFile}" ${_files} ${_dependsOption}) - endif() - - elseif ("${COTIRE_ARGV1}" STREQUAL "cleanup") - - cotire_cleanup("${COTIRE_ARGV2}" "${COTIRE_ARGV3}" "${COTIRE_ARGV4}") - - else() - message (FATAL_ERROR "cotire: unknown command \"${COTIRE_ARGV1}\".") - endif() - -else() - - # cotire is being run in include mode - # set up all variable and property definitions - - if (NOT DEFINED COTIRE_DEBUG_INIT) - if (DEFINED COTIRE_DEBUG) - set (COTIRE_DEBUG_INIT ${COTIRE_DEBUG}) - else() - set (COTIRE_DEBUG_INIT FALSE) - endif() - endif() - option (COTIRE_DEBUG "Enable cotire debugging output?" ${COTIRE_DEBUG_INIT}) - - if (NOT DEFINED COTIRE_VERBOSE_INIT) - if (DEFINED COTIRE_VERBOSE) - set (COTIRE_VERBOSE_INIT ${COTIRE_VERBOSE}) - else() - set (COTIRE_VERBOSE_INIT FALSE) - endif() - endif() - option (COTIRE_VERBOSE "Enable cotire verbose output?" ${COTIRE_VERBOSE_INIT}) - - set (COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_EXTENSIONS "inc;inl;ipp" CACHE STRING - "Ignore headers with the listed file extensions from the generated prefix header.") - - set (COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_PATH "" CACHE STRING - "Ignore headers from these directories when generating the prefix header.") - - set (COTIRE_UNITY_SOURCE_EXCLUDE_EXTENSIONS "m;mm" CACHE STRING - "Ignore sources with the listed file extensions from the generated unity source.") - - set (COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES "2" CACHE STRING - "Minimum number of sources in target required to enable use of precompiled header.") - - if (NOT DEFINED COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT) - if (DEFINED COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES) - set (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT ${COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES}) - elseif ("${CMAKE_GENERATOR}" MATCHES "JOM|Ninja|Visual Studio") - # enable parallelization for generators that run multiple jobs by default - set (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT "-j") - else() - set (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT "0") - endif() - endif() - set (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES "${COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT}" CACHE STRING - "Maximum number of source files to include in a single unity source file.") - - if (NOT COTIRE_PREFIX_HEADER_FILENAME_SUFFIX) - set (COTIRE_PREFIX_HEADER_FILENAME_SUFFIX "_prefix") - endif() - if (NOT COTIRE_UNITY_SOURCE_FILENAME_SUFFIX) - set (COTIRE_UNITY_SOURCE_FILENAME_SUFFIX "_unity") - endif() - if (NOT COTIRE_INTDIR) - set (COTIRE_INTDIR "cotire") - endif() - if (NOT COTIRE_PCH_ALL_TARGET_NAME) - set (COTIRE_PCH_ALL_TARGET_NAME "all_pch") - endif() - if (NOT COTIRE_UNITY_BUILD_ALL_TARGET_NAME) - set (COTIRE_UNITY_BUILD_ALL_TARGET_NAME "all_unity") - endif() - if (NOT COTIRE_CLEAN_ALL_TARGET_NAME) - set (COTIRE_CLEAN_ALL_TARGET_NAME "clean_cotire") - endif() - if (NOT COTIRE_CLEAN_TARGET_SUFFIX) - set (COTIRE_CLEAN_TARGET_SUFFIX "_clean_cotire") - endif() - if (NOT COTIRE_PCH_TARGET_SUFFIX) - set (COTIRE_PCH_TARGET_SUFFIX "_pch") - endif() - if (MSVC) - # MSVC default PCH memory scaling factor of 100 percent (75 MB) is too small for template heavy C++ code - # use a bigger default factor of 170 percent (128 MB) - if (NOT DEFINED COTIRE_PCH_MEMORY_SCALING_FACTOR) - set (COTIRE_PCH_MEMORY_SCALING_FACTOR "170") - endif() - endif() - if (NOT COTIRE_UNITY_BUILD_TARGET_SUFFIX) - set (COTIRE_UNITY_BUILD_TARGET_SUFFIX "_unity") - endif() - if (NOT DEFINED COTIRE_TARGETS_FOLDER) - set (COTIRE_TARGETS_FOLDER "cotire") - endif() - if (NOT DEFINED COTIRE_UNITY_OUTPUT_DIRECTORY) - if ("${CMAKE_GENERATOR}" MATCHES "Ninja") - # generated Ninja build files do not work if the unity target produces the same output file as the cotired target - set (COTIRE_UNITY_OUTPUT_DIRECTORY "unity") - else() - set (COTIRE_UNITY_OUTPUT_DIRECTORY "") - endif() - endif() - - # define cotire cache variables - - define_property( - CACHED_VARIABLE PROPERTY "COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_PATH" - BRIEF_DOCS "Ignore headers from these directories when generating the prefix header." - FULL_DOCS - "The variable can be set to a semicolon separated list of include directories." - "If a header file is found in one of these directories or sub-directories, it will be excluded from the generated prefix header." - "If not defined, defaults to empty list." - ) - - define_property( - CACHED_VARIABLE PROPERTY "COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_EXTENSIONS" - BRIEF_DOCS "Ignore includes with the listed file extensions from the generated prefix header." - FULL_DOCS - "The variable can be set to a semicolon separated list of file extensions." - "If a header file extension matches one in the list, it will be excluded from the generated prefix header." - "Includes with an extension in CMAKE__SOURCE_FILE_EXTENSIONS are always ignored." - "If not defined, defaults to inc;inl;ipp." - ) - - define_property( - CACHED_VARIABLE PROPERTY "COTIRE_UNITY_SOURCE_EXCLUDE_EXTENSIONS" - BRIEF_DOCS "Exclude sources with the listed file extensions from the generated unity source." - FULL_DOCS - "The variable can be set to a semicolon separated list of file extensions." - "If a source file extension matches one in the list, it will be excluded from the generated unity source file." - "Source files with an extension in CMAKE__IGNORE_EXTENSIONS are always excluded." - "If not defined, defaults to m;mm." - ) - - define_property( - CACHED_VARIABLE PROPERTY "COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES" - BRIEF_DOCS "Minimum number of sources in target required to enable use of precompiled header." - FULL_DOCS - "The variable can be set to an integer > 0." - "If a target contains less than that number of source files, cotire will not enable the use of the precompiled header for the target." - "If not defined, defaults to 2." - ) - - define_property( - CACHED_VARIABLE PROPERTY "COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES" - BRIEF_DOCS "Maximum number of source files to include in a single unity source file." - FULL_DOCS - "This may be set to an integer >= 0." - "If 0, cotire will only create a single unity source file." - "If a target contains more than that number of source files, cotire will create multiple unity source files for it." - "Can be set to \"-j\" to optimize the count of unity source files for the number of available processor cores." - "Can be set to \"-j jobs\" to optimize the number of unity source files for the given number of simultaneous jobs." - "Is used to initialize the target property COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES." - "Defaults to \"-j\" for the generators Visual Studio, JOM or Ninja. Defaults to 0 otherwise." - ) - - # define cotire directory properties - - define_property( - DIRECTORY PROPERTY "COTIRE_ENABLE_PRECOMPILED_HEADER" - BRIEF_DOCS "Modify build command of cotired targets added in this directory to make use of the generated precompiled header." - FULL_DOCS - "See target property COTIRE_ENABLE_PRECOMPILED_HEADER." - ) - - define_property( - DIRECTORY PROPERTY "COTIRE_ADD_UNITY_BUILD" - BRIEF_DOCS "Add a new target that performs a unity build for cotired targets added in this directory." - FULL_DOCS - "See target property COTIRE_ADD_UNITY_BUILD." - ) - - define_property( - DIRECTORY PROPERTY "COTIRE_ADD_CLEAN" - BRIEF_DOCS "Add a new target that cleans all cotire generated files for cotired targets added in this directory." - FULL_DOCS - "See target property COTIRE_ADD_CLEAN." - ) - - define_property( - DIRECTORY PROPERTY "COTIRE_PREFIX_HEADER_IGNORE_PATH" - BRIEF_DOCS "Ignore headers from these directories when generating the prefix header." - FULL_DOCS - "See target property COTIRE_PREFIX_HEADER_IGNORE_PATH." - ) - - define_property( - DIRECTORY PROPERTY "COTIRE_PREFIX_HEADER_INCLUDE_PATH" - BRIEF_DOCS "Honor headers from these directories when generating the prefix header." - FULL_DOCS - "See target property COTIRE_PREFIX_HEADER_INCLUDE_PATH." - ) - - define_property( - DIRECTORY PROPERTY "COTIRE_PREFIX_HEADER_INCLUDE_PRIORITY_PATH" - BRIEF_DOCS "Header paths matching one of these directories are put at the top of the prefix header." - FULL_DOCS - "See target property COTIRE_PREFIX_HEADER_INCLUDE_PRIORITY_PATH." - ) - - define_property( - DIRECTORY PROPERTY "COTIRE_UNITY_SOURCE_PRE_UNDEFS" - BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file before the inclusion of each source file." - FULL_DOCS - "See target property COTIRE_UNITY_SOURCE_PRE_UNDEFS." - ) - - define_property( - DIRECTORY PROPERTY "COTIRE_UNITY_SOURCE_POST_UNDEFS" - BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file after the inclusion of each source file." - FULL_DOCS - "See target property COTIRE_UNITY_SOURCE_POST_UNDEFS." - ) - - define_property( - DIRECTORY PROPERTY "COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES" - BRIEF_DOCS "Maximum number of source files to include in a single unity source file." - FULL_DOCS - "See target property COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES." - ) - - define_property( - DIRECTORY PROPERTY "COTIRE_UNITY_LINK_LIBRARIES_INIT" - BRIEF_DOCS "Define strategy for setting up the unity target's link libraries." - FULL_DOCS - "See target property COTIRE_UNITY_LINK_LIBRARIES_INIT." - ) - - # define cotire target properties - - define_property( - TARGET PROPERTY "COTIRE_ENABLE_PRECOMPILED_HEADER" INHERITED - BRIEF_DOCS "Modify this target's build command to make use of the generated precompiled header." - FULL_DOCS - "If this property is set to TRUE, cotire will modify the build command to make use of the generated precompiled header." - "Irrespective of the value of this property, cotire will setup custom commands to generate the unity source and prefix header for the target." - "For makefile based generators cotire will also set up a custom target to manually invoke the generation of the precompiled header." - "The target name will be set to this target's name with the suffix _pch appended." - "Inherited from directory." - "Defaults to TRUE." - ) - - define_property( - TARGET PROPERTY "COTIRE_ADD_UNITY_BUILD" INHERITED - BRIEF_DOCS "Add a new target that performs a unity build for this target." - FULL_DOCS - "If this property is set to TRUE, cotire creates a new target of the same type that uses the generated unity source file instead of the target sources." - "Most of the relevant target properties will be copied from this target to the new unity build target." - "Target dependencies and linked libraries have to be manually set up for the new unity build target." - "The unity target name will be set to this target's name with the suffix _unity appended." - "Inherited from directory." - "Defaults to TRUE." - ) - - define_property( - TARGET PROPERTY "COTIRE_ADD_CLEAN" INHERITED - BRIEF_DOCS "Add a new target that cleans all cotire generated files for this target." - FULL_DOCS - "If this property is set to TRUE, cotire creates a new target that clean all files (unity source, prefix header, precompiled header)." - "The clean target name will be set to this target's name with the suffix _clean_cotire appended." - "Inherited from directory." - "Defaults to FALSE." - ) - - define_property( - TARGET PROPERTY "COTIRE_PREFIX_HEADER_IGNORE_PATH" INHERITED - BRIEF_DOCS "Ignore headers from these directories when generating the prefix header." - FULL_DOCS - "The property can be set to a list of directories." - "If a header file is found in one of these directories or sub-directories, it will be excluded from the generated prefix header." - "Inherited from directory." - "If not set, this property is initialized to \${CMAKE_SOURCE_DIR};\${CMAKE_BINARY_DIR}." - ) - - define_property( - TARGET PROPERTY "COTIRE_PREFIX_HEADER_INCLUDE_PATH" INHERITED - BRIEF_DOCS "Honor headers from these directories when generating the prefix header." - FULL_DOCS - "The property can be set to a list of directories." - "If a header file is found in one of these directories or sub-directories, it will be included in the generated prefix header." - "If a header file is both selected by COTIRE_PREFIX_HEADER_IGNORE_PATH and COTIRE_PREFIX_HEADER_INCLUDE_PATH," - "the option which yields the closer relative path match wins." - "Inherited from directory." - "If not set, this property is initialized to the empty list." - ) - - define_property( - TARGET PROPERTY "COTIRE_PREFIX_HEADER_INCLUDE_PRIORITY_PATH" INHERITED - BRIEF_DOCS "Header paths matching one of these directories are put at the top of prefix header." - FULL_DOCS - "The property can be set to a list of directories." - "Header file paths matching one of these directories will be inserted at the beginning of the generated prefix header." - "Header files are sorted according to the order of the directories in the property." - "If not set, this property is initialized to the empty list." - ) - - define_property( - TARGET PROPERTY "COTIRE_UNITY_SOURCE_PRE_UNDEFS" INHERITED - BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file before the inclusion of each target source file." - FULL_DOCS - "This may be set to a semicolon-separated list of preprocessor symbols." - "cotire will add corresponding #undef directives to the generated unit source file before each target source file." - "Inherited from directory." - "Defaults to empty string." - ) - - define_property( - TARGET PROPERTY "COTIRE_UNITY_SOURCE_POST_UNDEFS" INHERITED - BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file after the inclusion of each target source file." - FULL_DOCS - "This may be set to a semicolon-separated list of preprocessor symbols." - "cotire will add corresponding #undef directives to the generated unit source file after each target source file." - "Inherited from directory." - "Defaults to empty string." - ) - - define_property( - TARGET PROPERTY "COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES" INHERITED - BRIEF_DOCS "Maximum number of source files to include in a single unity source file." - FULL_DOCS - "This may be set to an integer > 0." - "If a target contains more than that number of source files, cotire will create multiple unity build files for it." - "If not set, cotire will only create a single unity source file." - "Inherited from directory." - "Defaults to empty." - ) - - define_property( - TARGET PROPERTY "COTIRE__UNITY_SOURCE_INIT" - BRIEF_DOCS "User provided unity source file to be used instead of the automatically generated one." - FULL_DOCS - "If set, cotire will only add the given file(s) to the generated unity source file." - "If not set, cotire will add all the target source files to the generated unity source file." - "The property can be set to a user provided unity source file." - "Defaults to empty." - ) - - define_property( - TARGET PROPERTY "COTIRE__PREFIX_HEADER_INIT" - BRIEF_DOCS "User provided prefix header file to be used instead of the automatically generated one." - FULL_DOCS - "If set, cotire will add the given header file(s) to the generated prefix header file." - "If not set, cotire will generate a prefix header by tracking the header files included by the unity source file." - "The property can be set to a user provided prefix header file (e.g., stdafx.h)." - "Defaults to empty." - ) - - define_property( - TARGET PROPERTY "COTIRE_UNITY_LINK_LIBRARIES_INIT" INHERITED - BRIEF_DOCS "Define strategy for setting up unity target's link libraries." - FULL_DOCS - "If this property is empty or set to NONE, the generated unity target's link libraries have to be set up manually." - "If this property is set to COPY, the unity target's link libraries will be copied from this target." - "If this property is set to COPY_UNITY, the unity target's link libraries will be copied from this target with considering existing unity targets." - "Inherited from directory." - "Defaults to empty." - ) - - define_property( - TARGET PROPERTY "COTIRE__UNITY_SOURCE" - BRIEF_DOCS "Read-only property. The generated unity source file(s)." - FULL_DOCS - "cotire sets this property to the path of the generated single computation unit source file for the target." - "Defaults to empty string." - ) - - define_property( - TARGET PROPERTY "COTIRE__PREFIX_HEADER" - BRIEF_DOCS "Read-only property. The generated prefix header file." - FULL_DOCS - "cotire sets this property to the full path of the generated language prefix header for the target." - "Defaults to empty string." - ) - - define_property( - TARGET PROPERTY "COTIRE__PRECOMPILED_HEADER" - BRIEF_DOCS "Read-only property. The generated precompiled header file." - FULL_DOCS - "cotire sets this property to the full path of the generated language precompiled header binary for the target." - "Defaults to empty string." - ) - - define_property( - TARGET PROPERTY "COTIRE_UNITY_TARGET_NAME" - BRIEF_DOCS "The name of the generated unity build target corresponding to this target." - FULL_DOCS - "This property can be set to the desired name of the unity target that will be created by cotire." - "If not set, the unity target name will be set to this target's name with the suffix _unity appended." - "After this target has been processed by cotire, the property is set to the actual name of the generated unity target." - "Defaults to empty string." - ) - - # define cotire source properties - - define_property( - SOURCE PROPERTY "COTIRE_EXCLUDED" - BRIEF_DOCS "Do not modify source file's build command." - FULL_DOCS - "If this property is set to TRUE, the source file's build command will not be modified to make use of the precompiled header." - "The source file will also be excluded from the generated unity source file." - "Source files that have their COMPILE_FLAGS property set will be excluded by default." - "Defaults to FALSE." - ) - - define_property( - SOURCE PROPERTY "COTIRE_DEPENDENCY" - BRIEF_DOCS "Add this source file to dependencies of the automatically generated prefix header file." - FULL_DOCS - "If this property is set to TRUE, the source file is added to dependencies of the generated prefix header file." - "If the file is modified, cotire will re-generate the prefix header source upon build." - "Defaults to FALSE." - ) - - define_property( - SOURCE PROPERTY "COTIRE_UNITY_SOURCE_PRE_UNDEFS" - BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file before the inclusion of this source file." - FULL_DOCS - "This may be set to a semicolon-separated list of preprocessor symbols." - "cotire will add corresponding #undef directives to the generated unit source file before this file is included." - "Defaults to empty string." - ) - - define_property( - SOURCE PROPERTY "COTIRE_UNITY_SOURCE_POST_UNDEFS" - BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file after the inclusion of this source file." - FULL_DOCS - "This may be set to a semicolon-separated list of preprocessor symbols." - "cotire will add corresponding #undef directives to the generated unit source file after this file is included." - "Defaults to empty string." - ) - - define_property( - SOURCE PROPERTY "COTIRE_START_NEW_UNITY_SOURCE" - BRIEF_DOCS "Start a new unity source file which includes this source file as the first one." - FULL_DOCS - "If this property is set to TRUE, cotire will complete the current unity file and start a new one." - "The new unity source file will include this source file as the first one." - "This property essentially works as a separator for unity source files." - "Defaults to FALSE." - ) - - define_property( - SOURCE PROPERTY "COTIRE_TARGET" - BRIEF_DOCS "Read-only property. Mark this source file as cotired for the given target." - FULL_DOCS - "cotire sets this property to the name of target, that the source file's build command has been altered for." - "Defaults to empty string." - ) - - message (STATUS "cotire ${COTIRE_CMAKE_MODULE_VERSION} loaded.") - -endif() diff --git a/build/Utils.cmake b/build/Utils.cmake index bbbc85662..94f3f1e56 100644 --- a/build/Utils.cmake +++ b/build/Utils.cmake @@ -273,18 +273,6 @@ macro(add_option variable description value) endmacro() -# Set as Pre-Compiled Header automaticaly or to the given file name -macro(set_target_pch TRGT) - if(ENABLE_PRECOMPILED_HEADERS AND COMMAND cotire) - if(NOT ${ARGN} STREQUAL "") - set_target_properties("${TRGT}" PROPERTIES COTIRE_CXX_PREFIX_HEADER_INIT "${ARGN}") - endif() - set_target_properties("${TRGT}" PROPERTIES COTIRE_ADD_UNITY_BUILD FALSE) - cotire("${TRGT}") - endif() -endmacro() - - # Optimize compiler settings set(STATIC_COMPILER_FAIL_REGEX @@ -401,21 +389,6 @@ macro(optimize_default_compiler_settings) add_option(ENABLE_NOISY_WARNINGS "Show all warnings even if they are too noisy" OFF ) add_option(ENABLE_WARNINGS_AS_ERRORS "Treat warnings as errors" OFF ) - if(MINGW) - # mingw compiler is known to produce unstable SSE code with -O3 hence we are trying to use -O2 instead - if(CMAKE_COMPILER_IS_GNUCXX) - foreach(flags CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_DEBUG) - string(REPLACE "-O3" "-O2" ${flags} "${${flags}}") - endforeach() - endif() - - if(CMAKE_COMPILER_IS_GNUCC) - foreach(flags CMAKE_C_FLAGS CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_DEBUG) - string(REPLACE "-O3" "-O2" ${flags} "${${flags}}") - endforeach() - endif() - endif() - set(BUILD_EXTRA_FLAGS "") set(BUILD_EXTRA_C_FLAGS "") set(BUILD_EXTRA_CXX_FLAGS "") @@ -452,15 +425,6 @@ macro(optimize_default_compiler_settings) set(CMAKE_CXX_EXTENSIONS OFF) message("Compiling with C++${CMAKE_CXX_STANDARD}") - if(MINGW) - # http://gcc.gnu.org/bugzilla/show_bug.cgi?id=40838 - # here we are trying to workaround the problem - add_extra_compiler_option(-mstackrealign) - if(NOT HAVE_CXX_MSTACKREALIGN) - add_extra_compiler_option(-mpreferred-stack-boundary=2) - endif() - endif() - if(FLG_COMPILER_IS_GNU) # High level of warnings. add_extra_compiler_option(-W) @@ -526,17 +490,13 @@ macro(optimize_default_compiler_settings) add_extra_compiler_option(-Werror) endif() - if(X86 AND NOT MINGW64 AND NOT X86_64 AND NOT APPLE) - add_extra_compiler_option(-march=i686) - endif() - # Other optimizations if(ENABLE_OMIT_FRAME_POINTER) add_extra_compiler_option(-fomit-frame-pointer) else() add_extra_compiler_option(-fno-omit-frame-pointer) - endif() - if(NOT CLANG) + endif() + if(NOT CLANG) if(ENABLE_FAST_MATH) add_extra_compiler_option(-ffast-math) else() @@ -849,7 +809,7 @@ endmacro() # Defines the main libraries. User tests should link # with one of them. -function(cxx_library_with_type_no_pch name folder type cxx_flags) +function(cxx_library_with_type name folder type cxx_flags) # type can be either STATIC or SHARED to denote a static or shared library. # ARGN refers to additional arguments after 'cxx_flags'. add_library("${name}" ${type} ${ARGN}) @@ -863,29 +823,11 @@ function(cxx_library_with_type_no_pch name folder type cxx_flags) set_target_properties("${name}" PROPERTIES FOLDER "${folder}") endfunction() -function(cxx_library_with_type name folder type cxx_flags) - cxx_library_with_type_no_pch("${name}" "${folder}" "${type}" "${cxx_flags}" ${ARGN}) - # Generate precompiled headers - set_target_pch("${name}") -endfunction() - -######################################################################## -# -# Helper functions for creating build targets. - -function(cxx_shared_library name folder cxx_flags) - cxx_library_with_type("${name}" "${folder}" SHARED "${cxx_flags}" ${ARGN}) -endfunction() - -function(cxx_library name folder cxx_flags) - cxx_library_with_type("${name}" "${folder}" "" "${cxx_flags}" ${ARGN}) -endfunction() - # cxx_executable_with_flags(name cxx_flags libs srcs...) # # creates a named C++ executable that depends on the given libraries and # is built from the given source files with the given compiler flags. -function(cxx_executable_with_flags_no_pch name folder cxx_flags libs) +function(cxx_executable_with_flags name folder cxx_flags libs) add_executable("${name}" ${ARGN}) if (cxx_flags) set_target_properties("${name}" PROPERTIES COMPILE_FLAGS "${cxx_flags}") @@ -898,36 +840,3 @@ function(cxx_executable_with_flags_no_pch name folder cxx_flags libs) # Set project folder set_target_properties("${name}" PROPERTIES FOLDER "${folder}") endfunction() - -function(cxx_executable_with_flags name folder cxx_flags libs) - cxx_executable_with_flags_no_pch("${name}" "${folder}" "${cxx_flags}" "${libs}" ${ARGN}) - # Generate precompiled headers - set_target_pch("${name}") -endfunction() - -# cxx_executable(name dir lib srcs...) -# -# creates a named target that depends on the given libs and is built -# from the given source files. dir/name.cc is implicitly included in -# the source file list. -function(cxx_executable name folder dir libs) - cxx_executable_with_flags("${name}" "${folder}" "${cxx_default}" "${libs}" "${dir}/${name}.cpp" ${ARGN}) -endfunction() - -# cxx_test_with_flags(name cxx_flags libs srcs...) -# -# creates a named C++ test that depends on the given libs and is built -# from the given source files with the given compiler flags. -function(cxx_test_with_flags name folder cxx_flags libs) - cxx_executable_with_flags("${name}" "${folder}" "${cxx_flags}" "${libs}" ${ARGN}) - add_test("${name}" "${name}") -endfunction() - -# cxx_test(name libs srcs...) -# -# creates a named test target that depends on the given libs and is -# built from the given source files. Unlike cxx_test_with_flags, -# test/name.cc is already implicitly included in the source file list. -function(cxx_test name folder libs) - cxx_test_with_flags("${name}" "${folder}" "${cxx_default}" "${libs}" "test/${name}.cc" ${ARGN}) -endfunction() diff --git a/libs/Common/CMakeLists.txt b/libs/Common/CMakeLists.txt index 631164e61..18899afde 100644 --- a/libs/Common/CMakeLists.txt +++ b/libs/Common/CMakeLists.txt @@ -1,20 +1,15 @@ # List sources files -FILE(GLOB PCH_C "Common.cpp") - FILE(GLOB LIBRARY_FILES_C "*.cpp") FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl") -# Place Common.cpp as the first file in the list -# needed by cotire when setting PCH manually -LIST(REMOVE_ITEM LIBRARY_FILES_C ${PCH_C}) -SET(LIBRARY_FILES_C "${PCH_C};${LIBRARY_FILES_C}") - -cxx_library_with_type_no_pch(Common "Libs" "" "${cxx_default}" +cxx_library_with_type(Common "Libs" "" "${cxx_default}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H} ) # Manually set Common.h as the precompiled header -set_target_pch(Common Common.h) +IF(CMAKE_VERSION VERSION_GREATER_EQUAL 3.16.0) + TARGET_PRECOMPILE_HEADERS(Common PRIVATE "Common.h") +endif() # Link its dependencies TARGET_LINK_LIBRARIES(Common ${Boost_LIBRARIES} ${OpenCV_LIBS}) diff --git a/libs/IO/CMakeLists.txt b/libs/IO/CMakeLists.txt index 14d4d942f..da3c15920 100644 --- a/libs/IO/CMakeLists.txt +++ b/libs/IO/CMakeLists.txt @@ -25,22 +25,17 @@ else() endif() # List sources files -FILE(GLOB PCH_C "Common.cpp") - FILE(GLOB LIBRARY_FILES_C "*.cpp") FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl") -# Place Common.cpp as the first file in the list -# needed by cotire when setting PCH manually -LIST(REMOVE_ITEM LIBRARY_FILES_C ${PCH_C}) -SET(LIBRARY_FILES_C "${PCH_C};${LIBRARY_FILES_C}") - -cxx_library_with_type_no_pch(IO "Libs" "" "${cxx_default}" +cxx_library_with_type(IO "Libs" "" "${cxx_default}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H} ) # Manually set Common.h as the precompiled header -set_target_pch(IO Common.h) +IF(CMAKE_VERSION VERSION_GREATER_EQUAL 3.16.0) + TARGET_PRECOMPILE_HEADERS(IO PRIVATE "Common.h") +endif() # Link its dependencies TARGET_LINK_LIBRARIES(IO Common ${PNG_LIBRARIES} ${JPEG_LIBRARIES} ${TIFF_LIBRARIES} ${EXIV2_LIBS}) diff --git a/libs/MVS/CMakeLists.txt b/libs/MVS/CMakeLists.txt index 46188c727..dcfd1ceb2 100644 --- a/libs/MVS/CMakeLists.txt +++ b/libs/MVS/CMakeLists.txt @@ -25,22 +25,17 @@ if(OpenMVS_USE_CERES) endif() # List sources files -FILE(GLOB PCH_C "Common.cpp") - FILE(GLOB LIBRARY_FILES_C "*.cpp") FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl") -# Place Common.cpp as the first file in the list -# needed by cotire when setting PCH manually -LIST(REMOVE_ITEM LIBRARY_FILES_C ${PCH_C}) -SET(LIBRARY_FILES_C "${PCH_C};${LIBRARY_FILES_C}") - -cxx_library_with_type_no_pch(MVS "Libs" "" "${cxx_default}" +cxx_library_with_type(MVS "Libs" "" "${cxx_default}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H} ) # Manually set Common.h as the precompiled header -set_target_pch(MVS Common.h) +IF(CMAKE_VERSION VERSION_GREATER_EQUAL 3.16.0) + TARGET_PRECOMPILE_HEADERS(MVS PRIVATE "Common.h") +endif() # Link its dependencies TARGET_LINK_LIBRARIES(MVS PRIVATE Common Math IO CGAL::CGAL ${CERES_LIBRARIES} ${CUDA_CUDA_LIBRARY}) diff --git a/libs/Math/CMakeLists.txt b/libs/Math/CMakeLists.txt index 23db6f06a..407e2e403 100644 --- a/libs/Math/CMakeLists.txt +++ b/libs/Math/CMakeLists.txt @@ -1,8 +1,6 @@ INCLUDE_DIRECTORIES(".") # List sources files -FILE(GLOB PCH_C "Common.cpp") - FILE(GLOB LIBRARY_FILES_C "*.cpp") FILE(GLOB LIBRARY_FILES_H "*.h" "*.inl") @@ -18,12 +16,7 @@ FILE(GLOB TRWS_LIBRARY_FILES_C "TRWS/*.cpp") FILE(GLOB TRWS_LIBRARY_FILES_H "TRWS/*.h" "TRWS/*.inl") SOURCE_GROUP("TRWS" FILES ${TRWS_LIBRARY_FILES_C} ${TRWS_LIBRARY_FILES_H}) -# Place Common.cpp as the first file in the list -# needed by cotire when setting PCH manually -LIST(REMOVE_ITEM LIBRARY_FILES_C ${PCH_C}) -SET(LIBRARY_FILES_C "${PCH_C};${LIBRARY_FILES_C}") - -cxx_library_with_type_no_pch(Math "Libs" "" "${cxx_default}" +cxx_library_with_type(Math "Libs" "" "${cxx_default}" ${LIBRARY_FILES_C} ${LIBRARY_FILES_H} ${IBFS_LIBRARY_FILES_C} ${IBFS_LIBRARY_FILES_H} ${LMFit_LIBRARY_FILES_C} ${LMFit_LIBRARY_FILES_H} @@ -31,7 +24,9 @@ cxx_library_with_type_no_pch(Math "Libs" "" "${cxx_default}" ) # Manually set Common.h as the precompiled header -set_target_pch(Math Common.h) +IF(CMAKE_VERSION VERSION_GREATER_EQUAL 3.16.0) + TARGET_PRECOMPILE_HEADERS(Math PRIVATE "Common.h") +endif() # Link its dependencies TARGET_LINK_LIBRARIES(Math Common) From 1391d6157ceeadea811cf4d82bb417d3424e144d Mon Sep 17 00:00:00 2001 From: cDc Date: Sun, 21 Mar 2021 18:10:24 +0200 Subject: [PATCH 027/252] dense: merge small sub-scenes --- libs/MVS/Scene.cpp | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/libs/MVS/Scene.cpp b/libs/MVS/Scene.cpp index 0c16feeeb..0e1f77181 100644 --- a/libs/MVS/Scene.cpp +++ b/libs/MVS/Scene.cpp @@ -897,8 +897,50 @@ unsigned Scene::Split(ImagesChunkArr& chunks, IIndex maxArea, int depthMapStep) } } #endif - DEBUG_EXTRA("Scene split (%g max-area): %u chunks (%s)", maxArea, chunks.size(), TD_TIMER_GET_FMT().c_str()); #if 1 + // merge small chunks into larger chunk neighbors + // TODO: better manage the bounding-box merge + const unsigned minNumImagesPerChunk(4); + RFOREACH(cSmall, chunks) { + ImagesChunk& chunkSmall = chunks[cSmall]; + if (chunkSmall.images.size() > minNumImagesPerChunk) + continue; + // find the chunk having the most images in common + IIndex idxBestChunk; + unsigned numLargestCommonImages(0); + FOREACH(cLarge, chunks) { + if (cSmall == cLarge) + continue; + const ImagesChunk& chunkLarge = chunks[cLarge]; + unsigned numCommonImages(0); + for (const IIndex idxImage: chunkSmall.images) + if (chunkLarge.images.find(idxImage) != chunkLarge.images.end()) + ++numCommonImages; + if (numCommonImages == 0) + continue; + if (numLargestCommonImages < numCommonImages || + (numLargestCommonImages == numCommonImages && chunks[idxBestChunk].images.size() < chunkLarge.images.size())) + { + numLargestCommonImages = numCommonImages; + idxBestChunk = cLarge; + } + } + if (numLargestCommonImages == 0) { + DEBUG_ULTIMATE("warning: small chunk can not be merged (%u chunk, %u images)", + cSmall, chunkSmall.images.size()); + continue; + } + // merge the small chunk and remove it + ImagesChunk& chunkLarge = chunks[idxBestChunk]; + DEBUG_ULTIMATE("Small chunk merged: %u chunk (%u images) -> %u chunk (%u images)", + cSmall, chunkSmall.images.size(), idxBestChunk, chunkLarge.images.size()); + chunkLarge.aabb.Insert(chunkSmall.aabb); + chunkLarge.images.insert(chunkSmall.images.begin(), chunkSmall.images.end()); + chunks.RemoveAt(cSmall); + } + #endif + DEBUG_EXTRA("Scene split (%g max-area): %u chunks (%s)", maxArea, chunks.size(), TD_TIMER_GET_FMT().c_str()); + #if 0 || defined(_DEBUG) // dump chunks for visualization FOREACH(c, chunks) { const ImagesChunk& chunk = chunks[c]; From 29a15f571872993e3cbef442340d8d60abca23a6 Mon Sep 17 00:00:00 2001 From: cDc Date: Thu, 25 Mar 2021 17:56:27 +0200 Subject: [PATCH 028/252] interface: import COLMAP depth-maps --- apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp | 211 ++++++++++++++++++----- 1 file changed, 166 insertions(+), 45 deletions(-) diff --git a/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp b/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp index 6c1082e29..7512f4908 100644 --- a/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp +++ b/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp @@ -127,6 +127,8 @@ bool Initialize(size_t argc, LPCTSTR* argv) // parse command line options boost::program_options::store(boost::program_options::command_line_parser((int)argc, argv).options(cmdline_options).positional(p).run(), OPT::vm); boost::program_options::notify(OPT::vm); + Util::ensureValidPath(OPT::strInputFileName); + WORKING_FOLDER = (File::isFolder(OPT::strInputFileName) ? OPT::strInputFileName : Util::getFilePath(OPT::strInputFileName)); INIT_WORKING_FOLDER; // parse configuration file std::ifstream ifs(MAKE_PATH_SAFE(OPT::strConfigFileName)); @@ -148,7 +150,6 @@ bool Initialize(size_t argc, LPCTSTR* argv) LOG(_T("Command line: ") APPNAME _T("%s"), Util::CommandLineToString(argc, argv).c_str()); // validate input - Util::ensureValidPath(OPT::strInputFileName); const bool bInvalidCommand(OPT::strInputFileName.empty()); if (OPT::vm.count("help") || bInvalidCommand) { boost::program_options::options_description visible("Available options"); @@ -281,11 +282,9 @@ struct Camera { }; bool Read(std::istream& stream, bool binary) { - if (binary) { + if (binary) return ReadBIN(stream); - } else { - return ReadTXT(stream); - } + return ReadTXT(stream); } // Camera list with one line of data per camera: @@ -297,6 +296,7 @@ struct Camera { in >> ID >> model >> width >> height; if (in.fail()) return false; + --ID; if (model != _T("PINHOLE")) return false; params.resize(4); @@ -307,7 +307,6 @@ struct Camera { // See: colmap/src/base/reconstruction.cc // void Reconstruction::ReadCamerasBinary(const std::string& path) bool ReadBIN(std::istream& stream) { - if (stream.peek() == EOF) return false; @@ -317,7 +316,7 @@ struct Camera { parsedNumCameras = true; } - ID = ReadBinaryLittleEndian(&stream); + ID = ReadBinaryLittleEndian(&stream)-1; model = mapCameraModel.at(ReadBinaryLittleEndian(&stream)); width = (uint32_t)ReadBinaryLittleEndian(&stream); height = (uint32_t)ReadBinaryLittleEndian(&stream); @@ -361,11 +360,9 @@ struct Image { bool operator < (const Image& rhs) const { return ID < rhs.ID; } bool Read(std::istream& stream, bool binary) { - if (binary) { + if (binary) return ReadBIN(stream); - } else { - return ReadTXT(stream); - } + return ReadTXT(stream); } // Image list with two lines of data per image: @@ -381,6 +378,7 @@ struct Image { >> idCamera >> name; if (in.fail()) return false; + --ID; --idCamera; Util::ensureValidPath(name); if (!NextLine(stream, in, false)) return false; @@ -390,7 +388,7 @@ struct Image { in >> proj.p(0) >> proj.p(1) >> (int&)proj.idPoint; if (in.fail()) break; - projs.push_back(proj); + projs.emplace_back(proj); } return true; } @@ -398,7 +396,6 @@ struct Image { // See: colmap/src/base/reconstruction.cc // void Reconstruction::ReadImagesBinary(const std::string& path) bool ReadBIN(std::istream& stream) { - if (stream.peek() == EOF) return false; @@ -408,7 +405,7 @@ struct Image { parsedNumRegImages = true; } - ID = ReadBinaryLittleEndian(&stream); + ID = ReadBinaryLittleEndian(&stream)-1; q.w() = ReadBinaryLittleEndian(&stream); q.x() = ReadBinaryLittleEndian(&stream); q.y() = ReadBinaryLittleEndian(&stream); @@ -416,7 +413,7 @@ struct Image { t(0) = ReadBinaryLittleEndian(&stream); t(1) = ReadBinaryLittleEndian(&stream); t(2) = ReadBinaryLittleEndian(&stream); - idCamera = ReadBinaryLittleEndian(&stream); + idCamera = ReadBinaryLittleEndian(&stream)-1; name = ""; char nameChar; @@ -435,7 +432,7 @@ struct Image { proj.p(0) = (float)ReadBinaryLittleEndian(&stream); proj.p(1) = (float)ReadBinaryLittleEndian(&stream); proj.idPoint = (uint32_t)ReadBinaryLittleEndian(&stream); - projs.push_back(proj); + projs.emplace_back(proj); } return true; } @@ -474,11 +471,9 @@ struct Point { bool operator < (const Image& rhs) const { return ID < rhs.ID; } bool Read(std::istream& stream, bool binary) { - if (binary) { + if (binary) return ReadBIN(stream); - } else { - return ReadTXT(stream); - } + return ReadTXT(stream); } // 3D point list with one line of data per point: @@ -497,13 +492,15 @@ struct Point { c.z = CLAMP(r,0,255); if (in.fail()) return false; + --ID; tracks.clear(); while (true) { Track track; in >> track.idImage >> track.idProj; if (in.fail()) break; - tracks.push_back(track); + --track.idImage; --track.idProj; + tracks.emplace_back(track); } return !tracks.empty(); } @@ -511,7 +508,6 @@ struct Point { // See: colmap/src/base/reconstruction.cc // void Reconstruction::ReadPoints3DBinary(const std::string& path) bool ReadBIN(std::istream& stream) { - if (stream.peek() == EOF) return false; @@ -522,7 +518,7 @@ struct Point { } int r,g,b; - ID = (uint32_t)ReadBinaryLittleEndian(&stream); + ID = (uint32_t)ReadBinaryLittleEndian(&stream)-1; p.x = (float)ReadBinaryLittleEndian(&stream); p.y = (float)ReadBinaryLittleEndian(&stream); p.z = (float)ReadBinaryLittleEndian(&stream); @@ -538,9 +534,9 @@ struct Point { tracks.clear(); for (size_t j = 0; j < trackLength; ++j) { Track track; - track.idImage = ReadBinaryLittleEndian(&stream); - track.idProj = ReadBinaryLittleEndian(&stream); - tracks.push_back(track); + track.idImage = ReadBinaryLittleEndian(&stream)-1; + track.idProj = ReadBinaryLittleEndian(&stream)-1; + tracks.emplace_back(track); } return !tracks.empty(); } @@ -562,6 +558,44 @@ struct Point { } }; typedef std::vector Points; +// structure describing an 2D dynamic matrix +template +struct Mat { + size_t width_ = 0; + size_t height_ = 0; + size_t depth_ = 0; + std::vector data_; + + size_t GetNumBytes() const { + return data_.size() * sizeof(T); + } + const T* Mat::GetChannelPtr(size_t c) const { + return data_.data()+width_*height_*c; + } + + // See: colmap/src/mvs/mat.h + void Read(const std::string& path) { + std::streampos pos; { + std::fstream text_file(path, std::ios::in | std::ios::binary); + char unused_char; + text_file >> width_ >> unused_char >> height_ >> unused_char >> depth_ >> + unused_char; + pos = text_file.tellg(); + } + data_.resize(width_ * height_ * depth_); + std::fstream binary_file(path, std::ios::in | std::ios::binary); + binary_file.seekg(pos); + ReadBinaryLittleEndian(&binary_file, &data_); + } + void Write(const std::string& path) const { + { + std::fstream text_file(path, std::ios::out); + text_file << width_ << "&" << height_ << "&" << depth_ << "&"; + } + std::fstream binary_file(path, std::ios::out | std::ios::binary | std::ios::app); + WriteBinaryLittleEndian(&binary_file, data_); + } +}; } // namespace COLMAP typedef Eigen::Matrix EMat33d; @@ -588,7 +622,7 @@ bool DetermineInputSource(const String& filenameTXT, const String& filenameBIN, } -bool ImportScene(const String& strFolder, Interface& scene) +bool ImportScene(const String& strFolder, const String& strOutFolder, Interface& scene) { COLMAP::DefineCameraModels(); // read camera list @@ -634,8 +668,8 @@ bool ImportScene(const String& strFolder, Interface& scene) camera.width = colmapCamera.width; camera.height = colmapCamera.height; } - platform.cameras.push_back(camera); - scene.platforms.push_back(platform); + platform.cameras.emplace_back(camera); + scene.platforms.emplace_back(platform); } } if (mapCameras.empty()) { @@ -665,14 +699,14 @@ bool ImportScene(const String& strFolder, Interface& scene) EnsureRotationMatrix((Matrix3x3d&)pose.R); Eigen::Map(&pose.C.x) = -(imageColmap.q.inverse() * imageColmap.t); Interface::Image image; - image.name = OPT::strImageFolder+imageColmap.name; + image.name = MAKE_PATH_REL(strOutFolder,OPT::strImageFolder+imageColmap.name); image.platformID = mapCameras.at(imageColmap.idCamera); image.cameraID = 0; image.ID = imageColmap.ID; Interface::Platform& platform = scene.platforms[image.platformID]; image.poseID = (uint32_t)platform.poses.size(); - platform.poses.push_back(pose); - scene.images.push_back(image); + platform.poses.emplace_back(pose); + scene.images.emplace_back(image); } } @@ -689,7 +723,7 @@ bool ImportScene(const String& strFolder, Interface& scene) if (!DetermineInputSource(filenamePointsTXT, filenamePointsBIN, file, filenamePoints, binary)) { return false; } - LOG_OUT() << "Reading images: " << filenamePoints << std::endl; + LOG_OUT() << "Reading points: " << filenamePoints << std::endl; COLMAP::Point point; while (file.good() && point.Read(file, binary)) { @@ -743,6 +777,95 @@ bool ImportScene(const String& strFolder, Interface& scene) scene.verticesColor.emplace_back(Interface::Color{pointcloud.colors[i]}); } } + + // read depth-maps + const String pathDepthMaps(strFolder+COLMAP_STEREO_DEPTHMAPS_FOLDER); + const String pathNormalMaps(strFolder+COLMAP_STEREO_NORMALMAPS_FOLDER); + if (File::isFolder(pathDepthMaps) && File::isFolder(pathNormalMaps)) { + // read patch-match list + CLISTDEF2IDX(IIndexArr,IIndex) imagesNeighbors(scene.images.size()); + { + const String filenameFusion(strFolder+COLMAP_PATCHMATCH); + LOG_OUT() << "Reading patch-match configuration: " << filenameFusion << std::endl; + std::ifstream file(filenameFusion); + if (!file.good()) { + VERBOSE("error: unable to open file '%s'", filenameFusion.c_str()); + return false; + } + while (true) { + String imageName, neighbors; + std::getline(file, imageName); + std::getline(file, neighbors); + if (file.fail() || imageName.empty() || neighbors.empty()) + break; + const ImagesMap::const_iterator it_image = std::find_if(mapImages.begin(), mapImages.end(), + [&imageName](const ImagesMap::value_type& image) { + return image.first.name == imageName; + }); + if (it_image == mapImages.end()) + continue; + IIndexArr& imageNeighbors = imagesNeighbors[it_image->second]; + CLISTDEF2(String) neighborNames; + Util::strSplit(neighbors, _T(','), neighborNames); + FOREACH(i, neighborNames) { + String& neighborName = neighborNames[i]; + Util::strTrim(neighborName, _T(" ")); + const ImagesMap::const_iterator it_neighbor = std::find_if(mapImages.begin(), mapImages.end(), + [&neighborName](const ImagesMap::value_type& image) { + return image.first.name == neighborName; + }); + if (it_neighbor == mapImages.end()) { + if (i == 0) + break; + continue; + } + imageNeighbors.emplace_back(scene.images[it_neighbor->second].ID); + } + } + } + LOG_OUT() << "Reading depth-maps/normal-maps: " << pathDepthMaps << " and " << pathNormalMaps << std::endl; + Util::ensureFolder(strOutFolder); + const String strType[] = {".geometric.bin", ".photometric.bin"}; + FOREACH(idx, scene.images) { + const Interface::Image& image = scene.images[idx]; + COLMAP::Mat colDepthMap, colNormalMap; + const String filenameImage(Util::getFileNameExt(image.name)); + for (int i=0; i<2; ++i) { + const String filenameDepthMaps(pathDepthMaps+filenameImage+strType[i]); + if (File::isFile(filenameDepthMaps)) { + colDepthMap.Read(filenameDepthMaps); + const String filenameNormalMaps(pathNormalMaps+filenameImage+strType[i]); + if (File::isFile(filenameNormalMaps)) { + colNormalMap.Read(filenameNormalMaps); + } + break; + } + } + if (!colDepthMap.data_.empty()) { + IIndexArr IDs = {image.ID}; + IDs.Join(imagesNeighbors[idx]); + const Interface::Platform& platform = scene.platforms[image.platformID]; + const Interface::Platform::Pose pose(platform.GetPose(image.cameraID, image.poseID)); + const Interface::Mat33d K(platform.GetFullK(image.cameraID, (uint32_t)colDepthMap.width_, (uint32_t)colDepthMap.height_)); + MVS::DepthMap depthMap((int)colDepthMap.height_, (int)colDepthMap.width_); + memcpy(depthMap.getData(), colDepthMap.data_.data(), colDepthMap.GetNumBytes()); + MVS::NormalMap normalMap; + if (!colNormalMap.data_.empty()) { + normalMap.create((int)colNormalMap.height_, (int)colNormalMap.width_); + cv::merge(std::vector{ + cv::Mat((int)colNormalMap.height_, (int)colNormalMap.width_, CV_32F, (void*)colNormalMap.GetChannelPtr(0)), + cv::Mat((int)colNormalMap.height_, (int)colNormalMap.width_, CV_32F, (void*)colNormalMap.GetChannelPtr(1)), + cv::Mat((int)colNormalMap.height_, (int)colNormalMap.width_, CV_32F, (void*)colNormalMap.GetChannelPtr(2)) + }, normalMap); + } + MVS::ConfidenceMap confMap; + const auto depthMM(std::minmax_element(colDepthMap.data_.cbegin(), colDepthMap.data_.cend())); + const MVS::Depth dMin(*depthMM.first), dMax(*depthMM.second); + if (!ExportDepthDataRaw(strOutFolder+String::FormatString("depth%04u.dmap", image.ID), MAKE_PATH_FULL(strOutFolder, image.name), IDs, depthMap.size(), K, pose.R, pose.C, dMin, dMax, depthMap, normalMap, confMap)) + return false; + } + } + } return true; } @@ -792,11 +915,11 @@ bool ExportScene(const String& strFolder, const Interface& scene) cam.width = ptrImage->GetWidth(); cam.height = ptrImage->GetHeight(); // unnormalize camera intrinsics - const double scale(MVS::Camera::GetNormalizationScale(cam.width, cam.height)); - cam.params[0] = camera.K(0,0) * scale; - cam.params[1] = camera.K(1,1) * scale; - cam.params[2] = camera.K(0,2) * scale; - cam.params[3] = camera.K(1,2) * scale; + const Interface::Mat33d K(platform.GetFullK(0, cam.width, cam.height)); + cam.params[0] = K(0,0); + cam.params[1] = K(1,1); + cam.params[2] = K(0,2); + cam.params[3] = K(1,2); } else { cam.width = camera.width; cam.height = camera.height; @@ -872,15 +995,12 @@ bool ExportScene(const String& strFolder, const Interface& scene) point.p = vertex.X; for (const Interface::Vertex::View& view: vertex.views) { COLMAP::Image& img = images[view.imageID]; - COLMAP::Point::Track track; - track.idImage = view.imageID; - track.idProj = (uint32_t)img.projs.size(); - point.tracks.push_back(track); + point.tracks.emplace_back(COLMAP::Point::Track{view.imageID, (uint32_t)img.projs.size()}); COLMAP::Image::Proj proj; proj.idPoint = ID; const Point3 X(vertex.X); ProjectVertex_3x4_3_2(cameras[view.imageID].P.val, X.ptr(), proj.p.data()); - img.projs.push_back(proj); + img.projs.emplace_back(proj); } point.c = scene.verticesColor.empty() ? Interface::Col3(255,255,255) : scene.verticesColor[ID].c; point.e = 0; @@ -1118,10 +1238,11 @@ int main(int argc, LPCTSTR* argv) } else { // read COLMAP input data Interface scene; - if (!ImportScene(MAKE_PATH_SAFE(OPT::strInputFileName), scene)) + const String strOutFolder(Util::getFilePath(MAKE_PATH_FULL(WORKING_FOLDER_FULL, OPT::strOutputFileName))); + if (!ImportScene(MAKE_PATH_SAFE(OPT::strInputFileName), strOutFolder, scene)) return EXIT_FAILURE; // write MVS input data - Util::ensureFolder(Util::getFullPath(MAKE_PATH_FULL(WORKING_FOLDER_FULL, OPT::strOutputFileName))); + Util::ensureFolder(strOutFolder); if (!ARCHIVE::SerializeSave(scene, MAKE_PATH_SAFE(OPT::strOutputFileName))) return EXIT_FAILURE; VERBOSE("Exported data: %u images & %u vertices (%s)", scene.images.size(), scene.vertices.size(), TD_TIMER_GET_FMT().c_str()); From b6575348a0476a998014ca98202d0b3e7b1b678f Mon Sep 17 00:00:00 2001 From: cDc Date: Wed, 31 Mar 2021 16:59:24 +0300 Subject: [PATCH 029/252] interface: add MVS preset to the python script --- MvgMvsPipeline.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MvgMvsPipeline.py b/MvgMvsPipeline.py index 4f30795c0..06264ac84 100644 --- a/MvgMvsPipeline.py +++ b/MvgMvsPipeline.py @@ -44,6 +44,7 @@ GLOBAL = [0, 1, 2, 4, 9, 10, 11, 12, 13] MVG_SEQ = [0, 1, 2, 3, 5, 6, 7] MVG_GLOBAL = [0, 1, 2, 4, 5, 6, 7] + MVS = [10, 11, 12, 13] MVS_SGM = [14, 15] default : SEQUENTIAL @@ -117,6 +118,7 @@ def find(afile): 'GLOBAL': [0, 1, 2, 4, 9, 10, 11, 12, 13], 'MVG_SEQ': [0, 1, 2, 3, 5, 6, 7], 'MVG_GLOBAL': [0, 1, 2, 4, 5, 6, 7], + 'MVS': [10, 11, 12, 13], 'MVS_SGM': [14, 15]} PRESET_DEFAULT = 'SEQUENTIAL' From ecea2ddf8918e64b3b751ade17b5634422a6bba1 Mon Sep 17 00:00:00 2001 From: cDc Date: Wed, 31 Mar 2021 17:20:10 +0300 Subject: [PATCH 030/252] interface: fix COLMAP on linux --- apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp b/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp index 7512f4908..757f039c7 100644 --- a/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp +++ b/apps/InterfaceCOLMAP/InterfaceCOLMAP.cpp @@ -569,7 +569,7 @@ struct Mat { size_t GetNumBytes() const { return data_.size() * sizeof(T); } - const T* Mat::GetChannelPtr(size_t c) const { + const T* GetChannelPtr(size_t c) const { return data_.data()+width_*height_*c; } From 464493cebbc21e544f389b49e0603681a8bf3170 Mon Sep 17 00:00:00 2001 From: cDc Date: Thu, 1 Apr 2021 22:18:42 +0300 Subject: [PATCH 031/252] common: make binary projects portable --- libs/Common/Types.inl | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/libs/Common/Types.inl b/libs/Common/Types.inl index 381b58dd7..a3a409fdb 100644 --- a/libs/Common/Types.inl +++ b/libs/Common/Types.inl @@ -3619,8 +3619,7 @@ namespace boost { // Serialization support for cv::Mat template - void save(Archive& ar, const cv::Mat& m, const unsigned int /*version*/) - { + void save(Archive& ar, const cv::Mat& m, const unsigned int /*version*/) { const int elem_type = m.type(); const size_t elem_size = m.elemSize(); @@ -3639,8 +3638,7 @@ namespace boost { } } template - void load(Archive& ar, cv::Mat& m, const unsigned int /*version*/) - { + void load(Archive& ar, cv::Mat& m, const unsigned int /*version*/) { int cols, rows, elem_type; size_t elem_size; @@ -3661,8 +3659,7 @@ namespace boost { // Serialization support for cv::Mat_ template - void save(Archive& ar, const cv::Mat_<_Tp>& m, const unsigned int /*version*/) - { + void save(Archive& ar, const cv::Mat_<_Tp>& m, const unsigned int /*version*/) { ar & m.cols; ar & m.rows; @@ -3676,8 +3673,7 @@ namespace boost { } } template - void load(Archive& ar, cv::Mat_<_Tp>& m, const unsigned int /*version*/) - { + void load(Archive& ar, cv::Mat_<_Tp>& m, const unsigned int /*version*/) { int cols, rows; ar & cols; ar & rows; @@ -3811,7 +3807,7 @@ enum ARCHIVE_TYPE { // export the current state of the given reconstruction object template -bool SerializeSave(const TYPE& obj, std::ofstream& fs, ARCHIVE_TYPE type, unsigned flags=0) +bool SerializeSave(const TYPE& obj, std::ofstream& fs, ARCHIVE_TYPE type, unsigned flags=boost::archive::no_header) { // serialize out the current state switch (type) { @@ -3848,7 +3844,7 @@ bool SerializeSave(const TYPE& obj, std::ofstream& fs, ARCHIVE_TYPE type, unsign return true; } // SerializeSave template -bool SerializeSave(const TYPE& obj, const SEACAVE::String& fileName, ARCHIVE_TYPE type, unsigned flags=0) +bool SerializeSave(const TYPE& obj, const SEACAVE::String& fileName, ARCHIVE_TYPE type, unsigned flags=boost::archive::no_header) { // open the output stream std::ofstream fs(fileName, std::ios::out | std::ios::binary); @@ -3860,7 +3856,7 @@ bool SerializeSave(const TYPE& obj, const SEACAVE::String& fileName, ARCHIVE_TYP // import the state to the given reconstruction object template -bool SerializeLoad(TYPE& obj, std::ifstream& fs, ARCHIVE_TYPE type, unsigned flags=0) +bool SerializeLoad(TYPE& obj, std::ifstream& fs, ARCHIVE_TYPE type, unsigned flags=boost::archive::no_header) { try { // serialize in the saved state @@ -3903,7 +3899,7 @@ bool SerializeLoad(TYPE& obj, std::ifstream& fs, ARCHIVE_TYPE type, unsigned fla return true; } // SerializeLoad template -bool SerializeLoad(TYPE& obj, const SEACAVE::String& fileName, ARCHIVE_TYPE type, unsigned flags=0) +bool SerializeLoad(TYPE& obj, const SEACAVE::String& fileName, ARCHIVE_TYPE type, unsigned flags=boost::archive::no_header) { // open the input stream std::ifstream fs(fileName, std::ios::in | std::ios::binary); From 8e96a401914143854416523735bc0b42246e0aad Mon Sep 17 00:00:00 2001 From: cDc Date: Mon, 5 Apr 2021 10:22:28 +0300 Subject: [PATCH 032/252] viewer: display views seeing selected point or points seen by the current view --- .gitignore | 4 +- apps/Viewer/Camera.cpp | 144 +++++++++++---------- apps/Viewer/Camera.h | 30 +++-- apps/Viewer/Common.h | 15 ++- apps/Viewer/Scene.cpp | 110 +++++++++++----- apps/Viewer/Scene.h | 3 +- apps/Viewer/Viewer.cpp | 7 +- apps/Viewer/Window.cpp | 272 ++++++++++++++++++++++++++++------------ apps/Viewer/Window.h | 40 ++++-- libs/Common/AABB.h | 4 +- libs/Common/AABB.inl | 6 +- libs/MVS/Mesh.cpp | 18 +++ libs/MVS/Mesh.h | 1 + libs/MVS/PointCloud.cpp | 18 +++ libs/MVS/PointCloud.h | 1 + 15 files changed, 461 insertions(+), 212 deletions(-) diff --git a/.gitignore b/.gitignore index 9a2dbda4c..a12459285 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,4 @@ CMakeSettings.json .idea/ .vscode/ out/ -bin/ -binaries/ -build_wsl/ +bin*/ diff --git a/apps/Viewer/Camera.cpp b/apps/Viewer/Camera.cpp index b217c2d6b..7efa4eb77 100644 --- a/apps/Viewer/Camera.cpp +++ b/apps/Viewer/Camera.cpp @@ -40,122 +40,136 @@ using namespace VIEWER; // S T R U C T S /////////////////////////////////////////////////// -Camera::Camera(const AABB3d& _box, double _fov) +Camera::Camera(const AABB3d& _box, const Point3d& _center, float _scaleF, float _fov) : - box(_box), - width(0), height(0), + boxScene(_box), + centerScene(_center), rotation(Eigen::Quaterniond::Identity()), center(Eigen::Vector3d::Zero()), - dist(0), radius(100), fov(_fov), - scaleF(1.f), + dist(0), radius(100), + fovDef(_fov), scaleFDef(_scaleF), prevCamID(NO_ID), currentCamID(NO_ID), maxCamID(0) { Reset(); } -void Camera::CopyOf(const Camera& rhs) -{ - rotation = rhs.rotation; - center = rhs.center; - dist = rhs.dist; - radius = rhs.radius; - fov = rhs.fov; -} - - -void Camera::Init(const AABB3d& _box) -{ - box = _box; - Reset(); -} - void Camera::Reset() { - center = box.GetCenter(); - radius = box.GetSize().norm()*0.5; + if (boxScene.IsEmpty()) { + center = Point3d::ZERO; + radius = 1; + } else { + center = centerScene; + radius = boxScene.GetSize().norm()*0.5; + } rotation = Eigen::Quaterniond::Identity(); - scaleF = 1.f; + scaleF = scaleFDef; prevCamID = currentCamID = NO_ID; - fov = 40; - dist = radius * 0.5 / SIN(D2R(fov)); - Resize(width, height); + fov = fovDef; + dist = radius*0.5 / SIN(D2R((double)fov)); + if (size.area()) + Resize(size); } -void Camera::Resize(int _width, int _height) +void Camera::Resize(const cv::Size& _size) { + ASSERT(MINF(_size.width, _size.height) > 0); + size = _size; glMatrixMode(GL_PROJECTION); glLoadIdentity(); - const GLfloat zNear = 1e-2f; - const GLfloat zFar = 1e5f; - width = _width; height = _height; - GLfloat aspect = float(width)/float(height); - GLfloat fH = TAN(FD2R((float)fov)) * zNear; - GLfloat fW = fH * aspect; - glFrustum(-fW, fW, -fH, fH, zNear, zFar); + const GLfloat zNear = 1e-3f; + const GLfloat zFar = (float)boxScene.GetSize().norm()*10; + const GLfloat aspect = float(size.width)/float(size.height); + if (fov == 5.f) { + // orthographic projection + const GLfloat fH = (float)boxScene.GetSize().norm()*0.5f; + const GLfloat fW = fH * aspect; + glOrtho(-fW, fW, -fH, fH, zNear, zFar); + } else { + // perspective projection + const GLfloat fH = TAN(D2R(fov)) * zNear; + const GLfloat fW = fH * aspect; + glFrustum(-fW, fW, -fH, fH, zNear, zFar); + } } -void Camera::SetFOV(double _fov) +void Camera::SetFOV(float _fov) { - fov = _fov; - Resize(width, height); + fov = MAXF(_fov, 5.f); + Resize(size); } Eigen::Vector3d Camera::GetPosition() const { - const Eigen::Matrix3d R(rotation.toRotationMatrix()); - const Eigen::Vector3d eye(0, 0, dist); - return R * eye + center; + const Eigen::Matrix3d R(GetRotation()); + return center + R.col(2) * dist; +} + +Eigen::Matrix3d Camera::GetRotation() const +{ + return rotation.toRotationMatrix(); } Eigen::Matrix4d Camera::GetLookAt() const { - const Eigen::Matrix3d R(rotation.toRotationMatrix()); - const Eigen::Vector3d eye(R.col(2) * dist + center); + const Eigen::Matrix3d R(GetRotation()); + const Eigen::Vector3d eye(center + R.col(2) * dist); const Eigen::Vector3d up(R.col(1)); - + const Eigen::Vector3d n((center-eye).normalized()); const Eigen::Vector3d s(n.cross(up)); const Eigen::Vector3d v(s.cross(n)); - - Eigen::Matrix4d m; - m << - s(0), s(1), s(2), -eye.dot(s), - v(0), v(1), v(2), -eye.dot(v), - -n(0), -n(1), -n(2), eye.dot(n), - 0.0, 0.0, 0.0, 1.0; + + Eigen::Matrix4d m; m << + s(0), s(1), s(2), -eye.dot(s), + v(0), v(1), v(2), -eye.dot(v), + -n(0), -n(1), -n(2), eye.dot(n), + 0.0, 0.0, 0.0, 1.0; return m; } void Camera::GetLookAt(Eigen::Vector3d& _eye, Eigen::Vector3d& _center, Eigen::Vector3d& _up) const { - const Eigen::Matrix3d R(rotation.toRotationMatrix()); - const Eigen::Vector3d eye(0, 0, dist); - const Eigen::Vector3d up(0, 1, 0); - - _eye = R * eye + center; + const Eigen::Matrix3d R(GetRotation()); + _eye = center + R.col(2) * dist; _center = center; - _up = R * up; + _up = R.col(1); } + void Camera::Rotate(const Eigen::Vector2d& pos, const Eigen::Vector2d& prevPos) { - if (pos.isApprox(prevPos, ZERO_TOLERANCE)) + if (pos.isApprox(prevPos, ZEROTOLERANCE())) return; Eigen::Vector3d oldp(prevPos.x(), prevPos.y(), 0); Eigen::Vector3d newp(pos.x(), pos.y(), 0); - const double radius_virtual_sphere(0.9); - Project2Sphere(radius_virtual_sphere, oldp); - Project2Sphere(radius_virtual_sphere, newp); - Eigen::Quaterniond dr; - dr.setFromTwoVectors(newp, oldp); - rotation *= dr; + const double radiusSphere(0.9); + ProjectOnSphere(radiusSphere, oldp); + ProjectOnSphere(radiusSphere, newp); + rotation *= Eigen::Quaterniond().setFromTwoVectors(newp, oldp); + + // disable camera view mode + prevCamID = currentCamID; +} + +void Camera::Translate(const Eigen::Vector2d& pos, const Eigen::Vector2d& prevPos) +{ + if (pos.isApprox(prevPos, ZEROTOLERANCE())) + return; + + Eigen::Matrix P, V; + glGetDoublev(GL_MODELVIEW_MATRIX, V.data()); + glGetDoublev(GL_PROJECTION_MATRIX, P.data()); + Eigen::Vector3d centerScreen((P*V*center.homogeneous().eval()).hnormalized()); + centerScreen.head<2>() += prevPos - pos; + center = (V.inverse()*P.inverse()*centerScreen.homogeneous().eval()).hnormalized(); // disable camera view mode prevCamID = currentCamID; } -void Camera::Project2Sphere(double radius, Eigen::Vector3d& p) const +void Camera::ProjectOnSphere(double radius, Eigen::Vector3d& p) const { p.z() = 0; const double d = p.x()* p.x()+ p.y() * p.y(); diff --git a/apps/Viewer/Camera.h b/apps/Viewer/Camera.h index 37d982386..d83880a3e 100644 --- a/apps/Viewer/Camera.h +++ b/apps/Viewer/Camera.h @@ -48,34 +48,38 @@ class Camera public: EIGEN_MAKE_ALIGNED_OPERATOR_NEW - AABB3d box; - int width, height; + cv::Size size; + AABB3d boxScene; + Eigen::Vector3d centerScene; Eigen::Quaterniond rotation; Eigen::Vector3d center; - double dist; - double radius; - double fov; - float scaleF; + double dist, radius; + float fov, fovDef; + float scaleF, scaleFDef; MVS::IIndex prevCamID, currentCamID, maxCamID; public: - explicit Camera(const AABB3d& _box=AABB3d(true), double _fov=40); - void CopyOf(const Camera&); + Camera(const AABB3d& _box=AABB3d(true), const Point3d& _center=Point3d::ZERO, float _scaleF=1, float _fov=40); - void Init(const AABB3d&); void Reset(); - void Resize(int _width, int _height); - void SetFOV(double _fov); + void Resize(const cv::Size&); + void SetFOV(float _fov); + + const cv::Size& GetSize() const { return size; } Eigen::Vector3d GetPosition() const; + Eigen::Matrix3d GetRotation() const; Eigen::Matrix4d GetLookAt() const; + void GetLookAt(Eigen::Vector3d& eye, Eigen::Vector3d& center, Eigen::Vector3d& up) const; void Rotate(const Eigen::Vector2d& pos, const Eigen::Vector2d& prevPos); + void Translate(const Eigen::Vector2d& pos, const Eigen::Vector2d& prevPos); + + bool IsCameraViewMode() const { return prevCamID != currentCamID && currentCamID != NO_ID; } protected: - void Project2Sphere(double radius, Eigen::Vector3d& p) const; + void ProjectOnSphere(double radius, Eigen::Vector3d& p) const; }; -typedef CSharedPtr CameraPtr; /*----------------------------------------------------------------*/ } // namespace VIEWER diff --git a/apps/Viewer/Common.h b/apps/Viewer/Common.h index d1f1912b5..f3387834e 100644 --- a/apps/Viewer/Common.h +++ b/apps/Viewer/Common.h @@ -59,16 +59,19 @@ using namespace SEACAVE; namespace VIEWER { // the conversion matrix from OpenGL default coordinate system -// to the camera coordinate system: +// to the camera coordinate system (NADIR orientation): // [ 1 0 0 0] * [ x ] = [ x ] // 0 -1 0 0 y -y // 0 0 -1 0 z -z // 0 0 0 1 1 1 -static const GLfloat gs_convert[4][4] = { - {1.f, 0.f, 0.f, 0.f}, - {0.f, -1.f, 0.f, 0.f}, - {0.f, 0.f, -1.f, 0.f}, - {0.f, 0.f, 0.f, 1.f}}; +static const Eigen::Matrix4d gs_convert = [] { + Eigen::Matrix4d tmp; tmp << + 1, 0, 0, 0, + 0, -1, 0, 0, + 0, 0, -1, 0, + 0, 0, 0, 1; + return tmp; +}(); /// given rotation matrix R and translation vector t, /// column-major matrix m is equal to: diff --git a/apps/Viewer/Scene.cpp b/apps/Viewer/Scene.cpp index c5dca5bd5..b15bcb4b2 100644 --- a/apps/Viewer/Scene.cpp +++ b/apps/Viewer/Scene.cpp @@ -234,6 +234,11 @@ void Scene::Empty() { ReleasePointCloud(); ReleaseMesh(); + if (window.IsValid()) { + window.ReleaseClbk(); + window.Reset(); + window.SetName(_T("(empty)")); + } textures.Release(); images.Release(); scene.Release(); @@ -241,6 +246,8 @@ void Scene::Empty() } void Scene::Release() { + if (window.IsValid()) + window.SetVisible(false); if (!thread.isRunning()) { events.AddEvent(new EVTClose()); thread.join(); @@ -264,23 +271,19 @@ void Scene::ReleaseMesh() } } -bool Scene::Init(int width, int height, LPCTSTR windowName, LPCTSTR fileName, LPCTSTR meshFileName) +bool Scene::Init(const cv::Size& size, LPCTSTR windowName, LPCTSTR fileName, LPCTSTR meshFileName) { ASSERT(scene.IsEmpty()); // init window if (glfwInit() == GL_FALSE) return false; - if (!window.Init(width, height, windowName)) + if (!window.Init(size, windowName)) return false; if (glewInit() != GLEW_OK) return false; name = windowName; window.clbkOpenScene = DELEGATEBINDCLASS(Window::ClbkOpenScene, &Scene::Open, this); - window.clbkExportScene = DELEGATEBINDCLASS(Window::ClbkExportScene, &Scene::Export, this); - window.clbkRayScene = DELEGATEBINDCLASS(Window::ClbkRayScene, &Scene::CastRay, this); - window.clbkCompilePointCloud = DELEGATEBINDCLASS(Window::ClbkCompilePointCloud, &Scene::CompilePointCloud, this); - window.clbkCompileMesh = DELEGATEBINDCLASS(Window::ClbkCompileMesh, &Scene::CompileMesh, this); // init OpenGL glPolygonMode(GL_FRONT, GL_FILL); @@ -305,9 +308,9 @@ bool Scene::Init(int width, int height, LPCTSTR windowName, LPCTSTR fileName, LP thread.start(ThreadWorker); // open scene or init empty scene - if (fileName == NULL || !Open(fileName, meshFileName)) - window.SetCamera(CameraPtr(new Camera())); - + window.SetCamera(Camera()); + if (fileName != NULL) + Open(fileName, meshFileName); window.SetVisible(true); return true; } @@ -337,14 +340,18 @@ bool Scene::Open(LPCTSTR fileName, LPCTSTR meshFileName) // init scene AABB3d bounds(true); + AABB3d imageBounds(true); + Point3d center(Point3d::INF); if (!scene.pointcloud.IsEmpty()) { bounds = scene.pointcloud.GetAABB(MINF(3u,scene.nCalibratedImages)); if (bounds.IsEmpty()) bounds = scene.pointcloud.GetAABB(); + center = scene.pointcloud.GetCenter(); } if (!scene.mesh.IsEmpty()) { scene.mesh.ComputeNormalFaces(); bounds.Insert(scene.mesh.GetAABB()); + center = scene.mesh.GetCenter(); } // init images @@ -353,7 +360,8 @@ bool Scene::Open(LPCTSTR fileName, LPCTSTR meshFileName) const MVS::Image& imageData = scene.images[idxImage]; if (!imageData.IsValid()) continue; - images.AddConstruct(idxImage); + images.emplace_back(idxImage); + imageBounds.InsertFull(imageData.camera.C); } // init and load texture @@ -379,10 +387,18 @@ bool Scene::Open(LPCTSTR fileName, LPCTSTR meshFileName) CompileMesh(); // init camera - window.SetCamera(CameraPtr(new Camera(bounds))); - window.camera->maxCamID = images.size(); + window.SetCamera(Camera(bounds, + center == Point3d::INF ? Point3d(bounds.GetCenter()) : center, + images.size()<2?1.f:(float)imageBounds.EnlargePercent(REAL(1)/images.size()).GetSize().norm())); + window.camera.maxCamID = images.size(); window.SetName(String::FormatString((name + _T(": %s")).c_str(), Util::getFileName(fileName).c_str())); - window.Reset(MINF(2u, images.size())); + window.clbkExportScene = DELEGATEBINDCLASS(Window::ClbkExportScene, &Scene::Export, this); + window.clbkCompilePointCloud = DELEGATEBINDCLASS(Window::ClbkCompilePointCloud, &Scene::CompilePointCloud, this); + window.clbkCompileMesh = DELEGATEBINDCLASS(Window::ClbkCompileMesh, &Scene::CompileMesh, this); + if (!bounds.IsEmpty()) + window.clbkRayScene = DELEGATEBINDCLASS(Window::ClbkRayScene, &Scene::CastRay, this); + window.Reset(!scene.pointcloud.IsEmpty()&&!scene.mesh.IsEmpty()?Window::SPR_NONE:Window::SPR_ALL, + MINF(2u,images.size())); return true; } @@ -468,8 +484,6 @@ void Scene::Draw() glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPointSize(window.pointSize); - window.UpdateView(images, scene.images); - // render point-cloud if (listPointCloud) { glDisable(GL_TEXTURE_2D); @@ -493,6 +507,7 @@ void Scene::Draw() // render cameras if (window.bRenderCameras) { glDisable(GL_CULL_FACE); + const Point3* ptrPrevC(NULL); FOREACH(idx, images) { Image& image = images[idx]; const MVS::Image& imageData = scene.images[image.idx]; @@ -503,7 +518,7 @@ void Scene::Draw() glPointSize(window.pointSize+1.f); glDisable(GL_TEXTURE_2D); // draw camera position and image center - const double scaleFocal(window.camera->scaleF); + const double scaleFocal(window.camera.scaleF); glBegin(GL_POINTS); glColor3f(1,0,0); glVertex3f(0,0,0); // camera position glColor3f(0,1,0); glVertex3f(0,0,(float)scaleFocal); // image center @@ -520,7 +535,7 @@ void Scene::Draw() const Point3d ic3(px, py, scaleFocal); const Point3d ic4(px, cy, scaleFocal); // draw image thumbnail - const bool bSelectedImage(idx == window.camera->currentCamID); + const bool bSelectedImage(idx == window.camera.currentCamID); if (bSelectedImage) { if (image.IsValid()) { // render image @@ -565,6 +580,36 @@ void Scene::Draw() glEnd(); // restore coordinate system glPopMatrix(); + // render image visibility info + if (window.bRenderImageVisibility && idx != NO_ID && idx==window.camera.currentCamID) { + if (scene.pointcloud.IsValid()) { + const Image& image = images[idx]; + glPointSize(window.pointSize*1.1f); + glDisable(GL_DEPTH_TEST); + glBegin(GL_POINTS); + glColor3f(1.f,0.f,0.f); + FOREACH(i, scene.pointcloud.points) { + ASSERT(!scene.pointcloud.pointViews[i].empty()); + if (scene.pointcloud.pointViews[i].size() < window.minViews) + continue; + if (scene.pointcloud.pointViews[i].FindFirst(image.idx) == MVS::PointCloud::ViewArr::NO_INDEX) + continue; + glVertex3fv(scene.pointcloud.points[i].ptr()); + } + glEnd(); + glEnable(GL_DEPTH_TEST); + glPointSize(window.pointSize); + } + } + // render camera trajectory + if (window.bRenderCameraTrajectory && ptrPrevC) { + glBegin(GL_LINES); + glColor3f(1.f,0.5f,0.f); + glVertex3dv(ptrPrevC->ptr()); + glVertex3dv(camera.C.ptr()); + glEnd(); + } + ptrPrevC = &camera.C; } } if (window.selectionType != Window::SEL_NA) { @@ -577,25 +622,31 @@ void Scene::Draw() glColor3f(0,0,1); glVertex3fv(window.selectionPoints[2].ptr()); } glEnd(); + if (window.bRenderViews && window.selectionType == Window::SEL_POINT) { + if (!scene.pointcloud.pointViews.empty()) { + glBegin(GL_LINES); + const MVS::PointCloud::ViewArr& views = scene.pointcloud.pointViews[(MVS::PointCloud::Index)window.selectionIdx]; + ASSERT(!views.empty()); + for (MVS::PointCloud::View idxImage: views) { + const MVS::Image& imageData = scene.images[idxImage]; + glVertex3dv(imageData.camera.C.ptr()); + glVertex3fv(window.selectionPoints[0].ptr()); + } + glEnd(); + } + } glEnable(GL_DEPTH_TEST); glPointSize(window.pointSize); } glfwSwapBuffers(window.GetWindow()); } -void Scene::ProcessEvents() -{ - glfwWaitEvents(); - window.UpdateMousePosition(); - if (glfwGetMouseButton(window.GetWindow(), GLFW_MOUSE_BUTTON_1) != GLFW_RELEASE) - window.camera->Rotate(window.pos, window.prevPos); -} - void Scene::Loop() { while (!glfwWindowShouldClose(window.GetWindow())) { - ProcessEvents(); + window.UpdateView(images, scene.images); Draw(); + glfwWaitEvents(); } } @@ -620,8 +671,7 @@ void Scene::CastRay(const Ray3& ray, int action) if (window.selectionType != Window::SEL_NA && now-window.selectionTime < timeDblClick) { // this is a double click, center scene at the selected point - window.camera->center = Point3d(window.selectionPoints[3]); - window.camera->dist *= 0.7; + window.CenterCamera(window.selectionPoints[3]); window.selectionTime = now; } else if (!octMesh.IsEmpty()) { @@ -635,6 +685,7 @@ void Scene::CastRay(const Ray3& ray, int action) window.selectionPoints[3] = (ray.m_pOrig + ray.m_vDir*intRay.pick.dist).cast(); window.selectionType = Window::SEL_TRIANGLE; window.selectionTime = now; + window.selectionIdx = intRay.pick.idx; DEBUG("Face selected:\n\tindex: %u\n\tvertex 1: %u (%g %g %g)\n\tvertex 2: %u (%g %g %g)\n\tvertex 3: %u (%g %g %g)", intRay.pick.idx, face[0], window.selectionPoints[0].x, window.selectionPoints[0].y, window.selectionPoints[0].z, @@ -652,6 +703,7 @@ void Scene::CastRay(const Ray3& ray, int action) window.selectionPoints[0] = window.selectionPoints[3] = scene.pointcloud.points[intRay.pick.idx]; window.selectionType = Window::SEL_POINT; window.selectionTime = now; + window.selectionIdx = intRay.pick.idx; DEBUG("Point selected:\n\tindex: %u (%g %g %g)%s", intRay.pick.idx, window.selectionPoints[0].x, window.selectionPoints[0].y, window.selectionPoints[0].z, @@ -670,7 +722,7 @@ void Scene::CastRay(const Ray3& ray, int action) }().c_str() ); } else { - window.selectionType = Window::SEL_POINT; + window.selectionType = Window::SEL_NA; } } break; } diff --git a/apps/Viewer/Scene.h b/apps/Viewer/Scene.h index 63aca7d5e..5a02d5d2d 100644 --- a/apps/Viewer/Scene.h +++ b/apps/Viewer/Scene.h @@ -82,14 +82,13 @@ class Scene inline bool IsOpen() const { return IsValid() && !scene.IsEmpty(); } inline bool IsOctreeValid() const { return !octPoints.IsEmpty() || !octMesh.IsEmpty(); } - bool Init(int width, int height, LPCTSTR windowName, LPCTSTR fileName=NULL, LPCTSTR meshFileName=NULL); + bool Init(const cv::Size&, LPCTSTR windowName, LPCTSTR fileName=NULL, LPCTSTR meshFileName=NULL); bool Open(LPCTSTR fileName, LPCTSTR meshFileName=NULL); bool Export(LPCTSTR fileName, LPCTSTR exportType=NULL, bool losslessTexture=false) const; void CompilePointCloud(); void CompileMesh(); void Draw(); - void ProcessEvents(); void Loop(); void CastRay(const Ray3&, int); diff --git a/apps/Viewer/Viewer.cpp b/apps/Viewer/Viewer.cpp index e0ff5b56d..d8c952ead 100644 --- a/apps/Viewer/Viewer.cpp +++ b/apps/Viewer/Viewer.cpp @@ -155,9 +155,12 @@ bool Initialize(size_t argc, LPCTSTR* argv) "\tE: export scene\n" "\tR: reset scene\n" "\tC: render cameras\n" + "\tC + Shift: render camera trajectory\n" "\tLeft/Right: select next camera to view the scene\n" - "\tW: render wire-frame mesh\n" "\tT: render mesh texture\n" + "\tW: render wire-frame mesh\n" + "\tV: render view rays to the selected point\n" + "\tV + Shift: render points seen by the current view\n" "\tUp/Down: adjust point size\n" "\tUp/Down + Shift: adjust minimum number of views accepted when displaying a point or line\n" "\t+/-: adjust camera thumbnail transparency\n" @@ -216,7 +219,7 @@ int main(int argc, LPCTSTR* argv) // create viewer Scene viewer; - if (!viewer.Init(1280, 720, APPNAME, + if (!viewer.Init(cv::Size(1280, 720), APPNAME, OPT::strInputFileName.IsEmpty() ? NULL : MAKE_PATH_SAFE(OPT::strInputFileName).c_str(), OPT::strMeshFileName.IsEmpty() ? NULL : MAKE_PATH_SAFE(OPT::strMeshFileName).c_str())) return EXIT_FAILURE; diff --git a/apps/Viewer/Window.cpp b/apps/Viewer/Window.cpp index 4cb3eeef4..cc243ef7d 100644 --- a/apps/Viewer/Window.cpp +++ b/apps/Viewer/Window.cpp @@ -57,39 +57,52 @@ Window::~Window() void Window::Release() { if (IsValid()) { + #ifdef _USE_NUKLEAR + nk_glfw3_shutdown(); + #endif glfwDestroyWindow(window); window = NULL; } clbkOpenScene.reset(); + ReleaseClbk(); +} + +void Window::ReleaseClbk() +{ clbkExportScene.reset(); clbkRayScene.reset(); clbkCompilePointCloud.reset(); clbkCompileMesh.reset(); } -bool Window::Init(int width, int height, LPCTSTR name) +bool Window::Init(const cv::Size& _size, LPCTSTR name) { + sizeScale = 1; + size = _size; + glfwDefaultWindowHints(); glfwWindowHint(GLFW_VISIBLE, 0); - window = glfwCreateWindow(width, height, name, NULL, NULL); + window = glfwCreateWindow(size.width, size.height, name, NULL, NULL); if (!window) return false; glfwMakeContextCurrent(window); glfwSetFramebufferSizeCallback(window, Window::Resize); glfwSetKeyCallback(window, Window::Key); glfwSetMouseButtonCallback(window, Window::MouseButton); + glfwSetCursorPosCallback(window, Window::MouseMove); glfwSetScrollCallback(window, Window::Scroll); glfwSetDropCallback(window, Window::Drop); g_mapWindows[window] = this; + Reset(); return true; } -void Window::SetCamera(CameraPtr cam) +void Window::SetCamera(const Camera& cam) { camera = cam; - int width, height; - glfwGetWindowSize(window, &width, &height); - Resize(width, height); + cv::Size _size; + glfwGetFramebufferSize(window, &_size.width, &_size.height); + Resize(_size); } void Window::SetName(LPCTSTR name) { @@ -102,18 +115,26 @@ void Window::SetVisible(bool v) else glfwHideWindow(window); } -void Window::Reset(uint32_t _minViews) +bool Window::IsVisible() const { - if (camera) - camera->Reset(); - sparseType = SPR_ALL; + return glfwGetWindowAttrib(window, GLFW_VISIBLE) != 0; +} +void Window::Reset(SPARSE _sparseType, unsigned _minViews) +{ + camera.Reset(); + inputType = INP_NA; + sparseType = _sparseType; minViews = _minViews; pointSize = 2.f; cameraBlend = 0.5f; bRenderCameras = true; + bRenderCameraTrajectory = true; + bRenderImageVisibility = false; + bRenderViews = true; bRenderSolid = true; bRenderTexture = true; selectionType = SEL_NA; + selectionIdx = NO_IDX; if (clbkCompilePointCloud != NULL) clbkCompilePointCloud(); if (clbkCompileMesh != NULL) @@ -121,46 +142,76 @@ void Window::Reset(uint32_t _minViews) glfwPostEmptyEvent(); } -void Window::UpdateView(const ImageArr& images, const MVS::ImageArr& sceneImages) + +void Window::CenterCamera(const Point3& pos) { - glMatrixMode(GL_MODELVIEW); - if (camera->prevCamID != camera->currentCamID && camera->currentCamID != NO_ID) { - // enable camera view mode - // apply current camera transform - const Image& image = images[camera->currentCamID]; - const MVS::Image& imageData = sceneImages[image.idx]; - const MVS::Camera& camera = imageData.camera; - const Eigen::Matrix4d trans(TransW2L((const Matrix3x3::EMat)camera.R, camera.GetT())); - glLoadMatrixf((GLfloat*)gs_convert); - glMultMatrixd((GLdouble*)trans.data()); + camera.center = pos; + camera.dist *= 0.7; +} + + +void Window::UpdateView(const ImageArr& images, const MVS::ImageArr& sceneImagesMVS) +{ + if (camera.IsCameraViewMode()) { + // enable camera view mode and apply current camera transform + const Image& image = images[camera.currentCamID]; + const MVS::Camera& camera = sceneImagesMVS[image.idx].camera; + UpdateView((const Matrix3x3::EMat)camera.R, camera.GetT()); } else { // apply view point transform - const Eigen::Matrix4d trans(camera->GetLookAt()); + glMatrixMode(GL_MODELVIEW); + const Eigen::Matrix4d trans(camera.GetLookAt()); glLoadMatrixd((GLdouble*)trans.data()); } } -void Window::UpdateMousePosition() +void Window::UpdateView(const Eigen::Matrix3d& R, const Eigen::Vector3d& t) +{ + glMatrixMode(GL_MODELVIEW); + transform = gs_convert * TransW2L(R, t); + glLoadMatrixd((GLdouble*)transform.data()); +} + +void Window::UpdateMousePosition(double xpos, double ypos) { prevPos = pos; - // get current position - glfwGetCursorPos(window, &pos.x(), &pos.y()); + pos.x() = xpos; + pos.y() = ypos; // normalize position to [-1:1] range - const int w(camera->width); - const int h(camera->height); + const int w(camera.size.width); + const int h(camera.size.height); pos.x() = (2.0 * pos.x() - w) / w; pos.y() = (h - 2.0 * pos.y()) / h; } -void Window::Resize(int width, int height) + +void Window::GetFrame(Image8U3& image) const +{ + image.create(GetSize()); + glReadPixels(0, 0, image.width(), image.height(), GL_BGR_EXT, GL_UNSIGNED_BYTE, image.ptr()); + cv::flip(image, image, 0); +} + + +cv::Size Window::GetSize() const +{ + cv::Size _size; + glfwGetWindowSize(window, &_size.width, &_size.height); + return _size; +} +void Window::Resize(const cv::Size& _size) { + // detect scaled window + sizeScale = (double)GetSize().width/_size.width; + size = _size; + // update resolution glfwMakeContextCurrent(window); - glViewport(0, 0, (GLint)width, (GLint)height); - camera->Resize(width, height); + glViewport(0, 0, size.width, size.height); + camera.Resize(cv::Size(ROUND2INT(size.width*sizeScale), ROUND2INT(size.height*sizeScale))); } void Window::Resize(GLFWwindow* window, int width, int height) { - g_mapWindows[window]->Resize(width, height); + g_mapWindows[window]->Resize(cv::Size(width, height)); } void Window::Key(int k, int /*scancode*/, int action, int mod) @@ -196,31 +247,61 @@ void Window::Key(int k, int /*scancode*/, int action, int mod) break; case GLFW_KEY_LEFT: if (action != GLFW_RELEASE) { - camera->prevCamID = camera->currentCamID; - camera->currentCamID--; - if (camera->currentCamID < NO_ID && camera->currentCamID >= camera->maxCamID) - camera->currentCamID = camera->maxCamID-1; + camera.prevCamID = camera.currentCamID; + camera.currentCamID--; + if (camera.currentCamID < NO_ID && camera.currentCamID >= camera.maxCamID) + camera.currentCamID = camera.maxCamID-1; } break; case GLFW_KEY_RIGHT: if (action != GLFW_RELEASE) { - camera->prevCamID = camera->currentCamID; - camera->currentCamID++; - if (camera->currentCamID >= camera->maxCamID) - camera->currentCamID = NO_ID; + camera.prevCamID = camera.currentCamID; + camera.currentCamID++; + if (camera.currentCamID >= camera.maxCamID) + camera.currentCamID = NO_ID; + } + break; + case GLFW_KEY_C: + if (action == GLFW_RELEASE) { + if (mod & GLFW_MOD_SHIFT) { + bRenderCameraTrajectory = !bRenderCameraTrajectory; + } else { + bRenderCameras = !bRenderCameras; + } } break; case GLFW_KEY_E: if (action == GLFW_RELEASE && clbkExportScene != NULL) clbkExportScene(NULL, NULL, false); break; + case GLFW_KEY_P: + switch (sparseType) { + case SPR_POINTS: sparseType = SPR_LINES; break; + case SPR_LINES: sparseType = SPR_ALL; break; + case SPR_ALL: sparseType = SPR_POINTS; break; + } + if (clbkCompilePointCloud != NULL) + clbkCompilePointCloud(); + break; case GLFW_KEY_R: if (action == GLFW_RELEASE) Reset(); break; - case GLFW_KEY_C: - if (action == GLFW_RELEASE) - bRenderCameras = !bRenderCameras; + case GLFW_KEY_T: + if (action == GLFW_RELEASE) { + bRenderTexture = !bRenderTexture; + if (clbkCompileMesh != NULL) + clbkCompileMesh(); + } + break; + case GLFW_KEY_V: + if (action == GLFW_RELEASE) { + if (mod & GLFW_MOD_SHIFT) { + bRenderImageVisibility = !bRenderImageVisibility; + } else { + bRenderViews = !bRenderViews; + } + } break; case GLFW_KEY_W: if (action == GLFW_RELEASE) { @@ -233,28 +314,12 @@ void Window::Key(int k, int /*scancode*/, int action, int mod) } } break; - case GLFW_KEY_T: - if (action == GLFW_RELEASE) { - bRenderTexture = !bRenderTexture; - if (clbkCompileMesh != NULL) - clbkCompileMesh(); - } - break; - case GLFW_KEY_P: - switch (sparseType) { - case SPR_POINTS: sparseType = SPR_LINES; break; - case SPR_LINES: sparseType = SPR_ALL; break; - case SPR_ALL: sparseType = SPR_POINTS; break; - } - if (clbkCompilePointCloud != NULL) - clbkCompilePointCloud(); - break; case GLFW_KEY_KP_SUBTRACT: if (action == GLFW_RELEASE) { if (mod & GLFW_MOD_CONTROL) - camera->SetFOV(MAXF(camera->fov-5, 5.0)); + camera.SetFOV(camera.fov-5.f); else if (mod & GLFW_MOD_SHIFT) - camera->scaleF *= 0.9f; + camera.scaleF *= 0.9f; else cameraBlend = MAXF(cameraBlend-0.1f, 0.f); } @@ -262,9 +327,9 @@ void Window::Key(int k, int /*scancode*/, int action, int mod) case GLFW_KEY_KP_ADD: if (action == GLFW_RELEASE) { if (mod & GLFW_MOD_CONTROL) - camera->SetFOV(camera->fov+5); + camera.SetFOV(camera.fov+5.f); else if (mod & GLFW_MOD_SHIFT) - camera->scaleF *= 1.11f; + camera.scaleF *= 1.1111f; else cameraBlend = MINF(cameraBlend+0.1f, 1.f); } @@ -278,24 +343,53 @@ void Window::Key(GLFWwindow* window, int k, int scancode, int action, int mod) void Window::MouseButton(int button, int action, int /*mods*/) { - if (clbkRayScene != NULL && button == GLFW_MOUSE_BUTTON_LEFT) { - typedef Eigen::Matrix Mat4; - Mat4 P, V; - glGetDoublev(GL_MODELVIEW_MATRIX, V.data()); - glGetDoublev(GL_PROJECTION_MATRIX, P.data()); - // 4d Homogeneous Clip Coordinates - const Eigen::Vector4d ray_clip(pos.x(), pos.y(), -1.0, 1.0); - // 4d Eye (Camera) Coordinates - Eigen::Vector4d ray_eye(P.inverse()*ray_clip); - ray_eye.z() = -1.0; - ray_eye.w() = 0.0; - // 4d World Coordinates - const Mat4 invV(V.inverse()); - ASSERT(ISEQUAL(invV(3,3),1.0)); - const Eigen::Vector3d start(invV.topRightCorner<3,1>()); - const Eigen::Vector4d ray_wor(invV*ray_eye); - const Eigen::Vector3d dir(ray_wor.topRows<3>().normalized()); - clbkRayScene(Ray3d(start, dir), action); + switch (button) { + case GLFW_MOUSE_BUTTON_LEFT: { + if (action == GLFW_PRESS) { + inputType.set(INP_MOUSE_LEFT); + } else + if (action == GLFW_RELEASE) { + inputType.unset(INP_MOUSE_LEFT); + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + } + if (clbkRayScene != NULL) { + typedef Eigen::Matrix Mat4; + Mat4 P, V; + glGetDoublev(GL_MODELVIEW_MATRIX, V.data()); + glGetDoublev(GL_PROJECTION_MATRIX, P.data()); + // 4d Homogeneous Clip Coordinates + const Eigen::Vector4d ray_clip(pos.x(), pos.y(), -1.0, 1.0); + // 4d Eye (Camera) Coordinates + Eigen::Vector4d ray_eye(P.inverse()*ray_clip); + ray_eye.z() = -1.0; + ray_eye.w() = 0.0; + // 4d World Coordinates + const Mat4 invV(V.inverse()); + ASSERT(ISEQUAL(invV(3,3),1.0)); + const Eigen::Vector3d start(invV.topRightCorner<3,1>()); + const Eigen::Vector4d ray_wor(invV*ray_eye); + const Eigen::Vector3d dir(ray_wor.topRows<3>().normalized()); + clbkRayScene(Ray3d(start, dir), action); + } + } break; + case GLFW_MOUSE_BUTTON_MIDDLE: { + if (action == GLFW_PRESS) { + inputType.set(INP_MOUSE_MIDDLE); + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + } else + if (action == GLFW_RELEASE) { + inputType.unset(INP_MOUSE_MIDDLE); + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + } + } break; + case GLFW_MOUSE_BUTTON_RIGHT: { + if (action == GLFW_PRESS) { + inputType.set(INP_MOUSE_RIGHT); + } else + if (action == GLFW_RELEASE) { + inputType.unset(INP_MOUSE_RIGHT); + } + } } } void Window::MouseButton(GLFWwindow* window, int button, int action, int mods) @@ -303,9 +397,25 @@ void Window::MouseButton(GLFWwindow* window, int button, int action, int mods) g_mapWindows[window]->MouseButton(button, action, mods); } +void Window::MouseMove(double xpos, double ypos) +{ + UpdateMousePosition(xpos, ypos); + if (inputType.isSet(INP_MOUSE_LEFT)) { + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + camera.Rotate(pos, prevPos); + } else + if (inputType.isSet(INP_MOUSE_MIDDLE)) { + camera.Translate(pos, prevPos); + } +} +void Window::MouseMove(GLFWwindow* window, double xpos, double ypos) +{ + g_mapWindows[window]->MouseMove(xpos, ypos); +} + void Window::Scroll(double /*xoffset*/, double yoffset) { - camera->dist *= (yoffset>0 ? POW(1.11,yoffset) : POW(0.9,-yoffset)); + camera.dist *= (yoffset>0 ? POW(1.11,yoffset) : POW(0.9,-yoffset)); } void Window::Scroll(GLFWwindow* window, double xoffset, double yoffset) { diff --git a/apps/Viewer/Window.h b/apps/Viewer/Window.h index 0d9fe5034..cb892bdc2 100644 --- a/apps/Viewer/Window.h +++ b/apps/Viewer/Window.h @@ -50,10 +50,22 @@ class Window { public: GLFWwindow* window; // window handle - CameraPtr camera; // current camera (always valid) + Camera camera; // current camera Eigen::Vector2d pos, prevPos; // current and previous mouse position (normalized) + Eigen::Matrix4d transform; // view matrix corresponding to the currently selected image + cv::Size size; // resolution in pixels, sometimes not equal to window resolution, ex. on Retina display + double sizeScale; // window/screen resolution scale + + enum INPUT : unsigned { + INP_NA = 0, + INP_MOUSE_LEFT = (1 << 0), + INP_MOUSE_MIDDLE = (1 << 1), + INP_MOUSE_RIGHT = (1 << 2), + }; + Flags inputType; enum SPARSE { + SPR_NONE = 0, SPR_POINTS = (1 << 0), SPR_LINES = (1 << 1), SPR_ALL = SPR_POINTS|SPR_LINES @@ -63,6 +75,9 @@ class Window float pointSize; float cameraBlend; bool bRenderCameras; + bool bRenderCameraTrajectory; + bool bRenderImageVisibility; + bool bRenderViews; bool bRenderSolid; bool bRenderTexture; @@ -74,6 +89,7 @@ class Window SELECTION selectionType; Point3f selectionPoints[4]; double selectionTimeClick, selectionTime; + IDX selectionIdx; typedef DELEGATE ClbkOpenScene; ClbkOpenScene clbkOpenScene; @@ -94,25 +110,35 @@ class Window ~Window(); void Release(); + void ReleaseClbk(); inline bool IsValid() const { return window != NULL; } - bool Init(int width, int height, LPCTSTR name); - void SetCamera(CameraPtr); + inline GLFWwindow* GetWindow() { return window; } + + bool Init(const cv::Size&, LPCTSTR name); + void SetCamera(const Camera&); void SetName(LPCTSTR); void SetVisible(bool); - void Reset(uint32_t minViews=2); + bool IsVisible() const; + void Reset(SPARSE sparseType=SPR_ALL, unsigned minViews=2); - inline GLFWwindow* GetWindow() { return window; } + void CenterCamera(const Point3&); void UpdateView(const ImageArr&, const MVS::ImageArr&); - void UpdateMousePosition(); + void UpdateView(const Eigen::Matrix3d& R, const Eigen::Vector3d& t); + void UpdateMousePosition(double xpos, double ypos); + + void GetFrame(Image8U3&) const; - void Resize(int width, int height); + cv::Size GetSize() const; + void Resize(const cv::Size&); static void Resize(GLFWwindow* window, int width, int height); void Key(int k, int scancode, int action, int mod); static void Key(GLFWwindow* window, int k, int scancode, int action, int mod); void MouseButton(int button, int action, int mods); static void MouseButton(GLFWwindow* window, int button, int action, int mods); + void MouseMove(double xpos, double ypos); + static void MouseMove(GLFWwindow* window, double xpos, double ypos); void Scroll(double xoffset, double yoffset); static void Scroll(GLFWwindow* window, double xoffset, double yoffset); void Drop(int count, const char** paths); diff --git a/libs/Common/AABB.h b/libs/Common/AABB.h index be2546526..61904896b 100644 --- a/libs/Common/AABB.h +++ b/libs/Common/AABB.h @@ -56,8 +56,8 @@ class TAABB inline bool IsEmpty() const; - inline void Enlarge(TYPE); - inline void EnlargePercent(TYPE); + inline TAABB& Enlarge(TYPE); + inline TAABB& EnlargePercent(TYPE); void InsertFull(const POINT&); void Insert(const POINT&); diff --git a/libs/Common/AABB.inl b/libs/Common/AABB.inl index b9040cfea..d10543e38 100644 --- a/libs/Common/AABB.inl +++ b/libs/Common/AABB.inl @@ -99,16 +99,18 @@ inline bool TAABB::IsEmpty() const template -inline void TAABB::Enlarge(TYPE x) +inline TAABB& TAABB::Enlarge(TYPE x) { ptMin.array() -= x; ptMax.array() += x; + return *this; } template -inline void TAABB::EnlargePercent(TYPE x) +inline TAABB& TAABB::EnlargePercent(TYPE x) { ptMin *= x; ptMax *= x; + return *this; } // Enlarge /*----------------------------------------------------------------*/ diff --git a/libs/MVS/Mesh.cpp b/libs/MVS/Mesh.cpp index 2adb1be55..39886bd4c 100644 --- a/libs/MVS/Mesh.cpp +++ b/libs/MVS/Mesh.cpp @@ -114,6 +114,24 @@ Mesh::Box Mesh::GetAABB(const Box& bound) const box.InsertFull(X); return box; } + +// compute the center of the point-cloud as the median +Mesh::Vertex Mesh::GetCenter() const +{ + const VIndex step(5); + const VIndex numPoints(vertices.size()/step); + if (numPoints == 0) + return Vertex::INF; + typedef CLISTDEF0IDX(Vertex::Type,VIndex) Scalars; + Scalars x(numPoints), y(numPoints), z(numPoints); + for (VIndex i=0; i Date: Wed, 7 Apr 2021 17:29:45 +0300 Subject: [PATCH 033/252] dense: improve depth-map initialization --- libs/MVS/DepthMap.cpp | 225 +++++++++++++++++++------------------- libs/MVS/Mesh.cpp | 7 +- libs/MVS/Mesh.h | 49 ++++++--- libs/MVS/SceneTexture.cpp | 18 +-- 4 files changed, 152 insertions(+), 147 deletions(-) diff --git a/libs/MVS/DepthMap.cpp b/libs/MVS/DepthMap.cpp index b1266d361..4d822e058 100644 --- a/libs/MVS/DepthMap.cpp +++ b/libs/MVS/DepthMap.cpp @@ -31,13 +31,14 @@ #include "Common.h" #include "DepthMap.h" +#include "Mesh.h" #define _USE_OPENCV #include "Interface.h" #include "../Common/AutoEstimator.h" // CGAL: depth-map initialization #include #include -#include +#include // CGAL: estimate normals #include #include @@ -958,96 +959,102 @@ DepthEstimator::PixelEstimate DepthEstimator::PerturbEstimate(const PixelEstimat // S T R U C T S /////////////////////////////////////////////////// namespace CGAL { -typedef CGAL::Simple_cartesian kernel_t; -typedef CGAL::Projection_traits_xy_3 Geometry; -typedef CGAL::Delaunay_triangulation_2 Delaunay; -typedef CGAL::Delaunay::Face_circulator FaceCirculator; -typedef CGAL::Delaunay::Face_handle FaceHandle; -typedef CGAL::Delaunay::Vertex_circulator VertexCirculator; -typedef CGAL::Delaunay::Vertex_handle VertexHandle; -typedef kernel_t::Point_3 Point; } // triangulate in-view points, generating a 2D mesh // return also the estimated depth boundaries (min and max depth) -std::pair TriangulatePointsDelaunay(const DepthData::ViewData& image, const PointCloud& pointcloud, const IndexArr& points, CGAL::Delaunay& delaunay) +std::pair TriangulatePointsDelaunay(const DepthData::ViewData& image, const PointCloud& pointcloud, const IndexArr& points, Mesh& mesh, Point2fArr& projs) { + typedef CGAL::Simple_cartesian kernel_t; + typedef CGAL::Triangulation_vertex_base_with_info_2 vertex_base_t; + typedef CGAL::Triangulation_data_structure_2 triangulation_data_structure_t; + typedef CGAL::Delaunay_triangulation_2 Delaunay; + typedef Delaunay::Face_circulator FaceCirculator; + typedef Delaunay::Face_handle FaceHandle; + typedef Delaunay::Vertex_circulator VertexCirculator; + typedef Delaunay::Vertex_handle VertexHandle; + typedef kernel_t::Point_2 CPoint; + ASSERT(sizeof(Point3) == sizeof(X3D)); - ASSERT(sizeof(Point3) == sizeof(CGAL::Point)); + ASSERT(sizeof(Point2) == sizeof(CPoint)); std::pair depthBounds(FLT_MAX, 0.f); + mesh.vertices.reserve(points.size()+4); + projs.reserve(mesh.vertices.capacity()); + Delaunay delaunay; for (uint32_t idx: points) { const Point3f pt(image.camera.ProjectPointP3(pointcloud.points[idx])); - delaunay.insert(CGAL::Point(pt.x/pt.z, pt.y/pt.z, pt.z)); + const Point3f x(pt.x/pt.z, pt.y/pt.z, pt.z); + delaunay.insert(CPoint(x.x, x.y))->info() = mesh.vertices.size(); + mesh.vertices.emplace_back(image.camera.TransformPointI2C(x)); + projs.emplace_back(x.x, x.y); if (depthBounds.first > pt.z) depthBounds.first = pt.z; if (depthBounds.second < pt.z) depthBounds.second = pt.z; } // if full size depth-map requested - if (OPTDENSE::bAddCorners) { - typedef TIndexScore DepthDist; - typedef CLISTDEF0(DepthDist) DepthDistArr; - typedef Eigen::Map< Eigen::VectorXf, Eigen::Unaligned, Eigen::InnerStride<2> > FloatMap; + const size_t numPoints(3); + if (OPTDENSE::bAddCorners && points.size() >= numPoints) { // add the four image corners at the average depth ASSERT(image.pImageData->IsValid() && ISINSIDE(image.pImageData->avgDepth, depthBounds.first, depthBounds.second)); - const CGAL::VertexHandle vcorners[] = { - delaunay.insert(CGAL::Point(0, 0, image.pImageData->avgDepth)), - delaunay.insert(CGAL::Point(image.image.width(), 0, image.pImageData->avgDepth)), - delaunay.insert(CGAL::Point(0, image.image.height(), image.pImageData->avgDepth)), - delaunay.insert(CGAL::Point(image.image.width(), image.image.height(), image.pImageData->avgDepth)) - }; + const Mesh::VIndex idxFirstVertex = mesh.vertices.size(); + VertexHandle vcorners[4]; + for (const Point2f& x: {Point2f(0, 0), Point2f(image.image.width()-1, 0), Point2f(0, image.image.height()-1), Point2f(image.image.width()-1, image.image.height()-1)}) { + const Mesh::VIndex i(mesh.vertices.size() - idxFirstVertex); + (vcorners[i] = delaunay.insert(CPoint(x.x, x.y)))->info() = mesh.vertices.size(); + mesh.vertices.emplace_back(image.camera.TransformPointI2C(Point3f(x, image.pImageData->avgDepth))); + projs.emplace_back(x); + } // compute average depth from the closest 3 directly connected faces, // weighted by the distance - const size_t numPoints = 3; for (int i=0; i<4; ++i) { - const CGAL::VertexHandle vcorner = vcorners[i]; - CGAL::FaceCirculator cfc(delaunay.incident_faces(vcorner)); - if (cfc == 0) - continue; // normally this should never happen - const CGAL::FaceCirculator done(cfc); - Point3d& poszA = reinterpret_cast(vcorner->point()); - const Point2d& posA = reinterpret_cast(poszA); - const Ray3d rayA(Point3d::ZERO, normalized(image.camera.TransformPointI2C(poszA))); - DepthDistArr depths(0, numPoints); + const VertexHandle vcorner(vcorners[i]); + FaceCirculator cfc(delaunay.incident_faces(vcorner)); + ASSERT(cfc != 0); + const FaceCirculator done(cfc); + const Point2d& posA = reinterpret_cast(vcorner->point()); + const Ray3d rayA(Point3d::ZERO, normalized(Cast(mesh.vertices[vcorner->info()]))); + typedef TIndexScore DepthDist; + CLISTDEF0(DepthDist) depths(0, numPoints); do { - CGAL::FaceHandle fc(cfc->neighbor(cfc->index(vcorner))); - if (fc == delaunay.infinite_face()) + const FaceHandle fc(cfc->neighbor(cfc->index(vcorner))); + if (delaunay.is_infinite(fc)) continue; for (int j=0; j<4; ++j) if (fc->has_vertex(vcorners[j])) - goto Continue; + continue; // compute the depth as the intersection of the corner ray with // the plane defined by the face's vertices - { - const Point3d& poszB0 = reinterpret_cast(fc->vertex(0)->point()); - const Point3d& poszB1 = reinterpret_cast(fc->vertex(1)->point()); - const Point3d& poszB2 = reinterpret_cast(fc->vertex(2)->point()); const Planed planeB( - image.camera.TransformPointI2C(poszB0), - image.camera.TransformPointI2C(poszB1), - image.camera.TransformPointI2C(poszB2) + Cast(mesh.vertices[fc->vertex(0)->info()]), + Cast(mesh.vertices[fc->vertex(1)->info()]), + Cast(mesh.vertices[fc->vertex(2)->info()]) ); const Point3d poszB(rayA.Intersects(planeB)); if (poszB.z <= 0) continue; const Point2d posB(( - reinterpret_cast(poszB0)+ - reinterpret_cast(poszB1)+ - reinterpret_cast(poszB2))/3.f + reinterpret_cast(fc->vertex(0)->point())+ + reinterpret_cast(fc->vertex(1)->point())+ + reinterpret_cast(fc->vertex(2)->point()))/3.f ); const double dist(norm(posB-posA)); depths.StoreTop(DepthDist(CLAMP((float)poszB.z,depthBounds.first,depthBounds.second), INVERT((float)dist))); - } - Continue:; } while (++cfc != done); - if (depths.size() != numPoints) - continue; // normally this should never happen + ASSERT(depths.size() == numPoints); + typedef Eigen::Map< Eigen::VectorXf, Eigen::Unaligned, Eigen::InnerStride<2> > FloatMap; FloatMap vecDists(&depths[0].score, numPoints); vecDists *= 1.f/vecDists.sum(); FloatMap vecDepths(&depths[0].idx, numPoints); - poszA.z = vecDepths.dot(vecDists); + const float depth(vecDepths.dot(vecDists)); + mesh.vertices[idxFirstVertex+i] = image.camera.TransformPointI2C(Point3(posA, depth)); } } + mesh.faces.reserve(std::distance(delaunay.finite_faces_begin(),delaunay.finite_faces_end())); + for (Delaunay::Face_iterator it=delaunay.faces_begin(); it!=delaunay.faces_end(); ++it) { + const Delaunay::Face& face = *it; + mesh.faces.emplace_back(face.vertex(2)->info(), face.vertex(1)->info(), face.vertex(0)->info()); + } return depthBounds; } @@ -1060,54 +1067,54 @@ bool MVS::TriangulatePoints2DepthMap( ASSERT(image.pImageData != NULL); // triangulate in-view points - CGAL::Delaunay delaunay; - const std::pair thDepth(TriangulatePointsDelaunay(image, pointcloud, points, delaunay)); + Mesh mesh; + Point2fArr projs; + const std::pair thDepth(TriangulatePointsDelaunay(image, pointcloud, points, mesh, projs)); dMin = thDepth.first; dMax = thDepth.second; // create rough depth-map by interpolating inside triangles const Camera& camera = image.camera; + mesh.ComputeNormalVertices(); depthMap.create(image.image.size()); normalMap.create(image.image.size()); if (!OPTDENSE::bAddCorners) { depthMap.memset(0); normalMap.memset(0); } - struct RasterDepthDataPlaneData { - const Camera& P; - DepthMap& depthMap; + struct RasterDepth : TRasterMeshBase { + typedef TRasterMeshBase Base; + using Base::camera; + using Base::depthMap; + using Base::ptc; + using Base::pti; + const Mesh::NormalArr& vertexNormals; NormalMap& normalMap; - Point3f normal; - Point3f normalPlane; - inline void operator()(const ImageRef& pt) { - if (!depthMap.isInside(pt)) - return; - const Depth z(INVERT(normalPlane.dot(P.TransformPointI2C(Point2f(pt))))); - if (z <= 0) // due to numerical instability - return; + Mesh::Face face; + RasterDepth(const Mesh::NormalArr& _vertexNormals, const Camera& _camera, DepthMap& _depthMap, NormalMap& _normalMap) + : Base(_camera, _depthMap), vertexNormals(_vertexNormals), normalMap(_normalMap) {} + inline void operator()(const ImageRef& pt, const Point3f& bary) { + const Point3f pbary(PerspectiveCorrectBarycentricCoordinates(bary)); + const Depth z(ComputeDepth(pbary)); + ASSERT(z > Depth(0)); depthMap(pt) = z; - normalMap(pt) = normal; + normalMap(pt) = normalized( + vertexNormals[face[0]] * pbary[0]+ + vertexNormals[face[1]] * pbary[1]+ + vertexNormals[face[2]] * pbary[2] + ); } }; - RasterDepthDataPlaneData data = {camera, depthMap, normalMap}; - for (CGAL::Delaunay::Face_iterator it=delaunay.faces_begin(); it!=delaunay.faces_end(); ++it) { - const CGAL::Delaunay::Face& face = *it; - const Point3f i0(reinterpret_cast(face.vertex(0)->point())); - const Point3f i1(reinterpret_cast(face.vertex(1)->point())); - const Point3f i2(reinterpret_cast(face.vertex(2)->point())); - // compute the plane defined by the 3 points - const Point3f c0(camera.TransformPointI2C(i0)); - const Point3f c1(camera.TransformPointI2C(i1)); - const Point3f c2(camera.TransformPointI2C(i2)); - const Point3f edge1(c1-c0); - const Point3f edge2(c2-c0); - data.normal = normalized(edge2.cross(edge1)); - data.normalPlane = data.normal * INVERT(data.normal.dot(c0)); - // draw triangle and for each pixel compute depth as the ray intersection with the plane - Image8U::RasterizeTriangle( - reinterpret_cast(i2), - reinterpret_cast(i1), - reinterpret_cast(i0), data); + RasterDepth rasterer = {mesh.vertexNormals, camera, depthMap, normalMap}; + for (const Mesh::Face& face : mesh.faces) { + rasterer.face = face; + rasterer.ptc[0].z = mesh.vertices[face[0]].z; + rasterer.ptc[1].z = mesh.vertices[face[1]].z; + rasterer.ptc[2].z = mesh.vertices[face[2]].z; + Image8U::RasterizeTriangleBary( + projs[face[0]], + projs[face[1]], + projs[face[2]], rasterer); } return true; } // TriangulatePoints2DepthMap @@ -1119,8 +1126,9 @@ bool MVS::TriangulatePoints2DepthMap( ASSERT(image.pImageData != NULL); // triangulate in-view points - CGAL::Delaunay delaunay; - const std::pair thDepth(TriangulatePointsDelaunay(image, pointcloud, points, delaunay)); + Mesh mesh; + Point2fArr projs; + const std::pair thDepth(TriangulatePointsDelaunay(image, pointcloud, points, mesh, projs)); dMin = thDepth.first; dMax = thDepth.second; @@ -1129,38 +1137,27 @@ bool MVS::TriangulatePoints2DepthMap( depthMap.create(image.image.size()); if (!OPTDENSE::bAddCorners) depthMap.memset(0); - struct RasterDepthDataPlaneData { - const Camera& P; - DepthMap& depthMap; - Point3f normalPlane; - inline void operator()(const ImageRef& pt) { - if (!depthMap.isInside(pt)) - return; - const Depth z((Depth)INVERT(normalPlane.dot(P.TransformPointI2C(Point2f(pt))))); - if (z <= 0) // due to numerical instability - return; + struct RasterDepth : TRasterMeshBase { + typedef TRasterMeshBase Base; + using Base::depthMap; + RasterDepth(const Camera& _camera, DepthMap& _depthMap) + : Base(_camera, _depthMap) {} + inline void operator()(const ImageRef& pt, const Point3f& bary) { + const Point3f pbary(PerspectiveCorrectBarycentricCoordinates(bary)); + const Depth z(ComputeDepth(pbary)); + ASSERT(z > Depth(0)); depthMap(pt) = z; } }; - RasterDepthDataPlaneData data = {camera, depthMap}; - for (CGAL::Delaunay::Face_iterator it=delaunay.faces_begin(); it!=delaunay.faces_end(); ++it) { - const CGAL::Delaunay::Face& face = *it; - const Point3f i0(reinterpret_cast(face.vertex(0)->point())); - const Point3f i1(reinterpret_cast(face.vertex(1)->point())); - const Point3f i2(reinterpret_cast(face.vertex(2)->point())); - // compute the plane defined by the 3 points - const Point3f c0(camera.TransformPointI2C(i0)); - const Point3f c1(camera.TransformPointI2C(i1)); - const Point3f c2(camera.TransformPointI2C(i2)); - const Point3f edge1(c1-c0); - const Point3f edge2(c2-c0); - const Normal normal(normalized(edge2.cross(edge1))); - data.normalPlane = normal * INVERT(normal.dot(c0)); - // draw triangle and for each pixel compute depth as the ray intersection with the plane - Image8U::RasterizeTriangle( - reinterpret_cast(i2), - reinterpret_cast(i1), - reinterpret_cast(i0), data); + RasterDepth rasterer = {camera, depthMap}; + for (const Mesh::Face& face : mesh.faces) { + rasterer.ptc[0].z = mesh.vertices[face[0]].z; + rasterer.ptc[1].z = mesh.vertices[face[1]].z; + rasterer.ptc[2].z = mesh.vertices[face[2]].z; + Image8U::RasterizeTriangleBary( + projs[face[0]], + projs[face[1]], + projs[face[2]], rasterer); } return true; } // TriangulatePoints2DepthMap diff --git a/libs/MVS/Mesh.cpp b/libs/MVS/Mesh.cpp index 39886bd4c..7dad00500 100644 --- a/libs/MVS/Mesh.cpp +++ b/libs/MVS/Mesh.cpp @@ -229,8 +229,7 @@ void Mesh::ComputeNormalVertices() { vertexNormals.Resize(vertices.GetSize()); vertexNormals.Memset(0); - FOREACHPTR(pFace, faces) { - const Face& face = *pFace; + for (const Face& face: faces) { const Vertex& v0 = vertices[face[0]]; const Vertex& v1 = vertices[face[1]]; const Vertex& v2 = vertices[face[2]]; @@ -239,8 +238,8 @@ void Mesh::ComputeNormalVertices() vertexNormals[face[1]] += t; vertexNormals[face[2]] += t; } - FOREACHPTR(pVertexNormal, vertexNormals) - normalize(*pVertexNormal); + for (Normal& vertexNormal: vertexNormals) + normalize(vertexNormal); } #else // computes the vertex normal as an angle weighted average diff --git a/libs/MVS/Mesh.h b/libs/MVS/Mesh.h index 044b644bf..672a92810 100644 --- a/libs/MVS/Mesh.h +++ b/libs/MVS/Mesh.h @@ -246,10 +246,9 @@ class MVS_API Mesh /*----------------------------------------------------------------*/ -// used to render a mesh +// used to render a 3D triangle template -struct TRasterMesh { - const Mesh::VertexArr& vertices; +struct TRasterMeshBase { const Camera& camera; DepthMap& depthMap; @@ -257,8 +256,8 @@ struct TRasterMesh { Point3 ptc[3]; Point2f pti[3]; - TRasterMesh(const Mesh::VertexArr& _vertices, const Camera& _camera, DepthMap& _depthMap) - : vertices(_vertices), camera(_camera), depthMap(_depthMap) {} + TRasterMeshBase(const Camera& _camera, DepthMap& _depthMap) + : camera(_camera), depthMap(_depthMap) {} inline void Clear() { depthMap.memset(0); @@ -267,20 +266,10 @@ struct TRasterMesh { return depthMap.size(); } - inline bool ProjectVertex(const Mesh::Vertex& pt, int v) { + inline bool ProjectVertex(const Point3f& pt, int v) { return (ptc[v] = camera.TransformPointW2C(Cast(pt))).z > 0 && depthMap.isInsideWithBorder(pti[v] = camera.TransformPointC2I(ptc[v])); } - void Project(const Mesh::Face& facet) { - // project face vertices to image plane - for (int v=0; v<3; ++v) { - // skip face if not completely inside - if (!static_cast(this)->ProjectVertex(vertices[facet[v]], v)) - return; - } - // draw triangle - Image8U3::RasterizeTriangleBary(pti[0], pti[1], pti[2], *this); - } inline Point3f PerspectiveCorrectBarycentricCoordinates(const Point3f& bary) { return SEACAVE::PerspectiveCorrectBarycentricCoordinates(bary, (float)ptc[0].z, (float)ptc[1].z, (float)ptc[2].z); @@ -300,6 +289,34 @@ struct TRasterMesh { static_cast(this)->Raster(pt, bary); } }; + +// used to render a mesh +template +struct TRasterMesh : TRasterMeshBase { + typedef TRasterMeshBase Base; + + using Base::camera; + using Base::depthMap; + + using Base::ptc; + using Base::pti; + + const Mesh::VertexArr& vertices; + + TRasterMesh(const Mesh::VertexArr& _vertices, const Camera& _camera, DepthMap& _depthMap) + : Base(_camera, _depthMap), vertices(_vertices) {} + + void Project(const Mesh::Face& facet) { + // project face vertices to image plane + for (int v=0; v<3; ++v) { + // skip face if not completely inside + if (!static_cast(this)->ProjectVertex(vertices[facet[v]], v)) + return; + } + // draw triangle + Image8U3::RasterizeTriangleBary(pti[0], pti[1], pti[2], *this); + } +}; /*----------------------------------------------------------------*/ } // namespace MVS diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index 58b2c7aa0..084a5fa5b 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -317,16 +317,11 @@ struct MeshTexture { const TexCoord* tri; Color colors[3]; ColorMap& image; - inline RasterPatchColorData(ColorMap& _image) : image(_image) {} - inline void operator()(const ImageRef& pt) { - const Point3f b(BarycentricCoordinates(tri[0], tri[1], tri[2], TexCoord(pt))); - #if 0 - if (b.x<0 || b.y<0 || b.z<0) - return; // outside triangle - #endif + inline cv::Size Size() const { return image.size(); } + inline void operator()(const ImageRef& pt, const Point3f& bary) { ASSERT(image.isInside(pt)); - image(pt) = colors[0]*b.x + colors[1]*b.y + colors[2]*b.z; + image(pt) = colors[0]*bary.x + colors[1]*bary.y + colors[2]*bary.z; } }; @@ -334,7 +329,6 @@ struct MeshTexture { struct RasterPatchCoverageData { const TexCoord* tri; Image8U& image; - inline RasterPatchCoverageData(Image8U& _image) : image(_image) {} inline void operator()(const ImageRef& pt) { ASSERT(image.isInside(pt)); @@ -352,7 +346,6 @@ struct MeshTexture { const TexCoord p1, p1Dir; const float length; const Sampler sampler; - inline RasterPatchMeanEdgeData(Image32F3& _image, Image8U& _mask, const Image32F3& _image0, const Image8U3& _image1, const TexCoord& _p0, const TexCoord& _p0Adj, const TexCoord& _p1, const TexCoord& _p1Adj) : image(_image), mask(_mask), image0(_image0), image1(_image1), @@ -1342,7 +1335,7 @@ void MeshTexture::GlobalSeamLeveling() data.colors[v] = colorAdjustments.row(vertpatch2rows[face[v]].at(idxPatch)); // render triangle and for each pixel interpolate the color adjustment // from the triangle corners using barycentric coordinates - ColorMap::RasterizeTriangle(data.tri[0], data.tri[1], data.tri[2], data); + ColorMap::RasterizeTriangleBary(data.tri[0], data.tri[1], data.tri[2], data); } // dilate with one pixel width, in order to make sure patch border smooths out a little imageAdj.DilateMean<1>(imageAdj, Color::ZERO); @@ -1666,8 +1659,7 @@ void MeshTexture::LocalSeamLeveling() image0(texturePatch.rect).convertTo(image, CV_32FC3, 1.0/255.0); image.copyTo(imageOrg); // render patch coverage - Image8U mask(texturePatch.rect.size()); - { + Image8U mask(texturePatch.rect.size()); { mask.memset(0); RasterPatchCoverageData data(mask); FOREACHPTR(pIdxFace, texturePatch.faces) { From dd69b9f672bab3f761d198de4a9d875c068239f5 Mon Sep 17 00:00:00 2001 From: cDc Date: Mon, 12 Apr 2021 22:30:30 +0300 Subject: [PATCH 034/252] dense: merge depth-maps (no fusion) --- apps/DensifyPointCloud/DensifyPointCloud.cpp | 3 +- libs/MVS/DepthMap.cpp | 2 +- libs/MVS/SceneDensify.cpp | 75 +++++++++++++++++++- libs/MVS/SceneDensify.h | 1 + 4 files changed, 78 insertions(+), 3 deletions(-) diff --git a/apps/DensifyPointCloud/DensifyPointCloud.cpp b/apps/DensifyPointCloud/DensifyPointCloud.cpp index 61ef6ddf5..f2a6f986e 100644 --- a/apps/DensifyPointCloud/DensifyPointCloud.cpp +++ b/apps/DensifyPointCloud/DensifyPointCloud.cpp @@ -96,6 +96,7 @@ bool Initialize(size_t argc, LPCTSTR* argv) unsigned nMinViewsFuse; unsigned nEstimateColors; unsigned nEstimateNormals; + bool bFuseDepthMaps; int nIgnoreMaskLabel; boost::program_options::options_description config("Densify options"); config.add_options() @@ -105,7 +106,7 @@ bool Initialize(size_t argc, LPCTSTR* argv) ("max-resolution", boost::program_options::value(&nMaxResolution)->default_value(3200), "do not scale images higher than this resolution") ("min-resolution", boost::program_options::value(&nMinResolution)->default_value(640), "do not scale images lower than this resolution") ("number-views", boost::program_options::value(&nNumViews)->default_value(5), "number of views used for depth-map estimation (0 - all neighbor views available)") - ("number-views-fuse", boost::program_options::value(&nMinViewsFuse)->default_value(3), "minimum number of images that agrees with an estimate during fusion in order to consider it inlier") + ("number-views-fuse", boost::program_options::value(&nMinViewsFuse)->default_value(3), "minimum number of images that agrees with an estimate during fusion in order to consider it inlier (<2 - only merge depth-maps)") ("ignore-mask-label", boost::program_options::value(&nIgnoreMaskLabel)->default_value(-1), "integer value for the label to ignore in the segmentation mask (<0 - disabled)") ("estimate-colors", boost::program_options::value(&nEstimateColors)->default_value(2), "estimate the colors for the dense point-cloud (0 - disabled, 1 - final, 2 - estimate)") ("estimate-normals", boost::program_options::value(&nEstimateNormals)->default_value(2), "estimate the normals for the dense point-cloud (0 - disabled, 1 - final, 2 - estimate)") diff --git a/libs/MVS/DepthMap.cpp b/libs/MVS/DepthMap.cpp index 4d822e058..136f772ce 100644 --- a/libs/MVS/DepthMap.cpp +++ b/libs/MVS/DepthMap.cpp @@ -73,7 +73,7 @@ MDEFVAR_OPTDENSE_uint32(nMaxResolution, "Max Resolution", "Do not scale images l MDEFVAR_OPTDENSE_uint32(nMinResolution, "Min Resolution", "Do not scale images lower than this resolution", "640") DEFVAR_OPTDENSE_uint32(nMinViews, "Min Views", "minimum number of agreeing views to validate a depth", "2") MDEFVAR_OPTDENSE_uint32(nMaxViews, "Max Views", "maximum number of neighbor images used to compute the depth-map for the reference image", "12") -DEFVAR_OPTDENSE_uint32(nMinViewsFuse, "Min Views Fuse", "minimum number of images that agrees with an estimate during fusion in order to consider it inlier", "2") +DEFVAR_OPTDENSE_uint32(nMinViewsFuse, "Min Views Fuse", "minimum number of images that agrees with an estimate during fusion in order to consider it inlier (<2 - only merge depth-maps)", "2") DEFVAR_OPTDENSE_uint32(nMinViewsFilter, "Min Views Filter", "minimum number of images that agrees with an estimate in order to consider it inlier", "2") MDEFVAR_OPTDENSE_uint32(nMinViewsFilterAdjust, "Min Views Filter Adjust", "minimum number of images that agrees with an estimate in order to consider it inlier (0 - disabled)", "1") MDEFVAR_OPTDENSE_uint32(nMinViewsTrustPoint, "Min Views Trust Point", "min-number of views so that the point is considered for approximating the depth-maps (<2 - random initialization)", "2") diff --git a/libs/MVS/SceneDensify.cpp b/libs/MVS/SceneDensify.cpp index 4d07e6e03..8a086f57e 100644 --- a/libs/MVS/SceneDensify.cpp +++ b/libs/MVS/SceneDensify.cpp @@ -1159,6 +1159,73 @@ bool DepthMapsData::FilterDepthMap(DepthData& depthDataRef, const IIndexArr& idx } // FilterDepthMap /*----------------------------------------------------------------*/ + +// fuse all depth-maps by simply projecting them in a 3D point cloud +// in the world coordinate space +void DepthMapsData::MergeDepthMaps(PointCloud& pointcloud, bool bEstimateColor, bool bEstimateNormal) +{ + TD_TIMER_STARTD(); + + // estimate total number of 3D points that will be generated + size_t nPointsEstimate(0); + for (const DepthData& depthData: arrDepthData) + if (depthData.IsValid()) + nPointsEstimate += (size_t)depthData.depthMap.size().area()*7/10; + + // fuse all depth-maps + size_t nDepthMaps(0), nDepths(0); + pointcloud.points.reserve(nPointsEstimate); + pointcloud.pointViews.reserve(nPointsEstimate); + if (bEstimateColor) + pointcloud.colors.reserve(nPointsEstimate); + if (bEstimateNormal) + pointcloud.normals.reserve(nPointsEstimate); + Util::Progress progress(_T("Merged depth-maps"), arrDepthData.size()); + GET_LOGCONSOLE().Pause(); + FOREACH(idxImage, arrDepthData) { + TD_TIMER_STARTD(); + DepthData& depthData = arrDepthData[idxImage]; + ASSERT(depthData.GetView().GetLocalID(scene.images) == idxImage); + if (!depthData.IsValid()) + continue; + if (depthData.IncRef(ComposeDepthFilePath(depthData.GetView().GetID(), "dmap")) == 0) + return; + ASSERT(!depthData.IsEmpty()); + const DepthData::ViewData& image = depthData.GetView(); + const size_t nNumPointsPrev(pointcloud.points.size()); + for (int i=0; i(x),depth))); + pointcloud.pointViews.emplace_back().push_back(idxImage); + if (bEstimateColor) + pointcloud.colors.emplace_back(image.pImageData->image(x)); + if (bEstimateNormal) + depthData.GetNormal(x, pointcloud.normals.emplace_back()); + ++nDepths; + } + } + depthData.DecRef(); + ++nDepthMaps; + ASSERT(pointcloud.points.size() == pointcloud.pointViews.size()); + DEBUG_ULTIMATE("Depths map for reference image %3u merged using %u depths maps: %u new points (%s)", + idxImage, depthData.images.size()-1, pointcloud.points.size()-nNumPointsPrev, TD_TIMER_GET_FMT().c_str()); + progress.display(idxImage+1); + } + GET_LOGCONSOLE().Play(); + progress.close(); + + DEBUG_EXTRA("Depth-maps merged: %u depth-maps, %u depths, %u points (%d%%%%) (%s)", + nDepthMaps, nDepths, pointcloud.points.size(), ROUND2INT(100.f*pointcloud.points.size()/nDepths), TD_TIMER_GET_FMT().c_str()); +} // MergeDepthMaps +/*----------------------------------------------------------------*/ + // fuse all valid depth-maps in the same 3D point cloud; // join points very likely to represent the same 3D point and // filter out points blocking the view @@ -1437,7 +1504,13 @@ bool Scene::DenseReconstruction(int nFusionMode) // fuse all depth-maps pointcloud.Release(); - data.depthMaps.FuseDepthMaps(pointcloud, OPTDENSE::nEstimateColors == 2, OPTDENSE::nEstimateNormals == 2); + if (OPTDENSE::nMinViewsFuse < 2) { + // merge depth-maps + data.depthMaps.MergeDepthMaps(pointcloud, OPTDENSE::nEstimateColors == 2, OPTDENSE::nEstimateNormals == 2); + } else { + // fuse depth-maps + data.depthMaps.FuseDepthMaps(pointcloud, OPTDENSE::nEstimateColors == 2, OPTDENSE::nEstimateNormals == 2); + } #if TD_VERBOSE != TD_VERBOSE_OFF if (g_nVerbosityLevel > 2) { // print number of points with 3+ views diff --git a/libs/MVS/SceneDensify.h b/libs/MVS/SceneDensify.h index 2f21142ab..cacd94b38 100644 --- a/libs/MVS/SceneDensify.h +++ b/libs/MVS/SceneDensify.h @@ -62,6 +62,7 @@ class MVS_API DepthMapsData bool GapInterpolation(DepthData& depthData); bool FilterDepthMap(DepthData& depthData, const IIndexArr& idxNeighbors, bool bAdjust=true); + void MergeDepthMaps(PointCloud& pointcloud, bool bEstimateColor, bool bEstimateNormal); void FuseDepthMaps(PointCloud& pointcloud, bool bEstimateColor, bool bEstimateNormal); protected: From 17360bf3a72a5cad41920c6170e0df0906c5579e Mon Sep 17 00:00:00 2001 From: cDc Date: Fri, 16 Apr 2021 09:05:39 +0300 Subject: [PATCH 035/252] interface: add similarity transform functionality --- libs/MVS/Interface.h | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/libs/MVS/Interface.h b/libs/MVS/Interface.h index 209f9f00a..7b3d96a0c 100644 --- a/libs/MVS/Interface.h +++ b/libs/MVS/Interface.h @@ -416,6 +416,23 @@ struct Interface inline Pos3d GetTranslation() const { return R*(-C); } inline void SetTranslation(const Pos3d& T) { C = R.t()*(-T); } + // combine poses + inline Pose operator * (const Pose& P) const { + return Pose(R*P.R, P.R.t()*C+P.C); + } + inline Pose& operator *= (const Pose& P) { + R = R*P.R; C = P.R.t()*C+P.C; return *this; + } + + // project point: world to local coordinates + inline Pos3d operator * (const Pos3d& X) const { + return R * (X - C); + } + // back-project point: local to world coordinates + inline Pos3d operator / (const Pos3d& X) const { + return R.t() * X + C; + } + template void serialize(Archive& ar, const unsigned int /*version*/) { ar & R; @@ -631,6 +648,29 @@ struct Interface return platforms[image.platformID].GetPose(image.cameraID, image.poseID); } + // apply similarity transform + void Transform(const Mat33d& rotation, const Pos3d& translation, const double scale) { + for (Platform& platform : platforms) { + for (Platform::Pose& pose : platform.poses) { + pose.R = pose.R * rotation.t(); + pose.C = rotation * pose.C * scale + translation; + } + } + for (Vertex& vertex : vertices) { + vertex.X = rotation * Pos3d(vertex.X) * scale + translation; + } + for (Normal& normal : verticesNormal) { + normal.n = rotation * Pos3d(normal.n); + } + for (Line& line : lines) { + line.pt1 = rotation * Pos3d(line.pt1) * scale + translation; + line.pt2 = rotation * Pos3d(line.pt2) * scale + translation; + } + for (Normal& normal : linesNormal) { + normal.n = rotation * Pos3d(normal.n); + } + } + template void serialize(Archive& ar, const unsigned int version) { ar & platforms; From 432570303404baecb11cfcd71ca1a6570f4abaf9 Mon Sep 17 00:00:00 2001 From: cDc Date: Fri, 16 Apr 2021 09:06:14 +0300 Subject: [PATCH 036/252] common: fix file permissions on linux --- libs/Common/File.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/Common/File.h b/libs/Common/File.h index f369bb5df..9c4be9a7f 100644 --- a/libs/Common/File.h +++ b/libs/Common/File.h @@ -441,7 +441,7 @@ class GENERAL_API File : public IOStream { if (flags & NOBUFFER) m |= O_DIRECT; #endif - h = ::open(aFileName, m, S_IRUSR | S_IWUSR); + h = ::open(aFileName, m, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); } bool isOpen() { return h != -1; }; From 0f2a5a08502ced420f4c7600d7e95aecb6e0eaaa Mon Sep 17 00:00:00 2001 From: cDc Date: Sat, 17 Apr 2021 16:01:36 +0300 Subject: [PATCH 037/252] mesh: add GLTF writing support --- apps/TextureMesh/TextureMesh.cpp | 11 +- libs/IO/json.hpp | 20406 +++++++++++++++++++++++++++++ libs/IO/tiny_gltf.h | 7754 +++++++++++ libs/MVS/Mesh.cpp | 239 + libs/MVS/Mesh.h | 2 + 5 files changed, 28410 insertions(+), 2 deletions(-) create mode 100644 libs/IO/json.hpp create mode 100644 libs/IO/tiny_gltf.h diff --git a/apps/TextureMesh/TextureMesh.cpp b/apps/TextureMesh/TextureMesh.cpp index a0ce3cae5..07307d51d 100644 --- a/apps/TextureMesh/TextureMesh.cpp +++ b/apps/TextureMesh/TextureMesh.cpp @@ -82,7 +82,7 @@ bool Initialize(size_t argc, LPCTSTR* argv) ("help,h", "produce this help message") ("working-folder,w", boost::program_options::value(&WORKING_FOLDER), "working directory (default current directory)") ("config-file,c", boost::program_options::value(&OPT::strConfigFileName)->default_value(APPNAME _T(".cfg")), "file name containing program options") - ("export-type", boost::program_options::value(&OPT::strExportType)->default_value(_T("ply")), "file type used to export the 3D scene (ply or obj)") + ("export-type", boost::program_options::value(&OPT::strExportType)->default_value(_T("gltf")), "file type used to export the 3D scene (ply, obj or gltf)") ("archive-type", boost::program_options::value(&OPT::nArchiveType)->default_value(ARCHIVE_DEFAULT), "project archive type: 0-text, 1-binary, 2-compressed binary") ("process-priority", boost::program_options::value(&OPT::nProcessPriority)->default_value(-1), "process priority (below normal by default)") ("max-threads", boost::program_options::value(&OPT::nMaxThreads)->default_value(0), "maximum number of threads (0 for using all available cores)") @@ -166,7 +166,14 @@ bool Initialize(size_t argc, LPCTSTR* argv) } if (OPT::strInputFileName.IsEmpty()) return false; - OPT::strExportType = OPT::strExportType.ToLower() == _T("obj") ? _T(".obj") : _T(".ply"); + OPT::strExportType = OPT::strExportType.ToLower(); + if (OPT::strExportType == _T("obj")) + OPT::strExportType = _T(".obj"); + else + if (OPT::strExportType == _T("gltf")) + OPT::strExportType = _T(".gltf"); + else + OPT::strExportType = _T(".ply"); // initialize optional options Util::ensureValidPath(OPT::strOutputFileName); diff --git a/libs/IO/json.hpp b/libs/IO/json.hpp new file mode 100644 index 000000000..c9af0bed3 --- /dev/null +++ b/libs/IO/json.hpp @@ -0,0 +1,20406 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ +| | |__ | | | | | | version 3.5.0 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +SPDX-License-Identifier: MIT +Copyright (c) 2013-2018 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef NLOHMANN_JSON_HPP +#define NLOHMANN_JSON_HPP + +#define NLOHMANN_JSON_VERSION_MAJOR 3 +#define NLOHMANN_JSON_VERSION_MINOR 5 +#define NLOHMANN_JSON_VERSION_PATCH 0 + +#include // all_of, find, for_each +#include // assert +#include // and, not, or +#include // nullptr_t, ptrdiff_t, size_t +#include // hash, less +#include // initializer_list +#include // istream, ostream +#include // random_access_iterator_tag +#include // accumulate +#include // string, stoi, to_string +#include // declval, forward, move, pair, swap + +// #include +#ifndef NLOHMANN_JSON_FWD_HPP +#define NLOHMANN_JSON_FWD_HPP + +#include // int64_t, uint64_t +#include // map +#include // allocator +#include // string +#include // vector + +/*! +@brief namespace for Niels Lohmann +@see https://github.com/nlohmann +@since version 1.0.0 +*/ +namespace nlohmann +{ +/*! +@brief default JSONSerializer template argument + +This serializer ignores the template arguments and uses ADL +([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) +for serialization. +*/ +template +struct adl_serializer; + +template class ObjectType = + std::map, + template class ArrayType = std::vector, + class StringType = std::string, class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template class AllocatorType = std::allocator, + template class JSONSerializer = + adl_serializer> +class basic_json; + +/*! +@brief JSON Pointer + +A JSON pointer defines a string syntax for identifying a specific value +within a JSON document. It can be used with functions `at` and +`operator[]`. Furthermore, JSON pointers are the base for JSON patches. + +@sa [RFC 6901](https://tools.ietf.org/html/rfc6901) + +@since version 2.0.0 +*/ +template +class json_pointer; + +/*! +@brief default JSON class + +This type is the default specialization of the @ref basic_json class which +uses the standard template types. + +@since version 1.0.0 +*/ +using json = basic_json<>; +} // namespace nlohmann + +#endif + +// #include + + +// This file contains all internal macro definitions +// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them + +// exclude unsupported compilers +#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) + #if defined(__clang__) + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 + #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 + #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #endif +#endif + +// disable float-equal warnings on GCC/clang +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + +// disable documentation warnings on clang +#if defined(__clang__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdocumentation" +#endif + +// allow for portable deprecation warnings +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #define JSON_DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) + #define JSON_DEPRECATED __declspec(deprecated) +#else + #define JSON_DEPRECATED +#endif + +// allow to disable exceptions +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) + #define JSON_THROW(exception) throw exception + #define JSON_TRY try + #define JSON_CATCH(exception) catch(exception) + #define JSON_INTERNAL_CATCH(exception) catch(exception) +#else + #define JSON_THROW(exception) std::abort() + #define JSON_TRY if(true) + #define JSON_CATCH(exception) if(false) + #define JSON_INTERNAL_CATCH(exception) if(false) +#endif + +// override exception macros +#if defined(JSON_THROW_USER) + #undef JSON_THROW + #define JSON_THROW JSON_THROW_USER +#endif +#if defined(JSON_TRY_USER) + #undef JSON_TRY + #define JSON_TRY JSON_TRY_USER +#endif +#if defined(JSON_CATCH_USER) + #undef JSON_CATCH + #define JSON_CATCH JSON_CATCH_USER + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_CATCH_USER +#endif +#if defined(JSON_INTERNAL_CATCH_USER) + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER +#endif + +// manual branch prediction +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #define JSON_LIKELY(x) __builtin_expect(!!(x), 1) + #define JSON_UNLIKELY(x) __builtin_expect(!!(x), 0) +#else + #define JSON_LIKELY(x) x + #define JSON_UNLIKELY(x) x +#endif + +// C++ language standard detection +#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 +#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 +#endif + +/*! +@brief macro to briefly define a mapping between an enum and JSON +@def NLOHMANN_JSON_SERIALIZE_ENUM +@since version 3.4.0 +*/ +#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ + template \ + inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [e](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.first == e; \ + }); \ + j = ((it != std::end(m)) ? it : std::begin(m))->second; \ + } \ + template \ + inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [j](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.second == j; \ + }); \ + e = ((it != std::end(m)) ? it : std::begin(m))->first; \ + } + +// Ugly macros to avoid uglier copy-paste when specializing basic_json. They +// may be removed in the future once the class is split. + +#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ + template class ObjectType, \ + template class ArrayType, \ + class StringType, class BooleanType, class NumberIntegerType, \ + class NumberUnsignedType, class NumberFloatType, \ + template class AllocatorType, \ + template class JSONSerializer> + +#define NLOHMANN_BASIC_JSON_TPL \ + basic_json + +// #include + + +#include // not +#include // size_t +#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type + +namespace nlohmann +{ +namespace detail +{ +// alias templates to reduce boilerplate +template +using enable_if_t = typename std::enable_if::type; + +template +using uncvref_t = typename std::remove_cv::type>::type; + +// implementation of C++14 index_sequence and affiliates +// source: https://stackoverflow.com/a/32223343 +template +struct index_sequence +{ + using type = index_sequence; + using value_type = std::size_t; + static constexpr std::size_t size() noexcept + { + return sizeof...(Ints); + } +}; + +template +struct merge_and_renumber; + +template +struct merge_and_renumber, index_sequence> + : index_sequence < I1..., (sizeof...(I1) + I2)... > {}; + +template +struct make_index_sequence + : merge_and_renumber < typename make_index_sequence < N / 2 >::type, + typename make_index_sequence < N - N / 2 >::type > {}; + +template<> struct make_index_sequence<0> : index_sequence<> {}; +template<> struct make_index_sequence<1> : index_sequence<0> {}; + +template +using index_sequence_for = make_index_sequence; + +// dispatch utility (taken from ranges-v3) +template struct priority_tag : priority_tag < N - 1 > {}; +template<> struct priority_tag<0> {}; + +// taken from ranges-v3 +template +struct static_const +{ + static constexpr T value{}; +}; + +template +constexpr T static_const::value; +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // not +#include // numeric_limits +#include // false_type, is_constructible, is_integral, is_same, true_type +#include // declval + +// #include + +// #include + + +#include // random_access_iterator_tag + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template struct make_void +{ + using type = void; +}; +template using void_t = typename make_void::type; +} // namespace detail +} // namespace nlohmann + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template +struct iterator_types {}; + +template +struct iterator_types < + It, + void_t> +{ + using difference_type = typename It::difference_type; + using value_type = typename It::value_type; + using pointer = typename It::pointer; + using reference = typename It::reference; + using iterator_category = typename It::iterator_category; +}; + +// This is required as some compilers implement std::iterator_traits in a way that +// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. +template +struct iterator_traits +{ +}; + +template +struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> + : iterator_types +{ +}; + +template +struct iterator_traits::value>> +{ + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = T&; +}; +} +} + +// #include + +// #include + + +#include + +// #include + + +// http://en.cppreference.com/w/cpp/experimental/is_detected +namespace nlohmann +{ +namespace detail +{ +struct nonesuch +{ + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + void operator=(nonesuch const&) = delete; +}; + +template class Op, + class... Args> +struct detector +{ + using value_t = std::false_type; + using type = Default; +}; + +template class Op, class... Args> +struct detector>, Op, Args...> +{ + using value_t = std::true_type; + using type = Op; +}; + +template