History

Merge branch 'master' of https://github.com/highfidelity/hifi into dont-use-other-avatar-sounds

Howard Stearns committed 11 years ago

Commit 28246c12 parents ce1d0fe ec92c4e214 files · +9042 −4228Browse files

Unified · Split

214 files
  1. CMakeLists.txt +0 −14
  2. assignment-client/CMakeLists.txt +2 −2
  3. cmake/externals/faceshift/CMakeLists.txt +30 −0
  4. cmake/externals/sdl2/CMakeLists.txt +9 −21
  5. cmake/externals/sixense/CMakeLists.txt +11 −25
  6. cmake/macros/AddDependencyExternalProjects.cmake +1 −1
  7. cmake/macros/TargetFaceshift.cmake +14 −0
  8. cmake/macros/TargetSDL2.cmake +14 −0
  9. cmake/macros/TargetSixense.cmake +14 −0
  10. cmake/modules/FindFaceshift.cmake +0 −23
  11. cmake/modules/FindSixense.cmake +1 −44
  12. cmake/modules/FindiViewHMD.cmake +5 −33
  13. examples/clap.js +2 −2
  14. examples/controllers/controllerMappings.js +101 −0
  15. examples/controllers/handControllerGrab.js +30 −13
  16. examples/controllers/handPosesDebug.js +102 −0
  17. examples/controllers/hydra/airGuitar.js +107 −69
  18. examples/controllers/hydra/drumStick.js +3 −2
  19. examples/controllers/hydra/frisbee.js +19 −15
  20. examples/controllers/hydra/gun.js +336 −304
  21. examples/controllers/hydra/hydraMove.js +0 −303
  22. examples/controllers/hydra/paddleBall.js +14 −25
  23. examples/controllers/hydra/toyball.js +19 −43
  24. examples/example/avatarcontrol/controllerExample.js +9 −38
  25. examples/example/games/hydraGrabHockey.js +3 −5
  26. examples/example/games/sword.js +3 −3
  27. examples/example/painting/hydraPaint.js +8 −11
  28. examples/example/scripts/controllerScriptingExamples.js +47 −12
  29. examples/libraries/omniTool.js +33 −34
  30. examples/libraries/utils.js +1 −7
  31. examples/libraries/walkApi.js +2 −18
  32. examples/mouseLook.js +1 −1
  33. examples/stick.js +5 −13
  34. examples/tests/controllerInterfaceTest.js +77 −0
  35. examples/toybox/ping_pong_gun/pingPongGun.js +9 −8
  36. examples/toybox/spray_paint/sprayPaintCan.js +8 −7
  37. interface/CMakeLists.txt +12 −5
  38. interface/external/faceshift/readme.txt +0 −26
  39. interface/external/sdl2/readme.txt +0 −13
  40. interface/external/sixense/readme.txt +0 −10
  41. interface/resources/controllers/hydra.json +31 −0
  42. interface/resources/controllers/keyboardMouse.json +86 −0
  43. interface/resources/controllers/mapping-config.json +24 −0
  44. interface/resources/controllers/mapping-test0.json +36 −0
  45. interface/resources/controllers/standard-old.json +43 −0
  46. interface/resources/controllers/standard.json +36 −0
  47. interface/resources/controllers/vive.json +26 −0
  48. interface/resources/controllers/xbox.json +29 −0
  49. interface/resources/qml/ScrollingGraph.qml +111 −0
  50. interface/resources/qml/TestControllers.qml +161 −0
  51. interface/resources/qml/controller/AnalogButton.qml +45 −0
  52. interface/resources/qml/controller/AnalogStick.qml +55 −0
  53. interface/resources/qml/controller/Hydra.qml +34 −0
  54. interface/resources/qml/controller/Standard.qml +35 −0
  55. interface/resources/qml/controller/ToggleButton.qml +45 −0
  56. interface/resources/qml/controller/Xbox.qml +104 −0
  57. interface/resources/qml/controller/hydra/HydraButtons.qml +18 −0
  58. interface/resources/qml/controller/hydra/HydraStick.qml +91 −0
  59. interface/resources/qml/controller/hydra/hydra.png +0 −0
  60. interface/resources/qml/controller/xbox/DPad.qml +42 −0
  61. interface/resources/qml/controller/xbox/LeftAnalogStick.qml +21 −0
  62. interface/resources/qml/controller/xbox/RightAnalogStick.qml +21 −0
  63. interface/resources/qml/controller/xbox/XboxButtons.qml +46 −0
  64. interface/resources/qml/controller/xbox/xbox360-controller-md.png +0 −0
  65. interface/src/Application.cpp +232 −204
  66. interface/src/Application.h +26 −17
  67. interface/src/Menu.cpp +2 −0
  68. interface/src/avatar/Avatar.cpp +0 −32
  69. interface/src/avatar/Avatar.h +2 −19
  70. interface/src/avatar/AvatarManager.cpp +10 −0
  71. interface/src/avatar/AvatarManager.h +4 −0
  72. interface/src/avatar/Hand.cpp +5 −11
  73. interface/src/avatar/MyAvatar.cpp +111 −86
  74. interface/src/avatar/MyAvatar.h +53 −2
  75. interface/src/avatar/SkeletonModel.cpp +24 −42
  76. interface/src/avatar/SkeletonModel.h +1 −1
  77. interface/src/devices/3DConnexionClient.cpp +80 −94
  78. interface/src/devices/3DConnexionClient.h +17 −34
  79. interface/src/scripting/ControllerScriptingInterface.cpp +47 −442
  80. interface/src/scripting/ControllerScriptingInterface.h +49 −89
  81. interface/src/ui/ApplicationCompositor.cpp +10 −8
  82. interface/src/ui/PreferencesDialog.cpp +2 −2
  83. libraries/animation/src/AnimVariant.cpp +121 −0
  84. libraries/animation/src/AnimVariant.h +17 −2
  85. libraries/animation/src/Rig.cpp +67 −0
  86. libraries/animation/src/Rig.h +17 −0
  87. libraries/audio/src/Sound.cpp +17 −26
  88. libraries/avatars/src/HandData.cpp +18 −37
  89. libraries/avatars/src/HandData.h +47 −52
  90. libraries/controllers/CMakeLists.txt +14 −0
  91. libraries/controllers/src/controllers/Actions.cpp +120 −0
  92. libraries/controllers/src/controllers/Actions.h +103 −0
  93. libraries/controllers/src/controllers/DeviceProxy.cpp +14 −0
  94. libraries/controllers/src/controllers/DeviceProxy.h +47 −0
  95. libraries/controllers/src/controllers/Forward.h +37 −0
  96. libraries/controllers/src/controllers/Input.cpp +17 −0
  97. libraries/controllers/src/controllers/Input.h +74 −0
  98. libraries/controllers/src/controllers/InputDevice.cpp +114 −0
  99. libraries/controllers/src/controllers/InputDevice.h +46 −11
  100. libraries/controllers/src/controllers/Logging.cpp +11 −0
  101. libraries/controllers/src/controllers/Logging.h +16 −0
  102. libraries/controllers/src/controllers/Pose.cpp +49 −0
  103. libraries/controllers/src/controllers/Pose.h +50 −0
  104. libraries/controllers/src/controllers/ScriptingInterface.cpp +241 −0
  105. libraries/controllers/src/controllers/ScriptingInterface.h +155 −0
  106. libraries/controllers/src/controllers/StandardController.cpp +158 −0
  107. libraries/controllers/src/controllers/StandardController.h +40 −0
  108. libraries/controllers/src/controllers/StandardControls.h +85 −0
  109. libraries/controllers/src/controllers/StateController.cpp +50 −0
  110. libraries/controllers/src/controllers/StateController.h +51 −0
  111. libraries/controllers/src/controllers/UserInputMapper.cpp +1086 −0
  112. libraries/controllers/src/controllers/UserInputMapper.h +197 −0
  113. libraries/controllers/src/controllers/impl/Conditional.cpp +21 −0
  114. libraries/controllers/src/controllers/impl/Conditional.h +54 −0
  115. libraries/controllers/src/controllers/impl/Endpoint.cpp +14 −0
  116. libraries/controllers/src/controllers/impl/Endpoint.h +89 −0
  117. libraries/controllers/src/controllers/impl/Filter.cpp +196 −0
  118. libraries/controllers/src/controllers/impl/Filter.h +59 −0
  119. libraries/controllers/src/controllers/impl/Mapping.cpp +8 −0
  120. libraries/controllers/src/controllers/impl/Mapping.h +37 −0
  121. libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp +63 −0
  122. libraries/controllers/src/controllers/impl/MappingBuilderProxy.h +58 −0
  123. libraries/controllers/src/controllers/impl/Route.cpp +9 −0
  124. libraries/controllers/src/controllers/impl/Route.h +35 −0
  125. libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp +108 −0
  126. libraries/controllers/src/controllers/impl/RouteBuilderProxy.h +61 −0
  127. libraries/controllers/src/controllers/impl/conditionals/AndConditional.cpp +21 −0
  128. libraries/controllers/src/controllers/impl/conditionals/AndConditional.h +31 −0
  129. libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.cpp +9 −0
  130. libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h +27 −0
  131. libraries/controllers/src/controllers/impl/conditionals/NotConditional.cpp +9 −0
  132. libraries/controllers/src/controllers/impl/conditionals/NotConditional.h +16 −0
  133. libraries/controllers/src/controllers/impl/conditionals/ScriptConditional.cpp +27 −0
  134. libraries/controllers/src/controllers/impl/conditionals/ScriptConditional.h +34 −0
  135. libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp +40 −0
  136. libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.h +41 −0
  137. libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.cpp +63 −0
  138. libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.h +32 −0
  139. libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.cpp +9 −0
  140. libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.h +43 −0
  141. libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.cpp +36 −0
  142. libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.h +28 −0
  143. libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.cpp +41 −0
  144. libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.h +39 −0
  145. libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.cpp +9 −0
  146. libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.h +41 −0
  147. libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp +46 −0
  148. libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h +40 −0
  149. libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.cpp +9 −0
  150. libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.h +60 −0
  151. libraries/controllers/src/controllers/impl/filters/ClampFilter.cpp +38 −0
  152. libraries/controllers/src/controllers/impl/filters/ClampFilter.h +32 −0
  153. libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.cpp +9 −0
  154. libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.h +30 −0
  155. libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.cpp +9 −0
  156. libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.h +30 −0
  157. libraries/controllers/src/controllers/impl/filters/DeadZoneFilter.cpp +26 −0
  158. libraries/controllers/src/controllers/impl/filters/DeadZoneFilter.h +31 −0
  159. libraries/controllers/src/controllers/impl/filters/HysteresisFilter.cpp +64 −0
  160. libraries/controllers/src/controllers/impl/filters/HysteresisFilter.h +31 −0
  161. libraries/controllers/src/controllers/impl/filters/InvertFilter.cpp +9 −0
  162. libraries/controllers/src/controllers/impl/filters/InvertFilter.h +29 −0
  163. libraries/controllers/src/controllers/impl/filters/PulseFilter.cpp +37 −0
  164. libraries/controllers/src/controllers/impl/filters/PulseFilter.h +36 −0
  165. libraries/controllers/src/controllers/impl/filters/ScaleFilter.cpp +19 −0
  166. libraries/controllers/src/controllers/impl/filters/ScaleFilter.h +34 −0
  167. libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp +0 −3
  168. libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h +0 −2
  169. libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +2 −2
  170. libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +0 −1
  171. libraries/entities-renderer/src/EntityTreeRenderer.cpp +1 −2
  172. libraries/entities/src/EntityPropertyFlags.h +1 −1
  173. libraries/gl/src/gl/GlWindow.cpp +1 −2
  174. libraries/gl/src/gl/OffscreenQmlSurface.cpp +6 −0
  175. libraries/gl/src/gl/OffscreenQmlSurface.h +1 −0
  176. libraries/input-plugins/CMakeLists.txt +3 −53
  177. libraries/input-plugins/src/input-plugins/InputDevice.cpp +0 −59
  178. libraries/input-plugins/src/input-plugins/Joystick.cpp +59 −156
  179. libraries/input-plugins/src/input-plugins/Joystick.h +8 −23
  180. libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +130 −135
  181. libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h +17 −14
  182. libraries/input-plugins/src/input-plugins/SDL2Manager.cpp +41 −18
  183. libraries/input-plugins/src/input-plugins/SDL2Manager.h +9 −6
  184. libraries/input-plugins/src/input-plugins/SixenseManager.cpp +182 −155
  185. libraries/input-plugins/src/input-plugins/SixenseManager.h +20 −29
  186. libraries/input-plugins/src/input-plugins/StandardController.cpp +0 −160
  187. libraries/input-plugins/src/input-plugins/StandardController.h +0 −77
  188. libraries/input-plugins/src/input-plugins/UserInputMapper.cpp +0 −335
  189. libraries/input-plugins/src/input-plugins/UserInputMapper.h +0 −284
  190. libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp +145 −135
  191. libraries/input-plugins/src/input-plugins/ViveControllerManager.h +11 −33
  192. libraries/networking/src/AssetClient.cpp +0 −3
  193. libraries/networking/src/udt/PacketHeaders.cpp +1 −1
  194. libraries/networking/src/udt/PacketHeaders.h +1 −0
  195. libraries/physics/src/CharacterController.cpp +2 −2
  196. libraries/physics/src/PhysicsEngine.cpp +3 −2
  197. libraries/physics/src/ThreadSafeDynamicsWorld.cpp +3 −2
  198. libraries/physics/src/ThreadSafeDynamicsWorld.h +3 −2
  199. libraries/plugins/src/plugins/Plugin.h +12 −2
  200. libraries/script-engine/CMakeLists.txt +1 −1
  201. libraries/script-engine/src/AbstractControllerScriptingInterface.h +0 −125
  202. libraries/script-engine/src/AbstractScriptingServicesInterface.h +0 −8
  203. libraries/script-engine/src/ScriptEngine.cpp +59 −17
  204. libraries/script-engine/src/ScriptEngine.h +6 −4
  205. libraries/script-engine/src/Vec3.h +22 −0
  206. libraries/shared/src/DependencyManager.h +19 −3
  207. libraries/shared/src/SharedUtil.cpp +19 −12
  208. libraries/shared/src/SharedUtil.h +5 −0
  209. libraries/shared/src/SimpleMovingAverage.h +24 −0
  210. libraries/shared/src/shared/Factory.h +51 −0
  211. libraries/ui/src/VrMenu.cpp +4 −0
  212. tests/controllers/CMakeLists.txt +19 −0
  213. tests/controllers/qml/main.qml +22 −0
  214. tests/controllers/src/main.cpp +158 −0
CMakeLists.txt+0−14
@@ -185,22 +185,8 @@ set(EXTERNAL_PROJECT_PREFIX "project") · 184 unmodified lines
185set_property(DIRECTORY PROPERTY EP_PREFIX ${EXTERNAL_PROJECT_PREFIX})185set_property(DIRECTORY PROPERTY EP_PREFIX ${EXTERNAL_PROJECT_PREFIX})
186setup_externals_binary_dir()186setup_externals_binary_dir()
187 187
188# setup options to grab external project dependencies
189option(GET_BULLET "Get Bullet library automatically as external project" 1)
190option(GET_GLM "Get GLM library automatically as external project" 1)
191option(GET_GVERB "Get Gverb library automatically as external project" 1)
192option(GET_TBB "Get Threading Building Blocks library automatically as external project" 1)
193option(GET_LIBOVR "Get LibOVR library automatically as external project" 1)
194option(GET_VHACD "Get V-HACD library automatically as external project" 1)
195option(GET_POLYVOX "Get polyvox library automatically as external project" 1)
196option(GET_OPENVR "Get OpenVR library automatically as external project" 1)
197option(GET_BOOSTCONFIG "Get Boost-config library automatically as external project" 1)
198option(GET_OGLPLUS "Get OGLplus library automatically as external project" 1)
199option(GET_GLEW "Get GLEW library automatically as external project" 1)
200
201option(USE_NSIGHT "Attempt to find the nSight libraries" 1)188option(USE_NSIGHT "Attempt to find the nSight libraries" 1)
202 189
203option(GET_SDL2 "Get SDL2 library automatically as external project" 0)
204 190
205if (WIN32)191if (WIN32)
206 add_paths_to_fixup_libs("${QT_DIR}/bin")192 add_paths_to_fixup_libs("${QT_DIR}/bin")
assignment-client/CMakeLists.txt+2−2
@@ -1,12 +1,12 @@
1set(TARGET_NAME assignment-client)1set(TARGET_NAME assignment-client)
2 2
3setup_hifi_project(Core Gui Network Script Widgets WebSockets)3setup_hifi_project(Core Gui Network Script Quick Widgets WebSockets)
4 4
5# link in the shared libraries5# link in the shared libraries
6link_hifi_libraries( 6link_hifi_libraries(
7 audio avatars octree environment gpu model fbx entities 7 audio avatars octree environment gpu model fbx entities
8 networking animation shared script-engine embedded-webserver8 networking animation shared script-engine embedded-webserver
9 physics9 controllers physics
10)10)
11 11
12include_application_version()12include_application_version()
cmake/externals/faceshift/CMakeLists.txt+30−0
@@ -0,0 +1,30 @@
1set(EXTERNAL_NAME faceshift)
2
3include(ExternalProject)
4ExternalProject_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)
17set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
18
19ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
20
21string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
22set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE FILEPATH "Path to Faceshift include directory")
23
24if (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")
27elseif (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")
30endif()
cmake/externals/sdl2/CMakeLists.txt+9−21
@@ -47,25 +47,13 @@ if (APPLE) · 46 unmodified lines
47 ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)47 ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
48 set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${SOURCE_DIR}/SDL2.framework/Headers CACHE PATH "Location of SDL2 include directory")48 set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${SOURCE_DIR}/SDL2.framework/Headers CACHE PATH "Location of SDL2 include directory")
49 set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${SOURCE_DIR}/SDL2.framework/SDL2 CACHE STRING "Path to SDL2 library")49 set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${SOURCE_DIR}/SDL2.framework/SDL2 CACHE STRING "Path to SDL2 library")
50elseif (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")
50else ()55else ()
51 if (WIN32)56 ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
52 ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)57 set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIR ${INSTALL_DIR}/include/SDL2 CACHE PATH "Location of SDL2 include directory")
53 set(_ROOT_DIR ${SOURCE_DIR})58 set(${EXTERNAL_NAME_UPPER}_LIBRARY_TEMP ${INSTALL_DIR}/lib/libSDL2.so CACHE FILEPATH "Path to SDL2 library")
54 set(_INCLUDE_DIR ${_ROOT_DIR}/include)59endif ()
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")
71endif ()
cmake/externals/sixense/CMakeLists.txt+11−25
@@ -1,30 +1,20 @@
1include(ExternalProject)1include(ExternalProject)
2include(SelectLibraryConfigurations)
3 2
4set(EXTERNAL_NAME Sixense)3set(EXTERNAL_NAME sixense)
5 4
6string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)5string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
7 6
8ExternalProject_Add(7ExternalProject_Add(
9 ${EXTERNAL_NAME}8 ${EXTERNAL_NAME}
10 URL ./SixenseSDK_062612.zip9 URL http://hifi-public.s3.amazonaws.com/dependencies/SixenseSDK_071615.zip
11 URL_MD5 10cc8dc470d2ac1244a88cf04bc549cc10 URL_MD5 752a3901f334124e9cffc2ba4136ef7d
12 CONFIGURE_COMMAND ""11 CONFIGURE_COMMAND ""
13 BUILD_COMMAND ""12 BUILD_COMMAND ""
14 INSTALL_COMMAND ""13 INSTALL_COMMAND ""
15 LOG_DOWNLOAD 114 LOG_DOWNLOAD 1
16)15)
17 16
18if (APPLE)17set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
19 find_library(SIXENSE_LIBRARY_RELEASE lib/osx_x64/release_dll/libsixense_x64.dylib HINTS ${SIXENSE_SEARCH_DIRS})
20 find_library(SIXENSE_LIBRARY_DEBUG lib/osx_x64/debug_dll/libsixensed_x64.dylib HINTS ${SIXENSE_SEARCH_DIRS})
21elseif (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})
24elseif (WIN32)
25endif ()
26
27
28 18
29ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)19ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR)
30 20
@@ -39,22 +29,18 @@ if (WIN32) · 8 unmodified lines
39 set(ARCH_DIR "Win32")29 set(ARCH_DIR "Win32")
40 set(ARCH_SUFFIX "")30 set(ARCH_SUFFIX "")
41 endif()31 endif()
42 32
43 # FIXME need to account for different architectures 33 set(${EXTERNAL_NAME_UPPER}_LIBRARIES "${SOURCE_DIR}/lib/${ARCH_DIR}/VS2013/release_dll/sixense${ARCH_SUFFIX}.lib" CACHE TYPE INTERNAL)
44 set(${EXTERNAL_NAME_UPPER}_LIBRARIES "${SOURCE_DIR}/lib/${ARCH_DIR}/release_dll/sixense${ARCH_SUFFIX}.lib" CACHE TYPE INTERNAL)34 add_paths_to_fixup_libs("${SOURCE_DIR}/bin/${ARCH_DIR}/VS2013/release_dll")
45 add_paths_to_fixup_libs(${SOURCE_DIR}/bin/win32)
46 35
47elseif(APPLE)36elseif(APPLE)
48 37
49 # FIXME need to account for different architectures 38 set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/osx_x64/release_dll/libsixense_x64.dylib CACHE TYPE INTERNAL)
50 set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/osx_x64/release_dll/libsixense_x64.dylib CACHE TYPE INTERNAL)
51 add_paths_to_fixup_libs(${SOURCE_DIR}/bin/osx32)
52 39
53elseif(NOT ANDROID)40elseif(NOT ANDROID)
54 41
55 # FIXME need to account for different architectures 42 # FIXME need to account for different architectures
56 set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/linux_x64/release/libsixense_x64.so CACHE TYPE INTERNAL)43 set(${EXTERNAL_NAME_UPPER}_LIBRARIES ${SOURCE_DIR}/lib/linux_x64/release/libsixense_x64.so CACHE TYPE INTERNAL)
57 add_paths_to_fixup_libs(${SOURCE_DIR}/bin/linux32)44
58
59endif()45endif()
60 46
cmake/macros/AddDependencyExternalProjects.cmake+1−1
@@ -16,7 +16,7 @@ macro(ADD_DEPENDENCY_EXTERNAL_PROJECTS) · 15 unmodified lines
16 string(TOUPPER ${_PROJ_NAME} _PROJ_NAME_UPPER)16 string(TOUPPER ${_PROJ_NAME} _PROJ_NAME_UPPER)
17 17
18 # has the user told us they specific don't want this as an external project?18 # has the user told us they specific don't want this as an external project?
19 if (GET_${_PROJ_NAME_UPPER})19 if (NOT USE_LOCAL_${_PROJ_NAME_UPPER})
20 # have we already detected we can't have this as external project on this OS?20 # have we already detected we can't have this as external project on this OS?
21 if (NOT DEFINED ${_PROJ_NAME_UPPER}_EXTERNAL_PROJECT OR ${_PROJ_NAME_UPPER}_EXTERNAL_PROJECT)21 if (NOT DEFINED ${_PROJ_NAME_UPPER}_EXTERNAL_PROJECT OR ${_PROJ_NAME_UPPER}_EXTERNAL_PROJECT)
22 # have we already setup the target?22 # have we already setup the target?
cmake/macros/TargetFaceshift.cmake+14−0
@@ -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#
8macro(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)
14endmacro()
cmake/macros/TargetSDL2.cmake+14−0
@@ -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#
8macro(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)
14endmacro()
cmake/macros/TargetSixense.cmake+14−0
@@ -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#
8macro(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)
14endmacro()
cmake/modules/FindFaceshift.cmake+0−23
@@ -18,32 +18,9 @@ · 17 unmodified lines
18# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html18# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
19# 19#
20 20
21include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
22hifi_library_search_hints("faceshift")
23
24find_path(FACESHIFT_INCLUDE_DIRS fsbinarystream.h PATH_SUFFIXES include HINTS ${FACESHIFT_SEARCH_DIRS})
25
26if (APPLE)
27 set(ARCH_DIR "MacOS")
28elseif (UNIX)
29 set(ARCH_DIR "UNIX")
30elseif (WIN32)
31 if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
32 set(ARCH_DIR "x64")
33 else()
34 set(ARCH_DIR "Win32")
35 endif()
36endif ()
37
38find_library(FACESHIFT_LIBRARY_RELEASE NAME faceshift PATH_SUFFIXES lib/${ARCH_DIR} HINTS ${FACESHIFT_SEARCH_DIRS})
39find_library(FACESHIFT_LIBRARY_DEBUG NAME faceshiftd PATH_SUFFIXES lib/${ARCH_DIR} HINTS ${FACESHIFT_SEARCH_DIRS})
40
41include(SelectLibraryConfigurations)21include(SelectLibraryConfigurations)
42select_library_configurations(FACESHIFT)22select_library_configurations(FACESHIFT)
43 23
44set(FACESHIFT_LIBRARIES ${FACESHIFT_LIBRARY})
45
46include(FindPackageHandleStandardArgs)24include(FindPackageHandleStandardArgs)
47find_package_handle_standard_args(Faceshift DEFAULT_MSG FACESHIFT_INCLUDE_DIRS FACESHIFT_LIBRARIES)25find_package_handle_standard_args(Faceshift DEFAULT_MSG FACESHIFT_INCLUDE_DIRS FACESHIFT_LIBRARIES)
48
49mark_as_advanced(FACESHIFT_INCLUDE_DIRS FACESHIFT_LIBRARIES FACESHIFT_SEARCH_DIRS)26mark_as_advanced(FACESHIFT_INCLUDE_DIRS FACESHIFT_LIBRARIES FACESHIFT_SEARCH_DIRS)
cmake/modules/FindSixense.cmake+1−44
@@ -18,49 +18,6 @@ · 17 unmodified lines
18# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html18# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
19# 19#
20 20
21include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
22hifi_library_search_hints("sixense")
23
24find_path(SIXENSE_INCLUDE_DIRS sixense.h PATH_SUFFIXES include HINTS ${SIXENSE_SEARCH_DIRS})
25
26if (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})
29elseif (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})
32elseif (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})
47endif ()
48
49include(SelectLibraryConfigurations)
50select_library_configurations(SIXENSE)
51
52set(SIXENSE_REQUIREMENTS SIXENSE_INCLUDE_DIRS SIXENSE_LIBRARIES)
53if (WIN32)
54 list(APPEND SIXENSE_REQUIREMENTS SIXENSE_DEBUG_DLL_PATH SIXENSE_RELEASE_DLL_PATH SIXENSE_DEVICE_DLL_PATH)
55endif ()
56
57set(SIXENSE_LIBRARIES "${SIXENSE_LIBRARY}")
58
59include(FindPackageHandleStandardArgs)21include(FindPackageHandleStandardArgs)
60find_package_handle_standard_args(Sixense DEFAULT_MSG ${SIXENSE_REQUIREMENTS})22find_package_handle_standard_args(Sixense DEFAULT_MSG SIXENSE_INCLUDE_DIRS SIXENSE_LIBRARIES)
61
62if (WIN32)
63 add_paths_to_fixup_libs(${SIXENSE_DEBUG_DLL_PATH} ${SIXENSE_RELEASE_DLL_PATH} ${SIXENSE_DEVICE_DLL_PATH})
64endif ()
65
66mark_as_advanced(SIXENSE_LIBRARIES SIXENSE_INCLUDE_DIRS SIXENSE_SEARCH_DIRS)23mark_as_advanced(SIXENSE_LIBRARIES SIXENSE_INCLUDE_DIRS SIXENSE_SEARCH_DIRS)
cmake/modules/FindiViewHMD.cmake+5−33
@@ -21,45 +21,17 @@ if (WIN32) · 20 unmodified lines
21 hifi_library_search_hints("iViewHMD")21 hifi_library_search_hints("iViewHMD")
22 22
23 find_path(IVIEWHMD_INCLUDE_DIRS iViewHMDAPI.h PATH_SUFFIXES include HINTS ${IVIEWHMD_SEARCH_DIRS})23 find_path(IVIEWHMD_INCLUDE_DIRS iViewHMDAPI.h PATH_SUFFIXES include HINTS ${IVIEWHMD_SEARCH_DIRS})
24 find_library(IVIEWHMD_LIBRARIES NAMES iViewHMDAPI PATH_SUFFIXES libs HINTS ${IVIEWHMD_SEARCH_DIRS})24 find_library(IVIEWHMD_LIBRARIES NAMES iViewHMDAPI PATH_SUFFIXES libs/x86 HINTS ${IVIEWHMD_SEARCH_DIRS})
25 find_path(IVIEWHMD_API_DLL_PATH iViewHMDAPI.dll PATH_SUFFIXES libs HINTS ${IVIEWHMD_SEARCH_DIRS})25 find_path(IVIEWHMD_API_DLL_PATH iViewHMDAPI.dll PATH_SUFFIXES libs/x86 HINTS ${IVIEWHMD_SEARCH_DIRS})
26 list(APPEND IVIEWHMD_REQUIREMENTS IVIEWHMD_INCLUDE_DIRS IVIEWHMD_LIBRARIES IVIEWHMD_API_DLL_PATH)26 list(APPEND IVIEWHMD_REQUIREMENTS IVIEWHMD_INCLUDE_DIRS IVIEWHMD_LIBRARIES IVIEWHMD_API_DLL_PATH)
27 27
28 set(IVIEWHMD_DLLS28 find_path(IVIEWHMD_DLL_PATH_3RD_PARTY libiViewNG.dll PATH_SUFFIXES 3rdParty HINTS ${IVIEWHMD_SEARCH_DIRS})
29 avcodec-53.dll29 list(APPEND IVIEWHMD_REQUIREMENTS IVIEWHMD_DLL_PATH_3RD_PARTY)
30 avformat-53.dll
31 avutil-51.dll
32 libboost_filesystem-mgw45-mt-1_49.dll
33 libboost_system-mgw45-mt-1_49.dll
34 libboost_thread-mgw45-mt-1_49.dll
35 libgcc_s_dw2-1.dll
36 libiViewNG-LibCore.dll
37 libopencv_calib3d244.dll
38 libopencv_core244.dll
39 libopencv_features2d244.dll
40 libopencv_flann244.dll
41 libopencv_highgui244.dll
42 libopencv_imgproc244.dll
43 libopencv_legacy244.dll
44 libopencv_ml244.dll
45 libopencv_video244.dll
46 libstdc++-6.dll
47 opencv_core220.dll
48 opencv_highgui220.dll
49 opencv_imgproc220.dll
50 swscale-2.dll
51 )
52
53 foreach(IVIEWHMD_DLL ${IVIEWHMD_DLLS})
54 find_path(IVIEWHMD_DLL_PATH ${IVIEWHMD_DLL} PATH_SUFFIXES 3rdParty HINTS ${IVIEWHMD_SEARCH_DIRS})
55 list(APPEND IVIEWHMD_REQUIREMENTS IVIEWHMD_DLL_PATH)
56 list(APPEND IVIEWHMD_DLL_PATHS ${IVIEWHMD_DLL_PATH})
57 endforeach()
58 30
59 include(FindPackageHandleStandardArgs)31 include(FindPackageHandleStandardArgs)
60 find_package_handle_standard_args(IVIEWHMD DEFAULT_MSG ${IVIEWHMD_REQUIREMENTS})32 find_package_handle_standard_args(IVIEWHMD DEFAULT_MSG ${IVIEWHMD_REQUIREMENTS})
61 33
62 add_paths_to_fixup_libs(${IVIEWHMD_API_DLL_PATH} ${IVIEWHMD_DLL_PATHS})34 add_paths_to_fixup_libs(${IVIEWHMD_API_DLL_PATH} ${IVIEWHMD_DLL_PATH_3RD_PARTY})
63 35
64 mark_as_advanced(IVIEWHMD_INCLUDE_DIRS IVIEWHMD_LIBRARIES IVIEWHMD_SEARCH_DIRS)36 mark_as_advanced(IVIEWHMD_INCLUDE_DIRS IVIEWHMD_LIBRARIES IVIEWHMD_SEARCH_DIRS)
65 37
examples/clap.js+2−2
@@ -71,8 +71,8 @@ function maybePlaySound(deltaTime) { · 70 unmodified lines
71 var palm2Position = MyAvatar.getRightPalmPosition();71 var palm2Position = MyAvatar.getRightPalmPosition();
72 var distanceBetween = Vec3.length(Vec3.subtract(palm1Position, palm2Position));72 var distanceBetween = Vec3.length(Vec3.subtract(palm1Position, palm2Position));
73 73
74 var palm1Velocity = Controller.getSpatialControlVelocity(1);74 var palm1Velocity = Controller.getPoseValue(Controller.Standard.LeftHand).velocity;
75 var palm2Velocity = Controller.getSpatialControlVelocity(3);75 var palm2Velocity = Controller.getPoseValue(Controller.Standard.RightHand).velocity;
76 var closingVelocity = Vec3.length(Vec3.subtract(palm1Velocity, palm2Velocity));76 var closingVelocity = Vec3.length(Vec3.subtract(palm1Velocity, palm2Velocity));
77 77
78 const CLAP_SPEED = 0.7;78 const CLAP_SPEED = 0.7;
examples/controllers/controllerMappings.js+101−0
@@ -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() {
16return {
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*/
46mySecondMapping = function() {
47return {
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();
58print('myFirstMappingJSON' + JSON.stringify(myFirstMappingJSON));
59
60var mapping = Controller.parseMapping(JSON.stringify(myFirstMappingJSON));
61
62
63Controller.enableMapping("example3");
64
65var mySecondMappingJSON = mySecondMapping();
66print('mySecondMappingJSON' + JSON.stringify(mySecondMappingJSON));
67
68var mapping2 = Controller.parseMapping(JSON.stringify(mySecondMappingJSON));
69mapping2.enable();
70
71Controller.enableMapping("example2");
72*/
73var mapping3 = Controller.loadMapping(Script.resolvePath("example3.json"));
74Controller.enableMapping("example3");
75
76/*
77Object.keys(Controller.Standard).forEach(function (input) {
78 print("Controller.Standard." + input + ":" + Controller.Standard[input]);
79});
80
81Object.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
87Object.keys(Controller.Actions).forEach(function (actionName) {
88 print("Controller.Actions." + actionName + ":" + Controller.Actions[actionName]);
89});
90*/
91
92
93Controller.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});
examples/controllers/handControllerGrab.js+30−13
@@ -148,6 +148,7 @@ function MyController(hand, triggerAction) { · 147 unmodified lines
148 this.state = STATE_OFF;148 this.state = STATE_OFF;
149 this.pointer = null; // entity-id of line object149 this.pointer = null; // entity-id of line object
150 this.triggerValue = 0; // rolling average of trigger value150 this.triggerValue = 0; // rolling average of trigger value
151 this.rawTriggerValue = 0;
151 152
152 var _this = this;153 var _this = this;
153 154
@@ -244,12 +245,17 @@ function MyController(hand, triggerAction) { · 90 unmodified lines
244 this.pointer = null;245 this.pointer = null;
245 };246 };
246 247
247 this.updateSmoothedTrigger = function() {248 this.eitherTrigger = function (value) {
248 var triggerValue = Controller.getActionValue(this.triggerAction);249 _this.rawTriggerValue = value;
250 };
251
252 this.updateSmoothedTrigger = function () {
253 var triggerValue = this.rawTriggerValue;
249 // smooth out trigger value254 // smooth out trigger value
250 this.triggerValue = (this.triggerValue * TRIGGER_SMOOTH_RATIO) +255 this.triggerValue = (this.triggerValue * TRIGGER_SMOOTH_RATIO) +
251 (triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO));256 (triggerValue * (1.0 - TRIGGER_SMOOTH_RATIO));
252 }257
258 };
253 259
254 this.triggerSmoothedSqueezed = function() {260 this.triggerSmoothedSqueezed = function() {
255 return this.triggerValue > TRIGGER_ON_VALUE;261 return this.triggerValue > TRIGGER_ON_VALUE;
@@ -259,8 +265,8 @@ function MyController(hand, triggerAction) { · 3 unmodified lines
259 return this.triggerValue < TRIGGER_OFF_VALUE;265 return this.triggerValue < TRIGGER_OFF_VALUE;
260 };266 };
261 267
262 this.triggerSqueezed = function() {268 this.triggerSqueezed = function() {
263 var triggerValue = Controller.getActionValue(this.triggerAction);269 var triggerValue = this.rawTriggerValue;
264 return triggerValue > TRIGGER_ON_VALUE;270 return triggerValue > TRIGGER_ON_VALUE;
265 };271 };
266 272
@@ -402,8 +408,9 @@ function MyController(hand, triggerAction) { · 135 unmodified lines
402 408
403 this.distanceHolding = function() {409 this.distanceHolding = function() {
404 410
405 var handControllerPosition = Controller.getSpatialControlPosition(this.palm);411 var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition;
406 var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm));412 var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
413 var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation);
407 var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation",414 var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation",
408 "gravity", "ignoreForCollisions"]);415 "gravity", "ignoreForCollisions"]);
409 var now = Date.now();416 var now = Date.now();
@@ -452,8 +459,9 @@ function MyController(hand, triggerAction) { · 42 unmodified lines
452 }459 }
453 460
454 var handPosition = this.getHandPosition();461 var handPosition = this.getHandPosition();
455 var handControllerPosition = Controller.getSpatialControlPosition(this.palm);462 var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition;
456 var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(this.palm));463 var controllerHandInput = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
464 var handRotation = Quat.multiply(MyAvatar.orientation, Controller.getPoseValue(controllerHandInput).rotation);
457 var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation"]);465 var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["position", "rotation"]);
458 466
459 this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR);467 this.lineOn(handPosition, Vec3.subtract(grabbedProperties.position, handPosition), INTERSECT_COLOR);
@@ -595,7 +603,7 @@ function MyController(hand, triggerAction) { · 135 unmodified lines
595 603
596 }604 }
597 605
598 this.currentHandControllerTipPosition = Controller.getSpatialControlPosition(this.tip);606 this.currentHandControllerTipPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandTipPosition : MyAvatar.leftHandTipPosition;;
599 607
600 this.currentObjectTime = Date.now();608 this.currentObjectTime = Date.now();
601 };609 };
@@ -613,7 +621,7 @@ function MyController(hand, triggerAction) { · 11 unmodified lines
613 // of it's actual offset, let's try imparting a velocity which is at a fixed radius621 // of it's actual offset, let's try imparting a velocity which is at a fixed radius
614 // from the palm.622 // from the palm.
615 623
616 var handControllerPosition = Controller.getSpatialControlPosition(this.tip);624 var handControllerPosition = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition;
617 var now = Date.now();625 var now = Date.now();
618 626
619 var deltaPosition = Vec3.subtract(handControllerPosition, this.currentHandControllerTipPosition); // meters627 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
859var rightController = new MyController(RIGHT_HAND, Controller.findAction("RIGHT_HAND_CLICK"));867var rightController = new MyController(RIGHT_HAND, Controller.Standard.RT);
860var leftController = new MyController(LEFT_HAND, Controller.findAction("LEFT_HAND_CLICK"));868var leftController = new MyController(LEFT_HAND, Controller.Standard.LT);
869
870var MAPPING_NAME = "com.highfidelity.handControllerGrab";
871
872var mapping = Controller.newMapping(MAPPING_NAME);
873mapping.from([Controller.Standard.RB, Controller.Standard.RT]).to(rightController.eitherTrigger);
874mapping.from([Controller.Standard.LB, Controller.Standard.LT]).to(leftController.eitherTrigger);
875Controller.enableMapping(MAPPING_NAME);
876
861 877
862function update() {878function update() {
863 rightController.update();879 rightController.update();
@@ -867,6 +883,7 @@ function update() { · 3 unmodified lines
867function cleanup() {883function cleanup() {
868 rightController.cleanup();884 rightController.cleanup();
869 leftController.cleanup();885 leftController.cleanup();
886 Controller.disableMapping(MAPPING_NAME);
870}887}
871 888
872Script.scriptEnding.connect(cleanup);889Script.scriptEnding.connect(cleanup);
examples/controllers/handPosesDebug.js+102−0
@@ -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
13function 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
28var NUM_HANDS = 2;
29var NUM_SPHERES_PER_HAND = 2;
30var LEFT_HAND = 0;
31var RIGHT_HAND = 1;
32
33var COLORS = [ { red: 255, green: 0, blue: 0 }, { red: 0, green: 0, blue: 255 } ];
34
35
36function index(handNum, indexNum) {
37 return handNum * NUM_HANDS + indexNum;
38}
39
40var app = {};
41
42
43function 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
55function 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
88function update(deltaTime) {
89 updateHand(LEFT_HAND, deltaTime);
90 updateHand(RIGHT_HAND, deltaTime);
91}
92
93function 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
100setup();
101Script.update.connect(update);
102Script.scriptEnding.connect(scriptEnding);
examples/controllers/hydra/airGuitar.js+107−69
@@ -32,6 +32,8 @@ var guitarModel = HIFI_PUBLIC_BUCKET + "models/attachments/guitar.fst"; · 31 unmodified lines
32 32
33// Load sounds that will be played33// Load sounds that will be played
34 34
35var heyManWave = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/sounds/KenDoll_1%2303.wav");
36
35var chords = new Array();37var chords = new Array();
36// Nylon string guitar38// Nylon string guitar
37chords[1] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Nylon+A.raw");39chords[1] = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guitars/Guitar+-+Nylon+A.raw");
@@ -56,97 +58,128 @@ var NUM_GUITARS = 3; · 18 unmodified lines
56var guitarSelector = NUM_CHORDS;58var guitarSelector = NUM_CHORDS;
57var whichChord = 1;59var whichChord = 1;
58 60
59var leftHanded = true; 61var leftHanded = false;
62var strumHand, chordHand, strumTrigger, chordTrigger;
60if (leftHanded) {63if (leftHanded) {
61 var strumHand = 0;64 strumHand = Controller.Standard.LeftHand;
62 var chordHand = 1; 65 chordHand = Controller.Standard.RightHand;
66 strumTrigger = Controller.Standard.LT;
67 chordTrigger = Controller.Standard.RT;
68 changeGuitar = Controller.Standard.RB;
69 chord1 = Controller.Standard.X;
70 chord2 = Controller.Standard.Y;
71 chord3 = Controller.Standard.A;
72 chord4 = Controller.Standard.B;
63} else {73} else {
64 var strumHand = 1;74 strumHand = Controller.Standard.RightHand;
65 var chordHand = 0;75 chordHand = Controller.Standard.LeftHand;
76 strumTrigger = Controller.Standard.RT;
77 chordTrigger = Controller.Standard.LT;
78 changeGuitar = Controller.Standard.LB;
79 chord1 = Controller.Standard.DU; // these may not be correct, maybe we should map directly to Hydra??
80 chord2 = Controller.Standard.DD;
81 chord3 = Controller.Standard.DL;
82 chord4 = Controller.Standard.DR;
66}83}
67 84
68var lastPosition = { x: 0.0,85var lastPosition = { x: 0.0,
69 y: 0.0, 86 y: 0.0,
70 z: 0.0 }; 87 z: 0.0 };
71 88
72var soundPlaying = false; 89var audioInjector = null;
73var selectorPressed = false;90var selectorPressed = false;
74var position;91var position;
75 92
76MyAvatar.attach(guitarModel, "Hips", {x: -0.2, y: 0.0, z: 0.1}, Quat.fromPitchYawRollDegrees(90, 00, 90), 1.0);93MyAvatar.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
78function checkHands(deltaTime) {95function checkHands(deltaTime) {
79 for (var palm = 0; palm < 2; palm++) {96 var strumVelocity = Controller.getPoseValue(strumHand).velocity;
80 var palmVelocity = Controller.getSpatialControlVelocity(palm * 2 + 1);97 var volume = length(strumVelocity) / 5.0;
81 var volume = length(palmVelocity) / 5.0;
82 var position = Controller.getSpatialControlPosition(palm * 2 + 1);
83 var myPelvis = MyAvatar.position;
84 var trigger = Controller.getTriggerValue(strumHand);
85 var chord = Controller.getTriggerValue(chordHand);
86
87 if (volume > 1.0) volume = 1.0;
88 if ((chord > 0.1) && soundPlaying.isPlaying) {
89 // If chord finger trigger pulled, stop current chord
90 print("stopped sound");
91 soundPlaying.stop();
92 }
93 98
94 var BUTTON_COUNT = 6;99 if (volume == 0.0) {
95 100 volume = 1.0;
96 // Change guitars if button FWD (5) pressed101 }
97 if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 5)) {102
98 if (!selectorPressed) {103 var strumHandPosition = leftHanded ? MyAvatar.leftHandPosition : MyAvatar.rightHandPosition;
99 guitarSelector += NUM_CHORDS;104 var myPelvis = MyAvatar.position;
100 if (guitarSelector >= NUM_CHORDS * NUM_GUITARS) {105 var strumming = Controller.getValue(strumTrigger);
101 guitarSelector = 0;106 var chord = Controller.getValue(chordTrigger);
102 } 107
103 selectorPressed = true;108 if (volume > 1.0) volume = 1.0;
104 }109 if ((chord > 0.1) && audioInjector && audioInjector.isPlaying) {
105 } else {110 // If chord finger trigger pulled, stop current chord
106 selectorPressed = false;111 print("stopping chord because cord trigger pulled");
107 }112 audioInjector.stop();
113 }
108 114
109 if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 1)) {115 // Change guitars if button FWD (5) pressed
110 whichChord = 1;116 if (Controller.getValue(changeGuitar)) {
111 } else if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 2)) {117 if (!selectorPressed) {
112 whichChord = 2;118 print("changeGuitar:" + changeGuitar);
113 } else if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 3)) {119 guitarSelector += NUM_CHORDS;
114 whichChord = 3;120 if (guitarSelector >= NUM_CHORDS * NUM_GUITARS) {
115 } else if (Controller.isButtonPressed(chordHand * BUTTON_COUNT + 4)) {121 guitarSelector = 0;
116 whichChord = 4;122 }
123 print("new guitarBase: " + guitarSelector);
124 stopAudio(true);
125 selectorPressed = true;
117 }126 }
127 } else {
128 selectorPressed = false;
129 }
130
131 if (Controller.getValue(chord1)) {
132 whichChord = 1;
133 stopAudio(true);
134 } else if (Controller.getValue(chord2)) {
135 whichChord = 2;
136 stopAudio(true);
137 } else if (Controller.getValue(chord3)) {
138 whichChord = 3;
139 stopAudio(true);
140 } else if (Controller.getValue(chord4)) {
141 whichChord = 4;
142 stopAudio(true);
143 }
144
145 var STRUM_HEIGHT_ABOVE_PELVIS = 0.10;
146 var strummingHeight = myPelvis.y + STRUM_HEIGHT_ABOVE_PELVIS;
118 147
119 if (palm == strumHand) {148 var strumNowAbove = strumHandPosition.y > strummingHeight;
120 149 var strumNowBelow = strumHandPosition.y <= strummingHeight;
121 var STRUM_HEIGHT_ABOVE_PELVIS = 0.10;150 var strumWasAbove = lastPosition.y > strummingHeight;
122 var strumTriggerHeight = myPelvis.y + STRUM_HEIGHT_ABOVE_PELVIS;151 var strumWasBelow = lastPosition.y <= strummingHeight;
123 //printVector(position);152 var strumUp = strumNowAbove && strumWasBelow;
124 if ( ( ((position.y < strumTriggerHeight) && (lastPosition.y >= strumTriggerHeight)) ||153 var strumDown = strumNowBelow && strumWasAbove;
125 ((position.y > strumTriggerHeight) && (lastPosition.y <= strumTriggerHeight)) ) && (trigger > 0.1) ){154
126 // If hand passes downward or upward through 'strings', and finger trigger pulled, play155 if ((strumUp || strumDown) && (strumming > 0.1)) {
127 playChord(position, volume);156 // If hand passes downward or upward through 'strings', and finger trigger pulled, play
128 }157 playChord(strumHandPosition, volume);
129 lastPosition = Controller.getSpatialControlPosition(palm * 2 + 1);
130 }
131 }158 }
159 lastPosition = strumHandPosition;
132}160}
133 161
134function playChord(position, volume) {162function stopAudio(killInjector) {
135 if (soundPlaying.isPlaying) {163 if (audioInjector && audioInjector.isPlaying) {
136 print("stopped sound");164 print("stopped sound");
137 soundPlaying.stop();165 audioInjector.stop();
166 }
167 if (killInjector) {
168 audioInjector = null;
169 }
170}
171
172
173function 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
152function keyPressEvent(event) {185function keyPressEvent(event) {
@@ -154,15 +187,19 @@ function keyPressEvent(event) { · 1 unmodified line
154 keyVolume = 0.4;187 keyVolume = 0.4;
155 if (event.text == "1") {188 if (event.text == "1") {
156 whichChord = 1;189 whichChord = 1;
190 stopAudio(true);
157 playChord(MyAvatar.position, keyVolume);191 playChord(MyAvatar.position, keyVolume);
158 } else if (event.text == "2") {192 } else if (event.text == "2") {
159 whichChord = 2;193 whichChord = 2;
194 stopAudio(true);
160 playChord(MyAvatar.position, keyVolume);195 playChord(MyAvatar.position, keyVolume);
161 } else if (event.text == "3") {196 } else if (event.text == "3") {
162 whichChord = 3;197 whichChord = 3;
198 stopAudio(true);
163 playChord(MyAvatar.position, keyVolume);199 playChord(MyAvatar.position, keyVolume);
164 } else if (event.text == "4") {200 } else if (event.text == "4") {
165 whichChord = 4;201 whichChord = 4;
202 stopAudio(true);
166 playChord(MyAvatar.position, keyVolume);203 playChord(MyAvatar.position, keyVolume);
167 }204 }
168}205}
@@ -170,6 +207,7 @@ function keyPressEvent(event) { · 1 unmodified line
170function scriptEnding() {207function scriptEnding() {
171 MyAvatar.detachOne(guitarModel);208 MyAvatar.detachOne(guitarModel);
172}209}
210
173// Connect a call back that happens every frame211// Connect a call back that happens every frame
174Script.update.connect(checkHands);212Script.update.connect(checkHands);
175Script.scriptEnding.connect(scriptEnding);213Script.scriptEnding.connect(scriptEnding);
examples/controllers/hydra/drumStick.js+3−2
@@ -43,7 +43,8 @@ strokeSpeed[1] = 0.0; · 42 unmodified lines
43 43
44function checkSticks(deltaTime) {44function checkSticks(deltaTime) {
45 for (var palm = 0; palm < 2; palm++) {45 for (var palm = 0; palm < 2; palm++) {
46 var palmVelocity = Controller.getSpatialControlVelocity(palm * 2 + 1);46 var handPose = (palm == 0) ? MyAvatar.leftHandPose : MyAvatar.rightHandPose;
47 var palmVelocity = handPose.velocity;
47 var speed = length(palmVelocity);48 var speed = length(palmVelocity);
48 49
49 const TRIGGER_SPEED = 0.30; // Lower this value to let you 'drum' more gently50 const TRIGGER_SPEED = 0.30; // Lower this value to let you 'drum' more gently
@@ -64,7 +65,7 @@ function checkSticks(deltaTime) { · 14 unmodified lines
64 if ((palmVelocity.y > 0.0) || (speed < STOP_SPEED)) {65 if ((palmVelocity.y > 0.0) || (speed < STOP_SPEED)) {
65 state[palm] = 0;66 state[palm] = 0;
66 67
67 var options = { position: Controller.getSpatialControlPosition(palm * 2 + 1) };68 var options = { position: handPose.translation };
68 69
69 if (strokeSpeed[palm] > 1.0) { strokeSpeed[palm] = 1.0; }70 if (strokeSpeed[palm] > 1.0) { strokeSpeed[palm] = 1.0; }
70 options.volume = strokeSpeed[palm];71 options.volume = strokeSpeed[palm];
examples/controllers/hydra/frisbee.js+19−15
@@ -130,29 +130,38 @@ function Hand(name, palm, tip, forwardButton, button3, trigger) { · 129 unmodified lines
130 this.trigger = trigger;130 this.trigger = trigger;
131 this.holdingFrisbee = false;131 this.holdingFrisbee = false;
132 this.entity = false;132 this.entity = false;
133 this.palmPosition = function() { return Controller.getSpatialControlPosition(this.palm); }133 this.palmPosition = function () {
134 return this.palm == LEFT_PALM ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation;
135 };
136
134 this.grabButtonPressed = function() { 137 this.grabButtonPressed = function() {
135 return (138 return (
136 Controller.isButtonPressed(this.forwardButton) ||139 Controller.getValue(this.forwardButton) ||
137 Controller.isButtonPressed(this.button3) ||140 Controller.getValue(this.button3) ||
138 Controller.getTriggerValue(this.trigger) > 0.5141 Controller.getValue(this.trigger) > 0.5
139 )142 )
140 };143 };
141 this.holdPosition = function() { return this.palm == LEFT_PALM ? MyAvatar.getLeftPalmPosition() : MyAvatar.getRightPalmPosition(); };144 this.holdPosition = function () {
145 return this.palm == LEFT_PALM ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation;
146 };
147
142 this.holdRotation = function() {148 this.holdRotation = function() {
143 var q = Controller.getSpatialControlRawRotation(this.palm);149 var q = (this.palm == LEFT_PALM) ? Controller.getPoseValue(Controller.Standard.leftHand).rotation
150 : Controller.getPoseValue(Controller.Standard.rightHand).rotation;
144 q = Quat.multiply(MyAvatar.orientation, q);151 q = Quat.multiply(MyAvatar.orientation, q);
145 return {x: q.x, y: q.y, z: q.z, w: q.w};152 return {x: q.x, y: q.y, z: q.z, w: q.w};
146 };153 };
147 this.tipVelocity = function() { return Controller.getSpatialControlVelocity(this.tip); };154 this.tipVelocity = function () {
155 return this.tip == LEFT_TIP ? MyAvatar.leftHandTipPose.velocity : MyAvatar.rightHandTipPose.velocity;
156 };
148}157}
149 158
150function MouseControl(button) {159function MouseControl(button) {
151 this.button = button;160 this.button = button;
152}161}
153 162
154var leftHand = new Hand("LEFT", LEFT_PALM, LEFT_TIP, LEFT_BUTTON_FWD, LEFT_BUTTON_3, 0);163var leftHand = new Hand("LEFT", LEFT_PALM, LEFT_TIP, Controller.Standard.LB, Controller.Standard.LeftPrimaryThumb, Controller.Standard.LT);
155var rightHand = new Hand("RIGHT", RIGHT_PALM, RIGHT_TIP, RIGHT_BUTTON_FWD, RIGHT_BUTTON_3, 1);164var rightHand = new Hand("RIGHT", RIGHT_PALM, RIGHT_TIP, Controller.Standard.RB, Controller.Standard.RightPrimaryThumb, Controller.Standard.RT);
156 165
157var leftMouseControl = new MouseControl("LEFT");166var leftMouseControl = new MouseControl("LEFT");
158var middleMouseControl = new MouseControl("MIDDLE");167var middleMouseControl = new MouseControl("MIDDLE");
@@ -302,12 +311,7 @@ function initToolBar() { · 143 unmodified lines
302}311}
303 312
304function hydraCheck() {313function hydraCheck() {
305 var numberOfButtons = Controller.getNumberOfButtons();314 return Controller.Hardware.Hydra !== undefined;
306 var numberOfTriggers = Controller.getNumberOfTriggers();
307 var numberOfSpatialControls = Controller.getNumberOfSpatialControls();
308 var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers;
309 hydrasConnected = (numberOfButtons == 12 && numberOfTriggers == 2 && controllersPerTrigger == 2);
310 return true;//hydrasConnected;
311}315}
312 316
313function checkController(deltaTime) {317function checkController(deltaTime) {
examples/controllers/hydra/gun.js+336−304
@@ -15,24 +15,64 @@ · 14 unmodified lines
15// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html15// 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
21Script.include("../../libraries/utils.js");
22Script.include("../../libraries/constants.js");
23Script.include("../../libraries/toolBars.js");
24
18HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";25HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
19 26
20var RED = { red: 255, green: 0, blue: 0 };
21var LASER_WIDTH = 2;27var LASER_WIDTH = 2;
22 28var POSE_CONTROLS = [ Controller.Standard.LeftHand, Controller.Standard.RightHand ];
29var TRIGGER_CONTROLS = [ Controller.Standard.LT, Controller.Standard.RT ];
30var MIN_THROWER_DELAY = 1000;
31var MAX_THROWER_DELAY = 1000;
32var RELOAD_INTERVAL = 5;
33var GUN_MODEL = HIFI_PUBLIC_BUCKET + "cozza13/gun/m1911-handgun+1.fbx?v=4";
34var BULLET_VELOCITY = 10.0;
35var 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
45var GUN_ORIENTATIONS = [ Quat.fromPitchYawRollDegrees(0, 90, 90), Quat.fromPitchYawRollDegrees(0, -90, 270) ];
46
47var 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
57var mapping = Controller.newMapping();
58var validPoses = [ false, false ];
59var barrelVectors = [ 0, 0 ];
60var barrelTips = [ 0, 0 ];
23var pointer = [];61var pointer = [];
62
24pointer.push(Overlays.addOverlay("line3d", {63pointer.push(Overlays.addOverlay("line3d", {
25 start: { x: 0, y: 0, z: 0 },64 start: ZERO_VECTOR,
26 end: { x: 0, y: 0, z: 0 },65 end: ZERO_VECTOR,
27 color: RED,66 color: COLORS.RED,
28 alpha: 1,67 alpha: 1,
29 visible: true,68 visible: true,
30 lineWidth: LASER_WIDTH69 lineWidth: LASER_WIDTH
31}));70}));
71
32pointer.push(Overlays.addOverlay("line3d", {72pointer.push(Overlays.addOverlay("line3d", {
33 start: { x: 0, y: 0, z: 0 },73 start: ZERO_VECTOR,
34 end: { x: 0, y: 0, z: 0 },74 end: ZERO_VECTOR,
35 color: RED,75 color: COLORS.RED,
36 alpha: 1,76 alpha: 1,
37 visible: true,77 visible: true,
38 lineWidth: LASER_WIDTH78 lineWidth: LASER_WIDTH
@@ -42,135 +82,116 @@ function getRandomFloat(min, max) { · 3 unmodified lines
42 return Math.random() * (max - min) + min;82 return Math.random() * (max - min) + min;
43}83}
44 84
45var lastX = 0;
46var lastY = 0;
47var yawFromMouse = 0;
48var pitchFromMouse = 0;
49var isMouseDown = false;
50
51var MIN_THROWER_DELAY = 1000;
52var MAX_THROWER_DELAY = 1000;
53var LEFT_BUTTON_3 = 3;
54var RELOAD_INTERVAL = 5;
55
56var KICKBACK_ANGLE = 15;
57var elbowKickAngle = 0.0;
58var rotationBeforeKickback;
59
60var showScore = false;85var showScore = false;
61 86// Load some sound to use for loading and firing
62
63// Load some sound to use for loading and firing
64var fireSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/GUN-SHOT2.raw");87var fireSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/GUN-SHOT2.raw");
65var loadSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/Gun_Reload_Weapon22.raw");88var loadSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/Gun_Reload_Weapon22.raw");
66var impactSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/BulletImpact2.raw");89var impactSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/BulletImpact2.raw");
67var targetHitSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/hit.raw");90var targetHitSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/hit.raw");
68var targetLaunchSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/shoot.raw");91var targetLaunchSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/shoot.raw");
69 92
70var gunModel = "https://s3.amazonaws.com/hifi-public/cozza13/gun/m1911-handgun+1.fbx?v=4";
71
72var audioOptions = {93var audioOptions = {
73 volume: 0.994 volume: 0.9
74}95}
75 96
76var shotsFired = 0;97var shotsFired = 0;
77var shotTime = new Date(); 98var shotTime = new Date();
78 99var isLaunchButtonPressed = false;
79var activeControllers = 0; 100var score = 0;
80
81// initialize our controller triggers
82var triggerPulled = new Array();
83var numberOfTriggers = Controller.getNumberOfTriggers();
84for (t = 0; t < numberOfTriggers; t++) {
85 triggerPulled[t] = false;
86}
87
88var isLaunchButtonPressed = false;
89var score = 0;
90 101
91var bulletID = false;102var bulletID = false;
92var targetID = false;103var targetID = false;
93 104
94// Create overlay buttons and reticle 105// Create overlay buttons and reticle
95
96var BUTTON_SIZE = 32;106var BUTTON_SIZE = 32;
97var PADDING = 3;107var PADDING = 3;
98var NUM_BUTTONS = 3;108var NUM_BUTTONS = 3;
99
100var screenSize = Controller.getViewportDimensions();109var screenSize = Controller.getViewportDimensions();
101var startX = screenSize.x / 2 - (NUM_BUTTONS * (BUTTON_SIZE + PADDING)) / 2;110var startX = screenSize.x / 2 - (NUM_BUTTONS * (BUTTON_SIZE + PADDING)) / 2;
102Script.include(["../../libraries/toolBars.js"]);111var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL, "highfidelity.gun.toolbar", function(screenSize) {
103var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL, "highfidelity.gun.toolbar", function (screenSize) {
104 return {112 return {
105 x: startX,113 x: startX,
106 y: (screenSize.y - (BUTTON_SIZE + PADDING)),114 y: (screenSize.y - (BUTTON_SIZE + PADDING)),
107 };115 };
108});116});
109var 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
118var offButton = toolBar.addOverlay("image", {118var offButton = toolBar.addOverlay("image", {
119 width: BUTTON_SIZE,119 width: BUTTON_SIZE,
120 height: BUTTON_SIZE,120 height: BUTTON_SIZE,
121 imageURL: HIFI_PUBLIC_BUCKET + "images/gun/close.svg",121 imageURL: HIFI_PUBLIC_BUCKET + "images/gun/close.svg",
122 alpha: 1122 alpha: 1
123 });123});
124 124
125startX += BUTTON_SIZE + PADDING;125startX += BUTTON_SIZE + PADDING;
126var platformButton = toolBar.addOverlay("image", {126var platformButton = toolBar.addOverlay("image", {
127 x: startX,127 x: startX,
128 y: screenSize.y - (BUTTON_SIZE + PADDING),128 y: screenSize.y - (BUTTON_SIZE + PADDING),
129 width: BUTTON_SIZE,129 width: BUTTON_SIZE,
130 height: BUTTON_SIZE,130 height: BUTTON_SIZE,
131 imageURL: HIFI_PUBLIC_BUCKET + "images/gun/platform-targets.svg",131 imageURL: HIFI_PUBLIC_BUCKET + "images/gun/platform-targets.svg",
132 alpha: 1132 alpha: 1
133 });133});
134 134
135startX += BUTTON_SIZE + PADDING;135startX += BUTTON_SIZE + PADDING;
136var gridButton = toolBar.addOverlay("image", {136var gridButton = toolBar.addOverlay("image", {
137 x: startX,137 x: startX,
138 y: screenSize.y - (BUTTON_SIZE + PADDING),138 y: screenSize.y - (BUTTON_SIZE + PADDING),
139 width: BUTTON_SIZE,139 width: BUTTON_SIZE,
140 height: BUTTON_SIZE,140 height: BUTTON_SIZE,
141 imageURL: HIFI_PUBLIC_BUCKET + "images/gun/floating-targets.svg",141 imageURL: HIFI_PUBLIC_BUCKET + "images/gun/floating-targets.svg",
142 alpha: 1142 alpha: 1
143 });143});
144 144
145if (showScore) {145if (showScore) {
146 var text = Overlays.addOverlay("text", {146 var text = Overlays.addOverlay("text", {
147 x: screenSize.x / 2 - 100,147 x: screenSize.x / 2 - 100,
148 y: screenSize.y / 2 - 50,148 y: screenSize.y / 2 - 50,
149 width: 150,149 width: 150,
150 height: 50,150 height: 50,
151 color: { red: 0, green: 0, blue: 0},151 color: {
152 textColor: { red: 255, green: 0, blue: 0},152 red: 0,
153 topMargin: 4,153 green: 0,
154 leftMargin: 4,154 blue: 0
155 text: "Score: " + score155 },
156 });156 textColor: {
157 red: 255,
158 green: 0,
159 blue: 0
160 },
161 topMargin: 4,
162 leftMargin: 4,
163 text: "Score: " + score
164 });
157}165}
158 166
159var BULLET_VELOCITY = 10.0;
160
161function entityCollisionWithEntity(entity1, entity2, collision) {167function entityCollisionWithEntity(entity1, entity2, collision) {
162 if (entity2 === targetID) {168 if (entity2 === targetID) {
163 score++;169 score++;
164 if (showScore) {170 if (showScore) {
165 Overlays.editOverlay(text, { text: "Score: " + score } );171 Overlays.editOverlay(text, {
172 text: "Score: " + score
173 });
166 }174 }
167 175
168 // We will delete the bullet and target in 1/2 sec, but for now we can see them bounce!176 // We will delete the bullet and target in 1/2 sec, but for now we can
177 // see them bounce!
169 Script.setTimeout(deleteBulletAndTarget, 500);178 Script.setTimeout(deleteBulletAndTarget, 500);
170 179
171 // Turn the target and the bullet white180 // Turn the target and the bullet white
172 Entities.editEntity(entity1, { color: { red: 255, green: 255, blue: 255 }});181 Entities.editEntity(entity1, {
173 Entities.editEntity(entity2, { color: { red: 255, green: 255, blue: 255 }});182 color: {
183 red: 255,
184 green: 255,
185 blue: 255
186 }
187 });
188 Entities.editEntity(entity2, {
189 color: {
190 red: 255,
191 green: 255,
192 blue: 255
193 }
194 });
174 195
175 // play the sound near the camera so the shooter can hear it196 // play the sound near the camera so the shooter can hear it
176 audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation()));197 audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation()));
@@ -188,41 +209,45 @@ function shootBullet(position, velocity, grenade) { · 11 unmodified lines
188 209
189 var bVelocity = grenade ? Vec3.multiply(GRENADE_VELOCITY, Vec3.normalize(velocity)) : velocity;210 var bVelocity = grenade ? Vec3.multiply(GRENADE_VELOCITY, Vec3.normalize(velocity)) : velocity;
190 var bSize = grenade ? GRENADE_SIZE : BULLET_SIZE;211 var bSize = grenade ? GRENADE_SIZE : BULLET_SIZE;
191 var bGravity = grenade ? GRENADE_GRAVITY : BULLET_GRAVITY; 212 var bGravity = grenade ? GRENADE_GRAVITY : BULLET_GRAVITY;
192 213
193 bulletID = Entities.addEntity(214 bulletID = Entities.addEntity({
194 { type: "Sphere",215 type: "Sphere",
195 position: position, 216 position: position,
196 dimensions: { x: bSize, y: bSize, z: bSize }, 217 dimensions: {
197 color: { red: 0, green: 0, blue: 0 }, 218 x: bSize,
198 velocity: bVelocity, 219 y: bSize,
199 lifetime: BULLET_LIFETIME,220 z: bSize
200 gravity: { x: 0, y: bGravity, z: 0 },221 },
201 damping: 0.01,222 color: {
202 density: 8000,223 red: 0,
203 ignoreCollisions: false,224 green: 0,
204 collisionsWillMove: true225 blue: 0
205 });226 },
227 velocity: bVelocity,
228 lifetime: BULLET_LIFETIME,
229 gravity: {
230 x: 0,
231 y: bGravity,
232 z: 0
233 },
234 damping: 0.01,
235 density: 8000,
236 ignoreCollisions: false,
237 collisionsWillMove: true
238 });
239
206 Script.addEventHandler(bulletID, "collisionWithEntity", entityCollisionWithEntity);240 Script.addEventHandler(bulletID, "collisionWithEntity", entityCollisionWithEntity);
207 241
208 // Play firing sounds 242 // Play firing sounds
209 audioOptions.position = position; 243 audioOptions.position = position;
210 Audio.playSound(fireSound, audioOptions);244 Audio.playSound(fireSound, audioOptions);
211 shotsFired++;245 shotsFired++;
212 if ((shotsFired % RELOAD_INTERVAL) == 0) {246 if ((shotsFired % RELOAD_INTERVAL) == 0) {
213 Audio.playSound(loadSound, audioOptions);247 Audio.playSound(loadSound, audioOptions);
214 }248 }
215
216 // Kickback the arm
217 if (elbowKickAngle > 0.0) {
218 MyAvatar.setJointData("LeftForeArm", rotationBeforeKickback);
219 }
220 rotationBeforeKickback = MyAvatar.getJointRotation("LeftForeArm");
221 var armRotation = MyAvatar.getJointRotation("LeftForeArm");
222 armRotation = Quat.multiply(armRotation, Quat.fromPitchYawRollDegrees(0.0, 0.0, KICKBACK_ANGLE));
223 MyAvatar.setJointData("LeftForeArm", armRotation);
224 elbowKickAngle = KICKBACK_ANGLE;
225}249}
250
226function shootTarget() {251function shootTarget() {
227 var TARGET_SIZE = 0.50;252 var TARGET_SIZE = 0.50;
228 var TARGET_GRAVITY = 0.0;253 var TARGET_GRAVITY = 0.0;
@@ -232,95 +257,152 @@ function shootTarget() { · 3 unmodified lines
232 var DISTANCE_TO_LAUNCH_FROM = 5.0;257 var DISTANCE_TO_LAUNCH_FROM = 5.0;
233 var ANGLE_RANGE_FOR_LAUNCH = 20.0;258 var ANGLE_RANGE_FOR_LAUNCH = 20.0;
234 var camera = Camera.getPosition();259 var camera = Camera.getPosition();
235 260
236 var targetDirection = Quat.angleAxis(getRandomFloat(-ANGLE_RANGE_FOR_LAUNCH, ANGLE_RANGE_FOR_LAUNCH), { x:0, y:1, z:0 });261 var targetDirection = Quat.angleAxis(getRandomFloat(-ANGLE_RANGE_FOR_LAUNCH, ANGLE_RANGE_FOR_LAUNCH), {
262 x: 0,
263 y: 1,
264 z: 0
265 });
237 targetDirection = Quat.multiply(Camera.getOrientation(), targetDirection);266 targetDirection = Quat.multiply(Camera.getOrientation(), targetDirection);
238 var forwardVector = Quat.getFront(targetDirection);267 var forwardVector = Quat.getFront(targetDirection);
239 268
240 var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_TO_LAUNCH_FROM));269 var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_TO_LAUNCH_FROM));
241 270
242 var velocity = Vec3.multiply(forwardVector, TARGET_FWD_VELOCITY);271 var velocity = Vec3.multiply(forwardVector, TARGET_FWD_VELOCITY);
243 velocity.y += TARGET_UP_VELOCITY;272 velocity.y += TARGET_UP_VELOCITY;
244 273
245 targetID = Entities.addEntity(274 targetID = Entities.addEntity({
246 { type: "Box",275 type: "Box",
247 position: newPosition, 276 position: newPosition,
248 dimensions: { x: TARGET_SIZE * (0.5 + Math.random()), y: TARGET_SIZE * (0.5 + Math.random()), z: TARGET_SIZE * (0.5 + Math.random()) / 4.0 }, 277 dimensions: {
249 color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 }, 278 x: TARGET_SIZE * (0.5 + Math.random()),
250 velocity: velocity, 279 y: TARGET_SIZE * (0.5 + Math.random()),
251 gravity: { x: 0, y: TARGET_GRAVITY, z: 0 }, 280 z: TARGET_SIZE * (0.5 + Math.random()) / 4.0
252 lifetime: TARGET_LIFETIME,281 },
253 rotation: Camera.getOrientation(),282 color: {
254 damping: 0.1,283 red: Math.random() * 255,
255 density: 100.0, 284 green: Math.random() * 255,
256 collisionsWillMove: true });285 blue: Math.random() * 255
257 286 },
258 // Record start time 287 velocity: velocity,
288 gravity: {
289 x: 0,
290 y: TARGET_GRAVITY,
291 z: 0
292 },
293 lifetime: TARGET_LIFETIME,
294 rotation: Camera.getOrientation(),
295 damping: 0.1,
296 density: 100.0,
297 collisionsWillMove: true
298 });
299
300 // Record start time
259 shotTime = new Date();301 shotTime = new Date();
260 302
261 // Play target shoot sound303 // Play target shoot sound
262 audioOptions.position = newPosition; 304 audioOptions.position = newPosition;
263 Audio.playSound(targetLaunchSound, audioOptions);305 Audio.playSound(targetLaunchSound, audioOptions);
264}306}
265 307
266function makeGrid(type, scale, size) {308function makeGrid(type, scale, size) {
267 var separation = scale * 2; 309 var separation = scale * 2;
268 var pos = Vec3.sum(Camera.getPosition(), Vec3.multiply(10.0 * scale * separation, Quat.getFront(Camera.getOrientation())));310 var pos = Vec3.sum(Camera.getPosition(), Vec3.multiply(10.0 * scale * separation, Quat.getFront(Camera.getOrientation())));
269 var x, y, z;311 var x, y, z;
270 var GRID_LIFE = 60.0; 312 var GRID_LIFE = 60.0;
271 var dimensions; 313 var dimensions;
272 314
273 for (x = 0; x < size; x++) {315 for (x = 0; x < size; x++) {
274 for (y = 0; y < size; y++) {316 for (y = 0; y < size; y++) {
275 for (z = 0; z < size; z++) {317 for (z = 0; z < size; z++) {
276 318
277 dimensions = { x: separation/2.0 * (0.5 + Math.random()), y: separation/2.0 * (0.5 + Math.random()), z: separation/2.0 * (0.5 + Math.random()) / 4.0 };319 dimensions = {
278 320 x: separation / 2.0 * (0.5 + Math.random()),
279 Entities.addEntity(321 y: separation / 2.0 * (0.5 + Math.random()),
280 { type: type,322 z: separation / 2.0 * (0.5 + Math.random()) / 4.0
281 position: { x: pos.x + x * separation, y: pos.y + y * separation, z: pos.z + z * separation },323 };
282 dimensions: dimensions, 324
283 color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 }, 325 Entities.addEntity({
284 velocity: { x: 0, y: 0, z: 0 }, 326 type: type,
285 gravity: { x: 0, y: 0, z: 0 }, 327 position: {
286 lifetime: GRID_LIFE,328 x: pos.x + x * separation,
287 rotation: Camera.getOrientation(),329 y: pos.y + y * separation,
288 damping: 0.1,330 z: pos.z + z * separation
289 density: 100.0, 331 },
290 collisionsWillMove: true });332 dimensions: dimensions,
333 color: {
334 red: Math.random() * 255,
335 green: Math.random() * 255,
336 blue: Math.random() * 255
337 },
338 velocity: {
339 x: 0,
340 y: 0,
341 z: 0
342 },
343 gravity: {
344 x: 0,
345 y: 0,
346 z: 0
347 },
348 lifetime: GRID_LIFE,
349 rotation: Camera.getOrientation(),
350 damping: 0.1,
351 density: 100.0,
352 collisionsWillMove: true
353 });
291 }354 }
292 }355 }
293 }356 }
294}357}
295 358
296function makePlatform(gravity, scale, size) {359function makePlatform(gravity, scale, size) {
297 var separation = scale * 2; 360 var separation = scale * 2;
298 var pos = Vec3.sum(Camera.getPosition(), Vec3.multiply(10.0 * scale * separation, Quat.getFront(Camera.getOrientation())));361 var pos = Vec3.sum(Camera.getPosition(), Vec3.multiply(10.0 * scale * separation, Quat.getFront(Camera.getOrientation())));
299 pos.y -= separation * size;362 pos.y -= separation * size;
300 var x, y, z;363 var x, y, z;
301 var TARGET_LIFE = 60.0; 364 var TARGET_LIFE = 60.0;
302 var INITIAL_GAP = 0.5;365 var INITIAL_GAP = 0.5;
303 var dimensions; 366 var dimensions;
304 367
305 for (x = 0; x < size; x++) {368 for (x = 0; x < size; x++) {
306 for (y = 0; y < size; y++) {369 for (y = 0; y < size; y++) {
307 for (z = 0; z < size; z++) {370 for (z = 0; z < size; z++) {
308 371
309 dimensions = { x: separation/2.0, y: separation, z: separation/2.0 };372 dimensions = {
310 373 x: separation / 2.0,
311 Entities.addEntity(374 y: separation,
312 { type: "Box",375 z: separation / 2.0
313 position: { x: pos.x - (separation * size / 2.0) + x * separation, 376 };
314 y: pos.y + y * (separation + INITIAL_GAP), 377
315 z: pos.z - (separation * size / 2.0) + z * separation },378 Entities.addEntity({
316 dimensions: dimensions, 379 type: "Box",
317 color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 }, 380 position: {
318 velocity: { x: 0, y: 0.05, z: 0 }, 381 x: pos.x - (separation * size / 2.0) + x * separation,
319 gravity: { x: 0, y: gravity, z: 0 }, 382 y: pos.y + y * (separation + INITIAL_GAP),
320 lifetime: TARGET_LIFE,383 z: pos.z - (separation * size / 2.0) + z * separation
321 damping: 0.1,384 },
322 density: 100.0, 385 dimensions: dimensions,
323 collisionsWillMove: true });386 color: {
387 red: Math.random() * 255,
388 green: Math.random() * 255,
389 blue: Math.random() * 255
390 },
391 velocity: {
392 x: 0,
393 y: 0.05,
394 z: 0
395 },
396 gravity: {
397 x: 0,
398 y: gravity,
399 z: 0
400 },
401 lifetime: TARGET_LIFE,
402 damping: 0.1,
403 density: 100.0,
404 collisionsWillMove: true
405 });
324 }406 }
325 }407 }
326 }408 }
@@ -328,9 +410,21 @@ function makePlatform(gravity, scale, size) { · 1 unmodified line
328 // Make a floor for this stuff to fall onto410 // Make a floor for this stuff to fall onto
329 Entities.addEntity({411 Entities.addEntity({
330 type: "Box",412 type: "Box",
331 position: { x: pos.x, y: pos.y - separation / 2.0, z: pos.z }, 413 position: {
332 dimensions: { x: 2.0 * separation * size, y: separation / 2.0, z: 2.0 * separation * size },414 x: pos.x,
333 color: { red: 100, green: 100, blue: 100 },415 y: pos.y - separation / 2.0,
416 z: pos.z
417 },
418 dimensions: {
419 x: 2.0 * separation * size,
420 y: separation / 2.0,
421 z: 2.0 * separation * size
422 },
423 color: {
424 red: 100,
425 green: 100,
426 blue: 100
427 },
334 lifetime: TARGET_LIFE428 lifetime: TARGET_LIFE
335 });429 });
336 430
@@ -340,153 +434,79 @@ function keyPressEvent(event) { · 3 unmodified lines
340 // if our tools are off, then don't do anything434 // if our tools are off, then don't do anything
341 if (event.text == "t") {435 if (event.text == "t") {
342 var time = MIN_THROWER_DELAY + Math.random() * MAX_THROWER_DELAY;436 var time = MIN_THROWER_DELAY + Math.random() * MAX_THROWER_DELAY;
343 Script.setTimeout(shootTarget, time); 437 Script.setTimeout(shootTarget, time);
344 } else if ((event.text == ".") || (event.text == "SPACE")) {438 } else if ((event.text == ".") || (event.text == "SPACE")) {
345 shootFromMouse(false);439 shootFromMouse(false);
346 } else if (event.text == ",") {440 } else if (event.text == ",") {
347 shootFromMouse(true);441 shootFromMouse(true);
348 } else if (event.text == "r") {442 } else if (event.text == "r") {
349 playLoadSound();443 playLoadSound();
350 } else if (event.text == "s") {
351 // Hit this key to dump a posture from hydra to log
352 Quat.print("arm = ", MyAvatar.getJointRotation("LeftArm"));
353 Quat.print("forearm = ", MyAvatar.getJointRotation("LeftForeArm"));
354 Quat.print("hand = ", MyAvatar.getJointRotation("LeftHand"));
355 }444 }
356}445}
357 446
358function playLoadSound() {447function playLoadSound() {
359 audioOptions.position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); 448 audioOptions.position = MyAvatar.leftHandPose.translation;
360 Audio.playSound(loadSound, audioOptions);449 Audio.playSound(loadSound, audioOptions);
361 // Raise arm to firing posture
362 takeFiringPose();
363}
364
365function clearPose() {
366 MyAvatar.clearJointData("LeftForeArm");
367 MyAvatar.clearJointData("LeftArm");
368 MyAvatar.clearJointData("LeftHand");
369}450}
370 451
371function deleteBulletAndTarget() {452function deleteBulletAndTarget() {
372 Entities.deleteEntity(bulletID);453 Entities.deleteEntity(bulletID);
373 Entities.deleteEntity(targetID);454 Entities.deleteEntity(targetID);
374 bulletID = false; 455 bulletID = false;
375 targetID = false; 456 targetID = false;
376}
377
378function takeFiringPose() {
379 clearPose();
380 if (Controller.getNumberOfSpatialControls() == 0) {
381 MyAvatar.setJointData("LeftForeArm", {x: -0.251919, y: -0.0415449, z: 0.499487, w: 0.827843});
382 MyAvatar.setJointData("LeftArm", { x: 0.470196, y: -0.132559, z: 0.494033, w: 0.719219});
383 MyAvatar.setJointData("LeftHand", { x: -0.0104815, y: -0.110551, z: -0.352111, w: 0.929333});
384 }
385}457}
386 458
387MyAvatar.attach(gunModel, "RightHand", {x:0.04, y: 0.22, z: 0.02}, Quat.fromPitchYawRollDegrees(-172, -85, 79), 0.40);
388MyAvatar.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
391Script.setTimeout(playLoadSound, 2000);
392
393function update(deltaTime) {459function update(deltaTime) {
394 if (activeControllers == 0) {460 // FIXME we should also expose MyAvatar.handPoses[2], MyAvatar.tipPoses[2]
395 if (Controller.getNumberOfSpatialControls() > 0) { 461 var tipPoses = [ MyAvatar.leftHandTipPose, MyAvatar.rightHandTipPose ];
396 activeControllers = Controller.getNumberOfSpatialControls();462
397 clearPose();463 for (var side = 0; side < 2; side++) {
398 }464 // First check if the controller is valid
399 }465 var controllerPose = Controller.getPoseValue(POSE_CONTROLS[side]);
400 466 validPoses[side] = controllerPose.valid;
401 var KICKBACK_DECAY_RATE = 0.125;467 if (!controllerPose.valid) {
402 if (elbowKickAngle > 0.0) { 468 Overlays.editOverlay(pointer[side], {
403 if (elbowKickAngle > 0.5) {469 visible: false
404 var newAngle = elbowKickAngle * KICKBACK_DECAY_RATE;
405 elbowKickAngle -= newAngle;
406 var armRotation = MyAvatar.getJointRotation("LeftForeArm");
407 armRotation = Quat.multiply(armRotation, Quat.fromPitchYawRollDegrees(0.0, 0.0, -newAngle));
408 MyAvatar.setJointData("LeftForeArm", armRotation);
409 } else {
410 MyAvatar.setJointData("LeftForeArm", rotationBeforeKickback);
411 if (Controller.getNumberOfSpatialControls() > 0) {
412 clearPose();
413 }
414 elbowKickAngle = 0.0;
415 }
416 }
417
418
419 // check for trigger press
420
421 var numberOfTriggers = 2;
422 var controllersPerTrigger = 2;
423
424 if (numberOfTriggers == 2 && controllersPerTrigger == 2) {
425 for (var t = 0; t < 2; t++) {
426 var shootABullet = false;
427 var triggerValue = Controller.getTriggerValue(t);
428 if (triggerPulled[t]) {
429 // must release to at least 0.1
430 if (triggerValue < 0.1) {
431 triggerPulled[t] = false; // unpulled
432 }
433 } else {
434 // must pull to at least
435 if (triggerValue > 0.5) {
436 triggerPulled[t] = true; // pulled
437 shootABullet = true;
438 }
439 }
440 var palmController = t * controllersPerTrigger;
441 var palmPosition = Controller.getSpatialControlPosition(palmController);
442 var fingerTipController = palmController + 1;
443 var fingerTipPosition = Controller.getSpatialControlPosition(fingerTipController);
444 var laserTip = Vec3.sum(Vec3.multiply(100.0, Vec3.subtract(fingerTipPosition, palmPosition)), palmPosition);
445
446 // Update Lasers
447 Overlays.editOverlay(pointer[t], {
448 start: palmPosition,
449 end: laserTip,
450 alpha: 1
451 });470 });
452 471 continue;
453 if (shootABullet) {
454
455 var palmToFingerTipVector =
456 { x: (fingerTipPosition.x - palmPosition.x),
457 y: (fingerTipPosition.y - palmPosition.y),
458 z: (fingerTipPosition.z - palmPosition.z) };
459
460 // just off the front of the finger tip
461 var position = { x: fingerTipPosition.x + palmToFingerTipVector.x/2,
462 y: fingerTipPosition.y + palmToFingerTipVector.y/2,
463 z: fingerTipPosition.z + palmToFingerTipVector.z/2};
464
465 var velocity = Vec3.multiply(BULLET_VELOCITY, Vec3.normalize(palmToFingerTipVector));
466
467 shootBullet(position, velocity, false);
468 }
469 }472 }
470 }
471}
472 473
473function shootFromMouse(grenade) {474 // Need to adjust the laser
474 var DISTANCE_FROM_CAMERA = 1.0;475 var tipPose = tipPoses[side];
475 var camera = Camera.getPosition();476 var handRotation = tipPoses[side].rotation;
476 var forwardVector = Quat.getFront(Camera.getOrientation());477 var barrelOffset = Vec3.multiplyQbyV(handRotation, BARREL_OFFSETS[side]);
477 var newPosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_FROM_CAMERA));478 barrelTips[side] = Vec3.sum(tipPose.translation, barrelOffset);
478 var velocity = Vec3.multiply(forwardVector, BULLET_VELOCITY);479 barrelVectors[side] = Vec3.multiplyQbyV(handRotation, {
479 shootBullet(newPosition, velocity, grenade);480 x: 0,
481 y: 1,
482 z: 0
483 });
484
485 var laserTip = Vec3.sum(Vec3.multiply(100.0, barrelVectors[side]), barrelTips[side]);
486 // Update Lasers
487 Overlays.editOverlay(pointer[side], {
488 start: barrelTips[side],
489 end: laserTip,
490 alpha: 1,
491 visible: true
492 });
493 }
480}494}
481 495
482function mouseReleaseEvent(event) { 496function triggerChanged(side, value) {
483 // position 497 var pressed = (value != 0);
484 isMouseDown = false;498 if (pressed) {
499 var position = barrelTips[side];
500 var velocity = Vec3.multiply(BULLET_VELOCITY, Vec3.normalize(barrelVectors[side]));
501 shootBullet(position, velocity, false);
502 }
485}503}
486 504
487function mousePressEvent(event) {505function mousePressEvent(event) {
488 var clickedText = false;506 var clickedOverlay = Overlays.getOverlayAtPoint({
489 var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});507 x: event.x,
508 y: event.y
509 });
490 if (clickedOverlay == offButton) {510 if (clickedOverlay == offButton) {
491 Script.stop();511 Script.stop();
492 } else if (clickedOverlay == platformButton) {512 } else if (clickedOverlay == platformButton) {
@@ -494,25 +514,37 @@ function mousePressEvent(event) { · 1 unmodified line
494 makePlatform(-9.8, 1.0, platformSize);514 makePlatform(-9.8, 1.0, platformSize);
495 } else if (clickedOverlay == gridButton) {515 } else if (clickedOverlay == gridButton) {
496 makeGrid("Box", 1.0, 3);516 makeGrid("Box", 1.0, 3);
497 } 517 }
498}518}
499 519
500function scriptEnding() {520function scriptEnding() {
501 Overlays.deleteOverlay(reticle);521 mapping.disable();
502 toolBar.cleanup();522 toolBar.cleanup();
503 Overlays.deleteOverlay(pointer[0]);523 for (var i = 0; i < pointer.length; ++i) {
504 Overlays.deleteOverlay(pointer[1]);524 Overlays.deleteOverlay(pointer[i]);
525 }
505 Overlays.deleteOverlay(text);526 Overlays.deleteOverlay(text);
506 MyAvatar.detachOne(gunModel);527 MyAvatar.detachOne(GUN_MODEL);
507 MyAvatar.detachOne(gunModel);528 MyAvatar.detachOne(GUN_MODEL);
508 clearPose();529 clearPose();
509}530}
510 531
532MyAvatar.attach(GUN_MODEL, "LeftHand", GUN_OFFSETS[0], GUN_ORIENTATIONS[0], 0.40);
533MyAvatar.attach(GUN_MODEL, "RightHand", GUN_OFFSETS[1], GUN_ORIENTATIONS[1], 0.40);
534
535// Give a bit of time to load before playing sound
536Script.setTimeout(playLoadSound, 2000);
537
538mapping.from(Controller.Standard.LT).hysteresis(0.1, 0.5).to(function(value) {
539 triggerChanged(0, value);
540});
541
542mapping.from(Controller.Standard.RT).hysteresis(0.1, 0.5).to(function(value) {
543 triggerChanged(1, value);
544});
545mapping.enable();
546
511Script.scriptEnding.connect(scriptEnding);547Script.scriptEnding.connect(scriptEnding);
512Script.update.connect(update);548Script.update.connect(update);
513Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
514Controller.mousePressEvent.connect(mousePressEvent);549Controller.mousePressEvent.connect(mousePressEvent);
515Controller.keyPressEvent.connect(keyPressEvent);550Controller.keyPressEvent.connect(keyPressEvent);
516
517
518
examples/controllers/hydra/hydraMove.js+0−303
@@ -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
21var damping = 0.9;
22var position = { x: MyAvatar.position.x, y: MyAvatar.position.y, z: MyAvatar.position.z };
23var joysticksCaptured = false;
24var THRUST_CONTROLLER = 0;
25var VIEW_CONTROLLER = 1;
26var INITIAL_THRUST_MULTIPLIER = 1.0;
27var THRUST_INCREASE_RATE = 1.05;
28var MAX_THRUST_MULTIPLIER = 75.0;
29var thrustMultiplier = INITIAL_THRUST_MULTIPLIER;
30var grabDelta = { x: 0, y: 0, z: 0};
31var grabStartPosition = { x: 0, y: 0, z: 0};
32var grabDeltaVelocity = { x: 0, y: 0, z: 0};
33var grabStartRotation = { x: 0, y: 0, z: 0, w: 1};
34var grabCurrentRotation = { x: 0, y: 0, z: 0, w: 1};
35var grabbingWithRightHand = false;
36var wasGrabbingWithRightHand = false;
37var grabbingWithLeftHand = false;
38var wasGrabbingWithLeftHand = false;
39
40var EPSILON = 0.000001;
41var velocity = { x: 0, y: 0, z: 0};
42var THRUST_MAG_UP = 100.0;
43var THRUST_MAG_DOWN = 100.0;
44var THRUST_MAG_FWD = 150.0;
45var THRUST_MAG_BACK = 100.0;
46var THRUST_MAG_LATERAL = 150.0;
47var THRUST_JUMP = 120.0;
48
49var YAW_MAG = 100.0;
50var PITCH_MAG = 100.0;
51var THRUST_MAG_HAND_JETS = THRUST_MAG_FWD;
52var JOYSTICK_YAW_MAG = YAW_MAG;
53var JOYSTICK_PITCH_MAG = PITCH_MAG * 0.5;
54
55
56var LEFT_PALM = 0;
57var LEFT_BUTTON_4 = 4;
58var LEFT_BUTTON_FWD = 5;
59var RIGHT_PALM = 2;
60var RIGHT_BUTTON_4 = 10;
61var RIGHT_BUTTON_FWD = 11;
62
63
64
65function printVector(text, v, decimals) {
66 print(text + " " + v.x.toFixed(decimals) + ", " + v.y.toFixed(decimals) + ", " + v.z.toFixed(decimals));
67}
68
69var debug = false;
70var RED_COLOR = { red: 255, green: 0, blue: 0 };
71var GRAY_COLOR = { red: 25, green: 25, blue: 25 };
72var defaultPosition = { x: 0, y: 0, z: 0};
73var RADIUS = 0.05;
74var greenSphere = -1;
75var redSphere = -1;
76
77function 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
101function destroyDebugOverlay() {
102 if (greenSphere != -1) {
103 Overlays.deleteOverlay(greenSphere);
104 Overlays.deleteOverlay(redSphere);
105 greenSphere = -1;
106 redSphere = -1;
107 }
108}
109
110function 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
128function 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
138function 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
147function getGrabRotation() {
148 var quatDiff = Quat.multiply(grabCurrentRotation, Quat.inverse(grabStartRotation));
149 return quatDiff;
150}
151
152// When move button is pressed, process results
153function 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
248var THRUST_DEAD_ZONE = 0.1;
249var ROTATE_DEAD_ZONE = 0.1;
250function 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
291Script.update.connect(flyWithHydra);
292Controller.captureJoystick(THRUST_CONTROLLER);
293Controller.captureJoystick(VIEW_CONTROLLER);
294
295// Map keyPress and mouse move events to our callbacks
296function scriptEnding() {
297 // re-enabled the standard application for touch events
298 Controller.releaseJoystick(THRUST_CONTROLLER);
299 Controller.releaseJoystick(VIEW_CONTROLLER);
300}
301Script.scriptEnding.connect(scriptEnding);
302
303
examples/controllers/hydra/paddleBall.js+14−25
@@ -20,10 +20,11 @@ var BALL_SIZE = 0.08; · 19 unmodified lines
20var PADDLE_SIZE = 0.20;20var PADDLE_SIZE = 0.20;
21var PADDLE_THICKNESS = 0.06;21var PADDLE_THICKNESS = 0.06;
22var PADDLE_COLOR = { red: 184, green: 134, blue: 11 };22var PADDLE_COLOR = { red: 184, green: 134, blue: 11 };
23var BALL_COLOR = { red: 255, green: 0, blue: 0 };23var BALL_COLOR = { red: 0, green: 255, blue: 0 };
24var LINE_COLOR = { red: 255, green: 255, blue: 0 };24var LINE_COLOR = { red: 255, green: 255, blue: 0 };
25var PADDLE_BOX_OFFSET = { x: 0.05, y: 0.0, z: 0.0 };25var 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)
27var HOLD_POSITION_LEFT_OFFSET = { x: -0.15, y: 0.05, z: -0.05 }; 28var HOLD_POSITION_LEFT_OFFSET = { x: -0.15, y: 0.05, z: -0.05 };
28var HOLD_POSITION_RIGHT_OFFSET = { x: -0.15, y: 0.05, z: 0.05 }; 29var HOLD_POSITION_RIGHT_OFFSET = { x: -0.15, y: 0.05, z: 0.05 };
29var PADDLE_ORIENTATION = Quat.fromPitchYawRollDegrees(0,0,0);30var PADDLE_ORIENTATION = Quat.fromPitchYawRollDegrees(0,0,0);
@@ -32,18 +33,7 @@ var SPRING_FORCE = 15.0; · 2 unmodified lines
32var lastSoundTime = 0; 33var lastSoundTime = 0;
33var gameOn = false; 34var gameOn = false;
34var leftHanded = true; 35var leftHanded = true;
35var controllerID;
36 36
37
38function setControllerID() {
39 if (leftHanded) {
40 controllerID = 1;
41 } else {
42 controllerID = 3;
43 }
44}
45
46setControllerID();
47Menu.addMenu("PaddleBall");37Menu.addMenu("PaddleBall");
48Menu.addMenuItem({ menuName: "PaddleBall", menuItemName: "Left-Handed", isCheckable: true, isChecked: true });38Menu.addMenuItem({ menuName: "PaddleBall", menuItemName: "Left-Handed", isCheckable: true, isChecked: true });
49 39
@@ -63,7 +53,7 @@ var ball, paddle, paddleModel, line; · 13 unmodified lines
63function createEntities() {53function createEntities() {
64 ball = Entities.addEntity(54 ball = Entities.addEntity(
65 { type: "Sphere",55 { type: "Sphere",
66 position: Controller.getSpatialControlPosition(controllerID), 56 position: leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation,
67 dimensions: { x: BALL_SIZE, y: BALL_SIZE, z: BALL_SIZE }, 57 dimensions: { x: BALL_SIZE, y: BALL_SIZE, z: BALL_SIZE },
68 color: BALL_COLOR,58 color: BALL_COLOR,
69 gravity: { x: 0, y: GRAVITY, z: 0 },59 gravity: { x: 0, y: GRAVITY, z: 0 },
@@ -73,28 +63,28 @@ function createEntities() { · 3 unmodified lines
73 63
74 paddle = Entities.addEntity(64 paddle = Entities.addEntity(
75 { type: "Box",65 { type: "Box",
76 position: Controller.getSpatialControlPosition(controllerID), 66 position: leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation,
77 dimensions: { x: PADDLE_SIZE, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 0.80 }, 67 dimensions: { x: PADDLE_SIZE, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 0.80 },
78 color: PADDLE_COLOR,68 color: PADDLE_COLOR,
79 gravity: { x: 0, y: 0, z: 0 },69 gravity: { x: 0, y: 0, z: 0 },
80 ignoreCollisions: false,70 ignoreCollisions: false,
81 damping: 0.10,71 damping: 0.10,
82 visible: false,72 visible: false,
83 rotation: Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)),73 rotation : leftHanded ? MyAvatar.leftHandPose.rotation : MyAvatar.rightHandPose.rotation,
84 collisionsWillMove: false });74 collisionsWillMove: false });
85 75
86 modelURL = "http://public.highfidelity.io/models/attachments/pong_paddle.fbx";76 modelURL = "http://public.highfidelity.io/models/attachments/pong_paddle.fbx";
87 paddleModel = Entities.addEntity(77 paddleModel = Entities.addEntity(
88 { type: "Model",78 { type: "Model",
89 position: Vec3.sum(Controller.getSpatialControlPosition(controllerID), PADDLE_BOX_OFFSET), 79 position: Vec3.sum( leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation, PADDLE_BOX_OFFSET),
90 dimensions: { x: PADDLE_SIZE * 1.5, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 1.25 }, 80 dimensions: { x: PADDLE_SIZE * 1.5, y: PADDLE_THICKNESS, z: PADDLE_SIZE * 1.25 },
91 color: PADDLE_COLOR,81 color: PADDLE_COLOR,
92 gravity: { x: 0, y: 0, z: 0 },82 gravity: { x: 0, y: 0, z: 0 },
93 ignoreCollisions: true,83 ignoreCollisions: true,
94 modelURL: modelURL,84 modelURL: modelURL,
95 damping: 0.10,85 damping: 0.10,
96 rotation: Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)),86 rotation : leftHanded ? MyAvatar.leftHandPose.rotation : MyAvatar.rightHandPose.rotation,
97 collisionsWillMove: false });87 collisionsWillMove: false });
98 88
99 line = Overlays.addOverlay("line3d", {89 line = Overlays.addOverlay("line3d", {
100 start: { x: 0, y: 0, z: 0 },90 start: { x: 0, y: 0, z: 0 },
@@ -118,7 +108,7 @@ function deleteEntities() { · 17 unmodified lines
118}108}
119 109
120function update(deltaTime) {110function update(deltaTime) {
121 var palmPosition = Controller.getSpatialControlPosition(controllerID);111 var palmPosition = leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation;
122 var controllerActive = (Vec3.length(palmPosition) > 0);112 var controllerActive = (Vec3.length(palmPosition) > 0);
123 113
124 if (!gameOn && controllerActive) {114 if (!gameOn && controllerActive) {
@@ -133,8 +123,8 @@ function update(deltaTime) { · 8 unmodified lines
133 }123 }
134 124
135 var paddleOrientation = leftHanded ? PADDLE_ORIENTATION : Quat.multiply(PADDLE_ORIENTATION, Quat.fromPitchYawRollDegrees(0, 180, 0));125 var paddleOrientation = leftHanded ? PADDLE_ORIENTATION : Quat.multiply(PADDLE_ORIENTATION, Quat.fromPitchYawRollDegrees(0, 180, 0));
136 var paddleWorldOrientation = Quat.multiply(Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(controllerID)), paddleOrientation);126 var paddleWorldOrientation = Quat.multiply(leftHanded ? MyAvatar.leftHandPose.rotation : MyAvatar.rightHandPose.rotation, paddleOrientation);
137 var holdPosition = Vec3.sum(leftHanded ? MyAvatar.getLeftPalmPosition() : MyAvatar.getRightPalmPosition(), 127 var holdPosition = Vec3.sum(leftHanded ? MyAvatar.leftHandPose.translation : MyAvatar.rightHandPose.translation,
138 Vec3.multiplyQbyV(paddleWorldOrientation, leftHanded ? HOLD_POSITION_LEFT_OFFSET : HOLD_POSITION_RIGHT_OFFSET ));128 Vec3.multiplyQbyV(paddleWorldOrientation, leftHanded ? HOLD_POSITION_LEFT_OFFSET : HOLD_POSITION_RIGHT_OFFSET ));
139 129
140 var props = Entities.getEntityProperties(ball);130 var props = Entities.getEntityProperties(ball);
@@ -146,10 +136,10 @@ function update(deltaTime) { · 5 unmodified lines
146 Entities.editEntity(ball, { velocity: ballVelocity });136 Entities.editEntity(ball, { velocity: ballVelocity });
147 Overlays.editOverlay(line, { start: props.position, end: holdPosition });137 Overlays.editOverlay(line, { start: props.position, end: holdPosition });
148 Entities.editEntity(paddle, { position: holdPosition, 138 Entities.editEntity(paddle, { position: holdPosition,
149 velocity: Controller.getSpatialControlVelocity(controllerID),139 velocity: leftHanded ? MyAvatar.leftHandPose.velocity : MyAvatar.rightHandPose.velocity,
150 rotation: paddleWorldOrientation });140 rotation: paddleWorldOrientation });
151 Entities.editEntity(paddleModel, { position: Vec3.sum(holdPosition, Vec3.multiplyQbyV(paddleWorldOrientation, PADDLE_BOX_OFFSET)), 141 Entities.editEntity(paddleModel, { position: Vec3.sum(holdPosition, Vec3.multiplyQbyV(paddleWorldOrientation, PADDLE_BOX_OFFSET)),
152 velocity: Controller.getSpatialControlVelocity(controllerID),142 velocity: leftHanded ? MyAvatar.leftHandPose.velocity : MyAvatar.rightHandPose.velocity,
153 rotation: paddleWorldOrientation });143 rotation: paddleWorldOrientation });
154 144
155}145}
@@ -182,7 +172,6 @@ function menuItemEvent(menuItem) { · 26 unmodified lines
182 leftHanded = Menu.isOptionChecked("Left-Handed");172 leftHanded = Menu.isOptionChecked("Left-Handed");
183 }173 }
184 if ((leftHanded != oldHanded) && gameOn) {174 if ((leftHanded != oldHanded) && gameOn) {
185 setControllerID();
186 deleteEntities(); 175 deleteEntities();
187 createEntities();176 createEntities();
188 }177 }
examples/controllers/hydra/toyball.js+19−43
@@ -19,14 +19,7 @@ HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; · 18 unmodified lines
19 19
20// maybe we should make these constants...20// maybe we should make these constants...
21var LEFT_PALM = 0;21var LEFT_PALM = 0;
22var LEFT_TIP = 1;
23var LEFT_BUTTON_FWD = 5;
24var LEFT_BUTTON_3 = 3;
25
26var RIGHT_PALM = 2;22var RIGHT_PALM = 2;
27var RIGHT_TIP = 3;
28var RIGHT_BUTTON_FWD = 11;
29var RIGHT_BUTTON_3 = 9;
30 23
31var BALL_RADIUS = 0.08;24var BALL_RADIUS = 0.08;
32var GRAVITY_STRENGTH = 3.0;25var GRAVITY_STRENGTH = 3.0;
@@ -69,9 +62,6 @@ function getBallHoldPosition(whichSide) { · 36 unmodified lines
69}62}
70 63
71function checkControllerSide(whichSide) {64function checkControllerSide(whichSide) {
72 var BUTTON_FWD;
73 var BUTTON_3;
74 var TRIGGER;
75 var palmPosition;65 var palmPosition;
76 var palmRotation;66 var palmRotation;
77 var ballAlreadyInHand;67 var ballAlreadyInHand;
@@ -79,35 +69,35 @@ function checkControllerSide(whichSide) { · 1 unmodified line
79 var linearVelocity;69 var linearVelocity;
80 var angularVelocity; 70 var angularVelocity;
81 var AVERAGE_FACTOR = 0.33;71 var AVERAGE_FACTOR = 0.33;
82 72 var grabButtonPressed;
73
83 if (whichSide == LEFT_PALM) {74 if (whichSide == LEFT_PALM) {
84 BUTTON_FWD = LEFT_BUTTON_FWD;75 palmPosition = MyAvatar.leftHandPose.translation;
85 BUTTON_3 = LEFT_BUTTON_3;76 palmRotation = MyAvatar.leftHandPose.rotation;
86 TRIGGER = 0;
87 palmPosition = Controller.getSpatialControlPosition(LEFT_PALM);
88 palmRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(LEFT_PALM));
89 ballAlreadyInHand = leftBallAlreadyInHand;77 ballAlreadyInHand = leftBallAlreadyInHand;
90 handMessage = "LEFT";78 handMessage = "LEFT";
91 averageLinearVelocity[0] = Vec3.sum(Vec3.multiply(AVERAGE_FACTOR, Controller.getSpatialControlVelocity(LEFT_TIP)), 79 averageLinearVelocity[0] = Vec3.sum(Vec3.multiply(AVERAGE_FACTOR, MyAvatar.leftHandTipPose.velocity),
92 Vec3.multiply(1.0 - AVERAGE_FACTOR, averageLinearVelocity[0]));80 Vec3.multiply(1.0 - AVERAGE_FACTOR, averageLinearVelocity[0]));
81
93 linearVelocity = averageLinearVelocity[0];82 linearVelocity = averageLinearVelocity[0];
94 angularVelocity = Vec3.multiplyQbyV(MyAvatar.orientation, Controller.getSpatialControlRawAngularVelocity(LEFT_TIP));83 angularVelocity = MyAvatar.leftHandTipPose.angularVelocity;
84 grabButtonPressed = (Controller.getValue(Controller.Standard.LT) > 0.5);
85
95 } else {86 } else {
96 BUTTON_FWD = RIGHT_BUTTON_FWD;87 palmPosition = MyAvatar.rightHandPose.translation;
97 BUTTON_3 = RIGHT_BUTTON_3;88 palmRotation = MyAvatar.rightHandPose.rotation;
98 TRIGGER = 1;
99 palmPosition = Controller.getSpatialControlPosition(RIGHT_PALM);
100 palmRotation = Quat.multiply(MyAvatar.orientation, Controller.getSpatialControlRawRotation(RIGHT_PALM));
101 ballAlreadyInHand = rightBallAlreadyInHand;89 ballAlreadyInHand = rightBallAlreadyInHand;
102 averageLinearVelocity[1] = Vec3.sum(Vec3.multiply(AVERAGE_FACTOR, Controller.getSpatialControlVelocity(RIGHT_TIP)), 90 averageLinearVelocity[1] = Vec3.sum(Vec3.multiply(AVERAGE_FACTOR, MyAvatar.rightHandTipPose.velocity),
103 Vec3.multiply(1.0 - AVERAGE_FACTOR, averageLinearVelocity[1]));91 Vec3.multiply(1.0 - AVERAGE_FACTOR, averageLinearVelocity[1]));
92
104 linearVelocity = averageLinearVelocity[1];93 linearVelocity = averageLinearVelocity[1];
105 angularVelocity = Vec3.multiplyQbyV(MyAvatar.orientation, Controller.getSpatialControlRawAngularVelocity(RIGHT_TIP));94 angularVelocity = MyAvatar.rightHandTipPose.angularVelocity;
106 handMessage = "RIGHT";95 handMessage = "RIGHT";
96 grabButtonPressed = (Controller.getValue(Controller.Standard.RT) > 0.5);
97
107 }98 }
108 99
109 var grabButtonPressed = (Controller.isButtonPressed(BUTTON_FWD) || Controller.isButtonPressed(BUTTON_3) || (Controller.getTriggerValue(TRIGGER) > 0.5));100
110
111 // If I don't currently have a ball in my hand, then try to catch closest one101 // If I don't currently have a ball in my hand, then try to catch closest one
112 if (!ballAlreadyInHand && grabButtonPressed) {102 if (!ballAlreadyInHand && grabButtonPressed) {
113 var closestEntity = Entities.findClosestEntity(palmPosition, targetRadius);103 var closestEntity = Entities.findClosestEntity(palmPosition, targetRadius);
@@ -187,10 +177,8 @@ function checkControllerSide(whichSide) { · 73 unmodified lines
187 if (ballAlreadyInHand) {177 if (ballAlreadyInHand) {
188 if (whichSide == LEFT_PALM) {178 if (whichSide == LEFT_PALM) {
189 handEntity = leftHandEntity;179 handEntity = leftHandEntity;
190 whichTip = LEFT_TIP;
191 } else {180 } else {
192 handEntity = rightHandEntity;181 handEntity = rightHandEntity;
193 whichTip = RIGHT_TIP;
194 }182 }
195 183
196 // If holding the ball keep it in the palm184 // 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
236function checkController(deltaTime) {222function checkController(deltaTime) {
237 var numberOfButtons = Controller.getNumberOfButtons();
238 var numberOfTriggers = Controller.getNumberOfTriggers();
239 var numberOfSpatialControls = Controller.getNumberOfSpatialControls();
240 var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers;
241
242 // this is expected for hydras
243 if (!(numberOfButtons==12 && numberOfTriggers == 2 && controllersPerTrigger == 2)) {
244 debugPrint("total buttons = " + numberOfButtons + ", Triggers = " + numberOfTriggers + ", controllers/trigger = " + controllersPerTrigger);
245 return; // bail if no hydra
246 }
247 223
248 checkControllerSide(LEFT_PALM);224 checkControllerSide(LEFT_PALM);
249 checkControllerSide(RIGHT_PALM);225 checkControllerSide(RIGHT_PALM);
250}226}
251 227
252 228
examples/example/avatarcontrol/controllerExample.js+9−38
@@ -10,25 +10,19 @@ · 9 unmodified lines
10// Distributed under the Apache License, Version 2.0.10// Distributed under the Apache License, Version 2.0.
11// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html11// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
12//12//
13
14// initialize our triggers13// initialize our triggers
15var triggerPulled = new Array();14var triggerPulled = new Array();
16var numberOfTriggers = Controller.getNumberOfTriggers();15var NUMBER_OF_TRIGGERS = 2;
17for (t = 0; t < numberOfTriggers; t++) {16for (t = 0; t < NUMBER_OF_TRIGGERS; t++) {
18 triggerPulled[t] = false;17 triggerPulled[t] = false;
19}18}
20 19var triggers = new Array();
20triggers[0] = Controller.Standard.LT;
21triggers[1] = Controller.Standard.RT;
21function checkController(deltaTime) {22function checkController(deltaTime) {
22 var numberOfTriggers = Controller.getNumberOfTriggers();
23 var numberOfSpatialControls = Controller.getNumberOfSpatialControls();
24 var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers;
25 var triggerToggled = false;23 var triggerToggled = false;
26 24 for (var t = 0; t < NUMBER_OF_TRIGGERS; t++) {
27 // this is expected for hydras25 var triggerValue = Controller.getValue(triggers[t]);
28 if (numberOfTriggers == 2 && controllersPerTrigger == 2) {
29 for (var t = 0; t < numberOfTriggers; t++) {
30 var triggerValue = Controller.getTriggerValue(t);
31
32 if (triggerPulled[t]) {26 if (triggerPulled[t]) {
33 // must release to at least 0.127 // must release to at least 0.1
34 if (triggerValue < 0.1) {28 if (triggerValue < 0.1) {
@@ -41,17 +35,14 @@ function checkController(deltaTime) { · 6 unmodified lines
41 triggerToggled = true;35 triggerToggled = true;
42 }36 }
43 }37 }
44
45 if (triggerToggled) {38 if (triggerToggled) {
46 print("a trigger was toggled");39 print("a trigger was toggled");
47 }40 }
48 }41 }
49 }42
50}43}
51
52// register the call back so it fires before each data send44// register the call back so it fires before each data send
53Script.update.connect(checkController);45Script.update.connect(checkController);
54
55function printKeyEvent(eventName, event) {46function printKeyEvent(eventName, event) {
56 print(eventName);47 print(eventName);
57 print(" event.key=" + event.key);48 print(" event.key=" + event.key);
@@ -64,7 +55,6 @@ function printKeyEvent(eventName, event) { · 6 unmodified lines
64}55}
65function keyPressEvent(event) {56function keyPressEvent(event) {
66 printKeyEvent("keyPressEvent", event);57 printKeyEvent("keyPressEvent", event);
67
68 if (event.text == "A") {58 if (event.text == "A") {
69 print("the A key was pressed");59 print("the A key was pressed");
70 }60 }
@@ -72,10 +62,8 @@ function keyPressEvent(event) { · 1 unmodified line
72 print("the <space> key was pressed");62 print("the <space> key was pressed");
73 }63 }
74}64}
75
76function keyReleaseEvent(event) {65function keyReleaseEvent(event) {
77 printKeyEvent("keyReleaseEvent", event);66 printKeyEvent("keyReleaseEvent", event);
78
79 if (event.text == "A") {67 if (event.text == "A") {
80 print("the A key was released");68 print("the A key was released");
81 }69 }
@@ -83,11 +71,9 @@ function keyReleaseEvent(event) { · 1 unmodified line
83 print("the <space> key was pressed");71 print("the <space> key was pressed");
84 }72 }
85}73}
86
87// Map keyPress and mouse move events to our callbacks74// Map keyPress and mouse move events to our callbacks
88Controller.keyPressEvent.connect(keyPressEvent);75Controller.keyPressEvent.connect(keyPressEvent);
89Controller.keyReleaseEvent.connect(keyReleaseEvent);76Controller.keyReleaseEvent.connect(keyReleaseEvent);
90
91// prevent the A key from going through to the application77// prevent the A key from going through to the application
92Controller.captureKeyEvents({ text: "A" });78Controller.captureKeyEvents({ text: "A" });
93Controller.captureKeyEvents({ key: "A".charCodeAt(0) }); // same as above, just another example of how to capture the key79Controller.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
95Controller.captureKeyEvents({ text: "@", isMeta: true });81Controller.captureKeyEvents({ text: "@", isMeta: true });
96Controller.captureKeyEvents({ text: "page up" });82Controller.captureKeyEvents({ text: "page up" });
97Controller.captureKeyEvents({ text: "page down" });83Controller.captureKeyEvents({ text: "page down" });
98
99
100function printMouseEvent(eventName, event) {84function printMouseEvent(eventName, event) {
101 print(eventName);85 print(eventName);
102 print(" event.x,y=" + event.x + ", " + event.y);86 print(" event.x,y=" + event.x + ", " + event.y);
@@ -109,22 +93,18 @@ function printMouseEvent(eventName, event) { · 6 unmodified lines
109 print(" event.isMeta=" + event.isMeta);93 print(" event.isMeta=" + event.isMeta);
110 print(" event.isAlt=" + event.isAlt);94 print(" event.isAlt=" + event.isAlt);
111}95}
112
113function mouseMoveEvent(event) {96function mouseMoveEvent(event) {
114 printMouseEvent("mouseMoveEvent", event);97 printMouseEvent("mouseMoveEvent", event);
115}98}
116function mousePressEvent(event) {99function mousePressEvent(event) {
117 printMouseEvent("mousePressEvent", event);100 printMouseEvent("mousePressEvent", event);
118}101}
119
120function mouseReleaseEvent(event) {102function mouseReleaseEvent(event) {
121 printMouseEvent("mouseReleaseEvent", event);103 printMouseEvent("mouseReleaseEvent", event);
122}104}
123
124Controller.mouseMoveEvent.connect(mouseMoveEvent);105Controller.mouseMoveEvent.connect(mouseMoveEvent);
125Controller.mousePressEvent.connect(mousePressEvent);106Controller.mousePressEvent.connect(mousePressEvent);
126Controller.mouseReleaseEvent.connect(mouseReleaseEvent);107Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
127
128function printTouchEvent(eventName, event) {108function printTouchEvent(eventName, event) {
129 print(eventName);109 print(eventName);
130 110
@@ -143,7 +123,6 @@ function printTouchEvent(eventName, event) { · 12 unmodified lines
143 print(" event.radius=" + event.radius);123 print(" event.radius=" + event.radius);
144 print(" event.isPinching=" + event.isPinching);124 print(" event.isPinching=" + event.isPinching);
145 print(" event.isPinchOpening=" + event.isPinchOpening);125 print(" event.isPinchOpening=" + event.isPinchOpening);
146
147 print(" event.angle=" + event.angle);126 print(" event.angle=" + event.angle);
148 for (var i = 0; i < event.points.length; i++) {127 for (var i = 0; i < event.points.length; i++) {
149 print(" event.angles[" + i + "]:" + event.angles[i]);128 print(" event.angles[" + i + "]:" + event.angles[i]);
@@ -151,15 +130,12 @@ function printTouchEvent(eventName, event) { · 1 unmodified line
151 print(" event.isRotating=" + event.isRotating);130 print(" event.isRotating=" + event.isRotating);
152 print(" event.rotating=" + event.rotating);131 print(" event.rotating=" + event.rotating);
153}132}
154
155function touchBeginEvent(event) {133function touchBeginEvent(event) {
156 printTouchEvent("touchBeginEvent", event);134 printTouchEvent("touchBeginEvent", event);
157}135}
158
159function touchUpdateEvent(event) {136function touchUpdateEvent(event) {
160 printTouchEvent("touchUpdateEvent", event);137 printTouchEvent("touchUpdateEvent", event);
161}138}
162
163function touchEndEvent(event) {139function touchEndEvent(event) {
164 printTouchEvent("touchEndEvent", event);140 printTouchEvent("touchEndEvent", event);
165}141}
@@ -167,8 +143,6 @@ function touchEndEvent(event) { · 1 unmodified line
167Controller.touchBeginEvent.connect(touchBeginEvent);143Controller.touchBeginEvent.connect(touchBeginEvent);
168Controller.touchUpdateEvent.connect(touchUpdateEvent);144Controller.touchUpdateEvent.connect(touchUpdateEvent);
169Controller.touchEndEvent.connect(touchEndEvent);145Controller.touchEndEvent.connect(touchEndEvent);
170
171
172function wheelEvent(event) {146function wheelEvent(event) {
173 print("wheelEvent");147 print("wheelEvent");
174 print(" event.x,y=" + event.x + ", " + event.y);148 print(" event.x,y=" + event.x + ", " + event.y);
@@ -182,9 +156,7 @@ function wheelEvent(event) { · 7 unmodified lines
182 print(" event.isMeta=" + event.isMeta);156 print(" event.isMeta=" + event.isMeta);
183 print(" event.isAlt=" + event.isAlt);157 print(" event.isAlt=" + event.isAlt);
184}158}
185
186Controller.wheelEvent.connect(wheelEvent);159Controller.wheelEvent.connect(wheelEvent);
187
188function scriptEnding() {160function scriptEnding() {
189 // re-enabled the standard application for touch events161 // re-enabled the standard application for touch events
190 Controller.releaseKeyEvents({ text: "A" });162 Controller.releaseKeyEvents({ text: "A" });
@@ -194,5 +166,4 @@ function scriptEnding() { · 3 unmodified lines
194 Controller.releaseKeyEvents({ text: "page up" });166 Controller.releaseKeyEvents({ text: "page up" });
195 Controller.releaseKeyEvents({ text: "page down" });167 Controller.releaseKeyEvents({ text: "page down" });
196}168}
197 169Script.scriptEnding.connect(scriptEnding);
198Script.scriptEnding.connect(scriptEnding);
examples/example/games/hydraGrabHockey.js+3−5
@@ -59,9 +59,7 @@ function controller(side) { · 58 unmodified lines
59 this.triggerHeld = false;59 this.triggerHeld = false;
60 this.triggerThreshold = 0.9;60 this.triggerThreshold = 0.9;
61 this.side = side;61 this.side = side;
62 this.palm = 2 * side;62 this.trigger = side == LEFT ? Controller.Standard.LT : Controller.Standard.RT;
63 this.tip = 2 * side + 1;
64 this.trigger = side;
65 this.originalGravity = {63 this.originalGravity = {
66 x: 0,64 x: 0,
67 y: 0,65 y: 0,
@@ -150,8 +148,8 @@ function controller(side) { · 82 unmodified lines
150 148
151 149
152 this.updateControllerState = function() {150 this.updateControllerState = function() {
153 this.palmPosition = Controller.getSpatialControlPosition(this.palm);151 this.palmPosition = this.side == RIGHT ? MyAvatar.rightHandPose.translation : MyAvatar.leftHandPose.translation;
154 this.tipPosition = Controller.getSpatialControlPosition(this.tip);152 this.tipPosition = this.side == RIGHT ? MyAvatar.rightHandTipPose.translation : MyAvatar.leftHandTipPose.translation;
155 this.triggerValue = Controller.getTriggerValue(this.trigger);153 this.triggerValue = Controller.getTriggerValue(this.trigger);
156 }154 }
157 155
examples/example/games/sword.js+3−3
@@ -238,7 +238,7 @@ var inHand = false; · 237 unmodified lines
238 238
239function isControllerActive() {239function isControllerActive() {
240 // I don't think the hydra API provides any reliable way to know whether a particular controller is active. Ask for both.240 // I don't think the hydra API provides any reliable way to know whether a particular controller is active. Ask for both.
241 controllerActive = (Vec3.length(Controller.getSpatialControlPosition(3)) > 0) || Vec3.length(Controller.getSpatialControlPosition(4)) > 0;241 controllerActive = (Vec3.length(MyAvatar.leftHandPose.translation) > 0) || Vec3.length(MyAvatar.rightHandPose.translation) > 0;
242 return controllerActive;242 return controllerActive;
243}243}
244 244
@@ -312,10 +312,10 @@ function grabSword(hand) { · 67 unmodified lines
312 }312 }
313 var handRotation;313 var handRotation;
314 if (hand === "right") {314 if (hand === "right") {
315 handRotation = MyAvatar.getRightPalmRotation();315 handRotation = MyAvatar.rightHandPose.rotation;
316 316
317 } else if (hand === "left") {317 } else if (hand === "left") {
318 handRotation = MyAvatar.getLeftPalmRotation();318 handRotation = MyAvatar.leftHandPose.rotation;
319 }319 }
320 var swordRotation = Entities.getEntityProperties(swordID).rotation;320 var swordRotation = Entities.getEntityProperties(swordID).rotation;
321 var offsetRotation = Quat.multiply(Quat.inverse(handRotation), swordRotation);321 var offsetRotation = Quat.multiply(Quat.inverse(handRotation), swordRotation);
examples/example/painting/hydraPaint.js+8−11
@@ -71,10 +71,8 @@ function controller(side, cycleColorButton) { · 70 unmodified lines
71 this.triggerHeld = false;71 this.triggerHeld = false;
72 this.triggerThreshold = 0.9;72 this.triggerThreshold = 0.9;
73 this.side = side;73 this.side = side;
74 this.palm = 2 * side;74 this.trigger = side == LEFT ? Controller.Stantard.LT : Controller.Standard.RT;
75 this.tip = 2 * side + 1;75 this.cycleColorButton = side == LEFT ? Controller.Stantard.LeftPrimaryThumb : Controller.Standard.RightPrimaryThumb;
76 this.trigger = side;
77 this.cycleColorButton = cycleColorButton;
78 76
79 this.points = [];77 this.points = [];
80 this.normals = [];78 this.normals = [];
@@ -173,11 +171,10 @@ function controller(side, cycleColorButton) { · 92 unmodified lines
173 171
174 172
175 this.updateControllerState = function() {173 this.updateControllerState = function() {
176 this.cycleColorButtonPressed = Controller.isButtonPressed(this.cycleColorButton);174 this.cycleColorButtonPressed = Controller.getValue(this.cycleColorButton);
177 this.palmPosition = Controller.getSpatialControlPosition(this.palm);175 this.palmPosition = this.side == RIGHT ? MyAvatar.rightHandPose.translation : MyAvatar.leftHandPose.translation;
178 this.tipPosition = Controller.getSpatialControlPosition(this.tip);176 this.tipPosition = this.side == RIGHT ? MyAvatar.rightHandTipPose.translation : MyAvatar.leftHandTipPose.translation;
179 this.palmNormal = Controller.getSpatialControlNormal(this.palm);177 this.triggerValue = Controller.getValue(this.trigger);
180 this.triggerValue = Controller.getTriggerValue(this.trigger);
181 178
182 179
183 if (this.prevCycleColorButtonPressed === true && this.cycleColorButtonPressed === false) {180 if (this.prevCycleColorButtonPressed === true && this.cycleColorButtonPressed === false) {
@@ -215,8 +212,8 @@ function vectorIsZero(v) { · 31 unmodified lines
215}212}
216 213
217 214
218var rightController = new controller(RIGHT, RIGHT_BUTTON_4);215var rightController = new controller(RIGHT);
219var leftController = new controller(LEFT, LEFT_BUTTON_4);216var leftController = new controller(LEFT);
220Script.update.connect(update);217Script.update.connect(update);
221Script.scriptEnding.connect(scriptEnding);218Script.scriptEnding.connect(scriptEnding);
222 219
examples/example/scripts/controllerScriptingExamples.js+47−12
@@ -11,6 +11,37 @@ · 10 unmodified lines
11 11
12// Assumes you only have the default keyboard connected12// Assumes you only have the default keyboard connected
13 13
14function 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
26var hydra = Controller.Hardware.Hydra;
27if (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
15Object.keys(Controller.Standard).forEach(function (input) {46Object.keys(Controller.Standard).forEach(function (input) {
16 print("Controller.Standard." + input + ":" + Controller.Standard[input]);47 print("Controller.Standard." + input + ":" + Controller.Standard[input]);
@@ -32,44 +63,48 @@ Controller.resetAllDeviceBindings(); · 15 unmodified lines
32// Query all actions63// Query all actions
33print("All Actions: \n" + Controller.getAllActions());64print("All Actions: \n" + Controller.getAllActions());
34 65
66var actionId = findAction("YAW_LEFT")
67
68print("Yaw Left action ID: " + actionId)
69
35// Each action stores:70// Each action stores:
36// action: int representation of enum71// action: int representation of enum
37print("Action 5 int: \n" + Controller.getAllActions()[5].action);72print("Action int: \n" + Controller.getAllActions()[actionId].action);
38 73
39// actionName: string representation of enum74// actionName: string representation of enum
40print("Action 5 name: \n" + Controller.getAllActions()[5].actionName);75print("Action name: \n" + Controller.getAllActions()[actionId].actionName);
41 76
42// inputChannels: list of all inputchannels that control that action77// inputChannels: list of all inputchannels that control that action
43print("Action 5 input channels: \n" + Controller.getAllActions()[5].inputChannels + "\n");78print("Action input channels: \n" + Controller.getAllActions()[actionId].inputChannels + "\n");
44 79
45 80
46// Each input channel stores:81// Each input channel stores:
47// action: Action that this InputChannel maps to82// action: Action that this InputChannel maps to
48print("Input channel action: \n" + Controller.getAllActions()[5].inputChannels[0].action);83print("Input channel action: \n" + Controller.getAllActions()[actionId].inputChannels[0].action);
49 84
50// scale: sensitivity of input85// scale: sensitivity of input
51print("Input channel scale: \n" + Controller.getAllActions()[5].inputChannels[0].scale);86print("Input channel scale: \n" + Controller.getAllActions()[actionId].inputChannels[0].scale);
52 87
53// input and modifier: Inputs88// input and modifier: Inputs
54print("Input channel input and modifier: \n" + Controller.getAllActions()[5].inputChannels[0].input + "\n" + Controller.getAllActions()[5].inputChannels[0].modifier + "\n");89print("Input channel input and modifier: \n" + Controller.getAllActions()[actionId].inputChannels[0].input + "\n" + Controller.getAllActions()[actionId].inputChannels[0].modifier + "\n");
55 90
56 91
57// Each Input stores:92// Each Input stores:
58// device: device of input93// device: device of input
59print("Input device: \n" + Controller.getAllActions()[5].inputChannels[0].input.device);94print("Input device: \n" + Controller.getAllActions()[actionId].inputChannels[0].input.device);
60 95
61// channel: channel of input96// channel: channel of input
62print("Input channel: \n" + Controller.getAllActions()[5].inputChannels[0].input.channel);97print("Input channel: \n" + Controller.getAllActions()[actionId].inputChannels[0].input.channel);
63 98
64// type: type of input (Unknown, Button, Axis, Joint)99// type: type of input (Unknown, Button, Axis, Joint)
65print("Input type: \n" + Controller.getAllActions()[5].inputChannels[0].input.type);100print("Input type: \n" + Controller.getAllActions()[actionId].inputChannels[0].input.type);
66 101
67// id: id of input102// id: id of input
68print("Input id: \n" + Controller.getAllActions()[5].inputChannels[0].input.id + "\n");103print("Input id: \n" + Controller.getAllActions()[actionId].inputChannels[0].input.id + "\n");
69 104
70 105
71// You can get the name of a device from its id106// You can get the name of a device from its id
72print("Device 1 name: \n" + Controller.getDeviceName(Controller.getAllActions()[5].inputChannels[0].input.id));107print("Device 1 name: \n" + Controller.getDeviceName(Controller.getAllActions()[actionId].inputChannels[0].input.id));
73 108
74// You can also get all of a devices input channels109// You can also get all of a devices input channels
75print("Device 1's input channels: \n" + Controller.getAllInputsForDevice(1) + "\n");110print("Device 1's input channels: \n" + Controller.getAllInputsForDevice(1) + "\n");
@@ -105,7 +140,7 @@ for (i = 0; i < availableInputs.length; i++) { · 29 unmodified lines
105 140
106// You can modify key bindings by using these avaiable inputs141// You can modify key bindings by using these avaiable inputs
107// This will replace e (up) with 6142// This will replace e (up) with 6
108var e = Controller.getAllActions()[5].inputChannels[0];143var e = Controller.getAllActions()[actionId].inputChannels[0];
109Controller.removeInputChannel(e);144Controller.removeInputChannel(e);
110e.input = availableInputs[6].input;145e.input = availableInputs[6].input;
111Controller.addInputChannel(e);146Controller.addInputChannel(e);
examples/libraries/omniTool.js+33−34
@@ -15,16 +15,18 @@ Script.include("omniTool/models/invisibleWand.js"); · 14 unmodified lines
15 15
16OmniToolModules = {};16OmniToolModules = {};
17OmniToolModuleType = null;17OmniToolModuleType = null;
18LOG_DEBUG = 1;
18 19
19OmniTool = function(side) {20OmniTool = function(left) {
20 this.OMNI_KEY = "OmniTool";21 this.OMNI_KEY = "OmniTool";
21 this.MAX_FRAMERATE = 60;22 this.MAX_FRAMERATE = 60;
22 this.UPDATE_INTERVAL = 1.0 / this.MAX_FRAMERATE23 this.UPDATE_INTERVAL = 1.0 / this.MAX_FRAMERATE
23 this.SIDE = side;24 this.left = left;
24 this.PALM = 2 * side;25 this.triggered = false;
25 this.ACTION = findAction(side ? "ACTION2" : "ACTION1");26 var actions = Controller.Actions;
26 this.ALT_ACTION = findAction(side ? "ACTION1" : "ACTION2");27 var standard = Controller.Standard;
27 28 this.palmControl = left ? actions.LeftHand : actions.RightHand;
29 logDebug("Init OmniTool " + (left ? "left" : "right"));
28 this.highlighter = new Highlighter();30 this.highlighter = new Highlighter();
29 this.ignoreEntities = {};31 this.ignoreEntities = {};
30 this.nearestOmniEntity = {32 this.nearestOmniEntity = {
@@ -47,22 +49,25 @@ OmniTool = function(side) { · 16 unmodified lines
47 this.showWand(false);49 this.showWand(false);
48 50
49 // Connect to desired events51 // Connect to desired events
50 var _this = this;52 var that = this;
51 Controller.actionEvent.connect(function(action, state) {
52 _this.onActionEvent(action, state);
53 });
54 53
55 Script.update.connect(function(deltaTime) {54 Script.update.connect(function(deltaTime) {
56 _this.lastUpdateInterval += deltaTime;55 that.lastUpdateInterval += deltaTime;
57 if (_this.lastUpdateInterval >= _this.UPDATE_INTERVAL) {56 if (that.lastUpdateInterval >= that.UPDATE_INTERVAL) {
58 _this.onUpdate(_this.lastUpdateInterval);57 that.onUpdate(that.lastUpdateInterval);
59 _this.lastUpdateInterval = 0;58 that.lastUpdateInterval = 0;
60 }59 }
61 });60 });
62 61
63 Script.scriptEnding.connect(function() {62 Script.scriptEnding.connect(function() {
64 _this.onCleanup();63 that.onCleanup();
65 });64 });
65
66 this.mapping = Controller.newMapping();
67 this.mapping.from(left ? standard.LeftPrimaryThumb : standard.RightPrimaryThumb).to(function(value){
68 that.onUpdateTrigger(value);
69 })
70 this.mapping.enable();
66}71}
67 72
68OmniTool.prototype.showWand = function(show) {73OmniTool.prototype.showWand = function(show) {
@@ -81,30 +86,23 @@ OmniTool.prototype.showWand = function(show) { · 12 unmodified lines
81 }86 }
82}87}
83 88
84
85OmniTool.prototype.onCleanup = function(action) {89OmniTool.prototype.onCleanup = function(action) {
90 this.mapping.disable();
86 this.unloadModule();91 this.unloadModule();
87}92}
88 93
89OmniTool.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) {95OmniTool.prototype.onUpdateTrigger = function (value) {
94 this.module.onActionEvent(action, state);96 //logDebug("Trigger update value " + value);
95 }97 var triggered = value != 0;
96 98 if (triggered != this.triggered) {
97 if (action == this.ACTION) {99 this.triggered = triggered;
98 if (state) {100 if (this.triggered) {
99 this.onClick();101 this.onClick();
100 } else {102 } else {
101 this.onRelease();103 this.onRelease();
102 }104 }
103 }105 }
104
105 // FIXME Does not work
106 //// with only one controller active (listed as 2 here because 'tip' + 'palm')
107 //// then treat the alt action button as the action button
108}106}
109 107
110OmniTool.prototype.getOmniToolData = function(entityId) {108OmniTool.prototype.getOmniToolData = function(entityId) {
@@ -127,7 +125,7 @@ OmniTool.prototype.setActive = function(active) { · 16 unmodified lines
127 if (active === this.active) {125 if (active === this.active) {
128 return;126 return;
129 }127 }
130 logDebug("OmniTool changing active state: " + active);128 logDebug("OmniTool " + this.left + " changing active state: " + active);
131 this.active = active;129 this.active = active;
132 this.model.setVisible(this.active);130 this.model.setVisible(this.active);
133 if (this.module && this.module.onActiveChanged) {131 if (this.module && this.module.onActiveChanged) {
@@ -138,17 +136,17 @@ OmniTool.prototype.setActive = function(active) { · 4 unmodified lines
138 136
139OmniTool.prototype.onUpdate = function(deltaTime) {137OmniTool.prototype.onUpdate = function(deltaTime) {
140 // FIXME this returns data if either the left or right controller is not on the base138 // FIXME this returns data if either the left or right controller is not on the base
141 this.position = Controller.getSpatialControlPosition(this.PALM);139 this.pose = Controller.getPoseValue(this.palmControl);
140 this.position = this.left ? MyAvatar.leftHandTipPosition : MyAvatar.rightHandTipPosition;
142 // When on the base, hydras report a position of 0141 // When on the base, hydras report a position of 0
143 this.setActive(Vec3.length(this.position) > 0.001);142 this.setActive(Vec3.length(this.position) > 0.001);
144 if (!this.active) {143 if (!this.active) {
145 return;144 return;
146 }145 }
147 146
148
149 if (this.model) {147 if (this.model) {
150 // Update the wand148 // Update the wand
151 var rawRotation = Controller.getSpatialControlRawRotation(this.PALM);149 var rawRotation = this.pose.rotation;
152 this.rotation = Quat.multiply(MyAvatar.orientation, rawRotation);150 this.rotation = Quat.multiply(MyAvatar.orientation, rawRotation);
153 this.model.setTransform({151 this.model.setTransform({
154 rotation: this.rotation,152 rotation: this.rotation,
@@ -306,6 +304,7 @@ OmniTool.prototype.scan = function() { · 151 unmodified lines
306}304}
307 305
308OmniTool.prototype.unloadModule = function() {306OmniTool.prototype.unloadModule = function() {
307 logDebug("Unloading omniTool module")
309 if (this.module && this.module.onUnload) {308 if (this.module && this.module.onUnload) {
310 this.module.onUnload();309 this.module.onUnload();
311 }310 }
@@ -348,4 +347,4 @@ OmniTool.prototype.activateNewOmniModule = function() { · 36 unmodified lines
348}347}
349 348
350// FIXME find a good way to sync the two omni tools349// FIXME find a good way to sync the two omni tools
351OMNI_TOOLS = [ new OmniTool(0), new OmniTool(1) ];350OMNI_TOOLS = [ new OmniTool(true), new OmniTool(false) ];
examples/libraries/utils.js+1−7
@@ -31,13 +31,7 @@ scaleLine = function (start, end, scale) { · 30 unmodified lines
31}31}
32 32
33findAction = function(name) {33findAction = function(name) {
34 var actions = Controller.getAllActions();34 return Controller.findAction(name);
35 for (var i = 0; i < actions.length; i++) {
36 if (actions[i].actionName == name) {
37 return i;
38 }
39 }
40 return 0;
41}35}
42 36
43addLine = function(origin, vector, color) {37addLine = function(origin, vector, color) {
examples/libraries/walkApi.js+2−18
@@ -18,24 +18,8 @@ Script.include("./libraries/walkConstants.js"); · 17 unmodified lines
18 18
19Avatar = function() {19Avatar = function() {
20 // if Hydras are connected, the only way to enable use is to never set any arm joint rotation20 // if Hydras are connected, the only way to enable use is to never set any arm joint rotation
21 this.hydraCheck = function() {21 this.hydraCheck = function () {
22 // function courtesy of Thijs Wenker (frisbee.js)22 return Controller.Hardware.Hydra !== undefined;
23 var numberOfButtons = Controller.getNumberOfButtons();
24 var numberOfTriggers = Controller.getNumberOfTriggers();
25 var numberOfSpatialControls = Controller.getNumberOfSpatialControls();
26 const HYDRA_BUTTONS = 12;
27 const HYDRA_TRIGGERS = 2;
28 const HYDRA_CONTROLLERS_PER_TRIGGER = 2;
29 var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers;
30 if (numberOfButtons == HYDRA_BUTTONS &&
31 numberOfTriggers == HYDRA_TRIGGERS &&
32 controllersPerTrigger == HYDRA_CONTROLLERS_PER_TRIGGER) {
33 print('walk.js info: Razer Hydra detected. Setting arms free (not controlled by script)');
34 return true;
35 } else {
36 print('walk.js info: Razer Hydra not detected. Arms will be controlled by script.');
37 return false;
38 }
39 }23 }
40 // settings24 // settings
41 this.headFree = true;25 this.headFree = true;
examples/mouseLook.js+1−1
@@ -38,7 +38,7 @@ var mouseLook = (function () { · 37 unmodified lines
38 keyboardID = 0;38 keyboardID = 0;
39 39
40 function onKeyPressEvent(event) {40 function onKeyPressEvent(event) {
41 if (event.text == 'M') {41 if (event.text == 'm') {
42 active = !active;42 active = !active;
43 updateMapping();43 updateMapping();
44 }44 }
examples/stick.js+5−13
@@ -10,7 +10,7 @@ · 9 unmodified lines
10// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html10// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
11//11//
12 12
13var hand = "left";13var hand = "right";
14var nullActionID = "00000000-0000-0000-0000-000000000000";14var nullActionID = "00000000-0000-0000-0000-000000000000";
15var controllerID;15var controllerID;
16var controllerActive;16var controllerActive;
@@ -32,7 +32,7 @@ function makeNewStick() { · 15 unmodified lines
32 modelURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.fbx",32 modelURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.fbx",
33 compoundShapeURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.obj",33 compoundShapeURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.obj",
34 dimensions: {x: .11, y: .11, z: 1.0},34 dimensions: {x: .11, y: .11, z: 1.0},
35 position: MyAvatar.getRightPalmPosition(), // initial position doesn't matter, as long as it's close35 position: MyAvatar.rightHandPosition, // initial position doesn't matter, as long as it's close
36 rotation: MyAvatar.orientation,36 rotation: MyAvatar.orientation,
37 damping: .1,37 damping: .1,
38 collisionSoundURL: "http://public.highfidelity.io/sounds/Collisions-hitsandslaps/67LCollision07.wav",38 collisionSoundURL: "http://public.highfidelity.io/sounds/Collisions-hitsandslaps/67LCollision07.wav",
@@ -84,23 +84,15 @@ function mouseMoveEvent(event) { · 45 unmodified lines
84}84}
85 85
86 86
87function initControls(){
88 if (hand == "right") {
89 controllerID = 3; // right handed
90 } else {
91 controllerID = 4; // left handed
92 }
93}
94
95
96function update(deltaTime){87function update(deltaTime){
97 var palmPosition = Controller.getSpatialControlPosition(controllerID);88 var handPose = (hand == "right") ? MyAvatar.rightHandPose : MyAvatar.leftHandPose;
89 var palmPosition = handPose.translation;
98 controllerActive = (Vec3.length(palmPosition) > 0);90 controllerActive = (Vec3.length(palmPosition) > 0);
99 if(!controllerActive){91 if(!controllerActive){
100 return;92 return;
101 }93 }
102 94
103 stickOrientation = Controller.getSpatialControlRawRotation(controllerID);95 stickOrientation = handPose.rotation;
104 var adjustment = Quat.fromPitchYawRollDegrees(180, 0, 0);96 var adjustment = Quat.fromPitchYawRollDegrees(180, 0, 0);
105 stickOrientation = Quat.multiply(stickOrientation, adjustment);97 stickOrientation = Quat.multiply(stickOrientation, adjustment);
106 98
examples/tests/controllerInterfaceTest.js+77−0
@@ -0,0 +1,77 @@
1ControllerTest = 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
70ControllerTest.prototype.onCleanup = function() {
71 if (this.mappingEnabled) {
72 this.mapping.disable();
73 }
74}
75
76
77new ControllerTest();
examples/toybox/ping_pong_gun/pingPongGun.js+9−8
@@ -45,6 +45,12 @@ · 44 unmodified lines
45 green: 255,45 green: 255,
46 blue: 25546 blue: 255
47 };47 };
48
49 var TRIGGER_CONTROLS = [
50 Controller.Standard.LT,
51 Controller.Standard.RT,
52 ];
53
48 54
49 PingPongGun.prototype = {55 PingPongGun.prototype = {
50 hand: null,56 hand: null,
@@ -53,11 +59,11 @@ · 2 unmodified lines
53 canShoot: false,59 canShoot: false,
54 canShootTimeout: null,60 canShootTimeout: null,
55 setRightHand: function() {61 setRightHand: function() {
56 this.hand = 'RIGHT';62 this.hand = 1;
57 },63 },
58 64
59 setLeftHand: function() {65 setLeftHand: function() {
60 this.hand = 'LEFT';66 this.hand = 0;
61 },67 },
62 68
63 startNearGrab: function() {69 startNearGrab: function() {
@@ -92,12 +98,7 @@ · 28 unmodified lines
92 },98 },
93 99
94 checkTriggerPressure: function(gunHand) {100 checkTriggerPressure: function(gunHand) {
95 var handClickString = gunHand + "_HAND_CLICK";101 this.triggerValue = Controller.getValue(TRIGGER_CONTROLS[gunHand]);
96
97 var handClick = Controller.findAction(handClickString);
98
99 this.triggerValue = Controller.getActionValue(handClick);
100
101 if (this.triggerValue < RELOAD_THRESHOLD) {102 if (this.triggerValue < RELOAD_THRESHOLD) {
102 // print('RELOAD');103 // print('RELOAD');
103 this.canShoot = true;104 this.canShoot = true;
examples/toybox/spray_paint/sprayPaintCan.js+8−7
@@ -33,12 +33,17 @@ · 32 unmodified lines
33 var MIN_POINT_DISTANCE = 0.01;33 var MIN_POINT_DISTANCE = 0.01;
34 var STROKE_WIDTH = 0.02;34 var STROKE_WIDTH = 0.02;
35 35
36 var TRIGGER_CONTROLS = [
37 Controller.Standard.LT,
38 Controller.Standard.RT,
39 ];
40
36 this.setRightHand = function () {41 this.setRightHand = function () {
37 this.hand = 'RIGHT';42 this.hand = 1;
38 }43 }
39 44
40 this.setLeftHand = function () {45 this.setLeftHand = function () {
41 this.hand = 'LEFT';46 this.hand = 0;
42 }47 }
43 48
44 this.startNearGrab = function () {49 this.startNearGrab = function () {
@@ -46,11 +51,7 @@ · 1 unmodified line
46 }51 }
47 52
48 this.toggleWithTriggerPressure = function () {53 this.toggleWithTriggerPressure = function () {
49 var handClickString = this.whichHand + "_HAND_CLICK";54 this.triggerValue = Controller.getValue(TRIGGER_CONTROLS[this.whichHand]);
50
51 var handClick = Controller.findAction(handClickString);
52
53 this.triggerValue = Controller.getActionValue(handClick);
54 if (this.triggerValue < DISABLE_SPRAY_THRESHOLD && this.spraying === true) {55 if (this.triggerValue < DISABLE_SPRAY_THRESHOLD && this.spraying === true) {
55 this.spraying = false;56 this.spraying = false;
56 this.disableStream();57 this.disableStream();
interface/CMakeLists.txt+12−5
@@ -2,7 +2,12 @@ set(TARGET_NAME interface) · 1 unmodified line
2project(${TARGET_NAME})2project(${TARGET_NAME})
3 3
4# set a default root dir for each of our optional externals if it was not passed4# set a default root dir for each of our optional externals if it was not passed
5set(OPTIONAL_EXTERNALS "Faceshift" "LeapMotion" "RtMidi" "RSSDK" "3DConnexionClient" "iViewHMD")5set(OPTIONAL_EXTERNALS "LeapMotion" "RtMidi" "RSSDK" "iViewHMD")
6
7if(WIN32)
8 list(APPEND OPTIONAL_EXTERNALS "3DConnexionClient")
9endif()
10
6foreach(EXTERNAL ${OPTIONAL_EXTERNALS})11foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
7 string(TOUPPER ${EXTERNAL} ${EXTERNAL}_UPPERCASE)12 string(TOUPPER ${EXTERNAL} ${EXTERNAL}_UPPERCASE)
8 if (NOT ${${EXTERNAL}_UPPERCASE}_ROOT_DIR)13 if (NOT ${${EXTERNAL}_UPPERCASE}_ROOT_DIR)
@@ -99,15 +104,17 @@ endif() · 90 unmodified lines
99link_hifi_libraries(shared octree environment gpu gl procedural model render 104link_hifi_libraries(shared octree environment gpu gl procedural model render
100 fbx networking model-networking entities avatars 105 fbx networking model-networking entities avatars
101 audio audio-client animation script-engine physics 106 audio audio-client animation script-engine physics
102 render-utils entities-renderer ui auto-updater 107 render-utils entities-renderer ui auto-updater
103 plugins display-plugins input-plugins)108 controllers plugins display-plugins input-plugins )
104 109
110#fixme find a way to express faceshift as a plugin
105target_bullet()111target_bullet()
106target_glew()112target_glew()
107target_opengl()113target_opengl()
108 114
109 115if (WIN32 OR APPLE)
110add_dependency_external_projects(sdl2)116 target_faceshift()
117endif()
111 118
112# perform standard include and linking for found externals119# perform standard include and linking for found externals
113foreach(EXTERNAL ${OPTIONAL_EXTERNALS})120foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
interface/external/faceshift/readme.txt+0−26
@@ -1,26 +0,0 @@
1
2Instructions for adding the Faceshift library to Interface
3Stephen Birarda, July 18th, 2014
4
5OS X users: You can also use homebrew to get the Faceshift library by tapping our repo - highfidelity/homebrew-formulas
6and then calling 'brew install highfidelity/formulas/faceshift'.
7
8You can download the Faceshift SDK from http://download.faceshift.com/faceshift-network.zip.
9
10Create a ‘faceshift’ folder under interface/externals.
11
12You 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
14If 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
161. 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
192. 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
233. Copy the fsbinarystream.h header file from the Faceshift SDK into the ‘include’ folder in your Faceshift folder.
24
254. Clear your build directory, run cmake and build, and you should be all set.
26
interface/external/sdl2/readme.txt+0−13
@@ -1,13 +0,0 @@
1
2Instructions for adding the SDL library (SDL2) to Interface
3David Rowe, 11 Jan 2015
4
5You can download the SDL development library from https://www.libsdl.org/. Interface has been tested with version 2.0.3.
6
71. 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
132. Clear your build directory, run cmake and build, and you should be all set.
interface/external/sixense/readme.txt+0−10
@@ -1,10 +0,0 @@
1
2Instructions for adding the Sixense driver to Interface
3Andrzej Kapolka, November 18, 2013
4
51. 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
103. Delete your build directory, run cmake and build, and you should be all set.
interface/resources/controllers/hydra.json+31−0
@@ -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}
interface/resources/controllers/keyboardMouse.json+86−0
@@ -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}
interface/resources/controllers/mapping-config.json+24−0
@@ -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}
interface/resources/controllers/mapping-test0.json+36−0
@@ -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}
interface/resources/controllers/standard-old.json+43−0
@@ -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}
interface/resources/controllers/standard.json+36−0
@@ -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
interface/resources/controllers/vive.json+26−0
@@ -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}
interface/resources/controllers/xbox.json+29−0
@@ -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}
interface/resources/qml/ScrollingGraph.qml+111−0
@@ -0,0 +1,111 @@
1import QtQuick 2.1
2import QtQuick.Controls 1.0
3import QtQuick.Layouts 1.0
4import QtQuick.Dialogs 1.0
5
6Rectangle {
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
interface/resources/qml/TestControllers.qml+161−0
@@ -0,0 +1,161 @@
1import QtQuick 2.3
2import QtQuick.Controls 1.2
3import QtQuick.Layouts 1.0
4import QtQuick.Dialogs 1.0
5
6import "controller"
7import "controls" as HifiControls
8import "styles"
9
10HifiControls.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
interface/resources/qml/controller/AnalogButton.qml+45−0
@@ -0,0 +1,45 @@
1import QtQuick 2.1
2import QtQuick.Controls 1.0
3import QtQuick.Layouts 1.0
4import QtQuick.Dialogs 1.0
5
6Item {
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
interface/resources/qml/controller/AnalogStick.qml+55−0
@@ -0,0 +1,55 @@
1import QtQuick 2.1
2import QtQuick.Controls 1.0
3import QtQuick.Layouts 1.0
4import QtQuick.Dialogs 1.0
5
6Item {
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
interface/resources/qml/controller/Hydra.qml+34−0
@@ -0,0 +1,34 @@
1import QtQuick 2.1
2import QtQuick.Controls 1.0
3import QtQuick.Layouts 1.0
4import QtQuick.Dialogs 1.0
5
6import "hydra"
7
8Item {
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}
interface/resources/qml/controller/Standard.qml+35−0
@@ -0,0 +1,35 @@
1import QtQuick 2.1
2import QtQuick.Controls 1.0
3import QtQuick.Layouts 1.0
4import QtQuick.Dialogs 1.0
5
6import "xbox"
7
8Item {
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}
interface/resources/qml/controller/ToggleButton.qml+45−0
@@ -0,0 +1,45 @@
1import QtQuick 2.1
2import QtQuick.Controls 1.0
3import QtQuick.Layouts 1.0
4import QtQuick.Dialogs 1.0
5
6Item {
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
interface/resources/qml/controller/Xbox.qml+104−0
@@ -0,0 +1,104 @@
1import QtQuick 2.1
2import QtQuick.Controls 1.0
3import QtQuick.Layouts 1.0
4import QtQuick.Dialogs 1.0
5
6import "xbox"
7
8Item {
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}
interface/resources/qml/controller/hydra/HydraButtons.qml+18−0
@@ -0,0 +1,18 @@
1import QtQuick 2.1
2import QtQuick.Controls 1.0
3import QtQuick.Layouts 1.0
4import QtQuick.Dialogs 1.0
5
6import ".."
7
8Item {
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
interface/resources/qml/controller/hydra/HydraStick.qml+91−0
@@ -0,0 +1,91 @@
1import QtQuick 2.1
2import QtQuick.Controls 1.0
3import QtQuick.Layouts 1.0
4import QtQuick.Dialogs 1.0
5
6import ".."
7
8Item {
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}
interface/resources/qml/controller/hydra/hydra.png+0−0

Binary file not shown.

interface/resources/qml/controller/xbox/DPad.qml+42−0
@@ -0,0 +1,42 @@
1import QtQuick 2.1
2import QtQuick.Controls 1.0
3import QtQuick.Layouts 1.0
4import QtQuick.Dialogs 1.0
5
6import ".."
7
8Item {
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
interface/resources/qml/controller/xbox/LeftAnalogStick.qml+21−0
@@ -0,0 +1,21 @@
1import QtQuick 2.1
2import QtQuick.Controls 1.0
3import QtQuick.Layouts 1.0
4import QtQuick.Dialogs 1.0
5
6import ".."
7
8Item {
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
interface/resources/qml/controller/xbox/RightAnalogStick.qml+21−0
@@ -0,0 +1,21 @@
1import QtQuick 2.1
2import QtQuick.Controls 1.0
3import QtQuick.Layouts 1.0
4import QtQuick.Dialogs 1.0
5
6import ".."
7
8Item {
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
interface/resources/qml/controller/xbox/XboxButtons.qml+46−0
@@ -0,0 +1,46 @@
1import QtQuick 2.1
2import QtQuick.Controls 1.0
3import QtQuick.Layouts 1.0
4import QtQuick.Dialogs 1.0
5
6import ".."
7
8Item {
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
interface/resources/qml/controller/xbox/xbox360-controller-md.png+0−0

Binary file not shown.

interface/src/Application.cpp+232−204
@@ -17,27 +17,32 @@ · 16 unmodified lines
17#include <glm/gtx/vector_angle.hpp>17#include <glm/gtx/vector_angle.hpp>
18#include <glm/gtc/type_ptr.hpp>18#include <glm/gtc/type_ptr.hpp>
19 19
20#include <QAbstractNativeEventFilter>20#include <QtCore/QDebug>
21#include <QActionGroup>21#include <QtCore/QObject>
22#include <QDebug>22#include <QtCore/QUrl>
23#include <QDesktopServices>23#include <QtCore/QTimer>
24#include <QDesktopWidget>24#include <QtCore/QAbstractNativeEventFilter>
25#include <QImage>25#include <QtCore/QMimeData>
26#include <QFileDialog>26
27#include <QInputDialog>27#include <QtGui/QScreen>
28#include <QKeyEvent>28#include <QtGui/QImage>
29#include <QMediaPlayer>29#include <QtGui/QWheelEvent>
30#include <QMenuBar>30#include <QtGui/QWindow>
31#include <QMessageBox>31#include <QtQml/QQmlContext>
32#include <QMimeData>32#include <QtGui/QKeyEvent>
33#include <QMouseEvent>33#include <QtGui/QMouseEvent>
34#include <QNetworkDiskCache>34#include <QtGui/QDesktopServices>
35#include <QObject>35
36#include <QScreen>36#include <QtWidgets/QActionGroup>
37#include <QTimer>37#include <QtWidgets/QDesktopWidget>
38#include <QUrl>38#include <QtWidgets/QFileDialog>
39#include <QWheelEvent>39#include <QtWidgets/QInputDialog>
40#include <QWindow>40#include <QtWidgets/QMenuBar>
41#include <QtWidgets/QMessageBox>
42
43#include <QtMultimedia/QMediaPlayer>
44
45#include <QtNetwork/QNetworkDiskCache>
41 46
42#include <AccountManager.h>47#include <AccountManager.h>
43#include <AddressManager.h>48#include <AddressManager.h>
@@ -60,7 +65,8 @@ · 16 unmodified lines
60#include <InfoView.h>65#include <InfoView.h>
61#include <input-plugins/InputPlugin.h>66#include <input-plugins/InputPlugin.h>
62#include <input-plugins/Joystick.h> // this should probably be removed67#include <input-plugins/Joystick.h> // this should probably be removed
63#include <input-plugins/UserInputMapper.h>68#include <controllers/UserInputMapper.h>
69#include <controllers/StateController.h>
64#include <LogHandler.h>70#include <LogHandler.h>
65#include <MainWindow.h>71#include <MainWindow.h>
66#include <MessageDialog.h>72#include <MessageDialog.h>
@@ -120,6 +126,7 @@ · 53 unmodified lines
120#include "scripting/SettingsScriptingInterface.h"126#include "scripting/SettingsScriptingInterface.h"
121#include "scripting/WebWindowClass.h"127#include "scripting/WebWindowClass.h"
122#include "scripting/WindowScriptingInterface.h"128#include "scripting/WindowScriptingInterface.h"
129#include "scripting/ControllerScriptingInterface.h"
123#if defined(Q_OS_MAC) || defined(Q_OS_WIN)130#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
124#include "SpeechRecognizer.h"131#include "SpeechRecognizer.h"
125#endif132#endif
@@ -137,6 +144,7 @@ · 11 unmodified lines
137#include "ui/UpdateDialog.h"144#include "ui/UpdateDialog.h"
138#include "Util.h"145#include "Util.h"
139 146
147
140// ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU148// ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
141// FIXME seems to be broken.149// FIXME seems to be broken.
142#if defined(Q_OS_WIN)150#if defined(Q_OS_WIN)
@@ -316,6 +324,7 @@ bool setupEssentials(int& argc, char** argv) { · 173 unmodified lines
316 DependencyManager::set<EntityScriptingInterface>();324 DependencyManager::set<EntityScriptingInterface>();
317 DependencyManager::set<WindowScriptingInterface>();325 DependencyManager::set<WindowScriptingInterface>();
318 DependencyManager::set<HMDScriptingInterface>();326 DependencyManager::set<HMDScriptingInterface>();
327
319#if defined(Q_OS_MAC) || defined(Q_OS_WIN)328#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
320 DependencyManager::set<SpeechRecognizer>();329 DependencyManager::set<SpeechRecognizer>();
321#endif330#endif
@@ -327,7 +336,7 @@ bool setupEssentials(int& argc, char** argv) { · 5 unmodified lines
327 DependencyManager::set<InterfaceActionFactory>();336 DependencyManager::set<InterfaceActionFactory>();
328 DependencyManager::set<AssetClient>();337 DependencyManager::set<AssetClient>();
329 DependencyManager::set<UserInputMapper>();338 DependencyManager::set<UserInputMapper>();
330 339 DependencyManager::set<controller::ScriptingInterface, ControllerScriptingInterface>();
331 return true;340 return true;
332}341}
333 342
@@ -339,45 +348,47 @@ int _keyboardFocusHighlightID{ -1 }; · 5 unmodified lines
339PluginContainer* _pluginContainer;348PluginContainer* _pluginContainer;
340 349
341Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :350Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
342 QApplication(argc, argv),351 QApplication(argc, argv),
343 _dependencyManagerIsSetup(setupEssentials(argc, argv)),352 _dependencyManagerIsSetup(setupEssentials(argc, argv)),
344 _window(new MainWindow(desktop())),353 _window(new MainWindow(desktop())),
345 _toolWindow(NULL),354 _toolWindow(NULL),
346 _undoStackScriptingInterface(&_undoStack),355 _undoStackScriptingInterface(&_undoStack),
347 _frameCount(0),356 _frameCount(0),
348 _fps(60.0f),357 _fps(60.0f),
349 _physicsEngine(new PhysicsEngine(Vectors::ZERO)),358 _physicsEngine(new PhysicsEngine(Vectors::ZERO)),
350 _entities(true, this, this),359 _entities(true, this, this),
351 _entityClipboardRenderer(false, this, this),360 _entityClipboardRenderer(false, this, this),
352 _entityClipboard(new EntityTree()),361 _entityClipboard(new EntityTree()),
353 _lastQueriedTime(usecTimestampNow()),362 _lastQueriedTime(usecTimestampNow()),
354 _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)),363 _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)),
355 _firstRun("firstRun", true),364 _firstRun("firstRun", true),
356 _previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION),365 _previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION),
357 _scriptsLocationHandle("scriptsLocation", DESKTOP_LOCATION),366 _scriptsLocationHandle("scriptsLocation", DESKTOP_LOCATION),
358 _fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES),367 _fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES),
359 _scaleMirror(1.0f),368 _scaleMirror(1.0f),
360 _rotateMirror(0.0f),369 _rotateMirror(0.0f),
361 _raiseMirror(0.0f),370 _raiseMirror(0.0f),
362 _lastMouseMoveWasSimulated(false),371 _lastMouseMoveWasSimulated(false),
363 _enableProcessOctreeThread(true),372 _enableProcessOctreeThread(true),
364 _runningScriptsWidget(NULL),373 _runningScriptsWidget(NULL),
365 _runningScriptsWidgetWasVisible(false),374 _runningScriptsWidgetWasVisible(false),
366 _lastNackTime(usecTimestampNow()),375 _lastNackTime(usecTimestampNow()),
367 _lastSendDownstreamAudioStats(usecTimestampNow()),376 _lastSendDownstreamAudioStats(usecTimestampNow()),
368 _aboutToQuit(false),377 _aboutToQuit(false),
369 _notifiedPacketVersionMismatchThisDomain(false),378 _notifiedPacketVersionMismatchThisDomain(false),
370 _maxOctreePPS(maxOctreePacketsPerSecond.get()),379 _maxOctreePPS(maxOctreePacketsPerSecond.get()),
371 _lastFaceTrackerUpdate(0)380 _lastFaceTrackerUpdate(0)
372{381{
373 thread()->setObjectName("Main Thread");382 thread()->setObjectName("Main Thread");
374 383
375 setInstance(this);384 setInstance(this);
376 385
386 auto controllerScriptingInterface = DependencyManager::get<controller::ScriptingInterface>().data();
387 _controllerScriptingInterface = dynamic_cast<ControllerScriptingInterface*>(controllerScriptingInterface);
377 // to work around the Qt constant wireless scanning, set the env for polling interval very high388 // to work around the Qt constant wireless scanning, set the env for polling interval very high
378 const QByteArray EXTREME_BEARER_POLL_TIMEOUT = QString::number(INT_MAX).toLocal8Bit();389 const QByteArray EXTREME_BEARER_POLL_TIMEOUT = QString::number(INT_MAX).toLocal8Bit();
379 qputenv("QT_BEARER_POLL_TIMEOUT", EXTREME_BEARER_POLL_TIMEOUT);390 qputenv("QT_BEARER_POLL_TIMEOUT", EXTREME_BEARER_POLL_TIMEOUT);
380 391
381 _entityClipboard->createRootElement();392 _entityClipboard->createRootElement();
382 393
383 _pluginContainer = new PluginContainerProxy();394 _pluginContainer = new PluginContainerProxy();
@@ -440,7 +451,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 56 unmodified lines
440 audioIO->moveToThread(audioThread);451 audioIO->moveToThread(audioThread);
441 452
442 auto& audioScriptingInterface = AudioScriptingInterface::getInstance();453 auto& audioScriptingInterface = AudioScriptingInterface::getInstance();
443 454
444 connect(audioThread, &QThread::started, audioIO.data(), &AudioClient::start);455 connect(audioThread, &QThread::started, audioIO.data(), &AudioClient::start);
445 connect(audioIO.data(), &AudioClient::destroyed, audioThread, &QThread::quit);456 connect(audioIO.data(), &AudioClient::destroyed, audioThread, &QThread::quit);
446 connect(audioThread, &QThread::finished, audioThread, &QThread::deleteLater);457 connect(audioThread, &QThread::finished, audioThread, &QThread::deleteLater);
@@ -479,7 +490,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 32 unmodified lines
479 connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails()));490 connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails()));
480 connect(&domainHandler, &DomainHandler::settingsReceived, this, &Application::domainSettingsReceived);491 connect(&domainHandler, &DomainHandler::settingsReceived, this, &Application::domainSettingsReceived);
481 connect(&domainHandler, &DomainHandler::hostnameChanged,492 connect(&domainHandler, &DomainHandler::hostnameChanged,
482 DependencyManager::get<AddressManager>().data(), &AddressManager::storeCurrentAddress);493 DependencyManager::get<AddressManager>().data(), &AddressManager::storeCurrentAddress);
483 494
484 // update our location every 5 seconds in the metaverse server, assuming that we are authenticated with one495 // update our location every 5 seconds in the metaverse server, assuming that we are authenticated with one
485 const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * 1000;496 const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * 1000;
@@ -490,7 +501,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 4 unmodified lines
490 501
491 // if we get a domain change, immediately attempt update location in metaverse server502 // if we get a domain change, immediately attempt update location in metaverse server
492 connect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain,503 connect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain,
493 discoverabilityManager.data(), &DiscoverabilityManager::updateLocation);504 discoverabilityManager.data(), &DiscoverabilityManager::updateLocation);
494 505
495 connect(nodeList.data(), &NodeList::nodeAdded, this, &Application::nodeAdded);506 connect(nodeList.data(), &NodeList::nodeAdded, this, &Application::nodeAdded);
496 connect(nodeList.data(), &NodeList::nodeKilled, this, &Application::nodeKilled);507 connect(nodeList.data(), &NodeList::nodeKilled, this, &Application::nodeKilled);
@@ -529,14 +540,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 32 unmodified lines
529 connect(addressManager.data(), &AddressManager::hostChanged, this, &Application::updateWindowTitle);540 connect(addressManager.data(), &AddressManager::hostChanged, this, &Application::updateWindowTitle);
530 connect(this, &QCoreApplication::aboutToQuit, addressManager.data(), &AddressManager::storeCurrentAddress);541 connect(this, &QCoreApplication::aboutToQuit, addressManager.data(), &AddressManager::storeCurrentAddress);
531 542
532 #ifdef _WIN32543#ifdef _WIN32
533 WSADATA WsaData;544 WSADATA WsaData;
534 int wsaresult = WSAStartup(MAKEWORD(2,2), &WsaData);545 int wsaresult = WSAStartup(MAKEWORD(2, 2), &WsaData);
535 #endif546#endif
536 547
537 // tell the NodeList instance who to tell the domain server we care about548 // tell the NodeList instance who to tell the domain server we care about
538 nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer549 nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer
539 << NodeType::EntityServer << NodeType::AssetServer);550 << NodeType::EntityServer << NodeType::AssetServer);
540 551
541 // connect to the packet sent signal of the _entityEditSender552 // connect to the packet sent signal of the _entityEditSender
542 connect(&_entityEditSender, &EntityEditPacketSender::packetSent, this, &Application::packetSent);553 connect(&_entityEditSender, &EntityEditPacketSender::packetSent, this, &Application::packetSent);
@@ -609,29 +620,38 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 66 unmodified lines
609 // hook up bandwidth estimator620 // hook up bandwidth estimator
610 QSharedPointer<BandwidthRecorder> bandwidthRecorder = DependencyManager::get<BandwidthRecorder>();621 QSharedPointer<BandwidthRecorder> bandwidthRecorder = DependencyManager::get<BandwidthRecorder>();
611 connect(nodeList.data(), &LimitedNodeList::dataSent,622 connect(nodeList.data(), &LimitedNodeList::dataSent,
612 bandwidthRecorder.data(), &BandwidthRecorder::updateOutboundData);623 bandwidthRecorder.data(), &BandwidthRecorder::updateOutboundData);
613 connect(&nodeList->getPacketReceiver(), &PacketReceiver::dataReceived,624 connect(&nodeList->getPacketReceiver(), &PacketReceiver::dataReceived,
614 bandwidthRecorder.data(), &BandwidthRecorder::updateInboundData);625 bandwidthRecorder.data(), &BandwidthRecorder::updateInboundData);
615 626
616 connect(&getMyAvatar()->getSkeletonModel(), &SkeletonModel::skeletonLoaded,627 connect(&getMyAvatar()->getSkeletonModel(), &SkeletonModel::skeletonLoaded,
617 this, &Application::checkSkeleton, Qt::QueuedConnection);628 this, &Application::checkSkeleton, Qt::QueuedConnection);
618 629
619 // Setup the userInputMapper with the actions630 // Setup the userInputMapper with the actions
620 auto userInputMapper = DependencyManager::get<UserInputMapper>();631 auto userInputMapper = DependencyManager::get<UserInputMapper>();
621 connect(userInputMapper.data(), &UserInputMapper::actionEvent, &_controllerScriptingInterface, &AbstractControllerScriptingInterface::actionEvent);
622 connect(userInputMapper.data(), &UserInputMapper::actionEvent, [this](int action, float state) {632 connect(userInputMapper.data(), &UserInputMapper::actionEvent, [this](int action, float state) {
623 if (state) {633 if (state && action == toInt(controller::Action::TOGGLE_MUTE)) {
624 switch (action) {634 DependencyManager::get<AudioClient>()->toggleMute();
625 case UserInputMapper::Action::TOGGLE_MUTE:
626 DependencyManager::get<AudioClient>()->toggleMute();
627 break;
628 }
629 }635 }
630 });636 });
631 637
638 // A new controllerInput device used to reflect current values from the application state
639 _applicationStateDevice = std::make_shared<controller::StateController>();
640
641 _applicationStateDevice->addInputVariant(QString("InHMD"), controller::StateController::ReadLambda([]() -> float {
642 return (float)qApp->getAvatarUpdater()->isHMDMode();
643 }));
644 _applicationStateDevice->addInputVariant(QString("ComfortMode"), controller::StateController::ReadLambda([]() -> float {
645 return (float)Menu::getInstance()->isOptionChecked(MenuOption::ComfortMode);
646 }));
647
648 userInputMapper->registerDevice(_applicationStateDevice);
649
632 // Setup the keyboardMouseDevice and the user input mapper with the default bindings650 // Setup the keyboardMouseDevice and the user input mapper with the default bindings
633 _keyboardMouseDevice->registerToUserInputMapper(*userInputMapper);651 userInputMapper->registerDevice(_keyboardMouseDevice);
634 _keyboardMouseDevice->assignDefaultInputMapping(*userInputMapper);652
653
654 userInputMapper->loadDefaultMapping(userInputMapper->getStandardDeviceID());
635 655
636 // check first run...656 // check first run...
637 if (_firstRun.get()) {657 if (_firstRun.get()) {
@@ -704,8 +724,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 66 unmodified lines
704 // Now that menu is initalized we can sync myAvatar with it's state.724 // Now that menu is initalized we can sync myAvatar with it's state.
705 getMyAvatar()->updateMotionBehaviorFromMenu();725 getMyAvatar()->updateMotionBehaviorFromMenu();
706 726
727#if 0
707 // the 3Dconnexion device wants to be initiliazed after a window is displayed.728 // the 3Dconnexion device wants to be initiliazed after a window is displayed.
708 ConnexionClient::getInstance().init();729 ConnexionClient::getInstance().init();
730#endif
709 731
710 auto& packetReceiver = nodeList->getPacketReceiver();732 auto& packetReceiver = nodeList->getPacketReceiver();
711 packetReceiver.registerListener(PacketType::DomainConnectionDenied, this, "handleDomainConnectionDeniedPacket");733 packetReceiver.registerListener(PacketType::DomainConnectionDenied, this, "handleDomainConnectionDeniedPacket");
@@ -792,6 +814,9 @@ void Application::cleanupBeforeQuit() { · 80 unmodified lines
792 814
793 AnimDebugDraw::getInstance().shutdown();815 AnimDebugDraw::getInstance().shutdown();
794 816
817 // FIXME: once we move to shared pointer for the INputDevice we shoud remove this naked delete:
818 _applicationStateDevice.reset();
819
795 if (_keyboardFocusHighlightID > 0) {820 if (_keyboardFocusHighlightID > 0) {
796 getOverlays().deleteOverlay(_keyboardFocusHighlightID);821 getOverlays().deleteOverlay(_keyboardFocusHighlightID);
797 _keyboardFocusHighlightID = -1;822 _keyboardFocusHighlightID = -1;
@@ -902,7 +927,10 @@ Application::~Application() { · 104 unmodified lines
902 927
903 Leapmotion::destroy();928 Leapmotion::destroy();
904 RealSense::destroy();929 RealSense::destroy();
930
931#if 0
905 ConnexionClient::getInstance().destroy();932 ConnexionClient::getInstance().destroy();
933#endif
906 934
907 qInstallMessageHandler(NULL); // NOTE: Do this as late as possible so we continue to get our log messages935 qInstallMessageHandler(NULL); // NOTE: Do this as late as possible so we continue to get our log messages
908}936}
@@ -975,6 +1003,8 @@ void Application::initializeUi() { · 66 unmodified lines
975 offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));1003 offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));
976 offscreenUi->load("Root.qml");1004 offscreenUi->load("Root.qml");
977 offscreenUi->load("RootMenu.qml");1005 offscreenUi->load("RootMenu.qml");
1006 auto scriptingInterface = DependencyManager::get<controller::ScriptingInterface>();
1007 offscreenUi->getRootContext()->setContextProperty("Controller", scriptingInterface.data());
978 _glWidget->installEventFilter(offscreenUi.data());1008 _glWidget->installEventFilter(offscreenUi.data());
979 VrMenu::load();1009 VrMenu::load();
980 VrMenu::executeQueuedLambdas();1010 VrMenu::executeQueuedLambdas();
@@ -1003,7 +1033,10 @@ void Application::initializeUi() { · 22 unmodified lines
1003 foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) {1033 foreach(auto inputPlugin, PluginManager::getInstance()->getInputPlugins()) {
1004 QString name = inputPlugin->getName();1034 QString name = inputPlugin->getName();
1005 if (name == KeyboardMouseDevice::NAME) {1035 if (name == KeyboardMouseDevice::NAME) {
1006 _keyboardMouseDevice = static_cast<KeyboardMouseDevice*>(inputPlugin.data()); // TODO: this seems super hacky1036 auto kbm = static_cast<KeyboardMouseDevice*>(inputPlugin.data());
1037 // FIXME incredibly evil.... _keyboardMouseDevice is now owned by
1038 // both a QSharedPointer and a std::shared_ptr
1039 _keyboardMouseDevice = std::shared_ptr<KeyboardMouseDevice>(kbm);
1007 }1040 }
1008 }1041 }
1009 updateInputModes();1042 updateInputModes();
@@ -1456,7 +1489,7 @@ bool Application::event(QEvent* event) { · 446 unmodified lines
1456 }1489 }
1457 1490
1458 if (HFActionEvent::types().contains(event->type())) {1491 if (HFActionEvent::types().contains(event->type())) {
1459 _controllerScriptingInterface.handleMetaEvent(static_cast<HFMetaEvent*>(event));1492 _controllerScriptingInterface->handleMetaEvent(static_cast<HFMetaEvent*>(event));
1460 }1493 }
1461 1494
1462 return QApplication::event(event);1495 return QApplication::event(event);
@@ -1470,7 +1503,7 @@ bool Application::eventFilter(QObject* object, QEvent* event) { · 7 unmodified lines
1470 }1503 }
1471 1504
1472 // Filter out captured keys before they're used for shortcut actions.1505 // Filter out captured keys before they're used for shortcut actions.
1473 if (_controllerScriptingInterface.isKeyCaptured(static_cast<QKeyEvent*>(event))) {1506 if (_controllerScriptingInterface->isKeyCaptured(static_cast<QKeyEvent*>(event))) {
1474 event->accept();1507 event->accept();
1475 return true;1508 return true;
1476 }1509 }
@@ -1485,10 +1518,10 @@ void Application::keyPressEvent(QKeyEvent* event) { · 8 unmodified lines
1485 _altPressed = event->key() == Qt::Key_Alt;1518 _altPressed = event->key() == Qt::Key_Alt;
1486 _keysPressed.insert(event->key());1519 _keysPressed.insert(event->key());
1487 1520
1488 _controllerScriptingInterface.emitKeyPressEvent(event); // send events to any registered scripts1521 _controllerScriptingInterface->emitKeyPressEvent(event); // send events to any registered scripts
1489 1522
1490 // if one of our scripts have asked to capture this event, then stop processing it1523 // if one of our scripts have asked to capture this event, then stop processing it
1491 if (_controllerScriptingInterface.isKeyCaptured(event)) {1524 if (_controllerScriptingInterface->isKeyCaptured(event)) {
1492 return;1525 return;
1493 }1526 }
1494 1527
@@ -1518,6 +1551,13 @@ void Application::keyPressEvent(QKeyEvent* event) { · 23 unmodified lines
1518 if (isMeta) {1551 if (isMeta) {
1519 auto offscreenUi = DependencyManager::get<OffscreenUi>();1552 auto offscreenUi = DependencyManager::get<OffscreenUi>();
1520 offscreenUi->load("Browser.qml");1553 offscreenUi->load("Browser.qml");
1554 }
1555 break;
1556
1557 case Qt::Key_X:
1558 if (isMeta && isShifted) {
1559 auto offscreenUi = DependencyManager::get<OffscreenUi>();
1560 offscreenUi->load("TestControllers.qml");
1521 }1561 }
1522 break;1562 break;
1523 1563
@@ -1752,10 +1792,10 @@ void Application::keyReleaseEvent(QKeyEvent* event) { · 228 unmodified lines
1752 1792
1753 _keysPressed.remove(event->key());1793 _keysPressed.remove(event->key());
1754 1794
1755 _controllerScriptingInterface.emitKeyReleaseEvent(event); // send events to any registered scripts1795 _controllerScriptingInterface->emitKeyReleaseEvent(event); // send events to any registered scripts
1756 1796
1757 // if one of our scripts have asked to capture this event, then stop processing it1797 // if one of our scripts have asked to capture this event, then stop processing it
1758 if (_controllerScriptingInterface.isKeyCaptured(event)) {1798 if (_controllerScriptingInterface->isKeyCaptured(event)) {
1759 return;1799 return;
1760 }1800 }
1761 1801
@@ -1796,7 +1836,9 @@ void Application::focusOutEvent(QFocusEvent* event) { · 34 unmodified lines
1796 inputPlugin->pluginFocusOutEvent();1836 inputPlugin->pluginFocusOutEvent();
1797 }1837 }
1798 }1838 }
1839#if 0
1799 ConnexionData::getInstance().focusOutEvent();1840 ConnexionData::getInstance().focusOutEvent();
1841#endif
1800 1842
1801 // synthesize events for keys currently pressed, since we may not get their release events1843 // synthesize events for keys currently pressed, since we may not get their release events
1802 foreach (int key, _keysPressed) {1844 foreach (int key, _keysPressed) {
@@ -1844,10 +1886,10 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { · 41 unmodified lines
1844 1886
1845 1887
1846 _entities.mouseMoveEvent(&mappedEvent, deviceID);1888 _entities.mouseMoveEvent(&mappedEvent, deviceID);
1847 _controllerScriptingInterface.emitMouseMoveEvent(&mappedEvent, deviceID); // send events to any registered scripts1889 _controllerScriptingInterface->emitMouseMoveEvent(&mappedEvent, deviceID); // send events to any registered scripts
1848 1890
1849 // if one of our scripts have asked to capture this event, then stop processing it1891 // if one of our scripts have asked to capture this event, then stop processing it
1850 if (_controllerScriptingInterface.isMouseCaptured()) {1892 if (_controllerScriptingInterface->isMouseCaptured()) {
1851 return;1893 return;
1852 }1894 }
1853 1895
@@ -1872,10 +1914,10 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { · 18 unmodified lines
1872 _entities.mousePressEvent(&mappedEvent, deviceID);1914 _entities.mousePressEvent(&mappedEvent, deviceID);
1873 }1915 }
1874 1916
1875 _controllerScriptingInterface.emitMousePressEvent(&mappedEvent); // send events to any registered scripts1917 _controllerScriptingInterface->emitMousePressEvent(&mappedEvent); // send events to any registered scripts
1876 1918
1877 // if one of our scripts have asked to capture this event, then stop processing it1919 // if one of our scripts have asked to capture this event, then stop processing it
1878 if (_controllerScriptingInterface.isMouseCaptured()) {1920 if (_controllerScriptingInterface->isMouseCaptured()) {
1879 return;1921 return;
1880 }1922 }
1881 1923
@@ -1897,11 +1939,11 @@ void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { · 15 unmodified lines
1897 1939
1898void Application::mouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID) {1940void Application::mouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID) {
1899 // if one of our scripts have asked to capture this event, then stop processing it1941 // if one of our scripts have asked to capture this event, then stop processing it
1900 if (_controllerScriptingInterface.isMouseCaptured()) {1942 if (_controllerScriptingInterface->isMouseCaptured()) {
1901 return;1943 return;
1902 }1944 }
1903 1945
1904 _controllerScriptingInterface.emitMouseDoublePressEvent(event);1946 _controllerScriptingInterface->emitMouseDoublePressEvent(event);
1905}1947}
1906 1948
1907void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {1949void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
@@ -1917,10 +1959,10 @@ void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { · 9 unmodified lines
1917 _entities.mouseReleaseEvent(&mappedEvent, deviceID);1959 _entities.mouseReleaseEvent(&mappedEvent, deviceID);
1918 }1960 }
1919 1961
1920 _controllerScriptingInterface.emitMouseReleaseEvent(&mappedEvent); // send events to any registered scripts1962 _controllerScriptingInterface->emitMouseReleaseEvent(&mappedEvent); // send events to any registered scripts
1921 1963
1922 // if one of our scripts have asked to capture this event, then stop processing it1964 // if one of our scripts have asked to capture this event, then stop processing it
1923 if (_controllerScriptingInterface.isMouseCaptured()) {1965 if (_controllerScriptingInterface->isMouseCaptured()) {
1924 return;1966 return;
1925 }1967 }
1926 1968
@@ -1943,12 +1985,12 @@ void Application::touchUpdateEvent(QTouchEvent* event) { · 16 unmodified lines
1943 1985
1944 if (event->type() == QEvent::TouchUpdate) {1986 if (event->type() == QEvent::TouchUpdate) {
1945 TouchEvent thisEvent(*event, _lastTouchEvent);1987 TouchEvent thisEvent(*event, _lastTouchEvent);
1946 _controllerScriptingInterface.emitTouchUpdateEvent(thisEvent); // send events to any registered scripts1988 _controllerScriptingInterface->emitTouchUpdateEvent(thisEvent); // send events to any registered scripts
1947 _lastTouchEvent = thisEvent;1989 _lastTouchEvent = thisEvent;
1948 }1990 }
1949 1991
1950 // if one of our scripts have asked to capture this event, then stop processing it1992 // if one of our scripts have asked to capture this event, then stop processing it
1951 if (_controllerScriptingInterface.isTouchCaptured()) {1993 if (_controllerScriptingInterface->isTouchCaptured()) {
1952 return;1994 return;
1953 }1995 }
1954 1996
@@ -1960,13 +2002,13 @@ void Application::touchUpdateEvent(QTouchEvent* event) { · 5 unmodified lines
1960void Application::touchBeginEvent(QTouchEvent* event) {2002void Application::touchBeginEvent(QTouchEvent* event) {
1961 _altPressed = false;2003 _altPressed = false;
1962 TouchEvent thisEvent(*event); // on touch begin, we don't compare to last event2004 TouchEvent thisEvent(*event); // on touch begin, we don't compare to last event
1963 _controllerScriptingInterface.emitTouchBeginEvent(thisEvent); // send events to any registered scripts2005 _controllerScriptingInterface->emitTouchBeginEvent(thisEvent); // send events to any registered scripts
1964 2006
1965 _lastTouchEvent = thisEvent; // and we reset our last event to this event before we call our update2007 _lastTouchEvent = thisEvent; // and we reset our last event to this event before we call our update
1966 touchUpdateEvent(event);2008 touchUpdateEvent(event);
1967 2009
1968 // if one of our scripts have asked to capture this event, then stop processing it2010 // if one of our scripts have asked to capture this event, then stop processing it
1969 if (_controllerScriptingInterface.isTouchCaptured()) {2011 if (_controllerScriptingInterface->isTouchCaptured()) {
1970 return;2012 return;
1971 }2013 }
1972 2014
@@ -1979,11 +2021,11 @@ void Application::touchBeginEvent(QTouchEvent* event) { · 6 unmodified lines
1979void Application::touchEndEvent(QTouchEvent* event) {2021void Application::touchEndEvent(QTouchEvent* event) {
1980 _altPressed = false;2022 _altPressed = false;
1981 TouchEvent thisEvent(*event, _lastTouchEvent);2023 TouchEvent thisEvent(*event, _lastTouchEvent);
1982 _controllerScriptingInterface.emitTouchEndEvent(thisEvent); // send events to any registered scripts2024 _controllerScriptingInterface->emitTouchEndEvent(thisEvent); // send events to any registered scripts
1983 _lastTouchEvent = thisEvent;2025 _lastTouchEvent = thisEvent;
1984 2026
1985 // if one of our scripts have asked to capture this event, then stop processing it2027 // if one of our scripts have asked to capture this event, then stop processing it
1986 if (_controllerScriptingInterface.isTouchCaptured()) {2028 if (_controllerScriptingInterface->isTouchCaptured()) {
1987 return;2029 return;
1988 }2030 }
1989 2031
@@ -1996,10 +2038,10 @@ void Application::touchEndEvent(QTouchEvent* event) { · 6 unmodified lines
1996 2038
1997void Application::wheelEvent(QWheelEvent* event) {2039void Application::wheelEvent(QWheelEvent* event) {
1998 _altPressed = false;2040 _altPressed = false;
1999 _controllerScriptingInterface.emitWheelEvent(event); // send events to any registered scripts2041 _controllerScriptingInterface->emitWheelEvent(event); // send events to any registered scripts
2000 2042
2001 // if one of our scripts have asked to capture this event, then stop processing it2043 // if one of our scripts have asked to capture this event, then stop processing it
2002 if (_controllerScriptingInterface.isWheelCaptured()) {2044 if (_controllerScriptingInterface->isWheelCaptured()) {
2003 return;2045 return;
2004 }2046 }
2005 2047
@@ -2164,7 +2206,7 @@ float Application::getAvatarSimrate() { · 158 unmodified lines
2164}2206}
2165 2207
2166void Application::setLowVelocityFilter(bool lowVelocityFilter) {2208void Application::setLowVelocityFilter(bool lowVelocityFilter) {
2167 InputDevice::setLowVelocityFilter(lowVelocityFilter);2209 controller::InputDevice::setLowVelocityFilter(lowVelocityFilter);
2168}2210}
2169 2211
2170ivec2 Application::getMouse() const {2212ivec2 Application::getMouse() const {
@@ -2694,13 +2736,9 @@ void Application::update(float deltaTime) { · 523 unmodified lines
2694 userInputMapper->setSensorToWorldMat(myAvatar->getSensorToWorldMatrix());2736 userInputMapper->setSensorToWorldMat(myAvatar->getSensorToWorldMatrix());
2695 userInputMapper->update(deltaTime);2737 userInputMapper->update(deltaTime);
2696 2738
2697 // This needs to go after userInputMapper->update() because of the keyboard
2698 bool jointsCaptured = false;2739 bool jointsCaptured = false;
2699 auto inputPlugins = PluginManager::getInstance()->getInputPlugins();2740 for (auto inputPlugin : PluginManager::getInstance()->getInputPlugins()) {
2700 foreach(auto inputPlugin, inputPlugins) {2741 if (inputPlugin->isActive()) {
2701 QString name = inputPlugin->getName();
2702 QAction* action = Menu::getInstance()->getActionForOption(name);
2703 if (action && action->isChecked()) {
2704 inputPlugin->pluginUpdate(deltaTime, jointsCaptured);2742 inputPlugin->pluginUpdate(deltaTime, jointsCaptured);
2705 if (inputPlugin->isJointController()) {2743 if (inputPlugin->isJointController()) {
2706 jointsCaptured = true;2744 jointsCaptured = true;
@@ -2708,19 +2746,14 @@ void Application::update(float deltaTime) { · 1 unmodified line
2708 }2746 }
2709 }2747 }
2710 2748
2711 // Dispatch input events
2712 _controllerScriptingInterface.updateInputControllers();
2713
2714 // Transfer the user inputs to the driveKeys2749 // Transfer the user inputs to the driveKeys
2750 // FIXME can we drop drive keys and just have the avatar read the action states directly?
2715 myAvatar->clearDriveKeys();2751 myAvatar->clearDriveKeys();
2716 if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT) {2752 if (_myCamera.getMode() != CAMERA_MODE_INDEPENDENT) {
2717 if (!_controllerScriptingInterface.areActionsCaptured()) {2753 if (!_controllerScriptingInterface->areActionsCaptured()) {
2718 myAvatar->setDriveKeys(FWD, userInputMapper->getActionState(UserInputMapper::LONGITUDINAL_FORWARD));2754 myAvatar->setDriveKeys(TRANSLATE_Z, -1.0f * userInputMapper->getActionState(controller::Action::TRANSLATE_Z));
2719 myAvatar->setDriveKeys(BACK, userInputMapper->getActionState(UserInputMapper::LONGITUDINAL_BACKWARD));2755 myAvatar->setDriveKeys(TRANSLATE_Y, userInputMapper->getActionState(controller::Action::TRANSLATE_Y));
2720 myAvatar->setDriveKeys(UP, userInputMapper->getActionState(UserInputMapper::VERTICAL_UP));2756 myAvatar->setDriveKeys(TRANSLATE_X, userInputMapper->getActionState(controller::Action::TRANSLATE_X));
2721 myAvatar->setDriveKeys(DOWN, userInputMapper->getActionState(UserInputMapper::VERTICAL_DOWN));
2722 myAvatar->setDriveKeys(LEFT, userInputMapper->getActionState(UserInputMapper::LATERAL_LEFT));
2723 myAvatar->setDriveKeys(RIGHT, userInputMapper->getActionState(UserInputMapper::LATERAL_RIGHT));
2724 if (deltaTime > FLT_EPSILON) {2757 if (deltaTime > FLT_EPSILON) {
2725 // For rotations what we really want are meausures of "angles per second" (in order to prevent 2758 // For rotations what we really want are meausures of "angles per second" (in order to prevent
2726 // fps-dependent spin rates) so we need to scale the units of the controller contribution.2759 // fps-dependent spin rates) so we need to scale the units of the controller contribution.
@@ -2728,25 +2761,24 @@ void Application::update(float deltaTime) { · 1 unmodified line
2728 // controllers to provide a delta_per_second value rather than a raw delta.)2761 // controllers to provide a delta_per_second value rather than a raw delta.)
2729 const float EXPECTED_FRAME_RATE = 60.0f;2762 const float EXPECTED_FRAME_RATE = 60.0f;
2730 float timeFactor = EXPECTED_FRAME_RATE * deltaTime;2763 float timeFactor = EXPECTED_FRAME_RATE * deltaTime;
2731 myAvatar->setDriveKeys(ROT_UP, userInputMapper->getActionState(UserInputMapper::PITCH_UP) / timeFactor);2764 myAvatar->setDriveKeys(PITCH, -1.0f * userInputMapper->getActionState(controller::Action::PITCH) / timeFactor);
2732 myAvatar->setDriveKeys(ROT_DOWN, userInputMapper->getActionState(UserInputMapper::PITCH_DOWN) / timeFactor);2765 myAvatar->setDriveKeys(YAW, -1.0f * userInputMapper->getActionState(controller::Action::YAW) / timeFactor);
2733 myAvatar->setDriveKeys(ROT_LEFT, userInputMapper->getActionState(UserInputMapper::YAW_LEFT) / timeFactor);2766 myAvatar->setDriveKeys(STEP_YAW, -1.0f * userInputMapper->getActionState(controller::Action::STEP_YAW) / timeFactor);
2734 myAvatar->setDriveKeys(ROT_RIGHT, userInputMapper->getActionState(UserInputMapper::YAW_RIGHT) / timeFactor);
2735 }2767 }
2736 }2768 }
2737 myAvatar->setDriveKeys(BOOM_IN, userInputMapper->getActionState(UserInputMapper::BOOM_IN));2769 myAvatar->setDriveKeys(ZOOM, userInputMapper->getActionState(controller::Action::TRANSLATE_CAMERA_Z));
2738 myAvatar->setDriveKeys(BOOM_OUT, userInputMapper->getActionState(UserInputMapper::BOOM_OUT));
2739 }2770 }
2740 UserInputMapper::PoseValue leftHand = userInputMapper->getPoseState(UserInputMapper::LEFT_HAND);2771
2741 UserInputMapper::PoseValue rightHand = userInputMapper->getPoseState(UserInputMapper::RIGHT_HAND);2772 controller::Pose leftHand = userInputMapper->getPoseState(controller::Action::LEFT_HAND);
2773 controller::Pose rightHand = userInputMapper->getPoseState(controller::Action::RIGHT_HAND);
2742 Hand* hand = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHand();2774 Hand* hand = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHand();
2743 setPalmData(hand, leftHand, deltaTime, LEFT_HAND_INDEX, userInputMapper->getActionState(UserInputMapper::LEFT_HAND_CLICK));2775 setPalmData(hand, leftHand, deltaTime, HandData::LeftHand, userInputMapper->getActionState(controller::Action::LEFT_HAND_CLICK));
2744 setPalmData(hand, rightHand, deltaTime, RIGHT_HAND_INDEX, userInputMapper->getActionState(UserInputMapper::RIGHT_HAND_CLICK));2776 setPalmData(hand, rightHand, deltaTime, HandData::RightHand, userInputMapper->getActionState(controller::Action::RIGHT_HAND_CLICK));
2745 if (Menu::getInstance()->isOptionChecked(MenuOption::EnableHandMouseInput)) {2777 if (Menu::getInstance()->isOptionChecked(MenuOption::EnableHandMouseInput)) {
2746 emulateMouse(hand, userInputMapper->getActionState(UserInputMapper::LEFT_HAND_CLICK),2778 emulateMouse(hand, userInputMapper->getActionState(controller::Action::LEFT_HAND_CLICK),
2747 userInputMapper->getActionState(UserInputMapper::SHIFT), LEFT_HAND_INDEX);2779 userInputMapper->getActionState(controller::Action::SHIFT), HandData::LeftHand);
2748 emulateMouse(hand, userInputMapper->getActionState(UserInputMapper::RIGHT_HAND_CLICK),2780 emulateMouse(hand, userInputMapper->getActionState(controller::Action::RIGHT_HAND_CLICK),
2749 userInputMapper->getActionState(UserInputMapper::SHIFT), RIGHT_HAND_INDEX);2781 userInputMapper->getActionState(controller::Action::SHIFT), HandData::RightHand);
2750 }2782 }
2751 2783
2752 updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...2784 updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process...
@@ -4807,86 +4839,80 @@ mat4 Application::getHMDSensorPose() const { · 2054 unmodified lines
4807 return mat4();4839 return mat4();
4808}4840}
4809 4841
4810void Application::setPalmData(Hand* hand, UserInputMapper::PoseValue pose, float deltaTime, int index, float triggerValue) {4842void Application::setPalmData(Hand* hand, const controller::Pose& pose, float deltaTime, HandData::Hand whichHand, float triggerValue) {
4811 PalmData* palm;
4812 bool foundHand = false;
4813 for (size_t j = 0; j < hand->getNumPalms(); j++) {
4814 if (hand->getPalms()[j].getSixenseID() == index) {
4815 palm = &(hand->getPalms()[j]);
4816 foundHand = true;
4817 break;
4818 }
4819 }
4820 if (!foundHand) {
4821 PalmData newPalm(hand);
4822 hand->getPalms().push_back(newPalm);
4823 palm = &(hand->getPalms()[hand->getNumPalms() - 1]);
4824 palm->setSixenseID(index);
4825 }
4826
4827 palm->setActive(pose.isValid());
4828 4843
4829 // transform from sensor space, to world space, to avatar model space.4844 // NOTE: the Hand::modifyPalm() will allow the lambda to modify the palm data while ensuring some other user isn't
4830 glm::mat4 poseMat = createMatFromQuatAndPos(pose.getRotation(), pose.getTranslation());4845 // reading or writing to the Palms. This is definitely not the best way of handling this, and I'd like to see more
4831 glm::mat4 sensorToWorldMat = getMyAvatar()->getSensorToWorldMatrix();4846 // of this palm manipulation in the Hand class itself. But unfortunately the Hand and Palm don't knbow about
4832 glm::mat4 modelMat = createMatFromQuatAndPos(getMyAvatar()->getOrientation(), getMyAvatar()->getPosition());4847 // controller::Pose. More work is needed to clean this up.
4833 glm::mat4 objectPose = glm::inverse(modelMat) * sensorToWorldMat * poseMat;4848 hand->modifyPalm(whichHand, [&](PalmData& palm) {
4849 auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
4850 palm.setActive(pose.isValid());
4834 4851
4835 glm::vec3 position = extractTranslation(objectPose);4852 // transform from sensor space, to world space, to avatar model space.
4836 glm::quat rotation = glm::quat_cast(objectPose);4853 glm::mat4 poseMat = createMatFromQuatAndPos(pose.getRotation(), pose.getTranslation());
4854 glm::mat4 sensorToWorldMat = myAvatar->getSensorToWorldMatrix();
4855 glm::mat4 modelMat = createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition());
4856 glm::mat4 objectPose = glm::inverse(modelMat) * sensorToWorldMat * poseMat;
4837 4857
4838 // Compute current velocity from position change4858 glm::vec3 position = extractTranslation(objectPose);
4839 glm::vec3 rawVelocity;4859 glm::quat rotation = glm::quat_cast(objectPose);
4840 if (deltaTime > 0.0f) {4860
4841 rawVelocity = (position - palm->getRawPosition()) / deltaTime;4861 // Compute current velocity from position change
4842 } else {4862 glm::vec3 rawVelocity;
4843 rawVelocity = glm::vec3(0.0f);4863 if (deltaTime > 0.0f) {
4844 }4864 rawVelocity = (position - palm.getRawPosition()) / deltaTime;
4845 palm->setRawVelocity(rawVelocity); // meters/sec4865 } else {
4866 rawVelocity = glm::vec3(0.0f);
4867 }
4868 palm.setRawVelocity(rawVelocity); // meters/sec
4846 4869
4847 // Angular Velocity of Palm4870 // Angular Velocity of Palm
4848 glm::quat deltaRotation = rotation * glm::inverse(palm->getRawRotation());4871 glm::quat deltaRotation = rotation * glm::inverse(palm.getRawRotation());
4849 glm::vec3 angularVelocity(0.0f);4872 glm::vec3 angularVelocity(0.0f);
4850 float rotationAngle = glm::angle(deltaRotation);4873 float rotationAngle = glm::angle(deltaRotation);
4851 if ((rotationAngle > EPSILON) && (deltaTime > 0.0f)) {4874 if ((rotationAngle > EPSILON) && (deltaTime > 0.0f)) {
4852 angularVelocity = glm::normalize(glm::axis(deltaRotation));4875 angularVelocity = glm::normalize(glm::axis(deltaRotation));
4853 angularVelocity *= (rotationAngle / deltaTime);4876 angularVelocity *= (rotationAngle / deltaTime);
4854 palm->setRawAngularVelocity(angularVelocity);4877 palm.setRawAngularVelocity(angularVelocity);
4855 } else {4878 } else {
4856 palm->setRawAngularVelocity(glm::vec3(0.0f));4879 palm.setRawAngularVelocity(glm::vec3(0.0f));
4857 }4880 }
4858 4881
4859 if (InputDevice::getLowVelocityFilter()) {4882 if (controller::InputDevice::getLowVelocityFilter()) {
4860 // Use a velocity sensitive filter to damp small motions and preserve large ones with4883 // Use a velocity sensitive filter to damp small motions and preserve large ones with
4861 // no latency.4884 // no latency.
4862 float velocityFilter = glm::clamp(1.0f - glm::length(rawVelocity), 0.0f, 1.0f);4885 float velocityFilter = glm::clamp(1.0f - glm::length(rawVelocity), 0.0f, 1.0f);
4863 position = palm->getRawPosition() * velocityFilter + position * (1.0f - velocityFilter);4886 position = palm.getRawPosition() * velocityFilter + position * (1.0f - velocityFilter);
4864 rotation = safeMix(palm->getRawRotation(), rotation, 1.0f - velocityFilter);4887 rotation = safeMix(palm.getRawRotation(), rotation, 1.0f - velocityFilter);
4865 }4888 }
4866 palm->setRawPosition(position);4889 palm.setRawPosition(position);
4867 palm->setRawRotation(rotation);4890 palm.setRawRotation(rotation);
4868 4891
4869 // Store the one fingertip in the palm structure so we can track velocity4892 // Store the one fingertip in the palm structure so we can track velocity
4870 const float FINGER_LENGTH = 0.3f; // meters4893 const float FINGER_LENGTH = 0.3f; // meters
4871 const glm::vec3 FINGER_VECTOR(0.0f, FINGER_LENGTH, 0.0f);4894 const glm::vec3 FINGER_VECTOR(0.0f, FINGER_LENGTH, 0.0f);
4872 const glm::vec3 newTipPosition = position + rotation * FINGER_VECTOR;4895 const glm::vec3 newTipPosition = position + rotation * FINGER_VECTOR;
4873 glm::vec3 oldTipPosition = palm->getTipRawPosition();4896 glm::vec3 oldTipPosition = palm.getTipRawPosition();
4874 if (deltaTime > 0.0f) {4897 if (deltaTime > 0.0f) {
4875 palm->setTipVelocity((newTipPosition - oldTipPosition) / deltaTime);4898 palm.setTipVelocity((newTipPosition - oldTipPosition) / deltaTime);
4876 } else {4899 } else {
4877 palm->setTipVelocity(glm::vec3(0.0f));4900 palm.setTipVelocity(glm::vec3(0.0f));
4878 }4901 }
4879 palm->setTipPosition(newTipPosition);4902 palm.setTipPosition(newTipPosition);
4880 palm->setTrigger(triggerValue);4903 palm.setTrigger(triggerValue); // FIXME - we want to get rid of this idea of PalmData having a trigger
4904 });
4881}4905}
4882 4906
4883void Application::emulateMouse(Hand* hand, float click, float shift, int index) {4907void Application::emulateMouse(Hand* hand, float click, float shift, HandData::Hand whichHand) {
4908 auto palms = hand->getCopyOfPalms();
4909
4884 // Locate the palm, if it exists and is active4910 // Locate the palm, if it exists and is active
4885 PalmData* palm;4911 PalmData* palm;
4886 bool foundHand = false;4912 bool foundHand = false;
4887 for (size_t j = 0; j < hand->getNumPalms(); j++) {4913 for (size_t j = 0; j < palms.size(); j++) {
4888 if (hand->getPalms()[j].getSixenseID() == index) {4914 if (palms[j].whichHand() == whichHand) {
4889 palm = &(hand->getPalms()[j]);4915 palm = &(palms[j]);
4890 foundHand = true;4916 foundHand = true;
4891 break;4917 break;
4892 }4918 }
@@ -4898,12 +4924,14 @@ void Application::emulateMouse(Hand* hand, float click, float shift, int index) · 5 unmodified lines
4898 // Process the mouse events4924 // Process the mouse events
4899 QPoint pos;4925 QPoint pos;
4900 4926
4901 unsigned int deviceID = index == 0 ? CONTROLLER_0_EVENT : CONTROLLER_1_EVENT;4927
4928 // FIXME - this mouse emulation stuff needs to be reworked for new controller input plugins
4929 unsigned int deviceID = whichHand == HandData::LeftHand ? CONTROLLER_0_EVENT : CONTROLLER_1_EVENT;
4930 int index = (int)whichHand; // FIXME - hack attack
4902 4931
4903 if (isHMDMode()) {4932 if (isHMDMode()) {
4904 pos = getApplicationCompositor().getPalmClickLocation(palm);4933 pos = getApplicationCompositor().getPalmClickLocation(palm);
4905 }4934 } else {
4906 else {
4907 // Get directon relative to avatar orientation4935 // Get directon relative to avatar orientation
4908 glm::vec3 direction = glm::inverse(getMyAvatar()->getOrientation()) * palm->getFingerDirection();4936 glm::vec3 direction = glm::inverse(getMyAvatar()->getOrientation()) * palm->getFingerDirection();
4909 4937
@@ -4912,7 +4940,7 @@ void Application::emulateMouse(Hand* hand, float click, float shift, int index) · 2 unmodified lines
4912 float yAngle = 0.5f - ((atan2f(direction.z, direction.y) + (float)M_PI_2));4940 float yAngle = 0.5f - ((atan2f(direction.z, direction.y) + (float)M_PI_2));
4913 auto canvasSize = getCanvasSize();4941 auto canvasSize = getCanvasSize();
4914 // Get the pixel range over which the xAngle and yAngle are scaled4942 // Get the pixel range over which the xAngle and yAngle are scaled
4915 float cursorRange = canvasSize.x * InputDevice::getCursorPixelRangeMult();4943 float cursorRange = canvasSize.x * controller::InputDevice::getCursorPixelRangeMult();
4916 4944
4917 pos.setX(canvasSize.x / 2.0f + cursorRange * xAngle);4945 pos.setX(canvasSize.x / 2.0f + cursorRange * xAngle);
4918 pos.setY(canvasSize.y / 2.0f + cursorRange * yAngle);4946 pos.setY(canvasSize.y / 2.0f + cursorRange * yAngle);
interface/src/Application.h+26−17
@@ -14,13 +14,15 @@ · 13 unmodified lines
14 14
15#include <functional>15#include <functional>
16 16
17#include <QApplication>17#include <QtCore/QHash>
18#include <QHash>18#include <QtCore/QPointer>
19#include <QImage>19#include <QtCore/QSet>
20#include <QPointer>20#include <QtCore/QStringList>
21#include <QSet>21
22#include <QStringList>22#include <QtGui/QImage>
23#include <QUndoStack>23
24#include <QtWidgets/QApplication>
25#include <QtWidgets/QUndoStack>
24 26
25#include <AbstractScriptingServicesInterface.h>27#include <AbstractScriptingServicesInterface.h>
26#include <AbstractViewStateInterface.h>28#include <AbstractViewStateInterface.h>
@@ -69,6 +71,10 @@ class FaceTracker; · 42 unmodified lines
69class MainWindow;71class MainWindow;
70class AssetUpload;72class AssetUpload;
71 73
74namespace controller {
75 class StateController;
76}
77
72#ifdef Q_OS_WIN78#ifdef Q_OS_WIN
73static const UINT UWM_IDENTIFY_INSTANCES =79static const UINT UWM_IDENTIFY_INSTANCES =
74 RegisterWindowMessage("UWM_IDENTIFY_INSTANCES_{8AB82783-B74A-4258-955B-8188C22AA0D6}_" + qgetenv("USERNAME"));80 RegisterWindowMessage("UWM_IDENTIFY_INSTANCES_{8AB82783-B74A-4258-955B-8188C22AA0D6}_" + qgetenv("USERNAME"));
@@ -161,7 +167,7 @@ public: · 86 unmodified lines
161 167
162 ToolWindow* getToolWindow() { return _toolWindow ; }168 ToolWindow* getToolWindow() { return _toolWindow ; }
163 169
164 virtual AbstractControllerScriptingInterface* getControllerScriptingInterface() { return &_controllerScriptingInterface; }170 virtual controller::ScriptingInterface* getControllerScriptingInterface() { return _controllerScriptingInterface; }
165 virtual void registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine);171 virtual void registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine);
166 172
167 QImage renderAvatarBillboard(RenderArgs* renderArgs);173 QImage renderAvatarBillboard(RenderArgs* renderArgs);
@@ -350,8 +356,8 @@ private: · 182 unmodified lines
350 356
351 void update(float deltaTime);357 void update(float deltaTime);
352 358
353 void setPalmData(Hand* hand, UserInputMapper::PoseValue pose, float deltaTime, int index, float triggerValue);359 void setPalmData(Hand* hand, const controller::Pose& pose, float deltaTime, HandData::Hand whichHand, float triggerValue);
354 void emulateMouse(Hand* hand, float click, float shift, int index);360 void emulateMouse(Hand* hand, float click, float shift, HandData::Hand whichHand);
355 361
356 // Various helper functions called during update()362 // Various helper functions called during update()
357 void updateLOD();363 void updateLOD();
@@ -440,7 +446,8 @@ private: · 82 unmodified lines
440 446
441 OctreeQuery _octreeQuery; // NodeData derived class for querying octee cells from octree servers447 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 touchpad449 std::shared_ptr<controller::StateController> _applicationStateDevice; // Default ApplicationDevice reflecting the state of different properties of the session
450 std::shared_ptr<KeyboardMouseDevice> _keyboardMouseDevice; // Default input device, the good old keyboard mouse and maybe touchpad
444 AvatarUpdate* _avatarUpdate {nullptr};451 AvatarUpdate* _avatarUpdate {nullptr};
445 SimpleMovingAverage _avatarSimsPerSecond {10};452 SimpleMovingAverage _avatarSimsPerSecond {10};
446 int _avatarSimsPerSecondReport {0};453 int _avatarSimsPerSecondReport {0};
@@ -474,8 +481,7 @@ private: · 27 unmodified lines
474 481
475 NodeToJurisdictionMap _entityServerJurisdictions;482 NodeToJurisdictionMap _entityServerJurisdictions;
476 NodeToOctreeSceneStats _octreeServerSceneStats;483 NodeToOctreeSceneStats _octreeServerSceneStats;
477 484 ControllerScriptingInterface* _controllerScriptingInterface{ nullptr };
478 ControllerScriptingInterface _controllerScriptingInterface;
479 QPointer<LogDialog> _logDialog;485 QPointer<LogDialog> _logDialog;
480 QPointer<SnapshotShareDialog> _snapshotShareDialog;486 QPointer<SnapshotShareDialog> _snapshotShareDialog;
481 487
@@ -521,10 +527,13 @@ private: · 39 unmodified lines
521 ApplicationCompositor _compositor;527 ApplicationCompositor _compositor;
522 OverlayConductor _overlayConductor;528 OverlayConductor _overlayConductor;
523 529
524 int _oldHandMouseX[2];530
525 int _oldHandMouseY[2];531 // FIXME - Hand Controller to mouse emulation helpers. This is crufty and should be moved
526 bool _oldHandLeftClick[2];532 // into the input plugins or something.
527 bool _oldHandRightClick[2];533 int _oldHandMouseX[(int)HandData::NUMBER_OF_HANDS];
534 int _oldHandMouseY[(int)HandData::NUMBER_OF_HANDS];
535 bool _oldHandLeftClick[(int)HandData::NUMBER_OF_HANDS];
536 bool _oldHandRightClick[(int)HandData::NUMBER_OF_HANDS];
528 537
529 DialogsManagerScriptingInterface* _dialogsManagerScriptingInterface = new DialogsManagerScriptingInterface();538 DialogsManagerScriptingInterface* _dialogsManagerScriptingInterface = new DialogsManagerScriptingInterface();
530 539
interface/src/Menu.cpp+2−0
@@ -464,11 +464,13 @@ Menu::Menu() { · 463 unmodified lines
464 addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::MeshVisible, 0, true,464 addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::MeshVisible, 0, true,
465 avatar, SLOT(setEnableMeshVisible(bool)));465 avatar, SLOT(setEnableMeshVisible(bool)));
466 addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false);466 addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false);
467#if 0
467 addCheckableActionToQMenuAndActionHash(avatarDebugMenu,468 addCheckableActionToQMenuAndActionHash(avatarDebugMenu,
468 MenuOption::Connexion,469 MenuOption::Connexion,
469 0, false,470 0, false,
470 &ConnexionClient::getInstance(),471 &ConnexionClient::getInstance(),
471 SLOT(toggleConnexion(bool)));472 SLOT(toggleConnexion(bool)));
473#endif
472 addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ComfortMode, 0, true);474 addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ComfortMode, 0, true);
473 475
474 MenuWrapper* handOptionsMenu = developerMenu->addMenu("Hands");476 MenuWrapper* handOptionsMenu = developerMenu->addMenu("Hands");
interface/src/avatar/Avatar.cpp+0−32
@@ -1177,22 +1177,6 @@ glm::vec3 Avatar::getLeftPalmPosition() { · 1176 unmodified lines
1177 return leftHandPosition;1177 return leftHandPosition;
1178}1178}
1179 1179
1180glm::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
1188glm::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
1196glm::quat Avatar::getLeftPalmRotation() {1180glm::quat Avatar::getLeftPalmRotation() {
1197 glm::quat leftRotation;1181 glm::quat leftRotation;
1198 getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getLeftHandJointIndex(), leftRotation);1182 getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getLeftHandJointIndex(), leftRotation);
@@ -1208,22 +1192,6 @@ glm::vec3 Avatar::getRightPalmPosition() { · 9 unmodified lines
1208 return rightHandPosition;1192 return rightHandPosition;
1209}1193}
1210 1194
1211glm::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
1219glm::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
1227glm::quat Avatar::getRightPalmRotation() {1195glm::quat Avatar::getRightPalmRotation() {
1228 glm::quat rightRotation;1196 glm::quat rightRotation;
1229 getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightRotation);1197 getSkeletonModel().getJointRotationInWorldFrame(getSkeletonModel().getRightHandJointIndex(), rightRotation);
interface/src/avatar/Avatar.h+2−19
@@ -43,21 +43,6 @@ static const float BILLBOARD_DISTANCE = 5.56f; // meters · 42 unmodified lines
43extern const float CHAT_MESSAGE_SCALE;43extern const float CHAT_MESSAGE_SCALE;
44extern const float CHAT_MESSAGE_HEIGHT;44extern const float CHAT_MESSAGE_HEIGHT;
45 45
46enum 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
62enum ScreenTintLayer {47enum ScreenTintLayer {
63 SCREEN_TINT_BEFORE_LANDSCAPE = 0,48 SCREEN_TINT_BEFORE_LANDSCAPE = 0,
@@ -175,13 +160,11 @@ public: · 111 unmodified lines
175 AvatarMotionState* getMotionState() { return _motionState; }160 AvatarMotionState* getMotionState() { return _motionState; }
176 161
177public slots:162public slots:
163
164 // FIXME - these should be migrated to use Pose data instead
178 glm::vec3 getLeftPalmPosition();165 glm::vec3 getLeftPalmPosition();
179 glm::vec3 getLeftPalmVelocity();
180 glm::vec3 getLeftPalmAngularVelocity();
181 glm::quat getLeftPalmRotation();166 glm::quat getLeftPalmRotation();
182 glm::vec3 getRightPalmPosition();167 glm::vec3 getRightPalmPosition();
183 glm::vec3 getRightPalmVelocity();
184 glm::vec3 getRightPalmAngularVelocity();
185 glm::quat getRightPalmRotation();168 glm::quat getRightPalmRotation();
186 169
187protected:170protected:
interface/src/avatar/AvatarManager.cpp+10−0
@@ -248,6 +248,16 @@ QVector<AvatarManager::LocalLight> AvatarManager::getLocalLights() const { · 247 unmodified lines
248 return _localLights;248 return _localLights;
249}249}
250 250
251QVector<QUuid> AvatarManager::getAvatarIdentifiers() {
252 QReadLocker locker(&_hashLock);
253 return _avatarHash.keys().toVector();
254}
255AvatarData* 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
251void AvatarManager::getObjectsToDelete(VectorOfMotionStates& result) {261void AvatarManager::getObjectsToDelete(VectorOfMotionStates& result) {
252 result.clear();262 result.clear();
253 result.swap(_motionStatesToDelete);263 result.swap(_motionStatesToDelete);
interface/src/avatar/AvatarManager.h+4−0
@@ -52,6 +52,10 @@ public: · 51 unmodified lines
52 52
53 Q_INVOKABLE void setLocalLights(const QVector<AvatarManager::LocalLight>& localLights);53 Q_INVOKABLE void setLocalLights(const QVector<AvatarManager::LocalLight>& localLights);
54 Q_INVOKABLE QVector<AvatarManager::LocalLight> getLocalLights() const;54 Q_INVOKABLE QVector<AvatarManager::LocalLight> getLocalLights() const;
55 // Currently, your own avatar will be included as the null avatar id.
56 Q_INVOKABLE QVector<QUuid> getAvatarIdentifiers();
57 Q_INVOKABLE AvatarData* getAvatar(QUuid avatarID);
58
55 59
56 void getObjectsToDelete(VectorOfMotionStates& motionStates);60 void getObjectsToDelete(VectorOfMotionStates& motionStates);
57 void getObjectsToAdd(VectorOfMotionStates& motionStates);61 void getObjectsToAdd(VectorOfMotionStates& motionStates);
interface/src/avatar/Hand.cpp+5−11
@@ -31,13 +31,7 @@ Hand::Hand(Avatar* owningAvatar) : · 30 unmodified lines
31}31}
32 32
33void Hand::simulate(float deltaTime, bool isMine) {33void Hand::simulate(float deltaTime, bool isMine) {
34 if (isMine) {34 // nothing to do here
35 // Iterate hand controllers, take actions as needed
36 for (size_t i = 0; i < getNumPalms(); ++i) {
37 PalmData& palm = getPalms()[i];
38 palm.setLastControllerButtons(palm.getControllerButtons());
39 }
40 }
41}35}
42 36
43void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) {37void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) {
@@ -53,10 +47,11 @@ void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) { · 9 unmodified lines
53 const glm::vec3 grayColor(0.5f);47 const glm::vec3 grayColor(0.5f);
54 const float SPHERE_RADIUS = 0.03f * avatarScale;48 const float SPHERE_RADIUS = 0.03f * avatarScale;
55 49
50 auto palms = getCopyOfPalms();
51
56 gpu::Batch& batch = *renderArgs->_batch;52 gpu::Batch& batch = *renderArgs->_batch;
57 if (isMine) {53 if (isMine) {
58 for (size_t i = 0; i < getNumPalms(); i++) {54 for (const auto& palm : palms) {
59 PalmData& palm = getPalms()[i];
60 if (!palm.isActive()) {55 if (!palm.isActive()) {
61 continue;56 continue;
62 }57 }
@@ -82,8 +77,7 @@ void Hand::renderHandTargets(RenderArgs* renderArgs, bool isMine) { · 19 unmodified lines
82 const float AXIS_LENGTH = 10.0f * SPHERE_RADIUS;77 const float AXIS_LENGTH = 10.0f * SPHERE_RADIUS;
83 78
84 // Draw the coordinate frames of the hand targets79 // Draw the coordinate frames of the hand targets
85 for (size_t i = 0; i < getNumPalms(); ++i) {80 for (const auto& palm : palms) {
86 PalmData& palm = getPalms()[i];
87 if (palm.isActive()) {81 if (palm.isActive()) {
88 glm::vec3 root = palm.getPosition();82 glm::vec3 root = palm.getPosition();
89 83
interface/src/avatar/MyAvatar.cpp+111−86
@@ -53,6 +53,7 @@ · 52 unmodified lines
53 53
54using namespace std;54using namespace std;
55 55
56static quint64 COMFORT_MODE_PULSE_TIMING = USECS_PER_SECOND / 2; // turn once per half second
56const glm::vec3 DEFAULT_UP_DIRECTION(0.0f, 1.0f, 0.0f);57const glm::vec3 DEFAULT_UP_DIRECTION(0.0f, 1.0f, 0.0f);
57const float YAW_SPEED = 150.0f; // degrees/sec58const float YAW_SPEED = 150.0f; // degrees/sec
58const float PITCH_SPEED = 100.0f; // degrees/sec59const float PITCH_SPEED = 100.0f; // degrees/sec
@@ -246,8 +247,31 @@ void MyAvatar::simulate(float deltaTime) { · 187 unmodified lines
246 247
247 {248 {
248 PerformanceTimer perfTimer("transform");249 PerformanceTimer perfTimer("transform");
250 bool stepAction = false;
251 // When there are no step values, we zero out the last step pulse.
252 // This allows a user to do faster snapping by tapping a control
253 for (int i = STEP_TRANSLATE_X; !stepAction && i <= STEP_YAW; ++i) {
254 if (_driveKeys[i] != 0.0f) {
255 stepAction = true;
256 }
257 }
258 quint64 now = usecTimestampNow();
259 quint64 pulseDeltaTime = now - _lastStepPulse;
260 if (!stepAction) {
261 _lastStepPulse = 0;
262 }
263
264 if (stepAction && pulseDeltaTime > COMFORT_MODE_PULSE_TIMING) {
265 _pulseUpdate = true;
266 }
267
249 updateOrientation(deltaTime);268 updateOrientation(deltaTime);
250 updatePosition(deltaTime);269 updatePosition(deltaTime);
270
271 if (_pulseUpdate) {
272 _lastStepPulse = now;
273 _pulseUpdate = false;
274 }
251 }275 }
252 276
253 {277 {
@@ -531,6 +555,50 @@ void MyAvatar::updateFromTrackers(float deltaTime) { · 277 unmodified lines
531}555}
532 556
533 557
558glm::vec3 MyAvatar::getLeftHandPosition() const {
559 auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand);
560 return palmData.isValid() ? palmData.getPosition() : glm::vec3(0.0f);
561}
562
563glm::vec3 MyAvatar::getRightHandPosition() const {
564 auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand);
565 return palmData.isValid() ? palmData.getPosition() : glm::vec3(0.0f);
566}
567
568glm::vec3 MyAvatar::getLeftHandTipPosition() const {
569 auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand);
570 return palmData.isValid() ? palmData.getTipPosition() : glm::vec3(0.0f);
571}
572
573glm::vec3 MyAvatar::getRightHandTipPosition() const {
574 auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand);
575 return palmData.isValid() ? palmData.getTipPosition() : glm::vec3(0.0f);
576}
577
578controller::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
584controller::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
590controller::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
596controller::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// virtual602// virtual
535void MyAvatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {603void MyAvatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
536 // don't render if we've been asked to disable local rendering604 // 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
1534void MyAvatar::updateOrientation(float deltaTime) {1602void MyAvatar::updateOrientation(float deltaTime) {
1535 // Smoothly rotate body with arrow keys1603 // Smoothly rotate body with arrow keys
1536 float targetSpeed = 0.0f;1604 float targetSpeed = _driveKeys[YAW] * YAW_SPEED;
1537 1605 if (targetSpeed != 0.0f) {
1538 // FIXME - this comfort mode code is a total hack, remove it when we have new input mapping1606 const float ROTATION_RAMP_TIMESCALE = 0.1f;
1539 bool isComfortMode = Menu::getInstance()->isOptionChecked(MenuOption::ComfortMode);1607 float blend = deltaTime / ROTATION_RAMP_TIMESCALE;
1540 bool isHMDMode = qApp->getAvatarUpdater()->isHMDMode();1608 if (blend > 1.0f) {
1541 1609 blend = 1.0f;
1542 if (!isHMDMode || !isComfortMode) {
1543 targetSpeed = (_driveKeys[ROT_LEFT] - _driveKeys[ROT_RIGHT]) * YAW_SPEED;
1544
1545 if (targetSpeed != 0.0f) {
1546 const float ROTATION_RAMP_TIMESCALE = 0.1f;
1547 float blend = deltaTime / ROTATION_RAMP_TIMESCALE;
1548 if (blend > 1.0f) {
1549 blend = 1.0f;
1550 }
1551 _bodyYawDelta = (1.0f - blend) * _bodyYawDelta + blend * targetSpeed;
1552 } else if (_bodyYawDelta != 0.0f) {
1553 // attenuate body rotation speed
1554 const float ROTATION_DECAY_TIMESCALE = 0.05f;
1555 float attenuation = 1.0f - deltaTime / ROTATION_DECAY_TIMESCALE;
1556 if (attenuation < 0.0f) {
1557 attenuation = 0.0f;
1558 }
1559 _bodyYawDelta *= attenuation;
1560
1561 float MINIMUM_ROTATION_RATE = 2.0f;
1562 if (fabsf(_bodyYawDelta) < MINIMUM_ROTATION_RATE) {
1563 _bodyYawDelta = 0.0f;
1564 }
1565 }1610 }
1611 _bodyYawDelta = (1.0f - blend) * _bodyYawDelta + blend * targetSpeed;
1612 } else if (_bodyYawDelta != 0.0f) {
1613 // attenuate body rotation speed
1614 const float ROTATION_DECAY_TIMESCALE = 0.05f;
1615 float attenuation = 1.0f - deltaTime / ROTATION_DECAY_TIMESCALE;
1616 if (attenuation < 0.0f) {
1617 attenuation = 0.0f;
1618 }
1619 _bodyYawDelta *= attenuation;
1566 1620
1567 // update body orientation by movement inputs1621 float MINIMUM_ROTATION_RATE = 2.0f;
1568 setOrientation(getOrientation() *1622 if (fabsf(_bodyYawDelta) < MINIMUM_ROTATION_RATE) {
1569 glm::quat(glm::radians(glm::vec3(0.0f, _bodyYawDelta * deltaTime, 0.0f))));1623 _bodyYawDelta = 0.0f;
1570 1624 }
1571 } else {1625 }
1572 // Comfort Mode: If you press any of the left/right rotation drive keys or input, you'll
1573 // get an instantaneous 15 degree turn. If you keep holding the key down you'll get another
1574 // snap turn every half second.
1575 _bodyYawDelta = 0.0f;
1576
1577 static quint64 lastPulse = 0;
1578 quint64 now = usecTimestampNow();
1579 quint64 COMFORT_MODE_PULSE_TIMING = USECS_PER_SECOND / 2; // turn once per half second
1580
1581 float driveLeft = _driveKeys[ROT_LEFT];
1582 float driveRight= _driveKeys[ROT_RIGHT];
1583
1584 if ((driveLeft != 0.0f || driveRight != 0.0f) && (now - lastPulse > COMFORT_MODE_PULSE_TIMING)) {
1585 lastPulse = now;
1586 1626
1587 const float SNAP_TURN_DELTA = 15.0f; // degrees1627 float totalBodyYaw = _bodyYawDelta * deltaTime;
1588 float direction = (driveLeft - driveRight) < 0.0f ? -1.0f : 1.0f;
1589 float turnAmount = direction * SNAP_TURN_DELTA;
1590 1628
1591 // update body orientation by movement inputs
1592 setOrientation(getOrientation() *
1593 glm::quat(glm::radians(glm::vec3(0.0f, turnAmount, 0.0f))));
1594 1629
1595 }1630 // Comfort Mode: If you press any of the left/right rotation drive keys or input, you'll
1631 // get an instantaneous 15 degree turn. If you keep holding the key down you'll get another
1632 // snap turn every half second.
1633 quint64 now = usecTimestampNow();
1634 if (_driveKeys[STEP_YAW] != 0.0f && now - _lastStepPulse > COMFORT_MODE_PULSE_TIMING) {
1635 totalBodyYaw += _driveKeys[STEP_YAW];
1596 }1636 }
1597 1637
1598 getHead()->setBasePitch(getHead()->getBasePitch() + (_driveKeys[ROT_UP] - _driveKeys[ROT_DOWN]) * PITCH_SPEED * deltaTime);1638 // update body orientation by movement inputs
1639 setOrientation(getOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, totalBodyYaw, 0.0f))));
1640
1641 getHead()->setBasePitch(getHead()->getBasePitch() + _driveKeys[PITCH] * PITCH_SPEED * deltaTime);
1599 1642
1600 if (qApp->getAvatarUpdater()->isHMDMode()) {1643 if (qApp->getAvatarUpdater()->isHMDMode()) {
1601 glm::quat orientation = glm::quat_cast(getSensorToWorldMatrix()) * getHMDSensorOrientation();1644 glm::quat orientation = glm::quat_cast(getSensorToWorldMatrix()) * getHMDSensorOrientation();
@@ -1655,14 +1698,18 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe · 53 unmodified lines
1655 float motorEfficiency = glm::clamp(deltaTime / timescale, 0.0f, 1.0f);1698 float motorEfficiency = glm::clamp(deltaTime / timescale, 0.0f, 1.0f);
1656 1699
1657 glm::vec3 newLocalVelocity = localVelocity;1700 glm::vec3 newLocalVelocity = localVelocity;
1658 float keyboardInput = fabsf(_driveKeys[FWD] - _driveKeys[BACK]) +1701 float stepControllerInput = fabsf(_driveKeys[STEP_TRANSLATE_Z]) + fabsf(_driveKeys[STEP_TRANSLATE_Z]) + fabsf(_driveKeys[STEP_TRANSLATE_Z]);
1659 (fabsf(_driveKeys[RIGHT] - _driveKeys[LEFT])) +1702 quint64 now = usecTimestampNow();
1660 fabsf(_driveKeys[UP] - _driveKeys[DOWN]);1703 // FIXME how do I implement step translation as well?
1704 if (stepControllerInput && now - _lastStepPulse > COMFORT_MODE_PULSE_TIMING) {
1705 }
1706
1707 float keyboardInput = fabsf(_driveKeys[TRANSLATE_Z]) + fabsf(_driveKeys[TRANSLATE_X]) + fabsf(_driveKeys[TRANSLATE_Y]);
1661 if (keyboardInput) {1708 if (keyboardInput) {
1662 // Compute keyboard input1709 // Compute keyboard input
1663 glm::vec3 front = (_driveKeys[FWD] - _driveKeys[BACK]) * IDENTITY_FRONT;1710 glm::vec3 front = (_driveKeys[TRANSLATE_Z]) * IDENTITY_FRONT;
1664 glm::vec3 right = (_driveKeys[RIGHT] - _driveKeys[LEFT]) * IDENTITY_RIGHT;1711 glm::vec3 right = (_driveKeys[TRANSLATE_X]) * IDENTITY_RIGHT;
1665 glm::vec3 up = (_driveKeys[UP] - _driveKeys[DOWN]) * IDENTITY_UP;1712 glm::vec3 up = (_driveKeys[TRANSLATE_Y]) * IDENTITY_UP;
1666 1713
1667 glm::vec3 direction = front + right + up;1714 glm::vec3 direction = front + right + up;
1668 float directionLength = glm::length(direction);1715 float directionLength = glm::length(direction);
@@ -1713,7 +1760,7 @@ glm::vec3 MyAvatar::applyKeyboardMotor(float deltaTime, const glm::vec3& localVe · 44 unmodified lines
1713 }1760 }
1714 }1761 }
1715 1762
1716 float boomChange = _driveKeys[BOOM_OUT] - _driveKeys[BOOM_IN];1763 float boomChange = _driveKeys[ZOOM];
1717 _boomLength += 2.0f * _boomLength * boomChange + boomChange * boomChange;1764 _boomLength += 2.0f * _boomLength * boomChange + boomChange * boomChange;
1718 _boomLength = glm::clamp<float>(_boomLength, ZOOM_MIN, ZOOM_MAX);1765 _boomLength = glm::clamp<float>(_boomLength, ZOOM_MIN, ZOOM_MAX);
1719 1766
@@ -1915,28 +1962,6 @@ void MyAvatar::updateMotionBehaviorFromMenu() { · 195 unmodified lines
1915 _characterController.setEnabled(menu->isOptionChecked(MenuOption::EnableCharacterController));1962 _characterController.setEnabled(menu->isOptionChecked(MenuOption::EnableCharacterController));
1916}1963}
1917 1964
1918//Renders sixense laser pointers for UI selection with controllers
1919void MyAvatar::renderLaserPointers(gpu::Batch& batch) {
1920 const float PALM_TIP_ROD_RADIUS = 0.002f;
1921
1922 //If the Oculus is enabled, we will draw a blue cursor ray
1923
1924 for (size_t i = 0; i < getHand()->getNumPalms(); ++i) {
1925 PalmData& palm = getHand()->getPalms()[i];
1926 if (palm.isActive()) {
1927 glm::vec3 tip = getLaserPointerTipPosition(&palm);
1928 glm::vec3 root = palm.getPosition();
1929
1930 //Scale the root vector with the avatar scale
1931 scaleVectorRelativeToPosition(root);
1932 Transform transform = Transform();
1933 transform.setTranslation(glm::vec3());
1934 batch.setModelTransform(transform);
1935 Avatar::renderJointConnectingCone(batch, root, tip, PALM_TIP_ROD_RADIUS, PALM_TIP_ROD_RADIUS, glm::vec4(0, 1, 1, 1));
1936 }
1937 }
1938}
1939
1940//Gets the tip position for the laser pointer1965//Gets the tip position for the laser pointer
1941glm::vec3 MyAvatar::getLaserPointerTipPosition(const PalmData* palm) {1966glm::vec3 MyAvatar::getLaserPointerTipPosition(const PalmData* palm) {
1942 glm::vec3 direction = glm::normalize(palm->getTipPosition() - palm->getPosition());1967 glm::vec3 direction = glm::normalize(palm->getTipPosition() - palm->getPosition());
@@ -1962,7 +1987,7 @@ void MyAvatar::clearDriveKeys() { · 19 unmodified lines
1962}1987}
1963 1988
1964void MyAvatar::relayDriveKeysToCharacterController() {1989void MyAvatar::relayDriveKeysToCharacterController() {
1965 if (_driveKeys[UP] > 0.0f) {1990 if (_driveKeys[TRANSLATE_Y] > 0.0f) {
1966 _characterController.jump();1991 _characterController.jump();
1967 }1992 }
1968}1993}
interface/src/avatar/MyAvatar.h+53−2
@@ -17,6 +17,8 @@ · 16 unmodified lines
17#include <SettingHandle.h>17#include <SettingHandle.h>
18#include <Rig.h>18#include <Rig.h>
19 19
20#include <controllers/Pose.h>
21
20#include "Avatar.h"22#include "Avatar.h"
21#include "AtRestDetector.h"23#include "AtRestDetector.h"
22#include "MyCharacterController.h"24#include "MyCharacterController.h"
@@ -24,6 +26,20 @@ · 1 unmodified line
24 26
25class ModelItemID;27class ModelItemID;
26 28
29enum 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
27enum eyeContactTarget {43enum eyeContactTarget {
28 LEFT_EYE,44 LEFT_EYE,
29 RIGHT_EYE,45 RIGHT_EYE,
@@ -37,7 +53,6 @@ enum AudioListenerMode { · 7 unmodified lines
37};53};
38Q_DECLARE_METATYPE(AudioListenerMode);54Q_DECLARE_METATYPE(AudioListenerMode);
39 55
40
41class MyAvatar : public Avatar {56class MyAvatar : public Avatar {
42 Q_OBJECT57 Q_OBJECT
43 Q_PROPERTY(bool shouldRenderLocally READ getShouldRenderLocally WRITE setShouldRenderLocally)58 Q_PROPERTY(bool shouldRenderLocally READ getShouldRenderLocally WRITE setShouldRenderLocally)
@@ -53,6 +68,17 @@ class MyAvatar : public Avatar { · 9 unmodified lines
53 Q_PROPERTY(AudioListenerMode CUSTOM READ getAudioListenerModeCustom)68 Q_PROPERTY(AudioListenerMode CUSTOM READ getAudioListenerModeCustom)
54 //TODO: make gravity feature work Q_PROPERTY(glm::vec3 gravity READ getGravity WRITE setGravity)69 //TODO: make gravity feature work Q_PROPERTY(glm::vec3 gravity READ getGravity WRITE setGravity)
55 70
71
72 Q_PROPERTY(glm::vec3 leftHandPosition READ getLeftHandPosition)
73 Q_PROPERTY(glm::vec3 rightHandPosition READ getRightHandPosition)
74 Q_PROPERTY(glm::vec3 leftHandTipPosition READ getLeftHandTipPosition)
75 Q_PROPERTY(glm::vec3 rightHandTipPosition READ getRightHandTipPosition)
76
77 Q_PROPERTY(controller::Pose leftHandPose READ getLeftHandPose)
78 Q_PROPERTY(controller::Pose rightHandPose READ getRightHandPose)
79 Q_PROPERTY(controller::Pose leftHandTipPose READ getLeftHandTipPose)
80 Q_PROPERTY(controller::Pose rightHandTipPose READ getRightHandTipPose)
81
56public:82public:
57 MyAvatar(RigPointer rig);83 MyAvatar(RigPointer rig);
58 ~MyAvatar();84 ~MyAvatar();
@@ -111,6 +137,18 @@ public: · 52 unmodified lines
111 Q_INVOKABLE AnimationDetails getAnimationDetailsByRole(const QString& role);137 Q_INVOKABLE AnimationDetails getAnimationDetailsByRole(const QString& role);
112 Q_INVOKABLE AnimationDetails getAnimationDetails(const QString& url);138 Q_INVOKABLE AnimationDetails getAnimationDetails(const QString& url);
113 void clearJointAnimationPriorities();139 void clearJointAnimationPriorities();
140 // Adds handler(animStateDictionaryIn) => animStateDictionaryOut, which will be invoked just before each animGraph state update.
141 // The handler will be called with an animStateDictionaryIn that has all those properties specified by the (possibly empty)
142 // propertiesList argument. However for debugging, if the properties argument is null, all internal animGraph state is provided.
143 // The animStateDictionaryOut can be a different object than animStateDictionaryIn. Any properties set in animStateDictionaryOut
144 // will override those of the internal animation machinery.
145 // The animStateDictionaryIn may be shared among multiple handlers, and thus may contain additional properties specified when
146 // adding one of the other handlers. While any handler may change a value in animStateDictionaryIn (or supply different values in animStateDictionaryOut)
147 // a handler must not remove properties from animStateDictionaryIn, nor change property values that it does not intend to change.
148 // It is not specified in what order multiple handlers are called.
149 Q_INVOKABLE QScriptValue addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList) { return _rig->addAnimationStateHandler(handler, propertiesList); }
150 // Removes a handler previously added by addAnimationStateHandler.
151 Q_INVOKABLE void removeAnimationStateHandler(QScriptValue handler) { _rig->removeAnimationStateHandler(handler); }
114 152
115 // get/set avatar data153 // get/set avatar data
116 void saveData();154 void saveData();
@@ -141,6 +179,16 @@ public: · 24 unmodified lines
141 179
142 Q_INVOKABLE glm::vec3 getTargetAvatarPosition() const { return _targetAvatarPosition; }180 Q_INVOKABLE glm::vec3 getTargetAvatarPosition() const { return _targetAvatarPosition; }
143 181
182 Q_INVOKABLE glm::vec3 getLeftHandPosition() const;
183 Q_INVOKABLE glm::vec3 getRightHandPosition() const;
184 Q_INVOKABLE glm::vec3 getLeftHandTipPosition() const;
185 Q_INVOKABLE glm::vec3 getRightHandTipPosition() const;
186
187 Q_INVOKABLE controller::Pose getLeftHandPose() const;
188 Q_INVOKABLE controller::Pose getRightHandPose() const;
189 Q_INVOKABLE controller::Pose getLeftHandTipPose() const;
190 Q_INVOKABLE controller::Pose getRightHandTipPose() const;
191
144 AvatarWeakPointer getLookAtTargetAvatar() const { return _lookAtTargetAvatar; }192 AvatarWeakPointer getLookAtTargetAvatar() const { return _lookAtTargetAvatar; }
145 void updateLookAtTargetAvatar();193 void updateLookAtTargetAvatar();
146 void clearLookAtTargetAvatar();194 void clearLookAtTargetAvatar();
@@ -265,7 +313,6 @@ private: · 118 unmodified lines
265 const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), float scale = 1.0f,313 const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), float scale = 1.0f,
266 bool allowDuplicates = false, bool useSaved = true) override;314 bool allowDuplicates = false, bool useSaved = true) override;
267 315
268 void renderLaserPointers(gpu::Batch& batch);
269 const RecorderPointer getRecorder() const { return _recorder; }316 const RecorderPointer getRecorder() const { return _recorder; }
270 const PlayerPointer getPlayer() const { return _player; }317 const PlayerPointer getPlayer() const { return _player; }
271 318
@@ -281,6 +328,8 @@ private: · 9 unmodified lines
281 328
282 void setVisibleInSceneIfReady(Model* model, render::ScenePointer scene, bool visiblity);329 void setVisibleInSceneIfReady(Model* model, render::ScenePointer scene, bool visiblity);
283 330
331 PalmData getActivePalmData(int palmIndex) const;
332
284 // derive avatar body position and orientation from the current HMD Sensor location.333 // derive avatar body position and orientation from the current HMD Sensor location.
285 // results are in sensor space334 // results are in sensor space
286 glm::mat4 deriveBodyFromHMDSensor() const;335 glm::mat4 deriveBodyFromHMDSensor() const;
@@ -370,6 +419,8 @@ private: · 83 unmodified lines
370 AtRestDetector _hmdAtRestDetector;419 AtRestDetector _hmdAtRestDetector;
371 glm::vec3 _lastPosition;420 glm::vec3 _lastPosition;
372 bool _lastIsMoving { false };421 bool _lastIsMoving { false };
422 quint64 _lastStepPulse { 0 };
423 bool _pulseUpdate { false };
373};424};
374 425
375QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode);426QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode);
interface/src/avatar/SkeletonModel.cpp+24−42
@@ -97,23 +97,9 @@ void SkeletonModel::initJointStates(QVector<JointState> states) { · 96 unmodified lines
97 emit skeletonLoaded();97 emit skeletonLoaded();
98}98}
99 99
100static 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
111const float PALM_PRIORITY = DEFAULT_PRIORITY;100const float PALM_PRIORITY = DEFAULT_PRIORITY;
112// Called within Model::simulate call, below.101// Called within Model::simulate call, below.
113void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {102void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
114 if (_owningAvatar->isMyAvatar()) {
115 _rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation());
116 }
117 Head* head = _owningAvatar->getHead();103 Head* head = _owningAvatar->getHead();
118 if (_owningAvatar->isMyAvatar()) {104 if (_owningAvatar->isMyAvatar()) {
119 MyAvatar* myAvatar = static_cast<MyAvatar*>(_owningAvatar);105 MyAvatar* myAvatar = static_cast<MyAvatar*>(_owningAvatar);
@@ -160,28 +146,30 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { · 40 unmodified lines
160 _rig->updateFromHeadParameters(headParams, deltaTime);146 _rig->updateFromHeadParameters(headParams, deltaTime);
161 147
162 Rig::HandParameters handParams;148 Rig::HandParameters handParams;
163 const PalmData* leftPalm = getPalmWithIndex(myAvatar->getHand(), LEFT_HAND_INDEX);149
164 if (leftPalm && leftPalm->isActive()) {150 auto leftPalm = myAvatar->getHand()->getCopyOfPalmData(HandData::LeftHand);
151 if (leftPalm.isValid() && leftPalm.isActive()) {
165 handParams.isLeftEnabled = true;152 handParams.isLeftEnabled = true;
166 handParams.leftPosition = leftPalm->getRawPosition();153 handParams.leftPosition = leftPalm.getRawPosition();
167 handParams.leftOrientation = leftPalm->getRawRotation();154 handParams.leftOrientation = leftPalm.getRawRotation();
168 handParams.leftTrigger = leftPalm->getTrigger();155 handParams.leftTrigger = leftPalm.getTrigger();
169 } else {156 } else {
170 handParams.isLeftEnabled = false;157 handParams.isLeftEnabled = false;
171 }158 }
172 159
173 const PalmData* rightPalm = getPalmWithIndex(myAvatar->getHand(), RIGHT_HAND_INDEX);160 auto rightPalm = myAvatar->getHand()->getCopyOfPalmData(HandData::RightHand);
174 if (rightPalm && rightPalm->isActive()) {161 if (rightPalm.isValid() && rightPalm.isActive()) {
175 handParams.isRightEnabled = true;162 handParams.isRightEnabled = true;
176 handParams.rightPosition = rightPalm->getRawPosition();163 handParams.rightPosition = rightPalm.getRawPosition();
177 handParams.rightOrientation = rightPalm->getRawRotation();164 handParams.rightOrientation = rightPalm.getRawRotation();
178 handParams.rightTrigger = rightPalm->getTrigger();165 handParams.rightTrigger = rightPalm.getTrigger();
179 } else {166 } else {
180 handParams.isRightEnabled = false;167 handParams.isRightEnabled = false;
181 }168 }
182 169
183 _rig->updateFromHandParameters(handParams, deltaTime);170 _rig->updateFromHandParameters(handParams, deltaTime);
184 171
172 _rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation());
185 // evaluate AnimGraph animation and update jointStates.173 // evaluate AnimGraph animation and update jointStates.
186 Model::updateRig(deltaTime, parentTransform);174 Model::updateRig(deltaTime, parentTransform);
187 175
@@ -196,7 +184,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { · 8 unmodified lines
196 184
197 _rig->updateFromEyeParameters(eyeParams);185 _rig->updateFromEyeParameters(eyeParams);
198 186
199 // rebuild the jointState transform for the eyes only187 // rebuild the jointState transform for the eyes only. Must be after updateRig.
200 _rig->updateJointState(eyeParams.leftEyeJointIndex, parentTransform);188 _rig->updateJointState(eyeParams.leftEyeJointIndex, parentTransform);
201 _rig->updateJointState(eyeParams.rightEyeJointIndex, parentTransform);189 _rig->updateJointState(eyeParams.rightEyeJointIndex, parentTransform);
202 190
@@ -266,15 +254,15 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { · 63 unmodified lines
266 254
267 const FBXGeometry& geometry = _geometry->getFBXGeometry();255 const FBXGeometry& geometry = _geometry->getFBXGeometry();
268 256
269 // find the left and rightmost active palms
270 int leftPalmIndex, rightPalmIndex;
271 Hand* hand = _owningAvatar->getHand();
272 hand->getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex);
273
274 // Don't Relax toward hand positions when in animGraph mode.257 // Don't Relax toward hand positions when in animGraph mode.
275 if (!_rig->getEnableAnimGraph()) {258 if (!_rig->getEnableAnimGraph()) {
259
260 Hand* hand = _owningAvatar->getHand();
261 auto leftPalm = hand->getCopyOfPalmData(HandData::LeftHand);
262 auto rightPalm = hand->getCopyOfPalmData(HandData::RightHand);
263
276 const float HAND_RESTORATION_RATE = 0.25f;264 const float HAND_RESTORATION_RATE = 0.25f;
277 if (leftPalmIndex == -1 && rightPalmIndex == -1) {265 if (!leftPalm.isActive() && !rightPalm.isActive()) {
278 // palms are not yet set, use mouse266 // palms are not yet set, use mouse
279 if (_owningAvatar->getHandState() == HAND_STATE_NULL) {267 if (_owningAvatar->getHandState() == HAND_STATE_NULL) {
280 restoreRightHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);268 restoreRightHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
@@ -284,20 +272,14 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { · 3 unmodified lines
284 applyHandPosition(geometry.rightHandJointIndex, handPosition);272 applyHandPosition(geometry.rightHandJointIndex, handPosition);
285 }273 }
286 restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);274 restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
287
288 } else if (leftPalmIndex == rightPalmIndex) {
289 // right hand only
290 applyPalmData(geometry.rightHandJointIndex, hand->getPalms()[leftPalmIndex]);
291 restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
292
293 } else {275 } else {
294 if (leftPalmIndex != -1) {276 if (leftPalm.isActive()) {
295 applyPalmData(geometry.leftHandJointIndex, hand->getPalms()[leftPalmIndex]);277 applyPalmData(geometry.leftHandJointIndex, leftPalm);
296 } else {278 } else {
297 restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);279 restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
298 }280 }
299 if (rightPalmIndex != -1) {281 if (rightPalm.isActive()) {
300 applyPalmData(geometry.rightHandJointIndex, hand->getPalms()[rightPalmIndex]);282 applyPalmData(geometry.rightHandJointIndex, rightPalm);
301 } else {283 } else {
302 restoreRightHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);284 restoreRightHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
303 }285 }
@@ -348,7 +330,7 @@ void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position) · 44 unmodified lines
348 PALM_PRIORITY);330 PALM_PRIORITY);
349}331}
350 332
351void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {333void SkeletonModel::applyPalmData(int jointIndex, const PalmData& palm) {
352 if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) {334 if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) {
353 return;335 return;
354 }336 }
interface/src/avatar/SkeletonModel.h+1−1
@@ -118,7 +118,7 @@ protected: · 117 unmodified lines
118 /// \param position position of joint in model-frame118 /// \param position position of joint in model-frame
119 void applyHandPosition(int jointIndex, const glm::vec3& position);119 void applyHandPosition(int jointIndex, const glm::vec3& position);
120 120
121 void applyPalmData(int jointIndex, PalmData& palm);121 void applyPalmData(int jointIndex, const PalmData& palm);
122private:122private:
123 123
124 void renderJointConstraints(gpu::Batch& batch, int jointIndex);124 void renderJointConstraints(gpu::Batch& batch, int jointIndex);
interface/src/devices/3DConnexionClient.cpp+80−94
@@ -9,9 +9,14 @@ · 8 unmodified lines
9// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html9// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10//10//
11 11
12
12#include "3DConnexionClient.h"13#include "3DConnexionClient.h"
14
15#if 0
16#include <UserActivityLogger.h>
17#include <PathUtils.h>
18
13#include "Menu.h"19#include "Menu.h"
14#include "UserActivityLogger.h"
15 20
16const float MAX_AXIS = 75.0f; // max forward = 2x speed21const float MAX_AXIS = 75.0f; // max forward = 2x speed
17 22
@@ -25,22 +30,19 @@ ConnexionData& ConnexionData::getInstance() { · 7 unmodified lines
25 return sharedInstance;30 return sharedInstance;
26}31}
27 32
28ConnexionData::ConnexionData() {33
29}34ConnexionData::ConnexionData() : InputDevice("ConnexionClient") {}
35
30 36
31void ConnexionData::handleAxisEvent() {37void ConnexionData::handleAxisEvent() {
32 _axisStateMap[makeInput(ROTATION_AXIS_Y_POS).getChannel()] = (cc_rotation.y > 0.0f) ? cc_rotation.y / MAX_AXIS : 0.0f;38 auto rotation = cc_rotation / MAX_AXIS;
33 _axisStateMap[makeInput(ROTATION_AXIS_Y_NEG).getChannel()] = (cc_rotation.y < 0.0f) ? -cc_rotation.y / MAX_AXIS : 0.0f;39 _axisStateMap[ROTATE_X] = rotation.x;
34 _axisStateMap[makeInput(POSITION_AXIS_X_POS).getChannel()] = (cc_position.x > 0.0f) ? cc_position.x / MAX_AXIS : 0.0f;40 _axisStateMap[ROTATE_Y] = rotation.y;
35 _axisStateMap[makeInput(POSITION_AXIS_X_NEG).getChannel()] = (cc_position.x < 0.0f) ? -cc_position.x / MAX_AXIS : 0.0f;41 _axisStateMap[ROTATE_Z] = rotation.z;
36 _axisStateMap[makeInput(POSITION_AXIS_Y_POS).getChannel()] = (cc_position.y > 0.0f) ? cc_position.y / MAX_AXIS : 0.0f;42 auto position = cc_rotation / MAX_AXIS;
37 _axisStateMap[makeInput(POSITION_AXIS_Y_NEG).getChannel()] = (cc_position.y < 0.0f) ? -cc_position.y / MAX_AXIS : 0.0f;43 _axisStateMap[TRANSLATE_X] = position.x;
38 _axisStateMap[makeInput(POSITION_AXIS_Z_POS).getChannel()] = (cc_position.z > 0.0f) ? cc_position.z / MAX_AXIS : 0.0f;44 _axisStateMap[TRANSLATE_Y] = position.y;
39 _axisStateMap[makeInput(POSITION_AXIS_Z_NEG).getChannel()] = (cc_position.z < 0.0f) ? -cc_position.z / MAX_AXIS : 0.0f;45 _axisStateMap[TRANSLATE_Z] = position.z;
40 _axisStateMap[makeInput(ROTATION_AXIS_X_POS).getChannel()] = (cc_rotation.x > 0.0f) ? cc_rotation.x / MAX_AXIS : 0.0f;
41 _axisStateMap[makeInput(ROTATION_AXIS_X_NEG).getChannel()] = (cc_rotation.x < 0.0f) ? -cc_rotation.x / MAX_AXIS : 0.0f;
42 _axisStateMap[makeInput(ROTATION_AXIS_Z_POS).getChannel()] = (cc_rotation.z > 0.0f) ? cc_rotation.z / MAX_AXIS : 0.0f;
43 _axisStateMap[makeInput(ROTATION_AXIS_Z_NEG).getChannel()] = (cc_rotation.z < 0.0f) ? -cc_rotation.z / MAX_AXIS : 0.0f;
44}46}
45 47
46void ConnexionData::setButton(int lastButtonState) {48void ConnexionData::setButton(int lastButtonState) {
@@ -48,76 +50,65 @@ void ConnexionData::setButton(int lastButtonState) { · 1 unmodified line
48 _buttonPressedMap.insert(lastButtonState);50 _buttonPressedMap.insert(lastButtonState);
49}51}
50 52
51void ConnexionData::registerToUserInputMapper(UserInputMapper& mapper) {53void ConnexionData::buildDeviceProxy(controller::DeviceProxy::Pointer proxy) {
52 // Grab the current free device ID54 proxy->_name = _name = "ConnexionClient";
53 _deviceID = mapper.getFreeDeviceID();55 proxy->getButton = [this](const controller::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); };
54 56 proxy->getAxis = [this](const controller::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); };
55 auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy("ConnexionClient"));57 proxy->getAvailabeInputs = [this]() -> QVector<controller::Input::NamedPair> {
56 proxy->getButton = [this](const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); };58 using namespace controller;
57 proxy->getAxis = [this](const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); };59 static QVector<controller::Input::NamedPair> availableInputs {
58 proxy->getAvailabeInputs = [this]() -> QVector<UserInputMapper::InputPair> {60 Input::NamedPair(makeInput(BUTTON_1), "LeftButton"),
59 QVector<UserInputMapper::InputPair> availableInputs;61 Input::NamedPair(makeInput(BUTTON_2), "RightButton"),
60 62 Input::NamedPair(makeInput(BUTTON_3), "BothButtons"),
61 availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_1), "Left button"));63 Input::NamedPair(makeInput(TRANSLATE_X), "TranslateX"),
62 availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_2), "Right button"));64 Input::NamedPair(makeInput(TRANSLATE_Y), "TranslateY"),
63 availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_3), "Both buttons"));65 Input::NamedPair(makeInput(TRANSLATE_Z), "TranslateZ"),
64 66 Input::NamedPair(makeInput(ROTATE_X), "RotateX"),
65 availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_Y_NEG), "Move backward"));67 Input::NamedPair(makeInput(ROTATE_Y), "RotateY"),
66 availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_Y_POS), "Move forward"));68 Input::NamedPair(makeInput(ROTATE_Z), "RotateZ"),
67 availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_X_POS), "Move right"));69 };
68 availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_X_NEG), "Move Left"));
69 availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_Z_POS), "Move up"));
70 availableInputs.append(UserInputMapper::InputPair(makeInput(POSITION_AXIS_Z_NEG), "Move down"));
71 availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_Y_NEG), "Rotate backward"));
72 availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_Y_POS), "Rotate forward"));
73 availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_X_POS), "Rotate right"));
74 availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_X_NEG), "Rotate left"));
75 availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_Z_POS), "Rotate up"));
76 availableInputs.append(UserInputMapper::InputPair(makeInput(ROTATION_AXIS_Z_NEG), "Rotate down"));
77
78 return availableInputs;70 return availableInputs;
79 };71 };
80 proxy->resetDeviceBindings = [this, &mapper]() -> bool {
81 mapper.removeAllInputChannelsForDevice(_deviceID);
82 this->assignDefaultInputMapping(mapper);
83 return true;
84 };
85 mapper.registerDevice(_deviceID, proxy);
86}72}
87 73
88void ConnexionData::assignDefaultInputMapping(UserInputMapper& mapper) {74QString ConnexionData::getDefaultMappingConfig() {
89 const float JOYSTICK_MOVE_SPEED = 1.0f;75 static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/vive.json";
90 //const float DPAD_MOVE_SPEED = 0.5f;76 return MAPPING_JSON;
91 const float JOYSTICK_YAW_SPEED = 0.5f;
92 const float JOYSTICK_PITCH_SPEED = 0.25f;
93 const float BOOM_SPEED = 0.1f;
94
95 // Y axes are flipped (up is negative)
96 // postion: Movement, strafing
97 mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(POSITION_AXIS_Y_NEG), JOYSTICK_MOVE_SPEED);
98 mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(POSITION_AXIS_Y_POS), JOYSTICK_MOVE_SPEED);
99 mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(POSITION_AXIS_X_POS), JOYSTICK_MOVE_SPEED);
100 mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(POSITION_AXIS_X_NEG), JOYSTICK_MOVE_SPEED);
101 mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(POSITION_AXIS_Z_NEG), JOYSTICK_MOVE_SPEED);
102 mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(POSITION_AXIS_Z_POS), JOYSTICK_MOVE_SPEED);
103
104 // Rotation: Camera orientation with button 1
105 mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(ROTATION_AXIS_Z_POS), JOYSTICK_YAW_SPEED);
106 mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(ROTATION_AXIS_Z_NEG), JOYSTICK_YAW_SPEED);
107 mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(ROTATION_AXIS_Y_NEG), JOYSTICK_PITCH_SPEED);
108 mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(ROTATION_AXIS_Y_POS), JOYSTICK_PITCH_SPEED);
109
110 // Button controls
111 // Zoom
112 mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(BUTTON_1), BOOM_SPEED);
113 mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(BUTTON_2), BOOM_SPEED);
114
115 // Zoom
116 // mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(ROTATION_AXIS_Z_NEG), BOOM_SPEED);
117 // mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(ROTATION_AXIS_Z_POS), BOOM_SPEED);
118
119}77}
120 78
79//void ConnexionData::assignDefaultInputMapping(UserInputMapper& mapper) {
80// const float JOYSTICK_MOVE_SPEED = 1.0f;
81// //const float DPAD_MOVE_SPEED = 0.5f;
82// const float JOYSTICK_YAW_SPEED = 0.5f;
83// const float JOYSTICK_PITCH_SPEED = 0.25f;
84// const float BOOM_SPEED = 0.1f;
85//
86// // Y axes are flipped (up is negative)
87// // postion: Movement, strafing
88// mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(POSITION_AXIS_Y_NEG), JOYSTICK_MOVE_SPEED);
89// mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(POSITION_AXIS_Y_POS), JOYSTICK_MOVE_SPEED);
90// mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(POSITION_AXIS_X_POS), JOYSTICK_MOVE_SPEED);
91// mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(POSITION_AXIS_X_NEG), JOYSTICK_MOVE_SPEED);
92// mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(POSITION_AXIS_Z_NEG), JOYSTICK_MOVE_SPEED);
93// mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(POSITION_AXIS_Z_POS), JOYSTICK_MOVE_SPEED);
94//
95// // Rotation: Camera orientation with button 1
96// mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(ROTATION_AXIS_Z_POS), JOYSTICK_YAW_SPEED);
97// mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(ROTATION_AXIS_Z_NEG), JOYSTICK_YAW_SPEED);
98// mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(ROTATION_AXIS_Y_NEG), JOYSTICK_PITCH_SPEED);
99// mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(ROTATION_AXIS_Y_POS), JOYSTICK_PITCH_SPEED);
100//
101// // Button controls
102// // Zoom
103// mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(BUTTON_1), BOOM_SPEED);
104// mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(BUTTON_2), BOOM_SPEED);
105//
106// // Zoom
107// // mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(ROTATION_AXIS_Z_NEG), BOOM_SPEED);
108// // mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(ROTATION_AXIS_Z_POS), BOOM_SPEED);
109//
110//}
111
121float ConnexionData::getButton(int channel) const {112float ConnexionData::getButton(int channel) const {
122 if (!_buttonPressedMap.empty()) {113 if (!_buttonPressedMap.empty()) {
123 if (_buttonPressedMap.find(channel) != _buttonPressedMap.end()) {114 if (_buttonPressedMap.find(channel) != _buttonPressedMap.end()) {
@@ -138,15 +129,15 @@ float ConnexionData::getAxis(int channel) const { · 14 unmodified lines
138 }129 }
139}130}
140 131
141UserInputMapper::Input ConnexionData::makeInput(ConnexionData::ButtonChannel button) {132controller::Input ConnexionData::makeInput(ConnexionData::ButtonChannel button) {
142 return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON);133 return controller::Input(_deviceID, button, controller::ChannelType::BUTTON);
143}134}
144 135
145UserInputMapper::Input ConnexionData::makeInput(ConnexionData::PositionChannel axis) {136controller::Input ConnexionData::makeInput(ConnexionData::PositionChannel axis) {
146 return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS);137 return controller::Input(_deviceID, axis, controller::ChannelType::AXIS);
147}138}
148 139
149void ConnexionData::update() {140void ConnexionData::update(float deltaTime, bool jointsCaptured) {
150 // the update is done in the ConnexionClient class.141 // the update is done in the ConnexionClient class.
151 // for windows in the nativeEventFilter the inputmapper is connected or registed or removed when an 3Dconnnexion device is attached or detached142 // for windows in the nativeEventFilter the inputmapper is connected or registed or removed when an 3Dconnnexion device is attached or detached
152 // for osx the api will call DeviceAddedHandler or DeviceRemoveHandler when a 3Dconnexion device is attached or detached143 // for osx the api will call DeviceAddedHandler or DeviceRemoveHandler when a 3Dconnexion device is attached or detached
@@ -187,7 +178,6 @@ void ConnexionClient::destroy() { · 34 unmodified lines
187 QAbstractEventDispatcher::instance()->removeNativeEventFilter(this);178 QAbstractEventDispatcher::instance()->removeNativeEventFilter(this);
188 ConnexionData& connexiondata = ConnexionData::getInstance();179 ConnexionData& connexiondata = ConnexionData::getInstance();
189 int deviceid = connexiondata.getDeviceID();180 int deviceid = connexiondata.getDeviceID();
190 connexiondata.setDeviceID(0);
191 auto userInputMapper = DependencyManager::get<UserInputMapper>();181 auto userInputMapper = DependencyManager::get<UserInputMapper>();
192 userInputMapper->removeDevice(deviceid);182 userInputMapper->removeDevice(deviceid);
193}183}
@@ -295,13 +285,10 @@ bool ConnexionClient::RawInputEventFilter(void* msg, long* result) { · 101 unmodified lines
295 ConnexionData& connexiondata = ConnexionData::getInstance();285 ConnexionData& connexiondata = ConnexionData::getInstance();
296 auto userInputMapper = DependencyManager::get<UserInputMapper>();286 auto userInputMapper = DependencyManager::get<UserInputMapper>();
297 if (Is3dmouseAttached() && connexiondata.getDeviceID() == 0) {287 if (Is3dmouseAttached() && connexiondata.getDeviceID() == 0) {
298 connexiondata.registerToUserInputMapper(*userInputMapper);288 userInputMapper->registerDevice(&connexiondata);
299 connexiondata.assignDefaultInputMapping(*userInputMapper);
300 UserActivityLogger::getInstance().connectedDevice("controller", "3Dconnexion");289 UserActivityLogger::getInstance().connectedDevice("controller", "3Dconnexion");
301 } else if (!Is3dmouseAttached() && connexiondata.getDeviceID() != 0) {290 } else if (!Is3dmouseAttached() && connexiondata.getDeviceID() != 0) {
302 int deviceid = connexiondata.getDeviceID();291 userInputMapper->removeDevice(connexiondata.getDeviceID());
303 connexiondata.setDeviceID(0);
304 userInputMapper->removeDevice(deviceid);
305 }292 }
306 293
307 if (!Is3dmouseAttached()) {294 if (!Is3dmouseAttached()) {
@@ -894,8 +881,7 @@ void ConnexionClient::init() { · 586 unmodified lines
894 881
895 if (Is3dmouseAttached() && connexiondata.getDeviceID() == 0) {882 if (Is3dmouseAttached() && connexiondata.getDeviceID() == 0) {
896 auto userInputMapper = DependencyManager::get<UserInputMapper>();883 auto userInputMapper = DependencyManager::get<UserInputMapper>();
897 connexiondata.registerToUserInputMapper(*userInputMapper);884 userInputMapper->registerDevice(&connexiondata);
898 connexiondata.assignDefaultInputMapping(*userInputMapper);
899 UserActivityLogger::getInstance().connectedDevice("controller", "3Dconnexion");885 UserActivityLogger::getInstance().connectedDevice("controller", "3Dconnexion");
900 }886 }
901 //let one axis be dominant887 //let one axis be dominant
@@ -926,8 +912,7 @@ void DeviceAddedHandler(unsigned int connection) { · 24 unmodified lines
926 if (connexiondata.getDeviceID() == 0) {912 if (connexiondata.getDeviceID() == 0) {
927 qCWarning(interfaceapp) << "3Dconnexion device added ";913 qCWarning(interfaceapp) << "3Dconnexion device added ";
928 auto userInputMapper = DependencyManager::get<UserInputMapper>();914 auto userInputMapper = DependencyManager::get<UserInputMapper>();
929 connexiondata.registerToUserInputMapper(*userInputMapper);915 userInputMapper->registerDevice(&connexiondata);
930 connexiondata.assignDefaultInputMapping(*userInputMapper);
931 UserActivityLogger::getInstance().connectedDevice("controller", "3Dconnexion");916 UserActivityLogger::getInstance().connectedDevice("controller", "3Dconnexion");
932 }917 }
933}918}
@@ -984,3 +969,4 @@ void MessageHandler(unsigned int connection, unsigned int messageType, void *mes · 50 unmodified lines
984#endif // __APPLE__969#endif // __APPLE__
985 970
986#endif // HAVE_3DCONNEXIONCLIENT971#endif // HAVE_3DCONNEXIONCLIENT
972#endif
interface/src/devices/3DConnexionClient.h+17−34
@@ -11,9 +11,10 @@ · 10 unmodified lines
11#ifndef hifi_3DConnexionClient_h11#ifndef hifi_3DConnexionClient_h
12#define hifi_3DConnexionClient_h12#define hifi_3DConnexionClient_h
13 13
14#if 0
14#include <QObject>15#include <QObject>
15#include <QLibrary>16#include <QLibrary>
16#include <input-plugins/UserInputMapper.h>17#include <controllers/UserInputMapper.h>
17 18
18#include "InterfaceLogging.h"19#include "InterfaceLogging.h"
19 20
@@ -175,26 +176,19 @@ public slots: · 155 unmodified lines
175 176
176 177
177// connnects to the userinputmapper178// connnects to the userinputmapper
178class ConnexionData : public QObject {179class ConnexionData : public QObject, public controller::InputDevice {
179 Q_OBJECT180 Q_OBJECT
180 181
181public:182public:
182 static ConnexionData& getInstance();183 static ConnexionData& getInstance();
183 ConnexionData();184 ConnexionData();
184
185 enum PositionChannel {185 enum PositionChannel {
186 POSITION_AXIS_X_POS = 1,186 TRANSLATE_X,
187 POSITION_AXIS_X_NEG = 2,187 TRANSLATE_Y,
188 POSITION_AXIS_Y_POS = 3,188 TRANSLATE_Z,
189 POSITION_AXIS_Y_NEG = 4,189 ROTATE_X,
190 POSITION_AXIS_Z_POS = 5,190 ROTATE_Y,
191 POSITION_AXIS_Z_NEG = 6,191 ROTATE_Z,
192 ROTATION_AXIS_X_POS = 7,
193 ROTATION_AXIS_X_NEG = 8,
194 ROTATION_AXIS_Y_POS = 9,
195 ROTATION_AXIS_Y_NEG = 10,
196 ROTATION_AXIS_Z_POS = 11,
197 ROTATION_AXIS_Z_NEG = 12
198 };192 };
199 193
200 enum ButtonChannel {194 enum ButtonChannel {
@@ -209,19 +203,12 @@ public: · 8 unmodified lines
209 float getButton(int channel) const;203 float getButton(int channel) const;
210 float getAxis(int channel) const;204 float getAxis(int channel) const;
211 205
212 UserInputMapper::Input makeInput(ConnexionData::PositionChannel axis);206 controller::Input makeInput(ConnexionData::PositionChannel axis);
213 UserInputMapper::Input makeInput(ConnexionData::ButtonChannel button);207 controller::Input makeInput(ConnexionData::ButtonChannel button);
214 208 virtual void buildDeviceProxy(controller::DeviceProxy::Pointer proxy) override;
215 void registerToUserInputMapper(UserInputMapper& mapper);209 virtual QString getDefaultMappingConfig() override;
216 void assignDefaultInputMapping(UserInputMapper& mapper);210 virtual void update(float deltaTime, bool jointsCaptured) override;
217 211 virtual void focusOutEvent() override;
218 void update();
219 void focusOutEvent();
220
221 int getDeviceID() { return _deviceID; }
222 void setDeviceID(int deviceID) { _deviceID = deviceID; }
223
224 QString _name;
225 212
226 glm::vec3 cc_position;213 glm::vec3 cc_position;
227 glm::vec3 cc_rotation;214 glm::vec3 cc_rotation;
@@ -229,12 +216,8 @@ public: · 1 unmodified line
229 216
230 void setButton(int lastButtonState);217 void setButton(int lastButtonState);
231 void handleAxisEvent();218 void handleAxisEvent();
232
233protected:
234 int _deviceID = 0;
235
236 ButtonPressedMap _buttonPressedMap;
237 AxisStateMap _axisStateMap;
238};219};
239 220
221#endif
222
240#endif // defined(hifi_3DConnexionClient_h)223#endif // defined(hifi_3DConnexionClient_h)
interface/src/scripting/ControllerScriptingInterface.cpp+47−442
@@ -9,120 +9,20 @@ · 8 unmodified lines
9// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html9// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10//10//
11 11
12#include "ControllerScriptingInterface.h"
13
12#include <avatar/AvatarManager.h>14#include <avatar/AvatarManager.h>
13#include <avatar/MyAvatar.h>15#include <avatar/MyAvatar.h>
14#include <HandData.h>16#include <HandData.h>
15#include <HFBackEvent.h>17#include <HFBackEvent.h>
18#include <plugins/PluginManager.h>
16 19
17#include "Application.h"20#include "Application.h"
18#include "devices/MotionTracker.h"21#include "devices/MotionTracker.h"
19#include "ControllerScriptingInterface.h"
20 22
21// TODO: this needs to be removed, as well as any related controller-specific information23// TODO: this needs to be removed, as well as any related controller-specific information
22#include <input-plugins/SixenseManager.h>24#include <input-plugins/SixenseManager.h>
23 25
24
25ControllerScriptingInterface::ControllerScriptingInterface() :
26 _mouseCaptured(false),
27 _touchCaptured(false),
28 _wheelCaptured(false),
29 _actionsCaptured(false)
30{
31
32}
33
34static int actionMetaTypeId = qRegisterMetaType<UserInputMapper::Action>();
35static int inputChannelMetaTypeId = qRegisterMetaType<UserInputMapper::InputChannel>();
36static int inputMetaTypeId = qRegisterMetaType<UserInputMapper::Input>();
37static int inputPairMetaTypeId = qRegisterMetaType<UserInputMapper::InputPair>();
38
39QScriptValue inputToScriptValue(QScriptEngine* engine, const UserInputMapper::Input& input);
40void inputFromScriptValue(const QScriptValue& object, UserInputMapper::Input& input);
41QScriptValue inputChannelToScriptValue(QScriptEngine* engine, const UserInputMapper::InputChannel& inputChannel);
42void inputChannelFromScriptValue(const QScriptValue& object, UserInputMapper::InputChannel& inputChannel);
43QScriptValue actionToScriptValue(QScriptEngine* engine, const UserInputMapper::Action& action);
44void actionFromScriptValue(const QScriptValue& object, UserInputMapper::Action& action);
45QScriptValue inputPairToScriptValue(QScriptEngine* engine, const UserInputMapper::InputPair& inputPair);
46void inputPairFromScriptValue(const QScriptValue& object, UserInputMapper::InputPair& inputPair);
47
48QScriptValue 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
57void 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
64QScriptValue 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
73void 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
84QScriptValue 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
98void actionFromScriptValue(const QScriptValue& object, UserInputMapper::Action& action) {
99 action = UserInputMapper::Action(object.property("action").toVariant().toInt());
100}
101
102QScriptValue 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
109void 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
114void 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
126void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) {26void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) {
127 if (event->type() == HFActionEvent::startType()) {27 if (event->type() == HFActionEvent::startType()) {
128 emit actionStartEvent(static_cast<HFActionEvent&>(*event));28 emit actionStartEvent(static_cast<HFActionEvent&>(*event));
@@ -135,205 +35,6 @@ void ControllerScriptingInterface::handleMetaEvent(HFMetaEvent* event) { · 6 unmodified lines
135 }35 }
136}36}
137 37
138const 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
151int 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
163const PalmData* ControllerScriptingInterface::getPalm(int palmIndex) const {
164 const HandData* handData = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHandData();
165 return &handData->getPalms()[palmIndex];
166}
167
168const 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
183bool 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
194glm::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
203int ControllerScriptingInterface::getNumberOfButtons() const {
204 return getNumberOfActivePalms() * NUMBER_OF_BUTTONS_PER_PALM;
205}
206
207bool 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
230int ControllerScriptingInterface::getNumberOfTriggers() const {
231 return getNumberOfActivePalms() * NUMBER_OF_TRIGGERS_PER_PALM;
232}
233
234float 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
244int ControllerScriptingInterface::getNumberOfJoysticks() const {
245 return getNumberOfActivePalms() * NUMBER_OF_JOYSTICKS_PER_PALM;
246}
247
248glm::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
258int ControllerScriptingInterface::getNumberOfSpatialControls() const {
259 return getNumberOfActivePalms() * NUMBER_OF_SPATIALCONTROLS_PER_PALM;
260}
261
262glm::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
277glm::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
292glm::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
307glm::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
322glm::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
337bool ControllerScriptingInterface::isKeyCaptured(QKeyEvent* event) const {38bool ControllerScriptingInterface::isKeyCaptured(QKeyEvent* event) const {
338 return isKeyCaptured(KeyEvent(*event));39 return isKeyCaptured(KeyEvent(*event));
339}40}
@@ -383,157 +84,45 @@ glm::vec2 ControllerScriptingInterface::getViewportDimensions() const { · 43 unmodified lines
383 return qApp->getUiSize();84 return qApp->getUiSize();
384}85}
385 86
386QString ControllerScriptingInterface::sanatizeName(const QString& name) {87controller::InputController::Pointer ControllerScriptingInterface::createInputController(const QString& deviceName, const QString& tracker) {
387 QString cleanName { name };
388 cleanName.remove(QRegularExpression{"[\\(\\)\\.\\s]"});
389 return cleanName;
390}
391
392void 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
431AbstractInputController* ControllerScriptingInterface::createInputController(const QString& deviceName, const QString& tracker) {
432 // This is where we retreive the Device Tracker category and then the sub tracker within it88 // This is where we retreive the Device Tracker category and then the sub tracker within it
433 //TODO C++11 auto icIt = _inputControllers.find(0);89 auto icIt = _inputControllers.find(0);
434 InputControllerMap::iterator icIt = _inputControllers.find(0);
435
436 if (icIt != _inputControllers.end()) {90 if (icIt != _inputControllers.end()) {
437 return (*icIt).second;91 return (*icIt).second;
438 } else {92 }
439 93
440 // Look for device94
441 DeviceTracker::ID deviceID = DeviceTracker::getDeviceID(deviceName.toStdString());95 // Look for device
442 if (deviceID < 0) {96 DeviceTracker::ID deviceID = DeviceTracker::getDeviceID(deviceName.toStdString());
443 deviceID = 0;97 if (deviceID < 0) {
444 }98 deviceID = 0;
445 // TODO in this current implementation, we just pick the device assuming there is one (normally the Leapmotion)99 }
446 // in the near future we need to change that to a real mapping between the devices and the deviceName100 // TODO in this current implementation, we just pick the device assuming there is one (normally the Leapmotion)
447 // ALso we need to expand the spec so we can fall back on the "default" controller per categories101 // in the near future we need to change that to a real mapping between the devices and the deviceName
448 102 // ALso we need to expand the spec so we can fall back on the "default" controller per categories
449 if (deviceID >= 0) {103
450 // TODO here again the assumption it's the LeapMotion and so it's a MOtionTracker, this would need to be changed to support different types of devices104 if (deviceID >= 0) {
451 MotionTracker* motionTracker = dynamic_cast< MotionTracker* > (DeviceTracker::getDevice(deviceID));105 // TODO here again the assumption it's the LeapMotion and so it's a MOtionTracker, this would need to be changed to support different types of devices
452 if (motionTracker) {106 MotionTracker* motionTracker = dynamic_cast< MotionTracker* > (DeviceTracker::getDevice(deviceID));
453 MotionTracker::Index trackerID = motionTracker->findJointIndex(tracker.toStdString());107 if (motionTracker) {
454 if (trackerID >= 0) {108 MotionTracker::Index trackerID = motionTracker->findJointIndex(tracker.toStdString());
455 AbstractInputController* inputController = new InputController(deviceID, trackerID, this);109 if (trackerID >= 0) {
456 110 controller::InputController::Pointer inputController = std::make_shared<InputController>(deviceID, trackerID, this);
457 _inputControllers.insert(InputControllerMap::value_type(inputController->getKey(), inputController));111 controller::InputController::Key key = inputController->getKey();
458 112 _inputControllers.insert(InputControllerMap::value_type(key, inputController));
459 return inputController;113 return inputController;
460 }
461 }114 }
462 }115 }
463
464 return 0;
465 }116 }
466}
467 117
468void ControllerScriptingInterface::releaseInputController(AbstractInputController* input) {118 return controller::InputController::Pointer();
469 _inputControllers.erase(input->getKey());
470}119}
471 120
472void ControllerScriptingInterface::updateInputControllers() {121void ControllerScriptingInterface::releaseInputController(controller::InputController::Pointer input) {
473 //TODO C++11 for (auto it = _inputControllers.begin(); it != _inputControllers.end(); it++) {122 _inputControllers.erase(input->getKey());
474 for (InputControllerMap::iterator it = _inputControllers.begin(); it != _inputControllers.end(); it++) {
475 (*it).second->update();
476 }
477}
478
479QVector<UserInputMapper::Action> ControllerScriptingInterface::getAllActions() {
480 return DependencyManager::get<UserInputMapper>()->getAllActions();
481}
482
483QVector<UserInputMapper::InputChannel> ControllerScriptingInterface::getInputChannelsForAction(UserInputMapper::Action action) {
484 return DependencyManager::get<UserInputMapper>()->getInputChannelsForAction(action);
485}
486
487QString ControllerScriptingInterface::getDeviceName(unsigned int device) {
488 return DependencyManager::get<UserInputMapper>()->getDeviceName((unsigned short)device);
489}
490
491QVector<UserInputMapper::InputChannel> ControllerScriptingInterface::getAllInputsForDevice(unsigned int device) {
492 return DependencyManager::get<UserInputMapper>()->getAllInputsForDevice(device);
493}
494
495bool ControllerScriptingInterface::addInputChannel(UserInputMapper::InputChannel inputChannel) {
496 return DependencyManager::get<UserInputMapper>()->addInputChannel(inputChannel._action, inputChannel._input, inputChannel._modifier, inputChannel._scale);
497}
498
499bool ControllerScriptingInterface::removeInputChannel(UserInputMapper::InputChannel inputChannel) {
500 return DependencyManager::get<UserInputMapper>()->removeInputChannel(inputChannel);
501}
502
503QVector<UserInputMapper::InputPair> ControllerScriptingInterface::getAvailableInputs(unsigned int device) {
504 return DependencyManager::get<UserInputMapper>()->getAvailableInputs((unsigned short)device);
505}
506
507void ControllerScriptingInterface::resetAllDeviceBindings() {
508 DependencyManager::get<UserInputMapper>()->resetAllDeviceBindings();
509}
510
511void ControllerScriptingInterface::resetDevice(unsigned int device) {
512 DependencyManager::get<UserInputMapper>()->resetDevice(device);
513}
514
515int ControllerScriptingInterface::findDevice(QString name) {
516 return DependencyManager::get<UserInputMapper>()->findDevice(name);
517}
518
519QVector<QString> ControllerScriptingInterface::getDeviceNames() {
520 return DependencyManager::get<UserInputMapper>()->getDeviceNames();
521}
522
523float ControllerScriptingInterface::getActionValue(int action) {
524 return DependencyManager::get<UserInputMapper>()->getActionState(UserInputMapper::Action(action));
525}
526
527int ControllerScriptingInterface::findAction(QString actionName) {
528 return DependencyManager::get<UserInputMapper>()->findAction(actionName);
529}
530
531QVector<QString> ControllerScriptingInterface::getActionNames() const {
532 return DependencyManager::get<UserInputMapper>()->getActionNames();
533}123}
534 124
535InputController::InputController(int deviceTrackerId, int subTrackerId, QObject* parent) :125InputController::InputController(int deviceTrackerId, int subTrackerId, QObject* parent) :
536 AbstractInputController(),
537 _deviceTrackerId(deviceTrackerId),126 _deviceTrackerId(deviceTrackerId),
538 _subTrackerId(subTrackerId),127 _subTrackerId(subTrackerId),
539 _isActive(false)128 _isActive(false)
@@ -556,7 +145,7 @@ void InputController::update() { · 16 unmodified lines
556 joint->getLocFrame().getRotation(_eventCache.locRotation);145 joint->getLocFrame().getRotation(_eventCache.locRotation);
557 146
558 _isActive = true;147 _isActive = true;
559 emit spatialEvent(_eventCache);148 //emit spatialEvent(_eventCache);
560 }149 }
561 }150 }
562 }151 }
@@ -568,3 +157,19 @@ const unsigned int INPUTCONTROLLER_KEY_DEVICE_MASK = 16; · 5 unmodified lines
568InputController::Key InputController::getKey() const {157InputController::Key InputController::getKey() const {
569 return (((_deviceTrackerId & INPUTCONTROLLER_KEY_DEVICE_MASK) << INPUTCONTROLLER_KEY_DEVICE_OFFSET) | _subTrackerId);158 return (((_deviceTrackerId & INPUTCONTROLLER_KEY_DEVICE_MASK) << INPUTCONTROLLER_KEY_DEVICE_OFFSET) | _subTrackerId);
570}159}
160
161
162void ControllerScriptingInterface::emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); }
163void ControllerScriptingInterface::emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(KeyEvent(*event)); }
164
165void ControllerScriptingInterface::emitMouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { emit mouseMoveEvent(MouseEvent(*event, deviceID)); }
166void ControllerScriptingInterface::emitMousePressEvent(QMouseEvent* event, unsigned int deviceID) { emit mousePressEvent(MouseEvent(*event, deviceID)); }
167void ControllerScriptingInterface::emitMouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID) { emit mouseDoublePressEvent(MouseEvent(*event, deviceID)); }
168void ControllerScriptingInterface::emitMouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) { emit mouseReleaseEvent(MouseEvent(*event, deviceID)); }
169
170void ControllerScriptingInterface::emitTouchBeginEvent(const TouchEvent& event) { emit touchBeginEvent(event); }
171void ControllerScriptingInterface::emitTouchEndEvent(const TouchEvent& event) { emit touchEndEvent(event); }
172void ControllerScriptingInterface::emitTouchUpdateEvent(const TouchEvent& event) { emit touchUpdateEvent(event); }
173
174void ControllerScriptingInterface::emitWheelEvent(QWheelEvent* event) { emit wheelEvent(*event); }
175
interface/src/scripting/ControllerScriptingInterface.h+49−89
@@ -14,12 +14,20 @@ · 13 unmodified lines
14 14
15#include <QtCore/QObject>15#include <QtCore/QObject>
16 16
17#include <input-plugins/UserInputMapper.h>17#include <controllers/UserInputMapper.h>
18#include <controllers/ScriptingInterface.h>
19
20#include <HFActionEvent.h>
21#include <KeyEvent.h>
22#include <MouseEvent.h>
23#include <SpatialEvent.h>
24#include <TouchEvent.h>
25#include <WheelEvent.h>
26class ScriptEngine;
18 27
19#include <AbstractControllerScriptingInterface.h>
20class PalmData;28class PalmData;
21 29
22class InputController : public AbstractInputController {30class InputController : public controller::InputController {
23 Q_OBJECT31 Q_OBJECT
24 32
25public:33public:
@@ -50,125 +58,77 @@ signals: · 24 unmodified lines
50 58
51 59
52/// handles scripting of input controller commands from JS60/// handles scripting of input controller commands from JS
53class ControllerScriptingInterface : public AbstractControllerScriptingInterface {61class ControllerScriptingInterface : public controller::ScriptingInterface {
54 Q_OBJECT62 Q_OBJECT
55 63
64
56public: 65public:
57 ControllerScriptingInterface();66 virtual ~ControllerScriptingInterface() {}
58 67
59 virtual void registerControllerTypes(ScriptEngine* engine);68 void emitKeyPressEvent(QKeyEvent* event);
60 69 void emitKeyReleaseEvent(QKeyEvent* event);
61 void emitKeyPressEvent(QKeyEvent* event) { emit keyPressEvent(KeyEvent(*event)); }
62 void emitKeyReleaseEvent(QKeyEvent* event) { emit keyReleaseEvent(KeyEvent(*event)); }
63 70
64 void handleMetaEvent(HFMetaEvent* event);71 void handleMetaEvent(HFMetaEvent* event);
65 72
66 void emitMouseMoveEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mouseMoveEvent(MouseEvent(*event, deviceID)); }73 void emitMouseMoveEvent(QMouseEvent* event, unsigned int deviceID = 0);
67 void emitMousePressEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mousePressEvent(MouseEvent(*event, deviceID)); }74 void emitMousePressEvent(QMouseEvent* event, unsigned int deviceID = 0);
68 void emitMouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mouseDoublePressEvent(MouseEvent(*event, deviceID)); }75 void emitMouseDoublePressEvent(QMouseEvent* event, unsigned int deviceID = 0);
69 void emitMouseReleaseEvent(QMouseEvent* event, unsigned int deviceID = 0) { emit mouseReleaseEvent(MouseEvent(*event, deviceID)); }76 void emitMouseReleaseEvent(QMouseEvent* event, unsigned int deviceID = 0);
70 77
71 void emitTouchBeginEvent(const TouchEvent& event) { emit touchBeginEvent(event); }78 void emitTouchBeginEvent(const TouchEvent& event);
72 void emitTouchEndEvent(const TouchEvent& event) { emit touchEndEvent(event); }79 void emitTouchEndEvent(const TouchEvent& event);
73 void emitTouchUpdateEvent(const TouchEvent& event) { emit touchUpdateEvent(event); }80 void emitTouchUpdateEvent(const TouchEvent& event);
74 81
75 void emitWheelEvent(QWheelEvent* event) { emit wheelEvent(*event); }82 void emitWheelEvent(QWheelEvent* event);
76 83
77 bool isKeyCaptured(QKeyEvent* event) const;84 bool isKeyCaptured(QKeyEvent* event) const;
78 bool isKeyCaptured(const KeyEvent& event) const;85 bool isKeyCaptured(const KeyEvent& event) const;
79 bool isMouseCaptured() const { return _mouseCaptured; }
80 bool isTouchCaptured() const { return _touchCaptured; }
81 bool isWheelCaptured() const { return _wheelCaptured; }
82 bool areActionsCaptured() const { return _actionsCaptured; }
83 bool isJoystickCaptured(int joystickIndex) const;86 bool isJoystickCaptured(int joystickIndex) const;
84 87
85 void updateInputControllers();
86
87public slots:88public slots:
88 Q_INVOKABLE virtual QVector<UserInputMapper::Action> getAllActions();
89
90 Q_INVOKABLE virtual bool addInputChannel(UserInputMapper::InputChannel inputChannel);
91 Q_INVOKABLE virtual bool removeInputChannel(UserInputMapper::InputChannel inputChannel);
92 Q_INVOKABLE virtual QVector<UserInputMapper::InputChannel> getInputChannelsForAction(UserInputMapper::Action action);
93
94 Q_INVOKABLE virtual QVector<UserInputMapper::InputPair> getAvailableInputs(unsigned int device);
95 Q_INVOKABLE virtual QVector<UserInputMapper::InputChannel> getAllInputsForDevice(unsigned int device);
96
97 Q_INVOKABLE virtual QString getDeviceName(unsigned int device);
98
99 Q_INVOKABLE virtual float getActionValue(int action);
100
101 Q_INVOKABLE virtual void resetDevice(unsigned int device);
102 Q_INVOKABLE virtual void resetAllDeviceBindings();
103 Q_INVOKABLE virtual int findDevice(QString name);
104 Q_INVOKABLE virtual QVector<QString> getDeviceNames();
105
106 Q_INVOKABLE virtual int findAction(QString actionName);
107 Q_INVOKABLE virtual QVector<QString> getActionNames() const;
108
109 virtual bool isPrimaryButtonPressed() const;
110 virtual glm::vec2 getPrimaryJoystickPosition() const;
111
112 virtual int getNumberOfButtons() const;
113 virtual bool isButtonPressed(int buttonIndex) const;
114
115 virtual int getNumberOfTriggers() const;
116 virtual float getTriggerValue(int triggerIndex) const;
117 89
118 virtual int getNumberOfJoysticks() const;
119 virtual glm::vec2 getJoystickPosition(int joystickIndex) const;
120
121 virtual int getNumberOfSpatialControls() const;
122 virtual glm::vec3 getSpatialControlPosition(int controlIndex) const;
123 virtual glm::vec3 getSpatialControlVelocity(int controlIndex) const;
124 virtual glm::vec3 getSpatialControlNormal(int controlIndex) const;
125 virtual glm::quat getSpatialControlRawRotation(int controlIndex) const;
126 virtual glm::vec3 getSpatialControlRawAngularVelocity(int controlIndex) const;
127 virtual void captureKeyEvents(const KeyEvent& event);90 virtual void captureKeyEvents(const KeyEvent& event);
128 virtual void releaseKeyEvents(const KeyEvent& event);91 virtual void releaseKeyEvents(const KeyEvent& event);
129 92
130 virtual void captureMouseEvents() { _mouseCaptured = true; }
131 virtual void releaseMouseEvents() { _mouseCaptured = false; }
132
133 virtual void captureTouchEvents() { _touchCaptured = true; }
134 virtual void releaseTouchEvents() { _touchCaptured = false; }
135
136 virtual void captureWheelEvents() { _wheelCaptured = true; }
137 virtual void releaseWheelEvents() { _wheelCaptured = false; }
138
139 virtual void captureActionEvents() { _actionsCaptured = true; }
140 virtual void releaseActionEvents() { _actionsCaptured = false; }
141
142 virtual void captureJoystick(int joystickIndex);93 virtual void captureJoystick(int joystickIndex);
143 virtual void releaseJoystick(int joystickIndex);94 virtual void releaseJoystick(int joystickIndex);
144 95
145 virtual glm::vec2 getViewportDimensions() const;96 virtual glm::vec2 getViewportDimensions() const;
146 97
147 /// Factory to create an InputController98 /// 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
102signals:
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
152private:123private:
153 QString sanatizeName(const QString& name); /// makes a name clean for inclusing in JavaScript124 QString sanatizeName(const QString& name); /// makes a name clean for inclusing in JavaScript
154 125
155 const PalmData* getPrimaryPalm() const;
156 const PalmData* getPalm(int palmIndex) const;
157 int getNumberOfActivePalms() const;
158 const PalmData* getActivePalm(int palmIndex) const;
159
160 bool _mouseCaptured;
161 bool _touchCaptured;
162 bool _wheelCaptured;
163 bool _actionsCaptured;
164 QMultiMap<int,KeyEvent> _capturedKeys;126 QMultiMap<int,KeyEvent> _capturedKeys;
165 QSet<int> _capturedJoysticks;127 QSet<int> _capturedJoysticks;
166 128
167 typedef std::map< AbstractInputController::Key, AbstractInputController* > InputControllerMap;129 using InputKey = controller::InputController::Key;
130 using InputControllerMap = std::map<InputKey, controller::InputController::Pointer>;
168 InputControllerMap _inputControllers;131 InputControllerMap _inputControllers;
169
170 void wireUpControllers(ScriptEngine* engine);
171
172};132};
173 133
174const int NUMBER_OF_SPATIALCONTROLS_PER_PALM = 2; // the hand and the tip134const int NUMBER_OF_SPATIALCONTROLS_PER_PALM = 2; // the hand and the tip
interface/src/ui/ApplicationCompositor.cpp+10−8
@@ -27,7 +27,7 @@ · 26 unmodified lines
27 27
28#include "Application.h"28#include "Application.h"
29#include <input-plugins/SixenseManager.h> // TODO: any references to sixense should be removed here29#include <input-plugins/SixenseManager.h> // TODO: any references to sixense should be removed here
30#include <input-plugins/InputDevice.h>30#include <controllers/InputDevice.h>
31 31
32 32
33// Used to animate the magnification windows33// Used to animate the magnification windows
@@ -320,8 +320,8 @@ void ApplicationCompositor::displayOverlayTextureHmd(RenderArgs* renderArgs, int · 286 unmodified lines
320 // Only render the hand pointers if the EnableHandMouseInput is enabled320 // Only render the hand pointers if the EnableHandMouseInput is enabled
321 if (Menu::getInstance()->isOptionChecked(MenuOption::EnableHandMouseInput)) {321 if (Menu::getInstance()->isOptionChecked(MenuOption::EnableHandMouseInput)) {
322 MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();322 MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
323 for (int i = 0; i < (int)myAvatar->getHand()->getNumPalms(); i++) {323 auto palms = myAvatar->getHand()->getCopyOfPalms();
324 PalmData& palm = myAvatar->getHand()->getPalms()[i];324 for (const auto& palm : palms) {
325 if (palm.isActive()) {325 if (palm.isActive()) {
326 glm::vec2 polar = getPolarCoordinates(palm);326 glm::vec2 polar = getPolarCoordinates(palm);
327 // Convert to quaternion327 // 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
449void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) {450void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) {
450 MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();451 MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
451 452
@@ -455,23 +456,24 @@ void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) { · 3 unmodified lines
455 static bool stateWhenPressed[NUMBER_OF_RETICLES] = { false, false, false };456 static bool stateWhenPressed[NUMBER_OF_RETICLES] = { false, false, false };
456 457
457 const HandData* handData = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHandData();458 const HandData* handData = DependencyManager::get<AvatarManager>()->getMyAvatar()->getHandData();
459 auto palms = handData->getCopyOfPalms();
458 460
459 for (unsigned int palmIndex = 2; palmIndex < 4; palmIndex++) {461 for (unsigned int palmIndex = 2; palmIndex < 4; palmIndex++) {
460 const int index = palmIndex - 1;462 const int index = palmIndex - 1;
461 463
462 const PalmData* palmData = NULL;464 const PalmData* palmData = NULL;
463 465
464 if (palmIndex >= handData->getPalms().size()) {466 if (palmIndex >= palms.size()) {
465 return;467 return;
466 }468 }
467 469
468 if (handData->getPalms()[palmIndex].isActive()) {470 if (palms[palmIndex].isActive()) {
469 palmData = &handData->getPalms()[palmIndex];471 palmData = &palms[palmIndex];
470 } else {472 } else {
471 continue;473 continue;
472 }474 }
473 475
474 int controllerButtons = palmData->getControllerButtons();476 int controllerButtons = 0;
475 477
476 //Check for if we should toggle or drag the magnification window478 //Check for if we should toggle or drag the magnification window
477 if (controllerButtons & BUTTON_3) {479 if (controllerButtons & BUTTON_3) {
@@ -521,7 +523,7 @@ void ApplicationCompositor::renderControllerPointers(gpu::Batch& batch) { · 43 unmodified lines
521 float yAngle = 0.5f - ((atan2f(direction.z, direction.y) + (float)PI_OVER_TWO));523 float yAngle = 0.5f - ((atan2f(direction.z, direction.y) + (float)PI_OVER_TWO));
522 524
523 // Get the pixel range over which the xAngle and yAngle are scaled525 // Get the pixel range over which the xAngle and yAngle are scaled
524 float cursorRange = canvasSize.x * InputDevice::getCursorPixelRangeMult();526 float cursorRange = canvasSize.x * controller::InputDevice::getCursorPixelRangeMult();
525 527
526 mouseX = (canvasSize.x / 2.0f + cursorRange * xAngle);528 mouseX = (canvasSize.x / 2.0f + cursorRange * xAngle);
527 mouseY = (canvasSize.y / 2.0f + cursorRange * yAngle);529 mouseY = (canvasSize.y / 2.0f + cursorRange * yAngle);
interface/src/ui/PreferencesDialog.cpp+2−2
@@ -198,7 +198,7 @@ void PreferencesDialog::loadPreferences() { · 197 unmodified lines
198 ui.oculusUIAngularSizeSpin->setValue(qApp->getApplicationCompositor().getHmdUIAngularSize());198 ui.oculusUIAngularSizeSpin->setValue(qApp->getApplicationCompositor().getHmdUIAngularSize());
199#endif199#endif
200 200
201 ui.sixenseReticleMoveSpeedSpin->setValue(InputDevice::getReticleMoveSpeed());201 ui.sixenseReticleMoveSpeedSpin->setValue(controller::InputDevice::getReticleMoveSpeed());
202 202
203 // LOD items203 // LOD items
204 auto lodManager = DependencyManager::get<LODManager>();204 auto lodManager = DependencyManager::get<LODManager>();
@@ -273,7 +273,7 @@ void PreferencesDialog::savePreferences() { · 68 unmodified lines
273 273
274 qApp->getApplicationCompositor().setHmdUIAngularSize(ui.oculusUIAngularSizeSpin->value());274 qApp->getApplicationCompositor().setHmdUIAngularSize(ui.oculusUIAngularSizeSpin->value());
275 275
276 InputDevice::setReticleMoveSpeed(ui.sixenseReticleMoveSpeedSpin->value());276 controller::InputDevice::setReticleMoveSpeed(ui.sixenseReticleMoveSpeedSpin->value());
277 277
278 auto audio = DependencyManager::get<AudioClient>();278 auto audio = DependencyManager::get<AudioClient>();
279 MixedProcessedAudioStream& stream = audio->getReceivedAudioStream();279 MixedProcessedAudioStream& stream = audio->getReceivedAudioStream();
libraries/animation/src/AnimVariant.cpp+121−0
@@ -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
18QScriptValue 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}
65void AnimVariantMap::copyVariantsFrom(const AnimVariantMap& other) {
66 for (auto& pair : other._map) {
67 _map[pair.first] = pair.second;
68 }
69}
70
71void 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}
libraries/animation/src/AnimVariant.h+17−2
@@ -12,11 +12,14 @@ · 11 unmodified lines
12#define hifi_AnimVariant_h12#define hifi_AnimVariant_h
13 13
14#include <cassert>14#include <cassert>
15#include <functional>
15#include <glm/glm.hpp>16#include <glm/glm.hpp>
16#include <glm/gtx/quaternion.hpp>17#include <glm/gtx/quaternion.hpp>
17#include <map>18#include <map>
18#include <set>19#include <set>
20#include <QScriptValue>
19#include "AnimationLogging.h"21#include "AnimationLogging.h"
22#include "StreamUtils.h"
20 23
21class AnimVariant {24class AnimVariant {
22public:25public:
@@ -58,8 +61,9 @@ public: · 35 unmodified lines
58 void setString(const QString& value) { assert(_type == Type::String); _stringVal = value; }61 void setString(const QString& value) { assert(_type == Type::String); _stringVal = value; }
59 62
60 bool getBool() const { assert(_type == Type::Bool); return _val.boolVal; }63 bool getBool() const { assert(_type == Type::Bool); return _val.boolVal; }
61 int getInt() const { assert(_type == Type::Int); return _val.intVal; }64 int getInt() const { assert(_type == Type::Int || _type == Type::Float); return _type == Type::Float ? (int)_val.floats[0] : _val.intVal; }
62 float getFloat() const { assert(_type == Type::Float); return _val.floats[0]; }65 float getFloat() const { assert(_type == Type::Float || _type == Type::Int); return _type == Type::Int ? (float)_val.intVal : _val.floats[0]; }
66
63 const glm::vec3& getVec3() const { assert(_type == Type::Vec3); return *reinterpret_cast<const glm::vec3*>(&_val); }67 const glm::vec3& getVec3() const { assert(_type == Type::Vec3); return *reinterpret_cast<const glm::vec3*>(&_val); }
64 const glm::quat& getQuat() const { assert(_type == Type::Quat); return *reinterpret_cast<const glm::quat*>(&_val); }68 const glm::quat& getQuat() const { assert(_type == Type::Quat); return *reinterpret_cast<const glm::quat*>(&_val); }
65 const glm::mat4& getMat4() const { assert(_type == Type::Mat4); return *reinterpret_cast<const glm::mat4*>(&_val); }69 const glm::mat4& getMat4() const { assert(_type == Type::Mat4); return *reinterpret_cast<const glm::mat4*>(&_val); }
@@ -156,8 +160,15 @@ public: · 90 unmodified lines
156 void setTrigger(const QString& key) { _triggers.insert(key); }160 void setTrigger(const QString& key) { _triggers.insert(key); }
157 void clearTriggers() { _triggers.clear(); }161 void clearTriggers() { _triggers.clear(); }
158 162
163 void clearMap() { _map.clear(); }
159 bool hasKey(const QString& key) const { return _map.find(key) != _map.end(); }164 bool hasKey(const QString& key) const { return _map.find(key) != _map.end(); }
160 165
166 // Answer a Plain Old Javascript Object (for the given engine) all of our values set as properties.
167 QScriptValue animVariantMapToScriptValue(QScriptEngine* engine, const QStringList& names, bool useNames) const;
168 // Side-effect us with the value of object's own properties. (No inherited properties.)
169 void animVariantMapFromScriptValue(const QScriptValue& object);
170 void copyVariantsFrom(const AnimVariantMap& other);
171
161#ifdef NDEBUG172#ifdef NDEBUG
162 void dump() const {173 void dump() const {
163 qCDebug(animation) << "AnimVariantMap =";174 qCDebug(animation) << "AnimVariantMap =";
@@ -196,4 +207,8 @@ protected: · 32 unmodified lines
196 std::set<QString> _triggers;207 std::set<QString> _triggers;
197};208};
198 209
210typedef std::function<void(QScriptValue)> AnimVariantResultHandler;
211Q_DECLARE_METATYPE(AnimVariantResultHandler);
212Q_DECLARE_METATYPE(AnimVariantMap)
213
199#endif // hifi_AnimVariant_h214#endif // hifi_AnimVariant_h
libraries/animation/src/Rig.cpp+67−0
@@ -13,6 +13,7 @@ · 12 unmodified lines
13 13
14#include <glm/gtx/vector_angle.hpp>14#include <glm/gtx/vector_angle.hpp>
15#include <queue>15#include <queue>
16#include <QScriptValueIterator>
16 17
17#include <NumericalConstants.h>18#include <NumericalConstants.h>
18#include <DebugDraw.h>19#include <DebugDraw.h>
@@ -576,6 +577,71 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos · 557 unmodified lines
576 _lastPosition = worldPosition;577 _lastPosition = worldPosition;
577}578}
578 579
580// Allow script to add/remove handlers and report results, from within their thread.
581QScriptValue 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}
595void 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}
599void 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
608void 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
579void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) {645void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) {
580 646
581 if (_enableAnimGraph) {647 if (_enableAnimGraph) {
@@ -583,6 +649,7 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { · 1 unmodified line
583 return;649 return;
584 }650 }
585 651
652 updateAnimationStateHandlers();
586 // evaluate the animation653 // evaluate the animation
587 AnimNode::Triggers triggersOut;654 AnimNode::Triggers triggersOut;
588 AnimPoseVec poses = _animNode->evaluate(_animVars, deltaTime, triggersOut);655 AnimPoseVec poses = _animNode->evaluate(_animVars, deltaTime, triggersOut);
libraries/animation/src/Rig.h+17−0
@@ -37,6 +37,8 @@ · 36 unmodified lines
37#define __hifi__Rig__37#define __hifi__Rig__
38 38
39#include <QObject>39#include <QObject>
40#include <QMutex>
41#include <QScriptValue>
40 42
41#include "JointState.h" // We might want to change this (later) to something that doesn't depend on gpu, fbx and model. -HRS43#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
52class Rig : public QObject, public std::enable_shared_from_this<Rig> {54class Rig : public QObject, public std::enable_shared_from_this<Rig> {
53public:55public:
56 struct StateHandler {
57 AnimVariantMap results;
58 QStringList propertyNames;
59 QScriptValue function;
60 bool useNames;
61 };
54 62
55 struct HeadParameters {63 struct HeadParameters {
56 float leanSideways = 0.0f; // degrees64 float leanSideways = 0.0f; // degrees
@@ -199,10 +207,14 @@ public: · 142 unmodified lines
199 AnimNode::ConstPointer getAnimNode() const { return _animNode; }207 AnimNode::ConstPointer getAnimNode() const { return _animNode; }
200 AnimSkeleton::ConstPointer getAnimSkeleton() const { return _animSkeleton; }208 AnimSkeleton::ConstPointer getAnimSkeleton() const { return _animSkeleton; }
201 bool disableHands {false}; // should go away with rig animation (and Rig::inverseKinematics)209 bool disableHands {false}; // should go away with rig animation (and Rig::inverseKinematics)
210 QScriptValue addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList);
211 void removeAnimationStateHandler(QScriptValue handler);
212 void animationStateHandlerResult(int identifier, QScriptValue result);
202 213
203 bool getModelOffset(glm::vec3& modelOffsetOut) const;214 bool getModelOffset(glm::vec3& modelOffsetOut) const;
204 215
205 protected:216 protected:
217 void updateAnimationStateHandlers();
206 218
207 void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist);219 void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist);
208 void updateNeckJoint(int index, const HeadParameters& params);220 void updateNeckJoint(int index, const HeadParameters& params);
@@ -241,6 +253,11 @@ public: · 32 unmodified lines
241 float _desiredStateAge = 0.0f;253 float _desiredStateAge = 0.0f;
242 float _leftHandOverlayAlpha = 0.0f;254 float _leftHandOverlayAlpha = 0.0f;
243 float _rightHandOverlayAlpha = 0.0f;255 float _rightHandOverlayAlpha = 0.0f;
256
257private:
258 QMap<int, StateHandler> _stateHandlers;
259 int _nextStateHandlerId {0};
260 QMutex _stateMutex;
244};261};
245 262
246#endif /* defined(__hifi__Rig__) */263#endif /* defined(__hifi__Rig__) */
libraries/audio/src/Sound.cpp+17−26
@@ -59,39 +59,30 @@ Sound::Sound(const QUrl& url, bool isStereo) : · 58 unmodified lines
59void Sound::downloadFinished(const QByteArray& data) {59void Sound::downloadFinished(const QByteArray& data) {
60 // replace our byte array with the downloaded data60 // replace our byte array with the downloaded data
61 QByteArray rawAudioByteArray = QByteArray(data);61 QByteArray rawAudioByteArray = QByteArray(data);
62 QString fileName = getURL().fileName();62 QString fileName = getURL().fileName().toLower();
63
64 const QString WAV_EXTENSION = ".wav";
65 63
64 static const QString WAV_EXTENSION = ".wav";
65 static const QString RAW_EXTENSION = ".raw";
66 if (fileName.endsWith(WAV_EXTENSION)) {66 if (fileName.endsWith(WAV_EXTENSION)) {
67 67
68 QString headerContentType = "audio/x-wav";68 QByteArray outputAudioByteArray;
69 //QByteArray headerContentType = reply->rawHeader("Content-Type");
70
71 // WAV audio file encountered
72 if (headerContentType == "audio/x-wav"
73 || headerContentType == "audio/wav"
74 || headerContentType == "audio/wave"
75 || fileName.endsWith(WAV_EXTENSION)) {
76
77 QByteArray outputAudioByteArray;
78 69
79 interpretAsWav(rawAudioByteArray, outputAudioByteArray);70 interpretAsWav(rawAudioByteArray, outputAudioByteArray);
80 downSample(outputAudioByteArray);71 downSample(outputAudioByteArray);
81 } else {72 trimFrames();
82 // check if this was a stereo raw file73 } else if (fileName.endsWith(RAW_EXTENSION)) {
83 // since it's raw the only way for us to know that is if the file was called .stereo.raw74 // check if this was a stereo raw file
84 if (fileName.toLower().endsWith("stereo.raw")) {75 // since it's raw the only way for us to know that is if the file was called .stereo.raw
85 _isStereo = true;76 if (fileName.toLower().endsWith("stereo.raw")) {
86 qCDebug(audio) << "Processing sound of" << rawAudioByteArray.size() << "bytes from" << getURL() << "as stereo audio file.";77 _isStereo = true;
87 }78 qCDebug(audio) << "Processing sound of" << rawAudioByteArray.size() << "bytes from" << getURL() << "as stereo audio file.";
88
89 // Process as RAW file
90 downSample(rawAudioByteArray);
91 }79 }
80
81 // Process as RAW file
82 downSample(rawAudioByteArray);
92 trimFrames();83 trimFrames();
93 } else {84 } else {
94 qCDebug(audio) << "Network reply without 'Content-Type'.";85 qCDebug(audio) << "Unknown sound file type";
95 }86 }
96 87
97 _isReady = true;88 _isReady = true;
libraries/avatars/src/HandData.cpp+18−37
@@ -21,62 +21,43 @@ · 20 unmodified lines
21HandData::HandData(AvatarData* owningAvatar) :21HandData::HandData(AvatarData* owningAvatar) :
22 _owningAvatarData(owningAvatar)22 _owningAvatarData(owningAvatar)
23{23{
24 // Start with two palms24 addNewPalm(LeftHand);
25 addNewPalm();25 addNewPalm(RightHand);
26 addNewPalm();
27}26}
28 27
29glm::vec3 HandData::worldToLocalVector(const glm::vec3& worldVector) const {28glm::vec3 HandData::worldToLocalVector(const glm::vec3& worldVector) const {
30 return glm::inverse(getBaseOrientation()) * worldVector / getBaseScale();29 return glm::inverse(getBaseOrientation()) * worldVector / getBaseScale();
31}30}
32 31
33PalmData& HandData::addNewPalm() {32PalmData& HandData::addNewPalm(Hand whichHand) {
34 _palms.push_back(PalmData(this));33 QWriteLocker locker(&_palmsLock);
34 _palms.push_back(PalmData(this, whichHand));
35 return _palms.back();35 return _palms.back();
36}36}
37 37
38const PalmData* HandData::getPalm(int sixSenseID) const {38PalmData HandData::getCopyOfPalmData(Hand hand) const {
39 // the palms are not necessarily added in left-right order, 39 QReadLocker locker(&_palmsLock);
40 // so we have to search for the right SixSenseID
41 for (unsigned int i = 0; i < _palms.size(); i++) {
42 const PalmData* palm = &(_palms[i]);
43 if (palm->getSixenseID() == sixSenseID) {
44 return palm->isActive() ? palm : NULL;
45 }
46 }
47 return NULL;
48}
49 40
50void HandData::getLeftRightPalmIndices(int& leftPalmIndex, int& rightPalmIndex) const {41 // the palms are not necessarily added in left-right order,
51 leftPalmIndex = -1;42 // so we have to search for the correct hand
52 rightPalmIndex = -1;43 for (const auto& palm : _palms) {
53 for (size_t i = 0; i < _palms.size(); i++) {44 if (palm.whichHand() == hand && palm.isActive()) {
54 const PalmData& palm = _palms[i];45 return palm;
55 if (palm.isActive()) {
56 if (palm.getSixenseID() == LEFT_HAND_INDEX) {
57 leftPalmIndex = i;
58 }
59 if (palm.getSixenseID() == RIGHT_HAND_INDEX) {
60 rightPalmIndex = i;
61 }
62 }46 }
63 }47 }
48 return PalmData(); // invalid hand
64}49}
65 50
66PalmData::PalmData(HandData* owningHandData) :51PalmData::PalmData(HandData* owningHandData, HandData::Hand hand) :
67_rawRotation(0.0f, 0.0f, 0.0f, 1.0f),52_rawRotation(0.0f, 0.0f, 0.0f, 1.0f),
68_rawPosition(0.0f),53_rawPosition(0.0f),
69_rawVelocity(0.0f),54_rawVelocity(0.0f),
70_rawAngularVelocity(0.0f),55_rawAngularVelocity(0.0f),
71_totalPenetration(0.0f),56_totalPenetration(0.0f),
72_controllerButtons(0),
73_isActive(false),57_isActive(false),
74_sixenseID(SIXENSEID_INVALID),
75_numFramesWithoutData(0),58_numFramesWithoutData(0),
76_owningHandData(owningHandData),59_owningHandData(owningHandData),
77_isCollidingWithVoxel(false),60_hand(hand) {
78_isCollidingWithPalm(false),
79_collisionlessPaddleExpiry(0) {
80}61}
81 62
82void PalmData::addToPosition(const glm::vec3& delta) {63void PalmData::addToPosition(const glm::vec3& delta) {
@@ -85,9 +66,9 @@ void PalmData::addToPosition(const glm::vec3& delta) { · 2 unmodified lines
85 66
86bool HandData::findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration, 67bool HandData::findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration,
87 const PalmData*& collidingPalm) const {68 const PalmData*& collidingPalm) const {
88 69 QReadLocker locker(&_palmsLock);
89 for (size_t i = 0; i < _palms.size(); ++i) {70
90 const PalmData& palm = _palms[i];71 for (const auto& palm : _palms) {
91 if (!palm.isActive()) {72 if (!palm.isActive()) {
92 continue;73 continue;
93 }74 }
libraries/avatars/src/HandData.h+47−52
@@ -12,26 +12,30 @@ · 11 unmodified lines
12#ifndef hifi_HandData_h12#ifndef hifi_HandData_h
13#define hifi_HandData_h13#define hifi_HandData_h
14 14
15#include <functional>
15#include <iostream>16#include <iostream>
16#include <vector>17#include <vector>
17 18
18#include <glm/glm.hpp>19#include <glm/glm.hpp>
19#include <glm/gtc/quaternion.hpp>20#include <glm/gtc/quaternion.hpp>
20 21
22#include <QReadWriteLock>
23
21#include <NumericalConstants.h>24#include <NumericalConstants.h>
22#include <SharedUtil.h>25#include <SharedUtil.h>
23 26
24class AvatarData;27class AvatarData;
25class PalmData;28class PalmData;
26 29
27const int LEFT_HAND_INDEX = 0;
28const int RIGHT_HAND_INDEX = 1;
29const int NUM_HANDS = 2;
30
31const int SIXENSEID_INVALID = -1;
32
33class HandData {30class HandData {
34public:31public:
32 enum Hand {
33 LeftHand,
34 RightHand,
35 UnknownHand,
36 NUMBER_OF_HANDS
37 };
38
35 HandData(AvatarData* owningAvatar);39 HandData(AvatarData* owningAvatar);
36 virtual ~HandData() {}40 virtual ~HandData() {}
37 41
@@ -46,15 +50,9 @@ public: · 8 unmodified lines
46 50
47 glm::vec3 worldToLocalVector(const glm::vec3& worldVector) const;51 glm::vec3 worldToLocalVector(const glm::vec3& worldVector) const;
48 52
49 std::vector<PalmData>& getPalms() { return _palms; }53 PalmData getCopyOfPalmData(Hand hand) const;
50 const std::vector<PalmData>& getPalms() const { return _palms; }
51 const PalmData* getPalm(int sixSenseID) const;
52 size_t getNumPalms() const { return _palms.size(); }
53 PalmData& addNewPalm();
54 54
55 /// Finds the indices of the left and right palms according to their locations, or -1 if either or55 std::vector<PalmData> getCopyOfPalms() const { QReadLocker locker(&_palmsLock); return _palms; }
56 /// both is not found.
57 void getLeftRightPalmIndices(int& leftPalmIndex, int& rightPalmIndex) const;
58 56
59 /// Checks for penetration between the described sphere and the hand.57 /// Checks for penetration between the described sphere and the hand.
60 /// \param penetratorCenter the center of the penetration test sphere58 /// \param penetratorCenter the center of the penetration test sphere
@@ -67,14 +65,22 @@ public: · 6 unmodified lines
67 65
68 glm::quat getBaseOrientation() const;66 glm::quat getBaseOrientation() const;
69 67
68 /// Allows a lamda function write access to the specific palm for this Hand, this might
69 /// modify the _palms vector
70 template<typename PalmModifierFunction> void modifyPalm(Hand whichHand, PalmModifierFunction callback);
71
70 friend class AvatarData;72 friend class AvatarData;
71protected:73protected:
72 AvatarData* _owningAvatarData;74 AvatarData* _owningAvatarData;
73 std::vector<PalmData> _palms;75 std::vector<PalmData> _palms;
76 mutable QReadWriteLock _palmsLock{ QReadWriteLock::Recursive };
74 77
75 glm::vec3 getBasePosition() const;78 glm::vec3 getBasePosition() const;
76 float getBaseScale() const;79 float getBaseScale() const;
77 80
81 PalmData& addNewPalm(Hand whichHand);
82 PalmData& getPalmData(Hand hand);
83
78private:84private:
79 // privatize copy ctor and assignment operator so copies of this object cannot be made85 // privatize copy ctor and assignment operator so copies of this object cannot be made
80 HandData(const HandData&);86 HandData(const HandData&);
@@ -84,25 +90,30 @@ private: · 3 unmodified lines
84 90
85class PalmData {91class PalmData {
86public:92public:
87 PalmData(HandData* owningHandData);93 PalmData(HandData* owningHandData = nullptr, HandData::Hand hand = HandData::UnknownHand);
88 glm::vec3 getPosition() const { return _owningHandData->localToWorldPosition(_rawPosition); }94 glm::vec3 getPosition() const { return _owningHandData->localToWorldPosition(_rawPosition); }
89 glm::vec3 getVelocity() const { return _owningHandData->localToWorldDirection(_rawVelocity); }95 glm::vec3 getVelocity() const { return _owningHandData->localToWorldDirection(_rawVelocity); }
90 96
91 const glm::vec3& getRawPosition() const { return _rawPosition; }97 const glm::vec3& getRawPosition() const { return _rawPosition; }
92 bool isActive() const { return _isActive; }98 bool isActive() const { return _isActive; }
93 int getSixenseID() const { return _sixenseID; }99 bool isValid() const { return _owningHandData; }
94 100
95 void setActive(bool active) { _isActive = active; }101 void setActive(bool active) { _isActive = active; }
96 void setSixenseID(int id) { _sixenseID = id; }
97 102
98 void setRawRotation(const glm::quat rawRotation) { _rawRotation = rawRotation; };103 HandData::Hand whichHand() const { return _hand; }
104 void setHand(HandData::Hand hand) { _hand = hand; }
105
106 void setRawRotation(const glm::quat& rawRotation) { _rawRotation = rawRotation; };
99 glm::quat getRawRotation() const { return _rawRotation; }107 glm::quat getRawRotation() const { return _rawRotation; }
100 glm::quat getRotation() const { return _owningHandData->getBaseOrientation() * _rawRotation; }108 glm::quat getRotation() const { return _owningHandData->getBaseOrientation() * _rawRotation; }
101 void setRawPosition(const glm::vec3& pos) { _rawPosition = pos; }109 void setRawPosition(const glm::vec3& pos) { _rawPosition = pos; }
102 void setRawVelocity(const glm::vec3& velocity) { _rawVelocity = velocity; }110 void setRawVelocity(const glm::vec3& velocity) { _rawVelocity = velocity; }
103 const glm::vec3& getRawVelocity() const { return _rawVelocity; }111 const glm::vec3& getRawVelocity() const { return _rawVelocity; }
112
104 void setRawAngularVelocity(const glm::vec3& angularVelocity) { _rawAngularVelocity = angularVelocity; }113 void setRawAngularVelocity(const glm::vec3& angularVelocity) { _rawAngularVelocity = angularVelocity; }
105 const glm::vec3& getRawAngularVelocity() const { return _rawAngularVelocity; }114 const glm::vec3& getRawAngularVelocity() const { return _rawAngularVelocity; }
115 glm::quat getRawAngularVelocityAsQuat() const { return glm::quat(_rawAngularVelocity); }
116
106 void addToPosition(const glm::vec3& delta);117 void addToPosition(const glm::vec3& delta);
107 118
108 void addToPenetration(const glm::vec3& penetration) { _totalPenetration += penetration; }119 void addToPenetration(const glm::vec3& penetration) { _totalPenetration += penetration; }
@@ -120,31 +131,11 @@ public: · 11 unmodified lines
120 void resetFramesWithoutData() { _numFramesWithoutData = 0; }131 void resetFramesWithoutData() { _numFramesWithoutData = 0; }
121 int getFramesWithoutData() const { return _numFramesWithoutData; }132 int getFramesWithoutData() const { return _numFramesWithoutData; }
122 133
123 // Controller buttons134 // FIXME - these are used in SkeletonModel::updateRig() the skeleton/rig should probably get this information
124 void setControllerButtons(unsigned int controllerButtons) { _controllerButtons = controllerButtons; }135 // from an action and/or the UserInputMapper instead of piping it through here.
125 void setLastControllerButtons(unsigned int controllerButtons) { _lastControllerButtons = controllerButtons; }
126
127 unsigned int getControllerButtons() const { return _controllerButtons; }
128 unsigned int getLastControllerButtons() const { return _lastControllerButtons; }
129
130 void setTrigger(float trigger) { _trigger = trigger; }136 void setTrigger(float trigger) { _trigger = trigger; }
131 float getTrigger() const { return _trigger; }137 float getTrigger() const { return _trigger; }
132 void setJoystick(float joystickX, float joystickY) { _joystickX = joystickX; _joystickY = joystickY; }
133 float getJoystickX() const { return _joystickX; }
134 float getJoystickY() const { return _joystickY; }
135 138
136 bool getIsCollidingWithVoxel() const { return _isCollidingWithVoxel; }
137 void setIsCollidingWithVoxel(bool isCollidingWithVoxel) { _isCollidingWithVoxel = isCollidingWithVoxel; }
138
139 bool getIsCollidingWithPalm() const { return _isCollidingWithPalm; }
140 void setIsCollidingWithPalm(bool isCollidingWithPalm) { _isCollidingWithPalm = isCollidingWithPalm; }
141
142 bool hasPaddle() const { return _collisionlessPaddleExpiry < usecTimestampNow(); }
143 void updateCollisionlessPaddleExpiry() { _collisionlessPaddleExpiry = usecTimestampNow() + USECS_PER_SECOND; }
144
145 /// Store position where the palm holds the ball.
146 void getBallHoldPosition(glm::vec3& position) const;
147
148 // return world-frame:139 // return world-frame:
149 glm::vec3 getFingerDirection() const;140 glm::vec3 getFingerDirection() const;
150 glm::vec3 getNormal() const;141 glm::vec3 getNormal() const;
@@ -155,25 +146,29 @@ private: · 4 unmodified lines
155 glm::vec3 _rawPosition;146 glm::vec3 _rawPosition;
156 glm::vec3 _rawVelocity;147 glm::vec3 _rawVelocity;
157 glm::vec3 _rawAngularVelocity;148 glm::vec3 _rawAngularVelocity;
149 glm::quat _rawDeltaRotation;
158 glm::quat _lastRotation;150 glm::quat _lastRotation;
159 151
160 glm::vec3 _tipPosition;152 glm::vec3 _tipPosition;
161 glm::vec3 _tipVelocity;153 glm::vec3 _tipVelocity;
162 glm::vec3 _totalPenetration; // accumulator for per-frame penetrations154 glm::vec3 _totalPenetration; /// accumulator for per-frame penetrations
163 155
164 unsigned int _controllerButtons;
165 unsigned int _lastControllerButtons;
166 float _trigger;156 float _trigger;
167 float _joystickX, _joystickY;
168 157
169 bool _isActive; // This has current valid data158 bool _isActive; /// This has current valid data
170 int _sixenseID; // Sixense controller ID for this palm159 int _numFramesWithoutData; /// after too many frames without data, this tracked object assumed lost.
171 int _numFramesWithoutData; // after too many frames without data, this tracked object assumed lost.
172 HandData* _owningHandData;160 HandData* _owningHandData;
173 161 HandData::Hand _hand;
174 bool _isCollidingWithVoxel; /// Whether the finger of this palm is inside a leaf voxel
175 bool _isCollidingWithPalm;
176 quint64 _collisionlessPaddleExpiry; /// Timestamp after which paddle starts colliding
177};162};
178 163
164template<typename PalmModifierFunction> void HandData::modifyPalm(Hand whichHand, PalmModifierFunction callback) {
165 QReadLocker locker(&_palmsLock);
166 for (auto& palm : _palms) {
167 if (palm.whichHand() == whichHand && palm.isValid()) {
168 callback(palm);
169 return;
170 }
171 }
172}
173
179#endif // hifi_HandData_h174#endif // hifi_HandData_h
libraries/controllers/CMakeLists.txt+14−0
@@ -0,0 +1,14 @@
1set(TARGET_NAME controllers)
2
3# set a default root dir for each of our optional externals if it was not passed
4setup_hifi_library(Script)
5
6# use setup_hifi_library macro to setup our project and link appropriate Qt modules
7link_hifi_libraries(shared)
8
9GroupSources("src/controllers")
10
11add_dependency_external_projects(glm)
12find_package(GLM REQUIRED)
13target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS})
14
libraries/controllers/src/controllers/Actions.cpp+120−0
@@ -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
13namespace 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}
libraries/controllers/src/controllers/Actions.h+103−0
@@ -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
18namespace 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
22enum 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
84template <typename T>
85int toInt(T enumValue) { return static_cast<int>(enumValue); }
86
87class ActionsDevice : public QObject, public InputDevice {
88 Q_OBJECT
89 Q_PROPERTY(QString name READ getName)
90
91public:
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
libraries/controllers/src/controllers/DeviceProxy.cpp+14−0
@@ -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
12namespace controller {
13}
14
libraries/controllers/src/controllers/DeviceProxy.h+47−0
@@ -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
22namespace 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
libraries/controllers/src/controllers/Forward.h+37−0
@@ -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
13namespace controller {
14
15class Endpoint;
16using EndpointPointer = std::shared_ptr<Endpoint>;
17using EndpointList = std::list<EndpointPointer>;
18
19class Filter;
20using FilterPointer = std::shared_ptr<Filter>;
21using FilterList = std::list<FilterPointer>;
22
23class Route;
24using RoutePointer = std::shared_ptr<Route>;
25using RouteList = std::list<RoutePointer>;
26
27class Conditional;
28using ConditionalPointer = std::shared_ptr<Conditional>;
29using ConditionalList = std::list<ConditionalPointer>;
30
31class Mapping;
32using MappingPointer = std::shared_ptr<Mapping>;
33using MappingList = std::list<MappingPointer>;
34
35}
36
37#endif
libraries/controllers/src/controllers/Input.cpp+17−0
@@ -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
11namespace 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
libraries/controllers/src/controllers/Input.h+74−0
@@ -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
16namespace controller {
17
18enum 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
30struct 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
libraries/controllers/src/controllers/InputDevice.cpp+114−0
@@ -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
16namespace 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}
libraries/controllers/src/controllers/InputDevice.hrenamed from libraries/input-plugins/src/input-plugins/InputDevice.h+46−11
@@ -10,12 +10,27 @@ · 9 unmodified lines
10//10//
11#pragma once11#pragma once
12 12
13#include "UserInputMapper.h"13#include <memory>
14#include <map>
15#include <unordered_set>
16
17#include <QtCore/QString>
18
19#include "Pose.h"
20#include "Input.h"
21#include "StandardControls.h"
22#include "DeviceProxy.h"
23
14 24
15// Event types for each controller25// Event types for each controller
16const unsigned int CONTROLLER_0_EVENT = 1500U;26const unsigned int CONTROLLER_0_EVENT = 1500U;
17const unsigned int CONTROLLER_1_EVENT = 1501U;27const unsigned int CONTROLLER_1_EVENT = 1501U;
18 28
29namespace controller {
30
31class Endpoint;
32using EndpointPointer = std::shared_ptr<Endpoint>;
33
19// NOTE: If something inherits from both InputDevice and InputPlugin, InputPlugin must go first.34// NOTE: If something inherits from both InputDevice and InputPlugin, InputPlugin must go first.
20// e.g. class Example : public InputPlugin, public InputDevice35// e.g. class Example : public InputPlugin, public InputDevice
21// instead of class Example : public InputDevice, public InputPlugin36// instead of class Example : public InputDevice, public InputPlugin
@@ -23,17 +38,22 @@ class InputDevice { · 1 unmodified line
23public:38public:
24 InputDevice(const QString& name) : _name(name) {}39 InputDevice(const QString& name) : _name(name) {}
25 40
41 using Pointer = std::shared_ptr<InputDevice>;
42
26 typedef std::unordered_set<int> ButtonPressedMap;43 typedef std::unordered_set<int> ButtonPressedMap;
27 typedef std::map<int, float> AxisStateMap;44 typedef std::map<int, float> AxisStateMap;
28 typedef std::map<int, UserInputMapper::PoseValue> PoseStateMap;45 typedef std::map<int, Pose> PoseStateMap;
29 46
30 // Get current state for each channel47 // Get current state for each channel
31 float getButton(int channel) const;48 float getButton(int channel) const;
32 float getAxis(int channel) const;49 float getAxis(int channel) const;
33 UserInputMapper::PoseValue getPose(int channel) const;50 Pose getPose(int channel) const;
34 51
35 virtual void registerToUserInputMapper(UserInputMapper& mapper) = 0;52 float getValue(const Input& input) const;
36 virtual void assignDefaultInputMapping(UserInputMapper& mapper) = 0;53 float getValue(ChannelType channelType, uint16_t channel) const;
54 Pose getPoseValue(uint16_t channel) const;
55
56 const QString& getName() const { return _name; }
37 57
38 // Update call MUST be called once per simulation loop58 // Update call MUST be called once per simulation loop
39 // It takes care of updating the action states and deltas59 // It takes care of updating the action states and deltas
@@ -42,20 +62,33 @@ public: · 2 unmodified lines
42 virtual void focusOutEvent() = 0;62 virtual void focusOutEvent() = 0;
43 63
44 int getDeviceID() { return _deviceID; }64 int getDeviceID() { return _deviceID; }
65 void setDeviceID(int deviceID) { _deviceID = deviceID; }
45 66
46 static float getCursorPixelRangeMult();67 static float getCursorPixelRangeMult();
47 static float getReticleMoveSpeed() { return reticleMoveSpeed; }68 static float getReticleMoveSpeed() { return _reticleMoveSpeed; }
48 static void setReticleMoveSpeed(float sixenseReticleMoveSpeed) { reticleMoveSpeed = sixenseReticleMoveSpeed; }69 static void setReticleMoveSpeed(float reticleMoveSpeed) { _reticleMoveSpeed = reticleMoveSpeed; }
49 70
50 static bool getLowVelocityFilter() { return _lowVelocityFilter; };71 static bool getLowVelocityFilter() { return _lowVelocityFilter; };
51 72
73 Input makeInput(StandardButtonChannel button) const;
74 Input makeInput(StandardAxisChannel axis) const;
75 Input makeInput(StandardPoseChannel pose) const;
76 Input::NamedPair makePair(StandardButtonChannel button, const QString& name) const;
77 Input::NamedPair makePair(StandardAxisChannel button, const QString& name) const;
78 Input::NamedPair makePair(StandardPoseChannel button, const QString& name) const;
52public slots:79public slots:
53 static void setLowVelocityFilter(bool newLowVelocityFilter) { _lowVelocityFilter = newLowVelocityFilter; };80 static void setLowVelocityFilter(bool newLowVelocityFilter) { _lowVelocityFilter = newLowVelocityFilter; };
54 81
55protected:82protected:
56 int _deviceID = 0;83 friend class UserInputMapper;
84
85 virtual Input::NamedVector getAvailableInputs() const = 0;
86 virtual QString getDefaultMappingConfig() const { return QString(); }
87 virtual EndpointPointer createEndpoint(const Input& input) const;
57 88
58 QString _name;89 uint16_t _deviceID { Input::INVALID_DEVICE };
90
91 const QString _name;
59 92
60 ButtonPressedMap _buttonPressedMap;93 ButtonPressedMap _buttonPressedMap;
61 AxisStateMap _axisStateMap;94 AxisStateMap _axisStateMap;
@@ -64,5 +97,7 @@ protected: · 2 unmodified lines
64 static bool _lowVelocityFilter;97 static bool _lowVelocityFilter;
65 98
66private:99private:
67 static float reticleMoveSpeed;100 static float _reticleMoveSpeed;
68};101};
102
103}
libraries/controllers/src/controllers/Logging.cpp+11−0
@@ -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
11Q_LOGGING_CATEGORY(controllers, "hifi.controllers")
libraries/controllers/src/controllers/Logging.h+16−0
@@ -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
14Q_DECLARE_LOGGING_CATEGORY(controllers)
15
16#endif
libraries/controllers/src/controllers/Pose.cpp+49−0
@@ -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
16namespace 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
libraries/controllers/src/controllers/Pose.h+50−0
@@ -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
14class QScriptEngine;
15class QScriptValue;
16
17#include <GLMHelpers.h>
18
19namespace 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
libraries/controllers/src/controllers/ScriptingInterface.cpp+241−0
@@ -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
28static QRegularExpression SANITIZE_NAME_EXPRESSION{ "[\\(\\)\\.\\s]" };
29
30static 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
44controller::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
75namespace 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
libraries/controllers/src/controllers/ScriptingInterface.h+155−0
@@ -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
36namespace 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
libraries/controllers/src/controllers/StandardController.cpp+158−0
@@ -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
19namespace controller {
20
21StandardController::StandardController() : InputDevice("Standard") {
22 _deviceID = UserInputMapper::STANDARD_DEVICE;
23}
24
25StandardController::~StandardController() {
26}
27
28void StandardController::update(float deltaTime, bool jointsCaptured) {
29}
30
31void StandardController::focusOutEvent() {
32 _axisStateMap.clear();
33 _buttonPressedMap.clear();
34};
35
36Input::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
112EndpointPointer StandardController::createEndpoint(const Input& input) const {
113 return std::make_shared<StandardEndpoint>(input);
114}
115
116QString 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}
libraries/controllers/src/controllers/StandardController.h+40−0
@@ -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
21namespace controller {
22
23class StandardController : public QObject, public InputDevice {
24 Q_OBJECT
25 Q_PROPERTY(QString name READ getName)
26
27public:
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
libraries/controllers/src/controllers/StandardControls.h+85−0
@@ -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
10namespace 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}
libraries/controllers/src/controllers/StateController.cpp+50−0
@@ -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
20namespace controller {
21
22StateController::StateController() : InputDevice("Application") {
23}
24
25StateController::~StateController() {
26}
27
28void StateController::update(float deltaTime, bool jointsCaptured) {}
29
30void StateController::focusOutEvent() {}
31
32void StateController::addInputVariant(QString name, ReadLambda lambda) {
33 _namedReadLambdas.push_back(NamedReadLambda(name, lambda));
34}
35
36Input::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
46EndpointPointer StateController::createEndpoint(const Input& input) const {
47 return std::make_shared<LambdaEndpoint>(_namedReadLambdas[input.getChannel()].second);
48}
49
50}
libraries/controllers/src/controllers/StateController.h+51−0
@@ -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
20namespace controller {
21
22class StateController : public QObject, public InputDevice {
23 Q_OBJECT
24 Q_PROPERTY(QString name READ getName)
25
26public:
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
45protected:
46 QVector<NamedReadLambda> _namedReadLambdas;
47};
48
49}
50
51#endif // hifi_StateController_h
libraries/controllers/src/controllers/UserInputMapper.cpp+1086−0
@@ -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
45namespace 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
51controller::UserInputMapper::UserInputMapper() {
52 registerDevice(std::make_shared<ActionsDevice>());
53 registerDevice(std::make_shared<StandardController>());
54}
55
56namespace controller {
57
58
59UserInputMapper::~UserInputMapper() {
60}
61
62int 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
70void 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
112void 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
132void 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
153InputDevice::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
163QString 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
171int 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
181QVector<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
191int UserInputMapper::findAction(const QString& actionName) const {
192 return findDeviceInput("Actions." + actionName).getChannel();
193}
194
195Input 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
229void 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
235void 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
287Input::NamedVector UserInputMapper::getAvailableInputs(uint16 deviceID) const {
288 Locker locker(_lock);
289 auto iterator = _registeredDevices.find(deviceID);
290 return iterator->second->getAvailableInputs();
291}
292
293QVector<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
302QString 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
313QVector<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/*
322void 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
351static int actionMetaTypeId = qRegisterMetaType<Action>();
352static int inputMetaTypeId = qRegisterMetaType<Input>();
353static int inputPairMetaTypeId = qRegisterMetaType<Input::NamedPair>();
354static int poseMetaTypeId = qRegisterMetaType<controller::Pose>("Pose");
355
356
357QScriptValue inputToScriptValue(QScriptEngine* engine, const Input& input);
358void inputFromScriptValue(const QScriptValue& object, Input& input);
359QScriptValue actionToScriptValue(QScriptEngine* engine, const Action& action);
360void actionFromScriptValue(const QScriptValue& object, Action& action);
361QScriptValue inputPairToScriptValue(QScriptEngine* engine, const Input::NamedPair& inputPair);
362void inputPairFromScriptValue(const QScriptValue& object, Input::NamedPair& inputPair);
363
364QScriptValue 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
373void inputFromScriptValue(const QScriptValue& object, Input& input) {
374 input.id = object.property("id").toInt32();
375}
376
377QScriptValue 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
385void actionFromScriptValue(const QScriptValue& object, Action& action) {
386 action = Action(object.property("action").toVariant().toInt());
387}
388
389QScriptValue 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
396void 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
401void 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
411Input UserInputMapper::makeStandardInput(controller::StandardButtonChannel button) {
412 return Input(STANDARD_DEVICE, button, ChannelType::BUTTON);
413}
414
415Input UserInputMapper::makeStandardInput(controller::StandardAxisChannel axis) {
416 return Input(STANDARD_DEVICE, axis, ChannelType::AXIS);
417}
418
419Input UserInputMapper::makeStandardInput(controller::StandardPoseChannel pose) {
420 return Input(STANDARD_DEVICE, pose, ChannelType::POSE);
421}
422
423static auto lastDebugTime = usecTimestampNow();
424static auto debugRoutes = false;
425static auto debuggableRoutes = false;
426static const auto DEBUG_INTERVAL = USECS_PER_SECOND;
427
428void 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
460void 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
485bool 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
576Endpoint::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
590Endpoint::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
619Endpoint::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
629Endpoint::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
643Mapping::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
677void 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
694float UserInputMapper::getValue(const Endpoint::Pointer& endpoint) {
695 return endpoint->value();
696}
697
698float 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
707Pose UserInputMapper::getPose(const Endpoint::Pointer& endpoint) {
708 if (!endpoint->isPose()) {
709 return Pose();
710 }
711 return endpoint->pose();
712}
713
714Pose 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
723Mapping::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
741static const QString JSON_NAME = QStringLiteral("name");
742static const QString JSON_CHANNELS = QStringLiteral("channels");
743static const QString JSON_CHANNEL_FROM = QStringLiteral("from");
744static const QString JSON_CHANNEL_DEBUG = QStringLiteral("debug");
745static const QString JSON_CHANNEL_WHEN = QStringLiteral("when");
746static const QString JSON_CHANNEL_TO = QStringLiteral("to");
747static const QString JSON_CHANNEL_FILTERS = QStringLiteral("filters");
748
749Endpoint::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
774Conditional::Pointer UserInputMapper::conditionalFor(const QJSValue& condition) {
775 return Conditional::Pointer();
776}
777
778Conditional::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
804Conditional::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
814Conditional::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
840Filter::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
855Filter::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
880Endpoint::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
896Endpoint::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
917Endpoint::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
932Endpoint::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
945Route::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
994Mapping::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
1014Mapping::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
1035template <typename T>
1036bool hasDebuggableRoute(const T& routes) {
1037 for (auto route : routes) {
1038 if (route->debug) {
1039 return true;
1040 }
1041 }
1042 return false;
1043}
1044
1045
1046void 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
1069void 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
libraries/controllers/src/controllers/UserInputMapper.h+197−0
@@ -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
34namespace 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
187Q_DECLARE_METATYPE(controller::Input::NamedPair)
188Q_DECLARE_METATYPE(controller::Pose)
189Q_DECLARE_METATYPE(QVector<controller::Input::NamedPair>)
190Q_DECLARE_METATYPE(controller::Input)
191Q_DECLARE_METATYPE(controller::Action)
192Q_DECLARE_METATYPE(QVector<controller::Action>)
193
194// Cheating.
195using UserInputMapper = controller::UserInputMapper;
196
197#endif // hifi_UserInputMapper_h
libraries/controllers/src/controllers/impl/Conditional.cpp+21−0
@@ -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
15namespace controller {
16
17 Conditional::Pointer Conditional::parse(const QJsonValue& json) {
18 return Conditional::Pointer();
19 }
20
21}
libraries/controllers/src/controllers/impl/Conditional.h+54−0
@@ -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
20class QJsonValue;
21
22namespace 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
libraries/controllers/src/controllers/impl/Endpoint.cpp+14−0
@@ -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
11namespace controller {
12
13}
14
libraries/controllers/src/controllers/impl/Endpoint.h+89−0
@@ -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
22class QScriptValue;
23
24namespace 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
libraries/controllers/src/controllers/impl/Filter.cpp+196−0
@@ -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
28using namespace controller;
29
30Filter::Factory Filter::_factory;
31
32REGISTER_FILTER_CLASS_INSTANCE(ClampFilter, "clamp")
33REGISTER_FILTER_CLASS_INSTANCE(ConstrainToIntegerFilter, "constrainToInteger")
34REGISTER_FILTER_CLASS_INSTANCE(ConstrainToPositiveIntegerFilter, "constrainToPositiveInteger")
35REGISTER_FILTER_CLASS_INSTANCE(DeadZoneFilter, "deadZone")
36REGISTER_FILTER_CLASS_INSTANCE(HysteresisFilter, "hysteresis")
37REGISTER_FILTER_CLASS_INSTANCE(InvertFilter, "invert")
38REGISTER_FILTER_CLASS_INSTANCE(ScaleFilter, "scale")
39REGISTER_FILTER_CLASS_INSTANCE(PulseFilter, "pulse")
40
41const QString JSON_FILTER_TYPE = QStringLiteral("type");
42const QString JSON_FILTER_PARAMS = QStringLiteral("params");
43
44
45Filter::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
68bool 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
93namespace 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
libraries/controllers/src/controllers/impl/Filter.h+59−0
@@ -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
24class QJsonValue;
25
26namespace 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
libraries/controllers/src/controllers/impl/Mapping.cpp+8−0
@@ -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"
libraries/controllers/src/controllers/impl/Mapping.h+37−0
@@ -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
22namespace 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
libraries/controllers/src/controllers/impl/MappingBuilderProxy.cpp+63−0
@@ -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
21using namespace controller;
22
23QObject* 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
29QObject* 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
35QObject* 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
46QObject* 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
52QObject* 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
58QObject* MappingBuilderProxy::enable(bool enable) {
59 _parent.enableMapping(_mapping->name, enable);
60 return this;
61}
62
63
libraries/controllers/src/controllers/impl/MappingBuilderProxy.h+58−0
@@ -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
18class QJSValue;
19class QScriptValue;
20class QJsonValue;
21
22namespace controller {
23
24class ScriptingInterface;
25class 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)
29class MappingBuilderProxy : public QObject {
30 Q_OBJECT
31public:
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
44protected:
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
libraries/controllers/src/controllers/impl/Route.cpp+9−0
@@ -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"
libraries/controllers/src/controllers/impl/Route.h+35−0
@@ -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
17namespace 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
libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp+108−0
@@ -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
30using namespace controller;
31
32void RouteBuilderProxy::toQml(const QJSValue& destination) {
33 qCDebug(controllers) << "Completing route " << destination.toString();
34 auto destinationEndpoint = _parent.endpointFor(destination);
35 return to(destinationEndpoint);
36}
37
38void RouteBuilderProxy::to(const QScriptValue& destination) {
39 qCDebug(controllers) << "Completing route " << destination.toString();
40 auto destinationEndpoint = _parent.endpointFor(destination);
41 return to(destinationEndpoint);
42}
43
44void RouteBuilderProxy::to(const Endpoint::Pointer& destination) {
45 _route->destination = destination;
46 _mapping->routes.push_back(_route);
47 deleteLater();
48}
49
50QObject* RouteBuilderProxy::debug(bool enable) {
51 _route->debug = enable;
52 return this;
53}
54
55QObject* RouteBuilderProxy::when(const QScriptValue& expression) {
56 _route->conditional = _parent.conditionalFor(expression);
57 return this;
58}
59
60QObject* RouteBuilderProxy::whenQml(const QJSValue& expression) {
61 _route->conditional = _parent.conditionalFor(expression);
62 return this;
63}
64
65QObject* RouteBuilderProxy::clamp(float min, float max) {
66 addFilter(std::make_shared<ClampFilter>(min, max));
67 return this;
68}
69
70QObject* RouteBuilderProxy::scale(float multiplier) {
71 addFilter(std::make_shared<ScaleFilter>(multiplier));
72 return this;
73}
74
75QObject* RouteBuilderProxy::invert() {
76 addFilter(std::make_shared<InvertFilter>());
77 return this;
78}
79
80QObject* RouteBuilderProxy::hysteresis(float min, float max) {
81 addFilter(std::make_shared<HysteresisFilter>(min, max));
82 return this;
83}
84
85QObject* RouteBuilderProxy::deadZone(float min) {
86 addFilter(std::make_shared<DeadZoneFilter>(min));
87 return this;
88}
89
90QObject* RouteBuilderProxy::constrainToInteger() {
91 addFilter(std::make_shared<ConstrainToIntegerFilter>());
92 return this;
93}
94
95QObject* RouteBuilderProxy::constrainToPositiveInteger() {
96 addFilter(std::make_shared<ConstrainToPositiveIntegerFilter>());
97 return this;
98}
99
100QObject* RouteBuilderProxy::pulse(float interval) {
101 addFilter(std::make_shared<PulseFilter>(interval));
102 return this;
103}
104
105void RouteBuilderProxy::addFilter(Filter::Pointer filter) {
106 _route->filters.push_back(filter);
107}
108
libraries/controllers/src/controllers/impl/RouteBuilderProxy.h+61−0
@@ -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
20class QJSValue;
21class QScriptValue;
22class QJsonValue;
23
24namespace controller {
25
26class 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)
30class 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
51private:
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
libraries/controllers/src/controllers/impl/conditionals/AndConditional.cpp+21−0
@@ -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
11using namespace controller;
12
13bool AndConditional::satisfied() {
14 for (auto& conditional : _children) {
15 if (!conditional->satisfied()) {
16 return false;
17 }
18 }
19 return true;
20}
21
libraries/controllers/src/controllers/impl/conditionals/AndConditional.h+31−0
@@ -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
15namespace controller {
16
17class AndConditional : public Conditional {
18public:
19 using Pointer = std::shared_ptr<AndConditional>;
20
21 AndConditional(Conditional::List children) : _children(children) { }
22
23 virtual bool satisfied() override;
24
25private:
26 Conditional::List _children;
27};
28
29}
30
31#endif
libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.cpp+9−0
@@ -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"
libraries/controllers/src/controllers/impl/conditionals/EndpointConditional.h+27−0
@@ -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
16namespace controller {
17
18class EndpointConditional : public Conditional {
19public:
20 EndpointConditional(Endpoint::Pointer endpoint) : _endpoint(endpoint) {}
21 virtual bool satisfied() override { return _endpoint && _endpoint->value() != 0.0; }
22private:
23 Endpoint::Pointer _endpoint;
24};
25
26}
27#endif
libraries/controllers/src/controllers/impl/conditionals/NotConditional.cpp+9−0
@@ -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"
libraries/controllers/src/controllers/impl/conditionals/NotConditional.h+16−0
@@ -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
libraries/controllers/src/controllers/impl/conditionals/ScriptConditional.cpp+27−0
@@ -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
13using namespace controller;
14
15bool ScriptConditional::satisfied() {
16 updateValue();
17 return _lastValue;
18}
19
20void ScriptConditional::updateValue() {
21 if (QThread::currentThread() != thread()) {
22 QMetaObject::invokeMethod(this, "updateValue", Qt::QueuedConnection);
23 return;
24 }
25
26 _lastValue = _callable.call().toBool();
27}
libraries/controllers/src/controllers/impl/conditionals/ScriptConditional.h+34−0
@@ -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
19namespace controller {
20
21class ScriptConditional : public QObject, public Conditional {
22 Q_OBJECT;
23public:
24 ScriptConditional(const QScriptValue& callable) : _callable(callable) { }
25 virtual bool satisfied() override;
26protected:
27 Q_INVOKABLE void updateValue();
28private:
29 QScriptValue _callable;
30 bool _lastValue { false };
31};
32
33}
34#endif
libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.cpp+40−0
@@ -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
15using namespace controller;
16
17void 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
25void 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
36void ActionEndpoint::reset() {
37 _currentValue = 0.0f;
38 _currentPose = Pose();
39}
40
libraries/controllers/src/controllers/impl/endpoints/ActionEndpoint.h+41−0
@@ -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
20namespace controller {
21
22class ActionEndpoint : public Endpoint {
23public:
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
34private:
35 float _currentValue{ 0.0f };
36 Pose _currentPose{};
37};
38
39}
40
41#endif
libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.cpp+63−0
@@ -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
13using namespace controller;
14
15AnyEndpoint::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
30float 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
41void 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)
46bool AnyEndpoint::writeable() const {
47 for (auto& child : _children) {
48 if (!child->writeable()) {
49 return false;
50 }
51 }
52 return true;
53}
54
55bool AnyEndpoint::readable() const {
56 for (auto& child : _children) {
57 if (!child->readable()) {
58 return false;
59 }
60 }
61 return true;
62}
63
libraries/controllers/src/controllers/impl/endpoints/AnyEndpoint.h+32−0
@@ -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
15namespace controller {
16
17class AnyEndpoint : public Endpoint {
18 friend class UserInputMapper;
19public:
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
26private:
27 Endpoint::List _children;
28};
29
30}
31
32#endif
libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.cpp+9−0
@@ -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"
libraries/controllers/src/controllers/impl/endpoints/ArrayEndpoint.h+43−0
@@ -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
15namespace controller {
16
17class ArrayEndpoint : public Endpoint {
18 friend class UserInputMapper;
19public:
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
37private:
38 Endpoint::List _children;
39};
40
41}
42
43#endif
libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.cpp+36−0
@@ -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
13namespace controller {
14
15CompositeEndpoint::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
23bool CompositeEndpoint::readable() const {
24 return first->readable() && second->readable();
25}
26
27float CompositeEndpoint::value() {
28 float result = first->value() * -1.0f + second->value();
29 return result;
30}
31
32void CompositeEndpoint::apply(float newValue, const Pointer& source) {
33 // Composites are read only
34}
35
36}
libraries/controllers/src/controllers/impl/endpoints/CompositeEndpoint.h+28−0
@@ -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
15namespace 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
libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.cpp+41−0
@@ -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
15using namespace controller;
16float 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
29Pose 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
libraries/controllers/src/controllers/impl/endpoints/InputEndpoint.h+39−0
@@ -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
15namespace controller {
16
17class InputEndpoint : public Endpoint {
18public:
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
33private:
34 bool _read { false };
35};
36
37}
38
39#endif
libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.cpp+9−0
@@ -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"
libraries/controllers/src/controllers/impl/endpoints/JSEndpoint.h+41−0
@@ -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
18namespace controller {
19
20class JSEndpoint : public Endpoint {
21public:
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
35private:
36 QJSValue _callable;
37};
38
39}
40
41#endif
libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.cpp+46−0
@@ -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
13using namespace controller;
14
15float ScriptEndpoint::value() {
16 updateValue();
17 return _lastValueRead;
18}
19
20void 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
29void ScriptEndpoint::apply(float value, const Pointer& source) {
30 if (value == _lastValueWritten) {
31 return;
32 }
33 internalApply(value, source->getInput().getID());
34}
35
36void 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}
libraries/controllers/src/controllers/impl/endpoints/ScriptEndpoint.h+40−0
@@ -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
17namespace controller {
18
19class ScriptEndpoint : public Endpoint {
20 Q_OBJECT;
21public:
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
29protected:
30 Q_INVOKABLE void updateValue();
31 Q_INVOKABLE virtual void internalApply(float newValue, int sourceID);
32private:
33 QScriptValue _callable;
34 float _lastValueRead { 0.0f };
35 float _lastValueWritten { 0.0f };
36};
37
38}
39
40#endif
libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.cpp+9−0
@@ -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"
libraries/controllers/src/controllers/impl/endpoints/StandardEndpoint.h+60−0
@@ -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
15namespace controller {
16
17class StandardEndpoint : public VirtualEndpoint {
18public:
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
53private:
54 bool _written { false };
55 bool _read { false };
56};
57
58}
59
60#endif
libraries/controllers/src/controllers/impl/filters/ClampFilter.cpp+38−0
@@ -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
14using namespace controller;
15
16bool 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}
libraries/controllers/src/controllers/impl/filters/ClampFilter.h+32−0
@@ -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
15namespace controller {
16
17class ClampFilter : public Filter {
18 REGISTER_FILTER_CLASS(ClampFilter);
19public:
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;
25protected:
26 float _min = 0.0f;
27 float _max = 1.0f;
28};
29
30}
31
32#endif
libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.cpp+9−0
@@ -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"
libraries/controllers/src/controllers/impl/filters/ConstrainToIntegerFilter.h+30−0
@@ -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
15namespace controller {
16
17class ConstrainToIntegerFilter : public Filter {
18 REGISTER_FILTER_CLASS(ConstrainToIntegerFilter);
19public:
20 ConstrainToIntegerFilter() {};
21
22 virtual float apply(float value) const override {
23 return glm::sign(value);
24 }
25protected:
26};
27
28}
29
30#endif
libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.cpp+9−0
@@ -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"
libraries/controllers/src/controllers/impl/filters/ConstrainToPositiveIntegerFilter.h+30−0
@@ -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
15namespace controller {
16
17class ConstrainToPositiveIntegerFilter : public Filter {
18 REGISTER_FILTER_CLASS(ConstrainToPositiveIntegerFilter);
19public:
20 ConstrainToPositiveIntegerFilter() {};
21
22 virtual float apply(float value) const override {
23 return (value <= 0.0f) ? 0.0f : 1.0f;
24 }
25protected:
26};
27
28}
29
30#endif
libraries/controllers/src/controllers/impl/filters/DeadZoneFilter.cpp+26−0
@@ -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
14using namespace controller;
15float 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
23bool DeadZoneFilter::parseParameters(const QJsonValue& parameters) {
24 static const QString JSON_MIN = QStringLiteral("min");
25 return parseSingleFloatParameter(parameters, JSON_MIN, _min);
26}
libraries/controllers/src/controllers/impl/filters/DeadZoneFilter.h+31−0
@@ -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
15namespace controller {
16
17class DeadZoneFilter : public Filter {
18 REGISTER_FILTER_CLASS(DeadZoneFilter);
19public:
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;
24protected:
25 float _min = 0.0f;
26};
27
28
29}
30
31#endif
libraries/controllers/src/controllers/impl/filters/HysteresisFilter.cpp+64−0
@@ -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
14using namespace controller;
15
16HysteresisFilter::HysteresisFilter(float min, float max) : _min(min), _max(max) {
17 if (_min > _max) {
18 std::swap(_min, _max);
19 }
20};
21
22
23float 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
36bool 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}
libraries/controllers/src/controllers/impl/filters/HysteresisFilter.h+31−0
@@ -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
15namespace controller {
16
17class HysteresisFilter : public Filter {
18 REGISTER_FILTER_CLASS(HysteresisFilter);
19public:
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;
23protected:
24 float _min;
25 float _max;
26 mutable bool _signaled { false };
27};
28
29}
30
31#endif
libraries/controllers/src/controllers/impl/filters/InvertFilter.cpp+9−0
@@ -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"
libraries/controllers/src/controllers/impl/filters/InvertFilter.h+29−0
@@ -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
15namespace controller {
16
17class InvertFilter : public ScaleFilter {
18 REGISTER_FILTER_CLASS(InvertFilter);
19public:
20 InvertFilter() : ScaleFilter(-1.0f) {}
21
22 virtual bool parseParameters(const QJsonArray& parameters) { return true; }
23
24private:
25};
26
27}
28
29#endif
libraries/controllers/src/controllers/impl/filters/PulseFilter.cpp+37−0
@@ -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
14using namespace controller;
15
16
17
18float 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
33bool PulseFilter::parseParameters(const QJsonValue& parameters) {
34 static const QString JSON_MIN = QStringLiteral("interval");
35 return parseSingleFloatParameter(parameters, JSON_MIN, _interval);
36}
37
libraries/controllers/src/controllers/impl/filters/PulseFilter.h+36−0
@@ -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
15namespace controller {
16
17
18class PulseFilter : public Filter {
19 REGISTER_FILTER_CLASS(PulseFilter);
20public:
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
29private:
30 mutable float _lastEmitTime { -::std::numeric_limits<float>::max() };
31 float _interval = 1.0f;
32};
33
34}
35
36#endif
libraries/controllers/src/controllers/impl/filters/ScaleFilter.cpp+19−0
@@ -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
14using namespace controller;
15
16bool ScaleFilter::parseParameters(const QJsonValue& parameters) {
17 static const QString JSON_SCALE = QStringLiteral("scale");
18 return parseSingleFloatParameter(parameters, JSON_SCALE, _scale);
19}
libraries/controllers/src/controllers/impl/filters/ScaleFilter.h+34−0
@@ -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
15namespace controller {
16
17class ScaleFilter : public Filter {
18 REGISTER_FILTER_CLASS(ScaleFilter);
19public:
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
28private:
29 float _scale = 1.0f;
30};
31
32}
33
34#endif
libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp+0−3
@@ -27,7 +27,4 @@ void NullDisplayPlugin::preRender() {} · 26 unmodified lines
27void NullDisplayPlugin::preDisplay() {}27void NullDisplayPlugin::preDisplay() {}
28void NullDisplayPlugin::display(GLuint sceneTexture, const glm::uvec2& sceneSize) {}28void NullDisplayPlugin::display(GLuint sceneTexture, const glm::uvec2& sceneSize) {}
29void NullDisplayPlugin::finishFrame() {}29void NullDisplayPlugin::finishFrame() {}
30
31void NullDisplayPlugin::activate() {}
32void NullDisplayPlugin::deactivate() {}
33void NullDisplayPlugin::stop() {}30void NullDisplayPlugin::stop() {}
libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h+0−2
@@ -15,8 +15,6 @@ public: · 14 unmodified lines
15 virtual ~NullDisplayPlugin() final {}15 virtual ~NullDisplayPlugin() final {}
16 virtual const QString & getName() const override;16 virtual const QString & getName() const override;
17 17
18 void activate() override;
19 void deactivate() override;
20 void stop() override;18 void stop() override;
21 19
22 virtual glm::uvec2 getRecommendedRenderSize() const override;20 virtual glm::uvec2 getRecommendedRenderSize() const override;
libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp+2−2
@@ -57,12 +57,12 @@ void OpenGLDisplayPlugin::customizeContext() { · 56 unmodified lines
57}57}
58 58
59void OpenGLDisplayPlugin::activate() {59void OpenGLDisplayPlugin::activate() {
60 _active = true;60 DisplayPlugin::activate();
61 _timer.start(1);61 _timer.start(1);
62}62}
63 63
64void OpenGLDisplayPlugin::stop() {64void OpenGLDisplayPlugin::stop() {
65 _active = false;65 DisplayPlugin::activate();
66 _timer.stop();66 _timer.stop();
67}67}
68 68
libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h+0−1
@@ -44,7 +44,6 @@ protected: · 43 unmodified lines
44 mutable QTimer _timer;44 mutable QTimer _timer;
45 ProgramPtr _program;45 ProgramPtr _program;
46 ShapeWrapperPtr _plane;46 ShapeWrapperPtr _plane;
47 bool _active{ false };
48 bool _vsyncSupported{ false };47 bool _vsyncSupported{ false };
49};48};
50 49
libraries/entities-renderer/src/EntityTreeRenderer.cpp+1−2
@@ -107,8 +107,7 @@ void EntityTreeRenderer::init() { · 106 unmodified lines
107 entityTree->setFBXService(this);107 entityTree->setFBXService(this);
108 108
109 if (_wantScripts) {109 if (_wantScripts) {
110 _entitiesScriptEngine = new ScriptEngine(NO_SCRIPT, "Entities",110 _entitiesScriptEngine = new ScriptEngine(NO_SCRIPT, "Entities");
111 _scriptingServices->getControllerScriptingInterface());
112 _scriptingServices->registerScriptEngineWithApplicationServices(_entitiesScriptEngine);111 _scriptingServices->registerScriptEngineWithApplicationServices(_entitiesScriptEngine);
113 _entitiesScriptEngine->runInThread();112 _entitiesScriptEngine->runInThread();
114 DependencyManager::get<EntityScriptingInterface>()->setEntitiesScriptEngine(_entitiesScriptEngine);113 DependencyManager::get<EntityScriptingInterface>()->setEntitiesScriptEngine(_entitiesScriptEngine);
libraries/entities/src/EntityPropertyFlags.h+1−1
@@ -189,7 +189,7 @@ enum EntityPropertyList { · 188 unmodified lines
189 PROP_BACKGROUND_MODE = PROP_MODEL_URL,189 PROP_BACKGROUND_MODE = PROP_MODEL_URL,
190 PROP_SKYBOX_COLOR = PROP_ANIMATION_URL,190 PROP_SKYBOX_COLOR = PROP_ANIMATION_URL,
191 PROP_SKYBOX_URL = PROP_ANIMATION_FPS,191 PROP_SKYBOX_URL = PROP_ANIMATION_FPS,
192 PROP_KEYLIGHT_AMBIENT_URL = PROP_ANIMATION_FRAME_INDEX,192 PROP_KEYLIGHT_AMBIENT_URL = PROP_ANIMATION_PLAYING,
193 193
194 // Aliases/Piggyback properties for Web. These properties intentionally reuse the enum values for194 // Aliases/Piggyback properties for Web. These properties intentionally reuse the enum values for
195 // other properties which will never overlap with each other. 195 // other properties which will never overlap with each other.
libraries/gl/src/gl/GlWindow.cpp+1−2
@@ -44,8 +44,7 @@ bool GlWindow::makeCurrent() { · 43 unmodified lines
44 qDebug() << "GL Renderer: " << QString((const char*) glGetString(GL_RENDERER));44 qDebug() << "GL Renderer: " << QString((const char*) glGetString(GL_RENDERER));
45 });45 });
46 46
47 QOpenGLContext * currentContext = QOpenGLContext::currentContext();47 Q_ASSERT(_context == QOpenGLContext::currentContext());
48 Q_ASSERT(_context == currentContext);
49 48
50 return makeCurrentResult;49 return makeCurrentResult;
51}50}
libraries/gl/src/gl/OffscreenQmlSurface.cpp+6−0
@@ -19,6 +19,7 @@ · 18 unmodified lines
19#include <QMutex>19#include <QMutex>
20 20
21#include <PerfStat.h>21#include <PerfStat.h>
22#include <DependencyManager.h>
22#include <NumericalConstants.h>23#include <NumericalConstants.h>
23 24
24#include "GLEscrow.h"25#include "GLEscrow.h"
@@ -611,3 +612,8 @@ QQuickWindow* OffscreenQmlSurface::getWindow() { · 586 unmodified lines
611QSize OffscreenQmlSurface::size() const {612QSize OffscreenQmlSurface::size() const {
612 return _renderer->_quickWindow->geometry().size();613 return _renderer->_quickWindow->geometry().size();
613}614}
615
616QQmlContext* OffscreenQmlSurface::getRootContext() {
617 return _qmlEngine->rootContext();
618}
619
libraries/gl/src/gl/OffscreenQmlSurface.h+1−0
@@ -60,6 +60,7 @@ public: · 59 unmodified lines
60 QQuickItem* getRootItem();60 QQuickItem* getRootItem();
61 QQuickWindow* getWindow();61 QQuickWindow* getWindow();
62 QObject* getEventHandler();62 QObject* getEventHandler();
63 QQmlContext* getRootContext();
63 64
64 QPointF mapToVirtualScreen(const QPointF& originalPoint, QObject* originalWidget);65 QPointF mapToVirtualScreen(const QPointF& originalPoint, QObject* originalWidget);
65 virtual bool eventFilter(QObject* originalDestination, QEvent* event);66 virtual bool eventFilter(QObject* originalDestination, QEvent* event);
libraries/input-plugins/CMakeLists.txt+3−53
@@ -1,6 +1,6 @@
1set(TARGET_NAME input-plugins)1set(TARGET_NAME input-plugins)
2setup_hifi_library()2setup_hifi_library()
3link_hifi_libraries(shared plugins gpu render-utils)3link_hifi_libraries(shared plugins controllers script-engine render-utils)
4 4
5GroupSources("src/input-plugins")5GroupSources("src/input-plugins")
6 6
@@ -11,55 +11,5 @@ if (WIN32) · 4 unmodified lines
11 target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES})11 target_link_libraries(${TARGET_NAME} ${OPENVR_LIBRARIES})
12endif()12endif()
13 13
14#add_dependency_external_projects(Sixense)14target_sdl2()
15#find_package(Sixense REQUIRED)15target_sixense()
16#target_include_directories(${TARGET_NAME} PRIVATE ${SIXENSE_INCLUDE_DIRS})
17#target_link_libraries(${TARGET_NAME} ${SIXENSE_LIBRARIES})
18
19# set a default root dir for each of our optional externals if it was not passed
20set(OPTIONAL_EXTERNALS "SDL2" "Sixense")
21foreach(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 ()
27endforeach()
28
29# perform standard include and linking for found externals
30foreach(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 ()
65endforeach()
libraries/input-plugins/src/input-plugins/InputDevice.cpp+0−59
@@ -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
13bool InputDevice::_lowVelocityFilter = false;
14
15const float DEFAULT_HAND_RETICLE_MOVE_SPEED = 37.5f;
16float InputDevice::reticleMoveSpeed = DEFAULT_HAND_RETICLE_MOVE_SPEED;
17
18//Constants for getCursorPixelRangeMultiplier()
19const float MIN_PIXEL_RANGE_MULT = 0.4f;
20const float MAX_PIXEL_RANGE_MULT = 2.0f;
21const 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
24float 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
29float 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
41float 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
51UserInputMapper::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}
libraries/input-plugins/src/input-plugins/Joystick.cpp+59−156
@@ -9,19 +9,17 @@ · 8 unmodified lines
9// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html9// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10//10//
11 11
12#include <limits>
13
14#include <glm/glm.hpp>
15
16#include "Joystick.h"12#include "Joystick.h"
17 13
14#include <PathUtils.h>
15
18const float CONTROLLER_THRESHOLD = 0.3f;16const float CONTROLLER_THRESHOLD = 0.3f;
19 17
20#ifdef HAVE_SDL218#ifdef HAVE_SDL2
21const float MAX_AXIS = 32768.0f;19const float MAX_AXIS = 32768.0f;
22 20
23Joystick::Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController) :21Joystick::Joystick(SDL_JoystickID instanceId, SDL_GameController* sdlGameController) :
24 InputDevice(name),22 InputDevice("GamePad"),
25 _sdlGameController(sdlGameController),23 _sdlGameController(sdlGameController),
26 _sdlJoystick(SDL_GameControllerGetJoystick(_sdlGameController)),24 _sdlJoystick(SDL_GameControllerGetJoystick(_sdlGameController)),
27 _instanceId(instanceId)25 _instanceId(instanceId)
@@ -55,39 +53,14 @@ void Joystick::focusOutEvent() { · 27 unmodified lines
55};53};
56 54
57#ifdef HAVE_SDL255#ifdef HAVE_SDL2
56
58void Joystick::handleAxisEvent(const SDL_ControllerAxisEvent& event) {57void Joystick::handleAxisEvent(const SDL_ControllerAxisEvent& event) {
59 SDL_GameControllerAxis axis = (SDL_GameControllerAxis) event.axis;58 SDL_GameControllerAxis axis = (SDL_GameControllerAxis) event.axis;
60 59 _axisStateMap[makeInput((controller::StandardAxisChannel)axis).getChannel()] = (float)event.value / MAX_AXIS;
61 switch (axis) {
62 case SDL_CONTROLLER_AXIS_LEFTX:
63 _axisStateMap[makeInput(LEFT_AXIS_X_POS).getChannel()] = (event.value > 0.0f) ? event.value / MAX_AXIS : 0.0f;
64 _axisStateMap[makeInput(LEFT_AXIS_X_NEG).getChannel()] = (event.value < 0.0f) ? -event.value / MAX_AXIS : 0.0f;
65 break;
66 case SDL_CONTROLLER_AXIS_LEFTY:
67 _axisStateMap[makeInput(LEFT_AXIS_Y_POS).getChannel()] = (event.value > 0.0f) ? event.value / MAX_AXIS : 0.0f;
68 _axisStateMap[makeInput(LEFT_AXIS_Y_NEG).getChannel()] = (event.value < 0.0f) ? -event.value / MAX_AXIS : 0.0f;
69 break;
70 case SDL_CONTROLLER_AXIS_RIGHTX:
71 _axisStateMap[makeInput(RIGHT_AXIS_X_POS).getChannel()] = (event.value > 0.0f) ? event.value / MAX_AXIS : 0.0f;
72 _axisStateMap[makeInput(RIGHT_AXIS_X_NEG).getChannel()] = (event.value < 0.0f) ? -event.value / MAX_AXIS : 0.0f;
73 break;
74 case SDL_CONTROLLER_AXIS_RIGHTY:
75 _axisStateMap[makeInput(RIGHT_AXIS_Y_POS).getChannel()] = (event.value > 0.0f) ? event.value / MAX_AXIS : 0.0f;
76 _axisStateMap[makeInput(RIGHT_AXIS_Y_NEG).getChannel()] = (event.value < 0.0f) ? -event.value / MAX_AXIS : 0.0f;
77 break;
78 case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
79 _axisStateMap[makeInput(RIGHT_SHOULDER).getChannel()] = event.value / MAX_AXIS;
80 break;
81 case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
82 _axisStateMap[makeInput(LEFT_SHOULDER).getChannel()] = event.value / MAX_AXIS;
83 break;
84 default:
85 break;
86 }
87}60}
88 61
89void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) {62void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) {
90 auto input = makeInput((SDL_GameControllerButton) event.button);63 auto input = makeInput((controller::StandardButtonChannel)event.button);
91 bool newValue = event.state == SDL_PRESSED;64 bool newValue = event.state == SDL_PRESSED;
92 if (newValue) {65 if (newValue) {
93 _buttonPressedMap.insert(input.getChannel());66 _buttonPressedMap.insert(input.getChannel());
@@ -95,131 +68,61 @@ void Joystick::handleButtonEvent(const SDL_ControllerButtonEvent& event) { · 1 unmodified line
95 _buttonPressedMap.erase(input.getChannel());68 _buttonPressedMap.erase(input.getChannel());
96 }69 }
97}70}
98#endif
99
100
101void 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#endif72#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
146void Joystick::assignDefaultInputMapping(UserInputMapper& mapper) {74controller::Input::NamedVector Joystick::getAvailableInputs() const {
147#ifdef HAVE_SDL275 using namespace controller;
148 const float JOYSTICK_MOVE_SPEED = 1.0f;76 static const Input::NamedVector availableInputs{
149 const float DPAD_MOVE_SPEED = 0.5f;77 makePair(A, "A"),
150 const float JOYSTICK_YAW_SPEED = 0.5f;78 makePair(B, "B"),
151 const float JOYSTICK_PITCH_SPEED = 0.25f;79 makePair(X, "X"),
152 const float BOOM_SPEED = 0.1f;80 makePair(Y, "Y"),
153 81 // DPad
154 // Y axes are flipped (up is negative)82 makePair(DU, "DU"),
155 // Left Joystick: Movement, strafing83 makePair(DD, "DD"),
156 mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(LEFT_AXIS_Y_NEG), JOYSTICK_MOVE_SPEED);84 makePair(DL, "DL"),
157 mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(LEFT_AXIS_Y_POS), JOYSTICK_MOVE_SPEED);85 makePair(DR, "DR"),
158 mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(LEFT_AXIS_X_POS), JOYSTICK_MOVE_SPEED);86 // Bumpers
159 mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(LEFT_AXIS_X_NEG), JOYSTICK_MOVE_SPEED);87 makePair(LB, "LB"),
160 88 makePair(RB, "RB"),
161 // Right Joystick: Camera orientation89 // Stick press
162 mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(RIGHT_AXIS_X_POS), JOYSTICK_YAW_SPEED);90 makePair(LS, "LS"),
163 mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(RIGHT_AXIS_X_NEG), JOYSTICK_YAW_SPEED);91 makePair(RS, "RS"),
164 mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(RIGHT_AXIS_Y_NEG), JOYSTICK_PITCH_SPEED);92 // Center buttons
165 mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(RIGHT_AXIS_Y_POS), JOYSTICK_PITCH_SPEED);93 makePair(START, "Start"),
166 94 makePair(BACK, "Back"),
167 // Dpad movement95 // Analog sticks
168 mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_UP), DPAD_MOVE_SPEED);96 makePair(LX, "LX"),
169 mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_DOWN), DPAD_MOVE_SPEED);97 makePair(LY, "LY"),
170 mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_RIGHT), DPAD_MOVE_SPEED);98 makePair(RX, "RX"),
171 mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_LEFT), DPAD_MOVE_SPEED);99 makePair(RY, "RY"),
172 100
173 // Button controls101 // Triggers
174 mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(SDL_CONTROLLER_BUTTON_Y), DPAD_MOVE_SPEED);102 makePair(LT, "LT"),
175 mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(SDL_CONTROLLER_BUTTON_X), DPAD_MOVE_SPEED);103 makePair(RT, "RT"),
176 104
177 // Zoom105 // Aliases, PlayStation style names
178 mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(RIGHT_SHOULDER), BOOM_SPEED);106 makePair(LB, "L1"),
179 mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(LEFT_SHOULDER), BOOM_SPEED);107 makePair(RB, "R1"),
180 108 makePair(LT, "L2"),
181 109 makePair(RT, "R2"),
182 // Hold front right shoulder button for precision controls110 makePair(LS, "L3"),
183 // Left Joystick: Movement, strafing111 makePair(RS, "R3"),
184 mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(LEFT_AXIS_Y_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f);112 makePair(BACK, "Select"),
185 mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(LEFT_AXIS_Y_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f);113 makePair(A, "Cross"),
186 mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(LEFT_AXIS_X_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f);114 makePair(B, "Circle"),
187 mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(LEFT_AXIS_X_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_MOVE_SPEED/2.0f);115 makePair(X, "Square"),
188 116 makePair(Y, "Triangle"),
189 // Right Joystick: Camera orientation117 makePair(DU, "Up"),
190 mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(RIGHT_AXIS_X_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f);118 makePair(DD, "Down"),
191 mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(RIGHT_AXIS_X_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_YAW_SPEED/2.0f);119 makePair(DL, "Left"),
192 mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(RIGHT_AXIS_Y_NEG), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_PITCH_SPEED/2.0f);120 makePair(DR, "Right"),
193 mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(RIGHT_AXIS_Y_POS), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), JOYSTICK_PITCH_SPEED/2.0f);121 };
194 122 return availableInputs;
195 // Dpad movement
196 mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_UP), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
197 mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(SDL_CONTROLLER_BUTTON_DPAD_DOWN), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
198 mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_RIGHT), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
199 mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(SDL_CONTROLLER_BUTTON_DPAD_LEFT), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
200
201 // Button controls
202 mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(SDL_CONTROLLER_BUTTON_Y), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
203 mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(SDL_CONTROLLER_BUTTON_X), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), DPAD_MOVE_SPEED/2.0f);
204
205 // Zoom
206 mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(RIGHT_SHOULDER), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), BOOM_SPEED/2.0f);
207 mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(LEFT_SHOULDER), makeInput(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), BOOM_SPEED/2.0f);
208
209 mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(SDL_CONTROLLER_BUTTON_LEFTSHOULDER));
210
211 mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(SDL_CONTROLLER_BUTTON_B));
212 mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(SDL_CONTROLLER_BUTTON_A));
213#endif
214}
215
216#ifdef HAVE_SDL2
217UserInputMapper::Input Joystick::makeInput(SDL_GameControllerButton button) {
218 return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON);
219}123}
220#endif
221 124
222UserInputMapper::Input Joystick::makeInput(Joystick::JoystickAxisChannel axis) {125QString Joystick::getDefaultMappingConfig() const {
223 return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS);126 static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/xbox.json";
127 return MAPPING_JSON;
224}128}
225
libraries/input-plugins/src/input-plugins/Joystick.h+8−23
@@ -20,9 +20,10 @@ · 19 unmodified lines
20#undef main20#undef main
21#endif21#endif
22 22
23#include "InputDevice.h"23#include <controllers/InputDevice.h>
24#include <controllers/StandardControls.h>
24 25
25class Joystick : public QObject, public InputDevice {26class Joystick : public QObject, public controller::InputDevice {
26 Q_OBJECT27 Q_OBJECT
27 Q_PROPERTY(QString name READ getName)28 Q_PROPERTY(QString name READ getName)
28 29
@@ -31,37 +32,21 @@ class Joystick : public QObject, public InputDevice { · 2 unmodified lines
31#endif32#endif
32 33
33public:34public:
34 enum JoystickAxisChannel {35 using Pointer = std::shared_ptr<Joystick>;
35 LEFT_AXIS_X_POS = 0,
36 LEFT_AXIS_X_NEG,
37 LEFT_AXIS_Y_POS,
38 LEFT_AXIS_Y_NEG,
39 RIGHT_AXIS_X_POS,
40 RIGHT_AXIS_X_NEG,
41 RIGHT_AXIS_Y_POS,
42 RIGHT_AXIS_Y_NEG,
43 RIGHT_SHOULDER,
44 LEFT_SHOULDER,
45 };
46 36
47 const QString& getName() const { return _name; }37 const QString& getName() const { return _name; }
48 38
49 // Device functions39 // Device functions
50 virtual void registerToUserInputMapper(UserInputMapper& mapper) override;40 virtual controller::Input::NamedVector getAvailableInputs() const override;
51 virtual void assignDefaultInputMapping(UserInputMapper& mapper) override;41 virtual QString getDefaultMappingConfig() const override;
52 virtual void update(float deltaTime, bool jointsCaptured) override;42 virtual void update(float deltaTime, bool jointsCaptured) override;
53 virtual void focusOutEvent() override;43 virtual void focusOutEvent() override;
54 44
55 Joystick() : InputDevice("Joystick") {}45 Joystick() : InputDevice("GamePad") {}
56 ~Joystick();46 ~Joystick();
57 47
58#ifdef HAVE_SDL248#ifdef HAVE_SDL2
59 UserInputMapper::Input makeInput(SDL_GameControllerButton button);49 Joystick(SDL_JoystickID instanceId, SDL_GameController* sdlGameController);
60#endif
61 UserInputMapper::Input makeInput(Joystick::JoystickAxisChannel axis);
62
63#ifdef HAVE_SDL2
64 Joystick(SDL_JoystickID instanceId, const QString& name, SDL_GameController* sdlGameController);
65#endif50#endif
66 51
67 void closeJoystick();52 void closeJoystick();
libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp+130−135
@@ -10,6 +10,13 @@ · 9 unmodified lines
10//10//
11#include "KeyboardMouseDevice.h"11#include "KeyboardMouseDevice.h"
12 12
13#include <QtGui/QKeyEvent>
14#include <QtGui/QMouseEvent>
15#include <QtGui/QTouchEvent>
16
17#include <controllers/UserInputMapper.h>
18#include <PathUtils.h>
19
13const QString KeyboardMouseDevice::NAME = "Keyboard/Mouse";20const QString KeyboardMouseDevice::NAME = "Keyboard/Mouse";
14 21
15void KeyboardMouseDevice::update(float deltaTime, bool jointsCaptured) {22void KeyboardMouseDevice::update(float deltaTime, bool jointsCaptured) {
@@ -81,7 +88,7 @@ void KeyboardMouseDevice::wheelEvent(QWheelEvent* event) { · 65 unmodified lines
81 _axisStateMap[makeInput(MOUSE_AXIS_WHEEL_Y_NEG).getChannel()] = (currentMove.y() < 0 ? -currentMove.y() : 0.0f);88 _axisStateMap[makeInput(MOUSE_AXIS_WHEEL_Y_NEG).getChannel()] = (currentMove.y() < 0 ? -currentMove.y() : 0.0f);
82}89}
83 90
84glm::vec2 KeyboardMouseDevice::evalAverageTouchPoints(const QList<QTouchEvent::TouchPoint>& points) const {91glm::vec2 evalAverageTouchPoints(const QList<QTouchEvent::TouchPoint>& points) {
85 glm::vec2 averagePoint(0.0f);92 glm::vec2 averagePoint(0.0f);
86 if (points.count() > 0) {93 if (points.count() > 0) {
87 for (auto& point : points) {94 for (auto& point : points) {
@@ -123,163 +130,151 @@ void KeyboardMouseDevice::touchUpdateEvent(const QTouchEvent* event) { · 35 unmodified lines
123 _lastTouch = currentPos;130 _lastTouch = currentPos;
124}131}
125 132
126UserInputMapper::Input KeyboardMouseDevice::makeInput(Qt::Key code) {133controller::Input KeyboardMouseDevice::makeInput(Qt::Key code) const {
127 auto shortCode = (UserInputMapper::uint16)(code & KEYBOARD_MASK);134 auto shortCode = (uint16_t)(code & KEYBOARD_MASK);
128 if (shortCode != code) {135 if (shortCode != code) {
129 shortCode |= 0x0800; // add this bit instead of the way Qt::Key add a bit on the 3rd byte for some keys136 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
134UserInputMapper::Input KeyboardMouseDevice::makeInput(Qt::MouseButton code) {141controller::Input KeyboardMouseDevice::makeInput(Qt::MouseButton code) const {
135 switch (code) {142 switch (code) {
136 case Qt::LeftButton:143 case Qt::LeftButton:
137 return UserInputMapper::Input(_deviceID, MOUSE_BUTTON_LEFT, UserInputMapper::ChannelType::BUTTON);144 return controller::Input(_deviceID, MOUSE_BUTTON_LEFT, controller::ChannelType::BUTTON);
138 case Qt::RightButton:145 case Qt::RightButton:
139 return UserInputMapper::Input(_deviceID, MOUSE_BUTTON_RIGHT, UserInputMapper::ChannelType::BUTTON);146 return controller::Input(_deviceID, MOUSE_BUTTON_RIGHT, controller::ChannelType::BUTTON);
140 case Qt::MiddleButton:147 case Qt::MiddleButton:
141 return UserInputMapper::Input(_deviceID, MOUSE_BUTTON_MIDDLE, UserInputMapper::ChannelType::BUTTON);148 return controller::Input(_deviceID, MOUSE_BUTTON_MIDDLE, controller::ChannelType::BUTTON);
142 default:149 default:
143 return UserInputMapper::Input();150 return controller::Input();
144 };151 };
145}152}
146 153
147UserInputMapper::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::MouseAxisChannel axis) {154controller::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::MouseAxisChannel axis) const {
148 return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS);155 return controller::Input(_deviceID, axis, controller::ChannelType::AXIS);
149}156}
150 157
151UserInputMapper::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchAxisChannel axis) {158controller::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchAxisChannel axis) const {
152 return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS);159 return controller::Input(_deviceID, axis, controller::ChannelType::AXIS);
153}160}
154 161
155UserInputMapper::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchButtonChannel button) {162controller::Input KeyboardMouseDevice::makeInput(KeyboardMouseDevice::TouchButtonChannel button) const {
156 return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON);163 return controller::Input(_deviceID, button, controller::ChannelType::BUTTON);
157}164}
158 165
159void KeyboardMouseDevice::registerToUserInputMapper(UserInputMapper& mapper) {166controller::Input::NamedVector KeyboardMouseDevice::getAvailableInputs() const {
160 // Grab the current free device ID167 using namespace controller;
161 _deviceID = mapper.getFreeDeviceID();168 static QVector<Input::NamedPair> availableInputs;
162 169 static std::once_flag once;
163 auto proxy = std::make_shared<UserInputMapper::DeviceProxy>(_name);170 std::call_once(once, [&] {
164 proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); };171 for (int i = (int)Qt::Key_0; i <= (int)Qt::Key_9; i++) {
165 proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); };172 availableInputs.append(Input::NamedPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString()));
166 proxy->getAvailabeInputs = [this] () -> QVector<UserInputMapper::InputPair> {
167 QVector<UserInputMapper::InputPair> availableInputs;
168 for (int i = (int) Qt::Key_0; i <= (int) Qt::Key_9; i++) {
169 availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString()));
170 }173 }
171 for (int i = (int) Qt::Key_A; i <= (int) Qt::Key_Z; i++) {174 for (int i = (int)Qt::Key_A; i <= (int)Qt::Key_Z; i++) {
172 availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString()));175 availableInputs.append(Input::NamedPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString()));
173 }176 }
174 for (int i = (int) Qt::Key_Left; i <= (int) Qt::Key_Down; i++) {177 for (int i = (int)Qt::Key_Left; i <= (int)Qt::Key_Down; i++) {
175 availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString()));178 availableInputs.append(Input::NamedPair(makeInput(Qt::Key(i)), QKeySequence(Qt::Key(i)).toString()));
176 }179 }
177 availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_Space), QKeySequence(Qt::Key_Space).toString()));180 availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Space), QKeySequence(Qt::Key_Space).toString()));
178 availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_Shift), "Shift"));181 availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Shift), "Shift"));
179 availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_PageUp), QKeySequence(Qt::Key_PageUp).toString()));182 availableInputs.append(Input::NamedPair(makeInput(Qt::Key_PageUp), QKeySequence(Qt::Key_PageUp).toString()));
180 availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::Key_PageDown), QKeySequence(Qt::Key_PageDown).toString()));183 availableInputs.append(Input::NamedPair(makeInput(Qt::Key_PageDown), QKeySequence(Qt::Key_PageDown).toString()));
181 184
182 availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::LeftButton), "Left Mouse Click"));185 availableInputs.append(Input::NamedPair(makeInput(Qt::LeftButton), "LeftMouseClick"));
183 availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::MiddleButton), "Middle Mouse Click"));186 availableInputs.append(Input::NamedPair(makeInput(Qt::MiddleButton), "MiddleMouseClick"));
184 availableInputs.append(UserInputMapper::InputPair(makeInput(Qt::RightButton), "Right Mouse Click"));187 availableInputs.append(Input::NamedPair(makeInput(Qt::RightButton), "RightMouseClick"));
185 188
186 availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_X_POS), "Mouse Move Right"));189 availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_X_POS), "MouseMoveRight"));
187 availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_X_NEG), "Mouse Move Left"));190 availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_X_NEG), "MouseMoveLeft"));
188 availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_Y_POS), "Mouse Move Up"));191 availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_Y_POS), "MouseMoveUp"));
189 availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_Y_NEG), "Mouse Move Down"));192 availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_Y_NEG), "MouseMoveDown"));
190 193
191 availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_WHEEL_Y_POS), "Mouse Wheel Right"));194 availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_Y_POS), "MouseWheelRight"));
192 availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_WHEEL_Y_NEG), "Mouse Wheel Left"));195 availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_Y_NEG), "MouseWheelLeft"));
193 availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_WHEEL_X_POS), "Mouse Wheel Up"));196 availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_X_POS), "MouseWheelUp"));
194 availableInputs.append(UserInputMapper::InputPair(makeInput(MOUSE_AXIS_WHEEL_X_NEG), "Mouse Wheel Down"));197 availableInputs.append(Input::NamedPair(makeInput(MOUSE_AXIS_WHEEL_X_NEG), "MouseWheelDown"));
195 198
196 availableInputs.append(UserInputMapper::InputPair(makeInput(TOUCH_AXIS_X_POS), "Touchpad Right"));199 availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_X_POS), "TouchpadRight"));
197 availableInputs.append(UserInputMapper::InputPair(makeInput(TOUCH_AXIS_X_NEG), "Touchpad Left"));200 availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_X_NEG), "TouchpadLeft"));
198 availableInputs.append(UserInputMapper::InputPair(makeInput(TOUCH_AXIS_Y_POS), "Touchpad Up"));201 availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_Y_POS), "TouchpadUp"));
199 availableInputs.append(UserInputMapper::InputPair(makeInput(TOUCH_AXIS_Y_NEG), "Touchpad Down"));202 availableInputs.append(Input::NamedPair(makeInput(TOUCH_AXIS_Y_NEG), "TouchpadDown"));
200 203 });
201 return availableInputs;204 return availableInputs;
202 };
203 proxy->resetDeviceBindings = [this, &mapper] () -> bool {
204 mapper.removeAllInputChannelsForDevice(_deviceID);
205 this->assignDefaultInputMapping(mapper);
206 return true;
207 };
208 mapper.registerDevice(_deviceID, proxy);
209}205}
210 206
211void KeyboardMouseDevice::assignDefaultInputMapping(UserInputMapper& mapper) {207QString KeyboardMouseDevice::getDefaultMappingConfig() const {
212 const float BUTTON_MOVE_SPEED = 1.0f;208 static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/keyboardMouse.json";
213 const float BUTTON_YAW_SPEED = 0.75f;209 return MAPPING_JSON;
214 const float BUTTON_PITCH_SPEED = 0.5f;
215 const float MOUSE_YAW_SPEED = 0.5f;
216 const float MOUSE_PITCH_SPEED = 0.25f;
217 const float TOUCH_YAW_SPEED = 0.5f;
218 const float TOUCH_PITCH_SPEED = 0.25f;
219 const float BUTTON_BOOM_SPEED = 0.1f;
220
221 // AWSD keys mapping
222 mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(Qt::Key_S), BUTTON_MOVE_SPEED);
223 mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(Qt::Key_W), BUTTON_MOVE_SPEED);
224 mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(Qt::Key_A), BUTTON_MOVE_SPEED);
225 mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(Qt::Key_D), BUTTON_MOVE_SPEED);
226 mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(Qt::Key_C), BUTTON_MOVE_SPEED);
227 mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(Qt::Key_E), BUTTON_MOVE_SPEED);
228
229 mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(Qt::Key_E), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED);
230 mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(Qt::Key_C), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED);
231 mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(Qt::Key_A), makeInput(Qt::RightButton), BUTTON_YAW_SPEED);
232 mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(Qt::Key_D), makeInput(Qt::RightButton), BUTTON_YAW_SPEED);
233 mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(Qt::Key_A), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED);
234 mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(Qt::Key_D), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED);
235 mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(Qt::Key_S), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED);
236 mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(Qt::Key_W), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED);
237
238 // Arrow keys mapping
239 mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(Qt::Key_Down), BUTTON_MOVE_SPEED);
240 mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(Qt::Key_Up), BUTTON_MOVE_SPEED);
241 mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(Qt::Key_Left), BUTTON_MOVE_SPEED);
242 mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(Qt::Key_Right), BUTTON_MOVE_SPEED);
243 mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(Qt::Key_PageDown), BUTTON_MOVE_SPEED);
244 mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(Qt::Key_PageUp), BUTTON_MOVE_SPEED);
245
246 mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(Qt::Key_Left), makeInput(Qt::RightButton), BUTTON_YAW_SPEED);
247 mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(Qt::Key_Right), makeInput(Qt::RightButton), BUTTON_YAW_SPEED);
248 mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(Qt::Key_Left), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED);
249 mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(Qt::Key_Right), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED);
250 mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(Qt::Key_Down), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED);
251 mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(Qt::Key_Up), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED);
252
253 // Mouse move
254 mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(MOUSE_AXIS_Y_NEG), makeInput(Qt::RightButton), MOUSE_PITCH_SPEED);
255 mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(MOUSE_AXIS_Y_POS), makeInput(Qt::RightButton), MOUSE_PITCH_SPEED);
256 mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(MOUSE_AXIS_X_NEG), makeInput(Qt::RightButton), MOUSE_YAW_SPEED);
257 mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(MOUSE_AXIS_X_POS), makeInput(Qt::RightButton), MOUSE_YAW_SPEED);
258
259
260#ifdef Q_OS_MAC
261 // wheel event modifier on Mac collide with the touchpad scroll event
262 mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(TOUCH_AXIS_Y_NEG), TOUCH_PITCH_SPEED);
263 mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(TOUCH_AXIS_Y_POS), TOUCH_PITCH_SPEED);
264 mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(TOUCH_AXIS_X_NEG), TOUCH_YAW_SPEED);
265 mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(TOUCH_AXIS_X_POS), TOUCH_YAW_SPEED);
266#else
267 // Touch pad yaw pitch
268 mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(TOUCH_AXIS_Y_NEG), TOUCH_PITCH_SPEED);
269 mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(TOUCH_AXIS_Y_POS), TOUCH_PITCH_SPEED);
270 mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(TOUCH_AXIS_X_NEG), TOUCH_YAW_SPEED);
271 mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(TOUCH_AXIS_X_POS), TOUCH_YAW_SPEED);
272
273 // Wheel move
274 mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(MOUSE_AXIS_WHEEL_Y_POS), BUTTON_BOOM_SPEED);
275 mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(MOUSE_AXIS_WHEEL_Y_NEG), BUTTON_BOOM_SPEED);
276 mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(MOUSE_AXIS_WHEEL_X_NEG), BUTTON_YAW_SPEED);
277 mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(MOUSE_AXIS_WHEEL_X_POS), BUTTON_YAW_SPEED);
278
279#endif
280
281 mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(Qt::Key_Space));
282 mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(Qt::Key_R));
283 mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(Qt::Key_T));
284}210}
285 211
212//void KeyboardMouseDevice::assignDefaultInputMapping(UserInputMapper& mapper) {
213// const float BUTTON_MOVE_SPEED = 1.0f;
214// const float BUTTON_YAW_SPEED = 0.75f;
215// const float BUTTON_PITCH_SPEED = 0.5f;
216// const float MOUSE_YAW_SPEED = 0.5f;
217// const float MOUSE_PITCH_SPEED = 0.25f;
218// const float TOUCH_YAW_SPEED = 0.5f;
219// const float TOUCH_PITCH_SPEED = 0.25f;
220// const float BUTTON_BOOM_SPEED = 0.1f;
221//
222// // AWSD keys mapping
223//
224// mapper.addInputChannel(controller::BOOM_IN, makeInput(Qt::Key_E), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED);
225// mapper.addInputChannel(controller::BOOM_OUT, makeInput(Qt::Key_C), makeInput(Qt::Key_Shift), BUTTON_BOOM_SPEED);
226// mapper.addInputChannel(controller::LATERAL_LEFT, makeInput(Qt::Key_A), makeInput(Qt::RightButton), BUTTON_YAW_SPEED);
227// mapper.addInputChannel(controller::LATERAL_RIGHT, makeInput(Qt::Key_D), makeInput(Qt::RightButton), BUTTON_YAW_SPEED);
228// mapper.addInputChannel(controller::LATERAL_LEFT, makeInput(Qt::Key_A), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED);
229// mapper.addInputChannel(controller::LATERAL_RIGHT, makeInput(Qt::Key_D), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED);
230// mapper.addInputChannel(controller::PITCH_DOWN, makeInput(Qt::Key_S), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED);
231// mapper.addInputChannel(controller::PITCH_UP, makeInput(Qt::Key_W), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED);
232//
233// // Arrow keys mapping
234// mapper.addInputChannel(controller::LONGITUDINAL_BACKWARD, makeInput(Qt::Key_Down), BUTTON_MOVE_SPEED);
235// mapper.addInputChannel(controller::LONGITUDINAL_FORWARD, makeInput(Qt::Key_Up), BUTTON_MOVE_SPEED);
236// mapper.addInputChannel(controller::YAW_LEFT, makeInput(Qt::Key_Left), BUTTON_MOVE_SPEED);
237// mapper.addInputChannel(controller::YAW_RIGHT, makeInput(Qt::Key_Right), BUTTON_MOVE_SPEED);
238// mapper.addInputChannel(controller::VERTICAL_DOWN, makeInput(Qt::Key_PageDown), BUTTON_MOVE_SPEED);
239// mapper.addInputChannel(controller::VERTICAL_UP, makeInput(Qt::Key_PageUp), BUTTON_MOVE_SPEED);
240//
241// mapper.addInputChannel(controller::LATERAL_LEFT, makeInput(Qt::Key_Left), makeInput(Qt::RightButton), BUTTON_YAW_SPEED);
242// mapper.addInputChannel(controller::LATERAL_RIGHT, makeInput(Qt::Key_Right), makeInput(Qt::RightButton), BUTTON_YAW_SPEED);
243// mapper.addInputChannel(controller::LATERAL_LEFT, makeInput(Qt::Key_Left), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED);
244// mapper.addInputChannel(controller::LATERAL_RIGHT, makeInput(Qt::Key_Right), makeInput(Qt::Key_Shift), BUTTON_YAW_SPEED);
245// mapper.addInputChannel(controller::PITCH_DOWN, makeInput(Qt::Key_Down), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED);
246// mapper.addInputChannel(controller::PITCH_UP, makeInput(Qt::Key_Up), makeInput(Qt::Key_Shift), BUTTON_PITCH_SPEED);
247//
248// // Mouse move
249// mapper.addInputChannel(controller::PITCH_DOWN, makeInput(MOUSE_AXIS_Y_NEG), makeInput(Qt::RightButton), MOUSE_PITCH_SPEED);
250// mapper.addInputChannel(controller::PITCH_UP, makeInput(MOUSE_AXIS_Y_POS), makeInput(Qt::RightButton), MOUSE_PITCH_SPEED);
251// mapper.addInputChannel(controller::YAW_LEFT, makeInput(MOUSE_AXIS_X_NEG), makeInput(Qt::RightButton), MOUSE_YAW_SPEED);
252// mapper.addInputChannel(controller::YAW_RIGHT, makeInput(MOUSE_AXIS_X_POS), makeInput(Qt::RightButton), MOUSE_YAW_SPEED);
253//
254//
255//#ifdef Q_OS_MAC
256// // wheel event modifier on Mac collide with the touchpad scroll event
257// mapper.addInputChannel(controller::PITCH_DOWN, makeInput(TOUCH_AXIS_Y_NEG), TOUCH_PITCH_SPEED);
258// mapper.addInputChannel(controller::PITCH_UP, makeInput(TOUCH_AXIS_Y_POS), TOUCH_PITCH_SPEED);
259// mapper.addInputChannel(controller::YAW_LEFT, makeInput(TOUCH_AXIS_X_NEG), TOUCH_YAW_SPEED);
260// mapper.addInputChannel(controller::YAW_RIGHT, makeInput(TOUCH_AXIS_X_POS), TOUCH_YAW_SPEED);
261//#else
262// // Touch pad yaw pitch
263// mapper.addInputChannel(controller::PITCH_DOWN, makeInput(TOUCH_AXIS_Y_NEG), TOUCH_PITCH_SPEED);
264// mapper.addInputChannel(controller::PITCH_UP, makeInput(TOUCH_AXIS_Y_POS), TOUCH_PITCH_SPEED);
265// mapper.addInputChannel(controller::YAW_LEFT, makeInput(TOUCH_AXIS_X_NEG), TOUCH_YAW_SPEED);
266// mapper.addInputChannel(controller::YAW_RIGHT, makeInput(TOUCH_AXIS_X_POS), TOUCH_YAW_SPEED);
267//
268// // Wheel move
269// mapper.addInputChannel(controller::BOOM_IN, makeInput(MOUSE_AXIS_WHEEL_Y_POS), BUTTON_BOOM_SPEED);
270// mapper.addInputChannel(controller::BOOM_OUT, makeInput(MOUSE_AXIS_WHEEL_Y_NEG), BUTTON_BOOM_SPEED);
271// mapper.addInputChannel(controller::LATERAL_LEFT, makeInput(MOUSE_AXIS_WHEEL_X_NEG), BUTTON_YAW_SPEED);
272// mapper.addInputChannel(controller::LATERAL_RIGHT, makeInput(MOUSE_AXIS_WHEEL_X_POS), BUTTON_YAW_SPEED);
273//
274//#endif
275//
276// mapper.addInputChannel(controller::SHIFT, makeInput(Qt::Key_Space));
277// mapper.addInputChannel(controller::ACTION1, makeInput(Qt::Key_R));
278// mapper.addInputChannel(controller::ACTION2, makeInput(Qt::Key_T));
279//}
280
libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h+17−14
@@ -12,12 +12,19 @@ · 11 unmodified lines
12#ifndef hifi_KeyboardMouseDevice_h12#ifndef hifi_KeyboardMouseDevice_h
13#define hifi_KeyboardMouseDevice_h13#define hifi_KeyboardMouseDevice_h
14 14
15#include <QTouchEvent>
16#include <chrono>15#include <chrono>
17#include "InputDevice.h"16
17#include <QtCore/QPoint>
18
19#include <controllers/InputDevice.h>
18#include "InputPlugin.h"20#include "InputPlugin.h"
19 21
20class KeyboardMouseDevice : public InputPlugin, public InputDevice {22class QTouchEvent;
23class QKeyEvent;
24class QMouseEvent;
25class QWheelEvent;
26
27class KeyboardMouseDevice : public InputPlugin, public controller::InputDevice {
21 Q_OBJECT28 Q_OBJECT
22public:29public:
23 enum KeyboardChannel {30 enum KeyboardChannel {
@@ -61,15 +68,12 @@ public: · 37 unmodified lines
61 virtual bool isJointController() const override { return false; }68 virtual bool isJointController() const override { return false; }
62 const QString& getName() const override { return NAME; }69 const QString& getName() const override { return NAME; }
63 70
64 virtual void activate() override {};
65 virtual void deactivate() override {};
66
67 virtual void pluginFocusOutEvent() override { focusOutEvent(); }71 virtual void pluginFocusOutEvent() override { focusOutEvent(); }
68 virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); }72 virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); }
69 73
70 // Device functions74 // Device functions
71 virtual void registerToUserInputMapper(UserInputMapper& mapper) override;75 virtual controller::Input::NamedVector getAvailableInputs() const override;
72 virtual void assignDefaultInputMapping(UserInputMapper& mapper) override;76 virtual QString getDefaultMappingConfig() const override;
73 virtual void update(float deltaTime, bool jointsCaptured) override;77 virtual void update(float deltaTime, bool jointsCaptured) override;
74 virtual void focusOutEvent() override;78 virtual void focusOutEvent() override;
75 79
@@ -87,11 +91,11 @@ public: · 11 unmodified lines
87 void wheelEvent(QWheelEvent* event);91 void wheelEvent(QWheelEvent* event);
88 92
89 // Let's make it easy for Qt because we assume we love Qt forever93 // Let's make it easy for Qt because we assume we love Qt forever
90 UserInputMapper::Input makeInput(Qt::Key code);94 controller::Input makeInput(Qt::Key code) const;
91 UserInputMapper::Input makeInput(Qt::MouseButton code);95 controller::Input makeInput(Qt::MouseButton code) const;
92 UserInputMapper::Input makeInput(KeyboardMouseDevice::MouseAxisChannel axis);96 controller::Input makeInput(MouseAxisChannel axis) const;
93 UserInputMapper::Input makeInput(KeyboardMouseDevice::TouchAxisChannel axis);97 controller::Input makeInput(TouchAxisChannel axis) const;
94 UserInputMapper::Input makeInput(KeyboardMouseDevice::TouchButtonChannel button);98 controller::Input makeInput(TouchButtonChannel button) const;
95 99
96 static const QString NAME;100 static const QString NAME;
97 101
@@ -100,7 +104,6 @@ protected: · 2 unmodified lines
100 glm::vec2 _lastTouch;104 glm::vec2 _lastTouch;
101 bool _isTouching = false;105 bool _isTouching = false;
102 106
103 glm::vec2 evalAverageTouchPoints(const QList<QTouchEvent::TouchPoint>& points) const;
104 std::chrono::high_resolution_clock _clock;107 std::chrono::high_resolution_clock _clock;
105 std::chrono::high_resolution_clock::time_point _lastTouchTime;108 std::chrono::high_resolution_clock::time_point _lastTouchTime;
106};109};
libraries/input-plugins/src/input-plugins/SDL2Manager.cpp+41−18
@@ -14,6 +14,7 @@ · 13 unmodified lines
14#include <HFActionEvent.h>14#include <HFActionEvent.h>
15#include <HFBackEvent.h>15#include <HFBackEvent.h>
16#include <PerfStat.h>16#include <PerfStat.h>
17#include <controllers/UserInputMapper.h>
17 18
18#include "SDL2Manager.h"19#include "SDL2Manager.h"
19 20
@@ -47,12 +48,12 @@ void SDL2Manager::init() { · 27 unmodified lines
47 if (controller) {48 if (controller) {
48 SDL_JoystickID id = getInstanceId(controller);49 SDL_JoystickID id = getInstanceId(controller);
49 if (!_openJoysticks.contains(id)) {50 if (!_openJoysticks.contains(id)) {
50 Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller);51 //Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller);
52 Joystick::Pointer joystick = std::make_shared<Joystick>(id, controller);
51 _openJoysticks[id] = joystick;53 _openJoysticks[id] = joystick;
52 auto userInputMapper = DependencyManager::get<UserInputMapper>();54 auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
53 joystick->registerToUserInputMapper(*userInputMapper);55 userInputMapper->registerDevice(joystick);
54 joystick->assignDefaultInputMapping(*userInputMapper);56 emit joystickAdded(joystick.get());
55 emit joystickAdded(joystick);
56 }57 }
57 }58 }
58 }59 }
@@ -67,12 +68,33 @@ void SDL2Manager::init() { · 8 unmodified lines
67 68
68void SDL2Manager::deinit() {69void SDL2Manager::deinit() {
69#ifdef HAVE_SDL270#ifdef HAVE_SDL2
70 qDeleteAll(_openJoysticks);71 _openJoysticks.clear();
71 72
72 SDL_Quit();73 SDL_Quit();
73#endif74#endif
74}75}
75 76
77void 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
87void 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
76bool SDL2Manager::isSupported() const {98bool SDL2Manager::isSupported() const {
77#ifdef HAVE_SDL299#ifdef HAVE_SDL2
78 return true;100 return true;
@@ -92,7 +114,7 @@ void SDL2Manager::pluginFocusOutEvent() { · 13 unmodified lines
92void SDL2Manager::pluginUpdate(float deltaTime, bool jointsCaptured) {114void SDL2Manager::pluginUpdate(float deltaTime, bool jointsCaptured) {
93#ifdef HAVE_SDL2115#ifdef HAVE_SDL2
94 if (_isInitialized) {116 if (_isInitialized) {
95 auto userInputMapper = DependencyManager::get<UserInputMapper>();117 auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
96 for (auto joystick : _openJoysticks) {118 for (auto joystick : _openJoysticks) {
97 joystick->update(deltaTime, jointsCaptured);119 joystick->update(deltaTime, jointsCaptured);
98 }120 }
@@ -102,12 +124,12 @@ void SDL2Manager::pluginUpdate(float deltaTime, bool jointsCaptured) { · 3 unmodified lines
102 SDL_Event event;124 SDL_Event event;
103 while (SDL_PollEvent(&event)) {125 while (SDL_PollEvent(&event)) {
104 if (event.type == SDL_CONTROLLERAXISMOTION) {126 if (event.type == SDL_CONTROLLERAXISMOTION) {
105 Joystick* joystick = _openJoysticks[event.caxis.which];127 Joystick::Pointer joystick = _openJoysticks[event.caxis.which];
106 if (joystick) {128 if (joystick) {
107 joystick->handleAxisEvent(event.caxis);129 joystick->handleAxisEvent(event.caxis);
108 }130 }
109 } else if (event.type == SDL_CONTROLLERBUTTONDOWN || event.type == SDL_CONTROLLERBUTTONUP) {131 } else if (event.type == SDL_CONTROLLERBUTTONDOWN || event.type == SDL_CONTROLLERBUTTONUP) {
110 Joystick* joystick = _openJoysticks[event.cbutton.which];132 Joystick::Pointer joystick = _openJoysticks[event.cbutton.which];
111 if (joystick) {133 if (joystick) {
112 joystick->handleButtonEvent(event.cbutton);134 joystick->handleButtonEvent(event.cbutton);
113 }135 }
@@ -124,20 +146,21 @@ void SDL2Manager::pluginUpdate(float deltaTime, bool jointsCaptured) { · 10 unmodified lines
124 146
125 } else if (event.type == SDL_CONTROLLERDEVICEADDED) {147 } else if (event.type == SDL_CONTROLLERDEVICEADDED) {
126 SDL_GameController* controller = SDL_GameControllerOpen(event.cdevice.which);148 SDL_GameController* controller = SDL_GameControllerOpen(event.cdevice.which);
127
128 SDL_JoystickID id = getInstanceId(controller);149 SDL_JoystickID id = getInstanceId(controller);
129 if (!_openJoysticks.contains(id)) {150 if (!_openJoysticks.contains(id)) {
130 Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller);151 // Joystick* joystick = new Joystick(id, SDL_GameControllerName(controller), controller);
152 Joystick::Pointer joystick = std::make_shared<Joystick>(id, controller);
131 _openJoysticks[id] = joystick;153 _openJoysticks[id] = joystick;
132 joystick->registerToUserInputMapper(*userInputMapper);154 userInputMapper->registerDevice(joystick);
133 joystick->assignDefaultInputMapping(*userInputMapper);155 emit joystickAdded(joystick.get());
134 emit joystickAdded(joystick);
135 }156 }
136 } else if (event.type == SDL_CONTROLLERDEVICEREMOVED) {157 } else if (event.type == SDL_CONTROLLERDEVICEREMOVED) {
137 Joystick* joystick = _openJoysticks[event.cdevice.which];158 if (_openJoysticks.contains(event.cdevice.which)) {
138 _openJoysticks.remove(event.cdevice.which);159 Joystick::Pointer joystick = _openJoysticks[event.cdevice.which];
139 userInputMapper->removeDevice(joystick->getDeviceID());160 _openJoysticks.remove(event.cdevice.which);
140 emit joystickRemoved(joystick);161 userInputMapper->removeDevice(joystick->getDeviceID());
162 emit joystickRemoved(joystick.get());
163 }
141 }164 }
142 }165 }
143 }166 }
libraries/input-plugins/src/input-plugins/SDL2Manager.h+9−6
@@ -16,9 +16,9 @@ · 15 unmodified lines
16#include <SDL.h>16#include <SDL.h>
17#endif17#endif
18 18
19#include "InputPlugin.h"19#include <controllers/UserInputMapper.h>
20#include "UserInputMapper.h"
21 20
21#include "InputPlugin.h"
22#include "Joystick.h"22#include "Joystick.h"
23 23
24class SDL2Manager : public InputPlugin {24class SDL2Manager : public InputPlugin {
@@ -34,9 +34,12 @@ public: · 9 unmodified lines
34 34
35 virtual void init() override;35 virtual void init() override;
36 virtual void deinit() override;36 virtual void deinit() override;
37 virtual void activate() override {};37
38 virtual void deactivate() override {};38 /// Called when a plugin is being activated for use. May be called multiple times.
39 39 virtual void activate() override;
40 /// Called when a plugin is no longer being used. May be called multiple times.
41 virtual void deactivate() override;
42
40 virtual void pluginFocusOutEvent() override;43 virtual void pluginFocusOutEvent() override;
41 virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override;44 virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override;
42 45
@@ -78,7 +81,7 @@ private: · 35 unmodified lines
78 int buttonPressed() const { return SDL_PRESSED; }81 int buttonPressed() const { return SDL_PRESSED; }
79 int buttonRelease() const { return SDL_RELEASED; }82 int buttonRelease() const { return SDL_RELEASED; }
80 83
81 QMap<SDL_JoystickID, Joystick*> _openJoysticks;84 QMap<SDL_JoystickID, Joystick::Pointer> _openJoysticks;
82#endif85#endif
83 bool _isInitialized;86 bool _isInitialized;
84 static const QString NAME;87 static const QString NAME;
libraries/input-plugins/src/input-plugins/SixenseManager.cpp+182−155
@@ -18,10 +18,13 @@ · 17 unmodified lines
18#include <PerfStat.h>18#include <PerfStat.h>
19#include <SettingHandle.h>19#include <SettingHandle.h>
20#include <plugins/PluginContainer.h>20#include <plugins/PluginContainer.h>
21#include <PathUtils.h>
22#include <NumericalConstants.h>
23#include <UserActivityLogger.h>
24#include <controllers/UserInputMapper.h>
21 25
22#include "NumericalConstants.h"
23#include "SixenseManager.h"26#include "SixenseManager.h"
24#include "UserActivityLogger.h"27
25 28
26#ifdef HAVE_SIXENSE29#ifdef HAVE_SIXENSE
27 #include "sixense.h"30 #include "sixense.h"
@@ -32,10 +35,6 @@ · 4 unmodified lines
32Q_DECLARE_LOGGING_CATEGORY(inputplugins)35Q_DECLARE_LOGGING_CATEGORY(inputplugins)
33Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins")36Q_LOGGING_CATEGORY(inputplugins, "hifi.inputplugins")
34 37
35// These bits aren't used for buttons, so they can be used as masks:
36const unsigned int LEFT_MASK = 0;
37const unsigned int RIGHT_MASK = 1U << 1;
38
39#ifdef HAVE_SIXENSE38#ifdef HAVE_SIXENSE
40 39
41const int CALIBRATION_STATE_IDLE = 0;40const int CALIBRATION_STATE_IDLE = 0;
@@ -65,15 +64,12 @@ const QString MENU_PATH = MENU_PARENT + ">" + MENU_NAME; · 23 unmodified lines
65const QString TOGGLE_SMOOTH = "Smooth Sixense Movement";64const QString TOGGLE_SMOOTH = "Smooth Sixense Movement";
66const float DEFAULT_REACH_LENGTH = 1.5f;65const float DEFAULT_REACH_LENGTH = 1.5f;
67 66
68 67static std::shared_ptr<SixenseManager> instance;
69SixenseManager::SixenseManager() :68SixenseManager::SixenseManager() :
70 InputDevice("Hydra"),69 InputDevice("Hydra"),
71 _reachLength(DEFAULT_REACH_LENGTH),70 _reachLength(DEFAULT_REACH_LENGTH)
72#ifdef __APPLE__
73 _sixenseLibrary(nullptr),
74#endif
75 _hydrasConnected(false)
76{71{
72 instance = std::shared_ptr<SixenseManager>(this);
77}73}
78 74
79bool SixenseManager::isSupported() const {75bool SixenseManager::isSupported() const {
@@ -85,6 +81,7 @@ bool SixenseManager::isSupported() const { · 5 unmodified lines
85}81}
86 82
87void SixenseManager::activate() {83void SixenseManager::activate() {
84 InputPlugin::activate();
88#ifdef HAVE_SIXENSE85#ifdef HAVE_SIXENSE
89 _calibrationState = CALIBRATION_STATE_IDLE;86 _calibrationState = CALIBRATION_STATE_IDLE;
90 _avatarPosition = DEFAULT_AVATAR_POSITION;87 _avatarPosition = DEFAULT_AVATAR_POSITION;
@@ -94,6 +91,9 @@ void SixenseManager::activate() { · 3 unmodified lines
94 [this] (bool clicked) { this->setSixenseFilter(clicked); },91 [this] (bool clicked) { this->setSixenseFilter(clicked); },
95 true, true);92 true, true);
96 93
94 auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
95 userInputMapper->registerDevice(instance);
96
97#ifdef __APPLE__97#ifdef __APPLE__
98 98
99 if (!_sixenseLibrary) {99 if (!_sixenseLibrary) {
@@ -125,11 +125,19 @@ void SixenseManager::activate() { · 25 unmodified lines
125}125}
126 126
127void SixenseManager::deactivate() {127void SixenseManager::deactivate() {
128 InputPlugin::deactivate();
129
128#ifdef HAVE_SIXENSE130#ifdef HAVE_SIXENSE
129 CONTAINER->removeMenuItem(MENU_NAME, TOGGLE_SMOOTH);131 CONTAINER->removeMenuItem(MENU_NAME, TOGGLE_SMOOTH);
130 CONTAINER->removeMenu(MENU_PATH);132 CONTAINER->removeMenu(MENU_PATH);
131 133
132 _poseStateMap.clear();134 _poseStateMap.clear();
135 _collectedSamples.clear();
136
137 if (_deviceID != controller::Input::INVALID_DEVICE) {
138 auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
139 userInputMapper->removeDevice(_deviceID);
140 }
133 141
134#ifdef __APPLE__142#ifdef __APPLE__
135 SixenseBaseFunction sixenseExit = (SixenseBaseFunction)_sixenseLibrary->resolve("sixenseExit");143 SixenseBaseFunction sixenseExit = (SixenseBaseFunction)_sixenseLibrary->resolve("sixenseExit");
@@ -155,6 +163,12 @@ void SixenseManager::setSixenseFilter(bool filter) { · 19 unmodified lines
155}163}
156 164
157void SixenseManager::update(float deltaTime, bool jointsCaptured) {165void SixenseManager::update(float deltaTime, bool jointsCaptured) {
166 // FIXME - Some of the code in update() will crash if you haven't actually activated the
167 // plugin. But we want register with the UserInputMapper if we don't call this.
168 // We need to clean this up.
169 //if (!_activated) {
170 // return;
171 //}
158#ifdef HAVE_SIXENSE172#ifdef HAVE_SIXENSE
159 _buttonPressedMap.clear();173 _buttonPressedMap.clear();
160 174
@@ -163,33 +177,34 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) { · 2 unmodified lines
163 (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetNumActiveControllers");177 (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetNumActiveControllers");
164#endif178#endif
165 179
166 auto userInputMapper = DependencyManager::get<UserInputMapper>();180 auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
167 181
182 static const float MAX_DISCONNECTED_TIME = 2.0f;
183 static bool disconnected { false };
184 static float disconnectedInterval { 0.0f };
168 if (sixenseGetNumActiveControllers() == 0) {185 if (sixenseGetNumActiveControllers() == 0) {
169 if (_hydrasConnected) {186 if (!disconnected) {
170 qCDebug(inputplugins) << "hydra disconnected" << _badDataCount;187 disconnectedInterval += deltaTime;
171 if (_badDataCount++ < _allowedBadDataCount) { // gotta get some no-active in a row before we shut things down
172 return;
173 }
174 }188 }
175 _hydrasConnected = false;189 if (disconnectedInterval > MAX_DISCONNECTED_TIME) {
176 if (_deviceID != 0) {190 disconnected = true;
177 userInputMapper->removeDevice(_deviceID);191 _axisStateMap.clear();
178 _deviceID = 0;192 _buttonPressedMap.clear();
179 _poseStateMap.clear();193 _poseStateMap.clear();
194 _collectedSamples.clear();
180 }195 }
181 return;196 return;
182 }197 }
183 198
184 PerformanceTimer perfTimer("sixense");199 if (disconnected) {
185 if (!_hydrasConnected) {200 disconnected = 0;
186 _hydrasConnected = true;201 disconnectedInterval = 0.0f;
187 _badDataCount = 0;
188 registerToUserInputMapper(*userInputMapper);
189 assignDefaultInputMapping(*userInputMapper);
190 UserActivityLogger::getInstance().connectedDevice("spatial_controller", "hydra");
191 }202 }
192 203
204 PerformanceTimer perfTimer("sixense");
205 // FIXME send this message once when we've positively identified hydra hardware
206 //UserActivityLogger::getInstance().connectedDevice("spatial_controller", "hydra");
207
193#ifdef __APPLE__208#ifdef __APPLE__
194 SixenseBaseFunction sixenseGetMaxControllers =209 SixenseBaseFunction sixenseGetMaxControllers =
195 (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetMaxControllers");210 (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetMaxControllers");
@@ -220,28 +235,31 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) { · 24 unmodified lines
220 // NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters.235 // NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters.
221 glm::vec3 position(data->pos[0], data->pos[1], data->pos[2]);236 glm::vec3 position(data->pos[0], data->pos[1], data->pos[2]);
222 position *= METERS_PER_MILLIMETER;237 position *= METERS_PER_MILLIMETER;
223 238 bool left = i == 0;
239 using namespace controller;
224 // Check to see if this hand/controller is on the base240 // Check to see if this hand/controller is on the base
225 const float CONTROLLER_AT_BASE_DISTANCE = 0.075f;241 const float CONTROLLER_AT_BASE_DISTANCE = 0.075f;
226 if (glm::length(position) >= CONTROLLER_AT_BASE_DISTANCE) {242 if (glm::length(position) >= CONTROLLER_AT_BASE_DISTANCE) {
227 handleButtonEvent(data->buttons, numActiveControllers - 1);243 handleButtonEvent(data->buttons, left);
228 handleAxisEvent(data->joystick_x, data->joystick_y, data->trigger, numActiveControllers - 1);244 _axisStateMap[left ? LX : RX] = data->joystick_x;
245 _axisStateMap[left ? LY : RY] = data->joystick_y;
246 _axisStateMap[left ? LT : RT] = data->trigger;
229 247
230 if (!jointsCaptured) {248 if (!jointsCaptured) {
231 // Rotation of Palm249 // Rotation of Palm
232 glm::quat rotation(data->rot_quat[3], data->rot_quat[0], data->rot_quat[1], data->rot_quat[2]);250 glm::quat rotation(data->rot_quat[3], data->rot_quat[0], data->rot_quat[1], data->rot_quat[2]);
233 handlePoseEvent(position, rotation, numActiveControllers - 1);251 handlePoseEvent(deltaTime, position, rotation, left);
252
234 } else {253 } else {
235 _poseStateMap.clear();254 _poseStateMap.clear();
255 _collectedSamples.clear();
236 }256 }
237 } else {257 } else {
238 _poseStateMap[(numActiveControllers - 1) == 0 ? LEFT_HAND : RIGHT_HAND] = UserInputMapper::PoseValue();258 auto hand = left ? controller::StandardPoseChannel::LEFT_HAND : controller::StandardPoseChannel::RIGHT_HAND;
259 _poseStateMap[hand] = controller::Pose();
260 _collectedSamples[hand].first.clear();
261 _collectedSamples[hand].second.clear();
239 }262 }
240
241// // Read controller buttons and joystick into the hand
242// palm->setControllerButtons(data->buttons);
243// palm->setTrigger(data->trigger);
244// palm->setJoystick(data->joystick_x, data->joystick_y);
245 }263 }
246 264
247 if (numActiveControllers == 2) {265 if (numActiveControllers == 2) {
@@ -361,40 +379,38 @@ void SixenseManager::focusOutEvent() { · 113 unmodified lines
361 _buttonPressedMap.clear();379 _buttonPressedMap.clear();
362};380};
363 381
364void SixenseManager::handleAxisEvent(float stickX, float stickY, float trigger, int index) {382void SixenseManager::handleAxisEvent(float stickX, float stickY, float trigger, bool left) {
365 _axisStateMap[makeInput(AXIS_Y_POS, index).getChannel()] = (stickY > 0.0f) ? stickY : 0.0f;
366 _axisStateMap[makeInput(AXIS_Y_NEG, index).getChannel()] = (stickY < 0.0f) ? -stickY : 0.0f;
367 _axisStateMap[makeInput(AXIS_X_POS, index).getChannel()] = (stickX > 0.0f) ? stickX : 0.0f;
368 _axisStateMap[makeInput(AXIS_X_NEG, index).getChannel()] = (stickX < 0.0f) ? -stickX : 0.0f;
369 _axisStateMap[makeInput(BACK_TRIGGER, index).getChannel()] = trigger;
370}383}
371 384
372void SixenseManager::handleButtonEvent(unsigned int buttons, int index) {385void SixenseManager::handleButtonEvent(unsigned int buttons, bool left) {
386 using namespace controller;
373 if (buttons & BUTTON_0) {387 if (buttons & BUTTON_0) {
374 _buttonPressedMap.insert(makeInput(BUTTON_0, index).getChannel());388 _buttonPressedMap.insert(left ? BACK : START);
375 }389 }
376 if (buttons & BUTTON_1) {390 if (buttons & BUTTON_1) {
377 _buttonPressedMap.insert(makeInput(BUTTON_1, index).getChannel());391 _buttonPressedMap.insert(left ? DL : X);
378 }392 }
379 if (buttons & BUTTON_2) {393 if (buttons & BUTTON_2) {
380 _buttonPressedMap.insert(makeInput(BUTTON_2, index).getChannel());394 _buttonPressedMap.insert(left ? DD : A);
381 }395 }
382 if (buttons & BUTTON_3) {396 if (buttons & BUTTON_3) {
383 _buttonPressedMap.insert(makeInput(BUTTON_3, index).getChannel());397 _buttonPressedMap.insert(left ? DR : B);
384 }398 }
385 if (buttons & BUTTON_4) {399 if (buttons & BUTTON_4) {
386 _buttonPressedMap.insert(makeInput(BUTTON_4, index).getChannel());400 _buttonPressedMap.insert(left ? DU : Y);
387 }401 }
388 if (buttons & BUTTON_FWD) {402 if (buttons & BUTTON_FWD) {
389 _buttonPressedMap.insert(makeInput(BUTTON_FWD, index).getChannel());403 _buttonPressedMap.insert(left ? LB : RB);
390 }404 }
391 if (buttons & BUTTON_TRIGGER) {405 if (buttons & BUTTON_TRIGGER) {
392 _buttonPressedMap.insert(makeInput(BUTTON_TRIGGER, index).getChannel());406 _buttonPressedMap.insert(left ? LS : RS);
393 }407 }
394}408}
395 409
396void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int index) {410void SixenseManager::handlePoseEvent(float deltaTime, glm::vec3 position, glm::quat rotation, bool left) {
397#ifdef HAVE_SIXENSE411#ifdef HAVE_SIXENSE
412 auto hand = left ? controller::StandardPoseChannel::LEFT_HAND : controller::StandardPoseChannel::RIGHT_HAND;
413
398 // From ABOVE the sixense coordinate frame looks like this:414 // From ABOVE the sixense coordinate frame looks like this:
399 //415 //
400 // |416 // |
@@ -409,6 +425,7 @@ void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int · 8 unmodified lines
409 // |425 // |
410 // |426 // |
411 // z427 // z
428 auto prevPose = _poseStateMap[hand];
412 429
413 // Transform the measured position into body frame.430 // Transform the measured position into body frame.
414 position = _avatarRotation * (position + _avatarPosition);431 position = _avatarRotation * (position + _avatarPosition);
@@ -437,7 +454,7 @@ void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int · 22 unmodified lines
437 // In addition to Qsh each hand has pre-offset introduced by the shape of the sixense controllers454 // In addition to Qsh each hand has pre-offset introduced by the shape of the sixense controllers
438 // and how they fit into the hand in their relaxed state. This offset is a quarter turn about455 // and how they fit into the hand in their relaxed state. This offset is a quarter turn about
439 // the sixense's z-axis, with its direction different for the two hands:456 // the sixense's z-axis, with its direction different for the two hands:
440 float sign = (index == 0) ? 1.0f : -1.0f;457 float sign = left ? 1.0f : -1.0f;
441 const glm::quat preOffset = glm::angleAxis(sign * PI / 2.0f, Vectors::UNIT_Z);458 const glm::quat preOffset = glm::angleAxis(sign * PI / 2.0f, Vectors::UNIT_Z);
442 459
443 // Finally, there is a post-offset (same for both hands) to get the hand's rest orientation460 // Finally, there is a post-offset (same for both hands) to get the hand's rest orientation
@@ -452,104 +469,128 @@ void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int · 8 unmodified lines
452 // TODO: find a shortcut with fewer rotations.469 // TODO: find a shortcut with fewer rotations.
453 rotation = _avatarRotation * postOffset * glm::inverse(sixenseToHand) * rotation * preOffset * sixenseToHand;470 rotation = _avatarRotation * postOffset * glm::inverse(sixenseToHand) * rotation * preOffset * sixenseToHand;
454 471
455 _poseStateMap[makeInput(JointChannel(index)).getChannel()] = UserInputMapper::PoseValue(position, rotation);472 glm::vec3 velocity(0.0f);
456#endif // HAVE_SIXENSE473 glm::quat angularVelocity;
457}
458
459void 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
509void SixenseManager::assignDefaultInputMapping(UserInputMapper& mapper) {
510 const float JOYSTICK_MOVE_SPEED = 1.0f;
511 const float JOYSTICK_YAW_SPEED = 0.5f;
512 const float JOYSTICK_PITCH_SPEED = 0.25f;
513 const float BUTTON_MOVE_SPEED = 1.0f;
514 const float BOOM_SPEED = 0.1f;
515 474
516 // Left Joystick: Movement, strafing
517 mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(AXIS_Y_POS, 0), JOYSTICK_MOVE_SPEED);
518 mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(AXIS_Y_NEG, 0), JOYSTICK_MOVE_SPEED);
519 mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(AXIS_X_POS, 0), JOYSTICK_MOVE_SPEED);
520 mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(AXIS_X_NEG, 0), JOYSTICK_MOVE_SPEED);
521 475
522 // Right Joystick: Camera orientation
523 mapper.addInputChannel(UserInputMapper::YAW_RIGHT, makeInput(AXIS_X_POS, 1), JOYSTICK_YAW_SPEED);
524 mapper.addInputChannel(UserInputMapper::YAW_LEFT, makeInput(AXIS_X_NEG, 1), JOYSTICK_YAW_SPEED);
525 mapper.addInputChannel(UserInputMapper::PITCH_UP, makeInput(AXIS_Y_POS, 1), JOYSTICK_PITCH_SPEED);
526 mapper.addInputChannel(UserInputMapper::PITCH_DOWN, makeInput(AXIS_Y_NEG, 1), JOYSTICK_PITCH_SPEED);
527 476
528 // Buttons477 if (prevPose.isValid() && deltaTime > std::numeric_limits<float>::epsilon()) {
529 mapper.addInputChannel(UserInputMapper::BOOM_IN, makeInput(BUTTON_3, 0), BOOM_SPEED);
530 mapper.addInputChannel(UserInputMapper::BOOM_OUT, makeInput(BUTTON_1, 0), BOOM_SPEED);
531 478
532 mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(BUTTON_3, 1), BUTTON_MOVE_SPEED);479 velocity = (position - prevPose.getTranslation()) / deltaTime;
533 mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(BUTTON_1, 1), BUTTON_MOVE_SPEED);
534 480
535 mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_2, 0));481 auto deltaRot = rotation * glm::conjugate(prevPose.getRotation());
536 mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_2, 1));482 auto axis = glm::axis(deltaRot);
483 auto angle = glm::angle(deltaRot);
484 angularVelocity = glm::angleAxis(angle / deltaTime, axis);
537 485
538 mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(BUTTON_4, 0));486 // Average
539 mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(BUTTON_4, 1));487 auto& samples = _collectedSamples[hand];
488 samples.first.addSample(velocity);
489 velocity = samples.first.average;
490
491 // FIXME: // Not using quaternion average yet for angular velocity because it s probably wrong but keep the MovingAverage in place
492 //samples.second.addSample(glm::vec4(angularVelocity.x, angularVelocity.y, angularVelocity.z, angularVelocity.w));
493 //angularVelocity = glm::quat(samples.second.average.w, samples.second.average.x, samples.second.average.y, samples.second.average.z);
494 } else if (!prevPose.isValid()) {
495 _collectedSamples[hand].first.clear();
496 _collectedSamples[hand].second.clear();
497 }
540 498
541 mapper.addInputChannel(UserInputMapper::LEFT_HAND, makeInput(LEFT_HAND));499 _poseStateMap[hand] = controller::Pose(position, rotation, velocity, angularVelocity);
542 mapper.addInputChannel(UserInputMapper::RIGHT_HAND, makeInput(RIGHT_HAND));500#endif // HAVE_SIXENSE
501}
543 502
544 mapper.addInputChannel(UserInputMapper::LEFT_HAND_CLICK, makeInput(BACK_TRIGGER, 0));503static const auto L0 = controller::BACK;
545 mapper.addInputChannel(UserInputMapper::RIGHT_HAND_CLICK, makeInput(BACK_TRIGGER, 1));504static const auto L1 = controller::DL;
505static const auto L2 = controller::DD;
506static const auto L3 = controller::DR;
507static const auto L4 = controller::DU;
508static const auto R0 = controller::START;
509static const auto R1 = controller::X;
510static const auto R2 = controller::A;
511static const auto R3 = controller::B;
512static const auto R4 = controller::Y;
513
514using namespace controller;
515
516controller::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
546QString 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// virtual594// virtual
554void SixenseManager::saveSettings() const {595void SixenseManager::saveSettings() const {
555 Settings settings;596 Settings settings;
@@ -559,7 +600,6 @@ void SixenseManager::saveSettings() const { · 3 unmodified lines
559 settings.setVec3Value(QString("avatarPosition"), _avatarPosition);600 settings.setVec3Value(QString("avatarPosition"), _avatarPosition);
560 settings.setQuatValue(QString("avatarRotation"), _avatarRotation);601 settings.setQuatValue(QString("avatarRotation"), _avatarRotation);
561 settings.setValue(QString("reachLength"), QVariant(_reachLength));602 settings.setValue(QString("reachLength"), QVariant(_reachLength));
562 settings.setValue(QString("allowedHydraFailures"), 120);
563 }603 }
564 settings.endGroup();604 settings.endGroup();
565}605}
@@ -572,19 +612,6 @@ void SixenseManager::loadSettings() { · 6 unmodified lines
572 settings.getVec3ValueIfValid(QString("avatarPosition"), _avatarPosition);612 settings.getVec3ValueIfValid(QString("avatarPosition"), _avatarPosition);
573 settings.getQuatValueIfValid(QString("avatarRotation"), _avatarRotation);613 settings.getQuatValueIfValid(QString("avatarRotation"), _avatarRotation);
574 settings.getFloatValueIfValid(QString("reachLength"), _reachLength);614 settings.getFloatValueIfValid(QString("reachLength"), _reachLength);
575 _allowedBadDataCount = settings.value(QString("allowedHydraFailures"), 120).toInt();
576 }615 }
577 settings.endGroup();616 settings.endGroup();
578}617}
579
580UserInputMapper::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
584UserInputMapper::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
588UserInputMapper::Input SixenseManager::makeInput(JointChannel joint) {
589 return UserInputMapper::Input(_deviceID, joint, UserInputMapper::ChannelType::POSE);
590}
libraries/input-plugins/src/input-plugins/SixenseManager.h+20−29
@@ -24,8 +24,12 @@ · 23 unmodified lines
24 24
25#endif25#endif
26 26
27#include <SimpleMovingAverage.h>
28
29#include <controllers/InputDevice.h>
30#include <controllers/StandardControls.h>
31
27#include "InputPlugin.h"32#include "InputPlugin.h"
28#include "InputDevice.h"
29 33
30class QLibrary;34class QLibrary;
31 35
@@ -40,22 +44,9 @@ const unsigned int BUTTON_TRIGGER = 1U << 8; · 8 unmodified lines
40const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = false;44const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = false;
41 45
42// Handles interaction with the Sixense SDK (e.g., Razer Hydra).46// Handles interaction with the Sixense SDK (e.g., Razer Hydra).
43class SixenseManager : public InputPlugin, public InputDevice {47class SixenseManager : public InputPlugin, public controller::InputDevice {
44 Q_OBJECT48 Q_OBJECT
45public:49public:
46 enum JoystickAxisChannel {
47 AXIS_Y_POS = 1U << 0,
48 AXIS_Y_NEG = 1U << 3,
49 AXIS_X_POS = 1U << 4,
50 AXIS_X_NEG = 1U << 5,
51 BACK_TRIGGER = 1U << 6,
52 };
53
54 enum JointChannel {
55 LEFT_HAND = 0,
56 RIGHT_HAND,
57 };
58
59 SixenseManager();50 SixenseManager();
60 51
61 // Plugin functions52 // Plugin functions
@@ -71,15 +62,12 @@ public: · 9 unmodified lines
71 virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); }62 virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); }
72 63
73 // Device functions64 // Device functions
74 virtual void registerToUserInputMapper(UserInputMapper& mapper) override;65 virtual controller::Input::NamedVector getAvailableInputs() const override;
75 virtual void assignDefaultInputMapping(UserInputMapper& mapper) override;66 virtual QString getDefaultMappingConfig() const override;
67
76 virtual void update(float deltaTime, bool jointsCaptured) override;68 virtual void update(float deltaTime, bool jointsCaptured) override;
77 virtual void focusOutEvent() override;69 virtual void focusOutEvent() override;
78 70
79 UserInputMapper::Input makeInput(unsigned int button, int index);
80 UserInputMapper::Input makeInput(JoystickAxisChannel axis, int index);
81 UserInputMapper::Input makeInput(JointChannel joint);
82
83 virtual void saveSettings() const override;71 virtual void saveSettings() const override;
84 virtual void loadSettings() override;72 virtual void loadSettings() override;
85 73
@@ -87,9 +75,9 @@ public slots: · 1 unmodified line
87 void setSixenseFilter(bool filter);75 void setSixenseFilter(bool filter);
88 76
89private: 77private:
90 void handleButtonEvent(unsigned int buttons, int index);78 void handleButtonEvent(unsigned int buttons, bool left);
91 void handleAxisEvent(float x, float y, float trigger, int index);79 void handleAxisEvent(float x, float y, float trigger, bool left);
92 void handlePoseEvent(glm::vec3 position, glm::quat rotation, int index);80 void handlePoseEvent(float deltaTime, glm::vec3 position, glm::quat rotation, bool left);
93 81
94 void updateCalibration(void* controllers);82 void updateCalibration(void* controllers);
95 83
@@ -108,17 +96,20 @@ private: · 12 unmodified lines
108 glm::vec3 _reachRight;96 glm::vec3 _reachRight;
109 float _lastDistance;97 float _lastDistance;
110 bool _useSixenseFilter = true;98 bool _useSixenseFilter = true;
99
100
101 static const int MAX_NUM_AVERAGING_SAMPLES = 50; // At ~100 updates per seconds this means averaging over ~.5s
102 using Samples = std::pair< MovingAverage< glm::vec3, MAX_NUM_AVERAGING_SAMPLES>, MovingAverage< glm::vec4, MAX_NUM_AVERAGING_SAMPLES> >;
103 using MovingAverageMap = std::map< int, Samples >;
104 MovingAverageMap _collectedSamples;
111 105
112#ifdef __APPLE__106#ifdef __APPLE__
113 QLibrary* _sixenseLibrary;107 QLibrary* _sixenseLibrary { nullptr };
114#endif108#endif
115 109
116 bool _hydrasConnected;
117 int _badDataCount;
118 int _allowedBadDataCount;
119
120 static const QString NAME;110 static const QString NAME;
121 static const QString HYDRA_ID_STRING;111 static const QString HYDRA_ID_STRING;
122};112};
123 113
124#endif // hifi_SixenseManager_h114#endif // hifi_SixenseManager_h
115
libraries/input-plugins/src/input-plugins/StandardController.cpp+0−160
@@ -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
18const float CONTROLLER_THRESHOLD = 0.3f;
19
20StandardController::~StandardController() {
21}
22
23void 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
31void StandardController::focusOutEvent() {
32 _axisStateMap.clear();
33 _buttonPressedMap.clear();
34};
35
36void 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
82void 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
150UserInputMapper::Input StandardController::makeInput(StandardController::StandardControllerButtonChannel button) {
151 return UserInputMapper::Input(_deviceID, button, UserInputMapper::ChannelType::BUTTON);
152}
153
154UserInputMapper::Input StandardController::makeInput(StandardController::StandardControllerAxisChannel axis) {
155 return UserInputMapper::Input(_deviceID, axis, UserInputMapper::ChannelType::AXIS);
156}
157
158UserInputMapper::Input StandardController::makeInput(StandardController::StandardControllerPoseChannel pose) {
159 return UserInputMapper::Input(_deviceID, pose, UserInputMapper::ChannelType::POSE);
160}
libraries/input-plugins/src/input-plugins/StandardController.h+0−77
@@ -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
20typedef std::shared_ptr<StandardController> StandardControllerPointer;
21
22class StandardController : public QObject, public InputDevice {
23 Q_OBJECT
24 Q_PROPERTY(QString name READ getName)
25
26public:
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
74private:
75};
76
77#endif // hifi_StandardController_h
libraries/input-plugins/src/input-plugins/UserInputMapper.cpp+0−335
@@ -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
16UserInputMapper::UserInputMapper() {
17 registerStandardDevice();
18 assignDefaulActionScales();
19 createActionNames();
20}
21
22UserInputMapper::~UserInputMapper() {
23}
24
25
26bool UserInputMapper::registerDevice(uint16 deviceID, const DeviceProxy::Pointer& proxy){
27 proxy->_name += " (" + QString::number(deviceID) + ")";
28 _registeredDevices[deviceID] = proxy;
29 return true;
30}
31
32UserInputMapper::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
41QString UserInputMapper::getDeviceName(uint16 deviceID) {
42 if (_registeredDevices.find(deviceID) != _registeredDevices.end()) {
43 return _registeredDevices[deviceID]->_name;
44 }
45 return QString("unknown");
46}
47
48
49void UserInputMapper::resetAllDeviceBindings() {
50 for (auto device : _registeredDevices) {
51 device.second->resetDeviceBindings();
52 }
53}
54
55void UserInputMapper::resetDevice(uint16 deviceID) {
56 auto device = _registeredDevices.find(deviceID);
57 if (device != _registeredDevices.end()) {
58 device->second->resetDeviceBindings();
59 }
60}
61
62int 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
71QVector<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
81bool UserInputMapper::addInputChannel(Action action, const Input& input, float scale) {
82 return addInputChannel(action, input, Input(), scale);
83}
84
85bool 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
107int 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
115bool 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
134void UserInputMapper::removeAllInputChannels() {
135 _inputToModifiersMap.clear();
136 _actionToInputsMap.clear();
137}
138
139void UserInputMapper::removeAllInputChannelsForDevice(uint16 device) {
140 QVector<InputChannel> channels = getAllInputsForDevice(device);
141 for (auto& channel : channels) {
142 removeInputChannel(channel);
143 }
144}
145
146void UserInputMapper::removeDevice(int device) {
147 removeAllInputChannelsForDevice((uint16) device);
148 _registeredDevices.erase(device);
149}
150
151int UserInputMapper::getInputChannels(InputChannels& channels) const {
152 for (auto& channel : _actionToInputsMap) {
153 channels.push_back(channel.second);
154 }
155
156 return _actionToInputsMap.size();
157}
158
159QVector<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
173void 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
247QVector<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
255QVector<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
265int 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
276QVector<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
284void 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
308void 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
332void UserInputMapper::registerStandardDevice() {
333 _standardController = std::make_shared<StandardController>();
334 _standardController->registerToUserInputMapper(*this);
335}
libraries/input-plugins/src/input-plugins/UserInputMapper.h+0−284
@@ -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
23class StandardController;
24typedef std::shared_ptr<StandardController> StandardControllerPointer;
25
26class UserInputMapper : public QObject, public Dependency {
27 Q_OBJECT
28 SINGLETON_DEPENDENCY
29 Q_ENUMS(Action)
30public:
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
249signals:
250 void actionEvent(int action, float state);
251
252
253protected:
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
276Q_DECLARE_METATYPE(UserInputMapper::InputPair)
277Q_DECLARE_METATYPE(QVector<UserInputMapper::InputPair>)
278Q_DECLARE_METATYPE(UserInputMapper::Input)
279Q_DECLARE_METATYPE(UserInputMapper::InputChannel)
280Q_DECLARE_METATYPE(QVector<UserInputMapper::InputChannel>)
281Q_DECLARE_METATYPE(UserInputMapper::Action)
282Q_DECLARE_METATYPE(QVector<UserInputMapper::Action>)
283
284#endif // hifi_UserInputMapper_h
libraries/input-plugins/src/input-plugins/ViveControllerManager.cpp+145−135
@@ -12,15 +12,19 @@ · 11 unmodified lines
12#include "ViveControllerManager.h"12#include "ViveControllerManager.h"
13 13
14#include <PerfStat.h>14#include <PerfStat.h>
15 15#include <PathUtils.h>
16#include "GeometryCache.h"16#include <GeometryCache.h>
17#include <gpu/Batch.h>17#include <gpu/Batch.h>
18#include <gpu/Context.h>18#include <gpu/Context.h>
19#include <DeferredLightingEffect.h>19#include <DeferredLightingEffect.h>
20#include <display-plugins/openvr/OpenVrHelpers.h>20#include <display-plugins/openvr/OpenVrHelpers.h>
21#include "NumericalConstants.h"21#include <NumericalConstants.h>
22#include <plugins/PluginContainer.h>22#include <plugins/PluginContainer.h>
23#include "UserActivityLogger.h"23#include <UserActivityLogger.h>
24
25#include <controllers/UserInputMapper.h>
26
27#include <controllers/StandardControls.h>
24 28
25#ifdef Q_OS_WIN29#ifdef Q_OS_WIN
26extern vr::IVRSystem* _hmd;30extern vr::IVRSystem* _hmd;
@@ -29,18 +33,6 @@ extern vr::TrackedDevicePose_t _trackedDevicePose[vr::k_unMaxTrackedDeviceCount] · 2 unmodified lines
29extern mat4 _trackedDevicePoseMat4[vr::k_unMaxTrackedDeviceCount];33extern mat4 _trackedDevicePoseMat4[vr::k_unMaxTrackedDeviceCount];
30#endif34#endif
31 35
32const unsigned int LEFT_MASK = 0U;
33const unsigned int RIGHT_MASK = 1U;
34
35const uint64_t VR_BUTTON_A = 1U << 1;
36const uint64_t VR_GRIP_BUTTON = 1U << 2;
37const uint64_t VR_TRACKPAD_BUTTON = 1ULL << 32;
38const uint64_t VR_TRIGGER_BUTTON = 1ULL << 33;
39
40const unsigned int BUTTON_A = 1U << 1;
41const unsigned int GRIP_BUTTON = 1U << 2;
42const unsigned int TRACKPAD_BUTTON = 1U << 3;
43const unsigned int TRIGGER_BUTTON = 1U << 4;
44 36
45const float CONTROLLER_LENGTH_OFFSET = 0.0762f; // three inches37const float CONTROLLER_LENGTH_OFFSET = 0.0762f; // three inches
46const QString CONTROLLER_MODEL_STRING = "vr_controller_05_wireless_b";38const QString CONTROLLER_MODEL_STRING = "vr_controller_05_wireless_b";
@@ -52,15 +44,17 @@ const QString MENU_NAME = "Vive Controllers"; · 5 unmodified lines
52const QString MENU_PATH = MENU_PARENT + ">" + MENU_NAME;44const QString MENU_PATH = MENU_PARENT + ">" + MENU_NAME;
53const QString RENDER_CONTROLLERS = "Render Hand Controllers";45const QString RENDER_CONTROLLERS = "Render Hand Controllers";
54 46
47static std::shared_ptr<ViveControllerManager> instance;
48
55ViveControllerManager::ViveControllerManager() :49ViveControllerManager::ViveControllerManager() :
56 InputDevice("SteamVR Controller"),50 InputDevice("Vive"),
57 _trackedControllers(0),51 _trackedControllers(0),
58 _modelLoaded(false),52 _modelLoaded(false),
59 _leftHandRenderID(0),53 _leftHandRenderID(0),
60 _rightHandRenderID(0),54 _rightHandRenderID(0),
61 _renderControllers(false)55 _renderControllers(false)
62{56{
63 57 instance = std::shared_ptr<ViveControllerManager>(this);
64}58}
65 59
66bool ViveControllerManager::isSupported() const {60bool ViveControllerManager::isSupported() const {
@@ -79,6 +73,7 @@ bool ViveControllerManager::isSupported() const { · 12 unmodified lines
79}73}
80 74
81void ViveControllerManager::activate() {75void ViveControllerManager::activate() {
76 InputPlugin::activate();
82#ifdef Q_OS_WIN77#ifdef Q_OS_WIN
83 CONTAINER->addMenu(MENU_PATH);78 CONTAINER->addMenu(MENU_PATH);
84 CONTAINER->addMenuItem(MENU_PATH, RENDER_CONTROLLERS,79 CONTAINER->addMenuItem(MENU_PATH, RENDER_CONTROLLERS,
@@ -140,9 +135,16 @@ void ViveControllerManager::activate() { · 55 unmodified lines
140 _renderControllers = true;135 _renderControllers = true;
141 }136 }
142#endif137#endif
138
139 // unregister with UserInputMapper
140 auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
141 userInputMapper->registerDevice(instance);
142 _registeredWithInputMapper = true;
143}143}
144 144
145void ViveControllerManager::deactivate() {145void ViveControllerManager::deactivate() {
146 InputPlugin::deactivate();
147
146#ifdef Q_OS_WIN148#ifdef Q_OS_WIN
147 CONTAINER->removeMenuItem(MENU_NAME, RENDER_CONTROLLERS);149 CONTAINER->removeMenuItem(MENU_NAME, RENDER_CONTROLLERS);
148 CONTAINER->removeMenu(MENU_PATH);150 CONTAINER->removeMenu(MENU_PATH);
@@ -155,6 +157,11 @@ void ViveControllerManager::deactivate() { · 6 unmodified lines
155 }157 }
156 _poseStateMap.clear();158 _poseStateMap.clear();
157#endif159#endif
160
161 // unregister with UserInputMapper
162 auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
163 userInputMapper->removeDevice(_deviceID);
164 _registeredWithInputMapper = false;
158}165}
159 166
160void ViveControllerManager::updateRendering(RenderArgs* args, render::ScenePointer scene, render::PendingChanges pendingChanges) {167void ViveControllerManager::updateRendering(RenderArgs* args, render::ScenePointer scene, render::PendingChanges pendingChanges) {
@@ -170,8 +177,8 @@ void ViveControllerManager::updateRendering(RenderArgs* args, render::ScenePoint · 9 unmodified lines
170 //pendingChanges.updateItem(_leftHandRenderID, );177 //pendingChanges.updateItem(_leftHandRenderID, );
171 178
172 179
173 UserInputMapper::PoseValue leftHand = _poseStateMap[makeInput(JointChannel::LEFT_HAND).getChannel()];180 controller::Pose leftHand = _poseStateMap[controller::StandardPoseChannel::LEFT_HAND];
174 UserInputMapper::PoseValue rightHand = _poseStateMap[makeInput(JointChannel::RIGHT_HAND).getChannel()];181 controller::Pose rightHand = _poseStateMap[controller::StandardPoseChannel::RIGHT_HAND];
175 182
176 gpu::doInBatch(args->_context, [=](gpu::Batch& batch) {183 gpu::doInBatch(args->_context, [=](gpu::Batch& batch) {
177 auto geometryCache = DependencyManager::get<GeometryCache>();184 auto geometryCache = DependencyManager::get<GeometryCache>();
@@ -183,21 +190,20 @@ void ViveControllerManager::updateRendering(RenderArgs* args, render::ScenePoint · 5 unmodified lines
183 //batch._glBindTexture(GL_TEXTURE_2D, _uexture);190 //batch._glBindTexture(GL_TEXTURE_2D, _uexture);
184 191
185 if (leftHand.isValid()) {192 if (leftHand.isValid()) {
186 renderHand(leftHand, batch, LEFT_HAND);193 renderHand(leftHand, batch, 1);
187 }194 }
188 if (rightHand.isValid()) {195 if (rightHand.isValid()) {
189 renderHand(rightHand, batch, RIGHT_HAND);196 renderHand(rightHand, batch, -1);
190 }197 }
191 });198 });
192 }199 }
193}200}
194 201
195void ViveControllerManager::renderHand(UserInputMapper::PoseValue pose, gpu::Batch& batch, int index) {202void ViveControllerManager::renderHand(const controller::Pose& pose, gpu::Batch& batch, int sign) {
196 auto userInputMapper = DependencyManager::get<UserInputMapper>();203 auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
197 Transform transform(userInputMapper->getSensorToWorldMat());204 Transform transform(userInputMapper->getSensorToWorldMat());
198 transform.postTranslate(pose.getTranslation() + pose.getRotation() * glm::vec3(0, 0, CONTROLLER_LENGTH_OFFSET));205 transform.postTranslate(pose.getTranslation() + pose.getRotation() * glm::vec3(0, 0, CONTROLLER_LENGTH_OFFSET));
199 206
200 int sign = index == LEFT_HAND ? 1 : -1;
201 glm::quat rotation = pose.getRotation() * glm::angleAxis(PI, glm::vec3(1.0f, 0.0f, 0.0f)) * glm::angleAxis(sign * PI_OVER_TWO, glm::vec3(0.0f, 0.0f, 1.0f));207 glm::quat rotation = pose.getRotation() * glm::angleAxis(PI, glm::vec3(1.0f, 0.0f, 0.0f)) * glm::angleAxis(sign * PI_OVER_TWO, glm::vec3(0.0f, 0.0f, 1.0f));
202 transform.postRotate(rotation);208 transform.postRotate(rotation);
203 209
@@ -248,6 +254,7 @@ void ViveControllerManager::update(float deltaTime, bool jointsCaptured) { · 44 unmodified lines
248 }254 }
249 255
250 numTrackedControllers++;256 numTrackedControllers++;
257 bool left = numTrackedControllers == 1;
251 258
252 const mat4& mat = _trackedDevicePoseMat4[device];259 const mat4& mat = _trackedDevicePoseMat4[device];
253 260
@@ -261,26 +268,30 @@ void ViveControllerManager::update(float deltaTime, bool jointsCaptured) { · 7 unmodified lines
261 //qDebug() << (numTrackedControllers == 1 ? "Left: " : "Right: ");268 //qDebug() << (numTrackedControllers == 1 ? "Left: " : "Right: ");
262 //qDebug() << "Trackpad: " << controllerState.rAxis[0].x << " " << controllerState.rAxis[0].y;269 //qDebug() << "Trackpad: " << controllerState.rAxis[0].x << " " << controllerState.rAxis[0].y;
263 //qDebug() << "Trigger: " << controllerState.rAxis[1].x << " " << controllerState.rAxis[1].y;270 //qDebug() << "Trigger: " << controllerState.rAxis[1].x << " " << controllerState.rAxis[1].y;
264 handleButtonEvent(controllerState.ulButtonPressed, numTrackedControllers - 1);271 for (uint32_t i = 0; i < vr::k_EButton_Max; ++i) {
265 for (int i = 0; i < vr::k_unControllerStateAxisCount; i++) {272 auto mask = vr::ButtonMaskFromId((vr::EVRButtonId)i);
266 handleAxisEvent(Axis(i), controllerState.rAxis[i].x, controllerState.rAxis[i].y, numTrackedControllers - 1);273 bool pressed = 0 != (controllerState.ulButtonPressed & mask);
274 handleButtonEvent(i, pressed, left);
275 }
276 for (uint32_t i = 0; i < vr::k_unControllerStateAxisCount; i++) {
277 handleAxisEvent(i, controllerState.rAxis[i].x, controllerState.rAxis[i].y, left);
267 }278 }
268 }279 }
269 }280 }
270 281
271 auto userInputMapper = DependencyManager::get<UserInputMapper>();282 auto userInputMapper = DependencyManager::get<controller::UserInputMapper>();
272 283
273 if (numTrackedControllers == 0) {284 if (numTrackedControllers == 0) {
274 if (_deviceID != 0) {285 if (_registeredWithInputMapper) {
275 userInputMapper->removeDevice(_deviceID);286 userInputMapper->removeDevice(_deviceID);
276 _deviceID = 0;287 _registeredWithInputMapper = false;
277 _poseStateMap.clear();288 _poseStateMap.clear();
278 }289 }
279 }290 }
280 291
281 if (_trackedControllers == 0 && numTrackedControllers > 0) {292 if (_trackedControllers == 0 && numTrackedControllers > 0) {
282 registerToUserInputMapper(*userInputMapper);293 userInputMapper->registerDevice(instance);
283 assignDefaultInputMapping(*userInputMapper);294 _registeredWithInputMapper = true;
284 UserActivityLogger::getInstance().connectedDevice("spatial_controller", "steamVR");295 UserActivityLogger::getInstance().connectedDevice("spatial_controller", "steamVR");
285 }296 }
286 297
@@ -293,33 +304,45 @@ void ViveControllerManager::focusOutEvent() { · 6 unmodified lines
293 _buttonPressedMap.clear();304 _buttonPressedMap.clear();
294};305};
295 306
296void ViveControllerManager::handleAxisEvent(Axis axis, float x, float y, int index) {307// These functions do translation from the Steam IDs to the standard controller IDs
297 if (axis == TRACKPAD_AXIS) {308void ViveControllerManager::handleAxisEvent(uint32_t axis, float x, float y, bool left) {
298 _axisStateMap[makeInput(AXIS_Y_POS, index).getChannel()] = (y > 0.0f) ? y : 0.0f;309#ifdef Q_OS_WIN
299 _axisStateMap[makeInput(AXIS_Y_NEG, index).getChannel()] = (y < 0.0f) ? -y : 0.0f;310 axis += vr::k_EButton_Axis0;
300 _axisStateMap[makeInput(AXIS_X_POS, index).getChannel()] = (x > 0.0f) ? x : 0.0f;311 using namespace controller;
301 _axisStateMap[makeInput(AXIS_X_NEG, index).getChannel()] = (x < 0.0f) ? -x : 0.0f;312 if (axis == vr::k_EButton_SteamVR_Touchpad) {
302 } else if (axis == TRIGGER_AXIS) {313 _axisStateMap[left ? LX : RX] = x;
303 _axisStateMap[makeInput(BACK_TRIGGER, index).getChannel()] = x;314 _axisStateMap[left ? LY : RY] = y;
315 } else if (axis == vr::k_EButton_SteamVR_Trigger) {
316 //FIX ME: Seems that enters here everytime
317 _axisStateMap[left ? LT : RT] = x;
304 }318 }
319#endif
305}320}
306 321
307void ViveControllerManager::handleButtonEvent(uint64_t buttons, int index) {322// These functions do translation from the Steam IDs to the standard controller IDs
308 if (buttons & VR_BUTTON_A) {323void ViveControllerManager::handleButtonEvent(uint32_t button, bool pressed, bool left) {
309 _buttonPressedMap.insert(makeInput(BUTTON_A, index).getChannel());324#ifdef Q_OS_WIN
310 }325 if (!pressed) {
311 if (buttons & VR_GRIP_BUTTON) {326 return;
312 _buttonPressedMap.insert(makeInput(GRIP_BUTTON, index).getChannel());
313 }
314 if (buttons & VR_TRACKPAD_BUTTON) {
315 _buttonPressedMap.insert(makeInput(TRACKPAD_BUTTON, index).getChannel());
316 }327 }
317 if (buttons & VR_TRIGGER_BUTTON) {328
318 _buttonPressedMap.insert(makeInput(TRIGGER_BUTTON, index).getChannel());329 if (button == vr::k_EButton_ApplicationMenu) {
330 _buttonPressedMap.insert(left ? controller::LEFT_PRIMARY_THUMB : controller::RIGHT_PRIMARY_THUMB);
331 } else if (button == vr::k_EButton_Grip) {
332 // Tony says these are harder to reach, so make them the meta buttons
333 _buttonPressedMap.insert(left ? controller::LB : controller::RB);
334 } else if (button == vr::k_EButton_SteamVR_Trigger) {
335 _buttonPressedMap.insert(left ? controller::LT : controller::RT);
336 } else if (button == vr::k_EButton_SteamVR_Touchpad) {
337 _buttonPressedMap.insert(left ? controller::LS : controller::RS);
338 } else if (button == vr::k_EButton_System) {
339 //FIX ME: not able to ovrewrite the behaviour of this button
340 _buttonPressedMap.insert(left ? controller::LEFT_SECONDARY_THUMB : controller::RIGHT_SECONDARY_THUMB);
319 }341 }
342#endif
320}343}
321 344
322void ViveControllerManager::handlePoseEvent(const mat4& mat, int index) {345void ViveControllerManager::handlePoseEvent(const mat4& mat, bool left) {
323 glm::vec3 position = extractTranslation(mat);346 glm::vec3 position = extractTranslation(mat);
324 glm::quat rotation = glm::quat_cast(mat);347 glm::quat rotation = glm::quat_cast(mat);
325 348
@@ -375,7 +398,7 @@ void ViveControllerManager::handlePoseEvent(const mat4& mat, int index) { · 49 unmodified lines
375 398
376 const glm::quat quarterX = glm::angleAxis(PI / 2.0f, glm::vec3(1.0f, 0.0f, 0.0f));399 const glm::quat quarterX = glm::angleAxis(PI / 2.0f, glm::vec3(1.0f, 0.0f, 0.0f));
377 const glm::quat yFlip = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));400 const glm::quat yFlip = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
378 float sign = (index == LEFT_HAND) ? -1.0f : 1.0f;401 float sign = left ? -1.0f : 1.0f;
379 const glm::quat signedQuaterZ = glm::angleAxis(sign * PI / 2.0f, glm::vec3(0.0f, 0.0f, 1.0f)); 402 const glm::quat signedQuaterZ = glm::angleAxis(sign * PI / 2.0f, glm::vec3(0.0f, 0.0f, 1.0f));
380 const glm::quat eighthX = glm::angleAxis(PI / 4.0f, glm::vec3(1.0f, 0.0f, 0.0f));403 const glm::quat eighthX = glm::angleAxis(PI / 4.0f, glm::vec3(1.0f, 0.0f, 0.0f));
381 const glm::quat offset = glm::inverse(signedQuaterZ * eighthX);404 const glm::quat offset = glm::inverse(signedQuaterZ * eighthX);
@@ -383,92 +406,79 @@ void ViveControllerManager::handlePoseEvent(const mat4& mat, int index) { · 1 unmodified line
383 406
384 position += rotation * glm::vec3(0, 0, -CONTROLLER_LENGTH_OFFSET);407 position += rotation * glm::vec3(0, 0, -CONTROLLER_LENGTH_OFFSET);
385 408
386 _poseStateMap[makeInput(JointChannel(index)).getChannel()] = UserInputMapper::PoseValue(position, rotation);409 _poseStateMap[left ? controller::LEFT_HAND : controller::RIGHT_HAND] = controller::Pose(position, rotation);
387}410}
388 411
389void ViveControllerManager::registerToUserInputMapper(UserInputMapper& mapper) {412controller::Input::NamedVector ViveControllerManager::getAvailableInputs() const {
390 // Grab the current free device ID413 using namespace controller;
391 _deviceID = mapper.getFreeDeviceID();414 QVector<Input::NamedPair> availableInputs{
392 415 // Trackpad analogs
393 auto proxy = UserInputMapper::DeviceProxy::Pointer(new UserInputMapper::DeviceProxy(_name));416 makePair(LX, "LX"),
394 proxy->getButton = [this] (const UserInputMapper::Input& input, int timestamp) -> bool { return this->getButton(input.getChannel()); };417 makePair(LY, "LY"),
395 proxy->getAxis = [this] (const UserInputMapper::Input& input, int timestamp) -> float { return this->getAxis(input.getChannel()); };418 makePair(RX, "RX"),
396 proxy->getPose = [this](const UserInputMapper::Input& input, int timestamp) -> UserInputMapper::PoseValue { return this->getPose(input.getChannel()); };419 makePair(RY, "RY"),
397 proxy->getAvailabeInputs = [this] () -> QVector<UserInputMapper::InputPair> {420 // trigger analogs
398 QVector<UserInputMapper::InputPair> availableInputs;421 makePair(LT, "LT"),
399 availableInputs.append(UserInputMapper::InputPair(makeInput(LEFT_HAND), "Left Hand"));422 makePair(RT, "RT"),
400 423
401 availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_A, 0), "Left Button A"));424 makePair(LB, "LB"),
402 availableInputs.append(UserInputMapper::InputPair(makeInput(GRIP_BUTTON, 0), "Left Grip Button"));425 makePair(RB, "RB"),
403 availableInputs.append(UserInputMapper::InputPair(makeInput(TRACKPAD_BUTTON, 0), "Left Trackpad Button"));426
404 availableInputs.append(UserInputMapper::InputPair(makeInput(TRIGGER_BUTTON, 0), "Left Trigger Button"));427 makePair(LS, "LS"),
405 428 makePair(RS, "RS"),
406 availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_POS, 0), "Left Trackpad Up"));429 makePair(LEFT_HAND, "LeftHand"),
407 availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_NEG, 0), "Left Trackpad Down"));430 makePair(RIGHT_HAND, "RightHand"),
408 availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_POS, 0), "Left Trackpad Right"));431
409 availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_NEG, 0), "Left Trackpad Left"));432 makePair(LEFT_PRIMARY_THUMB, "LeftPrimaryThumb"),
410 availableInputs.append(UserInputMapper::InputPair(makeInput(BACK_TRIGGER, 0), "Left Back Trigger"));433 makePair(LEFT_SECONDARY_THUMB, "LeftSecondaryThumb"),
411 434 makePair(RIGHT_PRIMARY_THUMB, "RightPrimaryThumb"),
412 435 makePair(RIGHT_SECONDARY_THUMB, "RightSecondaryThumb"),
413 availableInputs.append(UserInputMapper::InputPair(makeInput(RIGHT_HAND), "Right Hand"));
414
415 availableInputs.append(UserInputMapper::InputPair(makeInput(BUTTON_A, 1), "Right Button A"));
416 availableInputs.append(UserInputMapper::InputPair(makeInput(GRIP_BUTTON, 1), "Right Grip Button"));
417 availableInputs.append(UserInputMapper::InputPair(makeInput(TRACKPAD_BUTTON, 1), "Right Trackpad Button"));
418 availableInputs.append(UserInputMapper::InputPair(makeInput(TRIGGER_BUTTON, 1), "Right Trigger Button"));
419
420 availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_POS, 1), "Right Trackpad Up"));
421 availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_Y_NEG, 1), "Right Trackpad Down"));
422 availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_POS, 1), "Right Trackpad Right"));
423 availableInputs.append(UserInputMapper::InputPair(makeInput(AXIS_X_NEG, 1), "Right Trackpad Left"));
424 availableInputs.append(UserInputMapper::InputPair(makeInput(BACK_TRIGGER, 1), "Right Back Trigger"));
425
426 return availableInputs;
427 };436 };
428 proxy->resetDeviceBindings = [this, &mapper] () -> bool {
429 mapper.removeAllInputChannelsForDevice(_deviceID);
430 this->assignDefaultInputMapping(mapper);
431 return true;
432 };
433 mapper.registerDevice(_deviceID, proxy);
434}
435
436void 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
464UserInputMapper::Input ViveControllerManager::makeInput(unsigned int button, int index) {438 //availableInputs.append(Input::NamedPair(makeInput(BUTTON_A, 0), "Left Button A"));
465 return UserInputMapper::Input(_deviceID, button | (index == 0 ? LEFT_MASK : RIGHT_MASK), UserInputMapper::ChannelType::BUTTON);439 //availableInputs.append(Input::NamedPair(makeInput(GRIP_BUTTON, 0), "Left Grip Button"));
440 //availableInputs.append(Input::NamedPair(makeInput(TRACKPAD_BUTTON, 0), "Left Trackpad Button"));
441 //availableInputs.append(Input::NamedPair(makeInput(TRIGGER_BUTTON, 0), "Left Trigger Button"));
442 //availableInputs.append(Input::NamedPair(makeInput(BACK_TRIGGER, 0), "Left Back Trigger"));
443 //availableInputs.append(Input::NamedPair(makeInput(RIGHT_HAND), "Right Hand"));
444 //availableInputs.append(Input::NamedPair(makeInput(BUTTON_A, 1), "Right Button A"));
445 //availableInputs.append(Input::NamedPair(makeInput(GRIP_BUTTON, 1), "Right Grip Button"));
446 //availableInputs.append(Input::NamedPair(makeInput(TRACKPAD_BUTTON, 1), "Right Trackpad Button"));
447 //availableInputs.append(Input::NamedPair(makeInput(TRIGGER_BUTTON, 1), "Right Trigger Button"));
448 //availableInputs.append(Input::NamedPair(makeInput(BACK_TRIGGER, 1), "Right Back Trigger"));
449
450 return availableInputs;
466}451}
467 452
468UserInputMapper::Input ViveControllerManager::makeInput(ViveControllerManager::JoystickAxisChannel axis, int index) {453QString ViveControllerManager::getDefaultMappingConfig() const {
469 return UserInputMapper::Input(_deviceID, axis | (index == 0 ? LEFT_MASK : RIGHT_MASK), UserInputMapper::ChannelType::AXIS);454 static const QString MAPPING_JSON = PathUtils::resourcesPath() + "/controllers/vive.json";
455 return MAPPING_JSON;
470}456}
471 457
472UserInputMapper::Input ViveControllerManager::makeInput(JointChannel joint) {458//void ViveControllerManager::assignDefaultInputMapping(UserInputMapper& mapper) {
473 return UserInputMapper::Input(_deviceID, joint, UserInputMapper::ChannelType::POSE);459// const float JOYSTICK_MOVE_SPEED = 1.0f;
474}460//
461// // Left Trackpad: Movement, strafing
462// mapper.addInputChannel(UserInputMapper::LONGITUDINAL_FORWARD, makeInput(AXIS_Y_POS, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED);
463// mapper.addInputChannel(UserInputMapper::LONGITUDINAL_BACKWARD, makeInput(AXIS_Y_NEG, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED);
464// mapper.addInputChannel(UserInputMapper::LATERAL_RIGHT, makeInput(AXIS_X_POS, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED);
465// mapper.addInputChannel(UserInputMapper::LATERAL_LEFT, makeInput(AXIS_X_NEG, 0), makeInput(TRACKPAD_BUTTON, 0), JOYSTICK_MOVE_SPEED);
466//
467// // Right Trackpad: Vertical movement, zooming
468// mapper.addInputChannel(UserInputMapper::VERTICAL_UP, makeInput(AXIS_Y_POS, 1), makeInput(TRACKPAD_BUTTON, 1), JOYSTICK_MOVE_SPEED);
469// mapper.addInputChannel(UserInputMapper::VERTICAL_DOWN, makeInput(AXIS_Y_NEG, 1), makeInput(TRACKPAD_BUTTON, 1), JOYSTICK_MOVE_SPEED);
470//
471// // Buttons
472// mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_A, 0));
473// mapper.addInputChannel(UserInputMapper::SHIFT, makeInput(BUTTON_A, 1));
474//
475// mapper.addInputChannel(UserInputMapper::ACTION1, makeInput(GRIP_BUTTON, 0));
476// mapper.addInputChannel(UserInputMapper::ACTION2, makeInput(GRIP_BUTTON, 1));
477//
478// mapper.addInputChannel(UserInputMapper::LEFT_HAND_CLICK, makeInput(BACK_TRIGGER, 0));
479// mapper.addInputChannel(UserInputMapper::RIGHT_HAND_CLICK, makeInput(BACK_TRIGGER, 1));
480//
481// // Hands
482// mapper.addInputChannel(UserInputMapper::LEFT_HAND, makeInput(LEFT_HAND));
483// mapper.addInputChannel(UserInputMapper::RIGHT_HAND, makeInput(RIGHT_HAND));
484//}
libraries/input-plugins/src/input-plugins/ViveControllerManager.h+11−33
@@ -19,35 +19,14 @@ · 18 unmodified lines
19 19
20#include <model/Geometry.h>20#include <model/Geometry.h>
21#include <gpu/Texture.h>21#include <gpu/Texture.h>
22#include "InputDevice.h"22#include <controllers/InputDevice.h>
23#include "InputPlugin.h"23#include "InputPlugin.h"
24#include <RenderArgs.h>24#include <RenderArgs.h>
25#include <render/Scene.h>25#include <render/Scene.h>
26 26
27class ViveControllerManager : public InputPlugin, public InputDevice {27class ViveControllerManager : public InputPlugin, public controller::InputDevice {
28 Q_OBJECT28 Q_OBJECT
29public:29public:
30 enum JoystickAxisChannel {
31 AXIS_Y_POS = 1U << 1,
32 AXIS_Y_NEG = 1U << 2,
33 AXIS_X_POS = 1U << 3,
34 AXIS_X_NEG = 1U << 4,
35 BACK_TRIGGER = 1U << 5,
36 };
37
38 enum Axis {
39 TRACKPAD_AXIS = 0,
40 TRIGGER_AXIS,
41 AXIS_2,
42 AXIS_3,
43 AXIS_4,
44 };
45
46 enum JointChannel {
47 LEFT_HAND = 0,
48 RIGHT_HAND,
49 };
50
51 ViveControllerManager();30 ViveControllerManager();
52 31
53 // Plugin functions32 // Plugin functions
@@ -62,8 +41,8 @@ public: · 8 unmodified lines
62 virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); }41 virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override { update(deltaTime, jointsCaptured); }
63 42
64 // Device functions43 // Device functions
65 virtual void registerToUserInputMapper(UserInputMapper& mapper) override;44 virtual controller::Input::NamedVector getAvailableInputs() const override;
66 virtual void assignDefaultInputMapping(UserInputMapper& mapper) override;45 virtual QString getDefaultMappingConfig() const override;
67 virtual void update(float deltaTime, bool jointsCaptured) override;46 virtual void update(float deltaTime, bool jointsCaptured) override;
68 virtual void focusOutEvent() override;47 virtual void focusOutEvent() override;
69 48
@@ -71,16 +50,12 @@ public: · 1 unmodified line
71 50
72 void setRenderControllers(bool renderControllers) { _renderControllers = renderControllers; }51 void setRenderControllers(bool renderControllers) { _renderControllers = renderControllers; }
73 52
74 UserInputMapper::Input makeInput(unsigned int button, int index);
75 UserInputMapper::Input makeInput(JoystickAxisChannel axis, int index);
76 UserInputMapper::Input makeInput(JointChannel joint);
77
78private:53private:
79 void renderHand(UserInputMapper::PoseValue pose, gpu::Batch& batch, int index);54 void renderHand(const controller::Pose& pose, gpu::Batch& batch, int sign);
80 55
81 void handleButtonEvent(uint64_t buttons, int index);56 void handleButtonEvent(uint32_t button, bool pressed, bool left);
82 void handleAxisEvent(Axis axis, float x, float y, int index);57 void handleAxisEvent(uint32_t axis, float x, float y, bool left);
83 void handlePoseEvent(const mat4& mat, int index);58 void handlePoseEvent(const mat4& mat, bool left);
84 59
85 int _trackedControllers;60 int _trackedControllers;
86 61
@@ -94,6 +69,9 @@ private: · 7 unmodified lines
94 bool _renderControllers;69 bool _renderControllers;
95 70
96 static const QString NAME;71 static const QString NAME;
72
73 bool _registeredWithInputMapper { false };
74
97};75};
98 76
99#endif // hifi__ViveControllerManager77#endif // hifi__ViveControllerManager
libraries/networking/src/AssetClient.cpp+0−3
@@ -89,7 +89,6 @@ AssetRequest* AssetClient::createRequest(const QString& hash, const QString& ext · 88 unmodified lines
89 89
90 // Move to the AssetClient thread in case we are not currently on that thread (which will usually be the case)90 // Move to the AssetClient thread in case we are not currently on that thread (which will usually be the case)
91 request->moveToThread(thread());91 request->moveToThread(thread());
92 request->setParent(this);
93 92
94 return request;93 return request;
95 } else {94 } else {
@@ -105,7 +104,6 @@ AssetUpload* AssetClient::createUpload(const QString& filename) { · 9 unmodified lines
105 auto upload = new AssetUpload(filename);104 auto upload = new AssetUpload(filename);
106 105
107 upload->moveToThread(thread());106 upload->moveToThread(thread());
108 upload->setParent(this);
109 107
110 return upload;108 return upload;
111 } else {109 } else {
@@ -118,7 +116,6 @@ AssetUpload* AssetClient::createUpload(const QByteArray& data, const QString& ex · 6 unmodified lines
118 auto upload = new AssetUpload(data, extension);116 auto upload = new AssetUpload(data, extension);
119 117
120 upload->moveToThread(thread());118 upload->moveToThread(thread());
121 upload->setParent(this);
122 119
123 return upload;120 return upload;
124 } else {121 } else {
libraries/networking/src/udt/PacketHeaders.cpp+1−1
@@ -38,7 +38,7 @@ PacketVersion versionForPacketType(PacketType packetType) { · 37 unmodified lines
38 case PacketType::EntityAdd:38 case PacketType::EntityAdd:
39 case PacketType::EntityEdit:39 case PacketType::EntityEdit:
40 case PacketType::EntityData:40 case PacketType::EntityData:
41 return VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP;41 return VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP_BIS;
42 case PacketType::AvatarData:42 case PacketType::AvatarData:
43 case PacketType::BulkAvatarData:43 case PacketType::BulkAvatarData:
44 default:44 default:
libraries/networking/src/udt/PacketHeaders.h+1−0
@@ -144,5 +144,6 @@ const PacketVersion VERSION_ENTITIES_PARTICLE_ELLIPSOID_EMITTER = 44; · 143 unmodified lines
144const PacketVersion VERSION_ENTITIES_PROTOCOL_CHANNELS = 45;144const PacketVersion VERSION_ENTITIES_PROTOCOL_CHANNELS = 45;
145const PacketVersion VERSION_ENTITIES_ANIMATION_PROPERTIES_GROUP = 46;145const PacketVersion VERSION_ENTITIES_ANIMATION_PROPERTIES_GROUP = 46;
146const PacketVersion VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP = 47;146const PacketVersion VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP = 47;
147const PacketVersion VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP_BIS = 48;
147 148
148#endif // hifi_PacketHeaders_h149#endif // hifi_PacketHeaders_h
libraries/physics/src/CharacterController.cpp+2−2
@@ -14,11 +14,11 @@ · 13 unmodified lines
14#include "PhysicsCollisionGroups.h"14#include "PhysicsCollisionGroups.h"
15 15
16bool CharacterController::needsRemoval() const {16bool 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
20bool CharacterController::needsAddition() const {20bool 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
24void CharacterController::setDynamicsWorld(btDynamicsWorld* world) {24void CharacterController::setDynamicsWorld(btDynamicsWorld* world) {
libraries/physics/src/PhysicsEngine.cpp+3−2
@@ -241,7 +241,7 @@ void PhysicsEngine::stepSimulation() { · 240 unmodified lines
241 float timeStep = btMin(dt, MAX_TIMESTEP);241 float timeStep = btMin(dt, MAX_TIMESTEP);
242 242
243 if (_myAvatarController) {243 if (_myAvatarController) {
244 // ADEBUG TODO: move this stuff outside and in front of stepSimulation, because 244 // ADEBUG TODO: move this stuff outside and in front of stepSimulation, because
245 // the updateShapeIfNecessary() call needs info from MyAvatar and should245 // the updateShapeIfNecessary() call needs info from MyAvatar and should
246 // be done on the main thread during the pre-simulation stuff246 // be done on the main thread during the pre-simulation stuff
247 if (_myAvatarController->needsRemoval()) {247 if (_myAvatarController->needsRemoval()) {
@@ -263,7 +263,8 @@ void PhysicsEngine::stepSimulation() { · 15 unmodified lines
263 updateContactMap();263 updateContactMap();
264 };264 };
265 265
266 int numSubsteps = _dynamicsWorld->stepSimulation(timeStep, PHYSICS_ENGINE_MAX_NUM_SUBSTEPS, PHYSICS_ENGINE_FIXED_SUBSTEP, onSubStep);266 int numSubsteps = _dynamicsWorld->stepSimulationWithSubstepCallback(timeStep, PHYSICS_ENGINE_MAX_NUM_SUBSTEPS,
267 PHYSICS_ENGINE_FIXED_SUBSTEP, onSubStep);
267 if (numSubsteps > 0) {268 if (numSubsteps > 0) {
268 BT_PROFILE("postSimulation");269 BT_PROFILE("postSimulation");
269 _numSubsteps += (uint32_t)numSubsteps;270 _numSubsteps += (uint32_t)numSubsteps;
libraries/physics/src/ThreadSafeDynamicsWorld.cpp+3−2
@@ -27,8 +27,9 @@ ThreadSafeDynamicsWorld::ThreadSafeDynamicsWorld( · 26 unmodified lines
27 : btDiscreteDynamicsWorld(dispatcher, pairCache, constraintSolver, collisionConfiguration) {27 : btDiscreteDynamicsWorld(dispatcher, pairCache, constraintSolver, collisionConfiguration) {
28}28}
29 29
30int ThreadSafeDynamicsWorld::stepSimulation( btScalar timeStep, int maxSubSteps, btScalar fixedTimeStep, SubStepCallback onSubStep) {30int ThreadSafeDynamicsWorld::stepSimulationWithSubstepCallback(btScalar timeStep, int maxSubSteps,
31 BT_PROFILE("stepSimulation");31 btScalar fixedTimeStep, SubStepCallback onSubStep) {
32 BT_PROFILE("stepSimulationWithSubstepCallback");
32 int subSteps = 0;33 int subSteps = 0;
33 if (maxSubSteps) {34 if (maxSubSteps) {
34 //fixed timestep with interpolation35 //fixed timestep with interpolation
libraries/physics/src/ThreadSafeDynamicsWorld.h+3−2
@@ -37,8 +37,9 @@ public: · 36 unmodified lines
37 btConstraintSolver* constraintSolver,37 btConstraintSolver* constraintSolver,
38 btCollisionConfiguration* collisionConfiguration);38 btCollisionConfiguration* collisionConfiguration);
39 39
40 // virtual overrides from btDiscreteDynamicsWorld40 int stepSimulationWithSubstepCallback(btScalar timeStep, int maxSubSteps = 1,
41 int stepSimulation( btScalar timeStep, int maxSubSteps=1, btScalar fixedTimeStep=btScalar(1.)/btScalar(60.), SubStepCallback onSubStep = []() { });41 btScalar fixedTimeStep = btScalar(1.)/btScalar(60.),
42 SubStepCallback onSubStep = []() { });
42 void synchronizeMotionStates();43 void synchronizeMotionStates();
43 44
44 // btDiscreteDynamicsWorld::m_localTime is the portion of real-time that has not yet been simulated45 // btDiscreteDynamicsWorld::m_localTime is the portion of real-time that has not yet been simulated
libraries/plugins/src/plugins/Plugin.h+12−2
@@ -33,9 +33,18 @@ public: · 32 unmodified lines
33 virtual void deinit();33 virtual void deinit();
34 34
35 /// Called when a plugin is being activated for use. May be called multiple times.35 /// Called when a plugin is being activated for use. May be called multiple times.
36 virtual void activate() = 0;36 virtual void activate() {
37 _active = true;
38 }
39
37 /// Called when a plugin is no longer being used. May be called multiple times.40 /// Called when a plugin is no longer being used. May be called multiple times.
38 virtual void deactivate() = 0;41 virtual void deactivate() {
42 _active = false;
43 }
44
45 virtual bool isActive() {
46 return _active;
47 }
39 48
40 /**49 /**
41 * Called by the application during it's idle phase. If the plugin needs to do50 * Called by the application during it's idle phase. If the plugin needs to do
@@ -48,6 +57,7 @@ public: · 6 unmodified lines
48 virtual void loadSettings() {}57 virtual void loadSettings() {}
49 58
50protected:59protected:
60 bool _active{ false };
51 static PluginContainer* CONTAINER;61 static PluginContainer* CONTAINER;
52 static QString UNKNOWN_PLUGIN_ID;62 static QString UNKNOWN_PLUGIN_ID;
53 63
libraries/script-engine/CMakeLists.txt+1−1
@@ -1,3 +1,3 @@
1set(TARGET_NAME script-engine)1set(TARGET_NAME script-engine)
2setup_hifi_library(Gui Network Script WebSockets Widgets)2setup_hifi_library(Gui Network Script WebSockets Widgets)
3link_hifi_libraries(shared networking octree gpu procedural model model-networking fbx entities animation audio physics)3link_hifi_libraries(shared networking octree gpu procedural model model-networking fbx entities controllers animation audio physics)
libraries/script-engine/src/AbstractControllerScriptingInterface.h+0−125
@@ -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
27class ScriptEngine;
28
29class AbstractInputController : public QObject {
30 Q_OBJECT
31
32public:
33 typedef unsigned int Key;
34
35 virtual void update() = 0;
36
37 virtual Key getKey() const = 0;
38
39public 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
47signals:
48 void spatialEvent(const SpatialEvent& event);
49
50};
51
52/// handles scripting of input controller commands from JS
53class AbstractControllerScriptingInterface : public QObject {
54 Q_OBJECT
55
56public 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
100signals:
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
libraries/script-engine/src/AbstractScriptingServicesInterface.h+0−8
@@ -12,21 +12,13 @@ · 11 unmodified lines
12#ifndef hifi_AbstractScriptingServicesInterface_h12#ifndef hifi_AbstractScriptingServicesInterface_h
13#define hifi_AbstractScriptingServicesInterface_h13#define hifi_AbstractScriptingServicesInterface_h
14 14
15class AbstractControllerScriptingInterface;
16class Transform;
17class ScriptEngine;15class ScriptEngine;
18class QThread;
19 16
20/// Interface provided by Application to other objects that need access to scripting services of the application17/// Interface provided by Application to other objects that need access to scripting services of the application
21class AbstractScriptingServicesInterface {18class AbstractScriptingServicesInterface {
22public:19public:
23
24 /// Returns the controller interface for the application
25 virtual AbstractControllerScriptingInterface* getControllerScriptingInterface() = 0;
26
27 /// Registers application specific services with a script engine.20 /// Registers application specific services with a script engine.
28 virtual void registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine) = 0;21 virtual void registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine) = 0;
29
30};22};
31 23
32 24
libraries/script-engine/src/ScriptEngine.cpp+59−17
@@ -28,7 +28,9 @@ · 27 unmodified lines
28#include <udt/PacketHeaders.h>28#include <udt/PacketHeaders.h>
29#include <UUID.h>29#include <UUID.h>
30 30
31#include "AnimationObject.h"31#include <controllers/ScriptingInterface.h>
32#include <AnimationObject.h>
33
32#include "ArrayBufferViewClass.h"34#include "ArrayBufferViewClass.h"
33#include "BatchLoader.h"35#include "BatchLoader.h"
34#include "DataViewClass.h"36#include "DataViewClass.h"
@@ -76,13 +78,16 @@ void avatarDataFromScriptValue(const QScriptValue &object, AvatarData* &out) { · 41 unmodified lines
76 out = qobject_cast<AvatarData*>(object.toQObject());78 out = qobject_cast<AvatarData*>(object.toQObject());
77}79}
78 80
79QScriptValue inputControllerToScriptValue(QScriptEngine *engine, AbstractInputController* const &in) {81QScriptValue inputControllerToScriptValue(QScriptEngine *engine, controller::InputController* const &in) {
80 return engine->newQObject(in);82 return engine->newQObject(in);
81}83}
82void inputControllerFromScriptValue(const QScriptValue &object, AbstractInputController* &out) {84
83 out = qobject_cast<AbstractInputController*>(object.toQObject());85void inputControllerFromScriptValue(const QScriptValue &object, controller::InputController* &out) {
86 out = qobject_cast<controller::InputController*>(object.toQObject());
84}87}
85 88
89
90
86static bool hasCorrectSyntax(const QScriptProgram& program) {91static bool hasCorrectSyntax(const QScriptProgram& program) {
87 const auto syntaxCheck = QScriptEngine::checkSyntax(program.sourceCode());92 const auto syntaxCheck = QScriptEngine::checkSyntax(program.sourceCode());
88 if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) {93 if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) {
@@ -114,16 +119,13 @@ static bool hadUncaughtExceptions(QScriptEngine& engine, const QString& fileName · 25 unmodified lines
114 return false;119 return false;
115}120}
116 121
117ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString,122ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString, bool wantSignals) :
118 AbstractControllerScriptingInterface* controllerScriptingInterface, bool wantSignals) :
119
120 _scriptContents(scriptContents),123 _scriptContents(scriptContents),
121 _isFinished(false),124 _isFinished(false),
122 _isRunning(false),125 _isRunning(false),
123 _isInitialized(false),126 _isInitialized(false),
124 _timerFunctionMap(),127 _timerFunctionMap(),
125 _wantSignals(wantSignals),128 _wantSignals(wantSignals),
126 _controllerScriptingInterface(controllerScriptingInterface),
127 _fileNameString(fileNameString),129 _fileNameString(fileNameString),
128 _isUserLoaded(false),130 _isUserLoaded(false),
129 _isReloading(false),131 _isReloading(false),
@@ -285,6 +287,27 @@ void ScriptEngine::errorInLoadingScript(const QUrl& url) { · 155 unmodified lines
285 }287 }
286}288}
287 289
290// Even though we never pass AnimVariantMap directly to and from javascript, the queued invokeMethod of
291// callAnimationStateHandler requires that the type be registered.
292// These two are meaningful, if we ever do want to use them...
293static QScriptValue animVarMapToScriptValue(QScriptEngine* engine, const AnimVariantMap& parameters) {
294 QStringList unused;
295 return parameters.animVariantMapToScriptValue(engine, unused, false);
296}
297static 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.
301static QScriptValue resultHandlerToScriptValue(QScriptEngine* engine, const AnimVariantResultHandler& resultHandler) {
302 qCCritical(scriptengine) << "Attempt to marshall result handler to javascript";
303 assert(false);
304 return QScriptValue();
305}
306static void resultHandlerFromScriptValue(const QScriptValue& value, AnimVariantResultHandler& resultHandler) {
307 qCCritical(scriptengine) << "Attempt to marshall result handler from javascript";
308 assert(false);
309}
310
288void ScriptEngine::init() {311void ScriptEngine::init() {
289 if (_isInitialized) {312 if (_isInitialized) {
290 return; // only initialize once313 return; // only initialize once
@@ -337,27 +360,26 @@ void ScriptEngine::init() { · 46 unmodified lines
337 360
338 registerGlobalObject("Script", this);361 registerGlobalObject("Script", this);
339 registerGlobalObject("Audio", &AudioScriptingInterface::getInstance());362 registerGlobalObject("Audio", &AudioScriptingInterface::getInstance());
340 registerGlobalObject("Controller", _controllerScriptingInterface);
341 registerGlobalObject("Entities", entityScriptingInterface.data());363 registerGlobalObject("Entities", entityScriptingInterface.data());
342 registerGlobalObject("Quat", &_quatLibrary);364 registerGlobalObject("Quat", &_quatLibrary);
343 registerGlobalObject("Vec3", &_vec3Library);365 registerGlobalObject("Vec3", &_vec3Library);
344 registerGlobalObject("Uuid", &_uuidLibrary);366 registerGlobalObject("Uuid", &_uuidLibrary);
345 registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCache>().data());367 registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCache>().data());
368 qScriptRegisterMetaType(this, animVarMapToScriptValue, animVarMapFromScriptValue);
369 qScriptRegisterMetaType(this, resultHandlerToScriptValue, resultHandlerFromScriptValue);
346 370
347 // constants371 // constants
348 globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE)));372 globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE)));
349 373
350 if (_controllerScriptingInterface) {374 auto scriptingInterface = DependencyManager::get<controller::ScriptingInterface>();
351 _controllerScriptingInterface->registerControllerTypes(this);375 registerGlobalObject("Controller", scriptingInterface.data());
352 }376 UserInputMapper::registerControllerTypes(this);
353
354
355}377}
356 378
357void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) {379void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) {
358 if (QThread::currentThread() != thread()) {380 if (QThread::currentThread() != thread()) {
359#ifdef THREAD_DEBUGGING381#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#endif383#endif
362 QMetaObject::invokeMethod(this, "registerValue",384 QMetaObject::invokeMethod(this, "registerValue",
363 Q_ARG(const QString&, valueName),385 Q_ARG(const QString&, valueName),
@@ -743,6 +765,27 @@ void ScriptEngine::stop() { · 379 unmodified lines
743 }765 }
744}766}
745 767
768// Other threads can invoke this through invokeMethod, which causes the callback to be asynchronously executed in this script's thread.
769void 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
746void ScriptEngine::timerFired() {789void ScriptEngine::timerFired() {
747 QTimer* callingTimer = reinterpret_cast<QTimer*>(sender());790 QTimer* callingTimer = reinterpret_cast<QTimer*>(sender());
748 QScriptValue timerFunction = _timerFunctionMap.value(callingTimer);791 QScriptValue timerFunction = _timerFunctionMap.value(callingTimer);
@@ -928,9 +971,8 @@ void ScriptEngine::forwardHandlerCall(const EntityItemID& entityID, const QStrin · 179 unmodified lines
928 if (QThread::currentThread() != thread()) {971 if (QThread::currentThread() != thread()) {
929 qDebug() << "*** ERROR *** ScriptEngine::forwardHandlerCall() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]";972 qDebug() << "*** ERROR *** ScriptEngine::forwardHandlerCall() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]";
930 assert(false);973 assert(false);
931 return;974 return ;
932 }975 }
933
934 if (!_registeredHandlers.contains(entityID)) {976 if (!_registeredHandlers.contains(entityID)) {
935 return;977 return;
936 }978 }
libraries/script-engine/src/ScriptEngine.h+6−4
@@ -21,13 +21,14 @@ · 20 unmodified lines
21#include <QtScript/QScriptEngine>21#include <QtScript/QScriptEngine>
22 22
23#include <AnimationCache.h>23#include <AnimationCache.h>
24#include <AnimVariant.h>
24#include <AvatarData.h>25#include <AvatarData.h>
25#include <AvatarHashMap.h>26#include <AvatarHashMap.h>
26#include <LimitedNodeList.h>27#include <LimitedNodeList.h>
27#include <EntityItemID.h>28#include <EntityItemID.h>
28#include <EntitiesScriptEngineProvider.h>29#include <EntitiesScriptEngineProvider.h>
29 30
30#include "AbstractControllerScriptingInterface.h"31#include "MouseEvent.h"
31#include "ArrayBufferClass.h"32#include "ArrayBufferClass.h"
32#include "AudioScriptingInterface.h"33#include "AudioScriptingInterface.h"
33#include "Quat.h"34#include "Quat.h"
@@ -53,7 +54,6 @@ class ScriptEngine : public QScriptEngine, public ScriptUser, public EntitiesScr · 19 unmodified lines
53public:54public:
54 ScriptEngine(const QString& scriptContents = NO_SCRIPT,55 ScriptEngine(const QString& scriptContents = NO_SCRIPT,
55 const QString& fileNameString = QString(""),56 const QString& fileNameString = QString(""),
56 AbstractControllerScriptingInterface* controllerScriptingInterface = NULL,
57 bool wantSignals = true);57 bool wantSignals = true);
58 58
59 ~ScriptEngine();59 ~ScriptEngine();
@@ -142,6 +142,9 @@ public: · 82 unmodified lines
142 // NOTE - this is used by the TypedArray implemetation. we need to review this for thread safety142 // NOTE - this is used by the TypedArray implemetation. we need to review this for thread safety
143 ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; }143 ArrayBufferClass* getArrayBufferClass() { return _arrayBufferClass; }
144 144
145public slots:
146 void callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters, QStringList names, bool useNames, AnimVariantResultHandler resultHandler);
147
145signals:148signals:
146 void scriptLoaded(const QString& scriptFilename);149 void scriptLoaded(const QString& scriptFilename);
147 void errorLoadingScript(const QString& scriptFilename);150 void errorLoadingScript(const QString& scriptFilename);
@@ -181,8 +184,7 @@ private: · 33 unmodified lines
181 184
182 QObject* setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot);185 QObject* setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot);
183 void stopTimer(QTimer* timer);186 void stopTimer(QTimer* timer);
184 187
185 AbstractControllerScriptingInterface* _controllerScriptingInterface;
186 QString _fileNameString;188 QString _fileNameString;
187 Quat _quatLibrary;189 Quat _quatLibrary;
188 Vec3 _vec3Library;190 Vec3 _vec3Library;
libraries/script-engine/src/Vec3.h+22−0
@@ -23,6 +23,25 @@ · 22 unmodified lines
23/// Scriptable interface a Vec3ernion helper class object. Used exclusively in the JavaScript API23/// Scriptable interface a Vec3ernion helper class object. Used exclusively in the JavaScript API
24class Vec3 : public QObject {24class Vec3 : public QObject {
25 Q_OBJECT25 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
27public slots:46public slots:
28 glm::vec3 reflect(const glm::vec3& v1, const glm::vec3& v2) { return glm::reflect(v1, v2); }47 glm::vec3 reflect(const glm::vec3& v1, const glm::vec3& v2) { return glm::reflect(v1, v2); }
@@ -30,6 +49,7 @@ public slots: · 1 unmodified line
30 float dot(const glm::vec3& v1, const glm::vec3& v2) { return glm::dot(v1, v2); }49 float dot(const glm::vec3& v1, const glm::vec3& v2) { return glm::dot(v1, v2); }
31 glm::vec3 multiply(const glm::vec3& v1, float f) { return v1 * f; }50 glm::vec3 multiply(const glm::vec3& v1, float f) { return v1 * f; }
32 glm::vec3 multiply(float f, const glm::vec3& v1) { return v1 * f; }51 glm::vec3 multiply(float f, const glm::vec3& v1) { return v1 * f; }
52 glm::vec3 multiplyVbyV(const glm::vec3& v1, const glm::vec3& v2) { return v1 * v2; }
33 glm::vec3 multiplyQbyV(const glm::quat& q, const glm::vec3& v) { return q * v; }53 glm::vec3 multiplyQbyV(const glm::quat& q, const glm::vec3& v) { return q * v; }
34 glm::vec3 sum(const glm::vec3& v1, const glm::vec3& v2) { return v1 + v2; }54 glm::vec3 sum(const glm::vec3& v1, const glm::vec3& v2) { return v1 + v2; }
35 glm::vec3 subtract(const glm::vec3& v1, const glm::vec3& v2) { return v1 - v2; }55 glm::vec3 subtract(const glm::vec3& v1, const glm::vec3& v2) { return v1 - v2; }
@@ -45,6 +65,8 @@ public slots: · 9 unmodified lines
45 glm::vec3 toPolar(const glm::vec3& v);65 glm::vec3 toPolar(const glm::vec3& v);
46 glm::vec3 fromPolar(const glm::vec3& polar);66 glm::vec3 fromPolar(const glm::vec3& polar);
47 glm::vec3 fromPolar(float elevation, float azimuth);67 glm::vec3 fromPolar(float elevation, float azimuth);
68
69private:
48 const glm::vec3& UNIT_X() { return Vectors::UNIT_X; }70 const glm::vec3& UNIT_X() { return Vectors::UNIT_X; }
49 const glm::vec3& UNIT_Y() { return Vectors::UNIT_Y; }71 const glm::vec3& UNIT_Y() { return Vectors::UNIT_Y; }
50 const glm::vec3& UNIT_Z() { return Vectors::UNIT_Z; }72 const glm::vec3& UNIT_Z() { return Vectors::UNIT_Z; }
libraries/shared/src/DependencyManager.h+19−3
@@ -51,7 +51,10 @@ public: · 50 unmodified lines
51 51
52 template<typename T, typename ...Args>52 template<typename T, typename ...Args>
53 static QSharedPointer<T> set(Args&&... args);53 static QSharedPointer<T> set(Args&&... args);
54 54
55 template<typename T, typename I, typename ...Args>
56 static QSharedPointer<T> set(Args&&... args);
57
55 template<typename T>58 template<typename T>
56 static void destroy();59 static void destroy();
57 60
@@ -89,13 +92,26 @@ QSharedPointer<T> DependencyManager::get() { · 31 unmodified lines
89template <typename T, typename ...Args>92template <typename T, typename ...Args>
90QSharedPointer<T> DependencyManager::set(Args&&... args) {93QSharedPointer<T> DependencyManager::set(Args&&... args) {
91 static size_t hashCode = _manager.getHashCode<T>();94 static size_t hashCode = _manager.getHashCode<T>();
92 95
93 QSharedPointer<Dependency>& instance = _manager.safeGet(hashCode);96 QSharedPointer<Dependency>& instance = _manager.safeGet(hashCode);
94 instance.clear(); // Clear instance before creation of new one to avoid edge cases97 instance.clear(); // Clear instance before creation of new one to avoid edge cases
95 QSharedPointer<T> newInstance(new T(args...), &T::customDeleter);98 QSharedPointer<T> newInstance(new T(args...), &T::customDeleter);
96 QSharedPointer<Dependency> storedInstance = qSharedPointerCast<Dependency>(newInstance);99 QSharedPointer<Dependency> storedInstance = qSharedPointerCast<Dependency>(newInstance);
97 instance.swap(storedInstance);100 instance.swap(storedInstance);
98 101
102 return newInstance;
103}
104
105template <typename T, typename I, typename ...Args>
106QSharedPointer<T> DependencyManager::set(Args&&... args) {
107 static size_t hashCode = _manager.getHashCode<T>();
108
109 QSharedPointer<Dependency>& instance = _manager.safeGet(hashCode);
110 instance.clear(); // Clear instance before creation of new one to avoid edge cases
111 QSharedPointer<T> newInstance(new I(args...), &I::customDeleter);
112 QSharedPointer<Dependency> storedInstance = qSharedPointerCast<Dependency>(newInstance);
113 instance.swap(storedInstance);
114
99 return newInstance;115 return newInstance;
100}116}
101 117
libraries/shared/src/SharedUtil.cpp+19−12
@@ -15,6 +15,7 @@ · 14 unmodified lines
15#include <cstring>15#include <cstring>
16#include <cctype>16#include <cctype>
17#include <time.h>17#include <time.h>
18#include <mutex>
18 19
19#ifdef _WIN3220#ifdef _WIN32
20#include <windows.h>21#include <windows.h>
@@ -39,30 +40,29 @@ void usecTimestampNowForceClockSkew(int clockSkew) { · 18 unmodified lines
39 ::usecTimestampNowAdjust = clockSkew;40 ::usecTimestampNowAdjust = clockSkew;
40}41}
41 42
43static qint64 TIME_REFERENCE = 0; // in usec
44static std::once_flag usecTimestampNowIsInitialized;
45static QElapsedTimer timestampTimer;
46
42quint64 usecTimestampNow(bool wantDebug) {47quint64 usecTimestampNow(bool wantDebug) {
43 static bool usecTimestampNowIsInitialized = false;48 std::call_once(usecTimestampNowIsInitialized, [&] {
44 static qint64 TIME_REFERENCE = 0; // in usec49 TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * USECS_PER_MSEC; // ms to usec
45 static QElapsedTimer timestampTimer;
46
47 if (!usecTimestampNowIsInitialized) {
48 TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * 1000; // ms to usec
49 timestampTimer.start();50 timestampTimer.start();
50 usecTimestampNowIsInitialized = true;51 });
51 }
52 52
53 quint64 now;53 quint64 now;
54 quint64 nsecsElapsed = timestampTimer.nsecsElapsed();54 quint64 nsecsElapsed = timestampTimer.nsecsElapsed();
55 quint64 usecsElapsed = nsecsElapsed / 1000; // nsec to usec55 quint64 usecsElapsed = nsecsElapsed / NSECS_PER_USEC; // nsec to usec
56 56
57 // QElapsedTimer may not advance if the CPU has gone to sleep. In which case it57 // QElapsedTimer may not advance if the CPU has gone to sleep. In which case it
58 // will begin to deviate from real time. We detect that here, and reset if necessary58 // will begin to deviate from real time. We detect that here, and reset if necessary
59 quint64 msecsCurrentTime = QDateTime::currentMSecsSinceEpoch();59 quint64 msecsCurrentTime = QDateTime::currentMSecsSinceEpoch();
60 quint64 msecsEstimate = (TIME_REFERENCE + usecsElapsed) / 1000; // usecs to msecs60 quint64 msecsEstimate = (TIME_REFERENCE + usecsElapsed) / USECS_PER_MSEC; // usecs to msecs
61 int possibleSkew = msecsEstimate - msecsCurrentTime;61 int possibleSkew = msecsEstimate - msecsCurrentTime;
62 const int TOLERANCE = 10000; // up to 10 seconds of skew is tolerated62 const int TOLERANCE = 10 * MSECS_PER_SECOND; // up to 10 seconds of skew is tolerated
63 if (abs(possibleSkew) > TOLERANCE) {63 if (abs(possibleSkew) > TOLERANCE) {
64 // reset our TIME_REFERENCE and timer64 // reset our TIME_REFERENCE and timer
65 TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * 1000; // ms to usec65 TIME_REFERENCE = QDateTime::currentMSecsSinceEpoch() * USECS_PER_MSEC; // ms to usec
66 timestampTimer.restart();66 timestampTimer.restart();
67 now = TIME_REFERENCE + ::usecTimestampNowAdjust;67 now = TIME_REFERENCE + ::usecTimestampNowAdjust;
68 68
@@ -118,6 +118,13 @@ quint64 usecTimestampNow(bool wantDebug) { · 49 unmodified lines
118 return now;118 return now;
119}119}
120 120
121float 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
121float randFloat() {128float randFloat() {
122 return (rand() % 10000)/10000.0f;129 return (rand() % 10000)/10000.0f;
123}130}
libraries/shared/src/SharedUtil.h+5−0
@@ -65,9 +65,14 @@ inline bool operator!=(const xColor& lhs, const xColor& rhs) · 64 unmodified lines
65// Use a custom User-Agent to avoid ModSecurity filtering, e.g. by hosting providers.65// Use a custom User-Agent to avoid ModSecurity filtering, e.g. by hosting providers.
66const QByteArray HIGH_FIDELITY_USER_AGENT = "Mozilla/5.0 (HighFidelityInterface)";66const QByteArray HIGH_FIDELITY_USER_AGENT = "Mozilla/5.0 (HighFidelityInterface)";
67 67
68// Equivalent to time_t but in usecs instead of secs
68quint64 usecTimestampNow(bool wantDebug = false);69quint64 usecTimestampNow(bool wantDebug = false);
69void usecTimestampNowForceClockSkew(int clockSkew);70void 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
74float secTimestampNow();
75
71float randFloat();76float randFloat();
72int randIntInRange (int min, int max);77int randIntInRange (int min, int max);
73float randFloatInRange (float min,float max);78float randFloatInRange (float min,float max);
libraries/shared/src/SimpleMovingAverage.h+24−0
@@ -40,4 +40,28 @@ private: · 39 unmodified lines
40 float ONE_MINUS_WEIGHTING;40 float ONE_MINUS_WEIGHTING;
41};41};
42 42
43
44template <class T, int MAX_NUM_SAMPLES> class MovingAverage {
45public:
46 const float WEIGHTING = 1.0f / (float)MAX_NUM_SAMPLES;
47 const float ONE_MINUS_WEIGHTING = 1.0f - WEIGHTING;
48 int numSamples{ 0 };
49 T average;
50
51 void clear() {
52 numSamples = 0;
53 }
54
55 bool isAverageValid() const { return (numSamples > 0); }
56
57 void addSample(T sample) {
58 if (numSamples > 0) {
59 average = (sample * WEIGHTING) + (average * ONE_MINUS_WEIGHTING);
60 } else {
61 average = sample;
62 }
63 numSamples++;
64 }
65};
66
43#endif // hifi_SimpleMovingAverage_h67#endif // hifi_SimpleMovingAverage_h
libraries/shared/src/shared/Factory.h+51−0
@@ -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
17namespace 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
libraries/ui/src/VrMenu.cpp+4−0
@@ -113,6 +113,7 @@ void VrMenu::addMenu(QMenu* menu) { · 112 unmodified lines
113 Q_ARG(QVariant, QVariant::fromValue(qmlParent)),113 Q_ARG(QVariant, QVariant::fromValue(qmlParent)),
114 Q_ARG(QVariant, QVariant::fromValue(menu->title())));114 Q_ARG(QVariant, QVariant::fromValue(menu->title())));
115 Q_ASSERT(invokeResult);115 Q_ASSERT(invokeResult);
116 Q_UNUSED(invokeResult); // FIXME - apparently we haven't upgraded the Qt on our unix Jenkins environments to 5.5.x
116 QObject* result = returnedValue.value<QObject*>();117 QObject* result = returnedValue.value<QObject*>();
117 Q_ASSERT(result);118 Q_ASSERT(result);
118 119
@@ -153,6 +154,7 @@ void VrMenu::addAction(QMenu* menu, QAction* action) { · 34 unmodified lines
153 Q_ARG(QVariant, QVariant::fromValue(menuQml)),154 Q_ARG(QVariant, QVariant::fromValue(menuQml)),
154 Q_ARG(QVariant, QVariant::fromValue(action->text())));155 Q_ARG(QVariant, QVariant::fromValue(action->text())));
155 Q_ASSERT(invokeResult);156 Q_ASSERT(invokeResult);
157 Q_UNUSED(invokeResult); // FIXME - apparently we haven't upgraded the Qt on our unix Jenkins environments to 5.5.x
156 QObject* result = returnedValue.value<QObject*>();158 QObject* result = returnedValue.value<QObject*>();
157 Q_ASSERT(result);159 Q_ASSERT(result);
158 // Bind the QML and Widget together160 // Bind the QML and Widget together
@@ -174,6 +176,7 @@ void VrMenu::insertAction(QAction* before, QAction* action) { · 15 unmodified lines
174 Q_ARG(QVariant, QVariant::fromValue(beforeQml)),176 Q_ARG(QVariant, QVariant::fromValue(beforeQml)),
175 Q_ARG(QVariant, QVariant::fromValue(action->text())));177 Q_ARG(QVariant, QVariant::fromValue(action->text())));
176 Q_ASSERT(invokeResult);178 Q_ASSERT(invokeResult);
179 Q_UNUSED(invokeResult); // FIXME - apparently we haven't upgraded the Qt on our unix Jenkins environments to 5.5.x
177 QObject* result = returnedValue.value<QObject*>();180 QObject* result = returnedValue.value<QObject*>();
178 Q_ASSERT(result);181 Q_ASSERT(result);
179 bindActionToQmlAction(result, action);182 bindActionToQmlAction(result, action);
@@ -192,4 +195,5 @@ void VrMenu::removeAction(QAction* action) { · 12 unmodified lines
192 Q_ARG(QVariant, QVariant::fromValue(menu)),195 Q_ARG(QVariant, QVariant::fromValue(menu)),
193 Q_ARG(QVariant, QVariant::fromValue(item)));196 Q_ARG(QVariant, QVariant::fromValue(item)));
194 Q_ASSERT(invokeResult);197 Q_ASSERT(invokeResult);
198 Q_UNUSED(invokeResult); // FIXME - apparently we haven't upgraded the Qt on our unix Jenkins environments to 5.5.x
195}199}
tests/controllers/CMakeLists.txt+19−0
@@ -0,0 +1,19 @@
1
2set(TARGET_NAME controllers-test)
3
4# This is not a testcase -- just set it up as a regular hifi project
5setup_hifi_project(Script Qml)
6set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
7
8# link in the shared libraries
9link_hifi_libraries(shared gl script-engine plugins render-utils input-plugins display-plugins controllers)
10
11
12if (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})
17endif()
18
19copy_dlls_beside_windows_executable()
tests/controllers/qml/main.qml+22−0
@@ -0,0 +1,22 @@
1import QtQuick 2.1
2import QtQuick.Controls 1.0
3import QtQuick.Layouts 1.0
4import QtQuick.Dialogs 1.0
5
6ApplicationWindow {
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}
tests/controllers/src/main.cpp+158−0
@@ -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
46const 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
57const 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
66const 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
77using namespace controller;
78
79
80class PluginContainerProxy : public QObject, PluginContainer {
81 Q_OBJECT
82public:
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
102class MyControllerScriptingInterface : public controller::ScriptingInterface {
103public:
104 virtual void registerControllerTypes(QScriptEngine* engine) {};
105};
106
107
108int 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