diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0cff5604add6f8058e9a2d2398ea0540b0142e0d..b3333652b9f5e2f245a29dcaf37c199c5d783bdb 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -30,7 +30,7 @@ stages: # Windows #------------------------------------------------------------------------------- -# casual builds +# common builds conan:windows:msvc++14.1: stage: conan @@ -188,7 +188,184 @@ test:windows_nightly:msvc++14.1: # Linux #------------------------------------------------------------------------------- -# casual builds +# clang + +# common build + +conan:linux:clang: + stage: conan + except: + - schedules + tags: + - CentOS + - opengl + - clang + script: + - mkdir build + - cd build + - export CC=clang + - export CXX=clang++ + - conan install .. --build=missing -s compiler="clang" -s compiler.version="4.0" -s compiler.libcxx="libstdc++11" -s build_type=Release + artifacts: + paths: + - build + expire_in: 1 week + +cmake:linux:clang: + stage: cmake + except: + - schedules + tags: + - CentOS + - opengl + - clang + dependencies: + - conan:linux:clang + script: + - cd build + - export CC=clang + - export CXX=clang++ + - cmake .. -DIS_BUILD_SERVER:BOOL=TRUE -DCMAKE_BUILD_TYPE=Release + artifacts: + untracked: true + paths: + - build + expire_in: 1 week + +build:linux:clang: + stage: build + except: + - schedules + tags: + - CentOS + - opengl + - clang + dependencies: + - cmake:linux:clang + script: + - cd build + - export CC=clang + - export CXX=clang++ + - make Cpplint-Test-Suite + - make Cppcheck-Test-Suite + - make -j8 + artifacts: + paths: + - build + expire_in: 1 week + allow_failure: true + +test:linux:clang: + stage: test + except: + - schedules + tags: + - CentOS + - opengl + - clang + dependencies: + - build:linux:clang + script: + - cd build + - export CC=clang + - export CXX=clang++ + - make Unit-Test-Suite + +# nightly build + +clear:linux_nightly:clang: + stage: clear + only: + - schedules + tags: + - nightly + - clang + script: + - echo $CONAN_USER_HOME + - cd /home/gitlab-runner/dev + - rm -rf ./conan_nightly_clang + - mkdir conan_nightly_clang + +conan:linux_nightly:clang: + stage: conan + only: + - schedules + tags: + - nightly + - clang + script: + - conan remote add rwth-vr--bintray https://api.bintray.com/conan/rwth-vr/conan + - conan remote add bincrafters_public https://api.bintray.com/conan/bincrafters/public-conan + - mkdir build + - cd build + - export CC=clang + - export CXX=clang++ + - conan install .. --build=missing -s compiler="clang" -s compiler.version="4.0" -s compiler.libcxx="libstdc++11" -s build_type=Release + artifacts: + paths: + - build + expire_in: 1 week + +cmake:linux_nightly:clang: + stage: cmake + only: + - schedules + tags: + - nightly + - clang + dependencies: + - conan:linux_nightly:clang + script: + - cd build + - export CC=clang + - export CXX=clang++ + - cmake .. -DIS_BUILD_SERVER:BOOL=TRUE -DCMAKE_BUILD_TYPE=Release + artifacts: + untracked: true + paths: + - build + expire_in: 1 week + +build:linux_nightly:clang: + stage: build + only: + - schedules + tags: + - nightly + - clang + dependencies: + - cmake:linux_nightly:clang + script: + - cd build + - export CC=clang + - export CXX=clang++ + - make Cpplint-Test-Suite + - make Cppcheck-Test-Suite + - make -j8 + artifacts: + paths: + - build + expire_in: 1 week + allow_failure: true + +test:linux_nightly:clang: + stage: test + only: + - schedules + tags: + - nightly + - clang + dependencies: + - build:linux_nightly:clang + script: + - cd build + - export CC=clang + - export CXX=clang++ + - make Unit-Test-Suite + +# gcc + +# common builds conan:linux:gcc5.3.1: stage: conan diff --git a/cmake/Testing.cmake b/cmake/Testing.cmake index 36cf0f171ae76dfa442e590a7ef9c2c4beb462b2..47ea597015eee6a1577dfdde1cb4cff0c98e69d3 100644 --- a/cmake/Testing.cmake +++ b/cmake/Testing.cmake @@ -28,12 +28,122 @@ include(WarningLevels) conan_or_find_package(catch REQUIRED) conan_or_find_package(trompeloeil REQUIRED) +define_property(TARGET + PROPERTY testing__removes_libraries + BRIEF_DOCS "Libraries to be removed for mocking" + FULL_DOCS "Libraries to be removed for mocking") +define_property(TARGET + PROPERTY testing__adds_libraries + BRIEF_DOCS "Libraries to be added for mocking" + FULL_DOCS "Libraries to be added for mocking") + set_property(GLOBAL PROPERTY USE_FOLDERS ON) +set_property(GLOBAL PROPERTY TESTING__AVAILABLE_TESTS "") + +set(IS_BUILD_SERVER + FALSE CACHE BOOL + "Is this the build server? So we, e.g., simulate user input for tests requiring it." + ) + -set_property(GLOBAL PROPERTY ADDED_TESTS "") +function(CREATE_TEST_MAIN) + testing__add_library_(NAME test_main ${ARGV}) +endfunction() + + +function(CREATE_MOCK_MAIN) + testing__add_library_(NAME mock_main ${ARGV}) +endfunction() + + +function(TESTING__ADD_LIBRARY_) + set(options) + set(oneValueArgs NAME) + set(multiValueArgs SOURCES HEADERS INCLUDE_DIRECTORIES LIBRARIES ) + cmake_parse_arguments(TESTING__ADD_LIBRARY_ + "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + add_library(${TESTING__ADD_LIBRARY__NAME} + ${TESTING__ADD_LIBRARY__SOURCES} + ${TESTING__ADD_LIBRARY__HEADERS} + ) + target_include_directories(${TESTING__ADD_LIBRARY__NAME} + PUBLIC ${TESTING__ADD_LIBRARY__INCLUDE_DIRECTORIES} + ) + target_link_libraries(${TESTING__ADD_LIBRARY__NAME} + ${TESTING__ADD_LIBRARY__LIBRARIES} + ) + set_property(TARGET ${TESTING__ADD_LIBRARY__NAME} PROPERTY POSITION_INDEPENDENT_CODE ON) +endfunction() + + +function(ADD_TESTS) + set(options ) + set(oneValueArgs NAME) + set(multiValueArgs + SOURCES HEADERS INCLUDE_DIRECTORIES LIBRARIES PATH_TO_ADD) + cmake_parse_arguments(ADD_TESTS_ + "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + foreach(TEST_SOURCE_FILE ${ADD_TESTS__SOURCES}) + add_single_test( + NAME ${TEST_NAME} + SOURCE ${TEST_SOURCE_FILE} + INCLUDE_DIRECTORIES ${ADD_TESTS__INCLUDE_DIRECTORIES} + LIBRARIES ${ADD_TESTS__LIBRARIES} + PATH_TO_ADD ${ADD_TESTS__PATH_TO_ADD} + ) + endforeach() +endfunction() + + +function(ADD_SINGLE_TEST) + set(options ) + set(oneValueArgs) + set(multiValueArgs SOURCE INCLUDE_DIRECTORIES LIBRARIES PATH_TO_ADD) + cmake_parse_arguments(ADD_SINGLE_TEST_ + "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + get_filename_component( + TEST_NAME + ${ADD_SINGLE_TEST__SOURCE} NAME_WE) -function(CONFIGURE_MSVC_USERFILE TARGET_NAME PATH_TO_ADD) + add_executable(${TEST_NAME} + ${ADD_SINGLE_TEST__SOURCE} + ) + target_include_directories(${TEST_NAME} + PRIVATE ${ADD_SINGLE_TEST__INCLUDE_DIRECTORIES} + ) + target_link_libraries(${TEST_NAME} + ${ADD_SINGLE_TEST__LIBRARIES} + ${CONAN_OR_CMAKE_catch} + test_main mock_main + ) + set_warning_levels_RWTH(${TEST_NAME}) + if(IS_BUILD_SERVER) + target_compile_definitions(${TEST_NAME} PUBLIC -DIS_BUILD_SERVER) + endif() + set_property(TARGET ${TEST_NAME} + PROPERTY FOLDER "Tests") + source_group("Source Files" + FILES ${ADD_SINGLE_TEST__SOURCE}) + + if(ADD_SINGLE_TEST__PATH_TO_ADD AND WIN32 AND MSVC) + CONFIGURE_MSVC_USERFILE_(${TEST_NAME} + ${ADD_SINGLE_TEST__PATH_TO_ADD}) + endif() + + add_test(NAME ${TEST_NAME} + COMMAND ${TEST_NAME} + ) + testing__set_timeout_(${TEST_NAME}) + + set_property(GLOBAL APPEND PROPERTY + TESTING__AVAILABLE_TESTS "${TEST_NAME}") +endfunction() + + +function(CONFIGURE_MSVC_USERFILE_ TARGET_NAME PATH_TO_ADD) file(TO_NATIVE_PATH "${PATH_TO_ADD}/Release" _DLL_PATH_RELEASE) file(TO_NATIVE_PATH "${PATH_TO_ADD}/Debug" _DLL_PATH_DEBUG) set(SOURCE_USER_FILE @@ -43,132 +153,157 @@ function(CONFIGURE_MSVC_USERFILE TARGET_NAME PATH_TO_ADD) configure_file(${SOURCE_USER_FILE} ${DESTINATION_USER_FILE} @ONLY) endfunction() -function(ADD_TEST_TARGET_INTERNAL_ - NAME SOURCES HEADERS INCLUDE_DIRECTORIES LINK_LIBRARIES PATH_TO_ADD) - add_executable(${NAME} ${SOURCES} ${HEADERS}) - target_include_directories(${NAME} PRIVATE ${INCLUDE_DIRECTORIES}) - target_link_libraries(${NAME} ${LINK_LIBRARIES}) - target_link_libraries(${NAME} ${CONAN_OR_CMAKE_catch}) - - if(WIN32 AND MSVC) - CONFIGURE_MSVC_USERFILE(${NAME} ${PATH_TO_ADD}) - endif() -endfunction() -function(ADD_TEST_CATCH_INTERNAL_ - NAME SOURCES HEADERS) - - add_test(NAME ${NAME} COMMAND ${NAME}) - if(NOT ${NAME} MATCHES "integration") - set_tests_properties(${NAME} PROPERTIES TIMEOUT 10.0) +function(TESTING__SET_TIMEOUT_ TARGET) + if(NOT ${TARGET} MATCHES "integration") + set_tests_properties(${TARGET} PROPERTIES TIMEOUT 10.0) else() - set_tests_properties(${NAME} PROPERTIES TIMEOUT 120.0) + set_tests_properties(${TARGET} PROPERTIES TIMEOUT 120.0) endif() - set_warning_levels_RWTH(${NAME}) +endfunction() - set_property(TARGET ${NAME} PROPERTY FOLDER "Tests") - source_group("Source Files" FILES ${SOURCES} ${HEADERS}) - - set_property(GLOBAL APPEND PROPERTY ADDED_TESTS "${NAME}") - - if(IS_BUILD_SERVER) - target_compile_definitions(${NAME} PUBLIC -DIS_BUILD_SERVER) - endif() + +function(ADD_MOCK) + set(options ) + set(oneValueArgs NAME) + set(multiValueArgs + SOURCES + HEADERS + INCLUDE_DIRECTORIES + INCLUDE_DIRECTORIES_OF + COMPILE_OPTIONS + REMOVES_LIBRARIES + ADDS_LIBRARIES + ) + cmake_parse_arguments(ADD_MOCK_ + "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + string(TOUPPER ${ADD_MOCK__NAME} ADD_MOCK__NAME_UPPER) + string(TOLOWER ${ADD_MOCK__NAME} ADD_MOCK__NAME_LOWER) -endfunction() + add_library(${ADD_MOCK__NAME} SHARED + ${ADD_MOCK__SOURCES} + ${ADD_MOCK__HEADERS} + ) + target_link_libraries(${ADD_MOCK__NAME} + ${CONAN_OR_CMAKE_trompeloeil} + mock_main test_main + ) + foreach(CURRENT_TARGET ${ADD_MOCK__INCLUDE_DIRECTORIES_OF}) + get_target_property(CURRENT_INCLUCE_DIRECTORIES + ${CURRENT_TARGET} INTERFACE_INCLUDE_DIRECTORIES) + if (CURRENT_INCLUCE_DIRECTORIES) + target_include_directories(${ADD_MOCK__NAME} + PUBLIC ${CURRENT_INCLUCE_DIRECTORIES} + ) + endif() + endforeach() + target_include_directories(${ADD_MOCK__NAME} + PUBLIC ${ADD_MOCK__INCLUDE_DIRECTORIES} + ) + target_compile_definitions(${ADD_MOCK__NAME} + PRIVATE ${ADD_MOCK__NAME_UPPER}_BUILD + ) + target_compile_options(${ADD_MOCK__NAME} + PUBLIC ${ADD_MOCK__COMPILE_OPTIONS} + ) -function(ADD_TEST_INTERNAL_ - NAME SOURCES HEADERS INCLUDE_DIRECTORIES LINK_LIBRARIES PATH_TO_ADD) - ADD_TEST_TARGET_INTERNAL_("${NAME}" "${SOURCES}" "${HEADERS}" "${INCLUDE_DIRECTORIES}" "${LINK_LIBRARIES}" "${PATH_TO_ADD}") - ADD_TEST_CATCH_INTERNAL_("${NAME}" "${SOURCES}" "${HEADERS}") -endfunction() + set_property(TARGET ${ADD_MOCK__NAME} PROPERTY FOLDER "Tests/Mocks") + set_target_properties(${ADD_MOCK__NAME} + PROPERTIES testing__removes_libraries "${ADD_MOCK__REMOVES_LIBRARIES}") + set_target_properties(${ADD_MOCK__NAME} + PROPERTIES testing__adds_libraries "${ADD_MOCK__ADDS_LIBRARIES}") + generate_export_header(${ADD_MOCK__NAME} + EXPORT_FILE_NAME ${CMAKE_CURRENT_BINARY_DIR}/mocks/${ADD_MOCK__NAME_LOWER}_export.hpp + ) + set_warning_levels_rwth(${ADD_MOCK__NAME}) +endfunction() -function(CREATE_CATCH_MAIN_INTERNAL_ - NAME SOURCE INCLUDE_DIRECTORIES) - add_library(${NAME} ${SOURCE}) - target_include_directories(${NAME} PRIVATE ${INCLUDE_DIRECTORIES}) - target_link_libraries(${NAME} ${CONAN_OR_CMAKE_catch} ${CONAN_OR_CMAKE_trompeloeil}) - set_property(TARGET ${NAME} PROPERTY FOLDER "Tests") - source_group("Source Files" FILES ${SOURCE}) +function(AUTOREMOVE_MOCKED_TEST_SOURCE_FROM) + set_property(GLOBAL PROPERTY TESTING__SOURCES__AUTOREMOVE_MOCKED ${ARGN}) endfunction() +function(GET_UNMOCKED_TEST_SOURCES test_list) + get_property(TEST_SOURCES_LIST + GLOBAL PROPERTY TESTING__SOURCES__AUTOREMOVE_MOCKED) + set(${test_list} ${TEST_SOURCES_LIST} PARENT_SCOPE) +endfunction() -function(ADD_TEST_CATCH) - # parse arguments +function(add_mocked_test cpp_file) set(options ) - set(oneValueArgs NAME CATCH_MAIN) - set(multiValueArgs - SOURCES HEADERS INCLUDE_DIRECTORIES LINK_LIBRARIES PATH_TO_ADD) - cmake_parse_arguments(ADD_TEST_CATCH + set(oneValueArgs ) + set(multiValueArgs LIBRARIES MOCKING_LIBRARIES_OF MOCKS) + cmake_parse_arguments(ADD_MOCKED_TEST "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - create_catch_main_internal_("${ADD_TEST_CATCH_NAME}_catch_main" - ${ADD_TEST_CATCH_CATCH_MAIN} - ${ADD_TEST_CATCH_INCLUDE_DIRECTORIES} - ) - - # remove catch_main file from sources - file(GLOB ADD_TEST_CATCH_CATCH_MAIN_ABSOLUTE ${ADD_TEST_CATCH_CATCH_MAIN}) - list(REMOVE_ITEM ADD_TEST_CATCH_SOURCES ${ADD_TEST_CATCH_CATCH_MAIN_ABSOLUTE}) - - # add test for each test source file - foreach(TEST_SOURCE_FILE ${ADD_TEST_CATCH_SOURCES}) - get_filename_component(TEST_NAME ${TEST_SOURCE_FILE} NAME_WE) - ADD_TEST_INTERNAL_("${TEST_NAME}" - "${TEST_SOURCE_FILE}" - "" - "${ADD_TEST_CATCH_INCLUDE_DIRECTORIES}" - "${ADD_TEST_CATCH_LINK_LIBRARIES};${ADD_TEST_CATCH_NAME}_catch_main" - "${ADD_TEST_CATCH_PATH_TO_ADD}" + + get_property(TEST_SOURCES_LIST + GLOBAL PROPERTY TESTING__SOURCES__AUTOREMOVE_MOCKED) + list(REMOVE_ITEM TEST_SOURCES_LIST + ${CMAKE_CURRENT_SOURCE_DIR}/src/${cpp_file}.cpp) + set_property(GLOBAL PROPERTY TESTING__SOURCES__AUTOREMOVE_MOCKED ${TEST_SOURCES_LIST}) + + set(test_sources src/${cpp_file}.cpp) + add_single_test( + SOURCE ${test_sources} ) - endforeach() -endfunction() + add_dependencies(${cpp_file} ${ADD_MOCKED_TEST_LIBRARIES}) + + set(LINK_LIBRARIES ) + foreach(CURR_TARGET ${ADD_MOCKED_TEST_LIBRARIES}) + get_target_property(CURR_LINK_LIBRARIES ${CURR_TARGET} LINK_LIBRARIES) + list(APPEND LINK_LIBRARIES ${CURR_LINK_LIBRARIES}) -macro(EXTRACT_UNIT_AND_INTEGRATION_TEST_TARGETS) - get_property(added_tests GLOBAL PROPERTY ADDED_TESTS) - foreach(filename ${added_tests}) - if(${filename} MATCHES "integration_test") - message(STATUS "Integration Test: ${filename}") - set(INTEGRATION_TEST_TARGETS ${INTEGRATION_TEST_TARGETS} ${filename}) - else() - message(STATUS "Unit Test: ${filename}") - set(UNIT_TEST_TARGETS ${UNIT_TEST_TARGETS} ${filename}) - endif() + target_link_libraries(${cpp_file} $<TARGET_FILE:${CURR_TARGET}>) + + get_target_property(CURR_INCLUDE_DIRECTORIES + ${CURR_TARGET} INCLUDE_DIRECTORIES) + target_include_directories(${cpp_file} PRIVATE ${CURR_INCLUDE_DIRECTORIES}) + endforeach() + list(REMOVE_DUPLICATES LINK_LIBRARIES) + #the libraries themselves must not be in LINK_LIBRARIES + foreach(CURR_TARGET ${ADD_MOCKED_TEST_LIBRARIES}) + list(REMOVE_ITEM LINK_LIBRARIES ${CURR_TARGET}) endforeach() -endmacro() -macro(ADD_TEST_SUITE) - set(options) - set(oneValueArgs NAME TEST_REGEX) - set(multiValueArgs DEPEND_ON_TARGETS) - cmake_parse_arguments(ADD_TEST_SUITE - "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - #we have to escape the " for visual studio) - add_custom_target(${ADD_TEST_SUITE_NAME} COMMAND ctest --tests-regex \"${ADD_TEST_SUITE_TEST_REGEX}\" -C $<CONFIG>) - # This comment just ends the escaped signs, for VS highlighting " - if(ADD_TEST_SUITE_DEPEND_ON_TARGETS) - add_dependencies(${ADD_TEST_SUITE_NAME} ${ADD_TEST_SUITE_DEPEND_ON_TARGETS}) - endif() - set_property(TARGET ${ADD_TEST_SUITE_NAME} PROPERTY FOLDER "_Test-Suites_") -endmacro() + foreach(MOCK ${ADD_MOCKED_TEST_MOCKS}) + get_target_property(LIBRARIES_TO_BE_REMOVED ${MOCK} testing__removes_libraries) + foreach(LIBRARY_TO_BE_REMOVED ${LIBRARIES_TO_BE_REMOVED}) + list(REMOVE_ITEM LINK_LIBRARIES ${LIBRARY_TO_BE_REMOVED}) + endforeach() + + get_target_property(LIBRARIES_TO_BE_ADDED ${MOCK} testing__adds_libraries) + foreach(LIBRARY_TO_BE_ADDED ${LIBRARIES_TO_BE_ADDED}) + list(APPEND LINK_LIBRARIES ${LIBRARY_TO_BE_ADDED}) + endforeach() + target_link_libraries(${cpp_file} ${MOCK}) + endforeach() + + target_link_libraries(${cpp_file} + ${CONAN_OR_CMAKE_trompeloeil} + ${CONAN_OR_CMAKE_catch} + ${LINK_LIBRARIES} + ) +endfunction() +#------------------------------------------------------------------------------- function(CREATE_TEST_SUITES) - EXTRACT_UNIT_AND_INTEGRATION_TEST_TARGETS() + testing__extract_unit_and_integration_test_targets_() ADD_TEST_SUITE( NAME "Unit-Test-Suite" TEST_REGEX "^test" - DEPEND_ON_TARGETS ${UNIT_TEST_TARGETS}) + DEPEND_ON_TARGETS ${TESTING__UNIT_TESTS}) ADD_TEST_SUITE( NAME "Integration-Test-Suite" TEST_REGEX "^integration" - DEPEND_ON_TARGETS ${INTEGRATION_TEST_TARGETS}) + DEPEND_ON_TARGETS ${TESTING__INTEGRATION_TESTS}) ADD_TEST_SUITE( NAME "Cpplint-Test-Suite" @@ -178,3 +313,36 @@ function(CREATE_TEST_SUITES) NAME "Cppcheck-Test-Suite" TEST_REGEX "cppcheck") endfunction() + + +function(ADD_TEST_SUITE) + set(options) + set(oneValueArgs NAME TEST_REGEX) + set(multiValueArgs DEPEND_ON_TARGETS) + cmake_parse_arguments(ADD_TEST_SUITE + "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + #we have to escape the " for visual studio) + add_custom_target(${ADD_TEST_SUITE_NAME} COMMAND ctest --tests-regex \"${ADD_TEST_SUITE_TEST_REGEX}\" -C $<CONFIG>) + # This comment just ends the escaped signs, for VS highlighting " + if(ADD_TEST_SUITE_DEPEND_ON_TARGETS) + add_dependencies(${ADD_TEST_SUITE_NAME} ${ADD_TEST_SUITE_DEPEND_ON_TARGETS}) + endif() + set_property(TARGET ${ADD_TEST_SUITE_NAME} PROPERTY FOLDER "_Test-Suites_") +endfunction() + + +function(TESTING__EXTRACT_UNIT_AND_INTEGRATION_TEST_TARGETS_) + set(TESTING__INTEGRATION_TESTS ) + set(TESTING__UNIT_TESTS ) + + get_property(TESTING__AVAILABLE_TESTS GLOBAL PROPERTY TESTING__AVAILABLE_TESTS) + foreach(CURRENT_TEST ${TESTING__AVAILABLE_TESTS}) + if(${CURRENT_TEST} MATCHES "integration_test") + list(APPEND TESTING__INTEGRATION_TESTS ${CURRENT_TEST}) + else() + list(APPEND TESTING__UNIT_TESTS ${CURRENT_TEST}) + endif() + endforeach() +endfunction() + diff --git a/cmake/WarningLevels.cmake b/cmake/WarningLevels.cmake index 56a8e549bb1067c10917f306c3ef5e7b68d45396..cfe1929d1499691c9051370dd51170a008947a62 100644 --- a/cmake/WarningLevels.cmake +++ b/cmake/WarningLevels.cmake @@ -34,6 +34,7 @@ set(WARNING_LEVELS_RWTH_CLANG -Wno-weak-vtables -Wno-exit-time-destructors -Wno-global-constructors + -Wno-padded ) set(WARNING_LEVELS_RWTH_GCC diff --git a/cmake/cppcheck.cmake b/cmake/cppcheck.cmake index 0f0416429527034f4cb413afef17df299c27f293..686a8abcdc7132a7208ec6163aaed78d9768d5c5 100644 --- a/cmake/cppcheck.cmake +++ b/cmake/cppcheck.cmake @@ -36,7 +36,7 @@ else() message(STATUS "Use cppcheck from: ${CPPCHECK_COMMAND}") endif() -set(CPPCHECK_ARGUMENTS --enable=warning,performance,portability,missingInclude,unusedFunction,style --error-exitcode=1 --quiet --verbose) +set(CPPCHECK_ARGUMENTS --enable=warning,performance,portability,missingInclude,style --suppress=unusedFunction --error-exitcode=1 --quiet --verbose) if(MSVC) list(APPEND CPPCHECK_ARGUMENTS --template=vs) elseif(CLANG) diff --git a/cmake/cpplint.cmake b/cmake/cpplint.cmake index 9342399d3f15c119b3fd1fddeb4fe741fa8dc999..30f125900088e662e140b531bbd9804c3b373ec8 100644 --- a/cmake/cpplint.cmake +++ b/cmake/cpplint.cmake @@ -77,5 +77,5 @@ function(ADD_TEST_CPPLINT) COMMAND ${PYTHON_EXECUTABLE} ${CPPLINT_COMMAND} ${CPPLINT_OUTPUT} ${CPPLINT_ARGS} ${ADD_TEST_CPPLINT_UNPARSED_ARGUMENTS} ) - set_tests_properties(${ADD_TEST_CPPLINT_NAME} PROPERTIES TIMEOUT 20.0) + set_tests_properties(${ADD_TEST_CPPLINT_NAME} PROPERTIES TIMEOUT 100.0) endfunction() diff --git a/cmake/suppress_warnings.hpp.in b/cmake/suppress_warnings.hpp.in index 5a9ae8a7c8ad2d395ce944f1a859c6d09c11b93c..ddd2a44b761befa44ce4e8096990ea918503ce1b 100644 --- a/cmake/suppress_warnings.hpp.in +++ b/cmake/suppress_warnings.hpp.in @@ -40,10 +40,8 @@ _Pragma("clang diagnostic ignored \"-Wsign-conversion\"") \ _Pragma("clang diagnostic ignored \"-Wnewline-eof\"") \ _Pragma("clang diagnostic ignored \"-Wnon-virtual-dtor\"") \ + _Pragma("clang diagnostic ignored \"-Wzero-as-null-pointer-constant\"") \ _Pragma("clang diagnostic ignored \"-Wextra-semi\"") -#define SUPPRESS_WARNINGS_BEGIN_PADDED \ - _Pragma("clang diagnostic push") \ - _Pragma("clang diagnostic ignored \"-Wpadded\"") #define SUPPRESS_WARNINGS_BEGIN_MISSING_DECLARATIONS \ _Pragma("clang diagnostic push") \ _Pragma("clang diagnostic ignored \"-Wmissing-prototypes\"") @@ -51,7 +49,6 @@ #elif defined _MSC_VER #define SUPPRESS_WARNINGS_BEGIN __pragma(warning(push, 0)); -#define SUPPRESS_WARNINGS_BEGIN_PADDED __pragma(warning(push)); #define SUPPRESS_WARNINGS_BEGIN_MISSING_DECLARATIONS __pragma(warning(push)); #define SUPPRESS_WARNINGS_END __pragma(warning(pop)); @@ -69,9 +66,6 @@ _Pragma("GCC diagnostic ignored \"-Wextra\"") \ _Pragma("GCC diagnostic ignored \"-Wmissing-declarations\"") \ _Pragma("GCC diagnostic ignored \"-Wpadded\"") -#define SUPPRESS_WARNINGS_BEGIN_PADDED \ - _Pragma("GCC diagnostic push") \ - _Pragma("GCC diagnostic ignored \"-Wpadded\"") #define SUPPRESS_WARNINGS_BEGIN_MISSING_DECLARATIONS \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wmissing-declarations\"") diff --git a/conanfile.py b/conanfile.py index f76578b8f71b5b0ee45153a53fd986c97e7dc5d7..60af502d5c97d0c1c252ab90c5351b1822132d22 100644 --- a/conanfile.py +++ b/conanfile.py @@ -24,7 +24,7 @@ from conans import ConanFile, CMake class ProjectPhoenix(ConanFile): name = "phx" - version = "18.05.0" + version = "18.06.0" license = "3-Clause BSD License" description = """Project Phoenix""" settings = "os", "compiler", "build_type", "arch" @@ -33,14 +33,14 @@ class ProjectPhoenix(ConanFile): ("boost_optional/1.66.0@bincrafters/testing"), ("boost_signals2/1.66.0@bincrafters/testing"), #still not working in stable, change whenever available (also boost_optional) ("catch/1.12.0@RWTH-VR/thirdparty"), - ("cppcheck/1.82@RWTH-VR/thirdparty"), + ("cppcheck/1.84@RWTH-VR/thirdparty"), ("cpplint/e8ffd7c@RWTH-VR/thirdparty"), ("freeimage/3.17.0_2@RWTH-VR/thirdparty"), ("gl/1.1.1@RWTH-VR/thirdparty"), ("glm/0.9.8.5@g-truc/stable"), ("jsonformoderncpp/3.0.1@vthiery/stable"), ("openvr/1.0.12@RWTH-VR/thirdparty"), - ("sdl2/2.0.7@bincrafters/stable"), + ("sdl2/2.0.8@bincrafters/stable"), ("spdlog/0.16.3@bincrafters/stable"), ("trompeloeil/v29@rollbear/stable")) generators = "cmake" @@ -67,6 +67,6 @@ class ProjectPhoenix(ConanFile): self.copy("*.dll", dst="demos/combustion_demo/Debug", src="bin") self.copy("*.dll", dst="demos/combustion_demo/Release", src="bin") self.copy("*.so", dst="lib", src="lib") - self.copy("*.so*", dst="lib", src="lib") + self.copy("*.so.*", dst="lib", src="lib") self.copy("*.dylib", dst="lib", src="lib") self.copy("*.dylib", dst="tests", src="lib") diff --git a/demos/combustion_demo/src/combustion_demo.cpp b/demos/combustion_demo/src/combustion_demo.cpp index ab98cf21304a2b4c89fb7af0d7c128616598acde..1703a2e915ec08804cb3b5925d32dc6d0890735c 100644 --- a/demos/combustion_demo/src/combustion_demo.cpp +++ b/demos/combustion_demo/src/combustion_demo.cpp @@ -33,24 +33,27 @@ #include "phx/core/runtime_component.hpp" #include "phx/display/display_system_openvr.hpp" #include "phx/display/display_system_window.hpp" +#include "phx/input/device_system.hpp" #include "phx/input/input_system.hpp" #include "phx/rendering/auxiliary/splash_screen.hpp" #include "phx/rendering/components/mesh_handle.hpp" +#include "phx/rendering/components/mesh_render_settings.hpp" #include "phx/resources/loaders/assimp_model_loader.hpp" #include "phx/resources/loaders/scene_loader.hpp" #include "phx/setup.hpp" -#include "vr_controller_navigation_behavior.hpp" +#include "vr_controller_interaction_behavior.hpp" #if defined __clang__ #pragma clang diagnostic ignored "-Wmissing-prototypes" #endif int main(int, char**) { - std::unique_ptr<phx::Engine> engine = phx::Setup::CreateDefaultEngine(false); + std::unique_ptr<phx::Engine> engine = phx::Setup::CreateDefaultEngine(true); auto scene = engine->GetScene(); auto rendering_system = engine->GetSystem<phx::RenderingSystem>(); rendering_system->SetEnabled(false); auto openvr_system = engine->GetSystem<phx::DisplaySystemOpenVR>(); + auto device_system = engine->GetSystem<phx::DeviceSystem>(); phx::SplashScreen* splash = engine->CreateSystem<phx::SplashScreen>( engine->GetSystem<phx::DisplaySystemWindow>()->GetWindow()); @@ -69,15 +72,17 @@ int main(int, char**) { if (key == 'q') engine->Stop(); }); - auto virtual_platform = scene->GetEntitiesWithComponents< - phx::RuntimeComponent<phx::USER_PLATFORM>>()[0]; - // virtual_platform->AddComponent<DesktopNavigationBehavior>( - // engine->GetSystem<phx::InputSystem>()); - auto controller_navigation_behavior = - virtual_platform->AddComponent<VRControllerNavigationBehavior>( - openvr_system); + auto right_controller_entities = scene->GetEntitiesWithComponents< + phx::RuntimeComponent<phx::RIGHT_CONTROLLER>>(); + VRControllerInteractionBehavior* right_interaction_behavior = nullptr; + if (right_controller_entities.size() >= 1) { + auto right_controller_entity = right_controller_entities[0]; + right_interaction_behavior = + right_controller_entity->AddComponent<VRControllerInteractionBehavior>( + device_system); + } - auto handle = std::async([&controller_navigation_behavior, &scene, + auto handle = std::async([&right_interaction_behavior, &scene, rendering_system, splash, input_system, openvr_system]() { auto model_surface_entity = phx::SceneLoader::InsertModelIntoScene( @@ -98,10 +103,13 @@ int main(int, char**) { "models/combustion/data_box.stl", scene.get()); auto boundingbox_transform = model_boundingbox->GetFirstComponent<phx::Transform>(); - auto boundingbox_mesh_handle = boundingbox_transform->GetChild(0) - ->GetEntity() - ->GetFirstComponent<phx::MeshHandle>(); - boundingbox_mesh_handle->SetWireframeMode(true); + auto boundigbox_mesh_entity = + boundingbox_transform->GetChild(0)->GetEntity(); + auto boundingbox_mesh_handle = + boundigbox_mesh_entity->GetFirstComponent<phx::MeshHandle>(); + auto render_settings = + boundigbox_mesh_entity->AddComponent<phx::MeshRenderSettings>(); + render_settings->SetWireframeMode(true); std::array<glm::vec3, 2> bbox = boundingbox_mesh_handle->GetMesh()->GetBoundingBox(); @@ -130,7 +138,8 @@ int main(int, char**) { glm::vec3(1.0f, 1.0f, 1.0f)); floor_material->GetMaterial()->SetAmbientColor(glm::vec3(0.1f, 0.1f, 0.1f)); - controller_navigation_behavior->SetTarget(vis_root_transform); + if (right_interaction_behavior) + right_interaction_behavior->SetTarget(vis_root_transform); auto desk_entity = phx::SceneLoader::InsertModelIntoScene( "models/cube/cube2.obj", scene.get()); @@ -196,6 +205,8 @@ int main(int, char**) { if (!openvr_system) { camera->AddComponent<DesktopNavigationBehavior>(input_system); + camera->GetFirstComponent<phx::Transform>()->Translate( + glm::vec3(0.0f, 1.0f, 1.0f)); } engine->Run(); diff --git a/demos/combustion_demo/src/vr_controller_interaction_behavior.cpp b/demos/combustion_demo/src/vr_controller_interaction_behavior.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6d113a15013a3ef318979fae5e1fbf739d8451ae --- /dev/null +++ b/demos/combustion_demo/src/vr_controller_interaction_behavior.cpp @@ -0,0 +1,105 @@ +//------------------------------------------------------------------------------ +// Project Phoenix +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualization Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the 3-Clause BSD License (the "License"); +// you may not use this file except in compliance with the License. +// See the file LICENSE for the full text. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include "vr_controller_interaction_behavior.hpp" + +#include <vector> + +#include "phx/suppress_warnings.hpp" + +SUPPRESS_WARNINGS_BEGIN +#include "glm/detail/type_vec3.hpp" +#include "glm/glm.hpp" +#include "glm/gtc/matrix_access.hpp" +SUPPRESS_WARNINGS_END + +#include "phx/core/engine.hpp" +#include "phx/core/entity.hpp" +#include "phx/core/runtime_component.hpp" +#include "phx/core/scene.hpp" +#include "phx/display/display_system_openvr.hpp" +#include "phx/display/hmd.hpp" +#include "phx/rendering/components/transform.hpp" + +VRControllerInteractionBehavior::VRControllerInteractionBehavior( + phx::DeviceSystem* device_system) + : device_system_(device_system) {} + +void VRControllerInteractionBehavior::OnButtonSignal( + phx::VRController::ButtonId id, phx::VRController::ButtonEvent event) { + const auto transform = GetEntity()->GetFirstComponent<phx::Transform>(); + if (target_ != nullptr) { + if (event == phx::VRController::BUTTON_PRESSED && + id == vr::EVRButtonId::k_EButton_SteamVR_Trigger) { + if (transform != nullptr) + target_->SetParent(GetEntity()->GetFirstComponent<phx::Transform>()); + } + if (event == phx::VRController::BUTTON_RELEASED && + id == vr::EVRButtonId::k_EButton_SteamVR_Trigger) { + target_->SetParent(nullptr); + } + } +} + +void VRControllerInteractionBehavior::OnUpdate() { + if (side_ == phx::VRController::INVALID_CONTROLLER) { + RegisterOnDeviceSignal(); + } +} + +void VRControllerInteractionBehavior::SetTarget(phx::Transform* target) { + target_ = target; +} + +phx::Transform* VRControllerInteractionBehavior::GetTarget() const { + return target_; +} + +void VRControllerInteractionBehavior::RegisterOnDeviceSignal() { + if (GetEntity() + ->GetFirstComponent<phx::RuntimeComponent<phx::LEFT_CONTROLLER>>() != + nullptr) + side_ = phx::VRController::LEFT_CONTROLLER; + else if (GetEntity() + ->GetFirstComponent< + phx::RuntimeComponent<phx::RIGHT_CONTROLLER>>() != nullptr) + side_ = phx::VRController::RIGHT_CONTROLLER; + else + phx::warn( + "Added VRControllerInteractionBehavior to a non-controller entity"); + + phx::VRController* controller = nullptr; + for (auto cont : device_system_->GetDevices<phx::VRController>()) { + if (cont->GetSide() == side_) { + controller = cont; + break; + } + } + + if (controller != nullptr) { + controller->RegisterButtonSignal( + [this](phx::VRController::ButtonId id, + phx::VRController::ButtonEvent event) { + this->OnButtonSignal(id, event); + }); + } +} diff --git a/demos/combustion_demo/src/vr_controller_interaction_behavior.hpp b/demos/combustion_demo/src/vr_controller_interaction_behavior.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c66c364edaf9e77ec3f5dd6938facee53789c175 --- /dev/null +++ b/demos/combustion_demo/src/vr_controller_interaction_behavior.hpp @@ -0,0 +1,62 @@ +//------------------------------------------------------------------------------ +// Project Phoenix +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualization Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the 3-Clause BSD License (the "License"); +// you may not use this file except in compliance with the License. +// See the file LICENSE for the full text. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#ifndef DEMOS_COMBUSTION_DEMO_SRC_VR_CONTROLLER_INTERACTION_BEHAVIOR_HPP_ +#define DEMOS_COMBUSTION_DEMO_SRC_VR_CONTROLLER_INTERACTION_BEHAVIOR_HPP_ + +#include "phx/input/device_system.hpp" +#include "phx/input/vr_controller.hpp" +#include "phx/rendering/components/transform.hpp" +#include "phx/scripting/behavior.hpp" + +class VRControllerInteractionBehavior : public phx::Behavior { + public: + explicit VRControllerInteractionBehavior(phx::DeviceSystem* device_system); + VRControllerInteractionBehavior(const VRControllerInteractionBehavior& that) = + default; + VRControllerInteractionBehavior(VRControllerInteractionBehavior&& temp) = + default; + virtual ~VRControllerInteractionBehavior() = default; + VRControllerInteractionBehavior& operator=( + const VRControllerInteractionBehavior& that) = default; + VRControllerInteractionBehavior& operator=( + VRControllerInteractionBehavior&& temp) = default; + + void OnUpdate() override; + void OnButtonSignal(phx::VRController::ButtonId id, + phx::VRController::ButtonEvent event); + + void SetTarget(phx::Transform* target); + phx::Transform* GetTarget() const; + + protected: + phx::DeviceSystem* device_system_; + + private: + void RegisterOnDeviceSignal(); + + phx::Transform* target_ = nullptr; + phx::VRController::ControllerSide side_ = + phx::VRController::INVALID_CONTROLLER; +}; + +#endif // DEMOS_COMBUSTION_DEMO_SRC_VR_CONTROLLER_INTERACTION_BEHAVIOR_HPP_ diff --git a/demos/combustion_demo/src/vr_controller_navigation_behavior.cpp b/demos/combustion_demo/src/vr_controller_navigation_behavior.cpp deleted file mode 100644 index 79d59837cba7a58dc44d2e46f68ee043c61af330..0000000000000000000000000000000000000000 --- a/demos/combustion_demo/src/vr_controller_navigation_behavior.cpp +++ /dev/null @@ -1,97 +0,0 @@ -//------------------------------------------------------------------------------ -// Project Phoenix -// -// Copyright (c) 2017-2018 RWTH Aachen University, Germany, -// Virtual Reality & Immersive Visualization Group. -//------------------------------------------------------------------------------ -// License -// -// Licensed under the 3-Clause BSD License (the "License"); -// you may not use this file except in compliance with the License. -// See the file LICENSE for the full text. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/BSD-3-Clause -// -// 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 CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -//------------------------------------------------------------------------------ - -#include "vr_controller_navigation_behavior.hpp" - -#include <vector> - -#include "phx/suppress_warnings.hpp" - -SUPPRESS_WARNINGS_BEGIN -#include "glm/detail/type_vec3.hpp" -#include "glm/glm.hpp" -#include "glm/gtc/matrix_access.hpp" -SUPPRESS_WARNINGS_END - -#include "phx/core/engine.hpp" -#include "phx/core/entity.hpp" -#include "phx/core/runtime_component.hpp" -#include "phx/core/scene.hpp" -#include "phx/display/display_system_openvr.hpp" -#include "phx/display/hmd.hpp" -#include "phx/rendering/components/transform.hpp" - -VRControllerNavigationBehavior::VRControllerNavigationBehavior( - phx::DisplaySystemOpenVR* display_system_openvr) - : display_system_openvr_(display_system_openvr) {} - -void VRControllerNavigationBehavior::OnUpdate() { - // If there exists an HMD and a transform. - phx::HMD* hmd = nullptr; - if (display_system_openvr_ != nullptr) { - hmd = display_system_openvr_->GetHMD(); - } - const auto transform = GetEntity()->GetFirstComponent<phx::Transform>(); - if (hmd == nullptr || transform == nullptr) return; - - auto indices = hmd->GetControllerIndices(); - - for (auto i = 0u; i < indices.size(); ++i) { - vr::VRControllerState_t controller_state; - vr::VRSystem()->GetControllerState(indices[i], &controller_state, - sizeof controller_state); - - // Set the transform based on whether the trigger is pressed. - if (controller_state.ulButtonTouched & - vr::ButtonMaskFromId(vr::EVRButtonId::k_EButton_SteamVR_Trigger)) - transform->Translate( - -0.05F * glm::column(hmd->GetRightControllerTransformation(), 2)); - - if (target_) { - if (!is_grip_down_ && - controller_state.ulButtonPressed & - vr::ButtonMaskFromId(vr::EVRButtonId::k_EButton_Grip)) { - target_->SetParent( - GetEntity() - ->GetScene() - ->GetEntitiesWithComponents< - phx::RuntimeComponent<phx::RIGHT_CONTROLLER>>()[0] - ->GetFirstComponent<phx::Transform>()); - is_grip_down_ = true; - } else if (is_grip_down_ && - controller_state.ulButtonPressed & - vr::ButtonMaskFromId( - vr::EVRButtonId::k_EButton_SteamVR_Touchpad)) { - target_->SetParent(nullptr); - is_grip_down_ = false; - } - } - } -} - -void VRControllerNavigationBehavior::SetTarget(phx::Transform* target) { - target_ = target; -} - -phx::Transform* VRControllerNavigationBehavior::GetTarget() const { - return target_; -} diff --git a/demos/viewer/src/navigation_behavior.cpp b/demos/viewer/src/navigation_behavior.cpp deleted file mode 100644 index e9c914fd4d8658c03d65d1362203764739d3e2ed..0000000000000000000000000000000000000000 --- a/demos/viewer/src/navigation_behavior.cpp +++ /dev/null @@ -1,72 +0,0 @@ -//------------------------------------------------------------------------------ -// Project Phoenix -// -// Copyright (c) 2017-2018 RWTH Aachen University, Germany, -// Virtual Reality & Immersive Visualization Group. -//------------------------------------------------------------------------------ -// License -// -// Licensed under the 3-Clause BSD License (the "License"); -// you may not use this file except in compliance with the License. -// See the file LICENSE for the full text. -// You may obtain a copy of the License at -// -// https://opensource.org/licenses/BSD-3-Clause -// -// 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 CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -//------------------------------------------------------------------------------ - -#include "navigation_behavior.hpp" - -#include <vector> - -#include "phx/suppress_warnings.hpp" - -SUPPRESS_WARNINGS_BEGIN -#include "glm/detail/type_vec3.hpp" -#include "glm/glm.hpp" -#include "glm/gtc/matrix_access.hpp" -SUPPRESS_WARNINGS_END - -#include "phx/core/engine.hpp" -#include "phx/core/entity.hpp" -#include "phx/core/scene.hpp" -#include "phx/display/display_system_openvr.hpp" -#include "phx/display/hmd.hpp" -#include "phx/rendering/components/transform.hpp" - -NavigationBehavior::NavigationBehavior( - phx::DisplaySystemOpenVR* display_system_openvr) - : display_system_openvr_(display_system_openvr) {} - -void NavigationBehavior::OnUpdate() { - // If there exists an HMD and a transform. - phx::HMD* hmd = nullptr; - if (display_system_openvr_ != nullptr) { - hmd = display_system_openvr_->GetHMD(); - } - const auto transform = GetEntity()->GetFirstComponent<phx::Transform>(); - if (hmd == nullptr || transform == nullptr) return; - - auto indices = hmd->GetControllerIndices(); - - for (auto i = 0u; i < indices.size(); ++i) { - vr::VRControllerState_t controller_state; - vr::VRSystem()->GetControllerState(indices[i], &controller_state, - sizeof controller_state); - - // Set the transform based on whether the trigger is pressed. - if (controller_state.ulButtonTouched & - vr::ButtonMaskFromId(vr::EVRButtonId::k_EButton_SteamVR_Trigger)) - transform->Translate( - -0.05F * glm::column(hmd->GetRightControllerTransformation(), 2)); - if (controller_state.ulButtonTouched & - vr::ButtonMaskFromId(vr::EVRButtonId::k_EButton_SteamVR_Touchpad)) - transform->Translate( - 0.05F * glm::column(hmd->GetRightControllerTransformation(), 2)); - } -} diff --git a/demos/viewer/src/viewer.cpp b/demos/viewer/src/viewer.cpp index d8d6fe5a575496de37b140e615c349bb0ee62fa0..a43834aba9cadeb24c0425e9c2373a7b4026f78d 100644 --- a/demos/viewer/src/viewer.cpp +++ b/demos/viewer/src/viewer.cpp @@ -22,10 +22,7 @@ #include <chrono> #include <future> -#include <iostream> #include <memory> -#include <string> -#include <utility> #include <vector> #include "phx/core/engine.hpp" @@ -36,47 +33,40 @@ #include "phx/display/display_system_openvr.hpp" #include "phx/display/display_system_window.hpp" #include "phx/display/window.hpp" +#include "phx/input/device_system.hpp" #include "phx/input/input_system.hpp" #include "phx/rendering/auxiliary/splash_screen.hpp" #include "phx/rendering/components/light.hpp" -#include "phx/rendering/components/material_handle.hpp" -#include "phx/rendering/components/mesh_handle.hpp" #include "phx/rendering/components/transform.hpp" #include "phx/rendering/rendering_system.hpp" #include "phx/resources/loaders/assimp_model_loader.hpp" -#include "phx/resources/loaders/openvr_resource_loader.hpp" #include "phx/resources/loaders/scene_loader.hpp" -#include "phx/resources/types/mesh.hpp" -#include "phx/resources/resource_declaration.hpp" #include "phx/resources/resource_manager.hpp" -#include "phx/resources/resource_pointer.hpp" -#include "phx/phoenix.hpp" #include "phx/setup.hpp" -#include "navigation_behavior.hpp" -#include "rotation_behavior.hpp" #include "viewer_system.hpp" +#include "vrcontroller_navigation_behavior.hpp" #if defined __clang__ #pragma clang diagnostic ignored "-Wmissing-prototypes" #endif int main(int, char**) { - std::unique_ptr<phx::Engine> engine = phx::Setup::CreateDefaultEngine(false); + std::unique_ptr<phx::Engine> engine = phx::Setup::CreateDefaultEngine(true); auto scene = engine->GetScene(); auto rendering_system = engine->GetSystem<phx::RenderingSystem>(); rendering_system->SetEnabled(false); phx::SplashScreen* splash = engine->CreateSystem<phx::SplashScreen>( - engine->GetSystem<phx::DisplaySystemWindow>()->GetWindow()); + engine->GetSystem<phx::DisplaySystemWindow>()->GetWindow()); engine->MoveSystemBefore(splash, - engine->GetSystem<phx::DisplaySystemWindow>()); + engine->GetSystem<phx::DisplaySystemWindow>()); auto assimp_loader = static_cast<phx::AssimpModelLoader*>( - phx::ResourceManager::instance().GetLoaderForType(".obj")); + phx::ResourceManager::instance().GetLoaderForType(".obj")); assimp_loader->SetProgressUpdateCallback( - [splash](float progress) { splash->SetLoadProgress(progress); }); + [splash](float progress) { splash->SetLoadProgress(progress); }); phx::InputSystem* input_system = engine->GetSystem<phx::InputSystem>(); ViewerSystem* viewer_system = engine->CreateSystem<ViewerSystem>(); @@ -88,8 +78,8 @@ int main(int, char**) { }); auto handle = std::async([&scene, rendering_system, splash]() { - phx::SceneLoader::InsertModelIntoScene( - "models/UniversityScene/Univers20171013.obj", scene.get()); + phx::SceneLoader::InsertModelIntoScene("models/bunny.obj", scene.get()); + // "models/UniversityScene/Univers20171013.obj" rendering_system->SetEnabled(true); splash->SetEnabled(false); @@ -124,8 +114,9 @@ int main(int, char**) { virtual_platform_transform->SetLocalTranslation(start_position); phx::info("The virtual platform's start position is: {}", start_position); - virtual_platform->AddComponent<NavigationBehavior>( - engine->GetSystem<phx::DisplaySystemOpenVR>()); + virtual_platform->AddComponent<VRControllerNavigationBehavior>( + engine->GetSystem<phx::DeviceSystem>(), + phx::VRController::RIGHT_CONTROLLER); engine->Run(); diff --git a/demos/viewer/src/viewer_system.hpp b/demos/viewer/src/viewer_system.hpp index 82dc5c9536685b356d43e3442c08b7aaff71e4dd..dcb4d7168a8d7c85fa9139653846d83a9ef33ab4 100644 --- a/demos/viewer/src/viewer_system.hpp +++ b/demos/viewer/src/viewer_system.hpp @@ -31,14 +31,12 @@ #include "phx/core/frame_timer.hpp" #include "phx/core/system.hpp" -SUPPRESS_WARNINGS_BEGIN_PADDED - class ViewerSystem : public phx::System { public: ViewerSystem() = delete; ViewerSystem(const ViewerSystem&) = delete; ViewerSystem(ViewerSystem&&) = default; - ~ViewerSystem() = default; + ~ViewerSystem() override = default; bool GetShowFramerate() const; void SetShowFramerate(bool show_framerate); @@ -58,6 +56,4 @@ class ViewerSystem : public phx::System { explicit ViewerSystem(phx::Engine* engine); }; -SUPPRESS_WARNINGS_END - #endif // DEMOS_VIEWER_SRC_VIEWER_SYSTEM_HPP_ diff --git a/demos/viewer/src/vrcontroller_navigation_behavior.cpp b/demos/viewer/src/vrcontroller_navigation_behavior.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9acee895c5d9784aa53d6c017de0fd72a8e5b7ba --- /dev/null +++ b/demos/viewer/src/vrcontroller_navigation_behavior.cpp @@ -0,0 +1,99 @@ +//------------------------------------------------------------------------------ +// Project Phoenix +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualization Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the 3-Clause BSD License (the "License"); +// you may not use this file except in compliance with the License. +// See the file LICENSE for the full text. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include "vrcontroller_navigation_behavior.hpp" + +#include "phx/suppress_warnings.hpp" + +SUPPRESS_WARNINGS_BEGIN +#include "glm/detail/type_vec3.hpp" +#include "glm/glm.hpp" +#include "glm/gtc/matrix_access.hpp" +SUPPRESS_WARNINGS_END + +#include "phx/core/entity.hpp" +#include "phx/core/logger.hpp" +#include "phx/core/runtime_component.hpp" +#include "phx/display/hmd.hpp" +#include "phx/rendering/components/transform.hpp" + +VRControllerNavigationBehavior::VRControllerNavigationBehavior( + phx::DeviceSystem* device_system, phx::VRController::ControllerSide side) + : device_system_(device_system), side_(side) { + phx::VRController* controller = GetController(); + + if (controller != nullptr) { + controller->RegisterButtonSignal( + [this](phx::VRController::ButtonId id, + phx::VRController::ButtonEvent event) { + this->OnButtonSignal(id, event); + }); + } +} + +void VRControllerNavigationBehavior::OnUpdate() { + const auto transform = GetEntity()->GetFirstComponent<phx::Transform>(); + phx::VRController* controller = GetController(); + if (transform != nullptr && controller != nullptr && button_pressed_) { + glm::mat4 controller_pos = controller->GetPose(); + glm::vec3 controller_forward = glm::column(controller_pos, 2); + float speed = + speed_ * static_cast<float>(time_info_->time_since_last_frame.count()); + speed *= + -1.0f * controller->GetAxesValue(phx::VRController::AXES_TRACKPAD)[1]; + transform->Translate(speed * controller_forward); + } + + phx::info("speed_: {}, axes: {}, time: {}, button_pressed: {}", speed_, + controller->GetAxesValue(phx::VRController::AXES_TRACKPAD), + time_info_->time_since_last_frame.count(), button_pressed_); +} + +void VRControllerNavigationBehavior::OnButtonSignal( + phx::VRController::ButtonId id, phx::VRController::ButtonEvent event) { + if (event == phx::VRController::BUTTON_TOUCH) { + if (id == vr::EVRButtonId::k_EButton_SteamVR_Touchpad) { + button_pressed_ = true; + } + } + if (event == phx::VRController::BUTTON_UNTOUCH && + id == vr::EVRButtonId::k_EButton_SteamVR_Touchpad) { + button_pressed_ = false; + } +} + +float VRControllerNavigationBehavior::GetNavigationSpeed() const { + return speed_; +} + +void VRControllerNavigationBehavior::SetNavigationSpeed(float speed) { + speed_ = speed; +} + +phx::VRController* VRControllerNavigationBehavior::GetController() { + for (auto controller : device_system_->GetDevices<phx::VRController>()) { + if (controller->GetSide() == side_) { + return controller; + } + } + return nullptr; +} diff --git a/demos/combustion_demo/src/vr_controller_navigation_behavior.hpp b/demos/viewer/src/vrcontroller_navigation_behavior.hpp similarity index 69% rename from demos/combustion_demo/src/vr_controller_navigation_behavior.hpp rename to demos/viewer/src/vrcontroller_navigation_behavior.hpp index f0110d07f44bd3c2acd2d6287feced6af904c3db..2423ca508a6ea343fc986a77e7deb9613ef4afd5 100644 --- a/demos/combustion_demo/src/vr_controller_navigation_behavior.hpp +++ b/demos/viewer/src/vrcontroller_navigation_behavior.hpp @@ -20,17 +20,18 @@ // limitations under the License. //------------------------------------------------------------------------------ -#ifndef DEMOS_COMBUSTION_DEMO_SRC_VR_CONTROLLER_NAVIGATION_BEHAVIOR_HPP_ -#define DEMOS_COMBUSTION_DEMO_SRC_VR_CONTROLLER_NAVIGATION_BEHAVIOR_HPP_ +#ifndef DEMOS_VIEWER_SRC_VRCONTROLLER_NAVIGATION_BEHAVIOR_HPP_ +#define DEMOS_VIEWER_SRC_VRCONTROLLER_NAVIGATION_BEHAVIOR_HPP_ -#include "phx/display/display_system_openvr.hpp" +#include "phx/input/device_system.hpp" +#include "phx/input/vr_controller.hpp" #include "phx/rendering/components/transform.hpp" #include "phx/scripting/behavior.hpp" class VRControllerNavigationBehavior : public phx::Behavior { public: explicit VRControllerNavigationBehavior( - phx::DisplaySystemOpenVR* display_system_openvr); + phx::DeviceSystem* device_system, phx::VRController::ControllerSide side); VRControllerNavigationBehavior(const VRControllerNavigationBehavior& that) = default; VRControllerNavigationBehavior(VRControllerNavigationBehavior&& temp) = @@ -42,16 +43,20 @@ class VRControllerNavigationBehavior : public phx::Behavior { VRControllerNavigationBehavior&& temp) = default; void OnUpdate() override; + void OnButtonSignal(phx::VRController::ButtonId id, + phx::VRController::ButtonEvent event); - void SetTarget(phx::Transform* target); - phx::Transform* GetTarget() const; - - protected: - phx::DisplaySystemOpenVR* display_system_openvr_; + float GetNavigationSpeed() const; + void SetNavigationSpeed(float speed); private: - phx::Transform* target_ = nullptr; - bool is_grip_down_ = false; + phx::DeviceSystem* device_system_; + phx::VRController* GetController(); + + float speed_ = 0.5f; // m/s + bool button_pressed_ = false; + phx::VRController::ControllerSide side_ = + phx::VRController::INVALID_CONTROLLER; }; -#endif // DEMOS_COMBUSTION_DEMO_SRC_VR_CONTROLLER_NAVIGATION_BEHAVIOR_HPP_ +#endif // DEMOS_VIEWER_SRC_VRCONTROLLER_NAVIGATION_BEHAVIOR_HPP_ diff --git a/library/phx/core/engine.hpp b/library/phx/core/engine.hpp index 8f2f9f38ce3c43499539959cf0065bc0f5cf5e5c..fabc9c0bd93fe3fa98a91c9c1dd28950dc96b83c 100644 --- a/library/phx/core/engine.hpp +++ b/library/phx/core/engine.hpp @@ -43,21 +43,19 @@ SUPPRESS_WARNINGS_END #include "phx/core/frame_timer.hpp" #include "phx/core/scene.hpp" #include "phx/core/system.hpp" +#include "phx/export.hpp" #include "phx/scripting/behavior.hpp" #include "phx/utility/aspects/loggable.hpp" #include "phx/utility/orderable_list.hpp" -#include "phx/export.hpp" namespace phx { -SUPPRESS_WARNINGS_BEGIN_PADDED - class PHOENIX_EXPORT Engine final : public Loggable { public: Engine(); Engine(const Engine&) = delete; Engine(Engine&&) = default; - ~Engine(); + ~Engine() override; Engine& operator=(const Engine&) = delete; Engine& operator=(Engine&&) = default; @@ -165,8 +163,6 @@ std::vector<std::tuple<Components*...>> Engine::GetFirstComponentsMany() const { return scene_->GetFirstComponentsMany<Components...>(); } -SUPPRESS_WARNINGS_END - } // namespace phx #endif // LIBRARY_PHX_CORE_ENGINE_HPP_ diff --git a/library/phx/core/entity.hpp b/library/phx/core/entity.hpp index 51bffc058a592a266ab21e9ba1740688ce54cf53..993438548637bb1baa3fb112dad705b52456e217 100644 --- a/library/phx/core/entity.hpp +++ b/library/phx/core/entity.hpp @@ -47,7 +47,7 @@ class PHOENIX_EXPORT Entity : public Nameable, public Loggable { explicit Entity(const std::string& name) : Nameable(name) {} Entity(const Entity&) = delete; Entity(Entity&&) = default; - virtual ~Entity() = default; + ~Entity() override = default; Entity& operator=(const Entity&) = delete; Entity& operator=(Entity&&) = default; diff --git a/library/phx/core/scene.hpp b/library/phx/core/scene.hpp index 6d4ba7d6b353587bbbc4f963ec86c058b66388e9..b4eb2d82437ab78d620b7d74c231bd2b85e4f74e 100644 --- a/library/phx/core/scene.hpp +++ b/library/phx/core/scene.hpp @@ -49,7 +49,7 @@ class PHOENIX_EXPORT Scene : public Nameable, public Loggable { Scene(); Scene(const Scene&) = delete; Scene(Scene&&) = default; - ~Scene() = default; + ~Scene() override = default; Scene& operator=(const Scene&) = delete; Scene& operator=(Scene&&) = default; diff --git a/library/phx/display/display_system.hpp b/library/phx/display/display_system.hpp index fd06a71bcaa5b4dc3f3f3298da08113f5ce1105c..96d78f7f662980cff91fb7294972ba6cad778344 100644 --- a/library/phx/display/display_system.hpp +++ b/library/phx/display/display_system.hpp @@ -37,7 +37,7 @@ class PHOENIX_EXPORT DisplaySystem : public System { public: DisplaySystem(const DisplaySystem&) = delete; DisplaySystem(DisplaySystem&&) = default; - ~DisplaySystem(); + ~DisplaySystem() override; DisplaySystem& operator=(const DisplaySystem&) = delete; DisplaySystem& operator=(DisplaySystem&&) = default; diff --git a/library/phx/display/display_system_openvr.cpp b/library/phx/display/display_system_openvr.cpp index a455ed5a07f6b738c4fd939f5402f22b04e55a8c..be1642e41657adba5f027f9e02f125b0956be080 100644 --- a/library/phx/display/display_system_openvr.cpp +++ b/library/phx/display/display_system_openvr.cpp @@ -33,6 +33,7 @@ #include "phx/core/logger.hpp" #include "phx/core/runtime_component.hpp" #include "phx/core/scene.hpp" +#include "phx/input/device_system.hpp" #include "phx/rendering/rendering_system.hpp" #undef CreateWindow @@ -50,28 +51,20 @@ DisplaySystemOpenVR::DisplaySystemOpenVR(Engine* engine) } DisplaySystemOpenVR::~DisplaySystemOpenVR() {} -phx::HMD* DisplaySystemOpenVR::CreateHMD() { - if (hmd_ == nullptr) { - hmd_ = std::make_unique<HMD>(); - } else { - warn( - "[DisplaySystemOpenVR] HMD already created, so far only one is " - "supported."); - } - return hmd_.get(); +phx::HMD* DisplaySystemOpenVR::GetHMD() { + auto hmds = engine_->GetSystem<DeviceSystem>()->GetDevices<HMD>(); + if (hmds.size() == 0) return nullptr; + return hmds[0]; } -void DisplaySystemOpenVR::DestroyHMD() { hmd_.reset(); } - -phx::HMD* DisplaySystemOpenVR::GetHMD() { return hmd_.get(); } - void DisplaySystemOpenVR::Update(const FrameTimer::TimeInfo&) { - if (hmd_ != nullptr) { + HMD* hmd = GetHMD(); + if (hmd != nullptr) { if (left_render_target_ != nullptr && right_render_target_ != nullptr) { auto right_texture = right_render_target_->GetColorTexture(); - hmd_->Submit(HMD::RIGHT_EYE, right_texture); + hmd->Submit(HMD::RIGHT_EYE, right_texture); auto left_texture = left_render_target_->GetColorTexture(); - hmd_->Submit(HMD::LEFT_EYE, left_texture); + hmd->Submit(HMD::LEFT_EYE, left_texture); } } } diff --git a/library/phx/display/display_system_openvr.hpp b/library/phx/display/display_system_openvr.hpp index 0fcdec3ddac0296a707c9e39afb980c87babd653..0e195ed9ba54aa8682d84d4853a14074fcee5245 100644 --- a/library/phx/display/display_system_openvr.hpp +++ b/library/phx/display/display_system_openvr.hpp @@ -30,8 +30,8 @@ #include "phx/display/display_system.hpp" #include "phx/display/hmd.hpp" #include "phx/display/window.hpp" -#include "phx/rendering/backend/render_target.hpp" #include "phx/export.hpp" +#include "phx/rendering/backend/render_target.hpp" namespace phx { @@ -40,22 +40,17 @@ class PHOENIX_EXPORT DisplaySystemOpenVR : public DisplaySystem { explicit DisplaySystemOpenVR(Engine* engine); DisplaySystemOpenVR(const DisplaySystemOpenVR&) = delete; DisplaySystemOpenVR(DisplaySystemOpenVR&&) = default; - ~DisplaySystemOpenVR(); + ~DisplaySystemOpenVR() override; DisplaySystemOpenVR& operator=(const DisplaySystemOpenVR&) = delete; DisplaySystemOpenVR& operator=(DisplaySystemOpenVR&&) = default; - HMD* CreateHMD(); - void DestroyHMD(); HMD* GetHMD(); void Update(const FrameTimer::TimeInfo&) override; void CreateRenderTargets(Scene* scene); - protected: - std::unique_ptr<HMD> hmd_; - private: void RemoveRenderTargets(); void SetEyeProjections(Scene* scene); diff --git a/library/phx/display/display_system_window.hpp b/library/phx/display/display_system_window.hpp index 1b41d76bb2835b793bed04e3dc284ed03013a6c9..cf8a3b559bbcc58796be3b1e4b34b76d01c36392 100644 --- a/library/phx/display/display_system_window.hpp +++ b/library/phx/display/display_system_window.hpp @@ -41,7 +41,7 @@ class PHOENIX_EXPORT DisplaySystemWindow : public DisplaySystem { explicit DisplaySystemWindow(Engine* engine); DisplaySystemWindow(const DisplaySystemWindow&) = delete; DisplaySystemWindow(DisplaySystemWindow&&) = default; - ~DisplaySystemWindow(); + ~DisplaySystemWindow() override; DisplaySystemWindow& operator=(const DisplaySystemWindow&) = delete; DisplaySystemWindow& operator=(DisplaySystemWindow&&) = default; diff --git a/library/phx/display/hmd.cpp b/library/phx/display/hmd.cpp index c43329e2076bf3ad6dca6f6bb04affa19112f743..f8d72a6ffaf9c5b2cff31fb25c1dad7c02abc725 100644 --- a/library/phx/display/hmd.cpp +++ b/library/phx/display/hmd.cpp @@ -25,7 +25,6 @@ #include <algorithm> #include <array> #include <memory> -#include <stdexcept> #include <string> #include <utility> #include <vector> @@ -42,16 +41,10 @@ SUPPRESS_WARNINGS_END #include "phx/core/logger.hpp" #include "phx/resources/resource_manager.hpp" -#include "phx/resources/resource_pointer.hpp" namespace phx { -HMD::HMD() { - vr::HmdError hmd_error = vr::VRInitError_None; - vr_system_ = vr::VR_Init(&hmd_error, vr::VRApplication_Scene); - if (vr_system_ == nullptr || hmd_error != vr::VRInitError_None) { - error("HMD cannot be initialized with error-code: {}", hmd_error); - throw std::runtime_error("OpenVR cannot be initialized!"); - } +HMD::HMD() : TrackedDevice() { + id_ = vr::k_unTrackedDeviceIndex_Hmd; uint32_t x, y; vr_system_->GetRecommendedRenderTargetSize(&x, &y); @@ -60,12 +53,9 @@ HMD::HMD() { projection_right_ = GetProjectionMatrixFromOpenVR(vr::Hmd_Eye::Eye_Right); projection_left_ = GetProjectionMatrixFromOpenVR(vr::Hmd_Eye::Eye_Left); - eye_to_head_right_ = GetEyeToHeadMatrixFromOpenVR(vr::Hmd_Eye::Eye_Right); - eye_to_head_left_ = GetEyeToHeadMatrixFromOpenVR(vr::Hmd_Eye::Eye_Left); + UpdateEyeToHeadMatrices(); } -HMD::~HMD() { vr::VR_Shutdown(); } - bool HMD::IsHMDPresent() { return vr::VR_IsHmdPresent(); } void HMD::Submit(Side side, gl::texture_2d* texture) { @@ -75,18 +65,7 @@ void HMD::Submit(Side side, gl::texture_2d* texture) { vr::VRCompositor()->Submit(static_cast<vr::EVREye>(side), &vr_texture); } -std::vector<vr::TrackedDeviceIndex_t> HMD::GetControllerIndices() { - std::vector<std::uint32_t> indices(vr::k_unMaxTrackedDeviceCount); - vr::VRSystem()->GetSortedTrackedDeviceIndicesOfClass( - vr::TrackedDeviceClass_Controller, indices.data(), - static_cast<std::uint32_t>(indices.size())); - return indices; -} - -vr::ETrackedControllerRole HMD::GetControllerRoleForTrackedDeviceIndex( - vr::TrackedDeviceIndex_t device_index) const { - return vr_system_->GetControllerRoleForTrackedDeviceIndex(device_index); -} +void HMD::UpdateDeviceIndex() { id_ = vr::k_unTrackedDeviceIndex_Hmd; } const glm::uvec2& HMD::GetViewportSize() const { return viewport_size_; } @@ -104,142 +83,26 @@ const glm::mat4& HMD::GetEyeToHeadMatrix(Side side) const { return eye_to_head_left_; } -void HMD::UpdateTrackedDevices() { - vr::VRCompositor()->WaitGetPoses(tracked_device_poses_, - vr::k_unMaxTrackedDeviceCount, NULL, 0); -} +void HMD::Update() { + last_wait_get_poses_.resize(vr::k_unMaxTrackedDeviceCount); -glm::mat4 HMD::GetHeadTransformation() { - if (tracked_device_poses_[vr::k_unTrackedDeviceIndex_Hmd].bPoseIsValid) { - glm::mat4 head = TransformToGlmMatrix( - tracked_device_poses_[vr::k_unTrackedDeviceIndex_Hmd] + vr::VRCompositor()->WaitGetPoses(&last_wait_get_poses_[0], + vr::k_unMaxTrackedDeviceCount, NULL, 0); + if (last_wait_get_poses_[vr::k_unTrackedDeviceIndex_Hmd].bPoseIsValid) { + pose_ = TransformToGlmMatrix( + last_wait_get_poses_[vr::k_unTrackedDeviceIndex_Hmd] .mDeviceToAbsoluteTracking); - last_head_transformation_ = head; - return head; + } else { + debug("[HMD] HMD pose is invalid, use the last valid one"); } - debug("[HMD] HMD pose is invalid, use the last valid one"); - return last_head_transformation_; -} - -glm::mat4 HMD::GetTransformationForRole(vr::ETrackedControllerRole role) { - auto controller_indices = GetControllerIndices(); - for (auto i = 0u; i < controller_indices.size(); ++i) - if (vr_system_->GetControllerRoleForTrackedDeviceIndex( - controller_indices[i]) == role) - return TransformToGlmMatrix(tracked_device_poses_[controller_indices[i]] - .mDeviceToAbsoluteTracking); - debug( - "[HMD::GetTransformationForRole] Unable to find tranformation for role " - "{}", - role); - return glm::mat4(); -} - -glm::mat4 HMD::GetLeftControllerTransformation() { - return GetTransformationForRole(vr::TrackedControllerRole_LeftHand); -} -glm::mat4 HMD::GetRightControllerTransformation() { - return GetTransformationForRole(vr::TrackedControllerRole_RightHand); + TrackedDevice::Update(); } -std::unique_ptr<phx::Mesh> HMD::GetControllerMesh(Controller controller) { - auto model = GetControllerModel(controller); - if (model == nullptr) { - return nullptr; +void HMD::OnOpenVREvent(const vr::VREvent_t& event) { + if (event.eventType == vr::VREvent_IpdChanged) { + UpdateEyeToHeadMatrices(); } - auto mesh = std::make_unique<phx::Mesh>(); - std::vector<glm::vec3> vertices; - std::vector<glm::vec3> normals; - std::vector<glm::vec2> texcoords; - for (std::size_t i = 0; i < model->unVertexCount; i++) { - vertices.push_back(glm::vec3(model->rVertexData[i].vPosition.v[0], - model->rVertexData[i].vPosition.v[1], - model->rVertexData[i].vPosition.v[2])); - normals.push_back(glm::vec3(model->rVertexData[i].vNormal.v[0], - model->rVertexData[i].vNormal.v[1], - model->rVertexData[i].vNormal.v[2])); - texcoords.push_back(glm::vec2(model->rVertexData[i].rfTextureCoord[0], - model->rVertexData[i].rfTextureCoord[1])); - } - std::vector<unsigned int> indices; - for (std::size_t i = 0; i < model->unTriangleCount * 3; i++) { - indices.push_back(model->rIndexData[i]); - } - mesh->SetVertices(std::move(vertices)); - mesh->SetNormals(std::move(normals)); - mesh->SetTextureCoords(std::move(texcoords)); - mesh->SetIndices(std::move(indices)); - return mesh; -} - -vr::RenderModel_t* HMD::GetControllerModel(Controller controller) { - std::string rendermodel_name; - rendermodel_name.resize(1024); - auto controller_indices = GetControllerIndices(); - for (auto i = 0u; i < controller_indices.size(); ++i) { - if (vr_system_->GetControllerRoleForTrackedDeviceIndex( - controller_indices[i]) == - static_cast<vr::ETrackedControllerRole>(controller)) { - vr::VRSystem()->GetStringTrackedDeviceProperty( - controller_indices[i], vr::Prop_RenderModelName_String, - &rendermodel_name[0], static_cast<uint32_t>(rendermodel_name.size())); - } - } - - vr::RenderModel_t* model = nullptr; - while (vr::VRRenderModels()->LoadRenderModel_Async( - &rendermodel_name[0], &model) == vr::VRRenderModelError_Loading) { - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - } - if (model == nullptr) { - return nullptr; - } - - return model; -} - -std::unique_ptr<Material> HMD::GetControllerMaterial(Controller controller) { - auto model = GetControllerModel(controller); - if (model == nullptr) return nullptr; - - auto material = std::make_unique<phx::Material>(); - material->SetAmbientColor(glm::vec3(0.1, 0.1, 0.1)); - material->SetSpecularColor(glm::vec3(0.3, 0.3, 0.3)); - - auto texture = ResourceManager::instance().DeclareResource<Image>( - {{"TYPE", "openVR"}, - {"OpenVR_type", "texture"}, - {"texture_id", model->diffuseTextureId}, - {"side", - controller == HMD::Controller::LEFT_CONTROLLER ? "left" : "right"}}); - texture.Load(); - material->SetDiffuseImage(texture); - - return material; -} - -std::unique_ptr<Image> HMD::GetControllerTexture(int id) { - vr::RenderModel_TextureMap_t* texture_map; - while (vr::VRRenderModels()->LoadTexture_Async(id, &texture_map) == - vr::VRRenderModelError_Loading) { - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - } - if (texture_map == nullptr) { - return nullptr; - } - - std::vector<unsigned char> image_data(texture_map->unWidth * - texture_map->unHeight * 4); - std::copy(texture_map->rubTextureMapData, - texture_map->rubTextureMapData + image_data.size(), - image_data.begin()); - auto image = std::make_unique<phx::Image>( - &image_data[0], std::array<std::size_t, 2>{ - {static_cast<std::size_t>(texture_map->unWidth), - static_cast<std::size_t>(texture_map->unHeight)}}, 32); - - return image; } glm::mat4 HMD::GetProjectionMatrixFromOpenVR(const vr::Hmd_Eye eye) { @@ -256,18 +119,9 @@ glm::mat4 HMD::GetEyeToHeadMatrixFromOpenVR(const vr::Hmd_Eye eye) { return TransformToGlmMatrix(steamvr_eye_head_matrix); } -glm::mat4 HMD::TransformToGlmMatrix(const vr::HmdMatrix34_t& mat) { - return glm::mat4(mat.m[0][0], mat.m[1][0], mat.m[2][0], 0.0f, mat.m[0][1], - mat.m[1][1], mat.m[2][1], 0.0f, mat.m[0][2], mat.m[1][2], - mat.m[2][2], 0.0f, mat.m[0][3], mat.m[1][3], mat.m[2][3], - 1.0f); -} - -glm::mat4 HMD::TransformToGlmMatrix(const vr::HmdMatrix44_t& mat) { - return glm::mat4(mat.m[0][0], mat.m[1][0], mat.m[2][0], mat.m[3][0], - mat.m[0][1], mat.m[1][1], mat.m[2][1], mat.m[3][1], - mat.m[0][2], mat.m[1][2], mat.m[2][2], mat.m[3][2], - mat.m[0][3], mat.m[1][3], mat.m[2][3], mat.m[3][3]); +void HMD::UpdateEyeToHeadMatrices() { + eye_to_head_right_ = GetEyeToHeadMatrixFromOpenVR(vr::Hmd_Eye::Eye_Right); + eye_to_head_left_ = GetEyeToHeadMatrixFromOpenVR(vr::Hmd_Eye::Eye_Left); } } // namespace phx diff --git a/library/phx/display/hmd.hpp b/library/phx/display/hmd.hpp index 4bf398e9009ead18a97c8aec08fbdce9345be680..be7f5f0f6398c3a2d03ad65af177873e5e9c9628 100644 --- a/library/phx/display/hmd.hpp +++ b/library/phx/display/hmd.hpp @@ -35,20 +35,20 @@ SUPPRESS_WARNINGS_BEGIN #include "openvr.h" //NOLINT SUPPRESS_WARNINGS_END +#include "phx/export.hpp" +#include "phx/input/tracked_device.hpp" #include "phx/resources/types/material.hpp" #include "phx/resources/types/mesh.hpp" -#include "phx/export.hpp" #include "gl/texture.hpp" namespace phx { -class OpenVRResourceLoader; - -class PHOENIX_EXPORT HMD { +class PHOENIX_EXPORT HMD : public TrackedDevice { public: HMD(); - ~HMD(); + ~HMD() = default; + HMD(const HMD&) = delete; HMD(HMD&&) = default; @@ -60,45 +60,25 @@ class PHOENIX_EXPORT HMD { LEFT_EYE = vr::EVREye::Eye_Left }; - enum Controller { - RIGHT_CONTROLLER = vr::TrackedControllerRole_RightHand, - LEFT_CONTROLLER = vr::TrackedControllerRole_LeftHand - }; + void Update() override; + void OnOpenVREvent(const vr::VREvent_t& event) override; const glm::uvec2& GetViewportSize() const; const glm::mat4& GetProjectionMatrix(Side side) const; const glm::mat4& GetEyeToHeadMatrix(Side side) const; - void UpdateTrackedDevices(); - glm::mat4 GetHeadTransformation(); - glm::mat4 GetLeftControllerTransformation(); - glm::mat4 GetRightControllerTransformation(); - static bool IsHMDPresent(); void Submit(Side side, gl::texture_2d* texture); - std::vector<vr::TrackedDeviceIndex_t> GetControllerIndices(); - vr::ETrackedControllerRole GetControllerRoleForTrackedDeviceIndex( - vr::TrackedDeviceIndex_t device_index) const; + void UpdateDeviceIndex() override; private: - glm::mat4 GetTransformationForRole(vr::ETrackedControllerRole role); - glm::mat4 GetProjectionMatrixFromOpenVR(const vr::Hmd_Eye eye); glm::mat4 GetEyeToHeadMatrixFromOpenVR(const vr::Hmd_Eye eye); + void UpdateEyeToHeadMatrices(); - friend OpenVRResourceLoader; - vr::RenderModel_t* GetControllerModel(Controller controller); - std::unique_ptr<Mesh> GetControllerMesh(Controller controller); - std::unique_ptr<Material> GetControllerMaterial(Controller controller); - std::unique_ptr<Image> GetControllerTexture(int id); - - static glm::mat4 TransformToGlmMatrix(const vr::HmdMatrix34_t& mat); - static glm::mat4 TransformToGlmMatrix(const vr::HmdMatrix44_t& mat); - - vr::IVRSystem* vr_system_; glm::uvec2 viewport_size_; glm::mat4 projection_right_; @@ -106,9 +86,6 @@ class PHOENIX_EXPORT HMD { glm::mat4 eye_to_head_right_; glm::mat4 eye_to_head_left_; - vr::TrackedDevicePose_t tracked_device_poses_ - [vr::k_unMaxTrackedDeviceCount]; // NOLINT(runtime/arrays) - glm::mat4 last_head_transformation_; }; diff --git a/library/phx/input/device.cpp b/library/phx/input/device.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5c68859d70073a2d10a384ed647c75afdd66c9b0 --- /dev/null +++ b/library/phx/input/device.cpp @@ -0,0 +1,25 @@ +//------------------------------------------------------------------------------ +// Project Phoenix +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualization Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the 3-Clause BSD License (the "License"); +// you may not use this file except in compliance with the License. +// See the file LICENSE for the full text. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include "phx/input/device.hpp" + +namespace phx {} // namespace phx diff --git a/demos/viewer/src/navigation_behavior.hpp b/library/phx/input/device.hpp similarity index 57% rename from demos/viewer/src/navigation_behavior.hpp rename to library/phx/input/device.hpp index ac2e06c9ef98424e746f42a41d3880e5079781da..c477639764a905efdc712809d5a74034be7aaf98 100644 --- a/demos/viewer/src/navigation_behavior.hpp +++ b/library/phx/input/device.hpp @@ -20,25 +20,19 @@ // limitations under the License. //------------------------------------------------------------------------------ -#ifndef DEMOS_VIEWER_SRC_NAVIGATION_BEHAVIOR_HPP_ -#define DEMOS_VIEWER_SRC_NAVIGATION_BEHAVIOR_HPP_ +#ifndef LIBRARY_PHX_INPUT_DEVICE_HPP_ +#define LIBRARY_PHX_INPUT_DEVICE_HPP_ -#include "phx/display/display_system_openvr.hpp" -#include "phx/scripting/behavior.hpp" +#include "phx/export.hpp" -class NavigationBehavior : public phx::Behavior { - public: - explicit NavigationBehavior(phx::DisplaySystemOpenVR* display_system_openvr); - NavigationBehavior(const NavigationBehavior& that) = default; - NavigationBehavior(NavigationBehavior&& temp) = default; - virtual ~NavigationBehavior() = default; - NavigationBehavior& operator=(const NavigationBehavior& that) = default; - NavigationBehavior& operator=(NavigationBehavior&& temp) = default; - - void OnUpdate() override; +namespace phx { - protected: - phx::DisplaySystemOpenVR* display_system_openvr_; +class PHOENIX_EXPORT Device { + public: + virtual ~Device() = default; + virtual void Update() = 0; }; -#endif // DEMOS_VIEWER_SRC_NAVIGATION_BEHAVIOR_HPP_ +} // namespace phx + +#endif // LIBRARY_PHX_INPUT_DEVICE_HPP_ diff --git a/tests/test_utilities/dummy_material_generator.cpp b/library/phx/input/device_system.cpp similarity index 57% rename from tests/test_utilities/dummy_material_generator.cpp rename to library/phx/input/device_system.cpp index 4dcf8e004a7d1bd854f0f734247ece4c1d28d155..c19d7947ef0a3d69cfecb728c33ddf9de591405a 100644 --- a/tests/test_utilities/dummy_material_generator.cpp +++ b/library/phx/input/device_system.cpp @@ -20,32 +20,34 @@ // limitations under the License. //------------------------------------------------------------------------------ -#include "dummy_material_generator.hpp" +#include "phx/input/device_system.hpp" #include <memory> +#include <string> #include <utility> -#include <vector> -#include "phx/resources/types/material.hpp" +#include "phx/core/logger.hpp" namespace phx { +DeviceSystem::DeviceSystem(Engine* engine) : System(engine) {} -DummyMaterialGenerator::DummyMaterialGenerator(glm::vec3 diffuse_color, - glm::vec3 specular_color, - glm::vec3 ambient_color, - float shininess) - : material_{std::make_unique<phx::Material>()} { - material_->SetAmbientColor(ambient_color); - material_->SetDiffuseColor(diffuse_color); - material_->SetSpecularColor(specular_color); - material_->SetShininess(shininess); +DeviceSystem::~DeviceSystem() {} + +void DeviceSystem::Update(const FrameTimer::TimeInfo&) { + for (auto& device : devices_) { + device->Update(); + } } -std::unique_ptr<phx::Resource> DummyMaterialGenerator::Load( - const ResourceDeclaration&) { - auto new_material = std::make_unique<phx::Material>(); - *new_material = *material_; - return new_material; +void DeviceSystem::RemoveDevice(Device* device) { + auto iterator = + std::remove_if(devices_.begin(), devices_.end(), + [device](const std::unique_ptr<Device>& iteratee) { + return device == iteratee.get(); + }); + devices_.erase(iterator, devices_.end()); } +std::string DeviceSystem::ToString() const { return "DeviceSystem"; } + } // namespace phx diff --git a/library/phx/input/device_system.hpp b/library/phx/input/device_system.hpp new file mode 100644 index 0000000000000000000000000000000000000000..644b8b471f4a3ed5b0ce43f5628dc33c7128ff6d --- /dev/null +++ b/library/phx/input/device_system.hpp @@ -0,0 +1,84 @@ +//------------------------------------------------------------------------------ +// Project Phoenix +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualization Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the 3-Clause BSD License (the "License"); +// you may not use this file except in compliance with the License. +// See the file LICENSE for the full text. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#ifndef LIBRARY_PHX_INPUT_DEVICE_SYSTEM_HPP_ +#define LIBRARY_PHX_INPUT_DEVICE_SYSTEM_HPP_ + +#include <memory> +#include <string> +#include <vector> + +#include "phx/core/engine.hpp" +#include "phx/core/system.hpp" +#include "phx/export.hpp" +#include "phx/input/device.hpp" + +namespace phx { +class InputSystem; + +class PHOENIX_EXPORT DeviceSystem : public System { + public: + DeviceSystem() = delete; + DeviceSystem(const DeviceSystem&) = delete; + DeviceSystem(DeviceSystem&&) = default; + virtual ~DeviceSystem(); + + DeviceSystem& operator=(const DeviceSystem&) = delete; + DeviceSystem& operator=(DeviceSystem&&) = default; + + void Update(const FrameTimer::TimeInfo& time_info) override; + + template <typename DeviceType> + std::vector<DeviceType*> GetDevices() { + static_assert(std::is_base_of<Device, DeviceType>::value, + "The type does not inherit from Device."); + + std::vector<DeviceType*> devices; + for (const auto& device : devices_) { + auto casted_device = dynamic_cast<DeviceType*>(device.get()); + if (casted_device != nullptr) { + devices.push_back(casted_device); + } + } + return devices; + } + + template <typename DeviceType, typename... ComponentArguments> + DeviceType* AddDevice(ComponentArguments&&... arguments) { + devices_.push_back(std::make_unique<DeviceType>(arguments...)); + return dynamic_cast<DeviceType*>(devices_.back().get()); + } + + void RemoveDevice(Device* device); + + std::string ToString() const override; + + private: + friend DeviceSystem* Engine::CreateSystem<DeviceSystem>(); + explicit DeviceSystem(Engine* engine); + + std::vector<std::unique_ptr<Device>> devices_; +}; + +} // namespace phx + +#endif // LIBRARY_PHX_INPUT_DEVICE_SYSTEM_HPP_ diff --git a/library/phx/input/input_system.hpp b/library/phx/input/input_system.hpp index bd9d6076aa905c46fec92866711bdd4321cc7490..9862ea78119438869c1e37162909df8c9faba169 100644 --- a/library/phx/input/input_system.hpp +++ b/library/phx/input/input_system.hpp @@ -45,7 +45,7 @@ class PHOENIX_EXPORT InputSystem : public System { InputSystem() = delete; InputSystem(const InputSystem&) = delete; InputSystem(InputSystem&&) = default; - virtual ~InputSystem(); + ~InputSystem() override; void Update(const FrameTimer::TimeInfo& time_info) override; @@ -73,20 +73,12 @@ class PHOENIX_EXPORT InputSystem : public System { void UpdateSDLEvents(); - // TODO(@tvierjahn) check if Wpadded is here only emitted by some clang - // versions. - // There will be only one input system.Padding will not waste - // significant memory - SUPPRESS_WARNINGS_BEGIN_PADDED - boost::signals2::signal<void()> quit_signal_; boost::signals2::signal<void(char)> key_press_signal_; boost::signals2::signal<void(char)> key_release_signal_; boost::signals2::signal<void(int, int)> mouse_move_signal_; boost::signals2::signal<void(unsigned int)> mouse_press_signal_; boost::signals2::signal<void(unsigned int)> mouse_release_signal_; - - SUPPRESS_WARNINGS_END }; } // namespace phx diff --git a/library/phx/input/openvr_controller_behavior.cpp b/library/phx/input/openvr_controller_behavior.cpp index cd229828f4a407039f843dc5219e07d7d5c6aaca..fefc8a7c802f0c27dc679072401cffb559017d98 100644 --- a/library/phx/input/openvr_controller_behavior.cpp +++ b/library/phx/input/openvr_controller_behavior.cpp @@ -31,14 +31,14 @@ namespace phx { void OpenVRControllerBehavior::OnUpdate() { phx::Scene* scene = GetEntity()->GetScene(); phx::Entity* runtime_entity = nullptr; - if (side_ == Side::LEFT) { + if (side_ == VRController::LEFT_CONTROLLER) { auto runtime_entities = scene->GetEntitiesWithComponents< phx::RuntimeComponent<phx::LEFT_CONTROLLER>>(); if (!runtime_entities.empty()) { runtime_entity = runtime_entities[0]; } } - if (side_ == Side::RIGHT) { + if (side_ == VRController::RIGHT_CONTROLLER) { auto runtime_entities = scene->GetEntitiesWithComponents< phx::RuntimeComponent<phx::RIGHT_CONTROLLER>>(); if (!runtime_entities.empty()) { @@ -53,9 +53,11 @@ void OpenVRControllerBehavior::OnUpdate() { } } -void OpenVRControllerBehavior::SetSide(Side side) { side_ = side; } +void OpenVRControllerBehavior::SetSide(VRController::ControllerSide side) { + side_ = side; +} -OpenVRControllerBehavior::Side OpenVRControllerBehavior::GetSide() const { +VRController::ControllerSide OpenVRControllerBehavior::GetSide() const { return side_; } diff --git a/library/phx/input/openvr_controller_behavior.hpp b/library/phx/input/openvr_controller_behavior.hpp index c31643b67d511b4a813eb82e0d75db5f0039648d..9f8ce400b7a42d04937543ca2daf4df505361814 100644 --- a/library/phx/input/openvr_controller_behavior.hpp +++ b/library/phx/input/openvr_controller_behavior.hpp @@ -25,24 +25,21 @@ #include "phx/suppress_warnings.hpp" -#include "phx/scripting/behavior.hpp" #include "phx/export.hpp" +#include "phx/input/vr_controller.hpp" +#include "phx/scripting/behavior.hpp" namespace phx { -SUPPRESS_WARNINGS_BEGIN_PADDED class PHOENIX_EXPORT OpenVRControllerBehavior : public Behavior { public: - enum Side { LEFT, RIGHT }; - void OnUpdate() override; - void SetSide(Side side); - Side GetSide() const; + void SetSide(VRController::ControllerSide side); + VRController::ControllerSide GetSide() const; private: - Side side_ = Side::LEFT; + VRController::ControllerSide side_ = VRController::LEFT_CONTROLLER; }; -SUPPRESS_WARNINGS_END } // namespace phx #endif // LIBRARY_PHX_INPUT_OPENVR_CONTROLLER_BEHAVIOR_HPP_ diff --git a/library/phx/input/openvr_controller_system.cpp b/library/phx/input/openvr_controller_model_system.cpp similarity index 75% rename from library/phx/input/openvr_controller_system.cpp rename to library/phx/input/openvr_controller_model_system.cpp index eecf2be7c87c8437f68888ac9670a81bb7123009..643328fc5d8d7169f2b312a6ea9573d3dda354b2 100644 --- a/library/phx/input/openvr_controller_system.cpp +++ b/library/phx/input/openvr_controller_model_system.cpp @@ -20,7 +20,7 @@ // limitations under the License. //------------------------------------------------------------------------------ -#include "phx/input/openvr_controller_system.hpp" +#include "phx/input/openvr_controller_model_system.hpp" #include <memory> #include <string> @@ -29,6 +29,7 @@ #include "phx/core/logger.hpp" #include "phx/display/display_system_openvr.hpp" #include "phx/input/openvr_controller_behavior.hpp" +#include "phx/input/vr_controller.hpp" #include "phx/rendering/components/material_handle.hpp" #include "phx/rendering/components/mesh_handle.hpp" #include "phx/rendering/components/transform.hpp" @@ -38,18 +39,11 @@ namespace phx { -OpenVRControllerSystem::OpenVRControllerSystem(Engine* engine, - DisplaySystem* display_system) - : System(engine) { - // find HMD - hmd_ = dynamic_cast<DisplaySystemOpenVR*>(display_system)->GetHMD(); - if (hmd_ == nullptr) { - error("Cannot use OpenVRControllerSystem without an HMD!"); - return; - } - +OpenVRControllerModelSystem::OpenVRControllerModelSystem( + Engine* engine, DeviceSystem* device_system) + : System(engine), device_system_(device_system) { auto& resource_manager = ResourceManager::instance(); - auto openvr_loader = std::make_unique<OpenVRResourceLoader>(hmd_); + auto openvr_loader = std::make_unique<OpenVRResourceLoader>(device_system); resource_manager.RegisterResourceType("openVR", std::move(openvr_loader)); } @@ -58,11 +52,11 @@ OpenVRControllerSystem::OpenVRControllerSystem(Engine* engine, #pragma clang diagnostic ignored "-Wmissing-prototypes" #endif -Entity* OpenVRControllerSystem::AddController( - phx::Scene* scene, OpenVRControllerBehavior::Side side) { +Entity* OpenVRControllerModelSystem::AddController( + phx::Scene* scene, VRController::ControllerSide side) { auto& resource_manager = ResourceManager::instance(); std::string side_string = - side == OpenVRControllerBehavior::LEFT ? "left" : "right"; + side == VRController::LEFT_CONTROLLER ? "left" : "right"; ResourceDeclaration mesh_declaration{ {"TYPE", "openVR"}, {"OpenVR_type", "mesh"}, {"side", side_string}}; @@ -82,9 +76,9 @@ Entity* OpenVRControllerSystem::AddController( return nullptr; } -Entity* OpenVRControllerSystem::AddControllerEntity( +Entity* OpenVRControllerModelSystem::AddControllerEntity( phx::Scene* scene, ResourcePointer<Mesh> mesh, - ResourcePointer<Material> material, OpenVRControllerBehavior::Side side) { + ResourcePointer<Material> material, VRController::ControllerSide side) { Entity* controller = scene->CreateEntity(); controller->AddComponent<MeshHandle>()->SetMesh(mesh); controller->AddComponent<Transform>(); @@ -98,10 +92,9 @@ Entity* OpenVRControllerSystem::AddControllerEntity( #pragma clang diagnostic pop #endif -void OpenVRControllerSystem::Update(const FrameTimer::TimeInfo&) { +void OpenVRControllerModelSystem::Update(const FrameTimer::TimeInfo&) { // check which controllers are active and update their scene representation, // if necessary - if (!hmd_) return; auto scene = GetEngine()->GetScene(); if (scene == nullptr) return; @@ -112,31 +105,30 @@ void OpenVRControllerSystem::Update(const FrameTimer::TimeInfo&) { GetEngine()->GetEntitiesWithComponents<OpenVRControllerBehavior>(); for (auto entity : controller_entities) { if (entity->GetFirstComponent<OpenVRControllerBehavior>()->GetSide() == - OpenVRControllerBehavior::LEFT) { + VRController::LEFT_CONTROLLER) { left_controller_entity = entity; } else { right_controller_entity = entity; } } - auto controller_indices = hmd_->GetControllerIndices(); bool left_controller_active = false; bool right_controller_active = false; - for (auto idx : controller_indices) { - // is it a left controller? - auto role = hmd_->GetControllerRoleForTrackedDeviceIndex(idx); - if (role == vr::TrackedControllerRole_LeftHand) { + + for (auto controller : device_system_->GetDevices<VRController>()) { + if (!controller->IsActive()) continue; + if (controller->GetSide() == VRController::LEFT_CONTROLLER) { // do we have a left controller in the scene? if (left_controller_entity == nullptr) { // create that controller left_controller_entity = - AddController(scene.get(), OpenVRControllerBehavior::LEFT); + AddController(scene.get(), VRController::LEFT_CONTROLLER); } left_controller_active = true; - } else if (role == vr::TrackedControllerRole_RightHand) { + } else if (controller->GetSide() == VRController::RIGHT_CONTROLLER) { if (right_controller_entity == nullptr) { right_controller_entity = - AddController(scene.get(), OpenVRControllerBehavior::RIGHT); + AddController(scene.get(), VRController::RIGHT_CONTROLLER); } right_controller_active = true; } diff --git a/library/phx/input/openvr_controller_system.hpp b/library/phx/input/openvr_controller_model_system.hpp similarity index 64% rename from library/phx/input/openvr_controller_system.hpp rename to library/phx/input/openvr_controller_model_system.hpp index c382d2585a76e56a4c2cdcaa7215bb7c9fa09011..693e4d4d864091a269509d3f3f94a082536b5fc6 100644 --- a/library/phx/input/openvr_controller_system.hpp +++ b/library/phx/input/openvr_controller_model_system.hpp @@ -20,8 +20,8 @@ // limitations under the License. //------------------------------------------------------------------------------ -#ifndef LIBRARY_PHX_INPUT_OPENVR_CONTROLLER_SYSTEM_HPP_ -#define LIBRARY_PHX_INPUT_OPENVR_CONTROLLER_SYSTEM_HPP_ +#ifndef LIBRARY_PHX_INPUT_OPENVR_CONTROLLER_MODEL_SYSTEM_HPP_ +#define LIBRARY_PHX_INPUT_OPENVR_CONTROLLER_MODEL_SYSTEM_HPP_ #include <memory> #include <vector> @@ -34,40 +34,43 @@ SUPPRESS_WARNINGS_END #include "phx/core/engine.hpp" #include "phx/core/system.hpp" -#include "phx/display/display_system.hpp" #include "phx/display/hmd.hpp" -#include "phx/input/openvr_controller_behavior.hpp" #include "phx/export.hpp" +#include "phx/input/device_system.hpp" +#include "phx/input/openvr_controller_behavior.hpp" +#include "phx/input/vr_controller.hpp" namespace phx { -class PHOENIX_EXPORT OpenVRControllerSystem : public System { +class PHOENIX_EXPORT OpenVRControllerModelSystem : public System { public: - OpenVRControllerSystem() = delete; - OpenVRControllerSystem(const OpenVRControllerSystem&) = delete; - OpenVRControllerSystem(OpenVRControllerSystem&&) = default; - virtual ~OpenVRControllerSystem() = default; + OpenVRControllerModelSystem() = delete; + OpenVRControllerModelSystem(const OpenVRControllerModelSystem&) = delete; + OpenVRControllerModelSystem(OpenVRControllerModelSystem&&) = default; + ~OpenVRControllerModelSystem() override = default; - OpenVRControllerSystem& operator=(const OpenVRControllerSystem&) = delete; - OpenVRControllerSystem& operator=(OpenVRControllerSystem&&) = default; + OpenVRControllerModelSystem& operator=(const OpenVRControllerModelSystem&) = + delete; + OpenVRControllerModelSystem& operator=(OpenVRControllerModelSystem&&) = + default; void Update(const FrameTimer::TimeInfo&) override; protected: template <typename SystemType, typename... SystemArguments> friend SystemType* Engine::CreateSystem(SystemArguments&&... arguments); - OpenVRControllerSystem(Engine* engine, DisplaySystem* display_system); + OpenVRControllerModelSystem(Engine* engine, DeviceSystem* device_system); private: static Entity* AddControllerEntity(phx::Scene* scene, ResourcePointer<Mesh> mesh, ResourcePointer<Material> material, - OpenVRControllerBehavior::Side side); + VRController::ControllerSide side); static Entity* AddController(phx::Scene* scene, - OpenVRControllerBehavior::Side side); + VRController::ControllerSide side); - HMD* hmd_ = nullptr; + DeviceSystem* device_system_ = nullptr; }; } // namespace phx -#endif // LIBRARY_PHX_INPUT_OPENVR_CONTROLLER_SYSTEM_HPP_ +#endif // LIBRARY_PHX_INPUT_OPENVR_CONTROLLER_MODEL_SYSTEM_HPP_ diff --git a/library/phx/input/tracked_device.cpp b/library/phx/input/tracked_device.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0800a10dc0068f407972ce018bb7a60a2d17636a --- /dev/null +++ b/library/phx/input/tracked_device.cpp @@ -0,0 +1,139 @@ +//------------------------------------------------------------------------------ +// Project Phoenix +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualization Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the 3-Clause BSD License (the "License"); +// you may not use this file except in compliance with the License. +// See the file LICENSE for the full text. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include "phx/input/tracked_device.hpp" + +#include <vector> + +#include "phx/core/entity.hpp" +#include "phx/core/logger.hpp" + +namespace phx { + +vr::IVRSystem* TrackedDevice::vr_system_ = nullptr; +int TrackedDevice::reference_counter_ = 0; +std::mutex TrackedDevice::openVR_init_mutex_; +TrackedDevice::OpenVREventDistributer TrackedDevice::vr_event_distributer; +std::vector<vr::TrackedDevicePose_t> TrackedDevice::last_wait_get_poses_; + +TrackedDevice::TrackedDevice() { + openVR_init_mutex_.lock(); + if (vr_system_ == nullptr) { + vr::EVRInitError init_error = vr::VRInitError_None; + vr_system_ = vr::VR_Init(&init_error, vr::VRApplication_Scene); + if (vr_system_ == nullptr || init_error != vr::VRInitError_None) { + error("OpenVR cannot be initialized with error-code: {}", init_error); + throw std::runtime_error("OpenVR cannot be initialized!"); + } + } + vr_event_distributer.AddDevice(this); + reference_counter_++; + openVR_init_mutex_.unlock(); +} + +TrackedDevice::~TrackedDevice() { + openVR_init_mutex_.lock(); + reference_counter_--; + if (reference_counter_ == 0) { + vr::VR_Shutdown(); + vr_system_ = nullptr; + } + vr_event_distributer.RemoveDevice(this); + openVR_init_mutex_.unlock(); +} + +void TrackedDevice::Update() { + if (id_ == vr::k_unTrackedDeviceIndexInvalid) return; + + vr::TrackedDevicePose_t tracked_device_pose; + if (last_wait_get_poses_.size() == vr::k_unMaxTrackedDeviceCount) { + // the HMD has aquired the poses of all devices with WaitGetPoses() + tracked_device_pose = last_wait_get_poses_[id_]; + } else { + // this tracked device has to get its pose itself directly from openVR + // this method however has a higher lag + vr::VRControllerState_t controller_state; + vr_system_->GetControllerStateWithPose( + vr::TrackingUniverseStanding, id_, &controller_state, + sizeof(controller_state), &tracked_device_pose); + } + if (tracked_device_pose.bPoseIsValid) { + pose_ = TransformToGlmMatrix(tracked_device_pose.mDeviceToAbsoluteTracking); + } else { + debug( + "[TrackedDevice] Tracked Device pose is invalid, device is marked as " + "invalid. The actual implementation has to find another valid index"); + id_ = vr::k_unTrackedDeviceIndexInvalid; + } + + vr_event_distributer.Update(); +} + +glm::mat4 TrackedDevice::GetPose() const { return pose_; } + +bool TrackedDevice::IsActive() const { + return id_ != vr::k_unTrackedDeviceIndexInvalid; +} + +void TrackedDevice::OpenVREventDistributer::AddDevice(TrackedDevice* device) { + tracked_devices_.push_back(device); +} + +void TrackedDevice::OpenVREventDistributer::RemoveDevice( + TrackedDevice* device) { + auto iterator = + std::remove_if(tracked_devices_.begin(), tracked_devices_.end(), + [device](Device* iteratee) { return device == iteratee; }); + tracked_devices_.erase(iterator, tracked_devices_.end()); +} + +void TrackedDevice::OpenVREventDistributer::Update() { + vr::VREvent_t event; + while (vr::VRSystem()->PollNextEvent(&event, sizeof(event))) { + for (TrackedDevice* device : tracked_devices_) { + device->OnOpenVREvent(event); + } + } +} +glm::mat4 TrackedDevice::TransformToGlmMatrix(const vr::HmdMatrix34_t& mat) { + return glm::mat4(mat.m[0][0], mat.m[1][0], mat.m[2][0], 0.0f, mat.m[0][1], + mat.m[1][1], mat.m[2][1], 0.0f, mat.m[0][2], mat.m[1][2], + mat.m[2][2], 0.0f, mat.m[0][3], mat.m[1][3], mat.m[2][3], + 1.0f); +} + +glm::mat4 TrackedDevice::TransformToGlmMatrix(const vr::HmdMatrix44_t& mat) { + return glm::mat4(mat.m[0][0], mat.m[1][0], mat.m[2][0], mat.m[3][0], + mat.m[0][1], mat.m[1][1], mat.m[2][1], mat.m[3][1], + mat.m[0][2], mat.m[1][2], mat.m[2][2], mat.m[3][2], + mat.m[0][3], mat.m[1][3], mat.m[2][3], mat.m[3][3]); +} + +std::vector<vr::TrackedDeviceIndex_t> TrackedDevice::GetDeviceIndicesForClass( + vr::ETrackedDeviceClass device_class) { + std::vector<std::uint32_t> indices(vr::k_unMaxTrackedDeviceCount); + vr::VRSystem()->GetSortedTrackedDeviceIndicesOfClass( + device_class, indices.data(), static_cast<std::uint32_t>(indices.size())); + return indices; +} + +} // namespace phx diff --git a/library/phx/input/tracked_device.hpp b/library/phx/input/tracked_device.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d02805708c3326d5f9e6cbf24954f938b4e8c79f --- /dev/null +++ b/library/phx/input/tracked_device.hpp @@ -0,0 +1,87 @@ +//------------------------------------------------------------------------------ +// Project Phoenix +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualization Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the 3-Clause BSD License (the "License"); +// you may not use this file except in compliance with the License. +// See the file LICENSE for the full text. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#ifndef LIBRARY_PHX_INPUT_TRACKED_DEVICE_HPP_ +#define LIBRARY_PHX_INPUT_TRACKED_DEVICE_HPP_ + +#include <vector> + +#include "phx/input/device.hpp" +#include "phx/resources/types/material.hpp" +#include "phx/resources/types/mesh.hpp" +#include "phx/suppress_warnings.hpp" + +SUPPRESS_WARNINGS_BEGIN +#include "glm/mat4x4.hpp" + +#include "openvr.h" //NOLINT +SUPPRESS_WARNINGS_END + +#include "phx/export.hpp" + +namespace phx { + +class PHOENIX_EXPORT TrackedDevice : public Device { + public: + TrackedDevice(); + virtual ~TrackedDevice(); + + virtual void Update(); + + glm::mat4 GetPose() const; + bool IsActive() const; + + virtual void UpdateDeviceIndex() = 0; + virtual void OnOpenVREvent(const vr::VREvent_t& event) = 0; + + protected: + class OpenVREventDistributer { + public: + void Update(); + void AddDevice(TrackedDevice* device); + void RemoveDevice(TrackedDevice* device); + + private: + std::vector<TrackedDevice*> tracked_devices_; + }; + + static vr::IVRSystem* vr_system_; + static OpenVREventDistributer vr_event_distributer; + static std::vector<vr::TrackedDevicePose_t> last_wait_get_poses_; + glm::mat4 pose_; + + static glm::mat4 TransformToGlmMatrix(const vr::HmdMatrix34_t& mat); + static glm::mat4 TransformToGlmMatrix(const vr::HmdMatrix44_t& mat); + + std::vector<vr::TrackedDeviceIndex_t> GetDeviceIndicesForClass( + vr::ETrackedDeviceClass device_class); + + vr::TrackedDeviceIndex_t id_ = vr::k_unTrackedDeviceIndexInvalid; + + private: + static int reference_counter_; + static std::mutex openVR_init_mutex_; +}; + +} // namespace phx + +#endif // LIBRARY_PHX_INPUT_TRACKED_DEVICE_HPP_ diff --git a/library/phx/input/vr_controller.cpp b/library/phx/input/vr_controller.cpp new file mode 100644 index 0000000000000000000000000000000000000000..97d39c52d8aa704808f833300da0e80049152fa4 --- /dev/null +++ b/library/phx/input/vr_controller.cpp @@ -0,0 +1,141 @@ +//------------------------------------------------------------------------------ +// Project Phoenix +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualization Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the 3-Clause BSD License (the "License"); +// you may not use this file except in compliance with the License. +// See the file LICENSE for the full text. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include "phx/input/vr_controller.hpp" + +#include <string> +#include <vector> + +#include "phx/core/logger.hpp" + +namespace phx { + +VRController::VRController(ControllerSide side) : TrackedDevice(), side_(side) { + VRController::Update(); +} + +void VRController::Update() { + if (id_ == vr::k_unTrackedDeviceIndexInvalid || + GetControllerRoleForTrackedDeviceIndex(id_) != side_) { + UpdateDeviceIndex(); + } + TrackedDevice::Update(); +} + +void VRController::OnOpenVREvent(const vr::VREvent_t& event) { + if (event.trackedDeviceIndex != id_) return; + if (event.eventType == vr::VREvent_ButtonPress) { + button_signal_(static_cast<ButtonId>(event.data.controller.button), + ButtonEvent::BUTTON_PRESSED); + } else if (event.eventType == vr::VREvent_ButtonUnpress) { + button_signal_(static_cast<ButtonId>(event.data.controller.button), + ButtonEvent::BUTTON_RELEASED); + } else if (event.eventType == vr::VREvent_ButtonTouch) { + button_signal_(static_cast<ButtonId>(event.data.controller.button), + ButtonEvent::BUTTON_TOUCH); + } else if (event.eventType == vr::VREvent_ButtonUntouch) { + button_signal_(static_cast<ButtonId>(event.data.controller.button), + ButtonEvent::BUTTON_UNTOUCH); + } +} + +void VRController::UpdateDeviceIndex() { + auto controller_indices = + GetDeviceIndicesForClass(vr::TrackedDeviceClass_Controller); + id_ = vr::k_unTrackedDeviceIndexInvalid; + for (auto index : controller_indices) { + if (index != vr::k_unTrackedDeviceIndex_Hmd && + GetControllerRoleForTrackedDeviceIndex(index) == side_) { + id_ = index; + return; + } + } +} + +VRController::ControllerSide VRController::GetSide() const { return side_; } + +vr::RenderModel_t* VRController::GetModel() { + if (id_ == vr::k_unTrackedDeviceIndexInvalid) { + warn("Try to obtain controller model of an invalid controller"); + return nullptr; + } + + std::string rendermodel_name; + rendermodel_name.resize(vr::k_unMaxPropertyStringSize); + + vr::VRSystem()->GetStringTrackedDeviceProperty( + id_, vr::Prop_RenderModelName_String, &rendermodel_name[0], + vr::k_unMaxPropertyStringSize); + + vr::RenderModel_t* model = nullptr; + while (vr::VRRenderModels()->LoadRenderModel_Async( + &rendermodel_name[0], &model) == vr::VRRenderModelError_Loading) { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + + return model; +} + +boost::signals2::connection VRController::RegisterButtonSignal( + const std::function<void(ButtonId, ButtonEvent)>& callback) { + return button_signal_.connect(callback); +} + +glm::vec2 VRController::GetAxesValue(AxesId axes_id) { + if (!IsActive()) return glm::vec2(0.0f); + + vr::TrackedDevicePose_t tracked_device_pose; + vr::VRControllerState_t controller_state; + vr_system_->GetControllerStateWithPose( + vr::TrackingUniverseStanding, id_, &controller_state, + sizeof(controller_state), &tracked_device_pose); + + for (uint32_t i = 0; i < vr::k_unControllerStateAxisCount; i++) { + vr::EVRControllerAxisType type = static_cast<vr::EVRControllerAxisType>( + vr_system_->GetInt32TrackedDeviceProperty( + id_, static_cast<vr::ETrackedDeviceProperty>( + vr::Prop_Axis0Type_Int32 + i))); + if (type == static_cast<vr::EVRControllerAxisType>(axes_id)) { + vr::VRControllerAxis_t axes = controller_state.rAxis[i]; + return glm::vec2(axes.x, axes.y); + } + } + warn("VRController device does not provide the requested axes: {} ", + vr_system_->GetControllerAxisTypeNameFromEnum( + static_cast<vr::EVRControllerAxisType>(axes_id))); + return glm::vec2(0.0f); +} + +VRController::ControllerSide +VRController::GetControllerRoleForTrackedDeviceIndex( + vr::TrackedDeviceIndex_t device_index) const { + switch (vr_system_->GetControllerRoleForTrackedDeviceIndex(device_index)) { + case vr::TrackedControllerRole_LeftHand: + return LEFT_CONTROLLER; + case vr::TrackedControllerRole_RightHand: + return RIGHT_CONTROLLER; + case vr::TrackedControllerRole_Invalid: + break; + } + return INVALID_CONTROLLER; +} +} // namespace phx diff --git a/library/phx/input/vr_controller.hpp b/library/phx/input/vr_controller.hpp new file mode 100644 index 0000000000000000000000000000000000000000..e9402b97223a14dd81f67606f35316c9be572e9a --- /dev/null +++ b/library/phx/input/vr_controller.hpp @@ -0,0 +1,106 @@ +//------------------------------------------------------------------------------ +// Project Phoenix +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualization Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the 3-Clause BSD License (the "License"); +// you may not use this file except in compliance with the License. +// See the file LICENSE for the full text. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#ifndef LIBRARY_PHX_INPUT_VR_CONTROLLER_HPP_ +#define LIBRARY_PHX_INPUT_VR_CONTROLLER_HPP_ + +#include <memory> + +#define BOOST_BIND_NO_PLACEHOLDERS +// otherwise boosts _ placeholders conflict with trompeloeil ones +#include "boost/signals2/connection.hpp" +#include "boost/signals2/signal.hpp" + +#include "phx/input/tracked_device.hpp" +#include "phx/resources/types/material.hpp" +#include "phx/resources/types/mesh.hpp" +#include "phx/suppress_warnings.hpp" + +SUPPRESS_WARNINGS_BEGIN +#include "glm/mat4x4.hpp" + +#include "openvr.h" //NOLINT +SUPPRESS_WARNINGS_END + +#include "phx/export.hpp" + +namespace phx { + +class OpenVRResourceLoader; + +class PHOENIX_EXPORT VRController : public TrackedDevice { + public: + enum ControllerSide { + RIGHT_CONTROLLER = vr::TrackedControllerRole_RightHand, + LEFT_CONTROLLER = vr::TrackedControllerRole_LeftHand, + INVALID_CONTROLLER = vr::TrackedControllerRole_Invalid + }; + + typedef vr::EVRButtonId ButtonId; + + enum ButtonEvent { + BUTTON_PRESSED, + BUTTON_RELEASED, + BUTTON_TOUCH, + BUTTON_UNTOUCH + }; + + enum AxesId { + AXES_TRACKPAD = vr::k_eControllerAxis_TrackPad, + AXES_JOYSTICK = vr::k_eControllerAxis_Joystick, + AXES_TRIGGER = vr::k_eControllerAxis_Trigger + }; + + explicit VRController(ControllerSide side); + + virtual ~VRController() = default; + + void Update() override; + void OnOpenVREvent(const vr::VREvent_t& event) override; + + void UpdateDeviceIndex() override; + + ControllerSide GetSide() const; + + vr::RenderModel_t* GetModel(); + + boost::signals2::connection RegisterButtonSignal( + const std::function<void(ButtonId, ButtonEvent)>& callback); + + // x ranges from -1.0 to 1.0 for joysticks and track pads and from 0.0 to 1.0 + // for triggers were 0 is fully released. + // y ranges from -1.0 to 1.0 for joysticks and track pads and is always 0.0 + // for triggers. + glm::vec2 GetAxesValue(AxesId id); + + private: + ControllerSide GetControllerRoleForTrackedDeviceIndex( + vr::TrackedDeviceIndex_t device_index) const; + + boost::signals2::signal<void(ButtonId, ButtonEvent)> button_signal_; + + ControllerSide side_; +}; + +} // namespace phx + +#endif // LIBRARY_PHX_INPUT_VR_CONTROLLER_HPP_ diff --git a/library/phx/rendering/auxiliary/splash_screen.hpp b/library/phx/rendering/auxiliary/splash_screen.hpp index 2a9007e7b2a870d788a5492fef07de605364332a..56faa94f741e3296f50dc891a5fe0f7d92428bbd 100644 --- a/library/phx/rendering/auxiliary/splash_screen.hpp +++ b/library/phx/rendering/auxiliary/splash_screen.hpp @@ -38,7 +38,6 @@ namespace phx { -SUPPRESS_WARNINGS_BEGIN_PADDED class PHOENIX_EXPORT SplashScreen : public System { public: SplashScreen() = delete; @@ -65,7 +64,6 @@ class PHOENIX_EXPORT SplashScreen : public System { std::mutex load_progress_mutex_; float load_progress_ = 0.f; }; -SUPPRESS_WARNINGS_END } // namespace phx diff --git a/library/phx/rendering/backend/render_target.hpp b/library/phx/rendering/backend/render_target.hpp index be07213d041ca9e41aec02c17b72d5e00f56090f..bed8eda040f06f9c5916be24de53b12962be7908 100644 --- a/library/phx/rendering/backend/render_target.hpp +++ b/library/phx/rendering/backend/render_target.hpp @@ -43,7 +43,6 @@ SUPPRESS_WARNINGS_END namespace phx { -SUPPRESS_WARNINGS_BEGIN_PADDED class PHOENIX_EXPORT RenderTarget : public Component, public gl::framebuffer { public: RenderTarget() = delete; @@ -69,7 +68,6 @@ class PHOENIX_EXPORT RenderTarget : public Component, public gl::framebuffer { glm::uvec2 dimensions_; }; -SUPPRESS_WARNINGS_END } // namespace phx diff --git a/library/phx/rendering/backend/shader_program.hpp b/library/phx/rendering/backend/shader_program.hpp index 93c6d7a56319bc436740a4e938fba132490a00ec..679130847e0cc4b611c486eb881fff57b752d819 100644 --- a/library/phx/rendering/backend/shader_program.hpp +++ b/library/phx/rendering/backend/shader_program.hpp @@ -37,19 +37,15 @@ SUPPRESS_WARNINGS_END #include "gl/program.hpp" #include "gl/shader.hpp" +#include "phx/export.hpp" #include "phx/rendering/components/transform.hpp" #include "phx/rendering/render_passes/render_pass.hpp" +#include "phx/resources/resource_pointer.hpp" #include "phx/resources/types/mesh.hpp" #include "phx/resources/types/shader_source.hpp" -#include "phx/resources/resource_pointer.hpp" -#include "phx/export.hpp" namespace phx { -// TODO(@all) Will padding waste too much memory? How many shaders will we -// have? -SUPPRESS_WARNINGS_BEGIN_PADDED - class PHOENIX_EXPORT ShaderProgram : public gl::program { public: enum ShaderChannel { @@ -71,7 +67,7 @@ class PHOENIX_EXPORT ShaderProgram : public gl::program { ShaderProgram& operator=(ShaderProgram&&) = default; bool SetShader(ShaderChannel channel, - ResourcePointer<ShaderSource> shader_source); + ResourcePointer<ShaderSource> shader_source); template <typename T> void SetUniform(const std::string& name, T value) { @@ -91,8 +87,6 @@ class PHOENIX_EXPORT ShaderProgram : public gl::program { std::array<ShaderHandle, MAX_SHADER_CHANNEL> shaders_; }; -SUPPRESS_WARNINGS_END - } // namespace phx #endif // LIBRARY_PHX_RENDERING_BACKEND_SHADER_PROGRAM_HPP_ diff --git a/library/phx/rendering/components/light.hpp b/library/phx/rendering/components/light.hpp index e8abc925810b1d63db905fad6a885e2843d2cb90..9c0403289ad1584c5ad8463b233cb6a001917679 100644 --- a/library/phx/rendering/components/light.hpp +++ b/library/phx/rendering/components/light.hpp @@ -36,8 +36,6 @@ SUPPRESS_WARNINGS_END namespace phx { -SUPPRESS_WARNINGS_BEGIN_PADDED - class PHOENIX_EXPORT Light final : public Component { public: enum class Type { @@ -80,8 +78,6 @@ class PHOENIX_EXPORT Light final : public Component { float spot_angle_ = 45.0f; // Only for spot lights. }; -SUPPRESS_WARNINGS_END - } // namespace phx #endif // LIBRARY_PHX_RENDERING_COMPONENTS_LIGHT_HPP_ diff --git a/library/phx/rendering/components/mesh_handle.cpp b/library/phx/rendering/components/mesh_handle.cpp index 14041b3414920d201f59526cee2f4cd310acb139..85fd32a7fcc2feea41f9f8f115e816a67c71fff3 100644 --- a/library/phx/rendering/components/mesh_handle.cpp +++ b/library/phx/rendering/components/mesh_handle.cpp @@ -32,10 +32,6 @@ void MeshHandle::SetMesh(ResourcePointer<Mesh> mesh) { mesh_ = mesh; } ResourcePointer<Mesh> MeshHandle::GetMesh() const { return mesh_; } -void MeshHandle::SetWireframeMode(bool enabled) { wireframe_mode_ = enabled; } - -bool MeshHandle::GetWireframeMode() const { return wireframe_mode_; } - std::string MeshHandle::ToString() const { if (mesh_ == nullptr) return GetName() + " (MeshHandle <empty>)"; diff --git a/library/phx/rendering/components/mesh_handle.hpp b/library/phx/rendering/components/mesh_handle.hpp index 5bbf59c677ff2ea2d6d9fa9160b361d38e603e7d..9ad2de1e10c9dbfd2f82b9636795000f366c2181 100644 --- a/library/phx/rendering/components/mesh_handle.hpp +++ b/library/phx/rendering/components/mesh_handle.hpp @@ -25,10 +25,10 @@ #include <string> #include "phx/core/component.hpp" -#include "phx/resources/types/mesh.hpp" +#include "phx/export.hpp" #include "phx/resources/resource_pointer.hpp" +#include "phx/resources/types/mesh.hpp" #include "phx/utility/aspects/nameable.hpp" -#include "phx/export.hpp" namespace phx { /** @@ -36,14 +36,11 @@ namespace phx { */ class PHOENIX_EXPORT MeshHandle final : public Component, public Nameable { public: - virtual ~MeshHandle() = default; + ~MeshHandle() override = default; void SetMesh(ResourcePointer<Mesh> mesh); ResourcePointer<Mesh> GetMesh() const; - void SetWireframeMode(bool enabled); - bool GetWireframeMode() const; - std::string ToString() const override; protected: @@ -57,7 +54,6 @@ class PHOENIX_EXPORT MeshHandle final : public Component, public Nameable { private: ResourcePointer<Mesh> mesh_{nullptr}; - bool wireframe_mode_ = false; }; } // namespace phx diff --git a/library/phx/rendering/components/mesh_render_settings.cpp b/library/phx/rendering/components/mesh_render_settings.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0a2907d581bad4d411c712ed357092391673441c --- /dev/null +++ b/library/phx/rendering/components/mesh_render_settings.cpp @@ -0,0 +1,39 @@ +//------------------------------------------------------------------------------ +// Project Phoenix +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualization Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the 3-Clause BSD License (the "License"); +// you may not use this file except in compliance with the License. +// See the file LICENSE for the full text. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include "phx/rendering/components/mesh_render_settings.hpp" + +namespace phx { + +const MeshRenderSettings MeshRenderSettings::default_settings_; + +void MeshRenderSettings::SetWireframeMode(const bool enabled) { + wireframe_mode_ = enabled; +} + +bool MeshRenderSettings::GetWireframeMode() const { return wireframe_mode_; } + +const MeshRenderSettings* MeshRenderSettings::GetDefault() { + return &default_settings_; +} + +} // namespace phx diff --git a/library/phx/rendering/components/mesh_render_settings.hpp b/library/phx/rendering/components/mesh_render_settings.hpp new file mode 100644 index 0000000000000000000000000000000000000000..b35f1710e2ba330fad793266883a2603bae5d593 --- /dev/null +++ b/library/phx/rendering/components/mesh_render_settings.hpp @@ -0,0 +1,48 @@ +//------------------------------------------------------------------------------ +// Project Phoenix +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualization Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the 3-Clause BSD License (the "License"); +// you may not use this file except in compliance with the License. +// See the file LICENSE for the full text. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ +#ifndef LIBRARY_PHX_RENDERING_COMPONENTS_MESH_RENDER_SETTINGS_HPP_ +#define LIBRARY_PHX_RENDERING_COMPONENTS_MESH_RENDER_SETTINGS_HPP_ + +#include "phx/core/component.hpp" +#include "phx/export.hpp" + +namespace phx { +/** + * A component holding a simple pointer to a mesh + */ +class PHOENIX_EXPORT MeshRenderSettings final : public Component { + public: + virtual ~MeshRenderSettings() = default; + + void SetWireframeMode(bool enabled); + bool GetWireframeMode() const; + + static const MeshRenderSettings* GetDefault(); + + private: + bool wireframe_mode_ = false; + + static const MeshRenderSettings default_settings_; +}; +} // namespace phx + +#endif // LIBRARY_PHX_RENDERING_COMPONENTS_MESH_RENDER_SETTINGS_HPP_ diff --git a/library/phx/rendering/components/transform.hpp b/library/phx/rendering/components/transform.hpp index b67c14855ba21e89aeb98eb9f80fda9f625a0fc9..c9e4f0c30f96753ad51946118f3e3d0628d0407f 100644 --- a/library/phx/rendering/components/transform.hpp +++ b/library/phx/rendering/components/transform.hpp @@ -44,7 +44,7 @@ namespace phx { class PHOENIX_EXPORT Transform : public Component, public Hierarchical<Transform> { public: - virtual ~Transform() = default; + ~Transform() override = default; // Getting/Setting Local transform const glm::vec3& GetLocalTranslation() const; diff --git a/library/phx/rendering/render_passes/geometry_pass.cpp b/library/phx/rendering/render_passes/geometry_pass.cpp index ca4eef6be568b55fbbaa72b8cffc8070f04d4460..aeaf4a167e430320f97bdc6b11101f152b261998 100644 --- a/library/phx/rendering/render_passes/geometry_pass.cpp +++ b/library/phx/rendering/render_passes/geometry_pass.cpp @@ -35,9 +35,9 @@ #include "phx/core/logger.hpp" #include "phx/rendering/components/transform.hpp" +#include "phx/resources/resource_utils.hpp" #include "phx/resources/types/mesh.hpp" #include "phx/resources/types/shader_source.hpp" -#include "phx/resources/resource_utils.hpp" namespace phx { @@ -67,6 +67,10 @@ void GeometryPass::UploadMeshData( mesh_cache_.clear(); RenderOffset offset{0, 0}; for (const auto& instance : rendering_instances) { + if (instance.material != nullptr) { + instance.material->UploadTextures(); + } + Mesh* mesh = instance.mesh; if (mesh_cache_.find(mesh) != mesh_cache_.end()) { continue; @@ -223,9 +227,15 @@ void GeometryPass::Draw(const RenderingInstance& rendering_instance) { SetTransformShaderUniforms(model_matrix, view_matrix, projection_matrix); SetMaterialShaderUniforms(rendering_instance.material); + const MeshRenderSettings* render_settings = + rendering_instance.mesh_render_settings; + if (render_settings == nullptr) { + render_settings = MeshRenderSettings::GetDefault(); + } + glEnable(GL_DEPTH_TEST); glPolygonMode(GL_FRONT_AND_BACK, - rendering_instance.wireframe_mode ? GL_LINE : GL_FILL); + render_settings->GetWireframeMode() ? GL_LINE : GL_FILL); glDrawElements( GL_TRIANGLES, static_cast<GLsizei>(rendering_instance.mesh->GetIndices().size()), @@ -291,41 +301,32 @@ void GeometryPass::SetLightShaderUniforms() { } } -void GeometryPass::SetMaterialShaderUniforms(Material* material) { - glm::uvec4 texture_toggle(0, 0, 0, 0); - glm::vec3 ambient_color(1, 0, 0); - glm::vec3 diffuse_color(1, 0, 0); - glm::vec3 specular_color(1, 0, 0); - float shininess = 1.0f; - if (material != nullptr) { - ambient_color = material->GetAmbientColor(); - diffuse_color = material->GetDiffuseColor(); - specular_color = material->GetSpecularColor(); - - texture_toggle = - glm::uvec4(material->GetAmbientTexture() != nullptr ? 1u : 0u, - material->GetDiffuseTexture() != nullptr ? 1u : 0u, - material->GetSpecularTexture() != nullptr ? 1u : 0u, 0u); - if (material->GetAmbientTexture()) - shader_program_->set_uniform_handle( - shader_program_->uniform_location("material.ambient_tex"), - gl::texture_handle(*material->GetAmbientTexture())); - if (material->GetDiffuseTexture()) - shader_program_->set_uniform_handle( - shader_program_->uniform_location("material.diffuse_tex"), - gl::texture_handle(*material->GetDiffuseTexture())); - if (material->GetSpecularTexture()) - shader_program_->set_uniform_handle( - shader_program_->uniform_location("material.specular_tex"), - gl::texture_handle(*material->GetSpecularTexture())); - - shininess = material->GetShininess(); - } +void GeometryPass::SetMaterialShaderUniforms(const Material* material) { + if (material == nullptr) material = Material::GetDefault(); + + const glm::uvec4 texture_toggle = + glm::uvec4(material->GetAmbientTexture() != nullptr ? 1u : 0u, + material->GetDiffuseTexture() != nullptr ? 1u : 0u, + material->GetSpecularTexture() != nullptr ? 1u : 0u, 0u); + if (material->GetAmbientTexture()) + shader_program_->set_uniform_handle( + shader_program_->uniform_location("material.ambient_tex"), + gl::texture_handle(*material->GetAmbientTexture())); + if (material->GetDiffuseTexture()) + shader_program_->set_uniform_handle( + shader_program_->uniform_location("material.diffuse_tex"), + gl::texture_handle(*material->GetDiffuseTexture())); + if (material->GetSpecularTexture()) + shader_program_->set_uniform_handle( + shader_program_->uniform_location("material.specular_tex"), + gl::texture_handle(*material->GetSpecularTexture())); + shader_program_->SetUniform("material.texture_toggle", texture_toggle); - shader_program_->SetUniform("material.ambient", ambient_color); - shader_program_->SetUniform("material.diffuse", diffuse_color); - shader_program_->SetUniform("material.specular", specular_color); - shader_program_->SetUniform("material.shininess", shininess); + shader_program_->SetUniform("material.ambient", material->GetAmbientColor()); + shader_program_->SetUniform("material.diffuse", material->GetDiffuseColor()); + shader_program_->SetUniform("material.specular", + material->GetSpecularColor()); + shader_program_->SetUniform("material.shininess", material->GetShininess()); } bool GeometryPass::IsValid() const { diff --git a/library/phx/rendering/render_passes/geometry_pass.hpp b/library/phx/rendering/render_passes/geometry_pass.hpp index bd2edb0c40e650bf294feac2a7834babe3c53570..a02733159fc998a2b8547c5565932bbf118e1203 100644 --- a/library/phx/rendering/render_passes/geometry_pass.hpp +++ b/library/phx/rendering/render_passes/geometry_pass.hpp @@ -36,21 +36,20 @@ SUPPRESS_WARNINGS_BEGIN #include "gl/vertex_array.hpp" SUPPRESS_WARNINGS_END +#include "phx/export.hpp" #include "phx/rendering/backend/render_target.hpp" #include "phx/rendering/backend/shader_program.hpp" #include "phx/rendering/components/light.hpp" #include "phx/rendering/components/material_handle.hpp" +#include "phx/rendering/components/mesh_render_settings.hpp" #include "phx/rendering/components/projection.hpp" #include "phx/rendering/components/transform.hpp" #include "phx/rendering/render_passes/render_pass.hpp" -#include "phx/resources/types/mesh.hpp" #include "phx/resources/resource_manager.hpp" -#include "phx/export.hpp" +#include "phx/resources/types/mesh.hpp" namespace phx { -SUPPRESS_WARNINGS_BEGIN_PADDED - class PHOENIX_EXPORT GeometryPass : public RenderPass { public: explicit GeometryPass(RenderTarget* render_target); @@ -64,7 +63,7 @@ class PHOENIX_EXPORT GeometryPass : public RenderPass { Mesh* mesh = nullptr; Material* material = nullptr; Transform* transform = nullptr; - bool wireframe_mode = false; + MeshRenderSettings* mesh_render_settings = nullptr; }; struct RenderOffset { std::size_t vertex_offset; @@ -87,8 +86,6 @@ class PHOENIX_EXPORT GeometryPass : public RenderPass { bool IsValid() const; private: - // TODO(@all) Will padding waste too much memory? - SUPPRESS_WARNINGS_BEGIN_PADDED struct RenderingResource { RenderingResource() = default; RenderingResource(const RenderingResource&) = delete; @@ -107,7 +104,6 @@ class PHOENIX_EXPORT GeometryPass : public RenderPass { unsigned int vertex_buffer_size; unsigned int index_buffer_size; }; - SUPPRESS_WARNINGS_END void SetUpShaders(); void CreateRenderingResource(); @@ -123,7 +119,7 @@ class PHOENIX_EXPORT GeometryPass : public RenderPass { const glm::mat4& view_matrix, const glm::mat4& projection_matrix); void SetLightShaderUniforms(); - void SetMaterialShaderUniforms(Material* material); + void SetMaterialShaderUniforms(const Material* material); std::unique_ptr<ShaderProgram> shader_program_; @@ -136,8 +132,6 @@ class PHOENIX_EXPORT GeometryPass : public RenderPass { RenderTarget* render_target_; }; -SUPPRESS_WARNINGS_END - } // namespace phx #endif // LIBRARY_PHX_RENDERING_RENDER_PASSES_GEOMETRY_PASS_HPP_ diff --git a/library/phx/rendering/rendering_system.cpp b/library/phx/rendering/rendering_system.cpp index bf4ea619bdfdc7660b2ddd95736d9c309a4061d8..78362871ef1b8b0e84f38fe4b3e414d0d0c86fe2 100644 --- a/library/phx/rendering/rendering_system.cpp +++ b/library/phx/rendering/rendering_system.cpp @@ -22,24 +22,20 @@ #include "phx/rendering/rendering_system.hpp" -#include <iostream> #include <memory> #include <string> -#include <tuple> #include <utility> #include <vector> #include "phx/core/engine.hpp" #include "phx/core/logger.hpp" -#include "phx/core/runtime_component.hpp" #include "phx/core/scene.hpp" #include "phx/core/system.hpp" #include "phx/rendering/components/light.hpp" #include "phx/rendering/components/material_handle.hpp" #include "phx/rendering/components/mesh_handle.hpp" -#include "phx/rendering/components/projection.hpp" +#include "phx/rendering/components/mesh_render_settings.hpp" #include "phx/rendering/components/transform.hpp" -#include "phx/resources/types/mesh.hpp" #include "gl/opengl.hpp" @@ -79,14 +75,15 @@ void RenderingSystem::Update(const FrameTimer::TimeInfo&) { auto light = entity->GetFirstComponent<Light>(); auto transform = entity->GetFirstComponent<Transform>(); auto material_handle = entity->GetFirstComponent<MaterialHandle>(); + Material* material = nullptr; + if (material_handle != nullptr) + material = material_handle->GetMaterial().Get(); + auto mesh_render_settings = entity->GetFirstComponent<MeshRenderSettings>(); + if (transform != nullptr) { if (mesh_handle != nullptr) { - Material* material = nullptr; - if (material_handle != nullptr) - material = material_handle->GetMaterial().Get(); rendering_instances.push_back({mesh_handle->GetMesh().Get(), material, - transform, - mesh_handle->GetWireframeMode()}); + transform, mesh_render_settings}); } else if (light != nullptr) { light_transform_pairs.push_back( std::pair<Light*, Transform*>(light, transform)); diff --git a/library/phx/rendering/rendering_system.hpp b/library/phx/rendering/rendering_system.hpp index 6d56ee71d065e304d9482dc023b099d30a87a77a..e0fad0bd36fc21c77b715afca7502e6ad2f093c8 100644 --- a/library/phx/rendering/rendering_system.hpp +++ b/library/phx/rendering/rendering_system.hpp @@ -32,10 +32,10 @@ #include "phx/core/scene.hpp" #include "phx/core/system.hpp" #include "phx/display/display_system.hpp" +#include "phx/export.hpp" #include "phx/rendering/backend/render_target.hpp" -#include "phx/rendering/render_passes/geometry_pass.hpp" #include "phx/rendering/frame_graph.hpp" -#include "phx/export.hpp" +#include "phx/rendering/render_passes/geometry_pass.hpp" namespace phx { @@ -44,7 +44,7 @@ class PHOENIX_EXPORT RenderingSystem : public System { RenderingSystem() = delete; RenderingSystem(const RenderingSystem&) = delete; RenderingSystem(RenderingSystem&&) = default; - virtual ~RenderingSystem() = default; + ~RenderingSystem() override = default; void Update(const FrameTimer::TimeInfo& time_info) override; diff --git a/library/phx/resources/loaders/assimp_model_loader.hpp b/library/phx/resources/loaders/assimp_model_loader.hpp index 9f5abd2073008c220b8b1aea02d50a0cc2b3b5b6..5b7777567c1c0a907194c4866ae77979b001134f 100644 --- a/library/phx/resources/loaders/assimp_model_loader.hpp +++ b/library/phx/resources/loaders/assimp_model_loader.hpp @@ -39,16 +39,15 @@ SUPPRESS_WARNINGS_BEGIN SUPPRESS_WARNINGS_END #include "phx/export.hpp" -#include "phx/resources/types/material.hpp" -#include "phx/resources/types/mesh.hpp" -#include "phx/resources/types/model.hpp" #include "phx/resources/resource_declaration.hpp" #include "phx/resources/resource_load_strategy.hpp" #include "phx/resources/resource_pointer.hpp" +#include "phx/resources/types/material.hpp" +#include "phx/resources/types/mesh.hpp" +#include "phx/resources/types/model.hpp" namespace phx { -SUPPRESS_WARNINGS_BEGIN_PADDED class PHOENIX_EXPORT AssimpModelLoader final : public ResourceLoadStrategy { public: AssimpModelLoader(); @@ -114,7 +113,6 @@ class PHOENIX_EXPORT AssimpModelLoader final : public ResourceLoadStrategy { int last_material_index_ = kInvalidMaterialIndex; std::function<void(float)> on_progress_update_callback_; }; -SUPPRESS_WARNINGS_END template <typename FileSourceType> std::vector<glm::vec3> phx::AssimpModelLoader::LoadVectorData( diff --git a/library/phx/resources/loaders/generic_material_loader.cpp b/library/phx/resources/loaders/generic_material_loader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..425dedc7d4253d45cffa63f30e48f14a7382a7db --- /dev/null +++ b/library/phx/resources/loaders/generic_material_loader.cpp @@ -0,0 +1,51 @@ +//------------------------------------------------------------------------------ +// Project Phoenix +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualization Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the 3-Clause BSD License (the "License"); +// you may not use this file except in compliance with the License. +// See the file LICENSE for the full text. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include "phx/resources/loaders/generic_material_loader.hpp" + +#include <memory> +#include <vector> + +#include "phx/resources/types/material.hpp" + +namespace phx { +std::unique_ptr<Resource> GenericMaterialLoader::Load( + const ResourceDeclaration& declaration) { + auto resource = std::make_unique<phx::Material>(); + + resource->SetName(declaration["material_name"]); + + std::vector<float> diffuseColor = declaration["diffuse_color"]; + resource->SetDiffuseColor( + glm::vec3(diffuseColor[0], diffuseColor[1], diffuseColor[2])); + std::vector<float> specularColor = declaration["specular_color"]; + resource->SetSpecularColor( + glm::vec3(specularColor[0], specularColor[1], specularColor[2])); + std::vector<float> ambientColor = declaration["ambient_color"]; + resource->SetAmbientColor( + glm::vec3(ambientColor[0], ambientColor[1], ambientColor[2])); + float shininess = declaration["shininess"]; + resource->SetShininess(shininess); + + return resource; +} +} // namespace phx diff --git a/tests/test_utilities/dummy_material_generator.hpp b/library/phx/resources/loaders/generic_material_loader.hpp similarity index 55% rename from tests/test_utilities/dummy_material_generator.hpp rename to library/phx/resources/loaders/generic_material_loader.hpp index 746df1f1d199b3887f8f26a600eeb20dfa6b71ab..408f868ea43769429985fd10e136c8a3ec809446 100644 --- a/tests/test_utilities/dummy_material_generator.hpp +++ b/library/phx/resources/loaders/generic_material_loader.hpp @@ -20,43 +20,27 @@ // limitations under the License. //------------------------------------------------------------------------------ -#ifndef TESTS_TEST_UTILITIES_DUMMY_MATERIAL_GENERATOR_HPP_ -#define TESTS_TEST_UTILITIES_DUMMY_MATERIAL_GENERATOR_HPP_ +#ifndef LIBRARY_PHX_RESOURCES_LOADERS_GENERIC_MATERIAL_LOADER_HPP_ +#define LIBRARY_PHX_RESOURCES_LOADERS_GENERIC_MATERIAL_LOADER_HPP_ #include <memory> -#include <utility> -#include <vector> - -#include "phx/suppress_warnings.hpp" - -SUPPRESS_WARNINGS_BEGIN -#include "glm/vec3.hpp" -SUPPRESS_WARNINGS_END #include "phx/resources/resource_load_strategy.hpp" namespace phx { -class Material; - -class DummyMaterialGenerator : public ResourceLoadStrategy { +class GenericMaterialLoader final : public ResourceLoadStrategy { public: - DummyMaterialGenerator() = delete; - DummyMaterialGenerator(glm::vec3 diffuse_color, glm::vec3 specular_color, - glm::vec3 ambient_color, float shininess); - DummyMaterialGenerator(const DummyMaterialGenerator &) = default; - DummyMaterialGenerator(DummyMaterialGenerator &&) = default; - ~DummyMaterialGenerator() = default; + GenericMaterialLoader() = default; + GenericMaterialLoader(const GenericMaterialLoader &) = delete; + GenericMaterialLoader(GenericMaterialLoader &&) = delete; + ~GenericMaterialLoader() = default; - DummyMaterialGenerator &operator=(const DummyMaterialGenerator &) = default; - DummyMaterialGenerator &operator=(DummyMaterialGenerator &&) = default; + GenericMaterialLoader &operator=(const GenericMaterialLoader &) = delete; + GenericMaterialLoader &operator=(GenericMaterialLoader &&) = delete; std::unique_ptr<Resource> Load( const ResourceDeclaration &declaration) override; - - protected: - private: - std::unique_ptr<phx::Material> material_; }; } // namespace phx -#endif // TESTS_TEST_UTILITIES_DUMMY_MATERIAL_GENERATOR_HPP_ +#endif // LIBRARY_PHX_RESOURCES_LOADERS_GENERIC_MATERIAL_LOADER_HPP_ diff --git a/library/phx/resources/loaders/image_loader.hpp b/library/phx/resources/loaders/image_loader.hpp index aaebda26146ff3e81c26ee33369a6dfe93e99961..9e0e61fb4f42d357e1689460a12100069acf2a4b 100644 --- a/library/phx/resources/loaders/image_loader.hpp +++ b/library/phx/resources/loaders/image_loader.hpp @@ -35,7 +35,7 @@ class ImageLoader final : public ResourceLoadStrategy { ImageLoader() = default; ImageLoader(const ImageLoader &) = delete; ImageLoader(ImageLoader &&) = delete; - ~ImageLoader() = default; + ~ImageLoader() override = default; ImageLoader &operator=(const ImageLoader &) = delete; ImageLoader &operator=(ImageLoader &&) = delete; diff --git a/library/phx/resources/loaders/openvr_resource_loader.cpp b/library/phx/resources/loaders/openvr_resource_loader.cpp index 3c24d99ef7846cdd8f5e5e6e9005e0bbf055c9e5..230337716f6077f804325f84ba4bc459cb6d415c 100644 --- a/library/phx/resources/loaders/openvr_resource_loader.cpp +++ b/library/phx/resources/loaders/openvr_resource_loader.cpp @@ -22,18 +22,25 @@ #include "phx/resources/loaders/openvr_resource_loader.hpp" +#include <algorithm> #include <memory> #include <string> +#include <utility> +#include <vector> #include "phx/core/logger.hpp" +#include "phx/input/device_system.hpp" +#include "phx/input/vr_controller.hpp" +#include "phx/resources/resource_manager.hpp" #include "phx/resources/types/material.hpp" namespace phx { -OpenVRResourceLoader::OpenVRResourceLoader(HMD *hmd) : hmd_(hmd) {} +OpenVRResourceLoader::OpenVRResourceLoader(DeviceSystem* device_system) + : device_system_(device_system) {} std::unique_ptr<phx::Resource> OpenVRResourceLoader::Load( - const ResourceDeclaration &declaration) { + const ResourceDeclaration& declaration) { if (declaration.find("OpenVR_type") == declaration.end() || !declaration["OpenVR_type"].is_string()) { warn( @@ -43,15 +50,15 @@ std::unique_ptr<phx::Resource> OpenVRResourceLoader::Load( return nullptr; } std::string type = declaration["OpenVR_type"]; - HMD::Controller controller_side = HMD::Controller::LEFT_CONTROLLER; + VRController::ControllerSide controller_side = VRController::LEFT_CONTROLLER; if (declaration.find("side") != declaration.end() && declaration["side"] == "right") { - controller_side = HMD::Controller::RIGHT_CONTROLLER; + controller_side = VRController::RIGHT_CONTROLLER; } // otherwise default left if (type == "material") { - return hmd_->GetControllerMaterial(controller_side); + return GetMaterial(controller_side); } if (type == "texture") { int texture_id = 0; @@ -64,12 +71,11 @@ std::unique_ptr<phx::Resource> OpenVRResourceLoader::Load( } else { texture_id = declaration["texture_id"]; } - return hmd_->GetControllerTexture(texture_id); + return GetTexture(texture_id); } if (type == "mesh") { - return hmd_->GetControllerMesh(controller_side); + return GetMesh(controller_side); } - warn( "OpenVRResource with OpenVR_type {}, cannot be loaded. Full declaration: " "{}", @@ -77,4 +83,92 @@ std::unique_ptr<phx::Resource> OpenVRResourceLoader::Load( return nullptr; } +vr::RenderModel_t* OpenVRResourceLoader::GetModel( + VRController::ControllerSide side) { + for (auto controller : device_system_->GetDevices<VRController>()) { + if (controller->GetSide() == side) return controller->GetModel(); + } + + warn( + "[OpenVRResourceLoader::GetModel] unable to find controller with side {}", + side == VRController::RIGHT_CONTROLLER ? "right" : "left"); + return nullptr; +} + +std::unique_ptr<phx::Mesh> OpenVRResourceLoader::GetMesh( + VRController::ControllerSide side) { + auto model = GetModel(side); + if (model == nullptr) { + return nullptr; + } + auto mesh = std::make_unique<phx::Mesh>(); + std::vector<glm::vec3> vertices; + std::vector<glm::vec3> normals; + std::vector<glm::vec2> texcoords; + for (std::size_t i = 0; i < model->unVertexCount; i++) { + vertices.push_back(glm::vec3(model->rVertexData[i].vPosition.v[0], + model->rVertexData[i].vPosition.v[1], + model->rVertexData[i].vPosition.v[2])); + normals.push_back(glm::vec3(model->rVertexData[i].vNormal.v[0], + model->rVertexData[i].vNormal.v[1], + model->rVertexData[i].vNormal.v[2])); + texcoords.push_back(glm::vec2(model->rVertexData[i].rfTextureCoord[0], + model->rVertexData[i].rfTextureCoord[1])); + } + std::vector<unsigned int> indices; + for (std::size_t i = 0; i < model->unTriangleCount * 3; i++) { + indices.push_back(model->rIndexData[i]); + } + mesh->SetVertices(std::move(vertices)); + mesh->SetNormals(std::move(normals)); + mesh->SetTextureCoords(std::move(texcoords)); + mesh->SetIndices(std::move(indices)); + return mesh; +} + +std::unique_ptr<Material> OpenVRResourceLoader::GetMaterial( + VRController::ControllerSide side) { + auto model = GetModel(side); + if (model == nullptr) return nullptr; + + auto material = std::make_unique<phx::Material>(); + material->SetAmbientColor(glm::vec3(0.1, 0.1, 0.1)); + material->SetSpecularColor(glm::vec3(0.3, 0.3, 0.3)); + + auto texture = ResourceManager::instance().DeclareResource<Image>( + {{"TYPE", "openVR"}, + {"OpenVR_type", "texture"}, + {"texture_id", model->diffuseTextureId}, + {"side", side == VRController::LEFT_CONTROLLER ? "left" : "right"}}); + texture.Load(); + material->SetDiffuseImage(texture); + + return material; +} + +std::unique_ptr<Image> OpenVRResourceLoader::GetTexture(int id) { + vr::RenderModel_TextureMap_t* texture_map; + while (vr::VRRenderModels()->LoadTexture_Async(id, &texture_map) == + vr::VRRenderModelError_Loading) { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + if (texture_map == nullptr) { + return nullptr; + } + + std::vector<unsigned char> image_data(texture_map->unWidth * + texture_map->unHeight * 4); + std::copy(texture_map->rubTextureMapData, + texture_map->rubTextureMapData + image_data.size(), + image_data.begin()); + auto image = std::make_unique<phx::Image>( + &image_data[0], + std::array<std::size_t, 2>{ + {static_cast<std::size_t>(texture_map->unWidth), + static_cast<std::size_t>(texture_map->unHeight)}}, + 32); + + return image; +} + } // namespace phx diff --git a/library/phx/resources/loaders/openvr_resource_loader.hpp b/library/phx/resources/loaders/openvr_resource_loader.hpp index 357ff8ad9489e2ac269f962b8a48979d884c26d9..3ccd32ea744e23e21de87a83617d63d739bec047 100644 --- a/library/phx/resources/loaders/openvr_resource_loader.hpp +++ b/library/phx/resources/loaders/openvr_resource_loader.hpp @@ -33,17 +33,18 @@ SUPPRESS_WARNINGS_BEGIN SUPPRESS_WARNINGS_END #include "phx/export.hpp" -#include "phx/display/hmd.hpp" -#include "phx/resources/types/mesh.hpp" +#include "phx/input/device_system.hpp" +#include "phx/input/vr_controller.hpp" #include "phx/resources/resource_declaration.hpp" #include "phx/resources/resource_load_strategy.hpp" +#include "phx/resources/types/mesh.hpp" namespace phx { class Mesh; class PHOENIX_EXPORT OpenVRResourceLoader final : public ResourceLoadStrategy { public: - explicit OpenVRResourceLoader(HMD *hmd); + explicit OpenVRResourceLoader(DeviceSystem *device_system); OpenVRResourceLoader(const OpenVRResourceLoader &) = delete; OpenVRResourceLoader(OpenVRResourceLoader &&) = delete; ~OpenVRResourceLoader() override = default; @@ -53,8 +54,12 @@ class PHOENIX_EXPORT OpenVRResourceLoader final : public ResourceLoadStrategy { std::unique_ptr<Resource> Load(const ResourceDeclaration &file_name) override; - private: - HMD *hmd_; + vr::RenderModel_t *GetModel(VRController::ControllerSide side); + std::unique_ptr<Mesh> GetMesh(VRController::ControllerSide side); + std::unique_ptr<Material> GetMaterial(VRController::ControllerSide side); + std::unique_ptr<Image> GetTexture(int id); + + DeviceSystem *device_system_ = nullptr; }; } // namespace phx diff --git a/library/phx/resources/loaders/shader_loader.hpp b/library/phx/resources/loaders/shader_loader.hpp index a23444fa3d7df4e823a3a31cd8fe9341c8ef8c96..3f2a816001083ab8ddd73849bd48183641fc608c 100644 --- a/library/phx/resources/loaders/shader_loader.hpp +++ b/library/phx/resources/loaders/shader_loader.hpp @@ -35,7 +35,7 @@ class ShaderLoader final : public ResourceLoadStrategy { ShaderLoader() = default; ShaderLoader(const ShaderLoader&) = delete; ShaderLoader(ShaderLoader&&) = delete; - ~ShaderLoader() = default; + ~ShaderLoader() override = default; ShaderLoader& operator=(const ShaderLoader&) = delete; ShaderLoader& operator=(ShaderLoader&&) = delete; diff --git a/library/phx/resources/resource_manager.cpp b/library/phx/resources/resource_manager.cpp index 938b6dd7f128ea37b08cc1566d7ea94697f9bc35..34702a628db713a454f71cef47de4e4339b0e058 100644 --- a/library/phx/resources/resource_manager.cpp +++ b/library/phx/resources/resource_manager.cpp @@ -27,6 +27,7 @@ #include <utility> #include "phx/resources/loaders/assimp_model_loader.hpp" +#include "phx/resources/loaders/generic_material_loader.hpp" #include "phx/resources/loaders/image_loader.hpp" #include "phx/resources/loaders/shader_loader.hpp" @@ -37,6 +38,7 @@ ResourceManager::ResourceManager() { this->RegisterMeshResourceExtensions(); this->RegisterShaderResourceExtensions(); this->RegisterImageResourceExtensions(); + this->RegisterResourceGenerators(); } void ResourceManager::RegisterResourceType( @@ -91,4 +93,9 @@ void ResourceManager::RegisterImageResourceExtensions() { this->RegisterResourceType(".png", std::make_unique<ImageLoader>()); } +void ResourceManager::RegisterResourceGenerators() { + this->RegisterResourceType("GEN_MATERIAL", + std::make_unique<GenericMaterialLoader>()); +} + } // namespace phx diff --git a/library/phx/resources/resource_manager.hpp b/library/phx/resources/resource_manager.hpp index 54cf258ca1a0b8cc9eb1c0c6ded2134152216a6f..a701b0673af3d49084b99ac3f27b6c6674ed61b6 100644 --- a/library/phx/resources/resource_manager.hpp +++ b/library/phx/resources/resource_manager.hpp @@ -28,12 +28,12 @@ #include <string> #include "phx/core/logger.hpp" +#include "phx/export.hpp" #include "phx/resources/resource_declaration.hpp" #include "phx/resources/resource_load_strategy.hpp" #include "phx/resources/resource_pointer.hpp" #include "phx/resources/resource_proxy.hpp" #include "phx/utility/aspects/singleton.hpp" -#include "phx/export.hpp" namespace phx { /** @@ -73,6 +73,8 @@ class PHOENIX_EXPORT ResourceManager final : public singleton<ResourceManager> { void RegisterMeshResourceExtensions(); void RegisterImageResourceExtensions(); + void RegisterResourceGenerators(); + std::map<std::string, std::unique_ptr<ResourceLoadStrategy>> loaders_by_type_; std::map<std::size_t, std::unique_ptr<ResourceProxy>> resources_; }; diff --git a/library/phx/resources/resource_utils.cpp b/library/phx/resources/resource_utils.cpp index e8e82fcb5356defa3988ae2696281f75a7054cc9..f79aefd817b2b90614cd941e4d88dbbb7ffb6351 100644 --- a/library/phx/resources/resource_utils.cpp +++ b/library/phx/resources/resource_utils.cpp @@ -22,13 +22,57 @@ #include "phx/resources/resource_utils.hpp" #include <algorithm> +#include <cassert> +#include <fstream> #include <locale> #include <string> +#include <vector> + +#include "boost/filesystem.hpp" #include "phx/resources/resource_manager.hpp" #include "phx/resources_path.hpp" namespace phx { +namespace { +std::vector<std::string> default_resource_search_paths = { + {std::string("./"), resources_root}}; +} + +std::vector<std::string> ResourceUtils::resource_search_paths_{ + default_resource_search_paths}; + +ResourceDeclaration ResourceUtils::GenericMaterialDeclaration( + const std::string& material_name, glm::vec3 diffuse_color, + glm::vec3 specular_color, glm::vec3 ambient_color, float shininess) { + ResourceDeclaration declaration = {{"TYPE", "GEN_MATERIAL"}}; + declaration["material_name"] = material_name; + + declaration["diffuse_color"] = {diffuse_color[0], diffuse_color[1], + diffuse_color[2]}; + declaration["specular_color"] = {specular_color[0], specular_color[1], + specular_color[2]}; + declaration["ambient_color"] = {ambient_color[0], ambient_color[1], + ambient_color[2]}; + declaration["shininess"] = shininess; + return declaration; +} + +void ResourceUtils::AddResourceSearchPath(const std::string& path_to_add) { + assert(!path_to_add.empty()); + auto p = (path_to_add.back() == '/' ? path_to_add + : path_to_add + std::string("/")); + resource_search_paths_.push_back(p); +} + +void ResourceUtils::ResetResourceSearchPath() { + resource_search_paths_ = default_resource_search_paths; +} + +std::vector<std::string> ResourceUtils::GetResourceSearchPath() { + return resource_search_paths_; +} + ResourceDeclaration ResourceUtils::DeclarationFromFile( const std::string& file_name, nlohmann::json additional_info /*= {}*/, bool absolute_path /*= false*/) { @@ -36,7 +80,7 @@ ResourceDeclaration ResourceUtils::DeclarationFromFile( if (absolute_path) { declaration["file_name"] = file_name; } else { - declaration["file_name"] = resources_root + file_name; + declaration["file_name"] = FindFileInSearchPath(file_name); } for (nlohmann::json::iterator it = additional_info.begin(); it != additional_info.end(); ++it) { @@ -44,6 +88,7 @@ ResourceDeclaration ResourceUtils::DeclarationFromFile( } return declaration; } + std::string ResourceUtils::ExtractFileExtension(const std::string& file_name) { std::string extension = file_name.substr(file_name.rfind('.')); std::transform(extension.begin(), extension.end(), extension.begin(), @@ -52,4 +97,24 @@ std::string ResourceUtils::ExtractFileExtension(const std::string& file_name) { return extension; } +ResourcePointer<Material> ResourceUtils::LoadGenericMaterial( + const std::string& material_name, glm::vec3 diffuse_color, + glm::vec3 specular_color /*= Material::GetDefault()->GetSpecularColor()*/, + glm::vec3 ambient_color /*= Material::GetDefault()->GetAmbientColor()*/, + float shininess /*= Material::GetDefault()->GetShininess()*/) { + auto res_ptr = phx::ResourceManager::instance().DeclareResource<Material>( + GenericMaterialDeclaration(material_name, diffuse_color, specular_color, + ambient_color, shininess)); + res_ptr.Load(); + return res_ptr; +} + +std::string ResourceUtils::FindFileInSearchPath(const std::string& file_name) { + for (auto& entry : resource_search_paths_) { + auto full_name = entry + file_name; + if (boost::filesystem::exists(full_name.c_str())) return full_name; + } + return ""; +} + } // namespace phx diff --git a/library/phx/resources/resource_utils.hpp b/library/phx/resources/resource_utils.hpp index 8f18bc1fa3c17ae0db1dc93a83ed4aebab8d8236..06d925854fe6c76ac45f36469e476a742b5510c8 100644 --- a/library/phx/resources/resource_utils.hpp +++ b/library/phx/resources/resource_utils.hpp @@ -26,29 +26,50 @@ #include <map> #include <memory> #include <string> +#include <vector> #include "json.hpp" #include "phx/core/logger.hpp" +#include "phx/export.hpp" #include "phx/resources/resource_declaration.hpp" #include "phx/resources/resource_load_strategy.hpp" #include "phx/resources/resource_manager.hpp" #include "phx/resources/resource_pointer.hpp" +#include "phx/resources/types/material.hpp" #include "phx/utility/aspects/singleton.hpp" -#include "phx/export.hpp" namespace phx { /** * The ResourceUtils class contains static convenience methods to simplify - * interactions with the resource system. It should never hold any state. + * interactions with the resource system. It also holds a user configurable + * resource search path. When operating on a relative file name, it will + * search all entries in the search path and return/load the first file that + * matches the given file name. Per default, the resource search path + * contains the local directory and phx::resources_root. */ class PHOENIX_EXPORT ResourceUtils final { public: + static ResourceDeclaration GenericMaterialDeclaration( + const std::string& material_name, glm::vec3 diffuse_color, + glm::vec3 specular_color, glm::vec3 ambient_color, float shininess); + + static void AddResourceSearchPath(const std::string& path_to_add); + static void ResetResourceSearchPath(); + static std::vector<std::string> GetResourceSearchPath(); + static std::string ExtractFileExtension(const std::string& file_name); + static ResourceDeclaration DeclarationFromFile( const std::string& file_name, nlohmann::json additional_info = {}, bool absolute_path = false); + static ResourcePointer<Material> LoadGenericMaterial( + const std::string& material_name, glm::vec3 diffuse_color, + glm::vec3 specular_color = Material::GetDefault()->GetSpecularColor(), + glm::vec3 ambient_color = Material::GetDefault()->GetAmbientColor(), + float shininess = Material::GetDefault()->GetShininess()); + template <class ResourceType> static ResourcePointer<ResourceType> LoadResourceFromFile( const std::string& file_name, nlohmann::json additional_info = {}, @@ -59,6 +80,10 @@ class PHOENIX_EXPORT ResourceUtils final { res_ptr.Load(); return res_ptr; } + + private: + static std::string FindFileInSearchPath(const std::string& file_name); + static std::vector<std::string> resource_search_paths_; }; } // namespace phx diff --git a/library/phx/resources/types/image.hpp b/library/phx/resources/types/image.hpp index a5497948d3270f868eee852939ae1a27acdfe367..8638e7bb8f9522751fa44555848248fd05ce3cbd 100644 --- a/library/phx/resources/types/image.hpp +++ b/library/phx/resources/types/image.hpp @@ -81,7 +81,7 @@ class PHOENIX_EXPORT Image : public Resource, public Loggable { const std::int32_t native_flags = 0); Image(const Image& that); Image(Image&& temp) noexcept; - virtual ~Image(); + ~Image() override; Image& operator=(const Image& that); Image& operator=(Image&& temp) noexcept; diff --git a/library/phx/resources/types/material.cpp b/library/phx/resources/types/material.cpp index f79e5d332942b63f933ddc00235cb201fe4e8d06..49bd551988f68eedfbee0d585a378d54e50c785a 100644 --- a/library/phx/resources/types/material.cpp +++ b/library/phx/resources/types/material.cpp @@ -32,6 +32,7 @@ namespace phx { const char Material::UNNAMED[] = "UnnamedMaterial"; +const Material Material::default_material_; glm::vec3 Material::GetDiffuseColor() const { return diffuse_color_; } void Material::SetDiffuseColor(glm::vec3 color) { diffuse_color_ = color; } @@ -41,11 +42,10 @@ ResourcePointer<Image> Material::GetDiffuseImage() const { } void Material::SetDiffuseImage(ResourcePointer<Image> image) { diffuse_image_ = image; + ResetTexture(&diffuse_texture_); } -gl::texture_2d* Material::GetDiffuseTexture() { - if (diffuse_image_ && !diffuse_texture_) - SetTexture(diffuse_image_, &diffuse_texture_); +gl::texture_2d* Material::GetDiffuseTexture() const { return diffuse_texture_.get(); } @@ -57,11 +57,10 @@ ResourcePointer<Image> Material::GetAmbientImage() const { } void Material::SetAmbientImage(ResourcePointer<Image> image) { ambient_image_ = image; + ResetTexture(&ambient_texture_); } -gl::texture_2d* Material::GetAmbientTexture() { - if (ambient_image_ && !ambient_texture_) - SetTexture(ambient_image_, &ambient_texture_); +gl::texture_2d* Material::GetAmbientTexture() const { return ambient_texture_.get(); } @@ -73,11 +72,10 @@ ResourcePointer<Image> Material::GetSpecularImage() const { } void Material::SetSpecularImage(ResourcePointer<Image> image) { specular_image_ = image; + ResetTexture(&specular_texture_); } -gl::texture_2d* Material::GetSpecularTexture() { - if (specular_image_ && !specular_texture_) - SetTexture(specular_image_, &specular_texture_); +gl::texture_2d* Material::GetSpecularTexture() const { return specular_texture_.get(); } @@ -93,12 +91,25 @@ void Material::SetShininess(float shininess) { } } +const Material* Material::GetDefault() { return &default_material_; } + +void Material::UploadTextures() { + if (!diffuse_texture_) { + SetTexture(diffuse_image_, &diffuse_texture_); + } + if (!specular_texture_) { + SetTexture(specular_image_, &specular_texture_); + } + if (!ambient_texture_) { + SetTexture(ambient_image_, &ambient_texture_); + } +} + void Material::SetTexture(ResourcePointer<Image> image, std::shared_ptr<gl::texture_2d>* texture) { if (image == nullptr) return; - if (*texture && (*texture)->is_valid()) { - gl::texture_handle handle(*texture->get()); - handle.set_resident(false); + if (*texture) { + ResetTexture(texture); } auto dimensions = image->GetDimensions(); @@ -123,4 +134,12 @@ void Material::SetTexture(ResourcePointer<Image> image, gl::print_error("[Material::SetTexture] OpenGl Error code"); } +void Material::ResetTexture(std::shared_ptr<gl::texture_2d>* texture) { + if (*texture && (*texture)->is_valid()) { + gl::texture_handle handle(*texture->get()); + handle.set_resident(false); + } + texture->reset(); +} + } // namespace phx diff --git a/library/phx/resources/types/material.hpp b/library/phx/resources/types/material.hpp index 557dbfc01397270ac47f926fec0be7a2d4f663ed..fc839341bbf95af5e3ec719aeaaef0caf15a56b6 100644 --- a/library/phx/resources/types/material.hpp +++ b/library/phx/resources/types/material.hpp @@ -35,11 +35,11 @@ SUPPRESS_WARNINGS_BEGIN #include "glm/vec3.hpp" SUPPRESS_WARNINGS_END +#include "phx/export.hpp" #include "phx/resources/resource.hpp" #include "phx/resources/resource_pointer.hpp" #include "phx/resources/types/image.hpp" #include "phx/utility/aspects/nameable.hpp" -#include "phx/export.hpp" namespace phx { @@ -62,31 +62,34 @@ class PHOENIX_EXPORT Material : public Resource, public Nameable { ResourcePointer<Image> GetDiffuseImage() const; void SetDiffuseImage(ResourcePointer<Image> image); - - gl::texture_2d* GetDiffuseTexture(); + gl::texture_2d* GetDiffuseTexture() const; glm::vec3 GetAmbientColor() const; void SetAmbientColor(glm::vec3 color); ResourcePointer<Image> GetAmbientImage() const; void SetAmbientImage(ResourcePointer<Image> image); - - gl::texture_2d* GetAmbientTexture(); + gl::texture_2d* GetAmbientTexture() const; glm::vec3 GetSpecularColor() const; void SetSpecularColor(glm::vec3 color); ResourcePointer<Image> GetSpecularImage() const; void SetSpecularImage(ResourcePointer<Image> image); - - gl::texture_2d* GetSpecularTexture(); + gl::texture_2d* GetSpecularTexture() const; float GetShininess() const; void SetShininess(float shininess); + static const Material* GetDefault(); + + // unless this was called Get..Texture return nullptr + void UploadTextures(); + private: void SetTexture(ResourcePointer<Image> image, std::shared_ptr<gl::texture_2d>* texture); + void ResetTexture(std::shared_ptr<gl::texture_2d>* texture); ResourcePointer<Image> ambient_image_{nullptr}; ResourcePointer<Image> diffuse_image_{nullptr}; @@ -97,7 +100,9 @@ class PHOENIX_EXPORT Material : public Resource, public Nameable { glm::vec3 ambient_color_ = glm::vec3(0, 0, 0); glm::vec3 diffuse_color_ = glm::vec3(1, 0, 0); glm::vec3 specular_color_ = glm::vec3(1, 1, 1); - float shininess_ = 1.0f; + float shininess_ = 64.0f; + + static const Material default_material_; }; } // namespace phx diff --git a/library/phx/scripting/behavior_system.hpp b/library/phx/scripting/behavior_system.hpp index 5513e828fc5cf1e59db20b0beabc5d914b4a4fc3..e723ab04016c3b1aa9369dd78fb0c9079a050a15 100644 --- a/library/phx/scripting/behavior_system.hpp +++ b/library/phx/scripting/behavior_system.hpp @@ -31,14 +31,12 @@ namespace phx { -SUPPRESS_WARNINGS_BEGIN_PADDED - class PHOENIX_EXPORT BehaviorSystem : public System { public: BehaviorSystem() = delete; BehaviorSystem(const BehaviorSystem&) = delete; BehaviorSystem(BehaviorSystem&&) = default; - ~BehaviorSystem() = default; + ~BehaviorSystem() override = default; void Update(const FrameTimer::TimeInfo& time_info) override; @@ -50,8 +48,6 @@ class PHOENIX_EXPORT BehaviorSystem : public System { explicit BehaviorSystem(Engine* engine); }; -SUPPRESS_WARNINGS_END - } // namespace phx #endif // LIBRARY_PHX_SCRIPTING_BEHAVIOR_SYSTEM_HPP_ diff --git a/library/phx/scripting/generic_behavior.cpp b/library/phx/scripting/generic_behavior.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7ac991bfecc58048c6c4ea80e05e202cdba7264a --- /dev/null +++ b/library/phx/scripting/generic_behavior.cpp @@ -0,0 +1,44 @@ +//------------------------------------------------------------------------------ +// Project Phoenix +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualization Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the 3-Clause BSD License (the "License"); +// you may not use this file except in compliance with the License. +// See the file LICENSE for the full text. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include "generic_behavior.hpp" + +#include <string> + +namespace phx { + +GenericBehavior::GenericBehavior( + const std::function<void(Entity*)>& on_construction, + const std::function<void(const FrameTimer::TimeInfo* time_info, Entity*)>& + on_update, + const std::string& name /*= "unnamed"*/) + : on_update_(on_update), name_(name) { + on_construction(GetEntity()); +} + +void GenericBehavior::OnUpdate() { on_update_(time_info_, GetEntity()); } + +std::string GenericBehavior::ToString() const { + return "GenericBehavior: " + name_; +} + +} // namespace phx diff --git a/library/phx/scripting/generic_behavior.hpp b/library/phx/scripting/generic_behavior.hpp new file mode 100644 index 0000000000000000000000000000000000000000..cc86fb25b0499a453395c8a3bc0ca220655a9ac8 --- /dev/null +++ b/library/phx/scripting/generic_behavior.hpp @@ -0,0 +1,60 @@ +//------------------------------------------------------------------------------ +// Project Phoenix +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualization Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the 3-Clause BSD License (the "License"); +// you may not use this file except in compliance with the License. +// See the file LICENSE for the full text. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#ifndef LIBRARY_PHX_SCRIPTING_GENERIC_BEHAVIOR_HPP_ +#define LIBRARY_PHX_SCRIPTING_GENERIC_BEHAVIOR_HPP_ + +#include <functional> +#include <string> + +#include "phx/scripting/behavior.hpp" + +namespace phx { + +class PHOENIX_EXPORT GenericBehavior : public Behavior { + public: + GenericBehavior( + const std::function<void(Entity*)>& on_construction, + const std::function<void(const FrameTimer::TimeInfo* time_info, Entity*)>& + on_update, + const std::string& name = "unnamed"); + GenericBehavior(const GenericBehavior&) = default; + GenericBehavior(GenericBehavior&&) = default; + + GenericBehavior& operator=(const GenericBehavior&) = default; + GenericBehavior& operator=(GenericBehavior&&) = default; + + ~GenericBehavior() = default; + + void OnUpdate() override; + std::string ToString() const override; + + protected: + std::function<void(Entity*)> on_construction_; + std::function<void(const FrameTimer::TimeInfo* time_info, Entity*)> + on_update_; + std::string name_; +}; + +} // namespace phx + +#endif // LIBRARY_PHX_SCRIPTING_GENERIC_BEHAVIOR_HPP_ diff --git a/library/phx/setup.cpp b/library/phx/setup.cpp index 0c0a862450954383f9621a8cc40f27a5473e5449..895d4eb3251b1257004b50e5222aeb558c984487 100644 --- a/library/phx/setup.cpp +++ b/library/phx/setup.cpp @@ -27,6 +27,8 @@ #include <string> #include <utility> +#include "input/device_system.hpp" +#include "input/vr_controller.hpp" #include "phx/core/component.hpp" #include "phx/core/engine.hpp" #include "phx/core/logger.hpp" @@ -36,7 +38,7 @@ #include "phx/display/display_system_window.hpp" #include "phx/display/hmd.hpp" #include "phx/input/input_system.hpp" -#include "phx/input/openvr_controller_system.hpp" +#include "phx/input/openvr_controller_model_system.hpp" #include "phx/rendering/backend/render_target.hpp" #include "phx/rendering/render_passes/blit_pass.hpp" #include "phx/rendering/render_passes/clear_pass.hpp" @@ -56,16 +58,21 @@ std::unique_ptr<Engine> Setup::CreateDefaultEngine(bool use_hmd_if_available) { engine->CreateSystem<InputSystem>()->AddQuitCallback( [engine_ptr]() { engine_ptr->Stop(); }); + auto device_system = engine->CreateSystem<DeviceSystem>(); + auto displaysys_window = engine->CreateSystem<DisplaySystemWindow>(); - DisplaySystemOpenVR* displaysys_hmd = nullptr; + DisplaySystemOpenVR* displaysys_openVR = nullptr; bool using_hmd = false; if (HMD::IsHMDPresent() && use_hmd_if_available) { info("An HMD is present so we use it"); using_hmd = true; - displaysys_hmd = engine->CreateSystem<DisplaySystemOpenVR>(); - displaysys_hmd->CreateHMD(); + device_system->AddDevice<HMD>(); + device_system->AddDevice<VRController>(VRController::LEFT_CONTROLLER); + device_system->AddDevice<VRController>(VRController::RIGHT_CONTROLLER); + + displaysys_openVR = engine->CreateSystem<DisplaySystemOpenVR>(); } const std::string window_title{using_hmd ? "Phoenix -- HMD Companion" @@ -79,21 +86,24 @@ std::unique_ptr<Engine> Setup::CreateDefaultEngine(bool use_hmd_if_available) { // fix update order engine->MoveSystemToBack(displaysys_window); + if (displaysys_openVR != nullptr) engine->MoveSystemToBack(displaysys_openVR); engine->MoveSystemBefore(rendering_system, displaysys_window); + engine->MoveSystemBefore(device_system, rendering_system); // setup rendering and frame graph - if (using_hmd) { - auto tracking_system = - engine->CreateSystem<TrackingSystemOpenVR>(displaysys_hmd); - auto controller_system = engine->CreateSystem<OpenVRControllerSystem>( - engine->GetSystem<DisplaySystem>()); + if (HMD::IsHMDPresent() && use_hmd_if_available) { + auto controller_model_system = + engine->CreateSystem<OpenVRControllerModelSystem>( + engine->GetSystem<DeviceSystem>()); + auto tracking_system = engine->CreateSystem<TrackingSystemOpenVR>( + engine->GetSystem<DeviceSystem>()); - displaysys_hmd->CreateRenderTargets(engine->GetScene().get()); + displaysys_openVR->CreateRenderTargets(engine->GetScene().get()); SetupDefaultFrameGraphOpenVR(rendering_system, engine.get()); engine->MoveSystemBefore(tracking_system, rendering_system); engine->MoveSystemAfter(behavior_system, tracking_system); - engine->MoveSystemAfter(controller_system, tracking_system); + engine->MoveSystemAfter(controller_model_system, tracking_system); } else { displaysys_window->CreateRenderTarget(engine->GetScene().get(), 68.0f, 0.01f, 1000.0f); diff --git a/library/phx/tracking/tracking_system_openvr.cpp b/library/phx/tracking/tracking_system_openvr.cpp index e101095e0ff421af0d03b59666e79bc2684abb70..8156bc3c4f16e7fb9a100611004a9b28a8f68932 100644 --- a/library/phx/tracking/tracking_system_openvr.cpp +++ b/library/phx/tracking/tracking_system_openvr.cpp @@ -25,23 +25,22 @@ #include <memory> #include <string> -#include "phx/core/logger.hpp" #include "phx/core/runtime_component.hpp" #include "phx/display/display_system_openvr.hpp" #include "phx/display/hmd.hpp" +#include "phx/input/device_system.hpp" +#include "phx/input/vr_controller.hpp" #include "phx/rendering/components/projection.hpp" #include "phx/rendering/components/transform.hpp" namespace phx { TrackingSystemOpenVR::TrackingSystemOpenVR(Engine* engine, - DisplaySystemOpenVR* display_system) + DeviceSystem* device_system) : System(engine) { - if (!display_system) - error("TrackingSystemOpenVR needs a valid DisplaySystemOpenVR."); - if (display_system->GetHMD()) { - CreateRuntimeEntities(engine->GetScene().get()); - scene_changed_connection_ = engine->AddSceneChangedCallback( + if (device_system->GetDevices<HMD>().size() != 0) { + CreateRuntimeEntities(engine_->GetScene().get()); + scene_changed_connection_ = engine_->AddSceneChangedCallback( [this](std::shared_ptr<Scene> old_scene, std::shared_ptr<Scene> new_scene) { OnSceneChanged(old_scene, new_scene); @@ -54,43 +53,34 @@ TrackingSystemOpenVR::~TrackingSystemOpenVR() { } void TrackingSystemOpenVR::Update(const FrameTimer::TimeInfo&) { - const auto dispsys_openvr = engine_->GetSystem<DisplaySystemOpenVR>(); - if (dispsys_openvr == nullptr) { + const auto device_system = engine_->GetSystem<DeviceSystem>(); + if (device_system == nullptr) { return; } - const auto hmd = dispsys_openvr->GetHMD(); - if (hmd == nullptr) { - return; - } - hmd->UpdateTrackedDevices(); - const auto head_transformation = hmd->GetHeadTransformation(); - if (hmd_entity_ != nullptr) { - hmd_entity_->GetFirstComponent<Transform>()->SetLocalMatrix( - head_transformation); - const auto left_eye_transformation = hmd->GetEyeToHeadMatrix(HMD::LEFT_EYE); - left_eye_entity_->GetFirstComponent<Transform>()->SetLocalMatrix( - left_eye_transformation); - const auto right_eye_transformation = - hmd->GetEyeToHeadMatrix(HMD::RIGHT_EYE); - right_eye_entity_->GetFirstComponent<Transform>()->SetLocalMatrix( - right_eye_transformation); - - left_controller_entity_->GetFirstComponent<Transform>()->SetLocalMatrix( - hmd->GetLeftControllerTransformation()); - - right_controller_entity_->GetFirstComponent<Transform>()->SetLocalMatrix( - hmd->GetRightControllerTransformation()); - } + + auto hmds = device_system->GetDevices<HMD>(); + if (hmds.size() != 1) return; + auto hmd = hmds[0]; + + hmd_entity_->GetFirstComponent<Transform>()->SetLocalMatrix(hmd->GetPose()); + left_eye_entity_->GetFirstComponent<Transform>()->SetLocalMatrix( + hmd->GetEyeToHeadMatrix(HMD::LEFT_EYE)); + right_eye_entity_->GetFirstComponent<Transform>()->SetLocalMatrix( + hmd->GetEyeToHeadMatrix(HMD::RIGHT_EYE)); + + left_controller_entity_->GetFirstComponent<Transform>()->SetLocalMatrix( + GetController(VRController::LEFT_CONTROLLER)->GetPose()); + + right_controller_entity_->GetFirstComponent<Transform>()->SetLocalMatrix( + GetController(VRController::RIGHT_CONTROLLER)->GetPose()); } void TrackingSystemOpenVR::CreateRuntimeEntities(Scene* scene) { - const auto hmd = engine_->GetSystem<DisplaySystemOpenVR>()->GetHMD(); - if (hmd == nullptr) { - return; - } - if (scene == nullptr) { + const auto device_system = engine_->GetSystem<DeviceSystem>(); + if (device_system == nullptr || scene == nullptr) { return; } + const auto virtual_platforms = scene->GetEntitiesWithComponents< phx::RuntimeComponent<phx::USER_PLATFORM>>(); if (!virtual_platforms.empty()) { @@ -102,18 +92,18 @@ void TrackingSystemOpenVR::CreateRuntimeEntities(Scene* scene) { hmd_entity_->AddComponent<RuntimeComponent<HEAD>>(); auto hmd_transform = hmd_entity_->AddComponent<Transform>(); hmd_transform->SetParent(virtual_platform_transform); - left_eye_entity_ = scene->CreateEntity(); + left_eye_entity_ = scene->CreateEntity(); left_eye_entity_->AddComponent<RuntimeComponent<LEFT_EYE>>(); auto left_eye_transform = left_eye_entity_->AddComponent<Transform>(); left_eye_transform->SetParent(hmd_transform); left_eye_entity_->AddComponent<Projection>(); - right_eye_entity_ = scene->CreateEntity(); + right_eye_entity_ = scene->CreateEntity(); right_eye_entity_->AddComponent<RuntimeComponent<RIGHT_EYE>>(); auto right_eye_transform = right_eye_entity_->AddComponent<Transform>(); - right_eye_entity_->AddComponent<Projection>(); right_eye_transform->SetParent(hmd_transform); + right_eye_entity_->AddComponent<Projection>(); left_controller_entity_ = scene->CreateEntity(); left_controller_entity_->AddComponent<RuntimeComponent<LEFT_CONTROLLER>>(); @@ -132,18 +122,34 @@ void TrackingSystemOpenVR::CreateRuntimeEntities(Scene* scene) { void TrackingSystemOpenVR::RemoveRuntimeEntities(Scene* scene) { scene->RemoveEntity(hmd_entity_); + hmd_entity_ = nullptr; scene->RemoveEntity(left_eye_entity_); + left_eye_entity_ = nullptr; scene->RemoveEntity(right_eye_entity_); + right_eye_entity_ = nullptr; scene->RemoveEntity(left_controller_entity_); + left_controller_entity_ = nullptr; scene->RemoveEntity(right_controller_entity_); + right_controller_entity_ = nullptr; } void TrackingSystemOpenVR::OnSceneChanged(std::shared_ptr<Scene> old_scene, - std::shared_ptr<Scene> new_scene) { + std::shared_ptr<Scene> new_scene) { RemoveRuntimeEntities(old_scene.get()); CreateRuntimeEntities(new_scene.get()); } +VRController* TrackingSystemOpenVR::GetController( + VRController::ControllerSide side) { + for (auto controller : + engine_->GetSystem<DeviceSystem>()->GetDevices<VRController>()) { + if (controller->GetSide() == side) { + return controller; + } + } + return nullptr; +} + std::string TrackingSystemOpenVR::ToString() const { return "Tracking System"; } } // namespace phx diff --git a/library/phx/tracking/tracking_system_openvr.hpp b/library/phx/tracking/tracking_system_openvr.hpp index ad35174c67e3bc5199e575e4dcff6ba65c9be1fb..fd76bce5588bf3018e16ea8fb33dbc9ffbf253a9 100644 --- a/library/phx/tracking/tracking_system_openvr.hpp +++ b/library/phx/tracking/tracking_system_openvr.hpp @@ -30,8 +30,8 @@ #include "phx/core/engine.hpp" #include "phx/core/system.hpp" -#include "phx/display/display_system_openvr.hpp" #include "phx/export.hpp" +#include "phx/input/vr_controller.hpp" SUPPRESS_WARNINGS_BEGIN #define BOOST_BIND_NO_PLACEHOLDERS @@ -39,13 +39,14 @@ SUPPRESS_WARNINGS_BEGIN SUPPRESS_WARNINGS_END namespace phx { +class DeviceSystem; class PHOENIX_EXPORT TrackingSystemOpenVR : public System { public: TrackingSystemOpenVR() = delete; TrackingSystemOpenVR(const TrackingSystemOpenVR&) = delete; TrackingSystemOpenVR(TrackingSystemOpenVR&&) = default; - ~TrackingSystemOpenVR(); + ~TrackingSystemOpenVR() override; TrackingSystemOpenVR& operator=(const TrackingSystemOpenVR&) = delete; TrackingSystemOpenVR& operator=(TrackingSystemOpenVR&&) = default; @@ -55,7 +56,7 @@ class PHOENIX_EXPORT TrackingSystemOpenVR : public System { std::string ToString() const override; protected: - TrackingSystemOpenVR(Engine* engine, DisplaySystemOpenVR* display_system); + TrackingSystemOpenVR(Engine* engine, DeviceSystem* device_system); private: template <typename SystemType, typename... SystemArguments> @@ -66,6 +67,8 @@ class PHOENIX_EXPORT TrackingSystemOpenVR : public System { void OnSceneChanged(std::shared_ptr<Scene> old_scene, std::shared_ptr<Scene> new_scene); + VRController* GetController(VRController::ControllerSide side); + Entity* hmd_entity_ = nullptr; Entity* left_eye_entity_ = nullptr; Entity* right_eye_entity_ = nullptr; diff --git a/library/phx/utility/aspects/hierarchical.hpp b/library/phx/utility/aspects/hierarchical.hpp index 45dd77d166b3908678d4e8e22ceb3ba00050bcb0..c9467b9137594b6f076cfe9ba988de142b74502f 100644 --- a/library/phx/utility/aspects/hierarchical.hpp +++ b/library/phx/utility/aspects/hierarchical.hpp @@ -34,15 +34,20 @@ namespace phx { template <typename Derived> class PHOENIX_EXPORT Hierarchical { public: - virtual ~Hierarchical() = default; + virtual ~Hierarchical() { + Hierarchical::SetParent(nullptr); + while (children_.size() != 0) { + children_[0]->SetParent(nullptr); + } + } virtual void SetParent(Derived* parent) { - if (parent_) + if (parent_) { parent_->children_.erase( std::remove(parent_->children_.begin(), parent_->children_.end(), static_cast<Derived*>(this)), parent_->children_.end()); - + } parent_ = parent; if (parent_) parent_->children_.push_back(static_cast<Derived*>(this)); diff --git a/library/phx/utility/stream_helpers.hpp b/library/phx/utility/stream_helpers.hpp index c464887c49b75208f9433a0181e50e5b2e4046db..89902b6cd7f208dc3f746915c003be3f36d55c51 100644 --- a/library/phx/utility/stream_helpers.hpp +++ b/library/phx/utility/stream_helpers.hpp @@ -41,7 +41,6 @@ SUPPRESS_WARNINGS_END // namespace like e.g. phx, leaving them in global namespace would not work namespace glm { -SUPPRESS_WARNINGS_BEGIN_PADDED class PHOENIX_EXPORT PhxStreamSettings { public: explicit PhxStreamSettings(std::ostream& stream); @@ -57,7 +56,6 @@ class PHOENIX_EXPORT PhxStreamSettings { std::streamsize stream_precision_; std::ios::fmtflags stream_flags_; }; -SUPPRESS_WARNINGS_END PHOENIX_EXPORT std::ostream& operator<<(std::ostream& out, const glm::vec2& vec); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7a9d51630e5abc3d55fc600d163802bf5d3266ed..f63ed905d8e76ba508e35cf4c75092d0c56031d9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -22,86 +22,43 @@ #see at the bottom how to add mocked tests -# configure reference image directory include string -set(_REFIMAGE_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/reference_images/") -set(SOURCE_REFIMAGE_INCLUDE_FILE "${CMAKE_CURRENT_SOURCE_DIR}/test_utilities/reference_image_path.hpp.template") -set(TARGET_REFIMAGE_INCLUDE_FILE "${CMAKE_CURRENT_BINARY_DIR}/test_utilities/reference_image_path.hpp") -configure_file(${SOURCE_REFIMAGE_INCLUDE_FILE} ${TARGET_REFIMAGE_INCLUDE_FILE} @ONLY) - file(GLOB PHOENIX_TEST_SOURCES src/*.cpp) file(GLOB PHOENIX_TEST_HEADERS src/*.hpp) file(GLOB PHOENIX_TEST_UTILITIES_TEST_SOURCES test_utilities/tests/src/*.cpp) file(GLOB PHOENIX_TEST_UTILITIES_HEADERS test_utilities/*.hpp) file(GLOB PHOENIX_TEST_UTILITIES_SOURCES test_utilities/*.cpp) +file(GLOB TEST_MAIN_SOURCES src/tests.cpp) +file(GLOB MOCK_MAIN_SOURCES src/mocks/mocks.cpp) -#get the include directories -get_target_property(glew_include_directories - ${CONAN_OR_CMAKE_glew} INTERFACE_INCLUDE_DIRECTORIES) -get_target_property(phoenix_include_directories - phoenix INCLUDE_DIRECTORIES) -get_target_property(sdl2_include_directories - ${CONAN_OR_CMAKE_sdl2} INTERFACE_INCLUDE_DIRECTORIES) -get_target_property(openvr_include_directories - ${CONAN_OR_CMAKE_openvr} INTERFACE_INCLUDE_DIRECTORIES) -get_target_property(gl_include_directories - ${CONAN_OR_CMAKE_gl} INTERFACE_INCLUDE_DIRECTORIES) - - -macro(add_mock_lib mock_name mock_src_path) - add_library(${mock_name} SHARED - ${mock_src_path}/${mock_name}.cpp - ${mock_src_path}/${mock_name}.hpp + +create_test_main( + SOURCES ${TEST_MAIN_SOURCES} + LIBRARIES ${CONAN_OR_CMAKE_catch} ) - target_link_libraries(${mock_name} - ${CONAN_OR_CMAKE_trompeloeil}) - #phx include dirs needed for suppress_warnings.hpp - target_include_directories(${mock_name} - PRIVATE ${phoenix_include_directories}) - set_property(TARGET ${mock_name} PROPERTY FOLDER "Tests") - set_warning_levels_rwth(${mock_name}) -endmacro() +list(REMOVE_ITEM PHOENIX_TEST_SOURCES ${TEST_MAIN_SOURCES}) -# generate opengl_mock -set( OPENGL_MOCK_SOURCE ${CMAKE_CURRENT_BINARY_DIR}/mocks) -set( OPENGL_MOCK_GENERATOR ${CMAKE_CURRENT_SOURCE_DIR}/src/mocks/generation/Create_openGL_mock.py) -if( NOT EXISTS ${OPENGL_MOCK_SOURCE}/opengl_mock.cpp OR - ${OPENGL_MOCK_GENERATOR} IS_NEWER_THAN - ${OPENGL_MOCK_SOURCE}/opengl_mock.cpp) - file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/mocks") - EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} - "${OPENGL_MOCK_GENERATOR}" - -g "${glew_include_directories}" - -m "${OPENGL_MOCK_SOURCE}/" - -t "${CMAKE_CURRENT_SOURCE_DIR}/src/mocks/generation/") -endif() +create_mock_main( + SOURCES ${MOCK_MAIN_SOURCES} + LIBRARIES ${CONAN_OR_CMAKE_catch} ${CONAN_OR_CMAKE_trompeloeil} + ) +list(REMOVE_ITEM PHOENIX_TEST_SOURCES ${MOCK_MAIN_SOURCES}) -add_mock_lib(opengl_mock ${OPENGL_MOCK_SOURCE}) -target_include_directories(opengl_mock - PUBLIC ${OPENGL_MOCK_SOURCE} - PUBLIC ${glew_include_directories}) -target_compile_definitions(opengl_mock - PRIVATE OPENGL_MOCK_BUILD) -generate_export_header(opengl_mock - EXPORT_FILE_NAME ${OPENGL_MOCK_SOURCE}/opengl_mock_export.hpp -) -# generate openvr_mock -add_mock_lib(openvr_mock ${CMAKE_CURRENT_SOURCE_DIR}/src/mocks) -target_include_directories(openvr_mock - PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src - PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/mocks - PUBLIC ${openvr_include_directories}) -target_compile_definitions(openvr_mock - PRIVATE OPENVR_MOCK_BUILD) -generate_export_header(openvr_mock - EXPORT_FILE_NAME ${CMAKE_CURRENT_BINARY_DIR}/mocks/openvr_mock_export.hpp -) +# configure reference image directory include string +set(_REFIMAGE_ROOT_PATH + "${CMAKE_CURRENT_SOURCE_DIR}/reference_images/" + ) +set(SOURCE_REFIMAGE_INCLUDE_FILE + "${CMAKE_CURRENT_SOURCE_DIR}/test_utilities/reference_image_path.hpp.template" + ) +set(TARGET_REFIMAGE_INCLUDE_FILE + "${CMAKE_CURRENT_BINARY_DIR}/test_utilities/reference_image_path.hpp" + ) +configure_file(${SOURCE_REFIMAGE_INCLUDE_FILE} + ${TARGET_REFIMAGE_INCLUDE_FILE} + @ONLY + ) -#the opengl mock gets quite large due to the trompleoeil templates -if(MSVC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /bigobj") -endif(MSVC) # generate test_utilities add_library(test_utilities STATIC @@ -120,82 +77,78 @@ target_link_libraries(test_utilities set_warning_levels_rwth(test_utilities) +# generate opengl_mock +get_target_property(glew_include_directories + ${CONAN_OR_CMAKE_glew} INTERFACE_INCLUDE_DIRECTORIES) +set( OPENGL_MOCK_SOURCE ${CMAKE_CURRENT_BINARY_DIR}/mocks) +set( OPENGL_MOCK_GENERATOR ${CMAKE_CURRENT_SOURCE_DIR}/src/mocks/generation/Create_openGL_mock.py) +if( NOT EXISTS ${OPENGL_MOCK_SOURCE}/opengl_mock.cpp OR + ${OPENGL_MOCK_GENERATOR} IS_NEWER_THAN + ${OPENGL_MOCK_SOURCE}/opengl_mock.cpp) + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/mocks") + EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} + "${OPENGL_MOCK_GENERATOR}" + -g "${glew_include_directories}" + -m "${OPENGL_MOCK_SOURCE}/" + -t "${CMAKE_CURRENT_SOURCE_DIR}/src/mocks/generation/") +endif() + #get the gl link library if(TARGET CONAN_LIB::gl_gl) - get_target_property(gl_link_library CONAN_LIB::gl_gl LOCATION) + get_target_property(GL_LIBRARY CONAN_LIB::gl_gl LOCATION) elseif(TARGET CONAN_LIB::gl_glrelease) - get_target_property(gl_imported_location_rel CONAN_LIB::gl_glrelease LOCATION) - get_target_property(gl_imported_location_deb CONAN_LIB::gl_gldebug LOCATION) - set(gl_link_library "$<$<CONFIG:Release>:${gl_imported_location_rel}>$<$<CONFIG:Debug>:${gl_imported_location_deb}>") -else() - message(FATAL_ERROR "You are using a conan version older than 0.29, please update e.g. by \"pip install conan --upgrade\"") + get_target_property(GL_LIBRARY_LOCATION_RELEASE + CONAN_LIB::gl_glrelease LOCATION) + get_target_property(GL_LIBRARY_LOCATION_DEBUG + CONAN_LIB::gl_gldebug LOCATION) + set(GL_LIBRARY + "$<$<CONFIG:Release>:${GL_LIBRARY_LOCATION_RELEASE}>$<$<CONFIG:Debug>:${GL_LIBRARY_LOCATION_DEBUG}>") endif() + +add_mock( + NAME opengl_mock + SOURCES ${CMAKE_CURRENT_BINARY_DIR}/mocks/opengl_mock.cpp + HEADERS ${CMAKE_CURRENT_BINARY_DIR}/mocks/opengl_mock.hpp + INCLUDE_DIRECTORIES + ${CMAKE_CURRENT_BINARY_DIR} + INCLUDE_DIRECTORIES_OF + phoenix + ${CONAN_OR_CMAKE_gl} + ${CONAN_OR_CMAKE_glew} + COMPILE_OPTIONS $<$<CXX_COMPILER_ID:MSVC>:/bigobj> + REMOVES_LIBRARIES + ${CONAN_OR_CMAKE_gl} + ${CONAN_OR_CMAKE_glew} + ${CONAN_OR_CMAKE_sdl2} + ${OPENGL_LIBRARIES} + ADDS_LIBRARIES ${GL_LIBRARY} + ) -set(IS_BUILD_SERVER FALSE CACHE BOOL "Is this the build server? So we, e.g., simulate user input for tests requiring it.") - - -macro(add_mocked_test cpp_file) - set(options MOCK_GLEW MOCK_SDL MOCK_OPENVR) - set(oneValueArgs ) - set(multiValueArgs ) - cmake_parse_arguments(ADD_MOCKED_TEST - "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - list(REMOVE_ITEM PHOENIX_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/${cpp_file}.cpp) - - set(test_sources src/tests.cpp src/${cpp_file}.cpp) - add_executable(${cpp_file} ${test_sources}) - - add_dependencies(${cpp_file} phoenix test_utilities) - - ADD_TEST_CATCH_INTERNAL_(${cpp_file} ${test_sources} "") - - # make shure linker dependency injection works - target_include_directories(${cpp_file} - PRIVATE ${phoenix_include_directories} - PRIVATE ${glew_include_directories} - PRIVATE ${gl_include_directories} - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src - PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) - - get_target_property(phoenix_link_libraries phoenix LINK_LIBRARIES) - - if(${ADD_MOCKED_TEST_MOCK_GLEW}) - list(REMOVE_ITEM phoenix_link_libraries ${CONAN_OR_CMAKE_gl}) - list(REMOVE_ITEM phoenix_link_libraries ${CONAN_OR_CMAKE_glew}) - #SDL has also to be removed since it contains OpenGL - list(REMOVE_ITEM phoenix_link_libraries ${CONAN_OR_CMAKE_sdl2}) - list(REMOVE_ITEM phoenix_link_libraries ${OPENGL_LIBRARIES}) - list(APPEND phoenix_link_libraries opengl_mock) - list(APPEND phoenix_link_libraries ${gl_link_library}) - - endif(${ADD_MOCKED_TEST_MOCK_GLEW}) - - if(${ADD_MOCKED_TEST_MOCK_SDL}) - target_sources(${cpp_file} - PRIVATE src/mocks/sdl_mock.cpp - PRIVATE src/mocks/sdl_mock.hpp) - target_compile_definitions(${cpp_file} PRIVATE TESTING) - target_include_directories(${cpp_file} - PRIVATE ${sdl2_include_directories}) - list(REMOVE_ITEM phoenix_link_libraries ${CONAN_OR_CMAKE_sdl2}) - endif(${ADD_MOCKED_TEST_MOCK_SDL}) - - if(${ADD_MOCKED_TEST_MOCK_OPENVR}) - list(REMOVE_ITEM phoenix_link_libraries ${CONAN_OR_CMAKE_openvr}) - list(APPEND phoenix_link_libraries openvr_mock) - endif(${ADD_MOCKED_TEST_MOCK_OPENVR}) - - target_link_libraries(${cpp_file} - ${CONAN_OR_CMAKE_trompeloeil} - ${CONAN_OR_CMAKE_catch} - $<TARGET_FILE:phoenix> - ${phoenix_link_libraries} - $<TARGET_FILE:test_utilities> +add_mock( + NAME openvr_mock + SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/mocks/openvr_mock.cpp + HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/mocks/openvr_mock.hpp + INCLUDE_DIRECTORIES + ${CMAKE_CURRENT_SOURCE_DIR}/src + ${CMAKE_CURRENT_BINARY_DIR} + INCLUDE_DIRECTORIES_OF + phoenix + ${CONAN_OR_CMAKE_openvr} + REMOVES_LIBRARIES ${CONAN_OR_CMAKE_openvr} ) -endmacro() +add_mock( + NAME sdl_mock + SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/mocks/sdl_mock.cpp + HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/mocks/sdl_mock.hpp + INCLUDE_DIRECTORIES + ${CMAKE_CURRENT_SOURCE_DIR}/src + ${CMAKE_CURRENT_BINARY_DIR} + INCLUDE_DIRECTORIES_OF + phoenix + ${CONAN_OR_CMAKE_sdl2} + REMOVES_LIBRARIES ${CONAN_OR_CMAKE_sdl2} + ) # these have to go before the mocked tests. @@ -214,37 +167,42 @@ add_test_cpplint(NAME "phoenix-tests--cpplint" ${PHOENIX_TEST_UTILITIES_SOURCES} ) -#------------------------------------------------------------------------------- -#------------------------------------------------------------------------------- # Only tests that require mocking need to be specified separately here. -# By doing so they are removed from ${PHOENIX_TEST_SOURCES} automatically. -add_mocked_test(test_clear_pass MOCK_GLEW) -add_mocked_test(test_input_system MOCK_SDL MOCK_OPENVR) -add_mocked_test(test_geometry_pass MOCK_GLEW MOCK_SDL) -add_mocked_test(test_rendering_system MOCK_GLEW MOCK_SDL) -add_mocked_test(test_shader MOCK_GLEW) -add_mocked_test(test_display_system MOCK_SDL) -add_mocked_test(test_engine MOCK_SDL MOCK_OPENVR MOCK_GLEW) -add_mocked_test(test_tracking_system MOCK_SDL MOCK_OPENVR) -add_mocked_test(test_openvr_controller_system MOCK_SDL MOCK_OPENVR MOCK_GLEW) - -add_mocked_test(integration_test_model_rendering MOCK_OPENVR) -add_mocked_test(integration_test_opengl_buffer_data_download MOCK_OPENVR) -add_mocked_test(integration_test_rendering MOCK_OPENVR) -add_mocked_test(integration_test_hmd MOCK_OPENVR) - -#------------------------------------------------------------------------------- -#------------------------------------------------------------------------------- - - -add_test_catch(NAME "phoenix-tests" +# By doing so they are removed from the list of sources +# specified via AUTOREMOVE_MOCKED_TEST_SOURCE_FROM +autoremove_mocked_test_source_from(${PHOENIX_TEST_SOURCES}) + +add_mocked_test(test_clear_pass LIBRARIES phoenix MOCKS opengl_mock) +add_mocked_test(test_input_system LIBRARIES phoenix MOCKS openvr_mock sdl_mock) +add_mocked_test(test_geometry_pass LIBRARIES phoenix test_utilities MOCKS opengl_mock sdl_mock) +add_mocked_test(test_rendering_system LIBRARIES phoenix MOCKS opengl_mock sdl_mock) +add_mocked_test(test_shader LIBRARIES phoenix MOCKS opengl_mock) +add_mocked_test(test_display_system LIBRARIES phoenix MOCKS sdl_mock) +add_mocked_test(test_engine LIBRARIES phoenix test_utilities MOCKS opengl_mock openvr_mock sdl_mock) +add_mocked_test(test_tracking_system LIBRARIES phoenix test_utilities MOCKS openvr_mock sdl_mock) +add_mocked_test(test_openvr_controller_model_system LIBRARIES phoenix MOCKS opengl_mock openvr_mock sdl_mock) +add_mocked_test(test_tracked_device LIBRARIES phoenix MOCKS openvr_mock) +add_mocked_test(test_vr_controller LIBRARIES phoenix test_utilities MOCKS openvr_mock) +add_mocked_test(test_assimp_loader LIBRARIES phoenix test_utilities MOCKS opengl_mock) +add_mocked_test(test_model LIBRARIES phoenix MOCKS opengl_mock) +add_mocked_test(test_scene_loader LIBRARIES phoenix MOCKS opengl_mock) + +add_mocked_test(integration_test_model_rendering LIBRARIES phoenix test_utilities MOCKS openvr_mock) +add_mocked_test(integration_test_opengl_buffer_data_download LIBRARIES phoenix test_utilities MOCKS openvr_mock) +add_mocked_test(integration_test_rendering LIBRARIES phoenix test_utilities MOCKS openvr_mock) +add_mocked_test(integration_test_hmd LIBRARIES phoenix test_utilities MOCKS openvr_mock) + + +get_unmocked_test_sources(PHOENIX_TEST_SOURCES) + +# This adds all cpp files in tests as single tests that were not used with +# add_mocked_test() above +add_tests(NAME "phoenix-tests" SOURCES ${PHOENIX_TEST_SOURCES} ${PHOENIX_TEST_UTILITIES_TEST_SOURCES} HEADERS ${PHOENIX_TEST_HEADERS} - CATCH_MAIN src/tests.cpp - INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} phoenix - LINK_LIBRARIES phoenix test_utilities + INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} + LIBRARIES phoenix test_utilities PATH_TO_ADD ${PROJECT_BINARY_DIR}/library ) - diff --git a/tests/reference_images/hmd_test_left.png b/tests/reference_images/hmd_test_left.png index 9989f7ae4601b0eb1fcd961ccba454ddf6fdd45d..2f975671215cd6917f0d62a78ec76a8ea84191be 100644 --- a/tests/reference_images/hmd_test_left.png +++ b/tests/reference_images/hmd_test_left.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e4a6d5d6c1eed96056d964381617ec97dd3b6c9d26717cdc55c1e66c8343feed -size 115516 +oid sha256:52c050bf258acaf19f3914bcc2792e4b130cc07e8c7017b8a4c5b69dcccec76f +size 116885 diff --git a/tests/reference_images/hmd_test_right.png b/tests/reference_images/hmd_test_right.png index 33f5fa2758442f23f4bcf04d353ea2e6661cc9a6..2519d5d6775c6b2fec9dbdf1083ac6ad7cd6fbf4 100644 --- a/tests/reference_images/hmd_test_right.png +++ b/tests/reference_images/hmd_test_right.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e07eb1a9c5bfea732801cbd4b6d6321c7797d815645b23fa205a14fa50d76b3d -size 115419 +oid sha256:a9ea7817c7c1254c1765bf4a0977dd7b430d8419d1355f60b92dce461befae35 +size 116057 diff --git a/tests/reference_images/model_loading_wireframe.png b/tests/reference_images/model_loading_wireframe.png new file mode 100644 index 0000000000000000000000000000000000000000..fba84750d93e4e9dda237e96dd0ab6004e07d5dd --- /dev/null +++ b/tests/reference_images/model_loading_wireframe.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:79ea2093c78aa11360295e2ee16ffa2f815ccd2e9f9e79ab5a06a29ef895564a +size 105684 diff --git a/tests/reference_images/triangle_without_material.png b/tests/reference_images/triangle_without_material.png index f93066742c9359d414787cbafdc31031d11f80eb..b5eb2e6a885ae9e371ef92880dfb74a9e9e0df68 100644 --- a/tests/reference_images/triangle_without_material.png +++ b/tests/reference_images/triangle_without_material.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cdf28b3e240057f66ad3e4323c1ffd5aa7200bd6708a1d8d0476ff9c5a6eae22 -size 3859 +oid sha256:dfcd770371589244e01ed9ac2a85fc9e113d4b96383901dcca61cb6a1038e060 +size 10984 diff --git a/tests/src/integration_test_hmd.cpp b/tests/src/integration_test_hmd.cpp index 7babf1ca229f2eb11ea665b3b9ce7ba0b4e2c82d..ee8600182ee2cb73f9084dcdd4d389e9e660ebc0 100644 --- a/tests/src/integration_test_hmd.cpp +++ b/tests/src/integration_test_hmd.cpp @@ -27,10 +27,10 @@ #include "catch/catch.hpp" -#include "phx/suppress_warnings.hpp" - #include "phx/core/engine.hpp" #include "phx/core/runtime_component.hpp" +#include "phx/display/hmd.hpp" +#include "phx/input/device_system.hpp" #include "phx/rendering/backend/opengl_image_buffer_data.hpp" #include "phx/rendering/components/light.hpp" #include "phx/rendering/components/transform.hpp" @@ -38,12 +38,11 @@ #include "phx/resources/resource_utils.hpp" #include "phx/setup.hpp" -SUPPRESS_WARNINGS_BEGIN #include "mocks/openvr_mock.hpp" #include "gl/texture.hpp" -SUPPRESS_WARNINGS_END +#include "test_utilities/glm_mat4.hpp" #include "test_utilities/opengl_buffer_data_comparison.hpp" #include "trompeloeil.hpp" @@ -158,7 +157,6 @@ SCENARIO("If a HMD is present we render for both eyes including controllers", ::MoveUserPlatform(engine.get()); WHEN("We run the engine") { - // TODO(JW): controller models should also be rendered engine->Run(); THEN("Submit() is called for both eyes submitting the expected images") { ::CheckSimilarity(::left_tex_id, "hmd_test_left.png"); @@ -167,3 +165,68 @@ SCENARIO("If a HMD is present we render for both eyes including controllers", } } } + +namespace { + +glm::mat4 ToMat4(float mat[12]) { + glm::mat4 result; + for (int i = 0; i < 12; ++i) { + result[i % 4][i / 4] = mat[i]; + } + return result; +} +} // namespace + +SCENARIO("The Eye to distance is changed via vive's eye distance knob", + "[phx][phx::HMD]") { + GIVEN("A standard engine that runs one frame") { + OPENVR_MOCK_ALLOW_ANY_CALL + phx::Engine engine; + phx::DeviceSystem* device_system = engine.CreateSystem<phx::DeviceSystem>(); + auto hmd = device_system->AddDevice<phx::HMD>(); + + REQUIRE(hmd->GetEyeToHeadMatrix(phx::HMD::LEFT_EYE) == + ToMat4(openvr_mock.eye_to_head_left_)); + REQUIRE(hmd->GetEyeToHeadMatrix(phx::HMD::RIGHT_EYE) == + ToMat4(openvr_mock.eye_to_head_right_)); + + WHEN("We change the eye distance while the engine is running") { + float eye_to_head_left[12] = {1.0f, 0.0f, 0.0f, 0.03f, 0.0f, 1.0f, + 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 12.0f}; + float eye_to_head_right[12] = {1.0f, 0.0f, 0.0f, -0.03f, 0.0f, 1.0f, + 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 13.0f}; + ALLOW_CALL(openvr_mock.GetSystem(), + GetEyeToHeadTransformArray(vr::EVREye::Eye_Left)) + .LR_RETURN(eye_to_head_left); + ALLOW_CALL(openvr_mock.GetSystem(), + GetEyeToHeadTransformArray(vr::EVREye::Eye_Right)) + .LR_RETURN(eye_to_head_right); + THEN("The eye distances are different") { + REQUIRE_FALSE(hmd->GetEyeToHeadMatrix(phx::HMD::LEFT_EYE) == + ToMat4(eye_to_head_left)); + REQUIRE_FALSE(hmd->GetEyeToHeadMatrix(phx::HMD::RIGHT_EYE) == + ToMat4(eye_to_head_right)); + WHEN("The HMD is Updated and an event is received") { + vr::VREvent_t event; + event.eventType = vr::VREvent_IpdChanged; + auto first_call = std::make_shared<bool>(true); + ALLOW_CALL(openvr_mock.GetSystem(), PollNextEvent(_, _)) + .SIDE_EFFECT(*_1 = event) + .SIDE_EFFECT(*first_call = false) + .WITH(*first_call == true) + .RETURN(true); + ALLOW_CALL(openvr_mock.GetSystem(), PollNextEvent(_, _)) + .WITH(*first_call == false) + .RETURN(false); + hmd->Update(); + THEN("The eye to head matrix has changed accordingly") { + REQUIRE(hmd->GetEyeToHeadMatrix(phx::HMD::LEFT_EYE) == + ToMat4(eye_to_head_left)); + REQUIRE(hmd->GetEyeToHeadMatrix(phx::HMD::RIGHT_EYE) == + ToMat4(eye_to_head_right)); + } + } + } + } + } +} diff --git a/tests/src/integration_test_model_rendering.cpp b/tests/src/integration_test_model_rendering.cpp index 729b5fc573b4f15c65d316a35ddfa63b460fc17c..39761c2ea606701292d9b3c2a9653c5fdc14f45b 100644 --- a/tests/src/integration_test_model_rendering.cpp +++ b/tests/src/integration_test_model_rendering.cpp @@ -27,18 +27,18 @@ #include "phx/suppress_warnings.hpp" -#include "phx/resources/loaders/assimp_model_loader.hpp" +#include "phx/core/frame_timer.hpp" +#include "phx/core/scene.hpp" #include "phx/display/display_system_window.hpp" #include "phx/display/window.hpp" -#include "phx/core/frame_timer.hpp" #include "phx/rendering/backend/opengl_image_buffer_data.hpp" #include "phx/rendering/components/mesh_handle.hpp" +#include "phx/rendering/components/mesh_render_settings.hpp" #include "phx/rendering/rendering_system.hpp" -#include "phx/resources/types/mesh.hpp" -#include "phx/resources/resource_declaration.hpp" +#include "phx/resources/loaders/assimp_model_loader.hpp" #include "phx/resources/resource_manager.hpp" #include "phx/resources/resource_utils.hpp" -#include "phx/core/scene.hpp" +#include "phx/resources/types/mesh.hpp" #include "phx/setup.hpp" #include "test_utilities/opengl_buffer_data_comparison.hpp" @@ -134,7 +134,7 @@ SCENARIO( "If no light and camera are give the model rendering takes default values", "[phx][phx::ModelLoading]") { ALLOW_CALL(openvr_mock.Get(), VR_IsHmdPresent()).RETURN(false); - GIVEN("A complete scene with two differently colored bunnies.") { + GIVEN("A complete scene with a bunny") { std::unique_ptr<phx::Engine> engine = phx::Setup::CreateDefaultEngine(); auto scene = engine->GetScene(); @@ -158,4 +158,34 @@ SCENARIO( } } +SCENARIO("We can render meshes as wireframe.", + "[phx][phx::MeshRenderSettings]") { + ALLOW_CALL(openvr_mock.Get(), VR_IsHmdPresent()).RETURN(false); + GIVEN("A complete scene with a bunny") { + std::unique_ptr<phx::Engine> engine = phx::Setup::CreateDefaultEngine(); + auto scene = engine->GetScene(); + + auto bunny = + LoadBunny(glm::vec3(0.0f, -0.1f, -0.3f), 0.0f, "bunny", scene.get()); + + auto rendering_system = engine->GetSystem<phx::RenderingSystem>(); + auto display_system = engine->GetSystem<phx::DisplaySystemWindow>(); + + WHEN("We set the rendering to be done in wireframe") { + auto render_settings = bunny->AddComponent<phx::MeshRenderSettings>(); + render_settings->SetWireframeMode(true); + THEN("the rendering matches our reference image") { + rendering_system->Update(phx::FrameTimer::TimeInfo{}); + display_system->Update(phx::FrameTimer::TimeInfo()); + phx::OpenGLImageBufferData<phx::OpenGLImageBufferDataType_RGB> buffer( + 1024, 768); + buffer.ReadColorPixels(true); + test_utilities::OpenGLBufferComparison:: + REQUIRE_REFERENCE_IMAGE_SIMILARITY( + buffer, "model_loading_wireframe.png", 1.0); + } + } + } +} + SUPPRESS_WARNINGS_END diff --git a/tests/src/integration_test_opengl_buffer_data_download.cpp b/tests/src/integration_test_opengl_buffer_data_download.cpp index 459f99ba1b3fc4785de28aaf5e70ce816e3a9bd6..237f49bd5455d3867d9fa9feb85596c1429f7193 100644 --- a/tests/src/integration_test_opengl_buffer_data_download.cpp +++ b/tests/src/integration_test_opengl_buffer_data_download.cpp @@ -46,7 +46,6 @@ SUPPRESS_WARNINGS_BEGIN #include "mocks/openvr_mock.hpp" SUPPRESS_WARNINGS_END -#include "test_utilities/dummy_material_generator.hpp" #include "test_utilities/dummy_mesh_generator.hpp" #include "test_utilities/opengl_buffer_data_comparison.hpp" @@ -91,14 +90,9 @@ class SceneSetupSimple { phx::Entity* triangle = scene->CreateEntity(); triangle->AddComponent<phx::Transform>(); - phx::ResourceManager::instance().RegisterResourceType( - "custommaterial", - std::make_unique<phx::DummyMaterialGenerator>( - glm::vec3(), glm::vec3(), glm::vec3(1.f, 0.5f, 0.25f), 500.0f)); - auto material = - phx::ResourceManager::instance().DeclareResource<phx::Material>( - {{"TYPE", "custommaterial"}}); - material.Load(); + auto material = phx::ResourceUtils::LoadGenericMaterial( + "custommaterial", glm::vec3(), glm::vec3(), glm::vec3(1.f, 0.5f, 0.25f), + 500.f); phx::MaterialHandle* material_handle = triangle->AddComponent<phx::MaterialHandle>(); diff --git a/tests/src/integration_test_rendering.cpp b/tests/src/integration_test_rendering.cpp index 0d23dbd4216219d241ce3bc04aa5ebfd669e6d2d..607b588d92db7d83bddd3a4933033188cafcdcac 100644 --- a/tests/src/integration_test_rendering.cpp +++ b/tests/src/integration_test_rendering.cpp @@ -35,15 +35,14 @@ #include "phx/rendering/backend/opengl_image_buffer_data.hpp" #include "phx/rendering/components/mesh_handle.hpp" #include "phx/rendering/rendering_system.hpp" -#include "phx/resources/types/mesh.hpp" #include "phx/resources/resource_manager.hpp" +#include "phx/resources/types/mesh.hpp" #include "phx/setup.hpp" SUPPRESS_WARNINGS_BEGIN #include "mocks/openvr_mock.hpp" SUPPRESS_WARNINGS_END -#include "test_utilities/dummy_material_generator.hpp" #include "test_utilities/dummy_mesh_generator.hpp" #include "test_utilities/opengl_buffer_data_comparison.hpp" @@ -108,14 +107,9 @@ SCENARIO("We can render a simple triangle", "[phx][phx::Rendering]") { phx::Entity* triangle = scene->CreateEntity(); CreateTestTriangleComponent(triangle); - phx::ResourceManager::instance().RegisterResourceType( - "custommaterial", std::make_unique<phx::DummyMaterialGenerator>( - glm::vec3(0.2f, 0.2f, 1.0f), glm::vec3(1, 1, 1), - glm::vec3(), 500.0f)); - auto material = - phx::ResourceManager::instance().DeclareResource<phx::Material>( - {{"TYPE", "custommaterial"}}); - material.Load(); + auto material = phx::ResourceUtils::LoadGenericMaterial( + "custommaterial", glm::vec3(0.2f, 0.2f, 1.0f), glm::vec3(1, 1, 1), + glm::vec3(), 500.0f); triangle->AddComponent<phx::Transform>(); phx::MaterialHandle* material_handle = diff --git a/tests/src/mocks/mocks.cpp b/tests/src/mocks/mocks.cpp new file mode 100644 index 0000000000000000000000000000000000000000..73334bc45712e87a4b20bbf33cee8eff29543345 --- /dev/null +++ b/tests/src/mocks/mocks.cpp @@ -0,0 +1,43 @@ +//------------------------------------------------------------------------------ +// Project Phoenix +// +// Copyright (c) 2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualization Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the 3-Clause BSD License (the "License"); +// you may not use this file except in compliance with the License. +// See the file LICENSE for the full text. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include "catch/catch.hpp" + +#include "trompeloeil.hpp" + +namespace trompeloeil { +template <> +void reporter<specialized>::send(severity s, const char* file, // NOLINT + unsigned long line, // NOLINT + const char* msg) { // NOLINT + std::ostringstream os; + if (line) os << file << ':' << line << '\n'; + os << msg; + auto failure = os.str(); + if (s == severity::fatal) { + FAIL(failure); + } else { + CAPTURE(failure); + CHECK(failure.empty()); + } +} +} // namespace trompeloeil diff --git a/tests/src/mocks/openvr_mock.hpp b/tests/src/mocks/openvr_mock.hpp index 83ec13827c371fd43eb7406175ab4393cb5867bd..38b02409278d868caf237b55a002dff137249a3e 100644 --- a/tests/src/mocks/openvr_mock.hpp +++ b/tests/src/mocks/openvr_mock.hpp @@ -36,7 +36,7 @@ SUPPRESS_WARNINGS_BEGIN #include "trompeloeil.hpp" SUPPRESS_WARNINGS_END -#include "openvr_mock_export.hpp" +#include "mocks/openvr_mock_export.hpp" using trompeloeil::_; @@ -114,8 +114,9 @@ class OPENVR_MOCK_EXPORT IVRSystemMock : public IVRSystem { MAKE_MOCK3(GetControllerState, bool(vr::TrackedDeviceIndex_t, vr::VRControllerState_t*, uint32_t)); MAKE_MOCK5(GetControllerStateWithPose, - bool(ETrackingUniverseOrigin, vr::TrackedDeviceIndex_t, - vr::VRControllerState_t*, uint32_t, TrackedDevicePose_t*)); + bool(vr::ETrackingUniverseOrigin, vr::TrackedDeviceIndex_t, + vr::VRControllerState_t*, uint32_t, + vr::TrackedDevicePose_t*)); MAKE_MOCK3(TriggerHapticPulse, void(vr::TrackedDeviceIndex_t, uint32_t, unsigned short)); MAKE_MOCK1(GetButtonIdNameFromEnum, const char*(EVRButtonId)); @@ -251,8 +252,6 @@ class OPENVR_MOCK_EXPORT IVRRenderModelsMock : public IVRRenderModels { } // namespace vr -SUPPRESS_WARNINGS_BEGIN_PADDED - class OPENVR_MOCK_EXPORT OpenVRMock { public: OpenVRMock() @@ -270,7 +269,7 @@ class OPENVR_MOCK_EXPORT OpenVRMock { } OpenVRMock& operator=(const OpenVRMock&) = delete; - OpenVRMock& operator=(OpenVRMock&&) = default; + OpenVRMock& operator=(OpenVRMock&&) = delete; OpenVRMockInternal& Get() { return *mock_; } vr::IVRSystemMock& GetSystem() const { return *ivr_system_mock_; } @@ -357,8 +356,6 @@ class OPENVR_MOCK_EXPORT OpenVRMock { vr::IVRRenderModelsMock* ivr_render_models_mock_; }; -SUPPRESS_WARNINGS_END - extern OPENVR_MOCK_EXPORT OpenVRMock openvr_mock; #define OPENVR_MOCK_ALLOW_ANY_CALL \ @@ -405,6 +402,24 @@ extern OPENVR_MOCK_EXPORT OpenVRMock openvr_mock; OpenVRMock::toHMDMatrix34_t(openvr_mock.l_cont_transformation_)) \ .SIDE_EFFECT(_1[openvr_mock.index_left_controller_].bPoseIsValid = \ true); \ + ALLOW_CALL(openvr_mock.GetSystem(), \ + GetControllerStateWithPose(vr::TrackingUniverseStanding, \ + openvr_mock.index_right_controller_, \ + _, _, _)) \ + .RETURN(true) \ + .SIDE_EFFECT( \ + _5->mDeviceToAbsoluteTracking = \ + OpenVRMock::toHMDMatrix34_t(openvr_mock.r_cont_transformation_)) \ + .SIDE_EFFECT(_5->bPoseIsValid = true); \ + ALLOW_CALL( \ + openvr_mock.GetSystem(), \ + GetControllerStateWithPose(vr::TrackingUniverseStanding, \ + openvr_mock.index_left_controller_, _, _, _)) \ + .RETURN(true) \ + .SIDE_EFFECT( \ + _5->mDeviceToAbsoluteTracking = \ + OpenVRMock::toHMDMatrix34_t(openvr_mock.l_cont_transformation_)) \ + .SIDE_EFFECT(_5->bPoseIsValid = true); \ ALLOW_CALL(openvr_mock.GetCompositor(), Submit(_, _, _, _)) \ .RETURN(vr::EVRCompositorError::VRCompositorError_None); \ ALLOW_CALL(openvr_mock.Get(), VR_GetGenericInterface(_, _)) \ @@ -451,6 +466,22 @@ extern OPENVR_MOCK_EXPORT OpenVRMock openvr_mock; ALLOW_CALL(openvr_mock.GetRenderModels(), LoadTexture_Async(_, _)) \ .RETURN(vr::VRRenderModelError_None) \ .SIDE_EFFECT(*_2 = openvr_mock.CreateDummyTexture( \ - static_cast<unsigned int>(_1))); + static_cast<unsigned int>(_1))); \ + ALLOW_CALL(openvr_mock.GetSystem(), PollNextEvent(_, _)).RETURN(false); \ + ALLOW_CALL(openvr_mock.GetSystem(), \ + GetInt32TrackedDeviceProperty(_, vr::Prop_Axis0Type_Int32, _)) \ + .RETURN(vr::k_eControllerAxis_None); \ + ALLOW_CALL(openvr_mock.GetSystem(), \ + GetInt32TrackedDeviceProperty(_, vr::Prop_Axis1Type_Int32, _)) \ + .RETURN(vr::k_eControllerAxis_None); \ + ALLOW_CALL(openvr_mock.GetSystem(), \ + GetInt32TrackedDeviceProperty(_, vr::Prop_Axis2Type_Int32, _)) \ + .RETURN(vr::k_eControllerAxis_None); \ + ALLOW_CALL(openvr_mock.GetSystem(), \ + GetInt32TrackedDeviceProperty(_, vr::Prop_Axis3Type_Int32, _)) \ + .RETURN(vr::k_eControllerAxis_None); \ + ALLOW_CALL(openvr_mock.GetSystem(), \ + GetInt32TrackedDeviceProperty(_, vr::Prop_Axis4Type_Int32, _)) \ + .RETURN(vr::k_eControllerAxis_None); #endif // TESTS_SRC_MOCKS_OPENVR_MOCK_HPP_ diff --git a/tests/src/mocks/sdl_mock.cpp b/tests/src/mocks/sdl_mock.cpp index 919ae46a7ece937b40f6c97ad5ccd936b21b35cb..3adf85769cb0e6a72a6723f7fbc1fce9e0bfc72f 100644 --- a/tests/src/mocks/sdl_mock.cpp +++ b/tests/src/mocks/sdl_mock.cpp @@ -33,8 +33,6 @@ SdlMock sdl_mock; #pragma clang diagnostic pop #endif -extern template struct trompeloeil::reporter<trompeloeil::specialized>; - extern "C" { int SDL_Init(Uint32 flags) { return sdl_mock.Get().SDL_Init(flags); } int SDL_InitSubSystem(Uint32 flags) { diff --git a/tests/src/mocks/sdl_mock.hpp b/tests/src/mocks/sdl_mock.hpp index c9146b5dda4c69339f0b10366da6f80b37acba15..1d00d1a68ce5b4aaf211871e66cb84680315e69b 100644 --- a/tests/src/mocks/sdl_mock.hpp +++ b/tests/src/mocks/sdl_mock.hpp @@ -31,7 +31,9 @@ SUPPRESS_WARNINGS_END #include "trompeloeil.hpp" -class SdlMockInternal { +#include "mocks/sdl_mock_export.hpp" + +class SDL_MOCK_EXPORT SdlMockInternal { public: MAKE_MOCK1(SDL_Init, int(Uint32 flags)); // NOLINT(readability/casting) MAKE_MOCK1(SDL_InitSubSystem, @@ -55,7 +57,7 @@ class SdlMockInternal { MAKE_MOCK1(SDL_HideWindow, void(SDL_Window*)); }; -class SdlMock { +class SDL_MOCK_EXPORT SdlMock { public: SdlMock() : mock_(new SdlMockInternal) {} SdlMock(const SdlMock&) = delete; @@ -64,7 +66,7 @@ class SdlMock { ~SdlMock() { delete mock_; } SdlMock& operator=(const SdlMock&) = delete; - SdlMock& operator=(SdlMock&&) = default; + SdlMock& operator=(SdlMock&&) = delete; SdlMockInternal& Get() { return *mock_; } @@ -72,7 +74,7 @@ class SdlMock { SdlMockInternal* mock_{nullptr}; }; -extern SdlMock sdl_mock; +extern SDL_MOCK_EXPORT SdlMock sdl_mock; #define SDL_MOCK_ALLOW_ANY_CALL \ ALLOW_CALL(sdl_mock.Get(), SDL_Init(SDL_INIT_VIDEO)).RETURN(0); \ diff --git a/tests/src/test-transform.cpp b/tests/src/test-transform.cpp index 4f844065e07dc3a97702f34d9cb4cef598eb9e11..a35aea6909dd71327875cae8dd57c77a3cf11f0f 100644 --- a/tests/src/test-transform.cpp +++ b/tests/src/test-transform.cpp @@ -39,7 +39,7 @@ SUPPRESS_WARNINGS_END #include "test_utilities/glm_mat4.hpp" #include "test_utilities/glm_quat.hpp" -#include "test_utilities/glm_vec3.hpp" +#include "test_utilities/glm_vec.hpp" #include "test_utilities/log_capture.hpp" SCENARIO( @@ -266,58 +266,6 @@ SCENARIO( SCENARIO( "The transform component enables hierarchical arrangement of transforms.", "[phx][phx::Transform]") { - GIVEN("Two entities in a scene with transform components.") { - auto scene = std::make_shared<phx::Scene>(); - auto entity1 = scene->CreateEntity(); - auto entity2 = scene->CreateEntity(); - phx::Transform* child = entity1->AddComponent<phx::Transform>(); - phx::Transform* parent = entity2->AddComponent<phx::Transform>(); - - WHEN("We query the first transform's parent.") { - auto parent_ptr = child->GetParent(); - THEN("It should be nullptr.") { REQUIRE(parent_ptr == nullptr); } - } - - WHEN("We set the first transform's parent to the second.") { - child->SetParent(parent); - - WHEN("We query the first transform's parent.") { - auto parent_ptr = child->GetParent(); - THEN("It should equal the second transform.") { - REQUIRE(parent_ptr == parent); - } - } - - WHEN("We query the second transform's child count.") { - auto child_count = parent->GetChildCount(); - THEN("Its size should equal 1.") { REQUIRE(child_count == 1); } - } - - WHEN("We query the second transform's first child.") { - auto child_ptr = parent->GetChild(0); - THEN("It should equal the first transform.") { - REQUIRE(child_ptr == child); - } - } - - WHEN("We iterate the second transform's children.") { - auto iteration_count = 0; - for (auto it = parent->begin(); it != parent->end(); ++it) - iteration_count++; - THEN("It should consist of a single iteration.") { - REQUIRE(iteration_count == 1); - } - } - - WHEN("We set the first transform's parent to nullptr.") { - child->SetParent(nullptr); - WHEN("We query the transform's parent.") { - auto parent_ptr = child->GetParent(); - THEN("It should be nullptr.") { REQUIRE(parent_ptr == nullptr); } - } - } - } - } GIVEN("A hierarchy of transforms: A( B, C( D ) )") { auto scene = std::make_shared<phx::Scene>(); auto entity1 = scene->CreateEntity(); diff --git a/tests/src/test_assimp_loader.cpp b/tests/src/test_assimp_loader.cpp index 16626f0aa7b5c5dc8cf2f39d99cee75eea8f4fc9..9711390663d673f35d6679ec15e424b86642a565 100644 --- a/tests/src/test_assimp_loader.cpp +++ b/tests/src/test_assimp_loader.cpp @@ -26,14 +26,18 @@ #include "catch/catch.hpp" +#include "mocks/opengl_mock.hpp" + #include "phx/core/logger.hpp" -#include "phx/resources/types/mesh.hpp" #include "phx/resources/loaders/assimp_model_loader.hpp" #include "phx/resources/resource_utils.hpp" +#include "phx/resources/types/mesh.hpp" #include "phx/utility/aspects/nameable.hpp" #include "test_utilities/log_capture.hpp" +extern template struct trompeloeil::reporter<trompeloeil::specialized>; + class TestLogger { public: TestLogger() : log_capture_{std::make_shared<test_utilities::LogCapture>()} { @@ -49,6 +53,7 @@ class TestLogger { SCENARIO("The assimp loader loads models using the Assimp library.", "[phx][phx::AssimpLoader]") { + OPENGL_MOCK_ALLOW_ANY_CALL GIVEN("A plain loader") { phx::CreateDefaultLogger(); TestLogger test_logger; diff --git a/tests/src/test_behavior_system.cpp b/tests/src/test_behavior_system.cpp index d7ce2e41fd95c38264588fbf57f7614c612dd97a..3584faecc25b600396331f84b53e838953f55c77 100644 --- a/tests/src/test_behavior_system.cpp +++ b/tests/src/test_behavior_system.cpp @@ -21,15 +21,17 @@ //------------------------------------------------------------------------------ #include <memory> -#include <vector> #include "catch/catch.hpp" #include "trompeloeil.hpp" #include "phx/core/engine.hpp" +#include "phx/core/logger.hpp" #include "phx/core/scene.hpp" #include "phx/scripting/behavior.hpp" #include "phx/scripting/behavior_system.hpp" +#include "phx/scripting/generic_behavior.hpp" +#include "test_utilities/log_capture.hpp" extern template struct trompeloeil::reporter<trompeloeil::specialized>; @@ -50,10 +52,11 @@ SCENARIO( auto scene = std::make_shared<phx::Scene>(); engine.SetScene(scene); auto entity = scene->CreateEntity(); - auto behavior = entity->AddComponent<BehaviorMock>(); - WHEN("We create a behavior system.") { - auto behavior_system = engine.CreateSystem<phx::BehaviorSystem>(); + auto behavior_system = engine.CreateSystem<phx::BehaviorSystem>(); + + WHEN("We create a mocked behavior") { + auto behavior = entity->AddComponent<BehaviorMock>(); WHEN("We update the behavior system 10 times.") { REQUIRE_CALL(*behavior, OnUpdate()).TIMES(10); for (auto i = 0; i < 10; ++i) @@ -62,3 +65,49 @@ SCENARIO( } } } +SCENARIO("we can have generic behaviors", "[phx][phx::GenericBehavior]") { + GIVEN("An engine with a behavior system and an entity") { + phx::Engine engine; + auto scene = std::make_shared<phx::Scene>(); + engine.SetScene(scene); + auto entity = scene->CreateEntity(); + auto behavior_system = engine.CreateSystem<phx::BehaviorSystem>(); + + WHEN("We create a generic behavior") { + bool onConstructionCalled = false; + bool onUpdateCalled = false; + entity->AddComponent<phx::GenericBehavior>( + [&onConstructionCalled](phx::Entity*) { + onConstructionCalled = true; + }, + [&onUpdateCalled](const phx::FrameTimer::TimeInfo*, phx::Entity*) { + onUpdateCalled = true; + }); + THEN("onConstruction should be called, but no onUpdate yet") { + REQUIRE(onConstructionCalled); + REQUIRE_FALSE(onUpdateCalled); + WHEN("The behavior system is updated") { + behavior_system->Update(phx::FrameTimer::TimeInfo()); + THEN("onUpdate is also called") { + REQUIRE(onConstructionCalled); + REQUIRE(onUpdateCalled); + } + } + } + } + WHEN("We we name a generic behavior") { + auto behavior = entity->AddComponent<phx::GenericBehavior>( + [](phx::Entity*) {}, + [](const phx::FrameTimer::TimeInfo*, phx::Entity*) {}, "GenericName"); + THEN("it is loggable") { + auto log_capture = std::make_shared<test_utilities::LogCapture>(); + phx::logger = + std::make_shared<spdlog::logger>("logcapture", log_capture); + phx::logger->set_pattern("%v"); + + phx::info("{}", *behavior); + REQUIRE(*log_capture == "GenericBehavior: GenericName"); + } + } + } +} diff --git a/tests/src/test_clear_pass.cpp b/tests/src/test_clear_pass.cpp index 2a8322c1a6361b681b86398e5a18b4d0df0f2a40..dce48d4c421761b0d77d5e3b5f087f061f54d827 100644 --- a/tests/src/test_clear_pass.cpp +++ b/tests/src/test_clear_pass.cpp @@ -20,8 +20,6 @@ // limitations under the License. //------------------------------------------------------------------------------ -#include "catch/catch.hpp" - #include "phx/suppress_warnings.hpp" SUPPRESS_WARNINGS_BEGIN @@ -31,11 +29,13 @@ SUPPRESS_WARNINGS_END #include "trompeloeil.hpp" -#include "mocks/opengl_mock.hpp" +#include "catch/catch.hpp" #include "phx/rendering/backend/render_target.hpp" #include "phx/rendering/render_passes/clear_pass.hpp" +#include "mocks/opengl_mock.hpp" + extern template struct trompeloeil::reporter<trompeloeil::specialized>; SCENARIO("The clear pass clears the active color and depth buffers.", diff --git a/tests/src/test_device_system.cpp b/tests/src/test_device_system.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dc5b0401fcbaf186a710783e4bc007b8187bb31f --- /dev/null +++ b/tests/src/test_device_system.cpp @@ -0,0 +1,83 @@ +//------------------------------------------------------------------------------ +// Project Phoenix +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualization Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the 3-Clause BSD License (the "License"); +// you may not use this file except in compliance with the License. +// See the file LICENSE for the full text. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include <memory> + +#include "catch/catch.hpp" + +#include "trompeloeil.hpp" + +#include "phx/core/engine.hpp" +#include "phx/input/device.hpp" +#include "phx/input/device_system.hpp" + +using trompeloeil::_; +using trompeloeil::ne; + +extern template struct trompeloeil::reporter<trompeloeil::specialized>; + +class MockDevice : public phx::Device { + public: + MAKE_MOCK0(Update, void()); +}; + +class MockDevice2 : public MockDevice { + public: + MAKE_MOCK0(Update, void()); +}; + +SCENARIO("The device system manages and updates its devices", + "[phx][phx::DeviceSystem]") { + GIVEN("an device system") { + phx::Engine engine; + auto device_system = engine.CreateSystem<phx::DeviceSystem>(); + + WHEN("We add devices") { + MockDevice* device1 = device_system->AddDevice<MockDevice>(); + MockDevice2* device2 = device_system->AddDevice<MockDevice2>(); + + THEN("their update method is called when the system is updated") { + REQUIRE_CALL(*device1, Update()).TIMES(1); + REQUIRE_CALL(*device2, Update()).TIMES(1); + device_system->Update(phx::FrameTimer::TimeInfo()); + } + + THEN("we can get these devices by type") { + auto devices = device_system->GetDevices<MockDevice>(); + REQUIRE(devices.size() == 2); + REQUIRE(std::find(devices.begin(), devices.end(), device1) != + devices.end()); + REQUIRE(std::find(devices.begin(), devices.end(), device2) != + devices.end()); + + auto devices2 = device_system->GetDevices<MockDevice2>(); + REQUIRE(devices2.size() == 1); + REQUIRE(devices2[0] == device2); + } + + THEN("devices can be removed again") { + device_system->RemoveDevice(device1); + REQUIRE(device_system->GetDevices<MockDevice>().size() == 1); + } + } + } +} diff --git a/tests/src/test_generic_material_loader.cpp b/tests/src/test_generic_material_loader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ed93d866e6ebb02e73eb6c0dda4466c517242983 --- /dev/null +++ b/tests/src/test_generic_material_loader.cpp @@ -0,0 +1,90 @@ +//------------------------------------------------------------------------------ +// Project Phoenix +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualization Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the 3-Clause BSD License (the "License"); +// you may not use this file except in compliance with the License. +// See the file LICENSE for the full text. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include "catch/catch.hpp" + +#include "phx/resources/resource_utils.hpp" +#include "phx/resources/types/material.hpp" + +#include "test_utilities/glm_vec.hpp" + +SCENARIO("The Generic Material Loader can create Generic materials.", + "[phx][phx::GenericMaterialLoader]") { + GIVEN("A plain Generic Material Loader") { + WHEN("We load a red material") { + auto materialResource = phx::ResourceUtils::LoadGenericMaterial( + "generic_material", glm::vec3(1.f, 0.f, 0.f)); + + THEN("We have a material with the given name") { + REQUIRE(materialResource != nullptr); + REQUIRE(materialResource->GetName() == "generic_material"); + } + + THEN( + "The material is red, and specular color, ambient color and " + "shininess are set to default values.") { + auto diffuseColor = materialResource->GetDiffuseColor(); + REQUIRE(diffuseColor == glm::vec3(1.0, 0.0, 0.0)); + + auto specularColor = materialResource->GetSpecularColor(); + auto specularColorDefault = + phx::Material::GetDefault()->GetSpecularColor(); + REQUIRE(specularColor == specularColorDefault); + + auto ambientColor = materialResource->GetAmbientColor(); + auto ambientColorDefault = + phx::Material::GetDefault()->GetAmbientColor(); + REQUIRE(ambientColor == ambientColorDefault); + + float shininess = materialResource->GetShininess(); + REQUIRE(shininess == 64.0f); // default shininess value + } + } + + WHEN( + "We load a different, more exotic material with some arbitrary " + "values") { + auto secondMaterial = phx::ResourceUtils::LoadGenericMaterial( + "generic_material_2", glm::vec3(0.4f, 0.3f, 0.55f), + glm::vec3(0.48f, 0.42f, 0.88f), glm::vec3(0.11f, 0.94f, 0.73f), 40.f); + + THEN("We still get a material with the given name") { + REQUIRE(secondMaterial != nullptr); + REQUIRE(secondMaterial->GetName() == "generic_material_2"); + } + + THEN("All the values in the material are set to the right value.") { + auto diffuseColor = secondMaterial->GetDiffuseColor(); + REQUIRE(diffuseColor == glm::vec3(0.4f, 0.3f, 0.55f)); + + auto specularColor = secondMaterial->GetSpecularColor(); + REQUIRE(specularColor == glm::vec3(0.48f, 0.42f, 0.88f)); + + auto ambientColor = secondMaterial->GetAmbientColor(); + REQUIRE(ambientColor == glm::vec3(0.11f, 0.94f, 0.73f)); + + float shininess = secondMaterial->GetShininess(); + REQUIRE(shininess == 40.f); + } + } + } +} diff --git a/tests/src/test_hierarchical.cpp b/tests/src/test_hierarchical.cpp new file mode 100644 index 0000000000000000000000000000000000000000..30a8437b52c98be02d0898aa12c141d7df807aff --- /dev/null +++ b/tests/src/test_hierarchical.cpp @@ -0,0 +1,97 @@ +//------------------------------------------------------------------------------ +// Project Phoenix +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualization Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the 3-Clause BSD License (the "License"); +// you may not use this file except in compliance with the License. +// See the file LICENSE for the full text. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include <cstddef> + +#include <memory> + +#include "catch/catch.hpp" + +#include "phx/utility/aspects/hierarchical.hpp" + +class SomeHierarchical : public phx::Hierarchical<SomeHierarchical> {}; + +SCENARIO("The hierarchical has parents and childs.", + "[phx][phx::Hierarchical]") { + GIVEN("Two hierarchical objects.") { + auto child = std::make_unique<SomeHierarchical>(); + auto parent = std::make_unique<SomeHierarchical>(); + WHEN("We query the first transform's parent.") { + auto parent_ptr = child->GetParent(); + THEN("It should be nullptr.") { REQUIRE(parent_ptr == nullptr); } + } + + WHEN("We set the first hierarchical's parent to the second.") { + child->SetParent(parent.get()); + + WHEN("We query the first hierarchical's parent.") { + auto parent_ptr = child->GetParent(); + THEN("It should equal the second hierarchical.") { + REQUIRE(parent_ptr == parent.get()); + } + } + + WHEN("We query the second hierarchical's child count.") { + auto child_count = parent->GetChildCount(); + THEN("Its size should equal 1.") { REQUIRE(child_count == 1); } + } + + WHEN("We query the second hierarchical's first child.") { + auto child_ptr = parent->GetChild(0); + THEN("It should equal the first hierarchical.") { + REQUIRE(child_ptr == child.get()); + } + } + + WHEN("We iterate the second hierarchical's children.") { + auto iteration_count = 0; + for (auto it = parent->begin(); it != parent->end(); ++it) + iteration_count++; + THEN("It should consist of a single iteration.") { + REQUIRE(iteration_count == 1); + } + } + + WHEN("We set the first hierarchical's parent to nullptr.") { + child->SetParent(nullptr); + WHEN("We query the hierarchical's parent.") { + auto parent_ptr = child->GetParent(); + THEN("It should be nullptr.") { REQUIRE(parent_ptr == nullptr); } + } + } + + WHEN("We destroy the child.") { + child.reset(); + THEN("The parent has no more children.") { + REQUIRE(parent->GetChildCount() == 0); + } + } + + WHEN("We destroy the parent.") { + parent.reset(); + THEN("The child has no more parent.") { + REQUIRE(child->GetParent() == nullptr); + } + } + } + } +} diff --git a/tests/src/test_material.cpp b/tests/src/test_material.cpp index 34f0552562a062641b060f9d222b54c5058fecda..93eb5ccb3eb8442d7417c346af9bea174c996904 100644 --- a/tests/src/test_material.cpp +++ b/tests/src/test_material.cpp @@ -29,7 +29,7 @@ #include "phx/core/logger.hpp" #include "phx/rendering/components/material_handle.hpp" -#include "test_utilities/glm_vec3.hpp" +#include "test_utilities/glm_vec.hpp" SCENARIO("The material component keeps track of its attributes", "[phx][phx::Material]") { @@ -42,7 +42,7 @@ SCENARIO("The material component keeps track of its attributes", REQUIRE(material.GetAmbientColor() == glm::vec3(0, 0, 0)); REQUIRE(material.GetSpecularColor() == glm::vec3(1, 1, 1)); REQUIRE(material.GetDiffuseColor() == glm::vec3(1, 0, 0)); - REQUIRE(material.GetShininess() == 1.0f); + REQUIRE(material.GetShininess() == 64.0f); } WHEN("we assign it to be some other material") { glm::vec3 blue(0, 0, 1), white(1, 1, 1), green(0, 1, 0); diff --git a/tests/src/test_model.cpp b/tests/src/test_model.cpp index 7098d7ca52f01c7dec6872d7a815ff80aee976b3..883d5e3b09fff45fb85c2f552b266b76073d4cdd 100644 --- a/tests/src/test_model.cpp +++ b/tests/src/test_model.cpp @@ -25,12 +25,18 @@ #include "catch/catch.hpp" #include "phx/resources/loaders/assimp_model_loader.hpp" -#include "phx/resources/types/model.hpp" #include "phx/resources/resource_manager.hpp" #include "phx/resources/resource_utils.hpp" +#include "phx/resources/types/model.hpp" + +#include "mocks/opengl_mock.hpp" +#include "trompeloeil.hpp" + +extern template struct trompeloeil::reporter<trompeloeil::specialized>; SCENARIO("A Model can be loaded that contains multiple meshes", "[phx][phx::Model]") { + OPENGL_MOCK_ALLOW_ANY_CALL GIVEN("A fresh resource manager...") { GIVEN("A file name of an .obj file...") { std::string model_file_name{"models/2MeshTest/2meshTest.obj"}; diff --git a/tests/src/test_openvr_controller_system.cpp b/tests/src/test_openvr_controller_model_system.cpp similarity index 86% rename from tests/src/test_openvr_controller_system.cpp rename to tests/src/test_openvr_controller_model_system.cpp index 0b2987fe9e6899e0ed6e1434137415228c94a054..1a90a5d68a079ee0d76f4d212626d0083e456326 100644 --- a/tests/src/test_openvr_controller_system.cpp +++ b/tests/src/test_openvr_controller_model_system.cpp @@ -34,9 +34,9 @@ SUPPRESS_WARNINGS_END #include "phx/core/entity.hpp" #include "phx/core/scene.hpp" -#include "phx/display/display_system_openvr.hpp" -#include "phx/input/openvr_controller_behavior.hpp" -#include "phx/input/openvr_controller_system.hpp" +#include "phx/input/device_system.hpp" +#include "phx/input/openvr_controller_model_system.hpp" +#include "phx/input/vr_controller.hpp" #include "phx/rendering/components/material_handle.hpp" #include "phx/rendering/components/mesh_handle.hpp" #include "phx/rendering/rendering_system.hpp" @@ -73,23 +73,23 @@ class EngineStopTestSystem : public phx::System { SCENARIO( "The OpenVRControllerSystem automatically inserts controller entities into " "a scene if controllers are connected", - "[phx][phx::OpenVRControllerSystem]") { + "[phx][phx::OpenVRControllerModelSystem]") { OPENGL_MOCK_ALLOW_ANY_CALL; OPENVR_MOCK_ALLOW_ANY_CALL; SDL_MOCK_ALLOW_ANY_CALL; phx::Engine engine; - auto display_system = engine.CreateSystem<phx::DisplaySystemOpenVR>(); + auto device_system = engine.CreateSystem<phx::DeviceSystem>(); - GIVEN( - "The display system has an HMD and the engine has a scene. Also, there's " - "an OpenVRControllerSystem.") { - display_system->CreateHMD(); + GIVEN("a device system with two vr controllers") { + device_system->AddDevice<phx::VRController>( + phx::VRController::LEFT_CONTROLLER); + device_system->AddDevice<phx::VRController>( + phx::VRController::RIGHT_CONTROLLER); auto scene = std::make_shared<phx::Scene>(); engine.SetScene(scene); - engine.CreateSystem<phx::OpenVRControllerSystem>(display_system); + engine.CreateSystem<phx::OpenVRControllerModelSystem>(device_system); engine.CreateSystem<EngineStopTestSystem>(); - engine.CreateSystem<phx::RenderingSystem>(display_system); WHEN("We run the engine for once frame (updating each system once)") { engine.Run(); diff --git a/tests/src/test_resource_utils.cpp b/tests/src/test_resource_utils.cpp index d2b0a9bb82b807735219581322600fec1e1c6e26..1da8fcab9e8789cba8a6a11d1a0261b45dc8aa36 100644 --- a/tests/src/test_resource_utils.cpp +++ b/tests/src/test_resource_utils.cpp @@ -20,15 +20,27 @@ // limitations under the License. //------------------------------------------------------------------------------ +#include <fstream> #include <string> #include "catch/catch.hpp" -#include "phx/resources/types/image.hpp" #include "phx/resources/resource_declaration.hpp" #include "phx/resources/resource_utils.hpp" +#include "phx/resources/types/image.hpp" #include "phx/resources_path.hpp" +namespace { +void CreateDummyFile(const std::string &file_name) { + std::fstream out_file(file_name.c_str(), std::ios::out); + // force fail test if file could not be created + REQUIRE(out_file.is_open()); +} +void DeleteDummyFile(const std::string &file_name) { + std::remove(file_name.c_str()); +} +} // namespace + SCENARIO("Resource utils can extract file extensions.", "[phx][phx::ResourceUtils]") { GIVEN("A file name") { @@ -56,17 +68,19 @@ SCENARIO("Resource utils can declarare a file resource.", GIVEN("A file name with absolute path") { std::string file_name{"C:/some_path/to/file.34.ext"}; - THEN("It can declare it") { + THEN("we get a declaration with a valid (unmodified) file name") { phx::ResourceDeclaration declaration = phx::ResourceUtils::DeclarationFromFile(file_name, {}, true); REQUIRE(declaration["TYPE"] == ".ext"); REQUIRE(declaration["file_name"] == file_name); } } - GIVEN("A file name with relative path") { - std::string file_name{"file.png"}; + GIVEN("A file name and a search path") { + std::string file_name{"textures/splash_progress.png"}; - THEN("It can declare it") { + THEN( + "we get a declaration with a valid file name that has been pre-pended " + "by the first hit in the search path") { phx::ResourceDeclaration declaration = phx::ResourceUtils::DeclarationFromFile(file_name); REQUIRE(declaration["TYPE"] == ".png"); @@ -74,11 +88,11 @@ SCENARIO("Resource utils can declarare a file resource.", } } GIVEN("Additional key, value pairs are provided") { - std::string file_name{"file.png"}; + std::string file_name{"textures/splash_progress.png"}; std::string key = "some_key"; int value = 42; - THEN("these are in the resulting declaration") { + THEN("the resulting declaration contains them") { phx::ResourceDeclaration declaration = phx::ResourceUtils::DeclarationFromFile(file_name, {{key, value}}, false); @@ -87,9 +101,26 @@ SCENARIO("Resource utils can declarare a file resource.", } } +SCENARIO("Resource utils find file resource outside the original search path", + "[phx][phx::ResourceUtils]") { + GIVEN("A file outside the original resource tree") { + std::string dummy_file = "dummy.txt"; + std::string offset_search_path = "../"; + ::CreateDummyFile(offset_search_path + dummy_file); + + phx::ResourceUtils::AddResourceSearchPath(offset_search_path); + THEN("the file is found and a valid declaration is returned") { + phx::ResourceDeclaration declaration = + phx::ResourceUtils::DeclarationFromFile(dummy_file, {}, false); + CHECK(declaration["file_name"] == offset_search_path + dummy_file); + ::DeleteDummyFile(offset_search_path + dummy_file); + } + } +} + SCENARIO("Resource utils can load a file resource.", "[phx][phx::ResourceUtils]") { - GIVEN("A file name") { + GIVEN("A file name within the original resource tree") { std::string file_name{"textures/splash_progress.png"}; THEN("It can load it") { diff --git a/tests/src/test_scene_loader.cpp b/tests/src/test_scene_loader.cpp index 1e69d84c692c57071f22d50d69d4df46bfe838d0..fefb838b4ec2a5fc8a0fd742f22ce9d624b754b9 100644 --- a/tests/src/test_scene_loader.cpp +++ b/tests/src/test_scene_loader.cpp @@ -30,7 +30,13 @@ #include "phx/rendering/components/transform.hpp" #include "phx/resources/loaders/scene_loader.hpp" +#include "mocks/opengl_mock.hpp" +#include "trompeloeil.hpp" + +extern template struct trompeloeil::reporter<trompeloeil::specialized>; + SCENARIO("The scene loader can load a model", "[phx][phx::SceneLoader]") { + OPENGL_MOCK_ALLOW_ANY_CALL GIVEN("An empty scene") { phx::Scene scene; WHEN("We load a model into the scene") { diff --git a/tests/src/test_tracked_device.cpp b/tests/src/test_tracked_device.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3166d86284093d5bb116345a8e69eeabdc5d2731 --- /dev/null +++ b/tests/src/test_tracked_device.cpp @@ -0,0 +1,135 @@ +//------------------------------------------------------------------------------ +// Project Phoenix +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualization Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the 3-Clause BSD License (the "License"); +// you may not use this file except in compliance with the License. +// See the file LICENSE for the full text. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include <memory> + +#include "catch/catch.hpp" + +#include "phx/display/hmd.hpp" +#include "phx/input/tracked_device.hpp" +#include "phx/input/vr_controller.hpp" + +#include "mocks/openvr_mock.hpp" + +#include "trompeloeil.hpp" + +#undef _ + +extern template struct trompeloeil::reporter<trompeloeil::specialized>; +using trompeloeil::_; + +SCENARIO("OpenVR is only initialized and shutdown once", + "[phx][phx::TrackedDevice]") { + OPENVR_MOCK_ALLOW_ANY_CALL + + WHEN("we initialize 2 derrived tracked devices") { + THEN("openVR is initialized exactly once") { + REQUIRE_CALL(openvr_mock.Get(), + VR_InitInternal2(_, vr::VRApplication_Scene, _)) + .TIMES(1) + .RETURN(static_cast<uint32_t>(1337)) + .SIDE_EFFECT(*_1 = vr::VRInitError_None); + auto device1 = std::make_unique<phx::HMD>(); + auto device2 = std::make_unique<phx::VRController>( + phx::VRController::LEFT_CONTROLLER); + + WHEN("we destroy one") { + THEN("openVR shutdown is not called") { + FORBID_CALL(openvr_mock.Get(), VR_ShutdownInternal()); + device1.reset(); + WHEN("we destroy the second one") { + THEN("openVR shutdown is called") { + REQUIRE_CALL(openvr_mock.Get(), VR_ShutdownInternal()).TIMES(1); + device2.reset(); + } + } + } + } + } + } +} + +#define ALLOW_EVENT_POLL_CALLS \ + ALLOW_CALL(openvr_mock.GetSystem(), PollNextEvent(_, _)) \ + .SIDE_EFFECT(*_1 = event) \ + .SIDE_EFFECT(*first_call = false) \ + .WITH(*first_call == true) \ + .RETURN(true); \ + ALLOW_CALL(openvr_mock.GetSystem(), PollNextEvent(_, _)) \ + .WITH(*first_call == false) \ + .RETURN(false); + +class ActualDevice : public phx::TrackedDevice { + public: + ActualDevice() : TrackedDevice() { id_ = 1; } + void Update() override { TrackedDevice::Update(); } + void UpdateDeviceIndex() override {} + MAKE_MOCK1(OnOpenVREvent, void(const vr::VREvent_t&)); +}; + +SCENARIO("OpenVR events are forwarded as signals to all tracked devices", + "[phx][phx::TrackedDevice]") { + OPENVR_MOCK_ALLOW_ANY_CALL + GIVEN("a derrived tracked device and some OpenVR event") { + auto device = std::make_unique<ActualDevice>(); + + auto first_call = std::make_shared<bool>(true); + vr::VREvent_t event; + event.eventType = vr::EVREventType::VREvent_ButtonPress; + WHEN("Update is called") { + THEN("OnOpenVREvent is called") { + *first_call = true; + ALLOW_EVENT_POLL_CALLS + REQUIRE_CALL(*device, OnOpenVREvent(_)) + .WITH(_1.eventType == vr::EVREventType::VREvent_ButtonPress); + + device->Update(); + } + } + WHEN("we create a second device") { + auto device2 = std::make_unique<ActualDevice>(); + THEN("the onOpenVREvent is called for both devices") { + *first_call = true; + ALLOW_EVENT_POLL_CALLS + REQUIRE_CALL(*device, OnOpenVREvent(_)) + .WITH(_1.eventType == vr::EVREventType::VREvent_ButtonPress); + REQUIRE_CALL(*device2, OnOpenVREvent(_)) + .WITH(_1.eventType == vr::EVREventType::VREvent_ButtonPress); + device->Update(); + device2->Update(); + + WHEN("the first device is deleted") { + device.reset(); + THEN("the second device still gets the event") { + *first_call = true; + ALLOW_EVENT_POLL_CALLS + REQUIRE_CALL(*device2, OnOpenVREvent(_)) + .WITH(_1.eventType == vr::EVREventType::VREvent_ButtonPress); + device2->Update(); + } + } + } + } + } +} + +// the pose etc. is tested in integration_test_hmd diff --git a/tests/src/test_tracking_system.cpp b/tests/src/test_tracking_system.cpp index 44124c76815edf17b45827619708cb257c698973..40de8072af27d14242bdd5fca156463bedf50692 100644 --- a/tests/src/test_tracking_system.cpp +++ b/tests/src/test_tracking_system.cpp @@ -27,6 +27,7 @@ #include "trompeloeil.hpp" +#include "phx/display/hmd.hpp" #include "phx/suppress_warnings.hpp" SUPPRESS_WARNINGS_BEGIN @@ -37,7 +38,7 @@ SUPPRESS_WARNINGS_END #include "phx/core/entity.hpp" #include "phx/core/runtime_component.hpp" #include "phx/core/scene.hpp" -#include "phx/display/display_system_openvr.hpp" +#include "phx/input/device_system.hpp" #include "phx/rendering/components/projection.hpp" #include "phx/rendering/components/transform.hpp" #include "phx/tracking/tracking_system_openvr.hpp" @@ -325,12 +326,17 @@ SCENARIO( SDL_MOCK_ALLOW_ANY_CALL; phx::Engine engine; - auto display_system = engine.CreateSystem<phx::DisplaySystemOpenVR>(); + auto device_system = engine.CreateSystem<phx::DeviceSystem>(); GIVEN( "The display system has an HMD and the engine has a scene with a " "user platform.") { - display_system->CreateHMD(); + device_system->AddDevice<phx::HMD>(); + device_system->AddDevice<phx::VRController>( + phx::VRController::LEFT_CONTROLLER); + device_system->AddDevice<phx::VRController>( + phx::VRController::RIGHT_CONTROLLER); + device_system->Update(phx::FrameTimer::TimeInfo()); auto first_scene = std::make_shared<phx::Scene>(); engine.SetScene(first_scene); auto platform_trans_mat = @@ -339,7 +345,7 @@ SCENARIO( ::SetPlatformMatrix(first_scene.get(), platform_trans_mat); WHEN("A tracking system is created and initialized.") { auto tracking_system = - engine.CreateSystem<phx::TrackingSystemOpenVR>(display_system); + engine.CreateSystem<phx::TrackingSystemOpenVR>(device_system); ::TestRuntimeEntityStructure(first_scene); ::TestRuntimeEntityUpdate(first_scene, tracking_system); diff --git a/tests/src/test_vr_controller.cpp b/tests/src/test_vr_controller.cpp new file mode 100644 index 0000000000000000000000000000000000000000..052a8ddd02495dd5a2902655c8b75a8a8ab5b182 --- /dev/null +++ b/tests/src/test_vr_controller.cpp @@ -0,0 +1,166 @@ +//------------------------------------------------------------------------------ +// Project Phoenix +// +// Copyright (c) 2017-2018 RWTH Aachen University, Germany, +// Virtual Reality & Immersive Visualization Group. +//------------------------------------------------------------------------------ +// License +// +// Licensed under the 3-Clause BSD License (the "License"); +// you may not use this file except in compliance with the License. +// See the file LICENSE for the full text. +// You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// 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 CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//------------------------------------------------------------------------------ + +#include <memory> +#include <string> + +#include "catch/catch.hpp" + +#include "phx/core/logger.hpp" +#include "phx/input/vr_controller.hpp" + +#include "mocks/openvr_mock.hpp" + +#include "test_utilities/glm_vec.hpp" +#include "test_utilities/log_capture.hpp" + +#include "trompeloeil.hpp" + +extern template struct trompeloeil::reporter<trompeloeil::specialized>; +using trompeloeil::_; + +namespace { + +auto first_call = std::make_shared<bool>(true); + +void CheckForButtonEvent(uint32_t openVR_event_type, + phx::VRController::ButtonEvent vr_cont_event, + uint32_t button_id, const std::string& side, + bool expect_to_fire, + phx::VRController* vr_controller) { + WHEN("a " + side + + " openVR event (type: " + std::to_string(openVR_event_type) + + " button id: " + std::to_string(button_id) + " is fired") { + vr::VREvent_t event; + event.eventType = openVR_event_type; + event.data.controller.button = button_id; + event.trackedDeviceIndex = + (side == "left" ? 2 // in the mock 2 is given to the left controller + : 1); // in the mock 1 is given to the right controller + *first_call = true; + ALLOW_CALL(openvr_mock.GetSystem(), PollNextEvent(_, _)) + .SIDE_EFFECT(*_1 = event) + .SIDE_EFFECT(*first_call = false) + .WITH(*first_call == true) + .RETURN(true); + ALLOW_CALL(openvr_mock.GetSystem(), PollNextEvent(_, _)) + .WITH(*first_call == false) + .RETURN(false); + THEN("we expect the signal to " + (expect_to_fire ? "" : "not") + " fire") { + bool signal_fired = false; + vr_controller->RegisterButtonSignal( + [&signal_fired, vr_cont_event, button_id]( + phx::VRController::ButtonId id, + phx::VRController::ButtonEvent input_event) { + if (input_event == vr_cont_event && + id == static_cast<phx::VRController::ButtonId>(button_id)) + signal_fired = true; + }); + vr_controller->Update(); + REQUIRE(signal_fired == expect_to_fire); + } + } +} +} // namespace + +SCENARIO("Button click events of openVR get forwarded as signals", + "[phx][phx::VRController]") { + OPENVR_MOCK_ALLOW_ANY_CALL + GIVEN("a VRController") { + auto vr_controller = + std::make_unique<phx::VRController>(phx::VRController::LEFT_CONTROLLER); + THEN("all button events are mapped correctly") { + CheckForButtonEvent(vr::VREvent_ButtonPress, + phx::VRController::BUTTON_PRESSED, vr::k_EButton_Grip, + "left", true, vr_controller.get()); + CheckForButtonEvent( + vr::VREvent_ButtonUnpress, phx::VRController::BUTTON_RELEASED, + vr::k_EButton_Grip, "left", true, vr_controller.get()); + CheckForButtonEvent(vr::VREvent_ButtonTouch, + phx::VRController::BUTTON_TOUCH, vr::k_EButton_Grip, + "left", true, vr_controller.get()); + CheckForButtonEvent(vr::VREvent_ButtonUntouch, + phx::VRController::BUTTON_UNTOUCH, vr::k_EButton_Grip, + "left", true, vr_controller.get()); + } + THEN("wrong sided events are ignored") { + CheckForButtonEvent(vr::VREvent_ButtonPress, + phx::VRController::BUTTON_PRESSED, vr::k_EButton_Grip, + "right", false, vr_controller.get()); + } + + THEN( + "events on other buttons are ignored, respectively the correct button " + "id is forwarded") { + CheckForButtonEvent( + vr::VREvent_ButtonPress, phx::VRController::BUTTON_PRESSED, + vr::k_EButton_SteamVR_Trigger, "right", false, vr_controller.get()); + } + } +} + +SCENARIO("we can get the actual position on the analog axes, like the trackpad", + "[phx][phx::VRController]") { + OPENVR_MOCK_ALLOW_ANY_CALL + GIVEN("a VRController") { + auto vr_controller = + std::make_unique<phx::VRController>(phx::VRController::LEFT_CONTROLLER); + + WHEN("for an axis is asked that does not exist for this mocked device") { + auto log_capture = std::make_shared<test_utilities::LogCapture>(); + phx::logger = std::make_shared<spdlog::logger>("logcapture", log_capture); + phx::logger->set_pattern("%w"); + ALLOW_CALL(openvr_mock.GetSystem(), GetControllerAxisTypeNameFromEnum( + vr::k_eControllerAxis_Trigger)) + .RETURN("k_eControllerAxis_Trigger"); + + vr_controller->GetAxesValue(phx::VRController::AXES_TRIGGER); + + THEN("we expect a warning") { + REQUIRE(*log_capture == + "VRController device does not provide the requested axes: " + "k_eControllerAxis_Trigger "); + } + } + + WHEN("we ask for an existing axis the values are returned") { + ALLOW_CALL( + openvr_mock.GetSystem(), + GetControllerStateWithPose(vr::TrackingUniverseStanding, 2, _, _, _)) + .RETURN(true) + .SIDE_EFFECT(_3->rAxis[0].x = 0.5f) + .SIDE_EFFECT(_3->rAxis[0].y = 0.7f); + ALLOW_CALL(openvr_mock.GetSystem(), + GetInt32TrackedDeviceProperty(_, vr::Prop_Axis0Type_Int32, _)) + .RETURN(vr::k_eControllerAxis_Trigger); + + glm::vec2 value = + vr_controller->GetAxesValue(phx::VRController::AXES_TRIGGER); + + THEN("we get the expected values") { + REQUIRE(value == glm::vec2(0.5f, 0.7f)); + } + } + } +} + +// TODO(JW) try also axes diff --git a/tests/src/tests.cpp b/tests/src/tests.cpp index 470c22fbbdaf5a1a78617debe061cf17ac89d6bb..ca1bc6a8bef06092ff36028073f3e64a8022b451 100644 --- a/tests/src/tests.cpp +++ b/tests/src/tests.cpp @@ -22,23 +22,3 @@ #define CATCH_CONFIG_MAIN #include "catch/catch.hpp" - -#include "trompeloeil.hpp" - -namespace trompeloeil { -template <> -void reporter<specialized>::send(severity s, const char* file, // NOLINT - unsigned long line, // NOLINT - const char* msg) { // NOLINT - std::ostringstream os; - if (line) os << file << ':' << line << '\n'; - os << msg; - auto failure = os.str(); - if (s == severity::fatal) { - FAIL(failure); - } else { - CAPTURE(failure); - CHECK(failure.empty()); - } -} -} // namespace trompeloeil diff --git a/tests/test_utilities/approx.hpp b/tests/test_utilities/approx.hpp index f09bbcaaba026bf7bad7ccfeaf8468dfe4ba3e1b..bfd3956d4824c5a64936d39a9ceb4c35922519c6 100644 --- a/tests/test_utilities/approx.hpp +++ b/tests/test_utilities/approx.hpp @@ -38,8 +38,6 @@ SUPPRESS_WARNINGS_END namespace test_utilities { -SUPPRESS_WARNINGS_BEGIN_PADDED - template <typename T> class Approx { public: @@ -76,8 +74,6 @@ class Approx { const T& value_; }; -SUPPRESS_WARNINGS_END - } // namespace test_utilities namespace Catch { diff --git a/tests/test_utilities/dummy_mesh_generator.hpp b/tests/test_utilities/dummy_mesh_generator.hpp index 49c73df2c9849330a259cd0883c07de04e87826c..5febf12190e0be8621034ef4bdb7cefeaab617a7 100644 --- a/tests/test_utilities/dummy_mesh_generator.hpp +++ b/tests/test_utilities/dummy_mesh_generator.hpp @@ -46,7 +46,7 @@ class DummyMeshLoader : public ResourceLoadStrategy { std::vector<unsigned int> &&indices); DummyMeshLoader(const DummyMeshLoader &) = default; DummyMeshLoader(DummyMeshLoader &&) = default; - ~DummyMeshLoader() = default; + ~DummyMeshLoader() override = default; DummyMeshLoader &operator=(const DummyMeshLoader &) = default; DummyMeshLoader &operator=(DummyMeshLoader &&) = default; diff --git a/tests/test_utilities/glm_vec3.hpp b/tests/test_utilities/glm_vec.hpp similarity index 58% rename from tests/test_utilities/glm_vec3.hpp rename to tests/test_utilities/glm_vec.hpp index d33ee272edb8b709e0fdb86e2d6f3f443d5a7f8f..2c56f39eae5c91ba404fcedf5269ada536f32e7f 100644 --- a/tests/test_utilities/glm_vec3.hpp +++ b/tests/test_utilities/glm_vec.hpp @@ -20,8 +20,8 @@ // limitations under the License. //------------------------------------------------------------------------------ -#ifndef TESTS_TEST_UTILITIES_GLM_VEC3_HPP_ -#define TESTS_TEST_UTILITIES_GLM_VEC3_HPP_ +#ifndef TESTS_TEST_UTILITIES_GLM_VEC_HPP_ +#define TESTS_TEST_UTILITIES_GLM_VEC_HPP_ #include <algorithm> #include <limits> @@ -42,6 +42,15 @@ SUPPRESS_WARNINGS_END namespace Catch { +template <> +struct StringMaker<glm::vec2> { + static std::string convert(const glm::vec2& vector) { + std::ostringstream sstr; + sstr << vector; + return sstr.str(); + } +}; + template <> struct StringMaker<glm::vec3> { static std::string convert(const glm::vec3& vector) { @@ -51,25 +60,50 @@ struct StringMaker<glm::vec3> { } }; +template <> +struct StringMaker<glm::vec4> { + static std::string convert(const glm::vec4& vector) { + std::ostringstream sstr; + sstr << vector; + return sstr.str(); + } +}; + } // namespace Catch namespace test_utilities { -template <> -inline bool Approx<glm::vec3>::operator==(const glm::vec3& rhs) { +template <class VectorType> +bool CompareVectors(VectorType a, VectorType b, int rows, float eps, + float margin, float scale) { bool result = true; - for (int row = 0; row < 3; ++row) { - const auto this_element = value_[row]; + for (int row = 0; row < rows; ++row) { + const auto this_element = a[row]; auto this_approx_element = Catch::Detail::Approx(this_element); - this_approx_element.epsilon(epsilon_); - this_approx_element.margin(margin_); - this_approx_element.scale(scale_); - const auto other_element = rhs[row]; + this_approx_element.epsilon(eps); + this_approx_element.margin(margin); + this_approx_element.scale(scale); + const auto other_element = b[row]; result &= (other_element == this_approx_element); } return result; } +template <> +inline bool Approx<glm::vec2>::operator==(const glm::vec2& rhs) { + return CompareVectors<glm::vec2>(value_, rhs, 2, epsilon_, margin_, scale_); +} + +template <> +inline bool Approx<glm::vec3>::operator==(const glm::vec3& rhs) { + return CompareVectors<glm::vec3>(value_, rhs, 3, epsilon_, margin_, scale_); +} + +template <> +inline bool Approx<glm::vec4>::operator==(const glm::vec4& rhs) { + return CompareVectors<glm::vec4>(value_, rhs, 4, epsilon_, margin_, scale_); +} + } // namespace test_utilities -#endif // TESTS_TEST_UTILITIES_GLM_VEC3_HPP_ +#endif // TESTS_TEST_UTILITIES_GLM_VEC_HPP_ diff --git a/tests/test_utilities/log_capture.hpp b/tests/test_utilities/log_capture.hpp index ba70a17190eb1d0c9f1ba7da9b9cc2cea32457cf..aa0e5dddec6f8ee823877b6e032eafe98b81cf5b 100644 --- a/tests/test_utilities/log_capture.hpp +++ b/tests/test_utilities/log_capture.hpp @@ -34,8 +34,6 @@ namespace test_utilities { -SUPPRESS_WARNINGS_BEGIN_PADDED - class LogCapture : public spdlog::sinks::sink { public: void log(const spdlog::details::log_msg& msg) override; @@ -49,8 +47,6 @@ class LogCapture : public spdlog::sinks::sink { std::stringstream log_stream_; }; -SUPPRESS_WARNINGS_END - } // namespace test_utilities namespace Catch {