Unified · Split
| @@ -185,22 +185,8 @@ set(EXTERNAL_PROJECT_PREFIX "project") · 184 unmodified lines | ||
| 185 | 185 | set_property(DIRECTORY PROPERTY EP_PREFIX ${EXTERNAL_PROJECT_PREFIX}) |
| 186 | 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 | 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 | 191 | if (WIN32) |
| 206 | 192 | add_paths_to_fixup_libs("${QT_DIR}/bin") |
| @@ -1,12 +1,12 @@ | ||
| 1 | 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 | 5 | # link in the shared libraries |
| 6 | 6 | link_hifi_libraries( |
| 7 | 7 | audio avatars octree environment gpu model fbx entities |
| 8 | 8 | networking animation shared script-engine embedded-webserver |
| 9 | − physics | |
| 9 | + controllers physics | |
| 10 | 10 | ) |
| 11 | 11 | |
| 12 | 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 | 47 | ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) |
| 48 | 48 | set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${SOURCE_DIR}/SDL2.framework/Headers CACHE PATH "Location of SDL2 include directory") |
| 49 | 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 | 55 | else () |
| 51 | − if (WIN32) | |
| 52 | − ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) | |
| 53 | − set(_ROOT_DIR ${SOURCE_DIR}) | |
| 54 | − set(_INCLUDE_DIR ${_ROOT_DIR}/include) | |
| 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 () | |
| 56 | + ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) | |
| 57 | + set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${INSTALL_DIR}/include/SDL2 CACHE PATH "Location of SDL2 include directory") | |
| 58 | + set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${INSTALL_DIR}/lib/libSDL2.so CACHE FILEPATH "Path to SDL2 library") | |
| 59 | +endif () | |
| @@ -1,30 +1,20 @@ | ||
| 1 | 1 | include(ExternalProject) |
| 2 | −include(SelectLibraryConfigurations) | |
| 3 | 2 | |
| 4 | −set(EXTERNAL_NAME Sixense) | |
| 3 | +set(EXTERNAL_NAME sixense) | |
| 5 | 4 | |
| 6 | 5 | string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) |
| 7 | 6 | |
| 8 | 7 | ExternalProject_Add( |
| 9 | 8 | ${EXTERNAL_NAME} |
| 10 | − URL ./SixenseSDK_062612.zip | |
| 11 | − URL_MD5 10cc8dc470d2ac1244a88cf04bc549cc | |
| 9 | + URL http://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_071615.zip | |
| 10 | + URL_MD5 752a3901f334124e9cffc2ba4136ef7d | |
| 12 | 11 | CONFIGURE_COMMAND "" |
| 13 | 12 | BUILD_COMMAND "" |
| 14 | 13 | INSTALL_COMMAND "" |
| 15 | 14 | LOG_DOWNLOAD 1 |
| 16 | 15 | ) |
| 17 | 16 | |
| 18 | −if (APPLE) | |
| 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 | − | |
| 17 | +set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") | |
| 28 | 18 | |
| 29 | 19 | ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) |
| 30 | 20 | |
| @@ -39,22 +29,18 @@ if (WIN32) · 8 unmodified lines | ||
| 39 | 29 | set(ARCH_DIR "Win32") |
| 40 | 30 | set(ARCH_SUFFIX "") |
| 41 | 31 | endif() |
| 42 | − | |
| 43 | − # FIXME need to account for different architectures | |
| 44 | − set(${EXTERNAL_NAME_UPPER}_LIBRARIES "${SOURCE_DIR}/lib/${ARCH_DIR}/release_dll/sixense${ARCH_SUFFIX}.lib" CACHE TYPE INTERNAL) | |
| 45 | − add_paths_to_fixup_libs(${SOURCE_DIR}/bin/win32) | |
| 32 | + | |
| 33 | + set(${EXTERNAL_NAME_UPPER}_LIBRARIES "${SOURCE_DIR}/lib/${ARCH_DIR}/VS2013/release_dll/sixense${ARCH_SUFFIX}.lib" CACHE TYPE INTERNAL) | |
| 34 | + add_paths_to_fixup_libs("${SOURCE_DIR}/bin/${ARCH_DIR}/VS2013/release_dll") | |
| 46 | 35 | |
| 47 | 36 | elseif(APPLE) |
| 48 | 37 | |
| 49 | − # FIXME need to account for different architectures | |
| 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) | |
| 38 | + set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/osx_x64/release_dll/libsixense_x64.dylib CACHE TYPE INTERNAL) | |
| 52 | 39 | |
| 53 | 40 | elseif(NOT ANDROID) |
| 54 | 41 | |
| 55 | − # 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) | |
| 57 | − add_paths_to_fixup_libs(${SOURCE_DIR}/bin/linux32) | |
| 58 | − | |
| 42 | + # FIXME need to account for different architectures | |
| 43 | + set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/linux_x64/release/libsixense_x64.so CACHE TYPE INTERNAL) | |
| 44 | + | |
| 59 | 45 | endif() |
| 60 | 46 | |
| @@ -16,7 +16,7 @@ macro(ADD_DEPENDENCY_EXTERNAL_PROJECTS) · 15 unmodified lines | ||
| 16 | 16 | string(TOUPPER ${_PROJ_NAME} _PROJ_NAME_UPPER) |
| 17 | 17 | |
| 18 | 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 | 20 | # have we already detected we can't have this as external project on this OS? |
| 21 | 21 | if (NOT DEFINED ${_PROJ_NAME_UPPER}_EXTERNAL_PROJECT OR ${_PROJ_NAME_UPPER}_EXTERNAL_PROJECT) |
| 22 | 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 | 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 | 21 | include(SelectLibraryConfigurations) |
| 42 | 22 | select_library_configurations(FACESHIFT) |
| 43 | 23 | |
| 44 | −set(FACESHIFT_LIBRARIES ${FACESHIFT_LIBRARY}) | |
| 45 | − | |
| 46 | 24 | include(FindPackageHandleStandardArgs) |
| 47 | 25 | find_package_handle_standard_args(Faceshift DEFAULT_MSG FACESHIFT_INCLUDE_DIRS FACESHIFT_LIBRARIES) |
| 48 | − | |
| 49 | 26 | mark_as_advanced(FACESHIFT_INCLUDE_DIRS FACESHIFT_LIBRARIES FACESHIFT_SEARCH_DIRS) |
| @@ -18,49 +18,6 @@ · 17 unmodified lines | ||
| 18 | 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 | 21 | include(FindPackageHandleStandardArgs) |
| 60 | −find_package_handle_standard_args(Sixense DEFAULT_MSG ${SIXENSE_REQUIREMENTS}) | |
| 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 | − | |
| 22 | +find_package_handle_standard_args(Sixense DEFAULT_MSG SIXENSE_INCLUDE_DIRS SIXENSE_LIBRARIES) | |
| 66 | 23 | mark_as_advanced(SIXENSE_LIBRARIES SIXENSE_INCLUDE_DIRS SIXENSE_SEARCH_DIRS) |
| @@ -21,45 +21,17 @@ if (WIN32) · 20 unmodified lines | ||
| 21 | 21 | hifi_library_search_hints("iViewHMD") |
| 22 | 22 | |
| 23 | 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}) | |
| 25 | − find_path(IVIEWHMD_API_DLL_PATH iViewHMDAPI.dll 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/x86 HINTS ${IVIEWHMD_SEARCH_DIRS}) | |
| 26 | 26 | list(APPEND IVIEWHMD_REQUIREMENTS IVIEWHMD_INCLUDE_DIRS IVIEWHMD_LIBRARIES IVIEWHMD_API_DLL_PATH) |
| 27 | 27 | |
| 28 | − set(IVIEWHMD_DLLS | |
| 29 | − avcodec-53.dll | |
| 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() | |
| 28 | + find_path(IVIEWHMD_DLL_PATH_3RD_PARTY libiViewNG.dll PATH_SUFFIXES 3rdParty HINTS ${IVIEWHMD_SEARCH_DIRS}) | |
| 29 | + list(APPEND IVIEWHMD_REQUIREMENTS IVIEWHMD_DLL_PATH_3RD_PARTY) | |
| 58 | 30 | |
| 59 | 31 | include(FindPackageHandleStandardArgs) |
| 60 | 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 | 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 | 71 | var palm2Position = MyAvatar.getRightPalmPosition(); |
| 72 | 72 | var distanceBetween = Vec3.length(Vec3.subtract(palm1Position, palm2Position)); |
| 73 | 73 | |
| 74 | − var palm1Velocity = Controller.getSpatialControlVelocity(1); | |
| 75 | − var palm2Velocity = Controller.getSpatialControlVelocity(3); | |
| 74 | + var palm1Velocity = Controller.getPoseValue(Controller.Standard.LeftHand).velocity; | |
| 75 | + var palm2Velocity = Controller.getPoseValue(Controller.Standard.RightHand).velocity; | |
| 76 | 76 | var closingVelocity = Vec3.length(Vec3.subtract(palm1Velocity, palm2Velocity)); |
| 77 | 77 | |
| 78 | 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 | 148 | this.state = STATE_OFF; |
| 149 | 149 | this.pointer = null; // entity-id of line object |
| 150 | 150 | this.triggerValue = 0; // rolling average of trigger value |
| 151 | + this.rawTriggerValue = 0; | |
| 151 | 152 | |
| 152 | 153 | var _this = this; |
| 153 | 154 | |
| @@ -244,12 +245,17 @@ function MyController(hand, triggerAction) { · 90 unmodified lines | ||
| 244 | 245 | this.pointer = null; |
| 245 | 246 | }; |
| 246 | 247 | |
| 247 | − this.updateSmoothedTrigger = function() { | |
| 248 | − var triggerValue = Controller.getActionValue(this.triggerAction); | |
| 248 | + this.eitherTrigger = function (value) { | |
| 249 | + _this.rawTriggerValue = value; | |
| 250 | + }; | |
| 251 | + | |
| 252 | + this.updateSmoothedTrigger = function () { | |
| 253 | + var triggerValue = this.rawTriggerValue; | |
| 249 | 254 | // smooth out trigger value |
| 250 | 255 | this.triggerValue = (this.triggerValue * TRIGGER_SMOOTH_RATIO) + |
| 251 | 256 | (triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO)); |
| 252 | − } | |
| 257 | + | |
| 258 | + }; | |
| 253 | 259 | |
| 254 | 260 | this.triggerSmoothedSqueezed = function() { |
| 255 | 261 | return this.triggerValue > TRIGGER_ON_VALUE; |
| @@ -259,8 +265,8 @@ function MyController(hand, triggerAction) { · 3 unmodified lines | ||
| 259 | 265 | return this.triggerValue < TRIGGER_OFF_VALUE; |
| 260 | 266 | }; |
| 261 | 267 | |
| 262 | − this.triggerSqueezed = function() { | |
| 263 | − var triggerValue = Controller.getActionValue(this.triggerAction); | |
| 268 | + this.triggerSqueezed = function() { | |
| 269 | + var triggerValue = this.rawTriggerValue; | |
| 264 | 270 | return triggerValue > TRIGGER_ON_VALUE; |
| 265 | 271 | }; |
| 266 | 272 | |
| @@ -402,8 +408,9 @@ function MyController(hand, triggerAction) { · 135 unmodified lines | ||
| 402 | 408 | |
| 403 | 409 | this.distanceHolding = function() { |
| 404 | 410 | |
| 405 | − var handControllerPosition = Controller.getSpatialControlPosition(this.palm); | |
| 406 | − var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm)); | |
| 411 | + var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; | |
| 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 | 414 | var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation", |
| 408 | 415 | "gravity", "ignoreForCollisions"]); |
| 409 | 416 | var now = Date.now(); |
| @@ -452,8 +459,9 @@ function MyController(hand, triggerAction) { · 42 unmodified lines | ||
| 452 | 459 | } |
| 453 | 460 | |
| 454 | 461 | var handPosition = this.getHandPosition(); |
| 455 | − var handControllerPosition = Controller.getSpatialControlPosition(this.palm); | |
| 456 | − var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm)); | |
| 462 | + var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition; | |
| 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 | 465 | var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation"]); |
| 458 | 466 | |
| 459 | 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 | 608 | this.currentObjectTime = Date.now(); |
| 601 | 609 | }; |
| @@ -613,7 +621,7 @@ function MyController(hand, triggerAction) { · 11 unmodified lines | ||
| 613 | 621 | // of it's actual offset, let's try imparting a velocity which is at a fixed radius |
| 614 | 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 | 625 | var now = Date.now(); |
| 618 | 626 | |
| 619 | 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")); | |
| 860 | −var leftController = new MyController(LEFT_HAND, Controller.findAction("LEFT_HAND_CLICK")); | |
| 867 | +var rightController = new MyController(RIGHT_HAND, Controller.Standard.RT); | |
| 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 | 878 | function update() { |
| 863 | 879 | rightController.update(); |
| @@ -867,6 +883,7 @@ function update() { · 3 unmodified lines | ||
| 867 | 883 | function cleanup() { |
| 868 | 884 | rightController.cleanup(); |
| 869 | 885 | leftController.cleanup(); |
| 886 | + Controller.disableMapping(MAPPING_NAME); | |
| 870 | 887 | } |
| 871 | 888 | |
| 872 | 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 | 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 | 37 | var chords = new Array(); |
| 36 | 38 | // Nylon string guitar |
| 37 | 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 | 58 | var guitarSelector = NUM_CHORDS; |
| 57 | 59 | var whichChord = 1; |
| 58 | 60 | |
| 59 | −var leftHanded = true; | |
| 61 | +var leftHanded = false; | |
| 62 | +var strumHand, chordHand, strumTrigger, chordTrigger; | |
| 60 | 63 | if (leftHanded) { |
| 61 | − var strumHand = 0; | |
| 62 | − var chordHand = 1; | |
| 64 | + strumHand = Controller.Standard.LeftHand; | |
| 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 | 73 | } else { |
| 64 | − var strumHand = 1; | |
| 65 | − var chordHand = 0; | |
| 74 | + strumHand = Controller.Standard.RightHand; | |
| 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 | 85 | var lastPosition = { x: 0.0, |
| 69 | 86 | y: 0.0, |
| 70 | 87 | z: 0.0 }; |
| 71 | 88 | |
| 72 | −var soundPlaying = false; | |
| 89 | +var audioInjector = null; | |
| 73 | 90 | var selectorPressed = false; |
| 74 | 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 | 95 | function checkHands(deltaTime) { |
| 79 | − for (var palm = 0; palm < 2; palm++) { | |
| 80 | − var palmVelocity = Controller.getSpatialControlVelocity(palm * 2 + 1); | |
| 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 | − } | |
| 96 | + var strumVelocity = Controller.getPoseValue(strumHand).velocity; | |
| 97 | + var volume = length(strumVelocity) / 5.0; | |
| 93 | 98 | |
| 94 | − var BUTTON_COUNT = 6; | |
| 95 | − | |
| 96 | − // Change guitars if button FWD (5) pressed | |
| 97 | − if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 5)) { | |
| 98 | − if (!selectorPressed) { | |
| 99 | − guitarSelector += NUM_CHORDS; | |
| 100 | − if (guitarSelector >= NUM_CHORDS * NUM_GUITARS) { | |
| 101 | − guitarSelector = 0; | |
| 102 | − } | |
| 103 | − selectorPressed = true; | |
| 104 | − } | |
| 105 | − } else { | |
| 106 | − selectorPressed = false; | |
| 107 | − } | |
| 99 | + if (volume == 0.0) { | |
| 100 | + volume = 1.0; | |
| 101 | + } | |
| 102 | + | |
| 103 | + var strumHandPosition = leftHanded ? MyAvatar.leftHandPosition : MyAvatar.rightHandPosition; | |
| 104 | + var myPelvis = MyAvatar.position; | |
| 105 | + var strumming = Controller.getValue(strumTrigger); | |
| 106 | + var chord = Controller.getValue(chordTrigger); | |
| 107 | + | |
| 108 | + if (volume > 1.0) volume = 1.0; | |
| 109 | + if ((chord > 0.1) && audioInjector && audioInjector.isPlaying) { | |
| 110 | + // If chord finger trigger pulled, stop current chord | |
| 111 | + print("stopping chord because cord trigger pulled"); | |
| 112 | + audioInjector.stop(); | |
| 113 | + } | |
| 108 | 114 | |
| 109 | − if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 1)) { | |
| 110 | − whichChord = 1; | |
| 111 | − } else if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 2)) { | |
| 112 | − whichChord = 2; | |
| 113 | − } else if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 3)) { | |
| 114 | − whichChord = 3; | |
| 115 | − } else if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 4)) { | |
| 116 | − whichChord = 4; | |
| 115 | + // Change guitars if button FWD (5) pressed | |
| 116 | + if (Controller.getValue(changeGuitar)) { | |
| 117 | + if (!selectorPressed) { | |
| 118 | + print("changeGuitar:" + changeGuitar); | |
| 119 | + guitarSelector += NUM_CHORDS; | |
| 120 | + if (guitarSelector >= NUM_CHORDS * NUM_GUITARS) { | |
| 121 | + guitarSelector = 0; | |
| 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) { | |
| 120 | − | |
| 121 | − var STRUM_HEIGHT_ABOVE_PELVIS = 0.10; | |
| 122 | − var strumTriggerHeight = myPelvis.y + STRUM_HEIGHT_ABOVE_PELVIS; | |
| 123 | − //printVector(position); | |
| 124 | − if ( ( ((position.y < strumTriggerHeight) && (lastPosition.y >= strumTriggerHeight)) || | |
| 125 | − ((position.y > strumTriggerHeight) && (lastPosition.y <= strumTriggerHeight)) ) && (trigger > 0.1) ){ | |
| 126 | − // If hand passes downward or upward through 'strings', and finger trigger pulled, play | |
| 127 | − playChord(position, volume); | |
| 128 | − } | |
| 129 | − lastPosition = Controller.getSpatialControlPosition(palm * 2 + 1); | |
| 130 | − } | |
| 148 | + var strumNowAbove = strumHandPosition.y > strummingHeight; | |
| 149 | + var strumNowBelow = strumHandPosition.y <= strummingHeight; | |
| 150 | + var strumWasAbove = lastPosition.y > strummingHeight; | |
| 151 | + var strumWasBelow = lastPosition.y <= strummingHeight; | |
| 152 | + var strumUp = strumNowAbove && strumWasBelow; | |
| 153 | + var strumDown = strumNowBelow && strumWasAbove; | |
| 154 | + | |
| 155 | + if ((strumUp || strumDown) && (strumming > 0.1)) { | |
| 156 | + // If hand passes downward or upward through 'strings', and finger trigger pulled, play | |
| 157 | + playChord(strumHandPosition, volume); | |
| 131 | 158 | } |
| 159 | + lastPosition = strumHandPosition; | |
| 132 | 160 | } |
| 133 | 161 | |
| 134 | −function playChord(position, volume) { | |
| 135 | − if (soundPlaying.isPlaying) { | |
| 162 | +function stopAudio(killInjector) { | |
| 163 | + if (audioInjector && audioInjector.isPlaying) { | |
| 136 | 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 | 185 | function keyPressEvent(event) { |
| @@ -154,15 +187,19 @@ function keyPressEvent(event) { · 1 unmodified line | ||
| 154 | 187 | keyVolume = 0.4; |
| 155 | 188 | if (event.text == "1") { |
| 156 | 189 | whichChord = 1; |
| 190 | + stopAudio(true); | |
| 157 | 191 | playChord(MyAvatar.position, keyVolume); |
| 158 | 192 | } else if (event.text == "2") { |
| 159 | 193 | whichChord = 2; |
| 194 | + stopAudio(true); | |
| 160 | 195 | playChord(MyAvatar.position, keyVolume); |
| 161 | 196 | } else if (event.text == "3") { |
| 162 | 197 | whichChord = 3; |
| 198 | + stopAudio(true); | |
| 163 | 199 | playChord(MyAvatar.position, keyVolume); |
| 164 | 200 | } else if (event.text == "4") { |
| 165 | 201 | whichChord = 4; |
| 202 | + stopAudio(true); | |
| 166 | 203 | playChord(MyAvatar.position, keyVolume); |
| 167 | 204 | } |
| 168 | 205 | } |
| @@ -170,6 +207,7 @@ function keyPressEvent(event) { · 1 unmodified line | ||
| 170 | 207 | function scriptEnding() { |
| 171 | 208 | MyAvatar.detachOne(guitarModel); |
| 172 | 209 | } |
| 210 | + | |
| 173 | 211 | // Connect a call back that happens every frame |
| 174 | 212 | Script.update.connect(checkHands); |
| 175 | 213 | Script.scriptEnding.connect(scriptEnding); |
| @@ -43,7 +43,8 @@ strokeSpeed[1] = 0.0; · 42 unmodified lines | ||
| 43 | 43 | |
| 44 | 44 | function checkSticks(deltaTime) { |
| 45 | 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 | 48 | var speed = length(palmVelocity); |
| 48 | 49 | |
| 49 | 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 | 65 | if ((palmVelocity.y > 0.0) || (speed < STOP_SPEED)) { |
| 65 | 66 | state[palm] = 0; |
| 66 | 67 | |
| 67 | − var options = { position: Controller.getSpatialControlPosition(palm * 2 + 1) }; | |
| 68 | + var options = { position: handPose.translation }; | |
| 68 | 69 | |
| 69 | 70 | if (strokeSpeed[palm] > 1.0) { strokeSpeed[palm] = 1.0; } |
| 70 | 71 | options.volume = strokeSpeed[palm]; |
| @@ -130,29 +130,38 @@ function Hand(name, palm, tip, forwardButton, button3, trigger) { · 129 unmodified lines | ||
| 130 | 130 | this.trigger = trigger; |
| 131 | 131 | this.holdingFrisbee = false; |
| 132 | 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 | 137 | this.grabButtonPressed = function() { |
| 135 | 138 | return ( |
| 136 | − Controller.isButtonPressed(this.forwardButton) || | |
| 137 | − Controller.isButtonPressed(this.button3) || | |
| 138 | − Controller.getTriggerValue(this.trigger) > 0.5 | |
| 139 | + Controller.getValue(this.forwardButton) || | |
| 140 | + Controller.getValue(this.button3) || | |
| 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 | 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 | 151 | q = Quat.multiply(MyAvatar.orientation, q); |
| 145 | 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 | 159 | function MouseControl(button) { |
| 151 | 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); | |
| 155 | −var rightHand = new Hand("RIGHT", RIGHT_PALM, RIGHT_TIP, RIGHT_BUTTON_FWD, RIGHT_BUTTON_3, 1); | |
| 163 | +var leftHand = new Hand("LEFT", LEFT_PALM, LEFT_TIP, Controller.Standard.LB, Controller.Standard.LeftPrimaryThumb, Controller.Standard.LT); | |
| 164 | +var rightHand = new Hand("RIGHT", RIGHT_PALM, RIGHT_TIP, Controller.Standard.RB, Controller.Standard.RightPrimaryThumb, Controller.Standard.RT); | |
| 156 | 165 | |
| 157 | 166 | var leftMouseControl = new MouseControl("LEFT"); |
| 158 | 167 | var middleMouseControl = new MouseControl("MIDDLE"); |
| @@ -302,12 +311,7 @@ function initToolBar() { · 143 unmodified lines | ||
| 302 | 311 | } |
| 303 | 312 | |
| 304 | 313 | function hydraCheck() { |
| 305 | − var numberOfButtons = Controller.getNumberOfButtons(); | |
| 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; | |
| 314 | + return Controller.Hardware.Hydra !== undefined; | |
| 311 | 315 | } |
| 312 | 316 | |
| 313 | 317 | function checkController(deltaTime) { |
| @@ -15,24 +15,64 @@ · 14 unmodified lines | ||
| 15 | 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 | 25 | HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; |
| 19 | 26 | |
| 20 | −var RED = { red: 255, green: 0, blue: 0 }; | |
| 21 | 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 | 61 | var pointer = []; |
| 62 | + | |
| 24 | 63 | pointer.push(Overlays.addOverlay("line3d", { |
| 25 | − start: { x: 0, y: 0, z: 0 }, | |
| 26 | − end: { x: 0, y: 0, z: 0 }, | |
| 27 | − color: RED, | |
| 64 | + start: ZERO_VECTOR, | |
| 65 | + end: ZERO_VECTOR, | |
| 66 | + color: COLORS.RED, | |
| 28 | 67 | alpha: 1, |
| 29 | 68 | visible: true, |
| 30 | 69 | lineWidth: LASER_WIDTH |
| 31 | 70 | })); |
| 71 | + | |
| 32 | 72 | pointer.push(Overlays.addOverlay("line3d", { |
| 33 | − start: { x: 0, y: 0, z: 0 }, | |
| 34 | − end: { x: 0, y: 0, z: 0 }, | |
| 35 | − color: RED, | |
| 73 | + start: ZERO_VECTOR, | |
| 74 | + end: ZERO_VECTOR, | |
| 75 | + color: COLORS.RED, | |
| 36 | 76 | alpha: 1, |
| 37 | 77 | visible: true, |
| 38 | 78 | lineWidth: LASER_WIDTH |
| @@ -42,135 +82,116 @@ function getRandomFloat(min, max) { · 3 unmodified lines | ||
| 42 | 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 | 85 | var showScore = false; |
| 61 | − | |
| 62 | − | |
| 63 | −// Load some sound to use for loading and firing | |
| 86 | +// Load some sound to use for loading and firing | |
| 64 | 87 | var fireSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/GUN-SHOT2.raw"); |
| 65 | 88 | var loadSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/Gun_Reload_Weapon22.raw"); |
| 66 | 89 | var impactSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/BulletImpact2.raw"); |
| 67 | 90 | var targetHitSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/hit.raw"); |
| 68 | 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 | 93 | var audioOptions = { |
| 73 | − volume: 0.9 | |
| 94 | + volume: 0.9 | |
| 74 | 95 | } |
| 75 | 96 | |
| 76 | 97 | var shotsFired = 0; |
| 77 | −var shotTime = new Date(); | |
| 78 | − | |
| 79 | −var activeControllers = 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; | |
| 98 | +var shotTime = new Date(); | |
| 99 | +var isLaunchButtonPressed = false; | |
| 100 | +var score = 0; | |
| 90 | 101 | |
| 91 | 102 | var bulletID = false; |
| 92 | 103 | var targetID = false; |
| 93 | 104 | |
| 94 | −// Create overlay buttons and reticle | |
| 95 | − | |
| 105 | +// Create overlay buttons and reticle | |
| 96 | 106 | var BUTTON_SIZE = 32; |
| 97 | 107 | var PADDING = 3; |
| 98 | 108 | var NUM_BUTTONS = 3; |
| 99 | − | |
| 100 | 109 | var screenSize = Controller.getViewportDimensions(); |
| 101 | 110 | var startX = screenSize.x / 2 - (NUM_BUTTONS * (BUTTON_SIZE + PADDING)) / 2; |
| 102 | −Script.include(["../../libraries/toolBars.js"]); | |
| 103 | −var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL, "highfidelity.gun.toolbar", function (screenSize) { | |
| 111 | +var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL, "highfidelity.gun.toolbar", function(screenSize) { | |
| 104 | 112 | return { |
| 105 | 113 | x: startX, |
| 106 | 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 | 118 | var offButton = toolBar.addOverlay("image", { |
| 119 | − width: BUTTON_SIZE, | |
| 120 | − height: BUTTON_SIZE, | |
| 121 | − imageURL: HIFI_PUBLIC_BUCKET + "images/gun/close.svg", | |
| 122 | − alpha: 1 | |
| 123 | − }); | |
| 119 | + width: BUTTON_SIZE, | |
| 120 | + height: BUTTON_SIZE, | |
| 121 | + imageURL: HIFI_PUBLIC_BUCKET + "images/gun/close.svg", | |
| 122 | + alpha: 1 | |
| 123 | +}); | |
| 124 | 124 | |
| 125 | 125 | startX += BUTTON_SIZE + PADDING; |
| 126 | 126 | var platformButton = toolBar.addOverlay("image", { |
| 127 | − x: startX, | |
| 128 | − y: screenSize.y - (BUTTON_SIZE + PADDING), | |
| 129 | − width: BUTTON_SIZE, | |
| 130 | − height: BUTTON_SIZE, | |
| 131 | − imageURL: HIFI_PUBLIC_BUCKET + "images/gun/platform-targets.svg", | |
| 132 | − alpha: 1 | |
| 133 | − }); | |
| 127 | + x: startX, | |
| 128 | + y: screenSize.y - (BUTTON_SIZE + PADDING), | |
| 129 | + width: BUTTON_SIZE, | |
| 130 | + height: BUTTON_SIZE, | |
| 131 | + imageURL: HIFI_PUBLIC_BUCKET + "images/gun/platform-targets.svg", | |
| 132 | + alpha: 1 | |
| 133 | +}); | |
| 134 | 134 | |
| 135 | 135 | startX += BUTTON_SIZE + PADDING; |
| 136 | 136 | var gridButton = toolBar.addOverlay("image", { |
| 137 | − x: startX, | |
| 138 | − y: screenSize.y - (BUTTON_SIZE + PADDING), | |
| 139 | − width: BUTTON_SIZE, | |
| 140 | − height: BUTTON_SIZE, | |
| 141 | − imageURL: HIFI_PUBLIC_BUCKET + "images/gun/floating-targets.svg", | |
| 142 | − alpha: 1 | |
| 143 | − }); | |
| 137 | + x: startX, | |
| 138 | + y: screenSize.y - (BUTTON_SIZE + PADDING), | |
| 139 | + width: BUTTON_SIZE, | |
| 140 | + height: BUTTON_SIZE, | |
| 141 | + imageURL: HIFI_PUBLIC_BUCKET + "images/gun/floating-targets.svg", | |
| 142 | + alpha: 1 | |
| 143 | +}); | |
| 144 | 144 | |
| 145 | 145 | if (showScore) { |
| 146 | 146 | var text = Overlays.addOverlay("text", { |
| 147 | − x: screenSize.x / 2 - 100, | |
| 148 | − y: screenSize.y / 2 - 50, | |
| 149 | − width: 150, | |
| 150 | − height: 50, | |
| 151 | − color: { red: 0, green: 0, blue: 0}, | |
| 152 | − textColor: { red: 255, green: 0, blue: 0}, | |
| 153 | − topMargin: 4, | |
| 154 | − leftMargin: 4, | |
| 155 | − text: "Score: " + score | |
| 156 | − }); | |
| 147 | + x: screenSize.x / 2 - 100, | |
| 148 | + y: screenSize.y / 2 - 50, | |
| 149 | + width: 150, | |
| 150 | + height: 50, | |
| 151 | + color: { | |
| 152 | + red: 0, | |
| 153 | + green: 0, | |
| 154 | + blue: 0 | |
| 155 | + }, | |
| 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 | 167 | function entityCollisionWithEntity(entity1, entity2, collision) { |
| 162 | 168 | if (entity2 === targetID) { |
| 163 | 169 | score++; |
| 164 | 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 | 178 | Script.setTimeout(deleteBulletAndTarget, 500); |
| 170 | 179 | |
| 171 | 180 | // Turn the target and the bullet white |
| 172 | − Entities.editEntity(entity1, { color: { red: 255, green: 255, blue: 255 }}); | |
| 173 | − Entities.editEntity(entity2, { color: { red: 255, green: 255, blue: 255 }}); | |
| 181 | + Entities.editEntity(entity1, { | |
| 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 | 196 | // play the sound near the camera so the shooter can hear it |
| 176 | 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 | 210 | var bVelocity = grenade ? Vec3.multiply(GRENADE_VELOCITY, Vec3.normalize(velocity)) : velocity; |
| 190 | 211 | var bSize = grenade ? GRENADE_SIZE : BULLET_SIZE; |
| 191 | − var bGravity = grenade ? GRENADE_GRAVITY : BULLET_GRAVITY; | |
| 192 | − | |
| 193 | − bulletID = Entities.addEntity( | |
| 194 | − { type: "Sphere", | |
| 195 | − position: position, | |
| 196 | − dimensions: { x: bSize, y: bSize, z: bSize }, | |
| 197 | − color: { red: 0, green: 0, blue: 0 }, | |
| 198 | − velocity: bVelocity, | |
| 199 | − lifetime: BULLET_LIFETIME, | |
| 200 | − gravity: { x: 0, y: bGravity, z: 0 }, | |
| 201 | − damping: 0.01, | |
| 202 | − density: 8000, | |
| 203 | − ignoreCollisions: false, | |
| 204 | − collisionsWillMove: true | |
| 205 | − }); | |
| 212 | + var bGravity = grenade ? GRENADE_GRAVITY : BULLET_GRAVITY; | |
| 213 | + | |
| 214 | + bulletID = Entities.addEntity({ | |
| 215 | + type: "Sphere", | |
| 216 | + position: position, | |
| 217 | + dimensions: { | |
| 218 | + x: bSize, | |
| 219 | + y: bSize, | |
| 220 | + z: bSize | |
| 221 | + }, | |
| 222 | + color: { | |
| 223 | + red: 0, | |
| 224 | + green: 0, | |
| 225 | + blue: 0 | |
| 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 | 240 | Script.addEventHandler(bulletID, "collisionWithEntity", entityCollisionWithEntity); |
| 207 | 241 | |
| 208 | − // Play firing sounds | |
| 209 | − audioOptions.position = position; | |
| 242 | + // Play firing sounds | |
| 243 | + audioOptions.position = position; | |
| 210 | 244 | Audio.playSound(fireSound, audioOptions); |
| 211 | 245 | shotsFired++; |
| 212 | 246 | if ((shotsFired % RELOAD_INTERVAL) == 0) { |
| 213 | 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 | 251 | function shootTarget() { |
| 227 | 252 | var TARGET_SIZE = 0.50; |
| 228 | 253 | var TARGET_GRAVITY = 0.0; |
| @@ -232,95 +257,152 @@ function shootTarget() { · 3 unmodified lines | ||
| 232 | 257 | var DISTANCE_TO_LAUNCH_FROM = 5.0; |
| 233 | 258 | var ANGLE_RANGE_FOR_LAUNCH = 20.0; |
| 234 | 259 | var camera = Camera.getPosition(); |
| 235 | − | |
| 236 | − var targetDirection = Quat.angleAxis(getRandomFloat(-ANGLE_RANGE_FOR_LAUNCH, ANGLE_RANGE_FOR_LAUNCH), { x:0, y:1, z:0 }); | |
| 260 | + | |
| 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 | 266 | targetDirection = Quat.multiply(Camera.getOrientation(), targetDirection); |
| 238 | 267 | var forwardVector = Quat.getFront(targetDirection); |
| 239 | − | |
| 268 | + | |
| 240 | 269 | var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_TO_LAUNCH_FROM)); |
| 241 | 270 | |
| 242 | 271 | var velocity = Vec3.multiply(forwardVector, TARGET_FWD_VELOCITY); |
| 243 | 272 | velocity.y += TARGET_UP_VELOCITY; |
| 244 | 273 | |
| 245 | − targetID = Entities.addEntity( | |
| 246 | − { type: "Box", | |
| 247 | − 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 }, | |
| 249 | − color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 }, | |
| 250 | − velocity: velocity, | |
| 251 | − gravity: { x: 0, y: TARGET_GRAVITY, z: 0 }, | |
| 252 | − lifetime: TARGET_LIFETIME, | |
| 253 | − rotation: Camera.getOrientation(), | |
| 254 | − damping: 0.1, | |
| 255 | − density: 100.0, | |
| 256 | − collisionsWillMove: true }); | |
| 257 | − | |
| 258 | − // Record start time | |
| 274 | + targetID = Entities.addEntity({ | |
| 275 | + type: "Box", | |
| 276 | + position: newPosition, | |
| 277 | + dimensions: { | |
| 278 | + x: TARGET_SIZE * (0.5 + Math.random()), | |
| 279 | + y: TARGET_SIZE * (0.5 + Math.random()), | |
| 280 | + z: TARGET_SIZE * (0.5 + Math.random()) / 4.0 | |
| 281 | + }, | |
| 282 | + color: { | |
| 283 | + red: Math.random() * 255, | |
| 284 | + green: Math.random() * 255, | |
| 285 | + blue: Math.random() * 255 | |
| 286 | + }, | |
| 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 | 301 | shotTime = new Date(); |
| 260 | 302 | |
| 261 | 303 | // Play target shoot sound |
| 262 | − audioOptions.position = newPosition; | |
| 304 | + audioOptions.position = newPosition; | |
| 263 | 305 | Audio.playSound(targetLaunchSound, audioOptions); |
| 264 | 306 | } |
| 265 | 307 | |
| 266 | 308 | function makeGrid(type, scale, size) { |
| 267 | − var separation = scale * 2; | |
| 309 | + var separation = scale * 2; | |
| 268 | 310 | var pos = Vec3.sum(Camera.getPosition(), Vec3.multiply(10.0 * scale * separation, Quat.getFront(Camera.getOrientation()))); |
| 269 | 311 | var x, y, z; |
| 270 | − var GRID_LIFE = 60.0; | |
| 271 | − var dimensions; | |
| 312 | + var GRID_LIFE = 60.0; | |
| 313 | + var dimensions; | |
| 272 | 314 | |
| 273 | 315 | for (x = 0; x < size; x++) { |
| 274 | 316 | for (y = 0; y < size; y++) { |
| 275 | 317 | for (z = 0; z < size; z++) { |
| 276 | − | |
| 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 }; | |
| 278 | − | |
| 279 | − Entities.addEntity( | |
| 280 | − { type: type, | |
| 281 | − position: { x: pos.x + x * separation, y: pos.y + y * separation, z: pos.z + z * separation }, | |
| 282 | − dimensions: dimensions, | |
| 283 | − color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 }, | |
| 284 | − velocity: { x: 0, y: 0, z: 0 }, | |
| 285 | − gravity: { x: 0, y: 0, z: 0 }, | |
| 286 | − lifetime: GRID_LIFE, | |
| 287 | − rotation: Camera.getOrientation(), | |
| 288 | − damping: 0.1, | |
| 289 | − density: 100.0, | |
| 290 | − collisionsWillMove: true }); | |
| 318 | + | |
| 319 | + dimensions = { | |
| 320 | + x: separation / 2.0 * (0.5 + Math.random()), | |
| 321 | + y: separation / 2.0 * (0.5 + Math.random()), | |
| 322 | + z: separation / 2.0 * (0.5 + Math.random()) / 4.0 | |
| 323 | + }; | |
| 324 | + | |
| 325 | + Entities.addEntity({ | |
| 326 | + type: type, | |
| 327 | + position: { | |
| 328 | + x: pos.x + x * separation, | |
| 329 | + y: pos.y + y * separation, | |
| 330 | + z: pos.z + z * separation | |
| 331 | + }, | |
| 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 | 359 | function makePlatform(gravity, scale, size) { |
| 297 | − var separation = scale * 2; | |
| 360 | + var separation = scale * 2; | |
| 298 | 361 | var pos = Vec3.sum(Camera.getPosition(), Vec3.multiply(10.0 * scale * separation, Quat.getFront(Camera.getOrientation()))); |
| 299 | 362 | pos.y -= separation * size; |
| 300 | 363 | var x, y, z; |
| 301 | − var TARGET_LIFE = 60.0; | |
| 364 | + var TARGET_LIFE = 60.0; | |
| 302 | 365 | var INITIAL_GAP = 0.5; |
| 303 | − var dimensions; | |
| 366 | + var dimensions; | |
| 304 | 367 | |
| 305 | 368 | for (x = 0; x < size; x++) { |
| 306 | 369 | for (y = 0; y < size; y++) { |
| 307 | 370 | for (z = 0; z < size; z++) { |
| 308 | 371 | |
| 309 | − dimensions = { x: separation/2.0, y: separation, z: separation/2.0 }; | |
| 310 | − | |
| 311 | − Entities.addEntity( | |
| 312 | − { type: "Box", | |
| 313 | − position: { x: pos.x - (separation * size / 2.0) + x * separation, | |
| 314 | − y: pos.y + y * (separation + INITIAL_GAP), | |
| 315 | − z: pos.z - (separation * size / 2.0) + z * separation }, | |
| 316 | − dimensions: dimensions, | |
| 317 | − color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 }, | |
| 318 | − velocity: { x: 0, y: 0.05, z: 0 }, | |
| 319 | − gravity: { x: 0, y: gravity, z: 0 }, | |
| 320 | − lifetime: TARGET_LIFE, | |
| 321 | − damping: 0.1, | |
| 322 | − density: 100.0, | |
| 323 | − collisionsWillMove: true }); | |
| 372 | + dimensions = { | |
| 373 | + x: separation / 2.0, | |
| 374 | + y: separation, | |
| 375 | + z: separation / 2.0 | |
| 376 | + }; | |
| 377 | + | |
| 378 | + Entities.addEntity({ | |
| 379 | + type: "Box", | |
| 380 | + position: { | |
| 381 | + x: pos.x - (separation * size / 2.0) + x * separation, | |
| 382 | + y: pos.y + y * (separation + INITIAL_GAP), | |
| 383 | + z: pos.z - (separation * size / 2.0) + z * separation | |
| 384 | + }, | |
| 385 | + dimensions: dimensions, | |
| 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 | 410 | // Make a floor for this stuff to fall onto |
| 329 | 411 | Entities.addEntity({ |
| 330 | 412 | type: "Box", |
| 331 | − position: { x: pos.x, y: pos.y - separation / 2.0, z: pos.z }, | |
| 332 | − dimensions: { x: 2.0 * separation * size, y: separation / 2.0, z: 2.0 * separation * size }, | |
| 333 | − color: { red: 100, green: 100, blue: 100 }, | |
| 413 | + position: { | |
| 414 | + x: pos.x, | |
| 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 | 428 | lifetime: TARGET_LIFE |
| 335 | 429 | }); |
| 336 | 430 | |
| @@ -340,153 +434,79 @@ function keyPressEvent(event) { · 3 unmodified lines | ||
| 340 | 434 | // if our tools are off, then don't do anything |
| 341 | 435 | if (event.text == "t") { |
| 342 | 436 | var time = MIN_THROWER_DELAY + Math.random() * MAX_THROWER_DELAY; |
| 343 | − Script.setTimeout(shootTarget, time); | |
| 437 | + Script.setTimeout(shootTarget, time); | |
| 344 | 438 | } else if ((event.text == ".") || (event.text == "SPACE")) { |
| 345 | 439 | shootFromMouse(false); |
| 346 | 440 | } else if (event.text == ",") { |
| 347 | 441 | shootFromMouse(true); |
| 348 | 442 | } else if (event.text == "r") { |
| 349 | 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 | 447 | function playLoadSound() { |
| 359 | − audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); | |
| 448 | + audioOptions.position = MyAvatar.leftHandPose.translation; | |
| 360 | 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 | 452 | function deleteBulletAndTarget() { |
| 372 | 453 | Entities.deleteEntity(bulletID); |
| 373 | 454 | Entities.deleteEntity(targetID); |
| 374 | − bulletID = false; | |
| 375 | − 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 | − } | |
| 455 | + bulletID = false; | |
| 456 | + targetID = false; | |
| 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 | 459 | function update(deltaTime) { |
| 394 | − if (activeControllers == 0) { | |
| 395 | − if (Controller.getNumberOfSpatialControls() > 0) { | |
| 396 | − activeControllers = Controller.getNumberOfSpatialControls(); | |
| 397 | − clearPose(); | |
| 398 | − } | |
| 399 | − } | |
| 400 | − | |
| 401 | − var KICKBACK_DECAY_RATE = 0.125; | |
| 402 | − if (elbowKickAngle > 0.0) { | |
| 403 | − if (elbowKickAngle > 0.5) { | |
| 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 | |
| 460 | + // FIXME we should also expose MyAvatar.handPoses[2], MyAvatar.tipPoses[2] | |
| 461 | + var tipPoses = [ MyAvatar.leftHandTipPose, MyAvatar.rightHandTipPose ]; | |
| 462 | + | |
| 463 | + for (var side = 0; side < 2; side++) { | |
| 464 | + // First check if the controller is valid | |
| 465 | + var controllerPose = Controller.getPoseValue(POSE_CONTROLS[side]); | |
| 466 | + validPoses[side] = controllerPose.valid; | |
| 467 | + if (!controllerPose.valid) { | |
| 468 | + Overlays.editOverlay(pointer[side], { | |
| 469 | + visible: false | |
| 451 | 470 | }); |
| 452 | − | |
| 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 | − } | |
| 471 | + continue; | |
| 469 | 472 | } |
| 470 | − } | |
| 471 | −} | |
| 472 | 473 | |
| 473 | −function shootFromMouse(grenade) { | |
| 474 | − var DISTANCE_FROM_CAMERA = 1.0; | |
| 475 | − var camera = Camera.getPosition(); | |
| 476 | − var forwardVector = Quat.getFront(Camera.getOrientation()); | |
| 477 | − var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_FROM_CAMERA)); | |
| 478 | − var velocity = Vec3.multiply(forwardVector, BULLET_VELOCITY); | |
| 479 | − shootBullet(newPosition, velocity, grenade); | |
| 474 | + // Need to adjust the laser | |
| 475 | + var tipPose = tipPoses[side]; | |
| 476 | + var handRotation = tipPoses[side].rotation; | |
| 477 | + var barrelOffset = Vec3.multiplyQbyV(handRotation, BARREL_OFFSETS[side]); | |
| 478 | + barrelTips[side] = Vec3.sum(tipPose.translation, barrelOffset); | |
| 479 | + barrelVectors[side] = Vec3.multiplyQbyV(handRotation, { | |
| 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) { | |
| 483 | − // position | |
| 484 | − isMouseDown = false; | |
| 496 | +function triggerChanged(side, value) { | |
| 497 | + var pressed = (value != 0); | |
| 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 | 505 | function mousePressEvent(event) { |
| 488 | − var clickedText = false; | |
| 489 | − var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); | |
| 506 | + var clickedOverlay = Overlays.getOverlayAtPoint({ | |
| 507 | + x: event.x, | |
| 508 | + y: event.y | |
| 509 | + }); | |
| 490 | 510 | if (clickedOverlay == offButton) { |
| 491 | 511 | Script.stop(); |
| 492 | 512 | } else if (clickedOverlay == platformButton) { |
| @@ -494,25 +514,37 @@ function mousePressEvent(event) { · 1 unmodified line | ||
| 494 | 514 | makePlatform(-9.8, 1.0, platformSize); |
| 495 | 515 | } else if (clickedOverlay == gridButton) { |
| 496 | 516 | makeGrid("Box", 1.0, 3); |
| 497 | − } | |
| 517 | + } | |
| 498 | 518 | } |
| 499 | 519 | |
| 500 | 520 | function scriptEnding() { |
| 501 | − Overlays.deleteOverlay(reticle); | |
| 521 | + mapping.disable(); | |
| 502 | 522 | toolBar.cleanup(); |
| 503 | − Overlays.deleteOverlay(pointer[0]); | |
| 504 | − Overlays.deleteOverlay(pointer[1]); | |
| 523 | + for (var i = 0; i < pointer.length; ++i) { | |
| 524 | + Overlays.deleteOverlay(pointer[i]); | |
| 525 | + } | |
| 505 | 526 | Overlays.deleteOverlay(text); |
| 506 | − MyAvatar.detachOne(gunModel); | |
| 507 | − MyAvatar.detachOne(gunModel); | |
| 527 | + MyAvatar.detachOne(GUN_MODEL); | |
| 528 | + MyAvatar.detachOne(GUN_MODEL); | |
| 508 | 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 | 547 | Script.scriptEnding.connect(scriptEnding); |
| 512 | 548 | Script.update.connect(update); |
| 513 | −Controller.mouseReleaseEvent.connect(mouseReleaseEvent); | |
| 514 | 549 | Controller.mousePressEvent.connect(mousePressEvent); |
| 515 | 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 | 20 | var PADDLE_SIZE = 0.20; |
| 21 | 21 | var PADDLE_THICKNESS = 0.06; |
| 22 | 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 | 24 | var LINE_COLOR = { red: 255, green: 255, blue: 0 }; |
| 25 | 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 | 28 | var HOLD_POSITION_LEFT_OFFSET = { x: -0.15, y: 0.05, z: -0.05 }; |
| 28 | 29 | var HOLD_POSITION_RIGHT_OFFSET = { x: -0.15, y: 0.05, z: 0.05 }; |
| 29 | 30 | var PADDLE_ORIENTATION = Quat.fromPitchYawRollDegrees(0,0,0); |
| @@ -32,18 +33,7 @@ var SPRING_FORCE = 15.0; · 2 unmodified lines | ||
| 32 | 33 | var lastSoundTime = 0; |
| 33 | 34 | var gameOn = false; |
| 34 | 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 | 37 | Menu.addMenu("PaddleBall"); |
| 48 | 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 | 53 | function createEntities() { |
| 64 | 54 | ball = Entities.addEntity( |
| 65 | 55 | { type: "Sphere", |
| 66 | − position: Controller.getSpatialControlPosition(controllerID), | |
| 56 | + position: leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation, | |
| 67 | 57 | dimensions: { x: BALL_SIZE, y: BALL_SIZE, z: BALL_SIZE }, |
| 68 | 58 | color: BALL_COLOR, |
| 69 | 59 | gravity: { x: 0, y: GRAVITY, z: 0 }, |
| @@ -73,28 +63,28 @@ function createEntities() { · 3 unmodified lines | ||
| 73 | 63 | |
| 74 | 64 | paddle = Entities.addEntity( |
| 75 | 65 | { type: "Box", |
| 76 | − position: Controller.getSpatialControlPosition(controllerID), | |
| 66 | + position: leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation, | |
| 77 | 67 | dimensions: { x: PADDLE_SIZE, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 0.80 }, |
| 78 | 68 | color: PADDLE_COLOR, |
| 79 | 69 | gravity: { x: 0, y: 0, z: 0 }, |
| 80 | 70 | ignoreCollisions: false, |
| 81 | 71 | damping: 0.10, |
| 82 | 72 | visible: false, |
| 83 | − rotation: Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)), | |
| 84 | − collisionsWillMove: false }); | |
| 73 | + rotation : leftHanded ? MyAvatar.leftHandPose.rotation : MyAvatar.rightHandPose.rotation, | |
| 74 | + collisionsWillMove: false }); | |
| 85 | 75 | |
| 86 | 76 | modelURL = "http://public.highfidelity.io/models/attachments/pong_paddle.fbx"; |
| 87 | 77 | paddleModel = Entities.addEntity( |
| 88 | 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 | 80 | dimensions: { x: PADDLE_SIZE * 1.5, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 1.25 }, |
| 91 | 81 | color: PADDLE_COLOR, |
| 92 | 82 | gravity: { x: 0, y: 0, z: 0 }, |
| 93 | 83 | ignoreCollisions: true, |
| 94 | 84 | modelURL: modelURL, |
| 95 | 85 | damping: 0.10, |
| 96 | − rotation: Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)), | |
| 97 | − collisionsWillMove: false }); | |
| 86 | + rotation : leftHanded ? MyAvatar.leftHandPose.rotation : MyAvatar.rightHandPose.rotation, | |
| 87 | + collisionsWillMove: false }); | |
| 98 | 88 | |
| 99 | 89 | line = Overlays.addOverlay("line3d", { |
| 100 | 90 | start: { x: 0, y: 0, z: 0 }, |
| @@ -118,7 +108,7 @@ function deleteEntities() { · 17 unmodified lines | ||
| 118 | 108 | } |
| 119 | 109 | |
| 120 | 110 | function update(deltaTime) { |
| 121 | − var palmPosition = Controller.getSpatialControlPosition(controllerID); | |
| 111 | + var palmPosition = leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation; | |
| 122 | 112 | var controllerActive = (Vec3.length(palmPosition) > 0); |
| 123 | 113 | |
| 124 | 114 | if (!gameOn && controllerActive) { |
| @@ -133,8 +123,8 @@ function update(deltaTime) { · 8 unmodified lines | ||
| 133 | 123 | } |
| 134 | 124 | |
| 135 | 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); | |
| 137 | − var holdPosition = Vec3.sum(leftHanded ? MyAvatar.getLeftPalmPosition() : MyAvatar.getRightPalmPosition(), | |
| 126 | + var paddleWorldOrientation = Quat.multiply(leftHanded ? MyAvatar.leftHandPose.rotation : MyAvatar.rightHandPose.rotation, paddleOrientation); | |
| 127 | + var holdPosition = Vec3.sum(leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation, | |
| 138 | 128 | Vec3.multiplyQbyV(paddleWorldOrientation, leftHanded ? HOLD_POSITION_LEFT_OFFSET : HOLD_POSITION_RIGHT_OFFSET )); |
| 139 | 129 | |
| 140 | 130 | var props = Entities.getEntityProperties(ball); |
| @@ -146,10 +136,10 @@ function update(deltaTime) { · 5 unmodified lines | ||
| 146 | 136 | Entities.editEntity(ball, { velocity: ballVelocity }); |
| 147 | 137 | Overlays.editOverlay(line, { start: props.position, end: holdPosition }); |
| 148 | 138 | Entities.editEntity(paddle, { position: holdPosition, |
| 149 | − velocity: Controller.getSpatialControlVelocity(controllerID), | |
| 139 | + velocity: leftHanded ? MyAvatar.leftHandPose.velocity : MyAvatar.rightHandPose.velocity, | |
| 150 | 140 | rotation: paddleWorldOrientation }); |
| 151 | 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 | 143 | rotation: paddleWorldOrientation }); |
| 154 | 144 | |
| 155 | 145 | } |
| @@ -182,7 +172,6 @@ function menuItemEvent(menuItem) { · 26 unmodified lines | ||
| 182 | 172 | leftHanded = Menu.isOptionChecked("Left-Handed"); |
| 183 | 173 | } |
| 184 | 174 | if ((leftHanded != oldHanded) && gameOn) { |
| 185 | − setControllerID(); | |
| 186 | 175 | deleteEntities(); |
| 187 | 176 | createEntities(); |
| 188 | 177 | } |
| @@ -19,14 +19,7 @@ HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; · 18 unmodified lines | ||
| 19 | 19 | |
| 20 | 20 | // maybe we should make these constants... |
| 21 | 21 | var LEFT_PALM = 0; |
| 22 | −var LEFT_TIP = 1; | |
| 23 | −var LEFT_BUTTON_FWD = 5; | |
| 24 | −var LEFT_BUTTON_3 = 3; | |
| 25 | − | |
| 26 | 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 | 24 | var BALL_RADIUS = 0.08; |
| 32 | 25 | var GRAVITY_STRENGTH = 3.0; |
| @@ -69,9 +62,6 @@ function getBallHoldPosition(whichSide) { · 36 unmodified lines | ||
| 69 | 62 | } |
| 70 | 63 | |
| 71 | 64 | function checkControllerSide(whichSide) { |
| 72 | − var BUTTON_FWD; | |
| 73 | − var BUTTON_3; | |
| 74 | − var TRIGGER; | |
| 75 | 65 | var palmPosition; |
| 76 | 66 | var palmRotation; |
| 77 | 67 | var ballAlreadyInHand; |
| @@ -79,35 +69,35 @@ function checkControllerSide(whichSide) { · 1 unmodified line | ||
| 79 | 69 | var linearVelocity; |
| 80 | 70 | var angularVelocity; |
| 81 | 71 | var AVERAGE_FACTOR = 0.33; |
| 82 | − | |
| 72 | + var grabButtonPressed; | |
| 73 | + | |
| 83 | 74 | if (whichSide == LEFT_PALM) { |
| 84 | − BUTTON_FWD = LEFT_BUTTON_FWD; | |
| 85 | − BUTTON_3 = LEFT_BUTTON_3; | |
| 86 | − TRIGGER = 0; | |
| 87 | − palmPosition = Controller.getSpatialControlPosition(LEFT_PALM); | |
| 88 | − palmRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(LEFT_PALM)); | |
| 75 | + palmPosition = MyAvatar.leftHandPose.translation; | |
| 76 | + palmRotation = MyAvatar.leftHandPose.rotation; | |
| 89 | 77 | ballAlreadyInHand = leftBallAlreadyInHand; |
| 90 | 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 | 80 | Vec3.multiply(1.0 - AVERAGE_FACTOR, averageLinearVelocity[0])); |
| 81 | + | |
| 93 | 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 | 86 | } else { |
| 96 | − BUTTON_FWD = RIGHT_BUTTON_FWD; | |
| 97 | − BUTTON_3 = RIGHT_BUTTON_3; | |
| 98 | − TRIGGER = 1; | |
| 99 | − palmPosition = Controller.getSpatialControlPosition(RIGHT_PALM); | |
| 100 | − palmRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(RIGHT_PALM)); | |
| 87 | + palmPosition = MyAvatar.rightHandPose.translation; | |
| 88 | + palmRotation = MyAvatar.rightHandPose.rotation; | |
| 101 | 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 | 91 | Vec3.multiply(1.0 - AVERAGE_FACTOR, averageLinearVelocity[1])); |
| 92 | + | |
| 104 | 93 | linearVelocity = averageLinearVelocity[1]; |
| 105 | − angularVelocity = Vec3.multiplyQbyV(MyAvatar.orientation, Controller.getSpatialControlRawAngularVelocity(RIGHT_TIP)); | |
| 94 | + angularVelocity = MyAvatar.rightHandTipPose.angularVelocity; | |
| 106 | 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)); | |
| 110 | − | |
| 100 | + | |
| 111 | 101 | // If I don't currently have a ball in my hand, then try to catch closest one |
| 112 | 102 | if (!ballAlreadyInHand && grabButtonPressed) { |
| 113 | 103 | var closestEntity = Entities.findClosestEntity(palmPosition, targetRadius); |
| @@ -187,10 +177,8 @@ function checkControllerSide(whichSide) { · 73 unmodified lines | ||
| 187 | 177 | if (ballAlreadyInHand) { |
| 188 | 178 | if (whichSide == LEFT_PALM) { |
| 189 | 179 | handEntity = leftHandEntity; |
| 190 | − whichTip = LEFT_TIP; | |
| 191 | 180 | } else { |
| 192 | 181 | handEntity = rightHandEntity; |
| 193 | − whichTip = RIGHT_TIP; | |
| 194 | 182 | } |
| 195 | 183 | |
| 196 | 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 | 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); | |
| 249 | − checkControllerSide(RIGHT_PALM); | |
| 224 | + checkControllerSide(LEFT_PALM); | |
| 225 | + checkControllerSide(RIGHT_PALM); | |
| 250 | 226 | } |
| 251 | 227 | |
| 252 | 228 | |
| @@ -10,25 +10,19 @@ · 9 unmodified lines | ||
| 10 | 10 | // Distributed under the Apache License, Version 2.0. |
| 11 | 11 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html |
| 12 | 12 | // |
| 13 | − | |
| 14 | 13 | // initialize our triggers |
| 15 | 14 | var triggerPulled = new Array(); |
| 16 | −var numberOfTriggers = Controller.getNumberOfTriggers(); | |
| 17 | −for (t = 0; t < numberOfTriggers; t++) { | |
| 15 | +var NUMBER_OF_TRIGGERS = 2; | |
| 16 | +for (t = 0; t < NUMBER_OF_TRIGGERS; t++) { | |
| 18 | 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 | 22 | function checkController(deltaTime) { |
| 22 | − var numberOfTriggers = Controller.getNumberOfTriggers(); | |
| 23 | − var numberOfSpatialControls = Controller.getNumberOfSpatialControls(); | |
| 24 | − var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers; | |
| 25 | 23 | var triggerToggled = false; |
| 26 | − | |
| 27 | − // this is expected for hydras | |
| 28 | − if (numberOfTriggers == 2 && controllersPerTrigger == 2) { | |
| 29 | − for (var t = 0; t < numberOfTriggers; t++) { | |
| 30 | − var triggerValue = Controller.getTriggerValue(t); | |
| 31 | − | |
| 24 | + for (var t = 0; t < NUMBER_OF_TRIGGERS; t++) { | |
| 25 | + var triggerValue = Controller.getValue(triggers[t]); | |
| 32 | 26 | if (triggerPulled[t]) { |
| 33 | 27 | // must release to at least 0.1 |
| 34 | 28 | if (triggerValue < 0.1) { |
| @@ -41,17 +35,14 @@ function checkController(deltaTime) { · 6 unmodified lines | ||
| 41 | 35 | triggerToggled = true; |
| 42 | 36 | } |
| 43 | 37 | } |
| 44 | − | |
| 45 | 38 | if (triggerToggled) { |
| 46 | 39 | print("a trigger was toggled"); |
| 47 | 40 | } |
| 48 | 41 | } |
| 49 | − } | |
| 42 | + | |
| 50 | 43 | } |
| 51 | − | |
| 52 | 44 | // register the call back so it fires before each data send |
| 53 | 45 | Script.update.connect(checkController); |
| 54 | − | |
| 55 | 46 | function printKeyEvent(eventName, event) { |
| 56 | 47 | print(eventName); |
| 57 | 48 | print(" event.key=" + event.key); |
| @@ -64,7 +55,6 @@ function printKeyEvent(eventName, event) { · 6 unmodified lines | ||
| 64 | 55 | } |
| 65 | 56 | function keyPressEvent(event) { |
| 66 | 57 | printKeyEvent("keyPressEvent", event); |
| 67 | − | |
| 68 | 58 | if (event.text == "A") { |
| 69 | 59 | print("the A key was pressed"); |
| 70 | 60 | } |
| @@ -72,10 +62,8 @@ function keyPressEvent(event) { · 1 unmodified line | ||
| 72 | 62 | print("the <space> key was pressed"); |
| 73 | 63 | } |
| 74 | 64 | } |
| 75 | − | |
| 76 | 65 | function keyReleaseEvent(event) { |
| 77 | 66 | printKeyEvent("keyReleaseEvent", event); |
| 78 | − | |
| 79 | 67 | if (event.text == "A") { |
| 80 | 68 | print("the A key was released"); |
| 81 | 69 | } |
| @@ -83,11 +71,9 @@ function keyReleaseEvent(event) { · 1 unmodified line | ||
| 83 | 71 | print("the <space> key was pressed"); |
| 84 | 72 | } |
| 85 | 73 | } |
| 86 | − | |
| 87 | 74 | // Map keyPress and mouse move events to our callbacks |
| 88 | 75 | Controller.keyPressEvent.connect(keyPressEvent); |
| 89 | 76 | Controller.keyReleaseEvent.connect(keyReleaseEvent); |
| 90 | − | |
| 91 | 77 | // prevent the A key from going through to the application |
| 92 | 78 | Controller.captureKeyEvents({ text: "A" }); |
| 93 | 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 | 81 | Controller.captureKeyEvents({ text: "@", isMeta: true }); |
| 96 | 82 | Controller.captureKeyEvents({ text: "page up" }); |
| 97 | 83 | Controller.captureKeyEvents({ text: "page down" }); |
| 98 | − | |
| 99 | − | |
| 100 | 84 | function printMouseEvent(eventName, event) { |
| 101 | 85 | print(eventName); |
| 102 | 86 | print(" event.x,y=" + event.x + ", " + event.y); |
| @@ -109,22 +93,18 @@ function printMouseEvent(eventName, event) { · 6 unmodified lines | ||
| 109 | 93 | print(" event.isMeta=" + event.isMeta); |
| 110 | 94 | print(" event.isAlt=" + event.isAlt); |
| 111 | 95 | } |
| 112 | − | |
| 113 | 96 | function mouseMoveEvent(event) { |
| 114 | 97 | printMouseEvent("mouseMoveEvent", event); |
| 115 | 98 | } |
| 116 | 99 | function mousePressEvent(event) { |
| 117 | 100 | printMouseEvent("mousePressEvent", event); |
| 118 | 101 | } |
| 119 | − | |
| 120 | 102 | function mouseReleaseEvent(event) { |
| 121 | 103 | printMouseEvent("mouseReleaseEvent", event); |
| 122 | 104 | } |
| 123 | − | |
| 124 | 105 | Controller.mouseMoveEvent.connect(mouseMoveEvent); |
| 125 | 106 | Controller.mousePressEvent.connect(mousePressEvent); |
| 126 | 107 | Controller.mouseReleaseEvent.connect(mouseReleaseEvent); |
| 127 | − | |
| 128 | 108 | function printTouchEvent(eventName, event) { |
| 129 | 109 | print(eventName); |
| 130 | 110 | |
| @@ -143,7 +123,6 @@ function printTouchEvent(eventName, event) { · 12 unmodified lines | ||
| 143 | 123 | print(" event.radius=" + event.radius); |
| 144 | 124 | print(" event.isPinching=" + event.isPinching); |
| 145 | 125 | print(" event.isPinchOpening=" + event.isPinchOpening); |
| 146 | − | |
| 147 | 126 | print(" event.angle=" + event.angle); |
| 148 | 127 | for (var i = 0; i < event.points.length; i++) { |
| 149 | 128 | print(" event.angles[" + i + "]:" + event.angles[i]); |
| @@ -151,15 +130,12 @@ function printTouchEvent(eventName, event) { · 1 unmodified line | ||
| 151 | 130 | print(" event.isRotating=" + event.isRotating); |
| 152 | 131 | print(" event.rotating=" + event.rotating); |
| 153 | 132 | } |
| 154 | − | |
| 155 | 133 | function touchBeginEvent(event) { |
| 156 | 134 | printTouchEvent("touchBeginEvent", event); |
| 157 | 135 | } |
| 158 | − | |
| 159 | 136 | function touchUpdateEvent(event) { |
| 160 | 137 | printTouchEvent("touchUpdateEvent", event); |
| 161 | 138 | } |
| 162 | − | |
| 163 | 139 | function touchEndEvent(event) { |
| 164 | 140 | printTouchEvent("touchEndEvent", event); |
| 165 | 141 | } |
| @@ -167,8 +143,6 @@ function touchEndEvent(event) { · 1 unmodified line | ||
| 167 | 143 | Controller.touchBeginEvent.connect(touchBeginEvent); |
| 168 | 144 | Controller.touchUpdateEvent.connect(touchUpdateEvent); |
| 169 | 145 | Controller.touchEndEvent.connect(touchEndEvent); |
| 170 | − | |
| 171 | − | |
| 172 | 146 | function wheelEvent(event) { |
| 173 | 147 | print("wheelEvent"); |
| 174 | 148 | print(" event.x,y=" + event.x + ", " + event.y); |
| @@ -182,9 +156,7 @@ function wheelEvent(event) { · 7 unmodified lines | ||
| 182 | 156 | print(" event.isMeta=" + event.isMeta); |
| 183 | 157 | print(" event.isAlt=" + event.isAlt); |
| 184 | 158 | } |
| 185 | − | |
| 186 | 159 | Controller.wheelEvent.connect(wheelEvent); |
| 187 | − | |
| 188 | 160 | function scriptEnding() { |
| 189 | 161 | // re-enabled the standard application for touch events |
| 190 | 162 | Controller.releaseKeyEvents({ text: "A" }); |
| @@ -194,5 +166,4 @@ function scriptEnding() { · 3 unmodified lines | ||
| 194 | 166 | Controller.releaseKeyEvents({ text: "page up" }); |
| 195 | 167 | Controller.releaseKeyEvents({ text: "page down" }); |
| 196 | 168 | } |
| 197 | − | |
| 198 | −Script.scriptEnding.connect(scriptEnding); | |
| 169 | +Script.scriptEnding.connect(scriptEnding); | |
| @@ -59,9 +59,7 @@ function controller(side) { · 58 unmodified lines | ||
| 59 | 59 | this.triggerHeld = false; |
| 60 | 60 | this.triggerThreshold = 0.9; |
| 61 | 61 | this.side = side; |
| 62 | − this.palm = 2 * side; | |
| 63 | − this.tip = 2 * side + 1; | |
| 64 | − this.trigger = side; | |
| 62 | + this.trigger = side == LEFT ? Controller.Standard.LT : Controller.Standard.RT; | |
| 65 | 63 | this.originalGravity = { |
| 66 | 64 | x: 0, |
| 67 | 65 | y: 0, |
| @@ -150,8 +148,8 @@ function controller(side) { · 82 unmodified lines | ||
| 150 | 148 | |
| 151 | 149 | |
| 152 | 150 | this.updateControllerState = function() { |
| 153 | − this.palmPosition = Controller.getSpatialControlPosition(this.palm); | |
| 154 | − this.tipPosition = Controller.getSpatialControlPosition(this.tip); | |
| 151 | + this.palmPosition = this.side == RIGHT ? MyAvatar.rightHandPose.translation : MyAvatar.leftHandPose.translation; | |
| 152 | + this.tipPosition = this.side == RIGHT ? MyAvatar.rightHandTipPose.translation : MyAvatar.leftHandTipPose.translation; | |
| 155 | 153 | this.triggerValue = Controller.getTriggerValue(this.trigger); |
| 156 | 154 | } |
| 157 | 155 | |
| @@ -238,7 +238,7 @@ var inHand = false; · 237 unmodified lines | ||
| 238 | 238 | |
| 239 | 239 | function isControllerActive() { |
| 240 | 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 | 242 | return controllerActive; |
| 243 | 243 | } |
| 244 | 244 | |
| @@ -312,10 +312,10 @@ function grabSword(hand) { · 67 unmodified lines | ||
| 312 | 312 | } |
| 313 | 313 | var handRotation; |
| 314 | 314 | if (hand === "right") { |
| 315 | − handRotation = MyAvatar.getRightPalmRotation(); | |
| 315 | + handRotation = MyAvatar.rightHandPose.rotation; | |
| 316 | 316 | |
| 317 | 317 | } else if (hand === "left") { |
| 318 | − handRotation = MyAvatar.getLeftPalmRotation(); | |
| 318 | + handRotation = MyAvatar.leftHandPose.rotation; | |
| 319 | 319 | } |
| 320 | 320 | var swordRotation = Entities.getEntityProperties(swordID).rotation; |
| 321 | 321 | var offsetRotation = Quat.multiply(Quat.inverse(handRotation), swordRotation); |
| @@ -71,10 +71,8 @@ function controller(side, cycleColorButton) { · 70 unmodified lines | ||
| 71 | 71 | this.triggerHeld = false; |
| 72 | 72 | this.triggerThreshold = 0.9; |
| 73 | 73 | this.side = side; |
| 74 | − this.palm = 2 * side; | |
| 75 | − this.tip = 2 * side + 1; | |
| 76 | − this.trigger = side; | |
| 77 | − this.cycleColorButton = cycleColorButton; | |
| 74 | + this.trigger = side == LEFT ? Controller.Stantard.LT : Controller.Standard.RT; | |
| 75 | + this.cycleColorButton = side == LEFT ? Controller.Stantard.LeftPrimaryThumb : Controller.Standard.RightPrimaryThumb; | |
| 78 | 76 | |
| 79 | 77 | this.points = []; |
| 80 | 78 | this.normals = []; |
| @@ -173,11 +171,10 @@ function controller(side, cycleColorButton) { · 92 unmodified lines | ||
| 173 | 171 | |
| 174 | 172 | |
| 175 | 173 | this.updateControllerState = function() { |
| 176 | − this.cycleColorButtonPressed = Controller.isButtonPressed(this.cycleColorButton); | |
| 177 | − this.palmPosition = Controller.getSpatialControlPosition(this.palm); | |
| 178 | − this.tipPosition = Controller.getSpatialControlPosition(this.tip); | |
| 179 | − this.palmNormal = Controller.getSpatialControlNormal(this.palm); | |
| 180 | − this.triggerValue = Controller.getTriggerValue(this.trigger); | |
| 174 | + this.cycleColorButtonPressed = Controller.getValue(this.cycleColorButton); | |
| 175 | + this.palmPosition = this.side == RIGHT ? MyAvatar.rightHandPose.translation : MyAvatar.leftHandPose.translation; | |
| 176 | + this.tipPosition = this.side == RIGHT ? MyAvatar.rightHandTipPose.translation : MyAvatar.leftHandTipPose.translation; | |
| 177 | + this.triggerValue = Controller.getValue(this.trigger); | |
| 181 | 178 | |
| 182 | 179 | |
| 183 | 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); | |
| 219 | −var leftController = new controller(LEFT, LEFT_BUTTON_4); | |
| 215 | +var rightController = new controller(RIGHT); | |
| 216 | +var leftController = new controller(LEFT); | |
| 220 | 217 | Script.update.connect(update); |
| 221 | 218 | Script.scriptEnding.connect(scriptEnding); |
| 222 | 219 | |
| @@ -11,6 +11,37 @@ · 10 unmodified lines | ||
| 11 | 11 | |
| 12 | 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 | 46 | Object.keys(Controller.Standard).forEach(function (input) { |
| 16 | 47 | print("Controller.Standard." + input + ":" + Controller.Standard[input]); |
| @@ -32,44 +63,48 @@ Controller.resetAllDeviceBindings(); · 15 unmodified lines | ||
| 32 | 63 | // Query all actions |
| 33 | 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 | 70 | // Each action stores: |
| 36 | 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 | 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 | 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 | 81 | // Each input channel stores: |
| 47 | 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 | 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 | 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 | 92 | // Each Input stores: |
| 58 | 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 | 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 | 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 | 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 | 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 | 109 | // You can also get all of a devices input channels |
| 75 | 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 | 141 | // You can modify key bindings by using these avaiable inputs |
| 107 | 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 | 144 | Controller.removeInputChannel(e); |
| 110 | 145 | e.input = availableInputs[6].input; |
| 111 | 146 | Controller.addInputChannel(e); |
| @@ -15,16 +15,18 @@ Script.include("omniTool/models/invisibleWand.js"); · 14 unmodified lines | ||
| 15 | 15 | |
| 16 | 16 | OmniToolModules = {}; |
| 17 | 17 | OmniToolModuleType = null; |
| 18 | +LOG_DEBUG = 1; | |
| 18 | 19 | |
| 19 | −OmniTool = function(side) { | |
| 20 | +OmniTool = function(left) { | |
| 20 | 21 | this.OMNI_KEY = "OmniTool"; |
| 21 | 22 | this.MAX_FRAMERATE = 60; |
| 22 | 23 | this.UPDATE_INTERVAL = 1.0 / this.MAX_FRAMERATE |
| 23 | − this.SIDE = side; | |
| 24 | − this.PALM = 2 * side; | |
| 25 | − this.ACTION = findAction(side ? "ACTION2" : "ACTION1"); | |
| 26 | − this.ALT_ACTION = findAction(side ? "ACTION1" : "ACTION2"); | |
| 27 | − | |
| 24 | + this.left = left; | |
| 25 | + this.triggered = false; | |
| 26 | + var actions = Controller.Actions; | |
| 27 | + var standard = Controller.Standard; | |
| 28 | + this.palmControl = left ? actions.LeftHand : actions.RightHand; | |
| 29 | + logDebug("Init OmniTool " + (left ? "left" : "right")); | |
| 28 | 30 | this.highlighter = new Highlighter(); |
| 29 | 31 | this.ignoreEntities = {}; |
| 30 | 32 | this.nearestOmniEntity = { |
| @@ -47,22 +49,25 @@ OmniTool = function(side) { · 16 unmodified lines | ||
| 47 | 49 | this.showWand(false); |
| 48 | 50 | |
| 49 | 51 | // Connect to desired events |
| 50 | − var _this = this; | |
| 51 | − Controller.actionEvent.connect(function(action, state) { | |
| 52 | − _this.onActionEvent(action, state); | |
| 53 | − }); | |
| 52 | + var that = this; | |
| 54 | 53 | |
| 55 | 54 | Script.update.connect(function(deltaTime) { |
| 56 | − _this.lastUpdateInterval += deltaTime; | |
| 57 | − if (_this.lastUpdateInterval >= _this.UPDATE_INTERVAL) { | |
| 58 | − _this.onUpdate(_this.lastUpdateInterval); | |
| 59 | − _this.lastUpdateInterval = 0; | |
| 55 | + that.lastUpdateInterval += deltaTime; | |
| 56 | + if (that.lastUpdateInterval >= that.UPDATE_INTERVAL) { | |
| 57 | + that.onUpdate(that.lastUpdateInterval); | |
| 58 | + that.lastUpdateInterval = 0; | |
| 60 | 59 | } |
| 61 | 60 | }); |
| 62 | 61 | |
| 63 | 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 | 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 | 89 | OmniTool.prototype.onCleanup = function(action) { |
| 90 | + this.mapping.disable(); | |
| 86 | 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) { | |
| 94 | − this.module.onActionEvent(action, state); | |
| 95 | − } | |
| 96 | − | |
| 97 | − if (action == this.ACTION) { | |
| 98 | − if (state) { | |
| 95 | +OmniTool.prototype.onUpdateTrigger = function (value) { | |
| 96 | + //logDebug("Trigger update value " + value); | |
| 97 | + var triggered = value != 0; | |
| 98 | + if (triggered != this.triggered) { | |
| 99 | + this.triggered = triggered; | |
| 100 | + if (this.triggered) { | |
| 99 | 101 | this.onClick(); |
| 100 | 102 | } else { |
| 101 | 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 | 108 | OmniTool.prototype.getOmniToolData = function(entityId) { |
| @@ -127,7 +125,7 @@ OmniTool.prototype.setActive = function(active) { · 16 unmodified lines | ||
| 127 | 125 | if (active === this.active) { |
| 128 | 126 | return; |
| 129 | 127 | } |
| 130 | − logDebug("OmniTool changing active state: " + active); | |
| 128 | + logDebug("OmniTool " + this.left + " changing active state: " + active); | |
| 131 | 129 | this.active = active; |
| 132 | 130 | this.model.setVisible(this.active); |
| 133 | 131 | if (this.module && this.module.onActiveChanged) { |
| @@ -138,17 +136,17 @@ OmniTool.prototype.setActive = function(active) { · 4 unmodified lines | ||
| 138 | 136 | |
| 139 | 137 | OmniTool.prototype.onUpdate = function(deltaTime) { |
| 140 | 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 | 141 | // When on the base, hydras report a position of 0 |
| 143 | 142 | this.setActive(Vec3.length(this.position) > 0.001); |
| 144 | 143 | if (!this.active) { |
| 145 | 144 | return; |
| 146 | 145 | } |
| 147 | 146 | |
| 148 | − | |
| 149 | 147 | if (this.model) { |
| 150 | 148 | // Update the wand |
| 151 | − var rawRotation = Controller.getSpatialControlRawRotation(this.PALM); | |
| 149 | + var rawRotation = this.pose.rotation; | |
| 152 | 150 | this.rotation = Quat.multiply(MyAvatar.orientation, rawRotation); |
| 153 | 151 | this.model.setTransform({ |
| 154 | 152 | rotation: this.rotation, |
| @@ -306,6 +304,7 @@ OmniTool.prototype.scan = function() { · 151 unmodified lines | ||
| 306 | 304 | } |
| 307 | 305 | |
| 308 | 306 | OmniTool.prototype.unloadModule = function() { |
| 307 | + logDebug("Unloading omniTool module") | |
| 309 | 308 | if (this.module && this.module.onUnload) { |
| 310 | 309 | this.module.onUnload(); |
| 311 | 310 | } |
| @@ -348,4 +347,4 @@ OmniTool.prototype.activateNewOmniModule = function() { · 36 unmodified lines | ||
| 348 | 347 | } |
| 349 | 348 | |
| 350 | 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 | 33 | findAction = function(name) { |
| 34 | − var actions = Controller.getAllActions(); | |
| 35 | − for (var i = 0; i < actions.length; i++) { | |
| 36 | − if (actions[i].actionName == name) { | |
| 37 | − return i; | |
| 38 | − } | |
| 39 | − } | |
| 40 | − return 0; | |
| 34 | + return Controller.findAction(name); | |
| 41 | 35 | } |
| 42 | 36 | |
| 43 | 37 | addLine = function(origin, vector, color) { |
| @@ -18,24 +18,8 @@ Script.include("./libraries/walkConstants.js"); · 17 unmodified lines | ||
| 18 | 18 | |
| 19 | 19 | Avatar = function() { |
| 20 | 20 | // if Hydras are connected, the only way to enable use is to never set any arm joint rotation |
| 21 | − this.hydraCheck = function() { | |
| 22 | − // function courtesy of Thijs Wenker (frisbee.js) | |
| 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 | − } | |
| 21 | + this.hydraCheck = function () { | |
| 22 | + return Controller.Hardware.Hydra !== undefined; | |
| 39 | 23 | } |
| 40 | 24 | // settings |
| 41 | 25 | this.headFree = true; |
| @@ -38,7 +38,7 @@ var mouseLook = (function () { · 37 unmodified lines | ||
| 38 | 38 | keyboardID = 0; |
| 39 | 39 | |
| 40 | 40 | function onKeyPressEvent(event) { |
| 41 | − if (event.text == 'M') { | |
| 41 | + if (event.text == 'm') { | |
| 42 | 42 | active = !active; |
| 43 | 43 | updateMapping(); |
| 44 | 44 | } |
| @@ -10,7 +10,7 @@ · 9 unmodified lines | ||
| 10 | 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 | 14 | var nullActionID = "00000000-0000-0000-0000-000000000000"; |
| 15 | 15 | var controllerID; |
| 16 | 16 | var controllerActive; |
| @@ -32,7 +32,7 @@ function makeNewStick() { · 15 unmodified lines | ||
| 32 | 32 | modelURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.fbx", |
| 33 | 33 | compoundShapeURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.obj", |
| 34 | 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 | 36 | rotation: MyAvatar.orientation, |
| 37 | 37 | damping: .1, |
| 38 | 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 | 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 | 90 | controllerActive = (Vec3.length(palmPosition) > 0); |
| 99 | 91 | if(!controllerActive){ |
| 100 | 92 | return; |
| 101 | 93 | } |
| 102 | 94 | |
| 103 | − stickOrientation = Controller.getSpatialControlRawRotation(controllerID); | |
| 95 | + stickOrientation = handPose.rotation; | |
| 104 | 96 | var adjustment = Quat.fromPitchYawRollDegrees(180, 0, 0); |
| 105 | 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 | 45 | green: 255, |
| 46 | 46 | blue: 255 |
| 47 | 47 | }; |
| 48 | + | |
| 49 | + var TRIGGER_CONTROLS = [ | |
| 50 | + Controller.Standard.LT, | |
| 51 | + Controller.Standard.RT, | |
| 52 | + ]; | |
| 53 | + | |
| 48 | 54 | |
| 49 | 55 | PingPongGun.prototype = { |
| 50 | 56 | hand: null, |
| @@ -53,11 +59,11 @@ · 2 unmodified lines | ||
| 53 | 59 | canShoot: false, |
| 54 | 60 | canShootTimeout: null, |
| 55 | 61 | setRightHand: function() { |
| 56 | − this.hand = 'RIGHT'; | |
| 62 | + this.hand = 1; | |
| 57 | 63 | }, |
| 58 | 64 | |
| 59 | 65 | setLeftHand: function() { |
| 60 | − this.hand = 'LEFT'; | |
| 66 | + this.hand = 0; | |
| 61 | 67 | }, |
| 62 | 68 | |
| 63 | 69 | startNearGrab: function() { |
| @@ -92,12 +98,7 @@ · 28 unmodified lines | ||
| 92 | 98 | }, |
| 93 | 99 | |
| 94 | 100 | checkTriggerPressure: function(gunHand) { |
| 95 | − var handClickString = gunHand + "_HAND_CLICK"; | |
| 96 | − | |
| 97 | − var handClick = Controller.findAction(handClickString); | |
| 98 | − | |
| 99 | − this.triggerValue = Controller.getActionValue(handClick); | |
| 100 | − | |
| 101 | + this.triggerValue = Controller.getValue(TRIGGER_CONTROLS[gunHand]); | |
| 101 | 102 | if (this.triggerValue < RELOAD_THRESHOLD) { |
| 102 | 103 | // print('RELOAD'); |
| 103 | 104 | this.canShoot = true; |
| @@ -33,12 +33,17 @@ · 32 unmodified lines | ||
| 33 | 33 | var MIN_POINT_DISTANCE = 0.01; |
| 34 | 34 | var STROKE_WIDTH = 0.02; |
| 35 | 35 | |
| 36 | + var TRIGGER_CONTROLS = [ | |
| 37 | + Controller.Standard.LT, | |
| 38 | + Controller.Standard.RT, | |
| 39 | + ]; | |
| 40 | + | |
| 36 | 41 | this.setRightHand = function () { |
| 37 | − this.hand = 'RIGHT'; | |
| 42 | + this.hand = 1; | |
| 38 | 43 | } |
| 39 | 44 | |
| 40 | 45 | this.setLeftHand = function () { |
| 41 | − this.hand = 'LEFT'; | |
| 46 | + this.hand = 0; | |
| 42 | 47 | } |
| 43 | 48 | |
| 44 | 49 | this.startNearGrab = function () { |
| @@ -46,11 +51,7 @@ · 1 unmodified line | ||
| 46 | 51 | } |
| 47 | 52 | |
| 48 | 53 | this.toggleWithTriggerPressure = function () { |
| 49 | − var handClickString = this.whichHand + "_HAND_CLICK"; | |
| 50 | − | |
| 51 | − var handClick = Controller.findAction(handClickString); | |
| 52 | − | |
| 53 | − this.triggerValue = Controller.getActionValue(handClick); | |
| 54 | + this.triggerValue = Controller.getValue(TRIGGER_CONTROLS[this.whichHand]); | |
| 54 | 55 | if (this.triggerValue < DISABLE_SPRAY_THRESHOLD && this.spraying === true) { |
| 55 | 56 | this.spraying = false; |
| 56 | 57 | this.disableStream(); |
| @@ -2,7 +2,12 @@ set(TARGET_NAME interface) · 1 unmodified line | ||
| 2 | 2 | project(${TARGET_NAME}) |
| 3 | 3 | |
| 4 | 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 | 11 | foreach(EXTERNAL ${OPTIONAL_EXTERNALS}) |
| 7 | 12 | string(TOUPPER ${EXTERNAL} ${EXTERNAL}_UPPERCASE) |
| 8 | 13 | if (NOT ${${EXTERNAL}_UPPERCASE}_ROOT_DIR) |
| @@ -99,15 +104,17 @@ endif() · 90 unmodified lines | ||
| 99 | 104 | link_hifi_libraries(shared octree environment gpu gl procedural model render |
| 100 | 105 | fbx networking model-networking entities avatars |
| 101 | 106 | audio audio-client animation script-engine physics |
| 102 | − render-utils entities-renderer ui auto-updater | |
| 103 | − plugins display-plugins input-plugins) | |
| 107 | + render-utils entities-renderer ui auto-updater | |
| 108 | + controllers plugins display-plugins input-plugins ) | |
| 104 | 109 | |
| 110 | +#fixme find a way to express faceshift as a plugin | |
| 105 | 111 | target_bullet() |
| 106 | 112 | target_glew() |
| 107 | 113 | target_opengl() |
| 108 | 114 | |
| 109 | − | |
| 110 | −add_dependency_external_projects(sdl2) | |
| 115 | +if (WIN32 OR APPLE) | |
| 116 | + target_faceshift() | |
| 117 | +endif() | |
| 111 | 118 | |
| 112 | 119 | # perform standard include and linking for found externals |
| 113 | 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 | 17 | #include <glm/gtx/vector_angle.hpp> |
| 18 | 18 | #include <glm/gtc/type_ptr.hpp> |
| 19 | 19 | |
| 20 | −#include <QAbstractNativeEventFilter> | |
| 21 | −#include <QActionGroup> | |
| 22 | −#include <QDebug> | |
| 23 | −#include <QDesktopServices> | |
| 24 | −#include <QDesktopWidget> | |
| 25 | −#include <QImage> | |
| 26 | −#include <QFileDialog> | |
| 27 | −#include <QInputDialog> | |
| 28 | −#include <QKeyEvent> | |
| 29 | −#include <QMediaPlayer> | |
| 30 | −#include <QMenuBar> | |
| 31 | −#include <QMessageBox> | |
| 32 | −#include <QMimeData> | |
| 33 | −#include <QMouseEvent> | |
| 34 | −#include <QNetworkDiskCache> | |
| 35 | −#include <QObject> | |
| 36 | −#include <QScreen> | |
| 37 | −#include <QTimer> | |
| 38 | −#include <QUrl> | |
| 39 | −#include <QWheelEvent> | |
| 40 | −#include <QWindow> | |
| 20 | +#include <QtCore/QDebug> | |
| 21 | +#include <QtCore/QObject> | |
| 22 | +#include <QtCore/QUrl> | |
| 23 | +#include <QtCore/QTimer> | |
| 24 | +#include <QtCore/QAbstractNativeEventFilter> | |
| 25 | +#include <QtCore/QMimeData> | |
| 26 | + | |
| 27 | +#include <QtGui/QScreen> | |
| 28 | +#include <QtGui/QImage> | |
| 29 | +#include <QtGui/QWheelEvent> | |
| 30 | +#include <QtGui/QWindow> | |
| 31 | +#include <QtQml/QQmlContext> | |
| 32 | +#include <QtGui/QKeyEvent> | |
| 33 | +#include <QtGui/QMouseEvent> | |
| 34 | +#include <QtGui/QDesktopServices> | |
| 35 | + | |
| 36 | +#include <QtWidgets/QActionGroup> | |
| 37 | +#include <QtWidgets/QDesktopWidget> | |
| 38 | +#include <QtWidgets/QFileDialog> | |
| 39 | +#include <QtWidgets/QInputDialog> | |
| 40 | +#include <QtWidgets/QMenuBar> | |
| 41 | +#include <QtWidgets/QMessageBox> | |
| 42 | + | |
| 43 | +#include <QtMultimedia/QMediaPlayer> | |
| 44 | + | |
| 45 | +#include <QtNetwork/QNetworkDiskCache> | |
| 41 | 46 | |
| 42 | 47 | #include <AccountManager.h> |
| 43 | 48 | #include <AddressManager.h> |
| @@ -60,7 +65,8 @@ · 16 unmodified lines | ||
| 60 | 65 | #include <InfoView.h> |
| 61 | 66 | #include <input-plugins/InputPlugin.h> |
| 62 | 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 | 70 | #include <LogHandler.h> |
| 65 | 71 | #include <MainWindow.h> |
| 66 | 72 | #include <MessageDialog.h> |
| @@ -120,6 +126,7 @@ · 53 unmodified lines | ||
| 120 | 126 | #include "scripting/SettingsScriptingInterface.h" |
| 121 | 127 | #include "scripting/WebWindowClass.h" |
| 122 | 128 | #include "scripting/WindowScriptingInterface.h" |
| 129 | +#include "scripting/ControllerScriptingInterface.h" | |
| 123 | 130 | #if defined(Q_OS_MAC) || defined(Q_OS_WIN) |
| 124 | 131 | #include "SpeechRecognizer.h" |
| 125 | 132 | #endif |
| @@ -137,6 +144,7 @@ · 11 unmodified lines | ||
| 137 | 144 | #include "ui/UpdateDialog.h" |
| 138 | 145 | #include "Util.h" |
| 139 | 146 | |
| 147 | + | |
| 140 | 148 | // ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU |
| 141 | 149 | // FIXME seems to be broken. |
| 142 | 150 | #if defined(Q_OS_WIN) |
| @@ -316,6 +324,7 @@ bool setupEssentials(int& argc, char** argv) { · 173 unmodified lines | ||
| 316 | 324 | DependencyManager::set<EntityScriptingInterface>(); |
| 317 | 325 | DependencyManager::set<WindowScriptingInterface>(); |
| 318 | 326 | DependencyManager::set<HMDScriptingInterface>(); |
| 327 | + | |
| 319 | 328 | #if defined(Q_OS_MAC) || defined(Q_OS_WIN) |
| 320 | 329 | DependencyManager::set<SpeechRecognizer>(); |
| 321 | 330 | #endif |
| @@ -327,7 +336,7 @@ bool setupEssentials(int& argc, char** argv) { · 5 unmodified lines | ||
| 327 | 336 | DependencyManager::set<InterfaceActionFactory>(); |
| 328 | 337 | DependencyManager::set<AssetClient>(); |
| 329 | 338 | DependencyManager::set<UserInputMapper>(); |
| 330 | − | |
| 339 | + DependencyManager::set<controller::ScriptingInterface, ControllerScriptingInterface>(); | |
| 331 | 340 | return true; |
| 332 | 341 | } |
| 333 | 342 | |
| @@ -339,45 +348,47 @@ int _keyboardFocusHighlightID{ -1 }; · 5 unmodified lines | ||
| 339 | 348 | PluginContainer* _pluginContainer; |
| 340 | 349 | |
| 341 | 350 | Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : |
| 342 | − QApplication(argc, argv), | |
| 343 | − _dependencyManagerIsSetup(setupEssentials(argc, argv)), | |
| 344 | − _window(new MainWindow(desktop())), | |
| 345 | − _toolWindow(NULL), | |
| 346 | − _undoStackScriptingInterface(&_undoStack), | |
| 347 | − _frameCount(0), | |
| 348 | − _fps(60.0f), | |
| 349 | − _physicsEngine(new PhysicsEngine(Vectors::ZERO)), | |
| 350 | − _entities(true, this, this), | |
| 351 | − _entityClipboardRenderer(false, this, this), | |
| 352 | − _entityClipboard(new EntityTree()), | |
| 353 | − _lastQueriedTime(usecTimestampNow()), | |
| 354 | − _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)), | |
| 355 | − _firstRun("firstRun", true), | |
| 356 | − _previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION), | |
| 357 | − _scriptsLocationHandle("scriptsLocation", DESKTOP_LOCATION), | |
| 358 | − _fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES), | |
| 359 | − _scaleMirror(1.0f), | |
| 360 | − _rotateMirror(0.0f), | |
| 361 | − _raiseMirror(0.0f), | |
| 362 | − _lastMouseMoveWasSimulated(false), | |
| 363 | − _enableProcessOctreeThread(true), | |
| 364 | − _runningScriptsWidget(NULL), | |
| 365 | − _runningScriptsWidgetWasVisible(false), | |
| 366 | − _lastNackTime(usecTimestampNow()), | |
| 367 | − _lastSendDownstreamAudioStats(usecTimestampNow()), | |
| 368 | − _aboutToQuit(false), | |
| 369 | − _notifiedPacketVersionMismatchThisDomain(false), | |
| 370 | − _maxOctreePPS(maxOctreePacketsPerSecond.get()), | |
| 371 | − _lastFaceTrackerUpdate(0) | |
| 351 | + QApplication(argc, argv), | |
| 352 | + _dependencyManagerIsSetup(setupEssentials(argc, argv)), | |
| 353 | + _window(new MainWindow(desktop())), | |
| 354 | + _toolWindow(NULL), | |
| 355 | + _undoStackScriptingInterface(&_undoStack), | |
| 356 | + _frameCount(0), | |
| 357 | + _fps(60.0f), | |
| 358 | + _physicsEngine(new PhysicsEngine(Vectors::ZERO)), | |
| 359 | + _entities(true, this, this), | |
| 360 | + _entityClipboardRenderer(false, this, this), | |
| 361 | + _entityClipboard(new EntityTree()), | |
| 362 | + _lastQueriedTime(usecTimestampNow()), | |
| 363 | + _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)), | |
| 364 | + _firstRun("firstRun", true), | |
| 365 | + _previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION), | |
| 366 | + _scriptsLocationHandle("scriptsLocation", DESKTOP_LOCATION), | |
| 367 | + _fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES), | |
| 368 | + _scaleMirror(1.0f), | |
| 369 | + _rotateMirror(0.0f), | |
| 370 | + _raiseMirror(0.0f), | |
| 371 | + _lastMouseMoveWasSimulated(false), | |
| 372 | + _enableProcessOctreeThread(true), | |
| 373 | + _runningScriptsWidget(NULL), | |
| 374 | + _runningScriptsWidgetWasVisible(false), | |
| 375 | + _lastNackTime(usecTimestampNow()), | |
| 376 | + _lastSendDownstreamAudioStats(usecTimestampNow()), | |
| 377 | + _aboutToQuit(false), | |
| 378 | + _notifiedPacketVersionMismatchThisDomain(false), | |
| 379 | + _maxOctreePPS(maxOctreePacketsPerSecond.get()), | |
| 380 | + _lastFaceTrackerUpdate(0) | |
| 372 | 381 | { |
| 373 | 382 | thread()->setObjectName("Main Thread"); |
| 374 | − | |
| 383 | + | |
| 375 | 384 | setInstance(this); |
| 376 | − | |
| 385 | + | |
| 386 | + auto controllerScriptingInterface = DependencyManager::get<controller::ScriptingInterface>().data(); | |
| 387 | + _controllerScriptingInterface = dynamic_cast<ControllerScriptingInterface*>(controllerScriptingInterface); | |
| 377 | 388 | // to work around the Qt constant wireless scanning, set the env for polling interval very high |
| 378 | 389 | const QByteArray EXTREME_BEARER_POLL_TIMEOUT = QString::number(INT_MAX).toLocal8Bit(); |
| 379 | 390 | qputenv("QT_BEARER_POLL_TIMEOUT", EXTREME_BEARER_POLL_TIMEOUT); |
| 380 | − | |
| 391 | + | |
| 381 | 392 | _entityClipboard->createRootElement(); |
| 382 | 393 | |
| 383 | 394 | _pluginContainer = new PluginContainerProxy(); |
| @@ -440,7 +451,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 56 unmodified lines | ||
| 440 | 451 | audioIO->moveToThread(audioThread); |
| 441 | 452 | |
| 442 | 453 | auto& audioScriptingInterface = AudioScriptingInterface::getInstance(); |
| 443 | − | |
| 454 | + | |
| 444 | 455 | connect(audioThread, &QThread::started, audioIO.data(), &AudioClient::start); |
| 445 | 456 | connect(audioIO.data(), &AudioClient::destroyed, audioThread, &QThread::quit); |
| 446 | 457 | connect(audioThread, &QThread::finished, audioThread, &QThread::deleteLater); |
| @@ -479,7 +490,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 32 unmodified lines | ||
| 479 | 490 | connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails())); |
| 480 | 491 | connect(&domainHandler, &DomainHandler::settingsReceived, this, &Application::domainSettingsReceived); |
| 481 | 492 | connect(&domainHandler, &DomainHandler::hostnameChanged, |
| 482 | − DependencyManager::get<AddressManager>().data(), &AddressManager::storeCurrentAddress); | |
| 493 | + DependencyManager::get<AddressManager>().data(), &AddressManager::storeCurrentAddress); | |
| 483 | 494 | |
| 484 | 495 | // update our location every 5 seconds in the metaverse server, assuming that we are authenticated with one |
| 485 | 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 | 502 | // if we get a domain change, immediately attempt update location in metaverse server |
| 492 | 503 | connect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain, |
| 493 | − discoverabilityManager.data(), &DiscoverabilityManager::updateLocation); | |
| 504 | + discoverabilityManager.data(), &DiscoverabilityManager::updateLocation); | |
| 494 | 505 | |
| 495 | 506 | connect(nodeList.data(), &NodeList::nodeAdded, this, &Application::nodeAdded); |
| 496 | 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 | 540 | connect(addressManager.data(), &AddressManager::hostChanged, this, &Application::updateWindowTitle); |
| 530 | 541 | connect(this, &QCoreApplication::aboutToQuit, addressManager.data(), &AddressManager::storeCurrentAddress); |
| 531 | 542 | |
| 532 | − #ifdef _WIN32 | |
| 543 | +#ifdef _WIN32 | |
| 533 | 544 | WSADATA WsaData; |
| 534 | − int wsaresult = WSAStartup(MAKEWORD(2,2), &WsaData); | |
| 535 | − #endif | |
| 545 | + int wsaresult = WSAStartup(MAKEWORD(2, 2), &WsaData); | |
| 546 | +#endif | |
| 536 | 547 | |
| 537 | 548 | // tell the NodeList instance who to tell the domain server we care about |
| 538 | 549 | nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer |
| 539 | − << NodeType::EntityServer << NodeType::AssetServer); | |
| 550 | + << NodeType::EntityServer << NodeType::AssetServer); | |
| 540 | 551 | |
| 541 | 552 | // connect to the packet sent signal of the _entityEditSender |
| 542 | 553 | connect(&_entityEditSender, &EntityEditPacketSender::packetSent, this, &Application::packetSent); |
| @@ -609,29 +620,38 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 66 unmodified lines | ||
| 609 | 620 | // hook up bandwidth estimator |
| 610 | 621 | QSharedPointer<BandwidthRecorder> bandwidthRecorder = DependencyManager::get<BandwidthRecorder>(); |
| 611 | 622 | connect(nodeList.data(), &LimitedNodeList::dataSent, |
| 612 | − bandwidthRecorder.data(), &BandwidthRecorder::updateOutboundData); | |
| 623 | + bandwidthRecorder.data(), &BandwidthRecorder::updateOutboundData); | |
| 613 | 624 | connect(&nodeList->getPacketReceiver(), &PacketReceiver::dataReceived, |
| 614 | − bandwidthRecorder.data(), &BandwidthRecorder::updateInboundData); | |
| 625 | + bandwidthRecorder.data(), &BandwidthRecorder::updateInboundData); | |
| 615 | 626 | |
| 616 | 627 | connect(&getMyAvatar()->getSkeletonModel(), &SkeletonModel::skeletonLoaded, |
| 617 | − this, &Application::checkSkeleton, Qt::QueuedConnection); | |
| 628 | + this, &Application::checkSkeleton, Qt::QueuedConnection); | |
| 618 | 629 | |
| 619 | 630 | // Setup the userInputMapper with the actions |
| 620 | 631 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); |
| 621 | − connect(userInputMapper.data(), &UserInputMapper::actionEvent, &_controllerScriptingInterface, &AbstractControllerScriptingInterface::actionEvent); | |
| 622 | 632 | connect(userInputMapper.data(), &UserInputMapper::actionEvent, [this](int action, float state) { |
| 623 | − if (state) { | |
| 624 | − switch (action) { | |
| 625 | − case UserInputMapper::Action::TOGGLE_MUTE: | |
| 626 | − DependencyManager::get<AudioClient>()->toggleMute(); | |
| 627 | − break; | |
| 628 | − } | |
| 633 | + if (state && action == toInt(controller::Action::TOGGLE_MUTE)) { | |
| 634 | + DependencyManager::get<AudioClient>()->toggleMute(); | |
| 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 | 650 | // Setup the keyboardMouseDevice and the user input mapper with the default bindings |
| 633 | − _keyboardMouseDevice->registerToUserInputMapper(*userInputMapper); | |
| 634 | − _keyboardMouseDevice->assignDefaultInputMapping(*userInputMapper); | |
| 651 | + userInputMapper->registerDevice(_keyboardMouseDevice); | |
| 652 | + | |
| 653 | + | |
| 654 | + userInputMapper->loadDefaultMapping(userInputMapper->getStandardDeviceID()); | |
| 635 | 655 | |
| 636 | 656 | // check first run... |
| 637 | 657 | if (_firstRun.get()) { |
| @@ -704,8 +724,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 66 unmodified lines | ||
| 704 | 724 | // Now that menu is initalized we can sync myAvatar with it's state. |
| 705 | 725 | getMyAvatar()->updateMotionBehaviorFromMenu(); |
| 706 | 726 | |
| 727 | +#if 0 | |
| 707 | 728 | // the 3Dconnexion device wants to be initiliazed after a window is displayed. |
| 708 | 729 | ConnexionClient::getInstance().init(); |
| 730 | +#endif | |
| 709 | 731 | |
| 710 | 732 | auto& packetReceiver = nodeList->getPacketReceiver(); |
| 711 | 733 | packetReceiver.registerListener(PacketType::DomainConnectionDenied, this, "handleDomainConnectionDeniedPacket"); |
| @@ -792,6 +814,9 @@ void Application::cleanupBeforeQuit() { · 80 unmodified lines | ||
| 792 | 814 | |
| 793 | 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 | 820 | if (_keyboardFocusHighlightID > 0) { |
| 796 | 821 | getOverlays().deleteOverlay(_keyboardFocusHighlightID); |
| 797 | 822 | _keyboardFocusHighlightID = -1; |
| @@ -902,7 +927,10 @@ Application::~Application() { · 104 unmodified lines | ||
| 902 | 927 | |
| 903 | 928 | Leapmotion::destroy(); |
| 904 | 929 | RealSense::destroy(); |
| 930 | + | |
| 931 | +#if 0 | |
| 905 | 932 | ConnexionClient::getInstance().destroy(); |
| 933 | +#endif | |
| 906 | 934 | |
| 907 | 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 | 1003 | offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/")); |
| 976 | 1004 | offscreenUi->load("Root.qml"); |
| 977 | 1005 | offscreenUi->load("RootMenu.qml"); |
| 1006 | + auto scriptingInterface = DependencyManager::get<controller::ScriptingInterface>(); | |
| 1007 | + offscreenUi->getRootContext()->setContextProperty("Controller", scriptingInterface.data()); | |
| 978 | 1008 | _glWidget->installEventFilter(offscreenUi.data()); |
| 979 | 1009 | VrMenu::load(); |
| 980 | 1010 | VrMenu::executeQueuedLambdas(); |
| @@ -1003,7 +1033,10 @@ void Application::initializeUi() { · 22 unmodified lines | ||
| 1003 | 1033 | foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) { |
| 1004 | 1034 | QString name = inputPlugin->getName(); |
| 1005 | 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 | 1042 | updateInputModes(); |
| @@ -1456,7 +1489,7 @@ bool Application::event(QEvent* event) { · 446 unmodified lines | ||
| 1456 | 1489 | } |
| 1457 | 1490 | |
| 1458 | 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 | 1495 | return QApplication::event(event); |
| @@ -1470,7 +1503,7 @@ bool Application::eventFilter(QObject* object, QEvent* event) { · 7 unmodified lines | ||
| 1470 | 1503 | } |
| 1471 | 1504 | |
| 1472 | 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 | 1507 | event->accept(); |
| 1475 | 1508 | return true; |
| 1476 | 1509 | } |
| @@ -1485,10 +1518,10 @@ void Application::keyPressEvent(QKeyEvent* event) { · 8 unmodified lines | ||
| 1485 | 1518 | _altPressed = event->key() == Qt::Key_Alt; |
| 1486 | 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 | 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 | 1525 | return; |
| 1493 | 1526 | } |
| 1494 | 1527 | |
| @@ -1518,6 +1551,13 @@ void Application::keyPressEvent(QKeyEvent* event) { · 23 unmodified lines | ||
| 1518 | 1551 | if (isMeta) { |
| 1519 | 1552 | auto offscreenUi = DependencyManager::get<OffscreenUi>(); |
| 1520 | 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 | 1562 | break; |
| 1523 | 1563 | |
| @@ -1752,10 +1792,10 @@ void Application::keyReleaseEvent(QKeyEvent* event) { · 228 unmodified lines | ||
| 1752 | 1792 | |
| 1753 | 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 | 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 | 1799 | return; |
| 1760 | 1800 | } |
| 1761 | 1801 | |
| @@ -1796,7 +1836,9 @@ void Application::focusOutEvent(QFocusEvent* event) { · 34 unmodified lines | ||
| 1796 | 1836 | inputPlugin->pluginFocusOutEvent(); |
| 1797 | 1837 | } |
| 1798 | 1838 | } |
| 1839 | +#if 0 | |
| 1799 | 1840 | ConnexionData::getInstance().focusOutEvent(); |
| 1841 | +#endif | |
| 1800 | 1842 | |
| 1801 | 1843 | // synthesize events for keys currently pressed, since we may not get their release events |
| 1802 | 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 | 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 | 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 | 1893 | return; |
| 1852 | 1894 | } |
| 1853 | 1895 | |
| @@ -1872,10 +1914,10 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { · 18 unmodified lines | ||
| 1872 | 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 | 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 | 1921 | return; |
| 1880 | 1922 | } |
| 1881 | 1923 | |
| @@ -1897,11 +1939,11 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { · 15 unmodified lines | ||
| 1897 | 1939 | |
| 1898 | 1940 | void Application::mouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID) { |
| 1899 | 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 | 1943 | return; |
| 1902 | 1944 | } |
| 1903 | 1945 | |
| 1904 | − _controllerScriptingInterface.emitMouseDoublePressEvent(event); | |
| 1946 | + _controllerScriptingInterface->emitMouseDoublePressEvent(event); | |
| 1905 | 1947 | } |
| 1906 | 1948 | |
| 1907 | 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 | 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 | 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 | 1966 | return; |
| 1925 | 1967 | } |
| 1926 | 1968 | |
| @@ -1943,12 +1985,12 @@ void Application::touchUpdateEvent(QTouchEvent* event) { · 16 unmodified lines | ||
| 1943 | 1985 | |
| 1944 | 1986 | if (event->type() == QEvent::TouchUpdate) { |
| 1945 | 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 | 1989 | _lastTouchEvent = thisEvent; |
| 1948 | 1990 | } |
| 1949 | 1991 | |
| 1950 | 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 | 1994 | return; |
| 1953 | 1995 | } |
| 1954 | 1996 | |
| @@ -1960,13 +2002,13 @@ void Application::touchUpdateEvent(QTouchEvent* event) { · 5 unmodified lines | ||
| 1960 | 2002 | void Application::touchBeginEvent(QTouchEvent* event) { |
| 1961 | 2003 | _altPressed = false; |
| 1962 | 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 | 2007 | _lastTouchEvent = thisEvent; // and we reset our last event to this event before we call our update |
| 1966 | 2008 | touchUpdateEvent(event); |
| 1967 | 2009 | |
| 1968 | 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 | 2012 | return; |
| 1971 | 2013 | } |
| 1972 | 2014 | |
| @@ -1979,11 +2021,11 @@ void Application::touchBeginEvent(QTouchEvent* event) { · 6 unmodified lines | ||
| 1979 | 2021 | void Application::touchEndEvent(QTouchEvent* event) { |
| 1980 | 2022 | _altPressed = false; |
| 1981 | 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 | 2025 | _lastTouchEvent = thisEvent; |
| 1984 | 2026 | |
| 1985 | 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 | 2029 | return; |
| 1988 | 2030 | } |
| 1989 | 2031 | |
| @@ -1996,10 +2038,10 @@ void Application::touchEndEvent(QTouchEvent* event) { · 6 unmodified lines | ||
| 1996 | 2038 | |
| 1997 | 2039 | void Application::wheelEvent(QWheelEvent* event) { |
| 1998 | 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 | 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 | 2045 | return; |
| 2004 | 2046 | } |
| 2005 | 2047 | |
| @@ -2164,7 +2206,7 @@ float Application::getAvatarSimrate() { · 158 unmodified lines | ||
| 2164 | 2206 | } |
| 2165 | 2207 | |
| 2166 | 2208 | void Application::setLowVelocityFilter(bool lowVelocityFilter) { |
| 2167 | − InputDevice::setLowVelocityFilter(lowVelocityFilter); | |
| 2209 | + controller::InputDevice::setLowVelocityFilter(lowVelocityFilter); | |
| 2168 | 2210 | } |
| 2169 | 2211 | |
| 2170 | 2212 | ivec2 Application::getMouse() const { |
| @@ -2694,13 +2736,9 @@ void Application::update(float deltaTime) { · 523 unmodified lines | ||
| 2694 | 2736 | userInputMapper->setSensorToWorldMat(myAvatar->getSensorToWorldMatrix()); |
| 2695 | 2737 | userInputMapper->update(deltaTime); |
| 2696 | 2738 | |
| 2697 | − // This needs to go after userInputMapper->update() because of the keyboard | |
| 2698 | 2739 | bool jointsCaptured = false; |
| 2699 | − auto inputPlugins = PluginManager::getInstance()->getInputPlugins(); | |
| 2700 | − foreach(auto inputPlugin, inputPlugins) { | |
| 2701 | − QString name = inputPlugin->getName(); | |
| 2702 | − QAction* action = Menu::getInstance()->getActionForOption(name); | |
| 2703 | − if (action && action->isChecked()) { | |
| 2740 | + for (auto inputPlugin : PluginManager::getInstance()->getInputPlugins()) { | |
| 2741 | + if (inputPlugin->isActive()) { | |
| 2704 | 2742 | inputPlugin->pluginUpdate(deltaTime, jointsCaptured); |
| 2705 | 2743 | if (inputPlugin->isJointController()) { |
| 2706 | 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 | 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 | 2751 | myAvatar->clearDriveKeys(); |
| 2716 | 2752 | if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT) { |
| 2717 | − if (!_controllerScriptingInterface.areActionsCaptured()) { | |
| 2718 | − myAvatar->setDriveKeys(FWD, userInputMapper->getActionState(UserInputMapper::LONGITUDINAL_FORWARD)); | |
| 2719 | − myAvatar->setDriveKeys(BACK, userInputMapper->getActionState(UserInputMapper::LONGITUDINAL_BACKWARD)); | |
| 2720 | − myAvatar->setDriveKeys(UP, userInputMapper->getActionState(UserInputMapper::VERTICAL_UP)); | |
| 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)); | |
| 2753 | + if (!_controllerScriptingInterface->areActionsCaptured()) { | |
| 2754 | + myAvatar->setDriveKeys(TRANSLATE_Z, -1.0f * userInputMapper->getActionState(controller::Action::TRANSLATE_Z)); | |
| 2755 | + myAvatar->setDriveKeys(TRANSLATE_Y, userInputMapper->getActionState(controller::Action::TRANSLATE_Y)); | |
| 2756 | + myAvatar->setDriveKeys(TRANSLATE_X, userInputMapper->getActionState(controller::Action::TRANSLATE_X)); | |
| 2724 | 2757 | if (deltaTime > FLT_EPSILON) { |
| 2725 | 2758 | // For rotations what we really want are meausures of "angles per second" (in order to prevent |
| 2726 | 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 | 2761 | // controllers to provide a delta_per_second value rather than a raw delta.) |
| 2729 | 2762 | const float EXPECTED_FRAME_RATE = 60.0f; |
| 2730 | 2763 | float timeFactor = EXPECTED_FRAME_RATE * deltaTime; |
| 2731 | − myAvatar->setDriveKeys(ROT_UP, userInputMapper->getActionState(UserInputMapper::PITCH_UP) / timeFactor); | |
| 2732 | − myAvatar->setDriveKeys(ROT_DOWN, userInputMapper->getActionState(UserInputMapper::PITCH_DOWN) / timeFactor); | |
| 2733 | − myAvatar->setDriveKeys(ROT_LEFT, userInputMapper->getActionState(UserInputMapper::YAW_LEFT) / timeFactor); | |
| 2734 | − myAvatar->setDriveKeys(ROT_RIGHT, userInputMapper->getActionState(UserInputMapper::YAW_RIGHT) / timeFactor); | |
| 2764 | + myAvatar->setDriveKeys(PITCH, -1.0f * userInputMapper->getActionState(controller::Action::PITCH) / timeFactor); | |
| 2765 | + myAvatar->setDriveKeys(YAW, -1.0f * userInputMapper->getActionState(controller::Action::YAW) / timeFactor); | |
| 2766 | + myAvatar->setDriveKeys(STEP_YAW, -1.0f * userInputMapper->getActionState(controller::Action::STEP_YAW) / timeFactor); | |
| 2735 | 2767 | } |
| 2736 | 2768 | } |
| 2737 | − myAvatar->setDriveKeys(BOOM_IN, userInputMapper->getActionState(UserInputMapper::BOOM_IN)); | |
| 2738 | − myAvatar->setDriveKeys(BOOM_OUT, userInputMapper->getActionState(UserInputMapper::BOOM_OUT)); | |
| 2769 | + myAvatar->setDriveKeys(ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z)); | |
| 2739 | 2770 | } |
| 2740 | − UserInputMapper::PoseValue leftHand = userInputMapper->getPoseState(UserInputMapper::LEFT_HAND); | |
| 2741 | − UserInputMapper::PoseValue rightHand = userInputMapper->getPoseState(UserInputMapper::RIGHT_HAND); | |
| 2771 | + | |
| 2772 | + controller::Pose leftHand = userInputMapper->getPoseState(controller::Action::LEFT_HAND); | |
| 2773 | + controller::Pose rightHand = userInputMapper->getPoseState(controller::Action::RIGHT_HAND); | |
| 2742 | 2774 | Hand* hand = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHand(); |
| 2743 | − setPalmData(hand, leftHand, deltaTime, LEFT_HAND_INDEX, userInputMapper->getActionState(UserInputMapper::LEFT_HAND_CLICK)); | |
| 2744 | − setPalmData(hand, rightHand, deltaTime, RIGHT_HAND_INDEX, userInputMapper->getActionState(UserInputMapper::RIGHT_HAND_CLICK)); | |
| 2775 | + setPalmData(hand, leftHand, deltaTime, HandData::LeftHand, userInputMapper->getActionState(controller::Action::LEFT_HAND_CLICK)); | |
| 2776 | + setPalmData(hand, rightHand, deltaTime, HandData::RightHand, userInputMapper->getActionState(controller::Action::RIGHT_HAND_CLICK)); | |
| 2745 | 2777 | if (Menu::getInstance()->isOptionChecked(MenuOption::EnableHandMouseInput)) { |
| 2746 | − emulateMouse(hand, userInputMapper->getActionState(UserInputMapper::LEFT_HAND_CLICK), | |
| 2747 | − userInputMapper->getActionState(UserInputMapper::SHIFT), LEFT_HAND_INDEX); | |
| 2748 | − emulateMouse(hand, userInputMapper->getActionState(UserInputMapper::RIGHT_HAND_CLICK), | |
| 2749 | − userInputMapper->getActionState(UserInputMapper::SHIFT), RIGHT_HAND_INDEX); | |
| 2778 | + emulateMouse(hand, userInputMapper->getActionState(controller::Action::LEFT_HAND_CLICK), | |
| 2779 | + userInputMapper->getActionState(controller::Action::SHIFT), HandData::LeftHand); | |
| 2780 | + emulateMouse(hand, userInputMapper->getActionState(controller::Action::RIGHT_HAND_CLICK), | |
| 2781 | + userInputMapper->getActionState(controller::Action::SHIFT), HandData::RightHand); | |
| 2750 | 2782 | } |
| 2751 | 2783 | |
| 2752 | 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 | 4839 | return mat4(); |
| 4808 | 4840 | } |
| 4809 | 4841 | |
| 4810 | −void Application::setPalmData(Hand* hand, UserInputMapper::PoseValue pose, float deltaTime, int index, 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()); | |
| 4842 | +void Application::setPalmData(Hand* hand, const controller::Pose& pose, float deltaTime, HandData::Hand whichHand, float triggerValue) { | |
| 4828 | 4843 | |
| 4829 | − // transform from sensor space, to world space, to avatar model space. | |
| 4830 | − glm::mat4 poseMat = createMatFromQuatAndPos(pose.getRotation(), pose.getTranslation()); | |
| 4831 | − glm::mat4 sensorToWorldMat = getMyAvatar()->getSensorToWorldMatrix(); | |
| 4832 | − glm::mat4 modelMat = createMatFromQuatAndPos(getMyAvatar()->getOrientation(), getMyAvatar()->getPosition()); | |
| 4833 | − glm::mat4 objectPose = glm::inverse(modelMat) * sensorToWorldMat * poseMat; | |
| 4844 | + // NOTE: the Hand::modifyPalm() will allow the lambda to modify the palm data while ensuring some other user isn't | |
| 4845 | + // reading or writing to the Palms. This is definitely not the best way of handling this, and I'd like to see more | |
| 4846 | + // of this palm manipulation in the Hand class itself. But unfortunately the Hand and Palm don't knbow about | |
| 4847 | + // controller::Pose. More work is needed to clean this up. | |
| 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); | |
| 4836 | − glm::quat rotation = glm::quat_cast(objectPose); | |
| 4852 | + // transform from sensor space, to world space, to avatar model space. | |
| 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 | |
| 4839 | − glm::vec3 rawVelocity; | |
| 4840 | − if (deltaTime > 0.0f) { | |
| 4841 | − rawVelocity = (position - palm->getRawPosition()) / deltaTime; | |
| 4842 | − } else { | |
| 4843 | − rawVelocity = glm::vec3(0.0f); | |
| 4844 | − } | |
| 4845 | − palm->setRawVelocity(rawVelocity); // meters/sec | |
| 4858 | + glm::vec3 position = extractTranslation(objectPose); | |
| 4859 | + glm::quat rotation = glm::quat_cast(objectPose); | |
| 4860 | + | |
| 4861 | + // Compute current velocity from position change | |
| 4862 | + glm::vec3 rawVelocity; | |
| 4863 | + if (deltaTime > 0.0f) { | |
| 4864 | + rawVelocity = (position - palm.getRawPosition()) / deltaTime; | |
| 4865 | + } else { | |
| 4866 | + rawVelocity = glm::vec3(0.0f); | |
| 4867 | + } | |
| 4868 | + palm.setRawVelocity(rawVelocity); // meters/sec | |
| 4846 | 4869 | |
| 4847 | − // Angular Velocity of Palm | |
| 4848 | − glm::quat deltaRotation = rotation * glm::inverse(palm->getRawRotation()); | |
| 4849 | − glm::vec3 angularVelocity(0.0f); | |
| 4850 | − float rotationAngle = glm::angle(deltaRotation); | |
| 4851 | − if ((rotationAngle > EPSILON) && (deltaTime > 0.0f)) { | |
| 4852 | − angularVelocity = glm::normalize(glm::axis(deltaRotation)); | |
| 4853 | − angularVelocity *= (rotationAngle / deltaTime); | |
| 4854 | − palm->setRawAngularVelocity(angularVelocity); | |
| 4855 | − } else { | |
| 4856 | − palm->setRawAngularVelocity(glm::vec3(0.0f)); | |
| 4857 | − } | |
| 4858 | − | |
| 4859 | − if (InputDevice::getLowVelocityFilter()) { | |
| 4860 | − // Use a velocity sensitive filter to damp small motions and preserve large ones with | |
| 4861 | − // no latency. | |
| 4862 | − float velocityFilter = glm::clamp(1.0f - glm::length(rawVelocity), 0.0f, 1.0f); | |
| 4863 | − position = palm->getRawPosition() * velocityFilter + position * (1.0f - velocityFilter); | |
| 4864 | − rotation = safeMix(palm->getRawRotation(), rotation, 1.0f - velocityFilter); | |
| 4865 | − } | |
| 4866 | − palm->setRawPosition(position); | |
| 4867 | − palm->setRawRotation(rotation); | |
| 4868 | − | |
| 4869 | − // Store the one fingertip in the palm structure so we can track velocity | |
| 4870 | − const float FINGER_LENGTH = 0.3f; // meters | |
| 4871 | − const glm::vec3 FINGER_VECTOR(0.0f, FINGER_LENGTH, 0.0f); | |
| 4872 | − const glm::vec3 newTipPosition = position + rotation * FINGER_VECTOR; | |
| 4873 | − glm::vec3 oldTipPosition = palm->getTipRawPosition(); | |
| 4874 | − if (deltaTime > 0.0f) { | |
| 4875 | − palm->setTipVelocity((newTipPosition - oldTipPosition) / deltaTime); | |
| 4876 | − } else { | |
| 4877 | − palm->setTipVelocity(glm::vec3(0.0f)); | |
| 4878 | − } | |
| 4879 | − palm->setTipPosition(newTipPosition); | |
| 4880 | − palm->setTrigger(triggerValue); | |
| 4870 | + // Angular Velocity of Palm | |
| 4871 | + glm::quat deltaRotation = rotation * glm::inverse(palm.getRawRotation()); | |
| 4872 | + glm::vec3 angularVelocity(0.0f); | |
| 4873 | + float rotationAngle = glm::angle(deltaRotation); | |
| 4874 | + if ((rotationAngle > EPSILON) && (deltaTime > 0.0f)) { | |
| 4875 | + angularVelocity = glm::normalize(glm::axis(deltaRotation)); | |
| 4876 | + angularVelocity *= (rotationAngle / deltaTime); | |
| 4877 | + palm.setRawAngularVelocity(angularVelocity); | |
| 4878 | + } else { | |
| 4879 | + palm.setRawAngularVelocity(glm::vec3(0.0f)); | |
| 4880 | + } | |
| 4881 | + | |
| 4882 | + if (controller::InputDevice::getLowVelocityFilter()) { | |
| 4883 | + // Use a velocity sensitive filter to damp small motions and preserve large ones with | |
| 4884 | + // no latency. | |
| 4885 | + float velocityFilter = glm::clamp(1.0f - glm::length(rawVelocity), 0.0f, 1.0f); | |
| 4886 | + position = palm.getRawPosition() * velocityFilter + position * (1.0f - velocityFilter); | |
| 4887 | + rotation = safeMix(palm.getRawRotation(), rotation, 1.0f - velocityFilter); | |
| 4888 | + } | |
| 4889 | + palm.setRawPosition(position); | |
| 4890 | + palm.setRawRotation(rotation); | |
| 4891 | + | |
| 4892 | + // Store the one fingertip in the palm structure so we can track velocity | |
| 4893 | + const float FINGER_LENGTH = 0.3f; // meters | |
| 4894 | + const glm::vec3 FINGER_VECTOR(0.0f, FINGER_LENGTH, 0.0f); | |
| 4895 | + const glm::vec3 newTipPosition = position + rotation * FINGER_VECTOR; | |
| 4896 | + glm::vec3 oldTipPosition = palm.getTipRawPosition(); | |
| 4897 | + if (deltaTime > 0.0f) { | |
| 4898 | + palm.setTipVelocity((newTipPosition - oldTipPosition) / deltaTime); | |
| 4899 | + } else { | |
| 4900 | + palm.setTipVelocity(glm::vec3(0.0f)); | |
| 4901 | + } | |
| 4902 | + palm.setTipPosition(newTipPosition); | |
| 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 | 4910 | // Locate the palm, if it exists and is active |
| 4885 | 4911 | PalmData* palm; |
| 4886 | 4912 | bool foundHand = false; |
| 4887 | − for (size_t j = 0; j < hand->getNumPalms(); j++) { | |
| 4888 | − if (hand->getPalms()[j].getSixenseID() == index) { | |
| 4889 | − palm = &(hand->getPalms()[j]); | |
| 4913 | + for (size_t j = 0; j < palms.size(); j++) { | |
| 4914 | + if (palms[j].whichHand() == whichHand) { | |
| 4915 | + palm = &(palms[j]); | |
| 4890 | 4916 | foundHand = true; |
| 4891 | 4917 | break; |
| 4892 | 4918 | } |
| @@ -4898,12 +4924,14 @@ void Application::emulateMouse(Hand* hand, float click, float shift, int index) · 5 unmodified lines | ||
| 4898 | 4924 | // Process the mouse events |
| 4899 | 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 | 4932 | if (isHMDMode()) { |
| 4904 | 4933 | pos = getApplicationCompositor().getPalmClickLocation(palm); |
| 4905 | − } | |
| 4906 | − else { | |
| 4934 | + } else { | |
| 4907 | 4935 | // Get directon relative to avatar orientation |
| 4908 | 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 | 4940 | float yAngle = 0.5f - ((atan2f(direction.z, direction.y) + (float)M_PI_2)); |
| 4913 | 4941 | auto canvasSize = getCanvasSize(); |
| 4914 | 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 | 4945 | pos.setX(canvasSize.x / 2.0f + cursorRange * xAngle); |
| 4918 | 4946 | pos.setY(canvasSize.y / 2.0f + cursorRange * yAngle); |
| @@ -14,13 +14,15 @@ · 13 unmodified lines | ||
| 14 | 14 | |
| 15 | 15 | #include <functional> |
| 16 | 16 | |
| 17 | −#include <QApplication> | |
| 18 | −#include <QHash> | |
| 19 | −#include <QImage> | |
| 20 | −#include <QPointer> | |
| 21 | −#include <QSet> | |
| 22 | −#include <QStringList> | |
| 23 | −#include <QUndoStack> | |
| 17 | +#include <QtCore/QHash> | |
| 18 | +#include <QtCore/QPointer> | |
| 19 | +#include <QtCore/QSet> | |
| 20 | +#include <QtCore/QStringList> | |
| 21 | + | |
| 22 | +#include <QtGui/QImage> | |
| 23 | + | |
| 24 | +#include <QtWidgets/QApplication> | |
| 25 | +#include <QtWidgets/QUndoStack> | |
| 24 | 26 | |
| 25 | 27 | #include <AbstractScriptingServicesInterface.h> |
| 26 | 28 | #include <AbstractViewStateInterface.h> |
| @@ -69,6 +71,10 @@ class FaceTracker; · 42 unmodified lines | ||
| 69 | 71 | class MainWindow; |
| 70 | 72 | class AssetUpload; |
| 71 | 73 | |
| 74 | +namespace controller { | |
| 75 | + class StateController; | |
| 76 | +} | |
| 77 | + | |
| 72 | 78 | #ifdef Q_OS_WIN |
| 73 | 79 | static const UINT UWM_IDENTIFY_INSTANCES = |
| 74 | 80 | RegisterWindowMessage("UWM_IDENTIFY_INSTANCES_{8AB82783-B74A-4258-955B-8188C22AA0D6}_" + qgetenv("USERNAME")); |
| @@ -161,7 +167,7 @@ public: · 86 unmodified lines | ||
| 161 | 167 | |
| 162 | 168 | ToolWindow* getToolWindow() { return _toolWindow ; } |
| 163 | 169 | |
| 164 | − virtual AbstractControllerScriptingInterface* getControllerScriptingInterface() { return &_controllerScriptingInterface; } | |
| 170 | + virtual controller::ScriptingInterface* getControllerScriptingInterface() { return _controllerScriptingInterface; } | |
| 165 | 171 | virtual void registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine); |
| 166 | 172 | |
| 167 | 173 | QImage renderAvatarBillboard(RenderArgs* renderArgs); |
| @@ -350,8 +356,8 @@ private: · 182 unmodified lines | ||
| 350 | 356 | |
| 351 | 357 | void update(float deltaTime); |
| 352 | 358 | |
| 353 | − void setPalmData(Hand* hand, UserInputMapper::PoseValue pose, float deltaTime, int index, float triggerValue); | |
| 354 | − void emulateMouse(Hand* hand, float click, float shift, int index); | |
| 359 | + void setPalmData(Hand* hand, const controller::Pose& pose, float deltaTime, HandData::Hand whichHand, float triggerValue); | |
| 360 | + void emulateMouse(Hand* hand, float click, float shift, HandData::Hand whichHand); | |
| 355 | 361 | |
| 356 | 362 | // Various helper functions called during update() |
| 357 | 363 | void updateLOD(); |
| @@ -440,7 +446,8 @@ private: · 82 unmodified lines | ||
| 440 | 446 | |
| 441 | 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 | 451 | AvatarUpdate* _avatarUpdate {nullptr}; |
| 445 | 452 | SimpleMovingAverage _avatarSimsPerSecond {10}; |
| 446 | 453 | int _avatarSimsPerSecondReport {0}; |
| @@ -474,8 +481,7 @@ private: · 27 unmodified lines | ||
| 474 | 481 | |
| 475 | 482 | NodeToJurisdictionMap _entityServerJurisdictions; |
| 476 | 483 | NodeToOctreeSceneStats _octreeServerSceneStats; |
| 477 | − | |
| 478 | − ControllerScriptingInterface _controllerScriptingInterface; | |
| 484 | + ControllerScriptingInterface* _controllerScriptingInterface{ nullptr }; | |
| 479 | 485 | QPointer<LogDialog> _logDialog; |
| 480 | 486 | QPointer<SnapshotShareDialog> _snapshotShareDialog; |
| 481 | 487 | |
| @@ -521,10 +527,13 @@ private: · 39 unmodified lines | ||
| 521 | 527 | ApplicationCompositor _compositor; |
| 522 | 528 | OverlayConductor _overlayConductor; |
| 523 | 529 | |
| 524 | − int _oldHandMouseX[2]; | |
| 525 | − int _oldHandMouseY[2]; | |
| 526 | − bool _oldHandLeftClick[2]; | |
| 527 | − bool _oldHandRightClick[2]; | |
| 530 | + | |
| 531 | + // FIXME - Hand Controller to mouse emulation helpers. This is crufty and should be moved | |
| 532 | + // into the input plugins or something. | |
| 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 | 538 | DialogsManagerScriptingInterface* _dialogsManagerScriptingInterface = new DialogsManagerScriptingInterface(); |
| 530 | 539 | |
| @@ -464,11 +464,13 @@ Menu::Menu() { · 463 unmodified lines | ||
| 464 | 464 | addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::MeshVisible, 0, true, |
| 465 | 465 | avatar, SLOT(setEnableMeshVisible(bool))); |
| 466 | 466 | addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false); |
| 467 | +#if 0 | |
| 467 | 468 | addCheckableActionToQMenuAndActionHash(avatarDebugMenu, |
| 468 | 469 | MenuOption::Connexion, |
| 469 | 470 | 0, false, |
| 470 | 471 | &ConnexionClient::getInstance(), |
| 471 | 472 | SLOT(toggleConnexion(bool))); |
| 473 | +#endif | |
| 472 | 474 | addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ComfortMode, 0, true); |
| 473 | 475 | |
| 474 | 476 | MenuWrapper* handOptionsMenu = developerMenu->addMenu("Hands"); |
| @@ -1177,22 +1177,6 @@ glm::vec3 Avatar::getLeftPalmPosition() { · 1176 unmodified lines | ||
| 1177 | 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 | 1180 | glm::quat Avatar::getLeftPalmRotation() { |
| 1197 | 1181 | glm::quat leftRotation; |
| 1198 | 1182 | getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getLeftHandJointIndex(), leftRotation); |
| @@ -1208,22 +1192,6 @@ glm::vec3 Avatar::getRightPalmPosition() { · 9 unmodified lines | ||
| 1208 | 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 | 1195 | glm::quat Avatar::getRightPalmRotation() { |
| 1228 | 1196 | glm::quat rightRotation; |
| 1229 | 1197 | getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightRotation); |
| @@ -43,21 +43,6 @@ static const float BILLBOARD_DISTANCE = 5.56f; // meters · 42 unmodified lines | ||
| 43 | 43 | extern const float CHAT_MESSAGE_SCALE; |
| 44 | 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 | 47 | enum ScreenTintLayer { |
| 63 | 48 | SCREEN_TINT_BEFORE_LANDSCAPE = 0, |
| @@ -175,13 +160,11 @@ public: · 111 unmodified lines | ||
| 175 | 160 | AvatarMotionState* getMotionState() { return _motionState; } |
| 176 | 161 | |
| 177 | 162 | public slots: |
| 163 | + | |
| 164 | + // FIXME - these should be migrated to use Pose data instead | |
| 178 | 165 | glm::vec3 getLeftPalmPosition(); |
| 179 | − glm::vec3 getLeftPalmVelocity(); | |
| 180 | − glm::vec3 getLeftPalmAngularVelocity(); | |
| 181 | 166 | glm::quat getLeftPalmRotation(); |
| 182 | 167 | glm::vec3 getRightPalmPosition(); |
| 183 | − glm::vec3 getRightPalmVelocity(); | |
| 184 | − glm::vec3 getRightPalmAngularVelocity(); | |
| 185 | 168 | glm::quat getRightPalmRotation(); |
| 186 | 169 | |
| 187 | 170 | protected: |
| @@ -248,6 +248,16 @@ QVector<AvatarManager::LocalLight> AvatarManager::getLocalLights() const { · 247 unmodified lines | ||
| 248 | 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 | 261 | void AvatarManager::getObjectsToDelete(VectorOfMotionStates& result) { |
| 252 | 262 | result.clear(); |
| 253 | 263 | result.swap(_motionStatesToDelete); |
| @@ -52,6 +52,10 @@ public: · 51 unmodified lines | ||
| 52 | 52 | |
| 53 | 53 | Q_INVOKABLE void setLocalLights(const QVector<AvatarManager::LocalLight>& localLights); |
| 54 | 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 | 60 | void getObjectsToDelete(VectorOfMotionStates& motionStates); |
| 57 | 61 | void getObjectsToAdd(VectorOfMotionStates& motionStates); |
| @@ -31,13 +31,7 @@ Hand::Hand(Avatar* owningAvatar) : · 30 unmodified lines | ||
| 31 | 31 | } |
| 32 | 32 | |
| 33 | 33 | void Hand::simulate(float deltaTime, bool isMine) { |
| 34 | − if (isMine) { | |
| 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 | − } | |
| 34 | + // nothing to do here | |
| 41 | 35 | } |
| 42 | 36 | |
| 43 | 37 | void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) { |
| @@ -53,10 +47,11 @@ void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) { · 9 unmodified lines | ||
| 53 | 47 | const glm::vec3 grayColor(0.5f); |
| 54 | 48 | const float SPHERE_RADIUS = 0.03f * avatarScale; |
| 55 | 49 | |
| 50 | + auto palms = getCopyOfPalms(); | |
| 51 | + | |
| 56 | 52 | gpu::Batch& batch = *renderArgs->_batch; |
| 57 | 53 | if (isMine) { |
| 58 | − for (size_t i = 0; i < getNumPalms(); i++) { | |
| 59 | − PalmData& palm = getPalms()[i]; | |
| 54 | + for (const auto& palm : palms) { | |
| 60 | 55 | if (!palm.isActive()) { |
| 61 | 56 | continue; |
| 62 | 57 | } |
| @@ -82,8 +77,7 @@ void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) { · 19 unmodified lines | ||
| 82 | 77 | const float AXIS_LENGTH = 10.0f * SPHERE_RADIUS; |
| 83 | 78 | |
| 84 | 79 | // Draw the coordinate frames of the hand targets |
| 85 | − for (size_t i = 0; i < getNumPalms(); ++i) { | |
| 86 | − PalmData& palm = getPalms()[i]; | |
| 80 | + for (const auto& palm : palms) { | |
| 87 | 81 | if (palm.isActive()) { |
| 88 | 82 | glm::vec3 root = palm.getPosition(); |
| 89 | 83 | |
| @@ -53,6 +53,7 @@ · 52 unmodified lines | ||
| 53 | 53 | |
| 54 | 54 | using namespace std; |
| 55 | 55 | |
| 56 | +static quint64 COMFORT_MODE_PULSE_TIMING = USECS_PER_SECOND / 2; // turn once per half second | |
| 56 | 57 | const glm::vec3 DEFAULT_UP_DIRECTION(0.0f, 1.0f, 0.0f); |
| 57 | 58 | const float YAW_SPEED = 150.0f; // degrees/sec |
| 58 | 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 | 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 | 268 | updateOrientation(deltaTime); |
| 250 | 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 | 602 | // virtual |
| 535 | 603 | void MyAvatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { |
| 536 | 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 | 1602 | void MyAvatar::updateOrientation(float deltaTime) { |
| 1535 | 1603 | // Smoothly rotate body with arrow keys |
| 1536 | − float targetSpeed = 0.0f; | |
| 1537 | − | |
| 1538 | − // FIXME - this comfort mode code is a total hack, remove it when we have new input mapping | |
| 1539 | − bool isComfortMode = Menu::getInstance()->isOptionChecked(MenuOption::ComfortMode); | |
| 1540 | − bool isHMDMode = qApp->getAvatarUpdater()->isHMDMode(); | |
| 1541 | − | |
| 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 | − } | |
| 1604 | + float targetSpeed = _driveKeys[YAW] * YAW_SPEED; | |
| 1605 | + if (targetSpeed != 0.0f) { | |
| 1606 | + const float ROTATION_RAMP_TIMESCALE = 0.1f; | |
| 1607 | + float blend = deltaTime / ROTATION_RAMP_TIMESCALE; | |
| 1608 | + if (blend > 1.0f) { | |
| 1609 | + blend = 1.0f; | |
| 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 | |
| 1568 | − setOrientation(getOrientation() * | |
| 1569 | − glm::quat(glm::radians(glm::vec3(0.0f, _bodyYawDelta * deltaTime, 0.0f)))); | |
| 1570 | − | |
| 1571 | − } else { | |
| 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; | |
| 1621 | + float MINIMUM_ROTATION_RATE = 2.0f; | |
| 1622 | + if (fabsf(_bodyYawDelta) < MINIMUM_ROTATION_RATE) { | |
| 1623 | + _bodyYawDelta = 0.0f; | |
| 1624 | + } | |
| 1625 | + } | |
| 1586 | 1626 | |
| 1587 | − const float SNAP_TURN_DELTA = 15.0f; // degrees | |
| 1588 | − float direction = (driveLeft - driveRight) < 0.0f ? -1.0f : 1.0f; | |
| 1589 | − float turnAmount = direction * SNAP_TURN_DELTA; | |
| 1627 | + float totalBodyYaw = _bodyYawDelta * deltaTime; | |
| 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 | 1643 | if (qApp->getAvatarUpdater()->isHMDMode()) { |
| 1601 | 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 | 1698 | float motorEfficiency = glm::clamp(deltaTime / timescale, 0.0f, 1.0f); |
| 1656 | 1699 | |
| 1657 | 1700 | glm::vec3 newLocalVelocity = localVelocity; |
| 1658 | − float keyboardInput = fabsf(_driveKeys[FWD] - _driveKeys[BACK]) + | |
| 1659 | − (fabsf(_driveKeys[RIGHT] - _driveKeys[LEFT])) + | |
| 1660 | − fabsf(_driveKeys[UP] - _driveKeys[DOWN]); | |
| 1701 | + float stepControllerInput = fabsf(_driveKeys[STEP_TRANSLATE_Z]) + fabsf(_driveKeys[STEP_TRANSLATE_Z]) + fabsf(_driveKeys[STEP_TRANSLATE_Z]); | |
| 1702 | + quint64 now = usecTimestampNow(); | |
| 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 | 1708 | if (keyboardInput) { |
| 1662 | 1709 | // Compute keyboard input |
| 1663 | − glm::vec3 front = (_driveKeys[FWD] - _driveKeys[BACK]) * IDENTITY_FRONT; | |
| 1664 | − glm::vec3 right = (_driveKeys[RIGHT] - _driveKeys[LEFT]) * IDENTITY_RIGHT; | |
| 1665 | − glm::vec3 up = (_driveKeys[UP] - _driveKeys[DOWN]) * IDENTITY_UP; | |
| 1710 | + glm::vec3 front = (_driveKeys[TRANSLATE_Z]) * IDENTITY_FRONT; | |
| 1711 | + glm::vec3 right = (_driveKeys[TRANSLATE_X]) * IDENTITY_RIGHT; | |
| 1712 | + glm::vec3 up = (_driveKeys[TRANSLATE_Y]) * IDENTITY_UP; | |
| 1666 | 1713 | |
| 1667 | 1714 | glm::vec3 direction = front + right + up; |
| 1668 | 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 | 1764 | _boomLength += 2.0f * _boomLength * boomChange + boomChange * boomChange; |
| 1718 | 1765 | _boomLength = glm::clamp<float>(_boomLength, ZOOM_MIN, ZOOM_MAX); |
| 1719 | 1766 | |
| @@ -1915,28 +1962,6 @@ void MyAvatar::updateMotionBehaviorFromMenu() { · 195 unmodified lines | ||
| 1915 | 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 | 1965 | //Gets the tip position for the laser pointer |
| 1941 | 1966 | glm::vec3 MyAvatar::getLaserPointerTipPosition(const PalmData* palm) { |
| 1942 | 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 | 1989 | void MyAvatar::relayDriveKeysToCharacterController() { |
| 1965 | − if (_driveKeys[UP] > 0.0f) { | |
| 1990 | + if (_driveKeys[TRANSLATE_Y] > 0.0f) { | |
| 1966 | 1991 | _characterController.jump(); |
| 1967 | 1992 | } |
| 1968 | 1993 | } |
| @@ -17,6 +17,8 @@ · 16 unmodified lines | ||
| 17 | 17 | #include <SettingHandle.h> |
| 18 | 18 | #include <Rig.h> |
| 19 | 19 | |
| 20 | +#include <controllers/Pose.h> | |
| 21 | + | |
| 20 | 22 | #include "Avatar.h" |
| 21 | 23 | #include "AtRestDetector.h" |
| 22 | 24 | #include "MyCharacterController.h" |
| @@ -24,6 +26,20 @@ · 1 unmodified line | ||
| 24 | 26 | |
| 25 | 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 | 43 | enum eyeContactTarget { |
| 28 | 44 | LEFT_EYE, |
| 29 | 45 | RIGHT_EYE, |
| @@ -37,7 +53,6 @@ enum AudioListenerMode { · 7 unmodified lines | ||
| 37 | 53 | }; |
| 38 | 54 | Q_DECLARE_METATYPE(AudioListenerMode); |
| 39 | 55 | |
| 40 | − | |
| 41 | 56 | class MyAvatar : public Avatar { |
| 42 | 57 | Q_OBJECT |
| 43 | 58 | Q_PROPERTY(bool shouldRenderLocally READ getShouldRenderLocally WRITE setShouldRenderLocally) |
| @@ -53,6 +68,17 @@ class MyAvatar : public Avatar { · 9 unmodified lines | ||
| 53 | 68 | Q_PROPERTY(AudioListenerMode CUSTOM READ getAudioListenerModeCustom) |
| 54 | 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 | 82 | public: |
| 57 | 83 | MyAvatar(RigPointer rig); |
| 58 | 84 | ~MyAvatar(); |
| @@ -111,6 +137,18 @@ public: · 52 unmodified lines | ||
| 111 | 137 | Q_INVOKABLE AnimationDetails getAnimationDetailsByRole(const QString& role); |
| 112 | 138 | Q_INVOKABLE AnimationDetails getAnimationDetails(const QString& url); |
| 113 | 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 | 153 | // get/set avatar data |
| 116 | 154 | void saveData(); |
| @@ -141,6 +179,16 @@ public: · 24 unmodified lines | ||
| 141 | 179 | |
| 142 | 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 | 192 | AvatarWeakPointer getLookAtTargetAvatar() const { return _lookAtTargetAvatar; } |
| 145 | 193 | void updateLookAtTargetAvatar(); |
| 146 | 194 | void clearLookAtTargetAvatar(); |
| @@ -265,7 +313,6 @@ private: · 118 unmodified lines | ||
| 265 | 313 | const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), float scale = 1.0f, |
| 266 | 314 | bool allowDuplicates = false, bool useSaved = true) override; |
| 267 | 315 | |
| 268 | − void renderLaserPointers(gpu::Batch& batch); | |
| 269 | 316 | const RecorderPointer getRecorder() const { return _recorder; } |
| 270 | 317 | const PlayerPointer getPlayer() const { return _player; } |
| 271 | 318 | |
| @@ -281,6 +328,8 @@ private: · 9 unmodified lines | ||
| 281 | 328 | |
| 282 | 329 | void setVisibleInSceneIfReady(Model* model, render::ScenePointer scene, bool visiblity); |
| 283 | 330 | |
| 331 | + PalmData getActivePalmData(int palmIndex) const; | |
| 332 | + | |
| 284 | 333 | // derive avatar body position and orientation from the current HMD Sensor location. |
| 285 | 334 | // results are in sensor space |
| 286 | 335 | glm::mat4 deriveBodyFromHMDSensor() const; |
| @@ -370,6 +419,8 @@ private: · 83 unmodified lines | ||
| 370 | 419 | AtRestDetector _hmdAtRestDetector; |
| 371 | 420 | glm::vec3 _lastPosition; |
| 372 | 421 | bool _lastIsMoving { false }; |
| 422 | + quint64 _lastStepPulse { 0 }; | |
| 423 | + bool _pulseUpdate { false }; | |
| 373 | 424 | }; |
| 374 | 425 | |
| 375 | 426 | QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); |
| @@ -97,23 +97,9 @@ void SkeletonModel::initJointStates(QVector<JointState> states) { · 96 unmodified lines | ||
| 97 | 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 | 100 | const float PALM_PRIORITY = DEFAULT_PRIORITY; |
| 112 | 101 | // Called within Model::simulate call, below. |
| 113 | 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 | 103 | Head* head = _owningAvatar->getHead(); |
| 118 | 104 | if (_owningAvatar->isMyAvatar()) { |
| 119 | 105 | MyAvatar* myAvatar = static_cast<MyAvatar*>(_owningAvatar); |
| @@ -160,28 +146,30 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { · 40 unmodified lines | ||
| 160 | 146 | _rig->updateFromHeadParameters(headParams, deltaTime); |
| 161 | 147 | |
| 162 | 148 | Rig::HandParameters handParams; |
| 163 | − const PalmData* leftPalm = getPalmWithIndex(myAvatar->getHand(), LEFT_HAND_INDEX); | |
| 164 | − if (leftPalm && leftPalm->isActive()) { | |
| 149 | + | |
| 150 | + auto leftPalm = myAvatar->getHand()->getCopyOfPalmData(HandData::LeftHand); | |
| 151 | + if (leftPalm.isValid() && leftPalm.isActive()) { | |
| 165 | 152 | handParams.isLeftEnabled = true; |
| 166 | − handParams.leftPosition = leftPalm->getRawPosition(); | |
| 167 | − handParams.leftOrientation = leftPalm->getRawRotation(); | |
| 168 | − handParams.leftTrigger = leftPalm->getTrigger(); | |
| 153 | + handParams.leftPosition = leftPalm.getRawPosition(); | |
| 154 | + handParams.leftOrientation = leftPalm.getRawRotation(); | |
| 155 | + handParams.leftTrigger = leftPalm.getTrigger(); | |
| 169 | 156 | } else { |
| 170 | 157 | handParams.isLeftEnabled = false; |
| 171 | 158 | } |
| 172 | 159 | |
| 173 | − const PalmData* rightPalm = getPalmWithIndex(myAvatar->getHand(), RIGHT_HAND_INDEX); | |
| 174 | − if (rightPalm && rightPalm->isActive()) { | |
| 160 | + auto rightPalm = myAvatar->getHand()->getCopyOfPalmData(HandData::RightHand); | |
| 161 | + if (rightPalm.isValid() && rightPalm.isActive()) { | |
| 175 | 162 | handParams.isRightEnabled = true; |
| 176 | − handParams.rightPosition = rightPalm->getRawPosition(); | |
| 177 | − handParams.rightOrientation = rightPalm->getRawRotation(); | |
| 178 | − handParams.rightTrigger = rightPalm->getTrigger(); | |
| 163 | + handParams.rightPosition = rightPalm.getRawPosition(); | |
| 164 | + handParams.rightOrientation = rightPalm.getRawRotation(); | |
| 165 | + handParams.rightTrigger = rightPalm.getTrigger(); | |
| 179 | 166 | } else { |
| 180 | 167 | handParams.isRightEnabled = false; |
| 181 | 168 | } |
| 182 | 169 | |
| 183 | 170 | _rig->updateFromHandParameters(handParams, deltaTime); |
| 184 | 171 | |
| 172 | + _rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation()); | |
| 185 | 173 | // evaluate AnimGraph animation and update jointStates. |
| 186 | 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 | 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 | 188 | _rig->updateJointState(eyeParams.leftEyeJointIndex, parentTransform); |
| 201 | 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 | 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 | 257 | // Don't Relax toward hand positions when in animGraph mode. |
| 275 | 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 | 264 | const float HAND_RESTORATION_RATE = 0.25f; |
| 277 | − if (leftPalmIndex == -1 && rightPalmIndex == -1) { | |
| 265 | + if (!leftPalm.isActive() && !rightPalm.isActive()) { | |
| 278 | 266 | // palms are not yet set, use mouse |
| 279 | 267 | if (_owningAvatar->getHandState() == HAND_STATE_NULL) { |
| 280 | 268 | restoreRightHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY); |
| @@ -284,20 +272,14 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { · 3 unmodified lines | ||
| 284 | 272 | applyHandPosition(geometry.rightHandJointIndex, handPosition); |
| 285 | 273 | } |
| 286 | 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 | 275 | } else { |
| 294 | − if (leftPalmIndex != -1) { | |
| 295 | − applyPalmData(geometry.leftHandJointIndex, hand->getPalms()[leftPalmIndex]); | |
| 276 | + if (leftPalm.isActive()) { | |
| 277 | + applyPalmData(geometry.leftHandJointIndex, leftPalm); | |
| 296 | 278 | } else { |
| 297 | 279 | restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY); |
| 298 | 280 | } |
| 299 | − if (rightPalmIndex != -1) { | |
| 300 | − applyPalmData(geometry.rightHandJointIndex, hand->getPalms()[rightPalmIndex]); | |
| 281 | + if (rightPalm.isActive()) { | |
| 282 | + applyPalmData(geometry.rightHandJointIndex, rightPalm); | |
| 301 | 283 | } else { |
| 302 | 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 | 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 | 334 | if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) { |
| 353 | 335 | return; |
| 354 | 336 | } |
| @@ -118,7 +118,7 @@ protected: · 117 unmodified lines | ||
| 118 | 118 | /// \param position position of joint in model-frame |
| 119 | 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 | 122 | private: |
| 123 | 123 | |
| 124 | 124 | void renderJointConstraints(gpu::Batch& batch, int jointIndex); |
| @@ -9,9 +9,14 @@ · 8 unmodified lines | ||
| 9 | 9 | // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html |
| 10 | 10 | // |
| 11 | 11 | |
| 12 | + | |
| 12 | 13 | #include "3DConnexionClient.h" |
| 14 | + | |
| 15 | +#if 0 | |
| 16 | +#include <UserActivityLogger.h> | |
| 17 | +#include <PathUtils.h> | |
| 18 | + | |
| 13 | 19 | #include "Menu.h" |
| 14 | −#include "UserActivityLogger.h" | |
| 15 | 20 | |
| 16 | 21 | const float MAX_AXIS = 75.0f; // max forward = 2x speed |
| 17 | 22 | |
| @@ -25,22 +30,19 @@ ConnexionData& ConnexionData::getInstance() { · 7 unmodified lines | ||
| 25 | 30 | return sharedInstance; |
| 26 | 31 | } |
| 27 | 32 | |
| 28 | −ConnexionData::ConnexionData() { | |
| 29 | −} | |
| 33 | + | |
| 34 | +ConnexionData::ConnexionData() : InputDevice("ConnexionClient") {} | |
| 35 | + | |
| 30 | 36 | |
| 31 | 37 | void ConnexionData::handleAxisEvent() { |
| 32 | − _axisStateMap[makeInput(ROTATION_AXIS_Y_POS).getChannel()] = (cc_rotation.y > 0.0f) ? cc_rotation.y / MAX_AXIS : 0.0f; | |
| 33 | − _axisStateMap[makeInput(ROTATION_AXIS_Y_NEG).getChannel()] = (cc_rotation.y < 0.0f) ? -cc_rotation.y / MAX_AXIS : 0.0f; | |
| 34 | − _axisStateMap[makeInput(POSITION_AXIS_X_POS).getChannel()] = (cc_position.x > 0.0f) ? cc_position.x / MAX_AXIS : 0.0f; | |
| 35 | − _axisStateMap[makeInput(POSITION_AXIS_X_NEG).getChannel()] = (cc_position.x < 0.0f) ? -cc_position.x / MAX_AXIS : 0.0f; | |
| 36 | − _axisStateMap[makeInput(POSITION_AXIS_Y_POS).getChannel()] = (cc_position.y > 0.0f) ? cc_position.y / MAX_AXIS : 0.0f; | |
| 37 | − _axisStateMap[makeInput(POSITION_AXIS_Y_NEG).getChannel()] = (cc_position.y < 0.0f) ? -cc_position.y / MAX_AXIS : 0.0f; | |
| 38 | − _axisStateMap[makeInput(POSITION_AXIS_Z_POS).getChannel()] = (cc_position.z > 0.0f) ? cc_position.z / MAX_AXIS : 0.0f; | |
| 39 | − _axisStateMap[makeInput(POSITION_AXIS_Z_NEG).getChannel()] = (cc_position.z < 0.0f) ? -cc_position.z / MAX_AXIS : 0.0f; | |
| 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; | |
| 38 | + auto rotation = cc_rotation / MAX_AXIS; | |
| 39 | + _axisStateMap[ROTATE_X] = rotation.x; | |
| 40 | + _axisStateMap[ROTATE_Y] = rotation.y; | |
| 41 | + _axisStateMap[ROTATE_Z] = rotation.z; | |
| 42 | + auto position = cc_rotation / MAX_AXIS; | |
| 43 | + _axisStateMap[TRANSLATE_X] = position.x; | |
| 44 | + _axisStateMap[TRANSLATE_Y] = position.y; | |
| 45 | + _axisStateMap[TRANSLATE_Z] = position.z; | |
| 44 | 46 | } |
| 45 | 47 | |
| 46 | 48 | void ConnexionData::setButton(int lastButtonState) { |
| @@ -48,76 +50,65 @@ void ConnexionData::setButton(int lastButtonState) { · 1 unmodified line | ||
| 48 | 50 | _buttonPressedMap.insert(lastButtonState); |
| 49 | 51 | } |
| 50 | 52 | |
| 51 | −void ConnexionData::registerToUserInputMapper(UserInputMapper& mapper) { | |
| 52 | − // Grab the current free device ID | |
| 53 | − _deviceID = mapper.getFreeDeviceID(); | |
| 54 | − | |
| 55 | − auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy("ConnexionClient")); | |
| 56 | − proxy->getButton = [this](const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; | |
| 57 | − proxy->getAxis = [this](const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; | |
| 58 | − proxy->getAvailabeInputs = [this]() -> QVector<UserInputMapper::InputPair> { | |
| 59 | − QVector<UserInputMapper::InputPair> availableInputs; | |
| 60 | − | |
| 61 | − availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_1), "Left button")); | |
| 62 | − availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_2), "Right button")); | |
| 63 | − availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_3), "Both buttons")); | |
| 64 | − | |
| 65 | − availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_Y_NEG), "Move backward")); | |
| 66 | − availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_Y_POS), "Move forward")); | |
| 67 | − availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_X_POS), "Move right")); | |
| 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 | − | |
| 53 | +void ConnexionData::buildDeviceProxy(controller::DeviceProxy::Pointer proxy) { | |
| 54 | + proxy->_name = _name = "ConnexionClient"; | |
| 55 | + proxy->getButton = [this](const controller::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; | |
| 56 | + proxy->getAxis = [this](const controller::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; | |
| 57 | + proxy->getAvailabeInputs = [this]() -> QVector<controller::Input::NamedPair> { | |
| 58 | + using namespace controller; | |
| 59 | + static QVector<controller::Input::NamedPair> availableInputs { | |
| 60 | + Input::NamedPair(makeInput(BUTTON_1), "LeftButton"), | |
| 61 | + Input::NamedPair(makeInput(BUTTON_2), "RightButton"), | |
| 62 | + Input::NamedPair(makeInput(BUTTON_3), "BothButtons"), | |
| 63 | + Input::NamedPair(makeInput(TRANSLATE_X), "TranslateX"), | |
| 64 | + Input::NamedPair(makeInput(TRANSLATE_Y), "TranslateY"), | |
| 65 | + Input::NamedPair(makeInput(TRANSLATE_Z), "TranslateZ"), | |
| 66 | + Input::NamedPair(makeInput(ROTATE_X), "RotateX"), | |
| 67 | + Input::NamedPair(makeInput(ROTATE_Y), "RotateY"), | |
| 68 | + Input::NamedPair(makeInput(ROTATE_Z), "RotateZ"), | |
| 69 | + }; | |
| 78 | 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) { | |
| 89 | − const float JOYSTICK_MOVE_SPEED = 1.0f; | |
| 90 | − //const float DPAD_MOVE_SPEED = 0.5f; | |
| 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 | − | |
| 74 | +QString ConnexionData::getDefaultMappingConfig() { | |
| 75 | + static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/vive.json"; | |
| 76 | + return MAPPING_JSON; | |
| 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 | 112 | float ConnexionData::getButton(int channel) const { |
| 122 | 113 | if (!_buttonPressedMap.empty()) { |
| 123 | 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) { | |
| 142 | − return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON); | |
| 132 | +controller::Input ConnexionData::makeInput(ConnexionData::ButtonChannel button) { | |
| 133 | + return controller::Input(_deviceID, button, controller::ChannelType::BUTTON); | |
| 143 | 134 | } |
| 144 | 135 | |
| 145 | −UserInputMapper::Input ConnexionData::makeInput(ConnexionData::PositionChannel axis) { | |
| 146 | − return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS); | |
| 136 | +controller::Input ConnexionData::makeInput(ConnexionData::PositionChannel 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 | 141 | // the update is done in the ConnexionClient class. |
| 151 | 142 | // for windows in the nativeEventFilter the inputmapper is connected or registed or removed when an 3Dconnnexion device is attached or detached |
| 152 | 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 | 178 | QAbstractEventDispatcher::instance()->removeNativeEventFilter(this); |
| 188 | 179 | ConnexionData& connexiondata = ConnexionData::getInstance(); |
| 189 | 180 | int deviceid = connexiondata.getDeviceID(); |
| 190 | − connexiondata.setDeviceID(0); | |
| 191 | 181 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); |
| 192 | 182 | userInputMapper->removeDevice(deviceid); |
| 193 | 183 | } |
| @@ -295,13 +285,10 @@ bool ConnexionClient::RawInputEventFilter(void* msg, long* result) { · 101 unmodified lines | ||
| 295 | 285 | ConnexionData& connexiondata = ConnexionData::getInstance(); |
| 296 | 286 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); |
| 297 | 287 | if (Is3dmouseAttached() && connexiondata.getDeviceID() == 0) { |
| 298 | − connexiondata.registerToUserInputMapper(*userInputMapper); | |
| 299 | − connexiondata.assignDefaultInputMapping(*userInputMapper); | |
| 288 | + userInputMapper->registerDevice(&connexiondata); | |
| 300 | 289 | UserActivityLogger::getInstance().connectedDevice("controller", "3Dconnexion"); |
| 301 | 290 | } else if (!Is3dmouseAttached() && connexiondata.getDeviceID() != 0) { |
| 302 | − int deviceid = connexiondata.getDeviceID(); | |
| 303 | − connexiondata.setDeviceID(0); | |
| 304 | − userInputMapper->removeDevice(deviceid); | |
| 291 | + userInputMapper->removeDevice(connexiondata.getDeviceID()); | |
| 305 | 292 | } |
| 306 | 293 | |
| 307 | 294 | if (!Is3dmouseAttached()) { |
| @@ -894,8 +881,7 @@ void ConnexionClient::init() { · 586 unmodified lines | ||
| 894 | 881 | |
| 895 | 882 | if (Is3dmouseAttached() && connexiondata.getDeviceID() == 0) { |
| 896 | 883 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); |
| 897 | − connexiondata.registerToUserInputMapper(*userInputMapper); | |
| 898 | − connexiondata.assignDefaultInputMapping(*userInputMapper); | |
| 884 | + userInputMapper->registerDevice(&connexiondata); | |
| 899 | 885 | UserActivityLogger::getInstance().connectedDevice("controller", "3Dconnexion"); |
| 900 | 886 | } |
| 901 | 887 | //let one axis be dominant |
| @@ -926,8 +912,7 @@ void DeviceAddedHandler(unsigned int connection) { · 24 unmodified lines | ||
| 926 | 912 | if (connexiondata.getDeviceID() == 0) { |
| 927 | 913 | qCWarning(interfaceapp) << "3Dconnexion device added "; |
| 928 | 914 | auto userInputMapper = DependencyManager::get<UserInputMapper>(); |
| 929 | − connexiondata.registerToUserInputMapper(*userInputMapper); | |
| 930 | − connexiondata.assignDefaultInputMapping(*userInputMapper); | |
| 915 | + userInputMapper->registerDevice(&connexiondata); | |
| 931 | 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 | 969 | #endif // __APPLE__ |
| 985 | 970 | |
| 986 | 971 | #endif // HAVE_3DCONNEXIONCLIENT |
| 972 | +#endif | |
| @@ -11,9 +11,10 @@ · 10 unmodified lines | ||
| 11 | 11 | #ifndef hifi_3DConnexionClient_h |
| 12 | 12 | #define hifi_3DConnexionClient_h |
| 13 | 13 | |
| 14 | +#if 0 | |
| 14 | 15 | #include <QObject> |
| 15 | 16 | #include <QLibrary> |
| 16 | −#include <input-plugins/UserInputMapper.h> | |
| 17 | +#include <controllers/UserInputMapper.h> | |
| 17 | 18 | |
| 18 | 19 | #include "InterfaceLogging.h" |
| 19 | 20 | |
| @@ -175,26 +176,19 @@ public slots: · 155 unmodified lines | ||
| 175 | 176 | |
| 176 | 177 | |
| 177 | 178 | // connnects to the userinputmapper |
| 178 | −class ConnexionData : public QObject { | |
| 179 | +class ConnexionData : public QObject, public controller::InputDevice { | |
| 179 | 180 | Q_OBJECT |
| 180 | 181 | |
| 181 | 182 | public: |
| 182 | 183 | static ConnexionData& getInstance(); |
| 183 | 184 | ConnexionData(); |
| 184 | − | |
| 185 | 185 | enum PositionChannel { |
| 186 | − POSITION_AXIS_X_POS = 1, | |
| 187 | − POSITION_AXIS_X_NEG = 2, | |
| 188 | − POSITION_AXIS_Y_POS = 3, | |
| 189 | − POSITION_AXIS_Y_NEG = 4, | |
| 190 | − POSITION_AXIS_Z_POS = 5, | |
| 191 | − POSITION_AXIS_Z_NEG = 6, | |
| 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 | |
| 186 | + TRANSLATE_X, | |
| 187 | + TRANSLATE_Y, | |
| 188 | + TRANSLATE_Z, | |
| 189 | + ROTATE_X, | |
| 190 | + ROTATE_Y, | |
| 191 | + ROTATE_Z, | |
| 198 | 192 | }; |
| 199 | 193 | |
| 200 | 194 | enum ButtonChannel { |
| @@ -209,19 +203,12 @@ public: · 8 unmodified lines | ||
| 209 | 203 | float getButton(int channel) const; |
| 210 | 204 | float getAxis(int channel) const; |
| 211 | 205 | |
| 212 | − UserInputMapper::Input makeInput(ConnexionData::PositionChannel axis); | |
| 213 | − UserInputMapper::Input makeInput(ConnexionData::ButtonChannel button); | |
| 214 | − | |
| 215 | − void registerToUserInputMapper(UserInputMapper& mapper); | |
| 216 | − void assignDefaultInputMapping(UserInputMapper& mapper); | |
| 217 | − | |
| 218 | − void update(); | |
| 219 | − void focusOutEvent(); | |
| 220 | − | |
| 221 | − int getDeviceID() { return _deviceID; } | |
| 222 | − void setDeviceID(int deviceID) { _deviceID = deviceID; } | |
| 223 | − | |
| 224 | − QString _name; | |
| 206 | + controller::Input makeInput(ConnexionData::PositionChannel axis); | |
| 207 | + controller::Input makeInput(ConnexionData::ButtonChannel button); | |
| 208 | + virtual void buildDeviceProxy(controller::DeviceProxy::Pointer proxy) override; | |
| 209 | + virtual QString getDefaultMappingConfig() override; | |
| 210 | + virtual void update(float deltaTime, bool jointsCaptured) override; | |
| 211 | + virtual void focusOutEvent() override; | |
| 225 | 212 | |
| 226 | 213 | glm::vec3 cc_position; |
| 227 | 214 | glm::vec3 cc_rotation; |
| @@ -229,12 +216,8 @@ public: · 1 unmodified line | ||
| 229 | 216 | |
| 230 | 217 | void setButton(int lastButtonState); |
| 231 | 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 | 223 | #endif // defined(hifi_3DConnexionClient_h) |
| @@ -9,120 +9,20 @@ · 8 unmodified lines | ||
| 9 | 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 | 14 | #include <avatar/AvatarManager.h> |
| 13 | 15 | #include <avatar/MyAvatar.h> |
| 14 | 16 | #include <HandData.h> |
| 15 | 17 | #include <HFBackEvent.h> |
| 18 | +#include <plugins/PluginManager.h> | |
| 16 | 19 | |
| 17 | 20 | #include "Application.h" |
| 18 | 21 | #include "devices/MotionTracker.h" |
| 19 | −#include "ControllerScriptingInterface.h" | |
| 20 | 22 | |
| 21 | 23 | // TODO: this needs to be removed, as well as any related controller-specific information |
| 22 | 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 | 26 | void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) { |
| 127 | 27 | if (event->type() == HFActionEvent::startType()) { |
| 128 | 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 | 38 | bool ControllerScriptingInterface::isKeyCaptured(QKeyEvent* event) const { |
| 338 | 39 | return isKeyCaptured(KeyEvent(*event)); |
| 339 | 40 | } |
| @@ -383,157 +84,45 @@ glm::vec2 ControllerScriptingInterface::getViewportDimensions() const { · 43 unmodified lines | ||
| 383 | 84 | return qApp->getUiSize(); |
| 384 | 85 | } |
| 385 | 86 | |
| 386 | −QString ControllerScriptingInterface::sanatizeName(const QString& name) { | |
| 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) { | |
| 87 | +controller::InputController::Pointer ControllerScriptingInterface::createInputController(const QString& deviceName, const QString& tracker) { | |
| 432 | 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); | |
| 434 | − InputControllerMap::iterator icIt = _inputControllers.find(0); | |
| 435 | − | |
| 89 | + auto icIt = _inputControllers.find(0); | |
| 436 | 90 | if (icIt != _inputControllers.end()) { |
| 437 | 91 | return (*icIt).second; |
| 438 | − } else { | |
| 439 | − | |
| 440 | − // Look for device | |
| 441 | − DeviceTracker::ID deviceID = DeviceTracker::getDeviceID(deviceName.toStdString()); | |
| 442 | − if (deviceID < 0) { | |
| 443 | − deviceID = 0; | |
| 444 | − } | |
| 445 | − // TODO in this current implementation, we just pick the device assuming there is one (normally the Leapmotion) | |
| 446 | − // in the near future we need to change that to a real mapping between the devices and the deviceName | |
| 447 | − // ALso we need to expand the spec so we can fall back on the "default" controller per categories | |
| 448 | − | |
| 449 | − if (deviceID >= 0) { | |
| 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 | |
| 451 | − MotionTracker* motionTracker = dynamic_cast< MotionTracker* > (DeviceTracker::getDevice(deviceID)); | |
| 452 | − if (motionTracker) { | |
| 453 | − MotionTracker::Index trackerID = motionTracker->findJointIndex(tracker.toStdString()); | |
| 454 | − if (trackerID >= 0) { | |
| 455 | − AbstractInputController* inputController = new InputController(deviceID, trackerID, this); | |
| 456 | − | |
| 457 | − _inputControllers.insert(InputControllerMap::value_type(inputController->getKey(), inputController)); | |
| 458 | − | |
| 459 | − return inputController; | |
| 460 | − } | |
| 92 | + } | |
| 93 | + | |
| 94 | + | |
| 95 | + // Look for device | |
| 96 | + DeviceTracker::ID deviceID = DeviceTracker::getDeviceID(deviceName.toStdString()); | |
| 97 | + if (deviceID < 0) { | |
| 98 | + deviceID = 0; | |
| 99 | + } | |
| 100 | + // TODO in this current implementation, we just pick the device assuming there is one (normally the Leapmotion) | |
| 101 | + // in the near future we need to change that to a real mapping between the devices and the deviceName | |
| 102 | + // ALso we need to expand the spec so we can fall back on the "default" controller per categories | |
| 103 | + | |
| 104 | + if (deviceID >= 0) { | |
| 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 | |
| 106 | + MotionTracker* motionTracker = dynamic_cast< MotionTracker* > (DeviceTracker::getDevice(deviceID)); | |
| 107 | + if (motionTracker) { | |
| 108 | + MotionTracker::Index trackerID = motionTracker->findJointIndex(tracker.toStdString()); | |
| 109 | + if (trackerID >= 0) { | |
| 110 | + controller::InputController::Pointer inputController = std::make_shared<InputController>(deviceID, trackerID, this); | |
| 111 | + controller::InputController::Key key = inputController->getKey(); | |
| 112 | + _inputControllers.insert(InputControllerMap::value_type(key, inputController)); | |
| 113 | + return inputController; | |
| 461 | 114 | } |
| 462 | 115 | } |
| 463 | − | |
| 464 | − return 0; | |
| 465 | 116 | } |
| 466 | −} | |
| 467 | 117 | |
| 468 | −void ControllerScriptingInterface::releaseInputController(AbstractInputController* input) { | |
| 469 | − _inputControllers.erase(input->getKey()); | |
| 118 | + return controller::InputController::Pointer(); | |
| 470 | 119 | } |
| 471 | 120 | |
| 472 | −void ControllerScriptingInterface::updateInputControllers() { | |
| 473 | − //TODO C++11 for (auto it = _inputControllers.begin(); it != _inputControllers.end(); it++) { | |
| 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(); | |
| 121 | +void ControllerScriptingInterface::releaseInputController(controller::InputController::Pointer input) { | |
| 122 | + _inputControllers.erase(input->getKey()); | |
| 533 | 123 | } |
| 534 | 124 | |
| 535 | 125 | InputController::InputController(int deviceTrackerId, int subTrackerId, QObject* parent) : |
| 536 | − AbstractInputController(), | |
| 537 | 126 | _deviceTrackerId(deviceTrackerId), |
| 538 | 127 | _subTrackerId(subTrackerId), |
| 539 | 128 | _isActive(false) |
| @@ -556,7 +145,7 @@ void InputController::update() { · 16 unmodified lines | ||
| 556 | 145 | joint->getLocFrame().getRotation(_eventCache.locRotation); |
| 557 | 146 | |
| 558 | 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 | 157 | InputController::Key InputController::getKey() const { |
| 569 | 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 | 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 | 28 | class PalmData; |
| 21 | 29 | |
| 22 | −class InputController : public AbstractInputController { | |
| 30 | +class InputController : public controller::InputController { | |
| 23 | 31 | Q_OBJECT |
| 24 | 32 | |
| 25 | 33 | public: |
| @@ -50,125 +58,77 @@ signals: · 24 unmodified lines | ||
| 50 | 58 | |
| 51 | 59 | |
| 52 | 60 | /// handles scripting of input controller commands from JS |
| 53 | −class ControllerScriptingInterface : public AbstractControllerScriptingInterface { | |
| 61 | +class ControllerScriptingInterface : public controller::ScriptingInterface { | |
| 54 | 62 | Q_OBJECT |
| 55 | 63 | |
| 64 | + | |
| 56 | 65 | public: |
| 57 | − ControllerScriptingInterface(); | |
| 58 | − | |
| 59 | − virtual void registerControllerTypes(ScriptEngine* engine); | |
| 60 | − | |
| 61 | − void emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); } | |
| 62 | − void emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(KeyEvent(*event)); } | |
| 66 | + virtual ~ControllerScriptingInterface() {} | |
| 67 | + | |
| 68 | + void emitKeyPressEvent(QKeyEvent* event); | |
| 69 | + void emitKeyReleaseEvent(QKeyEvent* event); | |
| 63 | 70 | |
| 64 | 71 | void handleMetaEvent(HFMetaEvent* event); |
| 65 | 72 | |
| 66 | − void emitMouseMoveEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mouseMoveEvent(MouseEvent(*event, deviceID)); } | |
| 67 | − void emitMousePressEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mousePressEvent(MouseEvent(*event, deviceID)); } | |
| 68 | − void emitMouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mouseDoublePressEvent(MouseEvent(*event, deviceID)); } | |
| 69 | − void emitMouseReleaseEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mouseReleaseEvent(MouseEvent(*event, deviceID)); } | |
| 73 | + void emitMouseMoveEvent(QMouseEvent* event, unsigned int deviceID = 0); | |
| 74 | + void emitMousePressEvent(QMouseEvent* event, unsigned int deviceID = 0); | |
| 75 | + void emitMouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID = 0); | |
| 76 | + void emitMouseReleaseEvent(QMouseEvent* event, unsigned int deviceID = 0); | |
| 70 | 77 | |
| 71 | − void emitTouchBeginEvent(const TouchEvent& event) { emit touchBeginEvent(event); } | |
| 72 | − void emitTouchEndEvent(const TouchEvent& event) { emit touchEndEvent(event); } | |
| 73 | − void emitTouchUpdateEvent(const TouchEvent& event) { emit touchUpdateEvent(event); } | |
| 78 | + void emitTouchBeginEvent(const TouchEvent& event); | |
| 79 | + void emitTouchEndEvent(const TouchEvent& 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 | 84 | bool isKeyCaptured(QKeyEvent* event) const; |
| 78 | 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 | 86 | bool isJoystickCaptured(int joystickIndex) const; |
| 84 | 87 | |
| 85 | − void updateInputControllers(); | |
| 86 | − | |
| 87 | 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 | 90 | virtual void captureKeyEvents(const KeyEvent& event); |
| 128 | 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 | 93 | virtual void captureJoystick(int joystickIndex); |
| 143 | 94 | virtual void releaseJoystick(int joystickIndex); |
| 144 | 95 | |
| 145 | 96 | virtual glm::vec2 getViewportDimensions() const; |
| 146 | 97 | |
| 147 | 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 | 123 | private: |
| 153 | 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 | 126 | QMultiMap<int,KeyEvent> _capturedKeys; |
| 165 | 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 | 131 | InputControllerMap _inputControllers; |
| 169 | − | |
| 170 | − void wireUpControllers(ScriptEngine* engine); | |
| 171 | − | |
| 172 | 132 | }; |
| 173 | 133 | |
| 174 | 134 | const int NUMBER_OF_SPATIALCONTROLS_PER_PALM = 2; // the hand and the tip |
| @@ -27,7 +27,7 @@ · 26 unmodified lines | ||
| 27 | 27 | |
| 28 | 28 | #include "Application.h" |
| 29 | 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 | 33 | // Used to animate the magnification windows |
| @@ -320,8 +320,8 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int · 286 unmodified lines | ||
| 320 | 320 | // Only render the hand pointers if the EnableHandMouseInput is enabled |
| 321 | 321 | if (Menu::getInstance()->isOptionChecked(MenuOption::EnableHandMouseInput)) { |
| 322 | 322 | MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); |
| 323 | − for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) { | |
| 324 | − PalmData& palm = myAvatar->getHand()->getPalms()[i]; | |
| 323 | + auto palms = myAvatar->getHand()->getCopyOfPalms(); | |
| 324 | + for (const auto& palm : palms) { | |
| 325 | 325 | if (palm.isActive()) { |
| 326 | 326 | glm::vec2 polar = getPolarCoordinates(palm); |
| 327 | 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 | 450 | void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) { |
| 450 | 451 | MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar(); |
| 451 | 452 | |
| @@ -455,23 +456,24 @@ void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) { · 3 unmodified lines | ||
| 455 | 456 | static bool stateWhenPressed[NUMBER_OF_RETICLES] = { false, false, false }; |
| 456 | 457 | |
| 457 | 458 | const HandData* handData = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHandData(); |
| 459 | + auto palms = handData->getCopyOfPalms(); | |
| 458 | 460 | |
| 459 | 461 | for (unsigned int palmIndex = 2; palmIndex < 4; palmIndex++) { |
| 460 | 462 | const int index = palmIndex - 1; |
| 461 | 463 | |
| 462 | 464 | const PalmData* palmData = NULL; |
| 463 | 465 | |
| 464 | − if (palmIndex >= handData->getPalms().size()) { | |
| 466 | + if (palmIndex >= palms.size()) { | |
| 465 | 467 | return; |
| 466 | 468 | } |
| 467 | 469 | |
| 468 | − if (handData->getPalms()[palmIndex].isActive()) { | |
| 469 | − palmData = &handData->getPalms()[palmIndex]; | |
| 470 | + if (palms[palmIndex].isActive()) { | |
| 471 | + palmData = &palms[palmIndex]; | |
| 470 | 472 | } else { |
| 471 | 473 | continue; |
| 472 | 474 | } |
| 473 | 475 | |
| 474 | − int controllerButtons = palmData->getControllerButtons(); | |
| 476 | + int controllerButtons = 0; | |
| 475 | 477 | |
| 476 | 478 | //Check for if we should toggle or drag the magnification window |
| 477 | 479 | if (controllerButtons & BUTTON_3) { |
| @@ -521,7 +523,7 @@ void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) { · 43 unmodified lines | ||
| 521 | 523 | float yAngle = 0.5f - ((atan2f(direction.z, direction.y) + (float)PI_OVER_TWO)); |
| 522 | 524 | |
| 523 | 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 | 528 | mouseX = (canvasSize.x / 2.0f + cursorRange * xAngle); |
| 527 | 529 | mouseY = (canvasSize.y / 2.0f + cursorRange * yAngle); |
| @@ -198,7 +198,7 @@ void PreferencesDialog::loadPreferences() { · 197 unmodified lines | ||
| 198 | 198 | ui.oculusUIAngularSizeSpin->setValue(qApp->getApplicationCompositor().getHmdUIAngularSize()); |
| 199 | 199 | #endif |
| 200 | 200 | |
| 201 | − ui.sixenseReticleMoveSpeedSpin->setValue(InputDevice::getReticleMoveSpeed()); | |
| 201 | + ui.sixenseReticleMoveSpeedSpin->setValue(controller::InputDevice::getReticleMoveSpeed()); | |
| 202 | 202 | |
| 203 | 203 | // LOD items |
| 204 | 204 | auto lodManager = DependencyManager::get<LODManager>(); |
| @@ -273,7 +273,7 @@ void PreferencesDialog::savePreferences() { · 68 unmodified lines | ||
| 273 | 273 | |
| 274 | 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 | 278 | auto audio = DependencyManager::get<AudioClient>(); |
| 279 | 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 | 12 | #define hifi_AnimVariant_h |
| 13 | 13 | |
| 14 | 14 | #include <cassert> |
| 15 | +#include <functional> | |
| 15 | 16 | #include <glm/glm.hpp> |
| 16 | 17 | #include <glm/gtx/quaternion.hpp> |
| 17 | 18 | #include <map> |
| 18 | 19 | #include <set> |
| 20 | +#include <QScriptValue> | |
| 19 | 21 | #include "AnimationLogging.h" |
| 22 | +#include "StreamUtils.h" | |
| 20 | 23 | |
| 21 | 24 | class AnimVariant { |
| 22 | 25 | public: |
| @@ -58,8 +61,9 @@ public: · 35 unmodified lines | ||
| 58 | 61 | void setString(const QString& value) { assert(_type == Type::String); _stringVal = value; } |
| 59 | 62 | |
| 60 | 63 | bool getBool() const { assert(_type == Type::Bool); return _val.boolVal; } |
| 61 | − int getInt() const { assert(_type == Type::Int); return _val.intVal; } | |
| 62 | − float getFloat() const { assert(_type == Type::Float); return _val.floats[0]; } | |
| 64 | + int getInt() const { assert(_type == Type::Int || _type == Type::Float); return _type == Type::Float ? (int)_val.floats[0] : _val.intVal; } | |
| 65 | + float getFloat() const { assert(_type == Type::Float || _type == Type::Int); return _type == Type::Int ? (float)_val.intVal : _val.floats[0]; } | |
| 66 | + | |
| 63 | 67 | const glm::vec3& getVec3() const { assert(_type == Type::Vec3); return *reinterpret_cast<const glm::vec3*>(&_val); } |
| 64 | 68 | const glm::quat& getQuat() const { assert(_type == Type::Quat); return *reinterpret_cast<const glm::quat*>(&_val); } |
| 65 | 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 | 160 | void setTrigger(const QString& key) { _triggers.insert(key); } |
| 157 | 161 | void clearTriggers() { _triggers.clear(); } |
| 158 | 162 | |
| 163 | + void clearMap() { _map.clear(); } | |
| 159 | 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 | 172 | #ifdef NDEBUG |
| 162 | 173 | void dump() const { |
| 163 | 174 | qCDebug(animation) << "AnimVariantMap ="; |
| @@ -196,4 +207,8 @@ protected: · 32 unmodified lines | ||
| 196 | 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 | 214 | #endif // hifi_AnimVariant_h |
| @@ -13,6 +13,7 @@ · 12 unmodified lines | ||
| 13 | 13 | |
| 14 | 14 | #include <glm/gtx/vector_angle.hpp> |
| 15 | 15 | #include <queue> |
| 16 | +#include <QScriptValueIterator> | |
| 16 | 17 | |
| 17 | 18 | #include <NumericalConstants.h> |
| 18 | 19 | #include <DebugDraw.h> |
| @@ -576,6 +577,71 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos · 557 unmodified lines | ||
| 576 | 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 | 645 | void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { |
| 580 | 646 | |
| 581 | 647 | if (_enableAnimGraph) { |
| @@ -583,6 +649,7 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { · 1 unmodified line | ||
| 583 | 649 | return; |
| 584 | 650 | } |
| 585 | 651 | |
| 652 | + updateAnimationStateHandlers(); | |
| 586 | 653 | // evaluate the animation |
| 587 | 654 | AnimNode::Triggers triggersOut; |
| 588 | 655 | AnimPoseVec poses = _animNode->evaluate(_animVars, deltaTime, triggersOut); |
| @@ -37,6 +37,8 @@ · 36 unmodified lines | ||
| 37 | 37 | #define __hifi__Rig__ |
| 38 | 38 | |
| 39 | 39 | #include <QObject> |
| 40 | +#include <QMutex> | |
| 41 | +#include <QScriptValue> | |
| 40 | 42 | |
| 41 | 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 | 54 | class Rig : public QObject, public std::enable_shared_from_this<Rig> { |
| 53 | 55 | public: |
| 56 | + struct StateHandler { | |
| 57 | + AnimVariantMap results; | |
| 58 | + QStringList propertyNames; | |
| 59 | + QScriptValue function; | |
| 60 | + bool useNames; | |
| 61 | + }; | |
| 54 | 62 | |
| 55 | 63 | struct HeadParameters { |
| 56 | 64 | float leanSideways = 0.0f; // degrees |
| @@ -199,10 +207,14 @@ public: · 142 unmodified lines | ||
| 199 | 207 | AnimNode::ConstPointer getAnimNode() const { return _animNode; } |
| 200 | 208 | AnimSkeleton::ConstPointer getAnimSkeleton() const { return _animSkeleton; } |
| 201 | 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 | 214 | bool getModelOffset(glm::vec3& modelOffsetOut) const; |
| 204 | 215 | |
| 205 | 216 | protected: |
| 217 | + void updateAnimationStateHandlers(); | |
| 206 | 218 | |
| 207 | 219 | void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist); |
| 208 | 220 | void updateNeckJoint(int index, const HeadParameters& params); |
| @@ -241,6 +253,11 @@ public: · 32 unmodified lines | ||
| 241 | 253 | float _desiredStateAge = 0.0f; |
| 242 | 254 | float _leftHandOverlayAlpha = 0.0f; |
| 243 | 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 | 263 | #endif /* defined(__hifi__Rig__) */ |
| @@ -59,39 +59,30 @@ Sound::Sound(const QUrl& url, bool isStereo) : · 58 unmodified lines | ||
| 59 | 59 | void Sound::downloadFinished(const QByteArray& data) { |
| 60 | 60 | // replace our byte array with the downloaded data |
| 61 | 61 | QByteArray rawAudioByteArray = QByteArray(data); |
| 62 | − QString fileName = getURL().fileName(); | |
| 63 | − | |
| 64 | − const QString WAV_EXTENSION = ".wav"; | |
| 62 | + QString fileName = getURL().fileName().toLower(); | |
| 65 | 63 | |
| 64 | + static const QString WAV_EXTENSION = ".wav"; | |
| 65 | + static const QString RAW_EXTENSION = ".raw"; | |
| 66 | 66 | if (fileName.endsWith(WAV_EXTENSION)) { |
| 67 | 67 | |
| 68 | − QString headerContentType = "audio/x-wav"; | |
| 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; | |
| 68 | + QByteArray outputAudioByteArray; | |
| 78 | 69 | |
| 79 | − interpretAsWav(rawAudioByteArray, outputAudioByteArray); | |
| 80 | − downSample(outputAudioByteArray); | |
| 81 | − } else { | |
| 82 | − // check if this was a stereo raw file | |
| 83 | − // since it's raw the only way for us to know that is if the file was called .stereo.raw | |
| 84 | − if (fileName.toLower().endsWith("stereo.raw")) { | |
| 85 | − _isStereo = true; | |
| 86 | − qCDebug(audio) << "Processing sound of" << rawAudioByteArray.size() << "bytes from" << getURL() << "as stereo audio file."; | |
| 87 | − } | |
| 88 | − | |
| 89 | − // Process as RAW file | |
| 90 | − downSample(rawAudioByteArray); | |
| 70 | + interpretAsWav(rawAudioByteArray, outputAudioByteArray); | |
| 71 | + downSample(outputAudioByteArray); | |
| 72 | + trimFrames(); | |
| 73 | + } else if (fileName.endsWith(RAW_EXTENSION)) { | |
| 74 | + // check if this was a stereo raw file | |
| 75 | + // since it's raw the only way for us to know that is if the file was called .stereo.raw | |
| 76 | + if (fileName.toLower().endsWith("stereo.raw")) { | |
| 77 | + _isStereo = true; | |
| 78 | + qCDebug(audio) << "Processing sound of" << rawAudioByteArray.size() << "bytes from" << getURL() << "as stereo audio file."; | |
| 91 | 79 | } |
| 80 | + | |
| 81 | + // Process as RAW file | |
| 82 | + downSample(rawAudioByteArray); | |
| 92 | 83 | trimFrames(); |
| 93 | 84 | } else { |
| 94 | − qCDebug(audio) << "Network reply without 'Content-Type'."; | |
| 85 | + qCDebug(audio) << "Unknown sound file type"; | |
| 95 | 86 | } |
| 96 | 87 | |
| 97 | 88 | _isReady = true; |
| @@ -21,62 +21,43 @@ · 20 unmodified lines | ||
| 21 | 21 | HandData::HandData(AvatarData* owningAvatar) : |
| 22 | 22 | _owningAvatarData(owningAvatar) |
| 23 | 23 | { |
| 24 | − // Start with two palms | |
| 25 | − addNewPalm(); | |
| 26 | − addNewPalm(); | |
| 24 | + addNewPalm(LeftHand); | |
| 25 | + addNewPalm(RightHand); | |
| 27 | 26 | } |
| 28 | 27 | |
| 29 | 28 | glm::vec3 HandData::worldToLocalVector(const glm::vec3& worldVector) const { |
| 30 | 29 | return glm::inverse(getBaseOrientation()) * worldVector / getBaseScale(); |
| 31 | 30 | } |
| 32 | 31 | |
| 33 | −PalmData& HandData::addNewPalm() { | |
| 34 | − _palms.push_back(PalmData(this)); | |
| 32 | +PalmData& HandData::addNewPalm(Hand whichHand) { | |
| 33 | + QWriteLocker locker(&_palmsLock); | |
| 34 | + _palms.push_back(PalmData(this, whichHand)); | |
| 35 | 35 | return _palms.back(); |
| 36 | 36 | } |
| 37 | 37 | |
| 38 | −const PalmData* HandData::getPalm(int sixSenseID) const { | |
| 39 | − // the palms are not necessarily added in left-right order, | |
| 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 | −} | |
| 38 | +PalmData HandData::getCopyOfPalmData(Hand hand) const { | |
| 39 | + QReadLocker locker(&_palmsLock); | |
| 49 | 40 | |
| 50 | −void HandData::getLeftRightPalmIndices(int& leftPalmIndex, int& rightPalmIndex) const { | |
| 51 | − leftPalmIndex = -1; | |
| 52 | − rightPalmIndex = -1; | |
| 53 | − for (size_t i = 0; i < _palms.size(); i++) { | |
| 54 | − const PalmData& palm = _palms[i]; | |
| 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 | − } | |
| 41 | + // the palms are not necessarily added in left-right order, | |
| 42 | + // so we have to search for the correct hand | |
| 43 | + for (const auto& palm : _palms) { | |
| 44 | + if (palm.whichHand() == hand && palm.isActive()) { | |
| 45 | + return palm; | |
| 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 | 52 | _rawRotation(0.0f, 0.0f, 0.0f, 1.0f), |
| 68 | 53 | _rawPosition(0.0f), |
| 69 | 54 | _rawVelocity(0.0f), |
| 70 | 55 | _rawAngularVelocity(0.0f), |
| 71 | 56 | _totalPenetration(0.0f), |
| 72 | −_controllerButtons(0), | |
| 73 | 57 | _isActive(false), |
| 74 | −_sixenseID(SIXENSEID_INVALID), | |
| 75 | 58 | _numFramesWithoutData(0), |
| 76 | 59 | _owningHandData(owningHandData), |
| 77 | −_isCollidingWithVoxel(false), | |
| 78 | −_isCollidingWithPalm(false), | |
| 79 | −_collisionlessPaddleExpiry(0) { | |
| 60 | +_hand(hand) { | |
| 80 | 61 | } |
| 81 | 62 | |
| 82 | 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 | 67 | bool HandData::findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration, |
| 87 | 68 | const PalmData*& collidingPalm) const { |
| 88 | − | |
| 89 | − for (size_t i = 0; i < _palms.size(); ++i) { | |
| 90 | − const PalmData& palm = _palms[i]; | |
| 69 | + QReadLocker locker(&_palmsLock); | |
| 70 | + | |
| 71 | + for (const auto& palm : _palms) { | |
| 91 | 72 | if (!palm.isActive()) { |
| 92 | 73 | continue; |
| 93 | 74 | } |
| @@ -12,26 +12,30 @@ · 11 unmodified lines | ||
| 12 | 12 | #ifndef hifi_HandData_h |
| 13 | 13 | #define hifi_HandData_h |
| 14 | 14 | |
| 15 | +#include <functional> | |
| 15 | 16 | #include <iostream> |
| 16 | 17 | #include <vector> |
| 17 | 18 | |
| 18 | 19 | #include <glm/glm.hpp> |
| 19 | 20 | #include <glm/gtc/quaternion.hpp> |
| 20 | 21 | |
| 22 | +#include <QReadWriteLock> | |
| 23 | + | |
| 21 | 24 | #include <NumericalConstants.h> |
| 22 | 25 | #include <SharedUtil.h> |
| 23 | 26 | |
| 24 | 27 | class AvatarData; |
| 25 | 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 | 30 | class HandData { |
| 34 | 31 | public: |
| 32 | + enum Hand { | |
| 33 | + LeftHand, | |
| 34 | + RightHand, | |
| 35 | + UnknownHand, | |
| 36 | + NUMBER_OF_HANDS | |
| 37 | + }; | |
| 38 | + | |
| 35 | 39 | HandData(AvatarData* owningAvatar); |
| 36 | 40 | virtual ~HandData() {} |
| 37 | 41 | |
| @@ -46,15 +50,9 @@ public: · 8 unmodified lines | ||
| 46 | 50 | |
| 47 | 51 | glm::vec3 worldToLocalVector(const glm::vec3& worldVector) const; |
| 48 | 52 | |
| 49 | − std::vector<PalmData>& getPalms() { return _palms; } | |
| 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(); | |
| 53 | + PalmData getCopyOfPalmData(Hand hand) const; | |
| 54 | 54 | |
| 55 | − /// Finds the indices of the left and right palms according to their locations, or -1 if either or | |
| 56 | − /// both is not found. | |
| 57 | − void getLeftRightPalmIndices(int& leftPalmIndex, int& rightPalmIndex) const; | |
| 55 | + std::vector<PalmData> getCopyOfPalms() const { QReadLocker locker(&_palmsLock); return _palms; } | |
| 58 | 56 | |
| 59 | 57 | /// Checks for penetration between the described sphere and the hand. |
| 60 | 58 | /// \param penetratorCenter the center of the penetration test sphere |
| @@ -67,14 +65,22 @@ public: · 6 unmodified lines | ||
| 67 | 65 | |
| 68 | 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 | 72 | friend class AvatarData; |
| 71 | 73 | protected: |
| 72 | 74 | AvatarData* _owningAvatarData; |
| 73 | 75 | std::vector<PalmData> _palms; |
| 76 | + mutable QReadWriteLock _palmsLock{ QReadWriteLock::Recursive }; | |
| 74 | 77 | |
| 75 | 78 | glm::vec3 getBasePosition() const; |
| 76 | 79 | float getBaseScale() const; |
| 77 | − | |
| 80 | + | |
| 81 | + PalmData& addNewPalm(Hand whichHand); | |
| 82 | + PalmData& getPalmData(Hand hand); | |
| 83 | + | |
| 78 | 84 | private: |
| 79 | 85 | // privatize copy ctor and assignment operator so copies of this object cannot be made |
| 80 | 86 | HandData(const HandData&); |
| @@ -84,25 +90,30 @@ private: · 3 unmodified lines | ||
| 84 | 90 | |
| 85 | 91 | class PalmData { |
| 86 | 92 | public: |
| 87 | − PalmData(HandData* owningHandData); | |
| 93 | + PalmData(HandData* owningHandData = nullptr, HandData::Hand hand = HandData::UnknownHand); | |
| 88 | 94 | glm::vec3 getPosition() const { return _owningHandData->localToWorldPosition(_rawPosition); } |
| 89 | 95 | glm::vec3 getVelocity() const { return _owningHandData->localToWorldDirection(_rawVelocity); } |
| 90 | 96 | |
| 91 | 97 | const glm::vec3& getRawPosition() const { return _rawPosition; } |
| 92 | 98 | bool isActive() const { return _isActive; } |
| 93 | − int getSixenseID() const { return _sixenseID; } | |
| 99 | + bool isValid() const { return _owningHandData; } | |
| 94 | 100 | |
| 95 | 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 | 107 | glm::quat getRawRotation() const { return _rawRotation; } |
| 100 | 108 | glm::quat getRotation() const { return _owningHandData->getBaseOrientation() * _rawRotation; } |
| 101 | 109 | void setRawPosition(const glm::vec3& pos) { _rawPosition = pos; } |
| 102 | 110 | void setRawVelocity(const glm::vec3& velocity) { _rawVelocity = velocity; } |
| 103 | 111 | const glm::vec3& getRawVelocity() const { return _rawVelocity; } |
| 112 | + | |
| 104 | 113 | void setRawAngularVelocity(const glm::vec3& angularVelocity) { _rawAngularVelocity = angularVelocity; } |
| 105 | 114 | const glm::vec3& getRawAngularVelocity() const { return _rawAngularVelocity; } |
| 115 | + glm::quat getRawAngularVelocityAsQuat() const { return glm::quat(_rawAngularVelocity); } | |
| 116 | + | |
| 106 | 117 | void addToPosition(const glm::vec3& delta); |
| 107 | 118 | |
| 108 | 119 | void addToPenetration(const glm::vec3& penetration) { _totalPenetration += penetration; } |
| @@ -120,31 +131,11 @@ public: · 11 unmodified lines | ||
| 120 | 131 | void resetFramesWithoutData() { _numFramesWithoutData = 0; } |
| 121 | 132 | int getFramesWithoutData() const { return _numFramesWithoutData; } |
| 122 | 133 | |
| 123 | − // Controller buttons | |
| 124 | − void setControllerButtons(unsigned int controllerButtons) { _controllerButtons = controllerButtons; } | |
| 125 | − void setLastControllerButtons(unsigned int controllerButtons) { _lastControllerButtons = controllerButtons; } | |
| 126 | − | |
| 127 | − unsigned int getControllerButtons() const { return _controllerButtons; } | |
| 128 | − unsigned int getLastControllerButtons() const { return _lastControllerButtons; } | |
| 129 | − | |
| 134 | + // FIXME - these are used in SkeletonModel::updateRig() the skeleton/rig should probably get this information | |
| 135 | + // from an action and/or the UserInputMapper instead of piping it through here. | |
| 130 | 136 | void setTrigger(float trigger) { _trigger = trigger; } |
| 131 | 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 | 139 | // return world-frame: |
| 149 | 140 | glm::vec3 getFingerDirection() const; |
| 150 | 141 | glm::vec3 getNormal() const; |
| @@ -155,25 +146,29 @@ private: · 4 unmodified lines | ||
| 155 | 146 | glm::vec3 _rawPosition; |
| 156 | 147 | glm::vec3 _rawVelocity; |
| 157 | 148 | glm::vec3 _rawAngularVelocity; |
| 149 | + glm::quat _rawDeltaRotation; | |
| 158 | 150 | glm::quat _lastRotation; |
| 159 | 151 | |
| 160 | 152 | glm::vec3 _tipPosition; |
| 161 | 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 | 156 | float _trigger; |
| 167 | − float _joystickX, _joystickY; | |
| 168 | 157 | |
| 169 | − bool _isActive; // This has current valid data | |
| 170 | − int _sixenseID; // Sixense controller ID for this palm | |
| 171 | − int _numFramesWithoutData; // after too many frames without data, this tracked object assumed lost. | |
| 158 | + bool _isActive; /// This has current valid data | |
| 159 | + int _numFramesWithoutData; /// after too many frames without data, this tracked object assumed lost. | |
| 172 | 160 | HandData* _owningHandData; |
| 173 | − | |
| 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 | |
| 161 | + HandData::Hand _hand; | |
| 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 | 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 | 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 | 25 | // Event types for each controller |
| 16 | 26 | const unsigned int CONTROLLER_0_EVENT = 1500U; |
| 17 | 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 | 34 | // NOTE: If something inherits from both InputDevice and InputPlugin, InputPlugin must go first. |
| 20 | 35 | // e.g. class Example : public InputPlugin, public InputDevice |
| 21 | 36 | // instead of class Example : public InputDevice, public InputPlugin |
| @@ -23,17 +38,22 @@ class InputDevice { · 1 unmodified line | ||
| 23 | 38 | public: |
| 24 | 39 | InputDevice(const QString& name) : _name(name) {} |
| 25 | 40 | |
| 41 | + using Pointer = std::shared_ptr<InputDevice>; | |
| 42 | + | |
| 26 | 43 | typedef std::unordered_set<int> ButtonPressedMap; |
| 27 | 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 | 47 | // Get current state for each channel |
| 31 | 48 | float getButton(int channel) const; |
| 32 | 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; | |
| 36 | − virtual void assignDefaultInputMapping(UserInputMapper& mapper) = 0; | |
| 52 | + float getValue(const Input& input) const; | |
| 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 | 58 | // Update call MUST be called once per simulation loop |
| 39 | 59 | // It takes care of updating the action states and deltas |
| @@ -42,20 +62,33 @@ public: · 2 unmodified lines | ||
| 42 | 62 | virtual void focusOutEvent() = 0; |
| 43 | 63 | |
| 44 | 64 | int getDeviceID() { return _deviceID; } |
| 65 | + void setDeviceID(int deviceID) { _deviceID = deviceID; } | |
| 45 | 66 | |
| 46 | 67 | static float getCursorPixelRangeMult(); |
| 47 | − static float getReticleMoveSpeed() { return reticleMoveSpeed; } | |
| 48 | − static void setReticleMoveSpeed(float sixenseReticleMoveSpeed) { reticleMoveSpeed = sixenseReticleMoveSpeed; } | |
| 68 | + static float getReticleMoveSpeed() { return _reticleMoveSpeed; } | |
| 69 | + static void setReticleMoveSpeed(float reticleMoveSpeed) { _reticleMoveSpeed = reticleMoveSpeed; } | |
| 49 | 70 | |
| 50 | 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 | 79 | public slots: |
| 53 | 80 | static void setLowVelocityFilter(bool newLowVelocityFilter) { _lowVelocityFilter = newLowVelocityFilter; }; |
| 54 | 81 | |
| 55 | 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 | 93 | ButtonPressedMap _buttonPressedMap; |
| 61 | 94 | AxisStateMap _axisStateMap; |
| @@ -64,5 +97,7 @@ protected: · 2 unmodified lines | ||
| 64 | 97 | static bool _lowVelocityFilter; |
| 65 | 98 | |
| 66 | 99 | private: |
| 67 | − static float reticleMoveSpeed; | |
| 68 | −}; | |
| 100 | + static float _reticleMoveSpeed; | |
| 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 | 27 | void NullDisplayPlugin::preDisplay() {} |
| 28 | 28 | void NullDisplayPlugin::display(GLuint sceneTexture, const glm::uvec2& sceneSize) {} |
| 29 | 29 | void NullDisplayPlugin::finishFrame() {} |
| 30 | − | |
| 31 | −void NullDisplayPlugin::activate() {} | |
| 32 | −void NullDisplayPlugin::deactivate() {} | |
| 33 | 30 | void NullDisplayPlugin::stop() {} |
| @@ -15,8 +15,6 @@ public: · 14 unmodified lines | ||
| 15 | 15 | virtual ~NullDisplayPlugin() final {} |
| 16 | 16 | virtual const QString & getName() const override; |
| 17 | 17 | |
| 18 | − void activate() override; | |
| 19 | − void deactivate() override; | |
| 20 | 18 | void stop() override; |
| 21 | 19 | |
| 22 | 20 | virtual glm::uvec2 getRecommendedRenderSize() const override; |
| @@ -57,12 +57,12 @@ void OpenGLDisplayPlugin::customizeContext() { · 56 unmodified lines | ||
| 57 | 57 | } |
| 58 | 58 | |
| 59 | 59 | void OpenGLDisplayPlugin::activate() { |
| 60 | − _active = true; | |
| 60 | + DisplayPlugin::activate(); | |
| 61 | 61 | _timer.start(1); |
| 62 | 62 | } |
| 63 | 63 | |
| 64 | 64 | void OpenGLDisplayPlugin::stop() { |
| 65 | − _active = false; | |
| 65 | + DisplayPlugin::activate(); | |
| 66 | 66 | _timer.stop(); |
| 67 | 67 | } |
| 68 | 68 | |
| @@ -44,7 +44,6 @@ protected: · 43 unmodified lines | ||
| 44 | 44 | mutable QTimer _timer; |
| 45 | 45 | ProgramPtr _program; |
| 46 | 46 | ShapeWrapperPtr _plane; |
| 47 | − bool _active{ false }; | |
| 48 | 47 | bool _vsyncSupported{ false }; |
| 49 | 48 | }; |
| 50 | 49 | |
| @@ -107,8 +107,7 @@ void EntityTreeRenderer::init() { · 106 unmodified lines | ||
| 107 | 107 | entityTree->setFBXService(this); |
| 108 | 108 | |
| 109 | 109 | if (_wantScripts) { |
| 110 | − _entitiesScriptEngine = new ScriptEngine(NO_SCRIPT, "Entities", | |
| 111 | − _scriptingServices->getControllerScriptingInterface()); | |
| 110 | + _entitiesScriptEngine = new ScriptEngine(NO_SCRIPT, "Entities"); | |
| 112 | 111 | _scriptingServices->registerScriptEngineWithApplicationServices(_entitiesScriptEngine); |
| 113 | 112 | _entitiesScriptEngine->runInThread(); |
| 114 | 113 | DependencyManager::get<EntityScriptingInterface>()->setEntitiesScriptEngine(_entitiesScriptEngine); |
| @@ -189,7 +189,7 @@ enum EntityPropertyList { · 188 unmodified lines | ||
| 189 | 189 | PROP_BACKGROUND_MODE = PROP_MODEL_URL, |
| 190 | 190 | PROP_SKYBOX_COLOR = PROP_ANIMATION_URL, |
| 191 | 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 | 194 | // Aliases/Piggyback properties for Web. These properties intentionally reuse the enum values for |
| 195 | 195 | // other properties which will never overlap with each other. |
| @@ -44,8 +44,7 @@ bool GlWindow::makeCurrent() { · 43 unmodified lines | ||
| 44 | 44 | qDebug() << "GL Renderer: " << QString((const char*) glGetString(GL_RENDERER)); |
| 45 | 45 | }); |
| 46 | 46 | |
| 47 | − QOpenGLContext * currentContext = QOpenGLContext::currentContext(); | |
| 48 | − Q_ASSERT(_context == currentContext); | |
| 47 | + Q_ASSERT(_context == QOpenGLContext::currentContext()); | |
| 49 | 48 | |
| 50 | 49 | return makeCurrentResult; |
| 51 | 50 | } |
| @@ -19,6 +19,7 @@ · 18 unmodified lines | ||
| 19 | 19 | #include <QMutex> |
| 20 | 20 | |
| 21 | 21 | #include <PerfStat.h> |
| 22 | +#include <DependencyManager.h> | |
| 22 | 23 | #include <NumericalConstants.h> |
| 23 | 24 | |
| 24 | 25 | #include "GLEscrow.h" |
| @@ -611,3 +612,8 @@ QQuickWindow* OffscreenQmlSurface::getWindow() { · 586 unmodified lines | ||
| 611 | 612 | QSize OffscreenQmlSurface::size() const { |
| 612 | 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 | 60 | QQuickItem* getRootItem(); |
| 61 | 61 | QQuickWindow* getWindow(); |
| 62 | 62 | QObject* getEventHandler(); |
| 63 | + QQmlContext* getRootContext(); | |
| 63 | 64 | |
| 64 | 65 | QPointF mapToVirtualScreen(const QPointF& originalPoint, QObject* originalWidget); |
| 65 | 66 | virtual bool eventFilter(QObject* originalDestination, QEvent* event); |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | set(TARGET_NAME input-plugins) |
| 2 | 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 | 5 | GroupSources("src/input-plugins") |
| 6 | 6 | |
| @@ -11,55 +11,5 @@ if (WIN32) · 4 unmodified lines | ||
| 11 | 11 | target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES}) |
| 12 | 12 | endif() |
| 13 | 13 | |
| 14 | −#add_dependency_external_projects(Sixense) | |
| 15 | −#find_package(Sixense REQUIRED) | |
| 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() | |
| 14 | +target_sdl2() | |
| 15 | +target_sixense() | |
| @@ -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 | 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 | 12 | #include "Joystick.h" |
| 17 | 13 | |
| 14 | +#include <PathUtils.h> | |
| 15 | + | |
| 18 | 16 | const float CONTROLLER_THRESHOLD = 0.3f; |
| 19 | 17 | |
| 20 | 18 | #ifdef HAVE_SDL2 |
| 21 | 19 | const float MAX_AXIS = 32768.0f; |
| 22 | 20 | |
| 23 | −Joystick::Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController) : | |
| 24 | − InputDevice(name), | |
| 21 | +Joystick::Joystick(SDL_JoystickID instanceId, SDL_GameController* sdlGameController) : | |
| 22 | + InputDevice("GamePad"), | |
| 25 | 23 | _sdlGameController(sdlGameController), |
| 26 | 24 | _sdlJoystick(SDL_GameControllerGetJoystick(_sdlGameController)), |
| 27 | 25 | _instanceId(instanceId) |
| @@ -55,39 +53,14 @@ void Joystick::focusOutEvent() { · 27 unmodified lines | ||
| 55 | 53 | }; |
| 56 | 54 | |
| 57 | 55 | #ifdef HAVE_SDL2 |
| 56 | + | |
| 58 | 57 | void Joystick::handleAxisEvent(const SDL_ControllerAxisEvent& event) { |
| 59 | 58 | SDL_GameControllerAxis axis = (SDL_GameControllerAxis) event.axis; |
| 60 | − | |
| 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 | − } | |
| 59 | + _axisStateMap[makeInput((controller::StandardAxisChannel)axis).getChannel()] = (float)event.value / MAX_AXIS; | |
| 87 | 60 | } |
| 88 | 61 | |
| 89 | 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 | 64 | bool newValue = event.state == SDL_PRESSED; |
| 92 | 65 | if (newValue) { |
| 93 | 66 | _buttonPressedMap.insert(input.getChannel()); |
| @@ -95,131 +68,61 @@ void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) { · 1 unmodified line | ||
| 95 | 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 | 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) { | |
| 147 | −#ifdef HAVE_SDL2 | |
| 148 | − const float JOYSTICK_MOVE_SPEED = 1.0f; | |
| 149 | − const float DPAD_MOVE_SPEED = 0.5f; | |
| 150 | − const float JOYSTICK_YAW_SPEED = 0.5f; | |
| 151 | − const float JOYSTICK_PITCH_SPEED = 0.25f; | |
| 152 | − const float BOOM_SPEED = 0.1f; | |
| 153 | − | |
| 154 | − // Y axes are flipped (up is negative) | |
| 155 | − // Left Joystick: Movement, strafing | |
| 156 | − mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(LEFT_AXIS_Y_NEG), JOYSTICK_MOVE_SPEED); | |
| 157 | − mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(LEFT_AXIS_Y_POS), JOYSTICK_MOVE_SPEED); | |
| 158 | − mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(LEFT_AXIS_X_POS), JOYSTICK_MOVE_SPEED); | |
| 159 | − mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(LEFT_AXIS_X_NEG), JOYSTICK_MOVE_SPEED); | |
| 160 | − | |
| 161 | − // Right Joystick: Camera orientation | |
| 162 | − mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(RIGHT_AXIS_X_POS), JOYSTICK_YAW_SPEED); | |
| 163 | − mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(RIGHT_AXIS_X_NEG), JOYSTICK_YAW_SPEED); | |
| 164 | − mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(RIGHT_AXIS_Y_NEG), JOYSTICK_PITCH_SPEED); | |
| 165 | − mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(RIGHT_AXIS_Y_POS), JOYSTICK_PITCH_SPEED); | |
| 166 | − | |
| 167 | − // Dpad movement | |
| 168 | − mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_UP), DPAD_MOVE_SPEED); | |
| 169 | − mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_DOWN), DPAD_MOVE_SPEED); | |
| 170 | − mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_RIGHT), DPAD_MOVE_SPEED); | |
| 171 | − mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_LEFT), DPAD_MOVE_SPEED); | |
| 172 | − | |
| 173 | − // Button controls | |
| 174 | − mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(SDL_CONTROLLER_BUTTON_Y), DPAD_MOVE_SPEED); | |
| 175 | − mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(SDL_CONTROLLER_BUTTON_X), DPAD_MOVE_SPEED); | |
| 176 | − | |
| 177 | − // Zoom | |
| 178 | − mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(RIGHT_SHOULDER), BOOM_SPEED); | |
| 179 | − mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(LEFT_SHOULDER), BOOM_SPEED); | |
| 180 | − | |
| 181 | − | |
| 182 | − // Hold front right shoulder button for precision controls | |
| 183 | − // Left Joystick: Movement, strafing | |
| 184 | − mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(LEFT_AXIS_Y_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f); | |
| 185 | − mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(LEFT_AXIS_Y_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f); | |
| 186 | − mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(LEFT_AXIS_X_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f); | |
| 187 | − mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(LEFT_AXIS_X_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f); | |
| 188 | − | |
| 189 | − // Right Joystick: Camera orientation | |
| 190 | − mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(RIGHT_AXIS_X_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f); | |
| 191 | − mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(RIGHT_AXIS_X_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f); | |
| 192 | − mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(RIGHT_AXIS_Y_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_PITCH_SPEED/2.0f); | |
| 193 | − mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(RIGHT_AXIS_Y_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_PITCH_SPEED/2.0f); | |
| 194 | − | |
| 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); | |
| 74 | +controller::Input::NamedVector Joystick::getAvailableInputs() const { | |
| 75 | + using namespace controller; | |
| 76 | + static const Input::NamedVector availableInputs{ | |
| 77 | + makePair(A, "A"), | |
| 78 | + makePair(B, "B"), | |
| 79 | + makePair(X, "X"), | |
| 80 | + makePair(Y, "Y"), | |
| 81 | + // DPad | |
| 82 | + makePair(DU, "DU"), | |
| 83 | + makePair(DD, "DD"), | |
| 84 | + makePair(DL, "DL"), | |
| 85 | + makePair(DR, "DR"), | |
| 86 | + // Bumpers | |
| 87 | + makePair(LB, "LB"), | |
| 88 | + makePair(RB, "RB"), | |
| 89 | + // Stick press | |
| 90 | + makePair(LS, "LS"), | |
| 91 | + makePair(RS, "RS"), | |
| 92 | + // Center buttons | |
| 93 | + makePair(START, "Start"), | |
| 94 | + makePair(BACK, "Back"), | |
| 95 | + // Analog sticks | |
| 96 | + makePair(LX, "LX"), | |
| 97 | + makePair(LY, "LY"), | |
| 98 | + makePair(RX, "RX"), | |
| 99 | + makePair(RY, "RY"), | |
| 100 | + | |
| 101 | + // Triggers | |
| 102 | + makePair(LT, "LT"), | |
| 103 | + makePair(RT, "RT"), | |
| 104 | + | |
| 105 | + // Aliases, PlayStation style names | |
| 106 | + makePair(LB, "L1"), | |
| 107 | + makePair(RB, "R1"), | |
| 108 | + makePair(LT, "L2"), | |
| 109 | + makePair(RT, "R2"), | |
| 110 | + makePair(LS, "L3"), | |
| 111 | + makePair(RS, "R3"), | |
| 112 | + makePair(BACK, "Select"), | |
| 113 | + makePair(A, "Cross"), | |
| 114 | + makePair(B, "Circle"), | |
| 115 | + makePair(X, "Square"), | |
| 116 | + makePair(Y, "Triangle"), | |
| 117 | + makePair(DU, "Up"), | |
| 118 | + makePair(DD, "Down"), | |
| 119 | + makePair(DL, "Left"), | |
| 120 | + makePair(DR, "Right"), | |
| 121 | + }; | |
| 122 | + return availableInputs; | |
| 219 | 123 | } |
| 220 | −#endif | |
| 221 | 124 | |
| 222 | −UserInputMapper::Input Joystick::makeInput(Joystick::JoystickAxisChannel axis) { | |
| 223 | − return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS); | |
| 125 | +QString Joystick::getDefaultMappingConfig() const { | |
| 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 | 20 | #undef main |
| 21 | 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 | 27 | Q_OBJECT |
| 27 | 28 | Q_PROPERTY(QString name READ getName) |
| 28 | 29 | |
| @@ -31,37 +32,21 @@ class Joystick : public QObject, public InputDevice { · 2 unmodified lines | ||
| 31 | 32 | #endif |
| 32 | 33 | |
| 33 | 34 | public: |
| 34 | − enum JoystickAxisChannel { | |
| 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 | − }; | |
| 35 | + using Pointer = std::shared_ptr<Joystick>; | |
| 46 | 36 | |
| 47 | 37 | const QString& getName() const { return _name; } |
| 48 | 38 | |
| 49 | 39 | // Device functions |
| 50 | − virtual void registerToUserInputMapper(UserInputMapper& mapper) override; | |
| 51 | − virtual void assignDefaultInputMapping(UserInputMapper& mapper) override; | |
| 40 | + virtual controller::Input::NamedVector getAvailableInputs() const override; | |
| 41 | + virtual QString getDefaultMappingConfig() const override; | |
| 52 | 42 | virtual void update(float deltaTime, bool jointsCaptured) override; |
| 53 | 43 | virtual void focusOutEvent() override; |
| 54 | 44 | |
| 55 | − Joystick() : InputDevice("Joystick") {} | |
| 45 | + Joystick() : InputDevice("GamePad") {} | |
| 56 | 46 | ~Joystick(); |
| 57 | 47 | |
| 58 | 48 | #ifdef HAVE_SDL2 |
| 59 | − UserInputMapper::Input makeInput(SDL_GameControllerButton button); | |
| 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); | |
| 49 | + Joystick(SDL_JoystickID instanceId, SDL_GameController* sdlGameController); | |
| 65 | 50 | #endif |
| 66 | 51 | |
| 67 | 52 | void closeJoystick(); |
| @@ -10,6 +10,13 @@ · 9 unmodified lines | ||
| 10 | 10 | // |
| 11 | 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 | 20 | const QString KeyboardMouseDevice::NAME = "Keyboard/Mouse"; |
| 14 | 21 | |
| 15 | 22 | void KeyboardMouseDevice::update(float deltaTime, bool jointsCaptured) { |
| @@ -81,7 +88,7 @@ void KeyboardMouseDevice::wheelEvent(QWheelEvent* event) { · 65 unmodified lines | ||
| 81 | 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 | 92 | glm::vec2 averagePoint(0.0f); |
| 86 | 93 | if (points.count() > 0) { |
| 87 | 94 | for (auto& point : points) { |
| @@ -123,163 +130,151 @@ void KeyboardMouseDevice::touchUpdateEvent(const QTouchEvent* event) { · 35 unmodified lines | ||
| 123 | 130 | _lastTouch = currentPos; |
| 124 | 131 | } |
| 125 | 132 | |
| 126 | −UserInputMapper::Input KeyboardMouseDevice::makeInput(Qt::Key code) { | |
| 127 | − auto shortCode = (UserInputMapper::uint16)(code & KEYBOARD_MASK); | |
| 133 | +controller::Input KeyboardMouseDevice::makeInput(Qt::Key code) const { | |
| 134 | + auto shortCode = (uint16_t)(code & KEYBOARD_MASK); | |
| 128 | 135 | if (shortCode != code) { |
| 129 | 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 | 142 | switch (code) { |
| 136 | 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 | 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 | 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 | 149 | default: |
| 143 | − return UserInputMapper::Input(); | |
| 150 | + return controller::Input(); | |
| 144 | 151 | }; |
| 145 | 152 | } |
| 146 | 153 | |
| 147 | −UserInputMapper::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::MouseAxisChannel axis) { | |
| 148 | − return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS); | |
| 154 | +controller::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::MouseAxisChannel axis) const { | |
| 155 | + return controller::Input(_deviceID, axis, controller::ChannelType::AXIS); | |
| 149 | 156 | } |
| 150 | 157 | |
| 151 | −UserInputMapper::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchAxisChannel axis) { | |
| 152 | − return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS); | |
| 158 | +controller::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchAxisChannel axis) const { | |
| 159 | + return controller::Input(_deviceID, axis, controller::ChannelType::AXIS); | |
| 153 | 160 | } |
| 154 | 161 | |
| 155 | −UserInputMapper::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchButtonChannel button) { | |
| 156 | − return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON); | |
| 162 | +controller::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchButtonChannel button) const { | |
| 163 | + return controller::Input(_deviceID, button, controller::ChannelType::BUTTON); | |
| 157 | 164 | } |
| 158 | 165 | |
| 159 | −void KeyboardMouseDevice::registerToUserInputMapper(UserInputMapper& mapper) { | |
| 160 | − // Grab the current free device ID | |
| 161 | − _deviceID = mapper.getFreeDeviceID(); | |
| 162 | − | |
| 163 | − auto proxy = std::make_shared<UserInputMapper::DeviceProxy>(_name); | |
| 164 | − proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; | |
| 165 | − proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; | |
| 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())); | |
| 166 | +controller::Input::NamedVector KeyboardMouseDevice::getAvailableInputs() const { | |
| 167 | + using namespace controller; | |
| 168 | + static QVector<Input::NamedPair> availableInputs; | |
| 169 | + static std::once_flag once; | |
| 170 | + std::call_once(once, [&] { | |
| 171 | + for (int i = (int)Qt::Key_0; i <= (int)Qt::Key_9; i++) { | |
| 172 | + availableInputs.append(Input::NamedPair(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++) { | |
| 172 | − availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString())); | |
| 174 | + for (int i = (int)Qt::Key_A; i <= (int)Qt::Key_Z; i++) { | |
| 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++) { | |
| 175 | − availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString())); | |
| 177 | + for (int i = (int)Qt::Key_Left; i <= (int)Qt::Key_Down; i++) { | |
| 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())); | |
| 178 | − availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_Shift), "Shift")); | |
| 179 | − availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_PageUp), QKeySequence(Qt::Key_PageUp).toString())); | |
| 180 | − availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_PageDown), QKeySequence(Qt::Key_PageDown).toString())); | |
| 181 | − | |
| 182 | − availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::LeftButton), "Left Mouse Click")); | |
| 183 | − availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::MiddleButton), "Middle Mouse Click")); | |
| 184 | − availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::RightButton), "Right Mouse Click")); | |
| 185 | − | |
| 186 | − availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_X_POS), "Mouse Move Right")); | |
| 187 | − availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_X_NEG), "Mouse Move Left")); | |
| 188 | − availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_Y_POS), "Mouse Move Up")); | |
| 189 | − availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_Y_NEG), "Mouse Move Down")); | |
| 190 | − | |
| 191 | − availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_WHEEL_Y_POS), "Mouse Wheel Right")); | |
| 192 | − availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_WHEEL_Y_NEG), "Mouse Wheel Left")); | |
| 193 | − availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_WHEEL_X_POS), "Mouse Wheel Up")); | |
| 194 | − availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_WHEEL_X_NEG), "Mouse Wheel Down")); | |
| 195 | − | |
| 196 | − availableInputs.append(UserInputMapper::InputPair(makeInput(TOUCH_AXIS_X_POS), "Touchpad Right")); | |
| 197 | − availableInputs.append(UserInputMapper::InputPair(makeInput(TOUCH_AXIS_X_NEG), "Touchpad Left")); | |
| 198 | − availableInputs.append(UserInputMapper::InputPair(makeInput(TOUCH_AXIS_Y_POS), "Touchpad Up")); | |
| 199 | − availableInputs.append(UserInputMapper::InputPair(makeInput(TOUCH_AXIS_Y_NEG), "Touchpad Down")); | |
| 200 | − | |
| 201 | − 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); | |
| 180 | + availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Space), QKeySequence(Qt::Key_Space).toString())); | |
| 181 | + availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Shift), "Shift")); | |
| 182 | + availableInputs.append(Input::NamedPair(makeInput(Qt::Key_PageUp), QKeySequence(Qt::Key_PageUp).toString())); | |
| 183 | + availableInputs.append(Input::NamedPair(makeInput(Qt::Key_PageDown), QKeySequence(Qt::Key_PageDown).toString())); | |
| 184 | + | |
| 185 | + availableInputs.append(Input::NamedPair(makeInput(Qt::LeftButton), "LeftMouseClick")); | |
| 186 | + availableInputs.append(Input::NamedPair(makeInput(Qt::MiddleButton), "MiddleMouseClick")); | |
| 187 | + availableInputs.append(Input::NamedPair(makeInput(Qt::RightButton), "RightMouseClick")); | |
| 188 | + | |
| 189 | + availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_X_POS), "MouseMoveRight")); | |
| 190 | + availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_X_NEG), "MouseMoveLeft")); | |
| 191 | + availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_Y_POS), "MouseMoveUp")); | |
| 192 | + availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_Y_NEG), "MouseMoveDown")); | |
| 193 | + | |
| 194 | + availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_Y_POS), "MouseWheelRight")); | |
| 195 | + availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_Y_NEG), "MouseWheelLeft")); | |
| 196 | + availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_X_POS), "MouseWheelUp")); | |
| 197 | + availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_X_NEG), "MouseWheelDown")); | |
| 198 | + | |
| 199 | + availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_X_POS), "TouchpadRight")); | |
| 200 | + availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_X_NEG), "TouchpadLeft")); | |
| 201 | + availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_Y_POS), "TouchpadUp")); | |
| 202 | + availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_Y_NEG), "TouchpadDown")); | |
| 203 | + }); | |
| 204 | + return availableInputs; | |
| 209 | 205 | } |
| 210 | 206 | |
| 211 | −void KeyboardMouseDevice::assignDefaultInputMapping(UserInputMapper& mapper) { | |
| 212 | − const float BUTTON_MOVE_SPEED = 1.0f; | |
| 213 | − const float BUTTON_YAW_SPEED = 0.75f; | |
| 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)); | |
| 207 | +QString KeyboardMouseDevice::getDefaultMappingConfig() const { | |
| 208 | + static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/keyboardMouse.json"; | |
| 209 | + return MAPPING_JSON; | |
| 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 | 12 | #ifndef hifi_KeyboardMouseDevice_h |
| 13 | 13 | #define hifi_KeyboardMouseDevice_h |
| 14 | 14 | |
| 15 | −#include <QTouchEvent> | |
| 16 | 15 | #include <chrono> |
| 17 | −#include "InputDevice.h" | |
| 16 | + | |
| 17 | +#include <QtCore/QPoint> | |
| 18 | + | |
| 19 | +#include <controllers/InputDevice.h> | |
| 18 | 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 | 28 | Q_OBJECT |
| 22 | 29 | public: |
| 23 | 30 | enum KeyboardChannel { |
| @@ -61,15 +68,12 @@ public: · 37 unmodified lines | ||
| 61 | 68 | virtual bool isJointController() const override { return false; } |
| 62 | 69 | const QString& getName() const override { return NAME; } |
| 63 | 70 | |
| 64 | − virtual void activate() override {}; | |
| 65 | − virtual void deactivate() override {}; | |
| 66 | − | |
| 67 | 71 | virtual void pluginFocusOutEvent() override { focusOutEvent(); } |
| 68 | 72 | virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); } |
| 69 | 73 | |
| 70 | 74 | // Device functions |
| 71 | − virtual void registerToUserInputMapper(UserInputMapper& mapper) override; | |
| 72 | − virtual void assignDefaultInputMapping(UserInputMapper& mapper) override; | |
| 75 | + virtual controller::Input::NamedVector getAvailableInputs() const override; | |
| 76 | + virtual QString getDefaultMappingConfig() const override; | |
| 73 | 77 | virtual void update(float deltaTime, bool jointsCaptured) override; |
| 74 | 78 | virtual void focusOutEvent() override; |
| 75 | 79 | |
| @@ -87,11 +91,11 @@ public: · 11 unmodified lines | ||
| 87 | 91 | void wheelEvent(QWheelEvent* event); |
| 88 | 92 | |
| 89 | 93 | // Let's make it easy for Qt because we assume we love Qt forever |
| 90 | − UserInputMapper::Input makeInput(Qt::Key code); | |
| 91 | − UserInputMapper::Input makeInput(Qt::MouseButton code); | |
| 92 | − UserInputMapper::Input makeInput(KeyboardMouseDevice::MouseAxisChannel axis); | |
| 93 | − UserInputMapper::Input makeInput(KeyboardMouseDevice::TouchAxisChannel axis); | |
| 94 | − UserInputMapper::Input makeInput(KeyboardMouseDevice::TouchButtonChannel button); | |
| 94 | + controller::Input makeInput(Qt::Key code) const; | |
| 95 | + controller::Input makeInput(Qt::MouseButton code) const; | |
| 96 | + controller::Input makeInput(MouseAxisChannel axis) const; | |
| 97 | + controller::Input makeInput(TouchAxisChannel axis) const; | |
| 98 | + controller::Input makeInput(TouchButtonChannel button) const; | |
| 95 | 99 | |
| 96 | 100 | static const QString NAME; |
| 97 | 101 | |
| @@ -100,7 +104,6 @@ protected: · 2 unmodified lines | ||
| 100 | 104 | glm::vec2 _lastTouch; |
| 101 | 105 | bool _isTouching = false; |
| 102 | 106 | |
| 103 | − glm::vec2 evalAverageTouchPoints(const QList<QTouchEvent::TouchPoint>& points) const; | |
| 104 | 107 | std::chrono::high_resolution_clock _clock; |
| 105 | 108 | std::chrono::high_resolution_clock::time_point _lastTouchTime; |
| 106 | 109 | }; |
| @@ -14,6 +14,7 @@ · 13 unmodified lines | ||
| 14 | 14 | #include <HFActionEvent.h> |
| 15 | 15 | #include <HFBackEvent.h> |
| 16 | 16 | #include <PerfStat.h> |
| 17 | +#include <controllers/UserInputMapper.h> | |
| 17 | 18 | |
| 18 | 19 | #include "SDL2Manager.h" |
| 19 | 20 | |
| @@ -47,12 +48,12 @@ void SDL2Manager::init() { · 27 unmodified lines | ||
| 47 | 48 | if (controller) { |
| 48 | 49 | SDL_JoystickID id = getInstanceId(controller); |
| 49 | 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 | 53 | _openJoysticks[id] = joystick; |
| 52 | − auto userInputMapper = DependencyManager::get<UserInputMapper>(); | |
| 53 | − joystick->registerToUserInputMapper(*userInputMapper); | |
| 54 | − joystick->assignDefaultInputMapping(*userInputMapper); | |
| 55 | − emit joystickAdded(joystick); | |
| 54 | + auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); | |
| 55 | + userInputMapper->registerDevice(joystick); | |
| 56 | + emit joystickAdded(joystick.get()); | |
| 56 | 57 | } |
| 57 | 58 | } |
| 58 | 59 | } |
| @@ -67,12 +68,33 @@ void SDL2Manager::init() { · 8 unmodified lines | ||
| 67 | 68 | |
| 68 | 69 | void SDL2Manager::deinit() { |
| 69 | 70 | #ifdef HAVE_SDL2 |
| 70 | − qDeleteAll(_openJoysticks); | |
| 71 | + _openJoysticks.clear(); | |
| 71 | 72 | |
| 72 | 73 | SDL_Quit(); |
| 73 | 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 | 98 | bool SDL2Manager::isSupported() const { |
| 77 | 99 | #ifdef HAVE_SDL2 |
| 78 | 100 | return true; |
| @@ -92,7 +114,7 @@ void SDL2Manager::pluginFocusOutEvent() { · 13 unmodified lines | ||
| 92 | 114 | void SDL2Manager::pluginUpdate(float deltaTime, bool jointsCaptured) { |
| 93 | 115 | #ifdef HAVE_SDL2 |
| 94 | 116 | if (_isInitialized) { |
| 95 | − auto userInputMapper = DependencyManager::get<UserInputMapper>(); | |
| 117 | + auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); | |
| 96 | 118 | for (auto joystick : _openJoysticks) { |
| 97 | 119 | joystick->update(deltaTime, jointsCaptured); |
| 98 | 120 | } |
| @@ -102,12 +124,12 @@ void SDL2Manager::pluginUpdate(float deltaTime, bool jointsCaptured) { · 3 unmodified lines | ||
| 102 | 124 | SDL_Event event; |
| 103 | 125 | while (SDL_PollEvent(&event)) { |
| 104 | 126 | if (event.type == SDL_CONTROLLERAXISMOTION) { |
| 105 | − Joystick* joystick = _openJoysticks[event.caxis.which]; | |
| 127 | + Joystick::Pointer joystick = _openJoysticks[event.caxis.which]; | |
| 106 | 128 | if (joystick) { |
| 107 | 129 | joystick->handleAxisEvent(event.caxis); |
| 108 | 130 | } |
| 109 | 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 | 133 | if (joystick) { |
| 112 | 134 | joystick->handleButtonEvent(event.cbutton); |
| 113 | 135 | } |
| @@ -124,20 +146,21 @@ void SDL2Manager::pluginUpdate(float deltaTime, bool jointsCaptured) { · 10 unmodified lines | ||
| 124 | 146 | |
| 125 | 147 | } else if (event.type == SDL_CONTROLLERDEVICEADDED) { |
| 126 | 148 | SDL_GameController* controller = SDL_GameControllerOpen(event.cdevice.which); |
| 127 | − | |
| 128 | 149 | SDL_JoystickID id = getInstanceId(controller); |
| 129 | 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 | 153 | _openJoysticks[id] = joystick; |
| 132 | − joystick->registerToUserInputMapper(*userInputMapper); | |
| 133 | − joystick->assignDefaultInputMapping(*userInputMapper); | |
| 134 | − emit joystickAdded(joystick); | |
| 154 | + userInputMapper->registerDevice(joystick); | |
| 155 | + emit joystickAdded(joystick.get()); | |
| 135 | 156 | } |
| 136 | 157 | } else if (event.type == SDL_CONTROLLERDEVICEREMOVED) { |
| 137 | − Joystick* joystick = _openJoysticks[event.cdevice.which]; | |
| 138 | − _openJoysticks.remove(event.cdevice.which); | |
| 139 | − userInputMapper->removeDevice(joystick->getDeviceID()); | |
| 140 | − emit joystickRemoved(joystick); | |
| 158 | + if (_openJoysticks.contains(event.cdevice.which)) { | |
| 159 | + Joystick::Pointer joystick = _openJoysticks[event.cdevice.which]; | |
| 160 | + _openJoysticks.remove(event.cdevice.which); | |
| 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 | 16 | #include <SDL.h> |
| 17 | 17 | #endif |
| 18 | 18 | |
| 19 | −#include "InputPlugin.h" | |
| 20 | −#include "UserInputMapper.h" | |
| 19 | +#include <controllers/UserInputMapper.h> | |
| 21 | 20 | |
| 21 | +#include "InputPlugin.h" | |
| 22 | 22 | #include "Joystick.h" |
| 23 | 23 | |
| 24 | 24 | class SDL2Manager : public InputPlugin { |
| @@ -34,9 +34,12 @@ public: · 9 unmodified lines | ||
| 34 | 34 | |
| 35 | 35 | virtual void init() override; |
| 36 | 36 | virtual void deinit() override; |
| 37 | − virtual void activate() override {}; | |
| 38 | − virtual void deactivate() override {}; | |
| 39 | − | |
| 37 | + | |
| 38 | + /// Called when a plugin is being activated for use. May be called multiple times. | |
| 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 | 43 | virtual void pluginFocusOutEvent() override; |
| 41 | 44 | virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override; |
| 42 | 45 | |
| @@ -78,7 +81,7 @@ private: · 35 unmodified lines | ||
| 78 | 81 | int buttonPressed() const { return SDL_PRESSED; } |
| 79 | 82 | int buttonRelease() const { return SDL_RELEASED; } |
| 80 | 83 | |
| 81 | − QMap<SDL_JoystickID, Joystick*> _openJoysticks; | |
| 84 | + QMap<SDL_JoystickID, Joystick::Pointer> _openJoysticks; | |
| 82 | 85 | #endif |
| 83 | 86 | bool _isInitialized; |
| 84 | 87 | static const QString NAME; |
| @@ -18,10 +18,13 @@ · 17 unmodified lines | ||
| 18 | 18 | #include <PerfStat.h> |
| 19 | 19 | #include <SettingHandle.h> |
| 20 | 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 | 26 | #include "SixenseManager.h" |
| 24 | −#include "UserActivityLogger.h" | |
| 27 | + | |
| 25 | 28 | |
| 26 | 29 | #ifdef HAVE_SIXENSE |
| 27 | 30 | #include "sixense.h" |
| @@ -32,10 +35,6 @@ · 4 unmodified lines | ||
| 32 | 35 | Q_DECLARE_LOGGING_CATEGORY(inputplugins) |
| 33 | 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 | 38 | #ifdef HAVE_SIXENSE |
| 40 | 39 | |
| 41 | 40 | const int CALIBRATION_STATE_IDLE = 0; |
| @@ -65,15 +64,12 @@ const QString MENU_PATH = MENU_PARENT + ">" + MENU_NAME; · 23 unmodified lines | ||
| 65 | 64 | const QString TOGGLE_SMOOTH = "Smooth Sixense Movement"; |
| 66 | 65 | const float DEFAULT_REACH_LENGTH = 1.5f; |
| 67 | 66 | |
| 68 | − | |
| 67 | +static std::shared_ptr<SixenseManager> instance; | |
| 69 | 68 | SixenseManager::SixenseManager() : |
| 70 | 69 | InputDevice("Hydra"), |
| 71 | − _reachLength(DEFAULT_REACH_LENGTH), | |
| 72 | −#ifdef __APPLE__ | |
| 73 | − _sixenseLibrary(nullptr), | |
| 74 | −#endif | |
| 75 | − _hydrasConnected(false) | |
| 70 | + _reachLength(DEFAULT_REACH_LENGTH) | |
| 76 | 71 | { |
| 72 | + instance = std::shared_ptr<SixenseManager>(this); | |
| 77 | 73 | } |
| 78 | 74 | |
| 79 | 75 | bool SixenseManager::isSupported() const { |
| @@ -85,6 +81,7 @@ bool SixenseManager::isSupported() const { · 5 unmodified lines | ||
| 85 | 81 | } |
| 86 | 82 | |
| 87 | 83 | void SixenseManager::activate() { |
| 84 | + InputPlugin::activate(); | |
| 88 | 85 | #ifdef HAVE_SIXENSE |
| 89 | 86 | _calibrationState = CALIBRATION_STATE_IDLE; |
| 90 | 87 | _avatarPosition = DEFAULT_AVATAR_POSITION; |
| @@ -94,6 +91,9 @@ void SixenseManager::activate() { · 3 unmodified lines | ||
| 94 | 91 | [this] (bool clicked) { this->setSixenseFilter(clicked); }, |
| 95 | 92 | true, true); |
| 96 | 93 | |
| 94 | + auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); | |
| 95 | + userInputMapper->registerDevice(instance); | |
| 96 | + | |
| 97 | 97 | #ifdef __APPLE__ |
| 98 | 98 | |
| 99 | 99 | if (!_sixenseLibrary) { |
| @@ -125,11 +125,19 @@ void SixenseManager::activate() { · 25 unmodified lines | ||
| 125 | 125 | } |
| 126 | 126 | |
| 127 | 127 | void SixenseManager::deactivate() { |
| 128 | + InputPlugin::deactivate(); | |
| 129 | + | |
| 128 | 130 | #ifdef HAVE_SIXENSE |
| 129 | 131 | CONTAINER->removeMenuItem(MENU_NAME, TOGGLE_SMOOTH); |
| 130 | 132 | CONTAINER->removeMenu(MENU_PATH); |
| 131 | 133 | |
| 132 | 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 | 142 | #ifdef __APPLE__ |
| 135 | 143 | SixenseBaseFunction sixenseExit = (SixenseBaseFunction)_sixenseLibrary->resolve("sixenseExit"); |
| @@ -155,6 +163,12 @@ void SixenseManager::setSixenseFilter(bool filter) { · 19 unmodified lines | ||
| 155 | 163 | } |
| 156 | 164 | |
| 157 | 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 | 172 | #ifdef HAVE_SIXENSE |
| 159 | 173 | _buttonPressedMap.clear(); |
| 160 | 174 | |
| @@ -163,33 +177,34 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) { · 2 unmodified lines | ||
| 163 | 177 | (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetNumActiveControllers"); |
| 164 | 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 | 185 | if (sixenseGetNumActiveControllers() == 0) { |
| 169 | − if (_hydrasConnected) { | |
| 170 | − qCDebug(inputplugins) << "hydra disconnected" << _badDataCount; | |
| 171 | − if (_badDataCount++ < _allowedBadDataCount) { // gotta get some no-active in a row before we shut things down | |
| 172 | − return; | |
| 173 | − } | |
| 186 | + if (!disconnected) { | |
| 187 | + disconnectedInterval += deltaTime; | |
| 174 | 188 | } |
| 175 | − _hydrasConnected = false; | |
| 176 | − if (_deviceID != 0) { | |
| 177 | − userInputMapper->removeDevice(_deviceID); | |
| 178 | − _deviceID = 0; | |
| 189 | + if (disconnectedInterval > MAX_DISCONNECTED_TIME) { | |
| 190 | + disconnected = true; | |
| 191 | + _axisStateMap.clear(); | |
| 192 | + _buttonPressedMap.clear(); | |
| 179 | 193 | _poseStateMap.clear(); |
| 194 | + _collectedSamples.clear(); | |
| 180 | 195 | } |
| 181 | 196 | return; |
| 182 | 197 | } |
| 183 | 198 | |
| 184 | − PerformanceTimer perfTimer("sixense"); | |
| 185 | − if (!_hydrasConnected) { | |
| 186 | − _hydrasConnected = true; | |
| 187 | − _badDataCount = 0; | |
| 188 | − registerToUserInputMapper(*userInputMapper); | |
| 189 | − assignDefaultInputMapping(*userInputMapper); | |
| 190 | − UserActivityLogger::getInstance().connectedDevice("spatial_controller", "hydra"); | |
| 199 | + if (disconnected) { | |
| 200 | + disconnected = 0; | |
| 201 | + disconnectedInterval = 0.0f; | |
| 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 | 208 | #ifdef __APPLE__ |
| 194 | 209 | SixenseBaseFunction sixenseGetMaxControllers = |
| 195 | 210 | (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetMaxControllers"); |
| @@ -220,28 +235,31 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) { · 24 unmodified lines | ||
| 220 | 235 | // NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters. |
| 221 | 236 | glm::vec3 position(data->pos[0], data->pos[1], data->pos[2]); |
| 222 | 237 | position *= METERS_PER_MILLIMETER; |
| 223 | − | |
| 238 | + bool left = i == 0; | |
| 239 | + using namespace controller; | |
| 224 | 240 | // Check to see if this hand/controller is on the base |
| 225 | 241 | const float CONTROLLER_AT_BASE_DISTANCE = 0.075f; |
| 226 | 242 | if (glm::length(position) >= CONTROLLER_AT_BASE_DISTANCE) { |
| 227 | − handleButtonEvent(data->buttons, numActiveControllers - 1); | |
| 228 | − handleAxisEvent(data->joystick_x, data->joystick_y, data->trigger, numActiveControllers - 1); | |
| 243 | + handleButtonEvent(data->buttons, left); | |
| 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 | 248 | if (!jointsCaptured) { |
| 231 | 249 | // Rotation of Palm |
| 232 | 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 | 253 | } else { |
| 235 | 254 | _poseStateMap.clear(); |
| 255 | + _collectedSamples.clear(); | |
| 236 | 256 | } |
| 237 | 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 | 265 | if (numActiveControllers == 2) { |
| @@ -361,40 +379,38 @@ void SixenseManager::focusOutEvent() { · 113 unmodified lines | ||
| 361 | 379 | _buttonPressedMap.clear(); |
| 362 | 380 | }; |
| 363 | 381 | |
| 364 | −void SixenseManager::handleAxisEvent(float stickX, float stickY, float trigger, int index) { | |
| 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; | |
| 382 | +void SixenseManager::handleAxisEvent(float stickX, float stickY, float trigger, bool left) { | |
| 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 | 387 | if (buttons & BUTTON_0) { |
| 374 | − _buttonPressedMap.insert(makeInput(BUTTON_0, index).getChannel()); | |
| 388 | + _buttonPressedMap.insert(left ? BACK : START); | |
| 375 | 389 | } |
| 376 | 390 | if (buttons & BUTTON_1) { |
| 377 | − _buttonPressedMap.insert(makeInput(BUTTON_1, index).getChannel()); | |
| 391 | + _buttonPressedMap.insert(left ? DL : X); | |
| 378 | 392 | } |
| 379 | 393 | if (buttons & BUTTON_2) { |
| 380 | − _buttonPressedMap.insert(makeInput(BUTTON_2, index).getChannel()); | |
| 394 | + _buttonPressedMap.insert(left ? DD : A); | |
| 381 | 395 | } |
| 382 | 396 | if (buttons & BUTTON_3) { |
| 383 | − _buttonPressedMap.insert(makeInput(BUTTON_3, index).getChannel()); | |
| 397 | + _buttonPressedMap.insert(left ? DR : B); | |
| 384 | 398 | } |
| 385 | 399 | if (buttons & BUTTON_4) { |
| 386 | − _buttonPressedMap.insert(makeInput(BUTTON_4, index).getChannel()); | |
| 400 | + _buttonPressedMap.insert(left ? DU : Y); | |
| 387 | 401 | } |
| 388 | 402 | if (buttons & BUTTON_FWD) { |
| 389 | − _buttonPressedMap.insert(makeInput(BUTTON_FWD, index).getChannel()); | |
| 403 | + _buttonPressedMap.insert(left ? LB : RB); | |
| 390 | 404 | } |
| 391 | 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 | 411 | #ifdef HAVE_SIXENSE |
| 412 | + auto hand = left ? controller::StandardPoseChannel::LEFT_HAND : controller::StandardPoseChannel::RIGHT_HAND; | |
| 413 | + | |
| 398 | 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 | 427 | // z |
| 428 | + auto prevPose = _poseStateMap[hand]; | |
| 412 | 429 | |
| 413 | 430 | // Transform the measured position into body frame. |
| 414 | 431 | position = _avatarRotation * (position + _avatarPosition); |
| @@ -437,7 +454,7 @@ void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int · 22 unmodified lines | ||
| 437 | 454 | // In addition to Qsh each hand has pre-offset introduced by the shape of the sixense controllers |
| 438 | 455 | // and how they fit into the hand in their relaxed state. This offset is a quarter turn about |
| 439 | 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 | 458 | const glm::quat preOffset = glm::angleAxis(sign * PI / 2.0f, Vectors::UNIT_Z); |
| 442 | 459 | |
| 443 | 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 | 469 | // TODO: find a shortcut with fewer rotations. |
| 453 | 470 | rotation = _avatarRotation * postOffset * glm::inverse(sixenseToHand) * rotation * preOffset * sixenseToHand; |
| 454 | 471 | |
| 455 | − _poseStateMap[makeInput(JointChannel(index)).getChannel()] = UserInputMapper::PoseValue(position, rotation); | |
| 456 | −#endif // HAVE_SIXENSE | |
| 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; | |
| 472 | + glm::vec3 velocity(0.0f); | |
| 473 | + glm::quat angularVelocity; | |
| 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 | |
| 529 | − mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(BUTTON_3, 0), BOOM_SPEED); | |
| 530 | − mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(BUTTON_1, 0), BOOM_SPEED); | |
| 477 | + if (prevPose.isValid() && deltaTime > std::numeric_limits<float>::epsilon()) { | |
| 531 | 478 | |
| 532 | − mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(BUTTON_3, 1), BUTTON_MOVE_SPEED); | |
| 533 | − mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(BUTTON_1, 1), BUTTON_MOVE_SPEED); | |
| 479 | + velocity = (position - prevPose.getTranslation()) / deltaTime; | |
| 534 | 480 | |
| 535 | − mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_2, 0)); | |
| 536 | − mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_2, 1)); | |
| 481 | + auto deltaRot = rotation * glm::conjugate(prevPose.getRotation()); | |
| 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)); | |
| 539 | − mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(BUTTON_4, 1)); | |
| 486 | + // Average | |
| 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)); | |
| 542 | − mapper.addInputChannel(UserInputMapper::RIGHT_HAND, makeInput(RIGHT_HAND)); | |
| 499 | + _poseStateMap[hand] = controller::Pose(position, rotation, velocity, angularVelocity); | |
| 500 | +#endif // HAVE_SIXENSE | |
| 501 | +} | |
| 543 | 502 | |
| 544 | − mapper.addInputChannel(UserInputMapper::LEFT_HAND_CLICK, makeInput(BACK_TRIGGER, 0)); | |
| 545 | − mapper.addInputChannel(UserInputMapper::RIGHT_HAND_CLICK, makeInput(BACK_TRIGGER, 1)); | |
| 503 | +static const auto L0 = controller::BACK; | |
| 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 | 594 | // virtual |
| 554 | 595 | void SixenseManager::saveSettings() const { |
| 555 | 596 | Settings settings; |
| @@ -559,7 +600,6 @@ void SixenseManager::saveSettings() const { · 3 unmodified lines | ||
| 559 | 600 | settings.setVec3Value(QString("avatarPosition"), _avatarPosition); |
| 560 | 601 | settings.setQuatValue(QString("avatarRotation"), _avatarRotation); |
| 561 | 602 | settings.setValue(QString("reachLength"), QVariant(_reachLength)); |
| 562 | − settings.setValue(QString("allowedHydraFailures"), 120); | |
| 563 | 603 | } |
| 564 | 604 | settings.endGroup(); |
| 565 | 605 | } |
| @@ -572,19 +612,6 @@ void SixenseManager::loadSettings() { · 6 unmodified lines | ||
| 572 | 612 | settings.getVec3ValueIfValid(QString("avatarPosition"), _avatarPosition); |
| 573 | 613 | settings.getQuatValueIfValid(QString("avatarRotation"), _avatarRotation); |
| 574 | 614 | settings.getFloatValueIfValid(QString("reachLength"), _reachLength); |
| 575 | − _allowedBadDataCount = settings.value(QString("allowedHydraFailures"), 120).toInt(); | |
| 576 | 615 | } |
| 577 | 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 | 25 | #endif |
| 26 | 26 | |
| 27 | +#include <SimpleMovingAverage.h> | |
| 28 | + | |
| 29 | +#include <controllers/InputDevice.h> | |
| 30 | +#include <controllers/StandardControls.h> | |
| 31 | + | |
| 27 | 32 | #include "InputPlugin.h" |
| 28 | −#include "InputDevice.h" | |
| 29 | 33 | |
| 30 | 34 | class QLibrary; |
| 31 | 35 | |
| @@ -40,22 +44,9 @@ const unsigned int BUTTON_TRIGGER = 1U << 8; · 8 unmodified lines | ||
| 40 | 44 | const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = false; |
| 41 | 45 | |
| 42 | 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 | 48 | Q_OBJECT |
| 45 | 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 | 50 | SixenseManager(); |
| 60 | 51 | |
| 61 | 52 | // Plugin functions |
| @@ -71,15 +62,12 @@ public: · 9 unmodified lines | ||
| 71 | 62 | virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); } |
| 72 | 63 | |
| 73 | 64 | // Device functions |
| 74 | − virtual void registerToUserInputMapper(UserInputMapper& mapper) override; | |
| 75 | − virtual void assignDefaultInputMapping(UserInputMapper& mapper) override; | |
| 65 | + virtual controller::Input::NamedVector getAvailableInputs() const override; | |
| 66 | + virtual QString getDefaultMappingConfig() const override; | |
| 67 | + | |
| 76 | 68 | virtual void update(float deltaTime, bool jointsCaptured) override; |
| 77 | 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 | 71 | virtual void saveSettings() const override; |
| 84 | 72 | virtual void loadSettings() override; |
| 85 | 73 | |
| @@ -87,9 +75,9 @@ public slots: · 1 unmodified line | ||
| 87 | 75 | void setSixenseFilter(bool filter); |
| 88 | 76 | |
| 89 | 77 | private: |
| 90 | − void handleButtonEvent(unsigned int buttons, int index); | |
| 91 | − void handleAxisEvent(float x, float y, float trigger, int index); | |
| 92 | − void handlePoseEvent(glm::vec3 position, glm::quat rotation, int index); | |
| 78 | + void handleButtonEvent(unsigned int buttons, bool left); | |
| 79 | + void handleAxisEvent(float x, float y, float trigger, bool left); | |
| 80 | + void handlePoseEvent(float deltaTime, glm::vec3 position, glm::quat rotation, bool left); | |
| 93 | 81 | |
| 94 | 82 | void updateCalibration(void* controllers); |
| 95 | 83 | |
| @@ -108,17 +96,20 @@ private: · 12 unmodified lines | ||
| 108 | 96 | glm::vec3 _reachRight; |
| 109 | 97 | float _lastDistance; |
| 110 | 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 | 106 | #ifdef __APPLE__ |
| 113 | − QLibrary* _sixenseLibrary; | |
| 107 | + QLibrary* _sixenseLibrary { nullptr }; | |
| 114 | 108 | #endif |
| 115 | 109 | |
| 116 | − bool _hydrasConnected; | |
| 117 | − int _badDataCount; | |
| 118 | − int _allowedBadDataCount; | |
| 119 | − | |
| 120 | 110 | static const QString NAME; |
| 121 | 111 | static const QString HYDRA_ID_STRING; |
| 122 | 112 | }; |
| 123 | 113 | |
| 124 | 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 | 12 | #include "ViveControllerManager.h" |
| 13 | 13 | |
| 14 | 14 | #include <PerfStat.h> |
| 15 | − | |
| 16 | −#include "GeometryCache.h" | |
| 15 | +#include <PathUtils.h> | |
| 16 | +#include <GeometryCache.h> | |
| 17 | 17 | #include <gpu/Batch.h> |
| 18 | 18 | #include <gpu/Context.h> |
| 19 | 19 | #include <DeferredLightingEffect.h> |
| 20 | 20 | #include <display-plugins/openvr/OpenVrHelpers.h> |
| 21 | −#include "NumericalConstants.h" | |
| 21 | +#include <NumericalConstants.h> | |
| 22 | 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 | 29 | #ifdef Q_OS_WIN |
| 26 | 30 | extern vr::IVRSystem* _hmd; |
| @@ -29,18 +33,6 @@ extern vr::TrackedDevicePose_t _trackedDevicePose[vr::k_unMaxTrackedDeviceCount] · 2 unmodified lines | ||
| 29 | 33 | extern mat4 _trackedDevicePoseMat4[vr::k_unMaxTrackedDeviceCount]; |
| 30 | 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 | 37 | const float CONTROLLER_LENGTH_OFFSET = 0.0762f; // three inches |
| 46 | 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 | 44 | const QString MENU_PATH = MENU_PARENT + ">" + MENU_NAME; |
| 53 | 45 | const QString RENDER_CONTROLLERS = "Render Hand Controllers"; |
| 54 | 46 | |
| 47 | +static std::shared_ptr<ViveControllerManager> instance; | |
| 48 | + | |
| 55 | 49 | ViveControllerManager::ViveControllerManager() : |
| 56 | − InputDevice("SteamVR Controller"), | |
| 50 | + InputDevice("Vive"), | |
| 57 | 51 | _trackedControllers(0), |
| 58 | 52 | _modelLoaded(false), |
| 59 | 53 | _leftHandRenderID(0), |
| 60 | 54 | _rightHandRenderID(0), |
| 61 | 55 | _renderControllers(false) |
| 62 | 56 | { |
| 63 | − | |
| 57 | + instance = std::shared_ptr<ViveControllerManager>(this); | |
| 64 | 58 | } |
| 65 | 59 | |
| 66 | 60 | bool ViveControllerManager::isSupported() const { |
| @@ -79,6 +73,7 @@ bool ViveControllerManager::isSupported() const { · 12 unmodified lines | ||
| 79 | 73 | } |
| 80 | 74 | |
| 81 | 75 | void ViveControllerManager::activate() { |
| 76 | + InputPlugin::activate(); | |
| 82 | 77 | #ifdef Q_OS_WIN |
| 83 | 78 | CONTAINER->addMenu(MENU_PATH); |
| 84 | 79 | CONTAINER->addMenuItem(MENU_PATH, RENDER_CONTROLLERS, |
| @@ -140,9 +135,16 @@ void ViveControllerManager::activate() { · 55 unmodified lines | ||
| 140 | 135 | _renderControllers = true; |
| 141 | 136 | } |
| 142 | 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 | 145 | void ViveControllerManager::deactivate() { |
| 146 | + InputPlugin::deactivate(); | |
| 147 | + | |
| 146 | 148 | #ifdef Q_OS_WIN |
| 147 | 149 | CONTAINER->removeMenuItem(MENU_NAME, RENDER_CONTROLLERS); |
| 148 | 150 | CONTAINER->removeMenu(MENU_PATH); |
| @@ -155,6 +157,11 @@ void ViveControllerManager::deactivate() { · 6 unmodified lines | ||
| 155 | 157 | } |
| 156 | 158 | _poseStateMap.clear(); |
| 157 | 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 | 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 | 177 | //pendingChanges.updateItem(_leftHandRenderID, ); |
| 171 | 178 | |
| 172 | 179 | |
| 173 | − UserInputMapper::PoseValue leftHand = _poseStateMap[makeInput(JointChannel::LEFT_HAND).getChannel()]; | |
| 174 | − UserInputMapper::PoseValue rightHand = _poseStateMap[makeInput(JointChannel::RIGHT_HAND).getChannel()]; | |
| 180 | + controller::Pose leftHand = _poseStateMap[controller::StandardPoseChannel::LEFT_HAND]; | |
| 181 | + controller::Pose rightHand = _poseStateMap[controller::StandardPoseChannel::RIGHT_HAND]; | |
| 175 | 182 | |
| 176 | 183 | gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { |
| 177 | 184 | auto geometryCache = DependencyManager::get<GeometryCache>(); |
| @@ -183,21 +190,20 @@ void ViveControllerManager::updateRendering(RenderArgs* args, render::ScenePoint · 5 unmodified lines | ||
| 183 | 190 | //batch._glBindTexture(GL_TEXTURE_2D, _uexture); |
| 184 | 191 | |
| 185 | 192 | if (leftHand.isValid()) { |
| 186 | − renderHand(leftHand, batch, LEFT_HAND); | |
| 193 | + renderHand(leftHand, batch, 1); | |
| 187 | 194 | } |
| 188 | 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) { | |
| 196 | − auto userInputMapper = DependencyManager::get<UserInputMapper>(); | |
| 202 | +void ViveControllerManager::renderHand(const controller::Pose& pose, gpu::Batch& batch, int sign) { | |
| 203 | + auto userInputMapper = DependencyManager::get<controller::UserInputMapper>(); | |
| 197 | 204 | Transform transform(userInputMapper->getSensorToWorldMat()); |
| 198 | 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 | 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 | 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 | 256 | numTrackedControllers++; |
| 257 | + bool left = numTrackedControllers == 1; | |
| 251 | 258 | |
| 252 | 259 | const mat4& mat = _trackedDevicePoseMat4[device]; |
| 253 | 260 | |
| @@ -261,26 +268,30 @@ void ViveControllerManager::update(float deltaTime, bool jointsCaptured) { · 7 unmodified lines | ||
| 261 | 268 | //qDebug() << (numTrackedControllers == 1 ? "Left: " : "Right: "); |
| 262 | 269 | //qDebug() << "Trackpad: " << controllerState.rAxis[0].x << " " << controllerState.rAxis[0].y; |
| 263 | 270 | //qDebug() << "Trigger: " << controllerState.rAxis[1].x << " " << controllerState.rAxis[1].y; |
| 264 | − handleButtonEvent(controllerState.ulButtonPressed, numTrackedControllers - 1); | |
| 265 | − for (int i = 0; i < vr::k_unControllerStateAxisCount; i++) { | |
| 266 | − handleAxisEvent(Axis(i), controllerState.rAxis[i].x, controllerState.rAxis[i].y, numTrackedControllers - 1); | |
| 271 | + for (uint32_t i = 0; i < vr::k_EButton_Max; ++i) { | |
| 272 | + auto mask = vr::ButtonMaskFromId((vr::EVRButtonId)i); | |
| 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 | 284 | if (numTrackedControllers == 0) { |
| 274 | − if (_deviceID != 0) { | |
| 285 | + if (_registeredWithInputMapper) { | |
| 275 | 286 | userInputMapper->removeDevice(_deviceID); |
| 276 | − _deviceID = 0; | |
| 287 | + _registeredWithInputMapper = false; | |
| 277 | 288 | _poseStateMap.clear(); |
| 278 | 289 | } |
| 279 | 290 | } |
| 280 | 291 | |
| 281 | 292 | if (_trackedControllers == 0 && numTrackedControllers > 0) { |
| 282 | − registerToUserInputMapper(*userInputMapper); | |
| 283 | − assignDefaultInputMapping(*userInputMapper); | |
| 293 | + userInputMapper->registerDevice(instance); | |
| 294 | + _registeredWithInputMapper = true; | |
| 284 | 295 | UserActivityLogger::getInstance().connectedDevice("spatial_controller", "steamVR"); |
| 285 | 296 | } |
| 286 | 297 | |
| @@ -293,33 +304,45 @@ void ViveControllerManager::focusOutEvent() { · 6 unmodified lines | ||
| 293 | 304 | _buttonPressedMap.clear(); |
| 294 | 305 | }; |
| 295 | 306 | |
| 296 | −void ViveControllerManager::handleAxisEvent(Axis axis, float x, float y, int index) { | |
| 297 | − if (axis == TRACKPAD_AXIS) { | |
| 298 | − _axisStateMap[makeInput(AXIS_Y_POS, index).getChannel()] = (y > 0.0f) ? y : 0.0f; | |
| 299 | − _axisStateMap[makeInput(AXIS_Y_NEG, index).getChannel()] = (y < 0.0f) ? -y : 0.0f; | |
| 300 | − _axisStateMap[makeInput(AXIS_X_POS, index).getChannel()] = (x > 0.0f) ? x : 0.0f; | |
| 301 | − _axisStateMap[makeInput(AXIS_X_NEG, index).getChannel()] = (x < 0.0f) ? -x : 0.0f; | |
| 302 | − } else if (axis == TRIGGER_AXIS) { | |
| 303 | − _axisStateMap[makeInput(BACK_TRIGGER, index).getChannel()] = x; | |
| 307 | +// These functions do translation from the Steam IDs to the standard controller IDs | |
| 308 | +void ViveControllerManager::handleAxisEvent(uint32_t axis, float x, float y, bool left) { | |
| 309 | +#ifdef Q_OS_WIN | |
| 310 | + axis += vr::k_EButton_Axis0; | |
| 311 | + using namespace controller; | |
| 312 | + if (axis == vr::k_EButton_SteamVR_Touchpad) { | |
| 313 | + _axisStateMap[left ? LX : RX] = 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) { | |
| 308 | − if (buttons & VR_BUTTON_A) { | |
| 309 | − _buttonPressedMap.insert(makeInput(BUTTON_A, index).getChannel()); | |
| 310 | − } | |
| 311 | − if (buttons & VR_GRIP_BUTTON) { | |
| 312 | − _buttonPressedMap.insert(makeInput(GRIP_BUTTON, index).getChannel()); | |
| 313 | − } | |
| 314 | − if (buttons & VR_TRACKPAD_BUTTON) { | |
| 315 | − _buttonPressedMap.insert(makeInput(TRACKPAD_BUTTON, index).getChannel()); | |
| 322 | +// These functions do translation from the Steam IDs to the standard controller IDs | |
| 323 | +void ViveControllerManager::handleButtonEvent(uint32_t button, bool pressed, bool left) { | |
| 324 | +#ifdef Q_OS_WIN | |
| 325 | + if (!pressed) { | |
| 326 | + return; | |
| 316 | 327 | } |
| 317 | − if (buttons & VR_TRIGGER_BUTTON) { | |
| 318 | − _buttonPressedMap.insert(makeInput(TRIGGER_BUTTON, index).getChannel()); | |
| 328 | + | |
| 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 | 346 | glm::vec3 position = extractTranslation(mat); |
| 324 | 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 | 399 | const glm::quat quarterX = glm::angleAxis(PI / 2.0f, glm::vec3(1.0f, 0.0f, 0.0f)); |
| 377 | 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 | 402 | const glm::quat signedQuaterZ = glm::angleAxis(sign * PI / 2.0f, glm::vec3(0.0f, 0.0f, 1.0f)); |
| 380 | 403 | const glm::quat eighthX = glm::angleAxis(PI / 4.0f, glm::vec3(1.0f, 0.0f, 0.0f)); |
| 381 | 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 | 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) { | |
| 390 | − // Grab the current free device ID | |
| 391 | − _deviceID = mapper.getFreeDeviceID(); | |
| 392 | − | |
| 393 | − auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy(_name)); | |
| 394 | − proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); }; | |
| 395 | − proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); }; | |
| 396 | − proxy->getPose = [this](const UserInputMapper::Input& input, int timestamp) -> UserInputMapper::PoseValue { return this->getPose(input.getChannel()); }; | |
| 397 | − proxy->getAvailabeInputs = [this] () -> QVector<UserInputMapper::InputPair> { | |
| 398 | − QVector<UserInputMapper::InputPair> availableInputs; | |
| 399 | − availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_HAND), "Left Hand")); | |
| 400 | − | |
| 401 | − availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_A, 0), "Left Button A")); | |
| 402 | − availableInputs.append(UserInputMapper::InputPair(makeInput(GRIP_BUTTON, 0), "Left Grip Button")); | |
| 403 | − availableInputs.append(UserInputMapper::InputPair(makeInput(TRACKPAD_BUTTON, 0), "Left Trackpad Button")); | |
| 404 | − availableInputs.append(UserInputMapper::InputPair(makeInput(TRIGGER_BUTTON, 0), "Left Trigger Button")); | |
| 405 | − | |
| 406 | − availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_POS, 0), "Left Trackpad Up")); | |
| 407 | − availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_NEG, 0), "Left Trackpad Down")); | |
| 408 | − availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_POS, 0), "Left Trackpad Right")); | |
| 409 | − availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_NEG, 0), "Left Trackpad Left")); | |
| 410 | − availableInputs.append(UserInputMapper::InputPair(makeInput(BACK_TRIGGER, 0), "Left Back Trigger")); | |
| 411 | − | |
| 412 | − | |
| 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; | |
| 412 | +controller::Input::NamedVector ViveControllerManager::getAvailableInputs() const { | |
| 413 | + using namespace controller; | |
| 414 | + QVector<Input::NamedPair> availableInputs{ | |
| 415 | + // Trackpad analogs | |
| 416 | + makePair(LX, "LX"), | |
| 417 | + makePair(LY, "LY"), | |
| 418 | + makePair(RX, "RX"), | |
| 419 | + makePair(RY, "RY"), | |
| 420 | + // trigger analogs | |
| 421 | + makePair(LT, "LT"), | |
| 422 | + makePair(RT, "RT"), | |
| 423 | + | |
| 424 | + makePair(LB, "LB"), | |
| 425 | + makePair(RB, "RB"), | |
| 426 | + | |
| 427 | + makePair(LS, "LS"), | |
| 428 | + makePair(RS, "RS"), | |
| 429 | + makePair(LEFT_HAND, "LeftHand"), | |
| 430 | + makePair(RIGHT_HAND, "RightHand"), | |
| 431 | + | |
| 432 | + makePair(LEFT_PRIMARY_THUMB, "LeftPrimaryThumb"), | |
| 433 | + makePair(LEFT_SECONDARY_THUMB, "LeftSecondaryThumb"), | |
| 434 | + makePair(RIGHT_PRIMARY_THUMB, "RightPrimaryThumb"), | |
| 435 | + makePair(RIGHT_SECONDARY_THUMB, "RightSecondaryThumb"), | |
| 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) { | |
| 465 | − return UserInputMapper::Input(_deviceID, button | (index == 0 ? LEFT_MASK : RIGHT_MASK), UserInputMapper::ChannelType::BUTTON); | |
| 438 | + //availableInputs.append(Input::NamedPair(makeInput(BUTTON_A, 0), "Left Button A")); | |
| 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) { | |
| 469 | − return UserInputMapper::Input(_deviceID, axis | (index == 0 ? LEFT_MASK : RIGHT_MASK), UserInputMapper::ChannelType::AXIS); | |
| 453 | +QString ViveControllerManager::getDefaultMappingConfig() const { | |
| 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) { | |
| 473 | − return UserInputMapper::Input(_deviceID, joint, UserInputMapper::ChannelType::POSE); | |
| 474 | −} | |
| 458 | +//void ViveControllerManager::assignDefaultInputMapping(UserInputMapper& mapper) { | |
| 459 | +// const float JOYSTICK_MOVE_SPEED = 1.0f; | |
| 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 | 20 | #include <model/Geometry.h> |
| 21 | 21 | #include <gpu/Texture.h> |
| 22 | −#include "InputDevice.h" | |
| 22 | +#include <controllers/InputDevice.h> | |
| 23 | 23 | #include "InputPlugin.h" |
| 24 | 24 | #include <RenderArgs.h> |
| 25 | 25 | #include <render/Scene.h> |
| 26 | 26 | |
| 27 | −class ViveControllerManager : public InputPlugin, public InputDevice { | |
| 27 | +class ViveControllerManager : public InputPlugin, public controller::InputDevice { | |
| 28 | 28 | Q_OBJECT |
| 29 | 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 | 30 | ViveControllerManager(); |
| 52 | 31 | |
| 53 | 32 | // Plugin functions |
| @@ -62,8 +41,8 @@ public: · 8 unmodified lines | ||
| 62 | 41 | virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); } |
| 63 | 42 | |
| 64 | 43 | // Device functions |
| 65 | − virtual void registerToUserInputMapper(UserInputMapper& mapper) override; | |
| 66 | − virtual void assignDefaultInputMapping(UserInputMapper& mapper) override; | |
| 44 | + virtual controller::Input::NamedVector getAvailableInputs() const override; | |
| 45 | + virtual QString getDefaultMappingConfig() const override; | |
| 67 | 46 | virtual void update(float deltaTime, bool jointsCaptured) override; |
| 68 | 47 | virtual void focusOutEvent() override; |
| 69 | 48 | |
| @@ -71,16 +50,12 @@ public: · 1 unmodified line | ||
| 71 | 50 | |
| 72 | 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 | 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); | |
| 82 | − void handleAxisEvent(Axis axis, float x, float y, int index); | |
| 83 | − void handlePoseEvent(const mat4& mat, int index); | |
| 56 | + void handleButtonEvent(uint32_t button, bool pressed, bool left); | |
| 57 | + void handleAxisEvent(uint32_t axis, float x, float y, bool left); | |
| 58 | + void handlePoseEvent(const mat4& mat, bool left); | |
| 84 | 59 | |
| 85 | 60 | int _trackedControllers; |
| 86 | 61 | |
| @@ -94,6 +69,9 @@ private: · 7 unmodified lines | ||
| 94 | 69 | bool _renderControllers; |
| 95 | 70 | |
| 96 | 71 | static const QString NAME; |
| 72 | + | |
| 73 | + bool _registeredWithInputMapper { false }; | |
| 74 | + | |
| 97 | 75 | }; |
| 98 | 76 | |
| 99 | 77 | #endif // hifi__ViveControllerManager |
| @@ -89,7 +89,6 @@ AssetRequest* AssetClient::createRequest(const QString& hash, const QString& ext · 88 unmodified lines | ||
| 89 | 89 | |
| 90 | 90 | // Move to the AssetClient thread in case we are not currently on that thread (which will usually be the case) |
| 91 | 91 | request->moveToThread(thread()); |
| 92 | − request->setParent(this); | |
| 93 | 92 | |
| 94 | 93 | return request; |
| 95 | 94 | } else { |
| @@ -105,7 +104,6 @@ AssetUpload* AssetClient::createUpload(const QString& filename) { · 9 unmodified lines | ||
| 105 | 104 | auto upload = new AssetUpload(filename); |
| 106 | 105 | |
| 107 | 106 | upload->moveToThread(thread()); |
| 108 | − upload->setParent(this); | |
| 109 | 107 | |
| 110 | 108 | return upload; |
| 111 | 109 | } else { |
| @@ -118,7 +116,6 @@ AssetUpload* AssetClient::createUpload(const QByteArray& data, const QString& ex · 6 unmodified lines | ||
| 118 | 116 | auto upload = new AssetUpload(data, extension); |
| 119 | 117 | |
| 120 | 118 | upload->moveToThread(thread()); |
| 121 | − upload->setParent(this); | |
| 122 | 119 | |
| 123 | 120 | return upload; |
| 124 | 121 | } else { |
| @@ -38,7 +38,7 @@ PacketVersion versionForPacketType(PacketType packetType) { · 37 unmodified lines | ||
| 38 | 38 | case PacketType::EntityAdd: |
| 39 | 39 | case PacketType::EntityEdit: |
| 40 | 40 | case PacketType::EntityData: |
| 41 | − return VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP; | |
| 41 | + return VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP_BIS; | |
| 42 | 42 | case PacketType::AvatarData: |
| 43 | 43 | case PacketType::BulkAvatarData: |
| 44 | 44 | default: |
| @@ -144,5 +144,6 @@ const PacketVersion VERSION_ENTITIES_PARTICLE_ELLIPSOID_EMITTER = 44; · 143 unmodified lines | ||
| 144 | 144 | const PacketVersion VERSION_ENTITIES_PROTOCOL_CHANNELS = 45; |
| 145 | 145 | const PacketVersion VERSION_ENTITIES_ANIMATION_PROPERTIES_GROUP = 46; |
| 146 | 146 | const PacketVersion VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP = 47; |
| 147 | +const PacketVersion VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP_BIS = 48; | |
| 147 | 148 | |
| 148 | 149 | #endif // hifi_PacketHeaders_h |
| @@ -14,11 +14,11 @@ · 13 unmodified lines | ||
| 14 | 14 | #include "PhysicsCollisionGroups.h" |
| 15 | 15 | |
| 16 | 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 | 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 | 24 | void CharacterController::setDynamicsWorld(btDynamicsWorld* world) { |
| @@ -241,7 +241,7 @@ void PhysicsEngine::stepSimulation() { · 240 unmodified lines | ||
| 241 | 241 | float timeStep = btMin(dt, MAX_TIMESTEP); |
| 242 | 242 | |
| 243 | 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 | 245 | // the updateShapeIfNecessary() call needs info from MyAvatar and should |
| 246 | 246 | // be done on the main thread during the pre-simulation stuff |
| 247 | 247 | if (_myAvatarController->needsRemoval()) { |
| @@ -263,7 +263,8 @@ void PhysicsEngine::stepSimulation() { · 15 unmodified lines | ||
| 263 | 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 | 268 | if (numSubsteps > 0) { |
| 268 | 269 | BT_PROFILE("postSimulation"); |
| 269 | 270 | _numSubsteps += (uint32_t)numSubsteps; |
| @@ -27,8 +27,9 @@ ThreadSafeDynamicsWorld::ThreadSafeDynamicsWorld( · 26 unmodified lines | ||
| 27 | 27 | : btDiscreteDynamicsWorld(dispatcher, pairCache, constraintSolver, collisionConfiguration) { |
| 28 | 28 | } |
| 29 | 29 | |
| 30 | −int ThreadSafeDynamicsWorld::stepSimulation( btScalar timeStep, int maxSubSteps, btScalar fixedTimeStep, SubStepCallback onSubStep) { | |
| 31 | − BT_PROFILE("stepSimulation"); | |
| 30 | +int ThreadSafeDynamicsWorld::stepSimulationWithSubstepCallback(btScalar timeStep, int maxSubSteps, | |
| 31 | + btScalar fixedTimeStep, SubStepCallback onSubStep) { | |
| 32 | + BT_PROFILE("stepSimulationWithSubstepCallback"); | |
| 32 | 33 | int subSteps = 0; |
| 33 | 34 | if (maxSubSteps) { |
| 34 | 35 | //fixed timestep with interpolation |
| @@ -37,8 +37,9 @@ public: · 36 unmodified lines | ||
| 37 | 37 | btConstraintSolver* constraintSolver, |
| 38 | 38 | btCollisionConfiguration* collisionConfiguration); |
| 39 | 39 | |
| 40 | − // virtual overrides from btDiscreteDynamicsWorld | |
| 41 | − int stepSimulation( btScalar timeStep, int maxSubSteps=1, btScalar fixedTimeStep=btScalar(1.)/btScalar(60.), SubStepCallback onSubStep = []() { }); | |
| 40 | + int stepSimulationWithSubstepCallback(btScalar timeStep, int maxSubSteps = 1, | |
| 41 | + btScalar fixedTimeStep = btScalar(1.)/btScalar(60.), | |
| 42 | + SubStepCallback onSubStep = []() { }); | |
| 42 | 43 | void synchronizeMotionStates(); |
| 43 | 44 | |
| 44 | 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 | 33 | virtual void deinit(); |
| 34 | 34 | |
| 35 | 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 | 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 | 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 | 57 | virtual void loadSettings() {} |
| 49 | 58 | |
| 50 | 59 | protected: |
| 60 | + bool _active{ false }; | |
| 51 | 61 | static PluginContainer* CONTAINER; |
| 52 | 62 | static QString UNKNOWN_PLUGIN_ID; |
| 53 | 63 | |
| @@ -1,3 +1,3 @@ | ||
| 1 | 1 | set(TARGET_NAME script-engine) |
| 2 | 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 | 12 | #ifndef hifi_AbstractScriptingServicesInterface_h |
| 13 | 13 | #define hifi_AbstractScriptingServicesInterface_h |
| 14 | 14 | |
| 15 | −class AbstractControllerScriptingInterface; | |
| 16 | −class Transform; | |
| 17 | 15 | class ScriptEngine; |
| 18 | −class QThread; | |
| 19 | 16 | |
| 20 | 17 | /// Interface provided by Application to other objects that need access to scripting services of the application |
| 21 | 18 | class AbstractScriptingServicesInterface { |
| 22 | 19 | public: |
| 23 | − | |
| 24 | − /// Returns the controller interface for the application | |
| 25 | − virtual AbstractControllerScriptingInterface* getControllerScriptingInterface() = 0; | |
| 26 | − | |
| 27 | 20 | /// Registers application specific services with a script engine. |
| 28 | 21 | virtual void registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine) = 0; |
| 29 | − | |
| 30 | 22 | }; |
| 31 | 23 | |
| 32 | 24 | |
| @@ -28,7 +28,9 @@ · 27 unmodified lines | ||
| 28 | 28 | #include <udt/PacketHeaders.h> |
| 29 | 29 | #include <UUID.h> |
| 30 | 30 | |
| 31 | −#include "AnimationObject.h" | |
| 31 | +#include <controllers/ScriptingInterface.h> | |
| 32 | +#include <AnimationObject.h> | |
| 33 | + | |
| 32 | 34 | #include "ArrayBufferViewClass.h" |
| 33 | 35 | #include "BatchLoader.h" |
| 34 | 36 | #include "DataViewClass.h" |
| @@ -76,13 +78,16 @@ void avatarDataFromScriptValue(const QScriptValue &object, AvatarData* &out) { · 41 unmodified lines | ||
| 76 | 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 | 82 | return engine->newQObject(in); |
| 81 | 83 | } |
| 82 | −void inputControllerFromScriptValue(const QScriptValue &object, AbstractInputController* &out) { | |
| 83 | − out = qobject_cast<AbstractInputController*>(object.toQObject()); | |
| 84 | + | |
| 85 | +void inputControllerFromScriptValue(const QScriptValue &object, controller::InputController* &out) { | |
| 86 | + out = qobject_cast<controller::InputController*>(object.toQObject()); | |
| 84 | 87 | } |
| 85 | 88 | |
| 89 | + | |
| 90 | + | |
| 86 | 91 | static bool hasCorrectSyntax(const QScriptProgram& program) { |
| 87 | 92 | const auto syntaxCheck = QScriptEngine::checkSyntax(program.sourceCode()); |
| 88 | 93 | if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) { |
| @@ -114,16 +119,13 @@ static bool hadUncaughtExceptions(QScriptEngine& engine, const QString& fileName · 25 unmodified lines | ||
| 114 | 119 | return false; |
| 115 | 120 | } |
| 116 | 121 | |
| 117 | −ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString, | |
| 118 | − AbstractControllerScriptingInterface* controllerScriptingInterface, bool wantSignals) : | |
| 119 | − | |
| 122 | +ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString, bool wantSignals) : | |
| 120 | 123 | _scriptContents(scriptContents), |
| 121 | 124 | _isFinished(false), |
| 122 | 125 | _isRunning(false), |
| 123 | 126 | _isInitialized(false), |
| 124 | 127 | _timerFunctionMap(), |
| 125 | 128 | _wantSignals(wantSignals), |
| 126 | − _controllerScriptingInterface(controllerScriptingInterface), | |
| 127 | 129 | _fileNameString(fileNameString), |
| 128 | 130 | _isUserLoaded(false), |
| 129 | 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 | 311 | void ScriptEngine::init() { |
| 289 | 312 | if (_isInitialized) { |
| 290 | 313 | return; // only initialize once |
| @@ -337,27 +360,26 @@ void ScriptEngine::init() { · 46 unmodified lines | ||
| 337 | 360 | |
| 338 | 361 | registerGlobalObject("Script", this); |
| 339 | 362 | registerGlobalObject("Audio", &AudioScriptingInterface::getInstance()); |
| 340 | − registerGlobalObject("Controller", _controllerScriptingInterface); | |
| 341 | 363 | registerGlobalObject("Entities", entityScriptingInterface.data()); |
| 342 | 364 | registerGlobalObject("Quat", &_quatLibrary); |
| 343 | 365 | registerGlobalObject("Vec3", &_vec3Library); |
| 344 | 366 | registerGlobalObject("Uuid", &_uuidLibrary); |
| 345 | 367 | registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCache>().data()); |
| 368 | + qScriptRegisterMetaType(this, animVarMapToScriptValue, animVarMapFromScriptValue); | |
| 369 | + qScriptRegisterMetaType(this, resultHandlerToScriptValue, resultHandlerFromScriptValue); | |
| 346 | 370 | |
| 347 | 371 | // constants |
| 348 | 372 | globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE))); |
| 349 | 373 | |
| 350 | − if (_controllerScriptingInterface) { | |
| 351 | − _controllerScriptingInterface->registerControllerTypes(this); | |
| 352 | − } | |
| 353 | − | |
| 354 | − | |
| 374 | + auto scriptingInterface = DependencyManager::get<controller::ScriptingInterface>(); | |
| 375 | + registerGlobalObject("Controller", scriptingInterface.data()); | |
| 376 | + UserInputMapper::registerControllerTypes(this); | |
| 355 | 377 | } |
| 356 | 378 | |
| 357 | 379 | void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) { |
| 358 | 380 | if (QThread::currentThread() != thread()) { |
| 359 | 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 | 383 | #endif |
| 362 | 384 | QMetaObject::invokeMethod(this, "registerValue", |
| 363 | 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 | 789 | void ScriptEngine::timerFired() { |
| 747 | 790 | QTimer* callingTimer = reinterpret_cast<QTimer*>(sender()); |
| 748 | 791 | QScriptValue timerFunction = _timerFunctionMap.value(callingTimer); |
| @@ -928,9 +971,8 @@ void ScriptEngine::forwardHandlerCall(const EntityItemID& entityID, const QStrin · 179 unmodified lines | ||
| 928 | 971 | if (QThread::currentThread() != thread()) { |
| 929 | 972 | qDebug() << "*** ERROR *** ScriptEngine::forwardHandlerCall() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]"; |
| 930 | 973 | assert(false); |
| 931 | − return; | |
| 974 | + return ; | |
| 932 | 975 | } |
| 933 | − | |
| 934 | 976 | if (!_registeredHandlers.contains(entityID)) { |
| 935 | 977 | return; |
| 936 | 978 | } |
| @@ -21,13 +21,14 @@ · 20 unmodified lines | ||
| 21 | 21 | #include <QtScript/QScriptEngine> |
| 22 | 22 | |
| 23 | 23 | #include <AnimationCache.h> |
| 24 | +#include <AnimVariant.h> | |
| 24 | 25 | #include <AvatarData.h> |
| 25 | 26 | #include <AvatarHashMap.h> |
| 26 | 27 | #include <LimitedNodeList.h> |
| 27 | 28 | #include <EntityItemID.h> |
| 28 | 29 | #include <EntitiesScriptEngineProvider.h> |
| 29 | 30 | |
| 30 | −#include "AbstractControllerScriptingInterface.h" | |
| 31 | +#include "MouseEvent.h" | |
| 31 | 32 | #include "ArrayBufferClass.h" |
| 32 | 33 | #include "AudioScriptingInterface.h" |
| 33 | 34 | #include "Quat.h" |
| @@ -53,7 +54,6 @@ class ScriptEngine : public QScriptEngine, public ScriptUser, public EntitiesScr · 19 unmodified lines | ||
| 53 | 54 | public: |
| 54 | 55 | ScriptEngine(const QString& scriptContents = NO_SCRIPT, |
| 55 | 56 | const QString& fileNameString = QString(""), |
| 56 | − AbstractControllerScriptingInterface* controllerScriptingInterface = NULL, | |
| 57 | 57 | bool wantSignals = true); |
| 58 | 58 | |
| 59 | 59 | ~ScriptEngine(); |
| @@ -142,6 +142,9 @@ public: · 82 unmodified lines | ||
| 142 | 142 | // NOTE - this is used by the TypedArray implemetation. we need to review this for thread safety |
| 143 | 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 | 148 | signals: |
| 146 | 149 | void scriptLoaded(const QString& scriptFilename); |
| 147 | 150 | void errorLoadingScript(const QString& scriptFilename); |
| @@ -181,8 +184,7 @@ private: · 33 unmodified lines | ||
| 181 | 184 | |
| 182 | 185 | QObject* setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot); |
| 183 | 186 | void stopTimer(QTimer* timer); |
| 184 | − | |
| 185 | − AbstractControllerScriptingInterface* _controllerScriptingInterface; | |
| 187 | + | |
| 186 | 188 | QString _fileNameString; |
| 187 | 189 | Quat _quatLibrary; |
| 188 | 190 | Vec3 _vec3Library; |
| @@ -23,6 +23,25 @@ · 22 unmodified lines | ||
| 23 | 23 | /// Scriptable interface a Vec3ernion helper class object. Used exclusively in the JavaScript API |
| 24 | 24 | class Vec3 : public QObject { |
| 25 | 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 | 46 | public slots: |
| 28 | 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 | 49 | float dot(const glm::vec3& v1, const glm::vec3& v2) { return glm::dot(v1, v2); } |
| 31 | 50 | glm::vec3 multiply(const glm::vec3& v1, float f) { return v1 * f; } |
| 32 | 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 | 53 | glm::vec3 multiplyQbyV(const glm::quat& q, const glm::vec3& v) { return q * v; } |
| 34 | 54 | glm::vec3 sum(const glm::vec3& v1, const glm::vec3& v2) { return v1 + v2; } |
| 35 | 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 | 65 | glm::vec3 toPolar(const glm::vec3& v); |
| 46 | 66 | glm::vec3 fromPolar(const glm::vec3& polar); |
| 47 | 67 | glm::vec3 fromPolar(float elevation, float azimuth); |
| 68 | + | |
| 69 | +private: | |
| 48 | 70 | const glm::vec3& UNIT_X() { return Vectors::UNIT_X; } |
| 49 | 71 | const glm::vec3& UNIT_Y() { return Vectors::UNIT_Y; } |
| 50 | 72 | const glm::vec3& UNIT_Z() { return Vectors::UNIT_Z; } |
| @@ -51,7 +51,10 @@ public: · 50 unmodified lines | ||
| 51 | 51 | |
| 52 | 52 | template<typename T, typename ...Args> |
| 53 | 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 | 58 | template<typename T> |
| 56 | 59 | static void destroy(); |
| 57 | 60 | |
| @@ -89,13 +92,26 @@ QSharedPointer<T> DependencyManager::get() { · 31 unmodified lines | ||
| 89 | 92 | template <typename T, typename ...Args> |
| 90 | 93 | QSharedPointer<T> DependencyManager::set(Args&&... args) { |
| 91 | 94 | static size_t hashCode = _manager.getHashCode<T>(); |
| 92 | − | |
| 95 | + | |
| 93 | 96 | QSharedPointer<Dependency>& instance = _manager.safeGet(hashCode); |
| 94 | 97 | instance.clear(); // Clear instance before creation of new one to avoid edge cases |
| 95 | 98 | QSharedPointer<T> newInstance(new T(args...), &T::customDeleter); |
| 96 | 99 | QSharedPointer<Dependency> storedInstance = qSharedPointerCast<Dependency>(newInstance); |
| 97 | 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 | 115 | return newInstance; |
| 100 | 116 | } |
| 101 | 117 | |
| @@ -15,6 +15,7 @@ · 14 unmodified lines | ||
| 15 | 15 | #include <cstring> |
| 16 | 16 | #include <cctype> |
| 17 | 17 | #include <time.h> |
| 18 | +#include <mutex> | |
| 18 | 19 | |
| 19 | 20 | #ifdef _WIN32 |
| 20 | 21 | #include <windows.h> |
| @@ -39,30 +40,29 @@ void usecTimestampNowForceClockSkew(int clockSkew) { · 18 unmodified lines | ||
| 39 | 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 | 47 | quint64 usecTimestampNow(bool wantDebug) { |
| 43 | − static bool usecTimestampNowIsInitialized = false; | |
| 44 | − static qint64 TIME_REFERENCE = 0; // in usec | |
| 45 | − static QElapsedTimer timestampTimer; | |
| 46 | − | |
| 47 | − if (!usecTimestampNowIsInitialized) { | |
| 48 | − TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * 1000; // ms to usec | |
| 48 | + std::call_once(usecTimestampNowIsInitialized, [&] { | |
| 49 | + TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * USECS_PER_MSEC; // ms to usec | |
| 49 | 50 | timestampTimer.start(); |
| 50 | − usecTimestampNowIsInitialized = true; | |
| 51 | − } | |
| 51 | + }); | |
| 52 | 52 | |
| 53 | 53 | quint64 now; |
| 54 | 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 | 57 | // QElapsedTimer may not advance if the CPU has gone to sleep. In which case it |
| 58 | 58 | // will begin to deviate from real time. We detect that here, and reset if necessary |
| 59 | 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 | 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 | 63 | if (abs(possibleSkew) > TOLERANCE) { |
| 64 | 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 | 66 | timestampTimer.restart(); |
| 67 | 67 | now = TIME_REFERENCE + ::usecTimestampNowAdjust; |
| 68 | 68 | |
| @@ -118,6 +118,13 @@ quint64 usecTimestampNow(bool wantDebug) { · 49 unmodified lines | ||
| 118 | 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 | 128 | float randFloat() { |
| 122 | 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 | 65 | // Use a custom User-Agent to avoid ModSecurity filtering, e.g. by hosting providers. |
| 66 | 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 | 69 | quint64 usecTimestampNow(bool wantDebug = false); |
| 69 | 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 | 76 | float randFloat(); |
| 72 | 77 | int randIntInRange (int min, int max); |
| 73 | 78 | float randFloatInRange (float min,float max); |
| @@ -40,4 +40,28 @@ private: · 39 unmodified lines | ||
| 40 | 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 | 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 | 113 | Q_ARG(QVariant, QVariant::fromValue(qmlParent)), |
| 114 | 114 | Q_ARG(QVariant, QVariant::fromValue(menu->title()))); |
| 115 | 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 | 117 | QObject* result = returnedValue.value<QObject*>(); |
| 117 | 118 | Q_ASSERT(result); |
| 118 | 119 | |
| @@ -153,6 +154,7 @@ void VrMenu::addAction(QMenu* menu, QAction* action) { · 34 unmodified lines | ||
| 153 | 154 | Q_ARG(QVariant, QVariant::fromValue(menuQml)), |
| 154 | 155 | Q_ARG(QVariant, QVariant::fromValue(action->text()))); |
| 155 | 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 | 158 | QObject* result = returnedValue.value<QObject*>(); |
| 157 | 159 | Q_ASSERT(result); |
| 158 | 160 | // Bind the QML and Widget together |
| @@ -174,6 +176,7 @@ void VrMenu::insertAction(QAction* before, QAction* action) { · 15 unmodified lines | ||
| 174 | 176 | Q_ARG(QVariant, QVariant::fromValue(beforeQml)), |
| 175 | 177 | Q_ARG(QVariant, QVariant::fromValue(action->text()))); |
| 176 | 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 | 180 | QObject* result = returnedValue.value<QObject*>(); |
| 178 | 181 | Q_ASSERT(result); |
| 179 | 182 | bindActionToQmlAction(result, action); |
| @@ -192,4 +195,5 @@ void VrMenu::removeAction(QAction* action) { · 12 unmodified lines | ||
| 192 | 195 | Q_ARG(QVariant, QVariant::fromValue(menu)), |
| 193 | 196 | Q_ARG(QVariant, QVariant::fromValue(item))); |
| 194 | 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 | + | |