diff --git a/CMakeLists.txt b/CMakeLists.txt index ead8e8c9..4cae4f74 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -189,6 +189,14 @@ else() set(BUILD_SHARED_LIBS OFF) endif() +if(BUILD_SHARED_LIBS) + message(WARNING + "You are building Lagrange as a shared library. This is generally discouraged. You are responsible for " + "configuring any Lagrange dependency as a shared library itself. Anything that Lagrange pulls itself is not " + "guaranteed to be compiled as a shared library." + ) +endif() + # Specific override for building on Jenkins if(LAGRANGE_JENKINS) # IPO is very slow and not needed for CI purposes @@ -247,7 +255,7 @@ endif() set(LAGRANGE_IDE_PREFIX ${LAGRANGE_IDE_PREFIX_DEFAULT} CACHE STRING "Folder prefix for Lagrange targets in MSVC/Xcode") # When building python module, compile MKL/TBB as a shared library -if(LAGRANGE_MODULE_PYTHON OR LAGRANGE_ALL) +if(LAGRANGE_MODULE_PYTHON OR LAGRANGE_ALL OR BUILD_SHARED_LIBS) set(MKL_LINKING "dynamic" CACHE STRING "Linking strategy to use with MKL (static, dynamic or sdl)") option(TBB_PREFER_STATIC "Build with static TBB" OFF) endif() diff --git a/VERSION b/VERSION index 00825aa9..7ecad140 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.20.1 +6.21.0 diff --git a/cmake/lagrange/lagrange_add_module.cmake b/cmake/lagrange/lagrange_add_module.cmake index 7493e691..376fb984 100644 --- a/cmake/lagrange/lagrange_add_module.cmake +++ b/cmake/lagrange/lagrange_add_module.cmake @@ -15,7 +15,7 @@ function(lagrange_add_module) get_filename_component(module_name "${module_path}" NAME) # Retrieve options - set(options INTERFACE) + set(options INTERFACE NO_INSTALL) set(oneValueArgs "") set(multiValueArgs "") cmake_parse_arguments(OPTIONS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) @@ -43,10 +43,29 @@ function(lagrange_add_module) # Target sources file(GLOB_RECURSE INC_FILES "include/*.h") - file(GLOB_RECURSE SRC_FILES "src/*.cpp") + file(GLOB_RECURSE SRC_FILES "src/*.cpp" "src/*.h") source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/include/" PREFIX "Header Files" FILES ${INC_FILES}) source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/src/" PREFIX "Source Files" FILES ${SRC_FILES}) - target_sources(lagrange_${module_name} PRIVATE ${INC_FILES} ${SRC_FILES}) + message(STATUS "Module ${module_name} using scope: ${module_scope}") + target_sources(lagrange_${module_name} + PRIVATE + ${SRC_FILES} + PUBLIC + FILE_SET HEADERS + BASE_DIRS + include + FILES + ${INC_FILES} + ) + + # Export headers for shared libraries + if(NOT OPTIONS_INTERFACE) + string(TOUPPER ${module_name} uc_module_name) + get_target_property(module_type lagrange_${module_name} TYPE) + if(module_type STREQUAL "STATIC_LIBRARY") + target_compile_definitions(lagrange_${module_name} PUBLIC "LA_${uc_module_name}_STATIC_DEFINE") + endif() + endif() # Target folder for IDE set_target_properties(lagrange_${module_name} PROPERTIES FOLDER "${LAGRANGE_IDE_PREFIX}Lagrange/Modules") @@ -57,4 +76,9 @@ function(lagrange_add_module) target_code_coverage(lagrange_${module_name} ${module_scope} AUTO ALL EXCLUDE "${FETCHCONTENT_BASE_DIR}/*") endif() + # Create install rules + if(LAGRANGE_INSTALL AND NOT OPTIONS_NO_INSTALL) + lagrange_install(lagrange_${module_name}) + endif() + endfunction() diff --git a/cmake/lagrange/lagrange_add_python_binding.cmake b/cmake/lagrange/lagrange_add_python_binding.cmake index 3e3cc0f8..7578229f 100644 --- a/cmake/lagrange/lagrange_add_python_binding.cmake +++ b/cmake/lagrange/lagrange_add_python_binding.cmake @@ -24,5 +24,19 @@ function(lagrange_add_python_binding) $ $) + # Add scripts if any + if(SKBUILD) + if (EXISTS ${CMAKE_CURRENT_LIST_DIR}/scripts) + MESSAGE(STATUS "Adding scripts for module ${module_name}") + MESSAGE(STATUS ${SKBUILD_SCRIPTS_DIR}) + file(GLOB_RECURSE SCRIPTS ${CMAKE_CURRENT_LIST_DIR}/scripts/*) + install(PROGRAMS ${SCRIPTS} + DESTINATION ${SKBUILD_SCRIPTS_DIR} + COMPONENT Lagrange_Python_Runtime + ) + endif() + endif() + + # Keep track of active modules set_property(TARGET lagrange_python APPEND PROPERTY LAGRANGE_ACTIVE_MODULES ${module_name}) endfunction() diff --git a/cmake/lagrange/lagrange_install.cmake b/cmake/lagrange/lagrange_install.cmake index cb0eec71..b22c5e52 100644 --- a/cmake/lagrange/lagrange_install.cmake +++ b/cmake/lagrange/lagrange_install.cmake @@ -10,6 +10,9 @@ # governing permissions and limitations under the License. # function(lagrange_install target) + string(REPLACE "lagrange_" "" module_name ${target}) + set_target_properties(${target} PROPERTIES EXPORT_NAME ${module_name}) + include(GNUInstallDirs) install(TARGETS ${target} EXPORT Lagrange_Targets @@ -22,13 +25,10 @@ function(lagrange_install target) COMPONENT Lagrange_Runtime PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT Lagrange_Development + FILE_SET HEADERS + COMPONENT Lagrange_Development INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) - install(DIRECTORY include/lagrange - DESTINATION include - COMPONENT Lagrange_Development - ) - # TODO: Maybe set PUBLIC_HEADER target property (for macOS framework installation) endfunction() diff --git a/cmake/lagrange/lagrange_runtime_dependencies.cmake b/cmake/lagrange/lagrange_runtime_dependencies.cmake index 4f5b7cad..d23409b1 100644 --- a/cmake/lagrange/lagrange_runtime_dependencies.cmake +++ b/cmake/lagrange/lagrange_runtime_dependencies.cmake @@ -80,8 +80,8 @@ function(lagrange_prepare_runtime_dependencies target) add_custom_command( TARGET ${target} PRE_LINK - COMMAND ${CMAKE_COMMAND} -E touch "${CMAKE_BINARY_DIR}/runtime_deps/copy_dll_${target}_$.cmake" - COMMAND ${CMAKE_COMMAND} -P "${CMAKE_BINARY_DIR}/runtime_deps/copy_dll_${target}_$.cmake" + COMMAND "${CMAKE_COMMAND}" -E touch "${CMAKE_BINARY_DIR}/runtime_deps/copy_dll_${target}_$.cmake" + COMMAND "${CMAKE_COMMAND}" -P "${CMAKE_BINARY_DIR}/runtime_deps/copy_dll_${target}_$.cmake" COMMENT "Copying dlls for target ${target}" ) endfunction() @@ -117,10 +117,14 @@ function(lagrange_populate_runtime_dependencies target) # Instruction to copy target file if it exists string(APPEND COPY_SCRIPT_CONTENT - "if(EXISTS \"$\")\n " - "execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different " + "if(EXISTS \"$\")\n" + " message(\"Copying dll file: $\")\n" + " execute_process(COMMAND \"${CMAKE_COMMAND}\" -E copy_if_different " "\"$\" " "\"$/$\")\n" + " if(NOT EXISTS \"$/$\")\n" + " message(FATAL_ERROR \"Failed to copy dll file: $\")\n" + " endif()\n" "endif()\n" ) @@ -135,9 +139,9 @@ function(lagrange_populate_runtime_dependencies target) get_filename_component(resourceDir resourceDst DIRECTORY) string(APPEND COPY_SCRIPT_CONTENT "if(EXISTS \"$\")\n" - " execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory " + " execute_process(COMMAND \"${CMAKE_COMMAND}\" -E make_directory " "\"$/${resourceDir}\")\n" - " execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different " + " execute_process(COMMAND \"${CMAKE_COMMAND}\" -E copy_if_different " "\"${resourceSrc}\" " "\"$/${resourceDst}\")\n" "endif()\n" @@ -145,9 +149,9 @@ function(lagrange_populate_runtime_dependencies target) endforeach() string(APPEND COPY_SCRIPT_CONTENT "if(EXISTS \"$\")\n" - " execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory " + " execute_process(COMMAND \"${CMAKE_COMMAND}\" -E make_directory " "\"$/usd\")\n" - " execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different " + " execute_process(COMMAND \"${CMAKE_COMMAND}\" -E copy_if_different " "\"${usd_BINARY_DIR}/usd_plugInfo.json\" " "\"$/usd/plugInfo.json\")\n" "endif()\n" diff --git a/cmake/lagrange/lagrange_warnings.cmake b/cmake/lagrange/lagrange_warnings.cmake index 6f46f07e..ea09a6b4 100644 --- a/cmake/lagrange/lagrange_warnings.cmake +++ b/cmake/lagrange/lagrange_warnings.cmake @@ -19,7 +19,7 @@ add_library(lagrange::warnings ALIAS lagrange_warnings) # installation set_target_properties(lagrange_warnings PROPERTIES EXPORT_NAME warnings) -install(TARGETS lagrange_warnings EXPORT Lagrange_Targets) +install(TARGETS lagrange_warnings EXPORT Lagrange_Targets FILE_SET HEADERS) include(lagrange_filter_flags) diff --git a/cmake/recipes/external/gl3w.cmake b/cmake/recipes/external/gl3w.cmake index 2a17e3d4..49e6be43 100644 --- a/cmake/recipes/external/gl3w.cmake +++ b/cmake/recipes/external/gl3w.cmake @@ -15,12 +15,15 @@ endif() message(STATUS "Third-party (external): creating target 'gl3w::gl3w'") -include(CPM) -CPMAddPackage( - NAME gl3w - GITHUB_REPOSITORY adobe/lagrange-gl3w - GIT_TAG a9e41479e30266cecb72df413f4f6d71b0228a71 -) +block() + set(BUILD_SHARED_LIBS OFF) + include(CPM) + CPMAddPackage( + NAME gl3w + GITHUB_REPOSITORY adobe/lagrange-gl3w + GIT_TAG a9e41479e30266cecb72df413f4f6d71b0228a71 + ) +endblock() add_library(gl3w::gl3w ALIAS gl3w) diff --git a/cmake/recipes/external/imgui.cmake b/cmake/recipes/external/imgui.cmake index 9782762c..6cdf9735 100644 --- a/cmake/recipes/external/imgui.cmake +++ b/cmake/recipes/external/imgui.cmake @@ -30,14 +30,18 @@ endif() message(STATUS "Third-party (external): creating target 'imgui::imgui' ('docking' branch)") -include(CPM) -CPMAddPackage( - NAME imgui - GITHUB_REPOSITORY adobe/imgui - GIT_TAG 447d0a713c635584471bd5bed3ebecdf953c0b5c # docking_v1.89.3 -) +block() + set(BUILD_SHARED_LIBS OFF) + include(CPM) + CPMAddPackage( + NAME imgui + GITHUB_REPOSITORY adobe/imgui + GIT_TAG fbaad5ad4cc881f9139c3e9fa9d5747d285b1940 # docking_v1.90.4 + ) +endblock() target_compile_definitions(imgui PUBLIC IMGUI_DISABLE_OBSOLETE_KEYIO) +target_compile_definitions(imgui PUBLIC IMGUI_DEFINE_MATH_OPERATORS) set_target_properties(imgui PROPERTIES FOLDER third_party) diff --git a/cmake/recipes/external/imgui_fonts.cmake b/cmake/recipes/external/imgui_fonts.cmake index 792f7611..5f96e103 100644 --- a/cmake/recipes/external/imgui_fonts.cmake +++ b/cmake/recipes/external/imgui_fonts.cmake @@ -24,8 +24,11 @@ if(NOT COMMAND fonts_add_font) endif() -fonts_add_font(fontawesome6) -fonts_add_font(source_sans_pro_regular) +block() + set(BUILD_SHARED_LIBS OFF) + fonts_add_font(fontawesome6) + fonts_add_font(source_sans_pro_regular) +endblock() set_target_properties(fonts_fontawesome6 PROPERTIES FOLDER third_party) set_target_properties(fonts_source_sans_pro_regular PROPERTIES FOLDER third_party) diff --git a/cmake/recipes/external/imguizmo.cmake b/cmake/recipes/external/imguizmo.cmake index 6531c844..52ab665a 100644 --- a/cmake/recipes/external/imguizmo.cmake +++ b/cmake/recipes/external/imguizmo.cmake @@ -22,7 +22,7 @@ CPMAddPackage( GIT_TAG e3174578bdc99c715e51c5ad88e7d50b4eeb19b0 ) -add_library(imguizmo +add_library(imguizmo STATIC "${imguizmo_SOURCE_DIR}/ImGuizmo.h" "${imguizmo_SOURCE_DIR}/ImGuizmo.cpp" ) diff --git a/cmake/recipes/external/libfive.cmake b/cmake/recipes/external/libfive.cmake index e256667d..76356dc7 100644 --- a/cmake/recipes/external/libfive.cmake +++ b/cmake/recipes/external/libfive.cmake @@ -141,6 +141,12 @@ if(MSVC) /bigobj ) endif() +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang" OR + "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + target_compile_options(libfive PRIVATE + "-Wno-enum-constexpr-conversion" + ) +endif() set_target_properties(libfive PROPERTIES POSITION_INDEPENDENT_CODE ON) add_library(libfive::libfive ALIAS libfive) set_target_properties(libfive PROPERTIES FOLDER third_party/libfive) diff --git a/cmake/recipes/external/mikktspace.cmake b/cmake/recipes/external/mikktspace.cmake index e7234ec5..3a6deaaa 100644 --- a/cmake/recipes/external/mikktspace.cmake +++ b/cmake/recipes/external/mikktspace.cmake @@ -22,7 +22,7 @@ CPMAddPackage( GIT_TAG b57786fd7aa5404904985829ef8dda445d7feac0 ) -add_library(mikktspace ${mikktspace_SOURCE_DIR}/mikktspace.c) +add_library(mikktspace STATIC ${mikktspace_SOURCE_DIR}/mikktspace.c) add_library(mikktspace::mikktspace ALIAS mikktspace) target_include_directories(mikktspace PUBLIC ${mikktspace_SOURCE_DIR}) diff --git a/cmake/recipes/external/miniz.cmake b/cmake/recipes/external/miniz.cmake index 105626e7..313ad729 100644 --- a/cmake/recipes/external/miniz.cmake +++ b/cmake/recipes/external/miniz.cmake @@ -22,7 +22,7 @@ CPMAddPackage( URL_MD5 bc866f2def5214188cd6481e2694bd3c ) -add_library(miniz ${miniz_SOURCE_DIR}/miniz.c) +add_library(miniz STATIC ${miniz_SOURCE_DIR}/miniz.c) add_library(miniz::miniz ALIAS miniz) include(GNUInstallDirs) diff --git a/cmake/recipes/external/nanoflann.cmake b/cmake/recipes/external/nanoflann.cmake index 42e0a7d9..b57521cc 100644 --- a/cmake/recipes/external/nanoflann.cmake +++ b/cmake/recipes/external/nanoflann.cmake @@ -26,19 +26,17 @@ CPMAddPackage( add_library(nanoflann INTERFACE) add_library(nanoflann::nanoflann ALIAS nanoflann) -include(GNUInstallDirs) -target_include_directories(nanoflann SYSTEM INTERFACE - "$" - "$" -) -target_sources(nanoflann PRIVATE - "${nanoflann_SOURCE_DIR}/include/nanoflann.hpp" +target_sources(nanoflann PUBLIC + FILE_SET HEADERS + BASE_DIRS + "${nanoflann_SOURCE_DIR}/include" + FILES + "${nanoflann_SOURCE_DIR}/include/nanoflann.hpp" ) set_target_properties(nanoflann PROPERTIES FOLDER "third_party") # Install rules set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME nanoflann) -install(FILES ${nanoflann_SOURCE_DIR}/include/nanoflann.hpp DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) -install(TARGETS nanoflann EXPORT Nanoflann_Targets) +install(TARGETS nanoflann EXPORT Nanoflann_Targets FILE_SET HEADERS INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install(EXPORT Nanoflann_Targets DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/nanoflann NAMESPACE nanoflann::) diff --git a/cmake/recipes/external/stb.cmake b/cmake/recipes/external/stb.cmake index 235dcce8..a169c3e5 100644 --- a/cmake/recipes/external/stb.cmake +++ b/cmake/recipes/external/stb.cmake @@ -34,7 +34,7 @@ function(stb_make_target header target_name macro_definition) ) add_library( - ${FULL_TARGET} EXCLUDE_FROM_ALL + ${FULL_TARGET} STATIC EXCLUDE_FROM_ALL ${stb_BINARY_DIR}/stb_${target_name}.cpp ) add_library(${TARGET_ALIAS} ALIAS ${FULL_TARGET}) diff --git a/cmake/recipes/external/tbb.cmake b/cmake/recipes/external/tbb.cmake index f9ca0aa3..64fd9e1e 100644 --- a/cmake/recipes/external/tbb.cmake +++ b/cmake/recipes/external/tbb.cmake @@ -124,26 +124,15 @@ function(tbb_copy_interface_dirs target) endforeach() endfunction() -# Generate a dummy .cpp so we can create a "real" target that depends on both tbb and tbbmalloc -# This is needed to trick embree 3.13.0 on Windows, which tries to access the location of the target TBB::tbb -# https://github.com/embree/embree/blob/12b99393438a4cc9e478e33459eed78bec6233fd/common/tasking/CMakeLists.txt#L42 -file(WRITE "${tbb_BINARY_DIR}/tbb_dummy.cpp.in" [[ - namespace { - void dummy() { } - } -]]) - -configure_file(${tbb_BINARY_DIR}/tbb_dummy.cpp.in ${tbb_BINARY_DIR}/tbb_dummy.cpp COPYONLY) - # Meta-target that brings both tbb and tbbmalloc. -add_library(tbb_tbb ${tbb_BINARY_DIR}/tbb_dummy.cpp) +add_library(tbb_tbb INTERFACE) add_library(TBB::tbb ALIAS tbb_tbb) if(TBB_PREFER_STATIC) - target_link_libraries(tbb_tbb PUBLIC tbb_static tbbmalloc_static) + target_link_libraries(tbb_tbb INTERFACE tbb_static tbbmalloc_static) tbb_fix_include_dirs(tbb_static) tbb_copy_interface_dirs(tbb_tbb tbb_static tbbmalloc_static) else() - target_link_libraries(tbb_tbb PUBLIC tbb tbbmalloc) + target_link_libraries(tbb_tbb INTERFACE tbb tbbmalloc) tbb_fix_include_dirs(tbb) tbb_copy_interface_dirs(tbb_tbb tbb tbbmalloc) endif() diff --git a/cmake/recipes/external/tinyexr.cmake b/cmake/recipes/external/tinyexr.cmake index 9a619fb3..36a2d76f 100644 --- a/cmake/recipes/external/tinyexr.cmake +++ b/cmake/recipes/external/tinyexr.cmake @@ -25,7 +25,7 @@ CPMAddPackage( DOWNLOAD_ONLY ON ) -add_library(tinyexr +add_library(tinyexr STATIC ${tinyexr_SOURCE_DIR}/tinyexr.h ${tinyexr_SOURCE_DIR}/tinyexr.cc ) diff --git a/cmake/recipes/external/tinygltf.cmake b/cmake/recipes/external/tinygltf.cmake index f81d8c08..740f60ec 100644 --- a/cmake/recipes/external/tinygltf.cmake +++ b/cmake/recipes/external/tinygltf.cmake @@ -17,7 +17,7 @@ message(STATUS "Third-party (external): creating target 'tinygltf::tinygltf'") # tinygltf has its own version of json and stb headers in the same folder, # so to avoid potential conflicts we just download the one header we want. -set(TINYGLTF_VERSION "v2.8.3") +set(TINYGLTF_VERSION "v2.8.21") set(TINYGLTF_URL "https://raw.githubusercontent.com/syoyo/tinygltf/${TINYGLTF_VERSION}/tiny_gltf.h") include(CPM) @@ -39,7 +39,7 @@ file(WRITE "${tinygltf_BINARY_DIR}/tinygltf.cpp.in" [[ configure_file(${tinygltf_BINARY_DIR}/tinygltf.cpp.in ${tinygltf_BINARY_DIR}/tinygltf.cpp COPYONLY) # define library -add_library(tinygltf ${tinygltf_BINARY_DIR}/tinygltf.cpp) +add_library(tinygltf STATIC ${tinygltf_BINARY_DIR}/tinygltf.cpp) add_library(tinygltf::tinygltf ALIAS tinygltf) target_compile_definitions(tinygltf PRIVATE TINYGLTF_NO_INCLUDE_JSON diff --git a/cmake/recipes/external/tinyobjloader.cmake b/cmake/recipes/external/tinyobjloader.cmake index 7b14abeb..bb9d6b96 100644 --- a/cmake/recipes/external/tinyobjloader.cmake +++ b/cmake/recipes/external/tinyobjloader.cmake @@ -37,7 +37,7 @@ file(WRITE "${tinyobjloader_BINARY_DIR}/tiny_obj_loader.cpp.in" [[ configure_file(${tinyobjloader_BINARY_DIR}/tiny_obj_loader.cpp.in ${tinyobjloader_BINARY_DIR}/tiny_obj_loader.cpp COPYONLY) # Define tinyobjloader library -add_library(tinyobjloader ${tinyobjloader_BINARY_DIR}/tiny_obj_loader.cpp) +add_library(tinyobjloader STATIC ${tinyobjloader_BINARY_DIR}/tiny_obj_loader.cpp) add_library(tinyobjloader::tinyobjloader ALIAS tinyobjloader) set_target_properties(tinyobjloader PROPERTIES POSITION_INDEPENDENT_CODE ON) diff --git a/cmake/recipes/external/ufbx.cmake b/cmake/recipes/external/ufbx.cmake index 77439269..4a75d5cd 100644 --- a/cmake/recipes/external/ufbx.cmake +++ b/cmake/recipes/external/ufbx.cmake @@ -22,9 +22,9 @@ CPMAddPackage( ) include(GNUInstallDirs) -add_library(ufbx ${ufbx_SOURCE_DIR}/ufbx.c) +add_library(ufbx STATIC ${ufbx_SOURCE_DIR}/ufbx.c) add_library(ufbx::ufbx ALIAS ufbx) -target_include_directories(ufbx PUBLIC +target_include_directories(ufbx PUBLIC $ $ ) diff --git a/modules/bvh/CMakeLists.txt b/modules/bvh/CMakeLists.txt index 787702eb..eed24bec 100644 --- a/modules/bvh/CMakeLists.txt +++ b/modules/bvh/CMakeLists.txt @@ -21,13 +21,7 @@ target_link_libraries(lagrange_bvh INTERFACE igl::core ) -# 3. installation -if(LAGRANGE_INSTALL) - set_target_properties(lagrange_bvh PROPERTIES EXPORT_NAME bvh) - lagrange_install(lagrange_bvh) -endif() - -# 4. unit tests and examples +# 3. unit tests and examples if(LAGRANGE_UNIT_TESTS) add_subdirectory(tests) endif() diff --git a/modules/core/CMakeLists.txt b/modules/core/CMakeLists.txt index 5046d09e..937d8c6d 100644 --- a/modules/core/CMakeLists.txt +++ b/modules/core/CMakeLists.txt @@ -124,13 +124,7 @@ if(LAGRANGE_USE_PCH) ) endif() -# 3. installation -if(LAGRANGE_INSTALL) - set_target_properties(lagrange_core PROPERTIES EXPORT_NAME core) - lagrange_install(lagrange_core) -endif() - -# 4. unit tests and examples +# 3. unit tests and examples if(LAGRANGE_UNIT_TESTS) add_subdirectory(tests) endif() @@ -143,7 +137,7 @@ if(LAGRANGE_EXAMPLES) add_subdirectory(examples) endif() -# 5. python binding +# 4. python binding if(LAGRANGE_MODULE_PYTHON OR LAGRANGE_ALL) add_subdirectory(python) endif() diff --git a/modules/core/include/lagrange/Attribute.h b/modules/core/include/lagrange/Attribute.h index cef8853e..601782fa 100644 --- a/modules/core/include/lagrange/Attribute.h +++ b/modules/core/include/lagrange/Attribute.h @@ -114,7 +114,7 @@ class AttributeBase /// [[nodiscard]] size_t get_num_channels() const { return m_num_channels; } -public: +protected: /// Element type (vertex, facet, indexed, etc.). AttributeElement m_element; @@ -140,6 +140,17 @@ class Attribute : public AttributeBase /// Whether this attribute type is indexed. static constexpr bool IsIndexed = false; + // Static assertions to prevent users from instantiating unsupported types. We do not use the + // macros from AttributeTypes.h since we do not want to implicitly expose them in the main + // Attribute.h header. + static_assert( + std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v, + "Attribute's ValueType template parameter can only be float, double, or a fixed size integer type."); + public: /// /// @name Attribute construction @@ -604,7 +615,6 @@ class Attribute : public AttributeBase lagrange::span ref_row(size_t element); /// @} -protected: protected: /// @cond LA_INTERNAL_DOCS diff --git a/modules/core/include/lagrange/AttributeTypes.h b/modules/core/include/lagrange/AttributeTypes.h index bb8fe6e7..69e75b5a 100644 --- a/modules/core/include/lagrange/AttributeTypes.h +++ b/modules/core/include/lagrange/AttributeTypes.h @@ -15,6 +15,8 @@ #pragma once +#include + #include // clang-format off diff --git a/modules/core/include/lagrange/ExactPredicates.h b/modules/core/include/lagrange/ExactPredicates.h index d2b71d20..9bbe577a 100644 --- a/modules/core/include/lagrange/ExactPredicates.h +++ b/modules/core/include/lagrange/ExactPredicates.h @@ -11,14 +11,15 @@ */ #pragma once +#include +#include + #include #include -#include - namespace lagrange { -class ExactPredicates +class LA_CORE_API ExactPredicates { public: /// diff --git a/modules/core/include/lagrange/ExactPredicatesShewchuk.h b/modules/core/include/lagrange/ExactPredicatesShewchuk.h index 91d4b074..d3752f12 100644 --- a/modules/core/include/lagrange/ExactPredicatesShewchuk.h +++ b/modules/core/include/lagrange/ExactPredicatesShewchuk.h @@ -15,7 +15,7 @@ namespace lagrange { -class ExactPredicatesShewchuk : public ExactPredicates +class LA_CORE_API ExactPredicatesShewchuk : public ExactPredicates { public: ExactPredicatesShewchuk(); diff --git a/modules/core/include/lagrange/Logger.h b/modules/core/include/lagrange/Logger.h index cc920232..641a2dac 100644 --- a/modules/core/include/lagrange/Logger.h +++ b/modules/core/include/lagrange/Logger.h @@ -11,6 +11,8 @@ */ #pragma once +#include + // clang-format off #include #include @@ -50,7 +52,7 @@ namespace lagrange { /// /// @return A const reference to Lagrange's logger object. /// -spdlog::logger& logger(); +LA_CORE_API spdlog::logger& logger(); /// /// Setup a logger object to be used by Lagrange. Calling this function with other Lagrange function @@ -58,13 +60,13 @@ spdlog::logger& logger(); /// /// @param[in] logger New logger object to be used by Lagrange. Ownership is shared with Lagrange. /// -void set_logger(std::shared_ptr logger); +LA_CORE_API void set_logger(std::shared_ptr logger); /// /// Changes the level of logger to something else in a scope. Mostly used in unit tests. Don't use /// in the library itself. /// -class ScopedLogLevel +class LA_CORE_API ScopedLogLevel { public: /// diff --git a/modules/core/include/lagrange/SurfaceMesh.h b/modules/core/include/lagrange/SurfaceMesh.h index df006bd5..a9feb659 100644 --- a/modules/core/include/lagrange/SurfaceMesh.h +++ b/modules/core/include/lagrange/SurfaceMesh.h @@ -19,6 +19,7 @@ #include #include +#include namespace lagrange { @@ -73,6 +74,14 @@ class SurfaceMesh /// Signed index type corresponding to the mesh index type. using SignedIndex = std::make_signed_t; + // Static assertions to prevent users from instantiating unsupported types. + static_assert( + std::is_same_v || std::is_same_v, + "SurfaceMesh's Scalar template parameter can only be float or double."); + static_assert( + std::is_same_v || std::is_same_v, + "SurfaceMesh's Index template parameter can only be uint32_t or uint64_t."); + public: /// /// Callback function to set vertex coordinates. @@ -2112,7 +2121,7 @@ class SurfaceMesh std::array get_edge_vertices(Index e) const; /// - /// Retrieve the edge index cooresponding to (v0, v1). + /// Retrieve the edge index corresponding to (v0, v1). /// /// @param[in] v0, v1 The vertex indices for edge end points. /// diff --git a/modules/core/include/lagrange/SurfaceMeshTypes.h b/modules/core/include/lagrange/SurfaceMeshTypes.h index 726615b4..0d8d2889 100644 --- a/modules/core/include/lagrange/SurfaceMeshTypes.h +++ b/modules/core/include/lagrange/SurfaceMeshTypes.h @@ -15,6 +15,8 @@ #pragma once +#include + #include // clang-format off diff --git a/modules/core/include/lagrange/api.h b/modules/core/include/lagrange/api.h new file mode 100644 index 00000000..82a8dbf0 --- /dev/null +++ b/modules/core/include/lagrange/api.h @@ -0,0 +1,23 @@ +#pragma once + +#ifdef LA_CORE_STATIC_DEFINE + #define LA_CORE_API +#else + #ifndef LA_CORE_API + #ifdef lagrange_core_EXPORTS + // We are building this library + #if defined(_WIN32) || defined(_WIN64) + #define LA_CORE_API __declspec(dllexport) + #else + #define LA_CORE_API __attribute__((visibility("default"))) + #endif + #else + // We are using this library + #if defined(_WIN32) || defined(_WIN64) + #define LA_CORE_API __declspec(dllimport) + #else + #define LA_CORE_API __attribute__((visibility("default"))) + #endif + #endif + #endif +#endif diff --git a/modules/core/include/lagrange/cast_attribute.h b/modules/core/include/lagrange/cast_attribute.h new file mode 100644 index 00000000..7fbd69e1 --- /dev/null +++ b/modules/core/include/lagrange/cast_attribute.h @@ -0,0 +1,98 @@ +/* + * Copyright 2024 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. + */ +#pragma once + +#include + +#include +#include + +namespace lagrange { + +/// +/// @defgroup group-surfacemesh-utils Mesh utilities +/// @ingroup group-surfacemesh +/// +/// Various attribute processing utilities +/// +/// @{ +/// + +/// +/// Cast an attribute in place to a different value type. This effectively replaces the existing +/// attribute with a new one. +/// +/// @param[in,out] mesh Input mesh. Modified to replace the attribute being cast. +/// @param[in] source_id Id of the source attribute to cast. +/// @param[in] target_name Name of the target attribute to be created. +/// +/// @tparam ToValueType Target value type for the attribute. +/// @tparam Scalar Mesh scalar type. +/// @tparam Index Mesh value type. +/// +template +AttributeId cast_attribute( + SurfaceMesh& mesh, + AttributeId source_id, + std::string_view target_name); + +/// +/// Cast an attribute in place to a different value type. This effectively replaces the existing +/// attribute with a new one. +/// +/// @param[in,out] mesh Input mesh. Modified to replace the attribute being cast. +/// @param[in] source_name Name of the source attribute being cast. +/// @param[in] target_name Name of the target attribute to be created. +/// +/// @tparam ToValueType Target value type for the attribute. +/// @tparam Scalar Mesh scalar type. +/// @tparam Index Mesh value type. +/// +template +AttributeId cast_attribute( + SurfaceMesh& mesh, + std::string_view source_name, + std::string_view target_name); + +/// +/// Cast an attribute in place to a different value type. This effectively replaces the existing +/// attribute with a new one. +/// +/// @param[in,out] mesh Input mesh. Modified to replace the attribute being cast. +/// @param[in] attribute_id Id of the attribute to cast. +/// +/// @tparam ToValueType Target value type for the attribute. +/// @tparam Scalar Mesh scalar type. +/// @tparam Index Mesh value type. +/// +template +AttributeId cast_attribute_in_place(SurfaceMesh& mesh, AttributeId attribute_id); + +/// +/// Cast an attribute in place to a different value type. This effectively replaces the existing +/// attribute with a new one. +/// +/// @param[in,out] mesh Input mesh. Modified to replace the attribute being cast. +/// @param[in] name Name of the attribute to cast. +/// +/// @tparam ToValueType Target value type for the attribute. +/// @tparam Scalar Mesh scalar type. +/// @tparam Index Mesh value type. +/// +template +AttributeId cast_attribute_in_place(SurfaceMesh& mesh, std::string_view name); + +/// +/// @} +/// + +} // namespace lagrange diff --git a/modules/core/include/lagrange/compute_dihedral_angles.h b/modules/core/include/lagrange/compute_dihedral_angles.h index 9a2718bb..7e982a68 100644 --- a/modules/core/include/lagrange/compute_dihedral_angles.h +++ b/modules/core/include/lagrange/compute_dihedral_angles.h @@ -15,8 +15,6 @@ #include #endif -#include - #include namespace lagrange { diff --git a/modules/core/include/lagrange/compute_greedy_coloring.h b/modules/core/include/lagrange/compute_greedy_coloring.h new file mode 100644 index 00000000..0d318be2 --- /dev/null +++ b/modules/core/include/lagrange/compute_greedy_coloring.h @@ -0,0 +1,61 @@ +/* + * Copyright 2024 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. + */ +#pragma once + +#include + +namespace lagrange { + +/// +/// @defgroup group-surfacemesh-utils Mesh utilities +/// @ingroup group-surfacemesh +/// +/// Various mesh processing utilities. +/// +/// @{ + +/// +/// Option struct for computing dihedral angles. +/// +struct GreedyColoringOptions +{ + /// Output attribute name. If the attribute already exists, it will be overwritten. + std::string_view output_attribute_name = "@color_id"; + + /// Element type to be colored. Can be either Vertex or Facet. + AttributeElement element_type = AttributeElement::Facet; + + /// Minimum number of colors to use. The algorithm will cycle through them but may use more. + size_t num_color_used = 8; +}; + +/// +/// Compute a greedy graph coloring of the mesh. The selected mesh element type (either Vertex or +/// Facet) will be colored in such a way that no two adjacent element share the same color. +/// +/// @param[in,out] mesh Input mesh to be colored. Modified to compute edge information and the +/// new color attribute. +/// @param[in] options Coloring options. +/// +/// @tparam Scalar Mesh scalar type. +/// @tparam Index Mesh index type. +/// +/// @return Id of the newly computed color attribute. The value type of the created attribute +/// will be the same as the mesh index type. +/// +template +AttributeId compute_greedy_coloring( + SurfaceMesh& mesh, + const GreedyColoringOptions& options = {}); + +/// @} +} // namespace lagrange diff --git a/modules/core/include/lagrange/compute_seam_edges.h b/modules/core/include/lagrange/compute_seam_edges.h new file mode 100644 index 00000000..b6ba515e --- /dev/null +++ b/modules/core/include/lagrange/compute_seam_edges.h @@ -0,0 +1,47 @@ +/* + * Copyright 2024 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. + */ +#include + +namespace lagrange { + +/// +/// Options for computing seam edges. +/// +struct SeamEdgesOptions +{ + /// Output attribute name. + std::string_view output_attribute_name = "@seam_edges"; +}; + +/// +/// Computes the seam edges for a given indexed attribute. A seam edge is an edge where either of +/// its endpoint vertex has corners whose attribute index differ accross the edge. +/// +/// @param[in,out] mesh Input mesh. Modified to add edge information and compute +/// output seam edges. +/// @param[in] indexed_attribute_id Id of the indexed attribute for which to compute seam +/// edges. +/// @param[in] options Optional parameters. +/// +/// @tparam Scalar Mesh scalar type. +/// @tparam Index Mesh index type. +/// +/// @return Id of the newly added per-edge attribute. The newly computed attribute will have +/// value type `uint8_t`, and contain 0 for non-seam edges, and 1 for seam edges. +/// +template +AttributeId compute_seam_edges( + SurfaceMesh& mesh, + AttributeId indexed_attribute_id, + const SeamEdgesOptions& options = {}); + +} // namespace lagrange diff --git a/modules/core/include/lagrange/create_mesh.h b/modules/core/include/lagrange/create_mesh.h index 15ac0863..3be268df 100644 --- a/modules/core/include/lagrange/create_mesh.h +++ b/modules/core/include/lagrange/create_mesh.h @@ -147,11 +147,10 @@ void wrap_with_mesh( "vertices cannot be an rvalue"); } +std::unique_ptr LA_CORE_API create_cube(); -std::unique_ptr create_cube(); +std::unique_ptr LA_CORE_API create_quad(bool with_center_vertex); -std::unique_ptr create_quad(bool with_center_vertex); - -std::unique_ptr create_sphere(double refine_order = 2); +std::unique_ptr LA_CORE_API create_sphere(double refine_order = 2); } // namespace lagrange diff --git a/modules/core/include/lagrange/foreach_attribute.h b/modules/core/include/lagrange/foreach_attribute.h index 00adb035..363363b2 100644 --- a/modules/core/include/lagrange/foreach_attribute.h +++ b/modules/core/include/lagrange/foreach_attribute.h @@ -58,7 +58,7 @@ namespace details { enum class Ordering { Sequential, Parallel }; enum class Access { Write, Read }; -void par_foreach_attribute_id(span ids, function_ref cb); +LA_CORE_API void par_foreach_attribute_id(span ids, function_ref cb); template < std::underlying_type_t mask, diff --git a/modules/core/include/lagrange/internal/attribute_string_utils.h b/modules/core/include/lagrange/internal/attribute_string_utils.h index 3b6f5057..4cd58f83 100644 --- a/modules/core/include/lagrange/internal/attribute_string_utils.h +++ b/modules/core/include/lagrange/internal/attribute_string_utils.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include #include @@ -26,7 +27,7 @@ namespace lagrange::internal { /// /// @return String representation. /// -std::string_view to_string(AttributeElement element); +LA_CORE_API std::string_view to_string(AttributeElement element); /// /// Returns a string representation of an attribute element type. @@ -35,7 +36,7 @@ std::string_view to_string(AttributeElement element); /// /// @return String representation. /// -std::string to_string(BitField element); +LA_CORE_API std::string to_string(BitField element); /// /// Returns a string representation of an attribute usage. @@ -44,7 +45,7 @@ std::string to_string(BitField element); /// /// @return String representation. /// -std::string_view to_string(AttributeUsage usage); +LA_CORE_API std::string_view to_string(AttributeUsage usage); /// /// Returns a string representation of the attribute value type. diff --git a/modules/subdivision/src/visit_attribute.h b/modules/core/include/lagrange/internal/visit_attribute.h similarity index 83% rename from modules/subdivision/src/visit_attribute.h rename to modules/core/include/lagrange/internal/visit_attribute.h index 610c8ce0..6efa3e5d 100644 --- a/modules/subdivision/src/visit_attribute.h +++ b/modules/core/include/lagrange/internal/visit_attribute.h @@ -17,7 +17,7 @@ #include #include -namespace lagrange::subdivision { +namespace lagrange::internal { /// /// Apply a function to a mesh attribute. @@ -30,6 +30,11 @@ namespace lagrange::subdivision { /// @tparam Scalar Mesh scalar type. /// @tparam Index Mesh index type. /// +/// @note To make this a public API function, we probably need (1) a _read a _write variant, +/// (2) a name vs id variant, and (3) maybe a variant for indexed vs non-indexed to +/// avoid having to constexpr our way through all possibilities. Or maybe we just make +/// the function take an `AttributeBase &` as input? +/// template void visit_attribute(const SurfaceMesh& mesh, AttributeId id, Func&& func) { @@ -51,4 +56,4 @@ void visit_attribute(const SurfaceMesh& mesh, AttributeId id, Fun } } -} // namespace lagrange::subdivision +} // namespace lagrange::internal diff --git a/modules/core/include/lagrange/map_attribute.h b/modules/core/include/lagrange/map_attribute.h index 05094c18..a7f0b88c 100644 --- a/modules/core/include/lagrange/map_attribute.h +++ b/modules/core/include/lagrange/map_attribute.h @@ -50,7 +50,7 @@ namespace lagrange { /// @tparam Scalar Mesh scalar type. /// @tparam Index Mesh index type. /// -/// @return The attribute identifier. +/// @return Id of the newly created attribute. /// template AttributeId map_attribute( @@ -72,7 +72,7 @@ AttributeId map_attribute( /// @tparam Scalar Mesh scalar type. /// @tparam Index Mesh index type. /// -/// @return The attribute identifier. +/// @return Id of the newly created attribute. /// template AttributeId map_attribute( @@ -94,7 +94,7 @@ AttributeId map_attribute( /// @tparam Scalar Mesh scalar type. /// @tparam Index Mesh index type. /// -/// @return The attribute identifier. +/// @return Id of the modified attribute. /// template AttributeId map_attribute_in_place( @@ -115,7 +115,7 @@ AttributeId map_attribute_in_place( /// @tparam Scalar Mesh scalar type. /// @tparam Index Mesh index type. /// -/// @return The attribute identifier. +/// @return Id of the modified attribute. /// template AttributeId map_attribute_in_place( diff --git a/modules/core/include/lagrange/utils/Error.h b/modules/core/include/lagrange/utils/Error.h index 8b670b06..c6b16094 100644 --- a/modules/core/include/lagrange/utils/Error.h +++ b/modules/core/include/lagrange/utils/Error.h @@ -11,6 +11,8 @@ */ #pragma once +#include + #include namespace lagrange { @@ -21,7 +23,7 @@ namespace lagrange { /// /// Base exception for errors thrown by Lagrange functions. /// -struct Error : public std::runtime_error +struct LA_CORE_API Error : public std::runtime_error { using runtime_error::runtime_error; @@ -31,7 +33,7 @@ struct Error : public std::runtime_error /// /// An exception of this type is thrown when a lagrange::safe_cast<> fails. /// -struct BadCastError : public Error +struct LA_CORE_API BadCastError : public Error { BadCastError() : Error("bad cast") {} ~BadCastError() override; diff --git a/modules/core/include/lagrange/utils/ProgressCallback.h b/modules/core/include/lagrange/utils/ProgressCallback.h index 25943a44..bbf0f1b7 100644 --- a/modules/core/include/lagrange/utils/ProgressCallback.h +++ b/modules/core/include/lagrange/utils/ProgressCallback.h @@ -11,6 +11,8 @@ */ #pragma once +#include + #include #include #include @@ -21,7 +23,7 @@ namespace lagrange { /// /// A simple thread-safe progress callback. /// -class ProgressCallback +class LA_CORE_API ProgressCallback { public: /// diff --git a/modules/core/include/lagrange/utils/assert.h b/modules/core/include/lagrange/utils/assert.h index c12532de..14cbaa10 100644 --- a/modules/core/include/lagrange/utils/assert.h +++ b/modules/core/include/lagrange/utils/assert.h @@ -11,8 +11,9 @@ */ #pragma once -#include +#include +#include /// @defgroup group-utils Utilites /// @ingroup module-core @@ -67,7 +68,7 @@ namespace lagrange { /// /// @param[in] enabled True to enable breakpoint debugging, false to disable. /// -void set_breakpoint_enabled(bool enabled); +LA_CORE_API void set_breakpoint_enabled(bool enabled); /// /// Returns whether to trigger a debugger breakpoint on assert failure. Use this function in a unit @@ -75,12 +76,12 @@ void set_breakpoint_enabled(bool enabled); /// /// @return True if assert failure triggers a debugger breakpoint, False otherwise. /// -bool is_breakpoint_enabled(); +LA_CORE_API bool is_breakpoint_enabled(); /// /// Call to explicitly trigger a debugger breakpoint. /// -void trigger_breakpoint(); +LA_CORE_API void trigger_breakpoint(); /// /// Called in case of an assertion failure. @@ -95,7 +96,7 @@ void trigger_breakpoint(); /// return boolean type allows it to be called in an expression such as `foo && /// assertion_failed(...)` /// -[[noreturn]] bool assertion_failed( +[[noreturn]] LA_CORE_API bool assertion_failed( const char* function, const char* file, unsigned int line, diff --git a/modules/core/include/lagrange/utils/fpe.h b/modules/core/include/lagrange/utils/fpe.h index 145f70ad..1c5435ce 100644 --- a/modules/core/include/lagrange/utils/fpe.h +++ b/modules/core/include/lagrange/utils/fpe.h @@ -11,6 +11,8 @@ */ #pragma once +#include + namespace lagrange { /// @addtogroup group-utils-misc @@ -19,12 +21,12 @@ namespace lagrange { /// /// Enable floating-point exceptions (useful for debugging).. /// -void enable_fpe(); +LA_CORE_API void enable_fpe(); /// /// Disable previously-enabled fpe.. /// -void disable_fpe(); +LA_CORE_API void disable_fpe(); /// @} diff --git a/modules/core/include/lagrange/utils/strings.h b/modules/core/include/lagrange/utils/strings.h index 92543f6f..a0e571b5 100644 --- a/modules/core/include/lagrange/utils/strings.h +++ b/modules/core/include/lagrange/utils/strings.h @@ -11,6 +11,8 @@ */ #pragma once +#include + // clang-format off #include #include @@ -18,9 +20,10 @@ // clang-format on #include +#include #include #include -#include + namespace lagrange { @@ -37,7 +40,7 @@ namespace lagrange { /// /// @return An array of strings obtained after splitting. /// -std::vector string_split(const std::string& str, char delimiter); +LA_CORE_API std::vector string_split(const std::string& str, char delimiter); /// /// Checks if the string begins with the given prefix. @@ -49,7 +52,7 @@ std::vector string_split(const std::string& str, char delimiter); /// /// @return true if the string begins with the provided prefix, false otherwise. /// -bool starts_with(std::string_view str, std::string_view prefix); +LA_CORE_API bool starts_with(std::string_view str, std::string_view prefix); /// /// Checks if the string ends with the given suffix. @@ -61,7 +64,7 @@ bool starts_with(std::string_view str, std::string_view prefix); /// /// @return true if the string end with the provided suffix, false otherwise. /// -bool ends_with(std::string_view str, std::string_view suffix); +LA_CORE_API bool ends_with(std::string_view str, std::string_view suffix); /// /// Convert a string to lowercase. @@ -72,7 +75,7 @@ bool ends_with(std::string_view str, std::string_view suffix); /// /// @note This method assumes the input string is ASCII. /// -std::string to_lower(std::string str); +LA_CORE_API std::string to_lower(std::string str); /// /// Convert a string to uppercase. @@ -83,7 +86,7 @@ std::string to_lower(std::string str); /// /// @note This method assumes the input string is ASCII. /// -std::string to_upper(std::string str); +LA_CORE_API std::string to_upper(std::string str); /// /// Format args according to the format string fmt, and return the result as a string. diff --git a/modules/core/include/lagrange/utils/tbb.h b/modules/core/include/lagrange/utils/tbb.h index a6ef514b..078bda33 100644 --- a/modules/core/include/lagrange/utils/tbb.h +++ b/modules/core/include/lagrange/utils/tbb.h @@ -11,6 +11,8 @@ */ #pragma once +#include + namespace lagrange { namespace tbb_utils { @@ -21,14 +23,14 @@ namespace tbb_utils { /// /// @return True if the group execution was canceled. /// -bool cancel_group_execution(); +LA_CORE_API bool cancel_group_execution(); /// /// Returns true if the current group execution was canceled. /// /// @return True if canceled, False otherwise. /// -bool is_cancelled(); +LA_CORE_API bool is_cancelled(); } // namespace tbb_utils diff --git a/modules/core/include/lagrange/utils/timing.h b/modules/core/include/lagrange/utils/timing.h index 8201191f..2bfdfe92 100644 --- a/modules/core/include/lagrange/utils/timing.h +++ b/modules/core/include/lagrange/utils/timing.h @@ -11,11 +11,11 @@ */ #pragma once +#include + #include #include -#include - namespace lagrange { using timestamp_type = std::chrono::time_point; diff --git a/modules/core/include/lagrange/utils/triangle_orientation_2d.h b/modules/core/include/lagrange/utils/triangle_orientation_2d.h index afe51e40..1d5cf9b1 100644 --- a/modules/core/include/lagrange/utils/triangle_orientation_2d.h +++ b/modules/core/include/lagrange/utils/triangle_orientation_2d.h @@ -16,7 +16,7 @@ namespace lagrange { -enum class Orientation : short{ +enum class Orientation : short { Positive = 1, Zero = 0, Negative = -1 diff --git a/modules/core/include/lagrange/utils/warnoff.h b/modules/core/include/lagrange/utils/warnoff.h index 1c10e325..84525544 100644 --- a/modules/core/include/lagrange/utils/warnoff.h +++ b/modules/core/include/lagrange/utils/warnoff.h @@ -43,6 +43,7 @@ #pragma clang diagnostic ignored "-Wweak-vtables" #pragma clang diagnostic ignored "-Wunused-private-field" #pragma clang diagnostic ignored "-Wmissing-field-initializers" + #pragma clang diagnostic ignored "-Wconversion" #elif defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-function-type" diff --git a/modules/core/python/scripts/meshstat.py b/modules/core/python/scripts/meshstat.py new file mode 100755 index 00000000..d122a41f --- /dev/null +++ b/modules/core/python/scripts/meshstat.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python +""" Print basic information about a mesh file """ + +import argparse +import lagrange +import colorama +import json +import pathlib +import numpy as np + +colorama.just_fix_windows_console() + + +def print_header(message): + print( + colorama.Fore.YELLOW + + colorama.Style.BRIGHT + + message + + colorama.Style.RESET_ALL + ) + + +def print_green(message): + print(colorama.Fore.GREEN + message + colorama.Style.RESET_ALL) + + +def print_red(message): + print(colorama.Fore.RED + message + colorama.Style.RESET_ALL) + + +def print_section_header(val): + print_green("{:_^55}".format(val)) + + +def print_property(name, val, expected=None): + if expected is not None and val != expected: + print_red("{:-<48}: {}".format(name, val)) + else: + print("{:-<48}: {}".format(name, val)) + + +def print_basic_info(mesh, info): + print_section_header("Basic information") + print("dim: {}".format(mesh.dimension)) + + # Vertex/facet/edge/corner count + num_vertices = mesh.num_vertices + num_facets = mesh.num_facets + num_edges = mesh.num_edges + num_corners = mesh.num_corners + info["num_vertices"] = num_vertices + info["num_facets"] = num_facets + info["num_edges"] = num_edges + info["num_corners"] = num_corners + print( + f"#v: {num_vertices:<10}#f: {num_facets:<10}#e: {num_edges:<10}#c: {num_corners:<10}" + ) + + # Mesh bbox + bbox_min = np.amin(mesh.vertices, axis=0) + bbox_max = np.amax(mesh.vertices, axis=0) + print(f"bbox min: [{bbox_min[0]:>10.3f} {bbox_min[1]:>10.3f} {bbox_min[2]:>10.3f}]") + print(f"bbox max: [{bbox_max[0]:>10.3f} {bbox_max[1]:>10.3f} {bbox_max[2]:>10.3f}]") + + # vertex_per_facet + if mesh.is_regular: + if mesh.vertex_per_facet == 3: + print("facet type: triangles") + elif mesh.vertex_per_facet == 4: + print("facet type: quads") + else: + print(f"facet type: polygons (mesh.vertex_per_facet)") + + info["is_regular"] = True + info["vertex_per_facet"] = mesh.vertex_per_facet + else: + print_red(f"facet type: hybrid") + info["is_regular"] = True + + +def print_extra_info(mesh, info): + # components + num_components = lagrange.compute_components(mesh) + print_property("num_components", num_components) + info["num_components"] = num_components + + # manifold check + edge_manifold = lagrange.is_edge_manifold(mesh) + if edge_manifold: + vertex_manifold = lagrange.is_vertex_manifold(mesh) + else: + vertex_manifold = False + info["edge_manifold"] = edge_manifold + info["vertex_manifold"] = vertex_manifold + info["manifold"] = edge_manifold and vertex_manifold + print_property("manifold", info["manifold"], True) + print_property("vertex manifold", vertex_manifold, True) + print_property("edge manifold", edge_manifold, True) + + # Degeneracy check + num_degenerate_facets = lagrange.detect_degenerate_facets(mesh) + info["degenerate_facets"] = num_degenerate_facets + print_property("num degenerate facets", len(num_degenerate_facets), 0) + + +def usage_to_str(usage): + return str(usage).split(".")[-1] + + +def element_to_str(element): + return str(element).split(".")[-1] + + +def print_attributes(mesh): + for id in mesh.get_matching_attribute_ids(): + name = mesh.get_attribute_name(id) + if name.startswith("@"): + continue + is_indexed = mesh.is_attribute_indexed(id) + if is_indexed: + attr = mesh.indexed_attribute(id) + else: + attr = mesh.attribute(id) + usage = usage_to_str(attr.usage) + element_type = element_to_str(attr.element_type) + num_channels = attr.num_channels + + print(f"Attribute {colorama.Fore.GREEN}{name}{colorama.Style.RESET_ALL}") + print( + f" id:{id:<5}usage: {usage:<10}elem: {element_type:<10}channels: {num_channels}" + ) + + +def load_info(mesh_file): + mesh_file = pathlib.Path(mesh_file) + info_file = mesh_file.with_suffix(".json") + info = {} + if info_file.exists(): + with open(info_file, "r") as fin: + try: + info = json.load(fin) + except ValueError: + print_red("Cannot parse {}, overwriting it".format(info_file)) + return info + + +def save_info(mesh_file, info): + mesh_file = pathlib.Path(mesh_file) + info_file = mesh_file.with_suffix(".json") + + with open(info_file, "w") as fout: + json.dump(info, fout, indent=4, sort_keys=True) + + +def parse_args(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--extended", + "-x", + action="store_true", + help="check for extended information such as number of components, manifoldness and more", + ) + parser.add_argument( + "--attribute", + "-a", + action="store_true", + help="print attribute information", + ) + parser.add_argument( + "--export", "-e", action="store_true", help="export stats into a .info file" + ) + parser.add_argument("input_mesh", help="input mesh file") + return parser.parse_args() + + +def main(): + args = parse_args() + mesh = lagrange.io.load_mesh(args.input_mesh) + mesh.initialize_edges() + info = load_info(args.input_mesh) + + header = "Summary of {}".format(args.input_mesh) + print_header("{:=^55}".format(header)) + print_basic_info(mesh, info) + + if args.extended: + print_extra_info(mesh, info) + if args.attribute: + print_attributes(mesh) + + if args.export: + save_info(args.input_mesh, info) + + +if __name__ == "__main__": + main() diff --git a/modules/core/python/src/bind_attribute.h b/modules/core/python/src/bind_attribute.h index 2b41ebd7..40f3c2bb 100644 --- a/modules/core/python/src/bind_attribute.h +++ b/modules/core/python/src/bind_attribute.h @@ -14,6 +14,7 @@ #include "PyAttribute.h" #include +#include #include #include #include @@ -24,6 +25,7 @@ // clang-format off #include #include +#include #include // clang-format on @@ -214,6 +216,25 @@ void bind_attribute(nanobind::module_& m) self.process(wrap_tensor); }, "Raw data buffer of the attribute."); + attr_class.def_prop_ro( + "dtype", + [](PyAttribute& self) -> std::optional { + auto np = nb::module_::import_("numpy"); + switch (self.ptr()->get_value_type()) { + case AttributeValueType::e_int8_t: return np.attr("int8"); + case AttributeValueType::e_int16_t: return np.attr("int16"); + case AttributeValueType::e_int32_t: return np.attr("int32"); + case AttributeValueType::e_int64_t: return np.attr("int64"); + case AttributeValueType::e_uint8_t: return np.attr("uint8"); + case AttributeValueType::e_uint16_t: return np.attr("uint16"); + case AttributeValueType::e_uint32_t: return np.attr("uint32"); + case AttributeValueType::e_uint64_t: return np.attr("uint64"); + case AttributeValueType::e_float: return np.attr("float32"); + case AttributeValueType::e_double: return np.attr("float64"); + default: logger().warn("Attribute has an unknown dtype."); return std::nullopt; + } + }, + "Value type of the attribute."); } } // namespace lagrange::python diff --git a/modules/core/python/src/bind_surface_mesh.h b/modules/core/python/src/bind_surface_mesh.h index fc2c634a..2e62de36 100644 --- a/modules/core/python/src/bind_surface_mesh.h +++ b/modules/core/python/src/bind_surface_mesh.h @@ -18,6 +18,7 @@ #include #include #include +#include #include // clang-format on @@ -199,33 +200,31 @@ void bind_surface_mesh(nanobind::module_& m) "create_attribute", [](MeshType& self, std::string_view name, - AttributeElement element, - AttributeUsage usage, - std::optional initial_values, - std::optional> initial_indices, + std::optional element, + std::optional usage, + std::variant initial_values, + std::variant, GenericTensor, nb::list> initial_indices, std::optional num_channels, std::optional dtype) { + const bool with_initial_values = initial_values.index() != 0; + const bool with_initial_indices = initial_indices.index() != 0; + auto create_attribute = [&](auto values) { - using ValueType = typename std::decay_t::Scalar; + using ValueType = typename std::decay_t::element_type; span init_values; span init_indices; + std::vector index_storage; Index n = num_channels.value_or(1); - - if (initial_values.has_value()) { - auto [value_data, value_shape, value_stride] = tensor_to_span(values); - la_runtime_assert(is_dense(value_shape, value_stride)); - init_values = value_data; - Index num_cols = - value_shape.size() == 1 ? 1 : static_cast(value_shape[1]); - if (num_channels.has_value()) { - la_runtime_assert( - num_cols == n, - "Number of channels does not match the number of columns in the " - "initial values!"); - } else { - n = num_cols; - } + AttributeElement elem_type; + AttributeUsage usage_type; + + // Extract initial values. + if (with_initial_values) { + init_values = values; + la_debug_assert(num_channels.has_value()); + n = num_channels.value(); + la_debug_assert(values.size() % n == 0); } else { la_runtime_assert( num_channels.has_value(), @@ -234,75 +233,177 @@ void bind_surface_mesh(nanobind::module_& m) dtype.has_value(), "dtype is required when initial values are not provided!"); } - if (initial_indices.has_value()) { - const auto& indices = initial_indices.value(); + + // Extract initial indices. + if (const Tensor* tensor_ptr = + std::get_if>(&initial_indices)) { + la_debug_assert(with_initial_indices); + const auto& indices = *tensor_ptr; auto [index_data, index_shape, index_stride] = tensor_to_span(indices); la_runtime_assert(is_dense(index_shape, index_stride)); init_indices = index_data; + } else if ( + GenericTensor* generic_tensor_ptr = + std::get_if(&initial_indices)) { + la_debug_assert(with_initial_indices); + auto& indices = *generic_tensor_ptr; + index_storage.resize(indices.size()); + +#define LA_X_create_attribute_index(_, IndexType) \ + if (indices.dtype() == nb::dtype()) { \ + auto view = indices.template view>(); \ + std::copy(view.data(), view.data() + indices.size(), index_storage.begin()); \ + } + LA_ATTRIBUTE_INDEX_X(create_attribute_index, 0) +#undef LA_X_create_attribute_index + init_indices = span(index_storage.data(), index_storage.size()); + } else if (const nb::list* list_ptr = std::get_if(&initial_indices)) { + la_debug_assert(with_initial_indices); + const nb::list& py_list = *list_ptr; + auto indices = nb::cast>(py_list); + init_indices = span(indices.begin(), indices.size()); } + + // Extract the proper attribute element. + if (element.has_value()) { + elem_type = element.value(); + } else if (with_initial_indices) { + elem_type = AttributeElement::Indexed; + } else { + // Guess element type based on the shape of initial values. + la_runtime_assert( + with_initial_values, + "Initial values are required to derive the appropriate element type."); + la_debug_assert(!init_values.empty()); + la_debug_assert(init_values.size() % n == 0); + Index num_elements = static_cast(init_values.size()) / n; + if (num_elements == self.get_num_vertices()) { + la_runtime_assert( + num_elements != self.get_num_facets(), + "Cannot infer attribute element due to ambiguity: " + "vertices vs facets"); + la_runtime_assert( + !self.has_edges() || num_elements != self.get_num_edges(), + "Cannot infer attribute element due to ambiguity: " + "vertices vs edges"); + la_runtime_assert( + num_elements != self.get_num_corners(), + "Cannot infer attribute element due to ambiguity: " + "vertices vs corners"); + elem_type = AttributeElement::Vertex; + } else if (num_elements == self.get_num_facets()) { + la_runtime_assert( + !self.has_edges() || num_elements != self.get_num_edges(), + "Cannot infer attribute element due to ambiguity: " + "facets vs edges"); + la_runtime_assert( + num_elements != self.get_num_corners(), + "Cannot infer attribute element due to ambiguity: " + "facets vs corners"); + elem_type = AttributeElement::Facet; + } else if (num_elements == self.get_num_corners()) { + la_runtime_assert( + !self.has_edges() || num_elements != self.get_num_edges(), + "Cannot infer attribute element due to ambiguity: " + "corners vs edges"); + elem_type = AttributeElement::Corner; + } else if (self.has_edges() && num_elements == self.get_num_edges()) { + elem_type = AttributeElement::Edge; + } else { + throw nb::type_error("Cannot infer attribute element type from initial_values!"); + } + } + + // Extract the proper usage. + if (usage.has_value()) { + usage_type = usage.value(); + } else if (n == 1) { + usage_type = AttributeUsage::Scalar; + } else { + usage_type = AttributeUsage::Vector; + } + return self.template create_attribute( name, - element, - usage, + elem_type, + usage_type, n, init_values, init_indices, AttributeCreatePolicy::ErrorIfReserved); }; - if (initial_values.has_value()) { - const auto& values = initial_values.value(); -#define LA_X_create_attribute(_, ValueType) \ - if (values.dtype() == nb::dtype()) { \ - Tensor local_values(values.handle()); \ - return create_attribute(local_values); \ + if (const GenericTensor* tensor_ptr = std::get_if(&initial_values)) { + const auto& values = *tensor_ptr; +#define LA_X_create_attribute(_, ValueType) \ + if (values.dtype() == nb::dtype()) { \ + Tensor local_values(values.handle()); \ + auto [value_data, value_shape, value_stride] = tensor_to_span(local_values); \ + la_runtime_assert(is_dense(value_shape, value_stride)); \ + if (!num_channels.has_value()) { \ + num_channels = value_shape.size() == 1 ? 1 : static_cast(value_shape[1]); \ + } else { \ + Index n = value_shape.size() == 1 ? 1 : static_cast(value_shape[1]); \ + la_runtime_assert( \ + n == num_channels.value(), \ + "Number of channels does not match initial_values"); \ + } \ + return create_attribute(value_data); \ } LA_ATTRIBUTE_X(create_attribute, 0) #undef LA_X_create_attribute + } else if (const nb::list* list_ptr = std::get_if(&initial_values)) { + if (!num_channels.has_value()) { + logger().info( + "Automatically set num_channels to 1 for list-typed initial_values."); + num_channels = 1; + } + auto values = nb::cast>(*list_ptr); + return create_attribute(span(values.data(), values.size())); } else if (dtype.has_value()) { const auto& t = dtype.value(); auto np = nb::module_::import_("numpy"); if (t.is(&PyFloat_Type)) { // Native python float is a C double. - Tensor local_values; + span local_values; return create_attribute(local_values); } else if (t.is(np.attr("float32"))) { - Tensor local_values; + span local_values; return create_attribute(local_values); } else if (t.is(np.attr("float64"))) { - Tensor local_values; + span local_values; return create_attribute(local_values); } else if (t.is(np.attr("int8"))) { - Tensor local_values; + span local_values; return create_attribute(local_values); } else if (t.is(np.attr("int16"))) { - Tensor local_values; + span local_values; return create_attribute(local_values); } else if (t.is(np.attr("int32"))) { - Tensor local_values; + span local_values; return create_attribute(local_values); } else if (t.is(np.attr("int64"))) { - Tensor local_values; + span local_values; return create_attribute(local_values); } else if (t.is(np.attr("uint8"))) { - Tensor local_values; + span local_values; return create_attribute(local_values); } else if (t.is(np.attr("uint16"))) { - Tensor local_values; + span local_values; return create_attribute(local_values); } else if (t.is(np.attr("uint32"))) { - Tensor local_values; + span local_values; return create_attribute(local_values); } else if (t.is(np.attr("uint64"))) { - Tensor local_values; + span local_values; return create_attribute(local_values); } } throw nb::type_error("`initial_values` and `dtype` cannot both be None!"); }, "name"_a, - "element"_a, - "usage"_a /*= AttributeUsage::Scalar*/, + "element"_a = nb::none(), + "usage"_a = nb::none(), "initial_values"_a = nb::none(), "initial_indices"_a = nb::none(), "num_channels"_a = nb::none(), @@ -311,10 +412,10 @@ void bind_surface_mesh(nanobind::module_& m) :param name: Name of the attribute. :type name: str -:param element: Element type of the attribute. -:type element: AttributeElement -:param usage: Usage type of the attribute. -:type usage: AttributeUsage +:param element: Element type of the attribute. If None, derive from the shape of initial values. +:type element: AttributeElement, optional +:param usage: Usage type of the attribute. If None, derive from the shape of initial values or the number of channels. +:type usage: AttributeUsage, optional :param initial_values: Initial values of the attribute. :type initial_values: numpy.ndarray, optional :param initial_indices: Initial indices of the attribute (Indexed attribute only). @@ -324,6 +425,14 @@ void bind_surface_mesh(nanobind::module_& m) :param dtype: Data type of the attribute. :type dtype: valid numpy.dtype, optional +.. note:: + If `element` is None, it will be derived based on the cardinality of the mesh elements. + If there is an ambiguity, an exception will be raised. + In addition, explicit `element` specification is required for value attributes. + +.. note:: + If `usage` is None, it will be derived based on the shape of `initial_values` or `num_channels` if specified. + :returns: The id of the created attribute. )"); diff --git a/modules/core/python/src/bind_utilities.h b/modules/core/python/src/bind_utilities.h index 8dc13c20..aeda3712 100644 --- a/modules/core/python/src/bind_utilities.h +++ b/modules/core/python/src/bind_utilities.h @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -21,7 +22,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -106,7 +109,7 @@ void bind_utilities(nanobind::module_& m) .def_rw( "keep_weighted_corner_normals", &VertexNormalOptions::keep_weighted_corner_normals, - "Whether to keep the weighted corner normal attirbute. Default is false."); + "Whether to keep the weighted corner normal attribute. Default is false."); m.def( "compute_vertex_normal", @@ -154,7 +157,7 @@ void bind_utilities(nanobind::module_& m) :param weight_type: Weighting type for normal computation. :param weighted_corner_normal_attribute_name: Precomputed weighted corner normals attribute name. :param recompute_weighted_corner_normals: Whether to recompute weighted corner normals. -:param keep_weighted_corner_normals: Whether to keep the weighted corner normal attirbute. +:param keep_weighted_corner_normals: Whether to keep the weighted corner normal attribute. :returns: Vertex normal attribute id.)"); @@ -170,7 +173,7 @@ void bind_utilities(nanobind::module_& m) &compute_facet_normal, "mesh"_a, "options"_a = FacetNormalOptions(), - R"(Computer facet normal. + R"(Compute facet normal. :param mesh: Input mesh. :param options: Options for computing facet normals. @@ -186,7 +189,7 @@ void bind_utilities(nanobind::module_& m) }, "mesh"_a, "output_attribute_name"_a = nb::none(), - R"(Computer facet normal (Pythonic API). + R"(Compute facet normal (Pythonic API). :param mesh: Input mesh. :param output_attribute_name: Output attribute name. @@ -214,7 +217,7 @@ void bind_utilities(nanobind::module_& m) .def_rw( "keep_facet_normals", &NormalOptions::keep_facet_normals, - "Whether to keep the computed facet normal attirbute. Default is false."); + "Whether to keep the computed facet normal attribute. Default is false."); m.def( "compute_normal", @@ -258,7 +261,7 @@ Vertices listed in `cone_vertices` are considered as cone vertices, which is alw :param cone_vertices: cone vertices :type cone_vertices: list[int] or numpy.ndarray, optional :param options: normal options -:type optionas: NormalOptions, optional +:type optional: NormalOptions, optional :returns: the id of the indexed normal attribute. )"); @@ -320,11 +323,36 @@ Vertices listed in `cone_vertices` are considered as cone vertices, which is alw :type facet_normal_attribute_name: str, optional :param recompute_facet_normals: whether to recompute facet normals :type recompute_facet_normals: bool, optional -:param keep_facet_normals: whether to keep the computed facet normal attirbute +:param keep_facet_normals: whether to keep the computed facet normal attribute :type keep_facet_normals: bool, optional :returns: the id of the indexed normal attribute.)"); + m.def( + "compute_greedy_coloring", + [](MeshType& mesh, + AttributeElement element_type, + size_t num_color_used, + std::optional output_attribute_name) { + GreedyColoringOptions options; + options.element_type = element_type; + options.num_color_used = num_color_used; + if (output_attribute_name) options.output_attribute_name = *output_attribute_name; + return compute_greedy_coloring(mesh, options); + }, + "mesh"_a, + "element_type"_a = AttributeElement::Facet, + "num_color_used"_a = 8, + "output_attribute_name"_a = nb::none(), + R"(Compute greedy coloring of mesh elements. + +:param mesh: Input mesh. +:param element_type: Element type to be colored. Can be either Vertex or Facet. +:param num_color_used: Minimum number of colors to use. The algorithm will cycle through them but may use more. +:param output_attribute_name: Output attribute name. + +:returns: Color attribute id.)"); + m.def( "normalize_mesh", &normalize_mesh, @@ -363,6 +391,26 @@ Vertices listed in `cone_vertices` are considered as cone vertices, which is alw :returns: the combined mesh)"); + m.def( + "compute_seam_edges", + [](MeshType& mesh, + AttributeId indexed_attribute_id, + std::optional output_attribute_name) { + SeamEdgesOptions options; + if (output_attribute_name) options.output_attribute_name = *output_attribute_name; + return compute_seam_edges(mesh, indexed_attribute_id, options); + }, + "mesh"_a, + "indexed_attribute_id"_a, + "output_attribute_name"_a = nb::none(), + R"(Computer seam edges for a given indexed attribute. + +:param mesh: Input mesh. +:param indexed_attribute_id: Input indexed attribute id. +:param output_attribute_name: Output attribute name. + +:returns: Attribute id for the output per-edge seam attribute (1 is a seam, 0 is not).)"); + m.def( "unify_index_buffer", [](MeshType& mesh) { return unify_index_buffer(mesh); }, @@ -477,7 +525,7 @@ argument. Each component id is in [0, num_components-1] range. :returns: The vertex valence attribute id.)"); m.def( - "compute_vertex_valance", + "compute_vertex_valence", [](MeshType& mesh, std::optional output_attribute_name) { VertexValenceOptions opt; if (output_attribute_name.has_value()) { @@ -1291,6 +1339,99 @@ A mesh considered as manifold if it is both vertex and edge manifold. :param excluded_attributes: List of attribute names or ids to exclude. By default, no attribute is excluded. :param included_usages: List of attribute usages to include. By default, all usages are included. :param included_element_types: List of attribute element types to include. By default, all element types are included.)"); + + m.def( + "cast_attribute", + [](MeshType& mesh, + std::variant input_attribute, + nb::type_object dtype, + std::optional output_attribute_name) { + /// Helper lambda function to cast an attribute to a new type + auto cast = [&](AttributeId attr_id) { + auto np = nb::module_::import_("numpy"); + if (output_attribute_name.has_value()) { + auto name = output_attribute_name.value(); + if (dtype.is(&PyFloat_Type)) { + // Native python float is a C double. + return cast_attribute(mesh, attr_id, name); + } else if (dtype.is(&PyLong_Type)) { + // Native python int maps to int64. + return cast_attribute(mesh, attr_id, name); + } else if (dtype.is(np.attr("float32"))) { + return cast_attribute(mesh, attr_id, name); + } else if (dtype.is(np.attr("float64"))) { + return cast_attribute(mesh, attr_id, name); + } else if (dtype.is(np.attr("int8"))) { + return cast_attribute(mesh, attr_id, name); + } else if (dtype.is(np.attr("int16"))) { + return cast_attribute(mesh, attr_id, name); + } else if (dtype.is(np.attr("int32"))) { + return cast_attribute(mesh, attr_id, name); + } else if (dtype.is(np.attr("int64"))) { + return cast_attribute(mesh, attr_id, name); + } else if (dtype.is(np.attr("uint8"))) { + return cast_attribute(mesh, attr_id, name); + } else if (dtype.is(np.attr("uint16"))) { + return cast_attribute(mesh, attr_id, name); + } else if (dtype.is(np.attr("uint32"))) { + return cast_attribute(mesh, attr_id, name); + } else if (dtype.is(np.attr("uint64"))) { + return cast_attribute(mesh, attr_id, name); + } else { + throw nb::type_error("Unsupported `dtype`!"); + } + } else { + if (dtype.is(&PyFloat_Type)) { + // Native python float is a C double. + return cast_attribute_in_place(mesh, attr_id); + } else if (dtype.is(&PyLong_Type)) { + // Native python int maps to int64. + return cast_attribute_in_place(mesh, attr_id); + } else if (dtype.is(np.attr("float32"))) { + return cast_attribute_in_place(mesh, attr_id); + } else if (dtype.is(np.attr("float64"))) { + return cast_attribute_in_place(mesh, attr_id); + } else if (dtype.is(np.attr("int8"))) { + return cast_attribute_in_place(mesh, attr_id); + } else if (dtype.is(np.attr("int16"))) { + return cast_attribute_in_place(mesh, attr_id); + } else if (dtype.is(np.attr("int32"))) { + return cast_attribute_in_place(mesh, attr_id); + } else if (dtype.is(np.attr("int64"))) { + return cast_attribute_in_place(mesh, attr_id); + } else if (dtype.is(np.attr("uint8"))) { + return cast_attribute_in_place(mesh, attr_id); + } else if (dtype.is(np.attr("uint16"))) { + return cast_attribute_in_place(mesh, attr_id); + } else if (dtype.is(np.attr("uint32"))) { + return cast_attribute_in_place(mesh, attr_id); + } else if (dtype.is(np.attr("uint64"))) { + return cast_attribute_in_place(mesh, attr_id); + } else { + throw nb::type_error("Unsupported `dtype`!"); + } + } + }; + + if (std::holds_alternative(input_attribute)) { + return cast(std::get(input_attribute)); + } else { + AttributeId id = mesh.get_attribute_id(std::get(input_attribute)); + return cast(id); + } + }, + "mesh"_a, + "input_attribute"_a, + "dtype"_a, + "output_attribute_name"_a = nb::none(), + R"(Cast an attribute to a new dtype. + +:param mesh: The input mesh. +:param input_attribute: The input attribute id or name. +:param dtype: The new dtype. +:param output_attribute_name: The output attribute name. If none, cast will replace the input attribute. + +:returns: The id of the new attribute.)"); } } // namespace lagrange::python diff --git a/modules/core/python/tests/test_cast_attribute.py b/modules/core/python/tests/test_cast_attribute.py new file mode 100644 index 00000000..4e57c29c --- /dev/null +++ b/modules/core/python/tests/test_cast_attribute.py @@ -0,0 +1,52 @@ +# +# Copyright 2024 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. +# +import lagrange + +import numpy as np +from .assets import single_triangle + + +class TestCastAttribute: + def test_simple(self, single_triangle): + mesh = single_triangle + attr_id = mesh.create_attribute( + "test", + element=lagrange.AttributeElement.Value, + initial_values=np.eye(3, dtype=np.float32), + ) + assert mesh.attribute("test").dtype == np.float32 + + # Cast to double + attr_id = lagrange.cast_attribute(mesh, "test", np.float64) + assert mesh.get_attribute_name(attr_id) == "test" + assert mesh.attribute("test").dtype == np.float64 + + # Cast to uint8 + attr_id = lagrange.cast_attribute(mesh, "test", np.uint8) + assert mesh.get_attribute_name(attr_id) == "test" + assert mesh.attribute("test").dtype == np.uint8 + + # Cast to another attribute with type int32 + attr_id = lagrange.cast_attribute(mesh, "test", np.int32, output_attribute_name="test2") + assert mesh.get_attribute_name(attr_id) == "test2" + assert mesh.attribute("test2").dtype == np.int32 + assert mesh.attribute("test").dtype == np.uint8 # Same as before + + # Cast to python float + attr_id = lagrange.cast_attribute(mesh, "test", float) + assert mesh.get_attribute_name(attr_id) == "test" + assert mesh.attribute("test").dtype == np.float64 + + # Cast to python int + attr_id = lagrange.cast_attribute(mesh, "test", int) + assert mesh.get_attribute_name(attr_id) == "test" + assert mesh.attribute("test").dtype == np.int64 diff --git a/modules/core/python/tests/test_compute_seam_edges.py b/modules/core/python/tests/test_compute_seam_edges.py new file mode 100644 index 00000000..061d81eb --- /dev/null +++ b/modules/core/python/tests/test_compute_seam_edges.py @@ -0,0 +1,23 @@ +# +# Copyright 2024 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. +# +import lagrange + +import numpy as np +import pytest + +from .assets import cube_with_uv, cube + +class TestComputeCentroid: + def test_cube(self, cube_with_uv): + mesh = cube_with_uv + seam_id = lagrange.compute_seam_edges(mesh, mesh.get_attribute_id("uv")) + assert np.count_nonzero(mesh.attribute(seam_id).data) == 7 diff --git a/modules/core/python/tests/test_surface_mesh.py b/modules/core/python/tests/test_surface_mesh.py index b02d8c1f..3e1506a7 100644 --- a/modules/core/python/tests/test_surface_mesh.py +++ b/modules/core/python/tests/test_surface_mesh.py @@ -141,6 +141,69 @@ def test_create_attribute_without_init_values(self, single_triangle): attr.data = np.ones(mesh.num_vertices, dtype=t) assert np.all(attr.data == 1) + def test_create_attribute_with_init_values(self, single_triangle): + mesh = single_triangle + + with pytest.raises(Exception): + # Ambiguous element type: could be vertex or corner. + mesh.create_attribute("vertex_index", initial_values=[1, 2, 3]) + + # Vertex attribute + mesh.add_vertex([1, 1, 1]) # Add an isolated vertex. + id = mesh.create_attribute("vertex_index", initial_values=[1, 2, 3, 4]) + attr = mesh.attribute(id) + assert attr.element_type == lagrange.AttributeElement.Vertex + assert attr.usage == lagrange.AttributeUsage.Scalar + assert np.all(attr.data == [1, 2, 3, 4]) + assert attr.dtype == np.float64 + assert attr.dtype == attr.data.dtype + + # Corner attribute + id = mesh.create_attribute( + "corner_index", initial_values=np.array([1, 2, 3]) + ) + attr = mesh.attribute(id) + assert attr.element_type == lagrange.AttributeElement.Corner + assert attr.usage == lagrange.AttributeUsage.Scalar + assert np.all(attr.data == [1, 2, 3]) + assert attr.dtype == attr.data.dtype + + # Facet attribute + id = mesh.create_attribute( + "facet_index", initial_values=[0], num_channels=1 + ) + attr = mesh.attribute(id) + assert attr.element_type == lagrange.AttributeElement.Facet + assert attr.usage == lagrange.AttributeUsage.Scalar + assert attr.dtype == np.float64 + assert attr.dtype == attr.data.dtype + + # Indexed attribute + id = mesh.create_attribute( + "custom", initial_values=np.array([0, 1]), initial_indices=[0, 0, 1] + ) + assert mesh.is_attribute_indexed(id) + attr = mesh.indexed_attribute(id) + assert attr.usage == lagrange.AttributeUsage.Scalar + assert np.all(attr.indices.data == [0, 0, 1]) + assert np.issubdtype(attr.values.dtype, np.integer) + assert attr.indices.dtype == np.uint32 + assert attr.values.dtype == attr.values.data.dtype + assert attr.indices.dtype == attr.indices.data.dtype + + # Indexed attribute with incorrect index dtype + id = mesh.create_attribute( + "custom2", + initial_values=np.array([0, 1]), + initial_indices=np.array([0, 0, 1]), + ) + assert mesh.is_attribute_indexed(id) + attr = mesh.indexed_attribute(id) + assert attr.usage == lagrange.AttributeUsage.Scalar + assert np.all(attr.indices.data == [0, 0, 1]) + assert np.issubdtype(attr.values.dtype, np.integer) + assert attr.indices.dtype == np.uint32 + def test_edges(self, single_triangle, cube): mesh = single_triangle mesh.initialize_edges( diff --git a/modules/core/src/Attribute.cpp b/modules/core/src/Attribute.cpp index 324e8ff7..a0a6ba06 100644 --- a/modules/core/src/Attribute.cpp +++ b/modules/core/src/Attribute.cpp @@ -635,22 +635,22 @@ void Attribute::clear_views() // clang-format on #define LA_X_cast_from(SourceValueType, TargetValueType) \ - template Attribute Attribute::cast_copy( \ + template LA_CORE_API Attribute Attribute::cast_copy( \ const Attribute& other); \ - template Attribute& Attribute::cast_assign( \ + template LA_CORE_API Attribute& Attribute::cast_assign( \ const Attribute& other); #define LA_X_cast_from_aux(_, SourceValueType) LA_ATTRIBUTE2_X(cast_from, SourceValueType) LA_ATTRIBUTE_X(cast_from_aux, 0) #define LA_X_attribute_get_type(_, ValueType) \ template <> \ - [[nodiscard]] AttributeValueType Attribute::get_value_type() const \ + [[nodiscard]] LA_CORE_API AttributeValueType Attribute::get_value_type() const \ { \ return AttributeValueType::e_##ValueType; \ } LA_ATTRIBUTE_X(attribute_get_type, 0) -#define LA_X_attr(_, ValueType) template class Attribute; +#define LA_X_attr(_, ValueType) template class LA_CORE_API Attribute; LA_ATTRIBUTE_X(attr, 0) } // namespace lagrange diff --git a/modules/core/src/IndexedAttribute.cpp b/modules/core/src/IndexedAttribute.cpp index a0582184..d20667b3 100644 --- a/modules/core/src/IndexedAttribute.cpp +++ b/modules/core/src/IndexedAttribute.cpp @@ -79,7 +79,7 @@ IndexedAttribute& IndexedAttribute::operator // Explicit template instantiation //////////////////////////////////////////////////////////////////////////////// -#define LA_X_attr(Index, ValueType) template class IndexedAttribute; +#define LA_X_attr(Index, ValueType) template class LA_CORE_API IndexedAttribute; #define LA_X_mesh(_, Index) LA_ATTRIBUTE_X(attr, Index) LA_SURFACE_MESH_INDEX_X(mesh, 0) diff --git a/modules/core/src/SurfaceMesh.cpp b/modules/core/src/SurfaceMesh.cpp index 9aaa359c..3db56275 100644 --- a/modules/core/src/SurfaceMesh.cpp +++ b/modules/core/src/SurfaceMesh.cpp @@ -1424,12 +1424,12 @@ SurfaceMesh SurfaceMesh::s const SurfaceMesh& source_mesh) { SurfaceMesh target_mesh(BareMeshTag{}); - target_mesh.m_num_vertices = source_mesh.m_num_vertices; - target_mesh.m_num_facets = source_mesh.m_num_facets; - target_mesh.m_num_corners = source_mesh.m_num_corners; - target_mesh.m_num_edges = source_mesh.m_num_edges; - target_mesh.m_dimension = source_mesh.m_dimension; - target_mesh.m_vertex_per_facet = source_mesh.m_vertex_per_facet; + target_mesh.m_num_vertices = static_cast(source_mesh.m_num_vertices); + target_mesh.m_num_facets = static_cast(source_mesh.m_num_facets); + target_mesh.m_num_corners = static_cast(source_mesh.m_num_corners); + target_mesh.m_num_edges = static_cast(source_mesh.m_num_edges); + target_mesh.m_dimension = static_cast(source_mesh.m_dimension); + target_mesh.m_vertex_per_facet = static_cast(source_mesh.m_vertex_per_facet); // TODO: Create empty slots where other attributes should have been to ensure id stability? constexpr int N = SurfaceMesh::ReservedAttributeIds::size(); @@ -1465,12 +1465,12 @@ SurfaceMesh SurfaceMesh::s SurfaceMesh&& source_mesh) { SurfaceMesh target_mesh(BareMeshTag{}); - target_mesh.m_num_vertices = source_mesh.m_num_vertices; - target_mesh.m_num_facets = source_mesh.m_num_facets; - target_mesh.m_num_corners = source_mesh.m_num_corners; - target_mesh.m_num_edges = source_mesh.m_num_edges; - target_mesh.m_dimension = source_mesh.m_dimension; - target_mesh.m_vertex_per_facet = source_mesh.m_vertex_per_facet; + target_mesh.m_num_vertices = static_cast(source_mesh.m_num_vertices); + target_mesh.m_num_facets = static_cast(source_mesh.m_num_facets); + target_mesh.m_num_corners = static_cast(source_mesh.m_num_corners); + target_mesh.m_num_edges = static_cast(source_mesh.m_num_edges); + target_mesh.m_dimension = static_cast(source_mesh.m_dimension); + target_mesh.m_vertex_per_facet = static_cast(source_mesh.m_vertex_per_facet); // TODO: Create empty slots where other attributes should have been to ensure id stability? constexpr int N = SurfaceMesh::ReservedAttributeIds::size(); @@ -3140,7 +3140,7 @@ AttributeId SurfaceMesh::wrap_as_attribute_internal( // Explicit instantiation of the templated mesh attribute methods. #define LA_X_surface_mesh_attr(ValueType, Scalar, Index) \ - template AttributeId SurfaceMesh::create_attribute( \ + template LA_CORE_API AttributeId SurfaceMesh::create_attribute( \ std::string_view name, \ AttributeElement element, \ size_t num_channels, \ @@ -3148,7 +3148,7 @@ AttributeId SurfaceMesh::wrap_as_attribute_internal( span initial_values, \ span initial_indices, \ AttributeCreatePolicy policy); \ - template AttributeId SurfaceMesh::create_attribute( \ + template LA_CORE_API AttributeId SurfaceMesh::create_attribute( \ std::string_view name, \ AttributeElement element, \ AttributeUsage usage, \ @@ -3156,121 +3156,121 @@ AttributeId SurfaceMesh::wrap_as_attribute_internal( span initial_values, \ span initial_indices, \ AttributeCreatePolicy policy); \ - template AttributeId SurfaceMesh::wrap_as_attribute( \ + template LA_CORE_API AttributeId SurfaceMesh::wrap_as_attribute( \ std::string_view name, \ AttributeElement element, \ AttributeUsage usage, \ size_t num_channels, \ span values_view); \ - template AttributeId SurfaceMesh::wrap_as_attribute( \ + template LA_CORE_API AttributeId SurfaceMesh::wrap_as_attribute( \ std::string_view name, \ AttributeElement element, \ AttributeUsage usage, \ size_t num_channels, \ SharedSpan shared_values); \ - template AttributeId SurfaceMesh::wrap_as_const_attribute( \ + template LA_CORE_API AttributeId SurfaceMesh::wrap_as_const_attribute( \ std::string_view name, \ AttributeElement element, \ AttributeUsage usage, \ size_t num_channels, \ span values_view); \ - template AttributeId SurfaceMesh::wrap_as_const_attribute( \ + template LA_CORE_API AttributeId SurfaceMesh::wrap_as_const_attribute( \ std::string_view name, \ AttributeElement element, \ AttributeUsage usage, \ size_t num_channels, \ SharedSpan shared_values); \ - template AttributeId SurfaceMesh::wrap_as_indexed_attribute( \ + template LA_CORE_API AttributeId SurfaceMesh::wrap_as_indexed_attribute( \ std::string_view name, \ AttributeUsage usage, \ size_t num_values, \ size_t num_channels, \ span values_view, \ span indices_view); \ - template AttributeId SurfaceMesh::wrap_as_indexed_attribute( \ + template LA_CORE_API AttributeId SurfaceMesh::wrap_as_indexed_attribute( \ std::string_view name, \ AttributeUsage usage, \ size_t num_values, \ size_t num_channels, \ SharedSpan shared_values, \ SharedSpan shared_indices); \ - template AttributeId SurfaceMesh::wrap_as_indexed_attribute( \ + template LA_CORE_API AttributeId SurfaceMesh::wrap_as_indexed_attribute( \ std::string_view name, \ AttributeUsage usage, \ size_t num_values, \ size_t num_channels, \ span values_view, \ SharedSpan shared_indices); \ - template AttributeId SurfaceMesh::wrap_as_indexed_attribute( \ + template LA_CORE_API AttributeId SurfaceMesh::wrap_as_indexed_attribute( \ std::string_view name, \ AttributeUsage usage, \ size_t num_values, \ size_t num_channels, \ SharedSpan shared_values, \ span indices_view); \ - template AttributeId SurfaceMesh::wrap_as_const_indexed_attribute( \ + template LA_CORE_API AttributeId SurfaceMesh::wrap_as_const_indexed_attribute( \ std::string_view name, \ AttributeUsage usage, \ size_t num_values, \ size_t num_channels, \ span values_view, \ span indices_view); \ - template AttributeId SurfaceMesh::wrap_as_const_indexed_attribute( \ + template LA_CORE_API AttributeId SurfaceMesh::wrap_as_const_indexed_attribute( \ std::string_view name, \ AttributeUsage usage, \ size_t num_values, \ size_t num_channels, \ SharedSpan shared_values, \ SharedSpan shared_indices); \ - template AttributeId SurfaceMesh::wrap_as_const_indexed_attribute( \ + template LA_CORE_API AttributeId SurfaceMesh::wrap_as_const_indexed_attribute( \ std::string_view name, \ AttributeUsage usage, \ size_t num_values, \ size_t num_channels, \ span values_view, \ SharedSpan shared_indices); \ - template AttributeId SurfaceMesh::wrap_as_const_indexed_attribute( \ + template LA_CORE_API AttributeId SurfaceMesh::wrap_as_const_indexed_attribute( \ std::string_view name, \ AttributeUsage usage, \ size_t num_values, \ size_t num_channels, \ SharedSpan shared_values, \ span indices_view); \ - template std::shared_ptr> \ + template LA_CORE_API std::shared_ptr> \ SurfaceMesh::delete_and_export_attribute( \ std::string_view name, \ AttributeDeletePolicy delete_policy, \ AttributeExportPolicy export_policy); \ - template std::shared_ptr> \ + template LA_CORE_API std::shared_ptr> \ SurfaceMesh::delete_and_export_const_attribute( \ std::string_view name, \ AttributeDeletePolicy delete_policy, \ AttributeExportPolicy export_policy); \ - template std::shared_ptr> \ + template LA_CORE_API std::shared_ptr> \ SurfaceMesh::delete_and_export_indexed_attribute( \ std::string_view name, \ AttributeExportPolicy policy); \ - template std::shared_ptr> \ + template LA_CORE_API std::shared_ptr> \ SurfaceMesh::delete_and_export_const_indexed_attribute( \ std::string_view name, \ AttributeExportPolicy policy); \ - template bool SurfaceMesh::is_attribute_type(std::string_view name) \ + template LA_CORE_API bool SurfaceMesh::is_attribute_type(std::string_view name) \ const; \ - template bool SurfaceMesh::is_attribute_type(AttributeId id) const; \ - template const Attribute& SurfaceMesh::get_attribute( \ + template LA_CORE_API bool SurfaceMesh::is_attribute_type(AttributeId id) const; \ + template LA_CORE_API const Attribute& SurfaceMesh::get_attribute( \ std::string_view name) const; \ - template const Attribute& SurfaceMesh::get_attribute(AttributeId id) \ + template LA_CORE_API const Attribute& SurfaceMesh::get_attribute(AttributeId id) \ const; \ - template Attribute& SurfaceMesh::ref_attribute( \ + template LA_CORE_API Attribute& SurfaceMesh::ref_attribute( \ std::string_view name); \ - template Attribute& SurfaceMesh::ref_attribute(AttributeId id); \ - template const IndexedAttribute& \ + template LA_CORE_API Attribute& SurfaceMesh::ref_attribute(AttributeId id); \ + template LA_CORE_API const IndexedAttribute& \ SurfaceMesh::get_indexed_attribute(std::string_view name) const; \ - template const IndexedAttribute& \ + template LA_CORE_API const IndexedAttribute& \ SurfaceMesh::get_indexed_attribute(AttributeId id) const; \ - template IndexedAttribute& \ + template LA_CORE_API IndexedAttribute& \ SurfaceMesh::ref_indexed_attribute(std::string_view name); \ - template IndexedAttribute& \ + template LA_CORE_API IndexedAttribute& \ SurfaceMesh::ref_indexed_attribute(AttributeId id); #define LA_X_surface_mesh_aux(_, ValueType) LA_SURFACE_MESH_X(surface_mesh_attr, ValueType) @@ -3280,13 +3280,13 @@ LA_ATTRIBUTE_X(surface_mesh_aux, 0) #define fst(first, second) first #define snd(first, second) second #define LA_X_surface_mesh_mesh_other(ScalarIndex, OtherScalar, OtherIndex) \ - template SurfaceMesh \ + template LA_CORE_API SurfaceMesh \ SurfaceMesh::stripped_copy( \ const SurfaceMesh& other); \ - template SurfaceMesh \ + template LA_CORE_API SurfaceMesh \ SurfaceMesh::stripped_move( \ SurfaceMesh&& other); \ - template AttributeId SurfaceMesh::create_attribute_from( \ + template LA_CORE_API AttributeId SurfaceMesh::create_attribute_from( \ std::string_view name, \ const SurfaceMesh& source_mesh, \ std::string_view source_name); @@ -3302,7 +3302,7 @@ LA_ATTRIBUTE_X(surface_mesh_aux, 0) LA_SURFACE_MESH_X(surface_mesh_mesh_aux, 0) // Explicit instantiation of the SurfaceMesh class -#define LA_X_surface_mesh_class(_, Scalar, Index) template class SurfaceMesh; +#define LA_X_surface_mesh_class(_, Scalar, Index) template class LA_CORE_API SurfaceMesh; LA_SURFACE_MESH_X(surface_mesh_class, 0) } // namespace lagrange diff --git a/modules/core/src/cast.cpp b/modules/core/src/cast.cpp index ef64c7ac..ceb85366 100644 --- a/modules/core/src/cast.cpp +++ b/modules/core/src/cast.cpp @@ -108,7 +108,7 @@ SurfaceMesh cast( #define fst(first, second) first #define snd(first, second) second #define LA_X_cast_mesh_to(SourceScalarIndex, TargetScalar, TargetIndex) \ - template SurfaceMesh cast( \ + template LA_CORE_API SurfaceMesh cast( \ const SurfaceMesh& source_mesh, \ const AttributeFilter& convertible_attributes, \ std::vector* converted_attributes_names); diff --git a/modules/core/src/cast_attribute.cpp b/modules/core/src/cast_attribute.cpp new file mode 100644 index 00000000..1533ac7c --- /dev/null +++ b/modules/core/src/cast_attribute.cpp @@ -0,0 +1,143 @@ +/* + * Copyright 2024 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. + */ +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace lagrange { + +template +AttributeId cast_attribute( + SurfaceMesh& mesh, + AttributeId source_id, + std::string_view target_name) +{ + const auto& source_attr_base = mesh.get_attribute_base(source_id); + + if (source_attr_base.get_value_type() == make_attribute_value_type()) { + logger().warn("Target value type is the same as source value type. Creating shallow copy."); + return mesh.create_attribute_from(target_name, mesh, mesh.get_attribute_name(source_id)); + } + + const auto target_id = mesh.template create_attribute( + target_name, + source_attr_base.get_element_type(), + source_attr_base.get_usage(), + source_attr_base.get_num_channels()); + + internal::visit_attribute(mesh, source_id, [&](auto& source_attr) { + using AttributeType = std::decay_t; + if constexpr (AttributeType::IsIndexed) { + auto& target_attr = mesh.template ref_indexed_attribute(target_id); + target_attr.values().cast_assign(source_attr.values()); + target_attr.indices() = source_attr.indices(); + } else { + auto& target_attr = mesh.template ref_attribute(target_id); + target_attr.cast_assign(source_attr); + } + }); + + return target_id; +} + +template +AttributeId cast_attribute( + SurfaceMesh& mesh, + std::string_view source_name, + std::string_view target_name) +{ + return cast_attribute(mesh, mesh.get_attribute_id(source_name), target_name); +} + +template +AttributeId cast_attribute_in_place(SurfaceMesh& mesh, AttributeId source_id) +{ + const auto& source_attr_base = mesh.get_attribute_base(source_id); + + if (source_attr_base.get_value_type() == make_attribute_value_type()) { + logger().warn("Target value type is the same as source value type. Nothing to do."); + return source_id; + } + + std::string target_name(mesh.get_attribute_name(source_id)); + + AttributeId target_id = invalid_attribute_id(); + internal::visit_attribute(mesh, source_id, [&](auto& source_attr_) { + using AttributeType = std::decay_t; + using SourceValueType = typename AttributeType::ValueType; + + if constexpr (AttributeType::IsIndexed) { + auto source_attr = + mesh.template delete_and_export_const_indexed_attribute( + mesh.get_attribute_name(source_id), + AttributeExportPolicy::KeepExternalPtr); + + target_id = mesh.template create_attribute( + target_name, + source_attr->get_element_type(), + source_attr->get_usage(), + source_attr->get_num_channels()); + + auto& target_attr = mesh.template ref_indexed_attribute(target_id); + target_attr.values().cast_assign(source_attr->values()); + target_attr.indices() = std::move(source_attr->indices()); + } else { + auto source_attr = mesh.template delete_and_export_const_attribute( + mesh.get_attribute_name(source_id), + AttributeDeletePolicy::ErrorIfReserved, + AttributeExportPolicy::KeepExternalPtr); + + target_id = mesh.template create_attribute( + target_name, + source_attr->get_element_type(), + source_attr->get_usage(), + source_attr->get_num_channels()); + + auto& target_attr = mesh.template ref_attribute(target_id); + target_attr.cast_assign(*source_attr); + } + }); + + return target_id; +} + +template +AttributeId cast_attribute_in_place(SurfaceMesh& mesh, std::string_view name) +{ + return cast_attribute_in_place(mesh, mesh.get_attribute_id(name)); +} + +#define LA_X_cast_attribute(ValueType, Scalar, Index) \ + template LA_CORE_API AttributeId cast_attribute( \ + SurfaceMesh & mesh, \ + AttributeId source_id, \ + std::string_view target_name); \ + template LA_CORE_API AttributeId cast_attribute( \ + SurfaceMesh & mesh, \ + std::string_view source_name, \ + std::string_view target_name); \ + template LA_CORE_API AttributeId cast_attribute_in_place( \ + SurfaceMesh & mesh, \ + AttributeId source_id); \ + template LA_CORE_API AttributeId cast_attribute_in_place( \ + SurfaceMesh & mesh, \ + std::string_view source_name); +#define LA_X_cast_attribute_aux(_, ValueType) LA_SURFACE_MESH_X(cast_attribute, ValueType) +LA_ATTRIBUTE_X(cast_attribute_aux, 0) + +} // namespace lagrange diff --git a/modules/core/src/combine_meshes.cpp b/modules/core/src/combine_meshes.cpp index af7e7af9..b9bbd1f3 100644 --- a/modules/core/src/combine_meshes.cpp +++ b/modules/core/src/combine_meshes.cpp @@ -316,13 +316,13 @@ SurfaceMesh combine_meshes( } #define LA_X_combine_meshes(_, Scalar, Index) \ - template SurfaceMesh combine_meshes( \ + template LA_CORE_API SurfaceMesh combine_meshes( \ std::initializer_list*>, \ bool); \ - template SurfaceMesh combine_meshes( \ + template LA_CORE_API SurfaceMesh combine_meshes( \ span>, \ bool); \ - template SurfaceMesh combine_meshes( \ + template LA_CORE_API SurfaceMesh combine_meshes( \ size_t, \ function_ref&(size_t)>, \ bool); diff --git a/modules/core/src/compute_area.cpp b/modules/core/src/compute_area.cpp index c43779a0..ff453a5d 100644 --- a/modules/core/src/compute_area.cpp +++ b/modules/core/src/compute_area.cpp @@ -341,19 +341,19 @@ Scalar compute_mesh_area( } #define LA_X_compute_facet_area(_, Scalar, Index) \ - template AttributeId compute_facet_area( \ + template LA_CORE_API AttributeId compute_facet_area( \ SurfaceMesh&, \ FacetAreaOptions); \ - template Scalar compute_mesh_area( \ + template LA_CORE_API Scalar compute_mesh_area( \ const SurfaceMesh&, \ MeshAreaOptions); #define LA_X_compute_facet_area_dim(_, Scalar, Index, Dimension) \ - template AttributeId compute_facet_area( \ + template LA_CORE_API AttributeId compute_facet_area( \ SurfaceMesh&, \ const Eigen::Transform&, \ FacetAreaOptions); \ - template Scalar compute_mesh_area( \ + template LA_CORE_API Scalar compute_mesh_area( \ const SurfaceMesh&, \ const Eigen::Transform&, \ MeshAreaOptions); diff --git a/modules/core/src/compute_centroid.cpp b/modules/core/src/compute_centroid.cpp index ce8b30eb..10b5354c 100644 --- a/modules/core/src/compute_centroid.cpp +++ b/modules/core/src/compute_centroid.cpp @@ -129,10 +129,10 @@ void compute_mesh_centroid( } #define LA_X_compute_centroid(_, Scalar, Index) \ - template AttributeId compute_facet_centroid( \ + template LA_CORE_API AttributeId compute_facet_centroid( \ SurfaceMesh&, \ FacetCentroidOptions); \ - template void compute_mesh_centroid( \ + template LA_CORE_API void compute_mesh_centroid( \ const SurfaceMesh&, \ span, \ MeshCentroidOptions); diff --git a/modules/core/src/compute_components.cpp b/modules/core/src/compute_components.cpp index 43862e72..81e8236d 100644 --- a/modules/core/src/compute_components.cpp +++ b/modules/core/src/compute_components.cpp @@ -133,10 +133,10 @@ size_t compute_components( } #define LA_X_compute_components(_, Scalar, Index) \ - template size_t compute_components( \ + template LA_CORE_API size_t compute_components( \ SurfaceMesh&, \ ComponentOptions); \ - template size_t compute_components( \ + template LA_CORE_API size_t compute_components( \ SurfaceMesh&, \ span, \ ComponentOptions); diff --git a/modules/core/src/compute_dihedral_angles.cpp b/modules/core/src/compute_dihedral_angles.cpp index b6d8cf8c..668aa188 100644 --- a/modules/core/src/compute_dihedral_angles.cpp +++ b/modules/core/src/compute_dihedral_angles.cpp @@ -82,7 +82,7 @@ AttributeId compute_dihedral_angles( } #define LA_X_compute_dihedral_angles(_, Scalar, Index) \ - template AttributeId compute_dihedral_angles( \ + template LA_CORE_API AttributeId compute_dihedral_angles( \ SurfaceMesh&, \ const DihedralAngleOptions&); LA_SURFACE_MESH_X(compute_dihedral_angles, 0) diff --git a/modules/core/src/compute_dijkstra_distance.cpp b/modules/core/src/compute_dijkstra_distance.cpp index d3916fa6..703f1852 100644 --- a/modules/core/src/compute_dijkstra_distance.cpp +++ b/modules/core/src/compute_dijkstra_distance.cpp @@ -89,7 +89,7 @@ std::optional> compute_dijkstra_distance( } #define LA_X_compute_dijkstra_distance(_, Scalar, Index) \ - template std::optional> compute_dijkstra_distance( \ + template LA_CORE_API std::optional> compute_dijkstra_distance( \ SurfaceMesh&, \ const DijkstraDistanceOptions& options); LA_SURFACE_MESH_X(compute_dijkstra_distance, 0) diff --git a/modules/core/src/compute_edge_lengths.cpp b/modules/core/src/compute_edge_lengths.cpp index ee935d87..077e2454 100644 --- a/modules/core/src/compute_edge_lengths.cpp +++ b/modules/core/src/compute_edge_lengths.cpp @@ -49,7 +49,7 @@ AttributeId compute_edge_lengths(SurfaceMesh& mesh, const EdgeLen } #define LA_X_compute_edge_lengths(_, Scalar, Index) \ - template AttributeId compute_edge_lengths( \ + template LA_CORE_API AttributeId compute_edge_lengths( \ SurfaceMesh&, \ const EdgeLengthOptions&); LA_SURFACE_MESH_X(compute_edge_lengths, 0) diff --git a/modules/core/src/compute_facet_normal.cpp b/modules/core/src/compute_facet_normal.cpp index 5e66321f..ef3dfaec 100644 --- a/modules/core/src/compute_facet_normal.cpp +++ b/modules/core/src/compute_facet_normal.cpp @@ -70,7 +70,7 @@ AttributeId compute_facet_normal(SurfaceMesh& mesh, FacetNormalOp } #define LA_X_compute_facet_normal(_, Scalar, Index) \ - template AttributeId compute_facet_normal(SurfaceMesh& mesh, FacetNormalOptions); + template LA_CORE_API AttributeId compute_facet_normal(SurfaceMesh& mesh, FacetNormalOptions); LA_SURFACE_MESH_X(compute_facet_normal, 0) } // namespace lagrange diff --git a/modules/core/src/compute_greedy_coloring.cpp b/modules/core/src/compute_greedy_coloring.cpp new file mode 100644 index 00000000..0d23bd25 --- /dev/null +++ b/modules/core/src/compute_greedy_coloring.cpp @@ -0,0 +1,177 @@ +/* + * Copyright 2024 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. + */ +#include + +#include +#include +#include +#include +#include + +#include +#include + +namespace lagrange { + +namespace { + +template +void foreach_facet_around_facet(const SurfaceMesh& mesh, Index f, Func&& func) +{ + for (Index c = mesh.get_facet_corner_begin(f); c != mesh.get_facet_corner_end(f); ++c) { + mesh.foreach_corner_around_edge(mesh.get_corner_edge(c), [&](Index c1) { + if (c1 != c) { + func(mesh.get_corner_facet(c1)); + } + }); + } +} + +template +void foreach_vertex_around_vertex(const SurfaceMesh& mesh, Index v, Func&& func) +{ + mesh.foreach_edge_around_vertex_with_duplicates(v, [&](Index e) { + auto adj_v = mesh.get_edge_vertices(e); + for (auto vi : adj_v) { + if (vi != v) { + func(vi); + } + } + }); +} + +template +[[maybe_unused]] inline constexpr bool always_false_v = false; + +template +void foreach_element_around_element(const SurfaceMesh& mesh, Index i, Func&& func) +{ + if constexpr (ElementType == AttributeElement::Vertex) { + foreach_vertex_around_vertex(mesh, i, func); + } else if constexpr (ElementType == AttributeElement::Facet) { + foreach_facet_around_facet(mesh, i, func); + } else { + static_assert(always_false_v, "Unsupported element type"); + } +} + +template +auto bounded_rand(RngType& rng, typename RngType::result_type upper_bound) -> + typename RngType::result_type +{ + typedef typename RngType::result_type rtype; + rtype threshold = (RngType::max() - RngType::min() + rtype(1) - upper_bound) % upper_bound; + for (;;) { + rtype r = rng() - RngType::min(); + if (r >= threshold) return r % upper_bound; + } +} + +template +AttributeId compute_greedy_coloring( + SurfaceMesh& mesh, + std::string_view output_attribute_name, + size_t num_colors_used) +{ + mesh.initialize_edges(); + + AttributeId colors_id = internal::find_or_create_attribute( + mesh, + output_attribute_name, + ElementType, + AttributeUsage::Scalar, + 1, + internal::ResetToDefault::Yes); + + auto colors = attribute_vector_ref(mesh, colors_id); + colors.setConstant(invalid()); + + const Index num_elements = + (ElementType == AttributeElement::Vertex ? mesh.get_num_vertices() : mesh.get_num_facets()); + std::vector is_color_used; + std::vector available_colors; + std::mt19937 gen; + + auto is_valid = [](Index c) { return (c != invalid()); }; + + auto get_random_available_color = [&](Index i) { + is_color_used.assign(num_colors_used, false); + foreach_element_around_element(mesh, i, [&](Index f2) { + if (is_valid(colors[f2])) { + is_color_used[colors[f2]] = true; + } + }); + available_colors.clear(); + for (Index c = 0; c < static_cast(num_colors_used); ++c) { + if (!is_color_used[c]) { + available_colors.push_back(c); + } + } + if (available_colors.empty()) { + available_colors.push_back(static_cast(num_colors_used++)); + } + const auto upper_bound = static_cast(available_colors.size() - 1); + return available_colors[bounded_rand(gen, upper_bound)]; + }; + + std::queue pending; + std::vector marked(num_elements, false); + for (Index i = 0; i < num_elements; ++i) { + if (!marked[i]) { + pending.emplace(i); + marked[i] = true; + while (!pending.empty()) { + Index x = pending.front(); + pending.pop(); + colors[x] = get_random_available_color(x); + foreach_element_around_element(mesh, x, [&](Index y) { + if (!marked[y]) { + pending.emplace(y); + marked[y] = true; + } + }); + } + } + } + + return colors_id; +} + +} // namespace + +template +AttributeId compute_greedy_coloring( + SurfaceMesh& mesh, + const GreedyColoringOptions& options) +{ + if (options.element_type == AttributeElement::Vertex) { + return compute_greedy_coloring( + mesh, + options.output_attribute_name, + options.num_color_used); + } else if (options.element_type == AttributeElement::Facet) { + return compute_greedy_coloring( + mesh, + options.output_attribute_name, + options.num_color_used); + } else { + throw Error("Unsupported element type"); + } +} + +#define LA_X_compute_greedy_coloring(_, Scalar, Index) \ + template LA_CORE_API AttributeId compute_greedy_coloring( \ + SurfaceMesh&, \ + const GreedyColoringOptions&); +LA_SURFACE_MESH_X(compute_greedy_coloring, 0) + +} // namespace lagrange diff --git a/modules/core/src/compute_normal.cpp b/modules/core/src/compute_normal.cpp index 6fb4e240..7937e9e9 100644 --- a/modules/core/src/compute_normal.cpp +++ b/modules/core/src/compute_normal.cpp @@ -248,17 +248,17 @@ AttributeId compute_normal( } #define LA_X_compute_normal(_, Scalar, Index) \ - template AttributeId compute_normal( \ + template LA_CORE_API AttributeId compute_normal( \ SurfaceMesh & mesh, \ function_ref, \ span, \ NormalOptions); \ - template AttributeId compute_normal( \ + template LA_CORE_API AttributeId compute_normal( \ SurfaceMesh & mesh, \ function_ref, \ span, \ NormalOptions); \ - template AttributeId compute_normal( \ + template LA_CORE_API AttributeId compute_normal( \ SurfaceMesh & mesh, \ Scalar, \ span, \ diff --git a/modules/core/src/compute_seam_edges.cpp b/modules/core/src/compute_seam_edges.cpp new file mode 100644 index 00000000..f7bf5be2 --- /dev/null +++ b/modules/core/src/compute_seam_edges.cpp @@ -0,0 +1,97 @@ +/* + * Copyright 2024 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. + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +namespace lagrange { + +template +AttributeId compute_seam_edges( + SurfaceMesh& mesh, + AttributeId source_id, + const SeamEdgesOptions& options) +{ + mesh.initialize_edges(); + + AttributeId output_id = internal::find_or_create_attribute( + mesh, + options.output_attribute_name, + AttributeElement::Edge, + AttributeUsage::Scalar, + 1, + internal::ResetToDefault::Yes); + auto output_is_seam = mesh.template ref_attribute(output_id).ref_all(); + + auto process_attribute = [&](auto&& attr) { + auto indices = attr.indices().get_all(); + tbb::parallel_for(Index(0), mesh.get_num_edges(), [&](Index e) { + auto v = mesh.get_edge_vertices(e); + std::optional> prev_indices; + mesh.foreach_corner_around_edge(e, [&](Index c0) { + const Index f = mesh.get_corner_facet(c0); + auto fv = mesh.get_facet_vertices(f); + const Index cs = mesh.get_facet_corner_begin(f); + const Index lv0 = c0 - cs; + const Index lv1 = (lv0 + 1) % fv.size(); + Index v0 = fv[lv0]; + Index v1 = fv[lv1]; + Index c1 = cs + lv1; + if (v[0] != v0) { + la_debug_assert(v[0] == v1); + LA_IGNORE(v1); + std::swap(c0, c1); + std::swap(v0, v1); + } + la_debug_assert(v[0] == v0); + la_debug_assert(v[1] == v1); + if (!prev_indices.has_value()) { + prev_indices = {indices[c0], indices[c1]}; + } else if ((*prev_indices)[0] != indices[c0] || (*prev_indices)[1] != indices[c1]) { + output_is_seam[e] = 1; + } + }); + }); + }; + + internal::visit_attribute(mesh, source_id, [&](auto&& attr) { + using AttributeType = std::decay_t; + if constexpr (AttributeType::IsIndexed) { + process_attribute(attr); + } else { + throw Error("Cannot compute seam edges for a non-indexed attribute."); + } + }); + + return output_id; +} + +#define LA_X_compute_facet_normal(_, Scalar, Index) \ + template LA_CORE_API AttributeId compute_seam_edges( \ + SurfaceMesh& mesh, \ + AttributeId source_id, \ + const SeamEdgesOptions& options); +LA_SURFACE_MESH_X(compute_facet_normal, 0) + +} // namespace lagrange diff --git a/modules/core/src/compute_tangent_bitangent.cpp b/modules/core/src/compute_tangent_bitangent.cpp index 937b2e52..2262baca 100644 --- a/modules/core/src/compute_tangent_bitangent.cpp +++ b/modules/core/src/compute_tangent_bitangent.cpp @@ -569,7 +569,7 @@ TangentBitangentResult compute_tangent_bitangent( } #define LA_X_compute_tangent_bitangent(_, Scalar, Index) \ - template TangentBitangentResult compute_tangent_bitangent( \ + template LA_CORE_API TangentBitangentResult compute_tangent_bitangent( \ SurfaceMesh& mesh, \ TangentBitangentOptions options); LA_SURFACE_MESH_X(compute_tangent_bitangent, 0) diff --git a/modules/core/src/compute_uv_distortion.cpp b/modules/core/src/compute_uv_distortion.cpp index 9652e0ca..09c18c1d 100644 --- a/modules/core/src/compute_uv_distortion.cpp +++ b/modules/core/src/compute_uv_distortion.cpp @@ -69,7 +69,7 @@ AttributeId compute_uv_distortion( } #define LA_X_compute_uv_distortion(_, Scalar, Index) \ - template AttributeId compute_uv_distortion( \ + template LA_CORE_API AttributeId compute_uv_distortion( \ SurfaceMesh&, \ const UVDistortionOptions&); LA_SURFACE_MESH_X(compute_uv_distortion, 0) diff --git a/modules/core/src/compute_uv_tile_list.cpp b/modules/core/src/compute_uv_tile_list.cpp index 4df1341d..4c8b854c 100644 --- a/modules/core/src/compute_uv_tile_list.cpp +++ b/modules/core/src/compute_uv_tile_list.cpp @@ -76,7 +76,7 @@ std::vector> compute_uv_tile_list( } #define LA_X_compute_uv_tile_list(_, Scalar, Index) \ - template std::vector> compute_uv_tile_list( \ + template LA_CORE_API std::vector> compute_uv_tile_list( \ const SurfaceMesh&); LA_SURFACE_MESH_X(compute_uv_tile_list, 0) diff --git a/modules/core/src/compute_vertex_normal.cpp b/modules/core/src/compute_vertex_normal.cpp index 13b5eda9..85c914d3 100644 --- a/modules/core/src/compute_vertex_normal.cpp +++ b/modules/core/src/compute_vertex_normal.cpp @@ -94,7 +94,7 @@ AttributeId compute_vertex_normal(SurfaceMesh& mesh, VertexNormal } #define LA_X_compute_vertex_normal(_, Scalar, Index) \ - template AttributeId compute_vertex_normal( \ + template LA_CORE_API AttributeId compute_vertex_normal( \ SurfaceMesh&, \ VertexNormalOptions); LA_SURFACE_MESH_X(compute_vertex_normal, 0) diff --git a/modules/core/src/compute_vertex_valence.cpp b/modules/core/src/compute_vertex_valence.cpp index aa9491c5..2f9830e7 100644 --- a/modules/core/src/compute_vertex_valence.cpp +++ b/modules/core/src/compute_vertex_valence.cpp @@ -44,7 +44,7 @@ AttributeId compute_vertex_valence(SurfaceMesh& mesh, VertexValen } #define LA_X_compute_vertex_valence(_, Scalar, Index) \ - template AttributeId compute_vertex_valence(SurfaceMesh&, VertexValenceOptions); + template LA_CORE_API AttributeId compute_vertex_valence(SurfaceMesh&, VertexValenceOptions); LA_SURFACE_MESH_X(compute_vertex_valence, 0) } // namespace lagrange diff --git a/modules/core/src/compute_vertex_vertex_adjacency.cpp b/modules/core/src/compute_vertex_vertex_adjacency.cpp index e53b052a..b1440a19 100644 --- a/modules/core/src/compute_vertex_vertex_adjacency.cpp +++ b/modules/core/src/compute_vertex_vertex_adjacency.cpp @@ -108,7 +108,7 @@ AdjacencyList compute_vertex_vertex_adjacency(SurfaceMesh& } #define LA_X_compute_vertex_vertex_adjacency(_, Scalar, Index) \ - template AdjacencyList compute_vertex_vertex_adjacency(SurfaceMesh&); + template LA_CORE_API AdjacencyList compute_vertex_vertex_adjacency(SurfaceMesh&); LA_SURFACE_MESH_X(compute_vertex_vertex_adjacency, 0) } // namespace lagrange diff --git a/modules/core/src/compute_weighted_corner_normal.cpp b/modules/core/src/compute_weighted_corner_normal.cpp index b8ff3d68..f9473548 100644 --- a/modules/core/src/compute_weighted_corner_normal.cpp +++ b/modules/core/src/compute_weighted_corner_normal.cpp @@ -53,7 +53,7 @@ AttributeId compute_weighted_corner_normal( } #define LA_X_compute_weighted_corner_normal(_, Scalar, Index) \ - template AttributeId compute_weighted_corner_normal( \ + template LA_CORE_API AttributeId compute_weighted_corner_normal( \ SurfaceMesh&, \ CornerNormalOptions); LA_SURFACE_MESH_X(compute_weighted_corner_normal, 0) diff --git a/modules/core/src/extract_boundary_loops.cpp b/modules/core/src/extract_boundary_loops.cpp index fe52f370..cad03b3d 100644 --- a/modules/core/src/extract_boundary_loops.cpp +++ b/modules/core/src/extract_boundary_loops.cpp @@ -54,7 +54,7 @@ std::vector> extract_boundary_loops(const SurfaceMesh> extract_boundary_loops( \ + template LA_CORE_API std::vector> extract_boundary_loops( \ const SurfaceMesh& mesh); LA_SURFACE_MESH_X(extract_boundary_loops, 0) diff --git a/modules/core/src/extract_submesh.cpp b/modules/core/src/extract_submesh.cpp index e882cd5f..7359d164 100644 --- a/modules/core/src/extract_submesh.cpp +++ b/modules/core/src/extract_submesh.cpp @@ -169,7 +169,7 @@ SurfaceMesh extract_submesh( #define LA_X_extract_submesh(_, Scalar, Index) \ - template SurfaceMesh extract_submesh( \ + template LA_CORE_API SurfaceMesh extract_submesh( \ const SurfaceMesh&, \ span, \ const SubmeshOptions&); diff --git a/modules/core/src/filter_attributes.cpp b/modules/core/src/filter_attributes.cpp index dbf25806..b43b8856 100644 --- a/modules/core/src/filter_attributes.cpp +++ b/modules/core/src/filter_attributes.cpp @@ -103,10 +103,10 @@ SurfaceMesh filter_attributes( } #define LA_X_filter_attributes(_, Scalar, Index) \ - template std::vector filtered_attribute_ids( \ + template LA_CORE_API std::vector filtered_attribute_ids( \ const SurfaceMesh& mesh, \ const AttributeFilter& options); \ - template SurfaceMesh filter_attributes( \ + template LA_CORE_API SurfaceMesh filter_attributes( \ SurfaceMesh, \ const AttributeFilter&); diff --git a/modules/core/src/fpe.cpp b/modules/core/src/fpe.cpp index b741a76c..debef1a3 100644 --- a/modules/core/src/fpe.cpp +++ b/modules/core/src/fpe.cpp @@ -9,6 +9,8 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ +#include + #if defined(_WIN32) || defined(LA_DISABLE_FPE) namespace { diff --git a/modules/core/src/internal/attribute_string_utils.cpp b/modules/core/src/internal/attribute_string_utils.cpp index 6b38508b..82ea893d 100644 --- a/modules/core/src/internal/attribute_string_utils.cpp +++ b/modules/core/src/internal/attribute_string_utils.cpp @@ -9,8 +9,9 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ -#include #include + +#include #include #define LA_ENUM_CASE(e, x) \ @@ -64,12 +65,12 @@ std::string_view to_string(AttributeUsage usage) #define LA_X_type_name(_, ValueType) \ template <> \ - std::string_view value_type_name(const lagrange::Attribute&) \ + LA_CORE_API std::string_view value_type_name(const lagrange::Attribute&) \ { \ return #ValueType; \ } \ template <> \ - std::string_view value_type_name() \ + LA_CORE_API std::string_view value_type_name() \ { \ return #ValueType; \ } diff --git a/modules/core/src/internal/dijstra.cpp b/modules/core/src/internal/dijstra.cpp index 68e6b60d..ac3a23f5 100644 --- a/modules/core/src/internal/dijstra.cpp +++ b/modules/core/src/internal/dijstra.cpp @@ -86,7 +86,7 @@ void dijkstra( } #define LA_X_dijkstra(_, Scalar, Index) \ - template void dijkstra( \ + template LA_CORE_API void dijkstra( \ SurfaceMesh&, \ span, \ span, \ diff --git a/modules/core/src/internal/find_attribute_utils.cpp b/modules/core/src/internal/find_attribute_utils.cpp index a3a04a26..51e3a981 100644 --- a/modules/core/src/internal/find_attribute_utils.cpp +++ b/modules/core/src/internal/find_attribute_utils.cpp @@ -66,11 +66,11 @@ void check_attribute( if (expected_writable == ShouldBeWritable::Yes) { if (mesh.is_attribute_indexed(id)) { - const auto& attr = mesh.template get_indexed_attribute(id); + const auto& attr = mesh.template get_indexed_attribute(id); la_runtime_assert(!attr.values().is_read_only(), "Attribute is read only"); la_runtime_assert(!attr.indices().is_read_only(), "Attribute is read only"); } else { - const auto& attr = mesh.template get_attribute(id); + const auto& attr = mesh.template get_attribute(id); la_runtime_assert(!attr.is_read_only(), "Attribute is read only"); } } @@ -195,10 +195,10 @@ AttributeId find_or_create_attribute( ShouldBeWritable::Yes); if (reset_tag == ResetToDefault::Yes) { if (expected_element != Indexed) { - auto& attr = mesh.template ref_attribute(id); + auto& attr = mesh.template ref_attribute(id); std::fill(attr.ref_all().begin(), attr.ref_all().end(), attr.get_default_value()); } else { - auto& attr = mesh.template ref_indexed_attribute(id).values(); + auto& attr = mesh.template ref_indexed_attribute(id).values(); std::fill(attr.ref_all().begin(), attr.ref_all().end(), attr.get_default_value()); } } @@ -208,31 +208,31 @@ AttributeId find_or_create_attribute( } // Iterate over attribute types x mesh (scalar, index) types -#define LA_X_find_attribute(ExpectedValueType, Scalar, Index) \ - template AttributeId find_matching_attribute( \ - const SurfaceMesh& mesh, \ - std::string_view name, \ - BitField expected_element, \ - AttributeUsage expected_usage, \ - size_t expected_channels); \ - template AttributeId find_matching_attribute( \ - const SurfaceMesh& mesh, \ - span, \ - BitField expected_element, \ - AttributeUsage expected_usage, \ - size_t expected_channels); \ - template AttributeId find_attribute( \ - const SurfaceMesh& mesh, \ - std::string_view name, \ - BitField expected_element, \ - AttributeUsage expected_usage, \ - size_t expected_channels); \ - template AttributeId find_or_create_attribute( \ - SurfaceMesh & mesh, \ - std::string_view name, \ - AttributeElement expected_element, \ - AttributeUsage expected_usage, \ - size_t expected_channels, \ +#define LA_X_find_attribute(ExpectedValueType, Scalar, Index) \ + template LA_CORE_API AttributeId find_matching_attribute( \ + const SurfaceMesh& mesh, \ + std::string_view name, \ + BitField expected_element, \ + AttributeUsage expected_usage, \ + size_t expected_channels); \ + template LA_CORE_API AttributeId find_matching_attribute( \ + const SurfaceMesh& mesh, \ + span, \ + BitField expected_element, \ + AttributeUsage expected_usage, \ + size_t expected_channels); \ + template LA_CORE_API AttributeId find_attribute( \ + const SurfaceMesh& mesh, \ + std::string_view name, \ + BitField expected_element, \ + AttributeUsage expected_usage, \ + size_t expected_channels); \ + template LA_CORE_API AttributeId find_or_create_attribute( \ + SurfaceMesh & mesh, \ + std::string_view name, \ + AttributeElement expected_element, \ + AttributeUsage expected_usage, \ + size_t expected_channels, \ ResetToDefault reset_tag); #define LA_X_find_attribute_aux(_, ExpectedValueType) \ LA_SURFACE_MESH_X(find_attribute, ExpectedValueType) diff --git a/modules/core/src/internal/string_from_scalar.cpp b/modules/core/src/internal/string_from_scalar.cpp index b2823a59..a0c073dc 100644 --- a/modules/core/src/internal/string_from_scalar.cpp +++ b/modules/core/src/internal/string_from_scalar.cpp @@ -17,7 +17,7 @@ namespace lagrange::internal { #define LA_X_string_from_scalar(_, ValueType) \ template <> \ - std::string_view string_from_scalar() \ + LA_CORE_API std::string_view string_from_scalar() \ { \ return #ValueType; \ } diff --git a/modules/core/src/map_attribute.cpp b/modules/core/src/map_attribute.cpp index 239c536b..dca45891 100644 --- a/modules/core/src/map_attribute.cpp +++ b/modules/core/src/map_attribute.cpp @@ -305,21 +305,21 @@ AttributeId map_attribute_in_place( } #define LA_X_map_attribute(_, Scalar, Index) \ - template AttributeId map_attribute( \ + template LA_CORE_API AttributeId map_attribute( \ SurfaceMesh& mesh, \ AttributeId id, \ std::string_view new_name, \ AttributeElement new_element); \ - template AttributeId map_attribute( \ + template LA_CORE_API AttributeId map_attribute( \ SurfaceMesh& mesh, \ std::string_view old_name, \ std::string_view new_name, \ AttributeElement new_element); \ - template AttributeId map_attribute_in_place( \ + template LA_CORE_API AttributeId map_attribute_in_place( \ SurfaceMesh& mesh, \ AttributeId id, \ AttributeElement new_element); \ - template AttributeId map_attribute_in_place( \ + template LA_CORE_API AttributeId map_attribute_in_place( \ SurfaceMesh& mesh, \ std::string_view name, \ AttributeElement new_element); diff --git a/modules/core/src/mesh_cleanup/detect_degenerate_facets.cpp b/modules/core/src/mesh_cleanup/detect_degenerate_facets.cpp index d83cca10..6b8bf71b 100644 --- a/modules/core/src/mesh_cleanup/detect_degenerate_facets.cpp +++ b/modules/core/src/mesh_cleanup/detect_degenerate_facets.cpp @@ -127,7 +127,7 @@ std::vector detect_degenerate_facets(const SurfaceMesh& me } #define LA_X_detect_degenerate_facets(_, Scalar, Index) \ - template std::vector detect_degenerate_facets( \ + template LA_CORE_API std::vector detect_degenerate_facets( \ const SurfaceMesh&); LA_SURFACE_MESH_X(detect_degenerate_facets, 0) diff --git a/modules/core/src/mesh_cleanup/remove_duplicate_facets.cpp b/modules/core/src/mesh_cleanup/remove_duplicate_facets.cpp index 86c97d4f..2c6b1ff5 100644 --- a/modules/core/src/mesh_cleanup/remove_duplicate_facets.cpp +++ b/modules/core/src/mesh_cleanup/remove_duplicate_facets.cpp @@ -244,7 +244,7 @@ void remove_duplicate_facets( } #define LA_X_remove_duplicate_facets(_, Scalar, Index) \ - template void remove_duplicate_facets( \ + template LA_CORE_API void remove_duplicate_facets( \ SurfaceMesh&, \ const RemoveDuplicateFacetOptions&); LA_SURFACE_MESH_X(remove_duplicate_facets, 0) diff --git a/modules/core/src/mesh_cleanup/remove_duplicate_vertices.cpp b/modules/core/src/mesh_cleanup/remove_duplicate_vertices.cpp index 36bf6adb..21aa0ab7 100644 --- a/modules/core/src/mesh_cleanup/remove_duplicate_vertices.cpp +++ b/modules/core/src/mesh_cleanup/remove_duplicate_vertices.cpp @@ -175,7 +175,7 @@ void remove_duplicate_vertices( } #define LA_X_remove_duplicate_vertices(_, Scalar, Index) \ - template void remove_duplicate_vertices( \ + template LA_CORE_API void remove_duplicate_vertices( \ SurfaceMesh&, \ const RemoveDuplicateVerticesOptions&); LA_SURFACE_MESH_X(remove_duplicate_vertices, 0) diff --git a/modules/core/src/mesh_cleanup/remove_isolated_vertices.cpp b/modules/core/src/mesh_cleanup/remove_isolated_vertices.cpp index 935cfa9f..14538cb5 100644 --- a/modules/core/src/mesh_cleanup/remove_isolated_vertices.cpp +++ b/modules/core/src/mesh_cleanup/remove_isolated_vertices.cpp @@ -31,7 +31,7 @@ void remove_isolated_vertices(SurfaceMesh& mesh) } #define LA_X_remove_isolated_vertices(_, Scalar, Index) \ - template void remove_isolated_vertices(SurfaceMesh&); + template LA_CORE_API void remove_isolated_vertices(SurfaceMesh&); LA_SURFACE_MESH_X(remove_isolated_vertices, 0) } // namespace lagrange diff --git a/modules/core/src/mesh_cleanup/remove_null_area_facets.cpp b/modules/core/src/mesh_cleanup/remove_null_area_facets.cpp index 3c484aaf..e6d97b3d 100644 --- a/modules/core/src/mesh_cleanup/remove_null_area_facets.cpp +++ b/modules/core/src/mesh_cleanup/remove_null_area_facets.cpp @@ -33,7 +33,7 @@ void remove_null_area_facets( } #define LA_X_remove_null_area_facets(_, Scalar, Index) \ - template void remove_null_area_facets( \ + template LA_CORE_API void remove_null_area_facets( \ SurfaceMesh&, \ const RemoveNullAreaFacetsOptions&); LA_SURFACE_MESH_X(remove_null_area_facets, 0) diff --git a/modules/core/src/mesh_cleanup/remove_topologically_degenerate_facets.cpp b/modules/core/src/mesh_cleanup/remove_topologically_degenerate_facets.cpp index 0613289d..7f4278cc 100644 --- a/modules/core/src/mesh_cleanup/remove_topologically_degenerate_facets.cpp +++ b/modules/core/src/mesh_cleanup/remove_topologically_degenerate_facets.cpp @@ -38,7 +38,7 @@ void remove_topologically_degenerate_facets(SurfaceMesh& mesh) } #define LA_X_remove_topologically_degenerate_facets(_, Scalar, Index) \ - template void remove_topologically_degenerate_facets( \ + template LA_CORE_API void remove_topologically_degenerate_facets( \ SurfaceMesh&); LA_SURFACE_MESH_X(remove_topologically_degenerate_facets, 0) diff --git a/modules/core/src/normalize_meshes.cpp b/modules/core/src/normalize_meshes.cpp index 348c7c22..352d373e 100644 --- a/modules/core/src/normalize_meshes.cpp +++ b/modules/core/src/normalize_meshes.cpp @@ -58,7 +58,7 @@ void normalize_meshes(span*> meshes) } #define LA_X_normalize_meshes(_, Scalar, Index) \ - template void normalize_mesh(SurfaceMesh & mesh); \ - template void normalize_meshes(span*> meshes); + template LA_CORE_API void normalize_mesh(SurfaceMesh & mesh); \ + template LA_CORE_API void normalize_meshes(span*> meshes); LA_SURFACE_MESH_X(normalize_meshes, 0) } // namespace lagrange diff --git a/modules/core/src/permute_facets.cpp b/modules/core/src/permute_facets.cpp index 384f1407..7c375f79 100644 --- a/modules/core/src/permute_facets.cpp +++ b/modules/core/src/permute_facets.cpp @@ -100,7 +100,7 @@ void permute_facets(SurfaceMesh& mesh, span new_to_o } #define LA_X_permute_facets(_, Scalar, Index) \ - template void permute_facets(SurfaceMesh&, span); + template LA_CORE_API void permute_facets(SurfaceMesh&, span); LA_SURFACE_MESH_X(permute_facets, 0) } // namespace lagrange diff --git a/modules/core/src/permute_vertices.cpp b/modules/core/src/permute_vertices.cpp index 59265a93..049ea181 100644 --- a/modules/core/src/permute_vertices.cpp +++ b/modules/core/src/permute_vertices.cpp @@ -65,7 +65,7 @@ void permute_vertices(SurfaceMesh& mesh, span new_to } #define LA_X_permute_vertices(_, Scalar, Index) \ - template void permute_vertices(SurfaceMesh&, span); + template LA_CORE_API void permute_vertices(SurfaceMesh&, span); LA_SURFACE_MESH_X(permute_vertices, 0) } // namespace lagrange diff --git a/modules/core/src/remap_vertices.cpp b/modules/core/src/remap_vertices.cpp index 91743371..48a1038c 100644 --- a/modules/core/src/remap_vertices.cpp +++ b/modules/core/src/remap_vertices.cpp @@ -205,7 +205,7 @@ void remap_vertices( } #define LA_X_remap_vertices(_, Scalar, Index) \ - template void remap_vertices( \ + template LA_CORE_API void remap_vertices( \ SurfaceMesh&, \ span, \ RemapVerticesOptions); diff --git a/modules/core/src/separate_by_components.cpp b/modules/core/src/separate_by_components.cpp index b74447d8..529ee9d5 100644 --- a/modules/core/src/separate_by_components.cpp +++ b/modules/core/src/separate_by_components.cpp @@ -38,7 +38,7 @@ std::vector> separate_by_components( } #define LA_X_separate_by_components(_, Scalar, Index) \ - template std::vector> separate_by_components( \ + template LA_CORE_API std::vector> separate_by_components( \ const SurfaceMesh&, \ const SeparateByComponentsOptions&); diff --git a/modules/core/src/separate_by_facet_groups.cpp b/modules/core/src/separate_by_facet_groups.cpp index 1ad1c24f..47983eae 100644 --- a/modules/core/src/separate_by_facet_groups.cpp +++ b/modules/core/src/separate_by_facet_groups.cpp @@ -92,16 +92,16 @@ std::vector> separate_by_facet_groups( } #define LA_X_separate_by_facet_groups(_, Scalar, Index) \ - template std::vector> separate_by_facet_groups( \ + template LA_CORE_API std::vector> separate_by_facet_groups( \ const SurfaceMesh&, \ size_t, \ span, \ const SeparateByFacetGroupsOptions&); \ - template std::vector> separate_by_facet_groups( \ + template LA_CORE_API std::vector> separate_by_facet_groups( \ const SurfaceMesh&, \ span, \ const SeparateByFacetGroupsOptions&); \ - template std::vector> separate_by_facet_groups( \ + template LA_CORE_API std::vector> separate_by_facet_groups( \ const SurfaceMesh&, \ size_t, \ function_ref, \ diff --git a/modules/core/src/topology.cpp b/modules/core/src/topology.cpp index ef505250..b7fce930 100644 --- a/modules/core/src/topology.cpp +++ b/modules/core/src/topology.cpp @@ -181,9 +181,9 @@ bool is_edge_manifold(const SurfaceMesh& mesh) } #define LA_X_topology(_, Scalar, Index) \ - template int compute_euler(const SurfaceMesh& mesh); \ - template bool is_vertex_manifold(const SurfaceMesh& mesh); \ - template bool is_edge_manifold(const SurfaceMesh& mesh); + template LA_CORE_API int compute_euler(const SurfaceMesh& mesh); \ + template LA_CORE_API bool is_vertex_manifold(const SurfaceMesh& mesh); \ + template LA_CORE_API bool is_edge_manifold(const SurfaceMesh& mesh); LA_SURFACE_MESH_X(topology, 0) } // namespace lagrange diff --git a/modules/core/src/transform_mesh.cpp b/modules/core/src/transform_mesh.cpp index aa95f69c..bf0afaaa 100644 --- a/modules/core/src/transform_mesh.cpp +++ b/modules/core/src/transform_mesh.cpp @@ -195,19 +195,19 @@ SurfaceMesh transformed_mesh( } #define LA_X_transform_mesh(_, Scalar, Index) \ - template void transform_mesh( \ + template LA_CORE_API void transform_mesh( \ SurfaceMesh & mesh, \ const Eigen::Transform& transform, \ const TransformOptions& options); \ - template SurfaceMesh transformed_mesh( \ + template LA_CORE_API SurfaceMesh transformed_mesh( \ SurfaceMesh mesh, \ const Eigen::Transform& transform, \ const TransformOptions& options); \ - template void transform_mesh( \ + template LA_CORE_API void transform_mesh( \ SurfaceMesh & mesh, \ const Eigen::Transform& transform, \ const TransformOptions& options); \ - template SurfaceMesh transformed_mesh( \ + template LA_CORE_API SurfaceMesh transformed_mesh( \ SurfaceMesh mesh, \ const Eigen::Transform& transform, \ const TransformOptions& options); diff --git a/modules/core/src/triangulate_polygonal_facets.cpp b/modules/core/src/triangulate_polygonal_facets.cpp index 817aed58..507cdf18 100644 --- a/modules/core/src/triangulate_polygonal_facets.cpp +++ b/modules/core/src/triangulate_polygonal_facets.cpp @@ -308,7 +308,7 @@ void triangulate_polygonal_facets(SurfaceMesh& mesh) // Iterate over mesh (scalar, index) types #define LA_X_triangulate_polygonal_facets(_, Scalar, Index) \ - template void triangulate_polygonal_facets(SurfaceMesh& mesh); + template LA_CORE_API void triangulate_polygonal_facets(SurfaceMesh& mesh); LA_SURFACE_MESH_X(triangulate_polygonal_facets, 0) } // namespace lagrange diff --git a/modules/core/src/unify_index_buffer.cpp b/modules/core/src/unify_index_buffer.cpp index be115e3c..59ebcf58 100644 --- a/modules/core/src/unify_index_buffer.cpp +++ b/modules/core/src/unify_index_buffer.cpp @@ -67,6 +67,8 @@ SurfaceMesh unify_index_buffer( std::vector corner_groups(num_corners); std::vector corner_group_indices = {0}; corner_group_indices.reserve(num_vertices * 2); + std::vector isolated_vertices; + isolated_vertices.reserve(num_vertices); { std::vector vertex_corners(num_vertices + 1, 0); @@ -99,6 +101,9 @@ SurfaceMesh unify_index_buffer( for (auto vid : range(num_vertices)) { auto start_itr = std::next(corner_groups.begin(), vertex_corners[vid]); auto end_itr = std::next(corner_groups.begin(), vertex_corners[vid + 1]); + if (start_itr == end_itr) { + isolated_vertices.push_back(vid); + } for (auto itr = start_itr; itr != end_itr; itr++) { auto next_itr = std::next(itr); if (next_itr == end_itr || compare_corner(*itr, *next_itr) != EQUAL) { @@ -128,6 +133,16 @@ SurfaceMesh unify_index_buffer( } }); + // Copy over isolated vertices. + const size_t num_isolated_vertices = isolated_vertices.size(); + output_mesh.add_vertices( + static_cast(num_isolated_vertices), + [&](Index i, span p) { + Index vid = isolated_vertices[i]; + const auto q = mesh.get_position(vid); + std::copy(q.begin(), q.end(), p.begin()); + }); + // Map facets. auto num_facets = mesh.get_num_facets(); logger().debug("Unified index buffer: {} facets", num_facets); @@ -155,7 +170,7 @@ SurfaceMesh unify_index_buffer( attr.get_num_channels()); auto& out_attr = output_mesh.template ref_attribute(id); - out_attr.resize_elements(num_unique_corners); + out_attr.resize_elements(num_unique_corners + num_isolated_vertices); for (auto vid : range(num_unique_corners)) { auto cid = corner_groups[corner_group_indices[vid]]; auto prev_vid = mesh.get_corner_vertex(cid); @@ -163,6 +178,13 @@ SurfaceMesh unify_index_buffer( auto target_value = out_attr.ref_row(vid); std::copy(source_value.begin(), source_value.end(), target_value.begin()); } + for (size_t i = 0; i < num_isolated_vertices; i++) { + Index prev_vid = isolated_vertices[i]; + Index vid = static_cast(num_unique_corners + i); + auto source_value = attr.get_row(prev_vid); + auto target_value = out_attr.ref_row(vid); + std::copy(source_value.begin(), source_value.end(), target_value.begin()); + } }); // Map facet, corner and value attributes. @@ -266,10 +288,10 @@ SurfaceMesh unify_named_index_buffer( #define LA_X_unify_index_buffer(_, Scalar, Index) \ - template SurfaceMesh unify_index_buffer( \ + template LA_CORE_API SurfaceMesh unify_index_buffer( \ const SurfaceMesh& mesh, \ const std::vector& attribute_ids); \ - template SurfaceMesh unify_named_index_buffer( \ + template LA_CORE_API SurfaceMesh unify_named_index_buffer( \ const SurfaceMesh& mesh, \ const std::vector& attribute_names); LA_SURFACE_MESH_X(unify_index_buffer, 0) diff --git a/modules/core/src/utils/DisjointSets.cpp b/modules/core/src/utils/DisjointSets.cpp index 6928e207..a74579f6 100644 --- a/modules/core/src/utils/DisjointSets.cpp +++ b/modules/core/src/utils/DisjointSets.cpp @@ -91,7 +91,7 @@ size_t DisjointSets::extract_disjoint_set_indices(span ind return static_cast(counter); } -#define LA_X_DisjointSets(_, Index) template class DisjointSets; +#define LA_X_DisjointSets(_, Index) template class LA_CORE_API DisjointSets; LA_ATTRIBUTE_INDEX_X(DisjointSets, 0) } // namespace lagrange diff --git a/modules/core/src/utils/chain_edges.cpp b/modules/core/src/utils/chain_edges.cpp index 88d344ea..99fd9722 100644 --- a/modules/core/src/utils/chain_edges.cpp +++ b/modules/core/src/utils/chain_edges.cpp @@ -487,10 +487,10 @@ ChainEdgesResult chain_undirected_edges( } #define LA_X_chain_edges(_, Index) \ - template ChainEdgesResult chain_directed_edges( \ + template LA_CORE_API ChainEdgesResult chain_directed_edges( \ const span edges, \ const ChainEdgesOptions& options); \ - template ChainEdgesResult chain_undirected_edges( \ + template LA_CORE_API ChainEdgesResult chain_undirected_edges( \ const span edges, \ const ChainEdgesOptions& options); LA_ATTRIBUTE_INDEX_X(chain_edges, 0) diff --git a/modules/core/src/utils/triangle_uv_distortion.cpp b/modules/core/src/utils/triangle_uv_distortion.cpp index 5c790b96..3c18e393 100644 --- a/modules/core/src/utils/triangle_uv_distortion.cpp +++ b/modules/core/src/utils/triangle_uv_distortion.cpp @@ -291,42 +291,42 @@ Scalar triangle_uv_distortion( } #define LA_X_triangle_uv_distortion(_, Scalar) \ - template Scalar triangle_uv_distortion( \ + template LA_CORE_API Scalar triangle_uv_distortion( \ span, \ span, \ span, \ span, \ span, \ span); \ - template Scalar triangle_uv_distortion( \ + template LA_CORE_API Scalar triangle_uv_distortion( \ span, \ span, \ span, \ span, \ span, \ span); \ - template Scalar triangle_uv_distortion( \ + template LA_CORE_API Scalar triangle_uv_distortion( \ span, \ span, \ span, \ span, \ span, \ span); \ - template Scalar triangle_uv_distortion( \ + template LA_CORE_API Scalar triangle_uv_distortion( \ span, \ span, \ span, \ span, \ span, \ span); \ - template Scalar triangle_uv_distortion( \ + template LA_CORE_API Scalar triangle_uv_distortion( \ span, \ span, \ span, \ span, \ span, \ span); \ - template Scalar triangle_uv_distortion( \ + template LA_CORE_API Scalar triangle_uv_distortion( \ span, \ span, \ span, \ diff --git a/modules/core/src/views.cpp b/modules/core/src/views.cpp index bb568db8..89c6679c 100644 --- a/modules/core/src/views.cpp +++ b/modules/core/src/views.cpp @@ -182,42 +182,42 @@ ConstRowMatrixView facet_view(const SurfaceMesh& mesh) // Iterate over attribute types #define LA_X_views_attr(_, ValueType) \ - template RowMatrixView matrix_ref(Attribute& attribute); \ - template ConstRowMatrixView matrix_view(const Attribute& attribute); \ - template VectorView vector_ref(Attribute& attribute); \ - template ConstVectorView vector_view(const Attribute& attribute); \ - template RowMatrixView reshaped_ref( \ + template LA_CORE_API RowMatrixView matrix_ref(Attribute& attribute); \ + template LA_CORE_API ConstRowMatrixView matrix_view(const Attribute& attribute); \ + template LA_CORE_API VectorView vector_ref(Attribute& attribute); \ + template LA_CORE_API ConstVectorView vector_view(const Attribute& attribute); \ + template LA_CORE_API RowMatrixView reshaped_ref( \ Attribute& attribute, \ size_t num_cols); \ - template ConstRowMatrixView reshaped_view( \ + template LA_CORE_API ConstRowMatrixView reshaped_view( \ const Attribute& attribute, \ size_t num_cols); LA_ATTRIBUTE_X(views_attr, 0) // Iterate over attribute types x mesh (scalar, index) types #define LA_X_views_mesh_attr(ValueType, Scalar, Index) \ - template RowMatrixView attribute_matrix_ref( \ + template LA_CORE_API RowMatrixView attribute_matrix_ref( \ SurfaceMesh& mesh, \ std::string_view name); \ - template ConstRowMatrixView attribute_matrix_view( \ + template LA_CORE_API ConstRowMatrixView attribute_matrix_view( \ const SurfaceMesh& mesh, \ std::string_view name); \ - template VectorView attribute_vector_ref( \ + template LA_CORE_API VectorView attribute_vector_ref( \ SurfaceMesh& mesh, \ std::string_view name); \ - template ConstVectorView attribute_vector_view( \ + template LA_CORE_API ConstVectorView attribute_vector_view( \ const SurfaceMesh& mesh, \ std::string_view name); \ - template RowMatrixView attribute_matrix_ref( \ + template LA_CORE_API RowMatrixView attribute_matrix_ref( \ SurfaceMesh& mesh, \ AttributeId id); \ - template ConstRowMatrixView attribute_matrix_view( \ + template LA_CORE_API ConstRowMatrixView attribute_matrix_view( \ const SurfaceMesh& mesh, \ AttributeId id); \ - template VectorView attribute_vector_ref( \ + template LA_CORE_API VectorView attribute_vector_ref( \ SurfaceMesh& mesh, \ AttributeId id); \ - template ConstVectorView attribute_vector_view( \ + template LA_CORE_API ConstVectorView attribute_vector_view( \ const SurfaceMesh& mesh, \ AttributeId id); #define LA_X_views_aux(_, ValueType) LA_SURFACE_MESH_X(views_mesh_attr, ValueType) @@ -225,10 +225,10 @@ LA_ATTRIBUTE_X(views_aux, 0) // Iterate over mesh (scalar, index) types #define LA_X_views_mesh(_, Scalar, Index) \ - template RowMatrixView vertex_ref(SurfaceMesh& mesh); \ - template ConstRowMatrixView vertex_view(const SurfaceMesh& mesh); \ - template RowMatrixView facet_ref(SurfaceMesh& mesh); \ - template ConstRowMatrixView facet_view(const SurfaceMesh& mesh); + template LA_CORE_API RowMatrixView vertex_ref(SurfaceMesh& mesh); \ + template LA_CORE_API ConstRowMatrixView vertex_view(const SurfaceMesh& mesh); \ + template LA_CORE_API RowMatrixView facet_ref(SurfaceMesh& mesh); \ + template LA_CORE_API ConstRowMatrixView facet_view(const SurfaceMesh& mesh); LA_SURFACE_MESH_X(views_mesh, 0) } // namespace lagrange diff --git a/modules/core/src/weld_indexed_attribute.cpp b/modules/core/src/weld_indexed_attribute.cpp index a145e7b1..ea4a6fa8 100644 --- a/modules/core/src/weld_indexed_attribute.cpp +++ b/modules/core/src/weld_indexed_attribute.cpp @@ -141,9 +141,8 @@ void weld_indexed_attribute(SurfaceMesh& mesh, AttributeId attr_i } #define LA_X_weld_indexed_attribute(ValueType, Scalar, Index) \ - template void weld_indexed_attribute(SurfaceMesh&, AttributeId); + template LA_CORE_API void weld_indexed_attribute(SurfaceMesh&, AttributeId); LA_SURFACE_MESH_X(weld_indexed_attribute, 0) - } // namespace lagrange diff --git a/modules/core/tests/compile_errors/CMakeLists.txt b/modules/core/tests/compile_errors/CMakeLists.txt index ddc5ae9a..82bfdd22 100644 --- a/modules/core/tests/compile_errors/CMakeLists.txt +++ b/modules/core/tests/compile_errors/CMakeLists.txt @@ -10,8 +10,10 @@ # governing permissions and limitations under the License. # lagrange_add_compile_test(test_assert.cpp) +lagrange_add_compile_test(test_attribute_type.cpp) lagrange_add_compile_test(test_mesh_attribute.cpp) lagrange_add_compile_test(test_mesh_convert1.cpp) lagrange_add_compile_test(test_mesh_convert2.cpp) +lagrange_add_compile_test(test_mesh_type.cpp) lagrange_add_compile_test(test_pimpl.cpp) lagrange_add_compile_test(test_safe_cast.cpp) diff --git a/modules/io/tests/test_load_mesh_obj.cpp b/modules/core/tests/compile_errors/test_attribute_type.cpp similarity index 63% rename from modules/io/tests/test_load_mesh_obj.cpp rename to modules/core/tests/compile_errors/test_attribute_type.cpp index bee7eb6c..4e4a6880 100644 --- a/modules/io/tests/test_load_mesh_obj.cpp +++ b/modules/core/tests/compile_errors/test_attribute_type.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2023 Adobe. All rights reserved. + * Copyright 2024 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 @@ -9,13 +9,12 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ -#include -#include +#include -#include - -TEST_CASE("Grenade_H", "[mesh][io]" LA_CORP_FLAG) +void test_mesh_type() { - using namespace lagrange; - auto mesh = io::load_mesh_obj(testing::get_data_path("corp/io/Grenade_H.obj")); + lagrange::Attribute attr( + lagrange::AttributeElement::Vertex, + lagrange::AttributeUsage::Vector, + 1); } diff --git a/modules/scene/src/SceneExtension.cpp b/modules/core/tests/compile_errors/test_mesh_type.cpp similarity index 77% rename from modules/scene/src/SceneExtension.cpp rename to modules/core/tests/compile_errors/test_mesh_type.cpp index 0b6ae2bb..edee8e2e 100644 --- a/modules/scene/src/SceneExtension.cpp +++ b/modules/core/tests/compile_errors/test_mesh_type.cpp @@ -9,13 +9,9 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ -#include -#include +#include -namespace lagrange { -namespace scene { - -Value::Value(){}; - -} // namespace scene -} // namespace lagrange +void test_mesh_type() +{ + lagrange::SurfaceMesh mesh; +} diff --git a/modules/core/tests/test_compute_greedy_coloring.cpp b/modules/core/tests/test_compute_greedy_coloring.cpp new file mode 100644 index 00000000..4a1a2293 --- /dev/null +++ b/modules/core/tests/test_compute_greedy_coloring.cpp @@ -0,0 +1,57 @@ +/* + * Copyright 2024 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. + */ +#include +#include +#include +#include + +#include +#include +#include + +#include + +TEST_CASE("compute_greedy_coloring", "[core][coloring]") +{ + using Scalar = double; + using Index = uint32_t; + + SECTION("facet coloring") + { + auto mesh = lagrange::testing::create_test_cube(); + auto color_id = lagrange::compute_greedy_coloring(mesh); + auto& color_attr = mesh.get_attribute(color_id); + REQUIRE(color_attr.get_element_type() == lagrange::AttributeElement::Facet); + auto colors = matrix_view(color_attr); + Eigen::VectorX expected(mesh.get_num_facets()); + expected << 1, 0, 1, 0, 3, 6, 5, 3, 2, 6, 6, 0; + CAPTURE(colors); + CHECK(colors.maxCoeff() + 1 <= 7); + CHECK((colors.array() == expected.array()).all()); + } + + SECTION("vertex coloring") + { + auto mesh = lagrange::testing::create_test_cube(); + lagrange::GreedyColoringOptions options; + options.element_type = lagrange::AttributeElement::Vertex; + auto color_id = lagrange::compute_greedy_coloring(mesh, options); + auto& color_attr = mesh.get_attribute(color_id); + REQUIRE(color_attr.get_element_type() == lagrange::AttributeElement::Vertex); + auto colors = matrix_view(color_attr); + Eigen::VectorX expected(mesh.get_num_vertices()); + expected << 1, 0, 5, 6, 6, 5, 2, 0; + CAPTURE(colors); + CHECK(colors.maxCoeff() + 1 <= 7); + CHECK((colors.array() == expected.array()).all()); + } +} diff --git a/modules/core/tests/test_compute_seam_edges.cpp b/modules/core/tests/test_compute_seam_edges.cpp new file mode 100644 index 00000000..c189a2c6 --- /dev/null +++ b/modules/core/tests/test_compute_seam_edges.cpp @@ -0,0 +1,70 @@ +/* + * Copyright 2024 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. + */ +#include +#include + +#include +#include +#include +#include + +using Scalar = double; +using Index = uint32_t; + +namespace { + +size_t count_seam_edges(const lagrange::SurfaceMesh& mesh, lagrange::AttributeId id) +{ + const auto& seam_edges = mesh.get_attribute(id).get_all(); + return std::count(seam_edges.begin(), seam_edges.end(), uint8_t(1)); +} + +} // namespace + +TEST_CASE("compute_seam_edges", "[core][seam]") +{ + SECTION("Cube") + { + lagrange::testing::CreateOptions options; + options.with_indexed_uv = true; + options.with_indexed_normal = true; + auto mesh = lagrange::testing::create_test_cube(options); + + auto uv_seam_id = lagrange::compute_seam_edges( + mesh, + mesh.get_attribute_id(lagrange::AttributeName::texcoord)); + REQUIRE(count_seam_edges(mesh, uv_seam_id) == 7); + + lagrange::NormalOptions normal_options; + auto normal_seam_id = lagrange::compute_seam_edges( + mesh, + mesh.get_attribute_id(normal_options.output_attribute_name)); + REQUIRE(count_seam_edges(mesh, normal_seam_id) == 12); + } + + SECTION("Sphere") + { + lagrange::testing::CreateOptions options; + options.with_indexed_uv = true; + options.with_indexed_normal = false; + auto mesh = lagrange::testing::create_test_sphere(options); + auto normal_id = lagrange::compute_normal(mesh, static_cast(M_PI / 4)); + + auto uv_seam_id = lagrange::compute_seam_edges( + mesh, + mesh.get_attribute_id(lagrange::AttributeName::texcoord)); + REQUIRE(count_seam_edges(mesh, uv_seam_id) == 22); + + auto normal_seam_id = lagrange::compute_seam_edges(mesh, normal_id); + REQUIRE(count_seam_edges(mesh, normal_seam_id) == 0); + } +} diff --git a/modules/core/tests/test_filter_cast.cpp b/modules/core/tests/test_filter_cast.cpp index e3c801af..3399ef7e 100644 --- a/modules/core/tests/test_filter_cast.cpp +++ b/modules/core/tests/test_filter_cast.cpp @@ -12,8 +12,10 @@ #include #include +#include #include #include +#include #include #include #include @@ -193,6 +195,34 @@ bool is_same_addr( return reinterpret_cast(&attr_a) == reinterpret_cast(&attr_b); } +template +bool is_same_addr( + const lagrange::SurfaceMesh& mesh, + std::string_view name_a, + std::string_view name_b) +{ + auto& attr_a = mesh.get_attribute_base(name_a); + auto& attr_b = mesh.get_attribute_base(name_b); + return reinterpret_cast(&attr_a) == reinterpret_cast(&attr_b); +} + +template +bool is_same_ptr( + const lagrange::SurfaceMesh& mesh, + std::string_view name, + const void* ptr) +{ + auto& attr = mesh.get_attribute_base(name); + return reinterpret_cast(&attr) == ptr; +} + +template +const void* get_addr(const lagrange::SurfaceMesh& mesh, std::string_view name) +{ + auto& attr = mesh.get_attribute_base(name); + return reinterpret_cast(&attr); +} + } // namespace TEST_CASE("cast address", "[core][surface][cast]") @@ -254,8 +284,12 @@ TEST_CASE("cast address", "[core][surface][cast]") REQUIRE(!is_same_addr(mesh, mesh2, positions)); REQUIRE(is_same_addr(mesh, mesh2, indices)); REQUIRE(!is_same_addr(mesh, mesh2, uvs)); + const auto& attr_pos2 = mesh2.get_attribute(positions); const auto& attr_uv = mesh.get_indexed_attribute(uvs); const auto& attr_uv2 = mesh2.get_indexed_attribute(uvs); + // Positions should be the same either way + REQUIRE(vertex_view(mesh).cast() == matrix_view(attr_pos2)); + REQUIRE(vertex_view(mesh) == matrix_view(attr_pos2).cast()); // Casting to float should yield the same value REQUIRE( matrix_view(attr_uv.values()).cast() == matrix_view(attr_uv2.values())); @@ -270,8 +304,12 @@ TEST_CASE("cast address", "[core][surface][cast]") REQUIRE(!is_same_addr(mesh, mesh2, positions)); REQUIRE(!is_same_addr(mesh, mesh2, indices)); REQUIRE(!is_same_addr(mesh, mesh2, uvs)); + const auto& attr_pos2 = mesh2.get_attribute(positions); const auto& attr_uv = mesh.get_indexed_attribute(uvs); const auto& attr_uv2 = mesh2.get_indexed_attribute(uvs); + // Positions should be the same either way + REQUIRE(vertex_view(mesh).cast() == matrix_view(attr_pos2)); + REQUIRE(vertex_view(mesh) == matrix_view(attr_pos2).cast()); // Casting to float should yield the same value REQUIRE( matrix_view(attr_uv.values()).cast() == matrix_view(attr_uv2.values())); @@ -279,6 +317,24 @@ TEST_CASE("cast address", "[core][surface][cast]") REQUIRE(matrix_view(attr_uv.values()) != matrix_view(attr_uv2.values()).cast()); REQUIRE(matrix_view(attr_uv.indices()) == matrix_view(attr_uv2.indices()).cast()); } + + SECTION("different scalar, cast attr") + { + auto positions2_id = lagrange::cast_attribute(mesh, positions, "positions2"); + auto uv2_id = lagrange::cast_attribute(mesh, uvs, "uv2"); + const auto& attr_pos2 = mesh.get_attribute(positions2_id); + const auto& attr_uv = mesh.get_indexed_attribute(uvs); + const auto& attr_uv2 = mesh.get_indexed_attribute(uv2_id); + // Positions should be the same either way + REQUIRE(vertex_view(mesh).cast() == matrix_view(attr_pos2)); + REQUIRE(vertex_view(mesh) == matrix_view(attr_pos2).cast()); + // Casting to float should yield the same value + REQUIRE( + matrix_view(attr_uv.values()).cast() == matrix_view(attr_uv2.values())); + // Casting to double should yield different value + REQUIRE(matrix_view(attr_uv.values()) != matrix_view(attr_uv2.values()).cast()); + REQUIRE(matrix_view(attr_uv.indices()) == matrix_view(attr_uv2.indices())); + } } TEST_CASE("cast external", "[core][surface][cast]") @@ -325,7 +381,7 @@ TEST_CASE("cast external", "[core][surface][cast]") 1, colors_view); - SECTION("same scalar") + SECTION("same scalar, cast mesh") { auto mesh2 = lagrange::cast(mesh); @@ -354,32 +410,157 @@ TEST_CASE("cast external", "[core][surface][cast]") REQUIRE(!colors_sh.is_read_only()); } - SECTION("different scalar") + SECTION("same scalar, cast attr") + { + lagrange::cast_attribute(mesh, "colors", "colors2"); + const auto& colors = mesh.get_attribute("colors2"); + REQUIRE(is_same_addr(mesh, "colors", "colors2")); + REQUIRE(!colors.is_external()); + REQUIRE(colors.is_managed()); + REQUIRE(!colors.is_read_only()); + + lagrange::cast_attribute(mesh, "colors_rw", "colors_rw2"); + const auto& colors_rw = mesh.get_attribute("colors_rw2"); + REQUIRE(is_same_addr(mesh, "colors_rw", "colors_rw2")); + REQUIRE(colors_rw.is_external()); + REQUIRE(!colors_rw.is_managed()); + REQUIRE(!colors_rw.is_read_only()); + + lagrange::cast_attribute(mesh, "colors_ro", "colors_ro2"); + const auto& colors_ro = mesh.get_attribute("colors_ro2"); + REQUIRE(is_same_addr(mesh, "colors_ro", "colors_ro2")); + REQUIRE(colors_ro.is_external()); + REQUIRE(!colors_ro.is_managed()); + REQUIRE(colors_ro.is_read_only()); + + lagrange::cast_attribute(mesh, "colors_sh", "colors_sh2"); + const auto& colors_sh = mesh.get_attribute("colors_sh2"); + REQUIRE(is_same_addr(mesh, "colors_sh", "colors_sh2")); + REQUIRE(colors_sh.is_external()); + REQUIRE(colors_sh.is_managed()); + REQUIRE(!colors_sh.is_read_only()); + } + + SECTION("same scalar, cast attr in place") + { + auto colors_ptr = get_addr(mesh, "colors"); + lagrange::cast_attribute_in_place(mesh, "colors"); + const auto& colors = mesh.get_attribute("colors"); + REQUIRE(is_same_ptr(mesh, "colors", colors_ptr)); + REQUIRE(!colors.is_external()); + REQUIRE(colors.is_managed()); + REQUIRE(!colors.is_read_only()); + + auto colors_rw_ptr = get_addr(mesh, "colors_rw"); + lagrange::cast_attribute_in_place(mesh, "colors_rw"); + const auto& colors_rw = mesh.get_attribute("colors_rw"); + REQUIRE(is_same_ptr(mesh, "colors_rw", colors_rw_ptr)); + REQUIRE(colors_rw.is_external()); + REQUIRE(!colors_rw.is_managed()); + REQUIRE(!colors_rw.is_read_only()); + + auto colors_ro_ptr = get_addr(mesh, "colors_ro"); + lagrange::cast_attribute_in_place(mesh, "colors_ro"); + const auto& colors_ro = mesh.get_attribute("colors_ro"); + REQUIRE(is_same_ptr(mesh, "colors_ro", colors_ro_ptr)); + REQUIRE(colors_ro.is_external()); + REQUIRE(!colors_ro.is_managed()); + REQUIRE(colors_ro.is_read_only()); + + auto colors_sh_ptr = get_addr(mesh, "colors_sh"); + lagrange::cast_attribute_in_place(mesh, "colors_sh"); + const auto& colors_sh = mesh.get_attribute("colors_sh"); + REQUIRE(is_same_ptr(mesh, "colors_sh", colors_sh_ptr)); + REQUIRE(colors_sh.is_external()); + REQUIRE(colors_sh.is_managed()); + REQUIRE(!colors_sh.is_read_only()); + } + + SECTION("different scalar, cast mesh") { auto mesh2 = lagrange::cast(mesh); - const auto& colors = mesh2.get_attribute("colors"); + const auto& colors = mesh2.get_attribute("colors"); REQUIRE(!is_same_addr(mesh, mesh2, "colors")); REQUIRE(!colors.is_external()); REQUIRE(colors.is_managed()); REQUIRE(!colors.is_read_only()); - const auto& colors_rw = mesh2.get_attribute("colors_rw"); + const auto& colors_rw = mesh2.get_attribute("colors_rw"); REQUIRE(!is_same_addr(mesh, mesh2, "colors_rw")); REQUIRE(!colors_rw.is_external()); REQUIRE(colors_rw.is_managed()); REQUIRE(!colors_rw.is_read_only()); - const auto& colors_ro = mesh2.get_attribute("colors_ro"); + const auto& colors_ro = mesh2.get_attribute("colors_ro"); REQUIRE(!is_same_addr(mesh, mesh2, "colors_ro")); REQUIRE(!colors_ro.is_external()); REQUIRE(colors_ro.is_managed()); REQUIRE(!colors_ro.is_read_only()); - const auto& colors_sh = mesh2.get_attribute("colors_sh"); + const auto& colors_sh = mesh2.get_attribute("colors_sh"); REQUIRE(!is_same_addr(mesh, mesh2, "colors_sh")); REQUIRE(!colors_sh.is_external()); REQUIRE(colors_sh.is_managed()); REQUIRE(!colors_sh.is_read_only()); } + + SECTION("different scalar, cast attr") + { + lagrange::cast_attribute(mesh, "colors", "colors2"); + const auto& colors = mesh.get_attribute("colors2"); + REQUIRE(!is_same_addr(mesh, "colors", "colors2")); + REQUIRE(!colors.is_external()); + REQUIRE(colors.is_managed()); + REQUIRE(!colors.is_read_only()); + + lagrange::cast_attribute(mesh, "colors_rw", "colors_rw2"); + const auto& colors_rw = mesh.get_attribute("colors_rw2"); + REQUIRE(!is_same_addr(mesh, "colors_rw", "colors_rw2")); + REQUIRE(!colors_rw.is_external()); + REQUIRE(colors_rw.is_managed()); + REQUIRE(!colors_rw.is_read_only()); + + lagrange::cast_attribute(mesh, "colors_ro", "colors_ro2"); + const auto& colors_ro = mesh.get_attribute("colors_ro2"); + REQUIRE(!is_same_addr(mesh, "colors_ro", "colors_ro2")); + REQUIRE(!colors_ro.is_external()); + REQUIRE(colors_ro.is_managed()); + REQUIRE(!colors_ro.is_read_only()); + + lagrange::cast_attribute(mesh, "colors_sh", "colors_sh2"); + const auto& colors_sh = mesh.get_attribute("colors_sh2"); + REQUIRE(!is_same_addr(mesh, "colors_sh", "colors_sh2")); + REQUIRE(!colors_sh.is_external()); + REQUIRE(colors_sh.is_managed()); + REQUIRE(!colors_sh.is_read_only()); + } + + SECTION("different scalar, cast attr in place") + { + // The Attribute object address could be the same, or different... no guarantee in this case + lagrange::cast_attribute_in_place(mesh, "colors"); + const auto& colors = mesh.get_attribute("colors"); + REQUIRE(!colors.is_external()); + REQUIRE(colors.is_managed()); + REQUIRE(!colors.is_read_only()); + + lagrange::cast_attribute_in_place(mesh, "colors_rw"); + const auto& colors_rw = mesh.get_attribute("colors_rw"); + REQUIRE(!colors_rw.is_external()); + REQUIRE(colors_rw.is_managed()); + REQUIRE(!colors_rw.is_read_only()); + + lagrange::cast_attribute_in_place(mesh, "colors_ro"); + const auto& colors_ro = mesh.get_attribute("colors_ro"); + REQUIRE(!colors_ro.is_external()); + REQUIRE(colors_ro.is_managed()); + REQUIRE(!colors_ro.is_read_only()); + + lagrange::cast_attribute_in_place(mesh, "colors_sh"); + const auto& colors_sh = mesh.get_attribute("colors_sh"); + REQUIRE(!colors_sh.is_external()); + REQUIRE(colors_sh.is_managed()); + REQUIRE(!colors_sh.is_read_only()); + } } diff --git a/modules/core/tests/test_unify_index_buffer.cpp b/modules/core/tests/test_unify_index_buffer.cpp index fa8f3479..98d71b15 100644 --- a/modules/core/tests/test_unify_index_buffer.cpp +++ b/modules/core/tests/test_unify_index_buffer.cpp @@ -502,4 +502,23 @@ TEST_CASE("unify_index_buffer", "[attribute][next][unify]") validate_edge_lengths(mesh2); } } + + SECTION("Isolated vertices") + { + lagrange::SurfaceMesh mesh = generate_rectangle(); + mesh.add_vertex({-1, -1, -1}); // isolated vertex + REQUIRE(mesh.get_num_vertices() == 7); + + std::vector vertex_ids = {0, 1, 2, 3, 4, 5, 6}; + mesh.create_attribute("vertex_id", Vertex, AttributeUsage::Scalar, 1, vertex_ids); + + std::vector values = {0, 1, 2}; + std::vector indices = {0, 0, 0, 1, 1, 1, 2, 2, 2, 2}; + add_indexed_attribute(mesh, "facet_id", values, indices); + + auto mesh2 = unify_index_buffer(mesh); + REQUIRE(mesh2.get_num_vertices() == 11); + mesh2.initialize_edges(); + check_for_consistency(mesh, mesh2); + } } diff --git a/modules/fs/CMakeLists.txt b/modules/fs/CMakeLists.txt index 840f90d1..da952539 100644 --- a/modules/fs/CMakeLists.txt +++ b/modules/fs/CMakeLists.txt @@ -45,13 +45,7 @@ else() message(FATAL_ERROR "Unsupported LAGRANGE_FS_BACKEND: ${LAGRANGE_FS_BACKEND}!") endif() -# 3. installation -if(LAGRANGE_INSTALL) - set_target_properties(lagrange_fs PROPERTIES EXPORT_NAME fs) - lagrange_install(lagrange_fs) -endif() - -# 4. unit tests and examples +# 3. unit tests and examples if(LAGRANGE_UNIT_TESTS) add_subdirectory(tests) endif() diff --git a/modules/fs/include/lagrange/fs/api.h b/modules/fs/include/lagrange/fs/api.h new file mode 100644 index 00000000..30acaf35 --- /dev/null +++ b/modules/fs/include/lagrange/fs/api.h @@ -0,0 +1,23 @@ +#pragma once + +#ifdef LA_FS_STATIC_DEFINE + #define LA_FS_API +#else + #ifndef LA_FS_API + #ifdef lagrange_fs_EXPORTS + // We are building this library + #if defined(_WIN32) || defined(_WIN64) + #define LA_FS_API __declspec(dllexport) + #else + #define LA_FS_API __attribute__((visibility("default"))) + #endif + #else + // We are using this library + #if defined(_WIN32) || defined(_WIN64) + #define LA_FS_API __declspec(dllimport) + #else + #define LA_FS_API __attribute__((visibility("default"))) + #endif + #endif + #endif +#endif diff --git a/modules/fs/include/lagrange/fs/file_utils.h b/modules/fs/include/lagrange/fs/file_utils.h index 1afe50ac..c3d60625 100644 --- a/modules/fs/include/lagrange/fs/file_utils.h +++ b/modules/fs/include/lagrange/fs/file_utils.h @@ -11,38 +11,40 @@ */ #pragma once +#include +#include + #include #include -#include - namespace lagrange { namespace fs { /// Helpers for when you don't want to use fs::path type. /// (You should use fs::path instead and avoid calling these) -std::string get_filename_extension(const std::string& filename); +LA_FS_API std::string get_filename_extension(const std::string& filename); -std::string get_base_dir(const std::string& filename); +LA_FS_API std::string get_base_dir(const std::string& filename); /// Ensures that the string ends with suffix. Will append suffix if needed. -std::string get_string_ending_with(const std::string& str, const char* suffix); +LA_FS_API std::string get_string_ending_with(const std::string& str, const char* suffix); -std::string read_file_to_string(const path& path); -std::string read_file_with_includes(const path& search_dir, const path& filepath); -std::string read_file_with_includes( - const path& filepath, const std::unordered_map& virtual_fs); +LA_FS_API std::string read_file_to_string(const path& path); +LA_FS_API std::string read_file_with_includes(const path& search_dir, const path& filepath); +LA_FS_API std::string read_file_with_includes( + const path& filepath, + const std::unordered_map& virtual_fs); // The executable is not present in WebAssembly's virtual file system, so it does not make sense // to provide get_executable_path() or get_executable_directory() when building with Emscripten. #if !defined(__EMSCRIPTEN__) -path get_executable_path(); +LA_FS_API path get_executable_path(); -path get_executable_directory(); +LA_FS_API path get_executable_directory(); #endif /// cwd -path get_current_working_directory(); +LA_FS_API path get_current_working_directory(); } // namespace fs } // namespace lagrange diff --git a/modules/fs/tests/test_file_utils.cpp b/modules/fs/tests/test_file_utils.cpp index 63c9a6d8..4e4d4341 100644 --- a/modules/fs/tests/test_file_utils.cpp +++ b/modules/fs/tests/test_file_utils.cpp @@ -29,7 +29,9 @@ TEST_CASE("file_utils", "[io]") CAPTURE(exec_path, exec_dir); REQUIRE(exec_path.parent_path() == exec_dir); - REQUIRE(exec_path == fs::path(TEST_APP_PATH)); + #ifndef TEST_APP_NOPATH + REQUIRE(exec_path == fs::path(TEST_APP_PATH)); + #endif #endif // The following test should succeed if the unit test is run by ctest, which calls the diff --git a/modules/image/CMakeLists.txt b/modules/image/CMakeLists.txt index b94eb8c4..9d1d1974 100644 --- a/modules/image/CMakeLists.txt +++ b/modules/image/CMakeLists.txt @@ -20,13 +20,7 @@ target_link_libraries(lagrange_image PUBLIC lagrange::core ) -# 3. installation -if(LAGRANGE_INSTALL) - set_target_properties(lagrange_image PROPERTIES EXPORT_NAME image) - lagrange_install(lagrange_image) -endif() - -# 4. unit tests and examples +# 3. unit tests and examples if(LAGRANGE_UNIT_TESTS) add_subdirectory(tests) endif() diff --git a/modules/image/include/lagrange/image/ImageType.h b/modules/image/include/lagrange/image/ImageType.h index 7e28864e..ef4a85a8 100644 --- a/modules/image/include/lagrange/image/ImageType.h +++ b/modules/image/include/lagrange/image/ImageType.h @@ -12,6 +12,7 @@ #pragma once #include + #include #include diff --git a/modules/image/include/lagrange/image/RawInputImage.h b/modules/image/include/lagrange/image/RawInputImage.h index b8c13dd9..58e8bdf7 100644 --- a/modules/image/include/lagrange/image/RawInputImage.h +++ b/modules/image/include/lagrange/image/RawInputImage.h @@ -12,6 +12,7 @@ #pragma once #include +#include #include #include @@ -32,7 +33,7 @@ namespace image { /// * get_pixel_data can pointer to external memory without ownership, or inside m_local_pixel_data with ownership /// if set_pixel_data(*, false), m_pixel_data points to external memory, m_local_pixel_data is empty /// if set_pixel_data(*, true), m_pixel_data is nullptr, m_local_pixel_data holds the pixels -class RawInputImage +class LA_IMAGE_API RawInputImage { public: enum class precision_semantic : uint32_t { @@ -274,11 +275,11 @@ void serialize(lagrange::image::RawInputImage& image, Archive& ar) } /// Wrap a void * data into a Linear 4-component image. Pixel memory ownership is not transferred. -RawInputImage +LA_IMAGE_API RawInputImage make_default_rgba_image(std::size_t i_width, std::size_t i_height, const void* i_pixels); /// Wrap a void * data into a Linear 1-component image. Pixel memory ownership is not transferred. -RawInputImage +LA_IMAGE_API RawInputImage make_default_luminance_image(std::size_t i_width, std::size_t i_height, const void* i_pixels); /// wrap uv coordinate diff --git a/modules/image/include/lagrange/image/api.h b/modules/image/include/lagrange/image/api.h new file mode 100644 index 00000000..c00da146 --- /dev/null +++ b/modules/image/include/lagrange/image/api.h @@ -0,0 +1,23 @@ +#pragma once + +#ifdef LA_IMAGE_STATIC_DEFINE + #define LA_IMAGE_API +#else + #ifndef LA_IMAGE_API + #ifdef lagrange_image_EXPORTS + // We are building this library + #if defined(_WIN32) || defined(_WIN64) + #define LA_IMAGE_API __declspec(dllexport) + #else + #define LA_IMAGE_API __attribute__((visibility("default"))) + #endif + #else + // We are using this library + #if defined(_WIN32) || defined(_WIN64) + #define LA_IMAGE_API __declspec(dllimport) + #else + #define LA_IMAGE_API __attribute__((visibility("default"))) + #endif + #endif + #endif +#endif diff --git a/modules/image/include/lagrange/image/image_sampling.h b/modules/image/include/lagrange/image/image_sampling.h index 6c9f1983..14a89720 100644 --- a/modules/image/include/lagrange/image/image_sampling.h +++ b/modules/image/include/lagrange/image/image_sampling.h @@ -13,6 +13,7 @@ #include #include +#include namespace lagrange { namespace image { @@ -28,13 +29,13 @@ namespace image { * may may be smaller. * @param[out] samples The resulting samples. */ -void sample_from_density_map( +LA_IMAGE_API void sample_from_density_map( const image::ImageView& density_map, size_t n_samples, lagrange::Vertices2Df& samples); /** - * Type of sampling to use + * Type of sampling to use */ enum class SampleType { @@ -43,19 +44,19 @@ enum class SampleType }; /** - * Populates a list of \p n_samples samples with samples along the image borders. - * + * Populates a list of \p n_samples samples with samples along the image borders. + * * There are two sampling modes: * SampleType::Density: sample according to density map. * SampleType::Regular: sample regularly (ignores density). - * + * * @param[in] density_map The input density map. * @param[in] n_samples The approximate number of samples to generate. The actual number of samples * may be smaller. * @param[out] samples The resulting samples. * @param[in] type The sampling type. Default is SampleType::Regular. */ -void sample_borders( +LA_IMAGE_API void sample_borders( const image::ImageView& density_map, size_t n_samples, lagrange::Vertices2Df& samples, @@ -70,7 +71,7 @@ void sample_borders( * may be smaller. * @param[out] samples The resulting samples. */ -void density_sample_borders( +LA_IMAGE_API void density_sample_borders( const image::ImageView& density_map, size_t n_samples, lagrange::Vertices2Df& samples); @@ -86,7 +87,7 @@ void density_sample_borders( * may be smaller. * @param[out] samples The resulting samples. */ -void regular_sample_borders( +LA_IMAGE_API void regular_sample_borders( const image::ImageView& density_map, size_t n_samples, lagrange::Vertices2Df& samples); @@ -99,7 +100,7 @@ void regular_sample_borders( * @param y y coordinate * @return float value at (x, y) bilinearly interpolated from the image */ -float bilinear_interpolation(const image::ImageView& image, float x, float y); +LA_IMAGE_API float bilinear_interpolation(const image::ImageView& image, float x, float y); /** * Performs nearest neighbor interpolation on an image. @@ -117,7 +118,7 @@ float bilinear_interpolation(const image::ImageView& image, float x, floa * * @return The interpolated pixel value at the provided coordinates. */ -float nearest_neighbor_interpolation(const image::ImageView& image, float x, float y); +LA_IMAGE_API float nearest_neighbor_interpolation(const image::ImageView& image, float x, float y); /** @@ -140,7 +141,7 @@ float nearest_neighbor_interpolation(const image::ImageView& image, float * @return An approximation of the x-th percentile of the pixel intensities in the image. * The return value is a floating point number representing a pixel intensity. */ -float percentile(const image::ImageView& image, const float x, const int num_bins = 1000); +LA_IMAGE_API float percentile(const image::ImageView& image, const float x, const int num_bins = 1000); } // namespace image } // namespace lagrange diff --git a/modules/image/include/lagrange/image/image_type_conversion.h b/modules/image/include/lagrange/image/image_type_conversion.h index 4311e079..e8b21a4a 100644 --- a/modules/image/include/lagrange/image/image_type_conversion.h +++ b/modules/image/include/lagrange/image/image_type_conversion.h @@ -21,9 +21,9 @@ namespace lagrange { namespace image { -std::shared_ptr image_storage_from_raw_input_image(const image::RawInputImage& image); +LA_IMAGE_API std::shared_ptr image_storage_from_raw_input_image(const image::RawInputImage& image); -image::RawInputImage raw_input_image_from_image_view( +LA_IMAGE_API image::RawInputImage raw_input_image_from_image_view( image::ImageViewBase& in, bool copy_buffer = false); diff --git a/modules/image_io/CMakeLists.txt b/modules/image_io/CMakeLists.txt index 2b8e6816..ce3af9fb 100644 --- a/modules/image_io/CMakeLists.txt +++ b/modules/image_io/CMakeLists.txt @@ -27,12 +27,7 @@ target_link_libraries(lagrange_image_io tinyexr::tinyexr ) -# 3. installation -if(LAGRANGE_INSTALL) - set_target_properties(lagrange_image_io PROPERTIES EXPORT_NAME image_io) - lagrange_install(lagrange_image_io) -endif() - +# 3. unit tests and examples if(LAGRANGE_UNIT_TESTS) add_subdirectory(tests) endif() diff --git a/modules/image_io/include/lagrange/image_io/api.h b/modules/image_io/include/lagrange/image_io/api.h new file mode 100644 index 00000000..ec56efd9 --- /dev/null +++ b/modules/image_io/include/lagrange/image_io/api.h @@ -0,0 +1,23 @@ +#pragma once + +#ifdef LA_IMAGE_IO_STATIC_DEFINE + #define LA_IMAGE_IO_API +#else + #ifndef LA_IMAGE_IO_API + #ifdef lagrange_image_io_EXPORTS + // We are building this library + #if defined(_WIN32) || defined(_WIN64) + #define LA_IMAGE_IO_API __declspec(dllexport) + #else + #define LA_IMAGE_IO_API __attribute__((visibility("default"))) + #endif + #else + // We are using this library + #if defined(_WIN32) || defined(_WIN64) + #define LA_IMAGE_IO_API __declspec(dllimport) + #else + #define LA_IMAGE_IO_API __attribute__((visibility("default"))) + #endif + #endif + #endif +#endif diff --git a/modules/image_io/include/lagrange/image_io/load_image.h b/modules/image_io/include/lagrange/image_io/load_image.h index 8190a530..8df1f609 100644 --- a/modules/image_io/include/lagrange/image_io/load_image.h +++ b/modules/image_io/include/lagrange/image_io/load_image.h @@ -13,6 +13,7 @@ #include #include +#include namespace lagrange { namespace image_io { @@ -28,17 +29,17 @@ struct LoadImageResult }; // Load image. Storage type is determined by the image file type. -LoadImageResult load_image(const fs::path& path); +LA_IMAGE_IO_API LoadImageResult load_image(const fs::path& path); // Load png or jpg image using stb library. Produces uint8 data. -LoadImageResult load_image_stb(const fs::path& path); +LA_IMAGE_IO_API LoadImageResult load_image_stb(const fs::path& path); // Note: #include to use the full load_image_exr directly // Load exr image using tinyexr. Produces multiple data types. -LoadImageResult load_image_exr(const fs::path& path); +LA_IMAGE_IO_API LoadImageResult load_image_exr(const fs::path& path); // Load image from our custom binary format. -LoadImageResult load_image_bin(const fs::path& path); +LA_IMAGE_IO_API LoadImageResult load_image_bin(const fs::path& path); // Load image as the provided type/view. Converts if needed/possible. Returns true on success. template diff --git a/modules/image_io/include/lagrange/image_io/save_image.h b/modules/image_io/include/lagrange/image_io/save_image.h index 9678bcb9..c2f2a99a 100644 --- a/modules/image_io/include/lagrange/image_io/save_image.h +++ b/modules/image_io/include/lagrange/image_io/save_image.h @@ -13,13 +13,13 @@ #include #include - +#include namespace lagrange { namespace image_io { // Save image. Returns true on success. -bool save_image( +LA_IMAGE_IO_API bool save_image( const fs::path& path, const unsigned char* data, size_t width, @@ -28,7 +28,7 @@ bool save_image( image::ImageChannel channel); // Save image using stb. Supports png or jpeg. Only supports uint8 data. -bool save_image_stb( +LA_IMAGE_IO_API bool save_image_stb( const fs::path& path, const unsigned char* data, size_t width, @@ -37,7 +37,7 @@ bool save_image_stb( // Note: #include to use the full save_image_exr directly // Save exr image using tinyexr. -bool save_image_exr( +LA_IMAGE_IO_API bool save_image_exr( const fs::path& path, const unsigned char* data, size_t width, @@ -46,7 +46,7 @@ bool save_image_exr( image::ImageChannel channel); // Save image to our custom binary format. -bool save_image_bin( +LA_IMAGE_IO_API bool save_image_bin( const fs::path& path, const unsigned char* data, size_t width, diff --git a/modules/io/CMakeLists.txt b/modules/io/CMakeLists.txt index 13aff7d2..7f6c6235 100644 --- a/modules/io/CMakeLists.txt +++ b/modules/io/CMakeLists.txt @@ -47,13 +47,7 @@ if(LAGRANGE_WITH_ASSIMP) target_compile_definitions(lagrange_io PUBLIC LAGRANGE_WITH_ASSIMP) endif() -# 3. installation -if(LAGRANGE_INSTALL) - set_target_properties(lagrange_io PROPERTIES EXPORT_NAME io) - lagrange_install(lagrange_io) -endif() - -# 4. unit tests and examples +# 3. unit tests and examples if(LAGRANGE_UNIT_TESTS) add_subdirectory(tests) endif() diff --git a/modules/io/examples/CMakeLists.txt b/modules/io/examples/CMakeLists.txt index 7f295a7e..d05ceb69 100644 --- a/modules/io/examples/CMakeLists.txt +++ b/modules/io/examples/CMakeLists.txt @@ -13,3 +13,6 @@ include(cli11) lagrange_add_example(mesh_convert mesh_convert.cpp) target_link_libraries(mesh_convert lagrange::core lagrange::io CLI11::CLI11) + +lagrange_add_example(scene_io scene_io.cpp) +target_link_libraries(scene_io lagrange::core lagrange::io CLI11::CLI11) diff --git a/modules/io/examples/scene_io.cpp b/modules/io/examples/scene_io.cpp new file mode 100644 index 00000000..3fc9f749 --- /dev/null +++ b/modules/io/examples/scene_io.cpp @@ -0,0 +1,59 @@ +/* + * Copyright 2024 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. + */ +#include +#include +#include + +#include + +#include + +int main(int argc, char** argv) +{ + struct + { + std::string input; + std::string output; + bool embed_images = false; + } args; + + lagrange::logger().set_level(spdlog::level::info); + + CLI::App app{argv[0]}; + app.option_defaults()->always_capture_default(); + app.add_option("input", args.input, "Input mesh.")->required()->check(CLI::ExistingFile); + app.add_option("output", args.output, "Output mesh.")->required(); + app.add_flag("--embed-images", args.embed_images, "Embed images in the output scene."); + CLI11_PARSE(app, argc, argv) + + auto scene = lagrange::io::load_scene>(args.input); + + for (const auto& img : scene.images) { + lagrange::logger().info("name: {}", img.name); + const auto& image_buffer = img.image; + lagrange::logger().info( + "size: {} x {} x {}", + image_buffer.width, + image_buffer.height, + image_buffer.num_channels); + if (!img.uri.empty()) { + lagrange::logger().info("uri: {}", img.uri.string()); + } + } + + + lagrange::io::SaveOptions save_options; + save_options.embed_images = args.embed_images; + lagrange::io::save_scene(args.output, scene, save_options); + + return 0; +} diff --git a/modules/io/include/lagrange/io/api.h b/modules/io/include/lagrange/io/api.h new file mode 100644 index 00000000..781075f4 --- /dev/null +++ b/modules/io/include/lagrange/io/api.h @@ -0,0 +1,23 @@ +#pragma once + +#ifdef LA_IO_STATIC_DEFINE + #define LA_IO_API +#else + #ifndef LA_IO_API + #ifdef lagrange_io_EXPORTS + // We are building this library + #if defined(_WIN32) || defined(_WIN64) + #define LA_IO_API __declspec(dllexport) + #else + #define LA_IO_API __attribute__((visibility("default"))) + #endif + #else + // We are using this library + #if defined(_WIN32) || defined(_WIN64) + #define LA_IO_API __declspec(dllimport) + #else + #define LA_IO_API __attribute__((visibility("default"))) + #endif + #endif + #endif +#endif diff --git a/modules/io/include/lagrange/io/internal/load_assimp.h b/modules/io/include/lagrange/io/internal/load_assimp.h index 9ac6a2dc..33fbce37 100644 --- a/modules/io/include/lagrange/io/internal/load_assimp.h +++ b/modules/io/include/lagrange/io/internal/load_assimp.h @@ -13,15 +13,15 @@ #ifdef LAGRANGE_WITH_ASSIMP -#include -#include -#include + #include + #include + #include -#include -#include -#include -#include -#include + #include + #include + #include + #include + #include namespace lagrange::io::internal { /** @@ -65,6 +65,6 @@ template SceneType load_scene_assimp(const aiScene& scene, const LoadOptions& options = {}); -} +} // namespace lagrange::io::internal #endif diff --git a/modules/io/include/lagrange/io/internal/scene_utils.h b/modules/io/include/lagrange/io/internal/scene_utils.h index 8ab657d5..fc3c3aa6 100644 --- a/modules/io/include/lagrange/io/internal/scene_utils.h +++ b/modules/io/include/lagrange/io/internal/scene_utils.h @@ -17,14 +17,14 @@ namespace lagrange::io::internal { /** - * Load an image from disk, and store it in the ImageLegacy - * + * Load an image from disk, and store it in the Image. + * * @param[in] name. Name of texture or relative or full path to texture. * @param[in] options. Options. Remember to set options.search_path if necessary. * @param[out] image. This will be filled with the loaded data. - * + * * @return true if successful. */ -bool try_load_image(const std::string& name, const LoadOptions& options, scene::ImageLegacy& image); +bool try_load_image(const std::string& name, const LoadOptions& options, scene::ImageExperimental& image); } // namespace lagrange::io::internal diff --git a/modules/io/include/lagrange/io/types.h b/modules/io/include/lagrange/io/types.h index 6bfcda21..e307a808 100644 --- a/modules/io/include/lagrange/io/types.h +++ b/modules/io/include/lagrange/io/types.h @@ -62,6 +62,9 @@ struct SaveOptions AttributeConversionPolicy attribute_conversion_policy = AttributeConversionPolicy::ExactMatchOnly; + /// Whether to embed images in the file (if supported by the filetype) + bool embed_images = false; + std::vector extension_converters; }; diff --git a/modules/io/python/src/io.cpp b/modules/io/python/src/io.cpp index 9e9fc0ba..03288043 100644 --- a/modules/io/python/src/io.cpp +++ b/modules/io/python/src/io.cpp @@ -67,7 +67,7 @@ void populate_io_module(nb::module_& m) .def_rw("load_vertex_colors", &io::LoadOptions::load_vertex_colors) .def_rw("load_object_id", &io::LoadOptions::load_object_id) .def_rw("search_path", &io::LoadOptions::search_path); - //.def_rw("extension_converters", &io::LoadOptions::extension_converters); + //.def_rw("extension_converters", &io::LoadOptions::extension_converters); nb::enum_(m, "FileEncoding") .value("Binary", io::FileEncoding::Binary) @@ -83,8 +83,9 @@ void populate_io_module(nb::module_& m) .def_rw("encoding", &io::SaveOptions::encoding) .def_rw("output_attributes", &io::SaveOptions::output_attributes) .def_rw("selected_attributes", &io::SaveOptions::selected_attributes) - .def_rw("attribute_conversion_policy", &io::SaveOptions::attribute_conversion_policy); - //.def_rw("extension_converters", &io::SaveOptions::extension_converters); + .def_rw("attribute_conversion_policy", &io::SaveOptions::attribute_conversion_policy) + .def_rw("embed_images", &io::SaveOptions::embed_images); + //.def_rw("extension_converters", &io::SaveOptions::extension_converters); nb::enum_(save_options, "OutputAttributes") .value("All", io::SaveOptions::OutputAttributes::All) .value("SelectedOnly", io::SaveOptions::OutputAttributes::SelectedOnly); diff --git a/modules/io/python/tests/test_io.py b/modules/io/python/tests/test_io.py index a489cdfd..ddd04814 100644 --- a/modules/io/python/tests/test_io.py +++ b/modules/io/python/tests/test_io.py @@ -56,7 +56,7 @@ def assert_same_vertices_and_facets(mesh, mesh2): assert mesh.num_vertices == mesh2.num_vertices assert mesh.num_facets == mesh2.num_facets assert mesh.vertices == pytest.approx(mesh2.vertices) - assert np.all(mesh.facets == mesh2.facets) + assert np.all(mesh.facets.ravel() == mesh2.facets.ravel()) def match_attribute(mesh, mesh2, id1, id2): @@ -104,20 +104,27 @@ def assert_same_attribute(mesh, mesh2, usage, required): class TestIO: def __save_and_load__( - self, suffix, mesh, binary, exact_match, selected_attributes + self, suffix, mesh, *, binary, exact_match, selected_attributes, as_scene ): with tempfile.TemporaryDirectory() as tmp_dir: tmp_dir_path = pathlib.Path(tmp_dir) stamp = datetime.datetime.now().isoformat().replace(":", ".") filename = tmp_dir_path / f"{stamp}{suffix}" - lagrange.io.save_mesh( - filename, - mesh, - binary=binary, - exact_match=exact_match, - selected_attributes=selected_attributes, - ) - mesh2 = lagrange.io.load_mesh(filename) + if not as_scene: + lagrange.io.save_mesh( + filename, + mesh, + binary=binary, + exact_match=exact_match, + selected_attributes=selected_attributes, + ) + mesh2 = lagrange.io.load_mesh(filename) + else: + unified_mesh = lagrange.unify_index_buffer(mesh) + mesh_scene = lagrange.scene.mesh_to_simple_scene(unified_mesh) + lagrange.io.save_simple_scene(filename, mesh_scene) + mesh_scene2 = lagrange.io.load_simple_scene(filename) + mesh2 = lagrange.scene.simple_scene_to_mesh(mesh_scene2) assert_same_vertices_and_facets(mesh, mesh2) @@ -153,6 +160,7 @@ def save_and_load(self, mesh, attribute_ids=None): binary=False, exact_match=exact_match, selected_attributes=attribute_ids, + as_scene=False, ) # For binary formats @@ -163,8 +171,28 @@ def save_and_load(self, mesh, attribute_ids=None): binary=True, exact_match=exact_match, selected_attributes=attribute_ids, + as_scene=False, ) + for ext in [".gltf", ".glb"]: + self.__save_and_load__( + ext, + mesh, + binary=(ext==".glb"), + exact_match=exact_match, + selected_attributes=attribute_ids, + as_scene=True, + ) + + def test_empty_mesh(self): + mesh = lagrange.SurfaceMesh() + self.save_and_load(mesh) + + def test_point_cloud(self): + mesh = lagrange.SurfaceMesh() + mesh.add_vertices(np.eye(3)) + self.save_and_load(mesh) + def test_single_triangle(self, triangle): mesh = triangle self.save_and_load(mesh) @@ -234,17 +262,17 @@ def test_io_with_selected_attributed(self, triangle): def test_load_scene(self, triangle): scene = lagrange.scene.Scene() - mesh_idx = lagrange.scene.add_mesh(scene, triangle) + mesh_idx = scene.add(triangle) assert len(scene.meshes) == 1 node = lagrange.scene.Node() node.name = "triangle" - node.extensions.data = {"my_ext": lagrange.scene.Value({"num": lagrange.scene.Value(12) }) } + node.extensions.data = {"my_ext": {"num": 12}} assert node.extensions.size == 1 # class Foo: - # a = 1 + # a = 1 # node.extensions.user_data = {"foo": Foo()} scene.materials.append(lagrange.scene.Material()) @@ -256,7 +284,7 @@ def test_load_scene(self, triangle): node.meshes.append(instance) assert len(node.meshes) == 1 - scene.nodes.append(node) + scene.add(node) with tempfile.TemporaryDirectory() as tmp_dir: tmp_dir_path = pathlib.Path(tmp_dir) @@ -270,4 +298,4 @@ def test_load_scene(self, triangle): assert scene2.nodes[0].extensions.size == 1 my_ext = scene2.nodes[0].extensions.data["my_ext"] - assert my_ext.value["num"].value == 12 + assert my_ext["num"] == 12 diff --git a/modules/io/src/internal/scene_utils.cpp b/modules/io/src/internal/scene_utils.cpp index 5b06f457..da3aa0d9 100644 --- a/modules/io/src/internal/scene_utils.cpp +++ b/modules/io/src/internal/scene_utils.cpp @@ -10,41 +10,48 @@ * governing permissions and limitations under the License. */ -#include +#include #include +#include namespace lagrange::io::internal { -bool try_load_image(const std::string& name, const LoadOptions& options, scene::ImageLegacy& image) +bool try_load_image( + const std::string& name, + const LoadOptions& options, + scene::ImageExperimental& image) { fs::path path = name; if (path.is_relative() && !options.search_path.empty()) path = options.search_path / name; if (path.empty()) return false; - if (image.data) return false; image_io::LoadImageResult result = image_io::load_image(path); if (!result.valid) return false; - image.width = result.width; - image.height = result.height; - image.precision = result.precision; - image.channel = result.channel; - image.data = result.storage; - image.uri = name; - if (path.has_extension()) { - auto extension = path.extension(); - if (extension == ".jpeg" || extension == ".jpg") { - image.type = scene::ImageLegacy::Type::Jpeg; - } else if (extension == ".png") { - image.type = scene::ImageLegacy::Type::Png; - } else if (extension == ".bmp") { - image.type = scene::ImageLegacy::Type::Bmp; - } else if (extension == ".gif") { - image.type = scene::ImageLegacy::Type::Gif; - } else { - image.type = scene::ImageLegacy::Type::Unknown; - } + scene::ImageBufferExperimental& buffer = image.image; + buffer.width = result.width; + buffer.height = result.height; + buffer.num_channels = static_cast(result.channel); + switch (result.precision) { + case image::ImagePrecision::uint8: buffer.element_type = AttributeValueType::e_uint8_t; break; + case image::ImagePrecision::int8: buffer.element_type = AttributeValueType::e_int8_t; break; + case image::ImagePrecision::uint32: buffer.element_type = AttributeValueType::e_uint32_t; break; + case image::ImagePrecision::int32: buffer.element_type = AttributeValueType::e_int32_t; break; + case image::ImagePrecision::float32: buffer.element_type = AttributeValueType::e_float; break; + case image::ImagePrecision::float64: buffer.element_type = AttributeValueType::e_double; break; + case image::ImagePrecision::float16: [[fallthrough]]; + default: throw std::runtime_error("Unsupported image precision"); } + + la_runtime_assert(result.storage != nullptr); + const size_t num_bytes = + buffer.width * buffer.height * buffer.num_channels * buffer.get_bits_per_element() / 8; + buffer.data.reserve(num_bytes); + std::copy( + result.storage->data(), + result.storage->data() + num_bytes, + std::back_inserter(buffer.data)); + return true; } diff --git a/modules/io/src/legacy_load_mesh.cpp b/modules/io/src/legacy_load_mesh.cpp index 77431e4a..232a8027 100644 --- a/modules/io/src/legacy_load_mesh.cpp +++ b/modules/io/src/legacy_load_mesh.cpp @@ -10,6 +10,7 @@ * governing permissions and limitations under the License. */ #include +#include namespace lagrange::io { LAGRANGE_LEGACY_INLINE @@ -24,10 +25,10 @@ template std::vector> load_obj_meshes(const fs: template std::unique_ptr load_obj_mesh(const fs::path&); template std::unique_ptr load_obj_mesh(const fs::path&); -template std::unique_ptr load_mesh(const fs::path&); -template std::unique_ptr load_mesh(const fs::path&); -template std::unique_ptr load_mesh(const fs::path&); -template std::unique_ptr> load_mesh(const fs::path&); +template LA_IO_API std::unique_ptr load_mesh(const fs::path&); +template LA_IO_API std::unique_ptr load_mesh(const fs::path&); +template LA_IO_API std::unique_ptr load_mesh(const fs::path&); +template LA_IO_API std::unique_ptr> load_mesh(const fs::path&); } // namespace legacy } // namespace lagrange::io diff --git a/modules/io/src/load_assimp.cpp b/modules/io/src/load_assimp.cpp index 92e4f638..0f939688 100644 --- a/modules/io/src/load_assimp.cpp +++ b/modules/io/src/load_assimp.cpp @@ -13,6 +13,7 @@ #ifdef LAGRANGE_WITH_ASSIMP // this .cpp provides implementations for functions defined in those headers: + #include #include #include #include @@ -20,6 +21,7 @@ // ==== #include + #include #include #include #include @@ -236,33 +238,35 @@ AffineTransform convert_transform_assimp_to_lagrange(const aiMatrix4x4 t) t.b1, t.b2, t.b3, t.b4, t.c1, t.c2, t.c3, t.c4, t.d1, t.d2, t.d3, t.d4; - //clang-format on + // clang-format on return transform; } -Eigen::Vector3f to_vec3(const aiVector3D v) { +Eigen::Vector3f to_vec3(const aiVector3D v) +{ return Eigen::Vector3f(v.x, v.y, v.z); } -Eigen::Vector3f to_color3(const aiColor3D v) { +Eigen::Vector3f to_color3(const aiColor3D v) +{ return Eigen::Vector3f(v.r, v.g, v.b); } -Eigen::Vector4f to_color4(const aiColor4D& v) { +Eigen::Vector4f to_color4(const aiColor4D& v) +{ return Eigen::Vector4f(v.r, v.g, v.b, v.a); } -scene::Light convert_light_assimp_to_lagrange(const aiLight* light) { +scene::Light convert_light_assimp_to_lagrange(const aiLight* light) +{ scene::Light llight; llight.name = light->mName.C_Str(); - switch (light->mType) - { - case aiLightSource_DIRECTIONAL: llight.type = scene::Light::Type::Directional; break; - case aiLightSource_POINT: llight.type = scene::Light::Type::Point; break; - case aiLightSource_SPOT: llight.type = scene::Light::Type::Spot; break; - case aiLightSource_AMBIENT: llight.type = scene::Light::Type::Ambient; break; - case aiLightSource_AREA: llight.type = scene::Light::Type::Area; break; - case aiLightSource_UNDEFINED: // passthrough to default - default: - llight.type = scene::Light::Type::Undefined; break; + switch (light->mType) { + case aiLightSource_DIRECTIONAL: llight.type = scene::Light::Type::Directional; break; + case aiLightSource_POINT: llight.type = scene::Light::Type::Point; break; + case aiLightSource_SPOT: llight.type = scene::Light::Type::Spot; break; + case aiLightSource_AMBIENT: llight.type = scene::Light::Type::Ambient; break; + case aiLightSource_AREA: llight.type = scene::Light::Type::Area; break; + case aiLightSource_UNDEFINED: // passthrough to default + default: llight.type = scene::Light::Type::Undefined; break; } llight.position = to_vec3(light->mPosition); llight.direction = to_vec3(light->mDirection); @@ -324,7 +328,8 @@ SceneType load_simple_scene_assimp(const aiScene& scene, const LoadOptions& opti visit_node = [&](aiNode* node, const AffineTransform& parent_transform) -> void { AffineTransform node_transform; if constexpr (SceneType::Dim == 3) { - node_transform = convert_transform_assimp_to_lagrange(node->mTransformation); + node_transform = + convert_transform_assimp_to_lagrange(node->mTransformation); } else { // TODO: convert 3d transforms into 2d logger().warn("Ignoring 3d node transform while loading 2d scene"); @@ -343,18 +348,23 @@ SceneType load_simple_scene_assimp(const aiScene& scene, const LoadOptions& opti visit_node(scene.mRootNode, AffineTransform::Identity()); return lscene; } -#define LA_X_load_simple_scene_assimp(_, S, I, D) \ - template scene::SimpleScene load_simple_scene_assimp(const aiScene& scene, const LoadOptions& options); + #define LA_X_load_simple_scene_assimp(_, S, I, D) \ + template scene::SimpleScene load_simple_scene_assimp( \ + const aiScene& scene, \ + const LoadOptions& options); LA_SIMPLE_SCENE_X(load_simple_scene_assimp, 0); -#undef LA_X_load_simple_scene_assimp + #undef LA_X_load_simple_scene_assimp template -SceneType load_scene_assimp(const aiScene& scene, const LoadOptions& options) { +SceneType load_scene_assimp(const aiScene& scene, const LoadOptions& options) +{ SceneType lscene; lscene.name = scene.mName.C_Str(); for (unsigned int i = 0; i < scene.mNumMeshes; ++i) { - scene::utils::add_mesh(lscene, convert_mesh_assimp_to_lagrange(*scene.mMeshes[i], options)); + lscene.add(convert_mesh_assimp_to_lagrange( + *scene.mMeshes[i], + options)); } // note that assimp's textures are really images. @@ -366,25 +376,31 @@ SceneType load_scene_assimp(const aiScene& scene, const LoadOptions& options) { // TODO add support } - scene::ImageLegacy limage; - limage.width = texture->mWidth; - limage.height = texture->mHeight; + scene::ImageExperimental limage; + scene::ImageBufferExperimental buffer; + + buffer.width = texture->mWidth; + buffer.height = texture->mHeight; + buffer.num_channels = 4; + buffer.element_type = AttributeValueType::e_uint8_t; + const size_t num_pixels = buffer.width * buffer.height; + buffer.data.resize(num_pixels); + for (size_t pi = 0; pi < num_pixels; pi++) { + const auto& pixel = texture->pcData[pi]; + // Note that pixel is in ARGB8888 format. In our image buffer, we use RGBA8888 format. + buffer.data[pi * 4] = pixel.r; + buffer.data[pi * 4 + 1] = pixel.g; + buffer.data[pi * 4 + 2] = pixel.b; + buffer.data[pi * 4 + 3] = pixel.a; + } - // assimp always loads argb8888 + limage.image = std::move(buffer); limage.name = texture->mFilename.C_Str(); - limage.uri = texture->mFilename.C_Str(); - limage.channel = image::ImageChannel::four; - limage.precision = image::ImagePrecision::uint8; - - limage.data = std::make_unique(limage.get_element_size() * limage.width * limage.get_num_channels(), limage.height, 1); - std::copy_n( - (unsigned char*)texture->pcData, - limage.width * limage.height * limage.get_num_channels() * limage.get_element_size(), - limage.data->data()); - lscene.images.push_back(std::move(limage)); + + lscene.add(std::move(limage)); } - // load an image from embedded or from disk. + // load an image from embedded or from disk. // If successful, saves the image in the scene and returns its index. // Otherwise, returns -1. auto try_image_load = [&](const aiMaterial*, const char* s) -> int { @@ -392,27 +408,33 @@ SceneType load_scene_assimp(const aiScene& scene, const LoadOptions& options) { if (index >= 0) { return index; } else { - scene::ImageLegacy limage; - if (io::internal::try_load_image(s, options, limage)) { - int image_idx = static_cast(lscene.images.size()); - lscene.images.push_back(limage); - return image_idx; + scene::ImageExperimental limage; + limage.name = s; + limage.uri = s; + if (options.load_images) { + if (io::internal::try_load_image(s, options, limage)) { + return static_cast(lscene.add(std::move(limage))); + } else { + return -1; + } } else { - return -1; + return static_cast(lscene.add(std::move(limage))); } } }; auto convert_map_mode = [](aiTextureMapMode mode) -> scene::Texture::WrapMode { switch (mode) { - case aiTextureMapMode_Wrap: return scene::Texture::WrapMode::Wrap; - case aiTextureMapMode_Clamp: return scene::Texture::WrapMode::Clamp; - case aiTextureMapMode_Decal: return scene::Texture::WrapMode::Decal; - case aiTextureMapMode_Mirror: return scene::Texture::WrapMode::Mirror; - default: return scene::Texture::WrapMode::Wrap; + case aiTextureMapMode_Wrap: return scene::Texture::WrapMode::Wrap; + case aiTextureMapMode_Clamp: return scene::Texture::WrapMode::Clamp; + case aiTextureMapMode_Decal: return scene::Texture::WrapMode::Decal; + case aiTextureMapMode_Mirror: return scene::Texture::WrapMode::Mirror; + default: return scene::Texture::WrapMode::Wrap; } - }; - auto try_load_texture = [&](const aiMaterial* material, aiTextureType type, scene::TextureInfo& tex_info) -> bool { - if (tex_info.index != invalid()) return false; // there was a texture here already + }; + auto try_load_texture = + [&](const aiMaterial* material, aiTextureType type, scene::TextureInfo& tex_info) -> bool { + if (tex_info.index != invalid()) + return false; // there was a texture here already aiString path; aiTextureMapping texture_mapping; @@ -420,22 +442,24 @@ SceneType load_scene_assimp(const aiScene& scene, const LoadOptions& options) { ai_real blend; aiTextureOp op; aiTextureMapMode map_mode[2]; - if (material->GetTexture(type, 0, &path, &texture_mapping, &uv_index, &blend, &op, map_mode) != aiReturn_SUCCESS) return false; - + if (material + ->GetTexture(type, 0, &path, &texture_mapping, &uv_index, &blend, &op, map_mode) != + aiReturn_SUCCESS) + return false; + int image_idx = try_image_load(material, path.C_Str()); if (image_idx == -1) return false; - tex_info.index = static_cast(lscene.textures.size()); tex_info.texcoord = uv_index; - + scene::Texture ltex; ltex.name = path.C_Str(); ltex.image = image_idx; ltex.wrap_u = convert_map_mode(map_mode[0]); ltex.wrap_v = convert_map_mode(map_mode[1]); - lscene.textures.push_back(ltex); + tex_info.index = lscene.add(std::move(ltex)); return true; - }; + }; for (unsigned int i = 0; i < scene.mNumMaterials; ++i) { scene::MaterialExperimental lmat; @@ -453,26 +477,33 @@ SceneType load_scene_assimp(const aiScene& scene, const LoadOptions& options) { // we do our best here, but there may be incompatible materials or missed information try_load_texture(material, aiTextureType_BASE_COLOR, lmat.base_color_texture); try_load_texture(material, aiTextureType_DIFFUSE, lmat.base_color_texture); - if (material->Get(AI_MATKEY_BASE_COLOR, color4) == aiReturn_SUCCESS) lmat.base_color_value = to_color4(color4); + if (material->Get(AI_MATKEY_BASE_COLOR, color4) == aiReturn_SUCCESS) + lmat.base_color_value = to_color4(color4); try_load_texture(material, aiTextureType_NORMALS, lmat.normal_texture); try_load_texture(material, aiTextureType_NORMAL_CAMERA, lmat.normal_texture); try_load_texture(material, aiTextureType_EMISSIVE, lmat.emissive_texture); try_load_texture(material, aiTextureType_EMISSION_COLOR, lmat.emissive_texture); - if (material->Get(AI_MATKEY_EMISSIVE_INTENSITY, color3) == aiReturn_SUCCESS) lmat.emissive_value = to_color3(color3); + if (material->Get(AI_MATKEY_EMISSIVE_INTENSITY, color3) == aiReturn_SUCCESS) + lmat.emissive_value = to_color3(color3); try_load_texture(material, aiTextureType_METALNESS, lmat.metallic_roughness_texture); - try_load_texture(material, aiTextureType_DIFFUSE_ROUGHNESS, lmat.metallic_roughness_texture); - if (material->Get(AI_MATKEY_METALLIC_FACTOR, real) == aiReturn_SUCCESS) lmat.metallic_value = real; - if (material->Get(AI_MATKEY_ROUGHNESS_FACTOR, real) == aiReturn_SUCCESS) lmat.metallic_value = real; + try_load_texture( + material, + aiTextureType_DIFFUSE_ROUGHNESS, + lmat.metallic_roughness_texture); + if (material->Get(AI_MATKEY_METALLIC_FACTOR, real) == aiReturn_SUCCESS) + lmat.metallic_value = real; + if (material->Get(AI_MATKEY_ROUGHNESS_FACTOR, real) == aiReturn_SUCCESS) + lmat.metallic_value = real; try_load_texture(material, aiTextureType_AMBIENT_OCCLUSION, lmat.occlusion_texture); if (material->Get(AI_MATKEY_TWOSIDED, b) == aiReturn_SUCCESS) lmat.double_sided = b; - + // we could also iterate over all textures, unnecessary for now: - //const std::vector texture_types = { + // const std::vector texture_types = { // // traditional // aiTextureType_DIFFUSE, // aiTextureType_SPECULAR, @@ -497,7 +528,7 @@ SceneType load_scene_assimp(const aiScene& scene, const LoadOptions& options) { // aiTextureType_CLEARCOAT, // aiTextureType_TRANSMISSION, //}; - //for (aiTextureType type : texture_types) { + // for (aiTextureType type : texture_types) { // unsigned int count = material->GetTextureCount(type); // const char* type_string = aiTextureTypeToString(type); // for (unsigned int j = 0; j < count; ++j) { @@ -507,8 +538,8 @@ SceneType load_scene_assimp(const aiScene& scene, const LoadOptions& options) { // ai_real blend; // aiTextureOp op; // aiTextureMapMode map_mode[2]; - // aiReturn ret = material->GetTexture(type, j, &path, &texture_mapping, &uv_index, &blend, &op, map_mode); - // if (ret == aiReturn_SUCCESS) { + // aiReturn ret = material->GetTexture(type, j, &path, &texture_mapping, &uv_index, + // &blend, &op, map_mode); if (ret == aiReturn_SUCCESS) { // logger().warn("{}: {}", type_string, path.C_Str()); // } // } @@ -532,7 +563,7 @@ SceneType load_scene_assimp(const aiScene& scene, const LoadOptions& options) { lcam.up = to_vec3(camera->mUp); lcam.look_at = to_vec3(camera->mLookAt); // assimp docs claim the fov is from the center to the side, so it would be half of our fov. - // but from testing it appears to be the same as ours. + // but from testing it appears to be the same as ours. // It may depend on the filetype, and it could be a bug. lcam.horizontal_fov = camera->mHorizontalFOV; lcam.aspect_ratio = camera->mAspect; @@ -554,7 +585,8 @@ SceneType load_scene_assimp(const aiScene& scene, const LoadOptions& options) { scene::Node& lnode = lscene.nodes.back(); lnode.name = node->mName.C_Str(); - lnode.transform = convert_transform_assimp_to_lagrange(node->mTransformation); + lnode.transform = + convert_transform_assimp_to_lagrange(node->mTransformation); lnode.parent = parent_idx; for (unsigned int i = 0; i < node->mNumMeshes; ++i) { @@ -581,7 +613,7 @@ SceneType load_scene_assimp(const aiScene& scene, const LoadOptions& options) { } return lnode_idx; }; - std::functioncount_nodes; + std::function count_nodes; count_nodes = [&count_nodes](aiNode* node) -> size_t { size_t count = 1; for (unsigned int i = 0; i < node->mNumChildren; ++i) { @@ -595,38 +627,46 @@ SceneType load_scene_assimp(const aiScene& scene, const LoadOptions& options) { return lscene; } -#define LA_X_load_scene_assimp(_, S, I) \ - template scene::Scene load_scene_assimp( \ - const aiScene& scene, \ - const LoadOptions& options); + #define LA_X_load_scene_assimp(_, S, I) \ + template scene::Scene load_scene_assimp( \ + const aiScene& scene, \ + const LoadOptions& options); LA_SCENE_X(load_scene_assimp, 0); -#undef LA_X_load_scene_assimp + #undef LA_X_load_scene_assimp -} // namespace lagrange::io::internal +} // namespace internal // ===================================== // load_mesh_assimp.h // ===================================== -template ::value>* /*= nullptr*/> -MeshType load_mesh_assimp(const fs::path& filename, const LoadOptions& options) { +MeshType load_mesh_assimp(const fs::path& filename, const LoadOptions& options) +{ std::unique_ptr scene = internal::load_assimp(filename); return internal::load_mesh_assimp(*scene, options); } -template ::value>* /*= nullptr*/> -MeshType load_mesh_assimp(std::istream& input_stream, const LoadOptions& options) { +MeshType load_mesh_assimp(std::istream& input_stream, const LoadOptions& options) +{ std::unique_ptr scene = internal::load_assimp(input_stream); return internal::load_mesh_assimp(*scene, options); } -#define LA_X_load_mesh_assimp(_, S, I) \ - template SurfaceMesh load_mesh_assimp(const fs::path& filename, const LoadOptions& options);\ - template SurfaceMesh load_mesh_assimp(std::istream&, const LoadOptions& options); + #define LA_X_load_mesh_assimp(_, S, I) \ + template LA_IO_API SurfaceMesh load_mesh_assimp( \ + const fs::path& filename, \ + const LoadOptions& options); \ + template LA_IO_API SurfaceMesh load_mesh_assimp( \ + std::istream&, \ + const LoadOptions& options); LA_SURFACE_MESH_X(load_mesh_assimp, 0); -#undef LA_X_load_mesh_assimp + #undef LA_X_load_mesh_assimp // ===================================== @@ -646,32 +686,40 @@ SceneType load_simple_scene_assimp(std::istream& input_stream, const LoadOptions return internal::load_simple_scene_assimp(*scene, options); } -#define LA_X_load_simple_scene_assimp(_, S, I, D) \ - template scene::SimpleScene load_simple_scene_assimp(const fs::path&, const LoadOptions&);\ - template scene::SimpleScene load_simple_scene_assimp(std::istream&, const LoadOptions&); + #define LA_X_load_simple_scene_assimp(_, S, I, D) \ + template LA_IO_API scene::SimpleScene load_simple_scene_assimp( \ + const fs::path&, \ + const LoadOptions&); \ + template LA_IO_API scene::SimpleScene load_simple_scene_assimp( \ + std::istream&, \ + const LoadOptions&); LA_SIMPLE_SCENE_X(load_simple_scene_assimp, 0); -#undef LA_X_load_simple_scene_assimp + #undef LA_X_load_simple_scene_assimp // ===================================== // load_scene_assimp.h // ===================================== template -SceneType load_scene_assimp(const fs::path& filename, const LoadOptions& options) { +SceneType load_scene_assimp(const fs::path& filename, const LoadOptions& options) +{ std::unique_ptr scene = internal::load_assimp(filename); LoadOptions opt2 = options; if (opt2.search_path.empty()) opt2.search_path = filename.parent_path(); return internal::load_scene_assimp(*scene, opt2); } template -SceneType load_scene_assimp(std::istream& input_stream, const LoadOptions& options) { +SceneType load_scene_assimp(std::istream& input_stream, const LoadOptions& options) +{ std::unique_ptr scene = internal::load_assimp(input_stream); return internal::load_scene_assimp(*scene, options); } -#define LA_X_load_scene_assimp(_, S, I) \ -template scene::Scene load_scene_assimp(const fs::path&, const LoadOptions&); \ -template scene::Scene load_scene_assimp(std::istream&, const LoadOptions&); + #define LA_X_load_scene_assimp(_, S, I) \ + template LA_IO_API scene::Scene load_scene_assimp( \ + const fs::path&, \ + const LoadOptions&); \ + template LA_IO_API scene::Scene load_scene_assimp(std::istream&, const LoadOptions&); LA_SCENE_X(load_scene_assimp, 0); -#undef LA_X_load_scene_assimp + #undef LA_X_load_scene_assimp } // namespace lagrange::io diff --git a/modules/io/src/load_fbx.cpp b/modules/io/src/load_fbx.cpp index 3c4739a5..7d1f85ee 100644 --- a/modules/io/src/load_fbx.cpp +++ b/modules/io/src/load_fbx.cpp @@ -11,9 +11,11 @@ */ // this .cpp provides implementations for functions defined in those headers: +#include #include #include #include + // ==== #include @@ -337,9 +339,8 @@ SceneType load_scene_fbx(const ufbx_scene* scene, const LoadOptions& opt) using MeshType = typename SceneType::MeshType; for (const ufbx_mesh* mesh : scene->meshes) { - element_index[mesh->element_id] = lscene.meshes.size(); auto lmesh = convert_mesh_ufbx_to_lagrange(mesh, opt); - scene::utils::add_mesh(lscene, lmesh); + element_index[mesh->element_id] = lscene.add(std::move(lmesh)); } for (const ufbx_light* light : scene->lights) { @@ -416,7 +417,7 @@ SceneType load_scene_fbx(const ufbx_scene* scene, const LoadOptions& opt) ltex.image = img_idx; lscene.textures.push_back(ltex); - scene::ImageLegacy limage; + scene::ImageExperimental limage; limage.name = texture->name.data; limage.uri = texture->relative_filename.data; // note: there is no width/height anywhere. read the image from disk, or read png from texture->content. @@ -436,7 +437,7 @@ SceneType load_scene_fbx(const ufbx_scene* scene, const LoadOptions& opt) } if (loaded) { - lscene.images.push_back(limage); + lscene.add(std::move(limage)); } } @@ -597,28 +598,28 @@ SceneType load_scene_fbx(std::istream& input_stream, const LoadOptions& options) return lscene; } -#define LA_X_load_mesh_fbx(_, S, I) \ - template SurfaceMesh load_mesh_fbx(const fs::path&, const LoadOptions&); \ - template SurfaceMesh load_mesh_fbx(std::istream&, const LoadOptions&); +#define LA_X_load_mesh_fbx(_, S, I) \ + template LA_IO_API SurfaceMesh load_mesh_fbx(const fs::path&, const LoadOptions&); \ + template LA_IO_API SurfaceMesh load_mesh_fbx(std::istream&, const LoadOptions&); LA_SURFACE_MESH_X(load_mesh_fbx, 0); #undef LA_X_load_mesh_fbx -#define LA_X_load_simple_scene_fbx(_, S, I, D) \ - template scene::SimpleScene load_simple_scene_fbx( \ - const fs::path& filename, \ - const LoadOptions& options); \ - template scene::SimpleScene load_simple_scene_fbx( \ - std::istream& input_stream, \ +#define LA_X_load_simple_scene_fbx(_, S, I, D) \ + template LA_IO_API scene::SimpleScene load_simple_scene_fbx( \ + const fs::path& filename, \ + const LoadOptions& options); \ + template LA_IO_API scene::SimpleScene load_simple_scene_fbx( \ + std::istream& input_stream, \ const LoadOptions& options); LA_SIMPLE_SCENE_X(load_simple_scene_fbx, 0); #undef LA_X_load_simple_scene_fbx -#define LA_X_load_scene_fbx(_, S, I) \ - template scene::Scene load_scene_fbx( \ - const fs::path& filename, \ - const LoadOptions& options); \ - template scene::Scene load_scene_fbx( \ - std::istream& input_stream, \ +#define LA_X_load_scene_fbx(_, S, I) \ + template LA_IO_API scene::Scene load_scene_fbx( \ + const fs::path& filename, \ + const LoadOptions& options); \ + template LA_IO_API scene::Scene load_scene_fbx( \ + std::istream& input_stream, \ const LoadOptions& options); LA_SCENE_X(load_scene_fbx, 0); #undef LA_X_load_scene_fbx diff --git a/modules/io/src/load_gltf.cpp b/modules/io/src/load_gltf.cpp index b40a9a39..ae1be490 100644 --- a/modules/io/src/load_gltf.cpp +++ b/modules/io/src/load_gltf.cpp @@ -11,11 +11,14 @@ */ // this .cpp provides implementations for functions defined in those headers: +#include #include #include #include + // ==== +#include #include #include #include @@ -60,8 +63,7 @@ scene::Value convert_value(const tinygltf::Value& value) } return scene::Value(std::move(obj)); } - case tinygltf::Type::NULL_TYPE: - [[fallthrough]]; + case tinygltf::Type::NULL_TYPE: [[fallthrough]]; default: return scene::Value(); } } @@ -131,7 +133,7 @@ std::vector load_buffer_data_internal( size_t buf_idx = start + buffer_view.byteStride * i; std::memcpy(&data.at(i * size), &buffer.data.at(buf_idx), size * sizeof(Orig_t)); } - } else { + } else if (accessor.count > 0) { std::memcpy(&data.at(0), &buffer.data.at(start), accessor.count * size * sizeof(Orig_t)); } @@ -624,8 +626,7 @@ SceneType load_scene_gltf(const tinygltf::Model& model, const LoadOptions& optio for (const tinygltf::Mesh& mesh : model.meshes) { for (const tinygltf::Primitive& primitive : mesh.primitives) { - scene::utils::add_mesh( - lscene, + lscene.add( convert_tinygltf_primitive_to_lagrange_mesh(model, primitive, options)); } primitive_count.push_back(primitive_count_tmp); @@ -650,7 +651,7 @@ SceneType load_scene_gltf(const tinygltf::Model& model, const LoadOptions& optio ltex.wrap_u = convert_map_mode(sampler.wrapS); ltex.wrap_v = convert_map_mode(sampler.wrapT); } - lscene.textures.emplace_back(std::move(ltex)); + lscene.add(std::move(ltex)); } for (const tinygltf::Material& material : model.materials) { @@ -694,7 +695,7 @@ SceneType load_scene_gltf(const tinygltf::Model& model, const LoadOptions& optio lmat.extensions = convert_extension_map(material.extensions, options); } - lscene.materials.push_back(lmat); + lscene.add(lmat); } for (const tinygltf::Animation& animation : model.animations) { scene::Animation lanim; @@ -705,7 +706,7 @@ SceneType load_scene_gltf(const tinygltf::Model& model, const LoadOptions& optio lanim.extensions = convert_extension_map(animation.extensions, options); } - lscene.animations.push_back(lanim); + lscene.add(lanim); } for (const tinygltf::Image& image : model.images) { // Note: @@ -718,61 +719,52 @@ SceneType load_scene_gltf(const tinygltf::Model& model, const LoadOptions& optio la_runtime_assert( static_cast(image.image.size()) == image.width * image.height * image.component); - scene::ImageLegacy limage; + scene::ImageExperimental limage; limage.name = image.name; - limage.uri = image.uri; - fs::path path; - if (!image.uri.empty()) path = image.uri; - if (image.mimeType == "image/jpeg" || path.extension() == ".jpg" || - path.extension() == ".jpeg") { - limage.type = scene::ImageLegacy::Type::Jpeg; - } else if (image.mimeType == "image/png" || path.extension() == ".png") { - limage.type = scene::ImageLegacy::Type::Png; - } else if (image.mimeType == "image/bmp" || path.extension() == ".bmp") { - limage.type = scene::ImageLegacy::Type::Bmp; - } else if (image.mimeType == "image/gif" || path.extension() == ".gif") { - limage.type = scene::ImageLegacy::Type::Gif; - } else { - limage.type = scene::ImageLegacy::Type::Unknown; - } - limage.width = image.width; - limage.height = image.height; - limage.channel = [](int component) { - switch (component) { - case 1: return image::ImageChannel::one; - case 3: return image::ImageChannel::three; - case 4: return image::ImageChannel::four; - default: - logger().warn("Loading image with unsupported number of channels!"); - return image::ImageChannel::unknown; - } - }(image.component); - limage.precision = [](int precision) { - switch (precision) { - case TINYGLTF_COMPONENT_TYPE_BYTE: return image::ImagePrecision::int8; - case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: return image::ImagePrecision::uint8; - case TINYGLTF_COMPONENT_TYPE_INT: return image::ImagePrecision::int32; - case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT: return image::ImagePrecision::uint32; - case TINYGLTF_COMPONENT_TYPE_FLOAT: return image::ImagePrecision::float32; - case TINYGLTF_COMPONENT_TYPE_DOUBLE: return image::ImagePrecision::float64; - case TINYGLTF_COMPONENT_TYPE_SHORT: // unsupported, fallthrough - case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: // unsupported, fallthrough - default: - logger().warn("Loading image with unsupported pixel precision!"); - return image::ImagePrecision::unknown; - } - }(image.pixel_type); - limage.data = std::make_unique( - limage.get_element_size() * image.width * image.component, - image.height, - 1); - std::copy(image.image.begin(), image.image.end(), limage.data->data()); - if (!image.extensions.empty()) { - limage.extensions = convert_extension_map(image.extensions, options); + if (!image.uri.empty()) { + // Image data is stored in a file. + limage.uri = image.uri; + } + + // TinyGltf always loads images into memory. + scene::ImageBufferExperimental& limage_buffer = limage.image; + limage_buffer.width = image.width; + limage_buffer.height = image.height; + limage_buffer.num_channels = image.component; + + switch (image.pixel_type) { + case TINYGLTF_COMPONENT_TYPE_BYTE: + limage_buffer.element_type = AttributeValueType::e_int8_t; + break; + case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: + limage_buffer.element_type = AttributeValueType::e_uint8_t; + break; + case TINYGLTF_COMPONENT_TYPE_INT: + limage_buffer.element_type = AttributeValueType::e_int32_t; + break; + case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT: + limage_buffer.element_type = AttributeValueType::e_uint32_t; + break; + case TINYGLTF_COMPONENT_TYPE_FLOAT: + limage_buffer.element_type = AttributeValueType::e_float; + break; + case TINYGLTF_COMPONENT_TYPE_DOUBLE: + limage_buffer.element_type = AttributeValueType::e_double; + break; + case TINYGLTF_COMPONENT_TYPE_SHORT: + limage_buffer.element_type = AttributeValueType::e_int16_t; + break; + case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: + limage_buffer.element_type = AttributeValueType::e_uint16_t; + break; + default: + logger().warn("Loading image with unsupported pixel precision!"); + throw std::runtime_error("Unsupported pixel type"); } + limage_buffer.data = std::move(image.image); - lscene.images.emplace_back(std::move(limage)); + lscene.add(std::move(limage)); } for (const tinygltf::Light& light : model.lights) { @@ -804,7 +796,7 @@ SceneType load_scene_gltf(const tinygltf::Model& model, const LoadOptions& optio llight.extensions = convert_extension_map(light.extensions, options); } - lscene.lights.push_back(llight); + lscene.add(llight); } for (const tinygltf::Camera& camera : model.cameras) { @@ -830,14 +822,13 @@ SceneType load_scene_gltf(const tinygltf::Model& model, const LoadOptions& optio lcam.extensions = convert_extension_map(camera.extensions, options); } - lscene.cameras.push_back(lcam); + lscene.add(lcam); } // TODO model.skins ? std::function create_node; create_node = [&](const tinygltf::Node& node, size_t parent_idx) -> size_t { - size_t lnode_idx = lscene.nodes.size(); - lscene.nodes.push_back(scene::Node()); + size_t lnode_idx = lscene.add(scene::Node()); auto& lnode = lscene.nodes.back(); lnode.name = node.name; @@ -964,28 +955,28 @@ SceneType load_scene_gltf(std::istream& input_stream, const LoadOptions& options // explicit template instantiations // ===================================== -#define LA_X_load_mesh_gltf(_, S, I) \ - template SurfaceMesh load_mesh_gltf(const fs::path&, const LoadOptions&); \ - template SurfaceMesh load_mesh_gltf(std::istream&, const LoadOptions&); +#define LA_X_load_mesh_gltf(_, S, I) \ + template LA_IO_API SurfaceMesh load_mesh_gltf(const fs::path&, const LoadOptions&); \ + template LA_IO_API SurfaceMesh load_mesh_gltf(std::istream&, const LoadOptions&); LA_SURFACE_MESH_X(load_mesh_gltf, 0); #undef LA_X_load_mesh_gltf -#define LA_X_load_simple_scene_gltf(_, S, I, D) \ - template scene::SimpleScene load_simple_scene_gltf( \ - const fs::path& filename, \ - const LoadOptions& options); \ - template scene::SimpleScene load_simple_scene_gltf( \ - std::istream& input_stream, \ +#define LA_X_load_simple_scene_gltf(_, S, I, D) \ + template LA_IO_API scene::SimpleScene load_simple_scene_gltf( \ + const fs::path& filename, \ + const LoadOptions& options); \ + template LA_IO_API scene::SimpleScene load_simple_scene_gltf( \ + std::istream& input_stream, \ const LoadOptions& options); LA_SIMPLE_SCENE_X(load_simple_scene_gltf, 0); #undef LA_X_load_simple_scene_gltf -#define LA_X_load_scene_gltf(_, S, I) \ - template scene::Scene load_scene_gltf( \ - const fs::path& filename, \ - const LoadOptions& options); \ - template scene::Scene load_scene_gltf( \ - std::istream& input_stream, \ +#define LA_X_load_scene_gltf(_, S, I) \ + template LA_IO_API scene::Scene load_scene_gltf( \ + const fs::path& filename, \ + const LoadOptions& options); \ + template LA_IO_API scene::Scene load_scene_gltf( \ + std::istream& input_stream, \ const LoadOptions& options); LA_SCENE_X(load_scene_gltf, 0); #undef LA_X_load_scene_gltf diff --git a/modules/io/src/load_mesh.cpp b/modules/io/src/load_mesh.cpp index ed50dc97..4d67d2b2 100644 --- a/modules/io/src/load_mesh.cpp +++ b/modules/io/src/load_mesh.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -19,6 +20,7 @@ #include #include #include + #ifdef LAGRANGE_WITH_ASSIMP #include #endif @@ -71,12 +73,12 @@ MeshType load_mesh(const fs::path& filename, const LoadOptions& options) } } -#define LA_X_load_mesh(_, S, I) \ - template SurfaceMesh load_mesh, nullptr>( \ - std::istream&, \ - const LoadOptions&); \ - template SurfaceMesh load_mesh, nullptr>( \ - const fs::path&, \ +#define LA_X_load_mesh(_, S, I) \ + template LA_IO_API SurfaceMesh load_mesh, nullptr>( \ + std::istream&, \ + const LoadOptions&); \ + template LA_IO_API SurfaceMesh load_mesh, nullptr>( \ + const fs::path&, \ const LoadOptions&); LA_SURFACE_MESH_X(load_mesh, 0); diff --git a/modules/io/src/load_mesh_msh.cpp b/modules/io/src/load_mesh_msh.cpp index 77e45719..e5f67028 100644 --- a/modules/io/src/load_mesh_msh.cpp +++ b/modules/io/src/load_mesh_msh.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -262,9 +263,11 @@ MeshType load_mesh_msh(const fs::path& filename, const LoadOptions& options) return load_mesh_msh(fin, options); } -#define LA_X_load_mesh_msh(_, S, I) \ - template SurfaceMesh load_mesh_msh(std::istream&, const LoadOptions& options); \ - template SurfaceMesh load_mesh_msh(const fs::path& filename, const LoadOptions& options); +#define LA_X_load_mesh_msh(_, S, I) \ + template LA_IO_API SurfaceMesh load_mesh_msh(std::istream&, const LoadOptions& options); \ + template LA_IO_API SurfaceMesh load_mesh_msh( \ + const fs::path& filename, \ + const LoadOptions& options); LA_SURFACE_MESH_X(load_mesh_msh, 0) #undef LA_X_load_mesh_msh diff --git a/modules/io/src/load_mesh_ply.cpp b/modules/io/src/load_mesh_ply.cpp index 981c1fcd..8fb5ca55 100644 --- a/modules/io/src/load_mesh_ply.cpp +++ b/modules/io/src/load_mesh_ply.cpp @@ -16,9 +16,10 @@ #include #include #include +#include #include -#include #include +#include #include // clang-format off @@ -340,9 +341,11 @@ MeshType load_mesh_ply(const fs::path& filename, const LoadOptions& options) return load_mesh_ply(fin, options); } -#define LA_X_load_mesh_ply(_, S, I) \ - template SurfaceMesh load_mesh_ply(std::istream&, const LoadOptions& options); \ - template SurfaceMesh load_mesh_ply(const fs::path& filename, const LoadOptions& options); +#define LA_X_load_mesh_ply(_, S, I) \ + template LA_IO_API SurfaceMesh load_mesh_ply(std::istream&, const LoadOptions& options); \ + template LA_IO_API SurfaceMesh load_mesh_ply( \ + const fs::path& filename, \ + const LoadOptions& options); LA_SURFACE_MESH_X(load_mesh_ply, 0) } // namespace lagrange::io diff --git a/modules/io/src/load_obj.cpp b/modules/io/src/load_obj.cpp index 917b8431..7929bc94 100644 --- a/modules/io/src/load_obj.cpp +++ b/modules/io/src/load_obj.cpp @@ -11,9 +11,11 @@ */ // this .cpp provides implementation for functions defined in those headers: +#include #include #include #include + // ==== #include @@ -164,7 +166,9 @@ ObjReaderResult extract_mes facet_counts.push_back(static_cast(shape.mesh.num_face_vertices.size())); result.names.push_back(shape.name); } - mesh.add_hybrid(facet_sizes); + if (!facet_sizes.empty()) { + mesh.add_hybrid(facet_sizes); + } // Initialize material id attr Attribute* mat_attr = nullptr; @@ -344,7 +348,7 @@ SceneType load_scene_obj(const tinyobj::ObjReader& reader, const LoadOptions& op auto result = extract_mesh(reader, options); SceneType lscene; - scene::ElementId mesh_idx = scene::utils::add_mesh(lscene, result.mesh); + scene::ElementId mesh_idx = lscene.add(std::move(result.mesh)); // make a node to hold the meshes scene::Node lnode; @@ -353,24 +357,24 @@ SceneType load_scene_obj(const tinyobj::ObjReader& reader, const LoadOptions& op for (const tinyobj::material_t& mat : reader.GetMaterials()) { scene::MaterialExperimental lmat; lmat.name = mat.name; - + // we use the PBR extension in tinyobj, but note that this data may not be in the mtl // http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr lmat.base_color_value = Eigen::Vector4(mat.diffuse[0], mat.diffuse[1], mat.diffuse[2], 1.0); - lmat.emissive_value = Eigen::Vector3(mat.emission[0], mat.emission[1], mat.emission[2]); + lmat.emissive_value = + Eigen::Vector3(mat.emission[0], mat.emission[1], mat.emission[2]); auto try_load_texture = [&](const std::string& name, const tinyobj::texture_option_t& tex_opt, scene::TextureInfo& tex_info) { - scene::ImageLegacy limage; + scene::ImageExperimental limage; limage.name = name; limage.uri = name; if (options.load_images) { if (!io::internal::try_load_image(name, options, limage)) return; } - int image_idx = static_cast(lscene.images.size()); - lscene.images.push_back(limage); + int image_idx = static_cast(lscene.add(std::move(limage))); tex_info.index = static_cast(lscene.textures.size()); @@ -417,12 +421,12 @@ SceneType load_scene_obj(const tinyobj::ObjReader& reader, const LoadOptions& op lscene.nodes.push_back(lnode); lscene.root_nodes.push_back(0); - + return lscene; } #define LA_X_load_scene_obj(_, S, I) \ template scene::Scene load_scene_obj( \ - const tinyobj::ObjReader& reader, \ + const tinyobj::ObjReader& reader, \ const LoadOptions& options); LA_SCENE_X(load_scene_obj, 0); #undef LA_X_load_scene_obj @@ -449,13 +453,13 @@ auto load_mesh_obj( return extract_mesh(reader, options); } -#define LA_X_load_mesh(_, Scalar, Index) \ - template ObjReaderResult load_mesh_obj>( \ - const fs::path& filename, \ - const LoadOptions& options); \ - template ObjReaderResult load_mesh_obj>( \ - std::istream & input_stream_obj, \ - std::istream & input_stream_mtl, \ +#define LA_X_load_mesh(_, Scalar, Index) \ + template LA_IO_API ObjReaderResult load_mesh_obj>( \ + const fs::path& filename, \ + const LoadOptions& options); \ + template LA_IO_API ObjReaderResult load_mesh_obj>( \ + std::istream & input_stream_obj, \ + std::istream & input_stream_mtl, \ const LoadOptions& options); LA_SURFACE_MESH_X(load_mesh, 0) @@ -486,11 +490,11 @@ MeshType load_mesh_obj(std::istream& input_stream_obj, const LoadOptions& option return std::move(ret.mesh); } -#define LA_X_load_mesh_obj(_, S, I) \ - template SurfaceMesh load_mesh_obj( \ - const fs::path& filename, \ - const LoadOptions& options); \ - template SurfaceMesh load_mesh_obj(std::istream&, const LoadOptions& options); +#define LA_X_load_mesh_obj(_, S, I) \ + template LA_IO_API SurfaceMesh load_mesh_obj( \ + const fs::path& filename, \ + const LoadOptions& options); \ + template LA_IO_API SurfaceMesh load_mesh_obj(std::istream&, const LoadOptions& options); LA_SURFACE_MESH_X(load_mesh_obj, 0) #undef LA_X_load_mesh_obj @@ -507,14 +511,20 @@ SceneType load_scene_obj(const fs::path& filename, const LoadOptions& options) } template -SceneType load_scene_obj(std::istream& input_stream_obj, std::istream& input_stream_mtl, const LoadOptions& options) +SceneType load_scene_obj( + std::istream& input_stream_obj, + std::istream& input_stream_mtl, + const LoadOptions& options) { tinyobj::ObjReader reader = internal::load_obj(input_stream_obj, input_stream_mtl, options); return internal::load_scene_obj(reader, options); } -#define LA_X_load_scene_obj(_, S, I) \ - template scene::Scene load_scene_obj(const fs::path&, const LoadOptions&); \ - template scene::Scene load_scene_obj(std::istream&, std::istream&, const LoadOptions&); +#define LA_X_load_scene_obj(_, S, I) \ + template LA_IO_API scene::Scene load_scene_obj(const fs::path&, const LoadOptions&); \ + template LA_IO_API scene::Scene load_scene_obj( \ + std::istream&, \ + std::istream&, \ + const LoadOptions&); LA_SCENE_X(load_scene_obj, 0); #undef LA_X_load_scene_obj } // namespace lagrange::io diff --git a/modules/io/src/load_scene.cpp b/modules/io/src/load_scene.cpp index 29979090..44719804 100644 --- a/modules/io/src/load_scene.cpp +++ b/modules/io/src/load_scene.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -44,8 +45,10 @@ SceneType load_scene(const fs::path& filename, const LoadOptions& options) #endif } } -#define LA_X_load_scene(_, S, I) \ - template scene::Scene load_scene(const fs::path& filename, const LoadOptions& options); +#define LA_X_load_scene(_, S, I) \ + template LA_IO_API scene::Scene load_scene( \ + const fs::path& filename, \ + const LoadOptions& options); LA_SCENE_X(load_scene, 0); } // namespace lagrange::io diff --git a/modules/io/src/load_simple_scene.cpp b/modules/io/src/load_simple_scene.cpp index 69da307b..e3415c8b 100644 --- a/modules/io/src/load_simple_scene.cpp +++ b/modules/io/src/load_simple_scene.cpp @@ -11,6 +11,7 @@ */ #include +#include #include #include #include @@ -41,9 +42,9 @@ SceneType load_simple_scene(const fs::path& filename, const LoadOptions& options } return SceneType(); } -#define LA_X_load_simple_scene(_, S, I, D) \ - template scene::SimpleScene load_simple_scene( \ - const fs::path& filename, \ +#define LA_X_load_simple_scene(_, S, I, D) \ + template LA_IO_API scene::SimpleScene load_simple_scene( \ + const fs::path& filename, \ const LoadOptions& options); LA_SIMPLE_SCENE_X(load_simple_scene, 0); diff --git a/modules/io/src/save_gltf.cpp b/modules/io/src/save_gltf.cpp index 948e0a7b..f5cbfaf7 100644 --- a/modules/io/src/save_gltf.cpp +++ b/modules/io/src/save_gltf.cpp @@ -11,12 +11,15 @@ */ // this .cpp provides implementations for functions defined in those headers: +#include #include #include #include + // ==== #include +#include #include #include #include @@ -49,7 +52,8 @@ namespace lagrange::io { // namespace { -tinygltf::Value convert_value(const scene::Value& value) { +tinygltf::Value convert_value(const scene::Value& value) +{ switch (value.get_type_index()) { case scene::Value::bool_index(): return tinygltf::Value(value.get_bool()); case scene::Value::int_index(): return tinygltf::Value(value.get_int()); @@ -117,8 +121,8 @@ void save_gltf(const fs::path& filename, const tinygltf::Model& model, const Sav } else if (!binary && options.encoding != FileEncoding::Ascii) { // this is extremely common, since the default value of `encoding` is binary. // But trying to save as `.gltf` is explicit enough, so skip this message. - - //logger().warn("Saving mesh in ascii due to `.gltf` extension."); + + // logger().warn("Saving mesh in ascii due to `.gltf` extension."); } // https://github.com/syoyo/tinygltf/issues/323 @@ -133,13 +137,12 @@ void save_gltf(const fs::path& filename, const tinygltf::Model& model, const Sav tinygltf::TinyGLTF loader; loader.SetImageWriter(write_image_data_function, &fs_callbacks); - constexpr bool embed_images = false; constexpr bool embed_buffers = true; constexpr bool pretty_print = true; bool success = loader.WriteGltfSceneToFile( &model, filename.string(), - embed_images, + options.embed_images, embed_buffers, pretty_print, binary); @@ -416,13 +419,15 @@ void populate_attributes( } } - const auto& values = [&]() -> const auto& { + const auto& values = [&]() -> const auto& + { if constexpr (AttributeType::IsIndexed) { return attr.values(); } else { return attr; } - }(); + } + (); // we are committed to writing the buffer here. Do not return early after this line. @@ -548,14 +553,14 @@ void save_mesh_gltf( save_simple_scene_gltf(output_stream, scene, options); } -#define LA_X_save_mesh_gltf(_, S, I) \ - template void save_mesh_gltf( \ - const fs::path& filename, \ - const SurfaceMesh& mesh, \ - const SaveOptions& options); \ - template void save_mesh_gltf( \ - std::ostream&, \ - const SurfaceMesh& mesh, \ +#define LA_X_save_mesh_gltf(_, S, I) \ + template LA_IO_API void save_mesh_gltf( \ + const fs::path& filename, \ + const SurfaceMesh& mesh, \ + const SaveOptions& options); \ + template LA_IO_API void save_mesh_gltf( \ + std::ostream&, \ + const SurfaceMesh& mesh, \ const SaveOptions& options); LA_SURFACE_MESH_X(save_mesh_gltf, 0) #undef LA_X_save_mesh_gltf @@ -636,14 +641,14 @@ void save_simple_scene_gltf( save_gltf(output_stream, model, options); } -#define LA_X_save_simple_scene_gltf(_, S, I, D) \ - template void save_simple_scene_gltf( \ - const fs::path& filename, \ - const scene::SimpleScene& scene, \ - const SaveOptions& options); \ - template void save_simple_scene_gltf( \ - std::ostream&, \ - const scene::SimpleScene& scene, \ +#define LA_X_save_simple_scene_gltf(_, S, I, D) \ + template LA_IO_API void save_simple_scene_gltf( \ + const fs::path& filename, \ + const scene::SimpleScene& scene, \ + const SaveOptions& options); \ + template LA_IO_API void save_simple_scene_gltf( \ + std::ostream&, \ + const scene::SimpleScene& scene, \ const SaveOptions& options); LA_SIMPLE_SCENE_X(save_simple_scene_gltf, 0); #undef LA_X_save_simple_scene_gltf @@ -735,48 +740,39 @@ tinygltf::Model lagrange_scene_to_gltf_model( for (const auto& limage : lscene.images) { tinygltf::Image image; image.name = limage.name; - image.uri = limage.uri; - image.mimeType = [](scene::ImageLegacy::Type type) { - switch (type) { - case scene::ImageLegacy::Type::Jpeg: return "image/jpeg"; - case scene::ImageLegacy::Type::Png: return "image/png"; - case scene::ImageLegacy::Type::Bmp: return "image/bmp"; - case scene::ImageLegacy::Type::Gif: return "image/gif"; - case scene::ImageLegacy::Type::Unknown: // fallthrough to default - default: return ""; - } - }(limage.type); - image.width = (int)limage.width; - image.height = (int)limage.height; - image.component = limage.get_num_channels(); - image.pixel_type = [](image::ImagePrecision precision) { - switch (precision) { - case image::ImagePrecision::int8: return TINYGLTF_COMPONENT_TYPE_BYTE; - case image::ImagePrecision::uint8: return TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE; - case image::ImagePrecision::int32: return TINYGLTF_COMPONENT_TYPE_INT; - case image::ImagePrecision::uint32: return TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT; - case image::ImagePrecision::float32: return TINYGLTF_COMPONENT_TYPE_FLOAT; - case image::ImagePrecision::float64: return TINYGLTF_COMPONENT_TYPE_DOUBLE; - default: - logger().error("Saving image with unsupported pixel precision!"); - return TINYGLTF_COMPONENT_TYPE_BYTE; - } - }(limage.precision); - image.bits = [](image::ImagePrecision precision) { - switch (precision) { - case image::ImagePrecision::int8: - case image::ImagePrecision::uint8: return 8; - case image::ImagePrecision::float16: return 16; - case image::ImagePrecision::int32: - case image::ImagePrecision::uint32: - case image::ImagePrecision::float32: return 32; - default: logger().error("Saving image with unsupported image bits."); return -1; - } - }(limage.precision); - const size_t count = - limage.get_element_size() * limage.get_num_channels() * limage.width * limage.height; - image.image = std::vector(count); - std::copy_n(limage.data->data(), count, image.image.begin()); + + const scene::ImageBufferExperimental& lbuffer = limage.image; + image.width = static_cast(lbuffer.width); + image.height = static_cast(lbuffer.height); + image.component = static_cast(lbuffer.num_channels); + switch (lbuffer.element_type) { + case AttributeValueType::e_uint8_t: + image.pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE; + break; + case AttributeValueType::e_int8_t: image.pixel_type = TINYGLTF_COMPONENT_TYPE_BYTE; break; + case AttributeValueType::e_uint16_t: + image.pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT; + break; + case AttributeValueType::e_int16_t: image.pixel_type = TINYGLTF_COMPONENT_TYPE_SHORT; break; + case AttributeValueType::e_uint32_t: + image.pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT; + break; + case AttributeValueType::e_int32_t: image.pixel_type = TINYGLTF_COMPONENT_TYPE_INT; break; + case AttributeValueType::e_float: image.pixel_type = TINYGLTF_COMPONENT_TYPE_FLOAT; break; + case AttributeValueType::e_double: image.pixel_type = TINYGLTF_COMPONENT_TYPE_DOUBLE; break; + default: + logger().error("Saving image with unsupported pixel precision!"); + // TODO: should we simply fail? + image.pixel_type = TINYGLTF_COMPONENT_TYPE_BYTE; + } + image.bits = static_cast(lbuffer.get_bits_per_element()); + std::copy(lbuffer.data.begin(), lbuffer.data.end(), std::back_inserter(image.image)); + + if (!limage.uri.empty()) { + image.uri = limage.uri.string(); + } else { + image.mimeType = "image/png"; + } if (!limage.extensions.empty()) { image.extensions = convert_extension_map(limage.extensions, options); @@ -924,14 +920,14 @@ void save_scene_gltf( save_gltf(output_stream, model, options); } -#define LA_X_save_scene_gltf(_, S, I) \ - template void save_scene_gltf( \ - const fs::path& filename, \ - const scene::Scene& scene, \ - const SaveOptions& options); \ - template void save_scene_gltf( \ - std::ostream&, \ - const scene::Scene& scene, \ +#define LA_X_save_scene_gltf(_, S, I) \ + template LA_IO_API void save_scene_gltf( \ + const fs::path& filename, \ + const scene::Scene& scene, \ + const SaveOptions& options); \ + template LA_IO_API void save_scene_gltf( \ + std::ostream&, \ + const scene::Scene& scene, \ const SaveOptions& options); LA_SCENE_X(save_scene_gltf, 0); #undef LA_X_save_scene_gltf diff --git a/modules/io/src/save_mesh.cpp b/modules/io/src/save_mesh.cpp index 88ac2e03..1fdd11a9 100644 --- a/modules/io/src/save_mesh.cpp +++ b/modules/io/src/save_mesh.cpp @@ -10,11 +10,13 @@ * governing permissions and limitations under the License. */ -#include #include + +#include #include #include +#include #include #include #include @@ -61,12 +63,12 @@ void save_mesh( } #define LA_X_save_mesh(_, S, I) \ - template void save_mesh( \ + template LA_IO_API void save_mesh( \ std::ostream&, \ const SurfaceMesh&, \ FileFormat, \ const SaveOptions&); \ - template void save_mesh(const fs::path&, const SurfaceMesh&, const SaveOptions&); + template LA_IO_API void save_mesh(const fs::path&, const SurfaceMesh&, const SaveOptions&); LA_SURFACE_MESH_X(save_mesh, 0); } // namespace lagrange::io diff --git a/modules/io/src/save_mesh_msh.cpp b/modules/io/src/save_mesh_msh.cpp index 5d3ab1ee..0e279e5d 100644 --- a/modules/io/src/save_mesh_msh.cpp +++ b/modules/io/src/save_mesh_msh.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -473,14 +474,14 @@ void save_mesh_msh( save_mesh_msh(fout, mesh, options); } -#define LA_X_save_mesh_msh(_, S, I) \ - template void save_mesh_msh( \ - std::ostream&, \ - const SurfaceMesh& mesh, \ - const SaveOptions& options); \ - template void save_mesh_msh( \ - const fs::path& filename, \ - const SurfaceMesh& mesh, \ +#define LA_X_save_mesh_msh(_, S, I) \ + template LA_IO_API void save_mesh_msh( \ + std::ostream&, \ + const SurfaceMesh& mesh, \ + const SaveOptions& options); \ + template LA_IO_API void save_mesh_msh( \ + const fs::path& filename, \ + const SurfaceMesh& mesh, \ const SaveOptions& options); LA_SURFACE_MESH_X(save_mesh_msh, 0) #undef LA_X_save_mesh_msh diff --git a/modules/io/src/save_mesh_obj.cpp b/modules/io/src/save_mesh_obj.cpp index 8ca2d716..ddbb9f89 100644 --- a/modules/io/src/save_mesh_obj.cpp +++ b/modules/io/src/save_mesh_obj.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include // clang-format off @@ -176,11 +177,11 @@ void save_mesh_obj( } #define LA_X_save_mesh(_, Scalar, Index) \ - template void save_mesh_obj( \ + template LA_IO_API void save_mesh_obj( \ std::ostream& output_stream, \ const SurfaceMesh& mesh, \ const SaveOptions& options); \ - template void save_mesh_obj( \ + template LA_IO_API void save_mesh_obj( \ const fs::path& filename, \ const SurfaceMesh& mesh, \ const SaveOptions& options); diff --git a/modules/io/src/save_mesh_ply.cpp b/modules/io/src/save_mesh_ply.cpp index bd32ce10..670ac725 100644 --- a/modules/io/src/save_mesh_ply.cpp +++ b/modules/io/src/save_mesh_ply.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -381,11 +382,11 @@ void save_mesh_ply( } #define LA_X_save_mesh_ply(_, Scalar, Index) \ - template void save_mesh_ply( \ + template LA_IO_API void save_mesh_ply( \ std::ostream&, \ const SurfaceMesh& mesh, \ const SaveOptions& options); \ - template void save_mesh_ply( \ + template LA_IO_API void save_mesh_ply( \ const fs::path& filename, \ const SurfaceMesh& mesh, \ const SaveOptions& options); diff --git a/modules/io/src/save_scene.cpp b/modules/io/src/save_scene.cpp index f4576193..f1c9eb5c 100644 --- a/modules/io/src/save_scene.cpp +++ b/modules/io/src/save_scene.cpp @@ -12,6 +12,7 @@ #include +#include #include #include #include @@ -35,7 +36,7 @@ void save_scene( } #define LA_X_save_scene(_, S, I) \ - template void save_scene( \ + template LA_IO_API void save_scene( \ const fs::path& filename, \ const scene::Scene& scene, \ const SaveOptions& options); diff --git a/modules/io/src/save_simple_scene.cpp b/modules/io/src/save_simple_scene.cpp index 0913c5ee..76033faa 100644 --- a/modules/io/src/save_simple_scene.cpp +++ b/modules/io/src/save_simple_scene.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -46,7 +47,7 @@ void save_simple_scene( } #define LA_X_save_simple_scene(_, S, I, D) \ - template void save_simple_scene( \ + template LA_IO_API void save_simple_scene( \ const fs::path& filename, \ const scene::SimpleScene& scene, \ const SaveOptions& options); diff --git a/modules/io/tests/test_load_assimp.cpp b/modules/io/tests/test_load_assimp.cpp index ac8942a2..f4a9de89 100644 --- a/modules/io/tests/test_load_assimp.cpp +++ b/modules/io/tests/test_load_assimp.cpp @@ -12,18 +12,20 @@ #ifdef LAGRANGE_WITH_ASSIMP -#include -#include -#include -#include -#include -#include -#include -#include + #include + #include + #include + #include + #include + #include + #include + #include + #include using namespace lagrange; -TEST_CASE("load_mesh_assimp", "[io]") { +TEST_CASE("load_mesh_assimp", "[io]") +{ auto mesh = io::load_mesh_assimp( testing::get_data_path("open/core/drop_tri.obj")); REQUIRE(mesh.get_num_facets() > 0); @@ -47,7 +49,7 @@ TEST_CASE("load_assimp_fbx", "[io]") auto lmesh = lagrange::io::internal::load_mesh_assimp(*scene); REQUIRE(lmesh.get_num_facets() > 0); REQUIRE(lmesh.get_num_vertices() > 0); - //REQUIRE(lmesh.has_attribute(AttributeName::texcoord)); + // REQUIRE(lmesh.has_attribute(AttributeName::texcoord)); REQUIRE(lmesh.has_attribute(AttributeName::normal)); REQUIRE(lmesh.has_attribute(AttributeName::indexed_joint)); REQUIRE(lmesh.has_attribute(AttributeName::indexed_weight)); @@ -72,15 +74,29 @@ TEST_CASE("load_assimp_glb", "[io]") REQUIRE(scene->mNumMaterials == 2); REQUIRE(scene->mMaterials[mesh->mMaterialIndex]->mNumProperties > 0); - using Index = lagrange::SurfaceMesh32f::Index; - auto lmesh = lagrange::io::internal::load_mesh_assimp(*scene); - REQUIRE(lmesh.get_num_vertices() == lagrange::safe_cast(mesh->mNumVertices)); - REQUIRE(lmesh.get_num_facets() == lagrange::safe_cast(mesh->mNumFaces)); - REQUIRE(lmesh.has_attribute(std::string(AttributeName::texcoord) + "_0")); - REQUIRE(lmesh.has_attribute(AttributeName::normal)); + SECTION("Convert to mesh") + { + using Index = lagrange::SurfaceMesh32f::Index; + auto lmesh = lagrange::io::internal::load_mesh_assimp(*scene); + REQUIRE(lmesh.get_num_vertices() == lagrange::safe_cast(mesh->mNumVertices)); + REQUIRE(lmesh.get_num_facets() == lagrange::safe_cast(mesh->mNumFaces)); + REQUIRE(lmesh.has_attribute(std::string(AttributeName::texcoord) + "_0")); + REQUIRE(lmesh.has_attribute(AttributeName::normal)); + } + + SECTION("Convert to scene") + { + using SceneType = lagrange::scene::Scene; + auto lscene = lagrange::io::internal::load_scene_assimp(*scene); + REQUIRE(lscene.images.size() == 1); + REQUIRE(lscene.textures.size() == 1); + REQUIRE(lscene.textures.front().image == 0); + REQUIRE(lscene.materials.front().base_color_texture.index == 0); + } } -TEST_CASE("load_simple_scene_assimp", "[io]") { +TEST_CASE("load_simple_scene_assimp", "[io]") +{ auto scene = lagrange::io::load_simple_scene_assimp( lagrange::testing::get_data_path("open/io/three_cubes_instances.gltf")); REQUIRE(scene.get_num_meshes() == 1); diff --git a/modules/io/tests/test_load_gltf.cpp b/modules/io/tests/test_load_gltf.cpp index 72d5ffb9..089c8a0f 100644 --- a/modules/io/tests/test_load_gltf.cpp +++ b/modules/io/tests/test_load_gltf.cpp @@ -12,7 +12,9 @@ #include #include #include +#include #include +#include #include using namespace lagrange; @@ -119,3 +121,20 @@ TEST_CASE("load_glb_triangle", "[io]") REQUIRE(mesh.get_num_vertices() == 3); REQUIRE(mesh.get_num_facets() == 1); } + +TEST_CASE("load_gltf_point_cloud", "[io][gltf]") +{ + using Scalar = double; + using Index = uint32_t; + + lagrange::SurfaceMesh mesh; + mesh.add_vertex({0, 0, 0}); + + std::stringstream ss; + auto scene = lagrange::scene::mesh_to_simple_scene(mesh); + REQUIRE(scene.get_num_meshes() == 1); + lagrange::io::save_simple_scene_gltf(ss, scene); + auto scene2 = + lagrange::io::load_simple_scene_gltf>(ss); + REQUIRE(scene2.get_num_meshes() == 1); +} diff --git a/modules/io/tests/test_load_scene.cpp b/modules/io/tests/test_load_scene.cpp index 58402fb5..f40b04b0 100644 --- a/modules/io/tests/test_load_scene.cpp +++ b/modules/io/tests/test_load_scene.cpp @@ -149,8 +149,13 @@ TEST_CASE("load_scene_avocado", "[io]") REQUIRE(!scene.materials.empty()); REQUIRE(scene.textures.size() >= 2); // 2 or 3 REQUIRE(scene.images.size() >= 2); // 2 or 3 - REQUIRE(scene.images.front().height > 0); - REQUIRE(scene.images.front().width > 0); + + for (const auto& image : scene.images) { + REQUIRE(!image.uri.empty()); + auto& img = image.image; + REQUIRE(img.width > 0); + REQUIRE(img.height > 0); + } REQUIRE(scene.lights.empty()); REQUIRE(scene.cameras.empty()); REQUIRE(scene.skeletons.empty()); @@ -269,7 +274,8 @@ TEST_CASE("load_save_scene_obj", "[io]") io::save_mesh_obj("avocado_from_obj.obj", mesh); } -TEST_CASE("load_gltf_gsplat", "[io]" LA_CORP_FLAG) { +TEST_CASE("load_gltf_gsplat", "[io]" LA_CORP_FLAG) +{ auto scene = io::load_scene_gltf( testing::get_data_path("corp/io/neural_assets/High_Heel.gltf")); REQUIRE(!scene.nodes[0].extensions.data.empty()); @@ -315,7 +321,8 @@ TEST_CASE("scene_extension_user", "[scene]" LA_CORP_FLAG) io::LoadOptions load_opt; load_opt.extension_converters = {&converter}; auto scene = io::load_scene_gltf( - testing::get_data_path("corp/io/neural_assets/High_Heel.gltf"), load_opt); + testing::get_data_path("corp/io/neural_assets/High_Heel.gltf"), + load_opt); REQUIRE(scene.nodes[0].extensions.data.size() == 0); REQUIRE(scene.nodes[0].extensions.user_data.size() == 1); diff --git a/modules/io/tests/test_obj.cpp b/modules/io/tests/test_obj.cpp new file mode 100644 index 00000000..2a7c4706 --- /dev/null +++ b/modules/io/tests/test_obj.cpp @@ -0,0 +1,65 @@ +/* + * Copyright 2023 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. + */ +#include +#include +#include +#include +#include +#include + +#include + +TEST_CASE("Grenade_H", "[mesh][io]" LA_CORP_FLAG) +{ + using namespace lagrange; + auto mesh = io::load_mesh_obj(testing::get_data_path("corp/io/Grenade_H.obj")); +} + +TEST_CASE("io/obj", "[io][obj]") +{ + using namespace lagrange; + using Scalar = double; + using Index = uint32_t; + + auto mesh = testing::create_test_sphere(); + std::stringstream data; + io::SaveOptions save_options; + save_options.output_attributes = io::SaveOptions::OutputAttributes::All; + save_options.attribute_conversion_policy = + io::SaveOptions::AttributeConversionPolicy::ConvertAsNeeded; + + save_options.encoding = io::FileEncoding::Ascii; + REQUIRE_NOTHROW(io::save_mesh_obj(data, mesh, save_options)); + auto mesh2 = io::load_mesh_obj>(data); + testing::check_mesh(mesh2); + testing::ensure_approx_equivalent_mesh(mesh, mesh2); +} + +TEST_CASE("io/obj empty", "[io][obj]") +{ + using namespace lagrange; + using Scalar = double; + using Index = uint32_t; + + SurfaceMesh mesh; + std::stringstream data; + io::SaveOptions save_options; + save_options.output_attributes = io::SaveOptions::OutputAttributes::All; + save_options.attribute_conversion_policy = + io::SaveOptions::AttributeConversionPolicy::ConvertAsNeeded; + + save_options.encoding = io::FileEncoding::Ascii; + REQUIRE_NOTHROW(io::save_mesh_obj(data, mesh, save_options)); + auto mesh2 = io::load_mesh_obj>(data); + testing::check_mesh(mesh2); + testing::ensure_approx_equivalent_mesh(mesh, mesh2); +} diff --git a/modules/io/tests/test_ply.cpp b/modules/io/tests/test_ply.cpp index 87769be5..33050642 100644 --- a/modules/io/tests/test_ply.cpp +++ b/modules/io/tests/test_ply.cpp @@ -65,7 +65,7 @@ TEST_CASE("io/ply empty", "[io][ply]") using Scalar = double; using Index = uint32_t; - auto mesh = testing::create_test_sphere(); + SurfaceMesh mesh; std::stringstream data; io::SaveOptions save_options; save_options.output_attributes = io::SaveOptions::OutputAttributes::All; diff --git a/modules/partitioning/CMakeLists.txt b/modules/partitioning/CMakeLists.txt index 452b2309..7ead108e 100644 --- a/modules/partitioning/CMakeLists.txt +++ b/modules/partitioning/CMakeLists.txt @@ -21,13 +21,7 @@ target_link_libraries(lagrange_partitioning metis::metis ) -# 3. installation -if(LAGRANGE_INSTALL) - set_target_properties(lagrange_partitioning PROPERTIES EXPORT_NAME partitioning) - lagrange_install(lagrange_partitioning) -endif() - -# 4. unit tests and examples +# 3. unit tests and examples if(LAGRANGE_UNIT_TESTS) add_subdirectory(tests) endif() diff --git a/modules/partitioning/include/lagrange/partitioning/api.h b/modules/partitioning/include/lagrange/partitioning/api.h new file mode 100644 index 00000000..ee67d501 --- /dev/null +++ b/modules/partitioning/include/lagrange/partitioning/api.h @@ -0,0 +1,23 @@ +#pragma once + +#ifdef LA_PARTITIONING_STATIC_DEFINE + #define LA_PARTITIONING_API +#else + #ifndef LA_PARTITIONING_API + #ifdef lagrange_partitioning_EXPORTS + // We are building this library + #if defined(_WIN32) || defined(_WIN64) + #define LA_PARTITIONING_API __declspec(dllexport) + #else + #define LA_PARTITIONING_API __attribute__((visibility("default"))) + #endif + #else + // We are using this library + #if defined(_WIN32) || defined(_WIN64) + #define LA_PARTITIONING_API __declspec(dllimport) + #else + #define LA_PARTITIONING_API __attribute__((visibility("default"))) + #endif + #endif + #endif +#endif diff --git a/modules/raycasting/CMakeLists.txt b/modules/raycasting/CMakeLists.txt index c4df26c9..fe878b4d 100644 --- a/modules/raycasting/CMakeLists.txt +++ b/modules/raycasting/CMakeLists.txt @@ -21,19 +21,12 @@ target_link_libraries(lagrange_raycasting PUBLIC embree::embree ) -# 3. options option(LAGRANGE_EMBREE_DEBUG "Perform error-checking when performing each single Embree call" OFF) if(LAGRANGE_EMBREE_DEBUG) target_compile_definitions(lagrange_raycasting PUBLIC LAGRANGE_EMBREE_DEBUG) endif() -# 4. installation -if(LAGRANGE_INSTALL) - set_target_properties(lagrange_raycasting PROPERTIES EXPORT_NAME raycasting) - lagrange_install(lagrange_raycasting) -endif() - -# 5. unit tests and examples +# 3. unit tests and examples if(LAGRANGE_UNIT_TESTS) add_subdirectory(tests) endif() diff --git a/modules/raycasting/include/lagrange/raycasting/api.h b/modules/raycasting/include/lagrange/raycasting/api.h new file mode 100644 index 00000000..d365ed87 --- /dev/null +++ b/modules/raycasting/include/lagrange/raycasting/api.h @@ -0,0 +1,23 @@ +#pragma once + +#ifdef LA_RAYCASTING_STATIC_DEFINE + #define LA_RAYCASTING_API +#else + #ifndef LA_RAYCASTING_API + #ifdef lagrange_raycasting_EXPORTS + // We are building this library + #if defined(_WIN32) || defined(_WIN64) + #define LA_RAYCASTING_API __declspec(dllexport) + #else + #define LA_RAYCASTING_API __attribute__((visibility("default"))) + #endif + #else + // We are using this library + #if defined(_WIN32) || defined(_WIN64) + #define LA_RAYCASTING_API __declspec(dllimport) + #else + #define LA_RAYCASTING_API __attribute__((visibility("default"))) + #endif + #endif + #endif +#endif diff --git a/modules/scene/CMakeLists.txt b/modules/scene/CMakeLists.txt index f6b4f60c..496803da 100644 --- a/modules/scene/CMakeLists.txt +++ b/modules/scene/CMakeLists.txt @@ -17,29 +17,19 @@ if(LAGRANGE_TOPLEVEL_PROJECT) set_target_properties(lagrange_scene PROPERTIES COMPILE_WARNING_AS_ERROR ON) endif() -lagrange_include_modules(image) +lagrange_include_modules(fs) # 2. dependencies target_link_libraries(lagrange_scene lagrange::core - lagrange::image + lagrange::fs ) -# 3. installation -if(LAGRANGE_INSTALL) - set_target_properties(lagrange_scene PROPERTIES EXPORT_NAME scene) - lagrange_install(lagrange_scene) -endif() - # 3. unit tests and examples if(LAGRANGE_UNIT_TESTS) add_subdirectory(tests) endif() -# if(LAGRANGE_EXAMPLES) -# add_subdirectory(examples) -# endif() - if(LAGRANGE_MODULE_PYTHON) add_subdirectory(python) endif() diff --git a/modules/scene/include/lagrange/scene/Scene.h b/modules/scene/include/lagrange/scene/Scene.h index 21117ce0..7412e7de 100644 --- a/modules/scene/include/lagrange/scene/Scene.h +++ b/modules/scene/include/lagrange/scene/Scene.h @@ -12,9 +12,11 @@ #pragma once #include -#include -#include +#include +#include #include +#include +#include #include @@ -22,16 +24,25 @@ #include namespace lagrange { -namespace scene { + +/// +/// Enum describing at runtime the value type of an attribute. This can be accessed from the base +/// attribute class and enables safe downcasting without global RTTI. +/// +enum class AttributeValueType : uint8_t; + +} // namespace lagrange + +namespace lagrange::scene { using ElementId = size_t; constexpr ElementId invalid_element = lagrange::invalid(); // Used in Node, it pairs a mesh with its materials (zero, one, or more). -struct SceneMeshInstance +struct LA_SCENE_API SceneMeshInstance { // Mesh index. Has to be a valid index in the scene.meshes vector. - ElementId mesh = lagrange::invalid(); + ElementId mesh = invalid_element; // Material indices in the scene.materials vector. // When a single mesh uses multiple materials, @@ -40,7 +51,7 @@ struct SceneMeshInstance }; // Represents a node in the scene hierarchy. -struct Node +struct LA_SCENE_API Node { // Note that the node name may not be unique, and can be empty. std::string name; @@ -49,7 +60,7 @@ struct Node Eigen::Affine3f transform = Eigen::Affine3f::Identity(); // parent index. May be invalid if the node has no parent (e.g. the root). - ElementId parent = lagrange::invalid(); + ElementId parent = invalid_element; // children indices. May be empty. std::vector children; @@ -69,78 +80,58 @@ struct Node Extensions extensions; }; -// -// Based on the image module, which is due for a rework, so the image data will change in the -// future. -// -// Describes a single image. Note that it may not actually contain image data, and only -// have a reference to a file on disk. This will always happen when LoadOptions.save_images == -// false. In this case, width/height/precision/channel/data will contain the default values. -// -struct ImageLegacy +/// +/// Minimalistic image data structure that stores the raw image data. +/// +struct LA_SCENE_API ImageBufferExperimental { - // Node that the name may not be unique, and can be empty - std::string name; - - // Image width. + /// Image width. size_t width = 0; - // Image height. + /// Image height. size_t height = 0; - // Image precision. You can also use `ImageLegacy.get_element_size` to get the byte-size of the - // elements. - image::ImagePrecision precision = image::ImagePrecision::unknown; + /// Number of image channels (must be 1, 3, or 4). + size_t num_channels = 4; - // Image channels. You can also use `ImageLegacy.get_channel_count` to get this as a number. - image::ImageChannel channel = image::ImageChannel::unknown; + /// The scalar type of the elements in the buffer. + AttributeValueType element_type; - // Image pixel data. Check ImageStorage for details. This will change in the future. - std::shared_ptr data; + /// Raw buffer of size (width * height * num_channels * num_bits_per_element / 8) bytes containing image data. + std::vector data; - // URI or IRI of the image. Optional, can be empty. - // Relative paths are relative to the main file asset. - // Note that you should never have to read from disk, as the data is above. - // During export, if this is non-empty, and it is supported by the file format, - // then the image will be saved as an external asset with this filename. - std::string uri; + /// + /// Get the size of an element in bits. + /// + size_t get_bits_per_element() const; +}; - // Image file type. Can be unknown. - enum class Type { Jpeg, Png, Bmp, Gif, Unknown }; - Type type = Type::Unknown; +/// +/// Image structure that can store either image data or reference to an image file. +/// +struct LA_SCENE_API ImageExperimental +{ + /// Image name. Not guaranteed to be unique and can be empty. + std::string name; - // ==== Utility functions ==== - // Get image channel count as a number rather than an enum. - int get_num_channels() const - { - if (channel == image::ImageChannel::unknown) return -1; - return static_cast(channel); - } + /// Image data. + ImageBufferExperimental image; - // Returns element byte size. - int get_element_size() const - { - switch (precision) { - case image::ImagePrecision::int8: return sizeof(char); - case image::ImagePrecision::uint8: return sizeof(unsigned char); - case image::ImagePrecision::int32: return sizeof(int); - case image::ImagePrecision::uint32: return sizeof(unsigned int); - case image::ImagePrecision::float16: return sizeof(short); - case image::ImagePrecision::float32: return sizeof(float); - case image::ImagePrecision::float64: return sizeof(double); - default: return -1; - } - } + /// Image file path. This path is relative to the file that contains the scene. + /// It is only valid if image data should be mapped to an external file. + fs::path uri; + /// Image extensions. Extensions extensions; }; + // Pair of texture index (which texture to use) and texture coordinate index (which set of UVs to // use). -struct TextureInfo +struct LA_SCENE_API TextureInfo { // Texture index. Index in scene.textures vector. - ElementId index = lagrange::invalid(); + ElementId index = invalid_element; // Index of UV coordinates. Usually stored in the mesh as `texcoord_x` attribute where x is this // variable. This is typically 0. @@ -149,7 +140,7 @@ struct TextureInfo // PBR material, based on the gltf specification. // This is subject to change, to support more material models. -struct MaterialExperimental +struct LA_SCENE_API MaterialExperimental { // Note that material name may not be unique, and can be empty. std::string name; @@ -190,7 +181,7 @@ struct MaterialExperimental Extensions extensions; }; -struct Texture +struct LA_SCENE_API Texture { std::string name; ElementId image = invalid(); // index of image in scene.images vector @@ -230,7 +221,7 @@ struct Texture Extensions extensions; }; -struct Light +struct LA_SCENE_API Light { std::string name; @@ -276,7 +267,7 @@ struct Light Extensions extensions; }; -struct Camera +struct LA_SCENE_API Camera { // note that the camera is part of the scene graph, and has an associated transform in its node. // The values below (position, up, look_at) are relative to the coordinate system defined by the @@ -334,7 +325,7 @@ struct Camera Extensions extensions; }; -struct Animation +struct LA_SCENE_API Animation { std::string name; // TODO @@ -342,7 +333,7 @@ struct Animation Extensions extensions; }; -struct Skeleton +struct LA_SCENE_API Skeleton { // This skeleton is used to deform those meshes. // This will typically contain one value, but can have zero or multiple meshes. @@ -372,7 +363,7 @@ struct Scene std::vector meshes; // Images. - std::vector images; + std::vector images; // Textures. They can reference images; std::vector textures; @@ -394,12 +385,69 @@ struct Scene // Extensions. Extensions extensions; + +public: + /// + /// Add an element to the scene. + /// + /// @param value The element to add. It can be node, mesh, image, texture, material, + /// light, camera, skeleton or animation. + /// + /// @return The element id of the added element. + /// + template + ElementId add(T&& value); + + /// + /// Add a child node to a given parent node. The parent-child relationship will be updated for + /// both nodes. + /// + /// @param parent_id The parent node id. + /// @param child_id The child node id. + /// + void add_child(ElementId parent_id, ElementId child_id); }; +template +template +ElementId Scene::add(T&& value) +{ + using ElementType = std::decay_t; + if constexpr (std::is_same_v) { + nodes.emplace_back(std::forward(value)); + return nodes.size() - 1; + } else if constexpr (std::is_same_v) { + meshes.emplace_back(std::forward(value)); + return meshes.size() - 1; + } else if constexpr (std::is_same_v) { + images.emplace_back(std::forward(value)); + return images.size() - 1; + } else if constexpr (std::is_same_v) { + textures.emplace_back(std::forward(value)); + return textures.size() - 1; + } else if constexpr (std::is_same_v) { + materials.emplace_back(std::forward(value)); + return materials.size() - 1; + } else if constexpr (std::is_same_v) { + lights.emplace_back(std::forward(value)); + return lights.size() - 1; + } else if constexpr (std::is_same_v) { + cameras.emplace_back(std::forward(value)); + return cameras.size() - 1; + } else if constexpr (std::is_same_v) { + skeletons.emplace_back(std::forward(value)); + return skeletons.size() - 1; + } else if constexpr (std::is_same_v) { + animations.emplace_back(std::forward(value)); + return animations.size() - 1; + } else { + static_assert(StaticAssertableBool::False, "Unsupported type"); + } +} + using Scene32f = Scene; using Scene32d = Scene; using Scene64f = Scene; using Scene64d = Scene; -} // namespace scene -} // namespace lagrange +} // namespace lagrange::scene diff --git a/modules/scene/include/lagrange/scene/SceneExtension.h b/modules/scene/include/lagrange/scene/SceneExtension.h index fecdec8c..bd7d1ab3 100644 --- a/modules/scene/include/lagrange/scene/SceneExtension.h +++ b/modules/scene/include/lagrange/scene/SceneExtension.h @@ -11,15 +11,17 @@ */ #pragma once +#include #include +#include #include #include -#include + #include +#include #include -#include #include -#include +#include #include #include @@ -27,7 +29,7 @@ namespace lagrange { namespace scene { // Json-like value used in scene extensions -class Value +class LA_SCENE_API Value { public: typedef std::vector Array; @@ -36,8 +38,8 @@ class Value using variant_type = std::variant; /** - * Checks if the type is in the variant. - */ + * Checks if the type is in the variant. + */ template static constexpr bool is_variant_type() { @@ -45,10 +47,11 @@ class Value } /** - * Returns index of the element type in the variant. Returns variant size if not found. - */ + * Returns index of the element type in the variant. Returns variant size if not found. + */ template - static constexpr std::size_t variant_index() { + static constexpr std::size_t variant_index() + { if constexpr (index == std::variant_size_v) { return index; } else if constexpr (std::is_same_v, T>) { @@ -70,7 +73,7 @@ class Value static Value create_array() { return Value(Array()); } static Value create_object() { return Value(Object()); } - Value(); + Value() = default; explicit Value(bool b) { value = b; } explicit Value(int i) { value = i; } explicit Value(double n) { value = n; } @@ -175,7 +178,7 @@ class Value variant_type value; }; -struct UserDataConverter +struct LA_SCENE_API UserDataConverter { virtual bool is_supported(const std::string& key) const = 0; virtual bool can_read(const std::string& key) const { return is_supported(key); } @@ -185,17 +188,17 @@ struct UserDataConverter virtual Value write(const std::any& value) const = 0; }; -struct Extensions +struct LA_SCENE_API Extensions { /** - * A map of extensions as json-like Value objects. - */ + * A map of extensions as json-like Value objects. + */ std::unordered_map data; /** - * A map of extensions as user-defined objects, stored in an std::any. - * Those are converted from/to the default Value with a UserDataConverter during I/O. - */ + * A map of extensions as user-defined objects, stored in an std::any. + * Those are converted from/to the default Value with a UserDataConverter during I/O. + */ std::unordered_map user_data; size_t size() const { return data.size() + user_data.size(); } diff --git a/modules/scene/include/lagrange/scene/SimpleScene.h b/modules/scene/include/lagrange/scene/SimpleScene.h index 3e89212c..af44e2dd 100644 --- a/modules/scene/include/lagrange/scene/SimpleScene.h +++ b/modules/scene/include/lagrange/scene/SimpleScene.h @@ -12,8 +12,10 @@ #pragma once #include +#include #include + #include #include @@ -30,7 +32,7 @@ namespace lagrange::scene { /// stack-allocated matrix. /// template -struct MeshInstance +struct LA_SCENE_API MeshInstance { /// Affine transformation matrix. using AffineTransform = Eigen::Transform(Dimension), Eigen::Affine>; @@ -56,7 +58,7 @@ struct MeshInstance /// @tparam Dimension Mesh dimension. /// template -class SimpleScene +class LA_SCENE_API SimpleScene { public: /// Mesh type. diff --git a/modules/scene/include/lagrange/scene/SimpleSceneTypes.h b/modules/scene/include/lagrange/scene/SimpleSceneTypes.h index 9cdbecff..5e63ca10 100644 --- a/modules/scene/include/lagrange/scene/SimpleSceneTypes.h +++ b/modules/scene/include/lagrange/scene/SimpleSceneTypes.h @@ -11,6 +11,10 @@ */ #pragma once +#include + +#include + // clang-format off /// diff --git a/modules/scene/include/lagrange/scene/api.h b/modules/scene/include/lagrange/scene/api.h new file mode 100644 index 00000000..d89b4964 --- /dev/null +++ b/modules/scene/include/lagrange/scene/api.h @@ -0,0 +1,23 @@ +#pragma once + +#ifdef LA_SCENE_STATIC_DEFINE + #define LA_SCENE_API +#else + #ifndef LA_SCENE_API + #ifdef lagrange_scene_EXPORTS + // We are building this library + #if defined(_WIN32) || defined(_WIN64) + #define LA_SCENE_API __declspec(dllexport) + #else + #define LA_SCENE_API __attribute__((visibility("default"))) + #endif + #else + // We are using this library + #if defined(_WIN32) || defined(_WIN64) + #define LA_SCENE_API __declspec(dllimport) + #else + #define LA_SCENE_API __attribute__((visibility("default"))) + #endif + #endif + #endif +#endif diff --git a/modules/scene/include/lagrange/scene/scene_utils.h b/modules/scene/include/lagrange/scene/scene_utils.h index a7164fe9..7bff9321 100644 --- a/modules/scene/include/lagrange/scene/scene_utils.h +++ b/modules/scene/include/lagrange/scene/scene_utils.h @@ -11,20 +11,23 @@ */ #pragma once -#include #include +#include namespace lagrange::scene::utils { // add child to the node. Assumes the child was already added to the scene. -inline void add_child(Node& node, ElementId child_index) +[[deprecated("Use Scene::add_child instead")]] inline void add_child( + Node& node, + ElementId child_index) { node.children.push_back(child_index); } // add child to the node and the scene. Assumes the child was not already added to the scene. template -ElementId add_child(Scene& scene, Node& node, Node child) +[[deprecated("Use Scene::add_child instead")]] ElementId +add_child(Scene& scene, Node& node, Node child) { ElementId child_idx = scene.nodes.size(); scene.nodes.push_back(child); @@ -34,7 +37,9 @@ ElementId add_child(Scene& scene, Node& node, Node child) // add mesh to the scene (but not to the node graph!) and return its index. template -ElementId add_mesh(Scene& scene, SurfaceMesh mesh) +[[deprecated("Use Scene::add(mesh) instead")]] ElementId add_mesh( + Scene& scene, + SurfaceMesh mesh) { scene.meshes.emplace_back(std::move(mesh)); return scene.meshes.size() - 1; diff --git a/modules/scene/python/src/bind_scene.h b/modules/scene/python/src/bind_scene.h index 9d7bd40f..e52bb68a 100644 --- a/modules/scene/python/src/bind_scene.h +++ b/modules/scene/python/src/bind_scene.h @@ -11,11 +11,15 @@ */ #pragma once +#include #include +#include #include #include #include +#include "bind_value.h" + // clang-format off #include #include @@ -25,52 +29,27 @@ #include #include #include +#include #include #include -#include // for ImageLegacy::data #include #include // clang-format on + namespace lagrange::python { namespace nb = nanobind; -namespace detail { -std::array, 4> affine3d_to_array(const Eigen::Affine3f& t) -{ - std::array, 4> data; - for (int i = 0; i < 4; ++i) { - for (int j = 0; j < 4; ++j) { - data[i][j] = t(i, j); - } - } - return data; -}; -Eigen::Affine3f array_to_affine3d(const std::array, 4> data) -{ - Eigen::Affine3f t; - for (int i = 0; i < 4; ++i) { - for (int j = 0; j < 4; ++j) { - t(i, j) = data[i][j]; - } - } - return t; -}; -} // namespace detail - -class ValuePublicist : public lagrange::scene::Value -{ -public: - using lagrange::scene::Value::value; -}; - #pragma GCC visibility push(hidden) struct UserDataConverterTrampoline : public lagrange::scene::UserDataConverter { NB_TRAMPOLINE(lagrange::scene::UserDataConverter, 5); - bool is_supported(const std::string& key) const override { NB_OVERRIDE_PURE(is_supported, key); } + bool is_supported(const std::string& key) const override + { + NB_OVERRIDE_PURE(is_supported, key); + } bool can_read(const std::string& key) const override { NB_OVERRIDE(can_read, key); } bool can_write(const std::string& key) const override { NB_OVERRIDE(can_write, key); } std::any read(const lagrange::scene::Value& value) const override { NB_OVERRIDE(read, value); } @@ -92,7 +71,7 @@ void bind_scene(nb::module_& m) nb::bind_vector>(m, "ElementIdList"); nb::bind_vector>(m, "SceneMeshInstanceList"); nb::bind_vector>>(m, "SurfaceMeshList"); - nb::bind_vector>(m, "ImageLegacyList"); + nb::bind_vector>(m, "ImageList"); nb::bind_vector>(m, "TextureList"); nb::bind_vector>(m, "MaterialList"); nb::bind_vector>(m, "LightList"); @@ -112,21 +91,21 @@ void bind_scene(nb::module_& m) .def_prop_ro("size", &Extensions::size) .def_prop_ro("empty", &Extensions::empty) .def_rw("data", &Extensions::data); - // .def_prop_rw( - // "user_data", - // [](const Extensions& ext) -> std::unordered_map { - // std::unordered_map ret; - // for (const auto& [key, val] : ext.user_data) { - // ret.insert({key, std::any_cast(val)}); - // } - // return ret; - // }, - // [](Extensions& ext, std::unordered_map data) -> void { - // ext.user_data.clear(); - // for (const auto& [key, val] : data) { - // ext.user_data.insert({key, val}); - // } - // }); + // .def_prop_rw( + // "user_data", + // [](const Extensions& ext) -> std::unordered_map { + // std::unordered_map ret; + // for (const auto& [key, val] : ext.user_data) { + // ret.insert({key, std::any_cast(val)}); + // } + // return ret; + // }, + // [](Extensions& ext, std::unordered_map data) -> void { + // ext.user_data.clear(); + // for (const auto& [key, val] : data) { + // ext.user_data.insert({key, val}); + // } + // }); // nb::class_(m, "UserExtension") // .def("is_supported", &UserDataConverter::is_supported) @@ -135,18 +114,6 @@ void bind_scene(nb::module_& m) // .def("read", &UserDataConverter::read) // .def("write", &UserDataConverter::write); - nb::class_(m, "Value") - .def(nb::init<>()) - .def(nb::init()) - .def(nb::init()) - .def(nb::init()) - .def(nb::init()) - .def(nb::init()) - .def(nb::init()) - .def(nb::init()) - .def(nb::init()) - .def_rw("value", &ValuePublicist::value); - nb::class_(m, "SceneMeshInstance", "Mesh and material index of a node") .def(nb::init<>()) .def_rw("mesh", &SceneMeshInstance::mesh) @@ -158,14 +125,23 @@ void bind_scene(nb::module_& m) .def_rw("name", &Node::name) .def_prop_rw( "transform", - // nanobind does not know the Eigen::Affine3f type. - // Eigen::Matrix4f compiles but returns bad data, so here's 2d arrays instead. - [](const Node& node) -> std::array, 4> { - return detail::affine3d_to_array(node.transform); + [](Node& node) { + return nb::ndarray>( + node.transform.data(), + {4, 4}, + nb::find(node), + {1, 4}); }, - [](Node& node, std::array, 4> t) -> void { - node.transform = detail::array_to_affine3d(t); - }) + [](Node& node, nb::ndarray> t) -> void { + auto view = t.view>(); + // Explicit 2D indexing because the input ndarray can be either row or column major. + for (size_t i = 0; i < 4; i++) { + for (size_t j = 0; j < 4; j++) { + node.transform.data()[i + j * 4] = view(i, j); + } + } + }, + "The affine transform associated with this node") .def_rw("parent", &Node::parent) .def_rw("children", &Node::children) .def_rw("meshes", &Node::meshes) @@ -173,33 +149,171 @@ void bind_scene(nb::module_& m) .def_rw("lights", &Node::lights) .def_rw("extensions", &Node::extensions); - nb::class_ image_legacy(m, "ImageLegacy"); - image_legacy.def(nb::init<>()) - .def( - "__repr__", - [](const ImageLegacy& image) { - return fmt::format( - "ImageLegacy('{}')", - image.name.empty() ? image.uri : image.name); - }) - .def_rw("name", &ImageLegacy::name) - .def_rw("width", &ImageLegacy::width) - .def_rw("height", &ImageLegacy::height) - .def_rw("precision", &ImageLegacy::precision) - .def_rw("channel", &ImageLegacy::channel) - .def_rw("data", &ImageLegacy::data) - .def_rw("uri", &ImageLegacy::uri) - .def_rw("type", &ImageLegacy::type) - .def_prop_ro("num_channels", &ImageLegacy::get_num_channels) - .def_prop_ro("element_size", &ImageLegacy::get_element_size) - .def_rw("extensions", &ImageLegacy::extensions); - - nb::enum_(image_legacy, "Type") - .value("Jpeg", ImageLegacy::Type::Jpeg) - .value("Png", ImageLegacy::Type::Png) - .value("Bmp", ImageLegacy::Type::Bmp) - .value("Gif", ImageLegacy::Type::Gif) - .value("Unknown", ImageLegacy::Type::Unknown); + nb::class_ image_buffer(m, "ImageBuffer"); + image_buffer.def(nb::init<>()) + .def_ro("width", &ImageBufferExperimental::width, "Image width") + .def_ro("height", &ImageBufferExperimental::height, "Image height") + .def_ro( + "num_channels", + &ImageBufferExperimental::num_channels, + "Number of channels in each pixel") + .def_prop_rw( + "data", + [](ImageBufferExperimental& self) { + size_t shape[3] = {self.height, self.width, self.num_channels}; + switch (self.element_type) { + case AttributeValueType::e_int8_t: + return nb::cast( + nb::ndarray( + reinterpret_cast(self.data.data()), + 3, + shape, + nb::find(self)), + nb::rv_policy::reference_internal); + case AttributeValueType::e_uint8_t: + return nb::cast( + nb::ndarray( + reinterpret_cast(self.data.data()), + 3, + shape, + nb::find(self)), + nb::rv_policy::reference_internal); + case AttributeValueType::e_int16_t: + return nb::cast( + nb::ndarray( + reinterpret_cast(self.data.data()), + 3, + shape, + nb::find(self)), + nb::rv_policy::reference_internal); + case AttributeValueType::e_uint16_t: + return nb::cast( + nb::ndarray( + reinterpret_cast(self.data.data()), + 3, + shape, + nb::find(self)), + nb::rv_policy::reference_internal); + case AttributeValueType::e_int32_t: + return nb::cast( + nb::ndarray( + reinterpret_cast(self.data.data()), + 3, + shape, + nb::find(self)), + nb::rv_policy::reference_internal); + case AttributeValueType::e_uint32_t: + return nb::cast( + nb::ndarray( + reinterpret_cast(self.data.data()), + 3, + shape, + nb::find(self)), + nb::rv_policy::reference_internal); + case AttributeValueType::e_int64_t: + return nb::cast( + nb::ndarray( + reinterpret_cast(self.data.data()), + 3, + shape, + nb::find(self)), + nb::rv_policy::reference_internal); + case AttributeValueType::e_uint64_t: + return nb::cast( + nb::ndarray( + reinterpret_cast(self.data.data()), + 3, + shape, + nb::find(self)), + nb::rv_policy::reference_internal); + case AttributeValueType::e_float: + return nb::cast( + nb::ndarray( + reinterpret_cast(self.data.data()), + 3, + shape, + nb::find(self)), + nb::rv_policy::reference_internal); + case AttributeValueType::e_double: + return nb::cast( + nb::ndarray( + reinterpret_cast(self.data.data()), + 3, + shape, + nb::find(self)), + nb::rv_policy::reference_internal); + default: throw nb::type_error("Unsupported image buffer `dtype`!"); + } + }, + [](ImageBufferExperimental& self, + nb::ndarray tensor) { + la_runtime_assert(tensor.ndim() == 3); + self.width = tensor.shape(1); + self.height = tensor.shape(0); + self.num_channels = tensor.shape(2); + auto dtype = tensor.dtype(); + if (dtype == nb::dtype()) { + self.element_type = AttributeValueType::e_int8_t; + } else if (dtype == nb::dtype()) { + self.element_type = AttributeValueType::e_uint8_t; + } else if (dtype == nb::dtype()) { + self.element_type = AttributeValueType::e_int16_t; + } else if (dtype == nb::dtype()) { + self.element_type = AttributeValueType::e_uint16_t; + } else if (dtype == nb::dtype()) { + self.element_type = AttributeValueType::e_int32_t; + } else if (dtype == nb::dtype()) { + self.element_type = AttributeValueType::e_uint32_t; + } else if (dtype == nb::dtype()) { + self.element_type = AttributeValueType::e_int64_t; + } else if (dtype == nb::dtype()) { + self.element_type = AttributeValueType::e_uint64_t; + } else if (dtype == nb::dtype()) { + self.element_type = AttributeValueType::e_float; + } else if (dtype == nb::dtype()) { + self.element_type = AttributeValueType::e_double; + } else { + throw nb::type_error("Unsupported input tensor `dtype`!"); + } + self.data.resize(tensor.nbytes()); + std::copy( + reinterpret_cast(tensor.data()), + reinterpret_cast(tensor.data()) + tensor.nbytes(), + self.data.data()); + }, + "Raw image data.") + .def_prop_ro( + "dtype", + [](ImageBufferExperimental& self) -> std::optional { + auto np = nb::module_::import_("numpy"); + switch (self.element_type) { + case AttributeValueType::e_int8_t: return np.attr("int8"); + case AttributeValueType::e_int16_t: return np.attr("int16"); + case AttributeValueType::e_int32_t: return np.attr("int32"); + case AttributeValueType::e_int64_t: return np.attr("int64"); + case AttributeValueType::e_uint8_t: return np.attr("uint8"); + case AttributeValueType::e_uint16_t: return np.attr("uint16"); + case AttributeValueType::e_uint32_t: return np.attr("uint32"); + case AttributeValueType::e_uint64_t: return np.attr("uint64"); + case AttributeValueType::e_float: return np.attr("float32"); + case AttributeValueType::e_double: return np.attr("float64"); + default: logger().warn("Image buffer has an unknown dtype."); return std::nullopt; + } + }, + "The element data type of the image buffer."); + + nb::class_ image(m, "Image"); + image.def(nb::init<>()) + .def_rw("name", &ImageExperimental::name, "Name of the image object") + .def_rw("image", &ImageExperimental::image, "Image buffer") + .def_rw("uri", &ImageExperimental::uri, "URI of the image file") + .def_rw( + "extensions", + &ImageExperimental::extensions, + "Additional data associated with the image") + .def("__repr__", [](const ImageExperimental& self) { + return fmt::format("Image ('{}')", self.name); + }); nb::class_(m, "TextureInfo") .def(nb::init<>()) @@ -334,6 +448,7 @@ void bind_scene(nb::module_& m) .def("__repr__", [](const SceneType& s) { return fmt::format("Scene('{}')", s.name); }) .def_rw("name", &SceneType::name) .def_rw("nodes", &SceneType::nodes) + .def_rw("root_nodes", &SceneType::root_nodes) .def_rw("meshes", &SceneType::meshes) .def_rw("images", &SceneType::images) .def_rw("textures", &SceneType::textures) @@ -342,16 +457,65 @@ void bind_scene(nb::module_& m) .def_rw("cameras", &SceneType::cameras) .def_rw("skeletons", &SceneType::skeletons) .def_rw("animations", &SceneType::animations) - .def_rw("extensions", &SceneType::extensions); - - m.def("add_child", &utils::add_child); + .def_rw("extensions", &SceneType::extensions) + .def( + "add", + [](SceneType& self, + std::variant< + Node, + SceneType::MeshType, + ImageExperimental, + Texture, + MaterialExperimental, + Light, + Camera, + Skeleton, + Animation> element) { + return std::visit( + [&](auto&& value) { + using T = std::decay_t; + return self.add(std::forward(value)); + }, + element); + }, + "element"_a, + R"(Add an element to the scene. - m.def("add_mesh", &utils::add_mesh); +:param element: The element to add to the scene. E.g. node, mesh, image, texture, material, light, camera, skeleton, or animation. - m.def("compute_global_node_transform", [](const SceneType& scene, size_t node_idx) { - return detail::affine3d_to_array( - utils::compute_global_node_transform(scene, node_idx)); - }); +:returns: The id of the added element.)") + .def( + "add_child", + &SceneType::add_child, + "parent_id"_a, + "child_id"_a, + R"(Add a child node to a parent node. + +:param parent_id: The parent node id. +:param child_id: The child node id. + +:returns: The id of the added child node.)"); + + m.def( + "compute_global_node_transform", + [](const SceneType& scene, size_t node_idx) { + auto t = utils::compute_global_node_transform(scene, node_idx); + return nb::ndarray>( + t.data(), + {4, 4}, + nb::handle(), // owner + {1, 4}); + }, + nb::rv_policy::copy, + "scene"_a, + "node_idx"_a, + R"(Compute the global transform associated with a node. + +:param scene: The input node. +:param node_idx: The index of the taget node. + +:returns: The global transform of the target node, which is the combination of transforms from this node all the way to the root. + )"); } } // namespace lagrange::python diff --git a/modules/scene/python/src/bind_value.h b/modules/scene/python/src/bind_value.h new file mode 100644 index 00000000..84c01c4d --- /dev/null +++ b/modules/scene/python/src/bind_value.h @@ -0,0 +1,159 @@ +/* + * Copyright 2024 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. + */ +#pragma once + +#include +#include + +// clang-format off +#include +#include +#include +#include +#include +// clang-format on + +NAMESPACE_BEGIN(NB_NAMESPACE) +NAMESPACE_BEGIN(detail) + +template <> +struct type_caster +{ + NB_TYPE_CASTER(lagrange::scene::Value, const_name("Value")) + + template + bool try_cast(const handle& src, uint8_t flags, cleanup_list* cleanup) + { + using CasterT = make_caster; + + flags |= (uint8_t)cast_flags::none_disallowed; + CasterT caster; + if (!caster.from_python(src, flags, cleanup)) return false; + value.set(caster.operator cast_t()); + return true; + } + + bool from_python(handle src, uint8_t flags, cleanup_list* cleanup) noexcept + { + if (PyNumber_Check(src.ptr())) { + lagrange::logger().debug("Number!"); + return try_cast(src, flags, cleanup) || try_cast(src, flags, cleanup); + } else if (try_cast(src, flags, cleanup)) { + lagrange::logger().debug("String!"); + return true; + } else if (PySequence_Check(src.ptr())) { + lagrange::logger().debug("Array of size {}!", PySequence_Size(src.ptr())); + + size_t n; + PyObject* temp; + /* Will initialize 'temp' (NULL in the case of a failure.) */ + PyObject** o = seq_get(src.ptr(), &n, &temp); + + bool success = o != nullptr; + + if (success) { + lagrange::scene::Value::Array arr; + arr.reserve(n); + make_caster caster; + + for (size_t i = 0; i < n; ++i) { + if (!caster.from_python(o[i], flags, cleanup)) { + success = false; + break; + } + + arr.push_back(caster.operator cast_t()); + } + value.set(arr); + } + + Py_XDECREF(temp); + return success; + } else if (PyMapping_Check(src.ptr())) { + lagrange::logger().debug("Dict!"); + + PyObject* items = PyMapping_Items(src.ptr()); + if (items == nullptr) { + PyErr_Clear(); + return false; + } + + Py_ssize_t size = NB_LIST_GET_SIZE(items); + bool success = size >= 0; + + make_caster key_caster; + make_caster val_caster; + lagrange::scene::Value::Object obj; + + for (Py_ssize_t i = 0; i < size; ++i) { + PyObject* item = NB_LIST_GET_ITEM(items, i); + PyObject* key = NB_TUPLE_GET_ITEM(item, 0); + PyObject* val = NB_TUPLE_GET_ITEM(item, 1); + + if (!key_caster.from_python(key, flags, cleanup)) { + success = false; + break; + } + + if (!val_caster.from_python(val, flags, cleanup)) { + success = false; + break; + } + + obj.emplace( + key_caster.operator cast_t(), + val_caster.operator cast_t()); + } + value.set(obj); + + Py_DECREF(items); + + return success; + } + + return false; + } + + static handle + from_cpp(const lagrange::scene::Value& value, rv_policy policy, cleanup_list* cleanup) noexcept + { + if (value.is_bool()) { + return make_caster::from_cpp(value.get_bool(), policy, cleanup); + } else if (value.is_int()) { + return make_caster::from_cpp(value.get_int(), policy, cleanup); + } else if (value.is_real()) { + return make_caster::from_cpp(value.get_real(), policy, cleanup); + } else if (value.is_string()) { + return make_caster::from_cpp(value.get_string(), policy, cleanup); + } else if (value.is_buffer()) { + return make_caster::from_cpp( + value.get_buffer(), + policy, + cleanup); + } else if (value.is_array()) { + return make_caster::from_cpp( + value.get_array(), + policy, + cleanup); + } else if (value.is_object()) { + return make_caster::from_cpp( + value.get_object(), + policy, + cleanup); + } + return none().release(); + } +}; + +NAMESPACE_END(detail) +NAMESPACE_END(NB_NAMESPACE) + diff --git a/modules/scene/python/tests/test_scene.py b/modules/scene/python/tests/test_scene.py new file mode 100644 index 00000000..9925efeb --- /dev/null +++ b/modules/scene/python/tests/test_scene.py @@ -0,0 +1,67 @@ +# +# Copyright 2024 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. +# +import lagrange + +import pytest +import numpy as np + + +class TestScene: + def test_empty_scene(self): + scene = lagrange.scene.Scene() + assert len(scene.nodes) == 0 + assert len(scene.root_nodes) == 0 + assert len(scene.meshes) == 0 + assert len(scene.images) == 0 + assert len(scene.textures) == 0 + assert len(scene.materials) == 0 + assert len(scene.lights) == 0 + assert len(scene.cameras) == 0 + assert len(scene.skeletons) == 0 + assert len(scene.animations) == 0 + + def test_node(self): + node = lagrange.scene.Node() + assert len(node.children) == 0 + assert len(node.meshes) == 0 + assert len(node.cameras) == 0 + assert len(node.lights) == 0 + assert np.all(node.transform == np.eye(4)) + assert node.transform.flags["F_CONTIGUOUS"] + assert not node.transform.flags["OWNDATA"] + + node.transform = np.arange(16).reshape((4, 4)) + assert np.all(node.transform.ravel() == np.arange(16)) + + def test_scene_construction(self): + mesh = lagrange.SurfaceMesh() + scene = lagrange.scene.Scene() + assert len(scene.meshes) == 0 + + # Add mesh to scene + mesh_id = scene.add(mesh) + assert mesh_id == 0 + assert len(scene.meshes) == 1 + + # Create mesh instance + instance = lagrange.scene.SceneMeshInstance() + instance.mesh = mesh_id + + # Create node + node = lagrange.scene.Node() + node.name = "test node" + node.meshes.append(instance) + assert len(node.meshes) == 1 + + # Add node to the scene + node_id = scene.add(node) + assert len(scene.nodes) == 1 diff --git a/modules/scene/src/Scene.cpp b/modules/scene/src/Scene.cpp index 14de3497..ca8bc029 100644 --- a/modules/scene/src/Scene.cpp +++ b/modules/scene/src/Scene.cpp @@ -9,15 +9,42 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ +#include #include namespace lagrange { namespace scene { -template struct Scene; -template struct Scene; -template struct Scene; -template struct Scene; +template +void Scene::add_child(ElementId parent_id, ElementId child_id) +{ + Node& parent = nodes[parent_id]; + Node& child = nodes[child_id]; + parent.children.push_back(child_id); + child.parent = parent_id; +} + +template struct LA_SCENE_API Scene; +template struct LA_SCENE_API Scene; +template struct LA_SCENE_API Scene; +template struct LA_SCENE_API Scene; + +size_t ImageBufferExperimental::get_bits_per_element() const +{ + switch (element_type) { + case AttributeValueType::e_uint8_t: return 8; + case AttributeValueType::e_int8_t: return 8; + case AttributeValueType::e_uint16_t: return 16; + case AttributeValueType::e_int16_t: return 16; + case AttributeValueType::e_uint32_t: return 32; + case AttributeValueType::e_int32_t: return 32; + case AttributeValueType::e_uint64_t: return 64; + case AttributeValueType::e_int64_t: return 64; + case AttributeValueType::e_float: return sizeof(float); + case AttributeValueType::e_double: return sizeof(double); + default: return 0; + } +} } // namespace scene } // namespace lagrange diff --git a/modules/scene/src/SimpleScene.cpp b/modules/scene/src/SimpleScene.cpp index 801d647f..8f7acbe2 100644 --- a/modules/scene/src/SimpleScene.cpp +++ b/modules/scene/src/SimpleScene.cpp @@ -76,7 +76,7 @@ void SimpleScene::foreach_instances( } } -#define LA_X_simple_scene(_, Scalar, Index, Dim) template class SimpleScene; +#define LA_X_simple_scene(_, Scalar, Index, Dim) template class LA_SCENE_API SimpleScene; LA_SIMPLE_SCENE_X(simple_scene, 0) } // namespace lagrange::scene diff --git a/modules/scene/src/cast.cpp b/modules/scene/src/cast.cpp index 7fde03c9..1795b3c1 100644 --- a/modules/scene/src/cast.cpp +++ b/modules/scene/src/cast.cpp @@ -29,7 +29,7 @@ SimpleScene cast( { using TargetSceneType = scene::SimpleScene; TargetSceneType target_scene; - target_scene.reserve_meshes(source_scene.get_num_meshes()); + target_scene.reserve_meshes(static_cast(source_scene.get_num_meshes())); std::vector tmp_attribute_names; std::vector* tmp_attribute_names_ptr = nullptr; @@ -44,7 +44,7 @@ SimpleScene cast( convertible_attributes, tmp_attribute_names_ptr); target_scene.add_mesh(std::move(converted_mesh)); - target_scene.reserve_instances(mesh_index, source_scene.get_num_instances(mesh_index)); + target_scene.reserve_instances(static_cast(mesh_index), static_cast(source_scene.get_num_instances(mesh_index))); if (converted_attributes_names && first) { *converted_attributes_names = tmp_attribute_names; first = false; @@ -55,7 +55,7 @@ SimpleScene cast( } source_scene.foreach_instances([&](auto& instance) { typename TargetSceneType::InstanceType converted_instance; - converted_instance.mesh_index = instance.mesh_index; + converted_instance.mesh_index = static_cast(instance.mesh_index); converted_instance.transform = typename TargetSceneType::AffineTransform(instance.transform); converted_instance.user_data = std::move(instance.user_data); @@ -75,11 +75,11 @@ SimpleScene cast( #define fst(first, second) first #define snd(first, second) second #define LA_X_cast_mesh_to(FromScalarIndex, ToScalar, ToIndex) \ - template SimpleScene cast( \ + template LA_SCENE_API SimpleScene cast( \ const SimpleScene& source_mesh, \ const AttributeFilter& convertible_attributes, \ std::vector* converted_attributes_names); \ - template SimpleScene cast( \ + template LA_SCENE_API SimpleScene cast( \ const SimpleScene& source_mesh, \ const AttributeFilter& convertible_attributes, \ std::vector* converted_attributes_names); diff --git a/modules/scene/src/compute_mesh_weights.cpp b/modules/scene/src/compute_mesh_weights.cpp index c9bebf18..59668c63 100644 --- a/modules/scene/src/compute_mesh_weights.cpp +++ b/modules/scene/src/compute_mesh_weights.cpp @@ -95,7 +95,7 @@ std::vector compute_mesh_weights( } #define LA_X_compute_mesh_weights(_, Scalar, Index, Dim) \ - template std::vector compute_mesh_weights( \ + template LA_SCENE_API std::vector compute_mesh_weights( \ const SimpleScene& scene, \ const FacetAllocationStrategy facet_allocation_strategy); LA_SIMPLE_SCENE_X(compute_mesh_weights, 0) diff --git a/modules/scene/src/scene_utils.cpp b/modules/scene/src/scene_utils.cpp index 60565461..815eaa80 100644 --- a/modules/scene/src/scene_utils.cpp +++ b/modules/scene/src/scene_utils.cpp @@ -11,6 +11,7 @@ */ #include +#include #include #include #include @@ -46,8 +47,9 @@ void convert_texcoord_uv_st(SurfaceMesh& mesh, AttributeId attrib } }); } + #define LA_X_convert_texcoord_uv_st(_, S, I) \ - template void convert_texcoord_uv_st(SurfaceMesh& mesh, AttributeId attribute_id); + template LA_SCENE_API void convert_texcoord_uv_st(SurfaceMesh& mesh, AttributeId attribute_id); LA_SURFACE_MESH_X(convert_texcoord_uv_st, 0) #undef LA_X_convert_texcoord_uv_st diff --git a/modules/scene/src/simple_scene_convert.cpp b/modules/scene/src/simple_scene_convert.cpp index e2211b14..c82f9cdb 100644 --- a/modules/scene/src/simple_scene_convert.cpp +++ b/modules/scene/src/simple_scene_convert.cpp @@ -59,11 +59,11 @@ SurfaceMesh simple_scene_to_mesh( } #define LA_X_simple_scene_convert(_, Scalar, Index, Dimension) \ - template SimpleScene mesh_to_simple_scene( \ + template LA_SCENE_API SimpleScene mesh_to_simple_scene( \ SurfaceMesh mesh); \ - template SimpleScene meshes_to_simple_scene( \ + template LA_SCENE_API SimpleScene meshes_to_simple_scene( \ std::vector> meshes); \ - template SurfaceMesh simple_scene_to_mesh( \ + template LA_SCENE_API SurfaceMesh simple_scene_to_mesh( \ const SimpleScene& scene, \ const TransformOptions& transform_options, \ bool preserve_attributes); diff --git a/modules/subdivision/CMakeLists.txt b/modules/subdivision/CMakeLists.txt index bb450b6f..e377a4e0 100644 --- a/modules/subdivision/CMakeLists.txt +++ b/modules/subdivision/CMakeLists.txt @@ -24,13 +24,7 @@ target_link_libraries(lagrange_subdivision opensubdiv::opensubdiv ) -# 3. installation -if(LAGRANGE_INSTALL) - set_target_properties(lagrange_subdivision PROPERTIES EXPORT_NAME subdivision) - lagrange_install(lagrange_subdivision) -endif() - -# 4. unit tests and examples +# 3. unit tests and examples if(LAGRANGE_UNIT_TESTS) add_subdirectory(tests) endif() diff --git a/modules/subdivision/examples/mesh_subdivision.cpp b/modules/subdivision/examples/mesh_subdivision.cpp index afca8167..40ee3a9e 100644 --- a/modules/subdivision/examples/mesh_subdivision.cpp +++ b/modules/subdivision/examples/mesh_subdivision.cpp @@ -12,12 +12,16 @@ #include #include +#include +#include #include #include +#include #include #include #include #include +#include #include @@ -29,6 +33,7 @@ int main(int argc, char** argv) std::string output = "output.obj"; std::string scheme = "auto"; bool output_btn = false; + std::optional autodetect_normal_threshold; } args; lagrange::subdivision::SubdivisionOptions options; @@ -40,6 +45,10 @@ int main(int argc, char** argv) app.add_option("-s,--scheme", args.scheme, "Subdivision scheme") ->check(CLI::IsMember({"auto", "bilinear", "loop", "catmark", "sqrt", "midpoint"})); app.add_option("-n,--num-levels", options.num_levels, "Number of subdivision levels"); + app.add_option( + "-a,--autodetect-normal-threshold", + args.autodetect_normal_threshold, + "Normal angle threshold (in degree) for autodetecting sharp edges"); app.add_flag( "--limit", options.use_limit_surface, @@ -51,15 +60,25 @@ int main(int argc, char** argv) lagrange::logger().info("Loading input mesh: {}", args.input); auto mesh = lagrange::io::load_mesh(args.input); + // gltf meshes are unindexed, so vertices at UV seams are duplicated + remove_duplicate_vertices(mesh); lagrange::logger().info( "Input mesh has {} vertices and {} facets", mesh.get_num_vertices(), mesh.get_num_facets()); - if ((std::set{"auto", "bilinear", "loop", "catmark"}).count(args.scheme)) { - // gltf meshes are unindexed, so vertices at UV seams are duplicated - remove_duplicate_vertices(mesh); + if (args.autodetect_normal_threshold.has_value()) { + lagrange::logger().info( + "Autodetecting sharp edges with a threshold of {} degrees", + args.autodetect_normal_threshold.value()); + float feature_angle_threshold = args.autodetect_normal_threshold.value() * M_PI / 180.f; + auto normal_id = lagrange::compute_normal(mesh, feature_angle_threshold); + auto seam_id = lagrange::compute_seam_edges(mesh, normal_id); + auto sharpness_id = lagrange::cast_attribute(mesh, seam_id, "sharpness"); + options.edge_sharpness_attr = sharpness_id; + } + if ((std::set{"auto", "bilinear", "loop", "catmark"}).count(args.scheme)) { // Convert subdiv scheme to enum if (args.scheme == "loop") { options.scheme = lagrange::subdivision::SchemeType::Loop; diff --git a/modules/subdivision/include/lagrange/subdivision/api.h b/modules/subdivision/include/lagrange/subdivision/api.h new file mode 100644 index 00000000..ba8aeff5 --- /dev/null +++ b/modules/subdivision/include/lagrange/subdivision/api.h @@ -0,0 +1,23 @@ +#pragma once + +#ifdef LA_SUBDIVISION_STATIC_DEFINE + #define LA_SUBDIVISION_API +#else + #ifndef LA_SUBDIVISION_API + #ifdef lagrange_subdivision_EXPORTS + // We are building this library + #if defined(_WIN32) || defined(_WIN64) + #define LA_SUBDIVISION_API __declspec(dllexport) + #else + #define LA_SUBDIVISION_API __attribute__((visibility("default"))) + #endif + #else + // We are using this library + #if defined(_WIN32) || defined(_WIN64) + #define LA_SUBDIVISION_API __declspec(dllimport) + #else + #define LA_SUBDIVISION_API __attribute__((visibility("default"))) + #endif + #endif + #endif +#endif diff --git a/modules/subdivision/src/TopologyRefinerFactory.h b/modules/subdivision/src/TopologyRefinerFactory.h index ad984f44..0166b1bd 100644 --- a/modules/subdivision/src/TopologyRefinerFactory.h +++ b/modules/subdivision/src/TopologyRefinerFactory.h @@ -11,10 +11,9 @@ */ // No #pragma once at the top of this file, this is on purpose. -#include "visit_attribute.h" - #include #include +#include #include namespace OpenSubdiv { @@ -82,7 +81,7 @@ bool TopologyRefinerFactory::assignComponentTags( const auto& options = conv.options; if (options.edge_sharpness_attr.has_value()) { - lagrange::subdivision::visit_attribute( + lagrange::internal::visit_attribute( mesh, options.edge_sharpness_attr.value(), [&](auto&& attr) { @@ -98,6 +97,7 @@ bool TopologyRefinerFactory::assignComponentTags( if constexpr (AttributeType::IsIndexed) { la_runtime_assert("Edge sharpness cannot be an indexed attribute"); } else { + lagrange::logger().debug("Using edge sharpness attribute"); auto values = attr.get_all(); for (int e = 0; e < static_cast(values.size()); ++e) { auto v = mesh.get_edge_vertices(e); @@ -126,7 +126,7 @@ bool TopologyRefinerFactory::assignComponentTags( } if (options.vertex_sharpness_attr.has_value()) { - lagrange::subdivision::visit_attribute( + lagrange::internal::visit_attribute( mesh, options.vertex_sharpness_attr.value(), [&](auto&& attr) { @@ -142,6 +142,7 @@ bool TopologyRefinerFactory::assignComponentTags( if constexpr (AttributeType::IsIndexed) { la_runtime_assert("Vertex sharpness cannot be an indexed attribute"); } else { + lagrange::logger().debug("Using vertex sharpness attribute"); auto values = attr.get_all(); for (int v = 0; v < static_cast(values.size()); ++v) { float s = static_cast(ValueType(10.0) * values[v]); @@ -152,7 +153,7 @@ bool TopologyRefinerFactory::assignComponentTags( } if (options.face_hole_attr.has_value()) { - lagrange::subdivision::visit_attribute( + lagrange::internal::visit_attribute( mesh, options.face_hole_attr.value(), [&](auto&& attr) { @@ -167,6 +168,7 @@ bool TopologyRefinerFactory::assignComponentTags( if constexpr (AttributeType::IsIndexed) { la_runtime_assert("Face holes cannot be an indexed attribute"); } else { + lagrange::logger().debug("Using facet hole attribute"); auto values = attr.get_all(); for (int f = 0; f < static_cast(values.size()); ++f) { if (values[f] != ValueType(0)) { @@ -193,7 +195,7 @@ bool TopologyRefinerFactory::assignFaceVaryingTopology( // TODO: Only define one fvar channel for each different set of indices (factorize shared set of // indices)? for (lagrange::AttributeId attr_id : conv.face_varying_attributes) { - lagrange::subdivision::visit_attribute(mesh, attr_id, [&](auto&& attr) { + lagrange::internal::visit_attribute(mesh, attr_id, [&](auto&& attr) { using AttributeType = std::decay_t; if constexpr (!AttributeType::IsIndexed) { la_runtime_assert( diff --git a/modules/subdivision/src/mesh_subdivision.cpp b/modules/subdivision/src/mesh_subdivision.cpp index 578bdfcc..ba1b4f2d 100644 --- a/modules/subdivision/src/mesh_subdivision.cpp +++ b/modules/subdivision/src/mesh_subdivision.cpp @@ -11,15 +11,14 @@ */ #include -#include "MeshConverter.h" -#include "visit_attribute.h" - #include #include #include #include #include +#include #include +#include "MeshConverter.h" #include @@ -556,7 +555,7 @@ SurfaceMesh subdivide_mesh( // Interpolate per-vertex data (including vertex positions) auto interpolate_attribute = [&](AttributeId id, bool smooth) { - visit_attribute(input_mesh, id, [&](auto&& attr) { + lagrange::internal::visit_attribute(input_mesh, id, [&](auto&& attr) { using AttributeType = std::decay_t; using ValueType = typename AttributeType::ValueType; if constexpr (!(std::is_same_v || @@ -621,7 +620,7 @@ SurfaceMesh subdivide_mesh( // Interpolate face-varying data (such as UVs) int fvar_index = 0; for (auto id : interpolated_attr.face_varying_attributes) { - visit_attribute(input_mesh, id, [&](auto&& attr) { + lagrange::internal::visit_attribute(input_mesh, id, [&](auto&& attr) { using AttributeType = std::decay_t; using ValueType = typename AttributeType::ValueType; if constexpr (!(std::is_same_v || diff --git a/modules/testing/CMakeLists.txt b/modules/testing/CMakeLists.txt index 011d8080..00eb349d 100644 --- a/modules/testing/CMakeLists.txt +++ b/modules/testing/CMakeLists.txt @@ -11,7 +11,7 @@ # # 1. define module -lagrange_add_module() +lagrange_add_module(NO_INSTALL) set_target_properties(lagrange_testing PROPERTIES FOLDER "${LAGRANGE_IDE_PREFIX}Lagrange//Tests") # 2. dependencies @@ -107,6 +107,8 @@ target_link_libraries(lagrange_testing_main PUBLIC lagrange::core lagrange::test add_library(lagrange::testing::main ALIAS lagrange_testing_main) set_target_properties(lagrange_testing_main PROPERTIES FOLDER "${LAGRANGE_IDE_PREFIX}Lagrange//Tests") +set_target_properties(lagrange_testing_main PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON) + # Enable StackWalker on Windows if(WIN32) option(LAGRANGE_STACKWALKER "Enable StackWalker on Windows" ON) diff --git a/modules/testing/include/lagrange/testing/api.h b/modules/testing/include/lagrange/testing/api.h new file mode 100644 index 00000000..1bb917c0 --- /dev/null +++ b/modules/testing/include/lagrange/testing/api.h @@ -0,0 +1,23 @@ +#pragma once + +#ifdef LA_TESTING_STATIC_DEFINE + #define LA_TESTING_API +#else + #ifndef LA_TESTING_API + #ifdef lagrange_testing_EXPORTS + // We are building this library + #if defined(_WIN32) || defined(_WIN64) + #define LA_TESTING_API __declspec(dllexport) + #else + #define LA_TESTING_API __attribute__((visibility("default"))) + #endif + #else + // We are using this library + #if defined(_WIN32) || defined(_WIN64) + #define LA_TESTING_API __declspec(dllimport) + #else + #define LA_TESTING_API __attribute__((visibility("default"))) + #endif + #endif + #endif +#endif diff --git a/modules/testing/include/lagrange/testing/common.h b/modules/testing/include/lagrange/testing/common.h index 4105cf1f..5d4e9336 100644 --- a/modules/testing/include/lagrange/testing/common.h +++ b/modules/testing/include/lagrange/testing/common.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -89,7 +90,7 @@ namespace testing { /// /// @return Absolute path to the file. /// -fs::path get_data_path(const fs::path& relative_path); +LA_TESTING_API fs::path get_data_path(const fs::path& relative_path); /// /// Loads a mesh from the test data directory. @@ -107,8 +108,8 @@ std::unique_ptr load_mesh(const fs::path& relative_path) REQUIRE(result); return result; } -extern template std::unique_ptr load_mesh(const fs::path&); -extern template std::unique_ptr load_mesh(const fs::path&); +extern template LA_TESTING_API std::unique_ptr load_mesh(const fs::path&); +extern template LA_TESTING_API std::unique_ptr load_mesh(const fs::path&); /// /// Load a mesh from test data directory as a `SurfaceMesh`. @@ -139,7 +140,7 @@ SurfaceMesh load_surface_mesh(const fs::path& relative_path) /// [1]: /// https://software.intel.com/content/www/us/en/develop/articles/introduction-to-the-conditional-numerical-reproducibility-cnr.html /// -void setup_mkl_reproducibility(); +LA_TESTING_API void setup_mkl_reproducibility(); } // namespace testing } // namespace lagrange diff --git a/modules/testing/include/lagrange/testing/detect_fp_behavior.h b/modules/testing/include/lagrange/testing/detect_fp_behavior.h index b618407b..cbfc5d49 100644 --- a/modules/testing/include/lagrange/testing/detect_fp_behavior.h +++ b/modules/testing/include/lagrange/testing/detect_fp_behavior.h @@ -11,6 +11,8 @@ */ #pragma once +#include + namespace lagrange::testing { /// @@ -42,6 +44,6 @@ enum class FloatPointBehavior { /// /// @return Detected float point behavior. /// -FloatPointBehavior detect_fp_behavior(); +LA_TESTING_API FloatPointBehavior detect_fp_behavior(); } // namespace lagrange::testing diff --git a/modules/testing/src/create_test_mesh.cpp b/modules/testing/src/create_test_mesh.cpp index 23fe021c..d4cb5a9b 100644 --- a/modules/testing/src/create_test_mesh.cpp +++ b/modules/testing/src/create_test_mesh.cpp @@ -10,9 +10,10 @@ * governing permissions and limitations under the License. */ #include +#include #include +#include #include -#include namespace lagrange::testing { @@ -200,9 +201,9 @@ SurfaceMesh create_test_sphere(CreateOptions options) return sphere; } -#define LA_X_create_test_mesh(_, Scalar, Index) \ - template SurfaceMesh create_test_cube(CreateOptions); \ - template SurfaceMesh create_test_sphere(CreateOptions); +#define LA_X_create_test_mesh(_, Scalar, Index) \ + template LA_TESTING_API SurfaceMesh create_test_cube(CreateOptions); \ + template LA_TESTING_API SurfaceMesh create_test_sphere(CreateOptions); LA_SURFACE_MESH_X(create_test_mesh, 0) } // namespace lagrange::testing diff --git a/modules/ui/CMakeLists.txt b/modules/ui/CMakeLists.txt index 44299bc9..3310b77e 100644 --- a/modules/ui/CMakeLists.txt +++ b/modules/ui/CMakeLists.txt @@ -11,7 +11,7 @@ # # 1. define module -lagrange_add_module() +lagrange_add_module(NO_INSTALL) if(WIN32) target_compile_definitions(lagrange_ui PUBLIC -DNOMINMAX -D_SILENCE_CXX17_ADAPTOR_TYPEDEFS_DEPRECATION_WARNING) @@ -115,13 +115,7 @@ if(LAGRANGE_USE_PCH) target_link_libraries(lagrange_ui PRIVATE lagrange_ui_pch) endif() -# 4. installation -# if(LAGRANGE_INSTALL) -# set_target_properties(lagrange_ui PROPERTIES EXPORT_NAME ui) -# lagrange_install(lagrange_ui) -# endif() - -# 5. unit tests and examples +# 4. unit tests and examples if(LAGRANGE_EXAMPLES) add_subdirectory(examples) endif() diff --git a/modules/ui/include/lagrange/ui/Viewer.h b/modules/ui/include/lagrange/ui/Viewer.h index c9cc1a10..69f44ff7 100644 --- a/modules/ui/include/lagrange/ui/Viewer.h +++ b/modules/ui/include/lagrange/ui/Viewer.h @@ -13,6 +13,7 @@ #include +#include #include #include #include @@ -43,7 +44,7 @@ class Renderer; /// @brief Viewer /// use `systems()` to add functions that should be called every frame /// use `registry()` or util functions to read and manipulate application's state -class Viewer +class LA_UI_API Viewer { public: /* @@ -143,9 +144,9 @@ class Viewer virtual ~Viewer(); - operator ui::Registry &() { return registry(); } + operator ui::Registry&() { return registry(); } - operator const ui::Registry &() const { return registry(); } + operator const ui::Registry&() const { return registry(); } /// /// UI action wants the window to close diff --git a/modules/ui/include/lagrange/ui/api.h b/modules/ui/include/lagrange/ui/api.h new file mode 100644 index 00000000..129796c0 --- /dev/null +++ b/modules/ui/include/lagrange/ui/api.h @@ -0,0 +1,23 @@ +#pragma once + +#ifdef LA_UI_STATIC_DEFINE + #define LA_UI_API +#else + #ifndef LA_UI_API + #ifdef lagrange_ui_EXPORTS + // We are building this library + #if defined(_WIN32) || defined(_WIN64) + #define LA_UI_API __declspec(dllexport) + #else + #define LA_UI_API __attribute__((visibility("default"))) + #endif + #else + // We are using this library + #if defined(_WIN32) || defined(_WIN64) + #define LA_UI_API __declspec(dllimport) + #else + #define LA_UI_API __attribute__((visibility("default"))) + #endif + #endif + #endif +#endif diff --git a/modules/ui/include/lagrange/ui/default_entities.h b/modules/ui/include/lagrange/ui/default_entities.h index 851c9a00..88c51b97 100644 --- a/modules/ui/include/lagrange/ui/default_entities.h +++ b/modules/ui/include/lagrange/ui/default_entities.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include #include #include @@ -20,27 +21,27 @@ namespace lagrange { namespace ui { -void set_material(Registry& r, Entity meshrender_entity, std::shared_ptr mat); +LA_UI_API void set_material(Registry& r, Entity meshrender_entity, std::shared_ptr mat); -Entity show_mesh( +LA_UI_API Entity show_mesh( Registry& r, const Entity& mesh_entity, StringID shader = DefaultShaders::PBR, const ShaderDefines& shader_defines = {}); -Entity show_submesh( +LA_UI_API Entity show_submesh( Registry& r, const Entity& mesh_entity, std::shared_ptr material, entt::id_type submesh_id); -Entity show_mesh( +LA_UI_API Entity show_mesh( Registry& r, Entity mesh_entity, Entity scene_node_entity, StringID shader = DefaultShaders::PBR, const ShaderDefines& shader_defines = {}); -Entity show_mesh( +LA_UI_API Entity show_mesh( Registry& r, Entity mesh_entity, Entity scene_node_entity, @@ -50,53 +51,53 @@ Entity show_mesh( /* Attribute visualization */ -Entity show_vertex_attribute( +LA_UI_API Entity show_vertex_attribute( Registry& r, const Entity& mesh_entity, const std::string& attribute, Glyph glyph); -Entity show_facet_attribute( +LA_UI_API Entity show_facet_attribute( Registry& r, const Entity& mesh_entity, const std::string& attribute, Glyph glyph); -Entity show_edge_attribute( +LA_UI_API Entity show_edge_attribute( Registry& r, const Entity& mesh_entity, const std::string& attribute, Glyph glyph); -Entity show_corner_attribute( +LA_UI_API Entity show_corner_attribute( Registry& r, const Entity& mesh_entity, const std::string& attribute, Glyph glyph); -Entity show_indexed_attribute( +LA_UI_API Entity show_indexed_attribute( Registry& r, const Entity& mesh_entity, const std::string& attribute, Glyph glyph); -void set_colormap(Registry& r, Entity meshrender_entity, std::shared_ptr texture); +LA_UI_API void set_colormap(Registry& r, Entity meshrender_entity, std::shared_ptr texture); -void set_colormap_range( +LA_UI_API void set_colormap_range( Registry& r, Entity meshrender_entity, const Eigen::Vector4f& range_min, const Eigen::Vector4f& range_max); -void set_colormap_range( +LA_UI_API void set_colormap_range( Registry& r, Entity meshrender_entity, const std::pair& range); -std::shared_ptr get_material(Registry& r, Entity meshrender_entity); +LA_UI_API std::shared_ptr get_material(Registry& r, Entity meshrender_entity); inline Transform& get_transform(Registry& r, Entity e) { @@ -189,38 +190,38 @@ Entity load_mesh( /* * Mesh update */ -void set_mesh_vertices_dirty(Registry& r, Entity mesh_entity); -void set_mesh_normals_dirty(Registry& r, Entity mesh_entity); -void set_mesh_dirty(Registry& r, Entity mesh_entity); +LA_UI_API void set_mesh_vertices_dirty(Registry& r, Entity mesh_entity); +LA_UI_API void set_mesh_normals_dirty(Registry& r, Entity mesh_entity); +LA_UI_API void set_mesh_dirty(Registry& r, Entity mesh_entity); -void set_show_attribute_dirty(Registry& r, Entity scene_entity); -void set_mesh_attribute_dirty( +LA_UI_API void set_show_attribute_dirty(Registry& r, Entity scene_entity); +LA_UI_API void set_mesh_attribute_dirty( Registry& r, Entity mesh_entity, IndexingMode mode, const std::string& name); -Entity get_meshdata_entity(Registry& r, Entity scene_entity); -MeshData& get_meshdata(Registry& r, Entity scene_or_mesh_entity); +LA_UI_API Entity get_meshdata_entity(Registry& r, Entity scene_entity); +LA_UI_API MeshData& get_meshdata(Registry& r, Entity scene_or_mesh_entity); /* Material */ std::shared_ptr -create_material(Registry& r, entt::id_type shader_id, const ShaderDefines& shader_defines = {}); +LA_UI_API create_material(Registry& r, entt::id_type shader_id, const ShaderDefines& shader_defines = {}); -Entity add_camera(Registry& r, const Camera& camera = Camera::default_camera(1, 1)); +LA_UI_API Entity add_camera(Registry& r, const Camera& camera = Camera::default_camera(1, 1)); // Clears all user added entities -void clear_scene(Registry& r); +LA_UI_API void clear_scene(Registry& r); /// Intersect ray with meshes in root's hierarchy /// Returns a pair of intersected entity and the corresponding hit /// If root == ui::NullEntity, entire scene is traversed -std::optional> intersect_ray( +LA_UI_API std::optional> intersect_ray( Registry& r, const Eigen::Vector3f& origin, const Eigen::Vector3f& dir, diff --git a/modules/ui/include/lagrange/ui/default_ibls.h b/modules/ui/include/lagrange/ui/default_ibls.h index b1f0b203..dc19c4ed 100644 --- a/modules/ui/include/lagrange/ui/default_ibls.h +++ b/modules/ui/include/lagrange/ui/default_ibls.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include namespace lagrange { @@ -25,7 +26,7 @@ namespace ui { /// https://www.deviantart.com/zbyg/art/HDRi-Pack-1-97402522 /// /// CC 3.0 License -IBL generate_default_ibl(size_t resolution = 256); +LA_UI_API IBL generate_default_ibl(size_t resolution = 256); } // namespace ui } // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/default_keybinds.h b/modules/ui/include/lagrange/ui/default_keybinds.h index df5caca7..1917685e 100644 --- a/modules/ui/include/lagrange/ui/default_keybinds.h +++ b/modules/ui/include/lagrange/ui/default_keybinds.h @@ -10,6 +10,8 @@ * governing permissions and limitations under the License. */ #pragma once + +#include #include namespace lagrange { @@ -33,19 +35,19 @@ enum class DefaultCameraScheme { DIMENSION, MAYA, BLENDER, SUBSTANCE }; /// In case of MAYA and SUBSTANCE, they remap viewport.selection.select.erase to /// LEFT MOUSE + LEFT CTRL + LEFT ALT /// -void set_camera_scheme(Keybinds& keybinds, DefaultCameraScheme camera_scheme); +LA_UI_API void set_camera_scheme(Keybinds& keybinds, DefaultCameraScheme camera_scheme); /// /// Do the keybinds have one of the default camera schemes? /// -bool has_camera_scheme(const Keybinds& keybinds, DefaultCameraScheme camera_scheme); +LA_UI_API bool has_camera_scheme(const Keybinds& keybinds, DefaultCameraScheme camera_scheme); /// /// Initializes all default keybinds /// -Keybinds initialize_default_keybinds(); +LA_UI_API Keybinds initialize_default_keybinds(); } // namespace ui diff --git a/modules/ui/include/lagrange/ui/default_panels.h b/modules/ui/include/lagrange/ui/default_panels.h index 988d3202..8a7bab2f 100644 --- a/modules/ui/include/lagrange/ui/default_panels.h +++ b/modules/ui/include/lagrange/ui/default_panels.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include namespace lagrange { @@ -30,9 +31,9 @@ struct DefaultPanels }; -DefaultPanels add_default_panels(Registry& registry); -void reset_layout(Registry& registry); +LA_UI_API DefaultPanels add_default_panels(Registry& registry); +LA_UI_API void reset_layout(Registry& registry); } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/default_shaders.h b/modules/ui/include/lagrange/ui/default_shaders.h index ce03e93b..17229701 100644 --- a/modules/ui/include/lagrange/ui/default_shaders.h +++ b/modules/ui/include/lagrange/ui/default_shaders.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include #include @@ -64,7 +65,7 @@ struct MeshElementIDMaterial constexpr static const StringID ElementMode = "element_mode"_hs; }; -void register_default_shaders(Registry& r); +LA_UI_API void register_default_shaders(Registry& r); } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/default_tools.h b/modules/ui/include/lagrange/ui/default_tools.h index 7afdec40..bdb53c48 100644 --- a/modules/ui/include/lagrange/ui/default_tools.h +++ b/modules/ui/include/lagrange/ui/default_tools.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include namespace lagrange { @@ -57,8 +58,8 @@ struct DefaultTools }; -void register_default_tools(Tools& tools); -void select(Registry& r, const std::function& selection_system); +LA_UI_API void register_default_tools(Tools& tools); +LA_UI_API void select(Registry& r, const std::function& selection_system); } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/imgui/buttons.h b/modules/ui/include/lagrange/ui/imgui/buttons.h index bdf2eb61..5fd81c62 100644 --- a/modules/ui/include/lagrange/ui/imgui/buttons.h +++ b/modules/ui/include/lagrange/ui/imgui/buttons.h @@ -14,16 +14,18 @@ /* Custom buttons used throughout the UI */ -#include #include +#include + +#include namespace lagrange { namespace ui { // A plain button with no decoration -bool button_unstyled(const char* label); +LA_UI_API bool button_unstyled(const char* label); -bool button_toolbar( +LA_UI_API bool button_toolbar( bool selected, const std::string& label, const std::string& tooltip = "", @@ -31,7 +33,7 @@ bool button_toolbar( const Keybinds* keybinds = nullptr, bool enabled = true); -bool button_icon( +LA_UI_API bool button_icon( bool selected, const std::string& label, const std::string& tooltip = "", diff --git a/modules/ui/include/lagrange/ui/imgui/progress.h b/modules/ui/include/lagrange/ui/imgui/progress.h index b35a5077..ca635a1f 100644 --- a/modules/ui/include/lagrange/ui/imgui/progress.h +++ b/modules/ui/include/lagrange/ui/imgui/progress.h @@ -30,20 +30,22 @@ #pragma once +#include + #include namespace lagrange { namespace ui { namespace imgui { -bool BufferingBar( +LA_UI_API bool BufferingBar( const char* label, float value, const ImVec2& size_arg, const ImU32& bg_col, const ImU32& fg_col); -bool Spinner(const char* label, float radius, int thickness, const ImU32& color); +LA_UI_API bool Spinner(const char* label, float radius, int thickness, const ImU32& color); } // namespace imgui } // namespace ui diff --git a/modules/ui/include/lagrange/ui/panels/ComponentPanel.h b/modules/ui/include/lagrange/ui/panels/ComponentPanel.h index fcd6d83b..a0d99c75 100644 --- a/modules/ui/include/lagrange/ui/panels/ComponentPanel.h +++ b/modules/ui/include/lagrange/ui/panels/ComponentPanel.h @@ -11,14 +11,15 @@ */ #pragma once +#include #include namespace lagrange { namespace ui { -Entity add_component_panel(Registry& r, const std::string& name = "Components"); +LA_UI_API Entity add_component_panel(Registry& r, const std::string& name = "Components"); } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/panels/KeybindsPanel.h b/modules/ui/include/lagrange/ui/panels/KeybindsPanel.h index a77c0285..157a91e9 100644 --- a/modules/ui/include/lagrange/ui/panels/KeybindsPanel.h +++ b/modules/ui/include/lagrange/ui/panels/KeybindsPanel.h @@ -11,12 +11,13 @@ */ #pragma once +#include #include namespace lagrange { namespace ui { -Entity add_keybinds_panel(Registry& r, const std::string& name = "Keybinds"); +LA_UI_API Entity add_keybinds_panel(Registry& r, const std::string& name = "Keybinds"); } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/panels/LoggerPanel.h b/modules/ui/include/lagrange/ui/panels/LoggerPanel.h index 1a75d250..4ac64680 100644 --- a/modules/ui/include/lagrange/ui/panels/LoggerPanel.h +++ b/modules/ui/include/lagrange/ui/panels/LoggerPanel.h @@ -10,15 +10,17 @@ * governing permissions and limitations under the License. */ #pragma once + +#include #include #include namespace lagrange { namespace ui { -Entity add_logger_panel(Registry& r, const std::string& name = "Logger"); +LA_UI_API Entity add_logger_panel(Registry& r, const std::string& name = "Logger"); -std::shared_ptr get_logger_sink(Registry& r); +LA_UI_API std::shared_ptr get_logger_sink(Registry& r); } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/panels/RendererPanel.h b/modules/ui/include/lagrange/ui/panels/RendererPanel.h index f59cd036..f1535b21 100644 --- a/modules/ui/include/lagrange/ui/panels/RendererPanel.h +++ b/modules/ui/include/lagrange/ui/panels/RendererPanel.h @@ -11,12 +11,13 @@ */ #pragma once +#include #include namespace lagrange { namespace ui { -Entity add_renderer_panel(Registry& r, const std::string& name = "Renderer"); +LA_UI_API Entity add_renderer_panel(Registry& r, const std::string& name = "Renderer"); } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/panels/ScenePanel.h b/modules/ui/include/lagrange/ui/panels/ScenePanel.h index 28087a9d..6b138897 100644 --- a/modules/ui/include/lagrange/ui/panels/ScenePanel.h +++ b/modules/ui/include/lagrange/ui/panels/ScenePanel.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include #include #include @@ -42,7 +43,7 @@ struct ScenePanel }; -Entity add_scene_panel(Registry& r, const std::string& name = "Scene"); +LA_UI_API Entity add_scene_panel(Registry& r, const std::string& name = "Scene"); } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/panels/ToolbarPanel.h b/modules/ui/include/lagrange/ui/panels/ToolbarPanel.h index f56ac6a8..e35a80d5 100644 --- a/modules/ui/include/lagrange/ui/panels/ToolbarPanel.h +++ b/modules/ui/include/lagrange/ui/panels/ToolbarPanel.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include namespace lagrange { @@ -24,8 +25,8 @@ struct ToolbarPanel }; -Entity add_toolbar_panel(Registry& r, const std::string& name = "Toolbar"); +LA_UI_API Entity add_toolbar_panel(Registry& r, const std::string& name = "Toolbar"); } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/panels/ViewportPanel.h b/modules/ui/include/lagrange/ui/panels/ViewportPanel.h index d32a0828..b712eb33 100644 --- a/modules/ui/include/lagrange/ui/panels/ViewportPanel.h +++ b/modules/ui/include/lagrange/ui/panels/ViewportPanel.h @@ -11,7 +11,9 @@ */ #pragma once +#include #include + #include namespace lagrange { @@ -64,8 +66,8 @@ struct FocusedViewportPanel Entity viewport_panel = NullEntity; }; -Entity add_viewport_panel(Registry& r, const std::string& name, Entity viewport); +LA_UI_API Entity add_viewport_panel(Registry& r, const std::string& name, Entity viewport); } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/systems/camera_systems.h b/modules/ui/include/lagrange/ui/systems/camera_systems.h index 0a061090..4cf67da1 100644 --- a/modules/ui/include/lagrange/ui/systems/camera_systems.h +++ b/modules/ui/include/lagrange/ui/systems/camera_systems.h @@ -11,22 +11,23 @@ */ #pragma once +#include #include namespace lagrange { namespace ui { /// Adjust camera based on CameraController -void camera_controller_system(Registry& registry); +LA_UI_API void camera_controller_system(Registry& registry); /// Rotates Camera based on CameraTurntable component -void camera_turntable_system(Registry& registry); +LA_UI_API void camera_turntable_system(Registry& registry); /// Zooms Camera based on CameraZoomToFit component -void camera_focusfit_system(Registry& registry); +LA_UI_API void camera_focusfit_system(Registry& registry); } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/systems/camera_turntable.h b/modules/ui/include/lagrange/ui/systems/camera_turntable.h index a830f404..5eb748cf 100644 --- a/modules/ui/include/lagrange/ui/systems/camera_turntable.h +++ b/modules/ui/include/lagrange/ui/systems/camera_turntable.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include namespace lagrange { @@ -18,4 +19,4 @@ namespace ui { } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/systems/render_background.h b/modules/ui/include/lagrange/ui/systems/render_background.h index d8375249..fa7e5a5c 100644 --- a/modules/ui/include/lagrange/ui/systems/render_background.h +++ b/modules/ui/include/lagrange/ui/systems/render_background.h @@ -10,14 +10,16 @@ * governing permissions and limitations under the License. */ #pragma once + +#include #include namespace lagrange { namespace ui { -void render_background(Registry& registry); +LA_UI_API void render_background(Registry& registry); } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/systems/render_geometry.h b/modules/ui/include/lagrange/ui/systems/render_geometry.h index c884e2a5..8505bc30 100644 --- a/modules/ui/include/lagrange/ui/systems/render_geometry.h +++ b/modules/ui/include/lagrange/ui/systems/render_geometry.h @@ -10,18 +10,20 @@ * governing permissions and limitations under the License. */ #pragma once + +#include #include namespace lagrange { namespace ui { -void setup_vertex_data(Registry& registry); -void render_geometry(Registry& registry); +LA_UI_API void setup_vertex_data(Registry& registry); +LA_UI_API void render_geometry(Registry& registry); // Todo move to its own file -void render_post_process(Registry& registry); +LA_UI_API void render_post_process(Registry& registry); } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/systems/render_shadowmaps.h b/modules/ui/include/lagrange/ui/systems/render_shadowmaps.h index c60eacf6..0e7c2ea8 100644 --- a/modules/ui/include/lagrange/ui/systems/render_shadowmaps.h +++ b/modules/ui/include/lagrange/ui/systems/render_shadowmaps.h @@ -10,14 +10,16 @@ * governing permissions and limitations under the License. */ #pragma once + +#include #include namespace lagrange { namespace ui { -void render_shadowmaps(Registry& registry); +LA_UI_API void render_shadowmaps(Registry& registry); } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/systems/render_viewports.h b/modules/ui/include/lagrange/ui/systems/render_viewports.h index fa767075..112d484b 100644 --- a/modules/ui/include/lagrange/ui/systems/render_viewports.h +++ b/modules/ui/include/lagrange/ui/systems/render_viewports.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include namespace lagrange { @@ -19,13 +20,13 @@ namespace ui { /// @brief Render viewport /// Renders all geometry visible in this viewport into the viewport's framebuffer -void render_viewport(Registry& registry, Entity e); +LA_UI_API void render_viewport(Registry& registry, Entity e); /// @brief Render all enabled viewports /// @param registry -void render_viewports(Registry& registry); +LA_UI_API void render_viewports(Registry& registry); } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/systems/update_accelerated_picking.h b/modules/ui/include/lagrange/ui/systems/update_accelerated_picking.h index 15dd81d2..94543d5a 100644 --- a/modules/ui/include/lagrange/ui/systems/update_accelerated_picking.h +++ b/modules/ui/include/lagrange/ui/systems/update_accelerated_picking.h @@ -11,12 +11,13 @@ */ #pragma once +#include #include namespace lagrange { namespace ui { -void update_accelerated_picking(Registry& r); +LA_UI_API void update_accelerated_picking(Registry& r); } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/systems/update_gizmo.h b/modules/ui/include/lagrange/ui/systems/update_gizmo.h index a027c798..55293174 100644 --- a/modules/ui/include/lagrange/ui/systems/update_gizmo.h +++ b/modules/ui/include/lagrange/ui/systems/update_gizmo.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include #include #include @@ -42,11 +43,11 @@ struct GizmoObjectTransform }; -bool gizmo_system_is_using(); -bool gizmo_system_is_over(); -void gizmo_system_set_draw_list(); +LA_UI_API bool gizmo_system_is_using(); +LA_UI_API bool gizmo_system_is_over(); +LA_UI_API void gizmo_system_set_draw_list(); -void gizmo_system( +LA_UI_API void gizmo_system( Registry& registry, const Camera& camera, const Eigen::Vector2f& canvas_pos, @@ -54,4 +55,4 @@ void gizmo_system( } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/systems/update_lights.h b/modules/ui/include/lagrange/ui/systems/update_lights.h index bf4eb13e..fb9533eb 100644 --- a/modules/ui/include/lagrange/ui/systems/update_lights.h +++ b/modules/ui/include/lagrange/ui/systems/update_lights.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include namespace lagrange { @@ -20,8 +21,8 @@ namespace ui { /* Updates lights and their visualization */ -void update_lights_system(Registry& registry); +LA_UI_API void update_lights_system(Registry& registry); } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/systems/update_mesh_bounds.h b/modules/ui/include/lagrange/ui/systems/update_mesh_bounds.h index 7279a9bd..5bd86ff1 100644 --- a/modules/ui/include/lagrange/ui/systems/update_mesh_bounds.h +++ b/modules/ui/include/lagrange/ui/systems/update_mesh_bounds.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include namespace lagrange { @@ -20,8 +21,8 @@ namespace ui { /* Updates AABB of every entity having */ -void update_mesh_bounds_system(Registry& ctx); +LA_UI_API void update_mesh_bounds_system(Registry& ctx); } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/systems/update_mesh_buffers.h b/modules/ui/include/lagrange/ui/systems/update_mesh_buffers.h index 784eeab0..83ef2612 100644 --- a/modules/ui/include/lagrange/ui/systems/update_mesh_buffers.h +++ b/modules/ui/include/lagrange/ui/systems/update_mesh_buffers.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include namespace lagrange { @@ -20,8 +21,8 @@ namespace ui { Creates mesh buffers for Output: Resource component on MeshGeometry entity */ -void update_mesh_buffers_system(Registry& ctx); +LA_UI_API void update_mesh_buffers_system(Registry& ctx); } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/systems/update_mesh_elements_hovered.h b/modules/ui/include/lagrange/ui/systems/update_mesh_elements_hovered.h index ccb0ddad..cfeba1c3 100644 --- a/modules/ui/include/lagrange/ui/systems/update_mesh_elements_hovered.h +++ b/modules/ui/include/lagrange/ui/systems/update_mesh_elements_hovered.h @@ -11,12 +11,13 @@ */ #pragma once +#include #include namespace lagrange { namespace ui { -void update_mesh_elements_hovered(Registry& r); +LA_UI_API void update_mesh_elements_hovered(Registry& r); } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/systems/update_mesh_hovered.h b/modules/ui/include/lagrange/ui/systems/update_mesh_hovered.h index 353aa64e..123f8335 100644 --- a/modules/ui/include/lagrange/ui/systems/update_mesh_hovered.h +++ b/modules/ui/include/lagrange/ui/systems/update_mesh_hovered.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include namespace lagrange { @@ -18,7 +19,7 @@ namespace ui { /// Sets component if the mesh is hovered a ViewportPanel. /// See SelectionContext and default_tools for details -void update_mesh_hovered(Registry& ctx); +LA_UI_API void update_mesh_hovered(Registry& ctx); } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/systems/update_scene_bounds.h b/modules/ui/include/lagrange/ui/systems/update_scene_bounds.h index 1b8e221f..b9926a4c 100644 --- a/modules/ui/include/lagrange/ui/systems/update_scene_bounds.h +++ b/modules/ui/include/lagrange/ui/systems/update_scene_bounds.h @@ -11,7 +11,7 @@ */ #pragma once - +#include #include #include @@ -20,8 +20,8 @@ namespace ui { /// Sets context variable -void update_scene_bounds_system(Registry& ctx); +LA_UI_API void update_scene_bounds_system(Registry& ctx); } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/systems/update_transform_hierarchy.h b/modules/ui/include/lagrange/ui/systems/update_transform_hierarchy.h index ed28efdc..a48d14a3 100644 --- a/modules/ui/include/lagrange/ui/systems/update_transform_hierarchy.h +++ b/modules/ui/include/lagrange/ui/systems/update_transform_hierarchy.h @@ -11,13 +11,14 @@ */ #pragma once +#include #include namespace lagrange { namespace ui { // Todo expose outside api callback -void update_transform_hierarchy(Registry& registry); +LA_UI_API void update_transform_hierarchy(Registry& registry); } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/types/AABB.h b/modules/ui/include/lagrange/ui/types/AABB.h index e1cd4d60..a66f7132 100644 --- a/modules/ui/include/lagrange/ui/types/AABB.h +++ b/modules/ui/include/lagrange/ui/types/AABB.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include #include @@ -20,7 +21,7 @@ namespace ui { class Frustum; -class AABB : public Eigen::AlignedBox3f +class LA_UI_API AABB : public Eigen::AlignedBox3f { public: using Super = Eigen::AlignedBox3f; diff --git a/modules/ui/include/lagrange/ui/types/Camera.h b/modules/ui/include/lagrange/ui/types/Camera.h index bb636e9b..ee14d3c8 100644 --- a/modules/ui/include/lagrange/ui/types/Camera.h +++ b/modules/ui/include/lagrange/ui/types/Camera.h @@ -10,7 +10,9 @@ * governing permissions and limitations under the License. */ #pragma once + #include +#include namespace lagrange { namespace ui { @@ -32,7 +34,7 @@ namespace ui { /// Note: Caches view and perspective matrices and their inverses. /// -class Camera +class LA_UI_API Camera { public: /// diff --git a/modules/ui/include/lagrange/ui/types/FrameBuffer.h b/modules/ui/include/lagrange/ui/types/FrameBuffer.h index 440abfc8..96c7354e 100644 --- a/modules/ui/include/lagrange/ui/types/FrameBuffer.h +++ b/modules/ui/include/lagrange/ui/types/FrameBuffer.h @@ -16,6 +16,7 @@ #include #include +#include namespace lagrange { namespace ui { @@ -25,7 +26,7 @@ namespace ui { Allows setting textures as color and depth attachements Shares ownership of the attached textures */ -class FrameBuffer +class LA_UI_API FrameBuffer { public: /* diff --git a/modules/ui/include/lagrange/ui/types/Frustum.h b/modules/ui/include/lagrange/ui/types/Frustum.h index ecde57b1..bc05f0a2 100644 --- a/modules/ui/include/lagrange/ui/types/Frustum.h +++ b/modules/ui/include/lagrange/ui/types/Frustum.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include #include @@ -38,7 +39,7 @@ enum FrustumVertices : unsigned int { }; /// Frustum defined using 6 planes -class Frustum +class LA_UI_API Frustum { public: using Plane = Eigen::Hyperplane; diff --git a/modules/ui/include/lagrange/ui/types/GLContext.h b/modules/ui/include/lagrange/ui/types/GLContext.h index 64614847..6d3d0a4d 100644 --- a/modules/ui/include/lagrange/ui/types/GLContext.h +++ b/modules/ui/include/lagrange/ui/types/GLContext.h @@ -11,6 +11,8 @@ */ #pragma once +#include + // GLEW must come before GLFW #if defined(__EMSCRIPTEN__) #include @@ -57,7 +59,7 @@ bool checkGLError(const char* label); } while (0) #endif -struct GLState +struct LA_UI_API GLState { GLState(); @@ -146,7 +148,7 @@ struct GLState std::thread::id m_gl_thread_id; }; -struct GLScope +struct LA_UI_API GLScope { GLScope(bool push = true); ~GLScope(); diff --git a/modules/ui/include/lagrange/ui/types/Keybinds.h b/modules/ui/include/lagrange/ui/types/Keybinds.h index 9b48e222..b10a02e3 100644 --- a/modules/ui/include/lagrange/ui/types/Keybinds.h +++ b/modules/ui/include/lagrange/ui/types/Keybinds.h @@ -11,15 +11,17 @@ */ #pragma once +#include +#include + +#include + #include #include #include #include #include -#include -#include - namespace lagrange { namespace ui { @@ -30,19 +32,19 @@ namespace ui { /// Use syntax "context.optional_category.action", e.g. "viewport.camera.pan" /// Use "global" context for keybinds to be available everywhere /// -class Keybinds +class LA_UI_API Keybinds { public: enum class KeyState { NONE, PRESSED, DOWN, RELEASED }; Keybinds() {} - + /// /// Key/mouse shortcut /// /// Stores main button, modifiers and current and previous state /// - struct Keybind + struct LA_UI_API Keybind { Keybind(ImGuiKey btn, const std::vector& modifier_keys = {}); ImGuiKey button = ImGuiKey_None; @@ -64,7 +66,7 @@ class Keybinds using MapType = std::map>; /// @brief Updating key states - /// + /// /// Updates keybinds state based on key states /// void update(); diff --git a/modules/ui/include/lagrange/ui/types/Material.h b/modules/ui/include/lagrange/ui/types/Material.h index 2f3c54e2..bbea853a 100644 --- a/modules/ui/include/lagrange/ui/types/Material.h +++ b/modules/ui/include/lagrange/ui/types/Material.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include #include @@ -18,7 +19,7 @@ namespace lagrange { namespace ui { -class Material +class LA_UI_API Material { public: Material(Registry& r, StringID shader_id, const ShaderDefines& shader_defines = {}); diff --git a/modules/ui/include/lagrange/ui/types/Shader.h b/modules/ui/include/lagrange/ui/types/Shader.h index 067fecc5..8dedae73 100644 --- a/modules/ui/include/lagrange/ui/types/Shader.h +++ b/modules/ui/include/lagrange/ui/types/Shader.h @@ -11,26 +11,28 @@ */ #pragma once +#include +#include +#include + +#include +#include + +#include + #if defined(__EMSCRIPTEN__) #include #else #include #endif -#include -#include +#include +#include #include #include #include #include -#include -#include -#include -#include - -#include - namespace lagrange { namespace ui { @@ -145,7 +147,7 @@ enum ShaderInterface { SHADER_INTERFACE_ATTRIB = 2 }; -struct ShaderValue +struct LA_UI_API ShaderValue { int location; int size; @@ -195,7 +197,7 @@ struct ShaderValue static ShaderValue none; }; -class ShaderException : public std::runtime_error +class LA_UI_API ShaderException : public std::runtime_error { public: ShaderException(const char* str) @@ -211,7 +213,7 @@ class ShaderException : public std::runtime_error using ShaderDefines = std::vector>; -class Shader +class LA_UI_API Shader { public: Shader(const std::string& code, const ShaderDefines& defines); // throws diff --git a/modules/ui/include/lagrange/ui/types/ShaderLoader.h b/modules/ui/include/lagrange/ui/types/ShaderLoader.h index cc103632..36621227 100644 --- a/modules/ui/include/lagrange/ui/types/ShaderLoader.h +++ b/modules/ui/include/lagrange/ui/types/ShaderLoader.h @@ -11,13 +11,14 @@ */ #pragma once +#include #include #include namespace lagrange { namespace ui { -struct ShaderLoader : entt::resource_loader +struct LA_UI_API ShaderLoader : entt::resource_loader { enum class PathType { REAL, VIRTUAL }; @@ -41,31 +42,31 @@ using RegisteredShaders = std::unordered_map; //Register shader with given id that can be used to load/reload and access the shader -entt::id_type register_shader_as(Registry& r, entt::id_type id, const ShaderDefinition& def); +LA_UI_API entt::id_type register_shader_as(Registry& r, entt::id_type id, const ShaderDefinition& def); //Register shader, returns and ID that can be used to load/reload and access the shader -entt::id_type register_shader(Registry& r, const ShaderDefinition& def); -entt::id_type +LA_UI_API entt::id_type register_shader(Registry& r, const ShaderDefinition& def); +LA_UI_API entt::id_type register_shader(Registry& r, const std::string& path, const std::string& display_name); -entt::id_type +LA_UI_API entt::id_type register_shader_variant(Registry& r, entt::id_type id, const ShaderDefines& shader_defines); -ShaderResource get_shader(Registry& r, entt::id_type id); +LA_UI_API ShaderResource get_shader(Registry& r, entt::id_type id); -RegisteredShaders& get_registered_shaders(Registry& r); -ShaderCache& get_shader_cache(Registry& r); +LA_UI_API RegisteredShaders& get_registered_shaders(Registry& r); +LA_UI_API ShaderCache& get_shader_cache(Registry& r); /// Creates a file using `virtual_path` with `contents` in the shader virtual file system. /// This file will be visible to the ShaderLoader, to be directly loaded as a shader /// or to be included in another shader via #include "virtual/fs/path/.." /// Returns true if written successfully. /// Returns false if 1. No virtual fs is used (DEFAULT_SHADERS_USE_REAL_PATH is defined), 2. File already exists and overwrite==false -bool add_file_to_shader_virtual_fs( +LA_UI_API bool add_file_to_shader_virtual_fs( const std::string& virtual_path, const std::string& contents, bool overwrite = false); } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/types/Systems.h b/modules/ui/include/lagrange/ui/types/Systems.h index a5f98bc1..31329fde 100644 --- a/modules/ui/include/lagrange/ui/types/Systems.h +++ b/modules/ui/include/lagrange/ui/types/Systems.h @@ -12,6 +12,8 @@ #pragma once #include +#include + #include namespace lagrange { @@ -28,7 +30,7 @@ namespace ui { /// //Run the systems in the Init stage /// S.run(Systems::Stage::Init, r); /// @endcode -class Systems +class LA_UI_API Systems { public: enum class Stage : int { Init = 0, Interface, Simulation, Render, Post, _SIZE }; @@ -81,4 +83,4 @@ class Systems } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/types/Texture.h b/modules/ui/include/lagrange/ui/types/Texture.h index 71323d43..ec1b80c0 100644 --- a/modules/ui/include/lagrange/ui/types/Texture.h +++ b/modules/ui/include/lagrange/ui/types/Texture.h @@ -14,6 +14,8 @@ #include #include #include +#include + #include #include #include @@ -24,7 +26,7 @@ namespace lagrange { namespace ui { -class Texture +class LA_UI_API Texture { public: struct Transform diff --git a/modules/ui/include/lagrange/ui/types/Tools.h b/modules/ui/include/lagrange/ui/types/Tools.h index daf7b926..f9c3abf7 100644 --- a/modules/ui/include/lagrange/ui/types/Tools.h +++ b/modules/ui/include/lagrange/ui/types/Tools.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include #include @@ -39,7 +40,7 @@ entt::id_type register_tool_type( /* Container for Tool systems */ -class Tools +class LA_UI_API Tools { public: template @@ -117,4 +118,4 @@ class Tools } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/types/VertexBuffer.h b/modules/ui/include/lagrange/ui/types/VertexBuffer.h index 5e8c5888..fc3c39da 100644 --- a/modules/ui/include/lagrange/ui/types/VertexBuffer.h +++ b/modules/ui/include/lagrange/ui/types/VertexBuffer.h @@ -11,7 +11,9 @@ */ #pragma once +#include #include + #include #include #include @@ -64,7 +66,7 @@ struct type_traits enum { type = GL_FLOAT, integral = 0 }; }; -struct VertexBuffer +struct LA_UI_API VertexBuffer { VertexBuffer(GLenum _target = GL_ARRAY_BUFFER); GLenum target; // most common: GL_ARRAY_BUFFER or GL_ELEMENT_ARRAY_BUFFER @@ -127,7 +129,7 @@ void VertexBuffer::upload(const std::vector& arr, uint32_t component_count) } -struct VAO +struct LA_UI_API VAO { VAO() : id(0) @@ -139,7 +141,7 @@ struct VAO }; -struct GPUBuffer +struct LA_UI_API GPUBuffer { GPUBuffer(const GPUBuffer&) = delete; GPUBuffer& operator=(const GPUBuffer&) = delete; diff --git a/modules/ui/include/lagrange/ui/utils/bounds.h b/modules/ui/include/lagrange/ui/utils/bounds.h index 383730e8..b4e90e27 100644 --- a/modules/ui/include/lagrange/ui/utils/bounds.h +++ b/modules/ui/include/lagrange/ui/utils/bounds.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include #include #include @@ -21,20 +22,20 @@ namespace ui { /// Returns Axis Aligned Bounding Box of the entity in world space /// If entity does not have bounds, returns an empty AABB -AABB get_bounding_box(const Registry& registry, Entity e); +LA_UI_API AABB get_bounding_box(const Registry& registry, Entity e); /// Returns Axis Aligned Bounding Box of the entity in model space /// If entity does not have bounds, returns an empty AABB -AABB get_bounding_box_local(const Registry& registry, Entity e); +LA_UI_API AABB get_bounding_box_local(const Registry& registry, Entity e); /// Returns Axis Aligned Bounding Box of all entities with component /// If there's no selection returns an empty AABB -AABB get_selection_bounding_box(const Registry& registry); +LA_UI_API AABB get_selection_bounding_box(const Registry& registry); /// Returns the least distance between `from` and any point within any bounding box. /// Returns 0 if `from` lies within a bounding box. /// Returns -1 if no bounds exist. -float get_nearest_bounds_distance( +LA_UI_API float get_nearest_bounds_distance( const Registry& registry, const Eigen::Vector3f& from, const Layer& visible, @@ -42,7 +43,7 @@ float get_nearest_bounds_distance( /// Returns the greatest distance between `from` and any point within any bounding box. /// Returns -1 if no bounds exist. -float get_furthest_bounds_distance( +LA_UI_API float get_furthest_bounds_distance( const Registry& registry, const Eigen::Vector3f& from, const Layer& visible, @@ -50,15 +51,15 @@ float get_furthest_bounds_distance( /// Returns the bounding box of everything /// (must be set as context variable after update_scene_bounds) -AABB get_scene_bounding_box(const Registry& registry); +LA_UI_API AABB get_scene_bounding_box(const Registry& registry); /// Returns the bounds of everything /// (must be set as context variable after update_scene_bounds) -const Bounds& get_scene_bounds(const Registry& registry); +LA_UI_API const Bounds& get_scene_bounds(const Registry& registry); /// @copydoc -Bounds& get_scene_bounds(Registry& registry); +LA_UI_API Bounds& get_scene_bounds(Registry& registry); } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/utils/colormap.h b/modules/ui/include/lagrange/ui/utils/colormap.h index 6e1ff04f..fb7c242a 100644 --- a/modules/ui/include/lagrange/ui/utils/colormap.h +++ b/modules/ui/include/lagrange/ui/utils/colormap.h @@ -10,21 +10,23 @@ * governing permissions and limitations under the License. */ #pragma once + +#include #include #include namespace lagrange { namespace ui { -Color colormap_viridis(float t); -Color colormap_magma(float t); -Color colormap_plasma(float t); -Color colormap_inferno(float t); -Color colormap_turbo(float t); -Color colormap_coolwarm(float t); +LA_UI_API Color colormap_viridis(float t); +LA_UI_API Color colormap_magma(float t); +LA_UI_API Color colormap_plasma(float t); +LA_UI_API Color colormap_inferno(float t); +LA_UI_API Color colormap_turbo(float t); +LA_UI_API Color colormap_coolwarm(float t); -std::shared_ptr generate_colormap( +LA_UI_API std::shared_ptr generate_colormap( const std::function& generator, int resolution = 256); diff --git a/modules/ui/include/lagrange/ui/utils/file_dialog.h b/modules/ui/include/lagrange/ui/utils/file_dialog.h index 9025bf95..c97259f5 100644 --- a/modules/ui/include/lagrange/ui/utils/file_dialog.h +++ b/modules/ui/include/lagrange/ui/utils/file_dialog.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include #include @@ -25,7 +26,7 @@ namespace ui { /// Removes the file from temporary browser filesystem when out of scope /// If created by `create_output_path`: triggers download of the file when going out of scope /// -class FileDialogPath +class LA_UI_API FileDialogPath { public: /// Create FileDialogPath from Open File Dialog @@ -94,7 +95,7 @@ enum class FolderOpen { /// /// @return Selected file path. /// -FileDialogPath open_file( +LA_UI_API FileDialogPath open_file( const std::string& title, const fs::path& default_path = ".", const std::vector& filters = {{"All Files", "*"}}); @@ -108,7 +109,7 @@ FileDialogPath open_file( /// /// @return Selected file path. /// -std::vector open_files( +LA_UI_API std::vector open_files( const std::string& title, const fs::path& default_path = ".", const std::vector& filters = {{"All Files", "*"}}); @@ -123,7 +124,7 @@ std::vector open_files( /// /// @return Selected file path. /// -FileDialogPath save_file( +LA_UI_API FileDialogPath save_file( const std::string& title, const fs::path& default_path = ".", const std::vector& filters = {{"All Files", "*"}}, @@ -139,7 +140,7 @@ FileDialogPath save_file( /// /// @return Selected folder path. /// -FileDialogPath open_folder( +LA_UI_API FileDialogPath open_folder( const std::string& title, const fs::path& default_path = ".", FolderOpen open_behavior = FolderOpen::LastOpened); @@ -159,7 +160,7 @@ namespace utils { /// Example: /// filters: {{"Label", "*.x *.z *.w image/png"}} output: ".x,.z,.w,image/png" /// -std::string transform_filters_to_accept(const std::vector& filters); +LA_UI_API std::string transform_filters_to_accept(const std::vector& filters); } // namespace utils diff --git a/modules/ui/include/lagrange/ui/utils/ibl.h b/modules/ui/include/lagrange/ui/utils/ibl.h index f63c9502..5d96dceb 100644 --- a/modules/ui/include/lagrange/ui/utils/ibl.h +++ b/modules/ui/include/lagrange/ui/utils/ibl.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include #include #include @@ -25,33 +26,33 @@ namespace ui { /// @param path /// @param resolution /// @return IBL -IBL generate_ibl(const fs::path& path, size_t resolution = 1024); +LA_UI_API IBL generate_ibl(const fs::path& path, size_t resolution = 1024); /// @brief Generates Image Based Light from given rectangular texture. /// Throws std::runtime_error on failure. /// @param path /// @param resolution /// @return IBL -IBL generate_ibl(const std::shared_ptr& background_texture, size_t resolution = 1024); +LA_UI_API IBL generate_ibl(const std::shared_ptr& background_texture, size_t resolution = 1024); /// @brief Returns first entity found in registry. If there are none, returns invalid Entity /// @param registry /// @return Entity -Entity get_ibl_entity(const Registry& registry); +LA_UI_API Entity get_ibl_entity(const Registry& registry); /// @brief Returns pointer to the first IBL found in the registry. Nullptr if there is none /// @param registry /// @return IBL pointer -const IBL* get_ibl(const Registry& registry); +LA_UI_API const IBL* get_ibl(const Registry& registry); /// @copydoc -IBL* get_ibl(Registry& registry); +LA_UI_API IBL* get_ibl(Registry& registry); /// @brief Adds IBL to the scene -Entity add_ibl(Registry& registry, IBL ibl); +LA_UI_API Entity add_ibl(Registry& registry, IBL ibl); /// @brief Removes all ibls -void clear_ibl(Registry& registry); +LA_UI_API void clear_ibl(Registry& registry); /// @brief Saves IBL as individual .png files in given folder. /// Generated files: @@ -60,7 +61,7 @@ void clear_ibl(Registry& registry); /// diffuse_{00-05}.png, /// specular_{00-05}_mip_{00-miplevels}.png /// Cube maps follow GL_TEXTURE_CUBE_MAP_POSITIVE_X + i pattern. -bool save_ibl(const IBL& ibl, const fs::path& folder); +LA_UI_API bool save_ibl(const IBL& ibl, const fs::path& folder); } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/utils/immediate.h b/modules/ui/include/lagrange/ui/utils/immediate.h index b0bbd1a7..4c19b663 100644 --- a/modules/ui/include/lagrange/ui/utils/immediate.h +++ b/modules/ui/include/lagrange/ui/utils/immediate.h @@ -11,20 +11,21 @@ */ #pragma once +#include #include -#include +#include namespace lagrange { namespace ui { -void render_points(Registry& r, const std::vector& points); -void render_point(Registry& r, const Eigen::Vector3f& point); -void render_lines(Registry& r, const std::vector& lines); -void upload_immediate_system(Registry& r); +LA_UI_API void render_points(Registry& r, const std::vector& points); +LA_UI_API void render_point(Registry& r, const Eigen::Vector3f& point); +LA_UI_API void render_lines(Registry& r, const std::vector& lines); +LA_UI_API void upload_immediate_system(Registry& r); /*void reset_immediate_system(Registry& r);*/ } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/utils/input.h b/modules/ui/include/lagrange/ui/utils/input.h index 19397e6a..91149940 100644 --- a/modules/ui/include/lagrange/ui/utils/input.h +++ b/modules/ui/include/lagrange/ui/utils/input.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include #include @@ -18,14 +19,14 @@ namespace lagrange { namespace ui { -InputState& get_input(Registry& r); -const InputState& get_input(const Registry& r); +LA_UI_API InputState& get_input(Registry& r); +LA_UI_API const InputState& get_input(const Registry& r); -Keybinds& get_keybinds(Registry& r); -const Keybinds& get_keybinds(const Registry& r); +LA_UI_API Keybinds& get_keybinds(Registry& r); +LA_UI_API const Keybinds& get_keybinds(const Registry& r); -InputState::Mouse& get_mouse(Registry& r); -const InputState::Mouse& get_mouse(const Registry& r); +LA_UI_API InputState::Mouse& get_mouse(Registry& r); +LA_UI_API const InputState::Mouse& get_mouse(const Registry& r); } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/utils/io.h b/modules/ui/include/lagrange/ui/utils/io.h index ec4452e9..b7c148f3 100644 --- a/modules/ui/include/lagrange/ui/utils/io.h +++ b/modules/ui/include/lagrange/ui/utils/io.h @@ -12,6 +12,7 @@ #pragma once #include +#include #include #include #include @@ -24,7 +25,7 @@ namespace lagrange { namespace ui { -std::shared_ptr load_texture( +LA_UI_API std::shared_ptr load_texture( const fs::path& path, const Texture::Params& params = Texture::Params()); @@ -32,7 +33,7 @@ std::shared_ptr load_texture( /// @param base_dir /// @param tinymat /// @return -std::shared_ptr +LA_UI_API std::shared_ptr convert_material(Registry& r, const fs::path& base_dir, const tinyobj::material_t& tinymat); diff --git a/modules/ui/include/lagrange/ui/utils/io_assimp.h b/modules/ui/include/lagrange/ui/utils/io_assimp.h index a301be8a..2e9e9e86 100644 --- a/modules/ui/include/lagrange/ui/utils/io_assimp.h +++ b/modules/ui/include/lagrange/ui/utils/io_assimp.h @@ -12,6 +12,7 @@ #pragma once #include +#include #include #include #include diff --git a/modules/ui/include/lagrange/ui/utils/layer.h b/modules/ui/include/lagrange/ui/utils/layer.h index fa6b58ba..28b613fc 100644 --- a/modules/ui/include/lagrange/ui/utils/layer.h +++ b/modules/ui/include/lagrange/ui/utils/layer.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include #include #include @@ -28,30 +29,30 @@ struct DefaultLayers }; -void add_to_layer(Registry& registry, Entity e, LayerIndex index); +LA_UI_API void add_to_layer(Registry& registry, Entity e, LayerIndex index); -void remove_from_layer(Registry& registry, Entity e, LayerIndex index); +LA_UI_API void remove_from_layer(Registry& registry, Entity e, LayerIndex index); -bool is_in_layer(Registry& registry, Entity e, LayerIndex index); +LA_UI_API bool is_in_layer(Registry& registry, Entity e, LayerIndex index); -bool is_in_any_layers(Registry& registry, Entity e, Layer layers_bitset); +LA_UI_API bool is_in_any_layers(Registry& registry, Entity e, Layer layers_bitset); -bool is_visible_in( +LA_UI_API bool is_visible_in( const Registry& registry, Entity e, const Layer& visible_layers, const Layer& hidden_layers); -LayerIndex get_next_available_layer_index(Registry& r); +LA_UI_API LayerIndex get_next_available_layer_index(Registry& r); -LayerIndex register_layer_name(Registry& registry, const std::string& name, LayerIndex index); -LayerIndex register_layer_name(Registry& registry, const std::string& name); +LA_UI_API LayerIndex register_layer_name(Registry& registry, const std::string& name, LayerIndex index); +LA_UI_API LayerIndex register_layer_name(Registry& registry, const std::string& name); -const std::string& get_layer_name(Registry& registry, LayerIndex index); +LA_UI_API const std::string& get_layer_name(Registry& registry, LayerIndex index); -void register_default_layer_names(Registry& registry); +LA_UI_API void register_default_layer_names(Registry& registry); } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/utils/lights.h b/modules/ui/include/lagrange/ui/utils/lights.h index 5c0a4dcf..3f4aea2c 100644 --- a/modules/ui/include/lagrange/ui/utils/lights.h +++ b/modules/ui/include/lagrange/ui/utils/lights.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include #include #include @@ -25,17 +26,17 @@ inline Eigen::Vector3f get_canonical_light_direction() } -Entity add_point_light( +LA_UI_API Entity add_point_light( Registry& r, Eigen::Vector3f intensity = Eigen::Vector3f::Ones(), Eigen::Vector3f position = Eigen::Vector3f::Zero()); -Entity add_directional_light( +LA_UI_API Entity add_directional_light( Registry& r, Eigen::Vector3f intensity = Eigen::Vector3f::Ones(), Eigen::Vector3f direction = -Eigen::Vector3f::UnitY()); -Entity add_spot_light( +LA_UI_API Entity add_spot_light( Registry& r, Eigen::Vector3f intensity = Eigen::Vector3f::Ones(), Eigen::Vector3f position = Eigen::Vector3f::Ones(), @@ -43,12 +44,12 @@ Entity add_spot_light( float cone_angle = pi() / 4.0f); -std::pair get_light_position_and_direction( +LA_UI_API std::pair get_light_position_and_direction( const Registry& r, Entity e); -void clear_lights(Registry& r); +LA_UI_API void clear_lights(Registry& r); } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/utils/math.h b/modules/ui/include/lagrange/ui/utils/math.h index 203fbf97..0bb1e901 100644 --- a/modules/ui/include/lagrange/ui/utils/math.h +++ b/modules/ui/include/lagrange/ui/utils/math.h @@ -10,6 +10,9 @@ * governing permissions and limitations under the License. */ #pragma once + +#include + #include #include @@ -21,7 +24,7 @@ using RowMajorMatrixXi = Eigen::Matrix #include #include +#include #include /// This header exposes functionality of previously registered mesh types @@ -60,103 +61,103 @@ lagrange::Mesh& cast_mesh(MeshData& mesh_data); template MeshType& get_mesh(Registry& r, Entity e); -MeshData& get_mesh_data(Registry& r, Entity e); -const MeshData& get_mesh_data(const Registry& r, Entity e); - -bool has_mesh_component(const Registry& r, Entity e); - -size_t get_num_vertices(const MeshData& d); -size_t get_num_facets(const MeshData& d); -size_t get_num_edges(const MeshData& d); -RowMajorMatrixXf get_mesh_vertices(const MeshData& d); -RowMajorMatrixXi get_mesh_facets(const MeshData& d); -RowMajorMatrixXf get_mesh_vertex_attribute(const MeshData& d, const std::string& name); -RowMajorMatrixXf get_mesh_corner_attribute(const MeshData& d, const std::string& name); -RowMajorMatrixXf get_mesh_facet_attribute(const MeshData& d, const std::string& name); -RowMajorMatrixXf get_mesh_edge_attribute(const MeshData& d, const std::string& name); -RowMajorMatrixXf get_mesh_attribute(const MeshData& d, IndexingMode mode, const std::string& name); +LA_UI_API MeshData& get_mesh_data(Registry& r, Entity e); +LA_UI_API const MeshData& get_mesh_data(const Registry& r, Entity e); + +LA_UI_API bool has_mesh_component(const Registry& r, Entity e); + +LA_UI_API size_t get_num_vertices(const MeshData& d); +LA_UI_API size_t get_num_facets(const MeshData& d); +LA_UI_API size_t get_num_edges(const MeshData& d); +LA_UI_API RowMajorMatrixXf get_mesh_vertices(const MeshData& d); +LA_UI_API RowMajorMatrixXi get_mesh_facets(const MeshData& d); +LA_UI_API RowMajorMatrixXf get_mesh_vertex_attribute(const MeshData& d, const std::string& name); +LA_UI_API RowMajorMatrixXf get_mesh_corner_attribute(const MeshData& d, const std::string& name); +LA_UI_API RowMajorMatrixXf get_mesh_facet_attribute(const MeshData& d, const std::string& name); +LA_UI_API RowMajorMatrixXf get_mesh_edge_attribute(const MeshData& d, const std::string& name); +LA_UI_API RowMajorMatrixXf get_mesh_attribute(const MeshData& d, IndexingMode mode, const std::string& name); std::pair -get_mesh_attribute_range(const MeshData& d, IndexingMode mode, const std::string& name); -AABB get_mesh_bounds(const MeshData& d); +LA_UI_API get_mesh_attribute_range(const MeshData& d, IndexingMode mode, const std::string& name); +LA_UI_API AABB get_mesh_bounds(const MeshData& d); ////////////////////////////////////////////////////////////////////////////////////// // Ensure existence of mesh attributes for rendering ////////////////////////////////////////////////////////////////////////////////////// -void ensure_uv(MeshData& d); -void ensure_normal(MeshData& d); -void ensure_tangent_bitangent(MeshData& d); -void ensure_is_selected_attribute(MeshData& d); -void map_indexed_attribute_to_corner_attribute(MeshData& d, const std::string& name); -void map_corner_attribute_to_vertex_attribute(MeshData& d, const std::string& name); +LA_UI_API void ensure_uv(MeshData& d); +LA_UI_API void ensure_normal(MeshData& d); +LA_UI_API void ensure_tangent_bitangent(MeshData& d); +LA_UI_API void ensure_is_selected_attribute(MeshData& d); +LA_UI_API void map_indexed_attribute_to_corner_attribute(MeshData& d, const std::string& name); +LA_UI_API void map_corner_attribute_to_vertex_attribute(MeshData& d, const std::string& name); ////////////////////////////////////////////////////////////////////////////////////// // Mesh to GPU upload ////////////////////////////////////////////////////////////////////////////////////// -void upload_mesh_vertices(const MeshData& d, GPUBuffer& gpu); -void upload_mesh_triangles(const MeshData& d, GPUBuffer& gpu); +LA_UI_API void upload_mesh_vertices(const MeshData& d, GPUBuffer& gpu); +LA_UI_API void upload_mesh_triangles(const MeshData& d, GPUBuffer& gpu); -void upload_mesh_vertex_attribute(const MeshData& d, const RowMajorMatrixXf& data, GPUBuffer& gpu); -void upload_mesh_corner_attribute(const MeshData& d, const RowMajorMatrixXf& data, GPUBuffer& gpu); -void upload_mesh_facet_attribute(const MeshData& d, const RowMajorMatrixXf& data, GPUBuffer& gpu); -void upload_mesh_edge_attribute(const MeshData& d, const RowMajorMatrixXf& data, GPUBuffer& gpu); +LA_UI_API void upload_mesh_vertex_attribute(const MeshData& d, const RowMajorMatrixXf& data, GPUBuffer& gpu); +LA_UI_API void upload_mesh_corner_attribute(const MeshData& d, const RowMajorMatrixXf& data, GPUBuffer& gpu); +LA_UI_API void upload_mesh_facet_attribute(const MeshData& d, const RowMajorMatrixXf& data, GPUBuffer& gpu); +LA_UI_API void upload_mesh_edge_attribute(const MeshData& d, const RowMajorMatrixXf& data, GPUBuffer& gpu); -std::unordered_map> upload_submesh_indices( +LA_UI_API std::unordered_map> upload_submesh_indices( const MeshData& d, const std::string& facet_attrib_name); ////////////////////////////////////////////////////////////////////////////////////// // Has attribute ////////////////////////////////////////////////////////////////////////////////////// -bool has_mesh_vertex_attribute(const MeshData& d, const std::string& name); -bool has_mesh_corner_attribute(const MeshData& d, const std::string& name); -bool has_mesh_facet_attribute(const MeshData& d, const std::string& name); -bool has_mesh_edge_attribute(const MeshData& d, const std::string& name); -bool has_mesh_indexed_attribute(const MeshData& d, const std::string& name); +LA_UI_API bool has_mesh_vertex_attribute(const MeshData& d, const std::string& name); +LA_UI_API bool has_mesh_corner_attribute(const MeshData& d, const std::string& name); +LA_UI_API bool has_mesh_facet_attribute(const MeshData& d, const std::string& name); +LA_UI_API bool has_mesh_edge_attribute(const MeshData& d, const std::string& name); +LA_UI_API bool has_mesh_indexed_attribute(const MeshData& d, const std::string& name); ////////////////////////////////////////////////////////////////////////////////////// // Picking ////////////////////////////////////////////////////////////////////////////////////// /// Intersect ray with MeshData -std::optional +LA_UI_API std::optional intersect_ray(const MeshData& d, const Eigen::Vector3f& origin, const Eigen::Vector3f& dir); -bool select_facets_in_frustum(MeshData& d, SelectionBehavior sel_behavior, const Frustum& frustum); -void select_vertices_in_frustum( +LA_UI_API bool select_facets_in_frustum(MeshData& d, SelectionBehavior sel_behavior, const Frustum& frustum); +LA_UI_API void select_vertices_in_frustum( MeshData& d, SelectionBehavior sel_behavior, const Frustum& frustum); -void select_edges_in_frustum(MeshData& d, SelectionBehavior sel_behavior, const Frustum& frustum); -void propagate_corner_selection(MeshData& d, const std::string& attrib_name); -void propagate_vertex_selection(MeshData& d, const std::string& attrib_name); -void propagate_facet_selection(MeshData& d, const std::string& attrib_name); -void combine_vertex_and_corner_selection(MeshData& d, const std::string& attrib_name); -void select_facets_by_color( +LA_UI_API void select_edges_in_frustum(MeshData& d, SelectionBehavior sel_behavior, const Frustum& frustum); +LA_UI_API void propagate_corner_selection(MeshData& d, const std::string& attrib_name); +LA_UI_API void propagate_vertex_selection(MeshData& d, const std::string& attrib_name); +LA_UI_API void propagate_facet_selection(MeshData& d, const std::string& attrib_name); +LA_UI_API void combine_vertex_and_corner_selection(MeshData& d, const std::string& attrib_name); +LA_UI_API void select_facets_by_color( MeshData& d, const std::string& attrib_name, SelectionBehavior sel_behavior, const unsigned char* color_bytes, size_t colors_byte_size); -void select_edges_by_color( +LA_UI_API void select_edges_by_color( MeshData& d, const std::string& attrib_name, SelectionBehavior sel_behavior, const unsigned char* color_bytes, size_t colors_byte_size); -void select_vertices_by_color( +LA_UI_API void select_vertices_by_color( MeshData& d, const std::string& attrib_name, SelectionBehavior sel_behavior, const unsigned char* color_bytes, size_t colors_byte_size); -void select_facets( +LA_UI_API void select_facets( MeshData& d, SelectionBehavior sel_behavior, const std::vector& facet_indices); -void filter_closest_vertex( +LA_UI_API void filter_closest_vertex( MeshData& d, const std::string& attrib_name, SelectionBehavior sel_behavior, diff --git a/modules/ui/include/lagrange/ui/utils/mesh_picking.h b/modules/ui/include/lagrange/ui/utils/mesh_picking.h index ad674fc5..0ed9fd9e 100644 --- a/modules/ui/include/lagrange/ui/utils/mesh_picking.h +++ b/modules/ui/include/lagrange/ui/utils/mesh_picking.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include #include #include @@ -24,7 +25,7 @@ struct MeshSelectionRender; /// Mesh triangle X ray intersection /// Uses an accelerated data structure if enabled for entity e (use `enable_accelerated_picking`) -std::optional +LA_UI_API std::optional intersect_ray(Registry& r, Entity e, const Eigen::Vector3f& origin, const Eigen::Vector3f& dir); /* @@ -39,7 +40,7 @@ intersect_ray(Registry& r, Entity e, const Eigen::Vector3f& origin, const Eigen: /// @param active_viewport Viewport to use for resolution and layer visibility /// @param local_frustum Frustum in mesh's local coordinate frame /// @return always true -bool select_visible_elements( +LA_UI_API bool select_visible_elements( Registry& r, StringID element_type, SelectionBehavior sel_behavior, @@ -54,7 +55,7 @@ bool select_visible_elements( /// @param selected_entity Entity with MeshGeometry component /// @param local_frustum Frustum in mesh's local coordinate frame /// @return always true -bool select_elements_in_frustum( +LA_UI_API bool select_elements_in_frustum( Registry& r, StringID element_type, SelectionBehavior sel_behavior, @@ -65,18 +66,18 @@ bool select_elements_in_frustum( Mesh element selection visualization utilities */ -void clear_element_selection_render(Registry& r, bool exclude_selected); +LA_UI_API void clear_element_selection_render(Registry& r, bool exclude_selected); -MeshSelectionRender& ensure_selection_render(Registry& r, Entity e); +LA_UI_API MeshSelectionRender& ensure_selection_render(Registry& r, Entity e); /// Update materials and visibility of different mesh elements -void update_selection_render( +LA_UI_API void update_selection_render( Registry& r, MeshSelectionRender& sel_render, const Entity& selected_mesh_entity, const StringID& current_element_type); -void mark_selection_dirty(Registry& r, MeshSelectionRender& sel_render); +LA_UI_API void mark_selection_dirty(Registry& r, MeshSelectionRender& sel_render); /* Accelerated picking @@ -85,10 +86,10 @@ void mark_selection_dirty(Registry& r, MeshSelectionRender& sel_render); /// Computes acceleration structure (igl::AABB) for faster ray-triangle intersection /// Entity can be entity with MeshGeometry or MeshData /// Returns false if entity does not have a mesh -bool enable_accelerated_picking(Registry& r, Entity e); +LA_UI_API bool enable_accelerated_picking(Registry& r, Entity e); -bool has_accelerated_picking(Registry& r, Entity e); +LA_UI_API bool has_accelerated_picking(Registry& r, Entity e); } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/utils/objectid_viewport.h b/modules/ui/include/lagrange/ui/utils/objectid_viewport.h index 89afef5c..3a57fb7f 100644 --- a/modules/ui/include/lagrange/ui/utils/objectid_viewport.h +++ b/modules/ui/include/lagrange/ui/utils/objectid_viewport.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include @@ -21,27 +22,27 @@ struct ViewportComponent; struct SelectionContext; /// Translates shader output color to numerial ID -int color_to_id(unsigned char r, unsigned char g, unsigned char b); +LA_UI_API int color_to_id(unsigned char r, unsigned char g, unsigned char b); /// Is numerical ID from shader a background? -bool is_id_background(int id); +LA_UI_API bool is_id_background(int id); /// Read pixels of viewport in rectangle at x,y of size w,h -const std::vector& +LA_UI_API const std::vector& read_pixels(Registry& r, ViewportComponent& v, int x, int y, int w, int h); /// Copies properties of active viewport to offscreen viewport, sets up a material override using override_shader -ViewportComponent& setup_offscreen_viewport( +LA_UI_API ViewportComponent& setup_offscreen_viewport( Registry& r, Entity ofscreen_viewport_entity, Entity active_viewport_entity, StringID override_shader); /// Sets rasterizer scissor based on SelectionContext -std::tuple +LA_UI_API std::tuple setup_scissor(Registry& r, ViewportComponent& offscreen_viewport, const SelectionContext& sel_ctx); } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/include/lagrange/ui/utils/render.h b/modules/ui/include/lagrange/ui/utils/render.h index ad9a3d03..21498815 100644 --- a/modules/ui/include/lagrange/ui/utils/render.h +++ b/modules/ui/include/lagrange/ui/utils/render.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include #include #include @@ -33,7 +34,7 @@ namespace render { /// Returns Eigen::Matrix4f perspective matrix with depth offset /// /// Based on http://www.terathon.com/gdc07_lengyel.pdf (slide 18) -Eigen::Matrix4f offset_depth(const Eigen::Projective3f& perspective, int layer_index); +LA_UI_API Eigen::Matrix4f offset_depth(const Eigen::Projective3f& perspective, int layer_index); /// @@ -44,14 +45,14 @@ Eigen::Matrix4f offset_depth(const Eigen::Projective3f& perspective, int layer_i /// Depth clamping: on /// Depth func: less or equal /// Seamless cube maps: on -void set_render_pass_defaults(GLScope& scope); +LA_UI_API void set_render_pass_defaults(GLScope& scope); //// /// Computes a plane perpendicular to direction /// /// Returns a pair of orthogonal directions, that together with direction form a orthogonal basis -std::pair compute_perpendicular_plane(Eigen::Vector3f direction); +LA_UI_API std::pair compute_perpendicular_plane(Eigen::Vector3f direction); } // namespace render @@ -71,31 +72,31 @@ struct Transform; Sets shader uniforms (PV, PVinv, M, NMat, screen_size) based on camera and optional transform Adjusts viewport glViewport if transform contains viewport transform */ -void set_render_transforms( +LA_UI_API void set_render_transforms( GLScope& scope, Shader& shader, const Camera& camera, const Transform* transform = nullptr); -void render_vertex_data(const VertexData& vd, GLenum primitive, GLsizei per_element_size); +LA_UI_API void render_vertex_data(const VertexData& vd, GLenum primitive, GLsizei per_element_size); -GLenum get_gl_primitive(const PrimitiveType& p); +LA_UI_API GLenum get_gl_primitive(const PrimitiveType& p); -GLsizei get_gl_primitive_size(const PrimitiveType& p); -GLsizei get_gl_primitive_size(GLenum primitive_enum); +LA_UI_API GLsizei get_gl_primitive_size(const PrimitiveType& p); +LA_UI_API GLsizei get_gl_primitive_size(GLenum primitive_enum); -std::shared_ptr generate_cube_vertex_data(bool edges = false); -std::shared_ptr generate_quad_vertex_data(); +LA_UI_API std::shared_ptr generate_cube_vertex_data(bool edges = false); +LA_UI_API std::shared_ptr generate_quad_vertex_data(); -GLMesh generate_quad_mesh_gpu(); +LA_UI_API GLMesh generate_quad_mesh_gpu(); -void update_vao(VertexData& vertex_data); +LA_UI_API void update_vao(VertexData& vertex_data); -entt::resource_handle get_or_load_shader( +LA_UI_API entt::resource_handle get_or_load_shader( entt::resource_cache& cache, const std::string& generic_path, bool virtual_fs = false); @@ -121,14 +122,14 @@ bool set_mesh_geometry_layout( } } -int get_gl_attribute_dimension(GLenum attrib_type); +LA_UI_API int get_gl_attribute_dimension(GLenum attrib_type); /// @brief Assigns buffers from GLMesh to GLVertexData to Shader specified locations /// @param glmesh collection of gpu buffers /// @param shader /// @param glvd /// @param indexing -void update_vertex_data( +LA_UI_API void update_vertex_data( const GLMesh& glmesh, const Shader& shader, VertexData& glvd, diff --git a/modules/ui/include/lagrange/ui/utils/selection.h b/modules/ui/include/lagrange/ui/utils/selection.h index 2a641ed5..6e5c2bb2 100644 --- a/modules/ui/include/lagrange/ui/utils/selection.h +++ b/modules/ui/include/lagrange/ui/utils/selection.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include #include #include @@ -21,8 +22,8 @@ namespace ui { class Keybinds; -bool deselect_all(Registry& registry); -bool dehover_all(Registry& registry); +LA_UI_API bool deselect_all(Registry& registry); +LA_UI_API bool dehover_all(Registry& registry); inline bool is_selected(const Registry& registry, Entity e) @@ -45,46 +46,46 @@ inline decltype(auto) hovered_view(Registry& registry) return registry.view(); } -bool is_child_selected(const Registry& registry, Entity e, bool recursive = true); -bool is_child_hovered(const Registry& registry, Entity e, bool recursive = true); +LA_UI_API bool is_child_selected(const Registry& registry, Entity e, bool recursive = true); +LA_UI_API bool is_child_hovered(const Registry& registry, Entity e, bool recursive = true); -std::vector collect_selected(const Registry& registry); -std::vector collect_hovered(const Registry& registry); +LA_UI_API std::vector collect_selected(const Registry& registry); +LA_UI_API std::vector collect_hovered(const Registry& registry); -bool set_selected( +LA_UI_API bool set_selected( Registry& registry, Entity e, SelectionBehavior behavior = SelectionBehavior::SET); -bool set_hovered(Registry& registry, Entity e, SelectionBehavior behavior = SelectionBehavior::SET); +LA_UI_API bool set_hovered(Registry& registry, Entity e, SelectionBehavior behavior = SelectionBehavior::SET); -bool select(Registry& registry, Entity e); +LA_UI_API bool select(Registry& registry, Entity e); -bool deselect(Registry& registry, Entity e); +LA_UI_API bool deselect(Registry& registry, Entity e); -bool hover(Registry& registry, Entity e); +LA_UI_API bool hover(Registry& registry, Entity e); -bool dehover(Registry& registry, Entity e); +LA_UI_API bool dehover(Registry& registry, Entity e); -SelectionContext& get_selection_context(Registry& r); +LA_UI_API SelectionContext& get_selection_context(Registry& r); -const SelectionContext& get_selection_context(const Registry& r); +LA_UI_API const SelectionContext& get_selection_context(const Registry& r); /* Utils*/ -SelectionBehavior selection_behavior(const Keybinds& keybinds); -bool are_selection_keys_down(const Keybinds& keybinds); -bool are_selection_keys_pressed(const Keybinds& keybinds); -bool are_selection_keys_released(const Keybinds& keybinds); +LA_UI_API SelectionBehavior selection_behavior(const Keybinds& keybinds); +LA_UI_API bool are_selection_keys_down(const Keybinds& keybinds); +LA_UI_API bool are_selection_keys_pressed(const Keybinds& keybinds); +LA_UI_API bool are_selection_keys_released(const Keybinds& keybinds); -SelectionBehavior selection_behavior(const Registry& r); -bool are_selection_keys_down(const Registry& r); -bool are_selection_keys_pressed(const Registry& r); -bool are_selection_keys_released(const Registry& r); +LA_UI_API SelectionBehavior selection_behavior(const Registry& r); +LA_UI_API bool are_selection_keys_down(const Registry& r); +LA_UI_API bool are_selection_keys_pressed(const Registry& r); +LA_UI_API bool are_selection_keys_released(const Registry& r); } // namespace ui diff --git a/modules/ui/include/lagrange/ui/utils/tools.h b/modules/ui/include/lagrange/ui/utils/tools.h index 6bf2cd13..4ce46c7e 100644 --- a/modules/ui/include/lagrange/ui/utils/tools.h +++ b/modules/ui/include/lagrange/ui/utils/tools.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include @@ -18,8 +19,8 @@ namespace lagrange { namespace ui { -Tools& get_tools(ui::Registry& r); -const Tools& get_tools(const ui::Registry& r); +LA_UI_API Tools& get_tools(ui::Registry& r); +LA_UI_API const Tools& get_tools(const ui::Registry& r); template @@ -29,22 +30,22 @@ bool is_element_type(entt::id_type elem_type) } /// Initialize Tools context variable -Tools& initialize_tools(ui::Registry& r); +LA_UI_API Tools& initialize_tools(ui::Registry& r); /// Run currently selected tool -void run_current_tool(ui::Registry& r); +LA_UI_API void run_current_tool(ui::Registry& r); /// Update previously used tool - must be called for is_tool_(de)activate to work -void update_previous_tool(ui::Registry& r); +LA_UI_API void update_previous_tool(ui::Registry& r); /// Was tool type activated this frame -bool is_tool_activated(const ui::Registry& r, entt::id_type tool_type); +LA_UI_API bool is_tool_activated(const ui::Registry& r, entt::id_type tool_type); /// Was tool type and element type activated this frame -bool is_tool_activated(const ui::Registry& r, entt::id_type tool_type, entt::id_type element_type); +LA_UI_API bool is_tool_activated(const ui::Registry& r, entt::id_type tool_type, entt::id_type element_type); /// Was tool type deactivated this frame -bool is_tool_deactivated(const ui::Registry& r, entt::id_type tool_type); +LA_UI_API bool is_tool_deactivated(const ui::Registry& r, entt::id_type tool_type); /// Was tool type and element type deactivated this frame -bool is_tool_deactivated( +LA_UI_API bool is_tool_deactivated( const ui::Registry& r, entt::id_type tool_type, entt::id_type element_type); diff --git a/modules/ui/include/lagrange/ui/utils/treenode.h b/modules/ui/include/lagrange/ui/utils/treenode.h index 5ae34715..18638339 100644 --- a/modules/ui/include/lagrange/ui/utils/treenode.h +++ b/modules/ui/include/lagrange/ui/utils/treenode.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include #include @@ -20,7 +21,7 @@ namespace lagrange { namespace ui { -Entity create_scene_node( +LA_UI_API Entity create_scene_node( Registry& r, const std::string& name = "Unnamed Scene Node Entity", Entity parent = NullEntity); @@ -30,43 +31,43 @@ Entity create_scene_node( /// @param r /// @param e /// @param recursive removes all children recursively -void remove(Registry& r, Entity e, bool recursive = false); +LA_UI_API void remove(Registry& r, Entity e, bool recursive = false); /// @brief Sets new_parent as as child's new parent. Both must have component. /// @param registry /// @param Entity child /// @param Entity new_parent -void set_parent(Registry& registry, Entity child, Entity new_parent); +LA_UI_API void set_parent(Registry& registry, Entity child, Entity new_parent); /// @brief Returns parent of e. Must have component. /// Returns NullEntity if e is top-level. /// @param registry /// @param e /// @return -Entity get_parent(const Registry& registry, Entity e); +LA_UI_API Entity get_parent(const Registry& registry, Entity e); /// @brief Returns all children of e. Must have component. /// Note: causes dynamic allocation. Use lagrange::ui::foreach_child to iterate children efficiently. /// @param registry /// @param e /// @return std::vector -std::vector get_children(const Registry& registry, Entity e); +LA_UI_API std::vector get_children(const Registry& registry, Entity e); /// @brief Returns true if child has no parents (is at top-level in the hierarachy) /// @param registry /// @param child -bool is_orphan(const Registry& registry, Entity child); +LA_UI_API bool is_orphan(const Registry& registry, Entity child); /// @brief Reparents child to be top-level (i.e., to have to parents). /// @param registry /// @param child -void orphan(Registry& registry, Entity child); +LA_UI_API void orphan(Registry& registry, Entity child); /// @brief Calls fn(Entity) on each direct child of parent entity /// @param registry /// @param parent parent entity with Tree component /// @param fn function to call, takes Entity parameter -void foreach_child( +LA_UI_API void foreach_child( const Registry& registry, const Entity parent, const std::function& fn); @@ -75,30 +76,30 @@ void foreach_child( /// @param registry /// @param parent parent entity with Tree component /// @param fn function to call, takes Entity parameter -void foreach_child_recursive( +LA_UI_API void foreach_child_recursive( const Registry& registry, const Entity parent, const std::function& fn); /// @brief Alternative to foreach_child_recursive -void iterate_inorder( +LA_UI_API void iterate_inorder( Registry& registry, const std::function& on_enter, const std::function& on_exit); -Entity group( +LA_UI_API Entity group( Registry& registry, const std::vector& entities, const std::string& name = "NewGroup"); -Entity group_under(Registry& registry, const std::vector& entities, Entity parent); +LA_UI_API Entity group_under(Registry& registry, const std::vector& entities, Entity parent); /// Returns new parent -Entity ungroup(Registry& registry, Entity parent, bool remove_parent = false); +LA_UI_API Entity ungroup(Registry& registry, Entity parent, bool remove_parent = false); /// Removes the node from tree and puts it as top-level node -void orphan_without_subtree(Registry& registry, Entity e); +LA_UI_API void orphan_without_subtree(Registry& registry, Entity e); } // namespace ui diff --git a/modules/ui/include/lagrange/ui/utils/uipanel.h b/modules/ui/include/lagrange/ui/utils/uipanel.h index ab393051..d1603572 100644 --- a/modules/ui/include/lagrange/ui/utils/uipanel.h +++ b/modules/ui/include/lagrange/ui/utils/uipanel.h @@ -11,11 +11,12 @@ */ #pragma once -#include +#include #include #include #include +#include #include #include @@ -25,8 +26,8 @@ namespace lagrange { namespace ui { -bool begin_panel(UIPanel& panel); -void end_panel(UIPanel& panel); +LA_UI_API bool begin_panel(UIPanel& panel); +LA_UI_API void end_panel(UIPanel& panel); /// @brief Adds window that executed given imgui_code. Imgui begin/end is called for you, do not @@ -35,9 +36,9 @@ void end_panel(UIPanel& panel); /// @param title /// @param imgui_code /// @return window entity -Entity add_panel(Registry& r, const std::string& title, const std::function& body_fn); +LA_UI_API Entity add_panel(Registry& r, const std::string& title, const std::function& body_fn); -Entity add_panel( +LA_UI_API Entity add_panel( Registry& r, const std::string& title, const std::function& body_fn, @@ -46,18 +47,18 @@ Entity add_panel( const std::function& menubar_fn = nullptr); -void toggle_panel(Registry& r, Entity e); +LA_UI_API void toggle_panel(Registry& r, Entity e); /// @brief Returns global window size /// @param registry /// @return -const WindowSize& get_window_size(const Registry& r); +LA_UI_API const WindowSize& get_window_size(const Registry& r); -MainMenuHeight get_menu_height(const Registry& r); +LA_UI_API MainMenuHeight get_menu_height(const Registry& r); -void hide_tab_bar(Registry& r, Entity uipanel_entity); +LA_UI_API void hide_tab_bar(Registry& r, Entity uipanel_entity); } // namespace ui diff --git a/modules/ui/include/lagrange/ui/utils/viewport.h b/modules/ui/include/lagrange/ui/utils/viewport.h index dafb23a9..a84cc25b 100644 --- a/modules/ui/include/lagrange/ui/utils/viewport.h +++ b/modules/ui/include/lagrange/ui/utils/viewport.h @@ -11,6 +11,7 @@ */ #pragma once +#include #include #include @@ -18,10 +19,10 @@ namespace lagrange { namespace ui { /// Creates an offscreen viewport with given camera. Create ViewportPanel to show it on screen. -Entity add_viewport(Registry& registry, Entity camera_entity, bool srgb = false); +LA_UI_API Entity add_viewport(Registry& registry, Entity camera_entity, bool srgb = false); -void instance_camera_to_viewports(Registry& registry, Entity source_viewport); -void copy_camera_to_viewports(Registry& registry, Entity source_viewport); +LA_UI_API void instance_camera_to_viewports(Registry& registry, Entity source_viewport); +LA_UI_API void copy_camera_to_viewports(Registry& registry, Entity source_viewport); /* Focused Viewport @@ -29,15 +30,15 @@ void copy_camera_to_viewports(Registry& registry, Entity source_viewport); /// Focused Viewport UI Panel /// Returns nullptr if there is no focused viewport -ViewportPanel* get_focused_viewport_panel(Registry& registry); +LA_UI_API ViewportPanel* get_focused_viewport_panel(Registry& registry); /// Focused Viewport (entity) /// Returns NullEntity if there is no focused viewport -Entity get_focused_viewport_entity(Registry& registry); +LA_UI_API Entity get_focused_viewport_entity(Registry& registry); /// Focused Viewport (component reference) /// Returns nullptr if there is no focused viewport -ViewportComponent* get_focused_viewport(Registry& registry); +LA_UI_API ViewportComponent* get_focused_viewport(Registry& registry); /* @@ -45,14 +46,14 @@ ViewportComponent* get_focused_viewport(Registry& registry); */ // Focused Camera (entity) /// Returns NullEntity if there is no focused viewport -Entity get_focused_camera_entity(Registry& registry); +LA_UI_API Entity get_focused_camera_entity(Registry& registry); // Focused Camera (component reference) /// Returns nullptr if there is no focused viewport -Camera* get_focused_camera(Registry& registry); +LA_UI_API Camera* get_focused_camera(Registry& registry); // Camera component of entity e -Camera& get_camera(Registry& registry, Entity e); +LA_UI_API Camera& get_camera(Registry& registry, Entity e); /* Hovered Viewport @@ -60,11 +61,11 @@ Camera& get_camera(Registry& registry, Entity e); // Hovered Viewport UI Panel (entity) /// Returns NullEntity if there is no hovered viewport -Entity get_hovered_viewport_panel_entity(Registry& registry); +LA_UI_API Entity get_hovered_viewport_panel_entity(Registry& registry); // Hovered Viewport (entity) /// Returns NullEntity if there is no hovered viewport -Entity get_hovered_viewport_entity(Registry& registry); +LA_UI_API Entity get_hovered_viewport_entity(Registry& registry); @@ -78,7 +79,7 @@ Entity get_hovered_viewport_entity(Registry& registry); /// @param duration_seconds duration in seconds /// @param filter /// Returns false if camera is not a valid entity -bool camera_focus_and_fit( +LA_UI_API bool camera_focus_and_fit( Registry& registry, Entity camera, bool focus = true, @@ -87,18 +88,18 @@ bool camera_focus_and_fit( const std::function& filter = nullptr); /// @brief Adjusts focused camera to fit the scene bounding box over the next several frames. -void camera_focus_and_fit(Registry& registry); +LA_UI_API void camera_focus_and_fit(Registry& registry); /* Internal offscreen viewport utilities */ -Entity get_selection_viewport_entity(const Registry& registry); -Entity get_objectid_viewport_entity(const Registry& registry); -void add_selection_outline_post_process( +LA_UI_API Entity get_selection_viewport_entity(const Registry& registry); +LA_UI_API Entity get_objectid_viewport_entity(const Registry& registry); +LA_UI_API void add_selection_outline_post_process( Registry& registry, Entity viewport_entity, const Color& selection_color); } // namespace ui -} // namespace lagrange \ No newline at end of file +} // namespace lagrange diff --git a/modules/ui/src/imgui/UIWidget.cpp b/modules/ui/src/imgui/UIWidget.cpp index f5b9d7f6..b2e7087e 100644 --- a/modules/ui/src/imgui/UIWidget.cpp +++ b/modules/ui/src/imgui/UIWidget.cpp @@ -187,11 +187,10 @@ bool UIWidget::operator()(Eigen::Affine3f& value) bool UIWidget::render_matrix(float* M, int dimension) { int ID = static_cast(reinterpret_cast(M)); - ImGui::BeginChildFrame( + ImGui::BeginChild( ID, - ImVec2( - ImGui::GetColumnWidth() - 17, - ImGui::GetTextLineHeightWithSpacing() * dimension + 5)); + ImVec2(ImGui::GetColumnWidth() - 17, ImGui::GetTextLineHeightWithSpacing() * dimension + 5), + ImGuiChildFlags_FrameStyle); ImGui::Columns(dimension); bool changed = false; @@ -205,7 +204,7 @@ bool UIWidget::render_matrix(float* M, int dimension) } ImGui::Columns(1); - ImGui::EndChildFrame(); + ImGui::EndChild(); return changed; } diff --git a/modules/ui/src/panels/KeybindsPanel.cpp b/modules/ui/src/panels/KeybindsPanel.cpp index e1f43e8c..b6a00c0c 100644 --- a/modules/ui/src/panels/KeybindsPanel.cpp +++ b/modules/ui/src/panels/KeybindsPanel.cpp @@ -143,10 +143,9 @@ void keybinds_panel_system(Registry& registry, Entity /*e*/) ImGui::ListBox( "Actions", &selected, - [](void* data, int idx, const char** out_text) { + [](void* data, int idx) -> const char* { auto& _items = *reinterpret_cast*>(data); - if (out_text) *out_text = _items[idx].c_str(); - return true; + return _items[idx].c_str(); }, &items, int(mapping.size())); diff --git a/modules/volume/CMakeLists.txt b/modules/volume/CMakeLists.txt index bd4ab3c2..d1cfae5a 100644 --- a/modules/volume/CMakeLists.txt +++ b/modules/volume/CMakeLists.txt @@ -10,7 +10,7 @@ # governing permissions and limitations under the License. # # 1. define module -lagrange_add_module() +lagrange_add_module(NO_INSTALL) # 2. dependencies lagrange_include_modules(core winding) @@ -21,13 +21,7 @@ target_link_libraries(lagrange_volume PUBLIC openvdb::openvdb ) -# 3. installation -# if(LAGRANGE_INSTALL) -# set_target_properties(lagrange_volume PROPERTIES EXPORT_NAME volume) -# lagrange_install(lagrange_volume) -# endif() - -# 4. unit tests and examples +# 3. unit tests and examples if(LAGRANGE_UNIT_TESTS) add_subdirectory(tests) endif() diff --git a/modules/volume/include/lagrange/volume/api.h b/modules/volume/include/lagrange/volume/api.h new file mode 100644 index 00000000..d13824d2 --- /dev/null +++ b/modules/volume/include/lagrange/volume/api.h @@ -0,0 +1,23 @@ +#pragma once + +#ifdef LA_VOLUME_STATIC_DEFINE + #define LA_VOLUME_API +#else + #ifndef LA_VOLUME_API + #ifdef lagrange_volume_EXPORTS + // We are building this library + #if defined(_WIN32) || defined(_WIN64) + #define LA_VOLUME_API __declspec(dllexport) + #else + #define LA_VOLUME_API __attribute__((visibility("default"))) + #endif + #else + // We are using this library + #if defined(_WIN32) || defined(_WIN64) + #define LA_VOLUME_API __declspec(dllimport) + #else + #define LA_VOLUME_API __attribute__((visibility("default"))) + #endif + #endif + #endif +#endif diff --git a/modules/winding/CMakeLists.txt b/modules/winding/CMakeLists.txt index 507348a3..c7391bd1 100644 --- a/modules/winding/CMakeLists.txt +++ b/modules/winding/CMakeLists.txt @@ -10,7 +10,7 @@ # governing permissions and limitations under the License. # # 1. define module -lagrange_add_module() +lagrange_add_module(NO_INSTALL) # 2. dependencies lagrange_include_modules(core) @@ -22,7 +22,7 @@ target_link_libraries(lagrange_winding WindingNumber::WindingNumber ) -# 4. unit tests and examples +# 3. unit tests and examples if(LAGRANGE_UNIT_TESTS) add_subdirectory(tests) endif() diff --git a/modules/winding/include/lagrange/winding/api.h b/modules/winding/include/lagrange/winding/api.h new file mode 100644 index 00000000..8b4feb6f --- /dev/null +++ b/modules/winding/include/lagrange/winding/api.h @@ -0,0 +1,23 @@ +#pragma once + +#ifdef LA_WINDING_STATIC_DEFINE + #define LA_WINDING_API +#else + #ifndef LA_WINDING_API + #ifdef lagrange_winding_EXPORTS + // We are building this library + #if defined(_WIN32) || defined(_WIN64) + #define LA_WINDING_API __declspec(dllexport) + #else + #define LA_WINDING_API __attribute__((visibility("default"))) + #endif + #else + // We are using this library + #if defined(_WIN32) || defined(_WIN64) + #define LA_WINDING_API __declspec(dllimport) + #else + #define LA_WINDING_API __attribute__((visibility("default"))) + #endif + #endif + #endif +#endif