diff --git a/CMakeLists.txt b/CMakeLists.txt index b81bfe64..77790118 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,3 +1,5 @@ +set(CMAKE_POLICY_VERSION_MINIMUM 3.5) + # Detects whether this is a top-level project get_directory_property(HAS_PARENT PARENT_DIRECTORY) if(HAS_PARENT) @@ -41,7 +43,7 @@ option(RIGID_IPC_WITH_DERIVATIVE_CHECK "Check derivatives using finite diff # Set default minimum C++ standard if(RIGID_IPC_TOPLEVEL_PROJECT) - set(CMAKE_CXX_STANDARD 14) + set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) endif() @@ -57,30 +59,10 @@ include(rigid_ipc_use_colors) include(rigid_ipc_autogen) -mark_as_advanced(CLEAR CMAKE_BUILD_TYPE) - ################################################################################ # External project settings ################################################################################ -# libigl options -option(LIBIGL_USE_STATIC_LIBRARY "Use libigl as static library" OFF) -option(LIBIGL_WITH_PREDICATES "Use exact predicates" ON) -set(LIBIGL_WITH_OPENGL_GLFW_IMGUI ${RIGID_IPC_WITH_OPENGL} CACHE BOOL "Use OpenGL" FORCE) -set(LIBIGL_WITH_OPENGL_GLFW ${RIGID_IPC_WITH_OPENGL} CACHE BOOL "Use GLFW" FORCE) -set(LIBIGL_WITH_OPENGL ${RIGID_IPC_WITH_OPENGL} CACHE BOOL "Use IMGUI" FORCE) -set(LIBIGL_WITH_PNG ${RIGID_IPC_WITH_OPENGL} CACHE BOOL "Use PNG" FORCE) - -# PolySolve settings -option(POLYSOLVE_WITH_CHOLMOD "Enable Cholmod library" ON) -option(POLYSOLVE_WITH_UMFPACK "Enable UmfPack library" OFF) -option(POLYSOLVE_WITH_SUPERLU "Enable SuperLU library" OFF) -option(POLYSOLVE_WITH_MKL "Enable MKL library" OFF) -option(POLYSOLVE_WITH_PARDISO "Enable Pardiso library" OFF) -option(POLYSOLVE_WITH_HYPRE "Enable hypre" OFF) -option(POLYSOLVE_WITH_AMGCL "Use AMGCL" OFF) -option(POLYSOLVE_WITH_SPECTRA "Enable computing spectrum" OFF) - set(TIGHT_INCLUSION_WITH_NO_ZERO_TOI OFF CACHE BOOL "Enable refinement if CCD produces a zero ToI" FORCE) ################################################################################ @@ -201,7 +183,7 @@ target_link_libraries(ipc_rigid PUBLIC # JSON Parser include(json) -target_link_libraries(ipc_rigid PUBLIC nlohmann::json) +target_link_libraries(ipc_rigid PUBLIC nlohmann_json::nlohmann_json) # Logger include(spdlog) @@ -216,17 +198,13 @@ include(boost) target_link_libraries(ipc_rigid PUBLIC Boost::boost) # TBB -include(tbb) +include(onetbb) target_link_libraries(ipc_rigid PUBLIC TBB::tbb) # MSCCD include(tight_inclusion) target_link_libraries(ipc_rigid PUBLIC tight_inclusion::tight_inclusion) -# PolySolve for wrapping linear solvers -include(polysolve) -target_link_libraries(ipc_rigid PUBLIC PolyFEM::polysolve) - # IPC Toolkit include(ipc_toolkit) target_link_libraries(ipc_rigid PUBLIC ipc::toolkit) @@ -243,10 +221,6 @@ target_link_libraries(ipc_rigid PUBLIC simple_bvh::simple_bvh) include(tinygltf) target_link_libraries(ipc_rigid PUBLIC tinygltf::tinygltf) -# Filesystem -include(filesystem) -target_link_libraries(ipc_rigid PUBLIC ghc::filesystem) - ################################################################################ # Compiler options ################################################################################ @@ -284,7 +258,7 @@ add_executable(rigid_ipc_sim src/main.cpp) if(RIGID_IPC_WITH_OPENGL) target_sources(rigid_ipc_sim PUBLIC src/viewer/imgui_ext.cpp - src/viewer/igl_viewer_ext.cpp + src/viewer/viewer_data.cpp src/viewer/UISimState.cpp src/viewer/UIMenu.cpp ) @@ -298,13 +272,13 @@ target_link_libraries(rigid_ipc_sim PUBLIC CLI11::CLI11) if(RIGID_IPC_WITH_OPENGL) target_compile_definitions(rigid_ipc_sim PUBLIC RIGID_IPC_WITH_OPENGL) - include(libigl) - target_link_libraries(rigid_ipc_sim PUBLIC - igl::opengl igl::opengl_glfw igl::opengl_glfw_imgui igl::png) - # Charlie Tangora's gif-h library include(gif_h) target_link_libraries(rigid_ipc_sim PRIVATE gif_h::gif_h) + + # Polyscope visualization library + include(polyscope) + target_link_libraries(rigid_ipc_sim PUBLIC polyscope::polyscope) endif() ################################################################################ diff --git a/cmake/recipes/eigen.cmake b/cmake/recipes/eigen.cmake index 8e33f247..0c0e3bac 100644 --- a/cmake/recipes/eigen.cmake +++ b/cmake/recipes/eigen.cmake @@ -1,53 +1,39 @@ -# -# Copyright 2020 Adobe. All rights reserved. -# This file is licensed to you under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. You may obtain a copy -# of the License at http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software distributed under -# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -# OF ANY KIND, either express or implied. See the License for the specific language -# governing permissions and limitations under the License. -# +# Eigen (https://gitlab.com/libeigen/eigen) +# License: MPL 2.0 if(TARGET Eigen3::Eigen) return() endif() option(EIGEN_WITH_MKL "Use Eigen with MKL" OFF) +option(EIGEN_DONT_VECTORIZE "Disable Eigen vectorization" OFF) +option(EIGEN_MPL2_ONLY "Enable Eigen MPL2 license only" OFF) -if(EIGEN_ROOT) - message(STATUS "Third-party: creating target 'Eigen3::Eigen' for external path: ${EIGEN_ROOT}") - set(EIGEN_INCLUDE_DIRS ${EIGEN_ROOT}) -else() - message(STATUS "Third-party: creating target 'Eigen3::Eigen'") +message(STATUS "Third-party: creating target 'Eigen3::Eigen'") - include(FetchContent) - FetchContent_Declare( - eigen - GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git - GIT_TAG tags/3.3.7 - GIT_SHALLOW TRUE - ) - FetchContent_GetProperties(eigen) - if(NOT eigen_POPULATED) - FetchContent_Populate(eigen) - endif() - set(EIGEN_INCLUDE_DIRS ${eigen_SOURCE_DIR}) - - install(DIRECTORY ${EIGEN_INCLUDE_DIRS}/Eigen - DESTINATION include - ) -endif() +include(CPM) +CPMAddPackage( + NAME eigen + GITLAB_REPOSITORY libeigen/eigen + GIT_TAG 3.3.7 + DOWNLOAD_ONLY YES +) add_library(Eigen3_Eigen INTERFACE) add_library(Eigen3::Eigen ALIAS Eigen3_Eigen) include(GNUInstallDirs) target_include_directories(Eigen3_Eigen SYSTEM INTERFACE - $ + $ $ ) -# target_compile_definitions(Eigen3_Eigen INTERFACE EIGEN_MPL2_ONLY) + +if(EIGEN_MPL2_ONLY) + target_compile_definitions(Eigen3_Eigen INTERFACE EIGEN_MPL2_ONLY) +endif() + +if(EIGEN_DONT_VECTORIZE) + target_compile_definitions(Eigen3_Eigen INTERFACE EIGEN_DONT_VECTORIZE) +endif() if(EIGEN_WITH_MKL) # TODO: Checks that, on 64bits systems, `mkl::mkl` is using the LP64 interface @@ -58,6 +44,27 @@ if(EIGEN_WITH_MKL) EIGEN_USE_MKL_ALL EIGEN_USE_LAPACKE_STRICT ) +elseif(APPLE) + find_package(BLAS REQUIRED) + find_library(LAPACKE lapacke PATHS + "/opt/local/lib/lapack" + "/opt/homebrew/opt/lapack/lib" + ) + if (NOT LAPACKE) + # BLAS should be available on macOS, but LAPACKE might not be + message(WARNING "LAPACKE library not found (required for EIGEN_USE_LAPACKE on macOS)! " + "Perhaps you need to install it (e.g., brew install lapack). " + "Eigen will be built without LAPACKE support.") + else() + message(STATUS "Found BLAS and LAPACKE. Enabling Eigen LAPACKE support.") + target_link_libraries(Eigen3_Eigen INTERFACE + ${BLAS_LIBRARIES} ${LAPACKE} + ) + target_compile_definitions(Eigen3_Eigen INTERFACE + EIGEN_USE_BLAS + EIGEN_USE_LAPACKE_STRICT + ) + endif() endif() # On Windows, enable natvis files to improve debugging experience @@ -68,6 +75,6 @@ endif() # Install rules set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME eigen) set_target_properties(Eigen3_Eigen PROPERTIES EXPORT_NAME Eigen) -install(DIRECTORY ${EIGEN_INCLUDE_DIRS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +install(DIRECTORY ${eigen_SOURCE_DIR} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install(TARGETS Eigen3_Eigen EXPORT Eigen_Targets) -install(EXPORT Eigen_Targets DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/eigen NAMESPACE Eigen3::) +install(EXPORT Eigen_Targets DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/eigen NAMESPACE Eigen3::) \ No newline at end of file diff --git a/cmake/recipes/filesystem.cmake b/cmake/recipes/filesystem.cmake deleted file mode 100644 index 97fd4898..00000000 --- a/cmake/recipes/filesystem.cmake +++ /dev/null @@ -1,63 +0,0 @@ -if(TARGET ghc::filesystem) - return() -endif() - -message(STATUS "Third-party: creating target 'ghc::filesystem'") - -include(FetchContent) -FetchContent_Declare( - filesystem - GIT_REPOSITORY https://github.com/gulrak/filesystem.git - GIT_TAG v1.5.8 - GIT_SHALLOW TRUE -) -FetchContent_MakeAvailable(filesystem) -add_library(ghc::filesystem ALIAS ghc_filesystem) - -# Check if we need to link against any special libraries (e.g., stdc++fs for GCC < 9) -set(SAVED_CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES}) -set(SAVED_CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) - -set(GHC_FILESYSTEM_TEST_CODE [[ - #include - int main() { - auto cwd = fs::current_path(); - return static_cast(cwd.string().size()); - } - ]]) - - -# Try to compile a simple filesystem program without any linker flags -list(APPEND CMAKE_REQUIRED_INCLUDES "${filesystem_SOURCE_DIR}/include") -check_cxx_source_compiles("${GHC_FILESYSTEM_TEST_CODE}" GHC_FILESYSTEM_NO_LINK_NEEDED) - -if(NOT GHC_FILESYSTEM_NO_LINK_NEEDED) - # Add the libstdc++ flag - set(CMAKE_REQUIRED_LIBRARIES ${SAVED_CMAKE_REQUIRED_LIBRARIES} -lstdc++fs) - check_cxx_source_compiles("${GHC_FILESYSTEM_TEST_CODE}" GHC_FILESYSTEM_STDCPPFS_NEEDED) - - if(NOT GHC_FILESYSTEM_STDCPPFS_NEEDED) - # Try the libc++ flag - set(CMAKE_REQUIRED_LIBRARIES ${SAVED_CMAKE_REQUIRED_LIBRARIES} -lc++fs) - check_cxx_source_compiles("${GHC_FILESYSTEM_TEST_CODE}" GHC_FILESYSTEM_CPPFS_NEEDED) - endif() -endif() - -if(GHC_FILESYSTEM_NO_LINK_NEEDED) - # on certain linux distros we have a version of libstdc++ which has the final code for c++17 fs in the - # libstdc++.so.*. BUT when compiling with g++ < 9, we MUST still link with libstdc++fs.a - # libc++ should not suffer from this issue, so, in theory we should be fine with only checking for - # GCC's libstdc++ - if((CMAKE_CXX_COMPILER_ID MATCHES "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "9.0.0")) - target_link_libraries(ghc_filesystem INTERFACE -lstdc++fs) - endif() -elseif(GHC_FILESYSTEM_STDCPPFS_NEEDED) - target_link_libraries(ghc_filesystem INTERFACE -lstdc++fs) -elseif(GHC_FILESYSTEM_CPPFS_NEEDED) - target_link_libraries(ghc_filesystem INTERFACE -lc++fs) -else() - message(FATAL_ERROR "Unable to determine correct linking options to compile GHC filesystem!") -endif() - -set(CMAKE_REQUIRED_INCLUDES ${SAVED_CMAKE_REQUIRED_INCLUDES}) -set(CMAKE_REQUIRED_LIBRARIES ${SAVED_CMAKE_REQUIRED_LIBRARIES}) diff --git a/cmake/recipes/json.cmake b/cmake/recipes/json.cmake index 1a4c9dca..38c76199 100644 --- a/cmake/recipes/json.cmake +++ b/cmake/recipes/json.cmake @@ -9,11 +9,13 @@ # OF ANY KIND, either express or implied. See the License for the specific language # governing permissions and limitations under the License. # -if(TARGET nlohmann::json) + +# JSON MIT +if(TARGET nlohmann_json::nlohmann_json) return() endif() -message(STATUS "Third-party: creating target 'nlohmann::json'") +message(STATUS "Third-party: creating target 'nlohmann_json::nlohmann_json'") # nlohmann_json is a big repo for a single header, so we just download the release archive set(NLOHMANNJSON_VERSION "v3.10.2") @@ -27,7 +29,7 @@ FetchContent_Declare( FetchContent_MakeAvailable(nlohmann_json) add_library(nlohmann_json INTERFACE) -add_library(nlohmann::json ALIAS nlohmann_json) +add_library(nlohmann_json::nlohmann_json ALIAS nlohmann_json) include(GNUInstallDirs) target_include_directories(nlohmann_json INTERFACE diff --git a/cmake/recipes/libigl.cmake b/cmake/recipes/libigl.cmake index 04b54564..742d4fcd 100644 --- a/cmake/recipes/libigl.cmake +++ b/cmake/recipes/libigl.cmake @@ -1,43 +1,19 @@ -# -# Copyright 2020 Adobe. All rights reserved. -# This file is licensed to you under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. You may obtain a copy -# of the License at http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software distributed under -# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -# OF ANY KIND, either express or implied. See the License for the specific language -# governing permissions and limitations under the License. -# +# libigl (https://github.com/libigl/libigl) +# License: MPL-2.0 if(TARGET igl::core) return() endif() message(STATUS "Third-party: creating target 'igl::core'") -include(FetchContent) -FetchContent_Declare( - libigl - GIT_REPOSITORY https://github.com/libigl/libigl.git - GIT_TAG v2.3.0 - GIT_SHALLOW TRUE -) -FetchContent_GetProperties(libigl) -if(libigl_POPULATED) - return() -endif() -FetchContent_Populate(libigl) +set(LIBIGL_PREDICATES ON CACHE BOOL "Use exact predicates" FORCE) include(eigen) -set(LIBIGL_WITH_PREDICATES ON CACHE BOOL "Use exact predicates" FORCE) - -list(APPEND CMAKE_MODULE_PATH ${libigl_SOURCE_DIR}/cmake) -include(${libigl_SOURCE_DIR}/cmake/libigl.cmake ${libigl_BINARY_DIR}) +include(CPM) +CPMAddPackage("gh:libigl/libigl@2.4.0") -# Install rules -set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME libigl) -set_target_properties(igl PROPERTIES EXPORT_NAME core) -install(DIRECTORY ${libigl_SOURCE_DIR}/include/igl DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) -install(TARGETS igl igl_common EXPORT Libigl_Targets) -install(EXPORT Libigl_Targets DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/igl NAMESPACE igl::) +# Folder name for IDE +foreach(target_name IN ITEMS core predicates) + set_target_properties(igl_${target_name} PROPERTIES FOLDER "ThirdParty/libigl") +endforeach() diff --git a/cmake/recipes/onetbb.cmake b/cmake/recipes/onetbb.cmake new file mode 100644 index 00000000..171e7b1f --- /dev/null +++ b/cmake/recipes/onetbb.cmake @@ -0,0 +1,90 @@ +# oneTBB (https://github.com/oneapi-src/oneTBB) +# License: Apache-2.0 +if(TARGET TBB::tbb) + return() +endif() + +message(STATUS "Third-party: creating target 'TBB::tbb' (OneTBB)") + +# Emscripten sets CMAKE_SYSTEM_PROCESSOR to "x86". Change it to "WASM" to prevent TBB from +# adding machine-specific "-mrtm" and "-mwaitpkg" compile options. +if(EMSCRIPTEN) + set(CMAKE_SYSTEM_PROCESSOR "WASM") +endif() + +option(TBB_TEST "Enable testing" OFF) +option(TBB_EXAMPLES "Enable examples" OFF) +option(TBB_STRICT "Treat compiler warnings as errors" OFF) +option(TBB_PREFER_STATIC "Use the static version of TBB for the alias target" ON) +unset(TBB_DIR CACHE) + +function(onetbb_import_target) + macro(push_variable var value) + if(DEFINED CACHE{${var}}) + set(ONETBB_OLD_${var}_VALUE "${${var}}") + set(ONETBB_OLD_${var}_TYPE CACHE_TYPE) + elseif(DEFINED ${var}) + set(ONETBB_OLD_${var}_VALUE "${${var}}") + set(ONETBB_OLD_${var}_TYPE NORMAL_TYPE) + else() + set(ONETBB_OLD_${var}_TYPE NONE_TYPE) + endif() + set(${var} "${value}" CACHE PATH "" FORCE) + endmacro() + + macro(pop_variable var) + if(ONETBB_OLD_${var}_TYPE STREQUAL CACHE_TYPE) + set(${var} "${ONETBB_OLD_${var}_VALUE}" CACHE PATH "" FORCE) + elseif(ONETBB_OLD_${var}_TYPE STREQUAL NORMAL_TYPE) + unset(${var} CACHE) + set(${var} "${ONETBB_OLD_${var}_VALUE}") + elseif(ONETBB_OLD_${var}_TYPE STREQUAL NONE_TYPE) + unset(${var} CACHE) + else() + message(FATAL_ERROR "Trying to pop a variable that has not been pushed: ${var}") + endif() + endmacro() + + if(TBB_PREFER_STATIC) + push_variable(BUILD_SHARED_LIBS OFF) + else() + push_variable(BUILD_SHARED_LIBS ON) + endif() + + set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME tbb) + include(CPM) + CPMAddPackage("gh:uxlfoundation/oneTBB@2022.1.0") + + pop_variable(BUILD_SHARED_LIBS) +endfunction() + +onetbb_import_target() + +if(NOT TARGET TBB::tbb) + message(FATAL_ERROR "TBB::tbb is still not defined!") +endif() + +foreach(name IN ITEMS tbb tbbmalloc tbbmalloc_proxy) + if(TARGET ${name}) + # Folder name for IDE + set_target_properties(${name} PROPERTIES FOLDER "ThirdParty/tbb") + + # Force debug postfix for library name. Our pre-compiled MKL library expects "tbb12.dll" (without postfix). + set_target_properties(${name} PROPERTIES DEBUG_POSTFIX "") + + # Without this macro, TBB will explicitly link against "tbb12_debug.lib" in Debug configs. + # This is undesirable, since our pre-compiled version of MKL is linked against "tbb12.dll". + target_compile_definitions(${name} PUBLIC -D__TBB_NO_IMPLICIT_LINKAGE=1) + + # Disable some features and avoid processor-specific code paths when compiling with + # Emscripten for WebAssembly. + if(EMSCRIPTEN) + target_compile_definitions(${name} PRIVATE + ITT_ARCH=-1 + __TBB_RESUMABLE_TASKS_USE_THREADS=1 + __TBB_DYNAMIC_LOAD_ENABLED=0 + __TBB_WEAK_SYMBOLS_PRESENT=0 + ) + endif() + endif() +endforeach() diff --git a/cmake/recipes/polyscope.cmake b/cmake/recipes/polyscope.cmake new file mode 100644 index 00000000..b4269fdd --- /dev/null +++ b/cmake/recipes/polyscope.cmake @@ -0,0 +1,12 @@ +# Polyscope (https://github.com/nmwsharp/polyscope) +# License: MIT +if(TARGET polyscope::polyscope) + return() +endif() + +message(STATUS "Third-party: creating target 'polyscope::polyscope'") + +include(CPM) +CPMAddPackage("gh:nmwsharp/polyscope@2.3.0") + +add_library(polyscope::polyscope ALIAS polyscope) \ No newline at end of file diff --git a/cmake/recipes/polysolve.cmake b/cmake/recipes/polysolve.cmake deleted file mode 100644 index 73e8a9ec..00000000 --- a/cmake/recipes/polysolve.cmake +++ /dev/null @@ -1,16 +0,0 @@ -if(TARGET PolyFEM::polysolve) - return() -endif() - -message(STATUS "Third-party: creating target 'PolyFEM::polysolve'") - -include(FetchContent) -FetchContent_Declare( - polysolve - GIT_REPOSITORY https://github.com/polyfem/polysolve.git - GIT_TAG 72e5eaca17b1ae975fa5a7149627a17e6b13cf80 - GIT_SHALLOW FALSE -) -FetchContent_MakeAvailable(polysolve) - -add_library(PolyFEM::polysolve ALIAS polysolve) diff --git a/cmake/recipes/software_renderer.cmake b/cmake/recipes/software_renderer.cmake index 0d8127c9..a4107906 100644 --- a/cmake/recipes/software_renderer.cmake +++ b/cmake/recipes/software_renderer.cmake @@ -8,7 +8,7 @@ include(FetchContent) FetchContent_Declare( software_renderer GIT_REPOSITORY https://github.com/zfergus/software-renderer.git - GIT_TAG cdfb409d02a1b3b55e916c549aedbff7e5bdb5e7 + GIT_TAG 2453b2b05b42e95ff38d1b6f91c9857390087a69 GIT_SHALLOW FALSE ) FetchContent_MakeAvailable(software_renderer) diff --git a/cmake/recipes/tbb.cmake b/cmake/recipes/tbb.cmake deleted file mode 100644 index 5b338e00..00000000 --- a/cmake/recipes/tbb.cmake +++ /dev/null @@ -1,137 +0,0 @@ -# -# Copyright 2020 Adobe. All rights reserved. -# This file is licensed to you under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. You may obtain a copy -# of the License at http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software distributed under -# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -# OF ANY KIND, either express or implied. See the License for the specific language -# governing permissions and limitations under the License. -# - -#################################################################################################### -# IMPORTANT -# -# This file defines a single ALIAS target `TBB::tbb`. -# -# Depending on the option TBB_PREFER_STATIC, this alias may point to either the dynamic version -# of TBB, or the static version. The official recommendation is to use the dynamic library: -# -# https://www.threadingbuildingblocks.org/faq/there-version-tbb-provides-statically-linked-libraries -# https://stackoverflow.com/questions/638278/how-to-statically-link-to-tbb -# -# For now we do not have a proper CMake workflow to deal with DLLs, so we default to tbb_static -#################################################################################################### - -if(TARGET TBB::tbb) - return() -endif() - -message(STATUS "Third-party: creating target 'TBB::tbb'") - -# Using wjakob's fork as it has a better cmake build system -# Change it back to intel's once they fix it -# https://github.com/intel/tbb/issues/6 -include(FetchContent) -FetchContent_Declare( - tbb - GIT_REPOSITORY https://github.com/wjakob/tbb.git - GIT_TAG 141b0e310e1fb552bdca887542c9c1a8544d6503 - GIT_SHALLOW FALSE -) - -option(TBB_PREFER_STATIC "Use the static version of TBB for the alias target" ON) -option(TBB_BUILD_SHARED "Build TBB shared library" OFF) -option(TBB_BUILD_STATIC "Build TBB static library" OFF) -option(TBB_BUILD_TBBMALLOC "Build TBB malloc library" ON) -option(TBB_BUILD_TBBMALLOC_PROXY "Build TBB malloc proxy library" OFF) -option(TBB_BUILD_TESTS "Build TBB tests and enable testing infrastructure" OFF) -option(TBB_NO_DATE "Do not save the configure date in the version string" ON) - -# Mark those options as advanced so they don't show up in CMake GUI -# Please use TBB_PREFER_STATIC instead -mark_as_advanced(TBB_BUILD_SHARED TBB_BUILD_STATIC) - -# Make sure tbb or tbb_static is built, according to the user's option -if(TBB_PREFER_STATIC) - set(TBB_BUILD_STATIC ON CACHE BOOL "Build TBB static library" FORCE) - set(TBB_BUILD_SHARED OFF CACHE BOOL "Build TBB shared library" FORCE) -else() - set(TBB_BUILD_SHARED ON CACHE BOOL "Build TBB shared library" FORCE) - set(TBB_BUILD_STATIC OFF CACHE BOOL "Build TBB static library" FORCE) -endif() - -set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME TBB) -FetchContent_MakeAvailable(tbb) - -# Install rules for the tbb_static target (not defined by upstream CMakeLists.txt) -if(TBB_INSTALL_TARGETS AND TBB_BUILD_STATIC) - if(NOT TBB_INSTALL_RUNTIME_DIR) - set(TBB_INSTALL_RUNTIME_DIR bin) - endif() - if(NOT TBB_INSTALL_LIBRARY_DIR) - set(TBB_INSTALL_LIBRARY_DIR lib) - endif() - if(NOT TBB_INSTALL_ARCHIVE_DIR) - set(TBB_INSTALL_ARCHIVE_DIR lib) - endif() - if(NOT TBB_INSTALL_INCLUDE_DIR) - set(TBB_INSTALL_INCLUDE_DIR include) - endif() - if(NOT TBB_CMAKE_PACKAGE_INSTALL_DIR) - set(TBB_CMAKE_PACKAGE_INSTALL_DIR lib/cmake/tbb) - endif() - if(TARGET tbb_interface) - install(TARGETS tbb_interface EXPORT TBB) - endif() - if(TARGET tbb_static) - install(TARGETS tbb_static EXPORT TBB - LIBRARY DESTINATION ${TBB_INSTALL_LIBRARY_DIR} - ARCHIVE DESTINATION ${TBB_INSTALL_ARCHIVE_DIR} - RUNTIME DESTINATION ${TBB_INSTALL_RUNTIME_DIR}) - endif() - if(TARGET tbbmalloc_static) - install(TARGETS tbbmalloc_static EXPORT TBB - LIBRARY DESTINATION ${TBB_INSTALL_LIBRARY_DIR} - ARCHIVE DESTINATION ${TBB_INSTALL_ARCHIVE_DIR} - RUNTIME DESTINATION ${TBB_INSTALL_RUNTIME_DIR}) - endif() - install(EXPORT TBB DESTINATION ${TBB_CMAKE_PACKAGE_INSTALL_DIR} NAMESPACE TBB:: FILE TBBConfig.cmake) -endif() - -# Fix include directories to not explicitly reference the build directory, otherwise install() will complain -function(tbb_fix_include_dirs) - foreach(name IN ITEMS ${ARGN}) - if(NOT TARGET ${name}) - message(FATAL_ERROR "'${name}' is not a CMake target") - endif() - get_target_property(__include_dirs ${name} INTERFACE_INCLUDE_DIRECTORIES) - set_property(TARGET ${name} PROPERTY INTERFACE_INCLUDE_DIRECTORIES - $ - $ - ) - endforeach() -endfunction() - -# Meta-target that brings both tbb and tbbmalloc -add_library(tbb_tbb INTERFACE) -add_library(TBB::tbb ALIAS tbb_tbb) -if(TBB_PREFER_STATIC) - target_link_libraries(tbb_tbb INTERFACE tbb_static tbbmalloc_static) - tbb_fix_include_dirs(tbb_static) -else() - target_link_libraries(tbb_tbb INTERFACE tbb tbbmalloc) - tbb_fix_include_dirs(tbb) -endif() - -# Install rules -install(TARGETS tbb_tbb EXPORT TBB) - -# Set -fPIC flag and IDE folder name for tbb targets -foreach(name IN ITEMS tbb_def_files tbb_static tbb tbbmalloc tbbmalloc_static) - if(TARGET ${name}) - set_target_properties(${name} PROPERTIES POSITION_INDEPENDENT_CODE ON) - set_target_properties(${name} PROPERTIES FOLDER third_party) - endif() -endforeach() diff --git a/src/SimState.cpp b/src/SimState.cpp index f2d8c75f..8faed2ba 100644 --- a/src/SimState.cpp +++ b/src/SimState.cpp @@ -6,7 +6,6 @@ #include #include -#include // filesystem #include #include #include @@ -24,6 +23,9 @@ #include #include +#include +namespace fs = std::filesystem; + namespace ipc::rigid { SimState::SimState() diff --git a/src/io/read_rb_scene.cpp b/src/io/read_rb_scene.cpp index 31e8ef4d..29bb065e 100644 --- a/src/io/read_rb_scene.cpp +++ b/src/io/read_rb_scene.cpp @@ -3,7 +3,6 @@ #include #include -#include // filesystem #include #include #include @@ -16,6 +15,9 @@ #include #include +#include +namespace fs = std::filesystem; + namespace ipc::rigid { template diff --git a/src/io/write_obj.cpp b/src/io/write_obj.cpp index 6d537730..195e0a6b 100644 --- a/src/io/write_obj.cpp +++ b/src/io/write_obj.cpp @@ -15,7 +15,8 @@ #include #include -#include // filesystem +#include +namespace fs = std::filesystem; #include #include @@ -81,7 +82,7 @@ bool write_obj( s << fmt::format("o body{0:04d}\nusemtl body{0:04d}\n", i); s << V.format(vertices_format) << (F.array() + start_vi).format(faces_format); - for (const size_t& ei : problem.codim_edges_to_edges(i)) { + for (const size_t ei : problem.codim_edges_to_edges(i)) { s << fmt::format( "l {:d} {:d}\n", E(ei, 0) + start_vi, E(ei, 1) + start_vi); } diff --git a/src/main.cpp b/src/main.cpp index 9a9bd0db..362d3fca 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,10 +3,11 @@ #include #include -#include +#include #include -#include // filesystem +#include +namespace fs = std::filesystem; #include #ifdef RIGID_IPC_WITH_OPENGL @@ -58,10 +59,11 @@ int main(int argc, char* argv[]) spdlog::level::level_enum loglevel = spdlog::level::info; app.add_option("--log,--loglevel", loglevel, "log level") ->default_val(loglevel) - ->transform(CLI::CheckedTransformer( - SPDLOG_LEVEL_NAMES_TO_LEVELS, CLI::ignore_case)); + ->transform( + CLI::CheckedTransformer( + SPDLOG_LEVEL_NAMES_TO_LEVELS, CLI::ignore_case)); - int nthreads = tbb::task_scheduler_init::default_num_threads(); + int nthreads = tbb::info::default_concurrency(); app.add_option("--nthreads", nthreads, "maximum number of threads to use") ->default_val(nthreads); @@ -74,13 +76,13 @@ int main(int argc, char* argv[]) set_logger_level(loglevel); if (nthreads <= 0) { - nthreads = tbb::task_scheduler_init::default_num_threads(); + nthreads = tbb::info::default_concurrency(); } - if (nthreads > tbb::task_scheduler_init::default_num_threads()) { + if (nthreads > tbb::info::default_concurrency()) { spdlog::warn( "Attempting to use more threads than available ({:d} > {:d})!", - nthreads, tbb::task_scheduler_init::default_num_threads()); + nthreads, tbb::info::default_concurrency()); } tbb::global_control thread_limiter( @@ -91,20 +93,23 @@ int main(int argc, char* argv[]) UISimState ui; ui.launch(scene_path); #else - exit(app.exit(CLI::Error( - "gui", - "Unable to use GUI mode because OpenGL is disable in CMake!"))); + exit(app.exit( + CLI::Error( + "gui", + "Unable to use GUI mode because OpenGL is disable in CMake!"))); #endif } else { if (scene_path.empty()) { - exit(app.exit(CLI::Error( - "scene_path", "Must provide a scene path in ngui mode!"))); + exit(app.exit( + CLI::Error( + "scene_path", "Must provide a scene path in ngui mode!"))); } if (output_dir.empty()) { - exit(app.exit(CLI::Error( - "output_dir", - "Must provide a output directory in ngui mode!"))); + exit(app.exit( + CLI::Error( + "output_dir", + "Must provide a output directory in ngui mode!"))); } // Create the output directory if it does not exist diff --git a/src/physics/rigid_body_assembler.cpp b/src/physics/rigid_body_assembler.cpp index 58433dbd..12fbe49b 100644 --- a/src/physics/rigid_body_assembler.cpp +++ b/src/physics/rigid_body_assembler.cpp @@ -39,6 +39,7 @@ void RigidBodyAssembler::init(const std::vector& rigid_bodies) m_faces.resize(m_body_face_id.back(), 3); m_faces_to_edges.resize(m_body_face_id.back(), 3); m_codim_edges_to_edges.clear(); + m_codim_vertices_to_vertices.clear(); for (size_t i = 0; i < num_bodies; ++i) { auto& rb = rigid_bodies[i]; if (rb.edges.size() != 0) { @@ -54,6 +55,9 @@ void RigidBodyAssembler::init(const std::vector& rigid_bodies) for (const auto& ei : rb.mesh_selector.codim_edges_to_edges()) { m_codim_edges_to_edges.push_back(ei + m_body_edge_id[i]); } + for (const auto& vi : rb.mesh_selector.codim_vertices_to_vertices()) { + m_codim_vertices_to_vertices.push_back(vi + m_body_vertex_id[i]); + } } // vertex to body map m_vertex_to_body_map.resize(num_vertices()); diff --git a/src/physics/rigid_body_assembler.hpp b/src/physics/rigid_body_assembler.hpp index a2dda51f..0f2f8986 100644 --- a/src/physics/rigid_body_assembler.hpp +++ b/src/physics/rigid_body_assembler.hpp @@ -13,8 +13,8 @@ namespace ipc::rigid { class RigidBodyAssembler { public: - RigidBodyAssembler() {} - ~RigidBodyAssembler() {} + RigidBodyAssembler() { } + ~RigidBodyAssembler() { } /// @brief inits assembler to use this set of rigid-bodies void init(const std::vector& rbs); @@ -195,8 +195,11 @@ class RigidBodyAssembler { /// @brief re-indexed faces to edges of the whole system Eigen::MatrixXi m_faces_to_edges; - /// @brief re-indexed faces to edges of the whole system - std::vector m_codim_edges_to_edges; + /// @brief re-indexed codimg vertices to vertices of the whole system + std::vector m_codim_vertices_to_vertices; + + /// @brief re-indexed codim edges to edges of the whole system + std::vector m_codim_edges_to_edges; /// @brief mass_matrix of the rigid bodies dof DiagonalMatrixXd m_rb_mass_matrix; diff --git a/src/physics/rigid_body_problem.hpp b/src/physics/rigid_body_problem.hpp index c9ba9d30..6b5d32bc 100644 --- a/src/physics/rigid_body_problem.hpp +++ b/src/physics/rigid_body_problem.hpp @@ -50,12 +50,17 @@ class RigidBodyProblem : public virtual SimulationProblem { return m_assembler.world_vertices_t1(); } + const std::vector& codim_vertices_to_vertices() const override + { + return m_assembler.m_codim_vertices_to_vertices; + } + const Eigen::MatrixXi& edges() const override { return m_assembler.m_edges; } - const std::vector& codim_edges_to_edges() const override + const std::vector& codim_edges_to_edges() const override { return m_assembler.m_codim_edges_to_edges; } @@ -75,7 +80,7 @@ class RigidBodyProblem : public virtual SimulationProblem { return m_assembler[i].edges; } - virtual const std::vector& + virtual const std::vector& codim_edges_to_edges(size_t i) const override { return m_assembler[i].mesh_selector.codim_edges_to_edges(); diff --git a/src/physics/simulation_problem.hpp b/src/physics/simulation_problem.hpp index 53b90f15..69cfae56 100644 --- a/src/physics/simulation_problem.hpp +++ b/src/physics/simulation_problem.hpp @@ -57,12 +57,13 @@ class SimulationProblem { virtual size_t num_bodies() const = 0; virtual Eigen::MatrixXd vertices() const = 0; + virtual const std::vector& codim_vertices_to_vertices() const = 0; virtual const Eigen::MatrixXi& edges() const = 0; - virtual const std::vector& codim_edges_to_edges() const = 0; + virtual const std::vector& codim_edges_to_edges() const = 0; virtual const Eigen::MatrixXi& faces() const = 0; virtual Eigen::MatrixXd vertices(size_t i) const { return vertices(); } virtual const Eigen::MatrixXi& edges(size_t i) const { return edges(); } - virtual const std::vector& codim_edges_to_edges(size_t i) const + virtual const std::vector& codim_edges_to_edges(size_t i) const { return codim_edges_to_edges(); } diff --git a/src/profiler.cpp b/src/profiler.cpp index 1ecba203..821d6c17 100644 --- a/src/profiler.cpp +++ b/src/profiler.cpp @@ -1,9 +1,11 @@ -#include // filesystem #include #include #include #include +#include +namespace fs = std::filesystem; + #ifdef RIGID_IPC_PROFILE_FUNCTIONS namespace ipc::rigid { diff --git a/src/solvers/newton_solver.cpp b/src/solvers/newton_solver.cpp index 046cfc37..af860722 100644 --- a/src/solvers/newton_solver.cpp +++ b/src/solvers/newton_solver.cpp @@ -23,7 +23,6 @@ NewtonSolver::NewtonSolver() , is_velocity_conv_tol_abs(false) , is_energy_converged(false) { - linear_solver = polysolve::LinearSolver::create("", ""); } void NewtonSolver::settings(const nlohmann::json& json) @@ -35,18 +34,6 @@ void NewtonSolver::settings(const nlohmann::json& json) is_velocity_conv_tol_abs = json["is_velocity_conv_tol_abs"]; m_line_search_lower_bound = json["line_search_lower_bound"]; - linear_solver_settings = json["linear_solver"]; - try { - linear_solver = - polysolve::LinearSolver::create(linear_solver_settings["name"], ""); - } catch (const std::runtime_error& err) { - spdlog::error("{}! Using Eigen::SimplicialLDLT instead.", err.what()); - linear_solver_settings["name"] = "Eigen::SimplicialLDLT"; - linear_solver = - polysolve::LinearSolver::create(linear_solver_settings["name"], ""); - } - linear_solver->setParameters(linear_solver_settings); - reset_stats(); } @@ -55,7 +42,6 @@ nlohmann::json NewtonSolver::settings() const nlohmann::json settings; settings["max_iterations"] = max_iterations; settings["convergence_criteria"] = convergence_criteria; - settings["linear_solver"] = linear_solver_settings; settings["energy_conv_tol"] = energy_conv_tol; settings["velocity_conv_tol"] = velocity_conv_tol; settings["is_velocity_conv_tol_abs"] = is_velocity_conv_tol_abs; @@ -517,18 +503,16 @@ bool NewtonSolver::compute_direction( // direction = dense_hessian.ldlt().solve(-gradient); // solve_success = true; // } else { - linear_solver->analyzePattern(hessian, hessian.rows()); - linear_solver->factorize(hessian); - nlohmann::json info; - linear_solver->getInfo(info); - // TODO: This check only works for direct Eigen solvers - if (!info.contains("solver_info") || info["solver_info"] == "Success") { + Eigen::SimplicialLDLT> linear_solver; + linear_solver.analyzePattern(hessian); + linear_solver.factorize(hessian); + + if (linear_solver.info() == Eigen::Success) { // TODO: Do we have a better initial guess for iterative // solvers? direction = Eigen::VectorXd::Zero(gradient.size()); - linear_solver->solve(-gradient, direction); - linear_solver->getInfo(info); - if (!info.contains("solver_info") || info["solver_info"] == "Success") { + direction = linear_solver.solve(-gradient); + if (linear_solver.info() == Eigen::Success) { solve_success = true; } else { spdlog::warn( diff --git a/src/solvers/newton_solver.hpp b/src/solvers/newton_solver.hpp index 2e6d11c1..5c109d58 100644 --- a/src/solvers/newton_solver.hpp +++ b/src/solvers/newton_solver.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include @@ -130,10 +129,6 @@ class NewtonSolver : public virtual OptimizationSolver { Eigen::VectorXd grad_direction; ///< Gradient with fixed DoF set to zero Eigen::SparseMatrix hessian, hessian_free; - // Linear solver pointer - std::unique_ptr linear_solver; - nlohmann::json linear_solver_settings; - private: void reset_stats(); diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt index f45fa2a3..8cd6f46b 100644 --- a/src/tools/CMakeLists.txt +++ b/src/tools/CMakeLists.txt @@ -25,19 +25,19 @@ set_target_properties(cli_ccd PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINAR ################################################################################ # Headless Renderer ################################################################################ -add_executable(render_simulation - renderer/main.cpp -) +# add_executable(render_simulation +# renderer/main.cpp +# ) -target_link_libraries(render_simulation PUBLIC ipc::rigid) +# target_link_libraries(render_simulation PUBLIC ipc::rigid) -include(software_renderer) -target_link_libraries(render_simulation PUBLIC software_renderer::software_renderer) +# include(software_renderer) +# target_link_libraries(render_simulation PUBLIC software_renderer::software_renderer) -include(cli11) -target_link_libraries(render_simulation PUBLIC CLI11::CLI11) +# include(cli11) +# target_link_libraries(render_simulation PUBLIC CLI11::CLI11) -set_target_properties(render_simulation PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tools") +# set_target_properties(render_simulation PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tools") ################################################################################ # OBJ Sequence Generator diff --git a/src/tools/generate_bullet_results.cpp b/src/tools/generate_bullet_results.cpp index b1ee3634..254552f7 100644 --- a/src/tools/generate_bullet_results.cpp +++ b/src/tools/generate_bullet_results.cpp @@ -1,10 +1,12 @@ #include -#include // filesystem #include #include #include +#include +namespace fs = std::filesystem; + int generate_bullet_results( const std::string& json_path, const std::string& bullet_result_file_path) { @@ -21,8 +23,8 @@ int generate_bullet_results( int lastSlash = bullet_result_file_path.find_last_of('/'); std::string folderPath = "output/" + bullet_result_file_path.substr( - lastSlash, - bullet_result_file_path.find_last_of('.') - lastSlash) + lastSlash, + bullet_result_file_path.find_last_of('.') - lastSlash) + "/"; fs::create_directories(fs::path(folderPath)); diff --git a/src/tools/obj_sequence.cpp b/src/tools/obj_sequence.cpp index 99765a83..812a2e51 100644 --- a/src/tools/obj_sequence.cpp +++ b/src/tools/obj_sequence.cpp @@ -1,5 +1,7 @@ #include -#include // filesystem + +#include +namespace fs = std::filesystem; #include #include diff --git a/src/tools/renderer/main.cpp b/src/tools/renderer/main.cpp index 9c944633..cb9efa21 100644 --- a/src/tools/renderer/main.cpp +++ b/src/tools/renderer/main.cpp @@ -1,7 +1,10 @@ #include #include -#include // filesystem + +#include +namespace fs = std::filesystem; + #include #include #include @@ -49,22 +52,25 @@ SimRenderArgs parse_args(int argc, char* argv[]) } if (!fs::exists(args.sim_path)) { - exit(app.exit(CLI::Error( - "input does not exist", - fmt::format( - "input path does not exist ({})", args.sim_path.string())))); + exit(app.exit( + CLI::Error( + "input does not exist", + fmt::format( + "input path does not exist ({})", + args.sim_path.string())))); } if (!fs::is_directory(args.sim_path) && args.sim_path.extension().string() != ".json" && args.sim_path.extension().string() != ".JSON") { - exit(app.exit(CLI::Error( - "invalid input", - fmt::format( - "invalid input path ({}) must be a simulation JSON or " - "directory " - "containing an OBJ sequence", - args.sim_path.string())))); + exit(app.exit( + CLI::Error( + "invalid input", + fmt::format( + "invalid input path ({}) must be a simulation JSON or " + "directory " + "containing an OBJ sequence", + args.sim_path.string())))); } return args; @@ -79,7 +85,7 @@ bool read_json(const std::string& filename, nlohmann::json& json) class MeshGenerator { public: - virtual ~MeshGenerator() {}; + virtual ~MeshGenerator() { }; virtual size_t num_meshes() = 0; virtual Eigen::MatrixXd vertices(size_t i) = 0; virtual Eigen::MatrixXi edges(size_t i) = 0; @@ -119,7 +125,7 @@ class OBJSequence : public MeshGenerator { } } - virtual ~OBJSequence() override {}; + virtual ~OBJSequence() override { }; size_t num_meshes() override { return vertices_sequence.size(); } @@ -185,7 +191,7 @@ class RigidBodySequence : public MeshGenerator { m_fps = int(1 / sim["args"]["timestep"].get()); } - virtual ~RigidBodySequence() override {}; + virtual ~RigidBodySequence() override { }; size_t num_meshes() override { return state_sequence.size(); } diff --git a/src/tools/sim_to_gltf.cpp b/src/tools/sim_to_gltf.cpp index be452771..ee4462c0 100644 --- a/src/tools/sim_to_gltf.cpp +++ b/src/tools/sim_to_gltf.cpp @@ -1,5 +1,7 @@ #include -#include // filesystem + +#include +namespace fs = std::filesystem; #include #include @@ -23,7 +25,8 @@ int main(int argc, char* argv[]) app.add_option("--log,--loglevel", loglevel, "log level") ->default_val(loglevel) ->transform( - CLI::CheckedTransformer(SPDLOG_LEVEL_NAMES_TO_LEVELS, CLI::ignore_case)); + CLI::CheckedTransformer( + SPDLOG_LEVEL_NAMES_TO_LEVELS, CLI::ignore_case)); try { app.parse(argc, argv); diff --git a/src/tools/time_ccd.cpp b/src/tools/time_ccd.cpp index 98496dbc..a17e4173 100644 --- a/src/tools/time_ccd.cpp +++ b/src/tools/time_ccd.cpp @@ -1,6 +1,8 @@ #include -#include // filesystem +#include +namespace fs = std::filesystem; + #include #include diff --git a/src/utils/mesh_selector.hpp b/src/utils/mesh_selector.hpp index 53daaf50..bfda657a 100644 --- a/src/utils/mesh_selector.hpp +++ b/src/utils/mesh_selector.hpp @@ -9,7 +9,7 @@ namespace ipc::rigid { class MeshSelector { public: - MeshSelector() {} + MeshSelector() { } MeshSelector( size_t num_vertices, const Eigen::MatrixXi& E, @@ -37,11 +37,16 @@ class MeshSelector { return m_codim_edges_to_edges[ei]; } - const std::vector& codim_edges_to_edges() const + const std::vector& codim_edges_to_edges() const { return m_codim_edges_to_edges; } + const std::vector& codim_vertices_to_vertices() const + { + return m_codim_vertices_to_vertices; + } + size_t num_codim_vertices() const { return m_codim_vertices_to_vertices.size(); @@ -77,9 +82,9 @@ class MeshSelector { /// A mapping from a face to the indices of the face's edges. Eigen::MatrixXi m_faces_to_edges; /// A map from codimensional vertices ids to full vertices ids. - std::vector m_codim_vertices_to_vertices; + std::vector m_codim_vertices_to_vertices; /// A map from codimensional edges ids to full edges ids. - std::vector m_codim_edges_to_edges; + std::vector m_codim_edges_to_edges; }; } // namespace ipc::rigid diff --git a/src/viewer/UIMenu.cpp b/src/viewer/UIMenu.cpp index c5982fa3..b7df442b 100644 --- a/src/viewer/UIMenu.cpp +++ b/src/viewer/UIMenu.cpp @@ -1,184 +1,157 @@ #include "UISimState.hpp" -#include - #include - #include +#include +#include +#include + namespace ipc::rigid { void UISimState::draw_menu() { - // if (m_show_vertex_data) { - // draw_labels_window(); - // } - - float menu_width = 220.f * menu_scaling(); static bool player_menu = true; static bool settings_menu = true; static bool collisions_menu = true; + static bool legends_menu = true; //-------------------------------------------------------------------------- - ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f), ImGuiCond_FirstUseEver); - ImGui::SetNextWindowSize(ImVec2(0.0f, 0.0f), ImGuiCond_FirstUseEver); - ImGui::SetNextWindowSizeConstraints( - ImVec2(menu_width, -1.0f), ImVec2(menu_width, -1.0f)); - bool _viewer_menu_visible = true; - - ImGui::Begin( - "Controls", &_viewer_menu_visible, - ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize); - ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.6f); { - const char* level_strings[] = { "trace", "debug", "info", "warning", - "error", "critical", "off" }; - int log_level = spdlog::get_level(); - if (ImGui::Combo( - "log-level##logger", &log_level, level_strings, - CCD_IM_ARRAYSIZE(level_strings))) { - set_logger_level(static_cast(log_level)); - } - draw_io(); if (m_has_scene) { - if (ImGui::CollapsingHeader( "Player", &player_menu, ImGuiTreeNodeFlags_DefaultOpen)) { draw_simulation_player(); } - if (ImGui::CollapsingHeader( - "Settings", &settings_menu, ImGuiTreeNodeFlags_DefaultOpen)) - draw_settings(); - } - } - ImGui::PopItemWidth(); - ImGui::End(); - - //-------------------------------------------------------------------------- - if (m_has_scene) { - float legends_width = 270.f * menu_scaling(); - ImGui::SetNextWindowPos( - ImVec2(ImGui::GetIO().DisplaySize.x - legends_width, 0.0f), - ImGuiCond_FirstUseEver); - ImGui::SetNextWindowSize(ImVec2(0.0f, 0.0f), ImGuiCond_FirstUseEver); - ImGui::SetNextWindowSizeConstraints( - ImVec2(200.0f, 30.0f), ImVec2(legends_width, -1.0f)); - bool _colors_menu_visible = true; - ImGui::Begin( - "Legend", &_colors_menu_visible, - ImGuiWindowFlags_NoSavedSettings - | ImGuiWindowFlags_AlwaysAutoResize); - { - draw_legends(); + ImGui::SetNextItemOpen(false, ImGuiCond_FirstUseEver); + if (ImGui::CollapsingHeader("Settings", &settings_menu)) { + draw_settings(); + } } - ImGui::End(); } } // namespace ipc::rigid void UISimState::draw_io() { - if (ImGui::Button("Load##IO", ImVec2(-1, 0))) { + ImGui::PushItemWidth(200); + const char* level_strings[] = { "trace", "debug", "info", "warning", + "error", "critical", "off" }; + int log_level = spdlog::get_level(); + if (ImGui::Combo( + "Log level##logger", &log_level, level_strings, + CCD_IM_ARRAYSIZE(level_strings))) { + set_logger_level(static_cast(log_level)); + } + ImGui::PopItemWidth(); + + if (ImGui::Button("Load##IO")) { std::string fname = igl::file_dialog_open(); if (fname != "") { load(fname); } } + if (m_has_scene) { - if (ImGui::Button("Reload##IO", ImVec2(-1, 0))) { + ImGui::SameLine(); + + if (ImGui::Button("Reload##IO")) { reload(); } - ImGui::BeginChild( - "##filename", - ImVec2( - ImGui::GetWindowContentRegionWidth() * 0.9f, - ImGui::GetFontSize() * 3), - false, ImGuiWindowFlags_HorizontalScrollbar); - { - ImGui::Text("%s", m_state.scene_file.c_str()); - ImGui::SetScrollHere(1.0f); - } - ImGui::EndChild(); - if (ImGui::Button("Save simulation", ImVec2(-1, 0))) { + ImGui::SameLine(); + if (ImGui::Button("Save checkpoint")) { std::string fname = igl::file_dialog_save(); save(fname); } - // if (ImGui::Button("Save OBJ sequence", ImVec2(-1, 0))) { - // std::string dir_name = igl::file_dialog_save(); - // if (dir_name != "") { - // save_obj_sequence(dir_name); - // } - // } - - if (ImGui::Button("Save gltf", ImVec2(-1, 0))) { + ImGui::SameLine(); + if (ImGui::Button("Save GLTF")) { std::string filename = igl::file_dialog_save(); if (filename != "") { save_gltf(filename); } } - if (ImGui::Button("Save screenshot", ImVec2(-1, 0))) { - std::string fname = igl::file_dialog_save(); - save_screenshot(fname); - } + ImGui::SameLine(); + if (m_is_gif_recording) { - if (ImGui::Button("End recording", ImVec2(-1, 0))) { + if (ImGui::Button("Save GIF")) { end_recording(); } - } else if (ImGui::Button("Start recording", ImVec2(-1, 0))) { + } else if (ImGui::Button("Record GIF")) { std::string fname = igl::file_dialog_save(); start_recording(fname); } + + ImGui::Text("Scene file:"); + ImGui::BeginChild( + "##filename", ImVec2(-1, 2.5 * ImGui::GetFontSize()), false, + ImGuiWindowFlags_HorizontalScrollbar); + { + ImGui::Text("%s", m_state.scene_file.c_str()); + } + ImGui::EndChild(); } } void UISimState::draw_simulation_player() { - int player_state = static_cast(m_player_state); - - ImGui::RadioButton("play##SimPlayer", &player_state, PlayerState::Playing); - ImGui::RadioButton("pause##SimPlayer", &player_state, PlayerState::Paused); - if (m_player_state == PlayerState::Playing - && player_state == PlayerState::Paused && !replaying) { - log_simulation_time(); - } - if (m_state.m_num_simulation_steps >= m_state.m_max_simulation_steps - && m_player_state == PlayerState::Paused - && player_state == PlayerState::Playing) { - m_state.m_max_simulation_steps = -1; // Turn off breaking + PlayerState player_state = m_player_state; + + if (ImGui::Button( + player_state == PlayerState::Playing ? "Pause" : "Play")) { + player_state = (m_player_state == PlayerState::Playing) + ? PlayerState::Paused + : PlayerState::Playing; + if (player_state == PlayerState::Paused) { + log_simulation_time(); + } + if (player_state == PlayerState::Playing + && m_state.m_num_simulation_steps + >= m_state.m_max_simulation_steps) { + m_state.m_max_simulation_steps = -1; // Turn off breaking + } } m_player_state = static_cast(player_state); - if (ImGui::Button("Step##SimPlayer", ImVec2(-1, 0))) { + ImGui::SameLine(); + if (ImGui::Button("Step##SimPlayer")) { m_player_state = PlayerState::Playing; pre_draw_loop(); m_player_state = PlayerState::Paused; } - // -------------------------------------------------------------------- - ImGui::Checkbox("pause if intersecting", &m_bkp_has_intersections); - ImGui::SameLine(); - ImGui::HelpMarker("yes - stop playing if step has intersections."); - - ImGui::Checkbox("solve collisions", &m_state.m_solve_collisions); - ImGui::SameLine(); - ImGui::HelpMarker("yes - solve collisions automatically on each step."); - - ImGui::Checkbox("pause if optimization failed", &m_bkp_optimization_failed); - ImGui::SameLine(); - ImGui::HelpMarker("yes - stop playing if step optimization failed."); - ImGui::Checkbox("pause on collisions", &m_bkp_had_collision); ImGui::SameLine(); - ImGui::HelpMarker("yes - stop playing if step had a collision."); - // -------------------------------------------------------------------- + ImGui::PushItemWidth(200); if (ImGui::SliderInt( - "step##Replay", &m_state.m_num_simulation_steps, 0, + "Frame##Replay", &m_state.m_num_simulation_steps, 0, m_state.state_sequence.size() - 1)) { - replaying = true; + m_state.problem_ptr->state( + m_state.state_sequence[m_state.m_num_simulation_steps]); + redraw_scene(); + m_scene_changed = true; + } + ImGui::PopItemWidth(); + + // -------------------------------------------------------------------- + + if (ImGui::TreeNode("Breakpoints")) { + ImGui::Checkbox("Intersecting", &m_bkp_has_intersections); + ImGui::SameLine(); + ImGui::HelpMarker("yes - stop playing if step has intersections."); + + ImGui::Checkbox("Optimization failed", &m_bkp_optimization_failed); + ImGui::SameLine(); + ImGui::HelpMarker("yes - stop playing if step optimization failed."); + + ImGui::Checkbox("On collision", &m_bkp_had_collision); + ImGui::SameLine(); + ImGui::HelpMarker("yes - stop playing if step had a collision."); + + ImGui::TreePop(); } } @@ -186,7 +159,7 @@ void UISimState::draw_settings() { auto config = m_state.get_active_config(); ImGui::BeginChild( - "##config", ImVec2(ImGui::GetWindowContentRegionWidth(), 300), false, + "##config", ImVec2(-1, 300), false, ImGuiWindowFlags_HorizontalScrollbar); { ImGui::TreeNodeJson(config); @@ -194,101 +167,4 @@ void UISimState::draw_settings() ImGui::EndChild(); } -void UISimState::draw_legends() -{ - static float second_col = 100; - float slider_width = ImGui::GetWindowWidth() * 0.2f; - for (auto& data : datas_) { - const std::string& label = data.first; - const auto& ptr = data.second; - - /// visibility checkbox - bool tmp = ptr->visibility(); - if (!ptr->is_com()) { - if (ImGui::Checkbox(("##UI-" + label).c_str(), &tmp)) { - ptr->visibility(tmp); - } - /// Color - ImGui::SameLine(); - if (ImGui::DoubleColorEdit3( - (label + "##UI-color").c_str(), ptr->m_color)) { - ptr->recolor(); - } - } else if (ImGui::Checkbox((label + "##UI-check").c_str(), &tmp)) { - ptr->visibility(tmp); - } - - /// other specific attributes - if (ptr->is_mesh()) { - ImGui::SameLine(second_col); - if (ImGui::Checkbox( - ("data##UI-" + label).c_str(), &m_show_vertex_data)) { - } - ptr->show_vertex_data = m_show_vertex_data; - ptr->update_vertex_data(); - - ImGui::SameLine(); - ImGui::PushItemWidth(slider_width); - { - float point_size = ptr->data().point_size / pixel_ratio(); - if (ImGui::SliderFloat( - ("vertices##UI-scaling" + label).c_str(), &point_size, - 0.00f, 10.0f, "%1.f")) { - ptr->data().point_size = point_size * pixel_ratio(); - } - } - ImGui::PopItemWidth(); - - } else if (ptr->is_scalar_field()) { - ImGui::SameLine(); - if (ImGui::Checkbox( - ("log##UI-" + label).c_str(), &ptr->m_use_log_scale)) { - ptr->recolor(); - } - ImGui::SameLine(); - bool show_iso = ptr->data().show_overlay; - if (ImGui::Checkbox(("isolines##UI-" + label).c_str(), &show_iso)) { - ptr->data().show_overlay = show_iso; - } - ImGui::SameLine(); - bool show_faces = ptr->data().show_faces; - if (ImGui::Checkbox(("faces##UI-" + label).c_str(), &show_faces)) { - ptr->data().show_faces = show_faces; - } - - } else if (ptr->is_vector_field()) { - ImGui::SameLine(second_col); - if (ImGui::Checkbox( - ("norm##UI-" + label).c_str(), &ptr->m_normalized)) { - ptr->recolor(); - } - ImGui::SameLine(); - ImGui::PushItemWidth(slider_width); - { - float aux = float(ptr->m_scaling); - if (ImGui::SliderFloat( - ("scale##UI-scaling" + label).c_str(), &aux, 0.00f, - 10.0f, "%1.f")) { - ptr->m_scaling = double(aux); - ptr->recolor(); - } - } - ImGui::PopItemWidth(); - } else if (ptr->is_com()) { - ImGui::SameLine(); - ImGui::PushItemWidth(ImGui::GetWindowWidth() - 150); - { - float aux = float(ptr->m_scaling); - if (ImGui::SliderFloat( - ("scale##UI-com-scaling" + label).c_str(), &aux, 0.00f, - 10.0f, "%1.1f", 1.0f)) { - ptr->m_scaling = double(aux); - ptr->recolor(); - } - } - ImGui::PopItemWidth(); - } - } -} - } // namespace ipc::rigid diff --git a/src/viewer/UISimState.cpp b/src/viewer/UISimState.cpp index 24df792b..1463a494 100644 --- a/src/viewer/UISimState.cpp +++ b/src/viewer/UISimState.cpp @@ -6,110 +6,84 @@ #include #include -namespace ipc::rigid { +#include -UISimState::UISimState() - : m_player_state(PlayerState::Paused) - , m_has_scene(false) - , m_bkp_had_collision(false) - , m_bkp_has_intersections(true) - , m_bkp_optimization_failed(true) - , m_interval_time(0.0) - , m_show_vertex_data(false) - , m_reloading_scene(false) - , m_scene_changed(false) - , m_simulation_time(0) -{ -} +namespace ps = polyscope; + +namespace ipc::rigid { void UISimState::launch(const std::string& inital_scene) { - m_viewer.plugins.push_back(this); - m_viewer.core().set_rotation_type( - igl::opengl::ViewerCore::ROTATION_TYPE_NO_ROTATION); - m_viewer.core().orthographic = true; - m_viewer.core().is_animating = true; - m_viewer.core().lighting_factor = 0.0; - m_viewer.core().animation_max_fps = 120.0; this->inital_scene = inital_scene; - m_viewer.callback_pre_draw = [&](igl::opengl::glfw::Viewer&) { - return pre_draw_loop(); - }; - m_viewer.callback_post_draw = [&](igl::opengl::glfw::Viewer&) { - return post_draw_loop(); - }; - m_viewer.callback_key_pressed = [&](igl::opengl::glfw::Viewer&, - unsigned int unicode_key, - int modifiers) { - return custom_key_pressed(unicode_key, modifiers); + + ps::options::programName = "Rigid IPC"; + ps::options::giveFocusOnShow = true; + + ps::init(); + ps::options::groundPlaneMode = ps::GroundPlaneMode::None; + + this->init(); + + ps::state::userCallback = [this]() { + draw_menu(); + pre_draw_loop(); + post_draw_loop(); }; - m_viewer.launch( - /*resizable=*/true, /*fullscreen=*/false, - /*name=*/"Simulation"); + + ps::show(); } -void UISimState::init(igl::opengl::glfw::Viewer* _viewer) +void UISimState::init() { load(this->inital_scene); } + +bool UISimState::load(std::string scene_filename) { - Super::init(_viewer); - viewer->data().clear(); - - viewer->append_mesh(); - mesh_data = std::make_unique( - _viewer, - Eigen::RowVector3d(0xE7, 0x4C, 0x3C) / 0xFF); // #E74C3C - ALIZARIN RED - mesh_data->visibility(true); - - viewer->append_mesh(); - velocity_data = std::make_unique( - _viewer, - Eigen::RowVector3d(0xF1, 0xC4, 0x0F) / 0xFF); // #F1C40F - SUN FLOWER - velocity_data->visibility(false); - - viewer->append_mesh(); - com_data = std::make_unique(_viewer); - com_data->visibility(true); - - datas_.emplace_back("edges", mesh_data); - datas_.emplace_back("velocity", velocity_data); - datas_.emplace_back("body-frame", com_data); - - load(this->inital_scene); + if (scene_filename != "" && m_state.load_scene(scene_filename)) { + load_scene(); + return true; + } + return false; +} + +void UISimState::reload() +{ + m_reloading_scene = true; + m_state.reload_scene(); + load_scene(); + m_reloading_scene = false; } void UISimState::load_scene() { Eigen::MatrixXd q = m_state.problem_ptr->vertices(); - Eigen::MatrixXd v = - m_state.problem_ptr->velocities() * m_state.problem_ptr->timestep(); + Eigen::MatrixXd v = m_state.problem_ptr->velocities(); - // mesh_data->data().show_vertid = false; - mesh_data->set_mesh( - q, m_state.problem_ptr->edges(), m_state.problem_ptr->faces()); - Eigen::VectorXi vertex_type; + m_mesh_data.set_mesh( + q, m_state.problem_ptr->edges(), m_state.problem_ptr->faces(), + m_state.problem_ptr->codim_edges_to_edges(), + m_state.problem_ptr->codim_vertices_to_vertices()); + m_mesh_data.update_velocities(v); + + Eigen::VectorXi vertex_types; if (m_state.problem_ptr->is_rb_problem()) { const auto& bodies = std::dynamic_pointer_cast(m_state.problem_ptr) ->m_assembler; - vertex_type.resize(bodies.num_vertices()); + vertex_types.resize(bodies.num_vertices()); int start_i = 0; for (const auto& body : bodies.m_rbs) { - vertex_type.segment(start_i, body.vertices.rows()) + vertex_types.segment(start_i, body.vertices.rows()) .setConstant(int(body.type)); start_i += body.vertices.rows(); } } else { - vertex_type = + vertex_types = m_state.problem_ptr->vertex_dof_fixed().rowwise().all().cast(); - vertex_type *= 2; // 0 is static, 1 is kinematic, 2 is dynamic + vertex_types *= 2; // 0 is static, 1 is kinematic, 2 is dynamic } - mesh_data->set_vertex_data( - m_state.problem_ptr->vertex_dof_fixed(), vertex_type); - mesh_data->data().point_size = 0 * pixel_ratio(); - - velocity_data->set_vector_field(q, v); + m_mesh_data.set_vertex_types(vertex_types); if (m_state.problem_ptr->is_rb_problem()) { - com_data->set_coms( + m_com_data.set_coms( std::dynamic_pointer_cast(m_state.problem_ptr) ->m_assembler.rb_poses()); } @@ -117,7 +91,6 @@ void UISimState::load_scene() m_has_scene = true; m_player_state = PlayerState::Paused; m_interval_time = 0.0; - m_simulation_time = 0; // Do not change the view setting upon reload @@ -126,218 +99,134 @@ void UISimState::load_scene() } int dim = q.cols(); - m_viewer.core().trackball_angle.setIdentity(); - m_viewer.core().set_rotation_type( - dim == 2 ? igl::opengl::ViewerCore::ROTATION_TYPE_NO_ROTATION - : igl::opengl::ViewerCore:: - ROTATION_TYPE_TWO_AXIS_VALUATOR_FIXED_UP); - m_viewer.core().orthographic = dim == 2; - m_viewer.core().lighting_factor = 0.0; // dim == 2 ? 0.0 : 1.0; - // mesh_data->data().set_face_based(true); - m_viewer.core().align_camera_center( - q, dim == 2.0 ? mesh_data->mE : mesh_data->mF); + // m_viewer.core().trackball_angle.setIdentity(); + if (dim == 2) { + ps::view::setNavigateStyle(ps::NavigateStyle::Planar); + ps::view::projectionMode = ps::ProjectionMode::Orthographic; + } else { + ps::view::setNavigateStyle(ps::NavigateStyle::Turntable); + ps::view::projectionMode = ps::ProjectionMode::Perspective; + } + // m_viewer.core().align_camera_center( + // q, dim == 2.0 ? mesh_data->mE : mesh_data->mF); // Default colors - // background_color << 0.3f, 0.3f, 0.5f, 1.0f; + // ps::view::bgColor = { { 0.3f, 0.3f, 0.5f, 0.0f } }; // Camera parameters - m_viewer.core().camera_zoom = 1.0f; - m_viewer.core().camera_translation << 0, 0, 0; -} - -void UISimState::redraw_scene() -{ - Eigen::MatrixXd q1 = m_state.problem_ptr->vertices(); - Eigen::MatrixXd v1 = - m_state.problem_ptr->velocities() * m_state.problem_ptr->timestep(); - - mesh_data->update_vertices(q1); - velocity_data->update_vector_field(q1, v1); - if (m_state.problem_ptr->is_rb_problem()) { - const auto& bodies = - std::dynamic_pointer_cast(m_state.problem_ptr) - ->m_assembler; - Eigen::VectorXi vertex_type(bodies.num_vertices()); - int start_i = 0; - for (const auto& body : bodies.m_rbs) { - vertex_type.segment(start_i, body.vertices.rows()) - .setConstant(int(body.type)); - start_i += body.vertices.rows(); - } - mesh_data->set_vertex_data( - m_state.problem_ptr->vertex_dof_fixed(), vertex_type); - } - - if (m_state.problem_ptr->is_rb_problem()) { - com_data->set_coms( - std::dynamic_pointer_cast(m_state.problem_ptr) - ->m_assembler.rb_poses()); - } + // m_viewer.core().camera_zoom = 1.0f; + // m_viewer.core().camera_translation << 0, 0, 0; } -bool UISimState::pre_draw_loop() +void UISimState::pre_draw_loop() { size_t last_save_state = m_state.state_sequence.size() - 1; if (m_state.m_num_simulation_steps > last_save_state) { m_state.m_num_simulation_steps = last_save_state; m_player_state = PlayerState::Paused; - replaying = false; } - if (replaying) { - m_state.problem_ptr->state( - m_state.state_sequence[m_state.m_num_simulation_steps]); - redraw_scene(); - m_scene_changed = true; - if (m_player_state == PlayerState::Playing) { - m_state.m_num_simulation_steps++; - } else if (m_state.m_num_simulation_steps >= last_save_state) { - m_state.m_num_simulation_steps = last_save_state; - m_player_state = PlayerState::Paused; - replaying = false; - } - } else if (m_player_state == PlayerState::Playing) { - simulation_step(); - - bool breakpoint = (m_bkp_had_collision && m_state.m_step_had_collision) - || (m_bkp_has_intersections && m_state.m_step_has_intersections) - || (m_bkp_optimization_failed - && !m_state.problem_ptr->opt_result.success) - || (m_state.m_max_simulation_steps >= 0 - && m_state.m_num_simulation_steps - >= m_state.m_max_simulation_steps); - if (breakpoint) { - m_player_state = PlayerState::Paused; - log_simulation_time(); + + if (m_player_state == PlayerState::Playing) { + if (m_state.m_num_simulation_steps < last_save_state) { + // Replaying the simulation + m_state.problem_ptr->state( + m_state.state_sequence[++m_state.m_num_simulation_steps]); + redraw_scene(); + if (m_state.m_num_simulation_steps >= last_save_state) { + m_state.m_num_simulation_steps = last_save_state; + m_player_state = PlayerState::Paused; + } + } else { + simulation_step(); // calls redraw_scene() + + bool breakpoint = + (m_bkp_had_collision && m_state.m_step_had_collision) + || (m_bkp_has_intersections && m_state.m_step_has_intersections) + || (m_bkp_optimization_failed + && !m_state.problem_ptr->opt_result.success) + || (m_state.m_max_simulation_steps >= 0 + && m_state.m_num_simulation_steps + >= m_state.m_max_simulation_steps); + if (breakpoint) { + m_player_state = PlayerState::Paused; + log_simulation_time(); + } } m_scene_changed = true; } - return false; } -void UISimState::save_screenshot(const std::string& filename) +void UISimState::redraw_scene() { - if (filename == "") { - return; - } - - int width, height; - get_window_dimensions(width, height); - using MatrixXuc = MatrixX; - // Allocate temporary buffers for image - MatrixXuc R(width, height), G(width, height), B(width, height), - A(width, height); - - // Draw the scene in the buffers - // m_viewer.core().draw_buffer(mesh_data->data(), false, R, G, B, A); - // m_viewer.core().draw_buffer(velocity_data->data(), true, R, G, B, A); - // m_viewer.core().draw_buffer(com_data->data(), true, R, G, B, A); - - std::vector data(4 * width * height); - glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data.data()); - // img->flip(); - for (int i = 0; i < width; i++) { - for (int j = 0; j < height; j++) { - R(i, j) = data[4 * (i + j * width) + 0]; - G(i, j) = data[4 * (i + j * width) + 1]; - B(i, j) = data[4 * (i + j * width) + 2]; - A(i, j) = data[4 * (i + j * width) + 3]; - } - } - bool success = igl::png::writePNG(R, G, B, A, filename); + Eigen::MatrixXd q1 = m_state.problem_ptr->vertices(); + Eigen::MatrixXd v1 = m_state.problem_ptr->velocities(); - if (!success) { - spdlog::error("Unable to save screenshot to {}", filename); - } -} + m_mesh_data.update_vertices(q1); + m_mesh_data.update_velocities(v1); -void UISimState::start_recording(const std::string& filename) -{ - if (filename == "") { - return; + if (m_state.problem_ptr->is_rb_problem()) { + m_com_data.update_coms( + std::dynamic_pointer_cast(m_state.problem_ptr) + ->m_assembler.rb_poses()); } - - int width, height; - get_window_dimensions(width, height); - width = static_cast(m_gif_scale * width); - height = static_cast(m_gif_scale * height); - GifBegin(&m_gif_writer, filename.c_str(), width, height, m_gif_delay); - m_scene_changed = true; - // post_draw_loop(); - m_is_gif_recording = true; } -bool UISimState::post_draw_loop() +void UISimState::post_draw_loop() { if (m_scene_changed && m_is_gif_recording) { - int width, height; - get_window_dimensions(width, height); - width = static_cast(m_gif_scale * width); - height = static_cast(m_gif_scale * height); - - using MatrixXuc = MatrixX; - // Allocate temporary buffers for image - MatrixXuc R(width, height), G(width, height), B(width, height), - A(width, height); + int width = ps::view::bufferWidth, height = ps::view::bufferHeight; + width = width >> m_gif_downscale; + height = height >> m_gif_downscale; - // Draw the scene in the buffers - m_viewer.core().draw_buffer(mesh_data->data(), false, R, G, B, A); + const std::vector buffer = ps::screenshotToBuffer( + /*transparentBG=*/true); + // Flip the buffer vertically and downscale it std::vector img(width * height * 4); - for (int rowI = 0; rowI < width; rowI++) { - for (int colI = 0; colI < height; colI++) { - int indStart = (rowI + (height - 1 - colI) * width) * 4; - img[indStart] = R(rowI, colI); - img[indStart + 1] = G(rowI, colI); - img[indStart + 2] = B(rowI, colI); - img[indStart + 3] = A(rowI, colI); + const int delta = 1 << m_gif_downscale; + for (int x = 0; x < ps::view::bufferWidth; x += delta) { + for (int y = 0; y < ps::view::bufferHeight; y += delta) { + // int buffer_i = (y * ps::view::bufferWidth + x) * 4; + int img_i = ((height - 1 - (y >> m_gif_downscale)) * width + + (x >> m_gif_downscale)) + * 4; + for (int d = 0; d < 4; ++d) { + img[img_i + d] = 0; // Initialize to zero + // Average the pixels in the downscaled region + for (int i = 0; i < delta; ++i) { + for (int j = 0; j < delta; ++j) { + int buffer_i = + ((y + i) * ps::view::bufferWidth + (x + j)) * 4; + img[img_i + d] += + buffer[buffer_i + d] / (delta * delta); + } + } + } } } + GifWriteFrame(&m_gif_writer, img.data(), width, height, m_gif_delay); } m_scene_changed = false; - return false; } -void UISimState::end_recording() +void UISimState::start_recording(const std::string& filename) { - GifEnd(&m_gif_writer); - m_is_gif_recording = false; + if (filename == "") { + return; + } + + int width = ps::view::bufferWidth, height = ps::view::bufferHeight; + width = width >> m_gif_downscale; + height = height >> m_gif_downscale; + GifBegin(&m_gif_writer, filename.c_str(), width, height, m_gif_delay); + m_is_gif_recording = true; } -bool UISimState::custom_key_pressed(unsigned int unicode_key, int modifiers) +void UISimState::end_recording() { - if (mesh_data == nullptr) { - return false; - } - - switch (unicode_key) { - case 'F': - case 'f': { - // mesh_data->data().set_face_based(!mesh_data->data().face_based); - return true; - } - case 'L': - case 'l': { - bool toggle = mesh_data->visibility(); - mesh_data->visibility(!toggle); - return true; - } - case 'T': - case 't': { - m_viewer.core().toggle(mesh_data->data().show_faces); - return true; - } - case ' ': { - m_player_state = m_player_state == PlayerState::Playing - ? PlayerState::Paused - : PlayerState::Playing; - log_simulation_time(); - return true; - } - default: - break; // do nothing - } - return false; + GifEnd(&m_gif_writer); + m_is_gif_recording = false; } } // namespace ipc::rigid diff --git a/src/viewer/UISimState.hpp b/src/viewer/UISimState.hpp index ba3a268f..21a4fee0 100644 --- a/src/viewer/UISimState.hpp +++ b/src/viewer/UISimState.hpp @@ -5,12 +5,8 @@ #include #include -#include -#include -#include -#include -#include +#include // WARNING: Use an anonymous namespace when including gif.h to avoid duplicate // symbols. @@ -23,40 +19,25 @@ namespace { namespace ipc::rigid { -class UISimState : public igl::opengl::glfw::imgui::ImGuiMenu { - typedef igl::opengl::glfw::imgui::ImGuiMenu Super; - +class UISimState { public: - UISimState(); - ~UISimState() override {} + UISimState() = default; enum PlayerState { Playing = 0, Paused, TotalPlayerStatus }; - virtual void init(igl::opengl::glfw::Viewer* _viewer) override; - virtual void draw_menu() override; - // virtual bool mouse_down(int button, int modifier) override; - // virtual bool key_pressed(unsigned int key, int modifiers) override; - - std::shared_ptr - get_data(const std::string& data) const; + void init(); + void draw_menu(); void launch(const std::string& inital_scene); void load_scene(); void redraw_scene(); - bool pre_draw_loop(); - bool post_draw_loop(); + void pre_draw_loop(); + void post_draw_loop(); bool custom_key_pressed(unsigned int unicode_key, int modifiers); - bool load(std::string scene_filename) override - { - if (scene_filename != "" && m_state.load_scene(scene_filename)) { - load_scene(); - return true; - } - return false; - } - bool save(std::string scene_filename) override + bool load(std::string scene_filename); + bool save(std::string scene_filename) { return m_state.save_simulation(scene_filename); } @@ -74,23 +55,10 @@ class UISimState : public igl::opengl::glfw::imgui::ImGuiMenu { return m_state.save_gltf(filename); } - void get_window_dimensions(int& width, int& height) const - { - width = m_viewer.core().viewport[2] - m_viewer.core().viewport[0]; - height = m_viewer.core().viewport[3] - m_viewer.core().viewport[1]; - } - - void save_screenshot(const std::string& filename); void start_recording(const std::string& filename); void end_recording(); - void reload() - { - m_reloading_scene = true; - m_state.reload_scene(); - load_scene(); - m_reloading_scene = false; - } + void reload(); void simulation_step() { @@ -108,17 +76,15 @@ class UISimState : public igl::opengl::glfw::imgui::ImGuiMenu { spdlog::info("total_simulation_time={:g}s", m_simulation_time); } - igl::opengl::glfw::Viewer m_viewer; SimState m_state; - PlayerState m_player_state; - bool replaying = false; + PlayerState m_player_state = PlayerState::Paused; - bool m_has_scene; - bool m_bkp_had_collision; - bool m_bkp_has_intersections; - bool m_bkp_optimization_failed; - double m_interval_time; ///< @brief time within the interval - bool m_show_vertex_data; + bool m_has_scene = false; ///< @brief true if a scene is loaded + bool m_bkp_had_collision = false; ///< @brief breakpoint on collisions + bool m_bkp_has_intersections = true; ///< @brief breakpoint on intersections + bool m_bkp_optimization_failed = true; ///< @brief breakpoint on opt failure + double m_interval_time = 0.0; ///< @brief time within the interval + bool m_show_vertex_data = false; ///< @brief show vertex data in the viewer protected: void draw_io(); @@ -127,23 +93,18 @@ class UISimState : public igl::opengl::glfw::imgui::ImGuiMenu { void draw_legends(); private: - std::shared_ptr mesh_data; - std::shared_ptr velocity_data; - std::shared_ptr com_data; - - std::vector< - std::pair>> - datas_; + MeshData m_mesh_data; + CoMData m_com_data; - bool m_reloading_scene; + bool m_reloading_scene = false; ///< @brief is the scene is being reloaded? GifWriter m_gif_writer; uint32_t m_gif_delay = 1; //*10ms - double m_gif_scale = 0.5; + int m_gif_downscale = 1; bool m_is_gif_recording = false; - bool m_scene_changed; + bool m_scene_changed = false; ///< @brief scene changed since last draw - double m_simulation_time; + double m_simulation_time = 0.0; ///< @brief total simulation time std::string inital_scene; }; diff --git a/src/viewer/igl_viewer_ext.cpp b/src/viewer/igl_viewer_ext.cpp deleted file mode 100644 index 81b32f1b..00000000 --- a/src/viewer/igl_viewer_ext.cpp +++ /dev/null @@ -1,396 +0,0 @@ -#include "igl_viewer_ext.hpp" - -#include -#include -#include -#include - -using namespace ipc; - -namespace igl { -namespace opengl { - ViewerDataExt::ViewerDataExt( - igl::opengl::glfw::Viewer* _viewer, const Eigen::RowVector3d& color) - : m_viewer(_viewer) - , m_color(color) - , show_vertex_data(false) - , m_use_log_scale(true) - , m_normalized(false) - , m_scaling(1.0) - { - data_id = m_viewer->data_list.size() - 1; - } - ViewerDataExt::~ViewerDataExt() {} - - Eigen::MatrixXd ViewerDataExt::set_vertices(const Eigen::MatrixXd& V) - { - Eigen::MatrixXd V_temp; - - // If P only has two columns, pad with a column of zeros - if (V.cols() == 2) { - V_temp = Eigen::MatrixXd::Zero(V.rows(), 3); - V_temp.block(0, 0, V.rows(), 2) = V; - } else { - V_temp = V; - } - data().set_vertices(V_temp); - - Eigen::MatrixXd nz = Eigen::MatrixXd::Zero(V.rows(), 3); - nz.col(2).setConstant(1.0); - data().set_normals(nz); - return V_temp; - } - - void ViewerDataExt::set_points( - const Eigen::MatrixXd& V, const Eigen::MatrixXd& color) - { - Eigen::MatrixXd V_temp; - - // If P only has two columns, pad with a column of zeros - if (V.cols() == 2) { - V_temp = Eigen::MatrixXd::Zero(V.rows(), 3); - V_temp.block(0, 0, V.rows(), 2) = V; - } else { - V_temp = V; - } - data().set_points(V_temp, color); - } - void ViewerDataExt::set_edges( - const Eigen::MatrixXd& V, - const Eigen::MatrixXi& E, - const Eigen::MatrixXd& color) - { - Eigen::MatrixXd V_temp; - - // If P only has two columns, pad with a column of zeros - if (V.cols() == 2) { - V_temp = Eigen::MatrixXd::Zero(V.rows(), 3); - V_temp.block(0, 0, V.rows(), 2) = V; - } else { - V_temp = V; - } - data().set_edges(V_temp, E, color); - } - void ViewerDataExt::set_faces( - const Eigen::MatrixXd& V, - const Eigen::MatrixXi& F, - const Eigen::MatrixXd& color) - { - data().V = Eigen::MatrixXd(0, 3); - data().F = Eigen::MatrixXi(0, 3); - data().set_mesh(V, F); - data().set_colors(color); - data().show_lines = false; - } - - /////////////////////////////////////////////////////////////////////////// - /// \brief MeshData::MeshData - /// \param _viewer - //////////////////////////////////////////////////////////////////////// - MeshData::MeshData( - igl::opengl::glfw::Viewer* _viewer, const Eigen::RowVector3d& color) - : ViewerDataExt(_viewer, color) - { - m_edge_color = Eigen::RowVector3d::Zero(); // #000000 - m_static_color = Eigen::RowVector3d(0xB3, 0xB3, 0xB3) / 0xFF; // #B3B3B3 - m_kinematic_color = - Eigen::RowVector3d(0xFF, 0x80, 0x00) / 0xFF; // #FF8000 - } - - void MeshData::set_mesh( - const Eigen::MatrixXd& V, - const Eigen::MatrixXi& E, - const Eigen::MatrixXi& F) - { - data().clear(); - mV = set_vertices(V); - set_points(V, F.size() ? m_edge_color : m_color); - set_edges(V, E, F.size() ? m_edge_color : m_color); - if (F.size()) { - set_faces(V, F, m_color); - } - - mE = E; - mF = F; - } - - void MeshData::update_vertices(const Eigen::MatrixXd& V) - { - mV = set_vertices(V); - recolor(); - data().labels_positions = mV; - } - - void MeshData::recolor() - { - assert(mV.rows() == m_vertex_type.rows()); - - Eigen::MatrixXd vertex_colors(mV.rows(), 3); - Eigen::MatrixXd edge_vertex_colors(mE.rows(), 3); - Eigen::MatrixXd face_vertex_colors(mV.rows(), 3); - - auto type_color = [&](int type) { - return type == 0 ? m_static_color - : (type == 1 ? m_kinematic_color : m_color); - }; - - for (int i = 0; i < mV.rows(); i++) { - vertex_colors.row(i) = - mF.size() ? m_edge_color : type_color(m_vertex_type(i)); - face_vertex_colors.row(i) = type_color(m_vertex_type(i)); - } - - for (int i = 0; i < mE.rows(); i++) { - edge_vertex_colors.row(i) = mF.size() - ? m_edge_color - : type_color(std::min( - m_vertex_type(mE(i, 0)), m_vertex_type(mE(i, 1)))); - } - - set_points(mV, vertex_colors); - if (mE.size()) { - set_edges(mV, mE, edge_vertex_colors); - } - if (mF.size()) { - set_faces(mV, mF, face_vertex_colors); - } - } - - void MeshData::set_vertex_data( - const MatrixXb& vtx_data, const Eigen::VectorXi& vertex_type) - { - assert(vtx_data.rows() == mV.rows()); - - vertex_data_labels.clear(); - vertex_data_labels.resize(size_t(vtx_data.rows())); - for (size_t i = 0; i < vtx_data.rows(); i++) { - std::string data_i = std::to_string(i) + ":"; - for (size_t j = 0; j < vtx_data.cols(); j++) { - if (vtx_data(i, j)) { - data_i += std::to_string(j) + ","; - } - } - vertex_data_labels[i] = data_i.substr(0, data_i.size() - 1); - } - show_vertex_data = true; - data().labels_positions = mV; - data().labels_strings = vertex_data_labels; - - m_vertex_type = vertex_type; - recolor(); - } - - void MeshData::update_vertex_data() - { - if (show_vertex_data) { - data().labels_positions = mV; - data().labels_strings = vertex_data_labels; - } else { - data().labels_positions.resize(0, 0); - } - } - - //////////////////////////////////////////////////////////////////////// - /// \brief VectorFieldData::VectorFieldData - /// \param _viewer - //////////////////////////////////////////////////////////////////////// - VectorFieldData::VectorFieldData( - igl::opengl::glfw::Viewer* _viewer, const Eigen::RowVector3d& color) - : ViewerDataExt(_viewer, color) - { - } - void VectorFieldData::set_vector_field( - const Eigen::MatrixXd& V, const Eigen::MatrixXd& F) - { - Eigen::MatrixXd f = F; - if (f.size() == 0) { - f.resizeLike(V); - f.setZero(); - } - if (m_normalized) { - f.rowwise().normalize(); - } - f *= m_scaling; - - data().lines.resize(0, 9); - data().add_edges(V, V + f, m_color); - mV = V; - mF = F; - } - - void VectorFieldData::update_vector_field( - const Eigen::MatrixXd& V, const Eigen::MatrixXd& F) - { - set_vector_field(V, F); - } - - void VectorFieldData::recolor() - { - if (mF.size() > 0) { - set_vector_field(mV, mF); - } - } - - //////////////////////////////////////////////////////////////////////// - /// \brief CoMData::CoMData - /// \param _viewer - //////////////////////////////////////////////////////////////////////// - CoMData::CoMData(igl::opengl::glfw::Viewer* _viewer) - : VectorFieldData(_viewer, Eigen::RowVector3d::Zero()) - { - } - - void CoMData::set_coms(const ipc::rigid::PosesD& poses) - { - int dim = poses.size() ? poses[0].dim() : 0; - Eigen::MatrixXd com(dim * poses.size(), dim); - Eigen::MatrixXd principle_axes(dim * poses.size(), dim); - - for (int i = 0; i < poses.size(); i++) { - MatrixMax3d R = poses[i].construct_rotation_matrix(); - for (int j = 0; j < dim; j++) { - com.row(dim * i + j) = poses[i].position; - VectorMax3d axis = VectorMax3d::Zero(dim); - axis(j) = 1; - principle_axes.row(dim * i + j) = R * axis; - } - } - - this->set_vector_field(com, principle_axes); - } - - void CoMData::set_vector_field( - const Eigen::MatrixXd& V, const Eigen::MatrixXd& F) - { - Eigen::MatrixXd f = F; - if (f.size() == 0) { - f.resizeLike(V); - f.setZero(); - } - if (m_normalized) { - f.rowwise().normalize(); - } - f *= m_scaling; - - data().lines.resize(0, 9); - - int dim = V.cols(); - Eigen::MatrixXd colors(V.rows(), 3); - for (int i = 0; i < colors.rows(); i += dim) { - colors.row(i + 0) = - Eigen::RowVector3d(0xFF, 0x26, 0x00) / 0xFF; // #FF2600 - colors.row(i + 1) = - Eigen::RowVector3d(0x00, 0xF9, 0x00) / 0xFF; // #00F900 - if (dim == 3) { - colors.row(i + 2) = - Eigen::RowVector3d(0x03, 0x29, 0xD0) / 0xFF; // #0329D0 - } - } - - data().add_edges(V, V + f, colors); - mV = V; - mF = F; - } - - //////////////////////////////////////////////////////////////////////// - /// \brief ScalarFieldData::ScalarFieldData - /// \param _viewer - //////////////////////////////////////////////////////////////////////// - ScalarFieldData::ScalarFieldData( - igl::opengl::glfw::Viewer* _viewer, - const Eigen::RowVector3d& inf_color, - const Eigen::RowVector3d& bg_color) - : ViewerDataExt(_viewer, inf_color) - , m_colormap_type(igl::COLOR_MAP_TYPE_JET) - , m_base_color(bg_color) - , is_visible(false) - { - m_num_isolines = 10; - } - - void ScalarFieldData::set_mesh( - const Eigen::MatrixXd& V, const Eigen::MatrixXi& F) - { - mV = V; - mF = F; - data().clear(); - data().set_mesh(V, F); - data().set_colors(m_base_color); - data().show_lines = false; - data().show_faces = true; - data().show_overlay = true; - } - - void ScalarFieldData::set_vertex_data(const Eigen::MatrixXd& fx) - { - m_vertex_data = fx; - m_vertex_clean_data = inf_to_max(fx); - recolor(); - } - - void ScalarFieldData::recolor() - { - if (m_vertex_data.size() == 0 || !is_visible) { - return; - } - Eigen::VectorXd fx = m_vertex_clean_data; - if (m_use_log_scale) { - fx = fx.array().log(); - } - - draw_mesh(fx); - draw_isolines(fx); - } - - void ScalarFieldData::visibility(const bool show) - { - static bool _show_faces = data().show_faces; - static bool _show_overlay = data().show_overlay; - - is_visible = show; - if (is_visible) { - data().show_faces = _show_faces; - data().show_overlay = _show_overlay; - } else { - _show_faces = data().show_faces; - _show_overlay = data().show_overlay; - data().show_faces = false; - data().show_overlay = false; - } - } - - void ScalarFieldData::draw_isolines(const Eigen::VectorXd& fx) - { - igl::isolines(mV, mF, fx, m_num_isolines, mIsoV, mIsoE); - set_edges(mIsoV, mIsoE, m_color); - } - - void ScalarFieldData::draw_mesh(const Eigen::VectorXd& fx) - { - Eigen::MatrixXd c; - igl::colormap(igl::COLOR_MAP_TYPE_JET, fx, /*normalize=*/true, c); - - for (int i = 0; i < m_vertex_data.rows(); ++i) { - if (std::isinf(m_vertex_data(i))) { - c.row(i) = m_color; - } - } - data().set_colors(c); - } - Eigen::VectorXd ScalarFieldData::inf_to_max(const Eigen::VectorXd& fx) - { - auto inf_to_neg = [](const double x) { - if (std::isinf(x)) { - return -1.0; - } - return x; - }; - - Eigen::VectorXd fx_clean = fx.unaryExpr(inf_to_neg); - double max_coeff = fx_clean.maxCoeff(); - // neg to max_coeff - fx_clean = (fx_clean.array() < 0).select(max_coeff, fx_clean); - return fx_clean; - } -} // namespace opengl -} // namespace igl diff --git a/src/viewer/igl_viewer_ext.hpp b/src/viewer/igl_viewer_ext.hpp deleted file mode 100644 index a17562ea..00000000 --- a/src/viewer/igl_viewer_ext.hpp +++ /dev/null @@ -1,176 +0,0 @@ -#pragma once - -#include -#include -#include - -#include -#include - -using namespace ipc; - -namespace igl { -namespace opengl { - - class ViewerDataExt { - public: - ViewerDataExt( - igl::opengl::glfw::Viewer* _viewer, - const Eigen::RowVector3d& color); - virtual ~ViewerDataExt(); - ViewerData& data() { return m_viewer->data_list[data_id]; } - - virtual void recolor() {} - virtual inline bool is_mesh() { return false; } - virtual inline bool is_scalar_field() { return false; } - virtual inline bool is_vector_field() { return false; } - virtual inline bool is_com() { return false; } - - /// only used in graph data for now - virtual void update_vertex_data() {} - virtual bool visibility() { return data().show_overlay; } - virtual void visibility(const bool show) { data().show_overlay = show; } - - igl::opengl::glfw::Viewer* m_viewer; - size_t data_id; - - Eigen::RowVector3d m_color; - - bool show_vertex_data; - - // for scalar field - bool m_use_log_scale; - int m_num_isolines; - - // for vector field - bool m_normalized; - double m_scaling; - - protected: - Eigen::MatrixXd set_vertices(const Eigen::MatrixXd& V); - - void set_points(const Eigen::MatrixXd& V, const Eigen::MatrixXd& color); - - void set_edges( - const Eigen::MatrixXd& V, - const Eigen::MatrixXi& E, - const Eigen::MatrixXd& color); - - void set_faces( - const Eigen::MatrixXd& V, - const Eigen::MatrixXi& F, - const Eigen::MatrixXd& color); - - void set_vector_field( - const Eigen::MatrixXd& V, - const Eigen::MatrixXd& F, - const Eigen::MatrixXd& color); - }; - - class MeshData : public ViewerDataExt { - public: - MeshData( - igl::opengl::glfw::Viewer* _viewer, - const Eigen::RowVector3d& color); - - void set_mesh( - const Eigen::MatrixXd& V, - const Eigen::MatrixXi& E, - const Eigen::MatrixXi& F); - - void set_vertex_data( - const MatrixXb& data, const Eigen::VectorXi& vertex_type); - void update_vertex_data() override; - void update_vertices(const Eigen::MatrixXd& V); - - virtual bool visibility() override { return data().show_overlay; } - virtual void visibility(const bool show) override - { - data().show_overlay = show; - data().show_lines = show; - } - - void recolor() override; - inline bool is_mesh() override { return true; } - Eigen::MatrixXd mV; - Eigen::MatrixXi mE; - Eigen::MatrixXi mF; - - Eigen::VectorXi m_vertex_type; - - Eigen::RowVector3d m_edge_color; - Eigen::RowVector3d m_static_color; - Eigen::RowVector3d m_kinematic_color; - - std::vector vertex_data_labels; - }; - - class VectorFieldData : public ViewerDataExt { - public: - VectorFieldData( - igl::opengl::glfw::Viewer* _viewer, - const Eigen::RowVector3d& color); - - virtual inline bool is_vector_field() override { return true; } - - void recolor() override; - virtual void - set_vector_field(const Eigen::MatrixXd& V, const Eigen::MatrixXd& F); - - void - update_vector_field(const Eigen::MatrixXd& V, const Eigen::MatrixXd& F); - - Eigen::MatrixXd mF; - Eigen::MatrixXd mV; - }; - - class CoMData : public VectorFieldData { - public: - CoMData(igl::opengl::glfw::Viewer* _viewer); - - inline bool is_vector_field() override { return false; } - inline bool is_com() override { return true; } - - void set_coms(const ipc::rigid::PosesD& poses); - - void set_vector_field( - const Eigen::MatrixXd& V, const Eigen::MatrixXd& F) override; - }; - - class ScalarFieldData : public ViewerDataExt { - public: - ScalarFieldData( - igl::opengl::glfw::Viewer* _viewer, - const Eigen::RowVector3d& inf_color, - const Eigen::RowVector3d& bg_color); - - void recolor() override; - void set_mesh(const Eigen::MatrixXd& V, const Eigen::MatrixXi& F); - - void set_vertex_data(const Eigen::MatrixXd& vertex_data); - - inline bool is_scalar_field() override { return true; } - bool visibility() override { return is_visible; } - void visibility(const bool show) override; - - protected: - void draw_mesh(const Eigen::VectorXd& fx); - void draw_isolines(const Eigen::VectorXd& fx); - Eigen::VectorXd inf_to_max(const Eigen::VectorXd& fx); - - Eigen::MatrixXi mF; - Eigen::MatrixXd mV; - - Eigen::MatrixXd mIsoV; - Eigen::MatrixXi mIsoE; - - Eigen::MatrixXd m_vertex_data; - Eigen::MatrixXd m_vertex_clean_data; - - igl::ColorMapType m_colormap_type; - Eigen::RowVector3d m_base_color; - bool is_visible; - }; - -} // namespace opengl -} // namespace igl diff --git a/src/viewer/imgui_ext.cpp b/src/viewer/imgui_ext.cpp index 4c9f19d6..3fd15a8d 100644 --- a/src/viewer/imgui_ext.cpp +++ b/src/viewer/imgui_ext.cpp @@ -1,75 +1,8 @@ #include "imgui_ext.hpp" -namespace ImGui { - -bool InputIntBounded( - const char* label, - int* val, - int lower_bound, - int upper_bound, - int step, - int step_fast, - ImGuiInputTextFlags flags) -{ - int unbounded_val = *val; - if (ImGui::InputInt(label, &unbounded_val, step, step_fast, flags)) { - if (unbounded_val >= lower_bound && unbounded_val <= upper_bound) { - *val = unbounded_val; - return true; - } - } - return false; -} +#include -bool InputDoubleBounded( - const char* label, - double* val, - double lower_bound, - double upper_bound, - double step, - double step_fast, - const char* format, - ImGuiInputTextFlags flags) -{ - double unbounded_val = *val; - if (ImGui::InputDouble( - label, &unbounded_val, step, step_fast, format, flags)) { - if (unbounded_val >= lower_bound && unbounded_val <= upper_bound) { - *val = unbounded_val; - return true; - } - } - return false; -} - -bool DragDouble( - const char* label, - double* v, - double v_speed, - double v_min, - double v_max, - const char* format, - float power) -{ - - return DragScalar( - label, ImGuiDataType_Double, v, float(v_speed), &v_min, &v_max, format, - power); -} - -bool DoubleColorEdit3(const char* label, Eigen::RowVector3d& color) -{ - Eigen::Vector3f color_f = color.cast(); - bool changed = false; - if (ImGui::ColorEdit3( - label, color_f.data(), - ImGuiColorEditFlags_NoInputs - | ImGuiColorEditFlags_PickerHueWheel)) { - color = color_f.cast(); - changed = true; - } - return changed; -} +namespace ImGui { void HelpMarker(const char* desc) { diff --git a/src/viewer/imgui_ext.hpp b/src/viewer/imgui_ext.hpp index aac5223d..f03690d8 100644 --- a/src/viewer/imgui_ext.hpp +++ b/src/viewer/imgui_ext.hpp @@ -1,43 +1,12 @@ #pragma once -#include -#include -#include #include #define CCD_IM_ARRAYSIZE(_ARR) (int(sizeof(_ARR) / sizeof(*_ARR))) namespace ImGui { -bool InputIntBounded( - const char* label, - int* val, - int lower_bound = std::numeric_limits::min(), - int upper_bound = std::numeric_limits::max(), - int step = 1, - int step_fast = 100, - ImGuiInputTextFlags flags = 0); - -bool InputDoubleBounded( - const char* label, - double* val, - double lower_bound = -std::numeric_limits::infinity(), - double upper_bound = std::numeric_limits::infinity(), - double step = 1, - double step_fast = 100, - const char* format = "%.6f", - ImGuiInputTextFlags flags = 0); - -bool DragDouble( - const char* label, - double* v, - double v_speed = 1.0, - double v_min = 0.0, - double v_max = 0.0, - const char* format = "%.3f", - float power = 1.0f); // If v_min >= v_max we have no bound - void TreeNodeJson(const nlohmann::json json); -bool DoubleColorEdit3(const char* label, Eigen::RowVector3d& color); void HelpMarker(const char* desc); + } // namespace ImGui diff --git a/src/viewer/viewer_data.cpp b/src/viewer/viewer_data.cpp new file mode 100644 index 00000000..30ac854d --- /dev/null +++ b/src/viewer/viewer_data.cpp @@ -0,0 +1,175 @@ +#include "viewer_data.hpp" + +#include +#include +#include +#include + +#include +#include + +#include + +namespace ipc::rigid { + +// ============================================================================= +/// MeshData +// ============================================================================= + +const glm::vec3 MeshData::EDGE_COLOR(0.0, 0.0, 0.0); // #000000 +const glm::vec3 + MeshData::STATIC_COLOR(0xB3 / 255.0, 0xB3 / 255.0, 0xB3 / 255.0); // #B3B3B3 +// const glm::vec3 MeshData::KINEMATIC_COLOR(1.0, 0.5, 0.0); // #FF8000 +const glm::vec3 MeshData::KINEMATIC_COLOR( + 0xE3 / 255.0, 0x82 / 255.0, 0x1C / 255.0); // #E3821C + +void MeshData::set_mesh( + const Eigen::MatrixXd& V, + const Eigen::MatrixXi& E, + const Eigen::MatrixXi& F, + const std::vector& CE2E, + const std::vector& CV2V) +{ + ps_surface_mesh = ps::registerSurfaceMesh("mesh", V, F); + + ps_velocities = ps_surface_mesh->addVertexVectorQuantity( + "velocities", Eigen::MatrixXd::Zero(V.rows(), V.cols())); + ps_velocities->setEnabled(false); + ps_velocities->setVectorColor(velocity_color); + + if (CE2E.size() > 0) { + Eigen::MatrixXi CE; + igl::slice( + E, Eigen::Map(CE2E.data(), CE2E.size()), 1, + CE); + assert(CE.rows() == CE2E.size() && CE.cols() == E.cols()); + + Eigen::MatrixXd CE_V; + Eigen::MatrixXi CE_E; + Eigen::VectorXi CE_I; + igl::remove_unreferenced(V, CE, CE_V, CE_E, CE_I, CE_J); + + if (CE.rows() != 0) { + ps_codim_edges = ps::registerCurveNetwork("edges", CE_V, CE_E); + ps_codim_edges->setRadius(5e-4); + ps_codim_edges->setColor(EDGE_COLOR); + } + } + + if (CV2V.size() > 0) { + CV_J = Eigen::Map(CV2V.data(), CV2V.size()); + + Eigen::MatrixXd CV; + igl::slice(V, CV_J, 1, CV); + + ps_codim_points = ps::registerPointCloud("vertices", CV); + ps_codim_points->setPointRadius(5e-4); + ps_codim_points->setPointColor(EDGE_COLOR); + } +} + +void MeshData::update_vertices(const Eigen::MatrixXd& V) +{ + if (ps_surface_mesh != nullptr) { + ps_surface_mesh->updateVertexPositions(V); + } + if (ps_codim_edges != nullptr) { + Eigen::MatrixXd CE_V; + igl::slice(V, CE_J, 1, CE_V); + ps_codim_edges->updateNodePositions(CE_V); + } + if (ps_codim_points != nullptr) { + Eigen::MatrixXd CV; + igl::slice(V, CV_J, 1, CV); + ps_codim_points->updatePointPositions(CV); + } +} + +void MeshData::update_velocities(const Eigen::MatrixXd& velocities) +{ + if (ps_surface_mesh != nullptr) { + ps_velocities = + ps_surface_mesh->addVertexVectorQuantity("velocities", velocities); + } +} + +void MeshData::set_vertex_types(const Eigen::VectorXi& vertex_types) +{ + if (ps_surface_mesh != nullptr) { + std::vector colors(vertex_types.size()); + for (int i = 0; i < vertex_types.size(); i++) { + if (vertex_types(i) == 0) { + colors[i] = STATIC_COLOR; + } else if (vertex_types(i) == 1) { + colors[i] = KINEMATIC_COLOR; + } else { + colors[i] = mesh_color; // Default color for other types + } + } + ps_surface_mesh->addVertexColorQuantity("type", colors) + ->setEnabled(true); + } +} + +// ============================================================================= +/// CoMData +// ============================================================================= + +void CoMData::set_coms(const ipc::rigid::PosesD& poses) +{ + int dim = poses.size() ? poses[0].dim() : 0; + Eigen::MatrixXd coms(poses.size(), dim); + std::vector principle_axes( + dim, Eigen::MatrixXd(poses.size(), dim)); + + for (int i = 0; i < poses.size(); i++) { + coms.row(i) = poses[i].position; + MatrixMax3d R = poses[i].construct_rotation_matrix(); + for (int j = 0; j < dim; j++) { + VectorMax3d axis = VectorMax3d::Zero(dim); + axis(j) = 1; + principle_axes[j].row(i) = R * axis; + } + } + + ps_coms = ps::registerPointCloud("CoMs", coms); + ps_coms->setPointColor(com_color); + + for (int j = 0; j < dim; j++) { + auto ps_axis = ps_coms->addVectorQuantity( + fmt::format("{}", "xyz"[j]), principle_axes[j]); + ps_axis->setEnabled(true); + ps_axis->setVectorColor( + j == 0 + ? glm::vec3(1.0, 0.14902, 0.0) // #FF2600 + : (j == 1 ? glm::vec3(0.0, 0.97647, 0.0) // #00F900 + : glm::vec3(0.01176, 0.16078, 0.81569))); // #0329D0 + } +} + +void CoMData::update_coms(const ipc::rigid::PosesD& poses) +{ + int dim = poses.size() ? poses[0].dim() : 0; + Eigen::MatrixXd coms(poses.size(), dim); + std::vector principle_axes( + dim, Eigen::MatrixXd(poses.size(), dim)); + + for (int i = 0; i < poses.size(); i++) { + coms.row(i) = poses[i].position; + MatrixMax3d R = poses[i].construct_rotation_matrix(); + for (int j = 0; j < dim; j++) { + VectorMax3d axis = VectorMax3d::Zero(dim); + axis(j) = 1; + principle_axes[j].row(i) = R * axis; + } + } + + ps_coms->updatePointPositions(coms); + + for (int j = 0; j < dim; j++) { + ps_coms->addVectorQuantity( + fmt::format("{}", "xyz"[j]), principle_axes[j]); + } +} + +} // namespace ipc::rigid diff --git a/src/viewer/viewer_data.hpp b/src/viewer/viewer_data.hpp new file mode 100644 index 00000000..84e3e830 --- /dev/null +++ b/src/viewer/viewer_data.hpp @@ -0,0 +1,73 @@ +#pragma once + +#include + +#include + +#include + +namespace polyscope { +class SurfaceMesh; +class PointCloud; +class CurveNetwork; +class SurfaceVertexVectorQuantity; +} // namespace polyscope +namespace ps = polyscope; + +namespace ipc::rigid { + +class MeshData { +public: + MeshData() = default; + + void set_mesh( + const Eigen::MatrixXd& V, + const Eigen::MatrixXi& E, + const Eigen::MatrixXi& F, + const std::vector& CE2E, + const std::vector& CV2V); + + void update_vertices(const Eigen::MatrixXd& V); + void update_velocities(const Eigen::MatrixXd& velocities); + void set_vertex_types(const Eigen::VectorXi& vertex_types); + + glm::vec3 mesh_color = glm::vec3( + 0xE3 / 255.0, 0x1C / 255.0, 0x1C / 255.0); // #E31C1C - ALIZARIN + // 0xE7 / 255.0, 0x4C / 255.0, 0x3C / 255.0); // #E74C3C - ALIZARIN + + glm::vec3 velocity_color = glm::vec3( + 0xF1 / 255.0, 0xC4 / 255.0, 0x0F / 255.0); // #F1C40F - SUN FLOWER + +private: + ps::SurfaceMesh* ps_surface_mesh = nullptr; + ps::CurveNetwork* ps_codim_edges = nullptr; + ps::PointCloud* ps_codim_points = nullptr; + ps::SurfaceVertexVectorQuantity* ps_velocities = nullptr; + + Eigen::VectorXi m_vertex_type; + + Eigen::VectorXi CE_J; + Eigen::VectorXi CV_J; + + static const glm::vec3 EDGE_COLOR; + static const glm::vec3 STATIC_COLOR; + static const glm::vec3 KINEMATIC_COLOR; + + std::vector vertex_data_labels; +}; + +class CoMData { +public: + CoMData() = default; + + void set_coms(const ipc::rigid::PosesD& poses); + void update_coms(const ipc::rigid::PosesD& poses); + + glm::vec3 com_color = glm::vec3( + 0xF1 / 255.0, 0xC4 / 255.0, 0x0F / 255.0); // #F1C40F - SUN FLOWER + +private: + ps::PointCloud* ps_coms = nullptr; +}; + +} // namespace ipc::rigid diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 45a298c2..323bb930 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -67,9 +67,3 @@ target_compile_definitions(rigid_ipc_tests PUBLIC CATCH_CONFIG_ENABLE_BENCHMARKI # Register tests set(PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS ON) catch_discover_tests(rigid_ipc_tests) - -################################################################################ - -add_executable(test_rounding rounding_test.cpp) -target_link_libraries(test_rounding PUBLIC Boost::boost) -target_compile_options(test_rounding PUBLIC -msse2) diff --git a/tests/ccd/test_hash_grid.cpp b/tests/ccd/test_hash_grid.cpp index 4b22c8d6..9628c265 100644 --- a/tests/ccd/test_hash_grid.cpp +++ b/tests/ccd/test_hash_grid.cpp @@ -2,7 +2,9 @@ #include -#include // filesystem +#include +namespace fs = std::filesystem; + #include #include @@ -130,7 +132,7 @@ TEST_CASE("3D hash grid", "[hashgrid][3D]") edges.row(0) << 0, 1; edges.row(1) << 2, 3; - SECTION("Without group ids") {} + SECTION("Without group ids") { } SECTION("With group ids") { group_ids.resize(4); @@ -145,8 +147,9 @@ TEST_CASE("3D hash grid", "[hashgrid][3D]") } SECTION("Complex") { - std::string fname = GENERATE(std::string("cube.obj") - /*, std::string("bunny-lowpoly.obj")*/); + std::string fname = GENERATE( + std::string("cube.obj") + /*, std::string("bunny-lowpoly.obj")*/); fs::path mesh_path = fs::path(__FILE__).parent_path().parent_path().parent_path() @@ -378,7 +381,7 @@ TEST_CASE("3D brute force is duplicate free", "[ccd][brute_force]") edges.row(0) << 0, 1; edges.row(1) << 2, 3; - SECTION("Without group ids") {} + SECTION("Without group ids") { } SECTION("With group ids") { group_ids.resize(4); @@ -389,8 +392,9 @@ TEST_CASE("3D brute force is duplicate free", "[ccd][brute_force]") } SECTION("Complex") { - std::string fname = GENERATE(std::string("cube.obj") - /*, std::string("bunny-lowpoly.obj")*/); + std::string fname = GENERATE( + std::string("cube.obj") + /*, std::string("bunny-lowpoly.obj")*/); fs::path mesh_path = fs::path(__FILE__).parent_path().parent_path().parent_path() diff --git a/tests/ccd/test_rigid_body_hash_grid.cpp b/tests/ccd/test_rigid_body_hash_grid.cpp index b7c2df8e..f30ec4c0 100644 --- a/tests/ccd/test_rigid_body_hash_grid.cpp +++ b/tests/ccd/test_rigid_body_hash_grid.cpp @@ -1,6 +1,8 @@ #include -#include // filesystem +#include +namespace fs = std::filesystem; + #include #include diff --git a/tests/ccd/test_rigid_body_time_of_impact.cpp b/tests/ccd/test_rigid_body_time_of_impact.cpp index fba31e93..5300b07e 100644 --- a/tests/ccd/test_rigid_body_time_of_impact.cpp +++ b/tests/ccd/test_rigid_body_time_of_impact.cpp @@ -1,6 +1,8 @@ #include -#include // filesystem +#include +namespace fs = std::filesystem; + #include #include