Unified · Split
| @@ -185,22 +185,8 @@ set(EXTERNAL_PROJECT_PREFIX "project") · 184 unmodified lines | |||
| 185 | set_property(DIRECTORY PROPERTY EP_PREFIX ${EXTERNAL_PROJECT_PREFIX}) | 185 | set_property(DIRECTORY PROPERTY EP_PREFIX ${EXTERNAL_PROJECT_PREFIX}) |
| 186 | setup_externals_binary_dir() | 186 | setup_externals_binary_dir() |
| 187 | 187 | ||
| 188 | # setup options to grab external project dependencies | ||
| 189 | option(GET_BULLET "Get Bullet library automatically as external project" 1) | ||
| 190 | option(GET_GLM "Get GLM library automatically as external project" 1) | ||
| 191 | option(GET_GVERB "Get Gverb library automatically as external project" 1) | ||
| 192 | option(GET_TBB "Get Threading Building Blocks library automatically as external project" 1) | ||
| 193 | option(GET_LIBOVR "Get LibOVR library automatically as external project" 1) | ||
| 194 | option(GET_VHACD "Get V-HACD library automatically as external project" 1) | ||
| 195 | option(GET_POLYVOX "Get polyvox library automatically as external project" 1) | ||
| 196 | option(GET_OPENVR "Get OpenVR library automatically as external project" 1) | ||
| 197 | option(GET_BOOSTCONFIG "Get Boost-config library automatically as external project" 1) | ||
| 198 | option(GET_OGLPLUS "Get OGLplus library automatically as external project" 1) | ||
| 199 | option(GET_GLEW "Get GLEW library automatically as external project" 1) | ||
| 200 | |||
| 201 | option(USE_NSIGHT "Attempt to find the nSight libraries" 1) | 188 | option(USE_NSIGHT "Attempt to find the nSight libraries" 1) |
| 202 | 189 | ||
| 203 | option(GET_SDL2 "Get SDL2 library automatically as external project" 0) | ||
| 204 | 190 | ||
| 205 | if (WIN32) | 191 | if (WIN32) |
| 206 | add_paths_to_fixup_libs("${QT_DIR}/bin") | 192 | add_paths_to_fixup_libs("${QT_DIR}/bin") |
| @@ -1,12 +1,12 @@ | |||
| 1 | set(TARGET_NAME assignment-client) | 1 | set(TARGET_NAME assignment-client) |
| 2 | 2 | ||
| 3 | setup_hifi_project(Core Gui Network Script Widgets WebSockets) | 3 | setup_hifi_project(Core Gui Network Script Quick Widgets WebSockets) |
| 4 | 4 | ||
| 5 | # link in the shared libraries | 5 | # link in the shared libraries |
| 6 | link_hifi_libraries( | 6 | link_hifi_libraries( |
| 7 | audio avatars octree environment gpu model fbx entities | 7 | audio avatars octree environment gpu model fbx entities |
| 8 | networking animation shared script-engine embedded-webserver | 8 | networking animation shared script-engine embedded-webserver |
| 9 | physics | 9 | controllers physics |
| 10 | ) | 10 | ) |
| 11 | 11 | ||
| 12 | include_application_version() | 12 | include_application_version() |
| @@ -0,0 +1,30 @@ | |||
| 1 | set(EXTERNAL_NAME faceshift) | ||
| 2 | |||
| 3 | include(ExternalProject) | ||
| 4 | ExternalProject_Add( | ||
| 5 | ${EXTERNAL_NAME} | ||
| 6 | URL https://hifi-public.s3.amazonaws.com/dependencies/faceshift.zip | ||
| 7 | CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> | ||
| 8 | BINARY_DIR ${EXTERNAL_PROJECT_PREFIX}/build | ||
| 9 | LOG_DOWNLOAD 1 | ||
| 10 | LOG_CONFIGURE 1 | ||
| 11 | LOG_BUILD 1 | ||
| 12 | ) | ||
| 13 | |||
| 14 | # URL_MD5 1bdcb8a0b8d5b1ede434cc41efade41d | ||
| 15 | |||
| 16 | # Hide this external target (for ide users) | ||
| 17 | set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") | ||
| 18 | |||
| 19 | ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) | ||
| 20 | |||
| 21 | string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) | ||
| 22 | set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE FILEPATH "Path to Faceshift include directory") | ||
| 23 | |||
| 24 | if (WIN32) | ||
| 25 | set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/Debug/faceshift.lib CACHE FILEPATH "Faceshift libraries") | ||
| 26 | set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/Release/faceshift.lib CACHE FILEPATH "Faceshift libraries") | ||
| 27 | elseif (APPLE) | ||
| 28 | set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/Debug/libfaceshift.a CACHE FILEPATH "Faceshift libraries") | ||
| 29 | set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/Release/libfaceshift.a CACHE FILEPATH "Faceshift libraries") | ||
| 30 | endif() | ||
| @@ -47,25 +47,13 @@ if (APPLE) · 46 unmodified lines | |||
| 47 | ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) | 47 | ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) |
| 48 | set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${SOURCE_DIR}/SDL2.framework/Headers CACHE PATH "Location of SDL2 include directory") | 48 | set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${SOURCE_DIR}/SDL2.framework/Headers CACHE PATH "Location of SDL2 include directory") |
| 49 | set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${SOURCE_DIR}/SDL2.framework/SDL2 CACHE STRING "Path to SDL2 library") | 49 | set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${SOURCE_DIR}/SDL2.framework/SDL2 CACHE STRING "Path to SDL2 library") |
| 50 | elseif (WIN32) | ||
| 51 | ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) | ||
| 52 | set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${SOURCE_DIR}/include CACHE PATH "Location of SDL2 include directory") | ||
| 53 | set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${SOURCE_DIR}/lib/x86/SDL2.lib CACHE FILEPATH "Path to SDL2 library") | ||
| 54 | set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR}/lib/x86 CACHE PATH "Location of SDL2 DLL") | ||
| 50 | else () | 55 | else () |
| 51 | if (WIN32) | 56 | ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) |
| 52 | ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) | 57 | set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${INSTALL_DIR}/include/SDL2 CACHE PATH "Location of SDL2 include directory") |
| 53 | set(_ROOT_DIR ${SOURCE_DIR}) | 58 | set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${INSTALL_DIR}/lib/libSDL2.so CACHE FILEPATH "Path to SDL2 library") |
| 54 | set(_INCLUDE_DIR ${_ROOT_DIR}/include) | 59 | endif () |
| 55 | set(_LIB_DIR "${SOURCE_DIR}/lib/x86") | ||
| 56 | set(_LIB_EXT "lib") | ||
| 57 | |||
| 58 | set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${_LIB_DIR} CACHE PATH "Location of SDL2 DLL") | ||
| 59 | else () | ||
| 60 | ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) | ||
| 61 | set(_ROOT_DIR ${INSTALL_DIR}) | ||
| 62 | set(_INCLUDE_DIR ${_ROOT_DIR}/include/SDL2) | ||
| 63 | |||
| 64 | set(_LIB_DIR ${INSTALL_DIR}/lib) | ||
| 65 | set(_LIB_EXT "so") | ||
| 66 | set(_LIB_PREFIX "lib") | ||
| 67 | endif () | ||
| 68 | |||
| 69 | set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${_INCLUDE_DIR} CACHE PATH "Location of SDL2 include directory") | ||
| 70 | set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${_LIB_DIR}/${_LIB_PREFIX}SDL2.${_LIB_EXT} CACHE FILEPATH "Path to SDL2 library") | ||
| 71 | endif () | ||
| @@ -1,30 +1,20 @@ | |||
| 1 | include(ExternalProject) | 1 | include(ExternalProject) |
| 2 | include(SelectLibraryConfigurations) | ||
| 3 | 2 | ||
| 4 | set(EXTERNAL_NAME Sixense) | 3 | set(EXTERNAL_NAME sixense) |
| 5 | 4 | ||
| 6 | string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) | 5 | string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) |
| 7 | 6 | ||
| 8 | ExternalProject_Add( | 7 | ExternalProject_Add( |
| 9 | ${EXTERNAL_NAME} | 8 | ${EXTERNAL_NAME} |
| 10 | URL ./SixenseSDK_062612.zip | 9 | URL http://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_071615.zip |
| 11 | URL_MD5 10cc8dc470d2ac1244a88cf04bc549cc | 10 | URL_MD5 752a3901f334124e9cffc2ba4136ef7d |
| 12 | CONFIGURE_COMMAND "" | 11 | CONFIGURE_COMMAND "" |
| 13 | BUILD_COMMAND "" | 12 | BUILD_COMMAND "" |
| 14 | INSTALL_COMMAND "" | 13 | INSTALL_COMMAND "" |
| 15 | LOG_DOWNLOAD 1 | 14 | LOG_DOWNLOAD 1 |
| 16 | ) | 15 | ) |
| 17 | 16 | ||
| 18 | if (APPLE) | 17 | set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") |
| 19 | find_library(SIXENSE_LIBRARY_RELEASE lib/osx_x64/release_dll/libsixense_x64.dylib HINTS ${SIXENSE_SEARCH_DIRS}) | ||
| 20 | find_library(SIXENSE_LIBRARY_DEBUG lib/osx_x64/debug_dll/libsixensed_x64.dylib HINTS ${SIXENSE_SEARCH_DIRS}) | ||
| 21 | elseif (UNIX) | ||
| 22 | find_library(SIXENSE_LIBRARY_RELEASE lib/linux_x64/release/libsixense_x64.so HINTS ${SIXENSE_SEARCH_DIRS}) | ||
| 23 | # find_library(SIXENSE_LIBRARY_DEBUG lib/linux_x64/debug/libsixensed_x64.so HINTS ${SIXENSE_SEARCH_DIRS}) | ||
| 24 | elseif (WIN32) | ||
| 25 | endif () | ||
| 26 | |||
| 27 | |||
| 28 | 18 | ||
| 29 | ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) | 19 | ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) |
| 30 | 20 | ||
| @@ -39,22 +29,18 @@ if (WIN32) · 8 unmodified lines | |||
| 39 | set(ARCH_DIR "Win32") | 29 | set(ARCH_DIR "Win32") |
| 40 | set(ARCH_SUFFIX "") | 30 | set(ARCH_SUFFIX "") |
| 41 | endif() | 31 | endif() |
| 42 | 32 | ||
| 43 | # FIXME need to account for different architectures | 33 | set(${EXTERNAL_NAME_UPPER}_LIBRARIES "${SOURCE_DIR}/lib/${ARCH_DIR}/VS2013/release_dll/sixense${ARCH_SUFFIX}.lib" CACHE TYPE INTERNAL) |
| 44 | set(${EXTERNAL_NAME_UPPER}_LIBRARIES "${SOURCE_DIR}/lib/${ARCH_DIR}/release_dll/sixense${ARCH_SUFFIX}.lib" CACHE TYPE INTERNAL) | 34 | add_paths_to_fixup_libs("${SOURCE_DIR}/bin/${ARCH_DIR}/VS2013/release_dll") |
| 45 | add_paths_to_fixup_libs(${SOURCE_DIR}/bin/win32) | ||
| 46 | 35 | ||
| 47 | elseif(APPLE) | 36 | elseif(APPLE) |
| 48 | 37 | ||
| 49 | # FIXME need to account for different architectures | 38 | set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/osx_x64/release_dll/libsixense_x64.dylib CACHE TYPE INTERNAL) |
| 50 | set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/osx_x64/release_dll/libsixense_x64.dylib CACHE TYPE INTERNAL) | ||
| 51 | add_paths_to_fixup_libs(${SOURCE_DIR}/bin/osx32) | ||
| 52 | 39 | ||
| 53 | elseif(NOT ANDROID) | 40 | elseif(NOT ANDROID) |
| 54 | 41 | ||
| 55 | # FIXME need to account for different architectures | 42 | # FIXME need to account for different architectures |
| 56 | set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/linux_x64/release/libsixense_x64.so CACHE TYPE INTERNAL) | 43 | set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/linux_x64/release/libsixense_x64.so CACHE TYPE INTERNAL) |
| 57 | add_paths_to_fixup_libs(${SOURCE_DIR}/bin/linux32) | 44 | |
| 58 | |||
| 59 | endif() | 45 | endif() |
| 60 | 46 | ||
| @@ -16,7 +16,7 @@ macro(ADD_DEPENDENCY_EXTERNAL_PROJECTS) · 15 unmodified lines | |||
| 16 | string(TOUPPER ${_PROJ_NAME} _PROJ_NAME_UPPER) | 16 | string(TOUPPER ${_PROJ_NAME} _PROJ_NAME_UPPER) |
| 17 | 17 | ||
| 18 | # has the user told us they specific don't want this as an external project? | 18 | # has the user told us they specific don't want this as an external project? |
| 19 | if (GET_${_PROJ_NAME_UPPER}) | 19 | if (NOT USE_LOCAL_${_PROJ_NAME_UPPER}) |
| 20 | # have we already detected we can't have this as external project on this OS? | 20 | # have we already detected we can't have this as external project on this OS? |
| 21 | if (NOT DEFINED ${_PROJ_NAME_UPPER}_EXTERNAL_PROJECT OR ${_PROJ_NAME_UPPER}_EXTERNAL_PROJECT) | 21 | if (NOT DEFINED ${_PROJ_NAME_UPPER}_EXTERNAL_PROJECT OR ${_PROJ_NAME_UPPER}_EXTERNAL_PROJECT) |
| 22 | # have we already setup the target? | 22 | # have we already setup the target? |
| @@ -0,0 +1,14 @@ | |||
| 1 | # | ||
| 2 | # Copyright 2015 High Fidelity, Inc. | ||
| 3 | # Created by Bradley Austin Davis on 2015/10/10 | ||
| 4 | # | ||
| 5 | # Distributed under the Apache License, Version 2.0. | ||
| 6 | # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | # | ||
| 8 | macro(TARGET_FACESHIFT) | ||
| 9 | add_dependency_external_projects(faceshift) | ||
| 10 | find_package(Faceshift REQUIRED) | ||
| 11 | target_include_directories(${TARGET_NAME} PRIVATE ${FACESHIFT_INCLUDE_DIRS}) | ||
| 12 | target_link_libraries(${TARGET_NAME} ${FACESHIFT_LIBRARIES}) | ||
| 13 | add_definitions(-DHAVE_FACESHIFT) | ||
| 14 | endmacro() | ||
| @@ -0,0 +1,14 @@ | |||
| 1 | # | ||
| 2 | # Copyright 2015 High Fidelity, Inc. | ||
| 3 | # Created by Bradley Austin Davis on 2015/10/10 | ||
| 4 | # | ||
| 5 | # Distributed under the Apache License, Version 2.0. | ||
| 6 | # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | # | ||
| 8 | macro(TARGET_SDL2) | ||
| 9 | add_dependency_external_projects(sdl2) | ||
| 10 | find_package(SDL2 REQUIRED) | ||
| 11 | target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${SDL2_INCLUDE_DIR}) | ||
| 12 | target_link_libraries(${TARGET_NAME} ${SDL2_LIBRARY}) | ||
| 13 | add_definitions(-DHAVE_SDL2) | ||
| 14 | endmacro() | ||
| @@ -0,0 +1,14 @@ | |||
| 1 | # | ||
| 2 | # Copyright 2015 High Fidelity, Inc. | ||
| 3 | # Created by Bradley Austin Davis on 2015/10/10 | ||
| 4 | # | ||
| 5 | # Distributed under the Apache License, Version 2.0. | ||
| 6 | # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | # | ||
| 8 | macro(TARGET_SIXENSE) | ||
| 9 | add_dependency_external_projects(sixense) | ||
| 10 | find_package(Sixense REQUIRED) | ||
| 11 | target_include_directories(${TARGET_NAME} PRIVATE ${SIXENSE_INCLUDE_DIRS}) | ||
| 12 | target_link_libraries(${TARGET_NAME} ${SIXENSE_LIBRARIES}) | ||
| 13 | add_definitions(-DHAVE_SIXENSE) | ||
| 14 | endmacro() | ||
| @@ -18,32 +18,9 @@ · 17 unmodified lines | |||
| 18 | # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | 18 | # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html |
| 19 | # | 19 | # |
| 20 | 20 | ||
| 21 | include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") | ||
| 22 | hifi_library_search_hints("faceshift") | ||
| 23 | |||
| 24 | find_path(FACESHIFT_INCLUDE_DIRS fsbinarystream.h PATH_SUFFIXES include HINTS ${FACESHIFT_SEARCH_DIRS}) | ||
| 25 | |||
| 26 | if (APPLE) | ||
| 27 | set(ARCH_DIR "MacOS") | ||
| 28 | elseif (UNIX) | ||
| 29 | set(ARCH_DIR "UNIX") | ||
| 30 | elseif (WIN32) | ||
| 31 | if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") | ||
| 32 | set(ARCH_DIR "x64") | ||
| 33 | else() | ||
| 34 | set(ARCH_DIR "Win32") | ||
| 35 | endif() | ||
| 36 | endif () | ||
| 37 | |||
| 38 | find_library(FACESHIFT_LIBRARY_RELEASE NAME faceshift PATH_SUFFIXES lib/${ARCH_DIR} HINTS ${FACESHIFT_SEARCH_DIRS}) | ||
| 39 | find_library(FACESHIFT_LIBRARY_DEBUG NAME faceshiftd PATH_SUFFIXES lib/${ARCH_DIR} HINTS ${FACESHIFT_SEARCH_DIRS}) | ||
| 40 | |||
| 41 | include(SelectLibraryConfigurations) | 21 | include(SelectLibraryConfigurations) |
| 42 | select_library_configurations(FACESHIFT) | 22 | select_library_configurations(FACESHIFT) |
| 43 | 23 | ||
| 44 | set(FACESHIFT_LIBRARIES ${FACESHIFT_LIBRARY}) | ||
| 45 | |||
| 46 | include(FindPackageHandleStandardArgs) | 24 | include(FindPackageHandleStandardArgs) |
| 47 | find_package_handle_standard_args(Faceshift DEFAULT_MSG FACESHIFT_INCLUDE_DIRS FACESHIFT_LIBRARIES) | 25 | find_package_handle_standard_args(Faceshift DEFAULT_MSG FACESHIFT_INCLUDE_DIRS FACESHIFT_LIBRARIES) |
| 48 | |||
| 49 | mark_as_advanced(FACESHIFT_INCLUDE_DIRS FACESHIFT_LIBRARIES FACESHIFT_SEARCH_DIRS) | 26 | mark_as_advanced(FACESHIFT_INCLUDE_DIRS FACESHIFT_LIBRARIES FACESHIFT_SEARCH_DIRS) |
| @@ -18,49 +18,6 @@ · 17 unmodified lines | |||
| 18 | # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | 18 | # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html |
| 19 | # | 19 | # |
| 20 | 20 | ||
| 21 | include("${MACRO_DIR}/HifiLibrarySearchHints.cmake") | ||
| 22 | hifi_library_search_hints("sixense") | ||
| 23 | |||
| 24 | find_path(SIXENSE_INCLUDE_DIRS sixense.h PATH_SUFFIXES include HINTS ${SIXENSE_SEARCH_DIRS}) | ||
| 25 | |||
| 26 | if (APPLE) | ||
| 27 | find_library(SIXENSE_LIBRARY_RELEASE lib/osx_x64/release_dll/libsixense_x64.dylib HINTS ${SIXENSE_SEARCH_DIRS}) | ||
| 28 | find_library(SIXENSE_LIBRARY_DEBUG lib/osx_x64/debug_dll/libsixensed_x64.dylib HINTS ${SIXENSE_SEARCH_DIRS}) | ||
| 29 | elseif (UNIX) | ||
| 30 | find_library(SIXENSE_LIBRARY_RELEASE lib/linux_x64/release/libsixense_x64.so HINTS ${SIXENSE_SEARCH_DIRS}) | ||
| 31 | # find_library(SIXENSE_LIBRARY_DEBUG lib/linux_x64/debug/libsixensed_x64.so HINTS ${SIXENSE_SEARCH_DIRS}) | ||
| 32 | elseif (WIN32) | ||
| 33 | if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") | ||
| 34 | set(ARCH_DIR "x64") | ||
| 35 | set(ARCH_SUFFIX "_x64") | ||
| 36 | else() | ||
| 37 | set(ARCH_DIR "Win32") | ||
| 38 | set(ARCH_SUFFIX "") | ||
| 39 | endif() | ||
| 40 | |||
| 41 | find_library(SIXENSE_LIBRARY_RELEASE "lib/${ARCH_DIR}/release_dll/sixense${ARCH_SUFFIX}.lib" HINTS ${SIXENSE_SEARCH_DIRS}) | ||
| 42 | find_library(SIXENSE_LIBRARY_DEBUG "lib/${ARCH_DIR}/debug_dll/sixensed.lib" HINTS ${SIXENSE_SEARCH_DIRS}) | ||
| 43 | |||
| 44 | find_path(SIXENSE_DEBUG_DLL_PATH "sixensed${ARCH_SUFFIX}.dll" PATH_SUFFIXES bin/${ARCH_DIR}/debug_dll HINTS ${SIXENSE_SEARCH_DIRS}) | ||
| 45 | find_path(SIXENSE_RELEASE_DLL_PATH "sixense${ARCH_SUFFIX}.dll" PATH_SUFFIXES bin/${ARCH_DIR}/release_dll HINTS ${SIXENSE_SEARCH_DIRS}) | ||
| 46 | find_path(SIXENSE_DEVICE_DLL_PATH DeviceDLL.dll PATH_SUFFIXES samples/${ARCH_DIR}/sixense_simple3d HINTS ${SIXENSE_SEARCH_DIRS}) | ||
| 47 | endif () | ||
| 48 | |||
| 49 | include(SelectLibraryConfigurations) | ||
| 50 | select_library_configurations(SIXENSE) | ||
| 51 | |||
| 52 | set(SIXENSE_REQUIREMENTS SIXENSE_INCLUDE_DIRS SIXENSE_LIBRARIES) | ||
| 53 | if (WIN32) | ||
| 54 | list(APPEND SIXENSE_REQUIREMENTS SIXENSE_DEBUG_DLL_PATH SIXENSE_RELEASE_DLL_PATH SIXENSE_DEVICE_DLL_PATH) | ||
| 55 | endif () | ||
| 56 | |||
| 57 | set(SIXENSE_LIBRARIES "${SIXENSE_LIBRARY}") | ||
| 58 | |||
| 59 | include(FindPackageHandleStandardArgs) | 21 | include(FindPackageHandleStandardArgs) |
| 60 | find_package_handle_standard_args(Sixense DEFAULT_MSG ${SIXENSE_REQUIREMENTS}) | 22 | find_package_handle_standard_args(Sixense DEFAULT_MSG SIXENSE_INCLUDE_DIRS SIXENSE_LIBRARIES) |
| 61 | |||
| 62 | if (WIN32) | ||
| 63 | add_paths_to_fixup_libs(${SIXENSE_DEBUG_DLL_PATH} ${SIXENSE_RELEASE_DLL_PATH} ${SIXENSE_DEVICE_DLL_PATH}) | ||
| 64 | endif () | ||
| 65 | |||
| 66 | mark_as_advanced(SIXENSE_LIBRARIES SIXENSE_INCLUDE_DIRS SIXENSE_SEARCH_DIRS) | 23 | mark_as_advanced(SIXENSE_LIBRARIES SIXENSE_INCLUDE_DIRS SIXENSE_SEARCH_DIRS) |
| @@ -21,45 +21,17 @@ if (WIN32) · 20 unmodified lines | |||
| 21 | hifi_library_search_hints("iViewHMD") | 21 | hifi_library_search_hints("iViewHMD") |
| 22 | 22 | ||
| 23 | find_path(IVIEWHMD_INCLUDE_DIRS iViewHMDAPI.h PATH_SUFFIXES include HINTS ${IVIEWHMD_SEARCH_DIRS}) | 23 | find_path(IVIEWHMD_INCLUDE_DIRS iViewHMDAPI.h PATH_SUFFIXES include HINTS ${IVIEWHMD_SEARCH_DIRS}) |
| 24 | find_library(IVIEWHMD_LIBRARIES NAMES iViewHMDAPI PATH_SUFFIXES libs HINTS ${IVIEWHMD_SEARCH_DIRS}) | 24 | find_library(IVIEWHMD_LIBRARIES NAMES iViewHMDAPI PATH_SUFFIXES libs/x86 HINTS ${IVIEWHMD_SEARCH_DIRS}) |
| 25 | find_path(IVIEWHMD_API_DLL_PATH iViewHMDAPI.dll PATH_SUFFIXES libs HINTS ${IVIEWHMD_SEARCH_DIRS}) | 25 | find_path(IVIEWHMD_API_DLL_PATH iViewHMDAPI.dll PATH_SUFFIXES libs/x86 HINTS ${IVIEWHMD_SEARCH_DIRS}) |
| 26 | list(APPEND IVIEWHMD_REQUIREMENTS IVIEWHMD_INCLUDE_DIRS IVIEWHMD_LIBRARIES IVIEWHMD_API_DLL_PATH) | 26 | list(APPEND IVIEWHMD_REQUIREMENTS IVIEWHMD_INCLUDE_DIRS IVIEWHMD_LIBRARIES IVIEWHMD_API_DLL_PATH) |
| 27 | 27 | ||
| 28 | set(IVIEWHMD_DLLS | 28 | find_path(IVIEWHMD_DLL_PATH_3RD_PARTY libiViewNG.dll PATH_SUFFIXES 3rdParty HINTS ${IVIEWHMD_SEARCH_DIRS}) |
| 29 | avcodec-53.dll | 29 | list(APPEND IVIEWHMD_REQUIREMENTS IVIEWHMD_DLL_PATH_3RD_PARTY) |
| 30 | avformat-53.dll | ||
| 31 | avutil-51.dll | ||
| 32 | libboost_filesystem-mgw45-mt-1_49.dll | ||
| 33 | libboost_system-mgw45-mt-1_49.dll | ||
| 34 | libboost_thread-mgw45-mt-1_49.dll | ||
| 35 | libgcc_s_dw2-1.dll | ||
| 36 | libiViewNG-LibCore.dll | ||
| 37 | libopencv_calib3d244.dll | ||
| 38 | libopencv_core244.dll | ||
| 39 | libopencv_features2d244.dll | ||
| 40 | libopencv_flann244.dll | ||
| 41 | libopencv_highgui244.dll | ||
| 42 | libopencv_imgproc244.dll | ||
| 43 | libopencv_legacy244.dll | ||
| 44 | libopencv_ml244.dll | ||
| 45 | libopencv_video244.dll | ||
| 46 | libstdc++-6.dll | ||
| 47 | opencv_core220.dll | ||
| 48 | opencv_highgui220.dll | ||
| 49 | opencv_imgproc220.dll | ||
| 50 | swscale-2.dll | ||
| 51 | ) | ||
| 52 | |||
| 53 | foreach(IVIEWHMD_DLL ${IVIEWHMD_DLLS}) | ||
| 54 | find_path(IVIEWHMD_DLL_PATH ${IVIEWHMD_DLL} PATH_SUFFIXES 3rdParty HINTS ${IVIEWHMD_SEARCH_DIRS}) | ||
| 55 | list(APPEND IVIEWHMD_REQUIREMENTS IVIEWHMD_DLL_PATH) | ||
| 56 | list(APPEND IVIEWHMD_DLL_PATHS ${IVIEWHMD_DLL_PATH}) | ||
| 57 | endforeach() | ||
| 58 | 30 | ||
| 59 | include(FindPackageHandleStandardArgs) | 31 | include(FindPackageHandleStandardArgs) |
| 60 | find_package_handle_standard_args(IVIEWHMD DEFAULT_MSG ${IVIEWHMD_REQUIREMENTS}) | 32 | find_package_handle_standard_args(IVIEWHMD DEFAULT_MSG ${IVIEWHMD_REQUIREMENTS}) |
| 61 | 33 | ||
| 62 | add_paths_to_fixup_libs(${IVIEWHMD_API_DLL_PATH} ${IVIEWHMD_DLL_PATHS}) | 34 | add_paths_to_fixup_libs(${IVIEWHMD_API_DLL_PATH} ${IVIEWHMD_DLL_PATH_3RD_PARTY}) |
| 63 | 35 | ||
| 64 | mark_as_advanced(IVIEWHMD_INCLUDE_DIRS IVIEWHMD_LIBRARIES IVIEWHMD_SEARCH_DIRS) | 36 | mark_as_advanced(IVIEWHMD_INCLUDE_DIRS IVIEWHMD_LIBRARIES IVIEWHMD_SEARCH_DIRS) |
| 65 | 37 | ||
| @@ -71,8 +71,8 @@ function maybePlaySound(deltaTime) { · 70 unmodified lines | |||
| 71 | var palm2Position = MyAvatar.getRightPalmPosition(); | 71 | var palm2Position = MyAvatar.getRightPalmPosition(); |
| 72 | var distanceBetween = Vec3.length(Vec3.subtract(palm1Position, palm2Position)); | 72 | var distanceBetween = Vec3.length(Vec3.subtract(palm1Position, palm2Position)); |
| 73 | 73 | ||
| 74 | var palm1Velocity = Controller.getSpatialControlVelocity(1); | 74 | var palm1Velocity = Controller.getPoseValue(Controller.Standard.LeftHand).velocity; |
| 75 | var palm2Velocity = Controller.getSpatialControlVelocity(3); | 75 | var palm2Velocity = Controller.getPoseValue(Controller.Standard.RightHand).velocity; |
| 76 | var closingVelocity = Vec3.length(Vec3.subtract(palm1Velocity, palm2Velocity)); | 76 | var closingVelocity = Vec3.length(Vec3.subtract(palm1Velocity, palm2Velocity)); |
| 77 | 77 | ||
| 78 | const CLAP_SPEED = 0.7; | 78 | const CLAP_SPEED = 0.7; |
| @@ -0,0 +1,101 @@ | |||
| 1 | |||
| 2 | // | ||
| 3 | // controllerScriptingExamples.js | ||
| 4 | // examples | ||
| 5 | // | ||
| 6 | // Created by Sam Gondelman on 6/2/15 | ||
| 7 | // Copyright 2015 High Fidelity, Inc. | ||
| 8 | // | ||
| 9 | // Distributed under the Apache License, Version 2.0. | ||
| 10 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 11 | // | ||
| 12 | |||
| 13 | // Assumes you only have the default keyboard connected | ||
| 14 | |||
| 15 | /*myFirstMapping = function() { | ||
| 16 | return { | ||
| 17 | "name": "example", | ||
| 18 | "channels": [ | ||
| 19 | { "from": "Keyboard.W", "to": "Actions.LONGITUDINAL_FORWARD" }, | ||
| 20 | { "from": "Keyboard.S", "to": "Actions.LONGITUDINAL_BACKWARD" }, | ||
| 21 | |||
| 22 | { "from": "Keyboard.Left", "to": "Actions.LATERAL_LEFT" }, | ||
| 23 | { "from": "Keyboard.Right", "to": "Actions.LATERAL_RIGHT" }, | ||
| 24 | |||
| 25 | { "from": "Keyboard.A", "to": "Actions.YAW_LEFT" }, | ||
| 26 | { "from": "Keyboard.D", "to": "Actions.YAW_RIGHT" }, | ||
| 27 | |||
| 28 | { "from": "Keyboard.C", "to": "Actions.VERTICAL_DOWN" }, | ||
| 29 | { "from": "Keyboard.E", "to": "Actions.VERTICAL_UP" }, | ||
| 30 | { | ||
| 31 | "from": "Standard.LX", | ||
| 32 | "filters": [ { | ||
| 33 | "type": "scale", | ||
| 34 | "params": [2.0], | ||
| 35 | } | ||
| 36 | ], | ||
| 37 | "to": "Actions.LATERAL_LEFT", | ||
| 38 | }, { | ||
| 39 | "from": "Keyboard.B", | ||
| 40 | "to": "Actions.Yaw" | ||
| 41 | } | ||
| 42 | ] | ||
| 43 | } | ||
| 44 | } | ||
| 45 | */ | ||
| 46 | mySecondMapping = function() { | ||
| 47 | return { | ||
| 48 | "name": "example2", | ||
| 49 | "channels": [ | ||
| 50 | { "from": "Standard.LY", "to": "Actions.TranslateZ" }, | ||
| 51 | { "from": "Standard.LX", "to": "Actions.Yaw" }, | ||
| 52 | ] | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | //Script.include('mapping-test0.json'); | ||
| 57 | /*var myFirstMappingJSON = myFirstMapping(); | ||
| 58 | print('myFirstMappingJSON' + JSON.stringify(myFirstMappingJSON)); | ||
| 59 | |||
| 60 | var mapping = Controller.parseMapping(JSON.stringify(myFirstMappingJSON)); | ||
| 61 | |||
| 62 | |||
| 63 | Controller.enableMapping("example3"); | ||
| 64 | |||
| 65 | var mySecondMappingJSON = mySecondMapping(); | ||
| 66 | print('mySecondMappingJSON' + JSON.stringify(mySecondMappingJSON)); | ||
| 67 | |||
| 68 | var mapping2 = Controller.parseMapping(JSON.stringify(mySecondMappingJSON)); | ||
| 69 | mapping2.enable(); | ||
| 70 | |||
| 71 | Controller.enableMapping("example2"); | ||
| 72 | */ | ||
| 73 | var mapping3 = Controller.loadMapping(Script.resolvePath("example3.json")); | ||
| 74 | Controller.enableMapping("example3"); | ||
| 75 | |||
| 76 | /* | ||
| 77 | Object.keys(Controller.Standard).forEach(function (input) { | ||
| 78 | print("Controller.Standard." + input + ":" + Controller.Standard[input]); | ||
| 79 | }); | ||
| 80 | |||
| 81 | Object.keys(Controller.Hardware).forEach(function (deviceName) { | ||
| 82 | Object.keys(Controller.Hardware[deviceName]).forEach(function (input) { | ||
| 83 | print("Controller.Hardware." + deviceName + "." + input + ":" + Controller.Hardware[deviceName][input]); | ||
| 84 | }); | ||
| 85 | }); | ||
| 86 | |||
| 87 | Object.keys(Controller.Actions).forEach(function (actionName) { | ||
| 88 | print("Controller.Actions." + actionName + ":" + Controller.Actions[actionName]); | ||
| 89 | }); | ||
| 90 | */ | ||
| 91 | |||
| 92 | |||
| 93 | Controller.hardwareChanged.connect(function () { | ||
| 94 | print("hardwareChanged ---------------------------------------------------"); | ||
| 95 | Object.keys(Controller.Hardware).forEach(function (deviceName) { | ||
| 96 | Object.keys(Controller.Hardware[deviceName]).forEach(function (input) { | ||
| 97 | print("Controller.Hardware." + deviceName + "." + input + ":" + Controller.Hardware[deviceName][input]); | ||
| 98 | }); | ||
| 99 | }); | ||
| 100 | print("-------------------------------------------------------------------"); | ||
| 101 | }); | ||
| @@ -148,6 +148,7 @@ function MyController(hand, triggerAction) { · 147 unmodified lines | |||
| 148 | this.state = STATE_OFF; | 148 | this.state = STATE_OFF; |
| 149 | this.pointer = null; // entity-id of line object | 149 | this.pointer = null; // entity-id of line object |
| 150 | this.triggerValue = 0; // rolling average of trigger value | 150 | this.triggerValue = 0; // rolling average of trigger value |
| 151 | this.rawTriggerValue = 0; | ||
| 151 | 152 | ||
| 152 | var _this = this; | 153 | var _this = this; |
| 153 | 154 | ||
| @@ -244,12 +245,17 @@ function MyController(hand, triggerAction) { · 90 unmodified lines | |||
| 244 | this.pointer = null; | 245 | this.pointer = null; |
| 245 | }; | 246 | }; |
| 246 | 247 | ||
| 247 | this.updateSmoothedTrigger = function() { | 248 | this.eitherTrigger = function (value) { |
| 248 | var triggerValue = Controller.getActionValue(this.triggerAction); | 249 | _this.rawTriggerValue = value; |
| 250 | }; | ||
| 251 | |||
| 252 | this.updateSmoothedTrigger = function () { | ||
| 253 | var triggerValue = this.rawTriggerValue; | ||
| 249 | // smooth out trigger value | 254 | // smooth out trigger value |
| 250 | this.triggerValue = (this.triggerValue * TRIGGER_SMOOTH_RATIO) + | 255 | this.triggerValue = (this.triggerValue * TRIGGER_SMOOTH_RATIO) + |
| 251 | (triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO)); | 256 | (triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO)); |
| 252 | } | 257 | |
| 258 | }; | ||
| 253 | 259 | ||
| 254 | this.triggerSmoothedSqueezed = function() { | 260 | this.triggerSmoothedSqueezed = function() { |
| 255 | return this.triggerValue > TRIGGER_ON_VALUE; | 261 | return this.triggerValue > TRIGGER_ON_VALUE; |
| @@ -259,8 +265,8 @@ function MyController(hand, triggerAction) { · 3 unmodified lines | |||
| 259 | return this.triggerValue < TRIGGER_OFF_VALUE; | 265 | return this.triggerValue < TRIGGER_OFF_VALUE; |
| 260 | }; | 266 | }; |
| 261 | 267 | ||
| 262 | this.triggerSqueezed = function() { | 268 | this.triggerSqueezed = function() { |
| 263 | var triggerValue = Controller.getActionValue(this.triggerAction); | 269 | var triggerValue = this.rawTriggerValue; |
| 264 | return triggerValue > TRIGGER_ON_VALUE; | 270 | return triggerValue > TRIGGER_ON_VALUE; |
| 265 | }; | 271 | }; |
| 266 | 272 | ||
| @@ -402,8 +408,9 @@ function MyController(hand, triggerAction) { · 135 unmodified lines | |||
| 402 | 408 | ||
| 403 | this.distanceHolding = function() { | 409 | this.distanceHolding = function() { |
| 404 | 410 | ||
| 405 | var handControllerPosition = Controller.getSpatialControlPosition(this.palm); | 411 | var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; |
| 406 | var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm)); | 412 | var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; |
| 413 | var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation); | ||
| 407 | var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation", | 414 | var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation", |
| 408 | "gravity", "ignoreForCollisions"]); | 415 | "gravity", "ignoreForCollisions"]); |
| 409 | var now = Date.now(); | 416 | var now = Date.now(); |
| @@ -452,8 +459,9 @@ function MyController(hand, triggerAction) { · 42 unmodified lines | |||
| 452 | } | 459 | } |
| 453 | 460 | ||
| 454 | var handPosition = this.getHandPosition(); | 461 | var handPosition = this.getHandPosition(); |
| 455 | var handControllerPosition = Controller.getSpatialControlPosition(this.palm); | 462 | var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; |
| 456 | var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm)); | 463 | var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; |
| 464 | var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation); | ||
| 457 | var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation"]); | 465 | var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation"]); |
| 458 | 466 | ||
| 459 | this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); | 467 | this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR); |
| @@ -595,7 +603,7 @@ function MyController(hand, triggerAction) { · 135 unmodified lines | |||
| 595 | 603 | ||
| 596 | } | 604 | } |
| 597 | 605 | ||
| 598 | this.currentHandControllerTipPosition = Controller.getSpatialControlPosition(this.tip); | 606 | this.currentHandControllerTipPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition;; |
| 599 | 607 | ||
| 600 | this.currentObjectTime = Date.now(); | 608 | this.currentObjectTime = Date.now(); |
| 601 | }; | 609 | }; |
| @@ -613,7 +621,7 @@ function MyController(hand, triggerAction) { · 11 unmodified lines | |||
| 613 | // of it's actual offset, let's try imparting a velocity which is at a fixed radius | 621 | // of it's actual offset, let's try imparting a velocity which is at a fixed radius |
| 614 | // from the palm. | 622 | // from the palm. |
| 615 | 623 | ||
| 616 | var handControllerPosition = Controller.getSpatialControlPosition(this.tip); | 624 | var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; |
| 617 | var now = Date.now(); | 625 | var now = Date.now(); |
| 618 | 626 | ||
| 619 | var deltaPosition = Vec3.subtract(handControllerPosition, this.currentHandControllerTipPosition); // meters | 627 | var deltaPosition = Vec3.subtract(handControllerPosition, this.currentHandControllerTipPosition); // meters |
| @@ -856,8 +864,16 @@ function MyController(hand, triggerAction) { · 236 unmodified lines | |||
| 856 | }; | 864 | }; |
| 857 | } | 865 | } |
| 858 | 866 | ||
| 859 | var rightController = new MyController(RIGHT_HAND, Controller.findAction("RIGHT_HAND_CLICK")); | 867 | var rightController = new MyController(RIGHT_HAND, Controller.Standard.RT); |
| 860 | var leftController = new MyController(LEFT_HAND, Controller.findAction("LEFT_HAND_CLICK")); | 868 | var leftController = new MyController(LEFT_HAND, Controller.Standard.LT); |
| 869 | |||
| 870 | var MAPPING_NAME = "com.highfidelity.handControllerGrab"; | ||
| 871 | |||
| 872 | var mapping = Controller.newMapping(MAPPING_NAME); | ||
| 873 | mapping.from([Controller.Standard.RB, Controller.Standard.RT]).to(rightController.eitherTrigger); | ||
| 874 | mapping.from([Controller.Standard.LB, Controller.Standard.LT]).to(leftController.eitherTrigger); | ||
| 875 | Controller.enableMapping(MAPPING_NAME); | ||
| 876 | |||
| 861 | 877 | ||
| 862 | function update() { | 878 | function update() { |
| 863 | rightController.update(); | 879 | rightController.update(); |
| @@ -867,6 +883,7 @@ function update() { · 3 unmodified lines | |||
| 867 | function cleanup() { | 883 | function cleanup() { |
| 868 | rightController.cleanup(); | 884 | rightController.cleanup(); |
| 869 | leftController.cleanup(); | 885 | leftController.cleanup(); |
| 886 | Controller.disableMapping(MAPPING_NAME); | ||
| 870 | } | 887 | } |
| 871 | 888 | ||
| 872 | Script.scriptEnding.connect(cleanup); | 889 | Script.scriptEnding.connect(cleanup); |
| @@ -0,0 +1,102 @@ | |||
| 1 | // | ||
| 2 | // handPosesDebug.js | ||
| 3 | // examples | ||
| 4 | // | ||
| 5 | // Copyright 2015 High Fidelity, Inc. | ||
| 6 | // | ||
| 7 | // Distributed under the Apache License, Version 2.0. | ||
| 8 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 9 | // | ||
| 10 | |||
| 11 | |||
| 12 | |||
| 13 | function makeSphere(color) { | ||
| 14 | var SPHERE_SIZE = 0.05; | ||
| 15 | var sphere = Overlays.addOverlay("sphere", { | ||
| 16 | position: { x: 0, y: 0, z: 0 }, | ||
| 17 | size: SPHERE_SIZE, | ||
| 18 | color: color, | ||
| 19 | alpha: 1.0, | ||
| 20 | solid: true, | ||
| 21 | visible: true, | ||
| 22 | }); | ||
| 23 | |||
| 24 | return sphere; | ||
| 25 | } | ||
| 26 | |||
| 27 | |||
| 28 | var NUM_HANDS = 2; | ||
| 29 | var NUM_SPHERES_PER_HAND = 2; | ||
| 30 | var LEFT_HAND = 0; | ||
| 31 | var RIGHT_HAND = 1; | ||
| 32 | |||
| 33 | var COLORS = [ { red: 255, green: 0, blue: 0 }, { red: 0, green: 0, blue: 255 } ]; | ||
| 34 | |||
| 35 | |||
| 36 | function index(handNum, indexNum) { | ||
| 37 | return handNum * NUM_HANDS + indexNum; | ||
| 38 | } | ||
| 39 | |||
| 40 | var app = {}; | ||
| 41 | |||
| 42 | |||
| 43 | function setup() { | ||
| 44 | app.spheres = new Array(); | ||
| 45 | |||
| 46 | for (var h = 0; h < NUM_HANDS; h++) { | ||
| 47 | for (var s = 0; s < NUM_SPHERES_PER_HAND; s++) { | ||
| 48 | var i = index(h, s); | ||
| 49 | app.spheres[i] = makeSphere(COLORS[h]); | ||
| 50 | print("Added Sphere num " + i + " = " + JSON.stringify(app.spheres[i])); | ||
| 51 | } | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | function updateHand(handNum, deltaTime) { | ||
| 56 | var pose; | ||
| 57 | var handName = "right"; | ||
| 58 | if (handNum == LEFT_HAND) { | ||
| 59 | pose = MyAvatar.getLeftHandPose(); | ||
| 60 | handName = "left"; | ||
| 61 | } else { | ||
| 62 | pose = MyAvatar.getRightHandPose(); | ||
| 63 | handName = "right"; | ||
| 64 | } | ||
| 65 | |||
| 66 | if (pose.valid) { | ||
| 67 | //print(handName + " hand moving" + JSON.stringify(pose)); | ||
| 68 | Overlays.editOverlay(app.spheres[index(handNum, 0)], { | ||
| 69 | position: pose.translation, | ||
| 70 | visible: true, | ||
| 71 | }); | ||
| 72 | var vpos = Vec3.sum(Vec3.multiply(10 * deltaTime, pose.velocity), pose.translation); | ||
| 73 | Overlays.editOverlay(app.spheres[index(handNum, 1)], { | ||
| 74 | position: vpos, | ||
| 75 | visible: true, | ||
| 76 | }); | ||
| 77 | } else { | ||
| 78 | Overlays.editOverlay(app.spheres[index(handNum, 0)], { | ||
| 79 | visible: false | ||
| 80 | }); | ||
| 81 | |||
| 82 | Overlays.editOverlay(app.spheres[index(handNum, 1)], { | ||
| 83 | visible: false | ||
| 84 | }); | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | function update(deltaTime) { | ||
| 89 | updateHand(LEFT_HAND, deltaTime); | ||
| 90 | updateHand(RIGHT_HAND, deltaTime); | ||
| 91 | } | ||
| 92 | |||
| 93 | function scriptEnding() { | ||
| 94 | print("Removing spheres = " + JSON.stringify(app.spheres)); | ||
| 95 | for (var i = 0; i < app.spheres.length; i++) { | ||
| 96 | Overlays.deleteOverlay(app.spheres[i]); | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | setup(); | ||
| 101 | Script.update.connect(update); | ||
| 102 | Script.scriptEnding.connect(scriptEnding); | ||
| @@ -32,6 +32,8 @@ var guitarModel = HIFI_PUBLIC_BUCKET + "models/attachments/guitar.fst"; · 31 unmodified lines | |||
| 32 | 32 | ||
| 33 | // Load sounds that will be played | 33 | // Load sounds that will be played |
| 34 | 34 | ||
| 35 | var heyManWave = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/sounds/KenDoll_1%2303.wav"); | ||
| 36 | |||
| 35 | var chords = new Array(); | 37 | var chords = new Array(); |
| 36 | // Nylon string guitar | 38 | // Nylon string guitar |
| 37 | chords[1] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Nylon+A.raw"); | 39 | chords[1] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Nylon+A.raw"); |
| @@ -56,97 +58,128 @@ var NUM_GUITARS = 3; · 18 unmodified lines | |||
| 56 | var guitarSelector = NUM_CHORDS; | 58 | var guitarSelector = NUM_CHORDS; |
| 57 | var whichChord = 1; | 59 | var whichChord = 1; |
| 58 | 60 | ||
| 59 | var leftHanded = true; | 61 | var leftHanded = false; |
| 62 | var strumHand, chordHand, strumTrigger, chordTrigger; | ||
| 60 | if (leftHanded) { | 63 | if (leftHanded) { |
| 61 | var strumHand = 0; | 64 | strumHand = Controller.Standard.LeftHand; |
| 62 | var chordHand = 1; | 65 | chordHand = Controller.Standard.RightHand; |
| 66 | strumTrigger = Controller.Standard.LT; | ||
| 67 | chordTrigger = Controller.Standard.RT; | ||
| 68 | changeGuitar = Controller.Standard.RB; | ||
| 69 | chord1 = Controller.Standard.X; | ||
| 70 | chord2 = Controller.Standard.Y; | ||
| 71 | chord3 = Controller.Standard.A; | ||
| 72 | chord4 = Controller.Standard.B; | ||
| 63 | } else { | 73 | } else { |
| 64 | var strumHand = 1; | 74 | strumHand = Controller.Standard.RightHand; |
| 65 | var chordHand = 0; | 75 | chordHand = Controller.Standard.LeftHand; |
| 76 | strumTrigger = Controller.Standard.RT; | ||
| 77 | chordTrigger = Controller.Standard.LT; | ||
| 78 | changeGuitar = Controller.Standard.LB; | ||
| 79 | chord1 = Controller.Standard.DU; // these may not be correct, maybe we should map directly to Hydra?? | ||
| 80 | chord2 = Controller.Standard.DD; | ||
| 81 | chord3 = Controller.Standard.DL; | ||
| 82 | chord4 = Controller.Standard.DR; | ||
| 66 | } | 83 | } |
| 67 | 84 | ||
| 68 | var lastPosition = { x: 0.0, | 85 | var lastPosition = { x: 0.0, |
| 69 | y: 0.0, | 86 | y: 0.0, |
| 70 | z: 0.0 }; | 87 | z: 0.0 }; |
| 71 | 88 | ||
| 72 | var soundPlaying = false; | 89 | var audioInjector = null; |
| 73 | var selectorPressed = false; | 90 | var selectorPressed = false; |
| 74 | var position; | 91 | var position; |
| 75 | 92 | ||
| 76 | MyAvatar.attach(guitarModel, "Hips", {x: -0.2, y: 0.0, z: 0.1}, Quat.fromPitchYawRollDegrees(90, 00, 90), 1.0); | 93 | MyAvatar.attach(guitarModel, "Hips", {x: leftHanded ? -0.2 : 0.2, y: 0.0, z: 0.1}, Quat.fromPitchYawRollDegrees(90, 00, leftHanded ? 75 : -75), 1.0); |
| 77 | 94 | ||
| 78 | function checkHands(deltaTime) { | 95 | function checkHands(deltaTime) { |
| 79 | for (var palm = 0; palm < 2; palm++) { | 96 | var strumVelocity = Controller.getPoseValue(strumHand).velocity; |
| 80 | var palmVelocity = Controller.getSpatialControlVelocity(palm * 2 + 1); | 97 | var volume = length(strumVelocity) / 5.0; |
| 81 | var volume = length(palmVelocity) / 5.0; | ||
| 82 | var position = Controller.getSpatialControlPosition(palm * 2 + 1); | ||
| 83 | var myPelvis = MyAvatar.position; | ||
| 84 | var trigger = Controller.getTriggerValue(strumHand); | ||
| 85 | var chord = Controller.getTriggerValue(chordHand); | ||
| 86 | |||
| 87 | if (volume > 1.0) volume = 1.0; | ||
| 88 | if ((chord > 0.1) && soundPlaying.isPlaying) { | ||
| 89 | // If chord finger trigger pulled, stop current chord | ||
| 90 | print("stopped sound"); | ||
| 91 | soundPlaying.stop(); | ||
| 92 | } | ||
| 93 | 98 | ||
| 94 | var BUTTON_COUNT = 6; | 99 | if (volume == 0.0) { |
| 95 | 100 | volume = 1.0; | |
| 96 | // Change guitars if button FWD (5) pressed | 101 | } |
| 97 | if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 5)) { | 102 | |
| 98 | if (!selectorPressed) { | 103 | var strumHandPosition = leftHanded ? MyAvatar.leftHandPosition : MyAvatar.rightHandPosition; |
| 99 | guitarSelector += NUM_CHORDS; | 104 | var myPelvis = MyAvatar.position; |
| 100 | if (guitarSelector >= NUM_CHORDS * NUM_GUITARS) { | 105 | var strumming = Controller.getValue(strumTrigger); |
| 101 | guitarSelector = 0; | 106 | var chord = Controller.getValue(chordTrigger); |
| 102 | } | 107 | |
| 103 | selectorPressed = true; | 108 | if (volume > 1.0) volume = 1.0; |
| 104 | } | 109 | if ((chord > 0.1) && audioInjector && audioInjector.isPlaying) { |
| 105 | } else { | 110 | // If chord finger trigger pulled, stop current chord |
| 106 | selectorPressed = false; | 111 | print("stopping chord because cord trigger pulled"); |
| 107 | } | 112 | audioInjector.stop(); |
| 113 | } | ||
| 108 | 114 | ||
| 109 | if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 1)) { | 115 | // Change guitars if button FWD (5) pressed |
| 110 | whichChord = 1; | 116 | if (Controller.getValue(changeGuitar)) { |
| 111 | } else if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 2)) { | 117 | if (!selectorPressed) { |
| 112 | whichChord = 2; | 118 | print("changeGuitar:" + changeGuitar); |
| 113 | } else if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 3)) { | 119 | guitarSelector += NUM_CHORDS; |
| 114 | whichChord = 3; | 120 | if (guitarSelector >= NUM_CHORDS * NUM_GUITARS) { |
| 115 | } else if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 4)) { | 121 | guitarSelector = 0; |
| 116 | whichChord = 4; | 122 | } |
| 123 | print("new guitarBase: " + guitarSelector); | ||
| 124 | stopAudio(true); | ||
| 125 | selectorPressed = true; | ||
| 117 | } | 126 | } |
| 127 | } else { | ||
| 128 | selectorPressed = false; | ||
| 129 | } | ||
| 130 | |||
| 131 | if (Controller.getValue(chord1)) { | ||
| 132 | whichChord = 1; | ||
| 133 | stopAudio(true); | ||
| 134 | } else if (Controller.getValue(chord2)) { | ||
| 135 | whichChord = 2; | ||
| 136 | stopAudio(true); | ||
| 137 | } else if (Controller.getValue(chord3)) { | ||
| 138 | whichChord = 3; | ||
| 139 | stopAudio(true); | ||
| 140 | } else if (Controller.getValue(chord4)) { | ||
| 141 | whichChord = 4; | ||
| 142 | stopAudio(true); | ||
| 143 | } | ||
| 144 | |||
| 145 | var STRUM_HEIGHT_ABOVE_PELVIS = 0.10; | ||
| 146 | var strummingHeight = myPelvis.y + STRUM_HEIGHT_ABOVE_PELVIS; | ||
| 118 | 147 | ||
| 119 | if (palm == strumHand) { | 148 | var strumNowAbove = strumHandPosition.y > strummingHeight; |
| 120 | 149 | var strumNowBelow = strumHandPosition.y <= strummingHeight; | |
| 121 | var STRUM_HEIGHT_ABOVE_PELVIS = 0.10; | 150 | var strumWasAbove = lastPosition.y > strummingHeight; |
| 122 | var strumTriggerHeight = myPelvis.y + STRUM_HEIGHT_ABOVE_PELVIS; | 151 | var strumWasBelow = lastPosition.y <= strummingHeight; |
| 123 | //printVector(position); | 152 | var strumUp = strumNowAbove && strumWasBelow; |
| 124 | if ( ( ((position.y < strumTriggerHeight) && (lastPosition.y >= strumTriggerHeight)) || | 153 | var strumDown = strumNowBelow && strumWasAbove; |
| 125 | ((position.y > strumTriggerHeight) && (lastPosition.y <= strumTriggerHeight)) ) && (trigger > 0.1) ){ | 154 | |
| 126 | // If hand passes downward or upward through 'strings', and finger trigger pulled, play | 155 | if ((strumUp || strumDown) && (strumming > 0.1)) { |
| 127 | playChord(position, volume); | 156 | // If hand passes downward or upward through 'strings', and finger trigger pulled, play |
| 128 | } | 157 | playChord(strumHandPosition, volume); |
| 129 | lastPosition = Controller.getSpatialControlPosition(palm * 2 + 1); | ||
| 130 | } | ||
| 131 | } | 158 | } |
| 159 | lastPosition = strumHandPosition; | ||
| 132 | } | 160 | } |
| 133 | 161 | ||
| 134 | function playChord(position, volume) { | 162 | function stopAudio(killInjector) { |
| 135 | if (soundPlaying.isPlaying) { | 163 | if (audioInjector && audioInjector.isPlaying) { |
| 136 | print("stopped sound"); | 164 | print("stopped sound"); |
| 137 | soundPlaying.stop(); | 165 | audioInjector.stop(); |
| 166 | } | ||
| 167 | if (killInjector) { | ||
| 168 | audioInjector = null; | ||
| 169 | } | ||
| 170 | } | ||
| 171 | |||
| 172 | |||
| 173 | function playChord(position, volume) { | ||
| 174 | stopAudio(); | ||
| 175 | print("Played sound: " + whichChord + " at volume " + volume); | ||
| 176 | if (!audioInjector) { | ||
| 177 | var index = guitarSelector + whichChord; | ||
| 178 | var chord = chords[guitarSelector + whichChord]; | ||
| 179 | audioInjector = Audio.playSound(chord, { position: position, volume: volume }); | ||
| 180 | } else { | ||
| 181 | audioInjector.restart(); | ||
| 138 | } | 182 | } |
| 139 | |||
| 140 | print("Played sound: " + whichChord + " at volume " + options.volume); | ||
| 141 | if (!soundPlaying) { | ||
| 142 | soundPlaying = Audio.playSound(chords[guitarSelector + whichChord], { | ||
| 143 | position: position, | ||
| 144 | volume: volume | ||
| 145 | }); | ||
| 146 | } else { | ||
| 147 | soundPlaying.restart(); | ||
| 148 | } | ||
| 149 | |||
| 150 | } | 183 | } |
| 151 | 184 | ||
| 152 | function keyPressEvent(event) { | 185 | function keyPressEvent(event) { |
| @@ -154,15 +187,19 @@ function keyPressEvent(event) { · 1 unmodified line | |||
| 154 | keyVolume = 0.4; | 187 | keyVolume = 0.4; |
| 155 | if (event.text == "1") { | 188 | if (event.text == "1") { |
| 156 | whichChord = 1; | 189 | whichChord = 1; |
| 190 | stopAudio(true); | ||
| 157 | playChord(MyAvatar.position, keyVolume); | 191 | playChord(MyAvatar.position, keyVolume); |
| 158 | } else if (event.text == "2") { | 192 | } else if (event.text == "2") { |
| 159 | whichChord = 2; | 193 | whichChord = 2; |
| 194 | stopAudio(true); | ||
| 160 | playChord(MyAvatar.position, keyVolume); | 195 | playChord(MyAvatar.position, keyVolume); |
| 161 | } else if (event.text == "3") { | 196 | } else if (event.text == "3") { |
| 162 | whichChord = 3; | 197 | whichChord = 3; |
| 198 | stopAudio(true); | ||
| 163 | playChord(MyAvatar.position, keyVolume); | 199 | playChord(MyAvatar.position, keyVolume); |
| 164 | } else if (event.text == "4") { | 200 | } else if (event.text == "4") { |
| 165 | whichChord = 4; | 201 | whichChord = 4; |
| 202 | stopAudio(true); | ||
| 166 | playChord(MyAvatar.position, keyVolume); | 203 | playChord(MyAvatar.position, keyVolume); |
| 167 | } | 204 | } |
| 168 | } | 205 | } |
| @@ -170,6 +207,7 @@ function keyPressEvent(event) { · 1 unmodified line | |||
| 170 | function scriptEnding() { | 207 | function scriptEnding() { |
| 171 | MyAvatar.detachOne(guitarModel); | 208 | MyAvatar.detachOne(guitarModel); |
| 172 | } | 209 | } |
| 210 | |||
| 173 | // Connect a call back that happens every frame | 211 | // Connect a call back that happens every frame |
| 174 | Script.update.connect(checkHands); | 212 | Script.update.connect(checkHands); |
| 175 | Script.scriptEnding.connect(scriptEnding); | 213 | Script.scriptEnding.connect(scriptEnding); |
| @@ -43,7 +43,8 @@ strokeSpeed[1] = 0.0; · 42 unmodified lines | |||
| 43 | 43 | ||
| 44 | function checkSticks(deltaTime) { | 44 | function checkSticks(deltaTime) { |
| 45 | for (var palm = 0; palm < 2; palm++) { | 45 | for (var palm = 0; palm < 2; palm++) { |
| 46 | var palmVelocity = Controller.getSpatialControlVelocity(palm * 2 + 1); | 46 | var handPose = (palm == 0) ? MyAvatar.leftHandPose : MyAvatar.rightHandPose; |
| 47 | var palmVelocity = handPose.velocity; | ||
| 47 | var speed = length(palmVelocity); | 48 | var speed = length(palmVelocity); |
| 48 | 49 | ||
| 49 | const TRIGGER_SPEED = 0.30; // Lower this value to let you 'drum' more gently | 50 | const TRIGGER_SPEED = 0.30; // Lower this value to let you 'drum' more gently |
| @@ -64,7 +65,7 @@ function checkSticks(deltaTime) { · 14 unmodified lines | |||
| 64 | if ((palmVelocity.y > 0.0) || (speed < STOP_SPEED)) { | 65 | if ((palmVelocity.y > 0.0) || (speed < STOP_SPEED)) { |
| 65 | state[palm] = 0; | 66 | state[palm] = 0; |
| 66 | 67 | ||
| 67 | var options = { position: Controller.getSpatialControlPosition(palm * 2 + 1) }; | 68 | var options = { position: handPose.translation }; |
| 68 | 69 | ||
| 69 | if (strokeSpeed[palm] > 1.0) { strokeSpeed[palm] = 1.0; } | 70 | if (strokeSpeed[palm] > 1.0) { strokeSpeed[palm] = 1.0; } |
| 70 | options.volume = strokeSpeed[palm]; | 71 | options.volume = strokeSpeed[palm]; |
| @@ -130,29 +130,38 @@ function Hand(name, palm, tip, forwardButton, button3, trigger) { · 129 unmodified lines | |||
| 130 | this.trigger = trigger; | 130 | this.trigger = trigger; |
| 131 | this.holdingFrisbee = false; | 131 | this.holdingFrisbee = false; |
| 132 | this.entity = false; | 132 | this.entity = false; |
| 133 | this.palmPosition = function() { return Controller.getSpatialControlPosition(this.palm); } | 133 | this.palmPosition = function () { |
| 134 | return this.palm == LEFT_PALM ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation; | ||
| 135 | }; | ||
| 136 | |||
| 134 | this.grabButtonPressed = function() { | 137 | this.grabButtonPressed = function() { |
| 135 | return ( | 138 | return ( |
| 136 | Controller.isButtonPressed(this.forwardButton) || | 139 | Controller.getValue(this.forwardButton) || |
| 137 | Controller.isButtonPressed(this.button3) || | 140 | Controller.getValue(this.button3) || |
| 138 | Controller.getTriggerValue(this.trigger) > 0.5 | 141 | Controller.getValue(this.trigger) > 0.5 |
| 139 | ) | 142 | ) |
| 140 | }; | 143 | }; |
| 141 | this.holdPosition = function() { return this.palm == LEFT_PALM ? MyAvatar.getLeftPalmPosition() : MyAvatar.getRightPalmPosition(); }; | 144 | this.holdPosition = function () { |
| 145 | return this.palm == LEFT_PALM ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation; | ||
| 146 | }; | ||
| 147 | |||
| 142 | this.holdRotation = function() { | 148 | this.holdRotation = function() { |
| 143 | var q = Controller.getSpatialControlRawRotation(this.palm); | 149 | var q = (this.palm == LEFT_PALM) ? Controller.getPoseValue(Controller.Standard.leftHand).rotation |
| 150 | : Controller.getPoseValue(Controller.Standard.rightHand).rotation; | ||
| 144 | q = Quat.multiply(MyAvatar.orientation, q); | 151 | q = Quat.multiply(MyAvatar.orientation, q); |
| 145 | return {x: q.x, y: q.y, z: q.z, w: q.w}; | 152 | return {x: q.x, y: q.y, z: q.z, w: q.w}; |
| 146 | }; | 153 | }; |
| 147 | this.tipVelocity = function() { return Controller.getSpatialControlVelocity(this.tip); }; | 154 | this.tipVelocity = function () { |
| 155 | return this.tip == LEFT_TIP ? MyAvatar.leftHandTipPose.velocity : MyAvatar.rightHandTipPose.velocity; | ||
| 156 | }; | ||
| 148 | } | 157 | } |
| 149 | 158 | ||
| 150 | function MouseControl(button) { | 159 | function MouseControl(button) { |
| 151 | this.button = button; | 160 | this.button = button; |
| 152 | } | 161 | } |
| 153 | 162 | ||
| 154 | var leftHand = new Hand("LEFT", LEFT_PALM, LEFT_TIP, LEFT_BUTTON_FWD, LEFT_BUTTON_3, 0); | 163 | var leftHand = new Hand("LEFT", LEFT_PALM, LEFT_TIP, Controller.Standard.LB, Controller.Standard.LeftPrimaryThumb, Controller.Standard.LT); |
| 155 | var rightHand = new Hand("RIGHT", RIGHT_PALM, RIGHT_TIP, RIGHT_BUTTON_FWD, RIGHT_BUTTON_3, 1); | 164 | var rightHand = new Hand("RIGHT", RIGHT_PALM, RIGHT_TIP, Controller.Standard.RB, Controller.Standard.RightPrimaryThumb, Controller.Standard.RT); |
| 156 | 165 | ||
| 157 | var leftMouseControl = new MouseControl("LEFT"); | 166 | var leftMouseControl = new MouseControl("LEFT"); |
| 158 | var middleMouseControl = new MouseControl("MIDDLE"); | 167 | var middleMouseControl = new MouseControl("MIDDLE"); |
| @@ -302,12 +311,7 @@ function initToolBar() { · 143 unmodified lines | |||
| 302 | } | 311 | } |
| 303 | 312 | ||
| 304 | function hydraCheck() { | 313 | function hydraCheck() { |
| 305 | var numberOfButtons = Controller.getNumberOfButtons(); | 314 | return Controller.Hardware.Hydra !== undefined; |
| 306 | var numberOfTriggers = Controller.getNumberOfTriggers(); | ||
| 307 | var numberOfSpatialControls = Controller.getNumberOfSpatialControls(); | ||
| 308 | var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers; | ||
| 309 | hydrasConnected = (numberOfButtons == 12 && numberOfTriggers == 2 && controllersPerTrigger == 2); | ||
| 310 | return true;//hydrasConnected; | ||
| 311 | } | 315 | } |
| 312 | 316 | ||
| 313 | function checkController(deltaTime) { | 317 | function checkController(deltaTime) { |
| @@ -15,24 +15,64 @@ · 14 unmodified lines | |||
| 15 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | 15 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html |
| 16 | // | 16 | // |
| 17 | 17 | ||
| 18 | // FIXME kickback functionality was removed because the joint setting interface in | ||
| 19 | // MyAvatar has apparently changed, breaking it. | ||
| 20 | |||
| 21 | Script.include("../../libraries/utils.js"); | ||
| 22 | Script.include("../../libraries/constants.js"); | ||
| 23 | Script.include("../../libraries/toolBars.js"); | ||
| 24 | |||
| 18 | HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; | 25 | HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; |
| 19 | 26 | ||
| 20 | var RED = { red: 255, green: 0, blue: 0 }; | ||
| 21 | var LASER_WIDTH = 2; | 27 | var LASER_WIDTH = 2; |
| 22 | 28 | var POSE_CONTROLS = [ Controller.Standard.LeftHand, Controller.Standard.RightHand ]; | |
| 29 | var TRIGGER_CONTROLS = [ Controller.Standard.LT, Controller.Standard.RT ]; | ||
| 30 | var MIN_THROWER_DELAY = 1000; | ||
| 31 | var MAX_THROWER_DELAY = 1000; | ||
| 32 | var RELOAD_INTERVAL = 5; | ||
| 33 | var GUN_MODEL = HIFI_PUBLIC_BUCKET + "cozza13/gun/m1911-handgun+1.fbx?v=4"; | ||
| 34 | var BULLET_VELOCITY = 10.0; | ||
| 35 | var GUN_OFFSETS = [ { | ||
| 36 | x: -0.04, | ||
| 37 | y: 0.26, | ||
| 38 | z: 0.04 | ||
| 39 | }, { | ||
| 40 | x: 0.04, | ||
| 41 | y: 0.26, | ||
| 42 | z: 0.04 | ||
| 43 | } ]; | ||
| 44 | |||
| 45 | var GUN_ORIENTATIONS = [ Quat.fromPitchYawRollDegrees(0, 90, 90), Quat.fromPitchYawRollDegrees(0, -90, 270) ]; | ||
| 46 | |||
| 47 | var BARREL_OFFSETS = [ { | ||
| 48 | x: -0.12, | ||
| 49 | y: 0.12, | ||
| 50 | z: 0.04 | ||
| 51 | }, { | ||
| 52 | x: 0.12, | ||
| 53 | y: 0.12, | ||
| 54 | z: 0.04 | ||
| 55 | } ]; | ||
| 56 | |||
| 57 | var mapping = Controller.newMapping(); | ||
| 58 | var validPoses = [ false, false ]; | ||
| 59 | var barrelVectors = [ 0, 0 ]; | ||
| 60 | var barrelTips = [ 0, 0 ]; | ||
| 23 | var pointer = []; | 61 | var pointer = []; |
| 62 | |||
| 24 | pointer.push(Overlays.addOverlay("line3d", { | 63 | pointer.push(Overlays.addOverlay("line3d", { |
| 25 | start: { x: 0, y: 0, z: 0 }, | 64 | start: ZERO_VECTOR, |
| 26 | end: { x: 0, y: 0, z: 0 }, | 65 | end: ZERO_VECTOR, |
| 27 | color: RED, | 66 | color: COLORS.RED, |
| 28 | alpha: 1, | 67 | alpha: 1, |
| 29 | visible: true, | 68 | visible: true, |
| 30 | lineWidth: LASER_WIDTH | 69 | lineWidth: LASER_WIDTH |
| 31 | })); | 70 | })); |
| 71 | |||
| 32 | pointer.push(Overlays.addOverlay("line3d", { | 72 | pointer.push(Overlays.addOverlay("line3d", { |
| 33 | start: { x: 0, y: 0, z: 0 }, | 73 | start: ZERO_VECTOR, |
| 34 | end: { x: 0, y: 0, z: 0 }, | 74 | end: ZERO_VECTOR, |
| 35 | color: RED, | 75 | color: COLORS.RED, |
| 36 | alpha: 1, | 76 | alpha: 1, |
| 37 | visible: true, | 77 | visible: true, |
| 38 | lineWidth: LASER_WIDTH | 78 | lineWidth: LASER_WIDTH |
| @@ -42,135 +82,116 @@ function getRandomFloat(min, max) { · 3 unmodified lines | |||
| 42 | return Math.random() * (max - min) + min; | 82 | return Math.random() * (max - min) + min; |
| 43 | } | 83 | } |
| 44 | 84 | ||
| 45 | var lastX = 0; | ||
| 46 | var lastY = 0; | ||
| 47 | var yawFromMouse = 0; | ||
| 48 | var pitchFromMouse = 0; | ||
| 49 | var isMouseDown = false; | ||
| 50 | |||
| 51 | var MIN_THROWER_DELAY = 1000; | ||
| 52 | var MAX_THROWER_DELAY = 1000; | ||
| 53 | var LEFT_BUTTON_3 = 3; | ||
| 54 | var RELOAD_INTERVAL = 5; | ||
| 55 | |||
| 56 | var KICKBACK_ANGLE = 15; | ||
| 57 | var elbowKickAngle = 0.0; | ||
| 58 | var rotationBeforeKickback; | ||
| 59 | |||
| 60 | var showScore = false; | 85 | var showScore = false; |
| 61 | 86 | // Load some sound to use for loading and firing | |
| 62 | |||
| 63 | // Load some sound to use for loading and firing | ||
| 64 | var fireSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/GUN-SHOT2.raw"); | 87 | var fireSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/GUN-SHOT2.raw"); |
| 65 | var loadSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/Gun_Reload_Weapon22.raw"); | 88 | var loadSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/Gun_Reload_Weapon22.raw"); |
| 66 | var impactSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/BulletImpact2.raw"); | 89 | var impactSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/BulletImpact2.raw"); |
| 67 | var targetHitSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/hit.raw"); | 90 | var targetHitSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/hit.raw"); |
| 68 | var targetLaunchSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/shoot.raw"); | 91 | var targetLaunchSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/shoot.raw"); |
| 69 | 92 | ||
| 70 | var gunModel = "https://s3.amazonaws.com/hifi-public/cozza13/gun/m1911-handgun+1.fbx?v=4"; | ||
| 71 | |||
| 72 | var audioOptions = { | 93 | var audioOptions = { |
| 73 | volume: 0.9 | 94 | volume: 0.9 |
| 74 | } | 95 | } |
| 75 | 96 | ||
| 76 | var shotsFired = 0; | 97 | var shotsFired = 0; |
| 77 | var shotTime = new Date(); | 98 | var shotTime = new Date(); |
| 78 | 99 | var isLaunchButtonPressed = false; | |
| 79 | var activeControllers = 0; | 100 | var score = 0; |
| 80 | |||
| 81 | // initialize our controller triggers | ||
| 82 | var triggerPulled = new Array(); | ||
| 83 | var numberOfTriggers = Controller.getNumberOfTriggers(); | ||
| 84 | for (t = 0; t < numberOfTriggers; t++) { | ||
| 85 | triggerPulled[t] = false; | ||
| 86 | } | ||
| 87 | |||
| 88 | var isLaunchButtonPressed = false; | ||
| 89 | var score = 0; | ||
| 90 | 101 | ||
| 91 | var bulletID = false; | 102 | var bulletID = false; |
| 92 | var targetID = false; | 103 | var targetID = false; |
| 93 | 104 | ||
| 94 | // Create overlay buttons and reticle | 105 | // Create overlay buttons and reticle |
| 95 | |||
| 96 | var BUTTON_SIZE = 32; | 106 | var BUTTON_SIZE = 32; |
| 97 | var PADDING = 3; | 107 | var PADDING = 3; |
| 98 | var NUM_BUTTONS = 3; | 108 | var NUM_BUTTONS = 3; |
| 99 | |||
| 100 | var screenSize = Controller.getViewportDimensions(); | 109 | var screenSize = Controller.getViewportDimensions(); |
| 101 | var startX = screenSize.x / 2 - (NUM_BUTTONS * (BUTTON_SIZE + PADDING)) / 2; | 110 | var startX = screenSize.x / 2 - (NUM_BUTTONS * (BUTTON_SIZE + PADDING)) / 2; |
| 102 | Script.include(["../../libraries/toolBars.js"]); | 111 | var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL, "highfidelity.gun.toolbar", function(screenSize) { |
| 103 | var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL, "highfidelity.gun.toolbar", function (screenSize) { | ||
| 104 | return { | 112 | return { |
| 105 | x: startX, | 113 | x: startX, |
| 106 | y: (screenSize.y - (BUTTON_SIZE + PADDING)), | 114 | y: (screenSize.y - (BUTTON_SIZE + PADDING)), |
| 107 | }; | 115 | }; |
| 108 | }); | 116 | }); |
| 109 | var reticle = Overlays.addOverlay("image", { | ||
| 110 | x: screenSize.x / 2 - (BUTTON_SIZE / 2), | ||
| 111 | y: screenSize.y / 2 - (BUTTON_SIZE / 2), | ||
| 112 | width: BUTTON_SIZE, | ||
| 113 | height: BUTTON_SIZE, | ||
| 114 | imageURL: HIFI_PUBLIC_BUCKET + "images/gun/crosshairs.svg", | ||
| 115 | alpha: 1 | ||
| 116 | }); | ||
| 117 | 117 | ||
| 118 | var offButton = toolBar.addOverlay("image", { | 118 | var offButton = toolBar.addOverlay("image", { |
| 119 | width: BUTTON_SIZE, | 119 | width: BUTTON_SIZE, |
| 120 | height: BUTTON_SIZE, | 120 | height: BUTTON_SIZE, |
| 121 | imageURL: HIFI_PUBLIC_BUCKET + "images/gun/close.svg", | 121 | imageURL: HIFI_PUBLIC_BUCKET + "images/gun/close.svg", |
| 122 | alpha: 1 | 122 | alpha: 1 |
| 123 | }); | 123 | }); |
| 124 | 124 | ||
| 125 | startX += BUTTON_SIZE + PADDING; | 125 | startX += BUTTON_SIZE + PADDING; |
| 126 | var platformButton = toolBar.addOverlay("image", { | 126 | var platformButton = toolBar.addOverlay("image", { |
| 127 | x: startX, | 127 | x: startX, |
| 128 | y: screenSize.y - (BUTTON_SIZE + PADDING), | 128 | y: screenSize.y - (BUTTON_SIZE + PADDING), |
| 129 | width: BUTTON_SIZE, | 129 | width: BUTTON_SIZE, |
| 130 | height: BUTTON_SIZE, | 130 | height: BUTTON_SIZE, |
| 131 | imageURL: HIFI_PUBLIC_BUCKET + "images/gun/platform-targets.svg", | 131 | imageURL: HIFI_PUBLIC_BUCKET + "images/gun/platform-targets.svg", |
| 132 | alpha: 1 | 132 | alpha: 1 |
| 133 | }); | 133 | }); |
| 134 | 134 | ||
| 135 | startX += BUTTON_SIZE + PADDING; | 135 | startX += BUTTON_SIZE + PADDING; |
| 136 | var gridButton = toolBar.addOverlay("image", { | 136 | var gridButton = toolBar.addOverlay("image", { |
| 137 | x: startX, | 137 | x: startX, |
| 138 | y: screenSize.y - (BUTTON_SIZE + PADDING), | 138 | y: screenSize.y - (BUTTON_SIZE + PADDING), |
| 139 | width: BUTTON_SIZE, | 139 | width: BUTTON_SIZE, |
| 140 | height: BUTTON_SIZE, | 140 | height: BUTTON_SIZE, |
| 141 | imageURL: HIFI_PUBLIC_BUCKET + "images/gun/floating-targets.svg", | 141 | imageURL: HIFI_PUBLIC_BUCKET + "images/gun/floating-targets.svg", |
| 142 | alpha: 1 | 142 | alpha: 1 |
| 143 | }); | 143 | }); |
| 144 | 144 | ||
| 145 | if (showScore) { | 145 | if (showScore) { |
| 146 | var text = Overlays.addOverlay("text", { | 146 | var text = Overlays.addOverlay("text", { |
| 147 | x: screenSize.x / 2 - 100, | 147 | x: screenSize.x / 2 - 100, |
| 148 | y: screenSize.y / 2 - 50, | 148 | y: screenSize.y / 2 - 50, |
| 149 | width: 150, | 149 | width: 150, |
| 150 | height: 50, | 150 | height: 50, |
| 151 | color: { red: 0, green: 0, blue: 0}, | 151 | color: { |
| 152 | textColor: { red: 255, green: 0, blue: 0}, | 152 | red: 0, |
| 153 | topMargin: 4, | 153 | green: 0, |
| 154 | leftMargin: 4, | 154 | blue: 0 |
| 155 | text: "Score: " + score | 155 | }, |
| 156 | }); | 156 | textColor: { |
| 157 | red: 255, | ||
| 158 | green: 0, | ||
| 159 | blue: 0 | ||
| 160 | }, | ||
| 161 | topMargin: 4, | ||
| 162 | leftMargin: 4, | ||
| 163 | text: "Score: " + score | ||
| 164 | }); | ||
| 157 | } | 165 | } |
| 158 | 166 | ||
| 159 | var BULLET_VELOCITY = 10.0; | ||
| 160 | |||
| 161 | function entityCollisionWithEntity(entity1, entity2, collision) { | 167 | function entityCollisionWithEntity(entity1, entity2, collision) { |
| 162 | if (entity2 === targetID) { | 168 | if (entity2 === targetID) { |
| 163 | score++; | 169 | score++; |
| 164 | if (showScore) { | 170 | if (showScore) { |
| 165 | Overlays.editOverlay(text, { text: "Score: " + score } ); | 171 | Overlays.editOverlay(text, { |
| 172 | text: "Score: " + score | ||
| 173 | }); | ||
| 166 | } | 174 | } |
| 167 | 175 | ||
| 168 | // We will delete the bullet and target in 1/2 sec, but for now we can see them bounce! | 176 | // We will delete the bullet and target in 1/2 sec, but for now we can |
| 177 | // see them bounce! | ||
| 169 | Script.setTimeout(deleteBulletAndTarget, 500); | 178 | Script.setTimeout(deleteBulletAndTarget, 500); |
| 170 | 179 | ||
| 171 | // Turn the target and the bullet white | 180 | // Turn the target and the bullet white |
| 172 | Entities.editEntity(entity1, { color: { red: 255, green: 255, blue: 255 }}); | 181 | Entities.editEntity(entity1, { |
| 173 | Entities.editEntity(entity2, { color: { red: 255, green: 255, blue: 255 }}); | 182 | color: { |
| 183 | red: 255, | ||
| 184 | green: 255, | ||
| 185 | blue: 255 | ||
| 186 | } | ||
| 187 | }); | ||
| 188 | Entities.editEntity(entity2, { | ||
| 189 | color: { | ||
| 190 | red: 255, | ||
| 191 | green: 255, | ||
| 192 | blue: 255 | ||
| 193 | } | ||
| 194 | }); | ||
| 174 | 195 | ||
| 175 | // play the sound near the camera so the shooter can hear it | 196 | // play the sound near the camera so the shooter can hear it |
| 176 | audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); | 197 | audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); |
| @@ -188,41 +209,45 @@ function shootBullet(position, velocity, grenade) { · 11 unmodified lines | |||
| 188 | 209 | ||
| 189 | var bVelocity = grenade ? Vec3.multiply(GRENADE_VELOCITY, Vec3.normalize(velocity)) : velocity; | 210 | var bVelocity = grenade ? Vec3.multiply(GRENADE_VELOCITY, Vec3.normalize(velocity)) : velocity; |
| 190 | var bSize = grenade ? GRENADE_SIZE : BULLET_SIZE; | 211 | var bSize = grenade ? GRENADE_SIZE : BULLET_SIZE; |
| 191 | var bGravity = grenade ? GRENADE_GRAVITY : BULLET_GRAVITY; | 212 | var bGravity = grenade ? GRENADE_GRAVITY : BULLET_GRAVITY; |
| 192 | 213 | ||
| 193 | bulletID = Entities.addEntity( | 214 | bulletID = Entities.addEntity({ |
| 194 | { type: "Sphere", | 215 | type: "Sphere", |
| 195 | position: position, | 216 | position: position, |
| 196 | dimensions: { x: bSize, y: bSize, z: bSize }, | 217 | dimensions: { |
| 197 | color: { red: 0, green: 0, blue: 0 }, | 218 | x: bSize, |
| 198 | velocity: bVelocity, | 219 | y: bSize, |
| 199 | lifetime: BULLET_LIFETIME, | 220 | z: bSize |
| 200 | gravity: { x: 0, y: bGravity, z: 0 }, | 221 | }, |
| 201 | damping: 0.01, | 222 | color: { |
| 202 | density: 8000, | 223 | red: 0, |
| 203 | ignoreCollisions: false, | 224 | green: 0, |
| 204 | collisionsWillMove: true | 225 | blue: 0 |
| 205 | }); | 226 | }, |
| 227 | velocity: bVelocity, | ||
| 228 | lifetime: BULLET_LIFETIME, | ||
| 229 | gravity: { | ||
| 230 | x: 0, | ||
| 231 | y: bGravity, | ||
| 232 | z: 0 | ||
| 233 | }, | ||
| 234 | damping: 0.01, | ||
| 235 | density: 8000, | ||
| 236 | ignoreCollisions: false, | ||
| 237 | collisionsWillMove: true | ||
| 238 | }); | ||
| 239 | |||
| 206 | Script.addEventHandler(bulletID, "collisionWithEntity", entityCollisionWithEntity); | 240 | Script.addEventHandler(bulletID, "collisionWithEntity", entityCollisionWithEntity); |
| 207 | 241 | ||
| 208 | // Play firing sounds | 242 | // Play firing sounds |
| 209 | audioOptions.position = position; | 243 | audioOptions.position = position; |
| 210 | Audio.playSound(fireSound, audioOptions); | 244 | Audio.playSound(fireSound, audioOptions); |
| 211 | shotsFired++; | 245 | shotsFired++; |
| 212 | if ((shotsFired % RELOAD_INTERVAL) == 0) { | 246 | if ((shotsFired % RELOAD_INTERVAL) == 0) { |
| 213 | Audio.playSound(loadSound, audioOptions); | 247 | Audio.playSound(loadSound, audioOptions); |
| 214 | } | 248 | } |
| 215 | |||
| 216 | // Kickback the arm | ||
| 217 | if (elbowKickAngle > 0.0) { | ||
| 218 | MyAvatar.setJointData("LeftForeArm", rotationBeforeKickback); | ||
| 219 | } | ||
| 220 | rotationBeforeKickback = MyAvatar.getJointRotation("LeftForeArm"); | ||
| 221 | var armRotation = MyAvatar.getJointRotation("LeftForeArm"); | ||
| 222 | armRotation = Quat.multiply(armRotation, Quat.fromPitchYawRollDegrees(0.0, 0.0, KICKBACK_ANGLE)); | ||
| 223 | MyAvatar.setJointData("LeftForeArm", armRotation); | ||
| 224 | elbowKickAngle = KICKBACK_ANGLE; | ||
| 225 | } | 249 | } |
| 250 | |||
| 226 | function shootTarget() { | 251 | function shootTarget() { |
| 227 | var TARGET_SIZE = 0.50; | 252 | var TARGET_SIZE = 0.50; |
| 228 | var TARGET_GRAVITY = 0.0; | 253 | var TARGET_GRAVITY = 0.0; |
| @@ -232,95 +257,152 @@ function shootTarget() { · 3 unmodified lines | |||
| 232 | var DISTANCE_TO_LAUNCH_FROM = 5.0; | 257 | var DISTANCE_TO_LAUNCH_FROM = 5.0; |
| 233 | var ANGLE_RANGE_FOR_LAUNCH = 20.0; | 258 | var ANGLE_RANGE_FOR_LAUNCH = 20.0; |
| 234 | var camera = Camera.getPosition(); | 259 | var camera = Camera.getPosition(); |
| 235 | 260 | ||
| 236 | var targetDirection = Quat.angleAxis(getRandomFloat(-ANGLE_RANGE_FOR_LAUNCH, ANGLE_RANGE_FOR_LAUNCH), { x:0, y:1, z:0 }); | 261 | var targetDirection = Quat.angleAxis(getRandomFloat(-ANGLE_RANGE_FOR_LAUNCH, ANGLE_RANGE_FOR_LAUNCH), { |
| 262 | x: 0, | ||
| 263 | y: 1, | ||
| 264 | z: 0 | ||
| 265 | }); | ||
| 237 | targetDirection = Quat.multiply(Camera.getOrientation(), targetDirection); | 266 | targetDirection = Quat.multiply(Camera.getOrientation(), targetDirection); |
| 238 | var forwardVector = Quat.getFront(targetDirection); | 267 | var forwardVector = Quat.getFront(targetDirection); |
| 239 | 268 | ||
| 240 | var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_TO_LAUNCH_FROM)); | 269 | var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_TO_LAUNCH_FROM)); |
| 241 | 270 | ||
| 242 | var velocity = Vec3.multiply(forwardVector, TARGET_FWD_VELOCITY); | 271 | var velocity = Vec3.multiply(forwardVector, TARGET_FWD_VELOCITY); |
| 243 | velocity.y += TARGET_UP_VELOCITY; | 272 | velocity.y += TARGET_UP_VELOCITY; |
| 244 | 273 | ||
| 245 | targetID = Entities.addEntity( | 274 | targetID = Entities.addEntity({ |
| 246 | { type: "Box", | 275 | type: "Box", |
| 247 | position: newPosition, | 276 | position: newPosition, |
| 248 | dimensions: { x: TARGET_SIZE * (0.5 + Math.random()), y: TARGET_SIZE * (0.5 + Math.random()), z: TARGET_SIZE * (0.5 + Math.random()) / 4.0 }, | 277 | dimensions: { |
| 249 | color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 }, | 278 | x: TARGET_SIZE * (0.5 + Math.random()), |
| 250 | velocity: velocity, | 279 | y: TARGET_SIZE * (0.5 + Math.random()), |
| 251 | gravity: { x: 0, y: TARGET_GRAVITY, z: 0 }, | 280 | z: TARGET_SIZE * (0.5 + Math.random()) / 4.0 |
| 252 | lifetime: TARGET_LIFETIME, | 281 | }, |
| 253 | rotation: Camera.getOrientation(), | 282 | color: { |
| 254 | damping: 0.1, | 283 | red: Math.random() * 255, |
| 255 | density: 100.0, | 284 | green: Math.random() * 255, |
| 256 | collisionsWillMove: true }); | 285 | blue: Math.random() * 255 |
| 257 | 286 | }, | |
| 258 | // Record start time | 287 | velocity: velocity, |
| 288 | gravity: { | ||
| 289 | x: 0, | ||
| 290 | y: TARGET_GRAVITY, | ||
| 291 | z: 0 | ||
| 292 | }, | ||
| 293 | lifetime: TARGET_LIFETIME, | ||
| 294 | rotation: Camera.getOrientation(), | ||
| 295 | damping: 0.1, | ||
| 296 | density: 100.0, | ||
| 297 | collisionsWillMove: true | ||
| 298 | }); | ||
| 299 | |||
| 300 | // Record start time | ||
| 259 | shotTime = new Date(); | 301 | shotTime = new Date(); |
| 260 | 302 | ||
| 261 | // Play target shoot sound | 303 | // Play target shoot sound |
| 262 | audioOptions.position = newPosition; | 304 | audioOptions.position = newPosition; |
| 263 | Audio.playSound(targetLaunchSound, audioOptions); | 305 | Audio.playSound(targetLaunchSound, audioOptions); |
| 264 | } | 306 | } |
| 265 | 307 | ||
| 266 | function makeGrid(type, scale, size) { | 308 | function makeGrid(type, scale, size) { |
| 267 | var separation = scale * 2; | 309 | var separation = scale * 2; |
| 268 | var pos = Vec3.sum(Camera.getPosition(), Vec3.multiply(10.0 * scale * separation, Quat.getFront(Camera.getOrientation()))); | 310 | var pos = Vec3.sum(Camera.getPosition(), Vec3.multiply(10.0 * scale * separation, Quat.getFront(Camera.getOrientation()))); |
| 269 | var x, y, z; | 311 | var x, y, z; |
| 270 | var GRID_LIFE = 60.0; | 312 | var GRID_LIFE = 60.0; |
| 271 | var dimensions; | 313 | var dimensions; |
| 272 | 314 | ||
| 273 | for (x = 0; x < size; x++) { | 315 | for (x = 0; x < size; x++) { |
| 274 | for (y = 0; y < size; y++) { | 316 | for (y = 0; y < size; y++) { |
| 275 | for (z = 0; z < size; z++) { | 317 | for (z = 0; z < size; z++) { |
| 276 | 318 | ||
| 277 | dimensions = { x: separation/2.0 * (0.5 + Math.random()), y: separation/2.0 * (0.5 + Math.random()), z: separation/2.0 * (0.5 + Math.random()) / 4.0 }; | 319 | dimensions = { |
| 278 | 320 | x: separation / 2.0 * (0.5 + Math.random()), | |
| 279 | Entities.addEntity( | 321 | y: separation / 2.0 * (0.5 + Math.random()), |
| 280 | { type: type, | 322 | z: separation / 2.0 * (0.5 + Math.random()) / 4.0 |
| 281 | position: { x: pos.x + x * separation, y: pos.y + y * separation, z: pos.z + z * separation }, | 323 | }; |
| 282 | dimensions: dimensions, | 324 | |
| 283 | color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 }, | 325 | Entities.addEntity({ |
| 284 | velocity: { x: 0, y: 0, z: 0 }, | 326 | type: type, |
| 285 | gravity: { x: 0, y: 0, z: 0 }, | 327 | position: { |
| 286 | lifetime: GRID_LIFE, | 328 | x: pos.x + x * separation, |
| 287 | rotation: Camera.getOrientation(), | 329 | y: pos.y + y * separation, |
| 288 | damping: 0.1, | 330 | z: pos.z + z * separation |
| 289 | density: 100.0, | 331 | }, |
| 290 | collisionsWillMove: true }); | 332 | dimensions: dimensions, |
| 333 | color: { | ||
| 334 | red: Math.random() * 255, | ||
| 335 | green: Math.random() * 255, | ||
| 336 | blue: Math.random() * 255 | ||
| 337 | }, | ||
| 338 | velocity: { | ||
| 339 | x: 0, | ||
| 340 | y: 0, | ||
| 341 | z: 0 | ||
| 342 | }, | ||
| 343 | gravity: { | ||
| 344 | x: 0, | ||
| 345 | y: 0, | ||
| 346 | z: 0 | ||
| 347 | }, | ||
| 348 | lifetime: GRID_LIFE, | ||
| 349 | rotation: Camera.getOrientation(), | ||
| 350 | damping: 0.1, | ||
| 351 | density: 100.0, | ||
| 352 | collisionsWillMove: true | ||
| 353 | }); | ||
| 291 | } | 354 | } |
| 292 | } | 355 | } |
| 293 | } | 356 | } |
| 294 | } | 357 | } |
| 295 | 358 | ||
| 296 | function makePlatform(gravity, scale, size) { | 359 | function makePlatform(gravity, scale, size) { |
| 297 | var separation = scale * 2; | 360 | var separation = scale * 2; |
| 298 | var pos = Vec3.sum(Camera.getPosition(), Vec3.multiply(10.0 * scale * separation, Quat.getFront(Camera.getOrientation()))); | 361 | var pos = Vec3.sum(Camera.getPosition(), Vec3.multiply(10.0 * scale * separation, Quat.getFront(Camera.getOrientation()))); |
| 299 | pos.y -= separation * size; | 362 | pos.y -= separation * size; |
| 300 | var x, y, z; | 363 | var x, y, z; |
| 301 | var TARGET_LIFE = 60.0; | 364 | var TARGET_LIFE = 60.0; |
| 302 | var INITIAL_GAP = 0.5; | 365 | var INITIAL_GAP = 0.5; |
| 303 | var dimensions; | 366 | var dimensions; |
| 304 | 367 | ||
| 305 | for (x = 0; x < size; x++) { | 368 | for (x = 0; x < size; x++) { |
| 306 | for (y = 0; y < size; y++) { | 369 | for (y = 0; y < size; y++) { |
| 307 | for (z = 0; z < size; z++) { | 370 | for (z = 0; z < size; z++) { |
| 308 | 371 | ||
| 309 | dimensions = { x: separation/2.0, y: separation, z: separation/2.0 }; | 372 | dimensions = { |
| 310 | 373 | x: separation / 2.0, | |
| 311 | Entities.addEntity( | 374 | y: separation, |
| 312 | { type: "Box", | 375 | z: separation / 2.0 |
| 313 | position: { x: pos.x - (separation * size / 2.0) + x * separation, | 376 | }; |
| 314 | y: pos.y + y * (separation + INITIAL_GAP), | 377 | |
| 315 | z: pos.z - (separation * size / 2.0) + z * separation }, | 378 | Entities.addEntity({ |
| 316 | dimensions: dimensions, | 379 | type: "Box", |
| 317 | color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 }, | 380 | position: { |
| 318 | velocity: { x: 0, y: 0.05, z: 0 }, | 381 | x: pos.x - (separation * size / 2.0) + x * separation, |
| 319 | gravity: { x: 0, y: gravity, z: 0 }, | 382 | y: pos.y + y * (separation + INITIAL_GAP), |
| 320 | lifetime: TARGET_LIFE, | 383 | z: pos.z - (separation * size / 2.0) + z * separation |
| 321 | damping: 0.1, | 384 | }, |
| 322 | density: 100.0, | 385 | dimensions: dimensions, |
| 323 | collisionsWillMove: true }); | 386 | color: { |
| 387 | red: Math.random() * 255, | ||
| 388 | green: Math.random() * 255, | ||
| 389 | blue: Math.random() * 255 | ||
| 390 | }, | ||
| 391 | velocity: { | ||
| 392 | x: 0, | ||
| 393 | y: 0.05, | ||
| 394 | z: 0 | ||
| 395 | }, | ||
| 396 | gravity: { | ||
| 397 | x: 0, | ||
| 398 | y: gravity, | ||
| 399 | z: 0 | ||
| 400 | }, | ||
| 401 | lifetime: TARGET_LIFE, | ||
| 402 | damping: 0.1, | ||
| 403 | density: 100.0, | ||
| 404 | collisionsWillMove: true | ||
| 405 | }); | ||
| 324 | } | 406 | } |
| 325 | } | 407 | } |
| 326 | } | 408 | } |
| @@ -328,9 +410,21 @@ function makePlatform(gravity, scale, size) { · 1 unmodified line | |||
| 328 | // Make a floor for this stuff to fall onto | 410 | // Make a floor for this stuff to fall onto |
| 329 | Entities.addEntity({ | 411 | Entities.addEntity({ |
| 330 | type: "Box", | 412 | type: "Box", |
| 331 | position: { x: pos.x, y: pos.y - separation / 2.0, z: pos.z }, | 413 | position: { |
| 332 | dimensions: { x: 2.0 * separation * size, y: separation / 2.0, z: 2.0 * separation * size }, | 414 | x: pos.x, |
| 333 | color: { red: 100, green: 100, blue: 100 }, | 415 | y: pos.y - separation / 2.0, |
| 416 | z: pos.z | ||
| 417 | }, | ||
| 418 | dimensions: { | ||
| 419 | x: 2.0 * separation * size, | ||
| 420 | y: separation / 2.0, | ||
| 421 | z: 2.0 * separation * size | ||
| 422 | }, | ||
| 423 | color: { | ||
| 424 | red: 100, | ||
| 425 | green: 100, | ||
| 426 | blue: 100 | ||
| 427 | }, | ||
| 334 | lifetime: TARGET_LIFE | 428 | lifetime: TARGET_LIFE |
| 335 | }); | 429 | }); |
| 336 | 430 | ||
| @@ -340,153 +434,79 @@ function keyPressEvent(event) { · 3 unmodified lines | |||
| 340 | // if our tools are off, then don't do anything | 434 | // if our tools are off, then don't do anything |
| 341 | if (event.text == "t") { | 435 | if (event.text == "t") { |
| 342 | var time = MIN_THROWER_DELAY + Math.random() * MAX_THROWER_DELAY; | 436 | var time = MIN_THROWER_DELAY + Math.random() * MAX_THROWER_DELAY; |
| 343 | Script.setTimeout(shootTarget, time); | 437 | Script.setTimeout(shootTarget, time); |
| 344 | } else if ((event.text == ".") || (event.text == "SPACE")) { | 438 | } else if ((event.text == ".") || (event.text == "SPACE")) { |
| 345 | shootFromMouse(false); | 439 | shootFromMouse(false); |
| 346 | } else if (event.text == ",") { | 440 | } else if (event.text == ",") { |
| 347 | shootFromMouse(true); | 441 | shootFromMouse(true); |
| 348 | } else if (event.text == "r") { | 442 | } else if (event.text == "r") { |
| 349 | playLoadSound(); | 443 | playLoadSound(); |
| 350 | } else if (event.text == "s") { | ||
| 351 | // Hit this key to dump a posture from hydra to log | ||
| 352 | Quat.print("arm = ", MyAvatar.getJointRotation("LeftArm")); | ||
| 353 | Quat.print("forearm = ", MyAvatar.getJointRotation("LeftForeArm")); | ||
| 354 | Quat.print("hand = ", MyAvatar.getJointRotation("LeftHand")); | ||
| 355 | } | 444 | } |
| 356 | } | 445 | } |
| 357 | 446 | ||
| 358 | function playLoadSound() { | 447 | function playLoadSound() { |
| 359 | audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); | 448 | audioOptions.position = MyAvatar.leftHandPose.translation; |
| 360 | Audio.playSound(loadSound, audioOptions); | 449 | Audio.playSound(loadSound, audioOptions); |
| 361 | // Raise arm to firing posture | ||
| 362 | takeFiringPose(); | ||
| 363 | } | ||
| 364 | |||
| 365 | function clearPose() { | ||
| 366 | MyAvatar.clearJointData("LeftForeArm"); | ||
| 367 | MyAvatar.clearJointData("LeftArm"); | ||
| 368 | MyAvatar.clearJointData("LeftHand"); | ||
| 369 | } | 450 | } |
| 370 | 451 | ||
| 371 | function deleteBulletAndTarget() { | 452 | function deleteBulletAndTarget() { |
| 372 | Entities.deleteEntity(bulletID); | 453 | Entities.deleteEntity(bulletID); |
| 373 | Entities.deleteEntity(targetID); | 454 | Entities.deleteEntity(targetID); |
| 374 | bulletID = false; | 455 | bulletID = false; |
| 375 | targetID = false; | 456 | targetID = false; |
| 376 | } | ||
| 377 | |||
| 378 | function takeFiringPose() { | ||
| 379 | clearPose(); | ||
| 380 | if (Controller.getNumberOfSpatialControls() == 0) { | ||
| 381 | MyAvatar.setJointData("LeftForeArm", {x: -0.251919, y: -0.0415449, z: 0.499487, w: 0.827843}); | ||
| 382 | MyAvatar.setJointData("LeftArm", { x: 0.470196, y: -0.132559, z: 0.494033, w: 0.719219}); | ||
| 383 | MyAvatar.setJointData("LeftHand", { x: -0.0104815, y: -0.110551, z: -0.352111, w: 0.929333}); | ||
| 384 | } | ||
| 385 | } | 457 | } |
| 386 | 458 | ||
| 387 | MyAvatar.attach(gunModel, "RightHand", {x:0.04, y: 0.22, z: 0.02}, Quat.fromPitchYawRollDegrees(-172, -85, 79), 0.40); | ||
| 388 | MyAvatar.attach(gunModel, "LeftHand", {x:-0.04, y: 0.22, z: 0.02}, Quat.fromPitchYawRollDegrees(-172, 85, -79), 0.40); | ||
| 389 | |||
| 390 | // Give a bit of time to load before playing sound | ||
| 391 | Script.setTimeout(playLoadSound, 2000); | ||
| 392 | |||
| 393 | function update(deltaTime) { | 459 | function update(deltaTime) { |
| 394 | if (activeControllers == 0) { | 460 | // FIXME we should also expose MyAvatar.handPoses[2], MyAvatar.tipPoses[2] |
| 395 | if (Controller.getNumberOfSpatialControls() > 0) { | 461 | var tipPoses = [ MyAvatar.leftHandTipPose, MyAvatar.rightHandTipPose ]; |
| 396 | activeControllers = Controller.getNumberOfSpatialControls(); | 462 | |
| 397 | clearPose(); | 463 | for (var side = 0; side < 2; side++) { |
| 398 | } | 464 | // First check if the controller is valid |
| 399 | } | 465 | var controllerPose = Controller.getPoseValue(POSE_CONTROLS[side]); |
| 400 | 466 | validPoses[side] = controllerPose.valid; | |
| 401 | var KICKBACK_DECAY_RATE = 0.125; | 467 | if (!controllerPose.valid) { |
| 402 | if (elbowKickAngle > 0.0) { | 468 | Overlays.editOverlay(pointer[side], { |
| 403 | if (elbowKickAngle > 0.5) { | 469 | visible: false |
| 404 | var newAngle = elbowKickAngle * KICKBACK_DECAY_RATE; | ||
| 405 | elbowKickAngle -= newAngle; | ||
| 406 | var armRotation = MyAvatar.getJointRotation("LeftForeArm"); | ||
| 407 | armRotation = Quat.multiply(armRotation, Quat.fromPitchYawRollDegrees(0.0, 0.0, -newAngle)); | ||
| 408 | MyAvatar.setJointData("LeftForeArm", armRotation); | ||
| 409 | } else { | ||
| 410 | MyAvatar.setJointData("LeftForeArm", rotationBeforeKickback); | ||
| 411 | if (Controller.getNumberOfSpatialControls() > 0) { | ||
| 412 | clearPose(); | ||
| 413 | } | ||
| 414 | elbowKickAngle = 0.0; | ||
| 415 | } | ||
| 416 | } | ||
| 417 | |||
| 418 | |||
| 419 | // check for trigger press | ||
| 420 | |||
| 421 | var numberOfTriggers = 2; | ||
| 422 | var controllersPerTrigger = 2; | ||
| 423 | |||
| 424 | if (numberOfTriggers == 2 && controllersPerTrigger == 2) { | ||
| 425 | for (var t = 0; t < 2; t++) { | ||
| 426 | var shootABullet = false; | ||
| 427 | var triggerValue = Controller.getTriggerValue(t); | ||
| 428 | if (triggerPulled[t]) { | ||
| 429 | // must release to at least 0.1 | ||
| 430 | if (triggerValue < 0.1) { | ||
| 431 | triggerPulled[t] = false; // unpulled | ||
| 432 | } | ||
| 433 | } else { | ||
| 434 | // must pull to at least | ||
| 435 | if (triggerValue > 0.5) { | ||
| 436 | triggerPulled[t] = true; // pulled | ||
| 437 | shootABullet = true; | ||
| 438 | } | ||
| 439 | } | ||
| 440 | var palmController = t * controllersPerTrigger; | ||
| 441 | var palmPosition = Controller.getSpatialControlPosition(palmController); | ||
| 442 | var fingerTipController = palmController + 1; | ||
| 443 | var fingerTipPosition = Controller.getSpatialControlPosition(fingerTipController); | ||
| 444 | var laserTip = Vec3.sum(Vec3.multiply(100.0, Vec3.subtract(fingerTipPosition, palmPosition)), palmPosition); | ||
| 445 | |||
| 446 | // Update Lasers | ||
| 447 | Overlays.editOverlay(pointer[t], { | ||
| 448 | start: palmPosition, | ||
| 449 | end: laserTip, | ||
| 450 | alpha: 1 | ||
| 451 | }); | 470 | }); |
| 452 | 471 | continue; | |
| 453 | if (shootABullet) { | ||
| 454 | |||
| 455 | var palmToFingerTipVector = | ||
| 456 | { x: (fingerTipPosition.x - palmPosition.x), | ||
| 457 | y: (fingerTipPosition.y - palmPosition.y), | ||
| 458 | z: (fingerTipPosition.z - palmPosition.z) }; | ||
| 459 | |||
| 460 | // just off the front of the finger tip | ||
| 461 | var position = { x: fingerTipPosition.x + palmToFingerTipVector.x/2, | ||
| 462 | y: fingerTipPosition.y + palmToFingerTipVector.y/2, | ||
| 463 | z: fingerTipPosition.z + palmToFingerTipVector.z/2}; | ||
| 464 | |||
| 465 | var velocity = Vec3.multiply(BULLET_VELOCITY, Vec3.normalize(palmToFingerTipVector)); | ||
| 466 | |||
| 467 | shootBullet(position, velocity, false); | ||
| 468 | } | ||
| 469 | } | 472 | } |
| 470 | } | ||
| 471 | } | ||
| 472 | 473 | ||
| 473 | function shootFromMouse(grenade) { | 474 | // Need to adjust the laser |
| 474 | var DISTANCE_FROM_CAMERA = 1.0; | 475 | var tipPose = tipPoses[side]; |
| 475 | var camera = Camera.getPosition(); | 476 | var handRotation = tipPoses[side].rotation; |
| 476 | var forwardVector = Quat.getFront(Camera.getOrientation()); | 477 | var barrelOffset = Vec3.multiplyQbyV(handRotation, BARREL_OFFSETS[side]); |
| 477 | var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_FROM_CAMERA)); | 478 | barrelTips[side] = Vec3.sum(tipPose.translation, barrelOffset); |
| 478 | var velocity = Vec3.multiply(forwardVector, BULLET_VELOCITY); | 479 | barrelVectors[side] = Vec3.multiplyQbyV(handRotation, { |
| 479 | shootBullet(newPosition, velocity, grenade); | 480 | x: 0, |
| 481 | y: 1, | ||
| 482 | z: 0 | ||
| 483 | }); | ||
| 484 | |||
| 485 | var laserTip = Vec3.sum(Vec3.multiply(100.0, barrelVectors[side]), barrelTips[side]); | ||
| 486 | // Update Lasers | ||
| 487 | Overlays.editOverlay(pointer[side], { | ||
| 488 | start: barrelTips[side], | ||
| 489 | end: laserTip, | ||
| 490 | alpha: 1, | ||
| 491 | visible: true | ||
| 492 | }); | ||
| 493 | } | ||
| 480 | } | 494 | } |
| 481 | 495 | ||
| 482 | function mouseReleaseEvent(event) { | 496 | function triggerChanged(side, value) { |
| 483 | // position | 497 | var pressed = (value != 0); |
| 484 | isMouseDown = false; | 498 | if (pressed) { |
| 499 | var position = barrelTips[side]; | ||
| 500 | var velocity = Vec3.multiply(BULLET_VELOCITY, Vec3.normalize(barrelVectors[side])); | ||
| 501 | shootBullet(position, velocity, false); | ||
| 502 | } | ||
| 485 | } | 503 | } |
| 486 | 504 | ||
| 487 | function mousePressEvent(event) { | 505 | function mousePressEvent(event) { |
| 488 | var clickedText = false; | 506 | var clickedOverlay = Overlays.getOverlayAtPoint({ |
| 489 | var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); | 507 | x: event.x, |
| 508 | y: event.y | ||
| 509 | }); | ||
| 490 | if (clickedOverlay == offButton) { | 510 | if (clickedOverlay == offButton) { |
| 491 | Script.stop(); | 511 | Script.stop(); |
| 492 | } else if (clickedOverlay == platformButton) { | 512 | } else if (clickedOverlay == platformButton) { |
| @@ -494,25 +514,37 @@ function mousePressEvent(event) { · 1 unmodified line | |||
| 494 | makePlatform(-9.8, 1.0, platformSize); | 514 | makePlatform(-9.8, 1.0, platformSize); |
| 495 | } else if (clickedOverlay == gridButton) { | 515 | } else if (clickedOverlay == gridButton) { |
| 496 | makeGrid("Box", 1.0, 3); | 516 | makeGrid("Box", 1.0, 3); |
| 497 | } | 517 | } |
| 498 | } | 518 | } |
| 499 | 519 | ||
| 500 | function scriptEnding() { | 520 | function scriptEnding() { |
| 501 | Overlays.deleteOverlay(reticle); | 521 | mapping.disable(); |
| 502 | toolBar.cleanup(); | 522 | toolBar.cleanup(); |
| 503 | Overlays.deleteOverlay(pointer[0]); | 523 | for (var i = 0; i < pointer.length; ++i) { |
| 504 | Overlays.deleteOverlay(pointer[1]); | 524 | Overlays.deleteOverlay(pointer[i]); |
| 525 | } | ||
| 505 | Overlays.deleteOverlay(text); | 526 | Overlays.deleteOverlay(text); |
| 506 | MyAvatar.detachOne(gunModel); | 527 | MyAvatar.detachOne(GUN_MODEL); |
| 507 | MyAvatar.detachOne(gunModel); | 528 | MyAvatar.detachOne(GUN_MODEL); |
| 508 | clearPose(); | 529 | clearPose(); |
| 509 | } | 530 | } |
| 510 | 531 | ||
| 532 | MyAvatar.attach(GUN_MODEL, "LeftHand", GUN_OFFSETS[0], GUN_ORIENTATIONS[0], 0.40); | ||
| 533 | MyAvatar.attach(GUN_MODEL, "RightHand", GUN_OFFSETS[1], GUN_ORIENTATIONS[1], 0.40); | ||
| 534 | |||
| 535 | // Give a bit of time to load before playing sound | ||
| 536 | Script.setTimeout(playLoadSound, 2000); | ||
| 537 | |||
| 538 | mapping.from(Controller.Standard.LT).hysteresis(0.1, 0.5).to(function(value) { | ||
| 539 | triggerChanged(0, value); | ||
| 540 | }); | ||
| 541 | |||
| 542 | mapping.from(Controller.Standard.RT).hysteresis(0.1, 0.5).to(function(value) { | ||
| 543 | triggerChanged(1, value); | ||
| 544 | }); | ||
| 545 | mapping.enable(); | ||
| 546 | |||
| 511 | Script.scriptEnding.connect(scriptEnding); | 547 | Script.scriptEnding.connect(scriptEnding); |
| 512 | Script.update.connect(update); | 548 | Script.update.connect(update); |
| 513 | Controller.mouseReleaseEvent.connect(mouseReleaseEvent); | ||
| 514 | Controller.mousePressEvent.connect(mousePressEvent); | 549 | Controller.mousePressEvent.connect(mousePressEvent); |
| 515 | Controller.keyPressEvent.connect(keyPressEvent); | 550 | Controller.keyPressEvent.connect(keyPressEvent); |
| 516 | |||
| 517 | |||
| 518 | |||
| @@ -1,303 +0,0 @@ | |||
| 1 | // | ||
| 2 | // hydraMove.js | ||
| 3 | // examples | ||
| 4 | // | ||
| 5 | // Created by Brad Hefta-Gaub on February 10, 2014 | ||
| 6 | // Updated by Philip Rosedale on September 8, 2014 | ||
| 7 | // | ||
| 8 | // Copyright 2014 High Fidelity, Inc. | ||
| 9 | // | ||
| 10 | // This is an example script that demonstrates use of the Controller and MyAvatar classes to implement | ||
| 11 | // avatar flying through the hydra/controller joysticks | ||
| 12 | // | ||
| 13 | // The joysticks (on hydra) will drive the avatar much like a playstation controller. | ||
| 14 | // | ||
| 15 | // Pressing the '4' or the 'FWD' button and moving/banking the hand will allow you to move and fly. | ||
| 16 | // | ||
| 17 | // Distributed under the Apache License, Version 2.0. | ||
| 18 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 19 | // | ||
| 20 | |||
| 21 | var damping = 0.9; | ||
| 22 | var position = { x: MyAvatar.position.x, y: MyAvatar.position.y, z: MyAvatar.position.z }; | ||
| 23 | var joysticksCaptured = false; | ||
| 24 | var THRUST_CONTROLLER = 0; | ||
| 25 | var VIEW_CONTROLLER = 1; | ||
| 26 | var INITIAL_THRUST_MULTIPLIER = 1.0; | ||
| 27 | var THRUST_INCREASE_RATE = 1.05; | ||
| 28 | var MAX_THRUST_MULTIPLIER = 75.0; | ||
| 29 | var thrustMultiplier = INITIAL_THRUST_MULTIPLIER; | ||
| 30 | var grabDelta = { x: 0, y: 0, z: 0}; | ||
| 31 | var grabStartPosition = { x: 0, y: 0, z: 0}; | ||
| 32 | var grabDeltaVelocity = { x: 0, y: 0, z: 0}; | ||
| 33 | var grabStartRotation = { x: 0, y: 0, z: 0, w: 1}; | ||
| 34 | var grabCurrentRotation = { x: 0, y: 0, z: 0, w: 1}; | ||
| 35 | var grabbingWithRightHand = false; | ||
| 36 | var wasGrabbingWithRightHand = false; | ||
| 37 | var grabbingWithLeftHand = false; | ||
| 38 | var wasGrabbingWithLeftHand = false; | ||
| 39 | |||
| 40 | var EPSILON = 0.000001; | ||
| 41 | var velocity = { x: 0, y: 0, z: 0}; | ||
| 42 | var THRUST_MAG_UP = 100.0; | ||
| 43 | var THRUST_MAG_DOWN = 100.0; | ||
| 44 | var THRUST_MAG_FWD = 150.0; | ||
| 45 | var THRUST_MAG_BACK = 100.0; | ||
| 46 | var THRUST_MAG_LATERAL = 150.0; | ||
| 47 | var THRUST_JUMP = 120.0; | ||
| 48 | |||
| 49 | var YAW_MAG = 100.0; | ||
| 50 | var PITCH_MAG = 100.0; | ||
| 51 | var THRUST_MAG_HAND_JETS = THRUST_MAG_FWD; | ||
| 52 | var JOYSTICK_YAW_MAG = YAW_MAG; | ||
| 53 | var JOYSTICK_PITCH_MAG = PITCH_MAG * 0.5; | ||
| 54 | |||
| 55 | |||
| 56 | var LEFT_PALM = 0; | ||
| 57 | var LEFT_BUTTON_4 = 4; | ||
| 58 | var LEFT_BUTTON_FWD = 5; | ||
| 59 | var RIGHT_PALM = 2; | ||
| 60 | var RIGHT_BUTTON_4 = 10; | ||
| 61 | var RIGHT_BUTTON_FWD = 11; | ||
| 62 | |||
| 63 | |||
| 64 | |||
| 65 | function printVector(text, v, decimals) { | ||
| 66 | print(text + " " + v.x.toFixed(decimals) + ", " + v.y.toFixed(decimals) + ", " + v.z.toFixed(decimals)); | ||
| 67 | } | ||
| 68 | |||
| 69 | var debug = false; | ||
| 70 | var RED_COLOR = { red: 255, green: 0, blue: 0 }; | ||
| 71 | var GRAY_COLOR = { red: 25, green: 25, blue: 25 }; | ||
| 72 | var defaultPosition = { x: 0, y: 0, z: 0}; | ||
| 73 | var RADIUS = 0.05; | ||
| 74 | var greenSphere = -1; | ||
| 75 | var redSphere = -1; | ||
| 76 | |||
| 77 | function createDebugOverlay() { | ||
| 78 | |||
| 79 | if (greenSphere == -1) { | ||
| 80 | greenSphere = Overlays.addOverlay("sphere", { | ||
| 81 | position: defaultPosition, | ||
| 82 | size: RADIUS, | ||
| 83 | color: GRAY_COLOR, | ||
| 84 | alpha: 0.75, | ||
| 85 | visible: true, | ||
| 86 | solid: true, | ||
| 87 | anchor: "MyAvatar" | ||
| 88 | }); | ||
| 89 | redSphere = Overlays.addOverlay("sphere", { | ||
| 90 | position: defaultPosition, | ||
| 91 | size: RADIUS, | ||
| 92 | color: RED_COLOR, | ||
| 93 | alpha: 0.5, | ||
| 94 | visible: true, | ||
| 95 | solid: true, | ||
| 96 | anchor: "MyAvatar" | ||
| 97 | }); | ||
| 98 | } | ||
| 99 | } | ||
| 100 | |||
| 101 | function destroyDebugOverlay() { | ||
| 102 | if (greenSphere != -1) { | ||
| 103 | Overlays.deleteOverlay(greenSphere); | ||
| 104 | Overlays.deleteOverlay(redSphere); | ||
| 105 | greenSphere = -1; | ||
| 106 | redSphere = -1; | ||
| 107 | } | ||
| 108 | } | ||
| 109 | |||
| 110 | function displayDebug() { | ||
| 111 | if (!(grabbingWithRightHand || grabbingWithLeftHand)) { | ||
| 112 | if (greenSphere != -1) { | ||
| 113 | destroyDebugOverlay(); | ||
| 114 | } | ||
| 115 | } else { | ||
| 116 | // update debug indicator | ||
| 117 | if (greenSphere == -1) { | ||
| 118 | createDebugOverlay(); | ||
| 119 | } | ||
| 120 | |||
| 121 | var displayOffset = { x:0, y:0.5, z:-0.5 }; | ||
| 122 | |||
| 123 | Overlays.editOverlay(greenSphere, { position: Vec3.sum(grabStartPosition, displayOffset) } ); | ||
| 124 | Overlays.editOverlay(redSphere, { position: Vec3.sum(Vec3.sum(grabStartPosition, grabDelta), displayOffset), size: RADIUS + (0.25 * Vec3.length(grabDelta)) } ); | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | function getJoystickPosition(palm) { | ||
| 129 | // returns CONTROLLER_ID position in avatar local frame | ||
| 130 | var invRotation = Quat.inverse(MyAvatar.orientation); | ||
| 131 | var palmWorld = Controller.getSpatialControlPosition(palm); | ||
| 132 | var palmRelative = Vec3.subtract(palmWorld, MyAvatar.position); | ||
| 133 | var palmLocal = Vec3.multiplyQbyV(invRotation, palmRelative); | ||
| 134 | return palmLocal; | ||
| 135 | } | ||
| 136 | |||
| 137 | // Used by handleGrabBehavior() for managing the grab position changes | ||
| 138 | function getAndResetGrabDelta() { | ||
| 139 | var HAND_GRAB_SCALE_DISTANCE = 2.0; | ||
| 140 | var delta = Vec3.multiply(grabDelta, (MyAvatar.scale * HAND_GRAB_SCALE_DISTANCE)); | ||
| 141 | grabDelta = { x: 0, y: 0, z: 0}; | ||
| 142 | var avatarRotation = MyAvatar.orientation; | ||
| 143 | var result = Vec3.multiplyQbyV(avatarRotation, Vec3.multiply(delta, -1)); | ||
| 144 | return result; | ||
| 145 | } | ||
| 146 | |||
| 147 | function getGrabRotation() { | ||
| 148 | var quatDiff = Quat.multiply(grabCurrentRotation, Quat.inverse(grabStartRotation)); | ||
| 149 | return quatDiff; | ||
| 150 | } | ||
| 151 | |||
| 152 | // When move button is pressed, process results | ||
| 153 | function handleGrabBehavior(deltaTime) { | ||
| 154 | // check for and handle grab behaviors | ||
| 155 | grabbingWithRightHand = Controller.isButtonPressed(RIGHT_BUTTON_4); | ||
| 156 | grabbingWithLeftHand = Controller.isButtonPressed(LEFT_BUTTON_4); | ||
| 157 | stoppedGrabbingWithLeftHand = false; | ||
| 158 | stoppedGrabbingWithRightHand = false; | ||
| 159 | |||
| 160 | if (grabbingWithRightHand && !wasGrabbingWithRightHand) { | ||
| 161 | // Just starting grab, capture starting rotation | ||
| 162 | grabStartRotation = Controller.getSpatialControlRawRotation(RIGHT_PALM); | ||
| 163 | grabStartPosition = getJoystickPosition(RIGHT_PALM); | ||
| 164 | if (debug) printVector("start position", grabStartPosition, 3); | ||
| 165 | } | ||
| 166 | if (grabbingWithRightHand) { | ||
| 167 | grabDelta = Vec3.subtract(getJoystickPosition(RIGHT_PALM), grabStartPosition); | ||
| 168 | grabCurrentRotation = Controller.getSpatialControlRawRotation(RIGHT_PALM); | ||
| 169 | } | ||
| 170 | if (!grabbingWithRightHand && wasGrabbingWithRightHand) { | ||
| 171 | // Just ending grab, capture velocity | ||
| 172 | grabDeltaVelocity = Controller.getSpatialControlVelocity(RIGHT_PALM); | ||
| 173 | stoppedGrabbingWithRightHand = true; | ||
| 174 | } | ||
| 175 | |||
| 176 | if (grabbingWithLeftHand && !wasGrabbingWithLeftHand) { | ||
| 177 | // Just starting grab, capture starting rotation | ||
| 178 | grabStartRotation = Controller.getSpatialControlRawRotation(LEFT_PALM); | ||
| 179 | grabStartPosition = getJoystickPosition(LEFT_PALM); | ||
| 180 | if (debug) printVector("start position", grabStartPosition, 3); | ||
| 181 | } | ||
| 182 | |||
| 183 | if (grabbingWithLeftHand) { | ||
| 184 | grabDelta = Vec3.subtract(getJoystickPosition(LEFT_PALM), grabStartPosition); | ||
| 185 | grabCurrentRotation = Controller.getSpatialControlRawRotation(LEFT_PALM); | ||
| 186 | } | ||
| 187 | if (!grabbingWithLeftHand && wasGrabbingWithLeftHand) { | ||
| 188 | // Just ending grab, capture velocity | ||
| 189 | grabDeltaVelocity = Controller.getSpatialControlVelocity(LEFT_PALM); | ||
| 190 | stoppedGrabbingWithLeftHand = true; | ||
| 191 | } | ||
| 192 | |||
| 193 | grabbing = grabbingWithRightHand || grabbingWithLeftHand; | ||
| 194 | stoppedGrabbing = stoppedGrabbingWithRightHand || stoppedGrabbingWithLeftHand; | ||
| 195 | |||
| 196 | if (grabbing) { | ||
| 197 | |||
| 198 | var headOrientation = MyAvatar.headOrientation; | ||
| 199 | var front = Quat.getFront(headOrientation); | ||
| 200 | var right = Quat.getRight(headOrientation); | ||
| 201 | var up = Quat.getUp(headOrientation); | ||
| 202 | |||
| 203 | if (debug) { | ||
| 204 | printVector("grabDelta: ", grabDelta, 3); | ||
| 205 | } | ||
| 206 | |||
| 207 | var thrust = Vec3.multiply(grabDelta, Math.abs(Vec3.length(grabDelta))); | ||
| 208 | |||
| 209 | var THRUST_GRAB_SCALING = 100000.0; | ||
| 210 | |||
| 211 | var thrustFront = Vec3.multiply(front, MyAvatar.scale * -thrust.z * THRUST_GRAB_SCALING * deltaTime); | ||
| 212 | MyAvatar.addThrust(thrustFront); | ||
| 213 | var thrustRight = Vec3.multiply(right, MyAvatar.scale * thrust.x * THRUST_GRAB_SCALING * deltaTime); | ||
| 214 | MyAvatar.addThrust(thrustRight); | ||
| 215 | var thrustUp = Vec3.multiply(up, MyAvatar.scale * thrust.y * THRUST_GRAB_SCALING * deltaTime); | ||
| 216 | MyAvatar.addThrust(thrustUp); | ||
| 217 | |||
| 218 | // add some rotation... | ||
| 219 | var deltaRotation = getGrabRotation(); | ||
| 220 | var PITCH_SCALING = 2.5; | ||
| 221 | var PITCH_DEAD_ZONE = 2.0; | ||
| 222 | var YAW_SCALING = 2.5; | ||
| 223 | var ROLL_SCALING = 2.0; | ||
| 224 | |||
| 225 | var euler = Quat.safeEulerAngles(deltaRotation); | ||
| 226 | |||
| 227 | // Adjust body yaw by roll from controller | ||
| 228 | var orientation = Quat.multiply(Quat.angleAxis(((euler.y * YAW_SCALING) + | ||
| 229 | (euler.z * ROLL_SCALING)) * deltaTime, {x:0, y: 1, z:0}), MyAvatar.orientation); | ||
| 230 | MyAvatar.orientation = orientation; | ||
| 231 | |||
| 232 | // Adjust head pitch from controller | ||
| 233 | var pitch = 0.0; | ||
| 234 | if (Math.abs(euler.x) > PITCH_DEAD_ZONE) { | ||
| 235 | pitch = (euler.x < 0.0) ? (euler.x + PITCH_DEAD_ZONE) : (euler.x - PITCH_DEAD_ZONE); | ||
| 236 | } | ||
| 237 | MyAvatar.headPitch = MyAvatar.headPitch + (pitch * PITCH_SCALING * deltaTime); | ||
| 238 | |||
| 239 | // TODO: Add some camera roll proportional to the rate of turn (so it feels like an airplane or roller coaster) | ||
| 240 | |||
| 241 | } | ||
| 242 | |||
| 243 | wasGrabbingWithRightHand = grabbingWithRightHand; | ||
| 244 | wasGrabbingWithLeftHand = grabbingWithLeftHand; | ||
| 245 | } | ||
| 246 | |||
| 247 | // Update for joysticks and move button | ||
| 248 | var THRUST_DEAD_ZONE = 0.1; | ||
| 249 | var ROTATE_DEAD_ZONE = 0.1; | ||
| 250 | function flyWithHydra(deltaTime) { | ||
| 251 | var thrustJoystickPosition = Controller.getJoystickPosition(THRUST_CONTROLLER); | ||
| 252 | |||
| 253 | if (Math.abs(thrustJoystickPosition.x) > THRUST_DEAD_ZONE || Math.abs(thrustJoystickPosition.y) > THRUST_DEAD_ZONE) { | ||
| 254 | if (thrustMultiplier < MAX_THRUST_MULTIPLIER) { | ||
| 255 | thrustMultiplier *= 1 + (deltaTime * THRUST_INCREASE_RATE); | ||
| 256 | } | ||
| 257 | var headOrientation = MyAvatar.headOrientation; | ||
| 258 | |||
| 259 | var front = Quat.getFront(headOrientation); | ||
| 260 | var right = Quat.getRight(headOrientation); | ||
| 261 | var up = Quat.getUp(headOrientation); | ||
| 262 | |||
| 263 | var thrustFront = Vec3.multiply(front, MyAvatar.scale * THRUST_MAG_HAND_JETS * | ||
| 264 | thrustJoystickPosition.y * thrustMultiplier * deltaTime); | ||
| 265 | MyAvatar.addThrust(thrustFront); | ||
| 266 | var thrustRight = Vec3.multiply(right, MyAvatar.scale * THRUST_MAG_HAND_JETS * | ||
| 267 | thrustJoystickPosition.x * thrustMultiplier * deltaTime); | ||
| 268 | MyAvatar.addThrust(thrustRight); | ||
| 269 | } else { | ||
| 270 | thrustMultiplier = INITIAL_THRUST_MULTIPLIER; | ||
| 271 | } | ||
| 272 | |||
| 273 | // View Controller | ||
| 274 | var viewJoystickPosition = Controller.getJoystickPosition(VIEW_CONTROLLER); | ||
| 275 | if (Math.abs(viewJoystickPosition.x) > ROTATE_DEAD_ZONE || Math.abs(viewJoystickPosition.y) > ROTATE_DEAD_ZONE) { | ||
| 276 | |||
| 277 | // change the body yaw based on our x controller | ||
| 278 | var orientation = MyAvatar.orientation; | ||
| 279 | var deltaOrientation = Quat.fromPitchYawRollDegrees(0, (-1 * viewJoystickPosition.x * JOYSTICK_YAW_MAG * deltaTime), 0); | ||
| 280 | MyAvatar.orientation = Quat.multiply(orientation, deltaOrientation); | ||
| 281 | |||
| 282 | // change the headPitch based on our x controller | ||
| 283 | var newPitch = MyAvatar.headPitch + (viewJoystickPosition.y * JOYSTICK_PITCH_MAG * deltaTime); | ||
| 284 | MyAvatar.headPitch = newPitch; | ||
| 285 | } | ||
| 286 | handleGrabBehavior(deltaTime); | ||
| 287 | displayDebug(); | ||
| 288 | |||
| 289 | } | ||
| 290 | |||
| 291 | Script.update.connect(flyWithHydra); | ||
| 292 | Controller.captureJoystick(THRUST_CONTROLLER); | ||
| 293 | Controller.captureJoystick(VIEW_CONTROLLER); | ||
| 294 | |||
| 295 | // Map keyPress and mouse move events to our callbacks | ||
| 296 | function scriptEnding() { | ||
| 297 | // re-enabled the standard application for touch events | ||
| 298 | Controller.releaseJoystick(THRUST_CONTROLLER); | ||
| 299 | Controller.releaseJoystick(VIEW_CONTROLLER); | ||
| 300 | } | ||
| 301 | Script.scriptEnding.connect(scriptEnding); | ||
| 302 | |||
| 303 | |||
| @@ -20,10 +20,11 @@ var BALL_SIZE = 0.08; · 19 unmodified lines | |||
| 20 | var PADDLE_SIZE = 0.20; | 20 | var PADDLE_SIZE = 0.20; |
| 21 | var PADDLE_THICKNESS = 0.06; | 21 | var PADDLE_THICKNESS = 0.06; |
| 22 | var PADDLE_COLOR = { red: 184, green: 134, blue: 11 }; | 22 | var PADDLE_COLOR = { red: 184, green: 134, blue: 11 }; |
| 23 | var BALL_COLOR = { red: 255, green: 0, blue: 0 }; | 23 | var BALL_COLOR = { red: 0, green: 255, blue: 0 }; |
| 24 | var LINE_COLOR = { red: 255, green: 255, blue: 0 }; | 24 | var LINE_COLOR = { red: 255, green: 255, blue: 0 }; |
| 25 | var PADDLE_BOX_OFFSET = { x: 0.05, y: 0.0, z: 0.0 }; | 25 | var PADDLE_BOX_OFFSET = { x: 0.05, y: 0.0, z: 0.0 }; |
| 26 | 26 | ||
| 27 | //probably we need to fix these initial values (offsets and orientation) | ||
| 27 | var HOLD_POSITION_LEFT_OFFSET = { x: -0.15, y: 0.05, z: -0.05 }; | 28 | var HOLD_POSITION_LEFT_OFFSET = { x: -0.15, y: 0.05, z: -0.05 }; |
| 28 | var HOLD_POSITION_RIGHT_OFFSET = { x: -0.15, y: 0.05, z: 0.05 }; | 29 | var HOLD_POSITION_RIGHT_OFFSET = { x: -0.15, y: 0.05, z: 0.05 }; |
| 29 | var PADDLE_ORIENTATION = Quat.fromPitchYawRollDegrees(0,0,0); | 30 | var PADDLE_ORIENTATION = Quat.fromPitchYawRollDegrees(0,0,0); |
| @@ -32,18 +33,7 @@ var SPRING_FORCE = 15.0; · 2 unmodified lines | |||
| 32 | var lastSoundTime = 0; | 33 | var lastSoundTime = 0; |
| 33 | var gameOn = false; | 34 | var gameOn = false; |
| 34 | var leftHanded = true; | 35 | var leftHanded = true; |
| 35 | var controllerID; | ||
| 36 | 36 | ||
| 37 | |||
| 38 | function setControllerID() { | ||
| 39 | if (leftHanded) { | ||
| 40 | controllerID = 1; | ||
| 41 | } else { | ||
| 42 | controllerID = 3; | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | setControllerID(); | ||
| 47 | Menu.addMenu("PaddleBall"); | 37 | Menu.addMenu("PaddleBall"); |
| 48 | Menu.addMenuItem({ menuName: "PaddleBall", menuItemName: "Left-Handed", isCheckable: true, isChecked: true }); | 38 | Menu.addMenuItem({ menuName: "PaddleBall", menuItemName: "Left-Handed", isCheckable: true, isChecked: true }); |
| 49 | 39 | ||
| @@ -63,7 +53,7 @@ var ball, paddle, paddleModel, line; · 13 unmodified lines | |||
| 63 | function createEntities() { | 53 | function createEntities() { |
| 64 | ball = Entities.addEntity( | 54 | ball = Entities.addEntity( |
| 65 | { type: "Sphere", | 55 | { type: "Sphere", |
| 66 | position: Controller.getSpatialControlPosition(controllerID), | 56 | position: leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation, |
| 67 | dimensions: { x: BALL_SIZE, y: BALL_SIZE, z: BALL_SIZE }, | 57 | dimensions: { x: BALL_SIZE, y: BALL_SIZE, z: BALL_SIZE }, |
| 68 | color: BALL_COLOR, | 58 | color: BALL_COLOR, |
| 69 | gravity: { x: 0, y: GRAVITY, z: 0 }, | 59 | gravity: { x: 0, y: GRAVITY, z: 0 }, |
| @@ -73,28 +63,28 @@ function createEntities() { · 3 unmodified lines | |||
| 73 | 63 | ||
| 74 | paddle = Entities.addEntity( | 64 | paddle = Entities.addEntity( |
| 75 | { type: "Box", | 65 | { type: "Box", |
| 76 | position: Controller.getSpatialControlPosition(controllerID), | 66 | position: leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation, |
| 77 | dimensions: { x: PADDLE_SIZE, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 0.80 }, | 67 | dimensions: { x: PADDLE_SIZE, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 0.80 }, |
| 78 | color: PADDLE_COLOR, | 68 | color: PADDLE_COLOR, |
| 79 | gravity: { x: 0, y: 0, z: 0 }, | 69 | gravity: { x: 0, y: 0, z: 0 }, |
| 80 | ignoreCollisions: false, | 70 | ignoreCollisions: false, |
| 81 | damping: 0.10, | 71 | damping: 0.10, |
| 82 | visible: false, | 72 | visible: false, |
| 83 | rotation: Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)), | 73 | rotation : leftHanded ? MyAvatar.leftHandPose.rotation : MyAvatar.rightHandPose.rotation, |
| 84 | collisionsWillMove: false }); | 74 | collisionsWillMove: false }); |
| 85 | 75 | ||
| 86 | modelURL = "http://public.highfidelity.io/models/attachments/pong_paddle.fbx"; | 76 | modelURL = "http://public.highfidelity.io/models/attachments/pong_paddle.fbx"; |
| 87 | paddleModel = Entities.addEntity( | 77 | paddleModel = Entities.addEntity( |
| 88 | { type: "Model", | 78 | { type: "Model", |
| 89 | position: Vec3.sum(Controller.getSpatialControlPosition(controllerID), PADDLE_BOX_OFFSET), | 79 | position: Vec3.sum( leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation, PADDLE_BOX_OFFSET), |
| 90 | dimensions: { x: PADDLE_SIZE * 1.5, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 1.25 }, | 80 | dimensions: { x: PADDLE_SIZE * 1.5, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 1.25 }, |
| 91 | color: PADDLE_COLOR, | 81 | color: PADDLE_COLOR, |
| 92 | gravity: { x: 0, y: 0, z: 0 }, | 82 | gravity: { x: 0, y: 0, z: 0 }, |
| 93 | ignoreCollisions: true, | 83 | ignoreCollisions: true, |
| 94 | modelURL: modelURL, | 84 | modelURL: modelURL, |
| 95 | damping: 0.10, | 85 | damping: 0.10, |
| 96 | rotation: Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)), | 86 | rotation : leftHanded ? MyAvatar.leftHandPose.rotation : MyAvatar.rightHandPose.rotation, |
| 97 | collisionsWillMove: false }); | 87 | collisionsWillMove: false }); |
| 98 | 88 | ||
| 99 | line = Overlays.addOverlay("line3d", { | 89 | line = Overlays.addOverlay("line3d", { |
| 100 | start: { x: 0, y: 0, z: 0 }, | 90 | start: { x: 0, y: 0, z: 0 }, |
| @@ -118,7 +108,7 @@ function deleteEntities() { · 17 unmodified lines | |||
| 118 | } | 108 | } |
| 119 | 109 | ||
| 120 | function update(deltaTime) { | 110 | function update(deltaTime) { |
| 121 | var palmPosition = Controller.getSpatialControlPosition(controllerID); | 111 | var palmPosition = leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation; |
| 122 | var controllerActive = (Vec3.length(palmPosition) > 0); | 112 | var controllerActive = (Vec3.length(palmPosition) > 0); |
| 123 | 113 | ||
| 124 | if (!gameOn && controllerActive) { | 114 | if (!gameOn && controllerActive) { |
| @@ -133,8 +123,8 @@ function update(deltaTime) { · 8 unmodified lines | |||
| 133 | } | 123 | } |
| 134 | 124 | ||
| 135 | var paddleOrientation = leftHanded ? PADDLE_ORIENTATION : Quat.multiply(PADDLE_ORIENTATION, Quat.fromPitchYawRollDegrees(0, 180, 0)); | 125 | var paddleOrientation = leftHanded ? PADDLE_ORIENTATION : Quat.multiply(PADDLE_ORIENTATION, Quat.fromPitchYawRollDegrees(0, 180, 0)); |
| 136 | var paddleWorldOrientation = Quat.multiply(Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)), paddleOrientation); | 126 | var paddleWorldOrientation = Quat.multiply(leftHanded ? MyAvatar.leftHandPose.rotation : MyAvatar.rightHandPose.rotation, paddleOrientation); |
| 137 | var holdPosition = Vec3.sum(leftHanded ? MyAvatar.getLeftPalmPosition() : MyAvatar.getRightPalmPosition(), | 127 | var holdPosition = Vec3.sum(leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation, |
| 138 | Vec3.multiplyQbyV(paddleWorldOrientation, leftHanded ? HOLD_POSITION_LEFT_OFFSET : HOLD_POSITION_RIGHT_OFFSET )); | 128 | Vec3.multiplyQbyV(paddleWorldOrientation, leftHanded ? HOLD_POSITION_LEFT_OFFSET : HOLD_POSITION_RIGHT_OFFSET )); |
| 139 | 129 | ||
| 140 | var props = Entities.getEntityProperties(ball); | 130 | var props = Entities.getEntityProperties(ball); |
| @@ -146,10 +136,10 @@ function update(deltaTime) { · 5 unmodified lines | |||
| 146 | Entities.editEntity(ball, { velocity: ballVelocity }); | 136 | Entities.editEntity(ball, { velocity: ballVelocity }); |
| 147 | Overlays.editOverlay(line, { start: props.position, end: holdPosition }); | 137 | Overlays.editOverlay(line, { start: props.position, end: holdPosition }); |
| 148 | Entities.editEntity(paddle, { position: holdPosition, | 138 | Entities.editEntity(paddle, { position: holdPosition, |
| 149 | velocity: Controller.getSpatialControlVelocity(controllerID), | 139 | velocity: leftHanded ? MyAvatar.leftHandPose.velocity : MyAvatar.rightHandPose.velocity, |
| 150 | rotation: paddleWorldOrientation }); | 140 | rotation: paddleWorldOrientation }); |
| 151 | Entities.editEntity(paddleModel, { position: Vec3.sum(holdPosition, Vec3.multiplyQbyV(paddleWorldOrientation, PADDLE_BOX_OFFSET)), | 141 | Entities.editEntity(paddleModel, { position: Vec3.sum(holdPosition, Vec3.multiplyQbyV(paddleWorldOrientation, PADDLE_BOX_OFFSET)), |
| 152 | velocity: Controller.getSpatialControlVelocity(controllerID), | 142 | velocity: leftHanded ? MyAvatar.leftHandPose.velocity : MyAvatar.rightHandPose.velocity, |
| 153 | rotation: paddleWorldOrientation }); | 143 | rotation: paddleWorldOrientation }); |
| 154 | 144 | ||
| 155 | } | 145 | } |
| @@ -182,7 +172,6 @@ function menuItemEvent(menuItem) { · 26 unmodified lines | |||
| 182 | leftHanded = Menu.isOptionChecked("Left-Handed"); | 172 | leftHanded = Menu.isOptionChecked("Left-Handed"); |
| 183 | } | 173 | } |
| 184 | if ((leftHanded != oldHanded) && gameOn) { | 174 | if ((leftHanded != oldHanded) && gameOn) { |
| 185 | setControllerID(); | ||
| 186 | deleteEntities(); | 175 | deleteEntities(); |
| 187 | createEntities(); | 176 | createEntities(); |
| 188 | } | 177 | } |
| @@ -19,14 +19,7 @@ HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; · 18 unmodified lines | |||
| 19 | 19 | ||
| 20 | // maybe we should make these constants... | 20 | // maybe we should make these constants... |
| 21 | var LEFT_PALM = 0; | 21 | var LEFT_PALM = 0; |
| 22 | var LEFT_TIP = 1; | ||
| 23 | var LEFT_BUTTON_FWD = 5; | ||
| 24 | var LEFT_BUTTON_3 = 3; | ||
| 25 | |||
| 26 | var RIGHT_PALM = 2; | 22 | var RIGHT_PALM = 2; |
| 27 | var RIGHT_TIP = 3; | ||
| 28 | var RIGHT_BUTTON_FWD = 11; | ||
| 29 | var RIGHT_BUTTON_3 = 9; | ||
| 30 | 23 | ||
| 31 | var BALL_RADIUS = 0.08; | 24 | var BALL_RADIUS = 0.08; |
| 32 | var GRAVITY_STRENGTH = 3.0; | 25 | var GRAVITY_STRENGTH = 3.0; |
| @@ -69,9 +62,6 @@ function getBallHoldPosition(whichSide) { · 36 unmodified lines | |||
| 69 | } | 62 | } |
| 70 | 63 | ||
| 71 | function checkControllerSide(whichSide) { | 64 | function checkControllerSide(whichSide) { |
| 72 | var BUTTON_FWD; | ||
| 73 | var BUTTON_3; | ||
| 74 | var TRIGGER; | ||
| 75 | var palmPosition; | 65 | var palmPosition; |
| 76 | var palmRotation; | 66 | var palmRotation; |
| 77 | var ballAlreadyInHand; | 67 | var ballAlreadyInHand; |
| @@ -79,35 +69,35 @@ function checkControllerSide(whichSide) { · 1 unmodified line | |||
| 79 | var linearVelocity; | 69 | var linearVelocity; |
| 80 | var angularVelocity; | 70 | var angularVelocity; |
| 81 | var AVERAGE_FACTOR = 0.33; | 71 | var AVERAGE_FACTOR = 0.33; |
| 82 | 72 | var grabButtonPressed; | |
| 73 | |||
| 83 | if (whichSide == LEFT_PALM) { | 74 | if (whichSide == LEFT_PALM) { |
| 84 | BUTTON_FWD = LEFT_BUTTON_FWD; | 75 | palmPosition = MyAvatar.leftHandPose.translation; |
| 85 | BUTTON_3 = LEFT_BUTTON_3; | 76 | palmRotation = MyAvatar.leftHandPose.rotation; |
| 86 | TRIGGER = 0; | ||
| 87 | palmPosition = Controller.getSpatialControlPosition(LEFT_PALM); | ||
| 88 | palmRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(LEFT_PALM)); | ||
| 89 | ballAlreadyInHand = leftBallAlreadyInHand; | 77 | ballAlreadyInHand = leftBallAlreadyInHand; |
| 90 | handMessage = "LEFT"; | 78 | handMessage = "LEFT"; |
| 91 | averageLinearVelocity[0] = Vec3.sum(Vec3.multiply(AVERAGE_FACTOR, Controller.getSpatialControlVelocity(LEFT_TIP)), | 79 | averageLinearVelocity[0] = Vec3.sum(Vec3.multiply(AVERAGE_FACTOR, MyAvatar.leftHandTipPose.velocity), |
| 92 | Vec3.multiply(1.0 - AVERAGE_FACTOR, averageLinearVelocity[0])); | 80 | Vec3.multiply(1.0 - AVERAGE_FACTOR, averageLinearVelocity[0])); |
| 81 | |||
| 93 | linearVelocity = averageLinearVelocity[0]; | 82 | linearVelocity = averageLinearVelocity[0]; |
| 94 | angularVelocity = Vec3.multiplyQbyV(MyAvatar.orientation, Controller.getSpatialControlRawAngularVelocity(LEFT_TIP)); | 83 | angularVelocity = MyAvatar.leftHandTipPose.angularVelocity; |
| 84 | grabButtonPressed = (Controller.getValue(Controller.Standard.LT) > 0.5); | ||
| 85 | |||
| 95 | } else { | 86 | } else { |
| 96 | BUTTON_FWD = RIGHT_BUTTON_FWD; | 87 | palmPosition = MyAvatar.rightHandPose.translation; |
| 97 | BUTTON_3 = RIGHT_BUTTON_3; | 88 | palmRotation = MyAvatar.rightHandPose.rotation; |
| 98 | TRIGGER = 1; | ||
| 99 | palmPosition = Controller.getSpatialControlPosition(RIGHT_PALM); | ||
| 100 | palmRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(RIGHT_PALM)); | ||
| 101 | ballAlreadyInHand = rightBallAlreadyInHand; | 89 | ballAlreadyInHand = rightBallAlreadyInHand; |
| 102 | averageLinearVelocity[1] = Vec3.sum(Vec3.multiply(AVERAGE_FACTOR, Controller.getSpatialControlVelocity(RIGHT_TIP)), | 90 | averageLinearVelocity[1] = Vec3.sum(Vec3.multiply(AVERAGE_FACTOR, MyAvatar.rightHandTipPose.velocity), |
| 103 | Vec3.multiply(1.0 - AVERAGE_FACTOR, averageLinearVelocity[1])); | 91 | Vec3.multiply(1.0 - AVERAGE_FACTOR, averageLinearVelocity[1])); |
| 92 | |||
| 104 | linearVelocity = averageLinearVelocity[1]; | 93 | linearVelocity = averageLinearVelocity[1]; |
| 105 | angularVelocity = Vec3.multiplyQbyV(MyAvatar.orientation, Controller.getSpatialControlRawAngularVelocity(RIGHT_TIP)); | 94 | angularVelocity = MyAvatar.rightHandTipPose.angularVelocity; |
| 106 | handMessage = "RIGHT"; | 95 | handMessage = "RIGHT"; |
| 96 | grabButtonPressed = (Controller.getValue(Controller.Standard.RT) > 0.5); | ||
| 97 | |||
| 107 | } | 98 | } |
| 108 | 99 | ||
| 109 | var grabButtonPressed = (Controller.isButtonPressed(BUTTON_FWD) || Controller.isButtonPressed(BUTTON_3) || (Controller.getTriggerValue(TRIGGER) > 0.5)); | 100 | |
| 110 | |||
| 111 | // If I don't currently have a ball in my hand, then try to catch closest one | 101 | // If I don't currently have a ball in my hand, then try to catch closest one |
| 112 | if (!ballAlreadyInHand && grabButtonPressed) { | 102 | if (!ballAlreadyInHand && grabButtonPressed) { |
| 113 | var closestEntity = Entities.findClosestEntity(palmPosition, targetRadius); | 103 | var closestEntity = Entities.findClosestEntity(palmPosition, targetRadius); |
| @@ -187,10 +177,8 @@ function checkControllerSide(whichSide) { · 73 unmodified lines | |||
| 187 | if (ballAlreadyInHand) { | 177 | if (ballAlreadyInHand) { |
| 188 | if (whichSide == LEFT_PALM) { | 178 | if (whichSide == LEFT_PALM) { |
| 189 | handEntity = leftHandEntity; | 179 | handEntity = leftHandEntity; |
| 190 | whichTip = LEFT_TIP; | ||
| 191 | } else { | 180 | } else { |
| 192 | handEntity = rightHandEntity; | 181 | handEntity = rightHandEntity; |
| 193 | whichTip = RIGHT_TIP; | ||
| 194 | } | 182 | } |
| 195 | 183 | ||
| 196 | // If holding the ball keep it in the palm | 184 | // If holding the ball keep it in the palm |
| @@ -231,22 +219,10 @@ function checkControllerSide(whichSide) { · 34 unmodified lines | |||
| 231 | } | 219 | } |
| 232 | } | 220 | } |
| 233 | } | 221 | } |
| 234 | |||
| 235 | |||
| 236 | function checkController(deltaTime) { | 222 | function checkController(deltaTime) { |
| 237 | var numberOfButtons = Controller.getNumberOfButtons(); | ||
| 238 | var numberOfTriggers = Controller.getNumberOfTriggers(); | ||
| 239 | var numberOfSpatialControls = Controller.getNumberOfSpatialControls(); | ||
| 240 | var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers; | ||
| 241 | |||
| 242 | // this is expected for hydras | ||
| 243 | if (!(numberOfButtons==12 && numberOfTriggers == 2 && controllersPerTrigger == 2)) { | ||
| 244 | debugPrint("total buttons = " + numberOfButtons + ", Triggers = " + numberOfTriggers + ", controllers/trigger = " + controllersPerTrigger); | ||
| 245 | return; // bail if no hydra | ||
| 246 | } | ||
| 247 | 223 | ||
| 248 | checkControllerSide(LEFT_PALM); | 224 | checkControllerSide(LEFT_PALM); |
| 249 | checkControllerSide(RIGHT_PALM); | 225 | checkControllerSide(RIGHT_PALM); |
| 250 | } | 226 | } |
| 251 | 227 | ||
| 252 | 228 | ||
| @@ -10,25 +10,19 @@ · 9 unmodified lines | |||
| 10 | // Distributed under the Apache License, Version 2.0. | 10 | // Distributed under the Apache License, Version 2.0. |
| 11 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | 11 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html |
| 12 | // | 12 | // |
| 13 | |||
| 14 | // initialize our triggers | 13 | // initialize our triggers |
| 15 | var triggerPulled = new Array(); | 14 | var triggerPulled = new Array(); |
| 16 | var numberOfTriggers = Controller.getNumberOfTriggers(); | 15 | var NUMBER_OF_TRIGGERS = 2; |
| 17 | for (t = 0; t < numberOfTriggers; t++) { | 16 | for (t = 0; t < NUMBER_OF_TRIGGERS; t++) { |
| 18 | triggerPulled[t] = false; | 17 | triggerPulled[t] = false; |
| 19 | } | 18 | } |
| 20 | 19 | var triggers = new Array(); | |
| 20 | triggers[0] = Controller.Standard.LT; | ||
| 21 | triggers[1] = Controller.Standard.RT; | ||
| 21 | function checkController(deltaTime) { | 22 | function checkController(deltaTime) { |
| 22 | var numberOfTriggers = Controller.getNumberOfTriggers(); | ||
| 23 | var numberOfSpatialControls = Controller.getNumberOfSpatialControls(); | ||
| 24 | var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers; | ||
| 25 | var triggerToggled = false; | 23 | var triggerToggled = false; |
| 26 | 24 | for (var t = 0; t < NUMBER_OF_TRIGGERS; t++) { | |
| 27 | // this is expected for hydras | 25 | var triggerValue = Controller.getValue(triggers[t]); |
| 28 | if (numberOfTriggers == 2 && controllersPerTrigger == 2) { | ||
| 29 | for (var t = 0; t < numberOfTriggers; t++) { | ||
| 30 | var triggerValue = Controller.getTriggerValue(t); | ||
| 31 | |||
| 32 | if (triggerPulled[t]) { | 26 | if (triggerPulled[t]) { |
| 33 | // must release to at least 0.1 | 27 | // must release to at least 0.1 |
| 34 | if (triggerValue < 0.1) { | 28 | if (triggerValue < 0.1) { |
| @@ -41,17 +35,14 @@ function checkController(deltaTime) { · 6 unmodified lines | |||
| 41 | triggerToggled = true; | 35 | triggerToggled = true; |
| 42 | } | 36 | } |
| 43 | } | 37 | } |
| 44 | |||
| 45 | if (triggerToggled) { | 38 | if (triggerToggled) { |
| 46 | print("a trigger was toggled"); | 39 | print("a trigger was toggled"); |
| 47 | } | 40 | } |
| 48 | } | 41 | } |
| 49 | } | 42 | |
| 50 | } | 43 | } |
| 51 | |||
| 52 | // register the call back so it fires before each data send | 44 | // register the call back so it fires before each data send |
| 53 | Script.update.connect(checkController); | 45 | Script.update.connect(checkController); |
| 54 | |||
| 55 | function printKeyEvent(eventName, event) { | 46 | function printKeyEvent(eventName, event) { |
| 56 | print(eventName); | 47 | print(eventName); |
| 57 | print(" event.key=" + event.key); | 48 | print(" event.key=" + event.key); |
| @@ -64,7 +55,6 @@ function printKeyEvent(eventName, event) { · 6 unmodified lines | |||
| 64 | } | 55 | } |
| 65 | function keyPressEvent(event) { | 56 | function keyPressEvent(event) { |
| 66 | printKeyEvent("keyPressEvent", event); | 57 | printKeyEvent("keyPressEvent", event); |
| 67 | |||
| 68 | if (event.text == "A") { | 58 | if (event.text == "A") { |
| 69 | print("the A key was pressed"); | 59 | print("the A key was pressed"); |
| 70 | } | 60 | } |
| @@ -72,10 +62,8 @@ function keyPressEvent(event) { · 1 unmodified line | |||
| 72 | print("the <space> key was pressed"); | 62 | print("the <space> key was pressed"); |
| 73 | } | 63 | } |
| 74 | } | 64 | } |
| 75 | |||
| 76 | function keyReleaseEvent(event) { | 65 | function keyReleaseEvent(event) { |
| 77 | printKeyEvent("keyReleaseEvent", event); | 66 | printKeyEvent("keyReleaseEvent", event); |
| 78 | |||
| 79 | if (event.text == "A") { | 67 | if (event.text == "A") { |
| 80 | print("the A key was released"); | 68 | print("the A key was released"); |
| 81 | } | 69 | } |
| @@ -83,11 +71,9 @@ function keyReleaseEvent(event) { · 1 unmodified line | |||
| 83 | print("the <space> key was pressed"); | 71 | print("the <space> key was pressed"); |
| 84 | } | 72 | } |
| 85 | } | 73 | } |
| 86 | |||
| 87 | // Map keyPress and mouse move events to our callbacks | 74 | // Map keyPress and mouse move events to our callbacks |
| 88 | Controller.keyPressEvent.connect(keyPressEvent); | 75 | Controller.keyPressEvent.connect(keyPressEvent); |
| 89 | Controller.keyReleaseEvent.connect(keyReleaseEvent); | 76 | Controller.keyReleaseEvent.connect(keyReleaseEvent); |
| 90 | |||
| 91 | // prevent the A key from going through to the application | 77 | // prevent the A key from going through to the application |
| 92 | Controller.captureKeyEvents({ text: "A" }); | 78 | Controller.captureKeyEvents({ text: "A" }); |
| 93 | Controller.captureKeyEvents({ key: "A".charCodeAt(0) }); // same as above, just another example of how to capture the key | 79 | Controller.captureKeyEvents({ key: "A".charCodeAt(0) }); // same as above, just another example of how to capture the key |
| @@ -95,8 +81,6 @@ Controller.captureKeyEvents({ text: " " }); · 1 unmodified line | |||
| 95 | Controller.captureKeyEvents({ text: "@", isMeta: true }); | 81 | Controller.captureKeyEvents({ text: "@", isMeta: true }); |
| 96 | Controller.captureKeyEvents({ text: "page up" }); | 82 | Controller.captureKeyEvents({ text: "page up" }); |
| 97 | Controller.captureKeyEvents({ text: "page down" }); | 83 | Controller.captureKeyEvents({ text: "page down" }); |
| 98 | |||
| 99 | |||
| 100 | function printMouseEvent(eventName, event) { | 84 | function printMouseEvent(eventName, event) { |
| 101 | print(eventName); | 85 | print(eventName); |
| 102 | print(" event.x,y=" + event.x + ", " + event.y); | 86 | print(" event.x,y=" + event.x + ", " + event.y); |
| @@ -109,22 +93,18 @@ function printMouseEvent(eventName, event) { · 6 unmodified lines | |||
| 109 | print(" event.isMeta=" + event.isMeta); | 93 | print(" event.isMeta=" + event.isMeta); |
| 110 | print(" event.isAlt=" + event.isAlt); | 94 | print(" event.isAlt=" + event.isAlt); |
| 111 | } | 95 | } |
| 112 | |||
| 113 | function mouseMoveEvent(event) { | 96 | function mouseMoveEvent(event) { |
| 114 | printMouseEvent("mouseMoveEvent", event); | 97 | printMouseEvent("mouseMoveEvent", event); |
| 115 | } | 98 | } |
| 116 | function mousePressEvent(event) { | 99 | function mousePressEvent(event) { |
| 117 | printMouseEvent("mousePressEvent", event); | 100 | printMouseEvent("mousePressEvent", event); |
| 118 | } | 101 | } |
| 119 | |||
| 120 | function mouseReleaseEvent(event) { | 102 | function mouseReleaseEvent(event) { |
| 121 | printMouseEvent("mouseReleaseEvent", event); | 103 | printMouseEvent("mouseReleaseEvent", event); |
| 122 | } | 104 | } |
| 123 | |||
| 124 | Controller.mouseMoveEvent.connect(mouseMoveEvent); | 105 | Controller.mouseMoveEvent.connect(mouseMoveEvent); |
| 125 | Controller.mousePressEvent.connect(mousePressEvent); | 106 | Controller.mousePressEvent.connect(mousePressEvent); |
| 126 | Controller.mouseReleaseEvent.connect(mouseReleaseEvent); | 107 | Controller.mouseReleaseEvent.connect(mouseReleaseEvent); |
| 127 | |||
| 128 | function printTouchEvent(eventName, event) { | 108 | function printTouchEvent(eventName, event) { |
| 129 | print(eventName); | 109 | print(eventName); |
| 130 | 110 | ||
| @@ -143,7 +123,6 @@ function printTouchEvent(eventName, event) { · 12 unmodified lines | |||
| 143 | print(" event.radius=" + event.radius); | 123 | print(" event.radius=" + event.radius); |
| 144 | print(" event.isPinching=" + event.isPinching); | 124 | print(" event.isPinching=" + event.isPinching); |
| 145 | print(" event.isPinchOpening=" + event.isPinchOpening); | 125 | print(" event.isPinchOpening=" + event.isPinchOpening); |
| 146 | |||
| 147 | print(" event.angle=" + event.angle); | 126 | print(" event.angle=" + event.angle); |
| 148 | for (var i = 0; i < event.points.length; i++) { | 127 | for (var i = 0; i < event.points.length; i++) { |
| 149 | print(" event.angles[" + i + "]:" + event.angles[i]); | 128 | print(" event.angles[" + i + "]:" + event.angles[i]); |
| @@ -151,15 +130,12 @@ function printTouchEvent(eventName, event) { · 1 unmodified line | |||
| 151 | print(" event.isRotating=" + event.isRotating); | 130 | print(" event.isRotating=" + event.isRotating); |
| 152 | print(" event.rotating=" + event.rotating); | 131 | print(" event.rotating=" + event.rotating); |
| 153 | } | 132 | } |
| 154 | |||
| 155 | function touchBeginEvent(event) { | 133 | function touchBeginEvent(event) { |
| 156 | printTouchEvent("touchBeginEvent", event); | 134 | printTouchEvent("touchBeginEvent", event); |
| 157 | } | 135 | } |
| 158 | |||
| 159 | function touchUpdateEvent(event) { | 136 | function touchUpdateEvent(event) { |
| 160 | printTouchEvent("touchUpdateEvent", event); | 137 | printTouchEvent("touchUpdateEvent", event); |
| 161 | } | 138 | } |
| 162 | |||
| 163 | function touchEndEvent(event) { | 139 | function touchEndEvent(event) { |
| 164 | printTouchEvent("touchEndEvent", event); | 140 | printTouchEvent("touchEndEvent", event); |
| 165 | } | 141 | } |
| @@ -167,8 +143,6 @@ function touchEndEvent(event) { · 1 unmodified line | |||
| 167 | Controller.touchBeginEvent.connect(touchBeginEvent); | 143 | Controller.touchBeginEvent.connect(touchBeginEvent); |
| 168 | Controller.touchUpdateEvent.connect(touchUpdateEvent); | 144 | Controller.touchUpdateEvent.connect(touchUpdateEvent); |
| 169 | Controller.touchEndEvent.connect(touchEndEvent); | 145 | Controller.touchEndEvent.connect(touchEndEvent); |
| 170 | |||
| 171 | |||
| 172 | function wheelEvent(event) { | 146 | function wheelEvent(event) { |
| 173 | print("wheelEvent"); | 147 | print("wheelEvent"); |
| 174 | print(" event.x,y=" + event.x + ", " + event.y); | 148 | print(" event.x,y=" + event.x + ", " + event.y); |
| @@ -182,9 +156,7 @@ function wheelEvent(event) { · 7 unmodified lines | |||
| 182 | print(" event.isMeta=" + event.isMeta); | 156 | print(" event.isMeta=" + event.isMeta); |
| 183 | print(" event.isAlt=" + event.isAlt); | 157 | print(" event.isAlt=" + event.isAlt); |
| 184 | } | 158 | } |
| 185 | |||
| 186 | Controller.wheelEvent.connect(wheelEvent); | 159 | Controller.wheelEvent.connect(wheelEvent); |
| 187 | |||
| 188 | function scriptEnding() { | 160 | function scriptEnding() { |
| 189 | // re-enabled the standard application for touch events | 161 | // re-enabled the standard application for touch events |
| 190 | Controller.releaseKeyEvents({ text: "A" }); | 162 | Controller.releaseKeyEvents({ text: "A" }); |
| @@ -194,5 +166,4 @@ function scriptEnding() { · 3 unmodified lines | |||
| 194 | Controller.releaseKeyEvents({ text: "page up" }); | 166 | Controller.releaseKeyEvents({ text: "page up" }); |
| 195 | Controller.releaseKeyEvents({ text: "page down" }); | 167 | Controller.releaseKeyEvents({ text: "page down" }); |
| 196 | } | 168 | } |
| 197 | 169 | Script.scriptEnding.connect(scriptEnding); | |
| 198 | Script.scriptEnding.connect(scriptEnding); | ||
| @@ -59,9 +59,7 @@ function controller(side) { · 58 unmodified lines | |||
| 59 | this.triggerHeld = false; | 59 | this.triggerHeld = false; |
| 60 | this.triggerThreshold = 0.9; | 60 | this.triggerThreshold = 0.9; |
| 61 | this.side = side; | 61 | this.side = side; |
| 62 | this.palm = 2 * side; | 62 | this.trigger = side == LEFT ? Controller.Standard.LT : Controller.Standard.RT; |
| 63 | this.tip = 2 * side + 1; | ||
| 64 | this.trigger = side; | ||
| 65 | this.originalGravity = { | 63 | this.originalGravity = { |
| 66 | x: 0, | 64 | x: 0, |
| 67 | y: 0, | 65 | y: 0, |
| @@ -150,8 +148,8 @@ function controller(side) { · 82 unmodified lines | |||
| 150 | 148 | ||
| 151 | 149 | ||
| 152 | this.updateControllerState = function() { | 150 | this.updateControllerState = function() { |
| 153 | this.palmPosition = Controller.getSpatialControlPosition(this.palm); | 151 | this.palmPosition = this.side == RIGHT ? MyAvatar.rightHandPose.translation : MyAvatar.leftHandPose.translation; |
| 154 | this.tipPosition = Controller.getSpatialControlPosition(this.tip); | 152 | this.tipPosition = this.side == RIGHT ? MyAvatar.rightHandTipPose.translation : MyAvatar.leftHandTipPose.translation; |
| 155 | this.triggerValue = Controller.getTriggerValue(this.trigger); | 153 | this.triggerValue = Controller.getTriggerValue(this.trigger); |
| 156 | } | 154 | } |
| 157 | 155 | ||
| @@ -238,7 +238,7 @@ var inHand = false; · 237 unmodified lines | |||
| 238 | 238 | ||
| 239 | function isControllerActive() { | 239 | function isControllerActive() { |
| 240 | // I don't think the hydra API provides any reliable way to know whether a particular controller is active. Ask for both. | 240 | // I don't think the hydra API provides any reliable way to know whether a particular controller is active. Ask for both. |
| 241 | controllerActive = (Vec3.length(Controller.getSpatialControlPosition(3)) > 0) || Vec3.length(Controller.getSpatialControlPosition(4)) > 0; | 241 | controllerActive = (Vec3.length(MyAvatar.leftHandPose.translation) > 0) || Vec3.length(MyAvatar.rightHandPose.translation) > 0; |
| 242 | return controllerActive; | 242 | return controllerActive; |
| 243 | } | 243 | } |
| 244 | 244 | ||
| @@ -312,10 +312,10 @@ function grabSword(hand) { · 67 unmodified lines | |||
| 312 | } | 312 | } |
| 313 | var handRotation; | 313 | var handRotation; |
| 314 | if (hand === "right") { | 314 | if (hand === "right") { |
| 315 | handRotation = MyAvatar.getRightPalmRotation(); | 315 | handRotation = MyAvatar.rightHandPose.rotation; |
| 316 | 316 | ||
| 317 | } else if (hand === "left") { | 317 | } else if (hand === "left") { |
| 318 | handRotation = MyAvatar.getLeftPalmRotation(); | 318 | handRotation = MyAvatar.leftHandPose.rotation; |
| 319 | } | 319 | } |
| 320 | var swordRotation = Entities.getEntityProperties(swordID).rotation; | 320 | var swordRotation = Entities.getEntityProperties(swordID).rotation; |
| 321 | var offsetRotation = Quat.multiply(Quat.inverse(handRotation), swordRotation); | 321 | var offsetRotation = Quat.multiply(Quat.inverse(handRotation), swordRotation); |
| @@ -71,10 +71,8 @@ function controller(side, cycleColorButton) { · 70 unmodified lines | |||
| 71 | this.triggerHeld = false; | 71 | this.triggerHeld = false; |
| 72 | this.triggerThreshold = 0.9; | 72 | this.triggerThreshold = 0.9; |
| 73 | this.side = side; | 73 | this.side = side; |
| 74 | this.palm = 2 * side; | 74 | this.trigger = side == LEFT ? Controller.Stantard.LT : Controller.Standard.RT; |
| 75 | this.tip = 2 * side + 1; | 75 | this.cycleColorButton = side == LEFT ? Controller.Stantard.LeftPrimaryThumb : Controller.Standard.RightPrimaryThumb; |
| 76 | this.trigger = side; | ||
| 77 | this.cycleColorButton = cycleColorButton; | ||
| 78 | 76 | ||
| 79 | this.points = []; | 77 | this.points = []; |
| 80 | this.normals = []; | 78 | this.normals = []; |
| @@ -173,11 +171,10 @@ function controller(side, cycleColorButton) { · 92 unmodified lines | |||
| 173 | 171 | ||
| 174 | 172 | ||
| 175 | this.updateControllerState = function() { | 173 | this.updateControllerState = function() { |
| 176 | this.cycleColorButtonPressed = Controller.isButtonPressed(this.cycleColorButton); | 174 | this.cycleColorButtonPressed = Controller.getValue(this.cycleColorButton); |
| 177 | this.palmPosition = Controller.getSpatialControlPosition(this.palm); | 175 | this.palmPosition = this.side == RIGHT ? MyAvatar.rightHandPose.translation : MyAvatar.leftHandPose.translation; |
| 178 | this.tipPosition = Controller.getSpatialControlPosition(this.tip); | 176 | this.tipPosition = this.side == RIGHT ? MyAvatar.rightHandTipPose.translation : MyAvatar.leftHandTipPose.translation; |
| 179 | this.palmNormal = Controller.getSpatialControlNormal(this.palm); | 177 | this.triggerValue = Controller.getValue(this.trigger); |
| 180 | this.triggerValue = Controller.getTriggerValue(this.trigger); | ||
| 181 | 178 | ||
| 182 | 179 | ||
| 183 | if (this.prevCycleColorButtonPressed === true && this.cycleColorButtonPressed === false) { | 180 | if (this.prevCycleColorButtonPressed === true && this.cycleColorButtonPressed === false) { |
| @@ -215,8 +212,8 @@ function vectorIsZero(v) { · 31 unmodified lines | |||
| 215 | } | 212 | } |
| 216 | 213 | ||
| 217 | 214 | ||
| 218 | var rightController = new controller(RIGHT, RIGHT_BUTTON_4); | 215 | var rightController = new controller(RIGHT); |
| 219 | var leftController = new controller(LEFT, LEFT_BUTTON_4); | 216 | var leftController = new controller(LEFT); |
| 220 | Script.update.connect(update); | 217 | Script.update.connect(update); |
| 221 | Script.scriptEnding.connect(scriptEnding); | 218 | Script.scriptEnding.connect(scriptEnding); |
| 222 | 219 | ||
| @@ -11,6 +11,37 @@ · 10 unmodified lines | |||
| 11 | 11 | ||
| 12 | // Assumes you only have the default keyboard connected | 12 | // Assumes you only have the default keyboard connected |
| 13 | 13 | ||
| 14 | function findAction(name) { | ||
| 15 | var actions = Controller.getAllActions(); | ||
| 16 | for (var i = 0; i < actions.length; i++) { | ||
| 17 | if (actions[i].actionName == name) { | ||
| 18 | return i; | ||
| 19 | } | ||
| 20 | } | ||
| 21 | // If the action isn't found, it will default to the first available action | ||
| 22 | return 0; | ||
| 23 | } | ||
| 24 | |||
| 25 | |||
| 26 | var hydra = Controller.Hardware.Hydra; | ||
| 27 | if (hydra !== undefined) { | ||
| 28 | print("-----------------------------------"); | ||
| 29 | var mapping = Controller.newMapping("Test"); | ||
| 30 | var standard = Controller.Standard; | ||
| 31 | print("standard:" + standard); | ||
| 32 | mapping.from(function () { return Math.sin(Date.now() / 250); }).to(function (newValue, oldValue, source) { | ||
| 33 | print("function source newValue:" + newValue + ", oldValue:" + oldValue + ", source:" + source); | ||
| 34 | }); | ||
| 35 | mapping.from(hydra.L1).to(standard.A); | ||
| 36 | mapping.from(hydra.L2).to(standard.B); | ||
| 37 | mapping.from(hydra.L3).to(function (newValue, oldValue, source) { | ||
| 38 | print("hydra.L3 newValue:" + newValue + ", oldValue:" + oldValue + ", source:" + source); | ||
| 39 | }); | ||
| 40 | Controller.enableMapping("Test"); | ||
| 41 | print("-----------------------------------"); | ||
| 42 | } else { | ||
| 43 | print("couldn't find hydra"); | ||
| 44 | } | ||
| 14 | 45 | ||
| 15 | Object.keys(Controller.Standard).forEach(function (input) { | 46 | Object.keys(Controller.Standard).forEach(function (input) { |
| 16 | print("Controller.Standard." + input + ":" + Controller.Standard[input]); | 47 | print("Controller.Standard." + input + ":" + Controller.Standard[input]); |
| @@ -32,44 +63,48 @@ Controller.resetAllDeviceBindings(); · 15 unmodified lines | |||
| 32 | // Query all actions | 63 | // Query all actions |
| 33 | print("All Actions: \n" + Controller.getAllActions()); | 64 | print("All Actions: \n" + Controller.getAllActions()); |
| 34 | 65 | ||
| 66 | var actionId = findAction("YAW_LEFT") | ||
| 67 | |||
| 68 | print("Yaw Left action ID: " + actionId) | ||
| 69 | |||
| 35 | // Each action stores: | 70 | // Each action stores: |
| 36 | // action: int representation of enum | 71 | // action: int representation of enum |
| 37 | print("Action 5 int: \n" + Controller.getAllActions()[5].action); | 72 | print("Action int: \n" + Controller.getAllActions()[actionId].action); |
| 38 | 73 | ||
| 39 | // actionName: string representation of enum | 74 | // actionName: string representation of enum |
| 40 | print("Action 5 name: \n" + Controller.getAllActions()[5].actionName); | 75 | print("Action name: \n" + Controller.getAllActions()[actionId].actionName); |
| 41 | 76 | ||
| 42 | // inputChannels: list of all inputchannels that control that action | 77 | // inputChannels: list of all inputchannels that control that action |
| 43 | print("Action 5 input channels: \n" + Controller.getAllActions()[5].inputChannels + "\n"); | 78 | print("Action input channels: \n" + Controller.getAllActions()[actionId].inputChannels + "\n"); |
| 44 | 79 | ||
| 45 | 80 | ||
| 46 | // Each input channel stores: | 81 | // Each input channel stores: |
| 47 | // action: Action that this InputChannel maps to | 82 | // action: Action that this InputChannel maps to |
| 48 | print("Input channel action: \n" + Controller.getAllActions()[5].inputChannels[0].action); | 83 | print("Input channel action: \n" + Controller.getAllActions()[actionId].inputChannels[0].action); |
| 49 | 84 | ||
| 50 | // scale: sensitivity of input | 85 | // scale: sensitivity of input |
| 51 | print("Input channel scale: \n" + Controller.getAllActions()[5].inputChannels[0].scale); | 86 | print("Input channel scale: \n" + Controller.getAllActions()[actionId].inputChannels[0].scale); |
| 52 | 87 | ||
| 53 | // input and modifier: Inputs | 88 | // input and modifier: Inputs |
| 54 | print("Input channel input and modifier: \n" + Controller.getAllActions()[5].inputChannels[0].input + "\n" + Controller.getAllActions()[5].inputChannels[0].modifier + "\n"); | 89 | print("Input channel input and modifier: \n" + Controller.getAllActions()[actionId].inputChannels[0].input + "\n" + Controller.getAllActions()[actionId].inputChannels[0].modifier + "\n"); |
| 55 | 90 | ||
| 56 | 91 | ||
| 57 | // Each Input stores: | 92 | // Each Input stores: |
| 58 | // device: device of input | 93 | // device: device of input |
| 59 | print("Input device: \n" + Controller.getAllActions()[5].inputChannels[0].input.device); | 94 | print("Input device: \n" + Controller.getAllActions()[actionId].inputChannels[0].input.device); |
| 60 | 95 | ||
| 61 | // channel: channel of input | 96 | // channel: channel of input |
| 62 | print("Input channel: \n" + Controller.getAllActions()[5].inputChannels[0].input.channel); | 97 | print("Input channel: \n" + Controller.getAllActions()[actionId].inputChannels[0].input.channel); |
| 63 | 98 | ||
| 64 | // type: type of input (Unknown, Button, Axis, Joint) | 99 | // type: type of input (Unknown, Button, Axis, Joint) |
| 65 | print("Input type: \n" + Controller.getAllActions()[5].inputChannels[0].input.type); | 100 | print("Input type: \n" + Controller.getAllActions()[actionId].inputChannels[0].input.type); |
| 66 | 101 | ||
| 67 | // id: id of input | 102 | // id: id of input |
| 68 | print("Input id: \n" + Controller.getAllActions()[5].inputChannels[0].input.id + "\n"); | 103 | print("Input id: \n" + Controller.getAllActions()[actionId].inputChannels[0].input.id + "\n"); |
| 69 | 104 | ||
| 70 | 105 | ||
| 71 | // You can get the name of a device from its id | 106 | // You can get the name of a device from its id |
| 72 | print("Device 1 name: \n" + Controller.getDeviceName(Controller.getAllActions()[5].inputChannels[0].input.id)); | 107 | print("Device 1 name: \n" + Controller.getDeviceName(Controller.getAllActions()[actionId].inputChannels[0].input.id)); |
| 73 | 108 | ||
| 74 | // You can also get all of a devices input channels | 109 | // You can also get all of a devices input channels |
| 75 | print("Device 1's input channels: \n" + Controller.getAllInputsForDevice(1) + "\n"); | 110 | print("Device 1's input channels: \n" + Controller.getAllInputsForDevice(1) + "\n"); |
| @@ -105,7 +140,7 @@ for (i = 0; i < availableInputs.length; i++) { · 29 unmodified lines | |||
| 105 | 140 | ||
| 106 | // You can modify key bindings by using these avaiable inputs | 141 | // You can modify key bindings by using these avaiable inputs |
| 107 | // This will replace e (up) with 6 | 142 | // This will replace e (up) with 6 |
| 108 | var e = Controller.getAllActions()[5].inputChannels[0]; | 143 | var e = Controller.getAllActions()[actionId].inputChannels[0]; |
| 109 | Controller.removeInputChannel(e); | 144 | Controller.removeInputChannel(e); |
| 110 | e.input = availableInputs[6].input; | 145 | e.input = availableInputs[6].input; |
| 111 | Controller.addInputChannel(e); | 146 | Controller.addInputChannel(e); |
| @@ -15,16 +15,18 @@ Script.include("omniTool/models/invisibleWand.js"); · 14 unmodified lines | |||
| 15 | 15 | ||
| 16 | OmniToolModules = {}; | 16 | OmniToolModules = {}; |
| 17 | OmniToolModuleType = null; | 17 | OmniToolModuleType = null; |
| 18 | LOG_DEBUG = 1; | ||
| 18 | 19 | ||
| 19 | OmniTool = function(side) { | 20 | OmniTool = function(left) { |
| 20 | this.OMNI_KEY = "OmniTool"; | 21 | this.OMNI_KEY = "OmniTool"; |
| 21 | this.MAX_FRAMERATE = 60; | 22 | this.MAX_FRAMERATE = 60; |
| 22 | this.UPDATE_INTERVAL = 1.0 / this.MAX_FRAMERATE | 23 | this.UPDATE_INTERVAL = 1.0 / this.MAX_FRAMERATE |
| 23 | this.SIDE = side; | 24 | this.left = left; |
| 24 | this.PALM = 2 * side; | 25 | this.triggered = false; |
| 25 | this.ACTION = findAction(side ? "ACTION2" : "ACTION1"); | 26 | var actions = Controller.Actions; |
| 26 | this.ALT_ACTION = findAction(side ? "ACTION1" : "ACTION2"); | 27 | var standard = Controller.Standard; |
| 27 | 28 | this.palmControl = left ? actions.LeftHand : actions.RightHand; | |
| 29 | logDebug("Init OmniTool " + (left ? "left" : "right")); | ||
| 28 | this.highlighter = new Highlighter(); | 30 | this.highlighter = new Highlighter(); |
| 29 | this.ignoreEntities = {}; | 31 | this.ignoreEntities = {}; |
| 30 | this.nearestOmniEntity = { | 32 | this.nearestOmniEntity = { |
| @@ -47,22 +49,25 @@ OmniTool = function(side) { · 16 unmodified lines | |||
| 47 | this.showWand(false); | 49 | this.showWand(false); |
| 48 | 50 | ||
| 49 | // Connect to desired events | 51 | // Connect to desired events |
| 50 | var _this = this; | 52 | var that = this; |
| 51 | Controller.actionEvent.connect(function(action, state) { | ||
| 52 | _this.onActionEvent(action, state); | ||
| 53 | }); | ||
| 54 | 53 | ||
| 55 | Script.update.connect(function(deltaTime) { | 54 | Script.update.connect(function(deltaTime) { |
| 56 | _this.lastUpdateInterval += deltaTime; | 55 | that.lastUpdateInterval += deltaTime; |
| 57 | if (_this.lastUpdateInterval >= _this.UPDATE_INTERVAL) { | 56 | if (that.lastUpdateInterval >= that.UPDATE_INTERVAL) { |
| 58 | _this.onUpdate(_this.lastUpdateInterval); | 57 | that.onUpdate(that.lastUpdateInterval); |
| 59 | _this.lastUpdateInterval = 0; | 58 | that.lastUpdateInterval = 0; |
| 60 | } | 59 | } |
| 61 | }); | 60 | }); |
| 62 | 61 | ||
| 63 | Script.scriptEnding.connect(function() { | 62 | Script.scriptEnding.connect(function() { |
| 64 | _this.onCleanup(); | 63 | that.onCleanup(); |
| 65 | }); | 64 | }); |
| 65 | |||
| 66 | this.mapping = Controller.newMapping(); | ||
| 67 | this.mapping.from(left ? standard.LeftPrimaryThumb : standard.RightPrimaryThumb).to(function(value){ | ||
| 68 | that.onUpdateTrigger(value); | ||
| 69 | }) | ||
| 70 | this.mapping.enable(); | ||
| 66 | } | 71 | } |
| 67 | 72 | ||
| 68 | OmniTool.prototype.showWand = function(show) { | 73 | OmniTool.prototype.showWand = function(show) { |
| @@ -81,30 +86,23 @@ OmniTool.prototype.showWand = function(show) { · 12 unmodified lines | |||
| 81 | } | 86 | } |
| 82 | } | 87 | } |
| 83 | 88 | ||
| 84 | |||
| 85 | OmniTool.prototype.onCleanup = function(action) { | 89 | OmniTool.prototype.onCleanup = function(action) { |
| 90 | this.mapping.disable(); | ||
| 86 | this.unloadModule(); | 91 | this.unloadModule(); |
| 87 | } | 92 | } |
| 88 | 93 | ||
| 89 | OmniTool.prototype.onActionEvent = function(action, state) { | ||
| 90 | // FIXME figure out the issues when only one spatial controller is active | ||
| 91 | // logDebug("Action: " + action + " " + state); | ||
| 92 | 94 | ||
| 93 | if (this.module && this.module.onActionEvent) { | 95 | OmniTool.prototype.onUpdateTrigger = function (value) { |
| 94 | this.module.onActionEvent(action, state); | 96 | //logDebug("Trigger update value " + value); |
| 95 | } | 97 | var triggered = value != 0; |
| 96 | 98 | if (triggered != this.triggered) { | |
| 97 | if (action == this.ACTION) { | 99 | this.triggered = triggered; |
| 98 | if (state) { | 100 | if (this.triggered) { |
| 99 | this.onClick(); | 101 | this.onClick(); |
| 100 | } else { | 102 | } else { |
| 101 | this.onRelease(); | 103 | this.onRelease(); |
| 102 | } | 104 | } |
| 103 | } | 105 | } |
| 104 | |||
| 105 | // FIXME Does not work | ||
| 106 | //// with only one controller active (listed as 2 here because 'tip' + 'palm') | ||
| 107 | //// then treat the alt action button as the action button | ||
| 108 | } | 106 | } |
| 109 | 107 | ||
| 110 | OmniTool.prototype.getOmniToolData = function(entityId) { | 108 | OmniTool.prototype.getOmniToolData = function(entityId) { |
| @@ -127,7 +125,7 @@ OmniTool.prototype.setActive = function(active) { · 16 unmodified lines | |||
| 127 | if (active === this.active) { | 125 | if (active === this.active) { |
| 128 | return; | 126 | return; |
| 129 | } | 127 | } |
| 130 | logDebug("OmniTool changing active state: " + active); | 128 | logDebug("OmniTool " + this.left + " changing active state: " + active); |
| 131 | this.active = active; | 129 | this.active = active; |
| 132 | this.model.setVisible(this.active); | 130 | this.model.setVisible(this.active); |
| 133 | if (this.module && this.module.onActiveChanged) { | 131 | if (this.module && this.module.onActiveChanged) { |
| @@ -138,17 +136,17 @@ OmniTool.prototype.setActive = function(active) { · 4 unmodified lines | |||
| 138 | 136 | ||
| 139 | OmniTool.prototype.onUpdate = function(deltaTime) { | 137 | OmniTool.prototype.onUpdate = function(deltaTime) { |
| 140 | // FIXME this returns data if either the left or right controller is not on the base | 138 | // FIXME this returns data if either the left or right controller is not on the base |
| 141 | this.position = Controller.getSpatialControlPosition(this.PALM); | 139 | this.pose = Controller.getPoseValue(this.palmControl); |
| 140 | this.position = this.left ? MyAvatar.leftHandTipPosition : MyAvatar.rightHandTipPosition; | ||
| 142 | // When on the base, hydras report a position of 0 | 141 | // When on the base, hydras report a position of 0 |
| 143 | this.setActive(Vec3.length(this.position) > 0.001); | 142 | this.setActive(Vec3.length(this.position) > 0.001); |
| 144 | if (!this.active) { | 143 | if (!this.active) { |
| 145 | return; | 144 | return; |
| 146 | } | 145 | } |
| 147 | 146 | ||
| 148 | |||
| 149 | if (this.model) { | 147 | if (this.model) { |
| 150 | // Update the wand | 148 | // Update the wand |
| 151 | var rawRotation = Controller.getSpatialControlRawRotation(this.PALM); | 149 | var rawRotation = this.pose.rotation; |
| 152 | this.rotation = Quat.multiply(MyAvatar.orientation, rawRotation); | 150 | this.rotation = Quat.multiply(MyAvatar.orientation, rawRotation); |
| 153 | this.model.setTransform({ | 151 | this.model.setTransform({ |
| 154 | rotation: this.rotation, | 152 | rotation: this.rotation, |
| @@ -306,6 +304,7 @@ OmniTool.prototype.scan = function() { · 151 unmodified lines | |||
| 306 | } | 304 | } |
| 307 | 305 | ||
| 308 | OmniTool.prototype.unloadModule = function() { | 306 | OmniTool.prototype.unloadModule = function() { |
| 307 | logDebug("Unloading omniTool module") | ||
| 309 | if (this.module && this.module.onUnload) { | 308 | if (this.module && this.module.onUnload) { |
| 310 | this.module.onUnload(); | 309 | this.module.onUnload(); |
| 311 | } | 310 | } |
| @@ -348,4 +347,4 @@ OmniTool.prototype.activateNewOmniModule = function() { · 36 unmodified lines | |||
| 348 | } | 347 | } |
| 349 | 348 | ||
| 350 | // FIXME find a good way to sync the two omni tools | 349 | // FIXME find a good way to sync the two omni tools |
| 351 | OMNI_TOOLS = [ new OmniTool(0), new OmniTool(1) ]; | 350 | OMNI_TOOLS = [ new OmniTool(true), new OmniTool(false) ]; |
| @@ -31,13 +31,7 @@ scaleLine = function (start, end, scale) { · 30 unmodified lines | |||
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | findAction = function(name) { | 33 | findAction = function(name) { |
| 34 | var actions = Controller.getAllActions(); | 34 | return Controller.findAction(name); |
| 35 | for (var i = 0; i < actions.length; i++) { | ||
| 36 | if (actions[i].actionName == name) { | ||
| 37 | return i; | ||
| 38 | } | ||
| 39 | } | ||
| 40 | return 0; | ||
| 41 | } | 35 | } |
| 42 | 36 | ||
| 43 | addLine = function(origin, vector, color) { | 37 | addLine = function(origin, vector, color) { |
| @@ -18,24 +18,8 @@ Script.include("./libraries/walkConstants.js"); · 17 unmodified lines | |||
| 18 | 18 | ||
| 19 | Avatar = function() { | 19 | Avatar = function() { |
| 20 | // if Hydras are connected, the only way to enable use is to never set any arm joint rotation | 20 | // if Hydras are connected, the only way to enable use is to never set any arm joint rotation |
| 21 | this.hydraCheck = function() { | 21 | this.hydraCheck = function () { |
| 22 | // function courtesy of Thijs Wenker (frisbee.js) | 22 | return Controller.Hardware.Hydra !== undefined; |
| 23 | var numberOfButtons = Controller.getNumberOfButtons(); | ||
| 24 | var numberOfTriggers = Controller.getNumberOfTriggers(); | ||
| 25 | var numberOfSpatialControls = Controller.getNumberOfSpatialControls(); | ||
| 26 | const HYDRA_BUTTONS = 12; | ||
| 27 | const HYDRA_TRIGGERS = 2; | ||
| 28 | const HYDRA_CONTROLLERS_PER_TRIGGER = 2; | ||
| 29 | var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers; | ||
| 30 | if (numberOfButtons == HYDRA_BUTTONS && | ||
| 31 | numberOfTriggers == HYDRA_TRIGGERS && | ||
| 32 | controllersPerTrigger == HYDRA_CONTROLLERS_PER_TRIGGER) { | ||
| 33 | print('walk.js info: Razer Hydra detected. Setting arms free (not controlled by script)'); | ||
| 34 | return true; | ||
| 35 | } else { | ||
| 36 | print('walk.js info: Razer Hydra not detected. Arms will be controlled by script.'); | ||
| 37 | return false; | ||
| 38 | } | ||
| 39 | } | 23 | } |
| 40 | // settings | 24 | // settings |
| 41 | this.headFree = true; | 25 | this.headFree = true; |
| @@ -38,7 +38,7 @@ var mouseLook = (function () { · 37 unmodified lines | |||
| 38 | keyboardID = 0; | 38 | keyboardID = 0; |
| 39 | 39 | ||
| 40 | function onKeyPressEvent(event) { | 40 | function onKeyPressEvent(event) { |
| 41 | if (event.text == 'M') { | 41 | if (event.text == 'm') { |
| 42 | active = !active; | 42 | active = !active; |
| 43 | updateMapping(); | 43 | updateMapping(); |
| 44 | } | 44 | } |
| @@ -10,7 +10,7 @@ · 9 unmodified lines | |||
| 10 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | 10 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html |
| 11 | // | 11 | // |
| 12 | 12 | ||
| 13 | var hand = "left"; | 13 | var hand = "right"; |
| 14 | var nullActionID = "00000000-0000-0000-0000-000000000000"; | 14 | var nullActionID = "00000000-0000-0000-0000-000000000000"; |
| 15 | var controllerID; | 15 | var controllerID; |
| 16 | var controllerActive; | 16 | var controllerActive; |
| @@ -32,7 +32,7 @@ function makeNewStick() { · 15 unmodified lines | |||
| 32 | modelURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.fbx", | 32 | modelURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.fbx", |
| 33 | compoundShapeURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.obj", | 33 | compoundShapeURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.obj", |
| 34 | dimensions: {x: .11, y: .11, z: 1.0}, | 34 | dimensions: {x: .11, y: .11, z: 1.0}, |
| 35 | position: MyAvatar.getRightPalmPosition(), // initial position doesn't matter, as long as it's close | 35 | position: MyAvatar.rightHandPosition, // initial position doesn't matter, as long as it's close |
| 36 | rotation: MyAvatar.orientation, | 36 | rotation: MyAvatar.orientation, |
| 37 | damping: .1, | 37 | damping: .1, |
| 38 | collisionSoundURL: "http://public.highfidelity.io/sounds/Collisions-hitsandslaps/67LCollision07.wav", | 38 | collisionSoundURL: "http://public.highfidelity.io/sounds/Collisions-hitsandslaps/67LCollision07.wav", |
| @@ -84,23 +84,15 @@ function mouseMoveEvent(event) { · 45 unmodified lines | |||
| 84 | } | 84 | } |
| 85 | 85 | ||
| 86 | 86 | ||
| 87 | function initControls(){ | ||
| 88 | if (hand == "right") { | ||
| 89 | controllerID = 3; // right handed | ||
| 90 | } else { | ||
| 91 | controllerID = 4; // left handed | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | |||
| 96 | function update(deltaTime){ | 87 | function update(deltaTime){ |
| 97 | var palmPosition = Controller.getSpatialControlPosition(controllerID); | 88 | var handPose = (hand == "right") ? MyAvatar.rightHandPose : MyAvatar.leftHandPose; |
| 89 | var palmPosition = handPose.translation; | ||
| 98 | controllerActive = (Vec3.length(palmPosition) > 0); | 90 | controllerActive = (Vec3.length(palmPosition) > 0); |
| 99 | if(!controllerActive){ | 91 | if(!controllerActive){ |
| 100 | return; | 92 | return; |
| 101 | } | 93 | } |
| 102 | 94 | ||
| 103 | stickOrientation = Controller.getSpatialControlRawRotation(controllerID); | 95 | stickOrientation = handPose.rotation; |
| 104 | var adjustment = Quat.fromPitchYawRollDegrees(180, 0, 0); | 96 | var adjustment = Quat.fromPitchYawRollDegrees(180, 0, 0); |
| 105 | stickOrientation = Quat.multiply(stickOrientation, adjustment); | 97 | stickOrientation = Quat.multiply(stickOrientation, adjustment); |
| 106 | 98 | ||
| @@ -0,0 +1,77 @@ | |||
| 1 | ControllerTest = function() { | ||
| 2 | var standard = Controller.Standard; | ||
| 3 | var actions = Controller.Actions; | ||
| 4 | var xbox = Controller.Hardware.GamePad; | ||
| 5 | this.mappingEnabled = false; | ||
| 6 | this.mapping = Controller.newMapping(); | ||
| 7 | this.mapping.from(standard.LX).when([standard.LB, standard.RB]).to(actions.Yaw); | ||
| 8 | this.mapping.from(standard.RX).to(actions.StepYaw); | ||
| 9 | this.mapping.from(standard.RY).invert().to(actions.Pitch); | ||
| 10 | this.mapping.from(standard.RY).invert().to(actions.Pitch); | ||
| 11 | |||
| 12 | |||
| 13 | var testMakeAxis = false; | ||
| 14 | if (testMakeAxis) { | ||
| 15 | this.mapping.makeAxis(standard.LB, standard.RB).pulse(0.25).scale(40.0).to(actions.StepYaw); | ||
| 16 | } | ||
| 17 | |||
| 18 | var testStepYaw = false; | ||
| 19 | if (!testMakeAxis && testStepYaw){ | ||
| 20 | this.mapping.from(standard.LB).pulse(0.10).invert().scale(40.0).to(actions.StepYaw); | ||
| 21 | this.mapping.from(standard.RB).pulse(0.10).scale(15.0).to(actions.StepYaw); | ||
| 22 | } | ||
| 23 | |||
| 24 | var testFunctionSource = false; | ||
| 25 | if (testFunctionSource) { | ||
| 26 | this.mapping.from(function(){ | ||
| 27 | return Math.sin(Date.now() / 250); | ||
| 28 | }).to(actions.Yaw); | ||
| 29 | } | ||
| 30 | |||
| 31 | var testFunctionDest = true; | ||
| 32 | if (testFunctionDest) { | ||
| 33 | this.mapping.from(standard.DU).pulse(1.0).to(function(value){ | ||
| 34 | if (value != 0.0) { | ||
| 35 | print(value); | ||
| 36 | } | ||
| 37 | }); | ||
| 38 | |||
| 39 | } | ||
| 40 | |||
| 41 | this.mapping.enable(); | ||
| 42 | this.mappingEnabled = true; | ||
| 43 | |||
| 44 | var dumpInputs = false; | ||
| 45 | if (dumpInputs) { | ||
| 46 | print("Actions"); | ||
| 47 | for (var prop in Controller.Actions) { | ||
| 48 | print("\t" + prop); | ||
| 49 | } | ||
| 50 | print("Standard"); | ||
| 51 | for (var prop in Controller.Standard) { | ||
| 52 | print("\t" + prop); | ||
| 53 | } | ||
| 54 | print("Hardware"); | ||
| 55 | for (var prop in Controller.Hardware) { | ||
| 56 | print("\t" + prop); | ||
| 57 | for (var prop2 in Controller.Hardware[prop]) { | ||
| 58 | print("\t\t" + prop2); | ||
| 59 | } | ||
| 60 | } | ||
| 61 | print("Done"); | ||
| 62 | } | ||
| 63 | |||
| 64 | var that = this; | ||
| 65 | Script.scriptEnding.connect(function() { | ||
| 66 | that.onCleanup(); | ||
| 67 | }); | ||
| 68 | } | ||
| 69 | |||
| 70 | ControllerTest.prototype.onCleanup = function() { | ||
| 71 | if (this.mappingEnabled) { | ||
| 72 | this.mapping.disable(); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | |||
| 77 | new ControllerTest(); | ||
| @@ -45,6 +45,12 @@ · 44 unmodified lines | |||
| 45 | green: 255, | 45 | green: 255, |
| 46 | blue: 255 | 46 | blue: 255 |
| 47 | }; | 47 | }; |
| 48 | |||
| 49 | var TRIGGER_CONTROLS = [ | ||
| 50 | Controller.Standard.LT, | ||
| 51 | Controller.Standard.RT, | ||
| 52 | ]; | ||
| 53 | |||
| 48 | 54 | ||
| 49 | PingPongGun.prototype = { | 55 | PingPongGun.prototype = { |
| 50 | hand: null, | 56 | hand: null, |
| @@ -53,11 +59,11 @@ · 2 unmodified lines | |||
| 53 | canShoot: false, | 59 | canShoot: false, |
| 54 | canShootTimeout: null, | 60 | canShootTimeout: null, |
| 55 | setRightHand: function() { | 61 | setRightHand: function() { |
| 56 | this.hand = 'RIGHT'; | 62 | this.hand = 1; |
| 57 | }, | 63 | }, |
| 58 | 64 | ||
| 59 | setLeftHand: function() { | 65 | setLeftHand: function() { |
| 60 | this.hand = 'LEFT'; | 66 | this.hand = 0; |
| 61 | }, | 67 | }, |
| 62 | 68 | ||
| 63 | startNearGrab: function() { | 69 | startNearGrab: function() { |
| @@ -92,12 +98,7 @@ · 28 unmodified lines | |||
| 92 | }, | 98 | }, |
| 93 | 99 | ||
| 94 | checkTriggerPressure: function(gunHand) { | 100 | checkTriggerPressure: function(gunHand) { |
| 95 | var handClickString = gunHand + "_HAND_CLICK"; | 101 | this.triggerValue = Controller.getValue(TRIGGER_CONTROLS[gunHand]); |
| 96 | |||
| 97 | var handClick = Controller.findAction(handClickString); | ||
| 98 | |||
| 99 | this.triggerValue = Controller.getActionValue(handClick); | ||
| 100 | |||
| 101 | if (this.triggerValue < RELOAD_THRESHOLD) { | 102 | if (this.triggerValue < RELOAD_THRESHOLD) { |
| 102 | // print('RELOAD'); | 103 | // print('RELOAD'); |
| 103 | this.canShoot = true; | 104 | this.canShoot = true; |
| @@ -33,12 +33,17 @@ · 32 unmodified lines | |||
| 33 | var MIN_POINT_DISTANCE = 0.01; | 33 | var MIN_POINT_DISTANCE = 0.01; |
| 34 | var STROKE_WIDTH = 0.02; | 34 | var STROKE_WIDTH = 0.02; |
| 35 | 35 | ||
| 36 | var TRIGGER_CONTROLS = [ | ||
| 37 | Controller.Standard.LT, | ||
| 38 | Controller.Standard.RT, | ||
| 39 | ]; | ||
| 40 | |||
| 36 | this.setRightHand = function () { | 41 | this.setRightHand = function () { |
| 37 | this.hand = 'RIGHT'; | 42 | this.hand = 1; |
| 38 | } | 43 | } |
| 39 | 44 | ||
| 40 | this.setLeftHand = function () { | 45 | this.setLeftHand = function () { |
| 41 | this.hand = 'LEFT'; | 46 | this.hand = 0; |
| 42 | } | 47 | } |
| 43 | 48 | ||
| 44 | this.startNearGrab = function () { | 49 | this.startNearGrab = function () { |
| @@ -46,11 +51,7 @@ · 1 unmodified line | |||
| 46 | } | 51 | } |
| 47 | 52 | ||
| 48 | this.toggleWithTriggerPressure = function () { | 53 | this.toggleWithTriggerPressure = function () { |
| 49 | var handClickString = this.whichHand + "_HAND_CLICK"; | 54 | this.triggerValue = Controller.getValue(TRIGGER_CONTROLS[this.whichHand]); |
| 50 | |||
| 51 | var handClick = Controller.findAction(handClickString); | ||
| 52 | |||
| 53 | this.triggerValue = Controller.getActionValue(handClick); | ||
| 54 | if (this.triggerValue < DISABLE_SPRAY_THRESHOLD && this.spraying === true) { | 55 | if (this.triggerValue < DISABLE_SPRAY_THRESHOLD && this.spraying === true) { |
| 55 | this.spraying = false; | 56 | this.spraying = false; |
| 56 | this.disableStream(); | 57 | this.disableStream(); |
| @@ -2,7 +2,12 @@ set(TARGET_NAME interface) · 1 unmodified line | |||
| 2 | project(${TARGET_NAME}) | 2 | project(${TARGET_NAME}) |
| 3 | 3 | ||
| 4 | # set a default root dir for each of our optional externals if it was not passed | 4 | # set a default root dir for each of our optional externals if it was not passed |
| 5 | set(OPTIONAL_EXTERNALS "Faceshift" "LeapMotion" "RtMidi" "RSSDK" "3DConnexionClient" "iViewHMD") | 5 | set(OPTIONAL_EXTERNALS "LeapMotion" "RtMidi" "RSSDK" "iViewHMD") |
| 6 | |||
| 7 | if(WIN32) | ||
| 8 | list(APPEND OPTIONAL_EXTERNALS "3DConnexionClient") | ||
| 9 | endif() | ||
| 10 | |||
| 6 | foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) | 11 | foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) |
| 7 | string(TOUPPER ${EXTERNAL} ${EXTERNAL}_UPPERCASE) | 12 | string(TOUPPER ${EXTERNAL} ${EXTERNAL}_UPPERCASE) |
| 8 | if (NOT ${${EXTERNAL}_UPPERCASE}_ROOT_DIR) | 13 | if (NOT ${${EXTERNAL}_UPPERCASE}_ROOT_DIR) |
| @@ -99,15 +104,17 @@ endif() · 90 unmodified lines | |||
| 99 | link_hifi_libraries(shared octree environment gpu gl procedural model render | 104 | link_hifi_libraries(shared octree environment gpu gl procedural model render |
| 100 | fbx networking model-networking entities avatars | 105 | fbx networking model-networking entities avatars |
| 101 | audio audio-client animation script-engine physics | 106 | audio audio-client animation script-engine physics |
| 102 | render-utils entities-renderer ui auto-updater | 107 | render-utils entities-renderer ui auto-updater |
| 103 | plugins display-plugins input-plugins) | 108 | controllers plugins display-plugins input-plugins ) |
| 104 | 109 | ||
| 110 | #fixme find a way to express faceshift as a plugin | ||
| 105 | target_bullet() | 111 | target_bullet() |
| 106 | target_glew() | 112 | target_glew() |
| 107 | target_opengl() | 113 | target_opengl() |
| 108 | 114 | ||
| 109 | 115 | if (WIN32 OR APPLE) | |
| 110 | add_dependency_external_projects(sdl2) | 116 | target_faceshift() |
| 117 | endif() | ||
| 111 | 118 | ||
| 112 | # perform standard include and linking for found externals | 119 | # perform standard include and linking for found externals |
| 113 | foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) | 120 | foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) |
| @@ -1,26 +0,0 @@ | |||
| 1 | |||
| 2 | Instructions for adding the Faceshift library to Interface | ||
| 3 | Stephen Birarda, July 18th, 2014 | ||
| 4 | |||
| 5 | OS X users: You can also use homebrew to get the Faceshift library by tapping our repo - highfidelity/homebrew-formulas | ||
| 6 | and then calling 'brew install highfidelity/formulas/faceshift'. | ||
| 7 | |||
| 8 | You can download the Faceshift SDK from http://download.faceshift.com/faceshift-network.zip. | ||
| 9 | |||
| 10 | Create a ‘faceshift’ folder under interface/externals. | ||
| 11 | |||
| 12 | You may optionally choose to place this folder in a location outside the repository (so you can re-use with different checkouts and different projects). | ||
| 13 | |||
| 14 | If so our CMake find module expects you to set the ENV variable 'HIFI_LIB_DIR' to a directory containing a subfolder ‘faceshift’ that contains the lib and include folders. | ||
| 15 | |||
| 16 | 1. Build a Faceshift static library from the fsbinarystream.cpp file. | ||
| 17 | Windows: Win32 console application; no precompiled header or SDL checks; no ATL or MFC headers; Project Properties, Configuration Type = Static Library (.lib). | ||
| 18 | |||
| 19 | 2. Copy the library files to the ‘lib’ folder in your Faceshift folder. | ||
| 20 | OSX: If you build a release version call it libfaceshift.a. The debug version should be called libfaceshiftd.a. | ||
| 21 | Windows: The release and debug versions should be called faceshift.lib and faceshiftd.lib, respectively. Copy them into a ‘Win32’ folder in your ‘lib’ folder. | ||
| 22 | |||
| 23 | 3. Copy the fsbinarystream.h header file from the Faceshift SDK into the ‘include’ folder in your Faceshift folder. | ||
| 24 | |||
| 25 | 4. Clear your build directory, run cmake and build, and you should be all set. | ||
| 26 | |||
| @@ -1,13 +0,0 @@ | |||
| 1 | |||
| 2 | Instructions for adding the SDL library (SDL2) to Interface | ||
| 3 | David Rowe, 11 Jan 2015 | ||
| 4 | |||
| 5 | You can download the SDL development library from https://www.libsdl.org/. Interface has been tested with version 2.0.3. | ||
| 6 | |||
| 7 | 1. Copy the include and lib folders into the interface/externals/sdl2 folder. | ||
| 8 | This readme.txt should be there as well. | ||
| 9 | |||
| 10 | You may optionally choose to copy the SDK folders to a location outside the repository (so you can re-use with different checkouts and different projects). | ||
| 11 | If so our CMake find module expects you to set the ENV variable 'HIFI_LIB_DIR' to a directory containing a subfolder 'sdl2' that contains the two folders mentioned above. | ||
| 12 | |||
| 13 | 2. Clear your build directory, run cmake and build, and you should be all set. | ||
| @@ -1,10 +0,0 @@ | |||
| 1 | |||
| 2 | Instructions for adding the Sixense driver to Interface | ||
| 3 | Andrzej Kapolka, November 18, 2013 | ||
| 4 | |||
| 5 | 1. Copy the Sixense sdk folders (bin, include, lib, and samples) into the interface/external/Sixense folder. This readme.txt should be there as well. | ||
| 6 | |||
| 7 | You may optionally choose to copy the SDK folders to a location outside the repository (so you can re-use with different checkouts and different projects). | ||
| 8 | If so our CMake find module expects you to set the ENV variable 'HIFI_LIB_DIR' to a directory containing a subfolder 'sixense' that contains the folders mentioned above. | ||
| 9 | |||
| 10 | 3. Delete your build directory, run cmake and build, and you should be all set. | ||
| @@ -0,0 +1,31 @@ | |||
| 1 | { | ||
| 2 | "name": "Hydra to Standard", | ||
| 3 | "channels": [ | ||
| 4 | { "from": "Hydra.LY", "filters": "invert", "to": "Standard.LY" }, | ||
| 5 | { "from": "Hydra.LX", "to": "Standard.LX" }, | ||
| 6 | { "from": "Hydra.LT", "to": "Standard.LT" }, | ||
| 7 | { "from": "Hydra.RY", "filters": "invert", "to": "Standard.RY" }, | ||
| 8 | { "from": "Hydra.RX", "to": "Standard.RX" }, | ||
| 9 | { "from": "Hydra.RT", "to": "Standard.RT" }, | ||
| 10 | |||
| 11 | { "from": "Hydra.LB", "to": "Standard.LB" }, | ||
| 12 | { "from": "Hydra.LS", "to": "Standard.LS" }, | ||
| 13 | { "from": "Hydra.RB", "to": "Standard.RB" }, | ||
| 14 | { "from": "Hydra.RS", "to": "Standard.RS" }, | ||
| 15 | |||
| 16 | { "from": "Hydra.L0", "to": "Standard.Back" }, | ||
| 17 | { "from": "Hydra.L1", "to": "Standard.DL" }, | ||
| 18 | { "from": "Hydra.L2", "to": "Standard.DD" }, | ||
| 19 | { "from": "Hydra.L3", "to": "Standard.DR" }, | ||
| 20 | { "from": "Hydra.L4", "to": "Standard.DU" }, | ||
| 21 | |||
| 22 | { "from": "Hydra.R0", "to": "Standard.Start" }, | ||
| 23 | { "from": "Hydra.R1", "to": "Standard.X" }, | ||
| 24 | { "from": "Hydra.R2", "to": "Standard.A" }, | ||
| 25 | { "from": "Hydra.R3", "to": "Standard.B" }, | ||
| 26 | { "from": "Hydra.R4", "to": "Standard.Y" }, | ||
| 27 | |||
| 28 | { "from": "Hydra.LeftHand", "to": "Standard.LeftHand" }, | ||
| 29 | { "from": "Hydra.RightHand", "to": "Standard.RightHand" } | ||
| 30 | ] | ||
| 31 | } | ||
| @@ -0,0 +1,86 @@ | |||
| 1 | { | ||
| 2 | "name": "Keyboard/Mouse to Actions", | ||
| 3 | "channels": [ | ||
| 4 | |||
| 5 | |||
| 6 | { "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] }, | ||
| 7 | "when": [ "Application.InHMD", "Application.ComfortMode", "Keyboard.RightMouseClick" ], | ||
| 8 | "to": "Actions.StepYaw", | ||
| 9 | "filters": | ||
| 10 | [ | ||
| 11 | "constrainToInteger", | ||
| 12 | { "type": "pulse", "interval": 0.5 }, | ||
| 13 | { "type": "scale", "scale": 15 } | ||
| 14 | ] | ||
| 15 | }, | ||
| 16 | |||
| 17 | { "from": { "makeAxis" : [ | ||
| 18 | ["Keyboard.A", "Keyboard.Left", "Keyboard.TouchpadLeft"], | ||
| 19 | ["Keyboard.D", "Keyboard.Right", "Keyboard.TouchpadRight"] | ||
| 20 | ] | ||
| 21 | }, | ||
| 22 | "when": [ "Application.InHMD", "Application.ComfortMode" ], | ||
| 23 | "to": "Actions.StepYaw", | ||
| 24 | "filters": | ||
| 25 | [ | ||
| 26 | { "type": "pulse", "interval": 0.5 }, | ||
| 27 | { "type": "scale", "scale": 15 } | ||
| 28 | ] | ||
| 29 | }, | ||
| 30 | |||
| 31 | { "from": { "makeAxis" : [ | ||
| 32 | ["Keyboard.A", "Keyboard.Left", "Keyboard.TouchpadLeft"], | ||
| 33 | ["Keyboard.D", "Keyboard.Right", "Keyboard.TouchpadRight"] | ||
| 34 | ] | ||
| 35 | }, | ||
| 36 | "to": "Actions.Yaw" | ||
| 37 | }, | ||
| 38 | |||
| 39 | { "from": { "makeAxis" : ["Keyboard.MouseMoveLeft", "Keyboard.MouseMoveRight"] }, | ||
| 40 | "when": "Keyboard.RightMouseClick", | ||
| 41 | "to": "Actions.Yaw" | ||
| 42 | }, | ||
| 43 | |||
| 44 | { "from": "Keyboard.A", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" }, | ||
| 45 | { "from": "Keyboard.D", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" }, | ||
| 46 | { "from": "Keyboard.A", "when": "Keyboard.RightMouseClick", "to": "Actions.LATERAL_LEFT" }, | ||
| 47 | { "from": "Keyboard.D", "when": "Keyboard.RightMouseClick", "to": "Actions.LATERAL_RIGHT" }, | ||
| 48 | { "from": "Keyboard.E", "when": "Keyboard.Shift", "to": "Actions.BOOM_IN", "filters": [ { "type": "scale", "scale": 0.05 } ] }, | ||
| 49 | { "from": "Keyboard.C", "when": "Keyboard.Shift", "to": "Actions.BOOM_OUT", "filters": [ { "type": "scale", "scale": 0.05 } ] }, | ||
| 50 | { "from": "Keyboard.S", "when": "Keyboard.Shift", "to": "Actions.PITCH_DOWN" }, | ||
| 51 | { "from": "Keyboard.W", "when": "Keyboard.Shift", "to": "Actions.PITCH_UP" }, | ||
| 52 | |||
| 53 | { "from": "Keyboard.W", "to": "Actions.LONGITUDINAL_FORWARD" }, | ||
| 54 | { "from": "Keyboard.S", "to": "Actions.LONGITUDINAL_BACKWARD" }, | ||
| 55 | { "from": "Keyboard.C", "to": "Actions.VERTICAL_DOWN" }, | ||
| 56 | { "from": "Keyboard.E", "to": "Actions.VERTICAL_UP" }, | ||
| 57 | |||
| 58 | { "from": "Keyboard.Left", "when": "Keyboard.RightMouseClick", "to": "Actions.LATERAL_LEFT" }, | ||
| 59 | { "from": "Keyboard.Right", "when": "Keyboard.RightMouseClick", "to": "Actions.LATERAL_RIGHT" }, | ||
| 60 | { "from": "Keyboard.Left", "when": "Keyboard.Shift", "to": "Actions.LATERAL_LEFT" }, | ||
| 61 | { "from": "Keyboard.Right", "when": "Keyboard.Shift", "to": "Actions.LATERAL_RIGHT" }, | ||
| 62 | { "from": "Keyboard.Down", "when": "Keyboard.Shift", "to": "Actions.PITCH_DOWN" }, | ||
| 63 | { "from": "Keyboard.Up", "when": "Keyboard.Shift", "to": "Actions.PITCH_UP" }, | ||
| 64 | |||
| 65 | { "from": "Keyboard.Up", "to": "Actions.LONGITUDINAL_FORWARD" }, | ||
| 66 | { "from": "Keyboard.Down", "to": "Actions.LONGITUDINAL_BACKWARD" }, | ||
| 67 | |||
| 68 | { "from": "Keyboard.PgDown", "to": "Actions.VERTICAL_DOWN" }, | ||
| 69 | { "from": "Keyboard.PgUp", "to": "Actions.VERTICAL_UP" }, | ||
| 70 | |||
| 71 | { "from": "Keyboard.MouseMoveUp", "when": "Keyboard.RightMouseClick", "to": "Actions.PITCH_UP" }, | ||
| 72 | { "from": "Keyboard.MouseMoveDown", "when": "Keyboard.RightMouseClick", "to": "Actions.PITCH_DOWN" }, | ||
| 73 | |||
| 74 | { "from": "Keyboard.TouchpadDown", "to": "Actions.PITCH_DOWN" }, | ||
| 75 | { "from": "Keyboard.TouchpadUp", "to": "Actions.PITCH_UP" }, | ||
| 76 | |||
| 77 | { "from": "Keyboard.MouseWheelUp", "to": "Actions.LATERAL_RIGHT" }, | ||
| 78 | { "from": "Keyboard.MouseWheelDown", "to": "Actions.LATERAL_LEFT" }, | ||
| 79 | { "from": "Keyboard.MouseWheelLeft", "to": "Actions.BOOM_OUT", "filters": [ { "type": "scale", "scale": 0.02 } ]}, | ||
| 80 | { "from": "Keyboard.MouseWheelRight", "to": "Actions.BOOM_IN", "filters": [ { "type": "scale", "scale": 0.02 } ]}, | ||
| 81 | |||
| 82 | { "from": "Keyboard.Space", "to": "Actions.SHIFT" }, | ||
| 83 | { "from": "Keyboard.R", "to": "Actions.ACTION1" }, | ||
| 84 | { "from": "Keyboard.T", "to": "Actions.ACTION2" } | ||
| 85 | ] | ||
| 86 | } | ||
| @@ -0,0 +1,24 @@ | |||
| 1 | { | ||
| 2 | "name": "Full Mapping config including the standard hydra and gamepad and one more thing", | ||
| 3 | "mappings": [ | ||
| 4 | { "src": "./mapping-hydra.json" }, | ||
| 5 | { "src": "./mapping-xbox.json" }, | ||
| 6 | { | ||
| 7 | "name": "example mapping for standard to js function", | ||
| 8 | "channels": [ { | ||
| 9 | "from": "Standard.B", | ||
| 10 | "to": { | ||
| 11 | "type":"js", | ||
| 12 | "function": "function(value){ print(\"Standard.B = \" + value );}" | ||
| 13 | } | ||
| 14 | }, { | ||
| 15 | "from": "Standard.B", | ||
| 16 | "to": { | ||
| 17 | "type":"js", | ||
| 18 | "src": "http://www.theNextBigThing.com/hifiInputSignalHandler.js" | ||
| 19 | } | ||
| 20 | } | ||
| 21 | ] | ||
| 22 | } | ||
| 23 | ] | ||
| 24 | } | ||
| @@ -0,0 +1,36 @@ | |||
| 1 | { | ||
| 2 | "name": "example mapping from Standard to actions", | ||
| 3 | "channels": [ { | ||
| 4 | "from": "Standard.LY", | ||
| 5 | "filters": [ { | ||
| 6 | "type": "clamp", | ||
| 7 | "min": 0, | ||
| 8 | "max": 1 | ||
| 9 | } | ||
| 10 | ], | ||
| 11 | "to": "Actions.Forward" | ||
| 12 | }, { | ||
| 13 | "from": "Standard.LY", | ||
| 14 | "filters": [ { | ||
| 15 | "type": "clamp", | ||
| 16 | "min": -1, | ||
| 17 | "max": 0 | ||
| 18 | }, { | ||
| 19 | "type": "invert" | ||
| 20 | } | ||
| 21 | ], | ||
| 22 | "to": "Actions.Backward" | ||
| 23 | }, { | ||
| 24 | "from": "Standard.LX", | ||
| 25 | "filters": [ { | ||
| 26 | "type": "scale", | ||
| 27 | "scale": 2.0 | ||
| 28 | } | ||
| 29 | ], | ||
| 30 | "to": "Actions.Yaw" | ||
| 31 | }, { | ||
| 32 | "from": "Standard.A", | ||
| 33 | "to": "Actions.Action0" | ||
| 34 | } | ||
| 35 | ] | ||
| 36 | } | ||
| @@ -0,0 +1,43 @@ | |||
| 1 | { | ||
| 2 | "name": "Standard to Action", | ||
| 3 | "channels": [ | ||
| 4 | { "from": "Standard.LY", "to": "Actions.TranslateZ" }, | ||
| 5 | { "from": "Standard.LX", "to": "Actions.TranslateX" }, | ||
| 6 | { "from": "Standard.RX", "to": "Actions.Yaw" }, | ||
| 7 | { "from": "Standard.RY", "to": "Actions.Pitch" }, | ||
| 8 | { | ||
| 9 | "from": "Standard.DU", | ||
| 10 | "to": "Actions.LONGITUDINAL_FORWARD", | ||
| 11 | "filters": [ { "type": "scale", "scale": 0.5 } ] | ||
| 12 | }, | ||
| 13 | { | ||
| 14 | "from": "Standard.DD", | ||
| 15 | "to": "Actions.LONGITUDINAL_BACKWARD", | ||
| 16 | "filters": [ { "type": "scale", "scale": 0.5 } ] | ||
| 17 | }, | ||
| 18 | { | ||
| 19 | "from": "Standard.DR", | ||
| 20 | "to": "Actions.LATERAL_RIGHT", | ||
| 21 | "filters": [ { "type": "scale", "scale": 0.5 } ] | ||
| 22 | }, | ||
| 23 | { | ||
| 24 | "from": "Standard.DL", | ||
| 25 | "to": "Actions.LATERAL_LEFT", | ||
| 26 | "filters": [ { "type": "scale", "scale": 0.5 } ] | ||
| 27 | }, | ||
| 28 | { "from": "Standard.Y", "to": "Actions.VERTICAL_UP" }, | ||
| 29 | { "from": "Standard.X", "to": "Actions.VERTICAL_DOWN" }, | ||
| 30 | { | ||
| 31 | "from": "Standard.RT", | ||
| 32 | "to": "Actions.BOOM_IN", | ||
| 33 | "filters": [ { "type": "scale", "scale": 0.1 } ] | ||
| 34 | }, | ||
| 35 | { | ||
| 36 | "from": "Standard.LT", | ||
| 37 | "to": "Actions.BOOM_OUT", | ||
| 38 | "filters": [ { "type": "scale", "scale": 0.1 } ] | ||
| 39 | }, | ||
| 40 | { "from": "Standard.LeftHand", "to": "Actions.LEFT_HAND" }, | ||
| 41 | { "from": "Standard.RightHand", "to": "Actions.RIGHT_HAND" } | ||
| 42 | ] | ||
| 43 | } | ||
| @@ -0,0 +1,36 @@ | |||
| 1 | { | ||
| 2 | "name": "Standard to Action", | ||
| 3 | "channels": [ | ||
| 4 | { "from": "Standard.LY", "to": "Actions.TranslateZ" }, | ||
| 5 | { "from": "Standard.LX", "to": "Actions.TranslateX" }, | ||
| 6 | |||
| 7 | { "from": "Standard.RX", | ||
| 8 | "when": [ "Application.InHMD", "Application.ComfortMode" ], | ||
| 9 | "to": "Actions.StepYaw", | ||
| 10 | "filters": | ||
| 11 | [ | ||
| 12 | { "type": "pulse", "interval": 0.5 }, | ||
| 13 | { "type": "scale", "scale": 15 } | ||
| 14 | ] | ||
| 15 | }, | ||
| 16 | |||
| 17 | |||
| 18 | { "from": "Standard.RX", "to": "Actions.Yaw" }, | ||
| 19 | { "from": "Standard.RY", "to": "Actions.Pitch" }, | ||
| 20 | |||
| 21 | |||
| 22 | { "from": [ "Standard.DU", "Standard.DL", "Standard.DR", "Standard.DD" ], "to": "Standard.LeftPrimaryThumb" }, | ||
| 23 | { "from": "Standard.Back", "to": "Standard.LeftSecondaryThumb" }, | ||
| 24 | |||
| 25 | { "from": [ "Standard.A", "Standard.B", "Standard.X", "Standard.Y" ], "to": "Standard.RightPrimaryThumb" }, | ||
| 26 | { "from": "Standard.Start", "to": "Standard.RightSecondaryThumb" }, | ||
| 27 | |||
| 28 | { "from": "Standard.LT", "to": "Actions.LeftHandClick" }, | ||
| 29 | { "from": "Standard.RT", "to": "Actions.RightHandClick" }, | ||
| 30 | |||
| 31 | { "from": "Standard.LeftHand", "to": "Actions.LeftHand" }, | ||
| 32 | { "from": "Standard.RightHand", "to": "Actions.RightHand" } | ||
| 33 | ] | ||
| 34 | } | ||
| 35 | |||
| 36 | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | { | ||
| 2 | "name": "Vive to Standard", | ||
| 3 | "channels": [ | ||
| 4 | { "from": "Vive.LY", "filters": [ "invert", { "type": "deadZone", "min": 0.7 } ], "to": "Standard.LY" }, | ||
| 5 | { "from": "Vive.LX", "filters": { "type": "deadZone", "min": 0.7 }, "to": "Standard.LX" }, | ||
| 6 | |||
| 7 | { "from": "Vive.LT", "to": "Standard.LT" }, | ||
| 8 | { "from": "Vive.LB", "to": "Standard.LB" }, | ||
| 9 | { "from": "Vive.LS", "to": "Standard.LS" }, | ||
| 10 | |||
| 11 | { "from": "Vive.RY", "filters": "invert", "to": "Standard.RY" }, | ||
| 12 | { "from": "Vive.RX", "to": "Standard.RX" }, | ||
| 13 | |||
| 14 | { "from": "Vive.RT", "to": "Standard.RT" }, | ||
| 15 | { "from": "Vive.RB", "to": "Standard.RB" }, | ||
| 16 | { "from": "Vive.RS", "to": "Standard.RS" }, | ||
| 17 | |||
| 18 | { "from": "Vive.LeftPrimaryThumb", "to": "Standard.LeftPrimaryThumb" }, | ||
| 19 | { "from": "Vive.RightPrimaryThumb", "to": "Standard.RightPrimaryThumb" }, | ||
| 20 | { "from": "Vive.LeftSecondaryThumb", "to": "Standard.LeftSecondaryThumb" }, | ||
| 21 | { "from": "Vive.RightSecondaryThumb", "to": "Standard.RightSecondaryThumb" }, | ||
| 22 | |||
| 23 | { "from": "Vive.LeftHand", "to": "Standard.LeftHand" }, | ||
| 24 | { "from": "Vive.RightHand", "to": "Standard.RightHand" } | ||
| 25 | ] | ||
| 26 | } | ||
| @@ -0,0 +1,29 @@ | |||
| 1 | { | ||
| 2 | "name": "XBox to Standard", | ||
| 3 | "channels": [ | ||
| 4 | { "from": "GamePad.LY", "to": "Standard.LY" }, | ||
| 5 | { "from": "GamePad.LX", "to": "Standard.LX" }, | ||
| 6 | { "from": "GamePad.LT", "to": "Standard.LT" }, | ||
| 7 | { "from": "GamePad.LB", "to": "Standard.LB" }, | ||
| 8 | { "from": "GamePad.LS", "to": "Standard.LS" }, | ||
| 9 | |||
| 10 | { "from": "GamePad.RY", "to": "Standard.RY" }, | ||
| 11 | { "from": "GamePad.RX", "to": "Standard.RX" }, | ||
| 12 | { "from": "GamePad.RT", "to": "Standard.RT" }, | ||
| 13 | { "from": "GamePad.RB", "to": "Standard.RB" }, | ||
| 14 | { "from": "GamePad.RS", "to": "Standard.RS" }, | ||
| 15 | |||
| 16 | { "from": "GamePad.Back", "to": "Standard.Back" }, | ||
| 17 | { "from": "GamePad.Start", "to": "Standard.Start" }, | ||
| 18 | |||
| 19 | { "from": "GamePad.DU", "to": "Standard.DU" }, | ||
| 20 | { "from": "GamePad.DD", "to": "Standard.DD" }, | ||
| 21 | { "from": "GamePad.DL", "to": "Standard.DL" }, | ||
| 22 | { "from": "GamePad.DR", "to": "Standard.DR" }, | ||
| 23 | |||
| 24 | { "from": "GamePad.A", "to": "Standard.A" }, | ||
| 25 | { "from": "GamePad.B", "to": "Standard.B" }, | ||
| 26 | { "from": "GamePad.X", "to": "Standard.X" }, | ||
| 27 | { "from": "GamePad.Y", "to": "Standard.Y" } | ||
| 28 | ] | ||
| 29 | } | ||
| @@ -0,0 +1,111 @@ | |||
| 1 | import QtQuick 2.1 | ||
| 2 | import QtQuick.Controls 1.0 | ||
| 3 | import QtQuick.Layouts 1.0 | ||
| 4 | import QtQuick.Dialogs 1.0 | ||
| 5 | |||
| 6 | Rectangle { | ||
| 7 | id: root | ||
| 8 | property int size: 64 | ||
| 9 | width: size | ||
| 10 | height: size | ||
| 11 | color: 'black' | ||
| 12 | property int controlId: 0 | ||
| 13 | property real value: 0.5 | ||
| 14 | property int scrollWidth: 1 | ||
| 15 | property real min: 0.0 | ||
| 16 | property real max: 1.0 | ||
| 17 | property bool log: false | ||
| 18 | property real range: max - min | ||
| 19 | property color lineColor: 'yellow' | ||
| 20 | property bool bar: false | ||
| 21 | property real lastHeight: -1 | ||
| 22 | property string label: "" | ||
| 23 | |||
| 24 | function update() { | ||
| 25 | value = Controller.getValue(controlId); | ||
| 26 | if (log) { | ||
| 27 | var log = Math.log(10) / Math.log(Math.abs(value)); | ||
| 28 | var sign = Math.sign(value); | ||
| 29 | value = log * sign; | ||
| 30 | } | ||
| 31 | canvas.requestPaint(); | ||
| 32 | } | ||
| 33 | |||
| 34 | function drawHeight() { | ||
| 35 | if (value < min) { | ||
| 36 | return 0; | ||
| 37 | } | ||
| 38 | if (value > max) { | ||
| 39 | return height; | ||
| 40 | } | ||
| 41 | return ((value - min) / range) * height; | ||
| 42 | } | ||
| 43 | |||
| 44 | Timer { | ||
| 45 | interval: 50; running: true; repeat: true | ||
| 46 | onTriggered: root.update() | ||
| 47 | } | ||
| 48 | |||
| 49 | Canvas { | ||
| 50 | id: canvas | ||
| 51 | anchors.fill: parent | ||
| 52 | antialiasing: false | ||
| 53 | |||
| 54 | Text { | ||
| 55 | anchors.top: parent.top | ||
| 56 | text: root.label | ||
| 57 | color: 'white' | ||
| 58 | } | ||
| 59 | |||
| 60 | Text { | ||
| 61 | anchors.right: parent.right | ||
| 62 | anchors.top: parent.top | ||
| 63 | text: root.max | ||
| 64 | color: 'white' | ||
| 65 | } | ||
| 66 | |||
| 67 | Text { | ||
| 68 | anchors.right: parent.right | ||
| 69 | anchors.bottom: parent.bottom | ||
| 70 | text: root.min | ||
| 71 | color: 'white' | ||
| 72 | } | ||
| 73 | |||
| 74 | function scroll() { | ||
| 75 | var ctx = canvas.getContext('2d'); | ||
| 76 | var image = ctx.getImageData(0, 0, canvas.width, canvas.height); | ||
| 77 | ctx.beginPath(); | ||
| 78 | ctx.clearRect(0, 0, canvas.width, canvas.height); | ||
| 79 | ctx.drawImage(image, -root.scrollWidth, 0, canvas.width, canvas.height) | ||
| 80 | ctx.restore() | ||
| 81 | } | ||
| 82 | |||
| 83 | onPaint: { | ||
| 84 | scroll(); | ||
| 85 | var ctx = canvas.getContext('2d'); | ||
| 86 | ctx.save(); | ||
| 87 | var currentHeight = root.drawHeight(); | ||
| 88 | if (root.lastHeight == -1) { | ||
| 89 | root.lastHeight = currentHeight | ||
| 90 | } | ||
| 91 | |||
| 92 | // var x = canvas.width - root.drawWidth; | ||
| 93 | // var y = canvas.height - drawHeight; | ||
| 94 | // ctx.fillStyle = root.color | ||
| 95 | // ctx.fillRect(x, y, root.drawWidth, root.bar ? drawHeight : 1) | ||
| 96 | // ctx.fill(); | ||
| 97 | // ctx.restore() | ||
| 98 | |||
| 99 | |||
| 100 | ctx.beginPath(); | ||
| 101 | ctx.lineWidth = 1 | ||
| 102 | ctx.strokeStyle = root.lineColor | ||
| 103 | ctx.moveTo(canvas.width - root.scrollWidth, root.lastHeight).lineTo(canvas.width, currentHeight) | ||
| 104 | ctx.stroke() | ||
| 105 | ctx.restore() | ||
| 106 | root.lastHeight = currentHeight | ||
| 107 | } | ||
| 108 | } | ||
| 109 | } | ||
| 110 | |||
| 111 | |||
| @@ -0,0 +1,161 @@ | |||
| 1 | import QtQuick 2.3 | ||
| 2 | import QtQuick.Controls 1.2 | ||
| 3 | import QtQuick.Layouts 1.0 | ||
| 4 | import QtQuick.Dialogs 1.0 | ||
| 5 | |||
| 6 | import "controller" | ||
| 7 | import "controls" as HifiControls | ||
| 8 | import "styles" | ||
| 9 | |||
| 10 | HifiControls.VrDialog { | ||
| 11 | id: root | ||
| 12 | HifiConstants { id: hifi } | ||
| 13 | title: "Controller Test" | ||
| 14 | resizable: true | ||
| 15 | contentImplicitWidth: clientArea.implicitWidth | ||
| 16 | contentImplicitHeight: clientArea.implicitHeight | ||
| 17 | backgroundColor: "beige" | ||
| 18 | |||
| 19 | property var actions: Controller.Actions | ||
| 20 | property var standard: Controller.Standard | ||
| 21 | property var hydra: null | ||
| 22 | property var testMapping: null | ||
| 23 | property bool testMappingEnabled: false | ||
| 24 | property var xbox: null | ||
| 25 | |||
| 26 | function buildMapping() { | ||
| 27 | testMapping = Controller.newMapping(); | ||
| 28 | testMapping.fromQml(standard.RY).invert().toQml(actions.Pitch); | ||
| 29 | testMapping.fromQml(function(){ | ||
| 30 | return Math.sin(Date.now() / 250); | ||
| 31 | }).toQml(actions.Yaw); | ||
| 32 | //testMapping.makeAxis(standard.LB, standard.RB).to(actions.Yaw); | ||
| 33 | // Step yaw takes a number of degrees | ||
| 34 | testMapping.fromQml(standard.LB).pulse(0.10).invert().scale(40.0).toQml(actions.StepYaw); | ||
| 35 | testMapping.fromQml(standard.RB).pulse(0.10).scale(15.0).toQml(actions.StepYaw); | ||
| 36 | testMapping.fromQml(standard.RX).scale(15.0).toQml(actions.StepYaw); | ||
| 37 | } | ||
| 38 | |||
| 39 | function toggleMapping() { | ||
| 40 | testMapping.enable(!testMappingEnabled); | ||
| 41 | testMappingEnabled = !testMappingEnabled; | ||
| 42 | } | ||
| 43 | |||
| 44 | Component.onCompleted: { | ||
| 45 | enabled = true | ||
| 46 | var xboxRegex = /^GamePad/; | ||
| 47 | var hydraRegex = /^Hydra/; | ||
| 48 | for (var prop in Controller.Hardware) { | ||
| 49 | if(xboxRegex.test(prop)) { | ||
| 50 | root.xbox = Controller.Hardware[prop] | ||
| 51 | print("found xbox") | ||
| 52 | continue | ||
| 53 | } | ||
| 54 | if (hydraRegex.test(prop)) { | ||
| 55 | root.hydra = Controller.Hardware[prop] | ||
| 56 | print("found hydra") | ||
| 57 | continue | ||
| 58 | } | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | Column { | ||
| 63 | id: clientArea | ||
| 64 | spacing: 12 | ||
| 65 | x: root.clientX | ||
| 66 | y: root.clientY | ||
| 67 | |||
| 68 | Row { | ||
| 69 | spacing: 8 | ||
| 70 | |||
| 71 | Button { | ||
| 72 | text: !root.testMapping ? "Build Mapping" : (root.testMappingEnabled ? "Disable Mapping" : "Enable Mapping") | ||
| 73 | onClicked: { | ||
| 74 | |||
| 75 | if (!root.testMapping) { | ||
| 76 | root.buildMapping() | ||
| 77 | } else { | ||
| 78 | root.toggleMapping(); | ||
| 79 | } | ||
| 80 | } | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | Row { | ||
| 85 | Standard { device: root.standard; label: "Standard"; width: 180 } | ||
| 86 | } | ||
| 87 | |||
| 88 | Row { | ||
| 89 | spacing: 8 | ||
| 90 | Xbox { device: root.xbox; label: "XBox"; width: 180 } | ||
| 91 | Hydra { device: root.hydra; width: 180 } | ||
| 92 | } | ||
| 93 | |||
| 94 | Row { | ||
| 95 | spacing: 4 | ||
| 96 | ScrollingGraph { | ||
| 97 | controlId: Controller.Actions.Yaw | ||
| 98 | label: "Yaw" | ||
| 99 | min: -2.0 | ||
| 100 | max: 2.0 | ||
| 101 | size: 64 | ||
| 102 | } | ||
| 103 | |||
| 104 | ScrollingGraph { | ||
| 105 | controlId: Controller.Actions.YawLeft | ||
| 106 | label: "Yaw Left" | ||
| 107 | min: -2.0 | ||
| 108 | max: 2.0 | ||
| 109 | size: 64 | ||
| 110 | } | ||
| 111 | |||
| 112 | ScrollingGraph { | ||
| 113 | controlId: Controller.Actions.YawRight | ||
| 114 | label: "Yaw Right" | ||
| 115 | min: -2.0 | ||
| 116 | max: 2.0 | ||
| 117 | size: 64 | ||
| 118 | } | ||
| 119 | |||
| 120 | ScrollingGraph { | ||
| 121 | controlId: Controller.Actions.StepYaw | ||
| 122 | label: "StepYaw" | ||
| 123 | min: -20.0 | ||
| 124 | max: 20.0 | ||
| 125 | size: 64 | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | Row { | ||
| 130 | ScrollingGraph { | ||
| 131 | controlId: Controller.Actions.TranslateZ | ||
| 132 | label: "TranslateZ" | ||
| 133 | min: -2.0 | ||
| 134 | max: 2.0 | ||
| 135 | size: 64 | ||
| 136 | } | ||
| 137 | |||
| 138 | ScrollingGraph { | ||
| 139 | controlId: Controller.Actions.Forward | ||
| 140 | label: "Forward" | ||
| 141 | min: -2.0 | ||
| 142 | max: 2.0 | ||
| 143 | size: 64 | ||
| 144 | } | ||
| 145 | |||
| 146 | ScrollingGraph { | ||
| 147 | controlId: Controller.Actions.Backward | ||
| 148 | label: "Backward" | ||
| 149 | min: -2.0 | ||
| 150 | max: 2.0 | ||
| 151 | size: 64 | ||
| 152 | } | ||
| 153 | |||
| 154 | } | ||
| 155 | } | ||
| 156 | } // dialog | ||
| 157 | |||
| 158 | |||
| 159 | |||
| 160 | |||
| 161 | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | import QtQuick 2.1 | ||
| 2 | import QtQuick.Controls 1.0 | ||
| 3 | import QtQuick.Layouts 1.0 | ||
| 4 | import QtQuick.Dialogs 1.0 | ||
| 5 | |||
| 6 | Item { | ||
| 7 | id: root | ||
| 8 | property int size: 64 | ||
| 9 | width: size | ||
| 10 | height: size | ||
| 11 | property int controlId: 0 | ||
| 12 | property real value: 0 | ||
| 13 | property color color: 'black' | ||
| 14 | |||
| 15 | function update() { | ||
| 16 | value = controlId ? Controller.getValue(controlId) : 0; | ||
| 17 | canvas.requestPaint(); | ||
| 18 | } | ||
| 19 | |||
| 20 | Timer { | ||
| 21 | interval: 50; running: true; repeat: true | ||
| 22 | onTriggered: root.update(); | ||
| 23 | } | ||
| 24 | |||
| 25 | Canvas { | ||
| 26 | id: canvas | ||
| 27 | anchors.fill: parent | ||
| 28 | antialiasing: false | ||
| 29 | |||
| 30 | onPaint: { | ||
| 31 | var ctx = canvas.getContext('2d'); | ||
| 32 | ctx.save(); | ||
| 33 | ctx.beginPath(); | ||
| 34 | ctx.clearRect(0, 0, canvas.width, canvas.height); | ||
| 35 | var fillHeight = root.value * canvas.height; | ||
| 36 | |||
| 37 | ctx.fillStyle = 'red' | ||
| 38 | ctx.fillRect(0, canvas.height - fillHeight, canvas.width, fillHeight); | ||
| 39 | ctx.fill(); | ||
| 40 | ctx.restore() | ||
| 41 | } | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | import QtQuick 2.1 | ||
| 2 | import QtQuick.Controls 1.0 | ||
| 3 | import QtQuick.Layouts 1.0 | ||
| 4 | import QtQuick.Dialogs 1.0 | ||
| 5 | |||
| 6 | Item { | ||
| 7 | id: root | ||
| 8 | property int size: 64 | ||
| 9 | width: size | ||
| 10 | height: size | ||
| 11 | property bool invertY: false | ||
| 12 | |||
| 13 | |||
| 14 | property int halfSize: size / 2 | ||
| 15 | property var controlIds: [ 0, 0 ] | ||
| 16 | property vector2d value: Qt.vector2d(0, 0) | ||
| 17 | |||
| 18 | function update() { | ||
| 19 | value = Qt.vector2d( | ||
| 20 | controlIds[0] ? Controller.getValue(controlIds[0]) : 0, | ||
| 21 | controlIds[1] ? Controller.getValue(controlIds[1]) : 0 | ||
| 22 | ); | ||
| 23 | if (root.invertY) { | ||
| 24 | value.y = value.y * -1.0 | ||
| 25 | } | ||
| 26 | canvas.requestPaint(); | ||
| 27 | } | ||
| 28 | |||
| 29 | Timer { | ||
| 30 | interval: 50; running: controlIds; repeat: true | ||
| 31 | onTriggered: root.update() | ||
| 32 | } | ||
| 33 | |||
| 34 | Canvas { | ||
| 35 | id: canvas | ||
| 36 | anchors.fill: parent | ||
| 37 | antialiasing: false | ||
| 38 | |||
| 39 | onPaint: { | ||
| 40 | var ctx = canvas.getContext('2d'); | ||
| 41 | ctx.save(); | ||
| 42 | ctx.beginPath(); | ||
| 43 | ctx.clearRect(0, 0, width, height); | ||
| 44 | ctx.fill(); | ||
| 45 | ctx.translate(root.halfSize, root.halfSize) | ||
| 46 | ctx.lineWidth = 4 | ||
| 47 | ctx.strokeStyle = Qt.rgba(Math.max(Math.abs(value.x), Math.abs(value.y)), 0, 0, 1) | ||
| 48 | ctx.moveTo(0, 0).lineTo(root.value.x * root.halfSize, root.value.y * root.halfSize) | ||
| 49 | ctx.stroke() | ||
| 50 | ctx.restore() | ||
| 51 | } | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | import QtQuick 2.1 | ||
| 2 | import QtQuick.Controls 1.0 | ||
| 3 | import QtQuick.Layouts 1.0 | ||
| 4 | import QtQuick.Dialogs 1.0 | ||
| 5 | |||
| 6 | import "hydra" | ||
| 7 | |||
| 8 | Item { | ||
| 9 | id: root | ||
| 10 | width: 480 | ||
| 11 | height: width * 3.0 / 4.0 | ||
| 12 | property var device | ||
| 13 | property real scale: width / 480 | ||
| 14 | property real rightOffset: (width / 2) * scale | ||
| 15 | |||
| 16 | Image { | ||
| 17 | anchors.fill: parent | ||
| 18 | source: "hydra/hydra.png" | ||
| 19 | |||
| 20 | HydraStick { | ||
| 21 | leftStick: true | ||
| 22 | scale: root.scale | ||
| 23 | device: root.device | ||
| 24 | } | ||
| 25 | |||
| 26 | |||
| 27 | HydraStick { | ||
| 28 | leftStick: false | ||
| 29 | scale: root.scale | ||
| 30 | device: root.device | ||
| 31 | } | ||
| 32 | |||
| 33 | } | ||
| 34 | } | ||
| @@ -0,0 +1,35 @@ | |||
| 1 | import QtQuick 2.1 | ||
| 2 | import QtQuick.Controls 1.0 | ||
| 3 | import QtQuick.Layouts 1.0 | ||
| 4 | import QtQuick.Dialogs 1.0 | ||
| 5 | |||
| 6 | import "xbox" | ||
| 7 | |||
| 8 | Item { | ||
| 9 | id: root | ||
| 10 | property real aspect: 300.0 / 215.0 | ||
| 11 | width: 300 | ||
| 12 | height: width / aspect | ||
| 13 | property var device | ||
| 14 | property string label: "" | ||
| 15 | property real scale: width / 300.0 | ||
| 16 | |||
| 17 | Xbox { | ||
| 18 | width: root.width; height: root.height | ||
| 19 | device: root.device | ||
| 20 | } | ||
| 21 | |||
| 22 | // Left primary | ||
| 23 | ToggleButton { | ||
| 24 | x: 0; y: parent.height - height; | ||
| 25 | controlId: root.device.LeftPrimaryThumb | ||
| 26 | width: 16 * root.scale; height: 16 * root.scale | ||
| 27 | } | ||
| 28 | |||
| 29 | // Left primary | ||
| 30 | ToggleButton { | ||
| 31 | x: parent.width - width; y: parent.height - height; | ||
| 32 | controlId: root.device.RightPrimaryThumb | ||
| 33 | width: 16 * root.scale; height: 16 * root.scale | ||
| 34 | } | ||
| 35 | } | ||
| @@ -0,0 +1,45 @@ | |||
| 1 | import QtQuick 2.1 | ||
| 2 | import QtQuick.Controls 1.0 | ||
| 3 | import QtQuick.Layouts 1.0 | ||
| 4 | import QtQuick.Dialogs 1.0 | ||
| 5 | |||
| 6 | Item { | ||
| 7 | id: root | ||
| 8 | width: size | ||
| 9 | height: size | ||
| 10 | property int size: 64 | ||
| 11 | property int controlId: 0 | ||
| 12 | property real value: 0 | ||
| 13 | property color color: 'black' | ||
| 14 | |||
| 15 | function update() { | ||
| 16 | value = controlId ? Controller.getValue(controlId) : 0; | ||
| 17 | canvas.requestPaint(); | ||
| 18 | } | ||
| 19 | |||
| 20 | Timer { | ||
| 21 | interval: 50; running: root.controlId; repeat: true | ||
| 22 | onTriggered: root.update() | ||
| 23 | } | ||
| 24 | |||
| 25 | Canvas { | ||
| 26 | id: canvas | ||
| 27 | anchors.fill: parent | ||
| 28 | antialiasing: false | ||
| 29 | |||
| 30 | onPaint: { | ||
| 31 | var ctx = canvas.getContext('2d'); | ||
| 32 | ctx.save(); | ||
| 33 | ctx.beginPath(); | ||
| 34 | ctx.clearRect(0, 0, width, height); | ||
| 35 | if (root.value > 0.0) { | ||
| 36 | ctx.fillStyle = root.color | ||
| 37 | ctx.fillRect(0, 0, canvas.width, canvas.height); | ||
| 38 | } | ||
| 39 | ctx.fill(); | ||
| 40 | ctx.restore() | ||
| 41 | } | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | |||
| @@ -0,0 +1,104 @@ | |||
| 1 | import QtQuick 2.1 | ||
| 2 | import QtQuick.Controls 1.0 | ||
| 3 | import QtQuick.Layouts 1.0 | ||
| 4 | import QtQuick.Dialogs 1.0 | ||
| 5 | |||
| 6 | import "xbox" | ||
| 7 | |||
| 8 | Item { | ||
| 9 | id: root | ||
| 10 | property real aspect: 300.0 / 215.0 | ||
| 11 | width: 300 | ||
| 12 | height: width / aspect | ||
| 13 | property var device | ||
| 14 | property string label: "" | ||
| 15 | property real scale: width / 300.0 | ||
| 16 | |||
| 17 | Image { | ||
| 18 | Text { | ||
| 19 | anchors.left: parent.left | ||
| 20 | anchors.top: parent.top | ||
| 21 | text: root.label | ||
| 22 | visible: root.label != "" | ||
| 23 | } | ||
| 24 | anchors.fill: parent | ||
| 25 | source: "xbox/xbox360-controller-md.png" | ||
| 26 | |||
| 27 | LeftAnalogStick { | ||
| 28 | device: root.device | ||
| 29 | x: (65 * root.scale) - width / 2; y: (42 * root.scale) - height / 2 | ||
| 30 | } | ||
| 31 | |||
| 32 | // Left stick press | ||
| 33 | ToggleButton { | ||
| 34 | controlId: root.device.LS | ||
| 35 | width: 16 * root.scale; height: 16 * root.scale | ||
| 36 | x: (65 * root.scale) - width / 2; y: (42 * root.scale) - height / 2 | ||
| 37 | } | ||
| 38 | |||
| 39 | |||
| 40 | RightAnalogStick { | ||
| 41 | device: root.device | ||
| 42 | x: (193 * root.scale) - width / 2; y: (96 * root.scale) - height / 2 | ||
| 43 | } | ||
| 44 | |||
| 45 | // Right stick press | ||
| 46 | ToggleButton { | ||
| 47 | controlId: root.device.RS | ||
| 48 | width: 16 * root.scale; height: 16 * root.scale | ||
| 49 | x: (193 * root.scale) - width / 2; y: (96 * root.scale) - height / 2 | ||
| 50 | } | ||
| 51 | |||
| 52 | // Left trigger | ||
| 53 | AnalogButton { | ||
| 54 | controlId: root.device.LT | ||
| 55 | width: 8; height: 64 | ||
| 56 | x: (20 * root.scale); y: (7 * root.scale) | ||
| 57 | } | ||
| 58 | |||
| 59 | // Right trigger | ||
| 60 | AnalogButton { | ||
| 61 | controlId: root.device.RT | ||
| 62 | width: 8; height: 64 | ||
| 63 | x: (272 * root.scale); y: (7 * root.scale) | ||
| 64 | } | ||
| 65 | |||
| 66 | // Left bumper | ||
| 67 | ToggleButton { | ||
| 68 | controlId: root.device.LB | ||
| 69 | width: 32 * root.scale; height: 16 * root.scale | ||
| 70 | x: (40 * root.scale); y: (7 * root.scale) | ||
| 71 | } | ||
| 72 | |||
| 73 | // Right bumper | ||
| 74 | ToggleButton { | ||
| 75 | controlId: root.device.RB | ||
| 76 | width: 32 * root.scale; height: 16 * root.scale | ||
| 77 | x: (root.width - width) - (40 * root.scale); y: (7 * root.scale) | ||
| 78 | } | ||
| 79 | |||
| 80 | DPad { | ||
| 81 | device: root.device | ||
| 82 | size: 48 * root.scale | ||
| 83 | x: (80 * root.scale); y: (71 * root.scale) | ||
| 84 | } | ||
| 85 | |||
| 86 | XboxButtons { | ||
| 87 | device: root.device | ||
| 88 | size: 65 * root.scale | ||
| 89 | x: (206 * root.scale); y: (19 * root.scale) | ||
| 90 | } | ||
| 91 | |||
| 92 | ToggleButton { | ||
| 93 | controlId: root.device.Back | ||
| 94 | width: 16 * root.scale; height: 12 * root.scale | ||
| 95 | x: (112 * root.scale); y: (45 * root.scale) | ||
| 96 | } | ||
| 97 | |||
| 98 | ToggleButton { | ||
| 99 | controlId: root.device.Start | ||
| 100 | width: 16 * root.scale; height: 12 * root.scale | ||
| 101 | x: (177 * root.scale); y: (45 * root.scale) | ||
| 102 | } | ||
| 103 | } | ||
| 104 | } | ||
| @@ -0,0 +1,18 @@ | |||
| 1 | import QtQuick 2.1 | ||
| 2 | import QtQuick.Controls 1.0 | ||
| 3 | import QtQuick.Layouts 1.0 | ||
| 4 | import QtQuick.Dialogs 1.0 | ||
| 5 | |||
| 6 | import ".." | ||
| 7 | |||
| 8 | Item { | ||
| 9 | id: root | ||
| 10 | width: 72 * scale | ||
| 11 | height: 48 * scale | ||
| 12 | property var device | ||
| 13 | property real scale: 1.0 | ||
| 14 | property bool leftStick: true | ||
| 15 | |||
| 16 | } | ||
| 17 | |||
| 18 | |||
| @@ -0,0 +1,91 @@ | |||
| 1 | import QtQuick 2.1 | ||
| 2 | import QtQuick.Controls 1.0 | ||
| 3 | import QtQuick.Layouts 1.0 | ||
| 4 | import QtQuick.Dialogs 1.0 | ||
| 5 | |||
| 6 | import ".." | ||
| 7 | |||
| 8 | Item { | ||
| 9 | id: root | ||
| 10 | property var device | ||
| 11 | property real scale: 1.0 | ||
| 12 | property bool leftStick: true | ||
| 13 | width: parent.width / 2; height: parent.height | ||
| 14 | x: leftStick ? 0 : parent.width / 2 | ||
| 15 | |||
| 16 | Text { | ||
| 17 | x: parent.width / 2 - width / 2; y: parent.height / 2 - height / 2 | ||
| 18 | text: root.leftStick ? "L" : "R" | ||
| 19 | color: 'green' | ||
| 20 | } | ||
| 21 | |||
| 22 | // Analog Stick | ||
| 23 | AnalogStick { | ||
| 24 | size: 64 * root.scale | ||
| 25 | x: 127 * root.scale - width / 2; y: 45 * root.scale - width / 2; z: 100 | ||
| 26 | invertY: true | ||
| 27 | controlIds: [ | ||
| 28 | root.leftStick ? root.device.LX : root.device.RX, | ||
| 29 | root.leftStick ? root.device.LY : root.device.RY | ||
| 30 | ] | ||
| 31 | } | ||
| 32 | |||
| 33 | // Stick press | ||
| 34 | ToggleButton { | ||
| 35 | controlId: root.leftStick ? root.device.LS : root.device.RS | ||
| 36 | width: 16 * root.scale; height: 16 * root.scale | ||
| 37 | x: 127 * root.scale - width / 2; y: 45 * root.scale - width / 2; | ||
| 38 | color: 'yellow' | ||
| 39 | } | ||
| 40 | |||
| 41 | // Trigger | ||
| 42 | AnalogButton { | ||
| 43 | controlId: root.leftStick ? root.device.LT : root.device.RT | ||
| 44 | width: 8 * root.scale ; height: 64 * root.scale | ||
| 45 | y: 24 * root.scale | ||
| 46 | x: root.leftStick ? (48 * root.scale) : root.width - (48 * root.scale) - width / 2 | ||
| 47 | } | ||
| 48 | |||
| 49 | // Bumper | ||
| 50 | ToggleButton { | ||
| 51 | controlId: root.leftStick ? root.device.LB : root.device.RB | ||
| 52 | height: 16 * root.scale; width: 32 * root.scale | ||
| 53 | x: 128 * root.scale - width / 2; y: 24 * root.scale | ||
| 54 | color: 'red' | ||
| 55 | } | ||
| 56 | |||
| 57 | ToggleButton { | ||
| 58 | controlId: root.leftStick ? root.device.L0 : root.device.R0 | ||
| 59 | height: 16 * root.scale; width: 4 * root.scale | ||
| 60 | x: 128 * root.scale - width / 2; y: 109 * root.scale | ||
| 61 | color: 'yellow' | ||
| 62 | } | ||
| 63 | |||
| 64 | ToggleButton { | ||
| 65 | controlId: root.leftStick ? root.device.L1 : root.device.R1 | ||
| 66 | width: 16 * root.scale; height: 16 * root.scale | ||
| 67 | x: 103 * root.scale - width / 2; y: 100 * root.scale - height / 2 | ||
| 68 | color: 'yellow' | ||
| 69 | } | ||
| 70 | |||
| 71 | ToggleButton { | ||
| 72 | controlId: root.leftStick ? root.device.L2 : root.device.R2 | ||
| 73 | width: 16 * root.scale; height: 16 * root.scale | ||
| 74 | x: 148 * root.scale - width / 2; y: 100 * root.scale - height / 2 | ||
| 75 | color: 'yellow' | ||
| 76 | } | ||
| 77 | |||
| 78 | ToggleButton { | ||
| 79 | controlId: root.leftStick ? root.device.L3 : root.device.R3 | ||
| 80 | width: 16 * root.scale; height: 16 * root.scale | ||
| 81 | x: 97 * root.scale - width / 2; y: 76 * root.scale - height / 2 | ||
| 82 | color: 'yellow' | ||
| 83 | } | ||
| 84 | |||
| 85 | ToggleButton { | ||
| 86 | controlId: root.leftStick ? root.device.L4 : root.device.R4 | ||
| 87 | width: 16 * root.scale; height: 16 * root.scale | ||
| 88 | x: 155 * root.scale - width / 2; y: 76 * root.scale - height / 2 | ||
| 89 | color: 'yellow' | ||
| 90 | } | ||
| 91 | } | ||
Binary file not shown.
| @@ -0,0 +1,42 @@ | |||
| 1 | import QtQuick 2.1 | ||
| 2 | import QtQuick.Controls 1.0 | ||
| 3 | import QtQuick.Layouts 1.0 | ||
| 4 | import QtQuick.Dialogs 1.0 | ||
| 5 | |||
| 6 | import ".." | ||
| 7 | |||
| 8 | Item { | ||
| 9 | id: root | ||
| 10 | property int size: 64 | ||
| 11 | width: size | ||
| 12 | height: size | ||
| 13 | property int spacer: size / 3 | ||
| 14 | property var device | ||
| 15 | property color color: 'black' | ||
| 16 | |||
| 17 | ToggleButton { | ||
| 18 | controlId: device.Up | ||
| 19 | x: spacer | ||
| 20 | width: spacer; height: spacer | ||
| 21 | } | ||
| 22 | |||
| 23 | ToggleButton { | ||
| 24 | controlId: device.Left | ||
| 25 | y: spacer | ||
| 26 | width: spacer; height: spacer | ||
| 27 | } | ||
| 28 | |||
| 29 | ToggleButton { | ||
| 30 | controlId: device.Right | ||
| 31 | x: spacer * 2; y: spacer | ||
| 32 | width: spacer; height: spacer | ||
| 33 | } | ||
| 34 | |||
| 35 | ToggleButton { | ||
| 36 | controlId: device.Down | ||
| 37 | x: spacer; y: spacer * 2 | ||
| 38 | width: spacer; height: spacer | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | import QtQuick 2.1 | ||
| 2 | import QtQuick.Controls 1.0 | ||
| 3 | import QtQuick.Layouts 1.0 | ||
| 4 | import QtQuick.Dialogs 1.0 | ||
| 5 | |||
| 6 | import ".." | ||
| 7 | |||
| 8 | Item { | ||
| 9 | id: root | ||
| 10 | property int size: 64 | ||
| 11 | width: size | ||
| 12 | height: size | ||
| 13 | property var device | ||
| 14 | |||
| 15 | AnalogStick { | ||
| 16 | size: size | ||
| 17 | controlIds: [ device.LX, device.LY ] | ||
| 18 | } | ||
| 19 | } | ||
| 20 | |||
| 21 | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | import QtQuick 2.1 | ||
| 2 | import QtQuick.Controls 1.0 | ||
| 3 | import QtQuick.Layouts 1.0 | ||
| 4 | import QtQuick.Dialogs 1.0 | ||
| 5 | |||
| 6 | import ".." | ||
| 7 | |||
| 8 | Item { | ||
| 9 | id: root | ||
| 10 | property int size: 64 | ||
| 11 | width: size | ||
| 12 | height: size | ||
| 13 | property var device | ||
| 14 | |||
| 15 | AnalogStick { | ||
| 16 | size: size | ||
| 17 | controlIds: [ device.RX, device.RY ] | ||
| 18 | } | ||
| 19 | } | ||
| 20 | |||
| 21 | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | import QtQuick 2.1 | ||
| 2 | import QtQuick.Controls 1.0 | ||
| 3 | import QtQuick.Layouts 1.0 | ||
| 4 | import QtQuick.Dialogs 1.0 | ||
| 5 | |||
| 6 | import ".." | ||
| 7 | |||
| 8 | Item { | ||
| 9 | id: root | ||
| 10 | property int size: 64 | ||
| 11 | width: size | ||
| 12 | height: size | ||
| 13 | property int spacer: size / 3 | ||
| 14 | property var device | ||
| 15 | property color color: 'black' | ||
| 16 | |||
| 17 | ToggleButton { | ||
| 18 | controlId: device.Y | ||
| 19 | x: spacer | ||
| 20 | width: spacer; height: spacer | ||
| 21 | color: 'yellow' | ||
| 22 | } | ||
| 23 | |||
| 24 | ToggleButton { | ||
| 25 | controlId: device.X | ||
| 26 | y: spacer | ||
| 27 | width: spacer; height: spacer | ||
| 28 | color: 'blue' | ||
| 29 | } | ||
| 30 | |||
| 31 | ToggleButton { | ||
| 32 | controlId: device.B | ||
| 33 | x: spacer * 2; y: spacer | ||
| 34 | width: spacer; height: spacer | ||
| 35 | color: 'red' | ||
| 36 | } | ||
| 37 | |||
| 38 | ToggleButton { | ||
| 39 | controlId: device.A | ||
| 40 | x: spacer; y: spacer * 2 | ||
| 41 | width: spacer; height: spacer | ||
| 42 | color: 'green' | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | |||
Binary file not shown.
| @@ -17,27 +17,32 @@ · 16 unmodified lines | |||
| 17 | #include <glm/gtx/vector_angle.hpp> | 17 | #include <glm/gtx/vector_angle.hpp> |
| 18 | #include <glm/gtc/type_ptr.hpp> | 18 | #include <glm/gtc/type_ptr.hpp> |
| 19 | 19 | ||
| 20 | #include <QAbstractNativeEventFilter> | 20 | #include <QtCore/QDebug> |
| 21 | #include <QActionGroup> | 21 | #include <QtCore/QObject> |
| 22 | #include <QDebug> | 22 | #include <QtCore/QUrl> |
| 23 | #include <QDesktopServices> | 23 | #include <QtCore/QTimer> |
| 24 | #include <QDesktopWidget> | 24 | #include <QtCore/QAbstractNativeEventFilter> |
| 25 | #include <QImage> | 25 | #include <QtCore/QMimeData> |
| 26 | #include <QFileDialog> | 26 | |
| 27 | #include <QInputDialog> | 27 | #include <QtGui/QScreen> |
| 28 | #include <QKeyEvent> | 28 | #include <QtGui/QImage> |
| 29 | #include <QMediaPlayer> | 29 | #include <QtGui/QWheelEvent> |
| 30 | #include <QMenuBar> | 30 | #include <QtGui/QWindow> |
| 31 | #include <QMessageBox> | 31 | #include <QtQml/QQmlContext> |
| 32 | #include <QMimeData> | 32 | #include <QtGui/QKeyEvent> |
| 33 | #include <QMouseEvent> | 33 | #include <QtGui/QMouseEvent> |
| 34 | #include <QNetworkDiskCache> | 34 | #include <QtGui/QDesktopServices> |
| 35 | #include <QObject> | 35 | |
| 36 | #include <QScreen> | 36 | #include <QtWidgets/QActionGroup> |
| 37 | #include <QTimer> | 37 | #include <QtWidgets/QDesktopWidget> |
| 38 | #include <QUrl> | 38 | #include <QtWidgets/QFileDialog> |
| 39 | #include <QWheelEvent> | 39 | #include <QtWidgets/QInputDialog> |
| 40 | #include <QWindow> | 40 | #include <QtWidgets/QMenuBar> |
| 41 | #include <QtWidgets/QMessageBox> | ||
| 42 | |||
| 43 | #include <QtMultimedia/QMediaPlayer> | ||
| 44 | |||
| 45 | #include <QtNetwork/QNetworkDiskCache> | ||
| 41 | 46 | ||
| 42 | #include <AccountManager.h> | 47 | #include <AccountManager.h> |
| 43 | #include <AddressManager.h> | 48 | #include <AddressManager.h> |
| @@ -60,7 +65,8 @@ · 16 unmodified lines | |||
| 60 | #include <InfoView.h> | 65 | #include <InfoView.h> |
| 61 | #include <input-plugins/InputPlugin.h> | 66 | #include <input-plugins/InputPlugin.h> |
| 62 | #include <input-plugins/Joystick.h> // this should probably be removed | 67 | #include <input-plugins/Joystick.h> // this should probably be removed |
| 63 | #include <input-plugins/UserInputMapper.h> | 68 | #include <controllers/UserInputMapper.h> |
| 69 | #include <controllers/StateController.h> | ||
| 64 | #include <LogHandler.h> | 70 | #include <LogHandler.h> |
| 65 | #include <MainWindow.h> | 71 | #include <MainWindow.h> |
| 66 | #include <MessageDialog.h> | 72 | #include <MessageDialog.h> |
| @@ -120,6 +126,7 @@ · 53 unmodified lines | |||
| 120 | #include "scripting/SettingsScriptingInterface.h" | 126 | #include "scripting/SettingsScriptingInterface.h" |
| 121 | #include "scripting/WebWindowClass.h" | 127 | #include "scripting/WebWindowClass.h" |
| 122 | #include "scripting/WindowScriptingInterface.h" | 128 | #include "scripting/WindowScriptingInterface.h" |
| 129 | #include "scripting/ControllerScriptingInterface.h" | ||
| 123 | #if defined(Q_OS_MAC) || defined(Q_OS_WIN) | 130 | #if defined(Q_OS_MAC) || defined(Q_OS_WIN) |
| 124 | #include "SpeechRecognizer.h" | 131 | #include "SpeechRecognizer.h" |
| 125 | #endif | 132 | #endif |
| @@ -137,6 +144,7 @@ · 11 unmodified lines | |||
| 137 | #include "ui/UpdateDialog.h" | 144 | #include "ui/UpdateDialog.h" |
| 138 | #include "Util.h" | 145 | #include "Util.h" |
| 139 | 146 | ||
| 147 | |||
| 140 | // ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU | 148 | // ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU |
| 141 | // FIXME seems to be broken. | 149 | // FIXME seems to be broken. |
| 142 | #if defined(Q_OS_WIN) | 150 | #if defined(Q_OS_WIN) |
| @@ -316,6 +324,7 @@ bool setupEssentials(int& argc, char** argv) { · 173 unmodified lines | |||
| 316 | DependencyManager::set<EntityScriptingInterface>(); | 324 | DependencyManager::set<EntityScriptingInterface>(); |
| 317 | DependencyManager::set<WindowScriptingInterface>(); | 325 | DependencyManager::set<WindowScriptingInterface>(); |
| 318 | DependencyManager::set<HMDScriptingInterface>(); | 326 | DependencyManager::set<HMDScriptingInterface>(); |
| 327 | |||
| 319 | #if defined(Q_OS_MAC) || defined(Q_OS_WIN) | 328 | #if defined(Q_OS_MAC) || defined(Q_OS_WIN) |
| 320 | DependencyManager::set<SpeechRecognizer>(); | 329 | DependencyManager::set<SpeechRecognizer>(); |
| 321 | #endif | 330 | #endif |
| @@ -327,7 +336,7 @@ bool setupEssentials(int& argc, char** argv) { · 5 unmodified lines | |||
| 327 | DependencyManager::set<InterfaceActionFactory>(); | 336 | DependencyManager::set<InterfaceActionFactory>(); |
| 328 | DependencyManager::set<AssetClient>(); | 337 | DependencyManager::set<AssetClient>(); |
| 329 | DependencyManager::set<UserInputMapper>(); | 338 | DependencyManager::set<UserInputMapper>(); |
| 330 | 339 | DependencyManager::set<controller::ScriptingInterface, ControllerScriptingInterface>(); | |
| 331 | return true; | 340 | return true; |
| 332 | } | 341 | } |
| 333 | 342 | ||
| @@ -339,45 +348,47 @@ int _keyboardFocusHighlightID{ -1 }; · 5 unmodified lines | |||
| 339 | PluginContainer* _pluginContainer; | 348 | PluginContainer* _pluginContainer; |
| 340 | 349 | ||
| 341 | Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : | 350 | Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : |
| 342 | QApplication(argc, argv), | 351 | QApplication(argc, argv), |
| 343 | _dependencyManagerIsSetup(setupEssentials(argc, argv)), | 352 | _dependencyManagerIsSetup(setupEssentials(argc, argv)), |
| 344 | _window(new MainWindow(desktop())), | 353 | _window(new MainWindow(desktop())), |
| 345 | _toolWindow(NULL), | 354 | _toolWindow(NULL), |
| 346 | _undoStackScriptingInterface(&_undoStack), | 355 | _undoStackScriptingInterface(&_undoStack), |
| 347 | _frameCount(0), | 356 | _frameCount(0), |
| 348 | _fps(60.0f), | 357 | _fps(60.0f), |
| 349 | _physicsEngine(new PhysicsEngine(Vectors::ZERO)), | 358 | _physicsEngine(new PhysicsEngine(Vectors::ZERO)), |
| 350 | _entities(true, this, this), | 359 | _entities(true, this, this), |
| 351 | _entityClipboardRenderer(false, this, this), | 360 | _entityClipboardRenderer(false, this, this), |
| 352 | _entityClipboard(new EntityTree()), | 361 | _entityClipboard(new EntityTree()), |
| 353 | _lastQueriedTime(usecTimestampNow()), | 362 | _lastQueriedTime(usecTimestampNow()), |
| 354 | _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)), | 363 | _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)), |
| 355 | _firstRun("firstRun", true), | 364 | _firstRun("firstRun", true), |
| 356 | _previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION), | 365 | _previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION), |
| 357 | _scriptsLocationHandle("scriptsLocation", DESKTOP_LOCATION), | 366 | _scriptsLocationHandle("scriptsLocation", DESKTOP_LOCATION), |
| 358 | _fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES), | 367 | _fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES), |
| 359 | _scaleMirror(1.0f), | 368 | _scaleMirror(1.0f), |
| 360 | _rotateMirror(0.0f), | 369 | _rotateMirror(0.0f), |
| 361 | _raiseMirror(0.0f), | 370 | _raiseMirror(0.0f), |
| 362 | _lastMouseMoveWasSimulated(false), | 371 | _lastMouseMoveWasSimulated(false), |
| 363 | _enableProcessOctreeThread(true), | 372 | _enableProcessOctreeThread(true), |
| 364 | _runningScriptsWidget(NULL), | 373 | _runningScriptsWidget(NULL), |
| 365 | _runningScriptsWidgetWasVisible(false), | 374 | _runningScriptsWidgetWasVisible(false), |
| 366 | _lastNackTime(usecTimestampNow()), | 375 | _lastNackTime(usecTimestampNow()), |
| 367 | _lastSendDownstreamAudioStats(usecTimestampNow()), | 376 | _lastSendDownstreamAudioStats(usecTimestampNow()), |
| 368 | _aboutToQuit(false), | 377 | _aboutToQuit(false), |
| 369 | _notifiedPacketVersionMismatchThisDomain(false), | 378 | _notifiedPacketVersionMismatchThisDomain(false), |
| 370 | _maxOctreePPS(maxOctreePacketsPerSecond.get()), | 379 | _maxOctreePPS(maxOctreePacketsPerSecond.get()), |
| 371 | _lastFaceTrackerUpdate(0) | 380 | _lastFaceTrackerUpdate(0) |
| 372 | { | 381 | { |
| 373 | thread()->setObjectName("Main Thread"); | 382 | thread()->setObjectName("Main Thread"); |
| 374 | 383 | ||
| 375 | setInstance(this); | 384 | setInstance(this); |
| 376 | 385 | ||
| 386 | auto controllerScriptingInterface = DependencyManager::get<controller::ScriptingInterface>().data(); | ||
| 387 | _controllerScriptingInterface = dynamic_cast<ControllerScriptingInterface*>(controllerScriptingInterface); | ||
| 377 | // to work around the Qt constant wireless scanning, set the env for polling interval very high | 388 | // to work around the Qt constant wireless scanning, set the env for polling interval very high |
| 378 | const QByteArray EXTREME_BEARER_POLL_TIMEOUT = QString::number(INT_MAX).toLocal8Bit(); | 389 | const QByteArray EXTREME_BEARER_POLL_TIMEOUT = QString::number(INT_MAX).toLocal8Bit(); |
| 379 | qputenv("QT_BEARER_POLL_TIMEOUT", EXTREME_BEARER_POLL_TIMEOUT); | 390 | qputenv("QT_BEARER_POLL_TIMEOUT", EXTREME_BEARER_POLL_TIMEOUT); |
| 380 | 391 | ||
| 381 | _entityClipboard->createRootElement(); | 392 | _entityClipboard->createRootElement(); |
| 382 | 393 | ||
| 383 | _pluginContainer = new PluginContainerProxy(); | 394 | _pluginContainer = new PluginContainerProxy(); |
| @@ -440,7 +451,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 56 unmodified lines | |||
| 440 | audioIO->moveToThread(audioThread); | 451 | audioIO->moveToThread(audioThread); |
| 441 | 452 | ||
| 442 | auto& audioScriptingInterface = AudioScriptingInterface::getInstance(); | 453 | auto& audioScriptingInterface = AudioScriptingInterface::getInstance(); |
| 443 | 454 | ||
| 444 | connect(audioThread, &QThread::started, audioIO.data(), &AudioClient::start); | 455 | connect(audioThread, &QThread::started, audioIO.data(), &AudioClient::start); |
| 445 | connect(audioIO.data(), &AudioClient::destroyed, audioThread, &QThread::quit); | 456 | connect(audioIO.data(), &AudioClient::destroyed, audioThread, &QThread::quit); |
| 446 | connect(audioThread, &QThread::finished, audioThread, &QThread::deleteLater); | 457 | connect(audioThread, &QThread::finished, audioThread, &QThread::deleteLater); |
| @@ -479,7 +490,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 32 unmodified lines | |||
| 479 | connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails())); | 490 | connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails())); |
| 480 | connect(&domainHandler, &DomainHandler::settingsReceived, this, &Application::domainSettingsReceived); | 491 | connect(&domainHandler, &DomainHandler::settingsReceived, this, &Application::domainSettingsReceived); |
| 481 | connect(&domainHandler, &DomainHandler::hostnameChanged, | 492 | connect(&domainHandler, &DomainHandler::hostnameChanged, |
| 482 | DependencyManager::get<AddressManager>().data(), &AddressManager::storeCurrentAddress); | 493 | DependencyManager::get<AddressManager>().data(), &AddressManager::storeCurrentAddress); |
| 483 | 494 | ||
| 484 | // update our location every 5 seconds in the metaverse server, assuming that we are authenticated with one | 495 | // update our location every 5 seconds in the metaverse server, assuming that we are authenticated with one |
| 485 | const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * 1000; | 496 | const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * 1000; |
| @@ -490,7 +501,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 4 unmodified lines | |||
| 490 | 501 | ||
| 491 | // if we get a domain change, immediately attempt update location in metaverse server | 502 | // if we get a domain change, immediately attempt update location in metaverse server |
| 492 | connect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain, | 503 | connect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain, |
| 493 | discoverabilityManager.data(), &DiscoverabilityManager::updateLocation); | 504 | discoverabilityManager.data(), &DiscoverabilityManager::updateLocation); |
| 494 | 505 | ||
| 495 | connect(nodeList.data(), &NodeList::nodeAdded, this, &Application::nodeAdded); | 506 | connect(nodeList.data(), &NodeList::nodeAdded, this, &Application::nodeAdded); |
| 496 | connect(nodeList.data(), &NodeList::nodeKilled, this, &Application::nodeKilled); | 507 | connect(nodeList.data(), &NodeList::nodeKilled, this, &Application::nodeKilled); |
| @@ -529,14 +540,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 32 unmodified lines | |||
| 529 | connect(addressManager.data(), &AddressManager::hostChanged, this, &Application::updateWindowTitle); | 540 | connect(addressManager.data(), &AddressManager::hostChanged, this, &Application::updateWindowTitle); |
| 530 | connect(this, &QCoreApplication::aboutToQuit, addressManager.data(), &AddressManager::storeCurrentAddress); | 541 | connect(this, &QCoreApplication::aboutToQuit, addressManager.data(), &AddressManager::storeCurrentAddress); |
| 531 | 542 | ||
| 532 | #ifdef _WIN32 | 543 | #ifdef _WIN32 |
| 533 | WSADATA WsaData; | 544 | WSADATA WsaData; |
| 534 | int wsaresult = WSAStartup(MAKEWORD(2,2), &WsaData); | 545 | int wsaresult = WSAStartup(MAKEWORD(2, 2), &WsaData); |
| 535 | #endif | 546 | #endif |
| 536 | 547 | ||
| 537 | // tell the NodeList instance who to tell the domain server we care about | 548 | // tell the NodeList instance who to tell the domain server we care about |
| 538 | nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer | 549 | nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer |
| 539 | << NodeType::EntityServer << NodeType::AssetServer); | 550 | << NodeType::EntityServer << NodeType::AssetServer); |
| 540 | 551 | ||
| 541 | // connect to the packet sent signal of the _entityEditSender | 552 | // connect to the packet sent signal of the _entityEditSender |
| 542 | connect(&_entityEditSender, &EntityEditPacketSender::packetSent, this, &Application::packetSent); | 553 | connect(&_entityEditSender, &EntityEditPacketSender::packetSent, this, &Application::packetSent); |
| @@ -609,29 +620,38 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 66 unmodified lines | |||
| 609 | // hook up bandwidth estimator | 620 | // hook up bandwidth estimator |
| 610 | QSharedPointer<BandwidthRecorder> bandwidthRecorder = DependencyManager::get<BandwidthRecorder>(); | 621 | QSharedPointer<BandwidthRecorder> bandwidthRecorder = DependencyManager::get<BandwidthRecorder>(); |
| 611 | connect(nodeList.data(), &LimitedNodeList::dataSent, | 622 | connect(nodeList.data(), &LimitedNodeList::dataSent, |
| 612 | bandwidthRecorder.data(), &BandwidthRecorder::updateOutboundData); | 623 | bandwidthRecorder.data(), &BandwidthRecorder::updateOutboundData); |
| 613 | connect(&nodeList->getPacketReceiver(), &PacketReceiver::dataReceived, | 624 | connect(&nodeList->getPacketReceiver(), &PacketReceiver::dataReceived, |
| 614 | bandwidthRecorder.data(), &BandwidthRecorder::updateInboundData); | 625 | bandwidthRecorder.data(), &BandwidthRecorder::updateInboundData); |
| 615 | 626 | ||
| 616 | connect(&getMyAvatar()->getSkeletonModel(), &SkeletonModel::skeletonLoaded, | 627 | connect(&getMyAvatar()->getSkeletonModel(), &SkeletonModel::skeletonLoaded, |
| 617 | this, &Application::checkSkeleton, Qt::QueuedConnection); | 628 | this, &Application::checkSkeleton, Qt::QueuedConnection); |
| 618 | 629 | ||
| 619 | // Setup the userInputMapper with the actions | 630 | // Setup the userInputMapper with the actions |
| 620 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); | 631 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); |
| 621 | connect(userInputMapper.data(), &UserInputMapper::actionEvent, &_controllerScriptingInterface, &AbstractControllerScriptingInterface::actionEvent); | ||
| 622 | connect(userInputMapper.data(), &UserInputMapper::actionEvent, [this](int action, float state) { | 632 | connect(userInputMapper.data(), &UserInputMapper::actionEvent, [this](int action, float state) { |
| 623 | if (state) { | 633 | if (state && action == toInt(controller::Action::TOGGLE_MUTE)) { |
| 624 | switch (action) { | 634 | DependencyManager::get<AudioClient>()->toggleMute(); |
| 625 | case UserInputMapper::Action::TOGGLE_MUTE: | ||
| 626 | DependencyManager::get<AudioClient>()->toggleMute(); | ||
| 627 | break; | ||
| 628 | } | ||
| 629 | } | 635 | } |
| 630 | }); | 636 | }); |
| 631 | 637 | ||
| 638 | // A new controllerInput device used to reflect current values from the application state | ||
| 639 | _applicationStateDevice = std::make_shared<controller::StateController>(); | ||
| 640 | |||
| 641 | _applicationStateDevice->addInputVariant(QString("InHMD"), controller::StateController::ReadLambda([]() -> float { | ||
| 642 | return (float)qApp->getAvatarUpdater()->isHMDMode(); | ||
| 643 | })); | ||
| 644 | _applicationStateDevice->addInputVariant(QString("ComfortMode"), controller::StateController::ReadLambda([]() -> float { | ||
| 645 | return (float)Menu::getInstance()->isOptionChecked(MenuOption::ComfortMode); | ||
| 646 | })); | ||
| 647 | |||
| 648 | userInputMapper->registerDevice(_applicationStateDevice); | ||
| 649 | |||
| 632 | // Setup the keyboardMouseDevice and the user input mapper with the default bindings | 650 | // Setup the keyboardMouseDevice and the user input mapper with the default bindings |
| 633 | _keyboardMouseDevice->registerToUserInputMapper(*userInputMapper); | 651 | userInputMapper->registerDevice(_keyboardMouseDevice); |
| 634 | _keyboardMouseDevice->assignDefaultInputMapping(*userInputMapper); | 652 | |
| 653 | |||
| 654 | userInputMapper->loadDefaultMapping(userInputMapper->getStandardDeviceID()); | ||
| 635 | 655 | ||
| 636 | // check first run... | 656 | // check first run... |
| 637 | if (_firstRun.get()) { | 657 | if (_firstRun.get()) { |
| @@ -704,8 +724,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 66 unmodified lines | |||
| 704 | // Now that menu is initalized we can sync myAvatar with it's state. | 724 | // Now that menu is initalized we can sync myAvatar with it's state. |
| 705 | getMyAvatar()->updateMotionBehaviorFromMenu(); | 725 | getMyAvatar()->updateMotionBehaviorFromMenu(); |
| 706 | 726 | ||
| 727 | #if 0 | ||
| 707 | // the 3Dconnexion device wants to be initiliazed after a window is displayed. | 728 | // the 3Dconnexion device wants to be initiliazed after a window is displayed. |
| 708 | ConnexionClient::getInstance().init(); | 729 | ConnexionClient::getInstance().init(); |
| 730 | #endif | ||
| 709 | 731 | ||
| 710 | auto& packetReceiver = nodeList->getPacketReceiver(); | 732 | auto& packetReceiver = nodeList->getPacketReceiver(); |
| 711 | packetReceiver.registerListener(PacketType::DomainConnectionDenied, this, "handleDomainConnectionDeniedPacket"); | 733 | packetReceiver.registerListener(PacketType::DomainConnectionDenied, this, "handleDomainConnectionDeniedPacket"); |
| @@ -792,6 +814,9 @@ void Application::cleanupBeforeQuit() { · 80 unmodified lines | |||
| 792 | 814 | ||
| 793 | AnimDebugDraw::getInstance().shutdown(); | 815 | AnimDebugDraw::getInstance().shutdown(); |
| 794 | 816 | ||
| 817 | // FIXME: once we move to shared pointer for the INputDevice we shoud remove this naked delete: | ||
| 818 | _applicationStateDevice.reset(); | ||
| 819 | |||
| 795 | if (_keyboardFocusHighlightID > 0) { | 820 | if (_keyboardFocusHighlightID > 0) { |
| 796 | getOverlays().deleteOverlay(_keyboardFocusHighlightID); | 821 | getOverlays().deleteOverlay(_keyboardFocusHighlightID); |
| 797 | _keyboardFocusHighlightID = -1; | 822 | _keyboardFocusHighlightID = -1; |
| @@ -902,7 +927,10 @@ Application::~Application() { · 104 unmodified lines | |||
| 902 | 927 | ||
| 903 | Leapmotion::destroy(); | 928 | Leapmotion::destroy(); |
| 904 | RealSense::destroy(); | 929 | RealSense::destroy(); |
| 930 | |||
| 931 | #if 0 | ||
| 905 | ConnexionClient::getInstance().destroy(); | 932 | ConnexionClient::getInstance().destroy(); |
| 933 | #endif | ||
| 906 | 934 | ||
| 907 | qInstallMessageHandler(NULL); // NOTE: Do this as late as possible so we continue to get our log messages | 935 | qInstallMessageHandler(NULL); // NOTE: Do this as late as possible so we continue to get our log messages |
| 908 | } | 936 | } |
| @@ -975,6 +1003,8 @@ void Application::initializeUi() { · 66 unmodified lines | |||
| 975 | offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/")); | 1003 | offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/")); |
| 976 | offscreenUi->load("Root.qml"); | 1004 | offscreenUi->load("Root.qml"); |
| 977 | offscreenUi->load("RootMenu.qml"); | 1005 | offscreenUi->load("RootMenu.qml"); |
| 1006 | auto scriptingInterface = DependencyManager::get<controller::ScriptingInterface>(); | ||
| 1007 | offscreenUi->getRootContext()->setContextProperty("Controller", scriptingInterface.data()); | ||
| 978 | _glWidget->installEventFilter(offscreenUi.data()); | 1008 | _glWidget->installEventFilter(offscreenUi.data()); |
| 979 | VrMenu::load(); | 1009 | VrMenu::load(); |
| 980 | VrMenu::executeQueuedLambdas(); | 1010 | VrMenu::executeQueuedLambdas(); |
| @@ -1003,7 +1033,10 @@ void Application::initializeUi() { · 22 unmodified lines | |||
| 1003 | foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { | 1033 | foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { |
| 1004 | QString name = inputPlugin->getName(); | 1034 | QString name = inputPlugin->getName(); |
| 1005 | if (name == KeyboardMouseDevice::NAME) { | 1035 | if (name == KeyboardMouseDevice::NAME) { |
| 1006 | _keyboardMouseDevice = static_cast<KeyboardMouseDevice*>(inputPlugin.data()); // TODO: this seems super hacky | 1036 | auto kbm = static_cast<KeyboardMouseDevice*>(inputPlugin.data()); |
| 1037 | // FIXME incredibly evil.... _keyboardMouseDevice is now owned by | ||
| 1038 | // both a QSharedPointer and a std::shared_ptr | ||
| 1039 | _keyboardMouseDevice = std::shared_ptr<KeyboardMouseDevice>(kbm); | ||
| 1007 | } | 1040 | } |
| 1008 | } | 1041 | } |
| 1009 | updateInputModes(); | 1042 | updateInputModes(); |
| @@ -1456,7 +1489,7 @@ bool Application::event(QEvent* event) { · 446 unmodified lines | |||
| 1456 | } | 1489 | } |
| 1457 | 1490 | ||
| 1458 | if (HFActionEvent::types().contains(event->type())) { | 1491 | if (HFActionEvent::types().contains(event->type())) { |
| 1459 | _controllerScriptingInterface.handleMetaEvent(static_cast<HFMetaEvent*>(event)); | 1492 | _controllerScriptingInterface->handleMetaEvent(static_cast<HFMetaEvent*>(event)); |
| 1460 | } | 1493 | } |
| 1461 | 1494 | ||
| 1462 | return QApplication::event(event); | 1495 | return QApplication::event(event); |
| @@ -1470,7 +1503,7 @@ bool Application::eventFilter(QObject* object, QEvent* event) { · 7 unmodified lines | |||
| 1470 | } | 1503 | } |
| 1471 | 1504 | ||
| 1472 | // Filter out captured keys before they're used for shortcut actions. | 1505 | // Filter out captured keys before they're used for shortcut actions. |
| 1473 | if (_controllerScriptingInterface.isKeyCaptured(static_cast<QKeyEvent*>(event))) { | 1506 | if (_controllerScriptingInterface->isKeyCaptured(static_cast<QKeyEvent*>(event))) { |
| 1474 | event->accept(); | 1507 | event->accept(); |
| 1475 | return true; | 1508 | return true; |
| 1476 | } | 1509 | } |
| @@ -1485,10 +1518,10 @@ void Application::keyPressEvent(QKeyEvent* event) { · 8 unmodified lines | |||
| 1485 | _altPressed = event->key() == Qt::Key_Alt; | 1518 | _altPressed = event->key() == Qt::Key_Alt; |
| 1486 | _keysPressed.insert(event->key()); | 1519 | _keysPressed.insert(event->key()); |
| 1487 | 1520 | ||
| 1488 | _controllerScriptingInterface.emitKeyPressEvent(event); // send events to any registered scripts | 1521 | _controllerScriptingInterface->emitKeyPressEvent(event); // send events to any registered scripts |
| 1489 | 1522 | ||
| 1490 | // if one of our scripts have asked to capture this event, then stop processing it | 1523 | // if one of our scripts have asked to capture this event, then stop processing it |
| 1491 | if (_controllerScriptingInterface.isKeyCaptured(event)) { | 1524 | if (_controllerScriptingInterface->isKeyCaptured(event)) { |
| 1492 | return; | 1525 | return; |
| 1493 | } | 1526 | } |
| 1494 | 1527 | ||
| @@ -1518,6 +1551,13 @@ void Application::keyPressEvent(QKeyEvent* event) { · 23 unmodified lines | |||
| 1518 | if (isMeta) { | 1551 | if (isMeta) { |
| 1519 | auto offscreenUi = DependencyManager::get<OffscreenUi>(); | 1552 | auto offscreenUi = DependencyManager::get<OffscreenUi>(); |
| 1520 | offscreenUi->load("Browser.qml"); | 1553 | offscreenUi->load("Browser.qml"); |
| 1554 | } | ||
| 1555 | break; | ||
| 1556 | |||
| 1557 | case Qt::Key_X: | ||
| 1558 | if (isMeta && isShifted) { | ||
| 1559 | auto offscreenUi = DependencyManager::get<OffscreenUi>(); | ||
| 1560 | offscreenUi->load("TestControllers.qml"); | ||
| 1521 | } | 1561 | } |
| 1522 | break; | 1562 | break; |
| 1523 | 1563 | ||
| @@ -1752,10 +1792,10 @@ void Application::keyReleaseEvent(QKeyEvent* event) { · 228 unmodified lines | |||
| 1752 | 1792 | ||
| 1753 | _keysPressed.remove(event->key()); | 1793 | _keysPressed.remove(event->key()); |
| 1754 | 1794 | ||
| 1755 | _controllerScriptingInterface.emitKeyReleaseEvent(event); // send events to any registered scripts | 1795 | _controllerScriptingInterface->emitKeyReleaseEvent(event); // send events to any registered scripts |
| 1756 | 1796 | ||
| 1757 | // if one of our scripts have asked to capture this event, then stop processing it | 1797 | // if one of our scripts have asked to capture this event, then stop processing it |
| 1758 | if (_controllerScriptingInterface.isKeyCaptured(event)) { | 1798 | if (_controllerScriptingInterface->isKeyCaptured(event)) { |
| 1759 | return; | 1799 | return; |
| 1760 | } | 1800 | } |
| 1761 | 1801 | ||
| @@ -1796,7 +1836,9 @@ void Application::focusOutEvent(QFocusEvent* event) { · 34 unmodified lines | |||
| 1796 | inputPlugin->pluginFocusOutEvent(); | 1836 | inputPlugin->pluginFocusOutEvent(); |
| 1797 | } | 1837 | } |
| 1798 | } | 1838 | } |
| 1839 | #if 0 | ||
| 1799 | ConnexionData::getInstance().focusOutEvent(); | 1840 | ConnexionData::getInstance().focusOutEvent(); |
| 1841 | #endif | ||
| 1800 | 1842 | ||
| 1801 | // synthesize events for keys currently pressed, since we may not get their release events | 1843 | // synthesize events for keys currently pressed, since we may not get their release events |
| 1802 | foreach (int key, _keysPressed) { | 1844 | foreach (int key, _keysPressed) { |
| @@ -1844,10 +1886,10 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { · 41 unmodified lines | |||
| 1844 | 1886 | ||
| 1845 | 1887 | ||
| 1846 | _entities.mouseMoveEvent(&mappedEvent, deviceID); | 1888 | _entities.mouseMoveEvent(&mappedEvent, deviceID); |
| 1847 | _controllerScriptingInterface.emitMouseMoveEvent(&mappedEvent, deviceID); // send events to any registered scripts | 1889 | _controllerScriptingInterface->emitMouseMoveEvent(&mappedEvent, deviceID); // send events to any registered scripts |
| 1848 | 1890 | ||
| 1849 | // if one of our scripts have asked to capture this event, then stop processing it | 1891 | // if one of our scripts have asked to capture this event, then stop processing it |
| 1850 | if (_controllerScriptingInterface.isMouseCaptured()) { | 1892 | if (_controllerScriptingInterface->isMouseCaptured()) { |
| 1851 | return; | 1893 | return; |
| 1852 | } | 1894 | } |
| 1853 | 1895 | ||
| @@ -1872,10 +1914,10 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { · 18 unmodified lines | |||
| 1872 | _entities.mousePressEvent(&mappedEvent, deviceID); | 1914 | _entities.mousePressEvent(&mappedEvent, deviceID); |
| 1873 | } | 1915 | } |
| 1874 | 1916 | ||
| 1875 | _controllerScriptingInterface.emitMousePressEvent(&mappedEvent); // send events to any registered scripts | 1917 | _controllerScriptingInterface->emitMousePressEvent(&mappedEvent); // send events to any registered scripts |
| 1876 | 1918 | ||
| 1877 | // if one of our scripts have asked to capture this event, then stop processing it | 1919 | // if one of our scripts have asked to capture this event, then stop processing it |
| 1878 | if (_controllerScriptingInterface.isMouseCaptured()) { | 1920 | if (_controllerScriptingInterface->isMouseCaptured()) { |
| 1879 | return; | 1921 | return; |
| 1880 | } | 1922 | } |
| 1881 | 1923 | ||
| @@ -1897,11 +1939,11 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { · 15 unmodified lines | |||
| 1897 | 1939 | ||
| 1898 | void Application::mouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID) { | 1940 | void Application::mouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID) { |
| 1899 | // if one of our scripts have asked to capture this event, then stop processing it | 1941 | // if one of our scripts have asked to capture this event, then stop processing it |
| 1900 | if (_controllerScriptingInterface.isMouseCaptured()) { | 1942 | if (_controllerScriptingInterface->isMouseCaptured()) { |
| 1901 | return; | 1943 | return; |
| 1902 | } | 1944 | } |
| 1903 | 1945 | ||
| 1904 | _controllerScriptingInterface.emitMouseDoublePressEvent(event); | 1946 | _controllerScriptingInterface->emitMouseDoublePressEvent(event); |
| 1905 | } | 1947 | } |
| 1906 | 1948 | ||
| 1907 | void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { | 1949 | void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { |
| @@ -1917,10 +1959,10 @@ void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { · 9 unmodified lines | |||
| 1917 | _entities.mouseReleaseEvent(&mappedEvent, deviceID); | 1959 | _entities.mouseReleaseEvent(&mappedEvent, deviceID); |
| 1918 | } | 1960 | } |
| 1919 | 1961 | ||
| 1920 | _controllerScriptingInterface.emitMouseReleaseEvent(&mappedEvent); // send events to any registered scripts | 1962 | _controllerScriptingInterface->emitMouseReleaseEvent(&mappedEvent); // send events to any registered scripts |
| 1921 | 1963 | ||
| 1922 | // if one of our scripts have asked to capture this event, then stop processing it | 1964 | // if one of our scripts have asked to capture this event, then stop processing it |
| 1923 | if (_controllerScriptingInterface.isMouseCaptured()) { | 1965 | if (_controllerScriptingInterface->isMouseCaptured()) { |
| 1924 | return; | 1966 | return; |
| 1925 | } | 1967 | } |
| 1926 | 1968 | ||
| @@ -1943,12 +1985,12 @@ void Application::touchUpdateEvent(QTouchEvent* event) { · 16 unmodified lines | |||
| 1943 | 1985 | ||
| 1944 | if (event->type() == QEvent::TouchUpdate) { | 1986 | if (event->type() == QEvent::TouchUpdate) { |
| 1945 | TouchEvent thisEvent(*event, _lastTouchEvent); | 1987 | TouchEvent thisEvent(*event, _lastTouchEvent); |
| 1946 | _controllerScriptingInterface.emitTouchUpdateEvent(thisEvent); // send events to any registered scripts | 1988 | _controllerScriptingInterface->emitTouchUpdateEvent(thisEvent); // send events to any registered scripts |
| 1947 | _lastTouchEvent = thisEvent; | 1989 | _lastTouchEvent = thisEvent; |
| 1948 | } | 1990 | } |
| 1949 | 1991 | ||
| 1950 | // if one of our scripts have asked to capture this event, then stop processing it | 1992 | // if one of our scripts have asked to capture this event, then stop processing it |
| 1951 | if (_controllerScriptingInterface.isTouchCaptured()) { | 1993 | if (_controllerScriptingInterface->isTouchCaptured()) { |
| 1952 | return; | 1994 | return; |
| 1953 | } | 1995 | } |
| 1954 | 1996 | ||
| @@ -1960,13 +2002,13 @@ void Application::touchUpdateEvent(QTouchEvent* event) { · 5 unmodified lines | |||
| 1960 | void Application::touchBeginEvent(QTouchEvent* event) { | 2002 | void Application::touchBeginEvent(QTouchEvent* event) { |
| 1961 | _altPressed = false; | 2003 | _altPressed = false; |
| 1962 | TouchEvent thisEvent(*event); // on touch begin, we don't compare to last event | 2004 | TouchEvent thisEvent(*event); // on touch begin, we don't compare to last event |
| 1963 | _controllerScriptingInterface.emitTouchBeginEvent(thisEvent); // send events to any registered scripts | 2005 | _controllerScriptingInterface->emitTouchBeginEvent(thisEvent); // send events to any registered scripts |
| 1964 | 2006 | ||
| 1965 | _lastTouchEvent = thisEvent; // and we reset our last event to this event before we call our update | 2007 | _lastTouchEvent = thisEvent; // and we reset our last event to this event before we call our update |
| 1966 | touchUpdateEvent(event); | 2008 | touchUpdateEvent(event); |
| 1967 | 2009 | ||
| 1968 | // if one of our scripts have asked to capture this event, then stop processing it | 2010 | // if one of our scripts have asked to capture this event, then stop processing it |
| 1969 | if (_controllerScriptingInterface.isTouchCaptured()) { | 2011 | if (_controllerScriptingInterface->isTouchCaptured()) { |
| 1970 | return; | 2012 | return; |
| 1971 | } | 2013 | } |
| 1972 | 2014 | ||
| @@ -1979,11 +2021,11 @@ void Application::touchBeginEvent(QTouchEvent* event) { · 6 unmodified lines | |||
| 1979 | void Application::touchEndEvent(QTouchEvent* event) { | 2021 | void Application::touchEndEvent(QTouchEvent* event) { |
| 1980 | _altPressed = false; | 2022 | _altPressed = false; |
| 1981 | TouchEvent thisEvent(*event, _lastTouchEvent); | 2023 | TouchEvent thisEvent(*event, _lastTouchEvent); |
| 1982 | _controllerScriptingInterface.emitTouchEndEvent(thisEvent); // send events to any registered scripts | 2024 | _controllerScriptingInterface->emitTouchEndEvent(thisEvent); // send events to any registered scripts |
| 1983 | _lastTouchEvent = thisEvent; | 2025 | _lastTouchEvent = thisEvent; |
| 1984 | 2026 | ||
| 1985 | // if one of our scripts have asked to capture this event, then stop processing it | 2027 | // if one of our scripts have asked to capture this event, then stop processing it |
| 1986 | if (_controllerScriptingInterface.isTouchCaptured()) { | 2028 | if (_controllerScriptingInterface->isTouchCaptured()) { |
| 1987 | return; | 2029 | return; |
| 1988 | } | 2030 | } |
| 1989 | 2031 | ||
| @@ -1996,10 +2038,10 @@ void Application::touchEndEvent(QTouchEvent* event) { · 6 unmodified lines | |||
| 1996 | 2038 | ||
| 1997 | void Application::wheelEvent(QWheelEvent* event) { | 2039 | void Application::wheelEvent(QWheelEvent* event) { |
| 1998 | _altPressed = false; | 2040 | _altPressed = false; |
| 1999 | _controllerScriptingInterface.emitWheelEvent(event); // send events to any registered scripts | 2041 | _controllerScriptingInterface->emitWheelEvent(event); // send events to any registered scripts |
| 2000 | 2042 | ||
| 2001 | // if one of our scripts have asked to capture this event, then stop processing it | 2043 | // if one of our scripts have asked to capture this event, then stop processing it |
| 2002 | if (_controllerScriptingInterface.isWheelCaptured()) { | 2044 | if (_controllerScriptingInterface->isWheelCaptured()) { |
| 2003 | return; | 2045 | return; |
| 2004 | } | 2046 | } |
| 2005 | 2047 | ||
| @@ -2164,7 +2206,7 @@ float Application::getAvatarSimrate() { · 158 unmodified lines | |||
| 2164 | } | 2206 | } |
| 2165 | 2207 | ||
| 2166 | void Application::setLowVelocityFilter(bool lowVelocityFilter) { | 2208 | void Application::setLowVelocityFilter(bool lowVelocityFilter) { |
| 2167 | InputDevice::setLowVelocityFilter(lowVelocityFilter); | 2209 | controller::InputDevice::setLowVelocityFilter(lowVelocityFilter); |
| 2168 | } | 2210 | } |
| 2169 | 2211 | ||
| 2170 | ivec2 Application::getMouse() const { | 2212 | ivec2 Application::getMouse() const { |
| @@ -2694,13 +2736,9 @@ void Application::update(float deltaTime) { · 523 unmodified lines | |||
| 2694 | userInputMapper->setSensorToWorldMat(myAvatar->getSensorToWorldMatrix()); | 2736 | userInputMapper->setSensorToWorldMat(myAvatar->getSensorToWorldMatrix()); |
| 2695 | userInputMapper->update(deltaTime); | 2737 | userInputMapper->update(deltaTime); |
| 2696 | 2738 | ||
| 2697 | // This needs to go after userInputMapper->update() because of the keyboard | ||
| 2698 | bool jointsCaptured = false; | 2739 | bool jointsCaptured = false; |
| 2699 | auto inputPlugins = PluginManager::getInstance()->getInputPlugins(); | 2740 | for (auto inputPlugin : PluginManager::getInstance()->getInputPlugins()) { |
| 2700 | foreach(auto inputPlugin, inputPlugins) { | 2741 | if (inputPlugin->isActive()) { |
| 2701 | QString name = inputPlugin->getName(); | ||
| 2702 | QAction* action = Menu::getInstance()->getActionForOption(name); | ||
| 2703 | if (action && action->isChecked()) { | ||
| 2704 | inputPlugin->pluginUpdate(deltaTime, jointsCaptured); | 2742 | inputPlugin->pluginUpdate(deltaTime, jointsCaptured); |
| 2705 | if (inputPlugin->isJointController()) { | 2743 | if (inputPlugin->isJointController()) { |
| 2706 | jointsCaptured = true; | 2744 | jointsCaptured = true; |
| @@ -2708,19 +2746,14 @@ void Application::update(float deltaTime) { · 1 unmodified line | |||
| 2708 | } | 2746 | } |
| 2709 | } | 2747 | } |
| 2710 | 2748 | ||
| 2711 | // Dispatch input events | ||
| 2712 | _controllerScriptingInterface.updateInputControllers(); | ||
| 2713 | |||
| 2714 | // Transfer the user inputs to the driveKeys | 2749 | // Transfer the user inputs to the driveKeys |
| 2750 | // FIXME can we drop drive keys and just have the avatar read the action states directly? | ||
| 2715 | myAvatar->clearDriveKeys(); | 2751 | myAvatar->clearDriveKeys(); |
| 2716 | if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT) { | 2752 | if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT) { |
| 2717 | if (!_controllerScriptingInterface.areActionsCaptured()) { | 2753 | if (!_controllerScriptingInterface->areActionsCaptured()) { |
| 2718 | myAvatar->setDriveKeys(FWD, userInputMapper->getActionState(UserInputMapper::LONGITUDINAL_FORWARD)); | 2754 | myAvatar->setDriveKeys(TRANSLATE_Z, -1.0f * userInputMapper->getActionState(controller::Action::TRANSLATE_Z)); |
| 2719 | myAvatar->setDriveKeys(BACK, userInputMapper->getActionState(UserInputMapper::LONGITUDINAL_BACKWARD)); | 2755 | myAvatar->setDriveKeys(TRANSLATE_Y, userInputMapper->getActionState(controller::Action::TRANSLATE_Y)); |
| 2720 | myAvatar->setDriveKeys(UP, userInputMapper->getActionState(UserInputMapper::VERTICAL_UP)); | 2756 | myAvatar->setDriveKeys(TRANSLATE_X, userInputMapper->getActionState(controller::Action::TRANSLATE_X)); |
| 2721 | myAvatar->setDriveKeys(DOWN, userInputMapper->getActionState(UserInputMapper::VERTICAL_DOWN)); | ||
| 2722 | myAvatar->setDriveKeys(LEFT, userInputMapper->getActionState(UserInputMapper::LATERAL_LEFT)); | ||
| 2723 | myAvatar->setDriveKeys(RIGHT, userInputMapper->getActionState(UserInputMapper::LATERAL_RIGHT)); | ||
| 2724 | if (deltaTime > FLT_EPSILON) { | 2757 | if (deltaTime > FLT_EPSILON) { |
| 2725 | // For rotations what we really want are meausures of "angles per second" (in order to prevent | 2758 | // For rotations what we really want are meausures of "angles per second" (in order to prevent |
| 2726 | // fps-dependent spin rates) so we need to scale the units of the controller contribution. | 2759 | // fps-dependent spin rates) so we need to scale the units of the controller contribution. |
| @@ -2728,25 +2761,24 @@ void Application::update(float deltaTime) { · 1 unmodified line | |||
| 2728 | // controllers to provide a delta_per_second value rather than a raw delta.) | 2761 | // controllers to provide a delta_per_second value rather than a raw delta.) |
| 2729 | const float EXPECTED_FRAME_RATE = 60.0f; | 2762 | const float EXPECTED_FRAME_RATE = 60.0f; |
| 2730 | float timeFactor = EXPECTED_FRAME_RATE * deltaTime; | 2763 | float timeFactor = EXPECTED_FRAME_RATE * deltaTime; |
| 2731 | myAvatar->setDriveKeys(ROT_UP, userInputMapper->getActionState(UserInputMapper::PITCH_UP) / timeFactor); | 2764 | myAvatar->setDriveKeys(PITCH, -1.0f * userInputMapper->getActionState(controller::Action::PITCH) / timeFactor); |
| 2732 | myAvatar->setDriveKeys(ROT_DOWN, userInputMapper->getActionState(UserInputMapper::PITCH_DOWN) / timeFactor); | 2765 | myAvatar->setDriveKeys(YAW, -1.0f * userInputMapper->getActionState(controller::Action::YAW) / timeFactor); |
| 2733 | myAvatar->setDriveKeys(ROT_LEFT, userInputMapper->getActionState(UserInputMapper::YAW_LEFT) / timeFactor); | 2766 | myAvatar->setDriveKeys(STEP_YAW, -1.0f * userInputMapper->getActionState(controller::Action::STEP_YAW) / timeFactor); |
| 2734 | myAvatar->setDriveKeys(ROT_RIGHT, userInputMapper->getActionState(UserInputMapper::YAW_RIGHT) / timeFactor); | ||
| 2735 | } | 2767 | } |
| 2736 | } | 2768 | } |
| 2737 | myAvatar->setDriveKeys(BOOM_IN, userInputMapper->getActionState(UserInputMapper::BOOM_IN)); | 2769 | myAvatar->setDriveKeys(ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z)); |
| 2738 | myAvatar->setDriveKeys(BOOM_OUT, userInputMapper->getActionState(UserInputMapper::BOOM_OUT)); | ||
| 2739 | } | 2770 | } |
| 2740 | UserInputMapper::PoseValue leftHand = userInputMapper->getPoseState(UserInputMapper::LEFT_HAND); | 2771 | |
| 2741 | UserInputMapper::PoseValue rightHand = userInputMapper->getPoseState(UserInputMapper::RIGHT_HAND); | 2772 | controller::Pose leftHand = userInputMapper->getPoseState(controller::Action::LEFT_HAND); |
| 2773 | controller::Pose rightHand = userInputMapper->getPoseState(controller::Action::RIGHT_HAND); | ||
| 2742 | Hand* hand = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHand(); | 2774 | Hand* hand = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHand(); |
| 2743 | setPalmData(hand, leftHand, deltaTime, LEFT_HAND_INDEX, userInputMapper->getActionState(UserInputMapper::LEFT_HAND_CLICK)); | 2775 | setPalmData(hand, leftHand, deltaTime, HandData::LeftHand, userInputMapper->getActionState(controller::Action::LEFT_HAND_CLICK)); |
| 2744 | setPalmData(hand, rightHand, deltaTime, RIGHT_HAND_INDEX, userInputMapper->getActionState(UserInputMapper::RIGHT_HAND_CLICK)); | 2776 | setPalmData(hand, rightHand, deltaTime, HandData::RightHand, userInputMapper->getActionState(controller::Action::RIGHT_HAND_CLICK)); |
| 2745 | if (Menu::getInstance()->isOptionChecked(MenuOption::EnableHandMouseInput)) { | 2777 | if (Menu::getInstance()->isOptionChecked(MenuOption::EnableHandMouseInput)) { |
| 2746 | emulateMouse(hand, userInputMapper->getActionState(UserInputMapper::LEFT_HAND_CLICK), | 2778 | emulateMouse(hand, userInputMapper->getActionState(controller::Action::LEFT_HAND_CLICK), |
| 2747 | userInputMapper->getActionState(UserInputMapper::SHIFT), LEFT_HAND_INDEX); | 2779 | userInputMapper->getActionState(controller::Action::SHIFT), HandData::LeftHand); |
| 2748 | emulateMouse(hand, userInputMapper->getActionState(UserInputMapper::RIGHT_HAND_CLICK), | 2780 | emulateMouse(hand, userInputMapper->getActionState(controller::Action::RIGHT_HAND_CLICK), |
| 2749 | userInputMapper->getActionState(UserInputMapper::SHIFT), RIGHT_HAND_INDEX); | 2781 | userInputMapper->getActionState(controller::Action::SHIFT), HandData::RightHand); |
| 2750 | } | 2782 | } |
| 2751 | 2783 | ||
| 2752 | updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process... | 2784 | updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process... |
| @@ -4807,86 +4839,80 @@ mat4 Application::getHMDSensorPose() const { · 2054 unmodified lines | |||
| 4807 | return mat4(); | 4839 | return mat4(); |
| 4808 | } | 4840 | } |
| 4809 | 4841 | ||
| 4810 | void Application::setPalmData(Hand* hand, UserInputMapper::PoseValue pose, float deltaTime, int index, float triggerValue) { | 4842 | void Application::setPalmData(Hand* hand, const controller::Pose& pose, float deltaTime, HandData::Hand whichHand, float triggerValue) { |
| 4811 | PalmData* palm; | ||
| 4812 | bool foundHand = false; | ||
| 4813 | for (size_t j = 0; j < hand->getNumPalms(); j++) { | ||
| 4814 | if (hand->getPalms()[j].getSixenseID() == index) { | ||
| 4815 | palm = &(hand->getPalms()[j]); | ||
| 4816 | foundHand = true; | ||
| 4817 | break; | ||
| 4818 | } | ||
| 4819 | } | ||
| 4820 | if (!foundHand) { | ||
| 4821 | PalmData newPalm(hand); | ||
| 4822 | hand->getPalms().push_back(newPalm); | ||
| 4823 | palm = &(hand->getPalms()[hand->getNumPalms() - 1]); | ||
| 4824 | palm->setSixenseID(index); | ||
| 4825 | } | ||
| 4826 | |||
| 4827 | palm->setActive(pose.isValid()); | ||
| 4828 | 4843 | ||
| 4829 | // transform from sensor space, to world space, to avatar model space. | 4844 | // NOTE: the Hand::modifyPalm() will allow the lambda to modify the palm data while ensuring some other user isn't |
| 4830 | glm::mat4 poseMat = createMatFromQuatAndPos(pose.getRotation(), pose.getTranslation()); | 4845 | // reading or writing to the Palms. This is definitely not the best way of handling this, and I'd like to see more |
| 4831 | glm::mat4 sensorToWorldMat = getMyAvatar()->getSensorToWorldMatrix(); | 4846 | // of this palm manipulation in the Hand class itself. But unfortunately the Hand and Palm don't knbow about |
| 4832 | glm::mat4 modelMat = createMatFromQuatAndPos(getMyAvatar()->getOrientation(), getMyAvatar()->getPosition()); | 4847 | // controller::Pose. More work is needed to clean this up. |
| 4833 | glm::mat4 objectPose = glm::inverse(modelMat) * sensorToWorldMat * poseMat; | 4848 | hand->modifyPalm(whichHand, [&](PalmData& palm) { |
| 4849 | auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); | ||
| 4850 | palm.setActive(pose.isValid()); | ||
| 4834 | 4851 | ||
| 4835 | glm::vec3 position = extractTranslation(objectPose); | 4852 | // transform from sensor space, to world space, to avatar model space. |
| 4836 | glm::quat rotation = glm::quat_cast(objectPose); | 4853 | glm::mat4 poseMat = createMatFromQuatAndPos(pose.getRotation(), pose.getTranslation()); |
| 4854 | glm::mat4 sensorToWorldMat = myAvatar->getSensorToWorldMatrix(); | ||
| 4855 | glm::mat4 modelMat = createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition()); | ||
| 4856 | glm::mat4 objectPose = glm::inverse(modelMat) * sensorToWorldMat * poseMat; | ||
| 4837 | 4857 | ||
| 4838 | // Compute current velocity from position change | 4858 | glm::vec3 position = extractTranslation(objectPose); |
| 4839 | glm::vec3 rawVelocity; | 4859 | glm::quat rotation = glm::quat_cast(objectPose); |
| 4840 | if (deltaTime > 0.0f) { | 4860 | |
| 4841 | rawVelocity = (position - palm->getRawPosition()) / deltaTime; | 4861 | // Compute current velocity from position change |
| 4842 | } else { | 4862 | glm::vec3 rawVelocity; |
| 4843 | rawVelocity = glm::vec3(0.0f); | 4863 | if (deltaTime > 0.0f) { |
| 4844 | } | 4864 | rawVelocity = (position - palm.getRawPosition()) / deltaTime; |
| 4845 | palm->setRawVelocity(rawVelocity); // meters/sec | 4865 | } else { |
| 4866 | rawVelocity = glm::vec3(0.0f); | ||
| 4867 | } | ||
| 4868 | palm.setRawVelocity(rawVelocity); // meters/sec | ||
| 4846 | 4869 | ||
| 4847 | // Angular Velocity of Palm | 4870 | // Angular Velocity of Palm |
| 4848 | glm::quat deltaRotation = rotation * glm::inverse(palm->getRawRotation()); | 4871 | glm::quat deltaRotation = rotation * glm::inverse(palm.getRawRotation()); |
| 4849 | glm::vec3 angularVelocity(0.0f); | 4872 | glm::vec3 angularVelocity(0.0f); |
| 4850 | float rotationAngle = glm::angle(deltaRotation); | 4873 | float rotationAngle = glm::angle(deltaRotation); |
| 4851 | if ((rotationAngle > EPSILON) && (deltaTime > 0.0f)) { | 4874 | if ((rotationAngle > EPSILON) && (deltaTime > 0.0f)) { |
| 4852 | angularVelocity = glm::normalize(glm::axis(deltaRotation)); | 4875 | angularVelocity = glm::normalize(glm::axis(deltaRotation)); |
| 4853 | angularVelocity *= (rotationAngle / deltaTime); | 4876 | angularVelocity *= (rotationAngle / deltaTime); |
| 4854 | palm->setRawAngularVelocity(angularVelocity); | 4877 | palm.setRawAngularVelocity(angularVelocity); |
| 4855 | } else { | 4878 | } else { |
| 4856 | palm->setRawAngularVelocity(glm::vec3(0.0f)); | 4879 | palm.setRawAngularVelocity(glm::vec3(0.0f)); |
| 4857 | } | 4880 | } |
| 4858 | 4881 | ||
| 4859 | if (InputDevice::getLowVelocityFilter()) { | 4882 | if (controller::InputDevice::getLowVelocityFilter()) { |
| 4860 | // Use a velocity sensitive filter to damp small motions and preserve large ones with | 4883 | // Use a velocity sensitive filter to damp small motions and preserve large ones with |
| 4861 | // no latency. | 4884 | // no latency. |
| 4862 | float velocityFilter = glm::clamp(1.0f - glm::length(rawVelocity), 0.0f, 1.0f); | 4885 | float velocityFilter = glm::clamp(1.0f - glm::length(rawVelocity), 0.0f, 1.0f); |
| 4863 | position = palm->getRawPosition() * velocityFilter + position * (1.0f - velocityFilter); | 4886 | position = palm.getRawPosition() * velocityFilter + position * (1.0f - velocityFilter); |
| 4864 | rotation = safeMix(palm->getRawRotation(), rotation, 1.0f - velocityFilter); | 4887 | rotation = safeMix(palm.getRawRotation(), rotation, 1.0f - velocityFilter); |
| 4865 | } | 4888 | } |
| 4866 | palm->setRawPosition(position); | 4889 | palm.setRawPosition(position); |
| 4867 | palm->setRawRotation(rotation); | 4890 | palm.setRawRotation(rotation); |
| 4868 | 4891 | ||
| 4869 | // Store the one fingertip in the palm structure so we can track velocity | 4892 | // Store the one fingertip in the palm structure so we can track velocity |
| 4870 | const float FINGER_LENGTH = 0.3f; // meters | 4893 | const float FINGER_LENGTH = 0.3f; // meters |
| 4871 | const glm::vec3 FINGER_VECTOR(0.0f, FINGER_LENGTH, 0.0f); | 4894 | const glm::vec3 FINGER_VECTOR(0.0f, FINGER_LENGTH, 0.0f); |
| 4872 | const glm::vec3 newTipPosition = position + rotation * FINGER_VECTOR; | 4895 | const glm::vec3 newTipPosition = position + rotation * FINGER_VECTOR; |
| 4873 | glm::vec3 oldTipPosition = palm->getTipRawPosition(); | 4896 | glm::vec3 oldTipPosition = palm.getTipRawPosition(); |
| 4874 | if (deltaTime > 0.0f) { | 4897 | if (deltaTime > 0.0f) { |
| 4875 | palm->setTipVelocity((newTipPosition - oldTipPosition) / deltaTime); | 4898 | palm.setTipVelocity((newTipPosition - oldTipPosition) / deltaTime); |
| 4876 | } else { | 4899 | } else { |
| 4877 | palm->setTipVelocity(glm::vec3(0.0f)); | 4900 | palm.setTipVelocity(glm::vec3(0.0f)); |
| 4878 | } | 4901 | } |
| 4879 | palm->setTipPosition(newTipPosition); | 4902 | palm.setTipPosition(newTipPosition); |
| 4880 | palm->setTrigger(triggerValue); | 4903 | palm.setTrigger(triggerValue); // FIXME - we want to get rid of this idea of PalmData having a trigger |
| 4904 | }); | ||
| 4881 | } | 4905 | } |
| 4882 | 4906 | ||
| 4883 | void Application::emulateMouse(Hand* hand, float click, float shift, int index) { | 4907 | void Application::emulateMouse(Hand* hand, float click, float shift, HandData::Hand whichHand) { |
| 4908 | auto palms = hand->getCopyOfPalms(); | ||
| 4909 | |||
| 4884 | // Locate the palm, if it exists and is active | 4910 | // Locate the palm, if it exists and is active |
| 4885 | PalmData* palm; | 4911 | PalmData* palm; |
| 4886 | bool foundHand = false; | 4912 | bool foundHand = false; |
| 4887 | for (size_t j = 0; j < hand->getNumPalms(); j++) { | 4913 | for (size_t j = 0; j < palms.size(); j++) { |
| 4888 | if (hand->getPalms()[j].getSixenseID() == index) { | 4914 | if (palms[j].whichHand() == whichHand) { |
| 4889 | palm = &(hand->getPalms()[j]); | 4915 | palm = &(palms[j]); |
| 4890 | foundHand = true; | 4916 | foundHand = true; |
| 4891 | break; | 4917 | break; |
| 4892 | } | 4918 | } |
| @@ -4898,12 +4924,14 @@ void Application::emulateMouse(Hand* hand, float click, float shift, int index) · 5 unmodified lines | |||
| 4898 | // Process the mouse events | 4924 | // Process the mouse events |
| 4899 | QPoint pos; | 4925 | QPoint pos; |
| 4900 | 4926 | ||
| 4901 | unsigned int deviceID = index == 0 ? CONTROLLER_0_EVENT : CONTROLLER_1_EVENT; | 4927 | |
| 4928 | // FIXME - this mouse emulation stuff needs to be reworked for new controller input plugins | ||
| 4929 | unsigned int deviceID = whichHand == HandData::LeftHand ? CONTROLLER_0_EVENT : CONTROLLER_1_EVENT; | ||
| 4930 | int index = (int)whichHand; // FIXME - hack attack | ||
| 4902 | 4931 | ||
| 4903 | if (isHMDMode()) { | 4932 | if (isHMDMode()) { |
| 4904 | pos = getApplicationCompositor().getPalmClickLocation(palm); | 4933 | pos = getApplicationCompositor().getPalmClickLocation(palm); |
| 4905 | } | 4934 | } else { |
| 4906 | else { | ||
| 4907 | // Get directon relative to avatar orientation | 4935 | // Get directon relative to avatar orientation |
| 4908 | glm::vec3 direction = glm::inverse(getMyAvatar()->getOrientation()) * palm->getFingerDirection(); | 4936 | glm::vec3 direction = glm::inverse(getMyAvatar()->getOrientation()) * palm->getFingerDirection(); |
| 4909 | 4937 | ||
| @@ -4912,7 +4940,7 @@ void Application::emulateMouse(Hand* hand, float click, float shift, int index) · 2 unmodified lines | |||
| 4912 | float yAngle = 0.5f - ((atan2f(direction.z, direction.y) + (float)M_PI_2)); | 4940 | float yAngle = 0.5f - ((atan2f(direction.z, direction.y) + (float)M_PI_2)); |
| 4913 | auto canvasSize = getCanvasSize(); | 4941 | auto canvasSize = getCanvasSize(); |
| 4914 | // Get the pixel range over which the xAngle and yAngle are scaled | 4942 | // Get the pixel range over which the xAngle and yAngle are scaled |
| 4915 | float cursorRange = canvasSize.x * InputDevice::getCursorPixelRangeMult(); | 4943 | float cursorRange = canvasSize.x * controller::InputDevice::getCursorPixelRangeMult(); |
| 4916 | 4944 | ||
| 4917 | pos.setX(canvasSize.x / 2.0f + cursorRange * xAngle); | 4945 | pos.setX(canvasSize.x / 2.0f + cursorRange * xAngle); |
| 4918 | pos.setY(canvasSize.y / 2.0f + cursorRange * yAngle); | 4946 | pos.setY(canvasSize.y / 2.0f + cursorRange * yAngle); |
| @@ -14,13 +14,15 @@ · 13 unmodified lines | |||
| 14 | 14 | ||
| 15 | #include <functional> | 15 | #include <functional> |
| 16 | 16 | ||
| 17 | #include <QApplication> | 17 | #include <QtCore/QHash> |
| 18 | #include <QHash> | 18 | #include <QtCore/QPointer> |
| 19 | #include <QImage> | 19 | #include <QtCore/QSet> |
| 20 | #include <QPointer> | 20 | #include <QtCore/QStringList> |
| 21 | #include <QSet> | 21 | |
| 22 | #include <QStringList> | 22 | #include <QtGui/QImage> |
| 23 | #include <QUndoStack> | 23 | |
| 24 | #include <QtWidgets/QApplication> | ||
| 25 | #include <QtWidgets/QUndoStack> | ||
| 24 | 26 | ||
| 25 | #include <AbstractScriptingServicesInterface.h> | 27 | #include <AbstractScriptingServicesInterface.h> |
| 26 | #include <AbstractViewStateInterface.h> | 28 | #include <AbstractViewStateInterface.h> |
| @@ -69,6 +71,10 @@ class FaceTracker; · 42 unmodified lines | |||
| 69 | class MainWindow; | 71 | class MainWindow; |
| 70 | class AssetUpload; | 72 | class AssetUpload; |
| 71 | 73 | ||
| 74 | namespace controller { | ||
| 75 | class StateController; | ||
| 76 | } | ||
| 77 | |||
| 72 | #ifdef Q_OS_WIN | 78 | #ifdef Q_OS_WIN |
| 73 | static const UINT UWM_IDENTIFY_INSTANCES = | 79 | static const UINT UWM_IDENTIFY_INSTANCES = |
| 74 | RegisterWindowMessage("UWM_IDENTIFY_INSTANCES_{8AB82783-B74A-4258-955B-8188C22AA0D6}_" + qgetenv("USERNAME")); | 80 | RegisterWindowMessage("UWM_IDENTIFY_INSTANCES_{8AB82783-B74A-4258-955B-8188C22AA0D6}_" + qgetenv("USERNAME")); |
| @@ -161,7 +167,7 @@ public: · 86 unmodified lines | |||
| 161 | 167 | ||
| 162 | ToolWindow* getToolWindow() { return _toolWindow ; } | 168 | ToolWindow* getToolWindow() { return _toolWindow ; } |
| 163 | 169 | ||
| 164 | virtual AbstractControllerScriptingInterface* getControllerScriptingInterface() { return &_controllerScriptingInterface; } | 170 | virtual controller::ScriptingInterface* getControllerScriptingInterface() { return _controllerScriptingInterface; } |
| 165 | virtual void registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine); | 171 | virtual void registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine); |
| 166 | 172 | ||
| 167 | QImage renderAvatarBillboard(RenderArgs* renderArgs); | 173 | QImage renderAvatarBillboard(RenderArgs* renderArgs); |
| @@ -350,8 +356,8 @@ private: · 182 unmodified lines | |||
| 350 | 356 | ||
| 351 | void update(float deltaTime); | 357 | void update(float deltaTime); |
| 352 | 358 | ||
| 353 | void setPalmData(Hand* hand, UserInputMapper::PoseValue pose, float deltaTime, int index, float triggerValue); | 359 | void setPalmData(Hand* hand, const controller::Pose& pose, float deltaTime, HandData::Hand whichHand, float triggerValue); |
| 354 | void emulateMouse(Hand* hand, float click, float shift, int index); | 360 | void emulateMouse(Hand* hand, float click, float shift, HandData::Hand whichHand); |
| 355 | 361 | ||
| 356 | // Various helper functions called during update() | 362 | // Various helper functions called during update() |
| 357 | void updateLOD(); | 363 | void updateLOD(); |
| @@ -440,7 +446,8 @@ private: · 82 unmodified lines | |||
| 440 | 446 | ||
| 441 | OctreeQuery _octreeQuery; // NodeData derived class for querying octee cells from octree servers | 447 | OctreeQuery _octreeQuery; // NodeData derived class for querying octee cells from octree servers |
| 442 | 448 | ||
| 443 | KeyboardMouseDevice* _keyboardMouseDevice{ nullptr }; // Default input device, the good old keyboard mouse and maybe touchpad | 449 | std::shared_ptr<controller::StateController> _applicationStateDevice; // Default ApplicationDevice reflecting the state of different properties of the session |
| 450 | std::shared_ptr<KeyboardMouseDevice> _keyboardMouseDevice; // Default input device, the good old keyboard mouse and maybe touchpad | ||
| 444 | AvatarUpdate* _avatarUpdate {nullptr}; | 451 | AvatarUpdate* _avatarUpdate {nullptr}; |
| 445 | SimpleMovingAverage _avatarSimsPerSecond {10}; | 452 | SimpleMovingAverage _avatarSimsPerSecond {10}; |
| 446 | int _avatarSimsPerSecondReport {0}; | 453 | int _avatarSimsPerSecondReport {0}; |
| @@ -474,8 +481,7 @@ private: · 27 unmodified lines | |||
| 474 | 481 | ||
| 475 | NodeToJurisdictionMap _entityServerJurisdictions; | 482 | NodeToJurisdictionMap _entityServerJurisdictions; |
| 476 | NodeToOctreeSceneStats _octreeServerSceneStats; | 483 | NodeToOctreeSceneStats _octreeServerSceneStats; |
| 477 | 484 | ControllerScriptingInterface* _controllerScriptingInterface{ nullptr }; | |
| 478 | ControllerScriptingInterface _controllerScriptingInterface; | ||
| 479 | QPointer<LogDialog> _logDialog; | 485 | QPointer<LogDialog> _logDialog; |
| 480 | QPointer<SnapshotShareDialog> _snapshotShareDialog; | 486 | QPointer<SnapshotShareDialog> _snapshotShareDialog; |
| 481 | 487 | ||
| @@ -521,10 +527,13 @@ private: · 39 unmodified lines | |||
| 521 | ApplicationCompositor _compositor; | 527 | ApplicationCompositor _compositor; |
| 522 | OverlayConductor _overlayConductor; | 528 | OverlayConductor _overlayConductor; |
| 523 | 529 | ||
| 524 | int _oldHandMouseX[2]; | 530 | |
| 525 | int _oldHandMouseY[2]; | 531 | // FIXME - Hand Controller to mouse emulation helpers. This is crufty and should be moved |
| 526 | bool _oldHandLeftClick[2]; | 532 | // into the input plugins or something. |
| 527 | bool _oldHandRightClick[2]; | 533 | int _oldHandMouseX[(int)HandData::NUMBER_OF_HANDS]; |
| 534 | int _oldHandMouseY[(int)HandData::NUMBER_OF_HANDS]; | ||
| 535 | bool _oldHandLeftClick[(int)HandData::NUMBER_OF_HANDS]; | ||
| 536 | bool _oldHandRightClick[(int)HandData::NUMBER_OF_HANDS]; | ||
| 528 | 537 | ||
| 529 | DialogsManagerScriptingInterface* _dialogsManagerScriptingInterface = new DialogsManagerScriptingInterface(); | 538 | DialogsManagerScriptingInterface* _dialogsManagerScriptingInterface = new DialogsManagerScriptingInterface(); |
| 530 | 539 | ||
| @@ -464,11 +464,13 @@ Menu::Menu() { · 463 unmodified lines | |||
| 464 | addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::MeshVisible, 0, true, | 464 | addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::MeshVisible, 0, true, |
| 465 | avatar, SLOT(setEnableMeshVisible(bool))); | 465 | avatar, SLOT(setEnableMeshVisible(bool))); |
| 466 | addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false); | 466 | addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false); |
| 467 | #if 0 | ||
| 467 | addCheckableActionToQMenuAndActionHash(avatarDebugMenu, | 468 | addCheckableActionToQMenuAndActionHash(avatarDebugMenu, |
| 468 | MenuOption::Connexion, | 469 | MenuOption::Connexion, |
| 469 | 0, false, | 470 | 0, false, |
| 470 | &ConnexionClient::getInstance(), | 471 | &ConnexionClient::getInstance(), |
| 471 | SLOT(toggleConnexion(bool))); | 472 | SLOT(toggleConnexion(bool))); |
| 473 | #endif | ||
| 472 | addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ComfortMode, 0, true); | 474 | addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ComfortMode, 0, true); |
| 473 | 475 | ||
| 474 | MenuWrapper* handOptionsMenu = developerMenu->addMenu("Hands"); | 476 | MenuWrapper* handOptionsMenu = developerMenu->addMenu("Hands"); |
| @@ -1177,22 +1177,6 @@ glm::vec3 Avatar::getLeftPalmPosition() { · 1176 unmodified lines | |||
| 1177 | return leftHandPosition; | 1177 | return leftHandPosition; |
| 1178 | } | 1178 | } |
| 1179 | 1179 | ||
| 1180 | glm::vec3 Avatar::getLeftPalmVelocity() { | ||
| 1181 | const PalmData* palm = getHand()->getPalm(LEFT_HAND_INDEX); | ||
| 1182 | if (palm != NULL) { | ||
| 1183 | return palm->getVelocity(); | ||
| 1184 | } | ||
| 1185 | return glm::vec3(0.0f); | ||
| 1186 | } | ||
| 1187 | |||
| 1188 | glm::vec3 Avatar::getLeftPalmAngularVelocity() { | ||
| 1189 | const PalmData* palm = getHand()->getPalm(LEFT_HAND_INDEX); | ||
| 1190 | if (palm != NULL) { | ||
| 1191 | return palm->getRawAngularVelocity(); | ||
| 1192 | } | ||
| 1193 | return glm::vec3(0.0f); | ||
| 1194 | } | ||
| 1195 | |||
| 1196 | glm::quat Avatar::getLeftPalmRotation() { | 1180 | glm::quat Avatar::getLeftPalmRotation() { |
| 1197 | glm::quat leftRotation; | 1181 | glm::quat leftRotation; |
| 1198 | getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getLeftHandJointIndex(), leftRotation); | 1182 | getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getLeftHandJointIndex(), leftRotation); |
| @@ -1208,22 +1192,6 @@ glm::vec3 Avatar::getRightPalmPosition() { · 9 unmodified lines | |||
| 1208 | return rightHandPosition; | 1192 | return rightHandPosition; |
| 1209 | } | 1193 | } |
| 1210 | 1194 | ||
| 1211 | glm::vec3 Avatar::getRightPalmVelocity() { | ||
| 1212 | const PalmData* palm = getHand()->getPalm(RIGHT_HAND_INDEX); | ||
| 1213 | if (palm != NULL) { | ||
| 1214 | return palm->getVelocity(); | ||
| 1215 | } | ||
| 1216 | return glm::vec3(0.0f); | ||
| 1217 | } | ||
| 1218 | |||
| 1219 | glm::vec3 Avatar::getRightPalmAngularVelocity() { | ||
| 1220 | const PalmData* palm = getHand()->getPalm(RIGHT_HAND_INDEX); | ||
| 1221 | if (palm != NULL) { | ||
| 1222 | return palm->getRawAngularVelocity(); | ||
| 1223 | } | ||
| 1224 | return glm::vec3(0.0f); | ||
| 1225 | } | ||
| 1226 | |||
| 1227 | glm::quat Avatar::getRightPalmRotation() { | 1195 | glm::quat Avatar::getRightPalmRotation() { |
| 1228 | glm::quat rightRotation; | 1196 | glm::quat rightRotation; |
| 1229 | getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightRotation); | 1197 | getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightRotation); |
| @@ -43,21 +43,6 @@ static const float BILLBOARD_DISTANCE = 5.56f; // meters · 42 unmodified lines | |||
| 43 | extern const float CHAT_MESSAGE_SCALE; | 43 | extern const float CHAT_MESSAGE_SCALE; |
| 44 | extern const float CHAT_MESSAGE_HEIGHT; | 44 | extern const float CHAT_MESSAGE_HEIGHT; |
| 45 | 45 | ||
| 46 | enum DriveKeys { | ||
| 47 | FWD = 0, | ||
| 48 | BACK, | ||
| 49 | LEFT, | ||
| 50 | RIGHT, | ||
| 51 | UP, | ||
| 52 | DOWN, | ||
| 53 | ROT_LEFT, | ||
| 54 | ROT_RIGHT, | ||
| 55 | ROT_UP, | ||
| 56 | ROT_DOWN, | ||
| 57 | BOOM_IN, | ||
| 58 | BOOM_OUT, | ||
| 59 | MAX_DRIVE_KEYS | ||
| 60 | }; | ||
| 61 | 46 | ||
| 62 | enum ScreenTintLayer { | 47 | enum ScreenTintLayer { |
| 63 | SCREEN_TINT_BEFORE_LANDSCAPE = 0, | 48 | SCREEN_TINT_BEFORE_LANDSCAPE = 0, |
| @@ -175,13 +160,11 @@ public: · 111 unmodified lines | |||
| 175 | AvatarMotionState* getMotionState() { return _motionState; } | 160 | AvatarMotionState* getMotionState() { return _motionState; } |
| 176 | 161 | ||
| 177 | public slots: | 162 | public slots: |
| 163 | |||
| 164 | // FIXME - these should be migrated to use Pose data instead | ||
| 178 | glm::vec3 getLeftPalmPosition(); | 165 | glm::vec3 getLeftPalmPosition(); |
| 179 | glm::vec3 getLeftPalmVelocity(); | ||
| 180 | glm::vec3 getLeftPalmAngularVelocity(); | ||
| 181 | glm::quat getLeftPalmRotation(); | 166 | glm::quat getLeftPalmRotation(); |
| 182 | glm::vec3 getRightPalmPosition(); | 167 | glm::vec3 getRightPalmPosition(); |
| 183 | glm::vec3 getRightPalmVelocity(); | ||
| 184 | glm::vec3 getRightPalmAngularVelocity(); | ||
| 185 | glm::quat getRightPalmRotation(); | 168 | glm::quat getRightPalmRotation(); |
| 186 | 169 | ||
| 187 | protected: | 170 | protected: |
| @@ -248,6 +248,16 @@ QVector<AvatarManager::LocalLight> AvatarManager::getLocalLights() const { · 247 unmodified lines | |||
| 248 | return _localLights; | 248 | return _localLights; |
| 249 | } | 249 | } |
| 250 | 250 | ||
| 251 | QVector<QUuid> AvatarManager::getAvatarIdentifiers() { | ||
| 252 | QReadLocker locker(&_hashLock); | ||
| 253 | return _avatarHash.keys().toVector(); | ||
| 254 | } | ||
| 255 | AvatarData* AvatarManager::getAvatar(QUuid avatarID) { | ||
| 256 | QReadLocker locker(&_hashLock); | ||
| 257 | return _avatarHash[avatarID].get(); // Non-obvious: A bogus avatarID answers your own avatar. | ||
| 258 | } | ||
| 259 | |||
| 260 | |||
| 251 | void AvatarManager::getObjectsToDelete(VectorOfMotionStates& result) { | 261 | void AvatarManager::getObjectsToDelete(VectorOfMotionStates& result) { |
| 252 | result.clear(); | 262 | result.clear(); |
| 253 | result.swap(_motionStatesToDelete); | 263 | result.swap(_motionStatesToDelete); |
| @@ -52,6 +52,10 @@ public: · 51 unmodified lines | |||
| 52 | 52 | ||
| 53 | Q_INVOKABLE void setLocalLights(const QVector<AvatarManager::LocalLight>& localLights); | 53 | Q_INVOKABLE void setLocalLights(const QVector<AvatarManager::LocalLight>& localLights); |
| 54 | Q_INVOKABLE QVector<AvatarManager::LocalLight> getLocalLights() const; | 54 | Q_INVOKABLE QVector<AvatarManager::LocalLight> getLocalLights() const; |
| 55 | // Currently, your own avatar will be included as the null avatar id. | ||
| 56 | Q_INVOKABLE QVector<QUuid> getAvatarIdentifiers(); | ||
| 57 | Q_INVOKABLE AvatarData* getAvatar(QUuid avatarID); | ||
| 58 | |||
| 55 | 59 | ||
| 56 | void getObjectsToDelete(VectorOfMotionStates& motionStates); | 60 | void getObjectsToDelete(VectorOfMotionStates& motionStates); |
| 57 | void getObjectsToAdd(VectorOfMotionStates& motionStates); | 61 | void getObjectsToAdd(VectorOfMotionStates& motionStates); |
| @@ -31,13 +31,7 @@ Hand::Hand(Avatar* owningAvatar) : · 30 unmodified lines | |||
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | void Hand::simulate(float deltaTime, bool isMine) { | 33 | void Hand::simulate(float deltaTime, bool isMine) { |
| 34 | if (isMine) { | 34 | // nothing to do here |
| 35 | // Iterate hand controllers, take actions as needed | ||
| 36 | for (size_t i = 0; i < getNumPalms(); ++i) { | ||
| 37 | PalmData& palm = getPalms()[i]; | ||
| 38 | palm.setLastControllerButtons(palm.getControllerButtons()); | ||
| 39 | } | ||
| 40 | } | ||
| 41 | } | 35 | } |
| 42 | 36 | ||
| 43 | void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) { | 37 | void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) { |
| @@ -53,10 +47,11 @@ void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) { · 9 unmodified lines | |||
| 53 | const glm::vec3 grayColor(0.5f); | 47 | const glm::vec3 grayColor(0.5f); |
| 54 | const float SPHERE_RADIUS = 0.03f * avatarScale; | 48 | const float SPHERE_RADIUS = 0.03f * avatarScale; |
| 55 | 49 | ||
| 50 | auto palms = getCopyOfPalms(); | ||
| 51 | |||
| 56 | gpu::Batch& batch = *renderArgs->_batch; | 52 | gpu::Batch& batch = *renderArgs->_batch; |
| 57 | if (isMine) { | 53 | if (isMine) { |
| 58 | for (size_t i = 0; i < getNumPalms(); i++) { | 54 | for (const auto& palm : palms) { |
| 59 | PalmData& palm = getPalms()[i]; | ||
| 60 | if (!palm.isActive()) { | 55 | if (!palm.isActive()) { |
| 61 | continue; | 56 | continue; |
| 62 | } | 57 | } |
| @@ -82,8 +77,7 @@ void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) { · 19 unmodified lines | |||
| 82 | const float AXIS_LENGTH = 10.0f * SPHERE_RADIUS; | 77 | const float AXIS_LENGTH = 10.0f * SPHERE_RADIUS; |
| 83 | 78 | ||
| 84 | // Draw the coordinate frames of the hand targets | 79 | // Draw the coordinate frames of the hand targets |
| 85 | for (size_t i = 0; i < getNumPalms(); ++i) { | 80 | for (const auto& palm : palms) { |
| 86 | PalmData& palm = getPalms()[i]; | ||
| 87 | if (palm.isActive()) { | 81 | if (palm.isActive()) { |
| 88 | glm::vec3 root = palm.getPosition(); | 82 | glm::vec3 root = palm.getPosition(); |
| 89 | 83 | ||
| @@ -53,6 +53,7 @@ · 52 unmodified lines | |||
| 53 | 53 | ||
| 54 | using namespace std; | 54 | using namespace std; |
| 55 | 55 | ||
| 56 | static quint64 COMFORT_MODE_PULSE_TIMING = USECS_PER_SECOND / 2; // turn once per half second | ||
| 56 | const glm::vec3 DEFAULT_UP_DIRECTION(0.0f, 1.0f, 0.0f); | 57 | const glm::vec3 DEFAULT_UP_DIRECTION(0.0f, 1.0f, 0.0f); |
| 57 | const float YAW_SPEED = 150.0f; // degrees/sec | 58 | const float YAW_SPEED = 150.0f; // degrees/sec |
| 58 | const float PITCH_SPEED = 100.0f; // degrees/sec | 59 | const float PITCH_SPEED = 100.0f; // degrees/sec |
| @@ -246,8 +247,31 @@ void MyAvatar::simulate(float deltaTime) { · 187 unmodified lines | |||
| 246 | 247 | ||
| 247 | { | 248 | { |
| 248 | PerformanceTimer perfTimer("transform"); | 249 | PerformanceTimer perfTimer("transform"); |
| 250 | bool stepAction = false; | ||
| 251 | // When there are no step values, we zero out the last step pulse. | ||
| 252 | // This allows a user to do faster snapping by tapping a control | ||
| 253 | for (int i = STEP_TRANSLATE_X; !stepAction && i <= STEP_YAW; ++i) { | ||
| 254 | if (_driveKeys[i] != 0.0f) { | ||
| 255 | stepAction = true; | ||
| 256 | } | ||
| 257 | } | ||
| 258 | quint64 now = usecTimestampNow(); | ||
| 259 | quint64 pulseDeltaTime = now - _lastStepPulse; | ||
| 260 | if (!stepAction) { | ||
| 261 | _lastStepPulse = 0; | ||
| 262 | } | ||
| 263 | |||
| 264 | if (stepAction && pulseDeltaTime > COMFORT_MODE_PULSE_TIMING) { | ||
| 265 | _pulseUpdate = true; | ||
| 266 | } | ||
| 267 | |||
| 249 | updateOrientation(deltaTime); | 268 | updateOrientation(deltaTime); |
| 250 | updatePosition(deltaTime); | 269 | updatePosition(deltaTime); |
| 270 | |||
| 271 | if (_pulseUpdate) { | ||
| 272 | _lastStepPulse = now; | ||
| 273 | _pulseUpdate = false; | ||
| 274 | } | ||
| 251 | } | 275 | } |
| 252 | 276 | ||
| 253 | { | 277 | { |
| @@ -531,6 +555,50 @@ void MyAvatar::updateFromTrackers(float deltaTime) { · 277 unmodified lines | |||
| 531 | } | 555 | } |
| 532 | 556 | ||
| 533 | 557 | ||
| 558 | glm::vec3 MyAvatar::getLeftHandPosition() const { | ||
| 559 | auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand); | ||
| 560 | return palmData.isValid() ? palmData.getPosition() : glm::vec3(0.0f); | ||
| 561 | } | ||
| 562 | |||
| 563 | glm::vec3 MyAvatar::getRightHandPosition() const { | ||
| 564 | auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand); | ||
| 565 | return palmData.isValid() ? palmData.getPosition() : glm::vec3(0.0f); | ||
| 566 | } | ||
| 567 | |||
| 568 | glm::vec3 MyAvatar::getLeftHandTipPosition() const { | ||
| 569 | auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand); | ||
| 570 | return palmData.isValid() ? palmData.getTipPosition() : glm::vec3(0.0f); | ||
| 571 | } | ||
| 572 | |||
| 573 | glm::vec3 MyAvatar::getRightHandTipPosition() const { | ||
| 574 | auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand); | ||
| 575 | return palmData.isValid() ? palmData.getTipPosition() : glm::vec3(0.0f); | ||
| 576 | } | ||
| 577 | |||
| 578 | controller::Pose MyAvatar::getLeftHandPose() const { | ||
| 579 | auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand); | ||
| 580 | return palmData.isValid() ? controller::Pose(palmData.getPosition(), palmData.getRotation(), | ||
| 581 | palmData.getVelocity(), palmData.getRawAngularVelocityAsQuat()) : controller::Pose(); | ||
| 582 | } | ||
| 583 | |||
| 584 | controller::Pose MyAvatar::getRightHandPose() const { | ||
| 585 | auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand); | ||
| 586 | return palmData.isValid() ? controller::Pose(palmData.getPosition(), palmData.getRotation(), | ||
| 587 | palmData.getVelocity(), palmData.getRawAngularVelocityAsQuat()) : controller::Pose(); | ||
| 588 | } | ||
| 589 | |||
| 590 | controller::Pose MyAvatar::getLeftHandTipPose() const { | ||
| 591 | auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand); | ||
| 592 | return palmData.isValid() ? controller::Pose(palmData.getTipPosition(), palmData.getRotation(), | ||
| 593 | palmData.getTipVelocity(), palmData.getRawAngularVelocityAsQuat()) : controller::Pose(); | ||
| 594 | } | ||
| 595 | |||
| 596 | controller::Pose MyAvatar::getRightHandTipPose() const { | ||
| 597 | auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand); | ||
| 598 | return palmData.isValid() ? controller::Pose(palmData.getTipPosition(), palmData.getRotation(), | ||
| 599 | palmData.getTipVelocity(), palmData.getRawAngularVelocityAsQuat()) : controller::Pose(); | ||
| 600 | } | ||
| 601 | |||
| 534 | // virtual | 602 | // virtual |
| 535 | void MyAvatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { | 603 | void MyAvatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { |
| 536 | // don't render if we've been asked to disable local rendering | 604 | // don't render if we've been asked to disable local rendering |
| @@ -1533,69 +1601,44 @@ bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const { · 996 unmodified lines | |||
| 1533 | 1601 | ||
| 1534 | void MyAvatar::updateOrientation(float deltaTime) { | 1602 | void MyAvatar::updateOrientation(float deltaTime) { |
| 1535 | // Smoothly rotate body with arrow keys | 1603 | // Smoothly rotate body with arrow keys |
| 1536 | float targetSpeed = 0.0f; | 1604 | float targetSpeed = _driveKeys[YAW] * YAW_SPEED; |
| 1537 | 1605 | if (targetSpeed != 0.0f) { | |
| 1538 | // FIXME - this comfort mode code is a total hack, remove it when we have new input mapping | 1606 | const float ROTATION_RAMP_TIMESCALE = 0.1f; |
| 1539 | bool isComfortMode = Menu::getInstance()->isOptionChecked(MenuOption::ComfortMode); | 1607 | float blend = deltaTime / ROTATION_RAMP_TIMESCALE; |
| 1540 | bool isHMDMode = qApp->getAvatarUpdater()->isHMDMode(); | 1608 | if (blend > 1.0f) { |
| 1541 | 1609 | blend = 1.0f; | |
| 1542 | if (!isHMDMode || !isComfortMode) { | ||
| 1543 | targetSpeed = (_driveKeys[ROT_LEFT] - _driveKeys[ROT_RIGHT]) * YAW_SPEED; | ||
| 1544 | |||
| 1545 | if (targetSpeed != 0.0f) { | ||
| 1546 | const float ROTATION_RAMP_TIMESCALE = 0.1f; | ||
| 1547 | float blend = deltaTime / ROTATION_RAMP_TIMESCALE; | ||
| 1548 | if (blend > 1.0f) { | ||
| 1549 | blend = 1.0f; | ||
| 1550 | } | ||
| 1551 | _bodyYawDelta = (1.0f - blend) * _bodyYawDelta + blend * targetSpeed; | ||
| 1552 | } else if (_bodyYawDelta != 0.0f) { | ||
| 1553 | // attenuate body rotation speed | ||
| 1554 | const float ROTATION_DECAY_TIMESCALE = 0.05f; | ||
| 1555 | float attenuation = 1.0f - deltaTime / ROTATION_DECAY_TIMESCALE; | ||
| 1556 | if (attenuation < 0.0f) { | ||
| 1557 | attenuation = 0.0f; | ||
| 1558 | } | ||
| 1559 | _bodyYawDelta *= attenuation; | ||
| 1560 | |||
| 1561 | float MINIMUM_ROTATION_RATE = 2.0f; | ||
| 1562 | if (fabsf(_bodyYawDelta) < MINIMUM_ROTATION_RATE) { | ||
| 1563 | _bodyYawDelta = 0.0f; | ||
| 1564 | } | ||
| 1565 | } | 1610 | } |
| 1611 | _bodyYawDelta = (1.0f - blend) * _bodyYawDelta + blend * targetSpeed; | ||
| 1612 | } else if (_bodyYawDelta != 0.0f) { | ||
| 1613 | // attenuate body rotation speed | ||
| 1614 | const float ROTATION_DECAY_TIMESCALE = 0.05f; | ||
| 1615 | float attenuation = 1.0f - deltaTime / ROTATION_DECAY_TIMESCALE; | ||
| 1616 | if (attenuation < 0.0f) { | ||
| 1617 | attenuation = 0.0f; | ||
| 1618 | } | ||
| 1619 | _bodyYawDelta *= attenuation; | ||
| 1566 | 1620 | ||
| 1567 | // update body orientation by movement inputs | 1621 | float MINIMUM_ROTATION_RATE = 2.0f; |
| 1568 | setOrientation(getOrientation() * | 1622 | if (fabsf(_bodyYawDelta) < MINIMUM_ROTATION_RATE) { |
| 1569 | glm::quat(glm::radians(glm::vec3(0.0f, _bodyYawDelta * deltaTime, 0.0f)))); | 1623 | _bodyYawDelta = 0.0f; |
| 1570 | 1624 | } | |
| 1571 | } else { | 1625 | } |
| 1572 | // Comfort Mode: If you press any of the left/right rotation drive keys or input, you'll | ||
| 1573 | // get an instantaneous 15 degree turn. If you keep holding the key down you'll get another | ||
| 1574 | // snap turn every half second. | ||
| 1575 | _bodyYawDelta = 0.0f; | ||
| 1576 | |||
| 1577 | static quint64 lastPulse = 0; | ||
| 1578 | quint64 now = usecTimestampNow(); | ||
| 1579 | quint64 COMFORT_MODE_PULSE_TIMING = USECS_PER_SECOND / 2; // turn once per half second | ||
| 1580 | |||
| 1581 | float driveLeft = _driveKeys[ROT_LEFT]; | ||
| 1582 | float driveRight= _driveKeys[ROT_RIGHT]; | ||
| 1583 | |||
| 1584 | if ((driveLeft != 0.0f || driveRight != 0.0f) && (now - lastPulse > COMFORT_MODE_PULSE_TIMING)) { | ||
| 1585 | lastPulse = now; | ||
| 1586 | 1626 | ||
| 1587 | const float SNAP_TURN_DELTA = 15.0f; // degrees | 1627 | float totalBodyYaw = _bodyYawDelta * deltaTime; |
| 1588 | float direction = (driveLeft - driveRight) < 0.0f ? -1.0f : 1.0f; | ||
| 1589 | float turnAmount = direction * SNAP_TURN_DELTA; | ||
| 1590 | 1628 | ||
| 1591 | // update body orientation by movement inputs | ||
| 1592 | setOrientation(getOrientation() * | ||
| 1593 | glm::quat(glm::radians(glm::vec3(0.0f, turnAmount, 0.0f)))); | ||
| 1594 | 1629 | ||
| 1595 | } | 1630 | // Comfort Mode: If you press any of the left/right rotation drive keys or input, you'll |
| 1631 | // get an instantaneous 15 degree turn. If you keep holding the key down you'll get another | ||
| 1632 | // snap turn every half second. | ||
| 1633 | quint64 now = usecTimestampNow(); | ||
| 1634 | if (_driveKeys[STEP_YAW] != 0.0f && now - _lastStepPulse > COMFORT_MODE_PULSE_TIMING) { | ||
| 1635 | totalBodyYaw += _driveKeys[STEP_YAW]; | ||
| 1596 | } | 1636 | } |
| 1597 | 1637 | ||
| 1598 | getHead()->setBasePitch(getHead()->getBasePitch() + (_driveKeys[ROT_UP] - _driveKeys[ROT_DOWN]) * PITCH_SPEED * deltaTime); | 1638 | // update body orientation by movement inputs |
| 1639 | setOrientation(getOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, totalBodyYaw, 0.0f)))); | ||
| 1640 | |||
| 1641 | getHead()->setBasePitch(getHead()->getBasePitch() + _driveKeys[PITCH] * PITCH_SPEED * deltaTime); | ||
| 1599 | 1642 | ||
| 1600 | if (qApp->getAvatarUpdater()->isHMDMode()) { | 1643 | if (qApp->getAvatarUpdater()->isHMDMode()) { |
| 1601 | glm::quat orientation = glm::quat_cast(getSensorToWorldMatrix()) * getHMDSensorOrientation(); | 1644 | glm::quat orientation = glm::quat_cast(getSensorToWorldMatrix()) * getHMDSensorOrientation(); |
| @@ -1655,14 +1698,18 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe · 53 unmodified lines | |||
| 1655 | float motorEfficiency = glm::clamp(deltaTime / timescale, 0.0f, 1.0f); | 1698 | float motorEfficiency = glm::clamp(deltaTime / timescale, 0.0f, 1.0f); |
| 1656 | 1699 | ||
| 1657 | glm::vec3 newLocalVelocity = localVelocity; | 1700 | glm::vec3 newLocalVelocity = localVelocity; |
| 1658 | float keyboardInput = fabsf(_driveKeys[FWD] - _driveKeys[BACK]) + | 1701 | float stepControllerInput = fabsf(_driveKeys[STEP_TRANSLATE_Z]) + fabsf(_driveKeys[STEP_TRANSLATE_Z]) + fabsf(_driveKeys[STEP_TRANSLATE_Z]); |
| 1659 | (fabsf(_driveKeys[RIGHT] - _driveKeys[LEFT])) + | 1702 | quint64 now = usecTimestampNow(); |
| 1660 | fabsf(_driveKeys[UP] - _driveKeys[DOWN]); | 1703 | // FIXME how do I implement step translation as well? |
| 1704 | if (stepControllerInput && now - _lastStepPulse > COMFORT_MODE_PULSE_TIMING) { | ||
| 1705 | } | ||
| 1706 | |||
| 1707 | float keyboardInput = fabsf(_driveKeys[TRANSLATE_Z]) + fabsf(_driveKeys[TRANSLATE_X]) + fabsf(_driveKeys[TRANSLATE_Y]); | ||
| 1661 | if (keyboardInput) { | 1708 | if (keyboardInput) { |
| 1662 | // Compute keyboard input | 1709 | // Compute keyboard input |
| 1663 | glm::vec3 front = (_driveKeys[FWD] - _driveKeys[BACK]) * IDENTITY_FRONT; | 1710 | glm::vec3 front = (_driveKeys[TRANSLATE_Z]) * IDENTITY_FRONT; |
| 1664 | glm::vec3 right = (_driveKeys[RIGHT] - _driveKeys[LEFT]) * IDENTITY_RIGHT; | 1711 | glm::vec3 right = (_driveKeys[TRANSLATE_X]) * IDENTITY_RIGHT; |
| 1665 | glm::vec3 up = (_driveKeys[UP] - _driveKeys[DOWN]) * IDENTITY_UP; | 1712 | glm::vec3 up = (_driveKeys[TRANSLATE_Y]) * IDENTITY_UP; |
| 1666 | 1713 | ||
| 1667 | glm::vec3 direction = front + right + up; | 1714 | glm::vec3 direction = front + right + up; |
| 1668 | float directionLength = glm::length(direction); | 1715 | float directionLength = glm::length(direction); |
| @@ -1713,7 +1760,7 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe · 44 unmodified lines | |||
| 1713 | } | 1760 | } |
| 1714 | } | 1761 | } |
| 1715 | 1762 | ||
| 1716 | float boomChange = _driveKeys[BOOM_OUT] - _driveKeys[BOOM_IN]; | 1763 | float boomChange = _driveKeys[ZOOM]; |
| 1717 | _boomLength += 2.0f * _boomLength * boomChange + boomChange * boomChange; | 1764 | _boomLength += 2.0f * _boomLength * boomChange + boomChange * boomChange; |
| 1718 | _boomLength = glm::clamp<float>(_boomLength, ZOOM_MIN, ZOOM_MAX); | 1765 | _boomLength = glm::clamp<float>(_boomLength, ZOOM_MIN, ZOOM_MAX); |
| 1719 | 1766 | ||
| @@ -1915,28 +1962,6 @@ void MyAvatar::updateMotionBehaviorFromMenu() { · 195 unmodified lines | |||
| 1915 | _characterController.setEnabled(menu->isOptionChecked(MenuOption::EnableCharacterController)); | 1962 | _characterController.setEnabled(menu->isOptionChecked(MenuOption::EnableCharacterController)); |
| 1916 | } | 1963 | } |
| 1917 | 1964 | ||
| 1918 | //Renders sixense laser pointers for UI selection with controllers | ||
| 1919 | void MyAvatar::renderLaserPointers(gpu::Batch& batch) { | ||
| 1920 | const float PALM_TIP_ROD_RADIUS = 0.002f; | ||
| 1921 | |||
| 1922 | //If the Oculus is enabled, we will draw a blue cursor ray | ||
| 1923 | |||
| 1924 | for (size_t i = 0; i < getHand()->getNumPalms(); ++i) { | ||
| 1925 | PalmData& palm = getHand()->getPalms()[i]; | ||
| 1926 | if (palm.isActive()) { | ||
| 1927 | glm::vec3 tip = getLaserPointerTipPosition(&palm); | ||
| 1928 | glm::vec3 root = palm.getPosition(); | ||
| 1929 | |||
| 1930 | //Scale the root vector with the avatar scale | ||
| 1931 | scaleVectorRelativeToPosition(root); | ||
| 1932 | Transform transform = Transform(); | ||
| 1933 | transform.setTranslation(glm::vec3()); | ||
| 1934 | batch.setModelTransform(transform); | ||
| 1935 | Avatar::renderJointConnectingCone(batch, root, tip, PALM_TIP_ROD_RADIUS, PALM_TIP_ROD_RADIUS, glm::vec4(0, 1, 1, 1)); | ||
| 1936 | } | ||
| 1937 | } | ||
| 1938 | } | ||
| 1939 | |||
| 1940 | //Gets the tip position for the laser pointer | 1965 | //Gets the tip position for the laser pointer |
| 1941 | glm::vec3 MyAvatar::getLaserPointerTipPosition(const PalmData* palm) { | 1966 | glm::vec3 MyAvatar::getLaserPointerTipPosition(const PalmData* palm) { |
| 1942 | glm::vec3 direction = glm::normalize(palm->getTipPosition() - palm->getPosition()); | 1967 | glm::vec3 direction = glm::normalize(palm->getTipPosition() - palm->getPosition()); |
| @@ -1962,7 +1987,7 @@ void MyAvatar::clearDriveKeys() { · 19 unmodified lines | |||
| 1962 | } | 1987 | } |
| 1963 | 1988 | ||
| 1964 | void MyAvatar::relayDriveKeysToCharacterController() { | 1989 | void MyAvatar::relayDriveKeysToCharacterController() { |
| 1965 | if (_driveKeys[UP] > 0.0f) { | 1990 | if (_driveKeys[TRANSLATE_Y] > 0.0f) { |
| 1966 | _characterController.jump(); | 1991 | _characterController.jump(); |
| 1967 | } | 1992 | } |
| 1968 | } | 1993 | } |
| @@ -17,6 +17,8 @@ · 16 unmodified lines | |||
| 17 | #include <SettingHandle.h> | 17 | #include <SettingHandle.h> |
| 18 | #include <Rig.h> | 18 | #include <Rig.h> |
| 19 | 19 | ||
| 20 | #include <controllers/Pose.h> | ||
| 21 | |||
| 20 | #include "Avatar.h" | 22 | #include "Avatar.h" |
| 21 | #include "AtRestDetector.h" | 23 | #include "AtRestDetector.h" |
| 22 | #include "MyCharacterController.h" | 24 | #include "MyCharacterController.h" |
| @@ -24,6 +26,20 @@ · 1 unmodified line | |||
| 24 | 26 | ||
| 25 | class ModelItemID; | 27 | class ModelItemID; |
| 26 | 28 | ||
| 29 | enum DriveKeys { | ||
| 30 | TRANSLATE_X = 0, | ||
| 31 | TRANSLATE_Y, | ||
| 32 | TRANSLATE_Z, | ||
| 33 | YAW, | ||
| 34 | STEP_TRANSLATE_X, | ||
| 35 | STEP_TRANSLATE_Y, | ||
| 36 | STEP_TRANSLATE_Z, | ||
| 37 | STEP_YAW, | ||
| 38 | PITCH, | ||
| 39 | ZOOM, | ||
| 40 | MAX_DRIVE_KEYS | ||
| 41 | }; | ||
| 42 | |||
| 27 | enum eyeContactTarget { | 43 | enum eyeContactTarget { |
| 28 | LEFT_EYE, | 44 | LEFT_EYE, |
| 29 | RIGHT_EYE, | 45 | RIGHT_EYE, |
| @@ -37,7 +53,6 @@ enum AudioListenerMode { · 7 unmodified lines | |||
| 37 | }; | 53 | }; |
| 38 | Q_DECLARE_METATYPE(AudioListenerMode); | 54 | Q_DECLARE_METATYPE(AudioListenerMode); |
| 39 | 55 | ||
| 40 | |||
| 41 | class MyAvatar : public Avatar { | 56 | class MyAvatar : public Avatar { |
| 42 | Q_OBJECT | 57 | Q_OBJECT |
| 43 | Q_PROPERTY(bool shouldRenderLocally READ getShouldRenderLocally WRITE setShouldRenderLocally) | 58 | Q_PROPERTY(bool shouldRenderLocally READ getShouldRenderLocally WRITE setShouldRenderLocally) |
| @@ -53,6 +68,17 @@ class MyAvatar : public Avatar { · 9 unmodified lines | |||
| 53 | Q_PROPERTY(AudioListenerMode CUSTOM READ getAudioListenerModeCustom) | 68 | Q_PROPERTY(AudioListenerMode CUSTOM READ getAudioListenerModeCustom) |
| 54 | //TODO: make gravity feature work Q_PROPERTY(glm::vec3 gravity READ getGravity WRITE setGravity) | 69 | //TODO: make gravity feature work Q_PROPERTY(glm::vec3 gravity READ getGravity WRITE setGravity) |
| 55 | 70 | ||
| 71 | |||
| 72 | Q_PROPERTY(glm::vec3 leftHandPosition READ getLeftHandPosition) | ||
| 73 | Q_PROPERTY(glm::vec3 rightHandPosition READ getRightHandPosition) | ||
| 74 | Q_PROPERTY(glm::vec3 leftHandTipPosition READ getLeftHandTipPosition) | ||
| 75 | Q_PROPERTY(glm::vec3 rightHandTipPosition READ getRightHandTipPosition) | ||
| 76 | |||
| 77 | Q_PROPERTY(controller::Pose leftHandPose READ getLeftHandPose) | ||
| 78 | Q_PROPERTY(controller::Pose rightHandPose READ getRightHandPose) | ||
| 79 | Q_PROPERTY(controller::Pose leftHandTipPose READ getLeftHandTipPose) | ||
| 80 | Q_PROPERTY(controller::Pose rightHandTipPose READ getRightHandTipPose) | ||
| 81 | |||
| 56 | public: | 82 | public: |
| 57 | MyAvatar(RigPointer rig); | 83 | MyAvatar(RigPointer rig); |
| 58 | ~MyAvatar(); | 84 | ~MyAvatar(); |
| @@ -111,6 +137,18 @@ public: · 52 unmodified lines | |||
| 111 | Q_INVOKABLE AnimationDetails getAnimationDetailsByRole(const QString& role); | 137 | Q_INVOKABLE AnimationDetails getAnimationDetailsByRole(const QString& role); |
| 112 | Q_INVOKABLE AnimationDetails getAnimationDetails(const QString& url); | 138 | Q_INVOKABLE AnimationDetails getAnimationDetails(const QString& url); |
| 113 | void clearJointAnimationPriorities(); | 139 | void clearJointAnimationPriorities(); |
| 140 | // Adds handler(animStateDictionaryIn) => animStateDictionaryOut, which will be invoked just before each animGraph state update. | ||
| 141 | // The handler will be called with an animStateDictionaryIn that has all those properties specified by the (possibly empty) | ||
| 142 | // propertiesList argument. However for debugging, if the properties argument is null, all internal animGraph state is provided. | ||
| 143 | // The animStateDictionaryOut can be a different object than animStateDictionaryIn. Any properties set in animStateDictionaryOut | ||
| 144 | // will override those of the internal animation machinery. | ||
| 145 | // The animStateDictionaryIn may be shared among multiple handlers, and thus may contain additional properties specified when | ||
| 146 | // adding one of the other handlers. While any handler may change a value in animStateDictionaryIn (or supply different values in animStateDictionaryOut) | ||
| 147 | // a handler must not remove properties from animStateDictionaryIn, nor change property values that it does not intend to change. | ||
| 148 | // It is not specified in what order multiple handlers are called. | ||
| 149 | Q_INVOKABLE QScriptValue addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { return _rig->addAnimationStateHandler(handler, propertiesList); } | ||
| 150 | // Removes a handler previously added by addAnimationStateHandler. | ||
| 151 | Q_INVOKABLE void removeAnimationStateHandler(QScriptValue handler) { _rig->removeAnimationStateHandler(handler); } | ||
| 114 | 152 | ||
| 115 | // get/set avatar data | 153 | // get/set avatar data |
| 116 | void saveData(); | 154 | void saveData(); |
| @@ -141,6 +179,16 @@ public: · 24 unmodified lines | |||
| 141 | 179 | ||
| 142 | Q_INVOKABLE glm::vec3 getTargetAvatarPosition() const { return _targetAvatarPosition; } | 180 | Q_INVOKABLE glm::vec3 getTargetAvatarPosition() const { return _targetAvatarPosition; } |
| 143 | 181 | ||
| 182 | Q_INVOKABLE glm::vec3 getLeftHandPosition() const; | ||
| 183 | Q_INVOKABLE glm::vec3 getRightHandPosition() const; | ||
| 184 | Q_INVOKABLE glm::vec3 getLeftHandTipPosition() const; | ||
| 185 | Q_INVOKABLE glm::vec3 getRightHandTipPosition() const; | ||
| 186 | |||
| 187 | Q_INVOKABLE controller::Pose getLeftHandPose() const; | ||
| 188 | Q_INVOKABLE controller::Pose getRightHandPose() const; | ||
| 189 | Q_INVOKABLE controller::Pose getLeftHandTipPose() const; | ||
| 190 | Q_INVOKABLE controller::Pose getRightHandTipPose() const; | ||
| 191 | |||
| 144 | AvatarWeakPointer getLookAtTargetAvatar() const { return _lookAtTargetAvatar; } | 192 | AvatarWeakPointer getLookAtTargetAvatar() const { return _lookAtTargetAvatar; } |
| 145 | void updateLookAtTargetAvatar(); | 193 | void updateLookAtTargetAvatar(); |
| 146 | void clearLookAtTargetAvatar(); | 194 | void clearLookAtTargetAvatar(); |
| @@ -265,7 +313,6 @@ private: · 118 unmodified lines | |||
| 265 | const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), float scale = 1.0f, | 313 | const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), float scale = 1.0f, |
| 266 | bool allowDuplicates = false, bool useSaved = true) override; | 314 | bool allowDuplicates = false, bool useSaved = true) override; |
| 267 | 315 | ||
| 268 | void renderLaserPointers(gpu::Batch& batch); | ||
| 269 | const RecorderPointer getRecorder() const { return _recorder; } | 316 | const RecorderPointer getRecorder() const { return _recorder; } |
| 270 | const PlayerPointer getPlayer() const { return _player; } | 317 | const PlayerPointer getPlayer() const { return _player; } |
| 271 | 318 | ||
| @@ -281,6 +328,8 @@ private: · 9 unmodified lines | |||
| 281 | 328 | ||
| 282 | void setVisibleInSceneIfReady(Model* model, render::ScenePointer scene, bool visiblity); | 329 | void setVisibleInSceneIfReady(Model* model, render::ScenePointer scene, bool visiblity); |
| 283 | 330 | ||
| 331 | PalmData getActivePalmData(int palmIndex) const; | ||
| 332 | |||
| 284 | // derive avatar body position and orientation from the current HMD Sensor location. | 333 | // derive avatar body position and orientation from the current HMD Sensor location. |
| 285 | // results are in sensor space | 334 | // results are in sensor space |
| 286 | glm::mat4 deriveBodyFromHMDSensor() const; | 335 | glm::mat4 deriveBodyFromHMDSensor() const; |
| @@ -370,6 +419,8 @@ private: · 83 unmodified lines | |||
| 370 | AtRestDetector _hmdAtRestDetector; | 419 | AtRestDetector _hmdAtRestDetector; |
| 371 | glm::vec3 _lastPosition; | 420 | glm::vec3 _lastPosition; |
| 372 | bool _lastIsMoving { false }; | 421 | bool _lastIsMoving { false }; |
| 422 | quint64 _lastStepPulse { 0 }; | ||
| 423 | bool _pulseUpdate { false }; | ||
| 373 | }; | 424 | }; |
| 374 | 425 | ||
| 375 | QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); | 426 | QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); |
| @@ -97,23 +97,9 @@ void SkeletonModel::initJointStates(QVector<JointState> states) { · 96 unmodified lines | |||
| 97 | emit skeletonLoaded(); | 97 | emit skeletonLoaded(); |
| 98 | } | 98 | } |
| 99 | 99 | ||
| 100 | static const PalmData* getPalmWithIndex(Hand* hand, int index) { | ||
| 101 | const PalmData* palm = nullptr; | ||
| 102 | for (size_t j = 0; j < hand->getNumPalms(); j++) { | ||
| 103 | if (hand->getPalms()[j].getSixenseID() == index) { | ||
| 104 | palm = &(hand->getPalms()[j]); | ||
| 105 | break; | ||
| 106 | } | ||
| 107 | } | ||
| 108 | return palm; | ||
| 109 | } | ||
| 110 | |||
| 111 | const float PALM_PRIORITY = DEFAULT_PRIORITY; | 100 | const float PALM_PRIORITY = DEFAULT_PRIORITY; |
| 112 | // Called within Model::simulate call, below. | 101 | // Called within Model::simulate call, below. |
| 113 | void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { | 102 | void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { |
| 114 | if (_owningAvatar->isMyAvatar()) { | ||
| 115 | _rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation()); | ||
| 116 | } | ||
| 117 | Head* head = _owningAvatar->getHead(); | 103 | Head* head = _owningAvatar->getHead(); |
| 118 | if (_owningAvatar->isMyAvatar()) { | 104 | if (_owningAvatar->isMyAvatar()) { |
| 119 | MyAvatar* myAvatar = static_cast<MyAvatar*>(_owningAvatar); | 105 | MyAvatar* myAvatar = static_cast<MyAvatar*>(_owningAvatar); |
| @@ -160,28 +146,30 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { · 40 unmodified lines | |||
| 160 | _rig->updateFromHeadParameters(headParams, deltaTime); | 146 | _rig->updateFromHeadParameters(headParams, deltaTime); |
| 161 | 147 | ||
| 162 | Rig::HandParameters handParams; | 148 | Rig::HandParameters handParams; |
| 163 | const PalmData* leftPalm = getPalmWithIndex(myAvatar->getHand(), LEFT_HAND_INDEX); | 149 | |
| 164 | if (leftPalm && leftPalm->isActive()) { | 150 | auto leftPalm = myAvatar->getHand()->getCopyOfPalmData(HandData::LeftHand); |
| 151 | if (leftPalm.isValid() && leftPalm.isActive()) { | ||
| 165 | handParams.isLeftEnabled = true; | 152 | handParams.isLeftEnabled = true; |
| 166 | handParams.leftPosition = leftPalm->getRawPosition(); | 153 | handParams.leftPosition = leftPalm.getRawPosition(); |
| 167 | handParams.leftOrientation = leftPalm->getRawRotation(); | 154 | handParams.leftOrientation = leftPalm.getRawRotation(); |
| 168 | handParams.leftTrigger = leftPalm->getTrigger(); | 155 | handParams.leftTrigger = leftPalm.getTrigger(); |
| 169 | } else { | 156 | } else { |
| 170 | handParams.isLeftEnabled = false; | 157 | handParams.isLeftEnabled = false; |
| 171 | } | 158 | } |
| 172 | 159 | ||
| 173 | const PalmData* rightPalm = getPalmWithIndex(myAvatar->getHand(), RIGHT_HAND_INDEX); | 160 | auto rightPalm = myAvatar->getHand()->getCopyOfPalmData(HandData::RightHand); |
| 174 | if (rightPalm && rightPalm->isActive()) { | 161 | if (rightPalm.isValid() && rightPalm.isActive()) { |
| 175 | handParams.isRightEnabled = true; | 162 | handParams.isRightEnabled = true; |
| 176 | handParams.rightPosition = rightPalm->getRawPosition(); | 163 | handParams.rightPosition = rightPalm.getRawPosition(); |
| 177 | handParams.rightOrientation = rightPalm->getRawRotation(); | 164 | handParams.rightOrientation = rightPalm.getRawRotation(); |
| 178 | handParams.rightTrigger = rightPalm->getTrigger(); | 165 | handParams.rightTrigger = rightPalm.getTrigger(); |
| 179 | } else { | 166 | } else { |
| 180 | handParams.isRightEnabled = false; | 167 | handParams.isRightEnabled = false; |
| 181 | } | 168 | } |
| 182 | 169 | ||
| 183 | _rig->updateFromHandParameters(handParams, deltaTime); | 170 | _rig->updateFromHandParameters(handParams, deltaTime); |
| 184 | 171 | ||
| 172 | _rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation()); | ||
| 185 | // evaluate AnimGraph animation and update jointStates. | 173 | // evaluate AnimGraph animation and update jointStates. |
| 186 | Model::updateRig(deltaTime, parentTransform); | 174 | Model::updateRig(deltaTime, parentTransform); |
| 187 | 175 | ||
| @@ -196,7 +184,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { · 8 unmodified lines | |||
| 196 | 184 | ||
| 197 | _rig->updateFromEyeParameters(eyeParams); | 185 | _rig->updateFromEyeParameters(eyeParams); |
| 198 | 186 | ||
| 199 | // rebuild the jointState transform for the eyes only | 187 | // rebuild the jointState transform for the eyes only. Must be after updateRig. |
| 200 | _rig->updateJointState(eyeParams.leftEyeJointIndex, parentTransform); | 188 | _rig->updateJointState(eyeParams.leftEyeJointIndex, parentTransform); |
| 201 | _rig->updateJointState(eyeParams.rightEyeJointIndex, parentTransform); | 189 | _rig->updateJointState(eyeParams.rightEyeJointIndex, parentTransform); |
| 202 | 190 | ||
| @@ -266,15 +254,15 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { · 63 unmodified lines | |||
| 266 | 254 | ||
| 267 | const FBXGeometry& geometry = _geometry->getFBXGeometry(); | 255 | const FBXGeometry& geometry = _geometry->getFBXGeometry(); |
| 268 | 256 | ||
| 269 | // find the left and rightmost active palms | ||
| 270 | int leftPalmIndex, rightPalmIndex; | ||
| 271 | Hand* hand = _owningAvatar->getHand(); | ||
| 272 | hand->getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex); | ||
| 273 | |||
| 274 | // Don't Relax toward hand positions when in animGraph mode. | 257 | // Don't Relax toward hand positions when in animGraph mode. |
| 275 | if (!_rig->getEnableAnimGraph()) { | 258 | if (!_rig->getEnableAnimGraph()) { |
| 259 | |||
| 260 | Hand* hand = _owningAvatar->getHand(); | ||
| 261 | auto leftPalm = hand->getCopyOfPalmData(HandData::LeftHand); | ||
| 262 | auto rightPalm = hand->getCopyOfPalmData(HandData::RightHand); | ||
| 263 | |||
| 276 | const float HAND_RESTORATION_RATE = 0.25f; | 264 | const float HAND_RESTORATION_RATE = 0.25f; |
| 277 | if (leftPalmIndex == -1 && rightPalmIndex == -1) { | 265 | if (!leftPalm.isActive() && !rightPalm.isActive()) { |
| 278 | // palms are not yet set, use mouse | 266 | // palms are not yet set, use mouse |
| 279 | if (_owningAvatar->getHandState() == HAND_STATE_NULL) { | 267 | if (_owningAvatar->getHandState() == HAND_STATE_NULL) { |
| 280 | restoreRightHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY); | 268 | restoreRightHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY); |
| @@ -284,20 +272,14 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { · 3 unmodified lines | |||
| 284 | applyHandPosition(geometry.rightHandJointIndex, handPosition); | 272 | applyHandPosition(geometry.rightHandJointIndex, handPosition); |
| 285 | } | 273 | } |
| 286 | restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY); | 274 | restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY); |
| 287 | |||
| 288 | } else if (leftPalmIndex == rightPalmIndex) { | ||
| 289 | // right hand only | ||
| 290 | applyPalmData(geometry.rightHandJointIndex, hand->getPalms()[leftPalmIndex]); | ||
| 291 | restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY); | ||
| 292 | |||
| 293 | } else { | 275 | } else { |
| 294 | if (leftPalmIndex != -1) { | 276 | if (leftPalm.isActive()) { |
| 295 | applyPalmData(geometry.leftHandJointIndex, hand->getPalms()[leftPalmIndex]); | 277 | applyPalmData(geometry.leftHandJointIndex, leftPalm); |
| 296 | } else { | 278 | } else { |
| 297 | restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY); | 279 | restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY); |
| 298 | } | 280 | } |
| 299 | if (rightPalmIndex != -1) { | 281 | if (rightPalm.isActive()) { |
| 300 | applyPalmData(geometry.rightHandJointIndex, hand->getPalms()[rightPalmIndex]); | 282 | applyPalmData(geometry.rightHandJointIndex, rightPalm); |
| 301 | } else { | 283 | } else { |
| 302 | restoreRightHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY); | 284 | restoreRightHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY); |
| 303 | } | 285 | } |
| @@ -348,7 +330,7 @@ void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position) · 44 unmodified lines | |||
| 348 | PALM_PRIORITY); | 330 | PALM_PRIORITY); |
| 349 | } | 331 | } |
| 350 | 332 | ||
| 351 | void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) { | 333 | void SkeletonModel::applyPalmData(int jointIndex, const PalmData& palm) { |
| 352 | if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) { | 334 | if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) { |
| 353 | return; | 335 | return; |
| 354 | } | 336 | } |
| @@ -118,7 +118,7 @@ protected: · 117 unmodified lines | |||
| 118 | /// \param position position of joint in model-frame | 118 | /// \param position position of joint in model-frame |
| 119 | void applyHandPosition(int jointIndex, const glm::vec3& position); | 119 | void applyHandPosition(int jointIndex, const glm::vec3& position); |
| 120 | 120 | ||
| 121 | void applyPalmData(int jointIndex, PalmData& palm); | 121 | void applyPalmData(int jointIndex, const PalmData& palm); |
| 122 | private: | 122 | private: |
| 123 | 123 | ||
| 124 | void renderJointConstraints(gpu::Batch& batch, int jointIndex); | 124 | void renderJointConstraints(gpu::Batch& batch, int jointIndex); |
| @@ -9,9 +9,14 @@ · 8 unmodified lines | |||
| 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html |
| 10 | // | 10 | // |
| 11 | 11 | ||
| 12 | |||
| 12 | #include "3DConnexionClient.h" | 13 | #include "3DConnexionClient.h" |
| 14 | |||
| 15 | #if 0 | ||
| 16 | #include <UserActivityLogger.h> | ||
| 17 | #include <PathUtils.h> | ||
| 18 | |||
| 13 | #include "Menu.h" | 19 | #include "Menu.h" |
| 14 | #include "UserActivityLogger.h" | ||
| 15 | 20 | ||
| 16 | const float MAX_AXIS = 75.0f; // max forward = 2x speed | 21 | const float MAX_AXIS = 75.0f; // max forward = 2x speed |
| 17 | 22 | ||
| @@ -25,22 +30,19 @@ ConnexionData& ConnexionData::getInstance() { · 7 unmodified lines | |||
| 25 | return sharedInstance; | 30 | return sharedInstance; |
| 26 | } | 31 | } |
| 27 | 32 | ||
| 28 | ConnexionData::ConnexionData() { | 33 | |
| 29 | } | 34 | ConnexionData::ConnexionData() : InputDevice("ConnexionClient") {} |
| 35 | |||
| 30 | 36 | ||
| 31 | void ConnexionData::handleAxisEvent() { | 37 | void ConnexionData::handleAxisEvent() { |
| 32 | _axisStateMap[makeInput(ROTATION_AXIS_Y_POS).getChannel()] = (cc_rotation.y > 0.0f) ? cc_rotation.y / MAX_AXIS : 0.0f; | 38 | auto rotation = cc_rotation / MAX_AXIS; |
| 33 | _axisStateMap[makeInput(ROTATION_AXIS_Y_NEG).getChannel()] = (cc_rotation.y < 0.0f) ? -cc_rotation.y / MAX_AXIS : 0.0f; | 39 | _axisStateMap[ROTATE_X] = rotation.x; |
| 34 | _axisStateMap[makeInput(POSITION_AXIS_X_POS).getChannel()] = (cc_position.x > 0.0f) ? cc_position.x / MAX_AXIS : 0.0f; | 40 | _axisStateMap[ROTATE_Y] = rotation.y; |
| 35 | _axisStateMap[makeInput(POSITION_AXIS_X_NEG).getChannel()] = (cc_position.x < 0.0f) ? -cc_position.x / MAX_AXIS : 0.0f; | 41 | _axisStateMap[ROTATE_Z] = rotation.z; |
| 36 | _axisStateMap[makeInput(POSITION_AXIS_Y_POS).getChannel()] = (cc_position.y > 0.0f) ? cc_position.y / MAX_AXIS : 0.0f; | 42 | auto position = cc_rotation / MAX_AXIS; |
| 37 | _axisStateMap[makeInput(POSITION_AXIS_Y_NEG).getChannel()] = (cc_position.y < 0.0f) ? -cc_position.y / MAX_AXIS : 0.0f; | 43 | _axisStateMap[TRANSLATE_X] = position.x; |
| 38 | _axisStateMap[makeInput(POSITION_AXIS_Z_POS).getChannel()] = (cc_position.z > 0.0f) ? cc_position.z / MAX_AXIS : 0.0f; | 44 | _axisStateMap[TRANSLATE_Y] = position.y; |
| 39 | _axisStateMap[makeInput(POSITION_AXIS_Z_NEG).getChannel()] = (cc_position.z < 0.0f) ? -cc_position.z / MAX_AXIS : 0.0f; | 45 | _axisStateMap[TRANSLATE_Z] = position.z; |
| 40 | _axisStateMap[makeInput(ROTATION_AXIS_X_POS).getChannel()] = (cc_rotation.x > 0.0f) ? cc_rotation.x / MAX_AXIS : 0.0f; | ||
| 41 | _axisStateMap[makeInput(ROTATION_AXIS_X_NEG).getChannel()] = (cc_rotation.x < 0.0f) ? -cc_rotation.x / MAX_AXIS : 0.0f; | ||
| 42 | _axisStateMap[makeInput(ROTATION_AXIS_Z_POS).getChannel()] = (cc_rotation.z > 0.0f) ? cc_rotation.z / MAX_AXIS : 0.0f; | ||
| 43 | _axisStateMap[makeInput(ROTATION_AXIS_Z_NEG).getChannel()] = (cc_rotation.z < 0.0f) ? -cc_rotation.z / MAX_AXIS : 0.0f; | ||
| 44 | } | 46 | } |
| 45 | 47 | ||
| 46 | void ConnexionData::setButton(int lastButtonState) { | 48 | void ConnexionData::setButton(int lastButtonState) { |
| @@ -48,76 +50,65 @@ void ConnexionData::setButton(int lastButtonState) { · 1 unmodified line | |||
| 48 | _buttonPressedMap.insert(lastButtonState); | 50 | _buttonPressedMap.insert(lastButtonState); |
| 49 | } | 51 | } |
| 50 | 52 | ||
| 51 | void ConnexionData::registerToUserInputMapper(UserInputMapper& mapper) { | 53 | void ConnexionData::buildDeviceProxy(controller::DeviceProxy::Pointer proxy) { |
| 52 | // Grab the current free device ID | 54 | proxy->_name = _name = "ConnexionClient"; |
| 53 | _deviceID = mapper.getFreeDeviceID(); | 55 | proxy->getButton = [this](const controller::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; |
| 54 | 56 | proxy->getAxis = [this](const controller::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; | |
| 55 | auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy("ConnexionClient")); | 57 | proxy->getAvailabeInputs = [this]() -> QVector<controller::Input::NamedPair> { |
| 56 | proxy->getButton = [this](const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; | 58 | using namespace controller; |
| 57 | proxy->getAxis = [this](const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; | 59 | static QVector<controller::Input::NamedPair> availableInputs { |
| 58 | proxy->getAvailabeInputs = [this]() -> QVector<UserInputMapper::InputPair> { | 60 | Input::NamedPair(makeInput(BUTTON_1), "LeftButton"), |
| 59 | QVector<UserInputMapper::InputPair> availableInputs; | 61 | Input::NamedPair(makeInput(BUTTON_2), "RightButton"), |
| 60 | 62 | Input::NamedPair(makeInput(BUTTON_3), "BothButtons"), | |
| 61 | availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_1), "Left button")); | 63 | Input::NamedPair(makeInput(TRANSLATE_X), "TranslateX"), |
| 62 | availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_2), "Right button")); | 64 | Input::NamedPair(makeInput(TRANSLATE_Y), "TranslateY"), |
| 63 | availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_3), "Both buttons")); | 65 | Input::NamedPair(makeInput(TRANSLATE_Z), "TranslateZ"), |
| 64 | 66 | Input::NamedPair(makeInput(ROTATE_X), "RotateX"), | |
| 65 | availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_Y_NEG), "Move backward")); | 67 | Input::NamedPair(makeInput(ROTATE_Y), "RotateY"), |
| 66 | availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_Y_POS), "Move forward")); | 68 | Input::NamedPair(makeInput(ROTATE_Z), "RotateZ"), |
| 67 | availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_X_POS), "Move right")); | 69 | }; |
| 68 | availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_X_NEG), "Move Left")); | ||
| 69 | availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_Z_POS), "Move up")); | ||
| 70 | availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_Z_NEG), "Move down")); | ||
| 71 | availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_Y_NEG), "Rotate backward")); | ||
| 72 | availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_Y_POS), "Rotate forward")); | ||
| 73 | availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_X_POS), "Rotate right")); | ||
| 74 | availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_X_NEG), "Rotate left")); | ||
| 75 | availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_Z_POS), "Rotate up")); | ||
| 76 | availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_Z_NEG), "Rotate down")); | ||
| 77 | |||
| 78 | return availableInputs; | 70 | return availableInputs; |
| 79 | }; | 71 | }; |
| 80 | proxy->resetDeviceBindings = [this, &mapper]() -> bool { | ||
| 81 | mapper.removeAllInputChannelsForDevice(_deviceID); | ||
| 82 | this->assignDefaultInputMapping(mapper); | ||
| 83 | return true; | ||
| 84 | }; | ||
| 85 | mapper.registerDevice(_deviceID, proxy); | ||
| 86 | } | 72 | } |
| 87 | 73 | ||
| 88 | void ConnexionData::assignDefaultInputMapping(UserInputMapper& mapper) { | 74 | QString ConnexionData::getDefaultMappingConfig() { |
| 89 | const float JOYSTICK_MOVE_SPEED = 1.0f; | 75 | static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/vive.json"; |
| 90 | //const float DPAD_MOVE_SPEED = 0.5f; | 76 | return MAPPING_JSON; |
| 91 | const float JOYSTICK_YAW_SPEED = 0.5f; | ||
| 92 | const float JOYSTICK_PITCH_SPEED = 0.25f; | ||
| 93 | const float BOOM_SPEED = 0.1f; | ||
| 94 | |||
| 95 | // Y axes are flipped (up is negative) | ||
| 96 | // postion: Movement, strafing | ||
| 97 | mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(POSITION_AXIS_Y_NEG), JOYSTICK_MOVE_SPEED); | ||
| 98 | mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(POSITION_AXIS_Y_POS), JOYSTICK_MOVE_SPEED); | ||
| 99 | mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(POSITION_AXIS_X_POS), JOYSTICK_MOVE_SPEED); | ||
| 100 | mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(POSITION_AXIS_X_NEG), JOYSTICK_MOVE_SPEED); | ||
| 101 | mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(POSITION_AXIS_Z_NEG), JOYSTICK_MOVE_SPEED); | ||
| 102 | mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(POSITION_AXIS_Z_POS), JOYSTICK_MOVE_SPEED); | ||
| 103 | |||
| 104 | // Rotation: Camera orientation with button 1 | ||
| 105 | mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(ROTATION_AXIS_Z_POS), JOYSTICK_YAW_SPEED); | ||
| 106 | mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(ROTATION_AXIS_Z_NEG), JOYSTICK_YAW_SPEED); | ||
| 107 | mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(ROTATION_AXIS_Y_NEG), JOYSTICK_PITCH_SPEED); | ||
| 108 | mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(ROTATION_AXIS_Y_POS), JOYSTICK_PITCH_SPEED); | ||
| 109 | |||
| 110 | // Button controls | ||
| 111 | // Zoom | ||
| 112 | mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(BUTTON_1), BOOM_SPEED); | ||
| 113 | mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(BUTTON_2), BOOM_SPEED); | ||
| 114 | |||
| 115 | // Zoom | ||
| 116 | // mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(ROTATION_AXIS_Z_NEG), BOOM_SPEED); | ||
| 117 | // mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(ROTATION_AXIS_Z_POS), BOOM_SPEED); | ||
| 118 | |||
| 119 | } | 77 | } |
| 120 | 78 | ||
| 79 | //void ConnexionData::assignDefaultInputMapping(UserInputMapper& mapper) { | ||
| 80 | // const float JOYSTICK_MOVE_SPEED = 1.0f; | ||
| 81 | // //const float DPAD_MOVE_SPEED = 0.5f; | ||
| 82 | // const float JOYSTICK_YAW_SPEED = 0.5f; | ||
| 83 | // const float JOYSTICK_PITCH_SPEED = 0.25f; | ||
| 84 | // const float BOOM_SPEED = 0.1f; | ||
| 85 | // | ||
| 86 | // // Y axes are flipped (up is negative) | ||
| 87 | // // postion: Movement, strafing | ||
| 88 | // mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(POSITION_AXIS_Y_NEG), JOYSTICK_MOVE_SPEED); | ||
| 89 | // mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(POSITION_AXIS_Y_POS), JOYSTICK_MOVE_SPEED); | ||
| 90 | // mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(POSITION_AXIS_X_POS), JOYSTICK_MOVE_SPEED); | ||
| 91 | // mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(POSITION_AXIS_X_NEG), JOYSTICK_MOVE_SPEED); | ||
| 92 | // mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(POSITION_AXIS_Z_NEG), JOYSTICK_MOVE_SPEED); | ||
| 93 | // mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(POSITION_AXIS_Z_POS), JOYSTICK_MOVE_SPEED); | ||
| 94 | // | ||
| 95 | // // Rotation: Camera orientation with button 1 | ||
| 96 | // mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(ROTATION_AXIS_Z_POS), JOYSTICK_YAW_SPEED); | ||
| 97 | // mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(ROTATION_AXIS_Z_NEG), JOYSTICK_YAW_SPEED); | ||
| 98 | // mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(ROTATION_AXIS_Y_NEG), JOYSTICK_PITCH_SPEED); | ||
| 99 | // mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(ROTATION_AXIS_Y_POS), JOYSTICK_PITCH_SPEED); | ||
| 100 | // | ||
| 101 | // // Button controls | ||
| 102 | // // Zoom | ||
| 103 | // mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(BUTTON_1), BOOM_SPEED); | ||
| 104 | // mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(BUTTON_2), BOOM_SPEED); | ||
| 105 | // | ||
| 106 | // // Zoom | ||
| 107 | // // mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(ROTATION_AXIS_Z_NEG), BOOM_SPEED); | ||
| 108 | // // mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(ROTATION_AXIS_Z_POS), BOOM_SPEED); | ||
| 109 | // | ||
| 110 | //} | ||
| 111 | |||
| 121 | float ConnexionData::getButton(int channel) const { | 112 | float ConnexionData::getButton(int channel) const { |
| 122 | if (!_buttonPressedMap.empty()) { | 113 | if (!_buttonPressedMap.empty()) { |
| 123 | if (_buttonPressedMap.find(channel) != _buttonPressedMap.end()) { | 114 | if (_buttonPressedMap.find(channel) != _buttonPressedMap.end()) { |
| @@ -138,15 +129,15 @@ float ConnexionData::getAxis(int channel) const { · 14 unmodified lines | |||
| 138 | } | 129 | } |
| 139 | } | 130 | } |
| 140 | 131 | ||
| 141 | UserInputMapper::Input ConnexionData::makeInput(ConnexionData::ButtonChannel button) { | 132 | controller::Input ConnexionData::makeInput(ConnexionData::ButtonChannel button) { |
| 142 | return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON); | 133 | return controller::Input(_deviceID, button, controller::ChannelType::BUTTON); |
| 143 | } | 134 | } |
| 144 | 135 | ||
| 145 | UserInputMapper::Input ConnexionData::makeInput(ConnexionData::PositionChannel axis) { | 136 | controller::Input ConnexionData::makeInput(ConnexionData::PositionChannel axis) { |
| 146 | return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS); | 137 | return controller::Input(_deviceID, axis, controller::ChannelType::AXIS); |
| 147 | } | 138 | } |
| 148 | 139 | ||
| 149 | void ConnexionData::update() { | 140 | void ConnexionData::update(float deltaTime, bool jointsCaptured) { |
| 150 | // the update is done in the ConnexionClient class. | 141 | // the update is done in the ConnexionClient class. |
| 151 | // for windows in the nativeEventFilter the inputmapper is connected or registed or removed when an 3Dconnnexion device is attached or detached | 142 | // for windows in the nativeEventFilter the inputmapper is connected or registed or removed when an 3Dconnnexion device is attached or detached |
| 152 | // for osx the api will call DeviceAddedHandler or DeviceRemoveHandler when a 3Dconnexion device is attached or detached | 143 | // for osx the api will call DeviceAddedHandler or DeviceRemoveHandler when a 3Dconnexion device is attached or detached |
| @@ -187,7 +178,6 @@ void ConnexionClient::destroy() { · 34 unmodified lines | |||
| 187 | QAbstractEventDispatcher::instance()->removeNativeEventFilter(this); | 178 | QAbstractEventDispatcher::instance()->removeNativeEventFilter(this); |
| 188 | ConnexionData& connexiondata = ConnexionData::getInstance(); | 179 | ConnexionData& connexiondata = ConnexionData::getInstance(); |
| 189 | int deviceid = connexiondata.getDeviceID(); | 180 | int deviceid = connexiondata.getDeviceID(); |
| 190 | connexiondata.setDeviceID(0); | ||
| 191 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); | 181 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); |
| 192 | userInputMapper->removeDevice(deviceid); | 182 | userInputMapper->removeDevice(deviceid); |
| 193 | } | 183 | } |
| @@ -295,13 +285,10 @@ bool ConnexionClient::RawInputEventFilter(void* msg, long* result) { · 101 unmodified lines | |||
| 295 | ConnexionData& connexiondata = ConnexionData::getInstance(); | 285 | ConnexionData& connexiondata = ConnexionData::getInstance(); |
| 296 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); | 286 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); |
| 297 | if (Is3dmouseAttached() && connexiondata.getDeviceID() == 0) { | 287 | if (Is3dmouseAttached() && connexiondata.getDeviceID() == 0) { |
| 298 | connexiondata.registerToUserInputMapper(*userInputMapper); | 288 | userInputMapper->registerDevice(&connexiondata); |
| 299 | connexiondata.assignDefaultInputMapping(*userInputMapper); | ||
| 300 | UserActivityLogger::getInstance().connectedDevice("controller", "3Dconnexion"); | 289 | UserActivityLogger::getInstance().connectedDevice("controller", "3Dconnexion"); |
| 301 | } else if (!Is3dmouseAttached() && connexiondata.getDeviceID() != 0) { | 290 | } else if (!Is3dmouseAttached() && connexiondata.getDeviceID() != 0) { |
| 302 | int deviceid = connexiondata.getDeviceID(); | 291 | userInputMapper->removeDevice(connexiondata.getDeviceID()); |
| 303 | connexiondata.setDeviceID(0); | ||
| 304 | userInputMapper->removeDevice(deviceid); | ||
| 305 | } | 292 | } |
| 306 | 293 | ||
| 307 | if (!Is3dmouseAttached()) { | 294 | if (!Is3dmouseAttached()) { |
| @@ -894,8 +881,7 @@ void ConnexionClient::init() { · 586 unmodified lines | |||
| 894 | 881 | ||
| 895 | if (Is3dmouseAttached() && connexiondata.getDeviceID() == 0) { | 882 | if (Is3dmouseAttached() && connexiondata.getDeviceID() == 0) { |
| 896 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); | 883 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); |
| 897 | connexiondata.registerToUserInputMapper(*userInputMapper); | 884 | userInputMapper->registerDevice(&connexiondata); |
| 898 | connexiondata.assignDefaultInputMapping(*userInputMapper); | ||
| 899 | UserActivityLogger::getInstance().connectedDevice("controller", "3Dconnexion"); | 885 | UserActivityLogger::getInstance().connectedDevice("controller", "3Dconnexion"); |
| 900 | } | 886 | } |
| 901 | //let one axis be dominant | 887 | //let one axis be dominant |
| @@ -926,8 +912,7 @@ void DeviceAddedHandler(unsigned int connection) { · 24 unmodified lines | |||
| 926 | if (connexiondata.getDeviceID() == 0) { | 912 | if (connexiondata.getDeviceID() == 0) { |
| 927 | qCWarning(interfaceapp) << "3Dconnexion device added "; | 913 | qCWarning(interfaceapp) << "3Dconnexion device added "; |
| 928 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); | 914 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); |
| 929 | connexiondata.registerToUserInputMapper(*userInputMapper); | 915 | userInputMapper->registerDevice(&connexiondata); |
| 930 | connexiondata.assignDefaultInputMapping(*userInputMapper); | ||
| 931 | UserActivityLogger::getInstance().connectedDevice("controller", "3Dconnexion"); | 916 | UserActivityLogger::getInstance().connectedDevice("controller", "3Dconnexion"); |
| 932 | } | 917 | } |
| 933 | } | 918 | } |
| @@ -984,3 +969,4 @@ void MessageHandler(unsigned int connection, unsigned int messageType, void *mes · 50 unmodified lines | |||
| 984 | #endif // __APPLE__ | 969 | #endif // __APPLE__ |
| 985 | 970 | ||
| 986 | #endif // HAVE_3DCONNEXIONCLIENT | 971 | #endif // HAVE_3DCONNEXIONCLIENT |
| 972 | #endif | ||
| @@ -11,9 +11,10 @@ · 10 unmodified lines | |||
| 11 | #ifndef hifi_3DConnexionClient_h | 11 | #ifndef hifi_3DConnexionClient_h |
| 12 | #define hifi_3DConnexionClient_h | 12 | #define hifi_3DConnexionClient_h |
| 13 | 13 | ||
| 14 | #if 0 | ||
| 14 | #include <QObject> | 15 | #include <QObject> |
| 15 | #include <QLibrary> | 16 | #include <QLibrary> |
| 16 | #include <input-plugins/UserInputMapper.h> | 17 | #include <controllers/UserInputMapper.h> |
| 17 | 18 | ||
| 18 | #include "InterfaceLogging.h" | 19 | #include "InterfaceLogging.h" |
| 19 | 20 | ||
| @@ -175,26 +176,19 @@ public slots: · 155 unmodified lines | |||
| 175 | 176 | ||
| 176 | 177 | ||
| 177 | // connnects to the userinputmapper | 178 | // connnects to the userinputmapper |
| 178 | class ConnexionData : public QObject { | 179 | class ConnexionData : public QObject, public controller::InputDevice { |
| 179 | Q_OBJECT | 180 | Q_OBJECT |
| 180 | 181 | ||
| 181 | public: | 182 | public: |
| 182 | static ConnexionData& getInstance(); | 183 | static ConnexionData& getInstance(); |
| 183 | ConnexionData(); | 184 | ConnexionData(); |
| 184 | |||
| 185 | enum PositionChannel { | 185 | enum PositionChannel { |
| 186 | POSITION_AXIS_X_POS = 1, | 186 | TRANSLATE_X, |
| 187 | POSITION_AXIS_X_NEG = 2, | 187 | TRANSLATE_Y, |
| 188 | POSITION_AXIS_Y_POS = 3, | 188 | TRANSLATE_Z, |
| 189 | POSITION_AXIS_Y_NEG = 4, | 189 | ROTATE_X, |
| 190 | POSITION_AXIS_Z_POS = 5, | 190 | ROTATE_Y, |
| 191 | POSITION_AXIS_Z_NEG = 6, | 191 | ROTATE_Z, |
| 192 | ROTATION_AXIS_X_POS = 7, | ||
| 193 | ROTATION_AXIS_X_NEG = 8, | ||
| 194 | ROTATION_AXIS_Y_POS = 9, | ||
| 195 | ROTATION_AXIS_Y_NEG = 10, | ||
| 196 | ROTATION_AXIS_Z_POS = 11, | ||
| 197 | ROTATION_AXIS_Z_NEG = 12 | ||
| 198 | }; | 192 | }; |
| 199 | 193 | ||
| 200 | enum ButtonChannel { | 194 | enum ButtonChannel { |
| @@ -209,19 +203,12 @@ public: · 8 unmodified lines | |||
| 209 | float getButton(int channel) const; | 203 | float getButton(int channel) const; |
| 210 | float getAxis(int channel) const; | 204 | float getAxis(int channel) const; |
| 211 | 205 | ||
| 212 | UserInputMapper::Input makeInput(ConnexionData::PositionChannel axis); | 206 | controller::Input makeInput(ConnexionData::PositionChannel axis); |
| 213 | UserInputMapper::Input makeInput(ConnexionData::ButtonChannel button); | 207 | controller::Input makeInput(ConnexionData::ButtonChannel button); |
| 214 | 208 | virtual void buildDeviceProxy(controller::DeviceProxy::Pointer proxy) override; | |
| 215 | void registerToUserInputMapper(UserInputMapper& mapper); | 209 | virtual QString getDefaultMappingConfig() override; |
| 216 | void assignDefaultInputMapping(UserInputMapper& mapper); | 210 | virtual void update(float deltaTime, bool jointsCaptured) override; |
| 217 | 211 | virtual void focusOutEvent() override; | |
| 218 | void update(); | ||
| 219 | void focusOutEvent(); | ||
| 220 | |||
| 221 | int getDeviceID() { return _deviceID; } | ||
| 222 | void setDeviceID(int deviceID) { _deviceID = deviceID; } | ||
| 223 | |||
| 224 | QString _name; | ||
| 225 | 212 | ||
| 226 | glm::vec3 cc_position; | 213 | glm::vec3 cc_position; |
| 227 | glm::vec3 cc_rotation; | 214 | glm::vec3 cc_rotation; |
| @@ -229,12 +216,8 @@ public: · 1 unmodified line | |||
| 229 | 216 | ||
| 230 | void setButton(int lastButtonState); | 217 | void setButton(int lastButtonState); |
| 231 | void handleAxisEvent(); | 218 | void handleAxisEvent(); |
| 232 | |||
| 233 | protected: | ||
| 234 | int _deviceID = 0; | ||
| 235 | |||
| 236 | ButtonPressedMap _buttonPressedMap; | ||
| 237 | AxisStateMap _axisStateMap; | ||
| 238 | }; | 219 | }; |
| 239 | 220 | ||
| 221 | #endif | ||
| 222 | |||
| 240 | #endif // defined(hifi_3DConnexionClient_h) | 223 | #endif // defined(hifi_3DConnexionClient_h) |
| @@ -9,120 +9,20 @@ · 8 unmodified lines | |||
| 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html |
| 10 | // | 10 | // |
| 11 | 11 | ||
| 12 | #include "ControllerScriptingInterface.h" | ||
| 13 | |||
| 12 | #include <avatar/AvatarManager.h> | 14 | #include <avatar/AvatarManager.h> |
| 13 | #include <avatar/MyAvatar.h> | 15 | #include <avatar/MyAvatar.h> |
| 14 | #include <HandData.h> | 16 | #include <HandData.h> |
| 15 | #include <HFBackEvent.h> | 17 | #include <HFBackEvent.h> |
| 18 | #include <plugins/PluginManager.h> | ||
| 16 | 19 | ||
| 17 | #include "Application.h" | 20 | #include "Application.h" |
| 18 | #include "devices/MotionTracker.h" | 21 | #include "devices/MotionTracker.h" |
| 19 | #include "ControllerScriptingInterface.h" | ||
| 20 | 22 | ||
| 21 | // TODO: this needs to be removed, as well as any related controller-specific information | 23 | // TODO: this needs to be removed, as well as any related controller-specific information |
| 22 | #include <input-plugins/SixenseManager.h> | 24 | #include <input-plugins/SixenseManager.h> |
| 23 | 25 | ||
| 24 | |||
| 25 | ControllerScriptingInterface::ControllerScriptingInterface() : | ||
| 26 | _mouseCaptured(false), | ||
| 27 | _touchCaptured(false), | ||
| 28 | _wheelCaptured(false), | ||
| 29 | _actionsCaptured(false) | ||
| 30 | { | ||
| 31 | |||
| 32 | } | ||
| 33 | |||
| 34 | static int actionMetaTypeId = qRegisterMetaType<UserInputMapper::Action>(); | ||
| 35 | static int inputChannelMetaTypeId = qRegisterMetaType<UserInputMapper::InputChannel>(); | ||
| 36 | static int inputMetaTypeId = qRegisterMetaType<UserInputMapper::Input>(); | ||
| 37 | static int inputPairMetaTypeId = qRegisterMetaType<UserInputMapper::InputPair>(); | ||
| 38 | |||
| 39 | QScriptValue inputToScriptValue(QScriptEngine* engine, const UserInputMapper::Input& input); | ||
| 40 | void inputFromScriptValue(const QScriptValue& object, UserInputMapper::Input& input); | ||
| 41 | QScriptValue inputChannelToScriptValue(QScriptEngine* engine, const UserInputMapper::InputChannel& inputChannel); | ||
| 42 | void inputChannelFromScriptValue(const QScriptValue& object, UserInputMapper::InputChannel& inputChannel); | ||
| 43 | QScriptValue actionToScriptValue(QScriptEngine* engine, const UserInputMapper::Action& action); | ||
| 44 | void actionFromScriptValue(const QScriptValue& object, UserInputMapper::Action& action); | ||
| 45 | QScriptValue inputPairToScriptValue(QScriptEngine* engine, const UserInputMapper::InputPair& inputPair); | ||
| 46 | void inputPairFromScriptValue(const QScriptValue& object, UserInputMapper::InputPair& inputPair); | ||
| 47 | |||
| 48 | QScriptValue inputToScriptValue(QScriptEngine* engine, const UserInputMapper::Input& input) { | ||
| 49 | QScriptValue obj = engine->newObject(); | ||
| 50 | obj.setProperty("device", input.getDevice()); | ||
| 51 | obj.setProperty("channel", input.getChannel()); | ||
| 52 | obj.setProperty("type", (unsigned short) input.getType()); | ||
| 53 | obj.setProperty("id", input.getID()); | ||
| 54 | return obj; | ||
| 55 | } | ||
| 56 | |||
| 57 | void inputFromScriptValue(const QScriptValue& object, UserInputMapper::Input& input) { | ||
| 58 | input.setDevice(object.property("device").toUInt16()); | ||
| 59 | input.setChannel(object.property("channel").toUInt16()); | ||
| 60 | input.setType(object.property("type").toUInt16()); | ||
| 61 | input.setID(object.property("id").toInt32()); | ||
| 62 | } | ||
| 63 | |||
| 64 | QScriptValue inputChannelToScriptValue(QScriptEngine* engine, const UserInputMapper::InputChannel& inputChannel) { | ||
| 65 | QScriptValue obj = engine->newObject(); | ||
| 66 | obj.setProperty("input", inputToScriptValue(engine, inputChannel.getInput())); | ||
| 67 | obj.setProperty("modifier", inputToScriptValue(engine, inputChannel.getModifier())); | ||
| 68 | obj.setProperty("action", inputChannel.getAction()); | ||
| 69 | obj.setProperty("scale", inputChannel.getScale()); | ||
| 70 | return obj; | ||
| 71 | } | ||
| 72 | |||
| 73 | void inputChannelFromScriptValue(const QScriptValue& object, UserInputMapper::InputChannel& inputChannel) { | ||
| 74 | UserInputMapper::Input input; | ||
| 75 | UserInputMapper::Input modifier; | ||
| 76 | inputFromScriptValue(object.property("input"), input); | ||
| 77 | inputChannel.setInput(input); | ||
| 78 | inputFromScriptValue(object.property("modifier"), modifier); | ||
| 79 | inputChannel.setModifier(modifier); | ||
| 80 | inputChannel.setAction(UserInputMapper::Action(object.property("action").toVariant().toInt())); | ||
| 81 | inputChannel.setScale(object.property("scale").toVariant().toFloat()); | ||
| 82 | } | ||
| 83 | |||
| 84 | QScriptValue actionToScriptValue(QScriptEngine* engine, const UserInputMapper::Action& action) { | ||
| 85 | QScriptValue obj = engine->newObject(); | ||
| 86 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); | ||
| 87 | QVector<UserInputMapper::InputChannel> inputChannels = userInputMapper->getInputChannelsForAction(action); | ||
| 88 | QScriptValue _inputChannels = engine->newArray(inputChannels.size()); | ||
| 89 | for (int i = 0; i < inputChannels.size(); i++) { | ||
| 90 | _inputChannels.setProperty(i, inputChannelToScriptValue(engine, inputChannels[i])); | ||
| 91 | } | ||
| 92 | obj.setProperty("action", (int) action); | ||
| 93 | obj.setProperty("actionName", userInputMapper->getActionName(action)); | ||
| 94 | obj.setProperty("inputChannels", _inputChannels); | ||
| 95 | return obj; | ||
| 96 | } | ||
| 97 | |||
| 98 | void actionFromScriptValue(const QScriptValue& object, UserInputMapper::Action& action) { | ||
| 99 | action = UserInputMapper::Action(object.property("action").toVariant().toInt()); | ||
| 100 | } | ||
| 101 | |||
| 102 | QScriptValue inputPairToScriptValue(QScriptEngine* engine, const UserInputMapper::InputPair& inputPair) { | ||
| 103 | QScriptValue obj = engine->newObject(); | ||
| 104 | obj.setProperty("input", inputToScriptValue(engine, inputPair.first)); | ||
| 105 | obj.setProperty("inputName", inputPair.second); | ||
| 106 | return obj; | ||
| 107 | } | ||
| 108 | |||
| 109 | void inputPairFromScriptValue(const QScriptValue& object, UserInputMapper::InputPair& inputPair) { | ||
| 110 | inputFromScriptValue(object.property("input"), inputPair.first); | ||
| 111 | inputPair.second = QString(object.property("inputName").toVariant().toString()); | ||
| 112 | } | ||
| 113 | |||
| 114 | void ControllerScriptingInterface::registerControllerTypes(ScriptEngine* engine) { | ||
| 115 | qScriptRegisterSequenceMetaType<QVector<UserInputMapper::Action> >(engine); | ||
| 116 | qScriptRegisterSequenceMetaType<QVector<UserInputMapper::InputChannel> >(engine); | ||
| 117 | qScriptRegisterSequenceMetaType<QVector<UserInputMapper::InputPair> >(engine); | ||
| 118 | qScriptRegisterMetaType(engine, actionToScriptValue, actionFromScriptValue); | ||
| 119 | qScriptRegisterMetaType(engine, inputChannelToScriptValue, inputChannelFromScriptValue); | ||
| 120 | qScriptRegisterMetaType(engine, inputToScriptValue, inputFromScriptValue); | ||
| 121 | qScriptRegisterMetaType(engine, inputPairToScriptValue, inputPairFromScriptValue); | ||
| 122 | |||
| 123 | wireUpControllers(engine); | ||
| 124 | } | ||
| 125 | |||
| 126 | void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) { | 26 | void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) { |
| 127 | if (event->type() == HFActionEvent::startType()) { | 27 | if (event->type() == HFActionEvent::startType()) { |
| 128 | emit actionStartEvent(static_cast<HFActionEvent&>(*event)); | 28 | emit actionStartEvent(static_cast<HFActionEvent&>(*event)); |
| @@ -135,205 +35,6 @@ void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) { · 6 unmodified lines | |||
| 135 | } | 35 | } |
| 136 | } | 36 | } |
| 137 | 37 | ||
| 138 | const PalmData* ControllerScriptingInterface::getPrimaryPalm() const { | ||
| 139 | int leftPalmIndex, rightPalmIndex; | ||
| 140 | |||
| 141 | const HandData* handData = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHandData(); | ||
| 142 | handData->getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex); | ||
| 143 | |||
| 144 | if (rightPalmIndex != -1) { | ||
| 145 | return &handData->getPalms()[rightPalmIndex]; | ||
| 146 | } | ||
| 147 | |||
| 148 | return NULL; | ||
| 149 | } | ||
| 150 | |||
| 151 | int ControllerScriptingInterface::getNumberOfActivePalms() const { | ||
| 152 | const HandData* handData = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHandData(); | ||
| 153 | int numberOfPalms = handData->getNumPalms(); | ||
| 154 | int numberOfActivePalms = 0; | ||
| 155 | for (int i = 0; i < numberOfPalms; i++) { | ||
| 156 | if (getPalm(i)->isActive()) { | ||
| 157 | numberOfActivePalms++; | ||
| 158 | } | ||
| 159 | } | ||
| 160 | return numberOfActivePalms; | ||
| 161 | } | ||
| 162 | |||
| 163 | const PalmData* ControllerScriptingInterface::getPalm(int palmIndex) const { | ||
| 164 | const HandData* handData = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHandData(); | ||
| 165 | return &handData->getPalms()[palmIndex]; | ||
| 166 | } | ||
| 167 | |||
| 168 | const PalmData* ControllerScriptingInterface::getActivePalm(int palmIndex) const { | ||
| 169 | const HandData* handData = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHandData(); | ||
| 170 | int numberOfPalms = handData->getNumPalms(); | ||
| 171 | int numberOfActivePalms = 0; | ||
| 172 | for (int i = 0; i < numberOfPalms; i++) { | ||
| 173 | if (getPalm(i)->isActive()) { | ||
| 174 | if (numberOfActivePalms == palmIndex) { | ||
| 175 | return &handData->getPalms()[i]; | ||
| 176 | } | ||
| 177 | numberOfActivePalms++; | ||
| 178 | } | ||
| 179 | } | ||
| 180 | return NULL; | ||
| 181 | } | ||
| 182 | |||
| 183 | bool ControllerScriptingInterface::isPrimaryButtonPressed() const { | ||
| 184 | const PalmData* primaryPalm = getPrimaryPalm(); | ||
| 185 | if (primaryPalm) { | ||
| 186 | if (primaryPalm->getControllerButtons() & BUTTON_FWD) { | ||
| 187 | return true; | ||
| 188 | } | ||
| 189 | } | ||
| 190 | |||
| 191 | return false; | ||
| 192 | } | ||
| 193 | |||
| 194 | glm::vec2 ControllerScriptingInterface::getPrimaryJoystickPosition() const { | ||
| 195 | const PalmData* primaryPalm = getPrimaryPalm(); | ||
| 196 | if (primaryPalm) { | ||
| 197 | return glm::vec2(primaryPalm->getJoystickX(), primaryPalm->getJoystickY()); | ||
| 198 | } | ||
| 199 | |||
| 200 | return glm::vec2(0); | ||
| 201 | } | ||
| 202 | |||
| 203 | int ControllerScriptingInterface::getNumberOfButtons() const { | ||
| 204 | return getNumberOfActivePalms() * NUMBER_OF_BUTTONS_PER_PALM; | ||
| 205 | } | ||
| 206 | |||
| 207 | bool ControllerScriptingInterface::isButtonPressed(int buttonIndex) const { | ||
| 208 | int palmIndex = buttonIndex / NUMBER_OF_BUTTONS_PER_PALM; | ||
| 209 | int buttonOnPalm = buttonIndex % NUMBER_OF_BUTTONS_PER_PALM; | ||
| 210 | const PalmData* palmData = getActivePalm(palmIndex); | ||
| 211 | if (palmData) { | ||
| 212 | switch (buttonOnPalm) { | ||
| 213 | case 0: | ||
| 214 | return palmData->getControllerButtons() & BUTTON_0; | ||
| 215 | case 1: | ||
| 216 | return palmData->getControllerButtons() & BUTTON_1; | ||
| 217 | case 2: | ||
| 218 | return palmData->getControllerButtons() & BUTTON_2; | ||
| 219 | case 3: | ||
| 220 | return palmData->getControllerButtons() & BUTTON_3; | ||
| 221 | case 4: | ||
| 222 | return palmData->getControllerButtons() & BUTTON_4; | ||
| 223 | case 5: | ||
| 224 | return palmData->getControllerButtons() & BUTTON_FWD; | ||
| 225 | } | ||
| 226 | } | ||
| 227 | return false; | ||
| 228 | } | ||
| 229 | |||
| 230 | int ControllerScriptingInterface::getNumberOfTriggers() const { | ||
| 231 | return getNumberOfActivePalms() * NUMBER_OF_TRIGGERS_PER_PALM; | ||
| 232 | } | ||
| 233 | |||
| 234 | float ControllerScriptingInterface::getTriggerValue(int triggerIndex) const { | ||
| 235 | // we know there's one trigger per palm, so the triggerIndex is the palm Index | ||
| 236 | int palmIndex = triggerIndex; | ||
| 237 | const PalmData* palmData = getActivePalm(palmIndex); | ||
| 238 | if (palmData) { | ||
| 239 | return palmData->getTrigger(); | ||
| 240 | } | ||
| 241 | return 0.0f; | ||
| 242 | } | ||
| 243 | |||
| 244 | int ControllerScriptingInterface::getNumberOfJoysticks() const { | ||
| 245 | return getNumberOfActivePalms() * NUMBER_OF_JOYSTICKS_PER_PALM; | ||
| 246 | } | ||
| 247 | |||
| 248 | glm::vec2 ControllerScriptingInterface::getJoystickPosition(int joystickIndex) const { | ||
| 249 | // we know there's one joystick per palm, so the joystickIndex is the palm Index | ||
| 250 | int palmIndex = joystickIndex; | ||
| 251 | const PalmData* palmData = getActivePalm(palmIndex); | ||
| 252 | if (palmData) { | ||
| 253 | return glm::vec2(palmData->getJoystickX(), palmData->getJoystickY()); | ||
| 254 | } | ||
| 255 | return glm::vec2(0); | ||
| 256 | } | ||
| 257 | |||
| 258 | int ControllerScriptingInterface::getNumberOfSpatialControls() const { | ||
| 259 | return getNumberOfActivePalms() * NUMBER_OF_SPATIALCONTROLS_PER_PALM; | ||
| 260 | } | ||
| 261 | |||
| 262 | glm::vec3 ControllerScriptingInterface::getSpatialControlPosition(int controlIndex) const { | ||
| 263 | int palmIndex = controlIndex / NUMBER_OF_SPATIALCONTROLS_PER_PALM; | ||
| 264 | int controlOfPalm = controlIndex % NUMBER_OF_SPATIALCONTROLS_PER_PALM; | ||
| 265 | const PalmData* palmData = getActivePalm(palmIndex); | ||
| 266 | if (palmData) { | ||
| 267 | switch (controlOfPalm) { | ||
| 268 | case PALM_SPATIALCONTROL: | ||
| 269 | return palmData->getPosition(); | ||
| 270 | case TIP_SPATIALCONTROL: | ||
| 271 | return palmData->getTipPosition(); | ||
| 272 | } | ||
| 273 | } | ||
| 274 | return glm::vec3(0); // bad index | ||
| 275 | } | ||
| 276 | |||
| 277 | glm::vec3 ControllerScriptingInterface::getSpatialControlVelocity(int controlIndex) const { | ||
| 278 | int palmIndex = controlIndex / NUMBER_OF_SPATIALCONTROLS_PER_PALM; | ||
| 279 | int controlOfPalm = controlIndex % NUMBER_OF_SPATIALCONTROLS_PER_PALM; | ||
| 280 | const PalmData* palmData = getActivePalm(palmIndex); | ||
| 281 | if (palmData) { | ||
| 282 | switch (controlOfPalm) { | ||
| 283 | case PALM_SPATIALCONTROL: | ||
| 284 | return palmData->getVelocity(); | ||
| 285 | case TIP_SPATIALCONTROL: | ||
| 286 | return palmData->getTipVelocity(); | ||
| 287 | } | ||
| 288 | } | ||
| 289 | return glm::vec3(0); // bad index | ||
| 290 | } | ||
| 291 | |||
| 292 | glm::quat ControllerScriptingInterface::getSpatialControlRawRotation(int controlIndex) const { | ||
| 293 | int palmIndex = controlIndex / NUMBER_OF_SPATIALCONTROLS_PER_PALM; | ||
| 294 | int controlOfPalm = controlIndex % NUMBER_OF_SPATIALCONTROLS_PER_PALM; | ||
| 295 | const PalmData* palmData = getActivePalm(palmIndex); | ||
| 296 | if (palmData) { | ||
| 297 | switch (controlOfPalm) { | ||
| 298 | case PALM_SPATIALCONTROL: | ||
| 299 | return palmData->getRawRotation(); | ||
| 300 | case TIP_SPATIALCONTROL: | ||
| 301 | return palmData->getRawRotation(); // currently the tip doesn't have a unique rotation, use the palm rotation | ||
| 302 | } | ||
| 303 | } | ||
| 304 | return glm::quat(); // bad index | ||
| 305 | } | ||
| 306 | |||
| 307 | glm::vec3 ControllerScriptingInterface::getSpatialControlRawAngularVelocity(int controlIndex) const { | ||
| 308 | int palmIndex = controlIndex / NUMBER_OF_SPATIALCONTROLS_PER_PALM; | ||
| 309 | int controlOfPalm = controlIndex % NUMBER_OF_SPATIALCONTROLS_PER_PALM; | ||
| 310 | const PalmData* palmData = getActivePalm(palmIndex); | ||
| 311 | if (palmData) { | ||
| 312 | switch (controlOfPalm) { | ||
| 313 | case PALM_SPATIALCONTROL: | ||
| 314 | return palmData->getRawAngularVelocity(); | ||
| 315 | case TIP_SPATIALCONTROL: | ||
| 316 | return palmData->getRawAngularVelocity(); // Tip = palm angular velocity | ||
| 317 | } | ||
| 318 | } | ||
| 319 | return glm::vec3(0); // bad index | ||
| 320 | } | ||
| 321 | |||
| 322 | glm::vec3 ControllerScriptingInterface::getSpatialControlNormal(int controlIndex) const { | ||
| 323 | int palmIndex = controlIndex / NUMBER_OF_SPATIALCONTROLS_PER_PALM; | ||
| 324 | int controlOfPalm = controlIndex % NUMBER_OF_SPATIALCONTROLS_PER_PALM; | ||
| 325 | const PalmData* palmData = getActivePalm(palmIndex); | ||
| 326 | if (palmData) { | ||
| 327 | switch (controlOfPalm) { | ||
| 328 | case PALM_SPATIALCONTROL: | ||
| 329 | return palmData->getNormal(); | ||
| 330 | case TIP_SPATIALCONTROL: | ||
| 331 | return palmData->getFingerDirection(); | ||
| 332 | } | ||
| 333 | } | ||
| 334 | return glm::vec3(0); // bad index | ||
| 335 | } | ||
| 336 | |||
| 337 | bool ControllerScriptingInterface::isKeyCaptured(QKeyEvent* event) const { | 38 | bool ControllerScriptingInterface::isKeyCaptured(QKeyEvent* event) const { |
| 338 | return isKeyCaptured(KeyEvent(*event)); | 39 | return isKeyCaptured(KeyEvent(*event)); |
| 339 | } | 40 | } |
| @@ -383,157 +84,45 @@ glm::vec2 ControllerScriptingInterface::getViewportDimensions() const { · 43 unmodified lines | |||
| 383 | return qApp->getUiSize(); | 84 | return qApp->getUiSize(); |
| 384 | } | 85 | } |
| 385 | 86 | ||
| 386 | QString ControllerScriptingInterface::sanatizeName(const QString& name) { | 87 | controller::InputController::Pointer ControllerScriptingInterface::createInputController(const QString& deviceName, const QString& tracker) { |
| 387 | QString cleanName { name }; | ||
| 388 | cleanName.remove(QRegularExpression{"[\\(\\)\\.\\s]"}); | ||
| 389 | return cleanName; | ||
| 390 | } | ||
| 391 | |||
| 392 | void ControllerScriptingInterface::wireUpControllers(ScriptEngine* engine) { | ||
| 393 | |||
| 394 | // Controller.Standard.* | ||
| 395 | auto standardDevice = DependencyManager::get<UserInputMapper>()->getStandardDevice(); | ||
| 396 | if (standardDevice) { | ||
| 397 | auto deviceName = sanatizeName(standardDevice->getName()); | ||
| 398 | auto deviceInputs = standardDevice->getAvailabeInputs(); | ||
| 399 | for (const auto& inputMapping : deviceInputs) { | ||
| 400 | auto input = inputMapping.first; | ||
| 401 | auto inputName = sanatizeName(inputMapping.second); | ||
| 402 | QString deviceInputName{ "Controller." + deviceName + "." + inputName }; | ||
| 403 | engine->registerValue(deviceInputName, input.getID()); | ||
| 404 | } | ||
| 405 | } | ||
| 406 | |||
| 407 | // Controller.Hardware.* | ||
| 408 | auto devices = DependencyManager::get<UserInputMapper>()->getDevices(); | ||
| 409 | for(const auto& deviceMapping : devices) { | ||
| 410 | auto device = deviceMapping.second.get(); | ||
| 411 | auto deviceName = sanatizeName(device->getName()); | ||
| 412 | auto deviceInputs = device->getAvailabeInputs(); | ||
| 413 | for (const auto& inputMapping : deviceInputs) { | ||
| 414 | auto input = inputMapping.first; | ||
| 415 | auto inputName = sanatizeName(inputMapping.second); | ||
| 416 | QString deviceInputName { "Controller.Hardware." + deviceName + "." + inputName }; | ||
| 417 | engine->registerValue(deviceInputName, input.getID()); | ||
| 418 | } | ||
| 419 | } | ||
| 420 | |||
| 421 | // Controller.Actions.* | ||
| 422 | auto actionNames = DependencyManager::get<UserInputMapper>()->getActionNames(); | ||
| 423 | int actionNumber = 0; | ||
| 424 | for (const auto& actionName : actionNames) { | ||
| 425 | QString safeActionName { "Controller.Actions." + sanatizeName(actionName) }; | ||
| 426 | engine->registerValue(safeActionName, actionNumber); | ||
| 427 | actionNumber++; | ||
| 428 | } | ||
| 429 | } | ||
| 430 | |||
| 431 | AbstractInputController* ControllerScriptingInterface::createInputController(const QString& deviceName, const QString& tracker) { | ||
| 432 | // This is where we retreive the Device Tracker category and then the sub tracker within it | 88 | // This is where we retreive the Device Tracker category and then the sub tracker within it |
| 433 | //TODO C++11 auto icIt = _inputControllers.find(0); | 89 | auto icIt = _inputControllers.find(0); |
| 434 | InputControllerMap::iterator icIt = _inputControllers.find(0); | ||
| 435 | |||
| 436 | if (icIt != _inputControllers.end()) { | 90 | if (icIt != _inputControllers.end()) { |
| 437 | return (*icIt).second; | 91 | return (*icIt).second; |
| 438 | } else { | 92 | } |
| 439 | 93 | ||
| 440 | // Look for device | 94 | |
| 441 | DeviceTracker::ID deviceID = DeviceTracker::getDeviceID(deviceName.toStdString()); | 95 | // Look for device |
| 442 | if (deviceID < 0) { | 96 | DeviceTracker::ID deviceID = DeviceTracker::getDeviceID(deviceName.toStdString()); |
| 443 | deviceID = 0; | 97 | if (deviceID < 0) { |
| 444 | } | 98 | deviceID = 0; |
| 445 | // TODO in this current implementation, we just pick the device assuming there is one (normally the Leapmotion) | 99 | } |
| 446 | // in the near future we need to change that to a real mapping between the devices and the deviceName | 100 | // TODO in this current implementation, we just pick the device assuming there is one (normally the Leapmotion) |
| 447 | // ALso we need to expand the spec so we can fall back on the "default" controller per categories | 101 | // in the near future we need to change that to a real mapping between the devices and the deviceName |
| 448 | 102 | // ALso we need to expand the spec so we can fall back on the "default" controller per categories | |
| 449 | if (deviceID >= 0) { | 103 | |
| 450 | // TODO here again the assumption it's the LeapMotion and so it's a MOtionTracker, this would need to be changed to support different types of devices | 104 | if (deviceID >= 0) { |
| 451 | MotionTracker* motionTracker = dynamic_cast< MotionTracker* > (DeviceTracker::getDevice(deviceID)); | 105 | // TODO here again the assumption it's the LeapMotion and so it's a MOtionTracker, this would need to be changed to support different types of devices |
| 452 | if (motionTracker) { | 106 | MotionTracker* motionTracker = dynamic_cast< MotionTracker* > (DeviceTracker::getDevice(deviceID)); |
| 453 | MotionTracker::Index trackerID = motionTracker->findJointIndex(tracker.toStdString()); | 107 | if (motionTracker) { |
| 454 | if (trackerID >= 0) { | 108 | MotionTracker::Index trackerID = motionTracker->findJointIndex(tracker.toStdString()); |
| 455 | AbstractInputController* inputController = new InputController(deviceID, trackerID, this); | 109 | if (trackerID >= 0) { |
| 456 | 110 | controller::InputController::Pointer inputController = std::make_shared<InputController>(deviceID, trackerID, this); | |
| 457 | _inputControllers.insert(InputControllerMap::value_type(inputController->getKey(), inputController)); | 111 | controller::InputController::Key key = inputController->getKey(); |
| 458 | 112 | _inputControllers.insert(InputControllerMap::value_type(key, inputController)); | |
| 459 | return inputController; | 113 | return inputController; |
| 460 | } | ||
| 461 | } | 114 | } |
| 462 | } | 115 | } |
| 463 | |||
| 464 | return 0; | ||
| 465 | } | 116 | } |
| 466 | } | ||
| 467 | 117 | ||
| 468 | void ControllerScriptingInterface::releaseInputController(AbstractInputController* input) { | 118 | return controller::InputController::Pointer(); |
| 469 | _inputControllers.erase(input->getKey()); | ||
| 470 | } | 119 | } |
| 471 | 120 | ||
| 472 | void ControllerScriptingInterface::updateInputControllers() { | 121 | void ControllerScriptingInterface::releaseInputController(controller::InputController::Pointer input) { |
| 473 | //TODO C++11 for (auto it = _inputControllers.begin(); it != _inputControllers.end(); it++) { | 122 | _inputControllers.erase(input->getKey()); |
| 474 | for (InputControllerMap::iterator it = _inputControllers.begin(); it != _inputControllers.end(); it++) { | ||
| 475 | (*it).second->update(); | ||
| 476 | } | ||
| 477 | } | ||
| 478 | |||
| 479 | QVector<UserInputMapper::Action> ControllerScriptingInterface::getAllActions() { | ||
| 480 | return DependencyManager::get<UserInputMapper>()->getAllActions(); | ||
| 481 | } | ||
| 482 | |||
| 483 | QVector<UserInputMapper::InputChannel> ControllerScriptingInterface::getInputChannelsForAction(UserInputMapper::Action action) { | ||
| 484 | return DependencyManager::get<UserInputMapper>()->getInputChannelsForAction(action); | ||
| 485 | } | ||
| 486 | |||
| 487 | QString ControllerScriptingInterface::getDeviceName(unsigned int device) { | ||
| 488 | return DependencyManager::get<UserInputMapper>()->getDeviceName((unsigned short)device); | ||
| 489 | } | ||
| 490 | |||
| 491 | QVector<UserInputMapper::InputChannel> ControllerScriptingInterface::getAllInputsForDevice(unsigned int device) { | ||
| 492 | return DependencyManager::get<UserInputMapper>()->getAllInputsForDevice(device); | ||
| 493 | } | ||
| 494 | |||
| 495 | bool ControllerScriptingInterface::addInputChannel(UserInputMapper::InputChannel inputChannel) { | ||
| 496 | return DependencyManager::get<UserInputMapper>()->addInputChannel(inputChannel._action, inputChannel._input, inputChannel._modifier, inputChannel._scale); | ||
| 497 | } | ||
| 498 | |||
| 499 | bool ControllerScriptingInterface::removeInputChannel(UserInputMapper::InputChannel inputChannel) { | ||
| 500 | return DependencyManager::get<UserInputMapper>()->removeInputChannel(inputChannel); | ||
| 501 | } | ||
| 502 | |||
| 503 | QVector<UserInputMapper::InputPair> ControllerScriptingInterface::getAvailableInputs(unsigned int device) { | ||
| 504 | return DependencyManager::get<UserInputMapper>()->getAvailableInputs((unsigned short)device); | ||
| 505 | } | ||
| 506 | |||
| 507 | void ControllerScriptingInterface::resetAllDeviceBindings() { | ||
| 508 | DependencyManager::get<UserInputMapper>()->resetAllDeviceBindings(); | ||
| 509 | } | ||
| 510 | |||
| 511 | void ControllerScriptingInterface::resetDevice(unsigned int device) { | ||
| 512 | DependencyManager::get<UserInputMapper>()->resetDevice(device); | ||
| 513 | } | ||
| 514 | |||
| 515 | int ControllerScriptingInterface::findDevice(QString name) { | ||
| 516 | return DependencyManager::get<UserInputMapper>()->findDevice(name); | ||
| 517 | } | ||
| 518 | |||
| 519 | QVector<QString> ControllerScriptingInterface::getDeviceNames() { | ||
| 520 | return DependencyManager::get<UserInputMapper>()->getDeviceNames(); | ||
| 521 | } | ||
| 522 | |||
| 523 | float ControllerScriptingInterface::getActionValue(int action) { | ||
| 524 | return DependencyManager::get<UserInputMapper>()->getActionState(UserInputMapper::Action(action)); | ||
| 525 | } | ||
| 526 | |||
| 527 | int ControllerScriptingInterface::findAction(QString actionName) { | ||
| 528 | return DependencyManager::get<UserInputMapper>()->findAction(actionName); | ||
| 529 | } | ||
| 530 | |||
| 531 | QVector<QString> ControllerScriptingInterface::getActionNames() const { | ||
| 532 | return DependencyManager::get<UserInputMapper>()->getActionNames(); | ||
| 533 | } | 123 | } |
| 534 | 124 | ||
| 535 | InputController::InputController(int deviceTrackerId, int subTrackerId, QObject* parent) : | 125 | InputController::InputController(int deviceTrackerId, int subTrackerId, QObject* parent) : |
| 536 | AbstractInputController(), | ||
| 537 | _deviceTrackerId(deviceTrackerId), | 126 | _deviceTrackerId(deviceTrackerId), |
| 538 | _subTrackerId(subTrackerId), | 127 | _subTrackerId(subTrackerId), |
| 539 | _isActive(false) | 128 | _isActive(false) |
| @@ -556,7 +145,7 @@ void InputController::update() { · 16 unmodified lines | |||
| 556 | joint->getLocFrame().getRotation(_eventCache.locRotation); | 145 | joint->getLocFrame().getRotation(_eventCache.locRotation); |
| 557 | 146 | ||
| 558 | _isActive = true; | 147 | _isActive = true; |
| 559 | emit spatialEvent(_eventCache); | 148 | //emit spatialEvent(_eventCache); |
| 560 | } | 149 | } |
| 561 | } | 150 | } |
| 562 | } | 151 | } |
| @@ -568,3 +157,19 @@ const unsigned int INPUTCONTROLLER_KEY_DEVICE_MASK = 16; · 5 unmodified lines | |||
| 568 | InputController::Key InputController::getKey() const { | 157 | InputController::Key InputController::getKey() const { |
| 569 | return (((_deviceTrackerId & INPUTCONTROLLER_KEY_DEVICE_MASK) << INPUTCONTROLLER_KEY_DEVICE_OFFSET) | _subTrackerId); | 158 | return (((_deviceTrackerId & INPUTCONTROLLER_KEY_DEVICE_MASK) << INPUTCONTROLLER_KEY_DEVICE_OFFSET) | _subTrackerId); |
| 570 | } | 159 | } |
| 160 | |||
| 161 | |||
| 162 | void ControllerScriptingInterface::emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); } | ||
| 163 | void ControllerScriptingInterface::emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(KeyEvent(*event)); } | ||
| 164 | |||
| 165 | void ControllerScriptingInterface::emitMouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { emit mouseMoveEvent(MouseEvent(*event, deviceID)); } | ||
| 166 | void ControllerScriptingInterface::emitMousePressEvent(QMouseEvent* event, unsigned int deviceID) { emit mousePressEvent(MouseEvent(*event, deviceID)); } | ||
| 167 | void ControllerScriptingInterface::emitMouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID) { emit mouseDoublePressEvent(MouseEvent(*event, deviceID)); } | ||
| 168 | void ControllerScriptingInterface::emitMouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { emit mouseReleaseEvent(MouseEvent(*event, deviceID)); } | ||
| 169 | |||
| 170 | void ControllerScriptingInterface::emitTouchBeginEvent(const TouchEvent& event) { emit touchBeginEvent(event); } | ||
| 171 | void ControllerScriptingInterface::emitTouchEndEvent(const TouchEvent& event) { emit touchEndEvent(event); } | ||
| 172 | void ControllerScriptingInterface::emitTouchUpdateEvent(const TouchEvent& event) { emit touchUpdateEvent(event); } | ||
| 173 | |||
| 174 | void ControllerScriptingInterface::emitWheelEvent(QWheelEvent* event) { emit wheelEvent(*event); } | ||
| 175 | |||
| @@ -14,12 +14,20 @@ · 13 unmodified lines | |||
| 14 | 14 | ||
| 15 | #include <QtCore/QObject> | 15 | #include <QtCore/QObject> |
| 16 | 16 | ||
| 17 | #include <input-plugins/UserInputMapper.h> | 17 | #include <controllers/UserInputMapper.h> |
| 18 | #include <controllers/ScriptingInterface.h> | ||
| 19 | |||
| 20 | #include <HFActionEvent.h> | ||
| 21 | #include <KeyEvent.h> | ||
| 22 | #include <MouseEvent.h> | ||
| 23 | #include <SpatialEvent.h> | ||
| 24 | #include <TouchEvent.h> | ||
| 25 | #include <WheelEvent.h> | ||
| 26 | class ScriptEngine; | ||
| 18 | 27 | ||
| 19 | #include <AbstractControllerScriptingInterface.h> | ||
| 20 | class PalmData; | 28 | class PalmData; |
| 21 | 29 | ||
| 22 | class InputController : public AbstractInputController { | 30 | class InputController : public controller::InputController { |
| 23 | Q_OBJECT | 31 | Q_OBJECT |
| 24 | 32 | ||
| 25 | public: | 33 | public: |
| @@ -50,125 +58,77 @@ signals: · 24 unmodified lines | |||
| 50 | 58 | ||
| 51 | 59 | ||
| 52 | /// handles scripting of input controller commands from JS | 60 | /// handles scripting of input controller commands from JS |
| 53 | class ControllerScriptingInterface : public AbstractControllerScriptingInterface { | 61 | class ControllerScriptingInterface : public controller::ScriptingInterface { |
| 54 | Q_OBJECT | 62 | Q_OBJECT |
| 55 | 63 | ||
| 64 | |||
| 56 | public: | 65 | public: |
| 57 | ControllerScriptingInterface(); | 66 | virtual ~ControllerScriptingInterface() {} |
| 58 | 67 | ||
| 59 | virtual void registerControllerTypes(ScriptEngine* engine); | 68 | void emitKeyPressEvent(QKeyEvent* event); |
| 60 | 69 | void emitKeyReleaseEvent(QKeyEvent* event); | |
| 61 | void emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); } | ||
| 62 | void emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(KeyEvent(*event)); } | ||
| 63 | 70 | ||
| 64 | void handleMetaEvent(HFMetaEvent* event); | 71 | void handleMetaEvent(HFMetaEvent* event); |
| 65 | 72 | ||
| 66 | void emitMouseMoveEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mouseMoveEvent(MouseEvent(*event, deviceID)); } | 73 | void emitMouseMoveEvent(QMouseEvent* event, unsigned int deviceID = 0); |
| 67 | void emitMousePressEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mousePressEvent(MouseEvent(*event, deviceID)); } | 74 | void emitMousePressEvent(QMouseEvent* event, unsigned int deviceID = 0); |
| 68 | void emitMouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mouseDoublePressEvent(MouseEvent(*event, deviceID)); } | 75 | void emitMouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID = 0); |
| 69 | void emitMouseReleaseEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mouseReleaseEvent(MouseEvent(*event, deviceID)); } | 76 | void emitMouseReleaseEvent(QMouseEvent* event, unsigned int deviceID = 0); |
| 70 | 77 | ||
| 71 | void emitTouchBeginEvent(const TouchEvent& event) { emit touchBeginEvent(event); } | 78 | void emitTouchBeginEvent(const TouchEvent& event); |
| 72 | void emitTouchEndEvent(const TouchEvent& event) { emit touchEndEvent(event); } | 79 | void emitTouchEndEvent(const TouchEvent& event); |
| 73 | void emitTouchUpdateEvent(const TouchEvent& event) { emit touchUpdateEvent(event); } | 80 | void emitTouchUpdateEvent(const TouchEvent& event); |
| 74 | 81 | ||
| 75 | void emitWheelEvent(QWheelEvent* event) { emit wheelEvent(*event); } | 82 | void emitWheelEvent(QWheelEvent* event); |
| 76 | 83 | ||
| 77 | bool isKeyCaptured(QKeyEvent* event) const; | 84 | bool isKeyCaptured(QKeyEvent* event) const; |
| 78 | bool isKeyCaptured(const KeyEvent& event) const; | 85 | bool isKeyCaptured(const KeyEvent& event) const; |
| 79 | bool isMouseCaptured() const { return _mouseCaptured; } | ||
| 80 | bool isTouchCaptured() const { return _touchCaptured; } | ||
| 81 | bool isWheelCaptured() const { return _wheelCaptured; } | ||
| 82 | bool areActionsCaptured() const { return _actionsCaptured; } | ||
| 83 | bool isJoystickCaptured(int joystickIndex) const; | 86 | bool isJoystickCaptured(int joystickIndex) const; |
| 84 | 87 | ||
| 85 | void updateInputControllers(); | ||
| 86 | |||
| 87 | public slots: | 88 | public slots: |
| 88 | Q_INVOKABLE virtual QVector<UserInputMapper::Action> getAllActions(); | ||
| 89 | |||
| 90 | Q_INVOKABLE virtual bool addInputChannel(UserInputMapper::InputChannel inputChannel); | ||
| 91 | Q_INVOKABLE virtual bool removeInputChannel(UserInputMapper::InputChannel inputChannel); | ||
| 92 | Q_INVOKABLE virtual QVector<UserInputMapper::InputChannel> getInputChannelsForAction(UserInputMapper::Action action); | ||
| 93 | |||
| 94 | Q_INVOKABLE virtual QVector<UserInputMapper::InputPair> getAvailableInputs(unsigned int device); | ||
| 95 | Q_INVOKABLE virtual QVector<UserInputMapper::InputChannel> getAllInputsForDevice(unsigned int device); | ||
| 96 | |||
| 97 | Q_INVOKABLE virtual QString getDeviceName(unsigned int device); | ||
| 98 | |||
| 99 | Q_INVOKABLE virtual float getActionValue(int action); | ||
| 100 | |||
| 101 | Q_INVOKABLE virtual void resetDevice(unsigned int device); | ||
| 102 | Q_INVOKABLE virtual void resetAllDeviceBindings(); | ||
| 103 | Q_INVOKABLE virtual int findDevice(QString name); | ||
| 104 | Q_INVOKABLE virtual QVector<QString> getDeviceNames(); | ||
| 105 | |||
| 106 | Q_INVOKABLE virtual int findAction(QString actionName); | ||
| 107 | Q_INVOKABLE virtual QVector<QString> getActionNames() const; | ||
| 108 | |||
| 109 | virtual bool isPrimaryButtonPressed() const; | ||
| 110 | virtual glm::vec2 getPrimaryJoystickPosition() const; | ||
| 111 | |||
| 112 | virtual int getNumberOfButtons() const; | ||
| 113 | virtual bool isButtonPressed(int buttonIndex) const; | ||
| 114 | |||
| 115 | virtual int getNumberOfTriggers() const; | ||
| 116 | virtual float getTriggerValue(int triggerIndex) const; | ||
| 117 | 89 | ||
| 118 | virtual int getNumberOfJoysticks() const; | ||
| 119 | virtual glm::vec2 getJoystickPosition(int joystickIndex) const; | ||
| 120 | |||
| 121 | virtual int getNumberOfSpatialControls() const; | ||
| 122 | virtual glm::vec3 getSpatialControlPosition(int controlIndex) const; | ||
| 123 | virtual glm::vec3 getSpatialControlVelocity(int controlIndex) const; | ||
| 124 | virtual glm::vec3 getSpatialControlNormal(int controlIndex) const; | ||
| 125 | virtual glm::quat getSpatialControlRawRotation(int controlIndex) const; | ||
| 126 | virtual glm::vec3 getSpatialControlRawAngularVelocity(int controlIndex) const; | ||
| 127 | virtual void captureKeyEvents(const KeyEvent& event); | 90 | virtual void captureKeyEvents(const KeyEvent& event); |
| 128 | virtual void releaseKeyEvents(const KeyEvent& event); | 91 | virtual void releaseKeyEvents(const KeyEvent& event); |
| 129 | 92 | ||
| 130 | virtual void captureMouseEvents() { _mouseCaptured = true; } | ||
| 131 | virtual void releaseMouseEvents() { _mouseCaptured = false; } | ||
| 132 | |||
| 133 | virtual void captureTouchEvents() { _touchCaptured = true; } | ||
| 134 | virtual void releaseTouchEvents() { _touchCaptured = false; } | ||
| 135 | |||
| 136 | virtual void captureWheelEvents() { _wheelCaptured = true; } | ||
| 137 | virtual void releaseWheelEvents() { _wheelCaptured = false; } | ||
| 138 | |||
| 139 | virtual void captureActionEvents() { _actionsCaptured = true; } | ||
| 140 | virtual void releaseActionEvents() { _actionsCaptured = false; } | ||
| 141 | |||
| 142 | virtual void captureJoystick(int joystickIndex); | 93 | virtual void captureJoystick(int joystickIndex); |
| 143 | virtual void releaseJoystick(int joystickIndex); | 94 | virtual void releaseJoystick(int joystickIndex); |
| 144 | 95 | ||
| 145 | virtual glm::vec2 getViewportDimensions() const; | 96 | virtual glm::vec2 getViewportDimensions() const; |
| 146 | 97 | ||
| 147 | /// Factory to create an InputController | 98 | /// Factory to create an InputController |
| 148 | virtual AbstractInputController* createInputController(const QString& deviceName, const QString& tracker); | 99 | virtual controller::InputController::Pointer createInputController(const QString& deviceName, const QString& tracker); |
| 100 | virtual void releaseInputController(controller::InputController::Pointer input); | ||
| 101 | |||
| 102 | signals: | ||
| 103 | void keyPressEvent(const KeyEvent& event); | ||
| 104 | void keyReleaseEvent(const KeyEvent& event); | ||
| 149 | 105 | ||
| 150 | virtual void releaseInputController(AbstractInputController* input); | 106 | void actionStartEvent(const HFActionEvent& event); |
| 107 | void actionEndEvent(const HFActionEvent& event); | ||
| 108 | |||
| 109 | void backStartEvent(); | ||
| 110 | void backEndEvent(); | ||
| 111 | |||
| 112 | void mouseMoveEvent(const MouseEvent& event, unsigned int deviceID = 0); | ||
| 113 | void mousePressEvent(const MouseEvent& event, unsigned int deviceID = 0); | ||
| 114 | void mouseDoublePressEvent(const MouseEvent& event, unsigned int deviceID = 0); | ||
| 115 | void mouseReleaseEvent(const MouseEvent& event, unsigned int deviceID = 0); | ||
| 116 | |||
| 117 | void touchBeginEvent(const TouchEvent& event); | ||
| 118 | void touchEndEvent(const TouchEvent& event); | ||
| 119 | void touchUpdateEvent(const TouchEvent& event); | ||
| 120 | |||
| 121 | void wheelEvent(const WheelEvent& event); | ||
| 151 | 122 | ||
| 152 | private: | 123 | private: |
| 153 | QString sanatizeName(const QString& name); /// makes a name clean for inclusing in JavaScript | 124 | QString sanatizeName(const QString& name); /// makes a name clean for inclusing in JavaScript |
| 154 | 125 | ||
| 155 | const PalmData* getPrimaryPalm() const; | ||
| 156 | const PalmData* getPalm(int palmIndex) const; | ||
| 157 | int getNumberOfActivePalms() const; | ||
| 158 | const PalmData* getActivePalm(int palmIndex) const; | ||
| 159 | |||
| 160 | bool _mouseCaptured; | ||
| 161 | bool _touchCaptured; | ||
| 162 | bool _wheelCaptured; | ||
| 163 | bool _actionsCaptured; | ||
| 164 | QMultiMap<int,KeyEvent> _capturedKeys; | 126 | QMultiMap<int,KeyEvent> _capturedKeys; |
| 165 | QSet<int> _capturedJoysticks; | 127 | QSet<int> _capturedJoysticks; |
| 166 | 128 | ||
| 167 | typedef std::map< AbstractInputController::Key, AbstractInputController* > InputControllerMap; | 129 | using InputKey = controller::InputController::Key; |
| 130 | using InputControllerMap = std::map<InputKey, controller::InputController::Pointer>; | ||
| 168 | InputControllerMap _inputControllers; | 131 | InputControllerMap _inputControllers; |
| 169 | |||
| 170 | void wireUpControllers(ScriptEngine* engine); | ||
| 171 | |||
| 172 | }; | 132 | }; |
| 173 | 133 | ||
| 174 | const int NUMBER_OF_SPATIALCONTROLS_PER_PALM = 2; // the hand and the tip | 134 | const int NUMBER_OF_SPATIALCONTROLS_PER_PALM = 2; // the hand and the tip |
| @@ -27,7 +27,7 @@ · 26 unmodified lines | |||
| 27 | 27 | ||
| 28 | #include "Application.h" | 28 | #include "Application.h" |
| 29 | #include <input-plugins/SixenseManager.h> // TODO: any references to sixense should be removed here | 29 | #include <input-plugins/SixenseManager.h> // TODO: any references to sixense should be removed here |
| 30 | #include <input-plugins/InputDevice.h> | 30 | #include <controllers/InputDevice.h> |
| 31 | 31 | ||
| 32 | 32 | ||
| 33 | // Used to animate the magnification windows | 33 | // Used to animate the magnification windows |
| @@ -320,8 +320,8 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int · 286 unmodified lines | |||
| 320 | // Only render the hand pointers if the EnableHandMouseInput is enabled | 320 | // Only render the hand pointers if the EnableHandMouseInput is enabled |
| 321 | if (Menu::getInstance()->isOptionChecked(MenuOption::EnableHandMouseInput)) { | 321 | if (Menu::getInstance()->isOptionChecked(MenuOption::EnableHandMouseInput)) { |
| 322 | MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); | 322 | MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); |
| 323 | for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) { | 323 | auto palms = myAvatar->getHand()->getCopyOfPalms(); |
| 324 | PalmData& palm = myAvatar->getHand()->getPalms()[i]; | 324 | for (const auto& palm : palms) { |
| 325 | if (palm.isActive()) { | 325 | if (palm.isActive()) { |
| 326 | glm::vec2 polar = getPolarCoordinates(palm); | 326 | glm::vec2 polar = getPolarCoordinates(palm); |
| 327 | // Convert to quaternion | 327 | // Convert to quaternion |
| @@ -446,6 +446,7 @@ void ApplicationCompositor::renderPointers(gpu::Batch& batch) { · 118 unmodified lines | |||
| 446 | } | 446 | } |
| 447 | 447 | ||
| 448 | 448 | ||
| 449 | // FIXME - this is old code that likely needs to be removed and/or reworked to support the new input control model | ||
| 449 | void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) { | 450 | void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) { |
| 450 | MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); | 451 | MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); |
| 451 | 452 | ||
| @@ -455,23 +456,24 @@ void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) { · 3 unmodified lines | |||
| 455 | static bool stateWhenPressed[NUMBER_OF_RETICLES] = { false, false, false }; | 456 | static bool stateWhenPressed[NUMBER_OF_RETICLES] = { false, false, false }; |
| 456 | 457 | ||
| 457 | const HandData* handData = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHandData(); | 458 | const HandData* handData = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHandData(); |
| 459 | auto palms = handData->getCopyOfPalms(); | ||
| 458 | 460 | ||
| 459 | for (unsigned int palmIndex = 2; palmIndex < 4; palmIndex++) { | 461 | for (unsigned int palmIndex = 2; palmIndex < 4; palmIndex++) { |
| 460 | const int index = palmIndex - 1; | 462 | const int index = palmIndex - 1; |
| 461 | 463 | ||
| 462 | const PalmData* palmData = NULL; | 464 | const PalmData* palmData = NULL; |
| 463 | 465 | ||
| 464 | if (palmIndex >= handData->getPalms().size()) { | 466 | if (palmIndex >= palms.size()) { |
| 465 | return; | 467 | return; |
| 466 | } | 468 | } |
| 467 | 469 | ||
| 468 | if (handData->getPalms()[palmIndex].isActive()) { | 470 | if (palms[palmIndex].isActive()) { |
| 469 | palmData = &handData->getPalms()[palmIndex]; | 471 | palmData = &palms[palmIndex]; |
| 470 | } else { | 472 | } else { |
| 471 | continue; | 473 | continue; |
| 472 | } | 474 | } |
| 473 | 475 | ||
| 474 | int controllerButtons = palmData->getControllerButtons(); | 476 | int controllerButtons = 0; |
| 475 | 477 | ||
| 476 | //Check for if we should toggle or drag the magnification window | 478 | //Check for if we should toggle or drag the magnification window |
| 477 | if (controllerButtons & BUTTON_3) { | 479 | if (controllerButtons & BUTTON_3) { |
| @@ -521,7 +523,7 @@ void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) { · 43 unmodified lines | |||
| 521 | float yAngle = 0.5f - ((atan2f(direction.z, direction.y) + (float)PI_OVER_TWO)); | 523 | float yAngle = 0.5f - ((atan2f(direction.z, direction.y) + (float)PI_OVER_TWO)); |
| 522 | 524 | ||
| 523 | // Get the pixel range over which the xAngle and yAngle are scaled | 525 | // Get the pixel range over which the xAngle and yAngle are scaled |
| 524 | float cursorRange = canvasSize.x * InputDevice::getCursorPixelRangeMult(); | 526 | float cursorRange = canvasSize.x * controller::InputDevice::getCursorPixelRangeMult(); |
| 525 | 527 | ||
| 526 | mouseX = (canvasSize.x / 2.0f + cursorRange * xAngle); | 528 | mouseX = (canvasSize.x / 2.0f + cursorRange * xAngle); |
| 527 | mouseY = (canvasSize.y / 2.0f + cursorRange * yAngle); | 529 | mouseY = (canvasSize.y / 2.0f + cursorRange * yAngle); |
| @@ -198,7 +198,7 @@ void PreferencesDialog::loadPreferences() { · 197 unmodified lines | |||
| 198 | ui.oculusUIAngularSizeSpin->setValue(qApp->getApplicationCompositor().getHmdUIAngularSize()); | 198 | ui.oculusUIAngularSizeSpin->setValue(qApp->getApplicationCompositor().getHmdUIAngularSize()); |
| 199 | #endif | 199 | #endif |
| 200 | 200 | ||
| 201 | ui.sixenseReticleMoveSpeedSpin->setValue(InputDevice::getReticleMoveSpeed()); | 201 | ui.sixenseReticleMoveSpeedSpin->setValue(controller::InputDevice::getReticleMoveSpeed()); |
| 202 | 202 | ||
| 203 | // LOD items | 203 | // LOD items |
| 204 | auto lodManager = DependencyManager::get<LODManager>(); | 204 | auto lodManager = DependencyManager::get<LODManager>(); |
| @@ -273,7 +273,7 @@ void PreferencesDialog::savePreferences() { · 68 unmodified lines | |||
| 273 | 273 | ||
| 274 | qApp->getApplicationCompositor().setHmdUIAngularSize(ui.oculusUIAngularSizeSpin->value()); | 274 | qApp->getApplicationCompositor().setHmdUIAngularSize(ui.oculusUIAngularSizeSpin->value()); |
| 275 | 275 | ||
| 276 | InputDevice::setReticleMoveSpeed(ui.sixenseReticleMoveSpeedSpin->value()); | 276 | controller::InputDevice::setReticleMoveSpeed(ui.sixenseReticleMoveSpeedSpin->value()); |
| 277 | 277 | ||
| 278 | auto audio = DependencyManager::get<AudioClient>(); | 278 | auto audio = DependencyManager::get<AudioClient>(); |
| 279 | MixedProcessedAudioStream& stream = audio->getReceivedAudioStream(); | 279 | MixedProcessedAudioStream& stream = audio->getReceivedAudioStream(); |
| @@ -0,0 +1,121 @@ | |||
| 1 | // | ||
| 2 | // AnimVariantMap.cpp | ||
| 3 | // library/animation | ||
| 4 | // | ||
| 5 | // Created by Howard Stearns on 10/15/15. | ||
| 6 | // Copyright (c) 2015 High Fidelity, Inc. All rights reserved. | ||
| 7 | // | ||
| 8 | // Distributed under the Apache License, Version 2.0. | ||
| 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 10 | // | ||
| 11 | |||
| 12 | #include <QScriptEngine> | ||
| 13 | #include <QScriptValueIterator> | ||
| 14 | #include <QThread> | ||
| 15 | #include <RegisteredMetaTypes.h> | ||
| 16 | #include "AnimVariant.h" // which has AnimVariant/AnimVariantMap | ||
| 17 | |||
| 18 | QScriptValue AnimVariantMap::animVariantMapToScriptValue(QScriptEngine* engine, const QStringList& names, bool useNames) const { | ||
| 19 | if (QThread::currentThread() != engine->thread()) { | ||
| 20 | qCWarning(animation) << "Cannot create Javacript object from non-script thread" << QThread::currentThread(); | ||
| 21 | Q_ASSERT(false); | ||
| 22 | return QScriptValue(); | ||
| 23 | } | ||
| 24 | QScriptValue target = engine->newObject(); | ||
| 25 | auto setOne = [&] (const QString& name, const AnimVariant& value) { | ||
| 26 | switch (value.getType()) { | ||
| 27 | case AnimVariant::Type::Bool: | ||
| 28 | target.setProperty(name, value.getBool()); | ||
| 29 | break; | ||
| 30 | case AnimVariant::Type::Int: | ||
| 31 | target.setProperty(name, value.getInt()); | ||
| 32 | break; | ||
| 33 | case AnimVariant::Type::Float: | ||
| 34 | target.setProperty(name, value.getFloat()); | ||
| 35 | break; | ||
| 36 | case AnimVariant::Type::String: | ||
| 37 | target.setProperty(name, value.getString()); | ||
| 38 | break; | ||
| 39 | case AnimVariant::Type::Vec3: | ||
| 40 | target.setProperty(name, vec3toScriptValue(engine, value.getVec3())); | ||
| 41 | break; | ||
| 42 | case AnimVariant::Type::Quat: | ||
| 43 | target.setProperty(name, quatToScriptValue(engine, value.getQuat())); | ||
| 44 | break; | ||
| 45 | default: | ||
| 46 | // Note that we don't do mat4 in Javascript currently, and there's not yet a reason to start now. | ||
| 47 | assert("AnimVariant::Type" == "valid"); | ||
| 48 | } | ||
| 49 | }; | ||
| 50 | if (useNames) { // copy only the requested names | ||
| 51 | for (const QString& name : names) { | ||
| 52 | auto search = _map.find(name); | ||
| 53 | if (search != _map.end()) { // scripts are allowed to request names that do not exist | ||
| 54 | setOne(name, search->second); | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | } else { // copy all of them | ||
| 59 | for (auto& pair : _map) { | ||
| 60 | setOne(pair.first, pair.second); | ||
| 61 | } | ||
| 62 | } | ||
| 63 | return target; | ||
| 64 | } | ||
| 65 | void AnimVariantMap::copyVariantsFrom(const AnimVariantMap& other) { | ||
| 66 | for (auto& pair : other._map) { | ||
| 67 | _map[pair.first] = pair.second; | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | void AnimVariantMap::animVariantMapFromScriptValue(const QScriptValue& source) { | ||
| 72 | if (QThread::currentThread() != source.engine()->thread()) { | ||
| 73 | qCWarning(animation) << "Cannot examine Javacript object from non-script thread" << QThread::currentThread(); | ||
| 74 | Q_ASSERT(false); | ||
| 75 | return; | ||
| 76 | } | ||
| 77 | // POTENTIAL OPTIMIZATION: cache the types we've seen. I.e, keep a dictionary mapping property names to an enumeration of types. | ||
| 78 | // Whenever we identify a new outbound type in animVariantMapToScriptValue above, or a new inbound type in the code that follows here, | ||
| 79 | // we would enter it into the dictionary. Then switch on that type here, with the code that follow being executed only if | ||
| 80 | // the type is not known. One problem with that is that there is no checking that two different script use the same name differently. | ||
| 81 | QScriptValueIterator property(source); | ||
| 82 | // Note: QScriptValueIterator iterates only over source's own properties. It does not follow the prototype chain. | ||
| 83 | while (property.hasNext()) { | ||
| 84 | property.next(); | ||
| 85 | QScriptValue value = property.value(); | ||
| 86 | if (value.isBool()) { | ||
| 87 | set(property.name(), value.toBool()); | ||
| 88 | } else if (value.isString()) { | ||
| 89 | set(property.name(), value.toString()); | ||
| 90 | } else if (value.isNumber()) { | ||
| 91 | int asInteger = value.toInt32(); | ||
| 92 | float asFloat = value.toNumber(); | ||
| 93 | if (asInteger == asFloat) { | ||
| 94 | set(property.name(), asInteger); | ||
| 95 | } else { | ||
| 96 | set(property.name(), asFloat); | ||
| 97 | } | ||
| 98 | } else { // Try to get x,y,z and possibly w | ||
| 99 | if (value.isObject()) { | ||
| 100 | QScriptValue x = value.property("x"); | ||
| 101 | if (x.isNumber()) { | ||
| 102 | QScriptValue y = value.property("y"); | ||
| 103 | if (y.isNumber()) { | ||
| 104 | QScriptValue z = value.property("z"); | ||
| 105 | if (z.isNumber()) { | ||
| 106 | QScriptValue w = value.property("w"); | ||
| 107 | if (w.isNumber()) { | ||
| 108 | set(property.name(), glm::quat(x.toNumber(), y.toNumber(), z.toNumber(), w.toNumber())); | ||
| 109 | } else { | ||
| 110 | set(property.name(), glm::vec3(x.toNumber(), y.toNumber(), z.toNumber())); | ||
| 111 | } | ||
| 112 | continue; // we got either a vector or quaternion object, so don't fall through to warning | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
| 116 | } | ||
| 117 | qCWarning(animation) << "Ignoring unrecognized data" << value.toString() << "for animation property" << property.name(); | ||
| 118 | Q_ASSERT(false); | ||
| 119 | } | ||
| 120 | } | ||
| 121 | } | ||
| @@ -12,11 +12,14 @@ · 11 unmodified lines | |||
| 12 | #define hifi_AnimVariant_h | 12 | #define hifi_AnimVariant_h |
| 13 | 13 | ||
| 14 | #include <cassert> | 14 | #include <cassert> |
| 15 | #include <functional> | ||
| 15 | #include <glm/glm.hpp> | 16 | #include <glm/glm.hpp> |
| 16 | #include <glm/gtx/quaternion.hpp> | 17 | #include <glm/gtx/quaternion.hpp> |
| 17 | #include <map> | 18 | #include <map> |
| 18 | #include <set> | 19 | #include <set> |
| 20 | #include <QScriptValue> | ||
| 19 | #include "AnimationLogging.h" | 21 | #include "AnimationLogging.h" |
| 22 | #include "StreamUtils.h" | ||
| 20 | 23 | ||
| 21 | class AnimVariant { | 24 | class AnimVariant { |
| 22 | public: | 25 | public: |
| @@ -58,8 +61,9 @@ public: · 35 unmodified lines | |||
| 58 | void setString(const QString& value) { assert(_type == Type::String); _stringVal = value; } | 61 | void setString(const QString& value) { assert(_type == Type::String); _stringVal = value; } |
| 59 | 62 | ||
| 60 | bool getBool() const { assert(_type == Type::Bool); return _val.boolVal; } | 63 | bool getBool() const { assert(_type == Type::Bool); return _val.boolVal; } |
| 61 | int getInt() const { assert(_type == Type::Int); return _val.intVal; } | 64 | int getInt() const { assert(_type == Type::Int || _type == Type::Float); return _type == Type::Float ? (int)_val.floats[0] : _val.intVal; } |
| 62 | float getFloat() const { assert(_type == Type::Float); return _val.floats[0]; } | 65 | float getFloat() const { assert(_type == Type::Float || _type == Type::Int); return _type == Type::Int ? (float)_val.intVal : _val.floats[0]; } |
| 66 | |||
| 63 | const glm::vec3& getVec3() const { assert(_type == Type::Vec3); return *reinterpret_cast<const glm::vec3*>(&_val); } | 67 | const glm::vec3& getVec3() const { assert(_type == Type::Vec3); return *reinterpret_cast<const glm::vec3*>(&_val); } |
| 64 | const glm::quat& getQuat() const { assert(_type == Type::Quat); return *reinterpret_cast<const glm::quat*>(&_val); } | 68 | const glm::quat& getQuat() const { assert(_type == Type::Quat); return *reinterpret_cast<const glm::quat*>(&_val); } |
| 65 | const glm::mat4& getMat4() const { assert(_type == Type::Mat4); return *reinterpret_cast<const glm::mat4*>(&_val); } | 69 | const glm::mat4& getMat4() const { assert(_type == Type::Mat4); return *reinterpret_cast<const glm::mat4*>(&_val); } |
| @@ -156,8 +160,15 @@ public: · 90 unmodified lines | |||
| 156 | void setTrigger(const QString& key) { _triggers.insert(key); } | 160 | void setTrigger(const QString& key) { _triggers.insert(key); } |
| 157 | void clearTriggers() { _triggers.clear(); } | 161 | void clearTriggers() { _triggers.clear(); } |
| 158 | 162 | ||
| 163 | void clearMap() { _map.clear(); } | ||
| 159 | bool hasKey(const QString& key) const { return _map.find(key) != _map.end(); } | 164 | bool hasKey(const QString& key) const { return _map.find(key) != _map.end(); } |
| 160 | 165 | ||
| 166 | // Answer a Plain Old Javascript Object (for the given engine) all of our values set as properties. | ||
| 167 | QScriptValue animVariantMapToScriptValue(QScriptEngine* engine, const QStringList& names, bool useNames) const; | ||
| 168 | // Side-effect us with the value of object's own properties. (No inherited properties.) | ||
| 169 | void animVariantMapFromScriptValue(const QScriptValue& object); | ||
| 170 | void copyVariantsFrom(const AnimVariantMap& other); | ||
| 171 | |||
| 161 | #ifdef NDEBUG | 172 | #ifdef NDEBUG |
| 162 | void dump() const { | 173 | void dump() const { |
| 163 | qCDebug(animation) << "AnimVariantMap ="; | 174 | qCDebug(animation) << "AnimVariantMap ="; |
| @@ -196,4 +207,8 @@ protected: · 32 unmodified lines | |||
| 196 | std::set<QString> _triggers; | 207 | std::set<QString> _triggers; |
| 197 | }; | 208 | }; |
| 198 | 209 | ||
| 210 | typedef std::function<void(QScriptValue)> AnimVariantResultHandler; | ||
| 211 | Q_DECLARE_METATYPE(AnimVariantResultHandler); | ||
| 212 | Q_DECLARE_METATYPE(AnimVariantMap) | ||
| 213 | |||
| 199 | #endif // hifi_AnimVariant_h | 214 | #endif // hifi_AnimVariant_h |
| @@ -13,6 +13,7 @@ · 12 unmodified lines | |||
| 13 | 13 | ||
| 14 | #include <glm/gtx/vector_angle.hpp> | 14 | #include <glm/gtx/vector_angle.hpp> |
| 15 | #include <queue> | 15 | #include <queue> |
| 16 | #include <QScriptValueIterator> | ||
| 16 | 17 | ||
| 17 | #include <NumericalConstants.h> | 18 | #include <NumericalConstants.h> |
| 18 | #include <DebugDraw.h> | 19 | #include <DebugDraw.h> |
| @@ -576,6 +577,71 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos · 557 unmodified lines | |||
| 576 | _lastPosition = worldPosition; | 577 | _lastPosition = worldPosition; |
| 577 | } | 578 | } |
| 578 | 579 | ||
| 580 | // Allow script to add/remove handlers and report results, from within their thread. | ||
| 581 | QScriptValue Rig::addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { // called in script thread | ||
| 582 | QMutexLocker locker(&_stateMutex); | ||
| 583 | // Find a safe id, even if there are lots of many scripts add and remove handlers repeatedly. | ||
| 584 | while (!_nextStateHandlerId || _stateHandlers.contains(_nextStateHandlerId)) { // 0 is unused, and don't reuse existing after wrap. | ||
| 585 | _nextStateHandlerId++; | ||
| 586 | } | ||
| 587 | StateHandler& data = _stateHandlers[_nextStateHandlerId]; | ||
| 588 | data.function = handler; | ||
| 589 | data.useNames = propertiesList.isArray(); | ||
| 590 | if (data.useNames) { | ||
| 591 | data.propertyNames = propertiesList.toVariant().toStringList(); | ||
| 592 | } | ||
| 593 | return QScriptValue(_nextStateHandlerId); // suitable for giving to removeAnimationStateHandler | ||
| 594 | } | ||
| 595 | void Rig::removeAnimationStateHandler(QScriptValue identifier) { // called in script thread | ||
| 596 | QMutexLocker locker(&_stateMutex); | ||
| 597 | _stateHandlers.remove(identifier.isNumber() ? identifier.toInt32() : 0); // silently continues if handler not present. 0 is unused | ||
| 598 | } | ||
| 599 | void Rig::animationStateHandlerResult(int identifier, QScriptValue result) { // called synchronously from script | ||
| 600 | QMutexLocker locker(&_stateMutex); | ||
| 601 | auto found = _stateHandlers.find(identifier); | ||
| 602 | if (found == _stateHandlers.end()) { | ||
| 603 | return; // Don't use late-breaking results that got reported after the handler was removed. | ||
| 604 | } | ||
| 605 | found.value().results.animVariantMapFromScriptValue(result); // Into our own copy. | ||
| 606 | } | ||
| 607 | |||
| 608 | void Rig::updateAnimationStateHandlers() { // called on avatar update thread (which may be main thread) | ||
| 609 | QMutexLocker locker(&_stateMutex); | ||
| 610 | // It might pay to produce just one AnimVariantMap copy here, with a union of all the requested propertyNames, | ||
| 611 | // rather than having each callAnimationStateHandler invocation make its own copy. | ||
| 612 | // However, that copying is done on the script's own time rather than ours, so even if it's less cpu, it would be more | ||
| 613 | // work on the avatar update thread (which is possibly the main thread). | ||
| 614 | for (auto data = _stateHandlers.begin(); data != _stateHandlers.end(); data++) { | ||
| 615 | // call out: | ||
| 616 | int identifier = data.key(); | ||
| 617 | StateHandler& value = data.value(); | ||
| 618 | QScriptValue& function = value.function; | ||
| 619 | auto handleResult = [this, identifier](QScriptValue result) { // called in script thread to get the result back to us. | ||
| 620 | animationStateHandlerResult(identifier, result); | ||
| 621 | }; | ||
| 622 | // invokeMethod makes a copy of the args, and copies of AnimVariantMap do copy the underlying map, so this will correctly capture | ||
| 623 | // the state of _animVars and allow continued changes to _animVars in this thread without conflict. | ||
| 624 | QMetaObject::invokeMethod(function.engine(), "callAnimationStateHandler", Qt::QueuedConnection, | ||
| 625 | Q_ARG(QScriptValue, function), | ||
| 626 | Q_ARG(AnimVariantMap, _animVars), | ||
| 627 | Q_ARG(QStringList, value.propertyNames), | ||
| 628 | Q_ARG(bool, value.useNames), | ||
| 629 | Q_ARG(AnimVariantResultHandler, handleResult)); | ||
| 630 | // It turns out that, for thread-safety reasons, ScriptEngine::callAnimationStateHandler will invoke itself if called from other | ||
| 631 | // than the script thread. Thus the above _could_ be replaced with an ordinary call, which will then trigger the same | ||
| 632 | // invokeMethod as is done explicitly above. However, the script-engine library depends on this animation library, not vice versa. | ||
| 633 | // We could create an AnimVariantCallingMixin class in shared, with an abstract virtual slot | ||
| 634 | // AnimVariantCallingMixin::callAnimationStateHandler (and move AnimVariantMap/AnimVaraintResultHandler to shared), but the | ||
| 635 | // call site here would look like this instead of the above: | ||
| 636 | // dynamic_cast<AnimVariantCallingMixin*>(function.engine())->callAnimationStateHandler(function, ..., handleResult); | ||
| 637 | // This works (I tried it), but the result would be that we would still have same runtime type checks as the invokeMethod above | ||
| 638 | // (occuring within the ScriptEngine::callAnimationStateHandler invokeMethod trampoline), _plus_ another runtime check for the dynamic_cast. | ||
| 639 | |||
| 640 | // gather results in (likely from an earlier update): | ||
| 641 | _animVars.copyVariantsFrom(value.results); // If multiple handlers write the same anim var, the last registgered wins. (_map preserves order). | ||
| 642 | } | ||
| 643 | } | ||
| 644 | |||
| 579 | void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { | 645 | void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { |
| 580 | 646 | ||
| 581 | if (_enableAnimGraph) { | 647 | if (_enableAnimGraph) { |
| @@ -583,6 +649,7 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { · 1 unmodified line | |||
| 583 | return; | 649 | return; |
| 584 | } | 650 | } |
| 585 | 651 | ||
| 652 | updateAnimationStateHandlers(); | ||
| 586 | // evaluate the animation | 653 | // evaluate the animation |
| 587 | AnimNode::Triggers triggersOut; | 654 | AnimNode::Triggers triggersOut; |
| 588 | AnimPoseVec poses = _animNode->evaluate(_animVars, deltaTime, triggersOut); | 655 | AnimPoseVec poses = _animNode->evaluate(_animVars, deltaTime, triggersOut); |
| @@ -37,6 +37,8 @@ · 36 unmodified lines | |||
| 37 | #define __hifi__Rig__ | 37 | #define __hifi__Rig__ |
| 38 | 38 | ||
| 39 | #include <QObject> | 39 | #include <QObject> |
| 40 | #include <QMutex> | ||
| 41 | #include <QScriptValue> | ||
| 40 | 42 | ||
| 41 | #include "JointState.h" // We might want to change this (later) to something that doesn't depend on gpu, fbx and model. -HRS | 43 | #include "JointState.h" // We might want to change this (later) to something that doesn't depend on gpu, fbx and model. -HRS |
| 42 | 44 | ||
| @@ -51,6 +53,12 @@ typedef std::shared_ptr<Rig> RigPointer; · 8 unmodified lines | |||
| 51 | 53 | ||
| 52 | class Rig : public QObject, public std::enable_shared_from_this<Rig> { | 54 | class Rig : public QObject, public std::enable_shared_from_this<Rig> { |
| 53 | public: | 55 | public: |
| 56 | struct StateHandler { | ||
| 57 | AnimVariantMap results; | ||
| 58 | QStringList propertyNames; | ||
| 59 | QScriptValue function; | ||
| 60 | bool useNames; | ||
| 61 | }; | ||
| 54 | 62 | ||
| 55 | struct HeadParameters { | 63 | struct HeadParameters { |
| 56 | float leanSideways = 0.0f; // degrees | 64 | float leanSideways = 0.0f; // degrees |
| @@ -199,10 +207,14 @@ public: · 142 unmodified lines | |||
| 199 | AnimNode::ConstPointer getAnimNode() const { return _animNode; } | 207 | AnimNode::ConstPointer getAnimNode() const { return _animNode; } |
| 200 | AnimSkeleton::ConstPointer getAnimSkeleton() const { return _animSkeleton; } | 208 | AnimSkeleton::ConstPointer getAnimSkeleton() const { return _animSkeleton; } |
| 201 | bool disableHands {false}; // should go away with rig animation (and Rig::inverseKinematics) | 209 | bool disableHands {false}; // should go away with rig animation (and Rig::inverseKinematics) |
| 210 | QScriptValue addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList); | ||
| 211 | void removeAnimationStateHandler(QScriptValue handler); | ||
| 212 | void animationStateHandlerResult(int identifier, QScriptValue result); | ||
| 202 | 213 | ||
| 203 | bool getModelOffset(glm::vec3& modelOffsetOut) const; | 214 | bool getModelOffset(glm::vec3& modelOffsetOut) const; |
| 204 | 215 | ||
| 205 | protected: | 216 | protected: |
| 217 | void updateAnimationStateHandlers(); | ||
| 206 | 218 | ||
| 207 | void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist); | 219 | void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist); |
| 208 | void updateNeckJoint(int index, const HeadParameters& params); | 220 | void updateNeckJoint(int index, const HeadParameters& params); |
| @@ -241,6 +253,11 @@ public: · 32 unmodified lines | |||
| 241 | float _desiredStateAge = 0.0f; | 253 | float _desiredStateAge = 0.0f; |
| 242 | float _leftHandOverlayAlpha = 0.0f; | 254 | float _leftHandOverlayAlpha = 0.0f; |
| 243 | float _rightHandOverlayAlpha = 0.0f; | 255 | float _rightHandOverlayAlpha = 0.0f; |
| 256 | |||
| 257 | private: | ||
| 258 | QMap<int, StateHandler> _stateHandlers; | ||
| 259 | int _nextStateHandlerId {0}; | ||
| 260 | QMutex _stateMutex; | ||
| 244 | }; | 261 | }; |
| 245 | 262 | ||
| 246 | #endif /* defined(__hifi__Rig__) */ | 263 | #endif /* defined(__hifi__Rig__) */ |
| @@ -59,39 +59,30 @@ Sound::Sound(const QUrl& url, bool isStereo) : · 58 unmodified lines | |||
| 59 | void Sound::downloadFinished(const QByteArray& data) { | 59 | void Sound::downloadFinished(const QByteArray& data) { |
| 60 | // replace our byte array with the downloaded data | 60 | // replace our byte array with the downloaded data |
| 61 | QByteArray rawAudioByteArray = QByteArray(data); | 61 | QByteArray rawAudioByteArray = QByteArray(data); |
| 62 | QString fileName = getURL().fileName(); | 62 | QString fileName = getURL().fileName().toLower(); |
| 63 | |||
| 64 | const QString WAV_EXTENSION = ".wav"; | ||
| 65 | 63 | ||
| 64 | static const QString WAV_EXTENSION = ".wav"; | ||
| 65 | static const QString RAW_EXTENSION = ".raw"; | ||
| 66 | if (fileName.endsWith(WAV_EXTENSION)) { | 66 | if (fileName.endsWith(WAV_EXTENSION)) { |
| 67 | 67 | ||
| 68 | QString headerContentType = "audio/x-wav"; | 68 | QByteArray outputAudioByteArray; |
| 69 | //QByteArray headerContentType = reply->rawHeader("Content-Type"); | ||
| 70 | |||
| 71 | // WAV audio file encountered | ||
| 72 | if (headerContentType == "audio/x-wav" | ||
| 73 | || headerContentType == "audio/wav" | ||
| 74 | || headerContentType == "audio/wave" | ||
| 75 | || fileName.endsWith(WAV_EXTENSION)) { | ||
| 76 | |||
| 77 | QByteArray outputAudioByteArray; | ||
| 78 | 69 | ||
| 79 | interpretAsWav(rawAudioByteArray, outputAudioByteArray); | 70 | interpretAsWav(rawAudioByteArray, outputAudioByteArray); |
| 80 | downSample(outputAudioByteArray); | 71 | downSample(outputAudioByteArray); |
| 81 | } else { | 72 | trimFrames(); |
| 82 | // check if this was a stereo raw file | 73 | } else if (fileName.endsWith(RAW_EXTENSION)) { |
| 83 | // since it's raw the only way for us to know that is if the file was called .stereo.raw | 74 | // check if this was a stereo raw file |
| 84 | if (fileName.toLower().endsWith("stereo.raw")) { | 75 | // since it's raw the only way for us to know that is if the file was called .stereo.raw |
| 85 | _isStereo = true; | 76 | if (fileName.toLower().endsWith("stereo.raw")) { |
| 86 | qCDebug(audio) << "Processing sound of" << rawAudioByteArray.size() << "bytes from" << getURL() << "as stereo audio file."; | 77 | _isStereo = true; |
| 87 | } | 78 | qCDebug(audio) << "Processing sound of" << rawAudioByteArray.size() << "bytes from" << getURL() << "as stereo audio file."; |
| 88 | |||
| 89 | // Process as RAW file | ||
| 90 | downSample(rawAudioByteArray); | ||
| 91 | } | 79 | } |
| 80 | |||
| 81 | // Process as RAW file | ||
| 82 | downSample(rawAudioByteArray); | ||
| 92 | trimFrames(); | 83 | trimFrames(); |
| 93 | } else { | 84 | } else { |
| 94 | qCDebug(audio) << "Network reply without 'Content-Type'."; | 85 | qCDebug(audio) << "Unknown sound file type"; |
| 95 | } | 86 | } |
| 96 | 87 | ||
| 97 | _isReady = true; | 88 | _isReady = true; |
| @@ -21,62 +21,43 @@ · 20 unmodified lines | |||
| 21 | HandData::HandData(AvatarData* owningAvatar) : | 21 | HandData::HandData(AvatarData* owningAvatar) : |
| 22 | _owningAvatarData(owningAvatar) | 22 | _owningAvatarData(owningAvatar) |
| 23 | { | 23 | { |
| 24 | // Start with two palms | 24 | addNewPalm(LeftHand); |
| 25 | addNewPalm(); | 25 | addNewPalm(RightHand); |
| 26 | addNewPalm(); | ||
| 27 | } | 26 | } |
| 28 | 27 | ||
| 29 | glm::vec3 HandData::worldToLocalVector(const glm::vec3& worldVector) const { | 28 | glm::vec3 HandData::worldToLocalVector(const glm::vec3& worldVector) const { |
| 30 | return glm::inverse(getBaseOrientation()) * worldVector / getBaseScale(); | 29 | return glm::inverse(getBaseOrientation()) * worldVector / getBaseScale(); |
| 31 | } | 30 | } |
| 32 | 31 | ||
| 33 | PalmData& HandData::addNewPalm() { | 32 | PalmData& HandData::addNewPalm(Hand whichHand) { |
| 34 | _palms.push_back(PalmData(this)); | 33 | QWriteLocker locker(&_palmsLock); |
| 34 | _palms.push_back(PalmData(this, whichHand)); | ||
| 35 | return _palms.back(); | 35 | return _palms.back(); |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | const PalmData* HandData::getPalm(int sixSenseID) const { | 38 | PalmData HandData::getCopyOfPalmData(Hand hand) const { |
| 39 | // the palms are not necessarily added in left-right order, | 39 | QReadLocker locker(&_palmsLock); |
| 40 | // so we have to search for the right SixSenseID | ||
| 41 | for (unsigned int i = 0; i < _palms.size(); i++) { | ||
| 42 | const PalmData* palm = &(_palms[i]); | ||
| 43 | if (palm->getSixenseID() == sixSenseID) { | ||
| 44 | return palm->isActive() ? palm : NULL; | ||
| 45 | } | ||
| 46 | } | ||
| 47 | return NULL; | ||
| 48 | } | ||
| 49 | 40 | ||
| 50 | void HandData::getLeftRightPalmIndices(int& leftPalmIndex, int& rightPalmIndex) const { | 41 | // the palms are not necessarily added in left-right order, |
| 51 | leftPalmIndex = -1; | 42 | // so we have to search for the correct hand |
| 52 | rightPalmIndex = -1; | 43 | for (const auto& palm : _palms) { |
| 53 | for (size_t i = 0; i < _palms.size(); i++) { | 44 | if (palm.whichHand() == hand && palm.isActive()) { |
| 54 | const PalmData& palm = _palms[i]; | 45 | return palm; |
| 55 | if (palm.isActive()) { | ||
| 56 | if (palm.getSixenseID() == LEFT_HAND_INDEX) { | ||
| 57 | leftPalmIndex = i; | ||
| 58 | } | ||
| 59 | if (palm.getSixenseID() == RIGHT_HAND_INDEX) { | ||
| 60 | rightPalmIndex = i; | ||
| 61 | } | ||
| 62 | } | 46 | } |
| 63 | } | 47 | } |
| 48 | return PalmData(); // invalid hand | ||
| 64 | } | 49 | } |
| 65 | 50 | ||
| 66 | PalmData::PalmData(HandData* owningHandData) : | 51 | PalmData::PalmData(HandData* owningHandData, HandData::Hand hand) : |
| 67 | _rawRotation(0.0f, 0.0f, 0.0f, 1.0f), | 52 | _rawRotation(0.0f, 0.0f, 0.0f, 1.0f), |
| 68 | _rawPosition(0.0f), | 53 | _rawPosition(0.0f), |
| 69 | _rawVelocity(0.0f), | 54 | _rawVelocity(0.0f), |
| 70 | _rawAngularVelocity(0.0f), | 55 | _rawAngularVelocity(0.0f), |
| 71 | _totalPenetration(0.0f), | 56 | _totalPenetration(0.0f), |
| 72 | _controllerButtons(0), | ||
| 73 | _isActive(false), | 57 | _isActive(false), |
| 74 | _sixenseID(SIXENSEID_INVALID), | ||
| 75 | _numFramesWithoutData(0), | 58 | _numFramesWithoutData(0), |
| 76 | _owningHandData(owningHandData), | 59 | _owningHandData(owningHandData), |
| 77 | _isCollidingWithVoxel(false), | 60 | _hand(hand) { |
| 78 | _isCollidingWithPalm(false), | ||
| 79 | _collisionlessPaddleExpiry(0) { | ||
| 80 | } | 61 | } |
| 81 | 62 | ||
| 82 | void PalmData::addToPosition(const glm::vec3& delta) { | 63 | void PalmData::addToPosition(const glm::vec3& delta) { |
| @@ -85,9 +66,9 @@ void PalmData::addToPosition(const glm::vec3& delta) { · 2 unmodified lines | |||
| 85 | 66 | ||
| 86 | bool HandData::findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration, | 67 | bool HandData::findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration, |
| 87 | const PalmData*& collidingPalm) const { | 68 | const PalmData*& collidingPalm) const { |
| 88 | 69 | QReadLocker locker(&_palmsLock); | |
| 89 | for (size_t i = 0; i < _palms.size(); ++i) { | 70 | |
| 90 | const PalmData& palm = _palms[i]; | 71 | for (const auto& palm : _palms) { |
| 91 | if (!palm.isActive()) { | 72 | if (!palm.isActive()) { |
| 92 | continue; | 73 | continue; |
| 93 | } | 74 | } |
| @@ -12,26 +12,30 @@ · 11 unmodified lines | |||
| 12 | #ifndef hifi_HandData_h | 12 | #ifndef hifi_HandData_h |
| 13 | #define hifi_HandData_h | 13 | #define hifi_HandData_h |
| 14 | 14 | ||
| 15 | #include <functional> | ||
| 15 | #include <iostream> | 16 | #include <iostream> |
| 16 | #include <vector> | 17 | #include <vector> |
| 17 | 18 | ||
| 18 | #include <glm/glm.hpp> | 19 | #include <glm/glm.hpp> |
| 19 | #include <glm/gtc/quaternion.hpp> | 20 | #include <glm/gtc/quaternion.hpp> |
| 20 | 21 | ||
| 22 | #include <QReadWriteLock> | ||
| 23 | |||
| 21 | #include <NumericalConstants.h> | 24 | #include <NumericalConstants.h> |
| 22 | #include <SharedUtil.h> | 25 | #include <SharedUtil.h> |
| 23 | 26 | ||
| 24 | class AvatarData; | 27 | class AvatarData; |
| 25 | class PalmData; | 28 | class PalmData; |
| 26 | 29 | ||
| 27 | const int LEFT_HAND_INDEX = 0; | ||
| 28 | const int RIGHT_HAND_INDEX = 1; | ||
| 29 | const int NUM_HANDS = 2; | ||
| 30 | |||
| 31 | const int SIXENSEID_INVALID = -1; | ||
| 32 | |||
| 33 | class HandData { | 30 | class HandData { |
| 34 | public: | 31 | public: |
| 32 | enum Hand { | ||
| 33 | LeftHand, | ||
| 34 | RightHand, | ||
| 35 | UnknownHand, | ||
| 36 | NUMBER_OF_HANDS | ||
| 37 | }; | ||
| 38 | |||
| 35 | HandData(AvatarData* owningAvatar); | 39 | HandData(AvatarData* owningAvatar); |
| 36 | virtual ~HandData() {} | 40 | virtual ~HandData() {} |
| 37 | 41 | ||
| @@ -46,15 +50,9 @@ public: · 8 unmodified lines | |||
| 46 | 50 | ||
| 47 | glm::vec3 worldToLocalVector(const glm::vec3& worldVector) const; | 51 | glm::vec3 worldToLocalVector(const glm::vec3& worldVector) const; |
| 48 | 52 | ||
| 49 | std::vector<PalmData>& getPalms() { return _palms; } | 53 | PalmData getCopyOfPalmData(Hand hand) const; |
| 50 | const std::vector<PalmData>& getPalms() const { return _palms; } | ||
| 51 | const PalmData* getPalm(int sixSenseID) const; | ||
| 52 | size_t getNumPalms() const { return _palms.size(); } | ||
| 53 | PalmData& addNewPalm(); | ||
| 54 | 54 | ||
| 55 | /// Finds the indices of the left and right palms according to their locations, or -1 if either or | 55 | std::vector<PalmData> getCopyOfPalms() const { QReadLocker locker(&_palmsLock); return _palms; } |
| 56 | /// both is not found. | ||
| 57 | void getLeftRightPalmIndices(int& leftPalmIndex, int& rightPalmIndex) const; | ||
| 58 | 56 | ||
| 59 | /// Checks for penetration between the described sphere and the hand. | 57 | /// Checks for penetration between the described sphere and the hand. |
| 60 | /// \param penetratorCenter the center of the penetration test sphere | 58 | /// \param penetratorCenter the center of the penetration test sphere |
| @@ -67,14 +65,22 @@ public: · 6 unmodified lines | |||
| 67 | 65 | ||
| 68 | glm::quat getBaseOrientation() const; | 66 | glm::quat getBaseOrientation() const; |
| 69 | 67 | ||
| 68 | /// Allows a lamda function write access to the specific palm for this Hand, this might | ||
| 69 | /// modify the _palms vector | ||
| 70 | template<typename PalmModifierFunction> void modifyPalm(Hand whichHand, PalmModifierFunction callback); | ||
| 71 | |||
| 70 | friend class AvatarData; | 72 | friend class AvatarData; |
| 71 | protected: | 73 | protected: |
| 72 | AvatarData* _owningAvatarData; | 74 | AvatarData* _owningAvatarData; |
| 73 | std::vector<PalmData> _palms; | 75 | std::vector<PalmData> _palms; |
| 76 | mutable QReadWriteLock _palmsLock{ QReadWriteLock::Recursive }; | ||
| 74 | 77 | ||
| 75 | glm::vec3 getBasePosition() const; | 78 | glm::vec3 getBasePosition() const; |
| 76 | float getBaseScale() const; | 79 | float getBaseScale() const; |
| 77 | 80 | ||
| 81 | PalmData& addNewPalm(Hand whichHand); | ||
| 82 | PalmData& getPalmData(Hand hand); | ||
| 83 | |||
| 78 | private: | 84 | private: |
| 79 | // privatize copy ctor and assignment operator so copies of this object cannot be made | 85 | // privatize copy ctor and assignment operator so copies of this object cannot be made |
| 80 | HandData(const HandData&); | 86 | HandData(const HandData&); |
| @@ -84,25 +90,30 @@ private: · 3 unmodified lines | |||
| 84 | 90 | ||
| 85 | class PalmData { | 91 | class PalmData { |
| 86 | public: | 92 | public: |
| 87 | PalmData(HandData* owningHandData); | 93 | PalmData(HandData* owningHandData = nullptr, HandData::Hand hand = HandData::UnknownHand); |
| 88 | glm::vec3 getPosition() const { return _owningHandData->localToWorldPosition(_rawPosition); } | 94 | glm::vec3 getPosition() const { return _owningHandData->localToWorldPosition(_rawPosition); } |
| 89 | glm::vec3 getVelocity() const { return _owningHandData->localToWorldDirection(_rawVelocity); } | 95 | glm::vec3 getVelocity() const { return _owningHandData->localToWorldDirection(_rawVelocity); } |
| 90 | 96 | ||
| 91 | const glm::vec3& getRawPosition() const { return _rawPosition; } | 97 | const glm::vec3& getRawPosition() const { return _rawPosition; } |
| 92 | bool isActive() const { return _isActive; } | 98 | bool isActive() const { return _isActive; } |
| 93 | int getSixenseID() const { return _sixenseID; } | 99 | bool isValid() const { return _owningHandData; } |
| 94 | 100 | ||
| 95 | void setActive(bool active) { _isActive = active; } | 101 | void setActive(bool active) { _isActive = active; } |
| 96 | void setSixenseID(int id) { _sixenseID = id; } | ||
| 97 | 102 | ||
| 98 | void setRawRotation(const glm::quat rawRotation) { _rawRotation = rawRotation; }; | 103 | HandData::Hand whichHand() const { return _hand; } |
| 104 | void setHand(HandData::Hand hand) { _hand = hand; } | ||
| 105 | |||
| 106 | void setRawRotation(const glm::quat& rawRotation) { _rawRotation = rawRotation; }; | ||
| 99 | glm::quat getRawRotation() const { return _rawRotation; } | 107 | glm::quat getRawRotation() const { return _rawRotation; } |
| 100 | glm::quat getRotation() const { return _owningHandData->getBaseOrientation() * _rawRotation; } | 108 | glm::quat getRotation() const { return _owningHandData->getBaseOrientation() * _rawRotation; } |
| 101 | void setRawPosition(const glm::vec3& pos) { _rawPosition = pos; } | 109 | void setRawPosition(const glm::vec3& pos) { _rawPosition = pos; } |
| 102 | void setRawVelocity(const glm::vec3& velocity) { _rawVelocity = velocity; } | 110 | void setRawVelocity(const glm::vec3& velocity) { _rawVelocity = velocity; } |
| 103 | const glm::vec3& getRawVelocity() const { return _rawVelocity; } | 111 | const glm::vec3& getRawVelocity() const { return _rawVelocity; } |
| 112 | |||
| 104 | void setRawAngularVelocity(const glm::vec3& angularVelocity) { _rawAngularVelocity = angularVelocity; } | 113 | void setRawAngularVelocity(const glm::vec3& angularVelocity) { _rawAngularVelocity = angularVelocity; } |
| 105 | const glm::vec3& getRawAngularVelocity() const { return _rawAngularVelocity; } | 114 | const glm::vec3& getRawAngularVelocity() const { return _rawAngularVelocity; } |
| 115 | glm::quat getRawAngularVelocityAsQuat() const { return glm::quat(_rawAngularVelocity); } | ||
| 116 | |||
| 106 | void addToPosition(const glm::vec3& delta); | 117 | void addToPosition(const glm::vec3& delta); |
| 107 | 118 | ||
| 108 | void addToPenetration(const glm::vec3& penetration) { _totalPenetration += penetration; } | 119 | void addToPenetration(const glm::vec3& penetration) { _totalPenetration += penetration; } |
| @@ -120,31 +131,11 @@ public: · 11 unmodified lines | |||
| 120 | void resetFramesWithoutData() { _numFramesWithoutData = 0; } | 131 | void resetFramesWithoutData() { _numFramesWithoutData = 0; } |
| 121 | int getFramesWithoutData() const { return _numFramesWithoutData; } | 132 | int getFramesWithoutData() const { return _numFramesWithoutData; } |
| 122 | 133 | ||
| 123 | // Controller buttons | 134 | // FIXME - these are used in SkeletonModel::updateRig() the skeleton/rig should probably get this information |
| 124 | void setControllerButtons(unsigned int controllerButtons) { _controllerButtons = controllerButtons; } | 135 | // from an action and/or the UserInputMapper instead of piping it through here. |
| 125 | void setLastControllerButtons(unsigned int controllerButtons) { _lastControllerButtons = controllerButtons; } | ||
| 126 | |||
| 127 | unsigned int getControllerButtons() const { return _controllerButtons; } | ||
| 128 | unsigned int getLastControllerButtons() const { return _lastControllerButtons; } | ||
| 129 | |||
| 130 | void setTrigger(float trigger) { _trigger = trigger; } | 136 | void setTrigger(float trigger) { _trigger = trigger; } |
| 131 | float getTrigger() const { return _trigger; } | 137 | float getTrigger() const { return _trigger; } |
| 132 | void setJoystick(float joystickX, float joystickY) { _joystickX = joystickX; _joystickY = joystickY; } | ||
| 133 | float getJoystickX() const { return _joystickX; } | ||
| 134 | float getJoystickY() const { return _joystickY; } | ||
| 135 | 138 | ||
| 136 | bool getIsCollidingWithVoxel() const { return _isCollidingWithVoxel; } | ||
| 137 | void setIsCollidingWithVoxel(bool isCollidingWithVoxel) { _isCollidingWithVoxel = isCollidingWithVoxel; } | ||
| 138 | |||
| 139 | bool getIsCollidingWithPalm() const { return _isCollidingWithPalm; } | ||
| 140 | void setIsCollidingWithPalm(bool isCollidingWithPalm) { _isCollidingWithPalm = isCollidingWithPalm; } | ||
| 141 | |||
| 142 | bool hasPaddle() const { return _collisionlessPaddleExpiry < usecTimestampNow(); } | ||
| 143 | void updateCollisionlessPaddleExpiry() { _collisionlessPaddleExpiry = usecTimestampNow() + USECS_PER_SECOND; } | ||
| 144 | |||
| 145 | /// Store position where the palm holds the ball. | ||
| 146 | void getBallHoldPosition(glm::vec3& position) const; | ||
| 147 | |||
| 148 | // return world-frame: | 139 | // return world-frame: |
| 149 | glm::vec3 getFingerDirection() const; | 140 | glm::vec3 getFingerDirection() const; |
| 150 | glm::vec3 getNormal() const; | 141 | glm::vec3 getNormal() const; |
| @@ -155,25 +146,29 @@ private: · 4 unmodified lines | |||
| 155 | glm::vec3 _rawPosition; | 146 | glm::vec3 _rawPosition; |
| 156 | glm::vec3 _rawVelocity; | 147 | glm::vec3 _rawVelocity; |
| 157 | glm::vec3 _rawAngularVelocity; | 148 | glm::vec3 _rawAngularVelocity; |
| 149 | glm::quat _rawDeltaRotation; | ||
| 158 | glm::quat _lastRotation; | 150 | glm::quat _lastRotation; |
| 159 | 151 | ||
| 160 | glm::vec3 _tipPosition; | 152 | glm::vec3 _tipPosition; |
| 161 | glm::vec3 _tipVelocity; | 153 | glm::vec3 _tipVelocity; |
| 162 | glm::vec3 _totalPenetration; // accumulator for per-frame penetrations | 154 | glm::vec3 _totalPenetration; /// accumulator for per-frame penetrations |
| 163 | 155 | ||
| 164 | unsigned int _controllerButtons; | ||
| 165 | unsigned int _lastControllerButtons; | ||
| 166 | float _trigger; | 156 | float _trigger; |
| 167 | float _joystickX, _joystickY; | ||
| 168 | 157 | ||
| 169 | bool _isActive; // This has current valid data | 158 | bool _isActive; /// This has current valid data |
| 170 | int _sixenseID; // Sixense controller ID for this palm | 159 | int _numFramesWithoutData; /// after too many frames without data, this tracked object assumed lost. |
| 171 | int _numFramesWithoutData; // after too many frames without data, this tracked object assumed lost. | ||
| 172 | HandData* _owningHandData; | 160 | HandData* _owningHandData; |
| 173 | 161 | HandData::Hand _hand; | |
| 174 | bool _isCollidingWithVoxel; /// Whether the finger of this palm is inside a leaf voxel | ||
| 175 | bool _isCollidingWithPalm; | ||
| 176 | quint64 _collisionlessPaddleExpiry; /// Timestamp after which paddle starts colliding | ||
| 177 | }; | 162 | }; |
| 178 | 163 | ||
| 164 | template<typename PalmModifierFunction> void HandData::modifyPalm(Hand whichHand, PalmModifierFunction callback) { | ||
| 165 | QReadLocker locker(&_palmsLock); | ||
| 166 | for (auto& palm : _palms) { | ||
| 167 | if (palm.whichHand() == whichHand && palm.isValid()) { | ||
| 168 | callback(palm); | ||
| 169 | return; | ||
| 170 | } | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 179 | #endif // hifi_HandData_h | 174 | #endif // hifi_HandData_h |
| @@ -0,0 +1,14 @@ | |||
| 1 | set(TARGET_NAME controllers) | ||
| 2 | |||
| 3 | # set a default root dir for each of our optional externals if it was not passed | ||
| 4 | setup_hifi_library(Script) | ||
| 5 | |||
| 6 | # use setup_hifi_library macro to setup our project and link appropriate Qt modules | ||
| 7 | link_hifi_libraries(shared) | ||
| 8 | |||
| 9 | GroupSources("src/controllers") | ||
| 10 | |||
| 11 | add_dependency_external_projects(glm) | ||
| 12 | find_package(GLM REQUIRED) | ||
| 13 | target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) | ||
| 14 | |||
| @@ -0,0 +1,120 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis on 2015/10/19 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #include "Actions.h" | ||
| 10 | |||
| 11 | #include "impl/endpoints/ActionEndpoint.h" | ||
| 12 | |||
| 13 | namespace controller { | ||
| 14 | |||
| 15 | Input::NamedPair makePair(ChannelType type, Action action, const QString& name) { | ||
| 16 | auto input = Input(UserInputMapper::ACTIONS_DEVICE, toInt(action), type); | ||
| 17 | return Input::NamedPair(input, name); | ||
| 18 | } | ||
| 19 | |||
| 20 | Input::NamedPair makeAxisPair(Action action, const QString& name) { | ||
| 21 | return makePair(ChannelType::AXIS, action, name); | ||
| 22 | } | ||
| 23 | |||
| 24 | Input::NamedPair makeButtonPair(Action action, const QString& name) { | ||
| 25 | return makePair(ChannelType::BUTTON, action, name); | ||
| 26 | } | ||
| 27 | |||
| 28 | Input::NamedPair makePosePair(Action action, const QString& name) { | ||
| 29 | return makePair(ChannelType::POSE, action, name); | ||
| 30 | } | ||
| 31 | |||
| 32 | EndpointPointer ActionsDevice::createEndpoint(const Input& input) const { | ||
| 33 | return std::make_shared<ActionEndpoint>(input); | ||
| 34 | } | ||
| 35 | |||
| 36 | // Device functions | ||
| 37 | Input::NamedVector ActionsDevice::getAvailableInputs() const { | ||
| 38 | static Input::NamedVector availableInputs { | ||
| 39 | makeAxisPair(Action::TRANSLATE_X, "TranslateX"), | ||
| 40 | makeAxisPair(Action::TRANSLATE_Y, "TranslateY"), | ||
| 41 | makeAxisPair(Action::TRANSLATE_Z, "TranslateZ"), | ||
| 42 | makeAxisPair(Action::ROLL, "Roll"), | ||
| 43 | makeAxisPair(Action::PITCH, "Pitch"), | ||
| 44 | makeAxisPair(Action::YAW, "Yaw"), | ||
| 45 | makeAxisPair(Action::STEP_YAW, "StepYaw"), | ||
| 46 | makeAxisPair(Action::STEP_PITCH, "StepPitch"), | ||
| 47 | makeAxisPair(Action::STEP_ROLL, "StepRoll"), | ||
| 48 | makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateX"), | ||
| 49 | makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateY"), | ||
| 50 | makeAxisPair(Action::STEP_TRANSLATE_X, "StepTranslateZ"), | ||
| 51 | |||
| 52 | makePosePair(Action::LEFT_HAND, "LeftHand"), | ||
| 53 | makePosePair(Action::RIGHT_HAND, "RightHand"), | ||
| 54 | |||
| 55 | makeButtonPair(Action::LEFT_HAND_CLICK, "LeftHandClick"), | ||
| 56 | makeButtonPair(Action::RIGHT_HAND_CLICK, "RightHandClick"), | ||
| 57 | |||
| 58 | makeButtonPair(Action::SHIFT, "Shift"), | ||
| 59 | makeButtonPair(Action::ACTION1, "PrimaryAction"), | ||
| 60 | makeButtonPair(Action::ACTION2, "SecondaryAction"), | ||
| 61 | makeButtonPair(Action::CONTEXT_MENU, "ContextMenu"), | ||
| 62 | makeButtonPair(Action::TOGGLE_MUTE, "ToggleMute"), | ||
| 63 | |||
| 64 | // Aliases and bisected versions | ||
| 65 | makeAxisPair(Action::LONGITUDINAL_BACKWARD, "Backward"), | ||
| 66 | makeAxisPair(Action::LONGITUDINAL_FORWARD, "Forward"), | ||
| 67 | makeAxisPair(Action::LATERAL_LEFT, "StrafeLeft"), | ||
| 68 | makeAxisPair(Action::LATERAL_RIGHT, "StrafeRight"), | ||
| 69 | makeAxisPair(Action::VERTICAL_DOWN, "Down"), | ||
| 70 | makeAxisPair(Action::VERTICAL_UP, "Up"), | ||
| 71 | makeAxisPair(Action::YAW_LEFT, "YawLeft"), | ||
| 72 | makeAxisPair(Action::YAW_RIGHT, "YawRight"), | ||
| 73 | makeAxisPair(Action::PITCH_DOWN, "PitchDown"), | ||
| 74 | makeAxisPair(Action::PITCH_UP, "PitchUp"), | ||
| 75 | makeAxisPair(Action::BOOM_IN, "BoomIn"), | ||
| 76 | makeAxisPair(Action::BOOM_OUT, "BoomOut"), | ||
| 77 | |||
| 78 | // Deprecated aliases | ||
| 79 | // FIXME remove after we port all scripts | ||
| 80 | makeAxisPair(Action::LONGITUDINAL_BACKWARD, "LONGITUDINAL_BACKWARD"), | ||
| 81 | makeAxisPair(Action::LONGITUDINAL_FORWARD, "LONGITUDINAL_FORWARD"), | ||
| 82 | makeAxisPair(Action::LATERAL_LEFT, "LATERAL_LEFT"), | ||
| 83 | makeAxisPair(Action::LATERAL_RIGHT, "LATERAL_RIGHT"), | ||
| 84 | makeAxisPair(Action::VERTICAL_DOWN, "VERTICAL_DOWN"), | ||
| 85 | makeAxisPair(Action::VERTICAL_UP, "VERTICAL_UP"), | ||
| 86 | makeAxisPair(Action::YAW_LEFT, "YAW_LEFT"), | ||
| 87 | makeAxisPair(Action::YAW_RIGHT, "YAW_RIGHT"), | ||
| 88 | makeAxisPair(Action::PITCH_DOWN, "PITCH_DOWN"), | ||
| 89 | makeAxisPair(Action::PITCH_UP, "PITCH_UP"), | ||
| 90 | makeAxisPair(Action::BOOM_IN, "BOOM_IN"), | ||
| 91 | makeAxisPair(Action::BOOM_OUT, "BOOM_OUT"), | ||
| 92 | |||
| 93 | makePosePair(Action::LEFT_HAND, "LEFT_HAND"), | ||
| 94 | makePosePair(Action::RIGHT_HAND, "RIGHT_HAND"), | ||
| 95 | |||
| 96 | makeButtonPair(Action::LEFT_HAND_CLICK, "LEFT_HAND_CLICK"), | ||
| 97 | makeButtonPair(Action::RIGHT_HAND_CLICK, "RIGHT_HAND_CLICK"), | ||
| 98 | |||
| 99 | makeButtonPair(Action::SHIFT, "SHIFT"), | ||
| 100 | makeButtonPair(Action::ACTION1, "ACTION1"), | ||
| 101 | makeButtonPair(Action::ACTION2, "ACTION2"), | ||
| 102 | makeButtonPair(Action::CONTEXT_MENU, "CONTEXT_MENU"), | ||
| 103 | makeButtonPair(Action::TOGGLE_MUTE, "TOGGLE_MUTE"), | ||
| 104 | }; | ||
| 105 | return availableInputs; | ||
| 106 | } | ||
| 107 | |||
| 108 | void ActionsDevice::update(float deltaTime, bool jointsCaptured) { | ||
| 109 | } | ||
| 110 | |||
| 111 | void ActionsDevice::focusOutEvent() { | ||
| 112 | } | ||
| 113 | |||
| 114 | ActionsDevice::ActionsDevice() : InputDevice("Actions") { | ||
| 115 | _deviceID = UserInputMapper::ACTIONS_DEVICE; | ||
| 116 | } | ||
| 117 | |||
| 118 | ActionsDevice::~ActionsDevice() {} | ||
| 119 | |||
| 120 | } | ||
| @@ -0,0 +1,103 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis on 2015/10/19 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | #ifndef hifi_controller_Actions_h | ||
| 11 | #define hifi_controller_Actions_h | ||
| 12 | |||
| 13 | #include <QtCore/QObject> | ||
| 14 | #include <QtCore/QVector> | ||
| 15 | |||
| 16 | #include "InputDevice.h" | ||
| 17 | |||
| 18 | namespace controller { | ||
| 19 | |||
| 20 | // Actions are the output channels of the Mapper, that's what the InputChannel map to | ||
| 21 | // For now the Actions are hardcoded, this is bad, but we will fix that in the near future | ||
| 22 | enum class Action { | ||
| 23 | TRANSLATE_X = 0, | ||
| 24 | TRANSLATE_Y, | ||
| 25 | TRANSLATE_Z, | ||
| 26 | ROTATE_X, PITCH = ROTATE_X, | ||
| 27 | ROTATE_Y, YAW = ROTATE_Y, | ||
| 28 | ROTATE_Z, ROLL = ROTATE_Z, | ||
| 29 | |||
| 30 | STEP_YAW, | ||
| 31 | // FIXME does this have a use case? | ||
| 32 | STEP_PITCH, | ||
| 33 | // FIXME does this have a use case? | ||
| 34 | STEP_ROLL, | ||
| 35 | |||
| 36 | STEP_TRANSLATE_X, | ||
| 37 | STEP_TRANSLATE_Y, | ||
| 38 | STEP_TRANSLATE_Z, | ||
| 39 | |||
| 40 | TRANSLATE_CAMERA_Z, | ||
| 41 | NUM_COMBINED_AXES, | ||
| 42 | |||
| 43 | LEFT_HAND = NUM_COMBINED_AXES, | ||
| 44 | RIGHT_HAND, | ||
| 45 | |||
| 46 | LEFT_HAND_CLICK, | ||
| 47 | RIGHT_HAND_CLICK, | ||
| 48 | |||
| 49 | ACTION1, | ||
| 50 | ACTION2, | ||
| 51 | |||
| 52 | CONTEXT_MENU, | ||
| 53 | TOGGLE_MUTE, | ||
| 54 | |||
| 55 | SHIFT, | ||
| 56 | |||
| 57 | // Biseced aliases for TRANSLATE_Z | ||
| 58 | LONGITUDINAL_BACKWARD, | ||
| 59 | LONGITUDINAL_FORWARD, | ||
| 60 | |||
| 61 | // Biseced aliases for TRANSLATE_X | ||
| 62 | LATERAL_LEFT, | ||
| 63 | LATERAL_RIGHT, | ||
| 64 | |||
| 65 | // Biseced aliases for TRANSLATE_Y | ||
| 66 | VERTICAL_DOWN, | ||
| 67 | VERTICAL_UP, | ||
| 68 | |||
| 69 | // Biseced aliases for ROTATE_Y | ||
| 70 | YAW_LEFT, | ||
| 71 | YAW_RIGHT, | ||
| 72 | |||
| 73 | // Biseced aliases for ROTATE_X | ||
| 74 | PITCH_DOWN, | ||
| 75 | PITCH_UP, | ||
| 76 | |||
| 77 | // Biseced aliases for TRANSLATE_CAMERA_Z | ||
| 78 | BOOM_IN, | ||
| 79 | BOOM_OUT, | ||
| 80 | |||
| 81 | NUM_ACTIONS, | ||
| 82 | }; | ||
| 83 | |||
| 84 | template <typename T> | ||
| 85 | int toInt(T enumValue) { return static_cast<int>(enumValue); } | ||
| 86 | |||
| 87 | class ActionsDevice : public QObject, public InputDevice { | ||
| 88 | Q_OBJECT | ||
| 89 | Q_PROPERTY(QString name READ getName) | ||
| 90 | |||
| 91 | public: | ||
| 92 | virtual EndpointPointer createEndpoint(const Input& input) const override; | ||
| 93 | virtual Input::NamedVector getAvailableInputs() const override; | ||
| 94 | virtual void update(float deltaTime, bool jointsCaptured) override; | ||
| 95 | virtual void focusOutEvent() override; | ||
| 96 | |||
| 97 | ActionsDevice(); | ||
| 98 | virtual ~ActionsDevice(); | ||
| 99 | }; | ||
| 100 | |||
| 101 | } | ||
| 102 | |||
| 103 | #endif // hifi_StandardController_h | ||
| @@ -0,0 +1,14 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis on 2015/10/18 | ||
| 3 | // (based on UserInputMapper inner class created by Sam Gateau on 4/27/15) | ||
| 4 | // Copyright 2015 High Fidelity, Inc. | ||
| 5 | // | ||
| 6 | // Distributed under the Apache License, Version 2.0. | ||
| 7 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 8 | // | ||
| 9 | |||
| 10 | #include "DeviceProxy.h" | ||
| 11 | |||
| 12 | namespace controller { | ||
| 13 | } | ||
| 14 | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis on 2015/10/18 | ||
| 3 | // (based on UserInputMapper inner class created by Sam Gateau on 4/27/15) | ||
| 4 | // Copyright 2015 High Fidelity, Inc. | ||
| 5 | // | ||
| 6 | // Distributed under the Apache License, Version 2.0. | ||
| 7 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 8 | // | ||
| 9 | |||
| 10 | #pragma once | ||
| 11 | #ifndef hifi_controllers_DeviceProxy_h | ||
| 12 | #define hifi_controllers_DeviceProxy_h | ||
| 13 | |||
| 14 | #include <functional> | ||
| 15 | |||
| 16 | #include <QtCore/QString> | ||
| 17 | #include <QtCore/QVector> | ||
| 18 | |||
| 19 | #include "Input.h" | ||
| 20 | #include "Pose.h" | ||
| 21 | |||
| 22 | namespace controller { | ||
| 23 | /* | ||
| 24 | using Modifiers = std::vector<Input>; | ||
| 25 | typedef QPair<Input, QString> InputPair; | ||
| 26 | class Endpoint; | ||
| 27 | using EndpointPtr = std::shared_ptr<Endpoint>; | ||
| 28 | |||
| 29 | template<typename T> | ||
| 30 | using InputGetter = std::function<T(const Input& input, int timestamp)>; | ||
| 31 | using ButtonGetter = InputGetter<bool>; | ||
| 32 | using AxisGetter = InputGetter<float>; | ||
| 33 | using PoseGetter = InputGetter<Pose>; | ||
| 34 | using ResetBindings = std::function<bool()>; | ||
| 35 | using AvailableInputGetter = std::function<Input::NamedVector()>; | ||
| 36 | using EndpointCreator = std::function<EndpointPtr(const Input&)>; | ||
| 37 | |||
| 38 | class DeviceProxy { | ||
| 39 | public: | ||
| 40 | using Pointer = std::shared_ptr<DeviceProxy>; | ||
| 41 | |||
| 42 | QString _name; | ||
| 43 | }; | ||
| 44 | */ | ||
| 45 | } | ||
| 46 | |||
| 47 | #endif | ||
| @@ -0,0 +1,37 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/20 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | #ifndef hifi_Controllers_Forward_h | ||
| 11 | #define hifi_Controllers_Forward_h | ||
| 12 | |||
| 13 | namespace controller { | ||
| 14 | |||
| 15 | class Endpoint; | ||
| 16 | using EndpointPointer = std::shared_ptr<Endpoint>; | ||
| 17 | using EndpointList = std::list<EndpointPointer>; | ||
| 18 | |||
| 19 | class Filter; | ||
| 20 | using FilterPointer = std::shared_ptr<Filter>; | ||
| 21 | using FilterList = std::list<FilterPointer>; | ||
| 22 | |||
| 23 | class Route; | ||
| 24 | using RoutePointer = std::shared_ptr<Route>; | ||
| 25 | using RouteList = std::list<RoutePointer>; | ||
| 26 | |||
| 27 | class Conditional; | ||
| 28 | using ConditionalPointer = std::shared_ptr<Conditional>; | ||
| 29 | using ConditionalList = std::list<ConditionalPointer>; | ||
| 30 | |||
| 31 | class Mapping; | ||
| 32 | using MappingPointer = std::shared_ptr<Mapping>; | ||
| 33 | using MappingList = std::list<MappingPointer>; | ||
| 34 | |||
| 35 | } | ||
| 36 | |||
| 37 | #endif | ||
| @@ -0,0 +1,17 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis on 2015/10/18 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #include "Input.h" | ||
| 10 | |||
| 11 | namespace controller { | ||
| 12 | const Input Input::INVALID_INPUT = Input(0x7fffffff); | ||
| 13 | const uint16_t Input::INVALID_DEVICE = Input::INVALID_INPUT.device; | ||
| 14 | const uint16_t Input::INVALID_CHANNEL = Input::INVALID_INPUT.channel; | ||
| 15 | const uint16_t Input::INVALID_TYPE = Input::INVALID_INPUT.type; | ||
| 16 | } | ||
| 17 | |||
| @@ -0,0 +1,74 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis on 2015/10/18 | ||
| 3 | // (based on UserInputMapper inner class created by Sam Gateau on 4/27/15) | ||
| 4 | // Copyright 2015 High Fidelity, Inc. | ||
| 5 | // | ||
| 6 | // Distributed under the Apache License, Version 2.0. | ||
| 7 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 8 | // | ||
| 9 | |||
| 10 | #pragma once | ||
| 11 | #ifndef hifi_controllers_Input_h | ||
| 12 | #define hifi_controllers_Input_h | ||
| 13 | |||
| 14 | #include <GLMHelpers.h> | ||
| 15 | |||
| 16 | namespace controller { | ||
| 17 | |||
| 18 | enum class ChannelType { | ||
| 19 | UNKNOWN = 0, | ||
| 20 | BUTTON, | ||
| 21 | AXIS, | ||
| 22 | POSE, | ||
| 23 | RUMBLE, | ||
| 24 | INVALID = 0x7 | ||
| 25 | }; | ||
| 26 | |||
| 27 | // Input is the unique identifier to find a n input channel of a particular device | ||
| 28 | // Devices are responsible for registering to the UseInputMapper so their input channels can be sued and mapped | ||
| 29 | // to the Action channels | ||
| 30 | struct Input { | ||
| 31 | union { | ||
| 32 | uint32_t id{ 0 }; // by default Input is 0 meaning invalid | ||
| 33 | struct { | ||
| 34 | uint16_t device; // Up to 64K possible devices | ||
| 35 | uint16_t channel : 12 ; // 2^12 possible channel per Device | ||
| 36 | uint16_t type : 3; // 2 bits to store the Type directly in the ID | ||
| 37 | uint16_t padding : 1; // 2 bits to store the Type directly in the ID | ||
| 38 | }; | ||
| 39 | }; | ||
| 40 | |||
| 41 | bool isValid() const { return (id != INVALID_INPUT.id); } | ||
| 42 | |||
| 43 | uint16_t getDevice() const { return device; } | ||
| 44 | uint16_t getChannel() const { return channel; } | ||
| 45 | uint32_t getID() const { return id; } | ||
| 46 | ChannelType getType() const { return (ChannelType) type; } | ||
| 47 | |||
| 48 | bool isButton() const { return getType() == ChannelType::BUTTON; } | ||
| 49 | bool isAxis() const { return getType() == ChannelType::AXIS; } | ||
| 50 | bool isPose() const { return getType() == ChannelType::POSE; } | ||
| 51 | |||
| 52 | // WORKAROUND: the explicit initializer here avoids a bug in GCC-4.8.2 (but not found in 4.9.2) | ||
| 53 | // where the default initializer (a C++-11ism) for the union data above is not applied. | ||
| 54 | explicit Input() {} | ||
| 55 | explicit Input(uint32_t id) : id(id) {} | ||
| 56 | explicit Input(uint16_t device, uint16_t channel, ChannelType type) : device(device), channel(channel), type(uint16_t(type)), padding(0) {} | ||
| 57 | Input(const Input& src) : id(src.id) {} | ||
| 58 | Input& operator = (const Input& src) { id = src.id; return (*this); } | ||
| 59 | bool operator ==(const Input& right) const { return INVALID_INPUT.id != id && INVALID_INPUT.id != right.id && id == right.id; } | ||
| 60 | bool operator !=(const Input& right) const { return !(*this == right); } | ||
| 61 | bool operator < (const Input& src) const { return id < src.id; } | ||
| 62 | |||
| 63 | static const Input INVALID_INPUT; | ||
| 64 | static const uint16_t INVALID_DEVICE; | ||
| 65 | static const uint16_t INVALID_CHANNEL; | ||
| 66 | static const uint16_t INVALID_TYPE; | ||
| 67 | |||
| 68 | using NamedPair = QPair<Input, QString>; | ||
| 69 | using NamedVector = QVector<NamedPair>; | ||
| 70 | }; | ||
| 71 | |||
| 72 | } | ||
| 73 | |||
| 74 | #endif | ||
| @@ -0,0 +1,114 @@ | |||
| 1 | // | ||
| 2 | // InputDevice.cpp | ||
| 3 | // input-plugins/src/input-plugins | ||
| 4 | // | ||
| 5 | // Created by Sam Gondelman on 7/15/2015 | ||
| 6 | // Copyright 2015 High Fidelity, Inc. | ||
| 7 | // | ||
| 8 | // Distributed under the Apache License, Version 2.0. | ||
| 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 10 | // | ||
| 11 | #include "InputDevice.h" | ||
| 12 | |||
| 13 | #include "Input.h" | ||
| 14 | #include "impl/endpoints/InputEndpoint.h" | ||
| 15 | |||
| 16 | namespace controller { | ||
| 17 | |||
| 18 | bool InputDevice::_lowVelocityFilter = false; | ||
| 19 | |||
| 20 | const float DEFAULT_HAND_RETICLE_MOVE_SPEED = 37.5f; | ||
| 21 | float InputDevice::_reticleMoveSpeed = DEFAULT_HAND_RETICLE_MOVE_SPEED; | ||
| 22 | |||
| 23 | //Constants for getCursorPixelRangeMultiplier() | ||
| 24 | const float MIN_PIXEL_RANGE_MULT = 0.4f; | ||
| 25 | const float MAX_PIXEL_RANGE_MULT = 2.0f; | ||
| 26 | const float RANGE_MULT = (MAX_PIXEL_RANGE_MULT - MIN_PIXEL_RANGE_MULT) * 0.01f; | ||
| 27 | |||
| 28 | //Returns a multiplier to be applied to the cursor range for the controllers | ||
| 29 | float InputDevice::getCursorPixelRangeMult() { | ||
| 30 | //scales (0,100) to (MINIMUM_PIXEL_RANGE_MULT, MAXIMUM_PIXEL_RANGE_MULT) | ||
| 31 | return InputDevice::_reticleMoveSpeed * RANGE_MULT + MIN_PIXEL_RANGE_MULT; | ||
| 32 | } | ||
| 33 | |||
| 34 | float InputDevice::getButton(int channel) const { | ||
| 35 | if (!_buttonPressedMap.empty()) { | ||
| 36 | if (_buttonPressedMap.find(channel) != _buttonPressedMap.end()) { | ||
| 37 | return 1.0f; | ||
| 38 | } else { | ||
| 39 | return 0.0f; | ||
| 40 | } | ||
| 41 | } | ||
| 42 | return 0.0f; | ||
| 43 | } | ||
| 44 | |||
| 45 | float InputDevice::getAxis(int channel) const { | ||
| 46 | auto axis = _axisStateMap.find(channel); | ||
| 47 | if (axis != _axisStateMap.end()) { | ||
| 48 | return (*axis).second; | ||
| 49 | } else { | ||
| 50 | return 0.0f; | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | Pose InputDevice::getPose(int channel) const { | ||
| 55 | auto pose = _poseStateMap.find(channel); | ||
| 56 | if (pose != _poseStateMap.end()) { | ||
| 57 | return (*pose).second; | ||
| 58 | } else { | ||
| 59 | return Pose(); | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 63 | Input InputDevice::makeInput(controller::StandardButtonChannel button) const { | ||
| 64 | return Input(_deviceID, button, ChannelType::BUTTON); | ||
| 65 | } | ||
| 66 | |||
| 67 | Input InputDevice::makeInput(controller::StandardAxisChannel axis) const { | ||
| 68 | return Input(_deviceID, axis, ChannelType::AXIS); | ||
| 69 | } | ||
| 70 | |||
| 71 | Input InputDevice::makeInput(controller::StandardPoseChannel pose) const { | ||
| 72 | return Input(_deviceID, pose, ChannelType::POSE); | ||
| 73 | } | ||
| 74 | |||
| 75 | Input::NamedPair InputDevice::makePair(controller::StandardButtonChannel button, const QString& name) const { | ||
| 76 | return Input::NamedPair(makeInput(button), name); | ||
| 77 | } | ||
| 78 | |||
| 79 | Input::NamedPair InputDevice::makePair(controller::StandardAxisChannel axis, const QString& name) const { | ||
| 80 | return Input::NamedPair(makeInput(axis), name); | ||
| 81 | } | ||
| 82 | |||
| 83 | Input::NamedPair InputDevice::makePair(controller::StandardPoseChannel pose, const QString& name) const { | ||
| 84 | return Input::NamedPair(makeInput(pose), name); | ||
| 85 | } | ||
| 86 | |||
| 87 | float InputDevice::getValue(ChannelType channelType, uint16_t channel) const { | ||
| 88 | switch (channelType) { | ||
| 89 | case ChannelType::AXIS: | ||
| 90 | return getAxis(channel); | ||
| 91 | |||
| 92 | case ChannelType::BUTTON: | ||
| 93 | return getButton(channel); | ||
| 94 | |||
| 95 | case ChannelType::POSE: | ||
| 96 | return getPose(channel).valid ? 1.0f : 0.0f; | ||
| 97 | |||
| 98 | default: | ||
| 99 | break; | ||
| 100 | } | ||
| 101 | |||
| 102 | return 0.0f; | ||
| 103 | } | ||
| 104 | |||
| 105 | |||
| 106 | float InputDevice::getValue(const Input& input) const { | ||
| 107 | return getValue(input.getType(), input.channel); | ||
| 108 | } | ||
| 109 | |||
| 110 | EndpointPointer InputDevice::createEndpoint(const Input& input) const { | ||
| 111 | return std::make_shared<InputEndpoint>(input); | ||
| 112 | } | ||
| 113 | |||
| 114 | } | ||
| @@ -10,12 +10,27 @@ · 9 unmodified lines | |||
| 10 | // | 10 | // |
| 11 | #pragma once | 11 | #pragma once |
| 12 | 12 | ||
| 13 | #include "UserInputMapper.h" | 13 | #include <memory> |
| 14 | #include <map> | ||
| 15 | #include <unordered_set> | ||
| 16 | |||
| 17 | #include <QtCore/QString> | ||
| 18 | |||
| 19 | #include "Pose.h" | ||
| 20 | #include "Input.h" | ||
| 21 | #include "StandardControls.h" | ||
| 22 | #include "DeviceProxy.h" | ||
| 23 | |||
| 14 | 24 | ||
| 15 | // Event types for each controller | 25 | // Event types for each controller |
| 16 | const unsigned int CONTROLLER_0_EVENT = 1500U; | 26 | const unsigned int CONTROLLER_0_EVENT = 1500U; |
| 17 | const unsigned int CONTROLLER_1_EVENT = 1501U; | 27 | const unsigned int CONTROLLER_1_EVENT = 1501U; |
| 18 | 28 | ||
| 29 | namespace controller { | ||
| 30 | |||
| 31 | class Endpoint; | ||
| 32 | using EndpointPointer = std::shared_ptr<Endpoint>; | ||
| 33 | |||
| 19 | // NOTE: If something inherits from both InputDevice and InputPlugin, InputPlugin must go first. | 34 | // NOTE: If something inherits from both InputDevice and InputPlugin, InputPlugin must go first. |
| 20 | // e.g. class Example : public InputPlugin, public InputDevice | 35 | // e.g. class Example : public InputPlugin, public InputDevice |
| 21 | // instead of class Example : public InputDevice, public InputPlugin | 36 | // instead of class Example : public InputDevice, public InputPlugin |
| @@ -23,17 +38,22 @@ class InputDevice { · 1 unmodified line | |||
| 23 | public: | 38 | public: |
| 24 | InputDevice(const QString& name) : _name(name) {} | 39 | InputDevice(const QString& name) : _name(name) {} |
| 25 | 40 | ||
| 41 | using Pointer = std::shared_ptr<InputDevice>; | ||
| 42 | |||
| 26 | typedef std::unordered_set<int> ButtonPressedMap; | 43 | typedef std::unordered_set<int> ButtonPressedMap; |
| 27 | typedef std::map<int, float> AxisStateMap; | 44 | typedef std::map<int, float> AxisStateMap; |
| 28 | typedef std::map<int, UserInputMapper::PoseValue> PoseStateMap; | 45 | typedef std::map<int, Pose> PoseStateMap; |
| 29 | 46 | ||
| 30 | // Get current state for each channel | 47 | // Get current state for each channel |
| 31 | float getButton(int channel) const; | 48 | float getButton(int channel) const; |
| 32 | float getAxis(int channel) const; | 49 | float getAxis(int channel) const; |
| 33 | UserInputMapper::PoseValue getPose(int channel) const; | 50 | Pose getPose(int channel) const; |
| 34 | 51 | ||
| 35 | virtual void registerToUserInputMapper(UserInputMapper& mapper) = 0; | 52 | float getValue(const Input& input) const; |
| 36 | virtual void assignDefaultInputMapping(UserInputMapper& mapper) = 0; | 53 | float getValue(ChannelType channelType, uint16_t channel) const; |
| 54 | Pose getPoseValue(uint16_t channel) const; | ||
| 55 | |||
| 56 | const QString& getName() const { return _name; } | ||
| 37 | 57 | ||
| 38 | // Update call MUST be called once per simulation loop | 58 | // Update call MUST be called once per simulation loop |
| 39 | // It takes care of updating the action states and deltas | 59 | // It takes care of updating the action states and deltas |
| @@ -42,20 +62,33 @@ public: · 2 unmodified lines | |||
| 42 | virtual void focusOutEvent() = 0; | 62 | virtual void focusOutEvent() = 0; |
| 43 | 63 | ||
| 44 | int getDeviceID() { return _deviceID; } | 64 | int getDeviceID() { return _deviceID; } |
| 65 | void setDeviceID(int deviceID) { _deviceID = deviceID; } | ||
| 45 | 66 | ||
| 46 | static float getCursorPixelRangeMult(); | 67 | static float getCursorPixelRangeMult(); |
| 47 | static float getReticleMoveSpeed() { return reticleMoveSpeed; } | 68 | static float getReticleMoveSpeed() { return _reticleMoveSpeed; } |
| 48 | static void setReticleMoveSpeed(float sixenseReticleMoveSpeed) { reticleMoveSpeed = sixenseReticleMoveSpeed; } | 69 | static void setReticleMoveSpeed(float reticleMoveSpeed) { _reticleMoveSpeed = reticleMoveSpeed; } |
| 49 | 70 | ||
| 50 | static bool getLowVelocityFilter() { return _lowVelocityFilter; }; | 71 | static bool getLowVelocityFilter() { return _lowVelocityFilter; }; |
| 51 | 72 | ||
| 73 | Input makeInput(StandardButtonChannel button) const; | ||
| 74 | Input makeInput(StandardAxisChannel axis) const; | ||
| 75 | Input makeInput(StandardPoseChannel pose) const; | ||
| 76 | Input::NamedPair makePair(StandardButtonChannel button, const QString& name) const; | ||
| 77 | Input::NamedPair makePair(StandardAxisChannel button, const QString& name) const; | ||
| 78 | Input::NamedPair makePair(StandardPoseChannel button, const QString& name) const; | ||
| 52 | public slots: | 79 | public slots: |
| 53 | static void setLowVelocityFilter(bool newLowVelocityFilter) { _lowVelocityFilter = newLowVelocityFilter; }; | 80 | static void setLowVelocityFilter(bool newLowVelocityFilter) { _lowVelocityFilter = newLowVelocityFilter; }; |
| 54 | 81 | ||
| 55 | protected: | 82 | protected: |
| 56 | int _deviceID = 0; | 83 | friend class UserInputMapper; |
| 84 | |||
| 85 | virtual Input::NamedVector getAvailableInputs() const = 0; | ||
| 86 | virtual QString getDefaultMappingConfig() const { return QString(); } | ||
| 87 | virtual EndpointPointer createEndpoint(const Input& input) const; | ||
| 57 | 88 | ||
| 58 | QString _name; | 89 | uint16_t _deviceID { Input::INVALID_DEVICE }; |
| 90 | |||
| 91 | const QString _name; | ||
| 59 | 92 | ||
| 60 | ButtonPressedMap _buttonPressedMap; | 93 | ButtonPressedMap _buttonPressedMap; |
| 61 | AxisStateMap _axisStateMap; | 94 | AxisStateMap _axisStateMap; |
| @@ -64,5 +97,7 @@ protected: · 2 unmodified lines | |||
| 64 | static bool _lowVelocityFilter; | 97 | static bool _lowVelocityFilter; |
| 65 | 98 | ||
| 66 | private: | 99 | private: |
| 67 | static float reticleMoveSpeed; | 100 | static float _reticleMoveSpeed; |
| 68 | }; | 101 | }; |
| 102 | |||
| 103 | } | ||
| @@ -0,0 +1,11 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/11 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #include "Logging.h" | ||
| 10 | |||
| 11 | Q_LOGGING_CATEGORY(controllers, "hifi.controllers") | ||
| @@ -0,0 +1,16 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/11 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #ifndef hifi_Controllers_Logging_h | ||
| 10 | #define hifi_Controllers_Logging_h | ||
| 11 | |||
| 12 | #include <QLoggingCategory> | ||
| 13 | |||
| 14 | Q_DECLARE_LOGGING_CATEGORY(controllers) | ||
| 15 | |||
| 16 | #endif | ||
| @@ -0,0 +1,49 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis on 2015/10/18 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #include <QtScript/QScriptEngine> | ||
| 10 | #include <QtScript/QScriptValue> | ||
| 11 | |||
| 12 | #include <RegisteredMetaTypes.h> | ||
| 13 | |||
| 14 | #include "Pose.h" | ||
| 15 | |||
| 16 | namespace controller { | ||
| 17 | |||
| 18 | Pose::Pose(const vec3& translation, const quat& rotation, | ||
| 19 | const vec3& velocity, const quat& angularVelocity) : | ||
| 20 | translation(translation), rotation(rotation), velocity(velocity), angularVelocity(angularVelocity), valid (true) { } | ||
| 21 | |||
| 22 | bool Pose::operator==(const Pose& right) const { | ||
| 23 | // invalid poses return false for comparison, even against identical invalid poses, like NaN | ||
| 24 | if (!valid || !right.valid) { | ||
| 25 | return false; | ||
| 26 | } | ||
| 27 | |||
| 28 | // FIXME add margin of error? Or add an additional withinEpsilon function? | ||
| 29 | return translation == right.getTranslation() && rotation == right.getRotation() && | ||
| 30 | velocity == right.getVelocity() && angularVelocity == right.getAngularVelocity(); | ||
| 31 | } | ||
| 32 | |||
| 33 | QScriptValue Pose::toScriptValue(QScriptEngine* engine, const Pose& pose) { | ||
| 34 | QScriptValue obj = engine->newObject(); | ||
| 35 | obj.setProperty("translation", vec3toScriptValue(engine, pose.translation)); | ||
| 36 | obj.setProperty("rotation", quatToScriptValue(engine, pose.rotation)); | ||
| 37 | obj.setProperty("velocity", vec3toScriptValue(engine, pose.velocity)); | ||
| 38 | obj.setProperty("angularVelocity", quatToScriptValue(engine, pose.angularVelocity)); | ||
| 39 | obj.setProperty("valid", pose.valid); | ||
| 40 | |||
| 41 | return obj; | ||
| 42 | } | ||
| 43 | |||
| 44 | void Pose::fromScriptValue(const QScriptValue& object, Pose& pose) { | ||
| 45 | // nothing for now... | ||
| 46 | } | ||
| 47 | |||
| 48 | } | ||
| 49 | |||
| @@ -0,0 +1,50 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis on 2015/10/18 | ||
| 3 | // (based on UserInputMapper inner class created by Sam Gateau on 4/27/15) | ||
| 4 | // Copyright 2015 High Fidelity, Inc. | ||
| 5 | // | ||
| 6 | // Distributed under the Apache License, Version 2.0. | ||
| 7 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 8 | // | ||
| 9 | |||
| 10 | #pragma once | ||
| 11 | #ifndef hifi_controllers_Pose_h | ||
| 12 | #define hifi_controllers_Pose_h | ||
| 13 | |||
| 14 | class QScriptEngine; | ||
| 15 | class QScriptValue; | ||
| 16 | |||
| 17 | #include <GLMHelpers.h> | ||
| 18 | |||
| 19 | namespace controller { | ||
| 20 | |||
| 21 | struct Pose { | ||
| 22 | public: | ||
| 23 | vec3 translation; | ||
| 24 | quat rotation; | ||
| 25 | vec3 velocity; | ||
| 26 | quat angularVelocity; | ||
| 27 | bool valid{ false }; | ||
| 28 | |||
| 29 | Pose() {} | ||
| 30 | Pose(const vec3& translation, const quat& rotation, | ||
| 31 | const vec3& velocity = vec3(), const quat& angularVelocity = quat()); | ||
| 32 | |||
| 33 | Pose(const Pose&) = default; | ||
| 34 | Pose& operator = (const Pose&) = default; | ||
| 35 | bool operator ==(const Pose& right) const; | ||
| 36 | bool operator !=(const Pose& right) const { return !(*this == right); } | ||
| 37 | bool isValid() const { return valid; } | ||
| 38 | vec3 getTranslation() const { return translation; } | ||
| 39 | quat getRotation() const { return rotation; } | ||
| 40 | vec3 getVelocity() const { return velocity; } | ||
| 41 | quat getAngularVelocity() const { return angularVelocity; } | ||
| 42 | |||
| 43 | static QScriptValue toScriptValue(QScriptEngine* engine, const Pose& event); | ||
| 44 | static void fromScriptValue(const QScriptValue& object, Pose& event); | ||
| 45 | }; | ||
| 46 | } | ||
| 47 | |||
| 48 | //Q_DECLARE_METATYPE(controller::Pose); | ||
| 49 | |||
| 50 | #endif | ||
| @@ -0,0 +1,241 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/09 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | #include "ScriptingInterface.h" | ||
| 9 | |||
| 10 | #include <mutex> | ||
| 11 | #include <set> | ||
| 12 | |||
| 13 | #include <QtCore/QRegularExpression> | ||
| 14 | |||
| 15 | #include <QEventLoop> | ||
| 16 | #include <QThread> | ||
| 17 | |||
| 18 | #include <GLMHelpers.h> | ||
| 19 | #include <DependencyManager.h> | ||
| 20 | |||
| 21 | #include <ResourceManager.h> | ||
| 22 | |||
| 23 | #include "impl/MappingBuilderProxy.h" | ||
| 24 | #include "Logging.h" | ||
| 25 | #include "InputDevice.h" | ||
| 26 | |||
| 27 | |||
| 28 | static QRegularExpression SANITIZE_NAME_EXPRESSION{ "[\\(\\)\\.\\s]" }; | ||
| 29 | |||
| 30 | static QVariantMap createDeviceMap(const controller::InputDevice::Pointer device) { | ||
| 31 | auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); | ||
| 32 | QVariantMap deviceMap; | ||
| 33 | for (const auto& inputMapping : userInputMapper->getAvailableInputs(device->getDeviceID())) { | ||
| 34 | const auto& input = inputMapping.first; | ||
| 35 | const auto inputName = QString(inputMapping.second).remove(SANITIZE_NAME_EXPRESSION); | ||
| 36 | qCDebug(controllers) << "\tInput " << input.getChannel() << (int)input.getType() | ||
| 37 | << QString::number(input.getID(), 16) << ": " << inputName; | ||
| 38 | deviceMap.insert(inputName, input.getID()); | ||
| 39 | } | ||
| 40 | return deviceMap; | ||
| 41 | } | ||
| 42 | |||
| 43 | // FIXME this throws a hissy fit on MSVC if I put it in the main controller namespace block | ||
| 44 | controller::ScriptingInterface::ScriptingInterface() { | ||
| 45 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); | ||
| 46 | |||
| 47 | connect(userInputMapper.data(), &UserInputMapper::actionEvent, this, &controller::ScriptingInterface::actionEvent); | ||
| 48 | connect(userInputMapper.data(), &UserInputMapper::inputEvent, this, &controller::ScriptingInterface::inputEvent); | ||
| 49 | |||
| 50 | // FIXME make this thread safe | ||
| 51 | connect(userInputMapper.data(), &UserInputMapper::hardwareChanged, this, [=] { | ||
| 52 | updateMaps(); | ||
| 53 | emit hardwareChanged(); | ||
| 54 | }); | ||
| 55 | |||
| 56 | qCDebug(controllers) << "Setting up standard controller abstraction"; | ||
| 57 | _standard = createDeviceMap(userInputMapper->getStandardDevice()); | ||
| 58 | |||
| 59 | // FIXME allow custom user actions? | ||
| 60 | auto actionNames = userInputMapper->getActionNames(); | ||
| 61 | qCDebug(controllers) << "Setting up standard actions"; | ||
| 62 | for (const auto& namedInput : userInputMapper->getActionInputs()) { | ||
| 63 | const QString& actionName = namedInput.second; | ||
| 64 | const Input& actionInput = namedInput.first; | ||
| 65 | qCDebug(controllers) << "\tAction: " << actionName << " " << actionInput.getChannel(); | ||
| 66 | |||
| 67 | // Expose the IDs to JS | ||
| 68 | QString cleanActionName = QString(actionName).remove(SANITIZE_NAME_EXPRESSION); | ||
| 69 | _actions.insert(cleanActionName, actionInput.getID()); | ||
| 70 | } | ||
| 71 | |||
| 72 | updateMaps(); | ||
| 73 | } | ||
| 74 | |||
| 75 | namespace controller { | ||
| 76 | |||
| 77 | QObject* ScriptingInterface::newMapping(const QString& mappingName) { | ||
| 78 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); | ||
| 79 | return new MappingBuilderProxy(*userInputMapper, userInputMapper->newMapping(mappingName)); | ||
| 80 | } | ||
| 81 | |||
| 82 | void ScriptingInterface::enableMapping(const QString& mappingName, bool enable) { | ||
| 83 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); | ||
| 84 | userInputMapper->enableMapping(mappingName, enable); | ||
| 85 | } | ||
| 86 | |||
| 87 | float ScriptingInterface::getValue(const int& source) const { | ||
| 88 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); | ||
| 89 | return userInputMapper->getValue(Input((uint32_t)source)); | ||
| 90 | } | ||
| 91 | |||
| 92 | float ScriptingInterface::getButtonValue(StandardButtonChannel source, uint16_t device) const { | ||
| 93 | return getValue(Input(device, source, ChannelType::BUTTON).getID()); | ||
| 94 | } | ||
| 95 | |||
| 96 | float ScriptingInterface::getAxisValue(StandardAxisChannel source, uint16_t device) const { | ||
| 97 | return getValue(Input(device, source, ChannelType::AXIS).getID()); | ||
| 98 | } | ||
| 99 | |||
| 100 | Pose ScriptingInterface::getPoseValue(const int& source) const { | ||
| 101 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); | ||
| 102 | return userInputMapper->getPose(Input((uint32_t)source)); | ||
| 103 | } | ||
| 104 | |||
| 105 | Pose ScriptingInterface::getPoseValue(StandardPoseChannel source, uint16_t device) const { | ||
| 106 | return getPoseValue(Input(device, source, ChannelType::POSE).getID()); | ||
| 107 | } | ||
| 108 | |||
| 109 | //bool ScriptingInterface::isPrimaryButtonPressed() const { | ||
| 110 | // return isButtonPressed(StandardButtonChannel::A); | ||
| 111 | //} | ||
| 112 | // | ||
| 113 | //glm::vec2 ScriptingInterface::getPrimaryJoystickPosition() const { | ||
| 114 | // return getJoystickPosition(0); | ||
| 115 | //} | ||
| 116 | |||
| 117 | //int ScriptingInterface::getNumberOfButtons() const { | ||
| 118 | // return StandardButtonChannel::NUM_STANDARD_BUTTONS; | ||
| 119 | //} | ||
| 120 | |||
| 121 | //bool ScriptingInterface::isButtonPressed(int buttonIndex) const { | ||
| 122 | // return getButtonValue((StandardButtonChannel)buttonIndex) == 0.0 ? false : true; | ||
| 123 | //} | ||
| 124 | |||
| 125 | //int ScriptingInterface::getNumberOfTriggers() const { | ||
| 126 | // return StandardCounts::TRIGGERS; | ||
| 127 | //} | ||
| 128 | |||
| 129 | //float ScriptingInterface::getTriggerValue(int triggerIndex) const { | ||
| 130 | // return getAxisValue(triggerIndex == 0 ? StandardAxisChannel::LT : StandardAxisChannel::RT); | ||
| 131 | //} | ||
| 132 | |||
| 133 | //int ScriptingInterface::getNumberOfJoysticks() const { | ||
| 134 | // return StandardCounts::ANALOG_STICKS; | ||
| 135 | //} | ||
| 136 | |||
| 137 | //glm::vec2 ScriptingInterface::getJoystickPosition(int joystickIndex) const { | ||
| 138 | // StandardAxisChannel xid = StandardAxisChannel::LX; | ||
| 139 | // StandardAxisChannel yid = StandardAxisChannel::LY; | ||
| 140 | // if (joystickIndex != 0) { | ||
| 141 | // xid = StandardAxisChannel::RX; | ||
| 142 | // yid = StandardAxisChannel::RY; | ||
| 143 | // } | ||
| 144 | // vec2 result; | ||
| 145 | // result.x = getAxisValue(xid); | ||
| 146 | // result.y = getAxisValue(yid); | ||
| 147 | // return result; | ||
| 148 | //} | ||
| 149 | |||
| 150 | //int ScriptingInterface::getNumberOfSpatialControls() const { | ||
| 151 | // return StandardCounts::POSES; | ||
| 152 | //} | ||
| 153 | |||
| 154 | //glm::vec3 ScriptingInterface::getSpatialControlPosition(int controlIndex) const { | ||
| 155 | // // FIXME extract the position from the standard pose | ||
| 156 | // return vec3(); | ||
| 157 | //} | ||
| 158 | |||
| 159 | //glm::vec3 ScriptingInterface::getSpatialControlVelocity(int controlIndex) const { | ||
| 160 | // // FIXME extract the velocity from the standard pose | ||
| 161 | // return vec3(); | ||
| 162 | //} | ||
| 163 | |||
| 164 | //glm::vec3 ScriptingInterface::getSpatialControlNormal(int controlIndex) const { | ||
| 165 | // // FIXME extract the normal from the standard pose | ||
| 166 | // return vec3(); | ||
| 167 | //} | ||
| 168 | // | ||
| 169 | //glm::quat ScriptingInterface::getSpatialControlRawRotation(int controlIndex) const { | ||
| 170 | // // FIXME extract the rotation from the standard pose | ||
| 171 | // return quat(); | ||
| 172 | //} | ||
| 173 | |||
| 174 | QVector<Action> ScriptingInterface::getAllActions() { | ||
| 175 | return DependencyManager::get<UserInputMapper>()->getAllActions(); | ||
| 176 | } | ||
| 177 | |||
| 178 | QString ScriptingInterface::getDeviceName(unsigned int device) { | ||
| 179 | return DependencyManager::get<UserInputMapper>()->getDeviceName((unsigned short)device); | ||
| 180 | } | ||
| 181 | |||
| 182 | QVector<Input::NamedPair> ScriptingInterface::getAvailableInputs(unsigned int device) { | ||
| 183 | return DependencyManager::get<UserInputMapper>()->getAvailableInputs((unsigned short)device); | ||
| 184 | } | ||
| 185 | |||
| 186 | int ScriptingInterface::findDevice(QString name) { | ||
| 187 | return DependencyManager::get<UserInputMapper>()->findDevice(name); | ||
| 188 | } | ||
| 189 | |||
| 190 | QVector<QString> ScriptingInterface::getDeviceNames() { | ||
| 191 | return DependencyManager::get<UserInputMapper>()->getDeviceNames(); | ||
| 192 | } | ||
| 193 | |||
| 194 | float ScriptingInterface::getActionValue(int action) { | ||
| 195 | return DependencyManager::get<UserInputMapper>()->getActionState(Action(action)); | ||
| 196 | } | ||
| 197 | |||
| 198 | int ScriptingInterface::findAction(QString actionName) { | ||
| 199 | return DependencyManager::get<UserInputMapper>()->findAction(actionName); | ||
| 200 | } | ||
| 201 | |||
| 202 | QVector<QString> ScriptingInterface::getActionNames() const { | ||
| 203 | return DependencyManager::get<UserInputMapper>()->getActionNames(); | ||
| 204 | } | ||
| 205 | |||
| 206 | void ScriptingInterface::updateMaps() { | ||
| 207 | QVariantMap newHardware; | ||
| 208 | auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); | ||
| 209 | auto devices = userInputMapper->getDevices(); | ||
| 210 | for (const auto& deviceMapping : devices) { | ||
| 211 | auto deviceID = deviceMapping.first; | ||
| 212 | if (deviceID != userInputMapper->getStandardDeviceID()) { | ||
| 213 | auto device = deviceMapping.second; | ||
| 214 | auto deviceName = QString(device->getName()).remove(SANITIZE_NAME_EXPRESSION); | ||
| 215 | qCDebug(controllers) << "Device" << deviceMapping.first << ":" << deviceName; | ||
| 216 | if (newHardware.contains(deviceName)) { | ||
| 217 | continue; | ||
| 218 | } | ||
| 219 | |||
| 220 | // Expose the IDs to JS | ||
| 221 | newHardware.insert(deviceName, createDeviceMap(device)); | ||
| 222 | } | ||
| 223 | } | ||
| 224 | _hardware = newHardware; | ||
| 225 | } | ||
| 226 | |||
| 227 | |||
| 228 | QObject* ScriptingInterface::parseMapping(const QString& json) { | ||
| 229 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); | ||
| 230 | auto mapping = userInputMapper->parseMapping(json); | ||
| 231 | return new MappingBuilderProxy(*userInputMapper, mapping); | ||
| 232 | } | ||
| 233 | |||
| 234 | QObject* ScriptingInterface::loadMapping(const QString& jsonUrl) { | ||
| 235 | return nullptr; | ||
| 236 | } | ||
| 237 | |||
| 238 | |||
| 239 | } // namespace controllers | ||
| 240 | |||
| 241 | |||
| @@ -0,0 +1,155 @@ | |||
| 1 | // | ||
| 2 | // AbstractControllerScriptingInterface.h | ||
| 3 | // libraries/script-engine/src | ||
| 4 | // | ||
| 5 | // Created by Brad Hefta-Gaub on 12/17/13. | ||
| 6 | // Copyright 2013 High Fidelity, Inc. | ||
| 7 | // | ||
| 8 | // Distributed under the Apache License, Version 2.0. | ||
| 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 10 | // | ||
| 11 | |||
| 12 | #pragma once | ||
| 13 | #ifndef hifi_AbstractControllerScriptingInterface_h | ||
| 14 | #define hifi_AbstractControllerScriptingInterface_h | ||
| 15 | |||
| 16 | #include <unordered_map> | ||
| 17 | #include <unordered_set> | ||
| 18 | #include <map> | ||
| 19 | #include <set> | ||
| 20 | |||
| 21 | #include <glm/glm.hpp> | ||
| 22 | #include <glm/gtc/quaternion.hpp> | ||
| 23 | |||
| 24 | #include <QThread> | ||
| 25 | #include <QtCore/QObject> | ||
| 26 | #include <QtCore/QVariant> | ||
| 27 | |||
| 28 | #include <QtQml/QJSValue> | ||
| 29 | #include <QtScript/QScriptValue> | ||
| 30 | |||
| 31 | #include <DependencyManager.h> | ||
| 32 | |||
| 33 | #include "UserInputMapper.h" | ||
| 34 | #include "StandardControls.h" | ||
| 35 | |||
| 36 | namespace controller { | ||
| 37 | class InputController : public QObject { | ||
| 38 | Q_OBJECT | ||
| 39 | |||
| 40 | public: | ||
| 41 | using Key = unsigned int; | ||
| 42 | using Pointer = std::shared_ptr<InputController>; | ||
| 43 | |||
| 44 | virtual void update() = 0; | ||
| 45 | virtual Key getKey() const = 0; | ||
| 46 | |||
| 47 | public slots: | ||
| 48 | virtual bool isActive() const = 0; | ||
| 49 | virtual glm::vec3 getAbsTranslation() const = 0; | ||
| 50 | virtual glm::quat getAbsRotation() const = 0; | ||
| 51 | virtual glm::vec3 getLocTranslation() const = 0; | ||
| 52 | virtual glm::quat getLocRotation() const = 0; | ||
| 53 | |||
| 54 | signals: | ||
| 55 | //void spatialEvent(const SpatialEvent& event); | ||
| 56 | }; | ||
| 57 | |||
| 58 | /// handles scripting of input controller commands from JS | ||
| 59 | class ScriptingInterface : public QObject, public Dependency { | ||
| 60 | Q_OBJECT | ||
| 61 | Q_PROPERTY(QVariantMap Hardware READ getHardware CONSTANT FINAL) | ||
| 62 | Q_PROPERTY(QVariantMap Actions READ getActions CONSTANT FINAL) | ||
| 63 | Q_PROPERTY(QVariantMap Standard READ getStandard CONSTANT FINAL) | ||
| 64 | |||
| 65 | public: | ||
| 66 | ScriptingInterface(); | ||
| 67 | virtual ~ScriptingInterface() {}; | ||
| 68 | |||
| 69 | Q_INVOKABLE QVector<Action> getAllActions(); | ||
| 70 | Q_INVOKABLE QVector<Input::NamedPair> getAvailableInputs(unsigned int device); | ||
| 71 | Q_INVOKABLE QString getDeviceName(unsigned int device); | ||
| 72 | Q_INVOKABLE float getActionValue(int action); | ||
| 73 | Q_INVOKABLE int findDevice(QString name); | ||
| 74 | Q_INVOKABLE QVector<QString> getDeviceNames(); | ||
| 75 | Q_INVOKABLE int findAction(QString actionName); | ||
| 76 | Q_INVOKABLE QVector<QString> getActionNames() const; | ||
| 77 | |||
| 78 | Q_INVOKABLE float getValue(const int& source) const; | ||
| 79 | Q_INVOKABLE float getButtonValue(StandardButtonChannel source, uint16_t device = 0) const; | ||
| 80 | Q_INVOKABLE float getAxisValue(StandardAxisChannel source, uint16_t device = 0) const; | ||
| 81 | Q_INVOKABLE Pose getPoseValue(const int& source) const; | ||
| 82 | Q_INVOKABLE Pose getPoseValue(StandardPoseChannel source, uint16_t device = 0) const; | ||
| 83 | |||
| 84 | Q_INVOKABLE QObject* newMapping(const QString& mappingName = QUuid::createUuid().toString()); | ||
| 85 | Q_INVOKABLE void enableMapping(const QString& mappingName, bool enable = true); | ||
| 86 | Q_INVOKABLE void disableMapping(const QString& mappingName) { enableMapping(mappingName, false); } | ||
| 87 | Q_INVOKABLE QObject* parseMapping(const QString& json); | ||
| 88 | Q_INVOKABLE QObject* loadMapping(const QString& jsonUrl); | ||
| 89 | |||
| 90 | |||
| 91 | //Q_INVOKABLE bool isPrimaryButtonPressed() const; | ||
| 92 | //Q_INVOKABLE glm::vec2 getPrimaryJoystickPosition() const; | ||
| 93 | |||
| 94 | //Q_INVOKABLE int getNumberOfButtons() const; | ||
| 95 | //Q_INVOKABLE bool isButtonPressed(int buttonIndex) const; | ||
| 96 | |||
| 97 | //Q_INVOKABLE int getNumberOfTriggers() const; | ||
| 98 | //Q_INVOKABLE float getTriggerValue(int triggerIndex) const; | ||
| 99 | |||
| 100 | //Q_INVOKABLE int getNumberOfJoysticks() const; | ||
| 101 | //Q_INVOKABLE glm::vec2 getJoystickPosition(int joystickIndex) const; | ||
| 102 | |||
| 103 | //Q_INVOKABLE int getNumberOfSpatialControls() const; | ||
| 104 | //Q_INVOKABLE glm::vec3 getSpatialControlPosition(int controlIndex) const; | ||
| 105 | //Q_INVOKABLE glm::vec3 getSpatialControlVelocity(int controlIndex) const; | ||
| 106 | //Q_INVOKABLE glm::vec3 getSpatialControlNormal(int controlIndex) const; | ||
| 107 | //Q_INVOKABLE glm::quat getSpatialControlRawRotation(int controlIndex) const; | ||
| 108 | |||
| 109 | Q_INVOKABLE const QVariantMap& getHardware() { return _hardware; } | ||
| 110 | Q_INVOKABLE const QVariantMap& getActions() { return _actions; } | ||
| 111 | Q_INVOKABLE const QVariantMap& getStandard() { return _standard; } | ||
| 112 | |||
| 113 | bool isMouseCaptured() const { return _mouseCaptured; } | ||
| 114 | bool isTouchCaptured() const { return _touchCaptured; } | ||
| 115 | bool isWheelCaptured() const { return _wheelCaptured; } | ||
| 116 | bool areActionsCaptured() const { return _actionsCaptured; } | ||
| 117 | |||
| 118 | public slots: | ||
| 119 | |||
| 120 | virtual void captureMouseEvents() { _mouseCaptured = true; } | ||
| 121 | virtual void releaseMouseEvents() { _mouseCaptured = false; } | ||
| 122 | |||
| 123 | virtual void captureTouchEvents() { _touchCaptured = true; } | ||
| 124 | virtual void releaseTouchEvents() { _touchCaptured = false; } | ||
| 125 | |||
| 126 | virtual void captureWheelEvents() { _wheelCaptured = true; } | ||
| 127 | virtual void releaseWheelEvents() { _wheelCaptured = false; } | ||
| 128 | |||
| 129 | virtual void captureActionEvents() { _actionsCaptured = true; } | ||
| 130 | virtual void releaseActionEvents() { _actionsCaptured = false; } | ||
| 131 | |||
| 132 | signals: | ||
| 133 | void actionEvent(int action, float state); | ||
| 134 | void inputEvent(int action, float state); | ||
| 135 | void hardwareChanged(); | ||
| 136 | |||
| 137 | private: | ||
| 138 | // Update the exposed variant maps reporting active hardware | ||
| 139 | void updateMaps(); | ||
| 140 | |||
| 141 | QVariantMap _hardware; | ||
| 142 | QVariantMap _actions; | ||
| 143 | QVariantMap _standard; | ||
| 144 | |||
| 145 | bool _mouseCaptured{ false }; | ||
| 146 | bool _touchCaptured{ false }; | ||
| 147 | bool _wheelCaptured{ false }; | ||
| 148 | bool _actionsCaptured{ false }; | ||
| 149 | }; | ||
| 150 | |||
| 151 | |||
| 152 | } | ||
| 153 | |||
| 154 | |||
| 155 | #endif // hifi_AbstractControllerScriptingInterface_h | ||
| @@ -0,0 +1,158 @@ | |||
| 1 | // | ||
| 2 | // StandardController.cpp | ||
| 3 | // input-plugins/src/input-plugins | ||
| 4 | // | ||
| 5 | // Created by Brad Hefta-Gaub on 2015-10-11. | ||
| 6 | // Copyright 2015 High Fidelity, Inc. | ||
| 7 | // | ||
| 8 | // Distributed under the Apache License, Version 2.0. | ||
| 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 10 | // | ||
| 11 | |||
| 12 | #include "StandardController.h" | ||
| 13 | |||
| 14 | #include <PathUtils.h> | ||
| 15 | |||
| 16 | #include "UserInputMapper.h" | ||
| 17 | #include "impl/endpoints/StandardEndpoint.h" | ||
| 18 | |||
| 19 | namespace controller { | ||
| 20 | |||
| 21 | StandardController::StandardController() : InputDevice("Standard") { | ||
| 22 | _deviceID = UserInputMapper::STANDARD_DEVICE; | ||
| 23 | } | ||
| 24 | |||
| 25 | StandardController::~StandardController() { | ||
| 26 | } | ||
| 27 | |||
| 28 | void StandardController::update(float deltaTime, bool jointsCaptured) { | ||
| 29 | } | ||
| 30 | |||
| 31 | void StandardController::focusOutEvent() { | ||
| 32 | _axisStateMap.clear(); | ||
| 33 | _buttonPressedMap.clear(); | ||
| 34 | }; | ||
| 35 | |||
| 36 | Input::NamedVector StandardController::getAvailableInputs() const { | ||
| 37 | static Input::NamedVector availableInputs { | ||
| 38 | // Buttons | ||
| 39 | makePair(A, "A"), | ||
| 40 | makePair(B, "B"), | ||
| 41 | makePair(X, "X"), | ||
| 42 | makePair(Y, "Y"), | ||
| 43 | |||
| 44 | // DPad | ||
| 45 | makePair(DU, "DU"), | ||
| 46 | makePair(DD, "DD"), | ||
| 47 | makePair(DL, "DL"), | ||
| 48 | makePair(DR, "DR"), | ||
| 49 | |||
| 50 | // Bumpers | ||
| 51 | makePair(LB, "LB"), | ||
| 52 | makePair(RB, "RB"), | ||
| 53 | |||
| 54 | // Stick press | ||
| 55 | makePair(LS, "LS"), | ||
| 56 | makePair(RS, "RS"), | ||
| 57 | |||
| 58 | // Center buttons | ||
| 59 | makePair(START, "Start"), | ||
| 60 | makePair(BACK, "Back"), | ||
| 61 | |||
| 62 | // Analog sticks | ||
| 63 | makePair(LY, "LY"), | ||
| 64 | makePair(LX, "LX"), | ||
| 65 | makePair(RY, "RY"), | ||
| 66 | makePair(RX, "RX"), | ||
| 67 | |||
| 68 | // Triggers | ||
| 69 | makePair(LT, "LT"), | ||
| 70 | makePair(RT, "RT"), | ||
| 71 | |||
| 72 | |||
| 73 | // Finger abstractions | ||
| 74 | makePair(LEFT_PRIMARY_THUMB, "LeftPrimaryThumb"), | ||
| 75 | makePair(LEFT_SECONDARY_THUMB, "LeftSecondaryThumb"), | ||
| 76 | makePair(RIGHT_PRIMARY_THUMB, "RightPrimaryThumb"), | ||
| 77 | makePair(RIGHT_SECONDARY_THUMB, "RightSecondaryThumb"), | ||
| 78 | |||
| 79 | makePair(LEFT_PRIMARY_INDEX, "LeftPrimaryIndex"), | ||
| 80 | makePair(LEFT_SECONDARY_INDEX, "LeftSecondaryIndex"), | ||
| 81 | makePair(RIGHT_PRIMARY_INDEX, "RightPrimaryIndex"), | ||
| 82 | makePair(RIGHT_SECONDARY_INDEX, "RightSecondaryIndex"), | ||
| 83 | |||
| 84 | makePair(LEFT_GRIP, "LeftGrip"), | ||
| 85 | makePair(RIGHT_GRIP, "RightGrip"), | ||
| 86 | |||
| 87 | // Poses | ||
| 88 | makePair(LEFT_HAND, "LeftHand"), | ||
| 89 | makePair(RIGHT_HAND, "RightHand"), | ||
| 90 | |||
| 91 | |||
| 92 | // Aliases, PlayStation style names | ||
| 93 | makePair(LB, "L1"), | ||
| 94 | makePair(RB, "R1"), | ||
| 95 | makePair(LT, "L2"), | ||
| 96 | makePair(RT, "R2"), | ||
| 97 | makePair(LS, "L3"), | ||
| 98 | makePair(RS, "R3"), | ||
| 99 | makePair(BACK, "Select"), | ||
| 100 | makePair(A, "Cross"), | ||
| 101 | makePair(B, "Circle"), | ||
| 102 | makePair(X, "Square"), | ||
| 103 | makePair(Y, "Triangle"), | ||
| 104 | makePair(DU, "Up"), | ||
| 105 | makePair(DD, "Down"), | ||
| 106 | makePair(DL, "Left"), | ||
| 107 | makePair(DR, "Right"), | ||
| 108 | }; | ||
| 109 | return availableInputs; | ||
| 110 | } | ||
| 111 | |||
| 112 | EndpointPointer StandardController::createEndpoint(const Input& input) const { | ||
| 113 | return std::make_shared<StandardEndpoint>(input); | ||
| 114 | } | ||
| 115 | |||
| 116 | QString StandardController::getDefaultMappingConfig() const { | ||
| 117 | static const QString DEFAULT_MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/standard.json"; | ||
| 118 | return DEFAULT_MAPPING_JSON; | ||
| 119 | } | ||
| 120 | |||
| 121 | // FIXME figure out how to move the shifted version to JSON | ||
| 122 | //void StandardController::assignDefaultInputMapping(UserInputMapper& mapper) { | ||
| 123 | // const float JOYSTICK_MOVE_SPEED = 1.0f; | ||
| 124 | // const float DPAD_MOVE_SPEED = 0.5f; | ||
| 125 | // const float JOYSTICK_YAW_SPEED = 0.5f; | ||
| 126 | // const float JOYSTICK_PITCH_SPEED = 0.25f; | ||
| 127 | // const float BOOM_SPEED = 0.1f; | ||
| 128 | // | ||
| 129 | // // Hold front right shoulder button for precision controls | ||
| 130 | // // Left Joystick: Movement, strafing | ||
| 131 | // mapper.addInputChannel(UserInputMapper::TRANSLATE_Z, makeInput(controller::LY), makeInput(controller::RB), JOYSTICK_MOVE_SPEED / 2.0f); | ||
| 132 | // mapper.addInputChannel(UserInputMapper::TRANSLATE_X, makeInput(controller::LY), makeInput(controller::RB), JOYSTICK_MOVE_SPEED / 2.0f); | ||
| 133 | // | ||
| 134 | // // Right Joystick: Camera orientation | ||
| 135 | // mapper.addInputChannel(UserInputMapper::YAW, makeInput(controller::RX), makeInput(controller::RB), JOYSTICK_YAW_SPEED / 2.0f); | ||
| 136 | // mapper.addInputChannel(UserInputMapper::PITCH, makeInput(controller::RY), makeInput(controller::RB), JOYSTICK_PITCH_SPEED / 2.0f); | ||
| 137 | // | ||
| 138 | // // Dpad movement | ||
| 139 | // mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(controller::DU), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); | ||
| 140 | // mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(controller::DD), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); | ||
| 141 | // mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(controller::DR), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); | ||
| 142 | // mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(controller::DL), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); | ||
| 143 | // | ||
| 144 | // // Button controls | ||
| 145 | // mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(controller::Y), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); | ||
| 146 | // mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(controller::X), makeInput(controller::RB), DPAD_MOVE_SPEED / 2.0f); | ||
| 147 | // | ||
| 148 | // // Zoom | ||
| 149 | // mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(controller::RT), makeInput(controller::RB), BOOM_SPEED / 2.0f); | ||
| 150 | // mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(controller::LT), makeInput(controller::RB), BOOM_SPEED / 2.0f); | ||
| 151 | // | ||
| 152 | // mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(controller::RB)); | ||
| 153 | // | ||
| 154 | // mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(controller::B)); | ||
| 155 | // mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(controller::A)); | ||
| 156 | //} | ||
| 157 | |||
| 158 | } | ||
| @@ -0,0 +1,40 @@ | |||
| 1 | // | ||
| 2 | // StandardController.h | ||
| 3 | // input-plugins/src/input-plugins | ||
| 4 | // | ||
| 5 | // Created by Brad Hefta-Gaub on 2015-10-11. | ||
| 6 | // Copyright 2015 High Fidelity, Inc. | ||
| 7 | // | ||
| 8 | // Distributed under the Apache License, Version 2.0. | ||
| 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 10 | // | ||
| 11 | |||
| 12 | #ifndef hifi_StandardController_h | ||
| 13 | #define hifi_StandardController_h | ||
| 14 | |||
| 15 | #include <QtCore/QObject> | ||
| 16 | #include <QtCore/QVector> | ||
| 17 | |||
| 18 | #include "InputDevice.h" | ||
| 19 | #include "StandardControls.h" | ||
| 20 | |||
| 21 | namespace controller { | ||
| 22 | |||
| 23 | class StandardController : public QObject, public InputDevice { | ||
| 24 | Q_OBJECT | ||
| 25 | Q_PROPERTY(QString name READ getName) | ||
| 26 | |||
| 27 | public: | ||
| 28 | virtual EndpointPointer createEndpoint(const Input& input) const override; | ||
| 29 | virtual Input::NamedVector getAvailableInputs() const override; | ||
| 30 | virtual QString getDefaultMappingConfig() const override; | ||
| 31 | virtual void update(float deltaTime, bool jointsCaptured) override; | ||
| 32 | virtual void focusOutEvent() override; | ||
| 33 | |||
| 34 | StandardController(); | ||
| 35 | virtual ~StandardController(); | ||
| 36 | }; | ||
| 37 | |||
| 38 | } | ||
| 39 | |||
| 40 | #endif // hifi_StandardController_h | ||
| @@ -0,0 +1,85 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/09 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | #pragma once | ||
| 9 | |||
| 10 | namespace controller { | ||
| 11 | |||
| 12 | // Needs to match order and values of SDL_GameControllerButton | ||
| 13 | enum StandardButtonChannel { | ||
| 14 | // Button quad | ||
| 15 | A = 0, | ||
| 16 | B, | ||
| 17 | X, | ||
| 18 | Y, | ||
| 19 | |||
| 20 | // Center buttons | ||
| 21 | BACK, | ||
| 22 | GUIDE, | ||
| 23 | START, | ||
| 24 | |||
| 25 | // Stick press | ||
| 26 | LS, | ||
| 27 | RS, | ||
| 28 | |||
| 29 | // Bumper press | ||
| 30 | LB, | ||
| 31 | RB, | ||
| 32 | |||
| 33 | // DPad | ||
| 34 | DU, | ||
| 35 | DD, | ||
| 36 | DL, | ||
| 37 | DR, | ||
| 38 | |||
| 39 | // These don't map to SDL types | ||
| 40 | LEFT_PRIMARY_THUMB, | ||
| 41 | LEFT_SECONDARY_THUMB, | ||
| 42 | RIGHT_PRIMARY_THUMB, | ||
| 43 | RIGHT_SECONDARY_THUMB, | ||
| 44 | |||
| 45 | LEFT_PRIMARY_INDEX, | ||
| 46 | LEFT_SECONDARY_INDEX, | ||
| 47 | RIGHT_PRIMARY_INDEX, | ||
| 48 | RIGHT_SECONDARY_INDEX, | ||
| 49 | |||
| 50 | LEFT_GRIP, | ||
| 51 | RIGHT_GRIP, | ||
| 52 | |||
| 53 | NUM_STANDARD_BUTTONS | ||
| 54 | }; | ||
| 55 | |||
| 56 | // Needs to match order and values of SDL_GameControllerAxis | ||
| 57 | enum StandardAxisChannel { | ||
| 58 | // Left Analog stick | ||
| 59 | LX = 0, | ||
| 60 | LY, | ||
| 61 | LZ, | ||
| 62 | // Right Analog stick | ||
| 63 | RX, | ||
| 64 | RY, | ||
| 65 | RZ, | ||
| 66 | // Triggers | ||
| 67 | LT, | ||
| 68 | RT, | ||
| 69 | NUM_STANDARD_AXES | ||
| 70 | }; | ||
| 71 | |||
| 72 | // No correlation to SDL | ||
| 73 | enum StandardPoseChannel { | ||
| 74 | LEFT_HAND = 0, | ||
| 75 | RIGHT_HAND, | ||
| 76 | HEAD, | ||
| 77 | NUM_STANDARD_POSES | ||
| 78 | }; | ||
| 79 | |||
| 80 | enum StandardCounts { | ||
| 81 | TRIGGERS = 2, | ||
| 82 | ANALOG_STICKS = 2, | ||
| 83 | POSES = 2, // FIXME 3? if we want to expose the head? | ||
| 84 | }; | ||
| 85 | } | ||
| @@ -0,0 +1,50 @@ | |||
| 1 | // | ||
| 2 | // StateController.cpp | ||
| 3 | // controllers/src/controllers | ||
| 4 | // | ||
| 5 | // Created by Sam Gateau on 2015-10-27. | ||
| 6 | // Copyright 2015 High Fidelity, Inc. | ||
| 7 | // | ||
| 8 | // Distributed under the Apache License, Version 2.0. | ||
| 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 10 | // | ||
| 11 | |||
| 12 | #include "StateController.h" | ||
| 13 | |||
| 14 | #include <PathUtils.h> | ||
| 15 | |||
| 16 | #include "DeviceProxy.h" | ||
| 17 | #include "UserInputMapper.h" | ||
| 18 | #include "impl/Endpoint.h" | ||
| 19 | |||
| 20 | namespace controller { | ||
| 21 | |||
| 22 | StateController::StateController() : InputDevice("Application") { | ||
| 23 | } | ||
| 24 | |||
| 25 | StateController::~StateController() { | ||
| 26 | } | ||
| 27 | |||
| 28 | void StateController::update(float deltaTime, bool jointsCaptured) {} | ||
| 29 | |||
| 30 | void StateController::focusOutEvent() {} | ||
| 31 | |||
| 32 | void StateController::addInputVariant(QString name, ReadLambda lambda) { | ||
| 33 | _namedReadLambdas.push_back(NamedReadLambda(name, lambda)); | ||
| 34 | } | ||
| 35 | |||
| 36 | Input::NamedVector StateController::getAvailableInputs() const { | ||
| 37 | Input::NamedVector availableInputs; | ||
| 38 | int i = 0; | ||
| 39 | for (auto& pair : _namedReadLambdas) { | ||
| 40 | availableInputs.push_back(Input::NamedPair(Input(_deviceID, i, ChannelType::BUTTON), pair.first)); | ||
| 41 | i++; | ||
| 42 | } | ||
| 43 | return availableInputs; | ||
| 44 | } | ||
| 45 | |||
| 46 | EndpointPointer StateController::createEndpoint(const Input& input) const { | ||
| 47 | return std::make_shared<LambdaEndpoint>(_namedReadLambdas[input.getChannel()].second); | ||
| 48 | } | ||
| 49 | |||
| 50 | } | ||
| @@ -0,0 +1,51 @@ | |||
| 1 | // | ||
| 2 | // StateController.h | ||
| 3 | // controllers/src/controllers | ||
| 4 | // | ||
| 5 | // Created by Sam Gateau on 2015-10-27. | ||
| 6 | // Copyright 2015 High Fidelity, Inc. | ||
| 7 | // | ||
| 8 | // Distributed under the Apache License, Version 2.0. | ||
| 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 10 | // | ||
| 11 | |||
| 12 | #ifndef hifi_StateController_h | ||
| 13 | #define hifi_StateController_h | ||
| 14 | |||
| 15 | #include <QtCore/QObject> | ||
| 16 | #include <QtCore/QVector> | ||
| 17 | |||
| 18 | #include "InputDevice.h" | ||
| 19 | |||
| 20 | namespace controller { | ||
| 21 | |||
| 22 | class StateController : public QObject, public InputDevice { | ||
| 23 | Q_OBJECT | ||
| 24 | Q_PROPERTY(QString name READ getName) | ||
| 25 | |||
| 26 | public: | ||
| 27 | const QString& getName() const { return _name; } | ||
| 28 | |||
| 29 | // Device functions | ||
| 30 | virtual Input::NamedVector getAvailableInputs() const override; | ||
| 31 | virtual void update(float deltaTime, bool jointsCaptured) override; | ||
| 32 | virtual void focusOutEvent() override; | ||
| 33 | |||
| 34 | StateController(); | ||
| 35 | virtual ~StateController(); | ||
| 36 | |||
| 37 | using ReadLambda = std::function<float()>; | ||
| 38 | using NamedReadLambda = QPair<QString, ReadLambda>; | ||
| 39 | |||
| 40 | void addInputVariant(QString name, ReadLambda lambda); | ||
| 41 | |||
| 42 | virtual EndpointPointer createEndpoint(const Input& input) const override; | ||
| 43 | |||
| 44 | |||
| 45 | protected: | ||
| 46 | QVector<NamedReadLambda> _namedReadLambdas; | ||
| 47 | }; | ||
| 48 | |||
| 49 | } | ||
| 50 | |||
| 51 | #endif // hifi_StateController_h | ||
| @@ -0,0 +1,1086 @@ | |||
| 1 | // | ||
| 2 | // Created by Sam Gateau on 4/27/15. | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #include "UserInputMapper.h" | ||
| 10 | |||
| 11 | #include <set> | ||
| 12 | |||
| 13 | #include <QtCore/QThread> | ||
| 14 | #include <QtCore/QFile> | ||
| 15 | |||
| 16 | #include <QtCore/QJsonDocument> | ||
| 17 | #include <QtCore/QJsonObject> | ||
| 18 | #include <QtCore/QJsonArray> | ||
| 19 | |||
| 20 | #include <PathUtils.h> | ||
| 21 | #include <NumericalConstants.h> | ||
| 22 | |||
| 23 | #include "StandardController.h" | ||
| 24 | #include "StateController.h" | ||
| 25 | |||
| 26 | #include "Logging.h" | ||
| 27 | |||
| 28 | #include "impl/conditionals/AndConditional.h" | ||
| 29 | #include "impl/conditionals/EndpointConditional.h" | ||
| 30 | #include "impl/conditionals/ScriptConditional.h" | ||
| 31 | |||
| 32 | #include "impl/endpoints/ActionEndpoint.h" | ||
| 33 | #include "impl/endpoints/AnyEndpoint.h" | ||
| 34 | #include "impl/endpoints/ArrayEndpoint.h" | ||
| 35 | #include "impl/endpoints/CompositeEndpoint.h" | ||
| 36 | #include "impl/endpoints/InputEndpoint.h" | ||
| 37 | #include "impl/endpoints/JSEndpoint.h" | ||
| 38 | #include "impl/endpoints/ScriptEndpoint.h" | ||
| 39 | #include "impl/endpoints/StandardEndpoint.h" | ||
| 40 | |||
| 41 | #include "impl/Route.h" | ||
| 42 | #include "impl/Mapping.h" | ||
| 43 | |||
| 44 | |||
| 45 | namespace controller { | ||
| 46 | const uint16_t UserInputMapper::ACTIONS_DEVICE = Input::INVALID_DEVICE - 0xFF; | ||
| 47 | const uint16_t UserInputMapper::STANDARD_DEVICE = 0; | ||
| 48 | } | ||
| 49 | |||
| 50 | // Default contruct allocate the poutput size with the current hardcoded action channels | ||
| 51 | controller::UserInputMapper::UserInputMapper() { | ||
| 52 | registerDevice(std::make_shared<ActionsDevice>()); | ||
| 53 | registerDevice(std::make_shared<StandardController>()); | ||
| 54 | } | ||
| 55 | |||
| 56 | namespace controller { | ||
| 57 | |||
| 58 | |||
| 59 | UserInputMapper::~UserInputMapper() { | ||
| 60 | } | ||
| 61 | |||
| 62 | int UserInputMapper::recordDeviceOfType(const QString& deviceName) { | ||
| 63 | if (!_deviceCounts.contains(deviceName)) { | ||
| 64 | _deviceCounts[deviceName] = 0; | ||
| 65 | } | ||
| 66 | _deviceCounts[deviceName] += 1; | ||
| 67 | return _deviceCounts[deviceName]; | ||
| 68 | } | ||
| 69 | |||
| 70 | void UserInputMapper::registerDevice(InputDevice::Pointer device) { | ||
| 71 | Locker locker(_lock); | ||
| 72 | if (device->_deviceID == Input::INVALID_DEVICE) { | ||
| 73 | device->_deviceID = getFreeDeviceID(); | ||
| 74 | } | ||
| 75 | const auto& deviceID = device->_deviceID; | ||
| 76 | |||
| 77 | int numberOfType = recordDeviceOfType(device->getName()); | ||
| 78 | |||
| 79 | qCDebug(controllers) << "Registered input device <" << device->getName() << "> deviceID = " << deviceID; | ||
| 80 | for (const auto& inputMapping : device->getAvailableInputs()) { | ||
| 81 | const auto& input = inputMapping.first; | ||
| 82 | // Ignore aliases | ||
| 83 | if (_endpointsByInput.count(input)) { | ||
| 84 | continue; | ||
| 85 | } | ||
| 86 | |||
| 87 | Endpoint::Pointer endpoint = device->createEndpoint(input); | ||
| 88 | if (!endpoint) { | ||
| 89 | if (input.device == STANDARD_DEVICE) { | ||
| 90 | endpoint = std::make_shared<StandardEndpoint>(input); | ||
| 91 | } else if (input.device == ACTIONS_DEVICE) { | ||
| 92 | endpoint = std::make_shared<ActionEndpoint>(input); | ||
| 93 | } else { | ||
| 94 | endpoint = std::make_shared<InputEndpoint>(input); | ||
| 95 | } | ||
| 96 | } | ||
| 97 | _inputsByEndpoint[endpoint] = input; | ||
| 98 | _endpointsByInput[input] = endpoint; | ||
| 99 | } | ||
| 100 | |||
| 101 | _registeredDevices[deviceID] = device; | ||
| 102 | auto mapping = loadMapping(device->getDefaultMappingConfig()); | ||
| 103 | if (mapping) { | ||
| 104 | _mappingsByDevice[deviceID] = mapping; | ||
| 105 | enableMapping(mapping); | ||
| 106 | } | ||
| 107 | |||
| 108 | emit hardwareChanged(); | ||
| 109 | } | ||
| 110 | |||
| 111 | // FIXME remove the associated device mappings | ||
| 112 | void UserInputMapper::removeDevice(int deviceID) { | ||
| 113 | Locker locker(_lock); | ||
| 114 | auto proxyEntry = _registeredDevices.find(deviceID); | ||
| 115 | if (_registeredDevices.end() == proxyEntry) { | ||
| 116 | qCWarning(controllers) << "Attempted to remove unknown device " << deviceID; | ||
| 117 | return; | ||
| 118 | } | ||
| 119 | auto proxy = proxyEntry->second; | ||
| 120 | auto mappingsEntry = _mappingsByDevice.find(deviceID); | ||
| 121 | if (_mappingsByDevice.end() != mappingsEntry) { | ||
| 122 | disableMapping(mappingsEntry->second); | ||
| 123 | _mappingsByDevice.erase(mappingsEntry); | ||
| 124 | } | ||
| 125 | |||
| 126 | _registeredDevices.erase(proxyEntry); | ||
| 127 | |||
| 128 | emit hardwareChanged(); | ||
| 129 | } | ||
| 130 | |||
| 131 | |||
| 132 | void UserInputMapper::loadDefaultMapping(uint16 deviceID) { | ||
| 133 | Locker locker(_lock); | ||
| 134 | auto proxyEntry = _registeredDevices.find(deviceID); | ||
| 135 | if (_registeredDevices.end() == proxyEntry) { | ||
| 136 | qCWarning(controllers) << "Unknown deviceID " << deviceID; | ||
| 137 | return; | ||
| 138 | } | ||
| 139 | |||
| 140 | |||
| 141 | auto mapping = loadMapping(proxyEntry->second->getDefaultMappingConfig()); | ||
| 142 | if (mapping) { | ||
| 143 | auto prevMapping = _mappingsByDevice[deviceID]; | ||
| 144 | disableMapping(prevMapping); | ||
| 145 | |||
| 146 | _mappingsByDevice[deviceID] = mapping; | ||
| 147 | enableMapping(mapping); | ||
| 148 | } | ||
| 149 | |||
| 150 | emit hardwareChanged(); | ||
| 151 | } | ||
| 152 | |||
| 153 | InputDevice::Pointer UserInputMapper::getDevice(const Input& input) { | ||
| 154 | Locker locker(_lock); | ||
| 155 | auto device = _registeredDevices.find(input.getDevice()); | ||
| 156 | if (device != _registeredDevices.end()) { | ||
| 157 | return (device->second); | ||
| 158 | } else { | ||
| 159 | return InputDevice::Pointer(); | ||
| 160 | } | ||
| 161 | } | ||
| 162 | |||
| 163 | QString UserInputMapper::getDeviceName(uint16 deviceID) { | ||
| 164 | Locker locker(_lock); | ||
| 165 | if (_registeredDevices.find(deviceID) != _registeredDevices.end()) { | ||
| 166 | return _registeredDevices[deviceID]->_name; | ||
| 167 | } | ||
| 168 | return QString("unknown"); | ||
| 169 | } | ||
| 170 | |||
| 171 | int UserInputMapper::findDevice(QString name) const { | ||
| 172 | Locker locker(_lock); | ||
| 173 | for (auto device : _registeredDevices) { | ||
| 174 | if (device.second->_name == name) { | ||
| 175 | return device.first; | ||
| 176 | } | ||
| 177 | } | ||
| 178 | return Input::INVALID_DEVICE; | ||
| 179 | } | ||
| 180 | |||
| 181 | QVector<QString> UserInputMapper::getDeviceNames() { | ||
| 182 | Locker locker(_lock); | ||
| 183 | QVector<QString> result; | ||
| 184 | for (auto device : _registeredDevices) { | ||
| 185 | QString deviceName = device.second->_name.split(" (")[0]; | ||
| 186 | result << deviceName; | ||
| 187 | } | ||
| 188 | return result; | ||
| 189 | } | ||
| 190 | |||
| 191 | int UserInputMapper::findAction(const QString& actionName) const { | ||
| 192 | return findDeviceInput("Actions." + actionName).getChannel(); | ||
| 193 | } | ||
| 194 | |||
| 195 | Input UserInputMapper::findDeviceInput(const QString& inputName) const { | ||
| 196 | Locker locker(_lock); | ||
| 197 | // Split the full input name as such: deviceName.inputName | ||
| 198 | auto names = inputName.split('.'); | ||
| 199 | |||
| 200 | if (names.size() >= 2) { | ||
| 201 | // Get the device name: | ||
| 202 | auto deviceName = names[0]; | ||
| 203 | auto inputName = names[1]; | ||
| 204 | |||
| 205 | int deviceID = findDevice(deviceName); | ||
| 206 | if (deviceID != Input::INVALID_DEVICE) { | ||
| 207 | const auto& device = _registeredDevices.at(deviceID); | ||
| 208 | auto deviceInputs = device->getAvailableInputs(); | ||
| 209 | |||
| 210 | for (auto input : deviceInputs) { | ||
| 211 | if (input.second == inputName) { | ||
| 212 | return input.first; | ||
| 213 | } | ||
| 214 | } | ||
| 215 | |||
| 216 | qCDebug(controllers) << "Couldn\'t find InputChannel named <" << inputName << "> for device <" << deviceName << ">"; | ||
| 217 | |||
| 218 | } else { | ||
| 219 | qCDebug(controllers) << "Couldn\'t find InputDevice named <" << deviceName << ">"; | ||
| 220 | findDevice(deviceName); | ||
| 221 | } | ||
| 222 | } else { | ||
| 223 | qCDebug(controllers) << "Couldn\'t understand <" << inputName << "> as a valid inputDevice.inputName"; | ||
| 224 | } | ||
| 225 | |||
| 226 | return Input::INVALID_INPUT; | ||
| 227 | } | ||
| 228 | |||
| 229 | void fixBisectedAxis(float& full, float& negative, float& positive) { | ||
| 230 | full = full + (negative * -1.0f) + positive; | ||
| 231 | negative = full >= 0.0f ? 0.0f : full * -1.0f; | ||
| 232 | positive = full <= 0.0f ? 0.0f : full; | ||
| 233 | } | ||
| 234 | |||
| 235 | void UserInputMapper::update(float deltaTime) { | ||
| 236 | Locker locker(_lock); | ||
| 237 | // Reset the axis state for next loop | ||
| 238 | for (auto& channel : _actionStates) { | ||
| 239 | channel = 0.0f; | ||
| 240 | } | ||
| 241 | |||
| 242 | for (auto& channel : _poseStates) { | ||
| 243 | channel = Pose(); | ||
| 244 | } | ||
| 245 | |||
| 246 | // Run the mappings code | ||
| 247 | runMappings(); | ||
| 248 | |||
| 249 | // merge the bisected and non-bisected axes for now | ||
| 250 | fixBisectedAxis(_actionStates[toInt(Action::TRANSLATE_X)], _actionStates[toInt(Action::LATERAL_LEFT)], _actionStates[toInt(Action::LATERAL_RIGHT)]); | ||
| 251 | fixBisectedAxis(_actionStates[toInt(Action::TRANSLATE_Y)], _actionStates[toInt(Action::VERTICAL_DOWN)], _actionStates[toInt(Action::VERTICAL_UP)]); | ||
| 252 | fixBisectedAxis(_actionStates[toInt(Action::TRANSLATE_Z)], _actionStates[toInt(Action::LONGITUDINAL_FORWARD)], _actionStates[toInt(Action::LONGITUDINAL_BACKWARD)]); | ||
| 253 | fixBisectedAxis(_actionStates[toInt(Action::TRANSLATE_CAMERA_Z)], _actionStates[toInt(Action::BOOM_IN)], _actionStates[toInt(Action::BOOM_OUT)]); | ||
| 254 | fixBisectedAxis(_actionStates[toInt(Action::ROTATE_Y)], _actionStates[toInt(Action::YAW_LEFT)], _actionStates[toInt(Action::YAW_RIGHT)]); | ||
| 255 | fixBisectedAxis(_actionStates[toInt(Action::ROTATE_X)], _actionStates[toInt(Action::PITCH_UP)], _actionStates[toInt(Action::PITCH_DOWN)]); | ||
| 256 | |||
| 257 | static const float EPSILON = 0.01f; | ||
| 258 | for (auto i = 0; i < toInt(Action::NUM_ACTIONS); i++) { | ||
| 259 | _actionStates[i] *= _actionScales[i]; | ||
| 260 | // Emit only on change, and emit when moving back to 0 | ||
| 261 | if (fabsf(_actionStates[i] - _lastActionStates[i]) > EPSILON) { | ||
| 262 | _lastActionStates[i] = _actionStates[i]; | ||
| 263 | emit actionEvent(i, _actionStates[i]); | ||
| 264 | } | ||
| 265 | // TODO: emit signal for pose changes | ||
| 266 | } | ||
| 267 | |||
| 268 | auto standardInputs = getStandardInputs(); | ||
| 269 | if (_lastStandardStates.size() != standardInputs.size()) { | ||
| 270 | _lastStandardStates.resize(standardInputs.size()); | ||
| 271 | for (auto& lastValue : _lastStandardStates) { | ||
| 272 | lastValue = 0; | ||
| 273 | } | ||
| 274 | } | ||
| 275 | |||
| 276 | for (int i = 0; i < standardInputs.size(); ++i) { | ||
| 277 | const auto& input = standardInputs[i].first; | ||
| 278 | float value = getValue(input); | ||
| 279 | float& oldValue = _lastStandardStates[i]; | ||
| 280 | if (value != oldValue) { | ||
| 281 | oldValue = value; | ||
| 282 | emit inputEvent(input.id, value); | ||
| 283 | } | ||
| 284 | } | ||
| 285 | } | ||
| 286 | |||
| 287 | Input::NamedVector UserInputMapper::getAvailableInputs(uint16 deviceID) const { | ||
| 288 | Locker locker(_lock); | ||
| 289 | auto iterator = _registeredDevices.find(deviceID); | ||
| 290 | return iterator->second->getAvailableInputs(); | ||
| 291 | } | ||
| 292 | |||
| 293 | QVector<Action> UserInputMapper::getAllActions() const { | ||
| 294 | Locker locker(_lock); | ||
| 295 | QVector<Action> actions; | ||
| 296 | for (auto i = 0; i < toInt(Action::NUM_ACTIONS); i++) { | ||
| 297 | actions.append(Action(i)); | ||
| 298 | } | ||
| 299 | return actions; | ||
| 300 | } | ||
| 301 | |||
| 302 | QString UserInputMapper::getActionName(Action action) const { | ||
| 303 | Locker locker(_lock); | ||
| 304 | for (auto actionPair : getActionInputs()) { | ||
| 305 | if (actionPair.first.channel == toInt(action)) { | ||
| 306 | return actionPair.second; | ||
| 307 | } | ||
| 308 | } | ||
| 309 | return QString(); | ||
| 310 | } | ||
| 311 | |||
| 312 | |||
| 313 | QVector<QString> UserInputMapper::getActionNames() const { | ||
| 314 | Locker locker(_lock); | ||
| 315 | QVector<QString> result; | ||
| 316 | for (auto actionPair : getActionInputs()) { | ||
| 317 | result << actionPair.second; | ||
| 318 | } | ||
| 319 | return result; | ||
| 320 | } | ||
| 321 | /* | ||
| 322 | void UserInputMapper::assignDefaulActionScales() { | ||
| 323 | _actionScales[toInt(Action::LONGITUDINAL_BACKWARD)] = 1.0f; // 1m per unit | ||
| 324 | _actionScales[toInt(Action::LONGITUDINAL_FORWARD)] = 1.0f; // 1m per unit | ||
| 325 | _actionScales[toInt(Action::LATERAL_LEFT)] = 1.0f; // 1m per unit | ||
| 326 | _actionScales[toInt(Action::LATERAL_RIGHT)] = 1.0f; // 1m per unit | ||
| 327 | _actionScales[toInt(Action::VERTICAL_DOWN)] = 1.0f; // 1m per unit | ||
| 328 | _actionScales[toInt(Action::VERTICAL_UP)] = 1.0f; // 1m per unit | ||
| 329 | _actionScales[toInt(Action::YAW_LEFT)] = 1.0f; // 1 degree per unit | ||
| 330 | _actionScales[toInt(Action::YAW_RIGHT)] = 1.0f; // 1 degree per unit | ||
| 331 | _actionScales[toInt(Action::PITCH_DOWN)] = 1.0f; // 1 degree per unit | ||
| 332 | _actionScales[toInt(Action::PITCH_UP)] = 1.0f; // 1 degree per unit | ||
| 333 | _actionScales[toInt(Action::BOOM_IN)] = 0.5f; // .5m per unit | ||
| 334 | _actionScales[toInt(Action::BOOM_OUT)] = 0.5f; // .5m per unit | ||
| 335 | _actionScales[toInt(Action::LEFT_HAND)] = 1.0f; // default | ||
| 336 | _actionScales[toInt(Action::RIGHT_HAND)] = 1.0f; // default | ||
| 337 | _actionScales[toInt(Action::LEFT_HAND_CLICK)] = 1.0f; // on | ||
| 338 | _actionScales[toInt(Action::RIGHT_HAND_CLICK)] = 1.0f; // on | ||
| 339 | _actionScales[toInt(Action::SHIFT)] = 1.0f; // on | ||
| 340 | _actionScales[toInt(Action::ACTION1)] = 1.0f; // default | ||
| 341 | _actionScales[toInt(Action::ACTION2)] = 1.0f; // default | ||
| 342 | _actionScales[toInt(Action::TRANSLATE_X)] = 1.0f; // default | ||
| 343 | _actionScales[toInt(Action::TRANSLATE_Y)] = 1.0f; // default | ||
| 344 | _actionScales[toInt(Action::TRANSLATE_Z)] = 1.0f; // default | ||
| 345 | _actionScales[toInt(Action::ROLL)] = 1.0f; // default | ||
| 346 | _actionScales[toInt(Action::PITCH)] = 1.0f; // default | ||
| 347 | _actionScales[toInt(Action::YAW)] = 1.0f; // default | ||
| 348 | } | ||
| 349 | */ | ||
| 350 | |||
| 351 | static int actionMetaTypeId = qRegisterMetaType<Action>(); | ||
| 352 | static int inputMetaTypeId = qRegisterMetaType<Input>(); | ||
| 353 | static int inputPairMetaTypeId = qRegisterMetaType<Input::NamedPair>(); | ||
| 354 | static int poseMetaTypeId = qRegisterMetaType<controller::Pose>("Pose"); | ||
| 355 | |||
| 356 | |||
| 357 | QScriptValue inputToScriptValue(QScriptEngine* engine, const Input& input); | ||
| 358 | void inputFromScriptValue(const QScriptValue& object, Input& input); | ||
| 359 | QScriptValue actionToScriptValue(QScriptEngine* engine, const Action& action); | ||
| 360 | void actionFromScriptValue(const QScriptValue& object, Action& action); | ||
| 361 | QScriptValue inputPairToScriptValue(QScriptEngine* engine, const Input::NamedPair& inputPair); | ||
| 362 | void inputPairFromScriptValue(const QScriptValue& object, Input::NamedPair& inputPair); | ||
| 363 | |||
| 364 | QScriptValue inputToScriptValue(QScriptEngine* engine, const Input& input) { | ||
| 365 | QScriptValue obj = engine->newObject(); | ||
| 366 | obj.setProperty("device", input.getDevice()); | ||
| 367 | obj.setProperty("channel", input.getChannel()); | ||
| 368 | obj.setProperty("type", (unsigned short)input.getType()); | ||
| 369 | obj.setProperty("id", input.getID()); | ||
| 370 | return obj; | ||
| 371 | } | ||
| 372 | |||
| 373 | void inputFromScriptValue(const QScriptValue& object, Input& input) { | ||
| 374 | input.id = object.property("id").toInt32(); | ||
| 375 | } | ||
| 376 | |||
| 377 | QScriptValue actionToScriptValue(QScriptEngine* engine, const Action& action) { | ||
| 378 | QScriptValue obj = engine->newObject(); | ||
| 379 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); | ||
| 380 | obj.setProperty("action", (int)action); | ||
| 381 | obj.setProperty("actionName", userInputMapper->getActionName(action)); | ||
| 382 | return obj; | ||
| 383 | } | ||
| 384 | |||
| 385 | void actionFromScriptValue(const QScriptValue& object, Action& action) { | ||
| 386 | action = Action(object.property("action").toVariant().toInt()); | ||
| 387 | } | ||
| 388 | |||
| 389 | QScriptValue inputPairToScriptValue(QScriptEngine* engine, const Input::NamedPair& inputPair) { | ||
| 390 | QScriptValue obj = engine->newObject(); | ||
| 391 | obj.setProperty("input", inputToScriptValue(engine, inputPair.first)); | ||
| 392 | obj.setProperty("inputName", inputPair.second); | ||
| 393 | return obj; | ||
| 394 | } | ||
| 395 | |||
| 396 | void inputPairFromScriptValue(const QScriptValue& object, Input::NamedPair& inputPair) { | ||
| 397 | inputFromScriptValue(object.property("input"), inputPair.first); | ||
| 398 | inputPair.second = QString(object.property("inputName").toVariant().toString()); | ||
| 399 | } | ||
| 400 | |||
| 401 | void UserInputMapper::registerControllerTypes(QScriptEngine* engine) { | ||
| 402 | qScriptRegisterSequenceMetaType<QVector<Action> >(engine); | ||
| 403 | qScriptRegisterSequenceMetaType<Input::NamedVector>(engine); | ||
| 404 | qScriptRegisterMetaType(engine, actionToScriptValue, actionFromScriptValue); | ||
| 405 | qScriptRegisterMetaType(engine, inputToScriptValue, inputFromScriptValue); | ||
| 406 | qScriptRegisterMetaType(engine, inputPairToScriptValue, inputPairFromScriptValue); | ||
| 407 | |||
| 408 | qScriptRegisterMetaType(engine, Pose::toScriptValue, Pose::fromScriptValue); | ||
| 409 | } | ||
| 410 | |||
| 411 | Input UserInputMapper::makeStandardInput(controller::StandardButtonChannel button) { | ||
| 412 | return Input(STANDARD_DEVICE, button, ChannelType::BUTTON); | ||
| 413 | } | ||
| 414 | |||
| 415 | Input UserInputMapper::makeStandardInput(controller::StandardAxisChannel axis) { | ||
| 416 | return Input(STANDARD_DEVICE, axis, ChannelType::AXIS); | ||
| 417 | } | ||
| 418 | |||
| 419 | Input UserInputMapper::makeStandardInput(controller::StandardPoseChannel pose) { | ||
| 420 | return Input(STANDARD_DEVICE, pose, ChannelType::POSE); | ||
| 421 | } | ||
| 422 | |||
| 423 | static auto lastDebugTime = usecTimestampNow(); | ||
| 424 | static auto debugRoutes = false; | ||
| 425 | static auto debuggableRoutes = false; | ||
| 426 | static const auto DEBUG_INTERVAL = USECS_PER_SECOND; | ||
| 427 | |||
| 428 | void UserInputMapper::runMappings() { | ||
| 429 | auto now = usecTimestampNow(); | ||
| 430 | if (debuggableRoutes && now - lastDebugTime > DEBUG_INTERVAL) { | ||
| 431 | lastDebugTime = now; | ||
| 432 | debugRoutes = true; | ||
| 433 | } | ||
| 434 | |||
| 435 | if (debugRoutes) { | ||
| 436 | qCDebug(controllers) << "Beginning mapping frame"; | ||
| 437 | } | ||
| 438 | for (auto endpointEntry : this->_endpointsByInput) { | ||
| 439 | endpointEntry.second->reset(); | ||
| 440 | } | ||
| 441 | |||
| 442 | if (debugRoutes) { | ||
| 443 | qCDebug(controllers) << "Processing device routes"; | ||
| 444 | } | ||
| 445 | // Now process the current values for each level of the stack | ||
| 446 | applyRoutes(_deviceRoutes); | ||
| 447 | |||
| 448 | if (debugRoutes) { | ||
| 449 | qCDebug(controllers) << "Processing standard routes"; | ||
| 450 | } | ||
| 451 | applyRoutes(_standardRoutes); | ||
| 452 | |||
| 453 | if (debugRoutes) { | ||
| 454 | qCDebug(controllers) << "Done with mappings"; | ||
| 455 | } | ||
| 456 | debugRoutes = false; | ||
| 457 | } | ||
| 458 | |||
| 459 | // Encapsulate the logic that routes should not be read before they are written | ||
| 460 | void UserInputMapper::applyRoutes(const Route::List& routes) { | ||
| 461 | Route::List deferredRoutes; | ||
| 462 | |||
| 463 | for (const auto& route : routes) { | ||
| 464 | if (!route) { | ||
| 465 | continue; | ||
| 466 | } | ||
| 467 | |||
| 468 | // Try all the deferred routes | ||
| 469 | deferredRoutes.remove_if([](Route::Pointer route) { | ||
| 470 | return UserInputMapper::applyRoute(route); | ||
| 471 | }); | ||
| 472 | |||
| 473 | if (!applyRoute(route)) { | ||
| 474 | deferredRoutes.push_back(route); | ||
| 475 | } | ||
| 476 | } | ||
| 477 | |||
| 478 | bool force = true; | ||
| 479 | for (const auto& route : deferredRoutes) { | ||
| 480 | UserInputMapper::applyRoute(route, force); | ||
| 481 | } | ||
| 482 | } | ||
| 483 | |||
| 484 | |||
| 485 | bool UserInputMapper::applyRoute(const Route::Pointer& route, bool force) { | ||
| 486 | if (debugRoutes && route->debug) { | ||
| 487 | qCDebug(controllers) << "Applying route " << route->json; | ||
| 488 | } | ||
| 489 | |||
| 490 | // If the source hasn't been written yet, defer processing of this route | ||
| 491 | auto source = route->source; | ||
| 492 | auto sourceInput = source->getInput(); | ||
| 493 | if (sourceInput.device == STANDARD_DEVICE && !force && source->writeable()) { | ||
| 494 | if (debugRoutes && route->debug) { | ||
| 495 | qCDebug(controllers) << "Source not yet written, deferring"; | ||
| 496 | } | ||
| 497 | return false; | ||
| 498 | } | ||
| 499 | |||
| 500 | if (route->conditional) { | ||
| 501 | // FIXME for endpoint conditionals we need to check if they've been written | ||
| 502 | if (!route->conditional->satisfied()) { | ||
| 503 | if (debugRoutes && route->debug) { | ||
| 504 | qCDebug(controllers) << "Conditional failed"; | ||
| 505 | } | ||
| 506 | return true; | ||
| 507 | } | ||
| 508 | } | ||
| 509 | |||
| 510 | |||
| 511 | // Most endpoints can only be read once (though a given mapping can route them to | ||
| 512 | // multiple places). Consider... If the default is to wire the A button to JUMP | ||
| 513 | // and someone else wires it to CONTEXT_MENU, I don't want both to occur when | ||
| 514 | // I press the button. The exception is if I'm wiring a control back to itself | ||
| 515 | // in order to adjust my interface, like inverting the Y axis on an analog stick | ||
| 516 | if (!source->readable()) { | ||
| 517 | if (debugRoutes && route->debug) { | ||
| 518 | qCDebug(controllers) << "Source unreadable"; | ||
| 519 | } | ||
| 520 | return true; | ||
| 521 | } | ||
| 522 | |||
| 523 | auto destination = route->destination; | ||
| 524 | // THis could happen if the route destination failed to create | ||
| 525 | // FIXME: Maybe do not create the route if the destination failed and avoid this case ? | ||
| 526 | if (!destination) { | ||
| 527 | if (debugRoutes && route->debug) { | ||
| 528 | qCDebug(controllers) << "Bad Destination"; | ||
| 529 | } | ||
| 530 | return true; | ||
| 531 | } | ||
| 532 | |||
| 533 | if (!destination->writeable()) { | ||
| 534 | if (debugRoutes && route->debug) { | ||
| 535 | qCDebug(controllers) << "Destination unwritable"; | ||
| 536 | } | ||
| 537 | return true; | ||
| 538 | } | ||
| 539 | |||
| 540 | // Fetch the value, may have been overriden by previous loopback routes | ||
| 541 | if (source->isPose()) { | ||
| 542 | Pose value = getPose(source); | ||
| 543 | static const Pose IDENTITY_POSE { vec3(), quat() }; | ||
| 544 | if (debugRoutes && route->debug) { | ||
| 545 | if (!value.valid) { | ||
| 546 | qCDebug(controllers) << "Applying invalid pose"; | ||
| 547 | } else if (value == IDENTITY_POSE) { | ||
| 548 | qCDebug(controllers) << "Applying identity pose"; | ||
| 549 | } else { | ||
| 550 | qCDebug(controllers) << "Applying valid pose"; | ||
| 551 | } | ||
| 552 | } | ||
| 553 | // no filters yet for pose | ||
| 554 | destination->apply(value, source); | ||
| 555 | } else { | ||
| 556 | // Fetch the value, may have been overriden by previous loopback routes | ||
| 557 | float value = getValue(source); | ||
| 558 | |||
| 559 | if (debugRoutes && route->debug) { | ||
| 560 | qCDebug(controllers) << "Value was " << value; | ||
| 561 | } | ||
| 562 | // Apply each of the filters. | ||
| 563 | for (const auto& filter : route->filters) { | ||
| 564 | value = filter->apply(value); | ||
| 565 | } | ||
| 566 | |||
| 567 | if (debugRoutes && route->debug) { | ||
| 568 | qCDebug(controllers) << "Filtered value was " << value; | ||
| 569 | } | ||
| 570 | |||
| 571 | destination->apply(value, source); | ||
| 572 | } | ||
| 573 | return true; | ||
| 574 | } | ||
| 575 | |||
| 576 | Endpoint::Pointer UserInputMapper::endpointFor(const QJSValue& endpoint) { | ||
| 577 | if (endpoint.isNumber()) { | ||
| 578 | return endpointFor(Input(endpoint.toInt())); | ||
| 579 | } | ||
| 580 | |||
| 581 | if (endpoint.isCallable()) { | ||
| 582 | auto result = std::make_shared<JSEndpoint>(endpoint); | ||
| 583 | return result; | ||
| 584 | } | ||
| 585 | |||
| 586 | qWarning() << "Unsupported input type " << endpoint.toString(); | ||
| 587 | return Endpoint::Pointer(); | ||
| 588 | } | ||
| 589 | |||
| 590 | Endpoint::Pointer UserInputMapper::endpointFor(const QScriptValue& endpoint) { | ||
| 591 | if (endpoint.isNumber()) { | ||
| 592 | return endpointFor(Input(endpoint.toInt32())); | ||
| 593 | } | ||
| 594 | |||
| 595 | if (endpoint.isFunction()) { | ||
| 596 | auto result = std::make_shared<ScriptEndpoint>(endpoint); | ||
| 597 | return result; | ||
| 598 | } | ||
| 599 | |||
| 600 | if (endpoint.isArray()) { | ||
| 601 | int length = endpoint.property("length").toInteger(); | ||
| 602 | Endpoint::List children; | ||
| 603 | for (int i = 0; i < length; i++) { | ||
| 604 | QScriptValue arrayItem = endpoint.property(i); | ||
| 605 | Endpoint::Pointer destination = endpointFor(arrayItem); | ||
| 606 | if (!destination) { | ||
| 607 | return Endpoint::Pointer(); | ||
| 608 | } | ||
| 609 | children.push_back(destination); | ||
| 610 | } | ||
| 611 | return std::make_shared<AnyEndpoint>(children); | ||
| 612 | } | ||
| 613 | |||
| 614 | |||
| 615 | qWarning() << "Unsupported input type " << endpoint.toString(); | ||
| 616 | return Endpoint::Pointer(); | ||
| 617 | } | ||
| 618 | |||
| 619 | Endpoint::Pointer UserInputMapper::endpointFor(const Input& inputId) const { | ||
| 620 | Locker locker(_lock); | ||
| 621 | auto iterator = _endpointsByInput.find(inputId); | ||
| 622 | if (_endpointsByInput.end() == iterator) { | ||
| 623 | qWarning() << "Unknown input: " << QString::number(inputId.getID(), 16); | ||
| 624 | return Endpoint::Pointer(); | ||
| 625 | } | ||
| 626 | return iterator->second; | ||
| 627 | } | ||
| 628 | |||
| 629 | Endpoint::Pointer UserInputMapper::compositeEndpointFor(Endpoint::Pointer first, Endpoint::Pointer second) { | ||
| 630 | EndpointPair pair(first, second); | ||
| 631 | Endpoint::Pointer result; | ||
| 632 | auto iterator = _compositeEndpoints.find(pair); | ||
| 633 | if (_compositeEndpoints.end() == iterator) { | ||
| 634 | result = std::make_shared<CompositeEndpoint>(first, second); | ||
| 635 | _compositeEndpoints[pair] = result; | ||
| 636 | } else { | ||
| 637 | result = iterator->second; | ||
| 638 | } | ||
| 639 | return result; | ||
| 640 | } | ||
| 641 | |||
| 642 | |||
| 643 | Mapping::Pointer UserInputMapper::newMapping(const QString& mappingName) { | ||
| 644 | Locker locker(_lock); | ||
| 645 | if (_mappingsByName.count(mappingName)) { | ||
| 646 | qCWarning(controllers) << "Refusing to recreate mapping named " << mappingName; | ||
| 647 | } | ||
| 648 | qDebug() << "Creating new Mapping " << mappingName; | ||
| 649 | auto mapping = std::make_shared<Mapping>(mappingName); | ||
| 650 | _mappingsByName[mappingName] = mapping; | ||
| 651 | return mapping; | ||
| 652 | } | ||
| 653 | |||
| 654 | // FIXME handle asynchronous loading in the UserInputMapper | ||
| 655 | //QObject* ScriptingInterface::loadMapping(const QString& jsonUrl) { | ||
| 656 | // QObject* result = nullptr; | ||
| 657 | // auto request = ResourceManager::createResourceRequest(nullptr, QUrl(jsonUrl)); | ||
| 658 | // if (request) { | ||
| 659 | // QEventLoop eventLoop; | ||
| 660 | // request->setCacheEnabled(false); | ||
| 661 | // connect(request, &ResourceRequest::finished, &eventLoop, &QEventLoop::quit); | ||
| 662 | // request->send(); | ||
| 663 | // if (request->getState() != ResourceRequest::Finished) { | ||
| 664 | // eventLoop.exec(); | ||
| 665 | // } | ||
| 666 | // | ||
| 667 | // if (request->getResult() == ResourceRequest::Success) { | ||
| 668 | // result = parseMapping(QString(request->getData())); | ||
| 669 | // } else { | ||
| 670 | // qCWarning(controllers) << "Failed to load mapping url <" << jsonUrl << ">" << endl; | ||
| 671 | // } | ||
| 672 | // request->deleteLater(); | ||
| 673 | // } | ||
| 674 | // return result; | ||
| 675 | //} | ||
| 676 | |||
| 677 | void UserInputMapper::enableMapping(const QString& mappingName, bool enable) { | ||
| 678 | Locker locker(_lock); | ||
| 679 | qCDebug(controllers) << "Attempting to enable mapping " << mappingName; | ||
| 680 | auto iterator = _mappingsByName.find(mappingName); | ||
| 681 | if (_mappingsByName.end() == iterator) { | ||
| 682 | qCWarning(controllers) << "Request to enable / disable unknown mapping " << mappingName; | ||
| 683 | return; | ||
| 684 | } | ||
| 685 | |||
| 686 | auto mapping = iterator->second; | ||
| 687 | if (enable) { | ||
| 688 | enableMapping(mapping); | ||
| 689 | } else { | ||
| 690 | disableMapping(mapping); | ||
| 691 | } | ||
| 692 | } | ||
| 693 | |||
| 694 | float UserInputMapper::getValue(const Endpoint::Pointer& endpoint) { | ||
| 695 | return endpoint->value(); | ||
| 696 | } | ||
| 697 | |||
| 698 | float UserInputMapper::getValue(const Input& input) const { | ||
| 699 | Locker locker(_lock); | ||
| 700 | auto endpoint = endpointFor(input); | ||
| 701 | if (!endpoint) { | ||
| 702 | return 0; | ||
| 703 | } | ||
| 704 | return endpoint->value(); | ||
| 705 | } | ||
| 706 | |||
| 707 | Pose UserInputMapper::getPose(const Endpoint::Pointer& endpoint) { | ||
| 708 | if (!endpoint->isPose()) { | ||
| 709 | return Pose(); | ||
| 710 | } | ||
| 711 | return endpoint->pose(); | ||
| 712 | } | ||
| 713 | |||
| 714 | Pose UserInputMapper::getPose(const Input& input) const { | ||
| 715 | Locker locker(_lock); | ||
| 716 | auto endpoint = endpointFor(input); | ||
| 717 | if (!endpoint) { | ||
| 718 | return Pose(); | ||
| 719 | } | ||
| 720 | return getPose(endpoint); | ||
| 721 | } | ||
| 722 | |||
| 723 | Mapping::Pointer UserInputMapper::loadMapping(const QString& jsonFile) { | ||
| 724 | Locker locker(_lock); | ||
| 725 | if (jsonFile.isEmpty()) { | ||
| 726 | return Mapping::Pointer(); | ||
| 727 | } | ||
| 728 | QString json; | ||
| 729 | { | ||
| 730 | QFile file(jsonFile); | ||
| 731 | if (file.open(QFile::ReadOnly | QFile::Text)) { | ||
| 732 | json = QTextStream(&file).readAll(); | ||
| 733 | } | ||
| 734 | file.close(); | ||
| 735 | } | ||
| 736 | return parseMapping(json); | ||
| 737 | } | ||
| 738 | |||
| 739 | |||
| 740 | |||
| 741 | static const QString JSON_NAME = QStringLiteral("name"); | ||
| 742 | static const QString JSON_CHANNELS = QStringLiteral("channels"); | ||
| 743 | static const QString JSON_CHANNEL_FROM = QStringLiteral("from"); | ||
| 744 | static const QString JSON_CHANNEL_DEBUG = QStringLiteral("debug"); | ||
| 745 | static const QString JSON_CHANNEL_WHEN = QStringLiteral("when"); | ||
| 746 | static const QString JSON_CHANNEL_TO = QStringLiteral("to"); | ||
| 747 | static const QString JSON_CHANNEL_FILTERS = QStringLiteral("filters"); | ||
| 748 | |||
| 749 | Endpoint::Pointer UserInputMapper::parseEndpoint(const QJsonValue& value) { | ||
| 750 | Endpoint::Pointer result; | ||
| 751 | if (value.isString()) { | ||
| 752 | auto input = findDeviceInput(value.toString()); | ||
| 753 | result = endpointFor(input); | ||
| 754 | } else if (value.isArray()) { | ||
| 755 | return parseAny(value); | ||
| 756 | } else if (value.isObject()) { | ||
| 757 | auto axisEndpoint = parseAxis(value); | ||
| 758 | if (axisEndpoint) { | ||
| 759 | return axisEndpoint; | ||
| 760 | } | ||
| 761 | // if we have other types of endpoints that are objects, follow the axisEndpoint example, and place them here | ||
| 762 | |||
| 763 | // Endpoint is defined as an object, we expect a js function then | ||
| 764 | return Endpoint::Pointer(); | ||
| 765 | } | ||
| 766 | |||
| 767 | if (!result) { | ||
| 768 | qWarning() << "Invalid endpoint definition " << value; | ||
| 769 | } | ||
| 770 | return result; | ||
| 771 | } | ||
| 772 | |||
| 773 | |||
| 774 | Conditional::Pointer UserInputMapper::conditionalFor(const QJSValue& condition) { | ||
| 775 | return Conditional::Pointer(); | ||
| 776 | } | ||
| 777 | |||
| 778 | Conditional::Pointer UserInputMapper::conditionalFor(const QScriptValue& condition) { | ||
| 779 | if (condition.isArray()) { | ||
| 780 | int length = condition.property("length").toInteger(); | ||
| 781 | Conditional::List children; | ||
| 782 | for (int i = 0; i < length; i++) { | ||
| 783 | Conditional::Pointer destination = conditionalFor(condition.property(i)); | ||
| 784 | if (!destination) { | ||
| 785 | return Conditional::Pointer(); | ||
| 786 | } | ||
| 787 | children.push_back(destination); | ||
| 788 | } | ||
| 789 | return std::make_shared<AndConditional>(children); | ||
| 790 | } | ||
| 791 | |||
| 792 | if (condition.isNumber()) { | ||
| 793 | return conditionalFor(Input(condition.toInt32())); | ||
| 794 | } | ||
| 795 | |||
| 796 | if (condition.isFunction()) { | ||
| 797 | return std::make_shared<ScriptConditional>(condition); | ||
| 798 | } | ||
| 799 | |||
| 800 | qWarning() << "Unsupported conditional type " << condition.toString(); | ||
| 801 | return Conditional::Pointer(); | ||
| 802 | } | ||
| 803 | |||
| 804 | Conditional::Pointer UserInputMapper::conditionalFor(const Input& inputId) const { | ||
| 805 | Locker locker(_lock); | ||
| 806 | auto iterator = _endpointsByInput.find(inputId); | ||
| 807 | if (_endpointsByInput.end() == iterator) { | ||
| 808 | qWarning() << "Unknown input: " << QString::number(inputId.getID(), 16); | ||
| 809 | return Conditional::Pointer(); | ||
| 810 | } | ||
| 811 | return std::make_shared<EndpointConditional>(iterator->second); | ||
| 812 | } | ||
| 813 | |||
| 814 | Conditional::Pointer UserInputMapper::parseConditional(const QJsonValue& value) { | ||
| 815 | if (value.isArray()) { | ||
| 816 | // Support "when" : [ "GamePad.RB", "GamePad.LB" ] | ||
| 817 | Conditional::List children; | ||
| 818 | for (auto arrayItem : value.toArray()) { | ||
| 819 | Conditional::Pointer childConditional = parseConditional(arrayItem); | ||
| 820 | if (!childConditional) { | ||
| 821 | return Conditional::Pointer(); | ||
| 822 | } | ||
| 823 | children.push_back(childConditional); | ||
| 824 | } | ||
| 825 | return std::make_shared<AndConditional>(children); | ||
| 826 | } else if (value.isString()) { | ||
| 827 | // Support "when" : "GamePad.RB" | ||
| 828 | auto input = findDeviceInput(value.toString()); | ||
| 829 | auto endpoint = endpointFor(input); | ||
| 830 | if (!endpoint) { | ||
| 831 | return Conditional::Pointer(); | ||
| 832 | } | ||
| 833 | |||
| 834 | return std::make_shared<EndpointConditional>(endpoint); | ||
| 835 | } | ||
| 836 | |||
| 837 | return Conditional::parse(value); | ||
| 838 | } | ||
| 839 | |||
| 840 | Filter::Pointer UserInputMapper::parseFilter(const QJsonValue& value) { | ||
| 841 | Filter::Pointer result; | ||
| 842 | if (value.isString()) { | ||
| 843 | result = Filter::getFactory().create(value.toString()); | ||
| 844 | } else if (value.isObject()) { | ||
| 845 | result = Filter::parse(value.toObject()); | ||
| 846 | } | ||
| 847 | |||
| 848 | if (!result) { | ||
| 849 | qWarning() << "Invalid filter definition " << value; | ||
| 850 | } | ||
| 851 | |||
| 852 | return result; | ||
| 853 | } | ||
| 854 | |||
| 855 | Filter::List UserInputMapper::parseFilters(const QJsonValue& value) { | ||
| 856 | if (value.isNull()) { | ||
| 857 | return Filter::List(); | ||
| 858 | } | ||
| 859 | |||
| 860 | if (value.isArray()) { | ||
| 861 | Filter::List result; | ||
| 862 | auto filtersArray = value.toArray(); | ||
| 863 | for (auto filterValue : filtersArray) { | ||
| 864 | Filter::Pointer filter = parseFilter(filterValue); | ||
| 865 | if (!filter) { | ||
| 866 | return Filter::List(); | ||
| 867 | } | ||
| 868 | result.push_back(filter); | ||
| 869 | } | ||
| 870 | return result; | ||
| 871 | } | ||
| 872 | |||
| 873 | Filter::Pointer filter = parseFilter(value); | ||
| 874 | if (!filter) { | ||
| 875 | return Filter::List(); | ||
| 876 | } | ||
| 877 | return Filter::List({ filter }); | ||
| 878 | } | ||
| 879 | |||
| 880 | Endpoint::Pointer UserInputMapper::parseDestination(const QJsonValue& value) { | ||
| 881 | if (value.isArray()) { | ||
| 882 | ArrayEndpoint::Pointer result = std::make_shared<ArrayEndpoint>(); | ||
| 883 | for (auto arrayItem : value.toArray()) { | ||
| 884 | Endpoint::Pointer destination = parseEndpoint(arrayItem); | ||
| 885 | if (!destination) { | ||
| 886 | return Endpoint::Pointer(); | ||
| 887 | } | ||
| 888 | result->_children.push_back(destination); | ||
| 889 | } | ||
| 890 | return result; | ||
| 891 | } | ||
| 892 | |||
| 893 | return parseEndpoint(value); | ||
| 894 | } | ||
| 895 | |||
| 896 | Endpoint::Pointer UserInputMapper::parseAxis(const QJsonValue& value) { | ||
| 897 | if (value.isObject()) { | ||
| 898 | auto object = value.toObject(); | ||
| 899 | if (object.contains("makeAxis")) { | ||
| 900 | auto axisValue = object.value("makeAxis"); | ||
| 901 | if (axisValue.isArray()) { | ||
| 902 | auto axisArray = axisValue.toArray(); | ||
| 903 | static const int AXIS_ARRAY_SIZE = 2; // axis can only have 2 children | ||
| 904 | if (axisArray.size() == AXIS_ARRAY_SIZE) { | ||
| 905 | Endpoint::Pointer first = parseEndpoint(axisArray.first()); | ||
| 906 | Endpoint::Pointer second = parseEndpoint(axisArray.last()); | ||
| 907 | if (first && second) { | ||
| 908 | return std::make_shared<CompositeEndpoint>(first, second); | ||
| 909 | } | ||
| 910 | } | ||
| 911 | } | ||
| 912 | } | ||
| 913 | } | ||
| 914 | return Endpoint::Pointer(); | ||
| 915 | } | ||
| 916 | |||
| 917 | Endpoint::Pointer UserInputMapper::parseAny(const QJsonValue& value) { | ||
| 918 | if (value.isArray()) { | ||
| 919 | Endpoint::List children; | ||
| 920 | for (auto arrayItem : value.toArray()) { | ||
| 921 | Endpoint::Pointer destination = parseEndpoint(arrayItem); | ||
| 922 | if (!destination) { | ||
| 923 | return Endpoint::Pointer(); | ||
| 924 | } | ||
| 925 | children.push_back(destination); | ||
| 926 | } | ||
| 927 | return std::make_shared<AnyEndpoint>(children); | ||
| 928 | } | ||
| 929 | return Endpoint::Pointer(); | ||
| 930 | } | ||
| 931 | |||
| 932 | Endpoint::Pointer UserInputMapper::parseSource(const QJsonValue& value) { | ||
| 933 | if (value.isObject()) { | ||
| 934 | auto axisEndpoint = parseAxis(value); | ||
| 935 | if (axisEndpoint) { | ||
| 936 | return axisEndpoint; | ||
| 937 | } | ||
| 938 | // if we have other types of endpoints that are objects, follow the axisEndpoint example, and place them here | ||
| 939 | } else if (value.isArray()) { | ||
| 940 | return parseAny(value); | ||
| 941 | } | ||
| 942 | return parseEndpoint(value); | ||
| 943 | } | ||
| 944 | |||
| 945 | Route::Pointer UserInputMapper::parseRoute(const QJsonValue& value) { | ||
| 946 | if (!value.isObject()) { | ||
| 947 | return Route::Pointer(); | ||
| 948 | } | ||
| 949 | |||
| 950 | auto obj = value.toObject(); | ||
| 951 | Route::Pointer result = std::make_shared<Route>(); | ||
| 952 | |||
| 953 | result->json = QString(QJsonDocument(obj).toJson()); | ||
| 954 | result->source = parseSource(obj[JSON_CHANNEL_FROM]); | ||
| 955 | result->debug = obj[JSON_CHANNEL_DEBUG].toBool(); | ||
| 956 | if (!result->source) { | ||
| 957 | qWarning() << "Invalid route source " << obj[JSON_CHANNEL_FROM]; | ||
| 958 | return Route::Pointer(); | ||
| 959 | } | ||
| 960 | |||
| 961 | |||
| 962 | result->destination = parseDestination(obj[JSON_CHANNEL_TO]); | ||
| 963 | if (!result->destination) { | ||
| 964 | qWarning() << "Invalid route destination " << obj[JSON_CHANNEL_TO]; | ||
| 965 | return Route::Pointer(); | ||
| 966 | } | ||
| 967 | |||
| 968 | if (result->source == result->destination) { | ||
| 969 | qWarning() << "Loopback routes not supported " << obj; | ||
| 970 | return Route::Pointer(); | ||
| 971 | } | ||
| 972 | |||
| 973 | if (obj.contains(JSON_CHANNEL_WHEN)) { | ||
| 974 | auto conditionalsValue = obj[JSON_CHANNEL_WHEN]; | ||
| 975 | result->conditional = parseConditional(conditionalsValue); | ||
| 976 | if (!result->conditional) { | ||
| 977 | qWarning() << "Invalid route conditionals " << conditionalsValue; | ||
| 978 | return Route::Pointer(); | ||
| 979 | } | ||
| 980 | } | ||
| 981 | |||
| 982 | if (obj.contains(JSON_CHANNEL_FILTERS)) { | ||
| 983 | auto filtersValue = obj[JSON_CHANNEL_FILTERS]; | ||
| 984 | result->filters = parseFilters(filtersValue); | ||
| 985 | if (result->filters.empty()) { | ||
| 986 | qWarning() << "Invalid route filters " << filtersValue; | ||
| 987 | return Route::Pointer(); | ||
| 988 | } | ||
| 989 | } | ||
| 990 | |||
| 991 | return result; | ||
| 992 | } | ||
| 993 | |||
| 994 | Mapping::Pointer UserInputMapper::parseMapping(const QJsonValue& json) { | ||
| 995 | if (!json.isObject()) { | ||
| 996 | return Mapping::Pointer(); | ||
| 997 | } | ||
| 998 | |||
| 999 | auto obj = json.toObject(); | ||
| 1000 | auto mapping = std::make_shared<Mapping>("default"); | ||
| 1001 | mapping->name = obj[JSON_NAME].toString(); | ||
| 1002 | const auto& jsonChannels = obj[JSON_CHANNELS].toArray(); | ||
| 1003 | for (const auto& channelIt : jsonChannels) { | ||
| 1004 | Route::Pointer route = parseRoute(channelIt); | ||
| 1005 | if (!route) { | ||
| 1006 | qWarning() << "Couldn't parse route"; | ||
| 1007 | continue; | ||
| 1008 | } | ||
| 1009 | mapping->routes.push_back(route); | ||
| 1010 | } | ||
| 1011 | return mapping; | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | Mapping::Pointer UserInputMapper::parseMapping(const QString& json) { | ||
| 1015 | Mapping::Pointer result; | ||
| 1016 | QJsonObject obj; | ||
| 1017 | QJsonParseError error; | ||
| 1018 | QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8(), &error); | ||
| 1019 | // check validity of the document | ||
| 1020 | if (doc.isNull()) { | ||
| 1021 | qDebug() << "Invalid JSON...\n"; | ||
| 1022 | qDebug() << error.errorString(); | ||
| 1023 | qDebug() << "JSON was:\n" << json << endl; | ||
| 1024 | return Mapping::Pointer(); | ||
| 1025 | } | ||
| 1026 | |||
| 1027 | if (!doc.isObject()) { | ||
| 1028 | qWarning() << "Mapping json Document is not an object" << endl; | ||
| 1029 | qDebug() << "JSON was:\n" << json << endl; | ||
| 1030 | return Mapping::Pointer(); | ||
| 1031 | } | ||
| 1032 | return parseMapping(doc.object()); | ||
| 1033 | } | ||
| 1034 | |||
| 1035 | template <typename T> | ||
| 1036 | bool hasDebuggableRoute(const T& routes) { | ||
| 1037 | for (auto route : routes) { | ||
| 1038 | if (route->debug) { | ||
| 1039 | return true; | ||
| 1040 | } | ||
| 1041 | } | ||
| 1042 | return false; | ||
| 1043 | } | ||
| 1044 | |||
| 1045 | |||
| 1046 | void UserInputMapper::enableMapping(const Mapping::Pointer& mapping) { | ||
| 1047 | Locker locker(_lock); | ||
| 1048 | // New routes for a device get injected IN FRONT of existing routes. Routes | ||
| 1049 | // are processed in order so this ensures that the standard -> action processing | ||
| 1050 | // takes place after all of the hardware -> standard or hardware -> action processing | ||
| 1051 | // because standard -> action is the first set of routes added. | ||
| 1052 | Route::List standardRoutes = mapping->routes; | ||
| 1053 | standardRoutes.remove_if([](const Route::Pointer& value) { | ||
| 1054 | return (value->source->getInput().device != STANDARD_DEVICE); | ||
| 1055 | }); | ||
| 1056 | _standardRoutes.insert(_standardRoutes.begin(), standardRoutes.begin(), standardRoutes.end()); | ||
| 1057 | |||
| 1058 | Route::List deviceRoutes = mapping->routes; | ||
| 1059 | deviceRoutes.remove_if([](const Route::Pointer& value) { | ||
| 1060 | return (value->source->getInput().device == STANDARD_DEVICE); | ||
| 1061 | }); | ||
| 1062 | _deviceRoutes.insert(_deviceRoutes.begin(), deviceRoutes.begin(), deviceRoutes.end()); | ||
| 1063 | |||
| 1064 | if (!debuggableRoutes) { | ||
| 1065 | debuggableRoutes = hasDebuggableRoute(_deviceRoutes) || hasDebuggableRoute(_standardRoutes); | ||
| 1066 | } | ||
| 1067 | } | ||
| 1068 | |||
| 1069 | void UserInputMapper::disableMapping(const Mapping::Pointer& mapping) { | ||
| 1070 | Locker locker(_lock); | ||
| 1071 | const auto& deviceRoutes = mapping->routes; | ||
| 1072 | std::set<Route::Pointer> routeSet(deviceRoutes.begin(), deviceRoutes.end()); | ||
| 1073 | _deviceRoutes.remove_if([&](const Route::Pointer& value){ | ||
| 1074 | return routeSet.count(value) != 0; | ||
| 1075 | }); | ||
| 1076 | _standardRoutes.remove_if([&](const Route::Pointer& value) { | ||
| 1077 | return routeSet.count(value) != 0; | ||
| 1078 | }); | ||
| 1079 | |||
| 1080 | if (debuggableRoutes) { | ||
| 1081 | debuggableRoutes = hasDebuggableRoute(_deviceRoutes) || hasDebuggableRoute(_standardRoutes); | ||
| 1082 | } | ||
| 1083 | } | ||
| 1084 | |||
| 1085 | } | ||
| 1086 | |||
| @@ -0,0 +1,197 @@ | |||
| 1 | // | ||
| 2 | // Created by Sam Gateau on 4/27/15. | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | #ifndef hifi_UserInputMapper_h | ||
| 11 | #define hifi_UserInputMapper_h | ||
| 12 | |||
| 13 | #include <glm/glm.hpp> | ||
| 14 | |||
| 15 | #include <unordered_set> | ||
| 16 | #include <functional> | ||
| 17 | #include <memory> | ||
| 18 | #include <mutex> | ||
| 19 | |||
| 20 | #include <QtQml/QJSValue> | ||
| 21 | #include <QtScript/QScriptValue> | ||
| 22 | |||
| 23 | #include <DependencyManager.h> | ||
| 24 | #include <RegisteredMetaTypes.h> | ||
| 25 | |||
| 26 | #include "Forward.h" | ||
| 27 | #include "Pose.h" | ||
| 28 | #include "Input.h" | ||
| 29 | #include "InputDevice.h" | ||
| 30 | #include "DeviceProxy.h" | ||
| 31 | #include "StandardControls.h" | ||
| 32 | #include "Actions.h" | ||
| 33 | |||
| 34 | namespace controller { | ||
| 35 | |||
| 36 | class RouteBuilderProxy; | ||
| 37 | class MappingBuilderProxy; | ||
| 38 | |||
| 39 | class UserInputMapper : public QObject, public Dependency { | ||
| 40 | Q_OBJECT | ||
| 41 | SINGLETON_DEPENDENCY | ||
| 42 | Q_ENUMS(Action) | ||
| 43 | |||
| 44 | public: | ||
| 45 | // FIXME move to unordered set / map | ||
| 46 | using EndpointToInputMap = std::map<EndpointPointer, Input>; | ||
| 47 | using MappingNameMap = std::map<QString, MappingPointer>; | ||
| 48 | using MappingDeviceMap = std::map<uint16_t, MappingPointer>; | ||
| 49 | using MappingStack = std::list<MappingPointer>; | ||
| 50 | using InputToEndpointMap = std::map<Input, EndpointPointer>; | ||
| 51 | using EndpointSet = std::unordered_set<EndpointPointer>; | ||
| 52 | using EndpointPair = std::pair<EndpointPointer, EndpointPointer>; | ||
| 53 | using EndpointPairMap = std::map<EndpointPair, EndpointPointer>; | ||
| 54 | using DevicesMap = std::map<int, InputDevice::Pointer>; | ||
| 55 | using uint16 = uint16_t; | ||
| 56 | using uint32 = uint32_t; | ||
| 57 | |||
| 58 | static const uint16_t ACTIONS_DEVICE; | ||
| 59 | static const uint16_t STANDARD_DEVICE; | ||
| 60 | |||
| 61 | UserInputMapper(); | ||
| 62 | virtual ~UserInputMapper(); | ||
| 63 | |||
| 64 | |||
| 65 | static void registerControllerTypes(QScriptEngine* engine); | ||
| 66 | |||
| 67 | void registerDevice(InputDevice::Pointer device); | ||
| 68 | InputDevice::Pointer getDevice(const Input& input); | ||
| 69 | QString getDeviceName(uint16 deviceID); | ||
| 70 | |||
| 71 | Input::NamedVector getAvailableInputs(uint16 deviceID) const; | ||
| 72 | Input::NamedVector getActionInputs() const { return getAvailableInputs(ACTIONS_DEVICE); } | ||
| 73 | Input::NamedVector getStandardInputs() const { return getAvailableInputs(STANDARD_DEVICE); } | ||
| 74 | |||
| 75 | int findDevice(QString name) const; | ||
| 76 | QVector<QString> getDeviceNames(); | ||
| 77 | Input findDeviceInput(const QString& inputName) const; | ||
| 78 | |||
| 79 | QVector<Action> getAllActions() const; | ||
| 80 | QString getActionName(Action action) const; | ||
| 81 | float getActionState(Action action) const { return _actionStates[toInt(action)]; } | ||
| 82 | Pose getPoseState(Action action) const { return _poseStates[toInt(action)]; } | ||
| 83 | int findAction(const QString& actionName) const; | ||
| 84 | QVector<QString> getActionNames() const; | ||
| 85 | Input inputFromAction(Action action) const { return getActionInputs()[toInt(action)].first; } | ||
| 86 | |||
| 87 | void setActionState(Action action, float value) { _actionStates[toInt(action)] = value; } | ||
| 88 | void deltaActionState(Action action, float delta) { _actionStates[toInt(action)] += delta; } | ||
| 89 | void setActionState(Action action, const Pose& value) { _poseStates[toInt(action)] = value; } | ||
| 90 | |||
| 91 | static Input makeStandardInput(controller::StandardButtonChannel button); | ||
| 92 | static Input makeStandardInput(controller::StandardAxisChannel axis); | ||
| 93 | static Input makeStandardInput(controller::StandardPoseChannel pose); | ||
| 94 | |||
| 95 | void removeDevice(int device); | ||
| 96 | |||
| 97 | // Update means go grab all the device input channels and update the output channel values | ||
| 98 | void update(float deltaTime); | ||
| 99 | |||
| 100 | void setSensorToWorldMat(glm::mat4 sensorToWorldMat) { _sensorToWorldMat = sensorToWorldMat; } | ||
| 101 | glm::mat4 getSensorToWorldMat() { return _sensorToWorldMat; } | ||
| 102 | |||
| 103 | DevicesMap getDevices() { return _registeredDevices; } | ||
| 104 | uint16 getStandardDeviceID() const { return STANDARD_DEVICE; } | ||
| 105 | InputDevice::Pointer getStandardDevice() { return _registeredDevices[getStandardDeviceID()]; } | ||
| 106 | |||
| 107 | MappingPointer newMapping(const QString& mappingName); | ||
| 108 | MappingPointer parseMapping(const QString& json); | ||
| 109 | MappingPointer loadMapping(const QString& jsonFile); | ||
| 110 | |||
| 111 | void loadDefaultMapping(uint16 deviceID); | ||
| 112 | void enableMapping(const QString& mappingName, bool enable = true); | ||
| 113 | float getValue(const Input& input) const; | ||
| 114 | Pose getPose(const Input& input) const; | ||
| 115 | |||
| 116 | signals: | ||
| 117 | void actionEvent(int action, float state); | ||
| 118 | void inputEvent(int input, float state); | ||
| 119 | void hardwareChanged(); | ||
| 120 | |||
| 121 | protected: | ||
| 122 | // GetFreeDeviceID should be called before registering a device to use an ID not used by a different device. | ||
| 123 | uint16 getFreeDeviceID() { return _nextFreeDeviceID++; } | ||
| 124 | DevicesMap _registeredDevices; | ||
| 125 | uint16 _nextFreeDeviceID = STANDARD_DEVICE + 1; | ||
| 126 | |||
| 127 | std::vector<float> _actionStates = std::vector<float>(toInt(Action::NUM_ACTIONS), 0.0f); | ||
| 128 | std::vector<float> _actionScales = std::vector<float>(toInt(Action::NUM_ACTIONS), 1.0f); | ||
| 129 | std::vector<float> _lastActionStates = std::vector<float>(toInt(Action::NUM_ACTIONS), 0.0f); | ||
| 130 | std::vector<Pose> _poseStates = std::vector<Pose>(toInt(Action::NUM_ACTIONS)); | ||
| 131 | std::vector<float> _lastStandardStates = std::vector<float>(); | ||
| 132 | |||
| 133 | glm::mat4 _sensorToWorldMat; | ||
| 134 | |||
| 135 | int recordDeviceOfType(const QString& deviceName); | ||
| 136 | QHash<const QString&, int> _deviceCounts; | ||
| 137 | |||
| 138 | static float getValue(const EndpointPointer& endpoint); | ||
| 139 | static Pose getPose(const EndpointPointer& endpoint); | ||
| 140 | |||
| 141 | friend class RouteBuilderProxy; | ||
| 142 | friend class MappingBuilderProxy; | ||
| 143 | |||
| 144 | void runMappings(); | ||
| 145 | |||
| 146 | static void applyRoutes(const RouteList& route); | ||
| 147 | static bool applyRoute(const RoutePointer& route, bool force = false); | ||
| 148 | void enableMapping(const MappingPointer& mapping); | ||
| 149 | void disableMapping(const MappingPointer& mapping); | ||
| 150 | EndpointPointer endpointFor(const QJSValue& endpoint); | ||
| 151 | EndpointPointer endpointFor(const QScriptValue& endpoint); | ||
| 152 | EndpointPointer endpointFor(const Input& endpoint) const; | ||
| 153 | EndpointPointer compositeEndpointFor(EndpointPointer first, EndpointPointer second); | ||
| 154 | ConditionalPointer conditionalFor(const QJSValue& endpoint); | ||
| 155 | ConditionalPointer conditionalFor(const QScriptValue& endpoint); | ||
| 156 | ConditionalPointer conditionalFor(const Input& endpoint) const; | ||
| 157 | |||
| 158 | MappingPointer parseMapping(const QJsonValue& json); | ||
| 159 | RoutePointer parseRoute(const QJsonValue& value); | ||
| 160 | EndpointPointer parseDestination(const QJsonValue& value); | ||
| 161 | EndpointPointer parseSource(const QJsonValue& value); | ||
| 162 | EndpointPointer parseAxis(const QJsonValue& value); | ||
| 163 | EndpointPointer parseAny(const QJsonValue& value); | ||
| 164 | EndpointPointer parseEndpoint(const QJsonValue& value); | ||
| 165 | ConditionalPointer parseConditional(const QJsonValue& value); | ||
| 166 | |||
| 167 | static FilterPointer parseFilter(const QJsonValue& value); | ||
| 168 | static FilterList parseFilters(const QJsonValue& value); | ||
| 169 | |||
| 170 | InputToEndpointMap _endpointsByInput; | ||
| 171 | EndpointToInputMap _inputsByEndpoint; | ||
| 172 | EndpointPairMap _compositeEndpoints; | ||
| 173 | |||
| 174 | MappingNameMap _mappingsByName; | ||
| 175 | MappingDeviceMap _mappingsByDevice; | ||
| 176 | |||
| 177 | RouteList _deviceRoutes; | ||
| 178 | RouteList _standardRoutes; | ||
| 179 | |||
| 180 | using Locker = std::unique_lock<std::recursive_mutex>; | ||
| 181 | |||
| 182 | mutable std::recursive_mutex _lock; | ||
| 183 | }; | ||
| 184 | |||
| 185 | } | ||
| 186 | |||
| 187 | Q_DECLARE_METATYPE(controller::Input::NamedPair) | ||
| 188 | Q_DECLARE_METATYPE(controller::Pose) | ||
| 189 | Q_DECLARE_METATYPE(QVector<controller::Input::NamedPair>) | ||
| 190 | Q_DECLARE_METATYPE(controller::Input) | ||
| 191 | Q_DECLARE_METATYPE(controller::Action) | ||
| 192 | Q_DECLARE_METATYPE(QVector<controller::Action>) | ||
| 193 | |||
| 194 | // Cheating. | ||
| 195 | using UserInputMapper = controller::UserInputMapper; | ||
| 196 | |||
| 197 | #endif // hifi_UserInputMapper_h | ||
| @@ -0,0 +1,21 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/20 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #include "Conditional.h" | ||
| 10 | |||
| 11 | #include <QtCore/QJsonValue> | ||
| 12 | |||
| 13 | #include "Endpoint.h" | ||
| 14 | |||
| 15 | namespace controller { | ||
| 16 | |||
| 17 | Conditional::Pointer Conditional::parse(const QJsonValue& json) { | ||
| 18 | return Conditional::Pointer(); | ||
| 19 | } | ||
| 20 | |||
| 21 | } | ||
| @@ -0,0 +1,54 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/20 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | #ifndef hifi_Controllers_Conditional_h | ||
| 11 | #define hifi_Controllers_Conditional_h | ||
| 12 | |||
| 13 | #include <list> | ||
| 14 | #include <memory> | ||
| 15 | |||
| 16 | #include <QtCore/QString> | ||
| 17 | |||
| 18 | #include <shared/Factory.h> | ||
| 19 | |||
| 20 | class QJsonValue; | ||
| 21 | |||
| 22 | namespace controller { | ||
| 23 | /* | ||
| 24 | * encapsulates a source, destination and filters to apply | ||
| 25 | */ | ||
| 26 | class Conditional { | ||
| 27 | public: | ||
| 28 | using Pointer = std::shared_ptr<Conditional>; | ||
| 29 | using List = std::list<Pointer>; | ||
| 30 | using Factory = hifi::SimpleFactory<Conditional, QString>; | ||
| 31 | using Lambda = std::function<bool()>; | ||
| 32 | |||
| 33 | virtual bool satisfied() = 0; | ||
| 34 | virtual bool parseParameters(const QJsonValue& parameters) { return true; } | ||
| 35 | |||
| 36 | static Pointer parse(const QJsonValue& json); | ||
| 37 | static void registerBuilder(const QString& name, Factory::Builder builder); | ||
| 38 | static Factory& getFactory() { return _factory; } | ||
| 39 | protected: | ||
| 40 | static Factory _factory; | ||
| 41 | }; | ||
| 42 | |||
| 43 | } | ||
| 44 | |||
| 45 | #define REGISTER_CONDITIONAL_CLASS(classEntry) \ | ||
| 46 | private: \ | ||
| 47 | using Registrar = Conditional::Factory::Registrar<classEntry>; \ | ||
| 48 | static Registrar _registrar; | ||
| 49 | |||
| 50 | #define REGISTER_CONDITIONAL_CLASS_INSTANCE(classEntry, className) \ | ||
| 51 | classEntry::Registrar classEntry::_registrar(className, Conditional::getFactory()); | ||
| 52 | |||
| 53 | |||
| 54 | #endif | ||
| @@ -0,0 +1,14 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/09 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #include "Endpoint.h" | ||
| 10 | |||
| 11 | namespace controller { | ||
| 12 | |||
| 13 | } | ||
| 14 | |||
| @@ -0,0 +1,89 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/09 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | #ifndef hifi_Controllers_Endpoint_h | ||
| 11 | #define hifi_Controllers_Endpoint_h | ||
| 12 | |||
| 13 | #include <list> | ||
| 14 | #include <memory> | ||
| 15 | #include <functional> | ||
| 16 | |||
| 17 | #include <QtCore/QObject> | ||
| 18 | |||
| 19 | #include "../Input.h" | ||
| 20 | #include "../Pose.h" | ||
| 21 | |||
| 22 | class QScriptValue; | ||
| 23 | |||
| 24 | namespace controller { | ||
| 25 | /* | ||
| 26 | * Encapsulates a particular input / output, | ||
| 27 | * i.e. Hydra.Button0, Standard.X, Action.Yaw | ||
| 28 | */ | ||
| 29 | class Endpoint : public QObject { | ||
| 30 | Q_OBJECT; | ||
| 31 | public: | ||
| 32 | using Pointer = std::shared_ptr<Endpoint>; | ||
| 33 | using List = std::list<Pointer>; | ||
| 34 | using Pair = std::pair<Pointer, Pointer>; | ||
| 35 | using ReadLambda = std::function<float()>; | ||
| 36 | using WriteLambda = std::function<void(float)>; | ||
| 37 | |||
| 38 | Endpoint(const Input& input) : _input(input) {} | ||
| 39 | virtual float value() = 0; | ||
| 40 | virtual void apply(float value, const Pointer& source) = 0; | ||
| 41 | virtual Pose pose() { return Pose(); } | ||
| 42 | virtual void apply(const Pose& value, const Pointer& source) {} | ||
| 43 | virtual const bool isPose() { return _input.isPose(); } | ||
| 44 | |||
| 45 | virtual bool writeable() const { return true; } | ||
| 46 | virtual bool readable() const { return true; } | ||
| 47 | virtual void reset() { } | ||
| 48 | |||
| 49 | const Input& getInput() { return _input; } | ||
| 50 | |||
| 51 | protected: | ||
| 52 | Input _input; | ||
| 53 | }; | ||
| 54 | |||
| 55 | class LambdaEndpoint : public Endpoint { | ||
| 56 | public: | ||
| 57 | LambdaEndpoint(ReadLambda readLambda, WriteLambda writeLambda = [](float) {}) | ||
| 58 | : Endpoint(Input::INVALID_INPUT), _readLambda(readLambda), _writeLambda(writeLambda) { } | ||
| 59 | |||
| 60 | virtual float value() override { return _readLambda(); } | ||
| 61 | virtual void apply(float value, const Pointer& source) override { _writeLambda(value); } | ||
| 62 | |||
| 63 | private: | ||
| 64 | ReadLambda _readLambda; | ||
| 65 | WriteLambda _writeLambda; | ||
| 66 | }; | ||
| 67 | |||
| 68 | |||
| 69 | class VirtualEndpoint : public Endpoint { | ||
| 70 | public: | ||
| 71 | VirtualEndpoint(const Input& id = Input::INVALID_INPUT) | ||
| 72 | : Endpoint(id) { | ||
| 73 | } | ||
| 74 | |||
| 75 | virtual float value() override { return _currentValue; } | ||
| 76 | virtual void apply(float value, const Pointer& source) override { _currentValue = value; } | ||
| 77 | |||
| 78 | virtual Pose pose() override { return _currentPose; } | ||
| 79 | virtual void apply(const Pose& value, const Pointer& source) override { | ||
| 80 | _currentPose = value; | ||
| 81 | } | ||
| 82 | protected: | ||
| 83 | float _currentValue { 0.0f }; | ||
| 84 | Pose _currentPose {}; | ||
| 85 | }; | ||
| 86 | |||
| 87 | } | ||
| 88 | |||
| 89 | #endif | ||
| @@ -0,0 +1,196 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/09 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #include "Filter.h" | ||
| 10 | |||
| 11 | #include <QtCore/QObject> | ||
| 12 | #include <QtScript/QScriptValue> | ||
| 13 | |||
| 14 | #include <QtCore/QJsonObject> | ||
| 15 | #include <QtCore/QJsonArray> | ||
| 16 | |||
| 17 | #include <SharedUtil.h> | ||
| 18 | |||
| 19 | #include "filters/ClampFilter.h" | ||
| 20 | #include "filters/ConstrainToIntegerFilter.h" | ||
| 21 | #include "filters/ConstrainToPositiveIntegerFilter.h" | ||
| 22 | #include "filters/DeadZoneFilter.h" | ||
| 23 | #include "filters/HysteresisFilter.h" | ||
| 24 | #include "filters/InvertFilter.h" | ||
| 25 | #include "filters/PulseFilter.h" | ||
| 26 | #include "filters/ScaleFilter.h" | ||
| 27 | |||
| 28 | using namespace controller; | ||
| 29 | |||
| 30 | Filter::Factory Filter::_factory; | ||
| 31 | |||
| 32 | REGISTER_FILTER_CLASS_INSTANCE(ClampFilter, "clamp") | ||
| 33 | REGISTER_FILTER_CLASS_INSTANCE(ConstrainToIntegerFilter, "constrainToInteger") | ||
| 34 | REGISTER_FILTER_CLASS_INSTANCE(ConstrainToPositiveIntegerFilter, "constrainToPositiveInteger") | ||
| 35 | REGISTER_FILTER_CLASS_INSTANCE(DeadZoneFilter, "deadZone") | ||
| 36 | REGISTER_FILTER_CLASS_INSTANCE(HysteresisFilter, "hysteresis") | ||
| 37 | REGISTER_FILTER_CLASS_INSTANCE(InvertFilter, "invert") | ||
| 38 | REGISTER_FILTER_CLASS_INSTANCE(ScaleFilter, "scale") | ||
| 39 | REGISTER_FILTER_CLASS_INSTANCE(PulseFilter, "pulse") | ||
| 40 | |||
| 41 | const QString JSON_FILTER_TYPE = QStringLiteral("type"); | ||
| 42 | const QString JSON_FILTER_PARAMS = QStringLiteral("params"); | ||
| 43 | |||
| 44 | |||
| 45 | Filter::Pointer Filter::parse(const QJsonValue& json) { | ||
| 46 | Filter::Pointer filter; | ||
| 47 | if (json.isString()) { | ||
| 48 | filter = Filter::getFactory().create(json.toString()); | ||
| 49 | } else if (json.isObject()) { | ||
| 50 | QJsonObject jsonObj = json.toObject(); | ||
| 51 | // The filter is an object, now let s check for type and potential arguments | ||
| 52 | auto filterType = jsonObj[JSON_FILTER_TYPE]; | ||
| 53 | filter = Filter::getFactory().create(filterType.toString()); | ||
| 54 | if (filter) { | ||
| 55 | QJsonValue params = jsonObj; | ||
| 56 | if (jsonObj.contains(JSON_FILTER_PARAMS)) { | ||
| 57 | params = jsonObj[JSON_FILTER_PARAMS]; | ||
| 58 | } | ||
| 59 | if (!filter->parseParameters(params)) { | ||
| 60 | qWarning() << "Unable to parse filter parameters " << params; | ||
| 61 | return Filter::Pointer(); | ||
| 62 | } | ||
| 63 | } | ||
| 64 | } | ||
| 65 | return filter; | ||
| 66 | } | ||
| 67 | |||
| 68 | bool Filter::parseSingleFloatParameter(const QJsonValue& parameters, const QString& name, float& output) { | ||
| 69 | if (parameters.isDouble()) { | ||
| 70 | output = parameters.toDouble(); | ||
| 71 | return true; | ||
| 72 | } else if (parameters.isArray()) { | ||
| 73 | auto arrayParameters = parameters.toArray(); | ||
| 74 | if (arrayParameters.size() > 1) { | ||
| 75 | output = arrayParameters[0].toDouble(); | ||
| 76 | return true; | ||
| 77 | } | ||
| 78 | } else if (parameters.isObject()) { | ||
| 79 | static const QString JSON_MIN = QStringLiteral("interval"); | ||
| 80 | auto objectParameters = parameters.toObject(); | ||
| 81 | if (objectParameters.contains(name)) { | ||
| 82 | output = objectParameters[name].toDouble(); | ||
| 83 | return true; | ||
| 84 | } | ||
| 85 | } | ||
| 86 | return false; | ||
| 87 | } | ||
| 88 | |||
| 89 | |||
| 90 | |||
| 91 | #if 0 | ||
| 92 | |||
| 93 | namespace controller { | ||
| 94 | |||
| 95 | class LambdaFilter : public Filter { | ||
| 96 | public: | ||
| 97 | // LambdaFilter() {}12 | ||
| 98 | LambdaFilter(Lambda f) : _function(f) {}; | ||
| 99 | |||
| 100 | virtual float apply(float value) const { | ||
| 101 | return _function(value); | ||
| 102 | } | ||
| 103 | |||
| 104 | virtual bool parseParameters(const QJsonArray& parameters) { return true; } | ||
| 105 | |||
| 106 | // REGISTER_FILTER_CLASS(LambdaFilter); | ||
| 107 | private: | ||
| 108 | Lambda _function; | ||
| 109 | }; | ||
| 110 | |||
| 111 | class ScriptFilter : public Filter { | ||
| 112 | public: | ||
| 113 | |||
| 114 | }; | ||
| 115 | |||
| 116 | |||
| 117 | |||
| 118 | //class EasingFilter : public Filter { | ||
| 119 | //public: | ||
| 120 | // virtual float apply(float value) const override; | ||
| 121 | |||
| 122 | //private: | ||
| 123 | // QEasingCurve _curve; | ||
| 124 | //}; | ||
| 125 | |||
| 126 | //// GLSL style filters | ||
| 127 | //class StepFilter : public Filter { | ||
| 128 | //public: | ||
| 129 | // StepFilter(float edge) : _edge(edge) {}; | ||
| 130 | // virtual float apply(float value) const override; | ||
| 131 | |||
| 132 | //private: | ||
| 133 | // const float _edge; | ||
| 134 | //}; | ||
| 135 | |||
| 136 | //class PowFilter : public Filter { | ||
| 137 | //public: | ||
| 138 | // PowFilter(float exponent) : _exponent(exponent) {}; | ||
| 139 | // virtual float apply(float value) const override; | ||
| 140 | |||
| 141 | //private: | ||
| 142 | // const float _exponent; | ||
| 143 | //}; | ||
| 144 | |||
| 145 | //class AbsFilter : public Filter { | ||
| 146 | //public: | ||
| 147 | // virtual float apply(float value) const override; | ||
| 148 | //}; | ||
| 149 | |||
| 150 | //class SignFilter : public Filter { | ||
| 151 | //public: | ||
| 152 | // virtual float apply(float value) const override; | ||
| 153 | //}; | ||
| 154 | |||
| 155 | //class FloorFilter : public Filter { | ||
| 156 | //public: | ||
| 157 | // virtual float apply(float value) const override { | ||
| 158 | // return floor(newValue); | ||
| 159 | // } | ||
| 160 | //}; | ||
| 161 | |||
| 162 | //class CeilFilter : public Filter { | ||
| 163 | //public: | ||
| 164 | // virtual float apply(float value) const override { | ||
| 165 | // return ceil(newValue); | ||
| 166 | // } | ||
| 167 | //}; | ||
| 168 | |||
| 169 | //class FractFilter : public Filter { | ||
| 170 | //public: | ||
| 171 | // virtual float apply(float value) const override { | ||
| 172 | // return fract(newValue); | ||
| 173 | // } | ||
| 174 | //}; | ||
| 175 | |||
| 176 | //class MinFilter : public Filter { | ||
| 177 | //public: | ||
| 178 | // MinFilter(float mine) : _min(min) {}; | ||
| 179 | |||
| 180 | // virtual float apply(float value) const override { | ||
| 181 | // return glm::min(_min, newValue); | ||
| 182 | // } | ||
| 183 | |||
| 184 | //private: | ||
| 185 | // const float _min; | ||
| 186 | //}; | ||
| 187 | |||
| 188 | //class MaxFilter : public Filter { | ||
| 189 | //public: | ||
| 190 | // MaxFilter(float max) : _max(max) {}; | ||
| 191 | // virtual float apply(float newValue, float oldValue) override; | ||
| 192 | //private: | ||
| 193 | // const float _max; | ||
| 194 | //}; | ||
| 195 | } | ||
| 196 | #endif | ||
| @@ -0,0 +1,59 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/09 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | #ifndef hifi_Controllers_Filter_h | ||
| 11 | #define hifi_Controllers_Filter_h | ||
| 12 | |||
| 13 | #include <list> | ||
| 14 | #include <memory> | ||
| 15 | #include <numeric> | ||
| 16 | #include <map> | ||
| 17 | |||
| 18 | #include <shared/Factory.h> | ||
| 19 | |||
| 20 | #include <GLMHelpers.h> | ||
| 21 | |||
| 22 | #include <QtCore/QEasingCurve> | ||
| 23 | |||
| 24 | class QJsonValue; | ||
| 25 | |||
| 26 | namespace controller { | ||
| 27 | |||
| 28 | // Encapsulates part of a filter chain | ||
| 29 | class Filter { | ||
| 30 | public: | ||
| 31 | using Pointer = std::shared_ptr<Filter>; | ||
| 32 | using List = std::list<Pointer>; | ||
| 33 | using Lambda = std::function<float(float)>; | ||
| 34 | using Factory = hifi::SimpleFactory<Filter, QString>; | ||
| 35 | |||
| 36 | virtual float apply(float value) const = 0; | ||
| 37 | // Factory features | ||
| 38 | virtual bool parseParameters(const QJsonValue& parameters) { return true; } | ||
| 39 | |||
| 40 | static Pointer parse(const QJsonValue& json); | ||
| 41 | static void registerBuilder(const QString& name, Factory::Builder builder); | ||
| 42 | static Factory& getFactory() { return _factory; } | ||
| 43 | |||
| 44 | static bool parseSingleFloatParameter(const QJsonValue& parameters, const QString& name, float& output); | ||
| 45 | protected: | ||
| 46 | static Factory _factory; | ||
| 47 | }; | ||
| 48 | } | ||
| 49 | |||
| 50 | #define REGISTER_FILTER_CLASS(classEntry) \ | ||
| 51 | private: \ | ||
| 52 | using Registrar = Filter::Factory::Registrar<classEntry>; \ | ||
| 53 | static Registrar _registrar; | ||
| 54 | |||
| 55 | #define REGISTER_FILTER_CLASS_INSTANCE(classEntry, className) \ | ||
| 56 | classEntry::Registrar classEntry::_registrar(className, Filter::getFactory()); | ||
| 57 | |||
| 58 | |||
| 59 | #endif | ||
| @@ -0,0 +1,8 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/09 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | #include "Mapping.h" | ||
| @@ -0,0 +1,37 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/09 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | #ifndef hifi_Controllers_Mapping_h | ||
| 11 | #define hifi_Controllers_Mapping_h | ||
| 12 | |||
| 13 | #include <map> | ||
| 14 | #include <unordered_map> | ||
| 15 | |||
| 16 | #include <QtCore/QString> | ||
| 17 | |||
| 18 | #include "Endpoint.h" | ||
| 19 | #include "Filter.h" | ||
| 20 | #include "Route.h" | ||
| 21 | |||
| 22 | namespace controller { | ||
| 23 | |||
| 24 | class Mapping { | ||
| 25 | public: | ||
| 26 | using Pointer = std::shared_ptr<Mapping>; | ||
| 27 | using List = Route::List; | ||
| 28 | |||
| 29 | Mapping(const QString& name) : name(name) {} | ||
| 30 | |||
| 31 | List routes; | ||
| 32 | QString name; | ||
| 33 | }; | ||
| 34 | |||
| 35 | } | ||
| 36 | |||
| 37 | #endif | ||
| @@ -0,0 +1,63 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/09 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #include "MappingBuilderProxy.h" | ||
| 10 | |||
| 11 | #include <QtCore/QHash> | ||
| 12 | #include <QtCore/QDebug> | ||
| 13 | |||
| 14 | #include <QtCore/QJsonObject> | ||
| 15 | #include <QtCore/QJsonArray> | ||
| 16 | |||
| 17 | #include "RouteBuilderProxy.h" | ||
| 18 | #include "../ScriptingInterface.h" | ||
| 19 | #include "../Logging.h" | ||
| 20 | |||
| 21 | using namespace controller; | ||
| 22 | |||
| 23 | QObject* MappingBuilderProxy::fromQml(const QJSValue& source) { | ||
| 24 | qCDebug(controllers) << "Creating new Route builder proxy from " << source.toString(); | ||
| 25 | auto sourceEndpoint = _parent.endpointFor(source); | ||
| 26 | return from(sourceEndpoint); | ||
| 27 | } | ||
| 28 | |||
| 29 | QObject* MappingBuilderProxy::from(const QScriptValue& source) { | ||
| 30 | qCDebug(controllers) << "Creating new Route builder proxy from " << source.toString(); | ||
| 31 | auto sourceEndpoint = _parent.endpointFor(source); | ||
| 32 | return from(sourceEndpoint); | ||
| 33 | } | ||
| 34 | |||
| 35 | QObject* MappingBuilderProxy::from(const Endpoint::Pointer& source) { | ||
| 36 | if (source) { | ||
| 37 | auto route = Route::Pointer(new Route()); | ||
| 38 | route->source = source; | ||
| 39 | return new RouteBuilderProxy(_parent, _mapping, route); | ||
| 40 | } else { | ||
| 41 | qCDebug(controllers) << "MappingBuilderProxy::from : source is null so no route created"; | ||
| 42 | return nullptr; | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | QObject* MappingBuilderProxy::makeAxisQml(const QJSValue& source1, const QJSValue& source2) { | ||
| 47 | auto source1Endpoint = _parent.endpointFor(source1); | ||
| 48 | auto source2Endpoint = _parent.endpointFor(source2); | ||
| 49 | return from(_parent.compositeEndpointFor(source1Endpoint, source2Endpoint)); | ||
| 50 | } | ||
| 51 | |||
| 52 | QObject* MappingBuilderProxy::makeAxis(const QScriptValue& source1, const QScriptValue& source2) { | ||
| 53 | auto source1Endpoint = _parent.endpointFor(source1); | ||
| 54 | auto source2Endpoint = _parent.endpointFor(source2); | ||
| 55 | return from(_parent.compositeEndpointFor(source1Endpoint, source2Endpoint)); | ||
| 56 | } | ||
| 57 | |||
| 58 | QObject* MappingBuilderProxy::enable(bool enable) { | ||
| 59 | _parent.enableMapping(_mapping->name, enable); | ||
| 60 | return this; | ||
| 61 | } | ||
| 62 | |||
| 63 | |||
| @@ -0,0 +1,58 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/09 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | #pragma once | ||
| 9 | #ifndef hifi_Controllers_Impl_MappingBuilderProxy_h | ||
| 10 | #define hifi_Controllers_Impl_MappingBuilderProxy_h | ||
| 11 | |||
| 12 | #include <QtCore/QObject> | ||
| 13 | #include <QtCore/QString> | ||
| 14 | |||
| 15 | #include "Mapping.h" | ||
| 16 | #include "Endpoint.h" | ||
| 17 | |||
| 18 | class QJSValue; | ||
| 19 | class QScriptValue; | ||
| 20 | class QJsonValue; | ||
| 21 | |||
| 22 | namespace controller { | ||
| 23 | |||
| 24 | class ScriptingInterface; | ||
| 25 | class UserInputMapper; | ||
| 26 | |||
| 27 | // TODO migrate functionality to a MappingBuilder class and make the proxy defer to that | ||
| 28 | // (for easier use in both C++ and JS) | ||
| 29 | class MappingBuilderProxy : public QObject { | ||
| 30 | Q_OBJECT | ||
| 31 | public: | ||
| 32 | MappingBuilderProxy(UserInputMapper& parent, Mapping::Pointer mapping) | ||
| 33 | : _parent(parent), _mapping(mapping) { } | ||
| 34 | |||
| 35 | Q_INVOKABLE QObject* fromQml(const QJSValue& source); | ||
| 36 | Q_INVOKABLE QObject* makeAxisQml(const QJSValue& source1, const QJSValue& source2); | ||
| 37 | |||
| 38 | Q_INVOKABLE QObject* from(const QScriptValue& source); | ||
| 39 | Q_INVOKABLE QObject* makeAxis(const QScriptValue& source1, const QScriptValue& source2); | ||
| 40 | |||
| 41 | Q_INVOKABLE QObject* enable(bool enable = true); | ||
| 42 | Q_INVOKABLE QObject* disable() { return enable(false); } | ||
| 43 | |||
| 44 | protected: | ||
| 45 | QObject* from(const Endpoint::Pointer& source); | ||
| 46 | |||
| 47 | friend class RouteBuilderProxy; | ||
| 48 | UserInputMapper& _parent; | ||
| 49 | Mapping::Pointer _mapping; | ||
| 50 | |||
| 51 | |||
| 52 | void parseRoute(const QJsonValue& json); | ||
| 53 | |||
| 54 | }; | ||
| 55 | |||
| 56 | } | ||
| 57 | |||
| 58 | #endif | ||
| @@ -0,0 +1,9 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/09 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #include "Route.h" | ||
| @@ -0,0 +1,35 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/09 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | #ifndef hifi_Controllers_Route_h | ||
| 11 | #define hifi_Controllers_Route_h | ||
| 12 | |||
| 13 | #include "Endpoint.h" | ||
| 14 | #include "Filter.h" | ||
| 15 | #include "Conditional.h" | ||
| 16 | |||
| 17 | namespace controller { | ||
| 18 | /* | ||
| 19 | * encapsulates a source, destination and filters to apply | ||
| 20 | */ | ||
| 21 | class Route { | ||
| 22 | public: | ||
| 23 | Endpoint::Pointer source; | ||
| 24 | Endpoint::Pointer destination; | ||
| 25 | Conditional::Pointer conditional; | ||
| 26 | Filter::List filters; | ||
| 27 | QString json; | ||
| 28 | bool debug { false }; | ||
| 29 | |||
| 30 | using Pointer = std::shared_ptr<Route>; | ||
| 31 | using List = std::list<Pointer>; | ||
| 32 | }; | ||
| 33 | } | ||
| 34 | |||
| 35 | #endif | ||
| @@ -0,0 +1,108 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/09 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | #include "RouteBuilderProxy.h" | ||
| 9 | |||
| 10 | #include <QtCore/QDebug> | ||
| 11 | |||
| 12 | #include <QtCore/QJsonObject> | ||
| 13 | #include <QtCore/QJsonArray> | ||
| 14 | |||
| 15 | #include <GLMHelpers.h> | ||
| 16 | |||
| 17 | #include "MappingBuilderProxy.h" | ||
| 18 | #include "../ScriptingInterface.h" | ||
| 19 | #include "../Logging.h" | ||
| 20 | |||
| 21 | #include "filters/ClampFilter.h" | ||
| 22 | #include "filters/ConstrainToIntegerFilter.h" | ||
| 23 | #include "filters/ConstrainToPositiveIntegerFilter.h" | ||
| 24 | #include "filters/DeadZoneFilter.h" | ||
| 25 | #include "filters/HysteresisFilter.h" | ||
| 26 | #include "filters/InvertFilter.h" | ||
| 27 | #include "filters/PulseFilter.h" | ||
| 28 | #include "filters/ScaleFilter.h" | ||
| 29 | |||
| 30 | using namespace controller; | ||
| 31 | |||
| 32 | void RouteBuilderProxy::toQml(const QJSValue& destination) { | ||
| 33 | qCDebug(controllers) << "Completing route " << destination.toString(); | ||
| 34 | auto destinationEndpoint = _parent.endpointFor(destination); | ||
| 35 | return to(destinationEndpoint); | ||
| 36 | } | ||
| 37 | |||
| 38 | void RouteBuilderProxy::to(const QScriptValue& destination) { | ||
| 39 | qCDebug(controllers) << "Completing route " << destination.toString(); | ||
| 40 | auto destinationEndpoint = _parent.endpointFor(destination); | ||
| 41 | return to(destinationEndpoint); | ||
| 42 | } | ||
| 43 | |||
| 44 | void RouteBuilderProxy::to(const Endpoint::Pointer& destination) { | ||
| 45 | _route->destination = destination; | ||
| 46 | _mapping->routes.push_back(_route); | ||
| 47 | deleteLater(); | ||
| 48 | } | ||
| 49 | |||
| 50 | QObject* RouteBuilderProxy::debug(bool enable) { | ||
| 51 | _route->debug = enable; | ||
| 52 | return this; | ||
| 53 | } | ||
| 54 | |||
| 55 | QObject* RouteBuilderProxy::when(const QScriptValue& expression) { | ||
| 56 | _route->conditional = _parent.conditionalFor(expression); | ||
| 57 | return this; | ||
| 58 | } | ||
| 59 | |||
| 60 | QObject* RouteBuilderProxy::whenQml(const QJSValue& expression) { | ||
| 61 | _route->conditional = _parent.conditionalFor(expression); | ||
| 62 | return this; | ||
| 63 | } | ||
| 64 | |||
| 65 | QObject* RouteBuilderProxy::clamp(float min, float max) { | ||
| 66 | addFilter(std::make_shared<ClampFilter>(min, max)); | ||
| 67 | return this; | ||
| 68 | } | ||
| 69 | |||
| 70 | QObject* RouteBuilderProxy::scale(float multiplier) { | ||
| 71 | addFilter(std::make_shared<ScaleFilter>(multiplier)); | ||
| 72 | return this; | ||
| 73 | } | ||
| 74 | |||
| 75 | QObject* RouteBuilderProxy::invert() { | ||
| 76 | addFilter(std::make_shared<InvertFilter>()); | ||
| 77 | return this; | ||
| 78 | } | ||
| 79 | |||
| 80 | QObject* RouteBuilderProxy::hysteresis(float min, float max) { | ||
| 81 | addFilter(std::make_shared<HysteresisFilter>(min, max)); | ||
| 82 | return this; | ||
| 83 | } | ||
| 84 | |||
| 85 | QObject* RouteBuilderProxy::deadZone(float min) { | ||
| 86 | addFilter(std::make_shared<DeadZoneFilter>(min)); | ||
| 87 | return this; | ||
| 88 | } | ||
| 89 | |||
| 90 | QObject* RouteBuilderProxy::constrainToInteger() { | ||
| 91 | addFilter(std::make_shared<ConstrainToIntegerFilter>()); | ||
| 92 | return this; | ||
| 93 | } | ||
| 94 | |||
| 95 | QObject* RouteBuilderProxy::constrainToPositiveInteger() { | ||
| 96 | addFilter(std::make_shared<ConstrainToPositiveIntegerFilter>()); | ||
| 97 | return this; | ||
| 98 | } | ||
| 99 | |||
| 100 | QObject* RouteBuilderProxy::pulse(float interval) { | ||
| 101 | addFilter(std::make_shared<PulseFilter>(interval)); | ||
| 102 | return this; | ||
| 103 | } | ||
| 104 | |||
| 105 | void RouteBuilderProxy::addFilter(Filter::Pointer filter) { | ||
| 106 | _route->filters.push_back(filter); | ||
| 107 | } | ||
| 108 | |||
| @@ -0,0 +1,61 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/09 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | #pragma once | ||
| 9 | #ifndef hifi_Controllers_Impl_RouteBuilderProxy_h | ||
| 10 | #define hifi_Controllers_Impl_RouteBuilderProxy_h | ||
| 11 | |||
| 12 | #include <QtCore/QObject> | ||
| 13 | |||
| 14 | #include "Filter.h" | ||
| 15 | #include "Route.h" | ||
| 16 | #include "Mapping.h" | ||
| 17 | |||
| 18 | #include "../UserInputMapper.h" | ||
| 19 | |||
| 20 | class QJSValue; | ||
| 21 | class QScriptValue; | ||
| 22 | class QJsonValue; | ||
| 23 | |||
| 24 | namespace controller { | ||
| 25 | |||
| 26 | class ScriptingInterface; | ||
| 27 | |||
| 28 | // TODO migrate functionality to a RouteBuilder class and make the proxy defer to that | ||
| 29 | // (for easier use in both C++ and JS) | ||
| 30 | class RouteBuilderProxy : public QObject { | ||
| 31 | Q_OBJECT | ||
| 32 | public: | ||
| 33 | RouteBuilderProxy(UserInputMapper& parent, Mapping::Pointer mapping, Route::Pointer route) | ||
| 34 | : _parent(parent), _mapping(mapping), _route(route) { } | ||
| 35 | |||
| 36 | Q_INVOKABLE void toQml(const QJSValue& destination); | ||
| 37 | Q_INVOKABLE QObject* whenQml(const QJSValue& expression); | ||
| 38 | |||
| 39 | Q_INVOKABLE void to(const QScriptValue& destination); | ||
| 40 | Q_INVOKABLE QObject* debug(bool enable = true); | ||
| 41 | Q_INVOKABLE QObject* when(const QScriptValue& expression); | ||
| 42 | Q_INVOKABLE QObject* clamp(float min, float max); | ||
| 43 | Q_INVOKABLE QObject* hysteresis(float min, float max); | ||
| 44 | Q_INVOKABLE QObject* pulse(float interval); | ||
| 45 | Q_INVOKABLE QObject* scale(float multiplier); | ||
| 46 | Q_INVOKABLE QObject* invert(); | ||
| 47 | Q_INVOKABLE QObject* deadZone(float min); | ||
| 48 | Q_INVOKABLE QObject* constrainToInteger(); | ||
| 49 | Q_INVOKABLE QObject* constrainToPositiveInteger(); | ||
| 50 | |||
| 51 | private: | ||
| 52 | void to(const Endpoint::Pointer& destination); | ||
| 53 | void conditional(const Conditional::Pointer& conditional); | ||
| 54 | void addFilter(Filter::Pointer filter); | ||
| 55 | UserInputMapper& _parent; | ||
| 56 | Mapping::Pointer _mapping; | ||
| 57 | Route::Pointer _route; | ||
| 58 | }; | ||
| 59 | |||
| 60 | } | ||
| 61 | #endif | ||
| @@ -0,0 +1,21 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/23 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #include "AndConditional.h" | ||
| 10 | |||
| 11 | using namespace controller; | ||
| 12 | |||
| 13 | bool AndConditional::satisfied() { | ||
| 14 | for (auto& conditional : _children) { | ||
| 15 | if (!conditional->satisfied()) { | ||
| 16 | return false; | ||
| 17 | } | ||
| 18 | } | ||
| 19 | return true; | ||
| 20 | } | ||
| 21 | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/20 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | #ifndef hifi_Controllers_AndConditional_h | ||
| 11 | #define hifi_Controllers_AndConditional_h | ||
| 12 | |||
| 13 | #include "../Conditional.h" | ||
| 14 | |||
| 15 | namespace controller { | ||
| 16 | |||
| 17 | class AndConditional : public Conditional { | ||
| 18 | public: | ||
| 19 | using Pointer = std::shared_ptr<AndConditional>; | ||
| 20 | |||
| 21 | AndConditional(Conditional::List children) : _children(children) { } | ||
| 22 | |||
| 23 | virtual bool satisfied() override; | ||
| 24 | |||
| 25 | private: | ||
| 26 | Conditional::List _children; | ||
| 27 | }; | ||
| 28 | |||
| 29 | } | ||
| 30 | |||
| 31 | #endif | ||
| @@ -0,0 +1,9 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/23 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #include "EndpointConditional.h" | ||
| @@ -0,0 +1,27 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/20 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | #ifndef hifi_Controllers_EndpointConditional_h | ||
| 11 | #define hifi_Controllers_EndpointConditional_h | ||
| 12 | |||
| 13 | #include "../Conditional.h" | ||
| 14 | #include "../Endpoint.h" | ||
| 15 | |||
| 16 | namespace controller { | ||
| 17 | |||
| 18 | class EndpointConditional : public Conditional { | ||
| 19 | public: | ||
| 20 | EndpointConditional(Endpoint::Pointer endpoint) : _endpoint(endpoint) {} | ||
| 21 | virtual bool satisfied() override { return _endpoint && _endpoint->value() != 0.0; } | ||
| 22 | private: | ||
| 23 | Endpoint::Pointer _endpoint; | ||
| 24 | }; | ||
| 25 | |||
| 26 | } | ||
| 27 | #endif | ||
| @@ -0,0 +1,9 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/23 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #include "NotConditional.h" | ||
| @@ -0,0 +1,16 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/20 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | #ifndef hifi_Controllers_NotConditional_h | ||
| 11 | #define hifi_Controllers_NotConditional_h | ||
| 12 | |||
| 13 | #include "../Conditional.h" | ||
| 14 | |||
| 15 | |||
| 16 | #endif | ||
| @@ -0,0 +1,27 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/23 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #include "ScriptConditional.h" | ||
| 10 | |||
| 11 | #include <QtCore/QThread> | ||
| 12 | |||
| 13 | using namespace controller; | ||
| 14 | |||
| 15 | bool ScriptConditional::satisfied() { | ||
| 16 | updateValue(); | ||
| 17 | return _lastValue; | ||
| 18 | } | ||
| 19 | |||
| 20 | void ScriptConditional::updateValue() { | ||
| 21 | if (QThread::currentThread() != thread()) { | ||
| 22 | QMetaObject::invokeMethod(this, "updateValue", Qt::QueuedConnection); | ||
| 23 | return; | ||
| 24 | } | ||
| 25 | |||
| 26 | _lastValue = _callable.call().toBool(); | ||
| 27 | } | ||
| @@ -0,0 +1,34 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/20 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | #ifndef hifi_Controllers_ScriptConditional_h | ||
| 11 | #define hifi_Controllers_ScriptConditional_h | ||
| 12 | |||
| 13 | #include <QtCore/QObject> | ||
| 14 | |||
| 15 | #include <QtScript/QScriptValue> | ||
| 16 | |||
| 17 | #include "../Conditional.h" | ||
| 18 | |||
| 19 | namespace controller { | ||
| 20 | |||
| 21 | class ScriptConditional : public QObject, public Conditional { | ||
| 22 | Q_OBJECT; | ||
| 23 | public: | ||
| 24 | ScriptConditional(const QScriptValue& callable) : _callable(callable) { } | ||
| 25 | virtual bool satisfied() override; | ||
| 26 | protected: | ||
| 27 | Q_INVOKABLE void updateValue(); | ||
| 28 | private: | ||
| 29 | QScriptValue _callable; | ||
| 30 | bool _lastValue { false }; | ||
| 31 | }; | ||
| 32 | |||
| 33 | } | ||
| 34 | #endif | ||
| @@ -0,0 +1,40 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/23 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #include "ActionEndpoint.h" | ||
| 10 | |||
| 11 | #include <DependencyManager.h> | ||
| 12 | |||
| 13 | #include "../../UserInputMapper.h" | ||
| 14 | |||
| 15 | using namespace controller; | ||
| 16 | |||
| 17 | void ActionEndpoint::apply(float newValue, const Pointer& source) { | ||
| 18 | _currentValue += newValue; | ||
| 19 | if (_input != Input::INVALID_INPUT) { | ||
| 20 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); | ||
| 21 | userInputMapper->deltaActionState(Action(_input.getChannel()), newValue); | ||
| 22 | } | ||
| 23 | } | ||
| 24 | |||
| 25 | void ActionEndpoint::apply(const Pose& value, const Pointer& source) { | ||
| 26 | _currentPose = value; | ||
| 27 | if (!_currentPose.isValid()) { | ||
| 28 | return; | ||
| 29 | } | ||
| 30 | if (_input != Input::INVALID_INPUT) { | ||
| 31 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); | ||
| 32 | userInputMapper->setActionState(Action(_input.getChannel()), _currentPose); | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | void ActionEndpoint::reset() { | ||
| 37 | _currentValue = 0.0f; | ||
| 38 | _currentPose = Pose(); | ||
| 39 | } | ||
| 40 | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/23 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | #ifndef hifi_Controllers_ActionEndpoint_h | ||
| 11 | #define hifi_Controllers_ActionEndpoint_h | ||
| 12 | |||
| 13 | #include "../Endpoint.h" | ||
| 14 | |||
| 15 | #include "../../Actions.h" | ||
| 16 | #include <DependencyManager.h> | ||
| 17 | |||
| 18 | #include "../../UserInputMapper.h" | ||
| 19 | |||
| 20 | namespace controller { | ||
| 21 | |||
| 22 | class ActionEndpoint : public Endpoint { | ||
| 23 | public: | ||
| 24 | ActionEndpoint(const Input& id = Input::INVALID_INPUT) : Endpoint(id) { } | ||
| 25 | |||
| 26 | virtual float value() override { return _currentValue; } | ||
| 27 | virtual void apply(float newValue, const Pointer& source) override; | ||
| 28 | |||
| 29 | virtual Pose pose() override { return _currentPose; } | ||
| 30 | virtual void apply(const Pose& value, const Pointer& source) override; | ||
| 31 | |||
| 32 | virtual void reset() override; | ||
| 33 | |||
| 34 | private: | ||
| 35 | float _currentValue{ 0.0f }; | ||
| 36 | Pose _currentPose{}; | ||
| 37 | }; | ||
| 38 | |||
| 39 | } | ||
| 40 | |||
| 41 | #endif | ||
| @@ -0,0 +1,63 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/23 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #include "AnyEndpoint.h" | ||
| 10 | |||
| 11 | #include "../../UserInputMapper.h" | ||
| 12 | |||
| 13 | using namespace controller; | ||
| 14 | |||
| 15 | AnyEndpoint::AnyEndpoint(Endpoint::List children) : Endpoint(Input::INVALID_INPUT), _children(children) { | ||
| 16 | bool standard = true; | ||
| 17 | // Ensure if we're building a composite of standard devices the composite itself | ||
| 18 | // is treated as a standard device for rule processing order | ||
| 19 | for (auto endpoint : children) { | ||
| 20 | if (endpoint->getInput().device != UserInputMapper::STANDARD_DEVICE) { | ||
| 21 | standard = false; | ||
| 22 | break; | ||
| 23 | } | ||
| 24 | } | ||
| 25 | if (standard) { | ||
| 26 | this->_input.device = UserInputMapper::STANDARD_DEVICE; | ||
| 27 | } | ||
| 28 | } | ||
| 29 | |||
| 30 | float AnyEndpoint::value() { | ||
| 31 | float result = 0; | ||
| 32 | for (auto& child : _children) { | ||
| 33 | float childResult = child->value(); | ||
| 34 | if (childResult != 0.0f) { | ||
| 35 | result = childResult; | ||
| 36 | } | ||
| 37 | } | ||
| 38 | return result; | ||
| 39 | } | ||
| 40 | |||
| 41 | void AnyEndpoint::apply(float newValue, const Endpoint::Pointer& source) { | ||
| 42 | qFatal("AnyEndpoint is read only"); | ||
| 43 | } | ||
| 44 | |||
| 45 | // AnyEndpoint is used for reading, so return false if any child returns false (has been written to) | ||
| 46 | bool AnyEndpoint::writeable() const { | ||
| 47 | for (auto& child : _children) { | ||
| 48 | if (!child->writeable()) { | ||
| 49 | return false; | ||
| 50 | } | ||
| 51 | } | ||
| 52 | return true; | ||
| 53 | } | ||
| 54 | |||
| 55 | bool AnyEndpoint::readable() const { | ||
| 56 | for (auto& child : _children) { | ||
| 57 | if (!child->readable()) { | ||
| 58 | return false; | ||
| 59 | } | ||
| 60 | } | ||
| 61 | return true; | ||
| 62 | } | ||
| 63 | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/23 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | #ifndef hifi_Controllers_AnyEndpoint_h | ||
| 11 | #define hifi_Controllers_AnyEndpoint_h | ||
| 12 | |||
| 13 | #include "../Endpoint.h" | ||
| 14 | |||
| 15 | namespace controller { | ||
| 16 | |||
| 17 | class AnyEndpoint : public Endpoint { | ||
| 18 | friend class UserInputMapper; | ||
| 19 | public: | ||
| 20 | AnyEndpoint(Endpoint::List children); | ||
| 21 | virtual float value() override; | ||
| 22 | virtual void apply(float newValue, const Endpoint::Pointer& source) override; | ||
| 23 | virtual bool writeable() const override; | ||
| 24 | virtual bool readable() const override; | ||
| 25 | |||
| 26 | private: | ||
| 27 | Endpoint::List _children; | ||
| 28 | }; | ||
| 29 | |||
| 30 | } | ||
| 31 | |||
| 32 | #endif | ||
| @@ -0,0 +1,9 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/23 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #include "ArrayEndpoint.h" | ||
| @@ -0,0 +1,43 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/23 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | #ifndef hifi_Controllers_ArrayEndpoint_h | ||
| 11 | #define hifi_Controllers_ArrayEndpoint_h | ||
| 12 | |||
| 13 | #include "../Endpoint.h" | ||
| 14 | |||
| 15 | namespace controller { | ||
| 16 | |||
| 17 | class ArrayEndpoint : public Endpoint { | ||
| 18 | friend class UserInputMapper; | ||
| 19 | public: | ||
| 20 | using Pointer = std::shared_ptr<ArrayEndpoint>; | ||
| 21 | ArrayEndpoint() : Endpoint(Input::INVALID_INPUT) { } | ||
| 22 | |||
| 23 | virtual float value() override { | ||
| 24 | return 0.0; | ||
| 25 | } | ||
| 26 | |||
| 27 | virtual void apply(float value, const Endpoint::Pointer& source) override { | ||
| 28 | for (auto& child : _children) { | ||
| 29 | if (child->writeable()) { | ||
| 30 | child->apply(value, source); | ||
| 31 | } | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | virtual bool readable() const override { return false; } | ||
| 36 | |||
| 37 | private: | ||
| 38 | Endpoint::List _children; | ||
| 39 | }; | ||
| 40 | |||
| 41 | } | ||
| 42 | |||
| 43 | #endif | ||
| @@ -0,0 +1,36 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/23 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #include "CompositeEndpoint.h" | ||
| 10 | |||
| 11 | #include "../../UserInputMapper.h" | ||
| 12 | |||
| 13 | namespace controller { | ||
| 14 | |||
| 15 | CompositeEndpoint::CompositeEndpoint(Endpoint::Pointer first, Endpoint::Pointer second) | ||
| 16 | : Endpoint(Input::INVALID_INPUT), Pair(first, second) { | ||
| 17 | if (first->getInput().device == UserInputMapper::STANDARD_DEVICE && | ||
| 18 | second->getInput().device == UserInputMapper::STANDARD_DEVICE) { | ||
| 19 | this->_input.device = UserInputMapper::STANDARD_DEVICE; | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | bool CompositeEndpoint::readable() const { | ||
| 24 | return first->readable() && second->readable(); | ||
| 25 | } | ||
| 26 | |||
| 27 | float CompositeEndpoint::value() { | ||
| 28 | float result = first->value() * -1.0f + second->value(); | ||
| 29 | return result; | ||
| 30 | } | ||
| 31 | |||
| 32 | void CompositeEndpoint::apply(float newValue, const Pointer& source) { | ||
| 33 | // Composites are read only | ||
| 34 | } | ||
| 35 | |||
| 36 | } | ||
| @@ -0,0 +1,28 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/23 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | #ifndef hifi_Controllers_CompositeEndpoint_h | ||
| 11 | #define hifi_Controllers_CompositeEndpoint_h | ||
| 12 | |||
| 13 | #include "../Endpoint.h" | ||
| 14 | |||
| 15 | namespace controller { | ||
| 16 | class CompositeEndpoint : public Endpoint, Endpoint::Pair { | ||
| 17 | public: | ||
| 18 | CompositeEndpoint(Endpoint::Pointer first, Endpoint::Pointer second); | ||
| 19 | |||
| 20 | virtual float value() override; | ||
| 21 | virtual void apply(float newValue, const Pointer& source) override; | ||
| 22 | virtual bool readable() const override; | ||
| 23 | |||
| 24 | }; | ||
| 25 | |||
| 26 | } | ||
| 27 | |||
| 28 | #endif | ||
| @@ -0,0 +1,41 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/23 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #include "InputEndpoint.h" | ||
| 10 | |||
| 11 | #include <DependencyManager.h> | ||
| 12 | |||
| 13 | #include "../../UserInputMapper.h" | ||
| 14 | |||
| 15 | using namespace controller; | ||
| 16 | float InputEndpoint::value(){ | ||
| 17 | _read = true; | ||
| 18 | if (isPose()) { | ||
| 19 | return pose().valid ? 1.0f : 0.0f; | ||
| 20 | } | ||
| 21 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); | ||
| 22 | auto deviceProxy = userInputMapper->getDevice(_input); | ||
| 23 | if (!deviceProxy) { | ||
| 24 | return 0.0f; | ||
| 25 | } | ||
| 26 | return deviceProxy->getValue(_input); | ||
| 27 | } | ||
| 28 | |||
| 29 | Pose InputEndpoint::pose() { | ||
| 30 | _read = true; | ||
| 31 | if (!isPose()) { | ||
| 32 | return Pose(); | ||
| 33 | } | ||
| 34 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); | ||
| 35 | auto deviceProxy = userInputMapper->getDevice(_input); | ||
| 36 | if (!deviceProxy) { | ||
| 37 | return Pose(); | ||
| 38 | } | ||
| 39 | return deviceProxy->getPose(_input.channel); | ||
| 40 | } | ||
| 41 | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/23 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | #ifndef hifi_Controllers_InputEndpoint_h | ||
| 11 | #define hifi_Controllers_InputEndpoint_h | ||
| 12 | |||
| 13 | #include "../Endpoint.h" | ||
| 14 | |||
| 15 | namespace controller { | ||
| 16 | |||
| 17 | class InputEndpoint : public Endpoint { | ||
| 18 | public: | ||
| 19 | InputEndpoint(const Input& id = Input::INVALID_INPUT) | ||
| 20 | : Endpoint(id) { | ||
| 21 | } | ||
| 22 | |||
| 23 | virtual float value() override; | ||
| 24 | // FIXME need support for writing back to vibration / force feedback effects | ||
| 25 | virtual void apply(float newValue, const Pointer& source) override {} | ||
| 26 | virtual Pose pose() override; | ||
| 27 | virtual void apply(const Pose& value, const Pointer& source) override { } | ||
| 28 | |||
| 29 | virtual bool writeable() const { return false; } | ||
| 30 | virtual bool readable() const { return !_read; } | ||
| 31 | virtual void reset() { _read = false; } | ||
| 32 | |||
| 33 | private: | ||
| 34 | bool _read { false }; | ||
| 35 | }; | ||
| 36 | |||
| 37 | } | ||
| 38 | |||
| 39 | #endif | ||
| @@ -0,0 +1,9 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/23 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #include "JSEndpoint.h" | ||
| @@ -0,0 +1,41 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/23 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | #ifndef hifi_Controllers_JSEndpoint_h | ||
| 11 | #define hifi_Controllers_JSEndpoint_h | ||
| 12 | |||
| 13 | #include "../Endpoint.h" | ||
| 14 | |||
| 15 | #include <QtQml/QJSValue> | ||
| 16 | #include <QtQml/QJSValueList> | ||
| 17 | |||
| 18 | namespace controller { | ||
| 19 | |||
| 20 | class JSEndpoint : public Endpoint { | ||
| 21 | public: | ||
| 22 | JSEndpoint(const QJSValue& callable) | ||
| 23 | : Endpoint(Input::INVALID_INPUT), _callable(callable) { | ||
| 24 | } | ||
| 25 | |||
| 26 | virtual float value() { | ||
| 27 | float result = (float)_callable.call().toNumber(); | ||
| 28 | return result; | ||
| 29 | } | ||
| 30 | |||
| 31 | virtual void apply(float newValue, const Pointer& source) { | ||
| 32 | _callable.call(QJSValueList({ QJSValue(newValue) })); | ||
| 33 | } | ||
| 34 | |||
| 35 | private: | ||
| 36 | QJSValue _callable; | ||
| 37 | }; | ||
| 38 | |||
| 39 | } | ||
| 40 | |||
| 41 | #endif | ||
| @@ -0,0 +1,46 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/23 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #include "ScriptEndpoint.h" | ||
| 10 | |||
| 11 | #include <QtCore/QThread> | ||
| 12 | |||
| 13 | using namespace controller; | ||
| 14 | |||
| 15 | float ScriptEndpoint::value() { | ||
| 16 | updateValue(); | ||
| 17 | return _lastValueRead; | ||
| 18 | } | ||
| 19 | |||
| 20 | void ScriptEndpoint::updateValue() { | ||
| 21 | if (QThread::currentThread() != thread()) { | ||
| 22 | QMetaObject::invokeMethod(this, "updateValue", Qt::QueuedConnection); | ||
| 23 | return; | ||
| 24 | } | ||
| 25 | |||
| 26 | _lastValueRead = (float)_callable.call().toNumber(); | ||
| 27 | } | ||
| 28 | |||
| 29 | void ScriptEndpoint::apply(float value, const Pointer& source) { | ||
| 30 | if (value == _lastValueWritten) { | ||
| 31 | return; | ||
| 32 | } | ||
| 33 | internalApply(value, source->getInput().getID()); | ||
| 34 | } | ||
| 35 | |||
| 36 | void ScriptEndpoint::internalApply(float value, int sourceID) { | ||
| 37 | _lastValueWritten = value; | ||
| 38 | if (QThread::currentThread() != thread()) { | ||
| 39 | QMetaObject::invokeMethod(this, "internalApply", Qt::QueuedConnection, | ||
| 40 | Q_ARG(float, value), | ||
| 41 | Q_ARG(int, sourceID)); | ||
| 42 | return; | ||
| 43 | } | ||
| 44 | _callable.call(QScriptValue(), | ||
| 45 | QScriptValueList({ QScriptValue(value), QScriptValue(sourceID) })); | ||
| 46 | } | ||
| @@ -0,0 +1,40 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/23 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | #ifndef hifi_Controllers_ScriptEndpoint_h | ||
| 11 | #define hifi_Controllers_ScriptEndpoint_h | ||
| 12 | |||
| 13 | #include <QtScript/QScriptValue> | ||
| 14 | |||
| 15 | #include "../Endpoint.h" | ||
| 16 | |||
| 17 | namespace controller { | ||
| 18 | |||
| 19 | class ScriptEndpoint : public Endpoint { | ||
| 20 | Q_OBJECT; | ||
| 21 | public: | ||
| 22 | ScriptEndpoint(const QScriptValue& callable) | ||
| 23 | : Endpoint(Input::INVALID_INPUT), _callable(callable) { | ||
| 24 | } | ||
| 25 | |||
| 26 | virtual float value(); | ||
| 27 | virtual void apply(float newValue, const Pointer& source); | ||
| 28 | |||
| 29 | protected: | ||
| 30 | Q_INVOKABLE void updateValue(); | ||
| 31 | Q_INVOKABLE virtual void internalApply(float newValue, int sourceID); | ||
| 32 | private: | ||
| 33 | QScriptValue _callable; | ||
| 34 | float _lastValueRead { 0.0f }; | ||
| 35 | float _lastValueWritten { 0.0f }; | ||
| 36 | }; | ||
| 37 | |||
| 38 | } | ||
| 39 | |||
| 40 | #endif | ||
| @@ -0,0 +1,9 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/23 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #include "StandardEndpoint.h" | ||
| @@ -0,0 +1,60 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/23 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | #ifndef hifi_Controllers_StandardEndpoint_h | ||
| 11 | #define hifi_Controllers_StandardEndpoint_h | ||
| 12 | |||
| 13 | #include "../Endpoint.h" | ||
| 14 | |||
| 15 | namespace controller { | ||
| 16 | |||
| 17 | class StandardEndpoint : public VirtualEndpoint { | ||
| 18 | public: | ||
| 19 | StandardEndpoint(const Input& input) : VirtualEndpoint(input) {} | ||
| 20 | virtual bool writeable() const override { return !_written; } | ||
| 21 | virtual bool readable() const override { return !_read; } | ||
| 22 | virtual void reset() override { | ||
| 23 | apply(0.0f, Endpoint::Pointer()); | ||
| 24 | apply(Pose(), Endpoint::Pointer()); | ||
| 25 | _written = _read = false; | ||
| 26 | } | ||
| 27 | |||
| 28 | virtual float value() override { | ||
| 29 | _read = true; | ||
| 30 | return VirtualEndpoint::value(); | ||
| 31 | } | ||
| 32 | |||
| 33 | virtual void apply(float value, const Pointer& source) override { | ||
| 34 | // For standard endpoints, the first NON-ZERO write counts. | ||
| 35 | if (value != 0.0) { | ||
| 36 | _written = true; | ||
| 37 | } | ||
| 38 | VirtualEndpoint::apply(value, source); | ||
| 39 | } | ||
| 40 | |||
| 41 | virtual Pose pose() override { | ||
| 42 | _read = true; | ||
| 43 | return VirtualEndpoint::pose(); | ||
| 44 | } | ||
| 45 | |||
| 46 | virtual void apply(const Pose& value, const Pointer& source) override { | ||
| 47 | if (value != Pose()) { | ||
| 48 | _written = true; | ||
| 49 | } | ||
| 50 | VirtualEndpoint::apply(value, source); | ||
| 51 | } | ||
| 52 | |||
| 53 | private: | ||
| 54 | bool _written { false }; | ||
| 55 | bool _read { false }; | ||
| 56 | }; | ||
| 57 | |||
| 58 | } | ||
| 59 | |||
| 60 | #endif | ||
| @@ -0,0 +1,38 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/25 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #include "ClampFilter.h" | ||
| 10 | |||
| 11 | #include <QtCore/QJsonObject> | ||
| 12 | #include <QtCore/QJsonArray> | ||
| 13 | |||
| 14 | using namespace controller; | ||
| 15 | |||
| 16 | bool ClampFilter::parseParameters(const QJsonValue& parameters) { | ||
| 17 | if (parameters.isArray()) { | ||
| 18 | auto arrayParameters = parameters.toArray(); | ||
| 19 | if (arrayParameters.size() > 1) { | ||
| 20 | _min = arrayParameters[0].toDouble(); | ||
| 21 | } | ||
| 22 | if (arrayParameters.size() > 2) { | ||
| 23 | _max = arrayParameters[1].toDouble(); | ||
| 24 | } | ||
| 25 | } else if (parameters.isObject()) { | ||
| 26 | static const QString JSON_MAX = QStringLiteral("max"); | ||
| 27 | static const QString JSON_MIN = QStringLiteral("min"); | ||
| 28 | |||
| 29 | auto objectParameters = parameters.toObject(); | ||
| 30 | if (objectParameters.contains(JSON_MIN)) { | ||
| 31 | _min = objectParameters[JSON_MIN].toDouble(); | ||
| 32 | } | ||
| 33 | if (objectParameters.contains(JSON_MAX)) { | ||
| 34 | _max = objectParameters[JSON_MAX].toDouble(); | ||
| 35 | } | ||
| 36 | } | ||
| 37 | return true; | ||
| 38 | } | ||
| @@ -0,0 +1,32 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/25 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | #ifndef hifi_Controllers_Filters_Clamp_h | ||
| 11 | #define hifi_Controllers_Filters_Clamp_h | ||
| 12 | |||
| 13 | #include "../Filter.h" | ||
| 14 | |||
| 15 | namespace controller { | ||
| 16 | |||
| 17 | class ClampFilter : public Filter { | ||
| 18 | REGISTER_FILTER_CLASS(ClampFilter); | ||
| 19 | public: | ||
| 20 | ClampFilter(float min = 0.0, float max = 1.0) : _min(min), _max(max) {}; | ||
| 21 | virtual float apply(float value) const override { | ||
| 22 | return glm::clamp(value, _min, _max); | ||
| 23 | } | ||
| 24 | virtual bool parseParameters(const QJsonValue& parameters) override; | ||
| 25 | protected: | ||
| 26 | float _min = 0.0f; | ||
| 27 | float _max = 1.0f; | ||
| 28 | }; | ||
| 29 | |||
| 30 | } | ||
| 31 | |||
| 32 | #endif | ||
| @@ -0,0 +1,9 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/25 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #include "ConstrainToIntegerFilter.h" | ||
| @@ -0,0 +1,30 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/25 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | #ifndef hifi_Controllers_Filters_ConstrainToIntegerFilter_h | ||
| 11 | #define hifi_Controllers_Filters_ConstrainToIntegerFilter_h | ||
| 12 | |||
| 13 | #include "../Filter.h" | ||
| 14 | |||
| 15 | namespace controller { | ||
| 16 | |||
| 17 | class ConstrainToIntegerFilter : public Filter { | ||
| 18 | REGISTER_FILTER_CLASS(ConstrainToIntegerFilter); | ||
| 19 | public: | ||
| 20 | ConstrainToIntegerFilter() {}; | ||
| 21 | |||
| 22 | virtual float apply(float value) const override { | ||
| 23 | return glm::sign(value); | ||
| 24 | } | ||
| 25 | protected: | ||
| 26 | }; | ||
| 27 | |||
| 28 | } | ||
| 29 | |||
| 30 | #endif | ||
| @@ -0,0 +1,9 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/25 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #include "ConstrainToPositiveIntegerFilter.h" | ||
| @@ -0,0 +1,30 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/25 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | #ifndef hifi_Controllers_Filters_ConstrainToPositiveInteger_h | ||
| 11 | #define hifi_Controllers_Filters_ConstrainToPositiveInteger_h | ||
| 12 | |||
| 13 | #include "../Filter.h" | ||
| 14 | |||
| 15 | namespace controller { | ||
| 16 | |||
| 17 | class ConstrainToPositiveIntegerFilter : public Filter { | ||
| 18 | REGISTER_FILTER_CLASS(ConstrainToPositiveIntegerFilter); | ||
| 19 | public: | ||
| 20 | ConstrainToPositiveIntegerFilter() {}; | ||
| 21 | |||
| 22 | virtual float apply(float value) const override { | ||
| 23 | return (value <= 0.0f) ? 0.0f : 1.0f; | ||
| 24 | } | ||
| 25 | protected: | ||
| 26 | }; | ||
| 27 | |||
| 28 | } | ||
| 29 | |||
| 30 | #endif | ||
| @@ -0,0 +1,26 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/25 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #include "DeadZoneFilter.h" | ||
| 10 | |||
| 11 | #include <QtCore/QJsonObject> | ||
| 12 | #include <QtCore/QJsonArray> | ||
| 13 | |||
| 14 | using namespace controller; | ||
| 15 | float DeadZoneFilter::apply(float value) const { | ||
| 16 | float scale = 1.0f / (1.0f - _min); | ||
| 17 | if (std::abs(value) < _min) { | ||
| 18 | return 0.0f; | ||
| 19 | } | ||
| 20 | return (value - _min) * scale; | ||
| 21 | } | ||
| 22 | |||
| 23 | bool DeadZoneFilter::parseParameters(const QJsonValue& parameters) { | ||
| 24 | static const QString JSON_MIN = QStringLiteral("min"); | ||
| 25 | return parseSingleFloatParameter(parameters, JSON_MIN, _min); | ||
| 26 | } | ||
| @@ -0,0 +1,31 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/25 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | #ifndef hifi_Controllers_Filters_DeadZoneFilter_h | ||
| 11 | #define hifi_Controllers_Filters_DeadZoneFilter_h | ||
| 12 | |||
| 13 | #include "../Filter.h" | ||
| 14 | |||
| 15 | namespace controller { | ||
| 16 | |||
| 17 | class DeadZoneFilter : public Filter { | ||
| 18 | REGISTER_FILTER_CLASS(DeadZoneFilter); | ||
| 19 | public: | ||
| 20 | DeadZoneFilter(float min = 0.0) : _min(min) {}; | ||
| 21 | |||
| 22 | virtual float apply(float value) const override; | ||
| 23 | virtual bool parseParameters(const QJsonValue& parameters) override; | ||
| 24 | protected: | ||
| 25 | float _min = 0.0f; | ||
| 26 | }; | ||
| 27 | |||
| 28 | |||
| 29 | } | ||
| 30 | |||
| 31 | #endif | ||
| @@ -0,0 +1,64 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/25 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #include "HysteresisFilter.h" | ||
| 10 | |||
| 11 | #include <QtCore/QJsonObject> | ||
| 12 | #include <QtCore/QJsonArray> | ||
| 13 | |||
| 14 | using namespace controller; | ||
| 15 | |||
| 16 | HysteresisFilter::HysteresisFilter(float min, float max) : _min(min), _max(max) { | ||
| 17 | if (_min > _max) { | ||
| 18 | std::swap(_min, _max); | ||
| 19 | } | ||
| 20 | }; | ||
| 21 | |||
| 22 | |||
| 23 | float HysteresisFilter::apply(float value) const { | ||
| 24 | if (_signaled) { | ||
| 25 | if (value <= _min) { | ||
| 26 | _signaled = false; | ||
| 27 | } | ||
| 28 | } else { | ||
| 29 | if (value >= _max) { | ||
| 30 | _signaled = true; | ||
| 31 | } | ||
| 32 | } | ||
| 33 | return _signaled ? 1.0f : 0.0f; | ||
| 34 | } | ||
| 35 | |||
| 36 | bool HysteresisFilter::parseParameters(const QJsonValue& parameters) { | ||
| 37 | if (parameters.isArray()) { | ||
| 38 | auto arrayParameters = parameters.toArray(); | ||
| 39 | if (arrayParameters.size() > 1) { | ||
| 40 | _min = arrayParameters[0].toDouble(); | ||
| 41 | } | ||
| 42 | if (arrayParameters.size() > 2) { | ||
| 43 | _max = arrayParameters[1].toDouble(); | ||
| 44 | } | ||
| 45 | } else if (parameters.isObject()) { | ||
| 46 | static const QString JSON_MAX = QStringLiteral("max"); | ||
| 47 | static const QString JSON_MIN = QStringLiteral("min"); | ||
| 48 | |||
| 49 | auto objectParameters = parameters.toObject(); | ||
| 50 | if (objectParameters.contains(JSON_MIN)) { | ||
| 51 | _min = objectParameters[JSON_MIN].toDouble(); | ||
| 52 | } | ||
| 53 | if (objectParameters.contains(JSON_MAX)) { | ||
| 54 | _max = objectParameters[JSON_MAX].toDouble(); | ||
| 55 | } | ||
| 56 | } else { | ||
| 57 | return false; | ||
| 58 | } | ||
| 59 | |||
| 60 | if (_min > _max) { | ||
| 61 | std::swap(_min, _max); | ||
| 62 | } | ||
| 63 | return true; | ||
| 64 | } | ||
| @@ -0,0 +1,31 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/25 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | #ifndef hifi_Controllers_Filters_Hysteresis_h | ||
| 11 | #define hifi_Controllers_Filters_Hysteresis_h | ||
| 12 | |||
| 13 | #include "../Filter.h" | ||
| 14 | |||
| 15 | namespace controller { | ||
| 16 | |||
| 17 | class HysteresisFilter : public Filter { | ||
| 18 | REGISTER_FILTER_CLASS(HysteresisFilter); | ||
| 19 | public: | ||
| 20 | HysteresisFilter(float min = 0.25, float max = 0.75); | ||
| 21 | virtual float apply(float value) const override; | ||
| 22 | virtual bool parseParameters(const QJsonValue& parameters) override; | ||
| 23 | protected: | ||
| 24 | float _min; | ||
| 25 | float _max; | ||
| 26 | mutable bool _signaled { false }; | ||
| 27 | }; | ||
| 28 | |||
| 29 | } | ||
| 30 | |||
| 31 | #endif | ||
| @@ -0,0 +1,9 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/25 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #include "InvertFilter.h" | ||
| @@ -0,0 +1,29 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/25 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | #ifndef hifi_Controllers_Filters_InvertFilter_h | ||
| 11 | #define hifi_Controllers_Filters_InvertFilter_h | ||
| 12 | |||
| 13 | #include "ScaleFilter.h" | ||
| 14 | |||
| 15 | namespace controller { | ||
| 16 | |||
| 17 | class InvertFilter : public ScaleFilter { | ||
| 18 | REGISTER_FILTER_CLASS(InvertFilter); | ||
| 19 | public: | ||
| 20 | InvertFilter() : ScaleFilter(-1.0f) {} | ||
| 21 | |||
| 22 | virtual bool parseParameters(const QJsonArray& parameters) { return true; } | ||
| 23 | |||
| 24 | private: | ||
| 25 | }; | ||
| 26 | |||
| 27 | } | ||
| 28 | |||
| 29 | #endif | ||
| @@ -0,0 +1,37 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/25 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #include "PulseFilter.h" | ||
| 10 | |||
| 11 | #include <QtCore/QJsonObject> | ||
| 12 | #include <QtCore/QJsonArray> | ||
| 13 | |||
| 14 | using namespace controller; | ||
| 15 | |||
| 16 | |||
| 17 | |||
| 18 | float PulseFilter::apply(float value) const { | ||
| 19 | float result = 0.0f; | ||
| 20 | |||
| 21 | if (0.0f != value) { | ||
| 22 | float now = secTimestampNow(); | ||
| 23 | float delta = now - _lastEmitTime; | ||
| 24 | if (delta >= _interval) { | ||
| 25 | _lastEmitTime = now; | ||
| 26 | result = value; | ||
| 27 | } | ||
| 28 | } | ||
| 29 | |||
| 30 | return result; | ||
| 31 | } | ||
| 32 | |||
| 33 | bool PulseFilter::parseParameters(const QJsonValue& parameters) { | ||
| 34 | static const QString JSON_MIN = QStringLiteral("interval"); | ||
| 35 | return parseSingleFloatParameter(parameters, JSON_MIN, _interval); | ||
| 36 | } | ||
| 37 | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/25 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | #ifndef hifi_Controllers_Filters_Pulse_h | ||
| 11 | #define hifi_Controllers_Filters_Pulse_h | ||
| 12 | |||
| 13 | #include "../Filter.h" | ||
| 14 | |||
| 15 | namespace controller { | ||
| 16 | |||
| 17 | |||
| 18 | class PulseFilter : public Filter { | ||
| 19 | REGISTER_FILTER_CLASS(PulseFilter); | ||
| 20 | public: | ||
| 21 | PulseFilter() {} | ||
| 22 | PulseFilter(float interval) : _interval(interval) {} | ||
| 23 | |||
| 24 | |||
| 25 | virtual float apply(float value) const override; | ||
| 26 | |||
| 27 | virtual bool parseParameters(const QJsonValue& parameters); | ||
| 28 | |||
| 29 | private: | ||
| 30 | mutable float _lastEmitTime { -::std::numeric_limits<float>::max() }; | ||
| 31 | float _interval = 1.0f; | ||
| 32 | }; | ||
| 33 | |||
| 34 | } | ||
| 35 | |||
| 36 | #endif | ||
| @@ -0,0 +1,19 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/25 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #include "ScaleFilter.h" | ||
| 10 | |||
| 11 | #include <QtCore/QJsonObject> | ||
| 12 | #include <QtCore/QJsonArray> | ||
| 13 | |||
| 14 | using namespace controller; | ||
| 15 | |||
| 16 | bool ScaleFilter::parseParameters(const QJsonValue& parameters) { | ||
| 17 | static const QString JSON_SCALE = QStringLiteral("scale"); | ||
| 18 | return parseSingleFloatParameter(parameters, JSON_SCALE, _scale); | ||
| 19 | } | ||
| @@ -0,0 +1,34 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/25 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | #ifndef hifi_Controllers_Filters_Scale_h | ||
| 11 | #define hifi_Controllers_Filters_Scale_h | ||
| 12 | |||
| 13 | #include "../Filter.h" | ||
| 14 | |||
| 15 | namespace controller { | ||
| 16 | |||
| 17 | class ScaleFilter : public Filter { | ||
| 18 | REGISTER_FILTER_CLASS(ScaleFilter); | ||
| 19 | public: | ||
| 20 | ScaleFilter() {} | ||
| 21 | ScaleFilter(float scale) : _scale(scale) {} | ||
| 22 | |||
| 23 | virtual float apply(float value) const override { | ||
| 24 | return value * _scale; | ||
| 25 | } | ||
| 26 | virtual bool parseParameters(const QJsonValue& parameters); | ||
| 27 | |||
| 28 | private: | ||
| 29 | float _scale = 1.0f; | ||
| 30 | }; | ||
| 31 | |||
| 32 | } | ||
| 33 | |||
| 34 | #endif | ||
| @@ -27,7 +27,4 @@ void NullDisplayPlugin::preRender() {} · 26 unmodified lines | |||
| 27 | void NullDisplayPlugin::preDisplay() {} | 27 | void NullDisplayPlugin::preDisplay() {} |
| 28 | void NullDisplayPlugin::display(GLuint sceneTexture, const glm::uvec2& sceneSize) {} | 28 | void NullDisplayPlugin::display(GLuint sceneTexture, const glm::uvec2& sceneSize) {} |
| 29 | void NullDisplayPlugin::finishFrame() {} | 29 | void NullDisplayPlugin::finishFrame() {} |
| 30 | |||
| 31 | void NullDisplayPlugin::activate() {} | ||
| 32 | void NullDisplayPlugin::deactivate() {} | ||
| 33 | void NullDisplayPlugin::stop() {} | 30 | void NullDisplayPlugin::stop() {} |
| @@ -15,8 +15,6 @@ public: · 14 unmodified lines | |||
| 15 | virtual ~NullDisplayPlugin() final {} | 15 | virtual ~NullDisplayPlugin() final {} |
| 16 | virtual const QString & getName() const override; | 16 | virtual const QString & getName() const override; |
| 17 | 17 | ||
| 18 | void activate() override; | ||
| 19 | void deactivate() override; | ||
| 20 | void stop() override; | 18 | void stop() override; |
| 21 | 19 | ||
| 22 | virtual glm::uvec2 getRecommendedRenderSize() const override; | 20 | virtual glm::uvec2 getRecommendedRenderSize() const override; |
| @@ -57,12 +57,12 @@ void OpenGLDisplayPlugin::customizeContext() { · 56 unmodified lines | |||
| 57 | } | 57 | } |
| 58 | 58 | ||
| 59 | void OpenGLDisplayPlugin::activate() { | 59 | void OpenGLDisplayPlugin::activate() { |
| 60 | _active = true; | 60 | DisplayPlugin::activate(); |
| 61 | _timer.start(1); | 61 | _timer.start(1); |
| 62 | } | 62 | } |
| 63 | 63 | ||
| 64 | void OpenGLDisplayPlugin::stop() { | 64 | void OpenGLDisplayPlugin::stop() { |
| 65 | _active = false; | 65 | DisplayPlugin::activate(); |
| 66 | _timer.stop(); | 66 | _timer.stop(); |
| 67 | } | 67 | } |
| 68 | 68 | ||
| @@ -44,7 +44,6 @@ protected: · 43 unmodified lines | |||
| 44 | mutable QTimer _timer; | 44 | mutable QTimer _timer; |
| 45 | ProgramPtr _program; | 45 | ProgramPtr _program; |
| 46 | ShapeWrapperPtr _plane; | 46 | ShapeWrapperPtr _plane; |
| 47 | bool _active{ false }; | ||
| 48 | bool _vsyncSupported{ false }; | 47 | bool _vsyncSupported{ false }; |
| 49 | }; | 48 | }; |
| 50 | 49 | ||
| @@ -107,8 +107,7 @@ void EntityTreeRenderer::init() { · 106 unmodified lines | |||
| 107 | entityTree->setFBXService(this); | 107 | entityTree->setFBXService(this); |
| 108 | 108 | ||
| 109 | if (_wantScripts) { | 109 | if (_wantScripts) { |
| 110 | _entitiesScriptEngine = new ScriptEngine(NO_SCRIPT, "Entities", | 110 | _entitiesScriptEngine = new ScriptEngine(NO_SCRIPT, "Entities"); |
| 111 | _scriptingServices->getControllerScriptingInterface()); | ||
| 112 | _scriptingServices->registerScriptEngineWithApplicationServices(_entitiesScriptEngine); | 111 | _scriptingServices->registerScriptEngineWithApplicationServices(_entitiesScriptEngine); |
| 113 | _entitiesScriptEngine->runInThread(); | 112 | _entitiesScriptEngine->runInThread(); |
| 114 | DependencyManager::get<EntityScriptingInterface>()->setEntitiesScriptEngine(_entitiesScriptEngine); | 113 | DependencyManager::get<EntityScriptingInterface>()->setEntitiesScriptEngine(_entitiesScriptEngine); |
| @@ -189,7 +189,7 @@ enum EntityPropertyList { · 188 unmodified lines | |||
| 189 | PROP_BACKGROUND_MODE = PROP_MODEL_URL, | 189 | PROP_BACKGROUND_MODE = PROP_MODEL_URL, |
| 190 | PROP_SKYBOX_COLOR = PROP_ANIMATION_URL, | 190 | PROP_SKYBOX_COLOR = PROP_ANIMATION_URL, |
| 191 | PROP_SKYBOX_URL = PROP_ANIMATION_FPS, | 191 | PROP_SKYBOX_URL = PROP_ANIMATION_FPS, |
| 192 | PROP_KEYLIGHT_AMBIENT_URL = PROP_ANIMATION_FRAME_INDEX, | 192 | PROP_KEYLIGHT_AMBIENT_URL = PROP_ANIMATION_PLAYING, |
| 193 | 193 | ||
| 194 | // Aliases/Piggyback properties for Web. These properties intentionally reuse the enum values for | 194 | // Aliases/Piggyback properties for Web. These properties intentionally reuse the enum values for |
| 195 | // other properties which will never overlap with each other. | 195 | // other properties which will never overlap with each other. |
| @@ -44,8 +44,7 @@ bool GlWindow::makeCurrent() { · 43 unmodified lines | |||
| 44 | qDebug() << "GL Renderer: " << QString((const char*) glGetString(GL_RENDERER)); | 44 | qDebug() << "GL Renderer: " << QString((const char*) glGetString(GL_RENDERER)); |
| 45 | }); | 45 | }); |
| 46 | 46 | ||
| 47 | QOpenGLContext * currentContext = QOpenGLContext::currentContext(); | 47 | Q_ASSERT(_context == QOpenGLContext::currentContext()); |
| 48 | Q_ASSERT(_context == currentContext); | ||
| 49 | 48 | ||
| 50 | return makeCurrentResult; | 49 | return makeCurrentResult; |
| 51 | } | 50 | } |
| @@ -19,6 +19,7 @@ · 18 unmodified lines | |||
| 19 | #include <QMutex> | 19 | #include <QMutex> |
| 20 | 20 | ||
| 21 | #include <PerfStat.h> | 21 | #include <PerfStat.h> |
| 22 | #include <DependencyManager.h> | ||
| 22 | #include <NumericalConstants.h> | 23 | #include <NumericalConstants.h> |
| 23 | 24 | ||
| 24 | #include "GLEscrow.h" | 25 | #include "GLEscrow.h" |
| @@ -611,3 +612,8 @@ QQuickWindow* OffscreenQmlSurface::getWindow() { · 586 unmodified lines | |||
| 611 | QSize OffscreenQmlSurface::size() const { | 612 | QSize OffscreenQmlSurface::size() const { |
| 612 | return _renderer->_quickWindow->geometry().size(); | 613 | return _renderer->_quickWindow->geometry().size(); |
| 613 | } | 614 | } |
| 615 | |||
| 616 | QQmlContext* OffscreenQmlSurface::getRootContext() { | ||
| 617 | return _qmlEngine->rootContext(); | ||
| 618 | } | ||
| 619 | |||
| @@ -60,6 +60,7 @@ public: · 59 unmodified lines | |||
| 60 | QQuickItem* getRootItem(); | 60 | QQuickItem* getRootItem(); |
| 61 | QQuickWindow* getWindow(); | 61 | QQuickWindow* getWindow(); |
| 62 | QObject* getEventHandler(); | 62 | QObject* getEventHandler(); |
| 63 | QQmlContext* getRootContext(); | ||
| 63 | 64 | ||
| 64 | QPointF mapToVirtualScreen(const QPointF& originalPoint, QObject* originalWidget); | 65 | QPointF mapToVirtualScreen(const QPointF& originalPoint, QObject* originalWidget); |
| 65 | virtual bool eventFilter(QObject* originalDestination, QEvent* event); | 66 | virtual bool eventFilter(QObject* originalDestination, QEvent* event); |
| @@ -1,6 +1,6 @@ | |||
| 1 | set(TARGET_NAME input-plugins) | 1 | set(TARGET_NAME input-plugins) |
| 2 | setup_hifi_library() | 2 | setup_hifi_library() |
| 3 | link_hifi_libraries(shared plugins gpu render-utils) | 3 | link_hifi_libraries(shared plugins controllers script-engine render-utils) |
| 4 | 4 | ||
| 5 | GroupSources("src/input-plugins") | 5 | GroupSources("src/input-plugins") |
| 6 | 6 | ||
| @@ -11,55 +11,5 @@ if (WIN32) · 4 unmodified lines | |||
| 11 | target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES}) | 11 | target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES}) |
| 12 | endif() | 12 | endif() |
| 13 | 13 | ||
| 14 | #add_dependency_external_projects(Sixense) | 14 | target_sdl2() |
| 15 | #find_package(Sixense REQUIRED) | 15 | target_sixense() |
| 16 | #target_include_directories(${TARGET_NAME} PRIVATE ${SIXENSE_INCLUDE_DIRS}) | ||
| 17 | #target_link_libraries(${TARGET_NAME} ${SIXENSE_LIBRARIES}) | ||
| 18 | |||
| 19 | # set a default root dir for each of our optional externals if it was not passed | ||
| 20 | set(OPTIONAL_EXTERNALS "SDL2" "Sixense") | ||
| 21 | foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) | ||
| 22 | string(TOUPPER ${EXTERNAL} ${EXTERNAL}_UPPERCASE) | ||
| 23 | if (NOT ${${EXTERNAL}_UPPERCASE}_ROOT_DIR) | ||
| 24 | string(TOLOWER ${EXTERNAL} ${EXTERNAL}_LOWERCASE) | ||
| 25 | set(${${EXTERNAL}_UPPERCASE}_ROOT_DIR "${CMAKE_SOURCE_DIR}/interface/external/${${EXTERNAL}_LOWERCASE}") | ||
| 26 | endif () | ||
| 27 | endforeach() | ||
| 28 | |||
| 29 | # perform standard include and linking for found externals | ||
| 30 | foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) | ||
| 31 | |||
| 32 | if (${${EXTERNAL}_UPPERCASE}_REQUIRED) | ||
| 33 | find_package(${EXTERNAL} REQUIRED) | ||
| 34 | else () | ||
| 35 | find_package(${EXTERNAL}) | ||
| 36 | endif () | ||
| 37 | |||
| 38 | if (${${EXTERNAL}_UPPERCASE}_FOUND AND NOT DISABLE_${${EXTERNAL}_UPPERCASE}) | ||
| 39 | add_definitions(-DHAVE_${${EXTERNAL}_UPPERCASE}) | ||
| 40 | |||
| 41 | # include the library directories (ignoring warnings) | ||
| 42 | if (NOT ${${EXTERNAL}_UPPERCASE}_INCLUDE_DIRS) | ||
| 43 | set(${${EXTERNAL}_UPPERCASE}_INCLUDE_DIRS ${${${EXTERNAL}_UPPERCASE}_INCLUDE_DIR}) | ||
| 44 | endif () | ||
| 45 | |||
| 46 | include_directories(SYSTEM ${${${EXTERNAL}_UPPERCASE}_INCLUDE_DIRS}) | ||
| 47 | |||
| 48 | # perform the system include hack for OS X to ignore warnings | ||
| 49 | if (APPLE) | ||
| 50 | foreach(EXTERNAL_INCLUDE_DIR ${${${EXTERNAL}_UPPERCASE}_INCLUDE_DIRS}) | ||
| 51 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${EXTERNAL_INCLUDE_DIR}") | ||
| 52 | endforeach() | ||
| 53 | endif () | ||
| 54 | |||
| 55 | if (NOT ${${EXTERNAL}_UPPERCASE}_LIBRARIES) | ||
| 56 | set(${${EXTERNAL}_UPPERCASE}_LIBRARIES ${${${EXTERNAL}_UPPERCASE}_LIBRARY}) | ||
| 57 | endif () | ||
| 58 | |||
| 59 | if (NOT APPLE OR NOT ${${EXTERNAL}_UPPERCASE} MATCHES "SIXENSE") | ||
| 60 | target_link_libraries(${TARGET_NAME} ${${${EXTERNAL}_UPPERCASE}_LIBRARIES}) | ||
| 61 | elseif (APPLE AND NOT INSTALLER_BUILD) | ||
| 62 | add_definitions(-DSIXENSE_LIB_FILENAME=\"${${${EXTERNAL}_UPPERCASE}_LIBRARY_RELEASE}\") | ||
| 63 | endif () | ||
| 64 | endif () | ||
| 65 | endforeach() | ||
| @@ -1,59 +0,0 @@ | |||
| 1 | // | ||
| 2 | // InputDevice.cpp | ||
| 3 | // input-plugins/src/input-plugins | ||
| 4 | // | ||
| 5 | // Created by Sam Gondelman on 7/15/2015 | ||
| 6 | // Copyright 2015 High Fidelity, Inc. | ||
| 7 | // | ||
| 8 | // Distributed under the Apache License, Version 2.0. | ||
| 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 10 | // | ||
| 11 | #include "InputDevice.h" | ||
| 12 | |||
| 13 | bool InputDevice::_lowVelocityFilter = false; | ||
| 14 | |||
| 15 | const float DEFAULT_HAND_RETICLE_MOVE_SPEED = 37.5f; | ||
| 16 | float InputDevice::reticleMoveSpeed = DEFAULT_HAND_RETICLE_MOVE_SPEED; | ||
| 17 | |||
| 18 | //Constants for getCursorPixelRangeMultiplier() | ||
| 19 | const float MIN_PIXEL_RANGE_MULT = 0.4f; | ||
| 20 | const float MAX_PIXEL_RANGE_MULT = 2.0f; | ||
| 21 | const float RANGE_MULT = (MAX_PIXEL_RANGE_MULT - MIN_PIXEL_RANGE_MULT) * 0.01f; | ||
| 22 | |||
| 23 | //Returns a multiplier to be applied to the cursor range for the controllers | ||
| 24 | float InputDevice::getCursorPixelRangeMult() { | ||
| 25 | //scales (0,100) to (MINIMUM_PIXEL_RANGE_MULT, MAXIMUM_PIXEL_RANGE_MULT) | ||
| 26 | return InputDevice::reticleMoveSpeed * RANGE_MULT + MIN_PIXEL_RANGE_MULT; | ||
| 27 | } | ||
| 28 | |||
| 29 | float InputDevice::getButton(int channel) const { | ||
| 30 | if (!_buttonPressedMap.empty()) { | ||
| 31 | if (_buttonPressedMap.find(channel) != _buttonPressedMap.end()) { | ||
| 32 | return 1.0f; | ||
| 33 | } | ||
| 34 | else { | ||
| 35 | return 0.0f; | ||
| 36 | } | ||
| 37 | } | ||
| 38 | return 0.0f; | ||
| 39 | } | ||
| 40 | |||
| 41 | float InputDevice::getAxis(int channel) const { | ||
| 42 | auto axis = _axisStateMap.find(channel); | ||
| 43 | if (axis != _axisStateMap.end()) { | ||
| 44 | return (*axis).second; | ||
| 45 | } | ||
| 46 | else { | ||
| 47 | return 0.0f; | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | UserInputMapper::PoseValue InputDevice::getPose(int channel) const { | ||
| 52 | auto pose = _poseStateMap.find(channel); | ||
| 53 | if (pose != _poseStateMap.end()) { | ||
| 54 | return (*pose).second; | ||
| 55 | } | ||
| 56 | else { | ||
| 57 | return UserInputMapper::PoseValue(); | ||
| 58 | } | ||
| 59 | } | ||
| @@ -9,19 +9,17 @@ · 8 unmodified lines | |||
| 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html |
| 10 | // | 10 | // |
| 11 | 11 | ||
| 12 | #include <limits> | ||
| 13 | |||
| 14 | #include <glm/glm.hpp> | ||
| 15 | |||
| 16 | #include "Joystick.h" | 12 | #include "Joystick.h" |
| 17 | 13 | ||
| 14 | #include <PathUtils.h> | ||
| 15 | |||
| 18 | const float CONTROLLER_THRESHOLD = 0.3f; | 16 | const float CONTROLLER_THRESHOLD = 0.3f; |
| 19 | 17 | ||
| 20 | #ifdef HAVE_SDL2 | 18 | #ifdef HAVE_SDL2 |
| 21 | const float MAX_AXIS = 32768.0f; | 19 | const float MAX_AXIS = 32768.0f; |
| 22 | 20 | ||
| 23 | Joystick::Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController) : | 21 | Joystick::Joystick(SDL_JoystickID instanceId, SDL_GameController* sdlGameController) : |
| 24 | InputDevice(name), | 22 | InputDevice("GamePad"), |
| 25 | _sdlGameController(sdlGameController), | 23 | _sdlGameController(sdlGameController), |
| 26 | _sdlJoystick(SDL_GameControllerGetJoystick(_sdlGameController)), | 24 | _sdlJoystick(SDL_GameControllerGetJoystick(_sdlGameController)), |
| 27 | _instanceId(instanceId) | 25 | _instanceId(instanceId) |
| @@ -55,39 +53,14 @@ void Joystick::focusOutEvent() { · 27 unmodified lines | |||
| 55 | }; | 53 | }; |
| 56 | 54 | ||
| 57 | #ifdef HAVE_SDL2 | 55 | #ifdef HAVE_SDL2 |
| 56 | |||
| 58 | void Joystick::handleAxisEvent(const SDL_ControllerAxisEvent& event) { | 57 | void Joystick::handleAxisEvent(const SDL_ControllerAxisEvent& event) { |
| 59 | SDL_GameControllerAxis axis = (SDL_GameControllerAxis) event.axis; | 58 | SDL_GameControllerAxis axis = (SDL_GameControllerAxis) event.axis; |
| 60 | 59 | _axisStateMap[makeInput((controller::StandardAxisChannel)axis).getChannel()] = (float)event.value / MAX_AXIS; | |
| 61 | switch (axis) { | ||
| 62 | case SDL_CONTROLLER_AXIS_LEFTX: | ||
| 63 | _axisStateMap[makeInput(LEFT_AXIS_X_POS).getChannel()] = (event.value > 0.0f) ? event.value / MAX_AXIS : 0.0f; | ||
| 64 | _axisStateMap[makeInput(LEFT_AXIS_X_NEG).getChannel()] = (event.value < 0.0f) ? -event.value / MAX_AXIS : 0.0f; | ||
| 65 | break; | ||
| 66 | case SDL_CONTROLLER_AXIS_LEFTY: | ||
| 67 | _axisStateMap[makeInput(LEFT_AXIS_Y_POS).getChannel()] = (event.value > 0.0f) ? event.value / MAX_AXIS : 0.0f; | ||
| 68 | _axisStateMap[makeInput(LEFT_AXIS_Y_NEG).getChannel()] = (event.value < 0.0f) ? -event.value / MAX_AXIS : 0.0f; | ||
| 69 | break; | ||
| 70 | case SDL_CONTROLLER_AXIS_RIGHTX: | ||
| 71 | _axisStateMap[makeInput(RIGHT_AXIS_X_POS).getChannel()] = (event.value > 0.0f) ? event.value / MAX_AXIS : 0.0f; | ||
| 72 | _axisStateMap[makeInput(RIGHT_AXIS_X_NEG).getChannel()] = (event.value < 0.0f) ? -event.value / MAX_AXIS : 0.0f; | ||
| 73 | break; | ||
| 74 | case SDL_CONTROLLER_AXIS_RIGHTY: | ||
| 75 | _axisStateMap[makeInput(RIGHT_AXIS_Y_POS).getChannel()] = (event.value > 0.0f) ? event.value / MAX_AXIS : 0.0f; | ||
| 76 | _axisStateMap[makeInput(RIGHT_AXIS_Y_NEG).getChannel()] = (event.value < 0.0f) ? -event.value / MAX_AXIS : 0.0f; | ||
| 77 | break; | ||
| 78 | case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: | ||
| 79 | _axisStateMap[makeInput(RIGHT_SHOULDER).getChannel()] = event.value / MAX_AXIS; | ||
| 80 | break; | ||
| 81 | case SDL_CONTROLLER_AXIS_TRIGGERLEFT: | ||
| 82 | _axisStateMap[makeInput(LEFT_SHOULDER).getChannel()] = event.value / MAX_AXIS; | ||
| 83 | break; | ||
| 84 | default: | ||
| 85 | break; | ||
| 86 | } | ||
| 87 | } | 60 | } |
| 88 | 61 | ||
| 89 | void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) { | 62 | void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) { |
| 90 | auto input = makeInput((SDL_GameControllerButton) event.button); | 63 | auto input = makeInput((controller::StandardButtonChannel)event.button); |
| 91 | bool newValue = event.state == SDL_PRESSED; | 64 | bool newValue = event.state == SDL_PRESSED; |
| 92 | if (newValue) { | 65 | if (newValue) { |
| 93 | _buttonPressedMap.insert(input.getChannel()); | 66 | _buttonPressedMap.insert(input.getChannel()); |
| @@ -95,131 +68,61 @@ void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) { · 1 unmodified line | |||
| 95 | _buttonPressedMap.erase(input.getChannel()); | 68 | _buttonPressedMap.erase(input.getChannel()); |
| 96 | } | 69 | } |
| 97 | } | 70 | } |
| 98 | #endif | ||
| 99 | |||
| 100 | |||
| 101 | void Joystick::registerToUserInputMapper(UserInputMapper& mapper) { | ||
| 102 | // Grab the current free device ID | ||
| 103 | _deviceID = mapper.getFreeDeviceID(); | ||
| 104 | |||
| 105 | auto proxy = std::make_shared<UserInputMapper::DeviceProxy>(_name); | ||
| 106 | proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; | ||
| 107 | proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; | ||
| 108 | proxy->getAvailabeInputs = [this] () -> QVector<UserInputMapper::InputPair> { | ||
| 109 | QVector<UserInputMapper::InputPair> availableInputs; | ||
| 110 | #ifdef HAVE_SDL2 | ||
| 111 | availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_A), "Bottom Button")); | ||
| 112 | availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_B), "Right Button")); | ||
| 113 | availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_X), "Left Button")); | ||
| 114 | availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_Y), "Top Button")); | ||
| 115 | |||
| 116 | availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_DPAD_UP), "DPad Up")); | ||
| 117 | availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_DPAD_DOWN), "DPad Down")); | ||
| 118 | availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_DPAD_LEFT), "DPad Left")); | ||
| 119 | availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_DPAD_RIGHT), "DPad Right")); | ||
| 120 | |||
| 121 | availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_LEFTSHOULDER), "L1")); | ||
| 122 | availableInputs.append(UserInputMapper::InputPair(makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), "R1")); | ||
| 123 | availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_SHOULDER), "L2")); | ||
| 124 | availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_SHOULDER), "R2")); | ||
| 125 | |||
| 126 | availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_Y_NEG), "Left Stick Up")); | ||
| 127 | availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_Y_POS), "Left Stick Down")); | ||
| 128 | availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_X_POS), "Left Stick Right")); | ||
| 129 | availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_X_NEG), "Left Stick Left")); | ||
| 130 | availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_Y_NEG), "Right Stick Up")); | ||
| 131 | availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_Y_POS), "Right Stick Down")); | ||
| 132 | availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_X_POS), "Right Stick Right")); | ||
| 133 | availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_X_NEG), "Right Stick Left")); | ||
| 134 | 71 | ||
| 135 | #endif | 72 | #endif |
| 136 | return availableInputs; | ||
| 137 | }; | ||
| 138 | proxy->resetDeviceBindings = [this, &mapper] () -> bool { | ||
| 139 | mapper.removeAllInputChannelsForDevice(_deviceID); | ||
| 140 | this->assignDefaultInputMapping(mapper); | ||
| 141 | return true; | ||
| 142 | }; | ||
| 143 | mapper.registerDevice(_deviceID, proxy); | ||
| 144 | } | ||
| 145 | 73 | ||
| 146 | void Joystick::assignDefaultInputMapping(UserInputMapper& mapper) { | 74 | controller::Input::NamedVector Joystick::getAvailableInputs() const { |
| 147 | #ifdef HAVE_SDL2 | 75 | using namespace controller; |
| 148 | const float JOYSTICK_MOVE_SPEED = 1.0f; | 76 | static const Input::NamedVector availableInputs{ |
| 149 | const float DPAD_MOVE_SPEED = 0.5f; | 77 | makePair(A, "A"), |
| 150 | const float JOYSTICK_YAW_SPEED = 0.5f; | 78 | makePair(B, "B"), |
| 151 | const float JOYSTICK_PITCH_SPEED = 0.25f; | 79 | makePair(X, "X"), |
| 152 | const float BOOM_SPEED = 0.1f; | 80 | makePair(Y, "Y"), |
| 153 | 81 | // DPad | |
| 154 | // Y axes are flipped (up is negative) | 82 | makePair(DU, "DU"), |
| 155 | // Left Joystick: Movement, strafing | 83 | makePair(DD, "DD"), |
| 156 | mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(LEFT_AXIS_Y_NEG), JOYSTICK_MOVE_SPEED); | 84 | makePair(DL, "DL"), |
| 157 | mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(LEFT_AXIS_Y_POS), JOYSTICK_MOVE_SPEED); | 85 | makePair(DR, "DR"), |
| 158 | mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(LEFT_AXIS_X_POS), JOYSTICK_MOVE_SPEED); | 86 | // Bumpers |
| 159 | mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(LEFT_AXIS_X_NEG), JOYSTICK_MOVE_SPEED); | 87 | makePair(LB, "LB"), |
| 160 | 88 | makePair(RB, "RB"), | |
| 161 | // Right Joystick: Camera orientation | 89 | // Stick press |
| 162 | mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(RIGHT_AXIS_X_POS), JOYSTICK_YAW_SPEED); | 90 | makePair(LS, "LS"), |
| 163 | mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(RIGHT_AXIS_X_NEG), JOYSTICK_YAW_SPEED); | 91 | makePair(RS, "RS"), |
| 164 | mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(RIGHT_AXIS_Y_NEG), JOYSTICK_PITCH_SPEED); | 92 | // Center buttons |
| 165 | mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(RIGHT_AXIS_Y_POS), JOYSTICK_PITCH_SPEED); | 93 | makePair(START, "Start"), |
| 166 | 94 | makePair(BACK, "Back"), | |
| 167 | // Dpad movement | 95 | // Analog sticks |
| 168 | mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_UP), DPAD_MOVE_SPEED); | 96 | makePair(LX, "LX"), |
| 169 | mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_DOWN), DPAD_MOVE_SPEED); | 97 | makePair(LY, "LY"), |
| 170 | mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_RIGHT), DPAD_MOVE_SPEED); | 98 | makePair(RX, "RX"), |
| 171 | mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_LEFT), DPAD_MOVE_SPEED); | 99 | makePair(RY, "RY"), |
| 172 | 100 | ||
| 173 | // Button controls | 101 | // Triggers |
| 174 | mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(SDL_CONTROLLER_BUTTON_Y), DPAD_MOVE_SPEED); | 102 | makePair(LT, "LT"), |
| 175 | mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(SDL_CONTROLLER_BUTTON_X), DPAD_MOVE_SPEED); | 103 | makePair(RT, "RT"), |
| 176 | 104 | ||
| 177 | // Zoom | 105 | // Aliases, PlayStation style names |
| 178 | mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(RIGHT_SHOULDER), BOOM_SPEED); | 106 | makePair(LB, "L1"), |
| 179 | mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(LEFT_SHOULDER), BOOM_SPEED); | 107 | makePair(RB, "R1"), |
| 180 | 108 | makePair(LT, "L2"), | |
| 181 | 109 | makePair(RT, "R2"), | |
| 182 | // Hold front right shoulder button for precision controls | 110 | makePair(LS, "L3"), |
| 183 | // Left Joystick: Movement, strafing | 111 | makePair(RS, "R3"), |
| 184 | mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(LEFT_AXIS_Y_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f); | 112 | makePair(BACK, "Select"), |
| 185 | mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(LEFT_AXIS_Y_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f); | 113 | makePair(A, "Cross"), |
| 186 | mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(LEFT_AXIS_X_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f); | 114 | makePair(B, "Circle"), |
| 187 | mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(LEFT_AXIS_X_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f); | 115 | makePair(X, "Square"), |
| 188 | 116 | makePair(Y, "Triangle"), | |
| 189 | // Right Joystick: Camera orientation | 117 | makePair(DU, "Up"), |
| 190 | mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(RIGHT_AXIS_X_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f); | 118 | makePair(DD, "Down"), |
| 191 | mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(RIGHT_AXIS_X_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f); | 119 | makePair(DL, "Left"), |
| 192 | mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(RIGHT_AXIS_Y_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_PITCH_SPEED/2.0f); | 120 | makePair(DR, "Right"), |
| 193 | mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(RIGHT_AXIS_Y_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_PITCH_SPEED/2.0f); | 121 | }; |
| 194 | 122 | return availableInputs; | |
| 195 | // Dpad movement | ||
| 196 | mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_UP), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); | ||
| 197 | mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_DOWN), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); | ||
| 198 | mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_RIGHT), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); | ||
| 199 | mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_LEFT), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); | ||
| 200 | |||
| 201 | // Button controls | ||
| 202 | mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(SDL_CONTROLLER_BUTTON_Y), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); | ||
| 203 | mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(SDL_CONTROLLER_BUTTON_X), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); | ||
| 204 | |||
| 205 | // Zoom | ||
| 206 | mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(RIGHT_SHOULDER), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), BOOM_SPEED/2.0f); | ||
| 207 | mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(LEFT_SHOULDER), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), BOOM_SPEED/2.0f); | ||
| 208 | |||
| 209 | mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(SDL_CONTROLLER_BUTTON_LEFTSHOULDER)); | ||
| 210 | |||
| 211 | mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(SDL_CONTROLLER_BUTTON_B)); | ||
| 212 | mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(SDL_CONTROLLER_BUTTON_A)); | ||
| 213 | #endif | ||
| 214 | } | ||
| 215 | |||
| 216 | #ifdef HAVE_SDL2 | ||
| 217 | UserInputMapper::Input Joystick::makeInput(SDL_GameControllerButton button) { | ||
| 218 | return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON); | ||
| 219 | } | 123 | } |
| 220 | #endif | ||
| 221 | 124 | ||
| 222 | UserInputMapper::Input Joystick::makeInput(Joystick::JoystickAxisChannel axis) { | 125 | QString Joystick::getDefaultMappingConfig() const { |
| 223 | return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS); | 126 | static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/xbox.json"; |
| 127 | return MAPPING_JSON; | ||
| 224 | } | 128 | } |
| 225 | |||
| @@ -20,9 +20,10 @@ · 19 unmodified lines | |||
| 20 | #undef main | 20 | #undef main |
| 21 | #endif | 21 | #endif |
| 22 | 22 | ||
| 23 | #include "InputDevice.h" | 23 | #include <controllers/InputDevice.h> |
| 24 | #include <controllers/StandardControls.h> | ||
| 24 | 25 | ||
| 25 | class Joystick : public QObject, public InputDevice { | 26 | class Joystick : public QObject, public controller::InputDevice { |
| 26 | Q_OBJECT | 27 | Q_OBJECT |
| 27 | Q_PROPERTY(QString name READ getName) | 28 | Q_PROPERTY(QString name READ getName) |
| 28 | 29 | ||
| @@ -31,37 +32,21 @@ class Joystick : public QObject, public InputDevice { · 2 unmodified lines | |||
| 31 | #endif | 32 | #endif |
| 32 | 33 | ||
| 33 | public: | 34 | public: |
| 34 | enum JoystickAxisChannel { | 35 | using Pointer = std::shared_ptr<Joystick>; |
| 35 | LEFT_AXIS_X_POS = 0, | ||
| 36 | LEFT_AXIS_X_NEG, | ||
| 37 | LEFT_AXIS_Y_POS, | ||
| 38 | LEFT_AXIS_Y_NEG, | ||
| 39 | RIGHT_AXIS_X_POS, | ||
| 40 | RIGHT_AXIS_X_NEG, | ||
| 41 | RIGHT_AXIS_Y_POS, | ||
| 42 | RIGHT_AXIS_Y_NEG, | ||
| 43 | RIGHT_SHOULDER, | ||
| 44 | LEFT_SHOULDER, | ||
| 45 | }; | ||
| 46 | 36 | ||
| 47 | const QString& getName() const { return _name; } | 37 | const QString& getName() const { return _name; } |
| 48 | 38 | ||
| 49 | // Device functions | 39 | // Device functions |
| 50 | virtual void registerToUserInputMapper(UserInputMapper& mapper) override; | 40 | virtual controller::Input::NamedVector getAvailableInputs() const override; |
| 51 | virtual void assignDefaultInputMapping(UserInputMapper& mapper) override; | 41 | virtual QString getDefaultMappingConfig() const override; |
| 52 | virtual void update(float deltaTime, bool jointsCaptured) override; | 42 | virtual void update(float deltaTime, bool jointsCaptured) override; |
| 53 | virtual void focusOutEvent() override; | 43 | virtual void focusOutEvent() override; |
| 54 | 44 | ||
| 55 | Joystick() : InputDevice("Joystick") {} | 45 | Joystick() : InputDevice("GamePad") {} |
| 56 | ~Joystick(); | 46 | ~Joystick(); |
| 57 | 47 | ||
| 58 | #ifdef HAVE_SDL2 | 48 | #ifdef HAVE_SDL2 |
| 59 | UserInputMapper::Input makeInput(SDL_GameControllerButton button); | 49 | Joystick(SDL_JoystickID instanceId, SDL_GameController* sdlGameController); |
| 60 | #endif | ||
| 61 | UserInputMapper::Input makeInput(Joystick::JoystickAxisChannel axis); | ||
| 62 | |||
| 63 | #ifdef HAVE_SDL2 | ||
| 64 | Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController); | ||
| 65 | #endif | 50 | #endif |
| 66 | 51 | ||
| 67 | void closeJoystick(); | 52 | void closeJoystick(); |
| @@ -10,6 +10,13 @@ · 9 unmodified lines | |||
| 10 | // | 10 | // |
| 11 | #include "KeyboardMouseDevice.h" | 11 | #include "KeyboardMouseDevice.h" |
| 12 | 12 | ||
| 13 | #include <QtGui/QKeyEvent> | ||
| 14 | #include <QtGui/QMouseEvent> | ||
| 15 | #include <QtGui/QTouchEvent> | ||
| 16 | |||
| 17 | #include <controllers/UserInputMapper.h> | ||
| 18 | #include <PathUtils.h> | ||
| 19 | |||
| 13 | const QString KeyboardMouseDevice::NAME = "Keyboard/Mouse"; | 20 | const QString KeyboardMouseDevice::NAME = "Keyboard/Mouse"; |
| 14 | 21 | ||
| 15 | void KeyboardMouseDevice::update(float deltaTime, bool jointsCaptured) { | 22 | void KeyboardMouseDevice::update(float deltaTime, bool jointsCaptured) { |
| @@ -81,7 +88,7 @@ void KeyboardMouseDevice::wheelEvent(QWheelEvent* event) { · 65 unmodified lines | |||
| 81 | _axisStateMap[makeInput(MOUSE_AXIS_WHEEL_Y_NEG).getChannel()] = (currentMove.y() < 0 ? -currentMove.y() : 0.0f); | 88 | _axisStateMap[makeInput(MOUSE_AXIS_WHEEL_Y_NEG).getChannel()] = (currentMove.y() < 0 ? -currentMove.y() : 0.0f); |
| 82 | } | 89 | } |
| 83 | 90 | ||
| 84 | glm::vec2 KeyboardMouseDevice::evalAverageTouchPoints(const QList<QTouchEvent::TouchPoint>& points) const { | 91 | glm::vec2 evalAverageTouchPoints(const QList<QTouchEvent::TouchPoint>& points) { |
| 85 | glm::vec2 averagePoint(0.0f); | 92 | glm::vec2 averagePoint(0.0f); |
| 86 | if (points.count() > 0) { | 93 | if (points.count() > 0) { |
| 87 | for (auto& point : points) { | 94 | for (auto& point : points) { |
| @@ -123,163 +130,151 @@ void KeyboardMouseDevice::touchUpdateEvent(const QTouchEvent* event) { · 35 unmodified lines | |||
| 123 | _lastTouch = currentPos; | 130 | _lastTouch = currentPos; |
| 124 | } | 131 | } |
| 125 | 132 | ||
| 126 | UserInputMapper::Input KeyboardMouseDevice::makeInput(Qt::Key code) { | 133 | controller::Input KeyboardMouseDevice::makeInput(Qt::Key code) const { |
| 127 | auto shortCode = (UserInputMapper::uint16)(code & KEYBOARD_MASK); | 134 | auto shortCode = (uint16_t)(code & KEYBOARD_MASK); |
| 128 | if (shortCode != code) { | 135 | if (shortCode != code) { |
| 129 | shortCode |= 0x0800; // add this bit instead of the way Qt::Key add a bit on the 3rd byte for some keys | 136 | shortCode |= 0x0800; // add this bit instead of the way Qt::Key add a bit on the 3rd byte for some keys |
| 130 | } | 137 | } |
| 131 | return UserInputMapper::Input(_deviceID, shortCode, UserInputMapper::ChannelType::BUTTON); | 138 | return controller::Input(_deviceID, shortCode, controller::ChannelType::BUTTON); |
| 132 | } | 139 | } |
| 133 | 140 | ||
| 134 | UserInputMapper::Input KeyboardMouseDevice::makeInput(Qt::MouseButton code) { | 141 | controller::Input KeyboardMouseDevice::makeInput(Qt::MouseButton code) const { |
| 135 | switch (code) { | 142 | switch (code) { |
| 136 | case Qt::LeftButton: | 143 | case Qt::LeftButton: |
| 137 | return UserInputMapper::Input(_deviceID, MOUSE_BUTTON_LEFT, UserInputMapper::ChannelType::BUTTON); | 144 | return controller::Input(_deviceID, MOUSE_BUTTON_LEFT, controller::ChannelType::BUTTON); |
| 138 | case Qt::RightButton: | 145 | case Qt::RightButton: |
| 139 | return UserInputMapper::Input(_deviceID, MOUSE_BUTTON_RIGHT, UserInputMapper::ChannelType::BUTTON); | 146 | return controller::Input(_deviceID, MOUSE_BUTTON_RIGHT, controller::ChannelType::BUTTON); |
| 140 | case Qt::MiddleButton: | 147 | case Qt::MiddleButton: |
| 141 | return UserInputMapper::Input(_deviceID, MOUSE_BUTTON_MIDDLE, UserInputMapper::ChannelType::BUTTON); | 148 | return controller::Input(_deviceID, MOUSE_BUTTON_MIDDLE, controller::ChannelType::BUTTON); |
| 142 | default: | 149 | default: |
| 143 | return UserInputMapper::Input(); | 150 | return controller::Input(); |
| 144 | }; | 151 | }; |
| 145 | } | 152 | } |
| 146 | 153 | ||
| 147 | UserInputMapper::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::MouseAxisChannel axis) { | 154 | controller::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::MouseAxisChannel axis) const { |
| 148 | return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS); | 155 | return controller::Input(_deviceID, axis, controller::ChannelType::AXIS); |
| 149 | } | 156 | } |
| 150 | 157 | ||
| 151 | UserInputMapper::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchAxisChannel axis) { | 158 | controller::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchAxisChannel axis) const { |
| 152 | return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS); | 159 | return controller::Input(_deviceID, axis, controller::ChannelType::AXIS); |
| 153 | } | 160 | } |
| 154 | 161 | ||
| 155 | UserInputMapper::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchButtonChannel button) { | 162 | controller::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchButtonChannel button) const { |
| 156 | return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON); | 163 | return controller::Input(_deviceID, button, controller::ChannelType::BUTTON); |
| 157 | } | 164 | } |
| 158 | 165 | ||
| 159 | void KeyboardMouseDevice::registerToUserInputMapper(UserInputMapper& mapper) { | 166 | controller::Input::NamedVector KeyboardMouseDevice::getAvailableInputs() const { |
| 160 | // Grab the current free device ID | 167 | using namespace controller; |
| 161 | _deviceID = mapper.getFreeDeviceID(); | 168 | static QVector<Input::NamedPair> availableInputs; |
| 162 | 169 | static std::once_flag once; | |
| 163 | auto proxy = std::make_shared<UserInputMapper::DeviceProxy>(_name); | 170 | std::call_once(once, [&] { |
| 164 | proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; | 171 | for (int i = (int)Qt::Key_0; i <= (int)Qt::Key_9; i++) { |
| 165 | proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; | 172 | availableInputs.append(Input::NamedPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString())); |
| 166 | proxy->getAvailabeInputs = [this] () -> QVector<UserInputMapper::InputPair> { | ||
| 167 | QVector<UserInputMapper::InputPair> availableInputs; | ||
| 168 | for (int i = (int) Qt::Key_0; i <= (int) Qt::Key_9; i++) { | ||
| 169 | availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString())); | ||
| 170 | } | 173 | } |
| 171 | for (int i = (int) Qt::Key_A; i <= (int) Qt::Key_Z; i++) { | 174 | for (int i = (int)Qt::Key_A; i <= (int)Qt::Key_Z; i++) { |
| 172 | availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString())); | 175 | availableInputs.append(Input::NamedPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString())); |
| 173 | } | 176 | } |
| 174 | for (int i = (int) Qt::Key_Left; i <= (int) Qt::Key_Down; i++) { | 177 | for (int i = (int)Qt::Key_Left; i <= (int)Qt::Key_Down; i++) { |
| 175 | availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString())); | 178 | availableInputs.append(Input::NamedPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString())); |
| 176 | } | 179 | } |
| 177 | availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_Space), QKeySequence(Qt::Key_Space).toString())); | 180 | availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Space), QKeySequence(Qt::Key_Space).toString())); |
| 178 | availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_Shift), "Shift")); | 181 | availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Shift), "Shift")); |
| 179 | availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_PageUp), QKeySequence(Qt::Key_PageUp).toString())); | 182 | availableInputs.append(Input::NamedPair(makeInput(Qt::Key_PageUp), QKeySequence(Qt::Key_PageUp).toString())); |
| 180 | availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_PageDown), QKeySequence(Qt::Key_PageDown).toString())); | 183 | availableInputs.append(Input::NamedPair(makeInput(Qt::Key_PageDown), QKeySequence(Qt::Key_PageDown).toString())); |
| 181 | 184 | ||
| 182 | availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::LeftButton), "Left Mouse Click")); | 185 | availableInputs.append(Input::NamedPair(makeInput(Qt::LeftButton), "LeftMouseClick")); |
| 183 | availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::MiddleButton), "Middle Mouse Click")); | 186 | availableInputs.append(Input::NamedPair(makeInput(Qt::MiddleButton), "MiddleMouseClick")); |
| 184 | availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::RightButton), "Right Mouse Click")); | 187 | availableInputs.append(Input::NamedPair(makeInput(Qt::RightButton), "RightMouseClick")); |
| 185 | 188 | ||
| 186 | availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_X_POS), "Mouse Move Right")); | 189 | availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_X_POS), "MouseMoveRight")); |
| 187 | availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_X_NEG), "Mouse Move Left")); | 190 | availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_X_NEG), "MouseMoveLeft")); |
| 188 | availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_Y_POS), "Mouse Move Up")); | 191 | availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_Y_POS), "MouseMoveUp")); |
| 189 | availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_Y_NEG), "Mouse Move Down")); | 192 | availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_Y_NEG), "MouseMoveDown")); |
| 190 | 193 | ||
| 191 | availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_WHEEL_Y_POS), "Mouse Wheel Right")); | 194 | availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_Y_POS), "MouseWheelRight")); |
| 192 | availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_WHEEL_Y_NEG), "Mouse Wheel Left")); | 195 | availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_Y_NEG), "MouseWheelLeft")); |
| 193 | availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_WHEEL_X_POS), "Mouse Wheel Up")); | 196 | availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_X_POS), "MouseWheelUp")); |
| 194 | availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_WHEEL_X_NEG), "Mouse Wheel Down")); | 197 | availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_X_NEG), "MouseWheelDown")); |
| 195 | 198 | ||
| 196 | availableInputs.append(UserInputMapper::InputPair(makeInput(TOUCH_AXIS_X_POS), "Touchpad Right")); | 199 | availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_X_POS), "TouchpadRight")); |
| 197 | availableInputs.append(UserInputMapper::InputPair(makeInput(TOUCH_AXIS_X_NEG), "Touchpad Left")); | 200 | availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_X_NEG), "TouchpadLeft")); |
| 198 | availableInputs.append(UserInputMapper::InputPair(makeInput(TOUCH_AXIS_Y_POS), "Touchpad Up")); | 201 | availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_Y_POS), "TouchpadUp")); |
| 199 | availableInputs.append(UserInputMapper::InputPair(makeInput(TOUCH_AXIS_Y_NEG), "Touchpad Down")); | 202 | availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_Y_NEG), "TouchpadDown")); |
| 200 | 203 | }); | |
| 201 | return availableInputs; | 204 | return availableInputs; |
| 202 | }; | ||
| 203 | proxy->resetDeviceBindings = [this, &mapper] () -> bool { | ||
| 204 | mapper.removeAllInputChannelsForDevice(_deviceID); | ||
| 205 | this->assignDefaultInputMapping(mapper); | ||
| 206 | return true; | ||
| 207 | }; | ||
| 208 | mapper.registerDevice(_deviceID, proxy); | ||
| 209 | } | 205 | } |
| 210 | 206 | ||
| 211 | void KeyboardMouseDevice::assignDefaultInputMapping(UserInputMapper& mapper) { | 207 | QString KeyboardMouseDevice::getDefaultMappingConfig() const { |
| 212 | const float BUTTON_MOVE_SPEED = 1.0f; | 208 | static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/keyboardMouse.json"; |
| 213 | const float BUTTON_YAW_SPEED = 0.75f; | 209 | return MAPPING_JSON; |
| 214 | const float BUTTON_PITCH_SPEED = 0.5f; | ||
| 215 | const float MOUSE_YAW_SPEED = 0.5f; | ||
| 216 | const float MOUSE_PITCH_SPEED = 0.25f; | ||
| 217 | const float TOUCH_YAW_SPEED = 0.5f; | ||
| 218 | const float TOUCH_PITCH_SPEED = 0.25f; | ||
| 219 | const float BUTTON_BOOM_SPEED = 0.1f; | ||
| 220 | |||
| 221 | // AWSD keys mapping | ||
| 222 | mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(Qt::Key_S), BUTTON_MOVE_SPEED); | ||
| 223 | mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(Qt::Key_W), BUTTON_MOVE_SPEED); | ||
| 224 | mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(Qt::Key_A), BUTTON_MOVE_SPEED); | ||
| 225 | mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(Qt::Key_D), BUTTON_MOVE_SPEED); | ||
| 226 | mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(Qt::Key_C), BUTTON_MOVE_SPEED); | ||
| 227 | mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(Qt::Key_E), BUTTON_MOVE_SPEED); | ||
| 228 | |||
| 229 | mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(Qt::Key_E), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED); | ||
| 230 | mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(Qt::Key_C), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED); | ||
| 231 | mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(Qt::Key_A), makeInput(Qt::RightButton), BUTTON_YAW_SPEED); | ||
| 232 | mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(Qt::Key_D), makeInput(Qt::RightButton), BUTTON_YAW_SPEED); | ||
| 233 | mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(Qt::Key_A), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED); | ||
| 234 | mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(Qt::Key_D), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED); | ||
| 235 | mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(Qt::Key_S), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED); | ||
| 236 | mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(Qt::Key_W), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED); | ||
| 237 | |||
| 238 | // Arrow keys mapping | ||
| 239 | mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(Qt::Key_Down), BUTTON_MOVE_SPEED); | ||
| 240 | mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(Qt::Key_Up), BUTTON_MOVE_SPEED); | ||
| 241 | mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(Qt::Key_Left), BUTTON_MOVE_SPEED); | ||
| 242 | mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(Qt::Key_Right), BUTTON_MOVE_SPEED); | ||
| 243 | mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(Qt::Key_PageDown), BUTTON_MOVE_SPEED); | ||
| 244 | mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(Qt::Key_PageUp), BUTTON_MOVE_SPEED); | ||
| 245 | |||
| 246 | mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(Qt::Key_Left), makeInput(Qt::RightButton), BUTTON_YAW_SPEED); | ||
| 247 | mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(Qt::Key_Right), makeInput(Qt::RightButton), BUTTON_YAW_SPEED); | ||
| 248 | mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(Qt::Key_Left), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED); | ||
| 249 | mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(Qt::Key_Right), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED); | ||
| 250 | mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(Qt::Key_Down), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED); | ||
| 251 | mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(Qt::Key_Up), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED); | ||
| 252 | |||
| 253 | // Mouse move | ||
| 254 | mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(MOUSE_AXIS_Y_NEG), makeInput(Qt::RightButton), MOUSE_PITCH_SPEED); | ||
| 255 | mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(MOUSE_AXIS_Y_POS), makeInput(Qt::RightButton), MOUSE_PITCH_SPEED); | ||
| 256 | mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(MOUSE_AXIS_X_NEG), makeInput(Qt::RightButton), MOUSE_YAW_SPEED); | ||
| 257 | mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(MOUSE_AXIS_X_POS), makeInput(Qt::RightButton), MOUSE_YAW_SPEED); | ||
| 258 | |||
| 259 | |||
| 260 | #ifdef Q_OS_MAC | ||
| 261 | // wheel event modifier on Mac collide with the touchpad scroll event | ||
| 262 | mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(TOUCH_AXIS_Y_NEG), TOUCH_PITCH_SPEED); | ||
| 263 | mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(TOUCH_AXIS_Y_POS), TOUCH_PITCH_SPEED); | ||
| 264 | mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(TOUCH_AXIS_X_NEG), TOUCH_YAW_SPEED); | ||
| 265 | mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(TOUCH_AXIS_X_POS), TOUCH_YAW_SPEED); | ||
| 266 | #else | ||
| 267 | // Touch pad yaw pitch | ||
| 268 | mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(TOUCH_AXIS_Y_NEG), TOUCH_PITCH_SPEED); | ||
| 269 | mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(TOUCH_AXIS_Y_POS), TOUCH_PITCH_SPEED); | ||
| 270 | mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(TOUCH_AXIS_X_NEG), TOUCH_YAW_SPEED); | ||
| 271 | mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(TOUCH_AXIS_X_POS), TOUCH_YAW_SPEED); | ||
| 272 | |||
| 273 | // Wheel move | ||
| 274 | mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(MOUSE_AXIS_WHEEL_Y_POS), BUTTON_BOOM_SPEED); | ||
| 275 | mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(MOUSE_AXIS_WHEEL_Y_NEG), BUTTON_BOOM_SPEED); | ||
| 276 | mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(MOUSE_AXIS_WHEEL_X_NEG), BUTTON_YAW_SPEED); | ||
| 277 | mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(MOUSE_AXIS_WHEEL_X_POS), BUTTON_YAW_SPEED); | ||
| 278 | |||
| 279 | #endif | ||
| 280 | |||
| 281 | mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(Qt::Key_Space)); | ||
| 282 | mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(Qt::Key_R)); | ||
| 283 | mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(Qt::Key_T)); | ||
| 284 | } | 210 | } |
| 285 | 211 | ||
| 212 | //void KeyboardMouseDevice::assignDefaultInputMapping(UserInputMapper& mapper) { | ||
| 213 | // const float BUTTON_MOVE_SPEED = 1.0f; | ||
| 214 | // const float BUTTON_YAW_SPEED = 0.75f; | ||
| 215 | // const float BUTTON_PITCH_SPEED = 0.5f; | ||
| 216 | // const float MOUSE_YAW_SPEED = 0.5f; | ||
| 217 | // const float MOUSE_PITCH_SPEED = 0.25f; | ||
| 218 | // const float TOUCH_YAW_SPEED = 0.5f; | ||
| 219 | // const float TOUCH_PITCH_SPEED = 0.25f; | ||
| 220 | // const float BUTTON_BOOM_SPEED = 0.1f; | ||
| 221 | // | ||
| 222 | // // AWSD keys mapping | ||
| 223 | // | ||
| 224 | // mapper.addInputChannel(controller::BOOM_IN, makeInput(Qt::Key_E), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED); | ||
| 225 | // mapper.addInputChannel(controller::BOOM_OUT, makeInput(Qt::Key_C), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED); | ||
| 226 | // mapper.addInputChannel(controller::LATERAL_LEFT, makeInput(Qt::Key_A), makeInput(Qt::RightButton), BUTTON_YAW_SPEED); | ||
| 227 | // mapper.addInputChannel(controller::LATERAL_RIGHT, makeInput(Qt::Key_D), makeInput(Qt::RightButton), BUTTON_YAW_SPEED); | ||
| 228 | // mapper.addInputChannel(controller::LATERAL_LEFT, makeInput(Qt::Key_A), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED); | ||
| 229 | // mapper.addInputChannel(controller::LATERAL_RIGHT, makeInput(Qt::Key_D), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED); | ||
| 230 | // mapper.addInputChannel(controller::PITCH_DOWN, makeInput(Qt::Key_S), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED); | ||
| 231 | // mapper.addInputChannel(controller::PITCH_UP, makeInput(Qt::Key_W), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED); | ||
| 232 | // | ||
| 233 | // // Arrow keys mapping | ||
| 234 | // mapper.addInputChannel(controller::LONGITUDINAL_BACKWARD, makeInput(Qt::Key_Down), BUTTON_MOVE_SPEED); | ||
| 235 | // mapper.addInputChannel(controller::LONGITUDINAL_FORWARD, makeInput(Qt::Key_Up), BUTTON_MOVE_SPEED); | ||
| 236 | // mapper.addInputChannel(controller::YAW_LEFT, makeInput(Qt::Key_Left), BUTTON_MOVE_SPEED); | ||
| 237 | // mapper.addInputChannel(controller::YAW_RIGHT, makeInput(Qt::Key_Right), BUTTON_MOVE_SPEED); | ||
| 238 | // mapper.addInputChannel(controller::VERTICAL_DOWN, makeInput(Qt::Key_PageDown), BUTTON_MOVE_SPEED); | ||
| 239 | // mapper.addInputChannel(controller::VERTICAL_UP, makeInput(Qt::Key_PageUp), BUTTON_MOVE_SPEED); | ||
| 240 | // | ||
| 241 | // mapper.addInputChannel(controller::LATERAL_LEFT, makeInput(Qt::Key_Left), makeInput(Qt::RightButton), BUTTON_YAW_SPEED); | ||
| 242 | // mapper.addInputChannel(controller::LATERAL_RIGHT, makeInput(Qt::Key_Right), makeInput(Qt::RightButton), BUTTON_YAW_SPEED); | ||
| 243 | // mapper.addInputChannel(controller::LATERAL_LEFT, makeInput(Qt::Key_Left), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED); | ||
| 244 | // mapper.addInputChannel(controller::LATERAL_RIGHT, makeInput(Qt::Key_Right), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED); | ||
| 245 | // mapper.addInputChannel(controller::PITCH_DOWN, makeInput(Qt::Key_Down), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED); | ||
| 246 | // mapper.addInputChannel(controller::PITCH_UP, makeInput(Qt::Key_Up), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED); | ||
| 247 | // | ||
| 248 | // // Mouse move | ||
| 249 | // mapper.addInputChannel(controller::PITCH_DOWN, makeInput(MOUSE_AXIS_Y_NEG), makeInput(Qt::RightButton), MOUSE_PITCH_SPEED); | ||
| 250 | // mapper.addInputChannel(controller::PITCH_UP, makeInput(MOUSE_AXIS_Y_POS), makeInput(Qt::RightButton), MOUSE_PITCH_SPEED); | ||
| 251 | // mapper.addInputChannel(controller::YAW_LEFT, makeInput(MOUSE_AXIS_X_NEG), makeInput(Qt::RightButton), MOUSE_YAW_SPEED); | ||
| 252 | // mapper.addInputChannel(controller::YAW_RIGHT, makeInput(MOUSE_AXIS_X_POS), makeInput(Qt::RightButton), MOUSE_YAW_SPEED); | ||
| 253 | // | ||
| 254 | // | ||
| 255 | //#ifdef Q_OS_MAC | ||
| 256 | // // wheel event modifier on Mac collide with the touchpad scroll event | ||
| 257 | // mapper.addInputChannel(controller::PITCH_DOWN, makeInput(TOUCH_AXIS_Y_NEG), TOUCH_PITCH_SPEED); | ||
| 258 | // mapper.addInputChannel(controller::PITCH_UP, makeInput(TOUCH_AXIS_Y_POS), TOUCH_PITCH_SPEED); | ||
| 259 | // mapper.addInputChannel(controller::YAW_LEFT, makeInput(TOUCH_AXIS_X_NEG), TOUCH_YAW_SPEED); | ||
| 260 | // mapper.addInputChannel(controller::YAW_RIGHT, makeInput(TOUCH_AXIS_X_POS), TOUCH_YAW_SPEED); | ||
| 261 | //#else | ||
| 262 | // // Touch pad yaw pitch | ||
| 263 | // mapper.addInputChannel(controller::PITCH_DOWN, makeInput(TOUCH_AXIS_Y_NEG), TOUCH_PITCH_SPEED); | ||
| 264 | // mapper.addInputChannel(controller::PITCH_UP, makeInput(TOUCH_AXIS_Y_POS), TOUCH_PITCH_SPEED); | ||
| 265 | // mapper.addInputChannel(controller::YAW_LEFT, makeInput(TOUCH_AXIS_X_NEG), TOUCH_YAW_SPEED); | ||
| 266 | // mapper.addInputChannel(controller::YAW_RIGHT, makeInput(TOUCH_AXIS_X_POS), TOUCH_YAW_SPEED); | ||
| 267 | // | ||
| 268 | // // Wheel move | ||
| 269 | // mapper.addInputChannel(controller::BOOM_IN, makeInput(MOUSE_AXIS_WHEEL_Y_POS), BUTTON_BOOM_SPEED); | ||
| 270 | // mapper.addInputChannel(controller::BOOM_OUT, makeInput(MOUSE_AXIS_WHEEL_Y_NEG), BUTTON_BOOM_SPEED); | ||
| 271 | // mapper.addInputChannel(controller::LATERAL_LEFT, makeInput(MOUSE_AXIS_WHEEL_X_NEG), BUTTON_YAW_SPEED); | ||
| 272 | // mapper.addInputChannel(controller::LATERAL_RIGHT, makeInput(MOUSE_AXIS_WHEEL_X_POS), BUTTON_YAW_SPEED); | ||
| 273 | // | ||
| 274 | //#endif | ||
| 275 | // | ||
| 276 | // mapper.addInputChannel(controller::SHIFT, makeInput(Qt::Key_Space)); | ||
| 277 | // mapper.addInputChannel(controller::ACTION1, makeInput(Qt::Key_R)); | ||
| 278 | // mapper.addInputChannel(controller::ACTION2, makeInput(Qt::Key_T)); | ||
| 279 | //} | ||
| 280 | |||
| @@ -12,12 +12,19 @@ · 11 unmodified lines | |||
| 12 | #ifndef hifi_KeyboardMouseDevice_h | 12 | #ifndef hifi_KeyboardMouseDevice_h |
| 13 | #define hifi_KeyboardMouseDevice_h | 13 | #define hifi_KeyboardMouseDevice_h |
| 14 | 14 | ||
| 15 | #include <QTouchEvent> | ||
| 16 | #include <chrono> | 15 | #include <chrono> |
| 17 | #include "InputDevice.h" | 16 | |
| 17 | #include <QtCore/QPoint> | ||
| 18 | |||
| 19 | #include <controllers/InputDevice.h> | ||
| 18 | #include "InputPlugin.h" | 20 | #include "InputPlugin.h" |
| 19 | 21 | ||
| 20 | class KeyboardMouseDevice : public InputPlugin, public InputDevice { | 22 | class QTouchEvent; |
| 23 | class QKeyEvent; | ||
| 24 | class QMouseEvent; | ||
| 25 | class QWheelEvent; | ||
| 26 | |||
| 27 | class KeyboardMouseDevice : public InputPlugin, public controller::InputDevice { | ||
| 21 | Q_OBJECT | 28 | Q_OBJECT |
| 22 | public: | 29 | public: |
| 23 | enum KeyboardChannel { | 30 | enum KeyboardChannel { |
| @@ -61,15 +68,12 @@ public: · 37 unmodified lines | |||
| 61 | virtual bool isJointController() const override { return false; } | 68 | virtual bool isJointController() const override { return false; } |
| 62 | const QString& getName() const override { return NAME; } | 69 | const QString& getName() const override { return NAME; } |
| 63 | 70 | ||
| 64 | virtual void activate() override {}; | ||
| 65 | virtual void deactivate() override {}; | ||
| 66 | |||
| 67 | virtual void pluginFocusOutEvent() override { focusOutEvent(); } | 71 | virtual void pluginFocusOutEvent() override { focusOutEvent(); } |
| 68 | virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); } | 72 | virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); } |
| 69 | 73 | ||
| 70 | // Device functions | 74 | // Device functions |
| 71 | virtual void registerToUserInputMapper(UserInputMapper& mapper) override; | 75 | virtual controller::Input::NamedVector getAvailableInputs() const override; |
| 72 | virtual void assignDefaultInputMapping(UserInputMapper& mapper) override; | 76 | virtual QString getDefaultMappingConfig() const override; |
| 73 | virtual void update(float deltaTime, bool jointsCaptured) override; | 77 | virtual void update(float deltaTime, bool jointsCaptured) override; |
| 74 | virtual void focusOutEvent() override; | 78 | virtual void focusOutEvent() override; |
| 75 | 79 | ||
| @@ -87,11 +91,11 @@ public: · 11 unmodified lines | |||
| 87 | void wheelEvent(QWheelEvent* event); | 91 | void wheelEvent(QWheelEvent* event); |
| 88 | 92 | ||
| 89 | // Let's make it easy for Qt because we assume we love Qt forever | 93 | // Let's make it easy for Qt because we assume we love Qt forever |
| 90 | UserInputMapper::Input makeInput(Qt::Key code); | 94 | controller::Input makeInput(Qt::Key code) const; |
| 91 | UserInputMapper::Input makeInput(Qt::MouseButton code); | 95 | controller::Input makeInput(Qt::MouseButton code) const; |
| 92 | UserInputMapper::Input makeInput(KeyboardMouseDevice::MouseAxisChannel axis); | 96 | controller::Input makeInput(MouseAxisChannel axis) const; |
| 93 | UserInputMapper::Input makeInput(KeyboardMouseDevice::TouchAxisChannel axis); | 97 | controller::Input makeInput(TouchAxisChannel axis) const; |
| 94 | UserInputMapper::Input makeInput(KeyboardMouseDevice::TouchButtonChannel button); | 98 | controller::Input makeInput(TouchButtonChannel button) const; |
| 95 | 99 | ||
| 96 | static const QString NAME; | 100 | static const QString NAME; |
| 97 | 101 | ||
| @@ -100,7 +104,6 @@ protected: · 2 unmodified lines | |||
| 100 | glm::vec2 _lastTouch; | 104 | glm::vec2 _lastTouch; |
| 101 | bool _isTouching = false; | 105 | bool _isTouching = false; |
| 102 | 106 | ||
| 103 | glm::vec2 evalAverageTouchPoints(const QList<QTouchEvent::TouchPoint>& points) const; | ||
| 104 | std::chrono::high_resolution_clock _clock; | 107 | std::chrono::high_resolution_clock _clock; |
| 105 | std::chrono::high_resolution_clock::time_point _lastTouchTime; | 108 | std::chrono::high_resolution_clock::time_point _lastTouchTime; |
| 106 | }; | 109 | }; |
| @@ -14,6 +14,7 @@ · 13 unmodified lines | |||
| 14 | #include <HFActionEvent.h> | 14 | #include <HFActionEvent.h> |
| 15 | #include <HFBackEvent.h> | 15 | #include <HFBackEvent.h> |
| 16 | #include <PerfStat.h> | 16 | #include <PerfStat.h> |
| 17 | #include <controllers/UserInputMapper.h> | ||
| 17 | 18 | ||
| 18 | #include "SDL2Manager.h" | 19 | #include "SDL2Manager.h" |
| 19 | 20 | ||
| @@ -47,12 +48,12 @@ void SDL2Manager::init() { · 27 unmodified lines | |||
| 47 | if (controller) { | 48 | if (controller) { |
| 48 | SDL_JoystickID id = getInstanceId(controller); | 49 | SDL_JoystickID id = getInstanceId(controller); |
| 49 | if (!_openJoysticks.contains(id)) { | 50 | if (!_openJoysticks.contains(id)) { |
| 50 | Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller); | 51 | //Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller); |
| 52 | Joystick::Pointer joystick = std::make_shared<Joystick>(id, controller); | ||
| 51 | _openJoysticks[id] = joystick; | 53 | _openJoysticks[id] = joystick; |
| 52 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); | 54 | auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); |
| 53 | joystick->registerToUserInputMapper(*userInputMapper); | 55 | userInputMapper->registerDevice(joystick); |
| 54 | joystick->assignDefaultInputMapping(*userInputMapper); | 56 | emit joystickAdded(joystick.get()); |
| 55 | emit joystickAdded(joystick); | ||
| 56 | } | 57 | } |
| 57 | } | 58 | } |
| 58 | } | 59 | } |
| @@ -67,12 +68,33 @@ void SDL2Manager::init() { · 8 unmodified lines | |||
| 67 | 68 | ||
| 68 | void SDL2Manager::deinit() { | 69 | void SDL2Manager::deinit() { |
| 69 | #ifdef HAVE_SDL2 | 70 | #ifdef HAVE_SDL2 |
| 70 | qDeleteAll(_openJoysticks); | 71 | _openJoysticks.clear(); |
| 71 | 72 | ||
| 72 | SDL_Quit(); | 73 | SDL_Quit(); |
| 73 | #endif | 74 | #endif |
| 74 | } | 75 | } |
| 75 | 76 | ||
| 77 | void SDL2Manager::activate() { | ||
| 78 | #ifdef HAVE_SDL2 | ||
| 79 | auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); | ||
| 80 | for (auto joystick : _openJoysticks) { | ||
| 81 | userInputMapper->registerDevice(joystick); | ||
| 82 | emit joystickAdded(joystick.get()); | ||
| 83 | } | ||
| 84 | #endif | ||
| 85 | } | ||
| 86 | |||
| 87 | void SDL2Manager::deactivate() { | ||
| 88 | #ifdef HAVE_SDL2 | ||
| 89 | auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); | ||
| 90 | for (auto joystick : _openJoysticks) { | ||
| 91 | userInputMapper->removeDevice(joystick->getDeviceID()); | ||
| 92 | emit joystickRemoved(joystick.get()); | ||
| 93 | } | ||
| 94 | #endif | ||
| 95 | } | ||
| 96 | |||
| 97 | |||
| 76 | bool SDL2Manager::isSupported() const { | 98 | bool SDL2Manager::isSupported() const { |
| 77 | #ifdef HAVE_SDL2 | 99 | #ifdef HAVE_SDL2 |
| 78 | return true; | 100 | return true; |
| @@ -92,7 +114,7 @@ void SDL2Manager::pluginFocusOutEvent() { · 13 unmodified lines | |||
| 92 | void SDL2Manager::pluginUpdate(float deltaTime, bool jointsCaptured) { | 114 | void SDL2Manager::pluginUpdate(float deltaTime, bool jointsCaptured) { |
| 93 | #ifdef HAVE_SDL2 | 115 | #ifdef HAVE_SDL2 |
| 94 | if (_isInitialized) { | 116 | if (_isInitialized) { |
| 95 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); | 117 | auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); |
| 96 | for (auto joystick : _openJoysticks) { | 118 | for (auto joystick : _openJoysticks) { |
| 97 | joystick->update(deltaTime, jointsCaptured); | 119 | joystick->update(deltaTime, jointsCaptured); |
| 98 | } | 120 | } |
| @@ -102,12 +124,12 @@ void SDL2Manager::pluginUpdate(float deltaTime, bool jointsCaptured) { · 3 unmodified lines | |||
| 102 | SDL_Event event; | 124 | SDL_Event event; |
| 103 | while (SDL_PollEvent(&event)) { | 125 | while (SDL_PollEvent(&event)) { |
| 104 | if (event.type == SDL_CONTROLLERAXISMOTION) { | 126 | if (event.type == SDL_CONTROLLERAXISMOTION) { |
| 105 | Joystick* joystick = _openJoysticks[event.caxis.which]; | 127 | Joystick::Pointer joystick = _openJoysticks[event.caxis.which]; |
| 106 | if (joystick) { | 128 | if (joystick) { |
| 107 | joystick->handleAxisEvent(event.caxis); | 129 | joystick->handleAxisEvent(event.caxis); |
| 108 | } | 130 | } |
| 109 | } else if (event.type == SDL_CONTROLLERBUTTONDOWN || event.type == SDL_CONTROLLERBUTTONUP) { | 131 | } else if (event.type == SDL_CONTROLLERBUTTONDOWN || event.type == SDL_CONTROLLERBUTTONUP) { |
| 110 | Joystick* joystick = _openJoysticks[event.cbutton.which]; | 132 | Joystick::Pointer joystick = _openJoysticks[event.cbutton.which]; |
| 111 | if (joystick) { | 133 | if (joystick) { |
| 112 | joystick->handleButtonEvent(event.cbutton); | 134 | joystick->handleButtonEvent(event.cbutton); |
| 113 | } | 135 | } |
| @@ -124,20 +146,21 @@ void SDL2Manager::pluginUpdate(float deltaTime, bool jointsCaptured) { · 10 unmodified lines | |||
| 124 | 146 | ||
| 125 | } else if (event.type == SDL_CONTROLLERDEVICEADDED) { | 147 | } else if (event.type == SDL_CONTROLLERDEVICEADDED) { |
| 126 | SDL_GameController* controller = SDL_GameControllerOpen(event.cdevice.which); | 148 | SDL_GameController* controller = SDL_GameControllerOpen(event.cdevice.which); |
| 127 | |||
| 128 | SDL_JoystickID id = getInstanceId(controller); | 149 | SDL_JoystickID id = getInstanceId(controller); |
| 129 | if (!_openJoysticks.contains(id)) { | 150 | if (!_openJoysticks.contains(id)) { |
| 130 | Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller); | 151 | // Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller); |
| 152 | Joystick::Pointer joystick = std::make_shared<Joystick>(id, controller); | ||
| 131 | _openJoysticks[id] = joystick; | 153 | _openJoysticks[id] = joystick; |
| 132 | joystick->registerToUserInputMapper(*userInputMapper); | 154 | userInputMapper->registerDevice(joystick); |
| 133 | joystick->assignDefaultInputMapping(*userInputMapper); | 155 | emit joystickAdded(joystick.get()); |
| 134 | emit joystickAdded(joystick); | ||
| 135 | } | 156 | } |
| 136 | } else if (event.type == SDL_CONTROLLERDEVICEREMOVED) { | 157 | } else if (event.type == SDL_CONTROLLERDEVICEREMOVED) { |
| 137 | Joystick* joystick = _openJoysticks[event.cdevice.which]; | 158 | if (_openJoysticks.contains(event.cdevice.which)) { |
| 138 | _openJoysticks.remove(event.cdevice.which); | 159 | Joystick::Pointer joystick = _openJoysticks[event.cdevice.which]; |
| 139 | userInputMapper->removeDevice(joystick->getDeviceID()); | 160 | _openJoysticks.remove(event.cdevice.which); |
| 140 | emit joystickRemoved(joystick); | 161 | userInputMapper->removeDevice(joystick->getDeviceID()); |
| 162 | emit joystickRemoved(joystick.get()); | ||
| 163 | } | ||
| 141 | } | 164 | } |
| 142 | } | 165 | } |
| 143 | } | 166 | } |
| @@ -16,9 +16,9 @@ · 15 unmodified lines | |||
| 16 | #include <SDL.h> | 16 | #include <SDL.h> |
| 17 | #endif | 17 | #endif |
| 18 | 18 | ||
| 19 | #include "InputPlugin.h" | 19 | #include <controllers/UserInputMapper.h> |
| 20 | #include "UserInputMapper.h" | ||
| 21 | 20 | ||
| 21 | #include "InputPlugin.h" | ||
| 22 | #include "Joystick.h" | 22 | #include "Joystick.h" |
| 23 | 23 | ||
| 24 | class SDL2Manager : public InputPlugin { | 24 | class SDL2Manager : public InputPlugin { |
| @@ -34,9 +34,12 @@ public: · 9 unmodified lines | |||
| 34 | 34 | ||
| 35 | virtual void init() override; | 35 | virtual void init() override; |
| 36 | virtual void deinit() override; | 36 | virtual void deinit() override; |
| 37 | virtual void activate() override {}; | 37 | |
| 38 | virtual void deactivate() override {}; | 38 | /// Called when a plugin is being activated for use. May be called multiple times. |
| 39 | 39 | virtual void activate() override; | |
| 40 | /// Called when a plugin is no longer being used. May be called multiple times. | ||
| 41 | virtual void deactivate() override; | ||
| 42 | |||
| 40 | virtual void pluginFocusOutEvent() override; | 43 | virtual void pluginFocusOutEvent() override; |
| 41 | virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override; | 44 | virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override; |
| 42 | 45 | ||
| @@ -78,7 +81,7 @@ private: · 35 unmodified lines | |||
| 78 | int buttonPressed() const { return SDL_PRESSED; } | 81 | int buttonPressed() const { return SDL_PRESSED; } |
| 79 | int buttonRelease() const { return SDL_RELEASED; } | 82 | int buttonRelease() const { return SDL_RELEASED; } |
| 80 | 83 | ||
| 81 | QMap<SDL_JoystickID, Joystick*> _openJoysticks; | 84 | QMap<SDL_JoystickID, Joystick::Pointer> _openJoysticks; |
| 82 | #endif | 85 | #endif |
| 83 | bool _isInitialized; | 86 | bool _isInitialized; |
| 84 | static const QString NAME; | 87 | static const QString NAME; |
| @@ -18,10 +18,13 @@ · 17 unmodified lines | |||
| 18 | #include <PerfStat.h> | 18 | #include <PerfStat.h> |
| 19 | #include <SettingHandle.h> | 19 | #include <SettingHandle.h> |
| 20 | #include <plugins/PluginContainer.h> | 20 | #include <plugins/PluginContainer.h> |
| 21 | #include <PathUtils.h> | ||
| 22 | #include <NumericalConstants.h> | ||
| 23 | #include <UserActivityLogger.h> | ||
| 24 | #include <controllers/UserInputMapper.h> | ||
| 21 | 25 | ||
| 22 | #include "NumericalConstants.h" | ||
| 23 | #include "SixenseManager.h" | 26 | #include "SixenseManager.h" |
| 24 | #include "UserActivityLogger.h" | 27 | |
| 25 | 28 | ||
| 26 | #ifdef HAVE_SIXENSE | 29 | #ifdef HAVE_SIXENSE |
| 27 | #include "sixense.h" | 30 | #include "sixense.h" |
| @@ -32,10 +35,6 @@ · 4 unmodified lines | |||
| 32 | Q_DECLARE_LOGGING_CATEGORY(inputplugins) | 35 | Q_DECLARE_LOGGING_CATEGORY(inputplugins) |
| 33 | Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins") | 36 | Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins") |
| 34 | 37 | ||
| 35 | // These bits aren't used for buttons, so they can be used as masks: | ||
| 36 | const unsigned int LEFT_MASK = 0; | ||
| 37 | const unsigned int RIGHT_MASK = 1U << 1; | ||
| 38 | |||
| 39 | #ifdef HAVE_SIXENSE | 38 | #ifdef HAVE_SIXENSE |
| 40 | 39 | ||
| 41 | const int CALIBRATION_STATE_IDLE = 0; | 40 | const int CALIBRATION_STATE_IDLE = 0; |
| @@ -65,15 +64,12 @@ const QString MENU_PATH = MENU_PARENT + ">" + MENU_NAME; · 23 unmodified lines | |||
| 65 | const QString TOGGLE_SMOOTH = "Smooth Sixense Movement"; | 64 | const QString TOGGLE_SMOOTH = "Smooth Sixense Movement"; |
| 66 | const float DEFAULT_REACH_LENGTH = 1.5f; | 65 | const float DEFAULT_REACH_LENGTH = 1.5f; |
| 67 | 66 | ||
| 68 | 67 | static std::shared_ptr<SixenseManager> instance; | |
| 69 | SixenseManager::SixenseManager() : | 68 | SixenseManager::SixenseManager() : |
| 70 | InputDevice("Hydra"), | 69 | InputDevice("Hydra"), |
| 71 | _reachLength(DEFAULT_REACH_LENGTH), | 70 | _reachLength(DEFAULT_REACH_LENGTH) |
| 72 | #ifdef __APPLE__ | ||
| 73 | _sixenseLibrary(nullptr), | ||
| 74 | #endif | ||
| 75 | _hydrasConnected(false) | ||
| 76 | { | 71 | { |
| 72 | instance = std::shared_ptr<SixenseManager>(this); | ||
| 77 | } | 73 | } |
| 78 | 74 | ||
| 79 | bool SixenseManager::isSupported() const { | 75 | bool SixenseManager::isSupported() const { |
| @@ -85,6 +81,7 @@ bool SixenseManager::isSupported() const { · 5 unmodified lines | |||
| 85 | } | 81 | } |
| 86 | 82 | ||
| 87 | void SixenseManager::activate() { | 83 | void SixenseManager::activate() { |
| 84 | InputPlugin::activate(); | ||
| 88 | #ifdef HAVE_SIXENSE | 85 | #ifdef HAVE_SIXENSE |
| 89 | _calibrationState = CALIBRATION_STATE_IDLE; | 86 | _calibrationState = CALIBRATION_STATE_IDLE; |
| 90 | _avatarPosition = DEFAULT_AVATAR_POSITION; | 87 | _avatarPosition = DEFAULT_AVATAR_POSITION; |
| @@ -94,6 +91,9 @@ void SixenseManager::activate() { · 3 unmodified lines | |||
| 94 | [this] (bool clicked) { this->setSixenseFilter(clicked); }, | 91 | [this] (bool clicked) { this->setSixenseFilter(clicked); }, |
| 95 | true, true); | 92 | true, true); |
| 96 | 93 | ||
| 94 | auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); | ||
| 95 | userInputMapper->registerDevice(instance); | ||
| 96 | |||
| 97 | #ifdef __APPLE__ | 97 | #ifdef __APPLE__ |
| 98 | 98 | ||
| 99 | if (!_sixenseLibrary) { | 99 | if (!_sixenseLibrary) { |
| @@ -125,11 +125,19 @@ void SixenseManager::activate() { · 25 unmodified lines | |||
| 125 | } | 125 | } |
| 126 | 126 | ||
| 127 | void SixenseManager::deactivate() { | 127 | void SixenseManager::deactivate() { |
| 128 | InputPlugin::deactivate(); | ||
| 129 | |||
| 128 | #ifdef HAVE_SIXENSE | 130 | #ifdef HAVE_SIXENSE |
| 129 | CONTAINER->removeMenuItem(MENU_NAME, TOGGLE_SMOOTH); | 131 | CONTAINER->removeMenuItem(MENU_NAME, TOGGLE_SMOOTH); |
| 130 | CONTAINER->removeMenu(MENU_PATH); | 132 | CONTAINER->removeMenu(MENU_PATH); |
| 131 | 133 | ||
| 132 | _poseStateMap.clear(); | 134 | _poseStateMap.clear(); |
| 135 | _collectedSamples.clear(); | ||
| 136 | |||
| 137 | if (_deviceID != controller::Input::INVALID_DEVICE) { | ||
| 138 | auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); | ||
| 139 | userInputMapper->removeDevice(_deviceID); | ||
| 140 | } | ||
| 133 | 141 | ||
| 134 | #ifdef __APPLE__ | 142 | #ifdef __APPLE__ |
| 135 | SixenseBaseFunction sixenseExit = (SixenseBaseFunction)_sixenseLibrary->resolve("sixenseExit"); | 143 | SixenseBaseFunction sixenseExit = (SixenseBaseFunction)_sixenseLibrary->resolve("sixenseExit"); |
| @@ -155,6 +163,12 @@ void SixenseManager::setSixenseFilter(bool filter) { · 19 unmodified lines | |||
| 155 | } | 163 | } |
| 156 | 164 | ||
| 157 | void SixenseManager::update(float deltaTime, bool jointsCaptured) { | 165 | void SixenseManager::update(float deltaTime, bool jointsCaptured) { |
| 166 | // FIXME - Some of the code in update() will crash if you haven't actually activated the | ||
| 167 | // plugin. But we want register with the UserInputMapper if we don't call this. | ||
| 168 | // We need to clean this up. | ||
| 169 | //if (!_activated) { | ||
| 170 | // return; | ||
| 171 | //} | ||
| 158 | #ifdef HAVE_SIXENSE | 172 | #ifdef HAVE_SIXENSE |
| 159 | _buttonPressedMap.clear(); | 173 | _buttonPressedMap.clear(); |
| 160 | 174 | ||
| @@ -163,33 +177,34 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) { · 2 unmodified lines | |||
| 163 | (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetNumActiveControllers"); | 177 | (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetNumActiveControllers"); |
| 164 | #endif | 178 | #endif |
| 165 | 179 | ||
| 166 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); | 180 | auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); |
| 167 | 181 | ||
| 182 | static const float MAX_DISCONNECTED_TIME = 2.0f; | ||
| 183 | static bool disconnected { false }; | ||
| 184 | static float disconnectedInterval { 0.0f }; | ||
| 168 | if (sixenseGetNumActiveControllers() == 0) { | 185 | if (sixenseGetNumActiveControllers() == 0) { |
| 169 | if (_hydrasConnected) { | 186 | if (!disconnected) { |
| 170 | qCDebug(inputplugins) << "hydra disconnected" << _badDataCount; | 187 | disconnectedInterval += deltaTime; |
| 171 | if (_badDataCount++ < _allowedBadDataCount) { // gotta get some no-active in a row before we shut things down | ||
| 172 | return; | ||
| 173 | } | ||
| 174 | } | 188 | } |
| 175 | _hydrasConnected = false; | 189 | if (disconnectedInterval > MAX_DISCONNECTED_TIME) { |
| 176 | if (_deviceID != 0) { | 190 | disconnected = true; |
| 177 | userInputMapper->removeDevice(_deviceID); | 191 | _axisStateMap.clear(); |
| 178 | _deviceID = 0; | 192 | _buttonPressedMap.clear(); |
| 179 | _poseStateMap.clear(); | 193 | _poseStateMap.clear(); |
| 194 | _collectedSamples.clear(); | ||
| 180 | } | 195 | } |
| 181 | return; | 196 | return; |
| 182 | } | 197 | } |
| 183 | 198 | ||
| 184 | PerformanceTimer perfTimer("sixense"); | 199 | if (disconnected) { |
| 185 | if (!_hydrasConnected) { | 200 | disconnected = 0; |
| 186 | _hydrasConnected = true; | 201 | disconnectedInterval = 0.0f; |
| 187 | _badDataCount = 0; | ||
| 188 | registerToUserInputMapper(*userInputMapper); | ||
| 189 | assignDefaultInputMapping(*userInputMapper); | ||
| 190 | UserActivityLogger::getInstance().connectedDevice("spatial_controller", "hydra"); | ||
| 191 | } | 202 | } |
| 192 | 203 | ||
| 204 | PerformanceTimer perfTimer("sixense"); | ||
| 205 | // FIXME send this message once when we've positively identified hydra hardware | ||
| 206 | //UserActivityLogger::getInstance().connectedDevice("spatial_controller", "hydra"); | ||
| 207 | |||
| 193 | #ifdef __APPLE__ | 208 | #ifdef __APPLE__ |
| 194 | SixenseBaseFunction sixenseGetMaxControllers = | 209 | SixenseBaseFunction sixenseGetMaxControllers = |
| 195 | (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetMaxControllers"); | 210 | (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetMaxControllers"); |
| @@ -220,28 +235,31 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) { · 24 unmodified lines | |||
| 220 | // NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters. | 235 | // NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters. |
| 221 | glm::vec3 position(data->pos[0], data->pos[1], data->pos[2]); | 236 | glm::vec3 position(data->pos[0], data->pos[1], data->pos[2]); |
| 222 | position *= METERS_PER_MILLIMETER; | 237 | position *= METERS_PER_MILLIMETER; |
| 223 | 238 | bool left = i == 0; | |
| 239 | using namespace controller; | ||
| 224 | // Check to see if this hand/controller is on the base | 240 | // Check to see if this hand/controller is on the base |
| 225 | const float CONTROLLER_AT_BASE_DISTANCE = 0.075f; | 241 | const float CONTROLLER_AT_BASE_DISTANCE = 0.075f; |
| 226 | if (glm::length(position) >= CONTROLLER_AT_BASE_DISTANCE) { | 242 | if (glm::length(position) >= CONTROLLER_AT_BASE_DISTANCE) { |
| 227 | handleButtonEvent(data->buttons, numActiveControllers - 1); | 243 | handleButtonEvent(data->buttons, left); |
| 228 | handleAxisEvent(data->joystick_x, data->joystick_y, data->trigger, numActiveControllers - 1); | 244 | _axisStateMap[left ? LX : RX] = data->joystick_x; |
| 245 | _axisStateMap[left ? LY : RY] = data->joystick_y; | ||
| 246 | _axisStateMap[left ? LT : RT] = data->trigger; | ||
| 229 | 247 | ||
| 230 | if (!jointsCaptured) { | 248 | if (!jointsCaptured) { |
| 231 | // Rotation of Palm | 249 | // Rotation of Palm |
| 232 | glm::quat rotation(data->rot_quat[3], data->rot_quat[0], data->rot_quat[1], data->rot_quat[2]); | 250 | glm::quat rotation(data->rot_quat[3], data->rot_quat[0], data->rot_quat[1], data->rot_quat[2]); |
| 233 | handlePoseEvent(position, rotation, numActiveControllers - 1); | 251 | handlePoseEvent(deltaTime, position, rotation, left); |
| 252 | |||
| 234 | } else { | 253 | } else { |
| 235 | _poseStateMap.clear(); | 254 | _poseStateMap.clear(); |
| 255 | _collectedSamples.clear(); | ||
| 236 | } | 256 | } |
| 237 | } else { | 257 | } else { |
| 238 | _poseStateMap[(numActiveControllers - 1) == 0 ? LEFT_HAND : RIGHT_HAND] = UserInputMapper::PoseValue(); | 258 | auto hand = left ? controller::StandardPoseChannel::LEFT_HAND : controller::StandardPoseChannel::RIGHT_HAND; |
| 259 | _poseStateMap[hand] = controller::Pose(); | ||
| 260 | _collectedSamples[hand].first.clear(); | ||
| 261 | _collectedSamples[hand].second.clear(); | ||
| 239 | } | 262 | } |
| 240 | |||
| 241 | // // Read controller buttons and joystick into the hand | ||
| 242 | // palm->setControllerButtons(data->buttons); | ||
| 243 | // palm->setTrigger(data->trigger); | ||
| 244 | // palm->setJoystick(data->joystick_x, data->joystick_y); | ||
| 245 | } | 263 | } |
| 246 | 264 | ||
| 247 | if (numActiveControllers == 2) { | 265 | if (numActiveControllers == 2) { |
| @@ -361,40 +379,38 @@ void SixenseManager::focusOutEvent() { · 113 unmodified lines | |||
| 361 | _buttonPressedMap.clear(); | 379 | _buttonPressedMap.clear(); |
| 362 | }; | 380 | }; |
| 363 | 381 | ||
| 364 | void SixenseManager::handleAxisEvent(float stickX, float stickY, float trigger, int index) { | 382 | void SixenseManager::handleAxisEvent(float stickX, float stickY, float trigger, bool left) { |
| 365 | _axisStateMap[makeInput(AXIS_Y_POS, index).getChannel()] = (stickY > 0.0f) ? stickY : 0.0f; | ||
| 366 | _axisStateMap[makeInput(AXIS_Y_NEG, index).getChannel()] = (stickY < 0.0f) ? -stickY : 0.0f; | ||
| 367 | _axisStateMap[makeInput(AXIS_X_POS, index).getChannel()] = (stickX > 0.0f) ? stickX : 0.0f; | ||
| 368 | _axisStateMap[makeInput(AXIS_X_NEG, index).getChannel()] = (stickX < 0.0f) ? -stickX : 0.0f; | ||
| 369 | _axisStateMap[makeInput(BACK_TRIGGER, index).getChannel()] = trigger; | ||
| 370 | } | 383 | } |
| 371 | 384 | ||
| 372 | void SixenseManager::handleButtonEvent(unsigned int buttons, int index) { | 385 | void SixenseManager::handleButtonEvent(unsigned int buttons, bool left) { |
| 386 | using namespace controller; | ||
| 373 | if (buttons & BUTTON_0) { | 387 | if (buttons & BUTTON_0) { |
| 374 | _buttonPressedMap.insert(makeInput(BUTTON_0, index).getChannel()); | 388 | _buttonPressedMap.insert(left ? BACK : START); |
| 375 | } | 389 | } |
| 376 | if (buttons & BUTTON_1) { | 390 | if (buttons & BUTTON_1) { |
| 377 | _buttonPressedMap.insert(makeInput(BUTTON_1, index).getChannel()); | 391 | _buttonPressedMap.insert(left ? DL : X); |
| 378 | } | 392 | } |
| 379 | if (buttons & BUTTON_2) { | 393 | if (buttons & BUTTON_2) { |
| 380 | _buttonPressedMap.insert(makeInput(BUTTON_2, index).getChannel()); | 394 | _buttonPressedMap.insert(left ? DD : A); |
| 381 | } | 395 | } |
| 382 | if (buttons & BUTTON_3) { | 396 | if (buttons & BUTTON_3) { |
| 383 | _buttonPressedMap.insert(makeInput(BUTTON_3, index).getChannel()); | 397 | _buttonPressedMap.insert(left ? DR : B); |
| 384 | } | 398 | } |
| 385 | if (buttons & BUTTON_4) { | 399 | if (buttons & BUTTON_4) { |
| 386 | _buttonPressedMap.insert(makeInput(BUTTON_4, index).getChannel()); | 400 | _buttonPressedMap.insert(left ? DU : Y); |
| 387 | } | 401 | } |
| 388 | if (buttons & BUTTON_FWD) { | 402 | if (buttons & BUTTON_FWD) { |
| 389 | _buttonPressedMap.insert(makeInput(BUTTON_FWD, index).getChannel()); | 403 | _buttonPressedMap.insert(left ? LB : RB); |
| 390 | } | 404 | } |
| 391 | if (buttons & BUTTON_TRIGGER) { | 405 | if (buttons & BUTTON_TRIGGER) { |
| 392 | _buttonPressedMap.insert(makeInput(BUTTON_TRIGGER, index).getChannel()); | 406 | _buttonPressedMap.insert(left ? LS : RS); |
| 393 | } | 407 | } |
| 394 | } | 408 | } |
| 395 | 409 | ||
| 396 | void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int index) { | 410 | void SixenseManager::handlePoseEvent(float deltaTime, glm::vec3 position, glm::quat rotation, bool left) { |
| 397 | #ifdef HAVE_SIXENSE | 411 | #ifdef HAVE_SIXENSE |
| 412 | auto hand = left ? controller::StandardPoseChannel::LEFT_HAND : controller::StandardPoseChannel::RIGHT_HAND; | ||
| 413 | |||
| 398 | // From ABOVE the sixense coordinate frame looks like this: | 414 | // From ABOVE the sixense coordinate frame looks like this: |
| 399 | // | 415 | // |
| 400 | // | | 416 | // | |
| @@ -409,6 +425,7 @@ void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int · 8 unmodified lines | |||
| 409 | // | | 425 | // | |
| 410 | // | | 426 | // | |
| 411 | // z | 427 | // z |
| 428 | auto prevPose = _poseStateMap[hand]; | ||
| 412 | 429 | ||
| 413 | // Transform the measured position into body frame. | 430 | // Transform the measured position into body frame. |
| 414 | position = _avatarRotation * (position + _avatarPosition); | 431 | position = _avatarRotation * (position + _avatarPosition); |
| @@ -437,7 +454,7 @@ void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int · 22 unmodified lines | |||
| 437 | // In addition to Qsh each hand has pre-offset introduced by the shape of the sixense controllers | 454 | // In addition to Qsh each hand has pre-offset introduced by the shape of the sixense controllers |
| 438 | // and how they fit into the hand in their relaxed state. This offset is a quarter turn about | 455 | // and how they fit into the hand in their relaxed state. This offset is a quarter turn about |
| 439 | // the sixense's z-axis, with its direction different for the two hands: | 456 | // the sixense's z-axis, with its direction different for the two hands: |
| 440 | float sign = (index == 0) ? 1.0f : -1.0f; | 457 | float sign = left ? 1.0f : -1.0f; |
| 441 | const glm::quat preOffset = glm::angleAxis(sign * PI / 2.0f, Vectors::UNIT_Z); | 458 | const glm::quat preOffset = glm::angleAxis(sign * PI / 2.0f, Vectors::UNIT_Z); |
| 442 | 459 | ||
| 443 | // Finally, there is a post-offset (same for both hands) to get the hand's rest orientation | 460 | // Finally, there is a post-offset (same for both hands) to get the hand's rest orientation |
| @@ -452,104 +469,128 @@ void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int · 8 unmodified lines | |||
| 452 | // TODO: find a shortcut with fewer rotations. | 469 | // TODO: find a shortcut with fewer rotations. |
| 453 | rotation = _avatarRotation * postOffset * glm::inverse(sixenseToHand) * rotation * preOffset * sixenseToHand; | 470 | rotation = _avatarRotation * postOffset * glm::inverse(sixenseToHand) * rotation * preOffset * sixenseToHand; |
| 454 | 471 | ||
| 455 | _poseStateMap[makeInput(JointChannel(index)).getChannel()] = UserInputMapper::PoseValue(position, rotation); | 472 | glm::vec3 velocity(0.0f); |
| 456 | #endif // HAVE_SIXENSE | 473 | glm::quat angularVelocity; |
| 457 | } | ||
| 458 | |||
| 459 | void SixenseManager::registerToUserInputMapper(UserInputMapper& mapper) { | ||
| 460 | // Grab the current free device ID | ||
| 461 | _deviceID = mapper.getFreeDeviceID(); | ||
| 462 | |||
| 463 | auto proxy = std::make_shared<UserInputMapper::DeviceProxy>(_name); | ||
| 464 | proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; | ||
| 465 | proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; | ||
| 466 | proxy->getPose = [this](const UserInputMapper::Input& input, int timestamp) -> UserInputMapper::PoseValue { return this->getPose(input.getChannel()); }; | ||
| 467 | proxy->getAvailabeInputs = [this] () -> QVector<UserInputMapper::InputPair> { | ||
| 468 | QVector<UserInputMapper::InputPair> availableInputs; | ||
| 469 | availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_0, 0), "Left Start")); | ||
| 470 | availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_1, 0), "Left Button 1")); | ||
| 471 | availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_2, 0), "Left Button 2")); | ||
| 472 | availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_3, 0), "Left Button 3")); | ||
| 473 | availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_4, 0), "Left Button 4")); | ||
| 474 | |||
| 475 | availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_FWD, 0), "L1")); | ||
| 476 | availableInputs.append(UserInputMapper::InputPair(makeInput(BACK_TRIGGER, 0), "L2")); | ||
| 477 | |||
| 478 | availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_POS, 0), "Left Stick Up")); | ||
| 479 | availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_NEG, 0), "Left Stick Down")); | ||
| 480 | availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_POS, 0), "Left Stick Right")); | ||
| 481 | availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_NEG, 0), "Left Stick Left")); | ||
| 482 | availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_TRIGGER, 0), "Left Trigger Press")); | ||
| 483 | |||
| 484 | availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_0, 1), "Right Start")); | ||
| 485 | availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_1, 1), "Right Button 1")); | ||
| 486 | availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_2, 1), "Right Button 2")); | ||
| 487 | availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_3, 1), "Right Button 3")); | ||
| 488 | availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_4, 1), "Right Button 4")); | ||
| 489 | |||
| 490 | availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_FWD, 1), "R1")); | ||
| 491 | availableInputs.append(UserInputMapper::InputPair(makeInput(BACK_TRIGGER, 1), "R2")); | ||
| 492 | |||
| 493 | availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_POS, 1), "Right Stick Up")); | ||
| 494 | availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_NEG, 1), "Right Stick Down")); | ||
| 495 | availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_POS, 1), "Right Stick Right")); | ||
| 496 | availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_NEG, 1), "Right Stick Left")); | ||
| 497 | availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_TRIGGER, 1), "Right Trigger Press")); | ||
| 498 | |||
| 499 | return availableInputs; | ||
| 500 | }; | ||
| 501 | proxy->resetDeviceBindings = [this, &mapper] () -> bool { | ||
| 502 | mapper.removeAllInputChannelsForDevice(_deviceID); | ||
| 503 | this->assignDefaultInputMapping(mapper); | ||
| 504 | return true; | ||
| 505 | }; | ||
| 506 | mapper.registerDevice(_deviceID, proxy); | ||
| 507 | } | ||
| 508 | |||
| 509 | void SixenseManager::assignDefaultInputMapping(UserInputMapper& mapper) { | ||
| 510 | const float JOYSTICK_MOVE_SPEED = 1.0f; | ||
| 511 | const float JOYSTICK_YAW_SPEED = 0.5f; | ||
| 512 | const float JOYSTICK_PITCH_SPEED = 0.25f; | ||
| 513 | const float BUTTON_MOVE_SPEED = 1.0f; | ||
| 514 | const float BOOM_SPEED = 0.1f; | ||
| 515 | 474 | ||
| 516 | // Left Joystick: Movement, strafing | ||
| 517 | mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(AXIS_Y_POS, 0), JOYSTICK_MOVE_SPEED); | ||
| 518 | mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(AXIS_Y_NEG, 0), JOYSTICK_MOVE_SPEED); | ||
| 519 | mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(AXIS_X_POS, 0), JOYSTICK_MOVE_SPEED); | ||
| 520 | mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(AXIS_X_NEG, 0), JOYSTICK_MOVE_SPEED); | ||
| 521 | 475 | ||
| 522 | // Right Joystick: Camera orientation | ||
| 523 | mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(AXIS_X_POS, 1), JOYSTICK_YAW_SPEED); | ||
| 524 | mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(AXIS_X_NEG, 1), JOYSTICK_YAW_SPEED); | ||
| 525 | mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(AXIS_Y_POS, 1), JOYSTICK_PITCH_SPEED); | ||
| 526 | mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(AXIS_Y_NEG, 1), JOYSTICK_PITCH_SPEED); | ||
| 527 | 476 | ||
| 528 | // Buttons | 477 | if (prevPose.isValid() && deltaTime > std::numeric_limits<float>::epsilon()) { |
| 529 | mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(BUTTON_3, 0), BOOM_SPEED); | ||
| 530 | mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(BUTTON_1, 0), BOOM_SPEED); | ||
| 531 | 478 | ||
| 532 | mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(BUTTON_3, 1), BUTTON_MOVE_SPEED); | 479 | velocity = (position - prevPose.getTranslation()) / deltaTime; |
| 533 | mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(BUTTON_1, 1), BUTTON_MOVE_SPEED); | ||
| 534 | 480 | ||
| 535 | mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_2, 0)); | 481 | auto deltaRot = rotation * glm::conjugate(prevPose.getRotation()); |
| 536 | mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_2, 1)); | 482 | auto axis = glm::axis(deltaRot); |
| 483 | auto angle = glm::angle(deltaRot); | ||
| 484 | angularVelocity = glm::angleAxis(angle / deltaTime, axis); | ||
| 537 | 485 | ||
| 538 | mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(BUTTON_4, 0)); | 486 | // Average |
| 539 | mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(BUTTON_4, 1)); | 487 | auto& samples = _collectedSamples[hand]; |
| 488 | samples.first.addSample(velocity); | ||
| 489 | velocity = samples.first.average; | ||
| 490 | |||
| 491 | // FIXME: // Not using quaternion average yet for angular velocity because it s probably wrong but keep the MovingAverage in place | ||
| 492 | //samples.second.addSample(glm::vec4(angularVelocity.x, angularVelocity.y, angularVelocity.z, angularVelocity.w)); | ||
| 493 | //angularVelocity = glm::quat(samples.second.average.w, samples.second.average.x, samples.second.average.y, samples.second.average.z); | ||
| 494 | } else if (!prevPose.isValid()) { | ||
| 495 | _collectedSamples[hand].first.clear(); | ||
| 496 | _collectedSamples[hand].second.clear(); | ||
| 497 | } | ||
| 540 | 498 | ||
| 541 | mapper.addInputChannel(UserInputMapper::LEFT_HAND, makeInput(LEFT_HAND)); | 499 | _poseStateMap[hand] = controller::Pose(position, rotation, velocity, angularVelocity); |
| 542 | mapper.addInputChannel(UserInputMapper::RIGHT_HAND, makeInput(RIGHT_HAND)); | 500 | #endif // HAVE_SIXENSE |
| 501 | } | ||
| 543 | 502 | ||
| 544 | mapper.addInputChannel(UserInputMapper::LEFT_HAND_CLICK, makeInput(BACK_TRIGGER, 0)); | 503 | static const auto L0 = controller::BACK; |
| 545 | mapper.addInputChannel(UserInputMapper::RIGHT_HAND_CLICK, makeInput(BACK_TRIGGER, 1)); | 504 | static const auto L1 = controller::DL; |
| 505 | static const auto L2 = controller::DD; | ||
| 506 | static const auto L3 = controller::DR; | ||
| 507 | static const auto L4 = controller::DU; | ||
| 508 | static const auto R0 = controller::START; | ||
| 509 | static const auto R1 = controller::X; | ||
| 510 | static const auto R2 = controller::A; | ||
| 511 | static const auto R3 = controller::B; | ||
| 512 | static const auto R4 = controller::Y; | ||
| 513 | |||
| 514 | using namespace controller; | ||
| 515 | |||
| 516 | controller::Input::NamedVector SixenseManager::getAvailableInputs() const { | ||
| 517 | using namespace controller; | ||
| 518 | static const Input::NamedVector availableInputs { | ||
| 519 | makePair(L0, "L0"), | ||
| 520 | makePair(L1, "L1"), | ||
| 521 | makePair(L2, "L2"), | ||
| 522 | makePair(L3, "L3"), | ||
| 523 | makePair(L4, "L4"), | ||
| 524 | makePair(LB, "LB"), | ||
| 525 | makePair(LS, "LS"), | ||
| 526 | makePair(LX, "LX"), | ||
| 527 | makePair(LY, "LY"), | ||
| 528 | makePair(LT, "LT"), | ||
| 529 | makePair(R0, "R0"), | ||
| 530 | makePair(R1, "R1"), | ||
| 531 | makePair(R2, "R2"), | ||
| 532 | makePair(R3, "R3"), | ||
| 533 | makePair(R4, "R4"), | ||
| 534 | makePair(RB, "RB"), | ||
| 535 | makePair(RS, "RS"), | ||
| 536 | makePair(RX, "RX"), | ||
| 537 | makePair(RY, "RY"), | ||
| 538 | makePair(RT, "RT"), | ||
| 539 | makePair(LEFT_HAND, "LeftHand"), | ||
| 540 | makePair(RIGHT_HAND, "RightHand"), | ||
| 541 | }; | ||
| 542 | return availableInputs; | ||
| 543 | }; | ||
| 546 | 544 | ||
| 547 | // TODO find a mechanism to allow users to navigate the context menu via | ||
| 548 | mapper.addInputChannel(UserInputMapper::CONTEXT_MENU, makeInput(BUTTON_0, 0)); | ||
| 549 | mapper.addInputChannel(UserInputMapper::TOGGLE_MUTE, makeInput(BUTTON_0, 1)); | ||
| 550 | 545 | ||
| 546 | QString SixenseManager::getDefaultMappingConfig() const { | ||
| 547 | static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/hydra.json"; | ||
| 548 | return MAPPING_JSON; | ||
| 551 | } | 549 | } |
| 552 | 550 | ||
| 551 | // | ||
| 552 | //void SixenseManager::assignDefaultInputMapping(UserInputMapper& mapper) { | ||
| 553 | // const float JOYSTICK_MOVE_SPEED = 1.0f; | ||
| 554 | // const float JOYSTICK_YAW_SPEED = 0.5f; | ||
| 555 | // const float JOYSTICK_PITCH_SPEED = 0.25f; | ||
| 556 | // const float BUTTON_MOVE_SPEED = 1.0f; | ||
| 557 | // const float BOOM_SPEED = 0.1f; | ||
| 558 | // using namespace controller; | ||
| 559 | // | ||
| 560 | // // Left Joystick: Movement, strafing | ||
| 561 | // mapper.addInputChannel(UserInputMapper::TRANSLATE_Z, makeInput(LY), JOYSTICK_MOVE_SPEED); | ||
| 562 | // mapper.addInputChannel(UserInputMapper::TRANSLATE_X, makeInput(LX), JOYSTICK_MOVE_SPEED); | ||
| 563 | // | ||
| 564 | // // Right Joystick: Camera orientation | ||
| 565 | // mapper.addInputChannel(UserInputMapper::YAW, makeInput(RX), JOYSTICK_YAW_SPEED); | ||
| 566 | // mapper.addInputChannel(UserInputMapper::PITCH, makeInput(RY), JOYSTICK_PITCH_SPEED); | ||
| 567 | // | ||
| 568 | // // Buttons | ||
| 569 | // mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(L3), BOOM_SPEED); | ||
| 570 | // mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(L1), BOOM_SPEED); | ||
| 571 | // | ||
| 572 | // mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(R3), BUTTON_MOVE_SPEED); | ||
| 573 | // mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(R1), BUTTON_MOVE_SPEED); | ||
| 574 | // | ||
| 575 | // mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(L2)); | ||
| 576 | // mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(R2)); | ||
| 577 | // | ||
| 578 | // mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(L4)); | ||
| 579 | // mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(R4)); | ||
| 580 | // | ||
| 581 | // // FIXME | ||
| 582 | //// mapper.addInputChannel(UserInputMapper::LEFT_HAND, makeInput(LEFT_HAND)); | ||
| 583 | //// mapper.addInputChannel(UserInputMapper::RIGHT_HAND, makeInput(RIGHT_HAND)); | ||
| 584 | // | ||
| 585 | // mapper.addInputChannel(UserInputMapper::LEFT_HAND_CLICK, makeInput(LT)); | ||
| 586 | // mapper.addInputChannel(UserInputMapper::RIGHT_HAND_CLICK, makeInput(RT)); | ||
| 587 | // | ||
| 588 | // // TODO find a mechanism to allow users to navigate the context menu via | ||
| 589 | // mapper.addInputChannel(UserInputMapper::CONTEXT_MENU, makeInput(L0)); | ||
| 590 | // mapper.addInputChannel(UserInputMapper::TOGGLE_MUTE, makeInput(R0)); | ||
| 591 | // | ||
| 592 | //} | ||
| 593 | |||
| 553 | // virtual | 594 | // virtual |
| 554 | void SixenseManager::saveSettings() const { | 595 | void SixenseManager::saveSettings() const { |
| 555 | Settings settings; | 596 | Settings settings; |
| @@ -559,7 +600,6 @@ void SixenseManager::saveSettings() const { · 3 unmodified lines | |||
| 559 | settings.setVec3Value(QString("avatarPosition"), _avatarPosition); | 600 | settings.setVec3Value(QString("avatarPosition"), _avatarPosition); |
| 560 | settings.setQuatValue(QString("avatarRotation"), _avatarRotation); | 601 | settings.setQuatValue(QString("avatarRotation"), _avatarRotation); |
| 561 | settings.setValue(QString("reachLength"), QVariant(_reachLength)); | 602 | settings.setValue(QString("reachLength"), QVariant(_reachLength)); |
| 562 | settings.setValue(QString("allowedHydraFailures"), 120); | ||
| 563 | } | 603 | } |
| 564 | settings.endGroup(); | 604 | settings.endGroup(); |
| 565 | } | 605 | } |
| @@ -572,19 +612,6 @@ void SixenseManager::loadSettings() { · 6 unmodified lines | |||
| 572 | settings.getVec3ValueIfValid(QString("avatarPosition"), _avatarPosition); | 612 | settings.getVec3ValueIfValid(QString("avatarPosition"), _avatarPosition); |
| 573 | settings.getQuatValueIfValid(QString("avatarRotation"), _avatarRotation); | 613 | settings.getQuatValueIfValid(QString("avatarRotation"), _avatarRotation); |
| 574 | settings.getFloatValueIfValid(QString("reachLength"), _reachLength); | 614 | settings.getFloatValueIfValid(QString("reachLength"), _reachLength); |
| 575 | _allowedBadDataCount = settings.value(QString("allowedHydraFailures"), 120).toInt(); | ||
| 576 | } | 615 | } |
| 577 | settings.endGroup(); | 616 | settings.endGroup(); |
| 578 | } | 617 | } |
| 579 | |||
| 580 | UserInputMapper::Input SixenseManager::makeInput(unsigned int button, int index) { | ||
| 581 | return UserInputMapper::Input(_deviceID, button | (index == 0 ? LEFT_MASK : RIGHT_MASK), UserInputMapper::ChannelType::BUTTON); | ||
| 582 | } | ||
| 583 | |||
| 584 | UserInputMapper::Input SixenseManager::makeInput(SixenseManager::JoystickAxisChannel axis, int index) { | ||
| 585 | return UserInputMapper::Input(_deviceID, axis | (index == 0 ? LEFT_MASK : RIGHT_MASK), UserInputMapper::ChannelType::AXIS); | ||
| 586 | } | ||
| 587 | |||
| 588 | UserInputMapper::Input SixenseManager::makeInput(JointChannel joint) { | ||
| 589 | return UserInputMapper::Input(_deviceID, joint, UserInputMapper::ChannelType::POSE); | ||
| 590 | } | ||
| @@ -24,8 +24,12 @@ · 23 unmodified lines | |||
| 24 | 24 | ||
| 25 | #endif | 25 | #endif |
| 26 | 26 | ||
| 27 | #include <SimpleMovingAverage.h> | ||
| 28 | |||
| 29 | #include <controllers/InputDevice.h> | ||
| 30 | #include <controllers/StandardControls.h> | ||
| 31 | |||
| 27 | #include "InputPlugin.h" | 32 | #include "InputPlugin.h" |
| 28 | #include "InputDevice.h" | ||
| 29 | 33 | ||
| 30 | class QLibrary; | 34 | class QLibrary; |
| 31 | 35 | ||
| @@ -40,22 +44,9 @@ const unsigned int BUTTON_TRIGGER = 1U << 8; · 8 unmodified lines | |||
| 40 | const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = false; | 44 | const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = false; |
| 41 | 45 | ||
| 42 | // Handles interaction with the Sixense SDK (e.g., Razer Hydra). | 46 | // Handles interaction with the Sixense SDK (e.g., Razer Hydra). |
| 43 | class SixenseManager : public InputPlugin, public InputDevice { | 47 | class SixenseManager : public InputPlugin, public controller::InputDevice { |
| 44 | Q_OBJECT | 48 | Q_OBJECT |
| 45 | public: | 49 | public: |
| 46 | enum JoystickAxisChannel { | ||
| 47 | AXIS_Y_POS = 1U << 0, | ||
| 48 | AXIS_Y_NEG = 1U << 3, | ||
| 49 | AXIS_X_POS = 1U << 4, | ||
| 50 | AXIS_X_NEG = 1U << 5, | ||
| 51 | BACK_TRIGGER = 1U << 6, | ||
| 52 | }; | ||
| 53 | |||
| 54 | enum JointChannel { | ||
| 55 | LEFT_HAND = 0, | ||
| 56 | RIGHT_HAND, | ||
| 57 | }; | ||
| 58 | |||
| 59 | SixenseManager(); | 50 | SixenseManager(); |
| 60 | 51 | ||
| 61 | // Plugin functions | 52 | // Plugin functions |
| @@ -71,15 +62,12 @@ public: · 9 unmodified lines | |||
| 71 | virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); } | 62 | virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); } |
| 72 | 63 | ||
| 73 | // Device functions | 64 | // Device functions |
| 74 | virtual void registerToUserInputMapper(UserInputMapper& mapper) override; | 65 | virtual controller::Input::NamedVector getAvailableInputs() const override; |
| 75 | virtual void assignDefaultInputMapping(UserInputMapper& mapper) override; | 66 | virtual QString getDefaultMappingConfig() const override; |
| 67 | |||
| 76 | virtual void update(float deltaTime, bool jointsCaptured) override; | 68 | virtual void update(float deltaTime, bool jointsCaptured) override; |
| 77 | virtual void focusOutEvent() override; | 69 | virtual void focusOutEvent() override; |
| 78 | 70 | ||
| 79 | UserInputMapper::Input makeInput(unsigned int button, int index); | ||
| 80 | UserInputMapper::Input makeInput(JoystickAxisChannel axis, int index); | ||
| 81 | UserInputMapper::Input makeInput(JointChannel joint); | ||
| 82 | |||
| 83 | virtual void saveSettings() const override; | 71 | virtual void saveSettings() const override; |
| 84 | virtual void loadSettings() override; | 72 | virtual void loadSettings() override; |
| 85 | 73 | ||
| @@ -87,9 +75,9 @@ public slots: · 1 unmodified line | |||
| 87 | void setSixenseFilter(bool filter); | 75 | void setSixenseFilter(bool filter); |
| 88 | 76 | ||
| 89 | private: | 77 | private: |
| 90 | void handleButtonEvent(unsigned int buttons, int index); | 78 | void handleButtonEvent(unsigned int buttons, bool left); |
| 91 | void handleAxisEvent(float x, float y, float trigger, int index); | 79 | void handleAxisEvent(float x, float y, float trigger, bool left); |
| 92 | void handlePoseEvent(glm::vec3 position, glm::quat rotation, int index); | 80 | void handlePoseEvent(float deltaTime, glm::vec3 position, glm::quat rotation, bool left); |
| 93 | 81 | ||
| 94 | void updateCalibration(void* controllers); | 82 | void updateCalibration(void* controllers); |
| 95 | 83 | ||
| @@ -108,17 +96,20 @@ private: · 12 unmodified lines | |||
| 108 | glm::vec3 _reachRight; | 96 | glm::vec3 _reachRight; |
| 109 | float _lastDistance; | 97 | float _lastDistance; |
| 110 | bool _useSixenseFilter = true; | 98 | bool _useSixenseFilter = true; |
| 99 | |||
| 100 | |||
| 101 | static const int MAX_NUM_AVERAGING_SAMPLES = 50; // At ~100 updates per seconds this means averaging over ~.5s | ||
| 102 | using Samples = std::pair< MovingAverage< glm::vec3, MAX_NUM_AVERAGING_SAMPLES>, MovingAverage< glm::vec4, MAX_NUM_AVERAGING_SAMPLES> >; | ||
| 103 | using MovingAverageMap = std::map< int, Samples >; | ||
| 104 | MovingAverageMap _collectedSamples; | ||
| 111 | 105 | ||
| 112 | #ifdef __APPLE__ | 106 | #ifdef __APPLE__ |
| 113 | QLibrary* _sixenseLibrary; | 107 | QLibrary* _sixenseLibrary { nullptr }; |
| 114 | #endif | 108 | #endif |
| 115 | 109 | ||
| 116 | bool _hydrasConnected; | ||
| 117 | int _badDataCount; | ||
| 118 | int _allowedBadDataCount; | ||
| 119 | |||
| 120 | static const QString NAME; | 110 | static const QString NAME; |
| 121 | static const QString HYDRA_ID_STRING; | 111 | static const QString HYDRA_ID_STRING; |
| 122 | }; | 112 | }; |
| 123 | 113 | ||
| 124 | #endif // hifi_SixenseManager_h | 114 | #endif // hifi_SixenseManager_h |
| 115 | |||
| @@ -1,160 +0,0 @@ | |||
| 1 | // | ||
| 2 | // StandardController.cpp | ||
| 3 | // input-plugins/src/input-plugins | ||
| 4 | // | ||
| 5 | // Created by Brad Hefta-Gaub on 2015-10-11. | ||
| 6 | // Copyright 2015 High Fidelity, Inc. | ||
| 7 | // | ||
| 8 | // Distributed under the Apache License, Version 2.0. | ||
| 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 10 | // | ||
| 11 | |||
| 12 | #include <limits> | ||
| 13 | |||
| 14 | #include <glm/glm.hpp> | ||
| 15 | |||
| 16 | #include "StandardController.h" | ||
| 17 | |||
| 18 | const float CONTROLLER_THRESHOLD = 0.3f; | ||
| 19 | |||
| 20 | StandardController::~StandardController() { | ||
| 21 | } | ||
| 22 | |||
| 23 | void StandardController::update(float deltaTime, bool jointsCaptured) { | ||
| 24 | for (auto axisState : _axisStateMap) { | ||
| 25 | if (fabsf(axisState.second) < CONTROLLER_THRESHOLD) { | ||
| 26 | _axisStateMap[axisState.first] = 0.0f; | ||
| 27 | } | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | void StandardController::focusOutEvent() { | ||
| 32 | _axisStateMap.clear(); | ||
| 33 | _buttonPressedMap.clear(); | ||
| 34 | }; | ||
| 35 | |||
| 36 | void StandardController::registerToUserInputMapper(UserInputMapper& mapper) { | ||
| 37 | // Grab the current free device ID | ||
| 38 | _deviceID = mapper.getStandardDeviceID(); | ||
| 39 | |||
| 40 | auto proxy = std::make_shared<UserInputMapper::DeviceProxy>(_name); | ||
| 41 | proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; | ||
| 42 | proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; | ||
| 43 | proxy->getAvailabeInputs = [this] () -> QVector<UserInputMapper::InputPair> { | ||
| 44 | QVector<UserInputMapper::InputPair> availableInputs; | ||
| 45 | availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_A), "Bottom Button")); | ||
| 46 | availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_B), "Right Button")); | ||
| 47 | availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_X), "Left Button")); | ||
| 48 | availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_Y), "Top Button")); | ||
| 49 | |||
| 50 | availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_UP), "DPad Up")); | ||
| 51 | availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_DOWN), "DPad Down")); | ||
| 52 | availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_LEFT), "DPad Left")); | ||
| 53 | availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_RIGHT), "DPad Right")); | ||
| 54 | |||
| 55 | availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_LEFTSHOULDER), "L1")); | ||
| 56 | availableInputs.append(UserInputMapper::InputPair(makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), "R1")); | ||
| 57 | availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_SHOULDER), "L2")); | ||
| 58 | availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_SHOULDER), "R2")); | ||
| 59 | |||
| 60 | availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_Y_NEG), "Left Stick Up")); | ||
| 61 | availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_Y_POS), "Left Stick Down")); | ||
| 62 | availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_X_POS), "Left Stick Right")); | ||
| 63 | availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_AXIS_X_NEG), "Left Stick Left")); | ||
| 64 | availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_Y_NEG), "Right Stick Up")); | ||
| 65 | availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_Y_POS), "Right Stick Down")); | ||
| 66 | availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_X_POS), "Right Stick Right")); | ||
| 67 | availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_AXIS_X_NEG), "Right Stick Left")); | ||
| 68 | |||
| 69 | availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_HAND), "Left Hand")); | ||
| 70 | availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_HAND), "Right Hand")); | ||
| 71 | |||
| 72 | return availableInputs; | ||
| 73 | }; | ||
| 74 | proxy->resetDeviceBindings = [this, &mapper] () -> bool { | ||
| 75 | mapper.removeAllInputChannelsForDevice(_deviceID); | ||
| 76 | this->assignDefaultInputMapping(mapper); | ||
| 77 | return true; | ||
| 78 | }; | ||
| 79 | mapper.registerStandardDevice(proxy); | ||
| 80 | } | ||
| 81 | |||
| 82 | void StandardController::assignDefaultInputMapping(UserInputMapper& mapper) { | ||
| 83 | const float JOYSTICK_MOVE_SPEED = 1.0f; | ||
| 84 | const float DPAD_MOVE_SPEED = 0.5f; | ||
| 85 | const float JOYSTICK_YAW_SPEED = 0.5f; | ||
| 86 | const float JOYSTICK_PITCH_SPEED = 0.25f; | ||
| 87 | const float BOOM_SPEED = 0.1f; | ||
| 88 | |||
| 89 | // Y axes are flipped (up is negative) | ||
| 90 | // Left StandardController: Movement, strafing | ||
| 91 | mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(LEFT_AXIS_Y_NEG), JOYSTICK_MOVE_SPEED); | ||
| 92 | mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(LEFT_AXIS_Y_POS), JOYSTICK_MOVE_SPEED); | ||
| 93 | mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(LEFT_AXIS_X_POS), JOYSTICK_MOVE_SPEED); | ||
| 94 | mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(LEFT_AXIS_X_NEG), JOYSTICK_MOVE_SPEED); | ||
| 95 | |||
| 96 | // Right StandardController: Camera orientation | ||
| 97 | mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(RIGHT_AXIS_X_POS), JOYSTICK_YAW_SPEED); | ||
| 98 | mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(RIGHT_AXIS_X_NEG), JOYSTICK_YAW_SPEED); | ||
| 99 | mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(RIGHT_AXIS_Y_NEG), JOYSTICK_PITCH_SPEED); | ||
| 100 | mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(RIGHT_AXIS_Y_POS), JOYSTICK_PITCH_SPEED); | ||
| 101 | |||
| 102 | // Dpad movement | ||
| 103 | mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_UP), DPAD_MOVE_SPEED); | ||
| 104 | mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_DOWN), DPAD_MOVE_SPEED); | ||
| 105 | mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_RIGHT), DPAD_MOVE_SPEED); | ||
| 106 | mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_LEFT), DPAD_MOVE_SPEED); | ||
| 107 | |||
| 108 | // Button controls | ||
| 109 | mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(STANDARD_CONTROLLER_BUTTON_Y), DPAD_MOVE_SPEED); | ||
| 110 | mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(STANDARD_CONTROLLER_BUTTON_X), DPAD_MOVE_SPEED); | ||
| 111 | |||
| 112 | // Zoom | ||
| 113 | mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(RIGHT_SHOULDER), BOOM_SPEED); | ||
| 114 | mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(LEFT_SHOULDER), BOOM_SPEED); | ||
| 115 | |||
| 116 | |||
| 117 | // Hold front right shoulder button for precision controls | ||
| 118 | // Left StandardController: Movement, strafing | ||
| 119 | mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(LEFT_AXIS_Y_NEG), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f); | ||
| 120 | mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(LEFT_AXIS_Y_POS), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f); | ||
| 121 | mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(LEFT_AXIS_X_POS), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f); | ||
| 122 | mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(LEFT_AXIS_X_NEG), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f); | ||
| 123 | |||
| 124 | // Right StandardController: Camera orientation | ||
| 125 | mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(RIGHT_AXIS_X_POS), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f); | ||
| 126 | mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(RIGHT_AXIS_X_NEG), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f); | ||
| 127 | mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(RIGHT_AXIS_Y_NEG), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_PITCH_SPEED/2.0f); | ||
| 128 | mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(RIGHT_AXIS_Y_POS), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_PITCH_SPEED/2.0f); | ||
| 129 | |||
| 130 | // Dpad movement | ||
| 131 | mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_UP), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); | ||
| 132 | mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_DOWN), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); | ||
| 133 | mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_RIGHT), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); | ||
| 134 | mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(STANDARD_CONTROLLER_BUTTON_DPAD_LEFT), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); | ||
| 135 | |||
| 136 | // Button controls | ||
| 137 | mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(STANDARD_CONTROLLER_BUTTON_Y), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); | ||
| 138 | mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(STANDARD_CONTROLLER_BUTTON_X), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f); | ||
| 139 | |||
| 140 | // Zoom | ||
| 141 | mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(RIGHT_SHOULDER), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), BOOM_SPEED/2.0f); | ||
| 142 | mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(LEFT_SHOULDER), makeInput(STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER), BOOM_SPEED/2.0f); | ||
| 143 | |||
| 144 | mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(STANDARD_CONTROLLER_BUTTON_LEFTSHOULDER)); | ||
| 145 | |||
| 146 | mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(STANDARD_CONTROLLER_BUTTON_B)); | ||
| 147 | mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(STANDARD_CONTROLLER_BUTTON_A)); | ||
| 148 | } | ||
| 149 | |||
| 150 | UserInputMapper::Input StandardController::makeInput(StandardController::StandardControllerButtonChannel button) { | ||
| 151 | return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON); | ||
| 152 | } | ||
| 153 | |||
| 154 | UserInputMapper::Input StandardController::makeInput(StandardController::StandardControllerAxisChannel axis) { | ||
| 155 | return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS); | ||
| 156 | } | ||
| 157 | |||
| 158 | UserInputMapper::Input StandardController::makeInput(StandardController::StandardControllerPoseChannel pose) { | ||
| 159 | return UserInputMapper::Input(_deviceID, pose, UserInputMapper::ChannelType::POSE); | ||
| 160 | } | ||
| @@ -1,77 +0,0 @@ | |||
| 1 | // | ||
| 2 | // StandardController.h | ||
| 3 | // input-plugins/src/input-plugins | ||
| 4 | // | ||
| 5 | // Created by Brad Hefta-Gaub on 2015-10-11. | ||
| 6 | // Copyright 2015 High Fidelity, Inc. | ||
| 7 | // | ||
| 8 | // Distributed under the Apache License, Version 2.0. | ||
| 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 10 | // | ||
| 11 | |||
| 12 | #ifndef hifi_StandardController_h | ||
| 13 | #define hifi_StandardController_h | ||
| 14 | |||
| 15 | #include <qobject.h> | ||
| 16 | #include <qvector.h> | ||
| 17 | |||
| 18 | #include "InputDevice.h" | ||
| 19 | |||
| 20 | typedef std::shared_ptr<StandardController> StandardControllerPointer; | ||
| 21 | |||
| 22 | class StandardController : public QObject, public InputDevice { | ||
| 23 | Q_OBJECT | ||
| 24 | Q_PROPERTY(QString name READ getName) | ||
| 25 | |||
| 26 | public: | ||
| 27 | enum StandardControllerAxisChannel { | ||
| 28 | LEFT_AXIS_X_POS = 0, | ||
| 29 | LEFT_AXIS_X_NEG, | ||
| 30 | LEFT_AXIS_Y_POS, | ||
| 31 | LEFT_AXIS_Y_NEG, | ||
| 32 | RIGHT_AXIS_X_POS, | ||
| 33 | RIGHT_AXIS_X_NEG, | ||
| 34 | RIGHT_AXIS_Y_POS, | ||
| 35 | RIGHT_AXIS_Y_NEG, | ||
| 36 | RIGHT_SHOULDER, | ||
| 37 | LEFT_SHOULDER, | ||
| 38 | }; | ||
| 39 | enum StandardControllerButtonChannel { | ||
| 40 | STANDARD_CONTROLLER_BUTTON_A = 0, | ||
| 41 | STANDARD_CONTROLLER_BUTTON_B, | ||
| 42 | STANDARD_CONTROLLER_BUTTON_X, | ||
| 43 | STANDARD_CONTROLLER_BUTTON_Y, | ||
| 44 | |||
| 45 | STANDARD_CONTROLLER_BUTTON_DPAD_UP, | ||
| 46 | STANDARD_CONTROLLER_BUTTON_DPAD_DOWN, | ||
| 47 | STANDARD_CONTROLLER_BUTTON_DPAD_LEFT, | ||
| 48 | STANDARD_CONTROLLER_BUTTON_DPAD_RIGHT, | ||
| 49 | |||
| 50 | STANDARD_CONTROLLER_BUTTON_LEFTSHOULDER, | ||
| 51 | STANDARD_CONTROLLER_BUTTON_RIGHTSHOULDER, | ||
| 52 | }; | ||
| 53 | |||
| 54 | enum StandardControllerPoseChannel { | ||
| 55 | LEFT_HAND = 0, | ||
| 56 | RIGHT_HAND, | ||
| 57 | }; | ||
| 58 | |||
| 59 | const QString& getName() const { return _name; } | ||
| 60 | |||
| 61 | // Device functions | ||
| 62 | virtual void registerToUserInputMapper(UserInputMapper& mapper) override; | ||
| 63 | virtual void assignDefaultInputMapping(UserInputMapper& mapper) override; | ||
| 64 | virtual void update(float deltaTime, bool jointsCaptured) override; | ||
| 65 | virtual void focusOutEvent() override; | ||
| 66 | |||
| 67 | StandardController() : InputDevice("Standard") {} | ||
| 68 | ~StandardController(); | ||
| 69 | |||
| 70 | UserInputMapper::Input makeInput(StandardController::StandardControllerButtonChannel button); | ||
| 71 | UserInputMapper::Input makeInput(StandardController::StandardControllerAxisChannel axis); | ||
| 72 | UserInputMapper::Input makeInput(StandardController::StandardControllerPoseChannel pose); | ||
| 73 | |||
| 74 | private: | ||
| 75 | }; | ||
| 76 | |||
| 77 | #endif // hifi_StandardController_h | ||
| @@ -1,335 +0,0 @@ | |||
| 1 | // | ||
| 2 | // UserInputMapper.cpp | ||
| 3 | // input-plugins/src/input-plugins | ||
| 4 | // | ||
| 5 | // Created by Sam Gateau on 4/27/15. | ||
| 6 | // Copyright 2015 High Fidelity, Inc. | ||
| 7 | // | ||
| 8 | // Distributed under the Apache License, Version 2.0. | ||
| 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 10 | // | ||
| 11 | |||
| 12 | #include "UserInputMapper.h" | ||
| 13 | #include "StandardController.h" | ||
| 14 | |||
| 15 | // Default contruct allocate the poutput size with the current hardcoded action channels | ||
| 16 | UserInputMapper::UserInputMapper() { | ||
| 17 | registerStandardDevice(); | ||
| 18 | assignDefaulActionScales(); | ||
| 19 | createActionNames(); | ||
| 20 | } | ||
| 21 | |||
| 22 | UserInputMapper::~UserInputMapper() { | ||
| 23 | } | ||
| 24 | |||
| 25 | |||
| 26 | bool UserInputMapper::registerDevice(uint16 deviceID, const DeviceProxy::Pointer& proxy){ | ||
| 27 | proxy->_name += " (" + QString::number(deviceID) + ")"; | ||
| 28 | _registeredDevices[deviceID] = proxy; | ||
| 29 | return true; | ||
| 30 | } | ||
| 31 | |||
| 32 | UserInputMapper::DeviceProxy::Pointer UserInputMapper::getDeviceProxy(const Input& input) { | ||
| 33 | auto device = _registeredDevices.find(input.getDevice()); | ||
| 34 | if (device != _registeredDevices.end()) { | ||
| 35 | return (device->second); | ||
| 36 | } else { | ||
| 37 | return DeviceProxy::Pointer(); | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | QString UserInputMapper::getDeviceName(uint16 deviceID) { | ||
| 42 | if (_registeredDevices.find(deviceID) != _registeredDevices.end()) { | ||
| 43 | return _registeredDevices[deviceID]->_name; | ||
| 44 | } | ||
| 45 | return QString("unknown"); | ||
| 46 | } | ||
| 47 | |||
| 48 | |||
| 49 | void UserInputMapper::resetAllDeviceBindings() { | ||
| 50 | for (auto device : _registeredDevices) { | ||
| 51 | device.second->resetDeviceBindings(); | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | void UserInputMapper::resetDevice(uint16 deviceID) { | ||
| 56 | auto device = _registeredDevices.find(deviceID); | ||
| 57 | if (device != _registeredDevices.end()) { | ||
| 58 | device->second->resetDeviceBindings(); | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | int UserInputMapper::findDevice(QString name) { | ||
| 63 | for (auto device : _registeredDevices) { | ||
| 64 | if (device.second->_name.split(" (")[0] == name) { | ||
| 65 | return device.first; | ||
| 66 | } | ||
| 67 | } | ||
| 68 | return 0; | ||
| 69 | } | ||
| 70 | |||
| 71 | QVector<QString> UserInputMapper::getDeviceNames() { | ||
| 72 | QVector<QString> result; | ||
| 73 | for (auto device : _registeredDevices) { | ||
| 74 | QString deviceName = device.second->_name.split(" (")[0]; | ||
| 75 | result << deviceName; | ||
| 76 | } | ||
| 77 | return result; | ||
| 78 | } | ||
| 79 | |||
| 80 | |||
| 81 | bool UserInputMapper::addInputChannel(Action action, const Input& input, float scale) { | ||
| 82 | return addInputChannel(action, input, Input(), scale); | ||
| 83 | } | ||
| 84 | |||
| 85 | bool UserInputMapper::addInputChannel(Action action, const Input& input, const Input& modifier, float scale) { | ||
| 86 | // Check that the device is registered | ||
| 87 | if (!getDeviceProxy(input)) { | ||
| 88 | qDebug() << "UserInputMapper::addInputChannel: The input comes from a device #" << input.getDevice() << "is unknown. no inputChannel mapped."; | ||
| 89 | return false; | ||
| 90 | } | ||
| 91 | |||
| 92 | auto inputChannel = InputChannel(input, modifier, action, scale); | ||
| 93 | |||
| 94 | // Insert or replace the input to modifiers | ||
| 95 | if (inputChannel.hasModifier()) { | ||
| 96 | auto& modifiers = _inputToModifiersMap[input.getID()]; | ||
| 97 | modifiers.push_back(inputChannel._modifier); | ||
| 98 | std::sort(modifiers.begin(), modifiers.end()); | ||
| 99 | } | ||
| 100 | |||
| 101 | // Now update the action To Inputs side of things | ||
| 102 | _actionToInputsMap.insert(ActionToInputsMap::value_type(action, inputChannel)); | ||
| 103 | |||
| 104 | return true; | ||
| 105 | } | ||
| 106 | |||
| 107 | int UserInputMapper::addInputChannels(const InputChannels& channels) { | ||
| 108 | int nbAdded = 0; | ||
| 109 | for (auto& channel : channels) { | ||
| 110 | nbAdded += addInputChannel(channel._action, channel._input, channel._modifier, channel._scale); | ||
| 111 | } | ||
| 112 | return nbAdded; | ||
| 113 | } | ||
| 114 | |||
| 115 | bool UserInputMapper::removeInputChannel(InputChannel inputChannel) { | ||
| 116 | // Remove from Input to Modifiers map | ||
| 117 | if (inputChannel.hasModifier()) { | ||
| 118 | _inputToModifiersMap.erase(inputChannel._input.getID()); | ||
| 119 | } | ||
| 120 | |||
| 121 | // Remove from Action to Inputs map | ||
| 122 | std::pair<ActionToInputsMap::iterator, ActionToInputsMap::iterator> ret; | ||
| 123 | ret = _actionToInputsMap.equal_range(inputChannel._action); | ||
| 124 | for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) { | ||
| 125 | if (it->second == inputChannel) { | ||
| 126 | _actionToInputsMap.erase(it); | ||
| 127 | return true; | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | return false; | ||
| 132 | } | ||
| 133 | |||
| 134 | void UserInputMapper::removeAllInputChannels() { | ||
| 135 | _inputToModifiersMap.clear(); | ||
| 136 | _actionToInputsMap.clear(); | ||
| 137 | } | ||
| 138 | |||
| 139 | void UserInputMapper::removeAllInputChannelsForDevice(uint16 device) { | ||
| 140 | QVector<InputChannel> channels = getAllInputsForDevice(device); | ||
| 141 | for (auto& channel : channels) { | ||
| 142 | removeInputChannel(channel); | ||
| 143 | } | ||
| 144 | } | ||
| 145 | |||
| 146 | void UserInputMapper::removeDevice(int device) { | ||
| 147 | removeAllInputChannelsForDevice((uint16) device); | ||
| 148 | _registeredDevices.erase(device); | ||
| 149 | } | ||
| 150 | |||
| 151 | int UserInputMapper::getInputChannels(InputChannels& channels) const { | ||
| 152 | for (auto& channel : _actionToInputsMap) { | ||
| 153 | channels.push_back(channel.second); | ||
| 154 | } | ||
| 155 | |||
| 156 | return _actionToInputsMap.size(); | ||
| 157 | } | ||
| 158 | |||
| 159 | QVector<UserInputMapper::InputChannel> UserInputMapper::getAllInputsForDevice(uint16 device) { | ||
| 160 | InputChannels allChannels; | ||
| 161 | getInputChannels(allChannels); | ||
| 162 | |||
| 163 | QVector<InputChannel> channels; | ||
| 164 | for (InputChannel inputChannel : allChannels) { | ||
| 165 | if (inputChannel._input._device == device) { | ||
| 166 | channels.push_back(inputChannel); | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | return channels; | ||
| 171 | } | ||
| 172 | |||
| 173 | void UserInputMapper::update(float deltaTime) { | ||
| 174 | |||
| 175 | // Reset the axis state for next loop | ||
| 176 | for (auto& channel : _actionStates) { | ||
| 177 | channel = 0.0f; | ||
| 178 | } | ||
| 179 | |||
| 180 | for (auto& channel : _poseStates) { | ||
| 181 | channel = PoseValue(); | ||
| 182 | } | ||
| 183 | |||
| 184 | int currentTimestamp = 0; | ||
| 185 | |||
| 186 | for (auto& channelInput : _actionToInputsMap) { | ||
| 187 | auto& inputMapping = channelInput.second; | ||
| 188 | auto& inputID = inputMapping._input; | ||
| 189 | bool enabled = true; | ||
| 190 | |||
| 191 | // Check if this input channel has modifiers and collect the possibilities | ||
| 192 | auto modifiersIt = _inputToModifiersMap.find(inputID.getID()); | ||
| 193 | if (modifiersIt != _inputToModifiersMap.end()) { | ||
| 194 | Modifiers validModifiers; | ||
| 195 | bool isActiveModifier = false; | ||
| 196 | for (auto& modifier : modifiersIt->second) { | ||
| 197 | auto deviceProxy = getDeviceProxy(modifier); | ||
| 198 | if (deviceProxy->getButton(modifier, currentTimestamp)) { | ||
| 199 | validModifiers.push_back(modifier); | ||
| 200 | isActiveModifier |= (modifier.getID() == inputMapping._modifier.getID()); | ||
| 201 | } | ||
| 202 | } | ||
| 203 | enabled = (validModifiers.empty() && !inputMapping.hasModifier()) || isActiveModifier; | ||
| 204 | } | ||
| 205 | |||
| 206 | // if enabled: default input or all modifiers on | ||
| 207 | if (enabled) { | ||
| 208 | auto deviceProxy = getDeviceProxy(inputID); | ||
| 209 | switch (inputMapping._input.getType()) { | ||
| 210 | case ChannelType::BUTTON: { | ||
| 211 | _actionStates[channelInput.first] += inputMapping._scale * float(deviceProxy->getButton(inputID, currentTimestamp));// * deltaTime; // weight the impulse by the deltaTime | ||
| 212 | break; | ||
| 213 | } | ||
| 214 | case ChannelType::AXIS: { | ||
| 215 | _actionStates[channelInput.first] += inputMapping._scale * deviceProxy->getAxis(inputID, currentTimestamp); | ||
| 216 | break; | ||
| 217 | } | ||
| 218 | case ChannelType::POSE: { | ||
| 219 | if (!_poseStates[channelInput.first].isValid()) { | ||
| 220 | _poseStates[channelInput.first] = deviceProxy->getPose(inputID, currentTimestamp); | ||
| 221 | } | ||
| 222 | break; | ||
| 223 | } | ||
| 224 | default: { | ||
| 225 | break; //silence please | ||
| 226 | } | ||
| 227 | } | ||
| 228 | } else{ | ||
| 229 | // Channel input not enabled | ||
| 230 | enabled = false; | ||
| 231 | } | ||
| 232 | } | ||
| 233 | |||
| 234 | // Scale all the channel step with the scale | ||
| 235 | static const float EPSILON = 0.01f; | ||
| 236 | for (auto i = 0; i < NUM_ACTIONS; i++) { | ||
| 237 | _actionStates[i] *= _actionScales[i]; | ||
| 238 | // Emit only on change, and emit when moving back to 0 | ||
| 239 | if (fabsf(_actionStates[i] - _lastActionStates[i]) > EPSILON) { | ||
| 240 | _lastActionStates[i] = _actionStates[i]; | ||
| 241 | emit actionEvent(i, _actionStates[i]); | ||
| 242 | } | ||
| 243 | // TODO: emit signal for pose changes | ||
| 244 | } | ||
| 245 | } | ||
| 246 | |||
| 247 | QVector<UserInputMapper::Action> UserInputMapper::getAllActions() const { | ||
| 248 | QVector<Action> actions; | ||
| 249 | for (auto i = 0; i < NUM_ACTIONS; i++) { | ||
| 250 | actions.append(Action(i)); | ||
| 251 | } | ||
| 252 | return actions; | ||
| 253 | } | ||
| 254 | |||
| 255 | QVector<UserInputMapper::InputChannel> UserInputMapper::getInputChannelsForAction(UserInputMapper::Action action) { | ||
| 256 | QVector<InputChannel> inputChannels; | ||
| 257 | std::pair <ActionToInputsMap::iterator, ActionToInputsMap::iterator> ret; | ||
| 258 | ret = _actionToInputsMap.equal_range(action); | ||
| 259 | for (ActionToInputsMap::iterator it=ret.first; it!=ret.second; ++it) { | ||
| 260 | inputChannels.append(it->second); | ||
| 261 | } | ||
| 262 | return inputChannels; | ||
| 263 | } | ||
| 264 | |||
| 265 | int UserInputMapper::findAction(const QString& actionName) const { | ||
| 266 | auto actions = getAllActions(); | ||
| 267 | for (auto action : actions) { | ||
| 268 | if (getActionName(action) == actionName) { | ||
| 269 | return action; | ||
| 270 | } | ||
| 271 | } | ||
| 272 | // If the action isn't found, return -1 | ||
| 273 | return -1; | ||
| 274 | } | ||
| 275 | |||
| 276 | QVector<QString> UserInputMapper::getActionNames() const { | ||
| 277 | QVector<QString> result; | ||
| 278 | for (auto i = 0; i < NUM_ACTIONS; i++) { | ||
| 279 | result << _actionNames[i]; | ||
| 280 | } | ||
| 281 | return result; | ||
| 282 | } | ||
| 283 | |||
| 284 | void UserInputMapper::assignDefaulActionScales() { | ||
| 285 | _actionScales[LONGITUDINAL_BACKWARD] = 1.0f; // 1m per unit | ||
| 286 | _actionScales[LONGITUDINAL_FORWARD] = 1.0f; // 1m per unit | ||
| 287 | _actionScales[LATERAL_LEFT] = 1.0f; // 1m per unit | ||
| 288 | _actionScales[LATERAL_RIGHT] = 1.0f; // 1m per unit | ||
| 289 | _actionScales[VERTICAL_DOWN] = 1.0f; // 1m per unit | ||
| 290 | _actionScales[VERTICAL_UP] = 1.0f; // 1m per unit | ||
| 291 | _actionScales[YAW_LEFT] = 1.0f; // 1 degree per unit | ||
| 292 | _actionScales[YAW_RIGHT] = 1.0f; // 1 degree per unit | ||
| 293 | _actionScales[PITCH_DOWN] = 1.0f; // 1 degree per unit | ||
| 294 | _actionScales[PITCH_UP] = 1.0f; // 1 degree per unit | ||
| 295 | _actionScales[BOOM_IN] = 0.5f; // .5m per unit | ||
| 296 | _actionScales[BOOM_OUT] = 0.5f; // .5m per unit | ||
| 297 | _actionScales[LEFT_HAND] = 1.0f; // default | ||
| 298 | _actionScales[RIGHT_HAND] = 1.0f; // default | ||
| 299 | _actionScales[LEFT_HAND_CLICK] = 1.0f; // on | ||
| 300 | _actionScales[RIGHT_HAND_CLICK] = 1.0f; // on | ||
| 301 | _actionStates[SHIFT] = 1.0f; // on | ||
| 302 | _actionStates[ACTION1] = 1.0f; // default | ||
| 303 | _actionStates[ACTION2] = 1.0f; // default | ||
| 304 | } | ||
| 305 | |||
| 306 | // This is only necessary as long as the actions are hardcoded | ||
| 307 | // Eventually you can just add the string when you add the action | ||
| 308 | void UserInputMapper::createActionNames() { | ||
| 309 | _actionNames[LONGITUDINAL_BACKWARD] = "LONGITUDINAL_BACKWARD"; | ||
| 310 | _actionNames[LONGITUDINAL_FORWARD] = "LONGITUDINAL_FORWARD"; | ||
| 311 | _actionNames[LATERAL_LEFT] = "LATERAL_LEFT"; | ||
| 312 | _actionNames[LATERAL_RIGHT] = "LATERAL_RIGHT"; | ||
| 313 | _actionNames[VERTICAL_DOWN] = "VERTICAL_DOWN"; | ||
| 314 | _actionNames[VERTICAL_UP] = "VERTICAL_UP"; | ||
| 315 | _actionNames[YAW_LEFT] = "YAW_LEFT"; | ||
| 316 | _actionNames[YAW_RIGHT] = "YAW_RIGHT"; | ||
| 317 | _actionNames[PITCH_DOWN] = "PITCH_DOWN"; | ||
| 318 | _actionNames[PITCH_UP] = "PITCH_UP"; | ||
| 319 | _actionNames[BOOM_IN] = "BOOM_IN"; | ||
| 320 | _actionNames[BOOM_OUT] = "BOOM_OUT"; | ||
| 321 | _actionNames[LEFT_HAND] = "LEFT_HAND"; | ||
| 322 | _actionNames[RIGHT_HAND] = "RIGHT_HAND"; | ||
| 323 | _actionNames[LEFT_HAND_CLICK] = "LEFT_HAND_CLICK"; | ||
| 324 | _actionNames[RIGHT_HAND_CLICK] = "RIGHT_HAND_CLICK"; | ||
| 325 | _actionNames[SHIFT] = "SHIFT"; | ||
| 326 | _actionNames[ACTION1] = "ACTION1"; | ||
| 327 | _actionNames[ACTION2] = "ACTION2"; | ||
| 328 | _actionNames[CONTEXT_MENU] = "CONTEXT_MENU"; | ||
| 329 | _actionNames[TOGGLE_MUTE] = "TOGGLE_MUTE"; | ||
| 330 | } | ||
| 331 | |||
| 332 | void UserInputMapper::registerStandardDevice() { | ||
| 333 | _standardController = std::make_shared<StandardController>(); | ||
| 334 | _standardController->registerToUserInputMapper(*this); | ||
| 335 | } | ||
| @@ -1,284 +0,0 @@ | |||
| 1 | // | ||
| 2 | // UserInputMapper.h | ||
| 3 | // input-plugins/src/input-plugins | ||
| 4 | // | ||
| 5 | // Created by Sam Gateau on 4/27/15. | ||
| 6 | // Copyright 2015 High Fidelity, Inc. | ||
| 7 | // | ||
| 8 | // Distributed under the Apache License, Version 2.0. | ||
| 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 10 | // | ||
| 11 | |||
| 12 | #ifndef hifi_UserInputMapper_h | ||
| 13 | #define hifi_UserInputMapper_h | ||
| 14 | |||
| 15 | #include <glm/glm.hpp> | ||
| 16 | |||
| 17 | #include <unordered_set> | ||
| 18 | #include <functional> | ||
| 19 | #include <memory> | ||
| 20 | #include <DependencyManager.h> | ||
| 21 | #include <RegisteredMetaTypes.h> | ||
| 22 | |||
| 23 | class StandardController; | ||
| 24 | typedef std::shared_ptr<StandardController> StandardControllerPointer; | ||
| 25 | |||
| 26 | class UserInputMapper : public QObject, public Dependency { | ||
| 27 | Q_OBJECT | ||
| 28 | SINGLETON_DEPENDENCY | ||
| 29 | Q_ENUMS(Action) | ||
| 30 | public: | ||
| 31 | ~UserInputMapper(); | ||
| 32 | |||
| 33 | typedef unsigned short uint16; | ||
| 34 | typedef unsigned int uint32; | ||
| 35 | |||
| 36 | enum class ChannelType { | ||
| 37 | UNKNOWN = 0, | ||
| 38 | BUTTON = 1, | ||
| 39 | AXIS, | ||
| 40 | POSE, | ||
| 41 | }; | ||
| 42 | |||
| 43 | // Input is the unique identifier to find a n input channel of a particular device | ||
| 44 | // Devices are responsible for registering to the UseInputMapper so their input channels can be sued and mapped | ||
| 45 | // to the Action channels | ||
| 46 | class Input { | ||
| 47 | public: | ||
| 48 | union { | ||
| 49 | struct { | ||
| 50 | uint16 _device; // Up to 64K possible devices | ||
| 51 | uint16 _channel : 14; // 2^14 possible channel per Device | ||
| 52 | uint16 _type : 2; // 2 bits to store the Type directly in the ID | ||
| 53 | }; | ||
| 54 | uint32 _id = 0; // by default Input is 0 meaning invalid | ||
| 55 | }; | ||
| 56 | |||
| 57 | bool isValid() const { return (_id != 0); } | ||
| 58 | |||
| 59 | uint16 getDevice() const { return _device; } | ||
| 60 | uint16 getChannel() const { return _channel; } | ||
| 61 | uint32 getID() const { return _id; } | ||
| 62 | ChannelType getType() const { return (ChannelType) _type; } | ||
| 63 | |||
| 64 | void setDevice(uint16 device) { _device = device; } | ||
| 65 | void setChannel(uint16 channel) { _channel = channel; } | ||
| 66 | void setType(uint16 type) { _type = type; } | ||
| 67 | void setID(uint32 ID) { _id = ID; } | ||
| 68 | |||
| 69 | bool isButton() const { return getType() == ChannelType::BUTTON; } | ||
| 70 | bool isAxis() const { return getType() == ChannelType::AXIS; } | ||
| 71 | bool isPose() const { return getType() == ChannelType::POSE; } | ||
| 72 | |||
| 73 | // WORKAROUND: the explicit initializer here avoids a bug in GCC-4.8.2 (but not found in 4.9.2) | ||
| 74 | // where the default initializer (a C++-11ism) for the union data above is not applied. | ||
| 75 | explicit Input() : _id(0) {} | ||
| 76 | explicit Input(uint32 id) : _id(id) {} | ||
| 77 | explicit Input(uint16 device, uint16 channel, ChannelType type) : _device(device), _channel(channel), _type(uint16(type)) {} | ||
| 78 | Input(const Input& src) : _id(src._id) {} | ||
| 79 | Input& operator = (const Input& src) { _id = src._id; return (*this); } | ||
| 80 | bool operator ==(const Input& right) const { return _id == right._id; } | ||
| 81 | bool operator < (const Input& src) const { return _id < src._id; } | ||
| 82 | }; | ||
| 83 | |||
| 84 | // Modifiers are just button inputID | ||
| 85 | typedef std::vector< Input > Modifiers; | ||
| 86 | |||
| 87 | class PoseValue { | ||
| 88 | public: | ||
| 89 | glm::vec3 _translation{ 0.0f }; | ||
| 90 | glm::quat _rotation; | ||
| 91 | bool _valid; | ||
| 92 | |||
| 93 | PoseValue() : _valid(false) {}; | ||
| 94 | PoseValue(glm::vec3 translation, glm::quat rotation) : _translation(translation), _rotation(rotation), _valid(true) {} | ||
| 95 | PoseValue(const PoseValue&) = default; | ||
| 96 | PoseValue& operator = (const PoseValue&) = default; | ||
| 97 | bool operator ==(const PoseValue& right) const { return _translation == right.getTranslation() && _rotation == right.getRotation() && _valid == right.isValid(); } | ||
| 98 | |||
| 99 | bool isValid() const { return _valid; } | ||
| 100 | glm::vec3 getTranslation() const { return _translation; } | ||
| 101 | glm::quat getRotation() const { return _rotation; } | ||
| 102 | }; | ||
| 103 | |||
| 104 | typedef std::function<bool (const Input& input, int timestamp)> ButtonGetter; | ||
| 105 | typedef std::function<float (const Input& input, int timestamp)> AxisGetter; | ||
| 106 | typedef std::function<PoseValue (const Input& input, int timestamp)> PoseGetter; | ||
| 107 | typedef QPair<Input, QString> InputPair; | ||
| 108 | typedef std::function<QVector<InputPair> ()> AvailableInputGetter; | ||
| 109 | typedef std::function<bool ()> ResetBindings; | ||
| 110 | |||
| 111 | typedef QVector<InputPair> AvailableInput; | ||
| 112 | |||
| 113 | class DeviceProxy { | ||
| 114 | public: | ||
| 115 | DeviceProxy(QString name) { _name = name; } | ||
| 116 | const QString& getName() const { return _name; } | ||
| 117 | |||
| 118 | QString _name; | ||
| 119 | ButtonGetter getButton = [] (const Input& input, int timestamp) -> bool { return false; }; | ||
| 120 | AxisGetter getAxis = [] (const Input& input, int timestamp) -> float { return 0.0f; }; | ||
| 121 | PoseGetter getPose = [] (const Input& input, int timestamp) -> PoseValue { return PoseValue(); }; | ||
| 122 | AvailableInputGetter getAvailabeInputs = [] () -> AvailableInput { return QVector<InputPair>(); }; | ||
| 123 | ResetBindings resetDeviceBindings = [] () -> bool { return true; }; | ||
| 124 | |||
| 125 | typedef std::shared_ptr<DeviceProxy> Pointer; | ||
| 126 | }; | ||
| 127 | // GetFreeDeviceID should be called before registering a device to use an ID not used by a different device. | ||
| 128 | uint16 getFreeDeviceID() { return _nextFreeDeviceID++; } | ||
| 129 | bool registerDevice(uint16 deviceID, const DeviceProxy::Pointer& device); | ||
| 130 | bool registerStandardDevice(const DeviceProxy::Pointer& device) { _standardDevice = device; return true; } | ||
| 131 | DeviceProxy::Pointer getDeviceProxy(const Input& input); | ||
| 132 | QString getDeviceName(uint16 deviceID); | ||
| 133 | QVector<InputPair> getAvailableInputs(uint16 deviceID) { return _registeredDevices[deviceID]->getAvailabeInputs(); } | ||
| 134 | void resetAllDeviceBindings(); | ||
| 135 | void resetDevice(uint16 deviceID); | ||
| 136 | int findDevice(QString name); | ||
| 137 | QVector<QString> getDeviceNames(); | ||
| 138 | |||
| 139 | // Actions are the output channels of the Mapper, that's what the InputChannel map to | ||
| 140 | // For now the Actions are hardcoded, this is bad, but we will fix that in the near future | ||
| 141 | enum Action { | ||
| 142 | LONGITUDINAL_BACKWARD = 0, | ||
| 143 | LONGITUDINAL_FORWARD, | ||
| 144 | |||
| 145 | LATERAL_LEFT, | ||
| 146 | LATERAL_RIGHT, | ||
| 147 | |||
| 148 | VERTICAL_DOWN, | ||
| 149 | VERTICAL_UP, | ||
| 150 | |||
| 151 | YAW_LEFT, | ||
| 152 | YAW_RIGHT, | ||
| 153 | |||
| 154 | PITCH_DOWN, | ||
| 155 | PITCH_UP, | ||
| 156 | |||
| 157 | BOOM_IN, | ||
| 158 | BOOM_OUT, | ||
| 159 | |||
| 160 | LEFT_HAND, | ||
| 161 | RIGHT_HAND, | ||
| 162 | |||
| 163 | LEFT_HAND_CLICK, | ||
| 164 | RIGHT_HAND_CLICK, | ||
| 165 | |||
| 166 | SHIFT, | ||
| 167 | |||
| 168 | ACTION1, | ||
| 169 | ACTION2, | ||
| 170 | |||
| 171 | CONTEXT_MENU, | ||
| 172 | TOGGLE_MUTE, | ||
| 173 | |||
| 174 | NUM_ACTIONS, | ||
| 175 | }; | ||
| 176 | |||
| 177 | std::vector<QString> _actionNames = std::vector<QString>(NUM_ACTIONS); | ||
| 178 | void createActionNames(); | ||
| 179 | |||
| 180 | QVector<Action> getAllActions() const; | ||
| 181 | QString getActionName(Action action) const { return UserInputMapper::_actionNames[(int) action]; } | ||
| 182 | float getActionState(Action action) const { return _actionStates[action]; } | ||
| 183 | PoseValue getPoseState(Action action) const { return _poseStates[action]; } | ||
| 184 | int findAction(const QString& actionName) const; | ||
| 185 | QVector<QString> getActionNames() const; | ||
| 186 | void assignDefaulActionScales(); | ||
| 187 | |||
| 188 | // Add input channel to the mapper and check that all the used channels are registered. | ||
| 189 | // Return true if theinput channel is created correctly, false either | ||
| 190 | bool addInputChannel(Action action, const Input& input, float scale = 1.0f); | ||
| 191 | bool addInputChannel(Action action, const Input& input, const Input& modifer, float scale = 1.0f); | ||
| 192 | |||
| 193 | // Under the hood, the input channels are organized in map sorted on the _output | ||
| 194 | // The InputChannel class is just the full values describing the input channel in one object | ||
| 195 | class InputChannel { | ||
| 196 | public: | ||
| 197 | Input _input; | ||
| 198 | Input _modifier = Input(); // make it invalid by default, meaning no modifier | ||
| 199 | Action _action = LONGITUDINAL_BACKWARD; | ||
| 200 | float _scale = 0.0f; | ||
| 201 | |||
| 202 | Input getInput() const { return _input; } | ||
| 203 | Input getModifier() const { return _modifier; } | ||
| 204 | Action getAction() const { return _action; } | ||
| 205 | float getScale() const { return _scale; } | ||
| 206 | |||
| 207 | void setInput(Input input) { _input = input; } | ||
| 208 | void setModifier(Input modifier) { _modifier = modifier; } | ||
| 209 | void setAction(Action action) { _action = action; } | ||
| 210 | void setScale(float scale) { _scale = scale; } | ||
| 211 | |||
| 212 | InputChannel() {} | ||
| 213 | InputChannel(const Input& input, const Input& modifier, Action action, float scale = 1.0f) : | ||
| 214 | _input(input), _modifier(modifier), _action(action), _scale(scale) {} | ||
| 215 | InputChannel(const InputChannel& src) : InputChannel(src._input, src._modifier, src._action, src._scale) {} | ||
| 216 | InputChannel& operator = (const InputChannel& src) { _input = src._input; _modifier = src._modifier; _action = src._action; _scale = src._scale; return (*this); } | ||
| 217 | bool operator ==(const InputChannel& right) const { return _input == right._input && _modifier == right._modifier && _action == right._action && _scale == right._scale; } | ||
| 218 | bool hasModifier() { return _modifier.isValid(); } | ||
| 219 | }; | ||
| 220 | typedef std::vector< InputChannel > InputChannels; | ||
| 221 | |||
| 222 | // Add a bunch of input channels, return the true number of channels that successfully were added | ||
| 223 | int addInputChannels(const InputChannels& channels); | ||
| 224 | // Remove the first found instance of the input channel from the input mapper, true if found | ||
| 225 | bool removeInputChannel(InputChannel channel); | ||
| 226 | void removeAllInputChannels(); | ||
| 227 | void removeAllInputChannelsForDevice(uint16 device); | ||
| 228 | void removeDevice(int device); | ||
| 229 | //Grab all the input channels currently in use, return the number | ||
| 230 | int getInputChannels(InputChannels& channels) const; | ||
| 231 | QVector<InputChannel> getAllInputsForDevice(uint16 device); | ||
| 232 | QVector<InputChannel> getInputChannelsForAction(UserInputMapper::Action action); | ||
| 233 | std::multimap<Action, InputChannel> getActionToInputsMap() { return _actionToInputsMap; } | ||
| 234 | |||
| 235 | // Update means go grab all the device input channels and update the output channel values | ||
| 236 | void update(float deltaTime); | ||
| 237 | |||
| 238 | void setSensorToWorldMat(glm::mat4 sensorToWorldMat) { _sensorToWorldMat = sensorToWorldMat; } | ||
| 239 | glm::mat4 getSensorToWorldMat() { return _sensorToWorldMat; } | ||
| 240 | |||
| 241 | UserInputMapper(); | ||
| 242 | |||
| 243 | typedef std::map<int, DeviceProxy::Pointer> DevicesMap; | ||
| 244 | DevicesMap getDevices() { return _registeredDevices; } | ||
| 245 | |||
| 246 | uint16 getStandardDeviceID() const { return _standardDeviceID; } | ||
| 247 | DeviceProxy::Pointer getStandardDevice() { return _standardDevice; } | ||
| 248 | |||
| 249 | signals: | ||
| 250 | void actionEvent(int action, float state); | ||
| 251 | |||
| 252 | |||
| 253 | protected: | ||
| 254 | void registerStandardDevice(); | ||
| 255 | uint16 _standardDeviceID = 0; | ||
| 256 | DeviceProxy::Pointer _standardDevice; | ||
| 257 | StandardControllerPointer _standardController; | ||
| 258 | |||
| 259 | DevicesMap _registeredDevices; | ||
| 260 | uint16 _nextFreeDeviceID = 1; | ||
| 261 | |||
| 262 | typedef std::map<int, Modifiers> InputToMoModifiersMap; | ||
| 263 | InputToMoModifiersMap _inputToModifiersMap; | ||
| 264 | |||
| 265 | typedef std::multimap<Action, InputChannel> ActionToInputsMap; | ||
| 266 | ActionToInputsMap _actionToInputsMap; | ||
| 267 | |||
| 268 | std::vector<float> _actionStates = std::vector<float>(NUM_ACTIONS, 0.0f); | ||
| 269 | std::vector<float> _actionScales = std::vector<float>(NUM_ACTIONS, 1.0f); | ||
| 270 | std::vector<float> _lastActionStates = std::vector<float>(NUM_ACTIONS, 0.0f); | ||
| 271 | std::vector<PoseValue> _poseStates = std::vector<PoseValue>(NUM_ACTIONS); | ||
| 272 | |||
| 273 | glm::mat4 _sensorToWorldMat; | ||
| 274 | }; | ||
| 275 | |||
| 276 | Q_DECLARE_METATYPE(UserInputMapper::InputPair) | ||
| 277 | Q_DECLARE_METATYPE(QVector<UserInputMapper::InputPair>) | ||
| 278 | Q_DECLARE_METATYPE(UserInputMapper::Input) | ||
| 279 | Q_DECLARE_METATYPE(UserInputMapper::InputChannel) | ||
| 280 | Q_DECLARE_METATYPE(QVector<UserInputMapper::InputChannel>) | ||
| 281 | Q_DECLARE_METATYPE(UserInputMapper::Action) | ||
| 282 | Q_DECLARE_METATYPE(QVector<UserInputMapper::Action>) | ||
| 283 | |||
| 284 | #endif // hifi_UserInputMapper_h | ||
| @@ -12,15 +12,19 @@ · 11 unmodified lines | |||
| 12 | #include "ViveControllerManager.h" | 12 | #include "ViveControllerManager.h" |
| 13 | 13 | ||
| 14 | #include <PerfStat.h> | 14 | #include <PerfStat.h> |
| 15 | 15 | #include <PathUtils.h> | |
| 16 | #include "GeometryCache.h" | 16 | #include <GeometryCache.h> |
| 17 | #include <gpu/Batch.h> | 17 | #include <gpu/Batch.h> |
| 18 | #include <gpu/Context.h> | 18 | #include <gpu/Context.h> |
| 19 | #include <DeferredLightingEffect.h> | 19 | #include <DeferredLightingEffect.h> |
| 20 | #include <display-plugins/openvr/OpenVrHelpers.h> | 20 | #include <display-plugins/openvr/OpenVrHelpers.h> |
| 21 | #include "NumericalConstants.h" | 21 | #include <NumericalConstants.h> |
| 22 | #include <plugins/PluginContainer.h> | 22 | #include <plugins/PluginContainer.h> |
| 23 | #include "UserActivityLogger.h" | 23 | #include <UserActivityLogger.h> |
| 24 | |||
| 25 | #include <controllers/UserInputMapper.h> | ||
| 26 | |||
| 27 | #include <controllers/StandardControls.h> | ||
| 24 | 28 | ||
| 25 | #ifdef Q_OS_WIN | 29 | #ifdef Q_OS_WIN |
| 26 | extern vr::IVRSystem* _hmd; | 30 | extern vr::IVRSystem* _hmd; |
| @@ -29,18 +33,6 @@ extern vr::TrackedDevicePose_t _trackedDevicePose[vr::k_unMaxTrackedDeviceCount] · 2 unmodified lines | |||
| 29 | extern mat4 _trackedDevicePoseMat4[vr::k_unMaxTrackedDeviceCount]; | 33 | extern mat4 _trackedDevicePoseMat4[vr::k_unMaxTrackedDeviceCount]; |
| 30 | #endif | 34 | #endif |
| 31 | 35 | ||
| 32 | const unsigned int LEFT_MASK = 0U; | ||
| 33 | const unsigned int RIGHT_MASK = 1U; | ||
| 34 | |||
| 35 | const uint64_t VR_BUTTON_A = 1U << 1; | ||
| 36 | const uint64_t VR_GRIP_BUTTON = 1U << 2; | ||
| 37 | const uint64_t VR_TRACKPAD_BUTTON = 1ULL << 32; | ||
| 38 | const uint64_t VR_TRIGGER_BUTTON = 1ULL << 33; | ||
| 39 | |||
| 40 | const unsigned int BUTTON_A = 1U << 1; | ||
| 41 | const unsigned int GRIP_BUTTON = 1U << 2; | ||
| 42 | const unsigned int TRACKPAD_BUTTON = 1U << 3; | ||
| 43 | const unsigned int TRIGGER_BUTTON = 1U << 4; | ||
| 44 | 36 | ||
| 45 | const float CONTROLLER_LENGTH_OFFSET = 0.0762f; // three inches | 37 | const float CONTROLLER_LENGTH_OFFSET = 0.0762f; // three inches |
| 46 | const QString CONTROLLER_MODEL_STRING = "vr_controller_05_wireless_b"; | 38 | const QString CONTROLLER_MODEL_STRING = "vr_controller_05_wireless_b"; |
| @@ -52,15 +44,17 @@ const QString MENU_NAME = "Vive Controllers"; · 5 unmodified lines | |||
| 52 | const QString MENU_PATH = MENU_PARENT + ">" + MENU_NAME; | 44 | const QString MENU_PATH = MENU_PARENT + ">" + MENU_NAME; |
| 53 | const QString RENDER_CONTROLLERS = "Render Hand Controllers"; | 45 | const QString RENDER_CONTROLLERS = "Render Hand Controllers"; |
| 54 | 46 | ||
| 47 | static std::shared_ptr<ViveControllerManager> instance; | ||
| 48 | |||
| 55 | ViveControllerManager::ViveControllerManager() : | 49 | ViveControllerManager::ViveControllerManager() : |
| 56 | InputDevice("SteamVR Controller"), | 50 | InputDevice("Vive"), |
| 57 | _trackedControllers(0), | 51 | _trackedControllers(0), |
| 58 | _modelLoaded(false), | 52 | _modelLoaded(false), |
| 59 | _leftHandRenderID(0), | 53 | _leftHandRenderID(0), |
| 60 | _rightHandRenderID(0), | 54 | _rightHandRenderID(0), |
| 61 | _renderControllers(false) | 55 | _renderControllers(false) |
| 62 | { | 56 | { |
| 63 | 57 | instance = std::shared_ptr<ViveControllerManager>(this); | |
| 64 | } | 58 | } |
| 65 | 59 | ||
| 66 | bool ViveControllerManager::isSupported() const { | 60 | bool ViveControllerManager::isSupported() const { |
| @@ -79,6 +73,7 @@ bool ViveControllerManager::isSupported() const { · 12 unmodified lines | |||
| 79 | } | 73 | } |
| 80 | 74 | ||
| 81 | void ViveControllerManager::activate() { | 75 | void ViveControllerManager::activate() { |
| 76 | InputPlugin::activate(); | ||
| 82 | #ifdef Q_OS_WIN | 77 | #ifdef Q_OS_WIN |
| 83 | CONTAINER->addMenu(MENU_PATH); | 78 | CONTAINER->addMenu(MENU_PATH); |
| 84 | CONTAINER->addMenuItem(MENU_PATH, RENDER_CONTROLLERS, | 79 | CONTAINER->addMenuItem(MENU_PATH, RENDER_CONTROLLERS, |
| @@ -140,9 +135,16 @@ void ViveControllerManager::activate() { · 55 unmodified lines | |||
| 140 | _renderControllers = true; | 135 | _renderControllers = true; |
| 141 | } | 136 | } |
| 142 | #endif | 137 | #endif |
| 138 | |||
| 139 | // unregister with UserInputMapper | ||
| 140 | auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); | ||
| 141 | userInputMapper->registerDevice(instance); | ||
| 142 | _registeredWithInputMapper = true; | ||
| 143 | } | 143 | } |
| 144 | 144 | ||
| 145 | void ViveControllerManager::deactivate() { | 145 | void ViveControllerManager::deactivate() { |
| 146 | InputPlugin::deactivate(); | ||
| 147 | |||
| 146 | #ifdef Q_OS_WIN | 148 | #ifdef Q_OS_WIN |
| 147 | CONTAINER->removeMenuItem(MENU_NAME, RENDER_CONTROLLERS); | 149 | CONTAINER->removeMenuItem(MENU_NAME, RENDER_CONTROLLERS); |
| 148 | CONTAINER->removeMenu(MENU_PATH); | 150 | CONTAINER->removeMenu(MENU_PATH); |
| @@ -155,6 +157,11 @@ void ViveControllerManager::deactivate() { · 6 unmodified lines | |||
| 155 | } | 157 | } |
| 156 | _poseStateMap.clear(); | 158 | _poseStateMap.clear(); |
| 157 | #endif | 159 | #endif |
| 160 | |||
| 161 | // unregister with UserInputMapper | ||
| 162 | auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); | ||
| 163 | userInputMapper->removeDevice(_deviceID); | ||
| 164 | _registeredWithInputMapper = false; | ||
| 158 | } | 165 | } |
| 159 | 166 | ||
| 160 | void ViveControllerManager::updateRendering(RenderArgs* args, render::ScenePointer scene, render::PendingChanges pendingChanges) { | 167 | void ViveControllerManager::updateRendering(RenderArgs* args, render::ScenePointer scene, render::PendingChanges pendingChanges) { |
| @@ -170,8 +177,8 @@ void ViveControllerManager::updateRendering(RenderArgs* args, render::ScenePoint · 9 unmodified lines | |||
| 170 | //pendingChanges.updateItem(_leftHandRenderID, ); | 177 | //pendingChanges.updateItem(_leftHandRenderID, ); |
| 171 | 178 | ||
| 172 | 179 | ||
| 173 | UserInputMapper::PoseValue leftHand = _poseStateMap[makeInput(JointChannel::LEFT_HAND).getChannel()]; | 180 | controller::Pose leftHand = _poseStateMap[controller::StandardPoseChannel::LEFT_HAND]; |
| 174 | UserInputMapper::PoseValue rightHand = _poseStateMap[makeInput(JointChannel::RIGHT_HAND).getChannel()]; | 181 | controller::Pose rightHand = _poseStateMap[controller::StandardPoseChannel::RIGHT_HAND]; |
| 175 | 182 | ||
| 176 | gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { | 183 | gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { |
| 177 | auto geometryCache = DependencyManager::get<GeometryCache>(); | 184 | auto geometryCache = DependencyManager::get<GeometryCache>(); |
| @@ -183,21 +190,20 @@ void ViveControllerManager::updateRendering(RenderArgs* args, render::ScenePoint · 5 unmodified lines | |||
| 183 | //batch._glBindTexture(GL_TEXTURE_2D, _uexture); | 190 | //batch._glBindTexture(GL_TEXTURE_2D, _uexture); |
| 184 | 191 | ||
| 185 | if (leftHand.isValid()) { | 192 | if (leftHand.isValid()) { |
| 186 | renderHand(leftHand, batch, LEFT_HAND); | 193 | renderHand(leftHand, batch, 1); |
| 187 | } | 194 | } |
| 188 | if (rightHand.isValid()) { | 195 | if (rightHand.isValid()) { |
| 189 | renderHand(rightHand, batch, RIGHT_HAND); | 196 | renderHand(rightHand, batch, -1); |
| 190 | } | 197 | } |
| 191 | }); | 198 | }); |
| 192 | } | 199 | } |
| 193 | } | 200 | } |
| 194 | 201 | ||
| 195 | void ViveControllerManager::renderHand(UserInputMapper::PoseValue pose, gpu::Batch& batch, int index) { | 202 | void ViveControllerManager::renderHand(const controller::Pose& pose, gpu::Batch& batch, int sign) { |
| 196 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); | 203 | auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); |
| 197 | Transform transform(userInputMapper->getSensorToWorldMat()); | 204 | Transform transform(userInputMapper->getSensorToWorldMat()); |
| 198 | transform.postTranslate(pose.getTranslation() + pose.getRotation() * glm::vec3(0, 0, CONTROLLER_LENGTH_OFFSET)); | 205 | transform.postTranslate(pose.getTranslation() + pose.getRotation() * glm::vec3(0, 0, CONTROLLER_LENGTH_OFFSET)); |
| 199 | 206 | ||
| 200 | int sign = index == LEFT_HAND ? 1 : -1; | ||
| 201 | glm::quat rotation = pose.getRotation() * glm::angleAxis(PI, glm::vec3(1.0f, 0.0f, 0.0f)) * glm::angleAxis(sign * PI_OVER_TWO, glm::vec3(0.0f, 0.0f, 1.0f)); | 207 | glm::quat rotation = pose.getRotation() * glm::angleAxis(PI, glm::vec3(1.0f, 0.0f, 0.0f)) * glm::angleAxis(sign * PI_OVER_TWO, glm::vec3(0.0f, 0.0f, 1.0f)); |
| 202 | transform.postRotate(rotation); | 208 | transform.postRotate(rotation); |
| 203 | 209 | ||
| @@ -248,6 +254,7 @@ void ViveControllerManager::update(float deltaTime, bool jointsCaptured) { · 44 unmodified lines | |||
| 248 | } | 254 | } |
| 249 | 255 | ||
| 250 | numTrackedControllers++; | 256 | numTrackedControllers++; |
| 257 | bool left = numTrackedControllers == 1; | ||
| 251 | 258 | ||
| 252 | const mat4& mat = _trackedDevicePoseMat4[device]; | 259 | const mat4& mat = _trackedDevicePoseMat4[device]; |
| 253 | 260 | ||
| @@ -261,26 +268,30 @@ void ViveControllerManager::update(float deltaTime, bool jointsCaptured) { · 7 unmodified lines | |||
| 261 | //qDebug() << (numTrackedControllers == 1 ? "Left: " : "Right: "); | 268 | //qDebug() << (numTrackedControllers == 1 ? "Left: " : "Right: "); |
| 262 | //qDebug() << "Trackpad: " << controllerState.rAxis[0].x << " " << controllerState.rAxis[0].y; | 269 | //qDebug() << "Trackpad: " << controllerState.rAxis[0].x << " " << controllerState.rAxis[0].y; |
| 263 | //qDebug() << "Trigger: " << controllerState.rAxis[1].x << " " << controllerState.rAxis[1].y; | 270 | //qDebug() << "Trigger: " << controllerState.rAxis[1].x << " " << controllerState.rAxis[1].y; |
| 264 | handleButtonEvent(controllerState.ulButtonPressed, numTrackedControllers - 1); | 271 | for (uint32_t i = 0; i < vr::k_EButton_Max; ++i) { |
| 265 | for (int i = 0; i < vr::k_unControllerStateAxisCount; i++) { | 272 | auto mask = vr::ButtonMaskFromId((vr::EVRButtonId)i); |
| 266 | handleAxisEvent(Axis(i), controllerState.rAxis[i].x, controllerState.rAxis[i].y, numTrackedControllers - 1); | 273 | bool pressed = 0 != (controllerState.ulButtonPressed & mask); |
| 274 | handleButtonEvent(i, pressed, left); | ||
| 275 | } | ||
| 276 | for (uint32_t i = 0; i < vr::k_unControllerStateAxisCount; i++) { | ||
| 277 | handleAxisEvent(i, controllerState.rAxis[i].x, controllerState.rAxis[i].y, left); | ||
| 267 | } | 278 | } |
| 268 | } | 279 | } |
| 269 | } | 280 | } |
| 270 | 281 | ||
| 271 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); | 282 | auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); |
| 272 | 283 | ||
| 273 | if (numTrackedControllers == 0) { | 284 | if (numTrackedControllers == 0) { |
| 274 | if (_deviceID != 0) { | 285 | if (_registeredWithInputMapper) { |
| 275 | userInputMapper->removeDevice(_deviceID); | 286 | userInputMapper->removeDevice(_deviceID); |
| 276 | _deviceID = 0; | 287 | _registeredWithInputMapper = false; |
| 277 | _poseStateMap.clear(); | 288 | _poseStateMap.clear(); |
| 278 | } | 289 | } |
| 279 | } | 290 | } |
| 280 | 291 | ||
| 281 | if (_trackedControllers == 0 && numTrackedControllers > 0) { | 292 | if (_trackedControllers == 0 && numTrackedControllers > 0) { |
| 282 | registerToUserInputMapper(*userInputMapper); | 293 | userInputMapper->registerDevice(instance); |
| 283 | assignDefaultInputMapping(*userInputMapper); | 294 | _registeredWithInputMapper = true; |
| 284 | UserActivityLogger::getInstance().connectedDevice("spatial_controller", "steamVR"); | 295 | UserActivityLogger::getInstance().connectedDevice("spatial_controller", "steamVR"); |
| 285 | } | 296 | } |
| 286 | 297 | ||
| @@ -293,33 +304,45 @@ void ViveControllerManager::focusOutEvent() { · 6 unmodified lines | |||
| 293 | _buttonPressedMap.clear(); | 304 | _buttonPressedMap.clear(); |
| 294 | }; | 305 | }; |
| 295 | 306 | ||
| 296 | void ViveControllerManager::handleAxisEvent(Axis axis, float x, float y, int index) { | 307 | // These functions do translation from the Steam IDs to the standard controller IDs |
| 297 | if (axis == TRACKPAD_AXIS) { | 308 | void ViveControllerManager::handleAxisEvent(uint32_t axis, float x, float y, bool left) { |
| 298 | _axisStateMap[makeInput(AXIS_Y_POS, index).getChannel()] = (y > 0.0f) ? y : 0.0f; | 309 | #ifdef Q_OS_WIN |
| 299 | _axisStateMap[makeInput(AXIS_Y_NEG, index).getChannel()] = (y < 0.0f) ? -y : 0.0f; | 310 | axis += vr::k_EButton_Axis0; |
| 300 | _axisStateMap[makeInput(AXIS_X_POS, index).getChannel()] = (x > 0.0f) ? x : 0.0f; | 311 | using namespace controller; |
| 301 | _axisStateMap[makeInput(AXIS_X_NEG, index).getChannel()] = (x < 0.0f) ? -x : 0.0f; | 312 | if (axis == vr::k_EButton_SteamVR_Touchpad) { |
| 302 | } else if (axis == TRIGGER_AXIS) { | 313 | _axisStateMap[left ? LX : RX] = x; |
| 303 | _axisStateMap[makeInput(BACK_TRIGGER, index).getChannel()] = x; | 314 | _axisStateMap[left ? LY : RY] = y; |
| 315 | } else if (axis == vr::k_EButton_SteamVR_Trigger) { | ||
| 316 | //FIX ME: Seems that enters here everytime | ||
| 317 | _axisStateMap[left ? LT : RT] = x; | ||
| 304 | } | 318 | } |
| 319 | #endif | ||
| 305 | } | 320 | } |
| 306 | 321 | ||
| 307 | void ViveControllerManager::handleButtonEvent(uint64_t buttons, int index) { | 322 | // These functions do translation from the Steam IDs to the standard controller IDs |
| 308 | if (buttons & VR_BUTTON_A) { | 323 | void ViveControllerManager::handleButtonEvent(uint32_t button, bool pressed, bool left) { |
| 309 | _buttonPressedMap.insert(makeInput(BUTTON_A, index).getChannel()); | 324 | #ifdef Q_OS_WIN |
| 310 | } | 325 | if (!pressed) { |
| 311 | if (buttons & VR_GRIP_BUTTON) { | 326 | return; |
| 312 | _buttonPressedMap.insert(makeInput(GRIP_BUTTON, index).getChannel()); | ||
| 313 | } | ||
| 314 | if (buttons & VR_TRACKPAD_BUTTON) { | ||
| 315 | _buttonPressedMap.insert(makeInput(TRACKPAD_BUTTON, index).getChannel()); | ||
| 316 | } | 327 | } |
| 317 | if (buttons & VR_TRIGGER_BUTTON) { | 328 | |
| 318 | _buttonPressedMap.insert(makeInput(TRIGGER_BUTTON, index).getChannel()); | 329 | if (button == vr::k_EButton_ApplicationMenu) { |
| 330 | _buttonPressedMap.insert(left ? controller::LEFT_PRIMARY_THUMB : controller::RIGHT_PRIMARY_THUMB); | ||
| 331 | } else if (button == vr::k_EButton_Grip) { | ||
| 332 | // Tony says these are harder to reach, so make them the meta buttons | ||
| 333 | _buttonPressedMap.insert(left ? controller::LB : controller::RB); | ||
| 334 | } else if (button == vr::k_EButton_SteamVR_Trigger) { | ||
| 335 | _buttonPressedMap.insert(left ? controller::LT : controller::RT); | ||
| 336 | } else if (button == vr::k_EButton_SteamVR_Touchpad) { | ||
| 337 | _buttonPressedMap.insert(left ? controller::LS : controller::RS); | ||
| 338 | } else if (button == vr::k_EButton_System) { | ||
| 339 | //FIX ME: not able to ovrewrite the behaviour of this button | ||
| 340 | _buttonPressedMap.insert(left ? controller::LEFT_SECONDARY_THUMB : controller::RIGHT_SECONDARY_THUMB); | ||
| 319 | } | 341 | } |
| 342 | #endif | ||
| 320 | } | 343 | } |
| 321 | 344 | ||
| 322 | void ViveControllerManager::handlePoseEvent(const mat4& mat, int index) { | 345 | void ViveControllerManager::handlePoseEvent(const mat4& mat, bool left) { |
| 323 | glm::vec3 position = extractTranslation(mat); | 346 | glm::vec3 position = extractTranslation(mat); |
| 324 | glm::quat rotation = glm::quat_cast(mat); | 347 | glm::quat rotation = glm::quat_cast(mat); |
| 325 | 348 | ||
| @@ -375,7 +398,7 @@ void ViveControllerManager::handlePoseEvent(const mat4& mat, int index) { · 49 unmodified lines | |||
| 375 | 398 | ||
| 376 | const glm::quat quarterX = glm::angleAxis(PI / 2.0f, glm::vec3(1.0f, 0.0f, 0.0f)); | 399 | const glm::quat quarterX = glm::angleAxis(PI / 2.0f, glm::vec3(1.0f, 0.0f, 0.0f)); |
| 377 | const glm::quat yFlip = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); | 400 | const glm::quat yFlip = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); |
| 378 | float sign = (index == LEFT_HAND) ? -1.0f : 1.0f; | 401 | float sign = left ? -1.0f : 1.0f; |
| 379 | const glm::quat signedQuaterZ = glm::angleAxis(sign * PI / 2.0f, glm::vec3(0.0f, 0.0f, 1.0f)); | 402 | const glm::quat signedQuaterZ = glm::angleAxis(sign * PI / 2.0f, glm::vec3(0.0f, 0.0f, 1.0f)); |
| 380 | const glm::quat eighthX = glm::angleAxis(PI / 4.0f, glm::vec3(1.0f, 0.0f, 0.0f)); | 403 | const glm::quat eighthX = glm::angleAxis(PI / 4.0f, glm::vec3(1.0f, 0.0f, 0.0f)); |
| 381 | const glm::quat offset = glm::inverse(signedQuaterZ * eighthX); | 404 | const glm::quat offset = glm::inverse(signedQuaterZ * eighthX); |
| @@ -383,92 +406,79 @@ void ViveControllerManager::handlePoseEvent(const mat4& mat, int index) { · 1 unmodified line | |||
| 383 | 406 | ||
| 384 | position += rotation * glm::vec3(0, 0, -CONTROLLER_LENGTH_OFFSET); | 407 | position += rotation * glm::vec3(0, 0, -CONTROLLER_LENGTH_OFFSET); |
| 385 | 408 | ||
| 386 | _poseStateMap[makeInput(JointChannel(index)).getChannel()] = UserInputMapper::PoseValue(position, rotation); | 409 | _poseStateMap[left ? controller::LEFT_HAND : controller::RIGHT_HAND] = controller::Pose(position, rotation); |
| 387 | } | 410 | } |
| 388 | 411 | ||
| 389 | void ViveControllerManager::registerToUserInputMapper(UserInputMapper& mapper) { | 412 | controller::Input::NamedVector ViveControllerManager::getAvailableInputs() const { |
| 390 | // Grab the current free device ID | 413 | using namespace controller; |
| 391 | _deviceID = mapper.getFreeDeviceID(); | 414 | QVector<Input::NamedPair> availableInputs{ |
| 392 | 415 | // Trackpad analogs | |
| 393 | auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy(_name)); | 416 | makePair(LX, "LX"), |
| 394 | proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; | 417 | makePair(LY, "LY"), |
| 395 | proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; | 418 | makePair(RX, "RX"), |
| 396 | proxy->getPose = [this](const UserInputMapper::Input& input, int timestamp) -> UserInputMapper::PoseValue { return this->getPose(input.getChannel()); }; | 419 | makePair(RY, "RY"), |
| 397 | proxy->getAvailabeInputs = [this] () -> QVector<UserInputMapper::InputPair> { | 420 | // trigger analogs |
| 398 | QVector<UserInputMapper::InputPair> availableInputs; | 421 | makePair(LT, "LT"), |
| 399 | availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_HAND), "Left Hand")); | 422 | makePair(RT, "RT"), |
| 400 | 423 | ||
| 401 | availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_A, 0), "Left Button A")); | 424 | makePair(LB, "LB"), |
| 402 | availableInputs.append(UserInputMapper::InputPair(makeInput(GRIP_BUTTON, 0), "Left Grip Button")); | 425 | makePair(RB, "RB"), |
| 403 | availableInputs.append(UserInputMapper::InputPair(makeInput(TRACKPAD_BUTTON, 0), "Left Trackpad Button")); | 426 | |
| 404 | availableInputs.append(UserInputMapper::InputPair(makeInput(TRIGGER_BUTTON, 0), "Left Trigger Button")); | 427 | makePair(LS, "LS"), |
| 405 | 428 | makePair(RS, "RS"), | |
| 406 | availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_POS, 0), "Left Trackpad Up")); | 429 | makePair(LEFT_HAND, "LeftHand"), |
| 407 | availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_NEG, 0), "Left Trackpad Down")); | 430 | makePair(RIGHT_HAND, "RightHand"), |
| 408 | availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_POS, 0), "Left Trackpad Right")); | 431 | |
| 409 | availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_NEG, 0), "Left Trackpad Left")); | 432 | makePair(LEFT_PRIMARY_THUMB, "LeftPrimaryThumb"), |
| 410 | availableInputs.append(UserInputMapper::InputPair(makeInput(BACK_TRIGGER, 0), "Left Back Trigger")); | 433 | makePair(LEFT_SECONDARY_THUMB, "LeftSecondaryThumb"), |
| 411 | 434 | makePair(RIGHT_PRIMARY_THUMB, "RightPrimaryThumb"), | |
| 412 | 435 | makePair(RIGHT_SECONDARY_THUMB, "RightSecondaryThumb"), | |
| 413 | availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_HAND), "Right Hand")); | ||
| 414 | |||
| 415 | availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_A, 1), "Right Button A")); | ||
| 416 | availableInputs.append(UserInputMapper::InputPair(makeInput(GRIP_BUTTON, 1), "Right Grip Button")); | ||
| 417 | availableInputs.append(UserInputMapper::InputPair(makeInput(TRACKPAD_BUTTON, 1), "Right Trackpad Button")); | ||
| 418 | availableInputs.append(UserInputMapper::InputPair(makeInput(TRIGGER_BUTTON, 1), "Right Trigger Button")); | ||
| 419 | |||
| 420 | availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_POS, 1), "Right Trackpad Up")); | ||
| 421 | availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_NEG, 1), "Right Trackpad Down")); | ||
| 422 | availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_POS, 1), "Right Trackpad Right")); | ||
| 423 | availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_NEG, 1), "Right Trackpad Left")); | ||
| 424 | availableInputs.append(UserInputMapper::InputPair(makeInput(BACK_TRIGGER, 1), "Right Back Trigger")); | ||
| 425 | |||
| 426 | return availableInputs; | ||
| 427 | }; | 436 | }; |
| 428 | proxy->resetDeviceBindings = [this, &mapper] () -> bool { | ||
| 429 | mapper.removeAllInputChannelsForDevice(_deviceID); | ||
| 430 | this->assignDefaultInputMapping(mapper); | ||
| 431 | return true; | ||
| 432 | }; | ||
| 433 | mapper.registerDevice(_deviceID, proxy); | ||
| 434 | } | ||
| 435 | |||
| 436 | void ViveControllerManager::assignDefaultInputMapping(UserInputMapper& mapper) { | ||
| 437 | const float JOYSTICK_MOVE_SPEED = 1.0f; | ||
| 438 | |||
| 439 | // Left Trackpad: Movement, strafing | ||
| 440 | mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(AXIS_Y_POS, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED); | ||
| 441 | mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(AXIS_Y_NEG, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED); | ||
| 442 | mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(AXIS_X_POS, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED); | ||
| 443 | mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(AXIS_X_NEG, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED); | ||
| 444 | |||
| 445 | // Right Trackpad: Vertical movement, zooming | ||
| 446 | mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(AXIS_Y_POS, 1), makeInput(TRACKPAD_BUTTON, 1), JOYSTICK_MOVE_SPEED); | ||
| 447 | mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(AXIS_Y_NEG, 1), makeInput(TRACKPAD_BUTTON, 1), JOYSTICK_MOVE_SPEED); | ||
| 448 | |||
| 449 | // Buttons | ||
| 450 | mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_A, 0)); | ||
| 451 | mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_A, 1)); | ||
| 452 | |||
| 453 | mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(GRIP_BUTTON, 0)); | ||
| 454 | mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(GRIP_BUTTON, 1)); | ||
| 455 | |||
| 456 | mapper.addInputChannel(UserInputMapper::LEFT_HAND_CLICK, makeInput(BACK_TRIGGER, 0)); | ||
| 457 | mapper.addInputChannel(UserInputMapper::RIGHT_HAND_CLICK, makeInput(BACK_TRIGGER, 1)); | ||
| 458 | |||
| 459 | // Hands | ||
| 460 | mapper.addInputChannel(UserInputMapper::LEFT_HAND, makeInput(LEFT_HAND)); | ||
| 461 | mapper.addInputChannel(UserInputMapper::RIGHT_HAND, makeInput(RIGHT_HAND)); | ||
| 462 | } | ||
| 463 | 437 | ||
| 464 | UserInputMapper::Input ViveControllerManager::makeInput(unsigned int button, int index) { | 438 | //availableInputs.append(Input::NamedPair(makeInput(BUTTON_A, 0), "Left Button A")); |
| 465 | return UserInputMapper::Input(_deviceID, button | (index == 0 ? LEFT_MASK : RIGHT_MASK), UserInputMapper::ChannelType::BUTTON); | 439 | //availableInputs.append(Input::NamedPair(makeInput(GRIP_BUTTON, 0), "Left Grip Button")); |
| 440 | //availableInputs.append(Input::NamedPair(makeInput(TRACKPAD_BUTTON, 0), "Left Trackpad Button")); | ||
| 441 | //availableInputs.append(Input::NamedPair(makeInput(TRIGGER_BUTTON, 0), "Left Trigger Button")); | ||
| 442 | //availableInputs.append(Input::NamedPair(makeInput(BACK_TRIGGER, 0), "Left Back Trigger")); | ||
| 443 | //availableInputs.append(Input::NamedPair(makeInput(RIGHT_HAND), "Right Hand")); | ||
| 444 | //availableInputs.append(Input::NamedPair(makeInput(BUTTON_A, 1), "Right Button A")); | ||
| 445 | //availableInputs.append(Input::NamedPair(makeInput(GRIP_BUTTON, 1), "Right Grip Button")); | ||
| 446 | //availableInputs.append(Input::NamedPair(makeInput(TRACKPAD_BUTTON, 1), "Right Trackpad Button")); | ||
| 447 | //availableInputs.append(Input::NamedPair(makeInput(TRIGGER_BUTTON, 1), "Right Trigger Button")); | ||
| 448 | //availableInputs.append(Input::NamedPair(makeInput(BACK_TRIGGER, 1), "Right Back Trigger")); | ||
| 449 | |||
| 450 | return availableInputs; | ||
| 466 | } | 451 | } |
| 467 | 452 | ||
| 468 | UserInputMapper::Input ViveControllerManager::makeInput(ViveControllerManager::JoystickAxisChannel axis, int index) { | 453 | QString ViveControllerManager::getDefaultMappingConfig() const { |
| 469 | return UserInputMapper::Input(_deviceID, axis | (index == 0 ? LEFT_MASK : RIGHT_MASK), UserInputMapper::ChannelType::AXIS); | 454 | static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/vive.json"; |
| 455 | return MAPPING_JSON; | ||
| 470 | } | 456 | } |
| 471 | 457 | ||
| 472 | UserInputMapper::Input ViveControllerManager::makeInput(JointChannel joint) { | 458 | //void ViveControllerManager::assignDefaultInputMapping(UserInputMapper& mapper) { |
| 473 | return UserInputMapper::Input(_deviceID, joint, UserInputMapper::ChannelType::POSE); | 459 | // const float JOYSTICK_MOVE_SPEED = 1.0f; |
| 474 | } | 460 | // |
| 461 | // // Left Trackpad: Movement, strafing | ||
| 462 | // mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(AXIS_Y_POS, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED); | ||
| 463 | // mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(AXIS_Y_NEG, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED); | ||
| 464 | // mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(AXIS_X_POS, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED); | ||
| 465 | // mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(AXIS_X_NEG, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED); | ||
| 466 | // | ||
| 467 | // // Right Trackpad: Vertical movement, zooming | ||
| 468 | // mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(AXIS_Y_POS, 1), makeInput(TRACKPAD_BUTTON, 1), JOYSTICK_MOVE_SPEED); | ||
| 469 | // mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(AXIS_Y_NEG, 1), makeInput(TRACKPAD_BUTTON, 1), JOYSTICK_MOVE_SPEED); | ||
| 470 | // | ||
| 471 | // // Buttons | ||
| 472 | // mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_A, 0)); | ||
| 473 | // mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_A, 1)); | ||
| 474 | // | ||
| 475 | // mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(GRIP_BUTTON, 0)); | ||
| 476 | // mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(GRIP_BUTTON, 1)); | ||
| 477 | // | ||
| 478 | // mapper.addInputChannel(UserInputMapper::LEFT_HAND_CLICK, makeInput(BACK_TRIGGER, 0)); | ||
| 479 | // mapper.addInputChannel(UserInputMapper::RIGHT_HAND_CLICK, makeInput(BACK_TRIGGER, 1)); | ||
| 480 | // | ||
| 481 | // // Hands | ||
| 482 | // mapper.addInputChannel(UserInputMapper::LEFT_HAND, makeInput(LEFT_HAND)); | ||
| 483 | // mapper.addInputChannel(UserInputMapper::RIGHT_HAND, makeInput(RIGHT_HAND)); | ||
| 484 | //} | ||
| @@ -19,35 +19,14 @@ · 18 unmodified lines | |||
| 19 | 19 | ||
| 20 | #include <model/Geometry.h> | 20 | #include <model/Geometry.h> |
| 21 | #include <gpu/Texture.h> | 21 | #include <gpu/Texture.h> |
| 22 | #include "InputDevice.h" | 22 | #include <controllers/InputDevice.h> |
| 23 | #include "InputPlugin.h" | 23 | #include "InputPlugin.h" |
| 24 | #include <RenderArgs.h> | 24 | #include <RenderArgs.h> |
| 25 | #include <render/Scene.h> | 25 | #include <render/Scene.h> |
| 26 | 26 | ||
| 27 | class ViveControllerManager : public InputPlugin, public InputDevice { | 27 | class ViveControllerManager : public InputPlugin, public controller::InputDevice { |
| 28 | Q_OBJECT | 28 | Q_OBJECT |
| 29 | public: | 29 | public: |
| 30 | enum JoystickAxisChannel { | ||
| 31 | AXIS_Y_POS = 1U << 1, | ||
| 32 | AXIS_Y_NEG = 1U << 2, | ||
| 33 | AXIS_X_POS = 1U << 3, | ||
| 34 | AXIS_X_NEG = 1U << 4, | ||
| 35 | BACK_TRIGGER = 1U << 5, | ||
| 36 | }; | ||
| 37 | |||
| 38 | enum Axis { | ||
| 39 | TRACKPAD_AXIS = 0, | ||
| 40 | TRIGGER_AXIS, | ||
| 41 | AXIS_2, | ||
| 42 | AXIS_3, | ||
| 43 | AXIS_4, | ||
| 44 | }; | ||
| 45 | |||
| 46 | enum JointChannel { | ||
| 47 | LEFT_HAND = 0, | ||
| 48 | RIGHT_HAND, | ||
| 49 | }; | ||
| 50 | |||
| 51 | ViveControllerManager(); | 30 | ViveControllerManager(); |
| 52 | 31 | ||
| 53 | // Plugin functions | 32 | // Plugin functions |
| @@ -62,8 +41,8 @@ public: · 8 unmodified lines | |||
| 62 | virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); } | 41 | virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); } |
| 63 | 42 | ||
| 64 | // Device functions | 43 | // Device functions |
| 65 | virtual void registerToUserInputMapper(UserInputMapper& mapper) override; | 44 | virtual controller::Input::NamedVector getAvailableInputs() const override; |
| 66 | virtual void assignDefaultInputMapping(UserInputMapper& mapper) override; | 45 | virtual QString getDefaultMappingConfig() const override; |
| 67 | virtual void update(float deltaTime, bool jointsCaptured) override; | 46 | virtual void update(float deltaTime, bool jointsCaptured) override; |
| 68 | virtual void focusOutEvent() override; | 47 | virtual void focusOutEvent() override; |
| 69 | 48 | ||
| @@ -71,16 +50,12 @@ public: · 1 unmodified line | |||
| 71 | 50 | ||
| 72 | void setRenderControllers(bool renderControllers) { _renderControllers = renderControllers; } | 51 | void setRenderControllers(bool renderControllers) { _renderControllers = renderControllers; } |
| 73 | 52 | ||
| 74 | UserInputMapper::Input makeInput(unsigned int button, int index); | ||
| 75 | UserInputMapper::Input makeInput(JoystickAxisChannel axis, int index); | ||
| 76 | UserInputMapper::Input makeInput(JointChannel joint); | ||
| 77 | |||
| 78 | private: | 53 | private: |
| 79 | void renderHand(UserInputMapper::PoseValue pose, gpu::Batch& batch, int index); | 54 | void renderHand(const controller::Pose& pose, gpu::Batch& batch, int sign); |
| 80 | 55 | ||
| 81 | void handleButtonEvent(uint64_t buttons, int index); | 56 | void handleButtonEvent(uint32_t button, bool pressed, bool left); |
| 82 | void handleAxisEvent(Axis axis, float x, float y, int index); | 57 | void handleAxisEvent(uint32_t axis, float x, float y, bool left); |
| 83 | void handlePoseEvent(const mat4& mat, int index); | 58 | void handlePoseEvent(const mat4& mat, bool left); |
| 84 | 59 | ||
| 85 | int _trackedControllers; | 60 | int _trackedControllers; |
| 86 | 61 | ||
| @@ -94,6 +69,9 @@ private: · 7 unmodified lines | |||
| 94 | bool _renderControllers; | 69 | bool _renderControllers; |
| 95 | 70 | ||
| 96 | static const QString NAME; | 71 | static const QString NAME; |
| 72 | |||
| 73 | bool _registeredWithInputMapper { false }; | ||
| 74 | |||
| 97 | }; | 75 | }; |
| 98 | 76 | ||
| 99 | #endif // hifi__ViveControllerManager | 77 | #endif // hifi__ViveControllerManager |
| @@ -89,7 +89,6 @@ AssetRequest* AssetClient::createRequest(const QString& hash, const QString& ext · 88 unmodified lines | |||
| 89 | 89 | ||
| 90 | // Move to the AssetClient thread in case we are not currently on that thread (which will usually be the case) | 90 | // Move to the AssetClient thread in case we are not currently on that thread (which will usually be the case) |
| 91 | request->moveToThread(thread()); | 91 | request->moveToThread(thread()); |
| 92 | request->setParent(this); | ||
| 93 | 92 | ||
| 94 | return request; | 93 | return request; |
| 95 | } else { | 94 | } else { |
| @@ -105,7 +104,6 @@ AssetUpload* AssetClient::createUpload(const QString& filename) { · 9 unmodified lines | |||
| 105 | auto upload = new AssetUpload(filename); | 104 | auto upload = new AssetUpload(filename); |
| 106 | 105 | ||
| 107 | upload->moveToThread(thread()); | 106 | upload->moveToThread(thread()); |
| 108 | upload->setParent(this); | ||
| 109 | 107 | ||
| 110 | return upload; | 108 | return upload; |
| 111 | } else { | 109 | } else { |
| @@ -118,7 +116,6 @@ AssetUpload* AssetClient::createUpload(const QByteArray& data, const QString& ex · 6 unmodified lines | |||
| 118 | auto upload = new AssetUpload(data, extension); | 116 | auto upload = new AssetUpload(data, extension); |
| 119 | 117 | ||
| 120 | upload->moveToThread(thread()); | 118 | upload->moveToThread(thread()); |
| 121 | upload->setParent(this); | ||
| 122 | 119 | ||
| 123 | return upload; | 120 | return upload; |
| 124 | } else { | 121 | } else { |
| @@ -38,7 +38,7 @@ PacketVersion versionForPacketType(PacketType packetType) { · 37 unmodified lines | |||
| 38 | case PacketType::EntityAdd: | 38 | case PacketType::EntityAdd: |
| 39 | case PacketType::EntityEdit: | 39 | case PacketType::EntityEdit: |
| 40 | case PacketType::EntityData: | 40 | case PacketType::EntityData: |
| 41 | return VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP; | 41 | return VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP_BIS; |
| 42 | case PacketType::AvatarData: | 42 | case PacketType::AvatarData: |
| 43 | case PacketType::BulkAvatarData: | 43 | case PacketType::BulkAvatarData: |
| 44 | default: | 44 | default: |
| @@ -144,5 +144,6 @@ const PacketVersion VERSION_ENTITIES_PARTICLE_ELLIPSOID_EMITTER = 44; · 143 unmodified lines | |||
| 144 | const PacketVersion VERSION_ENTITIES_PROTOCOL_CHANNELS = 45; | 144 | const PacketVersion VERSION_ENTITIES_PROTOCOL_CHANNELS = 45; |
| 145 | const PacketVersion VERSION_ENTITIES_ANIMATION_PROPERTIES_GROUP = 46; | 145 | const PacketVersion VERSION_ENTITIES_ANIMATION_PROPERTIES_GROUP = 46; |
| 146 | const PacketVersion VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP = 47; | 146 | const PacketVersion VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP = 47; |
| 147 | const PacketVersion VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP_BIS = 48; | ||
| 147 | 148 | ||
| 148 | #endif // hifi_PacketHeaders_h | 149 | #endif // hifi_PacketHeaders_h |
| @@ -14,11 +14,11 @@ · 13 unmodified lines | |||
| 14 | #include "PhysicsCollisionGroups.h" | 14 | #include "PhysicsCollisionGroups.h" |
| 15 | 15 | ||
| 16 | bool CharacterController::needsRemoval() const { | 16 | bool CharacterController::needsRemoval() const { |
| 17 | return (bool)(_pendingFlags & PENDING_FLAG_REMOVE_FROM_SIMULATION); | 17 | return ((_pendingFlags & PENDING_FLAG_REMOVE_FROM_SIMULATION) == PENDING_FLAG_REMOVE_FROM_SIMULATION); |
| 18 | } | 18 | } |
| 19 | 19 | ||
| 20 | bool CharacterController::needsAddition() const { | 20 | bool CharacterController::needsAddition() const { |
| 21 | return (bool)(_pendingFlags & PENDING_FLAG_ADD_TO_SIMULATION); | 21 | return ((_pendingFlags & PENDING_FLAG_ADD_TO_SIMULATION) == PENDING_FLAG_ADD_TO_SIMULATION); |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | void CharacterController::setDynamicsWorld(btDynamicsWorld* world) { | 24 | void CharacterController::setDynamicsWorld(btDynamicsWorld* world) { |
| @@ -241,7 +241,7 @@ void PhysicsEngine::stepSimulation() { · 240 unmodified lines | |||
| 241 | float timeStep = btMin(dt, MAX_TIMESTEP); | 241 | float timeStep = btMin(dt, MAX_TIMESTEP); |
| 242 | 242 | ||
| 243 | if (_myAvatarController) { | 243 | if (_myAvatarController) { |
| 244 | // ADEBUG TODO: move this stuff outside and in front of stepSimulation, because | 244 | // ADEBUG TODO: move this stuff outside and in front of stepSimulation, because |
| 245 | // the updateShapeIfNecessary() call needs info from MyAvatar and should | 245 | // the updateShapeIfNecessary() call needs info from MyAvatar and should |
| 246 | // be done on the main thread during the pre-simulation stuff | 246 | // be done on the main thread during the pre-simulation stuff |
| 247 | if (_myAvatarController->needsRemoval()) { | 247 | if (_myAvatarController->needsRemoval()) { |
| @@ -263,7 +263,8 @@ void PhysicsEngine::stepSimulation() { · 15 unmodified lines | |||
| 263 | updateContactMap(); | 263 | updateContactMap(); |
| 264 | }; | 264 | }; |
| 265 | 265 | ||
| 266 | int numSubsteps = _dynamicsWorld->stepSimulation(timeStep, PHYSICS_ENGINE_MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP, onSubStep); | 266 | int numSubsteps = _dynamicsWorld->stepSimulationWithSubstepCallback(timeStep, PHYSICS_ENGINE_MAX_NUM_SUBSTEPS, |
| 267 | PHYSICS_ENGINE_FIXED_SUBSTEP, onSubStep); | ||
| 267 | if (numSubsteps > 0) { | 268 | if (numSubsteps > 0) { |
| 268 | BT_PROFILE("postSimulation"); | 269 | BT_PROFILE("postSimulation"); |
| 269 | _numSubsteps += (uint32_t)numSubsteps; | 270 | _numSubsteps += (uint32_t)numSubsteps; |
| @@ -27,8 +27,9 @@ ThreadSafeDynamicsWorld::ThreadSafeDynamicsWorld( · 26 unmodified lines | |||
| 27 | : btDiscreteDynamicsWorld(dispatcher, pairCache, constraintSolver, collisionConfiguration) { | 27 | : btDiscreteDynamicsWorld(dispatcher, pairCache, constraintSolver, collisionConfiguration) { |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | int ThreadSafeDynamicsWorld::stepSimulation( btScalar timeStep, int maxSubSteps, btScalar fixedTimeStep, SubStepCallback onSubStep) { | 30 | int ThreadSafeDynamicsWorld::stepSimulationWithSubstepCallback(btScalar timeStep, int maxSubSteps, |
| 31 | BT_PROFILE("stepSimulation"); | 31 | btScalar fixedTimeStep, SubStepCallback onSubStep) { |
| 32 | BT_PROFILE("stepSimulationWithSubstepCallback"); | ||
| 32 | int subSteps = 0; | 33 | int subSteps = 0; |
| 33 | if (maxSubSteps) { | 34 | if (maxSubSteps) { |
| 34 | //fixed timestep with interpolation | 35 | //fixed timestep with interpolation |
| @@ -37,8 +37,9 @@ public: · 36 unmodified lines | |||
| 37 | btConstraintSolver* constraintSolver, | 37 | btConstraintSolver* constraintSolver, |
| 38 | btCollisionConfiguration* collisionConfiguration); | 38 | btCollisionConfiguration* collisionConfiguration); |
| 39 | 39 | ||
| 40 | // virtual overrides from btDiscreteDynamicsWorld | 40 | int stepSimulationWithSubstepCallback(btScalar timeStep, int maxSubSteps = 1, |
| 41 | int stepSimulation( btScalar timeStep, int maxSubSteps=1, btScalar fixedTimeStep=btScalar(1.)/btScalar(60.), SubStepCallback onSubStep = []() { }); | 41 | btScalar fixedTimeStep = btScalar(1.)/btScalar(60.), |
| 42 | SubStepCallback onSubStep = []() { }); | ||
| 42 | void synchronizeMotionStates(); | 43 | void synchronizeMotionStates(); |
| 43 | 44 | ||
| 44 | // btDiscreteDynamicsWorld::m_localTime is the portion of real-time that has not yet been simulated | 45 | // btDiscreteDynamicsWorld::m_localTime is the portion of real-time that has not yet been simulated |
| @@ -33,9 +33,18 @@ public: · 32 unmodified lines | |||
| 33 | virtual void deinit(); | 33 | virtual void deinit(); |
| 34 | 34 | ||
| 35 | /// Called when a plugin is being activated for use. May be called multiple times. | 35 | /// Called when a plugin is being activated for use. May be called multiple times. |
| 36 | virtual void activate() = 0; | 36 | virtual void activate() { |
| 37 | _active = true; | ||
| 38 | } | ||
| 39 | |||
| 37 | /// Called when a plugin is no longer being used. May be called multiple times. | 40 | /// Called when a plugin is no longer being used. May be called multiple times. |
| 38 | virtual void deactivate() = 0; | 41 | virtual void deactivate() { |
| 42 | _active = false; | ||
| 43 | } | ||
| 44 | |||
| 45 | virtual bool isActive() { | ||
| 46 | return _active; | ||
| 47 | } | ||
| 39 | 48 | ||
| 40 | /** | 49 | /** |
| 41 | * Called by the application during it's idle phase. If the plugin needs to do | 50 | * Called by the application during it's idle phase. If the plugin needs to do |
| @@ -48,6 +57,7 @@ public: · 6 unmodified lines | |||
| 48 | virtual void loadSettings() {} | 57 | virtual void loadSettings() {} |
| 49 | 58 | ||
| 50 | protected: | 59 | protected: |
| 60 | bool _active{ false }; | ||
| 51 | static PluginContainer* CONTAINER; | 61 | static PluginContainer* CONTAINER; |
| 52 | static QString UNKNOWN_PLUGIN_ID; | 62 | static QString UNKNOWN_PLUGIN_ID; |
| 53 | 63 | ||
| @@ -1,3 +1,3 @@ | |||
| 1 | set(TARGET_NAME script-engine) | 1 | set(TARGET_NAME script-engine) |
| 2 | setup_hifi_library(Gui Network Script WebSockets Widgets) | 2 | setup_hifi_library(Gui Network Script WebSockets Widgets) |
| 3 | link_hifi_libraries(shared networking octree gpu procedural model model-networking fbx entities animation audio physics) | 3 | link_hifi_libraries(shared networking octree gpu procedural model model-networking fbx entities controllers animation audio physics) |
| @@ -1,125 +0,0 @@ | |||
| 1 | // | ||
| 2 | // AbstractControllerScriptingInterface.h | ||
| 3 | // libraries/script-engine/src | ||
| 4 | // | ||
| 5 | // Created by Brad Hefta-Gaub on 12/17/13. | ||
| 6 | // Copyright 2013 High Fidelity, Inc. | ||
| 7 | // | ||
| 8 | // Distributed under the Apache License, Version 2.0. | ||
| 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 10 | // | ||
| 11 | |||
| 12 | #ifndef hifi_AbstractControllerScriptingInterface_h | ||
| 13 | #define hifi_AbstractControllerScriptingInterface_h | ||
| 14 | |||
| 15 | #include <QtCore/QObject> | ||
| 16 | |||
| 17 | #include <glm/glm.hpp> | ||
| 18 | #include <glm/gtc/quaternion.hpp> | ||
| 19 | |||
| 20 | #include "HFActionEvent.h" | ||
| 21 | #include "KeyEvent.h" | ||
| 22 | #include "MouseEvent.h" | ||
| 23 | #include "SpatialEvent.h" | ||
| 24 | #include "TouchEvent.h" | ||
| 25 | #include "WheelEvent.h" | ||
| 26 | |||
| 27 | class ScriptEngine; | ||
| 28 | |||
| 29 | class AbstractInputController : public QObject { | ||
| 30 | Q_OBJECT | ||
| 31 | |||
| 32 | public: | ||
| 33 | typedef unsigned int Key; | ||
| 34 | |||
| 35 | virtual void update() = 0; | ||
| 36 | |||
| 37 | virtual Key getKey() const = 0; | ||
| 38 | |||
| 39 | public slots: | ||
| 40 | |||
| 41 | virtual bool isActive() const = 0; | ||
| 42 | virtual glm::vec3 getAbsTranslation() const = 0; | ||
| 43 | virtual glm::quat getAbsRotation() const = 0; | ||
| 44 | virtual glm::vec3 getLocTranslation() const = 0; | ||
| 45 | virtual glm::quat getLocRotation() const = 0; | ||
| 46 | |||
| 47 | signals: | ||
| 48 | void spatialEvent(const SpatialEvent& event); | ||
| 49 | |||
| 50 | }; | ||
| 51 | |||
| 52 | /// handles scripting of input controller commands from JS | ||
| 53 | class AbstractControllerScriptingInterface : public QObject { | ||
| 54 | Q_OBJECT | ||
| 55 | |||
| 56 | public slots: | ||
| 57 | virtual void registerControllerTypes(ScriptEngine* engine) = 0; | ||
| 58 | |||
| 59 | virtual bool isPrimaryButtonPressed() const = 0; | ||
| 60 | virtual glm::vec2 getPrimaryJoystickPosition() const = 0; | ||
| 61 | |||
| 62 | virtual int getNumberOfButtons() const = 0; | ||
| 63 | virtual bool isButtonPressed(int buttonIndex) const = 0; | ||
| 64 | |||
| 65 | virtual int getNumberOfTriggers() const = 0; | ||
| 66 | virtual float getTriggerValue(int triggerIndex) const = 0; | ||
| 67 | |||
| 68 | virtual int getNumberOfJoysticks() const = 0; | ||
| 69 | virtual glm::vec2 getJoystickPosition(int joystickIndex) const = 0; | ||
| 70 | |||
| 71 | virtual int getNumberOfSpatialControls() const = 0; | ||
| 72 | virtual glm::vec3 getSpatialControlPosition(int controlIndex) const = 0; | ||
| 73 | virtual glm::vec3 getSpatialControlVelocity(int controlIndex) const = 0; | ||
| 74 | virtual glm::vec3 getSpatialControlNormal(int controlIndex) const = 0; | ||
| 75 | virtual glm::quat getSpatialControlRawRotation(int controlIndex) const = 0; | ||
| 76 | |||
| 77 | virtual void captureKeyEvents(const KeyEvent& event) = 0; | ||
| 78 | virtual void releaseKeyEvents(const KeyEvent& event) = 0; | ||
| 79 | |||
| 80 | virtual void captureMouseEvents() = 0; | ||
| 81 | virtual void releaseMouseEvents() = 0; | ||
| 82 | |||
| 83 | virtual void captureTouchEvents() = 0; | ||
| 84 | virtual void releaseTouchEvents() = 0; | ||
| 85 | |||
| 86 | virtual void captureWheelEvents() = 0; | ||
| 87 | virtual void releaseWheelEvents() = 0; | ||
| 88 | |||
| 89 | virtual void captureActionEvents() = 0; | ||
| 90 | virtual void releaseActionEvents() = 0; | ||
| 91 | |||
| 92 | virtual void captureJoystick(int joystickIndex) = 0; | ||
| 93 | virtual void releaseJoystick(int joystickIndex) = 0; | ||
| 94 | |||
| 95 | virtual glm::vec2 getViewportDimensions() const = 0; | ||
| 96 | |||
| 97 | |||
| 98 | virtual AbstractInputController* createInputController( const QString& category, const QString& tracker ) = 0; | ||
| 99 | |||
| 100 | signals: | ||
| 101 | void keyPressEvent(const KeyEvent& event); | ||
| 102 | void keyReleaseEvent(const KeyEvent& event); | ||
| 103 | |||
| 104 | void actionStartEvent(const HFActionEvent& event); | ||
| 105 | void actionEndEvent(const HFActionEvent& event); | ||
| 106 | |||
| 107 | void backStartEvent(); | ||
| 108 | void backEndEvent(); | ||
| 109 | |||
| 110 | void mouseMoveEvent(const MouseEvent& event, unsigned int deviceID = 0); | ||
| 111 | void mousePressEvent(const MouseEvent& event, unsigned int deviceID = 0); | ||
| 112 | void mouseDoublePressEvent(const MouseEvent& event, unsigned int deviceID = 0); | ||
| 113 | void mouseReleaseEvent(const MouseEvent& event, unsigned int deviceID = 0); | ||
| 114 | |||
| 115 | void touchBeginEvent(const TouchEvent& event); | ||
| 116 | void touchEndEvent(const TouchEvent& event); | ||
| 117 | void touchUpdateEvent(const TouchEvent& event); | ||
| 118 | |||
| 119 | void wheelEvent(const WheelEvent& event); | ||
| 120 | |||
| 121 | void actionEvent(int action, float state); | ||
| 122 | |||
| 123 | }; | ||
| 124 | |||
| 125 | #endif // hifi_AbstractControllerScriptingInterface_h | ||
| @@ -12,21 +12,13 @@ · 11 unmodified lines | |||
| 12 | #ifndef hifi_AbstractScriptingServicesInterface_h | 12 | #ifndef hifi_AbstractScriptingServicesInterface_h |
| 13 | #define hifi_AbstractScriptingServicesInterface_h | 13 | #define hifi_AbstractScriptingServicesInterface_h |
| 14 | 14 | ||
| 15 | class AbstractControllerScriptingInterface; | ||
| 16 | class Transform; | ||
| 17 | class ScriptEngine; | 15 | class ScriptEngine; |
| 18 | class QThread; | ||
| 19 | 16 | ||
| 20 | /// Interface provided by Application to other objects that need access to scripting services of the application | 17 | /// Interface provided by Application to other objects that need access to scripting services of the application |
| 21 | class AbstractScriptingServicesInterface { | 18 | class AbstractScriptingServicesInterface { |
| 22 | public: | 19 | public: |
| 23 | |||
| 24 | /// Returns the controller interface for the application | ||
| 25 | virtual AbstractControllerScriptingInterface* getControllerScriptingInterface() = 0; | ||
| 26 | |||
| 27 | /// Registers application specific services with a script engine. | 20 | /// Registers application specific services with a script engine. |
| 28 | virtual void registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine) = 0; | 21 | virtual void registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine) = 0; |
| 29 | |||
| 30 | }; | 22 | }; |
| 31 | 23 | ||
| 32 | 24 | ||
| @@ -28,7 +28,9 @@ · 27 unmodified lines | |||
| 28 | #include <udt/PacketHeaders.h> | 28 | #include <udt/PacketHeaders.h> |
| 29 | #include <UUID.h> | 29 | #include <UUID.h> |
| 30 | 30 | ||
| 31 | #include "AnimationObject.h" | 31 | #include <controllers/ScriptingInterface.h> |
| 32 | #include <AnimationObject.h> | ||
| 33 | |||
| 32 | #include "ArrayBufferViewClass.h" | 34 | #include "ArrayBufferViewClass.h" |
| 33 | #include "BatchLoader.h" | 35 | #include "BatchLoader.h" |
| 34 | #include "DataViewClass.h" | 36 | #include "DataViewClass.h" |
| @@ -76,13 +78,16 @@ void avatarDataFromScriptValue(const QScriptValue &object, AvatarData* &out) { · 41 unmodified lines | |||
| 76 | out = qobject_cast<AvatarData*>(object.toQObject()); | 78 | out = qobject_cast<AvatarData*>(object.toQObject()); |
| 77 | } | 79 | } |
| 78 | 80 | ||
| 79 | QScriptValue inputControllerToScriptValue(QScriptEngine *engine, AbstractInputController* const &in) { | 81 | QScriptValue inputControllerToScriptValue(QScriptEngine *engine, controller::InputController* const &in) { |
| 80 | return engine->newQObject(in); | 82 | return engine->newQObject(in); |
| 81 | } | 83 | } |
| 82 | void inputControllerFromScriptValue(const QScriptValue &object, AbstractInputController* &out) { | 84 | |
| 83 | out = qobject_cast<AbstractInputController*>(object.toQObject()); | 85 | void inputControllerFromScriptValue(const QScriptValue &object, controller::InputController* &out) { |
| 86 | out = qobject_cast<controller::InputController*>(object.toQObject()); | ||
| 84 | } | 87 | } |
| 85 | 88 | ||
| 89 | |||
| 90 | |||
| 86 | static bool hasCorrectSyntax(const QScriptProgram& program) { | 91 | static bool hasCorrectSyntax(const QScriptProgram& program) { |
| 87 | const auto syntaxCheck = QScriptEngine::checkSyntax(program.sourceCode()); | 92 | const auto syntaxCheck = QScriptEngine::checkSyntax(program.sourceCode()); |
| 88 | if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) { | 93 | if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) { |
| @@ -114,16 +119,13 @@ static bool hadUncaughtExceptions(QScriptEngine& engine, const QString& fileName · 25 unmodified lines | |||
| 114 | return false; | 119 | return false; |
| 115 | } | 120 | } |
| 116 | 121 | ||
| 117 | ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString, | 122 | ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString, bool wantSignals) : |
| 118 | AbstractControllerScriptingInterface* controllerScriptingInterface, bool wantSignals) : | ||
| 119 | |||
| 120 | _scriptContents(scriptContents), | 123 | _scriptContents(scriptContents), |
| 121 | _isFinished(false), | 124 | _isFinished(false), |
| 122 | _isRunning(false), | 125 | _isRunning(false), |
| 123 | _isInitialized(false), | 126 | _isInitialized(false), |
| 124 | _timerFunctionMap(), | 127 | _timerFunctionMap(), |
| 125 | _wantSignals(wantSignals), | 128 | _wantSignals(wantSignals), |
| 126 | _controllerScriptingInterface(controllerScriptingInterface), | ||
| 127 | _fileNameString(fileNameString), | 129 | _fileNameString(fileNameString), |
| 128 | _isUserLoaded(false), | 130 | _isUserLoaded(false), |
| 129 | _isReloading(false), | 131 | _isReloading(false), |
| @@ -285,6 +287,27 @@ void ScriptEngine::errorInLoadingScript(const QUrl& url) { · 155 unmodified lines | |||
| 285 | } | 287 | } |
| 286 | } | 288 | } |
| 287 | 289 | ||
| 290 | // Even though we never pass AnimVariantMap directly to and from javascript, the queued invokeMethod of | ||
| 291 | // callAnimationStateHandler requires that the type be registered. | ||
| 292 | // These two are meaningful, if we ever do want to use them... | ||
| 293 | static QScriptValue animVarMapToScriptValue(QScriptEngine* engine, const AnimVariantMap& parameters) { | ||
| 294 | QStringList unused; | ||
| 295 | return parameters.animVariantMapToScriptValue(engine, unused, false); | ||
| 296 | } | ||
| 297 | static void animVarMapFromScriptValue(const QScriptValue& value, AnimVariantMap& parameters) { | ||
| 298 | parameters.animVariantMapFromScriptValue(value); | ||
| 299 | } | ||
| 300 | // ... while these two are not. But none of the four are ever used. | ||
| 301 | static QScriptValue resultHandlerToScriptValue(QScriptEngine* engine, const AnimVariantResultHandler& resultHandler) { | ||
| 302 | qCCritical(scriptengine) << "Attempt to marshall result handler to javascript"; | ||
| 303 | assert(false); | ||
| 304 | return QScriptValue(); | ||
| 305 | } | ||
| 306 | static void resultHandlerFromScriptValue(const QScriptValue& value, AnimVariantResultHandler& resultHandler) { | ||
| 307 | qCCritical(scriptengine) << "Attempt to marshall result handler from javascript"; | ||
| 308 | assert(false); | ||
| 309 | } | ||
| 310 | |||
| 288 | void ScriptEngine::init() { | 311 | void ScriptEngine::init() { |
| 289 | if (_isInitialized) { | 312 | if (_isInitialized) { |
| 290 | return; // only initialize once | 313 | return; // only initialize once |
| @@ -337,27 +360,26 @@ void ScriptEngine::init() { · 46 unmodified lines | |||
| 337 | 360 | ||
| 338 | registerGlobalObject("Script", this); | 361 | registerGlobalObject("Script", this); |
| 339 | registerGlobalObject("Audio", &AudioScriptingInterface::getInstance()); | 362 | registerGlobalObject("Audio", &AudioScriptingInterface::getInstance()); |
| 340 | registerGlobalObject("Controller", _controllerScriptingInterface); | ||
| 341 | registerGlobalObject("Entities", entityScriptingInterface.data()); | 363 | registerGlobalObject("Entities", entityScriptingInterface.data()); |
| 342 | registerGlobalObject("Quat", &_quatLibrary); | 364 | registerGlobalObject("Quat", &_quatLibrary); |
| 343 | registerGlobalObject("Vec3", &_vec3Library); | 365 | registerGlobalObject("Vec3", &_vec3Library); |
| 344 | registerGlobalObject("Uuid", &_uuidLibrary); | 366 | registerGlobalObject("Uuid", &_uuidLibrary); |
| 345 | registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCache>().data()); | 367 | registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCache>().data()); |
| 368 | qScriptRegisterMetaType(this, animVarMapToScriptValue, animVarMapFromScriptValue); | ||
| 369 | qScriptRegisterMetaType(this, resultHandlerToScriptValue, resultHandlerFromScriptValue); | ||
| 346 | 370 | ||
| 347 | // constants | 371 | // constants |
| 348 | globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE))); | 372 | globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE))); |
| 349 | 373 | ||
| 350 | if (_controllerScriptingInterface) { | 374 | auto scriptingInterface = DependencyManager::get<controller::ScriptingInterface>(); |
| 351 | _controllerScriptingInterface->registerControllerTypes(this); | 375 | registerGlobalObject("Controller", scriptingInterface.data()); |
| 352 | } | 376 | UserInputMapper::registerControllerTypes(this); |
| 353 | |||
| 354 | |||
| 355 | } | 377 | } |
| 356 | 378 | ||
| 357 | void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) { | 379 | void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) { |
| 358 | if (QThread::currentThread() != thread()) { | 380 | if (QThread::currentThread() != thread()) { |
| 359 | #ifdef THREAD_DEBUGGING | 381 | #ifdef THREAD_DEBUGGING |
| 360 | qDebug() << "*** WARNING *** ScriptEngine::registerValue() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name; | 382 | qDebug() << "*** WARNING *** ScriptEngine::registerValue() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]"; |
| 361 | #endif | 383 | #endif |
| 362 | QMetaObject::invokeMethod(this, "registerValue", | 384 | QMetaObject::invokeMethod(this, "registerValue", |
| 363 | Q_ARG(const QString&, valueName), | 385 | Q_ARG(const QString&, valueName), |
| @@ -743,6 +765,27 @@ void ScriptEngine::stop() { · 379 unmodified lines | |||
| 743 | } | 765 | } |
| 744 | } | 766 | } |
| 745 | 767 | ||
| 768 | // Other threads can invoke this through invokeMethod, which causes the callback to be asynchronously executed in this script's thread. | ||
| 769 | void ScriptEngine::callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters, QStringList names, bool useNames, AnimVariantResultHandler resultHandler) { | ||
| 770 | if (QThread::currentThread() != thread()) { | ||
| 771 | #ifdef THREAD_DEBUGGING | ||
| 772 | qDebug() << "*** WARNING *** ScriptEngine::callAnimationStateHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] name:" << name; | ||
| 773 | #endif | ||
| 774 | QMetaObject::invokeMethod(this, "callAnimationStateHandler", | ||
| 775 | Q_ARG(QScriptValue, callback), | ||
| 776 | Q_ARG(AnimVariantMap, parameters), | ||
| 777 | Q_ARG(QStringList, names), | ||
| 778 | Q_ARG(bool, useNames), | ||
| 779 | Q_ARG(AnimVariantResultHandler, resultHandler)); | ||
| 780 | return; | ||
| 781 | } | ||
| 782 | QScriptValue javascriptParameters = parameters.animVariantMapToScriptValue(this, names, useNames); | ||
| 783 | QScriptValueList callingArguments; | ||
| 784 | callingArguments << javascriptParameters; | ||
| 785 | QScriptValue result = callback.call(QScriptValue(), callingArguments); | ||
| 786 | resultHandler(result); | ||
| 787 | } | ||
| 788 | |||
| 746 | void ScriptEngine::timerFired() { | 789 | void ScriptEngine::timerFired() { |
| 747 | QTimer* callingTimer = reinterpret_cast<QTimer*>(sender()); | 790 | QTimer* callingTimer = reinterpret_cast<QTimer*>(sender()); |
| 748 | QScriptValue timerFunction = _timerFunctionMap.value(callingTimer); | 791 | QScriptValue timerFunction = _timerFunctionMap.value(callingTimer); |
| @@ -928,9 +971,8 @@ void ScriptEngine::forwardHandlerCall(const EntityItemID& entityID, const QStrin · 179 unmodified lines | |||
| 928 | if (QThread::currentThread() != thread()) { | 971 | if (QThread::currentThread() != thread()) { |
| 929 | qDebug() << "*** ERROR *** ScriptEngine::forwardHandlerCall() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]"; | 972 | qDebug() << "*** ERROR *** ScriptEngine::forwardHandlerCall() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]"; |
| 930 | assert(false); | 973 | assert(false); |
| 931 | return; | 974 | return ; |
| 932 | } | 975 | } |
| 933 | |||
| 934 | if (!_registeredHandlers.contains(entityID)) { | 976 | if (!_registeredHandlers.contains(entityID)) { |
| 935 | return; | 977 | return; |
| 936 | } | 978 | } |
| @@ -21,13 +21,14 @@ · 20 unmodified lines | |||
| 21 | #include <QtScript/QScriptEngine> | 21 | #include <QtScript/QScriptEngine> |
| 22 | 22 | ||
| 23 | #include <AnimationCache.h> | 23 | #include <AnimationCache.h> |
| 24 | #include <AnimVariant.h> | ||
| 24 | #include <AvatarData.h> | 25 | #include <AvatarData.h> |
| 25 | #include <AvatarHashMap.h> | 26 | #include <AvatarHashMap.h> |
| 26 | #include <LimitedNodeList.h> | 27 | #include <LimitedNodeList.h> |
| 27 | #include <EntityItemID.h> | 28 | #include <EntityItemID.h> |
| 28 | #include <EntitiesScriptEngineProvider.h> | 29 | #include <EntitiesScriptEngineProvider.h> |
| 29 | 30 | ||
| 30 | #include "AbstractControllerScriptingInterface.h" | 31 | #include "MouseEvent.h" |
| 31 | #include "ArrayBufferClass.h" | 32 | #include "ArrayBufferClass.h" |
| 32 | #include "AudioScriptingInterface.h" | 33 | #include "AudioScriptingInterface.h" |
| 33 | #include "Quat.h" | 34 | #include "Quat.h" |
| @@ -53,7 +54,6 @@ class ScriptEngine : public QScriptEngine, public ScriptUser, public EntitiesScr · 19 unmodified lines | |||
| 53 | public: | 54 | public: |
| 54 | ScriptEngine(const QString& scriptContents = NO_SCRIPT, | 55 | ScriptEngine(const QString& scriptContents = NO_SCRIPT, |
| 55 | const QString& fileNameString = QString(""), | 56 | const QString& fileNameString = QString(""), |
| 56 | AbstractControllerScriptingInterface* controllerScriptingInterface = NULL, | ||
| 57 | bool wantSignals = true); | 57 | bool wantSignals = true); |
| 58 | 58 | ||
| 59 | ~ScriptEngine(); | 59 | ~ScriptEngine(); |
| @@ -142,6 +142,9 @@ public: · 82 unmodified lines | |||
| 142 | // NOTE - this is used by the TypedArray implemetation. we need to review this for thread safety | 142 | // NOTE - this is used by the TypedArray implemetation. we need to review this for thread safety |
| 143 | ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; } | 143 | ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; } |
| 144 | 144 | ||
| 145 | public slots: | ||
| 146 | void callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters, QStringList names, bool useNames, AnimVariantResultHandler resultHandler); | ||
| 147 | |||
| 145 | signals: | 148 | signals: |
| 146 | void scriptLoaded(const QString& scriptFilename); | 149 | void scriptLoaded(const QString& scriptFilename); |
| 147 | void errorLoadingScript(const QString& scriptFilename); | 150 | void errorLoadingScript(const QString& scriptFilename); |
| @@ -181,8 +184,7 @@ private: · 33 unmodified lines | |||
| 181 | 184 | ||
| 182 | QObject* setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot); | 185 | QObject* setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot); |
| 183 | void stopTimer(QTimer* timer); | 186 | void stopTimer(QTimer* timer); |
| 184 | 187 | ||
| 185 | AbstractControllerScriptingInterface* _controllerScriptingInterface; | ||
| 186 | QString _fileNameString; | 188 | QString _fileNameString; |
| 187 | Quat _quatLibrary; | 189 | Quat _quatLibrary; |
| 188 | Vec3 _vec3Library; | 190 | Vec3 _vec3Library; |
| @@ -23,6 +23,25 @@ · 22 unmodified lines | |||
| 23 | /// Scriptable interface a Vec3ernion helper class object. Used exclusively in the JavaScript API | 23 | /// Scriptable interface a Vec3ernion helper class object. Used exclusively in the JavaScript API |
| 24 | class Vec3 : public QObject { | 24 | class Vec3 : public QObject { |
| 25 | Q_OBJECT | 25 | Q_OBJECT |
| 26 | Q_PROPERTY(glm::vec3 UNIT_X READ UNIT_X CONSTANT) | ||
| 27 | Q_PROPERTY(glm::vec3 UNIT_Y READ UNIT_Y CONSTANT) | ||
| 28 | Q_PROPERTY(glm::vec3 UNIT_Z READ UNIT_Z CONSTANT) | ||
| 29 | Q_PROPERTY(glm::vec3 UNIT_NEG_X READ UNIT_NEG_X CONSTANT) | ||
| 30 | Q_PROPERTY(glm::vec3 UNIT_NEG_Y READ UNIT_NEG_Y CONSTANT) | ||
| 31 | Q_PROPERTY(glm::vec3 UNIT_NEG_Z READ UNIT_NEG_Z CONSTANT) | ||
| 32 | Q_PROPERTY(glm::vec3 UNIT_XY READ UNIT_XY CONSTANT) | ||
| 33 | Q_PROPERTY(glm::vec3 UNIT_XZ READ UNIT_XZ CONSTANT) | ||
| 34 | Q_PROPERTY(glm::vec3 UNIT_YZ READ UNIT_YZ CONSTANT) | ||
| 35 | Q_PROPERTY(glm::vec3 UNIT_XYZ READ UNIT_XYZ CONSTANT) | ||
| 36 | Q_PROPERTY(glm::vec3 FLOAT_MAX READ FLOAT_MAX CONSTANT) | ||
| 37 | Q_PROPERTY(glm::vec3 FLOAT_MIN READ FLOAT_MIN CONSTANT) | ||
| 38 | Q_PROPERTY(glm::vec3 ZERO READ ZERO CONSTANT) | ||
| 39 | Q_PROPERTY(glm::vec3 ONE READ ONE CONSTANT) | ||
| 40 | Q_PROPERTY(glm::vec3 TWO READ TWO CONSTANT) | ||
| 41 | Q_PROPERTY(glm::vec3 HALF READ HALF CONSTANT) | ||
| 42 | Q_PROPERTY(glm::vec3 RIGHT READ RIGHT CONSTANT) | ||
| 43 | Q_PROPERTY(glm::vec3 UP READ UP CONSTANT) | ||
| 44 | Q_PROPERTY(glm::vec3 FRONT READ FRONT CONSTANT) | ||
| 26 | 45 | ||
| 27 | public slots: | 46 | public slots: |
| 28 | glm::vec3 reflect(const glm::vec3& v1, const glm::vec3& v2) { return glm::reflect(v1, v2); } | 47 | glm::vec3 reflect(const glm::vec3& v1, const glm::vec3& v2) { return glm::reflect(v1, v2); } |
| @@ -30,6 +49,7 @@ public slots: · 1 unmodified line | |||
| 30 | float dot(const glm::vec3& v1, const glm::vec3& v2) { return glm::dot(v1, v2); } | 49 | float dot(const glm::vec3& v1, const glm::vec3& v2) { return glm::dot(v1, v2); } |
| 31 | glm::vec3 multiply(const glm::vec3& v1, float f) { return v1 * f; } | 50 | glm::vec3 multiply(const glm::vec3& v1, float f) { return v1 * f; } |
| 32 | glm::vec3 multiply(float f, const glm::vec3& v1) { return v1 * f; } | 51 | glm::vec3 multiply(float f, const glm::vec3& v1) { return v1 * f; } |
| 52 | glm::vec3 multiplyVbyV(const glm::vec3& v1, const glm::vec3& v2) { return v1 * v2; } | ||
| 33 | glm::vec3 multiplyQbyV(const glm::quat& q, const glm::vec3& v) { return q * v; } | 53 | glm::vec3 multiplyQbyV(const glm::quat& q, const glm::vec3& v) { return q * v; } |
| 34 | glm::vec3 sum(const glm::vec3& v1, const glm::vec3& v2) { return v1 + v2; } | 54 | glm::vec3 sum(const glm::vec3& v1, const glm::vec3& v2) { return v1 + v2; } |
| 35 | glm::vec3 subtract(const glm::vec3& v1, const glm::vec3& v2) { return v1 - v2; } | 55 | glm::vec3 subtract(const glm::vec3& v1, const glm::vec3& v2) { return v1 - v2; } |
| @@ -45,6 +65,8 @@ public slots: · 9 unmodified lines | |||
| 45 | glm::vec3 toPolar(const glm::vec3& v); | 65 | glm::vec3 toPolar(const glm::vec3& v); |
| 46 | glm::vec3 fromPolar(const glm::vec3& polar); | 66 | glm::vec3 fromPolar(const glm::vec3& polar); |
| 47 | glm::vec3 fromPolar(float elevation, float azimuth); | 67 | glm::vec3 fromPolar(float elevation, float azimuth); |
| 68 | |||
| 69 | private: | ||
| 48 | const glm::vec3& UNIT_X() { return Vectors::UNIT_X; } | 70 | const glm::vec3& UNIT_X() { return Vectors::UNIT_X; } |
| 49 | const glm::vec3& UNIT_Y() { return Vectors::UNIT_Y; } | 71 | const glm::vec3& UNIT_Y() { return Vectors::UNIT_Y; } |
| 50 | const glm::vec3& UNIT_Z() { return Vectors::UNIT_Z; } | 72 | const glm::vec3& UNIT_Z() { return Vectors::UNIT_Z; } |
| @@ -51,7 +51,10 @@ public: · 50 unmodified lines | |||
| 51 | 51 | ||
| 52 | template<typename T, typename ...Args> | 52 | template<typename T, typename ...Args> |
| 53 | static QSharedPointer<T> set(Args&&... args); | 53 | static QSharedPointer<T> set(Args&&... args); |
| 54 | 54 | ||
| 55 | template<typename T, typename I, typename ...Args> | ||
| 56 | static QSharedPointer<T> set(Args&&... args); | ||
| 57 | |||
| 55 | template<typename T> | 58 | template<typename T> |
| 56 | static void destroy(); | 59 | static void destroy(); |
| 57 | 60 | ||
| @@ -89,13 +92,26 @@ QSharedPointer<T> DependencyManager::get() { · 31 unmodified lines | |||
| 89 | template <typename T, typename ...Args> | 92 | template <typename T, typename ...Args> |
| 90 | QSharedPointer<T> DependencyManager::set(Args&&... args) { | 93 | QSharedPointer<T> DependencyManager::set(Args&&... args) { |
| 91 | static size_t hashCode = _manager.getHashCode<T>(); | 94 | static size_t hashCode = _manager.getHashCode<T>(); |
| 92 | 95 | ||
| 93 | QSharedPointer<Dependency>& instance = _manager.safeGet(hashCode); | 96 | QSharedPointer<Dependency>& instance = _manager.safeGet(hashCode); |
| 94 | instance.clear(); // Clear instance before creation of new one to avoid edge cases | 97 | instance.clear(); // Clear instance before creation of new one to avoid edge cases |
| 95 | QSharedPointer<T> newInstance(new T(args...), &T::customDeleter); | 98 | QSharedPointer<T> newInstance(new T(args...), &T::customDeleter); |
| 96 | QSharedPointer<Dependency> storedInstance = qSharedPointerCast<Dependency>(newInstance); | 99 | QSharedPointer<Dependency> storedInstance = qSharedPointerCast<Dependency>(newInstance); |
| 97 | instance.swap(storedInstance); | 100 | instance.swap(storedInstance); |
| 98 | 101 | ||
| 102 | return newInstance; | ||
| 103 | } | ||
| 104 | |||
| 105 | template <typename T, typename I, typename ...Args> | ||
| 106 | QSharedPointer<T> DependencyManager::set(Args&&... args) { | ||
| 107 | static size_t hashCode = _manager.getHashCode<T>(); | ||
| 108 | |||
| 109 | QSharedPointer<Dependency>& instance = _manager.safeGet(hashCode); | ||
| 110 | instance.clear(); // Clear instance before creation of new one to avoid edge cases | ||
| 111 | QSharedPointer<T> newInstance(new I(args...), &I::customDeleter); | ||
| 112 | QSharedPointer<Dependency> storedInstance = qSharedPointerCast<Dependency>(newInstance); | ||
| 113 | instance.swap(storedInstance); | ||
| 114 | |||
| 99 | return newInstance; | 115 | return newInstance; |
| 100 | } | 116 | } |
| 101 | 117 | ||
| @@ -15,6 +15,7 @@ · 14 unmodified lines | |||
| 15 | #include <cstring> | 15 | #include <cstring> |
| 16 | #include <cctype> | 16 | #include <cctype> |
| 17 | #include <time.h> | 17 | #include <time.h> |
| 18 | #include <mutex> | ||
| 18 | 19 | ||
| 19 | #ifdef _WIN32 | 20 | #ifdef _WIN32 |
| 20 | #include <windows.h> | 21 | #include <windows.h> |
| @@ -39,30 +40,29 @@ void usecTimestampNowForceClockSkew(int clockSkew) { · 18 unmodified lines | |||
| 39 | ::usecTimestampNowAdjust = clockSkew; | 40 | ::usecTimestampNowAdjust = clockSkew; |
| 40 | } | 41 | } |
| 41 | 42 | ||
| 43 | static qint64 TIME_REFERENCE = 0; // in usec | ||
| 44 | static std::once_flag usecTimestampNowIsInitialized; | ||
| 45 | static QElapsedTimer timestampTimer; | ||
| 46 | |||
| 42 | quint64 usecTimestampNow(bool wantDebug) { | 47 | quint64 usecTimestampNow(bool wantDebug) { |
| 43 | static bool usecTimestampNowIsInitialized = false; | 48 | std::call_once(usecTimestampNowIsInitialized, [&] { |
| 44 | static qint64 TIME_REFERENCE = 0; // in usec | 49 | TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * USECS_PER_MSEC; // ms to usec |
| 45 | static QElapsedTimer timestampTimer; | ||
| 46 | |||
| 47 | if (!usecTimestampNowIsInitialized) { | ||
| 48 | TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * 1000; // ms to usec | ||
| 49 | timestampTimer.start(); | 50 | timestampTimer.start(); |
| 50 | usecTimestampNowIsInitialized = true; | 51 | }); |
| 51 | } | ||
| 52 | 52 | ||
| 53 | quint64 now; | 53 | quint64 now; |
| 54 | quint64 nsecsElapsed = timestampTimer.nsecsElapsed(); | 54 | quint64 nsecsElapsed = timestampTimer.nsecsElapsed(); |
| 55 | quint64 usecsElapsed = nsecsElapsed / 1000; // nsec to usec | 55 | quint64 usecsElapsed = nsecsElapsed / NSECS_PER_USEC; // nsec to usec |
| 56 | 56 | ||
| 57 | // QElapsedTimer may not advance if the CPU has gone to sleep. In which case it | 57 | // QElapsedTimer may not advance if the CPU has gone to sleep. In which case it |
| 58 | // will begin to deviate from real time. We detect that here, and reset if necessary | 58 | // will begin to deviate from real time. We detect that here, and reset if necessary |
| 59 | quint64 msecsCurrentTime = QDateTime::currentMSecsSinceEpoch(); | 59 | quint64 msecsCurrentTime = QDateTime::currentMSecsSinceEpoch(); |
| 60 | quint64 msecsEstimate = (TIME_REFERENCE + usecsElapsed) / 1000; // usecs to msecs | 60 | quint64 msecsEstimate = (TIME_REFERENCE + usecsElapsed) / USECS_PER_MSEC; // usecs to msecs |
| 61 | int possibleSkew = msecsEstimate - msecsCurrentTime; | 61 | int possibleSkew = msecsEstimate - msecsCurrentTime; |
| 62 | const int TOLERANCE = 10000; // up to 10 seconds of skew is tolerated | 62 | const int TOLERANCE = 10 * MSECS_PER_SECOND; // up to 10 seconds of skew is tolerated |
| 63 | if (abs(possibleSkew) > TOLERANCE) { | 63 | if (abs(possibleSkew) > TOLERANCE) { |
| 64 | // reset our TIME_REFERENCE and timer | 64 | // reset our TIME_REFERENCE and timer |
| 65 | TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * 1000; // ms to usec | 65 | TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * USECS_PER_MSEC; // ms to usec |
| 66 | timestampTimer.restart(); | 66 | timestampTimer.restart(); |
| 67 | now = TIME_REFERENCE + ::usecTimestampNowAdjust; | 67 | now = TIME_REFERENCE + ::usecTimestampNowAdjust; |
| 68 | 68 | ||
| @@ -118,6 +118,13 @@ quint64 usecTimestampNow(bool wantDebug) { · 49 unmodified lines | |||
| 118 | return now; | 118 | return now; |
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | float secTimestampNow() { | ||
| 122 | static const auto START_TIME = usecTimestampNow(); | ||
| 123 | const auto nowUsecs = usecTimestampNow() - START_TIME; | ||
| 124 | const auto nowMsecs = nowUsecs / USECS_PER_MSEC; | ||
| 125 | return (float)nowMsecs / MSECS_PER_SECOND; | ||
| 126 | } | ||
| 127 | |||
| 121 | float randFloat() { | 128 | float randFloat() { |
| 122 | return (rand() % 10000)/10000.0f; | 129 | return (rand() % 10000)/10000.0f; |
| 123 | } | 130 | } |
| @@ -65,9 +65,14 @@ inline bool operator!=(const xColor& lhs, const xColor& rhs) · 64 unmodified lines | |||
| 65 | // Use a custom User-Agent to avoid ModSecurity filtering, e.g. by hosting providers. | 65 | // Use a custom User-Agent to avoid ModSecurity filtering, e.g. by hosting providers. |
| 66 | const QByteArray HIGH_FIDELITY_USER_AGENT = "Mozilla/5.0 (HighFidelityInterface)"; | 66 | const QByteArray HIGH_FIDELITY_USER_AGENT = "Mozilla/5.0 (HighFidelityInterface)"; |
| 67 | 67 | ||
| 68 | // Equivalent to time_t but in usecs instead of secs | ||
| 68 | quint64 usecTimestampNow(bool wantDebug = false); | 69 | quint64 usecTimestampNow(bool wantDebug = false); |
| 69 | void usecTimestampNowForceClockSkew(int clockSkew); | 70 | void usecTimestampNowForceClockSkew(int clockSkew); |
| 70 | 71 | ||
| 72 | // Number of seconds expressed since the first call to this function, expressed as a float | ||
| 73 | // Maximum accuracy in msecs | ||
| 74 | float secTimestampNow(); | ||
| 75 | |||
| 71 | float randFloat(); | 76 | float randFloat(); |
| 72 | int randIntInRange (int min, int max); | 77 | int randIntInRange (int min, int max); |
| 73 | float randFloatInRange (float min,float max); | 78 | float randFloatInRange (float min,float max); |
| @@ -40,4 +40,28 @@ private: · 39 unmodified lines | |||
| 40 | float ONE_MINUS_WEIGHTING; | 40 | float ONE_MINUS_WEIGHTING; |
| 41 | }; | 41 | }; |
| 42 | 42 | ||
| 43 | |||
| 44 | template <class T, int MAX_NUM_SAMPLES> class MovingAverage { | ||
| 45 | public: | ||
| 46 | const float WEIGHTING = 1.0f / (float)MAX_NUM_SAMPLES; | ||
| 47 | const float ONE_MINUS_WEIGHTING = 1.0f - WEIGHTING; | ||
| 48 | int numSamples{ 0 }; | ||
| 49 | T average; | ||
| 50 | |||
| 51 | void clear() { | ||
| 52 | numSamples = 0; | ||
| 53 | } | ||
| 54 | |||
| 55 | bool isAverageValid() const { return (numSamples > 0); } | ||
| 56 | |||
| 57 | void addSample(T sample) { | ||
| 58 | if (numSamples > 0) { | ||
| 59 | average = (sample * WEIGHTING) + (average * ONE_MINUS_WEIGHTING); | ||
| 60 | } else { | ||
| 61 | average = sample; | ||
| 62 | } | ||
| 63 | numSamples++; | ||
| 64 | } | ||
| 65 | }; | ||
| 66 | |||
| 43 | #endif // hifi_SimpleMovingAverage_h | 67 | #endif // hifi_SimpleMovingAverage_h |
| @@ -0,0 +1,51 @@ | |||
| 1 | // | ||
| 2 | // Created by Bradley Austin Davis 2015/10/09 | ||
| 3 | // Copyright 2015 High Fidelity, Inc. | ||
| 4 | // | ||
| 5 | // Distributed under the Apache License, Version 2.0. | ||
| 6 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 7 | // | ||
| 8 | |||
| 9 | #pragma once | ||
| 10 | #ifndef hifi_Shared_Factory_h | ||
| 11 | #define hifi_Shared_Factory_h | ||
| 12 | |||
| 13 | #include <functional> | ||
| 14 | #include <map> | ||
| 15 | #include <memory> | ||
| 16 | |||
| 17 | namespace hifi { | ||
| 18 | |||
| 19 | template <typename T, typename Key> | ||
| 20 | class SimpleFactory { | ||
| 21 | public: | ||
| 22 | using Pointer = std::shared_ptr<T>; | ||
| 23 | using Builder = std::function<Pointer()>; | ||
| 24 | using BuilderMap = std::map<Key, Builder>; | ||
| 25 | |||
| 26 | void registerBuilder(const Key& name, Builder builder) { | ||
| 27 | // FIXME don't allow name collisions | ||
| 28 | _builders[name] = builder; | ||
| 29 | } | ||
| 30 | |||
| 31 | Pointer create(const Key& name) const { | ||
| 32 | const auto& entryIt = _builders.find(name); | ||
| 33 | if (entryIt != _builders.end()) { | ||
| 34 | return (*entryIt).second(); | ||
| 35 | } | ||
| 36 | return Pointer(); | ||
| 37 | } | ||
| 38 | |||
| 39 | template <typename Impl> | ||
| 40 | class Registrar { | ||
| 41 | public: | ||
| 42 | Registrar(const Key& name, SimpleFactory& factory) { | ||
| 43 | factory.registerBuilder(name, [] { return std::make_shared<Impl>(); }); | ||
| 44 | } | ||
| 45 | }; | ||
| 46 | protected: | ||
| 47 | BuilderMap _builders; | ||
| 48 | }; | ||
| 49 | } | ||
| 50 | |||
| 51 | #endif | ||
| @@ -113,6 +113,7 @@ void VrMenu::addMenu(QMenu* menu) { · 112 unmodified lines | |||
| 113 | Q_ARG(QVariant, QVariant::fromValue(qmlParent)), | 113 | Q_ARG(QVariant, QVariant::fromValue(qmlParent)), |
| 114 | Q_ARG(QVariant, QVariant::fromValue(menu->title()))); | 114 | Q_ARG(QVariant, QVariant::fromValue(menu->title()))); |
| 115 | Q_ASSERT(invokeResult); | 115 | Q_ASSERT(invokeResult); |
| 116 | Q_UNUSED(invokeResult); // FIXME - apparently we haven't upgraded the Qt on our unix Jenkins environments to 5.5.x | ||
| 116 | QObject* result = returnedValue.value<QObject*>(); | 117 | QObject* result = returnedValue.value<QObject*>(); |
| 117 | Q_ASSERT(result); | 118 | Q_ASSERT(result); |
| 118 | 119 | ||
| @@ -153,6 +154,7 @@ void VrMenu::addAction(QMenu* menu, QAction* action) { · 34 unmodified lines | |||
| 153 | Q_ARG(QVariant, QVariant::fromValue(menuQml)), | 154 | Q_ARG(QVariant, QVariant::fromValue(menuQml)), |
| 154 | Q_ARG(QVariant, QVariant::fromValue(action->text()))); | 155 | Q_ARG(QVariant, QVariant::fromValue(action->text()))); |
| 155 | Q_ASSERT(invokeResult); | 156 | Q_ASSERT(invokeResult); |
| 157 | Q_UNUSED(invokeResult); // FIXME - apparently we haven't upgraded the Qt on our unix Jenkins environments to 5.5.x | ||
| 156 | QObject* result = returnedValue.value<QObject*>(); | 158 | QObject* result = returnedValue.value<QObject*>(); |
| 157 | Q_ASSERT(result); | 159 | Q_ASSERT(result); |
| 158 | // Bind the QML and Widget together | 160 | // Bind the QML and Widget together |
| @@ -174,6 +176,7 @@ void VrMenu::insertAction(QAction* before, QAction* action) { · 15 unmodified lines | |||
| 174 | Q_ARG(QVariant, QVariant::fromValue(beforeQml)), | 176 | Q_ARG(QVariant, QVariant::fromValue(beforeQml)), |
| 175 | Q_ARG(QVariant, QVariant::fromValue(action->text()))); | 177 | Q_ARG(QVariant, QVariant::fromValue(action->text()))); |
| 176 | Q_ASSERT(invokeResult); | 178 | Q_ASSERT(invokeResult); |
| 179 | Q_UNUSED(invokeResult); // FIXME - apparently we haven't upgraded the Qt on our unix Jenkins environments to 5.5.x | ||
| 177 | QObject* result = returnedValue.value<QObject*>(); | 180 | QObject* result = returnedValue.value<QObject*>(); |
| 178 | Q_ASSERT(result); | 181 | Q_ASSERT(result); |
| 179 | bindActionToQmlAction(result, action); | 182 | bindActionToQmlAction(result, action); |
| @@ -192,4 +195,5 @@ void VrMenu::removeAction(QAction* action) { · 12 unmodified lines | |||
| 192 | Q_ARG(QVariant, QVariant::fromValue(menu)), | 195 | Q_ARG(QVariant, QVariant::fromValue(menu)), |
| 193 | Q_ARG(QVariant, QVariant::fromValue(item))); | 196 | Q_ARG(QVariant, QVariant::fromValue(item))); |
| 194 | Q_ASSERT(invokeResult); | 197 | Q_ASSERT(invokeResult); |
| 198 | Q_UNUSED(invokeResult); // FIXME - apparently we haven't upgraded the Qt on our unix Jenkins environments to 5.5.x | ||
| 195 | } | 199 | } |
| @@ -0,0 +1,19 @@ | |||
| 1 | |||
| 2 | set(TARGET_NAME controllers-test) | ||
| 3 | |||
| 4 | # This is not a testcase -- just set it up as a regular hifi project | ||
| 5 | setup_hifi_project(Script Qml) | ||
| 6 | set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") | ||
| 7 | |||
| 8 | # link in the shared libraries | ||
| 9 | link_hifi_libraries(shared gl script-engine plugins render-utils input-plugins display-plugins controllers) | ||
| 10 | |||
| 11 | |||
| 12 | if (WIN32) | ||
| 13 | add_dependency_external_projects(OpenVR) | ||
| 14 | find_package(OpenVR REQUIRED) | ||
| 15 | target_include_directories(${TARGET_NAME} PRIVATE ${OPENVR_INCLUDE_DIRS}) | ||
| 16 | target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES}) | ||
| 17 | endif() | ||
| 18 | |||
| 19 | copy_dlls_beside_windows_executable() | ||
| @@ -0,0 +1,22 @@ | |||
| 1 | import QtQuick 2.1 | ||
| 2 | import QtQuick.Controls 1.0 | ||
| 3 | import QtQuick.Layouts 1.0 | ||
| 4 | import QtQuick.Dialogs 1.0 | ||
| 5 | |||
| 6 | ApplicationWindow { | ||
| 7 | id: window | ||
| 8 | visible: true | ||
| 9 | |||
| 10 | Timer { | ||
| 11 | interval: 50; running: true; repeat: true | ||
| 12 | onTriggered: { | ||
| 13 | Controller.update(); | ||
| 14 | } | ||
| 15 | } | ||
| 16 | |||
| 17 | |||
| 18 | Loader { | ||
| 19 | id: pageLoader | ||
| 20 | source: ResourcePath + "TestControllers.qml" | ||
| 21 | } | ||
| 22 | } | ||
| @@ -0,0 +1,158 @@ | |||
| 1 | // | ||
| 2 | // main.cpp | ||
| 3 | // tests/gpu-test/src | ||
| 4 | // | ||
| 5 | // Copyright 2015 High Fidelity, Inc. | ||
| 6 | // | ||
| 7 | // Distributed under the Apache License, Version 2.0. | ||
| 8 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html | ||
| 9 | // | ||
| 10 | |||
| 11 | #include <unordered_map> | ||
| 12 | #include <memory> | ||
| 13 | #include <cstdio> | ||
| 14 | #include <mutex> | ||
| 15 | #include <set> | ||
| 16 | |||
| 17 | #include <glm/glm.hpp> | ||
| 18 | #include <glm/gtc/matrix_transform.hpp> | ||
| 19 | |||
| 20 | #include <QtCore/QTime> | ||
| 21 | #include <QtCore/QTimer> | ||
| 22 | #include <QtCore/QDir> | ||
| 23 | #include <QtCore/QElapsedTimer> | ||
| 24 | #include <QtCore/QFile> | ||
| 25 | #include <QtCore/QLoggingCategory> | ||
| 26 | |||
| 27 | #include <QtGui/QResizeEvent> | ||
| 28 | #include <QtGui/QWindow> | ||
| 29 | #include <QtGui/QGuiApplication> | ||
| 30 | #include <QtGui/QImage> | ||
| 31 | |||
| 32 | #include <QtQuick/QQuickItem> | ||
| 33 | #include <QtQml/QQmlApplicationEngine> | ||
| 34 | #include <QtQml/QQmlContext> | ||
| 35 | |||
| 36 | #include <plugins/Plugin.h> | ||
| 37 | #include <plugins/PluginContainer.h> | ||
| 38 | #include <plugins/PluginManager.h> | ||
| 39 | #include <input-plugins/InputPlugin.h> | ||
| 40 | #include <input-plugins/KeyboardMouseDevice.h> | ||
| 41 | #include <controllers/ScriptingInterface.h> | ||
| 42 | |||
| 43 | #include <DependencyManager.h> | ||
| 44 | #include <controllers/UserInputMapper.h> | ||
| 45 | |||
| 46 | const QString& getResourcesDir() { | ||
| 47 | static QString dir; | ||
| 48 | if (dir.isEmpty()) { | ||
| 49 | QDir path(__FILE__); | ||
| 50 | path.cdUp(); | ||
| 51 | dir = path.cleanPath(path.absoluteFilePath("../../../interface/resources/")) + "/"; | ||
| 52 | qDebug() << "Resources Path: " << dir; | ||
| 53 | } | ||
| 54 | return dir; | ||
| 55 | } | ||
| 56 | |||
| 57 | const QString& getQmlDir() { | ||
| 58 | static QString dir; | ||
| 59 | if (dir.isEmpty()) { | ||
| 60 | dir = getResourcesDir() + "qml/"; | ||
| 61 | qDebug() << "Qml Path: " << dir; | ||
| 62 | } | ||
| 63 | return dir; | ||
| 64 | } | ||
| 65 | |||
| 66 | const QString& getTestQmlDir() { | ||
| 67 | static QString dir; | ||
| 68 | if (dir.isEmpty()) { | ||
| 69 | QDir path(__FILE__); | ||
| 70 | path.cdUp(); | ||
| 71 | dir = path.cleanPath(path.absoluteFilePath("../")) + "/qml/"; | ||
| 72 | qDebug() << "Qml Test Path: " << dir; | ||
| 73 | } | ||
| 74 | return dir; | ||
| 75 | } | ||
| 76 | |||
| 77 | using namespace controller; | ||
| 78 | |||
| 79 | |||
| 80 | class PluginContainerProxy : public QObject, PluginContainer { | ||
| 81 | Q_OBJECT | ||
| 82 | public: | ||
| 83 | PluginContainerProxy() { | ||
| 84 | Plugin::setContainer(this); | ||
| 85 | } | ||
| 86 | virtual ~PluginContainerProxy() {} | ||
| 87 | virtual void addMenu(const QString& menuName) override {} | ||
| 88 | virtual void removeMenu(const QString& menuName) override {} | ||
| 89 | virtual QAction* addMenuItem(const QString& path, const QString& name, std::function<void(bool)> onClicked, bool checkable = false, bool checked = false, const QString& groupName = "") override { return nullptr; } | ||
| 90 | virtual void removeMenuItem(const QString& menuName, const QString& menuItem) override {} | ||
| 91 | virtual bool isOptionChecked(const QString& name) override { return false; } | ||
| 92 | virtual void setIsOptionChecked(const QString& path, bool checked) override {} | ||
| 93 | virtual void setFullscreen(const QScreen* targetScreen, bool hideMenu = true) override {} | ||
| 94 | virtual void unsetFullscreen(const QScreen* avoidScreen = nullptr) override {} | ||
| 95 | virtual void showDisplayPluginsTools() override {} | ||
| 96 | virtual void requestReset() override {} | ||
| 97 | virtual QGLWidget* getPrimarySurface() override { return nullptr; } | ||
| 98 | virtual bool isForeground() override { return true; } | ||
| 99 | virtual const DisplayPlugin* getActiveDisplayPlugin() const override { return nullptr; } | ||
| 100 | }; | ||
| 101 | |||
| 102 | class MyControllerScriptingInterface : public controller::ScriptingInterface { | ||
| 103 | public: | ||
| 104 | virtual void registerControllerTypes(QScriptEngine* engine) {}; | ||
| 105 | }; | ||
| 106 | |||
| 107 | |||
| 108 | int main(int argc, char** argv) { | ||
| 109 | QGuiApplication app(argc, argv); | ||
| 110 | QQmlApplicationEngine engine; | ||
| 111 | auto rootContext = engine.rootContext(); | ||
| 112 | new PluginContainerProxy(); | ||
| 113 | |||
| 114 | // Simulate our application idle loop | ||
| 115 | QTimer timer; | ||
| 116 | QObject::connect(&timer, &QTimer::timeout, [] { | ||
| 117 | static float last = secTimestampNow(); | ||
| 118 | float now = secTimestampNow(); | ||
| 119 | float delta = now - last; | ||
| 120 | last = now; | ||
| 121 | |||
| 122 | foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { | ||
| 123 | inputPlugin->pluginUpdate(delta, false); | ||
| 124 | } | ||
| 125 | |||
| 126 | auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); | ||
| 127 | userInputMapper->update(delta); | ||
| 128 | }); | ||
| 129 | timer.start(50); | ||
| 130 | |||
| 131 | { | ||
| 132 | DependencyManager::set<controller::UserInputMapper>(); | ||
| 133 | foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { | ||
| 134 | QString name = inputPlugin->getName(); | ||
| 135 | inputPlugin->activate(); | ||
| 136 | auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); | ||
| 137 | if (name == KeyboardMouseDevice::NAME) { | ||
| 138 | auto keyboardMouseDevice = static_cast<KeyboardMouseDevice*>(inputPlugin.data()); // TODO: this seems super hacky | ||
| 139 | userInputMapper->registerDevice(std::shared_ptr<InputDevice>(keyboardMouseDevice)); | ||
| 140 | } | ||
| 141 | inputPlugin->pluginUpdate(0, false); | ||
| 142 | } | ||
| 143 | rootContext->setContextProperty("Controllers", new MyControllerScriptingInterface()); | ||
| 144 | } | ||
| 145 | qDebug() << getQmlDir(); | ||
| 146 | rootContext->setContextProperty("ResourcePath", getQmlDir()); | ||
| 147 | engine.setBaseUrl(QUrl::fromLocalFile(getQmlDir())); | ||
| 148 | engine.addImportPath(getQmlDir()); | ||
| 149 | engine.load(getTestQmlDir() + "main.qml"); | ||
| 150 | for (auto pathItem : engine.importPathList()) { | ||
| 151 | qDebug() << pathItem; | ||
| 152 | } | ||
| 153 | app.exec(); | ||
| 154 | return 0; | ||
| 155 | } | ||
| 156 | |||
| 157 | #include "main.moc" | ||
| 158 | |||